Repository: cadence-workflow/cadence Branch: master Commit: 43ac35270406 Files: 2794 Total size: 40.2 MB Directory structure: gitextract_vsx7kcnp/ ├── .dockerignore ├── .envrc ├── .fossa.yml ├── .gen/ │ ├── go/ │ │ ├── admin/ │ │ │ ├── admin.go │ │ │ ├── adminserviceclient/ │ │ │ │ └── client.go │ │ │ ├── adminservicefx/ │ │ │ │ ├── client.go │ │ │ │ ├── doc.go │ │ │ │ └── server.go │ │ │ ├── adminserviceserver/ │ │ │ │ └── server.go │ │ │ ├── adminservicetest/ │ │ │ │ └── client.go │ │ │ └── types_yarpc.go │ │ ├── cadence/ │ │ │ ├── cadence.go │ │ │ ├── types_yarpc.go │ │ │ ├── workflowserviceclient/ │ │ │ │ └── client.go │ │ │ ├── workflowservicefx/ │ │ │ │ ├── client.go │ │ │ │ ├── doc.go │ │ │ │ └── server.go │ │ │ ├── workflowserviceserver/ │ │ │ │ └── server.go │ │ │ └── workflowservicetest/ │ │ │ └── client.go │ │ ├── checksum/ │ │ │ ├── checksum.go │ │ │ └── types_yarpc.go │ │ ├── config/ │ │ │ ├── config.go │ │ │ └── types_yarpc.go │ │ ├── health/ │ │ │ ├── health.go │ │ │ ├── metaclient/ │ │ │ │ └── client.go │ │ │ ├── metafx/ │ │ │ │ ├── client.go │ │ │ │ ├── doc.go │ │ │ │ └── server.go │ │ │ ├── metaserver/ │ │ │ │ └── server.go │ │ │ ├── metatest/ │ │ │ │ └── client.go │ │ │ └── types_yarpc.go │ │ ├── history/ │ │ │ ├── history.go │ │ │ ├── historyserviceclient/ │ │ │ │ └── client.go │ │ │ ├── historyservicefx/ │ │ │ │ ├── client.go │ │ │ │ ├── doc.go │ │ │ │ └── server.go │ │ │ ├── historyserviceserver/ │ │ │ │ └── server.go │ │ │ ├── historyservicetest/ │ │ │ │ └── client.go │ │ │ └── types_yarpc.go │ │ ├── indexer/ │ │ │ ├── indexer.go │ │ │ └── types_yarpc.go │ │ ├── matching/ │ │ │ ├── matching.go │ │ │ ├── matchingserviceclient/ │ │ │ │ └── client.go │ │ │ ├── matchingservicefx/ │ │ │ │ ├── client.go │ │ │ │ ├── doc.go │ │ │ │ └── server.go │ │ │ ├── matchingserviceserver/ │ │ │ │ └── server.go │ │ │ ├── matchingservicetest/ │ │ │ │ └── client.go │ │ │ └── types_yarpc.go │ │ ├── replicator/ │ │ │ ├── replicator.go │ │ │ └── types_yarpc.go │ │ ├── shadower/ │ │ │ ├── shadower.go │ │ │ └── types_yarpc.go │ │ ├── shared/ │ │ │ ├── shared.go │ │ │ └── types_yarpc.go │ │ └── sqlblobs/ │ │ ├── sqlblobs.go │ │ └── types_yarpc.go │ └── proto/ │ ├── history/ │ │ └── v1/ │ │ ├── service.pb.go │ │ └── service.pb.yarpc.go │ ├── indexer/ │ │ └── v1/ │ │ ├── messages.pb.go │ │ └── messages.pb.yarpc.go │ ├── matching/ │ │ └── v1/ │ │ ├── service.pb.go │ │ └── service.pb.yarpc.go │ ├── sharddistributor/ │ │ └── v1/ │ │ ├── canary.pb.go │ │ ├── canary.pb.yarpc.go │ │ ├── executor.pb.go │ │ ├── executor.pb.yarpc.go │ │ ├── service.pb.go │ │ └── service.pb.yarpc.go │ └── shared/ │ └── v1/ │ ├── any.pb.go │ ├── any.pb.yarpc.go │ ├── error.pb.go │ ├── error.pb.yarpc.go │ ├── history.pb.go │ ├── history.pb.yarpc.go │ ├── tasklist.pb.go │ ├── tasklist.pb.yarpc.go │ ├── workflow.pb.go │ └── workflow.pb.yarpc.go ├── .gitar/ │ └── rules/ │ ├── assign-maintainer-reviewer.md │ ├── issue-linking-required.md │ └── pr-description-quality.md ├── .gitattributes ├── .github/ │ ├── CODEOWNERS │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── feature_request.md │ │ └── help_support.md │ ├── dco.yml │ ├── pull_request_guidance.md │ ├── pull_request_template.md │ └── workflows/ │ ├── breaking_change_pr_template.md │ ├── breaking_change_reminder.yml │ ├── ci-checks.yml │ ├── codecov-on-master.yml │ ├── codecov-on-pr.yml │ ├── docker_publish.yml │ ├── issue-validation.yml │ ├── replication-simulation.yml │ └── semantic-pr.yml ├── .gitignore ├── .gitmodules ├── ADOPTERS.md ├── CHANGELOG.md ├── CLAUDE.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── MAINTAINERS.md ├── Makefile ├── NOTICE ├── PROPOSALS.md ├── README.md ├── RELEASES.md ├── bench/ │ ├── README.md │ ├── lib/ │ │ ├── client.go │ │ ├── config.go │ │ ├── config_test.go │ │ ├── context.go │ │ ├── metrics.go │ │ └── types.go │ ├── load/ │ │ ├── basic/ │ │ │ ├── launchWorkflow.go │ │ │ └── stressWorkflow.go │ │ ├── cancellation/ │ │ │ └── workflow.go │ │ ├── common/ │ │ │ ├── activities.go │ │ │ ├── constants.go │ │ │ └── helpers.go │ │ ├── concurrentexec/ │ │ │ ├── batchWorkflow.go │ │ │ └── launchWorkflow.go │ │ ├── cron/ │ │ │ └── workflow.go │ │ ├── signal/ │ │ │ └── workflow.go │ │ └── timer/ │ │ ├── launchWorkflow.go │ │ └── timerWorkflow.go │ └── worker.go ├── canary/ │ ├── README.md │ ├── batch.go │ ├── canary.go │ ├── cancellation.go │ ├── client.go │ ├── common.go │ ├── concurrentExec.go │ ├── config.go │ ├── const.go │ ├── cron.go │ ├── crosscluster.go │ ├── echo.go │ ├── historyArchival.go │ ├── localactivity.go │ ├── metrics.go │ ├── query.go │ ├── reset.go │ ├── retry.go │ ├── runner.go │ ├── sanity.go │ ├── searchAttributes.go │ ├── signal.go │ ├── timeout.go │ ├── visibility.go │ ├── visibilityArchival.go │ └── workflow_test.go ├── client/ │ ├── admin/ │ │ ├── interface.go │ │ └── interface_mock.go │ ├── clientBean.go │ ├── clientBean_mock.go │ ├── clientfactory.go │ ├── frontend/ │ │ ├── interface.go │ │ └── interface_mock.go │ ├── history/ │ │ ├── client.go │ │ ├── client_test.go │ │ ├── interface.go │ │ ├── interface_mock.go │ │ ├── peer_resolver.go │ │ ├── peer_resolver_mock.go │ │ └── peer_resolver_test.go │ ├── matching/ │ │ ├── client.go │ │ ├── client_test.go │ │ ├── interface.go │ │ ├── interface_mock.go │ │ ├── isolation_loadbalancer.go │ │ ├── isolation_loadbalancer_test.go │ │ ├── loadbalancer.go │ │ ├── loadbalancer_mock.go │ │ ├── loadbalancer_test.go │ │ ├── multi_loadbalancer.go │ │ ├── multi_loadbalancer_test.go │ │ ├── partition_config_provider.go │ │ ├── partition_config_provider_mock.go │ │ ├── partition_config_provider_test.go │ │ ├── peer_resolver.go │ │ ├── peer_resolver_mock.go │ │ ├── peer_resolver_test.go │ │ ├── rr_loadbalancer.go │ │ ├── rr_loadbalancer_test.go │ │ ├── weighted_loadbalancer.go │ │ └── weighted_loadbalancer_test.go │ ├── sharddistributor/ │ │ ├── interface.go │ │ └── interface_mock.go │ ├── sharddistributorexecutor/ │ │ ├── executorinterface.go │ │ └── executorinterface_mock.go │ ├── templates/ │ │ ├── errorinjectors.tmpl │ │ ├── grpc.tmpl │ │ ├── metered.tmpl │ │ ├── retry.tmpl │ │ ├── thrift.tmpl │ │ └── timeout.tmpl │ └── wrappers/ │ ├── errorinjectors/ │ │ ├── admin_generated.go │ │ ├── errorinjectors_test.go │ │ ├── frontend_generated.go │ │ ├── history_generated.go │ │ ├── matching_generated.go │ │ ├── sharddistributor_generated.go │ │ └── sharddistributorexecutor_generated.go │ ├── grpc/ │ │ ├── admin_generated.go │ │ ├── constructor.go │ │ ├── frontend_generated.go │ │ ├── history_generated.go │ │ ├── matching_generated.go │ │ ├── sharddistributor_generated.go │ │ └── sharddistributorexecutor_generated.go │ ├── metered/ │ │ ├── admin_generated.go │ │ ├── base.go │ │ ├── frontend_generated.go │ │ ├── history_generated.go │ │ ├── matching_generated.go │ │ ├── metered_test.go │ │ ├── sharddistributor_generated.go │ │ └── sharddistributorexecutor_generated.go │ ├── retryable/ │ │ ├── admin_generated.go │ │ ├── frontend_generated.go │ │ ├── history_generated.go │ │ ├── matching_generated.go │ │ ├── sharddistributor_generated.go │ │ ├── sharddistributorexecutor_generated.go │ │ └── wrappers_test.go │ ├── thrift/ │ │ ├── admin_generated.go │ │ ├── constructor.go │ │ ├── frontend_generated.go │ │ ├── history_generated.go │ │ └── matching_generated.go │ └── timeout/ │ ├── admin_generated.go │ ├── frontend_generated.go │ ├── history_generated.go │ ├── history_generated_test.go │ ├── matching_generated.go │ ├── sharddistributor_generated.go │ ├── sharddistributorexecutor_generated.go │ └── timeout.go ├── cmd/ │ ├── bench/ │ │ └── main.go │ ├── canary/ │ │ └── main.go │ ├── server/ │ │ ├── README.md │ │ ├── cadence/ │ │ │ ├── cadence.go │ │ │ ├── cadence_test.go │ │ │ ├── fx.go │ │ │ ├── fx_test.go │ │ │ ├── server.go │ │ │ ├── server_test.go │ │ │ └── testdata/ │ │ │ └── config/ │ │ │ └── development.yaml │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ ├── sharddistributor-canary/ │ │ ├── main.go │ │ └── main_test.go │ └── tools/ │ ├── cassandra/ │ │ └── main.go │ ├── cli/ │ │ └── main.go │ ├── copyright/ │ │ └── licensegen.go │ ├── releaser/ │ │ ├── internal/ │ │ │ ├── console/ │ │ │ │ ├── console.go │ │ │ │ └── console_test.go │ │ │ ├── fs/ │ │ │ │ └── fs.go │ │ │ ├── git/ │ │ │ │ └── git.go │ │ │ └── release/ │ │ │ ├── release.go │ │ │ ├── release_mocks_test.go │ │ │ ├── release_test.go │ │ │ ├── scenario_test.go │ │ │ ├── types.go │ │ │ ├── validate_test.go │ │ │ └── version_test.go │ │ └── releaser.go │ └── sql/ │ └── main.go ├── codecov.yml ├── common/ │ ├── activecluster/ │ │ ├── execution_manager_provider_mock.go │ │ ├── manager.go │ │ ├── manager_mock.go │ │ ├── manager_test.go │ │ └── types.go │ ├── archiver/ │ │ ├── README.md │ │ ├── URI.go │ │ ├── URI_test.go │ │ ├── archivalMetadata.go │ │ ├── archivalMetadata_mock.go │ │ ├── constants.go │ │ ├── filestore/ │ │ │ ├── historyArchiver.go │ │ │ ├── historyArchiver_test.go │ │ │ ├── queryParser.go │ │ │ ├── queryParser_mock.go │ │ │ ├── queryParser_test.go │ │ │ ├── util.go │ │ │ ├── util_test.go │ │ │ ├── visibilityArchiver.go │ │ │ └── visibilityArchiver_test.go │ │ ├── gcloud/ │ │ │ ├── README.md │ │ │ ├── connector/ │ │ │ │ ├── client.go │ │ │ │ ├── clientDelegate.go │ │ │ │ ├── client_test.go │ │ │ │ └── mocks/ │ │ │ │ ├── BucketHandleWrapper.go │ │ │ │ ├── Client.go │ │ │ │ ├── GcloudStorageClient.go │ │ │ │ ├── ObjectHandleWrapper.go │ │ │ │ ├── ObjectIteratorWrapper.go │ │ │ │ ├── ReaderWrapper.go │ │ │ │ └── WriterWrapper.go │ │ │ ├── go.mod │ │ │ ├── go.sum │ │ │ ├── historyArchiver.go │ │ │ ├── historyArchiver_test.go │ │ │ ├── init.go │ │ │ ├── queryParser.go │ │ │ ├── queryParser_mock.go │ │ │ ├── util.go │ │ │ ├── util_test.go │ │ │ ├── visibilityArchiver.go │ │ │ └── visibilityArchiver_test.go │ │ ├── historyIterator.go │ │ ├── historyIterator_mock.go │ │ ├── historyIterator_test.go │ │ ├── interface.go │ │ ├── interface_mock.go │ │ ├── options.go │ │ ├── provider/ │ │ │ ├── init.go │ │ │ ├── noop_provider.go │ │ │ ├── provider.go │ │ │ └── provider_mock.go │ │ ├── s3store/ │ │ │ ├── README.md │ │ │ ├── historyArchiver.go │ │ │ ├── historyArchiver_test.go │ │ │ ├── mocks/ │ │ │ │ ├── S3API_generate.go │ │ │ │ └── s3_api_mock.go │ │ │ ├── queryParser.go │ │ │ ├── queryParser_mock.go │ │ │ ├── queryParser_test.go │ │ │ ├── util.go │ │ │ ├── visibilityArchiver.go │ │ │ └── visibilityArchiver_test.go │ │ ├── util.go │ │ └── util_test.go │ ├── asyncworkflow/ │ │ ├── queue/ │ │ │ ├── consumer/ │ │ │ │ ├── default_consumer.go │ │ │ │ ├── default_consumer_test.go │ │ │ │ └── errors.go │ │ │ ├── interface.go │ │ │ ├── interface_mock.go │ │ │ ├── kafka/ │ │ │ │ ├── config.go │ │ │ │ ├── config_test.go │ │ │ │ ├── decoder.go │ │ │ │ ├── decoder_test.go │ │ │ │ ├── init.go │ │ │ │ ├── queue.go │ │ │ │ └── queue_test.go │ │ │ ├── provider/ │ │ │ │ ├── interface_mock.go │ │ │ │ ├── provider.go │ │ │ │ └── provider_test.go │ │ │ ├── provider.go │ │ │ └── provider_test.go │ │ └── queueconfigapi/ │ │ ├── handler.go │ │ ├── handler_mock.go │ │ ├── handler_test.go │ │ └── interface.go │ ├── authorization/ │ │ ├── README.md │ │ ├── authority_mock.go │ │ ├── authorizer.go │ │ ├── authorizer_test.go │ │ ├── factory.go │ │ ├── factory_test.go │ │ ├── nopAuthorizer.go │ │ ├── oauthAuthorizer.go │ │ ├── oauthAuthorizer_test.go │ │ ├── scram_client.go │ │ └── test_result │ ├── backoff/ │ │ ├── cron.go │ │ ├── cron_test.go │ │ ├── jitter.go │ │ ├── jitter_test.go │ │ ├── retry.go │ │ ├── retry_test.go │ │ ├── retrypolicy.go │ │ └── retrypolicy_test.go │ ├── blobstore/ │ │ ├── client.go │ │ ├── client_mock.go │ │ ├── filestore/ │ │ │ ├── client.go │ │ │ └── client_test.go │ │ ├── retryable_client.go │ │ └── retryable_client_test.go │ ├── cache/ │ │ ├── ack_cache.go │ │ ├── ack_cache_test.go │ │ ├── budget.go │ │ ├── budget_test.go │ │ ├── cache.go │ │ ├── domainCache.go │ │ ├── domainCacheNoOp.go │ │ ├── domainCache_mock.go │ │ ├── domainCache_test.go │ │ ├── interface_mock.go │ │ ├── lru.go │ │ ├── lru_test.go │ │ ├── metricsScopeCache.go │ │ ├── metricsScopeCache_test.go │ │ ├── simple.go │ │ └── simple_test.go │ ├── checksum/ │ │ ├── crc.go │ │ ├── crc_test.go │ │ ├── ctc_benchmark_test.go │ │ └── defs.go │ ├── client/ │ │ ├── versionChecker.go │ │ ├── versionChecker_mock.go │ │ └── versionChecker_test.go │ ├── clock/ │ │ ├── clockfx/ │ │ │ └── clockfx.go │ │ ├── event_timer_gate.go │ │ ├── event_timer_gate_test.go │ │ ├── ratelimiter.go │ │ ├── ratelimiter_bench_test.go │ │ ├── ratelimiter_comparison_test.go │ │ ├── ratelimiter_mock.go │ │ ├── ratelimiter_test.go │ │ ├── real_timer_benchmark_test.go │ │ ├── sustain.go │ │ ├── sustain_test.go │ │ ├── testdata/ │ │ │ ├── amd_linux_go1.22.txt │ │ │ └── m1_mac_go1.21.txt │ │ ├── time_source.go │ │ ├── timer_gate.go │ │ ├── timer_gate_mock.go │ │ └── timer_gate_test.go │ ├── cluster/ │ │ ├── metadata.go │ │ ├── metadata_test.go │ │ ├── metadata_test_utils.go │ │ ├── utils.go │ │ └── utils_test.go │ ├── codec/ │ │ ├── gob/ │ │ │ ├── gob.go │ │ │ └── gob_test.go │ │ ├── interface.go │ │ ├── interfaces_mock.go │ │ ├── version0_thriftrw.go │ │ └── version0_thriftrw_test.go │ ├── collection/ │ │ ├── channelPriorityQueue.go │ │ ├── channelPriorityQueue_test.go │ │ ├── concurrent_priority_queue.go │ │ ├── concurrent_queue.go │ │ ├── concurrent_queue_test.go │ │ ├── concurrent_tx_map.go │ │ ├── concurrent_tx_map_test.go │ │ ├── interface.go │ │ ├── iterator.go │ │ ├── ordered_map.go │ │ ├── ordered_map_test.go │ │ ├── pagingIterator.go │ │ ├── pagingIterator_test.go │ │ ├── priority_queue.go │ │ ├── priority_queue_test.go │ │ └── util.go │ ├── config/ │ │ ├── archival.go │ │ ├── archival_test.go │ │ ├── authorization.go │ │ ├── authorization_test.go │ │ ├── cluster.go │ │ ├── cluster_test.go │ │ ├── config.go │ │ ├── config_test.go │ │ ├── elasticsearch.go │ │ ├── elasticsearch_test.go │ │ ├── fx.go │ │ ├── kafkaConfig.go │ │ ├── loader.go │ │ ├── loader_test.go │ │ ├── log.go │ │ ├── log_test.go │ │ ├── metrics.go │ │ ├── metrics_test.go │ │ ├── persistence.go │ │ ├── pinot.go │ │ ├── pprof.go │ │ ├── sasl.go │ │ └── tls.go │ ├── constants/ │ │ └── constants.go │ ├── convert.go │ ├── convert_test.go │ ├── ctxutils/ │ │ ├── ctxutils.go │ │ └── ctxutils_test.go │ ├── daemon.go │ ├── definition/ │ │ ├── indexedKeys.go │ │ ├── indexedKeys_test.go │ │ ├── resourceDeduplication.go │ │ ├── resourceDeduplication_test.go │ │ ├── workflowIdentifier.go │ │ └── workflowidentifier_test.go │ ├── domain/ │ │ ├── archivalConfigStateMachine.go │ │ ├── archivalConfigStateMachine_test.go │ │ ├── attrValidator.go │ │ ├── attrValidator_test.go │ │ ├── audit/ │ │ │ └── audit.go │ │ ├── clusterattributes.go │ │ ├── clusterattributes_test.go │ │ ├── dlq_message_handler.go │ │ ├── dlq_message_handler_mock.go │ │ ├── dlq_message_handler_test.go │ │ ├── errors.go │ │ ├── failover_watcher.go │ │ ├── failover_watcher_mock.go │ │ ├── failover_watcher_test.go │ │ ├── handler.go │ │ ├── handler_MasterCluster_test.go │ │ ├── handler_NotMasterCluster_test.go │ │ ├── handler_integration_test.go │ │ ├── handler_mock.go │ │ ├── handler_test.go │ │ ├── replicationTaskExecutor.go │ │ ├── replicationTaskExecutor_integration_test.go │ │ ├── replicationTaskExecutor_test.go │ │ ├── replicationTaskHandler_mock.go │ │ ├── replication_queue.go │ │ ├── replication_queue_mock.go │ │ ├── replication_queue_test.go │ │ ├── transmissionTaskHandler.go │ │ ├── transmissionTaskHandler_mock.go │ │ └── transmissionTaskHandler_test.go │ ├── dynamicconfig/ │ │ ├── clientInterface.go │ │ ├── clientInterface_mock.go │ │ ├── config/ │ │ │ └── testConfig.yaml │ │ ├── config.go │ │ ├── config_benchmark_test.go │ │ ├── config_test.go │ │ ├── configstore/ │ │ │ ├── config/ │ │ │ │ └── config.go │ │ │ ├── config_store_client.go │ │ │ ├── config_store_client_test.go │ │ │ └── configstore_mock.go │ │ ├── dynamicconfigfx/ │ │ │ ├── fx.go │ │ │ └── fx_test.go │ │ ├── dynamicproperties/ │ │ │ ├── config_mock.go │ │ │ ├── constants.go │ │ │ ├── constants_test.go │ │ │ ├── definitions.go │ │ │ ├── filter.go │ │ │ └── utils_test.go │ │ ├── file_based_client.go │ │ ├── file_based_client_test.go │ │ ├── inMemoryClient.go │ │ ├── nopClient.go │ │ └── quotas/ │ │ ├── dynamicratelimiterfactory.go │ │ ├── dynamicratelimiterfactory_test.go │ │ ├── fallbackdynamicratelimiterfactory.go │ │ └── fallbackdynamicratelimiterfactory_test.go │ ├── elasticsearch/ │ │ ├── bulk/ │ │ │ ├── backoff.go │ │ │ ├── bulk.go │ │ │ ├── bulk_delete_request.go │ │ │ ├── bulk_index_request.go │ │ │ ├── bulk_update_request.go │ │ │ └── mocks/ │ │ │ ├── GenericBulkProcessor.go │ │ │ └── GenericBulkableRequest.go │ │ ├── client/ │ │ │ ├── client.go │ │ │ ├── os2/ │ │ │ │ ├── client.go │ │ │ │ ├── client_bulk.go │ │ │ │ ├── client_bulk_test.go │ │ │ │ └── client_test.go │ │ │ ├── v6/ │ │ │ │ ├── client.go │ │ │ │ ├── client_bulk.go │ │ │ │ ├── client_bulk_test.go │ │ │ │ └── client_test.go │ │ │ └── v7/ │ │ │ ├── client.go │ │ │ ├── client_bulk.go │ │ │ ├── client_bulk_test.go │ │ │ └── client_test.go │ │ ├── client.go │ │ ├── common.go │ │ ├── defs.go │ │ ├── esql/ │ │ │ ├── README.md │ │ │ ├── aggregation.go │ │ │ ├── cadenceDevReadme.md │ │ │ ├── cadenceSpecial.go │ │ │ ├── cadencesql.go │ │ │ ├── esql.go │ │ │ ├── esql_test.go │ │ │ ├── globals.go │ │ │ ├── having.go │ │ │ ├── scriptQuery.go │ │ │ └── select.go │ │ ├── interfaces.go │ │ ├── mocks/ │ │ │ └── GenericClient.go │ │ ├── page_token.go │ │ ├── query/ │ │ │ ├── bool_query.go │ │ │ ├── bool_query_test.go │ │ │ ├── builder.go │ │ │ ├── builder_test.go │ │ │ ├── exists_query.go │ │ │ ├── exists_query_test.go │ │ │ ├── match_query.go │ │ │ ├── match_query_test.go │ │ │ ├── range_query.go │ │ │ ├── range_query_test.go │ │ │ ├── sort.go │ │ │ └── sort_test.go │ │ └── validator/ │ │ ├── queryValidator.go │ │ ├── queryValidator_test.go │ │ ├── searchAttrValidator.go │ │ └── searchAttrValidator_test.go │ ├── errors/ │ │ ├── fake_errors.go │ │ ├── internal_failure_error.go │ │ ├── peer_hostname_error.go │ │ ├── peer_hostname_error_test.go │ │ └── tasklist_not_owned_by_host_error.go │ ├── future/ │ │ ├── future.go │ │ └── future_test.go │ ├── headers.go │ ├── isolationgroup/ │ │ ├── context.go │ │ ├── context_test.go │ │ ├── defaultisolationgroupstate/ │ │ │ ├── state.go │ │ │ ├── state_test.go │ │ │ └── types.go │ │ ├── interface.go │ │ ├── isolation_group_mock.go │ │ └── isolationgroupapi/ │ │ ├── domain-api-handlers.go │ │ ├── domain-api-handlers_test.go │ │ ├── global-api-handlers.go │ │ ├── global-api-handlers_test.go │ │ ├── handler.go │ │ ├── interface.go │ │ ├── isolation_handler_mock.go │ │ ├── mappers.go │ │ └── mappers_test.go │ ├── json_task_token_serializer.go │ ├── json_task_token_serializer_test.go │ ├── locks/ │ │ ├── lock.go │ │ └── lock_test.go │ ├── log/ │ │ ├── interface.go │ │ ├── logfx/ │ │ │ ├── fx.go │ │ │ └── fx_test.go │ │ ├── logger.go │ │ ├── logger_bench_test.go │ │ ├── logger_mock.go │ │ ├── logger_test.go │ │ ├── options.go │ │ ├── panic.go │ │ ├── replay.go │ │ ├── tag/ │ │ │ ├── interface.go │ │ │ ├── tags.go │ │ │ └── values.go │ │ ├── testlogger/ │ │ │ ├── fx.go │ │ │ ├── fx_test.go │ │ │ ├── testlogger.go │ │ │ └── testlogger_test.go │ │ └── throttle.go │ ├── mapq/ │ │ ├── README.md │ │ ├── client_impl.go │ │ ├── client_impl_test.go │ │ ├── dispatcher/ │ │ │ ├── dispatcher.go │ │ │ └── dispatcher_test.go │ │ ├── example_test.go │ │ ├── mapq.go │ │ ├── tree/ │ │ │ ├── queue_tree.go │ │ │ ├── queue_tree_node.go │ │ │ └── queue_tree_test.go │ │ └── types/ │ │ ├── client.go │ │ ├── consumer.go │ │ ├── consumer_mock.go │ │ ├── item.go │ │ ├── item_mock.go │ │ ├── item_test.go │ │ ├── offsets.go │ │ ├── persister.go │ │ ├── persister_mock.go │ │ ├── policy.go │ │ ├── policy_collection.go │ │ └── policy_collection_test.go │ ├── membership/ │ │ ├── hashring.go │ │ ├── hashring_test.go │ │ ├── hostinfo.go │ │ ├── hostinfo_test.go │ │ ├── membershipfx/ │ │ │ ├── membershipfx.go │ │ │ └── memebershipfx_test.go │ │ ├── peerprovider_mock.go │ │ ├── resolver.go │ │ ├── resolver_mock.go │ │ ├── resolver_test.go │ │ ├── sharddistributorresolver.go │ │ ├── sharddistributorresolver_test.go │ │ ├── singleprovider.go │ │ ├── singleprovider_mock.go │ │ ├── tasklist_differentiator.go │ │ └── tasklist_differentiator_test.go │ ├── messaging/ │ │ ├── ackManager.go │ │ ├── ackManager_test.go │ │ ├── client_mock.go │ │ ├── errors.go │ │ ├── interface.go │ │ ├── kafka/ │ │ │ ├── client_impl.go │ │ │ ├── client_impl_test.go │ │ │ ├── consumer_impl.go │ │ │ ├── consumer_impl_test.go │ │ │ ├── partition_ack_manager.go │ │ │ ├── partition_ack_manager_test.go │ │ │ ├── producer_impl.go │ │ │ └── producer_impl_test.go │ │ ├── metrics_producer.go │ │ ├── metrics_producer_test.go │ │ ├── mocks/ │ │ │ └── message_mock.go │ │ └── noop_producer.go │ ├── metrics/ │ │ ├── client.go │ │ ├── config.go │ │ ├── config_test.go │ │ ├── context.go │ │ ├── context_test.go │ │ ├── defs.go │ │ ├── defs_test.go │ │ ├── histograms.go │ │ ├── histograms_test.go │ │ ├── interfaces.go │ │ ├── metricsfx/ │ │ │ ├── metricsfx.go │ │ │ └── metricsfx_test.go │ │ ├── mocks/ │ │ │ ├── Client.go │ │ │ └── Scope.go │ │ ├── nop.go │ │ ├── runtime.go │ │ ├── scope.go │ │ ├── scope_test.go │ │ ├── stopwatch.go │ │ ├── tags.go │ │ ├── tally/ │ │ │ ├── prometheus/ │ │ │ │ └── buckets.go │ │ │ └── statsd/ │ │ │ ├── reporter.go │ │ │ └── reporter_test.go │ │ └── version.go │ ├── mocks/ │ │ ├── ExecutionManager.go │ │ ├── ExecutionManagerFactory.go │ │ ├── HistoryV2Manager.go │ │ ├── KafkaProducer.go │ │ ├── MessagingClient.go │ │ ├── MetadataManager.go │ │ ├── ShardManager.go │ │ ├── TaskManager.go │ │ └── VisibilityManager.go │ ├── ndc/ │ │ ├── history_resender.go │ │ ├── history_resender_mock.go │ │ └── history_resender_test.go │ ├── pagination/ │ │ ├── interface.go │ │ ├── iterator.go │ │ ├── iterator_test.go │ │ ├── mocks.go │ │ ├── writer.go │ │ ├── writerIterator_test.go │ │ └── writer_test.go │ ├── peerprovider/ │ │ ├── plugin.go │ │ ├── plugin_test.go │ │ └── ringpopprovider/ │ │ ├── config/ │ │ │ ├── config.go │ │ │ └── config_test.go │ │ ├── factory.go │ │ ├── factory_test.go │ │ ├── provider.go │ │ ├── provider_test.go │ │ └── ringpopfx/ │ │ ├── ringpopfx.go │ │ └── ringpopfx_test.go │ ├── persistence/ │ │ ├── client/ │ │ │ ├── bean.go │ │ │ ├── bean_mock.go │ │ │ ├── bean_test.go │ │ │ ├── factory.go │ │ │ ├── factory_mock.go │ │ │ └── factory_test.go │ │ ├── config.go │ │ ├── config_store_manager.go │ │ ├── config_store_manager_test.go │ │ ├── data_manager_interfaces.go │ │ ├── data_manager_interfaces_mock.go │ │ ├── data_manager_interfaces_test.go │ │ ├── data_store_interfaces.go │ │ ├── data_store_interfaces_mock.go │ │ ├── data_store_interfaces_test.go │ │ ├── domain_audit_log.go │ │ ├── domain_audit_log_test.go │ │ ├── domain_audit_manager.go │ │ ├── domain_audit_manager_test.go │ │ ├── domain_manager.go │ │ ├── domain_manager_test.go │ │ ├── domain_replication_config_test.go │ │ ├── elasticsearch/ │ │ │ ├── decodeBench_test.go │ │ │ ├── es_visibility_metric_clients.go │ │ │ ├── es_visibility_store.go │ │ │ └── es_visibility_store_test.go │ │ ├── errors.go │ │ ├── errors_test.go │ │ ├── execution_manager.go │ │ ├── execution_manager_test.go │ │ ├── history_manager.go │ │ ├── history_manager_test.go │ │ ├── mappers.go │ │ ├── mappers_test.go │ │ ├── metered.go │ │ ├── metered_test.go │ │ ├── nosql/ │ │ │ ├── constants.go │ │ │ ├── factory.go │ │ │ ├── nosql_config_store.go │ │ │ ├── nosql_config_store_test.go │ │ │ ├── nosql_domain_audit_store.go │ │ │ ├── nosql_domain_audit_store_test.go │ │ │ ├── nosql_domain_store.go │ │ │ ├── nosql_domain_store_test.go │ │ │ ├── nosql_execution_store.go │ │ │ ├── nosql_execution_store_test.go │ │ │ ├── nosql_execution_store_util.go │ │ │ ├── nosql_execution_store_util_test.go │ │ │ ├── nosql_history_store.go │ │ │ ├── nosql_history_store_test.go │ │ │ ├── nosql_queue_store.go │ │ │ ├── nosql_queue_store_test.go │ │ │ ├── nosql_shard_store.go │ │ │ ├── nosql_shard_store_test.go │ │ │ ├── nosql_store.go │ │ │ ├── nosql_task_store.go │ │ │ ├── nosql_task_store_test.go │ │ │ ├── nosql_test_utils.go │ │ │ ├── nosql_visibility_store.go │ │ │ ├── nosql_visibility_store_test.go │ │ │ ├── nosqlplugin/ │ │ │ │ ├── cassandra/ │ │ │ │ │ ├── admin.go │ │ │ │ │ ├── cluster_config.go │ │ │ │ │ ├── cluster_config_cql.go │ │ │ │ │ ├── constants.go │ │ │ │ │ ├── db.go │ │ │ │ │ ├── db_test.go │ │ │ │ │ ├── domain.go │ │ │ │ │ ├── domain_audit_log.go │ │ │ │ │ ├── domain_audit_log_test.go │ │ │ │ │ ├── domain_cql.go │ │ │ │ │ ├── domain_test.go │ │ │ │ │ ├── gocql/ │ │ │ │ │ │ ├── batch.go │ │ │ │ │ │ ├── batch_test.go │ │ │ │ │ │ ├── client.go │ │ │ │ │ │ ├── client_test.go │ │ │ │ │ │ ├── consistency.go │ │ │ │ │ │ ├── consistency_test.go │ │ │ │ │ │ ├── interface.go │ │ │ │ │ │ ├── interface_mock.go │ │ │ │ │ │ ├── public/ │ │ │ │ │ │ │ ├── client.go │ │ │ │ │ │ │ ├── client_test.go │ │ │ │ │ │ │ └── testdata.go │ │ │ │ │ │ ├── query.go │ │ │ │ │ │ └── session.go │ │ │ │ │ ├── history_events.go │ │ │ │ │ ├── history_events_cql.go │ │ │ │ │ ├── history_events_test.go │ │ │ │ │ ├── plugin.go │ │ │ │ │ ├── plugin_test.go │ │ │ │ │ ├── queue.go │ │ │ │ │ ├── queue_cql.go │ │ │ │ │ ├── queue_test.go │ │ │ │ │ ├── shard.go │ │ │ │ │ ├── shard_cql.go │ │ │ │ │ ├── shard_test.go │ │ │ │ │ ├── tasks.go │ │ │ │ │ ├── tasks_cql.go │ │ │ │ │ ├── tasks_test.go │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ ├── domain.go │ │ │ │ │ │ ├── shard.go │ │ │ │ │ │ ├── visibility.go │ │ │ │ │ │ └── workflow_execution.go │ │ │ │ │ ├── visibility.go │ │ │ │ │ ├── visibility_cql.go │ │ │ │ │ ├── visibility_test.go │ │ │ │ │ ├── workflow.go │ │ │ │ │ ├── workflow_cql.go │ │ │ │ │ ├── workflow_parsing_utils.go │ │ │ │ │ ├── workflow_parsing_utils_test.go │ │ │ │ │ ├── workflow_test.go │ │ │ │ │ ├── workflow_utils.go │ │ │ │ │ └── workflow_utils_test.go │ │ │ │ ├── common.go │ │ │ │ ├── dynamodb/ │ │ │ │ │ ├── admin.go │ │ │ │ │ ├── configStore.go │ │ │ │ │ ├── db.go │ │ │ │ │ ├── domain.go │ │ │ │ │ ├── domain_audit_log.go │ │ │ │ │ ├── events.go │ │ │ │ │ ├── queue.go │ │ │ │ │ ├── shard.go │ │ │ │ │ ├── task.go │ │ │ │ │ ├── visibility.go │ │ │ │ │ └── workflow.go │ │ │ │ ├── errors.go │ │ │ │ ├── interfaces.go │ │ │ │ ├── interfaces_mock.go │ │ │ │ ├── mongodb/ │ │ │ │ │ ├── admin.go │ │ │ │ │ ├── configStore.go │ │ │ │ │ ├── db.go │ │ │ │ │ ├── domain.go │ │ │ │ │ ├── domain_audit_log.go │ │ │ │ │ ├── error.go │ │ │ │ │ ├── events.go │ │ │ │ │ ├── plugin.go │ │ │ │ │ ├── queue.go │ │ │ │ │ ├── shard.go │ │ │ │ │ ├── task.go │ │ │ │ │ ├── visibility.go │ │ │ │ │ └── workflow.go │ │ │ │ └── types.go │ │ │ ├── plugin.go │ │ │ ├── sharded_nosql_store.go │ │ │ ├── sharded_nosql_store_mock.go │ │ │ ├── sharded_nosql_store_test.go │ │ │ ├── sharding_policy.go │ │ │ ├── sharding_policy_test.go │ │ │ └── utils.go │ │ ├── operation_mode_validator.go │ │ ├── operation_mode_validator_test.go │ │ ├── persistence-tests/ │ │ │ ├── configStorePersistenceTest.go │ │ │ ├── dbVisibilityPersistenceTest.go │ │ │ ├── domainAuditPersistenceTest.go │ │ │ ├── executionManagerTest.go │ │ │ ├── executionManagerTestForEventsV2.go │ │ │ ├── historyV2PersistenceTest.go │ │ │ ├── matchingPersistenceTest.go │ │ │ ├── metadataPersistenceV2Test.go │ │ │ ├── persistenceTestBase.go │ │ │ ├── queuePersistenceTest.go │ │ │ ├── shardPersistenceTest.go │ │ │ ├── shared_test.go │ │ │ ├── testcluster/ │ │ │ │ └── interfaces.go │ │ │ └── visibilitySamplingClient_test.go │ │ ├── persistence-utils/ │ │ │ └── history_manager_util.go │ │ ├── pinot/ │ │ │ ├── pinot_visibility_metric_clients.go │ │ │ ├── pinot_visibility_metric_clients_test.go │ │ │ ├── pinot_visibility_store.go │ │ │ └── pinot_visibility_store_test.go │ │ ├── queue_manager.go │ │ ├── retryer.go │ │ ├── retryer_mock.go │ │ ├── retryer_test.go │ │ ├── serialization/ │ │ │ ├── getters.go │ │ │ ├── getters_fixtures_test.go │ │ │ ├── getters_test.go │ │ │ ├── interfaces.go │ │ │ ├── interfaces_mock.go │ │ │ ├── parser.go │ │ │ ├── parser_test.go │ │ │ ├── persistence_mapper.go │ │ │ ├── persistence_mapper_test.go │ │ │ ├── serialization_test_utils.go │ │ │ ├── snappy_thrift_decoder.go │ │ │ ├── snappy_thrift_decoder_test.go │ │ │ ├── snappy_thrift_encoder.go │ │ │ ├── snappy_thrift_encoder_test.go │ │ │ ├── task_serializer.go │ │ │ ├── task_serializer_mock.go │ │ │ ├── task_serializer_test.go │ │ │ ├── thrift_decoder.go │ │ │ ├── thrift_encoder.go │ │ │ ├── thrift_mapper.go │ │ │ ├── thrift_mapper_test.go │ │ │ └── uuid.go │ │ ├── serializer.go │ │ ├── serializer_mock.go │ │ ├── serializer_test.go │ │ ├── shard_manager.go │ │ ├── shard_manager_test.go │ │ ├── sql/ │ │ │ ├── common.go │ │ │ ├── common_test.go │ │ │ ├── factory.go │ │ │ ├── factory_test.go │ │ │ ├── main_test.go │ │ │ ├── plugin.go │ │ │ ├── plugin_test.go │ │ │ ├── sql_config_store.go │ │ │ ├── sql_config_store_test.go │ │ │ ├── sql_domain_audit_store.go │ │ │ ├── sql_domain_audit_store_test.go │ │ │ ├── sql_domain_store.go │ │ │ ├── sql_domain_store_test.go │ │ │ ├── sql_execution_store.go │ │ │ ├── sql_execution_store_test.go │ │ │ ├── sql_execution_store_util.go │ │ │ ├── sql_execution_store_util_test.go │ │ │ ├── sql_history_store.go │ │ │ ├── sql_history_store_test.go │ │ │ ├── sql_queue_store.go │ │ │ ├── sql_queue_store_test.go │ │ │ ├── sql_shard_store.go │ │ │ ├── sql_shard_store_test.go │ │ │ ├── sql_task_store.go │ │ │ ├── sql_task_store_test.go │ │ │ ├── sql_test_utils.go │ │ │ ├── sql_visibility_store.go │ │ │ ├── sqldriver/ │ │ │ │ ├── connections.go │ │ │ │ ├── driver.go │ │ │ │ ├── interface.go │ │ │ │ ├── interface_mock.go │ │ │ │ ├── sharded.go │ │ │ │ └── singleton.go │ │ │ ├── sqlplugin/ │ │ │ │ ├── dbSharding.go │ │ │ │ ├── interface_mock.go │ │ │ │ ├── interfaces.go │ │ │ │ ├── mysql/ │ │ │ │ │ ├── admin.go │ │ │ │ │ ├── configstore.go │ │ │ │ │ ├── configstore_sql.go │ │ │ │ │ ├── configstore_test.go │ │ │ │ │ ├── db.go │ │ │ │ │ ├── domain.go │ │ │ │ │ ├── domain_audit_log.go │ │ │ │ │ ├── domain_audit_log_test.go │ │ │ │ │ ├── dsn_test.go │ │ │ │ │ ├── events.go │ │ │ │ │ ├── execution.go │ │ │ │ │ ├── execution_maps.go │ │ │ │ │ ├── plugin.go │ │ │ │ │ ├── queue.go │ │ │ │ │ ├── shard.go │ │ │ │ │ ├── task.go │ │ │ │ │ ├── typeconv.go │ │ │ │ │ └── visibility.go │ │ │ │ ├── postgres/ │ │ │ │ │ ├── admin.go │ │ │ │ │ ├── configstore.go │ │ │ │ │ ├── db.go │ │ │ │ │ ├── domain.go │ │ │ │ │ ├── domain_audit_log.go │ │ │ │ │ ├── domain_audit_log_test.go │ │ │ │ │ ├── events.go │ │ │ │ │ ├── execution.go │ │ │ │ │ ├── execution_maps.go │ │ │ │ │ ├── plugin.go │ │ │ │ │ ├── plugin_test.go │ │ │ │ │ ├── queue.go │ │ │ │ │ ├── shard.go │ │ │ │ │ ├── task.go │ │ │ │ │ ├── typeconv.go │ │ │ │ │ └── visibility.go │ │ │ │ └── sqlite/ │ │ │ │ ├── admin.go │ │ │ │ ├── db.go │ │ │ │ ├── db_pool.go │ │ │ │ ├── domain.go │ │ │ │ ├── dsn.go │ │ │ │ ├── dsn_test.go │ │ │ │ ├── error_checker.go │ │ │ │ ├── events.go │ │ │ │ ├── execution.go │ │ │ │ ├── execution_maps.go │ │ │ │ ├── plugin.go │ │ │ │ ├── plugin_test.go │ │ │ │ ├── queue.go │ │ │ │ ├── shard.go │ │ │ │ ├── sqlite_persistence_test.go │ │ │ │ ├── task.go │ │ │ │ ├── typeconv.go │ │ │ │ └── visibility.go │ │ │ ├── workflow_state_maps.go │ │ │ ├── workflow_state_non_maps.go │ │ │ └── workflow_state_non_maps_test.go │ │ ├── statsComputer.go │ │ ├── statsComputer_test.go │ │ ├── task_manager.go │ │ ├── task_manager_test.go │ │ ├── tasks.go │ │ ├── tasks_test.go │ │ ├── versionHistory.go │ │ ├── versionHistory_test.go │ │ ├── visibility_hybrid_manager.go │ │ ├── visibility_hybrid_manager_test.go │ │ ├── visibility_manager_interfaces.go │ │ ├── visibility_manager_interfaces_mock.go │ │ ├── visibility_single_manager.go │ │ ├── visibility_single_manager_test.go │ │ ├── visibility_store_mock.go │ │ ├── workflowStateCloseStatusValidator.go │ │ ├── workflowStateCloseStatusValidator_test.go │ │ ├── workflow_execution_info.go │ │ ├── workflow_execution_info_test.go │ │ └── wrappers/ │ │ ├── errorinjectors/ │ │ │ ├── configstore_generated.go │ │ │ ├── domain_generated.go │ │ │ ├── execution_generated.go │ │ │ ├── history_generated.go │ │ │ ├── injectors_test.go │ │ │ ├── queue_generated.go │ │ │ ├── shard_generated.go │ │ │ ├── task_generated.go │ │ │ ├── utils.go │ │ │ └── visibility_generated.go │ │ ├── metered/ │ │ │ ├── base.go │ │ │ ├── configstore_generated.go │ │ │ ├── domain_generated.go │ │ │ ├── execution_generated.go │ │ │ ├── history_generated.go │ │ │ ├── metered_test.go │ │ │ ├── queue_generated.go │ │ │ ├── shard_generated.go │ │ │ ├── task_generated.go │ │ │ └── visibility_generated.go │ │ ├── ratelimited/ │ │ │ ├── configstore_generated.go │ │ │ ├── domain_generated.go │ │ │ ├── errors.go │ │ │ ├── execution_generated.go │ │ │ ├── history_generated.go │ │ │ ├── queue_generated.go │ │ │ ├── shard_generated.go │ │ │ ├── task_generated.go │ │ │ ├── utils_test.go │ │ │ ├── visibility_generated.go │ │ │ └── wrappers_test.go │ │ ├── sampled/ │ │ │ ├── tokenbucketfactory.go │ │ │ ├── tokenbucketfactory_test.go │ │ │ ├── visibility_manager.go │ │ │ └── visibility_manager_test.go │ │ └── templates/ │ │ ├── errorinjector.tmpl │ │ ├── metered.tmpl │ │ ├── metered_execution.tmpl │ │ └── ratelimited.tmpl │ ├── pinot/ │ │ ├── generic_client_mock.go │ │ ├── interfaces.go │ │ ├── page_token.go │ │ ├── page_token_test.go │ │ ├── pinotQueryValidator.go │ │ ├── pinotQueryValidator_test.go │ │ ├── pinot_client.go │ │ ├── pinot_client_test.go │ │ ├── response_utility.go │ │ └── response_utility_test.go │ ├── pprof.go │ ├── pprof_mock.go │ ├── quotas/ │ │ ├── caller_bypass.go │ │ ├── caller_bypass_test.go │ │ ├── collection.go │ │ ├── collection_mock.go │ │ ├── dynamicratelimiter.go │ │ ├── global/ │ │ │ ├── algorithm/ │ │ │ │ ├── requestweighted.go │ │ │ │ ├── requestweighted_fuzz_test.go │ │ │ │ ├── requestweighted_test.go │ │ │ │ └── testdata/ │ │ │ │ └── fuzz/ │ │ │ │ ├── FuzzMissedUpdate/ │ │ │ │ │ └── 264c784f7bafbf5f │ │ │ │ └── FuzzMultiUpdate/ │ │ │ │ ├── 00649674e28cdc32 │ │ │ │ ├── 0bb6b094a7f63d70 │ │ │ │ ├── 356e28f5914a0f16 │ │ │ │ ├── 449388c309f148fd │ │ │ │ ├── 582528ddfad69eb5 │ │ │ │ ├── 754d7f941db60f1d │ │ │ │ ├── 84c7bfae679f54a7 │ │ │ │ ├── 9abdd061069970e6 │ │ │ │ ├── c50ed3d1a22fe00d │ │ │ │ └── f1fafad245481a29 │ │ │ ├── collection/ │ │ │ │ ├── collection.go │ │ │ │ ├── collection_fuzz_test.go │ │ │ │ ├── collection_test.go │ │ │ │ ├── internal/ │ │ │ │ │ ├── atomicmap.go │ │ │ │ │ ├── atomicmap_external_test.go │ │ │ │ │ ├── counted.go │ │ │ │ │ ├── counted_test.go │ │ │ │ │ ├── fallback.go │ │ │ │ │ ├── fallback_test.go │ │ │ │ │ ├── shadowed.go │ │ │ │ │ └── shadowed_test.go │ │ │ │ └── testdata/ │ │ │ │ └── fuzz/ │ │ │ │ └── FuzzBoostRPS/ │ │ │ │ ├── 1b0805e3169f0ae7 │ │ │ │ └── 216cd14a71215fe2 │ │ │ ├── doc.go │ │ │ ├── rpc/ │ │ │ │ ├── client.go │ │ │ │ ├── client_mock.go │ │ │ │ ├── client_test.go │ │ │ │ ├── error.go │ │ │ │ ├── mapping.go │ │ │ │ └── mapping_test.go │ │ │ └── shared/ │ │ │ ├── keymapper.go │ │ │ ├── sanity.go │ │ │ └── sanity_test.go │ │ ├── interfaces.go │ │ ├── limiter_mock.go │ │ ├── limiter_test.go │ │ ├── limiterfactory_mock.go │ │ ├── multistageratelimiter.go │ │ ├── permember/ │ │ │ ├── permember.go │ │ │ └── permember_test.go │ │ └── policy_mock.go │ ├── rangeiter/ │ │ ├── dynamic_config_linear_iterator.go │ │ ├── dynamic_config_linear_iterator_test.go │ │ ├── iterator.go │ │ ├── linear_iterator.go │ │ └── linear_iterator_test.go │ ├── reconciliation/ │ │ ├── constants.go │ │ ├── entity/ │ │ │ ├── types.go │ │ │ └── types_test.go │ │ ├── fetcher/ │ │ │ ├── concrete.go │ │ │ ├── concrete_test.go │ │ │ ├── current.go │ │ │ ├── current_test.go │ │ │ ├── timer.go │ │ │ ├── timer_test.go │ │ │ └── types.go │ │ ├── invariant/ │ │ │ ├── collection_enumer_generated.go │ │ │ ├── concrete_execution_exists.go │ │ │ ├── concrete_execution_exists_test.go │ │ │ ├── history_exists.go │ │ │ ├── history_exists_test.go │ │ │ ├── inactive_domain_exists.go │ │ │ ├── inactive_domain_exists_test.go │ │ │ ├── invariant_manager.go │ │ │ ├── invariant_manager_test.go │ │ │ ├── invariant_test_utils.go │ │ │ ├── mocks.go │ │ │ ├── open_current_execution.go │ │ │ ├── open_current_execution_test.go │ │ │ ├── stale_workflow.go │ │ │ ├── stale_workflow_test.go │ │ │ ├── timer_invalid.go │ │ │ ├── timer_invalid_test.go │ │ │ ├── types.go │ │ │ ├── util.go │ │ │ └── util_test.go │ │ └── store/ │ │ ├── blobstoreIterator.go │ │ ├── blobstoreWriter.go │ │ ├── blobstorewriter_test.go │ │ ├── mocks.go │ │ ├── types.go │ │ └── writerIterator_test.go │ ├── resource/ │ │ ├── params.go │ │ ├── resource_impl.go │ │ ├── resource_impl_test.go │ │ ├── resource_mock.go │ │ ├── resource_test_utils.go │ │ └── types.go │ ├── rpc/ │ │ ├── direct_peer_chooser.go │ │ ├── direct_peer_chooser_test.go │ │ ├── dns_updater.go │ │ ├── dns_updater_test.go │ │ ├── factory.go │ │ ├── factory_mock.go │ │ ├── factory_test.go │ │ ├── localip.go │ │ ├── localip_test.go │ │ ├── middleware.go │ │ ├── middleware_test.go │ │ ├── outbounds.go │ │ ├── outbounds_mock.go │ │ ├── outbounds_test.go │ │ ├── params.go │ │ ├── params_test.go │ │ ├── peer_chooser.go │ │ ├── peer_chooser_mock.go │ │ ├── peer_chooser_test.go │ │ ├── rpcfx/ │ │ │ └── rpcfx.go │ │ └── types.go │ ├── rsa.go │ ├── scripting/ │ │ ├── exec.go │ │ └── exec_test.go │ ├── service/ │ │ ├── config.go │ │ ├── metrics.go │ │ ├── name.go │ │ └── name_test.go │ ├── stats/ │ │ ├── interface_mock.go │ │ ├── interfaces.go │ │ ├── stats.go │ │ ├── stats_benchmark_test.go │ │ └── stats_test.go │ ├── syncmap/ │ │ ├── syncmap.go │ │ └── syncmap_test.go │ ├── task/ │ │ ├── fifo_task_scheduler.go │ │ ├── fifo_task_scheduler_options.go │ │ ├── fifo_task_scheduler_test.go │ │ ├── hierarchical_weighted_round_robin_task_pool.go │ │ ├── hierarchical_weighted_round_robin_task_pool_test.go │ │ ├── hierarchical_weighted_round_robin_task_scheduler.go │ │ ├── hierarchical_weighted_round_robin_task_scheduler_test.go │ │ ├── interface.go │ │ ├── interface_mock.go │ │ ├── iwrr_node.go │ │ ├── iwrr_node_test.go │ │ ├── iwrr_schedule.go │ │ ├── iwrr_schedule_test.go │ │ ├── parallel_task_processor.go │ │ ├── parallel_task_processor_test.go │ │ ├── scheduler_options.go │ │ ├── scheduler_options_test.go │ │ ├── sequential_task_processor.go │ │ ├── sequential_task_processor_test.go │ │ ├── ttl_channel.go │ │ ├── ttl_channel_test.go │ │ ├── weighted_channel_pool.go │ │ ├── weighted_channel_pool_test.go │ │ ├── weighted_round_robin_task_scheduler.go │ │ ├── weighted_round_robin_task_scheduler_options.go │ │ └── weighted_round_robin_task_scheduler_test.go │ ├── taskTokenSerializerInterfaces.go │ ├── taskTokenSerializerInterfaces_mock.go │ ├── taskvalidator/ │ │ ├── validateworkflow.go │ │ └── validateworkflow_test.go │ ├── testing/ │ │ ├── allisset.go │ │ ├── allisset_test.go │ │ ├── event_generator.go │ │ ├── generator_interface.go │ │ ├── history_event_test.go │ │ ├── history_event_util.go │ │ └── testdatagen/ │ │ ├── fuzzer.go │ │ └── idlfuzzedtestdata/ │ │ └── history.go │ ├── tokenbucket/ │ │ ├── tb.go │ │ └── tb_test.go │ ├── types/ │ │ ├── admin.go │ │ ├── admin_test.go │ │ ├── caller.go │ │ ├── caller_test.go │ │ ├── configStore.go │ │ ├── configStore_test.go │ │ ├── enums.go │ │ ├── enums_test.go │ │ ├── errors.go │ │ ├── errors_test.go │ │ ├── health.go │ │ ├── history.go │ │ ├── history_test.go │ │ ├── mapper/ │ │ │ ├── errorutils/ │ │ │ │ ├── convert.go │ │ │ │ └── convert_test.go │ │ │ ├── proto/ │ │ │ │ ├── admin.go │ │ │ │ ├── admin_test.go │ │ │ │ ├── api.go │ │ │ │ ├── api_test.go │ │ │ │ ├── enum_test.go │ │ │ │ ├── errors.go │ │ │ │ ├── errors_test.go │ │ │ │ ├── helpers.go │ │ │ │ ├── helpers_test.go │ │ │ │ ├── history.go │ │ │ │ ├── history_test.go │ │ │ │ ├── matching.go │ │ │ │ ├── matching_test.go │ │ │ │ ├── schedule.go │ │ │ │ ├── schedule_test.go │ │ │ │ ├── sharddistributor.go │ │ │ │ ├── sharddistributor_test.go │ │ │ │ ├── shared.go │ │ │ │ └── shared_test.go │ │ │ ├── testutils/ │ │ │ │ ├── common_fuzzers.go │ │ │ │ ├── fuzz_mapper.go │ │ │ │ ├── fuzz_mapper_test.go │ │ │ │ └── fuzz_test_utils.go │ │ │ └── thrift/ │ │ │ ├── admin.go │ │ │ ├── admin_test.go │ │ │ ├── any.go │ │ │ ├── any_test.go │ │ │ ├── configStore.go │ │ │ ├── config_store_test.go │ │ │ ├── errors.go │ │ │ ├── errors_test.go │ │ │ ├── health.go │ │ │ ├── helpers.go │ │ │ ├── helpers_test.go │ │ │ ├── history.go │ │ │ ├── history_test.go │ │ │ ├── matching.go │ │ │ ├── matching_test.go │ │ │ ├── predicate.go │ │ │ ├── predicate_test.go │ │ │ ├── replicator.go │ │ │ ├── replicator_test.go │ │ │ ├── shared.go │ │ │ └── shared_test.go │ │ ├── matching.go │ │ ├── matching_test.go │ │ ├── predicate.go │ │ ├── replicator.go │ │ ├── replicator_test.go │ │ ├── schedule.go │ │ ├── schedule_service.go │ │ ├── schedule_service_test.go │ │ ├── schedule_test.go │ │ ├── sharddistributor.go │ │ ├── sharddistributor_statuses_enumer_generated.go │ │ ├── sharddistributor_test.go │ │ ├── shared.go │ │ ├── shared_test.go │ │ ├── test_util.go │ │ └── testdata/ │ │ ├── common.go │ │ ├── config_store.go │ │ ├── decision.go │ │ ├── domain.go │ │ ├── enum.go │ │ ├── error.go │ │ ├── history.go │ │ ├── queue.go │ │ ├── replication.go │ │ ├── schedule.go │ │ ├── service_admin.go │ │ ├── service_frontend.go │ │ ├── service_history.go │ │ ├── service_matching.go │ │ └── service_sharddistributor.go │ ├── util/ │ │ ├── file_util.go │ │ └── file_util_test.go │ ├── util.go │ ├── util_test.go │ └── visibility/ │ ├── validate_search_attribute_key.go │ └── validate_search_attribute_key_test.go ├── config/ │ ├── base.yaml │ ├── bench/ │ │ ├── base.yaml │ │ ├── basic.json │ │ ├── basic_panic.json │ │ ├── cancellation.json │ │ ├── concurrent_execution.json │ │ ├── cron.json │ │ ├── development.yaml │ │ ├── signal.json │ │ └── timer.json │ ├── canary/ │ │ ├── base.yaml │ │ └── development.yaml │ ├── credentials/ │ │ ├── client.key │ │ ├── keytest │ │ └── keytest.pub │ ├── development.yaml │ ├── development_async_wf_kafka_queue.yaml │ ├── development_es_opensearch.yaml │ ├── development_es_opensearch_migration.yaml │ ├── development_es_v6.yaml │ ├── development_es_v7.yaml │ ├── development_generic_oauth.yaml │ ├── development_http_api.yaml │ ├── development_instance2.yaml │ ├── development_multiple_cassandra.yaml │ ├── development_multiple_mysql.yaml │ ├── development_mysql.yaml │ ├── development_oauth.yaml │ ├── development_pinot.yaml │ ├── development_postgres.yaml │ ├── development_prometheus.yaml │ ├── development_scylla.yaml │ ├── development_sqlite.yaml │ ├── development_tls.yaml │ ├── development_xdc_cluster0.yaml │ ├── development_xdc_cluster1.yaml │ ├── development_xdc_cluster2.yaml │ └── dynamicconfig/ │ ├── README.md │ ├── development.yaml │ ├── development_es.yaml │ ├── development_pinot.yaml │ ├── replication_simulation_activeactive.yml │ ├── replication_simulation_activeactive_child.yml │ ├── replication_simulation_activeactive_cron.yml │ ├── replication_simulation_activeactive_invalid_cluster_attribute.yml │ ├── replication_simulation_activeactive_regional_failover.yml │ ├── replication_simulation_activeactive_regional_failover_start_same_wfid.yml │ ├── replication_simulation_activeactive_regional_failover_start_same_wfid_2.yml │ ├── replication_simulation_activeactive_same_wfid.yml │ ├── replication_simulation_activeactive_same_wfid_signalwithstart.yml │ ├── replication_simulation_activeactive_same_wfid_signalwithstart_delayed.yml │ ├── replication_simulation_activeactive_signalwithstart_terminateifrunning.yml │ ├── replication_simulation_activeactive_start_terminateifrunning.yml │ ├── replication_simulation_activepassive_to_activeactive.yml │ ├── replication_simulation_budget_manager.yml │ ├── replication_simulation_clusterredirection.yml │ ├── replication_simulation_default.yml │ └── replication_simulation_reset.yml ├── docker/ │ ├── README.md │ ├── config/ │ │ ├── bench/ │ │ │ └── development.yaml │ │ └── canary/ │ │ └── development.yaml │ ├── config_template.yaml │ ├── dev/ │ │ ├── cassandra-esv7-kafka.yml │ │ ├── cassandra-opensearch-kafka-migration.yml │ │ ├── cassandra-opensearch-kafka.yml │ │ ├── cassandra-pinot-kafka.yml │ │ ├── cassandra-testing/ │ │ │ └── docker-compose-local-caas-cluster.yaml │ │ ├── cassandra.yml │ │ ├── mongo-esv7-kafka.yml │ │ ├── mysql-esv7-kafka.yml │ │ ├── mysql.yml │ │ └── postgres.yml │ ├── docker-compose-archival-filestore.yml │ ├── docker-compose-async-wf-kafka-v4.yml │ ├── docker-compose-async-wf-kafka.yml │ ├── docker-compose-bench.yml │ ├── docker-compose-canary.yml │ ├── docker-compose-es-v7.yml │ ├── docker-compose-es.yml │ ├── docker-compose-http-api.yml │ ├── docker-compose-multiclusters-cass-mysql-es.yaml │ ├── docker-compose-multiclusters-es.yml │ ├── docker-compose-multiclusters.yml │ ├── docker-compose-mysql.yml │ ├── docker-compose-oauth.yml │ ├── docker-compose-opensearch.yml │ ├── docker-compose-pinot.yml │ ├── docker-compose-postgres.yml │ ├── docker-compose-scylla.yml │ ├── docker-compose-statsd.yml │ ├── docker-compose.yml │ ├── domain/ │ │ ├── cassandra.cql │ │ ├── mysql.sql │ │ └── postgres.sql │ ├── entrypoint.sh │ ├── github_actions/ │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── docker-compose-cassandra-lwt.yml │ │ ├── docker-compose-es7.yml │ │ ├── docker-compose-local-async-wf.yml │ │ ├── docker-compose-local-es7.yml │ │ ├── docker-compose-local-history-simulation.yml │ │ ├── docker-compose-local-matching-simulation.yml │ │ ├── docker-compose-local-pinot.yml │ │ ├── docker-compose-local-replication-simulation.yml │ │ ├── docker-compose-local.yml │ │ ├── docker-compose-opensearch2.yml │ │ ├── docker-compose-pinot.yml │ │ ├── docker-compose.yml │ │ ├── grafana/ │ │ │ ├── grafana.ini │ │ │ └── provisioning/ │ │ │ ├── dashboards/ │ │ │ │ ├── cadence-archival.json │ │ │ │ ├── cadence-client-overall.json │ │ │ │ ├── cadence-frontend.json │ │ │ │ ├── cadence-history.json │ │ │ │ ├── cadence-matching.json │ │ │ │ ├── cadence-persistence.json │ │ │ │ ├── cadence-server.json │ │ │ │ └── default.yaml │ │ │ └── datasources/ │ │ │ └── default.yaml │ │ └── prometheus/ │ │ ├── .gitignore │ │ ├── matching_simulation_prometheus.yml │ │ └── replication_simulation_prometheus.yml │ ├── grafana/ │ │ ├── grafana.ini │ │ └── provisioning/ │ │ ├── dashboards/ │ │ │ ├── cadence-archival.json │ │ │ ├── cadence-client-overall.json │ │ │ ├── cadence-frontend.json │ │ │ ├── cadence-history.json │ │ │ ├── cadence-matching.json │ │ │ ├── cadence-persistence.json │ │ │ ├── cadence-server.json │ │ │ └── default.yaml │ │ └── datasources/ │ │ └── default.yaml │ ├── prometheus/ │ │ └── prometheus.yml │ ├── prometheus_multiclusters/ │ │ └── prometheus.yml │ ├── setup-multiclusters-schema.sh │ ├── start-cadence.sh │ └── start.sh ├── docs/ │ ├── cassandra-executions-table.md │ ├── design/ │ │ ├── 1533-host-specific-tasklist.md │ │ ├── 2215-synchronous-request-reply.md │ │ ├── 2290-cadence-ndc.md │ │ ├── active-active/ │ │ │ └── active-active.md │ │ ├── domain-updates/ │ │ │ └── fencing-tokens.md │ │ ├── graceful-domain-failover/ │ │ │ └── 3051-graceful-domain-failover.md │ │ ├── history-queue-v2/ │ │ │ └── history-queue-v2.md │ │ ├── index.md │ │ └── workflow-shadowing/ │ │ └── 2547-workflow-shadowing.md │ ├── flow.md │ ├── howtos/ │ │ ├── async-api.md │ │ ├── cassandra-fql.md │ │ └── setup-cadence-locally-with-replication.md │ ├── migration/ │ │ ├── cassandra-shard-info.md │ │ └── tasklist-partition-config.md │ ├── non-deterministic-error.md │ ├── persistence.md │ ├── roadmap.md │ ├── scalable_tasklist.md │ ├── setup/ │ │ ├── MYSQL_SETUP.md │ │ └── POSTGRES_SETUP.md │ ├── toc.md │ └── visibility-on-elasticsearch.md ├── environment/ │ ├── env.go │ └── env_test.go ├── go.mod ├── go.sum ├── go.work ├── go.work.sum ├── host/ │ ├── activity_test.go │ ├── archival_test.go │ ├── async_wf_test.go │ ├── cancel_workflow_test.go │ ├── cassandra_lwt_test.go │ ├── cli/ │ │ ├── cassandra/ │ │ │ ├── cassandra_tool_cqlclient_test.go │ │ │ ├── cassandra_tool_setupTask_test.go │ │ │ ├── cassandra_tool_updateTask_test.go │ │ │ ├── cassandra_tool_version_test.go │ │ │ └── utils.go │ │ └── sql/ │ │ ├── cli_test.go │ │ ├── connTest.go │ │ ├── handlerTest.go │ │ ├── setuptaskTest.go │ │ ├── updatetaskTest.go │ │ ├── utils.go │ │ └── versionTest.go │ ├── client.go │ ├── client_integration_test.go │ ├── continue_as_new_test.go │ ├── decision_test.go │ ├── decision_timeout_test.go │ ├── dynamicconfig.go │ ├── elastic_search_test.go │ ├── esutils/ │ │ ├── client_os2.go │ │ ├── client_v6.go │ │ ├── client_v7.go │ │ └── interfaces.go │ ├── flag.go │ ├── integration_test.go │ ├── integration_test_cron.go │ ├── integrationbase.go │ ├── membership_hashring.go │ ├── membership_resolver.go │ ├── ndc/ │ │ ├── integration_test.go │ │ ├── replication_integration_test.go │ │ └── test_suites.go │ ├── onebox.go │ ├── persistence/ │ │ ├── cassandra/ │ │ │ └── cassandra_persistence_test.go │ │ ├── dynamodb/ │ │ │ └── dynamodb_persistence_test.go │ │ ├── mongodb/ │ │ │ └── mongodb_persistence_test.go │ │ ├── mysql/ │ │ │ └── mysql_persistence_test.go │ │ └── postgres/ │ │ └── postgres_persistence_test.go │ ├── pinot_test.go │ ├── pinotutils/ │ │ └── pinotClient.go │ ├── query_workflow_test.go │ ├── reset_workflow_test.go │ ├── retry_policy_workflow_test.go │ ├── service.go │ ├── signal_workflow_test.go │ ├── size_limit_test.go │ ├── task_list_isolation_test.go │ ├── task_list_test.go │ ├── taskpoller.go │ ├── test_suites.go │ ├── testcluster.go │ ├── testdata/ │ │ ├── clientintegrationtestcluster.yaml │ │ ├── dynamicconfig/ │ │ │ ├── integration_queuev2_test.yaml │ │ │ ├── integration_queuev2_with_alert_test.yaml │ │ │ └── integration_test.yaml │ │ ├── es_os2_index_template.json │ │ ├── es_v6_index_template.json │ │ ├── es_v7_index_template.json │ │ ├── integration_async_wf_with_kafka_cluster.yaml │ │ ├── integration_decision_timeout_cluster.yaml │ │ ├── integration_elasticsearch_os2_cluster.yaml │ │ ├── integration_elasticsearch_v6_cluster.yaml │ │ ├── integration_elasticsearch_v7_cluster.yaml │ │ ├── integration_pinot_cluster.yaml │ │ ├── integration_queuev2_cluster.yaml │ │ ├── integration_queuev2_with_alert_cluster.yaml │ │ ├── integration_sizelimit_cluster.yaml │ │ ├── integration_test_cluster.yaml │ │ ├── integration_wfidratelimit_cluster.yaml │ │ ├── ndc_integration_test_clusters.yaml │ │ ├── task_list_test_cluster.yaml │ │ ├── xdc_integration_es_clusters.yaml │ │ └── xdc_integration_test_clusters.yaml │ ├── workflowidratelimit_test.go │ ├── workflowsidinternalratelimit_test.go │ └── xdc/ │ └── elasticsearch_test.go ├── internal/ │ └── tools/ │ ├── go.mod │ ├── go.sum │ ├── go.work │ ├── go.work.sum │ └── tools.go ├── proto/ │ ├── buf.yaml │ ├── internal/ │ │ └── uber/ │ │ └── cadence/ │ │ ├── history/ │ │ │ └── v1/ │ │ │ └── service.proto │ │ ├── indexer/ │ │ │ └── v1/ │ │ │ └── messages.proto │ │ ├── matching/ │ │ │ └── v1/ │ │ │ └── service.proto │ │ ├── sharddistributor/ │ │ │ └── v1/ │ │ │ ├── canary.proto │ │ │ ├── executor.proto │ │ │ └── service.proto │ │ └── shared/ │ │ └── v1/ │ │ ├── any.proto │ │ ├── error.proto │ │ ├── history.proto │ │ ├── tasklist.proto │ │ └── workflow.proto │ └── persistenceblobs/ │ └── v1/ │ ├── gogo.proto │ └── message.proto ├── revive.toml ├── schema/ │ ├── cassandra/ │ │ ├── README.md │ │ ├── cadence/ │ │ │ ├── keyspace.cql │ │ │ ├── schema.cql │ │ │ └── versioned/ │ │ │ ├── s0.0-0.23/ │ │ │ │ ├── data_0_23.cql │ │ │ │ ├── manifest.json │ │ │ │ └── schema_0_23.cql │ │ │ ├── v0.1/ │ │ │ │ ├── base.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.10/ │ │ │ │ ├── event_batch_version.cql │ │ │ │ ├── execution_last_write_version_and_workflow_state.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.11/ │ │ │ │ ├── event_encoding.cql │ │ │ │ ├── history_size.cql │ │ │ │ ├── manifest.json │ │ │ │ ├── sync_activity.cql │ │ │ │ └── workflow_retry.cql │ │ │ ├── v0.12/ │ │ │ │ ├── add_archival_config.cql │ │ │ │ ├── cron.cql │ │ │ │ ├── events_v2.cql │ │ │ │ ├── manifest.json │ │ │ │ ├── signal_count.cql │ │ │ │ └── system_domain_bootstrap.cql │ │ │ ├── v0.13/ │ │ │ │ ├── events_cache.cql │ │ │ │ ├── manifest.json │ │ │ │ └── reset.cql │ │ │ ├── v0.14/ │ │ │ │ ├── last_event_task_id.cql │ │ │ │ ├── manifest.json │ │ │ │ └── task_list.cql │ │ │ ├── v0.15/ │ │ │ │ ├── auto_reset.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.16/ │ │ │ │ ├── decision_scheduled_timestamp.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.17/ │ │ │ │ ├── manifest.json │ │ │ │ ├── search_attr.cql │ │ │ │ └── task_created_time.cql │ │ │ ├── v0.18/ │ │ │ │ ├── activity_last_failure_info.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.19/ │ │ │ │ ├── archival_domain_config.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.2/ │ │ │ │ ├── add_buffered_events.cql │ │ │ │ ├── add_sticky_tasklist.cql │ │ │ │ ├── add_wf_timeout.cql │ │ │ │ ├── fail_decision_mutable_state.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.20/ │ │ │ │ ├── manifest.json │ │ │ │ └── memo.cql │ │ │ ├── v0.21/ │ │ │ │ ├── decision_original_scheduled_timestamp.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.22/ │ │ │ │ ├── activity_last_failure_details.cql │ │ │ │ ├── cluster_replication_level.cql │ │ │ │ ├── manifest.json │ │ │ │ ├── parent_close_policy.cql │ │ │ │ └── request_cancel_signal_batch_event_id.cql │ │ │ ├── v0.23/ │ │ │ │ ├── manifest.json │ │ │ │ ├── queue.cql │ │ │ │ ├── queue_metadata.cql │ │ │ │ ├── system_domain.cql │ │ │ │ └── version_histories.cql │ │ │ ├── v0.24/ │ │ │ │ ├── checksum.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.25/ │ │ │ │ ├── manifest.json │ │ │ │ └── replication_dlq.cql │ │ │ ├── v0.26/ │ │ │ │ ├── domain_dlq_id.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.27/ │ │ │ │ ├── domain_failover_end_time.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.28/ │ │ │ │ ├── manifest.json │ │ │ │ ├── previous_failover_version.cql │ │ │ │ ├── replication_task_creation_time.cql │ │ │ │ └── shard_info_marker.cql │ │ │ ├── v0.29/ │ │ │ │ ├── manifest.json │ │ │ │ └── processing_queue_states.cql │ │ │ ├── v0.3/ │ │ │ │ ├── add_client_version.cql │ │ │ │ ├── add_last_first_event_id.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.30/ │ │ │ │ ├── domain_last_updated_time.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.31/ │ │ │ │ ├── cross_cluster_queue.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.32/ │ │ │ │ ├── config_store.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.33/ │ │ │ │ ├── child_info_domain_id.cql │ │ │ │ ├── manifest.json │ │ │ │ └── target_domain_ids.cql │ │ │ ├── v0.34/ │ │ │ │ ├── manifest.json │ │ │ │ └── workflow_execution_first_run_id.cql │ │ │ ├── v0.35/ │ │ │ │ ├── manifest.json │ │ │ │ └── partition_config.cql │ │ │ ├── v0.36/ │ │ │ │ ├── isolation_groups.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.37/ │ │ │ │ ├── async_workflow_config.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.38/ │ │ │ │ ├── manifest.json │ │ │ │ └── task_list_partition_config.cql │ │ │ ├── v0.39/ │ │ │ │ ├── manifest.json │ │ │ │ └── timestamps.cql │ │ │ ├── v0.4/ │ │ │ │ ├── add_signal_decision.cql │ │ │ │ ├── add_tasklist_kind.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.40/ │ │ │ │ ├── isolation_partition_config.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.41/ │ │ │ │ ├── executions.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.42/ │ │ │ │ ├── domain_active_clusters_config.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.43/ │ │ │ │ ├── manifest.json │ │ │ │ └── workflow_execution_new_fields.cql │ │ │ ├── v0.44/ │ │ │ │ ├── domain_audit_log.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.45/ │ │ │ │ ├── manifest.json │ │ │ │ ├── timer_task.cql │ │ │ │ └── transfer_task.cql │ │ │ ├── v0.46/ │ │ │ │ ├── manifest.json │ │ │ │ └── transfer_task.cql │ │ │ ├── v0.5/ │ │ │ │ ├── add_replication_config.cql │ │ │ │ ├── add_target_child_workflow_only_to_transfer_task.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.6/ │ │ │ │ ├── add_shard_cluster_ack_level.cql │ │ │ │ ├── history_replication_task.cql │ │ │ │ └── manifest.json │ │ │ ├── v0.7/ │ │ │ │ ├── buffered_replication_task.cql │ │ │ │ ├── failover_version_persistence.cql │ │ │ │ ├── manifest.json │ │ │ │ └── retry_policy.cql │ │ │ ├── v0.8/ │ │ │ │ ├── domain_notification.cql │ │ │ │ └── manifest.json │ │ │ └── v0.9/ │ │ │ ├── domain_data.cql │ │ │ ├── manifest.json │ │ │ └── transfer_timestamp.cql │ │ ├── embed.go │ │ ├── version.go │ │ └── visibility/ │ │ ├── keyspace.cql │ │ ├── schema.cql │ │ └── versioned/ │ │ ├── v0.1/ │ │ │ ├── base.cql │ │ │ └── manifest.json │ │ ├── v0.10/ │ │ │ ├── add_execution_fields.cql │ │ │ └── manifest.json │ │ ├── v0.2/ │ │ │ ├── manifest.json │ │ │ └── reduce_open_workflow_tombstones.cql │ │ ├── v0.3/ │ │ │ ├── manifest.json │ │ │ └── sort_by_close_time.cql │ │ ├── v0.4/ │ │ │ ├── add_execution_time.cql │ │ │ ├── add_memo.cql │ │ │ └── manifest.json │ │ ├── v0.5/ │ │ │ ├── add_task_list.cql │ │ │ └── manifest.json │ │ ├── v0.6/ │ │ │ ├── add_is_cron.cql │ │ │ └── manifest.json │ │ ├── v0.7/ │ │ │ ├── add_num_clusters.cql │ │ │ └── manifest.json │ │ ├── v0.8/ │ │ │ ├── add_update_time.cql │ │ │ └── manifest.json │ │ └── v0.9/ │ │ ├── add_shard_id.cql │ │ └── manifest.json │ ├── elasticsearch/ │ │ ├── embed.go │ │ ├── os2/ │ │ │ └── visibility/ │ │ │ └── index_template.json │ │ ├── v6/ │ │ │ └── visibility/ │ │ │ └── index_template.json │ │ └── v7/ │ │ └── visibility/ │ │ └── index_template.json │ ├── mongodb/ │ │ ├── README.md │ │ ├── cadence/ │ │ │ ├── collectionSchema.go │ │ │ ├── schema.json │ │ │ └── versioned/ │ │ │ └── v0.1/ │ │ │ ├── base.json │ │ │ └── manifest.json │ │ └── version.go │ ├── mysql/ │ │ ├── embed.go │ │ ├── v8/ │ │ │ ├── cadence/ │ │ │ │ ├── database.sql │ │ │ │ ├── schema.sql │ │ │ │ └── versioned/ │ │ │ │ ├── v0.1/ │ │ │ │ │ ├── base.sql │ │ │ │ │ └── manifest.json │ │ │ │ ├── v0.2/ │ │ │ │ │ ├── manifest.json │ │ │ │ │ └── queue.sql │ │ │ │ ├── v0.3/ │ │ │ │ │ ├── manifest.json │ │ │ │ │ └── replication_tasks_dlq.sql │ │ │ │ ├── v0.4/ │ │ │ │ │ ├── blob_size.sql │ │ │ │ │ └── manifest.json │ │ │ │ ├── v0.5/ │ │ │ │ │ ├── cross_cluster_table.sql │ │ │ │ │ └── manifest.json │ │ │ │ ├── v0.6/ │ │ │ │ │ ├── cluster_config.sql │ │ │ │ │ └── manifest.json │ │ │ │ └── v0.7/ │ │ │ │ ├── domain_audit_log.sql │ │ │ │ └── manifest.json │ │ │ └── visibility/ │ │ │ ├── database.sql │ │ │ ├── schema.sql │ │ │ └── versioned/ │ │ │ ├── v0.1/ │ │ │ │ ├── base.sql │ │ │ │ └── manifest.json │ │ │ ├── v0.2/ │ │ │ │ ├── add_task_list.sql │ │ │ │ └── manifest.json │ │ │ ├── v0.3/ │ │ │ │ ├── manifest.json │ │ │ │ └── vs_index.sql │ │ │ ├── v0.4/ │ │ │ │ ├── add_is_cron.sql │ │ │ │ └── manifest.json │ │ │ ├── v0.5/ │ │ │ │ ├── add_num_clusters.sql │ │ │ │ └── manifest.json │ │ │ ├── v0.6/ │ │ │ │ ├── add_update_time.sql │ │ │ │ └── manifest.json │ │ │ ├── v0.7/ │ │ │ │ ├── add_shard_id.sql │ │ │ │ └── manifest.json │ │ │ └── v0.8/ │ │ │ ├── add_execution_fields.sql │ │ │ └── manifest.json │ │ └── version.go │ ├── pinot/ │ │ ├── README.md │ │ ├── cadence-visibility-config.json │ │ ├── cadence-visibility-schema.json │ │ └── create_pinot_table.sh │ ├── postgres/ │ │ ├── cadence/ │ │ │ ├── database.sql │ │ │ ├── schema.sql │ │ │ └── versioned/ │ │ │ ├── v0.1/ │ │ │ │ ├── base.sql │ │ │ │ └── manifest.json │ │ │ ├── v0.2/ │ │ │ │ ├── manifest.json │ │ │ │ └── queue.sql │ │ │ ├── v0.3/ │ │ │ │ ├── manifest.json │ │ │ │ └── replication_tasks_dlq.sql │ │ │ ├── v0.4/ │ │ │ │ ├── cross_cluster_table.sql │ │ │ │ └── manifest.json │ │ │ ├── v0.5/ │ │ │ │ ├── cluster_config.sql │ │ │ │ └── manifest.json │ │ │ ├── v0.6/ │ │ │ │ ├── extend_workflow_id_length.sql │ │ │ │ └── manifest.json │ │ │ └── v0.7/ │ │ │ ├── domain_audit_log.sql │ │ │ └── manifest.json │ │ ├── embed.go │ │ ├── version.go │ │ └── visibility/ │ │ ├── database.sql │ │ ├── schema.sql │ │ └── versioned/ │ │ ├── v0.1/ │ │ │ ├── base.sql │ │ │ └── manifest.json │ │ ├── v0.2/ │ │ │ ├── add_task_list.sql │ │ │ └── manifest.json │ │ ├── v0.3/ │ │ │ ├── manifest.json │ │ │ └── vs_index.sql │ │ ├── v0.4/ │ │ │ ├── add_is_cron.sql │ │ │ └── manifest.json │ │ ├── v0.5/ │ │ │ ├── manifest.json │ │ │ └── num_clusters.sql │ │ ├── v0.6/ │ │ │ ├── add_update_time.sql │ │ │ └── manifest.json │ │ ├── v0.7/ │ │ │ ├── add_shard_id.sql │ │ │ └── manifest.json │ │ ├── v0.8/ │ │ │ ├── extend_workflow_id_length.sql │ │ │ └── manifest.json │ │ └── v0.9/ │ │ ├── add_execution_fields.sql │ │ └── manifest.json │ └── sqlite/ │ ├── cadence/ │ │ ├── schema.sql │ │ └── versioned/ │ │ └── v0.1/ │ │ ├── base.sql │ │ └── manifest.json │ ├── embed.go │ ├── version.go │ └── visibility/ │ ├── schema.sql │ └── versioned/ │ ├── v0.1/ │ │ ├── base.sql │ │ └── manifest.json │ └── v0.2/ │ ├── add_execution_fields.sql │ └── manifest.json ├── scripts/ │ ├── build-with-ldflags.sh │ ├── check-go-toolchain.sh │ ├── check-gomod-version.sh │ ├── docker-build.sh │ ├── generate_cluster_attributes.sh │ ├── get-ldflags.sh │ ├── github_actions/ │ │ ├── gen_coverage_metadata.sh │ │ └── golint.sh │ ├── run_cass_and_test.sh │ ├── run_several_instances.sh │ ├── test_multicluster_domain_workflow.sh │ └── travis/ │ ├── install-xdc-deps.sh │ └── setup-mysql.sh ├── service/ │ ├── frontend/ │ │ ├── admin/ │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ ├── interface.go │ │ │ └── interface_mock.go │ │ ├── api/ │ │ │ ├── domain_handlers.go │ │ │ ├── domain_handlers_test.go │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ ├── interface.go │ │ │ ├── interface_mock.go │ │ │ ├── list_workflow_handlers.go │ │ │ ├── producer_manager.go │ │ │ ├── producer_manager_mock.go │ │ │ ├── producer_manager_test.go │ │ │ ├── refresh_workflow_tasks.go │ │ │ ├── refresh_workflow_tasks_test.go │ │ │ ├── request_validator.go │ │ │ ├── request_validator_mock.go │ │ │ ├── request_validator_test.go │ │ │ ├── shutting_down_test.go │ │ │ ├── task_list_handlers.go │ │ │ └── task_list_handlers_test.go │ │ ├── config/ │ │ │ ├── config.go │ │ │ └── config_test.go │ │ ├── service.go │ │ ├── templates/ │ │ │ ├── accesscontrolled.tmpl │ │ │ ├── clusterredirection.tmpl │ │ │ ├── metered.tmpl │ │ │ ├── ratelimited.tmpl │ │ │ └── versioncheck.tmpl │ │ ├── validate/ │ │ │ └── errors.go │ │ └── wrappers/ │ │ ├── accesscontrolled/ │ │ │ ├── access_controlled.go │ │ │ ├── access_controlled_test.go │ │ │ ├── admin_generated.go │ │ │ └── api_generated.go │ │ ├── clusterredirection/ │ │ │ ├── api_generated.go │ │ │ ├── api_test.go │ │ │ ├── callwrappers.go │ │ │ ├── policy.go │ │ │ ├── policy_mock.go │ │ │ ├── policy_test.go │ │ │ ├── utils.go │ │ │ └── utils_test.go │ │ ├── grpc/ │ │ │ ├── admin_generated.go │ │ │ ├── api_generated.go │ │ │ └── share.go │ │ ├── metered/ │ │ │ ├── api_generated.go │ │ │ ├── metered.go │ │ │ └── metered_test.go │ │ ├── ratelimited/ │ │ │ ├── api_generated.go │ │ │ ├── ratelimit.go │ │ │ └── ratelimit_test.go │ │ ├── thrift/ │ │ │ ├── admin_generated.go │ │ │ ├── admin_handler_test.go │ │ │ ├── api_generated.go │ │ │ ├── api_handler_test.go │ │ │ └── constructor.go │ │ └── versioncheck/ │ │ └── api_generated.go │ ├── history/ │ │ ├── common/ │ │ │ └── type.go │ │ ├── config/ │ │ │ ├── config.go │ │ │ └── config_test.go │ │ ├── constants/ │ │ │ ├── constants.go │ │ │ └── test_constants.go │ │ ├── decision/ │ │ │ ├── checker.go │ │ │ ├── checker_test.go │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ ├── task_handler.go │ │ │ └── task_handler_test.go │ │ ├── engine/ │ │ │ ├── engineimpl/ │ │ │ │ ├── describe_mutable_state.go │ │ │ │ ├── describe_mutable_state_test.go │ │ │ │ ├── describe_queues.go │ │ │ │ ├── describe_workflow_execution.go │ │ │ │ ├── describe_workflow_execution_test.go │ │ │ │ ├── dlq_operations.go │ │ │ │ ├── get_replication_messages.go │ │ │ │ ├── history_engine.go │ │ │ │ ├── history_engine2_test.go │ │ │ │ ├── history_engine3_eventsv2_test.go │ │ │ │ ├── history_engine_start_test.go │ │ │ │ ├── history_engine_test.go │ │ │ │ ├── notify_tasks.go │ │ │ │ ├── poll_mutable_state.go │ │ │ │ ├── query_workflow.go │ │ │ │ ├── reapply_events.go │ │ │ │ ├── record_activity_task_started.go │ │ │ │ ├── record_child_execution_completed.go │ │ │ │ ├── record_decision_task_started.go │ │ │ │ ├── refresh_workflow_tasks.go │ │ │ │ ├── refresh_workflow_tasks_test.go │ │ │ │ ├── register_domain_failover_callback.go │ │ │ │ ├── register_domain_failover_callback_test.go │ │ │ │ ├── remove_signal_mutable_state.go │ │ │ │ ├── request_cancel_workflow_execution.go │ │ │ │ ├── reset_queues.go │ │ │ │ ├── reset_sticky_tasklist.go │ │ │ │ ├── reset_sticky_tasklist_test.go │ │ │ │ ├── reset_workflow_execution.go │ │ │ │ ├── reset_workflow_execution_test.go │ │ │ │ ├── respond_activity_task_canceled.go │ │ │ │ ├── respond_activity_task_completed.go │ │ │ │ ├── respond_activity_task_failed.go │ │ │ │ ├── respond_activity_task_heartbeat.go │ │ │ │ ├── respond_decision_task_completed.go │ │ │ │ ├── respond_decision_task_failed.go │ │ │ │ ├── signal_workflow_execution.go │ │ │ │ ├── start_workflow_execution.go │ │ │ │ ├── start_workflow_execution_test.go │ │ │ │ ├── terminate_workflow_execution.go │ │ │ │ └── terminate_workflow_execution_test.go │ │ │ ├── interface.go │ │ │ ├── interface_mock.go │ │ │ └── testdata/ │ │ │ └── engine_for_tests.go │ │ ├── events/ │ │ │ ├── blob.go │ │ │ ├── blob_test.go │ │ │ ├── cache.go │ │ │ ├── cache_mock.go │ │ │ ├── cache_test.go │ │ │ ├── notifier.go │ │ │ ├── notifier_mock.go │ │ │ └── notifier_test.go │ │ ├── execution/ │ │ │ ├── cache.go │ │ │ ├── cache_mock.go │ │ │ ├── cache_test.go │ │ │ ├── checksum.go │ │ │ ├── context.go │ │ │ ├── context_mock.go │ │ │ ├── context_test.go │ │ │ ├── context_util.go │ │ │ ├── context_util_test.go │ │ │ ├── history_builder.go │ │ │ ├── history_builder_test.go │ │ │ ├── integrity.go │ │ │ ├── integrity_test.go │ │ │ ├── mutable_state.go │ │ │ ├── mutable_state_builder.go │ │ │ ├── mutable_state_builder_add_continue_as_new_event_test.go │ │ │ ├── mutable_state_builder_methods_activity.go │ │ │ ├── mutable_state_builder_methods_activity_test.go │ │ │ ├── mutable_state_builder_methods_cancellation.go │ │ │ ├── mutable_state_builder_methods_child_workflow.go │ │ │ ├── mutable_state_builder_methods_child_workflow_test.go │ │ │ ├── mutable_state_builder_methods_completed.go │ │ │ ├── mutable_state_builder_methods_decision.go │ │ │ ├── mutable_state_builder_methods_decision_test.go │ │ │ ├── mutable_state_builder_methods_signal.go │ │ │ ├── mutable_state_builder_methods_signal_test.go │ │ │ ├── mutable_state_builder_methods_started.go │ │ │ ├── mutable_state_builder_methods_timedout.go │ │ │ ├── mutable_state_builder_methods_timer.go │ │ │ ├── mutable_state_builder_methods_timer_test.go │ │ │ ├── mutable_state_builder_test.go │ │ │ ├── mutable_state_decision_task_manager.go │ │ │ ├── mutable_state_decision_task_manager_mock.go │ │ │ ├── mutable_state_decision_task_manager_test.go │ │ │ ├── mutable_state_mock.go │ │ │ ├── mutable_state_task_generator.go │ │ │ ├── mutable_state_task_generator_mock.go │ │ │ ├── mutable_state_task_generator_test.go │ │ │ ├── mutable_state_task_refresher.go │ │ │ ├── mutable_state_task_refresher_mock.go │ │ │ ├── mutable_state_task_refresher_test.go │ │ │ ├── mutable_state_util.go │ │ │ ├── mutable_state_util_test.go │ │ │ ├── mutable_state_util_test_helpers.go │ │ │ ├── retry.go │ │ │ ├── retry_test.go │ │ │ ├── state_builder.go │ │ │ ├── state_builder_mock.go │ │ │ ├── state_builder_test.go │ │ │ ├── state_rebuilder.go │ │ │ ├── state_rebuilder_mock.go │ │ │ ├── state_rebuilder_test.go │ │ │ ├── timer_sequence.go │ │ │ ├── timer_sequence_mock.go │ │ │ ├── timer_sequence_test.go │ │ │ ├── workflow.go │ │ │ ├── workflow_execution_util.go │ │ │ ├── workflow_mock.go │ │ │ └── workflow_test.go │ │ ├── failover/ │ │ │ ├── coordinator.go │ │ │ ├── coordinator_mock.go │ │ │ ├── coordinator_test.go │ │ │ ├── marker_notifier.go │ │ │ ├── marker_notifier_mock.go │ │ │ └── marker_notifier_test.go │ │ ├── handler/ │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ ├── interface.go │ │ │ └── interface_mock.go │ │ ├── lookup/ │ │ │ ├── lookup.go │ │ │ └── lookup_test.go │ │ ├── ndc/ │ │ │ ├── activity_replicator.go │ │ │ ├── activity_replicator_mock.go │ │ │ ├── activity_replicator_test.go │ │ │ ├── branch_manager.go │ │ │ ├── branch_manager_mock.go │ │ │ ├── branch_manager_test.go │ │ │ ├── conflict_resolver.go │ │ │ ├── conflict_resolver_mock.go │ │ │ ├── conflict_resolver_test.go │ │ │ ├── events_reapplier.go │ │ │ ├── events_reapplier_mock.go │ │ │ ├── events_reapplier_test.go │ │ │ ├── existing_workflow_transaction_manager.go │ │ │ ├── existing_workflow_transaction_manager_mock.go │ │ │ ├── existing_workflow_transaction_manager_test.go │ │ │ ├── history_replicator.go │ │ │ ├── history_replicator_test.go │ │ │ ├── new_workflow_transaction_mamanger_mock.go │ │ │ ├── new_workflow_transaction_manager.go │ │ │ ├── new_workflow_transaction_manager_test.go │ │ │ ├── replication_task.go │ │ │ ├── replication_task_mock.go │ │ │ ├── replication_task_test.go │ │ │ ├── transaction_manager.go │ │ │ ├── transaction_manager_mock.go │ │ │ ├── transaction_manager_test.go │ │ │ ├── workflow_resetter.go │ │ │ ├── workflow_resetter_mock.go │ │ │ └── workflow_resetter_test.go │ │ ├── query/ │ │ │ ├── query.go │ │ │ ├── query_test.go │ │ │ ├── registry.go │ │ │ ├── registry_mock.go │ │ │ └── registry_test.go │ │ ├── queue/ │ │ │ ├── action.go │ │ │ ├── constants.go │ │ │ ├── domain_filter.go │ │ │ ├── domain_filter_test.go │ │ │ ├── factory.go │ │ │ ├── factory_mock.go │ │ │ ├── factory_test.go │ │ │ ├── interface.go │ │ │ ├── interface_mock.go │ │ │ ├── main_test.go │ │ │ ├── processing_queue.go │ │ │ ├── processing_queue_collection.go │ │ │ ├── processing_queue_collection_test.go │ │ │ ├── processing_queue_state.go │ │ │ ├── processing_queue_test.go │ │ │ ├── processor_base.go │ │ │ ├── processor_base_test.go │ │ │ ├── processor_options.go │ │ │ ├── queue_processor_util.go │ │ │ ├── queue_processor_util_test.go │ │ │ ├── split_policy.go │ │ │ ├── split_policy_test.go │ │ │ ├── task_allocator.go │ │ │ ├── task_allocator_test.go │ │ │ ├── timer_queue_active_processor.go │ │ │ ├── timer_queue_failover_processor.go │ │ │ ├── timer_queue_processor.go │ │ │ ├── timer_queue_processor_base.go │ │ │ ├── timer_queue_processor_base_test.go │ │ │ ├── timer_queue_standby_processor.go │ │ │ ├── transfer_queue_processor.go │ │ │ ├── transfer_queue_processor_base.go │ │ │ ├── transfer_queue_processor_base_test.go │ │ │ ├── transfer_queue_processor_test.go │ │ │ ├── transfer_queue_validator.go │ │ │ └── transfer_queue_validator_test.go │ │ ├── queuev2/ │ │ │ ├── alert.go │ │ │ ├── convert.go │ │ │ ├── convert_test.go │ │ │ ├── interface.go │ │ │ ├── mitigator.go │ │ │ ├── mitigator_mock.go │ │ │ ├── mitigator_test.go │ │ │ ├── monitor.go │ │ │ ├── monitor_mock.go │ │ │ ├── monitor_test.go │ │ │ ├── pause_controller.go │ │ │ ├── pause_controller_mock.go │ │ │ ├── pause_controller_test.go │ │ │ ├── pending_task_tracker.go │ │ │ ├── pending_task_tracker_mock.go │ │ │ ├── pending_task_tracker_test.go │ │ │ ├── predicate.go │ │ │ ├── predicate_mock.go │ │ │ ├── predicate_operation.go │ │ │ ├── predicate_operation_test.go │ │ │ ├── predicate_test.go │ │ │ ├── queue_base.go │ │ │ ├── queue_base_test.go │ │ │ ├── queue_immediate.go │ │ │ ├── queue_immediate_test.go │ │ │ ├── queue_reader.go │ │ │ ├── queue_reader_mock.go │ │ │ ├── queue_reader_test.go │ │ │ ├── queue_scheduled.go │ │ │ ├── queue_scheduled_test.go │ │ │ ├── queue_state.go │ │ │ ├── queue_state_test.go │ │ │ ├── range.go │ │ │ ├── range_test.go │ │ │ ├── timer_queue_factory.go │ │ │ ├── timer_queue_factory_test.go │ │ │ ├── transfer_queue_factory.go │ │ │ ├── transfer_queue_factory_test.go │ │ │ ├── virtual_queue.go │ │ │ ├── virtual_queue_manager.go │ │ │ ├── virtual_queue_manager_mock.go │ │ │ ├── virtual_queue_manager_test.go │ │ │ ├── virtual_queue_mock.go │ │ │ ├── virtual_queue_test.go │ │ │ ├── virtual_slice.go │ │ │ ├── virtual_slice_mock.go │ │ │ └── virtual_slice_test.go │ │ ├── replication/ │ │ │ ├── dlq_handler.go │ │ │ ├── dlq_handler_test.go │ │ │ ├── dynamic_task_batch_sizer.go │ │ │ ├── dynamic_task_batch_sizer_test.go │ │ │ ├── metrics_emitter.go │ │ │ ├── metrics_emitter_test.go │ │ │ ├── task_ack_manager.go │ │ │ ├── task_ack_manager_test.go │ │ │ ├── task_executor.go │ │ │ ├── task_executor_mock.go │ │ │ ├── task_executor_test.go │ │ │ ├── task_fetcher.go │ │ │ ├── task_fetcher_mock.go │ │ │ ├── task_fetcher_test.go │ │ │ ├── task_hydrator.go │ │ │ ├── task_hydrator_test.go │ │ │ ├── task_processor.go │ │ │ ├── task_processor_test.go │ │ │ ├── task_reader.go │ │ │ ├── task_reader_test.go │ │ │ ├── task_store.go │ │ │ └── task_store_test.go │ │ ├── reset/ │ │ │ ├── resetter.go │ │ │ ├── resetter_mock.go │ │ │ └── resetter_test.go │ │ ├── resource/ │ │ │ ├── resource.go │ │ │ ├── resource_mock.go │ │ │ └── resource_test_utils.go │ │ ├── service.go │ │ ├── shard/ │ │ │ ├── context.go │ │ │ ├── context_mock.go │ │ │ ├── context_test.go │ │ │ ├── context_test_utils.go │ │ │ ├── controller.go │ │ │ ├── controller_benchmark_test.go │ │ │ ├── controller_mock.go │ │ │ └── controller_test.go │ │ ├── simulation/ │ │ │ └── event.go │ │ ├── task/ │ │ │ ├── constants.go │ │ │ ├── event_logger.go │ │ │ ├── event_logger_test.go │ │ │ ├── executor_wrapper.go │ │ │ ├── executor_wrapper_test.go │ │ │ ├── interface.go │ │ │ ├── interface_mock.go │ │ │ ├── priority_assigner.go │ │ │ ├── priority_assigner_test.go │ │ │ ├── processor.go │ │ │ ├── processor_test.go │ │ │ ├── rate_limited_processor.go │ │ │ ├── rate_limited_processor_test.go │ │ │ ├── redispatcher.go │ │ │ ├── redispatcher_test.go │ │ │ ├── rescheduler.go │ │ │ ├── rescheduler_test.go │ │ │ ├── standby_task_util.go │ │ │ ├── standby_task_util_test.go │ │ │ ├── task.go │ │ │ ├── task_rate_limiter.go │ │ │ ├── task_rate_limiter_mock.go │ │ │ ├── task_rate_limiter_test.go │ │ │ ├── task_test.go │ │ │ ├── task_util.go │ │ │ ├── task_util_test.go │ │ │ ├── timer_active_task_executor.go │ │ │ ├── timer_active_task_executor_test.go │ │ │ ├── timer_standby_task_executor.go │ │ │ ├── timer_standby_task_executor_test.go │ │ │ ├── timer_task_executor_base.go │ │ │ ├── timer_task_executor_base_test.go │ │ │ ├── transfer_active_task_executor.go │ │ │ ├── transfer_active_task_executor_test.go │ │ │ ├── transfer_standby_task_executor.go │ │ │ ├── transfer_standby_task_executor_test.go │ │ │ ├── transfer_task_executor_base.go │ │ │ └── transfer_task_executor_base_test.go │ │ ├── templates/ │ │ │ └── ratelimited.tmpl │ │ ├── testing/ │ │ │ ├── events_util.go │ │ │ └── workflow_util.go │ │ ├── workflow/ │ │ │ ├── context.go │ │ │ ├── errors.go │ │ │ ├── util.go │ │ │ └── util_test.go │ │ ├── workflowcache/ │ │ │ ├── cache.go │ │ │ ├── cache_mock.go │ │ │ ├── cache_test.go │ │ │ ├── metrics.go │ │ │ └── metrics_test.go │ │ └── wrappers/ │ │ ├── grpc/ │ │ │ ├── grpc_handler.go │ │ │ └── grpc_handler_generated.go │ │ ├── ratelimited/ │ │ │ ├── handler_generated.go │ │ │ ├── handler_generated_test.go │ │ │ ├── ratelimit.go │ │ │ └── ratelimit_test.go │ │ └── thrift/ │ │ ├── thrift_handler.go │ │ ├── thrift_handler_generated.go │ │ └── thrift_handler_test.go │ ├── matching/ │ │ ├── config/ │ │ │ ├── config.go │ │ │ └── config_test.go │ │ ├── event/ │ │ │ └── logger.go │ │ ├── handler/ │ │ │ ├── context.go │ │ │ ├── context_test.go │ │ │ ├── engine.go │ │ │ ├── engine_integration_test.go │ │ │ ├── engine_test.go │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ ├── interfaces.go │ │ │ ├── interfaces_mock.go │ │ │ ├── membership.go │ │ │ └── membership_test.go │ │ ├── liveness/ │ │ │ ├── liveness.go │ │ │ └── liveness_test.go │ │ ├── poller/ │ │ │ ├── manager.go │ │ │ └── manager_test.go │ │ ├── service.go │ │ ├── tasklist/ │ │ │ ├── adaptive_scaler.go │ │ │ ├── adaptive_scaler_test.go │ │ │ ├── db.go │ │ │ ├── forwarder.go │ │ │ ├── forwarder_test.go │ │ │ ├── identifier.go │ │ │ ├── identifier_test.go │ │ │ ├── interfaces.go │ │ │ ├── interfaces_mock.go │ │ │ ├── isolation_balancer.go │ │ │ ├── isolation_balancer_test.go │ │ │ ├── matcher.go │ │ │ ├── matcher_test.go │ │ │ ├── shard_processor.go │ │ │ ├── shard_processor_factory.go │ │ │ ├── shard_processor_test.go │ │ │ ├── task.go │ │ │ ├── task_completer.go │ │ │ ├── task_completer_test.go │ │ │ ├── task_gc.go │ │ │ ├── task_list_limiter.go │ │ │ ├── task_list_limiter_test.go │ │ │ ├── task_list_manager.go │ │ │ ├── task_list_manager_test.go │ │ │ ├── task_list_registry.go │ │ │ ├── task_list_registry_test.go │ │ │ ├── task_reader.go │ │ │ ├── task_reader_test.go │ │ │ ├── task_test.go │ │ │ ├── task_writer.go │ │ │ └── testing.go │ │ └── wrappers/ │ │ ├── grpc/ │ │ │ ├── grpc_handler.go │ │ │ └── grpc_handler_generated.go │ │ └── thrift/ │ │ ├── thrift_handler.go │ │ ├── thrift_handler_generated.go │ │ └── thrift_handler_test.go │ ├── sharddistributor/ │ │ ├── canary/ │ │ │ ├── config/ │ │ │ │ └── config.go │ │ │ ├── executors/ │ │ │ │ ├── executors.go │ │ │ │ └── executors_test.go │ │ │ ├── externalshardassignment/ │ │ │ │ ├── shardassigner.go │ │ │ │ └── shardassigner_test.go │ │ │ ├── factory/ │ │ │ │ ├── factory.go │ │ │ │ └── factory_test.go │ │ │ ├── handler/ │ │ │ │ ├── ping_handler.go │ │ │ │ └── ping_handler_test.go │ │ │ ├── module.go │ │ │ ├── module_test.go │ │ │ ├── pinger/ │ │ │ │ ├── canary_client_mock.go │ │ │ │ ├── pingAndLog.go │ │ │ │ ├── pinger.go │ │ │ │ └── pinger_test.go │ │ │ ├── processor/ │ │ │ │ ├── shardprocessor.go │ │ │ │ └── shardprocessor_test.go │ │ │ ├── processorephemeral/ │ │ │ │ ├── canary_client_mock_test.go │ │ │ │ ├── shardcreator.go │ │ │ │ ├── shardcreator_test.go │ │ │ │ ├── shardprocessor.go │ │ │ │ └── shardprocessor_test.go │ │ │ ├── sharddistributorclient/ │ │ │ │ └── shardDistributorClient.go │ │ │ └── sharddistributorexecutorclient/ │ │ │ └── shardDistributorExecutorClient.go │ │ ├── client/ │ │ │ ├── clientcommon/ │ │ │ │ ├── config.go │ │ │ │ ├── config_test.go │ │ │ │ ├── constants.go │ │ │ │ ├── drain_observer.go │ │ │ │ └── drain_observer_mock.go │ │ │ ├── executorclient/ │ │ │ │ ├── client.go │ │ │ │ ├── client_test.go │ │ │ │ ├── clientimpl.go │ │ │ │ ├── clientimpl_test.go │ │ │ │ ├── interface_mock.go │ │ │ │ ├── meteredClientDecorater.go │ │ │ │ ├── metricsconstants/ │ │ │ │ │ └── metrics.go │ │ │ │ ├── noop.go │ │ │ │ ├── noop_test.go │ │ │ │ ├── syncgeneric/ │ │ │ │ │ ├── map.go │ │ │ │ │ └── map_test.go │ │ │ │ └── yarpc_client_mock.go │ │ │ └── spectatorclient/ │ │ │ ├── client.go │ │ │ ├── clientimpl.go │ │ │ ├── clientimpl_test.go │ │ │ ├── interface_mock.go │ │ │ ├── metricsconstants/ │ │ │ │ └── metrics.go │ │ │ ├── peer_chooser.go │ │ │ ├── peer_chooser_test.go │ │ │ └── sync/ │ │ │ ├── resettable_signal.go │ │ │ └── resettable_signal_test.go │ │ ├── config/ │ │ │ ├── config.go │ │ │ ├── config_test.go │ │ │ └── configtest/ │ │ │ └── config.go │ │ ├── handler/ │ │ │ ├── batcher.go │ │ │ ├── batcher_test.go │ │ │ ├── ephemeral_assigner.go │ │ │ ├── ephemeral_assigner_test.go │ │ │ ├── executor.go │ │ │ ├── executor_test.go │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ ├── interfaces.go │ │ │ └── interfaces_mock.go │ │ ├── leader/ │ │ │ ├── election/ │ │ │ │ ├── election.go │ │ │ │ ├── election_mock.go │ │ │ │ └── election_test.go │ │ │ ├── namespace/ │ │ │ │ ├── manager.go │ │ │ │ └── manager_test.go │ │ │ └── process/ │ │ │ ├── process_mock.go │ │ │ ├── processor.go │ │ │ └── processor_test.go │ │ ├── sharddistributorfx/ │ │ │ ├── fx_test.go │ │ │ └── sharddistributorfx.go │ │ ├── sharddistributorspectatorclient/ │ │ │ └── factory/ │ │ │ ├── meteredClientDecorater.go │ │ │ └── sharddistributorspectatorclient.go │ │ ├── store/ │ │ │ ├── etcd/ │ │ │ │ ├── etcdclient/ │ │ │ │ │ ├── client.go │ │ │ │ │ └── client_mock.go │ │ │ │ ├── etcdkeys/ │ │ │ │ │ ├── etcdkeys.go │ │ │ │ │ └── etcdkeys_test.go │ │ │ │ ├── etcdtypes/ │ │ │ │ │ ├── state.go │ │ │ │ │ ├── state_test.go │ │ │ │ │ ├── time.go │ │ │ │ │ └── time_test.go │ │ │ │ ├── executorstore/ │ │ │ │ │ ├── client.go │ │ │ │ │ ├── client_test.go │ │ │ │ │ ├── common/ │ │ │ │ │ │ ├── compression.go │ │ │ │ │ │ └── compression_test.go │ │ │ │ │ ├── etcdstore.go │ │ │ │ │ ├── etcdstore_test.go │ │ │ │ │ ├── executorstore_mock.go │ │ │ │ │ ├── module.go │ │ │ │ │ └── shardcache/ │ │ │ │ │ ├── namespaceshardcache.go │ │ │ │ │ ├── namespaceshardcache_test.go │ │ │ │ │ ├── pubsub.go │ │ │ │ │ ├── pubsub_test.go │ │ │ │ │ ├── shardcache.go │ │ │ │ │ └── shardcache_test.go │ │ │ │ ├── leaderstore/ │ │ │ │ │ ├── etcdleaderstore.go │ │ │ │ │ └── etcdleaderstore_test.go │ │ │ │ ├── module.go │ │ │ │ └── testhelper/ │ │ │ │ └── testhelper.go │ │ │ ├── leaderstore.go │ │ │ ├── leaderstore_mock.go │ │ │ ├── state.go │ │ │ ├── state_test.go │ │ │ ├── store.go │ │ │ ├── store_mock.go │ │ │ └── wrappers/ │ │ │ ├── metered/ │ │ │ │ ├── base.go │ │ │ │ ├── metered_test.go │ │ │ │ └── store_generated.go │ │ │ └── templates/ │ │ │ └── metered.tmpl │ │ ├── templates/ │ │ │ └── metered.tmpl │ │ └── wrappers/ │ │ ├── grpc/ │ │ │ ├── grpc_executor_generated.go │ │ │ ├── grpc_handler.go │ │ │ └── grpc_handler_generated.go │ │ └── metered/ │ │ ├── api_generated.go │ │ ├── executor_generated.go │ │ ├── metered.go │ │ └── metered_test.go │ ├── templates/ │ │ ├── grpc.tmpl │ │ └── thrift.tmpl │ └── worker/ │ ├── README.md │ ├── archiver/ │ │ ├── activities.go │ │ ├── activities_test.go │ │ ├── client.go │ │ ├── client_mock.go │ │ ├── client_test.go │ │ ├── client_worker.go │ │ ├── handler.go │ │ ├── handler_mock.go │ │ ├── handler_test.go │ │ ├── pump.go │ │ ├── pump_mock.go │ │ ├── pump_test.go │ │ ├── replay_metrics_client.go │ │ ├── testdata/ │ │ │ └── archival_workflow_history_v1.json │ │ ├── util.go │ │ ├── util_test.go │ │ ├── workflow.go │ │ └── workflow_test.go │ ├── asyncworkflow/ │ │ ├── async_workflow_consumer_manager.go │ │ └── async_workflow_consumer_manager_test.go │ ├── batcher/ │ │ ├── batcher.go │ │ ├── batcher_test.go │ │ ├── entities.go │ │ ├── workflow.go │ │ ├── workflow_retry_test.go │ │ └── workflow_test.go │ ├── diagnostics/ │ │ ├── activities.go │ │ ├── activities_test.go │ │ ├── analytics/ │ │ │ ├── emitter.go │ │ │ ├── emitter_test.go │ │ │ ├── interface.go │ │ │ └── types.go │ │ ├── invariant/ │ │ │ ├── failure/ │ │ │ │ ├── failure.go │ │ │ │ ├── failure_test.go │ │ │ │ └── types.go │ │ │ ├── interface.go │ │ │ ├── retry/ │ │ │ │ ├── retry.go │ │ │ │ ├── retry_test.go │ │ │ │ └── types.go │ │ │ └── timeout/ │ │ │ ├── timeout.go │ │ │ ├── timeout_test.go │ │ │ ├── timeout_utils.go │ │ │ └── types.go │ │ ├── module.go │ │ ├── module_test.go │ │ ├── parent_workflow.go │ │ ├── workflow.go │ │ └── workflow_test.go │ ├── domaindeprecation/ │ │ ├── activities.go │ │ ├── activities_test.go │ │ ├── entities.go │ │ ├── helpers.go │ │ ├── module.go │ │ ├── module_test.go │ │ ├── workflow.go │ │ └── workflow_test.go │ ├── esanalyzer/ │ │ ├── analyzer.go │ │ ├── analyzer_test.go │ │ ├── domainWorkflowTypeCountWorkflow.go │ │ └── workflow.go │ ├── failovermanager/ │ │ ├── rebalance_workflow.go │ │ ├── rebalance_workflow_test.go │ │ ├── starter.go │ │ ├── workflow.go │ │ └── workflow_test.go │ ├── indexer/ │ │ ├── esProcessor.go │ │ ├── esProcessor_test.go │ │ ├── indexer.go │ │ ├── indexer_mock.go │ │ ├── indexer_test.go │ │ └── migration_indexer.go │ ├── parentclosepolicy/ │ │ ├── client.go │ │ ├── client_mock.go │ │ ├── processor.go │ │ └── workflow.go │ ├── replicator/ │ │ ├── domain_replication_processor.go │ │ ├── domain_replication_processor_test.go │ │ └── replicator.go │ ├── scanner/ │ │ ├── README.md │ │ ├── data_corruption_workflow.go │ │ ├── data_corruption_workflow_test.go │ │ ├── executions/ │ │ │ ├── concrete_execution.go │ │ │ ├── concrete_execution_test.go │ │ │ ├── current_execution.go │ │ │ ├── current_test.go │ │ │ ├── scantype_enumer_generated.go │ │ │ ├── types.go │ │ │ └── workflows_test.go │ │ ├── executor/ │ │ │ ├── executor.go │ │ │ ├── executor_test.go │ │ │ └── runq.go │ │ ├── history/ │ │ │ ├── scavenger.go │ │ │ └── scavenger_test.go │ │ ├── scanner.go │ │ ├── scanner_test.go │ │ ├── shardscanner/ │ │ │ ├── activities.go │ │ │ ├── activities_test.go │ │ │ ├── aggregators.go │ │ │ ├── aggregators_test.go │ │ │ ├── fixer.go │ │ │ ├── fixer_test.go │ │ │ ├── fixer_workflow.go │ │ │ ├── fixer_workflow_test.go │ │ │ ├── scanner.go │ │ │ ├── scanner_test.go │ │ │ ├── scanner_workflow.go │ │ │ ├── scanner_workflow_test.go │ │ │ ├── types.go │ │ │ ├── types_test.go │ │ │ ├── workflows.go │ │ │ └── workflows_test.go │ │ ├── tasklist/ │ │ │ ├── db.go │ │ │ ├── handler.go │ │ │ ├── mocks_test.go │ │ │ ├── scavenger.go │ │ │ └── scavenger_test.go │ │ ├── timers/ │ │ │ ├── timers.go │ │ │ └── timers_test.go │ │ ├── workflow.go │ │ └── workflow_test.go │ ├── scheduler/ │ │ ├── activity.go │ │ ├── activity_test.go │ │ ├── client_worker.go │ │ ├── client_worker_test.go │ │ ├── types.go │ │ ├── workflow.go │ │ └── workflow_test.go │ ├── service.go │ ├── worker/ │ │ ├── worker_interface.go │ │ └── worker_mock.go │ └── workercommon/ │ └── util.go ├── simulation/ │ ├── README.md │ ├── history/ │ │ ├── dynamicconfig/ │ │ │ ├── default.yaml │ │ │ ├── queuev2.yaml │ │ │ └── queuev2_split.yaml │ │ ├── history_simulation_test.go │ │ ├── run.sh │ │ ├── testdata/ │ │ │ ├── history_simulation_default.yaml │ │ │ ├── history_simulation_queuev2.yaml │ │ │ └── history_simulation_queuev2_split.yaml │ │ └── workflow/ │ │ └── workflow.go │ ├── matching/ │ │ ├── comparison/ │ │ │ ├── README.md │ │ │ └── main.go │ │ ├── matching_simulation_test.go │ │ ├── run.sh │ │ └── testdata/ │ │ ├── matching_simulation_burst.yaml │ │ ├── matching_simulation_burst_adaptive.yaml │ │ ├── matching_simulation_default.yaml │ │ ├── matching_simulation_fluctuating.yaml │ │ ├── matching_simulation_fluctuating_adaptive.yaml │ │ ├── matching_simulation_get_partition_config_from_db.yaml │ │ ├── matching_simulation_more_read_partitions.yaml │ │ ├── matching_simulation_no_forwarding.yaml │ │ ├── matching_simulation_no_wait_time_for_addtask.yaml │ │ ├── matching_simulation_no_wait_time_for_polltask.yaml │ │ ├── matching_simulation_no_wait_time_for_polltask_and_addtask.yaml │ │ ├── matching_simulation_round_robin_load_balancer.yaml │ │ ├── matching_simulation_throughput.yaml │ │ ├── matching_simulation_weighted_load_balancer_with_backlog.yaml │ │ ├── matching_simulation_with_backlog.yaml │ │ ├── matching_simulation_zonal_isolation.yaml │ │ ├── matching_simulation_zonal_isolation_few_pollers.yaml │ │ ├── matching_simulation_zonal_isolation_many_pollers.yaml │ │ ├── matching_simulation_zonal_isolation_single_partition.yaml │ │ ├── matching_simulation_zonal_isolation_skew.yaml │ │ ├── matching_simulation_zonal_isolation_skew_extreme.yaml │ │ └── matching_simulation_zonal_isolation_skew_forwarding.yaml │ └── replication/ │ ├── README.md │ ├── replication_simulation_test.go │ ├── run.sh │ ├── testdata/ │ │ ├── replication_simulation_activeactive.yaml │ │ ├── replication_simulation_activeactive_child.yaml │ │ ├── replication_simulation_activeactive_cron.yaml │ │ ├── replication_simulation_activeactive_invalid_cluster_attribute.yaml │ │ ├── replication_simulation_activeactive_regional_failover.yaml │ │ ├── replication_simulation_activeactive_regional_failover_start_same_wfid.yaml │ │ ├── replication_simulation_activeactive_regional_failover_start_same_wfid_2.yaml │ │ ├── replication_simulation_activeactive_same_wfid.yaml │ │ ├── replication_simulation_activeactive_same_wfid_signalwithstart.yaml │ │ ├── replication_simulation_activeactive_same_wfid_signalwithstart_delayed.yaml │ │ ├── replication_simulation_activeactive_signalwithstart_terminateifrunning.yaml │ │ ├── replication_simulation_activeactive_start_terminateifrunning.yaml │ │ ├── replication_simulation_activepassive_to_activeactive.yaml │ │ ├── replication_simulation_budget_manager.yaml │ │ ├── replication_simulation_clusterredirection.yaml │ │ ├── replication_simulation_default.yaml │ │ └── replication_simulation_reset.yaml │ ├── types/ │ │ ├── repl_sim_config.go │ │ └── types.go │ ├── worker/ │ │ └── cmd/ │ │ └── main.go │ └── workflows/ │ ├── activityloop/ │ │ └── workflow.go │ ├── childactivityloop/ │ │ └── workflow.go │ ├── query/ │ │ └── workflow.go │ ├── timeractivityloop/ │ │ └── workflow.go │ └── workflows.go ├── testflags/ │ └── testflags.go └── tools/ ├── cassandra/ │ ├── README.md │ ├── cqlclient.go │ ├── cqlclient_test.go │ ├── handler.go │ ├── handler_test.go │ └── main.go ├── cli/ │ ├── README.md │ ├── admin.go │ ├── admin_async_queue_commands.go │ ├── admin_async_queue_commands_test.go │ ├── admin_cluster_commands.go │ ├── admin_cluster_commands_test.go │ ├── admin_commands.go │ ├── admin_commands_test.go │ ├── admin_config_store_commands.go │ ├── admin_config_store_commands_test.go │ ├── admin_db_clean_command.go │ ├── admin_db_clean_command_test.go │ ├── admin_db_decode_thrift.go │ ├── admin_db_decode_thrift_test.go │ ├── admin_db_scan_command.go │ ├── admin_db_scan_command_test.go │ ├── admin_dlq_commands.go │ ├── admin_elastic_search_commands.go │ ├── admin_elastic_search_commands_test.go │ ├── admin_failover_commands.go │ ├── admin_failover_commands_test.go │ ├── admin_kafka_commands.go │ ├── admin_kafka_commands_test.go │ ├── admin_task_list_commands.go │ ├── admin_task_list_commands_test.go │ ├── admin_timers.go │ ├── admin_timers_mock.go │ ├── admin_timers_test.go │ ├── app.go │ ├── app_test.go │ ├── clitest/ │ │ ├── context.go │ │ ├── context_test.go │ │ ├── runner.go │ │ └── runner_test.go │ ├── cluster.go │ ├── cluster_commands.go │ ├── database.go │ ├── database_test.go │ ├── defs.go │ ├── domain.go │ ├── domain_commands.go │ ├── domain_commands_test.go │ ├── domain_migration_command.go │ ├── domain_migration_command_test.go │ ├── domain_utils.go │ ├── factory.go │ ├── factory_mock.go │ ├── flags.go │ ├── histogram.go │ ├── histogram_test.go │ ├── isolation-groups.go │ ├── isolation_groups_test.go │ ├── mock_manager_factory.go │ ├── render.go │ ├── render_test.go │ ├── task_list.go │ ├── task_list_commands.go │ ├── testdata/ │ │ ├── scan_input.json │ │ ├── scan_input_bad_data.json │ │ └── scan_input_empty.json │ ├── utils.go │ ├── utils_test.go │ ├── workflow.go │ ├── workflow_batch_commands.go │ ├── workflow_batch_commands_test.go │ ├── workflow_commands.go │ └── workflow_commands_test.go ├── common/ │ ├── commoncli/ │ │ ├── cli.go │ │ └── cli_test.go │ ├── flag/ │ │ ├── flag.go │ │ └── flag_test.go │ └── schema/ │ ├── handler.go │ ├── handler_test.go │ ├── schema_client_mock.go │ ├── schema_graph.go │ ├── schema_graph_test.go │ ├── setuptask.go │ ├── types.go │ ├── updatetask.go │ ├── updatetask_test.go │ ├── util.go │ ├── util_test.go │ ├── version.go │ └── version_test.go ├── linter/ │ └── funcorder/ │ ├── analyzer.go │ └── cmd/ │ └── funcorder/ │ └── main.go ├── mcp/ │ ├── README.md │ ├── main.go │ └── util.go └── sql/ ├── README.md ├── conn.go ├── handler.go ├── main.go └── sqlite/ └── sqlite_test.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ .bin/ .build/ cadence cadence-server cadence-canary cadence-bench cadence-sql-tool cadence-cassandra-tool vendor/ ================================================ FILE: .envrc ================================================ # Prepend project binary directories to the PATH PATH_add .bin PATH_add .build/bin ================================================ FILE: .fossa.yml ================================================ version: 2 cli: server: https://app.fossa.com fetcher: custom project: git@github.com:uber/cadence.git analyze: modules: - name: github.com/uber/cadence/cmd/canary type: go target: github.com/uber/cadence/cmd/canary path: cmd/canary - name: github.com/uber/cadence/cmd/server type: go target: github.com/uber/cadence/cmd/server path: cmd/server - name: github.com/uber/cadence/cmd/tools/cassandra type: go target: github.com/uber/cadence/cmd/tools/cassandra path: cmd/tools/cassandra - name: github.com/uber/cadence/cmd/tools/cli type: go target: github.com/uber/cadence/cmd/tools/cli path: cmd/tools/cli - name: github.com/uber/cadence/cmd/tools/copyright type: go target: github.com/uber/cadence/cmd/tools/copyright path: cmd/tools/copyright - name: github.com/uber/cadence/cmd/tools/sql type: go target: github.com/uber/cadence/cmd/tools/sql path: cmd/tools/sql ================================================ FILE: .gen/go/admin/admin.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package admin import ( bytes "bytes" base64 "encoding/base64" errors "errors" fmt "fmt" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" config "github.com/uber/cadence/.gen/go/config" replicator "github.com/uber/cadence/.gen/go/replicator" shared "github.com/uber/cadence/.gen/go/shared" ) type AddSearchAttributeRequest struct { SearchAttribute map[string]shared.IndexedValueType `json:"searchAttribute,omitempty"` SecurityToken *string `json:"securityToken,omitempty"` } type _Map_String_IndexedValueType_MapItemList map[string]shared.IndexedValueType func (m _Map_String_IndexedValueType_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_IndexedValueType_MapItemList) Size() int { return len(m) } func (_Map_String_IndexedValueType_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_IndexedValueType_MapItemList) ValueType() wire.Type { return wire.TI32 } func (_Map_String_IndexedValueType_MapItemList) Close() {} // ToWire translates a AddSearchAttributeRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AddSearchAttributeRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.SearchAttribute != nil { w, err = wire.NewValueMap(_Map_String_IndexedValueType_MapItemList(v.SearchAttribute)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.SecurityToken != nil { w, err = wire.NewValueString(*(v.SecurityToken)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _IndexedValueType_Read(w wire.Value) (shared.IndexedValueType, error) { var v shared.IndexedValueType err := v.FromWire(w) return v, err } func _Map_String_IndexedValueType_Read(m wire.MapItemList) (map[string]shared.IndexedValueType, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TI32 { return nil, nil } o := make(map[string]shared.IndexedValueType, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _IndexedValueType_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a AddSearchAttributeRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AddSearchAttributeRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AddSearchAttributeRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AddSearchAttributeRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.SearchAttribute, err = _Map_String_IndexedValueType_Read(field.Value.GetMap()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SecurityToken = &x if err != nil { return err } } } } return nil } func _Map_String_IndexedValueType_Encode(val map[string]shared.IndexedValueType, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TI32, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a AddSearchAttributeRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AddSearchAttributeRequest struct could not be encoded. func (v *AddSearchAttributeRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SearchAttribute != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_IndexedValueType_Encode(v.SearchAttribute, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SecurityToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SecurityToken)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _IndexedValueType_Decode(sr stream.Reader) (shared.IndexedValueType, error) { var v shared.IndexedValueType err := v.Decode(sr) return v, err } func _Map_String_IndexedValueType_Decode(sr stream.Reader) (map[string]shared.IndexedValueType, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TI32 { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]shared.IndexedValueType, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _IndexedValueType_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a AddSearchAttributeRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AddSearchAttributeRequest struct could not be generated from the wire // representation. func (v *AddSearchAttributeRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.SearchAttribute, err = _Map_String_IndexedValueType_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SecurityToken = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AddSearchAttributeRequest // struct. func (v *AddSearchAttributeRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.SearchAttribute != nil { fields[i] = fmt.Sprintf("SearchAttribute: %v", v.SearchAttribute) i++ } if v.SecurityToken != nil { fields[i] = fmt.Sprintf("SecurityToken: %v", *(v.SecurityToken)) i++ } return fmt.Sprintf("AddSearchAttributeRequest{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_IndexedValueType_Equals(lhs, rhs map[string]shared.IndexedValueType) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this AddSearchAttributeRequest match the // provided AddSearchAttributeRequest. // // This function performs a deep comparison. func (v *AddSearchAttributeRequest) Equals(rhs *AddSearchAttributeRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SearchAttribute == nil && rhs.SearchAttribute == nil) || (v.SearchAttribute != nil && rhs.SearchAttribute != nil && _Map_String_IndexedValueType_Equals(v.SearchAttribute, rhs.SearchAttribute))) { return false } if !_String_EqualsPtr(v.SecurityToken, rhs.SecurityToken) { return false } return true } type _Map_String_IndexedValueType_Zapper map[string]shared.IndexedValueType // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_IndexedValueType_Zapper. func (m _Map_String_IndexedValueType_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AddSearchAttributeRequest. func (v *AddSearchAttributeRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SearchAttribute != nil { err = multierr.Append(err, enc.AddObject("searchAttribute", (_Map_String_IndexedValueType_Zapper)(v.SearchAttribute))) } if v.SecurityToken != nil { enc.AddString("securityToken", *v.SecurityToken) } return err } // GetSearchAttribute returns the value of SearchAttribute if it is set or its // zero value if it is unset. func (v *AddSearchAttributeRequest) GetSearchAttribute() (o map[string]shared.IndexedValueType) { if v != nil && v.SearchAttribute != nil { return v.SearchAttribute } return } // IsSetSearchAttribute returns true if SearchAttribute is not nil. func (v *AddSearchAttributeRequest) IsSetSearchAttribute() bool { return v != nil && v.SearchAttribute != nil } // GetSecurityToken returns the value of SecurityToken if it is set or its // zero value if it is unset. func (v *AddSearchAttributeRequest) GetSecurityToken() (o string) { if v != nil && v.SecurityToken != nil { return *v.SecurityToken } return } // IsSetSecurityToken returns true if SecurityToken is not nil. func (v *AddSearchAttributeRequest) IsSetSecurityToken() bool { return v != nil && v.SecurityToken != nil } type AdminDeleteWorkflowRequest struct { Domain *string `json:"domain,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` } // ToWire translates a AdminDeleteWorkflowRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminDeleteWorkflowRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WorkflowExecution_Read(w wire.Value) (*shared.WorkflowExecution, error) { var v shared.WorkflowExecution err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminDeleteWorkflowRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminDeleteWorkflowRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminDeleteWorkflowRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminDeleteWorkflowRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminDeleteWorkflowRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminDeleteWorkflowRequest struct could not be encoded. func (v *AdminDeleteWorkflowRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _WorkflowExecution_Decode(sr stream.Reader) (*shared.WorkflowExecution, error) { var v shared.WorkflowExecution err := v.Decode(sr) return &v, err } // Decode deserializes a AdminDeleteWorkflowRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminDeleteWorkflowRequest struct could not be generated from the wire // representation. func (v *AdminDeleteWorkflowRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminDeleteWorkflowRequest // struct. func (v *AdminDeleteWorkflowRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } return fmt.Sprintf("AdminDeleteWorkflowRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminDeleteWorkflowRequest match the // provided AdminDeleteWorkflowRequest. // // This function performs a deep comparison. func (v *AdminDeleteWorkflowRequest) Equals(rhs *AdminDeleteWorkflowRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminDeleteWorkflowRequest. func (v *AdminDeleteWorkflowRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *AdminDeleteWorkflowRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *AdminDeleteWorkflowRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *AdminDeleteWorkflowRequest) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *AdminDeleteWorkflowRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } type AdminDeleteWorkflowResponse struct { HistoryDeleted *bool `json:"historyDeleted,omitempty"` ExecutionsDeleted *bool `json:"executionsDeleted,omitempty"` VisibilityDeleted *bool `json:"visibilityDeleted,omitempty"` } // ToWire translates a AdminDeleteWorkflowResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminDeleteWorkflowResponse) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.HistoryDeleted != nil { w, err = wire.NewValueBool(*(v.HistoryDeleted)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ExecutionsDeleted != nil { w, err = wire.NewValueBool(*(v.ExecutionsDeleted)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.VisibilityDeleted != nil { w, err = wire.NewValueBool(*(v.VisibilityDeleted)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminDeleteWorkflowResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminDeleteWorkflowResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminDeleteWorkflowResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminDeleteWorkflowResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.HistoryDeleted = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ExecutionsDeleted = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.VisibilityDeleted = &x if err != nil { return err } } } } return nil } // Encode serializes a AdminDeleteWorkflowResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminDeleteWorkflowResponse struct could not be encoded. func (v *AdminDeleteWorkflowResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.HistoryDeleted != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.HistoryDeleted)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionsDeleted != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ExecutionsDeleted)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityDeleted != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.VisibilityDeleted)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a AdminDeleteWorkflowResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminDeleteWorkflowResponse struct could not be generated from the wire // representation. func (v *AdminDeleteWorkflowResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.HistoryDeleted = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ExecutionsDeleted = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.VisibilityDeleted = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminDeleteWorkflowResponse // struct. func (v *AdminDeleteWorkflowResponse) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.HistoryDeleted != nil { fields[i] = fmt.Sprintf("HistoryDeleted: %v", *(v.HistoryDeleted)) i++ } if v.ExecutionsDeleted != nil { fields[i] = fmt.Sprintf("ExecutionsDeleted: %v", *(v.ExecutionsDeleted)) i++ } if v.VisibilityDeleted != nil { fields[i] = fmt.Sprintf("VisibilityDeleted: %v", *(v.VisibilityDeleted)) i++ } return fmt.Sprintf("AdminDeleteWorkflowResponse{%v}", strings.Join(fields[:i], ", ")) } func _Bool_EqualsPtr(lhs, rhs *bool) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this AdminDeleteWorkflowResponse match the // provided AdminDeleteWorkflowResponse. // // This function performs a deep comparison. func (v *AdminDeleteWorkflowResponse) Equals(rhs *AdminDeleteWorkflowResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Bool_EqualsPtr(v.HistoryDeleted, rhs.HistoryDeleted) { return false } if !_Bool_EqualsPtr(v.ExecutionsDeleted, rhs.ExecutionsDeleted) { return false } if !_Bool_EqualsPtr(v.VisibilityDeleted, rhs.VisibilityDeleted) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminDeleteWorkflowResponse. func (v *AdminDeleteWorkflowResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.HistoryDeleted != nil { enc.AddBool("historyDeleted", *v.HistoryDeleted) } if v.ExecutionsDeleted != nil { enc.AddBool("executionsDeleted", *v.ExecutionsDeleted) } if v.VisibilityDeleted != nil { enc.AddBool("visibilityDeleted", *v.VisibilityDeleted) } return err } // GetHistoryDeleted returns the value of HistoryDeleted if it is set or its // zero value if it is unset. func (v *AdminDeleteWorkflowResponse) GetHistoryDeleted() (o bool) { if v != nil && v.HistoryDeleted != nil { return *v.HistoryDeleted } return } // IsSetHistoryDeleted returns true if HistoryDeleted is not nil. func (v *AdminDeleteWorkflowResponse) IsSetHistoryDeleted() bool { return v != nil && v.HistoryDeleted != nil } // GetExecutionsDeleted returns the value of ExecutionsDeleted if it is set or its // zero value if it is unset. func (v *AdminDeleteWorkflowResponse) GetExecutionsDeleted() (o bool) { if v != nil && v.ExecutionsDeleted != nil { return *v.ExecutionsDeleted } return } // IsSetExecutionsDeleted returns true if ExecutionsDeleted is not nil. func (v *AdminDeleteWorkflowResponse) IsSetExecutionsDeleted() bool { return v != nil && v.ExecutionsDeleted != nil } // GetVisibilityDeleted returns the value of VisibilityDeleted if it is set or its // zero value if it is unset. func (v *AdminDeleteWorkflowResponse) GetVisibilityDeleted() (o bool) { if v != nil && v.VisibilityDeleted != nil { return *v.VisibilityDeleted } return } // IsSetVisibilityDeleted returns true if VisibilityDeleted is not nil. func (v *AdminDeleteWorkflowResponse) IsSetVisibilityDeleted() bool { return v != nil && v.VisibilityDeleted != nil } type AdminMaintainWorkflowRequest struct { Domain *string `json:"domain,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` } // ToWire translates a AdminMaintainWorkflowRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminMaintainWorkflowRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminMaintainWorkflowRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminMaintainWorkflowRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminMaintainWorkflowRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminMaintainWorkflowRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminMaintainWorkflowRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminMaintainWorkflowRequest struct could not be encoded. func (v *AdminMaintainWorkflowRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a AdminMaintainWorkflowRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminMaintainWorkflowRequest struct could not be generated from the wire // representation. func (v *AdminMaintainWorkflowRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminMaintainWorkflowRequest // struct. func (v *AdminMaintainWorkflowRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } return fmt.Sprintf("AdminMaintainWorkflowRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminMaintainWorkflowRequest match the // provided AdminMaintainWorkflowRequest. // // This function performs a deep comparison. func (v *AdminMaintainWorkflowRequest) Equals(rhs *AdminMaintainWorkflowRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminMaintainWorkflowRequest. func (v *AdminMaintainWorkflowRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *AdminMaintainWorkflowRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *AdminMaintainWorkflowRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *AdminMaintainWorkflowRequest) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *AdminMaintainWorkflowRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } type AdminMaintainWorkflowResponse struct { HistoryDeleted *bool `json:"historyDeleted,omitempty"` ExecutionsDeleted *bool `json:"executionsDeleted,omitempty"` VisibilityDeleted *bool `json:"visibilityDeleted,omitempty"` } // ToWire translates a AdminMaintainWorkflowResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminMaintainWorkflowResponse) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.HistoryDeleted != nil { w, err = wire.NewValueBool(*(v.HistoryDeleted)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ExecutionsDeleted != nil { w, err = wire.NewValueBool(*(v.ExecutionsDeleted)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.VisibilityDeleted != nil { w, err = wire.NewValueBool(*(v.VisibilityDeleted)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminMaintainWorkflowResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminMaintainWorkflowResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminMaintainWorkflowResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminMaintainWorkflowResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.HistoryDeleted = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ExecutionsDeleted = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.VisibilityDeleted = &x if err != nil { return err } } } } return nil } // Encode serializes a AdminMaintainWorkflowResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminMaintainWorkflowResponse struct could not be encoded. func (v *AdminMaintainWorkflowResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.HistoryDeleted != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.HistoryDeleted)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionsDeleted != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ExecutionsDeleted)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityDeleted != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.VisibilityDeleted)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a AdminMaintainWorkflowResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminMaintainWorkflowResponse struct could not be generated from the wire // representation. func (v *AdminMaintainWorkflowResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.HistoryDeleted = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ExecutionsDeleted = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.VisibilityDeleted = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminMaintainWorkflowResponse // struct. func (v *AdminMaintainWorkflowResponse) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.HistoryDeleted != nil { fields[i] = fmt.Sprintf("HistoryDeleted: %v", *(v.HistoryDeleted)) i++ } if v.ExecutionsDeleted != nil { fields[i] = fmt.Sprintf("ExecutionsDeleted: %v", *(v.ExecutionsDeleted)) i++ } if v.VisibilityDeleted != nil { fields[i] = fmt.Sprintf("VisibilityDeleted: %v", *(v.VisibilityDeleted)) i++ } return fmt.Sprintf("AdminMaintainWorkflowResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminMaintainWorkflowResponse match the // provided AdminMaintainWorkflowResponse. // // This function performs a deep comparison. func (v *AdminMaintainWorkflowResponse) Equals(rhs *AdminMaintainWorkflowResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Bool_EqualsPtr(v.HistoryDeleted, rhs.HistoryDeleted) { return false } if !_Bool_EqualsPtr(v.ExecutionsDeleted, rhs.ExecutionsDeleted) { return false } if !_Bool_EqualsPtr(v.VisibilityDeleted, rhs.VisibilityDeleted) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminMaintainWorkflowResponse. func (v *AdminMaintainWorkflowResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.HistoryDeleted != nil { enc.AddBool("historyDeleted", *v.HistoryDeleted) } if v.ExecutionsDeleted != nil { enc.AddBool("executionsDeleted", *v.ExecutionsDeleted) } if v.VisibilityDeleted != nil { enc.AddBool("visibilityDeleted", *v.VisibilityDeleted) } return err } // GetHistoryDeleted returns the value of HistoryDeleted if it is set or its // zero value if it is unset. func (v *AdminMaintainWorkflowResponse) GetHistoryDeleted() (o bool) { if v != nil && v.HistoryDeleted != nil { return *v.HistoryDeleted } return } // IsSetHistoryDeleted returns true if HistoryDeleted is not nil. func (v *AdminMaintainWorkflowResponse) IsSetHistoryDeleted() bool { return v != nil && v.HistoryDeleted != nil } // GetExecutionsDeleted returns the value of ExecutionsDeleted if it is set or its // zero value if it is unset. func (v *AdminMaintainWorkflowResponse) GetExecutionsDeleted() (o bool) { if v != nil && v.ExecutionsDeleted != nil { return *v.ExecutionsDeleted } return } // IsSetExecutionsDeleted returns true if ExecutionsDeleted is not nil. func (v *AdminMaintainWorkflowResponse) IsSetExecutionsDeleted() bool { return v != nil && v.ExecutionsDeleted != nil } // GetVisibilityDeleted returns the value of VisibilityDeleted if it is set or its // zero value if it is unset. func (v *AdminMaintainWorkflowResponse) GetVisibilityDeleted() (o bool) { if v != nil && v.VisibilityDeleted != nil { return *v.VisibilityDeleted } return } // IsSetVisibilityDeleted returns true if VisibilityDeleted is not nil. func (v *AdminMaintainWorkflowResponse) IsSetVisibilityDeleted() bool { return v != nil && v.VisibilityDeleted != nil } type DescribeClusterResponse struct { SupportedClientVersions *shared.SupportedClientVersions `json:"supportedClientVersions,omitempty"` MembershipInfo *MembershipInfo `json:"membershipInfo,omitempty"` PersistenceInfo map[string]*PersistenceInfo `json:"persistenceInfo,omitempty"` } type _Map_String_PersistenceInfo_MapItemList map[string]*PersistenceInfo func (m _Map_String_PersistenceInfo_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*PersistenceInfo', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_PersistenceInfo_MapItemList) Size() int { return len(m) } func (_Map_String_PersistenceInfo_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_PersistenceInfo_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_PersistenceInfo_MapItemList) Close() {} // ToWire translates a DescribeClusterResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeClusterResponse) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.SupportedClientVersions != nil { w, err = v.SupportedClientVersions.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.MembershipInfo != nil { w, err = v.MembershipInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.PersistenceInfo != nil { w, err = wire.NewValueMap(_Map_String_PersistenceInfo_MapItemList(v.PersistenceInfo)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SupportedClientVersions_Read(w wire.Value) (*shared.SupportedClientVersions, error) { var v shared.SupportedClientVersions err := v.FromWire(w) return &v, err } func _MembershipInfo_Read(w wire.Value) (*MembershipInfo, error) { var v MembershipInfo err := v.FromWire(w) return &v, err } func _PersistenceInfo_Read(w wire.Value) (*PersistenceInfo, error) { var v PersistenceInfo err := v.FromWire(w) return &v, err } func _Map_String_PersistenceInfo_Read(m wire.MapItemList) (map[string]*PersistenceInfo, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*PersistenceInfo, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _PersistenceInfo_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a DescribeClusterResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeClusterResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeClusterResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeClusterResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.SupportedClientVersions, err = _SupportedClientVersions_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.MembershipInfo, err = _MembershipInfo_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TMap { v.PersistenceInfo, err = _Map_String_PersistenceInfo_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_PersistenceInfo_Encode(val map[string]*PersistenceInfo, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*PersistenceInfo', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a DescribeClusterResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeClusterResponse struct could not be encoded. func (v *DescribeClusterResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SupportedClientVersions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.SupportedClientVersions.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MembershipInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.MembershipInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PersistenceInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TMap}); err != nil { return err } if err := _Map_String_PersistenceInfo_Encode(v.PersistenceInfo, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SupportedClientVersions_Decode(sr stream.Reader) (*shared.SupportedClientVersions, error) { var v shared.SupportedClientVersions err := v.Decode(sr) return &v, err } func _MembershipInfo_Decode(sr stream.Reader) (*MembershipInfo, error) { var v MembershipInfo err := v.Decode(sr) return &v, err } func _PersistenceInfo_Decode(sr stream.Reader) (*PersistenceInfo, error) { var v PersistenceInfo err := v.Decode(sr) return &v, err } func _Map_String_PersistenceInfo_Decode(sr stream.Reader) (map[string]*PersistenceInfo, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*PersistenceInfo, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _PersistenceInfo_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a DescribeClusterResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeClusterResponse struct could not be generated from the wire // representation. func (v *DescribeClusterResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.SupportedClientVersions, err = _SupportedClientVersions_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.MembershipInfo, err = _MembershipInfo_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TMap: v.PersistenceInfo, err = _Map_String_PersistenceInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeClusterResponse // struct. func (v *DescribeClusterResponse) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.SupportedClientVersions != nil { fields[i] = fmt.Sprintf("SupportedClientVersions: %v", v.SupportedClientVersions) i++ } if v.MembershipInfo != nil { fields[i] = fmt.Sprintf("MembershipInfo: %v", v.MembershipInfo) i++ } if v.PersistenceInfo != nil { fields[i] = fmt.Sprintf("PersistenceInfo: %v", v.PersistenceInfo) i++ } return fmt.Sprintf("DescribeClusterResponse{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_PersistenceInfo_Equals(lhs, rhs map[string]*PersistenceInfo) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this DescribeClusterResponse match the // provided DescribeClusterResponse. // // This function performs a deep comparison. func (v *DescribeClusterResponse) Equals(rhs *DescribeClusterResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SupportedClientVersions == nil && rhs.SupportedClientVersions == nil) || (v.SupportedClientVersions != nil && rhs.SupportedClientVersions != nil && v.SupportedClientVersions.Equals(rhs.SupportedClientVersions))) { return false } if !((v.MembershipInfo == nil && rhs.MembershipInfo == nil) || (v.MembershipInfo != nil && rhs.MembershipInfo != nil && v.MembershipInfo.Equals(rhs.MembershipInfo))) { return false } if !((v.PersistenceInfo == nil && rhs.PersistenceInfo == nil) || (v.PersistenceInfo != nil && rhs.PersistenceInfo != nil && _Map_String_PersistenceInfo_Equals(v.PersistenceInfo, rhs.PersistenceInfo))) { return false } return true } type _Map_String_PersistenceInfo_Zapper map[string]*PersistenceInfo // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_PersistenceInfo_Zapper. func (m _Map_String_PersistenceInfo_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeClusterResponse. func (v *DescribeClusterResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SupportedClientVersions != nil { err = multierr.Append(err, enc.AddObject("supportedClientVersions", v.SupportedClientVersions)) } if v.MembershipInfo != nil { err = multierr.Append(err, enc.AddObject("membershipInfo", v.MembershipInfo)) } if v.PersistenceInfo != nil { err = multierr.Append(err, enc.AddObject("persistenceInfo", (_Map_String_PersistenceInfo_Zapper)(v.PersistenceInfo))) } return err } // GetSupportedClientVersions returns the value of SupportedClientVersions if it is set or its // zero value if it is unset. func (v *DescribeClusterResponse) GetSupportedClientVersions() (o *shared.SupportedClientVersions) { if v != nil && v.SupportedClientVersions != nil { return v.SupportedClientVersions } return } // IsSetSupportedClientVersions returns true if SupportedClientVersions is not nil. func (v *DescribeClusterResponse) IsSetSupportedClientVersions() bool { return v != nil && v.SupportedClientVersions != nil } // GetMembershipInfo returns the value of MembershipInfo if it is set or its // zero value if it is unset. func (v *DescribeClusterResponse) GetMembershipInfo() (o *MembershipInfo) { if v != nil && v.MembershipInfo != nil { return v.MembershipInfo } return } // IsSetMembershipInfo returns true if MembershipInfo is not nil. func (v *DescribeClusterResponse) IsSetMembershipInfo() bool { return v != nil && v.MembershipInfo != nil } // GetPersistenceInfo returns the value of PersistenceInfo if it is set or its // zero value if it is unset. func (v *DescribeClusterResponse) GetPersistenceInfo() (o map[string]*PersistenceInfo) { if v != nil && v.PersistenceInfo != nil { return v.PersistenceInfo } return } // IsSetPersistenceInfo returns true if PersistenceInfo is not nil. func (v *DescribeClusterResponse) IsSetPersistenceInfo() bool { return v != nil && v.PersistenceInfo != nil } type DescribeWorkflowExecutionRequest struct { Domain *string `json:"domain,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` } // ToWire translates a DescribeWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DescribeWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a DescribeWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeWorkflowExecutionRequest struct could not be encoded. func (v *DescribeWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DescribeWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *DescribeWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeWorkflowExecutionRequest // struct. func (v *DescribeWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } return fmt.Sprintf("DescribeWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DescribeWorkflowExecutionRequest match the // provided DescribeWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *DescribeWorkflowExecutionRequest) Equals(rhs *DescribeWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeWorkflowExecutionRequest. func (v *DescribeWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *DescribeWorkflowExecutionRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionRequest) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *DescribeWorkflowExecutionRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } type DescribeWorkflowExecutionResponse struct { ShardId *string `json:"shardId,omitempty"` HistoryAddr *string `json:"historyAddr,omitempty"` MutableStateInCache *string `json:"mutableStateInCache,omitempty"` MutableStateInDatabase *string `json:"mutableStateInDatabase,omitempty"` } // ToWire translates a DescribeWorkflowExecutionResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeWorkflowExecutionResponse) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.ShardId != nil { w, err = wire.NewValueString(*(v.ShardId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.HistoryAddr != nil { w, err = wire.NewValueString(*(v.HistoryAddr)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.MutableStateInCache != nil { w, err = wire.NewValueString(*(v.MutableStateInCache)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.MutableStateInDatabase != nil { w, err = wire.NewValueString(*(v.MutableStateInDatabase)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DescribeWorkflowExecutionResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeWorkflowExecutionResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeWorkflowExecutionResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeWorkflowExecutionResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ShardId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.HistoryAddr = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.MutableStateInCache = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.MutableStateInDatabase = &x if err != nil { return err } } } } return nil } // Encode serializes a DescribeWorkflowExecutionResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeWorkflowExecutionResponse struct could not be encoded. func (v *DescribeWorkflowExecutionResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ShardId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ShardId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryAddr != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.HistoryAddr)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MutableStateInCache != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.MutableStateInCache)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MutableStateInDatabase != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.MutableStateInDatabase)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DescribeWorkflowExecutionResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeWorkflowExecutionResponse struct could not be generated from the wire // representation. func (v *DescribeWorkflowExecutionResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ShardId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.HistoryAddr = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.MutableStateInCache = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.MutableStateInDatabase = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeWorkflowExecutionResponse // struct. func (v *DescribeWorkflowExecutionResponse) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.ShardId != nil { fields[i] = fmt.Sprintf("ShardId: %v", *(v.ShardId)) i++ } if v.HistoryAddr != nil { fields[i] = fmt.Sprintf("HistoryAddr: %v", *(v.HistoryAddr)) i++ } if v.MutableStateInCache != nil { fields[i] = fmt.Sprintf("MutableStateInCache: %v", *(v.MutableStateInCache)) i++ } if v.MutableStateInDatabase != nil { fields[i] = fmt.Sprintf("MutableStateInDatabase: %v", *(v.MutableStateInDatabase)) i++ } return fmt.Sprintf("DescribeWorkflowExecutionResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DescribeWorkflowExecutionResponse match the // provided DescribeWorkflowExecutionResponse. // // This function performs a deep comparison. func (v *DescribeWorkflowExecutionResponse) Equals(rhs *DescribeWorkflowExecutionResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ShardId, rhs.ShardId) { return false } if !_String_EqualsPtr(v.HistoryAddr, rhs.HistoryAddr) { return false } if !_String_EqualsPtr(v.MutableStateInCache, rhs.MutableStateInCache) { return false } if !_String_EqualsPtr(v.MutableStateInDatabase, rhs.MutableStateInDatabase) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeWorkflowExecutionResponse. func (v *DescribeWorkflowExecutionResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ShardId != nil { enc.AddString("shardId", *v.ShardId) } if v.HistoryAddr != nil { enc.AddString("historyAddr", *v.HistoryAddr) } if v.MutableStateInCache != nil { enc.AddString("mutableStateInCache", *v.MutableStateInCache) } if v.MutableStateInDatabase != nil { enc.AddString("mutableStateInDatabase", *v.MutableStateInDatabase) } return err } // GetShardId returns the value of ShardId if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionResponse) GetShardId() (o string) { if v != nil && v.ShardId != nil { return *v.ShardId } return } // IsSetShardId returns true if ShardId is not nil. func (v *DescribeWorkflowExecutionResponse) IsSetShardId() bool { return v != nil && v.ShardId != nil } // GetHistoryAddr returns the value of HistoryAddr if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionResponse) GetHistoryAddr() (o string) { if v != nil && v.HistoryAddr != nil { return *v.HistoryAddr } return } // IsSetHistoryAddr returns true if HistoryAddr is not nil. func (v *DescribeWorkflowExecutionResponse) IsSetHistoryAddr() bool { return v != nil && v.HistoryAddr != nil } // GetMutableStateInCache returns the value of MutableStateInCache if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionResponse) GetMutableStateInCache() (o string) { if v != nil && v.MutableStateInCache != nil { return *v.MutableStateInCache } return } // IsSetMutableStateInCache returns true if MutableStateInCache is not nil. func (v *DescribeWorkflowExecutionResponse) IsSetMutableStateInCache() bool { return v != nil && v.MutableStateInCache != nil } // GetMutableStateInDatabase returns the value of MutableStateInDatabase if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionResponse) GetMutableStateInDatabase() (o string) { if v != nil && v.MutableStateInDatabase != nil { return *v.MutableStateInDatabase } return } // IsSetMutableStateInDatabase returns true if MutableStateInDatabase is not nil. func (v *DescribeWorkflowExecutionResponse) IsSetMutableStateInDatabase() bool { return v != nil && v.MutableStateInDatabase != nil } type GetDomainAsyncWorkflowConfiguratonRequest struct { Domain *string `json:"domain,omitempty"` } // ToWire translates a GetDomainAsyncWorkflowConfiguratonRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetDomainAsyncWorkflowConfiguratonRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a GetDomainAsyncWorkflowConfiguratonRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetDomainAsyncWorkflowConfiguratonRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetDomainAsyncWorkflowConfiguratonRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetDomainAsyncWorkflowConfiguratonRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } } } return nil } // Encode serializes a GetDomainAsyncWorkflowConfiguratonRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetDomainAsyncWorkflowConfiguratonRequest struct could not be encoded. func (v *GetDomainAsyncWorkflowConfiguratonRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a GetDomainAsyncWorkflowConfiguratonRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetDomainAsyncWorkflowConfiguratonRequest struct could not be generated from the wire // representation. func (v *GetDomainAsyncWorkflowConfiguratonRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetDomainAsyncWorkflowConfiguratonRequest // struct. func (v *GetDomainAsyncWorkflowConfiguratonRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } return fmt.Sprintf("GetDomainAsyncWorkflowConfiguratonRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetDomainAsyncWorkflowConfiguratonRequest match the // provided GetDomainAsyncWorkflowConfiguratonRequest. // // This function performs a deep comparison. func (v *GetDomainAsyncWorkflowConfiguratonRequest) Equals(rhs *GetDomainAsyncWorkflowConfiguratonRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetDomainAsyncWorkflowConfiguratonRequest. func (v *GetDomainAsyncWorkflowConfiguratonRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *GetDomainAsyncWorkflowConfiguratonRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *GetDomainAsyncWorkflowConfiguratonRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } type GetDomainAsyncWorkflowConfiguratonResponse struct { Configuration *shared.AsyncWorkflowConfiguration `json:"configuration,omitempty"` } // ToWire translates a GetDomainAsyncWorkflowConfiguratonResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetDomainAsyncWorkflowConfiguratonResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Configuration != nil { w, err = v.Configuration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AsyncWorkflowConfiguration_Read(w wire.Value) (*shared.AsyncWorkflowConfiguration, error) { var v shared.AsyncWorkflowConfiguration err := v.FromWire(w) return &v, err } // FromWire deserializes a GetDomainAsyncWorkflowConfiguratonResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetDomainAsyncWorkflowConfiguratonResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetDomainAsyncWorkflowConfiguratonResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetDomainAsyncWorkflowConfiguratonResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Configuration, err = _AsyncWorkflowConfiguration_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a GetDomainAsyncWorkflowConfiguratonResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetDomainAsyncWorkflowConfiguratonResponse struct could not be encoded. func (v *GetDomainAsyncWorkflowConfiguratonResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Configuration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Configuration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _AsyncWorkflowConfiguration_Decode(sr stream.Reader) (*shared.AsyncWorkflowConfiguration, error) { var v shared.AsyncWorkflowConfiguration err := v.Decode(sr) return &v, err } // Decode deserializes a GetDomainAsyncWorkflowConfiguratonResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetDomainAsyncWorkflowConfiguratonResponse struct could not be generated from the wire // representation. func (v *GetDomainAsyncWorkflowConfiguratonResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Configuration, err = _AsyncWorkflowConfiguration_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetDomainAsyncWorkflowConfiguratonResponse // struct. func (v *GetDomainAsyncWorkflowConfiguratonResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Configuration != nil { fields[i] = fmt.Sprintf("Configuration: %v", v.Configuration) i++ } return fmt.Sprintf("GetDomainAsyncWorkflowConfiguratonResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetDomainAsyncWorkflowConfiguratonResponse match the // provided GetDomainAsyncWorkflowConfiguratonResponse. // // This function performs a deep comparison. func (v *GetDomainAsyncWorkflowConfiguratonResponse) Equals(rhs *GetDomainAsyncWorkflowConfiguratonResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Configuration == nil && rhs.Configuration == nil) || (v.Configuration != nil && rhs.Configuration != nil && v.Configuration.Equals(rhs.Configuration))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetDomainAsyncWorkflowConfiguratonResponse. func (v *GetDomainAsyncWorkflowConfiguratonResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Configuration != nil { err = multierr.Append(err, enc.AddObject("configuration", v.Configuration)) } return err } // GetConfiguration returns the value of Configuration if it is set or its // zero value if it is unset. func (v *GetDomainAsyncWorkflowConfiguratonResponse) GetConfiguration() (o *shared.AsyncWorkflowConfiguration) { if v != nil && v.Configuration != nil { return v.Configuration } return } // IsSetConfiguration returns true if Configuration is not nil. func (v *GetDomainAsyncWorkflowConfiguratonResponse) IsSetConfiguration() bool { return v != nil && v.Configuration != nil } type GetDomainIsolationGroupsRequest struct { Domain *string `json:"domain,omitempty"` } // ToWire translates a GetDomainIsolationGroupsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetDomainIsolationGroupsRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a GetDomainIsolationGroupsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetDomainIsolationGroupsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetDomainIsolationGroupsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetDomainIsolationGroupsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } } } return nil } // Encode serializes a GetDomainIsolationGroupsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetDomainIsolationGroupsRequest struct could not be encoded. func (v *GetDomainIsolationGroupsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a GetDomainIsolationGroupsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetDomainIsolationGroupsRequest struct could not be generated from the wire // representation. func (v *GetDomainIsolationGroupsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetDomainIsolationGroupsRequest // struct. func (v *GetDomainIsolationGroupsRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } return fmt.Sprintf("GetDomainIsolationGroupsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetDomainIsolationGroupsRequest match the // provided GetDomainIsolationGroupsRequest. // // This function performs a deep comparison. func (v *GetDomainIsolationGroupsRequest) Equals(rhs *GetDomainIsolationGroupsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetDomainIsolationGroupsRequest. func (v *GetDomainIsolationGroupsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *GetDomainIsolationGroupsRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *GetDomainIsolationGroupsRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } type GetDomainIsolationGroupsResponse struct { IsolationGroups *shared.IsolationGroupConfiguration `json:"isolationGroups,omitempty"` } // ToWire translates a GetDomainIsolationGroupsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetDomainIsolationGroupsResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.IsolationGroups != nil { w, err = v.IsolationGroups.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _IsolationGroupConfiguration_Read(w wire.Value) (*shared.IsolationGroupConfiguration, error) { var v shared.IsolationGroupConfiguration err := v.FromWire(w) return &v, err } // FromWire deserializes a GetDomainIsolationGroupsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetDomainIsolationGroupsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetDomainIsolationGroupsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetDomainIsolationGroupsResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.IsolationGroups, err = _IsolationGroupConfiguration_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a GetDomainIsolationGroupsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetDomainIsolationGroupsResponse struct could not be encoded. func (v *GetDomainIsolationGroupsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.IsolationGroups != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.IsolationGroups.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _IsolationGroupConfiguration_Decode(sr stream.Reader) (*shared.IsolationGroupConfiguration, error) { var v shared.IsolationGroupConfiguration err := v.Decode(sr) return &v, err } // Decode deserializes a GetDomainIsolationGroupsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetDomainIsolationGroupsResponse struct could not be generated from the wire // representation. func (v *GetDomainIsolationGroupsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.IsolationGroups, err = _IsolationGroupConfiguration_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetDomainIsolationGroupsResponse // struct. func (v *GetDomainIsolationGroupsResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.IsolationGroups != nil { fields[i] = fmt.Sprintf("IsolationGroups: %v", v.IsolationGroups) i++ } return fmt.Sprintf("GetDomainIsolationGroupsResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetDomainIsolationGroupsResponse match the // provided GetDomainIsolationGroupsResponse. // // This function performs a deep comparison. func (v *GetDomainIsolationGroupsResponse) Equals(rhs *GetDomainIsolationGroupsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.IsolationGroups == nil && rhs.IsolationGroups == nil) || (v.IsolationGroups != nil && rhs.IsolationGroups != nil && v.IsolationGroups.Equals(rhs.IsolationGroups))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetDomainIsolationGroupsResponse. func (v *GetDomainIsolationGroupsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.IsolationGroups != nil { err = multierr.Append(err, enc.AddObject("isolationGroups", v.IsolationGroups)) } return err } // GetIsolationGroups returns the value of IsolationGroups if it is set or its // zero value if it is unset. func (v *GetDomainIsolationGroupsResponse) GetIsolationGroups() (o *shared.IsolationGroupConfiguration) { if v != nil && v.IsolationGroups != nil { return v.IsolationGroups } return } // IsSetIsolationGroups returns true if IsolationGroups is not nil. func (v *GetDomainIsolationGroupsResponse) IsSetIsolationGroups() bool { return v != nil && v.IsolationGroups != nil } type GetDynamicConfigRequest struct { ConfigName *string `json:"configName,omitempty"` Filters []*config.DynamicConfigFilter `json:"filters,omitempty"` } type _List_DynamicConfigFilter_ValueList []*config.DynamicConfigFilter func (v _List_DynamicConfigFilter_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*config.DynamicConfigFilter', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_DynamicConfigFilter_ValueList) Size() int { return len(v) } func (_List_DynamicConfigFilter_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_DynamicConfigFilter_ValueList) Close() {} // ToWire translates a GetDynamicConfigRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetDynamicConfigRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ConfigName != nil { w, err = wire.NewValueString(*(v.ConfigName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Filters != nil { w, err = wire.NewValueList(_List_DynamicConfigFilter_ValueList(v.Filters)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DynamicConfigFilter_Read(w wire.Value) (*config.DynamicConfigFilter, error) { var v config.DynamicConfigFilter err := v.FromWire(w) return &v, err } func _List_DynamicConfigFilter_Read(l wire.ValueList) ([]*config.DynamicConfigFilter, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*config.DynamicConfigFilter, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _DynamicConfigFilter_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a GetDynamicConfigRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetDynamicConfigRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetDynamicConfigRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetDynamicConfigRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ConfigName = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Filters, err = _List_DynamicConfigFilter_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_DynamicConfigFilter_Encode(val []*config.DynamicConfigFilter, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*config.DynamicConfigFilter', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a GetDynamicConfigRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetDynamicConfigRequest struct could not be encoded. func (v *GetDynamicConfigRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ConfigName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ConfigName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Filters != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_DynamicConfigFilter_Encode(v.Filters, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DynamicConfigFilter_Decode(sr stream.Reader) (*config.DynamicConfigFilter, error) { var v config.DynamicConfigFilter err := v.Decode(sr) return &v, err } func _List_DynamicConfigFilter_Decode(sr stream.Reader) ([]*config.DynamicConfigFilter, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*config.DynamicConfigFilter, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _DynamicConfigFilter_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a GetDynamicConfigRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetDynamicConfigRequest struct could not be generated from the wire // representation. func (v *GetDynamicConfigRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ConfigName = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Filters, err = _List_DynamicConfigFilter_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetDynamicConfigRequest // struct. func (v *GetDynamicConfigRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ConfigName != nil { fields[i] = fmt.Sprintf("ConfigName: %v", *(v.ConfigName)) i++ } if v.Filters != nil { fields[i] = fmt.Sprintf("Filters: %v", v.Filters) i++ } return fmt.Sprintf("GetDynamicConfigRequest{%v}", strings.Join(fields[:i], ", ")) } func _List_DynamicConfigFilter_Equals(lhs, rhs []*config.DynamicConfigFilter) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this GetDynamicConfigRequest match the // provided GetDynamicConfigRequest. // // This function performs a deep comparison. func (v *GetDynamicConfigRequest) Equals(rhs *GetDynamicConfigRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ConfigName, rhs.ConfigName) { return false } if !((v.Filters == nil && rhs.Filters == nil) || (v.Filters != nil && rhs.Filters != nil && _List_DynamicConfigFilter_Equals(v.Filters, rhs.Filters))) { return false } return true } type _List_DynamicConfigFilter_Zapper []*config.DynamicConfigFilter // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_DynamicConfigFilter_Zapper. func (l _List_DynamicConfigFilter_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetDynamicConfigRequest. func (v *GetDynamicConfigRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ConfigName != nil { enc.AddString("configName", *v.ConfigName) } if v.Filters != nil { err = multierr.Append(err, enc.AddArray("filters", (_List_DynamicConfigFilter_Zapper)(v.Filters))) } return err } // GetConfigName returns the value of ConfigName if it is set or its // zero value if it is unset. func (v *GetDynamicConfigRequest) GetConfigName() (o string) { if v != nil && v.ConfigName != nil { return *v.ConfigName } return } // IsSetConfigName returns true if ConfigName is not nil. func (v *GetDynamicConfigRequest) IsSetConfigName() bool { return v != nil && v.ConfigName != nil } // GetFilters returns the value of Filters if it is set or its // zero value if it is unset. func (v *GetDynamicConfigRequest) GetFilters() (o []*config.DynamicConfigFilter) { if v != nil && v.Filters != nil { return v.Filters } return } // IsSetFilters returns true if Filters is not nil. func (v *GetDynamicConfigRequest) IsSetFilters() bool { return v != nil && v.Filters != nil } type GetDynamicConfigResponse struct { Value *shared.DataBlob `json:"value,omitempty"` } // ToWire translates a GetDynamicConfigResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetDynamicConfigResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Value != nil { w, err = v.Value.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DataBlob_Read(w wire.Value) (*shared.DataBlob, error) { var v shared.DataBlob err := v.FromWire(w) return &v, err } // FromWire deserializes a GetDynamicConfigResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetDynamicConfigResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetDynamicConfigResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetDynamicConfigResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Value, err = _DataBlob_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a GetDynamicConfigResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetDynamicConfigResponse struct could not be encoded. func (v *GetDynamicConfigResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Value != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Value.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DataBlob_Decode(sr stream.Reader) (*shared.DataBlob, error) { var v shared.DataBlob err := v.Decode(sr) return &v, err } // Decode deserializes a GetDynamicConfigResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetDynamicConfigResponse struct could not be generated from the wire // representation. func (v *GetDynamicConfigResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Value, err = _DataBlob_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetDynamicConfigResponse // struct. func (v *GetDynamicConfigResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Value != nil { fields[i] = fmt.Sprintf("Value: %v", v.Value) i++ } return fmt.Sprintf("GetDynamicConfigResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetDynamicConfigResponse match the // provided GetDynamicConfigResponse. // // This function performs a deep comparison. func (v *GetDynamicConfigResponse) Equals(rhs *GetDynamicConfigResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Value == nil && rhs.Value == nil) || (v.Value != nil && rhs.Value != nil && v.Value.Equals(rhs.Value))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetDynamicConfigResponse. func (v *GetDynamicConfigResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Value != nil { err = multierr.Append(err, enc.AddObject("value", v.Value)) } return err } // GetValue returns the value of Value if it is set or its // zero value if it is unset. func (v *GetDynamicConfigResponse) GetValue() (o *shared.DataBlob) { if v != nil && v.Value != nil { return v.Value } return } // IsSetValue returns true if Value is not nil. func (v *GetDynamicConfigResponse) IsSetValue() bool { return v != nil && v.Value != nil } type GetGlobalIsolationGroupsRequest struct { } // ToWire translates a GetGlobalIsolationGroupsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetGlobalIsolationGroupsRequest) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a GetGlobalIsolationGroupsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetGlobalIsolationGroupsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetGlobalIsolationGroupsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetGlobalIsolationGroupsRequest) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a GetGlobalIsolationGroupsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetGlobalIsolationGroupsRequest struct could not be encoded. func (v *GetGlobalIsolationGroupsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a GetGlobalIsolationGroupsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetGlobalIsolationGroupsRequest struct could not be generated from the wire // representation. func (v *GetGlobalIsolationGroupsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetGlobalIsolationGroupsRequest // struct. func (v *GetGlobalIsolationGroupsRequest) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("GetGlobalIsolationGroupsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetGlobalIsolationGroupsRequest match the // provided GetGlobalIsolationGroupsRequest. // // This function performs a deep comparison. func (v *GetGlobalIsolationGroupsRequest) Equals(rhs *GetGlobalIsolationGroupsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetGlobalIsolationGroupsRequest. func (v *GetGlobalIsolationGroupsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type GetGlobalIsolationGroupsResponse struct { IsolationGroups *shared.IsolationGroupConfiguration `json:"isolationGroups,omitempty"` } // ToWire translates a GetGlobalIsolationGroupsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetGlobalIsolationGroupsResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.IsolationGroups != nil { w, err = v.IsolationGroups.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a GetGlobalIsolationGroupsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetGlobalIsolationGroupsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetGlobalIsolationGroupsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetGlobalIsolationGroupsResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.IsolationGroups, err = _IsolationGroupConfiguration_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a GetGlobalIsolationGroupsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetGlobalIsolationGroupsResponse struct could not be encoded. func (v *GetGlobalIsolationGroupsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.IsolationGroups != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.IsolationGroups.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a GetGlobalIsolationGroupsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetGlobalIsolationGroupsResponse struct could not be generated from the wire // representation. func (v *GetGlobalIsolationGroupsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.IsolationGroups, err = _IsolationGroupConfiguration_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetGlobalIsolationGroupsResponse // struct. func (v *GetGlobalIsolationGroupsResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.IsolationGroups != nil { fields[i] = fmt.Sprintf("IsolationGroups: %v", v.IsolationGroups) i++ } return fmt.Sprintf("GetGlobalIsolationGroupsResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetGlobalIsolationGroupsResponse match the // provided GetGlobalIsolationGroupsResponse. // // This function performs a deep comparison. func (v *GetGlobalIsolationGroupsResponse) Equals(rhs *GetGlobalIsolationGroupsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.IsolationGroups == nil && rhs.IsolationGroups == nil) || (v.IsolationGroups != nil && rhs.IsolationGroups != nil && v.IsolationGroups.Equals(rhs.IsolationGroups))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetGlobalIsolationGroupsResponse. func (v *GetGlobalIsolationGroupsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.IsolationGroups != nil { err = multierr.Append(err, enc.AddObject("isolationGroups", v.IsolationGroups)) } return err } // GetIsolationGroups returns the value of IsolationGroups if it is set or its // zero value if it is unset. func (v *GetGlobalIsolationGroupsResponse) GetIsolationGroups() (o *shared.IsolationGroupConfiguration) { if v != nil && v.IsolationGroups != nil { return v.IsolationGroups } return } // IsSetIsolationGroups returns true if IsolationGroups is not nil. func (v *GetGlobalIsolationGroupsResponse) IsSetIsolationGroups() bool { return v != nil && v.IsolationGroups != nil } // StartEventId defines the beginning of the event to fetch. The first event is exclusive. // EndEventId and EndEventVersion defines the end of the event to fetch. The end event is exclusive. type GetWorkflowExecutionRawHistoryV2Request struct { Domain *string `json:"domain,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` StartEventId *int64 `json:"startEventId,omitempty"` StartEventVersion *int64 `json:"startEventVersion,omitempty"` EndEventId *int64 `json:"endEventId,omitempty"` EndEventVersion *int64 `json:"endEventVersion,omitempty"` MaximumPageSize *int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // ToWire translates a GetWorkflowExecutionRawHistoryV2Request struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetWorkflowExecutionRawHistoryV2Request) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.StartEventId != nil { w, err = wire.NewValueI64(*(v.StartEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.StartEventVersion != nil { w, err = wire.NewValueI64(*(v.StartEventVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.EndEventId != nil { w, err = wire.NewValueI64(*(v.EndEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.EndEventVersion != nil { w, err = wire.NewValueI64(*(v.EndEventVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.MaximumPageSize != nil { w, err = wire.NewValueI32(*(v.MaximumPageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a GetWorkflowExecutionRawHistoryV2Request struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetWorkflowExecutionRawHistoryV2Request struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetWorkflowExecutionRawHistoryV2Request // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetWorkflowExecutionRawHistoryV2Request) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartEventVersion = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EndEventId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EndEventVersion = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.MaximumPageSize = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a GetWorkflowExecutionRawHistoryV2Request struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetWorkflowExecutionRawHistoryV2Request struct could not be encoded. func (v *GetWorkflowExecutionRawHistoryV2Request) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartEventVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartEventVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EndEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EndEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EndEventVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EndEventVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MaximumPageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.MaximumPageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a GetWorkflowExecutionRawHistoryV2Request struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetWorkflowExecutionRawHistoryV2Request struct could not be generated from the wire // representation. func (v *GetWorkflowExecutionRawHistoryV2Request) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartEventVersion = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EndEventId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EndEventVersion = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.MaximumPageSize = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetWorkflowExecutionRawHistoryV2Request // struct. func (v *GetWorkflowExecutionRawHistoryV2Request) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.StartEventId != nil { fields[i] = fmt.Sprintf("StartEventId: %v", *(v.StartEventId)) i++ } if v.StartEventVersion != nil { fields[i] = fmt.Sprintf("StartEventVersion: %v", *(v.StartEventVersion)) i++ } if v.EndEventId != nil { fields[i] = fmt.Sprintf("EndEventId: %v", *(v.EndEventId)) i++ } if v.EndEventVersion != nil { fields[i] = fmt.Sprintf("EndEventVersion: %v", *(v.EndEventVersion)) i++ } if v.MaximumPageSize != nil { fields[i] = fmt.Sprintf("MaximumPageSize: %v", *(v.MaximumPageSize)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("GetWorkflowExecutionRawHistoryV2Request{%v}", strings.Join(fields[:i], ", ")) } func _I64_EqualsPtr(lhs, rhs *int64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _I32_EqualsPtr(lhs, rhs *int32) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this GetWorkflowExecutionRawHistoryV2Request match the // provided GetWorkflowExecutionRawHistoryV2Request. // // This function performs a deep comparison. func (v *GetWorkflowExecutionRawHistoryV2Request) Equals(rhs *GetWorkflowExecutionRawHistoryV2Request) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !_I64_EqualsPtr(v.StartEventId, rhs.StartEventId) { return false } if !_I64_EqualsPtr(v.StartEventVersion, rhs.StartEventVersion) { return false } if !_I64_EqualsPtr(v.EndEventId, rhs.EndEventId) { return false } if !_I64_EqualsPtr(v.EndEventVersion, rhs.EndEventVersion) { return false } if !_I32_EqualsPtr(v.MaximumPageSize, rhs.MaximumPageSize) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetWorkflowExecutionRawHistoryV2Request. func (v *GetWorkflowExecutionRawHistoryV2Request) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.StartEventId != nil { enc.AddInt64("startEventId", *v.StartEventId) } if v.StartEventVersion != nil { enc.AddInt64("startEventVersion", *v.StartEventVersion) } if v.EndEventId != nil { enc.AddInt64("endEventId", *v.EndEventId) } if v.EndEventVersion != nil { enc.AddInt64("endEventVersion", *v.EndEventVersion) } if v.MaximumPageSize != nil { enc.AddInt32("maximumPageSize", *v.MaximumPageSize) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Request) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *GetWorkflowExecutionRawHistoryV2Request) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Request) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *GetWorkflowExecutionRawHistoryV2Request) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetStartEventId returns the value of StartEventId if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Request) GetStartEventId() (o int64) { if v != nil && v.StartEventId != nil { return *v.StartEventId } return } // IsSetStartEventId returns true if StartEventId is not nil. func (v *GetWorkflowExecutionRawHistoryV2Request) IsSetStartEventId() bool { return v != nil && v.StartEventId != nil } // GetStartEventVersion returns the value of StartEventVersion if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Request) GetStartEventVersion() (o int64) { if v != nil && v.StartEventVersion != nil { return *v.StartEventVersion } return } // IsSetStartEventVersion returns true if StartEventVersion is not nil. func (v *GetWorkflowExecutionRawHistoryV2Request) IsSetStartEventVersion() bool { return v != nil && v.StartEventVersion != nil } // GetEndEventId returns the value of EndEventId if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Request) GetEndEventId() (o int64) { if v != nil && v.EndEventId != nil { return *v.EndEventId } return } // IsSetEndEventId returns true if EndEventId is not nil. func (v *GetWorkflowExecutionRawHistoryV2Request) IsSetEndEventId() bool { return v != nil && v.EndEventId != nil } // GetEndEventVersion returns the value of EndEventVersion if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Request) GetEndEventVersion() (o int64) { if v != nil && v.EndEventVersion != nil { return *v.EndEventVersion } return } // IsSetEndEventVersion returns true if EndEventVersion is not nil. func (v *GetWorkflowExecutionRawHistoryV2Request) IsSetEndEventVersion() bool { return v != nil && v.EndEventVersion != nil } // GetMaximumPageSize returns the value of MaximumPageSize if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Request) GetMaximumPageSize() (o int32) { if v != nil && v.MaximumPageSize != nil { return *v.MaximumPageSize } return } // IsSetMaximumPageSize returns true if MaximumPageSize is not nil. func (v *GetWorkflowExecutionRawHistoryV2Request) IsSetMaximumPageSize() bool { return v != nil && v.MaximumPageSize != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Request) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *GetWorkflowExecutionRawHistoryV2Request) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type GetWorkflowExecutionRawHistoryV2Response struct { NextPageToken []byte `json:"nextPageToken,omitempty"` HistoryBatches []*shared.DataBlob `json:"historyBatches,omitempty"` VersionHistory *shared.VersionHistory `json:"versionHistory,omitempty"` } type _List_DataBlob_ValueList []*shared.DataBlob func (v _List_DataBlob_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*shared.DataBlob', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_DataBlob_ValueList) Size() int { return len(v) } func (_List_DataBlob_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_DataBlob_ValueList) Close() {} // ToWire translates a GetWorkflowExecutionRawHistoryV2Response struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetWorkflowExecutionRawHistoryV2Response) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.HistoryBatches != nil { w, err = wire.NewValueList(_List_DataBlob_ValueList(v.HistoryBatches)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.VersionHistory != nil { w, err = v.VersionHistory.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _List_DataBlob_Read(l wire.ValueList) ([]*shared.DataBlob, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*shared.DataBlob, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _DataBlob_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _VersionHistory_Read(w wire.Value) (*shared.VersionHistory, error) { var v shared.VersionHistory err := v.FromWire(w) return &v, err } // FromWire deserializes a GetWorkflowExecutionRawHistoryV2Response struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetWorkflowExecutionRawHistoryV2Response struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetWorkflowExecutionRawHistoryV2Response // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetWorkflowExecutionRawHistoryV2Response) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.HistoryBatches, err = _List_DataBlob_Read(field.Value.GetList()) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.VersionHistory, err = _VersionHistory_Read(field.Value) if err != nil { return err } } } } return nil } func _List_DataBlob_Encode(val []*shared.DataBlob, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*shared.DataBlob', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a GetWorkflowExecutionRawHistoryV2Response struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetWorkflowExecutionRawHistoryV2Response struct could not be encoded. func (v *GetWorkflowExecutionRawHistoryV2Response) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryBatches != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_DataBlob_Encode(v.HistoryBatches, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistory != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.VersionHistory.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _List_DataBlob_Decode(sr stream.Reader) ([]*shared.DataBlob, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*shared.DataBlob, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _DataBlob_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _VersionHistory_Decode(sr stream.Reader) (*shared.VersionHistory, error) { var v shared.VersionHistory err := v.Decode(sr) return &v, err } // Decode deserializes a GetWorkflowExecutionRawHistoryV2Response struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetWorkflowExecutionRawHistoryV2Response struct could not be generated from the wire // representation. func (v *GetWorkflowExecutionRawHistoryV2Response) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.HistoryBatches, err = _List_DataBlob_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.VersionHistory, err = _VersionHistory_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetWorkflowExecutionRawHistoryV2Response // struct. func (v *GetWorkflowExecutionRawHistoryV2Response) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.HistoryBatches != nil { fields[i] = fmt.Sprintf("HistoryBatches: %v", v.HistoryBatches) i++ } if v.VersionHistory != nil { fields[i] = fmt.Sprintf("VersionHistory: %v", v.VersionHistory) i++ } return fmt.Sprintf("GetWorkflowExecutionRawHistoryV2Response{%v}", strings.Join(fields[:i], ", ")) } func _List_DataBlob_Equals(lhs, rhs []*shared.DataBlob) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this GetWorkflowExecutionRawHistoryV2Response match the // provided GetWorkflowExecutionRawHistoryV2Response. // // This function performs a deep comparison. func (v *GetWorkflowExecutionRawHistoryV2Response) Equals(rhs *GetWorkflowExecutionRawHistoryV2Response) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !((v.HistoryBatches == nil && rhs.HistoryBatches == nil) || (v.HistoryBatches != nil && rhs.HistoryBatches != nil && _List_DataBlob_Equals(v.HistoryBatches, rhs.HistoryBatches))) { return false } if !((v.VersionHistory == nil && rhs.VersionHistory == nil) || (v.VersionHistory != nil && rhs.VersionHistory != nil && v.VersionHistory.Equals(rhs.VersionHistory))) { return false } return true } type _List_DataBlob_Zapper []*shared.DataBlob // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_DataBlob_Zapper. func (l _List_DataBlob_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetWorkflowExecutionRawHistoryV2Response. func (v *GetWorkflowExecutionRawHistoryV2Response) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.HistoryBatches != nil { err = multierr.Append(err, enc.AddArray("historyBatches", (_List_DataBlob_Zapper)(v.HistoryBatches))) } if v.VersionHistory != nil { err = multierr.Append(err, enc.AddObject("versionHistory", v.VersionHistory)) } return err } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Response) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *GetWorkflowExecutionRawHistoryV2Response) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetHistoryBatches returns the value of HistoryBatches if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Response) GetHistoryBatches() (o []*shared.DataBlob) { if v != nil && v.HistoryBatches != nil { return v.HistoryBatches } return } // IsSetHistoryBatches returns true if HistoryBatches is not nil. func (v *GetWorkflowExecutionRawHistoryV2Response) IsSetHistoryBatches() bool { return v != nil && v.HistoryBatches != nil } // GetVersionHistory returns the value of VersionHistory if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionRawHistoryV2Response) GetVersionHistory() (o *shared.VersionHistory) { if v != nil && v.VersionHistory != nil { return v.VersionHistory } return } // IsSetVersionHistory returns true if VersionHistory is not nil. func (v *GetWorkflowExecutionRawHistoryV2Response) IsSetVersionHistory() bool { return v != nil && v.VersionHistory != nil } type HostInfo struct { Identity *string `json:"Identity,omitempty"` } // ToWire translates a HostInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HostInfo) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HostInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HostInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HostInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HostInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a HostInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HostInfo struct could not be encoded. func (v *HostInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a HostInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HostInfo struct could not be generated from the wire // representation. func (v *HostInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HostInfo // struct. func (v *HostInfo) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("HostInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HostInfo match the // provided HostInfo. // // This function performs a deep comparison. func (v *HostInfo) Equals(rhs *HostInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HostInfo. func (v *HostInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Identity != nil { enc.AddString("Identity", *v.Identity) } return err } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *HostInfo) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *HostInfo) IsSetIdentity() bool { return v != nil && v.Identity != nil } type ListDynamicConfigRequest struct { ConfigName *string `json:"configName,omitempty"` } // ToWire translates a ListDynamicConfigRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListDynamicConfigRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ConfigName != nil { w, err = wire.NewValueString(*(v.ConfigName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListDynamicConfigRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListDynamicConfigRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListDynamicConfigRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListDynamicConfigRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ConfigName = &x if err != nil { return err } } } } return nil } // Encode serializes a ListDynamicConfigRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListDynamicConfigRequest struct could not be encoded. func (v *ListDynamicConfigRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ConfigName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ConfigName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListDynamicConfigRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListDynamicConfigRequest struct could not be generated from the wire // representation. func (v *ListDynamicConfigRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ConfigName = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListDynamicConfigRequest // struct. func (v *ListDynamicConfigRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ConfigName != nil { fields[i] = fmt.Sprintf("ConfigName: %v", *(v.ConfigName)) i++ } return fmt.Sprintf("ListDynamicConfigRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListDynamicConfigRequest match the // provided ListDynamicConfigRequest. // // This function performs a deep comparison. func (v *ListDynamicConfigRequest) Equals(rhs *ListDynamicConfigRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ConfigName, rhs.ConfigName) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListDynamicConfigRequest. func (v *ListDynamicConfigRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ConfigName != nil { enc.AddString("configName", *v.ConfigName) } return err } // GetConfigName returns the value of ConfigName if it is set or its // zero value if it is unset. func (v *ListDynamicConfigRequest) GetConfigName() (o string) { if v != nil && v.ConfigName != nil { return *v.ConfigName } return } // IsSetConfigName returns true if ConfigName is not nil. func (v *ListDynamicConfigRequest) IsSetConfigName() bool { return v != nil && v.ConfigName != nil } type ListDynamicConfigResponse struct { Entries []*config.DynamicConfigEntry `json:"entries,omitempty"` } type _List_DynamicConfigEntry_ValueList []*config.DynamicConfigEntry func (v _List_DynamicConfigEntry_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*config.DynamicConfigEntry', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_DynamicConfigEntry_ValueList) Size() int { return len(v) } func (_List_DynamicConfigEntry_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_DynamicConfigEntry_ValueList) Close() {} // ToWire translates a ListDynamicConfigResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListDynamicConfigResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Entries != nil { w, err = wire.NewValueList(_List_DynamicConfigEntry_ValueList(v.Entries)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DynamicConfigEntry_Read(w wire.Value) (*config.DynamicConfigEntry, error) { var v config.DynamicConfigEntry err := v.FromWire(w) return &v, err } func _List_DynamicConfigEntry_Read(l wire.ValueList) ([]*config.DynamicConfigEntry, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*config.DynamicConfigEntry, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _DynamicConfigEntry_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a ListDynamicConfigResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListDynamicConfigResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListDynamicConfigResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListDynamicConfigResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Entries, err = _List_DynamicConfigEntry_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_DynamicConfigEntry_Encode(val []*config.DynamicConfigEntry, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*config.DynamicConfigEntry', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a ListDynamicConfigResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListDynamicConfigResponse struct could not be encoded. func (v *ListDynamicConfigResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Entries != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_DynamicConfigEntry_Encode(v.Entries, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DynamicConfigEntry_Decode(sr stream.Reader) (*config.DynamicConfigEntry, error) { var v config.DynamicConfigEntry err := v.Decode(sr) return &v, err } func _List_DynamicConfigEntry_Decode(sr stream.Reader) ([]*config.DynamicConfigEntry, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*config.DynamicConfigEntry, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _DynamicConfigEntry_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ListDynamicConfigResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListDynamicConfigResponse struct could not be generated from the wire // representation. func (v *ListDynamicConfigResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Entries, err = _List_DynamicConfigEntry_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListDynamicConfigResponse // struct. func (v *ListDynamicConfigResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Entries != nil { fields[i] = fmt.Sprintf("Entries: %v", v.Entries) i++ } return fmt.Sprintf("ListDynamicConfigResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_DynamicConfigEntry_Equals(lhs, rhs []*config.DynamicConfigEntry) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this ListDynamicConfigResponse match the // provided ListDynamicConfigResponse. // // This function performs a deep comparison. func (v *ListDynamicConfigResponse) Equals(rhs *ListDynamicConfigResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Entries == nil && rhs.Entries == nil) || (v.Entries != nil && rhs.Entries != nil && _List_DynamicConfigEntry_Equals(v.Entries, rhs.Entries))) { return false } return true } type _List_DynamicConfigEntry_Zapper []*config.DynamicConfigEntry // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_DynamicConfigEntry_Zapper. func (l _List_DynamicConfigEntry_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListDynamicConfigResponse. func (v *ListDynamicConfigResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Entries != nil { err = multierr.Append(err, enc.AddArray("entries", (_List_DynamicConfigEntry_Zapper)(v.Entries))) } return err } // GetEntries returns the value of Entries if it is set or its // zero value if it is unset. func (v *ListDynamicConfigResponse) GetEntries() (o []*config.DynamicConfigEntry) { if v != nil && v.Entries != nil { return v.Entries } return } // IsSetEntries returns true if Entries is not nil. func (v *ListDynamicConfigResponse) IsSetEntries() bool { return v != nil && v.Entries != nil } type MembershipInfo struct { CurrentHost *HostInfo `json:"currentHost,omitempty"` ReachableMembers []string `json:"reachableMembers,omitempty"` Rings []*RingInfo `json:"rings,omitempty"` } type _List_String_ValueList []string func (v _List_String_ValueList) ForEach(f func(wire.Value) error) error { for _, x := range v { w, err := wire.NewValueString(x), error(nil) if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_String_ValueList) Size() int { return len(v) } func (_List_String_ValueList) ValueType() wire.Type { return wire.TBinary } func (_List_String_ValueList) Close() {} type _List_RingInfo_ValueList []*RingInfo func (v _List_RingInfo_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*RingInfo', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_RingInfo_ValueList) Size() int { return len(v) } func (_List_RingInfo_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_RingInfo_ValueList) Close() {} // ToWire translates a MembershipInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MembershipInfo) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.CurrentHost != nil { w, err = v.CurrentHost.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ReachableMembers != nil { w, err = wire.NewValueList(_List_String_ValueList(v.ReachableMembers)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Rings != nil { w, err = wire.NewValueList(_List_RingInfo_ValueList(v.Rings)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _HostInfo_Read(w wire.Value) (*HostInfo, error) { var v HostInfo err := v.FromWire(w) return &v, err } func _List_String_Read(l wire.ValueList) ([]string, error) { if l.ValueType() != wire.TBinary { return nil, nil } o := make([]string, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := x.GetString(), error(nil) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _RingInfo_Read(w wire.Value) (*RingInfo, error) { var v RingInfo err := v.FromWire(w) return &v, err } func _List_RingInfo_Read(l wire.ValueList) ([]*RingInfo, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*RingInfo, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _RingInfo_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a MembershipInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MembershipInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MembershipInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MembershipInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.CurrentHost, err = _HostInfo_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.ReachableMembers, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } } case 30: if field.Value.Type() == wire.TList { v.Rings, err = _List_RingInfo_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_String_Encode(val []string, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TBinary, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for _, v := range val { if err := sw.WriteString(v); err != nil { return err } } return sw.WriteListEnd() } func _List_RingInfo_Encode(val []*RingInfo, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*RingInfo', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a MembershipInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MembershipInfo struct could not be encoded. func (v *MembershipInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CurrentHost != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.CurrentHost.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReachableMembers != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.ReachableMembers, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Rings != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TList}); err != nil { return err } if err := _List_RingInfo_Encode(v.Rings, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _HostInfo_Decode(sr stream.Reader) (*HostInfo, error) { var v HostInfo err := v.Decode(sr) return &v, err } func _List_String_Decode(sr stream.Reader) ([]string, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TBinary { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]string, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := sr.ReadString() if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _RingInfo_Decode(sr stream.Reader) (*RingInfo, error) { var v RingInfo err := v.Decode(sr) return &v, err } func _List_RingInfo_Decode(sr stream.Reader) ([]*RingInfo, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*RingInfo, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _RingInfo_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a MembershipInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MembershipInfo struct could not be generated from the wire // representation. func (v *MembershipInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.CurrentHost, err = _HostInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.ReachableMembers, err = _List_String_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TList: v.Rings, err = _List_RingInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MembershipInfo // struct. func (v *MembershipInfo) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.CurrentHost != nil { fields[i] = fmt.Sprintf("CurrentHost: %v", v.CurrentHost) i++ } if v.ReachableMembers != nil { fields[i] = fmt.Sprintf("ReachableMembers: %v", v.ReachableMembers) i++ } if v.Rings != nil { fields[i] = fmt.Sprintf("Rings: %v", v.Rings) i++ } return fmt.Sprintf("MembershipInfo{%v}", strings.Join(fields[:i], ", ")) } func _List_String_Equals(lhs, rhs []string) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !(lv == rv) { return false } } return true } func _List_RingInfo_Equals(lhs, rhs []*RingInfo) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this MembershipInfo match the // provided MembershipInfo. // // This function performs a deep comparison. func (v *MembershipInfo) Equals(rhs *MembershipInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CurrentHost == nil && rhs.CurrentHost == nil) || (v.CurrentHost != nil && rhs.CurrentHost != nil && v.CurrentHost.Equals(rhs.CurrentHost))) { return false } if !((v.ReachableMembers == nil && rhs.ReachableMembers == nil) || (v.ReachableMembers != nil && rhs.ReachableMembers != nil && _List_String_Equals(v.ReachableMembers, rhs.ReachableMembers))) { return false } if !((v.Rings == nil && rhs.Rings == nil) || (v.Rings != nil && rhs.Rings != nil && _List_RingInfo_Equals(v.Rings, rhs.Rings))) { return false } return true } type _List_String_Zapper []string // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_String_Zapper. func (l _List_String_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { enc.AppendString(v) } return err } type _List_RingInfo_Zapper []*RingInfo // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_RingInfo_Zapper. func (l _List_RingInfo_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MembershipInfo. func (v *MembershipInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CurrentHost != nil { err = multierr.Append(err, enc.AddObject("currentHost", v.CurrentHost)) } if v.ReachableMembers != nil { err = multierr.Append(err, enc.AddArray("reachableMembers", (_List_String_Zapper)(v.ReachableMembers))) } if v.Rings != nil { err = multierr.Append(err, enc.AddArray("rings", (_List_RingInfo_Zapper)(v.Rings))) } return err } // GetCurrentHost returns the value of CurrentHost if it is set or its // zero value if it is unset. func (v *MembershipInfo) GetCurrentHost() (o *HostInfo) { if v != nil && v.CurrentHost != nil { return v.CurrentHost } return } // IsSetCurrentHost returns true if CurrentHost is not nil. func (v *MembershipInfo) IsSetCurrentHost() bool { return v != nil && v.CurrentHost != nil } // GetReachableMembers returns the value of ReachableMembers if it is set or its // zero value if it is unset. func (v *MembershipInfo) GetReachableMembers() (o []string) { if v != nil && v.ReachableMembers != nil { return v.ReachableMembers } return } // IsSetReachableMembers returns true if ReachableMembers is not nil. func (v *MembershipInfo) IsSetReachableMembers() bool { return v != nil && v.ReachableMembers != nil } // GetRings returns the value of Rings if it is set or its // zero value if it is unset. func (v *MembershipInfo) GetRings() (o []*RingInfo) { if v != nil && v.Rings != nil { return v.Rings } return } // IsSetRings returns true if Rings is not nil. func (v *MembershipInfo) IsSetRings() bool { return v != nil && v.Rings != nil } type PersistenceFeature struct { Key *string `json:"key,omitempty"` Enabled *bool `json:"enabled,omitempty"` } // ToWire translates a PersistenceFeature struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PersistenceFeature) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Key != nil { w, err = wire.NewValueString(*(v.Key)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Enabled != nil { w, err = wire.NewValueBool(*(v.Enabled)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a PersistenceFeature struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PersistenceFeature struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PersistenceFeature // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PersistenceFeature) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Key = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.Enabled = &x if err != nil { return err } } } } return nil } // Encode serializes a PersistenceFeature struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PersistenceFeature struct could not be encoded. func (v *PersistenceFeature) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Key != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Key)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Enabled != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.Enabled)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a PersistenceFeature struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PersistenceFeature struct could not be generated from the wire // representation. func (v *PersistenceFeature) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Key = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.Enabled = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PersistenceFeature // struct. func (v *PersistenceFeature) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Key != nil { fields[i] = fmt.Sprintf("Key: %v", *(v.Key)) i++ } if v.Enabled != nil { fields[i] = fmt.Sprintf("Enabled: %v", *(v.Enabled)) i++ } return fmt.Sprintf("PersistenceFeature{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PersistenceFeature match the // provided PersistenceFeature. // // This function performs a deep comparison. func (v *PersistenceFeature) Equals(rhs *PersistenceFeature) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Key, rhs.Key) { return false } if !_Bool_EqualsPtr(v.Enabled, rhs.Enabled) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PersistenceFeature. func (v *PersistenceFeature) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Key != nil { enc.AddString("key", *v.Key) } if v.Enabled != nil { enc.AddBool("enabled", *v.Enabled) } return err } // GetKey returns the value of Key if it is set or its // zero value if it is unset. func (v *PersistenceFeature) GetKey() (o string) { if v != nil && v.Key != nil { return *v.Key } return } // IsSetKey returns true if Key is not nil. func (v *PersistenceFeature) IsSetKey() bool { return v != nil && v.Key != nil } // GetEnabled returns the value of Enabled if it is set or its // zero value if it is unset. func (v *PersistenceFeature) GetEnabled() (o bool) { if v != nil && v.Enabled != nil { return *v.Enabled } return } // IsSetEnabled returns true if Enabled is not nil. func (v *PersistenceFeature) IsSetEnabled() bool { return v != nil && v.Enabled != nil } type PersistenceInfo struct { Backend *string `json:"backend,omitempty"` Settings []*PersistenceSetting `json:"settings,omitempty"` Features []*PersistenceFeature `json:"features,omitempty"` } type _List_PersistenceSetting_ValueList []*PersistenceSetting func (v _List_PersistenceSetting_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*PersistenceSetting', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_PersistenceSetting_ValueList) Size() int { return len(v) } func (_List_PersistenceSetting_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_PersistenceSetting_ValueList) Close() {} type _List_PersistenceFeature_ValueList []*PersistenceFeature func (v _List_PersistenceFeature_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*PersistenceFeature', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_PersistenceFeature_ValueList) Size() int { return len(v) } func (_List_PersistenceFeature_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_PersistenceFeature_ValueList) Close() {} // ToWire translates a PersistenceInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PersistenceInfo) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Backend != nil { w, err = wire.NewValueString(*(v.Backend)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Settings != nil { w, err = wire.NewValueList(_List_PersistenceSetting_ValueList(v.Settings)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Features != nil { w, err = wire.NewValueList(_List_PersistenceFeature_ValueList(v.Features)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PersistenceSetting_Read(w wire.Value) (*PersistenceSetting, error) { var v PersistenceSetting err := v.FromWire(w) return &v, err } func _List_PersistenceSetting_Read(l wire.ValueList) ([]*PersistenceSetting, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*PersistenceSetting, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _PersistenceSetting_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _PersistenceFeature_Read(w wire.Value) (*PersistenceFeature, error) { var v PersistenceFeature err := v.FromWire(w) return &v, err } func _List_PersistenceFeature_Read(l wire.ValueList) ([]*PersistenceFeature, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*PersistenceFeature, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _PersistenceFeature_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a PersistenceInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PersistenceInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PersistenceInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PersistenceInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Backend = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Settings, err = _List_PersistenceSetting_Read(field.Value.GetList()) if err != nil { return err } } case 30: if field.Value.Type() == wire.TList { v.Features, err = _List_PersistenceFeature_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_PersistenceSetting_Encode(val []*PersistenceSetting, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*PersistenceSetting', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } func _List_PersistenceFeature_Encode(val []*PersistenceFeature, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*PersistenceFeature', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a PersistenceInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PersistenceInfo struct could not be encoded. func (v *PersistenceInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Backend != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Backend)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Settings != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_PersistenceSetting_Encode(v.Settings, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Features != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TList}); err != nil { return err } if err := _List_PersistenceFeature_Encode(v.Features, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PersistenceSetting_Decode(sr stream.Reader) (*PersistenceSetting, error) { var v PersistenceSetting err := v.Decode(sr) return &v, err } func _List_PersistenceSetting_Decode(sr stream.Reader) ([]*PersistenceSetting, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*PersistenceSetting, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _PersistenceSetting_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _PersistenceFeature_Decode(sr stream.Reader) (*PersistenceFeature, error) { var v PersistenceFeature err := v.Decode(sr) return &v, err } func _List_PersistenceFeature_Decode(sr stream.Reader) ([]*PersistenceFeature, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*PersistenceFeature, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _PersistenceFeature_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a PersistenceInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PersistenceInfo struct could not be generated from the wire // representation. func (v *PersistenceInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Backend = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Settings, err = _List_PersistenceSetting_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TList: v.Features, err = _List_PersistenceFeature_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PersistenceInfo // struct. func (v *PersistenceInfo) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Backend != nil { fields[i] = fmt.Sprintf("Backend: %v", *(v.Backend)) i++ } if v.Settings != nil { fields[i] = fmt.Sprintf("Settings: %v", v.Settings) i++ } if v.Features != nil { fields[i] = fmt.Sprintf("Features: %v", v.Features) i++ } return fmt.Sprintf("PersistenceInfo{%v}", strings.Join(fields[:i], ", ")) } func _List_PersistenceSetting_Equals(lhs, rhs []*PersistenceSetting) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } func _List_PersistenceFeature_Equals(lhs, rhs []*PersistenceFeature) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this PersistenceInfo match the // provided PersistenceInfo. // // This function performs a deep comparison. func (v *PersistenceInfo) Equals(rhs *PersistenceInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Backend, rhs.Backend) { return false } if !((v.Settings == nil && rhs.Settings == nil) || (v.Settings != nil && rhs.Settings != nil && _List_PersistenceSetting_Equals(v.Settings, rhs.Settings))) { return false } if !((v.Features == nil && rhs.Features == nil) || (v.Features != nil && rhs.Features != nil && _List_PersistenceFeature_Equals(v.Features, rhs.Features))) { return false } return true } type _List_PersistenceSetting_Zapper []*PersistenceSetting // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_PersistenceSetting_Zapper. func (l _List_PersistenceSetting_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } type _List_PersistenceFeature_Zapper []*PersistenceFeature // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_PersistenceFeature_Zapper. func (l _List_PersistenceFeature_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PersistenceInfo. func (v *PersistenceInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Backend != nil { enc.AddString("backend", *v.Backend) } if v.Settings != nil { err = multierr.Append(err, enc.AddArray("settings", (_List_PersistenceSetting_Zapper)(v.Settings))) } if v.Features != nil { err = multierr.Append(err, enc.AddArray("features", (_List_PersistenceFeature_Zapper)(v.Features))) } return err } // GetBackend returns the value of Backend if it is set or its // zero value if it is unset. func (v *PersistenceInfo) GetBackend() (o string) { if v != nil && v.Backend != nil { return *v.Backend } return } // IsSetBackend returns true if Backend is not nil. func (v *PersistenceInfo) IsSetBackend() bool { return v != nil && v.Backend != nil } // GetSettings returns the value of Settings if it is set or its // zero value if it is unset. func (v *PersistenceInfo) GetSettings() (o []*PersistenceSetting) { if v != nil && v.Settings != nil { return v.Settings } return } // IsSetSettings returns true if Settings is not nil. func (v *PersistenceInfo) IsSetSettings() bool { return v != nil && v.Settings != nil } // GetFeatures returns the value of Features if it is set or its // zero value if it is unset. func (v *PersistenceInfo) GetFeatures() (o []*PersistenceFeature) { if v != nil && v.Features != nil { return v.Features } return } // IsSetFeatures returns true if Features is not nil. func (v *PersistenceInfo) IsSetFeatures() bool { return v != nil && v.Features != nil } type PersistenceSetting struct { Key *string `json:"key,omitempty"` Value *string `json:"value,omitempty"` } // ToWire translates a PersistenceSetting struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PersistenceSetting) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Key != nil { w, err = wire.NewValueString(*(v.Key)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Value != nil { w, err = wire.NewValueString(*(v.Value)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a PersistenceSetting struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PersistenceSetting struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PersistenceSetting // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PersistenceSetting) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Key = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Value = &x if err != nil { return err } } } } return nil } // Encode serializes a PersistenceSetting struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PersistenceSetting struct could not be encoded. func (v *PersistenceSetting) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Key != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Key)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Value != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Value)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a PersistenceSetting struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PersistenceSetting struct could not be generated from the wire // representation. func (v *PersistenceSetting) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Key = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Value = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PersistenceSetting // struct. func (v *PersistenceSetting) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Key != nil { fields[i] = fmt.Sprintf("Key: %v", *(v.Key)) i++ } if v.Value != nil { fields[i] = fmt.Sprintf("Value: %v", *(v.Value)) i++ } return fmt.Sprintf("PersistenceSetting{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PersistenceSetting match the // provided PersistenceSetting. // // This function performs a deep comparison. func (v *PersistenceSetting) Equals(rhs *PersistenceSetting) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Key, rhs.Key) { return false } if !_String_EqualsPtr(v.Value, rhs.Value) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PersistenceSetting. func (v *PersistenceSetting) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Key != nil { enc.AddString("key", *v.Key) } if v.Value != nil { enc.AddString("value", *v.Value) } return err } // GetKey returns the value of Key if it is set or its // zero value if it is unset. func (v *PersistenceSetting) GetKey() (o string) { if v != nil && v.Key != nil { return *v.Key } return } // IsSetKey returns true if Key is not nil. func (v *PersistenceSetting) IsSetKey() bool { return v != nil && v.Key != nil } // GetValue returns the value of Value if it is set or its // zero value if it is unset. func (v *PersistenceSetting) GetValue() (o string) { if v != nil && v.Value != nil { return *v.Value } return } // IsSetValue returns true if Value is not nil. func (v *PersistenceSetting) IsSetValue() bool { return v != nil && v.Value != nil } type ResendReplicationTasksRequest struct { DomainID *string `json:"domainID,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID *string `json:"runID,omitempty"` RemoteCluster *string `json:"remoteCluster,omitempty"` StartEventID *int64 `json:"startEventID,omitempty"` StartVersion *int64 `json:"startVersion,omitempty"` EndEventID *int64 `json:"endEventID,omitempty"` EndVersion *int64 `json:"endVersion,omitempty"` } // ToWire translates a ResendReplicationTasksRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResendReplicationTasksRequest) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.DomainID != nil { w, err = wire.NewValueString(*(v.DomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueString(*(v.RunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.RemoteCluster != nil { w, err = wire.NewValueString(*(v.RemoteCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.StartEventID != nil { w, err = wire.NewValueI64(*(v.StartEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.StartVersion != nil { w, err = wire.NewValueI64(*(v.StartVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.EndEventID != nil { w, err = wire.NewValueI64(*(v.EndEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.EndVersion != nil { w, err = wire.NewValueI64(*(v.EndVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ResendReplicationTasksRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResendReplicationTasksRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResendReplicationTasksRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResendReplicationTasksRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RemoteCluster = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartEventID = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartVersion = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EndEventID = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EndVersion = &x if err != nil { return err } } } } return nil } // Encode serializes a ResendReplicationTasksRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResendReplicationTasksRequest struct could not be encoded. func (v *ResendReplicationTasksRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RemoteCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RemoteCluster)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EndEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EndEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EndVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EndVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ResendReplicationTasksRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResendReplicationTasksRequest struct could not be generated from the wire // representation. func (v *ResendReplicationTasksRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RemoteCluster = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartEventID = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartVersion = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EndEventID = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EndVersion = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResendReplicationTasksRequest // struct. func (v *ResendReplicationTasksRequest) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", *(v.DomainID)) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", *(v.RunID)) i++ } if v.RemoteCluster != nil { fields[i] = fmt.Sprintf("RemoteCluster: %v", *(v.RemoteCluster)) i++ } if v.StartEventID != nil { fields[i] = fmt.Sprintf("StartEventID: %v", *(v.StartEventID)) i++ } if v.StartVersion != nil { fields[i] = fmt.Sprintf("StartVersion: %v", *(v.StartVersion)) i++ } if v.EndEventID != nil { fields[i] = fmt.Sprintf("EndEventID: %v", *(v.EndEventID)) i++ } if v.EndVersion != nil { fields[i] = fmt.Sprintf("EndVersion: %v", *(v.EndVersion)) i++ } return fmt.Sprintf("ResendReplicationTasksRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ResendReplicationTasksRequest match the // provided ResendReplicationTasksRequest. // // This function performs a deep comparison. func (v *ResendReplicationTasksRequest) Equals(rhs *ResendReplicationTasksRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainID, rhs.DomainID) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !_String_EqualsPtr(v.RunID, rhs.RunID) { return false } if !_String_EqualsPtr(v.RemoteCluster, rhs.RemoteCluster) { return false } if !_I64_EqualsPtr(v.StartEventID, rhs.StartEventID) { return false } if !_I64_EqualsPtr(v.StartVersion, rhs.StartVersion) { return false } if !_I64_EqualsPtr(v.EndEventID, rhs.EndEventID) { return false } if !_I64_EqualsPtr(v.EndVersion, rhs.EndVersion) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResendReplicationTasksRequest. func (v *ResendReplicationTasksRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainID != nil { enc.AddString("domainID", *v.DomainID) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", *v.RunID) } if v.RemoteCluster != nil { enc.AddString("remoteCluster", *v.RemoteCluster) } if v.StartEventID != nil { enc.AddInt64("startEventID", *v.StartEventID) } if v.StartVersion != nil { enc.AddInt64("startVersion", *v.StartVersion) } if v.EndEventID != nil { enc.AddInt64("endEventID", *v.EndEventID) } if v.EndVersion != nil { enc.AddInt64("endVersion", *v.EndVersion) } return err } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *ResendReplicationTasksRequest) GetDomainID() (o string) { if v != nil && v.DomainID != nil { return *v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *ResendReplicationTasksRequest) IsSetDomainID() bool { return v != nil && v.DomainID != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *ResendReplicationTasksRequest) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *ResendReplicationTasksRequest) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *ResendReplicationTasksRequest) GetRunID() (o string) { if v != nil && v.RunID != nil { return *v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *ResendReplicationTasksRequest) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetRemoteCluster returns the value of RemoteCluster if it is set or its // zero value if it is unset. func (v *ResendReplicationTasksRequest) GetRemoteCluster() (o string) { if v != nil && v.RemoteCluster != nil { return *v.RemoteCluster } return } // IsSetRemoteCluster returns true if RemoteCluster is not nil. func (v *ResendReplicationTasksRequest) IsSetRemoteCluster() bool { return v != nil && v.RemoteCluster != nil } // GetStartEventID returns the value of StartEventID if it is set or its // zero value if it is unset. func (v *ResendReplicationTasksRequest) GetStartEventID() (o int64) { if v != nil && v.StartEventID != nil { return *v.StartEventID } return } // IsSetStartEventID returns true if StartEventID is not nil. func (v *ResendReplicationTasksRequest) IsSetStartEventID() bool { return v != nil && v.StartEventID != nil } // GetStartVersion returns the value of StartVersion if it is set or its // zero value if it is unset. func (v *ResendReplicationTasksRequest) GetStartVersion() (o int64) { if v != nil && v.StartVersion != nil { return *v.StartVersion } return } // IsSetStartVersion returns true if StartVersion is not nil. func (v *ResendReplicationTasksRequest) IsSetStartVersion() bool { return v != nil && v.StartVersion != nil } // GetEndEventID returns the value of EndEventID if it is set or its // zero value if it is unset. func (v *ResendReplicationTasksRequest) GetEndEventID() (o int64) { if v != nil && v.EndEventID != nil { return *v.EndEventID } return } // IsSetEndEventID returns true if EndEventID is not nil. func (v *ResendReplicationTasksRequest) IsSetEndEventID() bool { return v != nil && v.EndEventID != nil } // GetEndVersion returns the value of EndVersion if it is set or its // zero value if it is unset. func (v *ResendReplicationTasksRequest) GetEndVersion() (o int64) { if v != nil && v.EndVersion != nil { return *v.EndVersion } return } // IsSetEndVersion returns true if EndVersion is not nil. func (v *ResendReplicationTasksRequest) IsSetEndVersion() bool { return v != nil && v.EndVersion != nil } type RestoreDynamicConfigRequest struct { ConfigName *string `json:"configName,omitempty"` Filters []*config.DynamicConfigFilter `json:"filters,omitempty"` } // ToWire translates a RestoreDynamicConfigRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RestoreDynamicConfigRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ConfigName != nil { w, err = wire.NewValueString(*(v.ConfigName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Filters != nil { w, err = wire.NewValueList(_List_DynamicConfigFilter_ValueList(v.Filters)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RestoreDynamicConfigRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RestoreDynamicConfigRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RestoreDynamicConfigRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RestoreDynamicConfigRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ConfigName = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Filters, err = _List_DynamicConfigFilter_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } // Encode serializes a RestoreDynamicConfigRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RestoreDynamicConfigRequest struct could not be encoded. func (v *RestoreDynamicConfigRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ConfigName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ConfigName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Filters != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_DynamicConfigFilter_Encode(v.Filters, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RestoreDynamicConfigRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RestoreDynamicConfigRequest struct could not be generated from the wire // representation. func (v *RestoreDynamicConfigRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ConfigName = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Filters, err = _List_DynamicConfigFilter_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RestoreDynamicConfigRequest // struct. func (v *RestoreDynamicConfigRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ConfigName != nil { fields[i] = fmt.Sprintf("ConfigName: %v", *(v.ConfigName)) i++ } if v.Filters != nil { fields[i] = fmt.Sprintf("Filters: %v", v.Filters) i++ } return fmt.Sprintf("RestoreDynamicConfigRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RestoreDynamicConfigRequest match the // provided RestoreDynamicConfigRequest. // // This function performs a deep comparison. func (v *RestoreDynamicConfigRequest) Equals(rhs *RestoreDynamicConfigRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ConfigName, rhs.ConfigName) { return false } if !((v.Filters == nil && rhs.Filters == nil) || (v.Filters != nil && rhs.Filters != nil && _List_DynamicConfigFilter_Equals(v.Filters, rhs.Filters))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RestoreDynamicConfigRequest. func (v *RestoreDynamicConfigRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ConfigName != nil { enc.AddString("configName", *v.ConfigName) } if v.Filters != nil { err = multierr.Append(err, enc.AddArray("filters", (_List_DynamicConfigFilter_Zapper)(v.Filters))) } return err } // GetConfigName returns the value of ConfigName if it is set or its // zero value if it is unset. func (v *RestoreDynamicConfigRequest) GetConfigName() (o string) { if v != nil && v.ConfigName != nil { return *v.ConfigName } return } // IsSetConfigName returns true if ConfigName is not nil. func (v *RestoreDynamicConfigRequest) IsSetConfigName() bool { return v != nil && v.ConfigName != nil } // GetFilters returns the value of Filters if it is set or its // zero value if it is unset. func (v *RestoreDynamicConfigRequest) GetFilters() (o []*config.DynamicConfigFilter) { if v != nil && v.Filters != nil { return v.Filters } return } // IsSetFilters returns true if Filters is not nil. func (v *RestoreDynamicConfigRequest) IsSetFilters() bool { return v != nil && v.Filters != nil } type RingInfo struct { Role *string `json:"role,omitempty"` MemberCount *int32 `json:"memberCount,omitempty"` Members []*HostInfo `json:"members,omitempty"` } type _List_HostInfo_ValueList []*HostInfo func (v _List_HostInfo_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*HostInfo', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_HostInfo_ValueList) Size() int { return len(v) } func (_List_HostInfo_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_HostInfo_ValueList) Close() {} // ToWire translates a RingInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RingInfo) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Role != nil { w, err = wire.NewValueString(*(v.Role)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.MemberCount != nil { w, err = wire.NewValueI32(*(v.MemberCount)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Members != nil { w, err = wire.NewValueList(_List_HostInfo_ValueList(v.Members)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _List_HostInfo_Read(l wire.ValueList) ([]*HostInfo, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*HostInfo, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _HostInfo_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a RingInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RingInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RingInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RingInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Role = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.MemberCount = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TList { v.Members, err = _List_HostInfo_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_HostInfo_Encode(val []*HostInfo, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*HostInfo', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a RingInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RingInfo struct could not be encoded. func (v *RingInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Role != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Role)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MemberCount != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.MemberCount)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Members != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TList}); err != nil { return err } if err := _List_HostInfo_Encode(v.Members, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _List_HostInfo_Decode(sr stream.Reader) ([]*HostInfo, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*HostInfo, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _HostInfo_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a RingInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RingInfo struct could not be generated from the wire // representation. func (v *RingInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Role = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.MemberCount = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TList: v.Members, err = _List_HostInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RingInfo // struct. func (v *RingInfo) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Role != nil { fields[i] = fmt.Sprintf("Role: %v", *(v.Role)) i++ } if v.MemberCount != nil { fields[i] = fmt.Sprintf("MemberCount: %v", *(v.MemberCount)) i++ } if v.Members != nil { fields[i] = fmt.Sprintf("Members: %v", v.Members) i++ } return fmt.Sprintf("RingInfo{%v}", strings.Join(fields[:i], ", ")) } func _List_HostInfo_Equals(lhs, rhs []*HostInfo) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this RingInfo match the // provided RingInfo. // // This function performs a deep comparison. func (v *RingInfo) Equals(rhs *RingInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Role, rhs.Role) { return false } if !_I32_EqualsPtr(v.MemberCount, rhs.MemberCount) { return false } if !((v.Members == nil && rhs.Members == nil) || (v.Members != nil && rhs.Members != nil && _List_HostInfo_Equals(v.Members, rhs.Members))) { return false } return true } type _List_HostInfo_Zapper []*HostInfo // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_HostInfo_Zapper. func (l _List_HostInfo_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RingInfo. func (v *RingInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Role != nil { enc.AddString("role", *v.Role) } if v.MemberCount != nil { enc.AddInt32("memberCount", *v.MemberCount) } if v.Members != nil { err = multierr.Append(err, enc.AddArray("members", (_List_HostInfo_Zapper)(v.Members))) } return err } // GetRole returns the value of Role if it is set or its // zero value if it is unset. func (v *RingInfo) GetRole() (o string) { if v != nil && v.Role != nil { return *v.Role } return } // IsSetRole returns true if Role is not nil. func (v *RingInfo) IsSetRole() bool { return v != nil && v.Role != nil } // GetMemberCount returns the value of MemberCount if it is set or its // zero value if it is unset. func (v *RingInfo) GetMemberCount() (o int32) { if v != nil && v.MemberCount != nil { return *v.MemberCount } return } // IsSetMemberCount returns true if MemberCount is not nil. func (v *RingInfo) IsSetMemberCount() bool { return v != nil && v.MemberCount != nil } // GetMembers returns the value of Members if it is set or its // zero value if it is unset. func (v *RingInfo) GetMembers() (o []*HostInfo) { if v != nil && v.Members != nil { return v.Members } return } // IsSetMembers returns true if Members is not nil. func (v *RingInfo) IsSetMembers() bool { return v != nil && v.Members != nil } type UpdateDomainAsyncWorkflowConfiguratonRequest struct { Domain *string `json:"domain,omitempty"` Configuration *shared.AsyncWorkflowConfiguration `json:"configuration,omitempty"` } // ToWire translates a UpdateDomainAsyncWorkflowConfiguratonRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Configuration != nil { w, err = v.Configuration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UpdateDomainAsyncWorkflowConfiguratonRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpdateDomainAsyncWorkflowConfiguratonRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpdateDomainAsyncWorkflowConfiguratonRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Configuration, err = _AsyncWorkflowConfiguration_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a UpdateDomainAsyncWorkflowConfiguratonRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpdateDomainAsyncWorkflowConfiguratonRequest struct could not be encoded. func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Configuration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Configuration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a UpdateDomainAsyncWorkflowConfiguratonRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpdateDomainAsyncWorkflowConfiguratonRequest struct could not be generated from the wire // representation. func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Configuration, err = _AsyncWorkflowConfiguration_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpdateDomainAsyncWorkflowConfiguratonRequest // struct. func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Configuration != nil { fields[i] = fmt.Sprintf("Configuration: %v", v.Configuration) i++ } return fmt.Sprintf("UpdateDomainAsyncWorkflowConfiguratonRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpdateDomainAsyncWorkflowConfiguratonRequest match the // provided UpdateDomainAsyncWorkflowConfiguratonRequest. // // This function performs a deep comparison. func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) Equals(rhs *UpdateDomainAsyncWorkflowConfiguratonRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Configuration == nil && rhs.Configuration == nil) || (v.Configuration != nil && rhs.Configuration != nil && v.Configuration.Equals(rhs.Configuration))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpdateDomainAsyncWorkflowConfiguratonRequest. func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Configuration != nil { err = multierr.Append(err, enc.AddObject("configuration", v.Configuration)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetConfiguration returns the value of Configuration if it is set or its // zero value if it is unset. func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) GetConfiguration() (o *shared.AsyncWorkflowConfiguration) { if v != nil && v.Configuration != nil { return v.Configuration } return } // IsSetConfiguration returns true if Configuration is not nil. func (v *UpdateDomainAsyncWorkflowConfiguratonRequest) IsSetConfiguration() bool { return v != nil && v.Configuration != nil } type UpdateDomainAsyncWorkflowConfiguratonResponse struct { } // ToWire translates a UpdateDomainAsyncWorkflowConfiguratonResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpdateDomainAsyncWorkflowConfiguratonResponse) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UpdateDomainAsyncWorkflowConfiguratonResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpdateDomainAsyncWorkflowConfiguratonResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpdateDomainAsyncWorkflowConfiguratonResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpdateDomainAsyncWorkflowConfiguratonResponse) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a UpdateDomainAsyncWorkflowConfiguratonResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpdateDomainAsyncWorkflowConfiguratonResponse struct could not be encoded. func (v *UpdateDomainAsyncWorkflowConfiguratonResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a UpdateDomainAsyncWorkflowConfiguratonResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpdateDomainAsyncWorkflowConfiguratonResponse struct could not be generated from the wire // representation. func (v *UpdateDomainAsyncWorkflowConfiguratonResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpdateDomainAsyncWorkflowConfiguratonResponse // struct. func (v *UpdateDomainAsyncWorkflowConfiguratonResponse) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("UpdateDomainAsyncWorkflowConfiguratonResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpdateDomainAsyncWorkflowConfiguratonResponse match the // provided UpdateDomainAsyncWorkflowConfiguratonResponse. // // This function performs a deep comparison. func (v *UpdateDomainAsyncWorkflowConfiguratonResponse) Equals(rhs *UpdateDomainAsyncWorkflowConfiguratonResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpdateDomainAsyncWorkflowConfiguratonResponse. func (v *UpdateDomainAsyncWorkflowConfiguratonResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type UpdateDomainIsolationGroupsRequest struct { Domain *string `json:"domain,omitempty"` IsolationGroups *shared.IsolationGroupConfiguration `json:"isolationGroups,omitempty"` } // ToWire translates a UpdateDomainIsolationGroupsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpdateDomainIsolationGroupsRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.IsolationGroups != nil { w, err = v.IsolationGroups.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UpdateDomainIsolationGroupsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpdateDomainIsolationGroupsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpdateDomainIsolationGroupsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpdateDomainIsolationGroupsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.IsolationGroups, err = _IsolationGroupConfiguration_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a UpdateDomainIsolationGroupsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpdateDomainIsolationGroupsRequest struct could not be encoded. func (v *UpdateDomainIsolationGroupsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsolationGroups != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.IsolationGroups.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a UpdateDomainIsolationGroupsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpdateDomainIsolationGroupsRequest struct could not be generated from the wire // representation. func (v *UpdateDomainIsolationGroupsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.IsolationGroups, err = _IsolationGroupConfiguration_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpdateDomainIsolationGroupsRequest // struct. func (v *UpdateDomainIsolationGroupsRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.IsolationGroups != nil { fields[i] = fmt.Sprintf("IsolationGroups: %v", v.IsolationGroups) i++ } return fmt.Sprintf("UpdateDomainIsolationGroupsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpdateDomainIsolationGroupsRequest match the // provided UpdateDomainIsolationGroupsRequest. // // This function performs a deep comparison. func (v *UpdateDomainIsolationGroupsRequest) Equals(rhs *UpdateDomainIsolationGroupsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.IsolationGroups == nil && rhs.IsolationGroups == nil) || (v.IsolationGroups != nil && rhs.IsolationGroups != nil && v.IsolationGroups.Equals(rhs.IsolationGroups))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpdateDomainIsolationGroupsRequest. func (v *UpdateDomainIsolationGroupsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.IsolationGroups != nil { err = multierr.Append(err, enc.AddObject("isolationGroups", v.IsolationGroups)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *UpdateDomainIsolationGroupsRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *UpdateDomainIsolationGroupsRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetIsolationGroups returns the value of IsolationGroups if it is set or its // zero value if it is unset. func (v *UpdateDomainIsolationGroupsRequest) GetIsolationGroups() (o *shared.IsolationGroupConfiguration) { if v != nil && v.IsolationGroups != nil { return v.IsolationGroups } return } // IsSetIsolationGroups returns true if IsolationGroups is not nil. func (v *UpdateDomainIsolationGroupsRequest) IsSetIsolationGroups() bool { return v != nil && v.IsolationGroups != nil } type UpdateDomainIsolationGroupsResponse struct { } // ToWire translates a UpdateDomainIsolationGroupsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpdateDomainIsolationGroupsResponse) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UpdateDomainIsolationGroupsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpdateDomainIsolationGroupsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpdateDomainIsolationGroupsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpdateDomainIsolationGroupsResponse) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a UpdateDomainIsolationGroupsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpdateDomainIsolationGroupsResponse struct could not be encoded. func (v *UpdateDomainIsolationGroupsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a UpdateDomainIsolationGroupsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpdateDomainIsolationGroupsResponse struct could not be generated from the wire // representation. func (v *UpdateDomainIsolationGroupsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpdateDomainIsolationGroupsResponse // struct. func (v *UpdateDomainIsolationGroupsResponse) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("UpdateDomainIsolationGroupsResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpdateDomainIsolationGroupsResponse match the // provided UpdateDomainIsolationGroupsResponse. // // This function performs a deep comparison. func (v *UpdateDomainIsolationGroupsResponse) Equals(rhs *UpdateDomainIsolationGroupsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpdateDomainIsolationGroupsResponse. func (v *UpdateDomainIsolationGroupsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type UpdateDynamicConfigRequest struct { ConfigName *string `json:"configName,omitempty"` ConfigValues []*config.DynamicConfigValue `json:"configValues,omitempty"` } type _List_DynamicConfigValue_ValueList []*config.DynamicConfigValue func (v _List_DynamicConfigValue_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*config.DynamicConfigValue', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_DynamicConfigValue_ValueList) Size() int { return len(v) } func (_List_DynamicConfigValue_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_DynamicConfigValue_ValueList) Close() {} // ToWire translates a UpdateDynamicConfigRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpdateDynamicConfigRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ConfigName != nil { w, err = wire.NewValueString(*(v.ConfigName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ConfigValues != nil { w, err = wire.NewValueList(_List_DynamicConfigValue_ValueList(v.ConfigValues)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DynamicConfigValue_Read(w wire.Value) (*config.DynamicConfigValue, error) { var v config.DynamicConfigValue err := v.FromWire(w) return &v, err } func _List_DynamicConfigValue_Read(l wire.ValueList) ([]*config.DynamicConfigValue, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*config.DynamicConfigValue, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _DynamicConfigValue_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a UpdateDynamicConfigRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpdateDynamicConfigRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpdateDynamicConfigRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpdateDynamicConfigRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ConfigName = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.ConfigValues, err = _List_DynamicConfigValue_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_DynamicConfigValue_Encode(val []*config.DynamicConfigValue, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*config.DynamicConfigValue', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a UpdateDynamicConfigRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpdateDynamicConfigRequest struct could not be encoded. func (v *UpdateDynamicConfigRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ConfigName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ConfigName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ConfigValues != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_DynamicConfigValue_Encode(v.ConfigValues, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DynamicConfigValue_Decode(sr stream.Reader) (*config.DynamicConfigValue, error) { var v config.DynamicConfigValue err := v.Decode(sr) return &v, err } func _List_DynamicConfigValue_Decode(sr stream.Reader) ([]*config.DynamicConfigValue, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*config.DynamicConfigValue, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _DynamicConfigValue_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a UpdateDynamicConfigRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpdateDynamicConfigRequest struct could not be generated from the wire // representation. func (v *UpdateDynamicConfigRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ConfigName = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.ConfigValues, err = _List_DynamicConfigValue_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpdateDynamicConfigRequest // struct. func (v *UpdateDynamicConfigRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ConfigName != nil { fields[i] = fmt.Sprintf("ConfigName: %v", *(v.ConfigName)) i++ } if v.ConfigValues != nil { fields[i] = fmt.Sprintf("ConfigValues: %v", v.ConfigValues) i++ } return fmt.Sprintf("UpdateDynamicConfigRequest{%v}", strings.Join(fields[:i], ", ")) } func _List_DynamicConfigValue_Equals(lhs, rhs []*config.DynamicConfigValue) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this UpdateDynamicConfigRequest match the // provided UpdateDynamicConfigRequest. // // This function performs a deep comparison. func (v *UpdateDynamicConfigRequest) Equals(rhs *UpdateDynamicConfigRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ConfigName, rhs.ConfigName) { return false } if !((v.ConfigValues == nil && rhs.ConfigValues == nil) || (v.ConfigValues != nil && rhs.ConfigValues != nil && _List_DynamicConfigValue_Equals(v.ConfigValues, rhs.ConfigValues))) { return false } return true } type _List_DynamicConfigValue_Zapper []*config.DynamicConfigValue // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_DynamicConfigValue_Zapper. func (l _List_DynamicConfigValue_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpdateDynamicConfigRequest. func (v *UpdateDynamicConfigRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ConfigName != nil { enc.AddString("configName", *v.ConfigName) } if v.ConfigValues != nil { err = multierr.Append(err, enc.AddArray("configValues", (_List_DynamicConfigValue_Zapper)(v.ConfigValues))) } return err } // GetConfigName returns the value of ConfigName if it is set or its // zero value if it is unset. func (v *UpdateDynamicConfigRequest) GetConfigName() (o string) { if v != nil && v.ConfigName != nil { return *v.ConfigName } return } // IsSetConfigName returns true if ConfigName is not nil. func (v *UpdateDynamicConfigRequest) IsSetConfigName() bool { return v != nil && v.ConfigName != nil } // GetConfigValues returns the value of ConfigValues if it is set or its // zero value if it is unset. func (v *UpdateDynamicConfigRequest) GetConfigValues() (o []*config.DynamicConfigValue) { if v != nil && v.ConfigValues != nil { return v.ConfigValues } return } // IsSetConfigValues returns true if ConfigValues is not nil. func (v *UpdateDynamicConfigRequest) IsSetConfigValues() bool { return v != nil && v.ConfigValues != nil } type UpdateGlobalIsolationGroupsRequest struct { IsolationGroups *shared.IsolationGroupConfiguration `json:"isolationGroups,omitempty"` } // ToWire translates a UpdateGlobalIsolationGroupsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpdateGlobalIsolationGroupsRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.IsolationGroups != nil { w, err = v.IsolationGroups.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UpdateGlobalIsolationGroupsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpdateGlobalIsolationGroupsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpdateGlobalIsolationGroupsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpdateGlobalIsolationGroupsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.IsolationGroups, err = _IsolationGroupConfiguration_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a UpdateGlobalIsolationGroupsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpdateGlobalIsolationGroupsRequest struct could not be encoded. func (v *UpdateGlobalIsolationGroupsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.IsolationGroups != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.IsolationGroups.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a UpdateGlobalIsolationGroupsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpdateGlobalIsolationGroupsRequest struct could not be generated from the wire // representation. func (v *UpdateGlobalIsolationGroupsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.IsolationGroups, err = _IsolationGroupConfiguration_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpdateGlobalIsolationGroupsRequest // struct. func (v *UpdateGlobalIsolationGroupsRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.IsolationGroups != nil { fields[i] = fmt.Sprintf("IsolationGroups: %v", v.IsolationGroups) i++ } return fmt.Sprintf("UpdateGlobalIsolationGroupsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpdateGlobalIsolationGroupsRequest match the // provided UpdateGlobalIsolationGroupsRequest. // // This function performs a deep comparison. func (v *UpdateGlobalIsolationGroupsRequest) Equals(rhs *UpdateGlobalIsolationGroupsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.IsolationGroups == nil && rhs.IsolationGroups == nil) || (v.IsolationGroups != nil && rhs.IsolationGroups != nil && v.IsolationGroups.Equals(rhs.IsolationGroups))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpdateGlobalIsolationGroupsRequest. func (v *UpdateGlobalIsolationGroupsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.IsolationGroups != nil { err = multierr.Append(err, enc.AddObject("isolationGroups", v.IsolationGroups)) } return err } // GetIsolationGroups returns the value of IsolationGroups if it is set or its // zero value if it is unset. func (v *UpdateGlobalIsolationGroupsRequest) GetIsolationGroups() (o *shared.IsolationGroupConfiguration) { if v != nil && v.IsolationGroups != nil { return v.IsolationGroups } return } // IsSetIsolationGroups returns true if IsolationGroups is not nil. func (v *UpdateGlobalIsolationGroupsRequest) IsSetIsolationGroups() bool { return v != nil && v.IsolationGroups != nil } type UpdateGlobalIsolationGroupsResponse struct { } // ToWire translates a UpdateGlobalIsolationGroupsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpdateGlobalIsolationGroupsResponse) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UpdateGlobalIsolationGroupsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpdateGlobalIsolationGroupsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpdateGlobalIsolationGroupsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpdateGlobalIsolationGroupsResponse) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a UpdateGlobalIsolationGroupsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpdateGlobalIsolationGroupsResponse struct could not be encoded. func (v *UpdateGlobalIsolationGroupsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a UpdateGlobalIsolationGroupsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpdateGlobalIsolationGroupsResponse struct could not be generated from the wire // representation. func (v *UpdateGlobalIsolationGroupsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpdateGlobalIsolationGroupsResponse // struct. func (v *UpdateGlobalIsolationGroupsResponse) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("UpdateGlobalIsolationGroupsResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpdateGlobalIsolationGroupsResponse match the // provided UpdateGlobalIsolationGroupsResponse. // // This function performs a deep comparison. func (v *UpdateGlobalIsolationGroupsResponse) Equals(rhs *UpdateGlobalIsolationGroupsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpdateGlobalIsolationGroupsResponse. func (v *UpdateGlobalIsolationGroupsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "admin", Package: "github.com/uber/cadence/.gen/go/admin", FilePath: "admin.thrift", SHA1: "cd332257c2e6f1bcc07bdfaae63cb757ecf2cba3", Includes: []*thriftreflect.ThriftModule{ config.ThriftModule, replicator.ThriftModule, shared.ThriftModule, }, Raw: rawIDL, } const rawIDL = "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nnamespace java com.uber.cadence.admin\n\ninclude \"shared.thrift\"\ninclude \"replicator.thrift\"\ninclude \"config.thrift\"\n\n/**\n* AdminService provides advanced APIs for debugging and analysis with admin privilege\n**/\nservice AdminService {\n /**\n * DescribeWorkflowExecution returns information about the internal states of workflow execution.\n **/\n DescribeWorkflowExecutionResponse DescribeWorkflowExecution(1: DescribeWorkflowExecutionRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * DescribeShardDistribution returns information about history shards within the cluster\n **/\n shared.DescribeShardDistributionResponse DescribeShardDistribution(1: shared.DescribeShardDistributionRequest request)\n throws (\n 1: shared.InternalServiceError internalServiceError,\n )\n\n /**\n * DescribeHistoryHost returns information about the internal states of a history host\n **/\n shared.DescribeHistoryHostResponse DescribeHistoryHost(1: shared.DescribeHistoryHostRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n void CloseShard(1: shared.CloseShardRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n void RemoveTask(1: shared.RemoveTaskRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n void ResetQueue(1: shared.ResetQueueRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n shared.DescribeQueueResponse DescribeQueue(1: shared.DescribeQueueRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * Returns the raw history of specified workflow execution. It fails with 'EntityNotExistError' if speficied workflow\n * execution in unknown to the service.\n * StartEventId defines the beginning of the event to fetch. The first event is inclusive.\n * EndEventId and EndEventVersion defines the end of the event to fetch. The end event is exclusive.\n **/\n GetWorkflowExecutionRawHistoryV2Response GetWorkflowExecutionRawHistoryV2(1: GetWorkflowExecutionRawHistoryV2Request getRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n )\n\n replicator.GetReplicationMessagesResponse GetReplicationMessages(1: replicator.GetReplicationMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.LimitExceededError limitExceededError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n )\n\n replicator.GetDomainReplicationMessagesResponse GetDomainReplicationMessages(1: replicator.GetDomainReplicationMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.LimitExceededError limitExceededError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n )\n\n replicator.GetDLQReplicationMessagesResponse GetDLQReplicationMessages(1: replicator.GetDLQReplicationMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * ReapplyEvents applies stale events to the current workflow and current run\n **/\n void ReapplyEvents(1: shared.ReapplyEventsRequest reapplyEventsRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.DomainNotActiveError domainNotActiveError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.ServiceBusyError serviceBusyError,\n 6: shared.EntityNotExistsError entityNotExistError,\n )\n\n /**\n * AddSearchAttribute whitelist search attribute in request.\n **/\n void AddSearchAttribute(1: AddSearchAttributeRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * DescribeCluster returns information about cadence cluster\n **/\n DescribeClusterResponse DescribeCluster()\n throws (\n 1: shared.InternalServiceError internalServiceError,\n 2: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * ReadDLQMessages returns messages from DLQ\n **/\n replicator.ReadDLQMessagesResponse ReadDLQMessages(1: replicator.ReadDLQMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.EntityNotExistsError entityNotExistError,\n )\n\n /**\n * PurgeDLQMessages purges messages from DLQ\n **/\n void PurgeDLQMessages(1: replicator.PurgeDLQMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.EntityNotExistsError entityNotExistError,\n )\n\n /**\n * MergeDLQMessages merges messages from DLQ\n **/\n replicator.MergeDLQMessagesResponse MergeDLQMessages(1: replicator.MergeDLQMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.EntityNotExistsError entityNotExistError,\n )\n\n /**\n * RefreshWorkflowTasks refreshes all tasks of a workflow\n **/\n void RefreshWorkflowTasks(1: shared.RefreshWorkflowTasksRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.DomainNotActiveError domainNotActiveError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.EntityNotExistsError entityNotExistError,\n )\n\n /**\n * ResendReplicationTasks requests replication tasks from remote cluster and apply tasks to current cluster\n **/\n void ResendReplicationTasks(1: ResendReplicationTasksRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.ServiceBusyError serviceBusyError,\n 3: shared.EntityNotExistsError entityNotExistError,\n )\n\n /**\n * GetCrossClusterTasks fetches cross cluster tasks\n **/\n shared.GetCrossClusterTasksResponse GetCrossClusterTasks(1: shared.GetCrossClusterTasksRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * RespondCrossClusterTasksCompleted responds the result of processing cross cluster tasks\n **/\n shared.RespondCrossClusterTasksCompletedResponse RespondCrossClusterTasksCompleted(1: shared.RespondCrossClusterTasksCompletedRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * GetDynamicConfig returns values associated with a specified dynamic config parameter.\n **/\n GetDynamicConfigResponse GetDynamicConfig(1: GetDynamicConfigRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n )\n\n void UpdateDynamicConfig(1: UpdateDynamicConfigRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n )\n\n void RestoreDynamicConfig(1: RestoreDynamicConfigRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n )\n\n ListDynamicConfigResponse ListDynamicConfig(1: ListDynamicConfigRequest request)\n throws (\n 1: shared.InternalServiceError internalServiceError,\n )\n\n AdminDeleteWorkflowResponse DeleteWorkflow(1: AdminDeleteWorkflowRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.EntityNotExistsError entityNotExistError,\n 3: shared.InternalServiceError internalServiceError,\n )\n\n AdminMaintainWorkflowResponse MaintainCorruptWorkflow(1: AdminMaintainWorkflowRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.EntityNotExistsError entityNotExistError,\n 3: shared.InternalServiceError internalServiceError,\n )\n\n GetGlobalIsolationGroupsResponse GetGlobalIsolationGroups(1: GetGlobalIsolationGroupsRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n )\n\n UpdateGlobalIsolationGroupsResponse UpdateGlobalIsolationGroups(1: UpdateGlobalIsolationGroupsRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n )\n\n GetDomainIsolationGroupsResponse GetDomainIsolationGroups(1: GetDomainIsolationGroupsRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n )\n\n UpdateDomainIsolationGroupsResponse UpdateDomainIsolationGroups(1: UpdateDomainIsolationGroupsRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n )\n\n\n GetDomainAsyncWorkflowConfiguratonResponse GetDomainAsyncWorkflowConfiguraton(1: GetDomainAsyncWorkflowConfiguratonRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n )\n\n UpdateDomainAsyncWorkflowConfiguratonResponse UpdateDomainAsyncWorkflowConfiguraton(1: UpdateDomainAsyncWorkflowConfiguratonRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n )\n}\n\nstruct DescribeWorkflowExecutionRequest {\n 10: optional string domain\n 20: optional shared.WorkflowExecution execution\n}\n\nstruct DescribeWorkflowExecutionResponse {\n 10: optional string shardId\n 20: optional string historyAddr\n 40: optional string mutableStateInCache\n 50: optional string mutableStateInDatabase\n}\n\n/**\n * StartEventId defines the beginning of the event to fetch. The first event is exclusive.\n * EndEventId and EndEventVersion defines the end of the event to fetch. The end event is exclusive.\n **/\nstruct GetWorkflowExecutionRawHistoryV2Request {\n 10: optional string domain\n 20: optional shared.WorkflowExecution execution\n 30: optional i64 (js.type = \"Long\") startEventId\n 40: optional i64 (js.type = \"Long\") startEventVersion\n 50: optional i64 (js.type = \"Long\") endEventId\n 60: optional i64 (js.type = \"Long\") endEventVersion\n 70: optional i32 maximumPageSize\n 80: optional binary nextPageToken\n}\n\nstruct GetWorkflowExecutionRawHistoryV2Response {\n 10: optional binary nextPageToken\n 20: optional list historyBatches\n 30: optional shared.VersionHistory versionHistory\n}\n\nstruct AddSearchAttributeRequest {\n 10: optional map searchAttribute\n 20: optional string securityToken\n}\n\nstruct HostInfo {\n 10: optional string Identity\n}\n\nstruct RingInfo {\n 10: optional string role\n 20: optional i32 memberCount\n 30: optional list members\n}\n\nstruct MembershipInfo {\n 10: optional HostInfo currentHost\n 20: optional list reachableMembers\n 30: optional list rings\n}\n\nstruct PersistenceSetting {\n 10: optional string key\n 20: optional string value\n}\n\nstruct PersistenceFeature {\n 10: optional string key\n 20: optional bool enabled\n}\n\nstruct PersistenceInfo {\n 10: optional string backend\n 20: optional list settings\n 30: optional list features\n}\n\nstruct DescribeClusterResponse {\n 10: optional shared.SupportedClientVersions supportedClientVersions\n 20: optional MembershipInfo membershipInfo\n 30: optional map persistenceInfo\n}\n\nstruct ResendReplicationTasksRequest {\n 10: optional string domainID\n 20: optional string workflowID\n 30: optional string runID\n 40: optional string remoteCluster\n 50: optional i64 (js.type = \"Long\") startEventID\n 60: optional i64 (js.type = \"Long\") startVersion\n 70: optional i64 (js.type = \"Long\") endEventID\n 80: optional i64 (js.type = \"Long\") endVersion\n}\n\nstruct GetDynamicConfigRequest {\n 10: optional string configName\n 20: optional list filters\n}\n\nstruct GetDynamicConfigResponse {\n 10: optional shared.DataBlob value\n}\n\nstruct UpdateDynamicConfigRequest {\n 10: optional string configName\n 20: optional list configValues\n}\n\nstruct RestoreDynamicConfigRequest {\n 10: optional string configName\n 20: optional list filters\n}\n\nstruct AdminDeleteWorkflowRequest {\n 10: optional string domain\n 20: optional shared.WorkflowExecution execution\n}\n\nstruct AdminDeleteWorkflowResponse {\n 10: optional bool historyDeleted\n 20: optional bool executionsDeleted\n 30: optional bool visibilityDeleted\n}\n\nstruct AdminMaintainWorkflowRequest {\n 10: optional string domain\n 20: optional shared.WorkflowExecution execution\n}\n\nstruct AdminMaintainWorkflowResponse {\n 10: optional bool historyDeleted\n 20: optional bool executionsDeleted\n 30: optional bool visibilityDeleted\n}\n\n//Eventually remove configName and integrate this functionality into Get.\n//GetDynamicConfigResponse would need to change as well.\nstruct ListDynamicConfigRequest {\n 10: optional string configName\n}\n\nstruct ListDynamicConfigResponse {\n 10: optional list entries\n}\n\n// global\nstruct GetGlobalIsolationGroupsRequest{}\n\nstruct GetGlobalIsolationGroupsResponse{\n 10: optional shared.IsolationGroupConfiguration isolationGroups\n}\n\nstruct UpdateGlobalIsolationGroupsRequest{\n 10: optional shared.IsolationGroupConfiguration isolationGroups\n}\n\nstruct UpdateGlobalIsolationGroupsResponse{}\n\n\n// For domains\nstruct GetDomainIsolationGroupsRequest{\n 10: optional string domain\n}\n\nstruct GetDomainIsolationGroupsResponse{\n 10: optional shared.IsolationGroupConfiguration isolationGroups\n}\n\nstruct UpdateDomainIsolationGroupsRequest{\n 10: optional string domain\n 20: optional shared.IsolationGroupConfiguration isolationGroups\n}\n\nstruct UpdateDomainIsolationGroupsResponse{}\n\n// Async workflow configuration request/response payloads\nstruct GetDomainAsyncWorkflowConfiguratonRequest {\n 10: optional string domain\n}\n\nstruct GetDomainAsyncWorkflowConfiguratonResponse {\n 10: optional shared.AsyncWorkflowConfiguration configuration\n}\n\nstruct UpdateDomainAsyncWorkflowConfiguratonRequest {\n 10: optional string domain\n 20: optional shared.AsyncWorkflowConfiguration configuration\n}\n\nstruct UpdateDomainAsyncWorkflowConfiguratonResponse {}\n" // AdminService_AddSearchAttribute_Args represents the arguments for the AdminService.AddSearchAttribute function. // // The arguments for AddSearchAttribute are sent and received over the wire as this struct. type AdminService_AddSearchAttribute_Args struct { Request *AddSearchAttributeRequest `json:"request,omitempty"` } // ToWire translates a AdminService_AddSearchAttribute_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_AddSearchAttribute_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AddSearchAttributeRequest_Read(w wire.Value) (*AddSearchAttributeRequest, error) { var v AddSearchAttributeRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_AddSearchAttribute_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_AddSearchAttribute_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_AddSearchAttribute_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_AddSearchAttribute_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _AddSearchAttributeRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_AddSearchAttribute_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_AddSearchAttribute_Args struct could not be encoded. func (v *AdminService_AddSearchAttribute_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _AddSearchAttributeRequest_Decode(sr stream.Reader) (*AddSearchAttributeRequest, error) { var v AddSearchAttributeRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_AddSearchAttribute_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_AddSearchAttribute_Args struct could not be generated from the wire // representation. func (v *AdminService_AddSearchAttribute_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _AddSearchAttributeRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_AddSearchAttribute_Args // struct. func (v *AdminService_AddSearchAttribute_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_AddSearchAttribute_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_AddSearchAttribute_Args match the // provided AdminService_AddSearchAttribute_Args. // // This function performs a deep comparison. func (v *AdminService_AddSearchAttribute_Args) Equals(rhs *AdminService_AddSearchAttribute_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_AddSearchAttribute_Args. func (v *AdminService_AddSearchAttribute_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_AddSearchAttribute_Args) GetRequest() (o *AddSearchAttributeRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_AddSearchAttribute_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "AddSearchAttribute" for this struct. func (v *AdminService_AddSearchAttribute_Args) MethodName() string { return "AddSearchAttribute" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_AddSearchAttribute_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_AddSearchAttribute_Helper provides functions that aid in handling the // parameters and return values of the AdminService.AddSearchAttribute // function. var AdminService_AddSearchAttribute_Helper = struct { // Args accepts the parameters of AddSearchAttribute in-order and returns // the arguments struct for the function. Args func( request *AddSearchAttributeRequest, ) *AdminService_AddSearchAttribute_Args // IsException returns true if the given error can be thrown // by AddSearchAttribute. // // An error can be thrown by AddSearchAttribute only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for AddSearchAttribute // given the error returned by it. The provided error may // be nil if AddSearchAttribute did not fail. // // This allows mapping errors returned by AddSearchAttribute into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // AddSearchAttribute // // err := AddSearchAttribute(args) // result, err := AdminService_AddSearchAttribute_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from AddSearchAttribute: %v", err) // } // serialize(result) WrapResponse func(error) (*AdminService_AddSearchAttribute_Result, error) // UnwrapResponse takes the result struct for AddSearchAttribute // and returns the erorr returned by it (if any). // // The error is non-nil only if AddSearchAttribute threw an // exception. // // result := deserialize(bytes) // err := AdminService_AddSearchAttribute_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_AddSearchAttribute_Result) error }{} func init() { AdminService_AddSearchAttribute_Helper.Args = func( request *AddSearchAttributeRequest, ) *AdminService_AddSearchAttribute_Args { return &AdminService_AddSearchAttribute_Args{ Request: request, } } AdminService_AddSearchAttribute_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true default: return false } } AdminService_AddSearchAttribute_Helper.WrapResponse = func(err error) (*AdminService_AddSearchAttribute_Result, error) { if err == nil { return &AdminService_AddSearchAttribute_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_AddSearchAttribute_Result.BadRequestError") } return &AdminService_AddSearchAttribute_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_AddSearchAttribute_Result.InternalServiceError") } return &AdminService_AddSearchAttribute_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_AddSearchAttribute_Result.ServiceBusyError") } return &AdminService_AddSearchAttribute_Result{ServiceBusyError: e}, nil } return nil, err } AdminService_AddSearchAttribute_Helper.UnwrapResponse = func(result *AdminService_AddSearchAttribute_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } return } } // AdminService_AddSearchAttribute_Result represents the result of a AdminService.AddSearchAttribute function call. // // The result of a AddSearchAttribute execution is sent and received over the wire as this struct. type AdminService_AddSearchAttribute_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a AdminService_AddSearchAttribute_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_AddSearchAttribute_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("AdminService_AddSearchAttribute_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _BadRequestError_Read(w wire.Value) (*shared.BadRequestError, error) { var v shared.BadRequestError err := v.FromWire(w) return &v, err } func _InternalServiceError_Read(w wire.Value) (*shared.InternalServiceError, error) { var v shared.InternalServiceError err := v.FromWire(w) return &v, err } func _ServiceBusyError_Read(w wire.Value) (*shared.ServiceBusyError, error) { var v shared.ServiceBusyError err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_AddSearchAttribute_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_AddSearchAttribute_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_AddSearchAttribute_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_AddSearchAttribute_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_AddSearchAttribute_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a AdminService_AddSearchAttribute_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_AddSearchAttribute_Result struct could not be encoded. func (v *AdminService_AddSearchAttribute_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_AddSearchAttribute_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _BadRequestError_Decode(sr stream.Reader) (*shared.BadRequestError, error) { var v shared.BadRequestError err := v.Decode(sr) return &v, err } func _InternalServiceError_Decode(sr stream.Reader) (*shared.InternalServiceError, error) { var v shared.InternalServiceError err := v.Decode(sr) return &v, err } func _ServiceBusyError_Decode(sr stream.Reader) (*shared.ServiceBusyError, error) { var v shared.ServiceBusyError err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_AddSearchAttribute_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_AddSearchAttribute_Result struct could not be generated from the wire // representation. func (v *AdminService_AddSearchAttribute_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_AddSearchAttribute_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_AddSearchAttribute_Result // struct. func (v *AdminService_AddSearchAttribute_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("AdminService_AddSearchAttribute_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_AddSearchAttribute_Result match the // provided AdminService_AddSearchAttribute_Result. // // This function performs a deep comparison. func (v *AdminService_AddSearchAttribute_Result) Equals(rhs *AdminService_AddSearchAttribute_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_AddSearchAttribute_Result. func (v *AdminService_AddSearchAttribute_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_AddSearchAttribute_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_AddSearchAttribute_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_AddSearchAttribute_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_AddSearchAttribute_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_AddSearchAttribute_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_AddSearchAttribute_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "AddSearchAttribute" for this struct. func (v *AdminService_AddSearchAttribute_Result) MethodName() string { return "AddSearchAttribute" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_AddSearchAttribute_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_CloseShard_Args represents the arguments for the AdminService.CloseShard function. // // The arguments for CloseShard are sent and received over the wire as this struct. type AdminService_CloseShard_Args struct { Request *shared.CloseShardRequest `json:"request,omitempty"` } // ToWire translates a AdminService_CloseShard_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_CloseShard_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CloseShardRequest_Read(w wire.Value) (*shared.CloseShardRequest, error) { var v shared.CloseShardRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_CloseShard_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_CloseShard_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_CloseShard_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_CloseShard_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _CloseShardRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_CloseShard_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_CloseShard_Args struct could not be encoded. func (v *AdminService_CloseShard_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CloseShardRequest_Decode(sr stream.Reader) (*shared.CloseShardRequest, error) { var v shared.CloseShardRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_CloseShard_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_CloseShard_Args struct could not be generated from the wire // representation. func (v *AdminService_CloseShard_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _CloseShardRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_CloseShard_Args // struct. func (v *AdminService_CloseShard_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_CloseShard_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_CloseShard_Args match the // provided AdminService_CloseShard_Args. // // This function performs a deep comparison. func (v *AdminService_CloseShard_Args) Equals(rhs *AdminService_CloseShard_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_CloseShard_Args. func (v *AdminService_CloseShard_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_CloseShard_Args) GetRequest() (o *shared.CloseShardRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_CloseShard_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "CloseShard" for this struct. func (v *AdminService_CloseShard_Args) MethodName() string { return "CloseShard" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_CloseShard_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_CloseShard_Helper provides functions that aid in handling the // parameters and return values of the AdminService.CloseShard // function. var AdminService_CloseShard_Helper = struct { // Args accepts the parameters of CloseShard in-order and returns // the arguments struct for the function. Args func( request *shared.CloseShardRequest, ) *AdminService_CloseShard_Args // IsException returns true if the given error can be thrown // by CloseShard. // // An error can be thrown by CloseShard only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for CloseShard // given the error returned by it. The provided error may // be nil if CloseShard did not fail. // // This allows mapping errors returned by CloseShard into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // CloseShard // // err := CloseShard(args) // result, err := AdminService_CloseShard_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from CloseShard: %v", err) // } // serialize(result) WrapResponse func(error) (*AdminService_CloseShard_Result, error) // UnwrapResponse takes the result struct for CloseShard // and returns the erorr returned by it (if any). // // The error is non-nil only if CloseShard threw an // exception. // // result := deserialize(bytes) // err := AdminService_CloseShard_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_CloseShard_Result) error }{} func init() { AdminService_CloseShard_Helper.Args = func( request *shared.CloseShardRequest, ) *AdminService_CloseShard_Args { return &AdminService_CloseShard_Args{ Request: request, } } AdminService_CloseShard_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.AccessDeniedError: return true default: return false } } AdminService_CloseShard_Helper.WrapResponse = func(err error) (*AdminService_CloseShard_Result, error) { if err == nil { return &AdminService_CloseShard_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_CloseShard_Result.BadRequestError") } return &AdminService_CloseShard_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_CloseShard_Result.InternalServiceError") } return &AdminService_CloseShard_Result{InternalServiceError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_CloseShard_Result.AccessDeniedError") } return &AdminService_CloseShard_Result{AccessDeniedError: e}, nil } return nil, err } AdminService_CloseShard_Helper.UnwrapResponse = func(result *AdminService_CloseShard_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // AdminService_CloseShard_Result represents the result of a AdminService.CloseShard function call. // // The result of a CloseShard execution is sent and received over the wire as this struct. type AdminService_CloseShard_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a AdminService_CloseShard_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_CloseShard_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("AdminService_CloseShard_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AccessDeniedError_Read(w wire.Value) (*shared.AccessDeniedError, error) { var v shared.AccessDeniedError err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_CloseShard_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_CloseShard_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_CloseShard_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_CloseShard_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_CloseShard_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a AdminService_CloseShard_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_CloseShard_Result struct could not be encoded. func (v *AdminService_CloseShard_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_CloseShard_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _AccessDeniedError_Decode(sr stream.Reader) (*shared.AccessDeniedError, error) { var v shared.AccessDeniedError err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_CloseShard_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_CloseShard_Result struct could not be generated from the wire // representation. func (v *AdminService_CloseShard_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_CloseShard_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_CloseShard_Result // struct. func (v *AdminService_CloseShard_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("AdminService_CloseShard_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_CloseShard_Result match the // provided AdminService_CloseShard_Result. // // This function performs a deep comparison. func (v *AdminService_CloseShard_Result) Equals(rhs *AdminService_CloseShard_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_CloseShard_Result. func (v *AdminService_CloseShard_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_CloseShard_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_CloseShard_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_CloseShard_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_CloseShard_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *AdminService_CloseShard_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *AdminService_CloseShard_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "CloseShard" for this struct. func (v *AdminService_CloseShard_Result) MethodName() string { return "CloseShard" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_CloseShard_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_DeleteWorkflow_Args represents the arguments for the AdminService.DeleteWorkflow function. // // The arguments for DeleteWorkflow are sent and received over the wire as this struct. type AdminService_DeleteWorkflow_Args struct { Request *AdminDeleteWorkflowRequest `json:"request,omitempty"` } // ToWire translates a AdminService_DeleteWorkflow_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DeleteWorkflow_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AdminDeleteWorkflowRequest_Read(w wire.Value) (*AdminDeleteWorkflowRequest, error) { var v AdminDeleteWorkflowRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DeleteWorkflow_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DeleteWorkflow_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DeleteWorkflow_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DeleteWorkflow_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _AdminDeleteWorkflowRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_DeleteWorkflow_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DeleteWorkflow_Args struct could not be encoded. func (v *AdminService_DeleteWorkflow_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _AdminDeleteWorkflowRequest_Decode(sr stream.Reader) (*AdminDeleteWorkflowRequest, error) { var v AdminDeleteWorkflowRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DeleteWorkflow_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DeleteWorkflow_Args struct could not be generated from the wire // representation. func (v *AdminService_DeleteWorkflow_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _AdminDeleteWorkflowRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_DeleteWorkflow_Args // struct. func (v *AdminService_DeleteWorkflow_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_DeleteWorkflow_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DeleteWorkflow_Args match the // provided AdminService_DeleteWorkflow_Args. // // This function performs a deep comparison. func (v *AdminService_DeleteWorkflow_Args) Equals(rhs *AdminService_DeleteWorkflow_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DeleteWorkflow_Args. func (v *AdminService_DeleteWorkflow_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_DeleteWorkflow_Args) GetRequest() (o *AdminDeleteWorkflowRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_DeleteWorkflow_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DeleteWorkflow" for this struct. func (v *AdminService_DeleteWorkflow_Args) MethodName() string { return "DeleteWorkflow" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_DeleteWorkflow_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_DeleteWorkflow_Helper provides functions that aid in handling the // parameters and return values of the AdminService.DeleteWorkflow // function. var AdminService_DeleteWorkflow_Helper = struct { // Args accepts the parameters of DeleteWorkflow in-order and returns // the arguments struct for the function. Args func( request *AdminDeleteWorkflowRequest, ) *AdminService_DeleteWorkflow_Args // IsException returns true if the given error can be thrown // by DeleteWorkflow. // // An error can be thrown by DeleteWorkflow only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DeleteWorkflow // given its return value and error. // // This allows mapping values and errors returned by // DeleteWorkflow into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DeleteWorkflow // // value, err := DeleteWorkflow(args) // result, err := AdminService_DeleteWorkflow_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DeleteWorkflow: %v", err) // } // serialize(result) WrapResponse func(*AdminDeleteWorkflowResponse, error) (*AdminService_DeleteWorkflow_Result, error) // UnwrapResponse takes the result struct for DeleteWorkflow // and returns the value or error returned by it. // // The error is non-nil only if DeleteWorkflow threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_DeleteWorkflow_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_DeleteWorkflow_Result) (*AdminDeleteWorkflowResponse, error) }{} func init() { AdminService_DeleteWorkflow_Helper.Args = func( request *AdminDeleteWorkflowRequest, ) *AdminService_DeleteWorkflow_Args { return &AdminService_DeleteWorkflow_Args{ Request: request, } } AdminService_DeleteWorkflow_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.InternalServiceError: return true default: return false } } AdminService_DeleteWorkflow_Helper.WrapResponse = func(success *AdminDeleteWorkflowResponse, err error) (*AdminService_DeleteWorkflow_Result, error) { if err == nil { return &AdminService_DeleteWorkflow_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DeleteWorkflow_Result.BadRequestError") } return &AdminService_DeleteWorkflow_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DeleteWorkflow_Result.EntityNotExistError") } return &AdminService_DeleteWorkflow_Result{EntityNotExistError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DeleteWorkflow_Result.InternalServiceError") } return &AdminService_DeleteWorkflow_Result{InternalServiceError: e}, nil } return nil, err } AdminService_DeleteWorkflow_Helper.UnwrapResponse = func(result *AdminService_DeleteWorkflow_Result) (success *AdminDeleteWorkflowResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_DeleteWorkflow_Result represents the result of a AdminService.DeleteWorkflow function call. // // The result of a DeleteWorkflow execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_DeleteWorkflow_Result struct { // Value returned by DeleteWorkflow after a successful execution. Success *AdminDeleteWorkflowResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` } // ToWire translates a AdminService_DeleteWorkflow_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DeleteWorkflow_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_DeleteWorkflow_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AdminDeleteWorkflowResponse_Read(w wire.Value) (*AdminDeleteWorkflowResponse, error) { var v AdminDeleteWorkflowResponse err := v.FromWire(w) return &v, err } func _EntityNotExistsError_Read(w wire.Value) (*shared.EntityNotExistsError, error) { var v shared.EntityNotExistsError err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DeleteWorkflow_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DeleteWorkflow_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DeleteWorkflow_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DeleteWorkflow_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _AdminDeleteWorkflowResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DeleteWorkflow_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_DeleteWorkflow_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DeleteWorkflow_Result struct could not be encoded. func (v *AdminService_DeleteWorkflow_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DeleteWorkflow_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _AdminDeleteWorkflowResponse_Decode(sr stream.Reader) (*AdminDeleteWorkflowResponse, error) { var v AdminDeleteWorkflowResponse err := v.Decode(sr) return &v, err } func _EntityNotExistsError_Decode(sr stream.Reader) (*shared.EntityNotExistsError, error) { var v shared.EntityNotExistsError err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DeleteWorkflow_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DeleteWorkflow_Result struct could not be generated from the wire // representation. func (v *AdminService_DeleteWorkflow_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _AdminDeleteWorkflowResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DeleteWorkflow_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_DeleteWorkflow_Result // struct. func (v *AdminService_DeleteWorkflow_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } return fmt.Sprintf("AdminService_DeleteWorkflow_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DeleteWorkflow_Result match the // provided AdminService_DeleteWorkflow_Result. // // This function performs a deep comparison. func (v *AdminService_DeleteWorkflow_Result) Equals(rhs *AdminService_DeleteWorkflow_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DeleteWorkflow_Result. func (v *AdminService_DeleteWorkflow_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_DeleteWorkflow_Result) GetSuccess() (o *AdminDeleteWorkflowResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_DeleteWorkflow_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_DeleteWorkflow_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_DeleteWorkflow_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *AdminService_DeleteWorkflow_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *AdminService_DeleteWorkflow_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_DeleteWorkflow_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_DeleteWorkflow_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DeleteWorkflow" for this struct. func (v *AdminService_DeleteWorkflow_Result) MethodName() string { return "DeleteWorkflow" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_DeleteWorkflow_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_DescribeCluster_Args represents the arguments for the AdminService.DescribeCluster function. // // The arguments for DescribeCluster are sent and received over the wire as this struct. type AdminService_DescribeCluster_Args struct { } // ToWire translates a AdminService_DescribeCluster_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DescribeCluster_Args) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminService_DescribeCluster_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DescribeCluster_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DescribeCluster_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DescribeCluster_Args) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a AdminService_DescribeCluster_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DescribeCluster_Args struct could not be encoded. func (v *AdminService_DescribeCluster_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a AdminService_DescribeCluster_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DescribeCluster_Args struct could not be generated from the wire // representation. func (v *AdminService_DescribeCluster_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_DescribeCluster_Args // struct. func (v *AdminService_DescribeCluster_Args) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("AdminService_DescribeCluster_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DescribeCluster_Args match the // provided AdminService_DescribeCluster_Args. // // This function performs a deep comparison. func (v *AdminService_DescribeCluster_Args) Equals(rhs *AdminService_DescribeCluster_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DescribeCluster_Args. func (v *AdminService_DescribeCluster_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeCluster" for this struct. func (v *AdminService_DescribeCluster_Args) MethodName() string { return "DescribeCluster" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_DescribeCluster_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_DescribeCluster_Helper provides functions that aid in handling the // parameters and return values of the AdminService.DescribeCluster // function. var AdminService_DescribeCluster_Helper = struct { // Args accepts the parameters of DescribeCluster in-order and returns // the arguments struct for the function. Args func() *AdminService_DescribeCluster_Args // IsException returns true if the given error can be thrown // by DescribeCluster. // // An error can be thrown by DescribeCluster only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeCluster // given its return value and error. // // This allows mapping values and errors returned by // DescribeCluster into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeCluster // // value, err := DescribeCluster(args) // result, err := AdminService_DescribeCluster_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeCluster: %v", err) // } // serialize(result) WrapResponse func(*DescribeClusterResponse, error) (*AdminService_DescribeCluster_Result, error) // UnwrapResponse takes the result struct for DescribeCluster // and returns the value or error returned by it. // // The error is non-nil only if DescribeCluster threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_DescribeCluster_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_DescribeCluster_Result) (*DescribeClusterResponse, error) }{} func init() { AdminService_DescribeCluster_Helper.Args = func() *AdminService_DescribeCluster_Args { return &AdminService_DescribeCluster_Args{} } AdminService_DescribeCluster_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true default: return false } } AdminService_DescribeCluster_Helper.WrapResponse = func(success *DescribeClusterResponse, err error) (*AdminService_DescribeCluster_Result, error) { if err == nil { return &AdminService_DescribeCluster_Result{Success: success}, nil } switch e := err.(type) { case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeCluster_Result.InternalServiceError") } return &AdminService_DescribeCluster_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeCluster_Result.ServiceBusyError") } return &AdminService_DescribeCluster_Result{ServiceBusyError: e}, nil } return nil, err } AdminService_DescribeCluster_Helper.UnwrapResponse = func(result *AdminService_DescribeCluster_Result) (success *DescribeClusterResponse, err error) { if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_DescribeCluster_Result represents the result of a AdminService.DescribeCluster function call. // // The result of a DescribeCluster execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_DescribeCluster_Result struct { // Value returned by DescribeCluster after a successful execution. Success *DescribeClusterResponse `json:"success,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a AdminService_DescribeCluster_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DescribeCluster_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_DescribeCluster_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeClusterResponse_Read(w wire.Value) (*DescribeClusterResponse, error) { var v DescribeClusterResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DescribeCluster_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DescribeCluster_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DescribeCluster_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DescribeCluster_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeClusterResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeCluster_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_DescribeCluster_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DescribeCluster_Result struct could not be encoded. func (v *AdminService_DescribeCluster_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeCluster_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeClusterResponse_Decode(sr stream.Reader) (*DescribeClusterResponse, error) { var v DescribeClusterResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DescribeCluster_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DescribeCluster_Result struct could not be generated from the wire // representation. func (v *AdminService_DescribeCluster_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeClusterResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeCluster_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_DescribeCluster_Result // struct. func (v *AdminService_DescribeCluster_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("AdminService_DescribeCluster_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DescribeCluster_Result match the // provided AdminService_DescribeCluster_Result. // // This function performs a deep comparison. func (v *AdminService_DescribeCluster_Result) Equals(rhs *AdminService_DescribeCluster_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DescribeCluster_Result. func (v *AdminService_DescribeCluster_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_DescribeCluster_Result) GetSuccess() (o *DescribeClusterResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_DescribeCluster_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeCluster_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_DescribeCluster_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeCluster_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_DescribeCluster_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeCluster" for this struct. func (v *AdminService_DescribeCluster_Result) MethodName() string { return "DescribeCluster" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_DescribeCluster_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_DescribeHistoryHost_Args represents the arguments for the AdminService.DescribeHistoryHost function. // // The arguments for DescribeHistoryHost are sent and received over the wire as this struct. type AdminService_DescribeHistoryHost_Args struct { Request *shared.DescribeHistoryHostRequest `json:"request,omitempty"` } // ToWire translates a AdminService_DescribeHistoryHost_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DescribeHistoryHost_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeHistoryHostRequest_Read(w wire.Value) (*shared.DescribeHistoryHostRequest, error) { var v shared.DescribeHistoryHostRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DescribeHistoryHost_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DescribeHistoryHost_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DescribeHistoryHost_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DescribeHistoryHost_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _DescribeHistoryHostRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_DescribeHistoryHost_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DescribeHistoryHost_Args struct could not be encoded. func (v *AdminService_DescribeHistoryHost_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeHistoryHostRequest_Decode(sr stream.Reader) (*shared.DescribeHistoryHostRequest, error) { var v shared.DescribeHistoryHostRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DescribeHistoryHost_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DescribeHistoryHost_Args struct could not be generated from the wire // representation. func (v *AdminService_DescribeHistoryHost_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _DescribeHistoryHostRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_DescribeHistoryHost_Args // struct. func (v *AdminService_DescribeHistoryHost_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_DescribeHistoryHost_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DescribeHistoryHost_Args match the // provided AdminService_DescribeHistoryHost_Args. // // This function performs a deep comparison. func (v *AdminService_DescribeHistoryHost_Args) Equals(rhs *AdminService_DescribeHistoryHost_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DescribeHistoryHost_Args. func (v *AdminService_DescribeHistoryHost_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_DescribeHistoryHost_Args) GetRequest() (o *shared.DescribeHistoryHostRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_DescribeHistoryHost_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeHistoryHost" for this struct. func (v *AdminService_DescribeHistoryHost_Args) MethodName() string { return "DescribeHistoryHost" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_DescribeHistoryHost_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_DescribeHistoryHost_Helper provides functions that aid in handling the // parameters and return values of the AdminService.DescribeHistoryHost // function. var AdminService_DescribeHistoryHost_Helper = struct { // Args accepts the parameters of DescribeHistoryHost in-order and returns // the arguments struct for the function. Args func( request *shared.DescribeHistoryHostRequest, ) *AdminService_DescribeHistoryHost_Args // IsException returns true if the given error can be thrown // by DescribeHistoryHost. // // An error can be thrown by DescribeHistoryHost only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeHistoryHost // given its return value and error. // // This allows mapping values and errors returned by // DescribeHistoryHost into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeHistoryHost // // value, err := DescribeHistoryHost(args) // result, err := AdminService_DescribeHistoryHost_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeHistoryHost: %v", err) // } // serialize(result) WrapResponse func(*shared.DescribeHistoryHostResponse, error) (*AdminService_DescribeHistoryHost_Result, error) // UnwrapResponse takes the result struct for DescribeHistoryHost // and returns the value or error returned by it. // // The error is non-nil only if DescribeHistoryHost threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_DescribeHistoryHost_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_DescribeHistoryHost_Result) (*shared.DescribeHistoryHostResponse, error) }{} func init() { AdminService_DescribeHistoryHost_Helper.Args = func( request *shared.DescribeHistoryHostRequest, ) *AdminService_DescribeHistoryHost_Args { return &AdminService_DescribeHistoryHost_Args{ Request: request, } } AdminService_DescribeHistoryHost_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.AccessDeniedError: return true default: return false } } AdminService_DescribeHistoryHost_Helper.WrapResponse = func(success *shared.DescribeHistoryHostResponse, err error) (*AdminService_DescribeHistoryHost_Result, error) { if err == nil { return &AdminService_DescribeHistoryHost_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeHistoryHost_Result.BadRequestError") } return &AdminService_DescribeHistoryHost_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeHistoryHost_Result.InternalServiceError") } return &AdminService_DescribeHistoryHost_Result{InternalServiceError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeHistoryHost_Result.AccessDeniedError") } return &AdminService_DescribeHistoryHost_Result{AccessDeniedError: e}, nil } return nil, err } AdminService_DescribeHistoryHost_Helper.UnwrapResponse = func(result *AdminService_DescribeHistoryHost_Result) (success *shared.DescribeHistoryHostResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_DescribeHistoryHost_Result represents the result of a AdminService.DescribeHistoryHost function call. // // The result of a DescribeHistoryHost execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_DescribeHistoryHost_Result struct { // Value returned by DescribeHistoryHost after a successful execution. Success *shared.DescribeHistoryHostResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a AdminService_DescribeHistoryHost_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DescribeHistoryHost_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_DescribeHistoryHost_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeHistoryHostResponse_Read(w wire.Value) (*shared.DescribeHistoryHostResponse, error) { var v shared.DescribeHistoryHostResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DescribeHistoryHost_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DescribeHistoryHost_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DescribeHistoryHost_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DescribeHistoryHost_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeHistoryHostResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeHistoryHost_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_DescribeHistoryHost_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DescribeHistoryHost_Result struct could not be encoded. func (v *AdminService_DescribeHistoryHost_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeHistoryHost_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeHistoryHostResponse_Decode(sr stream.Reader) (*shared.DescribeHistoryHostResponse, error) { var v shared.DescribeHistoryHostResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DescribeHistoryHost_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DescribeHistoryHost_Result struct could not be generated from the wire // representation. func (v *AdminService_DescribeHistoryHost_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeHistoryHostResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeHistoryHost_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_DescribeHistoryHost_Result // struct. func (v *AdminService_DescribeHistoryHost_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("AdminService_DescribeHistoryHost_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DescribeHistoryHost_Result match the // provided AdminService_DescribeHistoryHost_Result. // // This function performs a deep comparison. func (v *AdminService_DescribeHistoryHost_Result) Equals(rhs *AdminService_DescribeHistoryHost_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DescribeHistoryHost_Result. func (v *AdminService_DescribeHistoryHost_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_DescribeHistoryHost_Result) GetSuccess() (o *shared.DescribeHistoryHostResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_DescribeHistoryHost_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeHistoryHost_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_DescribeHistoryHost_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeHistoryHost_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_DescribeHistoryHost_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeHistoryHost_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *AdminService_DescribeHistoryHost_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeHistoryHost" for this struct. func (v *AdminService_DescribeHistoryHost_Result) MethodName() string { return "DescribeHistoryHost" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_DescribeHistoryHost_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_DescribeQueue_Args represents the arguments for the AdminService.DescribeQueue function. // // The arguments for DescribeQueue are sent and received over the wire as this struct. type AdminService_DescribeQueue_Args struct { Request *shared.DescribeQueueRequest `json:"request,omitempty"` } // ToWire translates a AdminService_DescribeQueue_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DescribeQueue_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeQueueRequest_Read(w wire.Value) (*shared.DescribeQueueRequest, error) { var v shared.DescribeQueueRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DescribeQueue_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DescribeQueue_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DescribeQueue_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DescribeQueue_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _DescribeQueueRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_DescribeQueue_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DescribeQueue_Args struct could not be encoded. func (v *AdminService_DescribeQueue_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeQueueRequest_Decode(sr stream.Reader) (*shared.DescribeQueueRequest, error) { var v shared.DescribeQueueRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DescribeQueue_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DescribeQueue_Args struct could not be generated from the wire // representation. func (v *AdminService_DescribeQueue_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _DescribeQueueRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_DescribeQueue_Args // struct. func (v *AdminService_DescribeQueue_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_DescribeQueue_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DescribeQueue_Args match the // provided AdminService_DescribeQueue_Args. // // This function performs a deep comparison. func (v *AdminService_DescribeQueue_Args) Equals(rhs *AdminService_DescribeQueue_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DescribeQueue_Args. func (v *AdminService_DescribeQueue_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_DescribeQueue_Args) GetRequest() (o *shared.DescribeQueueRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_DescribeQueue_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeQueue" for this struct. func (v *AdminService_DescribeQueue_Args) MethodName() string { return "DescribeQueue" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_DescribeQueue_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_DescribeQueue_Helper provides functions that aid in handling the // parameters and return values of the AdminService.DescribeQueue // function. var AdminService_DescribeQueue_Helper = struct { // Args accepts the parameters of DescribeQueue in-order and returns // the arguments struct for the function. Args func( request *shared.DescribeQueueRequest, ) *AdminService_DescribeQueue_Args // IsException returns true if the given error can be thrown // by DescribeQueue. // // An error can be thrown by DescribeQueue only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeQueue // given its return value and error. // // This allows mapping values and errors returned by // DescribeQueue into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeQueue // // value, err := DescribeQueue(args) // result, err := AdminService_DescribeQueue_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeQueue: %v", err) // } // serialize(result) WrapResponse func(*shared.DescribeQueueResponse, error) (*AdminService_DescribeQueue_Result, error) // UnwrapResponse takes the result struct for DescribeQueue // and returns the value or error returned by it. // // The error is non-nil only if DescribeQueue threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_DescribeQueue_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_DescribeQueue_Result) (*shared.DescribeQueueResponse, error) }{} func init() { AdminService_DescribeQueue_Helper.Args = func( request *shared.DescribeQueueRequest, ) *AdminService_DescribeQueue_Args { return &AdminService_DescribeQueue_Args{ Request: request, } } AdminService_DescribeQueue_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.AccessDeniedError: return true default: return false } } AdminService_DescribeQueue_Helper.WrapResponse = func(success *shared.DescribeQueueResponse, err error) (*AdminService_DescribeQueue_Result, error) { if err == nil { return &AdminService_DescribeQueue_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeQueue_Result.BadRequestError") } return &AdminService_DescribeQueue_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeQueue_Result.InternalServiceError") } return &AdminService_DescribeQueue_Result{InternalServiceError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeQueue_Result.AccessDeniedError") } return &AdminService_DescribeQueue_Result{AccessDeniedError: e}, nil } return nil, err } AdminService_DescribeQueue_Helper.UnwrapResponse = func(result *AdminService_DescribeQueue_Result) (success *shared.DescribeQueueResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_DescribeQueue_Result represents the result of a AdminService.DescribeQueue function call. // // The result of a DescribeQueue execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_DescribeQueue_Result struct { // Value returned by DescribeQueue after a successful execution. Success *shared.DescribeQueueResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a AdminService_DescribeQueue_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DescribeQueue_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_DescribeQueue_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeQueueResponse_Read(w wire.Value) (*shared.DescribeQueueResponse, error) { var v shared.DescribeQueueResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DescribeQueue_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DescribeQueue_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DescribeQueue_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DescribeQueue_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeQueueResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeQueue_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_DescribeQueue_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DescribeQueue_Result struct could not be encoded. func (v *AdminService_DescribeQueue_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeQueue_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeQueueResponse_Decode(sr stream.Reader) (*shared.DescribeQueueResponse, error) { var v shared.DescribeQueueResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DescribeQueue_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DescribeQueue_Result struct could not be generated from the wire // representation. func (v *AdminService_DescribeQueue_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeQueueResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeQueue_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_DescribeQueue_Result // struct. func (v *AdminService_DescribeQueue_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("AdminService_DescribeQueue_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DescribeQueue_Result match the // provided AdminService_DescribeQueue_Result. // // This function performs a deep comparison. func (v *AdminService_DescribeQueue_Result) Equals(rhs *AdminService_DescribeQueue_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DescribeQueue_Result. func (v *AdminService_DescribeQueue_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_DescribeQueue_Result) GetSuccess() (o *shared.DescribeQueueResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_DescribeQueue_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeQueue_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_DescribeQueue_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeQueue_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_DescribeQueue_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeQueue_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *AdminService_DescribeQueue_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeQueue" for this struct. func (v *AdminService_DescribeQueue_Result) MethodName() string { return "DescribeQueue" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_DescribeQueue_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_DescribeShardDistribution_Args represents the arguments for the AdminService.DescribeShardDistribution function. // // The arguments for DescribeShardDistribution are sent and received over the wire as this struct. type AdminService_DescribeShardDistribution_Args struct { Request *shared.DescribeShardDistributionRequest `json:"request,omitempty"` } // ToWire translates a AdminService_DescribeShardDistribution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DescribeShardDistribution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeShardDistributionRequest_Read(w wire.Value) (*shared.DescribeShardDistributionRequest, error) { var v shared.DescribeShardDistributionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DescribeShardDistribution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DescribeShardDistribution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DescribeShardDistribution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DescribeShardDistribution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _DescribeShardDistributionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_DescribeShardDistribution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DescribeShardDistribution_Args struct could not be encoded. func (v *AdminService_DescribeShardDistribution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeShardDistributionRequest_Decode(sr stream.Reader) (*shared.DescribeShardDistributionRequest, error) { var v shared.DescribeShardDistributionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DescribeShardDistribution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DescribeShardDistribution_Args struct could not be generated from the wire // representation. func (v *AdminService_DescribeShardDistribution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _DescribeShardDistributionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_DescribeShardDistribution_Args // struct. func (v *AdminService_DescribeShardDistribution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_DescribeShardDistribution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DescribeShardDistribution_Args match the // provided AdminService_DescribeShardDistribution_Args. // // This function performs a deep comparison. func (v *AdminService_DescribeShardDistribution_Args) Equals(rhs *AdminService_DescribeShardDistribution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DescribeShardDistribution_Args. func (v *AdminService_DescribeShardDistribution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_DescribeShardDistribution_Args) GetRequest() (o *shared.DescribeShardDistributionRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_DescribeShardDistribution_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeShardDistribution" for this struct. func (v *AdminService_DescribeShardDistribution_Args) MethodName() string { return "DescribeShardDistribution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_DescribeShardDistribution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_DescribeShardDistribution_Helper provides functions that aid in handling the // parameters and return values of the AdminService.DescribeShardDistribution // function. var AdminService_DescribeShardDistribution_Helper = struct { // Args accepts the parameters of DescribeShardDistribution in-order and returns // the arguments struct for the function. Args func( request *shared.DescribeShardDistributionRequest, ) *AdminService_DescribeShardDistribution_Args // IsException returns true if the given error can be thrown // by DescribeShardDistribution. // // An error can be thrown by DescribeShardDistribution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeShardDistribution // given its return value and error. // // This allows mapping values and errors returned by // DescribeShardDistribution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeShardDistribution // // value, err := DescribeShardDistribution(args) // result, err := AdminService_DescribeShardDistribution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeShardDistribution: %v", err) // } // serialize(result) WrapResponse func(*shared.DescribeShardDistributionResponse, error) (*AdminService_DescribeShardDistribution_Result, error) // UnwrapResponse takes the result struct for DescribeShardDistribution // and returns the value or error returned by it. // // The error is non-nil only if DescribeShardDistribution threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_DescribeShardDistribution_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_DescribeShardDistribution_Result) (*shared.DescribeShardDistributionResponse, error) }{} func init() { AdminService_DescribeShardDistribution_Helper.Args = func( request *shared.DescribeShardDistributionRequest, ) *AdminService_DescribeShardDistribution_Args { return &AdminService_DescribeShardDistribution_Args{ Request: request, } } AdminService_DescribeShardDistribution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.InternalServiceError: return true default: return false } } AdminService_DescribeShardDistribution_Helper.WrapResponse = func(success *shared.DescribeShardDistributionResponse, err error) (*AdminService_DescribeShardDistribution_Result, error) { if err == nil { return &AdminService_DescribeShardDistribution_Result{Success: success}, nil } switch e := err.(type) { case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeShardDistribution_Result.InternalServiceError") } return &AdminService_DescribeShardDistribution_Result{InternalServiceError: e}, nil } return nil, err } AdminService_DescribeShardDistribution_Helper.UnwrapResponse = func(result *AdminService_DescribeShardDistribution_Result) (success *shared.DescribeShardDistributionResponse, err error) { if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_DescribeShardDistribution_Result represents the result of a AdminService.DescribeShardDistribution function call. // // The result of a DescribeShardDistribution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_DescribeShardDistribution_Result struct { // Value returned by DescribeShardDistribution after a successful execution. Success *shared.DescribeShardDistributionResponse `json:"success,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` } // ToWire translates a AdminService_DescribeShardDistribution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DescribeShardDistribution_Result) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_DescribeShardDistribution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeShardDistributionResponse_Read(w wire.Value) (*shared.DescribeShardDistributionResponse, error) { var v shared.DescribeShardDistributionResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DescribeShardDistribution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DescribeShardDistribution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DescribeShardDistribution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DescribeShardDistribution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeShardDistributionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeShardDistribution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_DescribeShardDistribution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DescribeShardDistribution_Result struct could not be encoded. func (v *AdminService_DescribeShardDistribution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeShardDistribution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeShardDistributionResponse_Decode(sr stream.Reader) (*shared.DescribeShardDistributionResponse, error) { var v shared.DescribeShardDistributionResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DescribeShardDistribution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DescribeShardDistribution_Result struct could not be generated from the wire // representation. func (v *AdminService_DescribeShardDistribution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeShardDistributionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeShardDistribution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_DescribeShardDistribution_Result // struct. func (v *AdminService_DescribeShardDistribution_Result) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } return fmt.Sprintf("AdminService_DescribeShardDistribution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DescribeShardDistribution_Result match the // provided AdminService_DescribeShardDistribution_Result. // // This function performs a deep comparison. func (v *AdminService_DescribeShardDistribution_Result) Equals(rhs *AdminService_DescribeShardDistribution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DescribeShardDistribution_Result. func (v *AdminService_DescribeShardDistribution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_DescribeShardDistribution_Result) GetSuccess() (o *shared.DescribeShardDistributionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_DescribeShardDistribution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeShardDistribution_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_DescribeShardDistribution_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeShardDistribution" for this struct. func (v *AdminService_DescribeShardDistribution_Result) MethodName() string { return "DescribeShardDistribution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_DescribeShardDistribution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_DescribeWorkflowExecution_Args represents the arguments for the AdminService.DescribeWorkflowExecution function. // // The arguments for DescribeWorkflowExecution are sent and received over the wire as this struct. type AdminService_DescribeWorkflowExecution_Args struct { Request *DescribeWorkflowExecutionRequest `json:"request,omitempty"` } // ToWire translates a AdminService_DescribeWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DescribeWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeWorkflowExecutionRequest_Read(w wire.Value) (*DescribeWorkflowExecutionRequest, error) { var v DescribeWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DescribeWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DescribeWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DescribeWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DescribeWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _DescribeWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_DescribeWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DescribeWorkflowExecution_Args struct could not be encoded. func (v *AdminService_DescribeWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeWorkflowExecutionRequest_Decode(sr stream.Reader) (*DescribeWorkflowExecutionRequest, error) { var v DescribeWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DescribeWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DescribeWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *AdminService_DescribeWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _DescribeWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_DescribeWorkflowExecution_Args // struct. func (v *AdminService_DescribeWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_DescribeWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DescribeWorkflowExecution_Args match the // provided AdminService_DescribeWorkflowExecution_Args. // // This function performs a deep comparison. func (v *AdminService_DescribeWorkflowExecution_Args) Equals(rhs *AdminService_DescribeWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DescribeWorkflowExecution_Args. func (v *AdminService_DescribeWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_DescribeWorkflowExecution_Args) GetRequest() (o *DescribeWorkflowExecutionRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_DescribeWorkflowExecution_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeWorkflowExecution" for this struct. func (v *AdminService_DescribeWorkflowExecution_Args) MethodName() string { return "DescribeWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_DescribeWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_DescribeWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the AdminService.DescribeWorkflowExecution // function. var AdminService_DescribeWorkflowExecution_Helper = struct { // Args accepts the parameters of DescribeWorkflowExecution in-order and returns // the arguments struct for the function. Args func( request *DescribeWorkflowExecutionRequest, ) *AdminService_DescribeWorkflowExecution_Args // IsException returns true if the given error can be thrown // by DescribeWorkflowExecution. // // An error can be thrown by DescribeWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // DescribeWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeWorkflowExecution // // value, err := DescribeWorkflowExecution(args) // result, err := AdminService_DescribeWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*DescribeWorkflowExecutionResponse, error) (*AdminService_DescribeWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for DescribeWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if DescribeWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_DescribeWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_DescribeWorkflowExecution_Result) (*DescribeWorkflowExecutionResponse, error) }{} func init() { AdminService_DescribeWorkflowExecution_Helper.Args = func( request *DescribeWorkflowExecutionRequest, ) *AdminService_DescribeWorkflowExecution_Args { return &AdminService_DescribeWorkflowExecution_Args{ Request: request, } } AdminService_DescribeWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *shared.AccessDeniedError: return true default: return false } } AdminService_DescribeWorkflowExecution_Helper.WrapResponse = func(success *DescribeWorkflowExecutionResponse, err error) (*AdminService_DescribeWorkflowExecution_Result, error) { if err == nil { return &AdminService_DescribeWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeWorkflowExecution_Result.BadRequestError") } return &AdminService_DescribeWorkflowExecution_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeWorkflowExecution_Result.InternalServiceError") } return &AdminService_DescribeWorkflowExecution_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeWorkflowExecution_Result.EntityNotExistError") } return &AdminService_DescribeWorkflowExecution_Result{EntityNotExistError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_DescribeWorkflowExecution_Result.AccessDeniedError") } return &AdminService_DescribeWorkflowExecution_Result{AccessDeniedError: e}, nil } return nil, err } AdminService_DescribeWorkflowExecution_Helper.UnwrapResponse = func(result *AdminService_DescribeWorkflowExecution_Result) (success *DescribeWorkflowExecutionResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_DescribeWorkflowExecution_Result represents the result of a AdminService.DescribeWorkflowExecution function call. // // The result of a DescribeWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_DescribeWorkflowExecution_Result struct { // Value returned by DescribeWorkflowExecution after a successful execution. Success *DescribeWorkflowExecutionResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a AdminService_DescribeWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_DescribeWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeWorkflowExecutionResponse_Read(w wire.Value) (*DescribeWorkflowExecutionResponse, error) { var v DescribeWorkflowExecutionResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_DescribeWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_DescribeWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_DescribeWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_DescribeWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_DescribeWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_DescribeWorkflowExecution_Result struct could not be encoded. func (v *AdminService_DescribeWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeWorkflowExecutionResponse_Decode(sr stream.Reader) (*DescribeWorkflowExecutionResponse, error) { var v DescribeWorkflowExecutionResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_DescribeWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_DescribeWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *AdminService_DescribeWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_DescribeWorkflowExecution_Result // struct. func (v *AdminService_DescribeWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("AdminService_DescribeWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_DescribeWorkflowExecution_Result match the // provided AdminService_DescribeWorkflowExecution_Result. // // This function performs a deep comparison. func (v *AdminService_DescribeWorkflowExecution_Result) Equals(rhs *AdminService_DescribeWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_DescribeWorkflowExecution_Result. func (v *AdminService_DescribeWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_DescribeWorkflowExecution_Result) GetSuccess() (o *DescribeWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_DescribeWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_DescribeWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeWorkflowExecution_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_DescribeWorkflowExecution_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *AdminService_DescribeWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *AdminService_DescribeWorkflowExecution_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *AdminService_DescribeWorkflowExecution_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeWorkflowExecution" for this struct. func (v *AdminService_DescribeWorkflowExecution_Result) MethodName() string { return "DescribeWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_DescribeWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_GetCrossClusterTasks_Args represents the arguments for the AdminService.GetCrossClusterTasks function. // // The arguments for GetCrossClusterTasks are sent and received over the wire as this struct. type AdminService_GetCrossClusterTasks_Args struct { Request *shared.GetCrossClusterTasksRequest `json:"request,omitempty"` } // ToWire translates a AdminService_GetCrossClusterTasks_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetCrossClusterTasks_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetCrossClusterTasksRequest_Read(w wire.Value) (*shared.GetCrossClusterTasksRequest, error) { var v shared.GetCrossClusterTasksRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetCrossClusterTasks_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetCrossClusterTasks_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetCrossClusterTasks_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetCrossClusterTasks_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetCrossClusterTasksRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_GetCrossClusterTasks_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetCrossClusterTasks_Args struct could not be encoded. func (v *AdminService_GetCrossClusterTasks_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetCrossClusterTasksRequest_Decode(sr stream.Reader) (*shared.GetCrossClusterTasksRequest, error) { var v shared.GetCrossClusterTasksRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetCrossClusterTasks_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetCrossClusterTasks_Args struct could not be generated from the wire // representation. func (v *AdminService_GetCrossClusterTasks_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetCrossClusterTasksRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_GetCrossClusterTasks_Args // struct. func (v *AdminService_GetCrossClusterTasks_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_GetCrossClusterTasks_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetCrossClusterTasks_Args match the // provided AdminService_GetCrossClusterTasks_Args. // // This function performs a deep comparison. func (v *AdminService_GetCrossClusterTasks_Args) Equals(rhs *AdminService_GetCrossClusterTasks_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetCrossClusterTasks_Args. func (v *AdminService_GetCrossClusterTasks_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_GetCrossClusterTasks_Args) GetRequest() (o *shared.GetCrossClusterTasksRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_GetCrossClusterTasks_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetCrossClusterTasks" for this struct. func (v *AdminService_GetCrossClusterTasks_Args) MethodName() string { return "GetCrossClusterTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_GetCrossClusterTasks_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_GetCrossClusterTasks_Helper provides functions that aid in handling the // parameters and return values of the AdminService.GetCrossClusterTasks // function. var AdminService_GetCrossClusterTasks_Helper = struct { // Args accepts the parameters of GetCrossClusterTasks in-order and returns // the arguments struct for the function. Args func( request *shared.GetCrossClusterTasksRequest, ) *AdminService_GetCrossClusterTasks_Args // IsException returns true if the given error can be thrown // by GetCrossClusterTasks. // // An error can be thrown by GetCrossClusterTasks only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetCrossClusterTasks // given its return value and error. // // This allows mapping values and errors returned by // GetCrossClusterTasks into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetCrossClusterTasks // // value, err := GetCrossClusterTasks(args) // result, err := AdminService_GetCrossClusterTasks_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetCrossClusterTasks: %v", err) // } // serialize(result) WrapResponse func(*shared.GetCrossClusterTasksResponse, error) (*AdminService_GetCrossClusterTasks_Result, error) // UnwrapResponse takes the result struct for GetCrossClusterTasks // and returns the value or error returned by it. // // The error is non-nil only if GetCrossClusterTasks threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_GetCrossClusterTasks_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_GetCrossClusterTasks_Result) (*shared.GetCrossClusterTasksResponse, error) }{} func init() { AdminService_GetCrossClusterTasks_Helper.Args = func( request *shared.GetCrossClusterTasksRequest, ) *AdminService_GetCrossClusterTasks_Args { return &AdminService_GetCrossClusterTasks_Args{ Request: request, } } AdminService_GetCrossClusterTasks_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true default: return false } } AdminService_GetCrossClusterTasks_Helper.WrapResponse = func(success *shared.GetCrossClusterTasksResponse, err error) (*AdminService_GetCrossClusterTasks_Result, error) { if err == nil { return &AdminService_GetCrossClusterTasks_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetCrossClusterTasks_Result.BadRequestError") } return &AdminService_GetCrossClusterTasks_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetCrossClusterTasks_Result.InternalServiceError") } return &AdminService_GetCrossClusterTasks_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetCrossClusterTasks_Result.ServiceBusyError") } return &AdminService_GetCrossClusterTasks_Result{ServiceBusyError: e}, nil } return nil, err } AdminService_GetCrossClusterTasks_Helper.UnwrapResponse = func(result *AdminService_GetCrossClusterTasks_Result) (success *shared.GetCrossClusterTasksResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_GetCrossClusterTasks_Result represents the result of a AdminService.GetCrossClusterTasks function call. // // The result of a GetCrossClusterTasks execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_GetCrossClusterTasks_Result struct { // Value returned by GetCrossClusterTasks after a successful execution. Success *shared.GetCrossClusterTasksResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a AdminService_GetCrossClusterTasks_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetCrossClusterTasks_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_GetCrossClusterTasks_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetCrossClusterTasksResponse_Read(w wire.Value) (*shared.GetCrossClusterTasksResponse, error) { var v shared.GetCrossClusterTasksResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetCrossClusterTasks_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetCrossClusterTasks_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetCrossClusterTasks_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetCrossClusterTasks_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetCrossClusterTasksResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetCrossClusterTasks_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_GetCrossClusterTasks_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetCrossClusterTasks_Result struct could not be encoded. func (v *AdminService_GetCrossClusterTasks_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetCrossClusterTasks_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetCrossClusterTasksResponse_Decode(sr stream.Reader) (*shared.GetCrossClusterTasksResponse, error) { var v shared.GetCrossClusterTasksResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetCrossClusterTasks_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetCrossClusterTasks_Result struct could not be generated from the wire // representation. func (v *AdminService_GetCrossClusterTasks_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetCrossClusterTasksResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetCrossClusterTasks_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_GetCrossClusterTasks_Result // struct. func (v *AdminService_GetCrossClusterTasks_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("AdminService_GetCrossClusterTasks_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetCrossClusterTasks_Result match the // provided AdminService_GetCrossClusterTasks_Result. // // This function performs a deep comparison. func (v *AdminService_GetCrossClusterTasks_Result) Equals(rhs *AdminService_GetCrossClusterTasks_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetCrossClusterTasks_Result. func (v *AdminService_GetCrossClusterTasks_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_GetCrossClusterTasks_Result) GetSuccess() (o *shared.GetCrossClusterTasksResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_GetCrossClusterTasks_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_GetCrossClusterTasks_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_GetCrossClusterTasks_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_GetCrossClusterTasks_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_GetCrossClusterTasks_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_GetCrossClusterTasks_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_GetCrossClusterTasks_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetCrossClusterTasks" for this struct. func (v *AdminService_GetCrossClusterTasks_Result) MethodName() string { return "GetCrossClusterTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_GetCrossClusterTasks_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_GetDLQReplicationMessages_Args represents the arguments for the AdminService.GetDLQReplicationMessages function. // // The arguments for GetDLQReplicationMessages are sent and received over the wire as this struct. type AdminService_GetDLQReplicationMessages_Args struct { Request *replicator.GetDLQReplicationMessagesRequest `json:"request,omitempty"` } // ToWire translates a AdminService_GetDLQReplicationMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetDLQReplicationMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDLQReplicationMessagesRequest_Read(w wire.Value) (*replicator.GetDLQReplicationMessagesRequest, error) { var v replicator.GetDLQReplicationMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetDLQReplicationMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetDLQReplicationMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetDLQReplicationMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetDLQReplicationMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetDLQReplicationMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_GetDLQReplicationMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetDLQReplicationMessages_Args struct could not be encoded. func (v *AdminService_GetDLQReplicationMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetDLQReplicationMessagesRequest_Decode(sr stream.Reader) (*replicator.GetDLQReplicationMessagesRequest, error) { var v replicator.GetDLQReplicationMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetDLQReplicationMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetDLQReplicationMessages_Args struct could not be generated from the wire // representation. func (v *AdminService_GetDLQReplicationMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetDLQReplicationMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_GetDLQReplicationMessages_Args // struct. func (v *AdminService_GetDLQReplicationMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_GetDLQReplicationMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetDLQReplicationMessages_Args match the // provided AdminService_GetDLQReplicationMessages_Args. // // This function performs a deep comparison. func (v *AdminService_GetDLQReplicationMessages_Args) Equals(rhs *AdminService_GetDLQReplicationMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetDLQReplicationMessages_Args. func (v *AdminService_GetDLQReplicationMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_GetDLQReplicationMessages_Args) GetRequest() (o *replicator.GetDLQReplicationMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_GetDLQReplicationMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetDLQReplicationMessages" for this struct. func (v *AdminService_GetDLQReplicationMessages_Args) MethodName() string { return "GetDLQReplicationMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_GetDLQReplicationMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_GetDLQReplicationMessages_Helper provides functions that aid in handling the // parameters and return values of the AdminService.GetDLQReplicationMessages // function. var AdminService_GetDLQReplicationMessages_Helper = struct { // Args accepts the parameters of GetDLQReplicationMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.GetDLQReplicationMessagesRequest, ) *AdminService_GetDLQReplicationMessages_Args // IsException returns true if the given error can be thrown // by GetDLQReplicationMessages. // // An error can be thrown by GetDLQReplicationMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetDLQReplicationMessages // given its return value and error. // // This allows mapping values and errors returned by // GetDLQReplicationMessages into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetDLQReplicationMessages // // value, err := GetDLQReplicationMessages(args) // result, err := AdminService_GetDLQReplicationMessages_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetDLQReplicationMessages: %v", err) // } // serialize(result) WrapResponse func(*replicator.GetDLQReplicationMessagesResponse, error) (*AdminService_GetDLQReplicationMessages_Result, error) // UnwrapResponse takes the result struct for GetDLQReplicationMessages // and returns the value or error returned by it. // // The error is non-nil only if GetDLQReplicationMessages threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_GetDLQReplicationMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_GetDLQReplicationMessages_Result) (*replicator.GetDLQReplicationMessagesResponse, error) }{} func init() { AdminService_GetDLQReplicationMessages_Helper.Args = func( request *replicator.GetDLQReplicationMessagesRequest, ) *AdminService_GetDLQReplicationMessages_Args { return &AdminService_GetDLQReplicationMessages_Args{ Request: request, } } AdminService_GetDLQReplicationMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.ServiceBusyError: return true default: return false } } AdminService_GetDLQReplicationMessages_Helper.WrapResponse = func(success *replicator.GetDLQReplicationMessagesResponse, err error) (*AdminService_GetDLQReplicationMessages_Result, error) { if err == nil { return &AdminService_GetDLQReplicationMessages_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetDLQReplicationMessages_Result.BadRequestError") } return &AdminService_GetDLQReplicationMessages_Result{BadRequestError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetDLQReplicationMessages_Result.ServiceBusyError") } return &AdminService_GetDLQReplicationMessages_Result{ServiceBusyError: e}, nil } return nil, err } AdminService_GetDLQReplicationMessages_Helper.UnwrapResponse = func(result *AdminService_GetDLQReplicationMessages_Result) (success *replicator.GetDLQReplicationMessagesResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_GetDLQReplicationMessages_Result represents the result of a AdminService.GetDLQReplicationMessages function call. // // The result of a GetDLQReplicationMessages execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_GetDLQReplicationMessages_Result struct { // Value returned by GetDLQReplicationMessages after a successful execution. Success *replicator.GetDLQReplicationMessagesResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a AdminService_GetDLQReplicationMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetDLQReplicationMessages_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_GetDLQReplicationMessages_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDLQReplicationMessagesResponse_Read(w wire.Value) (*replicator.GetDLQReplicationMessagesResponse, error) { var v replicator.GetDLQReplicationMessagesResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetDLQReplicationMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetDLQReplicationMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetDLQReplicationMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetDLQReplicationMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetDLQReplicationMessagesResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDLQReplicationMessages_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_GetDLQReplicationMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetDLQReplicationMessages_Result struct could not be encoded. func (v *AdminService_GetDLQReplicationMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDLQReplicationMessages_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetDLQReplicationMessagesResponse_Decode(sr stream.Reader) (*replicator.GetDLQReplicationMessagesResponse, error) { var v replicator.GetDLQReplicationMessagesResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetDLQReplicationMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetDLQReplicationMessages_Result struct could not be generated from the wire // representation. func (v *AdminService_GetDLQReplicationMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetDLQReplicationMessagesResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDLQReplicationMessages_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_GetDLQReplicationMessages_Result // struct. func (v *AdminService_GetDLQReplicationMessages_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("AdminService_GetDLQReplicationMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetDLQReplicationMessages_Result match the // provided AdminService_GetDLQReplicationMessages_Result. // // This function performs a deep comparison. func (v *AdminService_GetDLQReplicationMessages_Result) Equals(rhs *AdminService_GetDLQReplicationMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetDLQReplicationMessages_Result. func (v *AdminService_GetDLQReplicationMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_GetDLQReplicationMessages_Result) GetSuccess() (o *replicator.GetDLQReplicationMessagesResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_GetDLQReplicationMessages_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_GetDLQReplicationMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_GetDLQReplicationMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_GetDLQReplicationMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_GetDLQReplicationMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetDLQReplicationMessages" for this struct. func (v *AdminService_GetDLQReplicationMessages_Result) MethodName() string { return "GetDLQReplicationMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_GetDLQReplicationMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_GetDomainAsyncWorkflowConfiguraton_Args represents the arguments for the AdminService.GetDomainAsyncWorkflowConfiguraton function. // // The arguments for GetDomainAsyncWorkflowConfiguraton are sent and received over the wire as this struct. type AdminService_GetDomainAsyncWorkflowConfiguraton_Args struct { Request *GetDomainAsyncWorkflowConfiguratonRequest `json:"request,omitempty"` } // ToWire translates a AdminService_GetDomainAsyncWorkflowConfiguraton_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDomainAsyncWorkflowConfiguratonRequest_Read(w wire.Value) (*GetDomainAsyncWorkflowConfiguratonRequest, error) { var v GetDomainAsyncWorkflowConfiguratonRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetDomainAsyncWorkflowConfiguraton_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetDomainAsyncWorkflowConfiguraton_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetDomainAsyncWorkflowConfiguraton_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetDomainAsyncWorkflowConfiguratonRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_GetDomainAsyncWorkflowConfiguraton_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetDomainAsyncWorkflowConfiguraton_Args struct could not be encoded. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetDomainAsyncWorkflowConfiguratonRequest_Decode(sr stream.Reader) (*GetDomainAsyncWorkflowConfiguratonRequest, error) { var v GetDomainAsyncWorkflowConfiguratonRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetDomainAsyncWorkflowConfiguraton_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetDomainAsyncWorkflowConfiguraton_Args struct could not be generated from the wire // representation. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetDomainAsyncWorkflowConfiguratonRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_GetDomainAsyncWorkflowConfiguraton_Args // struct. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_GetDomainAsyncWorkflowConfiguraton_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetDomainAsyncWorkflowConfiguraton_Args match the // provided AdminService_GetDomainAsyncWorkflowConfiguraton_Args. // // This function performs a deep comparison. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) Equals(rhs *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetDomainAsyncWorkflowConfiguraton_Args. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) GetRequest() (o *GetDomainAsyncWorkflowConfiguratonRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetDomainAsyncWorkflowConfiguraton" for this struct. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) MethodName() string { return "GetDomainAsyncWorkflowConfiguraton" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_GetDomainAsyncWorkflowConfiguraton_Helper provides functions that aid in handling the // parameters and return values of the AdminService.GetDomainAsyncWorkflowConfiguraton // function. var AdminService_GetDomainAsyncWorkflowConfiguraton_Helper = struct { // Args accepts the parameters of GetDomainAsyncWorkflowConfiguraton in-order and returns // the arguments struct for the function. Args func( request *GetDomainAsyncWorkflowConfiguratonRequest, ) *AdminService_GetDomainAsyncWorkflowConfiguraton_Args // IsException returns true if the given error can be thrown // by GetDomainAsyncWorkflowConfiguraton. // // An error can be thrown by GetDomainAsyncWorkflowConfiguraton only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetDomainAsyncWorkflowConfiguraton // given its return value and error. // // This allows mapping values and errors returned by // GetDomainAsyncWorkflowConfiguraton into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetDomainAsyncWorkflowConfiguraton // // value, err := GetDomainAsyncWorkflowConfiguraton(args) // result, err := AdminService_GetDomainAsyncWorkflowConfiguraton_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetDomainAsyncWorkflowConfiguraton: %v", err) // } // serialize(result) WrapResponse func(*GetDomainAsyncWorkflowConfiguratonResponse, error) (*AdminService_GetDomainAsyncWorkflowConfiguraton_Result, error) // UnwrapResponse takes the result struct for GetDomainAsyncWorkflowConfiguraton // and returns the value or error returned by it. // // The error is non-nil only if GetDomainAsyncWorkflowConfiguraton threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_GetDomainAsyncWorkflowConfiguraton_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_GetDomainAsyncWorkflowConfiguraton_Result) (*GetDomainAsyncWorkflowConfiguratonResponse, error) }{} func init() { AdminService_GetDomainAsyncWorkflowConfiguraton_Helper.Args = func( request *GetDomainAsyncWorkflowConfiguratonRequest, ) *AdminService_GetDomainAsyncWorkflowConfiguraton_Args { return &AdminService_GetDomainAsyncWorkflowConfiguraton_Args{ Request: request, } } AdminService_GetDomainAsyncWorkflowConfiguraton_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true default: return false } } AdminService_GetDomainAsyncWorkflowConfiguraton_Helper.WrapResponse = func(success *GetDomainAsyncWorkflowConfiguratonResponse, err error) (*AdminService_GetDomainAsyncWorkflowConfiguraton_Result, error) { if err == nil { return &AdminService_GetDomainAsyncWorkflowConfiguraton_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetDomainAsyncWorkflowConfiguraton_Result.BadRequestError") } return &AdminService_GetDomainAsyncWorkflowConfiguraton_Result{BadRequestError: e}, nil } return nil, err } AdminService_GetDomainAsyncWorkflowConfiguraton_Helper.UnwrapResponse = func(result *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) (success *GetDomainAsyncWorkflowConfiguratonResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_GetDomainAsyncWorkflowConfiguraton_Result represents the result of a AdminService.GetDomainAsyncWorkflowConfiguraton function call. // // The result of a GetDomainAsyncWorkflowConfiguraton execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_GetDomainAsyncWorkflowConfiguraton_Result struct { // Value returned by GetDomainAsyncWorkflowConfiguraton after a successful execution. Success *GetDomainAsyncWorkflowConfiguratonResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` } // ToWire translates a AdminService_GetDomainAsyncWorkflowConfiguraton_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_GetDomainAsyncWorkflowConfiguraton_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDomainAsyncWorkflowConfiguratonResponse_Read(w wire.Value) (*GetDomainAsyncWorkflowConfiguratonResponse, error) { var v GetDomainAsyncWorkflowConfiguratonResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetDomainAsyncWorkflowConfiguraton_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetDomainAsyncWorkflowConfiguraton_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetDomainAsyncWorkflowConfiguraton_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetDomainAsyncWorkflowConfiguratonResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDomainAsyncWorkflowConfiguraton_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_GetDomainAsyncWorkflowConfiguraton_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetDomainAsyncWorkflowConfiguraton_Result struct could not be encoded. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDomainAsyncWorkflowConfiguraton_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetDomainAsyncWorkflowConfiguratonResponse_Decode(sr stream.Reader) (*GetDomainAsyncWorkflowConfiguratonResponse, error) { var v GetDomainAsyncWorkflowConfiguratonResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetDomainAsyncWorkflowConfiguraton_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetDomainAsyncWorkflowConfiguraton_Result struct could not be generated from the wire // representation. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetDomainAsyncWorkflowConfiguratonResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDomainAsyncWorkflowConfiguraton_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_GetDomainAsyncWorkflowConfiguraton_Result // struct. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } return fmt.Sprintf("AdminService_GetDomainAsyncWorkflowConfiguraton_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetDomainAsyncWorkflowConfiguraton_Result match the // provided AdminService_GetDomainAsyncWorkflowConfiguraton_Result. // // This function performs a deep comparison. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) Equals(rhs *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetDomainAsyncWorkflowConfiguraton_Result. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) GetSuccess() (o *GetDomainAsyncWorkflowConfiguratonResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetDomainAsyncWorkflowConfiguraton" for this struct. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) MethodName() string { return "GetDomainAsyncWorkflowConfiguraton" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_GetDomainAsyncWorkflowConfiguraton_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_GetDomainIsolationGroups_Args represents the arguments for the AdminService.GetDomainIsolationGroups function. // // The arguments for GetDomainIsolationGroups are sent and received over the wire as this struct. type AdminService_GetDomainIsolationGroups_Args struct { Request *GetDomainIsolationGroupsRequest `json:"request,omitempty"` } // ToWire translates a AdminService_GetDomainIsolationGroups_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetDomainIsolationGroups_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDomainIsolationGroupsRequest_Read(w wire.Value) (*GetDomainIsolationGroupsRequest, error) { var v GetDomainIsolationGroupsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetDomainIsolationGroups_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetDomainIsolationGroups_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetDomainIsolationGroups_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetDomainIsolationGroups_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetDomainIsolationGroupsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_GetDomainIsolationGroups_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetDomainIsolationGroups_Args struct could not be encoded. func (v *AdminService_GetDomainIsolationGroups_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetDomainIsolationGroupsRequest_Decode(sr stream.Reader) (*GetDomainIsolationGroupsRequest, error) { var v GetDomainIsolationGroupsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetDomainIsolationGroups_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetDomainIsolationGroups_Args struct could not be generated from the wire // representation. func (v *AdminService_GetDomainIsolationGroups_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetDomainIsolationGroupsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_GetDomainIsolationGroups_Args // struct. func (v *AdminService_GetDomainIsolationGroups_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_GetDomainIsolationGroups_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetDomainIsolationGroups_Args match the // provided AdminService_GetDomainIsolationGroups_Args. // // This function performs a deep comparison. func (v *AdminService_GetDomainIsolationGroups_Args) Equals(rhs *AdminService_GetDomainIsolationGroups_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetDomainIsolationGroups_Args. func (v *AdminService_GetDomainIsolationGroups_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainIsolationGroups_Args) GetRequest() (o *GetDomainIsolationGroupsRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_GetDomainIsolationGroups_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetDomainIsolationGroups" for this struct. func (v *AdminService_GetDomainIsolationGroups_Args) MethodName() string { return "GetDomainIsolationGroups" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_GetDomainIsolationGroups_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_GetDomainIsolationGroups_Helper provides functions that aid in handling the // parameters and return values of the AdminService.GetDomainIsolationGroups // function. var AdminService_GetDomainIsolationGroups_Helper = struct { // Args accepts the parameters of GetDomainIsolationGroups in-order and returns // the arguments struct for the function. Args func( request *GetDomainIsolationGroupsRequest, ) *AdminService_GetDomainIsolationGroups_Args // IsException returns true if the given error can be thrown // by GetDomainIsolationGroups. // // An error can be thrown by GetDomainIsolationGroups only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetDomainIsolationGroups // given its return value and error. // // This allows mapping values and errors returned by // GetDomainIsolationGroups into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetDomainIsolationGroups // // value, err := GetDomainIsolationGroups(args) // result, err := AdminService_GetDomainIsolationGroups_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetDomainIsolationGroups: %v", err) // } // serialize(result) WrapResponse func(*GetDomainIsolationGroupsResponse, error) (*AdminService_GetDomainIsolationGroups_Result, error) // UnwrapResponse takes the result struct for GetDomainIsolationGroups // and returns the value or error returned by it. // // The error is non-nil only if GetDomainIsolationGroups threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_GetDomainIsolationGroups_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_GetDomainIsolationGroups_Result) (*GetDomainIsolationGroupsResponse, error) }{} func init() { AdminService_GetDomainIsolationGroups_Helper.Args = func( request *GetDomainIsolationGroupsRequest, ) *AdminService_GetDomainIsolationGroups_Args { return &AdminService_GetDomainIsolationGroups_Args{ Request: request, } } AdminService_GetDomainIsolationGroups_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true default: return false } } AdminService_GetDomainIsolationGroups_Helper.WrapResponse = func(success *GetDomainIsolationGroupsResponse, err error) (*AdminService_GetDomainIsolationGroups_Result, error) { if err == nil { return &AdminService_GetDomainIsolationGroups_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetDomainIsolationGroups_Result.BadRequestError") } return &AdminService_GetDomainIsolationGroups_Result{BadRequestError: e}, nil } return nil, err } AdminService_GetDomainIsolationGroups_Helper.UnwrapResponse = func(result *AdminService_GetDomainIsolationGroups_Result) (success *GetDomainIsolationGroupsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_GetDomainIsolationGroups_Result represents the result of a AdminService.GetDomainIsolationGroups function call. // // The result of a GetDomainIsolationGroups execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_GetDomainIsolationGroups_Result struct { // Value returned by GetDomainIsolationGroups after a successful execution. Success *GetDomainIsolationGroupsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` } // ToWire translates a AdminService_GetDomainIsolationGroups_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetDomainIsolationGroups_Result) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_GetDomainIsolationGroups_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDomainIsolationGroupsResponse_Read(w wire.Value) (*GetDomainIsolationGroupsResponse, error) { var v GetDomainIsolationGroupsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetDomainIsolationGroups_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetDomainIsolationGroups_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetDomainIsolationGroups_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetDomainIsolationGroups_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetDomainIsolationGroupsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDomainIsolationGroups_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_GetDomainIsolationGroups_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetDomainIsolationGroups_Result struct could not be encoded. func (v *AdminService_GetDomainIsolationGroups_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDomainIsolationGroups_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetDomainIsolationGroupsResponse_Decode(sr stream.Reader) (*GetDomainIsolationGroupsResponse, error) { var v GetDomainIsolationGroupsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetDomainIsolationGroups_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetDomainIsolationGroups_Result struct could not be generated from the wire // representation. func (v *AdminService_GetDomainIsolationGroups_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetDomainIsolationGroupsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDomainIsolationGroups_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_GetDomainIsolationGroups_Result // struct. func (v *AdminService_GetDomainIsolationGroups_Result) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } return fmt.Sprintf("AdminService_GetDomainIsolationGroups_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetDomainIsolationGroups_Result match the // provided AdminService_GetDomainIsolationGroups_Result. // // This function performs a deep comparison. func (v *AdminService_GetDomainIsolationGroups_Result) Equals(rhs *AdminService_GetDomainIsolationGroups_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetDomainIsolationGroups_Result. func (v *AdminService_GetDomainIsolationGroups_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainIsolationGroups_Result) GetSuccess() (o *GetDomainIsolationGroupsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_GetDomainIsolationGroups_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainIsolationGroups_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_GetDomainIsolationGroups_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetDomainIsolationGroups" for this struct. func (v *AdminService_GetDomainIsolationGroups_Result) MethodName() string { return "GetDomainIsolationGroups" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_GetDomainIsolationGroups_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_GetDomainReplicationMessages_Args represents the arguments for the AdminService.GetDomainReplicationMessages function. // // The arguments for GetDomainReplicationMessages are sent and received over the wire as this struct. type AdminService_GetDomainReplicationMessages_Args struct { Request *replicator.GetDomainReplicationMessagesRequest `json:"request,omitempty"` } // ToWire translates a AdminService_GetDomainReplicationMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetDomainReplicationMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDomainReplicationMessagesRequest_Read(w wire.Value) (*replicator.GetDomainReplicationMessagesRequest, error) { var v replicator.GetDomainReplicationMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetDomainReplicationMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetDomainReplicationMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetDomainReplicationMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetDomainReplicationMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetDomainReplicationMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_GetDomainReplicationMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetDomainReplicationMessages_Args struct could not be encoded. func (v *AdminService_GetDomainReplicationMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetDomainReplicationMessagesRequest_Decode(sr stream.Reader) (*replicator.GetDomainReplicationMessagesRequest, error) { var v replicator.GetDomainReplicationMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetDomainReplicationMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetDomainReplicationMessages_Args struct could not be generated from the wire // representation. func (v *AdminService_GetDomainReplicationMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetDomainReplicationMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_GetDomainReplicationMessages_Args // struct. func (v *AdminService_GetDomainReplicationMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_GetDomainReplicationMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetDomainReplicationMessages_Args match the // provided AdminService_GetDomainReplicationMessages_Args. // // This function performs a deep comparison. func (v *AdminService_GetDomainReplicationMessages_Args) Equals(rhs *AdminService_GetDomainReplicationMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetDomainReplicationMessages_Args. func (v *AdminService_GetDomainReplicationMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainReplicationMessages_Args) GetRequest() (o *replicator.GetDomainReplicationMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_GetDomainReplicationMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetDomainReplicationMessages" for this struct. func (v *AdminService_GetDomainReplicationMessages_Args) MethodName() string { return "GetDomainReplicationMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_GetDomainReplicationMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_GetDomainReplicationMessages_Helper provides functions that aid in handling the // parameters and return values of the AdminService.GetDomainReplicationMessages // function. var AdminService_GetDomainReplicationMessages_Helper = struct { // Args accepts the parameters of GetDomainReplicationMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.GetDomainReplicationMessagesRequest, ) *AdminService_GetDomainReplicationMessages_Args // IsException returns true if the given error can be thrown // by GetDomainReplicationMessages. // // An error can be thrown by GetDomainReplicationMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetDomainReplicationMessages // given its return value and error. // // This allows mapping values and errors returned by // GetDomainReplicationMessages into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetDomainReplicationMessages // // value, err := GetDomainReplicationMessages(args) // result, err := AdminService_GetDomainReplicationMessages_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetDomainReplicationMessages: %v", err) // } // serialize(result) WrapResponse func(*replicator.GetDomainReplicationMessagesResponse, error) (*AdminService_GetDomainReplicationMessages_Result, error) // UnwrapResponse takes the result struct for GetDomainReplicationMessages // and returns the value or error returned by it. // // The error is non-nil only if GetDomainReplicationMessages threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_GetDomainReplicationMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_GetDomainReplicationMessages_Result) (*replicator.GetDomainReplicationMessagesResponse, error) }{} func init() { AdminService_GetDomainReplicationMessages_Helper.Args = func( request *replicator.GetDomainReplicationMessagesRequest, ) *AdminService_GetDomainReplicationMessages_Args { return &AdminService_GetDomainReplicationMessages_Args{ Request: request, } } AdminService_GetDomainReplicationMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true default: return false } } AdminService_GetDomainReplicationMessages_Helper.WrapResponse = func(success *replicator.GetDomainReplicationMessagesResponse, err error) (*AdminService_GetDomainReplicationMessages_Result, error) { if err == nil { return &AdminService_GetDomainReplicationMessages_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetDomainReplicationMessages_Result.BadRequestError") } return &AdminService_GetDomainReplicationMessages_Result{BadRequestError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetDomainReplicationMessages_Result.LimitExceededError") } return &AdminService_GetDomainReplicationMessages_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetDomainReplicationMessages_Result.ServiceBusyError") } return &AdminService_GetDomainReplicationMessages_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetDomainReplicationMessages_Result.ClientVersionNotSupportedError") } return &AdminService_GetDomainReplicationMessages_Result{ClientVersionNotSupportedError: e}, nil } return nil, err } AdminService_GetDomainReplicationMessages_Helper.UnwrapResponse = func(result *AdminService_GetDomainReplicationMessages_Result) (success *replicator.GetDomainReplicationMessagesResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_GetDomainReplicationMessages_Result represents the result of a AdminService.GetDomainReplicationMessages function call. // // The result of a GetDomainReplicationMessages execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_GetDomainReplicationMessages_Result struct { // Value returned by GetDomainReplicationMessages after a successful execution. Success *replicator.GetDomainReplicationMessagesResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` } // ToWire translates a AdminService_GetDomainReplicationMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetDomainReplicationMessages_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_GetDomainReplicationMessages_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDomainReplicationMessagesResponse_Read(w wire.Value) (*replicator.GetDomainReplicationMessagesResponse, error) { var v replicator.GetDomainReplicationMessagesResponse err := v.FromWire(w) return &v, err } func _LimitExceededError_Read(w wire.Value) (*shared.LimitExceededError, error) { var v shared.LimitExceededError err := v.FromWire(w) return &v, err } func _ClientVersionNotSupportedError_Read(w wire.Value) (*shared.ClientVersionNotSupportedError, error) { var v shared.ClientVersionNotSupportedError err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetDomainReplicationMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetDomainReplicationMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetDomainReplicationMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetDomainReplicationMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetDomainReplicationMessagesResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDomainReplicationMessages_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_GetDomainReplicationMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetDomainReplicationMessages_Result struct could not be encoded. func (v *AdminService_GetDomainReplicationMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDomainReplicationMessages_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetDomainReplicationMessagesResponse_Decode(sr stream.Reader) (*replicator.GetDomainReplicationMessagesResponse, error) { var v replicator.GetDomainReplicationMessagesResponse err := v.Decode(sr) return &v, err } func _LimitExceededError_Decode(sr stream.Reader) (*shared.LimitExceededError, error) { var v shared.LimitExceededError err := v.Decode(sr) return &v, err } func _ClientVersionNotSupportedError_Decode(sr stream.Reader) (*shared.ClientVersionNotSupportedError, error) { var v shared.ClientVersionNotSupportedError err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetDomainReplicationMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetDomainReplicationMessages_Result struct could not be generated from the wire // representation. func (v *AdminService_GetDomainReplicationMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetDomainReplicationMessagesResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDomainReplicationMessages_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_GetDomainReplicationMessages_Result // struct. func (v *AdminService_GetDomainReplicationMessages_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } return fmt.Sprintf("AdminService_GetDomainReplicationMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetDomainReplicationMessages_Result match the // provided AdminService_GetDomainReplicationMessages_Result. // // This function performs a deep comparison. func (v *AdminService_GetDomainReplicationMessages_Result) Equals(rhs *AdminService_GetDomainReplicationMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetDomainReplicationMessages_Result. func (v *AdminService_GetDomainReplicationMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainReplicationMessages_Result) GetSuccess() (o *replicator.GetDomainReplicationMessagesResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_GetDomainReplicationMessages_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainReplicationMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_GetDomainReplicationMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainReplicationMessages_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *AdminService_GetDomainReplicationMessages_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainReplicationMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_GetDomainReplicationMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *AdminService_GetDomainReplicationMessages_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *AdminService_GetDomainReplicationMessages_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetDomainReplicationMessages" for this struct. func (v *AdminService_GetDomainReplicationMessages_Result) MethodName() string { return "GetDomainReplicationMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_GetDomainReplicationMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_GetDynamicConfig_Args represents the arguments for the AdminService.GetDynamicConfig function. // // The arguments for GetDynamicConfig are sent and received over the wire as this struct. type AdminService_GetDynamicConfig_Args struct { Request *GetDynamicConfigRequest `json:"request,omitempty"` } // ToWire translates a AdminService_GetDynamicConfig_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetDynamicConfig_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDynamicConfigRequest_Read(w wire.Value) (*GetDynamicConfigRequest, error) { var v GetDynamicConfigRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetDynamicConfig_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetDynamicConfig_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetDynamicConfig_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetDynamicConfig_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetDynamicConfigRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_GetDynamicConfig_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetDynamicConfig_Args struct could not be encoded. func (v *AdminService_GetDynamicConfig_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetDynamicConfigRequest_Decode(sr stream.Reader) (*GetDynamicConfigRequest, error) { var v GetDynamicConfigRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetDynamicConfig_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetDynamicConfig_Args struct could not be generated from the wire // representation. func (v *AdminService_GetDynamicConfig_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetDynamicConfigRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_GetDynamicConfig_Args // struct. func (v *AdminService_GetDynamicConfig_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_GetDynamicConfig_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetDynamicConfig_Args match the // provided AdminService_GetDynamicConfig_Args. // // This function performs a deep comparison. func (v *AdminService_GetDynamicConfig_Args) Equals(rhs *AdminService_GetDynamicConfig_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetDynamicConfig_Args. func (v *AdminService_GetDynamicConfig_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_GetDynamicConfig_Args) GetRequest() (o *GetDynamicConfigRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_GetDynamicConfig_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetDynamicConfig" for this struct. func (v *AdminService_GetDynamicConfig_Args) MethodName() string { return "GetDynamicConfig" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_GetDynamicConfig_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_GetDynamicConfig_Helper provides functions that aid in handling the // parameters and return values of the AdminService.GetDynamicConfig // function. var AdminService_GetDynamicConfig_Helper = struct { // Args accepts the parameters of GetDynamicConfig in-order and returns // the arguments struct for the function. Args func( request *GetDynamicConfigRequest, ) *AdminService_GetDynamicConfig_Args // IsException returns true if the given error can be thrown // by GetDynamicConfig. // // An error can be thrown by GetDynamicConfig only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetDynamicConfig // given its return value and error. // // This allows mapping values and errors returned by // GetDynamicConfig into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetDynamicConfig // // value, err := GetDynamicConfig(args) // result, err := AdminService_GetDynamicConfig_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetDynamicConfig: %v", err) // } // serialize(result) WrapResponse func(*GetDynamicConfigResponse, error) (*AdminService_GetDynamicConfig_Result, error) // UnwrapResponse takes the result struct for GetDynamicConfig // and returns the value or error returned by it. // // The error is non-nil only if GetDynamicConfig threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_GetDynamicConfig_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_GetDynamicConfig_Result) (*GetDynamicConfigResponse, error) }{} func init() { AdminService_GetDynamicConfig_Helper.Args = func( request *GetDynamicConfigRequest, ) *AdminService_GetDynamicConfig_Args { return &AdminService_GetDynamicConfig_Args{ Request: request, } } AdminService_GetDynamicConfig_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true default: return false } } AdminService_GetDynamicConfig_Helper.WrapResponse = func(success *GetDynamicConfigResponse, err error) (*AdminService_GetDynamicConfig_Result, error) { if err == nil { return &AdminService_GetDynamicConfig_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetDynamicConfig_Result.BadRequestError") } return &AdminService_GetDynamicConfig_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetDynamicConfig_Result.InternalServiceError") } return &AdminService_GetDynamicConfig_Result{InternalServiceError: e}, nil } return nil, err } AdminService_GetDynamicConfig_Helper.UnwrapResponse = func(result *AdminService_GetDynamicConfig_Result) (success *GetDynamicConfigResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_GetDynamicConfig_Result represents the result of a AdminService.GetDynamicConfig function call. // // The result of a GetDynamicConfig execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_GetDynamicConfig_Result struct { // Value returned by GetDynamicConfig after a successful execution. Success *GetDynamicConfigResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` } // ToWire translates a AdminService_GetDynamicConfig_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetDynamicConfig_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_GetDynamicConfig_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDynamicConfigResponse_Read(w wire.Value) (*GetDynamicConfigResponse, error) { var v GetDynamicConfigResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetDynamicConfig_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetDynamicConfig_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetDynamicConfig_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetDynamicConfig_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetDynamicConfigResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDynamicConfig_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_GetDynamicConfig_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetDynamicConfig_Result struct could not be encoded. func (v *AdminService_GetDynamicConfig_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDynamicConfig_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetDynamicConfigResponse_Decode(sr stream.Reader) (*GetDynamicConfigResponse, error) { var v GetDynamicConfigResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetDynamicConfig_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetDynamicConfig_Result struct could not be generated from the wire // representation. func (v *AdminService_GetDynamicConfig_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetDynamicConfigResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetDynamicConfig_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_GetDynamicConfig_Result // struct. func (v *AdminService_GetDynamicConfig_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } return fmt.Sprintf("AdminService_GetDynamicConfig_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetDynamicConfig_Result match the // provided AdminService_GetDynamicConfig_Result. // // This function performs a deep comparison. func (v *AdminService_GetDynamicConfig_Result) Equals(rhs *AdminService_GetDynamicConfig_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetDynamicConfig_Result. func (v *AdminService_GetDynamicConfig_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_GetDynamicConfig_Result) GetSuccess() (o *GetDynamicConfigResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_GetDynamicConfig_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_GetDynamicConfig_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_GetDynamicConfig_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_GetDynamicConfig_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_GetDynamicConfig_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetDynamicConfig" for this struct. func (v *AdminService_GetDynamicConfig_Result) MethodName() string { return "GetDynamicConfig" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_GetDynamicConfig_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_GetGlobalIsolationGroups_Args represents the arguments for the AdminService.GetGlobalIsolationGroups function. // // The arguments for GetGlobalIsolationGroups are sent and received over the wire as this struct. type AdminService_GetGlobalIsolationGroups_Args struct { Request *GetGlobalIsolationGroupsRequest `json:"request,omitempty"` } // ToWire translates a AdminService_GetGlobalIsolationGroups_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetGlobalIsolationGroups_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetGlobalIsolationGroupsRequest_Read(w wire.Value) (*GetGlobalIsolationGroupsRequest, error) { var v GetGlobalIsolationGroupsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetGlobalIsolationGroups_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetGlobalIsolationGroups_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetGlobalIsolationGroups_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetGlobalIsolationGroups_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetGlobalIsolationGroupsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_GetGlobalIsolationGroups_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetGlobalIsolationGroups_Args struct could not be encoded. func (v *AdminService_GetGlobalIsolationGroups_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetGlobalIsolationGroupsRequest_Decode(sr stream.Reader) (*GetGlobalIsolationGroupsRequest, error) { var v GetGlobalIsolationGroupsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetGlobalIsolationGroups_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetGlobalIsolationGroups_Args struct could not be generated from the wire // representation. func (v *AdminService_GetGlobalIsolationGroups_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetGlobalIsolationGroupsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_GetGlobalIsolationGroups_Args // struct. func (v *AdminService_GetGlobalIsolationGroups_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_GetGlobalIsolationGroups_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetGlobalIsolationGroups_Args match the // provided AdminService_GetGlobalIsolationGroups_Args. // // This function performs a deep comparison. func (v *AdminService_GetGlobalIsolationGroups_Args) Equals(rhs *AdminService_GetGlobalIsolationGroups_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetGlobalIsolationGroups_Args. func (v *AdminService_GetGlobalIsolationGroups_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_GetGlobalIsolationGroups_Args) GetRequest() (o *GetGlobalIsolationGroupsRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_GetGlobalIsolationGroups_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetGlobalIsolationGroups" for this struct. func (v *AdminService_GetGlobalIsolationGroups_Args) MethodName() string { return "GetGlobalIsolationGroups" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_GetGlobalIsolationGroups_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_GetGlobalIsolationGroups_Helper provides functions that aid in handling the // parameters and return values of the AdminService.GetGlobalIsolationGroups // function. var AdminService_GetGlobalIsolationGroups_Helper = struct { // Args accepts the parameters of GetGlobalIsolationGroups in-order and returns // the arguments struct for the function. Args func( request *GetGlobalIsolationGroupsRequest, ) *AdminService_GetGlobalIsolationGroups_Args // IsException returns true if the given error can be thrown // by GetGlobalIsolationGroups. // // An error can be thrown by GetGlobalIsolationGroups only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetGlobalIsolationGroups // given its return value and error. // // This allows mapping values and errors returned by // GetGlobalIsolationGroups into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetGlobalIsolationGroups // // value, err := GetGlobalIsolationGroups(args) // result, err := AdminService_GetGlobalIsolationGroups_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetGlobalIsolationGroups: %v", err) // } // serialize(result) WrapResponse func(*GetGlobalIsolationGroupsResponse, error) (*AdminService_GetGlobalIsolationGroups_Result, error) // UnwrapResponse takes the result struct for GetGlobalIsolationGroups // and returns the value or error returned by it. // // The error is non-nil only if GetGlobalIsolationGroups threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_GetGlobalIsolationGroups_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_GetGlobalIsolationGroups_Result) (*GetGlobalIsolationGroupsResponse, error) }{} func init() { AdminService_GetGlobalIsolationGroups_Helper.Args = func( request *GetGlobalIsolationGroupsRequest, ) *AdminService_GetGlobalIsolationGroups_Args { return &AdminService_GetGlobalIsolationGroups_Args{ Request: request, } } AdminService_GetGlobalIsolationGroups_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true default: return false } } AdminService_GetGlobalIsolationGroups_Helper.WrapResponse = func(success *GetGlobalIsolationGroupsResponse, err error) (*AdminService_GetGlobalIsolationGroups_Result, error) { if err == nil { return &AdminService_GetGlobalIsolationGroups_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetGlobalIsolationGroups_Result.BadRequestError") } return &AdminService_GetGlobalIsolationGroups_Result{BadRequestError: e}, nil } return nil, err } AdminService_GetGlobalIsolationGroups_Helper.UnwrapResponse = func(result *AdminService_GetGlobalIsolationGroups_Result) (success *GetGlobalIsolationGroupsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_GetGlobalIsolationGroups_Result represents the result of a AdminService.GetGlobalIsolationGroups function call. // // The result of a GetGlobalIsolationGroups execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_GetGlobalIsolationGroups_Result struct { // Value returned by GetGlobalIsolationGroups after a successful execution. Success *GetGlobalIsolationGroupsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` } // ToWire translates a AdminService_GetGlobalIsolationGroups_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetGlobalIsolationGroups_Result) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_GetGlobalIsolationGroups_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetGlobalIsolationGroupsResponse_Read(w wire.Value) (*GetGlobalIsolationGroupsResponse, error) { var v GetGlobalIsolationGroupsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetGlobalIsolationGroups_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetGlobalIsolationGroups_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetGlobalIsolationGroups_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetGlobalIsolationGroups_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetGlobalIsolationGroupsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetGlobalIsolationGroups_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_GetGlobalIsolationGroups_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetGlobalIsolationGroups_Result struct could not be encoded. func (v *AdminService_GetGlobalIsolationGroups_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetGlobalIsolationGroups_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetGlobalIsolationGroupsResponse_Decode(sr stream.Reader) (*GetGlobalIsolationGroupsResponse, error) { var v GetGlobalIsolationGroupsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetGlobalIsolationGroups_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetGlobalIsolationGroups_Result struct could not be generated from the wire // representation. func (v *AdminService_GetGlobalIsolationGroups_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetGlobalIsolationGroupsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetGlobalIsolationGroups_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_GetGlobalIsolationGroups_Result // struct. func (v *AdminService_GetGlobalIsolationGroups_Result) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } return fmt.Sprintf("AdminService_GetGlobalIsolationGroups_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetGlobalIsolationGroups_Result match the // provided AdminService_GetGlobalIsolationGroups_Result. // // This function performs a deep comparison. func (v *AdminService_GetGlobalIsolationGroups_Result) Equals(rhs *AdminService_GetGlobalIsolationGroups_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetGlobalIsolationGroups_Result. func (v *AdminService_GetGlobalIsolationGroups_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_GetGlobalIsolationGroups_Result) GetSuccess() (o *GetGlobalIsolationGroupsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_GetGlobalIsolationGroups_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_GetGlobalIsolationGroups_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_GetGlobalIsolationGroups_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetGlobalIsolationGroups" for this struct. func (v *AdminService_GetGlobalIsolationGroups_Result) MethodName() string { return "GetGlobalIsolationGroups" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_GetGlobalIsolationGroups_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_GetReplicationMessages_Args represents the arguments for the AdminService.GetReplicationMessages function. // // The arguments for GetReplicationMessages are sent and received over the wire as this struct. type AdminService_GetReplicationMessages_Args struct { Request *replicator.GetReplicationMessagesRequest `json:"request,omitempty"` } // ToWire translates a AdminService_GetReplicationMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetReplicationMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetReplicationMessagesRequest_Read(w wire.Value) (*replicator.GetReplicationMessagesRequest, error) { var v replicator.GetReplicationMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetReplicationMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetReplicationMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetReplicationMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetReplicationMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetReplicationMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_GetReplicationMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetReplicationMessages_Args struct could not be encoded. func (v *AdminService_GetReplicationMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetReplicationMessagesRequest_Decode(sr stream.Reader) (*replicator.GetReplicationMessagesRequest, error) { var v replicator.GetReplicationMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetReplicationMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetReplicationMessages_Args struct could not be generated from the wire // representation. func (v *AdminService_GetReplicationMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetReplicationMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_GetReplicationMessages_Args // struct. func (v *AdminService_GetReplicationMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_GetReplicationMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetReplicationMessages_Args match the // provided AdminService_GetReplicationMessages_Args. // // This function performs a deep comparison. func (v *AdminService_GetReplicationMessages_Args) Equals(rhs *AdminService_GetReplicationMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetReplicationMessages_Args. func (v *AdminService_GetReplicationMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_GetReplicationMessages_Args) GetRequest() (o *replicator.GetReplicationMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_GetReplicationMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetReplicationMessages" for this struct. func (v *AdminService_GetReplicationMessages_Args) MethodName() string { return "GetReplicationMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_GetReplicationMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_GetReplicationMessages_Helper provides functions that aid in handling the // parameters and return values of the AdminService.GetReplicationMessages // function. var AdminService_GetReplicationMessages_Helper = struct { // Args accepts the parameters of GetReplicationMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.GetReplicationMessagesRequest, ) *AdminService_GetReplicationMessages_Args // IsException returns true if the given error can be thrown // by GetReplicationMessages. // // An error can be thrown by GetReplicationMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetReplicationMessages // given its return value and error. // // This allows mapping values and errors returned by // GetReplicationMessages into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetReplicationMessages // // value, err := GetReplicationMessages(args) // result, err := AdminService_GetReplicationMessages_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetReplicationMessages: %v", err) // } // serialize(result) WrapResponse func(*replicator.GetReplicationMessagesResponse, error) (*AdminService_GetReplicationMessages_Result, error) // UnwrapResponse takes the result struct for GetReplicationMessages // and returns the value or error returned by it. // // The error is non-nil only if GetReplicationMessages threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_GetReplicationMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_GetReplicationMessages_Result) (*replicator.GetReplicationMessagesResponse, error) }{} func init() { AdminService_GetReplicationMessages_Helper.Args = func( request *replicator.GetReplicationMessagesRequest, ) *AdminService_GetReplicationMessages_Args { return &AdminService_GetReplicationMessages_Args{ Request: request, } } AdminService_GetReplicationMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true default: return false } } AdminService_GetReplicationMessages_Helper.WrapResponse = func(success *replicator.GetReplicationMessagesResponse, err error) (*AdminService_GetReplicationMessages_Result, error) { if err == nil { return &AdminService_GetReplicationMessages_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetReplicationMessages_Result.BadRequestError") } return &AdminService_GetReplicationMessages_Result{BadRequestError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetReplicationMessages_Result.LimitExceededError") } return &AdminService_GetReplicationMessages_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetReplicationMessages_Result.ServiceBusyError") } return &AdminService_GetReplicationMessages_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetReplicationMessages_Result.ClientVersionNotSupportedError") } return &AdminService_GetReplicationMessages_Result{ClientVersionNotSupportedError: e}, nil } return nil, err } AdminService_GetReplicationMessages_Helper.UnwrapResponse = func(result *AdminService_GetReplicationMessages_Result) (success *replicator.GetReplicationMessagesResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_GetReplicationMessages_Result represents the result of a AdminService.GetReplicationMessages function call. // // The result of a GetReplicationMessages execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_GetReplicationMessages_Result struct { // Value returned by GetReplicationMessages after a successful execution. Success *replicator.GetReplicationMessagesResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` } // ToWire translates a AdminService_GetReplicationMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetReplicationMessages_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_GetReplicationMessages_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetReplicationMessagesResponse_Read(w wire.Value) (*replicator.GetReplicationMessagesResponse, error) { var v replicator.GetReplicationMessagesResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetReplicationMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetReplicationMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetReplicationMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetReplicationMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetReplicationMessagesResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetReplicationMessages_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_GetReplicationMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetReplicationMessages_Result struct could not be encoded. func (v *AdminService_GetReplicationMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetReplicationMessages_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetReplicationMessagesResponse_Decode(sr stream.Reader) (*replicator.GetReplicationMessagesResponse, error) { var v replicator.GetReplicationMessagesResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetReplicationMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetReplicationMessages_Result struct could not be generated from the wire // representation. func (v *AdminService_GetReplicationMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetReplicationMessagesResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetReplicationMessages_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_GetReplicationMessages_Result // struct. func (v *AdminService_GetReplicationMessages_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } return fmt.Sprintf("AdminService_GetReplicationMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetReplicationMessages_Result match the // provided AdminService_GetReplicationMessages_Result. // // This function performs a deep comparison. func (v *AdminService_GetReplicationMessages_Result) Equals(rhs *AdminService_GetReplicationMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetReplicationMessages_Result. func (v *AdminService_GetReplicationMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_GetReplicationMessages_Result) GetSuccess() (o *replicator.GetReplicationMessagesResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_GetReplicationMessages_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_GetReplicationMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_GetReplicationMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *AdminService_GetReplicationMessages_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *AdminService_GetReplicationMessages_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_GetReplicationMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_GetReplicationMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *AdminService_GetReplicationMessages_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *AdminService_GetReplicationMessages_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetReplicationMessages" for this struct. func (v *AdminService_GetReplicationMessages_Result) MethodName() string { return "GetReplicationMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_GetReplicationMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_GetWorkflowExecutionRawHistoryV2_Args represents the arguments for the AdminService.GetWorkflowExecutionRawHistoryV2 function. // // The arguments for GetWorkflowExecutionRawHistoryV2 are sent and received over the wire as this struct. type AdminService_GetWorkflowExecutionRawHistoryV2_Args struct { GetRequest *GetWorkflowExecutionRawHistoryV2Request `json:"getRequest,omitempty"` } // ToWire translates a AdminService_GetWorkflowExecutionRawHistoryV2_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.GetRequest != nil { w, err = v.GetRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetWorkflowExecutionRawHistoryV2Request_Read(w wire.Value) (*GetWorkflowExecutionRawHistoryV2Request, error) { var v GetWorkflowExecutionRawHistoryV2Request err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetWorkflowExecutionRawHistoryV2_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetWorkflowExecutionRawHistoryV2_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetWorkflowExecutionRawHistoryV2_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.GetRequest, err = _GetWorkflowExecutionRawHistoryV2Request_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_GetWorkflowExecutionRawHistoryV2_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetWorkflowExecutionRawHistoryV2_Args struct could not be encoded. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.GetRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.GetRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetWorkflowExecutionRawHistoryV2Request_Decode(sr stream.Reader) (*GetWorkflowExecutionRawHistoryV2Request, error) { var v GetWorkflowExecutionRawHistoryV2Request err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetWorkflowExecutionRawHistoryV2_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetWorkflowExecutionRawHistoryV2_Args struct could not be generated from the wire // representation. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.GetRequest, err = _GetWorkflowExecutionRawHistoryV2Request_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_GetWorkflowExecutionRawHistoryV2_Args // struct. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.GetRequest != nil { fields[i] = fmt.Sprintf("GetRequest: %v", v.GetRequest) i++ } return fmt.Sprintf("AdminService_GetWorkflowExecutionRawHistoryV2_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetWorkflowExecutionRawHistoryV2_Args match the // provided AdminService_GetWorkflowExecutionRawHistoryV2_Args. // // This function performs a deep comparison. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) Equals(rhs *AdminService_GetWorkflowExecutionRawHistoryV2_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.GetRequest == nil && rhs.GetRequest == nil) || (v.GetRequest != nil && rhs.GetRequest != nil && v.GetRequest.Equals(rhs.GetRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetWorkflowExecutionRawHistoryV2_Args. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.GetRequest != nil { err = multierr.Append(err, enc.AddObject("getRequest", v.GetRequest)) } return err } // GetGetRequest returns the value of GetRequest if it is set or its // zero value if it is unset. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) GetGetRequest() (o *GetWorkflowExecutionRawHistoryV2Request) { if v != nil && v.GetRequest != nil { return v.GetRequest } return } // IsSetGetRequest returns true if GetRequest is not nil. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) IsSetGetRequest() bool { return v != nil && v.GetRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetWorkflowExecutionRawHistoryV2" for this struct. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) MethodName() string { return "GetWorkflowExecutionRawHistoryV2" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_GetWorkflowExecutionRawHistoryV2_Helper provides functions that aid in handling the // parameters and return values of the AdminService.GetWorkflowExecutionRawHistoryV2 // function. var AdminService_GetWorkflowExecutionRawHistoryV2_Helper = struct { // Args accepts the parameters of GetWorkflowExecutionRawHistoryV2 in-order and returns // the arguments struct for the function. Args func( getRequest *GetWorkflowExecutionRawHistoryV2Request, ) *AdminService_GetWorkflowExecutionRawHistoryV2_Args // IsException returns true if the given error can be thrown // by GetWorkflowExecutionRawHistoryV2. // // An error can be thrown by GetWorkflowExecutionRawHistoryV2 only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetWorkflowExecutionRawHistoryV2 // given its return value and error. // // This allows mapping values and errors returned by // GetWorkflowExecutionRawHistoryV2 into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetWorkflowExecutionRawHistoryV2 // // value, err := GetWorkflowExecutionRawHistoryV2(args) // result, err := AdminService_GetWorkflowExecutionRawHistoryV2_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetWorkflowExecutionRawHistoryV2: %v", err) // } // serialize(result) WrapResponse func(*GetWorkflowExecutionRawHistoryV2Response, error) (*AdminService_GetWorkflowExecutionRawHistoryV2_Result, error) // UnwrapResponse takes the result struct for GetWorkflowExecutionRawHistoryV2 // and returns the value or error returned by it. // // The error is non-nil only if GetWorkflowExecutionRawHistoryV2 threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_GetWorkflowExecutionRawHistoryV2_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_GetWorkflowExecutionRawHistoryV2_Result) (*GetWorkflowExecutionRawHistoryV2Response, error) }{} func init() { AdminService_GetWorkflowExecutionRawHistoryV2_Helper.Args = func( getRequest *GetWorkflowExecutionRawHistoryV2Request, ) *AdminService_GetWorkflowExecutionRawHistoryV2_Args { return &AdminService_GetWorkflowExecutionRawHistoryV2_Args{ GetRequest: getRequest, } } AdminService_GetWorkflowExecutionRawHistoryV2_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true default: return false } } AdminService_GetWorkflowExecutionRawHistoryV2_Helper.WrapResponse = func(success *GetWorkflowExecutionRawHistoryV2Response, err error) (*AdminService_GetWorkflowExecutionRawHistoryV2_Result, error) { if err == nil { return &AdminService_GetWorkflowExecutionRawHistoryV2_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetWorkflowExecutionRawHistoryV2_Result.BadRequestError") } return &AdminService_GetWorkflowExecutionRawHistoryV2_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetWorkflowExecutionRawHistoryV2_Result.InternalServiceError") } return &AdminService_GetWorkflowExecutionRawHistoryV2_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetWorkflowExecutionRawHistoryV2_Result.EntityNotExistError") } return &AdminService_GetWorkflowExecutionRawHistoryV2_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_GetWorkflowExecutionRawHistoryV2_Result.ServiceBusyError") } return &AdminService_GetWorkflowExecutionRawHistoryV2_Result{ServiceBusyError: e}, nil } return nil, err } AdminService_GetWorkflowExecutionRawHistoryV2_Helper.UnwrapResponse = func(result *AdminService_GetWorkflowExecutionRawHistoryV2_Result) (success *GetWorkflowExecutionRawHistoryV2Response, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_GetWorkflowExecutionRawHistoryV2_Result represents the result of a AdminService.GetWorkflowExecutionRawHistoryV2 function call. // // The result of a GetWorkflowExecutionRawHistoryV2 execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_GetWorkflowExecutionRawHistoryV2_Result struct { // Value returned by GetWorkflowExecutionRawHistoryV2 after a successful execution. Success *GetWorkflowExecutionRawHistoryV2Response `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a AdminService_GetWorkflowExecutionRawHistoryV2_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_GetWorkflowExecutionRawHistoryV2_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetWorkflowExecutionRawHistoryV2Response_Read(w wire.Value) (*GetWorkflowExecutionRawHistoryV2Response, error) { var v GetWorkflowExecutionRawHistoryV2Response err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_GetWorkflowExecutionRawHistoryV2_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_GetWorkflowExecutionRawHistoryV2_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_GetWorkflowExecutionRawHistoryV2_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetWorkflowExecutionRawHistoryV2Response_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetWorkflowExecutionRawHistoryV2_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_GetWorkflowExecutionRawHistoryV2_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_GetWorkflowExecutionRawHistoryV2_Result struct could not be encoded. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetWorkflowExecutionRawHistoryV2_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetWorkflowExecutionRawHistoryV2Response_Decode(sr stream.Reader) (*GetWorkflowExecutionRawHistoryV2Response, error) { var v GetWorkflowExecutionRawHistoryV2Response err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_GetWorkflowExecutionRawHistoryV2_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_GetWorkflowExecutionRawHistoryV2_Result struct could not be generated from the wire // representation. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetWorkflowExecutionRawHistoryV2Response_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_GetWorkflowExecutionRawHistoryV2_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_GetWorkflowExecutionRawHistoryV2_Result // struct. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("AdminService_GetWorkflowExecutionRawHistoryV2_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_GetWorkflowExecutionRawHistoryV2_Result match the // provided AdminService_GetWorkflowExecutionRawHistoryV2_Result. // // This function performs a deep comparison. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) Equals(rhs *AdminService_GetWorkflowExecutionRawHistoryV2_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_GetWorkflowExecutionRawHistoryV2_Result. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) GetSuccess() (o *GetWorkflowExecutionRawHistoryV2Response) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetWorkflowExecutionRawHistoryV2" for this struct. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) MethodName() string { return "GetWorkflowExecutionRawHistoryV2" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_GetWorkflowExecutionRawHistoryV2_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_ListDynamicConfig_Args represents the arguments for the AdminService.ListDynamicConfig function. // // The arguments for ListDynamicConfig are sent and received over the wire as this struct. type AdminService_ListDynamicConfig_Args struct { Request *ListDynamicConfigRequest `json:"request,omitempty"` } // ToWire translates a AdminService_ListDynamicConfig_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_ListDynamicConfig_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListDynamicConfigRequest_Read(w wire.Value) (*ListDynamicConfigRequest, error) { var v ListDynamicConfigRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_ListDynamicConfig_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_ListDynamicConfig_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_ListDynamicConfig_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_ListDynamicConfig_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _ListDynamicConfigRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_ListDynamicConfig_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_ListDynamicConfig_Args struct could not be encoded. func (v *AdminService_ListDynamicConfig_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ListDynamicConfigRequest_Decode(sr stream.Reader) (*ListDynamicConfigRequest, error) { var v ListDynamicConfigRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_ListDynamicConfig_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_ListDynamicConfig_Args struct could not be generated from the wire // representation. func (v *AdminService_ListDynamicConfig_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _ListDynamicConfigRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_ListDynamicConfig_Args // struct. func (v *AdminService_ListDynamicConfig_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_ListDynamicConfig_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_ListDynamicConfig_Args match the // provided AdminService_ListDynamicConfig_Args. // // This function performs a deep comparison. func (v *AdminService_ListDynamicConfig_Args) Equals(rhs *AdminService_ListDynamicConfig_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_ListDynamicConfig_Args. func (v *AdminService_ListDynamicConfig_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_ListDynamicConfig_Args) GetRequest() (o *ListDynamicConfigRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_ListDynamicConfig_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ListDynamicConfig" for this struct. func (v *AdminService_ListDynamicConfig_Args) MethodName() string { return "ListDynamicConfig" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_ListDynamicConfig_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_ListDynamicConfig_Helper provides functions that aid in handling the // parameters and return values of the AdminService.ListDynamicConfig // function. var AdminService_ListDynamicConfig_Helper = struct { // Args accepts the parameters of ListDynamicConfig in-order and returns // the arguments struct for the function. Args func( request *ListDynamicConfigRequest, ) *AdminService_ListDynamicConfig_Args // IsException returns true if the given error can be thrown // by ListDynamicConfig. // // An error can be thrown by ListDynamicConfig only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ListDynamicConfig // given its return value and error. // // This allows mapping values and errors returned by // ListDynamicConfig into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ListDynamicConfig // // value, err := ListDynamicConfig(args) // result, err := AdminService_ListDynamicConfig_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ListDynamicConfig: %v", err) // } // serialize(result) WrapResponse func(*ListDynamicConfigResponse, error) (*AdminService_ListDynamicConfig_Result, error) // UnwrapResponse takes the result struct for ListDynamicConfig // and returns the value or error returned by it. // // The error is non-nil only if ListDynamicConfig threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_ListDynamicConfig_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_ListDynamicConfig_Result) (*ListDynamicConfigResponse, error) }{} func init() { AdminService_ListDynamicConfig_Helper.Args = func( request *ListDynamicConfigRequest, ) *AdminService_ListDynamicConfig_Args { return &AdminService_ListDynamicConfig_Args{ Request: request, } } AdminService_ListDynamicConfig_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.InternalServiceError: return true default: return false } } AdminService_ListDynamicConfig_Helper.WrapResponse = func(success *ListDynamicConfigResponse, err error) (*AdminService_ListDynamicConfig_Result, error) { if err == nil { return &AdminService_ListDynamicConfig_Result{Success: success}, nil } switch e := err.(type) { case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ListDynamicConfig_Result.InternalServiceError") } return &AdminService_ListDynamicConfig_Result{InternalServiceError: e}, nil } return nil, err } AdminService_ListDynamicConfig_Helper.UnwrapResponse = func(result *AdminService_ListDynamicConfig_Result) (success *ListDynamicConfigResponse, err error) { if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_ListDynamicConfig_Result represents the result of a AdminService.ListDynamicConfig function call. // // The result of a ListDynamicConfig execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_ListDynamicConfig_Result struct { // Value returned by ListDynamicConfig after a successful execution. Success *ListDynamicConfigResponse `json:"success,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` } // ToWire translates a AdminService_ListDynamicConfig_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_ListDynamicConfig_Result) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_ListDynamicConfig_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListDynamicConfigResponse_Read(w wire.Value) (*ListDynamicConfigResponse, error) { var v ListDynamicConfigResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_ListDynamicConfig_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_ListDynamicConfig_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_ListDynamicConfig_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_ListDynamicConfig_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ListDynamicConfigResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_ListDynamicConfig_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_ListDynamicConfig_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_ListDynamicConfig_Result struct could not be encoded. func (v *AdminService_ListDynamicConfig_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_ListDynamicConfig_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ListDynamicConfigResponse_Decode(sr stream.Reader) (*ListDynamicConfigResponse, error) { var v ListDynamicConfigResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_ListDynamicConfig_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_ListDynamicConfig_Result struct could not be generated from the wire // representation. func (v *AdminService_ListDynamicConfig_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ListDynamicConfigResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_ListDynamicConfig_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_ListDynamicConfig_Result // struct. func (v *AdminService_ListDynamicConfig_Result) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } return fmt.Sprintf("AdminService_ListDynamicConfig_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_ListDynamicConfig_Result match the // provided AdminService_ListDynamicConfig_Result. // // This function performs a deep comparison. func (v *AdminService_ListDynamicConfig_Result) Equals(rhs *AdminService_ListDynamicConfig_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_ListDynamicConfig_Result. func (v *AdminService_ListDynamicConfig_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_ListDynamicConfig_Result) GetSuccess() (o *ListDynamicConfigResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_ListDynamicConfig_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_ListDynamicConfig_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_ListDynamicConfig_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ListDynamicConfig" for this struct. func (v *AdminService_ListDynamicConfig_Result) MethodName() string { return "ListDynamicConfig" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_ListDynamicConfig_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_MaintainCorruptWorkflow_Args represents the arguments for the AdminService.MaintainCorruptWorkflow function. // // The arguments for MaintainCorruptWorkflow are sent and received over the wire as this struct. type AdminService_MaintainCorruptWorkflow_Args struct { Request *AdminMaintainWorkflowRequest `json:"request,omitempty"` } // ToWire translates a AdminService_MaintainCorruptWorkflow_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_MaintainCorruptWorkflow_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AdminMaintainWorkflowRequest_Read(w wire.Value) (*AdminMaintainWorkflowRequest, error) { var v AdminMaintainWorkflowRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_MaintainCorruptWorkflow_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_MaintainCorruptWorkflow_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_MaintainCorruptWorkflow_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_MaintainCorruptWorkflow_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _AdminMaintainWorkflowRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_MaintainCorruptWorkflow_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_MaintainCorruptWorkflow_Args struct could not be encoded. func (v *AdminService_MaintainCorruptWorkflow_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _AdminMaintainWorkflowRequest_Decode(sr stream.Reader) (*AdminMaintainWorkflowRequest, error) { var v AdminMaintainWorkflowRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_MaintainCorruptWorkflow_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_MaintainCorruptWorkflow_Args struct could not be generated from the wire // representation. func (v *AdminService_MaintainCorruptWorkflow_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _AdminMaintainWorkflowRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_MaintainCorruptWorkflow_Args // struct. func (v *AdminService_MaintainCorruptWorkflow_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_MaintainCorruptWorkflow_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_MaintainCorruptWorkflow_Args match the // provided AdminService_MaintainCorruptWorkflow_Args. // // This function performs a deep comparison. func (v *AdminService_MaintainCorruptWorkflow_Args) Equals(rhs *AdminService_MaintainCorruptWorkflow_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_MaintainCorruptWorkflow_Args. func (v *AdminService_MaintainCorruptWorkflow_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_MaintainCorruptWorkflow_Args) GetRequest() (o *AdminMaintainWorkflowRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_MaintainCorruptWorkflow_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "MaintainCorruptWorkflow" for this struct. func (v *AdminService_MaintainCorruptWorkflow_Args) MethodName() string { return "MaintainCorruptWorkflow" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_MaintainCorruptWorkflow_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_MaintainCorruptWorkflow_Helper provides functions that aid in handling the // parameters and return values of the AdminService.MaintainCorruptWorkflow // function. var AdminService_MaintainCorruptWorkflow_Helper = struct { // Args accepts the parameters of MaintainCorruptWorkflow in-order and returns // the arguments struct for the function. Args func( request *AdminMaintainWorkflowRequest, ) *AdminService_MaintainCorruptWorkflow_Args // IsException returns true if the given error can be thrown // by MaintainCorruptWorkflow. // // An error can be thrown by MaintainCorruptWorkflow only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for MaintainCorruptWorkflow // given its return value and error. // // This allows mapping values and errors returned by // MaintainCorruptWorkflow into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by MaintainCorruptWorkflow // // value, err := MaintainCorruptWorkflow(args) // result, err := AdminService_MaintainCorruptWorkflow_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from MaintainCorruptWorkflow: %v", err) // } // serialize(result) WrapResponse func(*AdminMaintainWorkflowResponse, error) (*AdminService_MaintainCorruptWorkflow_Result, error) // UnwrapResponse takes the result struct for MaintainCorruptWorkflow // and returns the value or error returned by it. // // The error is non-nil only if MaintainCorruptWorkflow threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_MaintainCorruptWorkflow_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_MaintainCorruptWorkflow_Result) (*AdminMaintainWorkflowResponse, error) }{} func init() { AdminService_MaintainCorruptWorkflow_Helper.Args = func( request *AdminMaintainWorkflowRequest, ) *AdminService_MaintainCorruptWorkflow_Args { return &AdminService_MaintainCorruptWorkflow_Args{ Request: request, } } AdminService_MaintainCorruptWorkflow_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.InternalServiceError: return true default: return false } } AdminService_MaintainCorruptWorkflow_Helper.WrapResponse = func(success *AdminMaintainWorkflowResponse, err error) (*AdminService_MaintainCorruptWorkflow_Result, error) { if err == nil { return &AdminService_MaintainCorruptWorkflow_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_MaintainCorruptWorkflow_Result.BadRequestError") } return &AdminService_MaintainCorruptWorkflow_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_MaintainCorruptWorkflow_Result.EntityNotExistError") } return &AdminService_MaintainCorruptWorkflow_Result{EntityNotExistError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_MaintainCorruptWorkflow_Result.InternalServiceError") } return &AdminService_MaintainCorruptWorkflow_Result{InternalServiceError: e}, nil } return nil, err } AdminService_MaintainCorruptWorkflow_Helper.UnwrapResponse = func(result *AdminService_MaintainCorruptWorkflow_Result) (success *AdminMaintainWorkflowResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_MaintainCorruptWorkflow_Result represents the result of a AdminService.MaintainCorruptWorkflow function call. // // The result of a MaintainCorruptWorkflow execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_MaintainCorruptWorkflow_Result struct { // Value returned by MaintainCorruptWorkflow after a successful execution. Success *AdminMaintainWorkflowResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` } // ToWire translates a AdminService_MaintainCorruptWorkflow_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_MaintainCorruptWorkflow_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_MaintainCorruptWorkflow_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AdminMaintainWorkflowResponse_Read(w wire.Value) (*AdminMaintainWorkflowResponse, error) { var v AdminMaintainWorkflowResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_MaintainCorruptWorkflow_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_MaintainCorruptWorkflow_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_MaintainCorruptWorkflow_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_MaintainCorruptWorkflow_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _AdminMaintainWorkflowResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_MaintainCorruptWorkflow_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_MaintainCorruptWorkflow_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_MaintainCorruptWorkflow_Result struct could not be encoded. func (v *AdminService_MaintainCorruptWorkflow_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_MaintainCorruptWorkflow_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _AdminMaintainWorkflowResponse_Decode(sr stream.Reader) (*AdminMaintainWorkflowResponse, error) { var v AdminMaintainWorkflowResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_MaintainCorruptWorkflow_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_MaintainCorruptWorkflow_Result struct could not be generated from the wire // representation. func (v *AdminService_MaintainCorruptWorkflow_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _AdminMaintainWorkflowResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.InternalServiceError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_MaintainCorruptWorkflow_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_MaintainCorruptWorkflow_Result // struct. func (v *AdminService_MaintainCorruptWorkflow_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } return fmt.Sprintf("AdminService_MaintainCorruptWorkflow_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_MaintainCorruptWorkflow_Result match the // provided AdminService_MaintainCorruptWorkflow_Result. // // This function performs a deep comparison. func (v *AdminService_MaintainCorruptWorkflow_Result) Equals(rhs *AdminService_MaintainCorruptWorkflow_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_MaintainCorruptWorkflow_Result. func (v *AdminService_MaintainCorruptWorkflow_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_MaintainCorruptWorkflow_Result) GetSuccess() (o *AdminMaintainWorkflowResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_MaintainCorruptWorkflow_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_MaintainCorruptWorkflow_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_MaintainCorruptWorkflow_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *AdminService_MaintainCorruptWorkflow_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *AdminService_MaintainCorruptWorkflow_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_MaintainCorruptWorkflow_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_MaintainCorruptWorkflow_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "MaintainCorruptWorkflow" for this struct. func (v *AdminService_MaintainCorruptWorkflow_Result) MethodName() string { return "MaintainCorruptWorkflow" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_MaintainCorruptWorkflow_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_MergeDLQMessages_Args represents the arguments for the AdminService.MergeDLQMessages function. // // The arguments for MergeDLQMessages are sent and received over the wire as this struct. type AdminService_MergeDLQMessages_Args struct { Request *replicator.MergeDLQMessagesRequest `json:"request,omitempty"` } // ToWire translates a AdminService_MergeDLQMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_MergeDLQMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _MergeDLQMessagesRequest_Read(w wire.Value) (*replicator.MergeDLQMessagesRequest, error) { var v replicator.MergeDLQMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_MergeDLQMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_MergeDLQMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_MergeDLQMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_MergeDLQMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _MergeDLQMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_MergeDLQMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_MergeDLQMessages_Args struct could not be encoded. func (v *AdminService_MergeDLQMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _MergeDLQMessagesRequest_Decode(sr stream.Reader) (*replicator.MergeDLQMessagesRequest, error) { var v replicator.MergeDLQMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_MergeDLQMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_MergeDLQMessages_Args struct could not be generated from the wire // representation. func (v *AdminService_MergeDLQMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _MergeDLQMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_MergeDLQMessages_Args // struct. func (v *AdminService_MergeDLQMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_MergeDLQMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_MergeDLQMessages_Args match the // provided AdminService_MergeDLQMessages_Args. // // This function performs a deep comparison. func (v *AdminService_MergeDLQMessages_Args) Equals(rhs *AdminService_MergeDLQMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_MergeDLQMessages_Args. func (v *AdminService_MergeDLQMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_MergeDLQMessages_Args) GetRequest() (o *replicator.MergeDLQMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_MergeDLQMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "MergeDLQMessages" for this struct. func (v *AdminService_MergeDLQMessages_Args) MethodName() string { return "MergeDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_MergeDLQMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_MergeDLQMessages_Helper provides functions that aid in handling the // parameters and return values of the AdminService.MergeDLQMessages // function. var AdminService_MergeDLQMessages_Helper = struct { // Args accepts the parameters of MergeDLQMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.MergeDLQMessagesRequest, ) *AdminService_MergeDLQMessages_Args // IsException returns true if the given error can be thrown // by MergeDLQMessages. // // An error can be thrown by MergeDLQMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for MergeDLQMessages // given its return value and error. // // This allows mapping values and errors returned by // MergeDLQMessages into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by MergeDLQMessages // // value, err := MergeDLQMessages(args) // result, err := AdminService_MergeDLQMessages_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from MergeDLQMessages: %v", err) // } // serialize(result) WrapResponse func(*replicator.MergeDLQMessagesResponse, error) (*AdminService_MergeDLQMessages_Result, error) // UnwrapResponse takes the result struct for MergeDLQMessages // and returns the value or error returned by it. // // The error is non-nil only if MergeDLQMessages threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_MergeDLQMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_MergeDLQMessages_Result) (*replicator.MergeDLQMessagesResponse, error) }{} func init() { AdminService_MergeDLQMessages_Helper.Args = func( request *replicator.MergeDLQMessagesRequest, ) *AdminService_MergeDLQMessages_Args { return &AdminService_MergeDLQMessages_Args{ Request: request, } } AdminService_MergeDLQMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true default: return false } } AdminService_MergeDLQMessages_Helper.WrapResponse = func(success *replicator.MergeDLQMessagesResponse, err error) (*AdminService_MergeDLQMessages_Result, error) { if err == nil { return &AdminService_MergeDLQMessages_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_MergeDLQMessages_Result.BadRequestError") } return &AdminService_MergeDLQMessages_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_MergeDLQMessages_Result.InternalServiceError") } return &AdminService_MergeDLQMessages_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_MergeDLQMessages_Result.ServiceBusyError") } return &AdminService_MergeDLQMessages_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_MergeDLQMessages_Result.EntityNotExistError") } return &AdminService_MergeDLQMessages_Result{EntityNotExistError: e}, nil } return nil, err } AdminService_MergeDLQMessages_Helper.UnwrapResponse = func(result *AdminService_MergeDLQMessages_Result) (success *replicator.MergeDLQMessagesResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_MergeDLQMessages_Result represents the result of a AdminService.MergeDLQMessages function call. // // The result of a MergeDLQMessages execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_MergeDLQMessages_Result struct { // Value returned by MergeDLQMessages after a successful execution. Success *replicator.MergeDLQMessagesResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` } // ToWire translates a AdminService_MergeDLQMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_MergeDLQMessages_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_MergeDLQMessages_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _MergeDLQMessagesResponse_Read(w wire.Value) (*replicator.MergeDLQMessagesResponse, error) { var v replicator.MergeDLQMessagesResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_MergeDLQMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_MergeDLQMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_MergeDLQMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_MergeDLQMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _MergeDLQMessagesResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_MergeDLQMessages_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_MergeDLQMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_MergeDLQMessages_Result struct could not be encoded. func (v *AdminService_MergeDLQMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_MergeDLQMessages_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _MergeDLQMessagesResponse_Decode(sr stream.Reader) (*replicator.MergeDLQMessagesResponse, error) { var v replicator.MergeDLQMessagesResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_MergeDLQMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_MergeDLQMessages_Result struct could not be generated from the wire // representation. func (v *AdminService_MergeDLQMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _MergeDLQMessagesResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_MergeDLQMessages_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_MergeDLQMessages_Result // struct. func (v *AdminService_MergeDLQMessages_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } return fmt.Sprintf("AdminService_MergeDLQMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_MergeDLQMessages_Result match the // provided AdminService_MergeDLQMessages_Result. // // This function performs a deep comparison. func (v *AdminService_MergeDLQMessages_Result) Equals(rhs *AdminService_MergeDLQMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_MergeDLQMessages_Result. func (v *AdminService_MergeDLQMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_MergeDLQMessages_Result) GetSuccess() (o *replicator.MergeDLQMessagesResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_MergeDLQMessages_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_MergeDLQMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_MergeDLQMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_MergeDLQMessages_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_MergeDLQMessages_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_MergeDLQMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_MergeDLQMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *AdminService_MergeDLQMessages_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *AdminService_MergeDLQMessages_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "MergeDLQMessages" for this struct. func (v *AdminService_MergeDLQMessages_Result) MethodName() string { return "MergeDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_MergeDLQMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_PurgeDLQMessages_Args represents the arguments for the AdminService.PurgeDLQMessages function. // // The arguments for PurgeDLQMessages are sent and received over the wire as this struct. type AdminService_PurgeDLQMessages_Args struct { Request *replicator.PurgeDLQMessagesRequest `json:"request,omitempty"` } // ToWire translates a AdminService_PurgeDLQMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_PurgeDLQMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PurgeDLQMessagesRequest_Read(w wire.Value) (*replicator.PurgeDLQMessagesRequest, error) { var v replicator.PurgeDLQMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_PurgeDLQMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_PurgeDLQMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_PurgeDLQMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_PurgeDLQMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _PurgeDLQMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_PurgeDLQMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_PurgeDLQMessages_Args struct could not be encoded. func (v *AdminService_PurgeDLQMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PurgeDLQMessagesRequest_Decode(sr stream.Reader) (*replicator.PurgeDLQMessagesRequest, error) { var v replicator.PurgeDLQMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_PurgeDLQMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_PurgeDLQMessages_Args struct could not be generated from the wire // representation. func (v *AdminService_PurgeDLQMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _PurgeDLQMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_PurgeDLQMessages_Args // struct. func (v *AdminService_PurgeDLQMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_PurgeDLQMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_PurgeDLQMessages_Args match the // provided AdminService_PurgeDLQMessages_Args. // // This function performs a deep comparison. func (v *AdminService_PurgeDLQMessages_Args) Equals(rhs *AdminService_PurgeDLQMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_PurgeDLQMessages_Args. func (v *AdminService_PurgeDLQMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_PurgeDLQMessages_Args) GetRequest() (o *replicator.PurgeDLQMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_PurgeDLQMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "PurgeDLQMessages" for this struct. func (v *AdminService_PurgeDLQMessages_Args) MethodName() string { return "PurgeDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_PurgeDLQMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_PurgeDLQMessages_Helper provides functions that aid in handling the // parameters and return values of the AdminService.PurgeDLQMessages // function. var AdminService_PurgeDLQMessages_Helper = struct { // Args accepts the parameters of PurgeDLQMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.PurgeDLQMessagesRequest, ) *AdminService_PurgeDLQMessages_Args // IsException returns true if the given error can be thrown // by PurgeDLQMessages. // // An error can be thrown by PurgeDLQMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for PurgeDLQMessages // given the error returned by it. The provided error may // be nil if PurgeDLQMessages did not fail. // // This allows mapping errors returned by PurgeDLQMessages into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // PurgeDLQMessages // // err := PurgeDLQMessages(args) // result, err := AdminService_PurgeDLQMessages_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from PurgeDLQMessages: %v", err) // } // serialize(result) WrapResponse func(error) (*AdminService_PurgeDLQMessages_Result, error) // UnwrapResponse takes the result struct for PurgeDLQMessages // and returns the erorr returned by it (if any). // // The error is non-nil only if PurgeDLQMessages threw an // exception. // // result := deserialize(bytes) // err := AdminService_PurgeDLQMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_PurgeDLQMessages_Result) error }{} func init() { AdminService_PurgeDLQMessages_Helper.Args = func( request *replicator.PurgeDLQMessagesRequest, ) *AdminService_PurgeDLQMessages_Args { return &AdminService_PurgeDLQMessages_Args{ Request: request, } } AdminService_PurgeDLQMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true default: return false } } AdminService_PurgeDLQMessages_Helper.WrapResponse = func(err error) (*AdminService_PurgeDLQMessages_Result, error) { if err == nil { return &AdminService_PurgeDLQMessages_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_PurgeDLQMessages_Result.BadRequestError") } return &AdminService_PurgeDLQMessages_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_PurgeDLQMessages_Result.InternalServiceError") } return &AdminService_PurgeDLQMessages_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_PurgeDLQMessages_Result.ServiceBusyError") } return &AdminService_PurgeDLQMessages_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_PurgeDLQMessages_Result.EntityNotExistError") } return &AdminService_PurgeDLQMessages_Result{EntityNotExistError: e}, nil } return nil, err } AdminService_PurgeDLQMessages_Helper.UnwrapResponse = func(result *AdminService_PurgeDLQMessages_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } return } } // AdminService_PurgeDLQMessages_Result represents the result of a AdminService.PurgeDLQMessages function call. // // The result of a PurgeDLQMessages execution is sent and received over the wire as this struct. type AdminService_PurgeDLQMessages_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` } // ToWire translates a AdminService_PurgeDLQMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_PurgeDLQMessages_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("AdminService_PurgeDLQMessages_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminService_PurgeDLQMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_PurgeDLQMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_PurgeDLQMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_PurgeDLQMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_PurgeDLQMessages_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a AdminService_PurgeDLQMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_PurgeDLQMessages_Result struct could not be encoded. func (v *AdminService_PurgeDLQMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_PurgeDLQMessages_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a AdminService_PurgeDLQMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_PurgeDLQMessages_Result struct could not be generated from the wire // representation. func (v *AdminService_PurgeDLQMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_PurgeDLQMessages_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_PurgeDLQMessages_Result // struct. func (v *AdminService_PurgeDLQMessages_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } return fmt.Sprintf("AdminService_PurgeDLQMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_PurgeDLQMessages_Result match the // provided AdminService_PurgeDLQMessages_Result. // // This function performs a deep comparison. func (v *AdminService_PurgeDLQMessages_Result) Equals(rhs *AdminService_PurgeDLQMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_PurgeDLQMessages_Result. func (v *AdminService_PurgeDLQMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_PurgeDLQMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_PurgeDLQMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_PurgeDLQMessages_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_PurgeDLQMessages_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_PurgeDLQMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_PurgeDLQMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *AdminService_PurgeDLQMessages_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *AdminService_PurgeDLQMessages_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "PurgeDLQMessages" for this struct. func (v *AdminService_PurgeDLQMessages_Result) MethodName() string { return "PurgeDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_PurgeDLQMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_ReadDLQMessages_Args represents the arguments for the AdminService.ReadDLQMessages function. // // The arguments for ReadDLQMessages are sent and received over the wire as this struct. type AdminService_ReadDLQMessages_Args struct { Request *replicator.ReadDLQMessagesRequest `json:"request,omitempty"` } // ToWire translates a AdminService_ReadDLQMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_ReadDLQMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReadDLQMessagesRequest_Read(w wire.Value) (*replicator.ReadDLQMessagesRequest, error) { var v replicator.ReadDLQMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_ReadDLQMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_ReadDLQMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_ReadDLQMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_ReadDLQMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _ReadDLQMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_ReadDLQMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_ReadDLQMessages_Args struct could not be encoded. func (v *AdminService_ReadDLQMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReadDLQMessagesRequest_Decode(sr stream.Reader) (*replicator.ReadDLQMessagesRequest, error) { var v replicator.ReadDLQMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_ReadDLQMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_ReadDLQMessages_Args struct could not be generated from the wire // representation. func (v *AdminService_ReadDLQMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _ReadDLQMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_ReadDLQMessages_Args // struct. func (v *AdminService_ReadDLQMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_ReadDLQMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_ReadDLQMessages_Args match the // provided AdminService_ReadDLQMessages_Args. // // This function performs a deep comparison. func (v *AdminService_ReadDLQMessages_Args) Equals(rhs *AdminService_ReadDLQMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_ReadDLQMessages_Args. func (v *AdminService_ReadDLQMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_ReadDLQMessages_Args) GetRequest() (o *replicator.ReadDLQMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_ReadDLQMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ReadDLQMessages" for this struct. func (v *AdminService_ReadDLQMessages_Args) MethodName() string { return "ReadDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_ReadDLQMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_ReadDLQMessages_Helper provides functions that aid in handling the // parameters and return values of the AdminService.ReadDLQMessages // function. var AdminService_ReadDLQMessages_Helper = struct { // Args accepts the parameters of ReadDLQMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.ReadDLQMessagesRequest, ) *AdminService_ReadDLQMessages_Args // IsException returns true if the given error can be thrown // by ReadDLQMessages. // // An error can be thrown by ReadDLQMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ReadDLQMessages // given its return value and error. // // This allows mapping values and errors returned by // ReadDLQMessages into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ReadDLQMessages // // value, err := ReadDLQMessages(args) // result, err := AdminService_ReadDLQMessages_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ReadDLQMessages: %v", err) // } // serialize(result) WrapResponse func(*replicator.ReadDLQMessagesResponse, error) (*AdminService_ReadDLQMessages_Result, error) // UnwrapResponse takes the result struct for ReadDLQMessages // and returns the value or error returned by it. // // The error is non-nil only if ReadDLQMessages threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_ReadDLQMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_ReadDLQMessages_Result) (*replicator.ReadDLQMessagesResponse, error) }{} func init() { AdminService_ReadDLQMessages_Helper.Args = func( request *replicator.ReadDLQMessagesRequest, ) *AdminService_ReadDLQMessages_Args { return &AdminService_ReadDLQMessages_Args{ Request: request, } } AdminService_ReadDLQMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true default: return false } } AdminService_ReadDLQMessages_Helper.WrapResponse = func(success *replicator.ReadDLQMessagesResponse, err error) (*AdminService_ReadDLQMessages_Result, error) { if err == nil { return &AdminService_ReadDLQMessages_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ReadDLQMessages_Result.BadRequestError") } return &AdminService_ReadDLQMessages_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ReadDLQMessages_Result.InternalServiceError") } return &AdminService_ReadDLQMessages_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ReadDLQMessages_Result.ServiceBusyError") } return &AdminService_ReadDLQMessages_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ReadDLQMessages_Result.EntityNotExistError") } return &AdminService_ReadDLQMessages_Result{EntityNotExistError: e}, nil } return nil, err } AdminService_ReadDLQMessages_Helper.UnwrapResponse = func(result *AdminService_ReadDLQMessages_Result) (success *replicator.ReadDLQMessagesResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_ReadDLQMessages_Result represents the result of a AdminService.ReadDLQMessages function call. // // The result of a ReadDLQMessages execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_ReadDLQMessages_Result struct { // Value returned by ReadDLQMessages after a successful execution. Success *replicator.ReadDLQMessagesResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` } // ToWire translates a AdminService_ReadDLQMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_ReadDLQMessages_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_ReadDLQMessages_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReadDLQMessagesResponse_Read(w wire.Value) (*replicator.ReadDLQMessagesResponse, error) { var v replicator.ReadDLQMessagesResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_ReadDLQMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_ReadDLQMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_ReadDLQMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_ReadDLQMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ReadDLQMessagesResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_ReadDLQMessages_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_ReadDLQMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_ReadDLQMessages_Result struct could not be encoded. func (v *AdminService_ReadDLQMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_ReadDLQMessages_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ReadDLQMessagesResponse_Decode(sr stream.Reader) (*replicator.ReadDLQMessagesResponse, error) { var v replicator.ReadDLQMessagesResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_ReadDLQMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_ReadDLQMessages_Result struct could not be generated from the wire // representation. func (v *AdminService_ReadDLQMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ReadDLQMessagesResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_ReadDLQMessages_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_ReadDLQMessages_Result // struct. func (v *AdminService_ReadDLQMessages_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } return fmt.Sprintf("AdminService_ReadDLQMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_ReadDLQMessages_Result match the // provided AdminService_ReadDLQMessages_Result. // // This function performs a deep comparison. func (v *AdminService_ReadDLQMessages_Result) Equals(rhs *AdminService_ReadDLQMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_ReadDLQMessages_Result. func (v *AdminService_ReadDLQMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_ReadDLQMessages_Result) GetSuccess() (o *replicator.ReadDLQMessagesResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_ReadDLQMessages_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_ReadDLQMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_ReadDLQMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_ReadDLQMessages_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_ReadDLQMessages_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_ReadDLQMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_ReadDLQMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *AdminService_ReadDLQMessages_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *AdminService_ReadDLQMessages_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ReadDLQMessages" for this struct. func (v *AdminService_ReadDLQMessages_Result) MethodName() string { return "ReadDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_ReadDLQMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_ReapplyEvents_Args represents the arguments for the AdminService.ReapplyEvents function. // // The arguments for ReapplyEvents are sent and received over the wire as this struct. type AdminService_ReapplyEvents_Args struct { ReapplyEventsRequest *shared.ReapplyEventsRequest `json:"reapplyEventsRequest,omitempty"` } // ToWire translates a AdminService_ReapplyEvents_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_ReapplyEvents_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ReapplyEventsRequest != nil { w, err = v.ReapplyEventsRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReapplyEventsRequest_Read(w wire.Value) (*shared.ReapplyEventsRequest, error) { var v shared.ReapplyEventsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_ReapplyEvents_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_ReapplyEvents_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_ReapplyEvents_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_ReapplyEvents_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ReapplyEventsRequest, err = _ReapplyEventsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_ReapplyEvents_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_ReapplyEvents_Args struct could not be encoded. func (v *AdminService_ReapplyEvents_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ReapplyEventsRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ReapplyEventsRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReapplyEventsRequest_Decode(sr stream.Reader) (*shared.ReapplyEventsRequest, error) { var v shared.ReapplyEventsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_ReapplyEvents_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_ReapplyEvents_Args struct could not be generated from the wire // representation. func (v *AdminService_ReapplyEvents_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ReapplyEventsRequest, err = _ReapplyEventsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_ReapplyEvents_Args // struct. func (v *AdminService_ReapplyEvents_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ReapplyEventsRequest != nil { fields[i] = fmt.Sprintf("ReapplyEventsRequest: %v", v.ReapplyEventsRequest) i++ } return fmt.Sprintf("AdminService_ReapplyEvents_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_ReapplyEvents_Args match the // provided AdminService_ReapplyEvents_Args. // // This function performs a deep comparison. func (v *AdminService_ReapplyEvents_Args) Equals(rhs *AdminService_ReapplyEvents_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ReapplyEventsRequest == nil && rhs.ReapplyEventsRequest == nil) || (v.ReapplyEventsRequest != nil && rhs.ReapplyEventsRequest != nil && v.ReapplyEventsRequest.Equals(rhs.ReapplyEventsRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_ReapplyEvents_Args. func (v *AdminService_ReapplyEvents_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ReapplyEventsRequest != nil { err = multierr.Append(err, enc.AddObject("reapplyEventsRequest", v.ReapplyEventsRequest)) } return err } // GetReapplyEventsRequest returns the value of ReapplyEventsRequest if it is set or its // zero value if it is unset. func (v *AdminService_ReapplyEvents_Args) GetReapplyEventsRequest() (o *shared.ReapplyEventsRequest) { if v != nil && v.ReapplyEventsRequest != nil { return v.ReapplyEventsRequest } return } // IsSetReapplyEventsRequest returns true if ReapplyEventsRequest is not nil. func (v *AdminService_ReapplyEvents_Args) IsSetReapplyEventsRequest() bool { return v != nil && v.ReapplyEventsRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ReapplyEvents" for this struct. func (v *AdminService_ReapplyEvents_Args) MethodName() string { return "ReapplyEvents" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_ReapplyEvents_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_ReapplyEvents_Helper provides functions that aid in handling the // parameters and return values of the AdminService.ReapplyEvents // function. var AdminService_ReapplyEvents_Helper = struct { // Args accepts the parameters of ReapplyEvents in-order and returns // the arguments struct for the function. Args func( reapplyEventsRequest *shared.ReapplyEventsRequest, ) *AdminService_ReapplyEvents_Args // IsException returns true if the given error can be thrown // by ReapplyEvents. // // An error can be thrown by ReapplyEvents only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ReapplyEvents // given the error returned by it. The provided error may // be nil if ReapplyEvents did not fail. // // This allows mapping errors returned by ReapplyEvents into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // ReapplyEvents // // err := ReapplyEvents(args) // result, err := AdminService_ReapplyEvents_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from ReapplyEvents: %v", err) // } // serialize(result) WrapResponse func(error) (*AdminService_ReapplyEvents_Result, error) // UnwrapResponse takes the result struct for ReapplyEvents // and returns the erorr returned by it (if any). // // The error is non-nil only if ReapplyEvents threw an // exception. // // result := deserialize(bytes) // err := AdminService_ReapplyEvents_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_ReapplyEvents_Result) error }{} func init() { AdminService_ReapplyEvents_Helper.Args = func( reapplyEventsRequest *shared.ReapplyEventsRequest, ) *AdminService_ReapplyEvents_Args { return &AdminService_ReapplyEvents_Args{ ReapplyEventsRequest: reapplyEventsRequest, } } AdminService_ReapplyEvents_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true default: return false } } AdminService_ReapplyEvents_Helper.WrapResponse = func(err error) (*AdminService_ReapplyEvents_Result, error) { if err == nil { return &AdminService_ReapplyEvents_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ReapplyEvents_Result.BadRequestError") } return &AdminService_ReapplyEvents_Result{BadRequestError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ReapplyEvents_Result.DomainNotActiveError") } return &AdminService_ReapplyEvents_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ReapplyEvents_Result.LimitExceededError") } return &AdminService_ReapplyEvents_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ReapplyEvents_Result.ServiceBusyError") } return &AdminService_ReapplyEvents_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ReapplyEvents_Result.EntityNotExistError") } return &AdminService_ReapplyEvents_Result{EntityNotExistError: e}, nil } return nil, err } AdminService_ReapplyEvents_Helper.UnwrapResponse = func(result *AdminService_ReapplyEvents_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } return } } // AdminService_ReapplyEvents_Result represents the result of a AdminService.ReapplyEvents function call. // // The result of a ReapplyEvents execution is sent and received over the wire as this struct. type AdminService_ReapplyEvents_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` } // ToWire translates a AdminService_ReapplyEvents_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_ReapplyEvents_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("AdminService_ReapplyEvents_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DomainNotActiveError_Read(w wire.Value) (*shared.DomainNotActiveError, error) { var v shared.DomainNotActiveError err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_ReapplyEvents_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_ReapplyEvents_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_ReapplyEvents_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_ReapplyEvents_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_ReapplyEvents_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a AdminService_ReapplyEvents_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_ReapplyEvents_Result struct could not be encoded. func (v *AdminService_ReapplyEvents_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_ReapplyEvents_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _DomainNotActiveError_Decode(sr stream.Reader) (*shared.DomainNotActiveError, error) { var v shared.DomainNotActiveError err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_ReapplyEvents_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_ReapplyEvents_Result struct could not be generated from the wire // representation. func (v *AdminService_ReapplyEvents_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_ReapplyEvents_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_ReapplyEvents_Result // struct. func (v *AdminService_ReapplyEvents_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } return fmt.Sprintf("AdminService_ReapplyEvents_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_ReapplyEvents_Result match the // provided AdminService_ReapplyEvents_Result. // // This function performs a deep comparison. func (v *AdminService_ReapplyEvents_Result) Equals(rhs *AdminService_ReapplyEvents_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_ReapplyEvents_Result. func (v *AdminService_ReapplyEvents_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_ReapplyEvents_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_ReapplyEvents_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *AdminService_ReapplyEvents_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *AdminService_ReapplyEvents_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *AdminService_ReapplyEvents_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *AdminService_ReapplyEvents_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_ReapplyEvents_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_ReapplyEvents_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *AdminService_ReapplyEvents_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *AdminService_ReapplyEvents_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ReapplyEvents" for this struct. func (v *AdminService_ReapplyEvents_Result) MethodName() string { return "ReapplyEvents" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_ReapplyEvents_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_RefreshWorkflowTasks_Args represents the arguments for the AdminService.RefreshWorkflowTasks function. // // The arguments for RefreshWorkflowTasks are sent and received over the wire as this struct. type AdminService_RefreshWorkflowTasks_Args struct { Request *shared.RefreshWorkflowTasksRequest `json:"request,omitempty"` } // ToWire translates a AdminService_RefreshWorkflowTasks_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_RefreshWorkflowTasks_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RefreshWorkflowTasksRequest_Read(w wire.Value) (*shared.RefreshWorkflowTasksRequest, error) { var v shared.RefreshWorkflowTasksRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_RefreshWorkflowTasks_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_RefreshWorkflowTasks_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_RefreshWorkflowTasks_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_RefreshWorkflowTasks_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _RefreshWorkflowTasksRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_RefreshWorkflowTasks_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_RefreshWorkflowTasks_Args struct could not be encoded. func (v *AdminService_RefreshWorkflowTasks_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RefreshWorkflowTasksRequest_Decode(sr stream.Reader) (*shared.RefreshWorkflowTasksRequest, error) { var v shared.RefreshWorkflowTasksRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_RefreshWorkflowTasks_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_RefreshWorkflowTasks_Args struct could not be generated from the wire // representation. func (v *AdminService_RefreshWorkflowTasks_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _RefreshWorkflowTasksRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_RefreshWorkflowTasks_Args // struct. func (v *AdminService_RefreshWorkflowTasks_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_RefreshWorkflowTasks_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_RefreshWorkflowTasks_Args match the // provided AdminService_RefreshWorkflowTasks_Args. // // This function performs a deep comparison. func (v *AdminService_RefreshWorkflowTasks_Args) Equals(rhs *AdminService_RefreshWorkflowTasks_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_RefreshWorkflowTasks_Args. func (v *AdminService_RefreshWorkflowTasks_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_RefreshWorkflowTasks_Args) GetRequest() (o *shared.RefreshWorkflowTasksRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_RefreshWorkflowTasks_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RefreshWorkflowTasks" for this struct. func (v *AdminService_RefreshWorkflowTasks_Args) MethodName() string { return "RefreshWorkflowTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_RefreshWorkflowTasks_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_RefreshWorkflowTasks_Helper provides functions that aid in handling the // parameters and return values of the AdminService.RefreshWorkflowTasks // function. var AdminService_RefreshWorkflowTasks_Helper = struct { // Args accepts the parameters of RefreshWorkflowTasks in-order and returns // the arguments struct for the function. Args func( request *shared.RefreshWorkflowTasksRequest, ) *AdminService_RefreshWorkflowTasks_Args // IsException returns true if the given error can be thrown // by RefreshWorkflowTasks. // // An error can be thrown by RefreshWorkflowTasks only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RefreshWorkflowTasks // given the error returned by it. The provided error may // be nil if RefreshWorkflowTasks did not fail. // // This allows mapping errors returned by RefreshWorkflowTasks into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RefreshWorkflowTasks // // err := RefreshWorkflowTasks(args) // result, err := AdminService_RefreshWorkflowTasks_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RefreshWorkflowTasks: %v", err) // } // serialize(result) WrapResponse func(error) (*AdminService_RefreshWorkflowTasks_Result, error) // UnwrapResponse takes the result struct for RefreshWorkflowTasks // and returns the erorr returned by it (if any). // // The error is non-nil only if RefreshWorkflowTasks threw an // exception. // // result := deserialize(bytes) // err := AdminService_RefreshWorkflowTasks_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_RefreshWorkflowTasks_Result) error }{} func init() { AdminService_RefreshWorkflowTasks_Helper.Args = func( request *shared.RefreshWorkflowTasksRequest, ) *AdminService_RefreshWorkflowTasks_Args { return &AdminService_RefreshWorkflowTasks_Args{ Request: request, } } AdminService_RefreshWorkflowTasks_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.DomainNotActiveError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true default: return false } } AdminService_RefreshWorkflowTasks_Helper.WrapResponse = func(err error) (*AdminService_RefreshWorkflowTasks_Result, error) { if err == nil { return &AdminService_RefreshWorkflowTasks_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RefreshWorkflowTasks_Result.BadRequestError") } return &AdminService_RefreshWorkflowTasks_Result{BadRequestError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RefreshWorkflowTasks_Result.DomainNotActiveError") } return &AdminService_RefreshWorkflowTasks_Result{DomainNotActiveError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RefreshWorkflowTasks_Result.ServiceBusyError") } return &AdminService_RefreshWorkflowTasks_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RefreshWorkflowTasks_Result.EntityNotExistError") } return &AdminService_RefreshWorkflowTasks_Result{EntityNotExistError: e}, nil } return nil, err } AdminService_RefreshWorkflowTasks_Helper.UnwrapResponse = func(result *AdminService_RefreshWorkflowTasks_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } return } } // AdminService_RefreshWorkflowTasks_Result represents the result of a AdminService.RefreshWorkflowTasks function call. // // The result of a RefreshWorkflowTasks execution is sent and received over the wire as this struct. type AdminService_RefreshWorkflowTasks_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` } // ToWire translates a AdminService_RefreshWorkflowTasks_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_RefreshWorkflowTasks_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("AdminService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminService_RefreshWorkflowTasks_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_RefreshWorkflowTasks_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_RefreshWorkflowTasks_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_RefreshWorkflowTasks_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a AdminService_RefreshWorkflowTasks_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_RefreshWorkflowTasks_Result struct could not be encoded. func (v *AdminService_RefreshWorkflowTasks_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a AdminService_RefreshWorkflowTasks_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_RefreshWorkflowTasks_Result struct could not be generated from the wire // representation. func (v *AdminService_RefreshWorkflowTasks_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_RefreshWorkflowTasks_Result // struct. func (v *AdminService_RefreshWorkflowTasks_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } return fmt.Sprintf("AdminService_RefreshWorkflowTasks_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_RefreshWorkflowTasks_Result match the // provided AdminService_RefreshWorkflowTasks_Result. // // This function performs a deep comparison. func (v *AdminService_RefreshWorkflowTasks_Result) Equals(rhs *AdminService_RefreshWorkflowTasks_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_RefreshWorkflowTasks_Result. func (v *AdminService_RefreshWorkflowTasks_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_RefreshWorkflowTasks_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_RefreshWorkflowTasks_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *AdminService_RefreshWorkflowTasks_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *AdminService_RefreshWorkflowTasks_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_RefreshWorkflowTasks_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_RefreshWorkflowTasks_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *AdminService_RefreshWorkflowTasks_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *AdminService_RefreshWorkflowTasks_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RefreshWorkflowTasks" for this struct. func (v *AdminService_RefreshWorkflowTasks_Result) MethodName() string { return "RefreshWorkflowTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_RefreshWorkflowTasks_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_RemoveTask_Args represents the arguments for the AdminService.RemoveTask function. // // The arguments for RemoveTask are sent and received over the wire as this struct. type AdminService_RemoveTask_Args struct { Request *shared.RemoveTaskRequest `json:"request,omitempty"` } // ToWire translates a AdminService_RemoveTask_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_RemoveTask_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RemoveTaskRequest_Read(w wire.Value) (*shared.RemoveTaskRequest, error) { var v shared.RemoveTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_RemoveTask_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_RemoveTask_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_RemoveTask_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_RemoveTask_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _RemoveTaskRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_RemoveTask_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_RemoveTask_Args struct could not be encoded. func (v *AdminService_RemoveTask_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RemoveTaskRequest_Decode(sr stream.Reader) (*shared.RemoveTaskRequest, error) { var v shared.RemoveTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_RemoveTask_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_RemoveTask_Args struct could not be generated from the wire // representation. func (v *AdminService_RemoveTask_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _RemoveTaskRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_RemoveTask_Args // struct. func (v *AdminService_RemoveTask_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_RemoveTask_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_RemoveTask_Args match the // provided AdminService_RemoveTask_Args. // // This function performs a deep comparison. func (v *AdminService_RemoveTask_Args) Equals(rhs *AdminService_RemoveTask_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_RemoveTask_Args. func (v *AdminService_RemoveTask_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_RemoveTask_Args) GetRequest() (o *shared.RemoveTaskRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_RemoveTask_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RemoveTask" for this struct. func (v *AdminService_RemoveTask_Args) MethodName() string { return "RemoveTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_RemoveTask_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_RemoveTask_Helper provides functions that aid in handling the // parameters and return values of the AdminService.RemoveTask // function. var AdminService_RemoveTask_Helper = struct { // Args accepts the parameters of RemoveTask in-order and returns // the arguments struct for the function. Args func( request *shared.RemoveTaskRequest, ) *AdminService_RemoveTask_Args // IsException returns true if the given error can be thrown // by RemoveTask. // // An error can be thrown by RemoveTask only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RemoveTask // given the error returned by it. The provided error may // be nil if RemoveTask did not fail. // // This allows mapping errors returned by RemoveTask into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RemoveTask // // err := RemoveTask(args) // result, err := AdminService_RemoveTask_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RemoveTask: %v", err) // } // serialize(result) WrapResponse func(error) (*AdminService_RemoveTask_Result, error) // UnwrapResponse takes the result struct for RemoveTask // and returns the erorr returned by it (if any). // // The error is non-nil only if RemoveTask threw an // exception. // // result := deserialize(bytes) // err := AdminService_RemoveTask_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_RemoveTask_Result) error }{} func init() { AdminService_RemoveTask_Helper.Args = func( request *shared.RemoveTaskRequest, ) *AdminService_RemoveTask_Args { return &AdminService_RemoveTask_Args{ Request: request, } } AdminService_RemoveTask_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.AccessDeniedError: return true default: return false } } AdminService_RemoveTask_Helper.WrapResponse = func(err error) (*AdminService_RemoveTask_Result, error) { if err == nil { return &AdminService_RemoveTask_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RemoveTask_Result.BadRequestError") } return &AdminService_RemoveTask_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RemoveTask_Result.InternalServiceError") } return &AdminService_RemoveTask_Result{InternalServiceError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RemoveTask_Result.AccessDeniedError") } return &AdminService_RemoveTask_Result{AccessDeniedError: e}, nil } return nil, err } AdminService_RemoveTask_Helper.UnwrapResponse = func(result *AdminService_RemoveTask_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // AdminService_RemoveTask_Result represents the result of a AdminService.RemoveTask function call. // // The result of a RemoveTask execution is sent and received over the wire as this struct. type AdminService_RemoveTask_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a AdminService_RemoveTask_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_RemoveTask_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("AdminService_RemoveTask_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminService_RemoveTask_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_RemoveTask_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_RemoveTask_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_RemoveTask_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_RemoveTask_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a AdminService_RemoveTask_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_RemoveTask_Result struct could not be encoded. func (v *AdminService_RemoveTask_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_RemoveTask_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a AdminService_RemoveTask_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_RemoveTask_Result struct could not be generated from the wire // representation. func (v *AdminService_RemoveTask_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_RemoveTask_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_RemoveTask_Result // struct. func (v *AdminService_RemoveTask_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("AdminService_RemoveTask_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_RemoveTask_Result match the // provided AdminService_RemoveTask_Result. // // This function performs a deep comparison. func (v *AdminService_RemoveTask_Result) Equals(rhs *AdminService_RemoveTask_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_RemoveTask_Result. func (v *AdminService_RemoveTask_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_RemoveTask_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_RemoveTask_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_RemoveTask_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_RemoveTask_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *AdminService_RemoveTask_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *AdminService_RemoveTask_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RemoveTask" for this struct. func (v *AdminService_RemoveTask_Result) MethodName() string { return "RemoveTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_RemoveTask_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_ResendReplicationTasks_Args represents the arguments for the AdminService.ResendReplicationTasks function. // // The arguments for ResendReplicationTasks are sent and received over the wire as this struct. type AdminService_ResendReplicationTasks_Args struct { Request *ResendReplicationTasksRequest `json:"request,omitempty"` } // ToWire translates a AdminService_ResendReplicationTasks_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_ResendReplicationTasks_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResendReplicationTasksRequest_Read(w wire.Value) (*ResendReplicationTasksRequest, error) { var v ResendReplicationTasksRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_ResendReplicationTasks_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_ResendReplicationTasks_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_ResendReplicationTasks_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_ResendReplicationTasks_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _ResendReplicationTasksRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_ResendReplicationTasks_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_ResendReplicationTasks_Args struct could not be encoded. func (v *AdminService_ResendReplicationTasks_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ResendReplicationTasksRequest_Decode(sr stream.Reader) (*ResendReplicationTasksRequest, error) { var v ResendReplicationTasksRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_ResendReplicationTasks_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_ResendReplicationTasks_Args struct could not be generated from the wire // representation. func (v *AdminService_ResendReplicationTasks_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _ResendReplicationTasksRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_ResendReplicationTasks_Args // struct. func (v *AdminService_ResendReplicationTasks_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_ResendReplicationTasks_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_ResendReplicationTasks_Args match the // provided AdminService_ResendReplicationTasks_Args. // // This function performs a deep comparison. func (v *AdminService_ResendReplicationTasks_Args) Equals(rhs *AdminService_ResendReplicationTasks_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_ResendReplicationTasks_Args. func (v *AdminService_ResendReplicationTasks_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_ResendReplicationTasks_Args) GetRequest() (o *ResendReplicationTasksRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_ResendReplicationTasks_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ResendReplicationTasks" for this struct. func (v *AdminService_ResendReplicationTasks_Args) MethodName() string { return "ResendReplicationTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_ResendReplicationTasks_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_ResendReplicationTasks_Helper provides functions that aid in handling the // parameters and return values of the AdminService.ResendReplicationTasks // function. var AdminService_ResendReplicationTasks_Helper = struct { // Args accepts the parameters of ResendReplicationTasks in-order and returns // the arguments struct for the function. Args func( request *ResendReplicationTasksRequest, ) *AdminService_ResendReplicationTasks_Args // IsException returns true if the given error can be thrown // by ResendReplicationTasks. // // An error can be thrown by ResendReplicationTasks only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ResendReplicationTasks // given the error returned by it. The provided error may // be nil if ResendReplicationTasks did not fail. // // This allows mapping errors returned by ResendReplicationTasks into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // ResendReplicationTasks // // err := ResendReplicationTasks(args) // result, err := AdminService_ResendReplicationTasks_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from ResendReplicationTasks: %v", err) // } // serialize(result) WrapResponse func(error) (*AdminService_ResendReplicationTasks_Result, error) // UnwrapResponse takes the result struct for ResendReplicationTasks // and returns the erorr returned by it (if any). // // The error is non-nil only if ResendReplicationTasks threw an // exception. // // result := deserialize(bytes) // err := AdminService_ResendReplicationTasks_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_ResendReplicationTasks_Result) error }{} func init() { AdminService_ResendReplicationTasks_Helper.Args = func( request *ResendReplicationTasksRequest, ) *AdminService_ResendReplicationTasks_Args { return &AdminService_ResendReplicationTasks_Args{ Request: request, } } AdminService_ResendReplicationTasks_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true default: return false } } AdminService_ResendReplicationTasks_Helper.WrapResponse = func(err error) (*AdminService_ResendReplicationTasks_Result, error) { if err == nil { return &AdminService_ResendReplicationTasks_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ResendReplicationTasks_Result.BadRequestError") } return &AdminService_ResendReplicationTasks_Result{BadRequestError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ResendReplicationTasks_Result.ServiceBusyError") } return &AdminService_ResendReplicationTasks_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ResendReplicationTasks_Result.EntityNotExistError") } return &AdminService_ResendReplicationTasks_Result{EntityNotExistError: e}, nil } return nil, err } AdminService_ResendReplicationTasks_Helper.UnwrapResponse = func(result *AdminService_ResendReplicationTasks_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } return } } // AdminService_ResendReplicationTasks_Result represents the result of a AdminService.ResendReplicationTasks function call. // // The result of a ResendReplicationTasks execution is sent and received over the wire as this struct. type AdminService_ResendReplicationTasks_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` } // ToWire translates a AdminService_ResendReplicationTasks_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_ResendReplicationTasks_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("AdminService_ResendReplicationTasks_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminService_ResendReplicationTasks_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_ResendReplicationTasks_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_ResendReplicationTasks_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_ResendReplicationTasks_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_ResendReplicationTasks_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a AdminService_ResendReplicationTasks_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_ResendReplicationTasks_Result struct could not be encoded. func (v *AdminService_ResendReplicationTasks_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_ResendReplicationTasks_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a AdminService_ResendReplicationTasks_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_ResendReplicationTasks_Result struct could not be generated from the wire // representation. func (v *AdminService_ResendReplicationTasks_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_ResendReplicationTasks_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_ResendReplicationTasks_Result // struct. func (v *AdminService_ResendReplicationTasks_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } return fmt.Sprintf("AdminService_ResendReplicationTasks_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_ResendReplicationTasks_Result match the // provided AdminService_ResendReplicationTasks_Result. // // This function performs a deep comparison. func (v *AdminService_ResendReplicationTasks_Result) Equals(rhs *AdminService_ResendReplicationTasks_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_ResendReplicationTasks_Result. func (v *AdminService_ResendReplicationTasks_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_ResendReplicationTasks_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_ResendReplicationTasks_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_ResendReplicationTasks_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_ResendReplicationTasks_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *AdminService_ResendReplicationTasks_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *AdminService_ResendReplicationTasks_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ResendReplicationTasks" for this struct. func (v *AdminService_ResendReplicationTasks_Result) MethodName() string { return "ResendReplicationTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_ResendReplicationTasks_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_ResetQueue_Args represents the arguments for the AdminService.ResetQueue function. // // The arguments for ResetQueue are sent and received over the wire as this struct. type AdminService_ResetQueue_Args struct { Request *shared.ResetQueueRequest `json:"request,omitempty"` } // ToWire translates a AdminService_ResetQueue_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_ResetQueue_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetQueueRequest_Read(w wire.Value) (*shared.ResetQueueRequest, error) { var v shared.ResetQueueRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_ResetQueue_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_ResetQueue_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_ResetQueue_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_ResetQueue_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _ResetQueueRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_ResetQueue_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_ResetQueue_Args struct could not be encoded. func (v *AdminService_ResetQueue_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ResetQueueRequest_Decode(sr stream.Reader) (*shared.ResetQueueRequest, error) { var v shared.ResetQueueRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_ResetQueue_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_ResetQueue_Args struct could not be generated from the wire // representation. func (v *AdminService_ResetQueue_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _ResetQueueRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_ResetQueue_Args // struct. func (v *AdminService_ResetQueue_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_ResetQueue_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_ResetQueue_Args match the // provided AdminService_ResetQueue_Args. // // This function performs a deep comparison. func (v *AdminService_ResetQueue_Args) Equals(rhs *AdminService_ResetQueue_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_ResetQueue_Args. func (v *AdminService_ResetQueue_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_ResetQueue_Args) GetRequest() (o *shared.ResetQueueRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_ResetQueue_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ResetQueue" for this struct. func (v *AdminService_ResetQueue_Args) MethodName() string { return "ResetQueue" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_ResetQueue_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_ResetQueue_Helper provides functions that aid in handling the // parameters and return values of the AdminService.ResetQueue // function. var AdminService_ResetQueue_Helper = struct { // Args accepts the parameters of ResetQueue in-order and returns // the arguments struct for the function. Args func( request *shared.ResetQueueRequest, ) *AdminService_ResetQueue_Args // IsException returns true if the given error can be thrown // by ResetQueue. // // An error can be thrown by ResetQueue only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ResetQueue // given the error returned by it. The provided error may // be nil if ResetQueue did not fail. // // This allows mapping errors returned by ResetQueue into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // ResetQueue // // err := ResetQueue(args) // result, err := AdminService_ResetQueue_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from ResetQueue: %v", err) // } // serialize(result) WrapResponse func(error) (*AdminService_ResetQueue_Result, error) // UnwrapResponse takes the result struct for ResetQueue // and returns the erorr returned by it (if any). // // The error is non-nil only if ResetQueue threw an // exception. // // result := deserialize(bytes) // err := AdminService_ResetQueue_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_ResetQueue_Result) error }{} func init() { AdminService_ResetQueue_Helper.Args = func( request *shared.ResetQueueRequest, ) *AdminService_ResetQueue_Args { return &AdminService_ResetQueue_Args{ Request: request, } } AdminService_ResetQueue_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.AccessDeniedError: return true default: return false } } AdminService_ResetQueue_Helper.WrapResponse = func(err error) (*AdminService_ResetQueue_Result, error) { if err == nil { return &AdminService_ResetQueue_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ResetQueue_Result.BadRequestError") } return &AdminService_ResetQueue_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ResetQueue_Result.InternalServiceError") } return &AdminService_ResetQueue_Result{InternalServiceError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_ResetQueue_Result.AccessDeniedError") } return &AdminService_ResetQueue_Result{AccessDeniedError: e}, nil } return nil, err } AdminService_ResetQueue_Helper.UnwrapResponse = func(result *AdminService_ResetQueue_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // AdminService_ResetQueue_Result represents the result of a AdminService.ResetQueue function call. // // The result of a ResetQueue execution is sent and received over the wire as this struct. type AdminService_ResetQueue_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a AdminService_ResetQueue_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_ResetQueue_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("AdminService_ResetQueue_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminService_ResetQueue_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_ResetQueue_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_ResetQueue_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_ResetQueue_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_ResetQueue_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a AdminService_ResetQueue_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_ResetQueue_Result struct could not be encoded. func (v *AdminService_ResetQueue_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_ResetQueue_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a AdminService_ResetQueue_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_ResetQueue_Result struct could not be generated from the wire // representation. func (v *AdminService_ResetQueue_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_ResetQueue_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_ResetQueue_Result // struct. func (v *AdminService_ResetQueue_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("AdminService_ResetQueue_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_ResetQueue_Result match the // provided AdminService_ResetQueue_Result. // // This function performs a deep comparison. func (v *AdminService_ResetQueue_Result) Equals(rhs *AdminService_ResetQueue_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_ResetQueue_Result. func (v *AdminService_ResetQueue_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_ResetQueue_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_ResetQueue_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_ResetQueue_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_ResetQueue_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *AdminService_ResetQueue_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *AdminService_ResetQueue_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ResetQueue" for this struct. func (v *AdminService_ResetQueue_Result) MethodName() string { return "ResetQueue" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_ResetQueue_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_RespondCrossClusterTasksCompleted_Args represents the arguments for the AdminService.RespondCrossClusterTasksCompleted function. // // The arguments for RespondCrossClusterTasksCompleted are sent and received over the wire as this struct. type AdminService_RespondCrossClusterTasksCompleted_Args struct { Request *shared.RespondCrossClusterTasksCompletedRequest `json:"request,omitempty"` } // ToWire translates a AdminService_RespondCrossClusterTasksCompleted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_RespondCrossClusterTasksCompleted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondCrossClusterTasksCompletedRequest_Read(w wire.Value) (*shared.RespondCrossClusterTasksCompletedRequest, error) { var v shared.RespondCrossClusterTasksCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_RespondCrossClusterTasksCompleted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_RespondCrossClusterTasksCompleted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_RespondCrossClusterTasksCompleted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_RespondCrossClusterTasksCompleted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _RespondCrossClusterTasksCompletedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_RespondCrossClusterTasksCompleted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_RespondCrossClusterTasksCompleted_Args struct could not be encoded. func (v *AdminService_RespondCrossClusterTasksCompleted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondCrossClusterTasksCompletedRequest_Decode(sr stream.Reader) (*shared.RespondCrossClusterTasksCompletedRequest, error) { var v shared.RespondCrossClusterTasksCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_RespondCrossClusterTasksCompleted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_RespondCrossClusterTasksCompleted_Args struct could not be generated from the wire // representation. func (v *AdminService_RespondCrossClusterTasksCompleted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _RespondCrossClusterTasksCompletedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_RespondCrossClusterTasksCompleted_Args // struct. func (v *AdminService_RespondCrossClusterTasksCompleted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_RespondCrossClusterTasksCompleted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_RespondCrossClusterTasksCompleted_Args match the // provided AdminService_RespondCrossClusterTasksCompleted_Args. // // This function performs a deep comparison. func (v *AdminService_RespondCrossClusterTasksCompleted_Args) Equals(rhs *AdminService_RespondCrossClusterTasksCompleted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_RespondCrossClusterTasksCompleted_Args. func (v *AdminService_RespondCrossClusterTasksCompleted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_RespondCrossClusterTasksCompleted_Args) GetRequest() (o *shared.RespondCrossClusterTasksCompletedRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_RespondCrossClusterTasksCompleted_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondCrossClusterTasksCompleted" for this struct. func (v *AdminService_RespondCrossClusterTasksCompleted_Args) MethodName() string { return "RespondCrossClusterTasksCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_RespondCrossClusterTasksCompleted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_RespondCrossClusterTasksCompleted_Helper provides functions that aid in handling the // parameters and return values of the AdminService.RespondCrossClusterTasksCompleted // function. var AdminService_RespondCrossClusterTasksCompleted_Helper = struct { // Args accepts the parameters of RespondCrossClusterTasksCompleted in-order and returns // the arguments struct for the function. Args func( request *shared.RespondCrossClusterTasksCompletedRequest, ) *AdminService_RespondCrossClusterTasksCompleted_Args // IsException returns true if the given error can be thrown // by RespondCrossClusterTasksCompleted. // // An error can be thrown by RespondCrossClusterTasksCompleted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondCrossClusterTasksCompleted // given its return value and error. // // This allows mapping values and errors returned by // RespondCrossClusterTasksCompleted into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RespondCrossClusterTasksCompleted // // value, err := RespondCrossClusterTasksCompleted(args) // result, err := AdminService_RespondCrossClusterTasksCompleted_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RespondCrossClusterTasksCompleted: %v", err) // } // serialize(result) WrapResponse func(*shared.RespondCrossClusterTasksCompletedResponse, error) (*AdminService_RespondCrossClusterTasksCompleted_Result, error) // UnwrapResponse takes the result struct for RespondCrossClusterTasksCompleted // and returns the value or error returned by it. // // The error is non-nil only if RespondCrossClusterTasksCompleted threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_RespondCrossClusterTasksCompleted_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_RespondCrossClusterTasksCompleted_Result) (*shared.RespondCrossClusterTasksCompletedResponse, error) }{} func init() { AdminService_RespondCrossClusterTasksCompleted_Helper.Args = func( request *shared.RespondCrossClusterTasksCompletedRequest, ) *AdminService_RespondCrossClusterTasksCompleted_Args { return &AdminService_RespondCrossClusterTasksCompleted_Args{ Request: request, } } AdminService_RespondCrossClusterTasksCompleted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true default: return false } } AdminService_RespondCrossClusterTasksCompleted_Helper.WrapResponse = func(success *shared.RespondCrossClusterTasksCompletedResponse, err error) (*AdminService_RespondCrossClusterTasksCompleted_Result, error) { if err == nil { return &AdminService_RespondCrossClusterTasksCompleted_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RespondCrossClusterTasksCompleted_Result.BadRequestError") } return &AdminService_RespondCrossClusterTasksCompleted_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RespondCrossClusterTasksCompleted_Result.InternalServiceError") } return &AdminService_RespondCrossClusterTasksCompleted_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RespondCrossClusterTasksCompleted_Result.ServiceBusyError") } return &AdminService_RespondCrossClusterTasksCompleted_Result{ServiceBusyError: e}, nil } return nil, err } AdminService_RespondCrossClusterTasksCompleted_Helper.UnwrapResponse = func(result *AdminService_RespondCrossClusterTasksCompleted_Result) (success *shared.RespondCrossClusterTasksCompletedResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_RespondCrossClusterTasksCompleted_Result represents the result of a AdminService.RespondCrossClusterTasksCompleted function call. // // The result of a RespondCrossClusterTasksCompleted execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_RespondCrossClusterTasksCompleted_Result struct { // Value returned by RespondCrossClusterTasksCompleted after a successful execution. Success *shared.RespondCrossClusterTasksCompletedResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a AdminService_RespondCrossClusterTasksCompleted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_RespondCrossClusterTasksCompleted_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_RespondCrossClusterTasksCompleted_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondCrossClusterTasksCompletedResponse_Read(w wire.Value) (*shared.RespondCrossClusterTasksCompletedResponse, error) { var v shared.RespondCrossClusterTasksCompletedResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_RespondCrossClusterTasksCompleted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_RespondCrossClusterTasksCompleted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_RespondCrossClusterTasksCompleted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_RespondCrossClusterTasksCompleted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RespondCrossClusterTasksCompletedResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_RespondCrossClusterTasksCompleted_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_RespondCrossClusterTasksCompleted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_RespondCrossClusterTasksCompleted_Result struct could not be encoded. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_RespondCrossClusterTasksCompleted_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _RespondCrossClusterTasksCompletedResponse_Decode(sr stream.Reader) (*shared.RespondCrossClusterTasksCompletedResponse, error) { var v shared.RespondCrossClusterTasksCompletedResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_RespondCrossClusterTasksCompleted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_RespondCrossClusterTasksCompleted_Result struct could not be generated from the wire // representation. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RespondCrossClusterTasksCompletedResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_RespondCrossClusterTasksCompleted_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_RespondCrossClusterTasksCompleted_Result // struct. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("AdminService_RespondCrossClusterTasksCompleted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_RespondCrossClusterTasksCompleted_Result match the // provided AdminService_RespondCrossClusterTasksCompleted_Result. // // This function performs a deep comparison. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) Equals(rhs *AdminService_RespondCrossClusterTasksCompleted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_RespondCrossClusterTasksCompleted_Result. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) GetSuccess() (o *shared.RespondCrossClusterTasksCompletedResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondCrossClusterTasksCompleted" for this struct. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) MethodName() string { return "RespondCrossClusterTasksCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_RespondCrossClusterTasksCompleted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_RestoreDynamicConfig_Args represents the arguments for the AdminService.RestoreDynamicConfig function. // // The arguments for RestoreDynamicConfig are sent and received over the wire as this struct. type AdminService_RestoreDynamicConfig_Args struct { Request *RestoreDynamicConfigRequest `json:"request,omitempty"` } // ToWire translates a AdminService_RestoreDynamicConfig_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_RestoreDynamicConfig_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RestoreDynamicConfigRequest_Read(w wire.Value) (*RestoreDynamicConfigRequest, error) { var v RestoreDynamicConfigRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_RestoreDynamicConfig_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_RestoreDynamicConfig_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_RestoreDynamicConfig_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_RestoreDynamicConfig_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _RestoreDynamicConfigRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_RestoreDynamicConfig_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_RestoreDynamicConfig_Args struct could not be encoded. func (v *AdminService_RestoreDynamicConfig_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RestoreDynamicConfigRequest_Decode(sr stream.Reader) (*RestoreDynamicConfigRequest, error) { var v RestoreDynamicConfigRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_RestoreDynamicConfig_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_RestoreDynamicConfig_Args struct could not be generated from the wire // representation. func (v *AdminService_RestoreDynamicConfig_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _RestoreDynamicConfigRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_RestoreDynamicConfig_Args // struct. func (v *AdminService_RestoreDynamicConfig_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_RestoreDynamicConfig_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_RestoreDynamicConfig_Args match the // provided AdminService_RestoreDynamicConfig_Args. // // This function performs a deep comparison. func (v *AdminService_RestoreDynamicConfig_Args) Equals(rhs *AdminService_RestoreDynamicConfig_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_RestoreDynamicConfig_Args. func (v *AdminService_RestoreDynamicConfig_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_RestoreDynamicConfig_Args) GetRequest() (o *RestoreDynamicConfigRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_RestoreDynamicConfig_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RestoreDynamicConfig" for this struct. func (v *AdminService_RestoreDynamicConfig_Args) MethodName() string { return "RestoreDynamicConfig" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_RestoreDynamicConfig_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_RestoreDynamicConfig_Helper provides functions that aid in handling the // parameters and return values of the AdminService.RestoreDynamicConfig // function. var AdminService_RestoreDynamicConfig_Helper = struct { // Args accepts the parameters of RestoreDynamicConfig in-order and returns // the arguments struct for the function. Args func( request *RestoreDynamicConfigRequest, ) *AdminService_RestoreDynamicConfig_Args // IsException returns true if the given error can be thrown // by RestoreDynamicConfig. // // An error can be thrown by RestoreDynamicConfig only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RestoreDynamicConfig // given the error returned by it. The provided error may // be nil if RestoreDynamicConfig did not fail. // // This allows mapping errors returned by RestoreDynamicConfig into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RestoreDynamicConfig // // err := RestoreDynamicConfig(args) // result, err := AdminService_RestoreDynamicConfig_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RestoreDynamicConfig: %v", err) // } // serialize(result) WrapResponse func(error) (*AdminService_RestoreDynamicConfig_Result, error) // UnwrapResponse takes the result struct for RestoreDynamicConfig // and returns the erorr returned by it (if any). // // The error is non-nil only if RestoreDynamicConfig threw an // exception. // // result := deserialize(bytes) // err := AdminService_RestoreDynamicConfig_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_RestoreDynamicConfig_Result) error }{} func init() { AdminService_RestoreDynamicConfig_Helper.Args = func( request *RestoreDynamicConfigRequest, ) *AdminService_RestoreDynamicConfig_Args { return &AdminService_RestoreDynamicConfig_Args{ Request: request, } } AdminService_RestoreDynamicConfig_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true default: return false } } AdminService_RestoreDynamicConfig_Helper.WrapResponse = func(err error) (*AdminService_RestoreDynamicConfig_Result, error) { if err == nil { return &AdminService_RestoreDynamicConfig_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RestoreDynamicConfig_Result.BadRequestError") } return &AdminService_RestoreDynamicConfig_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_RestoreDynamicConfig_Result.InternalServiceError") } return &AdminService_RestoreDynamicConfig_Result{InternalServiceError: e}, nil } return nil, err } AdminService_RestoreDynamicConfig_Helper.UnwrapResponse = func(result *AdminService_RestoreDynamicConfig_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } return } } // AdminService_RestoreDynamicConfig_Result represents the result of a AdminService.RestoreDynamicConfig function call. // // The result of a RestoreDynamicConfig execution is sent and received over the wire as this struct. type AdminService_RestoreDynamicConfig_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` } // ToWire translates a AdminService_RestoreDynamicConfig_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_RestoreDynamicConfig_Result) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("AdminService_RestoreDynamicConfig_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminService_RestoreDynamicConfig_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_RestoreDynamicConfig_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_RestoreDynamicConfig_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_RestoreDynamicConfig_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_RestoreDynamicConfig_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a AdminService_RestoreDynamicConfig_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_RestoreDynamicConfig_Result struct could not be encoded. func (v *AdminService_RestoreDynamicConfig_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_RestoreDynamicConfig_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a AdminService_RestoreDynamicConfig_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_RestoreDynamicConfig_Result struct could not be generated from the wire // representation. func (v *AdminService_RestoreDynamicConfig_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_RestoreDynamicConfig_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_RestoreDynamicConfig_Result // struct. func (v *AdminService_RestoreDynamicConfig_Result) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } return fmt.Sprintf("AdminService_RestoreDynamicConfig_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_RestoreDynamicConfig_Result match the // provided AdminService_RestoreDynamicConfig_Result. // // This function performs a deep comparison. func (v *AdminService_RestoreDynamicConfig_Result) Equals(rhs *AdminService_RestoreDynamicConfig_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_RestoreDynamicConfig_Result. func (v *AdminService_RestoreDynamicConfig_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_RestoreDynamicConfig_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_RestoreDynamicConfig_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_RestoreDynamicConfig_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_RestoreDynamicConfig_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RestoreDynamicConfig" for this struct. func (v *AdminService_RestoreDynamicConfig_Result) MethodName() string { return "RestoreDynamicConfig" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_RestoreDynamicConfig_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args represents the arguments for the AdminService.UpdateDomainAsyncWorkflowConfiguraton function. // // The arguments for UpdateDomainAsyncWorkflowConfiguraton are sent and received over the wire as this struct. type AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args struct { Request *UpdateDomainAsyncWorkflowConfiguratonRequest `json:"request,omitempty"` } // ToWire translates a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _UpdateDomainAsyncWorkflowConfiguratonRequest_Read(w wire.Value) (*UpdateDomainAsyncWorkflowConfiguratonRequest, error) { var v UpdateDomainAsyncWorkflowConfiguratonRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _UpdateDomainAsyncWorkflowConfiguratonRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args struct could not be encoded. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _UpdateDomainAsyncWorkflowConfiguratonRequest_Decode(sr stream.Reader) (*UpdateDomainAsyncWorkflowConfiguratonRequest, error) { var v UpdateDomainAsyncWorkflowConfiguratonRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args struct could not be generated from the wire // representation. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _UpdateDomainAsyncWorkflowConfiguratonRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args // struct. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args match the // provided AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args. // // This function performs a deep comparison. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) Equals(rhs *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) GetRequest() (o *UpdateDomainAsyncWorkflowConfiguratonRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "UpdateDomainAsyncWorkflowConfiguraton" for this struct. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) MethodName() string { return "UpdateDomainAsyncWorkflowConfiguraton" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper provides functions that aid in handling the // parameters and return values of the AdminService.UpdateDomainAsyncWorkflowConfiguraton // function. var AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper = struct { // Args accepts the parameters of UpdateDomainAsyncWorkflowConfiguraton in-order and returns // the arguments struct for the function. Args func( request *UpdateDomainAsyncWorkflowConfiguratonRequest, ) *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args // IsException returns true if the given error can be thrown // by UpdateDomainAsyncWorkflowConfiguraton. // // An error can be thrown by UpdateDomainAsyncWorkflowConfiguraton only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for UpdateDomainAsyncWorkflowConfiguraton // given its return value and error. // // This allows mapping values and errors returned by // UpdateDomainAsyncWorkflowConfiguraton into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by UpdateDomainAsyncWorkflowConfiguraton // // value, err := UpdateDomainAsyncWorkflowConfiguraton(args) // result, err := AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from UpdateDomainAsyncWorkflowConfiguraton: %v", err) // } // serialize(result) WrapResponse func(*UpdateDomainAsyncWorkflowConfiguratonResponse, error) (*AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result, error) // UnwrapResponse takes the result struct for UpdateDomainAsyncWorkflowConfiguraton // and returns the value or error returned by it. // // The error is non-nil only if UpdateDomainAsyncWorkflowConfiguraton threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) (*UpdateDomainAsyncWorkflowConfiguratonResponse, error) }{} func init() { AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper.Args = func( request *UpdateDomainAsyncWorkflowConfiguratonRequest, ) *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args { return &AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args{ Request: request, } } AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true default: return false } } AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper.WrapResponse = func(success *UpdateDomainAsyncWorkflowConfiguratonResponse, err error) (*AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result, error) { if err == nil { return &AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result.BadRequestError") } return &AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result{BadRequestError: e}, nil } return nil, err } AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper.UnwrapResponse = func(result *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) (success *UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result represents the result of a AdminService.UpdateDomainAsyncWorkflowConfiguraton function call. // // The result of a UpdateDomainAsyncWorkflowConfiguraton execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result struct { // Value returned by UpdateDomainAsyncWorkflowConfiguraton after a successful execution. Success *UpdateDomainAsyncWorkflowConfiguratonResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` } // ToWire translates a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _UpdateDomainAsyncWorkflowConfiguratonResponse_Read(w wire.Value) (*UpdateDomainAsyncWorkflowConfiguratonResponse, error) { var v UpdateDomainAsyncWorkflowConfiguratonResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _UpdateDomainAsyncWorkflowConfiguratonResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result struct could not be encoded. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _UpdateDomainAsyncWorkflowConfiguratonResponse_Decode(sr stream.Reader) (*UpdateDomainAsyncWorkflowConfiguratonResponse, error) { var v UpdateDomainAsyncWorkflowConfiguratonResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result struct could not be generated from the wire // representation. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _UpdateDomainAsyncWorkflowConfiguratonResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result // struct. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } return fmt.Sprintf("AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result match the // provided AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result. // // This function performs a deep comparison. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) Equals(rhs *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) GetSuccess() (o *UpdateDomainAsyncWorkflowConfiguratonResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "UpdateDomainAsyncWorkflowConfiguraton" for this struct. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) MethodName() string { return "UpdateDomainAsyncWorkflowConfiguraton" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_UpdateDomainIsolationGroups_Args represents the arguments for the AdminService.UpdateDomainIsolationGroups function. // // The arguments for UpdateDomainIsolationGroups are sent and received over the wire as this struct. type AdminService_UpdateDomainIsolationGroups_Args struct { Request *UpdateDomainIsolationGroupsRequest `json:"request,omitempty"` } // ToWire translates a AdminService_UpdateDomainIsolationGroups_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_UpdateDomainIsolationGroups_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _UpdateDomainIsolationGroupsRequest_Read(w wire.Value) (*UpdateDomainIsolationGroupsRequest, error) { var v UpdateDomainIsolationGroupsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_UpdateDomainIsolationGroups_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_UpdateDomainIsolationGroups_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_UpdateDomainIsolationGroups_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_UpdateDomainIsolationGroups_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _UpdateDomainIsolationGroupsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_UpdateDomainIsolationGroups_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_UpdateDomainIsolationGroups_Args struct could not be encoded. func (v *AdminService_UpdateDomainIsolationGroups_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _UpdateDomainIsolationGroupsRequest_Decode(sr stream.Reader) (*UpdateDomainIsolationGroupsRequest, error) { var v UpdateDomainIsolationGroupsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_UpdateDomainIsolationGroups_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_UpdateDomainIsolationGroups_Args struct could not be generated from the wire // representation. func (v *AdminService_UpdateDomainIsolationGroups_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _UpdateDomainIsolationGroupsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_UpdateDomainIsolationGroups_Args // struct. func (v *AdminService_UpdateDomainIsolationGroups_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_UpdateDomainIsolationGroups_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_UpdateDomainIsolationGroups_Args match the // provided AdminService_UpdateDomainIsolationGroups_Args. // // This function performs a deep comparison. func (v *AdminService_UpdateDomainIsolationGroups_Args) Equals(rhs *AdminService_UpdateDomainIsolationGroups_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_UpdateDomainIsolationGroups_Args. func (v *AdminService_UpdateDomainIsolationGroups_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_UpdateDomainIsolationGroups_Args) GetRequest() (o *UpdateDomainIsolationGroupsRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_UpdateDomainIsolationGroups_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "UpdateDomainIsolationGroups" for this struct. func (v *AdminService_UpdateDomainIsolationGroups_Args) MethodName() string { return "UpdateDomainIsolationGroups" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_UpdateDomainIsolationGroups_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_UpdateDomainIsolationGroups_Helper provides functions that aid in handling the // parameters and return values of the AdminService.UpdateDomainIsolationGroups // function. var AdminService_UpdateDomainIsolationGroups_Helper = struct { // Args accepts the parameters of UpdateDomainIsolationGroups in-order and returns // the arguments struct for the function. Args func( request *UpdateDomainIsolationGroupsRequest, ) *AdminService_UpdateDomainIsolationGroups_Args // IsException returns true if the given error can be thrown // by UpdateDomainIsolationGroups. // // An error can be thrown by UpdateDomainIsolationGroups only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for UpdateDomainIsolationGroups // given its return value and error. // // This allows mapping values and errors returned by // UpdateDomainIsolationGroups into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by UpdateDomainIsolationGroups // // value, err := UpdateDomainIsolationGroups(args) // result, err := AdminService_UpdateDomainIsolationGroups_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from UpdateDomainIsolationGroups: %v", err) // } // serialize(result) WrapResponse func(*UpdateDomainIsolationGroupsResponse, error) (*AdminService_UpdateDomainIsolationGroups_Result, error) // UnwrapResponse takes the result struct for UpdateDomainIsolationGroups // and returns the value or error returned by it. // // The error is non-nil only if UpdateDomainIsolationGroups threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_UpdateDomainIsolationGroups_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_UpdateDomainIsolationGroups_Result) (*UpdateDomainIsolationGroupsResponse, error) }{} func init() { AdminService_UpdateDomainIsolationGroups_Helper.Args = func( request *UpdateDomainIsolationGroupsRequest, ) *AdminService_UpdateDomainIsolationGroups_Args { return &AdminService_UpdateDomainIsolationGroups_Args{ Request: request, } } AdminService_UpdateDomainIsolationGroups_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true default: return false } } AdminService_UpdateDomainIsolationGroups_Helper.WrapResponse = func(success *UpdateDomainIsolationGroupsResponse, err error) (*AdminService_UpdateDomainIsolationGroups_Result, error) { if err == nil { return &AdminService_UpdateDomainIsolationGroups_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_UpdateDomainIsolationGroups_Result.BadRequestError") } return &AdminService_UpdateDomainIsolationGroups_Result{BadRequestError: e}, nil } return nil, err } AdminService_UpdateDomainIsolationGroups_Helper.UnwrapResponse = func(result *AdminService_UpdateDomainIsolationGroups_Result) (success *UpdateDomainIsolationGroupsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_UpdateDomainIsolationGroups_Result represents the result of a AdminService.UpdateDomainIsolationGroups function call. // // The result of a UpdateDomainIsolationGroups execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_UpdateDomainIsolationGroups_Result struct { // Value returned by UpdateDomainIsolationGroups after a successful execution. Success *UpdateDomainIsolationGroupsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` } // ToWire translates a AdminService_UpdateDomainIsolationGroups_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_UpdateDomainIsolationGroups_Result) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_UpdateDomainIsolationGroups_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _UpdateDomainIsolationGroupsResponse_Read(w wire.Value) (*UpdateDomainIsolationGroupsResponse, error) { var v UpdateDomainIsolationGroupsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_UpdateDomainIsolationGroups_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_UpdateDomainIsolationGroups_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_UpdateDomainIsolationGroups_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_UpdateDomainIsolationGroups_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _UpdateDomainIsolationGroupsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_UpdateDomainIsolationGroups_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_UpdateDomainIsolationGroups_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_UpdateDomainIsolationGroups_Result struct could not be encoded. func (v *AdminService_UpdateDomainIsolationGroups_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_UpdateDomainIsolationGroups_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _UpdateDomainIsolationGroupsResponse_Decode(sr stream.Reader) (*UpdateDomainIsolationGroupsResponse, error) { var v UpdateDomainIsolationGroupsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_UpdateDomainIsolationGroups_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_UpdateDomainIsolationGroups_Result struct could not be generated from the wire // representation. func (v *AdminService_UpdateDomainIsolationGroups_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _UpdateDomainIsolationGroupsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_UpdateDomainIsolationGroups_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_UpdateDomainIsolationGroups_Result // struct. func (v *AdminService_UpdateDomainIsolationGroups_Result) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } return fmt.Sprintf("AdminService_UpdateDomainIsolationGroups_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_UpdateDomainIsolationGroups_Result match the // provided AdminService_UpdateDomainIsolationGroups_Result. // // This function performs a deep comparison. func (v *AdminService_UpdateDomainIsolationGroups_Result) Equals(rhs *AdminService_UpdateDomainIsolationGroups_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_UpdateDomainIsolationGroups_Result. func (v *AdminService_UpdateDomainIsolationGroups_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_UpdateDomainIsolationGroups_Result) GetSuccess() (o *UpdateDomainIsolationGroupsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_UpdateDomainIsolationGroups_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_UpdateDomainIsolationGroups_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_UpdateDomainIsolationGroups_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "UpdateDomainIsolationGroups" for this struct. func (v *AdminService_UpdateDomainIsolationGroups_Result) MethodName() string { return "UpdateDomainIsolationGroups" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_UpdateDomainIsolationGroups_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_UpdateDynamicConfig_Args represents the arguments for the AdminService.UpdateDynamicConfig function. // // The arguments for UpdateDynamicConfig are sent and received over the wire as this struct. type AdminService_UpdateDynamicConfig_Args struct { Request *UpdateDynamicConfigRequest `json:"request,omitempty"` } // ToWire translates a AdminService_UpdateDynamicConfig_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_UpdateDynamicConfig_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _UpdateDynamicConfigRequest_Read(w wire.Value) (*UpdateDynamicConfigRequest, error) { var v UpdateDynamicConfigRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_UpdateDynamicConfig_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_UpdateDynamicConfig_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_UpdateDynamicConfig_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_UpdateDynamicConfig_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _UpdateDynamicConfigRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_UpdateDynamicConfig_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_UpdateDynamicConfig_Args struct could not be encoded. func (v *AdminService_UpdateDynamicConfig_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _UpdateDynamicConfigRequest_Decode(sr stream.Reader) (*UpdateDynamicConfigRequest, error) { var v UpdateDynamicConfigRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_UpdateDynamicConfig_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_UpdateDynamicConfig_Args struct could not be generated from the wire // representation. func (v *AdminService_UpdateDynamicConfig_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _UpdateDynamicConfigRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_UpdateDynamicConfig_Args // struct. func (v *AdminService_UpdateDynamicConfig_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_UpdateDynamicConfig_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_UpdateDynamicConfig_Args match the // provided AdminService_UpdateDynamicConfig_Args. // // This function performs a deep comparison. func (v *AdminService_UpdateDynamicConfig_Args) Equals(rhs *AdminService_UpdateDynamicConfig_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_UpdateDynamicConfig_Args. func (v *AdminService_UpdateDynamicConfig_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_UpdateDynamicConfig_Args) GetRequest() (o *UpdateDynamicConfigRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_UpdateDynamicConfig_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "UpdateDynamicConfig" for this struct. func (v *AdminService_UpdateDynamicConfig_Args) MethodName() string { return "UpdateDynamicConfig" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_UpdateDynamicConfig_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_UpdateDynamicConfig_Helper provides functions that aid in handling the // parameters and return values of the AdminService.UpdateDynamicConfig // function. var AdminService_UpdateDynamicConfig_Helper = struct { // Args accepts the parameters of UpdateDynamicConfig in-order and returns // the arguments struct for the function. Args func( request *UpdateDynamicConfigRequest, ) *AdminService_UpdateDynamicConfig_Args // IsException returns true if the given error can be thrown // by UpdateDynamicConfig. // // An error can be thrown by UpdateDynamicConfig only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for UpdateDynamicConfig // given the error returned by it. The provided error may // be nil if UpdateDynamicConfig did not fail. // // This allows mapping errors returned by UpdateDynamicConfig into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // UpdateDynamicConfig // // err := UpdateDynamicConfig(args) // result, err := AdminService_UpdateDynamicConfig_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from UpdateDynamicConfig: %v", err) // } // serialize(result) WrapResponse func(error) (*AdminService_UpdateDynamicConfig_Result, error) // UnwrapResponse takes the result struct for UpdateDynamicConfig // and returns the erorr returned by it (if any). // // The error is non-nil only if UpdateDynamicConfig threw an // exception. // // result := deserialize(bytes) // err := AdminService_UpdateDynamicConfig_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_UpdateDynamicConfig_Result) error }{} func init() { AdminService_UpdateDynamicConfig_Helper.Args = func( request *UpdateDynamicConfigRequest, ) *AdminService_UpdateDynamicConfig_Args { return &AdminService_UpdateDynamicConfig_Args{ Request: request, } } AdminService_UpdateDynamicConfig_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true default: return false } } AdminService_UpdateDynamicConfig_Helper.WrapResponse = func(err error) (*AdminService_UpdateDynamicConfig_Result, error) { if err == nil { return &AdminService_UpdateDynamicConfig_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_UpdateDynamicConfig_Result.BadRequestError") } return &AdminService_UpdateDynamicConfig_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_UpdateDynamicConfig_Result.InternalServiceError") } return &AdminService_UpdateDynamicConfig_Result{InternalServiceError: e}, nil } return nil, err } AdminService_UpdateDynamicConfig_Helper.UnwrapResponse = func(result *AdminService_UpdateDynamicConfig_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } return } } // AdminService_UpdateDynamicConfig_Result represents the result of a AdminService.UpdateDynamicConfig function call. // // The result of a UpdateDynamicConfig execution is sent and received over the wire as this struct. type AdminService_UpdateDynamicConfig_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` } // ToWire translates a AdminService_UpdateDynamicConfig_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_UpdateDynamicConfig_Result) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("AdminService_UpdateDynamicConfig_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AdminService_UpdateDynamicConfig_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_UpdateDynamicConfig_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_UpdateDynamicConfig_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_UpdateDynamicConfig_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_UpdateDynamicConfig_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a AdminService_UpdateDynamicConfig_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_UpdateDynamicConfig_Result struct could not be encoded. func (v *AdminService_UpdateDynamicConfig_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_UpdateDynamicConfig_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a AdminService_UpdateDynamicConfig_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_UpdateDynamicConfig_Result struct could not be generated from the wire // representation. func (v *AdminService_UpdateDynamicConfig_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if count > 1 { return fmt.Errorf("AdminService_UpdateDynamicConfig_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_UpdateDynamicConfig_Result // struct. func (v *AdminService_UpdateDynamicConfig_Result) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } return fmt.Sprintf("AdminService_UpdateDynamicConfig_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_UpdateDynamicConfig_Result match the // provided AdminService_UpdateDynamicConfig_Result. // // This function performs a deep comparison. func (v *AdminService_UpdateDynamicConfig_Result) Equals(rhs *AdminService_UpdateDynamicConfig_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_UpdateDynamicConfig_Result. func (v *AdminService_UpdateDynamicConfig_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_UpdateDynamicConfig_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_UpdateDynamicConfig_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *AdminService_UpdateDynamicConfig_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *AdminService_UpdateDynamicConfig_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "UpdateDynamicConfig" for this struct. func (v *AdminService_UpdateDynamicConfig_Result) MethodName() string { return "UpdateDynamicConfig" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_UpdateDynamicConfig_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // AdminService_UpdateGlobalIsolationGroups_Args represents the arguments for the AdminService.UpdateGlobalIsolationGroups function. // // The arguments for UpdateGlobalIsolationGroups are sent and received over the wire as this struct. type AdminService_UpdateGlobalIsolationGroups_Args struct { Request *UpdateGlobalIsolationGroupsRequest `json:"request,omitempty"` } // ToWire translates a AdminService_UpdateGlobalIsolationGroups_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_UpdateGlobalIsolationGroups_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _UpdateGlobalIsolationGroupsRequest_Read(w wire.Value) (*UpdateGlobalIsolationGroupsRequest, error) { var v UpdateGlobalIsolationGroupsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_UpdateGlobalIsolationGroups_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_UpdateGlobalIsolationGroups_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_UpdateGlobalIsolationGroups_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_UpdateGlobalIsolationGroups_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _UpdateGlobalIsolationGroupsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AdminService_UpdateGlobalIsolationGroups_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_UpdateGlobalIsolationGroups_Args struct could not be encoded. func (v *AdminService_UpdateGlobalIsolationGroups_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _UpdateGlobalIsolationGroupsRequest_Decode(sr stream.Reader) (*UpdateGlobalIsolationGroupsRequest, error) { var v UpdateGlobalIsolationGroupsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_UpdateGlobalIsolationGroups_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_UpdateGlobalIsolationGroups_Args struct could not be generated from the wire // representation. func (v *AdminService_UpdateGlobalIsolationGroups_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _UpdateGlobalIsolationGroupsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AdminService_UpdateGlobalIsolationGroups_Args // struct. func (v *AdminService_UpdateGlobalIsolationGroups_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("AdminService_UpdateGlobalIsolationGroups_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_UpdateGlobalIsolationGroups_Args match the // provided AdminService_UpdateGlobalIsolationGroups_Args. // // This function performs a deep comparison. func (v *AdminService_UpdateGlobalIsolationGroups_Args) Equals(rhs *AdminService_UpdateGlobalIsolationGroups_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_UpdateGlobalIsolationGroups_Args. func (v *AdminService_UpdateGlobalIsolationGroups_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *AdminService_UpdateGlobalIsolationGroups_Args) GetRequest() (o *UpdateGlobalIsolationGroupsRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *AdminService_UpdateGlobalIsolationGroups_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "UpdateGlobalIsolationGroups" for this struct. func (v *AdminService_UpdateGlobalIsolationGroups_Args) MethodName() string { return "UpdateGlobalIsolationGroups" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *AdminService_UpdateGlobalIsolationGroups_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // AdminService_UpdateGlobalIsolationGroups_Helper provides functions that aid in handling the // parameters and return values of the AdminService.UpdateGlobalIsolationGroups // function. var AdminService_UpdateGlobalIsolationGroups_Helper = struct { // Args accepts the parameters of UpdateGlobalIsolationGroups in-order and returns // the arguments struct for the function. Args func( request *UpdateGlobalIsolationGroupsRequest, ) *AdminService_UpdateGlobalIsolationGroups_Args // IsException returns true if the given error can be thrown // by UpdateGlobalIsolationGroups. // // An error can be thrown by UpdateGlobalIsolationGroups only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for UpdateGlobalIsolationGroups // given its return value and error. // // This allows mapping values and errors returned by // UpdateGlobalIsolationGroups into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by UpdateGlobalIsolationGroups // // value, err := UpdateGlobalIsolationGroups(args) // result, err := AdminService_UpdateGlobalIsolationGroups_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from UpdateGlobalIsolationGroups: %v", err) // } // serialize(result) WrapResponse func(*UpdateGlobalIsolationGroupsResponse, error) (*AdminService_UpdateGlobalIsolationGroups_Result, error) // UnwrapResponse takes the result struct for UpdateGlobalIsolationGroups // and returns the value or error returned by it. // // The error is non-nil only if UpdateGlobalIsolationGroups threw an // exception. // // result := deserialize(bytes) // value, err := AdminService_UpdateGlobalIsolationGroups_Helper.UnwrapResponse(result) UnwrapResponse func(*AdminService_UpdateGlobalIsolationGroups_Result) (*UpdateGlobalIsolationGroupsResponse, error) }{} func init() { AdminService_UpdateGlobalIsolationGroups_Helper.Args = func( request *UpdateGlobalIsolationGroupsRequest, ) *AdminService_UpdateGlobalIsolationGroups_Args { return &AdminService_UpdateGlobalIsolationGroups_Args{ Request: request, } } AdminService_UpdateGlobalIsolationGroups_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true default: return false } } AdminService_UpdateGlobalIsolationGroups_Helper.WrapResponse = func(success *UpdateGlobalIsolationGroupsResponse, err error) (*AdminService_UpdateGlobalIsolationGroups_Result, error) { if err == nil { return &AdminService_UpdateGlobalIsolationGroups_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for AdminService_UpdateGlobalIsolationGroups_Result.BadRequestError") } return &AdminService_UpdateGlobalIsolationGroups_Result{BadRequestError: e}, nil } return nil, err } AdminService_UpdateGlobalIsolationGroups_Helper.UnwrapResponse = func(result *AdminService_UpdateGlobalIsolationGroups_Result) (success *UpdateGlobalIsolationGroupsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // AdminService_UpdateGlobalIsolationGroups_Result represents the result of a AdminService.UpdateGlobalIsolationGroups function call. // // The result of a UpdateGlobalIsolationGroups execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type AdminService_UpdateGlobalIsolationGroups_Result struct { // Value returned by UpdateGlobalIsolationGroups after a successful execution. Success *UpdateGlobalIsolationGroupsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` } // ToWire translates a AdminService_UpdateGlobalIsolationGroups_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AdminService_UpdateGlobalIsolationGroups_Result) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("AdminService_UpdateGlobalIsolationGroups_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _UpdateGlobalIsolationGroupsResponse_Read(w wire.Value) (*UpdateGlobalIsolationGroupsResponse, error) { var v UpdateGlobalIsolationGroupsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a AdminService_UpdateGlobalIsolationGroups_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AdminService_UpdateGlobalIsolationGroups_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AdminService_UpdateGlobalIsolationGroups_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AdminService_UpdateGlobalIsolationGroups_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _UpdateGlobalIsolationGroupsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_UpdateGlobalIsolationGroups_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a AdminService_UpdateGlobalIsolationGroups_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AdminService_UpdateGlobalIsolationGroups_Result struct could not be encoded. func (v *AdminService_UpdateGlobalIsolationGroups_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_UpdateGlobalIsolationGroups_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _UpdateGlobalIsolationGroupsResponse_Decode(sr stream.Reader) (*UpdateGlobalIsolationGroupsResponse, error) { var v UpdateGlobalIsolationGroupsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a AdminService_UpdateGlobalIsolationGroups_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AdminService_UpdateGlobalIsolationGroups_Result struct could not be generated from the wire // representation. func (v *AdminService_UpdateGlobalIsolationGroups_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _UpdateGlobalIsolationGroupsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if count != 1 { return fmt.Errorf("AdminService_UpdateGlobalIsolationGroups_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a AdminService_UpdateGlobalIsolationGroups_Result // struct. func (v *AdminService_UpdateGlobalIsolationGroups_Result) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } return fmt.Sprintf("AdminService_UpdateGlobalIsolationGroups_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AdminService_UpdateGlobalIsolationGroups_Result match the // provided AdminService_UpdateGlobalIsolationGroups_Result. // // This function performs a deep comparison. func (v *AdminService_UpdateGlobalIsolationGroups_Result) Equals(rhs *AdminService_UpdateGlobalIsolationGroups_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AdminService_UpdateGlobalIsolationGroups_Result. func (v *AdminService_UpdateGlobalIsolationGroups_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *AdminService_UpdateGlobalIsolationGroups_Result) GetSuccess() (o *UpdateGlobalIsolationGroupsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *AdminService_UpdateGlobalIsolationGroups_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *AdminService_UpdateGlobalIsolationGroups_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *AdminService_UpdateGlobalIsolationGroups_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "UpdateGlobalIsolationGroups" for this struct. func (v *AdminService_UpdateGlobalIsolationGroups_Result) MethodName() string { return "UpdateGlobalIsolationGroups" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *AdminService_UpdateGlobalIsolationGroups_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } ================================================ FILE: .gen/go/admin/adminserviceclient/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package adminserviceclient import ( context "context" reflect "reflect" wire "go.uber.org/thriftrw/wire" yarpc "go.uber.org/yarpc" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" admin "github.com/uber/cadence/.gen/go/admin" replicator "github.com/uber/cadence/.gen/go/replicator" shared "github.com/uber/cadence/.gen/go/shared" ) // Interface is a client for the AdminService service. type Interface interface { AddSearchAttribute( ctx context.Context, Request *admin.AddSearchAttributeRequest, opts ...yarpc.CallOption, ) error CloseShard( ctx context.Context, Request *shared.CloseShardRequest, opts ...yarpc.CallOption, ) error DeleteWorkflow( ctx context.Context, Request *admin.AdminDeleteWorkflowRequest, opts ...yarpc.CallOption, ) (*admin.AdminDeleteWorkflowResponse, error) DescribeCluster( ctx context.Context, opts ...yarpc.CallOption, ) (*admin.DescribeClusterResponse, error) DescribeHistoryHost( ctx context.Context, Request *shared.DescribeHistoryHostRequest, opts ...yarpc.CallOption, ) (*shared.DescribeHistoryHostResponse, error) DescribeQueue( ctx context.Context, Request *shared.DescribeQueueRequest, opts ...yarpc.CallOption, ) (*shared.DescribeQueueResponse, error) DescribeShardDistribution( ctx context.Context, Request *shared.DescribeShardDistributionRequest, opts ...yarpc.CallOption, ) (*shared.DescribeShardDistributionResponse, error) DescribeWorkflowExecution( ctx context.Context, Request *admin.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*admin.DescribeWorkflowExecutionResponse, error) GetCrossClusterTasks( ctx context.Context, Request *shared.GetCrossClusterTasksRequest, opts ...yarpc.CallOption, ) (*shared.GetCrossClusterTasksResponse, error) GetDLQReplicationMessages( ctx context.Context, Request *replicator.GetDLQReplicationMessagesRequest, opts ...yarpc.CallOption, ) (*replicator.GetDLQReplicationMessagesResponse, error) GetDomainAsyncWorkflowConfiguraton( ctx context.Context, Request *admin.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption, ) (*admin.GetDomainAsyncWorkflowConfiguratonResponse, error) GetDomainIsolationGroups( ctx context.Context, Request *admin.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption, ) (*admin.GetDomainIsolationGroupsResponse, error) GetDomainReplicationMessages( ctx context.Context, Request *replicator.GetDomainReplicationMessagesRequest, opts ...yarpc.CallOption, ) (*replicator.GetDomainReplicationMessagesResponse, error) GetDynamicConfig( ctx context.Context, Request *admin.GetDynamicConfigRequest, opts ...yarpc.CallOption, ) (*admin.GetDynamicConfigResponse, error) GetGlobalIsolationGroups( ctx context.Context, Request *admin.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption, ) (*admin.GetGlobalIsolationGroupsResponse, error) GetReplicationMessages( ctx context.Context, Request *replicator.GetReplicationMessagesRequest, opts ...yarpc.CallOption, ) (*replicator.GetReplicationMessagesResponse, error) GetWorkflowExecutionRawHistoryV2( ctx context.Context, GetRequest *admin.GetWorkflowExecutionRawHistoryV2Request, opts ...yarpc.CallOption, ) (*admin.GetWorkflowExecutionRawHistoryV2Response, error) ListDynamicConfig( ctx context.Context, Request *admin.ListDynamicConfigRequest, opts ...yarpc.CallOption, ) (*admin.ListDynamicConfigResponse, error) MaintainCorruptWorkflow( ctx context.Context, Request *admin.AdminMaintainWorkflowRequest, opts ...yarpc.CallOption, ) (*admin.AdminMaintainWorkflowResponse, error) MergeDLQMessages( ctx context.Context, Request *replicator.MergeDLQMessagesRequest, opts ...yarpc.CallOption, ) (*replicator.MergeDLQMessagesResponse, error) PurgeDLQMessages( ctx context.Context, Request *replicator.PurgeDLQMessagesRequest, opts ...yarpc.CallOption, ) error ReadDLQMessages( ctx context.Context, Request *replicator.ReadDLQMessagesRequest, opts ...yarpc.CallOption, ) (*replicator.ReadDLQMessagesResponse, error) ReapplyEvents( ctx context.Context, ReapplyEventsRequest *shared.ReapplyEventsRequest, opts ...yarpc.CallOption, ) error RefreshWorkflowTasks( ctx context.Context, Request *shared.RefreshWorkflowTasksRequest, opts ...yarpc.CallOption, ) error RemoveTask( ctx context.Context, Request *shared.RemoveTaskRequest, opts ...yarpc.CallOption, ) error ResendReplicationTasks( ctx context.Context, Request *admin.ResendReplicationTasksRequest, opts ...yarpc.CallOption, ) error ResetQueue( ctx context.Context, Request *shared.ResetQueueRequest, opts ...yarpc.CallOption, ) error RespondCrossClusterTasksCompleted( ctx context.Context, Request *shared.RespondCrossClusterTasksCompletedRequest, opts ...yarpc.CallOption, ) (*shared.RespondCrossClusterTasksCompletedResponse, error) RestoreDynamicConfig( ctx context.Context, Request *admin.RestoreDynamicConfigRequest, opts ...yarpc.CallOption, ) error UpdateDomainAsyncWorkflowConfiguraton( ctx context.Context, Request *admin.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption, ) (*admin.UpdateDomainAsyncWorkflowConfiguratonResponse, error) UpdateDomainIsolationGroups( ctx context.Context, Request *admin.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption, ) (*admin.UpdateDomainIsolationGroupsResponse, error) UpdateDynamicConfig( ctx context.Context, Request *admin.UpdateDynamicConfigRequest, opts ...yarpc.CallOption, ) error UpdateGlobalIsolationGroups( ctx context.Context, Request *admin.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption, ) (*admin.UpdateGlobalIsolationGroupsResponse, error) } // New builds a new client for the AdminService service. // // client := adminserviceclient.New(dispatcher.ClientConfig("adminservice")) func New(c transport.ClientConfig, opts ...thrift.ClientOption) Interface { return client{ c: thrift.New(thrift.Config{ Service: "AdminService", ClientConfig: c, }, opts...), nwc: thrift.NewNoWire(thrift.Config{ Service: "AdminService", ClientConfig: c, }, opts...), } } func init() { yarpc.RegisterClientBuilder( func(c transport.ClientConfig, f reflect.StructField) Interface { return New(c, thrift.ClientBuilderOptions(c, f)...) }, ) } type client struct { c thrift.Client nwc thrift.NoWireClient } func (c client) AddSearchAttribute( ctx context.Context, _Request *admin.AddSearchAttributeRequest, opts ...yarpc.CallOption, ) (err error) { var result admin.AdminService_AddSearchAttribute_Result args := admin.AdminService_AddSearchAttribute_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = admin.AdminService_AddSearchAttribute_Helper.UnwrapResponse(&result) return } func (c client) CloseShard( ctx context.Context, _Request *shared.CloseShardRequest, opts ...yarpc.CallOption, ) (err error) { var result admin.AdminService_CloseShard_Result args := admin.AdminService_CloseShard_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = admin.AdminService_CloseShard_Helper.UnwrapResponse(&result) return } func (c client) DeleteWorkflow( ctx context.Context, _Request *admin.AdminDeleteWorkflowRequest, opts ...yarpc.CallOption, ) (success *admin.AdminDeleteWorkflowResponse, err error) { var result admin.AdminService_DeleteWorkflow_Result args := admin.AdminService_DeleteWorkflow_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_DeleteWorkflow_Helper.UnwrapResponse(&result) return } func (c client) DescribeCluster( ctx context.Context, opts ...yarpc.CallOption, ) (success *admin.DescribeClusterResponse, err error) { var result admin.AdminService_DescribeCluster_Result args := admin.AdminService_DescribeCluster_Helper.Args() if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_DescribeCluster_Helper.UnwrapResponse(&result) return } func (c client) DescribeHistoryHost( ctx context.Context, _Request *shared.DescribeHistoryHostRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeHistoryHostResponse, err error) { var result admin.AdminService_DescribeHistoryHost_Result args := admin.AdminService_DescribeHistoryHost_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_DescribeHistoryHost_Helper.UnwrapResponse(&result) return } func (c client) DescribeQueue( ctx context.Context, _Request *shared.DescribeQueueRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeQueueResponse, err error) { var result admin.AdminService_DescribeQueue_Result args := admin.AdminService_DescribeQueue_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_DescribeQueue_Helper.UnwrapResponse(&result) return } func (c client) DescribeShardDistribution( ctx context.Context, _Request *shared.DescribeShardDistributionRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeShardDistributionResponse, err error) { var result admin.AdminService_DescribeShardDistribution_Result args := admin.AdminService_DescribeShardDistribution_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_DescribeShardDistribution_Helper.UnwrapResponse(&result) return } func (c client) DescribeWorkflowExecution( ctx context.Context, _Request *admin.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *admin.DescribeWorkflowExecutionResponse, err error) { var result admin.AdminService_DescribeWorkflowExecution_Result args := admin.AdminService_DescribeWorkflowExecution_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_DescribeWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) GetCrossClusterTasks( ctx context.Context, _Request *shared.GetCrossClusterTasksRequest, opts ...yarpc.CallOption, ) (success *shared.GetCrossClusterTasksResponse, err error) { var result admin.AdminService_GetCrossClusterTasks_Result args := admin.AdminService_GetCrossClusterTasks_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_GetCrossClusterTasks_Helper.UnwrapResponse(&result) return } func (c client) GetDLQReplicationMessages( ctx context.Context, _Request *replicator.GetDLQReplicationMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.GetDLQReplicationMessagesResponse, err error) { var result admin.AdminService_GetDLQReplicationMessages_Result args := admin.AdminService_GetDLQReplicationMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_GetDLQReplicationMessages_Helper.UnwrapResponse(&result) return } func (c client) GetDomainAsyncWorkflowConfiguraton( ctx context.Context, _Request *admin.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption, ) (success *admin.GetDomainAsyncWorkflowConfiguratonResponse, err error) { var result admin.AdminService_GetDomainAsyncWorkflowConfiguraton_Result args := admin.AdminService_GetDomainAsyncWorkflowConfiguraton_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_GetDomainAsyncWorkflowConfiguraton_Helper.UnwrapResponse(&result) return } func (c client) GetDomainIsolationGroups( ctx context.Context, _Request *admin.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption, ) (success *admin.GetDomainIsolationGroupsResponse, err error) { var result admin.AdminService_GetDomainIsolationGroups_Result args := admin.AdminService_GetDomainIsolationGroups_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_GetDomainIsolationGroups_Helper.UnwrapResponse(&result) return } func (c client) GetDomainReplicationMessages( ctx context.Context, _Request *replicator.GetDomainReplicationMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.GetDomainReplicationMessagesResponse, err error) { var result admin.AdminService_GetDomainReplicationMessages_Result args := admin.AdminService_GetDomainReplicationMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_GetDomainReplicationMessages_Helper.UnwrapResponse(&result) return } func (c client) GetDynamicConfig( ctx context.Context, _Request *admin.GetDynamicConfigRequest, opts ...yarpc.CallOption, ) (success *admin.GetDynamicConfigResponse, err error) { var result admin.AdminService_GetDynamicConfig_Result args := admin.AdminService_GetDynamicConfig_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_GetDynamicConfig_Helper.UnwrapResponse(&result) return } func (c client) GetGlobalIsolationGroups( ctx context.Context, _Request *admin.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption, ) (success *admin.GetGlobalIsolationGroupsResponse, err error) { var result admin.AdminService_GetGlobalIsolationGroups_Result args := admin.AdminService_GetGlobalIsolationGroups_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_GetGlobalIsolationGroups_Helper.UnwrapResponse(&result) return } func (c client) GetReplicationMessages( ctx context.Context, _Request *replicator.GetReplicationMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.GetReplicationMessagesResponse, err error) { var result admin.AdminService_GetReplicationMessages_Result args := admin.AdminService_GetReplicationMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_GetReplicationMessages_Helper.UnwrapResponse(&result) return } func (c client) GetWorkflowExecutionRawHistoryV2( ctx context.Context, _GetRequest *admin.GetWorkflowExecutionRawHistoryV2Request, opts ...yarpc.CallOption, ) (success *admin.GetWorkflowExecutionRawHistoryV2Response, err error) { var result admin.AdminService_GetWorkflowExecutionRawHistoryV2_Result args := admin.AdminService_GetWorkflowExecutionRawHistoryV2_Helper.Args(_GetRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_GetWorkflowExecutionRawHistoryV2_Helper.UnwrapResponse(&result) return } func (c client) ListDynamicConfig( ctx context.Context, _Request *admin.ListDynamicConfigRequest, opts ...yarpc.CallOption, ) (success *admin.ListDynamicConfigResponse, err error) { var result admin.AdminService_ListDynamicConfig_Result args := admin.AdminService_ListDynamicConfig_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_ListDynamicConfig_Helper.UnwrapResponse(&result) return } func (c client) MaintainCorruptWorkflow( ctx context.Context, _Request *admin.AdminMaintainWorkflowRequest, opts ...yarpc.CallOption, ) (success *admin.AdminMaintainWorkflowResponse, err error) { var result admin.AdminService_MaintainCorruptWorkflow_Result args := admin.AdminService_MaintainCorruptWorkflow_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_MaintainCorruptWorkflow_Helper.UnwrapResponse(&result) return } func (c client) MergeDLQMessages( ctx context.Context, _Request *replicator.MergeDLQMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.MergeDLQMessagesResponse, err error) { var result admin.AdminService_MergeDLQMessages_Result args := admin.AdminService_MergeDLQMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_MergeDLQMessages_Helper.UnwrapResponse(&result) return } func (c client) PurgeDLQMessages( ctx context.Context, _Request *replicator.PurgeDLQMessagesRequest, opts ...yarpc.CallOption, ) (err error) { var result admin.AdminService_PurgeDLQMessages_Result args := admin.AdminService_PurgeDLQMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = admin.AdminService_PurgeDLQMessages_Helper.UnwrapResponse(&result) return } func (c client) ReadDLQMessages( ctx context.Context, _Request *replicator.ReadDLQMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.ReadDLQMessagesResponse, err error) { var result admin.AdminService_ReadDLQMessages_Result args := admin.AdminService_ReadDLQMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_ReadDLQMessages_Helper.UnwrapResponse(&result) return } func (c client) ReapplyEvents( ctx context.Context, _ReapplyEventsRequest *shared.ReapplyEventsRequest, opts ...yarpc.CallOption, ) (err error) { var result admin.AdminService_ReapplyEvents_Result args := admin.AdminService_ReapplyEvents_Helper.Args(_ReapplyEventsRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = admin.AdminService_ReapplyEvents_Helper.UnwrapResponse(&result) return } func (c client) RefreshWorkflowTasks( ctx context.Context, _Request *shared.RefreshWorkflowTasksRequest, opts ...yarpc.CallOption, ) (err error) { var result admin.AdminService_RefreshWorkflowTasks_Result args := admin.AdminService_RefreshWorkflowTasks_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = admin.AdminService_RefreshWorkflowTasks_Helper.UnwrapResponse(&result) return } func (c client) RemoveTask( ctx context.Context, _Request *shared.RemoveTaskRequest, opts ...yarpc.CallOption, ) (err error) { var result admin.AdminService_RemoveTask_Result args := admin.AdminService_RemoveTask_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = admin.AdminService_RemoveTask_Helper.UnwrapResponse(&result) return } func (c client) ResendReplicationTasks( ctx context.Context, _Request *admin.ResendReplicationTasksRequest, opts ...yarpc.CallOption, ) (err error) { var result admin.AdminService_ResendReplicationTasks_Result args := admin.AdminService_ResendReplicationTasks_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = admin.AdminService_ResendReplicationTasks_Helper.UnwrapResponse(&result) return } func (c client) ResetQueue( ctx context.Context, _Request *shared.ResetQueueRequest, opts ...yarpc.CallOption, ) (err error) { var result admin.AdminService_ResetQueue_Result args := admin.AdminService_ResetQueue_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = admin.AdminService_ResetQueue_Helper.UnwrapResponse(&result) return } func (c client) RespondCrossClusterTasksCompleted( ctx context.Context, _Request *shared.RespondCrossClusterTasksCompletedRequest, opts ...yarpc.CallOption, ) (success *shared.RespondCrossClusterTasksCompletedResponse, err error) { var result admin.AdminService_RespondCrossClusterTasksCompleted_Result args := admin.AdminService_RespondCrossClusterTasksCompleted_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_RespondCrossClusterTasksCompleted_Helper.UnwrapResponse(&result) return } func (c client) RestoreDynamicConfig( ctx context.Context, _Request *admin.RestoreDynamicConfigRequest, opts ...yarpc.CallOption, ) (err error) { var result admin.AdminService_RestoreDynamicConfig_Result args := admin.AdminService_RestoreDynamicConfig_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = admin.AdminService_RestoreDynamicConfig_Helper.UnwrapResponse(&result) return } func (c client) UpdateDomainAsyncWorkflowConfiguraton( ctx context.Context, _Request *admin.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption, ) (success *admin.UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { var result admin.AdminService_UpdateDomainAsyncWorkflowConfiguraton_Result args := admin.AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper.UnwrapResponse(&result) return } func (c client) UpdateDomainIsolationGroups( ctx context.Context, _Request *admin.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption, ) (success *admin.UpdateDomainIsolationGroupsResponse, err error) { var result admin.AdminService_UpdateDomainIsolationGroups_Result args := admin.AdminService_UpdateDomainIsolationGroups_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_UpdateDomainIsolationGroups_Helper.UnwrapResponse(&result) return } func (c client) UpdateDynamicConfig( ctx context.Context, _Request *admin.UpdateDynamicConfigRequest, opts ...yarpc.CallOption, ) (err error) { var result admin.AdminService_UpdateDynamicConfig_Result args := admin.AdminService_UpdateDynamicConfig_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = admin.AdminService_UpdateDynamicConfig_Helper.UnwrapResponse(&result) return } func (c client) UpdateGlobalIsolationGroups( ctx context.Context, _Request *admin.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption, ) (success *admin.UpdateGlobalIsolationGroupsResponse, err error) { var result admin.AdminService_UpdateGlobalIsolationGroups_Result args := admin.AdminService_UpdateGlobalIsolationGroups_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = admin.AdminService_UpdateGlobalIsolationGroups_Helper.UnwrapResponse(&result) return } ================================================ FILE: .gen/go/admin/adminservicefx/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package adminservicefx import ( fx "go.uber.org/fx" yarpc "go.uber.org/yarpc" transport "go.uber.org/yarpc/api/transport" restriction "go.uber.org/yarpc/api/x/restriction" thrift "go.uber.org/yarpc/encoding/thrift" adminserviceclient "github.com/uber/cadence/.gen/go/admin/adminserviceclient" ) // Params defines the dependencies for the AdminService client. type Params struct { fx.In Provider yarpc.ClientConfig Restriction restriction.Checker `optional:"true"` } // Result defines the output of the AdminService client module. It provides a // AdminService client to an Fx application. type Result struct { fx.Out Client adminserviceclient.Interface // We are using an fx.Out struct here instead of just returning a client // so that we can add more values or add named versions of the client in // the future without breaking any existing code. } // Client provides a AdminService client to an Fx application using the given name // for routing. // // fx.Provide( // adminservicefx.Client("..."), // newHandler, // ) func Client(name string, opts ...thrift.ClientOption) interface{} { return func(p Params) Result { cc := p.Provider.ClientConfig(name) if namer, ok := cc.GetUnaryOutbound().(transport.Namer); ok && p.Restriction != nil { if err := p.Restriction.Check(thrift.Encoding, namer.TransportName()); err != nil { panic(err.Error()) } } client := adminserviceclient.New(cc, opts...) return Result{Client: client} } } ================================================ FILE: .gen/go/admin/adminservicefx/doc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated // Package adminservicefx provides better integration for Fx for services // implementing or calling AdminService. // // # Clients // // If you are making requests to AdminService, use the Client function to inject a // AdminService client into your container. // // fx.Provide(adminservicefx.Client("...")) // // # Servers // // If you are implementing AdminService, provide a adminserviceserver.Interface into // the container and use the Server function. // // Given, // // func NewAdminServiceHandler() adminserviceserver.Interface // // You can do the following to have the procedures of AdminService made available // to an Fx application. // // fx.Provide( // NewAdminServiceHandler, // adminservicefx.Server(), // ) package adminservicefx ================================================ FILE: .gen/go/admin/adminservicefx/server.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package adminservicefx import ( fx "go.uber.org/fx" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" adminserviceserver "github.com/uber/cadence/.gen/go/admin/adminserviceserver" ) // ServerParams defines the dependencies for the AdminService server. type ServerParams struct { fx.In Handler adminserviceserver.Interface } // ServerResult defines the output of AdminService server module. It provides the // procedures of a AdminService handler to an Fx application. // // The procedures are provided to the "yarpcfx" value group. Dig 1.2 or newer // must be used for this feature to work. type ServerResult struct { fx.Out Procedures []transport.Procedure `group:"yarpcfx"` } // Server provides procedures for AdminService to an Fx application. It expects a // adminservicefx.Interface to be present in the container. // // fx.Provide( // func(h *MyAdminServiceHandler) adminserviceserver.Interface { // return h // }, // adminservicefx.Server(), // ) func Server(opts ...thrift.RegisterOption) interface{} { return func(p ServerParams) ServerResult { procedures := adminserviceserver.New(p.Handler, opts...) return ServerResult{Procedures: procedures} } } ================================================ FILE: .gen/go/admin/adminserviceserver/server.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package adminserviceserver import ( context "context" stream "go.uber.org/thriftrw/protocol/stream" wire "go.uber.org/thriftrw/wire" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" yarpcerrors "go.uber.org/yarpc/yarpcerrors" admin "github.com/uber/cadence/.gen/go/admin" replicator "github.com/uber/cadence/.gen/go/replicator" shared "github.com/uber/cadence/.gen/go/shared" ) // Interface is the server-side interface for the AdminService service. type Interface interface { AddSearchAttribute( ctx context.Context, Request *admin.AddSearchAttributeRequest, ) error CloseShard( ctx context.Context, Request *shared.CloseShardRequest, ) error DeleteWorkflow( ctx context.Context, Request *admin.AdminDeleteWorkflowRequest, ) (*admin.AdminDeleteWorkflowResponse, error) DescribeCluster( ctx context.Context, ) (*admin.DescribeClusterResponse, error) DescribeHistoryHost( ctx context.Context, Request *shared.DescribeHistoryHostRequest, ) (*shared.DescribeHistoryHostResponse, error) DescribeQueue( ctx context.Context, Request *shared.DescribeQueueRequest, ) (*shared.DescribeQueueResponse, error) DescribeShardDistribution( ctx context.Context, Request *shared.DescribeShardDistributionRequest, ) (*shared.DescribeShardDistributionResponse, error) DescribeWorkflowExecution( ctx context.Context, Request *admin.DescribeWorkflowExecutionRequest, ) (*admin.DescribeWorkflowExecutionResponse, error) GetCrossClusterTasks( ctx context.Context, Request *shared.GetCrossClusterTasksRequest, ) (*shared.GetCrossClusterTasksResponse, error) GetDLQReplicationMessages( ctx context.Context, Request *replicator.GetDLQReplicationMessagesRequest, ) (*replicator.GetDLQReplicationMessagesResponse, error) GetDomainAsyncWorkflowConfiguraton( ctx context.Context, Request *admin.GetDomainAsyncWorkflowConfiguratonRequest, ) (*admin.GetDomainAsyncWorkflowConfiguratonResponse, error) GetDomainIsolationGroups( ctx context.Context, Request *admin.GetDomainIsolationGroupsRequest, ) (*admin.GetDomainIsolationGroupsResponse, error) GetDomainReplicationMessages( ctx context.Context, Request *replicator.GetDomainReplicationMessagesRequest, ) (*replicator.GetDomainReplicationMessagesResponse, error) GetDynamicConfig( ctx context.Context, Request *admin.GetDynamicConfigRequest, ) (*admin.GetDynamicConfigResponse, error) GetGlobalIsolationGroups( ctx context.Context, Request *admin.GetGlobalIsolationGroupsRequest, ) (*admin.GetGlobalIsolationGroupsResponse, error) GetReplicationMessages( ctx context.Context, Request *replicator.GetReplicationMessagesRequest, ) (*replicator.GetReplicationMessagesResponse, error) GetWorkflowExecutionRawHistoryV2( ctx context.Context, GetRequest *admin.GetWorkflowExecutionRawHistoryV2Request, ) (*admin.GetWorkflowExecutionRawHistoryV2Response, error) ListDynamicConfig( ctx context.Context, Request *admin.ListDynamicConfigRequest, ) (*admin.ListDynamicConfigResponse, error) MaintainCorruptWorkflow( ctx context.Context, Request *admin.AdminMaintainWorkflowRequest, ) (*admin.AdminMaintainWorkflowResponse, error) MergeDLQMessages( ctx context.Context, Request *replicator.MergeDLQMessagesRequest, ) (*replicator.MergeDLQMessagesResponse, error) PurgeDLQMessages( ctx context.Context, Request *replicator.PurgeDLQMessagesRequest, ) error ReadDLQMessages( ctx context.Context, Request *replicator.ReadDLQMessagesRequest, ) (*replicator.ReadDLQMessagesResponse, error) ReapplyEvents( ctx context.Context, ReapplyEventsRequest *shared.ReapplyEventsRequest, ) error RefreshWorkflowTasks( ctx context.Context, Request *shared.RefreshWorkflowTasksRequest, ) error RemoveTask( ctx context.Context, Request *shared.RemoveTaskRequest, ) error ResendReplicationTasks( ctx context.Context, Request *admin.ResendReplicationTasksRequest, ) error ResetQueue( ctx context.Context, Request *shared.ResetQueueRequest, ) error RespondCrossClusterTasksCompleted( ctx context.Context, Request *shared.RespondCrossClusterTasksCompletedRequest, ) (*shared.RespondCrossClusterTasksCompletedResponse, error) RestoreDynamicConfig( ctx context.Context, Request *admin.RestoreDynamicConfigRequest, ) error UpdateDomainAsyncWorkflowConfiguraton( ctx context.Context, Request *admin.UpdateDomainAsyncWorkflowConfiguratonRequest, ) (*admin.UpdateDomainAsyncWorkflowConfiguratonResponse, error) UpdateDomainIsolationGroups( ctx context.Context, Request *admin.UpdateDomainIsolationGroupsRequest, ) (*admin.UpdateDomainIsolationGroupsResponse, error) UpdateDynamicConfig( ctx context.Context, Request *admin.UpdateDynamicConfigRequest, ) error UpdateGlobalIsolationGroups( ctx context.Context, Request *admin.UpdateGlobalIsolationGroupsRequest, ) (*admin.UpdateGlobalIsolationGroupsResponse, error) } // New prepares an implementation of the AdminService service for // registration. // // handler := AdminServiceHandler{} // dispatcher.Register(adminserviceserver.New(handler)) func New(impl Interface, opts ...thrift.RegisterOption) []transport.Procedure { h := handler{impl} service := thrift.Service{ Name: "AdminService", Methods: []thrift.Method{ thrift.Method{ Name: "AddSearchAttribute", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.AddSearchAttribute), NoWire: addsearchattribute_NoWireHandler{impl}, }, Signature: "AddSearchAttribute(Request *admin.AddSearchAttributeRequest)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "CloseShard", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.CloseShard), NoWire: closeshard_NoWireHandler{impl}, }, Signature: "CloseShard(Request *shared.CloseShardRequest)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "DeleteWorkflow", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DeleteWorkflow), NoWire: deleteworkflow_NoWireHandler{impl}, }, Signature: "DeleteWorkflow(Request *admin.AdminDeleteWorkflowRequest) (*admin.AdminDeleteWorkflowResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "DescribeCluster", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeCluster), NoWire: describecluster_NoWireHandler{impl}, }, Signature: "DescribeCluster() (*admin.DescribeClusterResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "DescribeHistoryHost", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeHistoryHost), NoWire: describehistoryhost_NoWireHandler{impl}, }, Signature: "DescribeHistoryHost(Request *shared.DescribeHistoryHostRequest) (*shared.DescribeHistoryHostResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "DescribeQueue", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeQueue), NoWire: describequeue_NoWireHandler{impl}, }, Signature: "DescribeQueue(Request *shared.DescribeQueueRequest) (*shared.DescribeQueueResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "DescribeShardDistribution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeShardDistribution), NoWire: describesharddistribution_NoWireHandler{impl}, }, Signature: "DescribeShardDistribution(Request *shared.DescribeShardDistributionRequest) (*shared.DescribeShardDistributionResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "DescribeWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeWorkflowExecution), NoWire: describeworkflowexecution_NoWireHandler{impl}, }, Signature: "DescribeWorkflowExecution(Request *admin.DescribeWorkflowExecutionRequest) (*admin.DescribeWorkflowExecutionResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "GetCrossClusterTasks", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetCrossClusterTasks), NoWire: getcrossclustertasks_NoWireHandler{impl}, }, Signature: "GetCrossClusterTasks(Request *shared.GetCrossClusterTasksRequest) (*shared.GetCrossClusterTasksResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "GetDLQReplicationMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetDLQReplicationMessages), NoWire: getdlqreplicationmessages_NoWireHandler{impl}, }, Signature: "GetDLQReplicationMessages(Request *replicator.GetDLQReplicationMessagesRequest) (*replicator.GetDLQReplicationMessagesResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "GetDomainAsyncWorkflowConfiguraton", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetDomainAsyncWorkflowConfiguraton), NoWire: getdomainasyncworkflowconfiguraton_NoWireHandler{impl}, }, Signature: "GetDomainAsyncWorkflowConfiguraton(Request *admin.GetDomainAsyncWorkflowConfiguratonRequest) (*admin.GetDomainAsyncWorkflowConfiguratonResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "GetDomainIsolationGroups", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetDomainIsolationGroups), NoWire: getdomainisolationgroups_NoWireHandler{impl}, }, Signature: "GetDomainIsolationGroups(Request *admin.GetDomainIsolationGroupsRequest) (*admin.GetDomainIsolationGroupsResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "GetDomainReplicationMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetDomainReplicationMessages), NoWire: getdomainreplicationmessages_NoWireHandler{impl}, }, Signature: "GetDomainReplicationMessages(Request *replicator.GetDomainReplicationMessagesRequest) (*replicator.GetDomainReplicationMessagesResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "GetDynamicConfig", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetDynamicConfig), NoWire: getdynamicconfig_NoWireHandler{impl}, }, Signature: "GetDynamicConfig(Request *admin.GetDynamicConfigRequest) (*admin.GetDynamicConfigResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "GetGlobalIsolationGroups", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetGlobalIsolationGroups), NoWire: getglobalisolationgroups_NoWireHandler{impl}, }, Signature: "GetGlobalIsolationGroups(Request *admin.GetGlobalIsolationGroupsRequest) (*admin.GetGlobalIsolationGroupsResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "GetReplicationMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetReplicationMessages), NoWire: getreplicationmessages_NoWireHandler{impl}, }, Signature: "GetReplicationMessages(Request *replicator.GetReplicationMessagesRequest) (*replicator.GetReplicationMessagesResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "GetWorkflowExecutionRawHistoryV2", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetWorkflowExecutionRawHistoryV2), NoWire: getworkflowexecutionrawhistoryv2_NoWireHandler{impl}, }, Signature: "GetWorkflowExecutionRawHistoryV2(GetRequest *admin.GetWorkflowExecutionRawHistoryV2Request) (*admin.GetWorkflowExecutionRawHistoryV2Response)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "ListDynamicConfig", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ListDynamicConfig), NoWire: listdynamicconfig_NoWireHandler{impl}, }, Signature: "ListDynamicConfig(Request *admin.ListDynamicConfigRequest) (*admin.ListDynamicConfigResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "MaintainCorruptWorkflow", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.MaintainCorruptWorkflow), NoWire: maintaincorruptworkflow_NoWireHandler{impl}, }, Signature: "MaintainCorruptWorkflow(Request *admin.AdminMaintainWorkflowRequest) (*admin.AdminMaintainWorkflowResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "MergeDLQMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.MergeDLQMessages), NoWire: mergedlqmessages_NoWireHandler{impl}, }, Signature: "MergeDLQMessages(Request *replicator.MergeDLQMessagesRequest) (*replicator.MergeDLQMessagesResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "PurgeDLQMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.PurgeDLQMessages), NoWire: purgedlqmessages_NoWireHandler{impl}, }, Signature: "PurgeDLQMessages(Request *replicator.PurgeDLQMessagesRequest)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "ReadDLQMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ReadDLQMessages), NoWire: readdlqmessages_NoWireHandler{impl}, }, Signature: "ReadDLQMessages(Request *replicator.ReadDLQMessagesRequest) (*replicator.ReadDLQMessagesResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "ReapplyEvents", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ReapplyEvents), NoWire: reapplyevents_NoWireHandler{impl}, }, Signature: "ReapplyEvents(ReapplyEventsRequest *shared.ReapplyEventsRequest)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "RefreshWorkflowTasks", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RefreshWorkflowTasks), NoWire: refreshworkflowtasks_NoWireHandler{impl}, }, Signature: "RefreshWorkflowTasks(Request *shared.RefreshWorkflowTasksRequest)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "RemoveTask", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RemoveTask), NoWire: removetask_NoWireHandler{impl}, }, Signature: "RemoveTask(Request *shared.RemoveTaskRequest)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "ResendReplicationTasks", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ResendReplicationTasks), NoWire: resendreplicationtasks_NoWireHandler{impl}, }, Signature: "ResendReplicationTasks(Request *admin.ResendReplicationTasksRequest)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "ResetQueue", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ResetQueue), NoWire: resetqueue_NoWireHandler{impl}, }, Signature: "ResetQueue(Request *shared.ResetQueueRequest)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "RespondCrossClusterTasksCompleted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondCrossClusterTasksCompleted), NoWire: respondcrossclustertaskscompleted_NoWireHandler{impl}, }, Signature: "RespondCrossClusterTasksCompleted(Request *shared.RespondCrossClusterTasksCompletedRequest) (*shared.RespondCrossClusterTasksCompletedResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "RestoreDynamicConfig", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RestoreDynamicConfig), NoWire: restoredynamicconfig_NoWireHandler{impl}, }, Signature: "RestoreDynamicConfig(Request *admin.RestoreDynamicConfigRequest)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "UpdateDomainAsyncWorkflowConfiguraton", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.UpdateDomainAsyncWorkflowConfiguraton), NoWire: updatedomainasyncworkflowconfiguraton_NoWireHandler{impl}, }, Signature: "UpdateDomainAsyncWorkflowConfiguraton(Request *admin.UpdateDomainAsyncWorkflowConfiguratonRequest) (*admin.UpdateDomainAsyncWorkflowConfiguratonResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "UpdateDomainIsolationGroups", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.UpdateDomainIsolationGroups), NoWire: updatedomainisolationgroups_NoWireHandler{impl}, }, Signature: "UpdateDomainIsolationGroups(Request *admin.UpdateDomainIsolationGroupsRequest) (*admin.UpdateDomainIsolationGroupsResponse)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "UpdateDynamicConfig", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.UpdateDynamicConfig), NoWire: updatedynamicconfig_NoWireHandler{impl}, }, Signature: "UpdateDynamicConfig(Request *admin.UpdateDynamicConfigRequest)", ThriftModule: admin.ThriftModule, }, thrift.Method{ Name: "UpdateGlobalIsolationGroups", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.UpdateGlobalIsolationGroups), NoWire: updateglobalisolationgroups_NoWireHandler{impl}, }, Signature: "UpdateGlobalIsolationGroups(Request *admin.UpdateGlobalIsolationGroupsRequest) (*admin.UpdateGlobalIsolationGroupsResponse)", ThriftModule: admin.ThriftModule, }, }, } procedures := make([]transport.Procedure, 0, 33) procedures = append(procedures, thrift.BuildProcedures(service, opts...)...) return procedures } type handler struct{ impl Interface } type yarpcErrorNamer interface{ YARPCErrorName() string } type yarpcErrorCoder interface{ YARPCErrorCode() *yarpcerrors.Code } func (h handler) AddSearchAttribute(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_AddSearchAttribute_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'AddSearchAttribute': %w", err) } appErr := h.impl.AddSearchAttribute(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_AddSearchAttribute_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) CloseShard(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_CloseShard_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'CloseShard': %w", err) } appErr := h.impl.CloseShard(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_CloseShard_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DeleteWorkflow(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_DeleteWorkflow_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'DeleteWorkflow': %w", err) } success, appErr := h.impl.DeleteWorkflow(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_DeleteWorkflow_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeCluster(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_DescribeCluster_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'DescribeCluster': %w", err) } success, appErr := h.impl.DescribeCluster(ctx) hadError := appErr != nil result, err := admin.AdminService_DescribeCluster_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeHistoryHost(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_DescribeHistoryHost_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'DescribeHistoryHost': %w", err) } success, appErr := h.impl.DescribeHistoryHost(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_DescribeHistoryHost_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeQueue(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_DescribeQueue_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'DescribeQueue': %w", err) } success, appErr := h.impl.DescribeQueue(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_DescribeQueue_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeShardDistribution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_DescribeShardDistribution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'DescribeShardDistribution': %w", err) } success, appErr := h.impl.DescribeShardDistribution(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_DescribeShardDistribution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_DescribeWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'DescribeWorkflowExecution': %w", err) } success, appErr := h.impl.DescribeWorkflowExecution(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_DescribeWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetCrossClusterTasks(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_GetCrossClusterTasks_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'GetCrossClusterTasks': %w", err) } success, appErr := h.impl.GetCrossClusterTasks(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetCrossClusterTasks_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetDLQReplicationMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_GetDLQReplicationMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'GetDLQReplicationMessages': %w", err) } success, appErr := h.impl.GetDLQReplicationMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetDLQReplicationMessages_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_GetDomainAsyncWorkflowConfiguraton_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'GetDomainAsyncWorkflowConfiguraton': %w", err) } success, appErr := h.impl.GetDomainAsyncWorkflowConfiguraton(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetDomainAsyncWorkflowConfiguraton_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetDomainIsolationGroups(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_GetDomainIsolationGroups_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'GetDomainIsolationGroups': %w", err) } success, appErr := h.impl.GetDomainIsolationGroups(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetDomainIsolationGroups_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetDomainReplicationMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_GetDomainReplicationMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'GetDomainReplicationMessages': %w", err) } success, appErr := h.impl.GetDomainReplicationMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetDomainReplicationMessages_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetDynamicConfig(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_GetDynamicConfig_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'GetDynamicConfig': %w", err) } success, appErr := h.impl.GetDynamicConfig(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetDynamicConfig_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetGlobalIsolationGroups(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_GetGlobalIsolationGroups_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'GetGlobalIsolationGroups': %w", err) } success, appErr := h.impl.GetGlobalIsolationGroups(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetGlobalIsolationGroups_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetReplicationMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_GetReplicationMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'GetReplicationMessages': %w", err) } success, appErr := h.impl.GetReplicationMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetReplicationMessages_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetWorkflowExecutionRawHistoryV2(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_GetWorkflowExecutionRawHistoryV2_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'GetWorkflowExecutionRawHistoryV2': %w", err) } success, appErr := h.impl.GetWorkflowExecutionRawHistoryV2(ctx, args.GetRequest) hadError := appErr != nil result, err := admin.AdminService_GetWorkflowExecutionRawHistoryV2_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ListDynamicConfig(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_ListDynamicConfig_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'ListDynamicConfig': %w", err) } success, appErr := h.impl.ListDynamicConfig(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_ListDynamicConfig_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) MaintainCorruptWorkflow(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_MaintainCorruptWorkflow_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'MaintainCorruptWorkflow': %w", err) } success, appErr := h.impl.MaintainCorruptWorkflow(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_MaintainCorruptWorkflow_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) MergeDLQMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_MergeDLQMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'MergeDLQMessages': %w", err) } success, appErr := h.impl.MergeDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_MergeDLQMessages_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) PurgeDLQMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_PurgeDLQMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'PurgeDLQMessages': %w", err) } appErr := h.impl.PurgeDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_PurgeDLQMessages_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ReadDLQMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_ReadDLQMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'ReadDLQMessages': %w", err) } success, appErr := h.impl.ReadDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_ReadDLQMessages_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ReapplyEvents(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_ReapplyEvents_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'ReapplyEvents': %w", err) } appErr := h.impl.ReapplyEvents(ctx, args.ReapplyEventsRequest) hadError := appErr != nil result, err := admin.AdminService_ReapplyEvents_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RefreshWorkflowTasks(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_RefreshWorkflowTasks_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'RefreshWorkflowTasks': %w", err) } appErr := h.impl.RefreshWorkflowTasks(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_RefreshWorkflowTasks_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RemoveTask(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_RemoveTask_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'RemoveTask': %w", err) } appErr := h.impl.RemoveTask(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_RemoveTask_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ResendReplicationTasks(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_ResendReplicationTasks_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'ResendReplicationTasks': %w", err) } appErr := h.impl.ResendReplicationTasks(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_ResendReplicationTasks_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ResetQueue(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_ResetQueue_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'ResetQueue': %w", err) } appErr := h.impl.ResetQueue(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_ResetQueue_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondCrossClusterTasksCompleted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_RespondCrossClusterTasksCompleted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'RespondCrossClusterTasksCompleted': %w", err) } success, appErr := h.impl.RespondCrossClusterTasksCompleted(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_RespondCrossClusterTasksCompleted_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RestoreDynamicConfig(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_RestoreDynamicConfig_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'RestoreDynamicConfig': %w", err) } appErr := h.impl.RestoreDynamicConfig(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_RestoreDynamicConfig_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'UpdateDomainAsyncWorkflowConfiguraton': %w", err) } success, appErr := h.impl.UpdateDomainAsyncWorkflowConfiguraton(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) UpdateDomainIsolationGroups(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_UpdateDomainIsolationGroups_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'UpdateDomainIsolationGroups': %w", err) } success, appErr := h.impl.UpdateDomainIsolationGroups(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_UpdateDomainIsolationGroups_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) UpdateDynamicConfig(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_UpdateDynamicConfig_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'UpdateDynamicConfig': %w", err) } appErr := h.impl.UpdateDynamicConfig(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_UpdateDynamicConfig_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) UpdateGlobalIsolationGroups(ctx context.Context, body wire.Value) (thrift.Response, error) { var args admin.AdminService_UpdateGlobalIsolationGroups_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'AdminService' procedure 'UpdateGlobalIsolationGroups': %w", err) } success, appErr := h.impl.UpdateGlobalIsolationGroups(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_UpdateGlobalIsolationGroups_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type addsearchattribute_NoWireHandler struct{ impl Interface } func (h addsearchattribute_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_AddSearchAttribute_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'AddSearchAttribute': %w", err) } appErr := h.impl.AddSearchAttribute(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_AddSearchAttribute_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type closeshard_NoWireHandler struct{ impl Interface } func (h closeshard_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_CloseShard_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'CloseShard': %w", err) } appErr := h.impl.CloseShard(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_CloseShard_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type deleteworkflow_NoWireHandler struct{ impl Interface } func (h deleteworkflow_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_DeleteWorkflow_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'DeleteWorkflow': %w", err) } success, appErr := h.impl.DeleteWorkflow(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_DeleteWorkflow_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describecluster_NoWireHandler struct{ impl Interface } func (h describecluster_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_DescribeCluster_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'DescribeCluster': %w", err) } success, appErr := h.impl.DescribeCluster(ctx) hadError := appErr != nil result, err := admin.AdminService_DescribeCluster_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describehistoryhost_NoWireHandler struct{ impl Interface } func (h describehistoryhost_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_DescribeHistoryHost_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'DescribeHistoryHost': %w", err) } success, appErr := h.impl.DescribeHistoryHost(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_DescribeHistoryHost_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describequeue_NoWireHandler struct{ impl Interface } func (h describequeue_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_DescribeQueue_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'DescribeQueue': %w", err) } success, appErr := h.impl.DescribeQueue(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_DescribeQueue_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describesharddistribution_NoWireHandler struct{ impl Interface } func (h describesharddistribution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_DescribeShardDistribution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'DescribeShardDistribution': %w", err) } success, appErr := h.impl.DescribeShardDistribution(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_DescribeShardDistribution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describeworkflowexecution_NoWireHandler struct{ impl Interface } func (h describeworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_DescribeWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'DescribeWorkflowExecution': %w", err) } success, appErr := h.impl.DescribeWorkflowExecution(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_DescribeWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getcrossclustertasks_NoWireHandler struct{ impl Interface } func (h getcrossclustertasks_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_GetCrossClusterTasks_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'GetCrossClusterTasks': %w", err) } success, appErr := h.impl.GetCrossClusterTasks(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetCrossClusterTasks_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getdlqreplicationmessages_NoWireHandler struct{ impl Interface } func (h getdlqreplicationmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_GetDLQReplicationMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'GetDLQReplicationMessages': %w", err) } success, appErr := h.impl.GetDLQReplicationMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetDLQReplicationMessages_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getdomainasyncworkflowconfiguraton_NoWireHandler struct{ impl Interface } func (h getdomainasyncworkflowconfiguraton_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_GetDomainAsyncWorkflowConfiguraton_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'GetDomainAsyncWorkflowConfiguraton': %w", err) } success, appErr := h.impl.GetDomainAsyncWorkflowConfiguraton(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetDomainAsyncWorkflowConfiguraton_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getdomainisolationgroups_NoWireHandler struct{ impl Interface } func (h getdomainisolationgroups_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_GetDomainIsolationGroups_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'GetDomainIsolationGroups': %w", err) } success, appErr := h.impl.GetDomainIsolationGroups(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetDomainIsolationGroups_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getdomainreplicationmessages_NoWireHandler struct{ impl Interface } func (h getdomainreplicationmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_GetDomainReplicationMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'GetDomainReplicationMessages': %w", err) } success, appErr := h.impl.GetDomainReplicationMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetDomainReplicationMessages_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getdynamicconfig_NoWireHandler struct{ impl Interface } func (h getdynamicconfig_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_GetDynamicConfig_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'GetDynamicConfig': %w", err) } success, appErr := h.impl.GetDynamicConfig(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetDynamicConfig_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getglobalisolationgroups_NoWireHandler struct{ impl Interface } func (h getglobalisolationgroups_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_GetGlobalIsolationGroups_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'GetGlobalIsolationGroups': %w", err) } success, appErr := h.impl.GetGlobalIsolationGroups(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetGlobalIsolationGroups_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getreplicationmessages_NoWireHandler struct{ impl Interface } func (h getreplicationmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_GetReplicationMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'GetReplicationMessages': %w", err) } success, appErr := h.impl.GetReplicationMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_GetReplicationMessages_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getworkflowexecutionrawhistoryv2_NoWireHandler struct{ impl Interface } func (h getworkflowexecutionrawhistoryv2_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_GetWorkflowExecutionRawHistoryV2_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'GetWorkflowExecutionRawHistoryV2': %w", err) } success, appErr := h.impl.GetWorkflowExecutionRawHistoryV2(ctx, args.GetRequest) hadError := appErr != nil result, err := admin.AdminService_GetWorkflowExecutionRawHistoryV2_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type listdynamicconfig_NoWireHandler struct{ impl Interface } func (h listdynamicconfig_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_ListDynamicConfig_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'ListDynamicConfig': %w", err) } success, appErr := h.impl.ListDynamicConfig(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_ListDynamicConfig_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type maintaincorruptworkflow_NoWireHandler struct{ impl Interface } func (h maintaincorruptworkflow_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_MaintainCorruptWorkflow_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'MaintainCorruptWorkflow': %w", err) } success, appErr := h.impl.MaintainCorruptWorkflow(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_MaintainCorruptWorkflow_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type mergedlqmessages_NoWireHandler struct{ impl Interface } func (h mergedlqmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_MergeDLQMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'MergeDLQMessages': %w", err) } success, appErr := h.impl.MergeDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_MergeDLQMessages_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type purgedlqmessages_NoWireHandler struct{ impl Interface } func (h purgedlqmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_PurgeDLQMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'PurgeDLQMessages': %w", err) } appErr := h.impl.PurgeDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_PurgeDLQMessages_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type readdlqmessages_NoWireHandler struct{ impl Interface } func (h readdlqmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_ReadDLQMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'ReadDLQMessages': %w", err) } success, appErr := h.impl.ReadDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_ReadDLQMessages_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type reapplyevents_NoWireHandler struct{ impl Interface } func (h reapplyevents_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_ReapplyEvents_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'ReapplyEvents': %w", err) } appErr := h.impl.ReapplyEvents(ctx, args.ReapplyEventsRequest) hadError := appErr != nil result, err := admin.AdminService_ReapplyEvents_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type refreshworkflowtasks_NoWireHandler struct{ impl Interface } func (h refreshworkflowtasks_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_RefreshWorkflowTasks_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'RefreshWorkflowTasks': %w", err) } appErr := h.impl.RefreshWorkflowTasks(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_RefreshWorkflowTasks_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type removetask_NoWireHandler struct{ impl Interface } func (h removetask_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_RemoveTask_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'RemoveTask': %w", err) } appErr := h.impl.RemoveTask(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_RemoveTask_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type resendreplicationtasks_NoWireHandler struct{ impl Interface } func (h resendreplicationtasks_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_ResendReplicationTasks_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'ResendReplicationTasks': %w", err) } appErr := h.impl.ResendReplicationTasks(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_ResendReplicationTasks_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type resetqueue_NoWireHandler struct{ impl Interface } func (h resetqueue_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_ResetQueue_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'ResetQueue': %w", err) } appErr := h.impl.ResetQueue(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_ResetQueue_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondcrossclustertaskscompleted_NoWireHandler struct{ impl Interface } func (h respondcrossclustertaskscompleted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_RespondCrossClusterTasksCompleted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'RespondCrossClusterTasksCompleted': %w", err) } success, appErr := h.impl.RespondCrossClusterTasksCompleted(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_RespondCrossClusterTasksCompleted_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type restoredynamicconfig_NoWireHandler struct{ impl Interface } func (h restoredynamicconfig_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_RestoreDynamicConfig_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'RestoreDynamicConfig': %w", err) } appErr := h.impl.RestoreDynamicConfig(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_RestoreDynamicConfig_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type updatedomainasyncworkflowconfiguraton_NoWireHandler struct{ impl Interface } func (h updatedomainasyncworkflowconfiguraton_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_UpdateDomainAsyncWorkflowConfiguraton_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'UpdateDomainAsyncWorkflowConfiguraton': %w", err) } success, appErr := h.impl.UpdateDomainAsyncWorkflowConfiguraton(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_UpdateDomainAsyncWorkflowConfiguraton_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type updatedomainisolationgroups_NoWireHandler struct{ impl Interface } func (h updatedomainisolationgroups_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_UpdateDomainIsolationGroups_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'UpdateDomainIsolationGroups': %w", err) } success, appErr := h.impl.UpdateDomainIsolationGroups(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_UpdateDomainIsolationGroups_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type updatedynamicconfig_NoWireHandler struct{ impl Interface } func (h updatedynamicconfig_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_UpdateDynamicConfig_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'UpdateDynamicConfig': %w", err) } appErr := h.impl.UpdateDynamicConfig(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_UpdateDynamicConfig_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type updateglobalisolationgroups_NoWireHandler struct{ impl Interface } func (h updateglobalisolationgroups_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args admin.AdminService_UpdateGlobalIsolationGroups_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'AdminService' procedure 'UpdateGlobalIsolationGroups': %w", err) } success, appErr := h.impl.UpdateGlobalIsolationGroups(ctx, args.Request) hadError := appErr != nil result, err := admin.AdminService_UpdateGlobalIsolationGroups_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } ================================================ FILE: .gen/go/admin/adminservicetest/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package adminservicetest import ( context "context" gomock "github.com/golang/mock/gomock" yarpc "go.uber.org/yarpc" admin "github.com/uber/cadence/.gen/go/admin" adminserviceclient "github.com/uber/cadence/.gen/go/admin/adminserviceclient" replicator "github.com/uber/cadence/.gen/go/replicator" shared "github.com/uber/cadence/.gen/go/shared" ) // MockClient implements a gomock-compatible mock client for service // AdminService. type MockClient struct { ctrl *gomock.Controller recorder *_MockClientRecorder } var _ adminserviceclient.Interface = (*MockClient)(nil) type _MockClientRecorder struct { mock *MockClient } // Build a new mock client for service AdminService. // // mockCtrl := gomock.NewController(t) // client := adminservicetest.NewMockClient(mockCtrl) // // Use EXPECT() to set expectations on the mock. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &_MockClientRecorder{mock} return mock } // EXPECT returns an object that allows you to define an expectation on the // AdminService mock client. func (m *MockClient) EXPECT() *_MockClientRecorder { return m.recorder } // AddSearchAttribute responds to a AddSearchAttribute call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().AddSearchAttribute(gomock.Any(), ...).Return(...) // ... := client.AddSearchAttribute(...) func (m *MockClient) AddSearchAttribute( ctx context.Context, _Request *admin.AddSearchAttributeRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "AddSearchAttribute", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) AddSearchAttribute( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "AddSearchAttribute", args...) } // CloseShard responds to a CloseShard call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().CloseShard(gomock.Any(), ...).Return(...) // ... := client.CloseShard(...) func (m *MockClient) CloseShard( ctx context.Context, _Request *shared.CloseShardRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "CloseShard", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) CloseShard( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "CloseShard", args...) } // DeleteWorkflow responds to a DeleteWorkflow call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DeleteWorkflow(gomock.Any(), ...).Return(...) // ... := client.DeleteWorkflow(...) func (m *MockClient) DeleteWorkflow( ctx context.Context, _Request *admin.AdminDeleteWorkflowRequest, opts ...yarpc.CallOption, ) (success *admin.AdminDeleteWorkflowResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DeleteWorkflow", args...) success, _ = ret[i].(*admin.AdminDeleteWorkflowResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DeleteWorkflow( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DeleteWorkflow", args...) } // DescribeCluster responds to a DescribeCluster call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeCluster(gomock.Any(), ...).Return(...) // ... := client.DescribeCluster(...) func (m *MockClient) DescribeCluster( ctx context.Context, opts ...yarpc.CallOption, ) (success *admin.DescribeClusterResponse, err error) { args := []interface{}{ctx} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeCluster", args...) success, _ = ret[i].(*admin.DescribeClusterResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeCluster( ctx interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeCluster", args...) } // DescribeHistoryHost responds to a DescribeHistoryHost call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeHistoryHost(gomock.Any(), ...).Return(...) // ... := client.DescribeHistoryHost(...) func (m *MockClient) DescribeHistoryHost( ctx context.Context, _Request *shared.DescribeHistoryHostRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeHistoryHostResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeHistoryHost", args...) success, _ = ret[i].(*shared.DescribeHistoryHostResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeHistoryHost( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeHistoryHost", args...) } // DescribeQueue responds to a DescribeQueue call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeQueue(gomock.Any(), ...).Return(...) // ... := client.DescribeQueue(...) func (m *MockClient) DescribeQueue( ctx context.Context, _Request *shared.DescribeQueueRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeQueueResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeQueue", args...) success, _ = ret[i].(*shared.DescribeQueueResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeQueue( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeQueue", args...) } // DescribeShardDistribution responds to a DescribeShardDistribution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeShardDistribution(gomock.Any(), ...).Return(...) // ... := client.DescribeShardDistribution(...) func (m *MockClient) DescribeShardDistribution( ctx context.Context, _Request *shared.DescribeShardDistributionRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeShardDistributionResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeShardDistribution", args...) success, _ = ret[i].(*shared.DescribeShardDistributionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeShardDistribution( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeShardDistribution", args...) } // DescribeWorkflowExecution responds to a DescribeWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.DescribeWorkflowExecution(...) func (m *MockClient) DescribeWorkflowExecution( ctx context.Context, _Request *admin.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *admin.DescribeWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeWorkflowExecution", args...) success, _ = ret[i].(*admin.DescribeWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeWorkflowExecution( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeWorkflowExecution", args...) } // GetCrossClusterTasks responds to a GetCrossClusterTasks call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetCrossClusterTasks(gomock.Any(), ...).Return(...) // ... := client.GetCrossClusterTasks(...) func (m *MockClient) GetCrossClusterTasks( ctx context.Context, _Request *shared.GetCrossClusterTasksRequest, opts ...yarpc.CallOption, ) (success *shared.GetCrossClusterTasksResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetCrossClusterTasks", args...) success, _ = ret[i].(*shared.GetCrossClusterTasksResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetCrossClusterTasks( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetCrossClusterTasks", args...) } // GetDLQReplicationMessages responds to a GetDLQReplicationMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetDLQReplicationMessages(gomock.Any(), ...).Return(...) // ... := client.GetDLQReplicationMessages(...) func (m *MockClient) GetDLQReplicationMessages( ctx context.Context, _Request *replicator.GetDLQReplicationMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.GetDLQReplicationMessagesResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetDLQReplicationMessages", args...) success, _ = ret[i].(*replicator.GetDLQReplicationMessagesResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetDLQReplicationMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetDLQReplicationMessages", args...) } // GetDomainAsyncWorkflowConfiguraton responds to a GetDomainAsyncWorkflowConfiguraton call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetDomainAsyncWorkflowConfiguraton(gomock.Any(), ...).Return(...) // ... := client.GetDomainAsyncWorkflowConfiguraton(...) func (m *MockClient) GetDomainAsyncWorkflowConfiguraton( ctx context.Context, _Request *admin.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption, ) (success *admin.GetDomainAsyncWorkflowConfiguratonResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetDomainAsyncWorkflowConfiguraton", args...) success, _ = ret[i].(*admin.GetDomainAsyncWorkflowConfiguratonResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetDomainAsyncWorkflowConfiguraton( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetDomainAsyncWorkflowConfiguraton", args...) } // GetDomainIsolationGroups responds to a GetDomainIsolationGroups call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetDomainIsolationGroups(gomock.Any(), ...).Return(...) // ... := client.GetDomainIsolationGroups(...) func (m *MockClient) GetDomainIsolationGroups( ctx context.Context, _Request *admin.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption, ) (success *admin.GetDomainIsolationGroupsResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetDomainIsolationGroups", args...) success, _ = ret[i].(*admin.GetDomainIsolationGroupsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetDomainIsolationGroups( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetDomainIsolationGroups", args...) } // GetDomainReplicationMessages responds to a GetDomainReplicationMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetDomainReplicationMessages(gomock.Any(), ...).Return(...) // ... := client.GetDomainReplicationMessages(...) func (m *MockClient) GetDomainReplicationMessages( ctx context.Context, _Request *replicator.GetDomainReplicationMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.GetDomainReplicationMessagesResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetDomainReplicationMessages", args...) success, _ = ret[i].(*replicator.GetDomainReplicationMessagesResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetDomainReplicationMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetDomainReplicationMessages", args...) } // GetDynamicConfig responds to a GetDynamicConfig call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetDynamicConfig(gomock.Any(), ...).Return(...) // ... := client.GetDynamicConfig(...) func (m *MockClient) GetDynamicConfig( ctx context.Context, _Request *admin.GetDynamicConfigRequest, opts ...yarpc.CallOption, ) (success *admin.GetDynamicConfigResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetDynamicConfig", args...) success, _ = ret[i].(*admin.GetDynamicConfigResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetDynamicConfig( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetDynamicConfig", args...) } // GetGlobalIsolationGroups responds to a GetGlobalIsolationGroups call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetGlobalIsolationGroups(gomock.Any(), ...).Return(...) // ... := client.GetGlobalIsolationGroups(...) func (m *MockClient) GetGlobalIsolationGroups( ctx context.Context, _Request *admin.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption, ) (success *admin.GetGlobalIsolationGroupsResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetGlobalIsolationGroups", args...) success, _ = ret[i].(*admin.GetGlobalIsolationGroupsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetGlobalIsolationGroups( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetGlobalIsolationGroups", args...) } // GetReplicationMessages responds to a GetReplicationMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetReplicationMessages(gomock.Any(), ...).Return(...) // ... := client.GetReplicationMessages(...) func (m *MockClient) GetReplicationMessages( ctx context.Context, _Request *replicator.GetReplicationMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.GetReplicationMessagesResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetReplicationMessages", args...) success, _ = ret[i].(*replicator.GetReplicationMessagesResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetReplicationMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetReplicationMessages", args...) } // GetWorkflowExecutionRawHistoryV2 responds to a GetWorkflowExecutionRawHistoryV2 call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetWorkflowExecutionRawHistoryV2(gomock.Any(), ...).Return(...) // ... := client.GetWorkflowExecutionRawHistoryV2(...) func (m *MockClient) GetWorkflowExecutionRawHistoryV2( ctx context.Context, _GetRequest *admin.GetWorkflowExecutionRawHistoryV2Request, opts ...yarpc.CallOption, ) (success *admin.GetWorkflowExecutionRawHistoryV2Response, err error) { args := []interface{}{ctx, _GetRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetWorkflowExecutionRawHistoryV2", args...) success, _ = ret[i].(*admin.GetWorkflowExecutionRawHistoryV2Response) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetWorkflowExecutionRawHistoryV2( ctx interface{}, _GetRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _GetRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetWorkflowExecutionRawHistoryV2", args...) } // ListDynamicConfig responds to a ListDynamicConfig call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ListDynamicConfig(gomock.Any(), ...).Return(...) // ... := client.ListDynamicConfig(...) func (m *MockClient) ListDynamicConfig( ctx context.Context, _Request *admin.ListDynamicConfigRequest, opts ...yarpc.CallOption, ) (success *admin.ListDynamicConfigResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ListDynamicConfig", args...) success, _ = ret[i].(*admin.ListDynamicConfigResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ListDynamicConfig( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ListDynamicConfig", args...) } // MaintainCorruptWorkflow responds to a MaintainCorruptWorkflow call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().MaintainCorruptWorkflow(gomock.Any(), ...).Return(...) // ... := client.MaintainCorruptWorkflow(...) func (m *MockClient) MaintainCorruptWorkflow( ctx context.Context, _Request *admin.AdminMaintainWorkflowRequest, opts ...yarpc.CallOption, ) (success *admin.AdminMaintainWorkflowResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "MaintainCorruptWorkflow", args...) success, _ = ret[i].(*admin.AdminMaintainWorkflowResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) MaintainCorruptWorkflow( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "MaintainCorruptWorkflow", args...) } // MergeDLQMessages responds to a MergeDLQMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().MergeDLQMessages(gomock.Any(), ...).Return(...) // ... := client.MergeDLQMessages(...) func (m *MockClient) MergeDLQMessages( ctx context.Context, _Request *replicator.MergeDLQMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.MergeDLQMessagesResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "MergeDLQMessages", args...) success, _ = ret[i].(*replicator.MergeDLQMessagesResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) MergeDLQMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "MergeDLQMessages", args...) } // PurgeDLQMessages responds to a PurgeDLQMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().PurgeDLQMessages(gomock.Any(), ...).Return(...) // ... := client.PurgeDLQMessages(...) func (m *MockClient) PurgeDLQMessages( ctx context.Context, _Request *replicator.PurgeDLQMessagesRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "PurgeDLQMessages", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) PurgeDLQMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "PurgeDLQMessages", args...) } // ReadDLQMessages responds to a ReadDLQMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ReadDLQMessages(gomock.Any(), ...).Return(...) // ... := client.ReadDLQMessages(...) func (m *MockClient) ReadDLQMessages( ctx context.Context, _Request *replicator.ReadDLQMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.ReadDLQMessagesResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ReadDLQMessages", args...) success, _ = ret[i].(*replicator.ReadDLQMessagesResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ReadDLQMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ReadDLQMessages", args...) } // ReapplyEvents responds to a ReapplyEvents call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ReapplyEvents(gomock.Any(), ...).Return(...) // ... := client.ReapplyEvents(...) func (m *MockClient) ReapplyEvents( ctx context.Context, _ReapplyEventsRequest *shared.ReapplyEventsRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _ReapplyEventsRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ReapplyEvents", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ReapplyEvents( ctx interface{}, _ReapplyEventsRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ReapplyEventsRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ReapplyEvents", args...) } // RefreshWorkflowTasks responds to a RefreshWorkflowTasks call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RefreshWorkflowTasks(gomock.Any(), ...).Return(...) // ... := client.RefreshWorkflowTasks(...) func (m *MockClient) RefreshWorkflowTasks( ctx context.Context, _Request *shared.RefreshWorkflowTasksRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RefreshWorkflowTasks", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RefreshWorkflowTasks( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RefreshWorkflowTasks", args...) } // RemoveTask responds to a RemoveTask call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RemoveTask(gomock.Any(), ...).Return(...) // ... := client.RemoveTask(...) func (m *MockClient) RemoveTask( ctx context.Context, _Request *shared.RemoveTaskRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RemoveTask", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RemoveTask( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RemoveTask", args...) } // ResendReplicationTasks responds to a ResendReplicationTasks call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ResendReplicationTasks(gomock.Any(), ...).Return(...) // ... := client.ResendReplicationTasks(...) func (m *MockClient) ResendReplicationTasks( ctx context.Context, _Request *admin.ResendReplicationTasksRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ResendReplicationTasks", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ResendReplicationTasks( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ResendReplicationTasks", args...) } // ResetQueue responds to a ResetQueue call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ResetQueue(gomock.Any(), ...).Return(...) // ... := client.ResetQueue(...) func (m *MockClient) ResetQueue( ctx context.Context, _Request *shared.ResetQueueRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ResetQueue", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ResetQueue( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ResetQueue", args...) } // RespondCrossClusterTasksCompleted responds to a RespondCrossClusterTasksCompleted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondCrossClusterTasksCompleted(gomock.Any(), ...).Return(...) // ... := client.RespondCrossClusterTasksCompleted(...) func (m *MockClient) RespondCrossClusterTasksCompleted( ctx context.Context, _Request *shared.RespondCrossClusterTasksCompletedRequest, opts ...yarpc.CallOption, ) (success *shared.RespondCrossClusterTasksCompletedResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondCrossClusterTasksCompleted", args...) success, _ = ret[i].(*shared.RespondCrossClusterTasksCompletedResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondCrossClusterTasksCompleted( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondCrossClusterTasksCompleted", args...) } // RestoreDynamicConfig responds to a RestoreDynamicConfig call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RestoreDynamicConfig(gomock.Any(), ...).Return(...) // ... := client.RestoreDynamicConfig(...) func (m *MockClient) RestoreDynamicConfig( ctx context.Context, _Request *admin.RestoreDynamicConfigRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RestoreDynamicConfig", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RestoreDynamicConfig( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RestoreDynamicConfig", args...) } // UpdateDomainAsyncWorkflowConfiguraton responds to a UpdateDomainAsyncWorkflowConfiguraton call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().UpdateDomainAsyncWorkflowConfiguraton(gomock.Any(), ...).Return(...) // ... := client.UpdateDomainAsyncWorkflowConfiguraton(...) func (m *MockClient) UpdateDomainAsyncWorkflowConfiguraton( ctx context.Context, _Request *admin.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption, ) (success *admin.UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "UpdateDomainAsyncWorkflowConfiguraton", args...) success, _ = ret[i].(*admin.UpdateDomainAsyncWorkflowConfiguratonResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) UpdateDomainAsyncWorkflowConfiguraton( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "UpdateDomainAsyncWorkflowConfiguraton", args...) } // UpdateDomainIsolationGroups responds to a UpdateDomainIsolationGroups call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().UpdateDomainIsolationGroups(gomock.Any(), ...).Return(...) // ... := client.UpdateDomainIsolationGroups(...) func (m *MockClient) UpdateDomainIsolationGroups( ctx context.Context, _Request *admin.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption, ) (success *admin.UpdateDomainIsolationGroupsResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "UpdateDomainIsolationGroups", args...) success, _ = ret[i].(*admin.UpdateDomainIsolationGroupsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) UpdateDomainIsolationGroups( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "UpdateDomainIsolationGroups", args...) } // UpdateDynamicConfig responds to a UpdateDynamicConfig call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().UpdateDynamicConfig(gomock.Any(), ...).Return(...) // ... := client.UpdateDynamicConfig(...) func (m *MockClient) UpdateDynamicConfig( ctx context.Context, _Request *admin.UpdateDynamicConfigRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "UpdateDynamicConfig", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) UpdateDynamicConfig( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "UpdateDynamicConfig", args...) } // UpdateGlobalIsolationGroups responds to a UpdateGlobalIsolationGroups call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().UpdateGlobalIsolationGroups(gomock.Any(), ...).Return(...) // ... := client.UpdateGlobalIsolationGroups(...) func (m *MockClient) UpdateGlobalIsolationGroups( ctx context.Context, _Request *admin.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption, ) (success *admin.UpdateGlobalIsolationGroupsResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "UpdateGlobalIsolationGroups", args...) success, _ = ret[i].(*admin.UpdateGlobalIsolationGroupsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) UpdateGlobalIsolationGroups( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "UpdateGlobalIsolationGroups", args...) } ================================================ FILE: .gen/go/admin/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package admin ================================================ FILE: .gen/go/cadence/cadence.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package cadence import ( errors "errors" fmt "fmt" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" shared "github.com/uber/cadence/.gen/go/shared" ) // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "cadence", Package: "github.com/uber/cadence/.gen/go/cadence", FilePath: "cadence.thrift", SHA1: "8c644a4a8acae7e865a84d625bc845ffae7ff693", Includes: []*thriftreflect.ThriftModule{ shared.ThriftModule, }, Raw: rawIDL, } const rawIDL = "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\ninclude \"shared.thrift\"\n\nnamespace java com.uber.cadence\n\n/**\n* WorkflowService API is exposed to provide support for long running applications. Application is expected to call\n* StartWorkflowExecution to create an instance for each instance of long running workflow. Such applications are expected\n* to have a worker which regularly polls for DecisionTask and ActivityTask from the WorkflowService. For each\n* DecisionTask, application is expected to process the history of events for that session and respond back with next\n* decisions. For each ActivityTask, application is expected to execute the actual logic for that task and respond back\n* with completion or failure. Worker is expected to regularly heartbeat while activity task is running.\n**/\nservice WorkflowService {\n /**\n * RegisterDomain creates a new domain which can be used as a container for all resources. Domain is a top level\n * entity within Cadence, used as a container for all resources like workflow executions, tasklists, etc. Domain\n * acts as a sandbox and provides isolation for all resources within the domain. All resources belongs to exactly one\n * domain.\n **/\n void RegisterDomain(1: shared.RegisterDomainRequest registerRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.DomainAlreadyExistsError domainExistsError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * DescribeDomain returns the information and configuration for a registered domain.\n **/\n shared.DescribeDomainResponse DescribeDomain(1: shared.DescribeDomainRequest describeRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * ListDomains returns the information and configuration for all domains.\n **/\n shared.ListDomainsResponse ListDomains(1: shared.ListDomainsRequest listRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * UpdateDomain is used to update the information and configuration for a registered domain.\n **/\n shared.UpdateDomainResponse UpdateDomain(1: shared.UpdateDomainRequest updateRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 7: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * FailoverDomain is used to failover a registered domain to different cluster.\n **/\n shared.FailoverDomainResponse FailoverDomain(1: shared.FailoverDomainRequest failoverRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 7: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * DeprecateDomain us used to update status of a registered domain to DEPRECATED. Once the domain is deprecated\n * it cannot be used to start new workflow executions. Existing workflow executions will continue to run on\n * deprecated domains.\n **/\n void DeprecateDomain(1: shared.DeprecateDomainRequest deprecateRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 7: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * DeleteDomain permanently removes a domain record. This operation:\n * - Requires domain to be in DEPRECATED status\n * - Cannot be performed on domains with running workflows\n * - Is irreversible and removes all domain data\n * - Requires proper permissions and security token\n **/\n void DeleteDomain(1: shared.DeleteDomainRequest deleteRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.ServiceBusyError serviceBusyError,\n 3: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 4: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * ListFailoverHistory returns the history of failover events for a domain.\n **/\n shared.ListFailoverHistoryResponse ListFailoverHistory(1: shared.ListFailoverHistoryRequest listRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.ServiceBusyError serviceBusyError,\n 3: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 4: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RestartWorkflowExecution restarts a previous workflow\n * If the workflow is currently running it will terminate and restart\n **/\n shared.RestartWorkflowExecutionResponse RestartWorkflowExecution(1: shared.RestartWorkflowExecutionRequest restartRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.ServiceBusyError serviceBusyError,\n 3: shared.DomainNotActiveError domainNotActiveError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.EntityNotExistsError entityNotExistError,\n 6: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 7: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * DiagnoseWorkflowExecution diagnoses a previous workflow execution\n **/\n shared.DiagnoseWorkflowExecutionResponse DiagnoseWorkflowExecution(1: shared.DiagnoseWorkflowExecutionRequest diagnoseRequest)\n throws (\n 1: shared.DomainNotActiveError domainNotActiveError,\n 2: shared.ServiceBusyError serviceBusyError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 5: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * StartWorkflowExecution starts a new long running workflow instance. It will create the instance with\n * 'WorkflowExecutionStarted' event in history and also schedule the first DecisionTask for the worker to make the\n * first decision for this instance. It will return 'WorkflowExecutionAlreadyStartedError', if an instance already\n * exists with same workflowId.\n **/\n shared.StartWorkflowExecutionResponse StartWorkflowExecution(1: shared.StartWorkflowExecutionRequest startRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.WorkflowExecutionAlreadyStartedError sessionAlreadyExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.EntityNotExistsError entityNotExistError,\n 8: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n /**\n * StartWorkflowExecutionAsync starts a new long running workflow instance asynchronously. It will push a StartWorkflowExecutionRequest to a queue\n * and immediately return a response. The request will be processed by a separate consumer eventually.\n **/\n shared.StartWorkflowExecutionAsyncResponse StartWorkflowExecutionAsync(1: shared.StartWorkflowExecutionAsyncRequest startRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.WorkflowExecutionAlreadyStartedError sessionAlreadyExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.EntityNotExistsError entityNotExistError,\n 8: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n /**\n * Returns the history of specified workflow execution. It fails with 'EntityNotExistError' if speficied workflow\n * execution in unknown to the service.\n **/\n shared.GetWorkflowExecutionHistoryResponse GetWorkflowExecutionHistory(1: shared.GetWorkflowExecutionHistoryRequest getRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * PollForDecisionTask is called by application worker to process DecisionTask from a specific taskList. A\n * DecisionTask is dispatched to callers for active workflow executions, with pending decisions.\n * Application is then expected to call 'RespondDecisionTaskCompleted' API when it is done processing the DecisionTask.\n * It will also create a 'DecisionTaskStarted' event in the history for that session before handing off DecisionTask to\n * application worker.\n **/\n shared.PollForDecisionTaskResponse PollForDecisionTask(1: shared.PollForDecisionTaskRequest pollRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.EntityNotExistsError entityNotExistError,\n 6: shared.DomainNotActiveError domainNotActiveError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RespondDecisionTaskCompleted is called by application worker to complete a DecisionTask handed as a result of\n * 'PollForDecisionTask' API call. Completing a DecisionTask will result in new events for the workflow execution and\n * potentially new ActivityTask being created for corresponding decisions. It will also create a DecisionTaskCompleted\n * event in the history for that session. Use the 'taskToken' provided as response of PollForDecisionTask API call\n * for completing the DecisionTask.\n * The response could contain a new decision task if there is one or if the request asking for one.\n **/\n shared.RespondDecisionTaskCompletedResponse RespondDecisionTaskCompleted(1: shared.RespondDecisionTaskCompletedRequest completeRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RespondDecisionTaskFailed is called by application worker to indicate failure. This results in\n * DecisionTaskFailedEvent written to the history and a new DecisionTask created. This API can be used by client to\n * either clear sticky tasklist or report any panics during DecisionTask processing. Cadence will only append first\n * DecisionTaskFailed event to the history of workflow execution for consecutive failures.\n **/\n void RespondDecisionTaskFailed(1: shared.RespondDecisionTaskFailedRequest failedRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * PollForActivityTask is called by application worker to process ActivityTask from a specific taskList. ActivityTask\n * is dispatched to callers whenever a ScheduleTask decision is made for a workflow execution.\n * Application is expected to call 'RespondActivityTaskCompleted' or 'RespondActivityTaskFailed' once it is done\n * processing the task.\n * Application also needs to call 'RecordActivityTaskHeartbeat' API within 'heartbeatTimeoutSeconds' interval to\n * prevent the task from getting timed out. An event 'ActivityTaskStarted' event is also written to workflow execution\n * history before the ActivityTask is dispatched to application worker.\n **/\n shared.PollForActivityTaskResponse PollForActivityTask(1: shared.PollForActivityTaskRequest pollRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.EntityNotExistsError entityNotExistError,\n 6: shared.DomainNotActiveError domainNotActiveError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RecordActivityTaskHeartbeat is called by application worker while it is processing an ActivityTask. If worker fails\n * to heartbeat within 'heartbeatTimeoutSeconds' interval for the ActivityTask, then it will be marked as timedout and\n * 'ActivityTaskTimedOut' event will be written to the workflow history. Calling 'RecordActivityTaskHeartbeat' will\n * fail with 'EntityNotExistsError' in such situations. Use the 'taskToken' provided as response of\n * PollForActivityTask API call for heartbeating.\n **/\n shared.RecordActivityTaskHeartbeatResponse RecordActivityTaskHeartbeat(1: shared.RecordActivityTaskHeartbeatRequest heartbeatRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RecordActivityTaskHeartbeatByID is called by application worker while it is processing an ActivityTask. If worker fails\n * to heartbeat within 'heartbeatTimeoutSeconds' interval for the ActivityTask, then it will be marked as timedout and\n * 'ActivityTaskTimedOut' event will be written to the workflow history. Calling 'RecordActivityTaskHeartbeatByID' will\n * fail with 'EntityNotExistsError' in such situations. Instead of using 'taskToken' like in RecordActivityTaskHeartbeat,\n * use Domain, WorkflowID and ActivityID\n **/\n shared.RecordActivityTaskHeartbeatResponse RecordActivityTaskHeartbeatByID(1: shared.RecordActivityTaskHeartbeatByIDRequest heartbeatRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RespondActivityTaskCompleted is called by application worker when it is done processing an ActivityTask. It will\n * result in a new 'ActivityTaskCompleted' event being written to the workflow history and a new DecisionTask\n * created for the workflow so new decisions could be made. Use the 'taskToken' provided as response of\n * PollForActivityTask API call for completion. It fails with 'EntityNotExistsError' if the taskToken is not valid\n * anymore due to activity timeout.\n **/\n void RespondActivityTaskCompleted(1: shared.RespondActivityTaskCompletedRequest completeRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RespondActivityTaskCompletedByID is called by application worker when it is done processing an ActivityTask.\n * It will result in a new 'ActivityTaskCompleted' event being written to the workflow history and a new DecisionTask\n * created for the workflow so new decisions could be made. Similar to RespondActivityTaskCompleted but use Domain,\n * WorkflowID and ActivityID instead of 'taskToken' for completion. It fails with 'EntityNotExistsError'\n * if the these IDs are not valid anymore due to activity timeout.\n **/\n void RespondActivityTaskCompletedByID(1: shared.RespondActivityTaskCompletedByIDRequest completeRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RespondActivityTaskFailed is called by application worker when it is done processing an ActivityTask. It will\n * result in a new 'ActivityTaskFailed' event being written to the workflow history and a new DecisionTask\n * created for the workflow instance so new decisions could be made. Use the 'taskToken' provided as response of\n * PollForActivityTask API call for completion. It fails with 'EntityNotExistsError' if the taskToken is not valid\n * anymore due to activity timeout.\n **/\n void RespondActivityTaskFailed(1: shared.RespondActivityTaskFailedRequest failRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RespondActivityTaskFailedByID is called by application worker when it is done processing an ActivityTask.\n * It will result in a new 'ActivityTaskFailed' event being written to the workflow history and a new DecisionTask\n * created for the workflow instance so new decisions could be made. Similar to RespondActivityTaskFailed but use\n * Domain, WorkflowID and ActivityID instead of 'taskToken' for completion. It fails with 'EntityNotExistsError'\n * if the these IDs are not valid anymore due to activity timeout.\n **/\n void RespondActivityTaskFailedByID(1: shared.RespondActivityTaskFailedByIDRequest failRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RespondActivityTaskCanceled is called by application worker when it is successfully canceled an ActivityTask. It will\n * result in a new 'ActivityTaskCanceled' event being written to the workflow history and a new DecisionTask\n * created for the workflow instance so new decisions could be made. Use the 'taskToken' provided as response of\n * PollForActivityTask API call for completion. It fails with 'EntityNotExistsError' if the taskToken is not valid\n * anymore due to activity timeout.\n **/\n void RespondActivityTaskCanceled(1: shared.RespondActivityTaskCanceledRequest canceledRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RespondActivityTaskCanceledByID is called by application worker when it is successfully canceled an ActivityTask.\n * It will result in a new 'ActivityTaskCanceled' event being written to the workflow history and a new DecisionTask\n * created for the workflow instance so new decisions could be made. Similar to RespondActivityTaskCanceled but use\n * Domain, WorkflowID and ActivityID instead of 'taskToken' for completion. It fails with 'EntityNotExistsError'\n * if the these IDs are not valid anymore due to activity timeout.\n **/\n void RespondActivityTaskCanceledByID(1: shared.RespondActivityTaskCanceledByIDRequest canceledRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RequestCancelWorkflowExecution is called by application worker when it wants to request cancellation of a workflow instance.\n * It will result in a new 'WorkflowExecutionCancelRequested' event being written to the workflow history and a new DecisionTask\n * created for the workflow instance so new decisions could be made. It fails with 'EntityNotExistsError' if the workflow is not valid\n * anymore due to completion or doesn't exist.\n **/\n void RequestCancelWorkflowExecution(1: shared.RequestCancelWorkflowExecutionRequest cancelRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.CancellationAlreadyRequestedError cancellationAlreadyRequestedError,\n 5: shared.ServiceBusyError serviceBusyError,\n 6: shared.DomainNotActiveError domainNotActiveError,\n 7: shared.LimitExceededError limitExceededError,\n 8: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 9: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 10: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * SignalWorkflowExecution is used to send a signal event to running workflow execution. This results in\n * WorkflowExecutionSignaled event recorded in the history and a decision task being created for the execution.\n **/\n void SignalWorkflowExecution(1: shared.SignalWorkflowExecutionRequest signalRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * SignalWithStartWorkflowExecution is used to ensure sending signal to a workflow.\n * If the workflow is running, this results in WorkflowExecutionSignaled event being recorded in the history\n * and a decision task being created for the execution.\n * If the workflow is not running or not found, this results in WorkflowExecutionStarted and WorkflowExecutionSignaled\n * events being recorded in history, and a decision task being created for the execution\n **/\n shared.StartWorkflowExecutionResponse SignalWithStartWorkflowExecution(1: shared.SignalWithStartWorkflowExecutionRequest signalWithStartRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.WorkflowExecutionAlreadyStartedError workflowAlreadyStartedError,\n 8: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * SignalWithStartWorkflowExecutionAsync is used to ensure sending signal to a workflow asynchronously. It will push a SignalWithStartWorkflowExecutionRequest to a queue\n * and immediately return a response. The request will be processed by a separate consumer eventually.\n **/\n shared.SignalWithStartWorkflowExecutionAsyncResponse SignalWithStartWorkflowExecutionAsync(1: shared.SignalWithStartWorkflowExecutionAsyncRequest signalWithStartRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.WorkflowExecutionAlreadyStartedError sessionAlreadyExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.EntityNotExistsError entityNotExistError,\n 8: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n /**\n * ResetWorkflowExecution reset an existing workflow execution to DecisionTaskCompleted event(exclusive).\n * And it will immediately terminating the current execution instance.\n **/\n shared.ResetWorkflowExecutionResponse ResetWorkflowExecution(1: shared.ResetWorkflowExecutionRequest resetRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * TerminateWorkflowExecution terminates an existing workflow execution by recording WorkflowExecutionTerminated event\n * in the history and immediately terminating the execution instance.\n **/\n void TerminateWorkflowExecution(1: shared.TerminateWorkflowExecutionRequest terminateRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * ListOpenWorkflowExecutions is a visibility API to list the open executions in a specific domain.\n **/\n shared.ListOpenWorkflowExecutionsResponse ListOpenWorkflowExecutions(1: shared.ListOpenWorkflowExecutionsRequest listRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 7: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * ListClosedWorkflowExecutions is a visibility API to list the closed executions in a specific domain.\n **/\n shared.ListClosedWorkflowExecutionsResponse ListClosedWorkflowExecutions(1: shared.ListClosedWorkflowExecutionsRequest listRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * ListWorkflowExecutions is a visibility API to list workflow executions in a specific domain.\n **/\n shared.ListWorkflowExecutionsResponse ListWorkflowExecutions(1: shared.ListWorkflowExecutionsRequest listRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * ListArchivedWorkflowExecutions is a visibility API to list archived workflow executions in a specific domain.\n **/\n shared.ListArchivedWorkflowExecutionsResponse ListArchivedWorkflowExecutions(1: shared.ListArchivedWorkflowExecutionsRequest listRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * ScanWorkflowExecutions is a visibility API to list large amount of workflow executions in a specific domain without order.\n **/\n shared.ListWorkflowExecutionsResponse ScanWorkflowExecutions(1: shared.ListWorkflowExecutionsRequest listRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * CountWorkflowExecutions is a visibility API to count of workflow executions in a specific domain.\n **/\n shared.CountWorkflowExecutionsResponse CountWorkflowExecutions(1: shared.CountWorkflowExecutionsRequest countRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * GetSearchAttributes is a visibility API to get all legal keys that could be used in list APIs\n **/\n shared.GetSearchAttributesResponse GetSearchAttributes()\n throws (\n 2: shared.ServiceBusyError serviceBusyError,\n 3: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 4: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RespondQueryTaskCompleted is called by application worker to complete a QueryTask (which is a DecisionTask for query)\n * as a result of 'PollForDecisionTask' API call. Completing a QueryTask will unblock the client call to 'QueryWorkflow'\n * API and return the query result to client as a response to 'QueryWorkflow' API call.\n **/\n void RespondQueryTaskCompleted(1: shared.RespondQueryTaskCompletedRequest completeRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.ServiceBusyError serviceBusyError,\n 6: shared.DomainNotActiveError domainNotActiveError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * Reset the sticky tasklist related information in mutable state of a given workflow.\n * Things cleared are:\n * 1. StickyTaskList\n * 2. StickyScheduleToStartTimeout\n * 3. ClientLibraryVersion\n * 4. ClientFeatureVersion\n * 5. ClientImpl\n **/\n shared.ResetStickyTaskListResponse ResetStickyTaskList(1: shared.ResetStickyTaskListRequest resetRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.ServiceBusyError serviceBusyError,\n 6: shared.DomainNotActiveError domainNotActiveError,\n 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n 9: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * QueryWorkflow returns query result for a specified workflow execution\n **/\n shared.QueryWorkflowResponse QueryWorkflow(1: shared.QueryWorkflowRequest queryRequest)\n\tthrows (\n\t 1: shared.BadRequestError badRequestError,\n\t 3: shared.EntityNotExistsError entityNotExistError,\n\t 4: shared.QueryFailedError queryFailedError,\n\t 5: shared.LimitExceededError limitExceededError,\n\t 6: shared.ServiceBusyError serviceBusyError,\n\t 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 8: shared.AccessDeniedError accessDeniedError,\n\t)\n\n /**\n * DescribeWorkflowExecution returns information about the specified workflow execution.\n **/\n shared.DescribeWorkflowExecutionResponse DescribeWorkflowExecution(1: shared.DescribeWorkflowExecutionRequest describeRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.ServiceBusyError serviceBusyError,\n 6: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 7: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * DescribeTaskList returns information about the target tasklist, right now this API returns the\n * pollers which polled this tasklist in last few minutes.\n **/\n shared.DescribeTaskListResponse DescribeTaskList(1: shared.DescribeTaskListRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.ServiceBusyError serviceBusyError,\n 6: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 7: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * GetClusterInfo returns information about cadence cluster\n **/\n shared.ClusterInfo GetClusterInfo()\n throws (\n 1: shared.InternalServiceError internalServiceError,\n 2: shared.ServiceBusyError serviceBusyError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * GetTaskListsByDomain returns the list of all the task lists for a domainName.\n **/\n shared.GetTaskListsByDomainResponse GetTaskListsByDomain(1: shared.GetTaskListsByDomainRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.EntityNotExistsError entityNotExistError,\n 3: shared.LimitExceededError limitExceededError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * ReapplyEvents applies stale events to the current workflow and current run\n **/\n shared.ListTaskListPartitionsResponse ListTaskListPartitions(1: shared.ListTaskListPartitionsRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.ServiceBusyError serviceBusyError,\n 6: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RefreshWorkflowTasks refreshes all tasks of a workflow\n **/\n void RefreshWorkflowTasks(1: shared.RefreshWorkflowTasksRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.DomainNotActiveError domainNotActiveError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.EntityNotExistsError entityNotExistError,\n 5: shared.AccessDeniedError accessDeniedError,\n )\n}\n" // WorkflowService_CountWorkflowExecutions_Args represents the arguments for the WorkflowService.CountWorkflowExecutions function. // // The arguments for CountWorkflowExecutions are sent and received over the wire as this struct. type WorkflowService_CountWorkflowExecutions_Args struct { CountRequest *shared.CountWorkflowExecutionsRequest `json:"countRequest,omitempty"` } // ToWire translates a WorkflowService_CountWorkflowExecutions_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_CountWorkflowExecutions_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CountRequest != nil { w, err = v.CountRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CountWorkflowExecutionsRequest_Read(w wire.Value) (*shared.CountWorkflowExecutionsRequest, error) { var v shared.CountWorkflowExecutionsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_CountWorkflowExecutions_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_CountWorkflowExecutions_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_CountWorkflowExecutions_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_CountWorkflowExecutions_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CountRequest, err = _CountWorkflowExecutionsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_CountWorkflowExecutions_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_CountWorkflowExecutions_Args struct could not be encoded. func (v *WorkflowService_CountWorkflowExecutions_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CountRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CountRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CountWorkflowExecutionsRequest_Decode(sr stream.Reader) (*shared.CountWorkflowExecutionsRequest, error) { var v shared.CountWorkflowExecutionsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_CountWorkflowExecutions_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_CountWorkflowExecutions_Args struct could not be generated from the wire // representation. func (v *WorkflowService_CountWorkflowExecutions_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CountRequest, err = _CountWorkflowExecutionsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_CountWorkflowExecutions_Args // struct. func (v *WorkflowService_CountWorkflowExecutions_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CountRequest != nil { fields[i] = fmt.Sprintf("CountRequest: %v", v.CountRequest) i++ } return fmt.Sprintf("WorkflowService_CountWorkflowExecutions_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_CountWorkflowExecutions_Args match the // provided WorkflowService_CountWorkflowExecutions_Args. // // This function performs a deep comparison. func (v *WorkflowService_CountWorkflowExecutions_Args) Equals(rhs *WorkflowService_CountWorkflowExecutions_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CountRequest == nil && rhs.CountRequest == nil) || (v.CountRequest != nil && rhs.CountRequest != nil && v.CountRequest.Equals(rhs.CountRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_CountWorkflowExecutions_Args. func (v *WorkflowService_CountWorkflowExecutions_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CountRequest != nil { err = multierr.Append(err, enc.AddObject("countRequest", v.CountRequest)) } return err } // GetCountRequest returns the value of CountRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_CountWorkflowExecutions_Args) GetCountRequest() (o *shared.CountWorkflowExecutionsRequest) { if v != nil && v.CountRequest != nil { return v.CountRequest } return } // IsSetCountRequest returns true if CountRequest is not nil. func (v *WorkflowService_CountWorkflowExecutions_Args) IsSetCountRequest() bool { return v != nil && v.CountRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "CountWorkflowExecutions" for this struct. func (v *WorkflowService_CountWorkflowExecutions_Args) MethodName() string { return "CountWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_CountWorkflowExecutions_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_CountWorkflowExecutions_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.CountWorkflowExecutions // function. var WorkflowService_CountWorkflowExecutions_Helper = struct { // Args accepts the parameters of CountWorkflowExecutions in-order and returns // the arguments struct for the function. Args func( countRequest *shared.CountWorkflowExecutionsRequest, ) *WorkflowService_CountWorkflowExecutions_Args // IsException returns true if the given error can be thrown // by CountWorkflowExecutions. // // An error can be thrown by CountWorkflowExecutions only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for CountWorkflowExecutions // given its return value and error. // // This allows mapping values and errors returned by // CountWorkflowExecutions into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by CountWorkflowExecutions // // value, err := CountWorkflowExecutions(args) // result, err := WorkflowService_CountWorkflowExecutions_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from CountWorkflowExecutions: %v", err) // } // serialize(result) WrapResponse func(*shared.CountWorkflowExecutionsResponse, error) (*WorkflowService_CountWorkflowExecutions_Result, error) // UnwrapResponse takes the result struct for CountWorkflowExecutions // and returns the value or error returned by it. // // The error is non-nil only if CountWorkflowExecutions threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_CountWorkflowExecutions_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_CountWorkflowExecutions_Result) (*shared.CountWorkflowExecutionsResponse, error) }{} func init() { WorkflowService_CountWorkflowExecutions_Helper.Args = func( countRequest *shared.CountWorkflowExecutionsRequest, ) *WorkflowService_CountWorkflowExecutions_Args { return &WorkflowService_CountWorkflowExecutions_Args{ CountRequest: countRequest, } } WorkflowService_CountWorkflowExecutions_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_CountWorkflowExecutions_Helper.WrapResponse = func(success *shared.CountWorkflowExecutionsResponse, err error) (*WorkflowService_CountWorkflowExecutions_Result, error) { if err == nil { return &WorkflowService_CountWorkflowExecutions_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_CountWorkflowExecutions_Result.BadRequestError") } return &WorkflowService_CountWorkflowExecutions_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_CountWorkflowExecutions_Result.EntityNotExistError") } return &WorkflowService_CountWorkflowExecutions_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_CountWorkflowExecutions_Result.ServiceBusyError") } return &WorkflowService_CountWorkflowExecutions_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_CountWorkflowExecutions_Result.ClientVersionNotSupportedError") } return &WorkflowService_CountWorkflowExecutions_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_CountWorkflowExecutions_Result.AccessDeniedError") } return &WorkflowService_CountWorkflowExecutions_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_CountWorkflowExecutions_Helper.UnwrapResponse = func(result *WorkflowService_CountWorkflowExecutions_Result) (success *shared.CountWorkflowExecutionsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_CountWorkflowExecutions_Result represents the result of a WorkflowService.CountWorkflowExecutions function call. // // The result of a CountWorkflowExecutions execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_CountWorkflowExecutions_Result struct { // Value returned by CountWorkflowExecutions after a successful execution. Success *shared.CountWorkflowExecutionsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_CountWorkflowExecutions_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_CountWorkflowExecutions_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_CountWorkflowExecutions_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CountWorkflowExecutionsResponse_Read(w wire.Value) (*shared.CountWorkflowExecutionsResponse, error) { var v shared.CountWorkflowExecutionsResponse err := v.FromWire(w) return &v, err } func _BadRequestError_Read(w wire.Value) (*shared.BadRequestError, error) { var v shared.BadRequestError err := v.FromWire(w) return &v, err } func _EntityNotExistsError_Read(w wire.Value) (*shared.EntityNotExistsError, error) { var v shared.EntityNotExistsError err := v.FromWire(w) return &v, err } func _ServiceBusyError_Read(w wire.Value) (*shared.ServiceBusyError, error) { var v shared.ServiceBusyError err := v.FromWire(w) return &v, err } func _ClientVersionNotSupportedError_Read(w wire.Value) (*shared.ClientVersionNotSupportedError, error) { var v shared.ClientVersionNotSupportedError err := v.FromWire(w) return &v, err } func _AccessDeniedError_Read(w wire.Value) (*shared.AccessDeniedError, error) { var v shared.AccessDeniedError err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_CountWorkflowExecutions_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_CountWorkflowExecutions_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_CountWorkflowExecutions_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_CountWorkflowExecutions_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _CountWorkflowExecutionsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_CountWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_CountWorkflowExecutions_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_CountWorkflowExecutions_Result struct could not be encoded. func (v *WorkflowService_CountWorkflowExecutions_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_CountWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _CountWorkflowExecutionsResponse_Decode(sr stream.Reader) (*shared.CountWorkflowExecutionsResponse, error) { var v shared.CountWorkflowExecutionsResponse err := v.Decode(sr) return &v, err } func _BadRequestError_Decode(sr stream.Reader) (*shared.BadRequestError, error) { var v shared.BadRequestError err := v.Decode(sr) return &v, err } func _EntityNotExistsError_Decode(sr stream.Reader) (*shared.EntityNotExistsError, error) { var v shared.EntityNotExistsError err := v.Decode(sr) return &v, err } func _ServiceBusyError_Decode(sr stream.Reader) (*shared.ServiceBusyError, error) { var v shared.ServiceBusyError err := v.Decode(sr) return &v, err } func _ClientVersionNotSupportedError_Decode(sr stream.Reader) (*shared.ClientVersionNotSupportedError, error) { var v shared.ClientVersionNotSupportedError err := v.Decode(sr) return &v, err } func _AccessDeniedError_Decode(sr stream.Reader) (*shared.AccessDeniedError, error) { var v shared.AccessDeniedError err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_CountWorkflowExecutions_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_CountWorkflowExecutions_Result struct could not be generated from the wire // representation. func (v *WorkflowService_CountWorkflowExecutions_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _CountWorkflowExecutionsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_CountWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_CountWorkflowExecutions_Result // struct. func (v *WorkflowService_CountWorkflowExecutions_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_CountWorkflowExecutions_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_CountWorkflowExecutions_Result match the // provided WorkflowService_CountWorkflowExecutions_Result. // // This function performs a deep comparison. func (v *WorkflowService_CountWorkflowExecutions_Result) Equals(rhs *WorkflowService_CountWorkflowExecutions_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_CountWorkflowExecutions_Result. func (v *WorkflowService_CountWorkflowExecutions_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_CountWorkflowExecutions_Result) GetSuccess() (o *shared.CountWorkflowExecutionsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_CountWorkflowExecutions_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_CountWorkflowExecutions_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_CountWorkflowExecutions_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_CountWorkflowExecutions_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_CountWorkflowExecutions_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_CountWorkflowExecutions_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_CountWorkflowExecutions_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_CountWorkflowExecutions_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_CountWorkflowExecutions_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_CountWorkflowExecutions_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_CountWorkflowExecutions_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "CountWorkflowExecutions" for this struct. func (v *WorkflowService_CountWorkflowExecutions_Result) MethodName() string { return "CountWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_CountWorkflowExecutions_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_DeleteDomain_Args represents the arguments for the WorkflowService.DeleteDomain function. // // The arguments for DeleteDomain are sent and received over the wire as this struct. type WorkflowService_DeleteDomain_Args struct { DeleteRequest *shared.DeleteDomainRequest `json:"deleteRequest,omitempty"` } // ToWire translates a WorkflowService_DeleteDomain_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DeleteDomain_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.DeleteRequest != nil { w, err = v.DeleteRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DeleteDomainRequest_Read(w wire.Value) (*shared.DeleteDomainRequest, error) { var v shared.DeleteDomainRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DeleteDomain_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DeleteDomain_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DeleteDomain_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DeleteDomain_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.DeleteRequest, err = _DeleteDomainRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_DeleteDomain_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DeleteDomain_Args struct could not be encoded. func (v *WorkflowService_DeleteDomain_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DeleteRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.DeleteRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DeleteDomainRequest_Decode(sr stream.Reader) (*shared.DeleteDomainRequest, error) { var v shared.DeleteDomainRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DeleteDomain_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DeleteDomain_Args struct could not be generated from the wire // representation. func (v *WorkflowService_DeleteDomain_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.DeleteRequest, err = _DeleteDomainRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_DeleteDomain_Args // struct. func (v *WorkflowService_DeleteDomain_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.DeleteRequest != nil { fields[i] = fmt.Sprintf("DeleteRequest: %v", v.DeleteRequest) i++ } return fmt.Sprintf("WorkflowService_DeleteDomain_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DeleteDomain_Args match the // provided WorkflowService_DeleteDomain_Args. // // This function performs a deep comparison. func (v *WorkflowService_DeleteDomain_Args) Equals(rhs *WorkflowService_DeleteDomain_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DeleteRequest == nil && rhs.DeleteRequest == nil) || (v.DeleteRequest != nil && rhs.DeleteRequest != nil && v.DeleteRequest.Equals(rhs.DeleteRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DeleteDomain_Args. func (v *WorkflowService_DeleteDomain_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DeleteRequest != nil { err = multierr.Append(err, enc.AddObject("deleteRequest", v.DeleteRequest)) } return err } // GetDeleteRequest returns the value of DeleteRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_DeleteDomain_Args) GetDeleteRequest() (o *shared.DeleteDomainRequest) { if v != nil && v.DeleteRequest != nil { return v.DeleteRequest } return } // IsSetDeleteRequest returns true if DeleteRequest is not nil. func (v *WorkflowService_DeleteDomain_Args) IsSetDeleteRequest() bool { return v != nil && v.DeleteRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DeleteDomain" for this struct. func (v *WorkflowService_DeleteDomain_Args) MethodName() string { return "DeleteDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_DeleteDomain_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_DeleteDomain_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.DeleteDomain // function. var WorkflowService_DeleteDomain_Helper = struct { // Args accepts the parameters of DeleteDomain in-order and returns // the arguments struct for the function. Args func( deleteRequest *shared.DeleteDomainRequest, ) *WorkflowService_DeleteDomain_Args // IsException returns true if the given error can be thrown // by DeleteDomain. // // An error can be thrown by DeleteDomain only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DeleteDomain // given the error returned by it. The provided error may // be nil if DeleteDomain did not fail. // // This allows mapping errors returned by DeleteDomain into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // DeleteDomain // // err := DeleteDomain(args) // result, err := WorkflowService_DeleteDomain_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from DeleteDomain: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_DeleteDomain_Result, error) // UnwrapResponse takes the result struct for DeleteDomain // and returns the erorr returned by it (if any). // // The error is non-nil only if DeleteDomain threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_DeleteDomain_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_DeleteDomain_Result) error }{} func init() { WorkflowService_DeleteDomain_Helper.Args = func( deleteRequest *shared.DeleteDomainRequest, ) *WorkflowService_DeleteDomain_Args { return &WorkflowService_DeleteDomain_Args{ DeleteRequest: deleteRequest, } } WorkflowService_DeleteDomain_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_DeleteDomain_Helper.WrapResponse = func(err error) (*WorkflowService_DeleteDomain_Result, error) { if err == nil { return &WorkflowService_DeleteDomain_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DeleteDomain_Result.BadRequestError") } return &WorkflowService_DeleteDomain_Result{BadRequestError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DeleteDomain_Result.ServiceBusyError") } return &WorkflowService_DeleteDomain_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DeleteDomain_Result.ClientVersionNotSupportedError") } return &WorkflowService_DeleteDomain_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DeleteDomain_Result.AccessDeniedError") } return &WorkflowService_DeleteDomain_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_DeleteDomain_Helper.UnwrapResponse = func(result *WorkflowService_DeleteDomain_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_DeleteDomain_Result represents the result of a WorkflowService.DeleteDomain function call. // // The result of a DeleteDomain execution is sent and received over the wire as this struct. type WorkflowService_DeleteDomain_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_DeleteDomain_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DeleteDomain_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_DeleteDomain_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_DeleteDomain_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DeleteDomain_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DeleteDomain_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DeleteDomain_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_DeleteDomain_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_DeleteDomain_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DeleteDomain_Result struct could not be encoded. func (v *WorkflowService_DeleteDomain_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_DeleteDomain_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_DeleteDomain_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DeleteDomain_Result struct could not be generated from the wire // representation. func (v *WorkflowService_DeleteDomain_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_DeleteDomain_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_DeleteDomain_Result // struct. func (v *WorkflowService_DeleteDomain_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_DeleteDomain_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DeleteDomain_Result match the // provided WorkflowService_DeleteDomain_Result. // // This function performs a deep comparison. func (v *WorkflowService_DeleteDomain_Result) Equals(rhs *WorkflowService_DeleteDomain_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DeleteDomain_Result. func (v *WorkflowService_DeleteDomain_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_DeleteDomain_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_DeleteDomain_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_DeleteDomain_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_DeleteDomain_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DeleteDomain_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_DeleteDomain_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DeleteDomain_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_DeleteDomain_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DeleteDomain" for this struct. func (v *WorkflowService_DeleteDomain_Result) MethodName() string { return "DeleteDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_DeleteDomain_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_DeprecateDomain_Args represents the arguments for the WorkflowService.DeprecateDomain function. // // The arguments for DeprecateDomain are sent and received over the wire as this struct. type WorkflowService_DeprecateDomain_Args struct { DeprecateRequest *shared.DeprecateDomainRequest `json:"deprecateRequest,omitempty"` } // ToWire translates a WorkflowService_DeprecateDomain_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DeprecateDomain_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.DeprecateRequest != nil { w, err = v.DeprecateRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DeprecateDomainRequest_Read(w wire.Value) (*shared.DeprecateDomainRequest, error) { var v shared.DeprecateDomainRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DeprecateDomain_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DeprecateDomain_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DeprecateDomain_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DeprecateDomain_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.DeprecateRequest, err = _DeprecateDomainRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_DeprecateDomain_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DeprecateDomain_Args struct could not be encoded. func (v *WorkflowService_DeprecateDomain_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DeprecateRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.DeprecateRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DeprecateDomainRequest_Decode(sr stream.Reader) (*shared.DeprecateDomainRequest, error) { var v shared.DeprecateDomainRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DeprecateDomain_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DeprecateDomain_Args struct could not be generated from the wire // representation. func (v *WorkflowService_DeprecateDomain_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.DeprecateRequest, err = _DeprecateDomainRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_DeprecateDomain_Args // struct. func (v *WorkflowService_DeprecateDomain_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.DeprecateRequest != nil { fields[i] = fmt.Sprintf("DeprecateRequest: %v", v.DeprecateRequest) i++ } return fmt.Sprintf("WorkflowService_DeprecateDomain_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DeprecateDomain_Args match the // provided WorkflowService_DeprecateDomain_Args. // // This function performs a deep comparison. func (v *WorkflowService_DeprecateDomain_Args) Equals(rhs *WorkflowService_DeprecateDomain_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DeprecateRequest == nil && rhs.DeprecateRequest == nil) || (v.DeprecateRequest != nil && rhs.DeprecateRequest != nil && v.DeprecateRequest.Equals(rhs.DeprecateRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DeprecateDomain_Args. func (v *WorkflowService_DeprecateDomain_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DeprecateRequest != nil { err = multierr.Append(err, enc.AddObject("deprecateRequest", v.DeprecateRequest)) } return err } // GetDeprecateRequest returns the value of DeprecateRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_DeprecateDomain_Args) GetDeprecateRequest() (o *shared.DeprecateDomainRequest) { if v != nil && v.DeprecateRequest != nil { return v.DeprecateRequest } return } // IsSetDeprecateRequest returns true if DeprecateRequest is not nil. func (v *WorkflowService_DeprecateDomain_Args) IsSetDeprecateRequest() bool { return v != nil && v.DeprecateRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DeprecateDomain" for this struct. func (v *WorkflowService_DeprecateDomain_Args) MethodName() string { return "DeprecateDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_DeprecateDomain_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_DeprecateDomain_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.DeprecateDomain // function. var WorkflowService_DeprecateDomain_Helper = struct { // Args accepts the parameters of DeprecateDomain in-order and returns // the arguments struct for the function. Args func( deprecateRequest *shared.DeprecateDomainRequest, ) *WorkflowService_DeprecateDomain_Args // IsException returns true if the given error can be thrown // by DeprecateDomain. // // An error can be thrown by DeprecateDomain only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DeprecateDomain // given the error returned by it. The provided error may // be nil if DeprecateDomain did not fail. // // This allows mapping errors returned by DeprecateDomain into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // DeprecateDomain // // err := DeprecateDomain(args) // result, err := WorkflowService_DeprecateDomain_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from DeprecateDomain: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_DeprecateDomain_Result, error) // UnwrapResponse takes the result struct for DeprecateDomain // and returns the erorr returned by it (if any). // // The error is non-nil only if DeprecateDomain threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_DeprecateDomain_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_DeprecateDomain_Result) error }{} func init() { WorkflowService_DeprecateDomain_Helper.Args = func( deprecateRequest *shared.DeprecateDomainRequest, ) *WorkflowService_DeprecateDomain_Args { return &WorkflowService_DeprecateDomain_Args{ DeprecateRequest: deprecateRequest, } } WorkflowService_DeprecateDomain_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_DeprecateDomain_Helper.WrapResponse = func(err error) (*WorkflowService_DeprecateDomain_Result, error) { if err == nil { return &WorkflowService_DeprecateDomain_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DeprecateDomain_Result.BadRequestError") } return &WorkflowService_DeprecateDomain_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DeprecateDomain_Result.EntityNotExistError") } return &WorkflowService_DeprecateDomain_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DeprecateDomain_Result.ServiceBusyError") } return &WorkflowService_DeprecateDomain_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DeprecateDomain_Result.DomainNotActiveError") } return &WorkflowService_DeprecateDomain_Result{DomainNotActiveError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DeprecateDomain_Result.ClientVersionNotSupportedError") } return &WorkflowService_DeprecateDomain_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DeprecateDomain_Result.AccessDeniedError") } return &WorkflowService_DeprecateDomain_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_DeprecateDomain_Helper.UnwrapResponse = func(result *WorkflowService_DeprecateDomain_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_DeprecateDomain_Result represents the result of a WorkflowService.DeprecateDomain function call. // // The result of a DeprecateDomain execution is sent and received over the wire as this struct. type WorkflowService_DeprecateDomain_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_DeprecateDomain_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DeprecateDomain_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_DeprecateDomain_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DomainNotActiveError_Read(w wire.Value) (*shared.DomainNotActiveError, error) { var v shared.DomainNotActiveError err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DeprecateDomain_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DeprecateDomain_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DeprecateDomain_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DeprecateDomain_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_DeprecateDomain_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_DeprecateDomain_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DeprecateDomain_Result struct could not be encoded. func (v *WorkflowService_DeprecateDomain_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_DeprecateDomain_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _DomainNotActiveError_Decode(sr stream.Reader) (*shared.DomainNotActiveError, error) { var v shared.DomainNotActiveError err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DeprecateDomain_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DeprecateDomain_Result struct could not be generated from the wire // representation. func (v *WorkflowService_DeprecateDomain_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_DeprecateDomain_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_DeprecateDomain_Result // struct. func (v *WorkflowService_DeprecateDomain_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_DeprecateDomain_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DeprecateDomain_Result match the // provided WorkflowService_DeprecateDomain_Result. // // This function performs a deep comparison. func (v *WorkflowService_DeprecateDomain_Result) Equals(rhs *WorkflowService_DeprecateDomain_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DeprecateDomain_Result. func (v *WorkflowService_DeprecateDomain_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_DeprecateDomain_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_DeprecateDomain_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_DeprecateDomain_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_DeprecateDomain_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_DeprecateDomain_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_DeprecateDomain_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_DeprecateDomain_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_DeprecateDomain_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DeprecateDomain_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_DeprecateDomain_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DeprecateDomain_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_DeprecateDomain_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DeprecateDomain" for this struct. func (v *WorkflowService_DeprecateDomain_Result) MethodName() string { return "DeprecateDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_DeprecateDomain_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_DescribeDomain_Args represents the arguments for the WorkflowService.DescribeDomain function. // // The arguments for DescribeDomain are sent and received over the wire as this struct. type WorkflowService_DescribeDomain_Args struct { DescribeRequest *shared.DescribeDomainRequest `json:"describeRequest,omitempty"` } // ToWire translates a WorkflowService_DescribeDomain_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DescribeDomain_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.DescribeRequest != nil { w, err = v.DescribeRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeDomainRequest_Read(w wire.Value) (*shared.DescribeDomainRequest, error) { var v shared.DescribeDomainRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DescribeDomain_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DescribeDomain_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DescribeDomain_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DescribeDomain_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.DescribeRequest, err = _DescribeDomainRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_DescribeDomain_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DescribeDomain_Args struct could not be encoded. func (v *WorkflowService_DescribeDomain_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DescribeRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.DescribeRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeDomainRequest_Decode(sr stream.Reader) (*shared.DescribeDomainRequest, error) { var v shared.DescribeDomainRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DescribeDomain_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DescribeDomain_Args struct could not be generated from the wire // representation. func (v *WorkflowService_DescribeDomain_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.DescribeRequest, err = _DescribeDomainRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_DescribeDomain_Args // struct. func (v *WorkflowService_DescribeDomain_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.DescribeRequest != nil { fields[i] = fmt.Sprintf("DescribeRequest: %v", v.DescribeRequest) i++ } return fmt.Sprintf("WorkflowService_DescribeDomain_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DescribeDomain_Args match the // provided WorkflowService_DescribeDomain_Args. // // This function performs a deep comparison. func (v *WorkflowService_DescribeDomain_Args) Equals(rhs *WorkflowService_DescribeDomain_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DescribeRequest == nil && rhs.DescribeRequest == nil) || (v.DescribeRequest != nil && rhs.DescribeRequest != nil && v.DescribeRequest.Equals(rhs.DescribeRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DescribeDomain_Args. func (v *WorkflowService_DescribeDomain_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DescribeRequest != nil { err = multierr.Append(err, enc.AddObject("describeRequest", v.DescribeRequest)) } return err } // GetDescribeRequest returns the value of DescribeRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeDomain_Args) GetDescribeRequest() (o *shared.DescribeDomainRequest) { if v != nil && v.DescribeRequest != nil { return v.DescribeRequest } return } // IsSetDescribeRequest returns true if DescribeRequest is not nil. func (v *WorkflowService_DescribeDomain_Args) IsSetDescribeRequest() bool { return v != nil && v.DescribeRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeDomain" for this struct. func (v *WorkflowService_DescribeDomain_Args) MethodName() string { return "DescribeDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_DescribeDomain_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_DescribeDomain_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.DescribeDomain // function. var WorkflowService_DescribeDomain_Helper = struct { // Args accepts the parameters of DescribeDomain in-order and returns // the arguments struct for the function. Args func( describeRequest *shared.DescribeDomainRequest, ) *WorkflowService_DescribeDomain_Args // IsException returns true if the given error can be thrown // by DescribeDomain. // // An error can be thrown by DescribeDomain only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeDomain // given its return value and error. // // This allows mapping values and errors returned by // DescribeDomain into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeDomain // // value, err := DescribeDomain(args) // result, err := WorkflowService_DescribeDomain_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeDomain: %v", err) // } // serialize(result) WrapResponse func(*shared.DescribeDomainResponse, error) (*WorkflowService_DescribeDomain_Result, error) // UnwrapResponse takes the result struct for DescribeDomain // and returns the value or error returned by it. // // The error is non-nil only if DescribeDomain threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_DescribeDomain_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_DescribeDomain_Result) (*shared.DescribeDomainResponse, error) }{} func init() { WorkflowService_DescribeDomain_Helper.Args = func( describeRequest *shared.DescribeDomainRequest, ) *WorkflowService_DescribeDomain_Args { return &WorkflowService_DescribeDomain_Args{ DescribeRequest: describeRequest, } } WorkflowService_DescribeDomain_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_DescribeDomain_Helper.WrapResponse = func(success *shared.DescribeDomainResponse, err error) (*WorkflowService_DescribeDomain_Result, error) { if err == nil { return &WorkflowService_DescribeDomain_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeDomain_Result.BadRequestError") } return &WorkflowService_DescribeDomain_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeDomain_Result.EntityNotExistError") } return &WorkflowService_DescribeDomain_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeDomain_Result.ServiceBusyError") } return &WorkflowService_DescribeDomain_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeDomain_Result.ClientVersionNotSupportedError") } return &WorkflowService_DescribeDomain_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeDomain_Result.AccessDeniedError") } return &WorkflowService_DescribeDomain_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_DescribeDomain_Helper.UnwrapResponse = func(result *WorkflowService_DescribeDomain_Result) (success *shared.DescribeDomainResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_DescribeDomain_Result represents the result of a WorkflowService.DescribeDomain function call. // // The result of a DescribeDomain execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_DescribeDomain_Result struct { // Value returned by DescribeDomain after a successful execution. Success *shared.DescribeDomainResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_DescribeDomain_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DescribeDomain_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_DescribeDomain_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeDomainResponse_Read(w wire.Value) (*shared.DescribeDomainResponse, error) { var v shared.DescribeDomainResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DescribeDomain_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DescribeDomain_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DescribeDomain_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DescribeDomain_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeDomainResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DescribeDomain_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_DescribeDomain_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DescribeDomain_Result struct could not be encoded. func (v *WorkflowService_DescribeDomain_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DescribeDomain_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeDomainResponse_Decode(sr stream.Reader) (*shared.DescribeDomainResponse, error) { var v shared.DescribeDomainResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DescribeDomain_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DescribeDomain_Result struct could not be generated from the wire // representation. func (v *WorkflowService_DescribeDomain_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeDomainResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DescribeDomain_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_DescribeDomain_Result // struct. func (v *WorkflowService_DescribeDomain_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_DescribeDomain_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DescribeDomain_Result match the // provided WorkflowService_DescribeDomain_Result. // // This function performs a deep comparison. func (v *WorkflowService_DescribeDomain_Result) Equals(rhs *WorkflowService_DescribeDomain_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DescribeDomain_Result. func (v *WorkflowService_DescribeDomain_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeDomain_Result) GetSuccess() (o *shared.DescribeDomainResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_DescribeDomain_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeDomain_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_DescribeDomain_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeDomain_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_DescribeDomain_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeDomain_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_DescribeDomain_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeDomain_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_DescribeDomain_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeDomain_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_DescribeDomain_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeDomain" for this struct. func (v *WorkflowService_DescribeDomain_Result) MethodName() string { return "DescribeDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_DescribeDomain_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_DescribeTaskList_Args represents the arguments for the WorkflowService.DescribeTaskList function. // // The arguments for DescribeTaskList are sent and received over the wire as this struct. type WorkflowService_DescribeTaskList_Args struct { Request *shared.DescribeTaskListRequest `json:"request,omitempty"` } // ToWire translates a WorkflowService_DescribeTaskList_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DescribeTaskList_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeTaskListRequest_Read(w wire.Value) (*shared.DescribeTaskListRequest, error) { var v shared.DescribeTaskListRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DescribeTaskList_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DescribeTaskList_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DescribeTaskList_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DescribeTaskList_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _DescribeTaskListRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_DescribeTaskList_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DescribeTaskList_Args struct could not be encoded. func (v *WorkflowService_DescribeTaskList_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeTaskListRequest_Decode(sr stream.Reader) (*shared.DescribeTaskListRequest, error) { var v shared.DescribeTaskListRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DescribeTaskList_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DescribeTaskList_Args struct could not be generated from the wire // representation. func (v *WorkflowService_DescribeTaskList_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _DescribeTaskListRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_DescribeTaskList_Args // struct. func (v *WorkflowService_DescribeTaskList_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("WorkflowService_DescribeTaskList_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DescribeTaskList_Args match the // provided WorkflowService_DescribeTaskList_Args. // // This function performs a deep comparison. func (v *WorkflowService_DescribeTaskList_Args) Equals(rhs *WorkflowService_DescribeTaskList_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DescribeTaskList_Args. func (v *WorkflowService_DescribeTaskList_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeTaskList_Args) GetRequest() (o *shared.DescribeTaskListRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *WorkflowService_DescribeTaskList_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeTaskList" for this struct. func (v *WorkflowService_DescribeTaskList_Args) MethodName() string { return "DescribeTaskList" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_DescribeTaskList_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_DescribeTaskList_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.DescribeTaskList // function. var WorkflowService_DescribeTaskList_Helper = struct { // Args accepts the parameters of DescribeTaskList in-order and returns // the arguments struct for the function. Args func( request *shared.DescribeTaskListRequest, ) *WorkflowService_DescribeTaskList_Args // IsException returns true if the given error can be thrown // by DescribeTaskList. // // An error can be thrown by DescribeTaskList only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeTaskList // given its return value and error. // // This allows mapping values and errors returned by // DescribeTaskList into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeTaskList // // value, err := DescribeTaskList(args) // result, err := WorkflowService_DescribeTaskList_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeTaskList: %v", err) // } // serialize(result) WrapResponse func(*shared.DescribeTaskListResponse, error) (*WorkflowService_DescribeTaskList_Result, error) // UnwrapResponse takes the result struct for DescribeTaskList // and returns the value or error returned by it. // // The error is non-nil only if DescribeTaskList threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_DescribeTaskList_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_DescribeTaskList_Result) (*shared.DescribeTaskListResponse, error) }{} func init() { WorkflowService_DescribeTaskList_Helper.Args = func( request *shared.DescribeTaskListRequest, ) *WorkflowService_DescribeTaskList_Args { return &WorkflowService_DescribeTaskList_Args{ Request: request, } } WorkflowService_DescribeTaskList_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_DescribeTaskList_Helper.WrapResponse = func(success *shared.DescribeTaskListResponse, err error) (*WorkflowService_DescribeTaskList_Result, error) { if err == nil { return &WorkflowService_DescribeTaskList_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeTaskList_Result.BadRequestError") } return &WorkflowService_DescribeTaskList_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeTaskList_Result.EntityNotExistError") } return &WorkflowService_DescribeTaskList_Result{EntityNotExistError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeTaskList_Result.LimitExceededError") } return &WorkflowService_DescribeTaskList_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeTaskList_Result.ServiceBusyError") } return &WorkflowService_DescribeTaskList_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeTaskList_Result.ClientVersionNotSupportedError") } return &WorkflowService_DescribeTaskList_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeTaskList_Result.AccessDeniedError") } return &WorkflowService_DescribeTaskList_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_DescribeTaskList_Helper.UnwrapResponse = func(result *WorkflowService_DescribeTaskList_Result) (success *shared.DescribeTaskListResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_DescribeTaskList_Result represents the result of a WorkflowService.DescribeTaskList function call. // // The result of a DescribeTaskList execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_DescribeTaskList_Result struct { // Value returned by DescribeTaskList after a successful execution. Success *shared.DescribeTaskListResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_DescribeTaskList_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DescribeTaskList_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_DescribeTaskList_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeTaskListResponse_Read(w wire.Value) (*shared.DescribeTaskListResponse, error) { var v shared.DescribeTaskListResponse err := v.FromWire(w) return &v, err } func _LimitExceededError_Read(w wire.Value) (*shared.LimitExceededError, error) { var v shared.LimitExceededError err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DescribeTaskList_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DescribeTaskList_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DescribeTaskList_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DescribeTaskList_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeTaskListResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DescribeTaskList_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_DescribeTaskList_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DescribeTaskList_Result struct could not be encoded. func (v *WorkflowService_DescribeTaskList_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DescribeTaskList_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeTaskListResponse_Decode(sr stream.Reader) (*shared.DescribeTaskListResponse, error) { var v shared.DescribeTaskListResponse err := v.Decode(sr) return &v, err } func _LimitExceededError_Decode(sr stream.Reader) (*shared.LimitExceededError, error) { var v shared.LimitExceededError err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DescribeTaskList_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DescribeTaskList_Result struct could not be generated from the wire // representation. func (v *WorkflowService_DescribeTaskList_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeTaskListResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DescribeTaskList_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_DescribeTaskList_Result // struct. func (v *WorkflowService_DescribeTaskList_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_DescribeTaskList_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DescribeTaskList_Result match the // provided WorkflowService_DescribeTaskList_Result. // // This function performs a deep comparison. func (v *WorkflowService_DescribeTaskList_Result) Equals(rhs *WorkflowService_DescribeTaskList_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DescribeTaskList_Result. func (v *WorkflowService_DescribeTaskList_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeTaskList_Result) GetSuccess() (o *shared.DescribeTaskListResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_DescribeTaskList_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeTaskList_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_DescribeTaskList_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeTaskList_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_DescribeTaskList_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeTaskList_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_DescribeTaskList_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeTaskList_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_DescribeTaskList_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeTaskList_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_DescribeTaskList_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeTaskList_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_DescribeTaskList_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeTaskList" for this struct. func (v *WorkflowService_DescribeTaskList_Result) MethodName() string { return "DescribeTaskList" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_DescribeTaskList_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_DescribeWorkflowExecution_Args represents the arguments for the WorkflowService.DescribeWorkflowExecution function. // // The arguments for DescribeWorkflowExecution are sent and received over the wire as this struct. type WorkflowService_DescribeWorkflowExecution_Args struct { DescribeRequest *shared.DescribeWorkflowExecutionRequest `json:"describeRequest,omitempty"` } // ToWire translates a WorkflowService_DescribeWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DescribeWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.DescribeRequest != nil { w, err = v.DescribeRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeWorkflowExecutionRequest_Read(w wire.Value) (*shared.DescribeWorkflowExecutionRequest, error) { var v shared.DescribeWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DescribeWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DescribeWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DescribeWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DescribeWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.DescribeRequest, err = _DescribeWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_DescribeWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DescribeWorkflowExecution_Args struct could not be encoded. func (v *WorkflowService_DescribeWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DescribeRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.DescribeRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.DescribeWorkflowExecutionRequest, error) { var v shared.DescribeWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DescribeWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DescribeWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *WorkflowService_DescribeWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.DescribeRequest, err = _DescribeWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_DescribeWorkflowExecution_Args // struct. func (v *WorkflowService_DescribeWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.DescribeRequest != nil { fields[i] = fmt.Sprintf("DescribeRequest: %v", v.DescribeRequest) i++ } return fmt.Sprintf("WorkflowService_DescribeWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DescribeWorkflowExecution_Args match the // provided WorkflowService_DescribeWorkflowExecution_Args. // // This function performs a deep comparison. func (v *WorkflowService_DescribeWorkflowExecution_Args) Equals(rhs *WorkflowService_DescribeWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DescribeRequest == nil && rhs.DescribeRequest == nil) || (v.DescribeRequest != nil && rhs.DescribeRequest != nil && v.DescribeRequest.Equals(rhs.DescribeRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DescribeWorkflowExecution_Args. func (v *WorkflowService_DescribeWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DescribeRequest != nil { err = multierr.Append(err, enc.AddObject("describeRequest", v.DescribeRequest)) } return err } // GetDescribeRequest returns the value of DescribeRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeWorkflowExecution_Args) GetDescribeRequest() (o *shared.DescribeWorkflowExecutionRequest) { if v != nil && v.DescribeRequest != nil { return v.DescribeRequest } return } // IsSetDescribeRequest returns true if DescribeRequest is not nil. func (v *WorkflowService_DescribeWorkflowExecution_Args) IsSetDescribeRequest() bool { return v != nil && v.DescribeRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeWorkflowExecution" for this struct. func (v *WorkflowService_DescribeWorkflowExecution_Args) MethodName() string { return "DescribeWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_DescribeWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_DescribeWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.DescribeWorkflowExecution // function. var WorkflowService_DescribeWorkflowExecution_Helper = struct { // Args accepts the parameters of DescribeWorkflowExecution in-order and returns // the arguments struct for the function. Args func( describeRequest *shared.DescribeWorkflowExecutionRequest, ) *WorkflowService_DescribeWorkflowExecution_Args // IsException returns true if the given error can be thrown // by DescribeWorkflowExecution. // // An error can be thrown by DescribeWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // DescribeWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeWorkflowExecution // // value, err := DescribeWorkflowExecution(args) // result, err := WorkflowService_DescribeWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*shared.DescribeWorkflowExecutionResponse, error) (*WorkflowService_DescribeWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for DescribeWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if DescribeWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_DescribeWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_DescribeWorkflowExecution_Result) (*shared.DescribeWorkflowExecutionResponse, error) }{} func init() { WorkflowService_DescribeWorkflowExecution_Helper.Args = func( describeRequest *shared.DescribeWorkflowExecutionRequest, ) *WorkflowService_DescribeWorkflowExecution_Args { return &WorkflowService_DescribeWorkflowExecution_Args{ DescribeRequest: describeRequest, } } WorkflowService_DescribeWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_DescribeWorkflowExecution_Helper.WrapResponse = func(success *shared.DescribeWorkflowExecutionResponse, err error) (*WorkflowService_DescribeWorkflowExecution_Result, error) { if err == nil { return &WorkflowService_DescribeWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeWorkflowExecution_Result.BadRequestError") } return &WorkflowService_DescribeWorkflowExecution_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeWorkflowExecution_Result.EntityNotExistError") } return &WorkflowService_DescribeWorkflowExecution_Result{EntityNotExistError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeWorkflowExecution_Result.LimitExceededError") } return &WorkflowService_DescribeWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeWorkflowExecution_Result.ServiceBusyError") } return &WorkflowService_DescribeWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeWorkflowExecution_Result.ClientVersionNotSupportedError") } return &WorkflowService_DescribeWorkflowExecution_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DescribeWorkflowExecution_Result.AccessDeniedError") } return &WorkflowService_DescribeWorkflowExecution_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_DescribeWorkflowExecution_Helper.UnwrapResponse = func(result *WorkflowService_DescribeWorkflowExecution_Result) (success *shared.DescribeWorkflowExecutionResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_DescribeWorkflowExecution_Result represents the result of a WorkflowService.DescribeWorkflowExecution function call. // // The result of a DescribeWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_DescribeWorkflowExecution_Result struct { // Value returned by DescribeWorkflowExecution after a successful execution. Success *shared.DescribeWorkflowExecutionResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_DescribeWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DescribeWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeWorkflowExecutionResponse_Read(w wire.Value) (*shared.DescribeWorkflowExecutionResponse, error) { var v shared.DescribeWorkflowExecutionResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DescribeWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DescribeWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DescribeWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DescribeWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_DescribeWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DescribeWorkflowExecution_Result struct could not be encoded. func (v *WorkflowService_DescribeWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeWorkflowExecutionResponse_Decode(sr stream.Reader) (*shared.DescribeWorkflowExecutionResponse, error) { var v shared.DescribeWorkflowExecutionResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DescribeWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DescribeWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *WorkflowService_DescribeWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_DescribeWorkflowExecution_Result // struct. func (v *WorkflowService_DescribeWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_DescribeWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DescribeWorkflowExecution_Result match the // provided WorkflowService_DescribeWorkflowExecution_Result. // // This function performs a deep comparison. func (v *WorkflowService_DescribeWorkflowExecution_Result) Equals(rhs *WorkflowService_DescribeWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DescribeWorkflowExecution_Result. func (v *WorkflowService_DescribeWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeWorkflowExecution_Result) GetSuccess() (o *shared.DescribeWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_DescribeWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_DescribeWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_DescribeWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_DescribeWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_DescribeWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeWorkflowExecution_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_DescribeWorkflowExecution_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DescribeWorkflowExecution_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_DescribeWorkflowExecution_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeWorkflowExecution" for this struct. func (v *WorkflowService_DescribeWorkflowExecution_Result) MethodName() string { return "DescribeWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_DescribeWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_DiagnoseWorkflowExecution_Args represents the arguments for the WorkflowService.DiagnoseWorkflowExecution function. // // The arguments for DiagnoseWorkflowExecution are sent and received over the wire as this struct. type WorkflowService_DiagnoseWorkflowExecution_Args struct { DiagnoseRequest *shared.DiagnoseWorkflowExecutionRequest `json:"diagnoseRequest,omitempty"` } // ToWire translates a WorkflowService_DiagnoseWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DiagnoseWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.DiagnoseRequest != nil { w, err = v.DiagnoseRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DiagnoseWorkflowExecutionRequest_Read(w wire.Value) (*shared.DiagnoseWorkflowExecutionRequest, error) { var v shared.DiagnoseWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DiagnoseWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DiagnoseWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DiagnoseWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DiagnoseWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.DiagnoseRequest, err = _DiagnoseWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_DiagnoseWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DiagnoseWorkflowExecution_Args struct could not be encoded. func (v *WorkflowService_DiagnoseWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DiagnoseRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.DiagnoseRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DiagnoseWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.DiagnoseWorkflowExecutionRequest, error) { var v shared.DiagnoseWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DiagnoseWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DiagnoseWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *WorkflowService_DiagnoseWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.DiagnoseRequest, err = _DiagnoseWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_DiagnoseWorkflowExecution_Args // struct. func (v *WorkflowService_DiagnoseWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.DiagnoseRequest != nil { fields[i] = fmt.Sprintf("DiagnoseRequest: %v", v.DiagnoseRequest) i++ } return fmt.Sprintf("WorkflowService_DiagnoseWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DiagnoseWorkflowExecution_Args match the // provided WorkflowService_DiagnoseWorkflowExecution_Args. // // This function performs a deep comparison. func (v *WorkflowService_DiagnoseWorkflowExecution_Args) Equals(rhs *WorkflowService_DiagnoseWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DiagnoseRequest == nil && rhs.DiagnoseRequest == nil) || (v.DiagnoseRequest != nil && rhs.DiagnoseRequest != nil && v.DiagnoseRequest.Equals(rhs.DiagnoseRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DiagnoseWorkflowExecution_Args. func (v *WorkflowService_DiagnoseWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DiagnoseRequest != nil { err = multierr.Append(err, enc.AddObject("diagnoseRequest", v.DiagnoseRequest)) } return err } // GetDiagnoseRequest returns the value of DiagnoseRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_DiagnoseWorkflowExecution_Args) GetDiagnoseRequest() (o *shared.DiagnoseWorkflowExecutionRequest) { if v != nil && v.DiagnoseRequest != nil { return v.DiagnoseRequest } return } // IsSetDiagnoseRequest returns true if DiagnoseRequest is not nil. func (v *WorkflowService_DiagnoseWorkflowExecution_Args) IsSetDiagnoseRequest() bool { return v != nil && v.DiagnoseRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DiagnoseWorkflowExecution" for this struct. func (v *WorkflowService_DiagnoseWorkflowExecution_Args) MethodName() string { return "DiagnoseWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_DiagnoseWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_DiagnoseWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.DiagnoseWorkflowExecution // function. var WorkflowService_DiagnoseWorkflowExecution_Helper = struct { // Args accepts the parameters of DiagnoseWorkflowExecution in-order and returns // the arguments struct for the function. Args func( diagnoseRequest *shared.DiagnoseWorkflowExecutionRequest, ) *WorkflowService_DiagnoseWorkflowExecution_Args // IsException returns true if the given error can be thrown // by DiagnoseWorkflowExecution. // // An error can be thrown by DiagnoseWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DiagnoseWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // DiagnoseWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DiagnoseWorkflowExecution // // value, err := DiagnoseWorkflowExecution(args) // result, err := WorkflowService_DiagnoseWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DiagnoseWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*shared.DiagnoseWorkflowExecutionResponse, error) (*WorkflowService_DiagnoseWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for DiagnoseWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if DiagnoseWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_DiagnoseWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_DiagnoseWorkflowExecution_Result) (*shared.DiagnoseWorkflowExecutionResponse, error) }{} func init() { WorkflowService_DiagnoseWorkflowExecution_Helper.Args = func( diagnoseRequest *shared.DiagnoseWorkflowExecutionRequest, ) *WorkflowService_DiagnoseWorkflowExecution_Args { return &WorkflowService_DiagnoseWorkflowExecution_Args{ DiagnoseRequest: diagnoseRequest, } } WorkflowService_DiagnoseWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.DomainNotActiveError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_DiagnoseWorkflowExecution_Helper.WrapResponse = func(success *shared.DiagnoseWorkflowExecutionResponse, err error) (*WorkflowService_DiagnoseWorkflowExecution_Result, error) { if err == nil { return &WorkflowService_DiagnoseWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DiagnoseWorkflowExecution_Result.DomainNotActiveError") } return &WorkflowService_DiagnoseWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DiagnoseWorkflowExecution_Result.ServiceBusyError") } return &WorkflowService_DiagnoseWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DiagnoseWorkflowExecution_Result.EntityNotExistError") } return &WorkflowService_DiagnoseWorkflowExecution_Result{EntityNotExistError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DiagnoseWorkflowExecution_Result.ClientVersionNotSupportedError") } return &WorkflowService_DiagnoseWorkflowExecution_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_DiagnoseWorkflowExecution_Result.AccessDeniedError") } return &WorkflowService_DiagnoseWorkflowExecution_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_DiagnoseWorkflowExecution_Helper.UnwrapResponse = func(result *WorkflowService_DiagnoseWorkflowExecution_Result) (success *shared.DiagnoseWorkflowExecutionResponse, err error) { if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_DiagnoseWorkflowExecution_Result represents the result of a WorkflowService.DiagnoseWorkflowExecution function call. // // The result of a DiagnoseWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_DiagnoseWorkflowExecution_Result struct { // Value returned by DiagnoseWorkflowExecution after a successful execution. Success *shared.DiagnoseWorkflowExecutionResponse `json:"success,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_DiagnoseWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_DiagnoseWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_DiagnoseWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DiagnoseWorkflowExecutionResponse_Read(w wire.Value) (*shared.DiagnoseWorkflowExecutionResponse, error) { var v shared.DiagnoseWorkflowExecutionResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_DiagnoseWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_DiagnoseWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_DiagnoseWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_DiagnoseWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DiagnoseWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DiagnoseWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_DiagnoseWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_DiagnoseWorkflowExecution_Result struct could not be encoded. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DiagnoseWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DiagnoseWorkflowExecutionResponse_Decode(sr stream.Reader) (*shared.DiagnoseWorkflowExecutionResponse, error) { var v shared.DiagnoseWorkflowExecutionResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_DiagnoseWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_DiagnoseWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DiagnoseWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_DiagnoseWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_DiagnoseWorkflowExecution_Result // struct. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_DiagnoseWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_DiagnoseWorkflowExecution_Result match the // provided WorkflowService_DiagnoseWorkflowExecution_Result. // // This function performs a deep comparison. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) Equals(rhs *WorkflowService_DiagnoseWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_DiagnoseWorkflowExecution_Result. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) GetSuccess() (o *shared.DiagnoseWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DiagnoseWorkflowExecution" for this struct. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) MethodName() string { return "DiagnoseWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_DiagnoseWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_FailoverDomain_Args represents the arguments for the WorkflowService.FailoverDomain function. // // The arguments for FailoverDomain are sent and received over the wire as this struct. type WorkflowService_FailoverDomain_Args struct { FailoverRequest *shared.FailoverDomainRequest `json:"failoverRequest,omitempty"` } // ToWire translates a WorkflowService_FailoverDomain_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_FailoverDomain_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.FailoverRequest != nil { w, err = v.FailoverRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _FailoverDomainRequest_Read(w wire.Value) (*shared.FailoverDomainRequest, error) { var v shared.FailoverDomainRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_FailoverDomain_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_FailoverDomain_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_FailoverDomain_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_FailoverDomain_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.FailoverRequest, err = _FailoverDomainRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_FailoverDomain_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_FailoverDomain_Args struct could not be encoded. func (v *WorkflowService_FailoverDomain_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FailoverRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.FailoverRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _FailoverDomainRequest_Decode(sr stream.Reader) (*shared.FailoverDomainRequest, error) { var v shared.FailoverDomainRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_FailoverDomain_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_FailoverDomain_Args struct could not be generated from the wire // representation. func (v *WorkflowService_FailoverDomain_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.FailoverRequest, err = _FailoverDomainRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_FailoverDomain_Args // struct. func (v *WorkflowService_FailoverDomain_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.FailoverRequest != nil { fields[i] = fmt.Sprintf("FailoverRequest: %v", v.FailoverRequest) i++ } return fmt.Sprintf("WorkflowService_FailoverDomain_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_FailoverDomain_Args match the // provided WorkflowService_FailoverDomain_Args. // // This function performs a deep comparison. func (v *WorkflowService_FailoverDomain_Args) Equals(rhs *WorkflowService_FailoverDomain_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.FailoverRequest == nil && rhs.FailoverRequest == nil) || (v.FailoverRequest != nil && rhs.FailoverRequest != nil && v.FailoverRequest.Equals(rhs.FailoverRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_FailoverDomain_Args. func (v *WorkflowService_FailoverDomain_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FailoverRequest != nil { err = multierr.Append(err, enc.AddObject("failoverRequest", v.FailoverRequest)) } return err } // GetFailoverRequest returns the value of FailoverRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_FailoverDomain_Args) GetFailoverRequest() (o *shared.FailoverDomainRequest) { if v != nil && v.FailoverRequest != nil { return v.FailoverRequest } return } // IsSetFailoverRequest returns true if FailoverRequest is not nil. func (v *WorkflowService_FailoverDomain_Args) IsSetFailoverRequest() bool { return v != nil && v.FailoverRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "FailoverDomain" for this struct. func (v *WorkflowService_FailoverDomain_Args) MethodName() string { return "FailoverDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_FailoverDomain_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_FailoverDomain_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.FailoverDomain // function. var WorkflowService_FailoverDomain_Helper = struct { // Args accepts the parameters of FailoverDomain in-order and returns // the arguments struct for the function. Args func( failoverRequest *shared.FailoverDomainRequest, ) *WorkflowService_FailoverDomain_Args // IsException returns true if the given error can be thrown // by FailoverDomain. // // An error can be thrown by FailoverDomain only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for FailoverDomain // given its return value and error. // // This allows mapping values and errors returned by // FailoverDomain into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by FailoverDomain // // value, err := FailoverDomain(args) // result, err := WorkflowService_FailoverDomain_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from FailoverDomain: %v", err) // } // serialize(result) WrapResponse func(*shared.FailoverDomainResponse, error) (*WorkflowService_FailoverDomain_Result, error) // UnwrapResponse takes the result struct for FailoverDomain // and returns the value or error returned by it. // // The error is non-nil only if FailoverDomain threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_FailoverDomain_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_FailoverDomain_Result) (*shared.FailoverDomainResponse, error) }{} func init() { WorkflowService_FailoverDomain_Helper.Args = func( failoverRequest *shared.FailoverDomainRequest, ) *WorkflowService_FailoverDomain_Args { return &WorkflowService_FailoverDomain_Args{ FailoverRequest: failoverRequest, } } WorkflowService_FailoverDomain_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_FailoverDomain_Helper.WrapResponse = func(success *shared.FailoverDomainResponse, err error) (*WorkflowService_FailoverDomain_Result, error) { if err == nil { return &WorkflowService_FailoverDomain_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_FailoverDomain_Result.BadRequestError") } return &WorkflowService_FailoverDomain_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_FailoverDomain_Result.EntityNotExistError") } return &WorkflowService_FailoverDomain_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_FailoverDomain_Result.ServiceBusyError") } return &WorkflowService_FailoverDomain_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_FailoverDomain_Result.DomainNotActiveError") } return &WorkflowService_FailoverDomain_Result{DomainNotActiveError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_FailoverDomain_Result.ClientVersionNotSupportedError") } return &WorkflowService_FailoverDomain_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_FailoverDomain_Result.AccessDeniedError") } return &WorkflowService_FailoverDomain_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_FailoverDomain_Helper.UnwrapResponse = func(result *WorkflowService_FailoverDomain_Result) (success *shared.FailoverDomainResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_FailoverDomain_Result represents the result of a WorkflowService.FailoverDomain function call. // // The result of a FailoverDomain execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_FailoverDomain_Result struct { // Value returned by FailoverDomain after a successful execution. Success *shared.FailoverDomainResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_FailoverDomain_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_FailoverDomain_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_FailoverDomain_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _FailoverDomainResponse_Read(w wire.Value) (*shared.FailoverDomainResponse, error) { var v shared.FailoverDomainResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_FailoverDomain_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_FailoverDomain_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_FailoverDomain_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_FailoverDomain_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _FailoverDomainResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_FailoverDomain_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_FailoverDomain_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_FailoverDomain_Result struct could not be encoded. func (v *WorkflowService_FailoverDomain_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_FailoverDomain_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _FailoverDomainResponse_Decode(sr stream.Reader) (*shared.FailoverDomainResponse, error) { var v shared.FailoverDomainResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_FailoverDomain_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_FailoverDomain_Result struct could not be generated from the wire // representation. func (v *WorkflowService_FailoverDomain_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _FailoverDomainResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_FailoverDomain_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_FailoverDomain_Result // struct. func (v *WorkflowService_FailoverDomain_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_FailoverDomain_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_FailoverDomain_Result match the // provided WorkflowService_FailoverDomain_Result. // // This function performs a deep comparison. func (v *WorkflowService_FailoverDomain_Result) Equals(rhs *WorkflowService_FailoverDomain_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_FailoverDomain_Result. func (v *WorkflowService_FailoverDomain_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_FailoverDomain_Result) GetSuccess() (o *shared.FailoverDomainResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_FailoverDomain_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_FailoverDomain_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_FailoverDomain_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_FailoverDomain_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_FailoverDomain_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_FailoverDomain_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_FailoverDomain_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_FailoverDomain_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_FailoverDomain_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_FailoverDomain_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_FailoverDomain_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_FailoverDomain_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_FailoverDomain_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "FailoverDomain" for this struct. func (v *WorkflowService_FailoverDomain_Result) MethodName() string { return "FailoverDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_FailoverDomain_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_GetClusterInfo_Args represents the arguments for the WorkflowService.GetClusterInfo function. // // The arguments for GetClusterInfo are sent and received over the wire as this struct. type WorkflowService_GetClusterInfo_Args struct { } // ToWire translates a WorkflowService_GetClusterInfo_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_GetClusterInfo_Args) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_GetClusterInfo_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_GetClusterInfo_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_GetClusterInfo_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_GetClusterInfo_Args) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a WorkflowService_GetClusterInfo_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_GetClusterInfo_Args struct could not be encoded. func (v *WorkflowService_GetClusterInfo_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_GetClusterInfo_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_GetClusterInfo_Args struct could not be generated from the wire // representation. func (v *WorkflowService_GetClusterInfo_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_GetClusterInfo_Args // struct. func (v *WorkflowService_GetClusterInfo_Args) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("WorkflowService_GetClusterInfo_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_GetClusterInfo_Args match the // provided WorkflowService_GetClusterInfo_Args. // // This function performs a deep comparison. func (v *WorkflowService_GetClusterInfo_Args) Equals(rhs *WorkflowService_GetClusterInfo_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_GetClusterInfo_Args. func (v *WorkflowService_GetClusterInfo_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetClusterInfo" for this struct. func (v *WorkflowService_GetClusterInfo_Args) MethodName() string { return "GetClusterInfo" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_GetClusterInfo_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_GetClusterInfo_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.GetClusterInfo // function. var WorkflowService_GetClusterInfo_Helper = struct { // Args accepts the parameters of GetClusterInfo in-order and returns // the arguments struct for the function. Args func() *WorkflowService_GetClusterInfo_Args // IsException returns true if the given error can be thrown // by GetClusterInfo. // // An error can be thrown by GetClusterInfo only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetClusterInfo // given its return value and error. // // This allows mapping values and errors returned by // GetClusterInfo into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetClusterInfo // // value, err := GetClusterInfo(args) // result, err := WorkflowService_GetClusterInfo_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetClusterInfo: %v", err) // } // serialize(result) WrapResponse func(*shared.ClusterInfo, error) (*WorkflowService_GetClusterInfo_Result, error) // UnwrapResponse takes the result struct for GetClusterInfo // and returns the value or error returned by it. // // The error is non-nil only if GetClusterInfo threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_GetClusterInfo_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_GetClusterInfo_Result) (*shared.ClusterInfo, error) }{} func init() { WorkflowService_GetClusterInfo_Helper.Args = func() *WorkflowService_GetClusterInfo_Args { return &WorkflowService_GetClusterInfo_Args{} } WorkflowService_GetClusterInfo_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_GetClusterInfo_Helper.WrapResponse = func(success *shared.ClusterInfo, err error) (*WorkflowService_GetClusterInfo_Result, error) { if err == nil { return &WorkflowService_GetClusterInfo_Result{Success: success}, nil } switch e := err.(type) { case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetClusterInfo_Result.InternalServiceError") } return &WorkflowService_GetClusterInfo_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetClusterInfo_Result.ServiceBusyError") } return &WorkflowService_GetClusterInfo_Result{ServiceBusyError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetClusterInfo_Result.AccessDeniedError") } return &WorkflowService_GetClusterInfo_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_GetClusterInfo_Helper.UnwrapResponse = func(result *WorkflowService_GetClusterInfo_Result) (success *shared.ClusterInfo, err error) { if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_GetClusterInfo_Result represents the result of a WorkflowService.GetClusterInfo function call. // // The result of a GetClusterInfo execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_GetClusterInfo_Result struct { // Value returned by GetClusterInfo after a successful execution. Success *shared.ClusterInfo `json:"success,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_GetClusterInfo_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_GetClusterInfo_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_GetClusterInfo_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ClusterInfo_Read(w wire.Value) (*shared.ClusterInfo, error) { var v shared.ClusterInfo err := v.FromWire(w) return &v, err } func _InternalServiceError_Read(w wire.Value) (*shared.InternalServiceError, error) { var v shared.InternalServiceError err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_GetClusterInfo_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_GetClusterInfo_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_GetClusterInfo_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_GetClusterInfo_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ClusterInfo_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetClusterInfo_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_GetClusterInfo_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_GetClusterInfo_Result struct could not be encoded. func (v *WorkflowService_GetClusterInfo_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetClusterInfo_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ClusterInfo_Decode(sr stream.Reader) (*shared.ClusterInfo, error) { var v shared.ClusterInfo err := v.Decode(sr) return &v, err } func _InternalServiceError_Decode(sr stream.Reader) (*shared.InternalServiceError, error) { var v shared.InternalServiceError err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_GetClusterInfo_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_GetClusterInfo_Result struct could not be generated from the wire // representation. func (v *WorkflowService_GetClusterInfo_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ClusterInfo_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetClusterInfo_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_GetClusterInfo_Result // struct. func (v *WorkflowService_GetClusterInfo_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_GetClusterInfo_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_GetClusterInfo_Result match the // provided WorkflowService_GetClusterInfo_Result. // // This function performs a deep comparison. func (v *WorkflowService_GetClusterInfo_Result) Equals(rhs *WorkflowService_GetClusterInfo_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_GetClusterInfo_Result. func (v *WorkflowService_GetClusterInfo_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_GetClusterInfo_Result) GetSuccess() (o *shared.ClusterInfo) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_GetClusterInfo_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetClusterInfo_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *WorkflowService_GetClusterInfo_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetClusterInfo_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_GetClusterInfo_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetClusterInfo_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_GetClusterInfo_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetClusterInfo" for this struct. func (v *WorkflowService_GetClusterInfo_Result) MethodName() string { return "GetClusterInfo" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_GetClusterInfo_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_GetSearchAttributes_Args represents the arguments for the WorkflowService.GetSearchAttributes function. // // The arguments for GetSearchAttributes are sent and received over the wire as this struct. type WorkflowService_GetSearchAttributes_Args struct { } // ToWire translates a WorkflowService_GetSearchAttributes_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_GetSearchAttributes_Args) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_GetSearchAttributes_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_GetSearchAttributes_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_GetSearchAttributes_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_GetSearchAttributes_Args) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a WorkflowService_GetSearchAttributes_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_GetSearchAttributes_Args struct could not be encoded. func (v *WorkflowService_GetSearchAttributes_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_GetSearchAttributes_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_GetSearchAttributes_Args struct could not be generated from the wire // representation. func (v *WorkflowService_GetSearchAttributes_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_GetSearchAttributes_Args // struct. func (v *WorkflowService_GetSearchAttributes_Args) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("WorkflowService_GetSearchAttributes_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_GetSearchAttributes_Args match the // provided WorkflowService_GetSearchAttributes_Args. // // This function performs a deep comparison. func (v *WorkflowService_GetSearchAttributes_Args) Equals(rhs *WorkflowService_GetSearchAttributes_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_GetSearchAttributes_Args. func (v *WorkflowService_GetSearchAttributes_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetSearchAttributes" for this struct. func (v *WorkflowService_GetSearchAttributes_Args) MethodName() string { return "GetSearchAttributes" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_GetSearchAttributes_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_GetSearchAttributes_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.GetSearchAttributes // function. var WorkflowService_GetSearchAttributes_Helper = struct { // Args accepts the parameters of GetSearchAttributes in-order and returns // the arguments struct for the function. Args func() *WorkflowService_GetSearchAttributes_Args // IsException returns true if the given error can be thrown // by GetSearchAttributes. // // An error can be thrown by GetSearchAttributes only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetSearchAttributes // given its return value and error. // // This allows mapping values and errors returned by // GetSearchAttributes into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetSearchAttributes // // value, err := GetSearchAttributes(args) // result, err := WorkflowService_GetSearchAttributes_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetSearchAttributes: %v", err) // } // serialize(result) WrapResponse func(*shared.GetSearchAttributesResponse, error) (*WorkflowService_GetSearchAttributes_Result, error) // UnwrapResponse takes the result struct for GetSearchAttributes // and returns the value or error returned by it. // // The error is non-nil only if GetSearchAttributes threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_GetSearchAttributes_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_GetSearchAttributes_Result) (*shared.GetSearchAttributesResponse, error) }{} func init() { WorkflowService_GetSearchAttributes_Helper.Args = func() *WorkflowService_GetSearchAttributes_Args { return &WorkflowService_GetSearchAttributes_Args{} } WorkflowService_GetSearchAttributes_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_GetSearchAttributes_Helper.WrapResponse = func(success *shared.GetSearchAttributesResponse, err error) (*WorkflowService_GetSearchAttributes_Result, error) { if err == nil { return &WorkflowService_GetSearchAttributes_Result{Success: success}, nil } switch e := err.(type) { case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetSearchAttributes_Result.ServiceBusyError") } return &WorkflowService_GetSearchAttributes_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetSearchAttributes_Result.ClientVersionNotSupportedError") } return &WorkflowService_GetSearchAttributes_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetSearchAttributes_Result.AccessDeniedError") } return &WorkflowService_GetSearchAttributes_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_GetSearchAttributes_Helper.UnwrapResponse = func(result *WorkflowService_GetSearchAttributes_Result) (success *shared.GetSearchAttributesResponse, err error) { if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_GetSearchAttributes_Result represents the result of a WorkflowService.GetSearchAttributes function call. // // The result of a GetSearchAttributes execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_GetSearchAttributes_Result struct { // Value returned by GetSearchAttributes after a successful execution. Success *shared.GetSearchAttributesResponse `json:"success,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_GetSearchAttributes_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_GetSearchAttributes_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_GetSearchAttributes_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetSearchAttributesResponse_Read(w wire.Value) (*shared.GetSearchAttributesResponse, error) { var v shared.GetSearchAttributesResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_GetSearchAttributes_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_GetSearchAttributes_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_GetSearchAttributes_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_GetSearchAttributes_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetSearchAttributesResponse_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetSearchAttributes_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_GetSearchAttributes_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_GetSearchAttributes_Result struct could not be encoded. func (v *WorkflowService_GetSearchAttributes_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetSearchAttributes_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetSearchAttributesResponse_Decode(sr stream.Reader) (*shared.GetSearchAttributesResponse, error) { var v shared.GetSearchAttributesResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_GetSearchAttributes_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_GetSearchAttributes_Result struct could not be generated from the wire // representation. func (v *WorkflowService_GetSearchAttributes_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetSearchAttributesResponse_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetSearchAttributes_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_GetSearchAttributes_Result // struct. func (v *WorkflowService_GetSearchAttributes_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_GetSearchAttributes_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_GetSearchAttributes_Result match the // provided WorkflowService_GetSearchAttributes_Result. // // This function performs a deep comparison. func (v *WorkflowService_GetSearchAttributes_Result) Equals(rhs *WorkflowService_GetSearchAttributes_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_GetSearchAttributes_Result. func (v *WorkflowService_GetSearchAttributes_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_GetSearchAttributes_Result) GetSuccess() (o *shared.GetSearchAttributesResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_GetSearchAttributes_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetSearchAttributes_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_GetSearchAttributes_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetSearchAttributes_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_GetSearchAttributes_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetSearchAttributes_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_GetSearchAttributes_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetSearchAttributes" for this struct. func (v *WorkflowService_GetSearchAttributes_Result) MethodName() string { return "GetSearchAttributes" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_GetSearchAttributes_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_GetTaskListsByDomain_Args represents the arguments for the WorkflowService.GetTaskListsByDomain function. // // The arguments for GetTaskListsByDomain are sent and received over the wire as this struct. type WorkflowService_GetTaskListsByDomain_Args struct { Request *shared.GetTaskListsByDomainRequest `json:"request,omitempty"` } // ToWire translates a WorkflowService_GetTaskListsByDomain_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_GetTaskListsByDomain_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetTaskListsByDomainRequest_Read(w wire.Value) (*shared.GetTaskListsByDomainRequest, error) { var v shared.GetTaskListsByDomainRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_GetTaskListsByDomain_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_GetTaskListsByDomain_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_GetTaskListsByDomain_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_GetTaskListsByDomain_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetTaskListsByDomainRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_GetTaskListsByDomain_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_GetTaskListsByDomain_Args struct could not be encoded. func (v *WorkflowService_GetTaskListsByDomain_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetTaskListsByDomainRequest_Decode(sr stream.Reader) (*shared.GetTaskListsByDomainRequest, error) { var v shared.GetTaskListsByDomainRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_GetTaskListsByDomain_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_GetTaskListsByDomain_Args struct could not be generated from the wire // representation. func (v *WorkflowService_GetTaskListsByDomain_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetTaskListsByDomainRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_GetTaskListsByDomain_Args // struct. func (v *WorkflowService_GetTaskListsByDomain_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("WorkflowService_GetTaskListsByDomain_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_GetTaskListsByDomain_Args match the // provided WorkflowService_GetTaskListsByDomain_Args. // // This function performs a deep comparison. func (v *WorkflowService_GetTaskListsByDomain_Args) Equals(rhs *WorkflowService_GetTaskListsByDomain_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_GetTaskListsByDomain_Args. func (v *WorkflowService_GetTaskListsByDomain_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *WorkflowService_GetTaskListsByDomain_Args) GetRequest() (o *shared.GetTaskListsByDomainRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *WorkflowService_GetTaskListsByDomain_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetTaskListsByDomain" for this struct. func (v *WorkflowService_GetTaskListsByDomain_Args) MethodName() string { return "GetTaskListsByDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_GetTaskListsByDomain_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_GetTaskListsByDomain_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.GetTaskListsByDomain // function. var WorkflowService_GetTaskListsByDomain_Helper = struct { // Args accepts the parameters of GetTaskListsByDomain in-order and returns // the arguments struct for the function. Args func( request *shared.GetTaskListsByDomainRequest, ) *WorkflowService_GetTaskListsByDomain_Args // IsException returns true if the given error can be thrown // by GetTaskListsByDomain. // // An error can be thrown by GetTaskListsByDomain only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetTaskListsByDomain // given its return value and error. // // This allows mapping values and errors returned by // GetTaskListsByDomain into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetTaskListsByDomain // // value, err := GetTaskListsByDomain(args) // result, err := WorkflowService_GetTaskListsByDomain_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetTaskListsByDomain: %v", err) // } // serialize(result) WrapResponse func(*shared.GetTaskListsByDomainResponse, error) (*WorkflowService_GetTaskListsByDomain_Result, error) // UnwrapResponse takes the result struct for GetTaskListsByDomain // and returns the value or error returned by it. // // The error is non-nil only if GetTaskListsByDomain threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_GetTaskListsByDomain_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_GetTaskListsByDomain_Result) (*shared.GetTaskListsByDomainResponse, error) }{} func init() { WorkflowService_GetTaskListsByDomain_Helper.Args = func( request *shared.GetTaskListsByDomainRequest, ) *WorkflowService_GetTaskListsByDomain_Args { return &WorkflowService_GetTaskListsByDomain_Args{ Request: request, } } WorkflowService_GetTaskListsByDomain_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_GetTaskListsByDomain_Helper.WrapResponse = func(success *shared.GetTaskListsByDomainResponse, err error) (*WorkflowService_GetTaskListsByDomain_Result, error) { if err == nil { return &WorkflowService_GetTaskListsByDomain_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetTaskListsByDomain_Result.BadRequestError") } return &WorkflowService_GetTaskListsByDomain_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetTaskListsByDomain_Result.EntityNotExistError") } return &WorkflowService_GetTaskListsByDomain_Result{EntityNotExistError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetTaskListsByDomain_Result.LimitExceededError") } return &WorkflowService_GetTaskListsByDomain_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetTaskListsByDomain_Result.ServiceBusyError") } return &WorkflowService_GetTaskListsByDomain_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetTaskListsByDomain_Result.ClientVersionNotSupportedError") } return &WorkflowService_GetTaskListsByDomain_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetTaskListsByDomain_Result.AccessDeniedError") } return &WorkflowService_GetTaskListsByDomain_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_GetTaskListsByDomain_Helper.UnwrapResponse = func(result *WorkflowService_GetTaskListsByDomain_Result) (success *shared.GetTaskListsByDomainResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_GetTaskListsByDomain_Result represents the result of a WorkflowService.GetTaskListsByDomain function call. // // The result of a GetTaskListsByDomain execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_GetTaskListsByDomain_Result struct { // Value returned by GetTaskListsByDomain after a successful execution. Success *shared.GetTaskListsByDomainResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_GetTaskListsByDomain_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_GetTaskListsByDomain_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_GetTaskListsByDomain_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetTaskListsByDomainResponse_Read(w wire.Value) (*shared.GetTaskListsByDomainResponse, error) { var v shared.GetTaskListsByDomainResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_GetTaskListsByDomain_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_GetTaskListsByDomain_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_GetTaskListsByDomain_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_GetTaskListsByDomain_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetTaskListsByDomainResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetTaskListsByDomain_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_GetTaskListsByDomain_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_GetTaskListsByDomain_Result struct could not be encoded. func (v *WorkflowService_GetTaskListsByDomain_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetTaskListsByDomain_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetTaskListsByDomainResponse_Decode(sr stream.Reader) (*shared.GetTaskListsByDomainResponse, error) { var v shared.GetTaskListsByDomainResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_GetTaskListsByDomain_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_GetTaskListsByDomain_Result struct could not be generated from the wire // representation. func (v *WorkflowService_GetTaskListsByDomain_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetTaskListsByDomainResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetTaskListsByDomain_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_GetTaskListsByDomain_Result // struct. func (v *WorkflowService_GetTaskListsByDomain_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_GetTaskListsByDomain_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_GetTaskListsByDomain_Result match the // provided WorkflowService_GetTaskListsByDomain_Result. // // This function performs a deep comparison. func (v *WorkflowService_GetTaskListsByDomain_Result) Equals(rhs *WorkflowService_GetTaskListsByDomain_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_GetTaskListsByDomain_Result. func (v *WorkflowService_GetTaskListsByDomain_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_GetTaskListsByDomain_Result) GetSuccess() (o *shared.GetTaskListsByDomainResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_GetTaskListsByDomain_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetTaskListsByDomain_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_GetTaskListsByDomain_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetTaskListsByDomain_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_GetTaskListsByDomain_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetTaskListsByDomain_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_GetTaskListsByDomain_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetTaskListsByDomain_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_GetTaskListsByDomain_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetTaskListsByDomain_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_GetTaskListsByDomain_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetTaskListsByDomain_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_GetTaskListsByDomain_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetTaskListsByDomain" for this struct. func (v *WorkflowService_GetTaskListsByDomain_Result) MethodName() string { return "GetTaskListsByDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_GetTaskListsByDomain_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_GetWorkflowExecutionHistory_Args represents the arguments for the WorkflowService.GetWorkflowExecutionHistory function. // // The arguments for GetWorkflowExecutionHistory are sent and received over the wire as this struct. type WorkflowService_GetWorkflowExecutionHistory_Args struct { GetRequest *shared.GetWorkflowExecutionHistoryRequest `json:"getRequest,omitempty"` } // ToWire translates a WorkflowService_GetWorkflowExecutionHistory_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_GetWorkflowExecutionHistory_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.GetRequest != nil { w, err = v.GetRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetWorkflowExecutionHistoryRequest_Read(w wire.Value) (*shared.GetWorkflowExecutionHistoryRequest, error) { var v shared.GetWorkflowExecutionHistoryRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_GetWorkflowExecutionHistory_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_GetWorkflowExecutionHistory_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_GetWorkflowExecutionHistory_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_GetWorkflowExecutionHistory_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.GetRequest, err = _GetWorkflowExecutionHistoryRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_GetWorkflowExecutionHistory_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_GetWorkflowExecutionHistory_Args struct could not be encoded. func (v *WorkflowService_GetWorkflowExecutionHistory_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.GetRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.GetRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetWorkflowExecutionHistoryRequest_Decode(sr stream.Reader) (*shared.GetWorkflowExecutionHistoryRequest, error) { var v shared.GetWorkflowExecutionHistoryRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_GetWorkflowExecutionHistory_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_GetWorkflowExecutionHistory_Args struct could not be generated from the wire // representation. func (v *WorkflowService_GetWorkflowExecutionHistory_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.GetRequest, err = _GetWorkflowExecutionHistoryRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_GetWorkflowExecutionHistory_Args // struct. func (v *WorkflowService_GetWorkflowExecutionHistory_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.GetRequest != nil { fields[i] = fmt.Sprintf("GetRequest: %v", v.GetRequest) i++ } return fmt.Sprintf("WorkflowService_GetWorkflowExecutionHistory_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_GetWorkflowExecutionHistory_Args match the // provided WorkflowService_GetWorkflowExecutionHistory_Args. // // This function performs a deep comparison. func (v *WorkflowService_GetWorkflowExecutionHistory_Args) Equals(rhs *WorkflowService_GetWorkflowExecutionHistory_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.GetRequest == nil && rhs.GetRequest == nil) || (v.GetRequest != nil && rhs.GetRequest != nil && v.GetRequest.Equals(rhs.GetRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_GetWorkflowExecutionHistory_Args. func (v *WorkflowService_GetWorkflowExecutionHistory_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.GetRequest != nil { err = multierr.Append(err, enc.AddObject("getRequest", v.GetRequest)) } return err } // GetGetRequest returns the value of GetRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_GetWorkflowExecutionHistory_Args) GetGetRequest() (o *shared.GetWorkflowExecutionHistoryRequest) { if v != nil && v.GetRequest != nil { return v.GetRequest } return } // IsSetGetRequest returns true if GetRequest is not nil. func (v *WorkflowService_GetWorkflowExecutionHistory_Args) IsSetGetRequest() bool { return v != nil && v.GetRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetWorkflowExecutionHistory" for this struct. func (v *WorkflowService_GetWorkflowExecutionHistory_Args) MethodName() string { return "GetWorkflowExecutionHistory" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_GetWorkflowExecutionHistory_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_GetWorkflowExecutionHistory_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.GetWorkflowExecutionHistory // function. var WorkflowService_GetWorkflowExecutionHistory_Helper = struct { // Args accepts the parameters of GetWorkflowExecutionHistory in-order and returns // the arguments struct for the function. Args func( getRequest *shared.GetWorkflowExecutionHistoryRequest, ) *WorkflowService_GetWorkflowExecutionHistory_Args // IsException returns true if the given error can be thrown // by GetWorkflowExecutionHistory. // // An error can be thrown by GetWorkflowExecutionHistory only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetWorkflowExecutionHistory // given its return value and error. // // This allows mapping values and errors returned by // GetWorkflowExecutionHistory into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetWorkflowExecutionHistory // // value, err := GetWorkflowExecutionHistory(args) // result, err := WorkflowService_GetWorkflowExecutionHistory_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetWorkflowExecutionHistory: %v", err) // } // serialize(result) WrapResponse func(*shared.GetWorkflowExecutionHistoryResponse, error) (*WorkflowService_GetWorkflowExecutionHistory_Result, error) // UnwrapResponse takes the result struct for GetWorkflowExecutionHistory // and returns the value or error returned by it. // // The error is non-nil only if GetWorkflowExecutionHistory threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_GetWorkflowExecutionHistory_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_GetWorkflowExecutionHistory_Result) (*shared.GetWorkflowExecutionHistoryResponse, error) }{} func init() { WorkflowService_GetWorkflowExecutionHistory_Helper.Args = func( getRequest *shared.GetWorkflowExecutionHistoryRequest, ) *WorkflowService_GetWorkflowExecutionHistory_Args { return &WorkflowService_GetWorkflowExecutionHistory_Args{ GetRequest: getRequest, } } WorkflowService_GetWorkflowExecutionHistory_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_GetWorkflowExecutionHistory_Helper.WrapResponse = func(success *shared.GetWorkflowExecutionHistoryResponse, err error) (*WorkflowService_GetWorkflowExecutionHistory_Result, error) { if err == nil { return &WorkflowService_GetWorkflowExecutionHistory_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetWorkflowExecutionHistory_Result.BadRequestError") } return &WorkflowService_GetWorkflowExecutionHistory_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetWorkflowExecutionHistory_Result.EntityNotExistError") } return &WorkflowService_GetWorkflowExecutionHistory_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetWorkflowExecutionHistory_Result.ServiceBusyError") } return &WorkflowService_GetWorkflowExecutionHistory_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetWorkflowExecutionHistory_Result.ClientVersionNotSupportedError") } return &WorkflowService_GetWorkflowExecutionHistory_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_GetWorkflowExecutionHistory_Result.AccessDeniedError") } return &WorkflowService_GetWorkflowExecutionHistory_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_GetWorkflowExecutionHistory_Helper.UnwrapResponse = func(result *WorkflowService_GetWorkflowExecutionHistory_Result) (success *shared.GetWorkflowExecutionHistoryResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_GetWorkflowExecutionHistory_Result represents the result of a WorkflowService.GetWorkflowExecutionHistory function call. // // The result of a GetWorkflowExecutionHistory execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_GetWorkflowExecutionHistory_Result struct { // Value returned by GetWorkflowExecutionHistory after a successful execution. Success *shared.GetWorkflowExecutionHistoryResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_GetWorkflowExecutionHistory_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_GetWorkflowExecutionHistory_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_GetWorkflowExecutionHistory_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetWorkflowExecutionHistoryResponse_Read(w wire.Value) (*shared.GetWorkflowExecutionHistoryResponse, error) { var v shared.GetWorkflowExecutionHistoryResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_GetWorkflowExecutionHistory_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_GetWorkflowExecutionHistory_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_GetWorkflowExecutionHistory_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_GetWorkflowExecutionHistory_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetWorkflowExecutionHistoryResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetWorkflowExecutionHistory_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_GetWorkflowExecutionHistory_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_GetWorkflowExecutionHistory_Result struct could not be encoded. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetWorkflowExecutionHistory_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetWorkflowExecutionHistoryResponse_Decode(sr stream.Reader) (*shared.GetWorkflowExecutionHistoryResponse, error) { var v shared.GetWorkflowExecutionHistoryResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_GetWorkflowExecutionHistory_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_GetWorkflowExecutionHistory_Result struct could not be generated from the wire // representation. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetWorkflowExecutionHistoryResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_GetWorkflowExecutionHistory_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_GetWorkflowExecutionHistory_Result // struct. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_GetWorkflowExecutionHistory_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_GetWorkflowExecutionHistory_Result match the // provided WorkflowService_GetWorkflowExecutionHistory_Result. // // This function performs a deep comparison. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) Equals(rhs *WorkflowService_GetWorkflowExecutionHistory_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_GetWorkflowExecutionHistory_Result. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) GetSuccess() (o *shared.GetWorkflowExecutionHistoryResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetWorkflowExecutionHistory" for this struct. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) MethodName() string { return "GetWorkflowExecutionHistory" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_GetWorkflowExecutionHistory_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_ListArchivedWorkflowExecutions_Args represents the arguments for the WorkflowService.ListArchivedWorkflowExecutions function. // // The arguments for ListArchivedWorkflowExecutions are sent and received over the wire as this struct. type WorkflowService_ListArchivedWorkflowExecutions_Args struct { ListRequest *shared.ListArchivedWorkflowExecutionsRequest `json:"listRequest,omitempty"` } // ToWire translates a WorkflowService_ListArchivedWorkflowExecutions_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ListRequest != nil { w, err = v.ListRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListArchivedWorkflowExecutionsRequest_Read(w wire.Value) (*shared.ListArchivedWorkflowExecutionsRequest, error) { var v shared.ListArchivedWorkflowExecutionsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListArchivedWorkflowExecutions_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListArchivedWorkflowExecutions_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListArchivedWorkflowExecutions_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ListRequest, err = _ListArchivedWorkflowExecutionsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_ListArchivedWorkflowExecutions_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListArchivedWorkflowExecutions_Args struct could not be encoded. func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ListRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ListRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ListArchivedWorkflowExecutionsRequest_Decode(sr stream.Reader) (*shared.ListArchivedWorkflowExecutionsRequest, error) { var v shared.ListArchivedWorkflowExecutionsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListArchivedWorkflowExecutions_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListArchivedWorkflowExecutions_Args struct could not be generated from the wire // representation. func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ListRequest, err = _ListArchivedWorkflowExecutionsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_ListArchivedWorkflowExecutions_Args // struct. func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ListRequest != nil { fields[i] = fmt.Sprintf("ListRequest: %v", v.ListRequest) i++ } return fmt.Sprintf("WorkflowService_ListArchivedWorkflowExecutions_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListArchivedWorkflowExecutions_Args match the // provided WorkflowService_ListArchivedWorkflowExecutions_Args. // // This function performs a deep comparison. func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) Equals(rhs *WorkflowService_ListArchivedWorkflowExecutions_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ListRequest == nil && rhs.ListRequest == nil) || (v.ListRequest != nil && rhs.ListRequest != nil && v.ListRequest.Equals(rhs.ListRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListArchivedWorkflowExecutions_Args. func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ListRequest != nil { err = multierr.Append(err, enc.AddObject("listRequest", v.ListRequest)) } return err } // GetListRequest returns the value of ListRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) GetListRequest() (o *shared.ListArchivedWorkflowExecutionsRequest) { if v != nil && v.ListRequest != nil { return v.ListRequest } return } // IsSetListRequest returns true if ListRequest is not nil. func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) IsSetListRequest() bool { return v != nil && v.ListRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ListArchivedWorkflowExecutions" for this struct. func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) MethodName() string { return "ListArchivedWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_ListArchivedWorkflowExecutions_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_ListArchivedWorkflowExecutions_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.ListArchivedWorkflowExecutions // function. var WorkflowService_ListArchivedWorkflowExecutions_Helper = struct { // Args accepts the parameters of ListArchivedWorkflowExecutions in-order and returns // the arguments struct for the function. Args func( listRequest *shared.ListArchivedWorkflowExecutionsRequest, ) *WorkflowService_ListArchivedWorkflowExecutions_Args // IsException returns true if the given error can be thrown // by ListArchivedWorkflowExecutions. // // An error can be thrown by ListArchivedWorkflowExecutions only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ListArchivedWorkflowExecutions // given its return value and error. // // This allows mapping values and errors returned by // ListArchivedWorkflowExecutions into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ListArchivedWorkflowExecutions // // value, err := ListArchivedWorkflowExecutions(args) // result, err := WorkflowService_ListArchivedWorkflowExecutions_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ListArchivedWorkflowExecutions: %v", err) // } // serialize(result) WrapResponse func(*shared.ListArchivedWorkflowExecutionsResponse, error) (*WorkflowService_ListArchivedWorkflowExecutions_Result, error) // UnwrapResponse takes the result struct for ListArchivedWorkflowExecutions // and returns the value or error returned by it. // // The error is non-nil only if ListArchivedWorkflowExecutions threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_ListArchivedWorkflowExecutions_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_ListArchivedWorkflowExecutions_Result) (*shared.ListArchivedWorkflowExecutionsResponse, error) }{} func init() { WorkflowService_ListArchivedWorkflowExecutions_Helper.Args = func( listRequest *shared.ListArchivedWorkflowExecutionsRequest, ) *WorkflowService_ListArchivedWorkflowExecutions_Args { return &WorkflowService_ListArchivedWorkflowExecutions_Args{ ListRequest: listRequest, } } WorkflowService_ListArchivedWorkflowExecutions_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_ListArchivedWorkflowExecutions_Helper.WrapResponse = func(success *shared.ListArchivedWorkflowExecutionsResponse, err error) (*WorkflowService_ListArchivedWorkflowExecutions_Result, error) { if err == nil { return &WorkflowService_ListArchivedWorkflowExecutions_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListArchivedWorkflowExecutions_Result.BadRequestError") } return &WorkflowService_ListArchivedWorkflowExecutions_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListArchivedWorkflowExecutions_Result.EntityNotExistError") } return &WorkflowService_ListArchivedWorkflowExecutions_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListArchivedWorkflowExecutions_Result.ServiceBusyError") } return &WorkflowService_ListArchivedWorkflowExecutions_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListArchivedWorkflowExecutions_Result.ClientVersionNotSupportedError") } return &WorkflowService_ListArchivedWorkflowExecutions_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListArchivedWorkflowExecutions_Result.AccessDeniedError") } return &WorkflowService_ListArchivedWorkflowExecutions_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_ListArchivedWorkflowExecutions_Helper.UnwrapResponse = func(result *WorkflowService_ListArchivedWorkflowExecutions_Result) (success *shared.ListArchivedWorkflowExecutionsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_ListArchivedWorkflowExecutions_Result represents the result of a WorkflowService.ListArchivedWorkflowExecutions function call. // // The result of a ListArchivedWorkflowExecutions execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_ListArchivedWorkflowExecutions_Result struct { // Value returned by ListArchivedWorkflowExecutions after a successful execution. Success *shared.ListArchivedWorkflowExecutionsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_ListArchivedWorkflowExecutions_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_ListArchivedWorkflowExecutions_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListArchivedWorkflowExecutionsResponse_Read(w wire.Value) (*shared.ListArchivedWorkflowExecutionsResponse, error) { var v shared.ListArchivedWorkflowExecutionsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListArchivedWorkflowExecutions_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListArchivedWorkflowExecutions_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListArchivedWorkflowExecutions_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ListArchivedWorkflowExecutionsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListArchivedWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_ListArchivedWorkflowExecutions_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListArchivedWorkflowExecutions_Result struct could not be encoded. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListArchivedWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ListArchivedWorkflowExecutionsResponse_Decode(sr stream.Reader) (*shared.ListArchivedWorkflowExecutionsResponse, error) { var v shared.ListArchivedWorkflowExecutionsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListArchivedWorkflowExecutions_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListArchivedWorkflowExecutions_Result struct could not be generated from the wire // representation. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ListArchivedWorkflowExecutionsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListArchivedWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_ListArchivedWorkflowExecutions_Result // struct. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_ListArchivedWorkflowExecutions_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListArchivedWorkflowExecutions_Result match the // provided WorkflowService_ListArchivedWorkflowExecutions_Result. // // This function performs a deep comparison. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) Equals(rhs *WorkflowService_ListArchivedWorkflowExecutions_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListArchivedWorkflowExecutions_Result. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) GetSuccess() (o *shared.ListArchivedWorkflowExecutionsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ListArchivedWorkflowExecutions" for this struct. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) MethodName() string { return "ListArchivedWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_ListArchivedWorkflowExecutions_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_ListClosedWorkflowExecutions_Args represents the arguments for the WorkflowService.ListClosedWorkflowExecutions function. // // The arguments for ListClosedWorkflowExecutions are sent and received over the wire as this struct. type WorkflowService_ListClosedWorkflowExecutions_Args struct { ListRequest *shared.ListClosedWorkflowExecutionsRequest `json:"listRequest,omitempty"` } // ToWire translates a WorkflowService_ListClosedWorkflowExecutions_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListClosedWorkflowExecutions_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ListRequest != nil { w, err = v.ListRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListClosedWorkflowExecutionsRequest_Read(w wire.Value) (*shared.ListClosedWorkflowExecutionsRequest, error) { var v shared.ListClosedWorkflowExecutionsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListClosedWorkflowExecutions_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListClosedWorkflowExecutions_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListClosedWorkflowExecutions_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListClosedWorkflowExecutions_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ListRequest, err = _ListClosedWorkflowExecutionsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_ListClosedWorkflowExecutions_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListClosedWorkflowExecutions_Args struct could not be encoded. func (v *WorkflowService_ListClosedWorkflowExecutions_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ListRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ListRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ListClosedWorkflowExecutionsRequest_Decode(sr stream.Reader) (*shared.ListClosedWorkflowExecutionsRequest, error) { var v shared.ListClosedWorkflowExecutionsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListClosedWorkflowExecutions_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListClosedWorkflowExecutions_Args struct could not be generated from the wire // representation. func (v *WorkflowService_ListClosedWorkflowExecutions_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ListRequest, err = _ListClosedWorkflowExecutionsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_ListClosedWorkflowExecutions_Args // struct. func (v *WorkflowService_ListClosedWorkflowExecutions_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ListRequest != nil { fields[i] = fmt.Sprintf("ListRequest: %v", v.ListRequest) i++ } return fmt.Sprintf("WorkflowService_ListClosedWorkflowExecutions_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListClosedWorkflowExecutions_Args match the // provided WorkflowService_ListClosedWorkflowExecutions_Args. // // This function performs a deep comparison. func (v *WorkflowService_ListClosedWorkflowExecutions_Args) Equals(rhs *WorkflowService_ListClosedWorkflowExecutions_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ListRequest == nil && rhs.ListRequest == nil) || (v.ListRequest != nil && rhs.ListRequest != nil && v.ListRequest.Equals(rhs.ListRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListClosedWorkflowExecutions_Args. func (v *WorkflowService_ListClosedWorkflowExecutions_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ListRequest != nil { err = multierr.Append(err, enc.AddObject("listRequest", v.ListRequest)) } return err } // GetListRequest returns the value of ListRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_ListClosedWorkflowExecutions_Args) GetListRequest() (o *shared.ListClosedWorkflowExecutionsRequest) { if v != nil && v.ListRequest != nil { return v.ListRequest } return } // IsSetListRequest returns true if ListRequest is not nil. func (v *WorkflowService_ListClosedWorkflowExecutions_Args) IsSetListRequest() bool { return v != nil && v.ListRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ListClosedWorkflowExecutions" for this struct. func (v *WorkflowService_ListClosedWorkflowExecutions_Args) MethodName() string { return "ListClosedWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_ListClosedWorkflowExecutions_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_ListClosedWorkflowExecutions_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.ListClosedWorkflowExecutions // function. var WorkflowService_ListClosedWorkflowExecutions_Helper = struct { // Args accepts the parameters of ListClosedWorkflowExecutions in-order and returns // the arguments struct for the function. Args func( listRequest *shared.ListClosedWorkflowExecutionsRequest, ) *WorkflowService_ListClosedWorkflowExecutions_Args // IsException returns true if the given error can be thrown // by ListClosedWorkflowExecutions. // // An error can be thrown by ListClosedWorkflowExecutions only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ListClosedWorkflowExecutions // given its return value and error. // // This allows mapping values and errors returned by // ListClosedWorkflowExecutions into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ListClosedWorkflowExecutions // // value, err := ListClosedWorkflowExecutions(args) // result, err := WorkflowService_ListClosedWorkflowExecutions_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ListClosedWorkflowExecutions: %v", err) // } // serialize(result) WrapResponse func(*shared.ListClosedWorkflowExecutionsResponse, error) (*WorkflowService_ListClosedWorkflowExecutions_Result, error) // UnwrapResponse takes the result struct for ListClosedWorkflowExecutions // and returns the value or error returned by it. // // The error is non-nil only if ListClosedWorkflowExecutions threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_ListClosedWorkflowExecutions_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_ListClosedWorkflowExecutions_Result) (*shared.ListClosedWorkflowExecutionsResponse, error) }{} func init() { WorkflowService_ListClosedWorkflowExecutions_Helper.Args = func( listRequest *shared.ListClosedWorkflowExecutionsRequest, ) *WorkflowService_ListClosedWorkflowExecutions_Args { return &WorkflowService_ListClosedWorkflowExecutions_Args{ ListRequest: listRequest, } } WorkflowService_ListClosedWorkflowExecutions_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_ListClosedWorkflowExecutions_Helper.WrapResponse = func(success *shared.ListClosedWorkflowExecutionsResponse, err error) (*WorkflowService_ListClosedWorkflowExecutions_Result, error) { if err == nil { return &WorkflowService_ListClosedWorkflowExecutions_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListClosedWorkflowExecutions_Result.BadRequestError") } return &WorkflowService_ListClosedWorkflowExecutions_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListClosedWorkflowExecutions_Result.EntityNotExistError") } return &WorkflowService_ListClosedWorkflowExecutions_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListClosedWorkflowExecutions_Result.ServiceBusyError") } return &WorkflowService_ListClosedWorkflowExecutions_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListClosedWorkflowExecutions_Result.ClientVersionNotSupportedError") } return &WorkflowService_ListClosedWorkflowExecutions_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListClosedWorkflowExecutions_Result.AccessDeniedError") } return &WorkflowService_ListClosedWorkflowExecutions_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_ListClosedWorkflowExecutions_Helper.UnwrapResponse = func(result *WorkflowService_ListClosedWorkflowExecutions_Result) (success *shared.ListClosedWorkflowExecutionsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_ListClosedWorkflowExecutions_Result represents the result of a WorkflowService.ListClosedWorkflowExecutions function call. // // The result of a ListClosedWorkflowExecutions execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_ListClosedWorkflowExecutions_Result struct { // Value returned by ListClosedWorkflowExecutions after a successful execution. Success *shared.ListClosedWorkflowExecutionsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_ListClosedWorkflowExecutions_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListClosedWorkflowExecutions_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_ListClosedWorkflowExecutions_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListClosedWorkflowExecutionsResponse_Read(w wire.Value) (*shared.ListClosedWorkflowExecutionsResponse, error) { var v shared.ListClosedWorkflowExecutionsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListClosedWorkflowExecutions_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListClosedWorkflowExecutions_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListClosedWorkflowExecutions_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListClosedWorkflowExecutions_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ListClosedWorkflowExecutionsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListClosedWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_ListClosedWorkflowExecutions_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListClosedWorkflowExecutions_Result struct could not be encoded. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListClosedWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ListClosedWorkflowExecutionsResponse_Decode(sr stream.Reader) (*shared.ListClosedWorkflowExecutionsResponse, error) { var v shared.ListClosedWorkflowExecutionsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListClosedWorkflowExecutions_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListClosedWorkflowExecutions_Result struct could not be generated from the wire // representation. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ListClosedWorkflowExecutionsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListClosedWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_ListClosedWorkflowExecutions_Result // struct. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_ListClosedWorkflowExecutions_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListClosedWorkflowExecutions_Result match the // provided WorkflowService_ListClosedWorkflowExecutions_Result. // // This function performs a deep comparison. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) Equals(rhs *WorkflowService_ListClosedWorkflowExecutions_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListClosedWorkflowExecutions_Result. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) GetSuccess() (o *shared.ListClosedWorkflowExecutionsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ListClosedWorkflowExecutions" for this struct. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) MethodName() string { return "ListClosedWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_ListClosedWorkflowExecutions_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_ListDomains_Args represents the arguments for the WorkflowService.ListDomains function. // // The arguments for ListDomains are sent and received over the wire as this struct. type WorkflowService_ListDomains_Args struct { ListRequest *shared.ListDomainsRequest `json:"listRequest,omitempty"` } // ToWire translates a WorkflowService_ListDomains_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListDomains_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ListRequest != nil { w, err = v.ListRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListDomainsRequest_Read(w wire.Value) (*shared.ListDomainsRequest, error) { var v shared.ListDomainsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListDomains_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListDomains_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListDomains_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListDomains_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ListRequest, err = _ListDomainsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_ListDomains_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListDomains_Args struct could not be encoded. func (v *WorkflowService_ListDomains_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ListRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ListRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ListDomainsRequest_Decode(sr stream.Reader) (*shared.ListDomainsRequest, error) { var v shared.ListDomainsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListDomains_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListDomains_Args struct could not be generated from the wire // representation. func (v *WorkflowService_ListDomains_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ListRequest, err = _ListDomainsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_ListDomains_Args // struct. func (v *WorkflowService_ListDomains_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ListRequest != nil { fields[i] = fmt.Sprintf("ListRequest: %v", v.ListRequest) i++ } return fmt.Sprintf("WorkflowService_ListDomains_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListDomains_Args match the // provided WorkflowService_ListDomains_Args. // // This function performs a deep comparison. func (v *WorkflowService_ListDomains_Args) Equals(rhs *WorkflowService_ListDomains_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ListRequest == nil && rhs.ListRequest == nil) || (v.ListRequest != nil && rhs.ListRequest != nil && v.ListRequest.Equals(rhs.ListRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListDomains_Args. func (v *WorkflowService_ListDomains_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ListRequest != nil { err = multierr.Append(err, enc.AddObject("listRequest", v.ListRequest)) } return err } // GetListRequest returns the value of ListRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_ListDomains_Args) GetListRequest() (o *shared.ListDomainsRequest) { if v != nil && v.ListRequest != nil { return v.ListRequest } return } // IsSetListRequest returns true if ListRequest is not nil. func (v *WorkflowService_ListDomains_Args) IsSetListRequest() bool { return v != nil && v.ListRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ListDomains" for this struct. func (v *WorkflowService_ListDomains_Args) MethodName() string { return "ListDomains" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_ListDomains_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_ListDomains_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.ListDomains // function. var WorkflowService_ListDomains_Helper = struct { // Args accepts the parameters of ListDomains in-order and returns // the arguments struct for the function. Args func( listRequest *shared.ListDomainsRequest, ) *WorkflowService_ListDomains_Args // IsException returns true if the given error can be thrown // by ListDomains. // // An error can be thrown by ListDomains only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ListDomains // given its return value and error. // // This allows mapping values and errors returned by // ListDomains into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ListDomains // // value, err := ListDomains(args) // result, err := WorkflowService_ListDomains_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ListDomains: %v", err) // } // serialize(result) WrapResponse func(*shared.ListDomainsResponse, error) (*WorkflowService_ListDomains_Result, error) // UnwrapResponse takes the result struct for ListDomains // and returns the value or error returned by it. // // The error is non-nil only if ListDomains threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_ListDomains_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_ListDomains_Result) (*shared.ListDomainsResponse, error) }{} func init() { WorkflowService_ListDomains_Helper.Args = func( listRequest *shared.ListDomainsRequest, ) *WorkflowService_ListDomains_Args { return &WorkflowService_ListDomains_Args{ ListRequest: listRequest, } } WorkflowService_ListDomains_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_ListDomains_Helper.WrapResponse = func(success *shared.ListDomainsResponse, err error) (*WorkflowService_ListDomains_Result, error) { if err == nil { return &WorkflowService_ListDomains_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListDomains_Result.BadRequestError") } return &WorkflowService_ListDomains_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListDomains_Result.EntityNotExistError") } return &WorkflowService_ListDomains_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListDomains_Result.ServiceBusyError") } return &WorkflowService_ListDomains_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListDomains_Result.ClientVersionNotSupportedError") } return &WorkflowService_ListDomains_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListDomains_Result.AccessDeniedError") } return &WorkflowService_ListDomains_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_ListDomains_Helper.UnwrapResponse = func(result *WorkflowService_ListDomains_Result) (success *shared.ListDomainsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_ListDomains_Result represents the result of a WorkflowService.ListDomains function call. // // The result of a ListDomains execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_ListDomains_Result struct { // Value returned by ListDomains after a successful execution. Success *shared.ListDomainsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_ListDomains_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListDomains_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_ListDomains_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListDomainsResponse_Read(w wire.Value) (*shared.ListDomainsResponse, error) { var v shared.ListDomainsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListDomains_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListDomains_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListDomains_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListDomains_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ListDomainsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListDomains_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_ListDomains_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListDomains_Result struct could not be encoded. func (v *WorkflowService_ListDomains_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListDomains_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ListDomainsResponse_Decode(sr stream.Reader) (*shared.ListDomainsResponse, error) { var v shared.ListDomainsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListDomains_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListDomains_Result struct could not be generated from the wire // representation. func (v *WorkflowService_ListDomains_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ListDomainsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListDomains_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_ListDomains_Result // struct. func (v *WorkflowService_ListDomains_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_ListDomains_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListDomains_Result match the // provided WorkflowService_ListDomains_Result. // // This function performs a deep comparison. func (v *WorkflowService_ListDomains_Result) Equals(rhs *WorkflowService_ListDomains_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListDomains_Result. func (v *WorkflowService_ListDomains_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_ListDomains_Result) GetSuccess() (o *shared.ListDomainsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_ListDomains_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListDomains_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_ListDomains_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListDomains_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_ListDomains_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListDomains_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_ListDomains_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListDomains_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_ListDomains_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListDomains_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_ListDomains_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ListDomains" for this struct. func (v *WorkflowService_ListDomains_Result) MethodName() string { return "ListDomains" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_ListDomains_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_ListFailoverHistory_Args represents the arguments for the WorkflowService.ListFailoverHistory function. // // The arguments for ListFailoverHistory are sent and received over the wire as this struct. type WorkflowService_ListFailoverHistory_Args struct { ListRequest *shared.ListFailoverHistoryRequest `json:"listRequest,omitempty"` } // ToWire translates a WorkflowService_ListFailoverHistory_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListFailoverHistory_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ListRequest != nil { w, err = v.ListRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListFailoverHistoryRequest_Read(w wire.Value) (*shared.ListFailoverHistoryRequest, error) { var v shared.ListFailoverHistoryRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListFailoverHistory_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListFailoverHistory_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListFailoverHistory_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListFailoverHistory_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ListRequest, err = _ListFailoverHistoryRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_ListFailoverHistory_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListFailoverHistory_Args struct could not be encoded. func (v *WorkflowService_ListFailoverHistory_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ListRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ListRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ListFailoverHistoryRequest_Decode(sr stream.Reader) (*shared.ListFailoverHistoryRequest, error) { var v shared.ListFailoverHistoryRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListFailoverHistory_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListFailoverHistory_Args struct could not be generated from the wire // representation. func (v *WorkflowService_ListFailoverHistory_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ListRequest, err = _ListFailoverHistoryRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_ListFailoverHistory_Args // struct. func (v *WorkflowService_ListFailoverHistory_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ListRequest != nil { fields[i] = fmt.Sprintf("ListRequest: %v", v.ListRequest) i++ } return fmt.Sprintf("WorkflowService_ListFailoverHistory_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListFailoverHistory_Args match the // provided WorkflowService_ListFailoverHistory_Args. // // This function performs a deep comparison. func (v *WorkflowService_ListFailoverHistory_Args) Equals(rhs *WorkflowService_ListFailoverHistory_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ListRequest == nil && rhs.ListRequest == nil) || (v.ListRequest != nil && rhs.ListRequest != nil && v.ListRequest.Equals(rhs.ListRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListFailoverHistory_Args. func (v *WorkflowService_ListFailoverHistory_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ListRequest != nil { err = multierr.Append(err, enc.AddObject("listRequest", v.ListRequest)) } return err } // GetListRequest returns the value of ListRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_ListFailoverHistory_Args) GetListRequest() (o *shared.ListFailoverHistoryRequest) { if v != nil && v.ListRequest != nil { return v.ListRequest } return } // IsSetListRequest returns true if ListRequest is not nil. func (v *WorkflowService_ListFailoverHistory_Args) IsSetListRequest() bool { return v != nil && v.ListRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ListFailoverHistory" for this struct. func (v *WorkflowService_ListFailoverHistory_Args) MethodName() string { return "ListFailoverHistory" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_ListFailoverHistory_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_ListFailoverHistory_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.ListFailoverHistory // function. var WorkflowService_ListFailoverHistory_Helper = struct { // Args accepts the parameters of ListFailoverHistory in-order and returns // the arguments struct for the function. Args func( listRequest *shared.ListFailoverHistoryRequest, ) *WorkflowService_ListFailoverHistory_Args // IsException returns true if the given error can be thrown // by ListFailoverHistory. // // An error can be thrown by ListFailoverHistory only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ListFailoverHistory // given its return value and error. // // This allows mapping values and errors returned by // ListFailoverHistory into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ListFailoverHistory // // value, err := ListFailoverHistory(args) // result, err := WorkflowService_ListFailoverHistory_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ListFailoverHistory: %v", err) // } // serialize(result) WrapResponse func(*shared.ListFailoverHistoryResponse, error) (*WorkflowService_ListFailoverHistory_Result, error) // UnwrapResponse takes the result struct for ListFailoverHistory // and returns the value or error returned by it. // // The error is non-nil only if ListFailoverHistory threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_ListFailoverHistory_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_ListFailoverHistory_Result) (*shared.ListFailoverHistoryResponse, error) }{} func init() { WorkflowService_ListFailoverHistory_Helper.Args = func( listRequest *shared.ListFailoverHistoryRequest, ) *WorkflowService_ListFailoverHistory_Args { return &WorkflowService_ListFailoverHistory_Args{ ListRequest: listRequest, } } WorkflowService_ListFailoverHistory_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_ListFailoverHistory_Helper.WrapResponse = func(success *shared.ListFailoverHistoryResponse, err error) (*WorkflowService_ListFailoverHistory_Result, error) { if err == nil { return &WorkflowService_ListFailoverHistory_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListFailoverHistory_Result.BadRequestError") } return &WorkflowService_ListFailoverHistory_Result{BadRequestError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListFailoverHistory_Result.ServiceBusyError") } return &WorkflowService_ListFailoverHistory_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListFailoverHistory_Result.ClientVersionNotSupportedError") } return &WorkflowService_ListFailoverHistory_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListFailoverHistory_Result.AccessDeniedError") } return &WorkflowService_ListFailoverHistory_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_ListFailoverHistory_Helper.UnwrapResponse = func(result *WorkflowService_ListFailoverHistory_Result) (success *shared.ListFailoverHistoryResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_ListFailoverHistory_Result represents the result of a WorkflowService.ListFailoverHistory function call. // // The result of a ListFailoverHistory execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_ListFailoverHistory_Result struct { // Value returned by ListFailoverHistory after a successful execution. Success *shared.ListFailoverHistoryResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_ListFailoverHistory_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListFailoverHistory_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_ListFailoverHistory_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListFailoverHistoryResponse_Read(w wire.Value) (*shared.ListFailoverHistoryResponse, error) { var v shared.ListFailoverHistoryResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListFailoverHistory_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListFailoverHistory_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListFailoverHistory_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListFailoverHistory_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ListFailoverHistoryResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListFailoverHistory_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_ListFailoverHistory_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListFailoverHistory_Result struct could not be encoded. func (v *WorkflowService_ListFailoverHistory_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListFailoverHistory_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ListFailoverHistoryResponse_Decode(sr stream.Reader) (*shared.ListFailoverHistoryResponse, error) { var v shared.ListFailoverHistoryResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListFailoverHistory_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListFailoverHistory_Result struct could not be generated from the wire // representation. func (v *WorkflowService_ListFailoverHistory_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ListFailoverHistoryResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListFailoverHistory_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_ListFailoverHistory_Result // struct. func (v *WorkflowService_ListFailoverHistory_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_ListFailoverHistory_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListFailoverHistory_Result match the // provided WorkflowService_ListFailoverHistory_Result. // // This function performs a deep comparison. func (v *WorkflowService_ListFailoverHistory_Result) Equals(rhs *WorkflowService_ListFailoverHistory_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListFailoverHistory_Result. func (v *WorkflowService_ListFailoverHistory_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_ListFailoverHistory_Result) GetSuccess() (o *shared.ListFailoverHistoryResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_ListFailoverHistory_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListFailoverHistory_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_ListFailoverHistory_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListFailoverHistory_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_ListFailoverHistory_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListFailoverHistory_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_ListFailoverHistory_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListFailoverHistory_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_ListFailoverHistory_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ListFailoverHistory" for this struct. func (v *WorkflowService_ListFailoverHistory_Result) MethodName() string { return "ListFailoverHistory" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_ListFailoverHistory_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_ListOpenWorkflowExecutions_Args represents the arguments for the WorkflowService.ListOpenWorkflowExecutions function. // // The arguments for ListOpenWorkflowExecutions are sent and received over the wire as this struct. type WorkflowService_ListOpenWorkflowExecutions_Args struct { ListRequest *shared.ListOpenWorkflowExecutionsRequest `json:"listRequest,omitempty"` } // ToWire translates a WorkflowService_ListOpenWorkflowExecutions_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListOpenWorkflowExecutions_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ListRequest != nil { w, err = v.ListRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListOpenWorkflowExecutionsRequest_Read(w wire.Value) (*shared.ListOpenWorkflowExecutionsRequest, error) { var v shared.ListOpenWorkflowExecutionsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListOpenWorkflowExecutions_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListOpenWorkflowExecutions_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListOpenWorkflowExecutions_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListOpenWorkflowExecutions_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ListRequest, err = _ListOpenWorkflowExecutionsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_ListOpenWorkflowExecutions_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListOpenWorkflowExecutions_Args struct could not be encoded. func (v *WorkflowService_ListOpenWorkflowExecutions_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ListRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ListRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ListOpenWorkflowExecutionsRequest_Decode(sr stream.Reader) (*shared.ListOpenWorkflowExecutionsRequest, error) { var v shared.ListOpenWorkflowExecutionsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListOpenWorkflowExecutions_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListOpenWorkflowExecutions_Args struct could not be generated from the wire // representation. func (v *WorkflowService_ListOpenWorkflowExecutions_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ListRequest, err = _ListOpenWorkflowExecutionsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_ListOpenWorkflowExecutions_Args // struct. func (v *WorkflowService_ListOpenWorkflowExecutions_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ListRequest != nil { fields[i] = fmt.Sprintf("ListRequest: %v", v.ListRequest) i++ } return fmt.Sprintf("WorkflowService_ListOpenWorkflowExecutions_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListOpenWorkflowExecutions_Args match the // provided WorkflowService_ListOpenWorkflowExecutions_Args. // // This function performs a deep comparison. func (v *WorkflowService_ListOpenWorkflowExecutions_Args) Equals(rhs *WorkflowService_ListOpenWorkflowExecutions_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ListRequest == nil && rhs.ListRequest == nil) || (v.ListRequest != nil && rhs.ListRequest != nil && v.ListRequest.Equals(rhs.ListRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListOpenWorkflowExecutions_Args. func (v *WorkflowService_ListOpenWorkflowExecutions_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ListRequest != nil { err = multierr.Append(err, enc.AddObject("listRequest", v.ListRequest)) } return err } // GetListRequest returns the value of ListRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_ListOpenWorkflowExecutions_Args) GetListRequest() (o *shared.ListOpenWorkflowExecutionsRequest) { if v != nil && v.ListRequest != nil { return v.ListRequest } return } // IsSetListRequest returns true if ListRequest is not nil. func (v *WorkflowService_ListOpenWorkflowExecutions_Args) IsSetListRequest() bool { return v != nil && v.ListRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ListOpenWorkflowExecutions" for this struct. func (v *WorkflowService_ListOpenWorkflowExecutions_Args) MethodName() string { return "ListOpenWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_ListOpenWorkflowExecutions_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_ListOpenWorkflowExecutions_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.ListOpenWorkflowExecutions // function. var WorkflowService_ListOpenWorkflowExecutions_Helper = struct { // Args accepts the parameters of ListOpenWorkflowExecutions in-order and returns // the arguments struct for the function. Args func( listRequest *shared.ListOpenWorkflowExecutionsRequest, ) *WorkflowService_ListOpenWorkflowExecutions_Args // IsException returns true if the given error can be thrown // by ListOpenWorkflowExecutions. // // An error can be thrown by ListOpenWorkflowExecutions only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ListOpenWorkflowExecutions // given its return value and error. // // This allows mapping values and errors returned by // ListOpenWorkflowExecutions into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ListOpenWorkflowExecutions // // value, err := ListOpenWorkflowExecutions(args) // result, err := WorkflowService_ListOpenWorkflowExecutions_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ListOpenWorkflowExecutions: %v", err) // } // serialize(result) WrapResponse func(*shared.ListOpenWorkflowExecutionsResponse, error) (*WorkflowService_ListOpenWorkflowExecutions_Result, error) // UnwrapResponse takes the result struct for ListOpenWorkflowExecutions // and returns the value or error returned by it. // // The error is non-nil only if ListOpenWorkflowExecutions threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_ListOpenWorkflowExecutions_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_ListOpenWorkflowExecutions_Result) (*shared.ListOpenWorkflowExecutionsResponse, error) }{} func init() { WorkflowService_ListOpenWorkflowExecutions_Helper.Args = func( listRequest *shared.ListOpenWorkflowExecutionsRequest, ) *WorkflowService_ListOpenWorkflowExecutions_Args { return &WorkflowService_ListOpenWorkflowExecutions_Args{ ListRequest: listRequest, } } WorkflowService_ListOpenWorkflowExecutions_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.LimitExceededError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_ListOpenWorkflowExecutions_Helper.WrapResponse = func(success *shared.ListOpenWorkflowExecutionsResponse, err error) (*WorkflowService_ListOpenWorkflowExecutions_Result, error) { if err == nil { return &WorkflowService_ListOpenWorkflowExecutions_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListOpenWorkflowExecutions_Result.BadRequestError") } return &WorkflowService_ListOpenWorkflowExecutions_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListOpenWorkflowExecutions_Result.EntityNotExistError") } return &WorkflowService_ListOpenWorkflowExecutions_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListOpenWorkflowExecutions_Result.ServiceBusyError") } return &WorkflowService_ListOpenWorkflowExecutions_Result{ServiceBusyError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListOpenWorkflowExecutions_Result.LimitExceededError") } return &WorkflowService_ListOpenWorkflowExecutions_Result{LimitExceededError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListOpenWorkflowExecutions_Result.ClientVersionNotSupportedError") } return &WorkflowService_ListOpenWorkflowExecutions_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListOpenWorkflowExecutions_Result.AccessDeniedError") } return &WorkflowService_ListOpenWorkflowExecutions_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_ListOpenWorkflowExecutions_Helper.UnwrapResponse = func(result *WorkflowService_ListOpenWorkflowExecutions_Result) (success *shared.ListOpenWorkflowExecutionsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_ListOpenWorkflowExecutions_Result represents the result of a WorkflowService.ListOpenWorkflowExecutions function call. // // The result of a ListOpenWorkflowExecutions execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_ListOpenWorkflowExecutions_Result struct { // Value returned by ListOpenWorkflowExecutions after a successful execution. Success *shared.ListOpenWorkflowExecutionsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_ListOpenWorkflowExecutions_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListOpenWorkflowExecutions_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_ListOpenWorkflowExecutions_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListOpenWorkflowExecutionsResponse_Read(w wire.Value) (*shared.ListOpenWorkflowExecutionsResponse, error) { var v shared.ListOpenWorkflowExecutionsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListOpenWorkflowExecutions_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListOpenWorkflowExecutions_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListOpenWorkflowExecutions_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListOpenWorkflowExecutions_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ListOpenWorkflowExecutionsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListOpenWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_ListOpenWorkflowExecutions_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListOpenWorkflowExecutions_Result struct could not be encoded. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListOpenWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ListOpenWorkflowExecutionsResponse_Decode(sr stream.Reader) (*shared.ListOpenWorkflowExecutionsResponse, error) { var v shared.ListOpenWorkflowExecutionsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListOpenWorkflowExecutions_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListOpenWorkflowExecutions_Result struct could not be generated from the wire // representation. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ListOpenWorkflowExecutionsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListOpenWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_ListOpenWorkflowExecutions_Result // struct. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_ListOpenWorkflowExecutions_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListOpenWorkflowExecutions_Result match the // provided WorkflowService_ListOpenWorkflowExecutions_Result. // // This function performs a deep comparison. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) Equals(rhs *WorkflowService_ListOpenWorkflowExecutions_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListOpenWorkflowExecutions_Result. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) GetSuccess() (o *shared.ListOpenWorkflowExecutionsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ListOpenWorkflowExecutions" for this struct. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) MethodName() string { return "ListOpenWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_ListOpenWorkflowExecutions_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_ListTaskListPartitions_Args represents the arguments for the WorkflowService.ListTaskListPartitions function. // // The arguments for ListTaskListPartitions are sent and received over the wire as this struct. type WorkflowService_ListTaskListPartitions_Args struct { Request *shared.ListTaskListPartitionsRequest `json:"request,omitempty"` } // ToWire translates a WorkflowService_ListTaskListPartitions_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListTaskListPartitions_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListTaskListPartitionsRequest_Read(w wire.Value) (*shared.ListTaskListPartitionsRequest, error) { var v shared.ListTaskListPartitionsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListTaskListPartitions_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListTaskListPartitions_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListTaskListPartitions_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListTaskListPartitions_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _ListTaskListPartitionsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_ListTaskListPartitions_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListTaskListPartitions_Args struct could not be encoded. func (v *WorkflowService_ListTaskListPartitions_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ListTaskListPartitionsRequest_Decode(sr stream.Reader) (*shared.ListTaskListPartitionsRequest, error) { var v shared.ListTaskListPartitionsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListTaskListPartitions_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListTaskListPartitions_Args struct could not be generated from the wire // representation. func (v *WorkflowService_ListTaskListPartitions_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _ListTaskListPartitionsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_ListTaskListPartitions_Args // struct. func (v *WorkflowService_ListTaskListPartitions_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("WorkflowService_ListTaskListPartitions_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListTaskListPartitions_Args match the // provided WorkflowService_ListTaskListPartitions_Args. // // This function performs a deep comparison. func (v *WorkflowService_ListTaskListPartitions_Args) Equals(rhs *WorkflowService_ListTaskListPartitions_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListTaskListPartitions_Args. func (v *WorkflowService_ListTaskListPartitions_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *WorkflowService_ListTaskListPartitions_Args) GetRequest() (o *shared.ListTaskListPartitionsRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *WorkflowService_ListTaskListPartitions_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ListTaskListPartitions" for this struct. func (v *WorkflowService_ListTaskListPartitions_Args) MethodName() string { return "ListTaskListPartitions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_ListTaskListPartitions_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_ListTaskListPartitions_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.ListTaskListPartitions // function. var WorkflowService_ListTaskListPartitions_Helper = struct { // Args accepts the parameters of ListTaskListPartitions in-order and returns // the arguments struct for the function. Args func( request *shared.ListTaskListPartitionsRequest, ) *WorkflowService_ListTaskListPartitions_Args // IsException returns true if the given error can be thrown // by ListTaskListPartitions. // // An error can be thrown by ListTaskListPartitions only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ListTaskListPartitions // given its return value and error. // // This allows mapping values and errors returned by // ListTaskListPartitions into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ListTaskListPartitions // // value, err := ListTaskListPartitions(args) // result, err := WorkflowService_ListTaskListPartitions_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ListTaskListPartitions: %v", err) // } // serialize(result) WrapResponse func(*shared.ListTaskListPartitionsResponse, error) (*WorkflowService_ListTaskListPartitions_Result, error) // UnwrapResponse takes the result struct for ListTaskListPartitions // and returns the value or error returned by it. // // The error is non-nil only if ListTaskListPartitions threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_ListTaskListPartitions_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_ListTaskListPartitions_Result) (*shared.ListTaskListPartitionsResponse, error) }{} func init() { WorkflowService_ListTaskListPartitions_Helper.Args = func( request *shared.ListTaskListPartitionsRequest, ) *WorkflowService_ListTaskListPartitions_Args { return &WorkflowService_ListTaskListPartitions_Args{ Request: request, } } WorkflowService_ListTaskListPartitions_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_ListTaskListPartitions_Helper.WrapResponse = func(success *shared.ListTaskListPartitionsResponse, err error) (*WorkflowService_ListTaskListPartitions_Result, error) { if err == nil { return &WorkflowService_ListTaskListPartitions_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListTaskListPartitions_Result.BadRequestError") } return &WorkflowService_ListTaskListPartitions_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListTaskListPartitions_Result.EntityNotExistError") } return &WorkflowService_ListTaskListPartitions_Result{EntityNotExistError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListTaskListPartitions_Result.LimitExceededError") } return &WorkflowService_ListTaskListPartitions_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListTaskListPartitions_Result.ServiceBusyError") } return &WorkflowService_ListTaskListPartitions_Result{ServiceBusyError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListTaskListPartitions_Result.AccessDeniedError") } return &WorkflowService_ListTaskListPartitions_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_ListTaskListPartitions_Helper.UnwrapResponse = func(result *WorkflowService_ListTaskListPartitions_Result) (success *shared.ListTaskListPartitionsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_ListTaskListPartitions_Result represents the result of a WorkflowService.ListTaskListPartitions function call. // // The result of a ListTaskListPartitions execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_ListTaskListPartitions_Result struct { // Value returned by ListTaskListPartitions after a successful execution. Success *shared.ListTaskListPartitionsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_ListTaskListPartitions_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListTaskListPartitions_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_ListTaskListPartitions_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListTaskListPartitionsResponse_Read(w wire.Value) (*shared.ListTaskListPartitionsResponse, error) { var v shared.ListTaskListPartitionsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListTaskListPartitions_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListTaskListPartitions_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListTaskListPartitions_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListTaskListPartitions_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ListTaskListPartitionsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListTaskListPartitions_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_ListTaskListPartitions_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListTaskListPartitions_Result struct could not be encoded. func (v *WorkflowService_ListTaskListPartitions_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListTaskListPartitions_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ListTaskListPartitionsResponse_Decode(sr stream.Reader) (*shared.ListTaskListPartitionsResponse, error) { var v shared.ListTaskListPartitionsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListTaskListPartitions_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListTaskListPartitions_Result struct could not be generated from the wire // representation. func (v *WorkflowService_ListTaskListPartitions_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ListTaskListPartitionsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListTaskListPartitions_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_ListTaskListPartitions_Result // struct. func (v *WorkflowService_ListTaskListPartitions_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_ListTaskListPartitions_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListTaskListPartitions_Result match the // provided WorkflowService_ListTaskListPartitions_Result. // // This function performs a deep comparison. func (v *WorkflowService_ListTaskListPartitions_Result) Equals(rhs *WorkflowService_ListTaskListPartitions_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListTaskListPartitions_Result. func (v *WorkflowService_ListTaskListPartitions_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_ListTaskListPartitions_Result) GetSuccess() (o *shared.ListTaskListPartitionsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_ListTaskListPartitions_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListTaskListPartitions_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_ListTaskListPartitions_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListTaskListPartitions_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_ListTaskListPartitions_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListTaskListPartitions_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_ListTaskListPartitions_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListTaskListPartitions_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_ListTaskListPartitions_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListTaskListPartitions_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_ListTaskListPartitions_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ListTaskListPartitions" for this struct. func (v *WorkflowService_ListTaskListPartitions_Result) MethodName() string { return "ListTaskListPartitions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_ListTaskListPartitions_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_ListWorkflowExecutions_Args represents the arguments for the WorkflowService.ListWorkflowExecutions function. // // The arguments for ListWorkflowExecutions are sent and received over the wire as this struct. type WorkflowService_ListWorkflowExecutions_Args struct { ListRequest *shared.ListWorkflowExecutionsRequest `json:"listRequest,omitempty"` } // ToWire translates a WorkflowService_ListWorkflowExecutions_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListWorkflowExecutions_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ListRequest != nil { w, err = v.ListRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListWorkflowExecutionsRequest_Read(w wire.Value) (*shared.ListWorkflowExecutionsRequest, error) { var v shared.ListWorkflowExecutionsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListWorkflowExecutions_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListWorkflowExecutions_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListWorkflowExecutions_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListWorkflowExecutions_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ListRequest, err = _ListWorkflowExecutionsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_ListWorkflowExecutions_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListWorkflowExecutions_Args struct could not be encoded. func (v *WorkflowService_ListWorkflowExecutions_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ListRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ListRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ListWorkflowExecutionsRequest_Decode(sr stream.Reader) (*shared.ListWorkflowExecutionsRequest, error) { var v shared.ListWorkflowExecutionsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListWorkflowExecutions_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListWorkflowExecutions_Args struct could not be generated from the wire // representation. func (v *WorkflowService_ListWorkflowExecutions_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ListRequest, err = _ListWorkflowExecutionsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_ListWorkflowExecutions_Args // struct. func (v *WorkflowService_ListWorkflowExecutions_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ListRequest != nil { fields[i] = fmt.Sprintf("ListRequest: %v", v.ListRequest) i++ } return fmt.Sprintf("WorkflowService_ListWorkflowExecutions_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListWorkflowExecutions_Args match the // provided WorkflowService_ListWorkflowExecutions_Args. // // This function performs a deep comparison. func (v *WorkflowService_ListWorkflowExecutions_Args) Equals(rhs *WorkflowService_ListWorkflowExecutions_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ListRequest == nil && rhs.ListRequest == nil) || (v.ListRequest != nil && rhs.ListRequest != nil && v.ListRequest.Equals(rhs.ListRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListWorkflowExecutions_Args. func (v *WorkflowService_ListWorkflowExecutions_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ListRequest != nil { err = multierr.Append(err, enc.AddObject("listRequest", v.ListRequest)) } return err } // GetListRequest returns the value of ListRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_ListWorkflowExecutions_Args) GetListRequest() (o *shared.ListWorkflowExecutionsRequest) { if v != nil && v.ListRequest != nil { return v.ListRequest } return } // IsSetListRequest returns true if ListRequest is not nil. func (v *WorkflowService_ListWorkflowExecutions_Args) IsSetListRequest() bool { return v != nil && v.ListRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ListWorkflowExecutions" for this struct. func (v *WorkflowService_ListWorkflowExecutions_Args) MethodName() string { return "ListWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_ListWorkflowExecutions_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_ListWorkflowExecutions_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.ListWorkflowExecutions // function. var WorkflowService_ListWorkflowExecutions_Helper = struct { // Args accepts the parameters of ListWorkflowExecutions in-order and returns // the arguments struct for the function. Args func( listRequest *shared.ListWorkflowExecutionsRequest, ) *WorkflowService_ListWorkflowExecutions_Args // IsException returns true if the given error can be thrown // by ListWorkflowExecutions. // // An error can be thrown by ListWorkflowExecutions only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ListWorkflowExecutions // given its return value and error. // // This allows mapping values and errors returned by // ListWorkflowExecutions into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ListWorkflowExecutions // // value, err := ListWorkflowExecutions(args) // result, err := WorkflowService_ListWorkflowExecutions_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ListWorkflowExecutions: %v", err) // } // serialize(result) WrapResponse func(*shared.ListWorkflowExecutionsResponse, error) (*WorkflowService_ListWorkflowExecutions_Result, error) // UnwrapResponse takes the result struct for ListWorkflowExecutions // and returns the value or error returned by it. // // The error is non-nil only if ListWorkflowExecutions threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_ListWorkflowExecutions_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_ListWorkflowExecutions_Result) (*shared.ListWorkflowExecutionsResponse, error) }{} func init() { WorkflowService_ListWorkflowExecutions_Helper.Args = func( listRequest *shared.ListWorkflowExecutionsRequest, ) *WorkflowService_ListWorkflowExecutions_Args { return &WorkflowService_ListWorkflowExecutions_Args{ ListRequest: listRequest, } } WorkflowService_ListWorkflowExecutions_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_ListWorkflowExecutions_Helper.WrapResponse = func(success *shared.ListWorkflowExecutionsResponse, err error) (*WorkflowService_ListWorkflowExecutions_Result, error) { if err == nil { return &WorkflowService_ListWorkflowExecutions_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListWorkflowExecutions_Result.BadRequestError") } return &WorkflowService_ListWorkflowExecutions_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListWorkflowExecutions_Result.EntityNotExistError") } return &WorkflowService_ListWorkflowExecutions_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListWorkflowExecutions_Result.ServiceBusyError") } return &WorkflowService_ListWorkflowExecutions_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListWorkflowExecutions_Result.ClientVersionNotSupportedError") } return &WorkflowService_ListWorkflowExecutions_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ListWorkflowExecutions_Result.AccessDeniedError") } return &WorkflowService_ListWorkflowExecutions_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_ListWorkflowExecutions_Helper.UnwrapResponse = func(result *WorkflowService_ListWorkflowExecutions_Result) (success *shared.ListWorkflowExecutionsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_ListWorkflowExecutions_Result represents the result of a WorkflowService.ListWorkflowExecutions function call. // // The result of a ListWorkflowExecutions execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_ListWorkflowExecutions_Result struct { // Value returned by ListWorkflowExecutions after a successful execution. Success *shared.ListWorkflowExecutionsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_ListWorkflowExecutions_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ListWorkflowExecutions_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_ListWorkflowExecutions_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListWorkflowExecutionsResponse_Read(w wire.Value) (*shared.ListWorkflowExecutionsResponse, error) { var v shared.ListWorkflowExecutionsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ListWorkflowExecutions_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ListWorkflowExecutions_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ListWorkflowExecutions_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ListWorkflowExecutions_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ListWorkflowExecutionsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_ListWorkflowExecutions_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ListWorkflowExecutions_Result struct could not be encoded. func (v *WorkflowService_ListWorkflowExecutions_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ListWorkflowExecutionsResponse_Decode(sr stream.Reader) (*shared.ListWorkflowExecutionsResponse, error) { var v shared.ListWorkflowExecutionsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ListWorkflowExecutions_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ListWorkflowExecutions_Result struct could not be generated from the wire // representation. func (v *WorkflowService_ListWorkflowExecutions_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ListWorkflowExecutionsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ListWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_ListWorkflowExecutions_Result // struct. func (v *WorkflowService_ListWorkflowExecutions_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_ListWorkflowExecutions_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ListWorkflowExecutions_Result match the // provided WorkflowService_ListWorkflowExecutions_Result. // // This function performs a deep comparison. func (v *WorkflowService_ListWorkflowExecutions_Result) Equals(rhs *WorkflowService_ListWorkflowExecutions_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ListWorkflowExecutions_Result. func (v *WorkflowService_ListWorkflowExecutions_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_ListWorkflowExecutions_Result) GetSuccess() (o *shared.ListWorkflowExecutionsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_ListWorkflowExecutions_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListWorkflowExecutions_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_ListWorkflowExecutions_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListWorkflowExecutions_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_ListWorkflowExecutions_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListWorkflowExecutions_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_ListWorkflowExecutions_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListWorkflowExecutions_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_ListWorkflowExecutions_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ListWorkflowExecutions_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_ListWorkflowExecutions_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ListWorkflowExecutions" for this struct. func (v *WorkflowService_ListWorkflowExecutions_Result) MethodName() string { return "ListWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_ListWorkflowExecutions_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_PollForActivityTask_Args represents the arguments for the WorkflowService.PollForActivityTask function. // // The arguments for PollForActivityTask are sent and received over the wire as this struct. type WorkflowService_PollForActivityTask_Args struct { PollRequest *shared.PollForActivityTaskRequest `json:"pollRequest,omitempty"` } // ToWire translates a WorkflowService_PollForActivityTask_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_PollForActivityTask_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.PollRequest != nil { w, err = v.PollRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForActivityTaskRequest_Read(w wire.Value) (*shared.PollForActivityTaskRequest, error) { var v shared.PollForActivityTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_PollForActivityTask_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_PollForActivityTask_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_PollForActivityTask_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_PollForActivityTask_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.PollRequest, err = _PollForActivityTaskRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_PollForActivityTask_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_PollForActivityTask_Args struct could not be encoded. func (v *WorkflowService_PollForActivityTask_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.PollRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.PollRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollForActivityTaskRequest_Decode(sr stream.Reader) (*shared.PollForActivityTaskRequest, error) { var v shared.PollForActivityTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_PollForActivityTask_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_PollForActivityTask_Args struct could not be generated from the wire // representation. func (v *WorkflowService_PollForActivityTask_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.PollRequest, err = _PollForActivityTaskRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_PollForActivityTask_Args // struct. func (v *WorkflowService_PollForActivityTask_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.PollRequest != nil { fields[i] = fmt.Sprintf("PollRequest: %v", v.PollRequest) i++ } return fmt.Sprintf("WorkflowService_PollForActivityTask_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_PollForActivityTask_Args match the // provided WorkflowService_PollForActivityTask_Args. // // This function performs a deep comparison. func (v *WorkflowService_PollForActivityTask_Args) Equals(rhs *WorkflowService_PollForActivityTask_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.PollRequest == nil && rhs.PollRequest == nil) || (v.PollRequest != nil && rhs.PollRequest != nil && v.PollRequest.Equals(rhs.PollRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_PollForActivityTask_Args. func (v *WorkflowService_PollForActivityTask_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.PollRequest != nil { err = multierr.Append(err, enc.AddObject("pollRequest", v.PollRequest)) } return err } // GetPollRequest returns the value of PollRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForActivityTask_Args) GetPollRequest() (o *shared.PollForActivityTaskRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // IsSetPollRequest returns true if PollRequest is not nil. func (v *WorkflowService_PollForActivityTask_Args) IsSetPollRequest() bool { return v != nil && v.PollRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "PollForActivityTask" for this struct. func (v *WorkflowService_PollForActivityTask_Args) MethodName() string { return "PollForActivityTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_PollForActivityTask_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_PollForActivityTask_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.PollForActivityTask // function. var WorkflowService_PollForActivityTask_Helper = struct { // Args accepts the parameters of PollForActivityTask in-order and returns // the arguments struct for the function. Args func( pollRequest *shared.PollForActivityTaskRequest, ) *WorkflowService_PollForActivityTask_Args // IsException returns true if the given error can be thrown // by PollForActivityTask. // // An error can be thrown by PollForActivityTask only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for PollForActivityTask // given its return value and error. // // This allows mapping values and errors returned by // PollForActivityTask into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by PollForActivityTask // // value, err := PollForActivityTask(args) // result, err := WorkflowService_PollForActivityTask_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from PollForActivityTask: %v", err) // } // serialize(result) WrapResponse func(*shared.PollForActivityTaskResponse, error) (*WorkflowService_PollForActivityTask_Result, error) // UnwrapResponse takes the result struct for PollForActivityTask // and returns the value or error returned by it. // // The error is non-nil only if PollForActivityTask threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_PollForActivityTask_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_PollForActivityTask_Result) (*shared.PollForActivityTaskResponse, error) }{} func init() { WorkflowService_PollForActivityTask_Helper.Args = func( pollRequest *shared.PollForActivityTaskRequest, ) *WorkflowService_PollForActivityTask_Args { return &WorkflowService_PollForActivityTask_Args{ PollRequest: pollRequest, } } WorkflowService_PollForActivityTask_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.ServiceBusyError: return true case *shared.LimitExceededError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_PollForActivityTask_Helper.WrapResponse = func(success *shared.PollForActivityTaskResponse, err error) (*WorkflowService_PollForActivityTask_Result, error) { if err == nil { return &WorkflowService_PollForActivityTask_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForActivityTask_Result.BadRequestError") } return &WorkflowService_PollForActivityTask_Result{BadRequestError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForActivityTask_Result.ServiceBusyError") } return &WorkflowService_PollForActivityTask_Result{ServiceBusyError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForActivityTask_Result.LimitExceededError") } return &WorkflowService_PollForActivityTask_Result{LimitExceededError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForActivityTask_Result.EntityNotExistError") } return &WorkflowService_PollForActivityTask_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForActivityTask_Result.DomainNotActiveError") } return &WorkflowService_PollForActivityTask_Result{DomainNotActiveError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForActivityTask_Result.ClientVersionNotSupportedError") } return &WorkflowService_PollForActivityTask_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForActivityTask_Result.AccessDeniedError") } return &WorkflowService_PollForActivityTask_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_PollForActivityTask_Helper.UnwrapResponse = func(result *WorkflowService_PollForActivityTask_Result) (success *shared.PollForActivityTaskResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_PollForActivityTask_Result represents the result of a WorkflowService.PollForActivityTask function call. // // The result of a PollForActivityTask execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_PollForActivityTask_Result struct { // Value returned by PollForActivityTask after a successful execution. Success *shared.PollForActivityTaskResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_PollForActivityTask_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_PollForActivityTask_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_PollForActivityTask_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForActivityTaskResponse_Read(w wire.Value) (*shared.PollForActivityTaskResponse, error) { var v shared.PollForActivityTaskResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_PollForActivityTask_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_PollForActivityTask_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_PollForActivityTask_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_PollForActivityTask_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _PollForActivityTaskResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_PollForActivityTask_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_PollForActivityTask_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_PollForActivityTask_Result struct could not be encoded. func (v *WorkflowService_PollForActivityTask_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_PollForActivityTask_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _PollForActivityTaskResponse_Decode(sr stream.Reader) (*shared.PollForActivityTaskResponse, error) { var v shared.PollForActivityTaskResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_PollForActivityTask_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_PollForActivityTask_Result struct could not be generated from the wire // representation. func (v *WorkflowService_PollForActivityTask_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _PollForActivityTaskResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_PollForActivityTask_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_PollForActivityTask_Result // struct. func (v *WorkflowService_PollForActivityTask_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_PollForActivityTask_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_PollForActivityTask_Result match the // provided WorkflowService_PollForActivityTask_Result. // // This function performs a deep comparison. func (v *WorkflowService_PollForActivityTask_Result) Equals(rhs *WorkflowService_PollForActivityTask_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_PollForActivityTask_Result. func (v *WorkflowService_PollForActivityTask_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForActivityTask_Result) GetSuccess() (o *shared.PollForActivityTaskResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_PollForActivityTask_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForActivityTask_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_PollForActivityTask_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForActivityTask_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_PollForActivityTask_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForActivityTask_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_PollForActivityTask_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForActivityTask_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_PollForActivityTask_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForActivityTask_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_PollForActivityTask_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForActivityTask_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_PollForActivityTask_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForActivityTask_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_PollForActivityTask_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "PollForActivityTask" for this struct. func (v *WorkflowService_PollForActivityTask_Result) MethodName() string { return "PollForActivityTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_PollForActivityTask_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_PollForDecisionTask_Args represents the arguments for the WorkflowService.PollForDecisionTask function. // // The arguments for PollForDecisionTask are sent and received over the wire as this struct. type WorkflowService_PollForDecisionTask_Args struct { PollRequest *shared.PollForDecisionTaskRequest `json:"pollRequest,omitempty"` } // ToWire translates a WorkflowService_PollForDecisionTask_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_PollForDecisionTask_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.PollRequest != nil { w, err = v.PollRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForDecisionTaskRequest_Read(w wire.Value) (*shared.PollForDecisionTaskRequest, error) { var v shared.PollForDecisionTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_PollForDecisionTask_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_PollForDecisionTask_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_PollForDecisionTask_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_PollForDecisionTask_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.PollRequest, err = _PollForDecisionTaskRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_PollForDecisionTask_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_PollForDecisionTask_Args struct could not be encoded. func (v *WorkflowService_PollForDecisionTask_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.PollRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.PollRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollForDecisionTaskRequest_Decode(sr stream.Reader) (*shared.PollForDecisionTaskRequest, error) { var v shared.PollForDecisionTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_PollForDecisionTask_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_PollForDecisionTask_Args struct could not be generated from the wire // representation. func (v *WorkflowService_PollForDecisionTask_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.PollRequest, err = _PollForDecisionTaskRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_PollForDecisionTask_Args // struct. func (v *WorkflowService_PollForDecisionTask_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.PollRequest != nil { fields[i] = fmt.Sprintf("PollRequest: %v", v.PollRequest) i++ } return fmt.Sprintf("WorkflowService_PollForDecisionTask_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_PollForDecisionTask_Args match the // provided WorkflowService_PollForDecisionTask_Args. // // This function performs a deep comparison. func (v *WorkflowService_PollForDecisionTask_Args) Equals(rhs *WorkflowService_PollForDecisionTask_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.PollRequest == nil && rhs.PollRequest == nil) || (v.PollRequest != nil && rhs.PollRequest != nil && v.PollRequest.Equals(rhs.PollRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_PollForDecisionTask_Args. func (v *WorkflowService_PollForDecisionTask_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.PollRequest != nil { err = multierr.Append(err, enc.AddObject("pollRequest", v.PollRequest)) } return err } // GetPollRequest returns the value of PollRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForDecisionTask_Args) GetPollRequest() (o *shared.PollForDecisionTaskRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // IsSetPollRequest returns true if PollRequest is not nil. func (v *WorkflowService_PollForDecisionTask_Args) IsSetPollRequest() bool { return v != nil && v.PollRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "PollForDecisionTask" for this struct. func (v *WorkflowService_PollForDecisionTask_Args) MethodName() string { return "PollForDecisionTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_PollForDecisionTask_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_PollForDecisionTask_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.PollForDecisionTask // function. var WorkflowService_PollForDecisionTask_Helper = struct { // Args accepts the parameters of PollForDecisionTask in-order and returns // the arguments struct for the function. Args func( pollRequest *shared.PollForDecisionTaskRequest, ) *WorkflowService_PollForDecisionTask_Args // IsException returns true if the given error can be thrown // by PollForDecisionTask. // // An error can be thrown by PollForDecisionTask only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for PollForDecisionTask // given its return value and error. // // This allows mapping values and errors returned by // PollForDecisionTask into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by PollForDecisionTask // // value, err := PollForDecisionTask(args) // result, err := WorkflowService_PollForDecisionTask_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from PollForDecisionTask: %v", err) // } // serialize(result) WrapResponse func(*shared.PollForDecisionTaskResponse, error) (*WorkflowService_PollForDecisionTask_Result, error) // UnwrapResponse takes the result struct for PollForDecisionTask // and returns the value or error returned by it. // // The error is non-nil only if PollForDecisionTask threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_PollForDecisionTask_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_PollForDecisionTask_Result) (*shared.PollForDecisionTaskResponse, error) }{} func init() { WorkflowService_PollForDecisionTask_Helper.Args = func( pollRequest *shared.PollForDecisionTaskRequest, ) *WorkflowService_PollForDecisionTask_Args { return &WorkflowService_PollForDecisionTask_Args{ PollRequest: pollRequest, } } WorkflowService_PollForDecisionTask_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.ServiceBusyError: return true case *shared.LimitExceededError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_PollForDecisionTask_Helper.WrapResponse = func(success *shared.PollForDecisionTaskResponse, err error) (*WorkflowService_PollForDecisionTask_Result, error) { if err == nil { return &WorkflowService_PollForDecisionTask_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForDecisionTask_Result.BadRequestError") } return &WorkflowService_PollForDecisionTask_Result{BadRequestError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForDecisionTask_Result.ServiceBusyError") } return &WorkflowService_PollForDecisionTask_Result{ServiceBusyError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForDecisionTask_Result.LimitExceededError") } return &WorkflowService_PollForDecisionTask_Result{LimitExceededError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForDecisionTask_Result.EntityNotExistError") } return &WorkflowService_PollForDecisionTask_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForDecisionTask_Result.DomainNotActiveError") } return &WorkflowService_PollForDecisionTask_Result{DomainNotActiveError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForDecisionTask_Result.ClientVersionNotSupportedError") } return &WorkflowService_PollForDecisionTask_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_PollForDecisionTask_Result.AccessDeniedError") } return &WorkflowService_PollForDecisionTask_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_PollForDecisionTask_Helper.UnwrapResponse = func(result *WorkflowService_PollForDecisionTask_Result) (success *shared.PollForDecisionTaskResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_PollForDecisionTask_Result represents the result of a WorkflowService.PollForDecisionTask function call. // // The result of a PollForDecisionTask execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_PollForDecisionTask_Result struct { // Value returned by PollForDecisionTask after a successful execution. Success *shared.PollForDecisionTaskResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_PollForDecisionTask_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_PollForDecisionTask_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_PollForDecisionTask_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForDecisionTaskResponse_Read(w wire.Value) (*shared.PollForDecisionTaskResponse, error) { var v shared.PollForDecisionTaskResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_PollForDecisionTask_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_PollForDecisionTask_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_PollForDecisionTask_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_PollForDecisionTask_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _PollForDecisionTaskResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_PollForDecisionTask_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_PollForDecisionTask_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_PollForDecisionTask_Result struct could not be encoded. func (v *WorkflowService_PollForDecisionTask_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_PollForDecisionTask_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _PollForDecisionTaskResponse_Decode(sr stream.Reader) (*shared.PollForDecisionTaskResponse, error) { var v shared.PollForDecisionTaskResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_PollForDecisionTask_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_PollForDecisionTask_Result struct could not be generated from the wire // representation. func (v *WorkflowService_PollForDecisionTask_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _PollForDecisionTaskResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_PollForDecisionTask_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_PollForDecisionTask_Result // struct. func (v *WorkflowService_PollForDecisionTask_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_PollForDecisionTask_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_PollForDecisionTask_Result match the // provided WorkflowService_PollForDecisionTask_Result. // // This function performs a deep comparison. func (v *WorkflowService_PollForDecisionTask_Result) Equals(rhs *WorkflowService_PollForDecisionTask_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_PollForDecisionTask_Result. func (v *WorkflowService_PollForDecisionTask_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForDecisionTask_Result) GetSuccess() (o *shared.PollForDecisionTaskResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_PollForDecisionTask_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForDecisionTask_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_PollForDecisionTask_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForDecisionTask_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_PollForDecisionTask_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForDecisionTask_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_PollForDecisionTask_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForDecisionTask_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_PollForDecisionTask_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForDecisionTask_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_PollForDecisionTask_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForDecisionTask_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_PollForDecisionTask_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_PollForDecisionTask_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_PollForDecisionTask_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "PollForDecisionTask" for this struct. func (v *WorkflowService_PollForDecisionTask_Result) MethodName() string { return "PollForDecisionTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_PollForDecisionTask_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_QueryWorkflow_Args represents the arguments for the WorkflowService.QueryWorkflow function. // // The arguments for QueryWorkflow are sent and received over the wire as this struct. type WorkflowService_QueryWorkflow_Args struct { QueryRequest *shared.QueryWorkflowRequest `json:"queryRequest,omitempty"` } // ToWire translates a WorkflowService_QueryWorkflow_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_QueryWorkflow_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.QueryRequest != nil { w, err = v.QueryRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryWorkflowRequest_Read(w wire.Value) (*shared.QueryWorkflowRequest, error) { var v shared.QueryWorkflowRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_QueryWorkflow_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_QueryWorkflow_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_QueryWorkflow_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_QueryWorkflow_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.QueryRequest, err = _QueryWorkflowRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_QueryWorkflow_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_QueryWorkflow_Args struct could not be encoded. func (v *WorkflowService_QueryWorkflow_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.QueryRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.QueryRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryWorkflowRequest_Decode(sr stream.Reader) (*shared.QueryWorkflowRequest, error) { var v shared.QueryWorkflowRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_QueryWorkflow_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_QueryWorkflow_Args struct could not be generated from the wire // representation. func (v *WorkflowService_QueryWorkflow_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.QueryRequest, err = _QueryWorkflowRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_QueryWorkflow_Args // struct. func (v *WorkflowService_QueryWorkflow_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.QueryRequest != nil { fields[i] = fmt.Sprintf("QueryRequest: %v", v.QueryRequest) i++ } return fmt.Sprintf("WorkflowService_QueryWorkflow_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_QueryWorkflow_Args match the // provided WorkflowService_QueryWorkflow_Args. // // This function performs a deep comparison. func (v *WorkflowService_QueryWorkflow_Args) Equals(rhs *WorkflowService_QueryWorkflow_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.QueryRequest == nil && rhs.QueryRequest == nil) || (v.QueryRequest != nil && rhs.QueryRequest != nil && v.QueryRequest.Equals(rhs.QueryRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_QueryWorkflow_Args. func (v *WorkflowService_QueryWorkflow_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.QueryRequest != nil { err = multierr.Append(err, enc.AddObject("queryRequest", v.QueryRequest)) } return err } // GetQueryRequest returns the value of QueryRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_QueryWorkflow_Args) GetQueryRequest() (o *shared.QueryWorkflowRequest) { if v != nil && v.QueryRequest != nil { return v.QueryRequest } return } // IsSetQueryRequest returns true if QueryRequest is not nil. func (v *WorkflowService_QueryWorkflow_Args) IsSetQueryRequest() bool { return v != nil && v.QueryRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "QueryWorkflow" for this struct. func (v *WorkflowService_QueryWorkflow_Args) MethodName() string { return "QueryWorkflow" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_QueryWorkflow_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_QueryWorkflow_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.QueryWorkflow // function. var WorkflowService_QueryWorkflow_Helper = struct { // Args accepts the parameters of QueryWorkflow in-order and returns // the arguments struct for the function. Args func( queryRequest *shared.QueryWorkflowRequest, ) *WorkflowService_QueryWorkflow_Args // IsException returns true if the given error can be thrown // by QueryWorkflow. // // An error can be thrown by QueryWorkflow only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for QueryWorkflow // given its return value and error. // // This allows mapping values and errors returned by // QueryWorkflow into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by QueryWorkflow // // value, err := QueryWorkflow(args) // result, err := WorkflowService_QueryWorkflow_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from QueryWorkflow: %v", err) // } // serialize(result) WrapResponse func(*shared.QueryWorkflowResponse, error) (*WorkflowService_QueryWorkflow_Result, error) // UnwrapResponse takes the result struct for QueryWorkflow // and returns the value or error returned by it. // // The error is non-nil only if QueryWorkflow threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_QueryWorkflow_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_QueryWorkflow_Result) (*shared.QueryWorkflowResponse, error) }{} func init() { WorkflowService_QueryWorkflow_Helper.Args = func( queryRequest *shared.QueryWorkflowRequest, ) *WorkflowService_QueryWorkflow_Args { return &WorkflowService_QueryWorkflow_Args{ QueryRequest: queryRequest, } } WorkflowService_QueryWorkflow_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.QueryFailedError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_QueryWorkflow_Helper.WrapResponse = func(success *shared.QueryWorkflowResponse, err error) (*WorkflowService_QueryWorkflow_Result, error) { if err == nil { return &WorkflowService_QueryWorkflow_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_QueryWorkflow_Result.BadRequestError") } return &WorkflowService_QueryWorkflow_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_QueryWorkflow_Result.EntityNotExistError") } return &WorkflowService_QueryWorkflow_Result{EntityNotExistError: e}, nil case *shared.QueryFailedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_QueryWorkflow_Result.QueryFailedError") } return &WorkflowService_QueryWorkflow_Result{QueryFailedError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_QueryWorkflow_Result.LimitExceededError") } return &WorkflowService_QueryWorkflow_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_QueryWorkflow_Result.ServiceBusyError") } return &WorkflowService_QueryWorkflow_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_QueryWorkflow_Result.ClientVersionNotSupportedError") } return &WorkflowService_QueryWorkflow_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_QueryWorkflow_Result.AccessDeniedError") } return &WorkflowService_QueryWorkflow_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_QueryWorkflow_Helper.UnwrapResponse = func(result *WorkflowService_QueryWorkflow_Result) (success *shared.QueryWorkflowResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.QueryFailedError != nil { err = result.QueryFailedError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_QueryWorkflow_Result represents the result of a WorkflowService.QueryWorkflow function call. // // The result of a QueryWorkflow execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_QueryWorkflow_Result struct { // Value returned by QueryWorkflow after a successful execution. Success *shared.QueryWorkflowResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` QueryFailedError *shared.QueryFailedError `json:"queryFailedError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_QueryWorkflow_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_QueryWorkflow_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.QueryFailedError != nil { w, err = v.QueryFailedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_QueryWorkflow_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryWorkflowResponse_Read(w wire.Value) (*shared.QueryWorkflowResponse, error) { var v shared.QueryWorkflowResponse err := v.FromWire(w) return &v, err } func _QueryFailedError_Read(w wire.Value) (*shared.QueryFailedError, error) { var v shared.QueryFailedError err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_QueryWorkflow_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_QueryWorkflow_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_QueryWorkflow_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_QueryWorkflow_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _QueryWorkflowResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.QueryFailedError, err = _QueryFailedError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.QueryFailedError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_QueryWorkflow_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_QueryWorkflow_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_QueryWorkflow_Result struct could not be encoded. func (v *WorkflowService_QueryWorkflow_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryFailedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.QueryFailedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.QueryFailedError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_QueryWorkflow_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _QueryWorkflowResponse_Decode(sr stream.Reader) (*shared.QueryWorkflowResponse, error) { var v shared.QueryWorkflowResponse err := v.Decode(sr) return &v, err } func _QueryFailedError_Decode(sr stream.Reader) (*shared.QueryFailedError, error) { var v shared.QueryFailedError err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_QueryWorkflow_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_QueryWorkflow_Result struct could not be generated from the wire // representation. func (v *WorkflowService_QueryWorkflow_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _QueryWorkflowResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.QueryFailedError, err = _QueryFailedError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.QueryFailedError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_QueryWorkflow_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_QueryWorkflow_Result // struct. func (v *WorkflowService_QueryWorkflow_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.QueryFailedError != nil { fields[i] = fmt.Sprintf("QueryFailedError: %v", v.QueryFailedError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_QueryWorkflow_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_QueryWorkflow_Result match the // provided WorkflowService_QueryWorkflow_Result. // // This function performs a deep comparison. func (v *WorkflowService_QueryWorkflow_Result) Equals(rhs *WorkflowService_QueryWorkflow_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.QueryFailedError == nil && rhs.QueryFailedError == nil) || (v.QueryFailedError != nil && rhs.QueryFailedError != nil && v.QueryFailedError.Equals(rhs.QueryFailedError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_QueryWorkflow_Result. func (v *WorkflowService_QueryWorkflow_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.QueryFailedError != nil { err = multierr.Append(err, enc.AddObject("queryFailedError", v.QueryFailedError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_QueryWorkflow_Result) GetSuccess() (o *shared.QueryWorkflowResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_QueryWorkflow_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_QueryWorkflow_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_QueryWorkflow_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_QueryWorkflow_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_QueryWorkflow_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetQueryFailedError returns the value of QueryFailedError if it is set or its // zero value if it is unset. func (v *WorkflowService_QueryWorkflow_Result) GetQueryFailedError() (o *shared.QueryFailedError) { if v != nil && v.QueryFailedError != nil { return v.QueryFailedError } return } // IsSetQueryFailedError returns true if QueryFailedError is not nil. func (v *WorkflowService_QueryWorkflow_Result) IsSetQueryFailedError() bool { return v != nil && v.QueryFailedError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_QueryWorkflow_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_QueryWorkflow_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_QueryWorkflow_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_QueryWorkflow_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_QueryWorkflow_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_QueryWorkflow_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_QueryWorkflow_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_QueryWorkflow_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "QueryWorkflow" for this struct. func (v *WorkflowService_QueryWorkflow_Result) MethodName() string { return "QueryWorkflow" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_QueryWorkflow_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RecordActivityTaskHeartbeat_Args represents the arguments for the WorkflowService.RecordActivityTaskHeartbeat function. // // The arguments for RecordActivityTaskHeartbeat are sent and received over the wire as this struct. type WorkflowService_RecordActivityTaskHeartbeat_Args struct { HeartbeatRequest *shared.RecordActivityTaskHeartbeatRequest `json:"heartbeatRequest,omitempty"` } // ToWire translates a WorkflowService_RecordActivityTaskHeartbeat_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.HeartbeatRequest != nil { w, err = v.HeartbeatRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordActivityTaskHeartbeatRequest_Read(w wire.Value) (*shared.RecordActivityTaskHeartbeatRequest, error) { var v shared.RecordActivityTaskHeartbeatRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RecordActivityTaskHeartbeat_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RecordActivityTaskHeartbeat_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RecordActivityTaskHeartbeat_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.HeartbeatRequest, err = _RecordActivityTaskHeartbeatRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RecordActivityTaskHeartbeat_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RecordActivityTaskHeartbeat_Args struct could not be encoded. func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.HeartbeatRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.HeartbeatRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RecordActivityTaskHeartbeatRequest_Decode(sr stream.Reader) (*shared.RecordActivityTaskHeartbeatRequest, error) { var v shared.RecordActivityTaskHeartbeatRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RecordActivityTaskHeartbeat_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RecordActivityTaskHeartbeat_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.HeartbeatRequest, err = _RecordActivityTaskHeartbeatRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RecordActivityTaskHeartbeat_Args // struct. func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.HeartbeatRequest != nil { fields[i] = fmt.Sprintf("HeartbeatRequest: %v", v.HeartbeatRequest) i++ } return fmt.Sprintf("WorkflowService_RecordActivityTaskHeartbeat_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RecordActivityTaskHeartbeat_Args match the // provided WorkflowService_RecordActivityTaskHeartbeat_Args. // // This function performs a deep comparison. func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) Equals(rhs *WorkflowService_RecordActivityTaskHeartbeat_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.HeartbeatRequest == nil && rhs.HeartbeatRequest == nil) || (v.HeartbeatRequest != nil && rhs.HeartbeatRequest != nil && v.HeartbeatRequest.Equals(rhs.HeartbeatRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RecordActivityTaskHeartbeat_Args. func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.HeartbeatRequest != nil { err = multierr.Append(err, enc.AddObject("heartbeatRequest", v.HeartbeatRequest)) } return err } // GetHeartbeatRequest returns the value of HeartbeatRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) GetHeartbeatRequest() (o *shared.RecordActivityTaskHeartbeatRequest) { if v != nil && v.HeartbeatRequest != nil { return v.HeartbeatRequest } return } // IsSetHeartbeatRequest returns true if HeartbeatRequest is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) IsSetHeartbeatRequest() bool { return v != nil && v.HeartbeatRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RecordActivityTaskHeartbeat" for this struct. func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) MethodName() string { return "RecordActivityTaskHeartbeat" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RecordActivityTaskHeartbeat_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RecordActivityTaskHeartbeat_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RecordActivityTaskHeartbeat // function. var WorkflowService_RecordActivityTaskHeartbeat_Helper = struct { // Args accepts the parameters of RecordActivityTaskHeartbeat in-order and returns // the arguments struct for the function. Args func( heartbeatRequest *shared.RecordActivityTaskHeartbeatRequest, ) *WorkflowService_RecordActivityTaskHeartbeat_Args // IsException returns true if the given error can be thrown // by RecordActivityTaskHeartbeat. // // An error can be thrown by RecordActivityTaskHeartbeat only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RecordActivityTaskHeartbeat // given its return value and error. // // This allows mapping values and errors returned by // RecordActivityTaskHeartbeat into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RecordActivityTaskHeartbeat // // value, err := RecordActivityTaskHeartbeat(args) // result, err := WorkflowService_RecordActivityTaskHeartbeat_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RecordActivityTaskHeartbeat: %v", err) // } // serialize(result) WrapResponse func(*shared.RecordActivityTaskHeartbeatResponse, error) (*WorkflowService_RecordActivityTaskHeartbeat_Result, error) // UnwrapResponse takes the result struct for RecordActivityTaskHeartbeat // and returns the value or error returned by it. // // The error is non-nil only if RecordActivityTaskHeartbeat threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_RecordActivityTaskHeartbeat_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RecordActivityTaskHeartbeat_Result) (*shared.RecordActivityTaskHeartbeatResponse, error) }{} func init() { WorkflowService_RecordActivityTaskHeartbeat_Helper.Args = func( heartbeatRequest *shared.RecordActivityTaskHeartbeatRequest, ) *WorkflowService_RecordActivityTaskHeartbeat_Args { return &WorkflowService_RecordActivityTaskHeartbeat_Args{ HeartbeatRequest: heartbeatRequest, } } WorkflowService_RecordActivityTaskHeartbeat_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RecordActivityTaskHeartbeat_Helper.WrapResponse = func(success *shared.RecordActivityTaskHeartbeatResponse, err error) (*WorkflowService_RecordActivityTaskHeartbeat_Result, error) { if err == nil { return &WorkflowService_RecordActivityTaskHeartbeat_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeat_Result.BadRequestError") } return &WorkflowService_RecordActivityTaskHeartbeat_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeat_Result.EntityNotExistError") } return &WorkflowService_RecordActivityTaskHeartbeat_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeat_Result.DomainNotActiveError") } return &WorkflowService_RecordActivityTaskHeartbeat_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeat_Result.LimitExceededError") } return &WorkflowService_RecordActivityTaskHeartbeat_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeat_Result.ServiceBusyError") } return &WorkflowService_RecordActivityTaskHeartbeat_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeat_Result.ClientVersionNotSupportedError") } return &WorkflowService_RecordActivityTaskHeartbeat_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeat_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RecordActivityTaskHeartbeat_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeat_Result.AccessDeniedError") } return &WorkflowService_RecordActivityTaskHeartbeat_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RecordActivityTaskHeartbeat_Helper.UnwrapResponse = func(result *WorkflowService_RecordActivityTaskHeartbeat_Result) (success *shared.RecordActivityTaskHeartbeatResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_RecordActivityTaskHeartbeat_Result represents the result of a WorkflowService.RecordActivityTaskHeartbeat function call. // // The result of a RecordActivityTaskHeartbeat execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_RecordActivityTaskHeartbeat_Result struct { // Value returned by RecordActivityTaskHeartbeat after a successful execution. Success *shared.RecordActivityTaskHeartbeatResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RecordActivityTaskHeartbeat_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RecordActivityTaskHeartbeat_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordActivityTaskHeartbeatResponse_Read(w wire.Value) (*shared.RecordActivityTaskHeartbeatResponse, error) { var v shared.RecordActivityTaskHeartbeatResponse err := v.FromWire(w) return &v, err } func _WorkflowExecutionAlreadyCompletedError_Read(w wire.Value) (*shared.WorkflowExecutionAlreadyCompletedError, error) { var v shared.WorkflowExecutionAlreadyCompletedError err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RecordActivityTaskHeartbeat_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RecordActivityTaskHeartbeat_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RecordActivityTaskHeartbeat_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RecordActivityTaskHeartbeatResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RecordActivityTaskHeartbeat_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RecordActivityTaskHeartbeat_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RecordActivityTaskHeartbeat_Result struct could not be encoded. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RecordActivityTaskHeartbeat_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _RecordActivityTaskHeartbeatResponse_Decode(sr stream.Reader) (*shared.RecordActivityTaskHeartbeatResponse, error) { var v shared.RecordActivityTaskHeartbeatResponse err := v.Decode(sr) return &v, err } func _WorkflowExecutionAlreadyCompletedError_Decode(sr stream.Reader) (*shared.WorkflowExecutionAlreadyCompletedError, error) { var v shared.WorkflowExecutionAlreadyCompletedError err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RecordActivityTaskHeartbeat_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RecordActivityTaskHeartbeat_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RecordActivityTaskHeartbeatResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RecordActivityTaskHeartbeat_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RecordActivityTaskHeartbeat_Result // struct. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RecordActivityTaskHeartbeat_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RecordActivityTaskHeartbeat_Result match the // provided WorkflowService_RecordActivityTaskHeartbeat_Result. // // This function performs a deep comparison. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) Equals(rhs *WorkflowService_RecordActivityTaskHeartbeat_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RecordActivityTaskHeartbeat_Result. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) GetSuccess() (o *shared.RecordActivityTaskHeartbeatResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RecordActivityTaskHeartbeat" for this struct. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) MethodName() string { return "RecordActivityTaskHeartbeat" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RecordActivityTaskHeartbeat_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RecordActivityTaskHeartbeatByID_Args represents the arguments for the WorkflowService.RecordActivityTaskHeartbeatByID function. // // The arguments for RecordActivityTaskHeartbeatByID are sent and received over the wire as this struct. type WorkflowService_RecordActivityTaskHeartbeatByID_Args struct { HeartbeatRequest *shared.RecordActivityTaskHeartbeatByIDRequest `json:"heartbeatRequest,omitempty"` } // ToWire translates a WorkflowService_RecordActivityTaskHeartbeatByID_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.HeartbeatRequest != nil { w, err = v.HeartbeatRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordActivityTaskHeartbeatByIDRequest_Read(w wire.Value) (*shared.RecordActivityTaskHeartbeatByIDRequest, error) { var v shared.RecordActivityTaskHeartbeatByIDRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RecordActivityTaskHeartbeatByID_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RecordActivityTaskHeartbeatByID_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RecordActivityTaskHeartbeatByID_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.HeartbeatRequest, err = _RecordActivityTaskHeartbeatByIDRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RecordActivityTaskHeartbeatByID_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RecordActivityTaskHeartbeatByID_Args struct could not be encoded. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.HeartbeatRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.HeartbeatRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RecordActivityTaskHeartbeatByIDRequest_Decode(sr stream.Reader) (*shared.RecordActivityTaskHeartbeatByIDRequest, error) { var v shared.RecordActivityTaskHeartbeatByIDRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RecordActivityTaskHeartbeatByID_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RecordActivityTaskHeartbeatByID_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.HeartbeatRequest, err = _RecordActivityTaskHeartbeatByIDRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RecordActivityTaskHeartbeatByID_Args // struct. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.HeartbeatRequest != nil { fields[i] = fmt.Sprintf("HeartbeatRequest: %v", v.HeartbeatRequest) i++ } return fmt.Sprintf("WorkflowService_RecordActivityTaskHeartbeatByID_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RecordActivityTaskHeartbeatByID_Args match the // provided WorkflowService_RecordActivityTaskHeartbeatByID_Args. // // This function performs a deep comparison. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) Equals(rhs *WorkflowService_RecordActivityTaskHeartbeatByID_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.HeartbeatRequest == nil && rhs.HeartbeatRequest == nil) || (v.HeartbeatRequest != nil && rhs.HeartbeatRequest != nil && v.HeartbeatRequest.Equals(rhs.HeartbeatRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RecordActivityTaskHeartbeatByID_Args. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.HeartbeatRequest != nil { err = multierr.Append(err, enc.AddObject("heartbeatRequest", v.HeartbeatRequest)) } return err } // GetHeartbeatRequest returns the value of HeartbeatRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) GetHeartbeatRequest() (o *shared.RecordActivityTaskHeartbeatByIDRequest) { if v != nil && v.HeartbeatRequest != nil { return v.HeartbeatRequest } return } // IsSetHeartbeatRequest returns true if HeartbeatRequest is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) IsSetHeartbeatRequest() bool { return v != nil && v.HeartbeatRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RecordActivityTaskHeartbeatByID" for this struct. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) MethodName() string { return "RecordActivityTaskHeartbeatByID" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RecordActivityTaskHeartbeatByID_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RecordActivityTaskHeartbeatByID // function. var WorkflowService_RecordActivityTaskHeartbeatByID_Helper = struct { // Args accepts the parameters of RecordActivityTaskHeartbeatByID in-order and returns // the arguments struct for the function. Args func( heartbeatRequest *shared.RecordActivityTaskHeartbeatByIDRequest, ) *WorkflowService_RecordActivityTaskHeartbeatByID_Args // IsException returns true if the given error can be thrown // by RecordActivityTaskHeartbeatByID. // // An error can be thrown by RecordActivityTaskHeartbeatByID only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RecordActivityTaskHeartbeatByID // given its return value and error. // // This allows mapping values and errors returned by // RecordActivityTaskHeartbeatByID into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RecordActivityTaskHeartbeatByID // // value, err := RecordActivityTaskHeartbeatByID(args) // result, err := WorkflowService_RecordActivityTaskHeartbeatByID_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RecordActivityTaskHeartbeatByID: %v", err) // } // serialize(result) WrapResponse func(*shared.RecordActivityTaskHeartbeatResponse, error) (*WorkflowService_RecordActivityTaskHeartbeatByID_Result, error) // UnwrapResponse takes the result struct for RecordActivityTaskHeartbeatByID // and returns the value or error returned by it. // // The error is non-nil only if RecordActivityTaskHeartbeatByID threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_RecordActivityTaskHeartbeatByID_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RecordActivityTaskHeartbeatByID_Result) (*shared.RecordActivityTaskHeartbeatResponse, error) }{} func init() { WorkflowService_RecordActivityTaskHeartbeatByID_Helper.Args = func( heartbeatRequest *shared.RecordActivityTaskHeartbeatByIDRequest, ) *WorkflowService_RecordActivityTaskHeartbeatByID_Args { return &WorkflowService_RecordActivityTaskHeartbeatByID_Args{ HeartbeatRequest: heartbeatRequest, } } WorkflowService_RecordActivityTaskHeartbeatByID_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RecordActivityTaskHeartbeatByID_Helper.WrapResponse = func(success *shared.RecordActivityTaskHeartbeatResponse, err error) (*WorkflowService_RecordActivityTaskHeartbeatByID_Result, error) { if err == nil { return &WorkflowService_RecordActivityTaskHeartbeatByID_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeatByID_Result.BadRequestError") } return &WorkflowService_RecordActivityTaskHeartbeatByID_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeatByID_Result.EntityNotExistError") } return &WorkflowService_RecordActivityTaskHeartbeatByID_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeatByID_Result.DomainNotActiveError") } return &WorkflowService_RecordActivityTaskHeartbeatByID_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeatByID_Result.LimitExceededError") } return &WorkflowService_RecordActivityTaskHeartbeatByID_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeatByID_Result.ServiceBusyError") } return &WorkflowService_RecordActivityTaskHeartbeatByID_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeatByID_Result.ClientVersionNotSupportedError") } return &WorkflowService_RecordActivityTaskHeartbeatByID_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeatByID_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RecordActivityTaskHeartbeatByID_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RecordActivityTaskHeartbeatByID_Result.AccessDeniedError") } return &WorkflowService_RecordActivityTaskHeartbeatByID_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RecordActivityTaskHeartbeatByID_Helper.UnwrapResponse = func(result *WorkflowService_RecordActivityTaskHeartbeatByID_Result) (success *shared.RecordActivityTaskHeartbeatResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_RecordActivityTaskHeartbeatByID_Result represents the result of a WorkflowService.RecordActivityTaskHeartbeatByID function call. // // The result of a RecordActivityTaskHeartbeatByID execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_RecordActivityTaskHeartbeatByID_Result struct { // Value returned by RecordActivityTaskHeartbeatByID after a successful execution. Success *shared.RecordActivityTaskHeartbeatResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RecordActivityTaskHeartbeatByID_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RecordActivityTaskHeartbeatByID_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_RecordActivityTaskHeartbeatByID_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RecordActivityTaskHeartbeatByID_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RecordActivityTaskHeartbeatByID_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RecordActivityTaskHeartbeatResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RecordActivityTaskHeartbeatByID_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RecordActivityTaskHeartbeatByID_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RecordActivityTaskHeartbeatByID_Result struct could not be encoded. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RecordActivityTaskHeartbeatByID_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_RecordActivityTaskHeartbeatByID_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RecordActivityTaskHeartbeatByID_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RecordActivityTaskHeartbeatResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RecordActivityTaskHeartbeatByID_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RecordActivityTaskHeartbeatByID_Result // struct. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RecordActivityTaskHeartbeatByID_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RecordActivityTaskHeartbeatByID_Result match the // provided WorkflowService_RecordActivityTaskHeartbeatByID_Result. // // This function performs a deep comparison. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) Equals(rhs *WorkflowService_RecordActivityTaskHeartbeatByID_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RecordActivityTaskHeartbeatByID_Result. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) GetSuccess() (o *shared.RecordActivityTaskHeartbeatResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RecordActivityTaskHeartbeatByID" for this struct. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) MethodName() string { return "RecordActivityTaskHeartbeatByID" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RecordActivityTaskHeartbeatByID_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RefreshWorkflowTasks_Args represents the arguments for the WorkflowService.RefreshWorkflowTasks function. // // The arguments for RefreshWorkflowTasks are sent and received over the wire as this struct. type WorkflowService_RefreshWorkflowTasks_Args struct { Request *shared.RefreshWorkflowTasksRequest `json:"request,omitempty"` } // ToWire translates a WorkflowService_RefreshWorkflowTasks_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RefreshWorkflowTasks_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RefreshWorkflowTasksRequest_Read(w wire.Value) (*shared.RefreshWorkflowTasksRequest, error) { var v shared.RefreshWorkflowTasksRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RefreshWorkflowTasks_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RefreshWorkflowTasks_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RefreshWorkflowTasks_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RefreshWorkflowTasks_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _RefreshWorkflowTasksRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RefreshWorkflowTasks_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RefreshWorkflowTasks_Args struct could not be encoded. func (v *WorkflowService_RefreshWorkflowTasks_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RefreshWorkflowTasksRequest_Decode(sr stream.Reader) (*shared.RefreshWorkflowTasksRequest, error) { var v shared.RefreshWorkflowTasksRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RefreshWorkflowTasks_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RefreshWorkflowTasks_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RefreshWorkflowTasks_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _RefreshWorkflowTasksRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RefreshWorkflowTasks_Args // struct. func (v *WorkflowService_RefreshWorkflowTasks_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("WorkflowService_RefreshWorkflowTasks_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RefreshWorkflowTasks_Args match the // provided WorkflowService_RefreshWorkflowTasks_Args. // // This function performs a deep comparison. func (v *WorkflowService_RefreshWorkflowTasks_Args) Equals(rhs *WorkflowService_RefreshWorkflowTasks_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RefreshWorkflowTasks_Args. func (v *WorkflowService_RefreshWorkflowTasks_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *WorkflowService_RefreshWorkflowTasks_Args) GetRequest() (o *shared.RefreshWorkflowTasksRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *WorkflowService_RefreshWorkflowTasks_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RefreshWorkflowTasks" for this struct. func (v *WorkflowService_RefreshWorkflowTasks_Args) MethodName() string { return "RefreshWorkflowTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RefreshWorkflowTasks_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RefreshWorkflowTasks_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RefreshWorkflowTasks // function. var WorkflowService_RefreshWorkflowTasks_Helper = struct { // Args accepts the parameters of RefreshWorkflowTasks in-order and returns // the arguments struct for the function. Args func( request *shared.RefreshWorkflowTasksRequest, ) *WorkflowService_RefreshWorkflowTasks_Args // IsException returns true if the given error can be thrown // by RefreshWorkflowTasks. // // An error can be thrown by RefreshWorkflowTasks only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RefreshWorkflowTasks // given the error returned by it. The provided error may // be nil if RefreshWorkflowTasks did not fail. // // This allows mapping errors returned by RefreshWorkflowTasks into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RefreshWorkflowTasks // // err := RefreshWorkflowTasks(args) // result, err := WorkflowService_RefreshWorkflowTasks_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RefreshWorkflowTasks: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RefreshWorkflowTasks_Result, error) // UnwrapResponse takes the result struct for RefreshWorkflowTasks // and returns the erorr returned by it (if any). // // The error is non-nil only if RefreshWorkflowTasks threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RefreshWorkflowTasks_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RefreshWorkflowTasks_Result) error }{} func init() { WorkflowService_RefreshWorkflowTasks_Helper.Args = func( request *shared.RefreshWorkflowTasksRequest, ) *WorkflowService_RefreshWorkflowTasks_Args { return &WorkflowService_RefreshWorkflowTasks_Args{ Request: request, } } WorkflowService_RefreshWorkflowTasks_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.DomainNotActiveError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RefreshWorkflowTasks_Helper.WrapResponse = func(err error) (*WorkflowService_RefreshWorkflowTasks_Result, error) { if err == nil { return &WorkflowService_RefreshWorkflowTasks_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RefreshWorkflowTasks_Result.BadRequestError") } return &WorkflowService_RefreshWorkflowTasks_Result{BadRequestError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RefreshWorkflowTasks_Result.DomainNotActiveError") } return &WorkflowService_RefreshWorkflowTasks_Result{DomainNotActiveError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RefreshWorkflowTasks_Result.ServiceBusyError") } return &WorkflowService_RefreshWorkflowTasks_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RefreshWorkflowTasks_Result.EntityNotExistError") } return &WorkflowService_RefreshWorkflowTasks_Result{EntityNotExistError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RefreshWorkflowTasks_Result.AccessDeniedError") } return &WorkflowService_RefreshWorkflowTasks_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RefreshWorkflowTasks_Helper.UnwrapResponse = func(result *WorkflowService_RefreshWorkflowTasks_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RefreshWorkflowTasks_Result represents the result of a WorkflowService.RefreshWorkflowTasks function call. // // The result of a RefreshWorkflowTasks execution is sent and received over the wire as this struct. type WorkflowService_RefreshWorkflowTasks_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RefreshWorkflowTasks_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RefreshWorkflowTasks_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_RefreshWorkflowTasks_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RefreshWorkflowTasks_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RefreshWorkflowTasks_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RefreshWorkflowTasks_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RefreshWorkflowTasks_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RefreshWorkflowTasks_Result struct could not be encoded. func (v *WorkflowService_RefreshWorkflowTasks_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_RefreshWorkflowTasks_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RefreshWorkflowTasks_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RefreshWorkflowTasks_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RefreshWorkflowTasks_Result // struct. func (v *WorkflowService_RefreshWorkflowTasks_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RefreshWorkflowTasks_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RefreshWorkflowTasks_Result match the // provided WorkflowService_RefreshWorkflowTasks_Result. // // This function performs a deep comparison. func (v *WorkflowService_RefreshWorkflowTasks_Result) Equals(rhs *WorkflowService_RefreshWorkflowTasks_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RefreshWorkflowTasks_Result. func (v *WorkflowService_RefreshWorkflowTasks_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RefreshWorkflowTasks_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RefreshWorkflowTasks_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RefreshWorkflowTasks_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RefreshWorkflowTasks_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RefreshWorkflowTasks_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RefreshWorkflowTasks_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RefreshWorkflowTasks_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RefreshWorkflowTasks_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RefreshWorkflowTasks_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RefreshWorkflowTasks_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RefreshWorkflowTasks" for this struct. func (v *WorkflowService_RefreshWorkflowTasks_Result) MethodName() string { return "RefreshWorkflowTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RefreshWorkflowTasks_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RegisterDomain_Args represents the arguments for the WorkflowService.RegisterDomain function. // // The arguments for RegisterDomain are sent and received over the wire as this struct. type WorkflowService_RegisterDomain_Args struct { RegisterRequest *shared.RegisterDomainRequest `json:"registerRequest,omitempty"` } // ToWire translates a WorkflowService_RegisterDomain_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RegisterDomain_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.RegisterRequest != nil { w, err = v.RegisterRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RegisterDomainRequest_Read(w wire.Value) (*shared.RegisterDomainRequest, error) { var v shared.RegisterDomainRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RegisterDomain_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RegisterDomain_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RegisterDomain_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RegisterDomain_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.RegisterRequest, err = _RegisterDomainRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RegisterDomain_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RegisterDomain_Args struct could not be encoded. func (v *WorkflowService_RegisterDomain_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.RegisterRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.RegisterRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RegisterDomainRequest_Decode(sr stream.Reader) (*shared.RegisterDomainRequest, error) { var v shared.RegisterDomainRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RegisterDomain_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RegisterDomain_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RegisterDomain_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.RegisterRequest, err = _RegisterDomainRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RegisterDomain_Args // struct. func (v *WorkflowService_RegisterDomain_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.RegisterRequest != nil { fields[i] = fmt.Sprintf("RegisterRequest: %v", v.RegisterRequest) i++ } return fmt.Sprintf("WorkflowService_RegisterDomain_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RegisterDomain_Args match the // provided WorkflowService_RegisterDomain_Args. // // This function performs a deep comparison. func (v *WorkflowService_RegisterDomain_Args) Equals(rhs *WorkflowService_RegisterDomain_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.RegisterRequest == nil && rhs.RegisterRequest == nil) || (v.RegisterRequest != nil && rhs.RegisterRequest != nil && v.RegisterRequest.Equals(rhs.RegisterRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RegisterDomain_Args. func (v *WorkflowService_RegisterDomain_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.RegisterRequest != nil { err = multierr.Append(err, enc.AddObject("registerRequest", v.RegisterRequest)) } return err } // GetRegisterRequest returns the value of RegisterRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RegisterDomain_Args) GetRegisterRequest() (o *shared.RegisterDomainRequest) { if v != nil && v.RegisterRequest != nil { return v.RegisterRequest } return } // IsSetRegisterRequest returns true if RegisterRequest is not nil. func (v *WorkflowService_RegisterDomain_Args) IsSetRegisterRequest() bool { return v != nil && v.RegisterRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RegisterDomain" for this struct. func (v *WorkflowService_RegisterDomain_Args) MethodName() string { return "RegisterDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RegisterDomain_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RegisterDomain_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RegisterDomain // function. var WorkflowService_RegisterDomain_Helper = struct { // Args accepts the parameters of RegisterDomain in-order and returns // the arguments struct for the function. Args func( registerRequest *shared.RegisterDomainRequest, ) *WorkflowService_RegisterDomain_Args // IsException returns true if the given error can be thrown // by RegisterDomain. // // An error can be thrown by RegisterDomain only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RegisterDomain // given the error returned by it. The provided error may // be nil if RegisterDomain did not fail. // // This allows mapping errors returned by RegisterDomain into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RegisterDomain // // err := RegisterDomain(args) // result, err := WorkflowService_RegisterDomain_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RegisterDomain: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RegisterDomain_Result, error) // UnwrapResponse takes the result struct for RegisterDomain // and returns the erorr returned by it (if any). // // The error is non-nil only if RegisterDomain threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RegisterDomain_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RegisterDomain_Result) error }{} func init() { WorkflowService_RegisterDomain_Helper.Args = func( registerRequest *shared.RegisterDomainRequest, ) *WorkflowService_RegisterDomain_Args { return &WorkflowService_RegisterDomain_Args{ RegisterRequest: registerRequest, } } WorkflowService_RegisterDomain_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.DomainAlreadyExistsError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RegisterDomain_Helper.WrapResponse = func(err error) (*WorkflowService_RegisterDomain_Result, error) { if err == nil { return &WorkflowService_RegisterDomain_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RegisterDomain_Result.BadRequestError") } return &WorkflowService_RegisterDomain_Result{BadRequestError: e}, nil case *shared.DomainAlreadyExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RegisterDomain_Result.DomainExistsError") } return &WorkflowService_RegisterDomain_Result{DomainExistsError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RegisterDomain_Result.ServiceBusyError") } return &WorkflowService_RegisterDomain_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RegisterDomain_Result.ClientVersionNotSupportedError") } return &WorkflowService_RegisterDomain_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RegisterDomain_Result.AccessDeniedError") } return &WorkflowService_RegisterDomain_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RegisterDomain_Helper.UnwrapResponse = func(result *WorkflowService_RegisterDomain_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.DomainExistsError != nil { err = result.DomainExistsError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RegisterDomain_Result represents the result of a WorkflowService.RegisterDomain function call. // // The result of a RegisterDomain execution is sent and received over the wire as this struct. type WorkflowService_RegisterDomain_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` DomainExistsError *shared.DomainAlreadyExistsError `json:"domainExistsError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RegisterDomain_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RegisterDomain_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.DomainExistsError != nil { w, err = v.DomainExistsError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RegisterDomain_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DomainAlreadyExistsError_Read(w wire.Value) (*shared.DomainAlreadyExistsError, error) { var v shared.DomainAlreadyExistsError err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RegisterDomain_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RegisterDomain_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RegisterDomain_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RegisterDomain_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.DomainExistsError, err = _DomainAlreadyExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.DomainExistsError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RegisterDomain_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RegisterDomain_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RegisterDomain_Result struct could not be encoded. func (v *WorkflowService_RegisterDomain_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainExistsError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.DomainExistsError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.DomainExistsError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RegisterDomain_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _DomainAlreadyExistsError_Decode(sr stream.Reader) (*shared.DomainAlreadyExistsError, error) { var v shared.DomainAlreadyExistsError err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RegisterDomain_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RegisterDomain_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RegisterDomain_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.DomainExistsError, err = _DomainAlreadyExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.DomainExistsError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RegisterDomain_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RegisterDomain_Result // struct. func (v *WorkflowService_RegisterDomain_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.DomainExistsError != nil { fields[i] = fmt.Sprintf("DomainExistsError: %v", v.DomainExistsError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RegisterDomain_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RegisterDomain_Result match the // provided WorkflowService_RegisterDomain_Result. // // This function performs a deep comparison. func (v *WorkflowService_RegisterDomain_Result) Equals(rhs *WorkflowService_RegisterDomain_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.DomainExistsError == nil && rhs.DomainExistsError == nil) || (v.DomainExistsError != nil && rhs.DomainExistsError != nil && v.DomainExistsError.Equals(rhs.DomainExistsError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RegisterDomain_Result. func (v *WorkflowService_RegisterDomain_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.DomainExistsError != nil { err = multierr.Append(err, enc.AddObject("domainExistsError", v.DomainExistsError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RegisterDomain_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RegisterDomain_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetDomainExistsError returns the value of DomainExistsError if it is set or its // zero value if it is unset. func (v *WorkflowService_RegisterDomain_Result) GetDomainExistsError() (o *shared.DomainAlreadyExistsError) { if v != nil && v.DomainExistsError != nil { return v.DomainExistsError } return } // IsSetDomainExistsError returns true if DomainExistsError is not nil. func (v *WorkflowService_RegisterDomain_Result) IsSetDomainExistsError() bool { return v != nil && v.DomainExistsError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RegisterDomain_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RegisterDomain_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RegisterDomain_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RegisterDomain_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RegisterDomain_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RegisterDomain_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RegisterDomain" for this struct. func (v *WorkflowService_RegisterDomain_Result) MethodName() string { return "RegisterDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RegisterDomain_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RequestCancelWorkflowExecution_Args represents the arguments for the WorkflowService.RequestCancelWorkflowExecution function. // // The arguments for RequestCancelWorkflowExecution are sent and received over the wire as this struct. type WorkflowService_RequestCancelWorkflowExecution_Args struct { CancelRequest *shared.RequestCancelWorkflowExecutionRequest `json:"cancelRequest,omitempty"` } // ToWire translates a WorkflowService_RequestCancelWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RequestCancelWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CancelRequest != nil { w, err = v.CancelRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RequestCancelWorkflowExecutionRequest_Read(w wire.Value) (*shared.RequestCancelWorkflowExecutionRequest, error) { var v shared.RequestCancelWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RequestCancelWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RequestCancelWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RequestCancelWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RequestCancelWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CancelRequest, err = _RequestCancelWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RequestCancelWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RequestCancelWorkflowExecution_Args struct could not be encoded. func (v *WorkflowService_RequestCancelWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CancelRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CancelRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RequestCancelWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.RequestCancelWorkflowExecutionRequest, error) { var v shared.RequestCancelWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RequestCancelWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RequestCancelWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RequestCancelWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CancelRequest, err = _RequestCancelWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RequestCancelWorkflowExecution_Args // struct. func (v *WorkflowService_RequestCancelWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CancelRequest != nil { fields[i] = fmt.Sprintf("CancelRequest: %v", v.CancelRequest) i++ } return fmt.Sprintf("WorkflowService_RequestCancelWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RequestCancelWorkflowExecution_Args match the // provided WorkflowService_RequestCancelWorkflowExecution_Args. // // This function performs a deep comparison. func (v *WorkflowService_RequestCancelWorkflowExecution_Args) Equals(rhs *WorkflowService_RequestCancelWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CancelRequest == nil && rhs.CancelRequest == nil) || (v.CancelRequest != nil && rhs.CancelRequest != nil && v.CancelRequest.Equals(rhs.CancelRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RequestCancelWorkflowExecution_Args. func (v *WorkflowService_RequestCancelWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CancelRequest != nil { err = multierr.Append(err, enc.AddObject("cancelRequest", v.CancelRequest)) } return err } // GetCancelRequest returns the value of CancelRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RequestCancelWorkflowExecution_Args) GetCancelRequest() (o *shared.RequestCancelWorkflowExecutionRequest) { if v != nil && v.CancelRequest != nil { return v.CancelRequest } return } // IsSetCancelRequest returns true if CancelRequest is not nil. func (v *WorkflowService_RequestCancelWorkflowExecution_Args) IsSetCancelRequest() bool { return v != nil && v.CancelRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RequestCancelWorkflowExecution" for this struct. func (v *WorkflowService_RequestCancelWorkflowExecution_Args) MethodName() string { return "RequestCancelWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RequestCancelWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RequestCancelWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RequestCancelWorkflowExecution // function. var WorkflowService_RequestCancelWorkflowExecution_Helper = struct { // Args accepts the parameters of RequestCancelWorkflowExecution in-order and returns // the arguments struct for the function. Args func( cancelRequest *shared.RequestCancelWorkflowExecutionRequest, ) *WorkflowService_RequestCancelWorkflowExecution_Args // IsException returns true if the given error can be thrown // by RequestCancelWorkflowExecution. // // An error can be thrown by RequestCancelWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RequestCancelWorkflowExecution // given the error returned by it. The provided error may // be nil if RequestCancelWorkflowExecution did not fail. // // This allows mapping errors returned by RequestCancelWorkflowExecution into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RequestCancelWorkflowExecution // // err := RequestCancelWorkflowExecution(args) // result, err := WorkflowService_RequestCancelWorkflowExecution_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RequestCancelWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RequestCancelWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for RequestCancelWorkflowExecution // and returns the erorr returned by it (if any). // // The error is non-nil only if RequestCancelWorkflowExecution threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RequestCancelWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RequestCancelWorkflowExecution_Result) error }{} func init() { WorkflowService_RequestCancelWorkflowExecution_Helper.Args = func( cancelRequest *shared.RequestCancelWorkflowExecutionRequest, ) *WorkflowService_RequestCancelWorkflowExecution_Args { return &WorkflowService_RequestCancelWorkflowExecution_Args{ CancelRequest: cancelRequest, } } WorkflowService_RequestCancelWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.CancellationAlreadyRequestedError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RequestCancelWorkflowExecution_Helper.WrapResponse = func(err error) (*WorkflowService_RequestCancelWorkflowExecution_Result, error) { if err == nil { return &WorkflowService_RequestCancelWorkflowExecution_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RequestCancelWorkflowExecution_Result.BadRequestError") } return &WorkflowService_RequestCancelWorkflowExecution_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RequestCancelWorkflowExecution_Result.EntityNotExistError") } return &WorkflowService_RequestCancelWorkflowExecution_Result{EntityNotExistError: e}, nil case *shared.CancellationAlreadyRequestedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RequestCancelWorkflowExecution_Result.CancellationAlreadyRequestedError") } return &WorkflowService_RequestCancelWorkflowExecution_Result{CancellationAlreadyRequestedError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RequestCancelWorkflowExecution_Result.ServiceBusyError") } return &WorkflowService_RequestCancelWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RequestCancelWorkflowExecution_Result.DomainNotActiveError") } return &WorkflowService_RequestCancelWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RequestCancelWorkflowExecution_Result.LimitExceededError") } return &WorkflowService_RequestCancelWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RequestCancelWorkflowExecution_Result.ClientVersionNotSupportedError") } return &WorkflowService_RequestCancelWorkflowExecution_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RequestCancelWorkflowExecution_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RequestCancelWorkflowExecution_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RequestCancelWorkflowExecution_Result.AccessDeniedError") } return &WorkflowService_RequestCancelWorkflowExecution_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RequestCancelWorkflowExecution_Helper.UnwrapResponse = func(result *WorkflowService_RequestCancelWorkflowExecution_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.CancellationAlreadyRequestedError != nil { err = result.CancellationAlreadyRequestedError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RequestCancelWorkflowExecution_Result represents the result of a WorkflowService.RequestCancelWorkflowExecution function call. // // The result of a RequestCancelWorkflowExecution execution is sent and received over the wire as this struct. type WorkflowService_RequestCancelWorkflowExecution_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` CancellationAlreadyRequestedError *shared.CancellationAlreadyRequestedError `json:"cancellationAlreadyRequestedError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RequestCancelWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RequestCancelWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.CancellationAlreadyRequestedError != nil { w, err = v.CancellationAlreadyRequestedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RequestCancelWorkflowExecution_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CancellationAlreadyRequestedError_Read(w wire.Value) (*shared.CancellationAlreadyRequestedError, error) { var v shared.CancellationAlreadyRequestedError err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RequestCancelWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RequestCancelWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RequestCancelWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RequestCancelWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.CancellationAlreadyRequestedError, err = _CancellationAlreadyRequestedError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 10: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.CancellationAlreadyRequestedError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RequestCancelWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RequestCancelWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RequestCancelWorkflowExecution_Result struct could not be encoded. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancellationAlreadyRequestedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.CancellationAlreadyRequestedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.CancellationAlreadyRequestedError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RequestCancelWorkflowExecution_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _CancellationAlreadyRequestedError_Decode(sr stream.Reader) (*shared.CancellationAlreadyRequestedError, error) { var v shared.CancellationAlreadyRequestedError err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RequestCancelWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RequestCancelWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.CancellationAlreadyRequestedError, err = _CancellationAlreadyRequestedError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 10 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.CancellationAlreadyRequestedError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RequestCancelWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RequestCancelWorkflowExecution_Result // struct. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.CancellationAlreadyRequestedError != nil { fields[i] = fmt.Sprintf("CancellationAlreadyRequestedError: %v", v.CancellationAlreadyRequestedError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RequestCancelWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RequestCancelWorkflowExecution_Result match the // provided WorkflowService_RequestCancelWorkflowExecution_Result. // // This function performs a deep comparison. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) Equals(rhs *WorkflowService_RequestCancelWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.CancellationAlreadyRequestedError == nil && rhs.CancellationAlreadyRequestedError == nil) || (v.CancellationAlreadyRequestedError != nil && rhs.CancellationAlreadyRequestedError != nil && v.CancellationAlreadyRequestedError.Equals(rhs.CancellationAlreadyRequestedError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RequestCancelWorkflowExecution_Result. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.CancellationAlreadyRequestedError != nil { err = multierr.Append(err, enc.AddObject("cancellationAlreadyRequestedError", v.CancellationAlreadyRequestedError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetCancellationAlreadyRequestedError returns the value of CancellationAlreadyRequestedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) GetCancellationAlreadyRequestedError() (o *shared.CancellationAlreadyRequestedError) { if v != nil && v.CancellationAlreadyRequestedError != nil { return v.CancellationAlreadyRequestedError } return } // IsSetCancellationAlreadyRequestedError returns true if CancellationAlreadyRequestedError is not nil. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) IsSetCancellationAlreadyRequestedError() bool { return v != nil && v.CancellationAlreadyRequestedError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RequestCancelWorkflowExecution" for this struct. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) MethodName() string { return "RequestCancelWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RequestCancelWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_ResetStickyTaskList_Args represents the arguments for the WorkflowService.ResetStickyTaskList function. // // The arguments for ResetStickyTaskList are sent and received over the wire as this struct. type WorkflowService_ResetStickyTaskList_Args struct { ResetRequest *shared.ResetStickyTaskListRequest `json:"resetRequest,omitempty"` } // ToWire translates a WorkflowService_ResetStickyTaskList_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ResetStickyTaskList_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ResetRequest != nil { w, err = v.ResetRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetStickyTaskListRequest_Read(w wire.Value) (*shared.ResetStickyTaskListRequest, error) { var v shared.ResetStickyTaskListRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ResetStickyTaskList_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ResetStickyTaskList_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ResetStickyTaskList_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ResetStickyTaskList_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ResetRequest, err = _ResetStickyTaskListRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_ResetStickyTaskList_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ResetStickyTaskList_Args struct could not be encoded. func (v *WorkflowService_ResetStickyTaskList_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ResetRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ResetRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ResetStickyTaskListRequest_Decode(sr stream.Reader) (*shared.ResetStickyTaskListRequest, error) { var v shared.ResetStickyTaskListRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ResetStickyTaskList_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ResetStickyTaskList_Args struct could not be generated from the wire // representation. func (v *WorkflowService_ResetStickyTaskList_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ResetRequest, err = _ResetStickyTaskListRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_ResetStickyTaskList_Args // struct. func (v *WorkflowService_ResetStickyTaskList_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ResetRequest != nil { fields[i] = fmt.Sprintf("ResetRequest: %v", v.ResetRequest) i++ } return fmt.Sprintf("WorkflowService_ResetStickyTaskList_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ResetStickyTaskList_Args match the // provided WorkflowService_ResetStickyTaskList_Args. // // This function performs a deep comparison. func (v *WorkflowService_ResetStickyTaskList_Args) Equals(rhs *WorkflowService_ResetStickyTaskList_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ResetRequest == nil && rhs.ResetRequest == nil) || (v.ResetRequest != nil && rhs.ResetRequest != nil && v.ResetRequest.Equals(rhs.ResetRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ResetStickyTaskList_Args. func (v *WorkflowService_ResetStickyTaskList_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ResetRequest != nil { err = multierr.Append(err, enc.AddObject("resetRequest", v.ResetRequest)) } return err } // GetResetRequest returns the value of ResetRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetStickyTaskList_Args) GetResetRequest() (o *shared.ResetStickyTaskListRequest) { if v != nil && v.ResetRequest != nil { return v.ResetRequest } return } // IsSetResetRequest returns true if ResetRequest is not nil. func (v *WorkflowService_ResetStickyTaskList_Args) IsSetResetRequest() bool { return v != nil && v.ResetRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ResetStickyTaskList" for this struct. func (v *WorkflowService_ResetStickyTaskList_Args) MethodName() string { return "ResetStickyTaskList" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_ResetStickyTaskList_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_ResetStickyTaskList_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.ResetStickyTaskList // function. var WorkflowService_ResetStickyTaskList_Helper = struct { // Args accepts the parameters of ResetStickyTaskList in-order and returns // the arguments struct for the function. Args func( resetRequest *shared.ResetStickyTaskListRequest, ) *WorkflowService_ResetStickyTaskList_Args // IsException returns true if the given error can be thrown // by ResetStickyTaskList. // // An error can be thrown by ResetStickyTaskList only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ResetStickyTaskList // given its return value and error. // // This allows mapping values and errors returned by // ResetStickyTaskList into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ResetStickyTaskList // // value, err := ResetStickyTaskList(args) // result, err := WorkflowService_ResetStickyTaskList_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ResetStickyTaskList: %v", err) // } // serialize(result) WrapResponse func(*shared.ResetStickyTaskListResponse, error) (*WorkflowService_ResetStickyTaskList_Result, error) // UnwrapResponse takes the result struct for ResetStickyTaskList // and returns the value or error returned by it. // // The error is non-nil only if ResetStickyTaskList threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_ResetStickyTaskList_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_ResetStickyTaskList_Result) (*shared.ResetStickyTaskListResponse, error) }{} func init() { WorkflowService_ResetStickyTaskList_Helper.Args = func( resetRequest *shared.ResetStickyTaskListRequest, ) *WorkflowService_ResetStickyTaskList_Args { return &WorkflowService_ResetStickyTaskList_Args{ ResetRequest: resetRequest, } } WorkflowService_ResetStickyTaskList_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_ResetStickyTaskList_Helper.WrapResponse = func(success *shared.ResetStickyTaskListResponse, err error) (*WorkflowService_ResetStickyTaskList_Result, error) { if err == nil { return &WorkflowService_ResetStickyTaskList_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetStickyTaskList_Result.BadRequestError") } return &WorkflowService_ResetStickyTaskList_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetStickyTaskList_Result.EntityNotExistError") } return &WorkflowService_ResetStickyTaskList_Result{EntityNotExistError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetStickyTaskList_Result.LimitExceededError") } return &WorkflowService_ResetStickyTaskList_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetStickyTaskList_Result.ServiceBusyError") } return &WorkflowService_ResetStickyTaskList_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetStickyTaskList_Result.DomainNotActiveError") } return &WorkflowService_ResetStickyTaskList_Result{DomainNotActiveError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetStickyTaskList_Result.ClientVersionNotSupportedError") } return &WorkflowService_ResetStickyTaskList_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetStickyTaskList_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_ResetStickyTaskList_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetStickyTaskList_Result.AccessDeniedError") } return &WorkflowService_ResetStickyTaskList_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_ResetStickyTaskList_Helper.UnwrapResponse = func(result *WorkflowService_ResetStickyTaskList_Result) (success *shared.ResetStickyTaskListResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_ResetStickyTaskList_Result represents the result of a WorkflowService.ResetStickyTaskList function call. // // The result of a ResetStickyTaskList execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_ResetStickyTaskList_Result struct { // Value returned by ResetStickyTaskList after a successful execution. Success *shared.ResetStickyTaskListResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_ResetStickyTaskList_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ResetStickyTaskList_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_ResetStickyTaskList_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetStickyTaskListResponse_Read(w wire.Value) (*shared.ResetStickyTaskListResponse, error) { var v shared.ResetStickyTaskListResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ResetStickyTaskList_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ResetStickyTaskList_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ResetStickyTaskList_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ResetStickyTaskList_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ResetStickyTaskListResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ResetStickyTaskList_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_ResetStickyTaskList_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ResetStickyTaskList_Result struct could not be encoded. func (v *WorkflowService_ResetStickyTaskList_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ResetStickyTaskList_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ResetStickyTaskListResponse_Decode(sr stream.Reader) (*shared.ResetStickyTaskListResponse, error) { var v shared.ResetStickyTaskListResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ResetStickyTaskList_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ResetStickyTaskList_Result struct could not be generated from the wire // representation. func (v *WorkflowService_ResetStickyTaskList_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ResetStickyTaskListResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ResetStickyTaskList_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_ResetStickyTaskList_Result // struct. func (v *WorkflowService_ResetStickyTaskList_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_ResetStickyTaskList_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ResetStickyTaskList_Result match the // provided WorkflowService_ResetStickyTaskList_Result. // // This function performs a deep comparison. func (v *WorkflowService_ResetStickyTaskList_Result) Equals(rhs *WorkflowService_ResetStickyTaskList_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ResetStickyTaskList_Result. func (v *WorkflowService_ResetStickyTaskList_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetStickyTaskList_Result) GetSuccess() (o *shared.ResetStickyTaskListResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_ResetStickyTaskList_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetStickyTaskList_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_ResetStickyTaskList_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetStickyTaskList_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_ResetStickyTaskList_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetStickyTaskList_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_ResetStickyTaskList_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetStickyTaskList_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_ResetStickyTaskList_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetStickyTaskList_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_ResetStickyTaskList_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetStickyTaskList_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_ResetStickyTaskList_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetStickyTaskList_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_ResetStickyTaskList_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetStickyTaskList_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_ResetStickyTaskList_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ResetStickyTaskList" for this struct. func (v *WorkflowService_ResetStickyTaskList_Result) MethodName() string { return "ResetStickyTaskList" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_ResetStickyTaskList_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_ResetWorkflowExecution_Args represents the arguments for the WorkflowService.ResetWorkflowExecution function. // // The arguments for ResetWorkflowExecution are sent and received over the wire as this struct. type WorkflowService_ResetWorkflowExecution_Args struct { ResetRequest *shared.ResetWorkflowExecutionRequest `json:"resetRequest,omitempty"` } // ToWire translates a WorkflowService_ResetWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ResetWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ResetRequest != nil { w, err = v.ResetRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetWorkflowExecutionRequest_Read(w wire.Value) (*shared.ResetWorkflowExecutionRequest, error) { var v shared.ResetWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ResetWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ResetWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ResetWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ResetWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ResetRequest, err = _ResetWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_ResetWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ResetWorkflowExecution_Args struct could not be encoded. func (v *WorkflowService_ResetWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ResetRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ResetRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ResetWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.ResetWorkflowExecutionRequest, error) { var v shared.ResetWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ResetWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ResetWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *WorkflowService_ResetWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ResetRequest, err = _ResetWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_ResetWorkflowExecution_Args // struct. func (v *WorkflowService_ResetWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ResetRequest != nil { fields[i] = fmt.Sprintf("ResetRequest: %v", v.ResetRequest) i++ } return fmt.Sprintf("WorkflowService_ResetWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ResetWorkflowExecution_Args match the // provided WorkflowService_ResetWorkflowExecution_Args. // // This function performs a deep comparison. func (v *WorkflowService_ResetWorkflowExecution_Args) Equals(rhs *WorkflowService_ResetWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ResetRequest == nil && rhs.ResetRequest == nil) || (v.ResetRequest != nil && rhs.ResetRequest != nil && v.ResetRequest.Equals(rhs.ResetRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ResetWorkflowExecution_Args. func (v *WorkflowService_ResetWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ResetRequest != nil { err = multierr.Append(err, enc.AddObject("resetRequest", v.ResetRequest)) } return err } // GetResetRequest returns the value of ResetRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetWorkflowExecution_Args) GetResetRequest() (o *shared.ResetWorkflowExecutionRequest) { if v != nil && v.ResetRequest != nil { return v.ResetRequest } return } // IsSetResetRequest returns true if ResetRequest is not nil. func (v *WorkflowService_ResetWorkflowExecution_Args) IsSetResetRequest() bool { return v != nil && v.ResetRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ResetWorkflowExecution" for this struct. func (v *WorkflowService_ResetWorkflowExecution_Args) MethodName() string { return "ResetWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_ResetWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_ResetWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.ResetWorkflowExecution // function. var WorkflowService_ResetWorkflowExecution_Helper = struct { // Args accepts the parameters of ResetWorkflowExecution in-order and returns // the arguments struct for the function. Args func( resetRequest *shared.ResetWorkflowExecutionRequest, ) *WorkflowService_ResetWorkflowExecution_Args // IsException returns true if the given error can be thrown // by ResetWorkflowExecution. // // An error can be thrown by ResetWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ResetWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // ResetWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ResetWorkflowExecution // // value, err := ResetWorkflowExecution(args) // result, err := WorkflowService_ResetWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ResetWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*shared.ResetWorkflowExecutionResponse, error) (*WorkflowService_ResetWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for ResetWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if ResetWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_ResetWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_ResetWorkflowExecution_Result) (*shared.ResetWorkflowExecutionResponse, error) }{} func init() { WorkflowService_ResetWorkflowExecution_Helper.Args = func( resetRequest *shared.ResetWorkflowExecutionRequest, ) *WorkflowService_ResetWorkflowExecution_Args { return &WorkflowService_ResetWorkflowExecution_Args{ ResetRequest: resetRequest, } } WorkflowService_ResetWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_ResetWorkflowExecution_Helper.WrapResponse = func(success *shared.ResetWorkflowExecutionResponse, err error) (*WorkflowService_ResetWorkflowExecution_Result, error) { if err == nil { return &WorkflowService_ResetWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetWorkflowExecution_Result.BadRequestError") } return &WorkflowService_ResetWorkflowExecution_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetWorkflowExecution_Result.EntityNotExistError") } return &WorkflowService_ResetWorkflowExecution_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetWorkflowExecution_Result.ServiceBusyError") } return &WorkflowService_ResetWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetWorkflowExecution_Result.DomainNotActiveError") } return &WorkflowService_ResetWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetWorkflowExecution_Result.LimitExceededError") } return &WorkflowService_ResetWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetWorkflowExecution_Result.ClientVersionNotSupportedError") } return &WorkflowService_ResetWorkflowExecution_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ResetWorkflowExecution_Result.AccessDeniedError") } return &WorkflowService_ResetWorkflowExecution_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_ResetWorkflowExecution_Helper.UnwrapResponse = func(result *WorkflowService_ResetWorkflowExecution_Result) (success *shared.ResetWorkflowExecutionResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_ResetWorkflowExecution_Result represents the result of a WorkflowService.ResetWorkflowExecution function call. // // The result of a ResetWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_ResetWorkflowExecution_Result struct { // Value returned by ResetWorkflowExecution after a successful execution. Success *shared.ResetWorkflowExecutionResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_ResetWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ResetWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_ResetWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetWorkflowExecutionResponse_Read(w wire.Value) (*shared.ResetWorkflowExecutionResponse, error) { var v shared.ResetWorkflowExecutionResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_ResetWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ResetWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ResetWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ResetWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ResetWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ResetWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_ResetWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ResetWorkflowExecution_Result struct could not be encoded. func (v *WorkflowService_ResetWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ResetWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ResetWorkflowExecutionResponse_Decode(sr stream.Reader) (*shared.ResetWorkflowExecutionResponse, error) { var v shared.ResetWorkflowExecutionResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_ResetWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ResetWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *WorkflowService_ResetWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ResetWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ResetWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_ResetWorkflowExecution_Result // struct. func (v *WorkflowService_ResetWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_ResetWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ResetWorkflowExecution_Result match the // provided WorkflowService_ResetWorkflowExecution_Result. // // This function performs a deep comparison. func (v *WorkflowService_ResetWorkflowExecution_Result) Equals(rhs *WorkflowService_ResetWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ResetWorkflowExecution_Result. func (v *WorkflowService_ResetWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetWorkflowExecution_Result) GetSuccess() (o *shared.ResetWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_ResetWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_ResetWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_ResetWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_ResetWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_ResetWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_ResetWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetWorkflowExecution_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_ResetWorkflowExecution_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ResetWorkflowExecution_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_ResetWorkflowExecution_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ResetWorkflowExecution" for this struct. func (v *WorkflowService_ResetWorkflowExecution_Result) MethodName() string { return "ResetWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_ResetWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RespondActivityTaskCanceled_Args represents the arguments for the WorkflowService.RespondActivityTaskCanceled function. // // The arguments for RespondActivityTaskCanceled are sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskCanceled_Args struct { CanceledRequest *shared.RespondActivityTaskCanceledRequest `json:"canceledRequest,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskCanceled_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskCanceled_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CanceledRequest != nil { w, err = v.CanceledRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskCanceledRequest_Read(w wire.Value) (*shared.RespondActivityTaskCanceledRequest, error) { var v shared.RespondActivityTaskCanceledRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RespondActivityTaskCanceled_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskCanceled_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskCanceled_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskCanceled_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CanceledRequest, err = _RespondActivityTaskCanceledRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RespondActivityTaskCanceled_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCanceled_Args struct could not be encoded. func (v *WorkflowService_RespondActivityTaskCanceled_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CanceledRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CanceledRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskCanceledRequest_Decode(sr stream.Reader) (*shared.RespondActivityTaskCanceledRequest, error) { var v shared.RespondActivityTaskCanceledRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RespondActivityTaskCanceled_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCanceled_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskCanceled_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CanceledRequest, err = _RespondActivityTaskCanceledRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskCanceled_Args // struct. func (v *WorkflowService_RespondActivityTaskCanceled_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CanceledRequest != nil { fields[i] = fmt.Sprintf("CanceledRequest: %v", v.CanceledRequest) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskCanceled_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskCanceled_Args match the // provided WorkflowService_RespondActivityTaskCanceled_Args. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskCanceled_Args) Equals(rhs *WorkflowService_RespondActivityTaskCanceled_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CanceledRequest == nil && rhs.CanceledRequest == nil) || (v.CanceledRequest != nil && rhs.CanceledRequest != nil && v.CanceledRequest.Equals(rhs.CanceledRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskCanceled_Args. func (v *WorkflowService_RespondActivityTaskCanceled_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CanceledRequest != nil { err = multierr.Append(err, enc.AddObject("canceledRequest", v.CanceledRequest)) } return err } // GetCanceledRequest returns the value of CanceledRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceled_Args) GetCanceledRequest() (o *shared.RespondActivityTaskCanceledRequest) { if v != nil && v.CanceledRequest != nil { return v.CanceledRequest } return } // IsSetCanceledRequest returns true if CanceledRequest is not nil. func (v *WorkflowService_RespondActivityTaskCanceled_Args) IsSetCanceledRequest() bool { return v != nil && v.CanceledRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondActivityTaskCanceled" for this struct. func (v *WorkflowService_RespondActivityTaskCanceled_Args) MethodName() string { return "RespondActivityTaskCanceled" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RespondActivityTaskCanceled_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RespondActivityTaskCanceled_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RespondActivityTaskCanceled // function. var WorkflowService_RespondActivityTaskCanceled_Helper = struct { // Args accepts the parameters of RespondActivityTaskCanceled in-order and returns // the arguments struct for the function. Args func( canceledRequest *shared.RespondActivityTaskCanceledRequest, ) *WorkflowService_RespondActivityTaskCanceled_Args // IsException returns true if the given error can be thrown // by RespondActivityTaskCanceled. // // An error can be thrown by RespondActivityTaskCanceled only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondActivityTaskCanceled // given the error returned by it. The provided error may // be nil if RespondActivityTaskCanceled did not fail. // // This allows mapping errors returned by RespondActivityTaskCanceled into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondActivityTaskCanceled // // err := RespondActivityTaskCanceled(args) // result, err := WorkflowService_RespondActivityTaskCanceled_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondActivityTaskCanceled: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RespondActivityTaskCanceled_Result, error) // UnwrapResponse takes the result struct for RespondActivityTaskCanceled // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondActivityTaskCanceled threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RespondActivityTaskCanceled_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RespondActivityTaskCanceled_Result) error }{} func init() { WorkflowService_RespondActivityTaskCanceled_Helper.Args = func( canceledRequest *shared.RespondActivityTaskCanceledRequest, ) *WorkflowService_RespondActivityTaskCanceled_Args { return &WorkflowService_RespondActivityTaskCanceled_Args{ CanceledRequest: canceledRequest, } } WorkflowService_RespondActivityTaskCanceled_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RespondActivityTaskCanceled_Helper.WrapResponse = func(err error) (*WorkflowService_RespondActivityTaskCanceled_Result, error) { if err == nil { return &WorkflowService_RespondActivityTaskCanceled_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceled_Result.BadRequestError") } return &WorkflowService_RespondActivityTaskCanceled_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceled_Result.EntityNotExistError") } return &WorkflowService_RespondActivityTaskCanceled_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceled_Result.DomainNotActiveError") } return &WorkflowService_RespondActivityTaskCanceled_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceled_Result.LimitExceededError") } return &WorkflowService_RespondActivityTaskCanceled_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceled_Result.ServiceBusyError") } return &WorkflowService_RespondActivityTaskCanceled_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceled_Result.ClientVersionNotSupportedError") } return &WorkflowService_RespondActivityTaskCanceled_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceled_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RespondActivityTaskCanceled_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceled_Result.AccessDeniedError") } return &WorkflowService_RespondActivityTaskCanceled_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RespondActivityTaskCanceled_Helper.UnwrapResponse = func(result *WorkflowService_RespondActivityTaskCanceled_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RespondActivityTaskCanceled_Result represents the result of a WorkflowService.RespondActivityTaskCanceled function call. // // The result of a RespondActivityTaskCanceled execution is sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskCanceled_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskCanceled_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskCanceled_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RespondActivityTaskCanceled_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_RespondActivityTaskCanceled_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskCanceled_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskCanceled_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskCanceled_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCanceled_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RespondActivityTaskCanceled_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCanceled_Result struct could not be encoded. func (v *WorkflowService_RespondActivityTaskCanceled_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCanceled_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_RespondActivityTaskCanceled_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCanceled_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskCanceled_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCanceled_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskCanceled_Result // struct. func (v *WorkflowService_RespondActivityTaskCanceled_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskCanceled_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskCanceled_Result match the // provided WorkflowService_RespondActivityTaskCanceled_Result. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskCanceled_Result) Equals(rhs *WorkflowService_RespondActivityTaskCanceled_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskCanceled_Result. func (v *WorkflowService_RespondActivityTaskCanceled_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceled_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RespondActivityTaskCanceled_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceled_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RespondActivityTaskCanceled_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceled_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RespondActivityTaskCanceled_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceled_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RespondActivityTaskCanceled_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceled_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RespondActivityTaskCanceled_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceled_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RespondActivityTaskCanceled_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceled_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RespondActivityTaskCanceled_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceled_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RespondActivityTaskCanceled_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondActivityTaskCanceled" for this struct. func (v *WorkflowService_RespondActivityTaskCanceled_Result) MethodName() string { return "RespondActivityTaskCanceled" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RespondActivityTaskCanceled_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RespondActivityTaskCanceledByID_Args represents the arguments for the WorkflowService.RespondActivityTaskCanceledByID function. // // The arguments for RespondActivityTaskCanceledByID are sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskCanceledByID_Args struct { CanceledRequest *shared.RespondActivityTaskCanceledByIDRequest `json:"canceledRequest,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskCanceledByID_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CanceledRequest != nil { w, err = v.CanceledRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskCanceledByIDRequest_Read(w wire.Value) (*shared.RespondActivityTaskCanceledByIDRequest, error) { var v shared.RespondActivityTaskCanceledByIDRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RespondActivityTaskCanceledByID_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskCanceledByID_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskCanceledByID_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CanceledRequest, err = _RespondActivityTaskCanceledByIDRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RespondActivityTaskCanceledByID_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCanceledByID_Args struct could not be encoded. func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CanceledRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CanceledRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskCanceledByIDRequest_Decode(sr stream.Reader) (*shared.RespondActivityTaskCanceledByIDRequest, error) { var v shared.RespondActivityTaskCanceledByIDRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RespondActivityTaskCanceledByID_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCanceledByID_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CanceledRequest, err = _RespondActivityTaskCanceledByIDRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskCanceledByID_Args // struct. func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CanceledRequest != nil { fields[i] = fmt.Sprintf("CanceledRequest: %v", v.CanceledRequest) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskCanceledByID_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskCanceledByID_Args match the // provided WorkflowService_RespondActivityTaskCanceledByID_Args. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) Equals(rhs *WorkflowService_RespondActivityTaskCanceledByID_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CanceledRequest == nil && rhs.CanceledRequest == nil) || (v.CanceledRequest != nil && rhs.CanceledRequest != nil && v.CanceledRequest.Equals(rhs.CanceledRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskCanceledByID_Args. func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CanceledRequest != nil { err = multierr.Append(err, enc.AddObject("canceledRequest", v.CanceledRequest)) } return err } // GetCanceledRequest returns the value of CanceledRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) GetCanceledRequest() (o *shared.RespondActivityTaskCanceledByIDRequest) { if v != nil && v.CanceledRequest != nil { return v.CanceledRequest } return } // IsSetCanceledRequest returns true if CanceledRequest is not nil. func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) IsSetCanceledRequest() bool { return v != nil && v.CanceledRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondActivityTaskCanceledByID" for this struct. func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) MethodName() string { return "RespondActivityTaskCanceledByID" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RespondActivityTaskCanceledByID_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RespondActivityTaskCanceledByID_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RespondActivityTaskCanceledByID // function. var WorkflowService_RespondActivityTaskCanceledByID_Helper = struct { // Args accepts the parameters of RespondActivityTaskCanceledByID in-order and returns // the arguments struct for the function. Args func( canceledRequest *shared.RespondActivityTaskCanceledByIDRequest, ) *WorkflowService_RespondActivityTaskCanceledByID_Args // IsException returns true if the given error can be thrown // by RespondActivityTaskCanceledByID. // // An error can be thrown by RespondActivityTaskCanceledByID only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondActivityTaskCanceledByID // given the error returned by it. The provided error may // be nil if RespondActivityTaskCanceledByID did not fail. // // This allows mapping errors returned by RespondActivityTaskCanceledByID into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondActivityTaskCanceledByID // // err := RespondActivityTaskCanceledByID(args) // result, err := WorkflowService_RespondActivityTaskCanceledByID_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondActivityTaskCanceledByID: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RespondActivityTaskCanceledByID_Result, error) // UnwrapResponse takes the result struct for RespondActivityTaskCanceledByID // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondActivityTaskCanceledByID threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RespondActivityTaskCanceledByID_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RespondActivityTaskCanceledByID_Result) error }{} func init() { WorkflowService_RespondActivityTaskCanceledByID_Helper.Args = func( canceledRequest *shared.RespondActivityTaskCanceledByIDRequest, ) *WorkflowService_RespondActivityTaskCanceledByID_Args { return &WorkflowService_RespondActivityTaskCanceledByID_Args{ CanceledRequest: canceledRequest, } } WorkflowService_RespondActivityTaskCanceledByID_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RespondActivityTaskCanceledByID_Helper.WrapResponse = func(err error) (*WorkflowService_RespondActivityTaskCanceledByID_Result, error) { if err == nil { return &WorkflowService_RespondActivityTaskCanceledByID_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceledByID_Result.BadRequestError") } return &WorkflowService_RespondActivityTaskCanceledByID_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceledByID_Result.EntityNotExistError") } return &WorkflowService_RespondActivityTaskCanceledByID_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceledByID_Result.DomainNotActiveError") } return &WorkflowService_RespondActivityTaskCanceledByID_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceledByID_Result.LimitExceededError") } return &WorkflowService_RespondActivityTaskCanceledByID_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceledByID_Result.ServiceBusyError") } return &WorkflowService_RespondActivityTaskCanceledByID_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceledByID_Result.ClientVersionNotSupportedError") } return &WorkflowService_RespondActivityTaskCanceledByID_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceledByID_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RespondActivityTaskCanceledByID_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCanceledByID_Result.AccessDeniedError") } return &WorkflowService_RespondActivityTaskCanceledByID_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RespondActivityTaskCanceledByID_Helper.UnwrapResponse = func(result *WorkflowService_RespondActivityTaskCanceledByID_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RespondActivityTaskCanceledByID_Result represents the result of a WorkflowService.RespondActivityTaskCanceledByID function call. // // The result of a RespondActivityTaskCanceledByID execution is sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskCanceledByID_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskCanceledByID_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RespondActivityTaskCanceledByID_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_RespondActivityTaskCanceledByID_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskCanceledByID_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskCanceledByID_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCanceledByID_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RespondActivityTaskCanceledByID_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCanceledByID_Result struct could not be encoded. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCanceledByID_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_RespondActivityTaskCanceledByID_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCanceledByID_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCanceledByID_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskCanceledByID_Result // struct. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskCanceledByID_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskCanceledByID_Result match the // provided WorkflowService_RespondActivityTaskCanceledByID_Result. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) Equals(rhs *WorkflowService_RespondActivityTaskCanceledByID_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskCanceledByID_Result. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondActivityTaskCanceledByID" for this struct. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) MethodName() string { return "RespondActivityTaskCanceledByID" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RespondActivityTaskCanceledByID_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RespondActivityTaskCompleted_Args represents the arguments for the WorkflowService.RespondActivityTaskCompleted function. // // The arguments for RespondActivityTaskCompleted are sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskCompleted_Args struct { CompleteRequest *shared.RespondActivityTaskCompletedRequest `json:"completeRequest,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskCompleted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskCompleted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CompleteRequest != nil { w, err = v.CompleteRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskCompletedRequest_Read(w wire.Value) (*shared.RespondActivityTaskCompletedRequest, error) { var v shared.RespondActivityTaskCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RespondActivityTaskCompleted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskCompleted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskCompleted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskCompleted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CompleteRequest, err = _RespondActivityTaskCompletedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RespondActivityTaskCompleted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCompleted_Args struct could not be encoded. func (v *WorkflowService_RespondActivityTaskCompleted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CompleteRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CompleteRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskCompletedRequest_Decode(sr stream.Reader) (*shared.RespondActivityTaskCompletedRequest, error) { var v shared.RespondActivityTaskCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RespondActivityTaskCompleted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCompleted_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskCompleted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CompleteRequest, err = _RespondActivityTaskCompletedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskCompleted_Args // struct. func (v *WorkflowService_RespondActivityTaskCompleted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CompleteRequest != nil { fields[i] = fmt.Sprintf("CompleteRequest: %v", v.CompleteRequest) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskCompleted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskCompleted_Args match the // provided WorkflowService_RespondActivityTaskCompleted_Args. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskCompleted_Args) Equals(rhs *WorkflowService_RespondActivityTaskCompleted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CompleteRequest == nil && rhs.CompleteRequest == nil) || (v.CompleteRequest != nil && rhs.CompleteRequest != nil && v.CompleteRequest.Equals(rhs.CompleteRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskCompleted_Args. func (v *WorkflowService_RespondActivityTaskCompleted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CompleteRequest != nil { err = multierr.Append(err, enc.AddObject("completeRequest", v.CompleteRequest)) } return err } // GetCompleteRequest returns the value of CompleteRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompleted_Args) GetCompleteRequest() (o *shared.RespondActivityTaskCompletedRequest) { if v != nil && v.CompleteRequest != nil { return v.CompleteRequest } return } // IsSetCompleteRequest returns true if CompleteRequest is not nil. func (v *WorkflowService_RespondActivityTaskCompleted_Args) IsSetCompleteRequest() bool { return v != nil && v.CompleteRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondActivityTaskCompleted" for this struct. func (v *WorkflowService_RespondActivityTaskCompleted_Args) MethodName() string { return "RespondActivityTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RespondActivityTaskCompleted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RespondActivityTaskCompleted_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RespondActivityTaskCompleted // function. var WorkflowService_RespondActivityTaskCompleted_Helper = struct { // Args accepts the parameters of RespondActivityTaskCompleted in-order and returns // the arguments struct for the function. Args func( completeRequest *shared.RespondActivityTaskCompletedRequest, ) *WorkflowService_RespondActivityTaskCompleted_Args // IsException returns true if the given error can be thrown // by RespondActivityTaskCompleted. // // An error can be thrown by RespondActivityTaskCompleted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondActivityTaskCompleted // given the error returned by it. The provided error may // be nil if RespondActivityTaskCompleted did not fail. // // This allows mapping errors returned by RespondActivityTaskCompleted into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondActivityTaskCompleted // // err := RespondActivityTaskCompleted(args) // result, err := WorkflowService_RespondActivityTaskCompleted_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondActivityTaskCompleted: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RespondActivityTaskCompleted_Result, error) // UnwrapResponse takes the result struct for RespondActivityTaskCompleted // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondActivityTaskCompleted threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RespondActivityTaskCompleted_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RespondActivityTaskCompleted_Result) error }{} func init() { WorkflowService_RespondActivityTaskCompleted_Helper.Args = func( completeRequest *shared.RespondActivityTaskCompletedRequest, ) *WorkflowService_RespondActivityTaskCompleted_Args { return &WorkflowService_RespondActivityTaskCompleted_Args{ CompleteRequest: completeRequest, } } WorkflowService_RespondActivityTaskCompleted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RespondActivityTaskCompleted_Helper.WrapResponse = func(err error) (*WorkflowService_RespondActivityTaskCompleted_Result, error) { if err == nil { return &WorkflowService_RespondActivityTaskCompleted_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompleted_Result.BadRequestError") } return &WorkflowService_RespondActivityTaskCompleted_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompleted_Result.EntityNotExistError") } return &WorkflowService_RespondActivityTaskCompleted_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompleted_Result.DomainNotActiveError") } return &WorkflowService_RespondActivityTaskCompleted_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompleted_Result.LimitExceededError") } return &WorkflowService_RespondActivityTaskCompleted_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompleted_Result.ServiceBusyError") } return &WorkflowService_RespondActivityTaskCompleted_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompleted_Result.ClientVersionNotSupportedError") } return &WorkflowService_RespondActivityTaskCompleted_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompleted_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RespondActivityTaskCompleted_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompleted_Result.AccessDeniedError") } return &WorkflowService_RespondActivityTaskCompleted_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RespondActivityTaskCompleted_Helper.UnwrapResponse = func(result *WorkflowService_RespondActivityTaskCompleted_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RespondActivityTaskCompleted_Result represents the result of a WorkflowService.RespondActivityTaskCompleted function call. // // The result of a RespondActivityTaskCompleted execution is sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskCompleted_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskCompleted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskCompleted_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RespondActivityTaskCompleted_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_RespondActivityTaskCompleted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskCompleted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskCompleted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskCompleted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCompleted_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RespondActivityTaskCompleted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCompleted_Result struct could not be encoded. func (v *WorkflowService_RespondActivityTaskCompleted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCompleted_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_RespondActivityTaskCompleted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCompleted_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskCompleted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCompleted_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskCompleted_Result // struct. func (v *WorkflowService_RespondActivityTaskCompleted_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskCompleted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskCompleted_Result match the // provided WorkflowService_RespondActivityTaskCompleted_Result. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskCompleted_Result) Equals(rhs *WorkflowService_RespondActivityTaskCompleted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskCompleted_Result. func (v *WorkflowService_RespondActivityTaskCompleted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompleted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RespondActivityTaskCompleted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompleted_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RespondActivityTaskCompleted_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompleted_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RespondActivityTaskCompleted_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompleted_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RespondActivityTaskCompleted_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompleted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RespondActivityTaskCompleted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompleted_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RespondActivityTaskCompleted_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompleted_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RespondActivityTaskCompleted_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompleted_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RespondActivityTaskCompleted_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondActivityTaskCompleted" for this struct. func (v *WorkflowService_RespondActivityTaskCompleted_Result) MethodName() string { return "RespondActivityTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RespondActivityTaskCompleted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RespondActivityTaskCompletedByID_Args represents the arguments for the WorkflowService.RespondActivityTaskCompletedByID function. // // The arguments for RespondActivityTaskCompletedByID are sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskCompletedByID_Args struct { CompleteRequest *shared.RespondActivityTaskCompletedByIDRequest `json:"completeRequest,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskCompletedByID_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CompleteRequest != nil { w, err = v.CompleteRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskCompletedByIDRequest_Read(w wire.Value) (*shared.RespondActivityTaskCompletedByIDRequest, error) { var v shared.RespondActivityTaskCompletedByIDRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RespondActivityTaskCompletedByID_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskCompletedByID_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskCompletedByID_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CompleteRequest, err = _RespondActivityTaskCompletedByIDRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RespondActivityTaskCompletedByID_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCompletedByID_Args struct could not be encoded. func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CompleteRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CompleteRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskCompletedByIDRequest_Decode(sr stream.Reader) (*shared.RespondActivityTaskCompletedByIDRequest, error) { var v shared.RespondActivityTaskCompletedByIDRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RespondActivityTaskCompletedByID_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCompletedByID_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CompleteRequest, err = _RespondActivityTaskCompletedByIDRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskCompletedByID_Args // struct. func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CompleteRequest != nil { fields[i] = fmt.Sprintf("CompleteRequest: %v", v.CompleteRequest) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskCompletedByID_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskCompletedByID_Args match the // provided WorkflowService_RespondActivityTaskCompletedByID_Args. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) Equals(rhs *WorkflowService_RespondActivityTaskCompletedByID_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CompleteRequest == nil && rhs.CompleteRequest == nil) || (v.CompleteRequest != nil && rhs.CompleteRequest != nil && v.CompleteRequest.Equals(rhs.CompleteRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskCompletedByID_Args. func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CompleteRequest != nil { err = multierr.Append(err, enc.AddObject("completeRequest", v.CompleteRequest)) } return err } // GetCompleteRequest returns the value of CompleteRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) GetCompleteRequest() (o *shared.RespondActivityTaskCompletedByIDRequest) { if v != nil && v.CompleteRequest != nil { return v.CompleteRequest } return } // IsSetCompleteRequest returns true if CompleteRequest is not nil. func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) IsSetCompleteRequest() bool { return v != nil && v.CompleteRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondActivityTaskCompletedByID" for this struct. func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) MethodName() string { return "RespondActivityTaskCompletedByID" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RespondActivityTaskCompletedByID_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RespondActivityTaskCompletedByID_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RespondActivityTaskCompletedByID // function. var WorkflowService_RespondActivityTaskCompletedByID_Helper = struct { // Args accepts the parameters of RespondActivityTaskCompletedByID in-order and returns // the arguments struct for the function. Args func( completeRequest *shared.RespondActivityTaskCompletedByIDRequest, ) *WorkflowService_RespondActivityTaskCompletedByID_Args // IsException returns true if the given error can be thrown // by RespondActivityTaskCompletedByID. // // An error can be thrown by RespondActivityTaskCompletedByID only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondActivityTaskCompletedByID // given the error returned by it. The provided error may // be nil if RespondActivityTaskCompletedByID did not fail. // // This allows mapping errors returned by RespondActivityTaskCompletedByID into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondActivityTaskCompletedByID // // err := RespondActivityTaskCompletedByID(args) // result, err := WorkflowService_RespondActivityTaskCompletedByID_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondActivityTaskCompletedByID: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RespondActivityTaskCompletedByID_Result, error) // UnwrapResponse takes the result struct for RespondActivityTaskCompletedByID // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondActivityTaskCompletedByID threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RespondActivityTaskCompletedByID_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RespondActivityTaskCompletedByID_Result) error }{} func init() { WorkflowService_RespondActivityTaskCompletedByID_Helper.Args = func( completeRequest *shared.RespondActivityTaskCompletedByIDRequest, ) *WorkflowService_RespondActivityTaskCompletedByID_Args { return &WorkflowService_RespondActivityTaskCompletedByID_Args{ CompleteRequest: completeRequest, } } WorkflowService_RespondActivityTaskCompletedByID_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RespondActivityTaskCompletedByID_Helper.WrapResponse = func(err error) (*WorkflowService_RespondActivityTaskCompletedByID_Result, error) { if err == nil { return &WorkflowService_RespondActivityTaskCompletedByID_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompletedByID_Result.BadRequestError") } return &WorkflowService_RespondActivityTaskCompletedByID_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompletedByID_Result.EntityNotExistError") } return &WorkflowService_RespondActivityTaskCompletedByID_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompletedByID_Result.DomainNotActiveError") } return &WorkflowService_RespondActivityTaskCompletedByID_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompletedByID_Result.LimitExceededError") } return &WorkflowService_RespondActivityTaskCompletedByID_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompletedByID_Result.ServiceBusyError") } return &WorkflowService_RespondActivityTaskCompletedByID_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompletedByID_Result.ClientVersionNotSupportedError") } return &WorkflowService_RespondActivityTaskCompletedByID_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompletedByID_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RespondActivityTaskCompletedByID_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskCompletedByID_Result.AccessDeniedError") } return &WorkflowService_RespondActivityTaskCompletedByID_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RespondActivityTaskCompletedByID_Helper.UnwrapResponse = func(result *WorkflowService_RespondActivityTaskCompletedByID_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RespondActivityTaskCompletedByID_Result represents the result of a WorkflowService.RespondActivityTaskCompletedByID function call. // // The result of a RespondActivityTaskCompletedByID execution is sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskCompletedByID_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskCompletedByID_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RespondActivityTaskCompletedByID_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_RespondActivityTaskCompletedByID_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskCompletedByID_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskCompletedByID_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCompletedByID_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RespondActivityTaskCompletedByID_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCompletedByID_Result struct could not be encoded. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCompletedByID_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_RespondActivityTaskCompletedByID_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskCompletedByID_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskCompletedByID_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskCompletedByID_Result // struct. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskCompletedByID_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskCompletedByID_Result match the // provided WorkflowService_RespondActivityTaskCompletedByID_Result. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) Equals(rhs *WorkflowService_RespondActivityTaskCompletedByID_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskCompletedByID_Result. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondActivityTaskCompletedByID" for this struct. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) MethodName() string { return "RespondActivityTaskCompletedByID" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RespondActivityTaskCompletedByID_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RespondActivityTaskFailed_Args represents the arguments for the WorkflowService.RespondActivityTaskFailed function. // // The arguments for RespondActivityTaskFailed are sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskFailed_Args struct { FailRequest *shared.RespondActivityTaskFailedRequest `json:"failRequest,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskFailed_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskFailed_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.FailRequest != nil { w, err = v.FailRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskFailedRequest_Read(w wire.Value) (*shared.RespondActivityTaskFailedRequest, error) { var v shared.RespondActivityTaskFailedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RespondActivityTaskFailed_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskFailed_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskFailed_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskFailed_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.FailRequest, err = _RespondActivityTaskFailedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RespondActivityTaskFailed_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskFailed_Args struct could not be encoded. func (v *WorkflowService_RespondActivityTaskFailed_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FailRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.FailRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskFailedRequest_Decode(sr stream.Reader) (*shared.RespondActivityTaskFailedRequest, error) { var v shared.RespondActivityTaskFailedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RespondActivityTaskFailed_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskFailed_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskFailed_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.FailRequest, err = _RespondActivityTaskFailedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskFailed_Args // struct. func (v *WorkflowService_RespondActivityTaskFailed_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.FailRequest != nil { fields[i] = fmt.Sprintf("FailRequest: %v", v.FailRequest) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskFailed_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskFailed_Args match the // provided WorkflowService_RespondActivityTaskFailed_Args. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskFailed_Args) Equals(rhs *WorkflowService_RespondActivityTaskFailed_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.FailRequest == nil && rhs.FailRequest == nil) || (v.FailRequest != nil && rhs.FailRequest != nil && v.FailRequest.Equals(rhs.FailRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskFailed_Args. func (v *WorkflowService_RespondActivityTaskFailed_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FailRequest != nil { err = multierr.Append(err, enc.AddObject("failRequest", v.FailRequest)) } return err } // GetFailRequest returns the value of FailRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailed_Args) GetFailRequest() (o *shared.RespondActivityTaskFailedRequest) { if v != nil && v.FailRequest != nil { return v.FailRequest } return } // IsSetFailRequest returns true if FailRequest is not nil. func (v *WorkflowService_RespondActivityTaskFailed_Args) IsSetFailRequest() bool { return v != nil && v.FailRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondActivityTaskFailed" for this struct. func (v *WorkflowService_RespondActivityTaskFailed_Args) MethodName() string { return "RespondActivityTaskFailed" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RespondActivityTaskFailed_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RespondActivityTaskFailed_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RespondActivityTaskFailed // function. var WorkflowService_RespondActivityTaskFailed_Helper = struct { // Args accepts the parameters of RespondActivityTaskFailed in-order and returns // the arguments struct for the function. Args func( failRequest *shared.RespondActivityTaskFailedRequest, ) *WorkflowService_RespondActivityTaskFailed_Args // IsException returns true if the given error can be thrown // by RespondActivityTaskFailed. // // An error can be thrown by RespondActivityTaskFailed only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondActivityTaskFailed // given the error returned by it. The provided error may // be nil if RespondActivityTaskFailed did not fail. // // This allows mapping errors returned by RespondActivityTaskFailed into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondActivityTaskFailed // // err := RespondActivityTaskFailed(args) // result, err := WorkflowService_RespondActivityTaskFailed_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondActivityTaskFailed: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RespondActivityTaskFailed_Result, error) // UnwrapResponse takes the result struct for RespondActivityTaskFailed // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondActivityTaskFailed threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RespondActivityTaskFailed_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RespondActivityTaskFailed_Result) error }{} func init() { WorkflowService_RespondActivityTaskFailed_Helper.Args = func( failRequest *shared.RespondActivityTaskFailedRequest, ) *WorkflowService_RespondActivityTaskFailed_Args { return &WorkflowService_RespondActivityTaskFailed_Args{ FailRequest: failRequest, } } WorkflowService_RespondActivityTaskFailed_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RespondActivityTaskFailed_Helper.WrapResponse = func(err error) (*WorkflowService_RespondActivityTaskFailed_Result, error) { if err == nil { return &WorkflowService_RespondActivityTaskFailed_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailed_Result.BadRequestError") } return &WorkflowService_RespondActivityTaskFailed_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailed_Result.EntityNotExistError") } return &WorkflowService_RespondActivityTaskFailed_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailed_Result.DomainNotActiveError") } return &WorkflowService_RespondActivityTaskFailed_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailed_Result.LimitExceededError") } return &WorkflowService_RespondActivityTaskFailed_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailed_Result.ServiceBusyError") } return &WorkflowService_RespondActivityTaskFailed_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailed_Result.ClientVersionNotSupportedError") } return &WorkflowService_RespondActivityTaskFailed_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailed_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RespondActivityTaskFailed_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailed_Result.AccessDeniedError") } return &WorkflowService_RespondActivityTaskFailed_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RespondActivityTaskFailed_Helper.UnwrapResponse = func(result *WorkflowService_RespondActivityTaskFailed_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RespondActivityTaskFailed_Result represents the result of a WorkflowService.RespondActivityTaskFailed function call. // // The result of a RespondActivityTaskFailed execution is sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskFailed_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskFailed_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskFailed_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RespondActivityTaskFailed_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_RespondActivityTaskFailed_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskFailed_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskFailed_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskFailed_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskFailed_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RespondActivityTaskFailed_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskFailed_Result struct could not be encoded. func (v *WorkflowService_RespondActivityTaskFailed_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskFailed_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_RespondActivityTaskFailed_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskFailed_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskFailed_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskFailed_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskFailed_Result // struct. func (v *WorkflowService_RespondActivityTaskFailed_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskFailed_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskFailed_Result match the // provided WorkflowService_RespondActivityTaskFailed_Result. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskFailed_Result) Equals(rhs *WorkflowService_RespondActivityTaskFailed_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskFailed_Result. func (v *WorkflowService_RespondActivityTaskFailed_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailed_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RespondActivityTaskFailed_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailed_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RespondActivityTaskFailed_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailed_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RespondActivityTaskFailed_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailed_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RespondActivityTaskFailed_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailed_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RespondActivityTaskFailed_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailed_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RespondActivityTaskFailed_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailed_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RespondActivityTaskFailed_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailed_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RespondActivityTaskFailed_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondActivityTaskFailed" for this struct. func (v *WorkflowService_RespondActivityTaskFailed_Result) MethodName() string { return "RespondActivityTaskFailed" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RespondActivityTaskFailed_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RespondActivityTaskFailedByID_Args represents the arguments for the WorkflowService.RespondActivityTaskFailedByID function. // // The arguments for RespondActivityTaskFailedByID are sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskFailedByID_Args struct { FailRequest *shared.RespondActivityTaskFailedByIDRequest `json:"failRequest,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskFailedByID_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskFailedByID_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.FailRequest != nil { w, err = v.FailRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskFailedByIDRequest_Read(w wire.Value) (*shared.RespondActivityTaskFailedByIDRequest, error) { var v shared.RespondActivityTaskFailedByIDRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RespondActivityTaskFailedByID_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskFailedByID_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskFailedByID_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskFailedByID_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.FailRequest, err = _RespondActivityTaskFailedByIDRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RespondActivityTaskFailedByID_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskFailedByID_Args struct could not be encoded. func (v *WorkflowService_RespondActivityTaskFailedByID_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FailRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.FailRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskFailedByIDRequest_Decode(sr stream.Reader) (*shared.RespondActivityTaskFailedByIDRequest, error) { var v shared.RespondActivityTaskFailedByIDRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RespondActivityTaskFailedByID_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskFailedByID_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskFailedByID_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.FailRequest, err = _RespondActivityTaskFailedByIDRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskFailedByID_Args // struct. func (v *WorkflowService_RespondActivityTaskFailedByID_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.FailRequest != nil { fields[i] = fmt.Sprintf("FailRequest: %v", v.FailRequest) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskFailedByID_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskFailedByID_Args match the // provided WorkflowService_RespondActivityTaskFailedByID_Args. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskFailedByID_Args) Equals(rhs *WorkflowService_RespondActivityTaskFailedByID_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.FailRequest == nil && rhs.FailRequest == nil) || (v.FailRequest != nil && rhs.FailRequest != nil && v.FailRequest.Equals(rhs.FailRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskFailedByID_Args. func (v *WorkflowService_RespondActivityTaskFailedByID_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FailRequest != nil { err = multierr.Append(err, enc.AddObject("failRequest", v.FailRequest)) } return err } // GetFailRequest returns the value of FailRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailedByID_Args) GetFailRequest() (o *shared.RespondActivityTaskFailedByIDRequest) { if v != nil && v.FailRequest != nil { return v.FailRequest } return } // IsSetFailRequest returns true if FailRequest is not nil. func (v *WorkflowService_RespondActivityTaskFailedByID_Args) IsSetFailRequest() bool { return v != nil && v.FailRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondActivityTaskFailedByID" for this struct. func (v *WorkflowService_RespondActivityTaskFailedByID_Args) MethodName() string { return "RespondActivityTaskFailedByID" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RespondActivityTaskFailedByID_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RespondActivityTaskFailedByID_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RespondActivityTaskFailedByID // function. var WorkflowService_RespondActivityTaskFailedByID_Helper = struct { // Args accepts the parameters of RespondActivityTaskFailedByID in-order and returns // the arguments struct for the function. Args func( failRequest *shared.RespondActivityTaskFailedByIDRequest, ) *WorkflowService_RespondActivityTaskFailedByID_Args // IsException returns true if the given error can be thrown // by RespondActivityTaskFailedByID. // // An error can be thrown by RespondActivityTaskFailedByID only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondActivityTaskFailedByID // given the error returned by it. The provided error may // be nil if RespondActivityTaskFailedByID did not fail. // // This allows mapping errors returned by RespondActivityTaskFailedByID into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondActivityTaskFailedByID // // err := RespondActivityTaskFailedByID(args) // result, err := WorkflowService_RespondActivityTaskFailedByID_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondActivityTaskFailedByID: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RespondActivityTaskFailedByID_Result, error) // UnwrapResponse takes the result struct for RespondActivityTaskFailedByID // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondActivityTaskFailedByID threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RespondActivityTaskFailedByID_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RespondActivityTaskFailedByID_Result) error }{} func init() { WorkflowService_RespondActivityTaskFailedByID_Helper.Args = func( failRequest *shared.RespondActivityTaskFailedByIDRequest, ) *WorkflowService_RespondActivityTaskFailedByID_Args { return &WorkflowService_RespondActivityTaskFailedByID_Args{ FailRequest: failRequest, } } WorkflowService_RespondActivityTaskFailedByID_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RespondActivityTaskFailedByID_Helper.WrapResponse = func(err error) (*WorkflowService_RespondActivityTaskFailedByID_Result, error) { if err == nil { return &WorkflowService_RespondActivityTaskFailedByID_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailedByID_Result.BadRequestError") } return &WorkflowService_RespondActivityTaskFailedByID_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailedByID_Result.EntityNotExistError") } return &WorkflowService_RespondActivityTaskFailedByID_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailedByID_Result.DomainNotActiveError") } return &WorkflowService_RespondActivityTaskFailedByID_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailedByID_Result.LimitExceededError") } return &WorkflowService_RespondActivityTaskFailedByID_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailedByID_Result.ServiceBusyError") } return &WorkflowService_RespondActivityTaskFailedByID_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailedByID_Result.ClientVersionNotSupportedError") } return &WorkflowService_RespondActivityTaskFailedByID_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailedByID_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RespondActivityTaskFailedByID_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondActivityTaskFailedByID_Result.AccessDeniedError") } return &WorkflowService_RespondActivityTaskFailedByID_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RespondActivityTaskFailedByID_Helper.UnwrapResponse = func(result *WorkflowService_RespondActivityTaskFailedByID_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RespondActivityTaskFailedByID_Result represents the result of a WorkflowService.RespondActivityTaskFailedByID function call. // // The result of a RespondActivityTaskFailedByID execution is sent and received over the wire as this struct. type WorkflowService_RespondActivityTaskFailedByID_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RespondActivityTaskFailedByID_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondActivityTaskFailedByID_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RespondActivityTaskFailedByID_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_RespondActivityTaskFailedByID_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondActivityTaskFailedByID_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondActivityTaskFailedByID_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondActivityTaskFailedByID_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskFailedByID_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RespondActivityTaskFailedByID_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondActivityTaskFailedByID_Result struct could not be encoded. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskFailedByID_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_RespondActivityTaskFailedByID_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondActivityTaskFailedByID_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondActivityTaskFailedByID_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RespondActivityTaskFailedByID_Result // struct. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RespondActivityTaskFailedByID_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondActivityTaskFailedByID_Result match the // provided WorkflowService_RespondActivityTaskFailedByID_Result. // // This function performs a deep comparison. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) Equals(rhs *WorkflowService_RespondActivityTaskFailedByID_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondActivityTaskFailedByID_Result. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondActivityTaskFailedByID" for this struct. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) MethodName() string { return "RespondActivityTaskFailedByID" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RespondActivityTaskFailedByID_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RespondDecisionTaskCompleted_Args represents the arguments for the WorkflowService.RespondDecisionTaskCompleted function. // // The arguments for RespondDecisionTaskCompleted are sent and received over the wire as this struct. type WorkflowService_RespondDecisionTaskCompleted_Args struct { CompleteRequest *shared.RespondDecisionTaskCompletedRequest `json:"completeRequest,omitempty"` } // ToWire translates a WorkflowService_RespondDecisionTaskCompleted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondDecisionTaskCompleted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CompleteRequest != nil { w, err = v.CompleteRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondDecisionTaskCompletedRequest_Read(w wire.Value) (*shared.RespondDecisionTaskCompletedRequest, error) { var v shared.RespondDecisionTaskCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RespondDecisionTaskCompleted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondDecisionTaskCompleted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondDecisionTaskCompleted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondDecisionTaskCompleted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CompleteRequest, err = _RespondDecisionTaskCompletedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RespondDecisionTaskCompleted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondDecisionTaskCompleted_Args struct could not be encoded. func (v *WorkflowService_RespondDecisionTaskCompleted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CompleteRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CompleteRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondDecisionTaskCompletedRequest_Decode(sr stream.Reader) (*shared.RespondDecisionTaskCompletedRequest, error) { var v shared.RespondDecisionTaskCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RespondDecisionTaskCompleted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondDecisionTaskCompleted_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RespondDecisionTaskCompleted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CompleteRequest, err = _RespondDecisionTaskCompletedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RespondDecisionTaskCompleted_Args // struct. func (v *WorkflowService_RespondDecisionTaskCompleted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CompleteRequest != nil { fields[i] = fmt.Sprintf("CompleteRequest: %v", v.CompleteRequest) i++ } return fmt.Sprintf("WorkflowService_RespondDecisionTaskCompleted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondDecisionTaskCompleted_Args match the // provided WorkflowService_RespondDecisionTaskCompleted_Args. // // This function performs a deep comparison. func (v *WorkflowService_RespondDecisionTaskCompleted_Args) Equals(rhs *WorkflowService_RespondDecisionTaskCompleted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CompleteRequest == nil && rhs.CompleteRequest == nil) || (v.CompleteRequest != nil && rhs.CompleteRequest != nil && v.CompleteRequest.Equals(rhs.CompleteRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondDecisionTaskCompleted_Args. func (v *WorkflowService_RespondDecisionTaskCompleted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CompleteRequest != nil { err = multierr.Append(err, enc.AddObject("completeRequest", v.CompleteRequest)) } return err } // GetCompleteRequest returns the value of CompleteRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskCompleted_Args) GetCompleteRequest() (o *shared.RespondDecisionTaskCompletedRequest) { if v != nil && v.CompleteRequest != nil { return v.CompleteRequest } return } // IsSetCompleteRequest returns true if CompleteRequest is not nil. func (v *WorkflowService_RespondDecisionTaskCompleted_Args) IsSetCompleteRequest() bool { return v != nil && v.CompleteRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondDecisionTaskCompleted" for this struct. func (v *WorkflowService_RespondDecisionTaskCompleted_Args) MethodName() string { return "RespondDecisionTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RespondDecisionTaskCompleted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RespondDecisionTaskCompleted_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RespondDecisionTaskCompleted // function. var WorkflowService_RespondDecisionTaskCompleted_Helper = struct { // Args accepts the parameters of RespondDecisionTaskCompleted in-order and returns // the arguments struct for the function. Args func( completeRequest *shared.RespondDecisionTaskCompletedRequest, ) *WorkflowService_RespondDecisionTaskCompleted_Args // IsException returns true if the given error can be thrown // by RespondDecisionTaskCompleted. // // An error can be thrown by RespondDecisionTaskCompleted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondDecisionTaskCompleted // given its return value and error. // // This allows mapping values and errors returned by // RespondDecisionTaskCompleted into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RespondDecisionTaskCompleted // // value, err := RespondDecisionTaskCompleted(args) // result, err := WorkflowService_RespondDecisionTaskCompleted_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RespondDecisionTaskCompleted: %v", err) // } // serialize(result) WrapResponse func(*shared.RespondDecisionTaskCompletedResponse, error) (*WorkflowService_RespondDecisionTaskCompleted_Result, error) // UnwrapResponse takes the result struct for RespondDecisionTaskCompleted // and returns the value or error returned by it. // // The error is non-nil only if RespondDecisionTaskCompleted threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_RespondDecisionTaskCompleted_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RespondDecisionTaskCompleted_Result) (*shared.RespondDecisionTaskCompletedResponse, error) }{} func init() { WorkflowService_RespondDecisionTaskCompleted_Helper.Args = func( completeRequest *shared.RespondDecisionTaskCompletedRequest, ) *WorkflowService_RespondDecisionTaskCompleted_Args { return &WorkflowService_RespondDecisionTaskCompleted_Args{ CompleteRequest: completeRequest, } } WorkflowService_RespondDecisionTaskCompleted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RespondDecisionTaskCompleted_Helper.WrapResponse = func(success *shared.RespondDecisionTaskCompletedResponse, err error) (*WorkflowService_RespondDecisionTaskCompleted_Result, error) { if err == nil { return &WorkflowService_RespondDecisionTaskCompleted_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskCompleted_Result.BadRequestError") } return &WorkflowService_RespondDecisionTaskCompleted_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskCompleted_Result.EntityNotExistError") } return &WorkflowService_RespondDecisionTaskCompleted_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskCompleted_Result.DomainNotActiveError") } return &WorkflowService_RespondDecisionTaskCompleted_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskCompleted_Result.LimitExceededError") } return &WorkflowService_RespondDecisionTaskCompleted_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskCompleted_Result.ServiceBusyError") } return &WorkflowService_RespondDecisionTaskCompleted_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskCompleted_Result.ClientVersionNotSupportedError") } return &WorkflowService_RespondDecisionTaskCompleted_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskCompleted_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RespondDecisionTaskCompleted_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskCompleted_Result.AccessDeniedError") } return &WorkflowService_RespondDecisionTaskCompleted_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RespondDecisionTaskCompleted_Helper.UnwrapResponse = func(result *WorkflowService_RespondDecisionTaskCompleted_Result) (success *shared.RespondDecisionTaskCompletedResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_RespondDecisionTaskCompleted_Result represents the result of a WorkflowService.RespondDecisionTaskCompleted function call. // // The result of a RespondDecisionTaskCompleted execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_RespondDecisionTaskCompleted_Result struct { // Value returned by RespondDecisionTaskCompleted after a successful execution. Success *shared.RespondDecisionTaskCompletedResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RespondDecisionTaskCompleted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondDecisionTaskCompleted_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RespondDecisionTaskCompleted_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondDecisionTaskCompletedResponse_Read(w wire.Value) (*shared.RespondDecisionTaskCompletedResponse, error) { var v shared.RespondDecisionTaskCompletedResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RespondDecisionTaskCompleted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondDecisionTaskCompleted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondDecisionTaskCompleted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondDecisionTaskCompleted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RespondDecisionTaskCompletedResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RespondDecisionTaskCompleted_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RespondDecisionTaskCompleted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondDecisionTaskCompleted_Result struct could not be encoded. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RespondDecisionTaskCompleted_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _RespondDecisionTaskCompletedResponse_Decode(sr stream.Reader) (*shared.RespondDecisionTaskCompletedResponse, error) { var v shared.RespondDecisionTaskCompletedResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RespondDecisionTaskCompleted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondDecisionTaskCompleted_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RespondDecisionTaskCompletedResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RespondDecisionTaskCompleted_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RespondDecisionTaskCompleted_Result // struct. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RespondDecisionTaskCompleted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondDecisionTaskCompleted_Result match the // provided WorkflowService_RespondDecisionTaskCompleted_Result. // // This function performs a deep comparison. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) Equals(rhs *WorkflowService_RespondDecisionTaskCompleted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondDecisionTaskCompleted_Result. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) GetSuccess() (o *shared.RespondDecisionTaskCompletedResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondDecisionTaskCompleted" for this struct. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) MethodName() string { return "RespondDecisionTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RespondDecisionTaskCompleted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RespondDecisionTaskFailed_Args represents the arguments for the WorkflowService.RespondDecisionTaskFailed function. // // The arguments for RespondDecisionTaskFailed are sent and received over the wire as this struct. type WorkflowService_RespondDecisionTaskFailed_Args struct { FailedRequest *shared.RespondDecisionTaskFailedRequest `json:"failedRequest,omitempty"` } // ToWire translates a WorkflowService_RespondDecisionTaskFailed_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondDecisionTaskFailed_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.FailedRequest != nil { w, err = v.FailedRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondDecisionTaskFailedRequest_Read(w wire.Value) (*shared.RespondDecisionTaskFailedRequest, error) { var v shared.RespondDecisionTaskFailedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RespondDecisionTaskFailed_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondDecisionTaskFailed_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondDecisionTaskFailed_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondDecisionTaskFailed_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.FailedRequest, err = _RespondDecisionTaskFailedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RespondDecisionTaskFailed_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondDecisionTaskFailed_Args struct could not be encoded. func (v *WorkflowService_RespondDecisionTaskFailed_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FailedRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.FailedRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondDecisionTaskFailedRequest_Decode(sr stream.Reader) (*shared.RespondDecisionTaskFailedRequest, error) { var v shared.RespondDecisionTaskFailedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RespondDecisionTaskFailed_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondDecisionTaskFailed_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RespondDecisionTaskFailed_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.FailedRequest, err = _RespondDecisionTaskFailedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RespondDecisionTaskFailed_Args // struct. func (v *WorkflowService_RespondDecisionTaskFailed_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.FailedRequest != nil { fields[i] = fmt.Sprintf("FailedRequest: %v", v.FailedRequest) i++ } return fmt.Sprintf("WorkflowService_RespondDecisionTaskFailed_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondDecisionTaskFailed_Args match the // provided WorkflowService_RespondDecisionTaskFailed_Args. // // This function performs a deep comparison. func (v *WorkflowService_RespondDecisionTaskFailed_Args) Equals(rhs *WorkflowService_RespondDecisionTaskFailed_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.FailedRequest == nil && rhs.FailedRequest == nil) || (v.FailedRequest != nil && rhs.FailedRequest != nil && v.FailedRequest.Equals(rhs.FailedRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondDecisionTaskFailed_Args. func (v *WorkflowService_RespondDecisionTaskFailed_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FailedRequest != nil { err = multierr.Append(err, enc.AddObject("failedRequest", v.FailedRequest)) } return err } // GetFailedRequest returns the value of FailedRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskFailed_Args) GetFailedRequest() (o *shared.RespondDecisionTaskFailedRequest) { if v != nil && v.FailedRequest != nil { return v.FailedRequest } return } // IsSetFailedRequest returns true if FailedRequest is not nil. func (v *WorkflowService_RespondDecisionTaskFailed_Args) IsSetFailedRequest() bool { return v != nil && v.FailedRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondDecisionTaskFailed" for this struct. func (v *WorkflowService_RespondDecisionTaskFailed_Args) MethodName() string { return "RespondDecisionTaskFailed" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RespondDecisionTaskFailed_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RespondDecisionTaskFailed_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RespondDecisionTaskFailed // function. var WorkflowService_RespondDecisionTaskFailed_Helper = struct { // Args accepts the parameters of RespondDecisionTaskFailed in-order and returns // the arguments struct for the function. Args func( failedRequest *shared.RespondDecisionTaskFailedRequest, ) *WorkflowService_RespondDecisionTaskFailed_Args // IsException returns true if the given error can be thrown // by RespondDecisionTaskFailed. // // An error can be thrown by RespondDecisionTaskFailed only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondDecisionTaskFailed // given the error returned by it. The provided error may // be nil if RespondDecisionTaskFailed did not fail. // // This allows mapping errors returned by RespondDecisionTaskFailed into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondDecisionTaskFailed // // err := RespondDecisionTaskFailed(args) // result, err := WorkflowService_RespondDecisionTaskFailed_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondDecisionTaskFailed: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RespondDecisionTaskFailed_Result, error) // UnwrapResponse takes the result struct for RespondDecisionTaskFailed // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondDecisionTaskFailed threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RespondDecisionTaskFailed_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RespondDecisionTaskFailed_Result) error }{} func init() { WorkflowService_RespondDecisionTaskFailed_Helper.Args = func( failedRequest *shared.RespondDecisionTaskFailedRequest, ) *WorkflowService_RespondDecisionTaskFailed_Args { return &WorkflowService_RespondDecisionTaskFailed_Args{ FailedRequest: failedRequest, } } WorkflowService_RespondDecisionTaskFailed_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RespondDecisionTaskFailed_Helper.WrapResponse = func(err error) (*WorkflowService_RespondDecisionTaskFailed_Result, error) { if err == nil { return &WorkflowService_RespondDecisionTaskFailed_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskFailed_Result.BadRequestError") } return &WorkflowService_RespondDecisionTaskFailed_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskFailed_Result.EntityNotExistError") } return &WorkflowService_RespondDecisionTaskFailed_Result{EntityNotExistError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskFailed_Result.DomainNotActiveError") } return &WorkflowService_RespondDecisionTaskFailed_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskFailed_Result.LimitExceededError") } return &WorkflowService_RespondDecisionTaskFailed_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskFailed_Result.ServiceBusyError") } return &WorkflowService_RespondDecisionTaskFailed_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskFailed_Result.ClientVersionNotSupportedError") } return &WorkflowService_RespondDecisionTaskFailed_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskFailed_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_RespondDecisionTaskFailed_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondDecisionTaskFailed_Result.AccessDeniedError") } return &WorkflowService_RespondDecisionTaskFailed_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RespondDecisionTaskFailed_Helper.UnwrapResponse = func(result *WorkflowService_RespondDecisionTaskFailed_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RespondDecisionTaskFailed_Result represents the result of a WorkflowService.RespondDecisionTaskFailed function call. // // The result of a RespondDecisionTaskFailed execution is sent and received over the wire as this struct. type WorkflowService_RespondDecisionTaskFailed_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RespondDecisionTaskFailed_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondDecisionTaskFailed_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RespondDecisionTaskFailed_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_RespondDecisionTaskFailed_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondDecisionTaskFailed_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondDecisionTaskFailed_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondDecisionTaskFailed_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondDecisionTaskFailed_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RespondDecisionTaskFailed_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondDecisionTaskFailed_Result struct could not be encoded. func (v *WorkflowService_RespondDecisionTaskFailed_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondDecisionTaskFailed_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_RespondDecisionTaskFailed_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondDecisionTaskFailed_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RespondDecisionTaskFailed_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondDecisionTaskFailed_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RespondDecisionTaskFailed_Result // struct. func (v *WorkflowService_RespondDecisionTaskFailed_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RespondDecisionTaskFailed_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondDecisionTaskFailed_Result match the // provided WorkflowService_RespondDecisionTaskFailed_Result. // // This function performs a deep comparison. func (v *WorkflowService_RespondDecisionTaskFailed_Result) Equals(rhs *WorkflowService_RespondDecisionTaskFailed_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondDecisionTaskFailed_Result. func (v *WorkflowService_RespondDecisionTaskFailed_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskFailed_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RespondDecisionTaskFailed_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskFailed_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RespondDecisionTaskFailed_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskFailed_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RespondDecisionTaskFailed_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskFailed_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RespondDecisionTaskFailed_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskFailed_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RespondDecisionTaskFailed_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskFailed_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RespondDecisionTaskFailed_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskFailed_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_RespondDecisionTaskFailed_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondDecisionTaskFailed_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RespondDecisionTaskFailed_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondDecisionTaskFailed" for this struct. func (v *WorkflowService_RespondDecisionTaskFailed_Result) MethodName() string { return "RespondDecisionTaskFailed" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RespondDecisionTaskFailed_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RespondQueryTaskCompleted_Args represents the arguments for the WorkflowService.RespondQueryTaskCompleted function. // // The arguments for RespondQueryTaskCompleted are sent and received over the wire as this struct. type WorkflowService_RespondQueryTaskCompleted_Args struct { CompleteRequest *shared.RespondQueryTaskCompletedRequest `json:"completeRequest,omitempty"` } // ToWire translates a WorkflowService_RespondQueryTaskCompleted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondQueryTaskCompleted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CompleteRequest != nil { w, err = v.CompleteRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondQueryTaskCompletedRequest_Read(w wire.Value) (*shared.RespondQueryTaskCompletedRequest, error) { var v shared.RespondQueryTaskCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RespondQueryTaskCompleted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondQueryTaskCompleted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondQueryTaskCompleted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondQueryTaskCompleted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CompleteRequest, err = _RespondQueryTaskCompletedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RespondQueryTaskCompleted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondQueryTaskCompleted_Args struct could not be encoded. func (v *WorkflowService_RespondQueryTaskCompleted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CompleteRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CompleteRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondQueryTaskCompletedRequest_Decode(sr stream.Reader) (*shared.RespondQueryTaskCompletedRequest, error) { var v shared.RespondQueryTaskCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RespondQueryTaskCompleted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondQueryTaskCompleted_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RespondQueryTaskCompleted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CompleteRequest, err = _RespondQueryTaskCompletedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RespondQueryTaskCompleted_Args // struct. func (v *WorkflowService_RespondQueryTaskCompleted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CompleteRequest != nil { fields[i] = fmt.Sprintf("CompleteRequest: %v", v.CompleteRequest) i++ } return fmt.Sprintf("WorkflowService_RespondQueryTaskCompleted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondQueryTaskCompleted_Args match the // provided WorkflowService_RespondQueryTaskCompleted_Args. // // This function performs a deep comparison. func (v *WorkflowService_RespondQueryTaskCompleted_Args) Equals(rhs *WorkflowService_RespondQueryTaskCompleted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CompleteRequest == nil && rhs.CompleteRequest == nil) || (v.CompleteRequest != nil && rhs.CompleteRequest != nil && v.CompleteRequest.Equals(rhs.CompleteRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondQueryTaskCompleted_Args. func (v *WorkflowService_RespondQueryTaskCompleted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CompleteRequest != nil { err = multierr.Append(err, enc.AddObject("completeRequest", v.CompleteRequest)) } return err } // GetCompleteRequest returns the value of CompleteRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondQueryTaskCompleted_Args) GetCompleteRequest() (o *shared.RespondQueryTaskCompletedRequest) { if v != nil && v.CompleteRequest != nil { return v.CompleteRequest } return } // IsSetCompleteRequest returns true if CompleteRequest is not nil. func (v *WorkflowService_RespondQueryTaskCompleted_Args) IsSetCompleteRequest() bool { return v != nil && v.CompleteRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondQueryTaskCompleted" for this struct. func (v *WorkflowService_RespondQueryTaskCompleted_Args) MethodName() string { return "RespondQueryTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RespondQueryTaskCompleted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RespondQueryTaskCompleted_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RespondQueryTaskCompleted // function. var WorkflowService_RespondQueryTaskCompleted_Helper = struct { // Args accepts the parameters of RespondQueryTaskCompleted in-order and returns // the arguments struct for the function. Args func( completeRequest *shared.RespondQueryTaskCompletedRequest, ) *WorkflowService_RespondQueryTaskCompleted_Args // IsException returns true if the given error can be thrown // by RespondQueryTaskCompleted. // // An error can be thrown by RespondQueryTaskCompleted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondQueryTaskCompleted // given the error returned by it. The provided error may // be nil if RespondQueryTaskCompleted did not fail. // // This allows mapping errors returned by RespondQueryTaskCompleted into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondQueryTaskCompleted // // err := RespondQueryTaskCompleted(args) // result, err := WorkflowService_RespondQueryTaskCompleted_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondQueryTaskCompleted: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_RespondQueryTaskCompleted_Result, error) // UnwrapResponse takes the result struct for RespondQueryTaskCompleted // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondQueryTaskCompleted threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_RespondQueryTaskCompleted_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RespondQueryTaskCompleted_Result) error }{} func init() { WorkflowService_RespondQueryTaskCompleted_Helper.Args = func( completeRequest *shared.RespondQueryTaskCompletedRequest, ) *WorkflowService_RespondQueryTaskCompleted_Args { return &WorkflowService_RespondQueryTaskCompleted_Args{ CompleteRequest: completeRequest, } } WorkflowService_RespondQueryTaskCompleted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RespondQueryTaskCompleted_Helper.WrapResponse = func(err error) (*WorkflowService_RespondQueryTaskCompleted_Result, error) { if err == nil { return &WorkflowService_RespondQueryTaskCompleted_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondQueryTaskCompleted_Result.BadRequestError") } return &WorkflowService_RespondQueryTaskCompleted_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondQueryTaskCompleted_Result.EntityNotExistError") } return &WorkflowService_RespondQueryTaskCompleted_Result{EntityNotExistError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondQueryTaskCompleted_Result.LimitExceededError") } return &WorkflowService_RespondQueryTaskCompleted_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondQueryTaskCompleted_Result.ServiceBusyError") } return &WorkflowService_RespondQueryTaskCompleted_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondQueryTaskCompleted_Result.DomainNotActiveError") } return &WorkflowService_RespondQueryTaskCompleted_Result{DomainNotActiveError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondQueryTaskCompleted_Result.ClientVersionNotSupportedError") } return &WorkflowService_RespondQueryTaskCompleted_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RespondQueryTaskCompleted_Result.AccessDeniedError") } return &WorkflowService_RespondQueryTaskCompleted_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RespondQueryTaskCompleted_Helper.UnwrapResponse = func(result *WorkflowService_RespondQueryTaskCompleted_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_RespondQueryTaskCompleted_Result represents the result of a WorkflowService.RespondQueryTaskCompleted function call. // // The result of a RespondQueryTaskCompleted execution is sent and received over the wire as this struct. type WorkflowService_RespondQueryTaskCompleted_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RespondQueryTaskCompleted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RespondQueryTaskCompleted_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RespondQueryTaskCompleted_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_RespondQueryTaskCompleted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RespondQueryTaskCompleted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RespondQueryTaskCompleted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RespondQueryTaskCompleted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondQueryTaskCompleted_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RespondQueryTaskCompleted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RespondQueryTaskCompleted_Result struct could not be encoded. func (v *WorkflowService_RespondQueryTaskCompleted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondQueryTaskCompleted_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_RespondQueryTaskCompleted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RespondQueryTaskCompleted_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RespondQueryTaskCompleted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_RespondQueryTaskCompleted_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RespondQueryTaskCompleted_Result // struct. func (v *WorkflowService_RespondQueryTaskCompleted_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RespondQueryTaskCompleted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RespondQueryTaskCompleted_Result match the // provided WorkflowService_RespondQueryTaskCompleted_Result. // // This function performs a deep comparison. func (v *WorkflowService_RespondQueryTaskCompleted_Result) Equals(rhs *WorkflowService_RespondQueryTaskCompleted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RespondQueryTaskCompleted_Result. func (v *WorkflowService_RespondQueryTaskCompleted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondQueryTaskCompleted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RespondQueryTaskCompleted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondQueryTaskCompleted_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RespondQueryTaskCompleted_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondQueryTaskCompleted_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RespondQueryTaskCompleted_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondQueryTaskCompleted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RespondQueryTaskCompleted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondQueryTaskCompleted_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RespondQueryTaskCompleted_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondQueryTaskCompleted_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RespondQueryTaskCompleted_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RespondQueryTaskCompleted_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RespondQueryTaskCompleted_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondQueryTaskCompleted" for this struct. func (v *WorkflowService_RespondQueryTaskCompleted_Result) MethodName() string { return "RespondQueryTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RespondQueryTaskCompleted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_RestartWorkflowExecution_Args represents the arguments for the WorkflowService.RestartWorkflowExecution function. // // The arguments for RestartWorkflowExecution are sent and received over the wire as this struct. type WorkflowService_RestartWorkflowExecution_Args struct { RestartRequest *shared.RestartWorkflowExecutionRequest `json:"restartRequest,omitempty"` } // ToWire translates a WorkflowService_RestartWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RestartWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.RestartRequest != nil { w, err = v.RestartRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RestartWorkflowExecutionRequest_Read(w wire.Value) (*shared.RestartWorkflowExecutionRequest, error) { var v shared.RestartWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RestartWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RestartWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RestartWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RestartWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.RestartRequest, err = _RestartWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_RestartWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RestartWorkflowExecution_Args struct could not be encoded. func (v *WorkflowService_RestartWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.RestartRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.RestartRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RestartWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.RestartWorkflowExecutionRequest, error) { var v shared.RestartWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RestartWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RestartWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *WorkflowService_RestartWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.RestartRequest, err = _RestartWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_RestartWorkflowExecution_Args // struct. func (v *WorkflowService_RestartWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.RestartRequest != nil { fields[i] = fmt.Sprintf("RestartRequest: %v", v.RestartRequest) i++ } return fmt.Sprintf("WorkflowService_RestartWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RestartWorkflowExecution_Args match the // provided WorkflowService_RestartWorkflowExecution_Args. // // This function performs a deep comparison. func (v *WorkflowService_RestartWorkflowExecution_Args) Equals(rhs *WorkflowService_RestartWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.RestartRequest == nil && rhs.RestartRequest == nil) || (v.RestartRequest != nil && rhs.RestartRequest != nil && v.RestartRequest.Equals(rhs.RestartRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RestartWorkflowExecution_Args. func (v *WorkflowService_RestartWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.RestartRequest != nil { err = multierr.Append(err, enc.AddObject("restartRequest", v.RestartRequest)) } return err } // GetRestartRequest returns the value of RestartRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_RestartWorkflowExecution_Args) GetRestartRequest() (o *shared.RestartWorkflowExecutionRequest) { if v != nil && v.RestartRequest != nil { return v.RestartRequest } return } // IsSetRestartRequest returns true if RestartRequest is not nil. func (v *WorkflowService_RestartWorkflowExecution_Args) IsSetRestartRequest() bool { return v != nil && v.RestartRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RestartWorkflowExecution" for this struct. func (v *WorkflowService_RestartWorkflowExecution_Args) MethodName() string { return "RestartWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_RestartWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_RestartWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.RestartWorkflowExecution // function. var WorkflowService_RestartWorkflowExecution_Helper = struct { // Args accepts the parameters of RestartWorkflowExecution in-order and returns // the arguments struct for the function. Args func( restartRequest *shared.RestartWorkflowExecutionRequest, ) *WorkflowService_RestartWorkflowExecution_Args // IsException returns true if the given error can be thrown // by RestartWorkflowExecution. // // An error can be thrown by RestartWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RestartWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // RestartWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RestartWorkflowExecution // // value, err := RestartWorkflowExecution(args) // result, err := WorkflowService_RestartWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RestartWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*shared.RestartWorkflowExecutionResponse, error) (*WorkflowService_RestartWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for RestartWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if RestartWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_RestartWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_RestartWorkflowExecution_Result) (*shared.RestartWorkflowExecutionResponse, error) }{} func init() { WorkflowService_RestartWorkflowExecution_Helper.Args = func( restartRequest *shared.RestartWorkflowExecutionRequest, ) *WorkflowService_RestartWorkflowExecution_Args { return &WorkflowService_RestartWorkflowExecution_Args{ RestartRequest: restartRequest, } } WorkflowService_RestartWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.EntityNotExistsError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_RestartWorkflowExecution_Helper.WrapResponse = func(success *shared.RestartWorkflowExecutionResponse, err error) (*WorkflowService_RestartWorkflowExecution_Result, error) { if err == nil { return &WorkflowService_RestartWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RestartWorkflowExecution_Result.BadRequestError") } return &WorkflowService_RestartWorkflowExecution_Result{BadRequestError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RestartWorkflowExecution_Result.ServiceBusyError") } return &WorkflowService_RestartWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RestartWorkflowExecution_Result.DomainNotActiveError") } return &WorkflowService_RestartWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RestartWorkflowExecution_Result.LimitExceededError") } return &WorkflowService_RestartWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RestartWorkflowExecution_Result.EntityNotExistError") } return &WorkflowService_RestartWorkflowExecution_Result{EntityNotExistError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RestartWorkflowExecution_Result.ClientVersionNotSupportedError") } return &WorkflowService_RestartWorkflowExecution_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_RestartWorkflowExecution_Result.AccessDeniedError") } return &WorkflowService_RestartWorkflowExecution_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_RestartWorkflowExecution_Helper.UnwrapResponse = func(result *WorkflowService_RestartWorkflowExecution_Result) (success *shared.RestartWorkflowExecutionResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_RestartWorkflowExecution_Result represents the result of a WorkflowService.RestartWorkflowExecution function call. // // The result of a RestartWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_RestartWorkflowExecution_Result struct { // Value returned by RestartWorkflowExecution after a successful execution. Success *shared.RestartWorkflowExecutionResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_RestartWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_RestartWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_RestartWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RestartWorkflowExecutionResponse_Read(w wire.Value) (*shared.RestartWorkflowExecutionResponse, error) { var v shared.RestartWorkflowExecutionResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_RestartWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_RestartWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_RestartWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_RestartWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RestartWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RestartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_RestartWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_RestartWorkflowExecution_Result struct could not be encoded. func (v *WorkflowService_RestartWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RestartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _RestartWorkflowExecutionResponse_Decode(sr stream.Reader) (*shared.RestartWorkflowExecutionResponse, error) { var v shared.RestartWorkflowExecutionResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_RestartWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_RestartWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *WorkflowService_RestartWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RestartWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_RestartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_RestartWorkflowExecution_Result // struct. func (v *WorkflowService_RestartWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_RestartWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_RestartWorkflowExecution_Result match the // provided WorkflowService_RestartWorkflowExecution_Result. // // This function performs a deep comparison. func (v *WorkflowService_RestartWorkflowExecution_Result) Equals(rhs *WorkflowService_RestartWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_RestartWorkflowExecution_Result. func (v *WorkflowService_RestartWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_RestartWorkflowExecution_Result) GetSuccess() (o *shared.RestartWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_RestartWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_RestartWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_RestartWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_RestartWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_RestartWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_RestartWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_RestartWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_RestartWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_RestartWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_RestartWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_RestartWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RestartWorkflowExecution_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_RestartWorkflowExecution_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_RestartWorkflowExecution_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_RestartWorkflowExecution_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RestartWorkflowExecution" for this struct. func (v *WorkflowService_RestartWorkflowExecution_Result) MethodName() string { return "RestartWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_RestartWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_ScanWorkflowExecutions_Args represents the arguments for the WorkflowService.ScanWorkflowExecutions function. // // The arguments for ScanWorkflowExecutions are sent and received over the wire as this struct. type WorkflowService_ScanWorkflowExecutions_Args struct { ListRequest *shared.ListWorkflowExecutionsRequest `json:"listRequest,omitempty"` } // ToWire translates a WorkflowService_ScanWorkflowExecutions_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ScanWorkflowExecutions_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ListRequest != nil { w, err = v.ListRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_ScanWorkflowExecutions_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ScanWorkflowExecutions_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ScanWorkflowExecutions_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ScanWorkflowExecutions_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ListRequest, err = _ListWorkflowExecutionsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_ScanWorkflowExecutions_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ScanWorkflowExecutions_Args struct could not be encoded. func (v *WorkflowService_ScanWorkflowExecutions_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ListRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ListRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_ScanWorkflowExecutions_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ScanWorkflowExecutions_Args struct could not be generated from the wire // representation. func (v *WorkflowService_ScanWorkflowExecutions_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ListRequest, err = _ListWorkflowExecutionsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_ScanWorkflowExecutions_Args // struct. func (v *WorkflowService_ScanWorkflowExecutions_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ListRequest != nil { fields[i] = fmt.Sprintf("ListRequest: %v", v.ListRequest) i++ } return fmt.Sprintf("WorkflowService_ScanWorkflowExecutions_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ScanWorkflowExecutions_Args match the // provided WorkflowService_ScanWorkflowExecutions_Args. // // This function performs a deep comparison. func (v *WorkflowService_ScanWorkflowExecutions_Args) Equals(rhs *WorkflowService_ScanWorkflowExecutions_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ListRequest == nil && rhs.ListRequest == nil) || (v.ListRequest != nil && rhs.ListRequest != nil && v.ListRequest.Equals(rhs.ListRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ScanWorkflowExecutions_Args. func (v *WorkflowService_ScanWorkflowExecutions_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ListRequest != nil { err = multierr.Append(err, enc.AddObject("listRequest", v.ListRequest)) } return err } // GetListRequest returns the value of ListRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_ScanWorkflowExecutions_Args) GetListRequest() (o *shared.ListWorkflowExecutionsRequest) { if v != nil && v.ListRequest != nil { return v.ListRequest } return } // IsSetListRequest returns true if ListRequest is not nil. func (v *WorkflowService_ScanWorkflowExecutions_Args) IsSetListRequest() bool { return v != nil && v.ListRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ScanWorkflowExecutions" for this struct. func (v *WorkflowService_ScanWorkflowExecutions_Args) MethodName() string { return "ScanWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_ScanWorkflowExecutions_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_ScanWorkflowExecutions_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.ScanWorkflowExecutions // function. var WorkflowService_ScanWorkflowExecutions_Helper = struct { // Args accepts the parameters of ScanWorkflowExecutions in-order and returns // the arguments struct for the function. Args func( listRequest *shared.ListWorkflowExecutionsRequest, ) *WorkflowService_ScanWorkflowExecutions_Args // IsException returns true if the given error can be thrown // by ScanWorkflowExecutions. // // An error can be thrown by ScanWorkflowExecutions only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ScanWorkflowExecutions // given its return value and error. // // This allows mapping values and errors returned by // ScanWorkflowExecutions into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ScanWorkflowExecutions // // value, err := ScanWorkflowExecutions(args) // result, err := WorkflowService_ScanWorkflowExecutions_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ScanWorkflowExecutions: %v", err) // } // serialize(result) WrapResponse func(*shared.ListWorkflowExecutionsResponse, error) (*WorkflowService_ScanWorkflowExecutions_Result, error) // UnwrapResponse takes the result struct for ScanWorkflowExecutions // and returns the value or error returned by it. // // The error is non-nil only if ScanWorkflowExecutions threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_ScanWorkflowExecutions_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_ScanWorkflowExecutions_Result) (*shared.ListWorkflowExecutionsResponse, error) }{} func init() { WorkflowService_ScanWorkflowExecutions_Helper.Args = func( listRequest *shared.ListWorkflowExecutionsRequest, ) *WorkflowService_ScanWorkflowExecutions_Args { return &WorkflowService_ScanWorkflowExecutions_Args{ ListRequest: listRequest, } } WorkflowService_ScanWorkflowExecutions_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_ScanWorkflowExecutions_Helper.WrapResponse = func(success *shared.ListWorkflowExecutionsResponse, err error) (*WorkflowService_ScanWorkflowExecutions_Result, error) { if err == nil { return &WorkflowService_ScanWorkflowExecutions_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ScanWorkflowExecutions_Result.BadRequestError") } return &WorkflowService_ScanWorkflowExecutions_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ScanWorkflowExecutions_Result.EntityNotExistError") } return &WorkflowService_ScanWorkflowExecutions_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ScanWorkflowExecutions_Result.ServiceBusyError") } return &WorkflowService_ScanWorkflowExecutions_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ScanWorkflowExecutions_Result.ClientVersionNotSupportedError") } return &WorkflowService_ScanWorkflowExecutions_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_ScanWorkflowExecutions_Result.AccessDeniedError") } return &WorkflowService_ScanWorkflowExecutions_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_ScanWorkflowExecutions_Helper.UnwrapResponse = func(result *WorkflowService_ScanWorkflowExecutions_Result) (success *shared.ListWorkflowExecutionsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_ScanWorkflowExecutions_Result represents the result of a WorkflowService.ScanWorkflowExecutions function call. // // The result of a ScanWorkflowExecutions execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_ScanWorkflowExecutions_Result struct { // Value returned by ScanWorkflowExecutions after a successful execution. Success *shared.ListWorkflowExecutionsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_ScanWorkflowExecutions_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_ScanWorkflowExecutions_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_ScanWorkflowExecutions_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_ScanWorkflowExecutions_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_ScanWorkflowExecutions_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_ScanWorkflowExecutions_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_ScanWorkflowExecutions_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ListWorkflowExecutionsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ScanWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_ScanWorkflowExecutions_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_ScanWorkflowExecutions_Result struct could not be encoded. func (v *WorkflowService_ScanWorkflowExecutions_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ScanWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_ScanWorkflowExecutions_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_ScanWorkflowExecutions_Result struct could not be generated from the wire // representation. func (v *WorkflowService_ScanWorkflowExecutions_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ListWorkflowExecutionsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_ScanWorkflowExecutions_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_ScanWorkflowExecutions_Result // struct. func (v *WorkflowService_ScanWorkflowExecutions_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_ScanWorkflowExecutions_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_ScanWorkflowExecutions_Result match the // provided WorkflowService_ScanWorkflowExecutions_Result. // // This function performs a deep comparison. func (v *WorkflowService_ScanWorkflowExecutions_Result) Equals(rhs *WorkflowService_ScanWorkflowExecutions_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_ScanWorkflowExecutions_Result. func (v *WorkflowService_ScanWorkflowExecutions_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_ScanWorkflowExecutions_Result) GetSuccess() (o *shared.ListWorkflowExecutionsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_ScanWorkflowExecutions_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_ScanWorkflowExecutions_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_ScanWorkflowExecutions_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_ScanWorkflowExecutions_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_ScanWorkflowExecutions_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_ScanWorkflowExecutions_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_ScanWorkflowExecutions_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ScanWorkflowExecutions_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_ScanWorkflowExecutions_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_ScanWorkflowExecutions_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_ScanWorkflowExecutions_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ScanWorkflowExecutions" for this struct. func (v *WorkflowService_ScanWorkflowExecutions_Result) MethodName() string { return "ScanWorkflowExecutions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_ScanWorkflowExecutions_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_SignalWithStartWorkflowExecution_Args represents the arguments for the WorkflowService.SignalWithStartWorkflowExecution function. // // The arguments for SignalWithStartWorkflowExecution are sent and received over the wire as this struct. type WorkflowService_SignalWithStartWorkflowExecution_Args struct { SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionRequest `json:"signalWithStartRequest,omitempty"` } // ToWire translates a WorkflowService_SignalWithStartWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.SignalWithStartRequest != nil { w, err = v.SignalWithStartRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SignalWithStartWorkflowExecutionRequest_Read(w wire.Value) (*shared.SignalWithStartWorkflowExecutionRequest, error) { var v shared.SignalWithStartWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_SignalWithStartWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_SignalWithStartWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_SignalWithStartWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.SignalWithStartRequest, err = _SignalWithStartWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_SignalWithStartWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_SignalWithStartWorkflowExecution_Args struct could not be encoded. func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SignalWithStartRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.SignalWithStartRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SignalWithStartWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.SignalWithStartWorkflowExecutionRequest, error) { var v shared.SignalWithStartWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_SignalWithStartWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_SignalWithStartWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.SignalWithStartRequest, err = _SignalWithStartWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_SignalWithStartWorkflowExecution_Args // struct. func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.SignalWithStartRequest != nil { fields[i] = fmt.Sprintf("SignalWithStartRequest: %v", v.SignalWithStartRequest) i++ } return fmt.Sprintf("WorkflowService_SignalWithStartWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_SignalWithStartWorkflowExecution_Args match the // provided WorkflowService_SignalWithStartWorkflowExecution_Args. // // This function performs a deep comparison. func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) Equals(rhs *WorkflowService_SignalWithStartWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SignalWithStartRequest == nil && rhs.SignalWithStartRequest == nil) || (v.SignalWithStartRequest != nil && rhs.SignalWithStartRequest != nil && v.SignalWithStartRequest.Equals(rhs.SignalWithStartRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_SignalWithStartWorkflowExecution_Args. func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SignalWithStartRequest != nil { err = multierr.Append(err, enc.AddObject("signalWithStartRequest", v.SignalWithStartRequest)) } return err } // GetSignalWithStartRequest returns the value of SignalWithStartRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) GetSignalWithStartRequest() (o *shared.SignalWithStartWorkflowExecutionRequest) { if v != nil && v.SignalWithStartRequest != nil { return v.SignalWithStartRequest } return } // IsSetSignalWithStartRequest returns true if SignalWithStartRequest is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) IsSetSignalWithStartRequest() bool { return v != nil && v.SignalWithStartRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "SignalWithStartWorkflowExecution" for this struct. func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) MethodName() string { return "SignalWithStartWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_SignalWithStartWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_SignalWithStartWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.SignalWithStartWorkflowExecution // function. var WorkflowService_SignalWithStartWorkflowExecution_Helper = struct { // Args accepts the parameters of SignalWithStartWorkflowExecution in-order and returns // the arguments struct for the function. Args func( signalWithStartRequest *shared.SignalWithStartWorkflowExecutionRequest, ) *WorkflowService_SignalWithStartWorkflowExecution_Args // IsException returns true if the given error can be thrown // by SignalWithStartWorkflowExecution. // // An error can be thrown by SignalWithStartWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for SignalWithStartWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // SignalWithStartWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by SignalWithStartWorkflowExecution // // value, err := SignalWithStartWorkflowExecution(args) // result, err := WorkflowService_SignalWithStartWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from SignalWithStartWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*shared.StartWorkflowExecutionResponse, error) (*WorkflowService_SignalWithStartWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for SignalWithStartWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if SignalWithStartWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_SignalWithStartWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_SignalWithStartWorkflowExecution_Result) (*shared.StartWorkflowExecutionResponse, error) }{} func init() { WorkflowService_SignalWithStartWorkflowExecution_Helper.Args = func( signalWithStartRequest *shared.SignalWithStartWorkflowExecutionRequest, ) *WorkflowService_SignalWithStartWorkflowExecution_Args { return &WorkflowService_SignalWithStartWorkflowExecution_Args{ SignalWithStartRequest: signalWithStartRequest, } } WorkflowService_SignalWithStartWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.WorkflowExecutionAlreadyStartedError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_SignalWithStartWorkflowExecution_Helper.WrapResponse = func(success *shared.StartWorkflowExecutionResponse, err error) (*WorkflowService_SignalWithStartWorkflowExecution_Result, error) { if err == nil { return &WorkflowService_SignalWithStartWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecution_Result.BadRequestError") } return &WorkflowService_SignalWithStartWorkflowExecution_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecution_Result.EntityNotExistError") } return &WorkflowService_SignalWithStartWorkflowExecution_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecution_Result.ServiceBusyError") } return &WorkflowService_SignalWithStartWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecution_Result.DomainNotActiveError") } return &WorkflowService_SignalWithStartWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecution_Result.LimitExceededError") } return &WorkflowService_SignalWithStartWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.WorkflowExecutionAlreadyStartedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecution_Result.WorkflowAlreadyStartedError") } return &WorkflowService_SignalWithStartWorkflowExecution_Result{WorkflowAlreadyStartedError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecution_Result.ClientVersionNotSupportedError") } return &WorkflowService_SignalWithStartWorkflowExecution_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecution_Result.AccessDeniedError") } return &WorkflowService_SignalWithStartWorkflowExecution_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_SignalWithStartWorkflowExecution_Helper.UnwrapResponse = func(result *WorkflowService_SignalWithStartWorkflowExecution_Result) (success *shared.StartWorkflowExecutionResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.WorkflowAlreadyStartedError != nil { err = result.WorkflowAlreadyStartedError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_SignalWithStartWorkflowExecution_Result represents the result of a WorkflowService.SignalWithStartWorkflowExecution function call. // // The result of a SignalWithStartWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_SignalWithStartWorkflowExecution_Result struct { // Value returned by SignalWithStartWorkflowExecution after a successful execution. Success *shared.StartWorkflowExecutionResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` WorkflowAlreadyStartedError *shared.WorkflowExecutionAlreadyStartedError `json:"workflowAlreadyStartedError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_SignalWithStartWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.WorkflowAlreadyStartedError != nil { w, err = v.WorkflowAlreadyStartedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_SignalWithStartWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StartWorkflowExecutionResponse_Read(w wire.Value) (*shared.StartWorkflowExecutionResponse, error) { var v shared.StartWorkflowExecutionResponse err := v.FromWire(w) return &v, err } func _WorkflowExecutionAlreadyStartedError_Read(w wire.Value) (*shared.WorkflowExecutionAlreadyStartedError, error) { var v shared.WorkflowExecutionAlreadyStartedError err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_SignalWithStartWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_SignalWithStartWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_SignalWithStartWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _StartWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.WorkflowAlreadyStartedError, err = _WorkflowExecutionAlreadyStartedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.WorkflowAlreadyStartedError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_SignalWithStartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_SignalWithStartWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_SignalWithStartWorkflowExecution_Result struct could not be encoded. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowAlreadyStartedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowAlreadyStartedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.WorkflowAlreadyStartedError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_SignalWithStartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _StartWorkflowExecutionResponse_Decode(sr stream.Reader) (*shared.StartWorkflowExecutionResponse, error) { var v shared.StartWorkflowExecutionResponse err := v.Decode(sr) return &v, err } func _WorkflowExecutionAlreadyStartedError_Decode(sr stream.Reader) (*shared.WorkflowExecutionAlreadyStartedError, error) { var v shared.WorkflowExecutionAlreadyStartedError err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_SignalWithStartWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_SignalWithStartWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _StartWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.WorkflowAlreadyStartedError, err = _WorkflowExecutionAlreadyStartedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.WorkflowAlreadyStartedError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_SignalWithStartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_SignalWithStartWorkflowExecution_Result // struct. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.WorkflowAlreadyStartedError != nil { fields[i] = fmt.Sprintf("WorkflowAlreadyStartedError: %v", v.WorkflowAlreadyStartedError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_SignalWithStartWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_SignalWithStartWorkflowExecution_Result match the // provided WorkflowService_SignalWithStartWorkflowExecution_Result. // // This function performs a deep comparison. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) Equals(rhs *WorkflowService_SignalWithStartWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.WorkflowAlreadyStartedError == nil && rhs.WorkflowAlreadyStartedError == nil) || (v.WorkflowAlreadyStartedError != nil && rhs.WorkflowAlreadyStartedError != nil && v.WorkflowAlreadyStartedError.Equals(rhs.WorkflowAlreadyStartedError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_SignalWithStartWorkflowExecution_Result. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.WorkflowAlreadyStartedError != nil { err = multierr.Append(err, enc.AddObject("workflowAlreadyStartedError", v.WorkflowAlreadyStartedError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) GetSuccess() (o *shared.StartWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetWorkflowAlreadyStartedError returns the value of WorkflowAlreadyStartedError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) GetWorkflowAlreadyStartedError() (o *shared.WorkflowExecutionAlreadyStartedError) { if v != nil && v.WorkflowAlreadyStartedError != nil { return v.WorkflowAlreadyStartedError } return } // IsSetWorkflowAlreadyStartedError returns true if WorkflowAlreadyStartedError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) IsSetWorkflowAlreadyStartedError() bool { return v != nil && v.WorkflowAlreadyStartedError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "SignalWithStartWorkflowExecution" for this struct. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) MethodName() string { return "SignalWithStartWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_SignalWithStartWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_SignalWithStartWorkflowExecutionAsync_Args represents the arguments for the WorkflowService.SignalWithStartWorkflowExecutionAsync function. // // The arguments for SignalWithStartWorkflowExecutionAsync are sent and received over the wire as this struct. type WorkflowService_SignalWithStartWorkflowExecutionAsync_Args struct { SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionAsyncRequest `json:"signalWithStartRequest,omitempty"` } // ToWire translates a WorkflowService_SignalWithStartWorkflowExecutionAsync_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.SignalWithStartRequest != nil { w, err = v.SignalWithStartRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SignalWithStartWorkflowExecutionAsyncRequest_Read(w wire.Value) (*shared.SignalWithStartWorkflowExecutionAsyncRequest, error) { var v shared.SignalWithStartWorkflowExecutionAsyncRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_SignalWithStartWorkflowExecutionAsync_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_SignalWithStartWorkflowExecutionAsync_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_SignalWithStartWorkflowExecutionAsync_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.SignalWithStartRequest, err = _SignalWithStartWorkflowExecutionAsyncRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_SignalWithStartWorkflowExecutionAsync_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_SignalWithStartWorkflowExecutionAsync_Args struct could not be encoded. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SignalWithStartRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.SignalWithStartRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SignalWithStartWorkflowExecutionAsyncRequest_Decode(sr stream.Reader) (*shared.SignalWithStartWorkflowExecutionAsyncRequest, error) { var v shared.SignalWithStartWorkflowExecutionAsyncRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_SignalWithStartWorkflowExecutionAsync_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_SignalWithStartWorkflowExecutionAsync_Args struct could not be generated from the wire // representation. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.SignalWithStartRequest, err = _SignalWithStartWorkflowExecutionAsyncRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_SignalWithStartWorkflowExecutionAsync_Args // struct. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.SignalWithStartRequest != nil { fields[i] = fmt.Sprintf("SignalWithStartRequest: %v", v.SignalWithStartRequest) i++ } return fmt.Sprintf("WorkflowService_SignalWithStartWorkflowExecutionAsync_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_SignalWithStartWorkflowExecutionAsync_Args match the // provided WorkflowService_SignalWithStartWorkflowExecutionAsync_Args. // // This function performs a deep comparison. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) Equals(rhs *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SignalWithStartRequest == nil && rhs.SignalWithStartRequest == nil) || (v.SignalWithStartRequest != nil && rhs.SignalWithStartRequest != nil && v.SignalWithStartRequest.Equals(rhs.SignalWithStartRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_SignalWithStartWorkflowExecutionAsync_Args. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SignalWithStartRequest != nil { err = multierr.Append(err, enc.AddObject("signalWithStartRequest", v.SignalWithStartRequest)) } return err } // GetSignalWithStartRequest returns the value of SignalWithStartRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) GetSignalWithStartRequest() (o *shared.SignalWithStartWorkflowExecutionAsyncRequest) { if v != nil && v.SignalWithStartRequest != nil { return v.SignalWithStartRequest } return } // IsSetSignalWithStartRequest returns true if SignalWithStartRequest is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) IsSetSignalWithStartRequest() bool { return v != nil && v.SignalWithStartRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "SignalWithStartWorkflowExecutionAsync" for this struct. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) MethodName() string { return "SignalWithStartWorkflowExecutionAsync" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.SignalWithStartWorkflowExecutionAsync // function. var WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper = struct { // Args accepts the parameters of SignalWithStartWorkflowExecutionAsync in-order and returns // the arguments struct for the function. Args func( signalWithStartRequest *shared.SignalWithStartWorkflowExecutionAsyncRequest, ) *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args // IsException returns true if the given error can be thrown // by SignalWithStartWorkflowExecutionAsync. // // An error can be thrown by SignalWithStartWorkflowExecutionAsync only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for SignalWithStartWorkflowExecutionAsync // given its return value and error. // // This allows mapping values and errors returned by // SignalWithStartWorkflowExecutionAsync into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by SignalWithStartWorkflowExecutionAsync // // value, err := SignalWithStartWorkflowExecutionAsync(args) // result, err := WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from SignalWithStartWorkflowExecutionAsync: %v", err) // } // serialize(result) WrapResponse func(*shared.SignalWithStartWorkflowExecutionAsyncResponse, error) (*WorkflowService_SignalWithStartWorkflowExecutionAsync_Result, error) // UnwrapResponse takes the result struct for SignalWithStartWorkflowExecutionAsync // and returns the value or error returned by it. // // The error is non-nil only if SignalWithStartWorkflowExecutionAsync threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) (*shared.SignalWithStartWorkflowExecutionAsyncResponse, error) }{} func init() { WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper.Args = func( signalWithStartRequest *shared.SignalWithStartWorkflowExecutionAsyncRequest, ) *WorkflowService_SignalWithStartWorkflowExecutionAsync_Args { return &WorkflowService_SignalWithStartWorkflowExecutionAsync_Args{ SignalWithStartRequest: signalWithStartRequest, } } WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.WorkflowExecutionAlreadyStartedError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.EntityNotExistsError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper.WrapResponse = func(success *shared.SignalWithStartWorkflowExecutionAsyncResponse, err error) (*WorkflowService_SignalWithStartWorkflowExecutionAsync_Result, error) { if err == nil { return &WorkflowService_SignalWithStartWorkflowExecutionAsync_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecutionAsync_Result.BadRequestError") } return &WorkflowService_SignalWithStartWorkflowExecutionAsync_Result{BadRequestError: e}, nil case *shared.WorkflowExecutionAlreadyStartedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecutionAsync_Result.SessionAlreadyExistError") } return &WorkflowService_SignalWithStartWorkflowExecutionAsync_Result{SessionAlreadyExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecutionAsync_Result.ServiceBusyError") } return &WorkflowService_SignalWithStartWorkflowExecutionAsync_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecutionAsync_Result.DomainNotActiveError") } return &WorkflowService_SignalWithStartWorkflowExecutionAsync_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecutionAsync_Result.LimitExceededError") } return &WorkflowService_SignalWithStartWorkflowExecutionAsync_Result{LimitExceededError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecutionAsync_Result.EntityNotExistError") } return &WorkflowService_SignalWithStartWorkflowExecutionAsync_Result{EntityNotExistError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecutionAsync_Result.ClientVersionNotSupportedError") } return &WorkflowService_SignalWithStartWorkflowExecutionAsync_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWithStartWorkflowExecutionAsync_Result.AccessDeniedError") } return &WorkflowService_SignalWithStartWorkflowExecutionAsync_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper.UnwrapResponse = func(result *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) (success *shared.SignalWithStartWorkflowExecutionAsyncResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.SessionAlreadyExistError != nil { err = result.SessionAlreadyExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_SignalWithStartWorkflowExecutionAsync_Result represents the result of a WorkflowService.SignalWithStartWorkflowExecutionAsync function call. // // The result of a SignalWithStartWorkflowExecutionAsync execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_SignalWithStartWorkflowExecutionAsync_Result struct { // Value returned by SignalWithStartWorkflowExecutionAsync after a successful execution. Success *shared.SignalWithStartWorkflowExecutionAsyncResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` SessionAlreadyExistError *shared.WorkflowExecutionAlreadyStartedError `json:"sessionAlreadyExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_SignalWithStartWorkflowExecutionAsync_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.SessionAlreadyExistError != nil { w, err = v.SessionAlreadyExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_SignalWithStartWorkflowExecutionAsync_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SignalWithStartWorkflowExecutionAsyncResponse_Read(w wire.Value) (*shared.SignalWithStartWorkflowExecutionAsyncResponse, error) { var v shared.SignalWithStartWorkflowExecutionAsyncResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_SignalWithStartWorkflowExecutionAsync_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_SignalWithStartWorkflowExecutionAsync_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_SignalWithStartWorkflowExecutionAsync_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _SignalWithStartWorkflowExecutionAsyncResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.SessionAlreadyExistError, err = _WorkflowExecutionAlreadyStartedError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_SignalWithStartWorkflowExecutionAsync_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_SignalWithStartWorkflowExecutionAsync_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_SignalWithStartWorkflowExecutionAsync_Result struct could not be encoded. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SessionAlreadyExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.SessionAlreadyExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_SignalWithStartWorkflowExecutionAsync_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _SignalWithStartWorkflowExecutionAsyncResponse_Decode(sr stream.Reader) (*shared.SignalWithStartWorkflowExecutionAsyncResponse, error) { var v shared.SignalWithStartWorkflowExecutionAsyncResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_SignalWithStartWorkflowExecutionAsync_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_SignalWithStartWorkflowExecutionAsync_Result struct could not be generated from the wire // representation. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _SignalWithStartWorkflowExecutionAsyncResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.SessionAlreadyExistError, err = _WorkflowExecutionAlreadyStartedError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_SignalWithStartWorkflowExecutionAsync_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_SignalWithStartWorkflowExecutionAsync_Result // struct. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.SessionAlreadyExistError != nil { fields[i] = fmt.Sprintf("SessionAlreadyExistError: %v", v.SessionAlreadyExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_SignalWithStartWorkflowExecutionAsync_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_SignalWithStartWorkflowExecutionAsync_Result match the // provided WorkflowService_SignalWithStartWorkflowExecutionAsync_Result. // // This function performs a deep comparison. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) Equals(rhs *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.SessionAlreadyExistError == nil && rhs.SessionAlreadyExistError == nil) || (v.SessionAlreadyExistError != nil && rhs.SessionAlreadyExistError != nil && v.SessionAlreadyExistError.Equals(rhs.SessionAlreadyExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_SignalWithStartWorkflowExecutionAsync_Result. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.SessionAlreadyExistError != nil { err = multierr.Append(err, enc.AddObject("sessionAlreadyExistError", v.SessionAlreadyExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) GetSuccess() (o *shared.SignalWithStartWorkflowExecutionAsyncResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetSessionAlreadyExistError returns the value of SessionAlreadyExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) GetSessionAlreadyExistError() (o *shared.WorkflowExecutionAlreadyStartedError) { if v != nil && v.SessionAlreadyExistError != nil { return v.SessionAlreadyExistError } return } // IsSetSessionAlreadyExistError returns true if SessionAlreadyExistError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) IsSetSessionAlreadyExistError() bool { return v != nil && v.SessionAlreadyExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "SignalWithStartWorkflowExecutionAsync" for this struct. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) MethodName() string { return "SignalWithStartWorkflowExecutionAsync" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_SignalWithStartWorkflowExecutionAsync_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_SignalWorkflowExecution_Args represents the arguments for the WorkflowService.SignalWorkflowExecution function. // // The arguments for SignalWorkflowExecution are sent and received over the wire as this struct. type WorkflowService_SignalWorkflowExecution_Args struct { SignalRequest *shared.SignalWorkflowExecutionRequest `json:"signalRequest,omitempty"` } // ToWire translates a WorkflowService_SignalWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_SignalWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.SignalRequest != nil { w, err = v.SignalRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SignalWorkflowExecutionRequest_Read(w wire.Value) (*shared.SignalWorkflowExecutionRequest, error) { var v shared.SignalWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_SignalWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_SignalWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_SignalWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_SignalWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.SignalRequest, err = _SignalWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_SignalWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_SignalWorkflowExecution_Args struct could not be encoded. func (v *WorkflowService_SignalWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SignalRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.SignalRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SignalWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.SignalWorkflowExecutionRequest, error) { var v shared.SignalWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_SignalWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_SignalWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *WorkflowService_SignalWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.SignalRequest, err = _SignalWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_SignalWorkflowExecution_Args // struct. func (v *WorkflowService_SignalWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.SignalRequest != nil { fields[i] = fmt.Sprintf("SignalRequest: %v", v.SignalRequest) i++ } return fmt.Sprintf("WorkflowService_SignalWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_SignalWorkflowExecution_Args match the // provided WorkflowService_SignalWorkflowExecution_Args. // // This function performs a deep comparison. func (v *WorkflowService_SignalWorkflowExecution_Args) Equals(rhs *WorkflowService_SignalWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SignalRequest == nil && rhs.SignalRequest == nil) || (v.SignalRequest != nil && rhs.SignalRequest != nil && v.SignalRequest.Equals(rhs.SignalRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_SignalWorkflowExecution_Args. func (v *WorkflowService_SignalWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SignalRequest != nil { err = multierr.Append(err, enc.AddObject("signalRequest", v.SignalRequest)) } return err } // GetSignalRequest returns the value of SignalRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWorkflowExecution_Args) GetSignalRequest() (o *shared.SignalWorkflowExecutionRequest) { if v != nil && v.SignalRequest != nil { return v.SignalRequest } return } // IsSetSignalRequest returns true if SignalRequest is not nil. func (v *WorkflowService_SignalWorkflowExecution_Args) IsSetSignalRequest() bool { return v != nil && v.SignalRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "SignalWorkflowExecution" for this struct. func (v *WorkflowService_SignalWorkflowExecution_Args) MethodName() string { return "SignalWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_SignalWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_SignalWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.SignalWorkflowExecution // function. var WorkflowService_SignalWorkflowExecution_Helper = struct { // Args accepts the parameters of SignalWorkflowExecution in-order and returns // the arguments struct for the function. Args func( signalRequest *shared.SignalWorkflowExecutionRequest, ) *WorkflowService_SignalWorkflowExecution_Args // IsException returns true if the given error can be thrown // by SignalWorkflowExecution. // // An error can be thrown by SignalWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for SignalWorkflowExecution // given the error returned by it. The provided error may // be nil if SignalWorkflowExecution did not fail. // // This allows mapping errors returned by SignalWorkflowExecution into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // SignalWorkflowExecution // // err := SignalWorkflowExecution(args) // result, err := WorkflowService_SignalWorkflowExecution_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from SignalWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_SignalWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for SignalWorkflowExecution // and returns the erorr returned by it (if any). // // The error is non-nil only if SignalWorkflowExecution threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_SignalWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_SignalWorkflowExecution_Result) error }{} func init() { WorkflowService_SignalWorkflowExecution_Helper.Args = func( signalRequest *shared.SignalWorkflowExecutionRequest, ) *WorkflowService_SignalWorkflowExecution_Args { return &WorkflowService_SignalWorkflowExecution_Args{ SignalRequest: signalRequest, } } WorkflowService_SignalWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_SignalWorkflowExecution_Helper.WrapResponse = func(err error) (*WorkflowService_SignalWorkflowExecution_Result, error) { if err == nil { return &WorkflowService_SignalWorkflowExecution_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWorkflowExecution_Result.BadRequestError") } return &WorkflowService_SignalWorkflowExecution_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWorkflowExecution_Result.EntityNotExistError") } return &WorkflowService_SignalWorkflowExecution_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWorkflowExecution_Result.ServiceBusyError") } return &WorkflowService_SignalWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWorkflowExecution_Result.DomainNotActiveError") } return &WorkflowService_SignalWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWorkflowExecution_Result.LimitExceededError") } return &WorkflowService_SignalWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWorkflowExecution_Result.ClientVersionNotSupportedError") } return &WorkflowService_SignalWorkflowExecution_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWorkflowExecution_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_SignalWorkflowExecution_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_SignalWorkflowExecution_Result.AccessDeniedError") } return &WorkflowService_SignalWorkflowExecution_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_SignalWorkflowExecution_Helper.UnwrapResponse = func(result *WorkflowService_SignalWorkflowExecution_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_SignalWorkflowExecution_Result represents the result of a WorkflowService.SignalWorkflowExecution function call. // // The result of a SignalWorkflowExecution execution is sent and received over the wire as this struct. type WorkflowService_SignalWorkflowExecution_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_SignalWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_SignalWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_SignalWorkflowExecution_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_SignalWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_SignalWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_SignalWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_SignalWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_SignalWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_SignalWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_SignalWorkflowExecution_Result struct could not be encoded. func (v *WorkflowService_SignalWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_SignalWorkflowExecution_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_SignalWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_SignalWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *WorkflowService_SignalWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_SignalWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_SignalWorkflowExecution_Result // struct. func (v *WorkflowService_SignalWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_SignalWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_SignalWorkflowExecution_Result match the // provided WorkflowService_SignalWorkflowExecution_Result. // // This function performs a deep comparison. func (v *WorkflowService_SignalWorkflowExecution_Result) Equals(rhs *WorkflowService_SignalWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_SignalWorkflowExecution_Result. func (v *WorkflowService_SignalWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_SignalWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_SignalWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_SignalWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_SignalWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_SignalWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWorkflowExecution_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_SignalWorkflowExecution_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWorkflowExecution_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_SignalWorkflowExecution_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_SignalWorkflowExecution_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_SignalWorkflowExecution_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "SignalWorkflowExecution" for this struct. func (v *WorkflowService_SignalWorkflowExecution_Result) MethodName() string { return "SignalWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_SignalWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_StartWorkflowExecution_Args represents the arguments for the WorkflowService.StartWorkflowExecution function. // // The arguments for StartWorkflowExecution are sent and received over the wire as this struct. type WorkflowService_StartWorkflowExecution_Args struct { StartRequest *shared.StartWorkflowExecutionRequest `json:"startRequest,omitempty"` } // ToWire translates a WorkflowService_StartWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_StartWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.StartRequest != nil { w, err = v.StartRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StartWorkflowExecutionRequest_Read(w wire.Value) (*shared.StartWorkflowExecutionRequest, error) { var v shared.StartWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_StartWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_StartWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_StartWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_StartWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.StartRequest, err = _StartWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_StartWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_StartWorkflowExecution_Args struct could not be encoded. func (v *WorkflowService_StartWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.StartRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.StartRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _StartWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.StartWorkflowExecutionRequest, error) { var v shared.StartWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_StartWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_StartWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *WorkflowService_StartWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.StartRequest, err = _StartWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_StartWorkflowExecution_Args // struct. func (v *WorkflowService_StartWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.StartRequest != nil { fields[i] = fmt.Sprintf("StartRequest: %v", v.StartRequest) i++ } return fmt.Sprintf("WorkflowService_StartWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_StartWorkflowExecution_Args match the // provided WorkflowService_StartWorkflowExecution_Args. // // This function performs a deep comparison. func (v *WorkflowService_StartWorkflowExecution_Args) Equals(rhs *WorkflowService_StartWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.StartRequest == nil && rhs.StartRequest == nil) || (v.StartRequest != nil && rhs.StartRequest != nil && v.StartRequest.Equals(rhs.StartRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_StartWorkflowExecution_Args. func (v *WorkflowService_StartWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.StartRequest != nil { err = multierr.Append(err, enc.AddObject("startRequest", v.StartRequest)) } return err } // GetStartRequest returns the value of StartRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecution_Args) GetStartRequest() (o *shared.StartWorkflowExecutionRequest) { if v != nil && v.StartRequest != nil { return v.StartRequest } return } // IsSetStartRequest returns true if StartRequest is not nil. func (v *WorkflowService_StartWorkflowExecution_Args) IsSetStartRequest() bool { return v != nil && v.StartRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "StartWorkflowExecution" for this struct. func (v *WorkflowService_StartWorkflowExecution_Args) MethodName() string { return "StartWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_StartWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_StartWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.StartWorkflowExecution // function. var WorkflowService_StartWorkflowExecution_Helper = struct { // Args accepts the parameters of StartWorkflowExecution in-order and returns // the arguments struct for the function. Args func( startRequest *shared.StartWorkflowExecutionRequest, ) *WorkflowService_StartWorkflowExecution_Args // IsException returns true if the given error can be thrown // by StartWorkflowExecution. // // An error can be thrown by StartWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for StartWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // StartWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by StartWorkflowExecution // // value, err := StartWorkflowExecution(args) // result, err := WorkflowService_StartWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from StartWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*shared.StartWorkflowExecutionResponse, error) (*WorkflowService_StartWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for StartWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if StartWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_StartWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_StartWorkflowExecution_Result) (*shared.StartWorkflowExecutionResponse, error) }{} func init() { WorkflowService_StartWorkflowExecution_Helper.Args = func( startRequest *shared.StartWorkflowExecutionRequest, ) *WorkflowService_StartWorkflowExecution_Args { return &WorkflowService_StartWorkflowExecution_Args{ StartRequest: startRequest, } } WorkflowService_StartWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.WorkflowExecutionAlreadyStartedError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.EntityNotExistsError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_StartWorkflowExecution_Helper.WrapResponse = func(success *shared.StartWorkflowExecutionResponse, err error) (*WorkflowService_StartWorkflowExecution_Result, error) { if err == nil { return &WorkflowService_StartWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecution_Result.BadRequestError") } return &WorkflowService_StartWorkflowExecution_Result{BadRequestError: e}, nil case *shared.WorkflowExecutionAlreadyStartedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecution_Result.SessionAlreadyExistError") } return &WorkflowService_StartWorkflowExecution_Result{SessionAlreadyExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecution_Result.ServiceBusyError") } return &WorkflowService_StartWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecution_Result.DomainNotActiveError") } return &WorkflowService_StartWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecution_Result.LimitExceededError") } return &WorkflowService_StartWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecution_Result.EntityNotExistError") } return &WorkflowService_StartWorkflowExecution_Result{EntityNotExistError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecution_Result.ClientVersionNotSupportedError") } return &WorkflowService_StartWorkflowExecution_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecution_Result.AccessDeniedError") } return &WorkflowService_StartWorkflowExecution_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_StartWorkflowExecution_Helper.UnwrapResponse = func(result *WorkflowService_StartWorkflowExecution_Result) (success *shared.StartWorkflowExecutionResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.SessionAlreadyExistError != nil { err = result.SessionAlreadyExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_StartWorkflowExecution_Result represents the result of a WorkflowService.StartWorkflowExecution function call. // // The result of a StartWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_StartWorkflowExecution_Result struct { // Value returned by StartWorkflowExecution after a successful execution. Success *shared.StartWorkflowExecutionResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` SessionAlreadyExistError *shared.WorkflowExecutionAlreadyStartedError `json:"sessionAlreadyExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_StartWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_StartWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.SessionAlreadyExistError != nil { w, err = v.SessionAlreadyExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_StartWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_StartWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_StartWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_StartWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_StartWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _StartWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.SessionAlreadyExistError, err = _WorkflowExecutionAlreadyStartedError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_StartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_StartWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_StartWorkflowExecution_Result struct could not be encoded. func (v *WorkflowService_StartWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SessionAlreadyExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.SessionAlreadyExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_StartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_StartWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_StartWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *WorkflowService_StartWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _StartWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.SessionAlreadyExistError, err = _WorkflowExecutionAlreadyStartedError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_StartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_StartWorkflowExecution_Result // struct. func (v *WorkflowService_StartWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.SessionAlreadyExistError != nil { fields[i] = fmt.Sprintf("SessionAlreadyExistError: %v", v.SessionAlreadyExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_StartWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_StartWorkflowExecution_Result match the // provided WorkflowService_StartWorkflowExecution_Result. // // This function performs a deep comparison. func (v *WorkflowService_StartWorkflowExecution_Result) Equals(rhs *WorkflowService_StartWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.SessionAlreadyExistError == nil && rhs.SessionAlreadyExistError == nil) || (v.SessionAlreadyExistError != nil && rhs.SessionAlreadyExistError != nil && v.SessionAlreadyExistError.Equals(rhs.SessionAlreadyExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_StartWorkflowExecution_Result. func (v *WorkflowService_StartWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.SessionAlreadyExistError != nil { err = multierr.Append(err, enc.AddObject("sessionAlreadyExistError", v.SessionAlreadyExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecution_Result) GetSuccess() (o *shared.StartWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_StartWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_StartWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetSessionAlreadyExistError returns the value of SessionAlreadyExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecution_Result) GetSessionAlreadyExistError() (o *shared.WorkflowExecutionAlreadyStartedError) { if v != nil && v.SessionAlreadyExistError != nil { return v.SessionAlreadyExistError } return } // IsSetSessionAlreadyExistError returns true if SessionAlreadyExistError is not nil. func (v *WorkflowService_StartWorkflowExecution_Result) IsSetSessionAlreadyExistError() bool { return v != nil && v.SessionAlreadyExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_StartWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_StartWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_StartWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_StartWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecution_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_StartWorkflowExecution_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecution_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_StartWorkflowExecution_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "StartWorkflowExecution" for this struct. func (v *WorkflowService_StartWorkflowExecution_Result) MethodName() string { return "StartWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_StartWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_StartWorkflowExecutionAsync_Args represents the arguments for the WorkflowService.StartWorkflowExecutionAsync function. // // The arguments for StartWorkflowExecutionAsync are sent and received over the wire as this struct. type WorkflowService_StartWorkflowExecutionAsync_Args struct { StartRequest *shared.StartWorkflowExecutionAsyncRequest `json:"startRequest,omitempty"` } // ToWire translates a WorkflowService_StartWorkflowExecutionAsync_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_StartWorkflowExecutionAsync_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.StartRequest != nil { w, err = v.StartRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StartWorkflowExecutionAsyncRequest_Read(w wire.Value) (*shared.StartWorkflowExecutionAsyncRequest, error) { var v shared.StartWorkflowExecutionAsyncRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_StartWorkflowExecutionAsync_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_StartWorkflowExecutionAsync_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_StartWorkflowExecutionAsync_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_StartWorkflowExecutionAsync_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.StartRequest, err = _StartWorkflowExecutionAsyncRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_StartWorkflowExecutionAsync_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_StartWorkflowExecutionAsync_Args struct could not be encoded. func (v *WorkflowService_StartWorkflowExecutionAsync_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.StartRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.StartRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _StartWorkflowExecutionAsyncRequest_Decode(sr stream.Reader) (*shared.StartWorkflowExecutionAsyncRequest, error) { var v shared.StartWorkflowExecutionAsyncRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_StartWorkflowExecutionAsync_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_StartWorkflowExecutionAsync_Args struct could not be generated from the wire // representation. func (v *WorkflowService_StartWorkflowExecutionAsync_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.StartRequest, err = _StartWorkflowExecutionAsyncRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_StartWorkflowExecutionAsync_Args // struct. func (v *WorkflowService_StartWorkflowExecutionAsync_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.StartRequest != nil { fields[i] = fmt.Sprintf("StartRequest: %v", v.StartRequest) i++ } return fmt.Sprintf("WorkflowService_StartWorkflowExecutionAsync_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_StartWorkflowExecutionAsync_Args match the // provided WorkflowService_StartWorkflowExecutionAsync_Args. // // This function performs a deep comparison. func (v *WorkflowService_StartWorkflowExecutionAsync_Args) Equals(rhs *WorkflowService_StartWorkflowExecutionAsync_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.StartRequest == nil && rhs.StartRequest == nil) || (v.StartRequest != nil && rhs.StartRequest != nil && v.StartRequest.Equals(rhs.StartRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_StartWorkflowExecutionAsync_Args. func (v *WorkflowService_StartWorkflowExecutionAsync_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.StartRequest != nil { err = multierr.Append(err, enc.AddObject("startRequest", v.StartRequest)) } return err } // GetStartRequest returns the value of StartRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecutionAsync_Args) GetStartRequest() (o *shared.StartWorkflowExecutionAsyncRequest) { if v != nil && v.StartRequest != nil { return v.StartRequest } return } // IsSetStartRequest returns true if StartRequest is not nil. func (v *WorkflowService_StartWorkflowExecutionAsync_Args) IsSetStartRequest() bool { return v != nil && v.StartRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "StartWorkflowExecutionAsync" for this struct. func (v *WorkflowService_StartWorkflowExecutionAsync_Args) MethodName() string { return "StartWorkflowExecutionAsync" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_StartWorkflowExecutionAsync_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_StartWorkflowExecutionAsync_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.StartWorkflowExecutionAsync // function. var WorkflowService_StartWorkflowExecutionAsync_Helper = struct { // Args accepts the parameters of StartWorkflowExecutionAsync in-order and returns // the arguments struct for the function. Args func( startRequest *shared.StartWorkflowExecutionAsyncRequest, ) *WorkflowService_StartWorkflowExecutionAsync_Args // IsException returns true if the given error can be thrown // by StartWorkflowExecutionAsync. // // An error can be thrown by StartWorkflowExecutionAsync only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for StartWorkflowExecutionAsync // given its return value and error. // // This allows mapping values and errors returned by // StartWorkflowExecutionAsync into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by StartWorkflowExecutionAsync // // value, err := StartWorkflowExecutionAsync(args) // result, err := WorkflowService_StartWorkflowExecutionAsync_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from StartWorkflowExecutionAsync: %v", err) // } // serialize(result) WrapResponse func(*shared.StartWorkflowExecutionAsyncResponse, error) (*WorkflowService_StartWorkflowExecutionAsync_Result, error) // UnwrapResponse takes the result struct for StartWorkflowExecutionAsync // and returns the value or error returned by it. // // The error is non-nil only if StartWorkflowExecutionAsync threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_StartWorkflowExecutionAsync_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_StartWorkflowExecutionAsync_Result) (*shared.StartWorkflowExecutionAsyncResponse, error) }{} func init() { WorkflowService_StartWorkflowExecutionAsync_Helper.Args = func( startRequest *shared.StartWorkflowExecutionAsyncRequest, ) *WorkflowService_StartWorkflowExecutionAsync_Args { return &WorkflowService_StartWorkflowExecutionAsync_Args{ StartRequest: startRequest, } } WorkflowService_StartWorkflowExecutionAsync_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.WorkflowExecutionAlreadyStartedError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.EntityNotExistsError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_StartWorkflowExecutionAsync_Helper.WrapResponse = func(success *shared.StartWorkflowExecutionAsyncResponse, err error) (*WorkflowService_StartWorkflowExecutionAsync_Result, error) { if err == nil { return &WorkflowService_StartWorkflowExecutionAsync_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecutionAsync_Result.BadRequestError") } return &WorkflowService_StartWorkflowExecutionAsync_Result{BadRequestError: e}, nil case *shared.WorkflowExecutionAlreadyStartedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecutionAsync_Result.SessionAlreadyExistError") } return &WorkflowService_StartWorkflowExecutionAsync_Result{SessionAlreadyExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecutionAsync_Result.ServiceBusyError") } return &WorkflowService_StartWorkflowExecutionAsync_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecutionAsync_Result.DomainNotActiveError") } return &WorkflowService_StartWorkflowExecutionAsync_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecutionAsync_Result.LimitExceededError") } return &WorkflowService_StartWorkflowExecutionAsync_Result{LimitExceededError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecutionAsync_Result.EntityNotExistError") } return &WorkflowService_StartWorkflowExecutionAsync_Result{EntityNotExistError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecutionAsync_Result.ClientVersionNotSupportedError") } return &WorkflowService_StartWorkflowExecutionAsync_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_StartWorkflowExecutionAsync_Result.AccessDeniedError") } return &WorkflowService_StartWorkflowExecutionAsync_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_StartWorkflowExecutionAsync_Helper.UnwrapResponse = func(result *WorkflowService_StartWorkflowExecutionAsync_Result) (success *shared.StartWorkflowExecutionAsyncResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.SessionAlreadyExistError != nil { err = result.SessionAlreadyExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_StartWorkflowExecutionAsync_Result represents the result of a WorkflowService.StartWorkflowExecutionAsync function call. // // The result of a StartWorkflowExecutionAsync execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_StartWorkflowExecutionAsync_Result struct { // Value returned by StartWorkflowExecutionAsync after a successful execution. Success *shared.StartWorkflowExecutionAsyncResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` SessionAlreadyExistError *shared.WorkflowExecutionAlreadyStartedError `json:"sessionAlreadyExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_StartWorkflowExecutionAsync_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_StartWorkflowExecutionAsync_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.SessionAlreadyExistError != nil { w, err = v.SessionAlreadyExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_StartWorkflowExecutionAsync_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StartWorkflowExecutionAsyncResponse_Read(w wire.Value) (*shared.StartWorkflowExecutionAsyncResponse, error) { var v shared.StartWorkflowExecutionAsyncResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_StartWorkflowExecutionAsync_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_StartWorkflowExecutionAsync_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_StartWorkflowExecutionAsync_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_StartWorkflowExecutionAsync_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _StartWorkflowExecutionAsyncResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.SessionAlreadyExistError, err = _WorkflowExecutionAlreadyStartedError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_StartWorkflowExecutionAsync_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_StartWorkflowExecutionAsync_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_StartWorkflowExecutionAsync_Result struct could not be encoded. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SessionAlreadyExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.SessionAlreadyExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_StartWorkflowExecutionAsync_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _StartWorkflowExecutionAsyncResponse_Decode(sr stream.Reader) (*shared.StartWorkflowExecutionAsyncResponse, error) { var v shared.StartWorkflowExecutionAsyncResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_StartWorkflowExecutionAsync_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_StartWorkflowExecutionAsync_Result struct could not be generated from the wire // representation. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _StartWorkflowExecutionAsyncResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.SessionAlreadyExistError, err = _WorkflowExecutionAlreadyStartedError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_StartWorkflowExecutionAsync_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_StartWorkflowExecutionAsync_Result // struct. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.SessionAlreadyExistError != nil { fields[i] = fmt.Sprintf("SessionAlreadyExistError: %v", v.SessionAlreadyExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_StartWorkflowExecutionAsync_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_StartWorkflowExecutionAsync_Result match the // provided WorkflowService_StartWorkflowExecutionAsync_Result. // // This function performs a deep comparison. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) Equals(rhs *WorkflowService_StartWorkflowExecutionAsync_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.SessionAlreadyExistError == nil && rhs.SessionAlreadyExistError == nil) || (v.SessionAlreadyExistError != nil && rhs.SessionAlreadyExistError != nil && v.SessionAlreadyExistError.Equals(rhs.SessionAlreadyExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_StartWorkflowExecutionAsync_Result. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.SessionAlreadyExistError != nil { err = multierr.Append(err, enc.AddObject("sessionAlreadyExistError", v.SessionAlreadyExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) GetSuccess() (o *shared.StartWorkflowExecutionAsyncResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetSessionAlreadyExistError returns the value of SessionAlreadyExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) GetSessionAlreadyExistError() (o *shared.WorkflowExecutionAlreadyStartedError) { if v != nil && v.SessionAlreadyExistError != nil { return v.SessionAlreadyExistError } return } // IsSetSessionAlreadyExistError returns true if SessionAlreadyExistError is not nil. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) IsSetSessionAlreadyExistError() bool { return v != nil && v.SessionAlreadyExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "StartWorkflowExecutionAsync" for this struct. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) MethodName() string { return "StartWorkflowExecutionAsync" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_StartWorkflowExecutionAsync_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_TerminateWorkflowExecution_Args represents the arguments for the WorkflowService.TerminateWorkflowExecution function. // // The arguments for TerminateWorkflowExecution are sent and received over the wire as this struct. type WorkflowService_TerminateWorkflowExecution_Args struct { TerminateRequest *shared.TerminateWorkflowExecutionRequest `json:"terminateRequest,omitempty"` } // ToWire translates a WorkflowService_TerminateWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_TerminateWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.TerminateRequest != nil { w, err = v.TerminateRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TerminateWorkflowExecutionRequest_Read(w wire.Value) (*shared.TerminateWorkflowExecutionRequest, error) { var v shared.TerminateWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_TerminateWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_TerminateWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_TerminateWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_TerminateWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.TerminateRequest, err = _TerminateWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_TerminateWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_TerminateWorkflowExecution_Args struct could not be encoded. func (v *WorkflowService_TerminateWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TerminateRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.TerminateRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TerminateWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.TerminateWorkflowExecutionRequest, error) { var v shared.TerminateWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_TerminateWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_TerminateWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *WorkflowService_TerminateWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.TerminateRequest, err = _TerminateWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_TerminateWorkflowExecution_Args // struct. func (v *WorkflowService_TerminateWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.TerminateRequest != nil { fields[i] = fmt.Sprintf("TerminateRequest: %v", v.TerminateRequest) i++ } return fmt.Sprintf("WorkflowService_TerminateWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_TerminateWorkflowExecution_Args match the // provided WorkflowService_TerminateWorkflowExecution_Args. // // This function performs a deep comparison. func (v *WorkflowService_TerminateWorkflowExecution_Args) Equals(rhs *WorkflowService_TerminateWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TerminateRequest == nil && rhs.TerminateRequest == nil) || (v.TerminateRequest != nil && rhs.TerminateRequest != nil && v.TerminateRequest.Equals(rhs.TerminateRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_TerminateWorkflowExecution_Args. func (v *WorkflowService_TerminateWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TerminateRequest != nil { err = multierr.Append(err, enc.AddObject("terminateRequest", v.TerminateRequest)) } return err } // GetTerminateRequest returns the value of TerminateRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_TerminateWorkflowExecution_Args) GetTerminateRequest() (o *shared.TerminateWorkflowExecutionRequest) { if v != nil && v.TerminateRequest != nil { return v.TerminateRequest } return } // IsSetTerminateRequest returns true if TerminateRequest is not nil. func (v *WorkflowService_TerminateWorkflowExecution_Args) IsSetTerminateRequest() bool { return v != nil && v.TerminateRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "TerminateWorkflowExecution" for this struct. func (v *WorkflowService_TerminateWorkflowExecution_Args) MethodName() string { return "TerminateWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_TerminateWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_TerminateWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.TerminateWorkflowExecution // function. var WorkflowService_TerminateWorkflowExecution_Helper = struct { // Args accepts the parameters of TerminateWorkflowExecution in-order and returns // the arguments struct for the function. Args func( terminateRequest *shared.TerminateWorkflowExecutionRequest, ) *WorkflowService_TerminateWorkflowExecution_Args // IsException returns true if the given error can be thrown // by TerminateWorkflowExecution. // // An error can be thrown by TerminateWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for TerminateWorkflowExecution // given the error returned by it. The provided error may // be nil if TerminateWorkflowExecution did not fail. // // This allows mapping errors returned by TerminateWorkflowExecution into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // TerminateWorkflowExecution // // err := TerminateWorkflowExecution(args) // result, err := WorkflowService_TerminateWorkflowExecution_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from TerminateWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(error) (*WorkflowService_TerminateWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for TerminateWorkflowExecution // and returns the erorr returned by it (if any). // // The error is non-nil only if TerminateWorkflowExecution threw an // exception. // // result := deserialize(bytes) // err := WorkflowService_TerminateWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_TerminateWorkflowExecution_Result) error }{} func init() { WorkflowService_TerminateWorkflowExecution_Helper.Args = func( terminateRequest *shared.TerminateWorkflowExecutionRequest, ) *WorkflowService_TerminateWorkflowExecution_Args { return &WorkflowService_TerminateWorkflowExecution_Args{ TerminateRequest: terminateRequest, } } WorkflowService_TerminateWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_TerminateWorkflowExecution_Helper.WrapResponse = func(err error) (*WorkflowService_TerminateWorkflowExecution_Result, error) { if err == nil { return &WorkflowService_TerminateWorkflowExecution_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_TerminateWorkflowExecution_Result.BadRequestError") } return &WorkflowService_TerminateWorkflowExecution_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_TerminateWorkflowExecution_Result.EntityNotExistError") } return &WorkflowService_TerminateWorkflowExecution_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_TerminateWorkflowExecution_Result.ServiceBusyError") } return &WorkflowService_TerminateWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_TerminateWorkflowExecution_Result.DomainNotActiveError") } return &WorkflowService_TerminateWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_TerminateWorkflowExecution_Result.LimitExceededError") } return &WorkflowService_TerminateWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_TerminateWorkflowExecution_Result.ClientVersionNotSupportedError") } return &WorkflowService_TerminateWorkflowExecution_Result{ClientVersionNotSupportedError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_TerminateWorkflowExecution_Result.WorkflowExecutionAlreadyCompletedError") } return &WorkflowService_TerminateWorkflowExecution_Result{WorkflowExecutionAlreadyCompletedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_TerminateWorkflowExecution_Result.AccessDeniedError") } return &WorkflowService_TerminateWorkflowExecution_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_TerminateWorkflowExecution_Helper.UnwrapResponse = func(result *WorkflowService_TerminateWorkflowExecution_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // WorkflowService_TerminateWorkflowExecution_Result represents the result of a WorkflowService.TerminateWorkflowExecution function call. // // The result of a TerminateWorkflowExecution execution is sent and received over the wire as this struct. type WorkflowService_TerminateWorkflowExecution_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_TerminateWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_TerminateWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("WorkflowService_TerminateWorkflowExecution_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowService_TerminateWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_TerminateWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_TerminateWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_TerminateWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_TerminateWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_TerminateWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_TerminateWorkflowExecution_Result struct could not be encoded. func (v *WorkflowService_TerminateWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_TerminateWorkflowExecution_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a WorkflowService_TerminateWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_TerminateWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *WorkflowService_TerminateWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("WorkflowService_TerminateWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_TerminateWorkflowExecution_Result // struct. func (v *WorkflowService_TerminateWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_TerminateWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_TerminateWorkflowExecution_Result match the // provided WorkflowService_TerminateWorkflowExecution_Result. // // This function performs a deep comparison. func (v *WorkflowService_TerminateWorkflowExecution_Result) Equals(rhs *WorkflowService_TerminateWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_TerminateWorkflowExecution_Result. func (v *WorkflowService_TerminateWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_TerminateWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_TerminateWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_TerminateWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_TerminateWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_TerminateWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_TerminateWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_TerminateWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_TerminateWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *WorkflowService_TerminateWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *WorkflowService_TerminateWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_TerminateWorkflowExecution_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_TerminateWorkflowExecution_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *WorkflowService_TerminateWorkflowExecution_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *WorkflowService_TerminateWorkflowExecution_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_TerminateWorkflowExecution_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_TerminateWorkflowExecution_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "TerminateWorkflowExecution" for this struct. func (v *WorkflowService_TerminateWorkflowExecution_Result) MethodName() string { return "TerminateWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_TerminateWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // WorkflowService_UpdateDomain_Args represents the arguments for the WorkflowService.UpdateDomain function. // // The arguments for UpdateDomain are sent and received over the wire as this struct. type WorkflowService_UpdateDomain_Args struct { UpdateRequest *shared.UpdateDomainRequest `json:"updateRequest,omitempty"` } // ToWire translates a WorkflowService_UpdateDomain_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_UpdateDomain_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.UpdateRequest != nil { w, err = v.UpdateRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _UpdateDomainRequest_Read(w wire.Value) (*shared.UpdateDomainRequest, error) { var v shared.UpdateDomainRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_UpdateDomain_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_UpdateDomain_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_UpdateDomain_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_UpdateDomain_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.UpdateRequest, err = _UpdateDomainRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowService_UpdateDomain_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_UpdateDomain_Args struct could not be encoded. func (v *WorkflowService_UpdateDomain_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.UpdateRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.UpdateRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _UpdateDomainRequest_Decode(sr stream.Reader) (*shared.UpdateDomainRequest, error) { var v shared.UpdateDomainRequest err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_UpdateDomain_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_UpdateDomain_Args struct could not be generated from the wire // representation. func (v *WorkflowService_UpdateDomain_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.UpdateRequest, err = _UpdateDomainRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowService_UpdateDomain_Args // struct. func (v *WorkflowService_UpdateDomain_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.UpdateRequest != nil { fields[i] = fmt.Sprintf("UpdateRequest: %v", v.UpdateRequest) i++ } return fmt.Sprintf("WorkflowService_UpdateDomain_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_UpdateDomain_Args match the // provided WorkflowService_UpdateDomain_Args. // // This function performs a deep comparison. func (v *WorkflowService_UpdateDomain_Args) Equals(rhs *WorkflowService_UpdateDomain_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.UpdateRequest == nil && rhs.UpdateRequest == nil) || (v.UpdateRequest != nil && rhs.UpdateRequest != nil && v.UpdateRequest.Equals(rhs.UpdateRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_UpdateDomain_Args. func (v *WorkflowService_UpdateDomain_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.UpdateRequest != nil { err = multierr.Append(err, enc.AddObject("updateRequest", v.UpdateRequest)) } return err } // GetUpdateRequest returns the value of UpdateRequest if it is set or its // zero value if it is unset. func (v *WorkflowService_UpdateDomain_Args) GetUpdateRequest() (o *shared.UpdateDomainRequest) { if v != nil && v.UpdateRequest != nil { return v.UpdateRequest } return } // IsSetUpdateRequest returns true if UpdateRequest is not nil. func (v *WorkflowService_UpdateDomain_Args) IsSetUpdateRequest() bool { return v != nil && v.UpdateRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "UpdateDomain" for this struct. func (v *WorkflowService_UpdateDomain_Args) MethodName() string { return "UpdateDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *WorkflowService_UpdateDomain_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // WorkflowService_UpdateDomain_Helper provides functions that aid in handling the // parameters and return values of the WorkflowService.UpdateDomain // function. var WorkflowService_UpdateDomain_Helper = struct { // Args accepts the parameters of UpdateDomain in-order and returns // the arguments struct for the function. Args func( updateRequest *shared.UpdateDomainRequest, ) *WorkflowService_UpdateDomain_Args // IsException returns true if the given error can be thrown // by UpdateDomain. // // An error can be thrown by UpdateDomain only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for UpdateDomain // given its return value and error. // // This allows mapping values and errors returned by // UpdateDomain into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by UpdateDomain // // value, err := UpdateDomain(args) // result, err := WorkflowService_UpdateDomain_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from UpdateDomain: %v", err) // } // serialize(result) WrapResponse func(*shared.UpdateDomainResponse, error) (*WorkflowService_UpdateDomain_Result, error) // UnwrapResponse takes the result struct for UpdateDomain // and returns the value or error returned by it. // // The error is non-nil only if UpdateDomain threw an // exception. // // result := deserialize(bytes) // value, err := WorkflowService_UpdateDomain_Helper.UnwrapResponse(result) UnwrapResponse func(*WorkflowService_UpdateDomain_Result) (*shared.UpdateDomainResponse, error) }{} func init() { WorkflowService_UpdateDomain_Helper.Args = func( updateRequest *shared.UpdateDomainRequest, ) *WorkflowService_UpdateDomain_Args { return &WorkflowService_UpdateDomain_Args{ UpdateRequest: updateRequest, } } WorkflowService_UpdateDomain_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.DomainNotActiveError: return true case *shared.ClientVersionNotSupportedError: return true case *shared.AccessDeniedError: return true default: return false } } WorkflowService_UpdateDomain_Helper.WrapResponse = func(success *shared.UpdateDomainResponse, err error) (*WorkflowService_UpdateDomain_Result, error) { if err == nil { return &WorkflowService_UpdateDomain_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_UpdateDomain_Result.BadRequestError") } return &WorkflowService_UpdateDomain_Result{BadRequestError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_UpdateDomain_Result.EntityNotExistError") } return &WorkflowService_UpdateDomain_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_UpdateDomain_Result.ServiceBusyError") } return &WorkflowService_UpdateDomain_Result{ServiceBusyError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_UpdateDomain_Result.DomainNotActiveError") } return &WorkflowService_UpdateDomain_Result{DomainNotActiveError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_UpdateDomain_Result.ClientVersionNotSupportedError") } return &WorkflowService_UpdateDomain_Result{ClientVersionNotSupportedError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for WorkflowService_UpdateDomain_Result.AccessDeniedError") } return &WorkflowService_UpdateDomain_Result{AccessDeniedError: e}, nil } return nil, err } WorkflowService_UpdateDomain_Helper.UnwrapResponse = func(result *WorkflowService_UpdateDomain_Result) (success *shared.UpdateDomainResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // WorkflowService_UpdateDomain_Result represents the result of a WorkflowService.UpdateDomain function call. // // The result of a UpdateDomain execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type WorkflowService_UpdateDomain_Result struct { // Value returned by UpdateDomain after a successful execution. Success *shared.UpdateDomainResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a WorkflowService_UpdateDomain_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowService_UpdateDomain_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("WorkflowService_UpdateDomain_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _UpdateDomainResponse_Read(w wire.Value) (*shared.UpdateDomainResponse, error) { var v shared.UpdateDomainResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowService_UpdateDomain_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowService_UpdateDomain_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowService_UpdateDomain_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowService_UpdateDomain_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _UpdateDomainResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_UpdateDomain_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a WorkflowService_UpdateDomain_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowService_UpdateDomain_Result struct could not be encoded. func (v *WorkflowService_UpdateDomain_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_UpdateDomain_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _UpdateDomainResponse_Decode(sr stream.Reader) (*shared.UpdateDomainResponse, error) { var v shared.UpdateDomainResponse err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowService_UpdateDomain_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowService_UpdateDomain_Result struct could not be generated from the wire // representation. func (v *WorkflowService_UpdateDomain_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _UpdateDomainResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("WorkflowService_UpdateDomain_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a WorkflowService_UpdateDomain_Result // struct. func (v *WorkflowService_UpdateDomain_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("WorkflowService_UpdateDomain_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowService_UpdateDomain_Result match the // provided WorkflowService_UpdateDomain_Result. // // This function performs a deep comparison. func (v *WorkflowService_UpdateDomain_Result) Equals(rhs *WorkflowService_UpdateDomain_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowService_UpdateDomain_Result. func (v *WorkflowService_UpdateDomain_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *WorkflowService_UpdateDomain_Result) GetSuccess() (o *shared.UpdateDomainResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *WorkflowService_UpdateDomain_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *WorkflowService_UpdateDomain_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *WorkflowService_UpdateDomain_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *WorkflowService_UpdateDomain_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *WorkflowService_UpdateDomain_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *WorkflowService_UpdateDomain_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *WorkflowService_UpdateDomain_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *WorkflowService_UpdateDomain_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *WorkflowService_UpdateDomain_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *WorkflowService_UpdateDomain_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *WorkflowService_UpdateDomain_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *WorkflowService_UpdateDomain_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *WorkflowService_UpdateDomain_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "UpdateDomain" for this struct. func (v *WorkflowService_UpdateDomain_Result) MethodName() string { return "UpdateDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *WorkflowService_UpdateDomain_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } ================================================ FILE: .gen/go/cadence/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package cadence ================================================ FILE: .gen/go/cadence/workflowserviceclient/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package workflowserviceclient import ( context "context" reflect "reflect" wire "go.uber.org/thriftrw/wire" yarpc "go.uber.org/yarpc" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" cadence "github.com/uber/cadence/.gen/go/cadence" shared "github.com/uber/cadence/.gen/go/shared" ) // Interface is a client for the WorkflowService service. type Interface interface { CountWorkflowExecutions( ctx context.Context, CountRequest *shared.CountWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (*shared.CountWorkflowExecutionsResponse, error) DeleteDomain( ctx context.Context, DeleteRequest *shared.DeleteDomainRequest, opts ...yarpc.CallOption, ) error DeprecateDomain( ctx context.Context, DeprecateRequest *shared.DeprecateDomainRequest, opts ...yarpc.CallOption, ) error DescribeDomain( ctx context.Context, DescribeRequest *shared.DescribeDomainRequest, opts ...yarpc.CallOption, ) (*shared.DescribeDomainResponse, error) DescribeTaskList( ctx context.Context, Request *shared.DescribeTaskListRequest, opts ...yarpc.CallOption, ) (*shared.DescribeTaskListResponse, error) DescribeWorkflowExecution( ctx context.Context, DescribeRequest *shared.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*shared.DescribeWorkflowExecutionResponse, error) DiagnoseWorkflowExecution( ctx context.Context, DiagnoseRequest *shared.DiagnoseWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*shared.DiagnoseWorkflowExecutionResponse, error) FailoverDomain( ctx context.Context, FailoverRequest *shared.FailoverDomainRequest, opts ...yarpc.CallOption, ) (*shared.FailoverDomainResponse, error) GetClusterInfo( ctx context.Context, opts ...yarpc.CallOption, ) (*shared.ClusterInfo, error) GetSearchAttributes( ctx context.Context, opts ...yarpc.CallOption, ) (*shared.GetSearchAttributesResponse, error) GetTaskListsByDomain( ctx context.Context, Request *shared.GetTaskListsByDomainRequest, opts ...yarpc.CallOption, ) (*shared.GetTaskListsByDomainResponse, error) GetWorkflowExecutionHistory( ctx context.Context, GetRequest *shared.GetWorkflowExecutionHistoryRequest, opts ...yarpc.CallOption, ) (*shared.GetWorkflowExecutionHistoryResponse, error) ListArchivedWorkflowExecutions( ctx context.Context, ListRequest *shared.ListArchivedWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (*shared.ListArchivedWorkflowExecutionsResponse, error) ListClosedWorkflowExecutions( ctx context.Context, ListRequest *shared.ListClosedWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (*shared.ListClosedWorkflowExecutionsResponse, error) ListDomains( ctx context.Context, ListRequest *shared.ListDomainsRequest, opts ...yarpc.CallOption, ) (*shared.ListDomainsResponse, error) ListFailoverHistory( ctx context.Context, ListRequest *shared.ListFailoverHistoryRequest, opts ...yarpc.CallOption, ) (*shared.ListFailoverHistoryResponse, error) ListOpenWorkflowExecutions( ctx context.Context, ListRequest *shared.ListOpenWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (*shared.ListOpenWorkflowExecutionsResponse, error) ListTaskListPartitions( ctx context.Context, Request *shared.ListTaskListPartitionsRequest, opts ...yarpc.CallOption, ) (*shared.ListTaskListPartitionsResponse, error) ListWorkflowExecutions( ctx context.Context, ListRequest *shared.ListWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (*shared.ListWorkflowExecutionsResponse, error) PollForActivityTask( ctx context.Context, PollRequest *shared.PollForActivityTaskRequest, opts ...yarpc.CallOption, ) (*shared.PollForActivityTaskResponse, error) PollForDecisionTask( ctx context.Context, PollRequest *shared.PollForDecisionTaskRequest, opts ...yarpc.CallOption, ) (*shared.PollForDecisionTaskResponse, error) QueryWorkflow( ctx context.Context, QueryRequest *shared.QueryWorkflowRequest, opts ...yarpc.CallOption, ) (*shared.QueryWorkflowResponse, error) RecordActivityTaskHeartbeat( ctx context.Context, HeartbeatRequest *shared.RecordActivityTaskHeartbeatRequest, opts ...yarpc.CallOption, ) (*shared.RecordActivityTaskHeartbeatResponse, error) RecordActivityTaskHeartbeatByID( ctx context.Context, HeartbeatRequest *shared.RecordActivityTaskHeartbeatByIDRequest, opts ...yarpc.CallOption, ) (*shared.RecordActivityTaskHeartbeatResponse, error) RefreshWorkflowTasks( ctx context.Context, Request *shared.RefreshWorkflowTasksRequest, opts ...yarpc.CallOption, ) error RegisterDomain( ctx context.Context, RegisterRequest *shared.RegisterDomainRequest, opts ...yarpc.CallOption, ) error RequestCancelWorkflowExecution( ctx context.Context, CancelRequest *shared.RequestCancelWorkflowExecutionRequest, opts ...yarpc.CallOption, ) error ResetStickyTaskList( ctx context.Context, ResetRequest *shared.ResetStickyTaskListRequest, opts ...yarpc.CallOption, ) (*shared.ResetStickyTaskListResponse, error) ResetWorkflowExecution( ctx context.Context, ResetRequest *shared.ResetWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*shared.ResetWorkflowExecutionResponse, error) RespondActivityTaskCanceled( ctx context.Context, CanceledRequest *shared.RespondActivityTaskCanceledRequest, opts ...yarpc.CallOption, ) error RespondActivityTaskCanceledByID( ctx context.Context, CanceledRequest *shared.RespondActivityTaskCanceledByIDRequest, opts ...yarpc.CallOption, ) error RespondActivityTaskCompleted( ctx context.Context, CompleteRequest *shared.RespondActivityTaskCompletedRequest, opts ...yarpc.CallOption, ) error RespondActivityTaskCompletedByID( ctx context.Context, CompleteRequest *shared.RespondActivityTaskCompletedByIDRequest, opts ...yarpc.CallOption, ) error RespondActivityTaskFailed( ctx context.Context, FailRequest *shared.RespondActivityTaskFailedRequest, opts ...yarpc.CallOption, ) error RespondActivityTaskFailedByID( ctx context.Context, FailRequest *shared.RespondActivityTaskFailedByIDRequest, opts ...yarpc.CallOption, ) error RespondDecisionTaskCompleted( ctx context.Context, CompleteRequest *shared.RespondDecisionTaskCompletedRequest, opts ...yarpc.CallOption, ) (*shared.RespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed( ctx context.Context, FailedRequest *shared.RespondDecisionTaskFailedRequest, opts ...yarpc.CallOption, ) error RespondQueryTaskCompleted( ctx context.Context, CompleteRequest *shared.RespondQueryTaskCompletedRequest, opts ...yarpc.CallOption, ) error RestartWorkflowExecution( ctx context.Context, RestartRequest *shared.RestartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*shared.RestartWorkflowExecutionResponse, error) ScanWorkflowExecutions( ctx context.Context, ListRequest *shared.ListWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (*shared.ListWorkflowExecutionsResponse, error) SignalWithStartWorkflowExecution( ctx context.Context, SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*shared.StartWorkflowExecutionResponse, error) SignalWithStartWorkflowExecutionAsync( ctx context.Context, SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionAsyncRequest, opts ...yarpc.CallOption, ) (*shared.SignalWithStartWorkflowExecutionAsyncResponse, error) SignalWorkflowExecution( ctx context.Context, SignalRequest *shared.SignalWorkflowExecutionRequest, opts ...yarpc.CallOption, ) error StartWorkflowExecution( ctx context.Context, StartRequest *shared.StartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*shared.StartWorkflowExecutionResponse, error) StartWorkflowExecutionAsync( ctx context.Context, StartRequest *shared.StartWorkflowExecutionAsyncRequest, opts ...yarpc.CallOption, ) (*shared.StartWorkflowExecutionAsyncResponse, error) TerminateWorkflowExecution( ctx context.Context, TerminateRequest *shared.TerminateWorkflowExecutionRequest, opts ...yarpc.CallOption, ) error UpdateDomain( ctx context.Context, UpdateRequest *shared.UpdateDomainRequest, opts ...yarpc.CallOption, ) (*shared.UpdateDomainResponse, error) } // New builds a new client for the WorkflowService service. // // client := workflowserviceclient.New(dispatcher.ClientConfig("workflowservice")) func New(c transport.ClientConfig, opts ...thrift.ClientOption) Interface { return client{ c: thrift.New(thrift.Config{ Service: "WorkflowService", ClientConfig: c, }, opts...), nwc: thrift.NewNoWire(thrift.Config{ Service: "WorkflowService", ClientConfig: c, }, opts...), } } func init() { yarpc.RegisterClientBuilder( func(c transport.ClientConfig, f reflect.StructField) Interface { return New(c, thrift.ClientBuilderOptions(c, f)...) }, ) } type client struct { c thrift.Client nwc thrift.NoWireClient } func (c client) CountWorkflowExecutions( ctx context.Context, _CountRequest *shared.CountWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.CountWorkflowExecutionsResponse, err error) { var result cadence.WorkflowService_CountWorkflowExecutions_Result args := cadence.WorkflowService_CountWorkflowExecutions_Helper.Args(_CountRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_CountWorkflowExecutions_Helper.UnwrapResponse(&result) return } func (c client) DeleteDomain( ctx context.Context, _DeleteRequest *shared.DeleteDomainRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_DeleteDomain_Result args := cadence.WorkflowService_DeleteDomain_Helper.Args(_DeleteRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_DeleteDomain_Helper.UnwrapResponse(&result) return } func (c client) DeprecateDomain( ctx context.Context, _DeprecateRequest *shared.DeprecateDomainRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_DeprecateDomain_Result args := cadence.WorkflowService_DeprecateDomain_Helper.Args(_DeprecateRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_DeprecateDomain_Helper.UnwrapResponse(&result) return } func (c client) DescribeDomain( ctx context.Context, _DescribeRequest *shared.DescribeDomainRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeDomainResponse, err error) { var result cadence.WorkflowService_DescribeDomain_Result args := cadence.WorkflowService_DescribeDomain_Helper.Args(_DescribeRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_DescribeDomain_Helper.UnwrapResponse(&result) return } func (c client) DescribeTaskList( ctx context.Context, _Request *shared.DescribeTaskListRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeTaskListResponse, err error) { var result cadence.WorkflowService_DescribeTaskList_Result args := cadence.WorkflowService_DescribeTaskList_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_DescribeTaskList_Helper.UnwrapResponse(&result) return } func (c client) DescribeWorkflowExecution( ctx context.Context, _DescribeRequest *shared.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeWorkflowExecutionResponse, err error) { var result cadence.WorkflowService_DescribeWorkflowExecution_Result args := cadence.WorkflowService_DescribeWorkflowExecution_Helper.Args(_DescribeRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_DescribeWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) DiagnoseWorkflowExecution( ctx context.Context, _DiagnoseRequest *shared.DiagnoseWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.DiagnoseWorkflowExecutionResponse, err error) { var result cadence.WorkflowService_DiagnoseWorkflowExecution_Result args := cadence.WorkflowService_DiagnoseWorkflowExecution_Helper.Args(_DiagnoseRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_DiagnoseWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) FailoverDomain( ctx context.Context, _FailoverRequest *shared.FailoverDomainRequest, opts ...yarpc.CallOption, ) (success *shared.FailoverDomainResponse, err error) { var result cadence.WorkflowService_FailoverDomain_Result args := cadence.WorkflowService_FailoverDomain_Helper.Args(_FailoverRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_FailoverDomain_Helper.UnwrapResponse(&result) return } func (c client) GetClusterInfo( ctx context.Context, opts ...yarpc.CallOption, ) (success *shared.ClusterInfo, err error) { var result cadence.WorkflowService_GetClusterInfo_Result args := cadence.WorkflowService_GetClusterInfo_Helper.Args() if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_GetClusterInfo_Helper.UnwrapResponse(&result) return } func (c client) GetSearchAttributes( ctx context.Context, opts ...yarpc.CallOption, ) (success *shared.GetSearchAttributesResponse, err error) { var result cadence.WorkflowService_GetSearchAttributes_Result args := cadence.WorkflowService_GetSearchAttributes_Helper.Args() if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_GetSearchAttributes_Helper.UnwrapResponse(&result) return } func (c client) GetTaskListsByDomain( ctx context.Context, _Request *shared.GetTaskListsByDomainRequest, opts ...yarpc.CallOption, ) (success *shared.GetTaskListsByDomainResponse, err error) { var result cadence.WorkflowService_GetTaskListsByDomain_Result args := cadence.WorkflowService_GetTaskListsByDomain_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_GetTaskListsByDomain_Helper.UnwrapResponse(&result) return } func (c client) GetWorkflowExecutionHistory( ctx context.Context, _GetRequest *shared.GetWorkflowExecutionHistoryRequest, opts ...yarpc.CallOption, ) (success *shared.GetWorkflowExecutionHistoryResponse, err error) { var result cadence.WorkflowService_GetWorkflowExecutionHistory_Result args := cadence.WorkflowService_GetWorkflowExecutionHistory_Helper.Args(_GetRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_GetWorkflowExecutionHistory_Helper.UnwrapResponse(&result) return } func (c client) ListArchivedWorkflowExecutions( ctx context.Context, _ListRequest *shared.ListArchivedWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListArchivedWorkflowExecutionsResponse, err error) { var result cadence.WorkflowService_ListArchivedWorkflowExecutions_Result args := cadence.WorkflowService_ListArchivedWorkflowExecutions_Helper.Args(_ListRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_ListArchivedWorkflowExecutions_Helper.UnwrapResponse(&result) return } func (c client) ListClosedWorkflowExecutions( ctx context.Context, _ListRequest *shared.ListClosedWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListClosedWorkflowExecutionsResponse, err error) { var result cadence.WorkflowService_ListClosedWorkflowExecutions_Result args := cadence.WorkflowService_ListClosedWorkflowExecutions_Helper.Args(_ListRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_ListClosedWorkflowExecutions_Helper.UnwrapResponse(&result) return } func (c client) ListDomains( ctx context.Context, _ListRequest *shared.ListDomainsRequest, opts ...yarpc.CallOption, ) (success *shared.ListDomainsResponse, err error) { var result cadence.WorkflowService_ListDomains_Result args := cadence.WorkflowService_ListDomains_Helper.Args(_ListRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_ListDomains_Helper.UnwrapResponse(&result) return } func (c client) ListFailoverHistory( ctx context.Context, _ListRequest *shared.ListFailoverHistoryRequest, opts ...yarpc.CallOption, ) (success *shared.ListFailoverHistoryResponse, err error) { var result cadence.WorkflowService_ListFailoverHistory_Result args := cadence.WorkflowService_ListFailoverHistory_Helper.Args(_ListRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_ListFailoverHistory_Helper.UnwrapResponse(&result) return } func (c client) ListOpenWorkflowExecutions( ctx context.Context, _ListRequest *shared.ListOpenWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListOpenWorkflowExecutionsResponse, err error) { var result cadence.WorkflowService_ListOpenWorkflowExecutions_Result args := cadence.WorkflowService_ListOpenWorkflowExecutions_Helper.Args(_ListRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_ListOpenWorkflowExecutions_Helper.UnwrapResponse(&result) return } func (c client) ListTaskListPartitions( ctx context.Context, _Request *shared.ListTaskListPartitionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListTaskListPartitionsResponse, err error) { var result cadence.WorkflowService_ListTaskListPartitions_Result args := cadence.WorkflowService_ListTaskListPartitions_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_ListTaskListPartitions_Helper.UnwrapResponse(&result) return } func (c client) ListWorkflowExecutions( ctx context.Context, _ListRequest *shared.ListWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListWorkflowExecutionsResponse, err error) { var result cadence.WorkflowService_ListWorkflowExecutions_Result args := cadence.WorkflowService_ListWorkflowExecutions_Helper.Args(_ListRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_ListWorkflowExecutions_Helper.UnwrapResponse(&result) return } func (c client) PollForActivityTask( ctx context.Context, _PollRequest *shared.PollForActivityTaskRequest, opts ...yarpc.CallOption, ) (success *shared.PollForActivityTaskResponse, err error) { var result cadence.WorkflowService_PollForActivityTask_Result args := cadence.WorkflowService_PollForActivityTask_Helper.Args(_PollRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_PollForActivityTask_Helper.UnwrapResponse(&result) return } func (c client) PollForDecisionTask( ctx context.Context, _PollRequest *shared.PollForDecisionTaskRequest, opts ...yarpc.CallOption, ) (success *shared.PollForDecisionTaskResponse, err error) { var result cadence.WorkflowService_PollForDecisionTask_Result args := cadence.WorkflowService_PollForDecisionTask_Helper.Args(_PollRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_PollForDecisionTask_Helper.UnwrapResponse(&result) return } func (c client) QueryWorkflow( ctx context.Context, _QueryRequest *shared.QueryWorkflowRequest, opts ...yarpc.CallOption, ) (success *shared.QueryWorkflowResponse, err error) { var result cadence.WorkflowService_QueryWorkflow_Result args := cadence.WorkflowService_QueryWorkflow_Helper.Args(_QueryRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_QueryWorkflow_Helper.UnwrapResponse(&result) return } func (c client) RecordActivityTaskHeartbeat( ctx context.Context, _HeartbeatRequest *shared.RecordActivityTaskHeartbeatRequest, opts ...yarpc.CallOption, ) (success *shared.RecordActivityTaskHeartbeatResponse, err error) { var result cadence.WorkflowService_RecordActivityTaskHeartbeat_Result args := cadence.WorkflowService_RecordActivityTaskHeartbeat_Helper.Args(_HeartbeatRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_RecordActivityTaskHeartbeat_Helper.UnwrapResponse(&result) return } func (c client) RecordActivityTaskHeartbeatByID( ctx context.Context, _HeartbeatRequest *shared.RecordActivityTaskHeartbeatByIDRequest, opts ...yarpc.CallOption, ) (success *shared.RecordActivityTaskHeartbeatResponse, err error) { var result cadence.WorkflowService_RecordActivityTaskHeartbeatByID_Result args := cadence.WorkflowService_RecordActivityTaskHeartbeatByID_Helper.Args(_HeartbeatRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_RecordActivityTaskHeartbeatByID_Helper.UnwrapResponse(&result) return } func (c client) RefreshWorkflowTasks( ctx context.Context, _Request *shared.RefreshWorkflowTasksRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RefreshWorkflowTasks_Result args := cadence.WorkflowService_RefreshWorkflowTasks_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RefreshWorkflowTasks_Helper.UnwrapResponse(&result) return } func (c client) RegisterDomain( ctx context.Context, _RegisterRequest *shared.RegisterDomainRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RegisterDomain_Result args := cadence.WorkflowService_RegisterDomain_Helper.Args(_RegisterRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RegisterDomain_Helper.UnwrapResponse(&result) return } func (c client) RequestCancelWorkflowExecution( ctx context.Context, _CancelRequest *shared.RequestCancelWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RequestCancelWorkflowExecution_Result args := cadence.WorkflowService_RequestCancelWorkflowExecution_Helper.Args(_CancelRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RequestCancelWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) ResetStickyTaskList( ctx context.Context, _ResetRequest *shared.ResetStickyTaskListRequest, opts ...yarpc.CallOption, ) (success *shared.ResetStickyTaskListResponse, err error) { var result cadence.WorkflowService_ResetStickyTaskList_Result args := cadence.WorkflowService_ResetStickyTaskList_Helper.Args(_ResetRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_ResetStickyTaskList_Helper.UnwrapResponse(&result) return } func (c client) ResetWorkflowExecution( ctx context.Context, _ResetRequest *shared.ResetWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.ResetWorkflowExecutionResponse, err error) { var result cadence.WorkflowService_ResetWorkflowExecution_Result args := cadence.WorkflowService_ResetWorkflowExecution_Helper.Args(_ResetRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_ResetWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) RespondActivityTaskCanceled( ctx context.Context, _CanceledRequest *shared.RespondActivityTaskCanceledRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RespondActivityTaskCanceled_Result args := cadence.WorkflowService_RespondActivityTaskCanceled_Helper.Args(_CanceledRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RespondActivityTaskCanceled_Helper.UnwrapResponse(&result) return } func (c client) RespondActivityTaskCanceledByID( ctx context.Context, _CanceledRequest *shared.RespondActivityTaskCanceledByIDRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RespondActivityTaskCanceledByID_Result args := cadence.WorkflowService_RespondActivityTaskCanceledByID_Helper.Args(_CanceledRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RespondActivityTaskCanceledByID_Helper.UnwrapResponse(&result) return } func (c client) RespondActivityTaskCompleted( ctx context.Context, _CompleteRequest *shared.RespondActivityTaskCompletedRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RespondActivityTaskCompleted_Result args := cadence.WorkflowService_RespondActivityTaskCompleted_Helper.Args(_CompleteRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RespondActivityTaskCompleted_Helper.UnwrapResponse(&result) return } func (c client) RespondActivityTaskCompletedByID( ctx context.Context, _CompleteRequest *shared.RespondActivityTaskCompletedByIDRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RespondActivityTaskCompletedByID_Result args := cadence.WorkflowService_RespondActivityTaskCompletedByID_Helper.Args(_CompleteRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RespondActivityTaskCompletedByID_Helper.UnwrapResponse(&result) return } func (c client) RespondActivityTaskFailed( ctx context.Context, _FailRequest *shared.RespondActivityTaskFailedRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RespondActivityTaskFailed_Result args := cadence.WorkflowService_RespondActivityTaskFailed_Helper.Args(_FailRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RespondActivityTaskFailed_Helper.UnwrapResponse(&result) return } func (c client) RespondActivityTaskFailedByID( ctx context.Context, _FailRequest *shared.RespondActivityTaskFailedByIDRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RespondActivityTaskFailedByID_Result args := cadence.WorkflowService_RespondActivityTaskFailedByID_Helper.Args(_FailRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RespondActivityTaskFailedByID_Helper.UnwrapResponse(&result) return } func (c client) RespondDecisionTaskCompleted( ctx context.Context, _CompleteRequest *shared.RespondDecisionTaskCompletedRequest, opts ...yarpc.CallOption, ) (success *shared.RespondDecisionTaskCompletedResponse, err error) { var result cadence.WorkflowService_RespondDecisionTaskCompleted_Result args := cadence.WorkflowService_RespondDecisionTaskCompleted_Helper.Args(_CompleteRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_RespondDecisionTaskCompleted_Helper.UnwrapResponse(&result) return } func (c client) RespondDecisionTaskFailed( ctx context.Context, _FailedRequest *shared.RespondDecisionTaskFailedRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RespondDecisionTaskFailed_Result args := cadence.WorkflowService_RespondDecisionTaskFailed_Helper.Args(_FailedRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RespondDecisionTaskFailed_Helper.UnwrapResponse(&result) return } func (c client) RespondQueryTaskCompleted( ctx context.Context, _CompleteRequest *shared.RespondQueryTaskCompletedRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_RespondQueryTaskCompleted_Result args := cadence.WorkflowService_RespondQueryTaskCompleted_Helper.Args(_CompleteRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_RespondQueryTaskCompleted_Helper.UnwrapResponse(&result) return } func (c client) RestartWorkflowExecution( ctx context.Context, _RestartRequest *shared.RestartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.RestartWorkflowExecutionResponse, err error) { var result cadence.WorkflowService_RestartWorkflowExecution_Result args := cadence.WorkflowService_RestartWorkflowExecution_Helper.Args(_RestartRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_RestartWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) ScanWorkflowExecutions( ctx context.Context, _ListRequest *shared.ListWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListWorkflowExecutionsResponse, err error) { var result cadence.WorkflowService_ScanWorkflowExecutions_Result args := cadence.WorkflowService_ScanWorkflowExecutions_Helper.Args(_ListRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_ScanWorkflowExecutions_Helper.UnwrapResponse(&result) return } func (c client) SignalWithStartWorkflowExecution( ctx context.Context, _SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.StartWorkflowExecutionResponse, err error) { var result cadence.WorkflowService_SignalWithStartWorkflowExecution_Result args := cadence.WorkflowService_SignalWithStartWorkflowExecution_Helper.Args(_SignalWithStartRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_SignalWithStartWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) SignalWithStartWorkflowExecutionAsync( ctx context.Context, _SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionAsyncRequest, opts ...yarpc.CallOption, ) (success *shared.SignalWithStartWorkflowExecutionAsyncResponse, err error) { var result cadence.WorkflowService_SignalWithStartWorkflowExecutionAsync_Result args := cadence.WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper.Args(_SignalWithStartRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper.UnwrapResponse(&result) return } func (c client) SignalWorkflowExecution( ctx context.Context, _SignalRequest *shared.SignalWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_SignalWorkflowExecution_Result args := cadence.WorkflowService_SignalWorkflowExecution_Helper.Args(_SignalRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_SignalWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) StartWorkflowExecution( ctx context.Context, _StartRequest *shared.StartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.StartWorkflowExecutionResponse, err error) { var result cadence.WorkflowService_StartWorkflowExecution_Result args := cadence.WorkflowService_StartWorkflowExecution_Helper.Args(_StartRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_StartWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) StartWorkflowExecutionAsync( ctx context.Context, _StartRequest *shared.StartWorkflowExecutionAsyncRequest, opts ...yarpc.CallOption, ) (success *shared.StartWorkflowExecutionAsyncResponse, err error) { var result cadence.WorkflowService_StartWorkflowExecutionAsync_Result args := cadence.WorkflowService_StartWorkflowExecutionAsync_Helper.Args(_StartRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_StartWorkflowExecutionAsync_Helper.UnwrapResponse(&result) return } func (c client) TerminateWorkflowExecution( ctx context.Context, _TerminateRequest *shared.TerminateWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { var result cadence.WorkflowService_TerminateWorkflowExecution_Result args := cadence.WorkflowService_TerminateWorkflowExecution_Helper.Args(_TerminateRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = cadence.WorkflowService_TerminateWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) UpdateDomain( ctx context.Context, _UpdateRequest *shared.UpdateDomainRequest, opts ...yarpc.CallOption, ) (success *shared.UpdateDomainResponse, err error) { var result cadence.WorkflowService_UpdateDomain_Result args := cadence.WorkflowService_UpdateDomain_Helper.Args(_UpdateRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = cadence.WorkflowService_UpdateDomain_Helper.UnwrapResponse(&result) return } ================================================ FILE: .gen/go/cadence/workflowservicefx/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package workflowservicefx import ( fx "go.uber.org/fx" yarpc "go.uber.org/yarpc" transport "go.uber.org/yarpc/api/transport" restriction "go.uber.org/yarpc/api/x/restriction" thrift "go.uber.org/yarpc/encoding/thrift" workflowserviceclient "github.com/uber/cadence/.gen/go/cadence/workflowserviceclient" ) // Params defines the dependencies for the WorkflowService client. type Params struct { fx.In Provider yarpc.ClientConfig Restriction restriction.Checker `optional:"true"` } // Result defines the output of the WorkflowService client module. It provides a // WorkflowService client to an Fx application. type Result struct { fx.Out Client workflowserviceclient.Interface // We are using an fx.Out struct here instead of just returning a client // so that we can add more values or add named versions of the client in // the future without breaking any existing code. } // Client provides a WorkflowService client to an Fx application using the given name // for routing. // // fx.Provide( // workflowservicefx.Client("..."), // newHandler, // ) func Client(name string, opts ...thrift.ClientOption) interface{} { return func(p Params) Result { cc := p.Provider.ClientConfig(name) if namer, ok := cc.GetUnaryOutbound().(transport.Namer); ok && p.Restriction != nil { if err := p.Restriction.Check(thrift.Encoding, namer.TransportName()); err != nil { panic(err.Error()) } } client := workflowserviceclient.New(cc, opts...) return Result{Client: client} } } ================================================ FILE: .gen/go/cadence/workflowservicefx/doc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated // Package workflowservicefx provides better integration for Fx for services // implementing or calling WorkflowService. // // # Clients // // If you are making requests to WorkflowService, use the Client function to inject a // WorkflowService client into your container. // // fx.Provide(workflowservicefx.Client("...")) // // # Servers // // If you are implementing WorkflowService, provide a workflowserviceserver.Interface into // the container and use the Server function. // // Given, // // func NewWorkflowServiceHandler() workflowserviceserver.Interface // // You can do the following to have the procedures of WorkflowService made available // to an Fx application. // // fx.Provide( // NewWorkflowServiceHandler, // workflowservicefx.Server(), // ) package workflowservicefx ================================================ FILE: .gen/go/cadence/workflowservicefx/server.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package workflowservicefx import ( fx "go.uber.org/fx" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" workflowserviceserver "github.com/uber/cadence/.gen/go/cadence/workflowserviceserver" ) // ServerParams defines the dependencies for the WorkflowService server. type ServerParams struct { fx.In Handler workflowserviceserver.Interface } // ServerResult defines the output of WorkflowService server module. It provides the // procedures of a WorkflowService handler to an Fx application. // // The procedures are provided to the "yarpcfx" value group. Dig 1.2 or newer // must be used for this feature to work. type ServerResult struct { fx.Out Procedures []transport.Procedure `group:"yarpcfx"` } // Server provides procedures for WorkflowService to an Fx application. It expects a // workflowservicefx.Interface to be present in the container. // // fx.Provide( // func(h *MyWorkflowServiceHandler) workflowserviceserver.Interface { // return h // }, // workflowservicefx.Server(), // ) func Server(opts ...thrift.RegisterOption) interface{} { return func(p ServerParams) ServerResult { procedures := workflowserviceserver.New(p.Handler, opts...) return ServerResult{Procedures: procedures} } } ================================================ FILE: .gen/go/cadence/workflowserviceserver/server.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package workflowserviceserver import ( context "context" stream "go.uber.org/thriftrw/protocol/stream" wire "go.uber.org/thriftrw/wire" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" yarpcerrors "go.uber.org/yarpc/yarpcerrors" cadence "github.com/uber/cadence/.gen/go/cadence" shared "github.com/uber/cadence/.gen/go/shared" ) // Interface is the server-side interface for the WorkflowService service. type Interface interface { CountWorkflowExecutions( ctx context.Context, CountRequest *shared.CountWorkflowExecutionsRequest, ) (*shared.CountWorkflowExecutionsResponse, error) DeleteDomain( ctx context.Context, DeleteRequest *shared.DeleteDomainRequest, ) error DeprecateDomain( ctx context.Context, DeprecateRequest *shared.DeprecateDomainRequest, ) error DescribeDomain( ctx context.Context, DescribeRequest *shared.DescribeDomainRequest, ) (*shared.DescribeDomainResponse, error) DescribeTaskList( ctx context.Context, Request *shared.DescribeTaskListRequest, ) (*shared.DescribeTaskListResponse, error) DescribeWorkflowExecution( ctx context.Context, DescribeRequest *shared.DescribeWorkflowExecutionRequest, ) (*shared.DescribeWorkflowExecutionResponse, error) DiagnoseWorkflowExecution( ctx context.Context, DiagnoseRequest *shared.DiagnoseWorkflowExecutionRequest, ) (*shared.DiagnoseWorkflowExecutionResponse, error) FailoverDomain( ctx context.Context, FailoverRequest *shared.FailoverDomainRequest, ) (*shared.FailoverDomainResponse, error) GetClusterInfo( ctx context.Context, ) (*shared.ClusterInfo, error) GetSearchAttributes( ctx context.Context, ) (*shared.GetSearchAttributesResponse, error) GetTaskListsByDomain( ctx context.Context, Request *shared.GetTaskListsByDomainRequest, ) (*shared.GetTaskListsByDomainResponse, error) GetWorkflowExecutionHistory( ctx context.Context, GetRequest *shared.GetWorkflowExecutionHistoryRequest, ) (*shared.GetWorkflowExecutionHistoryResponse, error) ListArchivedWorkflowExecutions( ctx context.Context, ListRequest *shared.ListArchivedWorkflowExecutionsRequest, ) (*shared.ListArchivedWorkflowExecutionsResponse, error) ListClosedWorkflowExecutions( ctx context.Context, ListRequest *shared.ListClosedWorkflowExecutionsRequest, ) (*shared.ListClosedWorkflowExecutionsResponse, error) ListDomains( ctx context.Context, ListRequest *shared.ListDomainsRequest, ) (*shared.ListDomainsResponse, error) ListFailoverHistory( ctx context.Context, ListRequest *shared.ListFailoverHistoryRequest, ) (*shared.ListFailoverHistoryResponse, error) ListOpenWorkflowExecutions( ctx context.Context, ListRequest *shared.ListOpenWorkflowExecutionsRequest, ) (*shared.ListOpenWorkflowExecutionsResponse, error) ListTaskListPartitions( ctx context.Context, Request *shared.ListTaskListPartitionsRequest, ) (*shared.ListTaskListPartitionsResponse, error) ListWorkflowExecutions( ctx context.Context, ListRequest *shared.ListWorkflowExecutionsRequest, ) (*shared.ListWorkflowExecutionsResponse, error) PollForActivityTask( ctx context.Context, PollRequest *shared.PollForActivityTaskRequest, ) (*shared.PollForActivityTaskResponse, error) PollForDecisionTask( ctx context.Context, PollRequest *shared.PollForDecisionTaskRequest, ) (*shared.PollForDecisionTaskResponse, error) QueryWorkflow( ctx context.Context, QueryRequest *shared.QueryWorkflowRequest, ) (*shared.QueryWorkflowResponse, error) RecordActivityTaskHeartbeat( ctx context.Context, HeartbeatRequest *shared.RecordActivityTaskHeartbeatRequest, ) (*shared.RecordActivityTaskHeartbeatResponse, error) RecordActivityTaskHeartbeatByID( ctx context.Context, HeartbeatRequest *shared.RecordActivityTaskHeartbeatByIDRequest, ) (*shared.RecordActivityTaskHeartbeatResponse, error) RefreshWorkflowTasks( ctx context.Context, Request *shared.RefreshWorkflowTasksRequest, ) error RegisterDomain( ctx context.Context, RegisterRequest *shared.RegisterDomainRequest, ) error RequestCancelWorkflowExecution( ctx context.Context, CancelRequest *shared.RequestCancelWorkflowExecutionRequest, ) error ResetStickyTaskList( ctx context.Context, ResetRequest *shared.ResetStickyTaskListRequest, ) (*shared.ResetStickyTaskListResponse, error) ResetWorkflowExecution( ctx context.Context, ResetRequest *shared.ResetWorkflowExecutionRequest, ) (*shared.ResetWorkflowExecutionResponse, error) RespondActivityTaskCanceled( ctx context.Context, CanceledRequest *shared.RespondActivityTaskCanceledRequest, ) error RespondActivityTaskCanceledByID( ctx context.Context, CanceledRequest *shared.RespondActivityTaskCanceledByIDRequest, ) error RespondActivityTaskCompleted( ctx context.Context, CompleteRequest *shared.RespondActivityTaskCompletedRequest, ) error RespondActivityTaskCompletedByID( ctx context.Context, CompleteRequest *shared.RespondActivityTaskCompletedByIDRequest, ) error RespondActivityTaskFailed( ctx context.Context, FailRequest *shared.RespondActivityTaskFailedRequest, ) error RespondActivityTaskFailedByID( ctx context.Context, FailRequest *shared.RespondActivityTaskFailedByIDRequest, ) error RespondDecisionTaskCompleted( ctx context.Context, CompleteRequest *shared.RespondDecisionTaskCompletedRequest, ) (*shared.RespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed( ctx context.Context, FailedRequest *shared.RespondDecisionTaskFailedRequest, ) error RespondQueryTaskCompleted( ctx context.Context, CompleteRequest *shared.RespondQueryTaskCompletedRequest, ) error RestartWorkflowExecution( ctx context.Context, RestartRequest *shared.RestartWorkflowExecutionRequest, ) (*shared.RestartWorkflowExecutionResponse, error) ScanWorkflowExecutions( ctx context.Context, ListRequest *shared.ListWorkflowExecutionsRequest, ) (*shared.ListWorkflowExecutionsResponse, error) SignalWithStartWorkflowExecution( ctx context.Context, SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionRequest, ) (*shared.StartWorkflowExecutionResponse, error) SignalWithStartWorkflowExecutionAsync( ctx context.Context, SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionAsyncRequest, ) (*shared.SignalWithStartWorkflowExecutionAsyncResponse, error) SignalWorkflowExecution( ctx context.Context, SignalRequest *shared.SignalWorkflowExecutionRequest, ) error StartWorkflowExecution( ctx context.Context, StartRequest *shared.StartWorkflowExecutionRequest, ) (*shared.StartWorkflowExecutionResponse, error) StartWorkflowExecutionAsync( ctx context.Context, StartRequest *shared.StartWorkflowExecutionAsyncRequest, ) (*shared.StartWorkflowExecutionAsyncResponse, error) TerminateWorkflowExecution( ctx context.Context, TerminateRequest *shared.TerminateWorkflowExecutionRequest, ) error UpdateDomain( ctx context.Context, UpdateRequest *shared.UpdateDomainRequest, ) (*shared.UpdateDomainResponse, error) } // New prepares an implementation of the WorkflowService service for // registration. // // handler := WorkflowServiceHandler{} // dispatcher.Register(workflowserviceserver.New(handler)) func New(impl Interface, opts ...thrift.RegisterOption) []transport.Procedure { h := handler{impl} service := thrift.Service{ Name: "WorkflowService", Methods: []thrift.Method{ thrift.Method{ Name: "CountWorkflowExecutions", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.CountWorkflowExecutions), NoWire: countworkflowexecutions_NoWireHandler{impl}, }, Signature: "CountWorkflowExecutions(CountRequest *shared.CountWorkflowExecutionsRequest) (*shared.CountWorkflowExecutionsResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "DeleteDomain", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DeleteDomain), NoWire: deletedomain_NoWireHandler{impl}, }, Signature: "DeleteDomain(DeleteRequest *shared.DeleteDomainRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "DeprecateDomain", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DeprecateDomain), NoWire: deprecatedomain_NoWireHandler{impl}, }, Signature: "DeprecateDomain(DeprecateRequest *shared.DeprecateDomainRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "DescribeDomain", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeDomain), NoWire: describedomain_NoWireHandler{impl}, }, Signature: "DescribeDomain(DescribeRequest *shared.DescribeDomainRequest) (*shared.DescribeDomainResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "DescribeTaskList", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeTaskList), NoWire: describetasklist_NoWireHandler{impl}, }, Signature: "DescribeTaskList(Request *shared.DescribeTaskListRequest) (*shared.DescribeTaskListResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "DescribeWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeWorkflowExecution), NoWire: describeworkflowexecution_NoWireHandler{impl}, }, Signature: "DescribeWorkflowExecution(DescribeRequest *shared.DescribeWorkflowExecutionRequest) (*shared.DescribeWorkflowExecutionResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "DiagnoseWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DiagnoseWorkflowExecution), NoWire: diagnoseworkflowexecution_NoWireHandler{impl}, }, Signature: "DiagnoseWorkflowExecution(DiagnoseRequest *shared.DiagnoseWorkflowExecutionRequest) (*shared.DiagnoseWorkflowExecutionResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "FailoverDomain", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.FailoverDomain), NoWire: failoverdomain_NoWireHandler{impl}, }, Signature: "FailoverDomain(FailoverRequest *shared.FailoverDomainRequest) (*shared.FailoverDomainResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "GetClusterInfo", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetClusterInfo), NoWire: getclusterinfo_NoWireHandler{impl}, }, Signature: "GetClusterInfo() (*shared.ClusterInfo)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "GetSearchAttributes", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetSearchAttributes), NoWire: getsearchattributes_NoWireHandler{impl}, }, Signature: "GetSearchAttributes() (*shared.GetSearchAttributesResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "GetTaskListsByDomain", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetTaskListsByDomain), NoWire: gettasklistsbydomain_NoWireHandler{impl}, }, Signature: "GetTaskListsByDomain(Request *shared.GetTaskListsByDomainRequest) (*shared.GetTaskListsByDomainResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "GetWorkflowExecutionHistory", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetWorkflowExecutionHistory), NoWire: getworkflowexecutionhistory_NoWireHandler{impl}, }, Signature: "GetWorkflowExecutionHistory(GetRequest *shared.GetWorkflowExecutionHistoryRequest) (*shared.GetWorkflowExecutionHistoryResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "ListArchivedWorkflowExecutions", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ListArchivedWorkflowExecutions), NoWire: listarchivedworkflowexecutions_NoWireHandler{impl}, }, Signature: "ListArchivedWorkflowExecutions(ListRequest *shared.ListArchivedWorkflowExecutionsRequest) (*shared.ListArchivedWorkflowExecutionsResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "ListClosedWorkflowExecutions", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ListClosedWorkflowExecutions), NoWire: listclosedworkflowexecutions_NoWireHandler{impl}, }, Signature: "ListClosedWorkflowExecutions(ListRequest *shared.ListClosedWorkflowExecutionsRequest) (*shared.ListClosedWorkflowExecutionsResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "ListDomains", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ListDomains), NoWire: listdomains_NoWireHandler{impl}, }, Signature: "ListDomains(ListRequest *shared.ListDomainsRequest) (*shared.ListDomainsResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "ListFailoverHistory", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ListFailoverHistory), NoWire: listfailoverhistory_NoWireHandler{impl}, }, Signature: "ListFailoverHistory(ListRequest *shared.ListFailoverHistoryRequest) (*shared.ListFailoverHistoryResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "ListOpenWorkflowExecutions", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ListOpenWorkflowExecutions), NoWire: listopenworkflowexecutions_NoWireHandler{impl}, }, Signature: "ListOpenWorkflowExecutions(ListRequest *shared.ListOpenWorkflowExecutionsRequest) (*shared.ListOpenWorkflowExecutionsResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "ListTaskListPartitions", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ListTaskListPartitions), NoWire: listtasklistpartitions_NoWireHandler{impl}, }, Signature: "ListTaskListPartitions(Request *shared.ListTaskListPartitionsRequest) (*shared.ListTaskListPartitionsResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "ListWorkflowExecutions", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ListWorkflowExecutions), NoWire: listworkflowexecutions_NoWireHandler{impl}, }, Signature: "ListWorkflowExecutions(ListRequest *shared.ListWorkflowExecutionsRequest) (*shared.ListWorkflowExecutionsResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "PollForActivityTask", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.PollForActivityTask), NoWire: pollforactivitytask_NoWireHandler{impl}, }, Signature: "PollForActivityTask(PollRequest *shared.PollForActivityTaskRequest) (*shared.PollForActivityTaskResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "PollForDecisionTask", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.PollForDecisionTask), NoWire: pollfordecisiontask_NoWireHandler{impl}, }, Signature: "PollForDecisionTask(PollRequest *shared.PollForDecisionTaskRequest) (*shared.PollForDecisionTaskResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "QueryWorkflow", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.QueryWorkflow), NoWire: queryworkflow_NoWireHandler{impl}, }, Signature: "QueryWorkflow(QueryRequest *shared.QueryWorkflowRequest) (*shared.QueryWorkflowResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RecordActivityTaskHeartbeat", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RecordActivityTaskHeartbeat), NoWire: recordactivitytaskheartbeat_NoWireHandler{impl}, }, Signature: "RecordActivityTaskHeartbeat(HeartbeatRequest *shared.RecordActivityTaskHeartbeatRequest) (*shared.RecordActivityTaskHeartbeatResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RecordActivityTaskHeartbeatByID", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RecordActivityTaskHeartbeatByID), NoWire: recordactivitytaskheartbeatbyid_NoWireHandler{impl}, }, Signature: "RecordActivityTaskHeartbeatByID(HeartbeatRequest *shared.RecordActivityTaskHeartbeatByIDRequest) (*shared.RecordActivityTaskHeartbeatResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RefreshWorkflowTasks", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RefreshWorkflowTasks), NoWire: refreshworkflowtasks_NoWireHandler{impl}, }, Signature: "RefreshWorkflowTasks(Request *shared.RefreshWorkflowTasksRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RegisterDomain", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RegisterDomain), NoWire: registerdomain_NoWireHandler{impl}, }, Signature: "RegisterDomain(RegisterRequest *shared.RegisterDomainRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RequestCancelWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RequestCancelWorkflowExecution), NoWire: requestcancelworkflowexecution_NoWireHandler{impl}, }, Signature: "RequestCancelWorkflowExecution(CancelRequest *shared.RequestCancelWorkflowExecutionRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "ResetStickyTaskList", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ResetStickyTaskList), NoWire: resetstickytasklist_NoWireHandler{impl}, }, Signature: "ResetStickyTaskList(ResetRequest *shared.ResetStickyTaskListRequest) (*shared.ResetStickyTaskListResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "ResetWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ResetWorkflowExecution), NoWire: resetworkflowexecution_NoWireHandler{impl}, }, Signature: "ResetWorkflowExecution(ResetRequest *shared.ResetWorkflowExecutionRequest) (*shared.ResetWorkflowExecutionResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RespondActivityTaskCanceled", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondActivityTaskCanceled), NoWire: respondactivitytaskcanceled_NoWireHandler{impl}, }, Signature: "RespondActivityTaskCanceled(CanceledRequest *shared.RespondActivityTaskCanceledRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RespondActivityTaskCanceledByID", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondActivityTaskCanceledByID), NoWire: respondactivitytaskcanceledbyid_NoWireHandler{impl}, }, Signature: "RespondActivityTaskCanceledByID(CanceledRequest *shared.RespondActivityTaskCanceledByIDRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RespondActivityTaskCompleted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondActivityTaskCompleted), NoWire: respondactivitytaskcompleted_NoWireHandler{impl}, }, Signature: "RespondActivityTaskCompleted(CompleteRequest *shared.RespondActivityTaskCompletedRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RespondActivityTaskCompletedByID", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondActivityTaskCompletedByID), NoWire: respondactivitytaskcompletedbyid_NoWireHandler{impl}, }, Signature: "RespondActivityTaskCompletedByID(CompleteRequest *shared.RespondActivityTaskCompletedByIDRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RespondActivityTaskFailed", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondActivityTaskFailed), NoWire: respondactivitytaskfailed_NoWireHandler{impl}, }, Signature: "RespondActivityTaskFailed(FailRequest *shared.RespondActivityTaskFailedRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RespondActivityTaskFailedByID", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondActivityTaskFailedByID), NoWire: respondactivitytaskfailedbyid_NoWireHandler{impl}, }, Signature: "RespondActivityTaskFailedByID(FailRequest *shared.RespondActivityTaskFailedByIDRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RespondDecisionTaskCompleted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondDecisionTaskCompleted), NoWire: responddecisiontaskcompleted_NoWireHandler{impl}, }, Signature: "RespondDecisionTaskCompleted(CompleteRequest *shared.RespondDecisionTaskCompletedRequest) (*shared.RespondDecisionTaskCompletedResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RespondDecisionTaskFailed", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondDecisionTaskFailed), NoWire: responddecisiontaskfailed_NoWireHandler{impl}, }, Signature: "RespondDecisionTaskFailed(FailedRequest *shared.RespondDecisionTaskFailedRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RespondQueryTaskCompleted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondQueryTaskCompleted), NoWire: respondquerytaskcompleted_NoWireHandler{impl}, }, Signature: "RespondQueryTaskCompleted(CompleteRequest *shared.RespondQueryTaskCompletedRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "RestartWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RestartWorkflowExecution), NoWire: restartworkflowexecution_NoWireHandler{impl}, }, Signature: "RestartWorkflowExecution(RestartRequest *shared.RestartWorkflowExecutionRequest) (*shared.RestartWorkflowExecutionResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "ScanWorkflowExecutions", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ScanWorkflowExecutions), NoWire: scanworkflowexecutions_NoWireHandler{impl}, }, Signature: "ScanWorkflowExecutions(ListRequest *shared.ListWorkflowExecutionsRequest) (*shared.ListWorkflowExecutionsResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "SignalWithStartWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.SignalWithStartWorkflowExecution), NoWire: signalwithstartworkflowexecution_NoWireHandler{impl}, }, Signature: "SignalWithStartWorkflowExecution(SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionRequest) (*shared.StartWorkflowExecutionResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "SignalWithStartWorkflowExecutionAsync", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.SignalWithStartWorkflowExecutionAsync), NoWire: signalwithstartworkflowexecutionasync_NoWireHandler{impl}, }, Signature: "SignalWithStartWorkflowExecutionAsync(SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionAsyncRequest) (*shared.SignalWithStartWorkflowExecutionAsyncResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "SignalWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.SignalWorkflowExecution), NoWire: signalworkflowexecution_NoWireHandler{impl}, }, Signature: "SignalWorkflowExecution(SignalRequest *shared.SignalWorkflowExecutionRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "StartWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.StartWorkflowExecution), NoWire: startworkflowexecution_NoWireHandler{impl}, }, Signature: "StartWorkflowExecution(StartRequest *shared.StartWorkflowExecutionRequest) (*shared.StartWorkflowExecutionResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "StartWorkflowExecutionAsync", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.StartWorkflowExecutionAsync), NoWire: startworkflowexecutionasync_NoWireHandler{impl}, }, Signature: "StartWorkflowExecutionAsync(StartRequest *shared.StartWorkflowExecutionAsyncRequest) (*shared.StartWorkflowExecutionAsyncResponse)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "TerminateWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.TerminateWorkflowExecution), NoWire: terminateworkflowexecution_NoWireHandler{impl}, }, Signature: "TerminateWorkflowExecution(TerminateRequest *shared.TerminateWorkflowExecutionRequest)", ThriftModule: cadence.ThriftModule, }, thrift.Method{ Name: "UpdateDomain", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.UpdateDomain), NoWire: updatedomain_NoWireHandler{impl}, }, Signature: "UpdateDomain(UpdateRequest *shared.UpdateDomainRequest) (*shared.UpdateDomainResponse)", ThriftModule: cadence.ThriftModule, }, }, } procedures := make([]transport.Procedure, 0, 47) procedures = append(procedures, thrift.BuildProcedures(service, opts...)...) return procedures } type handler struct{ impl Interface } type yarpcErrorNamer interface{ YARPCErrorName() string } type yarpcErrorCoder interface{ YARPCErrorCode() *yarpcerrors.Code } func (h handler) CountWorkflowExecutions(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_CountWorkflowExecutions_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'CountWorkflowExecutions': %w", err) } success, appErr := h.impl.CountWorkflowExecutions(ctx, args.CountRequest) hadError := appErr != nil result, err := cadence.WorkflowService_CountWorkflowExecutions_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DeleteDomain(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_DeleteDomain_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'DeleteDomain': %w", err) } appErr := h.impl.DeleteDomain(ctx, args.DeleteRequest) hadError := appErr != nil result, err := cadence.WorkflowService_DeleteDomain_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DeprecateDomain(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_DeprecateDomain_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'DeprecateDomain': %w", err) } appErr := h.impl.DeprecateDomain(ctx, args.DeprecateRequest) hadError := appErr != nil result, err := cadence.WorkflowService_DeprecateDomain_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeDomain(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_DescribeDomain_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'DescribeDomain': %w", err) } success, appErr := h.impl.DescribeDomain(ctx, args.DescribeRequest) hadError := appErr != nil result, err := cadence.WorkflowService_DescribeDomain_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeTaskList(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_DescribeTaskList_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'DescribeTaskList': %w", err) } success, appErr := h.impl.DescribeTaskList(ctx, args.Request) hadError := appErr != nil result, err := cadence.WorkflowService_DescribeTaskList_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_DescribeWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'DescribeWorkflowExecution': %w", err) } success, appErr := h.impl.DescribeWorkflowExecution(ctx, args.DescribeRequest) hadError := appErr != nil result, err := cadence.WorkflowService_DescribeWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DiagnoseWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_DiagnoseWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'DiagnoseWorkflowExecution': %w", err) } success, appErr := h.impl.DiagnoseWorkflowExecution(ctx, args.DiagnoseRequest) hadError := appErr != nil result, err := cadence.WorkflowService_DiagnoseWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) FailoverDomain(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_FailoverDomain_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'FailoverDomain': %w", err) } success, appErr := h.impl.FailoverDomain(ctx, args.FailoverRequest) hadError := appErr != nil result, err := cadence.WorkflowService_FailoverDomain_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetClusterInfo(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_GetClusterInfo_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'GetClusterInfo': %w", err) } success, appErr := h.impl.GetClusterInfo(ctx) hadError := appErr != nil result, err := cadence.WorkflowService_GetClusterInfo_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetSearchAttributes(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_GetSearchAttributes_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'GetSearchAttributes': %w", err) } success, appErr := h.impl.GetSearchAttributes(ctx) hadError := appErr != nil result, err := cadence.WorkflowService_GetSearchAttributes_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetTaskListsByDomain(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_GetTaskListsByDomain_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'GetTaskListsByDomain': %w", err) } success, appErr := h.impl.GetTaskListsByDomain(ctx, args.Request) hadError := appErr != nil result, err := cadence.WorkflowService_GetTaskListsByDomain_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetWorkflowExecutionHistory(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_GetWorkflowExecutionHistory_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'GetWorkflowExecutionHistory': %w", err) } success, appErr := h.impl.GetWorkflowExecutionHistory(ctx, args.GetRequest) hadError := appErr != nil result, err := cadence.WorkflowService_GetWorkflowExecutionHistory_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ListArchivedWorkflowExecutions(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_ListArchivedWorkflowExecutions_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'ListArchivedWorkflowExecutions': %w", err) } success, appErr := h.impl.ListArchivedWorkflowExecutions(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListArchivedWorkflowExecutions_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ListClosedWorkflowExecutions(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_ListClosedWorkflowExecutions_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'ListClosedWorkflowExecutions': %w", err) } success, appErr := h.impl.ListClosedWorkflowExecutions(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListClosedWorkflowExecutions_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ListDomains(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_ListDomains_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'ListDomains': %w", err) } success, appErr := h.impl.ListDomains(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListDomains_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ListFailoverHistory(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_ListFailoverHistory_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'ListFailoverHistory': %w", err) } success, appErr := h.impl.ListFailoverHistory(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListFailoverHistory_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ListOpenWorkflowExecutions(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_ListOpenWorkflowExecutions_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'ListOpenWorkflowExecutions': %w", err) } success, appErr := h.impl.ListOpenWorkflowExecutions(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListOpenWorkflowExecutions_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ListTaskListPartitions(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_ListTaskListPartitions_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'ListTaskListPartitions': %w", err) } success, appErr := h.impl.ListTaskListPartitions(ctx, args.Request) hadError := appErr != nil result, err := cadence.WorkflowService_ListTaskListPartitions_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ListWorkflowExecutions(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_ListWorkflowExecutions_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'ListWorkflowExecutions': %w", err) } success, appErr := h.impl.ListWorkflowExecutions(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListWorkflowExecutions_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) PollForActivityTask(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_PollForActivityTask_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'PollForActivityTask': %w", err) } success, appErr := h.impl.PollForActivityTask(ctx, args.PollRequest) hadError := appErr != nil result, err := cadence.WorkflowService_PollForActivityTask_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) PollForDecisionTask(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_PollForDecisionTask_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'PollForDecisionTask': %w", err) } success, appErr := h.impl.PollForDecisionTask(ctx, args.PollRequest) hadError := appErr != nil result, err := cadence.WorkflowService_PollForDecisionTask_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) QueryWorkflow(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_QueryWorkflow_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'QueryWorkflow': %w", err) } success, appErr := h.impl.QueryWorkflow(ctx, args.QueryRequest) hadError := appErr != nil result, err := cadence.WorkflowService_QueryWorkflow_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RecordActivityTaskHeartbeat(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RecordActivityTaskHeartbeat_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RecordActivityTaskHeartbeat': %w", err) } success, appErr := h.impl.RecordActivityTaskHeartbeat(ctx, args.HeartbeatRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RecordActivityTaskHeartbeat_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RecordActivityTaskHeartbeatByID(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RecordActivityTaskHeartbeatByID_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RecordActivityTaskHeartbeatByID': %w", err) } success, appErr := h.impl.RecordActivityTaskHeartbeatByID(ctx, args.HeartbeatRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RecordActivityTaskHeartbeatByID_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RefreshWorkflowTasks(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RefreshWorkflowTasks_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RefreshWorkflowTasks': %w", err) } appErr := h.impl.RefreshWorkflowTasks(ctx, args.Request) hadError := appErr != nil result, err := cadence.WorkflowService_RefreshWorkflowTasks_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RegisterDomain(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RegisterDomain_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RegisterDomain': %w", err) } appErr := h.impl.RegisterDomain(ctx, args.RegisterRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RegisterDomain_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RequestCancelWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RequestCancelWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RequestCancelWorkflowExecution': %w", err) } appErr := h.impl.RequestCancelWorkflowExecution(ctx, args.CancelRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RequestCancelWorkflowExecution_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ResetStickyTaskList(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_ResetStickyTaskList_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'ResetStickyTaskList': %w", err) } success, appErr := h.impl.ResetStickyTaskList(ctx, args.ResetRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ResetStickyTaskList_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ResetWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_ResetWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'ResetWorkflowExecution': %w", err) } success, appErr := h.impl.ResetWorkflowExecution(ctx, args.ResetRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ResetWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondActivityTaskCanceled(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RespondActivityTaskCanceled_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskCanceled': %w", err) } appErr := h.impl.RespondActivityTaskCanceled(ctx, args.CanceledRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskCanceled_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondActivityTaskCanceledByID(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RespondActivityTaskCanceledByID_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskCanceledByID': %w", err) } appErr := h.impl.RespondActivityTaskCanceledByID(ctx, args.CanceledRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskCanceledByID_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondActivityTaskCompleted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RespondActivityTaskCompleted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskCompleted': %w", err) } appErr := h.impl.RespondActivityTaskCompleted(ctx, args.CompleteRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskCompleted_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondActivityTaskCompletedByID(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RespondActivityTaskCompletedByID_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskCompletedByID': %w", err) } appErr := h.impl.RespondActivityTaskCompletedByID(ctx, args.CompleteRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskCompletedByID_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondActivityTaskFailed(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RespondActivityTaskFailed_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskFailed': %w", err) } appErr := h.impl.RespondActivityTaskFailed(ctx, args.FailRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskFailed_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondActivityTaskFailedByID(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RespondActivityTaskFailedByID_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskFailedByID': %w", err) } appErr := h.impl.RespondActivityTaskFailedByID(ctx, args.FailRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskFailedByID_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondDecisionTaskCompleted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RespondDecisionTaskCompleted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RespondDecisionTaskCompleted': %w", err) } success, appErr := h.impl.RespondDecisionTaskCompleted(ctx, args.CompleteRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondDecisionTaskCompleted_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondDecisionTaskFailed(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RespondDecisionTaskFailed_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RespondDecisionTaskFailed': %w", err) } appErr := h.impl.RespondDecisionTaskFailed(ctx, args.FailedRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondDecisionTaskFailed_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondQueryTaskCompleted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RespondQueryTaskCompleted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RespondQueryTaskCompleted': %w", err) } appErr := h.impl.RespondQueryTaskCompleted(ctx, args.CompleteRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondQueryTaskCompleted_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RestartWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_RestartWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'RestartWorkflowExecution': %w", err) } success, appErr := h.impl.RestartWorkflowExecution(ctx, args.RestartRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RestartWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ScanWorkflowExecutions(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_ScanWorkflowExecutions_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'ScanWorkflowExecutions': %w", err) } success, appErr := h.impl.ScanWorkflowExecutions(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ScanWorkflowExecutions_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) SignalWithStartWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_SignalWithStartWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'SignalWithStartWorkflowExecution': %w", err) } success, appErr := h.impl.SignalWithStartWorkflowExecution(ctx, args.SignalWithStartRequest) hadError := appErr != nil result, err := cadence.WorkflowService_SignalWithStartWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) SignalWithStartWorkflowExecutionAsync(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_SignalWithStartWorkflowExecutionAsync_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'SignalWithStartWorkflowExecutionAsync': %w", err) } success, appErr := h.impl.SignalWithStartWorkflowExecutionAsync(ctx, args.SignalWithStartRequest) hadError := appErr != nil result, err := cadence.WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) SignalWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_SignalWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'SignalWorkflowExecution': %w", err) } appErr := h.impl.SignalWorkflowExecution(ctx, args.SignalRequest) hadError := appErr != nil result, err := cadence.WorkflowService_SignalWorkflowExecution_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) StartWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_StartWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'StartWorkflowExecution': %w", err) } success, appErr := h.impl.StartWorkflowExecution(ctx, args.StartRequest) hadError := appErr != nil result, err := cadence.WorkflowService_StartWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) StartWorkflowExecutionAsync(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_StartWorkflowExecutionAsync_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'StartWorkflowExecutionAsync': %w", err) } success, appErr := h.impl.StartWorkflowExecutionAsync(ctx, args.StartRequest) hadError := appErr != nil result, err := cadence.WorkflowService_StartWorkflowExecutionAsync_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) TerminateWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_TerminateWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'TerminateWorkflowExecution': %w", err) } appErr := h.impl.TerminateWorkflowExecution(ctx, args.TerminateRequest) hadError := appErr != nil result, err := cadence.WorkflowService_TerminateWorkflowExecution_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) UpdateDomain(ctx context.Context, body wire.Value) (thrift.Response, error) { var args cadence.WorkflowService_UpdateDomain_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'WorkflowService' procedure 'UpdateDomain': %w", err) } success, appErr := h.impl.UpdateDomain(ctx, args.UpdateRequest) hadError := appErr != nil result, err := cadence.WorkflowService_UpdateDomain_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type countworkflowexecutions_NoWireHandler struct{ impl Interface } func (h countworkflowexecutions_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_CountWorkflowExecutions_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'CountWorkflowExecutions': %w", err) } success, appErr := h.impl.CountWorkflowExecutions(ctx, args.CountRequest) hadError := appErr != nil result, err := cadence.WorkflowService_CountWorkflowExecutions_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type deletedomain_NoWireHandler struct{ impl Interface } func (h deletedomain_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_DeleteDomain_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'DeleteDomain': %w", err) } appErr := h.impl.DeleteDomain(ctx, args.DeleteRequest) hadError := appErr != nil result, err := cadence.WorkflowService_DeleteDomain_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type deprecatedomain_NoWireHandler struct{ impl Interface } func (h deprecatedomain_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_DeprecateDomain_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'DeprecateDomain': %w", err) } appErr := h.impl.DeprecateDomain(ctx, args.DeprecateRequest) hadError := appErr != nil result, err := cadence.WorkflowService_DeprecateDomain_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describedomain_NoWireHandler struct{ impl Interface } func (h describedomain_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_DescribeDomain_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'DescribeDomain': %w", err) } success, appErr := h.impl.DescribeDomain(ctx, args.DescribeRequest) hadError := appErr != nil result, err := cadence.WorkflowService_DescribeDomain_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describetasklist_NoWireHandler struct{ impl Interface } func (h describetasklist_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_DescribeTaskList_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'DescribeTaskList': %w", err) } success, appErr := h.impl.DescribeTaskList(ctx, args.Request) hadError := appErr != nil result, err := cadence.WorkflowService_DescribeTaskList_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describeworkflowexecution_NoWireHandler struct{ impl Interface } func (h describeworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_DescribeWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'DescribeWorkflowExecution': %w", err) } success, appErr := h.impl.DescribeWorkflowExecution(ctx, args.DescribeRequest) hadError := appErr != nil result, err := cadence.WorkflowService_DescribeWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type diagnoseworkflowexecution_NoWireHandler struct{ impl Interface } func (h diagnoseworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_DiagnoseWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'DiagnoseWorkflowExecution': %w", err) } success, appErr := h.impl.DiagnoseWorkflowExecution(ctx, args.DiagnoseRequest) hadError := appErr != nil result, err := cadence.WorkflowService_DiagnoseWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type failoverdomain_NoWireHandler struct{ impl Interface } func (h failoverdomain_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_FailoverDomain_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'FailoverDomain': %w", err) } success, appErr := h.impl.FailoverDomain(ctx, args.FailoverRequest) hadError := appErr != nil result, err := cadence.WorkflowService_FailoverDomain_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getclusterinfo_NoWireHandler struct{ impl Interface } func (h getclusterinfo_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_GetClusterInfo_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'GetClusterInfo': %w", err) } success, appErr := h.impl.GetClusterInfo(ctx) hadError := appErr != nil result, err := cadence.WorkflowService_GetClusterInfo_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getsearchattributes_NoWireHandler struct{ impl Interface } func (h getsearchattributes_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_GetSearchAttributes_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'GetSearchAttributes': %w", err) } success, appErr := h.impl.GetSearchAttributes(ctx) hadError := appErr != nil result, err := cadence.WorkflowService_GetSearchAttributes_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type gettasklistsbydomain_NoWireHandler struct{ impl Interface } func (h gettasklistsbydomain_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_GetTaskListsByDomain_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'GetTaskListsByDomain': %w", err) } success, appErr := h.impl.GetTaskListsByDomain(ctx, args.Request) hadError := appErr != nil result, err := cadence.WorkflowService_GetTaskListsByDomain_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getworkflowexecutionhistory_NoWireHandler struct{ impl Interface } func (h getworkflowexecutionhistory_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_GetWorkflowExecutionHistory_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'GetWorkflowExecutionHistory': %w", err) } success, appErr := h.impl.GetWorkflowExecutionHistory(ctx, args.GetRequest) hadError := appErr != nil result, err := cadence.WorkflowService_GetWorkflowExecutionHistory_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type listarchivedworkflowexecutions_NoWireHandler struct{ impl Interface } func (h listarchivedworkflowexecutions_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_ListArchivedWorkflowExecutions_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'ListArchivedWorkflowExecutions': %w", err) } success, appErr := h.impl.ListArchivedWorkflowExecutions(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListArchivedWorkflowExecutions_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type listclosedworkflowexecutions_NoWireHandler struct{ impl Interface } func (h listclosedworkflowexecutions_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_ListClosedWorkflowExecutions_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'ListClosedWorkflowExecutions': %w", err) } success, appErr := h.impl.ListClosedWorkflowExecutions(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListClosedWorkflowExecutions_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type listdomains_NoWireHandler struct{ impl Interface } func (h listdomains_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_ListDomains_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'ListDomains': %w", err) } success, appErr := h.impl.ListDomains(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListDomains_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type listfailoverhistory_NoWireHandler struct{ impl Interface } func (h listfailoverhistory_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_ListFailoverHistory_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'ListFailoverHistory': %w", err) } success, appErr := h.impl.ListFailoverHistory(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListFailoverHistory_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type listopenworkflowexecutions_NoWireHandler struct{ impl Interface } func (h listopenworkflowexecutions_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_ListOpenWorkflowExecutions_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'ListOpenWorkflowExecutions': %w", err) } success, appErr := h.impl.ListOpenWorkflowExecutions(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListOpenWorkflowExecutions_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type listtasklistpartitions_NoWireHandler struct{ impl Interface } func (h listtasklistpartitions_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_ListTaskListPartitions_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'ListTaskListPartitions': %w", err) } success, appErr := h.impl.ListTaskListPartitions(ctx, args.Request) hadError := appErr != nil result, err := cadence.WorkflowService_ListTaskListPartitions_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type listworkflowexecutions_NoWireHandler struct{ impl Interface } func (h listworkflowexecutions_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_ListWorkflowExecutions_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'ListWorkflowExecutions': %w", err) } success, appErr := h.impl.ListWorkflowExecutions(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ListWorkflowExecutions_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type pollforactivitytask_NoWireHandler struct{ impl Interface } func (h pollforactivitytask_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_PollForActivityTask_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'PollForActivityTask': %w", err) } success, appErr := h.impl.PollForActivityTask(ctx, args.PollRequest) hadError := appErr != nil result, err := cadence.WorkflowService_PollForActivityTask_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type pollfordecisiontask_NoWireHandler struct{ impl Interface } func (h pollfordecisiontask_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_PollForDecisionTask_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'PollForDecisionTask': %w", err) } success, appErr := h.impl.PollForDecisionTask(ctx, args.PollRequest) hadError := appErr != nil result, err := cadence.WorkflowService_PollForDecisionTask_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type queryworkflow_NoWireHandler struct{ impl Interface } func (h queryworkflow_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_QueryWorkflow_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'QueryWorkflow': %w", err) } success, appErr := h.impl.QueryWorkflow(ctx, args.QueryRequest) hadError := appErr != nil result, err := cadence.WorkflowService_QueryWorkflow_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type recordactivitytaskheartbeat_NoWireHandler struct{ impl Interface } func (h recordactivitytaskheartbeat_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RecordActivityTaskHeartbeat_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RecordActivityTaskHeartbeat': %w", err) } success, appErr := h.impl.RecordActivityTaskHeartbeat(ctx, args.HeartbeatRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RecordActivityTaskHeartbeat_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type recordactivitytaskheartbeatbyid_NoWireHandler struct{ impl Interface } func (h recordactivitytaskheartbeatbyid_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RecordActivityTaskHeartbeatByID_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RecordActivityTaskHeartbeatByID': %w", err) } success, appErr := h.impl.RecordActivityTaskHeartbeatByID(ctx, args.HeartbeatRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RecordActivityTaskHeartbeatByID_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type refreshworkflowtasks_NoWireHandler struct{ impl Interface } func (h refreshworkflowtasks_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RefreshWorkflowTasks_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RefreshWorkflowTasks': %w", err) } appErr := h.impl.RefreshWorkflowTasks(ctx, args.Request) hadError := appErr != nil result, err := cadence.WorkflowService_RefreshWorkflowTasks_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type registerdomain_NoWireHandler struct{ impl Interface } func (h registerdomain_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RegisterDomain_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RegisterDomain': %w", err) } appErr := h.impl.RegisterDomain(ctx, args.RegisterRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RegisterDomain_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type requestcancelworkflowexecution_NoWireHandler struct{ impl Interface } func (h requestcancelworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RequestCancelWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RequestCancelWorkflowExecution': %w", err) } appErr := h.impl.RequestCancelWorkflowExecution(ctx, args.CancelRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RequestCancelWorkflowExecution_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type resetstickytasklist_NoWireHandler struct{ impl Interface } func (h resetstickytasklist_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_ResetStickyTaskList_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'ResetStickyTaskList': %w", err) } success, appErr := h.impl.ResetStickyTaskList(ctx, args.ResetRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ResetStickyTaskList_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type resetworkflowexecution_NoWireHandler struct{ impl Interface } func (h resetworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_ResetWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'ResetWorkflowExecution': %w", err) } success, appErr := h.impl.ResetWorkflowExecution(ctx, args.ResetRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ResetWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondactivitytaskcanceled_NoWireHandler struct{ impl Interface } func (h respondactivitytaskcanceled_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RespondActivityTaskCanceled_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskCanceled': %w", err) } appErr := h.impl.RespondActivityTaskCanceled(ctx, args.CanceledRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskCanceled_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondactivitytaskcanceledbyid_NoWireHandler struct{ impl Interface } func (h respondactivitytaskcanceledbyid_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RespondActivityTaskCanceledByID_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskCanceledByID': %w", err) } appErr := h.impl.RespondActivityTaskCanceledByID(ctx, args.CanceledRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskCanceledByID_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondactivitytaskcompleted_NoWireHandler struct{ impl Interface } func (h respondactivitytaskcompleted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RespondActivityTaskCompleted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskCompleted': %w", err) } appErr := h.impl.RespondActivityTaskCompleted(ctx, args.CompleteRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskCompleted_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondactivitytaskcompletedbyid_NoWireHandler struct{ impl Interface } func (h respondactivitytaskcompletedbyid_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RespondActivityTaskCompletedByID_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskCompletedByID': %w", err) } appErr := h.impl.RespondActivityTaskCompletedByID(ctx, args.CompleteRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskCompletedByID_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondactivitytaskfailed_NoWireHandler struct{ impl Interface } func (h respondactivitytaskfailed_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RespondActivityTaskFailed_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskFailed': %w", err) } appErr := h.impl.RespondActivityTaskFailed(ctx, args.FailRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskFailed_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondactivitytaskfailedbyid_NoWireHandler struct{ impl Interface } func (h respondactivitytaskfailedbyid_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RespondActivityTaskFailedByID_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RespondActivityTaskFailedByID': %w", err) } appErr := h.impl.RespondActivityTaskFailedByID(ctx, args.FailRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondActivityTaskFailedByID_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type responddecisiontaskcompleted_NoWireHandler struct{ impl Interface } func (h responddecisiontaskcompleted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RespondDecisionTaskCompleted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RespondDecisionTaskCompleted': %w", err) } success, appErr := h.impl.RespondDecisionTaskCompleted(ctx, args.CompleteRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondDecisionTaskCompleted_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type responddecisiontaskfailed_NoWireHandler struct{ impl Interface } func (h responddecisiontaskfailed_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RespondDecisionTaskFailed_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RespondDecisionTaskFailed': %w", err) } appErr := h.impl.RespondDecisionTaskFailed(ctx, args.FailedRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondDecisionTaskFailed_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondquerytaskcompleted_NoWireHandler struct{ impl Interface } func (h respondquerytaskcompleted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RespondQueryTaskCompleted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RespondQueryTaskCompleted': %w", err) } appErr := h.impl.RespondQueryTaskCompleted(ctx, args.CompleteRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RespondQueryTaskCompleted_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type restartworkflowexecution_NoWireHandler struct{ impl Interface } func (h restartworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_RestartWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'RestartWorkflowExecution': %w", err) } success, appErr := h.impl.RestartWorkflowExecution(ctx, args.RestartRequest) hadError := appErr != nil result, err := cadence.WorkflowService_RestartWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type scanworkflowexecutions_NoWireHandler struct{ impl Interface } func (h scanworkflowexecutions_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_ScanWorkflowExecutions_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'ScanWorkflowExecutions': %w", err) } success, appErr := h.impl.ScanWorkflowExecutions(ctx, args.ListRequest) hadError := appErr != nil result, err := cadence.WorkflowService_ScanWorkflowExecutions_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type signalwithstartworkflowexecution_NoWireHandler struct{ impl Interface } func (h signalwithstartworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_SignalWithStartWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'SignalWithStartWorkflowExecution': %w", err) } success, appErr := h.impl.SignalWithStartWorkflowExecution(ctx, args.SignalWithStartRequest) hadError := appErr != nil result, err := cadence.WorkflowService_SignalWithStartWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type signalwithstartworkflowexecutionasync_NoWireHandler struct{ impl Interface } func (h signalwithstartworkflowexecutionasync_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_SignalWithStartWorkflowExecutionAsync_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'SignalWithStartWorkflowExecutionAsync': %w", err) } success, appErr := h.impl.SignalWithStartWorkflowExecutionAsync(ctx, args.SignalWithStartRequest) hadError := appErr != nil result, err := cadence.WorkflowService_SignalWithStartWorkflowExecutionAsync_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type signalworkflowexecution_NoWireHandler struct{ impl Interface } func (h signalworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_SignalWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'SignalWorkflowExecution': %w", err) } appErr := h.impl.SignalWorkflowExecution(ctx, args.SignalRequest) hadError := appErr != nil result, err := cadence.WorkflowService_SignalWorkflowExecution_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type startworkflowexecution_NoWireHandler struct{ impl Interface } func (h startworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_StartWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'StartWorkflowExecution': %w", err) } success, appErr := h.impl.StartWorkflowExecution(ctx, args.StartRequest) hadError := appErr != nil result, err := cadence.WorkflowService_StartWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type startworkflowexecutionasync_NoWireHandler struct{ impl Interface } func (h startworkflowexecutionasync_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_StartWorkflowExecutionAsync_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'StartWorkflowExecutionAsync': %w", err) } success, appErr := h.impl.StartWorkflowExecutionAsync(ctx, args.StartRequest) hadError := appErr != nil result, err := cadence.WorkflowService_StartWorkflowExecutionAsync_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type terminateworkflowexecution_NoWireHandler struct{ impl Interface } func (h terminateworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_TerminateWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'TerminateWorkflowExecution': %w", err) } appErr := h.impl.TerminateWorkflowExecution(ctx, args.TerminateRequest) hadError := appErr != nil result, err := cadence.WorkflowService_TerminateWorkflowExecution_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type updatedomain_NoWireHandler struct{ impl Interface } func (h updatedomain_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args cadence.WorkflowService_UpdateDomain_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'WorkflowService' procedure 'UpdateDomain': %w", err) } success, appErr := h.impl.UpdateDomain(ctx, args.UpdateRequest) hadError := appErr != nil result, err := cadence.WorkflowService_UpdateDomain_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } ================================================ FILE: .gen/go/cadence/workflowservicetest/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package workflowservicetest import ( context "context" gomock "github.com/golang/mock/gomock" yarpc "go.uber.org/yarpc" workflowserviceclient "github.com/uber/cadence/.gen/go/cadence/workflowserviceclient" shared "github.com/uber/cadence/.gen/go/shared" ) // MockClient implements a gomock-compatible mock client for service // WorkflowService. type MockClient struct { ctrl *gomock.Controller recorder *_MockClientRecorder } var _ workflowserviceclient.Interface = (*MockClient)(nil) type _MockClientRecorder struct { mock *MockClient } // Build a new mock client for service WorkflowService. // // mockCtrl := gomock.NewController(t) // client := workflowservicetest.NewMockClient(mockCtrl) // // Use EXPECT() to set expectations on the mock. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &_MockClientRecorder{mock} return mock } // EXPECT returns an object that allows you to define an expectation on the // WorkflowService mock client. func (m *MockClient) EXPECT() *_MockClientRecorder { return m.recorder } // CountWorkflowExecutions responds to a CountWorkflowExecutions call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().CountWorkflowExecutions(gomock.Any(), ...).Return(...) // ... := client.CountWorkflowExecutions(...) func (m *MockClient) CountWorkflowExecutions( ctx context.Context, _CountRequest *shared.CountWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.CountWorkflowExecutionsResponse, err error) { args := []interface{}{ctx, _CountRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "CountWorkflowExecutions", args...) success, _ = ret[i].(*shared.CountWorkflowExecutionsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) CountWorkflowExecutions( ctx interface{}, _CountRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CountRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "CountWorkflowExecutions", args...) } // DeleteDomain responds to a DeleteDomain call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DeleteDomain(gomock.Any(), ...).Return(...) // ... := client.DeleteDomain(...) func (m *MockClient) DeleteDomain( ctx context.Context, _DeleteRequest *shared.DeleteDomainRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _DeleteRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DeleteDomain", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DeleteDomain( ctx interface{}, _DeleteRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _DeleteRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DeleteDomain", args...) } // DeprecateDomain responds to a DeprecateDomain call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DeprecateDomain(gomock.Any(), ...).Return(...) // ... := client.DeprecateDomain(...) func (m *MockClient) DeprecateDomain( ctx context.Context, _DeprecateRequest *shared.DeprecateDomainRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _DeprecateRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DeprecateDomain", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DeprecateDomain( ctx interface{}, _DeprecateRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _DeprecateRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DeprecateDomain", args...) } // DescribeDomain responds to a DescribeDomain call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeDomain(gomock.Any(), ...).Return(...) // ... := client.DescribeDomain(...) func (m *MockClient) DescribeDomain( ctx context.Context, _DescribeRequest *shared.DescribeDomainRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeDomainResponse, err error) { args := []interface{}{ctx, _DescribeRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeDomain", args...) success, _ = ret[i].(*shared.DescribeDomainResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeDomain( ctx interface{}, _DescribeRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _DescribeRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeDomain", args...) } // DescribeTaskList responds to a DescribeTaskList call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeTaskList(gomock.Any(), ...).Return(...) // ... := client.DescribeTaskList(...) func (m *MockClient) DescribeTaskList( ctx context.Context, _Request *shared.DescribeTaskListRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeTaskListResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeTaskList", args...) success, _ = ret[i].(*shared.DescribeTaskListResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeTaskList( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeTaskList", args...) } // DescribeWorkflowExecution responds to a DescribeWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.DescribeWorkflowExecution(...) func (m *MockClient) DescribeWorkflowExecution( ctx context.Context, _DescribeRequest *shared.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _DescribeRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeWorkflowExecution", args...) success, _ = ret[i].(*shared.DescribeWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeWorkflowExecution( ctx interface{}, _DescribeRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _DescribeRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeWorkflowExecution", args...) } // DiagnoseWorkflowExecution responds to a DiagnoseWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DiagnoseWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.DiagnoseWorkflowExecution(...) func (m *MockClient) DiagnoseWorkflowExecution( ctx context.Context, _DiagnoseRequest *shared.DiagnoseWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.DiagnoseWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _DiagnoseRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DiagnoseWorkflowExecution", args...) success, _ = ret[i].(*shared.DiagnoseWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DiagnoseWorkflowExecution( ctx interface{}, _DiagnoseRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _DiagnoseRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DiagnoseWorkflowExecution", args...) } // FailoverDomain responds to a FailoverDomain call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().FailoverDomain(gomock.Any(), ...).Return(...) // ... := client.FailoverDomain(...) func (m *MockClient) FailoverDomain( ctx context.Context, _FailoverRequest *shared.FailoverDomainRequest, opts ...yarpc.CallOption, ) (success *shared.FailoverDomainResponse, err error) { args := []interface{}{ctx, _FailoverRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "FailoverDomain", args...) success, _ = ret[i].(*shared.FailoverDomainResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) FailoverDomain( ctx interface{}, _FailoverRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _FailoverRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "FailoverDomain", args...) } // GetClusterInfo responds to a GetClusterInfo call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetClusterInfo(gomock.Any(), ...).Return(...) // ... := client.GetClusterInfo(...) func (m *MockClient) GetClusterInfo( ctx context.Context, opts ...yarpc.CallOption, ) (success *shared.ClusterInfo, err error) { args := []interface{}{ctx} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetClusterInfo", args...) success, _ = ret[i].(*shared.ClusterInfo) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetClusterInfo( ctx interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetClusterInfo", args...) } // GetSearchAttributes responds to a GetSearchAttributes call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetSearchAttributes(gomock.Any(), ...).Return(...) // ... := client.GetSearchAttributes(...) func (m *MockClient) GetSearchAttributes( ctx context.Context, opts ...yarpc.CallOption, ) (success *shared.GetSearchAttributesResponse, err error) { args := []interface{}{ctx} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetSearchAttributes", args...) success, _ = ret[i].(*shared.GetSearchAttributesResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetSearchAttributes( ctx interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetSearchAttributes", args...) } // GetTaskListsByDomain responds to a GetTaskListsByDomain call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetTaskListsByDomain(gomock.Any(), ...).Return(...) // ... := client.GetTaskListsByDomain(...) func (m *MockClient) GetTaskListsByDomain( ctx context.Context, _Request *shared.GetTaskListsByDomainRequest, opts ...yarpc.CallOption, ) (success *shared.GetTaskListsByDomainResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetTaskListsByDomain", args...) success, _ = ret[i].(*shared.GetTaskListsByDomainResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetTaskListsByDomain( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetTaskListsByDomain", args...) } // GetWorkflowExecutionHistory responds to a GetWorkflowExecutionHistory call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), ...).Return(...) // ... := client.GetWorkflowExecutionHistory(...) func (m *MockClient) GetWorkflowExecutionHistory( ctx context.Context, _GetRequest *shared.GetWorkflowExecutionHistoryRequest, opts ...yarpc.CallOption, ) (success *shared.GetWorkflowExecutionHistoryResponse, err error) { args := []interface{}{ctx, _GetRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetWorkflowExecutionHistory", args...) success, _ = ret[i].(*shared.GetWorkflowExecutionHistoryResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetWorkflowExecutionHistory( ctx interface{}, _GetRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _GetRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetWorkflowExecutionHistory", args...) } // ListArchivedWorkflowExecutions responds to a ListArchivedWorkflowExecutions call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ListArchivedWorkflowExecutions(gomock.Any(), ...).Return(...) // ... := client.ListArchivedWorkflowExecutions(...) func (m *MockClient) ListArchivedWorkflowExecutions( ctx context.Context, _ListRequest *shared.ListArchivedWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListArchivedWorkflowExecutionsResponse, err error) { args := []interface{}{ctx, _ListRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ListArchivedWorkflowExecutions", args...) success, _ = ret[i].(*shared.ListArchivedWorkflowExecutionsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ListArchivedWorkflowExecutions( ctx interface{}, _ListRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ListRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ListArchivedWorkflowExecutions", args...) } // ListClosedWorkflowExecutions responds to a ListClosedWorkflowExecutions call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), ...).Return(...) // ... := client.ListClosedWorkflowExecutions(...) func (m *MockClient) ListClosedWorkflowExecutions( ctx context.Context, _ListRequest *shared.ListClosedWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListClosedWorkflowExecutionsResponse, err error) { args := []interface{}{ctx, _ListRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ListClosedWorkflowExecutions", args...) success, _ = ret[i].(*shared.ListClosedWorkflowExecutionsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ListClosedWorkflowExecutions( ctx interface{}, _ListRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ListRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ListClosedWorkflowExecutions", args...) } // ListDomains responds to a ListDomains call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ListDomains(gomock.Any(), ...).Return(...) // ... := client.ListDomains(...) func (m *MockClient) ListDomains( ctx context.Context, _ListRequest *shared.ListDomainsRequest, opts ...yarpc.CallOption, ) (success *shared.ListDomainsResponse, err error) { args := []interface{}{ctx, _ListRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ListDomains", args...) success, _ = ret[i].(*shared.ListDomainsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ListDomains( ctx interface{}, _ListRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ListRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ListDomains", args...) } // ListFailoverHistory responds to a ListFailoverHistory call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ListFailoverHistory(gomock.Any(), ...).Return(...) // ... := client.ListFailoverHistory(...) func (m *MockClient) ListFailoverHistory( ctx context.Context, _ListRequest *shared.ListFailoverHistoryRequest, opts ...yarpc.CallOption, ) (success *shared.ListFailoverHistoryResponse, err error) { args := []interface{}{ctx, _ListRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ListFailoverHistory", args...) success, _ = ret[i].(*shared.ListFailoverHistoryResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ListFailoverHistory( ctx interface{}, _ListRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ListRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ListFailoverHistory", args...) } // ListOpenWorkflowExecutions responds to a ListOpenWorkflowExecutions call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), ...).Return(...) // ... := client.ListOpenWorkflowExecutions(...) func (m *MockClient) ListOpenWorkflowExecutions( ctx context.Context, _ListRequest *shared.ListOpenWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListOpenWorkflowExecutionsResponse, err error) { args := []interface{}{ctx, _ListRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ListOpenWorkflowExecutions", args...) success, _ = ret[i].(*shared.ListOpenWorkflowExecutionsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ListOpenWorkflowExecutions( ctx interface{}, _ListRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ListRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ListOpenWorkflowExecutions", args...) } // ListTaskListPartitions responds to a ListTaskListPartitions call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ListTaskListPartitions(gomock.Any(), ...).Return(...) // ... := client.ListTaskListPartitions(...) func (m *MockClient) ListTaskListPartitions( ctx context.Context, _Request *shared.ListTaskListPartitionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListTaskListPartitionsResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ListTaskListPartitions", args...) success, _ = ret[i].(*shared.ListTaskListPartitionsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ListTaskListPartitions( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ListTaskListPartitions", args...) } // ListWorkflowExecutions responds to a ListWorkflowExecutions call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ListWorkflowExecutions(gomock.Any(), ...).Return(...) // ... := client.ListWorkflowExecutions(...) func (m *MockClient) ListWorkflowExecutions( ctx context.Context, _ListRequest *shared.ListWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListWorkflowExecutionsResponse, err error) { args := []interface{}{ctx, _ListRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ListWorkflowExecutions", args...) success, _ = ret[i].(*shared.ListWorkflowExecutionsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ListWorkflowExecutions( ctx interface{}, _ListRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ListRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ListWorkflowExecutions", args...) } // PollForActivityTask responds to a PollForActivityTask call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().PollForActivityTask(gomock.Any(), ...).Return(...) // ... := client.PollForActivityTask(...) func (m *MockClient) PollForActivityTask( ctx context.Context, _PollRequest *shared.PollForActivityTaskRequest, opts ...yarpc.CallOption, ) (success *shared.PollForActivityTaskResponse, err error) { args := []interface{}{ctx, _PollRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "PollForActivityTask", args...) success, _ = ret[i].(*shared.PollForActivityTaskResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) PollForActivityTask( ctx interface{}, _PollRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _PollRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "PollForActivityTask", args...) } // PollForDecisionTask responds to a PollForDecisionTask call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().PollForDecisionTask(gomock.Any(), ...).Return(...) // ... := client.PollForDecisionTask(...) func (m *MockClient) PollForDecisionTask( ctx context.Context, _PollRequest *shared.PollForDecisionTaskRequest, opts ...yarpc.CallOption, ) (success *shared.PollForDecisionTaskResponse, err error) { args := []interface{}{ctx, _PollRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "PollForDecisionTask", args...) success, _ = ret[i].(*shared.PollForDecisionTaskResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) PollForDecisionTask( ctx interface{}, _PollRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _PollRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "PollForDecisionTask", args...) } // QueryWorkflow responds to a QueryWorkflow call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().QueryWorkflow(gomock.Any(), ...).Return(...) // ... := client.QueryWorkflow(...) func (m *MockClient) QueryWorkflow( ctx context.Context, _QueryRequest *shared.QueryWorkflowRequest, opts ...yarpc.CallOption, ) (success *shared.QueryWorkflowResponse, err error) { args := []interface{}{ctx, _QueryRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "QueryWorkflow", args...) success, _ = ret[i].(*shared.QueryWorkflowResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) QueryWorkflow( ctx interface{}, _QueryRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _QueryRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "QueryWorkflow", args...) } // RecordActivityTaskHeartbeat responds to a RecordActivityTaskHeartbeat call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), ...).Return(...) // ... := client.RecordActivityTaskHeartbeat(...) func (m *MockClient) RecordActivityTaskHeartbeat( ctx context.Context, _HeartbeatRequest *shared.RecordActivityTaskHeartbeatRequest, opts ...yarpc.CallOption, ) (success *shared.RecordActivityTaskHeartbeatResponse, err error) { args := []interface{}{ctx, _HeartbeatRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RecordActivityTaskHeartbeat", args...) success, _ = ret[i].(*shared.RecordActivityTaskHeartbeatResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RecordActivityTaskHeartbeat( ctx interface{}, _HeartbeatRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _HeartbeatRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RecordActivityTaskHeartbeat", args...) } // RecordActivityTaskHeartbeatByID responds to a RecordActivityTaskHeartbeatByID call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RecordActivityTaskHeartbeatByID(gomock.Any(), ...).Return(...) // ... := client.RecordActivityTaskHeartbeatByID(...) func (m *MockClient) RecordActivityTaskHeartbeatByID( ctx context.Context, _HeartbeatRequest *shared.RecordActivityTaskHeartbeatByIDRequest, opts ...yarpc.CallOption, ) (success *shared.RecordActivityTaskHeartbeatResponse, err error) { args := []interface{}{ctx, _HeartbeatRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RecordActivityTaskHeartbeatByID", args...) success, _ = ret[i].(*shared.RecordActivityTaskHeartbeatResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RecordActivityTaskHeartbeatByID( ctx interface{}, _HeartbeatRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _HeartbeatRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RecordActivityTaskHeartbeatByID", args...) } // RefreshWorkflowTasks responds to a RefreshWorkflowTasks call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RefreshWorkflowTasks(gomock.Any(), ...).Return(...) // ... := client.RefreshWorkflowTasks(...) func (m *MockClient) RefreshWorkflowTasks( ctx context.Context, _Request *shared.RefreshWorkflowTasksRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RefreshWorkflowTasks", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RefreshWorkflowTasks( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RefreshWorkflowTasks", args...) } // RegisterDomain responds to a RegisterDomain call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RegisterDomain(gomock.Any(), ...).Return(...) // ... := client.RegisterDomain(...) func (m *MockClient) RegisterDomain( ctx context.Context, _RegisterRequest *shared.RegisterDomainRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _RegisterRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RegisterDomain", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RegisterDomain( ctx interface{}, _RegisterRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _RegisterRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RegisterDomain", args...) } // RequestCancelWorkflowExecution responds to a RequestCancelWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.RequestCancelWorkflowExecution(...) func (m *MockClient) RequestCancelWorkflowExecution( ctx context.Context, _CancelRequest *shared.RequestCancelWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _CancelRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RequestCancelWorkflowExecution", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RequestCancelWorkflowExecution( ctx interface{}, _CancelRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CancelRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RequestCancelWorkflowExecution", args...) } // ResetStickyTaskList responds to a ResetStickyTaskList call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ResetStickyTaskList(gomock.Any(), ...).Return(...) // ... := client.ResetStickyTaskList(...) func (m *MockClient) ResetStickyTaskList( ctx context.Context, _ResetRequest *shared.ResetStickyTaskListRequest, opts ...yarpc.CallOption, ) (success *shared.ResetStickyTaskListResponse, err error) { args := []interface{}{ctx, _ResetRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ResetStickyTaskList", args...) success, _ = ret[i].(*shared.ResetStickyTaskListResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ResetStickyTaskList( ctx interface{}, _ResetRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ResetRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ResetStickyTaskList", args...) } // ResetWorkflowExecution responds to a ResetWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ResetWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.ResetWorkflowExecution(...) func (m *MockClient) ResetWorkflowExecution( ctx context.Context, _ResetRequest *shared.ResetWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.ResetWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _ResetRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ResetWorkflowExecution", args...) success, _ = ret[i].(*shared.ResetWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ResetWorkflowExecution( ctx interface{}, _ResetRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ResetRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ResetWorkflowExecution", args...) } // RespondActivityTaskCanceled responds to a RespondActivityTaskCanceled call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondActivityTaskCanceled(gomock.Any(), ...).Return(...) // ... := client.RespondActivityTaskCanceled(...) func (m *MockClient) RespondActivityTaskCanceled( ctx context.Context, _CanceledRequest *shared.RespondActivityTaskCanceledRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _CanceledRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondActivityTaskCanceled", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondActivityTaskCanceled( ctx interface{}, _CanceledRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CanceledRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondActivityTaskCanceled", args...) } // RespondActivityTaskCanceledByID responds to a RespondActivityTaskCanceledByID call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondActivityTaskCanceledByID(gomock.Any(), ...).Return(...) // ... := client.RespondActivityTaskCanceledByID(...) func (m *MockClient) RespondActivityTaskCanceledByID( ctx context.Context, _CanceledRequest *shared.RespondActivityTaskCanceledByIDRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _CanceledRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondActivityTaskCanceledByID", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondActivityTaskCanceledByID( ctx interface{}, _CanceledRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CanceledRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondActivityTaskCanceledByID", args...) } // RespondActivityTaskCompleted responds to a RespondActivityTaskCompleted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondActivityTaskCompleted(gomock.Any(), ...).Return(...) // ... := client.RespondActivityTaskCompleted(...) func (m *MockClient) RespondActivityTaskCompleted( ctx context.Context, _CompleteRequest *shared.RespondActivityTaskCompletedRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _CompleteRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondActivityTaskCompleted", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondActivityTaskCompleted( ctx interface{}, _CompleteRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CompleteRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondActivityTaskCompleted", args...) } // RespondActivityTaskCompletedByID responds to a RespondActivityTaskCompletedByID call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondActivityTaskCompletedByID(gomock.Any(), ...).Return(...) // ... := client.RespondActivityTaskCompletedByID(...) func (m *MockClient) RespondActivityTaskCompletedByID( ctx context.Context, _CompleteRequest *shared.RespondActivityTaskCompletedByIDRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _CompleteRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondActivityTaskCompletedByID", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondActivityTaskCompletedByID( ctx interface{}, _CompleteRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CompleteRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondActivityTaskCompletedByID", args...) } // RespondActivityTaskFailed responds to a RespondActivityTaskFailed call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondActivityTaskFailed(gomock.Any(), ...).Return(...) // ... := client.RespondActivityTaskFailed(...) func (m *MockClient) RespondActivityTaskFailed( ctx context.Context, _FailRequest *shared.RespondActivityTaskFailedRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _FailRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondActivityTaskFailed", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondActivityTaskFailed( ctx interface{}, _FailRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _FailRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondActivityTaskFailed", args...) } // RespondActivityTaskFailedByID responds to a RespondActivityTaskFailedByID call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondActivityTaskFailedByID(gomock.Any(), ...).Return(...) // ... := client.RespondActivityTaskFailedByID(...) func (m *MockClient) RespondActivityTaskFailedByID( ctx context.Context, _FailRequest *shared.RespondActivityTaskFailedByIDRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _FailRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondActivityTaskFailedByID", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondActivityTaskFailedByID( ctx interface{}, _FailRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _FailRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondActivityTaskFailedByID", args...) } // RespondDecisionTaskCompleted responds to a RespondDecisionTaskCompleted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), ...).Return(...) // ... := client.RespondDecisionTaskCompleted(...) func (m *MockClient) RespondDecisionTaskCompleted( ctx context.Context, _CompleteRequest *shared.RespondDecisionTaskCompletedRequest, opts ...yarpc.CallOption, ) (success *shared.RespondDecisionTaskCompletedResponse, err error) { args := []interface{}{ctx, _CompleteRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondDecisionTaskCompleted", args...) success, _ = ret[i].(*shared.RespondDecisionTaskCompletedResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondDecisionTaskCompleted( ctx interface{}, _CompleteRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CompleteRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondDecisionTaskCompleted", args...) } // RespondDecisionTaskFailed responds to a RespondDecisionTaskFailed call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondDecisionTaskFailed(gomock.Any(), ...).Return(...) // ... := client.RespondDecisionTaskFailed(...) func (m *MockClient) RespondDecisionTaskFailed( ctx context.Context, _FailedRequest *shared.RespondDecisionTaskFailedRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _FailedRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondDecisionTaskFailed", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondDecisionTaskFailed( ctx interface{}, _FailedRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _FailedRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondDecisionTaskFailed", args...) } // RespondQueryTaskCompleted responds to a RespondQueryTaskCompleted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondQueryTaskCompleted(gomock.Any(), ...).Return(...) // ... := client.RespondQueryTaskCompleted(...) func (m *MockClient) RespondQueryTaskCompleted( ctx context.Context, _CompleteRequest *shared.RespondQueryTaskCompletedRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _CompleteRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondQueryTaskCompleted", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondQueryTaskCompleted( ctx interface{}, _CompleteRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CompleteRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondQueryTaskCompleted", args...) } // RestartWorkflowExecution responds to a RestartWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RestartWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.RestartWorkflowExecution(...) func (m *MockClient) RestartWorkflowExecution( ctx context.Context, _RestartRequest *shared.RestartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.RestartWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _RestartRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RestartWorkflowExecution", args...) success, _ = ret[i].(*shared.RestartWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RestartWorkflowExecution( ctx interface{}, _RestartRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _RestartRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RestartWorkflowExecution", args...) } // ScanWorkflowExecutions responds to a ScanWorkflowExecutions call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ScanWorkflowExecutions(gomock.Any(), ...).Return(...) // ... := client.ScanWorkflowExecutions(...) func (m *MockClient) ScanWorkflowExecutions( ctx context.Context, _ListRequest *shared.ListWorkflowExecutionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListWorkflowExecutionsResponse, err error) { args := []interface{}{ctx, _ListRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ScanWorkflowExecutions", args...) success, _ = ret[i].(*shared.ListWorkflowExecutionsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ScanWorkflowExecutions( ctx interface{}, _ListRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ListRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ScanWorkflowExecutions", args...) } // SignalWithStartWorkflowExecution responds to a SignalWithStartWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.SignalWithStartWorkflowExecution(...) func (m *MockClient) SignalWithStartWorkflowExecution( ctx context.Context, _SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.StartWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _SignalWithStartRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "SignalWithStartWorkflowExecution", args...) success, _ = ret[i].(*shared.StartWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) SignalWithStartWorkflowExecution( ctx interface{}, _SignalWithStartRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _SignalWithStartRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "SignalWithStartWorkflowExecution", args...) } // SignalWithStartWorkflowExecutionAsync responds to a SignalWithStartWorkflowExecutionAsync call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().SignalWithStartWorkflowExecutionAsync(gomock.Any(), ...).Return(...) // ... := client.SignalWithStartWorkflowExecutionAsync(...) func (m *MockClient) SignalWithStartWorkflowExecutionAsync( ctx context.Context, _SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionAsyncRequest, opts ...yarpc.CallOption, ) (success *shared.SignalWithStartWorkflowExecutionAsyncResponse, err error) { args := []interface{}{ctx, _SignalWithStartRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "SignalWithStartWorkflowExecutionAsync", args...) success, _ = ret[i].(*shared.SignalWithStartWorkflowExecutionAsyncResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) SignalWithStartWorkflowExecutionAsync( ctx interface{}, _SignalWithStartRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _SignalWithStartRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "SignalWithStartWorkflowExecutionAsync", args...) } // SignalWorkflowExecution responds to a SignalWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().SignalWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.SignalWorkflowExecution(...) func (m *MockClient) SignalWorkflowExecution( ctx context.Context, _SignalRequest *shared.SignalWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _SignalRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "SignalWorkflowExecution", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) SignalWorkflowExecution( ctx interface{}, _SignalRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _SignalRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "SignalWorkflowExecution", args...) } // StartWorkflowExecution responds to a StartWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().StartWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.StartWorkflowExecution(...) func (m *MockClient) StartWorkflowExecution( ctx context.Context, _StartRequest *shared.StartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.StartWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _StartRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "StartWorkflowExecution", args...) success, _ = ret[i].(*shared.StartWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) StartWorkflowExecution( ctx interface{}, _StartRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _StartRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "StartWorkflowExecution", args...) } // StartWorkflowExecutionAsync responds to a StartWorkflowExecutionAsync call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().StartWorkflowExecutionAsync(gomock.Any(), ...).Return(...) // ... := client.StartWorkflowExecutionAsync(...) func (m *MockClient) StartWorkflowExecutionAsync( ctx context.Context, _StartRequest *shared.StartWorkflowExecutionAsyncRequest, opts ...yarpc.CallOption, ) (success *shared.StartWorkflowExecutionAsyncResponse, err error) { args := []interface{}{ctx, _StartRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "StartWorkflowExecutionAsync", args...) success, _ = ret[i].(*shared.StartWorkflowExecutionAsyncResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) StartWorkflowExecutionAsync( ctx interface{}, _StartRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _StartRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "StartWorkflowExecutionAsync", args...) } // TerminateWorkflowExecution responds to a TerminateWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().TerminateWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.TerminateWorkflowExecution(...) func (m *MockClient) TerminateWorkflowExecution( ctx context.Context, _TerminateRequest *shared.TerminateWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _TerminateRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "TerminateWorkflowExecution", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) TerminateWorkflowExecution( ctx interface{}, _TerminateRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _TerminateRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "TerminateWorkflowExecution", args...) } // UpdateDomain responds to a UpdateDomain call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().UpdateDomain(gomock.Any(), ...).Return(...) // ... := client.UpdateDomain(...) func (m *MockClient) UpdateDomain( ctx context.Context, _UpdateRequest *shared.UpdateDomainRequest, opts ...yarpc.CallOption, ) (success *shared.UpdateDomainResponse, err error) { args := []interface{}{ctx, _UpdateRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "UpdateDomain", args...) success, _ = ret[i].(*shared.UpdateDomainResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) UpdateDomain( ctx interface{}, _UpdateRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _UpdateRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "UpdateDomain", args...) } ================================================ FILE: .gen/go/checksum/checksum.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package checksum import ( fmt "fmt" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" shared "github.com/uber/cadence/.gen/go/shared" ) type MutableStateChecksumPayload struct { CancelRequested *bool `json:"cancelRequested,omitempty"` State *int16 `json:"state,omitempty"` CloseStatus *int16 `json:"closeStatus,omitempty"` LastWriteVersion *int64 `json:"lastWriteVersion,omitempty"` LastWriteEventID *int64 `json:"lastWriteEventID,omitempty"` LastFirstEventID *int64 `json:"lastFirstEventID,omitempty"` NextEventID *int64 `json:"nextEventID,omitempty"` LastProcessedEventID *int64 `json:"lastProcessedEventID,omitempty"` SignalCount *int64 `json:"signalCount,omitempty"` DecisionAttempt *int32 `json:"decisionAttempt,omitempty"` DecisionVersion *int64 `json:"decisionVersion,omitempty"` DecisionScheduledID *int64 `json:"decisionScheduledID,omitempty"` DecisionStartedID *int64 `json:"decisionStartedID,omitempty"` PendingTimerStartedIDs []int64 `json:"pendingTimerStartedIDs,omitempty"` PendingActivityScheduledIDs []int64 `json:"pendingActivityScheduledIDs,omitempty"` PendingSignalInitiatedIDs []int64 `json:"pendingSignalInitiatedIDs,omitempty"` PendingReqCancelInitiatedIDs []int64 `json:"pendingReqCancelInitiatedIDs,omitempty"` PendingChildInitiatedIDs []int64 `json:"pendingChildInitiatedIDs,omitempty"` StickyTaskListName *string `json:"stickyTaskListName,omitempty"` VersionHistories *shared.VersionHistories `json:"VersionHistories,omitempty"` } type _List_I64_ValueList []int64 func (v _List_I64_ValueList) ForEach(f func(wire.Value) error) error { for _, x := range v { w, err := wire.NewValueI64(x), error(nil) if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_I64_ValueList) Size() int { return len(v) } func (_List_I64_ValueList) ValueType() wire.Type { return wire.TI64 } func (_List_I64_ValueList) Close() {} // ToWire translates a MutableStateChecksumPayload struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MutableStateChecksumPayload) ToWire() (wire.Value, error) { var ( fields [20]wire.Field i int = 0 w wire.Value err error ) if v.CancelRequested != nil { w, err = wire.NewValueBool(*(v.CancelRequested)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.State != nil { w, err = wire.NewValueI16(*(v.State)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 15, Value: w} i++ } if v.CloseStatus != nil { w, err = wire.NewValueI16(*(v.CloseStatus)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.LastWriteVersion != nil { w, err = wire.NewValueI64(*(v.LastWriteVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 21, Value: w} i++ } if v.LastWriteEventID != nil { w, err = wire.NewValueI64(*(v.LastWriteEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 22, Value: w} i++ } if v.LastFirstEventID != nil { w, err = wire.NewValueI64(*(v.LastFirstEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 23, Value: w} i++ } if v.NextEventID != nil { w, err = wire.NewValueI64(*(v.NextEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 24, Value: w} i++ } if v.LastProcessedEventID != nil { w, err = wire.NewValueI64(*(v.LastProcessedEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 25, Value: w} i++ } if v.SignalCount != nil { w, err = wire.NewValueI64(*(v.SignalCount)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 26, Value: w} i++ } if v.DecisionAttempt != nil { w, err = wire.NewValueI32(*(v.DecisionAttempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 35, Value: w} i++ } if v.DecisionVersion != nil { w, err = wire.NewValueI64(*(v.DecisionVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 36, Value: w} i++ } if v.DecisionScheduledID != nil { w, err = wire.NewValueI64(*(v.DecisionScheduledID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 37, Value: w} i++ } if v.DecisionStartedID != nil { w, err = wire.NewValueI64(*(v.DecisionStartedID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 38, Value: w} i++ } if v.PendingTimerStartedIDs != nil { w, err = wire.NewValueList(_List_I64_ValueList(v.PendingTimerStartedIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 45, Value: w} i++ } if v.PendingActivityScheduledIDs != nil { w, err = wire.NewValueList(_List_I64_ValueList(v.PendingActivityScheduledIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 46, Value: w} i++ } if v.PendingSignalInitiatedIDs != nil { w, err = wire.NewValueList(_List_I64_ValueList(v.PendingSignalInitiatedIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 47, Value: w} i++ } if v.PendingReqCancelInitiatedIDs != nil { w, err = wire.NewValueList(_List_I64_ValueList(v.PendingReqCancelInitiatedIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 48, Value: w} i++ } if v.PendingChildInitiatedIDs != nil { w, err = wire.NewValueList(_List_I64_ValueList(v.PendingChildInitiatedIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 49, Value: w} i++ } if v.StickyTaskListName != nil { w, err = wire.NewValueString(*(v.StickyTaskListName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 55, Value: w} i++ } if v.VersionHistories != nil { w, err = v.VersionHistories.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 56, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _List_I64_Read(l wire.ValueList) ([]int64, error) { if l.ValueType() != wire.TI64 { return nil, nil } o := make([]int64, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := x.GetI64(), error(nil) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _VersionHistories_Read(w wire.Value) (*shared.VersionHistories, error) { var v shared.VersionHistories err := v.FromWire(w) return &v, err } // FromWire deserializes a MutableStateChecksumPayload struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MutableStateChecksumPayload struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MutableStateChecksumPayload // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MutableStateChecksumPayload) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.CancelRequested = &x if err != nil { return err } } case 15: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.State = &x if err != nil { return err } } case 16: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.CloseStatus = &x if err != nil { return err } } case 21: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastWriteVersion = &x if err != nil { return err } } case 22: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastWriteEventID = &x if err != nil { return err } } case 23: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastFirstEventID = &x if err != nil { return err } } case 24: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NextEventID = &x if err != nil { return err } } case 25: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastProcessedEventID = &x if err != nil { return err } } case 26: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.SignalCount = &x if err != nil { return err } } case 35: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.DecisionAttempt = &x if err != nil { return err } } case 36: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionVersion = &x if err != nil { return err } } case 37: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionScheduledID = &x if err != nil { return err } } case 38: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionStartedID = &x if err != nil { return err } } case 45: if field.Value.Type() == wire.TList { v.PendingTimerStartedIDs, err = _List_I64_Read(field.Value.GetList()) if err != nil { return err } } case 46: if field.Value.Type() == wire.TList { v.PendingActivityScheduledIDs, err = _List_I64_Read(field.Value.GetList()) if err != nil { return err } } case 47: if field.Value.Type() == wire.TList { v.PendingSignalInitiatedIDs, err = _List_I64_Read(field.Value.GetList()) if err != nil { return err } } case 48: if field.Value.Type() == wire.TList { v.PendingReqCancelInitiatedIDs, err = _List_I64_Read(field.Value.GetList()) if err != nil { return err } } case 49: if field.Value.Type() == wire.TList { v.PendingChildInitiatedIDs, err = _List_I64_Read(field.Value.GetList()) if err != nil { return err } } case 55: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.StickyTaskListName = &x if err != nil { return err } } case 56: if field.Value.Type() == wire.TStruct { v.VersionHistories, err = _VersionHistories_Read(field.Value) if err != nil { return err } } } } return nil } func _List_I64_Encode(val []int64, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TI64, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for _, v := range val { if err := sw.WriteInt64(v); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a MutableStateChecksumPayload struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MutableStateChecksumPayload struct could not be encoded. func (v *MutableStateChecksumPayload) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CancelRequested != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.CancelRequested)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.State != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 15, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.State)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CloseStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.CloseStatus)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastWriteVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 21, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastWriteVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastWriteEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 22, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastWriteEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFirstEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 23, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastFirstEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 24, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NextEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastProcessedEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 25, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastProcessedEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalCount != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 26, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.SignalCount)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionAttempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 35, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.DecisionAttempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 36, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionScheduledID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 37, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionScheduledID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionStartedID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 38, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionStartedID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingTimerStartedIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 45, Type: wire.TList}); err != nil { return err } if err := _List_I64_Encode(v.PendingTimerStartedIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingActivityScheduledIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 46, Type: wire.TList}); err != nil { return err } if err := _List_I64_Encode(v.PendingActivityScheduledIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingSignalInitiatedIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 47, Type: wire.TList}); err != nil { return err } if err := _List_I64_Encode(v.PendingSignalInitiatedIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingReqCancelInitiatedIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 48, Type: wire.TList}); err != nil { return err } if err := _List_I64_Encode(v.PendingReqCancelInitiatedIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingChildInitiatedIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 49, Type: wire.TList}); err != nil { return err } if err := _List_I64_Encode(v.PendingChildInitiatedIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyTaskListName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 55, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.StickyTaskListName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistories != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 56, Type: wire.TStruct}); err != nil { return err } if err := v.VersionHistories.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _List_I64_Decode(sr stream.Reader) ([]int64, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TI64 { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]int64, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := sr.ReadInt64() if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _VersionHistories_Decode(sr stream.Reader) (*shared.VersionHistories, error) { var v shared.VersionHistories err := v.Decode(sr) return &v, err } // Decode deserializes a MutableStateChecksumPayload struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MutableStateChecksumPayload struct could not be generated from the wire // representation. func (v *MutableStateChecksumPayload) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.CancelRequested = &x if err != nil { return err } case fh.ID == 15 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.State = &x if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.CloseStatus = &x if err != nil { return err } case fh.ID == 21 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastWriteVersion = &x if err != nil { return err } case fh.ID == 22 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastWriteEventID = &x if err != nil { return err } case fh.ID == 23 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastFirstEventID = &x if err != nil { return err } case fh.ID == 24 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NextEventID = &x if err != nil { return err } case fh.ID == 25 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastProcessedEventID = &x if err != nil { return err } case fh.ID == 26 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.SignalCount = &x if err != nil { return err } case fh.ID == 35 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.DecisionAttempt = &x if err != nil { return err } case fh.ID == 36 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionVersion = &x if err != nil { return err } case fh.ID == 37 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionScheduledID = &x if err != nil { return err } case fh.ID == 38 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionStartedID = &x if err != nil { return err } case fh.ID == 45 && fh.Type == wire.TList: v.PendingTimerStartedIDs, err = _List_I64_Decode(sr) if err != nil { return err } case fh.ID == 46 && fh.Type == wire.TList: v.PendingActivityScheduledIDs, err = _List_I64_Decode(sr) if err != nil { return err } case fh.ID == 47 && fh.Type == wire.TList: v.PendingSignalInitiatedIDs, err = _List_I64_Decode(sr) if err != nil { return err } case fh.ID == 48 && fh.Type == wire.TList: v.PendingReqCancelInitiatedIDs, err = _List_I64_Decode(sr) if err != nil { return err } case fh.ID == 49 && fh.Type == wire.TList: v.PendingChildInitiatedIDs, err = _List_I64_Decode(sr) if err != nil { return err } case fh.ID == 55 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.StickyTaskListName = &x if err != nil { return err } case fh.ID == 56 && fh.Type == wire.TStruct: v.VersionHistories, err = _VersionHistories_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MutableStateChecksumPayload // struct. func (v *MutableStateChecksumPayload) String() string { if v == nil { return "" } var fields [20]string i := 0 if v.CancelRequested != nil { fields[i] = fmt.Sprintf("CancelRequested: %v", *(v.CancelRequested)) i++ } if v.State != nil { fields[i] = fmt.Sprintf("State: %v", *(v.State)) i++ } if v.CloseStatus != nil { fields[i] = fmt.Sprintf("CloseStatus: %v", *(v.CloseStatus)) i++ } if v.LastWriteVersion != nil { fields[i] = fmt.Sprintf("LastWriteVersion: %v", *(v.LastWriteVersion)) i++ } if v.LastWriteEventID != nil { fields[i] = fmt.Sprintf("LastWriteEventID: %v", *(v.LastWriteEventID)) i++ } if v.LastFirstEventID != nil { fields[i] = fmt.Sprintf("LastFirstEventID: %v", *(v.LastFirstEventID)) i++ } if v.NextEventID != nil { fields[i] = fmt.Sprintf("NextEventID: %v", *(v.NextEventID)) i++ } if v.LastProcessedEventID != nil { fields[i] = fmt.Sprintf("LastProcessedEventID: %v", *(v.LastProcessedEventID)) i++ } if v.SignalCount != nil { fields[i] = fmt.Sprintf("SignalCount: %v", *(v.SignalCount)) i++ } if v.DecisionAttempt != nil { fields[i] = fmt.Sprintf("DecisionAttempt: %v", *(v.DecisionAttempt)) i++ } if v.DecisionVersion != nil { fields[i] = fmt.Sprintf("DecisionVersion: %v", *(v.DecisionVersion)) i++ } if v.DecisionScheduledID != nil { fields[i] = fmt.Sprintf("DecisionScheduledID: %v", *(v.DecisionScheduledID)) i++ } if v.DecisionStartedID != nil { fields[i] = fmt.Sprintf("DecisionStartedID: %v", *(v.DecisionStartedID)) i++ } if v.PendingTimerStartedIDs != nil { fields[i] = fmt.Sprintf("PendingTimerStartedIDs: %v", v.PendingTimerStartedIDs) i++ } if v.PendingActivityScheduledIDs != nil { fields[i] = fmt.Sprintf("PendingActivityScheduledIDs: %v", v.PendingActivityScheduledIDs) i++ } if v.PendingSignalInitiatedIDs != nil { fields[i] = fmt.Sprintf("PendingSignalInitiatedIDs: %v", v.PendingSignalInitiatedIDs) i++ } if v.PendingReqCancelInitiatedIDs != nil { fields[i] = fmt.Sprintf("PendingReqCancelInitiatedIDs: %v", v.PendingReqCancelInitiatedIDs) i++ } if v.PendingChildInitiatedIDs != nil { fields[i] = fmt.Sprintf("PendingChildInitiatedIDs: %v", v.PendingChildInitiatedIDs) i++ } if v.StickyTaskListName != nil { fields[i] = fmt.Sprintf("StickyTaskListName: %v", *(v.StickyTaskListName)) i++ } if v.VersionHistories != nil { fields[i] = fmt.Sprintf("VersionHistories: %v", v.VersionHistories) i++ } return fmt.Sprintf("MutableStateChecksumPayload{%v}", strings.Join(fields[:i], ", ")) } func _Bool_EqualsPtr(lhs, rhs *bool) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _I16_EqualsPtr(lhs, rhs *int16) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _I64_EqualsPtr(lhs, rhs *int64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _I32_EqualsPtr(lhs, rhs *int32) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _List_I64_Equals(lhs, rhs []int64) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !(lv == rv) { return false } } return true } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this MutableStateChecksumPayload match the // provided MutableStateChecksumPayload. // // This function performs a deep comparison. func (v *MutableStateChecksumPayload) Equals(rhs *MutableStateChecksumPayload) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Bool_EqualsPtr(v.CancelRequested, rhs.CancelRequested) { return false } if !_I16_EqualsPtr(v.State, rhs.State) { return false } if !_I16_EqualsPtr(v.CloseStatus, rhs.CloseStatus) { return false } if !_I64_EqualsPtr(v.LastWriteVersion, rhs.LastWriteVersion) { return false } if !_I64_EqualsPtr(v.LastWriteEventID, rhs.LastWriteEventID) { return false } if !_I64_EqualsPtr(v.LastFirstEventID, rhs.LastFirstEventID) { return false } if !_I64_EqualsPtr(v.NextEventID, rhs.NextEventID) { return false } if !_I64_EqualsPtr(v.LastProcessedEventID, rhs.LastProcessedEventID) { return false } if !_I64_EqualsPtr(v.SignalCount, rhs.SignalCount) { return false } if !_I32_EqualsPtr(v.DecisionAttempt, rhs.DecisionAttempt) { return false } if !_I64_EqualsPtr(v.DecisionVersion, rhs.DecisionVersion) { return false } if !_I64_EqualsPtr(v.DecisionScheduledID, rhs.DecisionScheduledID) { return false } if !_I64_EqualsPtr(v.DecisionStartedID, rhs.DecisionStartedID) { return false } if !((v.PendingTimerStartedIDs == nil && rhs.PendingTimerStartedIDs == nil) || (v.PendingTimerStartedIDs != nil && rhs.PendingTimerStartedIDs != nil && _List_I64_Equals(v.PendingTimerStartedIDs, rhs.PendingTimerStartedIDs))) { return false } if !((v.PendingActivityScheduledIDs == nil && rhs.PendingActivityScheduledIDs == nil) || (v.PendingActivityScheduledIDs != nil && rhs.PendingActivityScheduledIDs != nil && _List_I64_Equals(v.PendingActivityScheduledIDs, rhs.PendingActivityScheduledIDs))) { return false } if !((v.PendingSignalInitiatedIDs == nil && rhs.PendingSignalInitiatedIDs == nil) || (v.PendingSignalInitiatedIDs != nil && rhs.PendingSignalInitiatedIDs != nil && _List_I64_Equals(v.PendingSignalInitiatedIDs, rhs.PendingSignalInitiatedIDs))) { return false } if !((v.PendingReqCancelInitiatedIDs == nil && rhs.PendingReqCancelInitiatedIDs == nil) || (v.PendingReqCancelInitiatedIDs != nil && rhs.PendingReqCancelInitiatedIDs != nil && _List_I64_Equals(v.PendingReqCancelInitiatedIDs, rhs.PendingReqCancelInitiatedIDs))) { return false } if !((v.PendingChildInitiatedIDs == nil && rhs.PendingChildInitiatedIDs == nil) || (v.PendingChildInitiatedIDs != nil && rhs.PendingChildInitiatedIDs != nil && _List_I64_Equals(v.PendingChildInitiatedIDs, rhs.PendingChildInitiatedIDs))) { return false } if !_String_EqualsPtr(v.StickyTaskListName, rhs.StickyTaskListName) { return false } if !((v.VersionHistories == nil && rhs.VersionHistories == nil) || (v.VersionHistories != nil && rhs.VersionHistories != nil && v.VersionHistories.Equals(rhs.VersionHistories))) { return false } return true } type _List_I64_Zapper []int64 // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_I64_Zapper. func (l _List_I64_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { enc.AppendInt64(v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MutableStateChecksumPayload. func (v *MutableStateChecksumPayload) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CancelRequested != nil { enc.AddBool("cancelRequested", *v.CancelRequested) } if v.State != nil { enc.AddInt16("state", *v.State) } if v.CloseStatus != nil { enc.AddInt16("closeStatus", *v.CloseStatus) } if v.LastWriteVersion != nil { enc.AddInt64("lastWriteVersion", *v.LastWriteVersion) } if v.LastWriteEventID != nil { enc.AddInt64("lastWriteEventID", *v.LastWriteEventID) } if v.LastFirstEventID != nil { enc.AddInt64("lastFirstEventID", *v.LastFirstEventID) } if v.NextEventID != nil { enc.AddInt64("nextEventID", *v.NextEventID) } if v.LastProcessedEventID != nil { enc.AddInt64("lastProcessedEventID", *v.LastProcessedEventID) } if v.SignalCount != nil { enc.AddInt64("signalCount", *v.SignalCount) } if v.DecisionAttempt != nil { enc.AddInt32("decisionAttempt", *v.DecisionAttempt) } if v.DecisionVersion != nil { enc.AddInt64("decisionVersion", *v.DecisionVersion) } if v.DecisionScheduledID != nil { enc.AddInt64("decisionScheduledID", *v.DecisionScheduledID) } if v.DecisionStartedID != nil { enc.AddInt64("decisionStartedID", *v.DecisionStartedID) } if v.PendingTimerStartedIDs != nil { err = multierr.Append(err, enc.AddArray("pendingTimerStartedIDs", (_List_I64_Zapper)(v.PendingTimerStartedIDs))) } if v.PendingActivityScheduledIDs != nil { err = multierr.Append(err, enc.AddArray("pendingActivityScheduledIDs", (_List_I64_Zapper)(v.PendingActivityScheduledIDs))) } if v.PendingSignalInitiatedIDs != nil { err = multierr.Append(err, enc.AddArray("pendingSignalInitiatedIDs", (_List_I64_Zapper)(v.PendingSignalInitiatedIDs))) } if v.PendingReqCancelInitiatedIDs != nil { err = multierr.Append(err, enc.AddArray("pendingReqCancelInitiatedIDs", (_List_I64_Zapper)(v.PendingReqCancelInitiatedIDs))) } if v.PendingChildInitiatedIDs != nil { err = multierr.Append(err, enc.AddArray("pendingChildInitiatedIDs", (_List_I64_Zapper)(v.PendingChildInitiatedIDs))) } if v.StickyTaskListName != nil { enc.AddString("stickyTaskListName", *v.StickyTaskListName) } if v.VersionHistories != nil { err = multierr.Append(err, enc.AddObject("VersionHistories", v.VersionHistories)) } return err } // GetCancelRequested returns the value of CancelRequested if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetCancelRequested() (o bool) { if v != nil && v.CancelRequested != nil { return *v.CancelRequested } return } // IsSetCancelRequested returns true if CancelRequested is not nil. func (v *MutableStateChecksumPayload) IsSetCancelRequested() bool { return v != nil && v.CancelRequested != nil } // GetState returns the value of State if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetState() (o int16) { if v != nil && v.State != nil { return *v.State } return } // IsSetState returns true if State is not nil. func (v *MutableStateChecksumPayload) IsSetState() bool { return v != nil && v.State != nil } // GetCloseStatus returns the value of CloseStatus if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetCloseStatus() (o int16) { if v != nil && v.CloseStatus != nil { return *v.CloseStatus } return } // IsSetCloseStatus returns true if CloseStatus is not nil. func (v *MutableStateChecksumPayload) IsSetCloseStatus() bool { return v != nil && v.CloseStatus != nil } // GetLastWriteVersion returns the value of LastWriteVersion if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetLastWriteVersion() (o int64) { if v != nil && v.LastWriteVersion != nil { return *v.LastWriteVersion } return } // IsSetLastWriteVersion returns true if LastWriteVersion is not nil. func (v *MutableStateChecksumPayload) IsSetLastWriteVersion() bool { return v != nil && v.LastWriteVersion != nil } // GetLastWriteEventID returns the value of LastWriteEventID if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetLastWriteEventID() (o int64) { if v != nil && v.LastWriteEventID != nil { return *v.LastWriteEventID } return } // IsSetLastWriteEventID returns true if LastWriteEventID is not nil. func (v *MutableStateChecksumPayload) IsSetLastWriteEventID() bool { return v != nil && v.LastWriteEventID != nil } // GetLastFirstEventID returns the value of LastFirstEventID if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetLastFirstEventID() (o int64) { if v != nil && v.LastFirstEventID != nil { return *v.LastFirstEventID } return } // IsSetLastFirstEventID returns true if LastFirstEventID is not nil. func (v *MutableStateChecksumPayload) IsSetLastFirstEventID() bool { return v != nil && v.LastFirstEventID != nil } // GetNextEventID returns the value of NextEventID if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetNextEventID() (o int64) { if v != nil && v.NextEventID != nil { return *v.NextEventID } return } // IsSetNextEventID returns true if NextEventID is not nil. func (v *MutableStateChecksumPayload) IsSetNextEventID() bool { return v != nil && v.NextEventID != nil } // GetLastProcessedEventID returns the value of LastProcessedEventID if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetLastProcessedEventID() (o int64) { if v != nil && v.LastProcessedEventID != nil { return *v.LastProcessedEventID } return } // IsSetLastProcessedEventID returns true if LastProcessedEventID is not nil. func (v *MutableStateChecksumPayload) IsSetLastProcessedEventID() bool { return v != nil && v.LastProcessedEventID != nil } // GetSignalCount returns the value of SignalCount if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetSignalCount() (o int64) { if v != nil && v.SignalCount != nil { return *v.SignalCount } return } // IsSetSignalCount returns true if SignalCount is not nil. func (v *MutableStateChecksumPayload) IsSetSignalCount() bool { return v != nil && v.SignalCount != nil } // GetDecisionAttempt returns the value of DecisionAttempt if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetDecisionAttempt() (o int32) { if v != nil && v.DecisionAttempt != nil { return *v.DecisionAttempt } return } // IsSetDecisionAttempt returns true if DecisionAttempt is not nil. func (v *MutableStateChecksumPayload) IsSetDecisionAttempt() bool { return v != nil && v.DecisionAttempt != nil } // GetDecisionVersion returns the value of DecisionVersion if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetDecisionVersion() (o int64) { if v != nil && v.DecisionVersion != nil { return *v.DecisionVersion } return } // IsSetDecisionVersion returns true if DecisionVersion is not nil. func (v *MutableStateChecksumPayload) IsSetDecisionVersion() bool { return v != nil && v.DecisionVersion != nil } // GetDecisionScheduledID returns the value of DecisionScheduledID if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetDecisionScheduledID() (o int64) { if v != nil && v.DecisionScheduledID != nil { return *v.DecisionScheduledID } return } // IsSetDecisionScheduledID returns true if DecisionScheduledID is not nil. func (v *MutableStateChecksumPayload) IsSetDecisionScheduledID() bool { return v != nil && v.DecisionScheduledID != nil } // GetDecisionStartedID returns the value of DecisionStartedID if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetDecisionStartedID() (o int64) { if v != nil && v.DecisionStartedID != nil { return *v.DecisionStartedID } return } // IsSetDecisionStartedID returns true if DecisionStartedID is not nil. func (v *MutableStateChecksumPayload) IsSetDecisionStartedID() bool { return v != nil && v.DecisionStartedID != nil } // GetPendingTimerStartedIDs returns the value of PendingTimerStartedIDs if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetPendingTimerStartedIDs() (o []int64) { if v != nil && v.PendingTimerStartedIDs != nil { return v.PendingTimerStartedIDs } return } // IsSetPendingTimerStartedIDs returns true if PendingTimerStartedIDs is not nil. func (v *MutableStateChecksumPayload) IsSetPendingTimerStartedIDs() bool { return v != nil && v.PendingTimerStartedIDs != nil } // GetPendingActivityScheduledIDs returns the value of PendingActivityScheduledIDs if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetPendingActivityScheduledIDs() (o []int64) { if v != nil && v.PendingActivityScheduledIDs != nil { return v.PendingActivityScheduledIDs } return } // IsSetPendingActivityScheduledIDs returns true if PendingActivityScheduledIDs is not nil. func (v *MutableStateChecksumPayload) IsSetPendingActivityScheduledIDs() bool { return v != nil && v.PendingActivityScheduledIDs != nil } // GetPendingSignalInitiatedIDs returns the value of PendingSignalInitiatedIDs if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetPendingSignalInitiatedIDs() (o []int64) { if v != nil && v.PendingSignalInitiatedIDs != nil { return v.PendingSignalInitiatedIDs } return } // IsSetPendingSignalInitiatedIDs returns true if PendingSignalInitiatedIDs is not nil. func (v *MutableStateChecksumPayload) IsSetPendingSignalInitiatedIDs() bool { return v != nil && v.PendingSignalInitiatedIDs != nil } // GetPendingReqCancelInitiatedIDs returns the value of PendingReqCancelInitiatedIDs if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetPendingReqCancelInitiatedIDs() (o []int64) { if v != nil && v.PendingReqCancelInitiatedIDs != nil { return v.PendingReqCancelInitiatedIDs } return } // IsSetPendingReqCancelInitiatedIDs returns true if PendingReqCancelInitiatedIDs is not nil. func (v *MutableStateChecksumPayload) IsSetPendingReqCancelInitiatedIDs() bool { return v != nil && v.PendingReqCancelInitiatedIDs != nil } // GetPendingChildInitiatedIDs returns the value of PendingChildInitiatedIDs if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetPendingChildInitiatedIDs() (o []int64) { if v != nil && v.PendingChildInitiatedIDs != nil { return v.PendingChildInitiatedIDs } return } // IsSetPendingChildInitiatedIDs returns true if PendingChildInitiatedIDs is not nil. func (v *MutableStateChecksumPayload) IsSetPendingChildInitiatedIDs() bool { return v != nil && v.PendingChildInitiatedIDs != nil } // GetStickyTaskListName returns the value of StickyTaskListName if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetStickyTaskListName() (o string) { if v != nil && v.StickyTaskListName != nil { return *v.StickyTaskListName } return } // IsSetStickyTaskListName returns true if StickyTaskListName is not nil. func (v *MutableStateChecksumPayload) IsSetStickyTaskListName() bool { return v != nil && v.StickyTaskListName != nil } // GetVersionHistories returns the value of VersionHistories if it is set or its // zero value if it is unset. func (v *MutableStateChecksumPayload) GetVersionHistories() (o *shared.VersionHistories) { if v != nil && v.VersionHistories != nil { return v.VersionHistories } return } // IsSetVersionHistories returns true if VersionHistories is not nil. func (v *MutableStateChecksumPayload) IsSetVersionHistories() bool { return v != nil && v.VersionHistories != nil } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "checksum", Package: "github.com/uber/cadence/.gen/go/checksum", FilePath: "checksum.thrift", SHA1: "c3ee77b53c2e06c35a3296cfeeeadf140711ed95", Includes: []*thriftreflect.ThriftModule{ shared.ThriftModule, }, Raw: rawIDL, } const rawIDL = "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\ninclude \"shared.thrift\"\n\nnamespace java com.uber.cadence\n\nstruct MutableStateChecksumPayload {\n 10: optional bool cancelRequested\n 15: optional i16 state\n 16: optional i16 closeStatus\n\n 21: optional i64 (js.type = \"Long\") lastWriteVersion\n 22: optional i64 (js.type = \"Long\") lastWriteEventID\n 23: optional i64 (js.type = \"Long\") lastFirstEventID\n 24: optional i64 (js.type = \"Long\") nextEventID\n 25: optional i64 (js.type = \"Long\") lastProcessedEventID\n 26: optional i64 (js.type = \"Long\") signalCount\n\n 35: optional i32 decisionAttempt\n 36: optional i64 (js.type = \"Long\") decisionVersion\n 37: optional i64 (js.type = \"Long\") decisionScheduledID\n 38: optional i64 (js.type = \"Long\") decisionStartedID\n\n 45: optional list pendingTimerStartedIDs\n 46: optional list pendingActivityScheduledIDs\n 47: optional list pendingSignalInitiatedIDs\n 48: optional list pendingReqCancelInitiatedIDs\n 49: optional list pendingChildInitiatedIDs\n\n 55: optional string stickyTaskListName\n 56: optional shared.VersionHistories VersionHistories\n}\n" ================================================ FILE: .gen/go/checksum/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package checksum ================================================ FILE: .gen/go/config/config.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package config import ( fmt "fmt" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" shared "github.com/uber/cadence/.gen/go/shared" ) type DynamicConfigBlob struct { SchemaVersion *int64 `json:"schemaVersion,omitempty"` Entries []*DynamicConfigEntry `json:"entries,omitempty"` } type _List_DynamicConfigEntry_ValueList []*DynamicConfigEntry func (v _List_DynamicConfigEntry_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*DynamicConfigEntry', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_DynamicConfigEntry_ValueList) Size() int { return len(v) } func (_List_DynamicConfigEntry_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_DynamicConfigEntry_ValueList) Close() {} // ToWire translates a DynamicConfigBlob struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DynamicConfigBlob) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.SchemaVersion != nil { w, err = wire.NewValueI64(*(v.SchemaVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Entries != nil { w, err = wire.NewValueList(_List_DynamicConfigEntry_ValueList(v.Entries)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DynamicConfigEntry_Read(w wire.Value) (*DynamicConfigEntry, error) { var v DynamicConfigEntry err := v.FromWire(w) return &v, err } func _List_DynamicConfigEntry_Read(l wire.ValueList) ([]*DynamicConfigEntry, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*DynamicConfigEntry, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _DynamicConfigEntry_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a DynamicConfigBlob struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DynamicConfigBlob struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DynamicConfigBlob // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DynamicConfigBlob) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.SchemaVersion = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Entries, err = _List_DynamicConfigEntry_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_DynamicConfigEntry_Encode(val []*DynamicConfigEntry, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*DynamicConfigEntry', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a DynamicConfigBlob struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DynamicConfigBlob struct could not be encoded. func (v *DynamicConfigBlob) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SchemaVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.SchemaVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Entries != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_DynamicConfigEntry_Encode(v.Entries, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DynamicConfigEntry_Decode(sr stream.Reader) (*DynamicConfigEntry, error) { var v DynamicConfigEntry err := v.Decode(sr) return &v, err } func _List_DynamicConfigEntry_Decode(sr stream.Reader) ([]*DynamicConfigEntry, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*DynamicConfigEntry, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _DynamicConfigEntry_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a DynamicConfigBlob struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DynamicConfigBlob struct could not be generated from the wire // representation. func (v *DynamicConfigBlob) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.SchemaVersion = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Entries, err = _List_DynamicConfigEntry_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DynamicConfigBlob // struct. func (v *DynamicConfigBlob) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.SchemaVersion != nil { fields[i] = fmt.Sprintf("SchemaVersion: %v", *(v.SchemaVersion)) i++ } if v.Entries != nil { fields[i] = fmt.Sprintf("Entries: %v", v.Entries) i++ } return fmt.Sprintf("DynamicConfigBlob{%v}", strings.Join(fields[:i], ", ")) } func _I64_EqualsPtr(lhs, rhs *int64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _List_DynamicConfigEntry_Equals(lhs, rhs []*DynamicConfigEntry) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this DynamicConfigBlob match the // provided DynamicConfigBlob. // // This function performs a deep comparison. func (v *DynamicConfigBlob) Equals(rhs *DynamicConfigBlob) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.SchemaVersion, rhs.SchemaVersion) { return false } if !((v.Entries == nil && rhs.Entries == nil) || (v.Entries != nil && rhs.Entries != nil && _List_DynamicConfigEntry_Equals(v.Entries, rhs.Entries))) { return false } return true } type _List_DynamicConfigEntry_Zapper []*DynamicConfigEntry // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_DynamicConfigEntry_Zapper. func (l _List_DynamicConfigEntry_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DynamicConfigBlob. func (v *DynamicConfigBlob) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SchemaVersion != nil { enc.AddInt64("schemaVersion", *v.SchemaVersion) } if v.Entries != nil { err = multierr.Append(err, enc.AddArray("entries", (_List_DynamicConfigEntry_Zapper)(v.Entries))) } return err } // GetSchemaVersion returns the value of SchemaVersion if it is set or its // zero value if it is unset. func (v *DynamicConfigBlob) GetSchemaVersion() (o int64) { if v != nil && v.SchemaVersion != nil { return *v.SchemaVersion } return } // IsSetSchemaVersion returns true if SchemaVersion is not nil. func (v *DynamicConfigBlob) IsSetSchemaVersion() bool { return v != nil && v.SchemaVersion != nil } // GetEntries returns the value of Entries if it is set or its // zero value if it is unset. func (v *DynamicConfigBlob) GetEntries() (o []*DynamicConfigEntry) { if v != nil && v.Entries != nil { return v.Entries } return } // IsSetEntries returns true if Entries is not nil. func (v *DynamicConfigBlob) IsSetEntries() bool { return v != nil && v.Entries != nil } type DynamicConfigEntry struct { Name *string `json:"name,omitempty"` Values []*DynamicConfigValue `json:"values,omitempty"` } type _List_DynamicConfigValue_ValueList []*DynamicConfigValue func (v _List_DynamicConfigValue_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*DynamicConfigValue', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_DynamicConfigValue_ValueList) Size() int { return len(v) } func (_List_DynamicConfigValue_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_DynamicConfigValue_ValueList) Close() {} // ToWire translates a DynamicConfigEntry struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DynamicConfigEntry) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Values != nil { w, err = wire.NewValueList(_List_DynamicConfigValue_ValueList(v.Values)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DynamicConfigValue_Read(w wire.Value) (*DynamicConfigValue, error) { var v DynamicConfigValue err := v.FromWire(w) return &v, err } func _List_DynamicConfigValue_Read(l wire.ValueList) ([]*DynamicConfigValue, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*DynamicConfigValue, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _DynamicConfigValue_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a DynamicConfigEntry struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DynamicConfigEntry struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DynamicConfigEntry // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DynamicConfigEntry) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Values, err = _List_DynamicConfigValue_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_DynamicConfigValue_Encode(val []*DynamicConfigValue, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*DynamicConfigValue', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a DynamicConfigEntry struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DynamicConfigEntry struct could not be encoded. func (v *DynamicConfigEntry) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Values != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_DynamicConfigValue_Encode(v.Values, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DynamicConfigValue_Decode(sr stream.Reader) (*DynamicConfigValue, error) { var v DynamicConfigValue err := v.Decode(sr) return &v, err } func _List_DynamicConfigValue_Decode(sr stream.Reader) ([]*DynamicConfigValue, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*DynamicConfigValue, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _DynamicConfigValue_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a DynamicConfigEntry struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DynamicConfigEntry struct could not be generated from the wire // representation. func (v *DynamicConfigEntry) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Values, err = _List_DynamicConfigValue_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DynamicConfigEntry // struct. func (v *DynamicConfigEntry) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.Values != nil { fields[i] = fmt.Sprintf("Values: %v", v.Values) i++ } return fmt.Sprintf("DynamicConfigEntry{%v}", strings.Join(fields[:i], ", ")) } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _List_DynamicConfigValue_Equals(lhs, rhs []*DynamicConfigValue) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this DynamicConfigEntry match the // provided DynamicConfigEntry. // // This function performs a deep comparison. func (v *DynamicConfigEntry) Equals(rhs *DynamicConfigEntry) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !((v.Values == nil && rhs.Values == nil) || (v.Values != nil && rhs.Values != nil && _List_DynamicConfigValue_Equals(v.Values, rhs.Values))) { return false } return true } type _List_DynamicConfigValue_Zapper []*DynamicConfigValue // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_DynamicConfigValue_Zapper. func (l _List_DynamicConfigValue_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DynamicConfigEntry. func (v *DynamicConfigEntry) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.Values != nil { err = multierr.Append(err, enc.AddArray("values", (_List_DynamicConfigValue_Zapper)(v.Values))) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *DynamicConfigEntry) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *DynamicConfigEntry) IsSetName() bool { return v != nil && v.Name != nil } // GetValues returns the value of Values if it is set or its // zero value if it is unset. func (v *DynamicConfigEntry) GetValues() (o []*DynamicConfigValue) { if v != nil && v.Values != nil { return v.Values } return } // IsSetValues returns true if Values is not nil. func (v *DynamicConfigEntry) IsSetValues() bool { return v != nil && v.Values != nil } type DynamicConfigFilter struct { Name *string `json:"name,omitempty"` Value *shared.DataBlob `json:"value,omitempty"` } // ToWire translates a DynamicConfigFilter struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DynamicConfigFilter) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Value != nil { w, err = v.Value.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DataBlob_Read(w wire.Value) (*shared.DataBlob, error) { var v shared.DataBlob err := v.FromWire(w) return &v, err } // FromWire deserializes a DynamicConfigFilter struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DynamicConfigFilter struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DynamicConfigFilter // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DynamicConfigFilter) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Value, err = _DataBlob_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a DynamicConfigFilter struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DynamicConfigFilter struct could not be encoded. func (v *DynamicConfigFilter) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Value != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Value.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DataBlob_Decode(sr stream.Reader) (*shared.DataBlob, error) { var v shared.DataBlob err := v.Decode(sr) return &v, err } // Decode deserializes a DynamicConfigFilter struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DynamicConfigFilter struct could not be generated from the wire // representation. func (v *DynamicConfigFilter) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Value, err = _DataBlob_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DynamicConfigFilter // struct. func (v *DynamicConfigFilter) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.Value != nil { fields[i] = fmt.Sprintf("Value: %v", v.Value) i++ } return fmt.Sprintf("DynamicConfigFilter{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DynamicConfigFilter match the // provided DynamicConfigFilter. // // This function performs a deep comparison. func (v *DynamicConfigFilter) Equals(rhs *DynamicConfigFilter) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !((v.Value == nil && rhs.Value == nil) || (v.Value != nil && rhs.Value != nil && v.Value.Equals(rhs.Value))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DynamicConfigFilter. func (v *DynamicConfigFilter) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.Value != nil { err = multierr.Append(err, enc.AddObject("value", v.Value)) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *DynamicConfigFilter) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *DynamicConfigFilter) IsSetName() bool { return v != nil && v.Name != nil } // GetValue returns the value of Value if it is set or its // zero value if it is unset. func (v *DynamicConfigFilter) GetValue() (o *shared.DataBlob) { if v != nil && v.Value != nil { return v.Value } return } // IsSetValue returns true if Value is not nil. func (v *DynamicConfigFilter) IsSetValue() bool { return v != nil && v.Value != nil } type DynamicConfigValue struct { Value *shared.DataBlob `json:"value,omitempty"` Filters []*DynamicConfigFilter `json:"filters,omitempty"` } type _List_DynamicConfigFilter_ValueList []*DynamicConfigFilter func (v _List_DynamicConfigFilter_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*DynamicConfigFilter', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_DynamicConfigFilter_ValueList) Size() int { return len(v) } func (_List_DynamicConfigFilter_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_DynamicConfigFilter_ValueList) Close() {} // ToWire translates a DynamicConfigValue struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DynamicConfigValue) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Value != nil { w, err = v.Value.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Filters != nil { w, err = wire.NewValueList(_List_DynamicConfigFilter_ValueList(v.Filters)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DynamicConfigFilter_Read(w wire.Value) (*DynamicConfigFilter, error) { var v DynamicConfigFilter err := v.FromWire(w) return &v, err } func _List_DynamicConfigFilter_Read(l wire.ValueList) ([]*DynamicConfigFilter, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*DynamicConfigFilter, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _DynamicConfigFilter_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a DynamicConfigValue struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DynamicConfigValue struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DynamicConfigValue // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DynamicConfigValue) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Value, err = _DataBlob_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Filters, err = _List_DynamicConfigFilter_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_DynamicConfigFilter_Encode(val []*DynamicConfigFilter, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*DynamicConfigFilter', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a DynamicConfigValue struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DynamicConfigValue struct could not be encoded. func (v *DynamicConfigValue) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Value != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Value.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Filters != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_DynamicConfigFilter_Encode(v.Filters, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DynamicConfigFilter_Decode(sr stream.Reader) (*DynamicConfigFilter, error) { var v DynamicConfigFilter err := v.Decode(sr) return &v, err } func _List_DynamicConfigFilter_Decode(sr stream.Reader) ([]*DynamicConfigFilter, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*DynamicConfigFilter, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _DynamicConfigFilter_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a DynamicConfigValue struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DynamicConfigValue struct could not be generated from the wire // representation. func (v *DynamicConfigValue) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Value, err = _DataBlob_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Filters, err = _List_DynamicConfigFilter_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DynamicConfigValue // struct. func (v *DynamicConfigValue) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Value != nil { fields[i] = fmt.Sprintf("Value: %v", v.Value) i++ } if v.Filters != nil { fields[i] = fmt.Sprintf("Filters: %v", v.Filters) i++ } return fmt.Sprintf("DynamicConfigValue{%v}", strings.Join(fields[:i], ", ")) } func _List_DynamicConfigFilter_Equals(lhs, rhs []*DynamicConfigFilter) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this DynamicConfigValue match the // provided DynamicConfigValue. // // This function performs a deep comparison. func (v *DynamicConfigValue) Equals(rhs *DynamicConfigValue) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Value == nil && rhs.Value == nil) || (v.Value != nil && rhs.Value != nil && v.Value.Equals(rhs.Value))) { return false } if !((v.Filters == nil && rhs.Filters == nil) || (v.Filters != nil && rhs.Filters != nil && _List_DynamicConfigFilter_Equals(v.Filters, rhs.Filters))) { return false } return true } type _List_DynamicConfigFilter_Zapper []*DynamicConfigFilter // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_DynamicConfigFilter_Zapper. func (l _List_DynamicConfigFilter_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DynamicConfigValue. func (v *DynamicConfigValue) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Value != nil { err = multierr.Append(err, enc.AddObject("value", v.Value)) } if v.Filters != nil { err = multierr.Append(err, enc.AddArray("filters", (_List_DynamicConfigFilter_Zapper)(v.Filters))) } return err } // GetValue returns the value of Value if it is set or its // zero value if it is unset. func (v *DynamicConfigValue) GetValue() (o *shared.DataBlob) { if v != nil && v.Value != nil { return v.Value } return } // IsSetValue returns true if Value is not nil. func (v *DynamicConfigValue) IsSetValue() bool { return v != nil && v.Value != nil } // GetFilters returns the value of Filters if it is set or its // zero value if it is unset. func (v *DynamicConfigValue) GetFilters() (o []*DynamicConfigFilter) { if v != nil && v.Filters != nil { return v.Filters } return } // IsSetFilters returns true if Filters is not nil. func (v *DynamicConfigValue) IsSetFilters() bool { return v != nil && v.Filters != nil } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "config", Package: "github.com/uber/cadence/.gen/go/config", FilePath: "config.thrift", SHA1: "cbc9d97e2a2f4820452fc2d273d5102e4721cf34", Includes: []*thriftreflect.ThriftModule{ shared.ThriftModule, }, Raw: rawIDL, } const rawIDL = "//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nnamespace java com.uber.cadence.config\n\ninclude \"shared.thrift\"\n\nstruct DynamicConfigBlob {\n\t10: optional i64 schemaVersion\n\t20: optional list entries\n}\n\nstruct DynamicConfigEntry {\n 10: optional string name\n 20: optional list values\n}\n\nstruct DynamicConfigValue {\n 10: optional shared.DataBlob value\n 20: optional list filters\n}\n\nstruct DynamicConfigFilter {\n 10: optional string name\n 20: optional shared.DataBlob value\n}\n" ================================================ FILE: .gen/go/config/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package config ================================================ FILE: .gen/go/health/health.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package health import ( errors "errors" fmt "fmt" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" ) type HealthStatus struct { Ok bool `json:"ok,required"` Msg *string `json:"msg,omitempty"` } // ToWire translates a HealthStatus struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HealthStatus) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueBool(v.Ok), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ if v.Msg != nil { w, err = wire.NewValueString(*(v.Msg)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HealthStatus struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HealthStatus struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HealthStatus // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HealthStatus) FromWire(w wire.Value) error { var err error okIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBool { v.Ok, err = field.Value.GetBool(), error(nil) if err != nil { return err } okIsSet = true } case 2: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Msg = &x if err != nil { return err } } } } if !okIsSet { return errors.New("field Ok of HealthStatus is required") } return nil } // Encode serializes a HealthStatus struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HealthStatus struct could not be encoded. func (v *HealthStatus) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(v.Ok); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if v.Msg != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Msg)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a HealthStatus struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HealthStatus struct could not be generated from the wire // representation. func (v *HealthStatus) Decode(sr stream.Reader) error { okIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBool: v.Ok, err = sr.ReadBool() if err != nil { return err } okIsSet = true case fh.ID == 2 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Msg = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !okIsSet { return errors.New("field Ok of HealthStatus is required") } return nil } // String returns a readable string representation of a HealthStatus // struct. func (v *HealthStatus) String() string { if v == nil { return "" } var fields [2]string i := 0 fields[i] = fmt.Sprintf("Ok: %v", v.Ok) i++ if v.Msg != nil { fields[i] = fmt.Sprintf("Msg: %v", *(v.Msg)) i++ } return fmt.Sprintf("HealthStatus{%v}", strings.Join(fields[:i], ", ")) } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this HealthStatus match the // provided HealthStatus. // // This function performs a deep comparison. func (v *HealthStatus) Equals(rhs *HealthStatus) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Ok == rhs.Ok) { return false } if !_String_EqualsPtr(v.Msg, rhs.Msg) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HealthStatus. func (v *HealthStatus) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddBool("ok", v.Ok) if v.Msg != nil { enc.AddString("msg", *v.Msg) } return err } // GetOk returns the value of Ok if it is set or its // zero value if it is unset. func (v *HealthStatus) GetOk() (o bool) { if v != nil { o = v.Ok } return } // GetMsg returns the value of Msg if it is set or its // zero value if it is unset. func (v *HealthStatus) GetMsg() (o string) { if v != nil && v.Msg != nil { return *v.Msg } return } // IsSetMsg returns true if Msg is not nil. func (v *HealthStatus) IsSetMsg() bool { return v != nil && v.Msg != nil } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "health", Package: "github.com/uber/cadence/.gen/go/health", FilePath: "health.thrift", SHA1: "8d52f05c157e47bef27c86d2133e1cdb475f8024", Raw: rawIDL, } const rawIDL = "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nnamespace java com.uber.cadence\n\n/* ==================== Health Check ==================== */\n\nstruct HealthStatus {\n 1: required bool ok\n 2: optional string msg\n}\n\nservice Meta {\n HealthStatus health()\n}\n\n" // Meta_Health_Args represents the arguments for the Meta.health function. // // The arguments for health are sent and received over the wire as this struct. type Meta_Health_Args struct { } // ToWire translates a Meta_Health_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *Meta_Health_Args) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a Meta_Health_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a Meta_Health_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v Meta_Health_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *Meta_Health_Args) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a Meta_Health_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a Meta_Health_Args struct could not be encoded. func (v *Meta_Health_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a Meta_Health_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a Meta_Health_Args struct could not be generated from the wire // representation. func (v *Meta_Health_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a Meta_Health_Args // struct. func (v *Meta_Health_Args) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("Meta_Health_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this Meta_Health_Args match the // provided Meta_Health_Args. // // This function performs a deep comparison. func (v *Meta_Health_Args) Equals(rhs *Meta_Health_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of Meta_Health_Args. func (v *Meta_Health_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "health" for this struct. func (v *Meta_Health_Args) MethodName() string { return "health" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *Meta_Health_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // Meta_Health_Helper provides functions that aid in handling the // parameters and return values of the Meta.health // function. var Meta_Health_Helper = struct { // Args accepts the parameters of health in-order and returns // the arguments struct for the function. Args func() *Meta_Health_Args // IsException returns true if the given error can be thrown // by health. // // An error can be thrown by health only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for health // given its return value and error. // // This allows mapping values and errors returned by // health into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by health // // value, err := health(args) // result, err := Meta_Health_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from health: %v", err) // } // serialize(result) WrapResponse func(*HealthStatus, error) (*Meta_Health_Result, error) // UnwrapResponse takes the result struct for health // and returns the value or error returned by it. // // The error is non-nil only if health threw an // exception. // // result := deserialize(bytes) // value, err := Meta_Health_Helper.UnwrapResponse(result) UnwrapResponse func(*Meta_Health_Result) (*HealthStatus, error) }{} func init() { Meta_Health_Helper.Args = func() *Meta_Health_Args { return &Meta_Health_Args{} } Meta_Health_Helper.IsException = func(err error) bool { switch err.(type) { default: return false } } Meta_Health_Helper.WrapResponse = func(success *HealthStatus, err error) (*Meta_Health_Result, error) { if err == nil { return &Meta_Health_Result{Success: success}, nil } return nil, err } Meta_Health_Helper.UnwrapResponse = func(result *Meta_Health_Result) (success *HealthStatus, err error) { if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // Meta_Health_Result represents the result of a Meta.health function call. // // The result of a health execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type Meta_Health_Result struct { // Value returned by health after a successful execution. Success *HealthStatus `json:"success,omitempty"` } // ToWire translates a Meta_Health_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *Meta_Health_Result) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("Meta_Health_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _HealthStatus_Read(w wire.Value) (*HealthStatus, error) { var v HealthStatus err := v.FromWire(w) return &v, err } // FromWire deserializes a Meta_Health_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a Meta_Health_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v Meta_Health_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *Meta_Health_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _HealthStatus_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if count != 1 { return fmt.Errorf("Meta_Health_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a Meta_Health_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a Meta_Health_Result struct could not be encoded. func (v *Meta_Health_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if count != 1 { return fmt.Errorf("Meta_Health_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _HealthStatus_Decode(sr stream.Reader) (*HealthStatus, error) { var v HealthStatus err := v.Decode(sr) return &v, err } // Decode deserializes a Meta_Health_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a Meta_Health_Result struct could not be generated from the wire // representation. func (v *Meta_Health_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _HealthStatus_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if count != 1 { return fmt.Errorf("Meta_Health_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a Meta_Health_Result // struct. func (v *Meta_Health_Result) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } return fmt.Sprintf("Meta_Health_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this Meta_Health_Result match the // provided Meta_Health_Result. // // This function performs a deep comparison. func (v *Meta_Health_Result) Equals(rhs *Meta_Health_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of Meta_Health_Result. func (v *Meta_Health_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *Meta_Health_Result) GetSuccess() (o *HealthStatus) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *Meta_Health_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "health" for this struct. func (v *Meta_Health_Result) MethodName() string { return "health" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *Meta_Health_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } ================================================ FILE: .gen/go/health/metaclient/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package metaclient import ( context "context" reflect "reflect" wire "go.uber.org/thriftrw/wire" yarpc "go.uber.org/yarpc" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" health "github.com/uber/cadence/.gen/go/health" ) // Interface is a client for the Meta service. type Interface interface { Health( ctx context.Context, opts ...yarpc.CallOption, ) (*health.HealthStatus, error) } // New builds a new client for the Meta service. // // client := metaclient.New(dispatcher.ClientConfig("meta")) func New(c transport.ClientConfig, opts ...thrift.ClientOption) Interface { return client{ c: thrift.New(thrift.Config{ Service: "Meta", ClientConfig: c, }, opts...), nwc: thrift.NewNoWire(thrift.Config{ Service: "Meta", ClientConfig: c, }, opts...), } } func init() { yarpc.RegisterClientBuilder( func(c transport.ClientConfig, f reflect.StructField) Interface { return New(c, thrift.ClientBuilderOptions(c, f)...) }, ) } type client struct { c thrift.Client nwc thrift.NoWireClient } func (c client) Health( ctx context.Context, opts ...yarpc.CallOption, ) (success *health.HealthStatus, err error) { var result health.Meta_Health_Result args := health.Meta_Health_Helper.Args() if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = health.Meta_Health_Helper.UnwrapResponse(&result) return } ================================================ FILE: .gen/go/health/metafx/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package metafx import ( fx "go.uber.org/fx" yarpc "go.uber.org/yarpc" transport "go.uber.org/yarpc/api/transport" restriction "go.uber.org/yarpc/api/x/restriction" thrift "go.uber.org/yarpc/encoding/thrift" metaclient "github.com/uber/cadence/.gen/go/health/metaclient" ) // Params defines the dependencies for the Meta client. type Params struct { fx.In Provider yarpc.ClientConfig Restriction restriction.Checker `optional:"true"` } // Result defines the output of the Meta client module. It provides a // Meta client to an Fx application. type Result struct { fx.Out Client metaclient.Interface // We are using an fx.Out struct here instead of just returning a client // so that we can add more values or add named versions of the client in // the future without breaking any existing code. } // Client provides a Meta client to an Fx application using the given name // for routing. // // fx.Provide( // metafx.Client("..."), // newHandler, // ) func Client(name string, opts ...thrift.ClientOption) interface{} { return func(p Params) Result { cc := p.Provider.ClientConfig(name) if namer, ok := cc.GetUnaryOutbound().(transport.Namer); ok && p.Restriction != nil { if err := p.Restriction.Check(thrift.Encoding, namer.TransportName()); err != nil { panic(err.Error()) } } client := metaclient.New(cc, opts...) return Result{Client: client} } } ================================================ FILE: .gen/go/health/metafx/doc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated // Package metafx provides better integration for Fx for services // implementing or calling Meta. // // # Clients // // If you are making requests to Meta, use the Client function to inject a // Meta client into your container. // // fx.Provide(metafx.Client("...")) // // # Servers // // If you are implementing Meta, provide a metaserver.Interface into // the container and use the Server function. // // Given, // // func NewMetaHandler() metaserver.Interface // // You can do the following to have the procedures of Meta made available // to an Fx application. // // fx.Provide( // NewMetaHandler, // metafx.Server(), // ) package metafx ================================================ FILE: .gen/go/health/metafx/server.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package metafx import ( fx "go.uber.org/fx" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" metaserver "github.com/uber/cadence/.gen/go/health/metaserver" ) // ServerParams defines the dependencies for the Meta server. type ServerParams struct { fx.In Handler metaserver.Interface } // ServerResult defines the output of Meta server module. It provides the // procedures of a Meta handler to an Fx application. // // The procedures are provided to the "yarpcfx" value group. Dig 1.2 or newer // must be used for this feature to work. type ServerResult struct { fx.Out Procedures []transport.Procedure `group:"yarpcfx"` } // Server provides procedures for Meta to an Fx application. It expects a // metafx.Interface to be present in the container. // // fx.Provide( // func(h *MyMetaHandler) metaserver.Interface { // return h // }, // metafx.Server(), // ) func Server(opts ...thrift.RegisterOption) interface{} { return func(p ServerParams) ServerResult { procedures := metaserver.New(p.Handler, opts...) return ServerResult{Procedures: procedures} } } ================================================ FILE: .gen/go/health/metaserver/server.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package metaserver import ( context "context" stream "go.uber.org/thriftrw/protocol/stream" wire "go.uber.org/thriftrw/wire" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" yarpcerrors "go.uber.org/yarpc/yarpcerrors" health "github.com/uber/cadence/.gen/go/health" ) // Interface is the server-side interface for the Meta service. type Interface interface { Health( ctx context.Context, ) (*health.HealthStatus, error) } // New prepares an implementation of the Meta service for // registration. // // handler := MetaHandler{} // dispatcher.Register(metaserver.New(handler)) func New(impl Interface, opts ...thrift.RegisterOption) []transport.Procedure { h := handler{impl} service := thrift.Service{ Name: "Meta", Methods: []thrift.Method{ thrift.Method{ Name: "health", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.Health), NoWire: health_NoWireHandler{impl}, }, Signature: "Health() (*health.HealthStatus)", ThriftModule: health.ThriftModule, }, }, } procedures := make([]transport.Procedure, 0, 1) procedures = append(procedures, thrift.BuildProcedures(service, opts...)...) return procedures } type handler struct{ impl Interface } type yarpcErrorNamer interface{ YARPCErrorName() string } type yarpcErrorCoder interface{ YARPCErrorCode() *yarpcerrors.Code } func (h handler) Health(ctx context.Context, body wire.Value) (thrift.Response, error) { var args health.Meta_Health_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'Meta' procedure 'Health': %w", err) } success, appErr := h.impl.Health(ctx) hadError := appErr != nil result, err := health.Meta_Health_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type health_NoWireHandler struct{ impl Interface } func (h health_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args health.Meta_Health_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'Meta' procedure 'Health': %w", err) } success, appErr := h.impl.Health(ctx) hadError := appErr != nil result, err := health.Meta_Health_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } ================================================ FILE: .gen/go/health/metatest/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package metatest import ( context "context" gomock "github.com/golang/mock/gomock" yarpc "go.uber.org/yarpc" health "github.com/uber/cadence/.gen/go/health" metaclient "github.com/uber/cadence/.gen/go/health/metaclient" ) // MockClient implements a gomock-compatible mock client for service // Meta. type MockClient struct { ctrl *gomock.Controller recorder *_MockClientRecorder } var _ metaclient.Interface = (*MockClient)(nil) type _MockClientRecorder struct { mock *MockClient } // Build a new mock client for service Meta. // // mockCtrl := gomock.NewController(t) // client := metatest.NewMockClient(mockCtrl) // // Use EXPECT() to set expectations on the mock. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &_MockClientRecorder{mock} return mock } // EXPECT returns an object that allows you to define an expectation on the // Meta mock client. func (m *MockClient) EXPECT() *_MockClientRecorder { return m.recorder } // Health responds to a Health call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().Health(gomock.Any(), ...).Return(...) // ... := client.Health(...) func (m *MockClient) Health( ctx context.Context, opts ...yarpc.CallOption, ) (success *health.HealthStatus, err error) { args := []interface{}{ctx} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "Health", args...) success, _ = ret[i].(*health.HealthStatus) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) Health( ctx interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "Health", args...) } ================================================ FILE: .gen/go/health/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package health ================================================ FILE: .gen/go/history/history.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package history import ( bytes "bytes" base64 "encoding/base64" errors "errors" fmt "fmt" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" replicator "github.com/uber/cadence/.gen/go/replicator" shared "github.com/uber/cadence/.gen/go/shared" ) // Any{ValueType} identifier for WeightedRatelimitQuotas data const WeightedRatelimitQuotasAnyType string = "cadence:loadbalanced:update_response" // Any{ValueType} identifier for WeightedRatelimitUsage data const WeightedRatelimitUsageAnyType string = "cadence:loadbalanced:update_request" const WeightedRatelimitUsageQuotasAnyType string = "cadence:loadbalanced:update_response_used" type DescribeMutableStateRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` } // ToWire translates a DescribeMutableStateRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeMutableStateRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WorkflowExecution_Read(w wire.Value) (*shared.WorkflowExecution, error) { var v shared.WorkflowExecution err := v.FromWire(w) return &v, err } // FromWire deserializes a DescribeMutableStateRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeMutableStateRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeMutableStateRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeMutableStateRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a DescribeMutableStateRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeMutableStateRequest struct could not be encoded. func (v *DescribeMutableStateRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _WorkflowExecution_Decode(sr stream.Reader) (*shared.WorkflowExecution, error) { var v shared.WorkflowExecution err := v.Decode(sr) return &v, err } // Decode deserializes a DescribeMutableStateRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeMutableStateRequest struct could not be generated from the wire // representation. func (v *DescribeMutableStateRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeMutableStateRequest // struct. func (v *DescribeMutableStateRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } return fmt.Sprintf("DescribeMutableStateRequest{%v}", strings.Join(fields[:i], ", ")) } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this DescribeMutableStateRequest match the // provided DescribeMutableStateRequest. // // This function performs a deep comparison. func (v *DescribeMutableStateRequest) Equals(rhs *DescribeMutableStateRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeMutableStateRequest. func (v *DescribeMutableStateRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *DescribeMutableStateRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *DescribeMutableStateRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *DescribeMutableStateRequest) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *DescribeMutableStateRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } type DescribeMutableStateResponse struct { MutableStateInCache *string `json:"mutableStateInCache,omitempty"` MutableStateInDatabase *string `json:"mutableStateInDatabase,omitempty"` } // ToWire translates a DescribeMutableStateResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeMutableStateResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.MutableStateInCache != nil { w, err = wire.NewValueString(*(v.MutableStateInCache)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.MutableStateInDatabase != nil { w, err = wire.NewValueString(*(v.MutableStateInDatabase)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DescribeMutableStateResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeMutableStateResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeMutableStateResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeMutableStateResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.MutableStateInCache = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.MutableStateInDatabase = &x if err != nil { return err } } } } return nil } // Encode serializes a DescribeMutableStateResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeMutableStateResponse struct could not be encoded. func (v *DescribeMutableStateResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.MutableStateInCache != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.MutableStateInCache)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MutableStateInDatabase != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.MutableStateInDatabase)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DescribeMutableStateResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeMutableStateResponse struct could not be generated from the wire // representation. func (v *DescribeMutableStateResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.MutableStateInCache = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.MutableStateInDatabase = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeMutableStateResponse // struct. func (v *DescribeMutableStateResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.MutableStateInCache != nil { fields[i] = fmt.Sprintf("MutableStateInCache: %v", *(v.MutableStateInCache)) i++ } if v.MutableStateInDatabase != nil { fields[i] = fmt.Sprintf("MutableStateInDatabase: %v", *(v.MutableStateInDatabase)) i++ } return fmt.Sprintf("DescribeMutableStateResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DescribeMutableStateResponse match the // provided DescribeMutableStateResponse. // // This function performs a deep comparison. func (v *DescribeMutableStateResponse) Equals(rhs *DescribeMutableStateResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.MutableStateInCache, rhs.MutableStateInCache) { return false } if !_String_EqualsPtr(v.MutableStateInDatabase, rhs.MutableStateInDatabase) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeMutableStateResponse. func (v *DescribeMutableStateResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.MutableStateInCache != nil { enc.AddString("mutableStateInCache", *v.MutableStateInCache) } if v.MutableStateInDatabase != nil { enc.AddString("mutableStateInDatabase", *v.MutableStateInDatabase) } return err } // GetMutableStateInCache returns the value of MutableStateInCache if it is set or its // zero value if it is unset. func (v *DescribeMutableStateResponse) GetMutableStateInCache() (o string) { if v != nil && v.MutableStateInCache != nil { return *v.MutableStateInCache } return } // IsSetMutableStateInCache returns true if MutableStateInCache is not nil. func (v *DescribeMutableStateResponse) IsSetMutableStateInCache() bool { return v != nil && v.MutableStateInCache != nil } // GetMutableStateInDatabase returns the value of MutableStateInDatabase if it is set or its // zero value if it is unset. func (v *DescribeMutableStateResponse) GetMutableStateInDatabase() (o string) { if v != nil && v.MutableStateInDatabase != nil { return *v.MutableStateInDatabase } return } // IsSetMutableStateInDatabase returns true if MutableStateInDatabase is not nil. func (v *DescribeMutableStateResponse) IsSetMutableStateInDatabase() bool { return v != nil && v.MutableStateInDatabase != nil } type DescribeWorkflowExecutionRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` Request *shared.DescribeWorkflowExecutionRequest `json:"request,omitempty"` } // ToWire translates a DescribeWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeWorkflowExecutionRequest_Read(w wire.Value) (*shared.DescribeWorkflowExecutionRequest, error) { var v shared.DescribeWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a DescribeWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Request, err = _DescribeWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a DescribeWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeWorkflowExecutionRequest struct could not be encoded. func (v *DescribeWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.DescribeWorkflowExecutionRequest, error) { var v shared.DescribeWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a DescribeWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *DescribeWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Request, err = _DescribeWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeWorkflowExecutionRequest // struct. func (v *DescribeWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("DescribeWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DescribeWorkflowExecutionRequest match the // provided DescribeWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *DescribeWorkflowExecutionRequest) Equals(rhs *DescribeWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeWorkflowExecutionRequest. func (v *DescribeWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *DescribeWorkflowExecutionRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionRequest) GetRequest() (o *shared.DescribeWorkflowExecutionRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *DescribeWorkflowExecutionRequest) IsSetRequest() bool { return v != nil && v.Request != nil } type DomainFilter struct { DomainIDs []string `json:"domainIDs,omitempty"` ReverseMatch *bool `json:"reverseMatch,omitempty"` } type _List_String_ValueList []string func (v _List_String_ValueList) ForEach(f func(wire.Value) error) error { for _, x := range v { w, err := wire.NewValueString(x), error(nil) if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_String_ValueList) Size() int { return len(v) } func (_List_String_ValueList) ValueType() wire.Type { return wire.TBinary } func (_List_String_ValueList) Close() {} // ToWire translates a DomainFilter struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DomainFilter) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainIDs != nil { w, err = wire.NewValueList(_List_String_ValueList(v.DomainIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ReverseMatch != nil { w, err = wire.NewValueBool(*(v.ReverseMatch)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _List_String_Read(l wire.ValueList) ([]string, error) { if l.ValueType() != wire.TBinary { return nil, nil } o := make([]string, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := x.GetString(), error(nil) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a DomainFilter struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DomainFilter struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DomainFilter // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DomainFilter) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.DomainIDs, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ReverseMatch = &x if err != nil { return err } } } } return nil } func _List_String_Encode(val []string, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TBinary, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for _, v := range val { if err := sw.WriteString(v); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a DomainFilter struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DomainFilter struct could not be encoded. func (v *DomainFilter) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.DomainIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReverseMatch != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ReverseMatch)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _List_String_Decode(sr stream.Reader) ([]string, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TBinary { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]string, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := sr.ReadString() if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a DomainFilter struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DomainFilter struct could not be generated from the wire // representation. func (v *DomainFilter) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.DomainIDs, err = _List_String_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ReverseMatch = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DomainFilter // struct. func (v *DomainFilter) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainIDs != nil { fields[i] = fmt.Sprintf("DomainIDs: %v", v.DomainIDs) i++ } if v.ReverseMatch != nil { fields[i] = fmt.Sprintf("ReverseMatch: %v", *(v.ReverseMatch)) i++ } return fmt.Sprintf("DomainFilter{%v}", strings.Join(fields[:i], ", ")) } func _List_String_Equals(lhs, rhs []string) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !(lv == rv) { return false } } return true } func _Bool_EqualsPtr(lhs, rhs *bool) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this DomainFilter match the // provided DomainFilter. // // This function performs a deep comparison. func (v *DomainFilter) Equals(rhs *DomainFilter) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DomainIDs == nil && rhs.DomainIDs == nil) || (v.DomainIDs != nil && rhs.DomainIDs != nil && _List_String_Equals(v.DomainIDs, rhs.DomainIDs))) { return false } if !_Bool_EqualsPtr(v.ReverseMatch, rhs.ReverseMatch) { return false } return true } type _List_String_Zapper []string // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_String_Zapper. func (l _List_String_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { enc.AppendString(v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainFilter. func (v *DomainFilter) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainIDs != nil { err = multierr.Append(err, enc.AddArray("domainIDs", (_List_String_Zapper)(v.DomainIDs))) } if v.ReverseMatch != nil { enc.AddBool("reverseMatch", *v.ReverseMatch) } return err } // GetDomainIDs returns the value of DomainIDs if it is set or its // zero value if it is unset. func (v *DomainFilter) GetDomainIDs() (o []string) { if v != nil && v.DomainIDs != nil { return v.DomainIDs } return } // IsSetDomainIDs returns true if DomainIDs is not nil. func (v *DomainFilter) IsSetDomainIDs() bool { return v != nil && v.DomainIDs != nil } // GetReverseMatch returns the value of ReverseMatch if it is set or its // zero value if it is unset. func (v *DomainFilter) GetReverseMatch() (o bool) { if v != nil && v.ReverseMatch != nil { return *v.ReverseMatch } return } // IsSetReverseMatch returns true if ReverseMatch is not nil. func (v *DomainFilter) IsSetReverseMatch() bool { return v != nil && v.ReverseMatch != nil } type EventAlreadyStartedError struct { Message string `json:"message,required"` } // ToWire translates a EventAlreadyStartedError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *EventAlreadyStartedError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a EventAlreadyStartedError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a EventAlreadyStartedError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v EventAlreadyStartedError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *EventAlreadyStartedError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of EventAlreadyStartedError is required") } return nil } // Encode serializes a EventAlreadyStartedError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a EventAlreadyStartedError struct could not be encoded. func (v *EventAlreadyStartedError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a EventAlreadyStartedError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a EventAlreadyStartedError struct could not be generated from the wire // representation. func (v *EventAlreadyStartedError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of EventAlreadyStartedError is required") } return nil } // String returns a readable string representation of a EventAlreadyStartedError // struct. func (v *EventAlreadyStartedError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("EventAlreadyStartedError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*EventAlreadyStartedError) ErrorName() string { return "EventAlreadyStartedError" } // Equals returns true if all the fields of this EventAlreadyStartedError match the // provided EventAlreadyStartedError. // // This function performs a deep comparison. func (v *EventAlreadyStartedError) Equals(rhs *EventAlreadyStartedError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of EventAlreadyStartedError. func (v *EventAlreadyStartedError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *EventAlreadyStartedError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *EventAlreadyStartedError) Error() string { return v.String() } type FailoverMarkerToken struct { ShardIDs []int32 `json:"shardIDs,omitempty"` FailoverMarker *replicator.FailoverMarkerAttributes `json:"failoverMarker,omitempty"` } type _List_I32_ValueList []int32 func (v _List_I32_ValueList) ForEach(f func(wire.Value) error) error { for _, x := range v { w, err := wire.NewValueI32(x), error(nil) if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_I32_ValueList) Size() int { return len(v) } func (_List_I32_ValueList) ValueType() wire.Type { return wire.TI32 } func (_List_I32_ValueList) Close() {} // ToWire translates a FailoverMarkerToken struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *FailoverMarkerToken) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ShardIDs != nil { w, err = wire.NewValueList(_List_I32_ValueList(v.ShardIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.FailoverMarker != nil { w, err = v.FailoverMarker.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _List_I32_Read(l wire.ValueList) ([]int32, error) { if l.ValueType() != wire.TI32 { return nil, nil } o := make([]int32, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := x.GetI32(), error(nil) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _FailoverMarkerAttributes_Read(w wire.Value) (*replicator.FailoverMarkerAttributes, error) { var v replicator.FailoverMarkerAttributes err := v.FromWire(w) return &v, err } // FromWire deserializes a FailoverMarkerToken struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a FailoverMarkerToken struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v FailoverMarkerToken // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *FailoverMarkerToken) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.ShardIDs, err = _List_I32_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.FailoverMarker, err = _FailoverMarkerAttributes_Read(field.Value) if err != nil { return err } } } } return nil } func _List_I32_Encode(val []int32, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TI32, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for _, v := range val { if err := sw.WriteInt32(v); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a FailoverMarkerToken struct directly into bytes, without going // through an intermediary type. // // An error is returned if a FailoverMarkerToken struct could not be encoded. func (v *FailoverMarkerToken) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ShardIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_I32_Encode(v.ShardIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverMarker != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.FailoverMarker.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _List_I32_Decode(sr stream.Reader) ([]int32, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TI32 { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]int32, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := sr.ReadInt32() if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _FailoverMarkerAttributes_Decode(sr stream.Reader) (*replicator.FailoverMarkerAttributes, error) { var v replicator.FailoverMarkerAttributes err := v.Decode(sr) return &v, err } // Decode deserializes a FailoverMarkerToken struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a FailoverMarkerToken struct could not be generated from the wire // representation. func (v *FailoverMarkerToken) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.ShardIDs, err = _List_I32_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.FailoverMarker, err = _FailoverMarkerAttributes_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a FailoverMarkerToken // struct. func (v *FailoverMarkerToken) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ShardIDs != nil { fields[i] = fmt.Sprintf("ShardIDs: %v", v.ShardIDs) i++ } if v.FailoverMarker != nil { fields[i] = fmt.Sprintf("FailoverMarker: %v", v.FailoverMarker) i++ } return fmt.Sprintf("FailoverMarkerToken{%v}", strings.Join(fields[:i], ", ")) } func _List_I32_Equals(lhs, rhs []int32) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !(lv == rv) { return false } } return true } // Equals returns true if all the fields of this FailoverMarkerToken match the // provided FailoverMarkerToken. // // This function performs a deep comparison. func (v *FailoverMarkerToken) Equals(rhs *FailoverMarkerToken) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ShardIDs == nil && rhs.ShardIDs == nil) || (v.ShardIDs != nil && rhs.ShardIDs != nil && _List_I32_Equals(v.ShardIDs, rhs.ShardIDs))) { return false } if !((v.FailoverMarker == nil && rhs.FailoverMarker == nil) || (v.FailoverMarker != nil && rhs.FailoverMarker != nil && v.FailoverMarker.Equals(rhs.FailoverMarker))) { return false } return true } type _List_I32_Zapper []int32 // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_I32_Zapper. func (l _List_I32_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { enc.AppendInt32(v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FailoverMarkerToken. func (v *FailoverMarkerToken) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ShardIDs != nil { err = multierr.Append(err, enc.AddArray("shardIDs", (_List_I32_Zapper)(v.ShardIDs))) } if v.FailoverMarker != nil { err = multierr.Append(err, enc.AddObject("failoverMarker", v.FailoverMarker)) } return err } // GetShardIDs returns the value of ShardIDs if it is set or its // zero value if it is unset. func (v *FailoverMarkerToken) GetShardIDs() (o []int32) { if v != nil && v.ShardIDs != nil { return v.ShardIDs } return } // IsSetShardIDs returns true if ShardIDs is not nil. func (v *FailoverMarkerToken) IsSetShardIDs() bool { return v != nil && v.ShardIDs != nil } // GetFailoverMarker returns the value of FailoverMarker if it is set or its // zero value if it is unset. func (v *FailoverMarkerToken) GetFailoverMarker() (o *replicator.FailoverMarkerAttributes) { if v != nil && v.FailoverMarker != nil { return v.FailoverMarker } return } // IsSetFailoverMarker returns true if FailoverMarker is not nil. func (v *FailoverMarkerToken) IsSetFailoverMarker() bool { return v != nil && v.FailoverMarker != nil } type GetFailoverInfoRequest struct { DomainID *string `json:"domainID,omitempty"` } // ToWire translates a GetFailoverInfoRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetFailoverInfoRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.DomainID != nil { w, err = wire.NewValueString(*(v.DomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a GetFailoverInfoRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetFailoverInfoRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetFailoverInfoRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetFailoverInfoRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainID = &x if err != nil { return err } } } } return nil } // Encode serializes a GetFailoverInfoRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetFailoverInfoRequest struct could not be encoded. func (v *GetFailoverInfoRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a GetFailoverInfoRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetFailoverInfoRequest struct could not be generated from the wire // representation. func (v *GetFailoverInfoRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetFailoverInfoRequest // struct. func (v *GetFailoverInfoRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", *(v.DomainID)) i++ } return fmt.Sprintf("GetFailoverInfoRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetFailoverInfoRequest match the // provided GetFailoverInfoRequest. // // This function performs a deep comparison. func (v *GetFailoverInfoRequest) Equals(rhs *GetFailoverInfoRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainID, rhs.DomainID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetFailoverInfoRequest. func (v *GetFailoverInfoRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainID != nil { enc.AddString("domainID", *v.DomainID) } return err } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *GetFailoverInfoRequest) GetDomainID() (o string) { if v != nil && v.DomainID != nil { return *v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *GetFailoverInfoRequest) IsSetDomainID() bool { return v != nil && v.DomainID != nil } type GetFailoverInfoResponse struct { CompletedShardCount *int32 `json:"completedShardCount,omitempty"` PendingShards []int32 `json:"pendingShards,omitempty"` } // ToWire translates a GetFailoverInfoResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetFailoverInfoResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.CompletedShardCount != nil { w, err = wire.NewValueI32(*(v.CompletedShardCount)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.PendingShards != nil { w, err = wire.NewValueList(_List_I32_ValueList(v.PendingShards)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a GetFailoverInfoResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetFailoverInfoResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetFailoverInfoResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetFailoverInfoResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.CompletedShardCount = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.PendingShards, err = _List_I32_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } // Encode serializes a GetFailoverInfoResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetFailoverInfoResponse struct could not be encoded. func (v *GetFailoverInfoResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CompletedShardCount != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.CompletedShardCount)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingShards != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_I32_Encode(v.PendingShards, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a GetFailoverInfoResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetFailoverInfoResponse struct could not be generated from the wire // representation. func (v *GetFailoverInfoResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.CompletedShardCount = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.PendingShards, err = _List_I32_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetFailoverInfoResponse // struct. func (v *GetFailoverInfoResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.CompletedShardCount != nil { fields[i] = fmt.Sprintf("CompletedShardCount: %v", *(v.CompletedShardCount)) i++ } if v.PendingShards != nil { fields[i] = fmt.Sprintf("PendingShards: %v", v.PendingShards) i++ } return fmt.Sprintf("GetFailoverInfoResponse{%v}", strings.Join(fields[:i], ", ")) } func _I32_EqualsPtr(lhs, rhs *int32) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this GetFailoverInfoResponse match the // provided GetFailoverInfoResponse. // // This function performs a deep comparison. func (v *GetFailoverInfoResponse) Equals(rhs *GetFailoverInfoResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.CompletedShardCount, rhs.CompletedShardCount) { return false } if !((v.PendingShards == nil && rhs.PendingShards == nil) || (v.PendingShards != nil && rhs.PendingShards != nil && _List_I32_Equals(v.PendingShards, rhs.PendingShards))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetFailoverInfoResponse. func (v *GetFailoverInfoResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CompletedShardCount != nil { enc.AddInt32("completedShardCount", *v.CompletedShardCount) } if v.PendingShards != nil { err = multierr.Append(err, enc.AddArray("pendingShards", (_List_I32_Zapper)(v.PendingShards))) } return err } // GetCompletedShardCount returns the value of CompletedShardCount if it is set or its // zero value if it is unset. func (v *GetFailoverInfoResponse) GetCompletedShardCount() (o int32) { if v != nil && v.CompletedShardCount != nil { return *v.CompletedShardCount } return } // IsSetCompletedShardCount returns true if CompletedShardCount is not nil. func (v *GetFailoverInfoResponse) IsSetCompletedShardCount() bool { return v != nil && v.CompletedShardCount != nil } // GetPendingShards returns the value of PendingShards if it is set or its // zero value if it is unset. func (v *GetFailoverInfoResponse) GetPendingShards() (o []int32) { if v != nil && v.PendingShards != nil { return v.PendingShards } return } // IsSetPendingShards returns true if PendingShards is not nil. func (v *GetFailoverInfoResponse) IsSetPendingShards() bool { return v != nil && v.PendingShards != nil } type GetMutableStateRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` ExpectedNextEventId *int64 `json:"expectedNextEventId,omitempty"` CurrentBranchToken []byte `json:"currentBranchToken,omitempty"` VersionHistoryItem *shared.VersionHistoryItem `json:"versionHistoryItem,omitempty"` } // ToWire translates a GetMutableStateRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetMutableStateRequest) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ExpectedNextEventId != nil { w, err = wire.NewValueI64(*(v.ExpectedNextEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.CurrentBranchToken != nil { w, err = wire.NewValueBinary(v.CurrentBranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.VersionHistoryItem != nil { w, err = v.VersionHistoryItem.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _VersionHistoryItem_Read(w wire.Value) (*shared.VersionHistoryItem, error) { var v shared.VersionHistoryItem err := v.FromWire(w) return &v, err } // FromWire deserializes a GetMutableStateRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetMutableStateRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetMutableStateRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetMutableStateRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExpectedNextEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.CurrentBranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.VersionHistoryItem, err = _VersionHistoryItem_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a GetMutableStateRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetMutableStateRequest struct could not be encoded. func (v *GetMutableStateRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExpectedNextEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExpectedNextEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CurrentBranchToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.CurrentBranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistoryItem != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.VersionHistoryItem.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _VersionHistoryItem_Decode(sr stream.Reader) (*shared.VersionHistoryItem, error) { var v shared.VersionHistoryItem err := v.Decode(sr) return &v, err } // Decode deserializes a GetMutableStateRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetMutableStateRequest struct could not be generated from the wire // representation. func (v *GetMutableStateRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExpectedNextEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.CurrentBranchToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.VersionHistoryItem, err = _VersionHistoryItem_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetMutableStateRequest // struct. func (v *GetMutableStateRequest) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.ExpectedNextEventId != nil { fields[i] = fmt.Sprintf("ExpectedNextEventId: %v", *(v.ExpectedNextEventId)) i++ } if v.CurrentBranchToken != nil { fields[i] = fmt.Sprintf("CurrentBranchToken: %v", v.CurrentBranchToken) i++ } if v.VersionHistoryItem != nil { fields[i] = fmt.Sprintf("VersionHistoryItem: %v", v.VersionHistoryItem) i++ } return fmt.Sprintf("GetMutableStateRequest{%v}", strings.Join(fields[:i], ", ")) } func _I64_EqualsPtr(lhs, rhs *int64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this GetMutableStateRequest match the // provided GetMutableStateRequest. // // This function performs a deep comparison. func (v *GetMutableStateRequest) Equals(rhs *GetMutableStateRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !_I64_EqualsPtr(v.ExpectedNextEventId, rhs.ExpectedNextEventId) { return false } if !((v.CurrentBranchToken == nil && rhs.CurrentBranchToken == nil) || (v.CurrentBranchToken != nil && rhs.CurrentBranchToken != nil && bytes.Equal(v.CurrentBranchToken, rhs.CurrentBranchToken))) { return false } if !((v.VersionHistoryItem == nil && rhs.VersionHistoryItem == nil) || (v.VersionHistoryItem != nil && rhs.VersionHistoryItem != nil && v.VersionHistoryItem.Equals(rhs.VersionHistoryItem))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetMutableStateRequest. func (v *GetMutableStateRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.ExpectedNextEventId != nil { enc.AddInt64("expectedNextEventId", *v.ExpectedNextEventId) } if v.CurrentBranchToken != nil { enc.AddString("currentBranchToken", base64.StdEncoding.EncodeToString(v.CurrentBranchToken)) } if v.VersionHistoryItem != nil { err = multierr.Append(err, enc.AddObject("versionHistoryItem", v.VersionHistoryItem)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *GetMutableStateRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *GetMutableStateRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *GetMutableStateRequest) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *GetMutableStateRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetExpectedNextEventId returns the value of ExpectedNextEventId if it is set or its // zero value if it is unset. func (v *GetMutableStateRequest) GetExpectedNextEventId() (o int64) { if v != nil && v.ExpectedNextEventId != nil { return *v.ExpectedNextEventId } return } // IsSetExpectedNextEventId returns true if ExpectedNextEventId is not nil. func (v *GetMutableStateRequest) IsSetExpectedNextEventId() bool { return v != nil && v.ExpectedNextEventId != nil } // GetCurrentBranchToken returns the value of CurrentBranchToken if it is set or its // zero value if it is unset. func (v *GetMutableStateRequest) GetCurrentBranchToken() (o []byte) { if v != nil && v.CurrentBranchToken != nil { return v.CurrentBranchToken } return } // IsSetCurrentBranchToken returns true if CurrentBranchToken is not nil. func (v *GetMutableStateRequest) IsSetCurrentBranchToken() bool { return v != nil && v.CurrentBranchToken != nil } // GetVersionHistoryItem returns the value of VersionHistoryItem if it is set or its // zero value if it is unset. func (v *GetMutableStateRequest) GetVersionHistoryItem() (o *shared.VersionHistoryItem) { if v != nil && v.VersionHistoryItem != nil { return v.VersionHistoryItem } return } // IsSetVersionHistoryItem returns true if VersionHistoryItem is not nil. func (v *GetMutableStateRequest) IsSetVersionHistoryItem() bool { return v != nil && v.VersionHistoryItem != nil } type GetMutableStateResponse struct { Execution *shared.WorkflowExecution `json:"execution,omitempty"` WorkflowType *shared.WorkflowType `json:"workflowType,omitempty"` NextEventId *int64 `json:"NextEventId,omitempty"` PreviousStartedEventId *int64 `json:"PreviousStartedEventId,omitempty"` LastFirstEventId *int64 `json:"LastFirstEventId,omitempty"` TaskList *shared.TaskList `json:"taskList,omitempty"` StickyTaskList *shared.TaskList `json:"stickyTaskList,omitempty"` ClientLibraryVersion *string `json:"clientLibraryVersion,omitempty"` ClientFeatureVersion *string `json:"clientFeatureVersion,omitempty"` ClientImpl *string `json:"clientImpl,omitempty"` IsWorkflowRunning *bool `json:"isWorkflowRunning,omitempty"` StickyTaskListScheduleToStartTimeout *int32 `json:"stickyTaskListScheduleToStartTimeout,omitempty"` EventStoreVersion *int32 `json:"eventStoreVersion,omitempty"` CurrentBranchToken []byte `json:"currentBranchToken,omitempty"` WorkflowState *int32 `json:"workflowState,omitempty"` WorkflowCloseState *int32 `json:"workflowCloseState,omitempty"` VersionHistories *shared.VersionHistories `json:"versionHistories,omitempty"` IsStickyTaskListEnabled *bool `json:"isStickyTaskListEnabled,omitempty"` HistorySize *int64 `json:"historySize,omitempty"` } // ToWire translates a GetMutableStateResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetMutableStateResponse) ToWire() (wire.Value, error) { var ( fields [19]wire.Field i int = 0 w wire.Value err error ) if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.NextEventId != nil { w, err = wire.NewValueI64(*(v.NextEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.PreviousStartedEventId != nil { w, err = wire.NewValueI64(*(v.PreviousStartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 35, Value: w} i++ } if v.LastFirstEventId != nil { w, err = wire.NewValueI64(*(v.LastFirstEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.StickyTaskList != nil { w, err = v.StickyTaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.ClientLibraryVersion != nil { w, err = wire.NewValueString(*(v.ClientLibraryVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.ClientFeatureVersion != nil { w, err = wire.NewValueString(*(v.ClientFeatureVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.ClientImpl != nil { w, err = wire.NewValueString(*(v.ClientImpl)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.IsWorkflowRunning != nil { w, err = wire.NewValueBool(*(v.IsWorkflowRunning)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.StickyTaskListScheduleToStartTimeout != nil { w, err = wire.NewValueI32(*(v.StickyTaskListScheduleToStartTimeout)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.EventStoreVersion != nil { w, err = wire.NewValueI32(*(v.EventStoreVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.CurrentBranchToken != nil { w, err = wire.NewValueBinary(v.CurrentBranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.WorkflowState != nil { w, err = wire.NewValueI32(*(v.WorkflowState)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.WorkflowCloseState != nil { w, err = wire.NewValueI32(*(v.WorkflowCloseState)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.VersionHistories != nil { w, err = v.VersionHistories.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } if v.IsStickyTaskListEnabled != nil { w, err = wire.NewValueBool(*(v.IsStickyTaskListEnabled)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 180, Value: w} i++ } if v.HistorySize != nil { w, err = wire.NewValueI64(*(v.HistorySize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 190, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WorkflowType_Read(w wire.Value) (*shared.WorkflowType, error) { var v shared.WorkflowType err := v.FromWire(w) return &v, err } func _TaskList_Read(w wire.Value) (*shared.TaskList, error) { var v shared.TaskList err := v.FromWire(w) return &v, err } func _VersionHistories_Read(w wire.Value) (*shared.VersionHistories, error) { var v shared.VersionHistories err := v.FromWire(w) return &v, err } // FromWire deserializes a GetMutableStateResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetMutableStateResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetMutableStateResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetMutableStateResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NextEventId = &x if err != nil { return err } } case 35: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.PreviousStartedEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastFirstEventId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.StickyTaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClientLibraryVersion = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClientFeatureVersion = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClientImpl = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.IsWorkflowRunning = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.StickyTaskListScheduleToStartTimeout = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.EventStoreVersion = &x if err != nil { return err } } case 130: if field.Value.Type() == wire.TBinary { v.CurrentBranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 150: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.WorkflowState = &x if err != nil { return err } } case 160: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.WorkflowCloseState = &x if err != nil { return err } } case 170: if field.Value.Type() == wire.TStruct { v.VersionHistories, err = _VersionHistories_Read(field.Value) if err != nil { return err } } case 180: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.IsStickyTaskListEnabled = &x if err != nil { return err } } case 190: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.HistorySize = &x if err != nil { return err } } } } return nil } // Encode serializes a GetMutableStateResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetMutableStateResponse struct could not be encoded. func (v *GetMutableStateResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NextEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PreviousStartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 35, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.PreviousStartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFirstEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastFirstEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyTaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.StickyTaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientLibraryVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClientLibraryVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientFeatureVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClientFeatureVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientImpl != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClientImpl)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsWorkflowRunning != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.IsWorkflowRunning)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyTaskListScheduleToStartTimeout != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.StickyTaskListScheduleToStartTimeout)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EventStoreVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.EventStoreVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CurrentBranchToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.CurrentBranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowState != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.WorkflowState)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowCloseState != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.WorkflowCloseState)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistories != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TStruct}); err != nil { return err } if err := v.VersionHistories.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsStickyTaskListEnabled != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 180, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.IsStickyTaskListEnabled)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistorySize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 190, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.HistorySize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _WorkflowType_Decode(sr stream.Reader) (*shared.WorkflowType, error) { var v shared.WorkflowType err := v.Decode(sr) return &v, err } func _TaskList_Decode(sr stream.Reader) (*shared.TaskList, error) { var v shared.TaskList err := v.Decode(sr) return &v, err } func _VersionHistories_Decode(sr stream.Reader) (*shared.VersionHistories, error) { var v shared.VersionHistories err := v.Decode(sr) return &v, err } // Decode deserializes a GetMutableStateResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetMutableStateResponse struct could not be generated from the wire // representation. func (v *GetMutableStateResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NextEventId = &x if err != nil { return err } case fh.ID == 35 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.PreviousStartedEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastFirstEventId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.StickyTaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClientLibraryVersion = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClientFeatureVersion = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClientImpl = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.IsWorkflowRunning = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.StickyTaskListScheduleToStartTimeout = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.EventStoreVersion = &x if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TBinary: v.CurrentBranchToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.WorkflowState = &x if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.WorkflowCloseState = &x if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TStruct: v.VersionHistories, err = _VersionHistories_Decode(sr) if err != nil { return err } case fh.ID == 180 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.IsStickyTaskListEnabled = &x if err != nil { return err } case fh.ID == 190 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.HistorySize = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetMutableStateResponse // struct. func (v *GetMutableStateResponse) String() string { if v == nil { return "" } var fields [19]string i := 0 if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.NextEventId != nil { fields[i] = fmt.Sprintf("NextEventId: %v", *(v.NextEventId)) i++ } if v.PreviousStartedEventId != nil { fields[i] = fmt.Sprintf("PreviousStartedEventId: %v", *(v.PreviousStartedEventId)) i++ } if v.LastFirstEventId != nil { fields[i] = fmt.Sprintf("LastFirstEventId: %v", *(v.LastFirstEventId)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.StickyTaskList != nil { fields[i] = fmt.Sprintf("StickyTaskList: %v", v.StickyTaskList) i++ } if v.ClientLibraryVersion != nil { fields[i] = fmt.Sprintf("ClientLibraryVersion: %v", *(v.ClientLibraryVersion)) i++ } if v.ClientFeatureVersion != nil { fields[i] = fmt.Sprintf("ClientFeatureVersion: %v", *(v.ClientFeatureVersion)) i++ } if v.ClientImpl != nil { fields[i] = fmt.Sprintf("ClientImpl: %v", *(v.ClientImpl)) i++ } if v.IsWorkflowRunning != nil { fields[i] = fmt.Sprintf("IsWorkflowRunning: %v", *(v.IsWorkflowRunning)) i++ } if v.StickyTaskListScheduleToStartTimeout != nil { fields[i] = fmt.Sprintf("StickyTaskListScheduleToStartTimeout: %v", *(v.StickyTaskListScheduleToStartTimeout)) i++ } if v.EventStoreVersion != nil { fields[i] = fmt.Sprintf("EventStoreVersion: %v", *(v.EventStoreVersion)) i++ } if v.CurrentBranchToken != nil { fields[i] = fmt.Sprintf("CurrentBranchToken: %v", v.CurrentBranchToken) i++ } if v.WorkflowState != nil { fields[i] = fmt.Sprintf("WorkflowState: %v", *(v.WorkflowState)) i++ } if v.WorkflowCloseState != nil { fields[i] = fmt.Sprintf("WorkflowCloseState: %v", *(v.WorkflowCloseState)) i++ } if v.VersionHistories != nil { fields[i] = fmt.Sprintf("VersionHistories: %v", v.VersionHistories) i++ } if v.IsStickyTaskListEnabled != nil { fields[i] = fmt.Sprintf("IsStickyTaskListEnabled: %v", *(v.IsStickyTaskListEnabled)) i++ } if v.HistorySize != nil { fields[i] = fmt.Sprintf("HistorySize: %v", *(v.HistorySize)) i++ } return fmt.Sprintf("GetMutableStateResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetMutableStateResponse match the // provided GetMutableStateResponse. // // This function performs a deep comparison. func (v *GetMutableStateResponse) Equals(rhs *GetMutableStateResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_I64_EqualsPtr(v.NextEventId, rhs.NextEventId) { return false } if !_I64_EqualsPtr(v.PreviousStartedEventId, rhs.PreviousStartedEventId) { return false } if !_I64_EqualsPtr(v.LastFirstEventId, rhs.LastFirstEventId) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.StickyTaskList == nil && rhs.StickyTaskList == nil) || (v.StickyTaskList != nil && rhs.StickyTaskList != nil && v.StickyTaskList.Equals(rhs.StickyTaskList))) { return false } if !_String_EqualsPtr(v.ClientLibraryVersion, rhs.ClientLibraryVersion) { return false } if !_String_EqualsPtr(v.ClientFeatureVersion, rhs.ClientFeatureVersion) { return false } if !_String_EqualsPtr(v.ClientImpl, rhs.ClientImpl) { return false } if !_Bool_EqualsPtr(v.IsWorkflowRunning, rhs.IsWorkflowRunning) { return false } if !_I32_EqualsPtr(v.StickyTaskListScheduleToStartTimeout, rhs.StickyTaskListScheduleToStartTimeout) { return false } if !_I32_EqualsPtr(v.EventStoreVersion, rhs.EventStoreVersion) { return false } if !((v.CurrentBranchToken == nil && rhs.CurrentBranchToken == nil) || (v.CurrentBranchToken != nil && rhs.CurrentBranchToken != nil && bytes.Equal(v.CurrentBranchToken, rhs.CurrentBranchToken))) { return false } if !_I32_EqualsPtr(v.WorkflowState, rhs.WorkflowState) { return false } if !_I32_EqualsPtr(v.WorkflowCloseState, rhs.WorkflowCloseState) { return false } if !((v.VersionHistories == nil && rhs.VersionHistories == nil) || (v.VersionHistories != nil && rhs.VersionHistories != nil && v.VersionHistories.Equals(rhs.VersionHistories))) { return false } if !_Bool_EqualsPtr(v.IsStickyTaskListEnabled, rhs.IsStickyTaskListEnabled) { return false } if !_I64_EqualsPtr(v.HistorySize, rhs.HistorySize) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetMutableStateResponse. func (v *GetMutableStateResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.NextEventId != nil { enc.AddInt64("NextEventId", *v.NextEventId) } if v.PreviousStartedEventId != nil { enc.AddInt64("PreviousStartedEventId", *v.PreviousStartedEventId) } if v.LastFirstEventId != nil { enc.AddInt64("LastFirstEventId", *v.LastFirstEventId) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.StickyTaskList != nil { err = multierr.Append(err, enc.AddObject("stickyTaskList", v.StickyTaskList)) } if v.ClientLibraryVersion != nil { enc.AddString("clientLibraryVersion", *v.ClientLibraryVersion) } if v.ClientFeatureVersion != nil { enc.AddString("clientFeatureVersion", *v.ClientFeatureVersion) } if v.ClientImpl != nil { enc.AddString("clientImpl", *v.ClientImpl) } if v.IsWorkflowRunning != nil { enc.AddBool("isWorkflowRunning", *v.IsWorkflowRunning) } if v.StickyTaskListScheduleToStartTimeout != nil { enc.AddInt32("stickyTaskListScheduleToStartTimeout", *v.StickyTaskListScheduleToStartTimeout) } if v.EventStoreVersion != nil { enc.AddInt32("eventStoreVersion", *v.EventStoreVersion) } if v.CurrentBranchToken != nil { enc.AddString("currentBranchToken", base64.StdEncoding.EncodeToString(v.CurrentBranchToken)) } if v.WorkflowState != nil { enc.AddInt32("workflowState", *v.WorkflowState) } if v.WorkflowCloseState != nil { enc.AddInt32("workflowCloseState", *v.WorkflowCloseState) } if v.VersionHistories != nil { err = multierr.Append(err, enc.AddObject("versionHistories", v.VersionHistories)) } if v.IsStickyTaskListEnabled != nil { enc.AddBool("isStickyTaskListEnabled", *v.IsStickyTaskListEnabled) } if v.HistorySize != nil { enc.AddInt64("historySize", *v.HistorySize) } return err } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *GetMutableStateResponse) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetWorkflowType() (o *shared.WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *GetMutableStateResponse) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetNextEventId returns the value of NextEventId if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetNextEventId() (o int64) { if v != nil && v.NextEventId != nil { return *v.NextEventId } return } // IsSetNextEventId returns true if NextEventId is not nil. func (v *GetMutableStateResponse) IsSetNextEventId() bool { return v != nil && v.NextEventId != nil } // GetPreviousStartedEventId returns the value of PreviousStartedEventId if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetPreviousStartedEventId() (o int64) { if v != nil && v.PreviousStartedEventId != nil { return *v.PreviousStartedEventId } return } // IsSetPreviousStartedEventId returns true if PreviousStartedEventId is not nil. func (v *GetMutableStateResponse) IsSetPreviousStartedEventId() bool { return v != nil && v.PreviousStartedEventId != nil } // GetLastFirstEventId returns the value of LastFirstEventId if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetLastFirstEventId() (o int64) { if v != nil && v.LastFirstEventId != nil { return *v.LastFirstEventId } return } // IsSetLastFirstEventId returns true if LastFirstEventId is not nil. func (v *GetMutableStateResponse) IsSetLastFirstEventId() bool { return v != nil && v.LastFirstEventId != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetTaskList() (o *shared.TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *GetMutableStateResponse) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetStickyTaskList returns the value of StickyTaskList if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetStickyTaskList() (o *shared.TaskList) { if v != nil && v.StickyTaskList != nil { return v.StickyTaskList } return } // IsSetStickyTaskList returns true if StickyTaskList is not nil. func (v *GetMutableStateResponse) IsSetStickyTaskList() bool { return v != nil && v.StickyTaskList != nil } // GetClientLibraryVersion returns the value of ClientLibraryVersion if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetClientLibraryVersion() (o string) { if v != nil && v.ClientLibraryVersion != nil { return *v.ClientLibraryVersion } return } // IsSetClientLibraryVersion returns true if ClientLibraryVersion is not nil. func (v *GetMutableStateResponse) IsSetClientLibraryVersion() bool { return v != nil && v.ClientLibraryVersion != nil } // GetClientFeatureVersion returns the value of ClientFeatureVersion if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetClientFeatureVersion() (o string) { if v != nil && v.ClientFeatureVersion != nil { return *v.ClientFeatureVersion } return } // IsSetClientFeatureVersion returns true if ClientFeatureVersion is not nil. func (v *GetMutableStateResponse) IsSetClientFeatureVersion() bool { return v != nil && v.ClientFeatureVersion != nil } // GetClientImpl returns the value of ClientImpl if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetClientImpl() (o string) { if v != nil && v.ClientImpl != nil { return *v.ClientImpl } return } // IsSetClientImpl returns true if ClientImpl is not nil. func (v *GetMutableStateResponse) IsSetClientImpl() bool { return v != nil && v.ClientImpl != nil } // GetIsWorkflowRunning returns the value of IsWorkflowRunning if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetIsWorkflowRunning() (o bool) { if v != nil && v.IsWorkflowRunning != nil { return *v.IsWorkflowRunning } return } // IsSetIsWorkflowRunning returns true if IsWorkflowRunning is not nil. func (v *GetMutableStateResponse) IsSetIsWorkflowRunning() bool { return v != nil && v.IsWorkflowRunning != nil } // GetStickyTaskListScheduleToStartTimeout returns the value of StickyTaskListScheduleToStartTimeout if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetStickyTaskListScheduleToStartTimeout() (o int32) { if v != nil && v.StickyTaskListScheduleToStartTimeout != nil { return *v.StickyTaskListScheduleToStartTimeout } return } // IsSetStickyTaskListScheduleToStartTimeout returns true if StickyTaskListScheduleToStartTimeout is not nil. func (v *GetMutableStateResponse) IsSetStickyTaskListScheduleToStartTimeout() bool { return v != nil && v.StickyTaskListScheduleToStartTimeout != nil } // GetEventStoreVersion returns the value of EventStoreVersion if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetEventStoreVersion() (o int32) { if v != nil && v.EventStoreVersion != nil { return *v.EventStoreVersion } return } // IsSetEventStoreVersion returns true if EventStoreVersion is not nil. func (v *GetMutableStateResponse) IsSetEventStoreVersion() bool { return v != nil && v.EventStoreVersion != nil } // GetCurrentBranchToken returns the value of CurrentBranchToken if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetCurrentBranchToken() (o []byte) { if v != nil && v.CurrentBranchToken != nil { return v.CurrentBranchToken } return } // IsSetCurrentBranchToken returns true if CurrentBranchToken is not nil. func (v *GetMutableStateResponse) IsSetCurrentBranchToken() bool { return v != nil && v.CurrentBranchToken != nil } // GetWorkflowState returns the value of WorkflowState if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetWorkflowState() (o int32) { if v != nil && v.WorkflowState != nil { return *v.WorkflowState } return } // IsSetWorkflowState returns true if WorkflowState is not nil. func (v *GetMutableStateResponse) IsSetWorkflowState() bool { return v != nil && v.WorkflowState != nil } // GetWorkflowCloseState returns the value of WorkflowCloseState if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetWorkflowCloseState() (o int32) { if v != nil && v.WorkflowCloseState != nil { return *v.WorkflowCloseState } return } // IsSetWorkflowCloseState returns true if WorkflowCloseState is not nil. func (v *GetMutableStateResponse) IsSetWorkflowCloseState() bool { return v != nil && v.WorkflowCloseState != nil } // GetVersionHistories returns the value of VersionHistories if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetVersionHistories() (o *shared.VersionHistories) { if v != nil && v.VersionHistories != nil { return v.VersionHistories } return } // IsSetVersionHistories returns true if VersionHistories is not nil. func (v *GetMutableStateResponse) IsSetVersionHistories() bool { return v != nil && v.VersionHistories != nil } // GetIsStickyTaskListEnabled returns the value of IsStickyTaskListEnabled if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetIsStickyTaskListEnabled() (o bool) { if v != nil && v.IsStickyTaskListEnabled != nil { return *v.IsStickyTaskListEnabled } return } // IsSetIsStickyTaskListEnabled returns true if IsStickyTaskListEnabled is not nil. func (v *GetMutableStateResponse) IsSetIsStickyTaskListEnabled() bool { return v != nil && v.IsStickyTaskListEnabled != nil } // GetHistorySize returns the value of HistorySize if it is set or its // zero value if it is unset. func (v *GetMutableStateResponse) GetHistorySize() (o int64) { if v != nil && v.HistorySize != nil { return *v.HistorySize } return } // IsSetHistorySize returns true if HistorySize is not nil. func (v *GetMutableStateResponse) IsSetHistorySize() bool { return v != nil && v.HistorySize != nil } type NotifyFailoverMarkersRequest struct { FailoverMarkerTokens []*FailoverMarkerToken `json:"failoverMarkerTokens,omitempty"` } type _List_FailoverMarkerToken_ValueList []*FailoverMarkerToken func (v _List_FailoverMarkerToken_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*FailoverMarkerToken', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_FailoverMarkerToken_ValueList) Size() int { return len(v) } func (_List_FailoverMarkerToken_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_FailoverMarkerToken_ValueList) Close() {} // ToWire translates a NotifyFailoverMarkersRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *NotifyFailoverMarkersRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.FailoverMarkerTokens != nil { w, err = wire.NewValueList(_List_FailoverMarkerToken_ValueList(v.FailoverMarkerTokens)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _FailoverMarkerToken_Read(w wire.Value) (*FailoverMarkerToken, error) { var v FailoverMarkerToken err := v.FromWire(w) return &v, err } func _List_FailoverMarkerToken_Read(l wire.ValueList) ([]*FailoverMarkerToken, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*FailoverMarkerToken, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _FailoverMarkerToken_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a NotifyFailoverMarkersRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a NotifyFailoverMarkersRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v NotifyFailoverMarkersRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *NotifyFailoverMarkersRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.FailoverMarkerTokens, err = _List_FailoverMarkerToken_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_FailoverMarkerToken_Encode(val []*FailoverMarkerToken, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*FailoverMarkerToken', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a NotifyFailoverMarkersRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a NotifyFailoverMarkersRequest struct could not be encoded. func (v *NotifyFailoverMarkersRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FailoverMarkerTokens != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_FailoverMarkerToken_Encode(v.FailoverMarkerTokens, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _FailoverMarkerToken_Decode(sr stream.Reader) (*FailoverMarkerToken, error) { var v FailoverMarkerToken err := v.Decode(sr) return &v, err } func _List_FailoverMarkerToken_Decode(sr stream.Reader) ([]*FailoverMarkerToken, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*FailoverMarkerToken, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _FailoverMarkerToken_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a NotifyFailoverMarkersRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a NotifyFailoverMarkersRequest struct could not be generated from the wire // representation. func (v *NotifyFailoverMarkersRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.FailoverMarkerTokens, err = _List_FailoverMarkerToken_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a NotifyFailoverMarkersRequest // struct. func (v *NotifyFailoverMarkersRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.FailoverMarkerTokens != nil { fields[i] = fmt.Sprintf("FailoverMarkerTokens: %v", v.FailoverMarkerTokens) i++ } return fmt.Sprintf("NotifyFailoverMarkersRequest{%v}", strings.Join(fields[:i], ", ")) } func _List_FailoverMarkerToken_Equals(lhs, rhs []*FailoverMarkerToken) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this NotifyFailoverMarkersRequest match the // provided NotifyFailoverMarkersRequest. // // This function performs a deep comparison. func (v *NotifyFailoverMarkersRequest) Equals(rhs *NotifyFailoverMarkersRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.FailoverMarkerTokens == nil && rhs.FailoverMarkerTokens == nil) || (v.FailoverMarkerTokens != nil && rhs.FailoverMarkerTokens != nil && _List_FailoverMarkerToken_Equals(v.FailoverMarkerTokens, rhs.FailoverMarkerTokens))) { return false } return true } type _List_FailoverMarkerToken_Zapper []*FailoverMarkerToken // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_FailoverMarkerToken_Zapper. func (l _List_FailoverMarkerToken_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of NotifyFailoverMarkersRequest. func (v *NotifyFailoverMarkersRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FailoverMarkerTokens != nil { err = multierr.Append(err, enc.AddArray("failoverMarkerTokens", (_List_FailoverMarkerToken_Zapper)(v.FailoverMarkerTokens))) } return err } // GetFailoverMarkerTokens returns the value of FailoverMarkerTokens if it is set or its // zero value if it is unset. func (v *NotifyFailoverMarkersRequest) GetFailoverMarkerTokens() (o []*FailoverMarkerToken) { if v != nil && v.FailoverMarkerTokens != nil { return v.FailoverMarkerTokens } return } // IsSetFailoverMarkerTokens returns true if FailoverMarkerTokens is not nil. func (v *NotifyFailoverMarkersRequest) IsSetFailoverMarkerTokens() bool { return v != nil && v.FailoverMarkerTokens != nil } type ParentExecutionInfo struct { DomainUUID *string `json:"domainUUID,omitempty"` Domain *string `json:"domain,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` InitiatedId *int64 `json:"initiatedId,omitempty"` } // ToWire translates a ParentExecutionInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ParentExecutionInfo) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 15, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.InitiatedId != nil { w, err = wire.NewValueI64(*(v.InitiatedId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ParentExecutionInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ParentExecutionInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ParentExecutionInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ParentExecutionInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 15: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedId = &x if err != nil { return err } } } } return nil } // Encode serializes a ParentExecutionInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ParentExecutionInfo struct could not be encoded. func (v *ParentExecutionInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 15, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ParentExecutionInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ParentExecutionInfo struct could not be generated from the wire // representation. func (v *ParentExecutionInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 15 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ParentExecutionInfo // struct. func (v *ParentExecutionInfo) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.InitiatedId != nil { fields[i] = fmt.Sprintf("InitiatedId: %v", *(v.InitiatedId)) i++ } return fmt.Sprintf("ParentExecutionInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ParentExecutionInfo match the // provided ParentExecutionInfo. // // This function performs a deep comparison. func (v *ParentExecutionInfo) Equals(rhs *ParentExecutionInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !_I64_EqualsPtr(v.InitiatedId, rhs.InitiatedId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ParentExecutionInfo. func (v *ParentExecutionInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.InitiatedId != nil { enc.AddInt64("initiatedId", *v.InitiatedId) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *ParentExecutionInfo) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *ParentExecutionInfo) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ParentExecutionInfo) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ParentExecutionInfo) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *ParentExecutionInfo) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *ParentExecutionInfo) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetInitiatedId returns the value of InitiatedId if it is set or its // zero value if it is unset. func (v *ParentExecutionInfo) GetInitiatedId() (o int64) { if v != nil && v.InitiatedId != nil { return *v.InitiatedId } return } // IsSetInitiatedId returns true if InitiatedId is not nil. func (v *ParentExecutionInfo) IsSetInitiatedId() bool { return v != nil && v.InitiatedId != nil } type PollMutableStateRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` ExpectedNextEventId *int64 `json:"expectedNextEventId,omitempty"` CurrentBranchToken []byte `json:"currentBranchToken,omitempty"` } // ToWire translates a PollMutableStateRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PollMutableStateRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ExpectedNextEventId != nil { w, err = wire.NewValueI64(*(v.ExpectedNextEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.CurrentBranchToken != nil { w, err = wire.NewValueBinary(v.CurrentBranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a PollMutableStateRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PollMutableStateRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PollMutableStateRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PollMutableStateRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExpectedNextEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.CurrentBranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a PollMutableStateRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PollMutableStateRequest struct could not be encoded. func (v *PollMutableStateRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExpectedNextEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExpectedNextEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CurrentBranchToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.CurrentBranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a PollMutableStateRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PollMutableStateRequest struct could not be generated from the wire // representation. func (v *PollMutableStateRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExpectedNextEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.CurrentBranchToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PollMutableStateRequest // struct. func (v *PollMutableStateRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.ExpectedNextEventId != nil { fields[i] = fmt.Sprintf("ExpectedNextEventId: %v", *(v.ExpectedNextEventId)) i++ } if v.CurrentBranchToken != nil { fields[i] = fmt.Sprintf("CurrentBranchToken: %v", v.CurrentBranchToken) i++ } return fmt.Sprintf("PollMutableStateRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PollMutableStateRequest match the // provided PollMutableStateRequest. // // This function performs a deep comparison. func (v *PollMutableStateRequest) Equals(rhs *PollMutableStateRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !_I64_EqualsPtr(v.ExpectedNextEventId, rhs.ExpectedNextEventId) { return false } if !((v.CurrentBranchToken == nil && rhs.CurrentBranchToken == nil) || (v.CurrentBranchToken != nil && rhs.CurrentBranchToken != nil && bytes.Equal(v.CurrentBranchToken, rhs.CurrentBranchToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PollMutableStateRequest. func (v *PollMutableStateRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.ExpectedNextEventId != nil { enc.AddInt64("expectedNextEventId", *v.ExpectedNextEventId) } if v.CurrentBranchToken != nil { enc.AddString("currentBranchToken", base64.StdEncoding.EncodeToString(v.CurrentBranchToken)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *PollMutableStateRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *PollMutableStateRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *PollMutableStateRequest) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *PollMutableStateRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetExpectedNextEventId returns the value of ExpectedNextEventId if it is set or its // zero value if it is unset. func (v *PollMutableStateRequest) GetExpectedNextEventId() (o int64) { if v != nil && v.ExpectedNextEventId != nil { return *v.ExpectedNextEventId } return } // IsSetExpectedNextEventId returns true if ExpectedNextEventId is not nil. func (v *PollMutableStateRequest) IsSetExpectedNextEventId() bool { return v != nil && v.ExpectedNextEventId != nil } // GetCurrentBranchToken returns the value of CurrentBranchToken if it is set or its // zero value if it is unset. func (v *PollMutableStateRequest) GetCurrentBranchToken() (o []byte) { if v != nil && v.CurrentBranchToken != nil { return v.CurrentBranchToken } return } // IsSetCurrentBranchToken returns true if CurrentBranchToken is not nil. func (v *PollMutableStateRequest) IsSetCurrentBranchToken() bool { return v != nil && v.CurrentBranchToken != nil } type PollMutableStateResponse struct { Execution *shared.WorkflowExecution `json:"execution,omitempty"` WorkflowType *shared.WorkflowType `json:"workflowType,omitempty"` NextEventId *int64 `json:"NextEventId,omitempty"` PreviousStartedEventId *int64 `json:"PreviousStartedEventId,omitempty"` LastFirstEventId *int64 `json:"LastFirstEventId,omitempty"` TaskList *shared.TaskList `json:"taskList,omitempty"` StickyTaskList *shared.TaskList `json:"stickyTaskList,omitempty"` ClientLibraryVersion *string `json:"clientLibraryVersion,omitempty"` ClientFeatureVersion *string `json:"clientFeatureVersion,omitempty"` ClientImpl *string `json:"clientImpl,omitempty"` StickyTaskListScheduleToStartTimeout *int32 `json:"stickyTaskListScheduleToStartTimeout,omitempty"` CurrentBranchToken []byte `json:"currentBranchToken,omitempty"` VersionHistories *shared.VersionHistories `json:"versionHistories,omitempty"` WorkflowState *int32 `json:"workflowState,omitempty"` WorkflowCloseState *int32 `json:"workflowCloseState,omitempty"` } // ToWire translates a PollMutableStateResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PollMutableStateResponse) ToWire() (wire.Value, error) { var ( fields [15]wire.Field i int = 0 w wire.Value err error ) if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.NextEventId != nil { w, err = wire.NewValueI64(*(v.NextEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.PreviousStartedEventId != nil { w, err = wire.NewValueI64(*(v.PreviousStartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 35, Value: w} i++ } if v.LastFirstEventId != nil { w, err = wire.NewValueI64(*(v.LastFirstEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.StickyTaskList != nil { w, err = v.StickyTaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.ClientLibraryVersion != nil { w, err = wire.NewValueString(*(v.ClientLibraryVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.ClientFeatureVersion != nil { w, err = wire.NewValueString(*(v.ClientFeatureVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.ClientImpl != nil { w, err = wire.NewValueString(*(v.ClientImpl)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.StickyTaskListScheduleToStartTimeout != nil { w, err = wire.NewValueI32(*(v.StickyTaskListScheduleToStartTimeout)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.CurrentBranchToken != nil { w, err = wire.NewValueBinary(v.CurrentBranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.VersionHistories != nil { w, err = v.VersionHistories.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.WorkflowState != nil { w, err = wire.NewValueI32(*(v.WorkflowState)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.WorkflowCloseState != nil { w, err = wire.NewValueI32(*(v.WorkflowCloseState)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a PollMutableStateResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PollMutableStateResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PollMutableStateResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PollMutableStateResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NextEventId = &x if err != nil { return err } } case 35: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.PreviousStartedEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastFirstEventId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.StickyTaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClientLibraryVersion = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClientFeatureVersion = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClientImpl = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.StickyTaskListScheduleToStartTimeout = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TBinary { v.CurrentBranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 130: if field.Value.Type() == wire.TStruct { v.VersionHistories, err = _VersionHistories_Read(field.Value) if err != nil { return err } } case 140: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.WorkflowState = &x if err != nil { return err } } case 150: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.WorkflowCloseState = &x if err != nil { return err } } } } return nil } // Encode serializes a PollMutableStateResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PollMutableStateResponse struct could not be encoded. func (v *PollMutableStateResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NextEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PreviousStartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 35, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.PreviousStartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFirstEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastFirstEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyTaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.StickyTaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientLibraryVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClientLibraryVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientFeatureVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClientFeatureVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientImpl != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClientImpl)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyTaskListScheduleToStartTimeout != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.StickyTaskListScheduleToStartTimeout)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CurrentBranchToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.CurrentBranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistories != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TStruct}); err != nil { return err } if err := v.VersionHistories.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowState != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.WorkflowState)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowCloseState != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.WorkflowCloseState)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a PollMutableStateResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PollMutableStateResponse struct could not be generated from the wire // representation. func (v *PollMutableStateResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NextEventId = &x if err != nil { return err } case fh.ID == 35 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.PreviousStartedEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastFirstEventId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.StickyTaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClientLibraryVersion = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClientFeatureVersion = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClientImpl = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.StickyTaskListScheduleToStartTimeout = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TBinary: v.CurrentBranchToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TStruct: v.VersionHistories, err = _VersionHistories_Decode(sr) if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.WorkflowState = &x if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.WorkflowCloseState = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PollMutableStateResponse // struct. func (v *PollMutableStateResponse) String() string { if v == nil { return "" } var fields [15]string i := 0 if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.NextEventId != nil { fields[i] = fmt.Sprintf("NextEventId: %v", *(v.NextEventId)) i++ } if v.PreviousStartedEventId != nil { fields[i] = fmt.Sprintf("PreviousStartedEventId: %v", *(v.PreviousStartedEventId)) i++ } if v.LastFirstEventId != nil { fields[i] = fmt.Sprintf("LastFirstEventId: %v", *(v.LastFirstEventId)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.StickyTaskList != nil { fields[i] = fmt.Sprintf("StickyTaskList: %v", v.StickyTaskList) i++ } if v.ClientLibraryVersion != nil { fields[i] = fmt.Sprintf("ClientLibraryVersion: %v", *(v.ClientLibraryVersion)) i++ } if v.ClientFeatureVersion != nil { fields[i] = fmt.Sprintf("ClientFeatureVersion: %v", *(v.ClientFeatureVersion)) i++ } if v.ClientImpl != nil { fields[i] = fmt.Sprintf("ClientImpl: %v", *(v.ClientImpl)) i++ } if v.StickyTaskListScheduleToStartTimeout != nil { fields[i] = fmt.Sprintf("StickyTaskListScheduleToStartTimeout: %v", *(v.StickyTaskListScheduleToStartTimeout)) i++ } if v.CurrentBranchToken != nil { fields[i] = fmt.Sprintf("CurrentBranchToken: %v", v.CurrentBranchToken) i++ } if v.VersionHistories != nil { fields[i] = fmt.Sprintf("VersionHistories: %v", v.VersionHistories) i++ } if v.WorkflowState != nil { fields[i] = fmt.Sprintf("WorkflowState: %v", *(v.WorkflowState)) i++ } if v.WorkflowCloseState != nil { fields[i] = fmt.Sprintf("WorkflowCloseState: %v", *(v.WorkflowCloseState)) i++ } return fmt.Sprintf("PollMutableStateResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PollMutableStateResponse match the // provided PollMutableStateResponse. // // This function performs a deep comparison. func (v *PollMutableStateResponse) Equals(rhs *PollMutableStateResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_I64_EqualsPtr(v.NextEventId, rhs.NextEventId) { return false } if !_I64_EqualsPtr(v.PreviousStartedEventId, rhs.PreviousStartedEventId) { return false } if !_I64_EqualsPtr(v.LastFirstEventId, rhs.LastFirstEventId) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.StickyTaskList == nil && rhs.StickyTaskList == nil) || (v.StickyTaskList != nil && rhs.StickyTaskList != nil && v.StickyTaskList.Equals(rhs.StickyTaskList))) { return false } if !_String_EqualsPtr(v.ClientLibraryVersion, rhs.ClientLibraryVersion) { return false } if !_String_EqualsPtr(v.ClientFeatureVersion, rhs.ClientFeatureVersion) { return false } if !_String_EqualsPtr(v.ClientImpl, rhs.ClientImpl) { return false } if !_I32_EqualsPtr(v.StickyTaskListScheduleToStartTimeout, rhs.StickyTaskListScheduleToStartTimeout) { return false } if !((v.CurrentBranchToken == nil && rhs.CurrentBranchToken == nil) || (v.CurrentBranchToken != nil && rhs.CurrentBranchToken != nil && bytes.Equal(v.CurrentBranchToken, rhs.CurrentBranchToken))) { return false } if !((v.VersionHistories == nil && rhs.VersionHistories == nil) || (v.VersionHistories != nil && rhs.VersionHistories != nil && v.VersionHistories.Equals(rhs.VersionHistories))) { return false } if !_I32_EqualsPtr(v.WorkflowState, rhs.WorkflowState) { return false } if !_I32_EqualsPtr(v.WorkflowCloseState, rhs.WorkflowCloseState) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PollMutableStateResponse. func (v *PollMutableStateResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.NextEventId != nil { enc.AddInt64("NextEventId", *v.NextEventId) } if v.PreviousStartedEventId != nil { enc.AddInt64("PreviousStartedEventId", *v.PreviousStartedEventId) } if v.LastFirstEventId != nil { enc.AddInt64("LastFirstEventId", *v.LastFirstEventId) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.StickyTaskList != nil { err = multierr.Append(err, enc.AddObject("stickyTaskList", v.StickyTaskList)) } if v.ClientLibraryVersion != nil { enc.AddString("clientLibraryVersion", *v.ClientLibraryVersion) } if v.ClientFeatureVersion != nil { enc.AddString("clientFeatureVersion", *v.ClientFeatureVersion) } if v.ClientImpl != nil { enc.AddString("clientImpl", *v.ClientImpl) } if v.StickyTaskListScheduleToStartTimeout != nil { enc.AddInt32("stickyTaskListScheduleToStartTimeout", *v.StickyTaskListScheduleToStartTimeout) } if v.CurrentBranchToken != nil { enc.AddString("currentBranchToken", base64.StdEncoding.EncodeToString(v.CurrentBranchToken)) } if v.VersionHistories != nil { err = multierr.Append(err, enc.AddObject("versionHistories", v.VersionHistories)) } if v.WorkflowState != nil { enc.AddInt32("workflowState", *v.WorkflowState) } if v.WorkflowCloseState != nil { enc.AddInt32("workflowCloseState", *v.WorkflowCloseState) } return err } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *PollMutableStateResponse) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetWorkflowType() (o *shared.WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *PollMutableStateResponse) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetNextEventId returns the value of NextEventId if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetNextEventId() (o int64) { if v != nil && v.NextEventId != nil { return *v.NextEventId } return } // IsSetNextEventId returns true if NextEventId is not nil. func (v *PollMutableStateResponse) IsSetNextEventId() bool { return v != nil && v.NextEventId != nil } // GetPreviousStartedEventId returns the value of PreviousStartedEventId if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetPreviousStartedEventId() (o int64) { if v != nil && v.PreviousStartedEventId != nil { return *v.PreviousStartedEventId } return } // IsSetPreviousStartedEventId returns true if PreviousStartedEventId is not nil. func (v *PollMutableStateResponse) IsSetPreviousStartedEventId() bool { return v != nil && v.PreviousStartedEventId != nil } // GetLastFirstEventId returns the value of LastFirstEventId if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetLastFirstEventId() (o int64) { if v != nil && v.LastFirstEventId != nil { return *v.LastFirstEventId } return } // IsSetLastFirstEventId returns true if LastFirstEventId is not nil. func (v *PollMutableStateResponse) IsSetLastFirstEventId() bool { return v != nil && v.LastFirstEventId != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetTaskList() (o *shared.TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *PollMutableStateResponse) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetStickyTaskList returns the value of StickyTaskList if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetStickyTaskList() (o *shared.TaskList) { if v != nil && v.StickyTaskList != nil { return v.StickyTaskList } return } // IsSetStickyTaskList returns true if StickyTaskList is not nil. func (v *PollMutableStateResponse) IsSetStickyTaskList() bool { return v != nil && v.StickyTaskList != nil } // GetClientLibraryVersion returns the value of ClientLibraryVersion if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetClientLibraryVersion() (o string) { if v != nil && v.ClientLibraryVersion != nil { return *v.ClientLibraryVersion } return } // IsSetClientLibraryVersion returns true if ClientLibraryVersion is not nil. func (v *PollMutableStateResponse) IsSetClientLibraryVersion() bool { return v != nil && v.ClientLibraryVersion != nil } // GetClientFeatureVersion returns the value of ClientFeatureVersion if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetClientFeatureVersion() (o string) { if v != nil && v.ClientFeatureVersion != nil { return *v.ClientFeatureVersion } return } // IsSetClientFeatureVersion returns true if ClientFeatureVersion is not nil. func (v *PollMutableStateResponse) IsSetClientFeatureVersion() bool { return v != nil && v.ClientFeatureVersion != nil } // GetClientImpl returns the value of ClientImpl if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetClientImpl() (o string) { if v != nil && v.ClientImpl != nil { return *v.ClientImpl } return } // IsSetClientImpl returns true if ClientImpl is not nil. func (v *PollMutableStateResponse) IsSetClientImpl() bool { return v != nil && v.ClientImpl != nil } // GetStickyTaskListScheduleToStartTimeout returns the value of StickyTaskListScheduleToStartTimeout if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetStickyTaskListScheduleToStartTimeout() (o int32) { if v != nil && v.StickyTaskListScheduleToStartTimeout != nil { return *v.StickyTaskListScheduleToStartTimeout } return } // IsSetStickyTaskListScheduleToStartTimeout returns true if StickyTaskListScheduleToStartTimeout is not nil. func (v *PollMutableStateResponse) IsSetStickyTaskListScheduleToStartTimeout() bool { return v != nil && v.StickyTaskListScheduleToStartTimeout != nil } // GetCurrentBranchToken returns the value of CurrentBranchToken if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetCurrentBranchToken() (o []byte) { if v != nil && v.CurrentBranchToken != nil { return v.CurrentBranchToken } return } // IsSetCurrentBranchToken returns true if CurrentBranchToken is not nil. func (v *PollMutableStateResponse) IsSetCurrentBranchToken() bool { return v != nil && v.CurrentBranchToken != nil } // GetVersionHistories returns the value of VersionHistories if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetVersionHistories() (o *shared.VersionHistories) { if v != nil && v.VersionHistories != nil { return v.VersionHistories } return } // IsSetVersionHistories returns true if VersionHistories is not nil. func (v *PollMutableStateResponse) IsSetVersionHistories() bool { return v != nil && v.VersionHistories != nil } // GetWorkflowState returns the value of WorkflowState if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetWorkflowState() (o int32) { if v != nil && v.WorkflowState != nil { return *v.WorkflowState } return } // IsSetWorkflowState returns true if WorkflowState is not nil. func (v *PollMutableStateResponse) IsSetWorkflowState() bool { return v != nil && v.WorkflowState != nil } // GetWorkflowCloseState returns the value of WorkflowCloseState if it is set or its // zero value if it is unset. func (v *PollMutableStateResponse) GetWorkflowCloseState() (o int32) { if v != nil && v.WorkflowCloseState != nil { return *v.WorkflowCloseState } return } // IsSetWorkflowCloseState returns true if WorkflowCloseState is not nil. func (v *PollMutableStateResponse) IsSetWorkflowCloseState() bool { return v != nil && v.WorkflowCloseState != nil } type ProcessingQueueState struct { Level *int32 `json:"level,omitempty"` AckLevel *int64 `json:"ackLevel,omitempty"` MaxLevel *int64 `json:"maxLevel,omitempty"` DomainFilter *DomainFilter `json:"domainFilter,omitempty"` } // ToWire translates a ProcessingQueueState struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ProcessingQueueState) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Level != nil { w, err = wire.NewValueI32(*(v.Level)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.AckLevel != nil { w, err = wire.NewValueI64(*(v.AckLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.MaxLevel != nil { w, err = wire.NewValueI64(*(v.MaxLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.DomainFilter != nil { w, err = v.DomainFilter.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DomainFilter_Read(w wire.Value) (*DomainFilter, error) { var v DomainFilter err := v.FromWire(w) return &v, err } // FromWire deserializes a ProcessingQueueState struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ProcessingQueueState struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ProcessingQueueState // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ProcessingQueueState) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Level = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.AckLevel = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.MaxLevel = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.DomainFilter, err = _DomainFilter_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ProcessingQueueState struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ProcessingQueueState struct could not be encoded. func (v *ProcessingQueueState) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Level != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Level)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AckLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.AckLevel)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MaxLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.MaxLevel)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainFilter != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.DomainFilter.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DomainFilter_Decode(sr stream.Reader) (*DomainFilter, error) { var v DomainFilter err := v.Decode(sr) return &v, err } // Decode deserializes a ProcessingQueueState struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ProcessingQueueState struct could not be generated from the wire // representation. func (v *ProcessingQueueState) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Level = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.AckLevel = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.MaxLevel = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.DomainFilter, err = _DomainFilter_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ProcessingQueueState // struct. func (v *ProcessingQueueState) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Level != nil { fields[i] = fmt.Sprintf("Level: %v", *(v.Level)) i++ } if v.AckLevel != nil { fields[i] = fmt.Sprintf("AckLevel: %v", *(v.AckLevel)) i++ } if v.MaxLevel != nil { fields[i] = fmt.Sprintf("MaxLevel: %v", *(v.MaxLevel)) i++ } if v.DomainFilter != nil { fields[i] = fmt.Sprintf("DomainFilter: %v", v.DomainFilter) i++ } return fmt.Sprintf("ProcessingQueueState{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ProcessingQueueState match the // provided ProcessingQueueState. // // This function performs a deep comparison. func (v *ProcessingQueueState) Equals(rhs *ProcessingQueueState) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.Level, rhs.Level) { return false } if !_I64_EqualsPtr(v.AckLevel, rhs.AckLevel) { return false } if !_I64_EqualsPtr(v.MaxLevel, rhs.MaxLevel) { return false } if !((v.DomainFilter == nil && rhs.DomainFilter == nil) || (v.DomainFilter != nil && rhs.DomainFilter != nil && v.DomainFilter.Equals(rhs.DomainFilter))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ProcessingQueueState. func (v *ProcessingQueueState) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Level != nil { enc.AddInt32("level", *v.Level) } if v.AckLevel != nil { enc.AddInt64("ackLevel", *v.AckLevel) } if v.MaxLevel != nil { enc.AddInt64("maxLevel", *v.MaxLevel) } if v.DomainFilter != nil { err = multierr.Append(err, enc.AddObject("domainFilter", v.DomainFilter)) } return err } // GetLevel returns the value of Level if it is set or its // zero value if it is unset. func (v *ProcessingQueueState) GetLevel() (o int32) { if v != nil && v.Level != nil { return *v.Level } return } // IsSetLevel returns true if Level is not nil. func (v *ProcessingQueueState) IsSetLevel() bool { return v != nil && v.Level != nil } // GetAckLevel returns the value of AckLevel if it is set or its // zero value if it is unset. func (v *ProcessingQueueState) GetAckLevel() (o int64) { if v != nil && v.AckLevel != nil { return *v.AckLevel } return } // IsSetAckLevel returns true if AckLevel is not nil. func (v *ProcessingQueueState) IsSetAckLevel() bool { return v != nil && v.AckLevel != nil } // GetMaxLevel returns the value of MaxLevel if it is set or its // zero value if it is unset. func (v *ProcessingQueueState) GetMaxLevel() (o int64) { if v != nil && v.MaxLevel != nil { return *v.MaxLevel } return } // IsSetMaxLevel returns true if MaxLevel is not nil. func (v *ProcessingQueueState) IsSetMaxLevel() bool { return v != nil && v.MaxLevel != nil } // GetDomainFilter returns the value of DomainFilter if it is set or its // zero value if it is unset. func (v *ProcessingQueueState) GetDomainFilter() (o *DomainFilter) { if v != nil && v.DomainFilter != nil { return v.DomainFilter } return } // IsSetDomainFilter returns true if DomainFilter is not nil. func (v *ProcessingQueueState) IsSetDomainFilter() bool { return v != nil && v.DomainFilter != nil } type ProcessingQueueStates struct { StatesByCluster map[string][]*ProcessingQueueState `json:"statesByCluster,omitempty"` } type _List_ProcessingQueueState_ValueList []*ProcessingQueueState func (v _List_ProcessingQueueState_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*ProcessingQueueState', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_ProcessingQueueState_ValueList) Size() int { return len(v) } func (_List_ProcessingQueueState_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_ProcessingQueueState_ValueList) Close() {} type _Map_String_List_ProcessingQueueState_MapItemList map[string][]*ProcessingQueueState func (m _Map_String_List_ProcessingQueueState_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string][]*ProcessingQueueState', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := wire.NewValueList(_List_ProcessingQueueState_ValueList(v)), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_List_ProcessingQueueState_MapItemList) Size() int { return len(m) } func (_Map_String_List_ProcessingQueueState_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_List_ProcessingQueueState_MapItemList) ValueType() wire.Type { return wire.TList } func (_Map_String_List_ProcessingQueueState_MapItemList) Close() {} // ToWire translates a ProcessingQueueStates struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ProcessingQueueStates) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.StatesByCluster != nil { w, err = wire.NewValueMap(_Map_String_List_ProcessingQueueState_MapItemList(v.StatesByCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ProcessingQueueState_Read(w wire.Value) (*ProcessingQueueState, error) { var v ProcessingQueueState err := v.FromWire(w) return &v, err } func _List_ProcessingQueueState_Read(l wire.ValueList) ([]*ProcessingQueueState, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*ProcessingQueueState, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _ProcessingQueueState_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _Map_String_List_ProcessingQueueState_Read(m wire.MapItemList) (map[string][]*ProcessingQueueState, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TList { return nil, nil } o := make(map[string][]*ProcessingQueueState, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _List_ProcessingQueueState_Read(x.Value.GetList()) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a ProcessingQueueStates struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ProcessingQueueStates struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ProcessingQueueStates // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ProcessingQueueStates) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.StatesByCluster, err = _Map_String_List_ProcessingQueueState_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _List_ProcessingQueueState_Encode(val []*ProcessingQueueState, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*ProcessingQueueState', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } func _Map_String_List_ProcessingQueueState_Encode(val map[string][]*ProcessingQueueState, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TList, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string][]*ProcessingQueueState', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := _List_ProcessingQueueState_Encode(v, sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a ProcessingQueueStates struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ProcessingQueueStates struct could not be encoded. func (v *ProcessingQueueStates) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.StatesByCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_List_ProcessingQueueState_Encode(v.StatesByCluster, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ProcessingQueueState_Decode(sr stream.Reader) (*ProcessingQueueState, error) { var v ProcessingQueueState err := v.Decode(sr) return &v, err } func _List_ProcessingQueueState_Decode(sr stream.Reader) ([]*ProcessingQueueState, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*ProcessingQueueState, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _ProcessingQueueState_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _Map_String_List_ProcessingQueueState_Decode(sr stream.Reader) (map[string][]*ProcessingQueueState, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TList { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string][]*ProcessingQueueState, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _List_ProcessingQueueState_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ProcessingQueueStates struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ProcessingQueueStates struct could not be generated from the wire // representation. func (v *ProcessingQueueStates) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.StatesByCluster, err = _Map_String_List_ProcessingQueueState_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ProcessingQueueStates // struct. func (v *ProcessingQueueStates) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.StatesByCluster != nil { fields[i] = fmt.Sprintf("StatesByCluster: %v", v.StatesByCluster) i++ } return fmt.Sprintf("ProcessingQueueStates{%v}", strings.Join(fields[:i], ", ")) } func _List_ProcessingQueueState_Equals(lhs, rhs []*ProcessingQueueState) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } func _Map_String_List_ProcessingQueueState_Equals(lhs, rhs map[string][]*ProcessingQueueState) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !_List_ProcessingQueueState_Equals(lv, rv) { return false } } return true } // Equals returns true if all the fields of this ProcessingQueueStates match the // provided ProcessingQueueStates. // // This function performs a deep comparison. func (v *ProcessingQueueStates) Equals(rhs *ProcessingQueueStates) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.StatesByCluster == nil && rhs.StatesByCluster == nil) || (v.StatesByCluster != nil && rhs.StatesByCluster != nil && _Map_String_List_ProcessingQueueState_Equals(v.StatesByCluster, rhs.StatesByCluster))) { return false } return true } type _List_ProcessingQueueState_Zapper []*ProcessingQueueState // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_ProcessingQueueState_Zapper. func (l _List_ProcessingQueueState_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } type _Map_String_List_ProcessingQueueState_Zapper map[string][]*ProcessingQueueState // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_List_ProcessingQueueState_Zapper. func (m _Map_String_List_ProcessingQueueState_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddArray((string)(k), (_List_ProcessingQueueState_Zapper)(v))) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ProcessingQueueStates. func (v *ProcessingQueueStates) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.StatesByCluster != nil { err = multierr.Append(err, enc.AddObject("statesByCluster", (_Map_String_List_ProcessingQueueState_Zapper)(v.StatesByCluster))) } return err } // GetStatesByCluster returns the value of StatesByCluster if it is set or its // zero value if it is unset. func (v *ProcessingQueueStates) GetStatesByCluster() (o map[string][]*ProcessingQueueState) { if v != nil && v.StatesByCluster != nil { return v.StatesByCluster } return } // IsSetStatesByCluster returns true if StatesByCluster is not nil. func (v *ProcessingQueueStates) IsSetStatesByCluster() bool { return v != nil && v.StatesByCluster != nil } type QueryWorkflowRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` Request *shared.QueryWorkflowRequest `json:"request,omitempty"` } // ToWire translates a QueryWorkflowRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *QueryWorkflowRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryWorkflowRequest_Read(w wire.Value) (*shared.QueryWorkflowRequest, error) { var v shared.QueryWorkflowRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a QueryWorkflowRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a QueryWorkflowRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v QueryWorkflowRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *QueryWorkflowRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Request, err = _QueryWorkflowRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a QueryWorkflowRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a QueryWorkflowRequest struct could not be encoded. func (v *QueryWorkflowRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryWorkflowRequest_Decode(sr stream.Reader) (*shared.QueryWorkflowRequest, error) { var v shared.QueryWorkflowRequest err := v.Decode(sr) return &v, err } // Decode deserializes a QueryWorkflowRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a QueryWorkflowRequest struct could not be generated from the wire // representation. func (v *QueryWorkflowRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Request, err = _QueryWorkflowRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a QueryWorkflowRequest // struct. func (v *QueryWorkflowRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("QueryWorkflowRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this QueryWorkflowRequest match the // provided QueryWorkflowRequest. // // This function performs a deep comparison. func (v *QueryWorkflowRequest) Equals(rhs *QueryWorkflowRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryWorkflowRequest. func (v *QueryWorkflowRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *QueryWorkflowRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetRequest() (o *shared.QueryWorkflowRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *QueryWorkflowRequest) IsSetRequest() bool { return v != nil && v.Request != nil } type QueryWorkflowResponse struct { Response *shared.QueryWorkflowResponse `json:"response,omitempty"` } // ToWire translates a QueryWorkflowResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *QueryWorkflowResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Response != nil { w, err = v.Response.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryWorkflowResponse_Read(w wire.Value) (*shared.QueryWorkflowResponse, error) { var v shared.QueryWorkflowResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a QueryWorkflowResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a QueryWorkflowResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v QueryWorkflowResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *QueryWorkflowResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Response, err = _QueryWorkflowResponse_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a QueryWorkflowResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a QueryWorkflowResponse struct could not be encoded. func (v *QueryWorkflowResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Response != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Response.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryWorkflowResponse_Decode(sr stream.Reader) (*shared.QueryWorkflowResponse, error) { var v shared.QueryWorkflowResponse err := v.Decode(sr) return &v, err } // Decode deserializes a QueryWorkflowResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a QueryWorkflowResponse struct could not be generated from the wire // representation. func (v *QueryWorkflowResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Response, err = _QueryWorkflowResponse_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a QueryWorkflowResponse // struct. func (v *QueryWorkflowResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Response != nil { fields[i] = fmt.Sprintf("Response: %v", v.Response) i++ } return fmt.Sprintf("QueryWorkflowResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this QueryWorkflowResponse match the // provided QueryWorkflowResponse. // // This function performs a deep comparison. func (v *QueryWorkflowResponse) Equals(rhs *QueryWorkflowResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Response == nil && rhs.Response == nil) || (v.Response != nil && rhs.Response != nil && v.Response.Equals(rhs.Response))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryWorkflowResponse. func (v *QueryWorkflowResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Response != nil { err = multierr.Append(err, enc.AddObject("response", v.Response)) } return err } // GetResponse returns the value of Response if it is set or its // zero value if it is unset. func (v *QueryWorkflowResponse) GetResponse() (o *shared.QueryWorkflowResponse) { if v != nil && v.Response != nil { return v.Response } return } // IsSetResponse returns true if Response is not nil. func (v *QueryWorkflowResponse) IsSetResponse() bool { return v != nil && v.Response != nil } type RatelimitUpdateRequest struct { // impl-specific data. // // likely some simple top-level keys and then either: // - map // - list // // this is a single blob rather than a collection to save on // repeated serialization of the type name, and to allow impls // to choose whatever structures are most-convenient for them. Data *shared.Any `json:"data,omitempty"` } // ToWire translates a RatelimitUpdateRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RatelimitUpdateRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Data != nil { w, err = v.Data.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Any_Read(w wire.Value) (*shared.Any, error) { var v shared.Any err := v.FromWire(w) return &v, err } // FromWire deserializes a RatelimitUpdateRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RatelimitUpdateRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RatelimitUpdateRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RatelimitUpdateRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Data, err = _Any_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RatelimitUpdateRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RatelimitUpdateRequest struct could not be encoded. func (v *RatelimitUpdateRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Data != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Data.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _Any_Decode(sr stream.Reader) (*shared.Any, error) { var v shared.Any err := v.Decode(sr) return &v, err } // Decode deserializes a RatelimitUpdateRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RatelimitUpdateRequest struct could not be generated from the wire // representation. func (v *RatelimitUpdateRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Data, err = _Any_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RatelimitUpdateRequest // struct. func (v *RatelimitUpdateRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Data != nil { fields[i] = fmt.Sprintf("Data: %v", v.Data) i++ } return fmt.Sprintf("RatelimitUpdateRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RatelimitUpdateRequest match the // provided RatelimitUpdateRequest. // // This function performs a deep comparison. func (v *RatelimitUpdateRequest) Equals(rhs *RatelimitUpdateRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Data == nil && rhs.Data == nil) || (v.Data != nil && rhs.Data != nil && v.Data.Equals(rhs.Data))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RatelimitUpdateRequest. func (v *RatelimitUpdateRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Data != nil { err = multierr.Append(err, enc.AddObject("data", v.Data)) } return err } // GetData returns the value of Data if it is set or its // zero value if it is unset. func (v *RatelimitUpdateRequest) GetData() (o *shared.Any) { if v != nil && v.Data != nil { return v.Data } return } // IsSetData returns true if Data is not nil. func (v *RatelimitUpdateRequest) IsSetData() bool { return v != nil && v.Data != nil } type RatelimitUpdateResponse struct { // impl-specific data. // // likely some simple top-level keys and then either: // - map // - list // // this is a single blob rather than a collection to save on // repeated serialization of the type name, and to allow impls // to choose whatever structures are most-convenient for them. Data *shared.Any `json:"data,omitempty"` } // ToWire translates a RatelimitUpdateResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RatelimitUpdateResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Data != nil { w, err = v.Data.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RatelimitUpdateResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RatelimitUpdateResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RatelimitUpdateResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RatelimitUpdateResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Data, err = _Any_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RatelimitUpdateResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RatelimitUpdateResponse struct could not be encoded. func (v *RatelimitUpdateResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Data != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Data.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RatelimitUpdateResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RatelimitUpdateResponse struct could not be generated from the wire // representation. func (v *RatelimitUpdateResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Data, err = _Any_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RatelimitUpdateResponse // struct. func (v *RatelimitUpdateResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Data != nil { fields[i] = fmt.Sprintf("Data: %v", v.Data) i++ } return fmt.Sprintf("RatelimitUpdateResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RatelimitUpdateResponse match the // provided RatelimitUpdateResponse. // // This function performs a deep comparison. func (v *RatelimitUpdateResponse) Equals(rhs *RatelimitUpdateResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Data == nil && rhs.Data == nil) || (v.Data != nil && rhs.Data != nil && v.Data.Equals(rhs.Data))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RatelimitUpdateResponse. func (v *RatelimitUpdateResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Data != nil { err = multierr.Append(err, enc.AddObject("data", v.Data)) } return err } // GetData returns the value of Data if it is set or its // zero value if it is unset. func (v *RatelimitUpdateResponse) GetData() (o *shared.Any) { if v != nil && v.Data != nil { return v.Data } return } // IsSetData returns true if Data is not nil. func (v *RatelimitUpdateResponse) IsSetData() bool { return v != nil && v.Data != nil } type ReapplyEventsRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` Request *shared.ReapplyEventsRequest `json:"request,omitempty"` } // ToWire translates a ReapplyEventsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReapplyEventsRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReapplyEventsRequest_Read(w wire.Value) (*shared.ReapplyEventsRequest, error) { var v shared.ReapplyEventsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a ReapplyEventsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReapplyEventsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReapplyEventsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReapplyEventsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Request, err = _ReapplyEventsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ReapplyEventsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReapplyEventsRequest struct could not be encoded. func (v *ReapplyEventsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReapplyEventsRequest_Decode(sr stream.Reader) (*shared.ReapplyEventsRequest, error) { var v shared.ReapplyEventsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a ReapplyEventsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReapplyEventsRequest struct could not be generated from the wire // representation. func (v *ReapplyEventsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Request, err = _ReapplyEventsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReapplyEventsRequest // struct. func (v *ReapplyEventsRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("ReapplyEventsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ReapplyEventsRequest match the // provided ReapplyEventsRequest. // // This function performs a deep comparison. func (v *ReapplyEventsRequest) Equals(rhs *ReapplyEventsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReapplyEventsRequest. func (v *ReapplyEventsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *ReapplyEventsRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *ReapplyEventsRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *ReapplyEventsRequest) GetRequest() (o *shared.ReapplyEventsRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *ReapplyEventsRequest) IsSetRequest() bool { return v != nil && v.Request != nil } type RecordActivityTaskHeartbeatRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` HeartbeatRequest *shared.RecordActivityTaskHeartbeatRequest `json:"heartbeatRequest,omitempty"` } // ToWire translates a RecordActivityTaskHeartbeatRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RecordActivityTaskHeartbeatRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.HeartbeatRequest != nil { w, err = v.HeartbeatRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordActivityTaskHeartbeatRequest_Read(w wire.Value) (*shared.RecordActivityTaskHeartbeatRequest, error) { var v shared.RecordActivityTaskHeartbeatRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RecordActivityTaskHeartbeatRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RecordActivityTaskHeartbeatRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RecordActivityTaskHeartbeatRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RecordActivityTaskHeartbeatRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.HeartbeatRequest, err = _RecordActivityTaskHeartbeatRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RecordActivityTaskHeartbeatRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RecordActivityTaskHeartbeatRequest struct could not be encoded. func (v *RecordActivityTaskHeartbeatRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HeartbeatRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.HeartbeatRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RecordActivityTaskHeartbeatRequest_Decode(sr stream.Reader) (*shared.RecordActivityTaskHeartbeatRequest, error) { var v shared.RecordActivityTaskHeartbeatRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RecordActivityTaskHeartbeatRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RecordActivityTaskHeartbeatRequest struct could not be generated from the wire // representation. func (v *RecordActivityTaskHeartbeatRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.HeartbeatRequest, err = _RecordActivityTaskHeartbeatRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RecordActivityTaskHeartbeatRequest // struct. func (v *RecordActivityTaskHeartbeatRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.HeartbeatRequest != nil { fields[i] = fmt.Sprintf("HeartbeatRequest: %v", v.HeartbeatRequest) i++ } return fmt.Sprintf("RecordActivityTaskHeartbeatRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RecordActivityTaskHeartbeatRequest match the // provided RecordActivityTaskHeartbeatRequest. // // This function performs a deep comparison. func (v *RecordActivityTaskHeartbeatRequest) Equals(rhs *RecordActivityTaskHeartbeatRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.HeartbeatRequest == nil && rhs.HeartbeatRequest == nil) || (v.HeartbeatRequest != nil && rhs.HeartbeatRequest != nil && v.HeartbeatRequest.Equals(rhs.HeartbeatRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RecordActivityTaskHeartbeatRequest. func (v *RecordActivityTaskHeartbeatRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.HeartbeatRequest != nil { err = multierr.Append(err, enc.AddObject("heartbeatRequest", v.HeartbeatRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RecordActivityTaskHeartbeatRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetHeartbeatRequest returns the value of HeartbeatRequest if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatRequest) GetHeartbeatRequest() (o *shared.RecordActivityTaskHeartbeatRequest) { if v != nil && v.HeartbeatRequest != nil { return v.HeartbeatRequest } return } // IsSetHeartbeatRequest returns true if HeartbeatRequest is not nil. func (v *RecordActivityTaskHeartbeatRequest) IsSetHeartbeatRequest() bool { return v != nil && v.HeartbeatRequest != nil } type RecordActivityTaskStartedRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` WorkflowExecution *shared.WorkflowExecution `json:"workflowExecution,omitempty"` ScheduleId *int64 `json:"scheduleId,omitempty"` TaskId *int64 `json:"taskId,omitempty"` RequestId *string `json:"requestId,omitempty"` PollRequest *shared.PollForActivityTaskRequest `json:"pollRequest,omitempty"` } // ToWire translates a RecordActivityTaskStartedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RecordActivityTaskStartedRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ScheduleId != nil { w, err = wire.NewValueI64(*(v.ScheduleId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.TaskId != nil { w, err = wire.NewValueI64(*(v.TaskId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 45, Value: w} i++ } if v.PollRequest != nil { w, err = v.PollRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForActivityTaskRequest_Read(w wire.Value) (*shared.PollForActivityTaskRequest, error) { var v shared.PollForActivityTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RecordActivityTaskStartedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RecordActivityTaskStartedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RecordActivityTaskStartedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RecordActivityTaskStartedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduleId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskId = &x if err != nil { return err } } case 45: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.PollRequest, err = _PollForActivityTaskRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RecordActivityTaskStartedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RecordActivityTaskStartedRequest struct could not be encoded. func (v *RecordActivityTaskStartedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduleId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 45, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PollRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.PollRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollForActivityTaskRequest_Decode(sr stream.Reader) (*shared.PollForActivityTaskRequest, error) { var v shared.PollForActivityTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RecordActivityTaskStartedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RecordActivityTaskStartedRequest struct could not be generated from the wire // representation. func (v *RecordActivityTaskStartedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduleId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskId = &x if err != nil { return err } case fh.ID == 45 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.PollRequest, err = _PollForActivityTaskRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RecordActivityTaskStartedRequest // struct. func (v *RecordActivityTaskStartedRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.ScheduleId != nil { fields[i] = fmt.Sprintf("ScheduleId: %v", *(v.ScheduleId)) i++ } if v.TaskId != nil { fields[i] = fmt.Sprintf("TaskId: %v", *(v.TaskId)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } if v.PollRequest != nil { fields[i] = fmt.Sprintf("PollRequest: %v", v.PollRequest) i++ } return fmt.Sprintf("RecordActivityTaskStartedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RecordActivityTaskStartedRequest match the // provided RecordActivityTaskStartedRequest. // // This function performs a deep comparison. func (v *RecordActivityTaskStartedRequest) Equals(rhs *RecordActivityTaskStartedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_I64_EqualsPtr(v.ScheduleId, rhs.ScheduleId) { return false } if !_I64_EqualsPtr(v.TaskId, rhs.TaskId) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } if !((v.PollRequest == nil && rhs.PollRequest == nil) || (v.PollRequest != nil && rhs.PollRequest != nil && v.PollRequest.Equals(rhs.PollRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RecordActivityTaskStartedRequest. func (v *RecordActivityTaskStartedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.ScheduleId != nil { enc.AddInt64("scheduleId", *v.ScheduleId) } if v.TaskId != nil { enc.AddInt64("taskId", *v.TaskId) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } if v.PollRequest != nil { err = multierr.Append(err, enc.AddObject("pollRequest", v.PollRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RecordActivityTaskStartedRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedRequest) GetWorkflowExecution() (o *shared.WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *RecordActivityTaskStartedRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetScheduleId returns the value of ScheduleId if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedRequest) GetScheduleId() (o int64) { if v != nil && v.ScheduleId != nil { return *v.ScheduleId } return } // IsSetScheduleId returns true if ScheduleId is not nil. func (v *RecordActivityTaskStartedRequest) IsSetScheduleId() bool { return v != nil && v.ScheduleId != nil } // GetTaskId returns the value of TaskId if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedRequest) GetTaskId() (o int64) { if v != nil && v.TaskId != nil { return *v.TaskId } return } // IsSetTaskId returns true if TaskId is not nil. func (v *RecordActivityTaskStartedRequest) IsSetTaskId() bool { return v != nil && v.TaskId != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedRequest) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *RecordActivityTaskStartedRequest) IsSetRequestId() bool { return v != nil && v.RequestId != nil } // GetPollRequest returns the value of PollRequest if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedRequest) GetPollRequest() (o *shared.PollForActivityTaskRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // IsSetPollRequest returns true if PollRequest is not nil. func (v *RecordActivityTaskStartedRequest) IsSetPollRequest() bool { return v != nil && v.PollRequest != nil } type RecordActivityTaskStartedResponse struct { ScheduledEvent *shared.HistoryEvent `json:"scheduledEvent,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Attempt *int64 `json:"attempt,omitempty"` ScheduledTimestampOfThisAttempt *int64 `json:"scheduledTimestampOfThisAttempt,omitempty"` HeartbeatDetails []byte `json:"heartbeatDetails,omitempty"` WorkflowType *shared.WorkflowType `json:"workflowType,omitempty"` WorkflowDomain *string `json:"workflowDomain,omitempty"` } // ToWire translates a RecordActivityTaskStartedResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RecordActivityTaskStartedResponse) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.ScheduledEvent != nil { w, err = v.ScheduledEvent.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.StartedTimestamp != nil { w, err = wire.NewValueI64(*(v.StartedTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI64(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ScheduledTimestampOfThisAttempt != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestampOfThisAttempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.HeartbeatDetails != nil { w, err = wire.NewValueBinary(v.HeartbeatDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.WorkflowDomain != nil { w, err = wire.NewValueString(*(v.WorkflowDomain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _HistoryEvent_Read(w wire.Value) (*shared.HistoryEvent, error) { var v shared.HistoryEvent err := v.FromWire(w) return &v, err } // FromWire deserializes a RecordActivityTaskStartedResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RecordActivityTaskStartedResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RecordActivityTaskStartedResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RecordActivityTaskStartedResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 20: if field.Value.Type() == wire.TStruct { v.ScheduledEvent, err = _HistoryEvent_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTimestamp = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Attempt = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestampOfThisAttempt = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.HeartbeatDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowDomain = &x if err != nil { return err } } } } return nil } // Encode serializes a RecordActivityTaskStartedResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RecordActivityTaskStartedResponse struct could not be encoded. func (v *RecordActivityTaskStartedResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ScheduledEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.ScheduledEvent.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestampOfThisAttempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestampOfThisAttempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HeartbeatDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.HeartbeatDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowDomain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowDomain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _HistoryEvent_Decode(sr stream.Reader) (*shared.HistoryEvent, error) { var v shared.HistoryEvent err := v.Decode(sr) return &v, err } // Decode deserializes a RecordActivityTaskStartedResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RecordActivityTaskStartedResponse struct could not be generated from the wire // representation. func (v *RecordActivityTaskStartedResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 20 && fh.Type == wire.TStruct: v.ScheduledEvent, err = _HistoryEvent_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTimestamp = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Attempt = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestampOfThisAttempt = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.HeartbeatDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowDomain = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RecordActivityTaskStartedResponse // struct. func (v *RecordActivityTaskStartedResponse) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.ScheduledEvent != nil { fields[i] = fmt.Sprintf("ScheduledEvent: %v", v.ScheduledEvent) i++ } if v.StartedTimestamp != nil { fields[i] = fmt.Sprintf("StartedTimestamp: %v", *(v.StartedTimestamp)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.ScheduledTimestampOfThisAttempt != nil { fields[i] = fmt.Sprintf("ScheduledTimestampOfThisAttempt: %v", *(v.ScheduledTimestampOfThisAttempt)) i++ } if v.HeartbeatDetails != nil { fields[i] = fmt.Sprintf("HeartbeatDetails: %v", v.HeartbeatDetails) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.WorkflowDomain != nil { fields[i] = fmt.Sprintf("WorkflowDomain: %v", *(v.WorkflowDomain)) i++ } return fmt.Sprintf("RecordActivityTaskStartedResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RecordActivityTaskStartedResponse match the // provided RecordActivityTaskStartedResponse. // // This function performs a deep comparison. func (v *RecordActivityTaskStartedResponse) Equals(rhs *RecordActivityTaskStartedResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ScheduledEvent == nil && rhs.ScheduledEvent == nil) || (v.ScheduledEvent != nil && rhs.ScheduledEvent != nil && v.ScheduledEvent.Equals(rhs.ScheduledEvent))) { return false } if !_I64_EqualsPtr(v.StartedTimestamp, rhs.StartedTimestamp) { return false } if !_I64_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_I64_EqualsPtr(v.ScheduledTimestampOfThisAttempt, rhs.ScheduledTimestampOfThisAttempt) { return false } if !((v.HeartbeatDetails == nil && rhs.HeartbeatDetails == nil) || (v.HeartbeatDetails != nil && rhs.HeartbeatDetails != nil && bytes.Equal(v.HeartbeatDetails, rhs.HeartbeatDetails))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_String_EqualsPtr(v.WorkflowDomain, rhs.WorkflowDomain) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RecordActivityTaskStartedResponse. func (v *RecordActivityTaskStartedResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ScheduledEvent != nil { err = multierr.Append(err, enc.AddObject("scheduledEvent", v.ScheduledEvent)) } if v.StartedTimestamp != nil { enc.AddInt64("startedTimestamp", *v.StartedTimestamp) } if v.Attempt != nil { enc.AddInt64("attempt", *v.Attempt) } if v.ScheduledTimestampOfThisAttempt != nil { enc.AddInt64("scheduledTimestampOfThisAttempt", *v.ScheduledTimestampOfThisAttempt) } if v.HeartbeatDetails != nil { enc.AddString("heartbeatDetails", base64.StdEncoding.EncodeToString(v.HeartbeatDetails)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.WorkflowDomain != nil { enc.AddString("workflowDomain", *v.WorkflowDomain) } return err } // GetScheduledEvent returns the value of ScheduledEvent if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedResponse) GetScheduledEvent() (o *shared.HistoryEvent) { if v != nil && v.ScheduledEvent != nil { return v.ScheduledEvent } return } // IsSetScheduledEvent returns true if ScheduledEvent is not nil. func (v *RecordActivityTaskStartedResponse) IsSetScheduledEvent() bool { return v != nil && v.ScheduledEvent != nil } // GetStartedTimestamp returns the value of StartedTimestamp if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedResponse) GetStartedTimestamp() (o int64) { if v != nil && v.StartedTimestamp != nil { return *v.StartedTimestamp } return } // IsSetStartedTimestamp returns true if StartedTimestamp is not nil. func (v *RecordActivityTaskStartedResponse) IsSetStartedTimestamp() bool { return v != nil && v.StartedTimestamp != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedResponse) GetAttempt() (o int64) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *RecordActivityTaskStartedResponse) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetScheduledTimestampOfThisAttempt returns the value of ScheduledTimestampOfThisAttempt if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedResponse) GetScheduledTimestampOfThisAttempt() (o int64) { if v != nil && v.ScheduledTimestampOfThisAttempt != nil { return *v.ScheduledTimestampOfThisAttempt } return } // IsSetScheduledTimestampOfThisAttempt returns true if ScheduledTimestampOfThisAttempt is not nil. func (v *RecordActivityTaskStartedResponse) IsSetScheduledTimestampOfThisAttempt() bool { return v != nil && v.ScheduledTimestampOfThisAttempt != nil } // GetHeartbeatDetails returns the value of HeartbeatDetails if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedResponse) GetHeartbeatDetails() (o []byte) { if v != nil && v.HeartbeatDetails != nil { return v.HeartbeatDetails } return } // IsSetHeartbeatDetails returns true if HeartbeatDetails is not nil. func (v *RecordActivityTaskStartedResponse) IsSetHeartbeatDetails() bool { return v != nil && v.HeartbeatDetails != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedResponse) GetWorkflowType() (o *shared.WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *RecordActivityTaskStartedResponse) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetWorkflowDomain returns the value of WorkflowDomain if it is set or its // zero value if it is unset. func (v *RecordActivityTaskStartedResponse) GetWorkflowDomain() (o string) { if v != nil && v.WorkflowDomain != nil { return *v.WorkflowDomain } return } // IsSetWorkflowDomain returns true if WorkflowDomain is not nil. func (v *RecordActivityTaskStartedResponse) IsSetWorkflowDomain() bool { return v != nil && v.WorkflowDomain != nil } // RecordChildExecutionCompletedRequest is used for reporting the completion of child execution to parent workflow // execution which started it. When a child execution is completed it creates this request and calls the // RecordChildExecutionCompleted API with the workflowExecution of parent. It also sets the completedExecution of the // child as it could potentially be different than the ChildExecutionStartedEvent of parent in the situation when // child creates multiple runs through ContinueAsNew before finally completing. type RecordChildExecutionCompletedRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` WorkflowExecution *shared.WorkflowExecution `json:"workflowExecution,omitempty"` InitiatedId *int64 `json:"initiatedId,omitempty"` CompletedExecution *shared.WorkflowExecution `json:"completedExecution,omitempty"` CompletionEvent *shared.HistoryEvent `json:"completionEvent,omitempty"` StartedId *int64 `json:"startedId,omitempty"` } // ToWire translates a RecordChildExecutionCompletedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RecordChildExecutionCompletedRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.InitiatedId != nil { w, err = wire.NewValueI64(*(v.InitiatedId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.CompletedExecution != nil { w, err = v.CompletedExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.CompletionEvent != nil { w, err = v.CompletionEvent.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.StartedId != nil { w, err = wire.NewValueI64(*(v.StartedId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RecordChildExecutionCompletedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RecordChildExecutionCompletedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RecordChildExecutionCompletedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RecordChildExecutionCompletedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.CompletedExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.CompletionEvent, err = _HistoryEvent_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedId = &x if err != nil { return err } } } } return nil } // Encode serializes a RecordChildExecutionCompletedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RecordChildExecutionCompletedRequest struct could not be encoded. func (v *RecordChildExecutionCompletedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompletedExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.CompletedExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompletionEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.CompletionEvent.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RecordChildExecutionCompletedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RecordChildExecutionCompletedRequest struct could not be generated from the wire // representation. func (v *RecordChildExecutionCompletedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.CompletedExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.CompletionEvent, err = _HistoryEvent_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RecordChildExecutionCompletedRequest // struct. func (v *RecordChildExecutionCompletedRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.InitiatedId != nil { fields[i] = fmt.Sprintf("InitiatedId: %v", *(v.InitiatedId)) i++ } if v.CompletedExecution != nil { fields[i] = fmt.Sprintf("CompletedExecution: %v", v.CompletedExecution) i++ } if v.CompletionEvent != nil { fields[i] = fmt.Sprintf("CompletionEvent: %v", v.CompletionEvent) i++ } if v.StartedId != nil { fields[i] = fmt.Sprintf("StartedId: %v", *(v.StartedId)) i++ } return fmt.Sprintf("RecordChildExecutionCompletedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RecordChildExecutionCompletedRequest match the // provided RecordChildExecutionCompletedRequest. // // This function performs a deep comparison. func (v *RecordChildExecutionCompletedRequest) Equals(rhs *RecordChildExecutionCompletedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_I64_EqualsPtr(v.InitiatedId, rhs.InitiatedId) { return false } if !((v.CompletedExecution == nil && rhs.CompletedExecution == nil) || (v.CompletedExecution != nil && rhs.CompletedExecution != nil && v.CompletedExecution.Equals(rhs.CompletedExecution))) { return false } if !((v.CompletionEvent == nil && rhs.CompletionEvent == nil) || (v.CompletionEvent != nil && rhs.CompletionEvent != nil && v.CompletionEvent.Equals(rhs.CompletionEvent))) { return false } if !_I64_EqualsPtr(v.StartedId, rhs.StartedId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RecordChildExecutionCompletedRequest. func (v *RecordChildExecutionCompletedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.InitiatedId != nil { enc.AddInt64("initiatedId", *v.InitiatedId) } if v.CompletedExecution != nil { err = multierr.Append(err, enc.AddObject("completedExecution", v.CompletedExecution)) } if v.CompletionEvent != nil { err = multierr.Append(err, enc.AddObject("completionEvent", v.CompletionEvent)) } if v.StartedId != nil { enc.AddInt64("startedId", *v.StartedId) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RecordChildExecutionCompletedRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RecordChildExecutionCompletedRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *RecordChildExecutionCompletedRequest) GetWorkflowExecution() (o *shared.WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *RecordChildExecutionCompletedRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetInitiatedId returns the value of InitiatedId if it is set or its // zero value if it is unset. func (v *RecordChildExecutionCompletedRequest) GetInitiatedId() (o int64) { if v != nil && v.InitiatedId != nil { return *v.InitiatedId } return } // IsSetInitiatedId returns true if InitiatedId is not nil. func (v *RecordChildExecutionCompletedRequest) IsSetInitiatedId() bool { return v != nil && v.InitiatedId != nil } // GetCompletedExecution returns the value of CompletedExecution if it is set or its // zero value if it is unset. func (v *RecordChildExecutionCompletedRequest) GetCompletedExecution() (o *shared.WorkflowExecution) { if v != nil && v.CompletedExecution != nil { return v.CompletedExecution } return } // IsSetCompletedExecution returns true if CompletedExecution is not nil. func (v *RecordChildExecutionCompletedRequest) IsSetCompletedExecution() bool { return v != nil && v.CompletedExecution != nil } // GetCompletionEvent returns the value of CompletionEvent if it is set or its // zero value if it is unset. func (v *RecordChildExecutionCompletedRequest) GetCompletionEvent() (o *shared.HistoryEvent) { if v != nil && v.CompletionEvent != nil { return v.CompletionEvent } return } // IsSetCompletionEvent returns true if CompletionEvent is not nil. func (v *RecordChildExecutionCompletedRequest) IsSetCompletionEvent() bool { return v != nil && v.CompletionEvent != nil } // GetStartedId returns the value of StartedId if it is set or its // zero value if it is unset. func (v *RecordChildExecutionCompletedRequest) GetStartedId() (o int64) { if v != nil && v.StartedId != nil { return *v.StartedId } return } // IsSetStartedId returns true if StartedId is not nil. func (v *RecordChildExecutionCompletedRequest) IsSetStartedId() bool { return v != nil && v.StartedId != nil } type RecordDecisionTaskStartedRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` WorkflowExecution *shared.WorkflowExecution `json:"workflowExecution,omitempty"` ScheduleId *int64 `json:"scheduleId,omitempty"` TaskId *int64 `json:"taskId,omitempty"` RequestId *string `json:"requestId,omitempty"` PollRequest *shared.PollForDecisionTaskRequest `json:"pollRequest,omitempty"` } // ToWire translates a RecordDecisionTaskStartedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RecordDecisionTaskStartedRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ScheduleId != nil { w, err = wire.NewValueI64(*(v.ScheduleId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.TaskId != nil { w, err = wire.NewValueI64(*(v.TaskId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 45, Value: w} i++ } if v.PollRequest != nil { w, err = v.PollRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForDecisionTaskRequest_Read(w wire.Value) (*shared.PollForDecisionTaskRequest, error) { var v shared.PollForDecisionTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RecordDecisionTaskStartedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RecordDecisionTaskStartedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RecordDecisionTaskStartedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RecordDecisionTaskStartedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduleId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskId = &x if err != nil { return err } } case 45: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.PollRequest, err = _PollForDecisionTaskRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RecordDecisionTaskStartedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RecordDecisionTaskStartedRequest struct could not be encoded. func (v *RecordDecisionTaskStartedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduleId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 45, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PollRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.PollRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollForDecisionTaskRequest_Decode(sr stream.Reader) (*shared.PollForDecisionTaskRequest, error) { var v shared.PollForDecisionTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RecordDecisionTaskStartedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RecordDecisionTaskStartedRequest struct could not be generated from the wire // representation. func (v *RecordDecisionTaskStartedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduleId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskId = &x if err != nil { return err } case fh.ID == 45 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.PollRequest, err = _PollForDecisionTaskRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RecordDecisionTaskStartedRequest // struct. func (v *RecordDecisionTaskStartedRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.ScheduleId != nil { fields[i] = fmt.Sprintf("ScheduleId: %v", *(v.ScheduleId)) i++ } if v.TaskId != nil { fields[i] = fmt.Sprintf("TaskId: %v", *(v.TaskId)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } if v.PollRequest != nil { fields[i] = fmt.Sprintf("PollRequest: %v", v.PollRequest) i++ } return fmt.Sprintf("RecordDecisionTaskStartedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RecordDecisionTaskStartedRequest match the // provided RecordDecisionTaskStartedRequest. // // This function performs a deep comparison. func (v *RecordDecisionTaskStartedRequest) Equals(rhs *RecordDecisionTaskStartedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_I64_EqualsPtr(v.ScheduleId, rhs.ScheduleId) { return false } if !_I64_EqualsPtr(v.TaskId, rhs.TaskId) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } if !((v.PollRequest == nil && rhs.PollRequest == nil) || (v.PollRequest != nil && rhs.PollRequest != nil && v.PollRequest.Equals(rhs.PollRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RecordDecisionTaskStartedRequest. func (v *RecordDecisionTaskStartedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.ScheduleId != nil { enc.AddInt64("scheduleId", *v.ScheduleId) } if v.TaskId != nil { enc.AddInt64("taskId", *v.TaskId) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } if v.PollRequest != nil { err = multierr.Append(err, enc.AddObject("pollRequest", v.PollRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RecordDecisionTaskStartedRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedRequest) GetWorkflowExecution() (o *shared.WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *RecordDecisionTaskStartedRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetScheduleId returns the value of ScheduleId if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedRequest) GetScheduleId() (o int64) { if v != nil && v.ScheduleId != nil { return *v.ScheduleId } return } // IsSetScheduleId returns true if ScheduleId is not nil. func (v *RecordDecisionTaskStartedRequest) IsSetScheduleId() bool { return v != nil && v.ScheduleId != nil } // GetTaskId returns the value of TaskId if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedRequest) GetTaskId() (o int64) { if v != nil && v.TaskId != nil { return *v.TaskId } return } // IsSetTaskId returns true if TaskId is not nil. func (v *RecordDecisionTaskStartedRequest) IsSetTaskId() bool { return v != nil && v.TaskId != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedRequest) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *RecordDecisionTaskStartedRequest) IsSetRequestId() bool { return v != nil && v.RequestId != nil } // GetPollRequest returns the value of PollRequest if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedRequest) GetPollRequest() (o *shared.PollForDecisionTaskRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // IsSetPollRequest returns true if PollRequest is not nil. func (v *RecordDecisionTaskStartedRequest) IsSetPollRequest() bool { return v != nil && v.PollRequest != nil } type RecordDecisionTaskStartedResponse struct { WorkflowType *shared.WorkflowType `json:"workflowType,omitempty"` PreviousStartedEventId *int64 `json:"previousStartedEventId,omitempty"` ScheduledEventId *int64 `json:"scheduledEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` NextEventId *int64 `json:"nextEventId,omitempty"` Attempt *int64 `json:"attempt,omitempty"` StickyExecutionEnabled *bool `json:"stickyExecutionEnabled,omitempty"` DecisionInfo *shared.TransientDecisionInfo `json:"decisionInfo,omitempty"` WorkflowExecutionTaskList *shared.TaskList `json:"WorkflowExecutionTaskList,omitempty"` EventStoreVersion *int32 `json:"eventStoreVersion,omitempty"` BranchToken []byte `json:"branchToken,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Queries map[string]*shared.WorkflowQuery `json:"queries,omitempty"` HistorySize *int64 `json:"historySize,omitempty"` } type _Map_String_WorkflowQuery_MapItemList map[string]*shared.WorkflowQuery func (m _Map_String_WorkflowQuery_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*shared.WorkflowQuery', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_WorkflowQuery_MapItemList) Size() int { return len(m) } func (_Map_String_WorkflowQuery_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_WorkflowQuery_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_WorkflowQuery_MapItemList) Close() {} // ToWire translates a RecordDecisionTaskStartedResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RecordDecisionTaskStartedResponse) ToWire() (wire.Value, error) { var ( fields [15]wire.Field i int = 0 w wire.Value err error ) if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.PreviousStartedEventId != nil { w, err = wire.NewValueI64(*(v.PreviousStartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ScheduledEventId != nil { w, err = wire.NewValueI64(*(v.ScheduledEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.NextEventId != nil { w, err = wire.NewValueI64(*(v.NextEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI64(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.StickyExecutionEnabled != nil { w, err = wire.NewValueBool(*(v.StickyExecutionEnabled)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.DecisionInfo != nil { w, err = v.DecisionInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.WorkflowExecutionTaskList != nil { w, err = v.WorkflowExecutionTaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.EventStoreVersion != nil { w, err = wire.NewValueI32(*(v.EventStoreVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.BranchToken != nil { w, err = wire.NewValueBinary(v.BranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.ScheduledTimestamp != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.StartedTimestamp != nil { w, err = wire.NewValueI64(*(v.StartedTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.Queries != nil { w, err = wire.NewValueMap(_Map_String_WorkflowQuery_MapItemList(v.Queries)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.HistorySize != nil { w, err = wire.NewValueI64(*(v.HistorySize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TransientDecisionInfo_Read(w wire.Value) (*shared.TransientDecisionInfo, error) { var v shared.TransientDecisionInfo err := v.FromWire(w) return &v, err } func _WorkflowQuery_Read(w wire.Value) (*shared.WorkflowQuery, error) { var v shared.WorkflowQuery err := v.FromWire(w) return &v, err } func _Map_String_WorkflowQuery_Read(m wire.MapItemList) (map[string]*shared.WorkflowQuery, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*shared.WorkflowQuery, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _WorkflowQuery_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a RecordDecisionTaskStartedResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RecordDecisionTaskStartedResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RecordDecisionTaskStartedResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RecordDecisionTaskStartedResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.PreviousStartedEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NextEventId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Attempt = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.StickyExecutionEnabled = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TStruct { v.DecisionInfo, err = _TransientDecisionInfo_Read(field.Value) if err != nil { return err } } case 90: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionTaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 100: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.EventStoreVersion = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TBinary { v.BranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 120: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestamp = &x if err != nil { return err } } case 130: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTimestamp = &x if err != nil { return err } } case 140: if field.Value.Type() == wire.TMap { v.Queries, err = _Map_String_WorkflowQuery_Read(field.Value.GetMap()) if err != nil { return err } } case 150: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.HistorySize = &x if err != nil { return err } } } } return nil } func _Map_String_WorkflowQuery_Encode(val map[string]*shared.WorkflowQuery, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*shared.WorkflowQuery', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a RecordDecisionTaskStartedResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RecordDecisionTaskStartedResponse struct could not be encoded. func (v *RecordDecisionTaskStartedResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PreviousStartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.PreviousStartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NextEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyExecutionEnabled != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.StickyExecutionEnabled)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TStruct}); err != nil { return err } if err := v.DecisionInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionTaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionTaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EventStoreVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.EventStoreVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BranchToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.BranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Queries != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TMap}); err != nil { return err } if err := _Map_String_WorkflowQuery_Encode(v.Queries, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistorySize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.HistorySize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TransientDecisionInfo_Decode(sr stream.Reader) (*shared.TransientDecisionInfo, error) { var v shared.TransientDecisionInfo err := v.Decode(sr) return &v, err } func _WorkflowQuery_Decode(sr stream.Reader) (*shared.WorkflowQuery, error) { var v shared.WorkflowQuery err := v.Decode(sr) return &v, err } func _Map_String_WorkflowQuery_Decode(sr stream.Reader) (map[string]*shared.WorkflowQuery, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*shared.WorkflowQuery, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _WorkflowQuery_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a RecordDecisionTaskStartedResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RecordDecisionTaskStartedResponse struct could not be generated from the wire // representation. func (v *RecordDecisionTaskStartedResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.PreviousStartedEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NextEventId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Attempt = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.StickyExecutionEnabled = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TStruct: v.DecisionInfo, err = _TransientDecisionInfo_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TStruct: v.WorkflowExecutionTaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.EventStoreVersion = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TBinary: v.BranchToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestamp = &x if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTimestamp = &x if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TMap: v.Queries, err = _Map_String_WorkflowQuery_Decode(sr) if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.HistorySize = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RecordDecisionTaskStartedResponse // struct. func (v *RecordDecisionTaskStartedResponse) String() string { if v == nil { return "" } var fields [15]string i := 0 if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.PreviousStartedEventId != nil { fields[i] = fmt.Sprintf("PreviousStartedEventId: %v", *(v.PreviousStartedEventId)) i++ } if v.ScheduledEventId != nil { fields[i] = fmt.Sprintf("ScheduledEventId: %v", *(v.ScheduledEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.NextEventId != nil { fields[i] = fmt.Sprintf("NextEventId: %v", *(v.NextEventId)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.StickyExecutionEnabled != nil { fields[i] = fmt.Sprintf("StickyExecutionEnabled: %v", *(v.StickyExecutionEnabled)) i++ } if v.DecisionInfo != nil { fields[i] = fmt.Sprintf("DecisionInfo: %v", v.DecisionInfo) i++ } if v.WorkflowExecutionTaskList != nil { fields[i] = fmt.Sprintf("WorkflowExecutionTaskList: %v", v.WorkflowExecutionTaskList) i++ } if v.EventStoreVersion != nil { fields[i] = fmt.Sprintf("EventStoreVersion: %v", *(v.EventStoreVersion)) i++ } if v.BranchToken != nil { fields[i] = fmt.Sprintf("BranchToken: %v", v.BranchToken) i++ } if v.ScheduledTimestamp != nil { fields[i] = fmt.Sprintf("ScheduledTimestamp: %v", *(v.ScheduledTimestamp)) i++ } if v.StartedTimestamp != nil { fields[i] = fmt.Sprintf("StartedTimestamp: %v", *(v.StartedTimestamp)) i++ } if v.Queries != nil { fields[i] = fmt.Sprintf("Queries: %v", v.Queries) i++ } if v.HistorySize != nil { fields[i] = fmt.Sprintf("HistorySize: %v", *(v.HistorySize)) i++ } return fmt.Sprintf("RecordDecisionTaskStartedResponse{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_WorkflowQuery_Equals(lhs, rhs map[string]*shared.WorkflowQuery) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this RecordDecisionTaskStartedResponse match the // provided RecordDecisionTaskStartedResponse. // // This function performs a deep comparison. func (v *RecordDecisionTaskStartedResponse) Equals(rhs *RecordDecisionTaskStartedResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_I64_EqualsPtr(v.PreviousStartedEventId, rhs.PreviousStartedEventId) { return false } if !_I64_EqualsPtr(v.ScheduledEventId, rhs.ScheduledEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_I64_EqualsPtr(v.NextEventId, rhs.NextEventId) { return false } if !_I64_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_Bool_EqualsPtr(v.StickyExecutionEnabled, rhs.StickyExecutionEnabled) { return false } if !((v.DecisionInfo == nil && rhs.DecisionInfo == nil) || (v.DecisionInfo != nil && rhs.DecisionInfo != nil && v.DecisionInfo.Equals(rhs.DecisionInfo))) { return false } if !((v.WorkflowExecutionTaskList == nil && rhs.WorkflowExecutionTaskList == nil) || (v.WorkflowExecutionTaskList != nil && rhs.WorkflowExecutionTaskList != nil && v.WorkflowExecutionTaskList.Equals(rhs.WorkflowExecutionTaskList))) { return false } if !_I32_EqualsPtr(v.EventStoreVersion, rhs.EventStoreVersion) { return false } if !((v.BranchToken == nil && rhs.BranchToken == nil) || (v.BranchToken != nil && rhs.BranchToken != nil && bytes.Equal(v.BranchToken, rhs.BranchToken))) { return false } if !_I64_EqualsPtr(v.ScheduledTimestamp, rhs.ScheduledTimestamp) { return false } if !_I64_EqualsPtr(v.StartedTimestamp, rhs.StartedTimestamp) { return false } if !((v.Queries == nil && rhs.Queries == nil) || (v.Queries != nil && rhs.Queries != nil && _Map_String_WorkflowQuery_Equals(v.Queries, rhs.Queries))) { return false } if !_I64_EqualsPtr(v.HistorySize, rhs.HistorySize) { return false } return true } type _Map_String_WorkflowQuery_Zapper map[string]*shared.WorkflowQuery // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_WorkflowQuery_Zapper. func (m _Map_String_WorkflowQuery_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RecordDecisionTaskStartedResponse. func (v *RecordDecisionTaskStartedResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.PreviousStartedEventId != nil { enc.AddInt64("previousStartedEventId", *v.PreviousStartedEventId) } if v.ScheduledEventId != nil { enc.AddInt64("scheduledEventId", *v.ScheduledEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.NextEventId != nil { enc.AddInt64("nextEventId", *v.NextEventId) } if v.Attempt != nil { enc.AddInt64("attempt", *v.Attempt) } if v.StickyExecutionEnabled != nil { enc.AddBool("stickyExecutionEnabled", *v.StickyExecutionEnabled) } if v.DecisionInfo != nil { err = multierr.Append(err, enc.AddObject("decisionInfo", v.DecisionInfo)) } if v.WorkflowExecutionTaskList != nil { err = multierr.Append(err, enc.AddObject("WorkflowExecutionTaskList", v.WorkflowExecutionTaskList)) } if v.EventStoreVersion != nil { enc.AddInt32("eventStoreVersion", *v.EventStoreVersion) } if v.BranchToken != nil { enc.AddString("branchToken", base64.StdEncoding.EncodeToString(v.BranchToken)) } if v.ScheduledTimestamp != nil { enc.AddInt64("scheduledTimestamp", *v.ScheduledTimestamp) } if v.StartedTimestamp != nil { enc.AddInt64("startedTimestamp", *v.StartedTimestamp) } if v.Queries != nil { err = multierr.Append(err, enc.AddObject("queries", (_Map_String_WorkflowQuery_Zapper)(v.Queries))) } if v.HistorySize != nil { enc.AddInt64("historySize", *v.HistorySize) } return err } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetWorkflowType() (o *shared.WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetPreviousStartedEventId returns the value of PreviousStartedEventId if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetPreviousStartedEventId() (o int64) { if v != nil && v.PreviousStartedEventId != nil { return *v.PreviousStartedEventId } return } // IsSetPreviousStartedEventId returns true if PreviousStartedEventId is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetPreviousStartedEventId() bool { return v != nil && v.PreviousStartedEventId != nil } // GetScheduledEventId returns the value of ScheduledEventId if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetScheduledEventId() (o int64) { if v != nil && v.ScheduledEventId != nil { return *v.ScheduledEventId } return } // IsSetScheduledEventId returns true if ScheduledEventId is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetScheduledEventId() bool { return v != nil && v.ScheduledEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetNextEventId returns the value of NextEventId if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetNextEventId() (o int64) { if v != nil && v.NextEventId != nil { return *v.NextEventId } return } // IsSetNextEventId returns true if NextEventId is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetNextEventId() bool { return v != nil && v.NextEventId != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetAttempt() (o int64) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetStickyExecutionEnabled returns the value of StickyExecutionEnabled if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetStickyExecutionEnabled() (o bool) { if v != nil && v.StickyExecutionEnabled != nil { return *v.StickyExecutionEnabled } return } // IsSetStickyExecutionEnabled returns true if StickyExecutionEnabled is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetStickyExecutionEnabled() bool { return v != nil && v.StickyExecutionEnabled != nil } // GetDecisionInfo returns the value of DecisionInfo if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetDecisionInfo() (o *shared.TransientDecisionInfo) { if v != nil && v.DecisionInfo != nil { return v.DecisionInfo } return } // IsSetDecisionInfo returns true if DecisionInfo is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetDecisionInfo() bool { return v != nil && v.DecisionInfo != nil } // GetWorkflowExecutionTaskList returns the value of WorkflowExecutionTaskList if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetWorkflowExecutionTaskList() (o *shared.TaskList) { if v != nil && v.WorkflowExecutionTaskList != nil { return v.WorkflowExecutionTaskList } return } // IsSetWorkflowExecutionTaskList returns true if WorkflowExecutionTaskList is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetWorkflowExecutionTaskList() bool { return v != nil && v.WorkflowExecutionTaskList != nil } // GetEventStoreVersion returns the value of EventStoreVersion if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetEventStoreVersion() (o int32) { if v != nil && v.EventStoreVersion != nil { return *v.EventStoreVersion } return } // IsSetEventStoreVersion returns true if EventStoreVersion is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetEventStoreVersion() bool { return v != nil && v.EventStoreVersion != nil } // GetBranchToken returns the value of BranchToken if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetBranchToken() (o []byte) { if v != nil && v.BranchToken != nil { return v.BranchToken } return } // IsSetBranchToken returns true if BranchToken is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetBranchToken() bool { return v != nil && v.BranchToken != nil } // GetScheduledTimestamp returns the value of ScheduledTimestamp if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetScheduledTimestamp() (o int64) { if v != nil && v.ScheduledTimestamp != nil { return *v.ScheduledTimestamp } return } // IsSetScheduledTimestamp returns true if ScheduledTimestamp is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetScheduledTimestamp() bool { return v != nil && v.ScheduledTimestamp != nil } // GetStartedTimestamp returns the value of StartedTimestamp if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetStartedTimestamp() (o int64) { if v != nil && v.StartedTimestamp != nil { return *v.StartedTimestamp } return } // IsSetStartedTimestamp returns true if StartedTimestamp is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetStartedTimestamp() bool { return v != nil && v.StartedTimestamp != nil } // GetQueries returns the value of Queries if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetQueries() (o map[string]*shared.WorkflowQuery) { if v != nil && v.Queries != nil { return v.Queries } return } // IsSetQueries returns true if Queries is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetQueries() bool { return v != nil && v.Queries != nil } // GetHistorySize returns the value of HistorySize if it is set or its // zero value if it is unset. func (v *RecordDecisionTaskStartedResponse) GetHistorySize() (o int64) { if v != nil && v.HistorySize != nil { return *v.HistorySize } return } // IsSetHistorySize returns true if HistorySize is not nil. func (v *RecordDecisionTaskStartedResponse) IsSetHistorySize() bool { return v != nil && v.HistorySize != nil } type RefreshWorkflowTasksRequest struct { DomainUIID *string `json:"domainUIID,omitempty"` Request *shared.RefreshWorkflowTasksRequest `json:"request,omitempty"` } // ToWire translates a RefreshWorkflowTasksRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RefreshWorkflowTasksRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUIID != nil { w, err = wire.NewValueString(*(v.DomainUIID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RefreshWorkflowTasksRequest_Read(w wire.Value) (*shared.RefreshWorkflowTasksRequest, error) { var v shared.RefreshWorkflowTasksRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RefreshWorkflowTasksRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RefreshWorkflowTasksRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RefreshWorkflowTasksRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RefreshWorkflowTasksRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUIID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Request, err = _RefreshWorkflowTasksRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RefreshWorkflowTasksRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RefreshWorkflowTasksRequest struct could not be encoded. func (v *RefreshWorkflowTasksRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUIID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUIID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RefreshWorkflowTasksRequest_Decode(sr stream.Reader) (*shared.RefreshWorkflowTasksRequest, error) { var v shared.RefreshWorkflowTasksRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RefreshWorkflowTasksRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RefreshWorkflowTasksRequest struct could not be generated from the wire // representation. func (v *RefreshWorkflowTasksRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUIID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Request, err = _RefreshWorkflowTasksRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RefreshWorkflowTasksRequest // struct. func (v *RefreshWorkflowTasksRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUIID != nil { fields[i] = fmt.Sprintf("DomainUIID: %v", *(v.DomainUIID)) i++ } if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("RefreshWorkflowTasksRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RefreshWorkflowTasksRequest match the // provided RefreshWorkflowTasksRequest. // // This function performs a deep comparison. func (v *RefreshWorkflowTasksRequest) Equals(rhs *RefreshWorkflowTasksRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUIID, rhs.DomainUIID) { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RefreshWorkflowTasksRequest. func (v *RefreshWorkflowTasksRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUIID != nil { enc.AddString("domainUIID", *v.DomainUIID) } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetDomainUIID returns the value of DomainUIID if it is set or its // zero value if it is unset. func (v *RefreshWorkflowTasksRequest) GetDomainUIID() (o string) { if v != nil && v.DomainUIID != nil { return *v.DomainUIID } return } // IsSetDomainUIID returns true if DomainUIID is not nil. func (v *RefreshWorkflowTasksRequest) IsSetDomainUIID() bool { return v != nil && v.DomainUIID != nil } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *RefreshWorkflowTasksRequest) GetRequest() (o *shared.RefreshWorkflowTasksRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *RefreshWorkflowTasksRequest) IsSetRequest() bool { return v != nil && v.Request != nil } type RemoveSignalMutableStateRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` WorkflowExecution *shared.WorkflowExecution `json:"workflowExecution,omitempty"` RequestId *string `json:"requestId,omitempty"` } // ToWire translates a RemoveSignalMutableStateRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RemoveSignalMutableStateRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RemoveSignalMutableStateRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RemoveSignalMutableStateRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RemoveSignalMutableStateRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RemoveSignalMutableStateRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } } } return nil } // Encode serializes a RemoveSignalMutableStateRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RemoveSignalMutableStateRequest struct could not be encoded. func (v *RemoveSignalMutableStateRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RemoveSignalMutableStateRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RemoveSignalMutableStateRequest struct could not be generated from the wire // representation. func (v *RemoveSignalMutableStateRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RemoveSignalMutableStateRequest // struct. func (v *RemoveSignalMutableStateRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } return fmt.Sprintf("RemoveSignalMutableStateRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RemoveSignalMutableStateRequest match the // provided RemoveSignalMutableStateRequest. // // This function performs a deep comparison. func (v *RemoveSignalMutableStateRequest) Equals(rhs *RemoveSignalMutableStateRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RemoveSignalMutableStateRequest. func (v *RemoveSignalMutableStateRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RemoveSignalMutableStateRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RemoveSignalMutableStateRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *RemoveSignalMutableStateRequest) GetWorkflowExecution() (o *shared.WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *RemoveSignalMutableStateRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *RemoveSignalMutableStateRequest) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *RemoveSignalMutableStateRequest) IsSetRequestId() bool { return v != nil && v.RequestId != nil } type ReplicateEventsV2Request struct { DomainUUID *string `json:"domainUUID,omitempty"` WorkflowExecution *shared.WorkflowExecution `json:"workflowExecution,omitempty"` VersionHistoryItems []*shared.VersionHistoryItem `json:"versionHistoryItems,omitempty"` Events *shared.DataBlob `json:"events,omitempty"` NewRunEvents *shared.DataBlob `json:"newRunEvents,omitempty"` } type _List_VersionHistoryItem_ValueList []*shared.VersionHistoryItem func (v _List_VersionHistoryItem_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*shared.VersionHistoryItem', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_VersionHistoryItem_ValueList) Size() int { return len(v) } func (_List_VersionHistoryItem_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_VersionHistoryItem_ValueList) Close() {} // ToWire translates a ReplicateEventsV2Request struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReplicateEventsV2Request) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.VersionHistoryItems != nil { w, err = wire.NewValueList(_List_VersionHistoryItem_ValueList(v.VersionHistoryItems)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Events != nil { w, err = v.Events.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.NewRunEvents != nil { w, err = v.NewRunEvents.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _List_VersionHistoryItem_Read(l wire.ValueList) ([]*shared.VersionHistoryItem, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*shared.VersionHistoryItem, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _VersionHistoryItem_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _DataBlob_Read(w wire.Value) (*shared.DataBlob, error) { var v shared.DataBlob err := v.FromWire(w) return &v, err } // FromWire deserializes a ReplicateEventsV2Request struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReplicateEventsV2Request struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReplicateEventsV2Request // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReplicateEventsV2Request) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TList { v.VersionHistoryItems, err = _List_VersionHistoryItem_Read(field.Value.GetList()) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.Events, err = _DataBlob_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.NewRunEvents, err = _DataBlob_Read(field.Value) if err != nil { return err } } } } return nil } func _List_VersionHistoryItem_Encode(val []*shared.VersionHistoryItem, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*shared.VersionHistoryItem', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a ReplicateEventsV2Request struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReplicateEventsV2Request struct could not be encoded. func (v *ReplicateEventsV2Request) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistoryItems != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TList}); err != nil { return err } if err := _List_VersionHistoryItem_Encode(v.VersionHistoryItems, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Events != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.Events.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NewRunEvents != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.NewRunEvents.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _List_VersionHistoryItem_Decode(sr stream.Reader) ([]*shared.VersionHistoryItem, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*shared.VersionHistoryItem, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _VersionHistoryItem_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _DataBlob_Decode(sr stream.Reader) (*shared.DataBlob, error) { var v shared.DataBlob err := v.Decode(sr) return &v, err } // Decode deserializes a ReplicateEventsV2Request struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReplicateEventsV2Request struct could not be generated from the wire // representation. func (v *ReplicateEventsV2Request) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TList: v.VersionHistoryItems, err = _List_VersionHistoryItem_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.Events, err = _DataBlob_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.NewRunEvents, err = _DataBlob_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReplicateEventsV2Request // struct. func (v *ReplicateEventsV2Request) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.VersionHistoryItems != nil { fields[i] = fmt.Sprintf("VersionHistoryItems: %v", v.VersionHistoryItems) i++ } if v.Events != nil { fields[i] = fmt.Sprintf("Events: %v", v.Events) i++ } if v.NewRunEvents != nil { fields[i] = fmt.Sprintf("NewRunEvents: %v", v.NewRunEvents) i++ } return fmt.Sprintf("ReplicateEventsV2Request{%v}", strings.Join(fields[:i], ", ")) } func _List_VersionHistoryItem_Equals(lhs, rhs []*shared.VersionHistoryItem) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this ReplicateEventsV2Request match the // provided ReplicateEventsV2Request. // // This function performs a deep comparison. func (v *ReplicateEventsV2Request) Equals(rhs *ReplicateEventsV2Request) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.VersionHistoryItems == nil && rhs.VersionHistoryItems == nil) || (v.VersionHistoryItems != nil && rhs.VersionHistoryItems != nil && _List_VersionHistoryItem_Equals(v.VersionHistoryItems, rhs.VersionHistoryItems))) { return false } if !((v.Events == nil && rhs.Events == nil) || (v.Events != nil && rhs.Events != nil && v.Events.Equals(rhs.Events))) { return false } if !((v.NewRunEvents == nil && rhs.NewRunEvents == nil) || (v.NewRunEvents != nil && rhs.NewRunEvents != nil && v.NewRunEvents.Equals(rhs.NewRunEvents))) { return false } return true } type _List_VersionHistoryItem_Zapper []*shared.VersionHistoryItem // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_VersionHistoryItem_Zapper. func (l _List_VersionHistoryItem_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReplicateEventsV2Request. func (v *ReplicateEventsV2Request) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.VersionHistoryItems != nil { err = multierr.Append(err, enc.AddArray("versionHistoryItems", (_List_VersionHistoryItem_Zapper)(v.VersionHistoryItems))) } if v.Events != nil { err = multierr.Append(err, enc.AddObject("events", v.Events)) } if v.NewRunEvents != nil { err = multierr.Append(err, enc.AddObject("newRunEvents", v.NewRunEvents)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *ReplicateEventsV2Request) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *ReplicateEventsV2Request) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ReplicateEventsV2Request) GetWorkflowExecution() (o *shared.WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ReplicateEventsV2Request) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetVersionHistoryItems returns the value of VersionHistoryItems if it is set or its // zero value if it is unset. func (v *ReplicateEventsV2Request) GetVersionHistoryItems() (o []*shared.VersionHistoryItem) { if v != nil && v.VersionHistoryItems != nil { return v.VersionHistoryItems } return } // IsSetVersionHistoryItems returns true if VersionHistoryItems is not nil. func (v *ReplicateEventsV2Request) IsSetVersionHistoryItems() bool { return v != nil && v.VersionHistoryItems != nil } // GetEvents returns the value of Events if it is set or its // zero value if it is unset. func (v *ReplicateEventsV2Request) GetEvents() (o *shared.DataBlob) { if v != nil && v.Events != nil { return v.Events } return } // IsSetEvents returns true if Events is not nil. func (v *ReplicateEventsV2Request) IsSetEvents() bool { return v != nil && v.Events != nil } // GetNewRunEvents returns the value of NewRunEvents if it is set or its // zero value if it is unset. func (v *ReplicateEventsV2Request) GetNewRunEvents() (o *shared.DataBlob) { if v != nil && v.NewRunEvents != nil { return v.NewRunEvents } return } // IsSetNewRunEvents returns true if NewRunEvents is not nil. func (v *ReplicateEventsV2Request) IsSetNewRunEvents() bool { return v != nil && v.NewRunEvents != nil } type RequestCancelWorkflowExecutionRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` CancelRequest *shared.RequestCancelWorkflowExecutionRequest `json:"cancelRequest,omitempty"` ExternalInitiatedEventId *int64 `json:"externalInitiatedEventId,omitempty"` ExternalWorkflowExecution *shared.WorkflowExecution `json:"externalWorkflowExecution,omitempty"` ChildWorkflowOnly *bool `json:"childWorkflowOnly,omitempty"` } // ToWire translates a RequestCancelWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RequestCancelWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.CancelRequest != nil { w, err = v.CancelRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ExternalInitiatedEventId != nil { w, err = wire.NewValueI64(*(v.ExternalInitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ExternalWorkflowExecution != nil { w, err = v.ExternalWorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ChildWorkflowOnly != nil { w, err = wire.NewValueBool(*(v.ChildWorkflowOnly)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RequestCancelWorkflowExecutionRequest_Read(w wire.Value) (*shared.RequestCancelWorkflowExecutionRequest, error) { var v shared.RequestCancelWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RequestCancelWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RequestCancelWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RequestCancelWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RequestCancelWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.CancelRequest, err = _RequestCancelWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExternalInitiatedEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.ExternalWorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ChildWorkflowOnly = &x if err != nil { return err } } } } return nil } // Encode serializes a RequestCancelWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RequestCancelWorkflowExecutionRequest struct could not be encoded. func (v *RequestCancelWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.CancelRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExternalInitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExternalInitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExternalWorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.ExternalWorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowOnly != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ChildWorkflowOnly)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RequestCancelWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.RequestCancelWorkflowExecutionRequest, error) { var v shared.RequestCancelWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RequestCancelWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RequestCancelWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *RequestCancelWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.CancelRequest, err = _RequestCancelWorkflowExecutionRequest_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExternalInitiatedEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.ExternalWorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ChildWorkflowOnly = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RequestCancelWorkflowExecutionRequest // struct. func (v *RequestCancelWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.CancelRequest != nil { fields[i] = fmt.Sprintf("CancelRequest: %v", v.CancelRequest) i++ } if v.ExternalInitiatedEventId != nil { fields[i] = fmt.Sprintf("ExternalInitiatedEventId: %v", *(v.ExternalInitiatedEventId)) i++ } if v.ExternalWorkflowExecution != nil { fields[i] = fmt.Sprintf("ExternalWorkflowExecution: %v", v.ExternalWorkflowExecution) i++ } if v.ChildWorkflowOnly != nil { fields[i] = fmt.Sprintf("ChildWorkflowOnly: %v", *(v.ChildWorkflowOnly)) i++ } return fmt.Sprintf("RequestCancelWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RequestCancelWorkflowExecutionRequest match the // provided RequestCancelWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *RequestCancelWorkflowExecutionRequest) Equals(rhs *RequestCancelWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.CancelRequest == nil && rhs.CancelRequest == nil) || (v.CancelRequest != nil && rhs.CancelRequest != nil && v.CancelRequest.Equals(rhs.CancelRequest))) { return false } if !_I64_EqualsPtr(v.ExternalInitiatedEventId, rhs.ExternalInitiatedEventId) { return false } if !((v.ExternalWorkflowExecution == nil && rhs.ExternalWorkflowExecution == nil) || (v.ExternalWorkflowExecution != nil && rhs.ExternalWorkflowExecution != nil && v.ExternalWorkflowExecution.Equals(rhs.ExternalWorkflowExecution))) { return false } if !_Bool_EqualsPtr(v.ChildWorkflowOnly, rhs.ChildWorkflowOnly) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RequestCancelWorkflowExecutionRequest. func (v *RequestCancelWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.CancelRequest != nil { err = multierr.Append(err, enc.AddObject("cancelRequest", v.CancelRequest)) } if v.ExternalInitiatedEventId != nil { enc.AddInt64("externalInitiatedEventId", *v.ExternalInitiatedEventId) } if v.ExternalWorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("externalWorkflowExecution", v.ExternalWorkflowExecution)) } if v.ChildWorkflowOnly != nil { enc.AddBool("childWorkflowOnly", *v.ChildWorkflowOnly) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetCancelRequest returns the value of CancelRequest if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetCancelRequest() (o *shared.RequestCancelWorkflowExecutionRequest) { if v != nil && v.CancelRequest != nil { return v.CancelRequest } return } // IsSetCancelRequest returns true if CancelRequest is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetCancelRequest() bool { return v != nil && v.CancelRequest != nil } // GetExternalInitiatedEventId returns the value of ExternalInitiatedEventId if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetExternalInitiatedEventId() (o int64) { if v != nil && v.ExternalInitiatedEventId != nil { return *v.ExternalInitiatedEventId } return } // IsSetExternalInitiatedEventId returns true if ExternalInitiatedEventId is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetExternalInitiatedEventId() bool { return v != nil && v.ExternalInitiatedEventId != nil } // GetExternalWorkflowExecution returns the value of ExternalWorkflowExecution if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetExternalWorkflowExecution() (o *shared.WorkflowExecution) { if v != nil && v.ExternalWorkflowExecution != nil { return v.ExternalWorkflowExecution } return } // IsSetExternalWorkflowExecution returns true if ExternalWorkflowExecution is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetExternalWorkflowExecution() bool { return v != nil && v.ExternalWorkflowExecution != nil } // GetChildWorkflowOnly returns the value of ChildWorkflowOnly if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetChildWorkflowOnly() (o bool) { if v != nil && v.ChildWorkflowOnly != nil { return *v.ChildWorkflowOnly } return } // IsSetChildWorkflowOnly returns true if ChildWorkflowOnly is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetChildWorkflowOnly() bool { return v != nil && v.ChildWorkflowOnly != nil } type ResetStickyTaskListRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` } // ToWire translates a ResetStickyTaskListRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResetStickyTaskListRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ResetStickyTaskListRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResetStickyTaskListRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResetStickyTaskListRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResetStickyTaskListRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ResetStickyTaskListRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResetStickyTaskListRequest struct could not be encoded. func (v *ResetStickyTaskListRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ResetStickyTaskListRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResetStickyTaskListRequest struct could not be generated from the wire // representation. func (v *ResetStickyTaskListRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResetStickyTaskListRequest // struct. func (v *ResetStickyTaskListRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } return fmt.Sprintf("ResetStickyTaskListRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ResetStickyTaskListRequest match the // provided ResetStickyTaskListRequest. // // This function performs a deep comparison. func (v *ResetStickyTaskListRequest) Equals(rhs *ResetStickyTaskListRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResetStickyTaskListRequest. func (v *ResetStickyTaskListRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *ResetStickyTaskListRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *ResetStickyTaskListRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *ResetStickyTaskListRequest) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *ResetStickyTaskListRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } type ResetStickyTaskListResponse struct { } // ToWire translates a ResetStickyTaskListResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResetStickyTaskListResponse) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ResetStickyTaskListResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResetStickyTaskListResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResetStickyTaskListResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResetStickyTaskListResponse) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a ResetStickyTaskListResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResetStickyTaskListResponse struct could not be encoded. func (v *ResetStickyTaskListResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a ResetStickyTaskListResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResetStickyTaskListResponse struct could not be generated from the wire // representation. func (v *ResetStickyTaskListResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResetStickyTaskListResponse // struct. func (v *ResetStickyTaskListResponse) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("ResetStickyTaskListResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ResetStickyTaskListResponse match the // provided ResetStickyTaskListResponse. // // This function performs a deep comparison. func (v *ResetStickyTaskListResponse) Equals(rhs *ResetStickyTaskListResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResetStickyTaskListResponse. func (v *ResetStickyTaskListResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type ResetWorkflowExecutionRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` ResetRequest *shared.ResetWorkflowExecutionRequest `json:"resetRequest,omitempty"` } // ToWire translates a ResetWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResetWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ResetRequest != nil { w, err = v.ResetRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetWorkflowExecutionRequest_Read(w wire.Value) (*shared.ResetWorkflowExecutionRequest, error) { var v shared.ResetWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a ResetWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResetWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResetWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResetWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.ResetRequest, err = _ResetWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ResetWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResetWorkflowExecutionRequest struct could not be encoded. func (v *ResetWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ResetRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.ResetRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ResetWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.ResetWorkflowExecutionRequest, error) { var v shared.ResetWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a ResetWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResetWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *ResetWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.ResetRequest, err = _ResetWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResetWorkflowExecutionRequest // struct. func (v *ResetWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.ResetRequest != nil { fields[i] = fmt.Sprintf("ResetRequest: %v", v.ResetRequest) i++ } return fmt.Sprintf("ResetWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ResetWorkflowExecutionRequest match the // provided ResetWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *ResetWorkflowExecutionRequest) Equals(rhs *ResetWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.ResetRequest == nil && rhs.ResetRequest == nil) || (v.ResetRequest != nil && rhs.ResetRequest != nil && v.ResetRequest.Equals(rhs.ResetRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResetWorkflowExecutionRequest. func (v *ResetWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.ResetRequest != nil { err = multierr.Append(err, enc.AddObject("resetRequest", v.ResetRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *ResetWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *ResetWorkflowExecutionRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetResetRequest returns the value of ResetRequest if it is set or its // zero value if it is unset. func (v *ResetWorkflowExecutionRequest) GetResetRequest() (o *shared.ResetWorkflowExecutionRequest) { if v != nil && v.ResetRequest != nil { return v.ResetRequest } return } // IsSetResetRequest returns true if ResetRequest is not nil. func (v *ResetWorkflowExecutionRequest) IsSetResetRequest() bool { return v != nil && v.ResetRequest != nil } type RespondActivityTaskCanceledRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` CancelRequest *shared.RespondActivityTaskCanceledRequest `json:"cancelRequest,omitempty"` } // ToWire translates a RespondActivityTaskCanceledRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondActivityTaskCanceledRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.CancelRequest != nil { w, err = v.CancelRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskCanceledRequest_Read(w wire.Value) (*shared.RespondActivityTaskCanceledRequest, error) { var v shared.RespondActivityTaskCanceledRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RespondActivityTaskCanceledRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondActivityTaskCanceledRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondActivityTaskCanceledRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondActivityTaskCanceledRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.CancelRequest, err = _RespondActivityTaskCanceledRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RespondActivityTaskCanceledRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondActivityTaskCanceledRequest struct could not be encoded. func (v *RespondActivityTaskCanceledRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.CancelRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskCanceledRequest_Decode(sr stream.Reader) (*shared.RespondActivityTaskCanceledRequest, error) { var v shared.RespondActivityTaskCanceledRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RespondActivityTaskCanceledRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondActivityTaskCanceledRequest struct could not be generated from the wire // representation. func (v *RespondActivityTaskCanceledRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.CancelRequest, err = _RespondActivityTaskCanceledRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondActivityTaskCanceledRequest // struct. func (v *RespondActivityTaskCanceledRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.CancelRequest != nil { fields[i] = fmt.Sprintf("CancelRequest: %v", v.CancelRequest) i++ } return fmt.Sprintf("RespondActivityTaskCanceledRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondActivityTaskCanceledRequest match the // provided RespondActivityTaskCanceledRequest. // // This function performs a deep comparison. func (v *RespondActivityTaskCanceledRequest) Equals(rhs *RespondActivityTaskCanceledRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.CancelRequest == nil && rhs.CancelRequest == nil) || (v.CancelRequest != nil && rhs.CancelRequest != nil && v.CancelRequest.Equals(rhs.CancelRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondActivityTaskCanceledRequest. func (v *RespondActivityTaskCanceledRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.CancelRequest != nil { err = multierr.Append(err, enc.AddObject("cancelRequest", v.CancelRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RespondActivityTaskCanceledRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetCancelRequest returns the value of CancelRequest if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledRequest) GetCancelRequest() (o *shared.RespondActivityTaskCanceledRequest) { if v != nil && v.CancelRequest != nil { return v.CancelRequest } return } // IsSetCancelRequest returns true if CancelRequest is not nil. func (v *RespondActivityTaskCanceledRequest) IsSetCancelRequest() bool { return v != nil && v.CancelRequest != nil } type RespondActivityTaskCompletedRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` CompleteRequest *shared.RespondActivityTaskCompletedRequest `json:"completeRequest,omitempty"` } // ToWire translates a RespondActivityTaskCompletedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondActivityTaskCompletedRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.CompleteRequest != nil { w, err = v.CompleteRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskCompletedRequest_Read(w wire.Value) (*shared.RespondActivityTaskCompletedRequest, error) { var v shared.RespondActivityTaskCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RespondActivityTaskCompletedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondActivityTaskCompletedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondActivityTaskCompletedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondActivityTaskCompletedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.CompleteRequest, err = _RespondActivityTaskCompletedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RespondActivityTaskCompletedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondActivityTaskCompletedRequest struct could not be encoded. func (v *RespondActivityTaskCompletedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompleteRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.CompleteRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskCompletedRequest_Decode(sr stream.Reader) (*shared.RespondActivityTaskCompletedRequest, error) { var v shared.RespondActivityTaskCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RespondActivityTaskCompletedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondActivityTaskCompletedRequest struct could not be generated from the wire // representation. func (v *RespondActivityTaskCompletedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.CompleteRequest, err = _RespondActivityTaskCompletedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondActivityTaskCompletedRequest // struct. func (v *RespondActivityTaskCompletedRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.CompleteRequest != nil { fields[i] = fmt.Sprintf("CompleteRequest: %v", v.CompleteRequest) i++ } return fmt.Sprintf("RespondActivityTaskCompletedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondActivityTaskCompletedRequest match the // provided RespondActivityTaskCompletedRequest. // // This function performs a deep comparison. func (v *RespondActivityTaskCompletedRequest) Equals(rhs *RespondActivityTaskCompletedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.CompleteRequest == nil && rhs.CompleteRequest == nil) || (v.CompleteRequest != nil && rhs.CompleteRequest != nil && v.CompleteRequest.Equals(rhs.CompleteRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondActivityTaskCompletedRequest. func (v *RespondActivityTaskCompletedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.CompleteRequest != nil { err = multierr.Append(err, enc.AddObject("completeRequest", v.CompleteRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RespondActivityTaskCompletedRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetCompleteRequest returns the value of CompleteRequest if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedRequest) GetCompleteRequest() (o *shared.RespondActivityTaskCompletedRequest) { if v != nil && v.CompleteRequest != nil { return v.CompleteRequest } return } // IsSetCompleteRequest returns true if CompleteRequest is not nil. func (v *RespondActivityTaskCompletedRequest) IsSetCompleteRequest() bool { return v != nil && v.CompleteRequest != nil } type RespondActivityTaskFailedRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` FailedRequest *shared.RespondActivityTaskFailedRequest `json:"failedRequest,omitempty"` } // ToWire translates a RespondActivityTaskFailedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondActivityTaskFailedRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.FailedRequest != nil { w, err = v.FailedRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskFailedRequest_Read(w wire.Value) (*shared.RespondActivityTaskFailedRequest, error) { var v shared.RespondActivityTaskFailedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RespondActivityTaskFailedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondActivityTaskFailedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondActivityTaskFailedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondActivityTaskFailedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.FailedRequest, err = _RespondActivityTaskFailedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RespondActivityTaskFailedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondActivityTaskFailedRequest struct could not be encoded. func (v *RespondActivityTaskFailedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailedRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.FailedRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskFailedRequest_Decode(sr stream.Reader) (*shared.RespondActivityTaskFailedRequest, error) { var v shared.RespondActivityTaskFailedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RespondActivityTaskFailedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondActivityTaskFailedRequest struct could not be generated from the wire // representation. func (v *RespondActivityTaskFailedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.FailedRequest, err = _RespondActivityTaskFailedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondActivityTaskFailedRequest // struct. func (v *RespondActivityTaskFailedRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.FailedRequest != nil { fields[i] = fmt.Sprintf("FailedRequest: %v", v.FailedRequest) i++ } return fmt.Sprintf("RespondActivityTaskFailedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondActivityTaskFailedRequest match the // provided RespondActivityTaskFailedRequest. // // This function performs a deep comparison. func (v *RespondActivityTaskFailedRequest) Equals(rhs *RespondActivityTaskFailedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.FailedRequest == nil && rhs.FailedRequest == nil) || (v.FailedRequest != nil && rhs.FailedRequest != nil && v.FailedRequest.Equals(rhs.FailedRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondActivityTaskFailedRequest. func (v *RespondActivityTaskFailedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.FailedRequest != nil { err = multierr.Append(err, enc.AddObject("failedRequest", v.FailedRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RespondActivityTaskFailedRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetFailedRequest returns the value of FailedRequest if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedRequest) GetFailedRequest() (o *shared.RespondActivityTaskFailedRequest) { if v != nil && v.FailedRequest != nil { return v.FailedRequest } return } // IsSetFailedRequest returns true if FailedRequest is not nil. func (v *RespondActivityTaskFailedRequest) IsSetFailedRequest() bool { return v != nil && v.FailedRequest != nil } type RespondDecisionTaskCompletedRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` CompleteRequest *shared.RespondDecisionTaskCompletedRequest `json:"completeRequest,omitempty"` } // ToWire translates a RespondDecisionTaskCompletedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondDecisionTaskCompletedRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.CompleteRequest != nil { w, err = v.CompleteRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondDecisionTaskCompletedRequest_Read(w wire.Value) (*shared.RespondDecisionTaskCompletedRequest, error) { var v shared.RespondDecisionTaskCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RespondDecisionTaskCompletedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondDecisionTaskCompletedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondDecisionTaskCompletedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondDecisionTaskCompletedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.CompleteRequest, err = _RespondDecisionTaskCompletedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RespondDecisionTaskCompletedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondDecisionTaskCompletedRequest struct could not be encoded. func (v *RespondDecisionTaskCompletedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompleteRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.CompleteRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondDecisionTaskCompletedRequest_Decode(sr stream.Reader) (*shared.RespondDecisionTaskCompletedRequest, error) { var v shared.RespondDecisionTaskCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RespondDecisionTaskCompletedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondDecisionTaskCompletedRequest struct could not be generated from the wire // representation. func (v *RespondDecisionTaskCompletedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.CompleteRequest, err = _RespondDecisionTaskCompletedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondDecisionTaskCompletedRequest // struct. func (v *RespondDecisionTaskCompletedRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.CompleteRequest != nil { fields[i] = fmt.Sprintf("CompleteRequest: %v", v.CompleteRequest) i++ } return fmt.Sprintf("RespondDecisionTaskCompletedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondDecisionTaskCompletedRequest match the // provided RespondDecisionTaskCompletedRequest. // // This function performs a deep comparison. func (v *RespondDecisionTaskCompletedRequest) Equals(rhs *RespondDecisionTaskCompletedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.CompleteRequest == nil && rhs.CompleteRequest == nil) || (v.CompleteRequest != nil && rhs.CompleteRequest != nil && v.CompleteRequest.Equals(rhs.CompleteRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondDecisionTaskCompletedRequest. func (v *RespondDecisionTaskCompletedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.CompleteRequest != nil { err = multierr.Append(err, enc.AddObject("completeRequest", v.CompleteRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetCompleteRequest returns the value of CompleteRequest if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetCompleteRequest() (o *shared.RespondDecisionTaskCompletedRequest) { if v != nil && v.CompleteRequest != nil { return v.CompleteRequest } return } // IsSetCompleteRequest returns true if CompleteRequest is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetCompleteRequest() bool { return v != nil && v.CompleteRequest != nil } type RespondDecisionTaskCompletedResponse struct { StartedResponse *RecordDecisionTaskStartedResponse `json:"startedResponse,omitempty"` ActivitiesToDispatchLocally map[string]*shared.ActivityLocalDispatchInfo `json:"activitiesToDispatchLocally,omitempty"` } type _Map_String_ActivityLocalDispatchInfo_MapItemList map[string]*shared.ActivityLocalDispatchInfo func (m _Map_String_ActivityLocalDispatchInfo_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*shared.ActivityLocalDispatchInfo', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_ActivityLocalDispatchInfo_MapItemList) Size() int { return len(m) } func (_Map_String_ActivityLocalDispatchInfo_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_ActivityLocalDispatchInfo_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_ActivityLocalDispatchInfo_MapItemList) Close() {} // ToWire translates a RespondDecisionTaskCompletedResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondDecisionTaskCompletedResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.StartedResponse != nil { w, err = v.StartedResponse.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ActivitiesToDispatchLocally != nil { w, err = wire.NewValueMap(_Map_String_ActivityLocalDispatchInfo_MapItemList(v.ActivitiesToDispatchLocally)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordDecisionTaskStartedResponse_Read(w wire.Value) (*RecordDecisionTaskStartedResponse, error) { var v RecordDecisionTaskStartedResponse err := v.FromWire(w) return &v, err } func _ActivityLocalDispatchInfo_Read(w wire.Value) (*shared.ActivityLocalDispatchInfo, error) { var v shared.ActivityLocalDispatchInfo err := v.FromWire(w) return &v, err } func _Map_String_ActivityLocalDispatchInfo_Read(m wire.MapItemList) (map[string]*shared.ActivityLocalDispatchInfo, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*shared.ActivityLocalDispatchInfo, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _ActivityLocalDispatchInfo_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a RespondDecisionTaskCompletedResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondDecisionTaskCompletedResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondDecisionTaskCompletedResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondDecisionTaskCompletedResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.StartedResponse, err = _RecordDecisionTaskStartedResponse_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TMap { v.ActivitiesToDispatchLocally, err = _Map_String_ActivityLocalDispatchInfo_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_ActivityLocalDispatchInfo_Encode(val map[string]*shared.ActivityLocalDispatchInfo, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*shared.ActivityLocalDispatchInfo', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a RespondDecisionTaskCompletedResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondDecisionTaskCompletedResponse struct could not be encoded. func (v *RespondDecisionTaskCompletedResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.StartedResponse != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.StartedResponse.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivitiesToDispatchLocally != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TMap}); err != nil { return err } if err := _Map_String_ActivityLocalDispatchInfo_Encode(v.ActivitiesToDispatchLocally, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RecordDecisionTaskStartedResponse_Decode(sr stream.Reader) (*RecordDecisionTaskStartedResponse, error) { var v RecordDecisionTaskStartedResponse err := v.Decode(sr) return &v, err } func _ActivityLocalDispatchInfo_Decode(sr stream.Reader) (*shared.ActivityLocalDispatchInfo, error) { var v shared.ActivityLocalDispatchInfo err := v.Decode(sr) return &v, err } func _Map_String_ActivityLocalDispatchInfo_Decode(sr stream.Reader) (map[string]*shared.ActivityLocalDispatchInfo, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*shared.ActivityLocalDispatchInfo, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _ActivityLocalDispatchInfo_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a RespondDecisionTaskCompletedResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondDecisionTaskCompletedResponse struct could not be generated from the wire // representation. func (v *RespondDecisionTaskCompletedResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.StartedResponse, err = _RecordDecisionTaskStartedResponse_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TMap: v.ActivitiesToDispatchLocally, err = _Map_String_ActivityLocalDispatchInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondDecisionTaskCompletedResponse // struct. func (v *RespondDecisionTaskCompletedResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.StartedResponse != nil { fields[i] = fmt.Sprintf("StartedResponse: %v", v.StartedResponse) i++ } if v.ActivitiesToDispatchLocally != nil { fields[i] = fmt.Sprintf("ActivitiesToDispatchLocally: %v", v.ActivitiesToDispatchLocally) i++ } return fmt.Sprintf("RespondDecisionTaskCompletedResponse{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_ActivityLocalDispatchInfo_Equals(lhs, rhs map[string]*shared.ActivityLocalDispatchInfo) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this RespondDecisionTaskCompletedResponse match the // provided RespondDecisionTaskCompletedResponse. // // This function performs a deep comparison. func (v *RespondDecisionTaskCompletedResponse) Equals(rhs *RespondDecisionTaskCompletedResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.StartedResponse == nil && rhs.StartedResponse == nil) || (v.StartedResponse != nil && rhs.StartedResponse != nil && v.StartedResponse.Equals(rhs.StartedResponse))) { return false } if !((v.ActivitiesToDispatchLocally == nil && rhs.ActivitiesToDispatchLocally == nil) || (v.ActivitiesToDispatchLocally != nil && rhs.ActivitiesToDispatchLocally != nil && _Map_String_ActivityLocalDispatchInfo_Equals(v.ActivitiesToDispatchLocally, rhs.ActivitiesToDispatchLocally))) { return false } return true } type _Map_String_ActivityLocalDispatchInfo_Zapper map[string]*shared.ActivityLocalDispatchInfo // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_ActivityLocalDispatchInfo_Zapper. func (m _Map_String_ActivityLocalDispatchInfo_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondDecisionTaskCompletedResponse. func (v *RespondDecisionTaskCompletedResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.StartedResponse != nil { err = multierr.Append(err, enc.AddObject("startedResponse", v.StartedResponse)) } if v.ActivitiesToDispatchLocally != nil { err = multierr.Append(err, enc.AddObject("activitiesToDispatchLocally", (_Map_String_ActivityLocalDispatchInfo_Zapper)(v.ActivitiesToDispatchLocally))) } return err } // GetStartedResponse returns the value of StartedResponse if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedResponse) GetStartedResponse() (o *RecordDecisionTaskStartedResponse) { if v != nil && v.StartedResponse != nil { return v.StartedResponse } return } // IsSetStartedResponse returns true if StartedResponse is not nil. func (v *RespondDecisionTaskCompletedResponse) IsSetStartedResponse() bool { return v != nil && v.StartedResponse != nil } // GetActivitiesToDispatchLocally returns the value of ActivitiesToDispatchLocally if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedResponse) GetActivitiesToDispatchLocally() (o map[string]*shared.ActivityLocalDispatchInfo) { if v != nil && v.ActivitiesToDispatchLocally != nil { return v.ActivitiesToDispatchLocally } return } // IsSetActivitiesToDispatchLocally returns true if ActivitiesToDispatchLocally is not nil. func (v *RespondDecisionTaskCompletedResponse) IsSetActivitiesToDispatchLocally() bool { return v != nil && v.ActivitiesToDispatchLocally != nil } type RespondDecisionTaskFailedRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` FailedRequest *shared.RespondDecisionTaskFailedRequest `json:"failedRequest,omitempty"` } // ToWire translates a RespondDecisionTaskFailedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondDecisionTaskFailedRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.FailedRequest != nil { w, err = v.FailedRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondDecisionTaskFailedRequest_Read(w wire.Value) (*shared.RespondDecisionTaskFailedRequest, error) { var v shared.RespondDecisionTaskFailedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RespondDecisionTaskFailedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondDecisionTaskFailedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondDecisionTaskFailedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondDecisionTaskFailedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.FailedRequest, err = _RespondDecisionTaskFailedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RespondDecisionTaskFailedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondDecisionTaskFailedRequest struct could not be encoded. func (v *RespondDecisionTaskFailedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailedRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.FailedRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondDecisionTaskFailedRequest_Decode(sr stream.Reader) (*shared.RespondDecisionTaskFailedRequest, error) { var v shared.RespondDecisionTaskFailedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RespondDecisionTaskFailedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondDecisionTaskFailedRequest struct could not be generated from the wire // representation. func (v *RespondDecisionTaskFailedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.FailedRequest, err = _RespondDecisionTaskFailedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondDecisionTaskFailedRequest // struct. func (v *RespondDecisionTaskFailedRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.FailedRequest != nil { fields[i] = fmt.Sprintf("FailedRequest: %v", v.FailedRequest) i++ } return fmt.Sprintf("RespondDecisionTaskFailedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondDecisionTaskFailedRequest match the // provided RespondDecisionTaskFailedRequest. // // This function performs a deep comparison. func (v *RespondDecisionTaskFailedRequest) Equals(rhs *RespondDecisionTaskFailedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.FailedRequest == nil && rhs.FailedRequest == nil) || (v.FailedRequest != nil && rhs.FailedRequest != nil && v.FailedRequest.Equals(rhs.FailedRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondDecisionTaskFailedRequest. func (v *RespondDecisionTaskFailedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.FailedRequest != nil { err = multierr.Append(err, enc.AddObject("failedRequest", v.FailedRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskFailedRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RespondDecisionTaskFailedRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetFailedRequest returns the value of FailedRequest if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskFailedRequest) GetFailedRequest() (o *shared.RespondDecisionTaskFailedRequest) { if v != nil && v.FailedRequest != nil { return v.FailedRequest } return } // IsSetFailedRequest returns true if FailedRequest is not nil. func (v *RespondDecisionTaskFailedRequest) IsSetFailedRequest() bool { return v != nil && v.FailedRequest != nil } type ScheduleDecisionTaskRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` WorkflowExecution *shared.WorkflowExecution `json:"workflowExecution,omitempty"` IsFirstDecision *bool `json:"isFirstDecision,omitempty"` } // ToWire translates a ScheduleDecisionTaskRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ScheduleDecisionTaskRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.IsFirstDecision != nil { w, err = wire.NewValueBool(*(v.IsFirstDecision)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ScheduleDecisionTaskRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ScheduleDecisionTaskRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ScheduleDecisionTaskRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ScheduleDecisionTaskRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.IsFirstDecision = &x if err != nil { return err } } } } return nil } // Encode serializes a ScheduleDecisionTaskRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ScheduleDecisionTaskRequest struct could not be encoded. func (v *ScheduleDecisionTaskRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsFirstDecision != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.IsFirstDecision)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ScheduleDecisionTaskRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ScheduleDecisionTaskRequest struct could not be generated from the wire // representation. func (v *ScheduleDecisionTaskRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.IsFirstDecision = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ScheduleDecisionTaskRequest // struct. func (v *ScheduleDecisionTaskRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.IsFirstDecision != nil { fields[i] = fmt.Sprintf("IsFirstDecision: %v", *(v.IsFirstDecision)) i++ } return fmt.Sprintf("ScheduleDecisionTaskRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ScheduleDecisionTaskRequest match the // provided ScheduleDecisionTaskRequest. // // This function performs a deep comparison. func (v *ScheduleDecisionTaskRequest) Equals(rhs *ScheduleDecisionTaskRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_Bool_EqualsPtr(v.IsFirstDecision, rhs.IsFirstDecision) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ScheduleDecisionTaskRequest. func (v *ScheduleDecisionTaskRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.IsFirstDecision != nil { enc.AddBool("isFirstDecision", *v.IsFirstDecision) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *ScheduleDecisionTaskRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *ScheduleDecisionTaskRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ScheduleDecisionTaskRequest) GetWorkflowExecution() (o *shared.WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ScheduleDecisionTaskRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetIsFirstDecision returns the value of IsFirstDecision if it is set or its // zero value if it is unset. func (v *ScheduleDecisionTaskRequest) GetIsFirstDecision() (o bool) { if v != nil && v.IsFirstDecision != nil { return *v.IsFirstDecision } return } // IsSetIsFirstDecision returns true if IsFirstDecision is not nil. func (v *ScheduleDecisionTaskRequest) IsSetIsFirstDecision() bool { return v != nil && v.IsFirstDecision != nil } type ShardOwnershipLostError struct { Message *string `json:"message,omitempty"` Owner *string `json:"owner,omitempty"` } // ToWire translates a ShardOwnershipLostError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ShardOwnershipLostError) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Message != nil { w, err = wire.NewValueString(*(v.Message)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Owner != nil { w, err = wire.NewValueString(*(v.Owner)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ShardOwnershipLostError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ShardOwnershipLostError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ShardOwnershipLostError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ShardOwnershipLostError) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Message = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Owner = &x if err != nil { return err } } } } return nil } // Encode serializes a ShardOwnershipLostError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ShardOwnershipLostError struct could not be encoded. func (v *ShardOwnershipLostError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Message != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Message)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Owner != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Owner)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ShardOwnershipLostError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ShardOwnershipLostError struct could not be generated from the wire // representation. func (v *ShardOwnershipLostError) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Message = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Owner = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ShardOwnershipLostError // struct. func (v *ShardOwnershipLostError) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Message != nil { fields[i] = fmt.Sprintf("Message: %v", *(v.Message)) i++ } if v.Owner != nil { fields[i] = fmt.Sprintf("Owner: %v", *(v.Owner)) i++ } return fmt.Sprintf("ShardOwnershipLostError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*ShardOwnershipLostError) ErrorName() string { return "ShardOwnershipLostError" } // Equals returns true if all the fields of this ShardOwnershipLostError match the // provided ShardOwnershipLostError. // // This function performs a deep comparison. func (v *ShardOwnershipLostError) Equals(rhs *ShardOwnershipLostError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Message, rhs.Message) { return false } if !_String_EqualsPtr(v.Owner, rhs.Owner) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ShardOwnershipLostError. func (v *ShardOwnershipLostError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Message != nil { enc.AddString("message", *v.Message) } if v.Owner != nil { enc.AddString("owner", *v.Owner) } return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *ShardOwnershipLostError) GetMessage() (o string) { if v != nil && v.Message != nil { return *v.Message } return } // IsSetMessage returns true if Message is not nil. func (v *ShardOwnershipLostError) IsSetMessage() bool { return v != nil && v.Message != nil } // GetOwner returns the value of Owner if it is set or its // zero value if it is unset. func (v *ShardOwnershipLostError) GetOwner() (o string) { if v != nil && v.Owner != nil { return *v.Owner } return } // IsSetOwner returns true if Owner is not nil. func (v *ShardOwnershipLostError) IsSetOwner() bool { return v != nil && v.Owner != nil } func (v *ShardOwnershipLostError) Error() string { return v.String() } type SignalWithStartWorkflowExecutionRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionRequest `json:"signalWithStartRequest,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` } type _Map_String_String_MapItemList map[string]string func (m _Map_String_String_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := wire.NewValueString(v), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_String_MapItemList) Size() int { return len(m) } func (_Map_String_String_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_String_MapItemList) ValueType() wire.Type { return wire.TBinary } func (_Map_String_String_MapItemList) Close() {} // ToWire translates a SignalWithStartWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SignalWithStartWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.SignalWithStartRequest != nil { w, err = v.SignalWithStartRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.PartitionConfig != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.PartitionConfig)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SignalWithStartWorkflowExecutionRequest_Read(w wire.Value) (*shared.SignalWithStartWorkflowExecutionRequest, error) { var v shared.SignalWithStartWorkflowExecutionRequest err := v.FromWire(w) return &v, err } func _Map_String_String_Read(m wire.MapItemList) (map[string]string, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TBinary { return nil, nil } o := make(map[string]string, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := x.Value.GetString(), error(nil) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a SignalWithStartWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SignalWithStartWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SignalWithStartWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SignalWithStartWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.SignalWithStartRequest, err = _SignalWithStartWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TMap { v.PartitionConfig, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_String_Encode(val map[string]string, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TBinary, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if err := sw.WriteString(k); err != nil { return err } if err := sw.WriteString(v); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a SignalWithStartWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SignalWithStartWorkflowExecutionRequest struct could not be encoded. func (v *SignalWithStartWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalWithStartRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.SignalWithStartRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PartitionConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.PartitionConfig, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SignalWithStartWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.SignalWithStartWorkflowExecutionRequest, error) { var v shared.SignalWithStartWorkflowExecutionRequest err := v.Decode(sr) return &v, err } func _Map_String_String_Decode(sr stream.Reader) (map[string]string, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TBinary { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]string, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := sr.ReadString() if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a SignalWithStartWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SignalWithStartWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *SignalWithStartWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.SignalWithStartRequest, err = _SignalWithStartWorkflowExecutionRequest_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TMap: v.PartitionConfig, err = _Map_String_String_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SignalWithStartWorkflowExecutionRequest // struct. func (v *SignalWithStartWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.SignalWithStartRequest != nil { fields[i] = fmt.Sprintf("SignalWithStartRequest: %v", v.SignalWithStartRequest) i++ } if v.PartitionConfig != nil { fields[i] = fmt.Sprintf("PartitionConfig: %v", v.PartitionConfig) i++ } return fmt.Sprintf("SignalWithStartWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_String_Equals(lhs, rhs map[string]string) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !(lv == rv) { return false } } return true } // Equals returns true if all the fields of this SignalWithStartWorkflowExecutionRequest match the // provided SignalWithStartWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *SignalWithStartWorkflowExecutionRequest) Equals(rhs *SignalWithStartWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.SignalWithStartRequest == nil && rhs.SignalWithStartRequest == nil) || (v.SignalWithStartRequest != nil && rhs.SignalWithStartRequest != nil && v.SignalWithStartRequest.Equals(rhs.SignalWithStartRequest))) { return false } if !((v.PartitionConfig == nil && rhs.PartitionConfig == nil) || (v.PartitionConfig != nil && rhs.PartitionConfig != nil && _Map_String_String_Equals(v.PartitionConfig, rhs.PartitionConfig))) { return false } return true } type _Map_String_String_Zapper map[string]string // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_String_Zapper. func (m _Map_String_String_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { enc.AddString((string)(k), v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalWithStartWorkflowExecutionRequest. func (v *SignalWithStartWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.SignalWithStartRequest != nil { err = multierr.Append(err, enc.AddObject("signalWithStartRequest", v.SignalWithStartRequest)) } if v.PartitionConfig != nil { err = multierr.Append(err, enc.AddObject("partitionConfig", (_Map_String_String_Zapper)(v.PartitionConfig))) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetSignalWithStartRequest returns the value of SignalWithStartRequest if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetSignalWithStartRequest() (o *shared.SignalWithStartWorkflowExecutionRequest) { if v != nil && v.SignalWithStartRequest != nil { return v.SignalWithStartRequest } return } // IsSetSignalWithStartRequest returns true if SignalWithStartRequest is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetSignalWithStartRequest() bool { return v != nil && v.SignalWithStartRequest != nil } // GetPartitionConfig returns the value of PartitionConfig if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // IsSetPartitionConfig returns true if PartitionConfig is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetPartitionConfig() bool { return v != nil && v.PartitionConfig != nil } type SignalWorkflowExecutionRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` SignalRequest *shared.SignalWorkflowExecutionRequest `json:"signalRequest,omitempty"` ExternalWorkflowExecution *shared.WorkflowExecution `json:"externalWorkflowExecution,omitempty"` ChildWorkflowOnly *bool `json:"childWorkflowOnly,omitempty"` } // ToWire translates a SignalWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SignalWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.SignalRequest != nil { w, err = v.SignalRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ExternalWorkflowExecution != nil { w, err = v.ExternalWorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ChildWorkflowOnly != nil { w, err = wire.NewValueBool(*(v.ChildWorkflowOnly)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SignalWorkflowExecutionRequest_Read(w wire.Value) (*shared.SignalWorkflowExecutionRequest, error) { var v shared.SignalWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a SignalWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SignalWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SignalWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SignalWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.SignalRequest, err = _SignalWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ExternalWorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ChildWorkflowOnly = &x if err != nil { return err } } } } return nil } // Encode serializes a SignalWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SignalWorkflowExecutionRequest struct could not be encoded. func (v *SignalWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.SignalRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExternalWorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ExternalWorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowOnly != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ChildWorkflowOnly)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SignalWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.SignalWorkflowExecutionRequest, error) { var v shared.SignalWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a SignalWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SignalWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *SignalWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.SignalRequest, err = _SignalWorkflowExecutionRequest_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ExternalWorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ChildWorkflowOnly = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SignalWorkflowExecutionRequest // struct. func (v *SignalWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.SignalRequest != nil { fields[i] = fmt.Sprintf("SignalRequest: %v", v.SignalRequest) i++ } if v.ExternalWorkflowExecution != nil { fields[i] = fmt.Sprintf("ExternalWorkflowExecution: %v", v.ExternalWorkflowExecution) i++ } if v.ChildWorkflowOnly != nil { fields[i] = fmt.Sprintf("ChildWorkflowOnly: %v", *(v.ChildWorkflowOnly)) i++ } return fmt.Sprintf("SignalWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SignalWorkflowExecutionRequest match the // provided SignalWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *SignalWorkflowExecutionRequest) Equals(rhs *SignalWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.SignalRequest == nil && rhs.SignalRequest == nil) || (v.SignalRequest != nil && rhs.SignalRequest != nil && v.SignalRequest.Equals(rhs.SignalRequest))) { return false } if !((v.ExternalWorkflowExecution == nil && rhs.ExternalWorkflowExecution == nil) || (v.ExternalWorkflowExecution != nil && rhs.ExternalWorkflowExecution != nil && v.ExternalWorkflowExecution.Equals(rhs.ExternalWorkflowExecution))) { return false } if !_Bool_EqualsPtr(v.ChildWorkflowOnly, rhs.ChildWorkflowOnly) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalWorkflowExecutionRequest. func (v *SignalWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.SignalRequest != nil { err = multierr.Append(err, enc.AddObject("signalRequest", v.SignalRequest)) } if v.ExternalWorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("externalWorkflowExecution", v.ExternalWorkflowExecution)) } if v.ChildWorkflowOnly != nil { enc.AddBool("childWorkflowOnly", *v.ChildWorkflowOnly) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *SignalWorkflowExecutionRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetSignalRequest returns the value of SignalRequest if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetSignalRequest() (o *shared.SignalWorkflowExecutionRequest) { if v != nil && v.SignalRequest != nil { return v.SignalRequest } return } // IsSetSignalRequest returns true if SignalRequest is not nil. func (v *SignalWorkflowExecutionRequest) IsSetSignalRequest() bool { return v != nil && v.SignalRequest != nil } // GetExternalWorkflowExecution returns the value of ExternalWorkflowExecution if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetExternalWorkflowExecution() (o *shared.WorkflowExecution) { if v != nil && v.ExternalWorkflowExecution != nil { return v.ExternalWorkflowExecution } return } // IsSetExternalWorkflowExecution returns true if ExternalWorkflowExecution is not nil. func (v *SignalWorkflowExecutionRequest) IsSetExternalWorkflowExecution() bool { return v != nil && v.ExternalWorkflowExecution != nil } // GetChildWorkflowOnly returns the value of ChildWorkflowOnly if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetChildWorkflowOnly() (o bool) { if v != nil && v.ChildWorkflowOnly != nil { return *v.ChildWorkflowOnly } return } // IsSetChildWorkflowOnly returns true if ChildWorkflowOnly is not nil. func (v *SignalWorkflowExecutionRequest) IsSetChildWorkflowOnly() bool { return v != nil && v.ChildWorkflowOnly != nil } type StartWorkflowExecutionRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` StartRequest *shared.StartWorkflowExecutionRequest `json:"startRequest,omitempty"` ParentExecutionInfo *ParentExecutionInfo `json:"parentExecutionInfo,omitempty"` Attempt *int32 `json:"attempt,omitempty"` ExpirationTimestamp *int64 `json:"expirationTimestamp,omitempty"` ContinueAsNewInitiator *shared.ContinueAsNewInitiator `json:"continueAsNewInitiator,omitempty"` ContinuedFailureReason *string `json:"continuedFailureReason,omitempty"` ContinuedFailureDetails []byte `json:"continuedFailureDetails,omitempty"` LastCompletionResult []byte `json:"lastCompletionResult,omitempty"` FirstDecisionTaskBackoffSeconds *int32 `json:"firstDecisionTaskBackoffSeconds,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` } // ToWire translates a StartWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StartWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [11]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartRequest != nil { w, err = v.StartRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ParentExecutionInfo != nil { w, err = v.ParentExecutionInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI32(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ExpirationTimestamp != nil { w, err = wire.NewValueI64(*(v.ExpirationTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ContinueAsNewInitiator != nil { w, err = v.ContinueAsNewInitiator.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 55, Value: w} i++ } if v.ContinuedFailureReason != nil { w, err = wire.NewValueString(*(v.ContinuedFailureReason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 56, Value: w} i++ } if v.ContinuedFailureDetails != nil { w, err = wire.NewValueBinary(v.ContinuedFailureDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 57, Value: w} i++ } if v.LastCompletionResult != nil { w, err = wire.NewValueBinary(v.LastCompletionResult), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 58, Value: w} i++ } if v.FirstDecisionTaskBackoffSeconds != nil { w, err = wire.NewValueI32(*(v.FirstDecisionTaskBackoffSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.PartitionConfig != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.PartitionConfig)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 62, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StartWorkflowExecutionRequest_Read(w wire.Value) (*shared.StartWorkflowExecutionRequest, error) { var v shared.StartWorkflowExecutionRequest err := v.FromWire(w) return &v, err } func _ParentExecutionInfo_Read(w wire.Value) (*ParentExecutionInfo, error) { var v ParentExecutionInfo err := v.FromWire(w) return &v, err } func _ContinueAsNewInitiator_Read(w wire.Value) (shared.ContinueAsNewInitiator, error) { var v shared.ContinueAsNewInitiator err := v.FromWire(w) return v, err } // FromWire deserializes a StartWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StartWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StartWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StartWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.StartRequest, err = _StartWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ParentExecutionInfo, err = _ParentExecutionInfo_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Attempt = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExpirationTimestamp = &x if err != nil { return err } } case 55: if field.Value.Type() == wire.TI32 { var x shared.ContinueAsNewInitiator x, err = _ContinueAsNewInitiator_Read(field.Value) v.ContinueAsNewInitiator = &x if err != nil { return err } } case 56: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ContinuedFailureReason = &x if err != nil { return err } } case 57: if field.Value.Type() == wire.TBinary { v.ContinuedFailureDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 58: if field.Value.Type() == wire.TBinary { v.LastCompletionResult, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.FirstDecisionTaskBackoffSeconds = &x if err != nil { return err } } case 62: if field.Value.Type() == wire.TMap { v.PartitionConfig, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } // Encode serializes a StartWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StartWorkflowExecutionRequest struct could not be encoded. func (v *StartWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.StartRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentExecutionInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ParentExecutionInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExpirationTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExpirationTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ContinueAsNewInitiator != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 55, Type: wire.TI32}); err != nil { return err } if err := v.ContinueAsNewInitiator.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ContinuedFailureReason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 56, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ContinuedFailureReason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ContinuedFailureDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 57, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.ContinuedFailureDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastCompletionResult != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 58, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.LastCompletionResult); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstDecisionTaskBackoffSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.FirstDecisionTaskBackoffSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PartitionConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 62, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.PartitionConfig, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _StartWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.StartWorkflowExecutionRequest, error) { var v shared.StartWorkflowExecutionRequest err := v.Decode(sr) return &v, err } func _ParentExecutionInfo_Decode(sr stream.Reader) (*ParentExecutionInfo, error) { var v ParentExecutionInfo err := v.Decode(sr) return &v, err } func _ContinueAsNewInitiator_Decode(sr stream.Reader) (shared.ContinueAsNewInitiator, error) { var v shared.ContinueAsNewInitiator err := v.Decode(sr) return v, err } // Decode deserializes a StartWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StartWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *StartWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.StartRequest, err = _StartWorkflowExecutionRequest_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ParentExecutionInfo, err = _ParentExecutionInfo_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Attempt = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExpirationTimestamp = &x if err != nil { return err } case fh.ID == 55 && fh.Type == wire.TI32: var x shared.ContinueAsNewInitiator x, err = _ContinueAsNewInitiator_Decode(sr) v.ContinueAsNewInitiator = &x if err != nil { return err } case fh.ID == 56 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ContinuedFailureReason = &x if err != nil { return err } case fh.ID == 57 && fh.Type == wire.TBinary: v.ContinuedFailureDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 58 && fh.Type == wire.TBinary: v.LastCompletionResult, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.FirstDecisionTaskBackoffSeconds = &x if err != nil { return err } case fh.ID == 62 && fh.Type == wire.TMap: v.PartitionConfig, err = _Map_String_String_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StartWorkflowExecutionRequest // struct. func (v *StartWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [11]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.StartRequest != nil { fields[i] = fmt.Sprintf("StartRequest: %v", v.StartRequest) i++ } if v.ParentExecutionInfo != nil { fields[i] = fmt.Sprintf("ParentExecutionInfo: %v", v.ParentExecutionInfo) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.ExpirationTimestamp != nil { fields[i] = fmt.Sprintf("ExpirationTimestamp: %v", *(v.ExpirationTimestamp)) i++ } if v.ContinueAsNewInitiator != nil { fields[i] = fmt.Sprintf("ContinueAsNewInitiator: %v", *(v.ContinueAsNewInitiator)) i++ } if v.ContinuedFailureReason != nil { fields[i] = fmt.Sprintf("ContinuedFailureReason: %v", *(v.ContinuedFailureReason)) i++ } if v.ContinuedFailureDetails != nil { fields[i] = fmt.Sprintf("ContinuedFailureDetails: %v", v.ContinuedFailureDetails) i++ } if v.LastCompletionResult != nil { fields[i] = fmt.Sprintf("LastCompletionResult: %v", v.LastCompletionResult) i++ } if v.FirstDecisionTaskBackoffSeconds != nil { fields[i] = fmt.Sprintf("FirstDecisionTaskBackoffSeconds: %v", *(v.FirstDecisionTaskBackoffSeconds)) i++ } if v.PartitionConfig != nil { fields[i] = fmt.Sprintf("PartitionConfig: %v", v.PartitionConfig) i++ } return fmt.Sprintf("StartWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } func _ContinueAsNewInitiator_EqualsPtr(lhs, rhs *shared.ContinueAsNewInitiator) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this StartWorkflowExecutionRequest match the // provided StartWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *StartWorkflowExecutionRequest) Equals(rhs *StartWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.StartRequest == nil && rhs.StartRequest == nil) || (v.StartRequest != nil && rhs.StartRequest != nil && v.StartRequest.Equals(rhs.StartRequest))) { return false } if !((v.ParentExecutionInfo == nil && rhs.ParentExecutionInfo == nil) || (v.ParentExecutionInfo != nil && rhs.ParentExecutionInfo != nil && v.ParentExecutionInfo.Equals(rhs.ParentExecutionInfo))) { return false } if !_I32_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_I64_EqualsPtr(v.ExpirationTimestamp, rhs.ExpirationTimestamp) { return false } if !_ContinueAsNewInitiator_EqualsPtr(v.ContinueAsNewInitiator, rhs.ContinueAsNewInitiator) { return false } if !_String_EqualsPtr(v.ContinuedFailureReason, rhs.ContinuedFailureReason) { return false } if !((v.ContinuedFailureDetails == nil && rhs.ContinuedFailureDetails == nil) || (v.ContinuedFailureDetails != nil && rhs.ContinuedFailureDetails != nil && bytes.Equal(v.ContinuedFailureDetails, rhs.ContinuedFailureDetails))) { return false } if !((v.LastCompletionResult == nil && rhs.LastCompletionResult == nil) || (v.LastCompletionResult != nil && rhs.LastCompletionResult != nil && bytes.Equal(v.LastCompletionResult, rhs.LastCompletionResult))) { return false } if !_I32_EqualsPtr(v.FirstDecisionTaskBackoffSeconds, rhs.FirstDecisionTaskBackoffSeconds) { return false } if !((v.PartitionConfig == nil && rhs.PartitionConfig == nil) || (v.PartitionConfig != nil && rhs.PartitionConfig != nil && _Map_String_String_Equals(v.PartitionConfig, rhs.PartitionConfig))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StartWorkflowExecutionRequest. func (v *StartWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.StartRequest != nil { err = multierr.Append(err, enc.AddObject("startRequest", v.StartRequest)) } if v.ParentExecutionInfo != nil { err = multierr.Append(err, enc.AddObject("parentExecutionInfo", v.ParentExecutionInfo)) } if v.Attempt != nil { enc.AddInt32("attempt", *v.Attempt) } if v.ExpirationTimestamp != nil { enc.AddInt64("expirationTimestamp", *v.ExpirationTimestamp) } if v.ContinueAsNewInitiator != nil { err = multierr.Append(err, enc.AddObject("continueAsNewInitiator", *v.ContinueAsNewInitiator)) } if v.ContinuedFailureReason != nil { enc.AddString("continuedFailureReason", *v.ContinuedFailureReason) } if v.ContinuedFailureDetails != nil { enc.AddString("continuedFailureDetails", base64.StdEncoding.EncodeToString(v.ContinuedFailureDetails)) } if v.LastCompletionResult != nil { enc.AddString("lastCompletionResult", base64.StdEncoding.EncodeToString(v.LastCompletionResult)) } if v.FirstDecisionTaskBackoffSeconds != nil { enc.AddInt32("firstDecisionTaskBackoffSeconds", *v.FirstDecisionTaskBackoffSeconds) } if v.PartitionConfig != nil { err = multierr.Append(err, enc.AddObject("partitionConfig", (_Map_String_String_Zapper)(v.PartitionConfig))) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *StartWorkflowExecutionRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetStartRequest returns the value of StartRequest if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetStartRequest() (o *shared.StartWorkflowExecutionRequest) { if v != nil && v.StartRequest != nil { return v.StartRequest } return } // IsSetStartRequest returns true if StartRequest is not nil. func (v *StartWorkflowExecutionRequest) IsSetStartRequest() bool { return v != nil && v.StartRequest != nil } // GetParentExecutionInfo returns the value of ParentExecutionInfo if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetParentExecutionInfo() (o *ParentExecutionInfo) { if v != nil && v.ParentExecutionInfo != nil { return v.ParentExecutionInfo } return } // IsSetParentExecutionInfo returns true if ParentExecutionInfo is not nil. func (v *StartWorkflowExecutionRequest) IsSetParentExecutionInfo() bool { return v != nil && v.ParentExecutionInfo != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetAttempt() (o int32) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *StartWorkflowExecutionRequest) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetExpirationTimestamp returns the value of ExpirationTimestamp if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetExpirationTimestamp() (o int64) { if v != nil && v.ExpirationTimestamp != nil { return *v.ExpirationTimestamp } return } // IsSetExpirationTimestamp returns true if ExpirationTimestamp is not nil. func (v *StartWorkflowExecutionRequest) IsSetExpirationTimestamp() bool { return v != nil && v.ExpirationTimestamp != nil } // GetContinueAsNewInitiator returns the value of ContinueAsNewInitiator if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetContinueAsNewInitiator() (o shared.ContinueAsNewInitiator) { if v != nil && v.ContinueAsNewInitiator != nil { return *v.ContinueAsNewInitiator } return } // IsSetContinueAsNewInitiator returns true if ContinueAsNewInitiator is not nil. func (v *StartWorkflowExecutionRequest) IsSetContinueAsNewInitiator() bool { return v != nil && v.ContinueAsNewInitiator != nil } // GetContinuedFailureReason returns the value of ContinuedFailureReason if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetContinuedFailureReason() (o string) { if v != nil && v.ContinuedFailureReason != nil { return *v.ContinuedFailureReason } return } // IsSetContinuedFailureReason returns true if ContinuedFailureReason is not nil. func (v *StartWorkflowExecutionRequest) IsSetContinuedFailureReason() bool { return v != nil && v.ContinuedFailureReason != nil } // GetContinuedFailureDetails returns the value of ContinuedFailureDetails if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetContinuedFailureDetails() (o []byte) { if v != nil && v.ContinuedFailureDetails != nil { return v.ContinuedFailureDetails } return } // IsSetContinuedFailureDetails returns true if ContinuedFailureDetails is not nil. func (v *StartWorkflowExecutionRequest) IsSetContinuedFailureDetails() bool { return v != nil && v.ContinuedFailureDetails != nil } // GetLastCompletionResult returns the value of LastCompletionResult if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetLastCompletionResult() (o []byte) { if v != nil && v.LastCompletionResult != nil { return v.LastCompletionResult } return } // IsSetLastCompletionResult returns true if LastCompletionResult is not nil. func (v *StartWorkflowExecutionRequest) IsSetLastCompletionResult() bool { return v != nil && v.LastCompletionResult != nil } // GetFirstDecisionTaskBackoffSeconds returns the value of FirstDecisionTaskBackoffSeconds if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetFirstDecisionTaskBackoffSeconds() (o int32) { if v != nil && v.FirstDecisionTaskBackoffSeconds != nil { return *v.FirstDecisionTaskBackoffSeconds } return } // IsSetFirstDecisionTaskBackoffSeconds returns true if FirstDecisionTaskBackoffSeconds is not nil. func (v *StartWorkflowExecutionRequest) IsSetFirstDecisionTaskBackoffSeconds() bool { return v != nil && v.FirstDecisionTaskBackoffSeconds != nil } // GetPartitionConfig returns the value of PartitionConfig if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // IsSetPartitionConfig returns true if PartitionConfig is not nil. func (v *StartWorkflowExecutionRequest) IsSetPartitionConfig() bool { return v != nil && v.PartitionConfig != nil } type SyncActivityRequest struct { DomainId *string `json:"domainId,omitempty"` WorkflowId *string `json:"workflowId,omitempty"` RunId *string `json:"runId,omitempty"` Version *int64 `json:"version,omitempty"` ScheduledId *int64 `json:"scheduledId,omitempty"` ScheduledTime *int64 `json:"scheduledTime,omitempty"` StartedId *int64 `json:"startedId,omitempty"` StartedTime *int64 `json:"startedTime,omitempty"` LastHeartbeatTime *int64 `json:"lastHeartbeatTime,omitempty"` Details []byte `json:"details,omitempty"` Attempt *int32 `json:"attempt,omitempty"` LastFailureReason *string `json:"lastFailureReason,omitempty"` LastWorkerIdentity *string `json:"lastWorkerIdentity,omitempty"` LastFailureDetails []byte `json:"lastFailureDetails,omitempty"` VersionHistory *shared.VersionHistory `json:"versionHistory,omitempty"` } // ToWire translates a SyncActivityRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SyncActivityRequest) ToWire() (wire.Value, error) { var ( fields [15]wire.Field i int = 0 w wire.Value err error ) if v.DomainId != nil { w, err = wire.NewValueString(*(v.DomainId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ScheduledId != nil { w, err = wire.NewValueI64(*(v.ScheduledId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ScheduledTime != nil { w, err = wire.NewValueI64(*(v.ScheduledTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.StartedId != nil { w, err = wire.NewValueI64(*(v.StartedId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.StartedTime != nil { w, err = wire.NewValueI64(*(v.StartedTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.LastHeartbeatTime != nil { w, err = wire.NewValueI64(*(v.LastHeartbeatTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI32(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.LastFailureReason != nil { w, err = wire.NewValueString(*(v.LastFailureReason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.LastWorkerIdentity != nil { w, err = wire.NewValueString(*(v.LastWorkerIdentity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.LastFailureDetails != nil { w, err = wire.NewValueBinary(v.LastFailureDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.VersionHistory != nil { w, err = v.VersionHistory.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _VersionHistory_Read(w wire.Value) (*shared.VersionHistory, error) { var v shared.VersionHistory err := v.FromWire(w) return &v, err } // FromWire deserializes a SyncActivityRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SyncActivityRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SyncActivityRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SyncActivityRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTime = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedId = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTime = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastHeartbeatTime = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 110: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Attempt = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.LastFailureReason = &x if err != nil { return err } } case 130: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.LastWorkerIdentity = &x if err != nil { return err } } case 140: if field.Value.Type() == wire.TBinary { v.LastFailureDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 150: if field.Value.Type() == wire.TStruct { v.VersionHistory, err = _VersionHistory_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a SyncActivityRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SyncActivityRequest struct could not be encoded. func (v *SyncActivityRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastHeartbeatTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastHeartbeatTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFailureReason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.LastFailureReason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastWorkerIdentity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.LastWorkerIdentity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFailureDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.LastFailureDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistory != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TStruct}); err != nil { return err } if err := v.VersionHistory.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _VersionHistory_Decode(sr stream.Reader) (*shared.VersionHistory, error) { var v shared.VersionHistory err := v.Decode(sr) return &v, err } // Decode deserializes a SyncActivityRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SyncActivityRequest struct could not be generated from the wire // representation. func (v *SyncActivityRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTime = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedId = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTime = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastHeartbeatTime = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Attempt = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.LastFailureReason = &x if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.LastWorkerIdentity = &x if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TBinary: v.LastFailureDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TStruct: v.VersionHistory, err = _VersionHistory_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SyncActivityRequest // struct. func (v *SyncActivityRequest) String() string { if v == nil { return "" } var fields [15]string i := 0 if v.DomainId != nil { fields[i] = fmt.Sprintf("DomainId: %v", *(v.DomainId)) i++ } if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.ScheduledId != nil { fields[i] = fmt.Sprintf("ScheduledId: %v", *(v.ScheduledId)) i++ } if v.ScheduledTime != nil { fields[i] = fmt.Sprintf("ScheduledTime: %v", *(v.ScheduledTime)) i++ } if v.StartedId != nil { fields[i] = fmt.Sprintf("StartedId: %v", *(v.StartedId)) i++ } if v.StartedTime != nil { fields[i] = fmt.Sprintf("StartedTime: %v", *(v.StartedTime)) i++ } if v.LastHeartbeatTime != nil { fields[i] = fmt.Sprintf("LastHeartbeatTime: %v", *(v.LastHeartbeatTime)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.LastFailureReason != nil { fields[i] = fmt.Sprintf("LastFailureReason: %v", *(v.LastFailureReason)) i++ } if v.LastWorkerIdentity != nil { fields[i] = fmt.Sprintf("LastWorkerIdentity: %v", *(v.LastWorkerIdentity)) i++ } if v.LastFailureDetails != nil { fields[i] = fmt.Sprintf("LastFailureDetails: %v", v.LastFailureDetails) i++ } if v.VersionHistory != nil { fields[i] = fmt.Sprintf("VersionHistory: %v", v.VersionHistory) i++ } return fmt.Sprintf("SyncActivityRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SyncActivityRequest match the // provided SyncActivityRequest. // // This function performs a deep comparison. func (v *SyncActivityRequest) Equals(rhs *SyncActivityRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainId, rhs.DomainId) { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.ScheduledId, rhs.ScheduledId) { return false } if !_I64_EqualsPtr(v.ScheduledTime, rhs.ScheduledTime) { return false } if !_I64_EqualsPtr(v.StartedId, rhs.StartedId) { return false } if !_I64_EqualsPtr(v.StartedTime, rhs.StartedTime) { return false } if !_I64_EqualsPtr(v.LastHeartbeatTime, rhs.LastHeartbeatTime) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_I32_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_String_EqualsPtr(v.LastFailureReason, rhs.LastFailureReason) { return false } if !_String_EqualsPtr(v.LastWorkerIdentity, rhs.LastWorkerIdentity) { return false } if !((v.LastFailureDetails == nil && rhs.LastFailureDetails == nil) || (v.LastFailureDetails != nil && rhs.LastFailureDetails != nil && bytes.Equal(v.LastFailureDetails, rhs.LastFailureDetails))) { return false } if !((v.VersionHistory == nil && rhs.VersionHistory == nil) || (v.VersionHistory != nil && rhs.VersionHistory != nil && v.VersionHistory.Equals(rhs.VersionHistory))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SyncActivityRequest. func (v *SyncActivityRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainId != nil { enc.AddString("domainId", *v.DomainId) } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.RunId != nil { enc.AddString("runId", *v.RunId) } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.ScheduledId != nil { enc.AddInt64("scheduledId", *v.ScheduledId) } if v.ScheduledTime != nil { enc.AddInt64("scheduledTime", *v.ScheduledTime) } if v.StartedId != nil { enc.AddInt64("startedId", *v.StartedId) } if v.StartedTime != nil { enc.AddInt64("startedTime", *v.StartedTime) } if v.LastHeartbeatTime != nil { enc.AddInt64("lastHeartbeatTime", *v.LastHeartbeatTime) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Attempt != nil { enc.AddInt32("attempt", *v.Attempt) } if v.LastFailureReason != nil { enc.AddString("lastFailureReason", *v.LastFailureReason) } if v.LastWorkerIdentity != nil { enc.AddString("lastWorkerIdentity", *v.LastWorkerIdentity) } if v.LastFailureDetails != nil { enc.AddString("lastFailureDetails", base64.StdEncoding.EncodeToString(v.LastFailureDetails)) } if v.VersionHistory != nil { err = multierr.Append(err, enc.AddObject("versionHistory", v.VersionHistory)) } return err } // GetDomainId returns the value of DomainId if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetDomainId() (o string) { if v != nil && v.DomainId != nil { return *v.DomainId } return } // IsSetDomainId returns true if DomainId is not nil. func (v *SyncActivityRequest) IsSetDomainId() bool { return v != nil && v.DomainId != nil } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *SyncActivityRequest) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *SyncActivityRequest) IsSetRunId() bool { return v != nil && v.RunId != nil } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *SyncActivityRequest) IsSetVersion() bool { return v != nil && v.Version != nil } // GetScheduledId returns the value of ScheduledId if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetScheduledId() (o int64) { if v != nil && v.ScheduledId != nil { return *v.ScheduledId } return } // IsSetScheduledId returns true if ScheduledId is not nil. func (v *SyncActivityRequest) IsSetScheduledId() bool { return v != nil && v.ScheduledId != nil } // GetScheduledTime returns the value of ScheduledTime if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetScheduledTime() (o int64) { if v != nil && v.ScheduledTime != nil { return *v.ScheduledTime } return } // IsSetScheduledTime returns true if ScheduledTime is not nil. func (v *SyncActivityRequest) IsSetScheduledTime() bool { return v != nil && v.ScheduledTime != nil } // GetStartedId returns the value of StartedId if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetStartedId() (o int64) { if v != nil && v.StartedId != nil { return *v.StartedId } return } // IsSetStartedId returns true if StartedId is not nil. func (v *SyncActivityRequest) IsSetStartedId() bool { return v != nil && v.StartedId != nil } // GetStartedTime returns the value of StartedTime if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetStartedTime() (o int64) { if v != nil && v.StartedTime != nil { return *v.StartedTime } return } // IsSetStartedTime returns true if StartedTime is not nil. func (v *SyncActivityRequest) IsSetStartedTime() bool { return v != nil && v.StartedTime != nil } // GetLastHeartbeatTime returns the value of LastHeartbeatTime if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetLastHeartbeatTime() (o int64) { if v != nil && v.LastHeartbeatTime != nil { return *v.LastHeartbeatTime } return } // IsSetLastHeartbeatTime returns true if LastHeartbeatTime is not nil. func (v *SyncActivityRequest) IsSetLastHeartbeatTime() bool { return v != nil && v.LastHeartbeatTime != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *SyncActivityRequest) IsSetDetails() bool { return v != nil && v.Details != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetAttempt() (o int32) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *SyncActivityRequest) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetLastFailureReason returns the value of LastFailureReason if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetLastFailureReason() (o string) { if v != nil && v.LastFailureReason != nil { return *v.LastFailureReason } return } // IsSetLastFailureReason returns true if LastFailureReason is not nil. func (v *SyncActivityRequest) IsSetLastFailureReason() bool { return v != nil && v.LastFailureReason != nil } // GetLastWorkerIdentity returns the value of LastWorkerIdentity if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetLastWorkerIdentity() (o string) { if v != nil && v.LastWorkerIdentity != nil { return *v.LastWorkerIdentity } return } // IsSetLastWorkerIdentity returns true if LastWorkerIdentity is not nil. func (v *SyncActivityRequest) IsSetLastWorkerIdentity() bool { return v != nil && v.LastWorkerIdentity != nil } // GetLastFailureDetails returns the value of LastFailureDetails if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetLastFailureDetails() (o []byte) { if v != nil && v.LastFailureDetails != nil { return v.LastFailureDetails } return } // IsSetLastFailureDetails returns true if LastFailureDetails is not nil. func (v *SyncActivityRequest) IsSetLastFailureDetails() bool { return v != nil && v.LastFailureDetails != nil } // GetVersionHistory returns the value of VersionHistory if it is set or its // zero value if it is unset. func (v *SyncActivityRequest) GetVersionHistory() (o *shared.VersionHistory) { if v != nil && v.VersionHistory != nil { return v.VersionHistory } return } // IsSetVersionHistory returns true if VersionHistory is not nil. func (v *SyncActivityRequest) IsSetVersionHistory() bool { return v != nil && v.VersionHistory != nil } type SyncShardStatusRequest struct { SourceCluster *string `json:"sourceCluster,omitempty"` ShardId *int64 `json:"shardId,omitempty"` Timestamp *int64 `json:"timestamp,omitempty"` } // ToWire translates a SyncShardStatusRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SyncShardStatusRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.SourceCluster != nil { w, err = wire.NewValueString(*(v.SourceCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ShardId != nil { w, err = wire.NewValueI64(*(v.ShardId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Timestamp != nil { w, err = wire.NewValueI64(*(v.Timestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a SyncShardStatusRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SyncShardStatusRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SyncShardStatusRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SyncShardStatusRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SourceCluster = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ShardId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Timestamp = &x if err != nil { return err } } } } return nil } // Encode serializes a SyncShardStatusRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SyncShardStatusRequest struct could not be encoded. func (v *SyncShardStatusRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SourceCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SourceCluster)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ShardId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Timestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Timestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a SyncShardStatusRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SyncShardStatusRequest struct could not be generated from the wire // representation. func (v *SyncShardStatusRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SourceCluster = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ShardId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Timestamp = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SyncShardStatusRequest // struct. func (v *SyncShardStatusRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.SourceCluster != nil { fields[i] = fmt.Sprintf("SourceCluster: %v", *(v.SourceCluster)) i++ } if v.ShardId != nil { fields[i] = fmt.Sprintf("ShardId: %v", *(v.ShardId)) i++ } if v.Timestamp != nil { fields[i] = fmt.Sprintf("Timestamp: %v", *(v.Timestamp)) i++ } return fmt.Sprintf("SyncShardStatusRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SyncShardStatusRequest match the // provided SyncShardStatusRequest. // // This function performs a deep comparison. func (v *SyncShardStatusRequest) Equals(rhs *SyncShardStatusRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.SourceCluster, rhs.SourceCluster) { return false } if !_I64_EqualsPtr(v.ShardId, rhs.ShardId) { return false } if !_I64_EqualsPtr(v.Timestamp, rhs.Timestamp) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SyncShardStatusRequest. func (v *SyncShardStatusRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SourceCluster != nil { enc.AddString("sourceCluster", *v.SourceCluster) } if v.ShardId != nil { enc.AddInt64("shardId", *v.ShardId) } if v.Timestamp != nil { enc.AddInt64("timestamp", *v.Timestamp) } return err } // GetSourceCluster returns the value of SourceCluster if it is set or its // zero value if it is unset. func (v *SyncShardStatusRequest) GetSourceCluster() (o string) { if v != nil && v.SourceCluster != nil { return *v.SourceCluster } return } // IsSetSourceCluster returns true if SourceCluster is not nil. func (v *SyncShardStatusRequest) IsSetSourceCluster() bool { return v != nil && v.SourceCluster != nil } // GetShardId returns the value of ShardId if it is set or its // zero value if it is unset. func (v *SyncShardStatusRequest) GetShardId() (o int64) { if v != nil && v.ShardId != nil { return *v.ShardId } return } // IsSetShardId returns true if ShardId is not nil. func (v *SyncShardStatusRequest) IsSetShardId() bool { return v != nil && v.ShardId != nil } // GetTimestamp returns the value of Timestamp if it is set or its // zero value if it is unset. func (v *SyncShardStatusRequest) GetTimestamp() (o int64) { if v != nil && v.Timestamp != nil { return *v.Timestamp } return } // IsSetTimestamp returns true if Timestamp is not nil. func (v *SyncShardStatusRequest) IsSetTimestamp() bool { return v != nil && v.Timestamp != nil } type TerminateWorkflowExecutionRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` TerminateRequest *shared.TerminateWorkflowExecutionRequest `json:"terminateRequest,omitempty"` ExternalWorkflowExecution *shared.WorkflowExecution `json:"externalWorkflowExecution,omitempty"` ChildWorkflowOnly *bool `json:"childWorkflowOnly,omitempty"` } // ToWire translates a TerminateWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TerminateWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TerminateRequest != nil { w, err = v.TerminateRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ExternalWorkflowExecution != nil { w, err = v.ExternalWorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ChildWorkflowOnly != nil { w, err = wire.NewValueBool(*(v.ChildWorkflowOnly)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TerminateWorkflowExecutionRequest_Read(w wire.Value) (*shared.TerminateWorkflowExecutionRequest, error) { var v shared.TerminateWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a TerminateWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TerminateWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TerminateWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TerminateWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TerminateRequest, err = _TerminateWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ExternalWorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ChildWorkflowOnly = &x if err != nil { return err } } } } return nil } // Encode serializes a TerminateWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TerminateWorkflowExecutionRequest struct could not be encoded. func (v *TerminateWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TerminateRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TerminateRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExternalWorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ExternalWorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowOnly != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ChildWorkflowOnly)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TerminateWorkflowExecutionRequest_Decode(sr stream.Reader) (*shared.TerminateWorkflowExecutionRequest, error) { var v shared.TerminateWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a TerminateWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TerminateWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *TerminateWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TerminateRequest, err = _TerminateWorkflowExecutionRequest_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ExternalWorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ChildWorkflowOnly = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TerminateWorkflowExecutionRequest // struct. func (v *TerminateWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.TerminateRequest != nil { fields[i] = fmt.Sprintf("TerminateRequest: %v", v.TerminateRequest) i++ } if v.ExternalWorkflowExecution != nil { fields[i] = fmt.Sprintf("ExternalWorkflowExecution: %v", v.ExternalWorkflowExecution) i++ } if v.ChildWorkflowOnly != nil { fields[i] = fmt.Sprintf("ChildWorkflowOnly: %v", *(v.ChildWorkflowOnly)) i++ } return fmt.Sprintf("TerminateWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TerminateWorkflowExecutionRequest match the // provided TerminateWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *TerminateWorkflowExecutionRequest) Equals(rhs *TerminateWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.TerminateRequest == nil && rhs.TerminateRequest == nil) || (v.TerminateRequest != nil && rhs.TerminateRequest != nil && v.TerminateRequest.Equals(rhs.TerminateRequest))) { return false } if !((v.ExternalWorkflowExecution == nil && rhs.ExternalWorkflowExecution == nil) || (v.ExternalWorkflowExecution != nil && rhs.ExternalWorkflowExecution != nil && v.ExternalWorkflowExecution.Equals(rhs.ExternalWorkflowExecution))) { return false } if !_Bool_EqualsPtr(v.ChildWorkflowOnly, rhs.ChildWorkflowOnly) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TerminateWorkflowExecutionRequest. func (v *TerminateWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.TerminateRequest != nil { err = multierr.Append(err, enc.AddObject("terminateRequest", v.TerminateRequest)) } if v.ExternalWorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("externalWorkflowExecution", v.ExternalWorkflowExecution)) } if v.ChildWorkflowOnly != nil { enc.AddBool("childWorkflowOnly", *v.ChildWorkflowOnly) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *TerminateWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *TerminateWorkflowExecutionRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetTerminateRequest returns the value of TerminateRequest if it is set or its // zero value if it is unset. func (v *TerminateWorkflowExecutionRequest) GetTerminateRequest() (o *shared.TerminateWorkflowExecutionRequest) { if v != nil && v.TerminateRequest != nil { return v.TerminateRequest } return } // IsSetTerminateRequest returns true if TerminateRequest is not nil. func (v *TerminateWorkflowExecutionRequest) IsSetTerminateRequest() bool { return v != nil && v.TerminateRequest != nil } // GetExternalWorkflowExecution returns the value of ExternalWorkflowExecution if it is set or its // zero value if it is unset. func (v *TerminateWorkflowExecutionRequest) GetExternalWorkflowExecution() (o *shared.WorkflowExecution) { if v != nil && v.ExternalWorkflowExecution != nil { return v.ExternalWorkflowExecution } return } // IsSetExternalWorkflowExecution returns true if ExternalWorkflowExecution is not nil. func (v *TerminateWorkflowExecutionRequest) IsSetExternalWorkflowExecution() bool { return v != nil && v.ExternalWorkflowExecution != nil } // GetChildWorkflowOnly returns the value of ChildWorkflowOnly if it is set or its // zero value if it is unset. func (v *TerminateWorkflowExecutionRequest) GetChildWorkflowOnly() (o bool) { if v != nil && v.ChildWorkflowOnly != nil { return *v.ChildWorkflowOnly } return } // IsSetChildWorkflowOnly returns true if ChildWorkflowOnly is not nil. func (v *TerminateWorkflowExecutionRequest) IsSetChildWorkflowOnly() bool { return v != nil && v.ChildWorkflowOnly != nil } // fields are required to encourage compact serialization, zeros are expected type WeightedRatelimitCalls struct { // number of allowed requests since last call. // assumed to be <1m or so, saturates at MAX_INT32. Allowed int32 `json:"allowed,required"` // number of rejected requests since last call. // assumed to be <1m or so, saturates at MAX_INT32. Rejected int32 `json:"rejected,required"` } // ToWire translates a WeightedRatelimitCalls struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WeightedRatelimitCalls) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueI32(v.Allowed), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ w, err = wire.NewValueI32(v.Rejected), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WeightedRatelimitCalls struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WeightedRatelimitCalls struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WeightedRatelimitCalls // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WeightedRatelimitCalls) FromWire(w wire.Value) error { var err error allowedIsSet := false rejectedIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { v.Allowed, err = field.Value.GetI32(), error(nil) if err != nil { return err } allowedIsSet = true } case 20: if field.Value.Type() == wire.TI32 { v.Rejected, err = field.Value.GetI32(), error(nil) if err != nil { return err } rejectedIsSet = true } } } if !allowedIsSet { return errors.New("field Allowed of WeightedRatelimitCalls is required") } if !rejectedIsSet { return errors.New("field Rejected of WeightedRatelimitCalls is required") } return nil } // Encode serializes a WeightedRatelimitCalls struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WeightedRatelimitCalls struct could not be encoded. func (v *WeightedRatelimitCalls) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(v.Allowed); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(v.Rejected); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a WeightedRatelimitCalls struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WeightedRatelimitCalls struct could not be generated from the wire // representation. func (v *WeightedRatelimitCalls) Decode(sr stream.Reader) error { allowedIsSet := false rejectedIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: v.Allowed, err = sr.ReadInt32() if err != nil { return err } allowedIsSet = true case fh.ID == 20 && fh.Type == wire.TI32: v.Rejected, err = sr.ReadInt32() if err != nil { return err } rejectedIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !allowedIsSet { return errors.New("field Allowed of WeightedRatelimitCalls is required") } if !rejectedIsSet { return errors.New("field Rejected of WeightedRatelimitCalls is required") } return nil } // String returns a readable string representation of a WeightedRatelimitCalls // struct. func (v *WeightedRatelimitCalls) String() string { if v == nil { return "" } var fields [2]string i := 0 fields[i] = fmt.Sprintf("Allowed: %v", v.Allowed) i++ fields[i] = fmt.Sprintf("Rejected: %v", v.Rejected) i++ return fmt.Sprintf("WeightedRatelimitCalls{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WeightedRatelimitCalls match the // provided WeightedRatelimitCalls. // // This function performs a deep comparison. func (v *WeightedRatelimitCalls) Equals(rhs *WeightedRatelimitCalls) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Allowed == rhs.Allowed) { return false } if !(v.Rejected == rhs.Rejected) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WeightedRatelimitCalls. func (v *WeightedRatelimitCalls) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddInt32("allowed", v.Allowed) enc.AddInt32("rejected", v.Rejected) return err } // GetAllowed returns the value of Allowed if it is set or its // zero value if it is unset. func (v *WeightedRatelimitCalls) GetAllowed() (o int32) { if v != nil { o = v.Allowed } return } // GetRejected returns the value of Rejected if it is set or its // zero value if it is unset. func (v *WeightedRatelimitCalls) GetRejected() (o int32) { if v != nil { o = v.Rejected } return } // first impl of ratelimiting data, result from aggregator to limiter. // // used in an Any with ValueType: WeightedRatelimitQuotasAnyType type WeightedRatelimitQuotas struct { // RPS-weights to allow per key Quotas map[string]float64 `json:"quotas,required"` } type _Map_String_Double_MapItemList map[string]float64 func (m _Map_String_Double_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := wire.NewValueDouble(v), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_Double_MapItemList) Size() int { return len(m) } func (_Map_String_Double_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_Double_MapItemList) ValueType() wire.Type { return wire.TDouble } func (_Map_String_Double_MapItemList) Close() {} // ToWire translates a WeightedRatelimitQuotas struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WeightedRatelimitQuotas) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Quotas == nil { return w, errors.New("field Quotas of WeightedRatelimitQuotas is required") } w, err = wire.NewValueMap(_Map_String_Double_MapItemList(v.Quotas)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Map_String_Double_Read(m wire.MapItemList) (map[string]float64, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TDouble { return nil, nil } o := make(map[string]float64, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := x.Value.GetDouble(), error(nil) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a WeightedRatelimitQuotas struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WeightedRatelimitQuotas struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WeightedRatelimitQuotas // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WeightedRatelimitQuotas) FromWire(w wire.Value) error { var err error quotasIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.Quotas, err = _Map_String_Double_Read(field.Value.GetMap()) if err != nil { return err } quotasIsSet = true } } } if !quotasIsSet { return errors.New("field Quotas of WeightedRatelimitQuotas is required") } return nil } func _Map_String_Double_Encode(val map[string]float64, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TDouble, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if err := sw.WriteString(k); err != nil { return err } if err := sw.WriteDouble(v); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a WeightedRatelimitQuotas struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WeightedRatelimitQuotas struct could not be encoded. func (v *WeightedRatelimitQuotas) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Quotas == nil { return errors.New("field Quotas of WeightedRatelimitQuotas is required") } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_Double_Encode(v.Quotas, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } func _Map_String_Double_Decode(sr stream.Reader) (map[string]float64, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TDouble { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]float64, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := sr.ReadDouble() if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a WeightedRatelimitQuotas struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WeightedRatelimitQuotas struct could not be generated from the wire // representation. func (v *WeightedRatelimitQuotas) Decode(sr stream.Reader) error { quotasIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.Quotas, err = _Map_String_Double_Decode(sr) if err != nil { return err } quotasIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !quotasIsSet { return errors.New("field Quotas of WeightedRatelimitQuotas is required") } return nil } // String returns a readable string representation of a WeightedRatelimitQuotas // struct. func (v *WeightedRatelimitQuotas) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Quotas: %v", v.Quotas) i++ return fmt.Sprintf("WeightedRatelimitQuotas{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_Double_Equals(lhs, rhs map[string]float64) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !(lv == rv) { return false } } return true } // Equals returns true if all the fields of this WeightedRatelimitQuotas match the // provided WeightedRatelimitQuotas. // // This function performs a deep comparison. func (v *WeightedRatelimitQuotas) Equals(rhs *WeightedRatelimitQuotas) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Map_String_Double_Equals(v.Quotas, rhs.Quotas) { return false } return true } type _Map_String_Double_Zapper map[string]float64 // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_Double_Zapper. func (m _Map_String_Double_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { enc.AddFloat64((string)(k), v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WeightedRatelimitQuotas. func (v *WeightedRatelimitQuotas) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } err = multierr.Append(err, enc.AddObject("quotas", (_Map_String_Double_Zapper)(v.Quotas))) return err } // GetQuotas returns the value of Quotas if it is set or its // zero value if it is unset. func (v *WeightedRatelimitQuotas) GetQuotas() (o map[string]float64) { if v != nil { o = v.Quotas } return } // IsSetQuotas returns true if Quotas is not nil. func (v *WeightedRatelimitQuotas) IsSetQuotas() bool { return v != nil && v.Quotas != nil } // first impl of ratelimiting data, collected by limiters and sent to aggregators. // // used in an Any with ValueType: WeightedRatelimitUsageAnyType type WeightedRatelimitUsage struct { // unique, stable identifier of the calling host, to identify future data from the same host Caller string `json:"caller,required"` // milliseconds since last update call. expected to be on the order of a few seconds or less. ElapsedMS int32 `json:"elapsedMS,required"` // per key, number of allowed vs rejected calls since last update. Calls map[string]*WeightedRatelimitCalls `json:"calls,required"` } type _Map_String_WeightedRatelimitCalls_MapItemList map[string]*WeightedRatelimitCalls func (m _Map_String_WeightedRatelimitCalls_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*WeightedRatelimitCalls', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_WeightedRatelimitCalls_MapItemList) Size() int { return len(m) } func (_Map_String_WeightedRatelimitCalls_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_WeightedRatelimitCalls_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_WeightedRatelimitCalls_MapItemList) Close() {} // ToWire translates a WeightedRatelimitUsage struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WeightedRatelimitUsage) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Caller), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ w, err = wire.NewValueI32(v.ElapsedMS), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ if v.Calls == nil { return w, errors.New("field Calls of WeightedRatelimitUsage is required") } w, err = wire.NewValueMap(_Map_String_WeightedRatelimitCalls_MapItemList(v.Calls)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WeightedRatelimitCalls_Read(w wire.Value) (*WeightedRatelimitCalls, error) { var v WeightedRatelimitCalls err := v.FromWire(w) return &v, err } func _Map_String_WeightedRatelimitCalls_Read(m wire.MapItemList) (map[string]*WeightedRatelimitCalls, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*WeightedRatelimitCalls, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _WeightedRatelimitCalls_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a WeightedRatelimitUsage struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WeightedRatelimitUsage struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WeightedRatelimitUsage // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WeightedRatelimitUsage) FromWire(w wire.Value) error { var err error callerIsSet := false elapsedMSIsSet := false callsIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.Caller, err = field.Value.GetString(), error(nil) if err != nil { return err } callerIsSet = true } case 20: if field.Value.Type() == wire.TI32 { v.ElapsedMS, err = field.Value.GetI32(), error(nil) if err != nil { return err } elapsedMSIsSet = true } case 30: if field.Value.Type() == wire.TMap { v.Calls, err = _Map_String_WeightedRatelimitCalls_Read(field.Value.GetMap()) if err != nil { return err } callsIsSet = true } } } if !callerIsSet { return errors.New("field Caller of WeightedRatelimitUsage is required") } if !elapsedMSIsSet { return errors.New("field ElapsedMS of WeightedRatelimitUsage is required") } if !callsIsSet { return errors.New("field Calls of WeightedRatelimitUsage is required") } return nil } func _Map_String_WeightedRatelimitCalls_Encode(val map[string]*WeightedRatelimitCalls, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*WeightedRatelimitCalls', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a WeightedRatelimitUsage struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WeightedRatelimitUsage struct could not be encoded. func (v *WeightedRatelimitUsage) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Caller); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(v.ElapsedMS); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if v.Calls == nil { return errors.New("field Calls of WeightedRatelimitUsage is required") } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TMap}); err != nil { return err } if err := _Map_String_WeightedRatelimitCalls_Encode(v.Calls, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } func _WeightedRatelimitCalls_Decode(sr stream.Reader) (*WeightedRatelimitCalls, error) { var v WeightedRatelimitCalls err := v.Decode(sr) return &v, err } func _Map_String_WeightedRatelimitCalls_Decode(sr stream.Reader) (map[string]*WeightedRatelimitCalls, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*WeightedRatelimitCalls, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _WeightedRatelimitCalls_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a WeightedRatelimitUsage struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WeightedRatelimitUsage struct could not be generated from the wire // representation. func (v *WeightedRatelimitUsage) Decode(sr stream.Reader) error { callerIsSet := false elapsedMSIsSet := false callsIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.Caller, err = sr.ReadString() if err != nil { return err } callerIsSet = true case fh.ID == 20 && fh.Type == wire.TI32: v.ElapsedMS, err = sr.ReadInt32() if err != nil { return err } elapsedMSIsSet = true case fh.ID == 30 && fh.Type == wire.TMap: v.Calls, err = _Map_String_WeightedRatelimitCalls_Decode(sr) if err != nil { return err } callsIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !callerIsSet { return errors.New("field Caller of WeightedRatelimitUsage is required") } if !elapsedMSIsSet { return errors.New("field ElapsedMS of WeightedRatelimitUsage is required") } if !callsIsSet { return errors.New("field Calls of WeightedRatelimitUsage is required") } return nil } // String returns a readable string representation of a WeightedRatelimitUsage // struct. func (v *WeightedRatelimitUsage) String() string { if v == nil { return "" } var fields [3]string i := 0 fields[i] = fmt.Sprintf("Caller: %v", v.Caller) i++ fields[i] = fmt.Sprintf("ElapsedMS: %v", v.ElapsedMS) i++ fields[i] = fmt.Sprintf("Calls: %v", v.Calls) i++ return fmt.Sprintf("WeightedRatelimitUsage{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_WeightedRatelimitCalls_Equals(lhs, rhs map[string]*WeightedRatelimitCalls) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this WeightedRatelimitUsage match the // provided WeightedRatelimitUsage. // // This function performs a deep comparison. func (v *WeightedRatelimitUsage) Equals(rhs *WeightedRatelimitUsage) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Caller == rhs.Caller) { return false } if !(v.ElapsedMS == rhs.ElapsedMS) { return false } if !_Map_String_WeightedRatelimitCalls_Equals(v.Calls, rhs.Calls) { return false } return true } type _Map_String_WeightedRatelimitCalls_Zapper map[string]*WeightedRatelimitCalls // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_WeightedRatelimitCalls_Zapper. func (m _Map_String_WeightedRatelimitCalls_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WeightedRatelimitUsage. func (v *WeightedRatelimitUsage) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("caller", v.Caller) enc.AddInt32("elapsedMS", v.ElapsedMS) err = multierr.Append(err, enc.AddObject("calls", (_Map_String_WeightedRatelimitCalls_Zapper)(v.Calls))) return err } // GetCaller returns the value of Caller if it is set or its // zero value if it is unset. func (v *WeightedRatelimitUsage) GetCaller() (o string) { if v != nil { o = v.Caller } return } // GetElapsedMS returns the value of ElapsedMS if it is set or its // zero value if it is unset. func (v *WeightedRatelimitUsage) GetElapsedMS() (o int32) { if v != nil { o = v.ElapsedMS } return } // GetCalls returns the value of Calls if it is set or its // zero value if it is unset. func (v *WeightedRatelimitUsage) GetCalls() (o map[string]*WeightedRatelimitCalls) { if v != nil { o = v.Calls } return } // IsSetCalls returns true if Calls is not nil. func (v *WeightedRatelimitUsage) IsSetCalls() bool { return v != nil && v.Calls != nil } type WeightedRatelimitUsageQuotaEntry struct { // Amount of the quota that the receiving host can use, between 0 and 1 Weight float64 `json:"weight,required"` // RPS estimated across the whole cluster Used float64 `json:"used,required"` } // ToWire translates a WeightedRatelimitUsageQuotaEntry struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WeightedRatelimitUsageQuotaEntry) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueDouble(v.Weight), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ w, err = wire.NewValueDouble(v.Used), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WeightedRatelimitUsageQuotaEntry struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WeightedRatelimitUsageQuotaEntry struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WeightedRatelimitUsageQuotaEntry // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WeightedRatelimitUsageQuotaEntry) FromWire(w wire.Value) error { var err error weightIsSet := false usedIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TDouble { v.Weight, err = field.Value.GetDouble(), error(nil) if err != nil { return err } weightIsSet = true } case 20: if field.Value.Type() == wire.TDouble { v.Used, err = field.Value.GetDouble(), error(nil) if err != nil { return err } usedIsSet = true } } } if !weightIsSet { return errors.New("field Weight of WeightedRatelimitUsageQuotaEntry is required") } if !usedIsSet { return errors.New("field Used of WeightedRatelimitUsageQuotaEntry is required") } return nil } // Encode serializes a WeightedRatelimitUsageQuotaEntry struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WeightedRatelimitUsageQuotaEntry struct could not be encoded. func (v *WeightedRatelimitUsageQuotaEntry) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(v.Weight); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(v.Used); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a WeightedRatelimitUsageQuotaEntry struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WeightedRatelimitUsageQuotaEntry struct could not be generated from the wire // representation. func (v *WeightedRatelimitUsageQuotaEntry) Decode(sr stream.Reader) error { weightIsSet := false usedIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TDouble: v.Weight, err = sr.ReadDouble() if err != nil { return err } weightIsSet = true case fh.ID == 20 && fh.Type == wire.TDouble: v.Used, err = sr.ReadDouble() if err != nil { return err } usedIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !weightIsSet { return errors.New("field Weight of WeightedRatelimitUsageQuotaEntry is required") } if !usedIsSet { return errors.New("field Used of WeightedRatelimitUsageQuotaEntry is required") } return nil } // String returns a readable string representation of a WeightedRatelimitUsageQuotaEntry // struct. func (v *WeightedRatelimitUsageQuotaEntry) String() string { if v == nil { return "" } var fields [2]string i := 0 fields[i] = fmt.Sprintf("Weight: %v", v.Weight) i++ fields[i] = fmt.Sprintf("Used: %v", v.Used) i++ return fmt.Sprintf("WeightedRatelimitUsageQuotaEntry{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WeightedRatelimitUsageQuotaEntry match the // provided WeightedRatelimitUsageQuotaEntry. // // This function performs a deep comparison. func (v *WeightedRatelimitUsageQuotaEntry) Equals(rhs *WeightedRatelimitUsageQuotaEntry) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Weight == rhs.Weight) { return false } if !(v.Used == rhs.Used) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WeightedRatelimitUsageQuotaEntry. func (v *WeightedRatelimitUsageQuotaEntry) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddFloat64("weight", v.Weight) enc.AddFloat64("used", v.Used) return err } // GetWeight returns the value of Weight if it is set or its // zero value if it is unset. func (v *WeightedRatelimitUsageQuotaEntry) GetWeight() (o float64) { if v != nil { o = v.Weight } return } // GetUsed returns the value of Used if it is set or its // zero value if it is unset. func (v *WeightedRatelimitUsageQuotaEntry) GetUsed() (o float64) { if v != nil { o = v.Used } return } // second impl, includes unused-RPS data so limiters can decide if they // want to allow exceeding limits when there is free space. // // used in an Any with ValueType: WeightedRatelimitUsageQuotasAnyType type WeightedRatelimitUsageQuotas struct { // RPS weights and total usage per key Quotas map[string]*WeightedRatelimitUsageQuotaEntry `json:"quotas,required"` } type _Map_String_WeightedRatelimitUsageQuotaEntry_MapItemList map[string]*WeightedRatelimitUsageQuotaEntry func (m _Map_String_WeightedRatelimitUsageQuotaEntry_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*WeightedRatelimitUsageQuotaEntry', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_WeightedRatelimitUsageQuotaEntry_MapItemList) Size() int { return len(m) } func (_Map_String_WeightedRatelimitUsageQuotaEntry_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_WeightedRatelimitUsageQuotaEntry_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_WeightedRatelimitUsageQuotaEntry_MapItemList) Close() {} // ToWire translates a WeightedRatelimitUsageQuotas struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WeightedRatelimitUsageQuotas) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Quotas == nil { return w, errors.New("field Quotas of WeightedRatelimitUsageQuotas is required") } w, err = wire.NewValueMap(_Map_String_WeightedRatelimitUsageQuotaEntry_MapItemList(v.Quotas)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WeightedRatelimitUsageQuotaEntry_Read(w wire.Value) (*WeightedRatelimitUsageQuotaEntry, error) { var v WeightedRatelimitUsageQuotaEntry err := v.FromWire(w) return &v, err } func _Map_String_WeightedRatelimitUsageQuotaEntry_Read(m wire.MapItemList) (map[string]*WeightedRatelimitUsageQuotaEntry, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*WeightedRatelimitUsageQuotaEntry, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _WeightedRatelimitUsageQuotaEntry_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a WeightedRatelimitUsageQuotas struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WeightedRatelimitUsageQuotas struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WeightedRatelimitUsageQuotas // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WeightedRatelimitUsageQuotas) FromWire(w wire.Value) error { var err error quotasIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.Quotas, err = _Map_String_WeightedRatelimitUsageQuotaEntry_Read(field.Value.GetMap()) if err != nil { return err } quotasIsSet = true } } } if !quotasIsSet { return errors.New("field Quotas of WeightedRatelimitUsageQuotas is required") } return nil } func _Map_String_WeightedRatelimitUsageQuotaEntry_Encode(val map[string]*WeightedRatelimitUsageQuotaEntry, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*WeightedRatelimitUsageQuotaEntry', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a WeightedRatelimitUsageQuotas struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WeightedRatelimitUsageQuotas struct could not be encoded. func (v *WeightedRatelimitUsageQuotas) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Quotas == nil { return errors.New("field Quotas of WeightedRatelimitUsageQuotas is required") } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_WeightedRatelimitUsageQuotaEntry_Encode(v.Quotas, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } func _WeightedRatelimitUsageQuotaEntry_Decode(sr stream.Reader) (*WeightedRatelimitUsageQuotaEntry, error) { var v WeightedRatelimitUsageQuotaEntry err := v.Decode(sr) return &v, err } func _Map_String_WeightedRatelimitUsageQuotaEntry_Decode(sr stream.Reader) (map[string]*WeightedRatelimitUsageQuotaEntry, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*WeightedRatelimitUsageQuotaEntry, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _WeightedRatelimitUsageQuotaEntry_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a WeightedRatelimitUsageQuotas struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WeightedRatelimitUsageQuotas struct could not be generated from the wire // representation. func (v *WeightedRatelimitUsageQuotas) Decode(sr stream.Reader) error { quotasIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.Quotas, err = _Map_String_WeightedRatelimitUsageQuotaEntry_Decode(sr) if err != nil { return err } quotasIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !quotasIsSet { return errors.New("field Quotas of WeightedRatelimitUsageQuotas is required") } return nil } // String returns a readable string representation of a WeightedRatelimitUsageQuotas // struct. func (v *WeightedRatelimitUsageQuotas) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Quotas: %v", v.Quotas) i++ return fmt.Sprintf("WeightedRatelimitUsageQuotas{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_WeightedRatelimitUsageQuotaEntry_Equals(lhs, rhs map[string]*WeightedRatelimitUsageQuotaEntry) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this WeightedRatelimitUsageQuotas match the // provided WeightedRatelimitUsageQuotas. // // This function performs a deep comparison. func (v *WeightedRatelimitUsageQuotas) Equals(rhs *WeightedRatelimitUsageQuotas) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Map_String_WeightedRatelimitUsageQuotaEntry_Equals(v.Quotas, rhs.Quotas) { return false } return true } type _Map_String_WeightedRatelimitUsageQuotaEntry_Zapper map[string]*WeightedRatelimitUsageQuotaEntry // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_WeightedRatelimitUsageQuotaEntry_Zapper. func (m _Map_String_WeightedRatelimitUsageQuotaEntry_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WeightedRatelimitUsageQuotas. func (v *WeightedRatelimitUsageQuotas) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } err = multierr.Append(err, enc.AddObject("quotas", (_Map_String_WeightedRatelimitUsageQuotaEntry_Zapper)(v.Quotas))) return err } // GetQuotas returns the value of Quotas if it is set or its // zero value if it is unset. func (v *WeightedRatelimitUsageQuotas) GetQuotas() (o map[string]*WeightedRatelimitUsageQuotaEntry) { if v != nil { o = v.Quotas } return } // IsSetQuotas returns true if Quotas is not nil. func (v *WeightedRatelimitUsageQuotas) IsSetQuotas() bool { return v != nil && v.Quotas != nil } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "history", Package: "github.com/uber/cadence/.gen/go/history", FilePath: "history.thrift", SHA1: "4f9bc03480287dbdd2c4c579e95be64a2750f07a", Includes: []*thriftreflect.ThriftModule{ replicator.ThriftModule, shared.ThriftModule, }, Raw: rawIDL, } const rawIDL = "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\ninclude \"shared.thrift\"\ninclude \"replicator.thrift\"\n\nnamespace java com.uber.cadence.history\n\nexception EventAlreadyStartedError {\n 1: required string message\n}\n\nexception ShardOwnershipLostError {\n 10: optional string message\n 20: optional string owner\n}\n\nstruct ParentExecutionInfo {\n 10: optional string domainUUID\n 15: optional string domain\n 20: optional shared.WorkflowExecution execution\n 30: optional i64 (js.type = \"Long\") initiatedId\n}\n\nstruct StartWorkflowExecutionRequest {\n 10: optional string domainUUID\n 20: optional shared.StartWorkflowExecutionRequest startRequest\n 30: optional ParentExecutionInfo parentExecutionInfo\n 40: optional i32 attempt\n 50: optional i64 (js.type = \"Long\") expirationTimestamp\n 55: optional shared.ContinueAsNewInitiator continueAsNewInitiator\n 56: optional string continuedFailureReason\n 57: optional binary continuedFailureDetails\n 58: optional binary lastCompletionResult\n 60: optional i32 firstDecisionTaskBackoffSeconds\n 62: optional map partitionConfig\n}\n\nstruct DescribeMutableStateRequest{\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution execution\n}\n\nstruct DescribeMutableStateResponse{\n 30: optional string mutableStateInCache\n 40: optional string mutableStateInDatabase\n}\n\nstruct GetMutableStateRequest {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution execution\n 30: optional i64 (js.type = \"Long\") expectedNextEventId\n 40: optional binary currentBranchToken\n 50: optional shared.VersionHistoryItem versionHistoryItem\n}\n\nstruct GetMutableStateResponse {\n 10: optional shared.WorkflowExecution execution\n 20: optional shared.WorkflowType workflowType\n 30: optional i64 (js.type = \"Long\") NextEventId\n 35: optional i64 (js.type = \"Long\") PreviousStartedEventId\n 40: optional i64 (js.type = \"Long\") LastFirstEventId\n 50: optional shared.TaskList taskList\n 60: optional shared.TaskList stickyTaskList\n 70: optional string clientLibraryVersion\n 80: optional string clientFeatureVersion\n 90: optional string clientImpl\n //TODO: isWorkflowRunning is deprecating. workflowState is going replace this field\n 100: optional bool isWorkflowRunning\n 110: optional i32 stickyTaskListScheduleToStartTimeout\n 120: optional i32 eventStoreVersion\n 130: optional binary currentBranchToken\n // TODO: when migrating to gRPC, make this a enum\n // TODO: when migrating to gRPC, unify internal & external representation\n // NOTE: workflowState & workflowCloseState are the same as persistence representation\n 150: optional i32 workflowState\n 160: optional i32 workflowCloseState\n 170: optional shared.VersionHistories versionHistories\n 180: optional bool isStickyTaskListEnabled\n 190: optional i64 (js.type = \"Long\") historySize\n}\n\nstruct PollMutableStateRequest {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution execution\n 30: optional i64 (js.type = \"Long\") expectedNextEventId\n 40: optional binary currentBranchToken\n}\n\nstruct PollMutableStateResponse {\n 10: optional shared.WorkflowExecution execution\n 20: optional shared.WorkflowType workflowType\n 30: optional i64 (js.type = \"Long\") NextEventId\n 35: optional i64 (js.type = \"Long\") PreviousStartedEventId\n 40: optional i64 (js.type = \"Long\") LastFirstEventId\n 50: optional shared.TaskList taskList\n 60: optional shared.TaskList stickyTaskList\n 70: optional string clientLibraryVersion\n 80: optional string clientFeatureVersion\n 90: optional string clientImpl\n 100: optional i32 stickyTaskListScheduleToStartTimeout\n 110: optional binary currentBranchToken\n 130: optional shared.VersionHistories versionHistories\n // TODO: when migrating to gRPC, make this a enum\n // TODO: when migrating to gRPC, unify internal & external representation\n // NOTE: workflowState & workflowCloseState are the same as persistence representation\n 140: optional i32 workflowState\n 150: optional i32 workflowCloseState\n}\n\nstruct ResetStickyTaskListRequest {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution execution\n}\n\nstruct ResetStickyTaskListResponse {\n // The reason to keep this response is to allow returning\n // information in the future.\n}\n\nstruct RespondDecisionTaskCompletedRequest {\n 10: optional string domainUUID\n 20: optional shared.RespondDecisionTaskCompletedRequest completeRequest\n}\n\nstruct RespondDecisionTaskCompletedResponse {\n 10: optional RecordDecisionTaskStartedResponse startedResponse\n 20: optional map activitiesToDispatchLocally\n}\n\nstruct RespondDecisionTaskFailedRequest {\n 10: optional string domainUUID\n 20: optional shared.RespondDecisionTaskFailedRequest failedRequest\n}\n\nstruct RecordActivityTaskHeartbeatRequest {\n 10: optional string domainUUID\n 20: optional shared.RecordActivityTaskHeartbeatRequest heartbeatRequest\n}\n\nstruct RespondActivityTaskCompletedRequest {\n 10: optional string domainUUID\n 20: optional shared.RespondActivityTaskCompletedRequest completeRequest\n}\n\nstruct RespondActivityTaskFailedRequest {\n 10: optional string domainUUID\n 20: optional shared.RespondActivityTaskFailedRequest failedRequest\n}\n\nstruct RespondActivityTaskCanceledRequest {\n 10: optional string domainUUID\n 20: optional shared.RespondActivityTaskCanceledRequest cancelRequest\n}\n\nstruct RefreshWorkflowTasksRequest {\n 10: optional string domainUIID\n 20: optional shared.RefreshWorkflowTasksRequest request\n}\n\nstruct RecordActivityTaskStartedRequest {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution workflowExecution\n 30: optional i64 (js.type = \"Long\") scheduleId\n 40: optional i64 (js.type = \"Long\") taskId\n 45: optional string requestId // Unique id of each poll request. Used to ensure at most once delivery of tasks.\n 50: optional shared.PollForActivityTaskRequest pollRequest\n}\n\nstruct RecordActivityTaskStartedResponse {\n 20: optional shared.HistoryEvent scheduledEvent\n 30: optional i64 (js.type = \"Long\") startedTimestamp\n 40: optional i64 (js.type = \"Long\") attempt\n 50: optional i64 (js.type = \"Long\") scheduledTimestampOfThisAttempt\n 60: optional binary heartbeatDetails\n 70: optional shared.WorkflowType workflowType\n 80: optional string workflowDomain\n}\n\nstruct RecordDecisionTaskStartedRequest {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution workflowExecution\n 30: optional i64 (js.type = \"Long\") scheduleId\n 40: optional i64 (js.type = \"Long\") taskId\n 45: optional string requestId // Unique id of each poll request. Used to ensure at most once delivery of tasks.\n 50: optional shared.PollForDecisionTaskRequest pollRequest\n}\n\nstruct RecordDecisionTaskStartedResponse {\n 10: optional shared.WorkflowType workflowType\n 20: optional i64 (js.type = \"Long\") previousStartedEventId\n 30: optional i64 (js.type = \"Long\") scheduledEventId\n 40: optional i64 (js.type = \"Long\") startedEventId\n 50: optional i64 (js.type = \"Long\") nextEventId\n 60: optional i64 (js.type = \"Long\") attempt\n 70: optional bool stickyExecutionEnabled\n 80: optional shared.TransientDecisionInfo decisionInfo\n 90: optional shared.TaskList WorkflowExecutionTaskList\n 100: optional i32 eventStoreVersion\n 110: optional binary branchToken\n 120: optional i64 (js.type = \"Long\") scheduledTimestamp\n 130: optional i64 (js.type = \"Long\") startedTimestamp\n 140: optional map queries\n 150: optional i64 (js.type = \"Long\") historySize\n}\n\nstruct SignalWorkflowExecutionRequest {\n 10: optional string domainUUID\n 20: optional shared.SignalWorkflowExecutionRequest signalRequest\n // workflow execution that requests this signal, for making sure\n // the workflow being signaled is actually a child of the workflow\n // making the request\n 30: optional shared.WorkflowExecution externalWorkflowExecution\n 40: optional bool childWorkflowOnly\n}\n\nstruct SignalWithStartWorkflowExecutionRequest {\n 10: optional string domainUUID\n 20: optional shared.SignalWithStartWorkflowExecutionRequest signalWithStartRequest\n 30: optional map partitionConfig\n}\n\nstruct RemoveSignalMutableStateRequest {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution workflowExecution\n 30: optional string requestId\n}\n\nstruct TerminateWorkflowExecutionRequest {\n 10: optional string domainUUID\n 20: optional shared.TerminateWorkflowExecutionRequest terminateRequest\n // workflow execution that requests this termination, for making sure\n // the workflow being terminated is actually a child of the workflow\n // making the request\n 30: optional shared.WorkflowExecution externalWorkflowExecution\n 40: optional bool childWorkflowOnly\n}\n\nstruct ResetWorkflowExecutionRequest {\n 10: optional string domainUUID\n 20: optional shared.ResetWorkflowExecutionRequest resetRequest\n}\n\nstruct RequestCancelWorkflowExecutionRequest {\n 10: optional string domainUUID\n 20: optional shared.RequestCancelWorkflowExecutionRequest cancelRequest\n // workflow execution that requests this cancellation, for making sure\n // the workflow being cancelled is actually a child of the workflow\n // making the request\n 30: optional i64 (js.type = \"Long\") externalInitiatedEventId\n 40: optional shared.WorkflowExecution externalWorkflowExecution\n 50: optional bool childWorkflowOnly\n}\n\nstruct ScheduleDecisionTaskRequest {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution workflowExecution\n 30: optional bool isFirstDecision\n}\n\nstruct DescribeWorkflowExecutionRequest {\n 10: optional string domainUUID\n 20: optional shared.DescribeWorkflowExecutionRequest request\n}\n\n/**\n* RecordChildExecutionCompletedRequest is used for reporting the completion of child execution to parent workflow\n* execution which started it. When a child execution is completed it creates this request and calls the\n* RecordChildExecutionCompleted API with the workflowExecution of parent. It also sets the completedExecution of the\n* child as it could potentially be different than the ChildExecutionStartedEvent of parent in the situation when\n* child creates multiple runs through ContinueAsNew before finally completing.\n**/\nstruct RecordChildExecutionCompletedRequest {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution workflowExecution\n 30: optional i64 (js.type = \"Long\") initiatedId\n 40: optional shared.WorkflowExecution completedExecution\n 50: optional shared.HistoryEvent completionEvent\n 60: optional i64 (js.type = \"Long\") startedId\n}\n\nstruct ReplicateEventsV2Request {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution workflowExecution\n 30: optional list versionHistoryItems\n 40: optional shared.DataBlob events\n // new run events does not need version history since there is no prior events\n 60: optional shared.DataBlob newRunEvents\n}\n\nstruct SyncShardStatusRequest {\n 10: optional string sourceCluster\n 20: optional i64 (js.type = \"Long\") shardId\n 30: optional i64 (js.type = \"Long\") timestamp\n}\n\nstruct SyncActivityRequest {\n 10: optional string domainId\n 20: optional string workflowId\n 30: optional string runId\n 40: optional i64 (js.type = \"Long\") version\n 50: optional i64 (js.type = \"Long\") scheduledId\n 60: optional i64 (js.type = \"Long\") scheduledTime\n 70: optional i64 (js.type = \"Long\") startedId\n 80: optional i64 (js.type = \"Long\") startedTime\n 90: optional i64 (js.type = \"Long\") lastHeartbeatTime\n 100: optional binary details\n 110: optional i32 attempt\n 120: optional string lastFailureReason\n 130: optional string lastWorkerIdentity\n 140: optional binary lastFailureDetails\n 150: optional shared.VersionHistory versionHistory\n}\n\nstruct QueryWorkflowRequest {\n 10: optional string domainUUID\n 20: optional shared.QueryWorkflowRequest request\n}\n\nstruct QueryWorkflowResponse {\n 10: optional shared.QueryWorkflowResponse response\n}\n\nstruct ReapplyEventsRequest {\n 10: optional string domainUUID\n 20: optional shared.ReapplyEventsRequest request\n}\n\nstruct FailoverMarkerToken {\n 10: optional list shardIDs\n 20: optional replicator.FailoverMarkerAttributes failoverMarker\n}\n\nstruct NotifyFailoverMarkersRequest {\n 10: optional list failoverMarkerTokens\n}\n\nstruct ProcessingQueueStates {\n 10: optional map> statesByCluster\n}\n\nstruct ProcessingQueueState {\n 10: optional i32 level\n 20: optional i64 ackLevel\n 30: optional i64 maxLevel\n 40: optional DomainFilter domainFilter\n}\n\nstruct DomainFilter {\n 10: optional list domainIDs\n 20: optional bool reverseMatch\n}\n\nstruct GetFailoverInfoRequest {\n 10: optional string domainID\n}\n\nstruct GetFailoverInfoResponse {\n 10: optional i32 completedShardCount\n 20: optional list pendingShards\n}\n\nstruct RatelimitUpdateRequest {\n /**\n * impl-specific data.\n *\n * likely some simple top-level keys and then either:\n * - map\n * - list\n *\n * this is a single blob rather than a collection to save on\n * repeated serialization of the type name, and to allow impls\n * to choose whatever structures are most-convenient for them.\n */\n 10: optional shared.Any data\n}\n\nstruct RatelimitUpdateResponse {\n /**\n * impl-specific data.\n *\n * likely some simple top-level keys and then either:\n * - map\n * - list\n *\n * this is a single blob rather than a collection to save on\n * repeated serialization of the type name, and to allow impls\n * to choose whatever structures are most-convenient for them.\n */\n 10: optional shared.Any data\n}\n\n/**\n* first impl of ratelimiting data, collected by limiters and sent to aggregators.\n*\n* used in an Any with ValueType: WeightedRatelimitUsageAnyType\n*/\nstruct WeightedRatelimitUsage {\n /** unique, stable identifier of the calling host, to identify future data from the same host */\n 10: required string caller\n /** milliseconds since last update call. expected to be on the order of a few seconds or less. */\n 20: required i32 elapsedMS\n /** per key, number of allowed vs rejected calls since last update. */\n 30: required map calls\n}\n\n/** Any{ValueType} identifier for WeightedRatelimitUsage data */\nconst string WeightedRatelimitUsageAnyType = \"cadence:loadbalanced:update_request\"\n\n/** fields are required to encourage compact serialization, zeros are expected */\nstruct WeightedRatelimitCalls {\n /**\n * number of allowed requests since last call.\n * assumed to be <1m or so, saturates at MAX_INT32.\n */\n 10: required i32 allowed\n /**\n * number of rejected requests since last call.\n * assumed to be <1m or so, saturates at MAX_INT32.\n */\n 20: required i32 rejected\n}\n\n/**\n* first impl of ratelimiting data, result from aggregator to limiter.\n*\n* used in an Any with ValueType: WeightedRatelimitQuotasAnyType\n*/\nstruct WeightedRatelimitQuotas {\n /** RPS-weights to allow per key */\n 10: required map quotas\n}\n\n/** Any{ValueType} identifier for WeightedRatelimitQuotas data */\nconst string WeightedRatelimitQuotasAnyType = \"cadence:loadbalanced:update_response\"\n\n/**\n* second impl, includes unused-RPS data so limiters can decide if they\n* want to allow exceeding limits when there is free space.\n*\n* used in an Any with ValueType: WeightedRatelimitUsageQuotasAnyType\n*/\nstruct WeightedRatelimitUsageQuotas {\n /** RPS weights and total usage per key */\n 10: required map quotas\n}\n\nstruct WeightedRatelimitUsageQuotaEntry {\n /** Amount of the quota that the receiving host can use, between 0 and 1 */\n 10: required double weight\n /** RPS estimated across the whole cluster */\n 20: required double used\n}\n\nconst string WeightedRatelimitUsageQuotasAnyType = \"cadence:loadbalanced:update_response_used\"\n\n/**\n* HistoryService provides API to start a new long running workflow instance, as well as query and update the history\n* of workflow instances already created.\n**/\nservice HistoryService {\n /**\n * StartWorkflowExecution starts a new long running workflow instance. It will create the instance with\n * 'WorkflowExecutionStarted' event in history and also schedule the first DecisionTask for the worker to make the\n * first decision for this instance. It will return 'WorkflowExecutionAlreadyStartedError', if an instance already\n * exists with same workflowId.\n **/\n shared.StartWorkflowExecutionResponse StartWorkflowExecution(1: StartWorkflowExecutionRequest startRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.WorkflowExecutionAlreadyStartedError sessionAlreadyExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * Returns the information from mutable state of workflow execution.\n * It fails with 'EntityNotExistError' if specified workflow execution in unknown to the service.\n * It returns CurrentBranchChangedError if the workflow version branch has changed.\n **/\n GetMutableStateResponse GetMutableState(1: GetMutableStateRequest getRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.CurrentBranchChangedError currentBranchChangedError,\n )\n\n /**\n * Returns the information from mutable state of workflow execution.\n * It fails with 'EntityNotExistError' if specified workflow execution in unknown to the service.\n * It returns CurrentBranchChangedError if the workflow version branch has changed.\n **/\n PollMutableStateResponse PollMutableState(1: PollMutableStateRequest pollRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.CurrentBranchChangedError currentBranchChangedError,\n )\n\n /**\n * Reset the sticky tasklist related information in mutable state of a given workflow.\n * Things cleared are:\n * 1. StickyTaskList\n * 2. StickyScheduleToStartTimeout\n * 3. ClientLibraryVersion\n * 4. ClientFeatureVersion\n * 5. ClientImpl\n **/\n ResetStickyTaskListResponse ResetStickyTaskList(1: ResetStickyTaskListRequest resetRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * RecordDecisionTaskStarted is called by the Matchingservice before it hands a decision task to the application worker in response to\n * a PollForDecisionTask call. It records in the history the event that the decision task has started. It will return 'EventAlreadyStartedError',\n * if the workflow's execution history already includes a record of the event starting.\n **/\n RecordDecisionTaskStartedResponse RecordDecisionTaskStarted(1: RecordDecisionTaskStartedRequest addRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: EventAlreadyStartedError eventAlreadyStartedError,\n 4: shared.EntityNotExistsError entityNotExistError,\n 5: ShardOwnershipLostError shardOwnershipLostError,\n 6: shared.DomainNotActiveError domainNotActiveError,\n 7: shared.LimitExceededError limitExceededError,\n 8: shared.ServiceBusyError serviceBusyError,\n 9: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * RecordActivityTaskStarted is called by the Matchingservice before it hands a decision task to the application worker in response to\n * a PollForActivityTask call. It records in the history the event that the decision task has started. It will return 'EventAlreadyStartedError',\n * if the workflow's execution history already includes a record of the event starting.\n **/\n RecordActivityTaskStartedResponse RecordActivityTaskStarted(1: RecordActivityTaskStartedRequest addRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: EventAlreadyStartedError eventAlreadyStartedError,\n 4: shared.EntityNotExistsError entityNotExistError,\n 5: ShardOwnershipLostError shardOwnershipLostError,\n 6: shared.DomainNotActiveError domainNotActiveError,\n 7: shared.LimitExceededError limitExceededError,\n 8: shared.ServiceBusyError serviceBusyError,\n 9: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * RespondDecisionTaskCompleted is called by application worker to complete a DecisionTask handed as a result of\n * 'PollForDecisionTask' API call. Completing a DecisionTask will result in new events for the workflow execution and\n * potentially new ActivityTask being created for corresponding decisions. It will also create a DecisionTaskCompleted\n * event in the history for that session. Use the 'taskToken' provided as response of PollForDecisionTask API call\n * for completing the DecisionTask.\n **/\n RespondDecisionTaskCompletedResponse RespondDecisionTaskCompleted(1: RespondDecisionTaskCompletedRequest completeRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * RespondDecisionTaskFailed is called by application worker to indicate failure. This results in\n * DecisionTaskFailedEvent written to the history and a new DecisionTask created. This API can be used by client to\n * either clear sticky tasklist or report ny panics during DecisionTask processing.\n **/\n void RespondDecisionTaskFailed(1: RespondDecisionTaskFailedRequest failedRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * RecordActivityTaskHeartbeat is called by application worker while it is processing an ActivityTask. If worker fails\n * to heartbeat within 'heartbeatTimeoutSeconds' interval for the ActivityTask, then it will be marked as timedout and\n * 'ActivityTaskTimedOut' event will be written to the workflow history. Calling 'RecordActivityTaskHeartbeat' will\n * fail with 'EntityNotExistsError' in such situations. Use the 'taskToken' provided as response of\n * PollForActivityTask API call for heartbeating.\n **/\n shared.RecordActivityTaskHeartbeatResponse RecordActivityTaskHeartbeat(1: RecordActivityTaskHeartbeatRequest heartbeatRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * RespondActivityTaskCompleted is called by application worker when it is done processing an ActivityTask. It will\n * result in a new 'ActivityTaskCompleted' event being written to the workflow history and a new DecisionTask\n * created for the workflow so new decisions could be made. Use the 'taskToken' provided as response of\n * PollForActivityTask API call for completion. It fails with 'EntityNotExistsError' if the taskToken is not valid\n * anymore due to activity timeout.\n **/\n void RespondActivityTaskCompleted(1: RespondActivityTaskCompletedRequest completeRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * RespondActivityTaskFailed is called by application worker when it is done processing an ActivityTask. It will\n * result in a new 'ActivityTaskFailed' event being written to the workflow history and a new DecisionTask\n * created for the workflow instance so new decisions could be made. Use the 'taskToken' provided as response of\n * PollForActivityTask API call for completion. It fails with 'EntityNotExistsError' if the taskToken is not valid\n * anymore due to activity timeout.\n **/\n void RespondActivityTaskFailed(1: RespondActivityTaskFailedRequest failRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * RespondActivityTaskCanceled is called by application worker when it is successfully canceled an ActivityTask. It will\n * result in a new 'ActivityTaskCanceled' event being written to the workflow history and a new DecisionTask\n * created for the workflow instance so new decisions could be made. Use the 'taskToken' provided as response of\n * PollForActivityTask API call for completion. It fails with 'EntityNotExistsError' if the taskToken is not valid\n * anymore due to activity timeout.\n **/\n void RespondActivityTaskCanceled(1: RespondActivityTaskCanceledRequest canceledRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * SignalWorkflowExecution is used to send a signal event to running workflow execution. This results in\n * WorkflowExecutionSignaled event recorded in the history and a decision task being created for the execution.\n **/\n void SignalWorkflowExecution(1: SignalWorkflowExecutionRequest signalRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.LimitExceededError limitExceededError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * SignalWithStartWorkflowExecution is used to ensure sending a signal event to a workflow execution.\n * If workflow is running, this results in WorkflowExecutionSignaled event recorded in the history\n * and a decision task being created for the execution.\n * If workflow is not running or not found, it will first try start workflow with given WorkflowIDResuePolicy,\n * and record WorkflowExecutionStarted and WorkflowExecutionSignaled event in case of success.\n * It will return `WorkflowExecutionAlreadyStartedError` if start workflow failed with given policy.\n **/\n shared.StartWorkflowExecutionResponse SignalWithStartWorkflowExecution(1: SignalWithStartWorkflowExecutionRequest signalWithStartRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: ShardOwnershipLostError shardOwnershipLostError,\n 4: shared.DomainNotActiveError domainNotActiveError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.WorkflowExecutionAlreadyStartedError workflowAlreadyStartedError,\n )\n\n /**\n * RemoveSignalMutableState is used to remove a signal request ID that was previously recorded. This is currently\n * used to clean execution info when signal decision finished.\n **/\n void RemoveSignalMutableState(1: RemoveSignalMutableStateRequest removeRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * TerminateWorkflowExecution terminates an existing workflow execution by recording WorkflowExecutionTerminated event\n * in the history and immediately terminating the execution instance.\n **/\n void TerminateWorkflowExecution(1: TerminateWorkflowExecutionRequest terminateRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * ResetWorkflowExecution reset an existing workflow execution by a firstEventID of a existing event batch\n * in the history and immediately terminating the current execution instance.\n * After reset, the history will grow from nextFirstEventID.\n **/\n shared.ResetWorkflowExecutionResponse ResetWorkflowExecution(1: ResetWorkflowExecutionRequest resetRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * RequestCancelWorkflowExecution is called by application worker when it wants to request cancellation of a workflow instance.\n * It will result in a new 'WorkflowExecutionCancelRequested' event being written to the workflow history and a new DecisionTask\n * created for the workflow instance so new decisions could be made. It fails with\n * 'WorkflowExecutionAlreadyCompletedError' if the workflow is not valid\n * anymore due to completion or with 'EntityNotExistsError' if worfklow doesn't exist.\n **/\n void RequestCancelWorkflowExecution(1: RequestCancelWorkflowExecutionRequest cancelRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.CancellationAlreadyRequestedError cancellationAlreadyRequestedError,\n 6: shared.DomainNotActiveError domainNotActiveError,\n 7: shared.LimitExceededError limitExceededError,\n 8: shared.ServiceBusyError serviceBusyError,\n 10: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * ScheduleDecisionTask is used for creating a decision task for already started workflow execution. This is mainly\n * used by transfer queue processor during the processing of StartChildWorkflowExecution task, where it first starts\n * child execution without creating the decision task and then calls this API after updating the mutable state of\n * parent execution.\n **/\n void ScheduleDecisionTask(1: ScheduleDecisionTaskRequest scheduleRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * RecordChildExecutionCompleted is used for reporting the completion of child workflow execution to parent.\n * This is mainly called by transfer queue processor during the processing of DeleteExecution task.\n **/\n void RecordChildExecutionCompleted(1: RecordChildExecutionCompletedRequest completionRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.LimitExceededError limitExceededError,\n 7: shared.ServiceBusyError serviceBusyError,\n 8: shared.WorkflowExecutionAlreadyCompletedError workflowExecutionAlreadyCompletedError,\n )\n\n /**\n * DescribeWorkflowExecution returns information about the specified workflow execution.\n **/\n shared.DescribeWorkflowExecutionResponse DescribeWorkflowExecution(1: DescribeWorkflowExecutionRequest describeRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n )\n\n void ReplicateEventsV2(1: ReplicateEventsV2Request replicateV2Request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.RetryTaskV2Error retryTaskError,\n 7: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * SyncShardStatus sync the status between shards\n **/\n void SyncShardStatus(1: SyncShardStatusRequest syncShardStatusRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * SyncActivity sync the activity status\n **/\n void SyncActivity(1: SyncActivityRequest syncActivityRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.ServiceBusyError serviceBusyError,\n 7: shared.RetryTaskV2Error retryTaskV2Error,\n )\n\n /**\n * DescribeMutableState returns information about the internal states of workflow mutable state.\n **/\n DescribeMutableStateResponse DescribeMutableState(1: DescribeMutableStateRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.AccessDeniedError accessDeniedError,\n 5: ShardOwnershipLostError shardOwnershipLostError,\n 6: shared.LimitExceededError limitExceededError,\n )\n\n /**\n * DescribeHistoryHost returns information about the internal states of a history host\n **/\n shared.DescribeHistoryHostResponse DescribeHistoryHost(1: shared.DescribeHistoryHostRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * CloseShard close the shard\n **/\n void CloseShard(1: shared.CloseShardRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * RemoveTask remove task based on type, taskid, shardid\n **/\n void RemoveTask(1: shared.RemoveTaskRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * ResetQueue reset processing queue state based on cluster name and type\n **/\n void ResetQueue(1: shared.ResetQueueRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * DescribeQueue return queue states based on cluster name and type\n **/\n shared.DescribeQueueResponse DescribeQueue(1: shared.DescribeQueueRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.AccessDeniedError accessDeniedError,\n )\n\n /**\n * GetReplicationMessages return replication messages based on the read level\n **/\n replicator.GetReplicationMessagesResponse GetReplicationMessages(1: replicator.GetReplicationMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.LimitExceededError limitExceededError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n )\n\n /**\n * GetDLQReplicationMessages return replication messages based on dlq info\n **/\n replicator.GetDLQReplicationMessagesResponse GetDLQReplicationMessages(1: replicator.GetDLQReplicationMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.EntityNotExistsError entityNotExistError,\n )\n\n /**\n * QueryWorkflow returns query result for a specified workflow execution\n **/\n QueryWorkflowResponse QueryWorkflow(1: QueryWorkflowRequest queryRequest)\n\tthrows (\n\t 1: shared.BadRequestError badRequestError,\n\t 2: shared.InternalServiceError internalServiceError,\n\t 3: shared.EntityNotExistsError entityNotExistError,\n\t 4: shared.QueryFailedError queryFailedError,\n\t 5: shared.LimitExceededError limitExceededError,\n\t 6: shared.ServiceBusyError serviceBusyError,\n\t 7: shared.ClientVersionNotSupportedError clientVersionNotSupportedError,\n\t)\n\n /**\n * ReapplyEvents applies stale events to the current workflow and current run\n **/\n void ReapplyEvents(1: ReapplyEventsRequest reapplyEventsRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.DomainNotActiveError domainNotActiveError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.ServiceBusyError serviceBusyError,\n 6: ShardOwnershipLostError shardOwnershipLostError,\n 7: shared.EntityNotExistsError entityNotExistError,\n )\n\n /**\n * RefreshWorkflowTasks refreshes all tasks of a workflow\n **/\n void RefreshWorkflowTasks(1: RefreshWorkflowTasksRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.DomainNotActiveError domainNotActiveError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n 5: shared.ServiceBusyError serviceBusyError,\n 6: shared.EntityNotExistsError entityNotExistError,\n )\n\n /**\n * ReadDLQMessages returns messages from DLQ\n **/\n replicator.ReadDLQMessagesResponse ReadDLQMessages(1: replicator.ReadDLQMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.EntityNotExistsError entityNotExistError,\n 5: ShardOwnershipLostError shardOwnershipLostError,\n )\n\n /**\n * PurgeDLQMessages purges messages from DLQ\n **/\n void PurgeDLQMessages(1: replicator.PurgeDLQMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.EntityNotExistsError entityNotExistError,\n 5: ShardOwnershipLostError shardOwnershipLostError,\n )\n\n /**\n * MergeDLQMessages merges messages from DLQ\n **/\n replicator.MergeDLQMessagesResponse MergeDLQMessages(1: replicator.MergeDLQMessagesRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.EntityNotExistsError entityNotExistError,\n 5: ShardOwnershipLostError shardOwnershipLostError,\n )\n\n /**\n * NotifyFailoverMarkers sends failover marker to the failover coordinator\n **/\n void NotifyFailoverMarkers(1: NotifyFailoverMarkersRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * GetCrossClusterTasks fetches cross cluster tasks\n **/\n shared.GetCrossClusterTasksResponse GetCrossClusterTasks(1: shared.GetCrossClusterTasksRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * RespondCrossClusterTasksCompleted responds the result of processing cross cluster tasks\n **/\n shared.RespondCrossClusterTasksCompletedResponse RespondCrossClusterTasksCompleted(1: shared.RespondCrossClusterTasksCompletedRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n )\n\n /**\n * GetFailoverInfo responds the failover info about an on-going graceful failover\n **/\n GetFailoverInfoResponse GetFailoverInfo(1: GetFailoverInfoRequest request)\n throws (\n 1: shared.InternalServiceError internalServiceError,\n 2: shared.ServiceBusyError serviceBusyError,\n 3: ShardOwnershipLostError shardOwnershipLostError,\n 4: shared.EntityNotExistsError entityNotExistError,\n )\n\n /**\n * RatelimitUpdate pushes global-ratelimiting data to aggregating hosts,\n * and returns data describing how to update the caller's ratelimits.\n *\n * For more details, see github.com/uber/cadence/common/quotas/global documentation.\n *\n * Request and response structures are intentionally loosely defined, to allow plugging\n * in externally-defined algorithms without changing protocol-level details.\n **/\n RatelimitUpdateResponse RatelimitUpdate(1: RatelimitUpdateRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: ShardOwnershipLostError shardOwnershipLostError,\n )\n}\n" // HistoryService_CloseShard_Args represents the arguments for the HistoryService.CloseShard function. // // The arguments for CloseShard are sent and received over the wire as this struct. type HistoryService_CloseShard_Args struct { Request *shared.CloseShardRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_CloseShard_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_CloseShard_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CloseShardRequest_Read(w wire.Value) (*shared.CloseShardRequest, error) { var v shared.CloseShardRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_CloseShard_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_CloseShard_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_CloseShard_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_CloseShard_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _CloseShardRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_CloseShard_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_CloseShard_Args struct could not be encoded. func (v *HistoryService_CloseShard_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CloseShardRequest_Decode(sr stream.Reader) (*shared.CloseShardRequest, error) { var v shared.CloseShardRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_CloseShard_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_CloseShard_Args struct could not be generated from the wire // representation. func (v *HistoryService_CloseShard_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _CloseShardRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_CloseShard_Args // struct. func (v *HistoryService_CloseShard_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_CloseShard_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_CloseShard_Args match the // provided HistoryService_CloseShard_Args. // // This function performs a deep comparison. func (v *HistoryService_CloseShard_Args) Equals(rhs *HistoryService_CloseShard_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_CloseShard_Args. func (v *HistoryService_CloseShard_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_CloseShard_Args) GetRequest() (o *shared.CloseShardRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_CloseShard_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "CloseShard" for this struct. func (v *HistoryService_CloseShard_Args) MethodName() string { return "CloseShard" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_CloseShard_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_CloseShard_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.CloseShard // function. var HistoryService_CloseShard_Helper = struct { // Args accepts the parameters of CloseShard in-order and returns // the arguments struct for the function. Args func( request *shared.CloseShardRequest, ) *HistoryService_CloseShard_Args // IsException returns true if the given error can be thrown // by CloseShard. // // An error can be thrown by CloseShard only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for CloseShard // given the error returned by it. The provided error may // be nil if CloseShard did not fail. // // This allows mapping errors returned by CloseShard into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // CloseShard // // err := CloseShard(args) // result, err := HistoryService_CloseShard_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from CloseShard: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_CloseShard_Result, error) // UnwrapResponse takes the result struct for CloseShard // and returns the erorr returned by it (if any). // // The error is non-nil only if CloseShard threw an // exception. // // result := deserialize(bytes) // err := HistoryService_CloseShard_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_CloseShard_Result) error }{} func init() { HistoryService_CloseShard_Helper.Args = func( request *shared.CloseShardRequest, ) *HistoryService_CloseShard_Args { return &HistoryService_CloseShard_Args{ Request: request, } } HistoryService_CloseShard_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.AccessDeniedError: return true default: return false } } HistoryService_CloseShard_Helper.WrapResponse = func(err error) (*HistoryService_CloseShard_Result, error) { if err == nil { return &HistoryService_CloseShard_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_CloseShard_Result.BadRequestError") } return &HistoryService_CloseShard_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_CloseShard_Result.InternalServiceError") } return &HistoryService_CloseShard_Result{InternalServiceError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_CloseShard_Result.AccessDeniedError") } return &HistoryService_CloseShard_Result{AccessDeniedError: e}, nil } return nil, err } HistoryService_CloseShard_Helper.UnwrapResponse = func(result *HistoryService_CloseShard_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // HistoryService_CloseShard_Result represents the result of a HistoryService.CloseShard function call. // // The result of a CloseShard execution is sent and received over the wire as this struct. type HistoryService_CloseShard_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a HistoryService_CloseShard_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_CloseShard_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_CloseShard_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _BadRequestError_Read(w wire.Value) (*shared.BadRequestError, error) { var v shared.BadRequestError err := v.FromWire(w) return &v, err } func _InternalServiceError_Read(w wire.Value) (*shared.InternalServiceError, error) { var v shared.InternalServiceError err := v.FromWire(w) return &v, err } func _AccessDeniedError_Read(w wire.Value) (*shared.AccessDeniedError, error) { var v shared.AccessDeniedError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_CloseShard_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_CloseShard_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_CloseShard_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_CloseShard_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_CloseShard_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_CloseShard_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_CloseShard_Result struct could not be encoded. func (v *HistoryService_CloseShard_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_CloseShard_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _BadRequestError_Decode(sr stream.Reader) (*shared.BadRequestError, error) { var v shared.BadRequestError err := v.Decode(sr) return &v, err } func _InternalServiceError_Decode(sr stream.Reader) (*shared.InternalServiceError, error) { var v shared.InternalServiceError err := v.Decode(sr) return &v, err } func _AccessDeniedError_Decode(sr stream.Reader) (*shared.AccessDeniedError, error) { var v shared.AccessDeniedError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_CloseShard_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_CloseShard_Result struct could not be generated from the wire // representation. func (v *HistoryService_CloseShard_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_CloseShard_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_CloseShard_Result // struct. func (v *HistoryService_CloseShard_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("HistoryService_CloseShard_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_CloseShard_Result match the // provided HistoryService_CloseShard_Result. // // This function performs a deep comparison. func (v *HistoryService_CloseShard_Result) Equals(rhs *HistoryService_CloseShard_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_CloseShard_Result. func (v *HistoryService_CloseShard_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_CloseShard_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_CloseShard_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_CloseShard_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_CloseShard_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *HistoryService_CloseShard_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *HistoryService_CloseShard_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "CloseShard" for this struct. func (v *HistoryService_CloseShard_Result) MethodName() string { return "CloseShard" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_CloseShard_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_DescribeHistoryHost_Args represents the arguments for the HistoryService.DescribeHistoryHost function. // // The arguments for DescribeHistoryHost are sent and received over the wire as this struct. type HistoryService_DescribeHistoryHost_Args struct { Request *shared.DescribeHistoryHostRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_DescribeHistoryHost_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_DescribeHistoryHost_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeHistoryHostRequest_Read(w wire.Value) (*shared.DescribeHistoryHostRequest, error) { var v shared.DescribeHistoryHostRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_DescribeHistoryHost_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_DescribeHistoryHost_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_DescribeHistoryHost_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_DescribeHistoryHost_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _DescribeHistoryHostRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_DescribeHistoryHost_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_DescribeHistoryHost_Args struct could not be encoded. func (v *HistoryService_DescribeHistoryHost_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeHistoryHostRequest_Decode(sr stream.Reader) (*shared.DescribeHistoryHostRequest, error) { var v shared.DescribeHistoryHostRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_DescribeHistoryHost_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_DescribeHistoryHost_Args struct could not be generated from the wire // representation. func (v *HistoryService_DescribeHistoryHost_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _DescribeHistoryHostRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_DescribeHistoryHost_Args // struct. func (v *HistoryService_DescribeHistoryHost_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_DescribeHistoryHost_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_DescribeHistoryHost_Args match the // provided HistoryService_DescribeHistoryHost_Args. // // This function performs a deep comparison. func (v *HistoryService_DescribeHistoryHost_Args) Equals(rhs *HistoryService_DescribeHistoryHost_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_DescribeHistoryHost_Args. func (v *HistoryService_DescribeHistoryHost_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeHistoryHost_Args) GetRequest() (o *shared.DescribeHistoryHostRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_DescribeHistoryHost_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeHistoryHost" for this struct. func (v *HistoryService_DescribeHistoryHost_Args) MethodName() string { return "DescribeHistoryHost" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_DescribeHistoryHost_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_DescribeHistoryHost_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.DescribeHistoryHost // function. var HistoryService_DescribeHistoryHost_Helper = struct { // Args accepts the parameters of DescribeHistoryHost in-order and returns // the arguments struct for the function. Args func( request *shared.DescribeHistoryHostRequest, ) *HistoryService_DescribeHistoryHost_Args // IsException returns true if the given error can be thrown // by DescribeHistoryHost. // // An error can be thrown by DescribeHistoryHost only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeHistoryHost // given its return value and error. // // This allows mapping values and errors returned by // DescribeHistoryHost into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeHistoryHost // // value, err := DescribeHistoryHost(args) // result, err := HistoryService_DescribeHistoryHost_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeHistoryHost: %v", err) // } // serialize(result) WrapResponse func(*shared.DescribeHistoryHostResponse, error) (*HistoryService_DescribeHistoryHost_Result, error) // UnwrapResponse takes the result struct for DescribeHistoryHost // and returns the value or error returned by it. // // The error is non-nil only if DescribeHistoryHost threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_DescribeHistoryHost_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_DescribeHistoryHost_Result) (*shared.DescribeHistoryHostResponse, error) }{} func init() { HistoryService_DescribeHistoryHost_Helper.Args = func( request *shared.DescribeHistoryHostRequest, ) *HistoryService_DescribeHistoryHost_Args { return &HistoryService_DescribeHistoryHost_Args{ Request: request, } } HistoryService_DescribeHistoryHost_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.AccessDeniedError: return true default: return false } } HistoryService_DescribeHistoryHost_Helper.WrapResponse = func(success *shared.DescribeHistoryHostResponse, err error) (*HistoryService_DescribeHistoryHost_Result, error) { if err == nil { return &HistoryService_DescribeHistoryHost_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeHistoryHost_Result.BadRequestError") } return &HistoryService_DescribeHistoryHost_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeHistoryHost_Result.InternalServiceError") } return &HistoryService_DescribeHistoryHost_Result{InternalServiceError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeHistoryHost_Result.AccessDeniedError") } return &HistoryService_DescribeHistoryHost_Result{AccessDeniedError: e}, nil } return nil, err } HistoryService_DescribeHistoryHost_Helper.UnwrapResponse = func(result *HistoryService_DescribeHistoryHost_Result) (success *shared.DescribeHistoryHostResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_DescribeHistoryHost_Result represents the result of a HistoryService.DescribeHistoryHost function call. // // The result of a DescribeHistoryHost execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_DescribeHistoryHost_Result struct { // Value returned by DescribeHistoryHost after a successful execution. Success *shared.DescribeHistoryHostResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a HistoryService_DescribeHistoryHost_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_DescribeHistoryHost_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_DescribeHistoryHost_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeHistoryHostResponse_Read(w wire.Value) (*shared.DescribeHistoryHostResponse, error) { var v shared.DescribeHistoryHostResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_DescribeHistoryHost_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_DescribeHistoryHost_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_DescribeHistoryHost_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_DescribeHistoryHost_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeHistoryHostResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeHistoryHost_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_DescribeHistoryHost_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_DescribeHistoryHost_Result struct could not be encoded. func (v *HistoryService_DescribeHistoryHost_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeHistoryHost_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeHistoryHostResponse_Decode(sr stream.Reader) (*shared.DescribeHistoryHostResponse, error) { var v shared.DescribeHistoryHostResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_DescribeHistoryHost_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_DescribeHistoryHost_Result struct could not be generated from the wire // representation. func (v *HistoryService_DescribeHistoryHost_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeHistoryHostResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeHistoryHost_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_DescribeHistoryHost_Result // struct. func (v *HistoryService_DescribeHistoryHost_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("HistoryService_DescribeHistoryHost_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_DescribeHistoryHost_Result match the // provided HistoryService_DescribeHistoryHost_Result. // // This function performs a deep comparison. func (v *HistoryService_DescribeHistoryHost_Result) Equals(rhs *HistoryService_DescribeHistoryHost_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_DescribeHistoryHost_Result. func (v *HistoryService_DescribeHistoryHost_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeHistoryHost_Result) GetSuccess() (o *shared.DescribeHistoryHostResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_DescribeHistoryHost_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeHistoryHost_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_DescribeHistoryHost_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeHistoryHost_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_DescribeHistoryHost_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeHistoryHost_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *HistoryService_DescribeHistoryHost_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeHistoryHost" for this struct. func (v *HistoryService_DescribeHistoryHost_Result) MethodName() string { return "DescribeHistoryHost" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_DescribeHistoryHost_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_DescribeMutableState_Args represents the arguments for the HistoryService.DescribeMutableState function. // // The arguments for DescribeMutableState are sent and received over the wire as this struct. type HistoryService_DescribeMutableState_Args struct { Request *DescribeMutableStateRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_DescribeMutableState_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_DescribeMutableState_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeMutableStateRequest_Read(w wire.Value) (*DescribeMutableStateRequest, error) { var v DescribeMutableStateRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_DescribeMutableState_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_DescribeMutableState_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_DescribeMutableState_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_DescribeMutableState_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _DescribeMutableStateRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_DescribeMutableState_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_DescribeMutableState_Args struct could not be encoded. func (v *HistoryService_DescribeMutableState_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeMutableStateRequest_Decode(sr stream.Reader) (*DescribeMutableStateRequest, error) { var v DescribeMutableStateRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_DescribeMutableState_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_DescribeMutableState_Args struct could not be generated from the wire // representation. func (v *HistoryService_DescribeMutableState_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _DescribeMutableStateRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_DescribeMutableState_Args // struct. func (v *HistoryService_DescribeMutableState_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_DescribeMutableState_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_DescribeMutableState_Args match the // provided HistoryService_DescribeMutableState_Args. // // This function performs a deep comparison. func (v *HistoryService_DescribeMutableState_Args) Equals(rhs *HistoryService_DescribeMutableState_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_DescribeMutableState_Args. func (v *HistoryService_DescribeMutableState_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeMutableState_Args) GetRequest() (o *DescribeMutableStateRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_DescribeMutableState_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeMutableState" for this struct. func (v *HistoryService_DescribeMutableState_Args) MethodName() string { return "DescribeMutableState" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_DescribeMutableState_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_DescribeMutableState_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.DescribeMutableState // function. var HistoryService_DescribeMutableState_Helper = struct { // Args accepts the parameters of DescribeMutableState in-order and returns // the arguments struct for the function. Args func( request *DescribeMutableStateRequest, ) *HistoryService_DescribeMutableState_Args // IsException returns true if the given error can be thrown // by DescribeMutableState. // // An error can be thrown by DescribeMutableState only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeMutableState // given its return value and error. // // This allows mapping values and errors returned by // DescribeMutableState into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeMutableState // // value, err := DescribeMutableState(args) // result, err := HistoryService_DescribeMutableState_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeMutableState: %v", err) // } // serialize(result) WrapResponse func(*DescribeMutableStateResponse, error) (*HistoryService_DescribeMutableState_Result, error) // UnwrapResponse takes the result struct for DescribeMutableState // and returns the value or error returned by it. // // The error is non-nil only if DescribeMutableState threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_DescribeMutableState_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_DescribeMutableState_Result) (*DescribeMutableStateResponse, error) }{} func init() { HistoryService_DescribeMutableState_Helper.Args = func( request *DescribeMutableStateRequest, ) *HistoryService_DescribeMutableState_Args { return &HistoryService_DescribeMutableState_Args{ Request: request, } } HistoryService_DescribeMutableState_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *shared.AccessDeniedError: return true case *ShardOwnershipLostError: return true case *shared.LimitExceededError: return true default: return false } } HistoryService_DescribeMutableState_Helper.WrapResponse = func(success *DescribeMutableStateResponse, err error) (*HistoryService_DescribeMutableState_Result, error) { if err == nil { return &HistoryService_DescribeMutableState_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeMutableState_Result.BadRequestError") } return &HistoryService_DescribeMutableState_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeMutableState_Result.InternalServiceError") } return &HistoryService_DescribeMutableState_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeMutableState_Result.EntityNotExistError") } return &HistoryService_DescribeMutableState_Result{EntityNotExistError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeMutableState_Result.AccessDeniedError") } return &HistoryService_DescribeMutableState_Result{AccessDeniedError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeMutableState_Result.ShardOwnershipLostError") } return &HistoryService_DescribeMutableState_Result{ShardOwnershipLostError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeMutableState_Result.LimitExceededError") } return &HistoryService_DescribeMutableState_Result{LimitExceededError: e}, nil } return nil, err } HistoryService_DescribeMutableState_Helper.UnwrapResponse = func(result *HistoryService_DescribeMutableState_Result) (success *DescribeMutableStateResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_DescribeMutableState_Result represents the result of a HistoryService.DescribeMutableState function call. // // The result of a DescribeMutableState execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_DescribeMutableState_Result struct { // Value returned by DescribeMutableState after a successful execution. Success *DescribeMutableStateResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` } // ToWire translates a HistoryService_DescribeMutableState_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_DescribeMutableState_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_DescribeMutableState_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeMutableStateResponse_Read(w wire.Value) (*DescribeMutableStateResponse, error) { var v DescribeMutableStateResponse err := v.FromWire(w) return &v, err } func _EntityNotExistsError_Read(w wire.Value) (*shared.EntityNotExistsError, error) { var v shared.EntityNotExistsError err := v.FromWire(w) return &v, err } func _ShardOwnershipLostError_Read(w wire.Value) (*ShardOwnershipLostError, error) { var v ShardOwnershipLostError err := v.FromWire(w) return &v, err } func _LimitExceededError_Read(w wire.Value) (*shared.LimitExceededError, error) { var v shared.LimitExceededError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_DescribeMutableState_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_DescribeMutableState_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_DescribeMutableState_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_DescribeMutableState_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeMutableStateResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.AccessDeniedError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeMutableState_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_DescribeMutableState_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_DescribeMutableState_Result struct could not be encoded. func (v *HistoryService_DescribeMutableState_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.AccessDeniedError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeMutableState_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeMutableStateResponse_Decode(sr stream.Reader) (*DescribeMutableStateResponse, error) { var v DescribeMutableStateResponse err := v.Decode(sr) return &v, err } func _EntityNotExistsError_Decode(sr stream.Reader) (*shared.EntityNotExistsError, error) { var v shared.EntityNotExistsError err := v.Decode(sr) return &v, err } func _ShardOwnershipLostError_Decode(sr stream.Reader) (*ShardOwnershipLostError, error) { var v ShardOwnershipLostError err := v.Decode(sr) return &v, err } func _LimitExceededError_Decode(sr stream.Reader) (*shared.LimitExceededError, error) { var v shared.LimitExceededError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_DescribeMutableState_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_DescribeMutableState_Result struct could not be generated from the wire // representation. func (v *HistoryService_DescribeMutableState_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeMutableStateResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.AccessDeniedError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeMutableState_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_DescribeMutableState_Result // struct. func (v *HistoryService_DescribeMutableState_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } return fmt.Sprintf("HistoryService_DescribeMutableState_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_DescribeMutableState_Result match the // provided HistoryService_DescribeMutableState_Result. // // This function performs a deep comparison. func (v *HistoryService_DescribeMutableState_Result) Equals(rhs *HistoryService_DescribeMutableState_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_DescribeMutableState_Result. func (v *HistoryService_DescribeMutableState_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeMutableState_Result) GetSuccess() (o *DescribeMutableStateResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_DescribeMutableState_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeMutableState_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_DescribeMutableState_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeMutableState_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_DescribeMutableState_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeMutableState_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_DescribeMutableState_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeMutableState_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *HistoryService_DescribeMutableState_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeMutableState_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_DescribeMutableState_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeMutableState_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_DescribeMutableState_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeMutableState" for this struct. func (v *HistoryService_DescribeMutableState_Result) MethodName() string { return "DescribeMutableState" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_DescribeMutableState_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_DescribeQueue_Args represents the arguments for the HistoryService.DescribeQueue function. // // The arguments for DescribeQueue are sent and received over the wire as this struct. type HistoryService_DescribeQueue_Args struct { Request *shared.DescribeQueueRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_DescribeQueue_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_DescribeQueue_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeQueueRequest_Read(w wire.Value) (*shared.DescribeQueueRequest, error) { var v shared.DescribeQueueRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_DescribeQueue_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_DescribeQueue_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_DescribeQueue_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_DescribeQueue_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _DescribeQueueRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_DescribeQueue_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_DescribeQueue_Args struct could not be encoded. func (v *HistoryService_DescribeQueue_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeQueueRequest_Decode(sr stream.Reader) (*shared.DescribeQueueRequest, error) { var v shared.DescribeQueueRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_DescribeQueue_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_DescribeQueue_Args struct could not be generated from the wire // representation. func (v *HistoryService_DescribeQueue_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _DescribeQueueRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_DescribeQueue_Args // struct. func (v *HistoryService_DescribeQueue_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_DescribeQueue_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_DescribeQueue_Args match the // provided HistoryService_DescribeQueue_Args. // // This function performs a deep comparison. func (v *HistoryService_DescribeQueue_Args) Equals(rhs *HistoryService_DescribeQueue_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_DescribeQueue_Args. func (v *HistoryService_DescribeQueue_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeQueue_Args) GetRequest() (o *shared.DescribeQueueRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_DescribeQueue_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeQueue" for this struct. func (v *HistoryService_DescribeQueue_Args) MethodName() string { return "DescribeQueue" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_DescribeQueue_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_DescribeQueue_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.DescribeQueue // function. var HistoryService_DescribeQueue_Helper = struct { // Args accepts the parameters of DescribeQueue in-order and returns // the arguments struct for the function. Args func( request *shared.DescribeQueueRequest, ) *HistoryService_DescribeQueue_Args // IsException returns true if the given error can be thrown // by DescribeQueue. // // An error can be thrown by DescribeQueue only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeQueue // given its return value and error. // // This allows mapping values and errors returned by // DescribeQueue into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeQueue // // value, err := DescribeQueue(args) // result, err := HistoryService_DescribeQueue_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeQueue: %v", err) // } // serialize(result) WrapResponse func(*shared.DescribeQueueResponse, error) (*HistoryService_DescribeQueue_Result, error) // UnwrapResponse takes the result struct for DescribeQueue // and returns the value or error returned by it. // // The error is non-nil only if DescribeQueue threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_DescribeQueue_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_DescribeQueue_Result) (*shared.DescribeQueueResponse, error) }{} func init() { HistoryService_DescribeQueue_Helper.Args = func( request *shared.DescribeQueueRequest, ) *HistoryService_DescribeQueue_Args { return &HistoryService_DescribeQueue_Args{ Request: request, } } HistoryService_DescribeQueue_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.AccessDeniedError: return true default: return false } } HistoryService_DescribeQueue_Helper.WrapResponse = func(success *shared.DescribeQueueResponse, err error) (*HistoryService_DescribeQueue_Result, error) { if err == nil { return &HistoryService_DescribeQueue_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeQueue_Result.BadRequestError") } return &HistoryService_DescribeQueue_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeQueue_Result.InternalServiceError") } return &HistoryService_DescribeQueue_Result{InternalServiceError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeQueue_Result.AccessDeniedError") } return &HistoryService_DescribeQueue_Result{AccessDeniedError: e}, nil } return nil, err } HistoryService_DescribeQueue_Helper.UnwrapResponse = func(result *HistoryService_DescribeQueue_Result) (success *shared.DescribeQueueResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_DescribeQueue_Result represents the result of a HistoryService.DescribeQueue function call. // // The result of a DescribeQueue execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_DescribeQueue_Result struct { // Value returned by DescribeQueue after a successful execution. Success *shared.DescribeQueueResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a HistoryService_DescribeQueue_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_DescribeQueue_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_DescribeQueue_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeQueueResponse_Read(w wire.Value) (*shared.DescribeQueueResponse, error) { var v shared.DescribeQueueResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_DescribeQueue_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_DescribeQueue_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_DescribeQueue_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_DescribeQueue_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeQueueResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeQueue_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_DescribeQueue_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_DescribeQueue_Result struct could not be encoded. func (v *HistoryService_DescribeQueue_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeQueue_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeQueueResponse_Decode(sr stream.Reader) (*shared.DescribeQueueResponse, error) { var v shared.DescribeQueueResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_DescribeQueue_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_DescribeQueue_Result struct could not be generated from the wire // representation. func (v *HistoryService_DescribeQueue_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeQueueResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeQueue_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_DescribeQueue_Result // struct. func (v *HistoryService_DescribeQueue_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("HistoryService_DescribeQueue_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_DescribeQueue_Result match the // provided HistoryService_DescribeQueue_Result. // // This function performs a deep comparison. func (v *HistoryService_DescribeQueue_Result) Equals(rhs *HistoryService_DescribeQueue_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_DescribeQueue_Result. func (v *HistoryService_DescribeQueue_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeQueue_Result) GetSuccess() (o *shared.DescribeQueueResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_DescribeQueue_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeQueue_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_DescribeQueue_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeQueue_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_DescribeQueue_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeQueue_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *HistoryService_DescribeQueue_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeQueue" for this struct. func (v *HistoryService_DescribeQueue_Result) MethodName() string { return "DescribeQueue" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_DescribeQueue_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_DescribeWorkflowExecution_Args represents the arguments for the HistoryService.DescribeWorkflowExecution function. // // The arguments for DescribeWorkflowExecution are sent and received over the wire as this struct. type HistoryService_DescribeWorkflowExecution_Args struct { DescribeRequest *DescribeWorkflowExecutionRequest `json:"describeRequest,omitempty"` } // ToWire translates a HistoryService_DescribeWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_DescribeWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.DescribeRequest != nil { w, err = v.DescribeRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeWorkflowExecutionRequest_1_Read(w wire.Value) (*DescribeWorkflowExecutionRequest, error) { var v DescribeWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_DescribeWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_DescribeWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_DescribeWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_DescribeWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.DescribeRequest, err = _DescribeWorkflowExecutionRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_DescribeWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_DescribeWorkflowExecution_Args struct could not be encoded. func (v *HistoryService_DescribeWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DescribeRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.DescribeRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeWorkflowExecutionRequest_1_Decode(sr stream.Reader) (*DescribeWorkflowExecutionRequest, error) { var v DescribeWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_DescribeWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_DescribeWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *HistoryService_DescribeWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.DescribeRequest, err = _DescribeWorkflowExecutionRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_DescribeWorkflowExecution_Args // struct. func (v *HistoryService_DescribeWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.DescribeRequest != nil { fields[i] = fmt.Sprintf("DescribeRequest: %v", v.DescribeRequest) i++ } return fmt.Sprintf("HistoryService_DescribeWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_DescribeWorkflowExecution_Args match the // provided HistoryService_DescribeWorkflowExecution_Args. // // This function performs a deep comparison. func (v *HistoryService_DescribeWorkflowExecution_Args) Equals(rhs *HistoryService_DescribeWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DescribeRequest == nil && rhs.DescribeRequest == nil) || (v.DescribeRequest != nil && rhs.DescribeRequest != nil && v.DescribeRequest.Equals(rhs.DescribeRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_DescribeWorkflowExecution_Args. func (v *HistoryService_DescribeWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DescribeRequest != nil { err = multierr.Append(err, enc.AddObject("describeRequest", v.DescribeRequest)) } return err } // GetDescribeRequest returns the value of DescribeRequest if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeWorkflowExecution_Args) GetDescribeRequest() (o *DescribeWorkflowExecutionRequest) { if v != nil && v.DescribeRequest != nil { return v.DescribeRequest } return } // IsSetDescribeRequest returns true if DescribeRequest is not nil. func (v *HistoryService_DescribeWorkflowExecution_Args) IsSetDescribeRequest() bool { return v != nil && v.DescribeRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeWorkflowExecution" for this struct. func (v *HistoryService_DescribeWorkflowExecution_Args) MethodName() string { return "DescribeWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_DescribeWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_DescribeWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.DescribeWorkflowExecution // function. var HistoryService_DescribeWorkflowExecution_Helper = struct { // Args accepts the parameters of DescribeWorkflowExecution in-order and returns // the arguments struct for the function. Args func( describeRequest *DescribeWorkflowExecutionRequest, ) *HistoryService_DescribeWorkflowExecution_Args // IsException returns true if the given error can be thrown // by DescribeWorkflowExecution. // // An error can be thrown by DescribeWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // DescribeWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeWorkflowExecution // // value, err := DescribeWorkflowExecution(args) // result, err := HistoryService_DescribeWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*shared.DescribeWorkflowExecutionResponse, error) (*HistoryService_DescribeWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for DescribeWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if DescribeWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_DescribeWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_DescribeWorkflowExecution_Result) (*shared.DescribeWorkflowExecutionResponse, error) }{} func init() { HistoryService_DescribeWorkflowExecution_Helper.Args = func( describeRequest *DescribeWorkflowExecutionRequest, ) *HistoryService_DescribeWorkflowExecution_Args { return &HistoryService_DescribeWorkflowExecution_Args{ DescribeRequest: describeRequest, } } HistoryService_DescribeWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true default: return false } } HistoryService_DescribeWorkflowExecution_Helper.WrapResponse = func(success *shared.DescribeWorkflowExecutionResponse, err error) (*HistoryService_DescribeWorkflowExecution_Result, error) { if err == nil { return &HistoryService_DescribeWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeWorkflowExecution_Result.BadRequestError") } return &HistoryService_DescribeWorkflowExecution_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeWorkflowExecution_Result.InternalServiceError") } return &HistoryService_DescribeWorkflowExecution_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeWorkflowExecution_Result.EntityNotExistError") } return &HistoryService_DescribeWorkflowExecution_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeWorkflowExecution_Result.ShardOwnershipLostError") } return &HistoryService_DescribeWorkflowExecution_Result{ShardOwnershipLostError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeWorkflowExecution_Result.LimitExceededError") } return &HistoryService_DescribeWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_DescribeWorkflowExecution_Result.ServiceBusyError") } return &HistoryService_DescribeWorkflowExecution_Result{ServiceBusyError: e}, nil } return nil, err } HistoryService_DescribeWorkflowExecution_Helper.UnwrapResponse = func(result *HistoryService_DescribeWorkflowExecution_Result) (success *shared.DescribeWorkflowExecutionResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_DescribeWorkflowExecution_Result represents the result of a HistoryService.DescribeWorkflowExecution function call. // // The result of a DescribeWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_DescribeWorkflowExecution_Result struct { // Value returned by DescribeWorkflowExecution after a successful execution. Success *shared.DescribeWorkflowExecutionResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a HistoryService_DescribeWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_DescribeWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeWorkflowExecutionResponse_Read(w wire.Value) (*shared.DescribeWorkflowExecutionResponse, error) { var v shared.DescribeWorkflowExecutionResponse err := v.FromWire(w) return &v, err } func _ServiceBusyError_Read(w wire.Value) (*shared.ServiceBusyError, error) { var v shared.ServiceBusyError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_DescribeWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_DescribeWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_DescribeWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_DescribeWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_DescribeWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_DescribeWorkflowExecution_Result struct could not be encoded. func (v *HistoryService_DescribeWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeWorkflowExecutionResponse_Decode(sr stream.Reader) (*shared.DescribeWorkflowExecutionResponse, error) { var v shared.DescribeWorkflowExecutionResponse err := v.Decode(sr) return &v, err } func _ServiceBusyError_Decode(sr stream.Reader) (*shared.ServiceBusyError, error) { var v shared.ServiceBusyError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_DescribeWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_DescribeWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *HistoryService_DescribeWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_DescribeWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_DescribeWorkflowExecution_Result // struct. func (v *HistoryService_DescribeWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("HistoryService_DescribeWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_DescribeWorkflowExecution_Result match the // provided HistoryService_DescribeWorkflowExecution_Result. // // This function performs a deep comparison. func (v *HistoryService_DescribeWorkflowExecution_Result) Equals(rhs *HistoryService_DescribeWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_DescribeWorkflowExecution_Result. func (v *HistoryService_DescribeWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeWorkflowExecution_Result) GetSuccess() (o *shared.DescribeWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_DescribeWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_DescribeWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeWorkflowExecution_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_DescribeWorkflowExecution_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_DescribeWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeWorkflowExecution_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_DescribeWorkflowExecution_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_DescribeWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_DescribeWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_DescribeWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeWorkflowExecution" for this struct. func (v *HistoryService_DescribeWorkflowExecution_Result) MethodName() string { return "DescribeWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_DescribeWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_GetCrossClusterTasks_Args represents the arguments for the HistoryService.GetCrossClusterTasks function. // // The arguments for GetCrossClusterTasks are sent and received over the wire as this struct. type HistoryService_GetCrossClusterTasks_Args struct { Request *shared.GetCrossClusterTasksRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_GetCrossClusterTasks_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_GetCrossClusterTasks_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetCrossClusterTasksRequest_Read(w wire.Value) (*shared.GetCrossClusterTasksRequest, error) { var v shared.GetCrossClusterTasksRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_GetCrossClusterTasks_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_GetCrossClusterTasks_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_GetCrossClusterTasks_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_GetCrossClusterTasks_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetCrossClusterTasksRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_GetCrossClusterTasks_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_GetCrossClusterTasks_Args struct could not be encoded. func (v *HistoryService_GetCrossClusterTasks_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetCrossClusterTasksRequest_Decode(sr stream.Reader) (*shared.GetCrossClusterTasksRequest, error) { var v shared.GetCrossClusterTasksRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_GetCrossClusterTasks_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_GetCrossClusterTasks_Args struct could not be generated from the wire // representation. func (v *HistoryService_GetCrossClusterTasks_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetCrossClusterTasksRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_GetCrossClusterTasks_Args // struct. func (v *HistoryService_GetCrossClusterTasks_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_GetCrossClusterTasks_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_GetCrossClusterTasks_Args match the // provided HistoryService_GetCrossClusterTasks_Args. // // This function performs a deep comparison. func (v *HistoryService_GetCrossClusterTasks_Args) Equals(rhs *HistoryService_GetCrossClusterTasks_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_GetCrossClusterTasks_Args. func (v *HistoryService_GetCrossClusterTasks_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_GetCrossClusterTasks_Args) GetRequest() (o *shared.GetCrossClusterTasksRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_GetCrossClusterTasks_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetCrossClusterTasks" for this struct. func (v *HistoryService_GetCrossClusterTasks_Args) MethodName() string { return "GetCrossClusterTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_GetCrossClusterTasks_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_GetCrossClusterTasks_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.GetCrossClusterTasks // function. var HistoryService_GetCrossClusterTasks_Helper = struct { // Args accepts the parameters of GetCrossClusterTasks in-order and returns // the arguments struct for the function. Args func( request *shared.GetCrossClusterTasksRequest, ) *HistoryService_GetCrossClusterTasks_Args // IsException returns true if the given error can be thrown // by GetCrossClusterTasks. // // An error can be thrown by GetCrossClusterTasks only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetCrossClusterTasks // given its return value and error. // // This allows mapping values and errors returned by // GetCrossClusterTasks into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetCrossClusterTasks // // value, err := GetCrossClusterTasks(args) // result, err := HistoryService_GetCrossClusterTasks_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetCrossClusterTasks: %v", err) // } // serialize(result) WrapResponse func(*shared.GetCrossClusterTasksResponse, error) (*HistoryService_GetCrossClusterTasks_Result, error) // UnwrapResponse takes the result struct for GetCrossClusterTasks // and returns the value or error returned by it. // // The error is non-nil only if GetCrossClusterTasks threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_GetCrossClusterTasks_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_GetCrossClusterTasks_Result) (*shared.GetCrossClusterTasksResponse, error) }{} func init() { HistoryService_GetCrossClusterTasks_Helper.Args = func( request *shared.GetCrossClusterTasksRequest, ) *HistoryService_GetCrossClusterTasks_Args { return &HistoryService_GetCrossClusterTasks_Args{ Request: request, } } HistoryService_GetCrossClusterTasks_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true default: return false } } HistoryService_GetCrossClusterTasks_Helper.WrapResponse = func(success *shared.GetCrossClusterTasksResponse, err error) (*HistoryService_GetCrossClusterTasks_Result, error) { if err == nil { return &HistoryService_GetCrossClusterTasks_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetCrossClusterTasks_Result.BadRequestError") } return &HistoryService_GetCrossClusterTasks_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetCrossClusterTasks_Result.InternalServiceError") } return &HistoryService_GetCrossClusterTasks_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetCrossClusterTasks_Result.ServiceBusyError") } return &HistoryService_GetCrossClusterTasks_Result{ServiceBusyError: e}, nil } return nil, err } HistoryService_GetCrossClusterTasks_Helper.UnwrapResponse = func(result *HistoryService_GetCrossClusterTasks_Result) (success *shared.GetCrossClusterTasksResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_GetCrossClusterTasks_Result represents the result of a HistoryService.GetCrossClusterTasks function call. // // The result of a GetCrossClusterTasks execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_GetCrossClusterTasks_Result struct { // Value returned by GetCrossClusterTasks after a successful execution. Success *shared.GetCrossClusterTasksResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a HistoryService_GetCrossClusterTasks_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_GetCrossClusterTasks_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_GetCrossClusterTasks_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetCrossClusterTasksResponse_Read(w wire.Value) (*shared.GetCrossClusterTasksResponse, error) { var v shared.GetCrossClusterTasksResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_GetCrossClusterTasks_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_GetCrossClusterTasks_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_GetCrossClusterTasks_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_GetCrossClusterTasks_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetCrossClusterTasksResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetCrossClusterTasks_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_GetCrossClusterTasks_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_GetCrossClusterTasks_Result struct could not be encoded. func (v *HistoryService_GetCrossClusterTasks_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetCrossClusterTasks_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetCrossClusterTasksResponse_Decode(sr stream.Reader) (*shared.GetCrossClusterTasksResponse, error) { var v shared.GetCrossClusterTasksResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_GetCrossClusterTasks_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_GetCrossClusterTasks_Result struct could not be generated from the wire // representation. func (v *HistoryService_GetCrossClusterTasks_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetCrossClusterTasksResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetCrossClusterTasks_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_GetCrossClusterTasks_Result // struct. func (v *HistoryService_GetCrossClusterTasks_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("HistoryService_GetCrossClusterTasks_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_GetCrossClusterTasks_Result match the // provided HistoryService_GetCrossClusterTasks_Result. // // This function performs a deep comparison. func (v *HistoryService_GetCrossClusterTasks_Result) Equals(rhs *HistoryService_GetCrossClusterTasks_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_GetCrossClusterTasks_Result. func (v *HistoryService_GetCrossClusterTasks_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_GetCrossClusterTasks_Result) GetSuccess() (o *shared.GetCrossClusterTasksResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_GetCrossClusterTasks_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_GetCrossClusterTasks_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_GetCrossClusterTasks_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_GetCrossClusterTasks_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_GetCrossClusterTasks_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_GetCrossClusterTasks_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_GetCrossClusterTasks_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetCrossClusterTasks" for this struct. func (v *HistoryService_GetCrossClusterTasks_Result) MethodName() string { return "GetCrossClusterTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_GetCrossClusterTasks_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_GetDLQReplicationMessages_Args represents the arguments for the HistoryService.GetDLQReplicationMessages function. // // The arguments for GetDLQReplicationMessages are sent and received over the wire as this struct. type HistoryService_GetDLQReplicationMessages_Args struct { Request *replicator.GetDLQReplicationMessagesRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_GetDLQReplicationMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_GetDLQReplicationMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDLQReplicationMessagesRequest_Read(w wire.Value) (*replicator.GetDLQReplicationMessagesRequest, error) { var v replicator.GetDLQReplicationMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_GetDLQReplicationMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_GetDLQReplicationMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_GetDLQReplicationMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_GetDLQReplicationMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetDLQReplicationMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_GetDLQReplicationMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_GetDLQReplicationMessages_Args struct could not be encoded. func (v *HistoryService_GetDLQReplicationMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetDLQReplicationMessagesRequest_Decode(sr stream.Reader) (*replicator.GetDLQReplicationMessagesRequest, error) { var v replicator.GetDLQReplicationMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_GetDLQReplicationMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_GetDLQReplicationMessages_Args struct could not be generated from the wire // representation. func (v *HistoryService_GetDLQReplicationMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetDLQReplicationMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_GetDLQReplicationMessages_Args // struct. func (v *HistoryService_GetDLQReplicationMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_GetDLQReplicationMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_GetDLQReplicationMessages_Args match the // provided HistoryService_GetDLQReplicationMessages_Args. // // This function performs a deep comparison. func (v *HistoryService_GetDLQReplicationMessages_Args) Equals(rhs *HistoryService_GetDLQReplicationMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_GetDLQReplicationMessages_Args. func (v *HistoryService_GetDLQReplicationMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_GetDLQReplicationMessages_Args) GetRequest() (o *replicator.GetDLQReplicationMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_GetDLQReplicationMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetDLQReplicationMessages" for this struct. func (v *HistoryService_GetDLQReplicationMessages_Args) MethodName() string { return "GetDLQReplicationMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_GetDLQReplicationMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_GetDLQReplicationMessages_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.GetDLQReplicationMessages // function. var HistoryService_GetDLQReplicationMessages_Helper = struct { // Args accepts the parameters of GetDLQReplicationMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.GetDLQReplicationMessagesRequest, ) *HistoryService_GetDLQReplicationMessages_Args // IsException returns true if the given error can be thrown // by GetDLQReplicationMessages. // // An error can be thrown by GetDLQReplicationMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetDLQReplicationMessages // given its return value and error. // // This allows mapping values and errors returned by // GetDLQReplicationMessages into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetDLQReplicationMessages // // value, err := GetDLQReplicationMessages(args) // result, err := HistoryService_GetDLQReplicationMessages_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetDLQReplicationMessages: %v", err) // } // serialize(result) WrapResponse func(*replicator.GetDLQReplicationMessagesResponse, error) (*HistoryService_GetDLQReplicationMessages_Result, error) // UnwrapResponse takes the result struct for GetDLQReplicationMessages // and returns the value or error returned by it. // // The error is non-nil only if GetDLQReplicationMessages threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_GetDLQReplicationMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_GetDLQReplicationMessages_Result) (*replicator.GetDLQReplicationMessagesResponse, error) }{} func init() { HistoryService_GetDLQReplicationMessages_Helper.Args = func( request *replicator.GetDLQReplicationMessagesRequest, ) *HistoryService_GetDLQReplicationMessages_Args { return &HistoryService_GetDLQReplicationMessages_Args{ Request: request, } } HistoryService_GetDLQReplicationMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true default: return false } } HistoryService_GetDLQReplicationMessages_Helper.WrapResponse = func(success *replicator.GetDLQReplicationMessagesResponse, err error) (*HistoryService_GetDLQReplicationMessages_Result, error) { if err == nil { return &HistoryService_GetDLQReplicationMessages_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetDLQReplicationMessages_Result.BadRequestError") } return &HistoryService_GetDLQReplicationMessages_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetDLQReplicationMessages_Result.InternalServiceError") } return &HistoryService_GetDLQReplicationMessages_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetDLQReplicationMessages_Result.ServiceBusyError") } return &HistoryService_GetDLQReplicationMessages_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetDLQReplicationMessages_Result.EntityNotExistError") } return &HistoryService_GetDLQReplicationMessages_Result{EntityNotExistError: e}, nil } return nil, err } HistoryService_GetDLQReplicationMessages_Helper.UnwrapResponse = func(result *HistoryService_GetDLQReplicationMessages_Result) (success *replicator.GetDLQReplicationMessagesResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_GetDLQReplicationMessages_Result represents the result of a HistoryService.GetDLQReplicationMessages function call. // // The result of a GetDLQReplicationMessages execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_GetDLQReplicationMessages_Result struct { // Value returned by GetDLQReplicationMessages after a successful execution. Success *replicator.GetDLQReplicationMessagesResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` } // ToWire translates a HistoryService_GetDLQReplicationMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_GetDLQReplicationMessages_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_GetDLQReplicationMessages_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetDLQReplicationMessagesResponse_Read(w wire.Value) (*replicator.GetDLQReplicationMessagesResponse, error) { var v replicator.GetDLQReplicationMessagesResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_GetDLQReplicationMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_GetDLQReplicationMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_GetDLQReplicationMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_GetDLQReplicationMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetDLQReplicationMessagesResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetDLQReplicationMessages_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_GetDLQReplicationMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_GetDLQReplicationMessages_Result struct could not be encoded. func (v *HistoryService_GetDLQReplicationMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetDLQReplicationMessages_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetDLQReplicationMessagesResponse_Decode(sr stream.Reader) (*replicator.GetDLQReplicationMessagesResponse, error) { var v replicator.GetDLQReplicationMessagesResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_GetDLQReplicationMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_GetDLQReplicationMessages_Result struct could not be generated from the wire // representation. func (v *HistoryService_GetDLQReplicationMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetDLQReplicationMessagesResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetDLQReplicationMessages_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_GetDLQReplicationMessages_Result // struct. func (v *HistoryService_GetDLQReplicationMessages_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } return fmt.Sprintf("HistoryService_GetDLQReplicationMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_GetDLQReplicationMessages_Result match the // provided HistoryService_GetDLQReplicationMessages_Result. // // This function performs a deep comparison. func (v *HistoryService_GetDLQReplicationMessages_Result) Equals(rhs *HistoryService_GetDLQReplicationMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_GetDLQReplicationMessages_Result. func (v *HistoryService_GetDLQReplicationMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_GetDLQReplicationMessages_Result) GetSuccess() (o *replicator.GetDLQReplicationMessagesResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_GetDLQReplicationMessages_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_GetDLQReplicationMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_GetDLQReplicationMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_GetDLQReplicationMessages_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_GetDLQReplicationMessages_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_GetDLQReplicationMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_GetDLQReplicationMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_GetDLQReplicationMessages_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_GetDLQReplicationMessages_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetDLQReplicationMessages" for this struct. func (v *HistoryService_GetDLQReplicationMessages_Result) MethodName() string { return "GetDLQReplicationMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_GetDLQReplicationMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_GetFailoverInfo_Args represents the arguments for the HistoryService.GetFailoverInfo function. // // The arguments for GetFailoverInfo are sent and received over the wire as this struct. type HistoryService_GetFailoverInfo_Args struct { Request *GetFailoverInfoRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_GetFailoverInfo_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_GetFailoverInfo_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetFailoverInfoRequest_Read(w wire.Value) (*GetFailoverInfoRequest, error) { var v GetFailoverInfoRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_GetFailoverInfo_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_GetFailoverInfo_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_GetFailoverInfo_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_GetFailoverInfo_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetFailoverInfoRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_GetFailoverInfo_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_GetFailoverInfo_Args struct could not be encoded. func (v *HistoryService_GetFailoverInfo_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetFailoverInfoRequest_Decode(sr stream.Reader) (*GetFailoverInfoRequest, error) { var v GetFailoverInfoRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_GetFailoverInfo_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_GetFailoverInfo_Args struct could not be generated from the wire // representation. func (v *HistoryService_GetFailoverInfo_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetFailoverInfoRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_GetFailoverInfo_Args // struct. func (v *HistoryService_GetFailoverInfo_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_GetFailoverInfo_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_GetFailoverInfo_Args match the // provided HistoryService_GetFailoverInfo_Args. // // This function performs a deep comparison. func (v *HistoryService_GetFailoverInfo_Args) Equals(rhs *HistoryService_GetFailoverInfo_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_GetFailoverInfo_Args. func (v *HistoryService_GetFailoverInfo_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_GetFailoverInfo_Args) GetRequest() (o *GetFailoverInfoRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_GetFailoverInfo_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetFailoverInfo" for this struct. func (v *HistoryService_GetFailoverInfo_Args) MethodName() string { return "GetFailoverInfo" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_GetFailoverInfo_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_GetFailoverInfo_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.GetFailoverInfo // function. var HistoryService_GetFailoverInfo_Helper = struct { // Args accepts the parameters of GetFailoverInfo in-order and returns // the arguments struct for the function. Args func( request *GetFailoverInfoRequest, ) *HistoryService_GetFailoverInfo_Args // IsException returns true if the given error can be thrown // by GetFailoverInfo. // // An error can be thrown by GetFailoverInfo only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetFailoverInfo // given its return value and error. // // This allows mapping values and errors returned by // GetFailoverInfo into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetFailoverInfo // // value, err := GetFailoverInfo(args) // result, err := HistoryService_GetFailoverInfo_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetFailoverInfo: %v", err) // } // serialize(result) WrapResponse func(*GetFailoverInfoResponse, error) (*HistoryService_GetFailoverInfo_Result, error) // UnwrapResponse takes the result struct for GetFailoverInfo // and returns the value or error returned by it. // // The error is non-nil only if GetFailoverInfo threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_GetFailoverInfo_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_GetFailoverInfo_Result) (*GetFailoverInfoResponse, error) }{} func init() { HistoryService_GetFailoverInfo_Helper.Args = func( request *GetFailoverInfoRequest, ) *HistoryService_GetFailoverInfo_Args { return &HistoryService_GetFailoverInfo_Args{ Request: request, } } HistoryService_GetFailoverInfo_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *ShardOwnershipLostError: return true case *shared.EntityNotExistsError: return true default: return false } } HistoryService_GetFailoverInfo_Helper.WrapResponse = func(success *GetFailoverInfoResponse, err error) (*HistoryService_GetFailoverInfo_Result, error) { if err == nil { return &HistoryService_GetFailoverInfo_Result{Success: success}, nil } switch e := err.(type) { case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetFailoverInfo_Result.InternalServiceError") } return &HistoryService_GetFailoverInfo_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetFailoverInfo_Result.ServiceBusyError") } return &HistoryService_GetFailoverInfo_Result{ServiceBusyError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetFailoverInfo_Result.ShardOwnershipLostError") } return &HistoryService_GetFailoverInfo_Result{ShardOwnershipLostError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetFailoverInfo_Result.EntityNotExistError") } return &HistoryService_GetFailoverInfo_Result{EntityNotExistError: e}, nil } return nil, err } HistoryService_GetFailoverInfo_Helper.UnwrapResponse = func(result *HistoryService_GetFailoverInfo_Result) (success *GetFailoverInfoResponse, err error) { if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_GetFailoverInfo_Result represents the result of a HistoryService.GetFailoverInfo function call. // // The result of a GetFailoverInfo execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_GetFailoverInfo_Result struct { // Value returned by GetFailoverInfo after a successful execution. Success *GetFailoverInfoResponse `json:"success,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` } // ToWire translates a HistoryService_GetFailoverInfo_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_GetFailoverInfo_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_GetFailoverInfo_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetFailoverInfoResponse_Read(w wire.Value) (*GetFailoverInfoResponse, error) { var v GetFailoverInfoResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_GetFailoverInfo_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_GetFailoverInfo_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_GetFailoverInfo_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_GetFailoverInfo_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetFailoverInfoResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetFailoverInfo_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_GetFailoverInfo_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_GetFailoverInfo_Result struct could not be encoded. func (v *HistoryService_GetFailoverInfo_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetFailoverInfo_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetFailoverInfoResponse_Decode(sr stream.Reader) (*GetFailoverInfoResponse, error) { var v GetFailoverInfoResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_GetFailoverInfo_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_GetFailoverInfo_Result struct could not be generated from the wire // representation. func (v *HistoryService_GetFailoverInfo_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetFailoverInfoResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetFailoverInfo_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_GetFailoverInfo_Result // struct. func (v *HistoryService_GetFailoverInfo_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } return fmt.Sprintf("HistoryService_GetFailoverInfo_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_GetFailoverInfo_Result match the // provided HistoryService_GetFailoverInfo_Result. // // This function performs a deep comparison. func (v *HistoryService_GetFailoverInfo_Result) Equals(rhs *HistoryService_GetFailoverInfo_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_GetFailoverInfo_Result. func (v *HistoryService_GetFailoverInfo_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_GetFailoverInfo_Result) GetSuccess() (o *GetFailoverInfoResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_GetFailoverInfo_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_GetFailoverInfo_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_GetFailoverInfo_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_GetFailoverInfo_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_GetFailoverInfo_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_GetFailoverInfo_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_GetFailoverInfo_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_GetFailoverInfo_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_GetFailoverInfo_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetFailoverInfo" for this struct. func (v *HistoryService_GetFailoverInfo_Result) MethodName() string { return "GetFailoverInfo" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_GetFailoverInfo_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_GetMutableState_Args represents the arguments for the HistoryService.GetMutableState function. // // The arguments for GetMutableState are sent and received over the wire as this struct. type HistoryService_GetMutableState_Args struct { GetRequest *GetMutableStateRequest `json:"getRequest,omitempty"` } // ToWire translates a HistoryService_GetMutableState_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_GetMutableState_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.GetRequest != nil { w, err = v.GetRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetMutableStateRequest_Read(w wire.Value) (*GetMutableStateRequest, error) { var v GetMutableStateRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_GetMutableState_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_GetMutableState_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_GetMutableState_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_GetMutableState_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.GetRequest, err = _GetMutableStateRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_GetMutableState_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_GetMutableState_Args struct could not be encoded. func (v *HistoryService_GetMutableState_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.GetRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.GetRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetMutableStateRequest_Decode(sr stream.Reader) (*GetMutableStateRequest, error) { var v GetMutableStateRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_GetMutableState_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_GetMutableState_Args struct could not be generated from the wire // representation. func (v *HistoryService_GetMutableState_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.GetRequest, err = _GetMutableStateRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_GetMutableState_Args // struct. func (v *HistoryService_GetMutableState_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.GetRequest != nil { fields[i] = fmt.Sprintf("GetRequest: %v", v.GetRequest) i++ } return fmt.Sprintf("HistoryService_GetMutableState_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_GetMutableState_Args match the // provided HistoryService_GetMutableState_Args. // // This function performs a deep comparison. func (v *HistoryService_GetMutableState_Args) Equals(rhs *HistoryService_GetMutableState_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.GetRequest == nil && rhs.GetRequest == nil) || (v.GetRequest != nil && rhs.GetRequest != nil && v.GetRequest.Equals(rhs.GetRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_GetMutableState_Args. func (v *HistoryService_GetMutableState_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.GetRequest != nil { err = multierr.Append(err, enc.AddObject("getRequest", v.GetRequest)) } return err } // GetGetRequest returns the value of GetRequest if it is set or its // zero value if it is unset. func (v *HistoryService_GetMutableState_Args) GetGetRequest() (o *GetMutableStateRequest) { if v != nil && v.GetRequest != nil { return v.GetRequest } return } // IsSetGetRequest returns true if GetRequest is not nil. func (v *HistoryService_GetMutableState_Args) IsSetGetRequest() bool { return v != nil && v.GetRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetMutableState" for this struct. func (v *HistoryService_GetMutableState_Args) MethodName() string { return "GetMutableState" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_GetMutableState_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_GetMutableState_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.GetMutableState // function. var HistoryService_GetMutableState_Helper = struct { // Args accepts the parameters of GetMutableState in-order and returns // the arguments struct for the function. Args func( getRequest *GetMutableStateRequest, ) *HistoryService_GetMutableState_Args // IsException returns true if the given error can be thrown // by GetMutableState. // // An error can be thrown by GetMutableState only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetMutableState // given its return value and error. // // This allows mapping values and errors returned by // GetMutableState into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetMutableState // // value, err := GetMutableState(args) // result, err := HistoryService_GetMutableState_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetMutableState: %v", err) // } // serialize(result) WrapResponse func(*GetMutableStateResponse, error) (*HistoryService_GetMutableState_Result, error) // UnwrapResponse takes the result struct for GetMutableState // and returns the value or error returned by it. // // The error is non-nil only if GetMutableState threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_GetMutableState_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_GetMutableState_Result) (*GetMutableStateResponse, error) }{} func init() { HistoryService_GetMutableState_Helper.Args = func( getRequest *GetMutableStateRequest, ) *HistoryService_GetMutableState_Args { return &HistoryService_GetMutableState_Args{ GetRequest: getRequest, } } HistoryService_GetMutableState_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.CurrentBranchChangedError: return true default: return false } } HistoryService_GetMutableState_Helper.WrapResponse = func(success *GetMutableStateResponse, err error) (*HistoryService_GetMutableState_Result, error) { if err == nil { return &HistoryService_GetMutableState_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetMutableState_Result.BadRequestError") } return &HistoryService_GetMutableState_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetMutableState_Result.InternalServiceError") } return &HistoryService_GetMutableState_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetMutableState_Result.EntityNotExistError") } return &HistoryService_GetMutableState_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetMutableState_Result.ShardOwnershipLostError") } return &HistoryService_GetMutableState_Result{ShardOwnershipLostError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetMutableState_Result.LimitExceededError") } return &HistoryService_GetMutableState_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetMutableState_Result.ServiceBusyError") } return &HistoryService_GetMutableState_Result{ServiceBusyError: e}, nil case *shared.CurrentBranchChangedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetMutableState_Result.CurrentBranchChangedError") } return &HistoryService_GetMutableState_Result{CurrentBranchChangedError: e}, nil } return nil, err } HistoryService_GetMutableState_Helper.UnwrapResponse = func(result *HistoryService_GetMutableState_Result) (success *GetMutableStateResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.CurrentBranchChangedError != nil { err = result.CurrentBranchChangedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_GetMutableState_Result represents the result of a HistoryService.GetMutableState function call. // // The result of a GetMutableState execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_GetMutableState_Result struct { // Value returned by GetMutableState after a successful execution. Success *GetMutableStateResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` CurrentBranchChangedError *shared.CurrentBranchChangedError `json:"currentBranchChangedError,omitempty"` } // ToWire translates a HistoryService_GetMutableState_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_GetMutableState_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.CurrentBranchChangedError != nil { w, err = v.CurrentBranchChangedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_GetMutableState_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetMutableStateResponse_Read(w wire.Value) (*GetMutableStateResponse, error) { var v GetMutableStateResponse err := v.FromWire(w) return &v, err } func _CurrentBranchChangedError_Read(w wire.Value) (*shared.CurrentBranchChangedError, error) { var v shared.CurrentBranchChangedError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_GetMutableState_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_GetMutableState_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_GetMutableState_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_GetMutableState_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetMutableStateResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.CurrentBranchChangedError, err = _CurrentBranchChangedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.CurrentBranchChangedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetMutableState_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_GetMutableState_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_GetMutableState_Result struct could not be encoded. func (v *HistoryService_GetMutableState_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CurrentBranchChangedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.CurrentBranchChangedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.CurrentBranchChangedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetMutableState_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetMutableStateResponse_Decode(sr stream.Reader) (*GetMutableStateResponse, error) { var v GetMutableStateResponse err := v.Decode(sr) return &v, err } func _CurrentBranchChangedError_Decode(sr stream.Reader) (*shared.CurrentBranchChangedError, error) { var v shared.CurrentBranchChangedError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_GetMutableState_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_GetMutableState_Result struct could not be generated from the wire // representation. func (v *HistoryService_GetMutableState_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetMutableStateResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.CurrentBranchChangedError, err = _CurrentBranchChangedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.CurrentBranchChangedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetMutableState_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_GetMutableState_Result // struct. func (v *HistoryService_GetMutableState_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.CurrentBranchChangedError != nil { fields[i] = fmt.Sprintf("CurrentBranchChangedError: %v", v.CurrentBranchChangedError) i++ } return fmt.Sprintf("HistoryService_GetMutableState_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_GetMutableState_Result match the // provided HistoryService_GetMutableState_Result. // // This function performs a deep comparison. func (v *HistoryService_GetMutableState_Result) Equals(rhs *HistoryService_GetMutableState_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.CurrentBranchChangedError == nil && rhs.CurrentBranchChangedError == nil) || (v.CurrentBranchChangedError != nil && rhs.CurrentBranchChangedError != nil && v.CurrentBranchChangedError.Equals(rhs.CurrentBranchChangedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_GetMutableState_Result. func (v *HistoryService_GetMutableState_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.CurrentBranchChangedError != nil { err = multierr.Append(err, enc.AddObject("currentBranchChangedError", v.CurrentBranchChangedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_GetMutableState_Result) GetSuccess() (o *GetMutableStateResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_GetMutableState_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_GetMutableState_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_GetMutableState_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_GetMutableState_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_GetMutableState_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_GetMutableState_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_GetMutableState_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_GetMutableState_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_GetMutableState_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_GetMutableState_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_GetMutableState_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_GetMutableState_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_GetMutableState_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetCurrentBranchChangedError returns the value of CurrentBranchChangedError if it is set or its // zero value if it is unset. func (v *HistoryService_GetMutableState_Result) GetCurrentBranchChangedError() (o *shared.CurrentBranchChangedError) { if v != nil && v.CurrentBranchChangedError != nil { return v.CurrentBranchChangedError } return } // IsSetCurrentBranchChangedError returns true if CurrentBranchChangedError is not nil. func (v *HistoryService_GetMutableState_Result) IsSetCurrentBranchChangedError() bool { return v != nil && v.CurrentBranchChangedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetMutableState" for this struct. func (v *HistoryService_GetMutableState_Result) MethodName() string { return "GetMutableState" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_GetMutableState_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_GetReplicationMessages_Args represents the arguments for the HistoryService.GetReplicationMessages function. // // The arguments for GetReplicationMessages are sent and received over the wire as this struct. type HistoryService_GetReplicationMessages_Args struct { Request *replicator.GetReplicationMessagesRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_GetReplicationMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_GetReplicationMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetReplicationMessagesRequest_Read(w wire.Value) (*replicator.GetReplicationMessagesRequest, error) { var v replicator.GetReplicationMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_GetReplicationMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_GetReplicationMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_GetReplicationMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_GetReplicationMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetReplicationMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_GetReplicationMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_GetReplicationMessages_Args struct could not be encoded. func (v *HistoryService_GetReplicationMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetReplicationMessagesRequest_Decode(sr stream.Reader) (*replicator.GetReplicationMessagesRequest, error) { var v replicator.GetReplicationMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_GetReplicationMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_GetReplicationMessages_Args struct could not be generated from the wire // representation. func (v *HistoryService_GetReplicationMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetReplicationMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_GetReplicationMessages_Args // struct. func (v *HistoryService_GetReplicationMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_GetReplicationMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_GetReplicationMessages_Args match the // provided HistoryService_GetReplicationMessages_Args. // // This function performs a deep comparison. func (v *HistoryService_GetReplicationMessages_Args) Equals(rhs *HistoryService_GetReplicationMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_GetReplicationMessages_Args. func (v *HistoryService_GetReplicationMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_GetReplicationMessages_Args) GetRequest() (o *replicator.GetReplicationMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_GetReplicationMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetReplicationMessages" for this struct. func (v *HistoryService_GetReplicationMessages_Args) MethodName() string { return "GetReplicationMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_GetReplicationMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_GetReplicationMessages_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.GetReplicationMessages // function. var HistoryService_GetReplicationMessages_Helper = struct { // Args accepts the parameters of GetReplicationMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.GetReplicationMessagesRequest, ) *HistoryService_GetReplicationMessages_Args // IsException returns true if the given error can be thrown // by GetReplicationMessages. // // An error can be thrown by GetReplicationMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetReplicationMessages // given its return value and error. // // This allows mapping values and errors returned by // GetReplicationMessages into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetReplicationMessages // // value, err := GetReplicationMessages(args) // result, err := HistoryService_GetReplicationMessages_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetReplicationMessages: %v", err) // } // serialize(result) WrapResponse func(*replicator.GetReplicationMessagesResponse, error) (*HistoryService_GetReplicationMessages_Result, error) // UnwrapResponse takes the result struct for GetReplicationMessages // and returns the value or error returned by it. // // The error is non-nil only if GetReplicationMessages threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_GetReplicationMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_GetReplicationMessages_Result) (*replicator.GetReplicationMessagesResponse, error) }{} func init() { HistoryService_GetReplicationMessages_Helper.Args = func( request *replicator.GetReplicationMessagesRequest, ) *HistoryService_GetReplicationMessages_Args { return &HistoryService_GetReplicationMessages_Args{ Request: request, } } HistoryService_GetReplicationMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true default: return false } } HistoryService_GetReplicationMessages_Helper.WrapResponse = func(success *replicator.GetReplicationMessagesResponse, err error) (*HistoryService_GetReplicationMessages_Result, error) { if err == nil { return &HistoryService_GetReplicationMessages_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetReplicationMessages_Result.BadRequestError") } return &HistoryService_GetReplicationMessages_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetReplicationMessages_Result.InternalServiceError") } return &HistoryService_GetReplicationMessages_Result{InternalServiceError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetReplicationMessages_Result.LimitExceededError") } return &HistoryService_GetReplicationMessages_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetReplicationMessages_Result.ServiceBusyError") } return &HistoryService_GetReplicationMessages_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_GetReplicationMessages_Result.ClientVersionNotSupportedError") } return &HistoryService_GetReplicationMessages_Result{ClientVersionNotSupportedError: e}, nil } return nil, err } HistoryService_GetReplicationMessages_Helper.UnwrapResponse = func(result *HistoryService_GetReplicationMessages_Result) (success *replicator.GetReplicationMessagesResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_GetReplicationMessages_Result represents the result of a HistoryService.GetReplicationMessages function call. // // The result of a GetReplicationMessages execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_GetReplicationMessages_Result struct { // Value returned by GetReplicationMessages after a successful execution. Success *replicator.GetReplicationMessagesResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` } // ToWire translates a HistoryService_GetReplicationMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_GetReplicationMessages_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_GetReplicationMessages_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetReplicationMessagesResponse_Read(w wire.Value) (*replicator.GetReplicationMessagesResponse, error) { var v replicator.GetReplicationMessagesResponse err := v.FromWire(w) return &v, err } func _ClientVersionNotSupportedError_Read(w wire.Value) (*shared.ClientVersionNotSupportedError, error) { var v shared.ClientVersionNotSupportedError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_GetReplicationMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_GetReplicationMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_GetReplicationMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_GetReplicationMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetReplicationMessagesResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetReplicationMessages_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_GetReplicationMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_GetReplicationMessages_Result struct could not be encoded. func (v *HistoryService_GetReplicationMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetReplicationMessages_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetReplicationMessagesResponse_Decode(sr stream.Reader) (*replicator.GetReplicationMessagesResponse, error) { var v replicator.GetReplicationMessagesResponse err := v.Decode(sr) return &v, err } func _ClientVersionNotSupportedError_Decode(sr stream.Reader) (*shared.ClientVersionNotSupportedError, error) { var v shared.ClientVersionNotSupportedError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_GetReplicationMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_GetReplicationMessages_Result struct could not be generated from the wire // representation. func (v *HistoryService_GetReplicationMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetReplicationMessagesResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_GetReplicationMessages_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_GetReplicationMessages_Result // struct. func (v *HistoryService_GetReplicationMessages_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } return fmt.Sprintf("HistoryService_GetReplicationMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_GetReplicationMessages_Result match the // provided HistoryService_GetReplicationMessages_Result. // // This function performs a deep comparison. func (v *HistoryService_GetReplicationMessages_Result) Equals(rhs *HistoryService_GetReplicationMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_GetReplicationMessages_Result. func (v *HistoryService_GetReplicationMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_GetReplicationMessages_Result) GetSuccess() (o *replicator.GetReplicationMessagesResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_GetReplicationMessages_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_GetReplicationMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_GetReplicationMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_GetReplicationMessages_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_GetReplicationMessages_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_GetReplicationMessages_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_GetReplicationMessages_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_GetReplicationMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_GetReplicationMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *HistoryService_GetReplicationMessages_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *HistoryService_GetReplicationMessages_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetReplicationMessages" for this struct. func (v *HistoryService_GetReplicationMessages_Result) MethodName() string { return "GetReplicationMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_GetReplicationMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_MergeDLQMessages_Args represents the arguments for the HistoryService.MergeDLQMessages function. // // The arguments for MergeDLQMessages are sent and received over the wire as this struct. type HistoryService_MergeDLQMessages_Args struct { Request *replicator.MergeDLQMessagesRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_MergeDLQMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_MergeDLQMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _MergeDLQMessagesRequest_Read(w wire.Value) (*replicator.MergeDLQMessagesRequest, error) { var v replicator.MergeDLQMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_MergeDLQMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_MergeDLQMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_MergeDLQMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_MergeDLQMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _MergeDLQMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_MergeDLQMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_MergeDLQMessages_Args struct could not be encoded. func (v *HistoryService_MergeDLQMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _MergeDLQMessagesRequest_Decode(sr stream.Reader) (*replicator.MergeDLQMessagesRequest, error) { var v replicator.MergeDLQMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_MergeDLQMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_MergeDLQMessages_Args struct could not be generated from the wire // representation. func (v *HistoryService_MergeDLQMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _MergeDLQMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_MergeDLQMessages_Args // struct. func (v *HistoryService_MergeDLQMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_MergeDLQMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_MergeDLQMessages_Args match the // provided HistoryService_MergeDLQMessages_Args. // // This function performs a deep comparison. func (v *HistoryService_MergeDLQMessages_Args) Equals(rhs *HistoryService_MergeDLQMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_MergeDLQMessages_Args. func (v *HistoryService_MergeDLQMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_MergeDLQMessages_Args) GetRequest() (o *replicator.MergeDLQMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_MergeDLQMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "MergeDLQMessages" for this struct. func (v *HistoryService_MergeDLQMessages_Args) MethodName() string { return "MergeDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_MergeDLQMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_MergeDLQMessages_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.MergeDLQMessages // function. var HistoryService_MergeDLQMessages_Helper = struct { // Args accepts the parameters of MergeDLQMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.MergeDLQMessagesRequest, ) *HistoryService_MergeDLQMessages_Args // IsException returns true if the given error can be thrown // by MergeDLQMessages. // // An error can be thrown by MergeDLQMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for MergeDLQMessages // given its return value and error. // // This allows mapping values and errors returned by // MergeDLQMessages into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by MergeDLQMessages // // value, err := MergeDLQMessages(args) // result, err := HistoryService_MergeDLQMessages_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from MergeDLQMessages: %v", err) // } // serialize(result) WrapResponse func(*replicator.MergeDLQMessagesResponse, error) (*HistoryService_MergeDLQMessages_Result, error) // UnwrapResponse takes the result struct for MergeDLQMessages // and returns the value or error returned by it. // // The error is non-nil only if MergeDLQMessages threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_MergeDLQMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_MergeDLQMessages_Result) (*replicator.MergeDLQMessagesResponse, error) }{} func init() { HistoryService_MergeDLQMessages_Helper.Args = func( request *replicator.MergeDLQMessagesRequest, ) *HistoryService_MergeDLQMessages_Args { return &HistoryService_MergeDLQMessages_Args{ Request: request, } } HistoryService_MergeDLQMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true default: return false } } HistoryService_MergeDLQMessages_Helper.WrapResponse = func(success *replicator.MergeDLQMessagesResponse, err error) (*HistoryService_MergeDLQMessages_Result, error) { if err == nil { return &HistoryService_MergeDLQMessages_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_MergeDLQMessages_Result.BadRequestError") } return &HistoryService_MergeDLQMessages_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_MergeDLQMessages_Result.InternalServiceError") } return &HistoryService_MergeDLQMessages_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_MergeDLQMessages_Result.ServiceBusyError") } return &HistoryService_MergeDLQMessages_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_MergeDLQMessages_Result.EntityNotExistError") } return &HistoryService_MergeDLQMessages_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_MergeDLQMessages_Result.ShardOwnershipLostError") } return &HistoryService_MergeDLQMessages_Result{ShardOwnershipLostError: e}, nil } return nil, err } HistoryService_MergeDLQMessages_Helper.UnwrapResponse = func(result *HistoryService_MergeDLQMessages_Result) (success *replicator.MergeDLQMessagesResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_MergeDLQMessages_Result represents the result of a HistoryService.MergeDLQMessages function call. // // The result of a MergeDLQMessages execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_MergeDLQMessages_Result struct { // Value returned by MergeDLQMessages after a successful execution. Success *replicator.MergeDLQMessagesResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` } // ToWire translates a HistoryService_MergeDLQMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_MergeDLQMessages_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_MergeDLQMessages_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _MergeDLQMessagesResponse_Read(w wire.Value) (*replicator.MergeDLQMessagesResponse, error) { var v replicator.MergeDLQMessagesResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_MergeDLQMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_MergeDLQMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_MergeDLQMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_MergeDLQMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _MergeDLQMessagesResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_MergeDLQMessages_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_MergeDLQMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_MergeDLQMessages_Result struct could not be encoded. func (v *HistoryService_MergeDLQMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_MergeDLQMessages_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _MergeDLQMessagesResponse_Decode(sr stream.Reader) (*replicator.MergeDLQMessagesResponse, error) { var v replicator.MergeDLQMessagesResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_MergeDLQMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_MergeDLQMessages_Result struct could not be generated from the wire // representation. func (v *HistoryService_MergeDLQMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _MergeDLQMessagesResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_MergeDLQMessages_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_MergeDLQMessages_Result // struct. func (v *HistoryService_MergeDLQMessages_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } return fmt.Sprintf("HistoryService_MergeDLQMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_MergeDLQMessages_Result match the // provided HistoryService_MergeDLQMessages_Result. // // This function performs a deep comparison. func (v *HistoryService_MergeDLQMessages_Result) Equals(rhs *HistoryService_MergeDLQMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_MergeDLQMessages_Result. func (v *HistoryService_MergeDLQMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_MergeDLQMessages_Result) GetSuccess() (o *replicator.MergeDLQMessagesResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_MergeDLQMessages_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_MergeDLQMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_MergeDLQMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_MergeDLQMessages_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_MergeDLQMessages_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_MergeDLQMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_MergeDLQMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_MergeDLQMessages_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_MergeDLQMessages_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_MergeDLQMessages_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_MergeDLQMessages_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "MergeDLQMessages" for this struct. func (v *HistoryService_MergeDLQMessages_Result) MethodName() string { return "MergeDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_MergeDLQMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_NotifyFailoverMarkers_Args represents the arguments for the HistoryService.NotifyFailoverMarkers function. // // The arguments for NotifyFailoverMarkers are sent and received over the wire as this struct. type HistoryService_NotifyFailoverMarkers_Args struct { Request *NotifyFailoverMarkersRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_NotifyFailoverMarkers_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_NotifyFailoverMarkers_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _NotifyFailoverMarkersRequest_Read(w wire.Value) (*NotifyFailoverMarkersRequest, error) { var v NotifyFailoverMarkersRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_NotifyFailoverMarkers_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_NotifyFailoverMarkers_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_NotifyFailoverMarkers_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_NotifyFailoverMarkers_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _NotifyFailoverMarkersRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_NotifyFailoverMarkers_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_NotifyFailoverMarkers_Args struct could not be encoded. func (v *HistoryService_NotifyFailoverMarkers_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _NotifyFailoverMarkersRequest_Decode(sr stream.Reader) (*NotifyFailoverMarkersRequest, error) { var v NotifyFailoverMarkersRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_NotifyFailoverMarkers_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_NotifyFailoverMarkers_Args struct could not be generated from the wire // representation. func (v *HistoryService_NotifyFailoverMarkers_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _NotifyFailoverMarkersRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_NotifyFailoverMarkers_Args // struct. func (v *HistoryService_NotifyFailoverMarkers_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_NotifyFailoverMarkers_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_NotifyFailoverMarkers_Args match the // provided HistoryService_NotifyFailoverMarkers_Args. // // This function performs a deep comparison. func (v *HistoryService_NotifyFailoverMarkers_Args) Equals(rhs *HistoryService_NotifyFailoverMarkers_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_NotifyFailoverMarkers_Args. func (v *HistoryService_NotifyFailoverMarkers_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_NotifyFailoverMarkers_Args) GetRequest() (o *NotifyFailoverMarkersRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_NotifyFailoverMarkers_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "NotifyFailoverMarkers" for this struct. func (v *HistoryService_NotifyFailoverMarkers_Args) MethodName() string { return "NotifyFailoverMarkers" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_NotifyFailoverMarkers_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_NotifyFailoverMarkers_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.NotifyFailoverMarkers // function. var HistoryService_NotifyFailoverMarkers_Helper = struct { // Args accepts the parameters of NotifyFailoverMarkers in-order and returns // the arguments struct for the function. Args func( request *NotifyFailoverMarkersRequest, ) *HistoryService_NotifyFailoverMarkers_Args // IsException returns true if the given error can be thrown // by NotifyFailoverMarkers. // // An error can be thrown by NotifyFailoverMarkers only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for NotifyFailoverMarkers // given the error returned by it. The provided error may // be nil if NotifyFailoverMarkers did not fail. // // This allows mapping errors returned by NotifyFailoverMarkers into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // NotifyFailoverMarkers // // err := NotifyFailoverMarkers(args) // result, err := HistoryService_NotifyFailoverMarkers_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from NotifyFailoverMarkers: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_NotifyFailoverMarkers_Result, error) // UnwrapResponse takes the result struct for NotifyFailoverMarkers // and returns the erorr returned by it (if any). // // The error is non-nil only if NotifyFailoverMarkers threw an // exception. // // result := deserialize(bytes) // err := HistoryService_NotifyFailoverMarkers_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_NotifyFailoverMarkers_Result) error }{} func init() { HistoryService_NotifyFailoverMarkers_Helper.Args = func( request *NotifyFailoverMarkersRequest, ) *HistoryService_NotifyFailoverMarkers_Args { return &HistoryService_NotifyFailoverMarkers_Args{ Request: request, } } HistoryService_NotifyFailoverMarkers_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true default: return false } } HistoryService_NotifyFailoverMarkers_Helper.WrapResponse = func(err error) (*HistoryService_NotifyFailoverMarkers_Result, error) { if err == nil { return &HistoryService_NotifyFailoverMarkers_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_NotifyFailoverMarkers_Result.BadRequestError") } return &HistoryService_NotifyFailoverMarkers_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_NotifyFailoverMarkers_Result.InternalServiceError") } return &HistoryService_NotifyFailoverMarkers_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_NotifyFailoverMarkers_Result.ServiceBusyError") } return &HistoryService_NotifyFailoverMarkers_Result{ServiceBusyError: e}, nil } return nil, err } HistoryService_NotifyFailoverMarkers_Helper.UnwrapResponse = func(result *HistoryService_NotifyFailoverMarkers_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } return } } // HistoryService_NotifyFailoverMarkers_Result represents the result of a HistoryService.NotifyFailoverMarkers function call. // // The result of a NotifyFailoverMarkers execution is sent and received over the wire as this struct. type HistoryService_NotifyFailoverMarkers_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a HistoryService_NotifyFailoverMarkers_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_NotifyFailoverMarkers_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_NotifyFailoverMarkers_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_NotifyFailoverMarkers_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_NotifyFailoverMarkers_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_NotifyFailoverMarkers_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_NotifyFailoverMarkers_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_NotifyFailoverMarkers_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_NotifyFailoverMarkers_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_NotifyFailoverMarkers_Result struct could not be encoded. func (v *HistoryService_NotifyFailoverMarkers_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_NotifyFailoverMarkers_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_NotifyFailoverMarkers_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_NotifyFailoverMarkers_Result struct could not be generated from the wire // representation. func (v *HistoryService_NotifyFailoverMarkers_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_NotifyFailoverMarkers_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_NotifyFailoverMarkers_Result // struct. func (v *HistoryService_NotifyFailoverMarkers_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("HistoryService_NotifyFailoverMarkers_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_NotifyFailoverMarkers_Result match the // provided HistoryService_NotifyFailoverMarkers_Result. // // This function performs a deep comparison. func (v *HistoryService_NotifyFailoverMarkers_Result) Equals(rhs *HistoryService_NotifyFailoverMarkers_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_NotifyFailoverMarkers_Result. func (v *HistoryService_NotifyFailoverMarkers_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_NotifyFailoverMarkers_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_NotifyFailoverMarkers_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_NotifyFailoverMarkers_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_NotifyFailoverMarkers_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_NotifyFailoverMarkers_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_NotifyFailoverMarkers_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "NotifyFailoverMarkers" for this struct. func (v *HistoryService_NotifyFailoverMarkers_Result) MethodName() string { return "NotifyFailoverMarkers" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_NotifyFailoverMarkers_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_PollMutableState_Args represents the arguments for the HistoryService.PollMutableState function. // // The arguments for PollMutableState are sent and received over the wire as this struct. type HistoryService_PollMutableState_Args struct { PollRequest *PollMutableStateRequest `json:"pollRequest,omitempty"` } // ToWire translates a HistoryService_PollMutableState_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_PollMutableState_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.PollRequest != nil { w, err = v.PollRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollMutableStateRequest_Read(w wire.Value) (*PollMutableStateRequest, error) { var v PollMutableStateRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_PollMutableState_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_PollMutableState_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_PollMutableState_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_PollMutableState_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.PollRequest, err = _PollMutableStateRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_PollMutableState_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_PollMutableState_Args struct could not be encoded. func (v *HistoryService_PollMutableState_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.PollRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.PollRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollMutableStateRequest_Decode(sr stream.Reader) (*PollMutableStateRequest, error) { var v PollMutableStateRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_PollMutableState_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_PollMutableState_Args struct could not be generated from the wire // representation. func (v *HistoryService_PollMutableState_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.PollRequest, err = _PollMutableStateRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_PollMutableState_Args // struct. func (v *HistoryService_PollMutableState_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.PollRequest != nil { fields[i] = fmt.Sprintf("PollRequest: %v", v.PollRequest) i++ } return fmt.Sprintf("HistoryService_PollMutableState_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_PollMutableState_Args match the // provided HistoryService_PollMutableState_Args. // // This function performs a deep comparison. func (v *HistoryService_PollMutableState_Args) Equals(rhs *HistoryService_PollMutableState_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.PollRequest == nil && rhs.PollRequest == nil) || (v.PollRequest != nil && rhs.PollRequest != nil && v.PollRequest.Equals(rhs.PollRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_PollMutableState_Args. func (v *HistoryService_PollMutableState_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.PollRequest != nil { err = multierr.Append(err, enc.AddObject("pollRequest", v.PollRequest)) } return err } // GetPollRequest returns the value of PollRequest if it is set or its // zero value if it is unset. func (v *HistoryService_PollMutableState_Args) GetPollRequest() (o *PollMutableStateRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // IsSetPollRequest returns true if PollRequest is not nil. func (v *HistoryService_PollMutableState_Args) IsSetPollRequest() bool { return v != nil && v.PollRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "PollMutableState" for this struct. func (v *HistoryService_PollMutableState_Args) MethodName() string { return "PollMutableState" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_PollMutableState_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_PollMutableState_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.PollMutableState // function. var HistoryService_PollMutableState_Helper = struct { // Args accepts the parameters of PollMutableState in-order and returns // the arguments struct for the function. Args func( pollRequest *PollMutableStateRequest, ) *HistoryService_PollMutableState_Args // IsException returns true if the given error can be thrown // by PollMutableState. // // An error can be thrown by PollMutableState only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for PollMutableState // given its return value and error. // // This allows mapping values and errors returned by // PollMutableState into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by PollMutableState // // value, err := PollMutableState(args) // result, err := HistoryService_PollMutableState_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from PollMutableState: %v", err) // } // serialize(result) WrapResponse func(*PollMutableStateResponse, error) (*HistoryService_PollMutableState_Result, error) // UnwrapResponse takes the result struct for PollMutableState // and returns the value or error returned by it. // // The error is non-nil only if PollMutableState threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_PollMutableState_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_PollMutableState_Result) (*PollMutableStateResponse, error) }{} func init() { HistoryService_PollMutableState_Helper.Args = func( pollRequest *PollMutableStateRequest, ) *HistoryService_PollMutableState_Args { return &HistoryService_PollMutableState_Args{ PollRequest: pollRequest, } } HistoryService_PollMutableState_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.CurrentBranchChangedError: return true default: return false } } HistoryService_PollMutableState_Helper.WrapResponse = func(success *PollMutableStateResponse, err error) (*HistoryService_PollMutableState_Result, error) { if err == nil { return &HistoryService_PollMutableState_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PollMutableState_Result.BadRequestError") } return &HistoryService_PollMutableState_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PollMutableState_Result.InternalServiceError") } return &HistoryService_PollMutableState_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PollMutableState_Result.EntityNotExistError") } return &HistoryService_PollMutableState_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PollMutableState_Result.ShardOwnershipLostError") } return &HistoryService_PollMutableState_Result{ShardOwnershipLostError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PollMutableState_Result.LimitExceededError") } return &HistoryService_PollMutableState_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PollMutableState_Result.ServiceBusyError") } return &HistoryService_PollMutableState_Result{ServiceBusyError: e}, nil case *shared.CurrentBranchChangedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PollMutableState_Result.CurrentBranchChangedError") } return &HistoryService_PollMutableState_Result{CurrentBranchChangedError: e}, nil } return nil, err } HistoryService_PollMutableState_Helper.UnwrapResponse = func(result *HistoryService_PollMutableState_Result) (success *PollMutableStateResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.CurrentBranchChangedError != nil { err = result.CurrentBranchChangedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_PollMutableState_Result represents the result of a HistoryService.PollMutableState function call. // // The result of a PollMutableState execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_PollMutableState_Result struct { // Value returned by PollMutableState after a successful execution. Success *PollMutableStateResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` CurrentBranchChangedError *shared.CurrentBranchChangedError `json:"currentBranchChangedError,omitempty"` } // ToWire translates a HistoryService_PollMutableState_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_PollMutableState_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.CurrentBranchChangedError != nil { w, err = v.CurrentBranchChangedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_PollMutableState_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollMutableStateResponse_Read(w wire.Value) (*PollMutableStateResponse, error) { var v PollMutableStateResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_PollMutableState_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_PollMutableState_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_PollMutableState_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_PollMutableState_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _PollMutableStateResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.CurrentBranchChangedError, err = _CurrentBranchChangedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.CurrentBranchChangedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_PollMutableState_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_PollMutableState_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_PollMutableState_Result struct could not be encoded. func (v *HistoryService_PollMutableState_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CurrentBranchChangedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.CurrentBranchChangedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.CurrentBranchChangedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_PollMutableState_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _PollMutableStateResponse_Decode(sr stream.Reader) (*PollMutableStateResponse, error) { var v PollMutableStateResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_PollMutableState_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_PollMutableState_Result struct could not be generated from the wire // representation. func (v *HistoryService_PollMutableState_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _PollMutableStateResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.CurrentBranchChangedError, err = _CurrentBranchChangedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.CurrentBranchChangedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_PollMutableState_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_PollMutableState_Result // struct. func (v *HistoryService_PollMutableState_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.CurrentBranchChangedError != nil { fields[i] = fmt.Sprintf("CurrentBranchChangedError: %v", v.CurrentBranchChangedError) i++ } return fmt.Sprintf("HistoryService_PollMutableState_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_PollMutableState_Result match the // provided HistoryService_PollMutableState_Result. // // This function performs a deep comparison. func (v *HistoryService_PollMutableState_Result) Equals(rhs *HistoryService_PollMutableState_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.CurrentBranchChangedError == nil && rhs.CurrentBranchChangedError == nil) || (v.CurrentBranchChangedError != nil && rhs.CurrentBranchChangedError != nil && v.CurrentBranchChangedError.Equals(rhs.CurrentBranchChangedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_PollMutableState_Result. func (v *HistoryService_PollMutableState_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.CurrentBranchChangedError != nil { err = multierr.Append(err, enc.AddObject("currentBranchChangedError", v.CurrentBranchChangedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_PollMutableState_Result) GetSuccess() (o *PollMutableStateResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_PollMutableState_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_PollMutableState_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_PollMutableState_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_PollMutableState_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_PollMutableState_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_PollMutableState_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_PollMutableState_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_PollMutableState_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_PollMutableState_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_PollMutableState_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_PollMutableState_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_PollMutableState_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_PollMutableState_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetCurrentBranchChangedError returns the value of CurrentBranchChangedError if it is set or its // zero value if it is unset. func (v *HistoryService_PollMutableState_Result) GetCurrentBranchChangedError() (o *shared.CurrentBranchChangedError) { if v != nil && v.CurrentBranchChangedError != nil { return v.CurrentBranchChangedError } return } // IsSetCurrentBranchChangedError returns true if CurrentBranchChangedError is not nil. func (v *HistoryService_PollMutableState_Result) IsSetCurrentBranchChangedError() bool { return v != nil && v.CurrentBranchChangedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "PollMutableState" for this struct. func (v *HistoryService_PollMutableState_Result) MethodName() string { return "PollMutableState" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_PollMutableState_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_PurgeDLQMessages_Args represents the arguments for the HistoryService.PurgeDLQMessages function. // // The arguments for PurgeDLQMessages are sent and received over the wire as this struct. type HistoryService_PurgeDLQMessages_Args struct { Request *replicator.PurgeDLQMessagesRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_PurgeDLQMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_PurgeDLQMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PurgeDLQMessagesRequest_Read(w wire.Value) (*replicator.PurgeDLQMessagesRequest, error) { var v replicator.PurgeDLQMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_PurgeDLQMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_PurgeDLQMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_PurgeDLQMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_PurgeDLQMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _PurgeDLQMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_PurgeDLQMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_PurgeDLQMessages_Args struct could not be encoded. func (v *HistoryService_PurgeDLQMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PurgeDLQMessagesRequest_Decode(sr stream.Reader) (*replicator.PurgeDLQMessagesRequest, error) { var v replicator.PurgeDLQMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_PurgeDLQMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_PurgeDLQMessages_Args struct could not be generated from the wire // representation. func (v *HistoryService_PurgeDLQMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _PurgeDLQMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_PurgeDLQMessages_Args // struct. func (v *HistoryService_PurgeDLQMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_PurgeDLQMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_PurgeDLQMessages_Args match the // provided HistoryService_PurgeDLQMessages_Args. // // This function performs a deep comparison. func (v *HistoryService_PurgeDLQMessages_Args) Equals(rhs *HistoryService_PurgeDLQMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_PurgeDLQMessages_Args. func (v *HistoryService_PurgeDLQMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_PurgeDLQMessages_Args) GetRequest() (o *replicator.PurgeDLQMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_PurgeDLQMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "PurgeDLQMessages" for this struct. func (v *HistoryService_PurgeDLQMessages_Args) MethodName() string { return "PurgeDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_PurgeDLQMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_PurgeDLQMessages_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.PurgeDLQMessages // function. var HistoryService_PurgeDLQMessages_Helper = struct { // Args accepts the parameters of PurgeDLQMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.PurgeDLQMessagesRequest, ) *HistoryService_PurgeDLQMessages_Args // IsException returns true if the given error can be thrown // by PurgeDLQMessages. // // An error can be thrown by PurgeDLQMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for PurgeDLQMessages // given the error returned by it. The provided error may // be nil if PurgeDLQMessages did not fail. // // This allows mapping errors returned by PurgeDLQMessages into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // PurgeDLQMessages // // err := PurgeDLQMessages(args) // result, err := HistoryService_PurgeDLQMessages_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from PurgeDLQMessages: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_PurgeDLQMessages_Result, error) // UnwrapResponse takes the result struct for PurgeDLQMessages // and returns the erorr returned by it (if any). // // The error is non-nil only if PurgeDLQMessages threw an // exception. // // result := deserialize(bytes) // err := HistoryService_PurgeDLQMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_PurgeDLQMessages_Result) error }{} func init() { HistoryService_PurgeDLQMessages_Helper.Args = func( request *replicator.PurgeDLQMessagesRequest, ) *HistoryService_PurgeDLQMessages_Args { return &HistoryService_PurgeDLQMessages_Args{ Request: request, } } HistoryService_PurgeDLQMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true default: return false } } HistoryService_PurgeDLQMessages_Helper.WrapResponse = func(err error) (*HistoryService_PurgeDLQMessages_Result, error) { if err == nil { return &HistoryService_PurgeDLQMessages_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PurgeDLQMessages_Result.BadRequestError") } return &HistoryService_PurgeDLQMessages_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PurgeDLQMessages_Result.InternalServiceError") } return &HistoryService_PurgeDLQMessages_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PurgeDLQMessages_Result.ServiceBusyError") } return &HistoryService_PurgeDLQMessages_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PurgeDLQMessages_Result.EntityNotExistError") } return &HistoryService_PurgeDLQMessages_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_PurgeDLQMessages_Result.ShardOwnershipLostError") } return &HistoryService_PurgeDLQMessages_Result{ShardOwnershipLostError: e}, nil } return nil, err } HistoryService_PurgeDLQMessages_Helper.UnwrapResponse = func(result *HistoryService_PurgeDLQMessages_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } return } } // HistoryService_PurgeDLQMessages_Result represents the result of a HistoryService.PurgeDLQMessages function call. // // The result of a PurgeDLQMessages execution is sent and received over the wire as this struct. type HistoryService_PurgeDLQMessages_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` } // ToWire translates a HistoryService_PurgeDLQMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_PurgeDLQMessages_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_PurgeDLQMessages_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_PurgeDLQMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_PurgeDLQMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_PurgeDLQMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_PurgeDLQMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_PurgeDLQMessages_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_PurgeDLQMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_PurgeDLQMessages_Result struct could not be encoded. func (v *HistoryService_PurgeDLQMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_PurgeDLQMessages_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_PurgeDLQMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_PurgeDLQMessages_Result struct could not be generated from the wire // representation. func (v *HistoryService_PurgeDLQMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_PurgeDLQMessages_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_PurgeDLQMessages_Result // struct. func (v *HistoryService_PurgeDLQMessages_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } return fmt.Sprintf("HistoryService_PurgeDLQMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_PurgeDLQMessages_Result match the // provided HistoryService_PurgeDLQMessages_Result. // // This function performs a deep comparison. func (v *HistoryService_PurgeDLQMessages_Result) Equals(rhs *HistoryService_PurgeDLQMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_PurgeDLQMessages_Result. func (v *HistoryService_PurgeDLQMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_PurgeDLQMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_PurgeDLQMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_PurgeDLQMessages_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_PurgeDLQMessages_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_PurgeDLQMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_PurgeDLQMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_PurgeDLQMessages_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_PurgeDLQMessages_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_PurgeDLQMessages_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_PurgeDLQMessages_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "PurgeDLQMessages" for this struct. func (v *HistoryService_PurgeDLQMessages_Result) MethodName() string { return "PurgeDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_PurgeDLQMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_QueryWorkflow_Args represents the arguments for the HistoryService.QueryWorkflow function. // // The arguments for QueryWorkflow are sent and received over the wire as this struct. type HistoryService_QueryWorkflow_Args struct { QueryRequest *QueryWorkflowRequest `json:"queryRequest,omitempty"` } // ToWire translates a HistoryService_QueryWorkflow_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_QueryWorkflow_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.QueryRequest != nil { w, err = v.QueryRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryWorkflowRequest_1_Read(w wire.Value) (*QueryWorkflowRequest, error) { var v QueryWorkflowRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_QueryWorkflow_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_QueryWorkflow_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_QueryWorkflow_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_QueryWorkflow_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.QueryRequest, err = _QueryWorkflowRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_QueryWorkflow_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_QueryWorkflow_Args struct could not be encoded. func (v *HistoryService_QueryWorkflow_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.QueryRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.QueryRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryWorkflowRequest_1_Decode(sr stream.Reader) (*QueryWorkflowRequest, error) { var v QueryWorkflowRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_QueryWorkflow_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_QueryWorkflow_Args struct could not be generated from the wire // representation. func (v *HistoryService_QueryWorkflow_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.QueryRequest, err = _QueryWorkflowRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_QueryWorkflow_Args // struct. func (v *HistoryService_QueryWorkflow_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.QueryRequest != nil { fields[i] = fmt.Sprintf("QueryRequest: %v", v.QueryRequest) i++ } return fmt.Sprintf("HistoryService_QueryWorkflow_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_QueryWorkflow_Args match the // provided HistoryService_QueryWorkflow_Args. // // This function performs a deep comparison. func (v *HistoryService_QueryWorkflow_Args) Equals(rhs *HistoryService_QueryWorkflow_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.QueryRequest == nil && rhs.QueryRequest == nil) || (v.QueryRequest != nil && rhs.QueryRequest != nil && v.QueryRequest.Equals(rhs.QueryRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_QueryWorkflow_Args. func (v *HistoryService_QueryWorkflow_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.QueryRequest != nil { err = multierr.Append(err, enc.AddObject("queryRequest", v.QueryRequest)) } return err } // GetQueryRequest returns the value of QueryRequest if it is set or its // zero value if it is unset. func (v *HistoryService_QueryWorkflow_Args) GetQueryRequest() (o *QueryWorkflowRequest) { if v != nil && v.QueryRequest != nil { return v.QueryRequest } return } // IsSetQueryRequest returns true if QueryRequest is not nil. func (v *HistoryService_QueryWorkflow_Args) IsSetQueryRequest() bool { return v != nil && v.QueryRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "QueryWorkflow" for this struct. func (v *HistoryService_QueryWorkflow_Args) MethodName() string { return "QueryWorkflow" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_QueryWorkflow_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_QueryWorkflow_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.QueryWorkflow // function. var HistoryService_QueryWorkflow_Helper = struct { // Args accepts the parameters of QueryWorkflow in-order and returns // the arguments struct for the function. Args func( queryRequest *QueryWorkflowRequest, ) *HistoryService_QueryWorkflow_Args // IsException returns true if the given error can be thrown // by QueryWorkflow. // // An error can be thrown by QueryWorkflow only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for QueryWorkflow // given its return value and error. // // This allows mapping values and errors returned by // QueryWorkflow into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by QueryWorkflow // // value, err := QueryWorkflow(args) // result, err := HistoryService_QueryWorkflow_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from QueryWorkflow: %v", err) // } // serialize(result) WrapResponse func(*QueryWorkflowResponse, error) (*HistoryService_QueryWorkflow_Result, error) // UnwrapResponse takes the result struct for QueryWorkflow // and returns the value or error returned by it. // // The error is non-nil only if QueryWorkflow threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_QueryWorkflow_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_QueryWorkflow_Result) (*QueryWorkflowResponse, error) }{} func init() { HistoryService_QueryWorkflow_Helper.Args = func( queryRequest *QueryWorkflowRequest, ) *HistoryService_QueryWorkflow_Args { return &HistoryService_QueryWorkflow_Args{ QueryRequest: queryRequest, } } HistoryService_QueryWorkflow_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *shared.QueryFailedError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.ClientVersionNotSupportedError: return true default: return false } } HistoryService_QueryWorkflow_Helper.WrapResponse = func(success *QueryWorkflowResponse, err error) (*HistoryService_QueryWorkflow_Result, error) { if err == nil { return &HistoryService_QueryWorkflow_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_QueryWorkflow_Result.BadRequestError") } return &HistoryService_QueryWorkflow_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_QueryWorkflow_Result.InternalServiceError") } return &HistoryService_QueryWorkflow_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_QueryWorkflow_Result.EntityNotExistError") } return &HistoryService_QueryWorkflow_Result{EntityNotExistError: e}, nil case *shared.QueryFailedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_QueryWorkflow_Result.QueryFailedError") } return &HistoryService_QueryWorkflow_Result{QueryFailedError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_QueryWorkflow_Result.LimitExceededError") } return &HistoryService_QueryWorkflow_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_QueryWorkflow_Result.ServiceBusyError") } return &HistoryService_QueryWorkflow_Result{ServiceBusyError: e}, nil case *shared.ClientVersionNotSupportedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_QueryWorkflow_Result.ClientVersionNotSupportedError") } return &HistoryService_QueryWorkflow_Result{ClientVersionNotSupportedError: e}, nil } return nil, err } HistoryService_QueryWorkflow_Helper.UnwrapResponse = func(result *HistoryService_QueryWorkflow_Result) (success *QueryWorkflowResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.QueryFailedError != nil { err = result.QueryFailedError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ClientVersionNotSupportedError != nil { err = result.ClientVersionNotSupportedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_QueryWorkflow_Result represents the result of a HistoryService.QueryWorkflow function call. // // The result of a QueryWorkflow execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_QueryWorkflow_Result struct { // Value returned by QueryWorkflow after a successful execution. Success *QueryWorkflowResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` QueryFailedError *shared.QueryFailedError `json:"queryFailedError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ClientVersionNotSupportedError *shared.ClientVersionNotSupportedError `json:"clientVersionNotSupportedError,omitempty"` } // ToWire translates a HistoryService_QueryWorkflow_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_QueryWorkflow_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.QueryFailedError != nil { w, err = v.QueryFailedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ClientVersionNotSupportedError != nil { w, err = v.ClientVersionNotSupportedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_QueryWorkflow_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryWorkflowResponse_1_Read(w wire.Value) (*QueryWorkflowResponse, error) { var v QueryWorkflowResponse err := v.FromWire(w) return &v, err } func _QueryFailedError_Read(w wire.Value) (*shared.QueryFailedError, error) { var v shared.QueryFailedError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_QueryWorkflow_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_QueryWorkflow_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_QueryWorkflow_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_QueryWorkflow_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _QueryWorkflowResponse_1_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.QueryFailedError, err = _QueryFailedError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.QueryFailedError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_QueryWorkflow_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_QueryWorkflow_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_QueryWorkflow_Result struct could not be encoded. func (v *HistoryService_QueryWorkflow_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryFailedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.QueryFailedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientVersionNotSupportedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ClientVersionNotSupportedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.QueryFailedError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_QueryWorkflow_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _QueryWorkflowResponse_1_Decode(sr stream.Reader) (*QueryWorkflowResponse, error) { var v QueryWorkflowResponse err := v.Decode(sr) return &v, err } func _QueryFailedError_Decode(sr stream.Reader) (*shared.QueryFailedError, error) { var v shared.QueryFailedError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_QueryWorkflow_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_QueryWorkflow_Result struct could not be generated from the wire // representation. func (v *HistoryService_QueryWorkflow_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _QueryWorkflowResponse_1_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.QueryFailedError, err = _QueryFailedError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ClientVersionNotSupportedError, err = _ClientVersionNotSupportedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.QueryFailedError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ClientVersionNotSupportedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_QueryWorkflow_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_QueryWorkflow_Result // struct. func (v *HistoryService_QueryWorkflow_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.QueryFailedError != nil { fields[i] = fmt.Sprintf("QueryFailedError: %v", v.QueryFailedError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ClientVersionNotSupportedError != nil { fields[i] = fmt.Sprintf("ClientVersionNotSupportedError: %v", v.ClientVersionNotSupportedError) i++ } return fmt.Sprintf("HistoryService_QueryWorkflow_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_QueryWorkflow_Result match the // provided HistoryService_QueryWorkflow_Result. // // This function performs a deep comparison. func (v *HistoryService_QueryWorkflow_Result) Equals(rhs *HistoryService_QueryWorkflow_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.QueryFailedError == nil && rhs.QueryFailedError == nil) || (v.QueryFailedError != nil && rhs.QueryFailedError != nil && v.QueryFailedError.Equals(rhs.QueryFailedError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ClientVersionNotSupportedError == nil && rhs.ClientVersionNotSupportedError == nil) || (v.ClientVersionNotSupportedError != nil && rhs.ClientVersionNotSupportedError != nil && v.ClientVersionNotSupportedError.Equals(rhs.ClientVersionNotSupportedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_QueryWorkflow_Result. func (v *HistoryService_QueryWorkflow_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.QueryFailedError != nil { err = multierr.Append(err, enc.AddObject("queryFailedError", v.QueryFailedError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ClientVersionNotSupportedError != nil { err = multierr.Append(err, enc.AddObject("clientVersionNotSupportedError", v.ClientVersionNotSupportedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_QueryWorkflow_Result) GetSuccess() (o *QueryWorkflowResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_QueryWorkflow_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_QueryWorkflow_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_QueryWorkflow_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_QueryWorkflow_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_QueryWorkflow_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_QueryWorkflow_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_QueryWorkflow_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetQueryFailedError returns the value of QueryFailedError if it is set or its // zero value if it is unset. func (v *HistoryService_QueryWorkflow_Result) GetQueryFailedError() (o *shared.QueryFailedError) { if v != nil && v.QueryFailedError != nil { return v.QueryFailedError } return } // IsSetQueryFailedError returns true if QueryFailedError is not nil. func (v *HistoryService_QueryWorkflow_Result) IsSetQueryFailedError() bool { return v != nil && v.QueryFailedError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_QueryWorkflow_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_QueryWorkflow_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_QueryWorkflow_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_QueryWorkflow_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetClientVersionNotSupportedError returns the value of ClientVersionNotSupportedError if it is set or its // zero value if it is unset. func (v *HistoryService_QueryWorkflow_Result) GetClientVersionNotSupportedError() (o *shared.ClientVersionNotSupportedError) { if v != nil && v.ClientVersionNotSupportedError != nil { return v.ClientVersionNotSupportedError } return } // IsSetClientVersionNotSupportedError returns true if ClientVersionNotSupportedError is not nil. func (v *HistoryService_QueryWorkflow_Result) IsSetClientVersionNotSupportedError() bool { return v != nil && v.ClientVersionNotSupportedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "QueryWorkflow" for this struct. func (v *HistoryService_QueryWorkflow_Result) MethodName() string { return "QueryWorkflow" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_QueryWorkflow_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RatelimitUpdate_Args represents the arguments for the HistoryService.RatelimitUpdate function. // // The arguments for RatelimitUpdate are sent and received over the wire as this struct. type HistoryService_RatelimitUpdate_Args struct { Request *RatelimitUpdateRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_RatelimitUpdate_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RatelimitUpdate_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RatelimitUpdateRequest_Read(w wire.Value) (*RatelimitUpdateRequest, error) { var v RatelimitUpdateRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RatelimitUpdate_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RatelimitUpdate_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RatelimitUpdate_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RatelimitUpdate_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _RatelimitUpdateRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RatelimitUpdate_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RatelimitUpdate_Args struct could not be encoded. func (v *HistoryService_RatelimitUpdate_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RatelimitUpdateRequest_Decode(sr stream.Reader) (*RatelimitUpdateRequest, error) { var v RatelimitUpdateRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RatelimitUpdate_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RatelimitUpdate_Args struct could not be generated from the wire // representation. func (v *HistoryService_RatelimitUpdate_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _RatelimitUpdateRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RatelimitUpdate_Args // struct. func (v *HistoryService_RatelimitUpdate_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_RatelimitUpdate_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RatelimitUpdate_Args match the // provided HistoryService_RatelimitUpdate_Args. // // This function performs a deep comparison. func (v *HistoryService_RatelimitUpdate_Args) Equals(rhs *HistoryService_RatelimitUpdate_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RatelimitUpdate_Args. func (v *HistoryService_RatelimitUpdate_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_RatelimitUpdate_Args) GetRequest() (o *RatelimitUpdateRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_RatelimitUpdate_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RatelimitUpdate" for this struct. func (v *HistoryService_RatelimitUpdate_Args) MethodName() string { return "RatelimitUpdate" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RatelimitUpdate_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RatelimitUpdate_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RatelimitUpdate // function. var HistoryService_RatelimitUpdate_Helper = struct { // Args accepts the parameters of RatelimitUpdate in-order and returns // the arguments struct for the function. Args func( request *RatelimitUpdateRequest, ) *HistoryService_RatelimitUpdate_Args // IsException returns true if the given error can be thrown // by RatelimitUpdate. // // An error can be thrown by RatelimitUpdate only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RatelimitUpdate // given its return value and error. // // This allows mapping values and errors returned by // RatelimitUpdate into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RatelimitUpdate // // value, err := RatelimitUpdate(args) // result, err := HistoryService_RatelimitUpdate_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RatelimitUpdate: %v", err) // } // serialize(result) WrapResponse func(*RatelimitUpdateResponse, error) (*HistoryService_RatelimitUpdate_Result, error) // UnwrapResponse takes the result struct for RatelimitUpdate // and returns the value or error returned by it. // // The error is non-nil only if RatelimitUpdate threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_RatelimitUpdate_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RatelimitUpdate_Result) (*RatelimitUpdateResponse, error) }{} func init() { HistoryService_RatelimitUpdate_Helper.Args = func( request *RatelimitUpdateRequest, ) *HistoryService_RatelimitUpdate_Args { return &HistoryService_RatelimitUpdate_Args{ Request: request, } } HistoryService_RatelimitUpdate_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *ShardOwnershipLostError: return true default: return false } } HistoryService_RatelimitUpdate_Helper.WrapResponse = func(success *RatelimitUpdateResponse, err error) (*HistoryService_RatelimitUpdate_Result, error) { if err == nil { return &HistoryService_RatelimitUpdate_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RatelimitUpdate_Result.BadRequestError") } return &HistoryService_RatelimitUpdate_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RatelimitUpdate_Result.InternalServiceError") } return &HistoryService_RatelimitUpdate_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RatelimitUpdate_Result.ServiceBusyError") } return &HistoryService_RatelimitUpdate_Result{ServiceBusyError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RatelimitUpdate_Result.ShardOwnershipLostError") } return &HistoryService_RatelimitUpdate_Result{ShardOwnershipLostError: e}, nil } return nil, err } HistoryService_RatelimitUpdate_Helper.UnwrapResponse = func(result *HistoryService_RatelimitUpdate_Result) (success *RatelimitUpdateResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_RatelimitUpdate_Result represents the result of a HistoryService.RatelimitUpdate function call. // // The result of a RatelimitUpdate execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_RatelimitUpdate_Result struct { // Value returned by RatelimitUpdate after a successful execution. Success *RatelimitUpdateResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` } // ToWire translates a HistoryService_RatelimitUpdate_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RatelimitUpdate_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_RatelimitUpdate_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RatelimitUpdateResponse_Read(w wire.Value) (*RatelimitUpdateResponse, error) { var v RatelimitUpdateResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RatelimitUpdate_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RatelimitUpdate_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RatelimitUpdate_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RatelimitUpdate_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RatelimitUpdateResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RatelimitUpdate_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RatelimitUpdate_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RatelimitUpdate_Result struct could not be encoded. func (v *HistoryService_RatelimitUpdate_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RatelimitUpdate_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _RatelimitUpdateResponse_Decode(sr stream.Reader) (*RatelimitUpdateResponse, error) { var v RatelimitUpdateResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RatelimitUpdate_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RatelimitUpdate_Result struct could not be generated from the wire // representation. func (v *HistoryService_RatelimitUpdate_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RatelimitUpdateResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RatelimitUpdate_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RatelimitUpdate_Result // struct. func (v *HistoryService_RatelimitUpdate_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } return fmt.Sprintf("HistoryService_RatelimitUpdate_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RatelimitUpdate_Result match the // provided HistoryService_RatelimitUpdate_Result. // // This function performs a deep comparison. func (v *HistoryService_RatelimitUpdate_Result) Equals(rhs *HistoryService_RatelimitUpdate_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RatelimitUpdate_Result. func (v *HistoryService_RatelimitUpdate_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_RatelimitUpdate_Result) GetSuccess() (o *RatelimitUpdateResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_RatelimitUpdate_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RatelimitUpdate_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RatelimitUpdate_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RatelimitUpdate_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RatelimitUpdate_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RatelimitUpdate_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RatelimitUpdate_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RatelimitUpdate_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RatelimitUpdate_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RatelimitUpdate" for this struct. func (v *HistoryService_RatelimitUpdate_Result) MethodName() string { return "RatelimitUpdate" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RatelimitUpdate_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_ReadDLQMessages_Args represents the arguments for the HistoryService.ReadDLQMessages function. // // The arguments for ReadDLQMessages are sent and received over the wire as this struct. type HistoryService_ReadDLQMessages_Args struct { Request *replicator.ReadDLQMessagesRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_ReadDLQMessages_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ReadDLQMessages_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReadDLQMessagesRequest_Read(w wire.Value) (*replicator.ReadDLQMessagesRequest, error) { var v replicator.ReadDLQMessagesRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ReadDLQMessages_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ReadDLQMessages_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ReadDLQMessages_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ReadDLQMessages_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _ReadDLQMessagesRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_ReadDLQMessages_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ReadDLQMessages_Args struct could not be encoded. func (v *HistoryService_ReadDLQMessages_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReadDLQMessagesRequest_Decode(sr stream.Reader) (*replicator.ReadDLQMessagesRequest, error) { var v replicator.ReadDLQMessagesRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ReadDLQMessages_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ReadDLQMessages_Args struct could not be generated from the wire // representation. func (v *HistoryService_ReadDLQMessages_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _ReadDLQMessagesRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_ReadDLQMessages_Args // struct. func (v *HistoryService_ReadDLQMessages_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_ReadDLQMessages_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ReadDLQMessages_Args match the // provided HistoryService_ReadDLQMessages_Args. // // This function performs a deep comparison. func (v *HistoryService_ReadDLQMessages_Args) Equals(rhs *HistoryService_ReadDLQMessages_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ReadDLQMessages_Args. func (v *HistoryService_ReadDLQMessages_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_ReadDLQMessages_Args) GetRequest() (o *replicator.ReadDLQMessagesRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_ReadDLQMessages_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ReadDLQMessages" for this struct. func (v *HistoryService_ReadDLQMessages_Args) MethodName() string { return "ReadDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_ReadDLQMessages_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_ReadDLQMessages_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.ReadDLQMessages // function. var HistoryService_ReadDLQMessages_Helper = struct { // Args accepts the parameters of ReadDLQMessages in-order and returns // the arguments struct for the function. Args func( request *replicator.ReadDLQMessagesRequest, ) *HistoryService_ReadDLQMessages_Args // IsException returns true if the given error can be thrown // by ReadDLQMessages. // // An error can be thrown by ReadDLQMessages only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ReadDLQMessages // given its return value and error. // // This allows mapping values and errors returned by // ReadDLQMessages into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ReadDLQMessages // // value, err := ReadDLQMessages(args) // result, err := HistoryService_ReadDLQMessages_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ReadDLQMessages: %v", err) // } // serialize(result) WrapResponse func(*replicator.ReadDLQMessagesResponse, error) (*HistoryService_ReadDLQMessages_Result, error) // UnwrapResponse takes the result struct for ReadDLQMessages // and returns the value or error returned by it. // // The error is non-nil only if ReadDLQMessages threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_ReadDLQMessages_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_ReadDLQMessages_Result) (*replicator.ReadDLQMessagesResponse, error) }{} func init() { HistoryService_ReadDLQMessages_Helper.Args = func( request *replicator.ReadDLQMessagesRequest, ) *HistoryService_ReadDLQMessages_Args { return &HistoryService_ReadDLQMessages_Args{ Request: request, } } HistoryService_ReadDLQMessages_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true default: return false } } HistoryService_ReadDLQMessages_Helper.WrapResponse = func(success *replicator.ReadDLQMessagesResponse, err error) (*HistoryService_ReadDLQMessages_Result, error) { if err == nil { return &HistoryService_ReadDLQMessages_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReadDLQMessages_Result.BadRequestError") } return &HistoryService_ReadDLQMessages_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReadDLQMessages_Result.InternalServiceError") } return &HistoryService_ReadDLQMessages_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReadDLQMessages_Result.ServiceBusyError") } return &HistoryService_ReadDLQMessages_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReadDLQMessages_Result.EntityNotExistError") } return &HistoryService_ReadDLQMessages_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReadDLQMessages_Result.ShardOwnershipLostError") } return &HistoryService_ReadDLQMessages_Result{ShardOwnershipLostError: e}, nil } return nil, err } HistoryService_ReadDLQMessages_Helper.UnwrapResponse = func(result *HistoryService_ReadDLQMessages_Result) (success *replicator.ReadDLQMessagesResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_ReadDLQMessages_Result represents the result of a HistoryService.ReadDLQMessages function call. // // The result of a ReadDLQMessages execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_ReadDLQMessages_Result struct { // Value returned by ReadDLQMessages after a successful execution. Success *replicator.ReadDLQMessagesResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` } // ToWire translates a HistoryService_ReadDLQMessages_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ReadDLQMessages_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_ReadDLQMessages_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReadDLQMessagesResponse_Read(w wire.Value) (*replicator.ReadDLQMessagesResponse, error) { var v replicator.ReadDLQMessagesResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ReadDLQMessages_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ReadDLQMessages_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ReadDLQMessages_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ReadDLQMessages_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ReadDLQMessagesResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_ReadDLQMessages_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_ReadDLQMessages_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ReadDLQMessages_Result struct could not be encoded. func (v *HistoryService_ReadDLQMessages_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_ReadDLQMessages_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ReadDLQMessagesResponse_Decode(sr stream.Reader) (*replicator.ReadDLQMessagesResponse, error) { var v replicator.ReadDLQMessagesResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ReadDLQMessages_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ReadDLQMessages_Result struct could not be generated from the wire // representation. func (v *HistoryService_ReadDLQMessages_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ReadDLQMessagesResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_ReadDLQMessages_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_ReadDLQMessages_Result // struct. func (v *HistoryService_ReadDLQMessages_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } return fmt.Sprintf("HistoryService_ReadDLQMessages_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ReadDLQMessages_Result match the // provided HistoryService_ReadDLQMessages_Result. // // This function performs a deep comparison. func (v *HistoryService_ReadDLQMessages_Result) Equals(rhs *HistoryService_ReadDLQMessages_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ReadDLQMessages_Result. func (v *HistoryService_ReadDLQMessages_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_ReadDLQMessages_Result) GetSuccess() (o *replicator.ReadDLQMessagesResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_ReadDLQMessages_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_ReadDLQMessages_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_ReadDLQMessages_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_ReadDLQMessages_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_ReadDLQMessages_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_ReadDLQMessages_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_ReadDLQMessages_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_ReadDLQMessages_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_ReadDLQMessages_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_ReadDLQMessages_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_ReadDLQMessages_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ReadDLQMessages" for this struct. func (v *HistoryService_ReadDLQMessages_Result) MethodName() string { return "ReadDLQMessages" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_ReadDLQMessages_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_ReapplyEvents_Args represents the arguments for the HistoryService.ReapplyEvents function. // // The arguments for ReapplyEvents are sent and received over the wire as this struct. type HistoryService_ReapplyEvents_Args struct { ReapplyEventsRequest *ReapplyEventsRequest `json:"reapplyEventsRequest,omitempty"` } // ToWire translates a HistoryService_ReapplyEvents_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ReapplyEvents_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ReapplyEventsRequest != nil { w, err = v.ReapplyEventsRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReapplyEventsRequest_1_Read(w wire.Value) (*ReapplyEventsRequest, error) { var v ReapplyEventsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ReapplyEvents_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ReapplyEvents_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ReapplyEvents_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ReapplyEvents_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ReapplyEventsRequest, err = _ReapplyEventsRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_ReapplyEvents_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ReapplyEvents_Args struct could not be encoded. func (v *HistoryService_ReapplyEvents_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ReapplyEventsRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ReapplyEventsRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReapplyEventsRequest_1_Decode(sr stream.Reader) (*ReapplyEventsRequest, error) { var v ReapplyEventsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ReapplyEvents_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ReapplyEvents_Args struct could not be generated from the wire // representation. func (v *HistoryService_ReapplyEvents_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ReapplyEventsRequest, err = _ReapplyEventsRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_ReapplyEvents_Args // struct. func (v *HistoryService_ReapplyEvents_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ReapplyEventsRequest != nil { fields[i] = fmt.Sprintf("ReapplyEventsRequest: %v", v.ReapplyEventsRequest) i++ } return fmt.Sprintf("HistoryService_ReapplyEvents_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ReapplyEvents_Args match the // provided HistoryService_ReapplyEvents_Args. // // This function performs a deep comparison. func (v *HistoryService_ReapplyEvents_Args) Equals(rhs *HistoryService_ReapplyEvents_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ReapplyEventsRequest == nil && rhs.ReapplyEventsRequest == nil) || (v.ReapplyEventsRequest != nil && rhs.ReapplyEventsRequest != nil && v.ReapplyEventsRequest.Equals(rhs.ReapplyEventsRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ReapplyEvents_Args. func (v *HistoryService_ReapplyEvents_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ReapplyEventsRequest != nil { err = multierr.Append(err, enc.AddObject("reapplyEventsRequest", v.ReapplyEventsRequest)) } return err } // GetReapplyEventsRequest returns the value of ReapplyEventsRequest if it is set or its // zero value if it is unset. func (v *HistoryService_ReapplyEvents_Args) GetReapplyEventsRequest() (o *ReapplyEventsRequest) { if v != nil && v.ReapplyEventsRequest != nil { return v.ReapplyEventsRequest } return } // IsSetReapplyEventsRequest returns true if ReapplyEventsRequest is not nil. func (v *HistoryService_ReapplyEvents_Args) IsSetReapplyEventsRequest() bool { return v != nil && v.ReapplyEventsRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ReapplyEvents" for this struct. func (v *HistoryService_ReapplyEvents_Args) MethodName() string { return "ReapplyEvents" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_ReapplyEvents_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_ReapplyEvents_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.ReapplyEvents // function. var HistoryService_ReapplyEvents_Helper = struct { // Args accepts the parameters of ReapplyEvents in-order and returns // the arguments struct for the function. Args func( reapplyEventsRequest *ReapplyEventsRequest, ) *HistoryService_ReapplyEvents_Args // IsException returns true if the given error can be thrown // by ReapplyEvents. // // An error can be thrown by ReapplyEvents only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ReapplyEvents // given the error returned by it. The provided error may // be nil if ReapplyEvents did not fail. // // This allows mapping errors returned by ReapplyEvents into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // ReapplyEvents // // err := ReapplyEvents(args) // result, err := HistoryService_ReapplyEvents_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from ReapplyEvents: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_ReapplyEvents_Result, error) // UnwrapResponse takes the result struct for ReapplyEvents // and returns the erorr returned by it (if any). // // The error is non-nil only if ReapplyEvents threw an // exception. // // result := deserialize(bytes) // err := HistoryService_ReapplyEvents_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_ReapplyEvents_Result) error }{} func init() { HistoryService_ReapplyEvents_Helper.Args = func( reapplyEventsRequest *ReapplyEventsRequest, ) *HistoryService_ReapplyEvents_Args { return &HistoryService_ReapplyEvents_Args{ ReapplyEventsRequest: reapplyEventsRequest, } } HistoryService_ReapplyEvents_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *ShardOwnershipLostError: return true case *shared.EntityNotExistsError: return true default: return false } } HistoryService_ReapplyEvents_Helper.WrapResponse = func(err error) (*HistoryService_ReapplyEvents_Result, error) { if err == nil { return &HistoryService_ReapplyEvents_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReapplyEvents_Result.BadRequestError") } return &HistoryService_ReapplyEvents_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReapplyEvents_Result.InternalServiceError") } return &HistoryService_ReapplyEvents_Result{InternalServiceError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReapplyEvents_Result.DomainNotActiveError") } return &HistoryService_ReapplyEvents_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReapplyEvents_Result.LimitExceededError") } return &HistoryService_ReapplyEvents_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReapplyEvents_Result.ServiceBusyError") } return &HistoryService_ReapplyEvents_Result{ServiceBusyError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReapplyEvents_Result.ShardOwnershipLostError") } return &HistoryService_ReapplyEvents_Result{ShardOwnershipLostError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReapplyEvents_Result.EntityNotExistError") } return &HistoryService_ReapplyEvents_Result{EntityNotExistError: e}, nil } return nil, err } HistoryService_ReapplyEvents_Helper.UnwrapResponse = func(result *HistoryService_ReapplyEvents_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } return } } // HistoryService_ReapplyEvents_Result represents the result of a HistoryService.ReapplyEvents function call. // // The result of a ReapplyEvents execution is sent and received over the wire as this struct. type HistoryService_ReapplyEvents_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` } // ToWire translates a HistoryService_ReapplyEvents_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ReapplyEvents_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_ReapplyEvents_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DomainNotActiveError_Read(w wire.Value) (*shared.DomainNotActiveError, error) { var v shared.DomainNotActiveError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ReapplyEvents_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ReapplyEvents_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ReapplyEvents_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ReapplyEvents_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ReapplyEvents_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_ReapplyEvents_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ReapplyEvents_Result struct could not be encoded. func (v *HistoryService_ReapplyEvents_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ReapplyEvents_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _DomainNotActiveError_Decode(sr stream.Reader) (*shared.DomainNotActiveError, error) { var v shared.DomainNotActiveError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ReapplyEvents_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ReapplyEvents_Result struct could not be generated from the wire // representation. func (v *HistoryService_ReapplyEvents_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ReapplyEvents_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_ReapplyEvents_Result // struct. func (v *HistoryService_ReapplyEvents_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } return fmt.Sprintf("HistoryService_ReapplyEvents_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ReapplyEvents_Result match the // provided HistoryService_ReapplyEvents_Result. // // This function performs a deep comparison. func (v *HistoryService_ReapplyEvents_Result) Equals(rhs *HistoryService_ReapplyEvents_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ReapplyEvents_Result. func (v *HistoryService_ReapplyEvents_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_ReapplyEvents_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_ReapplyEvents_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_ReapplyEvents_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_ReapplyEvents_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_ReapplyEvents_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_ReapplyEvents_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_ReapplyEvents_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_ReapplyEvents_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_ReapplyEvents_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_ReapplyEvents_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_ReapplyEvents_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_ReapplyEvents_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_ReapplyEvents_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_ReapplyEvents_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ReapplyEvents" for this struct. func (v *HistoryService_ReapplyEvents_Result) MethodName() string { return "ReapplyEvents" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_ReapplyEvents_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RecordActivityTaskHeartbeat_Args represents the arguments for the HistoryService.RecordActivityTaskHeartbeat function. // // The arguments for RecordActivityTaskHeartbeat are sent and received over the wire as this struct. type HistoryService_RecordActivityTaskHeartbeat_Args struct { HeartbeatRequest *RecordActivityTaskHeartbeatRequest `json:"heartbeatRequest,omitempty"` } // ToWire translates a HistoryService_RecordActivityTaskHeartbeat_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RecordActivityTaskHeartbeat_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.HeartbeatRequest != nil { w, err = v.HeartbeatRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordActivityTaskHeartbeatRequest_1_Read(w wire.Value) (*RecordActivityTaskHeartbeatRequest, error) { var v RecordActivityTaskHeartbeatRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RecordActivityTaskHeartbeat_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RecordActivityTaskHeartbeat_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RecordActivityTaskHeartbeat_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RecordActivityTaskHeartbeat_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.HeartbeatRequest, err = _RecordActivityTaskHeartbeatRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RecordActivityTaskHeartbeat_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RecordActivityTaskHeartbeat_Args struct could not be encoded. func (v *HistoryService_RecordActivityTaskHeartbeat_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.HeartbeatRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.HeartbeatRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RecordActivityTaskHeartbeatRequest_1_Decode(sr stream.Reader) (*RecordActivityTaskHeartbeatRequest, error) { var v RecordActivityTaskHeartbeatRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RecordActivityTaskHeartbeat_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RecordActivityTaskHeartbeat_Args struct could not be generated from the wire // representation. func (v *HistoryService_RecordActivityTaskHeartbeat_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.HeartbeatRequest, err = _RecordActivityTaskHeartbeatRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RecordActivityTaskHeartbeat_Args // struct. func (v *HistoryService_RecordActivityTaskHeartbeat_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.HeartbeatRequest != nil { fields[i] = fmt.Sprintf("HeartbeatRequest: %v", v.HeartbeatRequest) i++ } return fmt.Sprintf("HistoryService_RecordActivityTaskHeartbeat_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RecordActivityTaskHeartbeat_Args match the // provided HistoryService_RecordActivityTaskHeartbeat_Args. // // This function performs a deep comparison. func (v *HistoryService_RecordActivityTaskHeartbeat_Args) Equals(rhs *HistoryService_RecordActivityTaskHeartbeat_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.HeartbeatRequest == nil && rhs.HeartbeatRequest == nil) || (v.HeartbeatRequest != nil && rhs.HeartbeatRequest != nil && v.HeartbeatRequest.Equals(rhs.HeartbeatRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RecordActivityTaskHeartbeat_Args. func (v *HistoryService_RecordActivityTaskHeartbeat_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.HeartbeatRequest != nil { err = multierr.Append(err, enc.AddObject("heartbeatRequest", v.HeartbeatRequest)) } return err } // GetHeartbeatRequest returns the value of HeartbeatRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskHeartbeat_Args) GetHeartbeatRequest() (o *RecordActivityTaskHeartbeatRequest) { if v != nil && v.HeartbeatRequest != nil { return v.HeartbeatRequest } return } // IsSetHeartbeatRequest returns true if HeartbeatRequest is not nil. func (v *HistoryService_RecordActivityTaskHeartbeat_Args) IsSetHeartbeatRequest() bool { return v != nil && v.HeartbeatRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RecordActivityTaskHeartbeat" for this struct. func (v *HistoryService_RecordActivityTaskHeartbeat_Args) MethodName() string { return "RecordActivityTaskHeartbeat" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RecordActivityTaskHeartbeat_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RecordActivityTaskHeartbeat_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RecordActivityTaskHeartbeat // function. var HistoryService_RecordActivityTaskHeartbeat_Helper = struct { // Args accepts the parameters of RecordActivityTaskHeartbeat in-order and returns // the arguments struct for the function. Args func( heartbeatRequest *RecordActivityTaskHeartbeatRequest, ) *HistoryService_RecordActivityTaskHeartbeat_Args // IsException returns true if the given error can be thrown // by RecordActivityTaskHeartbeat. // // An error can be thrown by RecordActivityTaskHeartbeat only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RecordActivityTaskHeartbeat // given its return value and error. // // This allows mapping values and errors returned by // RecordActivityTaskHeartbeat into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RecordActivityTaskHeartbeat // // value, err := RecordActivityTaskHeartbeat(args) // result, err := HistoryService_RecordActivityTaskHeartbeat_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RecordActivityTaskHeartbeat: %v", err) // } // serialize(result) WrapResponse func(*shared.RecordActivityTaskHeartbeatResponse, error) (*HistoryService_RecordActivityTaskHeartbeat_Result, error) // UnwrapResponse takes the result struct for RecordActivityTaskHeartbeat // and returns the value or error returned by it. // // The error is non-nil only if RecordActivityTaskHeartbeat threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_RecordActivityTaskHeartbeat_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RecordActivityTaskHeartbeat_Result) (*shared.RecordActivityTaskHeartbeatResponse, error) }{} func init() { HistoryService_RecordActivityTaskHeartbeat_Helper.Args = func( heartbeatRequest *RecordActivityTaskHeartbeatRequest, ) *HistoryService_RecordActivityTaskHeartbeat_Args { return &HistoryService_RecordActivityTaskHeartbeat_Args{ HeartbeatRequest: heartbeatRequest, } } HistoryService_RecordActivityTaskHeartbeat_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RecordActivityTaskHeartbeat_Helper.WrapResponse = func(success *shared.RecordActivityTaskHeartbeatResponse, err error) (*HistoryService_RecordActivityTaskHeartbeat_Result, error) { if err == nil { return &HistoryService_RecordActivityTaskHeartbeat_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskHeartbeat_Result.BadRequestError") } return &HistoryService_RecordActivityTaskHeartbeat_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskHeartbeat_Result.InternalServiceError") } return &HistoryService_RecordActivityTaskHeartbeat_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskHeartbeat_Result.EntityNotExistError") } return &HistoryService_RecordActivityTaskHeartbeat_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskHeartbeat_Result.ShardOwnershipLostError") } return &HistoryService_RecordActivityTaskHeartbeat_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskHeartbeat_Result.DomainNotActiveError") } return &HistoryService_RecordActivityTaskHeartbeat_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskHeartbeat_Result.LimitExceededError") } return &HistoryService_RecordActivityTaskHeartbeat_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskHeartbeat_Result.ServiceBusyError") } return &HistoryService_RecordActivityTaskHeartbeat_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskHeartbeat_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RecordActivityTaskHeartbeat_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RecordActivityTaskHeartbeat_Helper.UnwrapResponse = func(result *HistoryService_RecordActivityTaskHeartbeat_Result) (success *shared.RecordActivityTaskHeartbeatResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_RecordActivityTaskHeartbeat_Result represents the result of a HistoryService.RecordActivityTaskHeartbeat function call. // // The result of a RecordActivityTaskHeartbeat execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_RecordActivityTaskHeartbeat_Result struct { // Value returned by RecordActivityTaskHeartbeat after a successful execution. Success *shared.RecordActivityTaskHeartbeatResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RecordActivityTaskHeartbeat_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RecordActivityTaskHeartbeat_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_RecordActivityTaskHeartbeat_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordActivityTaskHeartbeatResponse_Read(w wire.Value) (*shared.RecordActivityTaskHeartbeatResponse, error) { var v shared.RecordActivityTaskHeartbeatResponse err := v.FromWire(w) return &v, err } func _WorkflowExecutionAlreadyCompletedError_Read(w wire.Value) (*shared.WorkflowExecutionAlreadyCompletedError, error) { var v shared.WorkflowExecutionAlreadyCompletedError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RecordActivityTaskHeartbeat_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RecordActivityTaskHeartbeat_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RecordActivityTaskHeartbeat_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RecordActivityTaskHeartbeat_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RecordActivityTaskHeartbeatResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RecordActivityTaskHeartbeat_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RecordActivityTaskHeartbeat_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RecordActivityTaskHeartbeat_Result struct could not be encoded. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RecordActivityTaskHeartbeat_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _RecordActivityTaskHeartbeatResponse_Decode(sr stream.Reader) (*shared.RecordActivityTaskHeartbeatResponse, error) { var v shared.RecordActivityTaskHeartbeatResponse err := v.Decode(sr) return &v, err } func _WorkflowExecutionAlreadyCompletedError_Decode(sr stream.Reader) (*shared.WorkflowExecutionAlreadyCompletedError, error) { var v shared.WorkflowExecutionAlreadyCompletedError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RecordActivityTaskHeartbeat_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RecordActivityTaskHeartbeat_Result struct could not be generated from the wire // representation. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RecordActivityTaskHeartbeatResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RecordActivityTaskHeartbeat_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RecordActivityTaskHeartbeat_Result // struct. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RecordActivityTaskHeartbeat_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RecordActivityTaskHeartbeat_Result match the // provided HistoryService_RecordActivityTaskHeartbeat_Result. // // This function performs a deep comparison. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) Equals(rhs *HistoryService_RecordActivityTaskHeartbeat_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RecordActivityTaskHeartbeat_Result. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) GetSuccess() (o *shared.RecordActivityTaskHeartbeatResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RecordActivityTaskHeartbeat" for this struct. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) MethodName() string { return "RecordActivityTaskHeartbeat" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RecordActivityTaskHeartbeat_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RecordActivityTaskStarted_Args represents the arguments for the HistoryService.RecordActivityTaskStarted function. // // The arguments for RecordActivityTaskStarted are sent and received over the wire as this struct. type HistoryService_RecordActivityTaskStarted_Args struct { AddRequest *RecordActivityTaskStartedRequest `json:"addRequest,omitempty"` } // ToWire translates a HistoryService_RecordActivityTaskStarted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RecordActivityTaskStarted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.AddRequest != nil { w, err = v.AddRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordActivityTaskStartedRequest_Read(w wire.Value) (*RecordActivityTaskStartedRequest, error) { var v RecordActivityTaskStartedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RecordActivityTaskStarted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RecordActivityTaskStarted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RecordActivityTaskStarted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RecordActivityTaskStarted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.AddRequest, err = _RecordActivityTaskStartedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RecordActivityTaskStarted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RecordActivityTaskStarted_Args struct could not be encoded. func (v *HistoryService_RecordActivityTaskStarted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.AddRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.AddRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RecordActivityTaskStartedRequest_Decode(sr stream.Reader) (*RecordActivityTaskStartedRequest, error) { var v RecordActivityTaskStartedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RecordActivityTaskStarted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RecordActivityTaskStarted_Args struct could not be generated from the wire // representation. func (v *HistoryService_RecordActivityTaskStarted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.AddRequest, err = _RecordActivityTaskStartedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RecordActivityTaskStarted_Args // struct. func (v *HistoryService_RecordActivityTaskStarted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.AddRequest != nil { fields[i] = fmt.Sprintf("AddRequest: %v", v.AddRequest) i++ } return fmt.Sprintf("HistoryService_RecordActivityTaskStarted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RecordActivityTaskStarted_Args match the // provided HistoryService_RecordActivityTaskStarted_Args. // // This function performs a deep comparison. func (v *HistoryService_RecordActivityTaskStarted_Args) Equals(rhs *HistoryService_RecordActivityTaskStarted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.AddRequest == nil && rhs.AddRequest == nil) || (v.AddRequest != nil && rhs.AddRequest != nil && v.AddRequest.Equals(rhs.AddRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RecordActivityTaskStarted_Args. func (v *HistoryService_RecordActivityTaskStarted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.AddRequest != nil { err = multierr.Append(err, enc.AddObject("addRequest", v.AddRequest)) } return err } // GetAddRequest returns the value of AddRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Args) GetAddRequest() (o *RecordActivityTaskStartedRequest) { if v != nil && v.AddRequest != nil { return v.AddRequest } return } // IsSetAddRequest returns true if AddRequest is not nil. func (v *HistoryService_RecordActivityTaskStarted_Args) IsSetAddRequest() bool { return v != nil && v.AddRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RecordActivityTaskStarted" for this struct. func (v *HistoryService_RecordActivityTaskStarted_Args) MethodName() string { return "RecordActivityTaskStarted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RecordActivityTaskStarted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RecordActivityTaskStarted_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RecordActivityTaskStarted // function. var HistoryService_RecordActivityTaskStarted_Helper = struct { // Args accepts the parameters of RecordActivityTaskStarted in-order and returns // the arguments struct for the function. Args func( addRequest *RecordActivityTaskStartedRequest, ) *HistoryService_RecordActivityTaskStarted_Args // IsException returns true if the given error can be thrown // by RecordActivityTaskStarted. // // An error can be thrown by RecordActivityTaskStarted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RecordActivityTaskStarted // given its return value and error. // // This allows mapping values and errors returned by // RecordActivityTaskStarted into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RecordActivityTaskStarted // // value, err := RecordActivityTaskStarted(args) // result, err := HistoryService_RecordActivityTaskStarted_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RecordActivityTaskStarted: %v", err) // } // serialize(result) WrapResponse func(*RecordActivityTaskStartedResponse, error) (*HistoryService_RecordActivityTaskStarted_Result, error) // UnwrapResponse takes the result struct for RecordActivityTaskStarted // and returns the value or error returned by it. // // The error is non-nil only if RecordActivityTaskStarted threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_RecordActivityTaskStarted_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RecordActivityTaskStarted_Result) (*RecordActivityTaskStartedResponse, error) }{} func init() { HistoryService_RecordActivityTaskStarted_Helper.Args = func( addRequest *RecordActivityTaskStartedRequest, ) *HistoryService_RecordActivityTaskStarted_Args { return &HistoryService_RecordActivityTaskStarted_Args{ AddRequest: addRequest, } } HistoryService_RecordActivityTaskStarted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *EventAlreadyStartedError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RecordActivityTaskStarted_Helper.WrapResponse = func(success *RecordActivityTaskStartedResponse, err error) (*HistoryService_RecordActivityTaskStarted_Result, error) { if err == nil { return &HistoryService_RecordActivityTaskStarted_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskStarted_Result.BadRequestError") } return &HistoryService_RecordActivityTaskStarted_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskStarted_Result.InternalServiceError") } return &HistoryService_RecordActivityTaskStarted_Result{InternalServiceError: e}, nil case *EventAlreadyStartedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskStarted_Result.EventAlreadyStartedError") } return &HistoryService_RecordActivityTaskStarted_Result{EventAlreadyStartedError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskStarted_Result.EntityNotExistError") } return &HistoryService_RecordActivityTaskStarted_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskStarted_Result.ShardOwnershipLostError") } return &HistoryService_RecordActivityTaskStarted_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskStarted_Result.DomainNotActiveError") } return &HistoryService_RecordActivityTaskStarted_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskStarted_Result.LimitExceededError") } return &HistoryService_RecordActivityTaskStarted_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskStarted_Result.ServiceBusyError") } return &HistoryService_RecordActivityTaskStarted_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordActivityTaskStarted_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RecordActivityTaskStarted_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RecordActivityTaskStarted_Helper.UnwrapResponse = func(result *HistoryService_RecordActivityTaskStarted_Result) (success *RecordActivityTaskStartedResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EventAlreadyStartedError != nil { err = result.EventAlreadyStartedError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_RecordActivityTaskStarted_Result represents the result of a HistoryService.RecordActivityTaskStarted function call. // // The result of a RecordActivityTaskStarted execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_RecordActivityTaskStarted_Result struct { // Value returned by RecordActivityTaskStarted after a successful execution. Success *RecordActivityTaskStartedResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EventAlreadyStartedError *EventAlreadyStartedError `json:"eventAlreadyStartedError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RecordActivityTaskStarted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RecordActivityTaskStarted_Result) ToWire() (wire.Value, error) { var ( fields [10]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EventAlreadyStartedError != nil { w, err = v.EventAlreadyStartedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_RecordActivityTaskStarted_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordActivityTaskStartedResponse_Read(w wire.Value) (*RecordActivityTaskStartedResponse, error) { var v RecordActivityTaskStartedResponse err := v.FromWire(w) return &v, err } func _EventAlreadyStartedError_Read(w wire.Value) (*EventAlreadyStartedError, error) { var v EventAlreadyStartedError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RecordActivityTaskStarted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RecordActivityTaskStarted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RecordActivityTaskStarted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RecordActivityTaskStarted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RecordActivityTaskStartedResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EventAlreadyStartedError, err = _EventAlreadyStartedError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EventAlreadyStartedError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RecordActivityTaskStarted_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RecordActivityTaskStarted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RecordActivityTaskStarted_Result struct could not be encoded. func (v *HistoryService_RecordActivityTaskStarted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EventAlreadyStartedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EventAlreadyStartedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EventAlreadyStartedError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RecordActivityTaskStarted_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _RecordActivityTaskStartedResponse_Decode(sr stream.Reader) (*RecordActivityTaskStartedResponse, error) { var v RecordActivityTaskStartedResponse err := v.Decode(sr) return &v, err } func _EventAlreadyStartedError_Decode(sr stream.Reader) (*EventAlreadyStartedError, error) { var v EventAlreadyStartedError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RecordActivityTaskStarted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RecordActivityTaskStarted_Result struct could not be generated from the wire // representation. func (v *HistoryService_RecordActivityTaskStarted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RecordActivityTaskStartedResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EventAlreadyStartedError, err = _EventAlreadyStartedError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EventAlreadyStartedError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RecordActivityTaskStarted_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RecordActivityTaskStarted_Result // struct. func (v *HistoryService_RecordActivityTaskStarted_Result) String() string { if v == nil { return "" } var fields [10]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EventAlreadyStartedError != nil { fields[i] = fmt.Sprintf("EventAlreadyStartedError: %v", v.EventAlreadyStartedError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RecordActivityTaskStarted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RecordActivityTaskStarted_Result match the // provided HistoryService_RecordActivityTaskStarted_Result. // // This function performs a deep comparison. func (v *HistoryService_RecordActivityTaskStarted_Result) Equals(rhs *HistoryService_RecordActivityTaskStarted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EventAlreadyStartedError == nil && rhs.EventAlreadyStartedError == nil) || (v.EventAlreadyStartedError != nil && rhs.EventAlreadyStartedError != nil && v.EventAlreadyStartedError.Equals(rhs.EventAlreadyStartedError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RecordActivityTaskStarted_Result. func (v *HistoryService_RecordActivityTaskStarted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EventAlreadyStartedError != nil { err = multierr.Append(err, enc.AddObject("eventAlreadyStartedError", v.EventAlreadyStartedError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Result) GetSuccess() (o *RecordActivityTaskStartedResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_RecordActivityTaskStarted_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RecordActivityTaskStarted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RecordActivityTaskStarted_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEventAlreadyStartedError returns the value of EventAlreadyStartedError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Result) GetEventAlreadyStartedError() (o *EventAlreadyStartedError) { if v != nil && v.EventAlreadyStartedError != nil { return v.EventAlreadyStartedError } return } // IsSetEventAlreadyStartedError returns true if EventAlreadyStartedError is not nil. func (v *HistoryService_RecordActivityTaskStarted_Result) IsSetEventAlreadyStartedError() bool { return v != nil && v.EventAlreadyStartedError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RecordActivityTaskStarted_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RecordActivityTaskStarted_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RecordActivityTaskStarted_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RecordActivityTaskStarted_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RecordActivityTaskStarted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordActivityTaskStarted_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RecordActivityTaskStarted_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RecordActivityTaskStarted" for this struct. func (v *HistoryService_RecordActivityTaskStarted_Result) MethodName() string { return "RecordActivityTaskStarted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RecordActivityTaskStarted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RecordChildExecutionCompleted_Args represents the arguments for the HistoryService.RecordChildExecutionCompleted function. // // The arguments for RecordChildExecutionCompleted are sent and received over the wire as this struct. type HistoryService_RecordChildExecutionCompleted_Args struct { CompletionRequest *RecordChildExecutionCompletedRequest `json:"completionRequest,omitempty"` } // ToWire translates a HistoryService_RecordChildExecutionCompleted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RecordChildExecutionCompleted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CompletionRequest != nil { w, err = v.CompletionRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordChildExecutionCompletedRequest_Read(w wire.Value) (*RecordChildExecutionCompletedRequest, error) { var v RecordChildExecutionCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RecordChildExecutionCompleted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RecordChildExecutionCompleted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RecordChildExecutionCompleted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RecordChildExecutionCompleted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CompletionRequest, err = _RecordChildExecutionCompletedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RecordChildExecutionCompleted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RecordChildExecutionCompleted_Args struct could not be encoded. func (v *HistoryService_RecordChildExecutionCompleted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CompletionRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CompletionRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RecordChildExecutionCompletedRequest_Decode(sr stream.Reader) (*RecordChildExecutionCompletedRequest, error) { var v RecordChildExecutionCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RecordChildExecutionCompleted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RecordChildExecutionCompleted_Args struct could not be generated from the wire // representation. func (v *HistoryService_RecordChildExecutionCompleted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CompletionRequest, err = _RecordChildExecutionCompletedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RecordChildExecutionCompleted_Args // struct. func (v *HistoryService_RecordChildExecutionCompleted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CompletionRequest != nil { fields[i] = fmt.Sprintf("CompletionRequest: %v", v.CompletionRequest) i++ } return fmt.Sprintf("HistoryService_RecordChildExecutionCompleted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RecordChildExecutionCompleted_Args match the // provided HistoryService_RecordChildExecutionCompleted_Args. // // This function performs a deep comparison. func (v *HistoryService_RecordChildExecutionCompleted_Args) Equals(rhs *HistoryService_RecordChildExecutionCompleted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CompletionRequest == nil && rhs.CompletionRequest == nil) || (v.CompletionRequest != nil && rhs.CompletionRequest != nil && v.CompletionRequest.Equals(rhs.CompletionRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RecordChildExecutionCompleted_Args. func (v *HistoryService_RecordChildExecutionCompleted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CompletionRequest != nil { err = multierr.Append(err, enc.AddObject("completionRequest", v.CompletionRequest)) } return err } // GetCompletionRequest returns the value of CompletionRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RecordChildExecutionCompleted_Args) GetCompletionRequest() (o *RecordChildExecutionCompletedRequest) { if v != nil && v.CompletionRequest != nil { return v.CompletionRequest } return } // IsSetCompletionRequest returns true if CompletionRequest is not nil. func (v *HistoryService_RecordChildExecutionCompleted_Args) IsSetCompletionRequest() bool { return v != nil && v.CompletionRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RecordChildExecutionCompleted" for this struct. func (v *HistoryService_RecordChildExecutionCompleted_Args) MethodName() string { return "RecordChildExecutionCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RecordChildExecutionCompleted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RecordChildExecutionCompleted_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RecordChildExecutionCompleted // function. var HistoryService_RecordChildExecutionCompleted_Helper = struct { // Args accepts the parameters of RecordChildExecutionCompleted in-order and returns // the arguments struct for the function. Args func( completionRequest *RecordChildExecutionCompletedRequest, ) *HistoryService_RecordChildExecutionCompleted_Args // IsException returns true if the given error can be thrown // by RecordChildExecutionCompleted. // // An error can be thrown by RecordChildExecutionCompleted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RecordChildExecutionCompleted // given the error returned by it. The provided error may // be nil if RecordChildExecutionCompleted did not fail. // // This allows mapping errors returned by RecordChildExecutionCompleted into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RecordChildExecutionCompleted // // err := RecordChildExecutionCompleted(args) // result, err := HistoryService_RecordChildExecutionCompleted_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RecordChildExecutionCompleted: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_RecordChildExecutionCompleted_Result, error) // UnwrapResponse takes the result struct for RecordChildExecutionCompleted // and returns the erorr returned by it (if any). // // The error is non-nil only if RecordChildExecutionCompleted threw an // exception. // // result := deserialize(bytes) // err := HistoryService_RecordChildExecutionCompleted_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RecordChildExecutionCompleted_Result) error }{} func init() { HistoryService_RecordChildExecutionCompleted_Helper.Args = func( completionRequest *RecordChildExecutionCompletedRequest, ) *HistoryService_RecordChildExecutionCompleted_Args { return &HistoryService_RecordChildExecutionCompleted_Args{ CompletionRequest: completionRequest, } } HistoryService_RecordChildExecutionCompleted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RecordChildExecutionCompleted_Helper.WrapResponse = func(err error) (*HistoryService_RecordChildExecutionCompleted_Result, error) { if err == nil { return &HistoryService_RecordChildExecutionCompleted_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordChildExecutionCompleted_Result.BadRequestError") } return &HistoryService_RecordChildExecutionCompleted_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordChildExecutionCompleted_Result.InternalServiceError") } return &HistoryService_RecordChildExecutionCompleted_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordChildExecutionCompleted_Result.EntityNotExistError") } return &HistoryService_RecordChildExecutionCompleted_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordChildExecutionCompleted_Result.ShardOwnershipLostError") } return &HistoryService_RecordChildExecutionCompleted_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordChildExecutionCompleted_Result.DomainNotActiveError") } return &HistoryService_RecordChildExecutionCompleted_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordChildExecutionCompleted_Result.LimitExceededError") } return &HistoryService_RecordChildExecutionCompleted_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordChildExecutionCompleted_Result.ServiceBusyError") } return &HistoryService_RecordChildExecutionCompleted_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordChildExecutionCompleted_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RecordChildExecutionCompleted_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RecordChildExecutionCompleted_Helper.UnwrapResponse = func(result *HistoryService_RecordChildExecutionCompleted_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } return } } // HistoryService_RecordChildExecutionCompleted_Result represents the result of a HistoryService.RecordChildExecutionCompleted function call. // // The result of a RecordChildExecutionCompleted execution is sent and received over the wire as this struct. type HistoryService_RecordChildExecutionCompleted_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RecordChildExecutionCompleted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RecordChildExecutionCompleted_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_RecordChildExecutionCompleted_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_RecordChildExecutionCompleted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RecordChildExecutionCompleted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RecordChildExecutionCompleted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RecordChildExecutionCompleted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RecordChildExecutionCompleted_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RecordChildExecutionCompleted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RecordChildExecutionCompleted_Result struct could not be encoded. func (v *HistoryService_RecordChildExecutionCompleted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RecordChildExecutionCompleted_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_RecordChildExecutionCompleted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RecordChildExecutionCompleted_Result struct could not be generated from the wire // representation. func (v *HistoryService_RecordChildExecutionCompleted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RecordChildExecutionCompleted_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RecordChildExecutionCompleted_Result // struct. func (v *HistoryService_RecordChildExecutionCompleted_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RecordChildExecutionCompleted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RecordChildExecutionCompleted_Result match the // provided HistoryService_RecordChildExecutionCompleted_Result. // // This function performs a deep comparison. func (v *HistoryService_RecordChildExecutionCompleted_Result) Equals(rhs *HistoryService_RecordChildExecutionCompleted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RecordChildExecutionCompleted_Result. func (v *HistoryService_RecordChildExecutionCompleted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordChildExecutionCompleted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RecordChildExecutionCompleted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordChildExecutionCompleted_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RecordChildExecutionCompleted_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordChildExecutionCompleted_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RecordChildExecutionCompleted_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordChildExecutionCompleted_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RecordChildExecutionCompleted_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordChildExecutionCompleted_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RecordChildExecutionCompleted_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordChildExecutionCompleted_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RecordChildExecutionCompleted_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordChildExecutionCompleted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RecordChildExecutionCompleted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordChildExecutionCompleted_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RecordChildExecutionCompleted_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RecordChildExecutionCompleted" for this struct. func (v *HistoryService_RecordChildExecutionCompleted_Result) MethodName() string { return "RecordChildExecutionCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RecordChildExecutionCompleted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RecordDecisionTaskStarted_Args represents the arguments for the HistoryService.RecordDecisionTaskStarted function. // // The arguments for RecordDecisionTaskStarted are sent and received over the wire as this struct. type HistoryService_RecordDecisionTaskStarted_Args struct { AddRequest *RecordDecisionTaskStartedRequest `json:"addRequest,omitempty"` } // ToWire translates a HistoryService_RecordDecisionTaskStarted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RecordDecisionTaskStarted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.AddRequest != nil { w, err = v.AddRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RecordDecisionTaskStartedRequest_Read(w wire.Value) (*RecordDecisionTaskStartedRequest, error) { var v RecordDecisionTaskStartedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RecordDecisionTaskStarted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RecordDecisionTaskStarted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RecordDecisionTaskStarted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RecordDecisionTaskStarted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.AddRequest, err = _RecordDecisionTaskStartedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RecordDecisionTaskStarted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RecordDecisionTaskStarted_Args struct could not be encoded. func (v *HistoryService_RecordDecisionTaskStarted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.AddRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.AddRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RecordDecisionTaskStartedRequest_Decode(sr stream.Reader) (*RecordDecisionTaskStartedRequest, error) { var v RecordDecisionTaskStartedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RecordDecisionTaskStarted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RecordDecisionTaskStarted_Args struct could not be generated from the wire // representation. func (v *HistoryService_RecordDecisionTaskStarted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.AddRequest, err = _RecordDecisionTaskStartedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RecordDecisionTaskStarted_Args // struct. func (v *HistoryService_RecordDecisionTaskStarted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.AddRequest != nil { fields[i] = fmt.Sprintf("AddRequest: %v", v.AddRequest) i++ } return fmt.Sprintf("HistoryService_RecordDecisionTaskStarted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RecordDecisionTaskStarted_Args match the // provided HistoryService_RecordDecisionTaskStarted_Args. // // This function performs a deep comparison. func (v *HistoryService_RecordDecisionTaskStarted_Args) Equals(rhs *HistoryService_RecordDecisionTaskStarted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.AddRequest == nil && rhs.AddRequest == nil) || (v.AddRequest != nil && rhs.AddRequest != nil && v.AddRequest.Equals(rhs.AddRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RecordDecisionTaskStarted_Args. func (v *HistoryService_RecordDecisionTaskStarted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.AddRequest != nil { err = multierr.Append(err, enc.AddObject("addRequest", v.AddRequest)) } return err } // GetAddRequest returns the value of AddRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Args) GetAddRequest() (o *RecordDecisionTaskStartedRequest) { if v != nil && v.AddRequest != nil { return v.AddRequest } return } // IsSetAddRequest returns true if AddRequest is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Args) IsSetAddRequest() bool { return v != nil && v.AddRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RecordDecisionTaskStarted" for this struct. func (v *HistoryService_RecordDecisionTaskStarted_Args) MethodName() string { return "RecordDecisionTaskStarted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RecordDecisionTaskStarted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RecordDecisionTaskStarted_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RecordDecisionTaskStarted // function. var HistoryService_RecordDecisionTaskStarted_Helper = struct { // Args accepts the parameters of RecordDecisionTaskStarted in-order and returns // the arguments struct for the function. Args func( addRequest *RecordDecisionTaskStartedRequest, ) *HistoryService_RecordDecisionTaskStarted_Args // IsException returns true if the given error can be thrown // by RecordDecisionTaskStarted. // // An error can be thrown by RecordDecisionTaskStarted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RecordDecisionTaskStarted // given its return value and error. // // This allows mapping values and errors returned by // RecordDecisionTaskStarted into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RecordDecisionTaskStarted // // value, err := RecordDecisionTaskStarted(args) // result, err := HistoryService_RecordDecisionTaskStarted_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RecordDecisionTaskStarted: %v", err) // } // serialize(result) WrapResponse func(*RecordDecisionTaskStartedResponse, error) (*HistoryService_RecordDecisionTaskStarted_Result, error) // UnwrapResponse takes the result struct for RecordDecisionTaskStarted // and returns the value or error returned by it. // // The error is non-nil only if RecordDecisionTaskStarted threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_RecordDecisionTaskStarted_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RecordDecisionTaskStarted_Result) (*RecordDecisionTaskStartedResponse, error) }{} func init() { HistoryService_RecordDecisionTaskStarted_Helper.Args = func( addRequest *RecordDecisionTaskStartedRequest, ) *HistoryService_RecordDecisionTaskStarted_Args { return &HistoryService_RecordDecisionTaskStarted_Args{ AddRequest: addRequest, } } HistoryService_RecordDecisionTaskStarted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *EventAlreadyStartedError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RecordDecisionTaskStarted_Helper.WrapResponse = func(success *RecordDecisionTaskStartedResponse, err error) (*HistoryService_RecordDecisionTaskStarted_Result, error) { if err == nil { return &HistoryService_RecordDecisionTaskStarted_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordDecisionTaskStarted_Result.BadRequestError") } return &HistoryService_RecordDecisionTaskStarted_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordDecisionTaskStarted_Result.InternalServiceError") } return &HistoryService_RecordDecisionTaskStarted_Result{InternalServiceError: e}, nil case *EventAlreadyStartedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordDecisionTaskStarted_Result.EventAlreadyStartedError") } return &HistoryService_RecordDecisionTaskStarted_Result{EventAlreadyStartedError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordDecisionTaskStarted_Result.EntityNotExistError") } return &HistoryService_RecordDecisionTaskStarted_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordDecisionTaskStarted_Result.ShardOwnershipLostError") } return &HistoryService_RecordDecisionTaskStarted_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordDecisionTaskStarted_Result.DomainNotActiveError") } return &HistoryService_RecordDecisionTaskStarted_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordDecisionTaskStarted_Result.LimitExceededError") } return &HistoryService_RecordDecisionTaskStarted_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordDecisionTaskStarted_Result.ServiceBusyError") } return &HistoryService_RecordDecisionTaskStarted_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RecordDecisionTaskStarted_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RecordDecisionTaskStarted_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RecordDecisionTaskStarted_Helper.UnwrapResponse = func(result *HistoryService_RecordDecisionTaskStarted_Result) (success *RecordDecisionTaskStartedResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EventAlreadyStartedError != nil { err = result.EventAlreadyStartedError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_RecordDecisionTaskStarted_Result represents the result of a HistoryService.RecordDecisionTaskStarted function call. // // The result of a RecordDecisionTaskStarted execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_RecordDecisionTaskStarted_Result struct { // Value returned by RecordDecisionTaskStarted after a successful execution. Success *RecordDecisionTaskStartedResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EventAlreadyStartedError *EventAlreadyStartedError `json:"eventAlreadyStartedError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RecordDecisionTaskStarted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RecordDecisionTaskStarted_Result) ToWire() (wire.Value, error) { var ( fields [10]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EventAlreadyStartedError != nil { w, err = v.EventAlreadyStartedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 9, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_RecordDecisionTaskStarted_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_RecordDecisionTaskStarted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RecordDecisionTaskStarted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RecordDecisionTaskStarted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RecordDecisionTaskStarted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RecordDecisionTaskStartedResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EventAlreadyStartedError, err = _EventAlreadyStartedError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 9: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EventAlreadyStartedError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RecordDecisionTaskStarted_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RecordDecisionTaskStarted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RecordDecisionTaskStarted_Result struct could not be encoded. func (v *HistoryService_RecordDecisionTaskStarted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EventAlreadyStartedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EventAlreadyStartedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 9, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EventAlreadyStartedError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RecordDecisionTaskStarted_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_RecordDecisionTaskStarted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RecordDecisionTaskStarted_Result struct could not be generated from the wire // representation. func (v *HistoryService_RecordDecisionTaskStarted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RecordDecisionTaskStartedResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EventAlreadyStartedError, err = _EventAlreadyStartedError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 9 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EventAlreadyStartedError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RecordDecisionTaskStarted_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RecordDecisionTaskStarted_Result // struct. func (v *HistoryService_RecordDecisionTaskStarted_Result) String() string { if v == nil { return "" } var fields [10]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EventAlreadyStartedError != nil { fields[i] = fmt.Sprintf("EventAlreadyStartedError: %v", v.EventAlreadyStartedError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RecordDecisionTaskStarted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RecordDecisionTaskStarted_Result match the // provided HistoryService_RecordDecisionTaskStarted_Result. // // This function performs a deep comparison. func (v *HistoryService_RecordDecisionTaskStarted_Result) Equals(rhs *HistoryService_RecordDecisionTaskStarted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EventAlreadyStartedError == nil && rhs.EventAlreadyStartedError == nil) || (v.EventAlreadyStartedError != nil && rhs.EventAlreadyStartedError != nil && v.EventAlreadyStartedError.Equals(rhs.EventAlreadyStartedError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RecordDecisionTaskStarted_Result. func (v *HistoryService_RecordDecisionTaskStarted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EventAlreadyStartedError != nil { err = multierr.Append(err, enc.AddObject("eventAlreadyStartedError", v.EventAlreadyStartedError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Result) GetSuccess() (o *RecordDecisionTaskStartedResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEventAlreadyStartedError returns the value of EventAlreadyStartedError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Result) GetEventAlreadyStartedError() (o *EventAlreadyStartedError) { if v != nil && v.EventAlreadyStartedError != nil { return v.EventAlreadyStartedError } return } // IsSetEventAlreadyStartedError returns true if EventAlreadyStartedError is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Result) IsSetEventAlreadyStartedError() bool { return v != nil && v.EventAlreadyStartedError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RecordDecisionTaskStarted_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RecordDecisionTaskStarted_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RecordDecisionTaskStarted" for this struct. func (v *HistoryService_RecordDecisionTaskStarted_Result) MethodName() string { return "RecordDecisionTaskStarted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RecordDecisionTaskStarted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RefreshWorkflowTasks_Args represents the arguments for the HistoryService.RefreshWorkflowTasks function. // // The arguments for RefreshWorkflowTasks are sent and received over the wire as this struct. type HistoryService_RefreshWorkflowTasks_Args struct { Request *RefreshWorkflowTasksRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_RefreshWorkflowTasks_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RefreshWorkflowTasks_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RefreshWorkflowTasksRequest_1_Read(w wire.Value) (*RefreshWorkflowTasksRequest, error) { var v RefreshWorkflowTasksRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RefreshWorkflowTasks_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RefreshWorkflowTasks_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RefreshWorkflowTasks_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RefreshWorkflowTasks_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _RefreshWorkflowTasksRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RefreshWorkflowTasks_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RefreshWorkflowTasks_Args struct could not be encoded. func (v *HistoryService_RefreshWorkflowTasks_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RefreshWorkflowTasksRequest_1_Decode(sr stream.Reader) (*RefreshWorkflowTasksRequest, error) { var v RefreshWorkflowTasksRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RefreshWorkflowTasks_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RefreshWorkflowTasks_Args struct could not be generated from the wire // representation. func (v *HistoryService_RefreshWorkflowTasks_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _RefreshWorkflowTasksRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RefreshWorkflowTasks_Args // struct. func (v *HistoryService_RefreshWorkflowTasks_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_RefreshWorkflowTasks_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RefreshWorkflowTasks_Args match the // provided HistoryService_RefreshWorkflowTasks_Args. // // This function performs a deep comparison. func (v *HistoryService_RefreshWorkflowTasks_Args) Equals(rhs *HistoryService_RefreshWorkflowTasks_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RefreshWorkflowTasks_Args. func (v *HistoryService_RefreshWorkflowTasks_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_RefreshWorkflowTasks_Args) GetRequest() (o *RefreshWorkflowTasksRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_RefreshWorkflowTasks_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RefreshWorkflowTasks" for this struct. func (v *HistoryService_RefreshWorkflowTasks_Args) MethodName() string { return "RefreshWorkflowTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RefreshWorkflowTasks_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RefreshWorkflowTasks_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RefreshWorkflowTasks // function. var HistoryService_RefreshWorkflowTasks_Helper = struct { // Args accepts the parameters of RefreshWorkflowTasks in-order and returns // the arguments struct for the function. Args func( request *RefreshWorkflowTasksRequest, ) *HistoryService_RefreshWorkflowTasks_Args // IsException returns true if the given error can be thrown // by RefreshWorkflowTasks. // // An error can be thrown by RefreshWorkflowTasks only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RefreshWorkflowTasks // given the error returned by it. The provided error may // be nil if RefreshWorkflowTasks did not fail. // // This allows mapping errors returned by RefreshWorkflowTasks into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RefreshWorkflowTasks // // err := RefreshWorkflowTasks(args) // result, err := HistoryService_RefreshWorkflowTasks_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RefreshWorkflowTasks: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_RefreshWorkflowTasks_Result, error) // UnwrapResponse takes the result struct for RefreshWorkflowTasks // and returns the erorr returned by it (if any). // // The error is non-nil only if RefreshWorkflowTasks threw an // exception. // // result := deserialize(bytes) // err := HistoryService_RefreshWorkflowTasks_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RefreshWorkflowTasks_Result) error }{} func init() { HistoryService_RefreshWorkflowTasks_Helper.Args = func( request *RefreshWorkflowTasksRequest, ) *HistoryService_RefreshWorkflowTasks_Args { return &HistoryService_RefreshWorkflowTasks_Args{ Request: request, } } HistoryService_RefreshWorkflowTasks_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.DomainNotActiveError: return true case *ShardOwnershipLostError: return true case *shared.ServiceBusyError: return true case *shared.EntityNotExistsError: return true default: return false } } HistoryService_RefreshWorkflowTasks_Helper.WrapResponse = func(err error) (*HistoryService_RefreshWorkflowTasks_Result, error) { if err == nil { return &HistoryService_RefreshWorkflowTasks_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RefreshWorkflowTasks_Result.BadRequestError") } return &HistoryService_RefreshWorkflowTasks_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RefreshWorkflowTasks_Result.InternalServiceError") } return &HistoryService_RefreshWorkflowTasks_Result{InternalServiceError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RefreshWorkflowTasks_Result.DomainNotActiveError") } return &HistoryService_RefreshWorkflowTasks_Result{DomainNotActiveError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RefreshWorkflowTasks_Result.ShardOwnershipLostError") } return &HistoryService_RefreshWorkflowTasks_Result{ShardOwnershipLostError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RefreshWorkflowTasks_Result.ServiceBusyError") } return &HistoryService_RefreshWorkflowTasks_Result{ServiceBusyError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RefreshWorkflowTasks_Result.EntityNotExistError") } return &HistoryService_RefreshWorkflowTasks_Result{EntityNotExistError: e}, nil } return nil, err } HistoryService_RefreshWorkflowTasks_Helper.UnwrapResponse = func(result *HistoryService_RefreshWorkflowTasks_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } return } } // HistoryService_RefreshWorkflowTasks_Result represents the result of a HistoryService.RefreshWorkflowTasks function call. // // The result of a RefreshWorkflowTasks execution is sent and received over the wire as this struct. type HistoryService_RefreshWorkflowTasks_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` } // ToWire translates a HistoryService_RefreshWorkflowTasks_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RefreshWorkflowTasks_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_RefreshWorkflowTasks_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RefreshWorkflowTasks_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RefreshWorkflowTasks_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RefreshWorkflowTasks_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RefreshWorkflowTasks_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RefreshWorkflowTasks_Result struct could not be encoded. func (v *HistoryService_RefreshWorkflowTasks_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_RefreshWorkflowTasks_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RefreshWorkflowTasks_Result struct could not be generated from the wire // representation. func (v *HistoryService_RefreshWorkflowTasks_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.EntityNotExistError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RefreshWorkflowTasks_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RefreshWorkflowTasks_Result // struct. func (v *HistoryService_RefreshWorkflowTasks_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } return fmt.Sprintf("HistoryService_RefreshWorkflowTasks_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RefreshWorkflowTasks_Result match the // provided HistoryService_RefreshWorkflowTasks_Result. // // This function performs a deep comparison. func (v *HistoryService_RefreshWorkflowTasks_Result) Equals(rhs *HistoryService_RefreshWorkflowTasks_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RefreshWorkflowTasks_Result. func (v *HistoryService_RefreshWorkflowTasks_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RefreshWorkflowTasks_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RefreshWorkflowTasks_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RefreshWorkflowTasks_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RefreshWorkflowTasks_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RefreshWorkflowTasks_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RefreshWorkflowTasks_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RefreshWorkflowTasks_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RefreshWorkflowTasks_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RefreshWorkflowTasks_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RefreshWorkflowTasks_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RefreshWorkflowTasks_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RefreshWorkflowTasks_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RefreshWorkflowTasks" for this struct. func (v *HistoryService_RefreshWorkflowTasks_Result) MethodName() string { return "RefreshWorkflowTasks" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RefreshWorkflowTasks_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RemoveSignalMutableState_Args represents the arguments for the HistoryService.RemoveSignalMutableState function. // // The arguments for RemoveSignalMutableState are sent and received over the wire as this struct. type HistoryService_RemoveSignalMutableState_Args struct { RemoveRequest *RemoveSignalMutableStateRequest `json:"removeRequest,omitempty"` } // ToWire translates a HistoryService_RemoveSignalMutableState_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RemoveSignalMutableState_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.RemoveRequest != nil { w, err = v.RemoveRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RemoveSignalMutableStateRequest_Read(w wire.Value) (*RemoveSignalMutableStateRequest, error) { var v RemoveSignalMutableStateRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RemoveSignalMutableState_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RemoveSignalMutableState_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RemoveSignalMutableState_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RemoveSignalMutableState_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.RemoveRequest, err = _RemoveSignalMutableStateRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RemoveSignalMutableState_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RemoveSignalMutableState_Args struct could not be encoded. func (v *HistoryService_RemoveSignalMutableState_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.RemoveRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.RemoveRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RemoveSignalMutableStateRequest_Decode(sr stream.Reader) (*RemoveSignalMutableStateRequest, error) { var v RemoveSignalMutableStateRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RemoveSignalMutableState_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RemoveSignalMutableState_Args struct could not be generated from the wire // representation. func (v *HistoryService_RemoveSignalMutableState_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.RemoveRequest, err = _RemoveSignalMutableStateRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RemoveSignalMutableState_Args // struct. func (v *HistoryService_RemoveSignalMutableState_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.RemoveRequest != nil { fields[i] = fmt.Sprintf("RemoveRequest: %v", v.RemoveRequest) i++ } return fmt.Sprintf("HistoryService_RemoveSignalMutableState_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RemoveSignalMutableState_Args match the // provided HistoryService_RemoveSignalMutableState_Args. // // This function performs a deep comparison. func (v *HistoryService_RemoveSignalMutableState_Args) Equals(rhs *HistoryService_RemoveSignalMutableState_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.RemoveRequest == nil && rhs.RemoveRequest == nil) || (v.RemoveRequest != nil && rhs.RemoveRequest != nil && v.RemoveRequest.Equals(rhs.RemoveRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RemoveSignalMutableState_Args. func (v *HistoryService_RemoveSignalMutableState_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.RemoveRequest != nil { err = multierr.Append(err, enc.AddObject("removeRequest", v.RemoveRequest)) } return err } // GetRemoveRequest returns the value of RemoveRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveSignalMutableState_Args) GetRemoveRequest() (o *RemoveSignalMutableStateRequest) { if v != nil && v.RemoveRequest != nil { return v.RemoveRequest } return } // IsSetRemoveRequest returns true if RemoveRequest is not nil. func (v *HistoryService_RemoveSignalMutableState_Args) IsSetRemoveRequest() bool { return v != nil && v.RemoveRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RemoveSignalMutableState" for this struct. func (v *HistoryService_RemoveSignalMutableState_Args) MethodName() string { return "RemoveSignalMutableState" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RemoveSignalMutableState_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RemoveSignalMutableState_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RemoveSignalMutableState // function. var HistoryService_RemoveSignalMutableState_Helper = struct { // Args accepts the parameters of RemoveSignalMutableState in-order and returns // the arguments struct for the function. Args func( removeRequest *RemoveSignalMutableStateRequest, ) *HistoryService_RemoveSignalMutableState_Args // IsException returns true if the given error can be thrown // by RemoveSignalMutableState. // // An error can be thrown by RemoveSignalMutableState only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RemoveSignalMutableState // given the error returned by it. The provided error may // be nil if RemoveSignalMutableState did not fail. // // This allows mapping errors returned by RemoveSignalMutableState into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RemoveSignalMutableState // // err := RemoveSignalMutableState(args) // result, err := HistoryService_RemoveSignalMutableState_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RemoveSignalMutableState: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_RemoveSignalMutableState_Result, error) // UnwrapResponse takes the result struct for RemoveSignalMutableState // and returns the erorr returned by it (if any). // // The error is non-nil only if RemoveSignalMutableState threw an // exception. // // result := deserialize(bytes) // err := HistoryService_RemoveSignalMutableState_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RemoveSignalMutableState_Result) error }{} func init() { HistoryService_RemoveSignalMutableState_Helper.Args = func( removeRequest *RemoveSignalMutableStateRequest, ) *HistoryService_RemoveSignalMutableState_Args { return &HistoryService_RemoveSignalMutableState_Args{ RemoveRequest: removeRequest, } } HistoryService_RemoveSignalMutableState_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RemoveSignalMutableState_Helper.WrapResponse = func(err error) (*HistoryService_RemoveSignalMutableState_Result, error) { if err == nil { return &HistoryService_RemoveSignalMutableState_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveSignalMutableState_Result.BadRequestError") } return &HistoryService_RemoveSignalMutableState_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveSignalMutableState_Result.InternalServiceError") } return &HistoryService_RemoveSignalMutableState_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveSignalMutableState_Result.EntityNotExistError") } return &HistoryService_RemoveSignalMutableState_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveSignalMutableState_Result.ShardOwnershipLostError") } return &HistoryService_RemoveSignalMutableState_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveSignalMutableState_Result.DomainNotActiveError") } return &HistoryService_RemoveSignalMutableState_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveSignalMutableState_Result.LimitExceededError") } return &HistoryService_RemoveSignalMutableState_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveSignalMutableState_Result.ServiceBusyError") } return &HistoryService_RemoveSignalMutableState_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveSignalMutableState_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RemoveSignalMutableState_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RemoveSignalMutableState_Helper.UnwrapResponse = func(result *HistoryService_RemoveSignalMutableState_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } return } } // HistoryService_RemoveSignalMutableState_Result represents the result of a HistoryService.RemoveSignalMutableState function call. // // The result of a RemoveSignalMutableState execution is sent and received over the wire as this struct. type HistoryService_RemoveSignalMutableState_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RemoveSignalMutableState_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RemoveSignalMutableState_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_RemoveSignalMutableState_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_RemoveSignalMutableState_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RemoveSignalMutableState_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RemoveSignalMutableState_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RemoveSignalMutableState_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RemoveSignalMutableState_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RemoveSignalMutableState_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RemoveSignalMutableState_Result struct could not be encoded. func (v *HistoryService_RemoveSignalMutableState_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RemoveSignalMutableState_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_RemoveSignalMutableState_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RemoveSignalMutableState_Result struct could not be generated from the wire // representation. func (v *HistoryService_RemoveSignalMutableState_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RemoveSignalMutableState_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RemoveSignalMutableState_Result // struct. func (v *HistoryService_RemoveSignalMutableState_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RemoveSignalMutableState_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RemoveSignalMutableState_Result match the // provided HistoryService_RemoveSignalMutableState_Result. // // This function performs a deep comparison. func (v *HistoryService_RemoveSignalMutableState_Result) Equals(rhs *HistoryService_RemoveSignalMutableState_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RemoveSignalMutableState_Result. func (v *HistoryService_RemoveSignalMutableState_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveSignalMutableState_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RemoveSignalMutableState_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveSignalMutableState_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RemoveSignalMutableState_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveSignalMutableState_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RemoveSignalMutableState_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveSignalMutableState_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RemoveSignalMutableState_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveSignalMutableState_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RemoveSignalMutableState_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveSignalMutableState_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RemoveSignalMutableState_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveSignalMutableState_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RemoveSignalMutableState_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveSignalMutableState_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RemoveSignalMutableState_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RemoveSignalMutableState" for this struct. func (v *HistoryService_RemoveSignalMutableState_Result) MethodName() string { return "RemoveSignalMutableState" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RemoveSignalMutableState_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RemoveTask_Args represents the arguments for the HistoryService.RemoveTask function. // // The arguments for RemoveTask are sent and received over the wire as this struct. type HistoryService_RemoveTask_Args struct { Request *shared.RemoveTaskRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_RemoveTask_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RemoveTask_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RemoveTaskRequest_Read(w wire.Value) (*shared.RemoveTaskRequest, error) { var v shared.RemoveTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RemoveTask_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RemoveTask_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RemoveTask_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RemoveTask_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _RemoveTaskRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RemoveTask_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RemoveTask_Args struct could not be encoded. func (v *HistoryService_RemoveTask_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RemoveTaskRequest_Decode(sr stream.Reader) (*shared.RemoveTaskRequest, error) { var v shared.RemoveTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RemoveTask_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RemoveTask_Args struct could not be generated from the wire // representation. func (v *HistoryService_RemoveTask_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _RemoveTaskRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RemoveTask_Args // struct. func (v *HistoryService_RemoveTask_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_RemoveTask_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RemoveTask_Args match the // provided HistoryService_RemoveTask_Args. // // This function performs a deep comparison. func (v *HistoryService_RemoveTask_Args) Equals(rhs *HistoryService_RemoveTask_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RemoveTask_Args. func (v *HistoryService_RemoveTask_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveTask_Args) GetRequest() (o *shared.RemoveTaskRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_RemoveTask_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RemoveTask" for this struct. func (v *HistoryService_RemoveTask_Args) MethodName() string { return "RemoveTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RemoveTask_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RemoveTask_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RemoveTask // function. var HistoryService_RemoveTask_Helper = struct { // Args accepts the parameters of RemoveTask in-order and returns // the arguments struct for the function. Args func( request *shared.RemoveTaskRequest, ) *HistoryService_RemoveTask_Args // IsException returns true if the given error can be thrown // by RemoveTask. // // An error can be thrown by RemoveTask only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RemoveTask // given the error returned by it. The provided error may // be nil if RemoveTask did not fail. // // This allows mapping errors returned by RemoveTask into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RemoveTask // // err := RemoveTask(args) // result, err := HistoryService_RemoveTask_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RemoveTask: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_RemoveTask_Result, error) // UnwrapResponse takes the result struct for RemoveTask // and returns the erorr returned by it (if any). // // The error is non-nil only if RemoveTask threw an // exception. // // result := deserialize(bytes) // err := HistoryService_RemoveTask_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RemoveTask_Result) error }{} func init() { HistoryService_RemoveTask_Helper.Args = func( request *shared.RemoveTaskRequest, ) *HistoryService_RemoveTask_Args { return &HistoryService_RemoveTask_Args{ Request: request, } } HistoryService_RemoveTask_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.AccessDeniedError: return true default: return false } } HistoryService_RemoveTask_Helper.WrapResponse = func(err error) (*HistoryService_RemoveTask_Result, error) { if err == nil { return &HistoryService_RemoveTask_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveTask_Result.BadRequestError") } return &HistoryService_RemoveTask_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveTask_Result.InternalServiceError") } return &HistoryService_RemoveTask_Result{InternalServiceError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RemoveTask_Result.AccessDeniedError") } return &HistoryService_RemoveTask_Result{AccessDeniedError: e}, nil } return nil, err } HistoryService_RemoveTask_Helper.UnwrapResponse = func(result *HistoryService_RemoveTask_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // HistoryService_RemoveTask_Result represents the result of a HistoryService.RemoveTask function call. // // The result of a RemoveTask execution is sent and received over the wire as this struct. type HistoryService_RemoveTask_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a HistoryService_RemoveTask_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RemoveTask_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_RemoveTask_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_RemoveTask_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RemoveTask_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RemoveTask_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RemoveTask_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RemoveTask_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RemoveTask_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RemoveTask_Result struct could not be encoded. func (v *HistoryService_RemoveTask_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RemoveTask_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_RemoveTask_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RemoveTask_Result struct could not be generated from the wire // representation. func (v *HistoryService_RemoveTask_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RemoveTask_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RemoveTask_Result // struct. func (v *HistoryService_RemoveTask_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("HistoryService_RemoveTask_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RemoveTask_Result match the // provided HistoryService_RemoveTask_Result. // // This function performs a deep comparison. func (v *HistoryService_RemoveTask_Result) Equals(rhs *HistoryService_RemoveTask_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RemoveTask_Result. func (v *HistoryService_RemoveTask_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveTask_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RemoveTask_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveTask_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RemoveTask_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *HistoryService_RemoveTask_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *HistoryService_RemoveTask_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RemoveTask" for this struct. func (v *HistoryService_RemoveTask_Result) MethodName() string { return "RemoveTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RemoveTask_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_ReplicateEventsV2_Args represents the arguments for the HistoryService.ReplicateEventsV2 function. // // The arguments for ReplicateEventsV2 are sent and received over the wire as this struct. type HistoryService_ReplicateEventsV2_Args struct { ReplicateV2Request *ReplicateEventsV2Request `json:"replicateV2Request,omitempty"` } // ToWire translates a HistoryService_ReplicateEventsV2_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ReplicateEventsV2_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ReplicateV2Request != nil { w, err = v.ReplicateV2Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReplicateEventsV2Request_Read(w wire.Value) (*ReplicateEventsV2Request, error) { var v ReplicateEventsV2Request err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ReplicateEventsV2_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ReplicateEventsV2_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ReplicateEventsV2_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ReplicateEventsV2_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ReplicateV2Request, err = _ReplicateEventsV2Request_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_ReplicateEventsV2_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ReplicateEventsV2_Args struct could not be encoded. func (v *HistoryService_ReplicateEventsV2_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ReplicateV2Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ReplicateV2Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReplicateEventsV2Request_Decode(sr stream.Reader) (*ReplicateEventsV2Request, error) { var v ReplicateEventsV2Request err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ReplicateEventsV2_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ReplicateEventsV2_Args struct could not be generated from the wire // representation. func (v *HistoryService_ReplicateEventsV2_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ReplicateV2Request, err = _ReplicateEventsV2Request_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_ReplicateEventsV2_Args // struct. func (v *HistoryService_ReplicateEventsV2_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ReplicateV2Request != nil { fields[i] = fmt.Sprintf("ReplicateV2Request: %v", v.ReplicateV2Request) i++ } return fmt.Sprintf("HistoryService_ReplicateEventsV2_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ReplicateEventsV2_Args match the // provided HistoryService_ReplicateEventsV2_Args. // // This function performs a deep comparison. func (v *HistoryService_ReplicateEventsV2_Args) Equals(rhs *HistoryService_ReplicateEventsV2_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ReplicateV2Request == nil && rhs.ReplicateV2Request == nil) || (v.ReplicateV2Request != nil && rhs.ReplicateV2Request != nil && v.ReplicateV2Request.Equals(rhs.ReplicateV2Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ReplicateEventsV2_Args. func (v *HistoryService_ReplicateEventsV2_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ReplicateV2Request != nil { err = multierr.Append(err, enc.AddObject("replicateV2Request", v.ReplicateV2Request)) } return err } // GetReplicateV2Request returns the value of ReplicateV2Request if it is set or its // zero value if it is unset. func (v *HistoryService_ReplicateEventsV2_Args) GetReplicateV2Request() (o *ReplicateEventsV2Request) { if v != nil && v.ReplicateV2Request != nil { return v.ReplicateV2Request } return } // IsSetReplicateV2Request returns true if ReplicateV2Request is not nil. func (v *HistoryService_ReplicateEventsV2_Args) IsSetReplicateV2Request() bool { return v != nil && v.ReplicateV2Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ReplicateEventsV2" for this struct. func (v *HistoryService_ReplicateEventsV2_Args) MethodName() string { return "ReplicateEventsV2" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_ReplicateEventsV2_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_ReplicateEventsV2_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.ReplicateEventsV2 // function. var HistoryService_ReplicateEventsV2_Helper = struct { // Args accepts the parameters of ReplicateEventsV2 in-order and returns // the arguments struct for the function. Args func( replicateV2Request *ReplicateEventsV2Request, ) *HistoryService_ReplicateEventsV2_Args // IsException returns true if the given error can be thrown // by ReplicateEventsV2. // // An error can be thrown by ReplicateEventsV2 only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ReplicateEventsV2 // given the error returned by it. The provided error may // be nil if ReplicateEventsV2 did not fail. // // This allows mapping errors returned by ReplicateEventsV2 into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // ReplicateEventsV2 // // err := ReplicateEventsV2(args) // result, err := HistoryService_ReplicateEventsV2_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from ReplicateEventsV2: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_ReplicateEventsV2_Result, error) // UnwrapResponse takes the result struct for ReplicateEventsV2 // and returns the erorr returned by it (if any). // // The error is non-nil only if ReplicateEventsV2 threw an // exception. // // result := deserialize(bytes) // err := HistoryService_ReplicateEventsV2_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_ReplicateEventsV2_Result) error }{} func init() { HistoryService_ReplicateEventsV2_Helper.Args = func( replicateV2Request *ReplicateEventsV2Request, ) *HistoryService_ReplicateEventsV2_Args { return &HistoryService_ReplicateEventsV2_Args{ ReplicateV2Request: replicateV2Request, } } HistoryService_ReplicateEventsV2_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.LimitExceededError: return true case *shared.RetryTaskV2Error: return true case *shared.ServiceBusyError: return true default: return false } } HistoryService_ReplicateEventsV2_Helper.WrapResponse = func(err error) (*HistoryService_ReplicateEventsV2_Result, error) { if err == nil { return &HistoryService_ReplicateEventsV2_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReplicateEventsV2_Result.BadRequestError") } return &HistoryService_ReplicateEventsV2_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReplicateEventsV2_Result.InternalServiceError") } return &HistoryService_ReplicateEventsV2_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReplicateEventsV2_Result.EntityNotExistError") } return &HistoryService_ReplicateEventsV2_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReplicateEventsV2_Result.ShardOwnershipLostError") } return &HistoryService_ReplicateEventsV2_Result{ShardOwnershipLostError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReplicateEventsV2_Result.LimitExceededError") } return &HistoryService_ReplicateEventsV2_Result{LimitExceededError: e}, nil case *shared.RetryTaskV2Error: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReplicateEventsV2_Result.RetryTaskError") } return &HistoryService_ReplicateEventsV2_Result{RetryTaskError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ReplicateEventsV2_Result.ServiceBusyError") } return &HistoryService_ReplicateEventsV2_Result{ServiceBusyError: e}, nil } return nil, err } HistoryService_ReplicateEventsV2_Helper.UnwrapResponse = func(result *HistoryService_ReplicateEventsV2_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.RetryTaskError != nil { err = result.RetryTaskError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } return } } // HistoryService_ReplicateEventsV2_Result represents the result of a HistoryService.ReplicateEventsV2 function call. // // The result of a ReplicateEventsV2 execution is sent and received over the wire as this struct. type HistoryService_ReplicateEventsV2_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` RetryTaskError *shared.RetryTaskV2Error `json:"retryTaskError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a HistoryService_ReplicateEventsV2_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ReplicateEventsV2_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.RetryTaskError != nil { w, err = v.RetryTaskError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_ReplicateEventsV2_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RetryTaskV2Error_Read(w wire.Value) (*shared.RetryTaskV2Error, error) { var v shared.RetryTaskV2Error err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ReplicateEventsV2_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ReplicateEventsV2_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ReplicateEventsV2_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ReplicateEventsV2_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.RetryTaskError, err = _RetryTaskV2Error_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.RetryTaskError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ReplicateEventsV2_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_ReplicateEventsV2_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ReplicateEventsV2_Result struct could not be encoded. func (v *HistoryService_ReplicateEventsV2_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryTaskError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.RetryTaskError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.RetryTaskError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ReplicateEventsV2_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _RetryTaskV2Error_Decode(sr stream.Reader) (*shared.RetryTaskV2Error, error) { var v shared.RetryTaskV2Error err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ReplicateEventsV2_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ReplicateEventsV2_Result struct could not be generated from the wire // representation. func (v *HistoryService_ReplicateEventsV2_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.RetryTaskError, err = _RetryTaskV2Error_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.RetryTaskError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ReplicateEventsV2_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_ReplicateEventsV2_Result // struct. func (v *HistoryService_ReplicateEventsV2_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.RetryTaskError != nil { fields[i] = fmt.Sprintf("RetryTaskError: %v", v.RetryTaskError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("HistoryService_ReplicateEventsV2_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ReplicateEventsV2_Result match the // provided HistoryService_ReplicateEventsV2_Result. // // This function performs a deep comparison. func (v *HistoryService_ReplicateEventsV2_Result) Equals(rhs *HistoryService_ReplicateEventsV2_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.RetryTaskError == nil && rhs.RetryTaskError == nil) || (v.RetryTaskError != nil && rhs.RetryTaskError != nil && v.RetryTaskError.Equals(rhs.RetryTaskError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ReplicateEventsV2_Result. func (v *HistoryService_ReplicateEventsV2_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.RetryTaskError != nil { err = multierr.Append(err, enc.AddObject("retryTaskError", v.RetryTaskError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_ReplicateEventsV2_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_ReplicateEventsV2_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_ReplicateEventsV2_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_ReplicateEventsV2_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_ReplicateEventsV2_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_ReplicateEventsV2_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_ReplicateEventsV2_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_ReplicateEventsV2_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_ReplicateEventsV2_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_ReplicateEventsV2_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetRetryTaskError returns the value of RetryTaskError if it is set or its // zero value if it is unset. func (v *HistoryService_ReplicateEventsV2_Result) GetRetryTaskError() (o *shared.RetryTaskV2Error) { if v != nil && v.RetryTaskError != nil { return v.RetryTaskError } return } // IsSetRetryTaskError returns true if RetryTaskError is not nil. func (v *HistoryService_ReplicateEventsV2_Result) IsSetRetryTaskError() bool { return v != nil && v.RetryTaskError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_ReplicateEventsV2_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_ReplicateEventsV2_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ReplicateEventsV2" for this struct. func (v *HistoryService_ReplicateEventsV2_Result) MethodName() string { return "ReplicateEventsV2" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_ReplicateEventsV2_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RequestCancelWorkflowExecution_Args represents the arguments for the HistoryService.RequestCancelWorkflowExecution function. // // The arguments for RequestCancelWorkflowExecution are sent and received over the wire as this struct. type HistoryService_RequestCancelWorkflowExecution_Args struct { CancelRequest *RequestCancelWorkflowExecutionRequest `json:"cancelRequest,omitempty"` } // ToWire translates a HistoryService_RequestCancelWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RequestCancelWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CancelRequest != nil { w, err = v.CancelRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RequestCancelWorkflowExecutionRequest_1_Read(w wire.Value) (*RequestCancelWorkflowExecutionRequest, error) { var v RequestCancelWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RequestCancelWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RequestCancelWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RequestCancelWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RequestCancelWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CancelRequest, err = _RequestCancelWorkflowExecutionRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RequestCancelWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RequestCancelWorkflowExecution_Args struct could not be encoded. func (v *HistoryService_RequestCancelWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CancelRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CancelRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RequestCancelWorkflowExecutionRequest_1_Decode(sr stream.Reader) (*RequestCancelWorkflowExecutionRequest, error) { var v RequestCancelWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RequestCancelWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RequestCancelWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *HistoryService_RequestCancelWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CancelRequest, err = _RequestCancelWorkflowExecutionRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RequestCancelWorkflowExecution_Args // struct. func (v *HistoryService_RequestCancelWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CancelRequest != nil { fields[i] = fmt.Sprintf("CancelRequest: %v", v.CancelRequest) i++ } return fmt.Sprintf("HistoryService_RequestCancelWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RequestCancelWorkflowExecution_Args match the // provided HistoryService_RequestCancelWorkflowExecution_Args. // // This function performs a deep comparison. func (v *HistoryService_RequestCancelWorkflowExecution_Args) Equals(rhs *HistoryService_RequestCancelWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CancelRequest == nil && rhs.CancelRequest == nil) || (v.CancelRequest != nil && rhs.CancelRequest != nil && v.CancelRequest.Equals(rhs.CancelRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RequestCancelWorkflowExecution_Args. func (v *HistoryService_RequestCancelWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CancelRequest != nil { err = multierr.Append(err, enc.AddObject("cancelRequest", v.CancelRequest)) } return err } // GetCancelRequest returns the value of CancelRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RequestCancelWorkflowExecution_Args) GetCancelRequest() (o *RequestCancelWorkflowExecutionRequest) { if v != nil && v.CancelRequest != nil { return v.CancelRequest } return } // IsSetCancelRequest returns true if CancelRequest is not nil. func (v *HistoryService_RequestCancelWorkflowExecution_Args) IsSetCancelRequest() bool { return v != nil && v.CancelRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RequestCancelWorkflowExecution" for this struct. func (v *HistoryService_RequestCancelWorkflowExecution_Args) MethodName() string { return "RequestCancelWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RequestCancelWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RequestCancelWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RequestCancelWorkflowExecution // function. var HistoryService_RequestCancelWorkflowExecution_Helper = struct { // Args accepts the parameters of RequestCancelWorkflowExecution in-order and returns // the arguments struct for the function. Args func( cancelRequest *RequestCancelWorkflowExecutionRequest, ) *HistoryService_RequestCancelWorkflowExecution_Args // IsException returns true if the given error can be thrown // by RequestCancelWorkflowExecution. // // An error can be thrown by RequestCancelWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RequestCancelWorkflowExecution // given the error returned by it. The provided error may // be nil if RequestCancelWorkflowExecution did not fail. // // This allows mapping errors returned by RequestCancelWorkflowExecution into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RequestCancelWorkflowExecution // // err := RequestCancelWorkflowExecution(args) // result, err := HistoryService_RequestCancelWorkflowExecution_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RequestCancelWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_RequestCancelWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for RequestCancelWorkflowExecution // and returns the erorr returned by it (if any). // // The error is non-nil only if RequestCancelWorkflowExecution threw an // exception. // // result := deserialize(bytes) // err := HistoryService_RequestCancelWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RequestCancelWorkflowExecution_Result) error }{} func init() { HistoryService_RequestCancelWorkflowExecution_Helper.Args = func( cancelRequest *RequestCancelWorkflowExecutionRequest, ) *HistoryService_RequestCancelWorkflowExecution_Args { return &HistoryService_RequestCancelWorkflowExecution_Args{ CancelRequest: cancelRequest, } } HistoryService_RequestCancelWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.CancellationAlreadyRequestedError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RequestCancelWorkflowExecution_Helper.WrapResponse = func(err error) (*HistoryService_RequestCancelWorkflowExecution_Result, error) { if err == nil { return &HistoryService_RequestCancelWorkflowExecution_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RequestCancelWorkflowExecution_Result.BadRequestError") } return &HistoryService_RequestCancelWorkflowExecution_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RequestCancelWorkflowExecution_Result.InternalServiceError") } return &HistoryService_RequestCancelWorkflowExecution_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RequestCancelWorkflowExecution_Result.EntityNotExistError") } return &HistoryService_RequestCancelWorkflowExecution_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RequestCancelWorkflowExecution_Result.ShardOwnershipLostError") } return &HistoryService_RequestCancelWorkflowExecution_Result{ShardOwnershipLostError: e}, nil case *shared.CancellationAlreadyRequestedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RequestCancelWorkflowExecution_Result.CancellationAlreadyRequestedError") } return &HistoryService_RequestCancelWorkflowExecution_Result{CancellationAlreadyRequestedError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RequestCancelWorkflowExecution_Result.DomainNotActiveError") } return &HistoryService_RequestCancelWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RequestCancelWorkflowExecution_Result.LimitExceededError") } return &HistoryService_RequestCancelWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RequestCancelWorkflowExecution_Result.ServiceBusyError") } return &HistoryService_RequestCancelWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RequestCancelWorkflowExecution_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RequestCancelWorkflowExecution_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RequestCancelWorkflowExecution_Helper.UnwrapResponse = func(result *HistoryService_RequestCancelWorkflowExecution_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.CancellationAlreadyRequestedError != nil { err = result.CancellationAlreadyRequestedError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } return } } // HistoryService_RequestCancelWorkflowExecution_Result represents the result of a HistoryService.RequestCancelWorkflowExecution function call. // // The result of a RequestCancelWorkflowExecution execution is sent and received over the wire as this struct. type HistoryService_RequestCancelWorkflowExecution_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` CancellationAlreadyRequestedError *shared.CancellationAlreadyRequestedError `json:"cancellationAlreadyRequestedError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RequestCancelWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RequestCancelWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.CancellationAlreadyRequestedError != nil { w, err = v.CancellationAlreadyRequestedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_RequestCancelWorkflowExecution_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CancellationAlreadyRequestedError_Read(w wire.Value) (*shared.CancellationAlreadyRequestedError, error) { var v shared.CancellationAlreadyRequestedError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RequestCancelWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RequestCancelWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RequestCancelWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RequestCancelWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.CancellationAlreadyRequestedError, err = _CancellationAlreadyRequestedError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 10: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.CancellationAlreadyRequestedError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RequestCancelWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RequestCancelWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RequestCancelWorkflowExecution_Result struct could not be encoded. func (v *HistoryService_RequestCancelWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancellationAlreadyRequestedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.CancellationAlreadyRequestedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.CancellationAlreadyRequestedError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RequestCancelWorkflowExecution_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _CancellationAlreadyRequestedError_Decode(sr stream.Reader) (*shared.CancellationAlreadyRequestedError, error) { var v shared.CancellationAlreadyRequestedError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RequestCancelWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RequestCancelWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *HistoryService_RequestCancelWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.CancellationAlreadyRequestedError, err = _CancellationAlreadyRequestedError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 10 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.CancellationAlreadyRequestedError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RequestCancelWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RequestCancelWorkflowExecution_Result // struct. func (v *HistoryService_RequestCancelWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.CancellationAlreadyRequestedError != nil { fields[i] = fmt.Sprintf("CancellationAlreadyRequestedError: %v", v.CancellationAlreadyRequestedError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RequestCancelWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RequestCancelWorkflowExecution_Result match the // provided HistoryService_RequestCancelWorkflowExecution_Result. // // This function performs a deep comparison. func (v *HistoryService_RequestCancelWorkflowExecution_Result) Equals(rhs *HistoryService_RequestCancelWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.CancellationAlreadyRequestedError == nil && rhs.CancellationAlreadyRequestedError == nil) || (v.CancellationAlreadyRequestedError != nil && rhs.CancellationAlreadyRequestedError != nil && v.CancellationAlreadyRequestedError.Equals(rhs.CancellationAlreadyRequestedError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RequestCancelWorkflowExecution_Result. func (v *HistoryService_RequestCancelWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.CancellationAlreadyRequestedError != nil { err = multierr.Append(err, enc.AddObject("cancellationAlreadyRequestedError", v.CancellationAlreadyRequestedError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RequestCancelWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RequestCancelWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RequestCancelWorkflowExecution_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RequestCancelWorkflowExecution_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RequestCancelWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RequestCancelWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RequestCancelWorkflowExecution_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RequestCancelWorkflowExecution_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetCancellationAlreadyRequestedError returns the value of CancellationAlreadyRequestedError if it is set or its // zero value if it is unset. func (v *HistoryService_RequestCancelWorkflowExecution_Result) GetCancellationAlreadyRequestedError() (o *shared.CancellationAlreadyRequestedError) { if v != nil && v.CancellationAlreadyRequestedError != nil { return v.CancellationAlreadyRequestedError } return } // IsSetCancellationAlreadyRequestedError returns true if CancellationAlreadyRequestedError is not nil. func (v *HistoryService_RequestCancelWorkflowExecution_Result) IsSetCancellationAlreadyRequestedError() bool { return v != nil && v.CancellationAlreadyRequestedError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RequestCancelWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RequestCancelWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RequestCancelWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RequestCancelWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RequestCancelWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RequestCancelWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RequestCancelWorkflowExecution_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RequestCancelWorkflowExecution_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RequestCancelWorkflowExecution" for this struct. func (v *HistoryService_RequestCancelWorkflowExecution_Result) MethodName() string { return "RequestCancelWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RequestCancelWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_ResetQueue_Args represents the arguments for the HistoryService.ResetQueue function. // // The arguments for ResetQueue are sent and received over the wire as this struct. type HistoryService_ResetQueue_Args struct { Request *shared.ResetQueueRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_ResetQueue_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ResetQueue_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetQueueRequest_Read(w wire.Value) (*shared.ResetQueueRequest, error) { var v shared.ResetQueueRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ResetQueue_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ResetQueue_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ResetQueue_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ResetQueue_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _ResetQueueRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_ResetQueue_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ResetQueue_Args struct could not be encoded. func (v *HistoryService_ResetQueue_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ResetQueueRequest_Decode(sr stream.Reader) (*shared.ResetQueueRequest, error) { var v shared.ResetQueueRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ResetQueue_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ResetQueue_Args struct could not be generated from the wire // representation. func (v *HistoryService_ResetQueue_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _ResetQueueRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_ResetQueue_Args // struct. func (v *HistoryService_ResetQueue_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_ResetQueue_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ResetQueue_Args match the // provided HistoryService_ResetQueue_Args. // // This function performs a deep comparison. func (v *HistoryService_ResetQueue_Args) Equals(rhs *HistoryService_ResetQueue_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ResetQueue_Args. func (v *HistoryService_ResetQueue_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_ResetQueue_Args) GetRequest() (o *shared.ResetQueueRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_ResetQueue_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ResetQueue" for this struct. func (v *HistoryService_ResetQueue_Args) MethodName() string { return "ResetQueue" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_ResetQueue_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_ResetQueue_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.ResetQueue // function. var HistoryService_ResetQueue_Helper = struct { // Args accepts the parameters of ResetQueue in-order and returns // the arguments struct for the function. Args func( request *shared.ResetQueueRequest, ) *HistoryService_ResetQueue_Args // IsException returns true if the given error can be thrown // by ResetQueue. // // An error can be thrown by ResetQueue only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ResetQueue // given the error returned by it. The provided error may // be nil if ResetQueue did not fail. // // This allows mapping errors returned by ResetQueue into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // ResetQueue // // err := ResetQueue(args) // result, err := HistoryService_ResetQueue_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from ResetQueue: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_ResetQueue_Result, error) // UnwrapResponse takes the result struct for ResetQueue // and returns the erorr returned by it (if any). // // The error is non-nil only if ResetQueue threw an // exception. // // result := deserialize(bytes) // err := HistoryService_ResetQueue_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_ResetQueue_Result) error }{} func init() { HistoryService_ResetQueue_Helper.Args = func( request *shared.ResetQueueRequest, ) *HistoryService_ResetQueue_Args { return &HistoryService_ResetQueue_Args{ Request: request, } } HistoryService_ResetQueue_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.AccessDeniedError: return true default: return false } } HistoryService_ResetQueue_Helper.WrapResponse = func(err error) (*HistoryService_ResetQueue_Result, error) { if err == nil { return &HistoryService_ResetQueue_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetQueue_Result.BadRequestError") } return &HistoryService_ResetQueue_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetQueue_Result.InternalServiceError") } return &HistoryService_ResetQueue_Result{InternalServiceError: e}, nil case *shared.AccessDeniedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetQueue_Result.AccessDeniedError") } return &HistoryService_ResetQueue_Result{AccessDeniedError: e}, nil } return nil, err } HistoryService_ResetQueue_Helper.UnwrapResponse = func(result *HistoryService_ResetQueue_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.AccessDeniedError != nil { err = result.AccessDeniedError return } return } } // HistoryService_ResetQueue_Result represents the result of a HistoryService.ResetQueue function call. // // The result of a ResetQueue execution is sent and received over the wire as this struct. type HistoryService_ResetQueue_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` AccessDeniedError *shared.AccessDeniedError `json:"accessDeniedError,omitempty"` } // ToWire translates a HistoryService_ResetQueue_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ResetQueue_Result) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.AccessDeniedError != nil { w, err = v.AccessDeniedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_ResetQueue_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_ResetQueue_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ResetQueue_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ResetQueue_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ResetQueue_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.AccessDeniedError, err = _AccessDeniedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ResetQueue_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_ResetQueue_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ResetQueue_Result struct could not be encoded. func (v *HistoryService_ResetQueue_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AccessDeniedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.AccessDeniedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ResetQueue_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_ResetQueue_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ResetQueue_Result struct could not be generated from the wire // representation. func (v *HistoryService_ResetQueue_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.AccessDeniedError, err = _AccessDeniedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.AccessDeniedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ResetQueue_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_ResetQueue_Result // struct. func (v *HistoryService_ResetQueue_Result) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.AccessDeniedError != nil { fields[i] = fmt.Sprintf("AccessDeniedError: %v", v.AccessDeniedError) i++ } return fmt.Sprintf("HistoryService_ResetQueue_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ResetQueue_Result match the // provided HistoryService_ResetQueue_Result. // // This function performs a deep comparison. func (v *HistoryService_ResetQueue_Result) Equals(rhs *HistoryService_ResetQueue_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.AccessDeniedError == nil && rhs.AccessDeniedError == nil) || (v.AccessDeniedError != nil && rhs.AccessDeniedError != nil && v.AccessDeniedError.Equals(rhs.AccessDeniedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ResetQueue_Result. func (v *HistoryService_ResetQueue_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.AccessDeniedError != nil { err = multierr.Append(err, enc.AddObject("accessDeniedError", v.AccessDeniedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetQueue_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_ResetQueue_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetQueue_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_ResetQueue_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetAccessDeniedError returns the value of AccessDeniedError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetQueue_Result) GetAccessDeniedError() (o *shared.AccessDeniedError) { if v != nil && v.AccessDeniedError != nil { return v.AccessDeniedError } return } // IsSetAccessDeniedError returns true if AccessDeniedError is not nil. func (v *HistoryService_ResetQueue_Result) IsSetAccessDeniedError() bool { return v != nil && v.AccessDeniedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ResetQueue" for this struct. func (v *HistoryService_ResetQueue_Result) MethodName() string { return "ResetQueue" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_ResetQueue_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_ResetStickyTaskList_Args represents the arguments for the HistoryService.ResetStickyTaskList function. // // The arguments for ResetStickyTaskList are sent and received over the wire as this struct. type HistoryService_ResetStickyTaskList_Args struct { ResetRequest *ResetStickyTaskListRequest `json:"resetRequest,omitempty"` } // ToWire translates a HistoryService_ResetStickyTaskList_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ResetStickyTaskList_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ResetRequest != nil { w, err = v.ResetRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetStickyTaskListRequest_Read(w wire.Value) (*ResetStickyTaskListRequest, error) { var v ResetStickyTaskListRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ResetStickyTaskList_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ResetStickyTaskList_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ResetStickyTaskList_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ResetStickyTaskList_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ResetRequest, err = _ResetStickyTaskListRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_ResetStickyTaskList_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ResetStickyTaskList_Args struct could not be encoded. func (v *HistoryService_ResetStickyTaskList_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ResetRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ResetRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ResetStickyTaskListRequest_Decode(sr stream.Reader) (*ResetStickyTaskListRequest, error) { var v ResetStickyTaskListRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ResetStickyTaskList_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ResetStickyTaskList_Args struct could not be generated from the wire // representation. func (v *HistoryService_ResetStickyTaskList_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ResetRequest, err = _ResetStickyTaskListRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_ResetStickyTaskList_Args // struct. func (v *HistoryService_ResetStickyTaskList_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ResetRequest != nil { fields[i] = fmt.Sprintf("ResetRequest: %v", v.ResetRequest) i++ } return fmt.Sprintf("HistoryService_ResetStickyTaskList_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ResetStickyTaskList_Args match the // provided HistoryService_ResetStickyTaskList_Args. // // This function performs a deep comparison. func (v *HistoryService_ResetStickyTaskList_Args) Equals(rhs *HistoryService_ResetStickyTaskList_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ResetRequest == nil && rhs.ResetRequest == nil) || (v.ResetRequest != nil && rhs.ResetRequest != nil && v.ResetRequest.Equals(rhs.ResetRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ResetStickyTaskList_Args. func (v *HistoryService_ResetStickyTaskList_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ResetRequest != nil { err = multierr.Append(err, enc.AddObject("resetRequest", v.ResetRequest)) } return err } // GetResetRequest returns the value of ResetRequest if it is set or its // zero value if it is unset. func (v *HistoryService_ResetStickyTaskList_Args) GetResetRequest() (o *ResetStickyTaskListRequest) { if v != nil && v.ResetRequest != nil { return v.ResetRequest } return } // IsSetResetRequest returns true if ResetRequest is not nil. func (v *HistoryService_ResetStickyTaskList_Args) IsSetResetRequest() bool { return v != nil && v.ResetRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ResetStickyTaskList" for this struct. func (v *HistoryService_ResetStickyTaskList_Args) MethodName() string { return "ResetStickyTaskList" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_ResetStickyTaskList_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_ResetStickyTaskList_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.ResetStickyTaskList // function. var HistoryService_ResetStickyTaskList_Helper = struct { // Args accepts the parameters of ResetStickyTaskList in-order and returns // the arguments struct for the function. Args func( resetRequest *ResetStickyTaskListRequest, ) *HistoryService_ResetStickyTaskList_Args // IsException returns true if the given error can be thrown // by ResetStickyTaskList. // // An error can be thrown by ResetStickyTaskList only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ResetStickyTaskList // given its return value and error. // // This allows mapping values and errors returned by // ResetStickyTaskList into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ResetStickyTaskList // // value, err := ResetStickyTaskList(args) // result, err := HistoryService_ResetStickyTaskList_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ResetStickyTaskList: %v", err) // } // serialize(result) WrapResponse func(*ResetStickyTaskListResponse, error) (*HistoryService_ResetStickyTaskList_Result, error) // UnwrapResponse takes the result struct for ResetStickyTaskList // and returns the value or error returned by it. // // The error is non-nil only if ResetStickyTaskList threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_ResetStickyTaskList_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_ResetStickyTaskList_Result) (*ResetStickyTaskListResponse, error) }{} func init() { HistoryService_ResetStickyTaskList_Helper.Args = func( resetRequest *ResetStickyTaskListRequest, ) *HistoryService_ResetStickyTaskList_Args { return &HistoryService_ResetStickyTaskList_Args{ ResetRequest: resetRequest, } } HistoryService_ResetStickyTaskList_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_ResetStickyTaskList_Helper.WrapResponse = func(success *ResetStickyTaskListResponse, err error) (*HistoryService_ResetStickyTaskList_Result, error) { if err == nil { return &HistoryService_ResetStickyTaskList_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetStickyTaskList_Result.BadRequestError") } return &HistoryService_ResetStickyTaskList_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetStickyTaskList_Result.InternalServiceError") } return &HistoryService_ResetStickyTaskList_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetStickyTaskList_Result.EntityNotExistError") } return &HistoryService_ResetStickyTaskList_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetStickyTaskList_Result.ShardOwnershipLostError") } return &HistoryService_ResetStickyTaskList_Result{ShardOwnershipLostError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetStickyTaskList_Result.LimitExceededError") } return &HistoryService_ResetStickyTaskList_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetStickyTaskList_Result.ServiceBusyError") } return &HistoryService_ResetStickyTaskList_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetStickyTaskList_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_ResetStickyTaskList_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_ResetStickyTaskList_Helper.UnwrapResponse = func(result *HistoryService_ResetStickyTaskList_Result) (success *ResetStickyTaskListResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_ResetStickyTaskList_Result represents the result of a HistoryService.ResetStickyTaskList function call. // // The result of a ResetStickyTaskList execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_ResetStickyTaskList_Result struct { // Value returned by ResetStickyTaskList after a successful execution. Success *ResetStickyTaskListResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_ResetStickyTaskList_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ResetStickyTaskList_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_ResetStickyTaskList_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetStickyTaskListResponse_Read(w wire.Value) (*ResetStickyTaskListResponse, error) { var v ResetStickyTaskListResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ResetStickyTaskList_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ResetStickyTaskList_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ResetStickyTaskList_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ResetStickyTaskList_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ResetStickyTaskListResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_ResetStickyTaskList_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_ResetStickyTaskList_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ResetStickyTaskList_Result struct could not be encoded. func (v *HistoryService_ResetStickyTaskList_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_ResetStickyTaskList_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ResetStickyTaskListResponse_Decode(sr stream.Reader) (*ResetStickyTaskListResponse, error) { var v ResetStickyTaskListResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ResetStickyTaskList_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ResetStickyTaskList_Result struct could not be generated from the wire // representation. func (v *HistoryService_ResetStickyTaskList_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ResetStickyTaskListResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_ResetStickyTaskList_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_ResetStickyTaskList_Result // struct. func (v *HistoryService_ResetStickyTaskList_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_ResetStickyTaskList_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ResetStickyTaskList_Result match the // provided HistoryService_ResetStickyTaskList_Result. // // This function performs a deep comparison. func (v *HistoryService_ResetStickyTaskList_Result) Equals(rhs *HistoryService_ResetStickyTaskList_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ResetStickyTaskList_Result. func (v *HistoryService_ResetStickyTaskList_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_ResetStickyTaskList_Result) GetSuccess() (o *ResetStickyTaskListResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_ResetStickyTaskList_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetStickyTaskList_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_ResetStickyTaskList_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetStickyTaskList_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_ResetStickyTaskList_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetStickyTaskList_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_ResetStickyTaskList_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetStickyTaskList_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_ResetStickyTaskList_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetStickyTaskList_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_ResetStickyTaskList_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetStickyTaskList_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_ResetStickyTaskList_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetStickyTaskList_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_ResetStickyTaskList_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ResetStickyTaskList" for this struct. func (v *HistoryService_ResetStickyTaskList_Result) MethodName() string { return "ResetStickyTaskList" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_ResetStickyTaskList_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_ResetWorkflowExecution_Args represents the arguments for the HistoryService.ResetWorkflowExecution function. // // The arguments for ResetWorkflowExecution are sent and received over the wire as this struct. type HistoryService_ResetWorkflowExecution_Args struct { ResetRequest *ResetWorkflowExecutionRequest `json:"resetRequest,omitempty"` } // ToWire translates a HistoryService_ResetWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ResetWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ResetRequest != nil { w, err = v.ResetRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetWorkflowExecutionRequest_1_Read(w wire.Value) (*ResetWorkflowExecutionRequest, error) { var v ResetWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ResetWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ResetWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ResetWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ResetWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ResetRequest, err = _ResetWorkflowExecutionRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_ResetWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ResetWorkflowExecution_Args struct could not be encoded. func (v *HistoryService_ResetWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ResetRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ResetRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ResetWorkflowExecutionRequest_1_Decode(sr stream.Reader) (*ResetWorkflowExecutionRequest, error) { var v ResetWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ResetWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ResetWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *HistoryService_ResetWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ResetRequest, err = _ResetWorkflowExecutionRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_ResetWorkflowExecution_Args // struct. func (v *HistoryService_ResetWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ResetRequest != nil { fields[i] = fmt.Sprintf("ResetRequest: %v", v.ResetRequest) i++ } return fmt.Sprintf("HistoryService_ResetWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ResetWorkflowExecution_Args match the // provided HistoryService_ResetWorkflowExecution_Args. // // This function performs a deep comparison. func (v *HistoryService_ResetWorkflowExecution_Args) Equals(rhs *HistoryService_ResetWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ResetRequest == nil && rhs.ResetRequest == nil) || (v.ResetRequest != nil && rhs.ResetRequest != nil && v.ResetRequest.Equals(rhs.ResetRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ResetWorkflowExecution_Args. func (v *HistoryService_ResetWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ResetRequest != nil { err = multierr.Append(err, enc.AddObject("resetRequest", v.ResetRequest)) } return err } // GetResetRequest returns the value of ResetRequest if it is set or its // zero value if it is unset. func (v *HistoryService_ResetWorkflowExecution_Args) GetResetRequest() (o *ResetWorkflowExecutionRequest) { if v != nil && v.ResetRequest != nil { return v.ResetRequest } return } // IsSetResetRequest returns true if ResetRequest is not nil. func (v *HistoryService_ResetWorkflowExecution_Args) IsSetResetRequest() bool { return v != nil && v.ResetRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ResetWorkflowExecution" for this struct. func (v *HistoryService_ResetWorkflowExecution_Args) MethodName() string { return "ResetWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_ResetWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_ResetWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.ResetWorkflowExecution // function. var HistoryService_ResetWorkflowExecution_Helper = struct { // Args accepts the parameters of ResetWorkflowExecution in-order and returns // the arguments struct for the function. Args func( resetRequest *ResetWorkflowExecutionRequest, ) *HistoryService_ResetWorkflowExecution_Args // IsException returns true if the given error can be thrown // by ResetWorkflowExecution. // // An error can be thrown by ResetWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ResetWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // ResetWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ResetWorkflowExecution // // value, err := ResetWorkflowExecution(args) // result, err := HistoryService_ResetWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ResetWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*shared.ResetWorkflowExecutionResponse, error) (*HistoryService_ResetWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for ResetWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if ResetWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_ResetWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_ResetWorkflowExecution_Result) (*shared.ResetWorkflowExecutionResponse, error) }{} func init() { HistoryService_ResetWorkflowExecution_Helper.Args = func( resetRequest *ResetWorkflowExecutionRequest, ) *HistoryService_ResetWorkflowExecution_Args { return &HistoryService_ResetWorkflowExecution_Args{ ResetRequest: resetRequest, } } HistoryService_ResetWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true default: return false } } HistoryService_ResetWorkflowExecution_Helper.WrapResponse = func(success *shared.ResetWorkflowExecutionResponse, err error) (*HistoryService_ResetWorkflowExecution_Result, error) { if err == nil { return &HistoryService_ResetWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetWorkflowExecution_Result.BadRequestError") } return &HistoryService_ResetWorkflowExecution_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetWorkflowExecution_Result.InternalServiceError") } return &HistoryService_ResetWorkflowExecution_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetWorkflowExecution_Result.EntityNotExistError") } return &HistoryService_ResetWorkflowExecution_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetWorkflowExecution_Result.ShardOwnershipLostError") } return &HistoryService_ResetWorkflowExecution_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetWorkflowExecution_Result.DomainNotActiveError") } return &HistoryService_ResetWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetWorkflowExecution_Result.LimitExceededError") } return &HistoryService_ResetWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ResetWorkflowExecution_Result.ServiceBusyError") } return &HistoryService_ResetWorkflowExecution_Result{ServiceBusyError: e}, nil } return nil, err } HistoryService_ResetWorkflowExecution_Helper.UnwrapResponse = func(result *HistoryService_ResetWorkflowExecution_Result) (success *shared.ResetWorkflowExecutionResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_ResetWorkflowExecution_Result represents the result of a HistoryService.ResetWorkflowExecution function call. // // The result of a ResetWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_ResetWorkflowExecution_Result struct { // Value returned by ResetWorkflowExecution after a successful execution. Success *shared.ResetWorkflowExecutionResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a HistoryService_ResetWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ResetWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_ResetWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetWorkflowExecutionResponse_Read(w wire.Value) (*shared.ResetWorkflowExecutionResponse, error) { var v shared.ResetWorkflowExecutionResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ResetWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ResetWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ResetWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ResetWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ResetWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_ResetWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_ResetWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ResetWorkflowExecution_Result struct could not be encoded. func (v *HistoryService_ResetWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_ResetWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ResetWorkflowExecutionResponse_Decode(sr stream.Reader) (*shared.ResetWorkflowExecutionResponse, error) { var v shared.ResetWorkflowExecutionResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ResetWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ResetWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *HistoryService_ResetWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ResetWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_ResetWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_ResetWorkflowExecution_Result // struct. func (v *HistoryService_ResetWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("HistoryService_ResetWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ResetWorkflowExecution_Result match the // provided HistoryService_ResetWorkflowExecution_Result. // // This function performs a deep comparison. func (v *HistoryService_ResetWorkflowExecution_Result) Equals(rhs *HistoryService_ResetWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ResetWorkflowExecution_Result. func (v *HistoryService_ResetWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_ResetWorkflowExecution_Result) GetSuccess() (o *shared.ResetWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_ResetWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_ResetWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetWorkflowExecution_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_ResetWorkflowExecution_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_ResetWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetWorkflowExecution_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_ResetWorkflowExecution_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_ResetWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_ResetWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_ResetWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_ResetWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ResetWorkflowExecution" for this struct. func (v *HistoryService_ResetWorkflowExecution_Result) MethodName() string { return "ResetWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_ResetWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RespondActivityTaskCanceled_Args represents the arguments for the HistoryService.RespondActivityTaskCanceled function. // // The arguments for RespondActivityTaskCanceled are sent and received over the wire as this struct. type HistoryService_RespondActivityTaskCanceled_Args struct { CanceledRequest *RespondActivityTaskCanceledRequest `json:"canceledRequest,omitempty"` } // ToWire translates a HistoryService_RespondActivityTaskCanceled_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondActivityTaskCanceled_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CanceledRequest != nil { w, err = v.CanceledRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskCanceledRequest_1_Read(w wire.Value) (*RespondActivityTaskCanceledRequest, error) { var v RespondActivityTaskCanceledRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RespondActivityTaskCanceled_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondActivityTaskCanceled_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondActivityTaskCanceled_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondActivityTaskCanceled_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CanceledRequest, err = _RespondActivityTaskCanceledRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RespondActivityTaskCanceled_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondActivityTaskCanceled_Args struct could not be encoded. func (v *HistoryService_RespondActivityTaskCanceled_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CanceledRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CanceledRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskCanceledRequest_1_Decode(sr stream.Reader) (*RespondActivityTaskCanceledRequest, error) { var v RespondActivityTaskCanceledRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RespondActivityTaskCanceled_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondActivityTaskCanceled_Args struct could not be generated from the wire // representation. func (v *HistoryService_RespondActivityTaskCanceled_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CanceledRequest, err = _RespondActivityTaskCanceledRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RespondActivityTaskCanceled_Args // struct. func (v *HistoryService_RespondActivityTaskCanceled_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CanceledRequest != nil { fields[i] = fmt.Sprintf("CanceledRequest: %v", v.CanceledRequest) i++ } return fmt.Sprintf("HistoryService_RespondActivityTaskCanceled_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondActivityTaskCanceled_Args match the // provided HistoryService_RespondActivityTaskCanceled_Args. // // This function performs a deep comparison. func (v *HistoryService_RespondActivityTaskCanceled_Args) Equals(rhs *HistoryService_RespondActivityTaskCanceled_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CanceledRequest == nil && rhs.CanceledRequest == nil) || (v.CanceledRequest != nil && rhs.CanceledRequest != nil && v.CanceledRequest.Equals(rhs.CanceledRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondActivityTaskCanceled_Args. func (v *HistoryService_RespondActivityTaskCanceled_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CanceledRequest != nil { err = multierr.Append(err, enc.AddObject("canceledRequest", v.CanceledRequest)) } return err } // GetCanceledRequest returns the value of CanceledRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCanceled_Args) GetCanceledRequest() (o *RespondActivityTaskCanceledRequest) { if v != nil && v.CanceledRequest != nil { return v.CanceledRequest } return } // IsSetCanceledRequest returns true if CanceledRequest is not nil. func (v *HistoryService_RespondActivityTaskCanceled_Args) IsSetCanceledRequest() bool { return v != nil && v.CanceledRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondActivityTaskCanceled" for this struct. func (v *HistoryService_RespondActivityTaskCanceled_Args) MethodName() string { return "RespondActivityTaskCanceled" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RespondActivityTaskCanceled_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RespondActivityTaskCanceled_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RespondActivityTaskCanceled // function. var HistoryService_RespondActivityTaskCanceled_Helper = struct { // Args accepts the parameters of RespondActivityTaskCanceled in-order and returns // the arguments struct for the function. Args func( canceledRequest *RespondActivityTaskCanceledRequest, ) *HistoryService_RespondActivityTaskCanceled_Args // IsException returns true if the given error can be thrown // by RespondActivityTaskCanceled. // // An error can be thrown by RespondActivityTaskCanceled only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondActivityTaskCanceled // given the error returned by it. The provided error may // be nil if RespondActivityTaskCanceled did not fail. // // This allows mapping errors returned by RespondActivityTaskCanceled into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondActivityTaskCanceled // // err := RespondActivityTaskCanceled(args) // result, err := HistoryService_RespondActivityTaskCanceled_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondActivityTaskCanceled: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_RespondActivityTaskCanceled_Result, error) // UnwrapResponse takes the result struct for RespondActivityTaskCanceled // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondActivityTaskCanceled threw an // exception. // // result := deserialize(bytes) // err := HistoryService_RespondActivityTaskCanceled_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RespondActivityTaskCanceled_Result) error }{} func init() { HistoryService_RespondActivityTaskCanceled_Helper.Args = func( canceledRequest *RespondActivityTaskCanceledRequest, ) *HistoryService_RespondActivityTaskCanceled_Args { return &HistoryService_RespondActivityTaskCanceled_Args{ CanceledRequest: canceledRequest, } } HistoryService_RespondActivityTaskCanceled_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RespondActivityTaskCanceled_Helper.WrapResponse = func(err error) (*HistoryService_RespondActivityTaskCanceled_Result, error) { if err == nil { return &HistoryService_RespondActivityTaskCanceled_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCanceled_Result.BadRequestError") } return &HistoryService_RespondActivityTaskCanceled_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCanceled_Result.InternalServiceError") } return &HistoryService_RespondActivityTaskCanceled_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCanceled_Result.EntityNotExistError") } return &HistoryService_RespondActivityTaskCanceled_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCanceled_Result.ShardOwnershipLostError") } return &HistoryService_RespondActivityTaskCanceled_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCanceled_Result.DomainNotActiveError") } return &HistoryService_RespondActivityTaskCanceled_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCanceled_Result.LimitExceededError") } return &HistoryService_RespondActivityTaskCanceled_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCanceled_Result.ServiceBusyError") } return &HistoryService_RespondActivityTaskCanceled_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCanceled_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RespondActivityTaskCanceled_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RespondActivityTaskCanceled_Helper.UnwrapResponse = func(result *HistoryService_RespondActivityTaskCanceled_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } return } } // HistoryService_RespondActivityTaskCanceled_Result represents the result of a HistoryService.RespondActivityTaskCanceled function call. // // The result of a RespondActivityTaskCanceled execution is sent and received over the wire as this struct. type HistoryService_RespondActivityTaskCanceled_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RespondActivityTaskCanceled_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondActivityTaskCanceled_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_RespondActivityTaskCanceled_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_RespondActivityTaskCanceled_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondActivityTaskCanceled_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondActivityTaskCanceled_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondActivityTaskCanceled_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondActivityTaskCanceled_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RespondActivityTaskCanceled_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondActivityTaskCanceled_Result struct could not be encoded. func (v *HistoryService_RespondActivityTaskCanceled_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondActivityTaskCanceled_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_RespondActivityTaskCanceled_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondActivityTaskCanceled_Result struct could not be generated from the wire // representation. func (v *HistoryService_RespondActivityTaskCanceled_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondActivityTaskCanceled_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RespondActivityTaskCanceled_Result // struct. func (v *HistoryService_RespondActivityTaskCanceled_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RespondActivityTaskCanceled_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondActivityTaskCanceled_Result match the // provided HistoryService_RespondActivityTaskCanceled_Result. // // This function performs a deep comparison. func (v *HistoryService_RespondActivityTaskCanceled_Result) Equals(rhs *HistoryService_RespondActivityTaskCanceled_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondActivityTaskCanceled_Result. func (v *HistoryService_RespondActivityTaskCanceled_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCanceled_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RespondActivityTaskCanceled_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCanceled_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RespondActivityTaskCanceled_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCanceled_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RespondActivityTaskCanceled_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCanceled_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RespondActivityTaskCanceled_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCanceled_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RespondActivityTaskCanceled_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCanceled_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RespondActivityTaskCanceled_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCanceled_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RespondActivityTaskCanceled_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCanceled_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RespondActivityTaskCanceled_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondActivityTaskCanceled" for this struct. func (v *HistoryService_RespondActivityTaskCanceled_Result) MethodName() string { return "RespondActivityTaskCanceled" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RespondActivityTaskCanceled_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RespondActivityTaskCompleted_Args represents the arguments for the HistoryService.RespondActivityTaskCompleted function. // // The arguments for RespondActivityTaskCompleted are sent and received over the wire as this struct. type HistoryService_RespondActivityTaskCompleted_Args struct { CompleteRequest *RespondActivityTaskCompletedRequest `json:"completeRequest,omitempty"` } // ToWire translates a HistoryService_RespondActivityTaskCompleted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondActivityTaskCompleted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CompleteRequest != nil { w, err = v.CompleteRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskCompletedRequest_1_Read(w wire.Value) (*RespondActivityTaskCompletedRequest, error) { var v RespondActivityTaskCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RespondActivityTaskCompleted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondActivityTaskCompleted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondActivityTaskCompleted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondActivityTaskCompleted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CompleteRequest, err = _RespondActivityTaskCompletedRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RespondActivityTaskCompleted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondActivityTaskCompleted_Args struct could not be encoded. func (v *HistoryService_RespondActivityTaskCompleted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CompleteRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CompleteRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskCompletedRequest_1_Decode(sr stream.Reader) (*RespondActivityTaskCompletedRequest, error) { var v RespondActivityTaskCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RespondActivityTaskCompleted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondActivityTaskCompleted_Args struct could not be generated from the wire // representation. func (v *HistoryService_RespondActivityTaskCompleted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CompleteRequest, err = _RespondActivityTaskCompletedRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RespondActivityTaskCompleted_Args // struct. func (v *HistoryService_RespondActivityTaskCompleted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CompleteRequest != nil { fields[i] = fmt.Sprintf("CompleteRequest: %v", v.CompleteRequest) i++ } return fmt.Sprintf("HistoryService_RespondActivityTaskCompleted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondActivityTaskCompleted_Args match the // provided HistoryService_RespondActivityTaskCompleted_Args. // // This function performs a deep comparison. func (v *HistoryService_RespondActivityTaskCompleted_Args) Equals(rhs *HistoryService_RespondActivityTaskCompleted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CompleteRequest == nil && rhs.CompleteRequest == nil) || (v.CompleteRequest != nil && rhs.CompleteRequest != nil && v.CompleteRequest.Equals(rhs.CompleteRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondActivityTaskCompleted_Args. func (v *HistoryService_RespondActivityTaskCompleted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CompleteRequest != nil { err = multierr.Append(err, enc.AddObject("completeRequest", v.CompleteRequest)) } return err } // GetCompleteRequest returns the value of CompleteRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCompleted_Args) GetCompleteRequest() (o *RespondActivityTaskCompletedRequest) { if v != nil && v.CompleteRequest != nil { return v.CompleteRequest } return } // IsSetCompleteRequest returns true if CompleteRequest is not nil. func (v *HistoryService_RespondActivityTaskCompleted_Args) IsSetCompleteRequest() bool { return v != nil && v.CompleteRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondActivityTaskCompleted" for this struct. func (v *HistoryService_RespondActivityTaskCompleted_Args) MethodName() string { return "RespondActivityTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RespondActivityTaskCompleted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RespondActivityTaskCompleted_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RespondActivityTaskCompleted // function. var HistoryService_RespondActivityTaskCompleted_Helper = struct { // Args accepts the parameters of RespondActivityTaskCompleted in-order and returns // the arguments struct for the function. Args func( completeRequest *RespondActivityTaskCompletedRequest, ) *HistoryService_RespondActivityTaskCompleted_Args // IsException returns true if the given error can be thrown // by RespondActivityTaskCompleted. // // An error can be thrown by RespondActivityTaskCompleted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondActivityTaskCompleted // given the error returned by it. The provided error may // be nil if RespondActivityTaskCompleted did not fail. // // This allows mapping errors returned by RespondActivityTaskCompleted into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondActivityTaskCompleted // // err := RespondActivityTaskCompleted(args) // result, err := HistoryService_RespondActivityTaskCompleted_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondActivityTaskCompleted: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_RespondActivityTaskCompleted_Result, error) // UnwrapResponse takes the result struct for RespondActivityTaskCompleted // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondActivityTaskCompleted threw an // exception. // // result := deserialize(bytes) // err := HistoryService_RespondActivityTaskCompleted_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RespondActivityTaskCompleted_Result) error }{} func init() { HistoryService_RespondActivityTaskCompleted_Helper.Args = func( completeRequest *RespondActivityTaskCompletedRequest, ) *HistoryService_RespondActivityTaskCompleted_Args { return &HistoryService_RespondActivityTaskCompleted_Args{ CompleteRequest: completeRequest, } } HistoryService_RespondActivityTaskCompleted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RespondActivityTaskCompleted_Helper.WrapResponse = func(err error) (*HistoryService_RespondActivityTaskCompleted_Result, error) { if err == nil { return &HistoryService_RespondActivityTaskCompleted_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCompleted_Result.BadRequestError") } return &HistoryService_RespondActivityTaskCompleted_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCompleted_Result.InternalServiceError") } return &HistoryService_RespondActivityTaskCompleted_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCompleted_Result.EntityNotExistError") } return &HistoryService_RespondActivityTaskCompleted_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCompleted_Result.ShardOwnershipLostError") } return &HistoryService_RespondActivityTaskCompleted_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCompleted_Result.DomainNotActiveError") } return &HistoryService_RespondActivityTaskCompleted_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCompleted_Result.LimitExceededError") } return &HistoryService_RespondActivityTaskCompleted_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCompleted_Result.ServiceBusyError") } return &HistoryService_RespondActivityTaskCompleted_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskCompleted_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RespondActivityTaskCompleted_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RespondActivityTaskCompleted_Helper.UnwrapResponse = func(result *HistoryService_RespondActivityTaskCompleted_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } return } } // HistoryService_RespondActivityTaskCompleted_Result represents the result of a HistoryService.RespondActivityTaskCompleted function call. // // The result of a RespondActivityTaskCompleted execution is sent and received over the wire as this struct. type HistoryService_RespondActivityTaskCompleted_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RespondActivityTaskCompleted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondActivityTaskCompleted_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_RespondActivityTaskCompleted_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_RespondActivityTaskCompleted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondActivityTaskCompleted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondActivityTaskCompleted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondActivityTaskCompleted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondActivityTaskCompleted_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RespondActivityTaskCompleted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondActivityTaskCompleted_Result struct could not be encoded. func (v *HistoryService_RespondActivityTaskCompleted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondActivityTaskCompleted_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_RespondActivityTaskCompleted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondActivityTaskCompleted_Result struct could not be generated from the wire // representation. func (v *HistoryService_RespondActivityTaskCompleted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondActivityTaskCompleted_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RespondActivityTaskCompleted_Result // struct. func (v *HistoryService_RespondActivityTaskCompleted_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RespondActivityTaskCompleted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondActivityTaskCompleted_Result match the // provided HistoryService_RespondActivityTaskCompleted_Result. // // This function performs a deep comparison. func (v *HistoryService_RespondActivityTaskCompleted_Result) Equals(rhs *HistoryService_RespondActivityTaskCompleted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondActivityTaskCompleted_Result. func (v *HistoryService_RespondActivityTaskCompleted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCompleted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RespondActivityTaskCompleted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCompleted_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RespondActivityTaskCompleted_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCompleted_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RespondActivityTaskCompleted_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCompleted_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RespondActivityTaskCompleted_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCompleted_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RespondActivityTaskCompleted_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCompleted_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RespondActivityTaskCompleted_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCompleted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RespondActivityTaskCompleted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskCompleted_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RespondActivityTaskCompleted_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondActivityTaskCompleted" for this struct. func (v *HistoryService_RespondActivityTaskCompleted_Result) MethodName() string { return "RespondActivityTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RespondActivityTaskCompleted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RespondActivityTaskFailed_Args represents the arguments for the HistoryService.RespondActivityTaskFailed function. // // The arguments for RespondActivityTaskFailed are sent and received over the wire as this struct. type HistoryService_RespondActivityTaskFailed_Args struct { FailRequest *RespondActivityTaskFailedRequest `json:"failRequest,omitempty"` } // ToWire translates a HistoryService_RespondActivityTaskFailed_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondActivityTaskFailed_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.FailRequest != nil { w, err = v.FailRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondActivityTaskFailedRequest_1_Read(w wire.Value) (*RespondActivityTaskFailedRequest, error) { var v RespondActivityTaskFailedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RespondActivityTaskFailed_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondActivityTaskFailed_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondActivityTaskFailed_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondActivityTaskFailed_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.FailRequest, err = _RespondActivityTaskFailedRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RespondActivityTaskFailed_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondActivityTaskFailed_Args struct could not be encoded. func (v *HistoryService_RespondActivityTaskFailed_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FailRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.FailRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondActivityTaskFailedRequest_1_Decode(sr stream.Reader) (*RespondActivityTaskFailedRequest, error) { var v RespondActivityTaskFailedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RespondActivityTaskFailed_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondActivityTaskFailed_Args struct could not be generated from the wire // representation. func (v *HistoryService_RespondActivityTaskFailed_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.FailRequest, err = _RespondActivityTaskFailedRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RespondActivityTaskFailed_Args // struct. func (v *HistoryService_RespondActivityTaskFailed_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.FailRequest != nil { fields[i] = fmt.Sprintf("FailRequest: %v", v.FailRequest) i++ } return fmt.Sprintf("HistoryService_RespondActivityTaskFailed_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondActivityTaskFailed_Args match the // provided HistoryService_RespondActivityTaskFailed_Args. // // This function performs a deep comparison. func (v *HistoryService_RespondActivityTaskFailed_Args) Equals(rhs *HistoryService_RespondActivityTaskFailed_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.FailRequest == nil && rhs.FailRequest == nil) || (v.FailRequest != nil && rhs.FailRequest != nil && v.FailRequest.Equals(rhs.FailRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondActivityTaskFailed_Args. func (v *HistoryService_RespondActivityTaskFailed_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FailRequest != nil { err = multierr.Append(err, enc.AddObject("failRequest", v.FailRequest)) } return err } // GetFailRequest returns the value of FailRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskFailed_Args) GetFailRequest() (o *RespondActivityTaskFailedRequest) { if v != nil && v.FailRequest != nil { return v.FailRequest } return } // IsSetFailRequest returns true if FailRequest is not nil. func (v *HistoryService_RespondActivityTaskFailed_Args) IsSetFailRequest() bool { return v != nil && v.FailRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondActivityTaskFailed" for this struct. func (v *HistoryService_RespondActivityTaskFailed_Args) MethodName() string { return "RespondActivityTaskFailed" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RespondActivityTaskFailed_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RespondActivityTaskFailed_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RespondActivityTaskFailed // function. var HistoryService_RespondActivityTaskFailed_Helper = struct { // Args accepts the parameters of RespondActivityTaskFailed in-order and returns // the arguments struct for the function. Args func( failRequest *RespondActivityTaskFailedRequest, ) *HistoryService_RespondActivityTaskFailed_Args // IsException returns true if the given error can be thrown // by RespondActivityTaskFailed. // // An error can be thrown by RespondActivityTaskFailed only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondActivityTaskFailed // given the error returned by it. The provided error may // be nil if RespondActivityTaskFailed did not fail. // // This allows mapping errors returned by RespondActivityTaskFailed into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondActivityTaskFailed // // err := RespondActivityTaskFailed(args) // result, err := HistoryService_RespondActivityTaskFailed_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondActivityTaskFailed: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_RespondActivityTaskFailed_Result, error) // UnwrapResponse takes the result struct for RespondActivityTaskFailed // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondActivityTaskFailed threw an // exception. // // result := deserialize(bytes) // err := HistoryService_RespondActivityTaskFailed_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RespondActivityTaskFailed_Result) error }{} func init() { HistoryService_RespondActivityTaskFailed_Helper.Args = func( failRequest *RespondActivityTaskFailedRequest, ) *HistoryService_RespondActivityTaskFailed_Args { return &HistoryService_RespondActivityTaskFailed_Args{ FailRequest: failRequest, } } HistoryService_RespondActivityTaskFailed_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RespondActivityTaskFailed_Helper.WrapResponse = func(err error) (*HistoryService_RespondActivityTaskFailed_Result, error) { if err == nil { return &HistoryService_RespondActivityTaskFailed_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskFailed_Result.BadRequestError") } return &HistoryService_RespondActivityTaskFailed_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskFailed_Result.InternalServiceError") } return &HistoryService_RespondActivityTaskFailed_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskFailed_Result.EntityNotExistError") } return &HistoryService_RespondActivityTaskFailed_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskFailed_Result.ShardOwnershipLostError") } return &HistoryService_RespondActivityTaskFailed_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskFailed_Result.DomainNotActiveError") } return &HistoryService_RespondActivityTaskFailed_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskFailed_Result.LimitExceededError") } return &HistoryService_RespondActivityTaskFailed_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskFailed_Result.ServiceBusyError") } return &HistoryService_RespondActivityTaskFailed_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondActivityTaskFailed_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RespondActivityTaskFailed_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RespondActivityTaskFailed_Helper.UnwrapResponse = func(result *HistoryService_RespondActivityTaskFailed_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } return } } // HistoryService_RespondActivityTaskFailed_Result represents the result of a HistoryService.RespondActivityTaskFailed function call. // // The result of a RespondActivityTaskFailed execution is sent and received over the wire as this struct. type HistoryService_RespondActivityTaskFailed_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RespondActivityTaskFailed_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondActivityTaskFailed_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_RespondActivityTaskFailed_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_RespondActivityTaskFailed_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondActivityTaskFailed_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondActivityTaskFailed_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondActivityTaskFailed_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondActivityTaskFailed_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RespondActivityTaskFailed_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondActivityTaskFailed_Result struct could not be encoded. func (v *HistoryService_RespondActivityTaskFailed_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondActivityTaskFailed_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_RespondActivityTaskFailed_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondActivityTaskFailed_Result struct could not be generated from the wire // representation. func (v *HistoryService_RespondActivityTaskFailed_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondActivityTaskFailed_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RespondActivityTaskFailed_Result // struct. func (v *HistoryService_RespondActivityTaskFailed_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RespondActivityTaskFailed_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondActivityTaskFailed_Result match the // provided HistoryService_RespondActivityTaskFailed_Result. // // This function performs a deep comparison. func (v *HistoryService_RespondActivityTaskFailed_Result) Equals(rhs *HistoryService_RespondActivityTaskFailed_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondActivityTaskFailed_Result. func (v *HistoryService_RespondActivityTaskFailed_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskFailed_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RespondActivityTaskFailed_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskFailed_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RespondActivityTaskFailed_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskFailed_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RespondActivityTaskFailed_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskFailed_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RespondActivityTaskFailed_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskFailed_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RespondActivityTaskFailed_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskFailed_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RespondActivityTaskFailed_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskFailed_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RespondActivityTaskFailed_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondActivityTaskFailed_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RespondActivityTaskFailed_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondActivityTaskFailed" for this struct. func (v *HistoryService_RespondActivityTaskFailed_Result) MethodName() string { return "RespondActivityTaskFailed" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RespondActivityTaskFailed_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RespondCrossClusterTasksCompleted_Args represents the arguments for the HistoryService.RespondCrossClusterTasksCompleted function. // // The arguments for RespondCrossClusterTasksCompleted are sent and received over the wire as this struct. type HistoryService_RespondCrossClusterTasksCompleted_Args struct { Request *shared.RespondCrossClusterTasksCompletedRequest `json:"request,omitempty"` } // ToWire translates a HistoryService_RespondCrossClusterTasksCompleted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondCrossClusterTasksCompletedRequest_Read(w wire.Value) (*shared.RespondCrossClusterTasksCompletedRequest, error) { var v shared.RespondCrossClusterTasksCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RespondCrossClusterTasksCompleted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondCrossClusterTasksCompleted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondCrossClusterTasksCompleted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _RespondCrossClusterTasksCompletedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RespondCrossClusterTasksCompleted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondCrossClusterTasksCompleted_Args struct could not be encoded. func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondCrossClusterTasksCompletedRequest_Decode(sr stream.Reader) (*shared.RespondCrossClusterTasksCompletedRequest, error) { var v shared.RespondCrossClusterTasksCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RespondCrossClusterTasksCompleted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondCrossClusterTasksCompleted_Args struct could not be generated from the wire // representation. func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _RespondCrossClusterTasksCompletedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RespondCrossClusterTasksCompleted_Args // struct. func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("HistoryService_RespondCrossClusterTasksCompleted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondCrossClusterTasksCompleted_Args match the // provided HistoryService_RespondCrossClusterTasksCompleted_Args. // // This function performs a deep comparison. func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) Equals(rhs *HistoryService_RespondCrossClusterTasksCompleted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondCrossClusterTasksCompleted_Args. func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) GetRequest() (o *shared.RespondCrossClusterTasksCompletedRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondCrossClusterTasksCompleted" for this struct. func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) MethodName() string { return "RespondCrossClusterTasksCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RespondCrossClusterTasksCompleted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RespondCrossClusterTasksCompleted_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RespondCrossClusterTasksCompleted // function. var HistoryService_RespondCrossClusterTasksCompleted_Helper = struct { // Args accepts the parameters of RespondCrossClusterTasksCompleted in-order and returns // the arguments struct for the function. Args func( request *shared.RespondCrossClusterTasksCompletedRequest, ) *HistoryService_RespondCrossClusterTasksCompleted_Args // IsException returns true if the given error can be thrown // by RespondCrossClusterTasksCompleted. // // An error can be thrown by RespondCrossClusterTasksCompleted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondCrossClusterTasksCompleted // given its return value and error. // // This allows mapping values and errors returned by // RespondCrossClusterTasksCompleted into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RespondCrossClusterTasksCompleted // // value, err := RespondCrossClusterTasksCompleted(args) // result, err := HistoryService_RespondCrossClusterTasksCompleted_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RespondCrossClusterTasksCompleted: %v", err) // } // serialize(result) WrapResponse func(*shared.RespondCrossClusterTasksCompletedResponse, error) (*HistoryService_RespondCrossClusterTasksCompleted_Result, error) // UnwrapResponse takes the result struct for RespondCrossClusterTasksCompleted // and returns the value or error returned by it. // // The error is non-nil only if RespondCrossClusterTasksCompleted threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_RespondCrossClusterTasksCompleted_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RespondCrossClusterTasksCompleted_Result) (*shared.RespondCrossClusterTasksCompletedResponse, error) }{} func init() { HistoryService_RespondCrossClusterTasksCompleted_Helper.Args = func( request *shared.RespondCrossClusterTasksCompletedRequest, ) *HistoryService_RespondCrossClusterTasksCompleted_Args { return &HistoryService_RespondCrossClusterTasksCompleted_Args{ Request: request, } } HistoryService_RespondCrossClusterTasksCompleted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *ShardOwnershipLostError: return true default: return false } } HistoryService_RespondCrossClusterTasksCompleted_Helper.WrapResponse = func(success *shared.RespondCrossClusterTasksCompletedResponse, err error) (*HistoryService_RespondCrossClusterTasksCompleted_Result, error) { if err == nil { return &HistoryService_RespondCrossClusterTasksCompleted_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondCrossClusterTasksCompleted_Result.BadRequestError") } return &HistoryService_RespondCrossClusterTasksCompleted_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondCrossClusterTasksCompleted_Result.InternalServiceError") } return &HistoryService_RespondCrossClusterTasksCompleted_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondCrossClusterTasksCompleted_Result.ServiceBusyError") } return &HistoryService_RespondCrossClusterTasksCompleted_Result{ServiceBusyError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondCrossClusterTasksCompleted_Result.ShardOwnershipLostError") } return &HistoryService_RespondCrossClusterTasksCompleted_Result{ShardOwnershipLostError: e}, nil } return nil, err } HistoryService_RespondCrossClusterTasksCompleted_Helper.UnwrapResponse = func(result *HistoryService_RespondCrossClusterTasksCompleted_Result) (success *shared.RespondCrossClusterTasksCompletedResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_RespondCrossClusterTasksCompleted_Result represents the result of a HistoryService.RespondCrossClusterTasksCompleted function call. // // The result of a RespondCrossClusterTasksCompleted execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_RespondCrossClusterTasksCompleted_Result struct { // Value returned by RespondCrossClusterTasksCompleted after a successful execution. Success *shared.RespondCrossClusterTasksCompletedResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` } // ToWire translates a HistoryService_RespondCrossClusterTasksCompleted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_RespondCrossClusterTasksCompleted_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondCrossClusterTasksCompletedResponse_Read(w wire.Value) (*shared.RespondCrossClusterTasksCompletedResponse, error) { var v shared.RespondCrossClusterTasksCompletedResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RespondCrossClusterTasksCompleted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondCrossClusterTasksCompleted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondCrossClusterTasksCompleted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RespondCrossClusterTasksCompletedResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RespondCrossClusterTasksCompleted_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RespondCrossClusterTasksCompleted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondCrossClusterTasksCompleted_Result struct could not be encoded. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RespondCrossClusterTasksCompleted_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _RespondCrossClusterTasksCompletedResponse_Decode(sr stream.Reader) (*shared.RespondCrossClusterTasksCompletedResponse, error) { var v shared.RespondCrossClusterTasksCompletedResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RespondCrossClusterTasksCompleted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondCrossClusterTasksCompleted_Result struct could not be generated from the wire // representation. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RespondCrossClusterTasksCompletedResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RespondCrossClusterTasksCompleted_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RespondCrossClusterTasksCompleted_Result // struct. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } return fmt.Sprintf("HistoryService_RespondCrossClusterTasksCompleted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondCrossClusterTasksCompleted_Result match the // provided HistoryService_RespondCrossClusterTasksCompleted_Result. // // This function performs a deep comparison. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) Equals(rhs *HistoryService_RespondCrossClusterTasksCompleted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondCrossClusterTasksCompleted_Result. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) GetSuccess() (o *shared.RespondCrossClusterTasksCompletedResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondCrossClusterTasksCompleted" for this struct. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) MethodName() string { return "RespondCrossClusterTasksCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RespondCrossClusterTasksCompleted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RespondDecisionTaskCompleted_Args represents the arguments for the HistoryService.RespondDecisionTaskCompleted function. // // The arguments for RespondDecisionTaskCompleted are sent and received over the wire as this struct. type HistoryService_RespondDecisionTaskCompleted_Args struct { CompleteRequest *RespondDecisionTaskCompletedRequest `json:"completeRequest,omitempty"` } // ToWire translates a HistoryService_RespondDecisionTaskCompleted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondDecisionTaskCompleted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CompleteRequest != nil { w, err = v.CompleteRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondDecisionTaskCompletedRequest_1_Read(w wire.Value) (*RespondDecisionTaskCompletedRequest, error) { var v RespondDecisionTaskCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RespondDecisionTaskCompleted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondDecisionTaskCompleted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondDecisionTaskCompleted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondDecisionTaskCompleted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.CompleteRequest, err = _RespondDecisionTaskCompletedRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RespondDecisionTaskCompleted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondDecisionTaskCompleted_Args struct could not be encoded. func (v *HistoryService_RespondDecisionTaskCompleted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CompleteRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.CompleteRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondDecisionTaskCompletedRequest_1_Decode(sr stream.Reader) (*RespondDecisionTaskCompletedRequest, error) { var v RespondDecisionTaskCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RespondDecisionTaskCompleted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondDecisionTaskCompleted_Args struct could not be generated from the wire // representation. func (v *HistoryService_RespondDecisionTaskCompleted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.CompleteRequest, err = _RespondDecisionTaskCompletedRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RespondDecisionTaskCompleted_Args // struct. func (v *HistoryService_RespondDecisionTaskCompleted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CompleteRequest != nil { fields[i] = fmt.Sprintf("CompleteRequest: %v", v.CompleteRequest) i++ } return fmt.Sprintf("HistoryService_RespondDecisionTaskCompleted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondDecisionTaskCompleted_Args match the // provided HistoryService_RespondDecisionTaskCompleted_Args. // // This function performs a deep comparison. func (v *HistoryService_RespondDecisionTaskCompleted_Args) Equals(rhs *HistoryService_RespondDecisionTaskCompleted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.CompleteRequest == nil && rhs.CompleteRequest == nil) || (v.CompleteRequest != nil && rhs.CompleteRequest != nil && v.CompleteRequest.Equals(rhs.CompleteRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondDecisionTaskCompleted_Args. func (v *HistoryService_RespondDecisionTaskCompleted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CompleteRequest != nil { err = multierr.Append(err, enc.AddObject("completeRequest", v.CompleteRequest)) } return err } // GetCompleteRequest returns the value of CompleteRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskCompleted_Args) GetCompleteRequest() (o *RespondDecisionTaskCompletedRequest) { if v != nil && v.CompleteRequest != nil { return v.CompleteRequest } return } // IsSetCompleteRequest returns true if CompleteRequest is not nil. func (v *HistoryService_RespondDecisionTaskCompleted_Args) IsSetCompleteRequest() bool { return v != nil && v.CompleteRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondDecisionTaskCompleted" for this struct. func (v *HistoryService_RespondDecisionTaskCompleted_Args) MethodName() string { return "RespondDecisionTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RespondDecisionTaskCompleted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RespondDecisionTaskCompleted_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RespondDecisionTaskCompleted // function. var HistoryService_RespondDecisionTaskCompleted_Helper = struct { // Args accepts the parameters of RespondDecisionTaskCompleted in-order and returns // the arguments struct for the function. Args func( completeRequest *RespondDecisionTaskCompletedRequest, ) *HistoryService_RespondDecisionTaskCompleted_Args // IsException returns true if the given error can be thrown // by RespondDecisionTaskCompleted. // // An error can be thrown by RespondDecisionTaskCompleted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondDecisionTaskCompleted // given its return value and error. // // This allows mapping values and errors returned by // RespondDecisionTaskCompleted into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by RespondDecisionTaskCompleted // // value, err := RespondDecisionTaskCompleted(args) // result, err := HistoryService_RespondDecisionTaskCompleted_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from RespondDecisionTaskCompleted: %v", err) // } // serialize(result) WrapResponse func(*RespondDecisionTaskCompletedResponse, error) (*HistoryService_RespondDecisionTaskCompleted_Result, error) // UnwrapResponse takes the result struct for RespondDecisionTaskCompleted // and returns the value or error returned by it. // // The error is non-nil only if RespondDecisionTaskCompleted threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_RespondDecisionTaskCompleted_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RespondDecisionTaskCompleted_Result) (*RespondDecisionTaskCompletedResponse, error) }{} func init() { HistoryService_RespondDecisionTaskCompleted_Helper.Args = func( completeRequest *RespondDecisionTaskCompletedRequest, ) *HistoryService_RespondDecisionTaskCompleted_Args { return &HistoryService_RespondDecisionTaskCompleted_Args{ CompleteRequest: completeRequest, } } HistoryService_RespondDecisionTaskCompleted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RespondDecisionTaskCompleted_Helper.WrapResponse = func(success *RespondDecisionTaskCompletedResponse, err error) (*HistoryService_RespondDecisionTaskCompleted_Result, error) { if err == nil { return &HistoryService_RespondDecisionTaskCompleted_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskCompleted_Result.BadRequestError") } return &HistoryService_RespondDecisionTaskCompleted_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskCompleted_Result.InternalServiceError") } return &HistoryService_RespondDecisionTaskCompleted_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskCompleted_Result.EntityNotExistError") } return &HistoryService_RespondDecisionTaskCompleted_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskCompleted_Result.ShardOwnershipLostError") } return &HistoryService_RespondDecisionTaskCompleted_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskCompleted_Result.DomainNotActiveError") } return &HistoryService_RespondDecisionTaskCompleted_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskCompleted_Result.LimitExceededError") } return &HistoryService_RespondDecisionTaskCompleted_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskCompleted_Result.ServiceBusyError") } return &HistoryService_RespondDecisionTaskCompleted_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskCompleted_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RespondDecisionTaskCompleted_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RespondDecisionTaskCompleted_Helper.UnwrapResponse = func(result *HistoryService_RespondDecisionTaskCompleted_Result) (success *RespondDecisionTaskCompletedResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_RespondDecisionTaskCompleted_Result represents the result of a HistoryService.RespondDecisionTaskCompleted function call. // // The result of a RespondDecisionTaskCompleted execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_RespondDecisionTaskCompleted_Result struct { // Value returned by RespondDecisionTaskCompleted after a successful execution. Success *RespondDecisionTaskCompletedResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RespondDecisionTaskCompleted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondDecisionTaskCompleted_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_RespondDecisionTaskCompleted_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondDecisionTaskCompletedResponse_Read(w wire.Value) (*RespondDecisionTaskCompletedResponse, error) { var v RespondDecisionTaskCompletedResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RespondDecisionTaskCompleted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondDecisionTaskCompleted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondDecisionTaskCompleted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondDecisionTaskCompleted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _RespondDecisionTaskCompletedResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RespondDecisionTaskCompleted_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RespondDecisionTaskCompleted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondDecisionTaskCompleted_Result struct could not be encoded. func (v *HistoryService_RespondDecisionTaskCompleted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RespondDecisionTaskCompleted_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _RespondDecisionTaskCompletedResponse_Decode(sr stream.Reader) (*RespondDecisionTaskCompletedResponse, error) { var v RespondDecisionTaskCompletedResponse err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RespondDecisionTaskCompleted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondDecisionTaskCompleted_Result struct could not be generated from the wire // representation. func (v *HistoryService_RespondDecisionTaskCompleted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _RespondDecisionTaskCompletedResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_RespondDecisionTaskCompleted_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RespondDecisionTaskCompleted_Result // struct. func (v *HistoryService_RespondDecisionTaskCompleted_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RespondDecisionTaskCompleted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondDecisionTaskCompleted_Result match the // provided HistoryService_RespondDecisionTaskCompleted_Result. // // This function performs a deep comparison. func (v *HistoryService_RespondDecisionTaskCompleted_Result) Equals(rhs *HistoryService_RespondDecisionTaskCompleted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondDecisionTaskCompleted_Result. func (v *HistoryService_RespondDecisionTaskCompleted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskCompleted_Result) GetSuccess() (o *RespondDecisionTaskCompletedResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_RespondDecisionTaskCompleted_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskCompleted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RespondDecisionTaskCompleted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskCompleted_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RespondDecisionTaskCompleted_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskCompleted_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RespondDecisionTaskCompleted_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskCompleted_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RespondDecisionTaskCompleted_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskCompleted_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RespondDecisionTaskCompleted_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskCompleted_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RespondDecisionTaskCompleted_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskCompleted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RespondDecisionTaskCompleted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskCompleted_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RespondDecisionTaskCompleted_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondDecisionTaskCompleted" for this struct. func (v *HistoryService_RespondDecisionTaskCompleted_Result) MethodName() string { return "RespondDecisionTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RespondDecisionTaskCompleted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_RespondDecisionTaskFailed_Args represents the arguments for the HistoryService.RespondDecisionTaskFailed function. // // The arguments for RespondDecisionTaskFailed are sent and received over the wire as this struct. type HistoryService_RespondDecisionTaskFailed_Args struct { FailedRequest *RespondDecisionTaskFailedRequest `json:"failedRequest,omitempty"` } // ToWire translates a HistoryService_RespondDecisionTaskFailed_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondDecisionTaskFailed_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.FailedRequest != nil { w, err = v.FailedRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondDecisionTaskFailedRequest_1_Read(w wire.Value) (*RespondDecisionTaskFailedRequest, error) { var v RespondDecisionTaskFailedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_RespondDecisionTaskFailed_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondDecisionTaskFailed_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondDecisionTaskFailed_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondDecisionTaskFailed_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.FailedRequest, err = _RespondDecisionTaskFailedRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_RespondDecisionTaskFailed_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondDecisionTaskFailed_Args struct could not be encoded. func (v *HistoryService_RespondDecisionTaskFailed_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FailedRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.FailedRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondDecisionTaskFailedRequest_1_Decode(sr stream.Reader) (*RespondDecisionTaskFailedRequest, error) { var v RespondDecisionTaskFailedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_RespondDecisionTaskFailed_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondDecisionTaskFailed_Args struct could not be generated from the wire // representation. func (v *HistoryService_RespondDecisionTaskFailed_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.FailedRequest, err = _RespondDecisionTaskFailedRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_RespondDecisionTaskFailed_Args // struct. func (v *HistoryService_RespondDecisionTaskFailed_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.FailedRequest != nil { fields[i] = fmt.Sprintf("FailedRequest: %v", v.FailedRequest) i++ } return fmt.Sprintf("HistoryService_RespondDecisionTaskFailed_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondDecisionTaskFailed_Args match the // provided HistoryService_RespondDecisionTaskFailed_Args. // // This function performs a deep comparison. func (v *HistoryService_RespondDecisionTaskFailed_Args) Equals(rhs *HistoryService_RespondDecisionTaskFailed_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.FailedRequest == nil && rhs.FailedRequest == nil) || (v.FailedRequest != nil && rhs.FailedRequest != nil && v.FailedRequest.Equals(rhs.FailedRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondDecisionTaskFailed_Args. func (v *HistoryService_RespondDecisionTaskFailed_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FailedRequest != nil { err = multierr.Append(err, enc.AddObject("failedRequest", v.FailedRequest)) } return err } // GetFailedRequest returns the value of FailedRequest if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskFailed_Args) GetFailedRequest() (o *RespondDecisionTaskFailedRequest) { if v != nil && v.FailedRequest != nil { return v.FailedRequest } return } // IsSetFailedRequest returns true if FailedRequest is not nil. func (v *HistoryService_RespondDecisionTaskFailed_Args) IsSetFailedRequest() bool { return v != nil && v.FailedRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondDecisionTaskFailed" for this struct. func (v *HistoryService_RespondDecisionTaskFailed_Args) MethodName() string { return "RespondDecisionTaskFailed" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_RespondDecisionTaskFailed_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_RespondDecisionTaskFailed_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.RespondDecisionTaskFailed // function. var HistoryService_RespondDecisionTaskFailed_Helper = struct { // Args accepts the parameters of RespondDecisionTaskFailed in-order and returns // the arguments struct for the function. Args func( failedRequest *RespondDecisionTaskFailedRequest, ) *HistoryService_RespondDecisionTaskFailed_Args // IsException returns true if the given error can be thrown // by RespondDecisionTaskFailed. // // An error can be thrown by RespondDecisionTaskFailed only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondDecisionTaskFailed // given the error returned by it. The provided error may // be nil if RespondDecisionTaskFailed did not fail. // // This allows mapping errors returned by RespondDecisionTaskFailed into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondDecisionTaskFailed // // err := RespondDecisionTaskFailed(args) // result, err := HistoryService_RespondDecisionTaskFailed_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondDecisionTaskFailed: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_RespondDecisionTaskFailed_Result, error) // UnwrapResponse takes the result struct for RespondDecisionTaskFailed // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondDecisionTaskFailed threw an // exception. // // result := deserialize(bytes) // err := HistoryService_RespondDecisionTaskFailed_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_RespondDecisionTaskFailed_Result) error }{} func init() { HistoryService_RespondDecisionTaskFailed_Helper.Args = func( failedRequest *RespondDecisionTaskFailedRequest, ) *HistoryService_RespondDecisionTaskFailed_Args { return &HistoryService_RespondDecisionTaskFailed_Args{ FailedRequest: failedRequest, } } HistoryService_RespondDecisionTaskFailed_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_RespondDecisionTaskFailed_Helper.WrapResponse = func(err error) (*HistoryService_RespondDecisionTaskFailed_Result, error) { if err == nil { return &HistoryService_RespondDecisionTaskFailed_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskFailed_Result.BadRequestError") } return &HistoryService_RespondDecisionTaskFailed_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskFailed_Result.InternalServiceError") } return &HistoryService_RespondDecisionTaskFailed_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskFailed_Result.EntityNotExistError") } return &HistoryService_RespondDecisionTaskFailed_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskFailed_Result.ShardOwnershipLostError") } return &HistoryService_RespondDecisionTaskFailed_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskFailed_Result.DomainNotActiveError") } return &HistoryService_RespondDecisionTaskFailed_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskFailed_Result.LimitExceededError") } return &HistoryService_RespondDecisionTaskFailed_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskFailed_Result.ServiceBusyError") } return &HistoryService_RespondDecisionTaskFailed_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_RespondDecisionTaskFailed_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_RespondDecisionTaskFailed_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_RespondDecisionTaskFailed_Helper.UnwrapResponse = func(result *HistoryService_RespondDecisionTaskFailed_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } return } } // HistoryService_RespondDecisionTaskFailed_Result represents the result of a HistoryService.RespondDecisionTaskFailed function call. // // The result of a RespondDecisionTaskFailed execution is sent and received over the wire as this struct. type HistoryService_RespondDecisionTaskFailed_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_RespondDecisionTaskFailed_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_RespondDecisionTaskFailed_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_RespondDecisionTaskFailed_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_RespondDecisionTaskFailed_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_RespondDecisionTaskFailed_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_RespondDecisionTaskFailed_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_RespondDecisionTaskFailed_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondDecisionTaskFailed_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_RespondDecisionTaskFailed_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_RespondDecisionTaskFailed_Result struct could not be encoded. func (v *HistoryService_RespondDecisionTaskFailed_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondDecisionTaskFailed_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_RespondDecisionTaskFailed_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_RespondDecisionTaskFailed_Result struct could not be generated from the wire // representation. func (v *HistoryService_RespondDecisionTaskFailed_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_RespondDecisionTaskFailed_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_RespondDecisionTaskFailed_Result // struct. func (v *HistoryService_RespondDecisionTaskFailed_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_RespondDecisionTaskFailed_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_RespondDecisionTaskFailed_Result match the // provided HistoryService_RespondDecisionTaskFailed_Result. // // This function performs a deep comparison. func (v *HistoryService_RespondDecisionTaskFailed_Result) Equals(rhs *HistoryService_RespondDecisionTaskFailed_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_RespondDecisionTaskFailed_Result. func (v *HistoryService_RespondDecisionTaskFailed_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskFailed_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_RespondDecisionTaskFailed_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskFailed_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_RespondDecisionTaskFailed_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskFailed_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_RespondDecisionTaskFailed_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskFailed_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_RespondDecisionTaskFailed_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskFailed_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_RespondDecisionTaskFailed_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskFailed_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_RespondDecisionTaskFailed_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskFailed_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_RespondDecisionTaskFailed_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_RespondDecisionTaskFailed_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_RespondDecisionTaskFailed_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondDecisionTaskFailed" for this struct. func (v *HistoryService_RespondDecisionTaskFailed_Result) MethodName() string { return "RespondDecisionTaskFailed" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_RespondDecisionTaskFailed_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_ScheduleDecisionTask_Args represents the arguments for the HistoryService.ScheduleDecisionTask function. // // The arguments for ScheduleDecisionTask are sent and received over the wire as this struct. type HistoryService_ScheduleDecisionTask_Args struct { ScheduleRequest *ScheduleDecisionTaskRequest `json:"scheduleRequest,omitempty"` } // ToWire translates a HistoryService_ScheduleDecisionTask_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ScheduleDecisionTask_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ScheduleRequest != nil { w, err = v.ScheduleRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ScheduleDecisionTaskRequest_Read(w wire.Value) (*ScheduleDecisionTaskRequest, error) { var v ScheduleDecisionTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_ScheduleDecisionTask_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ScheduleDecisionTask_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ScheduleDecisionTask_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ScheduleDecisionTask_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ScheduleRequest, err = _ScheduleDecisionTaskRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_ScheduleDecisionTask_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ScheduleDecisionTask_Args struct could not be encoded. func (v *HistoryService_ScheduleDecisionTask_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ScheduleRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ScheduleRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ScheduleDecisionTaskRequest_Decode(sr stream.Reader) (*ScheduleDecisionTaskRequest, error) { var v ScheduleDecisionTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_ScheduleDecisionTask_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ScheduleDecisionTask_Args struct could not be generated from the wire // representation. func (v *HistoryService_ScheduleDecisionTask_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ScheduleRequest, err = _ScheduleDecisionTaskRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_ScheduleDecisionTask_Args // struct. func (v *HistoryService_ScheduleDecisionTask_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ScheduleRequest != nil { fields[i] = fmt.Sprintf("ScheduleRequest: %v", v.ScheduleRequest) i++ } return fmt.Sprintf("HistoryService_ScheduleDecisionTask_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ScheduleDecisionTask_Args match the // provided HistoryService_ScheduleDecisionTask_Args. // // This function performs a deep comparison. func (v *HistoryService_ScheduleDecisionTask_Args) Equals(rhs *HistoryService_ScheduleDecisionTask_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ScheduleRequest == nil && rhs.ScheduleRequest == nil) || (v.ScheduleRequest != nil && rhs.ScheduleRequest != nil && v.ScheduleRequest.Equals(rhs.ScheduleRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ScheduleDecisionTask_Args. func (v *HistoryService_ScheduleDecisionTask_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ScheduleRequest != nil { err = multierr.Append(err, enc.AddObject("scheduleRequest", v.ScheduleRequest)) } return err } // GetScheduleRequest returns the value of ScheduleRequest if it is set or its // zero value if it is unset. func (v *HistoryService_ScheduleDecisionTask_Args) GetScheduleRequest() (o *ScheduleDecisionTaskRequest) { if v != nil && v.ScheduleRequest != nil { return v.ScheduleRequest } return } // IsSetScheduleRequest returns true if ScheduleRequest is not nil. func (v *HistoryService_ScheduleDecisionTask_Args) IsSetScheduleRequest() bool { return v != nil && v.ScheduleRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ScheduleDecisionTask" for this struct. func (v *HistoryService_ScheduleDecisionTask_Args) MethodName() string { return "ScheduleDecisionTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_ScheduleDecisionTask_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_ScheduleDecisionTask_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.ScheduleDecisionTask // function. var HistoryService_ScheduleDecisionTask_Helper = struct { // Args accepts the parameters of ScheduleDecisionTask in-order and returns // the arguments struct for the function. Args func( scheduleRequest *ScheduleDecisionTaskRequest, ) *HistoryService_ScheduleDecisionTask_Args // IsException returns true if the given error can be thrown // by ScheduleDecisionTask. // // An error can be thrown by ScheduleDecisionTask only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ScheduleDecisionTask // given the error returned by it. The provided error may // be nil if ScheduleDecisionTask did not fail. // // This allows mapping errors returned by ScheduleDecisionTask into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // ScheduleDecisionTask // // err := ScheduleDecisionTask(args) // result, err := HistoryService_ScheduleDecisionTask_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from ScheduleDecisionTask: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_ScheduleDecisionTask_Result, error) // UnwrapResponse takes the result struct for ScheduleDecisionTask // and returns the erorr returned by it (if any). // // The error is non-nil only if ScheduleDecisionTask threw an // exception. // // result := deserialize(bytes) // err := HistoryService_ScheduleDecisionTask_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_ScheduleDecisionTask_Result) error }{} func init() { HistoryService_ScheduleDecisionTask_Helper.Args = func( scheduleRequest *ScheduleDecisionTaskRequest, ) *HistoryService_ScheduleDecisionTask_Args { return &HistoryService_ScheduleDecisionTask_Args{ ScheduleRequest: scheduleRequest, } } HistoryService_ScheduleDecisionTask_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_ScheduleDecisionTask_Helper.WrapResponse = func(err error) (*HistoryService_ScheduleDecisionTask_Result, error) { if err == nil { return &HistoryService_ScheduleDecisionTask_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ScheduleDecisionTask_Result.BadRequestError") } return &HistoryService_ScheduleDecisionTask_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ScheduleDecisionTask_Result.InternalServiceError") } return &HistoryService_ScheduleDecisionTask_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ScheduleDecisionTask_Result.EntityNotExistError") } return &HistoryService_ScheduleDecisionTask_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ScheduleDecisionTask_Result.ShardOwnershipLostError") } return &HistoryService_ScheduleDecisionTask_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ScheduleDecisionTask_Result.DomainNotActiveError") } return &HistoryService_ScheduleDecisionTask_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ScheduleDecisionTask_Result.LimitExceededError") } return &HistoryService_ScheduleDecisionTask_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ScheduleDecisionTask_Result.ServiceBusyError") } return &HistoryService_ScheduleDecisionTask_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_ScheduleDecisionTask_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_ScheduleDecisionTask_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_ScheduleDecisionTask_Helper.UnwrapResponse = func(result *HistoryService_ScheduleDecisionTask_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } return } } // HistoryService_ScheduleDecisionTask_Result represents the result of a HistoryService.ScheduleDecisionTask function call. // // The result of a ScheduleDecisionTask execution is sent and received over the wire as this struct. type HistoryService_ScheduleDecisionTask_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_ScheduleDecisionTask_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_ScheduleDecisionTask_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_ScheduleDecisionTask_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_ScheduleDecisionTask_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_ScheduleDecisionTask_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_ScheduleDecisionTask_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_ScheduleDecisionTask_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ScheduleDecisionTask_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_ScheduleDecisionTask_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_ScheduleDecisionTask_Result struct could not be encoded. func (v *HistoryService_ScheduleDecisionTask_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ScheduleDecisionTask_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_ScheduleDecisionTask_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_ScheduleDecisionTask_Result struct could not be generated from the wire // representation. func (v *HistoryService_ScheduleDecisionTask_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_ScheduleDecisionTask_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_ScheduleDecisionTask_Result // struct. func (v *HistoryService_ScheduleDecisionTask_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_ScheduleDecisionTask_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_ScheduleDecisionTask_Result match the // provided HistoryService_ScheduleDecisionTask_Result. // // This function performs a deep comparison. func (v *HistoryService_ScheduleDecisionTask_Result) Equals(rhs *HistoryService_ScheduleDecisionTask_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_ScheduleDecisionTask_Result. func (v *HistoryService_ScheduleDecisionTask_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_ScheduleDecisionTask_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_ScheduleDecisionTask_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_ScheduleDecisionTask_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_ScheduleDecisionTask_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_ScheduleDecisionTask_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_ScheduleDecisionTask_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_ScheduleDecisionTask_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_ScheduleDecisionTask_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_ScheduleDecisionTask_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_ScheduleDecisionTask_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_ScheduleDecisionTask_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_ScheduleDecisionTask_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_ScheduleDecisionTask_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_ScheduleDecisionTask_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_ScheduleDecisionTask_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_ScheduleDecisionTask_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ScheduleDecisionTask" for this struct. func (v *HistoryService_ScheduleDecisionTask_Result) MethodName() string { return "ScheduleDecisionTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_ScheduleDecisionTask_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_SignalWithStartWorkflowExecution_Args represents the arguments for the HistoryService.SignalWithStartWorkflowExecution function. // // The arguments for SignalWithStartWorkflowExecution are sent and received over the wire as this struct. type HistoryService_SignalWithStartWorkflowExecution_Args struct { SignalWithStartRequest *SignalWithStartWorkflowExecutionRequest `json:"signalWithStartRequest,omitempty"` } // ToWire translates a HistoryService_SignalWithStartWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_SignalWithStartWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.SignalWithStartRequest != nil { w, err = v.SignalWithStartRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SignalWithStartWorkflowExecutionRequest_1_Read(w wire.Value) (*SignalWithStartWorkflowExecutionRequest, error) { var v SignalWithStartWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_SignalWithStartWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_SignalWithStartWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_SignalWithStartWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_SignalWithStartWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.SignalWithStartRequest, err = _SignalWithStartWorkflowExecutionRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_SignalWithStartWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_SignalWithStartWorkflowExecution_Args struct could not be encoded. func (v *HistoryService_SignalWithStartWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SignalWithStartRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.SignalWithStartRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SignalWithStartWorkflowExecutionRequest_1_Decode(sr stream.Reader) (*SignalWithStartWorkflowExecutionRequest, error) { var v SignalWithStartWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_SignalWithStartWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_SignalWithStartWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *HistoryService_SignalWithStartWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.SignalWithStartRequest, err = _SignalWithStartWorkflowExecutionRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_SignalWithStartWorkflowExecution_Args // struct. func (v *HistoryService_SignalWithStartWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.SignalWithStartRequest != nil { fields[i] = fmt.Sprintf("SignalWithStartRequest: %v", v.SignalWithStartRequest) i++ } return fmt.Sprintf("HistoryService_SignalWithStartWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_SignalWithStartWorkflowExecution_Args match the // provided HistoryService_SignalWithStartWorkflowExecution_Args. // // This function performs a deep comparison. func (v *HistoryService_SignalWithStartWorkflowExecution_Args) Equals(rhs *HistoryService_SignalWithStartWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SignalWithStartRequest == nil && rhs.SignalWithStartRequest == nil) || (v.SignalWithStartRequest != nil && rhs.SignalWithStartRequest != nil && v.SignalWithStartRequest.Equals(rhs.SignalWithStartRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_SignalWithStartWorkflowExecution_Args. func (v *HistoryService_SignalWithStartWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SignalWithStartRequest != nil { err = multierr.Append(err, enc.AddObject("signalWithStartRequest", v.SignalWithStartRequest)) } return err } // GetSignalWithStartRequest returns the value of SignalWithStartRequest if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWithStartWorkflowExecution_Args) GetSignalWithStartRequest() (o *SignalWithStartWorkflowExecutionRequest) { if v != nil && v.SignalWithStartRequest != nil { return v.SignalWithStartRequest } return } // IsSetSignalWithStartRequest returns true if SignalWithStartRequest is not nil. func (v *HistoryService_SignalWithStartWorkflowExecution_Args) IsSetSignalWithStartRequest() bool { return v != nil && v.SignalWithStartRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "SignalWithStartWorkflowExecution" for this struct. func (v *HistoryService_SignalWithStartWorkflowExecution_Args) MethodName() string { return "SignalWithStartWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_SignalWithStartWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_SignalWithStartWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.SignalWithStartWorkflowExecution // function. var HistoryService_SignalWithStartWorkflowExecution_Helper = struct { // Args accepts the parameters of SignalWithStartWorkflowExecution in-order and returns // the arguments struct for the function. Args func( signalWithStartRequest *SignalWithStartWorkflowExecutionRequest, ) *HistoryService_SignalWithStartWorkflowExecution_Args // IsException returns true if the given error can be thrown // by SignalWithStartWorkflowExecution. // // An error can be thrown by SignalWithStartWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for SignalWithStartWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // SignalWithStartWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by SignalWithStartWorkflowExecution // // value, err := SignalWithStartWorkflowExecution(args) // result, err := HistoryService_SignalWithStartWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from SignalWithStartWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*shared.StartWorkflowExecutionResponse, error) (*HistoryService_SignalWithStartWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for SignalWithStartWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if SignalWithStartWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_SignalWithStartWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_SignalWithStartWorkflowExecution_Result) (*shared.StartWorkflowExecutionResponse, error) }{} func init() { HistoryService_SignalWithStartWorkflowExecution_Helper.Args = func( signalWithStartRequest *SignalWithStartWorkflowExecutionRequest, ) *HistoryService_SignalWithStartWorkflowExecution_Args { return &HistoryService_SignalWithStartWorkflowExecution_Args{ SignalWithStartRequest: signalWithStartRequest, } } HistoryService_SignalWithStartWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyStartedError: return true default: return false } } HistoryService_SignalWithStartWorkflowExecution_Helper.WrapResponse = func(success *shared.StartWorkflowExecutionResponse, err error) (*HistoryService_SignalWithStartWorkflowExecution_Result, error) { if err == nil { return &HistoryService_SignalWithStartWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWithStartWorkflowExecution_Result.BadRequestError") } return &HistoryService_SignalWithStartWorkflowExecution_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWithStartWorkflowExecution_Result.InternalServiceError") } return &HistoryService_SignalWithStartWorkflowExecution_Result{InternalServiceError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWithStartWorkflowExecution_Result.ShardOwnershipLostError") } return &HistoryService_SignalWithStartWorkflowExecution_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWithStartWorkflowExecution_Result.DomainNotActiveError") } return &HistoryService_SignalWithStartWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWithStartWorkflowExecution_Result.LimitExceededError") } return &HistoryService_SignalWithStartWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWithStartWorkflowExecution_Result.ServiceBusyError") } return &HistoryService_SignalWithStartWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyStartedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWithStartWorkflowExecution_Result.WorkflowAlreadyStartedError") } return &HistoryService_SignalWithStartWorkflowExecution_Result{WorkflowAlreadyStartedError: e}, nil } return nil, err } HistoryService_SignalWithStartWorkflowExecution_Helper.UnwrapResponse = func(result *HistoryService_SignalWithStartWorkflowExecution_Result) (success *shared.StartWorkflowExecutionResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowAlreadyStartedError != nil { err = result.WorkflowAlreadyStartedError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_SignalWithStartWorkflowExecution_Result represents the result of a HistoryService.SignalWithStartWorkflowExecution function call. // // The result of a SignalWithStartWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_SignalWithStartWorkflowExecution_Result struct { // Value returned by SignalWithStartWorkflowExecution after a successful execution. Success *shared.StartWorkflowExecutionResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowAlreadyStartedError *shared.WorkflowExecutionAlreadyStartedError `json:"workflowAlreadyStartedError,omitempty"` } // ToWire translates a HistoryService_SignalWithStartWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_SignalWithStartWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.WorkflowAlreadyStartedError != nil { w, err = v.WorkflowAlreadyStartedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_SignalWithStartWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StartWorkflowExecutionResponse_Read(w wire.Value) (*shared.StartWorkflowExecutionResponse, error) { var v shared.StartWorkflowExecutionResponse err := v.FromWire(w) return &v, err } func _WorkflowExecutionAlreadyStartedError_Read(w wire.Value) (*shared.WorkflowExecutionAlreadyStartedError, error) { var v shared.WorkflowExecutionAlreadyStartedError err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_SignalWithStartWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_SignalWithStartWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_SignalWithStartWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_SignalWithStartWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _StartWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.WorkflowAlreadyStartedError, err = _WorkflowExecutionAlreadyStartedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowAlreadyStartedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_SignalWithStartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_SignalWithStartWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_SignalWithStartWorkflowExecution_Result struct could not be encoded. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowAlreadyStartedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowAlreadyStartedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowAlreadyStartedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_SignalWithStartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _StartWorkflowExecutionResponse_Decode(sr stream.Reader) (*shared.StartWorkflowExecutionResponse, error) { var v shared.StartWorkflowExecutionResponse err := v.Decode(sr) return &v, err } func _WorkflowExecutionAlreadyStartedError_Decode(sr stream.Reader) (*shared.WorkflowExecutionAlreadyStartedError, error) { var v shared.WorkflowExecutionAlreadyStartedError err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_SignalWithStartWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_SignalWithStartWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _StartWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.WorkflowAlreadyStartedError, err = _WorkflowExecutionAlreadyStartedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowAlreadyStartedError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_SignalWithStartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_SignalWithStartWorkflowExecution_Result // struct. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowAlreadyStartedError != nil { fields[i] = fmt.Sprintf("WorkflowAlreadyStartedError: %v", v.WorkflowAlreadyStartedError) i++ } return fmt.Sprintf("HistoryService_SignalWithStartWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_SignalWithStartWorkflowExecution_Result match the // provided HistoryService_SignalWithStartWorkflowExecution_Result. // // This function performs a deep comparison. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) Equals(rhs *HistoryService_SignalWithStartWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowAlreadyStartedError == nil && rhs.WorkflowAlreadyStartedError == nil) || (v.WorkflowAlreadyStartedError != nil && rhs.WorkflowAlreadyStartedError != nil && v.WorkflowAlreadyStartedError.Equals(rhs.WorkflowAlreadyStartedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_SignalWithStartWorkflowExecution_Result. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowAlreadyStartedError != nil { err = multierr.Append(err, enc.AddObject("workflowAlreadyStartedError", v.WorkflowAlreadyStartedError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) GetSuccess() (o *shared.StartWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowAlreadyStartedError returns the value of WorkflowAlreadyStartedError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) GetWorkflowAlreadyStartedError() (o *shared.WorkflowExecutionAlreadyStartedError) { if v != nil && v.WorkflowAlreadyStartedError != nil { return v.WorkflowAlreadyStartedError } return } // IsSetWorkflowAlreadyStartedError returns true if WorkflowAlreadyStartedError is not nil. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) IsSetWorkflowAlreadyStartedError() bool { return v != nil && v.WorkflowAlreadyStartedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "SignalWithStartWorkflowExecution" for this struct. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) MethodName() string { return "SignalWithStartWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_SignalWithStartWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_SignalWorkflowExecution_Args represents the arguments for the HistoryService.SignalWorkflowExecution function. // // The arguments for SignalWorkflowExecution are sent and received over the wire as this struct. type HistoryService_SignalWorkflowExecution_Args struct { SignalRequest *SignalWorkflowExecutionRequest `json:"signalRequest,omitempty"` } // ToWire translates a HistoryService_SignalWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_SignalWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.SignalRequest != nil { w, err = v.SignalRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SignalWorkflowExecutionRequest_1_Read(w wire.Value) (*SignalWorkflowExecutionRequest, error) { var v SignalWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_SignalWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_SignalWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_SignalWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_SignalWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.SignalRequest, err = _SignalWorkflowExecutionRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_SignalWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_SignalWorkflowExecution_Args struct could not be encoded. func (v *HistoryService_SignalWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SignalRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.SignalRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SignalWorkflowExecutionRequest_1_Decode(sr stream.Reader) (*SignalWorkflowExecutionRequest, error) { var v SignalWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_SignalWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_SignalWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *HistoryService_SignalWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.SignalRequest, err = _SignalWorkflowExecutionRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_SignalWorkflowExecution_Args // struct. func (v *HistoryService_SignalWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.SignalRequest != nil { fields[i] = fmt.Sprintf("SignalRequest: %v", v.SignalRequest) i++ } return fmt.Sprintf("HistoryService_SignalWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_SignalWorkflowExecution_Args match the // provided HistoryService_SignalWorkflowExecution_Args. // // This function performs a deep comparison. func (v *HistoryService_SignalWorkflowExecution_Args) Equals(rhs *HistoryService_SignalWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SignalRequest == nil && rhs.SignalRequest == nil) || (v.SignalRequest != nil && rhs.SignalRequest != nil && v.SignalRequest.Equals(rhs.SignalRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_SignalWorkflowExecution_Args. func (v *HistoryService_SignalWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SignalRequest != nil { err = multierr.Append(err, enc.AddObject("signalRequest", v.SignalRequest)) } return err } // GetSignalRequest returns the value of SignalRequest if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWorkflowExecution_Args) GetSignalRequest() (o *SignalWorkflowExecutionRequest) { if v != nil && v.SignalRequest != nil { return v.SignalRequest } return } // IsSetSignalRequest returns true if SignalRequest is not nil. func (v *HistoryService_SignalWorkflowExecution_Args) IsSetSignalRequest() bool { return v != nil && v.SignalRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "SignalWorkflowExecution" for this struct. func (v *HistoryService_SignalWorkflowExecution_Args) MethodName() string { return "SignalWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_SignalWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_SignalWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.SignalWorkflowExecution // function. var HistoryService_SignalWorkflowExecution_Helper = struct { // Args accepts the parameters of SignalWorkflowExecution in-order and returns // the arguments struct for the function. Args func( signalRequest *SignalWorkflowExecutionRequest, ) *HistoryService_SignalWorkflowExecution_Args // IsException returns true if the given error can be thrown // by SignalWorkflowExecution. // // An error can be thrown by SignalWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for SignalWorkflowExecution // given the error returned by it. The provided error may // be nil if SignalWorkflowExecution did not fail. // // This allows mapping errors returned by SignalWorkflowExecution into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // SignalWorkflowExecution // // err := SignalWorkflowExecution(args) // result, err := HistoryService_SignalWorkflowExecution_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from SignalWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_SignalWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for SignalWorkflowExecution // and returns the erorr returned by it (if any). // // The error is non-nil only if SignalWorkflowExecution threw an // exception. // // result := deserialize(bytes) // err := HistoryService_SignalWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_SignalWorkflowExecution_Result) error }{} func init() { HistoryService_SignalWorkflowExecution_Helper.Args = func( signalRequest *SignalWorkflowExecutionRequest, ) *HistoryService_SignalWorkflowExecution_Args { return &HistoryService_SignalWorkflowExecution_Args{ SignalRequest: signalRequest, } } HistoryService_SignalWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.ServiceBusyError: return true case *shared.LimitExceededError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_SignalWorkflowExecution_Helper.WrapResponse = func(err error) (*HistoryService_SignalWorkflowExecution_Result, error) { if err == nil { return &HistoryService_SignalWorkflowExecution_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWorkflowExecution_Result.BadRequestError") } return &HistoryService_SignalWorkflowExecution_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWorkflowExecution_Result.InternalServiceError") } return &HistoryService_SignalWorkflowExecution_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWorkflowExecution_Result.EntityNotExistError") } return &HistoryService_SignalWorkflowExecution_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWorkflowExecution_Result.ShardOwnershipLostError") } return &HistoryService_SignalWorkflowExecution_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWorkflowExecution_Result.DomainNotActiveError") } return &HistoryService_SignalWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWorkflowExecution_Result.ServiceBusyError") } return &HistoryService_SignalWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWorkflowExecution_Result.LimitExceededError") } return &HistoryService_SignalWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SignalWorkflowExecution_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_SignalWorkflowExecution_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_SignalWorkflowExecution_Helper.UnwrapResponse = func(result *HistoryService_SignalWorkflowExecution_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } return } } // HistoryService_SignalWorkflowExecution_Result represents the result of a HistoryService.SignalWorkflowExecution function call. // // The result of a SignalWorkflowExecution execution is sent and received over the wire as this struct. type HistoryService_SignalWorkflowExecution_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_SignalWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_SignalWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_SignalWorkflowExecution_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_SignalWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_SignalWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_SignalWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_SignalWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_SignalWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_SignalWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_SignalWorkflowExecution_Result struct could not be encoded. func (v *HistoryService_SignalWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_SignalWorkflowExecution_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_SignalWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_SignalWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *HistoryService_SignalWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_SignalWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_SignalWorkflowExecution_Result // struct. func (v *HistoryService_SignalWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_SignalWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_SignalWorkflowExecution_Result match the // provided HistoryService_SignalWorkflowExecution_Result. // // This function performs a deep comparison. func (v *HistoryService_SignalWorkflowExecution_Result) Equals(rhs *HistoryService_SignalWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_SignalWorkflowExecution_Result. func (v *HistoryService_SignalWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_SignalWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWorkflowExecution_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_SignalWorkflowExecution_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_SignalWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWorkflowExecution_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_SignalWorkflowExecution_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_SignalWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_SignalWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_SignalWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_SignalWorkflowExecution_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_SignalWorkflowExecution_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "SignalWorkflowExecution" for this struct. func (v *HistoryService_SignalWorkflowExecution_Result) MethodName() string { return "SignalWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_SignalWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_StartWorkflowExecution_Args represents the arguments for the HistoryService.StartWorkflowExecution function. // // The arguments for StartWorkflowExecution are sent and received over the wire as this struct. type HistoryService_StartWorkflowExecution_Args struct { StartRequest *StartWorkflowExecutionRequest `json:"startRequest,omitempty"` } // ToWire translates a HistoryService_StartWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_StartWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.StartRequest != nil { w, err = v.StartRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StartWorkflowExecutionRequest_1_Read(w wire.Value) (*StartWorkflowExecutionRequest, error) { var v StartWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_StartWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_StartWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_StartWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_StartWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.StartRequest, err = _StartWorkflowExecutionRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_StartWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_StartWorkflowExecution_Args struct could not be encoded. func (v *HistoryService_StartWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.StartRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.StartRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _StartWorkflowExecutionRequest_1_Decode(sr stream.Reader) (*StartWorkflowExecutionRequest, error) { var v StartWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_StartWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_StartWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *HistoryService_StartWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.StartRequest, err = _StartWorkflowExecutionRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_StartWorkflowExecution_Args // struct. func (v *HistoryService_StartWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.StartRequest != nil { fields[i] = fmt.Sprintf("StartRequest: %v", v.StartRequest) i++ } return fmt.Sprintf("HistoryService_StartWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_StartWorkflowExecution_Args match the // provided HistoryService_StartWorkflowExecution_Args. // // This function performs a deep comparison. func (v *HistoryService_StartWorkflowExecution_Args) Equals(rhs *HistoryService_StartWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.StartRequest == nil && rhs.StartRequest == nil) || (v.StartRequest != nil && rhs.StartRequest != nil && v.StartRequest.Equals(rhs.StartRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_StartWorkflowExecution_Args. func (v *HistoryService_StartWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.StartRequest != nil { err = multierr.Append(err, enc.AddObject("startRequest", v.StartRequest)) } return err } // GetStartRequest returns the value of StartRequest if it is set or its // zero value if it is unset. func (v *HistoryService_StartWorkflowExecution_Args) GetStartRequest() (o *StartWorkflowExecutionRequest) { if v != nil && v.StartRequest != nil { return v.StartRequest } return } // IsSetStartRequest returns true if StartRequest is not nil. func (v *HistoryService_StartWorkflowExecution_Args) IsSetStartRequest() bool { return v != nil && v.StartRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "StartWorkflowExecution" for this struct. func (v *HistoryService_StartWorkflowExecution_Args) MethodName() string { return "StartWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_StartWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_StartWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.StartWorkflowExecution // function. var HistoryService_StartWorkflowExecution_Helper = struct { // Args accepts the parameters of StartWorkflowExecution in-order and returns // the arguments struct for the function. Args func( startRequest *StartWorkflowExecutionRequest, ) *HistoryService_StartWorkflowExecution_Args // IsException returns true if the given error can be thrown // by StartWorkflowExecution. // // An error can be thrown by StartWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for StartWorkflowExecution // given its return value and error. // // This allows mapping values and errors returned by // StartWorkflowExecution into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by StartWorkflowExecution // // value, err := StartWorkflowExecution(args) // result, err := HistoryService_StartWorkflowExecution_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from StartWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(*shared.StartWorkflowExecutionResponse, error) (*HistoryService_StartWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for StartWorkflowExecution // and returns the value or error returned by it. // // The error is non-nil only if StartWorkflowExecution threw an // exception. // // result := deserialize(bytes) // value, err := HistoryService_StartWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_StartWorkflowExecution_Result) (*shared.StartWorkflowExecutionResponse, error) }{} func init() { HistoryService_StartWorkflowExecution_Helper.Args = func( startRequest *StartWorkflowExecutionRequest, ) *HistoryService_StartWorkflowExecution_Args { return &HistoryService_StartWorkflowExecution_Args{ StartRequest: startRequest, } } HistoryService_StartWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.WorkflowExecutionAlreadyStartedError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true default: return false } } HistoryService_StartWorkflowExecution_Helper.WrapResponse = func(success *shared.StartWorkflowExecutionResponse, err error) (*HistoryService_StartWorkflowExecution_Result, error) { if err == nil { return &HistoryService_StartWorkflowExecution_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_StartWorkflowExecution_Result.BadRequestError") } return &HistoryService_StartWorkflowExecution_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_StartWorkflowExecution_Result.InternalServiceError") } return &HistoryService_StartWorkflowExecution_Result{InternalServiceError: e}, nil case *shared.WorkflowExecutionAlreadyStartedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_StartWorkflowExecution_Result.SessionAlreadyExistError") } return &HistoryService_StartWorkflowExecution_Result{SessionAlreadyExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_StartWorkflowExecution_Result.ShardOwnershipLostError") } return &HistoryService_StartWorkflowExecution_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_StartWorkflowExecution_Result.DomainNotActiveError") } return &HistoryService_StartWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_StartWorkflowExecution_Result.LimitExceededError") } return &HistoryService_StartWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_StartWorkflowExecution_Result.ServiceBusyError") } return &HistoryService_StartWorkflowExecution_Result{ServiceBusyError: e}, nil } return nil, err } HistoryService_StartWorkflowExecution_Helper.UnwrapResponse = func(result *HistoryService_StartWorkflowExecution_Result) (success *shared.StartWorkflowExecutionResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.SessionAlreadyExistError != nil { err = result.SessionAlreadyExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // HistoryService_StartWorkflowExecution_Result represents the result of a HistoryService.StartWorkflowExecution function call. // // The result of a StartWorkflowExecution execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type HistoryService_StartWorkflowExecution_Result struct { // Value returned by StartWorkflowExecution after a successful execution. Success *shared.StartWorkflowExecutionResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` SessionAlreadyExistError *shared.WorkflowExecutionAlreadyStartedError `json:"sessionAlreadyExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a HistoryService_StartWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_StartWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.SessionAlreadyExistError != nil { w, err = v.SessionAlreadyExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("HistoryService_StartWorkflowExecution_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_StartWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_StartWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_StartWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_StartWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _StartWorkflowExecutionResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.SessionAlreadyExistError, err = _WorkflowExecutionAlreadyStartedError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_StartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_StartWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_StartWorkflowExecution_Result struct could not be encoded. func (v *HistoryService_StartWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SessionAlreadyExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.SessionAlreadyExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_StartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_StartWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_StartWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *HistoryService_StartWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _StartWorkflowExecutionResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.SessionAlreadyExistError, err = _WorkflowExecutionAlreadyStartedError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.SessionAlreadyExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("HistoryService_StartWorkflowExecution_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_StartWorkflowExecution_Result // struct. func (v *HistoryService_StartWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.SessionAlreadyExistError != nil { fields[i] = fmt.Sprintf("SessionAlreadyExistError: %v", v.SessionAlreadyExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("HistoryService_StartWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_StartWorkflowExecution_Result match the // provided HistoryService_StartWorkflowExecution_Result. // // This function performs a deep comparison. func (v *HistoryService_StartWorkflowExecution_Result) Equals(rhs *HistoryService_StartWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.SessionAlreadyExistError == nil && rhs.SessionAlreadyExistError == nil) || (v.SessionAlreadyExistError != nil && rhs.SessionAlreadyExistError != nil && v.SessionAlreadyExistError.Equals(rhs.SessionAlreadyExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_StartWorkflowExecution_Result. func (v *HistoryService_StartWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.SessionAlreadyExistError != nil { err = multierr.Append(err, enc.AddObject("sessionAlreadyExistError", v.SessionAlreadyExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *HistoryService_StartWorkflowExecution_Result) GetSuccess() (o *shared.StartWorkflowExecutionResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *HistoryService_StartWorkflowExecution_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_StartWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_StartWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_StartWorkflowExecution_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_StartWorkflowExecution_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetSessionAlreadyExistError returns the value of SessionAlreadyExistError if it is set or its // zero value if it is unset. func (v *HistoryService_StartWorkflowExecution_Result) GetSessionAlreadyExistError() (o *shared.WorkflowExecutionAlreadyStartedError) { if v != nil && v.SessionAlreadyExistError != nil { return v.SessionAlreadyExistError } return } // IsSetSessionAlreadyExistError returns true if SessionAlreadyExistError is not nil. func (v *HistoryService_StartWorkflowExecution_Result) IsSetSessionAlreadyExistError() bool { return v != nil && v.SessionAlreadyExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_StartWorkflowExecution_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_StartWorkflowExecution_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_StartWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_StartWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_StartWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_StartWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_StartWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_StartWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "StartWorkflowExecution" for this struct. func (v *HistoryService_StartWorkflowExecution_Result) MethodName() string { return "StartWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_StartWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_SyncActivity_Args represents the arguments for the HistoryService.SyncActivity function. // // The arguments for SyncActivity are sent and received over the wire as this struct. type HistoryService_SyncActivity_Args struct { SyncActivityRequest *SyncActivityRequest `json:"syncActivityRequest,omitempty"` } // ToWire translates a HistoryService_SyncActivity_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_SyncActivity_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.SyncActivityRequest != nil { w, err = v.SyncActivityRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SyncActivityRequest_Read(w wire.Value) (*SyncActivityRequest, error) { var v SyncActivityRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_SyncActivity_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_SyncActivity_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_SyncActivity_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_SyncActivity_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.SyncActivityRequest, err = _SyncActivityRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_SyncActivity_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_SyncActivity_Args struct could not be encoded. func (v *HistoryService_SyncActivity_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SyncActivityRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.SyncActivityRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SyncActivityRequest_Decode(sr stream.Reader) (*SyncActivityRequest, error) { var v SyncActivityRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_SyncActivity_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_SyncActivity_Args struct could not be generated from the wire // representation. func (v *HistoryService_SyncActivity_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.SyncActivityRequest, err = _SyncActivityRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_SyncActivity_Args // struct. func (v *HistoryService_SyncActivity_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.SyncActivityRequest != nil { fields[i] = fmt.Sprintf("SyncActivityRequest: %v", v.SyncActivityRequest) i++ } return fmt.Sprintf("HistoryService_SyncActivity_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_SyncActivity_Args match the // provided HistoryService_SyncActivity_Args. // // This function performs a deep comparison. func (v *HistoryService_SyncActivity_Args) Equals(rhs *HistoryService_SyncActivity_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SyncActivityRequest == nil && rhs.SyncActivityRequest == nil) || (v.SyncActivityRequest != nil && rhs.SyncActivityRequest != nil && v.SyncActivityRequest.Equals(rhs.SyncActivityRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_SyncActivity_Args. func (v *HistoryService_SyncActivity_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SyncActivityRequest != nil { err = multierr.Append(err, enc.AddObject("syncActivityRequest", v.SyncActivityRequest)) } return err } // GetSyncActivityRequest returns the value of SyncActivityRequest if it is set or its // zero value if it is unset. func (v *HistoryService_SyncActivity_Args) GetSyncActivityRequest() (o *SyncActivityRequest) { if v != nil && v.SyncActivityRequest != nil { return v.SyncActivityRequest } return } // IsSetSyncActivityRequest returns true if SyncActivityRequest is not nil. func (v *HistoryService_SyncActivity_Args) IsSetSyncActivityRequest() bool { return v != nil && v.SyncActivityRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "SyncActivity" for this struct. func (v *HistoryService_SyncActivity_Args) MethodName() string { return "SyncActivity" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_SyncActivity_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_SyncActivity_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.SyncActivity // function. var HistoryService_SyncActivity_Helper = struct { // Args accepts the parameters of SyncActivity in-order and returns // the arguments struct for the function. Args func( syncActivityRequest *SyncActivityRequest, ) *HistoryService_SyncActivity_Args // IsException returns true if the given error can be thrown // by SyncActivity. // // An error can be thrown by SyncActivity only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for SyncActivity // given the error returned by it. The provided error may // be nil if SyncActivity did not fail. // // This allows mapping errors returned by SyncActivity into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // SyncActivity // // err := SyncActivity(args) // result, err := HistoryService_SyncActivity_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from SyncActivity: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_SyncActivity_Result, error) // UnwrapResponse takes the result struct for SyncActivity // and returns the erorr returned by it (if any). // // The error is non-nil only if SyncActivity threw an // exception. // // result := deserialize(bytes) // err := HistoryService_SyncActivity_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_SyncActivity_Result) error }{} func init() { HistoryService_SyncActivity_Helper.Args = func( syncActivityRequest *SyncActivityRequest, ) *HistoryService_SyncActivity_Args { return &HistoryService_SyncActivity_Args{ SyncActivityRequest: syncActivityRequest, } } HistoryService_SyncActivity_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.ServiceBusyError: return true case *shared.RetryTaskV2Error: return true default: return false } } HistoryService_SyncActivity_Helper.WrapResponse = func(err error) (*HistoryService_SyncActivity_Result, error) { if err == nil { return &HistoryService_SyncActivity_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncActivity_Result.BadRequestError") } return &HistoryService_SyncActivity_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncActivity_Result.InternalServiceError") } return &HistoryService_SyncActivity_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncActivity_Result.EntityNotExistError") } return &HistoryService_SyncActivity_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncActivity_Result.ShardOwnershipLostError") } return &HistoryService_SyncActivity_Result{ShardOwnershipLostError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncActivity_Result.ServiceBusyError") } return &HistoryService_SyncActivity_Result{ServiceBusyError: e}, nil case *shared.RetryTaskV2Error: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncActivity_Result.RetryTaskV2Error") } return &HistoryService_SyncActivity_Result{RetryTaskV2Error: e}, nil } return nil, err } HistoryService_SyncActivity_Helper.UnwrapResponse = func(result *HistoryService_SyncActivity_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.RetryTaskV2Error != nil { err = result.RetryTaskV2Error return } return } } // HistoryService_SyncActivity_Result represents the result of a HistoryService.SyncActivity function call. // // The result of a SyncActivity execution is sent and received over the wire as this struct. type HistoryService_SyncActivity_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` RetryTaskV2Error *shared.RetryTaskV2Error `json:"retryTaskV2Error,omitempty"` } // ToWire translates a HistoryService_SyncActivity_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_SyncActivity_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.RetryTaskV2Error != nil { w, err = v.RetryTaskV2Error.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_SyncActivity_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_SyncActivity_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_SyncActivity_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_SyncActivity_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_SyncActivity_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.RetryTaskV2Error, err = _RetryTaskV2Error_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.RetryTaskV2Error != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_SyncActivity_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_SyncActivity_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_SyncActivity_Result struct could not be encoded. func (v *HistoryService_SyncActivity_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryTaskV2Error != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.RetryTaskV2Error.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.RetryTaskV2Error != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_SyncActivity_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_SyncActivity_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_SyncActivity_Result struct could not be generated from the wire // representation. func (v *HistoryService_SyncActivity_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.RetryTaskV2Error, err = _RetryTaskV2Error_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.RetryTaskV2Error != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_SyncActivity_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_SyncActivity_Result // struct. func (v *HistoryService_SyncActivity_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.RetryTaskV2Error != nil { fields[i] = fmt.Sprintf("RetryTaskV2Error: %v", v.RetryTaskV2Error) i++ } return fmt.Sprintf("HistoryService_SyncActivity_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_SyncActivity_Result match the // provided HistoryService_SyncActivity_Result. // // This function performs a deep comparison. func (v *HistoryService_SyncActivity_Result) Equals(rhs *HistoryService_SyncActivity_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.RetryTaskV2Error == nil && rhs.RetryTaskV2Error == nil) || (v.RetryTaskV2Error != nil && rhs.RetryTaskV2Error != nil && v.RetryTaskV2Error.Equals(rhs.RetryTaskV2Error))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_SyncActivity_Result. func (v *HistoryService_SyncActivity_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.RetryTaskV2Error != nil { err = multierr.Append(err, enc.AddObject("retryTaskV2Error", v.RetryTaskV2Error)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_SyncActivity_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_SyncActivity_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_SyncActivity_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_SyncActivity_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_SyncActivity_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_SyncActivity_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_SyncActivity_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_SyncActivity_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_SyncActivity_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_SyncActivity_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetRetryTaskV2Error returns the value of RetryTaskV2Error if it is set or its // zero value if it is unset. func (v *HistoryService_SyncActivity_Result) GetRetryTaskV2Error() (o *shared.RetryTaskV2Error) { if v != nil && v.RetryTaskV2Error != nil { return v.RetryTaskV2Error } return } // IsSetRetryTaskV2Error returns true if RetryTaskV2Error is not nil. func (v *HistoryService_SyncActivity_Result) IsSetRetryTaskV2Error() bool { return v != nil && v.RetryTaskV2Error != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "SyncActivity" for this struct. func (v *HistoryService_SyncActivity_Result) MethodName() string { return "SyncActivity" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_SyncActivity_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_SyncShardStatus_Args represents the arguments for the HistoryService.SyncShardStatus function. // // The arguments for SyncShardStatus are sent and received over the wire as this struct. type HistoryService_SyncShardStatus_Args struct { SyncShardStatusRequest *SyncShardStatusRequest `json:"syncShardStatusRequest,omitempty"` } // ToWire translates a HistoryService_SyncShardStatus_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_SyncShardStatus_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.SyncShardStatusRequest != nil { w, err = v.SyncShardStatusRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SyncShardStatusRequest_Read(w wire.Value) (*SyncShardStatusRequest, error) { var v SyncShardStatusRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_SyncShardStatus_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_SyncShardStatus_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_SyncShardStatus_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_SyncShardStatus_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.SyncShardStatusRequest, err = _SyncShardStatusRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_SyncShardStatus_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_SyncShardStatus_Args struct could not be encoded. func (v *HistoryService_SyncShardStatus_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SyncShardStatusRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.SyncShardStatusRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SyncShardStatusRequest_Decode(sr stream.Reader) (*SyncShardStatusRequest, error) { var v SyncShardStatusRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_SyncShardStatus_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_SyncShardStatus_Args struct could not be generated from the wire // representation. func (v *HistoryService_SyncShardStatus_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.SyncShardStatusRequest, err = _SyncShardStatusRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_SyncShardStatus_Args // struct. func (v *HistoryService_SyncShardStatus_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.SyncShardStatusRequest != nil { fields[i] = fmt.Sprintf("SyncShardStatusRequest: %v", v.SyncShardStatusRequest) i++ } return fmt.Sprintf("HistoryService_SyncShardStatus_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_SyncShardStatus_Args match the // provided HistoryService_SyncShardStatus_Args. // // This function performs a deep comparison. func (v *HistoryService_SyncShardStatus_Args) Equals(rhs *HistoryService_SyncShardStatus_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SyncShardStatusRequest == nil && rhs.SyncShardStatusRequest == nil) || (v.SyncShardStatusRequest != nil && rhs.SyncShardStatusRequest != nil && v.SyncShardStatusRequest.Equals(rhs.SyncShardStatusRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_SyncShardStatus_Args. func (v *HistoryService_SyncShardStatus_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SyncShardStatusRequest != nil { err = multierr.Append(err, enc.AddObject("syncShardStatusRequest", v.SyncShardStatusRequest)) } return err } // GetSyncShardStatusRequest returns the value of SyncShardStatusRequest if it is set or its // zero value if it is unset. func (v *HistoryService_SyncShardStatus_Args) GetSyncShardStatusRequest() (o *SyncShardStatusRequest) { if v != nil && v.SyncShardStatusRequest != nil { return v.SyncShardStatusRequest } return } // IsSetSyncShardStatusRequest returns true if SyncShardStatusRequest is not nil. func (v *HistoryService_SyncShardStatus_Args) IsSetSyncShardStatusRequest() bool { return v != nil && v.SyncShardStatusRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "SyncShardStatus" for this struct. func (v *HistoryService_SyncShardStatus_Args) MethodName() string { return "SyncShardStatus" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_SyncShardStatus_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_SyncShardStatus_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.SyncShardStatus // function. var HistoryService_SyncShardStatus_Helper = struct { // Args accepts the parameters of SyncShardStatus in-order and returns // the arguments struct for the function. Args func( syncShardStatusRequest *SyncShardStatusRequest, ) *HistoryService_SyncShardStatus_Args // IsException returns true if the given error can be thrown // by SyncShardStatus. // // An error can be thrown by SyncShardStatus only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for SyncShardStatus // given the error returned by it. The provided error may // be nil if SyncShardStatus did not fail. // // This allows mapping errors returned by SyncShardStatus into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // SyncShardStatus // // err := SyncShardStatus(args) // result, err := HistoryService_SyncShardStatus_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from SyncShardStatus: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_SyncShardStatus_Result, error) // UnwrapResponse takes the result struct for SyncShardStatus // and returns the erorr returned by it (if any). // // The error is non-nil only if SyncShardStatus threw an // exception. // // result := deserialize(bytes) // err := HistoryService_SyncShardStatus_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_SyncShardStatus_Result) error }{} func init() { HistoryService_SyncShardStatus_Helper.Args = func( syncShardStatusRequest *SyncShardStatusRequest, ) *HistoryService_SyncShardStatus_Args { return &HistoryService_SyncShardStatus_Args{ SyncShardStatusRequest: syncShardStatusRequest, } } HistoryService_SyncShardStatus_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *ShardOwnershipLostError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true default: return false } } HistoryService_SyncShardStatus_Helper.WrapResponse = func(err error) (*HistoryService_SyncShardStatus_Result, error) { if err == nil { return &HistoryService_SyncShardStatus_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncShardStatus_Result.BadRequestError") } return &HistoryService_SyncShardStatus_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncShardStatus_Result.InternalServiceError") } return &HistoryService_SyncShardStatus_Result{InternalServiceError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncShardStatus_Result.ShardOwnershipLostError") } return &HistoryService_SyncShardStatus_Result{ShardOwnershipLostError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncShardStatus_Result.LimitExceededError") } return &HistoryService_SyncShardStatus_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_SyncShardStatus_Result.ServiceBusyError") } return &HistoryService_SyncShardStatus_Result{ServiceBusyError: e}, nil } return nil, err } HistoryService_SyncShardStatus_Helper.UnwrapResponse = func(result *HistoryService_SyncShardStatus_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } return } } // HistoryService_SyncShardStatus_Result represents the result of a HistoryService.SyncShardStatus function call. // // The result of a SyncShardStatus execution is sent and received over the wire as this struct. type HistoryService_SyncShardStatus_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a HistoryService_SyncShardStatus_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_SyncShardStatus_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_SyncShardStatus_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_SyncShardStatus_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_SyncShardStatus_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_SyncShardStatus_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_SyncShardStatus_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_SyncShardStatus_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_SyncShardStatus_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_SyncShardStatus_Result struct could not be encoded. func (v *HistoryService_SyncShardStatus_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_SyncShardStatus_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_SyncShardStatus_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_SyncShardStatus_Result struct could not be generated from the wire // representation. func (v *HistoryService_SyncShardStatus_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_SyncShardStatus_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_SyncShardStatus_Result // struct. func (v *HistoryService_SyncShardStatus_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("HistoryService_SyncShardStatus_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_SyncShardStatus_Result match the // provided HistoryService_SyncShardStatus_Result. // // This function performs a deep comparison. func (v *HistoryService_SyncShardStatus_Result) Equals(rhs *HistoryService_SyncShardStatus_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_SyncShardStatus_Result. func (v *HistoryService_SyncShardStatus_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_SyncShardStatus_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_SyncShardStatus_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_SyncShardStatus_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_SyncShardStatus_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_SyncShardStatus_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_SyncShardStatus_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_SyncShardStatus_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_SyncShardStatus_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_SyncShardStatus_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_SyncShardStatus_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "SyncShardStatus" for this struct. func (v *HistoryService_SyncShardStatus_Result) MethodName() string { return "SyncShardStatus" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_SyncShardStatus_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // HistoryService_TerminateWorkflowExecution_Args represents the arguments for the HistoryService.TerminateWorkflowExecution function. // // The arguments for TerminateWorkflowExecution are sent and received over the wire as this struct. type HistoryService_TerminateWorkflowExecution_Args struct { TerminateRequest *TerminateWorkflowExecutionRequest `json:"terminateRequest,omitempty"` } // ToWire translates a HistoryService_TerminateWorkflowExecution_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_TerminateWorkflowExecution_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.TerminateRequest != nil { w, err = v.TerminateRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TerminateWorkflowExecutionRequest_1_Read(w wire.Value) (*TerminateWorkflowExecutionRequest, error) { var v TerminateWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryService_TerminateWorkflowExecution_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_TerminateWorkflowExecution_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_TerminateWorkflowExecution_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_TerminateWorkflowExecution_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.TerminateRequest, err = _TerminateWorkflowExecutionRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryService_TerminateWorkflowExecution_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_TerminateWorkflowExecution_Args struct could not be encoded. func (v *HistoryService_TerminateWorkflowExecution_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TerminateRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.TerminateRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TerminateWorkflowExecutionRequest_1_Decode(sr stream.Reader) (*TerminateWorkflowExecutionRequest, error) { var v TerminateWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryService_TerminateWorkflowExecution_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_TerminateWorkflowExecution_Args struct could not be generated from the wire // representation. func (v *HistoryService_TerminateWorkflowExecution_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.TerminateRequest, err = _TerminateWorkflowExecutionRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryService_TerminateWorkflowExecution_Args // struct. func (v *HistoryService_TerminateWorkflowExecution_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.TerminateRequest != nil { fields[i] = fmt.Sprintf("TerminateRequest: %v", v.TerminateRequest) i++ } return fmt.Sprintf("HistoryService_TerminateWorkflowExecution_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_TerminateWorkflowExecution_Args match the // provided HistoryService_TerminateWorkflowExecution_Args. // // This function performs a deep comparison. func (v *HistoryService_TerminateWorkflowExecution_Args) Equals(rhs *HistoryService_TerminateWorkflowExecution_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TerminateRequest == nil && rhs.TerminateRequest == nil) || (v.TerminateRequest != nil && rhs.TerminateRequest != nil && v.TerminateRequest.Equals(rhs.TerminateRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_TerminateWorkflowExecution_Args. func (v *HistoryService_TerminateWorkflowExecution_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TerminateRequest != nil { err = multierr.Append(err, enc.AddObject("terminateRequest", v.TerminateRequest)) } return err } // GetTerminateRequest returns the value of TerminateRequest if it is set or its // zero value if it is unset. func (v *HistoryService_TerminateWorkflowExecution_Args) GetTerminateRequest() (o *TerminateWorkflowExecutionRequest) { if v != nil && v.TerminateRequest != nil { return v.TerminateRequest } return } // IsSetTerminateRequest returns true if TerminateRequest is not nil. func (v *HistoryService_TerminateWorkflowExecution_Args) IsSetTerminateRequest() bool { return v != nil && v.TerminateRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "TerminateWorkflowExecution" for this struct. func (v *HistoryService_TerminateWorkflowExecution_Args) MethodName() string { return "TerminateWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *HistoryService_TerminateWorkflowExecution_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // HistoryService_TerminateWorkflowExecution_Helper provides functions that aid in handling the // parameters and return values of the HistoryService.TerminateWorkflowExecution // function. var HistoryService_TerminateWorkflowExecution_Helper = struct { // Args accepts the parameters of TerminateWorkflowExecution in-order and returns // the arguments struct for the function. Args func( terminateRequest *TerminateWorkflowExecutionRequest, ) *HistoryService_TerminateWorkflowExecution_Args // IsException returns true if the given error can be thrown // by TerminateWorkflowExecution. // // An error can be thrown by TerminateWorkflowExecution only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for TerminateWorkflowExecution // given the error returned by it. The provided error may // be nil if TerminateWorkflowExecution did not fail. // // This allows mapping errors returned by TerminateWorkflowExecution into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // TerminateWorkflowExecution // // err := TerminateWorkflowExecution(args) // result, err := HistoryService_TerminateWorkflowExecution_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from TerminateWorkflowExecution: %v", err) // } // serialize(result) WrapResponse func(error) (*HistoryService_TerminateWorkflowExecution_Result, error) // UnwrapResponse takes the result struct for TerminateWorkflowExecution // and returns the erorr returned by it (if any). // // The error is non-nil only if TerminateWorkflowExecution threw an // exception. // // result := deserialize(bytes) // err := HistoryService_TerminateWorkflowExecution_Helper.UnwrapResponse(result) UnwrapResponse func(*HistoryService_TerminateWorkflowExecution_Result) error }{} func init() { HistoryService_TerminateWorkflowExecution_Helper.Args = func( terminateRequest *TerminateWorkflowExecutionRequest, ) *HistoryService_TerminateWorkflowExecution_Args { return &HistoryService_TerminateWorkflowExecution_Args{ TerminateRequest: terminateRequest, } } HistoryService_TerminateWorkflowExecution_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *ShardOwnershipLostError: return true case *shared.DomainNotActiveError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.WorkflowExecutionAlreadyCompletedError: return true default: return false } } HistoryService_TerminateWorkflowExecution_Helper.WrapResponse = func(err error) (*HistoryService_TerminateWorkflowExecution_Result, error) { if err == nil { return &HistoryService_TerminateWorkflowExecution_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_TerminateWorkflowExecution_Result.BadRequestError") } return &HistoryService_TerminateWorkflowExecution_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_TerminateWorkflowExecution_Result.InternalServiceError") } return &HistoryService_TerminateWorkflowExecution_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_TerminateWorkflowExecution_Result.EntityNotExistError") } return &HistoryService_TerminateWorkflowExecution_Result{EntityNotExistError: e}, nil case *ShardOwnershipLostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_TerminateWorkflowExecution_Result.ShardOwnershipLostError") } return &HistoryService_TerminateWorkflowExecution_Result{ShardOwnershipLostError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_TerminateWorkflowExecution_Result.DomainNotActiveError") } return &HistoryService_TerminateWorkflowExecution_Result{DomainNotActiveError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_TerminateWorkflowExecution_Result.LimitExceededError") } return &HistoryService_TerminateWorkflowExecution_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_TerminateWorkflowExecution_Result.ServiceBusyError") } return &HistoryService_TerminateWorkflowExecution_Result{ServiceBusyError: e}, nil case *shared.WorkflowExecutionAlreadyCompletedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for HistoryService_TerminateWorkflowExecution_Result.WorkflowExecutionAlreadyCompletedError") } return &HistoryService_TerminateWorkflowExecution_Result{WorkflowExecutionAlreadyCompletedError: e}, nil } return nil, err } HistoryService_TerminateWorkflowExecution_Helper.UnwrapResponse = func(result *HistoryService_TerminateWorkflowExecution_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ShardOwnershipLostError != nil { err = result.ShardOwnershipLostError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.WorkflowExecutionAlreadyCompletedError != nil { err = result.WorkflowExecutionAlreadyCompletedError return } return } } // HistoryService_TerminateWorkflowExecution_Result represents the result of a HistoryService.TerminateWorkflowExecution function call. // // The result of a TerminateWorkflowExecution execution is sent and received over the wire as this struct. type HistoryService_TerminateWorkflowExecution_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ShardOwnershipLostError *ShardOwnershipLostError `json:"shardOwnershipLostError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` WorkflowExecutionAlreadyCompletedError *shared.WorkflowExecutionAlreadyCompletedError `json:"workflowExecutionAlreadyCompletedError,omitempty"` } // ToWire translates a HistoryService_TerminateWorkflowExecution_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryService_TerminateWorkflowExecution_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ShardOwnershipLostError != nil { w, err = v.ShardOwnershipLostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { w, err = v.WorkflowExecutionAlreadyCompletedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("HistoryService_TerminateWorkflowExecution_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryService_TerminateWorkflowExecution_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryService_TerminateWorkflowExecution_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryService_TerminateWorkflowExecution_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryService_TerminateWorkflowExecution_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_TerminateWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a HistoryService_TerminateWorkflowExecution_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryService_TerminateWorkflowExecution_Result struct could not be encoded. func (v *HistoryService_TerminateWorkflowExecution_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardOwnershipLostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ShardOwnershipLostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionAlreadyCompletedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionAlreadyCompletedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_TerminateWorkflowExecution_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a HistoryService_TerminateWorkflowExecution_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryService_TerminateWorkflowExecution_Result struct could not be generated from the wire // representation. func (v *HistoryService_TerminateWorkflowExecution_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ShardOwnershipLostError, err = _ShardOwnershipLostError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.WorkflowExecutionAlreadyCompletedError, err = _WorkflowExecutionAlreadyCompletedError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ShardOwnershipLostError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.WorkflowExecutionAlreadyCompletedError != nil { count++ } if count > 1 { return fmt.Errorf("HistoryService_TerminateWorkflowExecution_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a HistoryService_TerminateWorkflowExecution_Result // struct. func (v *HistoryService_TerminateWorkflowExecution_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ShardOwnershipLostError != nil { fields[i] = fmt.Sprintf("ShardOwnershipLostError: %v", v.ShardOwnershipLostError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.WorkflowExecutionAlreadyCompletedError != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedError: %v", v.WorkflowExecutionAlreadyCompletedError) i++ } return fmt.Sprintf("HistoryService_TerminateWorkflowExecution_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryService_TerminateWorkflowExecution_Result match the // provided HistoryService_TerminateWorkflowExecution_Result. // // This function performs a deep comparison. func (v *HistoryService_TerminateWorkflowExecution_Result) Equals(rhs *HistoryService_TerminateWorkflowExecution_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ShardOwnershipLostError == nil && rhs.ShardOwnershipLostError == nil) || (v.ShardOwnershipLostError != nil && rhs.ShardOwnershipLostError != nil && v.ShardOwnershipLostError.Equals(rhs.ShardOwnershipLostError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.WorkflowExecutionAlreadyCompletedError == nil && rhs.WorkflowExecutionAlreadyCompletedError == nil) || (v.WorkflowExecutionAlreadyCompletedError != nil && rhs.WorkflowExecutionAlreadyCompletedError != nil && v.WorkflowExecutionAlreadyCompletedError.Equals(rhs.WorkflowExecutionAlreadyCompletedError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryService_TerminateWorkflowExecution_Result. func (v *HistoryService_TerminateWorkflowExecution_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ShardOwnershipLostError != nil { err = multierr.Append(err, enc.AddObject("shardOwnershipLostError", v.ShardOwnershipLostError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.WorkflowExecutionAlreadyCompletedError != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionAlreadyCompletedError", v.WorkflowExecutionAlreadyCompletedError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *HistoryService_TerminateWorkflowExecution_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *HistoryService_TerminateWorkflowExecution_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *HistoryService_TerminateWorkflowExecution_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *HistoryService_TerminateWorkflowExecution_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *HistoryService_TerminateWorkflowExecution_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *HistoryService_TerminateWorkflowExecution_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetShardOwnershipLostError returns the value of ShardOwnershipLostError if it is set or its // zero value if it is unset. func (v *HistoryService_TerminateWorkflowExecution_Result) GetShardOwnershipLostError() (o *ShardOwnershipLostError) { if v != nil && v.ShardOwnershipLostError != nil { return v.ShardOwnershipLostError } return } // IsSetShardOwnershipLostError returns true if ShardOwnershipLostError is not nil. func (v *HistoryService_TerminateWorkflowExecution_Result) IsSetShardOwnershipLostError() bool { return v != nil && v.ShardOwnershipLostError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *HistoryService_TerminateWorkflowExecution_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *HistoryService_TerminateWorkflowExecution_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *HistoryService_TerminateWorkflowExecution_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *HistoryService_TerminateWorkflowExecution_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *HistoryService_TerminateWorkflowExecution_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *HistoryService_TerminateWorkflowExecution_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetWorkflowExecutionAlreadyCompletedError returns the value of WorkflowExecutionAlreadyCompletedError if it is set or its // zero value if it is unset. func (v *HistoryService_TerminateWorkflowExecution_Result) GetWorkflowExecutionAlreadyCompletedError() (o *shared.WorkflowExecutionAlreadyCompletedError) { if v != nil && v.WorkflowExecutionAlreadyCompletedError != nil { return v.WorkflowExecutionAlreadyCompletedError } return } // IsSetWorkflowExecutionAlreadyCompletedError returns true if WorkflowExecutionAlreadyCompletedError is not nil. func (v *HistoryService_TerminateWorkflowExecution_Result) IsSetWorkflowExecutionAlreadyCompletedError() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "TerminateWorkflowExecution" for this struct. func (v *HistoryService_TerminateWorkflowExecution_Result) MethodName() string { return "TerminateWorkflowExecution" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *HistoryService_TerminateWorkflowExecution_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } ================================================ FILE: .gen/go/history/historyserviceclient/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package historyserviceclient import ( context "context" reflect "reflect" wire "go.uber.org/thriftrw/wire" yarpc "go.uber.org/yarpc" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" history "github.com/uber/cadence/.gen/go/history" replicator "github.com/uber/cadence/.gen/go/replicator" shared "github.com/uber/cadence/.gen/go/shared" ) // Interface is a client for the HistoryService service. type Interface interface { CloseShard( ctx context.Context, Request *shared.CloseShardRequest, opts ...yarpc.CallOption, ) error DescribeHistoryHost( ctx context.Context, Request *shared.DescribeHistoryHostRequest, opts ...yarpc.CallOption, ) (*shared.DescribeHistoryHostResponse, error) DescribeMutableState( ctx context.Context, Request *history.DescribeMutableStateRequest, opts ...yarpc.CallOption, ) (*history.DescribeMutableStateResponse, error) DescribeQueue( ctx context.Context, Request *shared.DescribeQueueRequest, opts ...yarpc.CallOption, ) (*shared.DescribeQueueResponse, error) DescribeWorkflowExecution( ctx context.Context, DescribeRequest *history.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*shared.DescribeWorkflowExecutionResponse, error) GetCrossClusterTasks( ctx context.Context, Request *shared.GetCrossClusterTasksRequest, opts ...yarpc.CallOption, ) (*shared.GetCrossClusterTasksResponse, error) GetDLQReplicationMessages( ctx context.Context, Request *replicator.GetDLQReplicationMessagesRequest, opts ...yarpc.CallOption, ) (*replicator.GetDLQReplicationMessagesResponse, error) GetFailoverInfo( ctx context.Context, Request *history.GetFailoverInfoRequest, opts ...yarpc.CallOption, ) (*history.GetFailoverInfoResponse, error) GetMutableState( ctx context.Context, GetRequest *history.GetMutableStateRequest, opts ...yarpc.CallOption, ) (*history.GetMutableStateResponse, error) GetReplicationMessages( ctx context.Context, Request *replicator.GetReplicationMessagesRequest, opts ...yarpc.CallOption, ) (*replicator.GetReplicationMessagesResponse, error) MergeDLQMessages( ctx context.Context, Request *replicator.MergeDLQMessagesRequest, opts ...yarpc.CallOption, ) (*replicator.MergeDLQMessagesResponse, error) NotifyFailoverMarkers( ctx context.Context, Request *history.NotifyFailoverMarkersRequest, opts ...yarpc.CallOption, ) error PollMutableState( ctx context.Context, PollRequest *history.PollMutableStateRequest, opts ...yarpc.CallOption, ) (*history.PollMutableStateResponse, error) PurgeDLQMessages( ctx context.Context, Request *replicator.PurgeDLQMessagesRequest, opts ...yarpc.CallOption, ) error QueryWorkflow( ctx context.Context, QueryRequest *history.QueryWorkflowRequest, opts ...yarpc.CallOption, ) (*history.QueryWorkflowResponse, error) RatelimitUpdate( ctx context.Context, Request *history.RatelimitUpdateRequest, opts ...yarpc.CallOption, ) (*history.RatelimitUpdateResponse, error) ReadDLQMessages( ctx context.Context, Request *replicator.ReadDLQMessagesRequest, opts ...yarpc.CallOption, ) (*replicator.ReadDLQMessagesResponse, error) ReapplyEvents( ctx context.Context, ReapplyEventsRequest *history.ReapplyEventsRequest, opts ...yarpc.CallOption, ) error RecordActivityTaskHeartbeat( ctx context.Context, HeartbeatRequest *history.RecordActivityTaskHeartbeatRequest, opts ...yarpc.CallOption, ) (*shared.RecordActivityTaskHeartbeatResponse, error) RecordActivityTaskStarted( ctx context.Context, AddRequest *history.RecordActivityTaskStartedRequest, opts ...yarpc.CallOption, ) (*history.RecordActivityTaskStartedResponse, error) RecordChildExecutionCompleted( ctx context.Context, CompletionRequest *history.RecordChildExecutionCompletedRequest, opts ...yarpc.CallOption, ) error RecordDecisionTaskStarted( ctx context.Context, AddRequest *history.RecordDecisionTaskStartedRequest, opts ...yarpc.CallOption, ) (*history.RecordDecisionTaskStartedResponse, error) RefreshWorkflowTasks( ctx context.Context, Request *history.RefreshWorkflowTasksRequest, opts ...yarpc.CallOption, ) error RemoveSignalMutableState( ctx context.Context, RemoveRequest *history.RemoveSignalMutableStateRequest, opts ...yarpc.CallOption, ) error RemoveTask( ctx context.Context, Request *shared.RemoveTaskRequest, opts ...yarpc.CallOption, ) error ReplicateEventsV2( ctx context.Context, ReplicateV2Request *history.ReplicateEventsV2Request, opts ...yarpc.CallOption, ) error RequestCancelWorkflowExecution( ctx context.Context, CancelRequest *history.RequestCancelWorkflowExecutionRequest, opts ...yarpc.CallOption, ) error ResetQueue( ctx context.Context, Request *shared.ResetQueueRequest, opts ...yarpc.CallOption, ) error ResetStickyTaskList( ctx context.Context, ResetRequest *history.ResetStickyTaskListRequest, opts ...yarpc.CallOption, ) (*history.ResetStickyTaskListResponse, error) ResetWorkflowExecution( ctx context.Context, ResetRequest *history.ResetWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*shared.ResetWorkflowExecutionResponse, error) RespondActivityTaskCanceled( ctx context.Context, CanceledRequest *history.RespondActivityTaskCanceledRequest, opts ...yarpc.CallOption, ) error RespondActivityTaskCompleted( ctx context.Context, CompleteRequest *history.RespondActivityTaskCompletedRequest, opts ...yarpc.CallOption, ) error RespondActivityTaskFailed( ctx context.Context, FailRequest *history.RespondActivityTaskFailedRequest, opts ...yarpc.CallOption, ) error RespondCrossClusterTasksCompleted( ctx context.Context, Request *shared.RespondCrossClusterTasksCompletedRequest, opts ...yarpc.CallOption, ) (*shared.RespondCrossClusterTasksCompletedResponse, error) RespondDecisionTaskCompleted( ctx context.Context, CompleteRequest *history.RespondDecisionTaskCompletedRequest, opts ...yarpc.CallOption, ) (*history.RespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed( ctx context.Context, FailedRequest *history.RespondDecisionTaskFailedRequest, opts ...yarpc.CallOption, ) error ScheduleDecisionTask( ctx context.Context, ScheduleRequest *history.ScheduleDecisionTaskRequest, opts ...yarpc.CallOption, ) error SignalWithStartWorkflowExecution( ctx context.Context, SignalWithStartRequest *history.SignalWithStartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*shared.StartWorkflowExecutionResponse, error) SignalWorkflowExecution( ctx context.Context, SignalRequest *history.SignalWorkflowExecutionRequest, opts ...yarpc.CallOption, ) error StartWorkflowExecution( ctx context.Context, StartRequest *history.StartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*shared.StartWorkflowExecutionResponse, error) SyncActivity( ctx context.Context, SyncActivityRequest *history.SyncActivityRequest, opts ...yarpc.CallOption, ) error SyncShardStatus( ctx context.Context, SyncShardStatusRequest *history.SyncShardStatusRequest, opts ...yarpc.CallOption, ) error TerminateWorkflowExecution( ctx context.Context, TerminateRequest *history.TerminateWorkflowExecutionRequest, opts ...yarpc.CallOption, ) error } // New builds a new client for the HistoryService service. // // client := historyserviceclient.New(dispatcher.ClientConfig("historyservice")) func New(c transport.ClientConfig, opts ...thrift.ClientOption) Interface { return client{ c: thrift.New(thrift.Config{ Service: "HistoryService", ClientConfig: c, }, opts...), nwc: thrift.NewNoWire(thrift.Config{ Service: "HistoryService", ClientConfig: c, }, opts...), } } func init() { yarpc.RegisterClientBuilder( func(c transport.ClientConfig, f reflect.StructField) Interface { return New(c, thrift.ClientBuilderOptions(c, f)...) }, ) } type client struct { c thrift.Client nwc thrift.NoWireClient } func (c client) CloseShard( ctx context.Context, _Request *shared.CloseShardRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_CloseShard_Result args := history.HistoryService_CloseShard_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_CloseShard_Helper.UnwrapResponse(&result) return } func (c client) DescribeHistoryHost( ctx context.Context, _Request *shared.DescribeHistoryHostRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeHistoryHostResponse, err error) { var result history.HistoryService_DescribeHistoryHost_Result args := history.HistoryService_DescribeHistoryHost_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_DescribeHistoryHost_Helper.UnwrapResponse(&result) return } func (c client) DescribeMutableState( ctx context.Context, _Request *history.DescribeMutableStateRequest, opts ...yarpc.CallOption, ) (success *history.DescribeMutableStateResponse, err error) { var result history.HistoryService_DescribeMutableState_Result args := history.HistoryService_DescribeMutableState_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_DescribeMutableState_Helper.UnwrapResponse(&result) return } func (c client) DescribeQueue( ctx context.Context, _Request *shared.DescribeQueueRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeQueueResponse, err error) { var result history.HistoryService_DescribeQueue_Result args := history.HistoryService_DescribeQueue_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_DescribeQueue_Helper.UnwrapResponse(&result) return } func (c client) DescribeWorkflowExecution( ctx context.Context, _DescribeRequest *history.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeWorkflowExecutionResponse, err error) { var result history.HistoryService_DescribeWorkflowExecution_Result args := history.HistoryService_DescribeWorkflowExecution_Helper.Args(_DescribeRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_DescribeWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) GetCrossClusterTasks( ctx context.Context, _Request *shared.GetCrossClusterTasksRequest, opts ...yarpc.CallOption, ) (success *shared.GetCrossClusterTasksResponse, err error) { var result history.HistoryService_GetCrossClusterTasks_Result args := history.HistoryService_GetCrossClusterTasks_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_GetCrossClusterTasks_Helper.UnwrapResponse(&result) return } func (c client) GetDLQReplicationMessages( ctx context.Context, _Request *replicator.GetDLQReplicationMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.GetDLQReplicationMessagesResponse, err error) { var result history.HistoryService_GetDLQReplicationMessages_Result args := history.HistoryService_GetDLQReplicationMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_GetDLQReplicationMessages_Helper.UnwrapResponse(&result) return } func (c client) GetFailoverInfo( ctx context.Context, _Request *history.GetFailoverInfoRequest, opts ...yarpc.CallOption, ) (success *history.GetFailoverInfoResponse, err error) { var result history.HistoryService_GetFailoverInfo_Result args := history.HistoryService_GetFailoverInfo_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_GetFailoverInfo_Helper.UnwrapResponse(&result) return } func (c client) GetMutableState( ctx context.Context, _GetRequest *history.GetMutableStateRequest, opts ...yarpc.CallOption, ) (success *history.GetMutableStateResponse, err error) { var result history.HistoryService_GetMutableState_Result args := history.HistoryService_GetMutableState_Helper.Args(_GetRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_GetMutableState_Helper.UnwrapResponse(&result) return } func (c client) GetReplicationMessages( ctx context.Context, _Request *replicator.GetReplicationMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.GetReplicationMessagesResponse, err error) { var result history.HistoryService_GetReplicationMessages_Result args := history.HistoryService_GetReplicationMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_GetReplicationMessages_Helper.UnwrapResponse(&result) return } func (c client) MergeDLQMessages( ctx context.Context, _Request *replicator.MergeDLQMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.MergeDLQMessagesResponse, err error) { var result history.HistoryService_MergeDLQMessages_Result args := history.HistoryService_MergeDLQMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_MergeDLQMessages_Helper.UnwrapResponse(&result) return } func (c client) NotifyFailoverMarkers( ctx context.Context, _Request *history.NotifyFailoverMarkersRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_NotifyFailoverMarkers_Result args := history.HistoryService_NotifyFailoverMarkers_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_NotifyFailoverMarkers_Helper.UnwrapResponse(&result) return } func (c client) PollMutableState( ctx context.Context, _PollRequest *history.PollMutableStateRequest, opts ...yarpc.CallOption, ) (success *history.PollMutableStateResponse, err error) { var result history.HistoryService_PollMutableState_Result args := history.HistoryService_PollMutableState_Helper.Args(_PollRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_PollMutableState_Helper.UnwrapResponse(&result) return } func (c client) PurgeDLQMessages( ctx context.Context, _Request *replicator.PurgeDLQMessagesRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_PurgeDLQMessages_Result args := history.HistoryService_PurgeDLQMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_PurgeDLQMessages_Helper.UnwrapResponse(&result) return } func (c client) QueryWorkflow( ctx context.Context, _QueryRequest *history.QueryWorkflowRequest, opts ...yarpc.CallOption, ) (success *history.QueryWorkflowResponse, err error) { var result history.HistoryService_QueryWorkflow_Result args := history.HistoryService_QueryWorkflow_Helper.Args(_QueryRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_QueryWorkflow_Helper.UnwrapResponse(&result) return } func (c client) RatelimitUpdate( ctx context.Context, _Request *history.RatelimitUpdateRequest, opts ...yarpc.CallOption, ) (success *history.RatelimitUpdateResponse, err error) { var result history.HistoryService_RatelimitUpdate_Result args := history.HistoryService_RatelimitUpdate_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_RatelimitUpdate_Helper.UnwrapResponse(&result) return } func (c client) ReadDLQMessages( ctx context.Context, _Request *replicator.ReadDLQMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.ReadDLQMessagesResponse, err error) { var result history.HistoryService_ReadDLQMessages_Result args := history.HistoryService_ReadDLQMessages_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_ReadDLQMessages_Helper.UnwrapResponse(&result) return } func (c client) ReapplyEvents( ctx context.Context, _ReapplyEventsRequest *history.ReapplyEventsRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_ReapplyEvents_Result args := history.HistoryService_ReapplyEvents_Helper.Args(_ReapplyEventsRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_ReapplyEvents_Helper.UnwrapResponse(&result) return } func (c client) RecordActivityTaskHeartbeat( ctx context.Context, _HeartbeatRequest *history.RecordActivityTaskHeartbeatRequest, opts ...yarpc.CallOption, ) (success *shared.RecordActivityTaskHeartbeatResponse, err error) { var result history.HistoryService_RecordActivityTaskHeartbeat_Result args := history.HistoryService_RecordActivityTaskHeartbeat_Helper.Args(_HeartbeatRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_RecordActivityTaskHeartbeat_Helper.UnwrapResponse(&result) return } func (c client) RecordActivityTaskStarted( ctx context.Context, _AddRequest *history.RecordActivityTaskStartedRequest, opts ...yarpc.CallOption, ) (success *history.RecordActivityTaskStartedResponse, err error) { var result history.HistoryService_RecordActivityTaskStarted_Result args := history.HistoryService_RecordActivityTaskStarted_Helper.Args(_AddRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_RecordActivityTaskStarted_Helper.UnwrapResponse(&result) return } func (c client) RecordChildExecutionCompleted( ctx context.Context, _CompletionRequest *history.RecordChildExecutionCompletedRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_RecordChildExecutionCompleted_Result args := history.HistoryService_RecordChildExecutionCompleted_Helper.Args(_CompletionRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_RecordChildExecutionCompleted_Helper.UnwrapResponse(&result) return } func (c client) RecordDecisionTaskStarted( ctx context.Context, _AddRequest *history.RecordDecisionTaskStartedRequest, opts ...yarpc.CallOption, ) (success *history.RecordDecisionTaskStartedResponse, err error) { var result history.HistoryService_RecordDecisionTaskStarted_Result args := history.HistoryService_RecordDecisionTaskStarted_Helper.Args(_AddRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_RecordDecisionTaskStarted_Helper.UnwrapResponse(&result) return } func (c client) RefreshWorkflowTasks( ctx context.Context, _Request *history.RefreshWorkflowTasksRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_RefreshWorkflowTasks_Result args := history.HistoryService_RefreshWorkflowTasks_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_RefreshWorkflowTasks_Helper.UnwrapResponse(&result) return } func (c client) RemoveSignalMutableState( ctx context.Context, _RemoveRequest *history.RemoveSignalMutableStateRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_RemoveSignalMutableState_Result args := history.HistoryService_RemoveSignalMutableState_Helper.Args(_RemoveRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_RemoveSignalMutableState_Helper.UnwrapResponse(&result) return } func (c client) RemoveTask( ctx context.Context, _Request *shared.RemoveTaskRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_RemoveTask_Result args := history.HistoryService_RemoveTask_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_RemoveTask_Helper.UnwrapResponse(&result) return } func (c client) ReplicateEventsV2( ctx context.Context, _ReplicateV2Request *history.ReplicateEventsV2Request, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_ReplicateEventsV2_Result args := history.HistoryService_ReplicateEventsV2_Helper.Args(_ReplicateV2Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_ReplicateEventsV2_Helper.UnwrapResponse(&result) return } func (c client) RequestCancelWorkflowExecution( ctx context.Context, _CancelRequest *history.RequestCancelWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_RequestCancelWorkflowExecution_Result args := history.HistoryService_RequestCancelWorkflowExecution_Helper.Args(_CancelRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_RequestCancelWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) ResetQueue( ctx context.Context, _Request *shared.ResetQueueRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_ResetQueue_Result args := history.HistoryService_ResetQueue_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_ResetQueue_Helper.UnwrapResponse(&result) return } func (c client) ResetStickyTaskList( ctx context.Context, _ResetRequest *history.ResetStickyTaskListRequest, opts ...yarpc.CallOption, ) (success *history.ResetStickyTaskListResponse, err error) { var result history.HistoryService_ResetStickyTaskList_Result args := history.HistoryService_ResetStickyTaskList_Helper.Args(_ResetRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_ResetStickyTaskList_Helper.UnwrapResponse(&result) return } func (c client) ResetWorkflowExecution( ctx context.Context, _ResetRequest *history.ResetWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.ResetWorkflowExecutionResponse, err error) { var result history.HistoryService_ResetWorkflowExecution_Result args := history.HistoryService_ResetWorkflowExecution_Helper.Args(_ResetRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_ResetWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) RespondActivityTaskCanceled( ctx context.Context, _CanceledRequest *history.RespondActivityTaskCanceledRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_RespondActivityTaskCanceled_Result args := history.HistoryService_RespondActivityTaskCanceled_Helper.Args(_CanceledRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_RespondActivityTaskCanceled_Helper.UnwrapResponse(&result) return } func (c client) RespondActivityTaskCompleted( ctx context.Context, _CompleteRequest *history.RespondActivityTaskCompletedRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_RespondActivityTaskCompleted_Result args := history.HistoryService_RespondActivityTaskCompleted_Helper.Args(_CompleteRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_RespondActivityTaskCompleted_Helper.UnwrapResponse(&result) return } func (c client) RespondActivityTaskFailed( ctx context.Context, _FailRequest *history.RespondActivityTaskFailedRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_RespondActivityTaskFailed_Result args := history.HistoryService_RespondActivityTaskFailed_Helper.Args(_FailRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_RespondActivityTaskFailed_Helper.UnwrapResponse(&result) return } func (c client) RespondCrossClusterTasksCompleted( ctx context.Context, _Request *shared.RespondCrossClusterTasksCompletedRequest, opts ...yarpc.CallOption, ) (success *shared.RespondCrossClusterTasksCompletedResponse, err error) { var result history.HistoryService_RespondCrossClusterTasksCompleted_Result args := history.HistoryService_RespondCrossClusterTasksCompleted_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_RespondCrossClusterTasksCompleted_Helper.UnwrapResponse(&result) return } func (c client) RespondDecisionTaskCompleted( ctx context.Context, _CompleteRequest *history.RespondDecisionTaskCompletedRequest, opts ...yarpc.CallOption, ) (success *history.RespondDecisionTaskCompletedResponse, err error) { var result history.HistoryService_RespondDecisionTaskCompleted_Result args := history.HistoryService_RespondDecisionTaskCompleted_Helper.Args(_CompleteRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_RespondDecisionTaskCompleted_Helper.UnwrapResponse(&result) return } func (c client) RespondDecisionTaskFailed( ctx context.Context, _FailedRequest *history.RespondDecisionTaskFailedRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_RespondDecisionTaskFailed_Result args := history.HistoryService_RespondDecisionTaskFailed_Helper.Args(_FailedRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_RespondDecisionTaskFailed_Helper.UnwrapResponse(&result) return } func (c client) ScheduleDecisionTask( ctx context.Context, _ScheduleRequest *history.ScheduleDecisionTaskRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_ScheduleDecisionTask_Result args := history.HistoryService_ScheduleDecisionTask_Helper.Args(_ScheduleRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_ScheduleDecisionTask_Helper.UnwrapResponse(&result) return } func (c client) SignalWithStartWorkflowExecution( ctx context.Context, _SignalWithStartRequest *history.SignalWithStartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.StartWorkflowExecutionResponse, err error) { var result history.HistoryService_SignalWithStartWorkflowExecution_Result args := history.HistoryService_SignalWithStartWorkflowExecution_Helper.Args(_SignalWithStartRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_SignalWithStartWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) SignalWorkflowExecution( ctx context.Context, _SignalRequest *history.SignalWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_SignalWorkflowExecution_Result args := history.HistoryService_SignalWorkflowExecution_Helper.Args(_SignalRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_SignalWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) StartWorkflowExecution( ctx context.Context, _StartRequest *history.StartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.StartWorkflowExecutionResponse, err error) { var result history.HistoryService_StartWorkflowExecution_Result args := history.HistoryService_StartWorkflowExecution_Helper.Args(_StartRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = history.HistoryService_StartWorkflowExecution_Helper.UnwrapResponse(&result) return } func (c client) SyncActivity( ctx context.Context, _SyncActivityRequest *history.SyncActivityRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_SyncActivity_Result args := history.HistoryService_SyncActivity_Helper.Args(_SyncActivityRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_SyncActivity_Helper.UnwrapResponse(&result) return } func (c client) SyncShardStatus( ctx context.Context, _SyncShardStatusRequest *history.SyncShardStatusRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_SyncShardStatus_Result args := history.HistoryService_SyncShardStatus_Helper.Args(_SyncShardStatusRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_SyncShardStatus_Helper.UnwrapResponse(&result) return } func (c client) TerminateWorkflowExecution( ctx context.Context, _TerminateRequest *history.TerminateWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { var result history.HistoryService_TerminateWorkflowExecution_Result args := history.HistoryService_TerminateWorkflowExecution_Helper.Args(_TerminateRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = history.HistoryService_TerminateWorkflowExecution_Helper.UnwrapResponse(&result) return } ================================================ FILE: .gen/go/history/historyservicefx/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package historyservicefx import ( fx "go.uber.org/fx" yarpc "go.uber.org/yarpc" transport "go.uber.org/yarpc/api/transport" restriction "go.uber.org/yarpc/api/x/restriction" thrift "go.uber.org/yarpc/encoding/thrift" historyserviceclient "github.com/uber/cadence/.gen/go/history/historyserviceclient" ) // Params defines the dependencies for the HistoryService client. type Params struct { fx.In Provider yarpc.ClientConfig Restriction restriction.Checker `optional:"true"` } // Result defines the output of the HistoryService client module. It provides a // HistoryService client to an Fx application. type Result struct { fx.Out Client historyserviceclient.Interface // We are using an fx.Out struct here instead of just returning a client // so that we can add more values or add named versions of the client in // the future without breaking any existing code. } // Client provides a HistoryService client to an Fx application using the given name // for routing. // // fx.Provide( // historyservicefx.Client("..."), // newHandler, // ) func Client(name string, opts ...thrift.ClientOption) interface{} { return func(p Params) Result { cc := p.Provider.ClientConfig(name) if namer, ok := cc.GetUnaryOutbound().(transport.Namer); ok && p.Restriction != nil { if err := p.Restriction.Check(thrift.Encoding, namer.TransportName()); err != nil { panic(err.Error()) } } client := historyserviceclient.New(cc, opts...) return Result{Client: client} } } ================================================ FILE: .gen/go/history/historyservicefx/doc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated // Package historyservicefx provides better integration for Fx for services // implementing or calling HistoryService. // // # Clients // // If you are making requests to HistoryService, use the Client function to inject a // HistoryService client into your container. // // fx.Provide(historyservicefx.Client("...")) // // # Servers // // If you are implementing HistoryService, provide a historyserviceserver.Interface into // the container and use the Server function. // // Given, // // func NewHistoryServiceHandler() historyserviceserver.Interface // // You can do the following to have the procedures of HistoryService made available // to an Fx application. // // fx.Provide( // NewHistoryServiceHandler, // historyservicefx.Server(), // ) package historyservicefx ================================================ FILE: .gen/go/history/historyservicefx/server.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package historyservicefx import ( fx "go.uber.org/fx" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" historyserviceserver "github.com/uber/cadence/.gen/go/history/historyserviceserver" ) // ServerParams defines the dependencies for the HistoryService server. type ServerParams struct { fx.In Handler historyserviceserver.Interface } // ServerResult defines the output of HistoryService server module. It provides the // procedures of a HistoryService handler to an Fx application. // // The procedures are provided to the "yarpcfx" value group. Dig 1.2 or newer // must be used for this feature to work. type ServerResult struct { fx.Out Procedures []transport.Procedure `group:"yarpcfx"` } // Server provides procedures for HistoryService to an Fx application. It expects a // historyservicefx.Interface to be present in the container. // // fx.Provide( // func(h *MyHistoryServiceHandler) historyserviceserver.Interface { // return h // }, // historyservicefx.Server(), // ) func Server(opts ...thrift.RegisterOption) interface{} { return func(p ServerParams) ServerResult { procedures := historyserviceserver.New(p.Handler, opts...) return ServerResult{Procedures: procedures} } } ================================================ FILE: .gen/go/history/historyserviceserver/server.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package historyserviceserver import ( context "context" stream "go.uber.org/thriftrw/protocol/stream" wire "go.uber.org/thriftrw/wire" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" yarpcerrors "go.uber.org/yarpc/yarpcerrors" history "github.com/uber/cadence/.gen/go/history" replicator "github.com/uber/cadence/.gen/go/replicator" shared "github.com/uber/cadence/.gen/go/shared" ) // Interface is the server-side interface for the HistoryService service. type Interface interface { CloseShard( ctx context.Context, Request *shared.CloseShardRequest, ) error DescribeHistoryHost( ctx context.Context, Request *shared.DescribeHistoryHostRequest, ) (*shared.DescribeHistoryHostResponse, error) DescribeMutableState( ctx context.Context, Request *history.DescribeMutableStateRequest, ) (*history.DescribeMutableStateResponse, error) DescribeQueue( ctx context.Context, Request *shared.DescribeQueueRequest, ) (*shared.DescribeQueueResponse, error) DescribeWorkflowExecution( ctx context.Context, DescribeRequest *history.DescribeWorkflowExecutionRequest, ) (*shared.DescribeWorkflowExecutionResponse, error) GetCrossClusterTasks( ctx context.Context, Request *shared.GetCrossClusterTasksRequest, ) (*shared.GetCrossClusterTasksResponse, error) GetDLQReplicationMessages( ctx context.Context, Request *replicator.GetDLQReplicationMessagesRequest, ) (*replicator.GetDLQReplicationMessagesResponse, error) GetFailoverInfo( ctx context.Context, Request *history.GetFailoverInfoRequest, ) (*history.GetFailoverInfoResponse, error) GetMutableState( ctx context.Context, GetRequest *history.GetMutableStateRequest, ) (*history.GetMutableStateResponse, error) GetReplicationMessages( ctx context.Context, Request *replicator.GetReplicationMessagesRequest, ) (*replicator.GetReplicationMessagesResponse, error) MergeDLQMessages( ctx context.Context, Request *replicator.MergeDLQMessagesRequest, ) (*replicator.MergeDLQMessagesResponse, error) NotifyFailoverMarkers( ctx context.Context, Request *history.NotifyFailoverMarkersRequest, ) error PollMutableState( ctx context.Context, PollRequest *history.PollMutableStateRequest, ) (*history.PollMutableStateResponse, error) PurgeDLQMessages( ctx context.Context, Request *replicator.PurgeDLQMessagesRequest, ) error QueryWorkflow( ctx context.Context, QueryRequest *history.QueryWorkflowRequest, ) (*history.QueryWorkflowResponse, error) RatelimitUpdate( ctx context.Context, Request *history.RatelimitUpdateRequest, ) (*history.RatelimitUpdateResponse, error) ReadDLQMessages( ctx context.Context, Request *replicator.ReadDLQMessagesRequest, ) (*replicator.ReadDLQMessagesResponse, error) ReapplyEvents( ctx context.Context, ReapplyEventsRequest *history.ReapplyEventsRequest, ) error RecordActivityTaskHeartbeat( ctx context.Context, HeartbeatRequest *history.RecordActivityTaskHeartbeatRequest, ) (*shared.RecordActivityTaskHeartbeatResponse, error) RecordActivityTaskStarted( ctx context.Context, AddRequest *history.RecordActivityTaskStartedRequest, ) (*history.RecordActivityTaskStartedResponse, error) RecordChildExecutionCompleted( ctx context.Context, CompletionRequest *history.RecordChildExecutionCompletedRequest, ) error RecordDecisionTaskStarted( ctx context.Context, AddRequest *history.RecordDecisionTaskStartedRequest, ) (*history.RecordDecisionTaskStartedResponse, error) RefreshWorkflowTasks( ctx context.Context, Request *history.RefreshWorkflowTasksRequest, ) error RemoveSignalMutableState( ctx context.Context, RemoveRequest *history.RemoveSignalMutableStateRequest, ) error RemoveTask( ctx context.Context, Request *shared.RemoveTaskRequest, ) error ReplicateEventsV2( ctx context.Context, ReplicateV2Request *history.ReplicateEventsV2Request, ) error RequestCancelWorkflowExecution( ctx context.Context, CancelRequest *history.RequestCancelWorkflowExecutionRequest, ) error ResetQueue( ctx context.Context, Request *shared.ResetQueueRequest, ) error ResetStickyTaskList( ctx context.Context, ResetRequest *history.ResetStickyTaskListRequest, ) (*history.ResetStickyTaskListResponse, error) ResetWorkflowExecution( ctx context.Context, ResetRequest *history.ResetWorkflowExecutionRequest, ) (*shared.ResetWorkflowExecutionResponse, error) RespondActivityTaskCanceled( ctx context.Context, CanceledRequest *history.RespondActivityTaskCanceledRequest, ) error RespondActivityTaskCompleted( ctx context.Context, CompleteRequest *history.RespondActivityTaskCompletedRequest, ) error RespondActivityTaskFailed( ctx context.Context, FailRequest *history.RespondActivityTaskFailedRequest, ) error RespondCrossClusterTasksCompleted( ctx context.Context, Request *shared.RespondCrossClusterTasksCompletedRequest, ) (*shared.RespondCrossClusterTasksCompletedResponse, error) RespondDecisionTaskCompleted( ctx context.Context, CompleteRequest *history.RespondDecisionTaskCompletedRequest, ) (*history.RespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed( ctx context.Context, FailedRequest *history.RespondDecisionTaskFailedRequest, ) error ScheduleDecisionTask( ctx context.Context, ScheduleRequest *history.ScheduleDecisionTaskRequest, ) error SignalWithStartWorkflowExecution( ctx context.Context, SignalWithStartRequest *history.SignalWithStartWorkflowExecutionRequest, ) (*shared.StartWorkflowExecutionResponse, error) SignalWorkflowExecution( ctx context.Context, SignalRequest *history.SignalWorkflowExecutionRequest, ) error StartWorkflowExecution( ctx context.Context, StartRequest *history.StartWorkflowExecutionRequest, ) (*shared.StartWorkflowExecutionResponse, error) SyncActivity( ctx context.Context, SyncActivityRequest *history.SyncActivityRequest, ) error SyncShardStatus( ctx context.Context, SyncShardStatusRequest *history.SyncShardStatusRequest, ) error TerminateWorkflowExecution( ctx context.Context, TerminateRequest *history.TerminateWorkflowExecutionRequest, ) error } // New prepares an implementation of the HistoryService service for // registration. // // handler := HistoryServiceHandler{} // dispatcher.Register(historyserviceserver.New(handler)) func New(impl Interface, opts ...thrift.RegisterOption) []transport.Procedure { h := handler{impl} service := thrift.Service{ Name: "HistoryService", Methods: []thrift.Method{ thrift.Method{ Name: "CloseShard", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.CloseShard), NoWire: closeshard_NoWireHandler{impl}, }, Signature: "CloseShard(Request *shared.CloseShardRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "DescribeHistoryHost", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeHistoryHost), NoWire: describehistoryhost_NoWireHandler{impl}, }, Signature: "DescribeHistoryHost(Request *shared.DescribeHistoryHostRequest) (*shared.DescribeHistoryHostResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "DescribeMutableState", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeMutableState), NoWire: describemutablestate_NoWireHandler{impl}, }, Signature: "DescribeMutableState(Request *history.DescribeMutableStateRequest) (*history.DescribeMutableStateResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "DescribeQueue", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeQueue), NoWire: describequeue_NoWireHandler{impl}, }, Signature: "DescribeQueue(Request *shared.DescribeQueueRequest) (*shared.DescribeQueueResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "DescribeWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeWorkflowExecution), NoWire: describeworkflowexecution_NoWireHandler{impl}, }, Signature: "DescribeWorkflowExecution(DescribeRequest *history.DescribeWorkflowExecutionRequest) (*shared.DescribeWorkflowExecutionResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "GetCrossClusterTasks", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetCrossClusterTasks), NoWire: getcrossclustertasks_NoWireHandler{impl}, }, Signature: "GetCrossClusterTasks(Request *shared.GetCrossClusterTasksRequest) (*shared.GetCrossClusterTasksResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "GetDLQReplicationMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetDLQReplicationMessages), NoWire: getdlqreplicationmessages_NoWireHandler{impl}, }, Signature: "GetDLQReplicationMessages(Request *replicator.GetDLQReplicationMessagesRequest) (*replicator.GetDLQReplicationMessagesResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "GetFailoverInfo", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetFailoverInfo), NoWire: getfailoverinfo_NoWireHandler{impl}, }, Signature: "GetFailoverInfo(Request *history.GetFailoverInfoRequest) (*history.GetFailoverInfoResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "GetMutableState", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetMutableState), NoWire: getmutablestate_NoWireHandler{impl}, }, Signature: "GetMutableState(GetRequest *history.GetMutableStateRequest) (*history.GetMutableStateResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "GetReplicationMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetReplicationMessages), NoWire: getreplicationmessages_NoWireHandler{impl}, }, Signature: "GetReplicationMessages(Request *replicator.GetReplicationMessagesRequest) (*replicator.GetReplicationMessagesResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "MergeDLQMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.MergeDLQMessages), NoWire: mergedlqmessages_NoWireHandler{impl}, }, Signature: "MergeDLQMessages(Request *replicator.MergeDLQMessagesRequest) (*replicator.MergeDLQMessagesResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "NotifyFailoverMarkers", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.NotifyFailoverMarkers), NoWire: notifyfailovermarkers_NoWireHandler{impl}, }, Signature: "NotifyFailoverMarkers(Request *history.NotifyFailoverMarkersRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "PollMutableState", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.PollMutableState), NoWire: pollmutablestate_NoWireHandler{impl}, }, Signature: "PollMutableState(PollRequest *history.PollMutableStateRequest) (*history.PollMutableStateResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "PurgeDLQMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.PurgeDLQMessages), NoWire: purgedlqmessages_NoWireHandler{impl}, }, Signature: "PurgeDLQMessages(Request *replicator.PurgeDLQMessagesRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "QueryWorkflow", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.QueryWorkflow), NoWire: queryworkflow_NoWireHandler{impl}, }, Signature: "QueryWorkflow(QueryRequest *history.QueryWorkflowRequest) (*history.QueryWorkflowResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RatelimitUpdate", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RatelimitUpdate), NoWire: ratelimitupdate_NoWireHandler{impl}, }, Signature: "RatelimitUpdate(Request *history.RatelimitUpdateRequest) (*history.RatelimitUpdateResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "ReadDLQMessages", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ReadDLQMessages), NoWire: readdlqmessages_NoWireHandler{impl}, }, Signature: "ReadDLQMessages(Request *replicator.ReadDLQMessagesRequest) (*replicator.ReadDLQMessagesResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "ReapplyEvents", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ReapplyEvents), NoWire: reapplyevents_NoWireHandler{impl}, }, Signature: "ReapplyEvents(ReapplyEventsRequest *history.ReapplyEventsRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RecordActivityTaskHeartbeat", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RecordActivityTaskHeartbeat), NoWire: recordactivitytaskheartbeat_NoWireHandler{impl}, }, Signature: "RecordActivityTaskHeartbeat(HeartbeatRequest *history.RecordActivityTaskHeartbeatRequest) (*shared.RecordActivityTaskHeartbeatResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RecordActivityTaskStarted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RecordActivityTaskStarted), NoWire: recordactivitytaskstarted_NoWireHandler{impl}, }, Signature: "RecordActivityTaskStarted(AddRequest *history.RecordActivityTaskStartedRequest) (*history.RecordActivityTaskStartedResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RecordChildExecutionCompleted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RecordChildExecutionCompleted), NoWire: recordchildexecutioncompleted_NoWireHandler{impl}, }, Signature: "RecordChildExecutionCompleted(CompletionRequest *history.RecordChildExecutionCompletedRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RecordDecisionTaskStarted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RecordDecisionTaskStarted), NoWire: recorddecisiontaskstarted_NoWireHandler{impl}, }, Signature: "RecordDecisionTaskStarted(AddRequest *history.RecordDecisionTaskStartedRequest) (*history.RecordDecisionTaskStartedResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RefreshWorkflowTasks", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RefreshWorkflowTasks), NoWire: refreshworkflowtasks_NoWireHandler{impl}, }, Signature: "RefreshWorkflowTasks(Request *history.RefreshWorkflowTasksRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RemoveSignalMutableState", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RemoveSignalMutableState), NoWire: removesignalmutablestate_NoWireHandler{impl}, }, Signature: "RemoveSignalMutableState(RemoveRequest *history.RemoveSignalMutableStateRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RemoveTask", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RemoveTask), NoWire: removetask_NoWireHandler{impl}, }, Signature: "RemoveTask(Request *shared.RemoveTaskRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "ReplicateEventsV2", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ReplicateEventsV2), NoWire: replicateeventsv2_NoWireHandler{impl}, }, Signature: "ReplicateEventsV2(ReplicateV2Request *history.ReplicateEventsV2Request)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RequestCancelWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RequestCancelWorkflowExecution), NoWire: requestcancelworkflowexecution_NoWireHandler{impl}, }, Signature: "RequestCancelWorkflowExecution(CancelRequest *history.RequestCancelWorkflowExecutionRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "ResetQueue", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ResetQueue), NoWire: resetqueue_NoWireHandler{impl}, }, Signature: "ResetQueue(Request *shared.ResetQueueRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "ResetStickyTaskList", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ResetStickyTaskList), NoWire: resetstickytasklist_NoWireHandler{impl}, }, Signature: "ResetStickyTaskList(ResetRequest *history.ResetStickyTaskListRequest) (*history.ResetStickyTaskListResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "ResetWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ResetWorkflowExecution), NoWire: resetworkflowexecution_NoWireHandler{impl}, }, Signature: "ResetWorkflowExecution(ResetRequest *history.ResetWorkflowExecutionRequest) (*shared.ResetWorkflowExecutionResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RespondActivityTaskCanceled", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondActivityTaskCanceled), NoWire: respondactivitytaskcanceled_NoWireHandler{impl}, }, Signature: "RespondActivityTaskCanceled(CanceledRequest *history.RespondActivityTaskCanceledRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RespondActivityTaskCompleted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondActivityTaskCompleted), NoWire: respondactivitytaskcompleted_NoWireHandler{impl}, }, Signature: "RespondActivityTaskCompleted(CompleteRequest *history.RespondActivityTaskCompletedRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RespondActivityTaskFailed", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondActivityTaskFailed), NoWire: respondactivitytaskfailed_NoWireHandler{impl}, }, Signature: "RespondActivityTaskFailed(FailRequest *history.RespondActivityTaskFailedRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RespondCrossClusterTasksCompleted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondCrossClusterTasksCompleted), NoWire: respondcrossclustertaskscompleted_NoWireHandler{impl}, }, Signature: "RespondCrossClusterTasksCompleted(Request *shared.RespondCrossClusterTasksCompletedRequest) (*shared.RespondCrossClusterTasksCompletedResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RespondDecisionTaskCompleted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondDecisionTaskCompleted), NoWire: responddecisiontaskcompleted_NoWireHandler{impl}, }, Signature: "RespondDecisionTaskCompleted(CompleteRequest *history.RespondDecisionTaskCompletedRequest) (*history.RespondDecisionTaskCompletedResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "RespondDecisionTaskFailed", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondDecisionTaskFailed), NoWire: responddecisiontaskfailed_NoWireHandler{impl}, }, Signature: "RespondDecisionTaskFailed(FailedRequest *history.RespondDecisionTaskFailedRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "ScheduleDecisionTask", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ScheduleDecisionTask), NoWire: scheduledecisiontask_NoWireHandler{impl}, }, Signature: "ScheduleDecisionTask(ScheduleRequest *history.ScheduleDecisionTaskRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "SignalWithStartWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.SignalWithStartWorkflowExecution), NoWire: signalwithstartworkflowexecution_NoWireHandler{impl}, }, Signature: "SignalWithStartWorkflowExecution(SignalWithStartRequest *history.SignalWithStartWorkflowExecutionRequest) (*shared.StartWorkflowExecutionResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "SignalWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.SignalWorkflowExecution), NoWire: signalworkflowexecution_NoWireHandler{impl}, }, Signature: "SignalWorkflowExecution(SignalRequest *history.SignalWorkflowExecutionRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "StartWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.StartWorkflowExecution), NoWire: startworkflowexecution_NoWireHandler{impl}, }, Signature: "StartWorkflowExecution(StartRequest *history.StartWorkflowExecutionRequest) (*shared.StartWorkflowExecutionResponse)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "SyncActivity", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.SyncActivity), NoWire: syncactivity_NoWireHandler{impl}, }, Signature: "SyncActivity(SyncActivityRequest *history.SyncActivityRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "SyncShardStatus", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.SyncShardStatus), NoWire: syncshardstatus_NoWireHandler{impl}, }, Signature: "SyncShardStatus(SyncShardStatusRequest *history.SyncShardStatusRequest)", ThriftModule: history.ThriftModule, }, thrift.Method{ Name: "TerminateWorkflowExecution", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.TerminateWorkflowExecution), NoWire: terminateworkflowexecution_NoWireHandler{impl}, }, Signature: "TerminateWorkflowExecution(TerminateRequest *history.TerminateWorkflowExecutionRequest)", ThriftModule: history.ThriftModule, }, }, } procedures := make([]transport.Procedure, 0, 43) procedures = append(procedures, thrift.BuildProcedures(service, opts...)...) return procedures } type handler struct{ impl Interface } type yarpcErrorNamer interface{ YARPCErrorName() string } type yarpcErrorCoder interface{ YARPCErrorCode() *yarpcerrors.Code } func (h handler) CloseShard(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_CloseShard_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'CloseShard': %w", err) } appErr := h.impl.CloseShard(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_CloseShard_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeHistoryHost(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_DescribeHistoryHost_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'DescribeHistoryHost': %w", err) } success, appErr := h.impl.DescribeHistoryHost(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_DescribeHistoryHost_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeMutableState(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_DescribeMutableState_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'DescribeMutableState': %w", err) } success, appErr := h.impl.DescribeMutableState(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_DescribeMutableState_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeQueue(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_DescribeQueue_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'DescribeQueue': %w", err) } success, appErr := h.impl.DescribeQueue(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_DescribeQueue_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_DescribeWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'DescribeWorkflowExecution': %w", err) } success, appErr := h.impl.DescribeWorkflowExecution(ctx, args.DescribeRequest) hadError := appErr != nil result, err := history.HistoryService_DescribeWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetCrossClusterTasks(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_GetCrossClusterTasks_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'GetCrossClusterTasks': %w", err) } success, appErr := h.impl.GetCrossClusterTasks(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_GetCrossClusterTasks_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetDLQReplicationMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_GetDLQReplicationMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'GetDLQReplicationMessages': %w", err) } success, appErr := h.impl.GetDLQReplicationMessages(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_GetDLQReplicationMessages_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetFailoverInfo(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_GetFailoverInfo_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'GetFailoverInfo': %w", err) } success, appErr := h.impl.GetFailoverInfo(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_GetFailoverInfo_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetMutableState(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_GetMutableState_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'GetMutableState': %w", err) } success, appErr := h.impl.GetMutableState(ctx, args.GetRequest) hadError := appErr != nil result, err := history.HistoryService_GetMutableState_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetReplicationMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_GetReplicationMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'GetReplicationMessages': %w", err) } success, appErr := h.impl.GetReplicationMessages(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_GetReplicationMessages_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) MergeDLQMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_MergeDLQMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'MergeDLQMessages': %w", err) } success, appErr := h.impl.MergeDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_MergeDLQMessages_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) NotifyFailoverMarkers(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_NotifyFailoverMarkers_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'NotifyFailoverMarkers': %w", err) } appErr := h.impl.NotifyFailoverMarkers(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_NotifyFailoverMarkers_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) PollMutableState(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_PollMutableState_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'PollMutableState': %w", err) } success, appErr := h.impl.PollMutableState(ctx, args.PollRequest) hadError := appErr != nil result, err := history.HistoryService_PollMutableState_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) PurgeDLQMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_PurgeDLQMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'PurgeDLQMessages': %w", err) } appErr := h.impl.PurgeDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_PurgeDLQMessages_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) QueryWorkflow(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_QueryWorkflow_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'QueryWorkflow': %w", err) } success, appErr := h.impl.QueryWorkflow(ctx, args.QueryRequest) hadError := appErr != nil result, err := history.HistoryService_QueryWorkflow_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RatelimitUpdate(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RatelimitUpdate_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RatelimitUpdate': %w", err) } success, appErr := h.impl.RatelimitUpdate(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_RatelimitUpdate_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ReadDLQMessages(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_ReadDLQMessages_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'ReadDLQMessages': %w", err) } success, appErr := h.impl.ReadDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_ReadDLQMessages_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ReapplyEvents(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_ReapplyEvents_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'ReapplyEvents': %w", err) } appErr := h.impl.ReapplyEvents(ctx, args.ReapplyEventsRequest) hadError := appErr != nil result, err := history.HistoryService_ReapplyEvents_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RecordActivityTaskHeartbeat(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RecordActivityTaskHeartbeat_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RecordActivityTaskHeartbeat': %w", err) } success, appErr := h.impl.RecordActivityTaskHeartbeat(ctx, args.HeartbeatRequest) hadError := appErr != nil result, err := history.HistoryService_RecordActivityTaskHeartbeat_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RecordActivityTaskStarted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RecordActivityTaskStarted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RecordActivityTaskStarted': %w", err) } success, appErr := h.impl.RecordActivityTaskStarted(ctx, args.AddRequest) hadError := appErr != nil result, err := history.HistoryService_RecordActivityTaskStarted_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RecordChildExecutionCompleted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RecordChildExecutionCompleted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RecordChildExecutionCompleted': %w", err) } appErr := h.impl.RecordChildExecutionCompleted(ctx, args.CompletionRequest) hadError := appErr != nil result, err := history.HistoryService_RecordChildExecutionCompleted_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RecordDecisionTaskStarted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RecordDecisionTaskStarted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RecordDecisionTaskStarted': %w", err) } success, appErr := h.impl.RecordDecisionTaskStarted(ctx, args.AddRequest) hadError := appErr != nil result, err := history.HistoryService_RecordDecisionTaskStarted_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RefreshWorkflowTasks(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RefreshWorkflowTasks_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RefreshWorkflowTasks': %w", err) } appErr := h.impl.RefreshWorkflowTasks(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_RefreshWorkflowTasks_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RemoveSignalMutableState(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RemoveSignalMutableState_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RemoveSignalMutableState': %w", err) } appErr := h.impl.RemoveSignalMutableState(ctx, args.RemoveRequest) hadError := appErr != nil result, err := history.HistoryService_RemoveSignalMutableState_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RemoveTask(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RemoveTask_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RemoveTask': %w", err) } appErr := h.impl.RemoveTask(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_RemoveTask_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ReplicateEventsV2(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_ReplicateEventsV2_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'ReplicateEventsV2': %w", err) } appErr := h.impl.ReplicateEventsV2(ctx, args.ReplicateV2Request) hadError := appErr != nil result, err := history.HistoryService_ReplicateEventsV2_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RequestCancelWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RequestCancelWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RequestCancelWorkflowExecution': %w", err) } appErr := h.impl.RequestCancelWorkflowExecution(ctx, args.CancelRequest) hadError := appErr != nil result, err := history.HistoryService_RequestCancelWorkflowExecution_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ResetQueue(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_ResetQueue_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'ResetQueue': %w", err) } appErr := h.impl.ResetQueue(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_ResetQueue_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ResetStickyTaskList(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_ResetStickyTaskList_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'ResetStickyTaskList': %w", err) } success, appErr := h.impl.ResetStickyTaskList(ctx, args.ResetRequest) hadError := appErr != nil result, err := history.HistoryService_ResetStickyTaskList_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ResetWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_ResetWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'ResetWorkflowExecution': %w", err) } success, appErr := h.impl.ResetWorkflowExecution(ctx, args.ResetRequest) hadError := appErr != nil result, err := history.HistoryService_ResetWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondActivityTaskCanceled(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RespondActivityTaskCanceled_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RespondActivityTaskCanceled': %w", err) } appErr := h.impl.RespondActivityTaskCanceled(ctx, args.CanceledRequest) hadError := appErr != nil result, err := history.HistoryService_RespondActivityTaskCanceled_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondActivityTaskCompleted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RespondActivityTaskCompleted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RespondActivityTaskCompleted': %w", err) } appErr := h.impl.RespondActivityTaskCompleted(ctx, args.CompleteRequest) hadError := appErr != nil result, err := history.HistoryService_RespondActivityTaskCompleted_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondActivityTaskFailed(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RespondActivityTaskFailed_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RespondActivityTaskFailed': %w", err) } appErr := h.impl.RespondActivityTaskFailed(ctx, args.FailRequest) hadError := appErr != nil result, err := history.HistoryService_RespondActivityTaskFailed_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondCrossClusterTasksCompleted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RespondCrossClusterTasksCompleted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RespondCrossClusterTasksCompleted': %w", err) } success, appErr := h.impl.RespondCrossClusterTasksCompleted(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_RespondCrossClusterTasksCompleted_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondDecisionTaskCompleted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RespondDecisionTaskCompleted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RespondDecisionTaskCompleted': %w", err) } success, appErr := h.impl.RespondDecisionTaskCompleted(ctx, args.CompleteRequest) hadError := appErr != nil result, err := history.HistoryService_RespondDecisionTaskCompleted_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondDecisionTaskFailed(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_RespondDecisionTaskFailed_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'RespondDecisionTaskFailed': %w", err) } appErr := h.impl.RespondDecisionTaskFailed(ctx, args.FailedRequest) hadError := appErr != nil result, err := history.HistoryService_RespondDecisionTaskFailed_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ScheduleDecisionTask(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_ScheduleDecisionTask_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'ScheduleDecisionTask': %w", err) } appErr := h.impl.ScheduleDecisionTask(ctx, args.ScheduleRequest) hadError := appErr != nil result, err := history.HistoryService_ScheduleDecisionTask_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) SignalWithStartWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_SignalWithStartWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'SignalWithStartWorkflowExecution': %w", err) } success, appErr := h.impl.SignalWithStartWorkflowExecution(ctx, args.SignalWithStartRequest) hadError := appErr != nil result, err := history.HistoryService_SignalWithStartWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) SignalWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_SignalWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'SignalWorkflowExecution': %w", err) } appErr := h.impl.SignalWorkflowExecution(ctx, args.SignalRequest) hadError := appErr != nil result, err := history.HistoryService_SignalWorkflowExecution_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) StartWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_StartWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'StartWorkflowExecution': %w", err) } success, appErr := h.impl.StartWorkflowExecution(ctx, args.StartRequest) hadError := appErr != nil result, err := history.HistoryService_StartWorkflowExecution_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) SyncActivity(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_SyncActivity_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'SyncActivity': %w", err) } appErr := h.impl.SyncActivity(ctx, args.SyncActivityRequest) hadError := appErr != nil result, err := history.HistoryService_SyncActivity_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) SyncShardStatus(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_SyncShardStatus_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'SyncShardStatus': %w", err) } appErr := h.impl.SyncShardStatus(ctx, args.SyncShardStatusRequest) hadError := appErr != nil result, err := history.HistoryService_SyncShardStatus_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) TerminateWorkflowExecution(ctx context.Context, body wire.Value) (thrift.Response, error) { var args history.HistoryService_TerminateWorkflowExecution_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'HistoryService' procedure 'TerminateWorkflowExecution': %w", err) } appErr := h.impl.TerminateWorkflowExecution(ctx, args.TerminateRequest) hadError := appErr != nil result, err := history.HistoryService_TerminateWorkflowExecution_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type closeshard_NoWireHandler struct{ impl Interface } func (h closeshard_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_CloseShard_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'CloseShard': %w", err) } appErr := h.impl.CloseShard(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_CloseShard_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describehistoryhost_NoWireHandler struct{ impl Interface } func (h describehistoryhost_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_DescribeHistoryHost_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'DescribeHistoryHost': %w", err) } success, appErr := h.impl.DescribeHistoryHost(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_DescribeHistoryHost_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describemutablestate_NoWireHandler struct{ impl Interface } func (h describemutablestate_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_DescribeMutableState_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'DescribeMutableState': %w", err) } success, appErr := h.impl.DescribeMutableState(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_DescribeMutableState_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describequeue_NoWireHandler struct{ impl Interface } func (h describequeue_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_DescribeQueue_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'DescribeQueue': %w", err) } success, appErr := h.impl.DescribeQueue(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_DescribeQueue_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describeworkflowexecution_NoWireHandler struct{ impl Interface } func (h describeworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_DescribeWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'DescribeWorkflowExecution': %w", err) } success, appErr := h.impl.DescribeWorkflowExecution(ctx, args.DescribeRequest) hadError := appErr != nil result, err := history.HistoryService_DescribeWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getcrossclustertasks_NoWireHandler struct{ impl Interface } func (h getcrossclustertasks_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_GetCrossClusterTasks_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'GetCrossClusterTasks': %w", err) } success, appErr := h.impl.GetCrossClusterTasks(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_GetCrossClusterTasks_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getdlqreplicationmessages_NoWireHandler struct{ impl Interface } func (h getdlqreplicationmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_GetDLQReplicationMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'GetDLQReplicationMessages': %w", err) } success, appErr := h.impl.GetDLQReplicationMessages(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_GetDLQReplicationMessages_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getfailoverinfo_NoWireHandler struct{ impl Interface } func (h getfailoverinfo_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_GetFailoverInfo_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'GetFailoverInfo': %w", err) } success, appErr := h.impl.GetFailoverInfo(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_GetFailoverInfo_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getmutablestate_NoWireHandler struct{ impl Interface } func (h getmutablestate_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_GetMutableState_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'GetMutableState': %w", err) } success, appErr := h.impl.GetMutableState(ctx, args.GetRequest) hadError := appErr != nil result, err := history.HistoryService_GetMutableState_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type getreplicationmessages_NoWireHandler struct{ impl Interface } func (h getreplicationmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_GetReplicationMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'GetReplicationMessages': %w", err) } success, appErr := h.impl.GetReplicationMessages(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_GetReplicationMessages_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type mergedlqmessages_NoWireHandler struct{ impl Interface } func (h mergedlqmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_MergeDLQMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'MergeDLQMessages': %w", err) } success, appErr := h.impl.MergeDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_MergeDLQMessages_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type notifyfailovermarkers_NoWireHandler struct{ impl Interface } func (h notifyfailovermarkers_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_NotifyFailoverMarkers_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'NotifyFailoverMarkers': %w", err) } appErr := h.impl.NotifyFailoverMarkers(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_NotifyFailoverMarkers_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type pollmutablestate_NoWireHandler struct{ impl Interface } func (h pollmutablestate_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_PollMutableState_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'PollMutableState': %w", err) } success, appErr := h.impl.PollMutableState(ctx, args.PollRequest) hadError := appErr != nil result, err := history.HistoryService_PollMutableState_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type purgedlqmessages_NoWireHandler struct{ impl Interface } func (h purgedlqmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_PurgeDLQMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'PurgeDLQMessages': %w", err) } appErr := h.impl.PurgeDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_PurgeDLQMessages_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type queryworkflow_NoWireHandler struct{ impl Interface } func (h queryworkflow_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_QueryWorkflow_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'QueryWorkflow': %w", err) } success, appErr := h.impl.QueryWorkflow(ctx, args.QueryRequest) hadError := appErr != nil result, err := history.HistoryService_QueryWorkflow_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type ratelimitupdate_NoWireHandler struct{ impl Interface } func (h ratelimitupdate_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RatelimitUpdate_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RatelimitUpdate': %w", err) } success, appErr := h.impl.RatelimitUpdate(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_RatelimitUpdate_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type readdlqmessages_NoWireHandler struct{ impl Interface } func (h readdlqmessages_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_ReadDLQMessages_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'ReadDLQMessages': %w", err) } success, appErr := h.impl.ReadDLQMessages(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_ReadDLQMessages_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type reapplyevents_NoWireHandler struct{ impl Interface } func (h reapplyevents_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_ReapplyEvents_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'ReapplyEvents': %w", err) } appErr := h.impl.ReapplyEvents(ctx, args.ReapplyEventsRequest) hadError := appErr != nil result, err := history.HistoryService_ReapplyEvents_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type recordactivitytaskheartbeat_NoWireHandler struct{ impl Interface } func (h recordactivitytaskheartbeat_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RecordActivityTaskHeartbeat_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RecordActivityTaskHeartbeat': %w", err) } success, appErr := h.impl.RecordActivityTaskHeartbeat(ctx, args.HeartbeatRequest) hadError := appErr != nil result, err := history.HistoryService_RecordActivityTaskHeartbeat_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type recordactivitytaskstarted_NoWireHandler struct{ impl Interface } func (h recordactivitytaskstarted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RecordActivityTaskStarted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RecordActivityTaskStarted': %w", err) } success, appErr := h.impl.RecordActivityTaskStarted(ctx, args.AddRequest) hadError := appErr != nil result, err := history.HistoryService_RecordActivityTaskStarted_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type recordchildexecutioncompleted_NoWireHandler struct{ impl Interface } func (h recordchildexecutioncompleted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RecordChildExecutionCompleted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RecordChildExecutionCompleted': %w", err) } appErr := h.impl.RecordChildExecutionCompleted(ctx, args.CompletionRequest) hadError := appErr != nil result, err := history.HistoryService_RecordChildExecutionCompleted_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type recorddecisiontaskstarted_NoWireHandler struct{ impl Interface } func (h recorddecisiontaskstarted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RecordDecisionTaskStarted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RecordDecisionTaskStarted': %w", err) } success, appErr := h.impl.RecordDecisionTaskStarted(ctx, args.AddRequest) hadError := appErr != nil result, err := history.HistoryService_RecordDecisionTaskStarted_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type refreshworkflowtasks_NoWireHandler struct{ impl Interface } func (h refreshworkflowtasks_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RefreshWorkflowTasks_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RefreshWorkflowTasks': %w", err) } appErr := h.impl.RefreshWorkflowTasks(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_RefreshWorkflowTasks_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type removesignalmutablestate_NoWireHandler struct{ impl Interface } func (h removesignalmutablestate_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RemoveSignalMutableState_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RemoveSignalMutableState': %w", err) } appErr := h.impl.RemoveSignalMutableState(ctx, args.RemoveRequest) hadError := appErr != nil result, err := history.HistoryService_RemoveSignalMutableState_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type removetask_NoWireHandler struct{ impl Interface } func (h removetask_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RemoveTask_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RemoveTask': %w", err) } appErr := h.impl.RemoveTask(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_RemoveTask_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type replicateeventsv2_NoWireHandler struct{ impl Interface } func (h replicateeventsv2_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_ReplicateEventsV2_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'ReplicateEventsV2': %w", err) } appErr := h.impl.ReplicateEventsV2(ctx, args.ReplicateV2Request) hadError := appErr != nil result, err := history.HistoryService_ReplicateEventsV2_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type requestcancelworkflowexecution_NoWireHandler struct{ impl Interface } func (h requestcancelworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RequestCancelWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RequestCancelWorkflowExecution': %w", err) } appErr := h.impl.RequestCancelWorkflowExecution(ctx, args.CancelRequest) hadError := appErr != nil result, err := history.HistoryService_RequestCancelWorkflowExecution_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type resetqueue_NoWireHandler struct{ impl Interface } func (h resetqueue_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_ResetQueue_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'ResetQueue': %w", err) } appErr := h.impl.ResetQueue(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_ResetQueue_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type resetstickytasklist_NoWireHandler struct{ impl Interface } func (h resetstickytasklist_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_ResetStickyTaskList_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'ResetStickyTaskList': %w", err) } success, appErr := h.impl.ResetStickyTaskList(ctx, args.ResetRequest) hadError := appErr != nil result, err := history.HistoryService_ResetStickyTaskList_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type resetworkflowexecution_NoWireHandler struct{ impl Interface } func (h resetworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_ResetWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'ResetWorkflowExecution': %w", err) } success, appErr := h.impl.ResetWorkflowExecution(ctx, args.ResetRequest) hadError := appErr != nil result, err := history.HistoryService_ResetWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondactivitytaskcanceled_NoWireHandler struct{ impl Interface } func (h respondactivitytaskcanceled_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RespondActivityTaskCanceled_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RespondActivityTaskCanceled': %w", err) } appErr := h.impl.RespondActivityTaskCanceled(ctx, args.CanceledRequest) hadError := appErr != nil result, err := history.HistoryService_RespondActivityTaskCanceled_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondactivitytaskcompleted_NoWireHandler struct{ impl Interface } func (h respondactivitytaskcompleted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RespondActivityTaskCompleted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RespondActivityTaskCompleted': %w", err) } appErr := h.impl.RespondActivityTaskCompleted(ctx, args.CompleteRequest) hadError := appErr != nil result, err := history.HistoryService_RespondActivityTaskCompleted_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondactivitytaskfailed_NoWireHandler struct{ impl Interface } func (h respondactivitytaskfailed_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RespondActivityTaskFailed_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RespondActivityTaskFailed': %w", err) } appErr := h.impl.RespondActivityTaskFailed(ctx, args.FailRequest) hadError := appErr != nil result, err := history.HistoryService_RespondActivityTaskFailed_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondcrossclustertaskscompleted_NoWireHandler struct{ impl Interface } func (h respondcrossclustertaskscompleted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RespondCrossClusterTasksCompleted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RespondCrossClusterTasksCompleted': %w", err) } success, appErr := h.impl.RespondCrossClusterTasksCompleted(ctx, args.Request) hadError := appErr != nil result, err := history.HistoryService_RespondCrossClusterTasksCompleted_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type responddecisiontaskcompleted_NoWireHandler struct{ impl Interface } func (h responddecisiontaskcompleted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RespondDecisionTaskCompleted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RespondDecisionTaskCompleted': %w", err) } success, appErr := h.impl.RespondDecisionTaskCompleted(ctx, args.CompleteRequest) hadError := appErr != nil result, err := history.HistoryService_RespondDecisionTaskCompleted_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type responddecisiontaskfailed_NoWireHandler struct{ impl Interface } func (h responddecisiontaskfailed_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_RespondDecisionTaskFailed_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'RespondDecisionTaskFailed': %w", err) } appErr := h.impl.RespondDecisionTaskFailed(ctx, args.FailedRequest) hadError := appErr != nil result, err := history.HistoryService_RespondDecisionTaskFailed_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type scheduledecisiontask_NoWireHandler struct{ impl Interface } func (h scheduledecisiontask_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_ScheduleDecisionTask_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'ScheduleDecisionTask': %w", err) } appErr := h.impl.ScheduleDecisionTask(ctx, args.ScheduleRequest) hadError := appErr != nil result, err := history.HistoryService_ScheduleDecisionTask_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type signalwithstartworkflowexecution_NoWireHandler struct{ impl Interface } func (h signalwithstartworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_SignalWithStartWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'SignalWithStartWorkflowExecution': %w", err) } success, appErr := h.impl.SignalWithStartWorkflowExecution(ctx, args.SignalWithStartRequest) hadError := appErr != nil result, err := history.HistoryService_SignalWithStartWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type signalworkflowexecution_NoWireHandler struct{ impl Interface } func (h signalworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_SignalWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'SignalWorkflowExecution': %w", err) } appErr := h.impl.SignalWorkflowExecution(ctx, args.SignalRequest) hadError := appErr != nil result, err := history.HistoryService_SignalWorkflowExecution_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type startworkflowexecution_NoWireHandler struct{ impl Interface } func (h startworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_StartWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'StartWorkflowExecution': %w", err) } success, appErr := h.impl.StartWorkflowExecution(ctx, args.StartRequest) hadError := appErr != nil result, err := history.HistoryService_StartWorkflowExecution_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type syncactivity_NoWireHandler struct{ impl Interface } func (h syncactivity_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_SyncActivity_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'SyncActivity': %w", err) } appErr := h.impl.SyncActivity(ctx, args.SyncActivityRequest) hadError := appErr != nil result, err := history.HistoryService_SyncActivity_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type syncshardstatus_NoWireHandler struct{ impl Interface } func (h syncshardstatus_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_SyncShardStatus_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'SyncShardStatus': %w", err) } appErr := h.impl.SyncShardStatus(ctx, args.SyncShardStatusRequest) hadError := appErr != nil result, err := history.HistoryService_SyncShardStatus_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type terminateworkflowexecution_NoWireHandler struct{ impl Interface } func (h terminateworkflowexecution_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args history.HistoryService_TerminateWorkflowExecution_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'HistoryService' procedure 'TerminateWorkflowExecution': %w", err) } appErr := h.impl.TerminateWorkflowExecution(ctx, args.TerminateRequest) hadError := appErr != nil result, err := history.HistoryService_TerminateWorkflowExecution_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } ================================================ FILE: .gen/go/history/historyservicetest/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package historyservicetest import ( context "context" gomock "github.com/golang/mock/gomock" yarpc "go.uber.org/yarpc" history "github.com/uber/cadence/.gen/go/history" historyserviceclient "github.com/uber/cadence/.gen/go/history/historyserviceclient" replicator "github.com/uber/cadence/.gen/go/replicator" shared "github.com/uber/cadence/.gen/go/shared" ) // MockClient implements a gomock-compatible mock client for service // HistoryService. type MockClient struct { ctrl *gomock.Controller recorder *_MockClientRecorder } var _ historyserviceclient.Interface = (*MockClient)(nil) type _MockClientRecorder struct { mock *MockClient } // Build a new mock client for service HistoryService. // // mockCtrl := gomock.NewController(t) // client := historyservicetest.NewMockClient(mockCtrl) // // Use EXPECT() to set expectations on the mock. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &_MockClientRecorder{mock} return mock } // EXPECT returns an object that allows you to define an expectation on the // HistoryService mock client. func (m *MockClient) EXPECT() *_MockClientRecorder { return m.recorder } // CloseShard responds to a CloseShard call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().CloseShard(gomock.Any(), ...).Return(...) // ... := client.CloseShard(...) func (m *MockClient) CloseShard( ctx context.Context, _Request *shared.CloseShardRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "CloseShard", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) CloseShard( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "CloseShard", args...) } // DescribeHistoryHost responds to a DescribeHistoryHost call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeHistoryHost(gomock.Any(), ...).Return(...) // ... := client.DescribeHistoryHost(...) func (m *MockClient) DescribeHistoryHost( ctx context.Context, _Request *shared.DescribeHistoryHostRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeHistoryHostResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeHistoryHost", args...) success, _ = ret[i].(*shared.DescribeHistoryHostResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeHistoryHost( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeHistoryHost", args...) } // DescribeMutableState responds to a DescribeMutableState call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeMutableState(gomock.Any(), ...).Return(...) // ... := client.DescribeMutableState(...) func (m *MockClient) DescribeMutableState( ctx context.Context, _Request *history.DescribeMutableStateRequest, opts ...yarpc.CallOption, ) (success *history.DescribeMutableStateResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeMutableState", args...) success, _ = ret[i].(*history.DescribeMutableStateResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeMutableState( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeMutableState", args...) } // DescribeQueue responds to a DescribeQueue call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeQueue(gomock.Any(), ...).Return(...) // ... := client.DescribeQueue(...) func (m *MockClient) DescribeQueue( ctx context.Context, _Request *shared.DescribeQueueRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeQueueResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeQueue", args...) success, _ = ret[i].(*shared.DescribeQueueResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeQueue( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeQueue", args...) } // DescribeWorkflowExecution responds to a DescribeWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.DescribeWorkflowExecution(...) func (m *MockClient) DescribeWorkflowExecution( ctx context.Context, _DescribeRequest *history.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _DescribeRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeWorkflowExecution", args...) success, _ = ret[i].(*shared.DescribeWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeWorkflowExecution( ctx interface{}, _DescribeRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _DescribeRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeWorkflowExecution", args...) } // GetCrossClusterTasks responds to a GetCrossClusterTasks call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetCrossClusterTasks(gomock.Any(), ...).Return(...) // ... := client.GetCrossClusterTasks(...) func (m *MockClient) GetCrossClusterTasks( ctx context.Context, _Request *shared.GetCrossClusterTasksRequest, opts ...yarpc.CallOption, ) (success *shared.GetCrossClusterTasksResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetCrossClusterTasks", args...) success, _ = ret[i].(*shared.GetCrossClusterTasksResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetCrossClusterTasks( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetCrossClusterTasks", args...) } // GetDLQReplicationMessages responds to a GetDLQReplicationMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetDLQReplicationMessages(gomock.Any(), ...).Return(...) // ... := client.GetDLQReplicationMessages(...) func (m *MockClient) GetDLQReplicationMessages( ctx context.Context, _Request *replicator.GetDLQReplicationMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.GetDLQReplicationMessagesResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetDLQReplicationMessages", args...) success, _ = ret[i].(*replicator.GetDLQReplicationMessagesResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetDLQReplicationMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetDLQReplicationMessages", args...) } // GetFailoverInfo responds to a GetFailoverInfo call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetFailoverInfo(gomock.Any(), ...).Return(...) // ... := client.GetFailoverInfo(...) func (m *MockClient) GetFailoverInfo( ctx context.Context, _Request *history.GetFailoverInfoRequest, opts ...yarpc.CallOption, ) (success *history.GetFailoverInfoResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetFailoverInfo", args...) success, _ = ret[i].(*history.GetFailoverInfoResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetFailoverInfo( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetFailoverInfo", args...) } // GetMutableState responds to a GetMutableState call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetMutableState(gomock.Any(), ...).Return(...) // ... := client.GetMutableState(...) func (m *MockClient) GetMutableState( ctx context.Context, _GetRequest *history.GetMutableStateRequest, opts ...yarpc.CallOption, ) (success *history.GetMutableStateResponse, err error) { args := []interface{}{ctx, _GetRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetMutableState", args...) success, _ = ret[i].(*history.GetMutableStateResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetMutableState( ctx interface{}, _GetRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _GetRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetMutableState", args...) } // GetReplicationMessages responds to a GetReplicationMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetReplicationMessages(gomock.Any(), ...).Return(...) // ... := client.GetReplicationMessages(...) func (m *MockClient) GetReplicationMessages( ctx context.Context, _Request *replicator.GetReplicationMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.GetReplicationMessagesResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetReplicationMessages", args...) success, _ = ret[i].(*replicator.GetReplicationMessagesResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetReplicationMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetReplicationMessages", args...) } // MergeDLQMessages responds to a MergeDLQMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().MergeDLQMessages(gomock.Any(), ...).Return(...) // ... := client.MergeDLQMessages(...) func (m *MockClient) MergeDLQMessages( ctx context.Context, _Request *replicator.MergeDLQMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.MergeDLQMessagesResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "MergeDLQMessages", args...) success, _ = ret[i].(*replicator.MergeDLQMessagesResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) MergeDLQMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "MergeDLQMessages", args...) } // NotifyFailoverMarkers responds to a NotifyFailoverMarkers call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().NotifyFailoverMarkers(gomock.Any(), ...).Return(...) // ... := client.NotifyFailoverMarkers(...) func (m *MockClient) NotifyFailoverMarkers( ctx context.Context, _Request *history.NotifyFailoverMarkersRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "NotifyFailoverMarkers", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) NotifyFailoverMarkers( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "NotifyFailoverMarkers", args...) } // PollMutableState responds to a PollMutableState call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().PollMutableState(gomock.Any(), ...).Return(...) // ... := client.PollMutableState(...) func (m *MockClient) PollMutableState( ctx context.Context, _PollRequest *history.PollMutableStateRequest, opts ...yarpc.CallOption, ) (success *history.PollMutableStateResponse, err error) { args := []interface{}{ctx, _PollRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "PollMutableState", args...) success, _ = ret[i].(*history.PollMutableStateResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) PollMutableState( ctx interface{}, _PollRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _PollRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "PollMutableState", args...) } // PurgeDLQMessages responds to a PurgeDLQMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().PurgeDLQMessages(gomock.Any(), ...).Return(...) // ... := client.PurgeDLQMessages(...) func (m *MockClient) PurgeDLQMessages( ctx context.Context, _Request *replicator.PurgeDLQMessagesRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "PurgeDLQMessages", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) PurgeDLQMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "PurgeDLQMessages", args...) } // QueryWorkflow responds to a QueryWorkflow call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().QueryWorkflow(gomock.Any(), ...).Return(...) // ... := client.QueryWorkflow(...) func (m *MockClient) QueryWorkflow( ctx context.Context, _QueryRequest *history.QueryWorkflowRequest, opts ...yarpc.CallOption, ) (success *history.QueryWorkflowResponse, err error) { args := []interface{}{ctx, _QueryRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "QueryWorkflow", args...) success, _ = ret[i].(*history.QueryWorkflowResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) QueryWorkflow( ctx interface{}, _QueryRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _QueryRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "QueryWorkflow", args...) } // RatelimitUpdate responds to a RatelimitUpdate call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RatelimitUpdate(gomock.Any(), ...).Return(...) // ... := client.RatelimitUpdate(...) func (m *MockClient) RatelimitUpdate( ctx context.Context, _Request *history.RatelimitUpdateRequest, opts ...yarpc.CallOption, ) (success *history.RatelimitUpdateResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RatelimitUpdate", args...) success, _ = ret[i].(*history.RatelimitUpdateResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RatelimitUpdate( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RatelimitUpdate", args...) } // ReadDLQMessages responds to a ReadDLQMessages call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ReadDLQMessages(gomock.Any(), ...).Return(...) // ... := client.ReadDLQMessages(...) func (m *MockClient) ReadDLQMessages( ctx context.Context, _Request *replicator.ReadDLQMessagesRequest, opts ...yarpc.CallOption, ) (success *replicator.ReadDLQMessagesResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ReadDLQMessages", args...) success, _ = ret[i].(*replicator.ReadDLQMessagesResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ReadDLQMessages( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ReadDLQMessages", args...) } // ReapplyEvents responds to a ReapplyEvents call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ReapplyEvents(gomock.Any(), ...).Return(...) // ... := client.ReapplyEvents(...) func (m *MockClient) ReapplyEvents( ctx context.Context, _ReapplyEventsRequest *history.ReapplyEventsRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _ReapplyEventsRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ReapplyEvents", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ReapplyEvents( ctx interface{}, _ReapplyEventsRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ReapplyEventsRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ReapplyEvents", args...) } // RecordActivityTaskHeartbeat responds to a RecordActivityTaskHeartbeat call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), ...).Return(...) // ... := client.RecordActivityTaskHeartbeat(...) func (m *MockClient) RecordActivityTaskHeartbeat( ctx context.Context, _HeartbeatRequest *history.RecordActivityTaskHeartbeatRequest, opts ...yarpc.CallOption, ) (success *shared.RecordActivityTaskHeartbeatResponse, err error) { args := []interface{}{ctx, _HeartbeatRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RecordActivityTaskHeartbeat", args...) success, _ = ret[i].(*shared.RecordActivityTaskHeartbeatResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RecordActivityTaskHeartbeat( ctx interface{}, _HeartbeatRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _HeartbeatRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RecordActivityTaskHeartbeat", args...) } // RecordActivityTaskStarted responds to a RecordActivityTaskStarted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RecordActivityTaskStarted(gomock.Any(), ...).Return(...) // ... := client.RecordActivityTaskStarted(...) func (m *MockClient) RecordActivityTaskStarted( ctx context.Context, _AddRequest *history.RecordActivityTaskStartedRequest, opts ...yarpc.CallOption, ) (success *history.RecordActivityTaskStartedResponse, err error) { args := []interface{}{ctx, _AddRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RecordActivityTaskStarted", args...) success, _ = ret[i].(*history.RecordActivityTaskStartedResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RecordActivityTaskStarted( ctx interface{}, _AddRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _AddRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RecordActivityTaskStarted", args...) } // RecordChildExecutionCompleted responds to a RecordChildExecutionCompleted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RecordChildExecutionCompleted(gomock.Any(), ...).Return(...) // ... := client.RecordChildExecutionCompleted(...) func (m *MockClient) RecordChildExecutionCompleted( ctx context.Context, _CompletionRequest *history.RecordChildExecutionCompletedRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _CompletionRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RecordChildExecutionCompleted", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RecordChildExecutionCompleted( ctx interface{}, _CompletionRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CompletionRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RecordChildExecutionCompleted", args...) } // RecordDecisionTaskStarted responds to a RecordDecisionTaskStarted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RecordDecisionTaskStarted(gomock.Any(), ...).Return(...) // ... := client.RecordDecisionTaskStarted(...) func (m *MockClient) RecordDecisionTaskStarted( ctx context.Context, _AddRequest *history.RecordDecisionTaskStartedRequest, opts ...yarpc.CallOption, ) (success *history.RecordDecisionTaskStartedResponse, err error) { args := []interface{}{ctx, _AddRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RecordDecisionTaskStarted", args...) success, _ = ret[i].(*history.RecordDecisionTaskStartedResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RecordDecisionTaskStarted( ctx interface{}, _AddRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _AddRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RecordDecisionTaskStarted", args...) } // RefreshWorkflowTasks responds to a RefreshWorkflowTasks call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RefreshWorkflowTasks(gomock.Any(), ...).Return(...) // ... := client.RefreshWorkflowTasks(...) func (m *MockClient) RefreshWorkflowTasks( ctx context.Context, _Request *history.RefreshWorkflowTasksRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RefreshWorkflowTasks", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RefreshWorkflowTasks( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RefreshWorkflowTasks", args...) } // RemoveSignalMutableState responds to a RemoveSignalMutableState call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RemoveSignalMutableState(gomock.Any(), ...).Return(...) // ... := client.RemoveSignalMutableState(...) func (m *MockClient) RemoveSignalMutableState( ctx context.Context, _RemoveRequest *history.RemoveSignalMutableStateRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _RemoveRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RemoveSignalMutableState", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RemoveSignalMutableState( ctx interface{}, _RemoveRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _RemoveRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RemoveSignalMutableState", args...) } // RemoveTask responds to a RemoveTask call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RemoveTask(gomock.Any(), ...).Return(...) // ... := client.RemoveTask(...) func (m *MockClient) RemoveTask( ctx context.Context, _Request *shared.RemoveTaskRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RemoveTask", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RemoveTask( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RemoveTask", args...) } // ReplicateEventsV2 responds to a ReplicateEventsV2 call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ReplicateEventsV2(gomock.Any(), ...).Return(...) // ... := client.ReplicateEventsV2(...) func (m *MockClient) ReplicateEventsV2( ctx context.Context, _ReplicateV2Request *history.ReplicateEventsV2Request, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _ReplicateV2Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ReplicateEventsV2", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ReplicateEventsV2( ctx interface{}, _ReplicateV2Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ReplicateV2Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ReplicateEventsV2", args...) } // RequestCancelWorkflowExecution responds to a RequestCancelWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.RequestCancelWorkflowExecution(...) func (m *MockClient) RequestCancelWorkflowExecution( ctx context.Context, _CancelRequest *history.RequestCancelWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _CancelRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RequestCancelWorkflowExecution", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RequestCancelWorkflowExecution( ctx interface{}, _CancelRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CancelRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RequestCancelWorkflowExecution", args...) } // ResetQueue responds to a ResetQueue call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ResetQueue(gomock.Any(), ...).Return(...) // ... := client.ResetQueue(...) func (m *MockClient) ResetQueue( ctx context.Context, _Request *shared.ResetQueueRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ResetQueue", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ResetQueue( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ResetQueue", args...) } // ResetStickyTaskList responds to a ResetStickyTaskList call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ResetStickyTaskList(gomock.Any(), ...).Return(...) // ... := client.ResetStickyTaskList(...) func (m *MockClient) ResetStickyTaskList( ctx context.Context, _ResetRequest *history.ResetStickyTaskListRequest, opts ...yarpc.CallOption, ) (success *history.ResetStickyTaskListResponse, err error) { args := []interface{}{ctx, _ResetRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ResetStickyTaskList", args...) success, _ = ret[i].(*history.ResetStickyTaskListResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ResetStickyTaskList( ctx interface{}, _ResetRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ResetRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ResetStickyTaskList", args...) } // ResetWorkflowExecution responds to a ResetWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ResetWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.ResetWorkflowExecution(...) func (m *MockClient) ResetWorkflowExecution( ctx context.Context, _ResetRequest *history.ResetWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.ResetWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _ResetRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ResetWorkflowExecution", args...) success, _ = ret[i].(*shared.ResetWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ResetWorkflowExecution( ctx interface{}, _ResetRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ResetRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ResetWorkflowExecution", args...) } // RespondActivityTaskCanceled responds to a RespondActivityTaskCanceled call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondActivityTaskCanceled(gomock.Any(), ...).Return(...) // ... := client.RespondActivityTaskCanceled(...) func (m *MockClient) RespondActivityTaskCanceled( ctx context.Context, _CanceledRequest *history.RespondActivityTaskCanceledRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _CanceledRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondActivityTaskCanceled", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondActivityTaskCanceled( ctx interface{}, _CanceledRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CanceledRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondActivityTaskCanceled", args...) } // RespondActivityTaskCompleted responds to a RespondActivityTaskCompleted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondActivityTaskCompleted(gomock.Any(), ...).Return(...) // ... := client.RespondActivityTaskCompleted(...) func (m *MockClient) RespondActivityTaskCompleted( ctx context.Context, _CompleteRequest *history.RespondActivityTaskCompletedRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _CompleteRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondActivityTaskCompleted", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondActivityTaskCompleted( ctx interface{}, _CompleteRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CompleteRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondActivityTaskCompleted", args...) } // RespondActivityTaskFailed responds to a RespondActivityTaskFailed call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondActivityTaskFailed(gomock.Any(), ...).Return(...) // ... := client.RespondActivityTaskFailed(...) func (m *MockClient) RespondActivityTaskFailed( ctx context.Context, _FailRequest *history.RespondActivityTaskFailedRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _FailRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondActivityTaskFailed", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondActivityTaskFailed( ctx interface{}, _FailRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _FailRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondActivityTaskFailed", args...) } // RespondCrossClusterTasksCompleted responds to a RespondCrossClusterTasksCompleted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondCrossClusterTasksCompleted(gomock.Any(), ...).Return(...) // ... := client.RespondCrossClusterTasksCompleted(...) func (m *MockClient) RespondCrossClusterTasksCompleted( ctx context.Context, _Request *shared.RespondCrossClusterTasksCompletedRequest, opts ...yarpc.CallOption, ) (success *shared.RespondCrossClusterTasksCompletedResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondCrossClusterTasksCompleted", args...) success, _ = ret[i].(*shared.RespondCrossClusterTasksCompletedResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondCrossClusterTasksCompleted( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondCrossClusterTasksCompleted", args...) } // RespondDecisionTaskCompleted responds to a RespondDecisionTaskCompleted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), ...).Return(...) // ... := client.RespondDecisionTaskCompleted(...) func (m *MockClient) RespondDecisionTaskCompleted( ctx context.Context, _CompleteRequest *history.RespondDecisionTaskCompletedRequest, opts ...yarpc.CallOption, ) (success *history.RespondDecisionTaskCompletedResponse, err error) { args := []interface{}{ctx, _CompleteRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondDecisionTaskCompleted", args...) success, _ = ret[i].(*history.RespondDecisionTaskCompletedResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondDecisionTaskCompleted( ctx interface{}, _CompleteRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _CompleteRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondDecisionTaskCompleted", args...) } // RespondDecisionTaskFailed responds to a RespondDecisionTaskFailed call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondDecisionTaskFailed(gomock.Any(), ...).Return(...) // ... := client.RespondDecisionTaskFailed(...) func (m *MockClient) RespondDecisionTaskFailed( ctx context.Context, _FailedRequest *history.RespondDecisionTaskFailedRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _FailedRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondDecisionTaskFailed", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondDecisionTaskFailed( ctx interface{}, _FailedRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _FailedRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondDecisionTaskFailed", args...) } // ScheduleDecisionTask responds to a ScheduleDecisionTask call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ScheduleDecisionTask(gomock.Any(), ...).Return(...) // ... := client.ScheduleDecisionTask(...) func (m *MockClient) ScheduleDecisionTask( ctx context.Context, _ScheduleRequest *history.ScheduleDecisionTaskRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _ScheduleRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ScheduleDecisionTask", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ScheduleDecisionTask( ctx interface{}, _ScheduleRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _ScheduleRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ScheduleDecisionTask", args...) } // SignalWithStartWorkflowExecution responds to a SignalWithStartWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.SignalWithStartWorkflowExecution(...) func (m *MockClient) SignalWithStartWorkflowExecution( ctx context.Context, _SignalWithStartRequest *history.SignalWithStartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.StartWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _SignalWithStartRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "SignalWithStartWorkflowExecution", args...) success, _ = ret[i].(*shared.StartWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) SignalWithStartWorkflowExecution( ctx interface{}, _SignalWithStartRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _SignalWithStartRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "SignalWithStartWorkflowExecution", args...) } // SignalWorkflowExecution responds to a SignalWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().SignalWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.SignalWorkflowExecution(...) func (m *MockClient) SignalWorkflowExecution( ctx context.Context, _SignalRequest *history.SignalWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _SignalRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "SignalWorkflowExecution", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) SignalWorkflowExecution( ctx interface{}, _SignalRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _SignalRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "SignalWorkflowExecution", args...) } // StartWorkflowExecution responds to a StartWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().StartWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.StartWorkflowExecution(...) func (m *MockClient) StartWorkflowExecution( ctx context.Context, _StartRequest *history.StartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (success *shared.StartWorkflowExecutionResponse, err error) { args := []interface{}{ctx, _StartRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "StartWorkflowExecution", args...) success, _ = ret[i].(*shared.StartWorkflowExecutionResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) StartWorkflowExecution( ctx interface{}, _StartRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _StartRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "StartWorkflowExecution", args...) } // SyncActivity responds to a SyncActivity call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().SyncActivity(gomock.Any(), ...).Return(...) // ... := client.SyncActivity(...) func (m *MockClient) SyncActivity( ctx context.Context, _SyncActivityRequest *history.SyncActivityRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _SyncActivityRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "SyncActivity", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) SyncActivity( ctx interface{}, _SyncActivityRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _SyncActivityRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "SyncActivity", args...) } // SyncShardStatus responds to a SyncShardStatus call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().SyncShardStatus(gomock.Any(), ...).Return(...) // ... := client.SyncShardStatus(...) func (m *MockClient) SyncShardStatus( ctx context.Context, _SyncShardStatusRequest *history.SyncShardStatusRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _SyncShardStatusRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "SyncShardStatus", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) SyncShardStatus( ctx interface{}, _SyncShardStatusRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _SyncShardStatusRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "SyncShardStatus", args...) } // TerminateWorkflowExecution responds to a TerminateWorkflowExecution call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().TerminateWorkflowExecution(gomock.Any(), ...).Return(...) // ... := client.TerminateWorkflowExecution(...) func (m *MockClient) TerminateWorkflowExecution( ctx context.Context, _TerminateRequest *history.TerminateWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _TerminateRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "TerminateWorkflowExecution", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) TerminateWorkflowExecution( ctx interface{}, _TerminateRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _TerminateRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "TerminateWorkflowExecution", args...) } ================================================ FILE: .gen/go/history/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package history import yarpcerrors "go.uber.org/yarpc/yarpcerrors" // YARPCErrorCode returns nil for EventAlreadyStartedError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *EventAlreadyStartedError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for EventAlreadyStartedError. func (e *EventAlreadyStartedError) YARPCErrorName() string { return "EventAlreadyStartedError" } // YARPCErrorCode returns nil for ShardOwnershipLostError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *ShardOwnershipLostError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for ShardOwnershipLostError. func (e *ShardOwnershipLostError) YARPCErrorName() string { return "ShardOwnershipLostError" } ================================================ FILE: .gen/go/indexer/indexer.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package indexer import ( bytes "bytes" base64 "encoding/base64" json "encoding/json" fmt "fmt" math "math" strconv "strconv" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" shared "github.com/uber/cadence/.gen/go/shared" ) type Field struct { Type *FieldType `json:"type,omitempty"` StringData *string `json:"stringData,omitempty"` IntData *int64 `json:"intData,omitempty"` BoolData *bool `json:"boolData,omitempty"` BinaryData []byte `json:"binaryData,omitempty"` } // ToWire translates a Field struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *Field) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Type != nil { w, err = v.Type.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StringData != nil { w, err = wire.NewValueString(*(v.StringData)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.IntData != nil { w, err = wire.NewValueI64(*(v.IntData)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.BoolData != nil { w, err = wire.NewValueBool(*(v.BoolData)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.BinaryData != nil { w, err = wire.NewValueBinary(v.BinaryData), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _FieldType_Read(w wire.Value) (FieldType, error) { var v FieldType err := v.FromWire(w) return v, err } // FromWire deserializes a Field struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a Field struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v Field // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *Field) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x FieldType x, err = _FieldType_Read(field.Value) v.Type = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.StringData = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.IntData = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.BoolData = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.BinaryData, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a Field struct directly into bytes, without going // through an intermediary type. // // An error is returned if a Field struct could not be encoded. func (v *Field) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Type != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.Type.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StringData != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.StringData)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IntData != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.IntData)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BoolData != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.BoolData)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BinaryData != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.BinaryData); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _FieldType_Decode(sr stream.Reader) (FieldType, error) { var v FieldType err := v.Decode(sr) return v, err } // Decode deserializes a Field struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a Field struct could not be generated from the wire // representation. func (v *Field) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x FieldType x, err = _FieldType_Decode(sr) v.Type = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.StringData = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.IntData = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.BoolData = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.BinaryData, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a Field // struct. func (v *Field) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Type != nil { fields[i] = fmt.Sprintf("Type: %v", *(v.Type)) i++ } if v.StringData != nil { fields[i] = fmt.Sprintf("StringData: %v", *(v.StringData)) i++ } if v.IntData != nil { fields[i] = fmt.Sprintf("IntData: %v", *(v.IntData)) i++ } if v.BoolData != nil { fields[i] = fmt.Sprintf("BoolData: %v", *(v.BoolData)) i++ } if v.BinaryData != nil { fields[i] = fmt.Sprintf("BinaryData: %v", v.BinaryData) i++ } return fmt.Sprintf("Field{%v}", strings.Join(fields[:i], ", ")) } func _FieldType_EqualsPtr(lhs, rhs *FieldType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _I64_EqualsPtr(lhs, rhs *int64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _Bool_EqualsPtr(lhs, rhs *bool) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this Field match the // provided Field. // // This function performs a deep comparison. func (v *Field) Equals(rhs *Field) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_FieldType_EqualsPtr(v.Type, rhs.Type) { return false } if !_String_EqualsPtr(v.StringData, rhs.StringData) { return false } if !_I64_EqualsPtr(v.IntData, rhs.IntData) { return false } if !_Bool_EqualsPtr(v.BoolData, rhs.BoolData) { return false } if !((v.BinaryData == nil && rhs.BinaryData == nil) || (v.BinaryData != nil && rhs.BinaryData != nil && bytes.Equal(v.BinaryData, rhs.BinaryData))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of Field. func (v *Field) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Type != nil { err = multierr.Append(err, enc.AddObject("type", *v.Type)) } if v.StringData != nil { enc.AddString("stringData", *v.StringData) } if v.IntData != nil { enc.AddInt64("intData", *v.IntData) } if v.BoolData != nil { enc.AddBool("boolData", *v.BoolData) } if v.BinaryData != nil { enc.AddString("binaryData", base64.StdEncoding.EncodeToString(v.BinaryData)) } return err } // GetType returns the value of Type if it is set or its // zero value if it is unset. func (v *Field) GetType() (o FieldType) { if v != nil && v.Type != nil { return *v.Type } return } // IsSetType returns true if Type is not nil. func (v *Field) IsSetType() bool { return v != nil && v.Type != nil } // GetStringData returns the value of StringData if it is set or its // zero value if it is unset. func (v *Field) GetStringData() (o string) { if v != nil && v.StringData != nil { return *v.StringData } return } // IsSetStringData returns true if StringData is not nil. func (v *Field) IsSetStringData() bool { return v != nil && v.StringData != nil } // GetIntData returns the value of IntData if it is set or its // zero value if it is unset. func (v *Field) GetIntData() (o int64) { if v != nil && v.IntData != nil { return *v.IntData } return } // IsSetIntData returns true if IntData is not nil. func (v *Field) IsSetIntData() bool { return v != nil && v.IntData != nil } // GetBoolData returns the value of BoolData if it is set or its // zero value if it is unset. func (v *Field) GetBoolData() (o bool) { if v != nil && v.BoolData != nil { return *v.BoolData } return } // IsSetBoolData returns true if BoolData is not nil. func (v *Field) IsSetBoolData() bool { return v != nil && v.BoolData != nil } // GetBinaryData returns the value of BinaryData if it is set or its // zero value if it is unset. func (v *Field) GetBinaryData() (o []byte) { if v != nil && v.BinaryData != nil { return v.BinaryData } return } // IsSetBinaryData returns true if BinaryData is not nil. func (v *Field) IsSetBinaryData() bool { return v != nil && v.BinaryData != nil } type FieldType int32 const ( FieldTypeString FieldType = 0 FieldTypeInt FieldType = 1 FieldTypeBool FieldType = 2 FieldTypeBinary FieldType = 3 ) // FieldType_Values returns all recognized values of FieldType. func FieldType_Values() []FieldType { return []FieldType{ FieldTypeString, FieldTypeInt, FieldTypeBool, FieldTypeBinary, } } // UnmarshalText tries to decode FieldType from a byte slice // containing its name. // // var v FieldType // err := v.UnmarshalText([]byte("String")) func (v *FieldType) UnmarshalText(value []byte) error { switch s := string(value); s { case "String": *v = FieldTypeString return nil case "Int": *v = FieldTypeInt return nil case "Bool": *v = FieldTypeBool return nil case "Binary": *v = FieldTypeBinary return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "FieldType", err) } *v = FieldType(val) return nil } } // MarshalText encodes FieldType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v FieldType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("String"), nil case 1: return []byte("Int"), nil case 2: return []byte("Bool"), nil case 3: return []byte("Binary"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FieldType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v FieldType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "String") case 1: enc.AddString("name", "Int") case 2: enc.AddString("name", "Bool") case 3: enc.AddString("name", "Binary") } return nil } // Ptr returns a pointer to this enum value. func (v FieldType) Ptr() *FieldType { return &v } // Encode encodes FieldType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v FieldType // return v.Encode(sWriter) func (v FieldType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates FieldType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v FieldType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes FieldType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return FieldType(0), err // } // // var v FieldType // if err := v.FromWire(x); err != nil { // return FieldType(0), err // } // return v, nil func (v *FieldType) FromWire(w wire.Value) error { *v = (FieldType)(w.GetI32()) return nil } // Decode reads off the encoded FieldType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v FieldType // if err := v.Decode(sReader); err != nil { // return FieldType(0), err // } // return v, nil func (v *FieldType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (FieldType)(i) return nil } // String returns a readable string representation of FieldType. func (v FieldType) String() string { w := int32(v) switch w { case 0: return "String" case 1: return "Int" case 2: return "Bool" case 3: return "Binary" } return fmt.Sprintf("FieldType(%d)", w) } // Equals returns true if this FieldType value matches the provided // value. func (v FieldType) Equals(rhs FieldType) bool { return v == rhs } // MarshalJSON serializes FieldType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v FieldType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"String\""), nil case 1: return ([]byte)("\"Int\""), nil case 2: return ([]byte)("\"Bool\""), nil case 3: return ([]byte)("\"Binary\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode FieldType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *FieldType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "FieldType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "FieldType") } *v = (FieldType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "FieldType") } } type Message struct { MessageType *MessageType `json:"messageType,omitempty"` DomainID *string `json:"domainID,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID *string `json:"runID,omitempty"` Version *int64 `json:"version,omitempty"` Fields map[string]*Field `json:"fields,omitempty"` VisibilityOperation *VisibilityOperation `json:"visibilityOperation,omitempty"` } type _Map_String_Field_MapItemList map[string]*Field func (m _Map_String_Field_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*Field', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_Field_MapItemList) Size() int { return len(m) } func (_Map_String_Field_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_Field_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_Field_MapItemList) Close() {} // ToWire translates a Message struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *Message) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.MessageType != nil { w, err = v.MessageType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.DomainID != nil { w, err = wire.NewValueString(*(v.DomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueString(*(v.RunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Fields != nil { w, err = wire.NewValueMap(_Map_String_Field_MapItemList(v.Fields)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.VisibilityOperation != nil { w, err = v.VisibilityOperation.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _MessageType_Read(w wire.Value) (MessageType, error) { var v MessageType err := v.FromWire(w) return v, err } func _Field_Read(w wire.Value) (*Field, error) { var v Field err := v.FromWire(w) return &v, err } func _Map_String_Field_Read(m wire.MapItemList) (map[string]*Field, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*Field, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _Field_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } func _VisibilityOperation_Read(w wire.Value) (VisibilityOperation, error) { var v VisibilityOperation err := v.FromWire(w) return v, err } // FromWire deserializes a Message struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a Message struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v Message // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *Message) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x MessageType x, err = _MessageType_Read(field.Value) v.MessageType = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TMap { v.Fields, err = _Map_String_Field_Read(field.Value.GetMap()) if err != nil { return err } } case 70: if field.Value.Type() == wire.TI32 { var x VisibilityOperation x, err = _VisibilityOperation_Read(field.Value) v.VisibilityOperation = &x if err != nil { return err } } } } return nil } func _Map_String_Field_Encode(val map[string]*Field, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*Field', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a Message struct directly into bytes, without going // through an intermediary type. // // An error is returned if a Message struct could not be encoded. func (v *Message) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.MessageType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.MessageType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Fields != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TMap}); err != nil { return err } if err := _Map_String_Field_Encode(v.Fields, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityOperation != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI32}); err != nil { return err } if err := v.VisibilityOperation.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _MessageType_Decode(sr stream.Reader) (MessageType, error) { var v MessageType err := v.Decode(sr) return v, err } func _Field_Decode(sr stream.Reader) (*Field, error) { var v Field err := v.Decode(sr) return &v, err } func _Map_String_Field_Decode(sr stream.Reader) (map[string]*Field, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*Field, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _Field_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } func _VisibilityOperation_Decode(sr stream.Reader) (VisibilityOperation, error) { var v VisibilityOperation err := v.Decode(sr) return v, err } // Decode deserializes a Message struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a Message struct could not be generated from the wire // representation. func (v *Message) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x MessageType x, err = _MessageType_Decode(sr) v.MessageType = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TMap: v.Fields, err = _Map_String_Field_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI32: var x VisibilityOperation x, err = _VisibilityOperation_Decode(sr) v.VisibilityOperation = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a Message // struct. func (v *Message) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.MessageType != nil { fields[i] = fmt.Sprintf("MessageType: %v", *(v.MessageType)) i++ } if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", *(v.DomainID)) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", *(v.RunID)) i++ } if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.Fields != nil { fields[i] = fmt.Sprintf("Fields: %v", v.Fields) i++ } if v.VisibilityOperation != nil { fields[i] = fmt.Sprintf("VisibilityOperation: %v", *(v.VisibilityOperation)) i++ } return fmt.Sprintf("Message{%v}", strings.Join(fields[:i], ", ")) } func _MessageType_EqualsPtr(lhs, rhs *MessageType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } func _Map_String_Field_Equals(lhs, rhs map[string]*Field) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } func _VisibilityOperation_EqualsPtr(lhs, rhs *VisibilityOperation) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this Message match the // provided Message. // // This function performs a deep comparison. func (v *Message) Equals(rhs *Message) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_MessageType_EqualsPtr(v.MessageType, rhs.MessageType) { return false } if !_String_EqualsPtr(v.DomainID, rhs.DomainID) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !_String_EqualsPtr(v.RunID, rhs.RunID) { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !((v.Fields == nil && rhs.Fields == nil) || (v.Fields != nil && rhs.Fields != nil && _Map_String_Field_Equals(v.Fields, rhs.Fields))) { return false } if !_VisibilityOperation_EqualsPtr(v.VisibilityOperation, rhs.VisibilityOperation) { return false } return true } type _Map_String_Field_Zapper map[string]*Field // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_Field_Zapper. func (m _Map_String_Field_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of Message. func (v *Message) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.MessageType != nil { err = multierr.Append(err, enc.AddObject("messageType", *v.MessageType)) } if v.DomainID != nil { enc.AddString("domainID", *v.DomainID) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", *v.RunID) } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.Fields != nil { err = multierr.Append(err, enc.AddObject("fields", (_Map_String_Field_Zapper)(v.Fields))) } if v.VisibilityOperation != nil { err = multierr.Append(err, enc.AddObject("visibilityOperation", *v.VisibilityOperation)) } return err } // GetMessageType returns the value of MessageType if it is set or its // zero value if it is unset. func (v *Message) GetMessageType() (o MessageType) { if v != nil && v.MessageType != nil { return *v.MessageType } return } // IsSetMessageType returns true if MessageType is not nil. func (v *Message) IsSetMessageType() bool { return v != nil && v.MessageType != nil } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *Message) GetDomainID() (o string) { if v != nil && v.DomainID != nil { return *v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *Message) IsSetDomainID() bool { return v != nil && v.DomainID != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *Message) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *Message) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *Message) GetRunID() (o string) { if v != nil && v.RunID != nil { return *v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *Message) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *Message) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *Message) IsSetVersion() bool { return v != nil && v.Version != nil } // GetFields returns the value of Fields if it is set or its // zero value if it is unset. func (v *Message) GetFields() (o map[string]*Field) { if v != nil && v.Fields != nil { return v.Fields } return } // IsSetFields returns true if Fields is not nil. func (v *Message) IsSetFields() bool { return v != nil && v.Fields != nil } // GetVisibilityOperation returns the value of VisibilityOperation if it is set or its // zero value if it is unset. func (v *Message) GetVisibilityOperation() (o VisibilityOperation) { if v != nil && v.VisibilityOperation != nil { return *v.VisibilityOperation } return } // IsSetVisibilityOperation returns true if VisibilityOperation is not nil. func (v *Message) IsSetVisibilityOperation() bool { return v != nil && v.VisibilityOperation != nil } type MessageType int32 const ( MessageTypeIndex MessageType = 0 MessageTypeDelete MessageType = 1 MessageTypeCreate MessageType = 2 ) // MessageType_Values returns all recognized values of MessageType. func MessageType_Values() []MessageType { return []MessageType{ MessageTypeIndex, MessageTypeDelete, MessageTypeCreate, } } // UnmarshalText tries to decode MessageType from a byte slice // containing its name. // // var v MessageType // err := v.UnmarshalText([]byte("Index")) func (v *MessageType) UnmarshalText(value []byte) error { switch s := string(value); s { case "Index": *v = MessageTypeIndex return nil case "Delete": *v = MessageTypeDelete return nil case "Create": *v = MessageTypeCreate return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "MessageType", err) } *v = MessageType(val) return nil } } // MarshalText encodes MessageType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v MessageType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("Index"), nil case 1: return []byte("Delete"), nil case 2: return []byte("Create"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MessageType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v MessageType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "Index") case 1: enc.AddString("name", "Delete") case 2: enc.AddString("name", "Create") } return nil } // Ptr returns a pointer to this enum value. func (v MessageType) Ptr() *MessageType { return &v } // Encode encodes MessageType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v MessageType // return v.Encode(sWriter) func (v MessageType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates MessageType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v MessageType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes MessageType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return MessageType(0), err // } // // var v MessageType // if err := v.FromWire(x); err != nil { // return MessageType(0), err // } // return v, nil func (v *MessageType) FromWire(w wire.Value) error { *v = (MessageType)(w.GetI32()) return nil } // Decode reads off the encoded MessageType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v MessageType // if err := v.Decode(sReader); err != nil { // return MessageType(0), err // } // return v, nil func (v *MessageType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (MessageType)(i) return nil } // String returns a readable string representation of MessageType. func (v MessageType) String() string { w := int32(v) switch w { case 0: return "Index" case 1: return "Delete" case 2: return "Create" } return fmt.Sprintf("MessageType(%d)", w) } // Equals returns true if this MessageType value matches the provided // value. func (v MessageType) Equals(rhs MessageType) bool { return v == rhs } // MarshalJSON serializes MessageType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v MessageType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"Index\""), nil case 1: return ([]byte)("\"Delete\""), nil case 2: return ([]byte)("\"Create\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode MessageType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *MessageType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "MessageType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "MessageType") } *v = (MessageType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "MessageType") } } type PinotMessage struct { WorkflowID *string `json:"workflowID,omitempty"` Payload []byte `json:"payload,omitempty"` } // ToWire translates a PinotMessage struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PinotMessage) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Payload != nil { w, err = wire.NewValueBinary(v.Payload), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a PinotMessage struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PinotMessage struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PinotMessage // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PinotMessage) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Payload, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a PinotMessage struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PinotMessage struct could not be encoded. func (v *PinotMessage) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Payload != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Payload); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a PinotMessage struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PinotMessage struct could not be generated from the wire // representation. func (v *PinotMessage) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Payload, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PinotMessage // struct. func (v *PinotMessage) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.Payload != nil { fields[i] = fmt.Sprintf("Payload: %v", v.Payload) i++ } return fmt.Sprintf("PinotMessage{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PinotMessage match the // provided PinotMessage. // // This function performs a deep comparison. func (v *PinotMessage) Equals(rhs *PinotMessage) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !((v.Payload == nil && rhs.Payload == nil) || (v.Payload != nil && rhs.Payload != nil && bytes.Equal(v.Payload, rhs.Payload))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PinotMessage. func (v *PinotMessage) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.Payload != nil { enc.AddString("payload", base64.StdEncoding.EncodeToString(v.Payload)) } return err } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *PinotMessage) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *PinotMessage) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetPayload returns the value of Payload if it is set or its // zero value if it is unset. func (v *PinotMessage) GetPayload() (o []byte) { if v != nil && v.Payload != nil { return v.Payload } return } // IsSetPayload returns true if Payload is not nil. func (v *PinotMessage) IsSetPayload() bool { return v != nil && v.Payload != nil } type VisibilityOperation int32 const ( VisibilityOperationRecordStarted VisibilityOperation = 0 VisibilityOperationRecordClosed VisibilityOperation = 1 VisibilityOperationUpsertSearchAttributes VisibilityOperation = 2 ) // VisibilityOperation_Values returns all recognized values of VisibilityOperation. func VisibilityOperation_Values() []VisibilityOperation { return []VisibilityOperation{ VisibilityOperationRecordStarted, VisibilityOperationRecordClosed, VisibilityOperationUpsertSearchAttributes, } } // UnmarshalText tries to decode VisibilityOperation from a byte slice // containing its name. // // var v VisibilityOperation // err := v.UnmarshalText([]byte("RecordStarted")) func (v *VisibilityOperation) UnmarshalText(value []byte) error { switch s := string(value); s { case "RecordStarted": *v = VisibilityOperationRecordStarted return nil case "RecordClosed": *v = VisibilityOperationRecordClosed return nil case "UpsertSearchAttributes": *v = VisibilityOperationUpsertSearchAttributes return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "VisibilityOperation", err) } *v = VisibilityOperation(val) return nil } } // MarshalText encodes VisibilityOperation to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v VisibilityOperation) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("RecordStarted"), nil case 1: return []byte("RecordClosed"), nil case 2: return []byte("UpsertSearchAttributes"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of VisibilityOperation. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v VisibilityOperation) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "RecordStarted") case 1: enc.AddString("name", "RecordClosed") case 2: enc.AddString("name", "UpsertSearchAttributes") } return nil } // Ptr returns a pointer to this enum value. func (v VisibilityOperation) Ptr() *VisibilityOperation { return &v } // Encode encodes VisibilityOperation directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v VisibilityOperation // return v.Encode(sWriter) func (v VisibilityOperation) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates VisibilityOperation into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v VisibilityOperation) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes VisibilityOperation from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return VisibilityOperation(0), err // } // // var v VisibilityOperation // if err := v.FromWire(x); err != nil { // return VisibilityOperation(0), err // } // return v, nil func (v *VisibilityOperation) FromWire(w wire.Value) error { *v = (VisibilityOperation)(w.GetI32()) return nil } // Decode reads off the encoded VisibilityOperation directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v VisibilityOperation // if err := v.Decode(sReader); err != nil { // return VisibilityOperation(0), err // } // return v, nil func (v *VisibilityOperation) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (VisibilityOperation)(i) return nil } // String returns a readable string representation of VisibilityOperation. func (v VisibilityOperation) String() string { w := int32(v) switch w { case 0: return "RecordStarted" case 1: return "RecordClosed" case 2: return "UpsertSearchAttributes" } return fmt.Sprintf("VisibilityOperation(%d)", w) } // Equals returns true if this VisibilityOperation value matches the provided // value. func (v VisibilityOperation) Equals(rhs VisibilityOperation) bool { return v == rhs } // MarshalJSON serializes VisibilityOperation into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v VisibilityOperation) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"RecordStarted\""), nil case 1: return ([]byte)("\"RecordClosed\""), nil case 2: return ([]byte)("\"UpsertSearchAttributes\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode VisibilityOperation from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *VisibilityOperation) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "VisibilityOperation") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "VisibilityOperation") } *v = (VisibilityOperation)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "VisibilityOperation") } } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "indexer", Package: "github.com/uber/cadence/.gen/go/indexer", FilePath: "indexer.thrift", SHA1: "d5b60fa082530a64a77c19c573c0f3ccfce5c408", Includes: []*thriftreflect.ThriftModule{ shared.ThriftModule, }, Raw: rawIDL, } const rawIDL = "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nnamespace java com.uber.cadence.indexer\n\ninclude \"shared.thrift\"\n\nenum MessageType {\n Index\n Delete\n Create\n}\n\nenum VisibilityOperation {\n RecordStarted\n RecordClosed\n UpsertSearchAttributes\n}\n\nenum FieldType {\n String\n Int\n Bool\n Binary\n}\n\nstruct Field {\n 10: optional FieldType type\n 20: optional string stringData\n 30: optional i64 (js.type = \"Long\") intData\n 40: optional bool boolData\n 50: optional binary binaryData\n}\n\nstruct Message {\n 10: optional MessageType messageType\n 20: optional string domainID\n 30: optional string workflowID\n 40: optional string runID\n 50: optional i64 (js.type = \"Long\") version\n 60: optional map fields\n 70: optional VisibilityOperation visibilityOperation\n}\n\nstruct PinotMessage {\n 10: optional string workflowID\n 20: optional binary payload\n}" ================================================ FILE: .gen/go/indexer/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package indexer ================================================ FILE: .gen/go/matching/matching.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package matching import ( bytes "bytes" base64 "encoding/base64" json "encoding/json" errors "errors" fmt "fmt" math "math" strconv "strconv" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" shared "github.com/uber/cadence/.gen/go/shared" ) type ActivityTaskDispatchInfo struct { ScheduledEvent *shared.HistoryEvent `json:"scheduledEvent,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Attempt *int64 `json:"attempt,omitempty"` ScheduledTimestampOfThisAttempt *int64 `json:"scheduledTimestampOfThisAttempt,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` HeartbeatDetails []byte `json:"heartbeatDetails,omitempty"` WorkflowType *shared.WorkflowType `json:"workflowType,omitempty"` WorkflowDomain *string `json:"workflowDomain,omitempty"` } // ToWire translates a ActivityTaskDispatchInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityTaskDispatchInfo) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.ScheduledEvent != nil { w, err = v.ScheduledEvent.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartedTimestamp != nil { w, err = wire.NewValueI64(*(v.StartedTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI64(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ScheduledTimestampOfThisAttempt != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestampOfThisAttempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ScheduledTimestamp != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.HeartbeatDetails != nil { w, err = wire.NewValueBinary(v.HeartbeatDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.WorkflowDomain != nil { w, err = wire.NewValueString(*(v.WorkflowDomain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _HistoryEvent_Read(w wire.Value) (*shared.HistoryEvent, error) { var v shared.HistoryEvent err := v.FromWire(w) return &v, err } func _WorkflowType_Read(w wire.Value) (*shared.WorkflowType, error) { var v shared.WorkflowType err := v.FromWire(w) return &v, err } // FromWire deserializes a ActivityTaskDispatchInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityTaskDispatchInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityTaskDispatchInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityTaskDispatchInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.ScheduledEvent, err = _HistoryEvent_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTimestamp = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Attempt = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestampOfThisAttempt = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestamp = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.HeartbeatDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowDomain = &x if err != nil { return err } } } } return nil } // Encode serializes a ActivityTaskDispatchInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityTaskDispatchInfo struct could not be encoded. func (v *ActivityTaskDispatchInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ScheduledEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.ScheduledEvent.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestampOfThisAttempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestampOfThisAttempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HeartbeatDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.HeartbeatDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowDomain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowDomain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _HistoryEvent_Decode(sr stream.Reader) (*shared.HistoryEvent, error) { var v shared.HistoryEvent err := v.Decode(sr) return &v, err } func _WorkflowType_Decode(sr stream.Reader) (*shared.WorkflowType, error) { var v shared.WorkflowType err := v.Decode(sr) return &v, err } // Decode deserializes a ActivityTaskDispatchInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityTaskDispatchInfo struct could not be generated from the wire // representation. func (v *ActivityTaskDispatchInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.ScheduledEvent, err = _HistoryEvent_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTimestamp = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Attempt = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestampOfThisAttempt = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestamp = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.HeartbeatDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowDomain = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityTaskDispatchInfo // struct. func (v *ActivityTaskDispatchInfo) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.ScheduledEvent != nil { fields[i] = fmt.Sprintf("ScheduledEvent: %v", v.ScheduledEvent) i++ } if v.StartedTimestamp != nil { fields[i] = fmt.Sprintf("StartedTimestamp: %v", *(v.StartedTimestamp)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.ScheduledTimestampOfThisAttempt != nil { fields[i] = fmt.Sprintf("ScheduledTimestampOfThisAttempt: %v", *(v.ScheduledTimestampOfThisAttempt)) i++ } if v.ScheduledTimestamp != nil { fields[i] = fmt.Sprintf("ScheduledTimestamp: %v", *(v.ScheduledTimestamp)) i++ } if v.HeartbeatDetails != nil { fields[i] = fmt.Sprintf("HeartbeatDetails: %v", v.HeartbeatDetails) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.WorkflowDomain != nil { fields[i] = fmt.Sprintf("WorkflowDomain: %v", *(v.WorkflowDomain)) i++ } return fmt.Sprintf("ActivityTaskDispatchInfo{%v}", strings.Join(fields[:i], ", ")) } func _I64_EqualsPtr(lhs, rhs *int64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ActivityTaskDispatchInfo match the // provided ActivityTaskDispatchInfo. // // This function performs a deep comparison. func (v *ActivityTaskDispatchInfo) Equals(rhs *ActivityTaskDispatchInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ScheduledEvent == nil && rhs.ScheduledEvent == nil) || (v.ScheduledEvent != nil && rhs.ScheduledEvent != nil && v.ScheduledEvent.Equals(rhs.ScheduledEvent))) { return false } if !_I64_EqualsPtr(v.StartedTimestamp, rhs.StartedTimestamp) { return false } if !_I64_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_I64_EqualsPtr(v.ScheduledTimestampOfThisAttempt, rhs.ScheduledTimestampOfThisAttempt) { return false } if !_I64_EqualsPtr(v.ScheduledTimestamp, rhs.ScheduledTimestamp) { return false } if !((v.HeartbeatDetails == nil && rhs.HeartbeatDetails == nil) || (v.HeartbeatDetails != nil && rhs.HeartbeatDetails != nil && bytes.Equal(v.HeartbeatDetails, rhs.HeartbeatDetails))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_String_EqualsPtr(v.WorkflowDomain, rhs.WorkflowDomain) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityTaskDispatchInfo. func (v *ActivityTaskDispatchInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ScheduledEvent != nil { err = multierr.Append(err, enc.AddObject("scheduledEvent", v.ScheduledEvent)) } if v.StartedTimestamp != nil { enc.AddInt64("startedTimestamp", *v.StartedTimestamp) } if v.Attempt != nil { enc.AddInt64("attempt", *v.Attempt) } if v.ScheduledTimestampOfThisAttempt != nil { enc.AddInt64("scheduledTimestampOfThisAttempt", *v.ScheduledTimestampOfThisAttempt) } if v.ScheduledTimestamp != nil { enc.AddInt64("scheduledTimestamp", *v.ScheduledTimestamp) } if v.HeartbeatDetails != nil { enc.AddString("heartbeatDetails", base64.StdEncoding.EncodeToString(v.HeartbeatDetails)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.WorkflowDomain != nil { enc.AddString("workflowDomain", *v.WorkflowDomain) } return err } // GetScheduledEvent returns the value of ScheduledEvent if it is set or its // zero value if it is unset. func (v *ActivityTaskDispatchInfo) GetScheduledEvent() (o *shared.HistoryEvent) { if v != nil && v.ScheduledEvent != nil { return v.ScheduledEvent } return } // IsSetScheduledEvent returns true if ScheduledEvent is not nil. func (v *ActivityTaskDispatchInfo) IsSetScheduledEvent() bool { return v != nil && v.ScheduledEvent != nil } // GetStartedTimestamp returns the value of StartedTimestamp if it is set or its // zero value if it is unset. func (v *ActivityTaskDispatchInfo) GetStartedTimestamp() (o int64) { if v != nil && v.StartedTimestamp != nil { return *v.StartedTimestamp } return } // IsSetStartedTimestamp returns true if StartedTimestamp is not nil. func (v *ActivityTaskDispatchInfo) IsSetStartedTimestamp() bool { return v != nil && v.StartedTimestamp != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *ActivityTaskDispatchInfo) GetAttempt() (o int64) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *ActivityTaskDispatchInfo) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetScheduledTimestampOfThisAttempt returns the value of ScheduledTimestampOfThisAttempt if it is set or its // zero value if it is unset. func (v *ActivityTaskDispatchInfo) GetScheduledTimestampOfThisAttempt() (o int64) { if v != nil && v.ScheduledTimestampOfThisAttempt != nil { return *v.ScheduledTimestampOfThisAttempt } return } // IsSetScheduledTimestampOfThisAttempt returns true if ScheduledTimestampOfThisAttempt is not nil. func (v *ActivityTaskDispatchInfo) IsSetScheduledTimestampOfThisAttempt() bool { return v != nil && v.ScheduledTimestampOfThisAttempt != nil } // GetScheduledTimestamp returns the value of ScheduledTimestamp if it is set or its // zero value if it is unset. func (v *ActivityTaskDispatchInfo) GetScheduledTimestamp() (o int64) { if v != nil && v.ScheduledTimestamp != nil { return *v.ScheduledTimestamp } return } // IsSetScheduledTimestamp returns true if ScheduledTimestamp is not nil. func (v *ActivityTaskDispatchInfo) IsSetScheduledTimestamp() bool { return v != nil && v.ScheduledTimestamp != nil } // GetHeartbeatDetails returns the value of HeartbeatDetails if it is set or its // zero value if it is unset. func (v *ActivityTaskDispatchInfo) GetHeartbeatDetails() (o []byte) { if v != nil && v.HeartbeatDetails != nil { return v.HeartbeatDetails } return } // IsSetHeartbeatDetails returns true if HeartbeatDetails is not nil. func (v *ActivityTaskDispatchInfo) IsSetHeartbeatDetails() bool { return v != nil && v.HeartbeatDetails != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *ActivityTaskDispatchInfo) GetWorkflowType() (o *shared.WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *ActivityTaskDispatchInfo) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetWorkflowDomain returns the value of WorkflowDomain if it is set or its // zero value if it is unset. func (v *ActivityTaskDispatchInfo) GetWorkflowDomain() (o string) { if v != nil && v.WorkflowDomain != nil { return *v.WorkflowDomain } return } // IsSetWorkflowDomain returns true if WorkflowDomain is not nil. func (v *ActivityTaskDispatchInfo) IsSetWorkflowDomain() bool { return v != nil && v.WorkflowDomain != nil } type AddActivityTaskRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` SourceDomainUUID *string `json:"sourceDomainUUID,omitempty"` TaskList *shared.TaskList `json:"taskList,omitempty"` ScheduleId *int64 `json:"scheduleId,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` Source *TaskSource `json:"source,omitempty"` ForwardedFrom *string `json:"forwardedFrom,omitempty"` ActivityTaskDispatchInfo *ActivityTaskDispatchInfo `json:"activityTaskDispatchInfo,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` } type _Map_String_String_MapItemList map[string]string func (m _Map_String_String_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := wire.NewValueString(v), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_String_MapItemList) Size() int { return len(m) } func (_Map_String_String_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_String_MapItemList) ValueType() wire.Type { return wire.TBinary } func (_Map_String_String_MapItemList) Close() {} // ToWire translates a AddActivityTaskRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AddActivityTaskRequest) ToWire() (wire.Value, error) { var ( fields [10]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.SourceDomainUUID != nil { w, err = wire.NewValueString(*(v.SourceDomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ScheduleId != nil { w, err = wire.NewValueI64(*(v.ScheduleId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ScheduleToStartTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ScheduleToStartTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.Source != nil { w, err = v.Source.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 69, Value: w} i++ } if v.ForwardedFrom != nil { w, err = wire.NewValueString(*(v.ForwardedFrom)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.ActivityTaskDispatchInfo != nil { w, err = v.ActivityTaskDispatchInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.PartitionConfig != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.PartitionConfig)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WorkflowExecution_Read(w wire.Value) (*shared.WorkflowExecution, error) { var v shared.WorkflowExecution err := v.FromWire(w) return &v, err } func _TaskList_Read(w wire.Value) (*shared.TaskList, error) { var v shared.TaskList err := v.FromWire(w) return &v, err } func _TaskSource_Read(w wire.Value) (TaskSource, error) { var v TaskSource err := v.FromWire(w) return v, err } func _ActivityTaskDispatchInfo_Read(w wire.Value) (*ActivityTaskDispatchInfo, error) { var v ActivityTaskDispatchInfo err := v.FromWire(w) return &v, err } func _Map_String_String_Read(m wire.MapItemList) (map[string]string, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TBinary { return nil, nil } o := make(map[string]string, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := x.Value.GetString(), error(nil) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a AddActivityTaskRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AddActivityTaskRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AddActivityTaskRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AddActivityTaskRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SourceDomainUUID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduleId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } } case 69: if field.Value.Type() == wire.TI32 { var x TaskSource x, err = _TaskSource_Read(field.Value) v.Source = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ForwardedFrom = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TStruct { v.ActivityTaskDispatchInfo, err = _ActivityTaskDispatchInfo_Read(field.Value) if err != nil { return err } } case 90: if field.Value.Type() == wire.TMap { v.PartitionConfig, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_String_Encode(val map[string]string, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TBinary, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if err := sw.WriteString(k); err != nil { return err } if err := sw.WriteString(v); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a AddActivityTaskRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AddActivityTaskRequest struct could not be encoded. func (v *AddActivityTaskRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SourceDomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SourceDomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduleId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleToStartTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ScheduleToStartTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Source != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 69, Type: wire.TI32}); err != nil { return err } if err := v.Source.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ForwardedFrom != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ForwardedFrom)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityTaskDispatchInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityTaskDispatchInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PartitionConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.PartitionConfig, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _WorkflowExecution_Decode(sr stream.Reader) (*shared.WorkflowExecution, error) { var v shared.WorkflowExecution err := v.Decode(sr) return &v, err } func _TaskList_Decode(sr stream.Reader) (*shared.TaskList, error) { var v shared.TaskList err := v.Decode(sr) return &v, err } func _TaskSource_Decode(sr stream.Reader) (TaskSource, error) { var v TaskSource err := v.Decode(sr) return v, err } func _ActivityTaskDispatchInfo_Decode(sr stream.Reader) (*ActivityTaskDispatchInfo, error) { var v ActivityTaskDispatchInfo err := v.Decode(sr) return &v, err } func _Map_String_String_Decode(sr stream.Reader) (map[string]string, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TBinary { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]string, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := sr.ReadString() if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a AddActivityTaskRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AddActivityTaskRequest struct could not be generated from the wire // representation. func (v *AddActivityTaskRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SourceDomainUUID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduleId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } case fh.ID == 69 && fh.Type == wire.TI32: var x TaskSource x, err = _TaskSource_Decode(sr) v.Source = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ForwardedFrom = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TStruct: v.ActivityTaskDispatchInfo, err = _ActivityTaskDispatchInfo_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TMap: v.PartitionConfig, err = _Map_String_String_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AddActivityTaskRequest // struct. func (v *AddActivityTaskRequest) String() string { if v == nil { return "" } var fields [10]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.SourceDomainUUID != nil { fields[i] = fmt.Sprintf("SourceDomainUUID: %v", *(v.SourceDomainUUID)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.ScheduleId != nil { fields[i] = fmt.Sprintf("ScheduleId: %v", *(v.ScheduleId)) i++ } if v.ScheduleToStartTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ScheduleToStartTimeoutSeconds: %v", *(v.ScheduleToStartTimeoutSeconds)) i++ } if v.Source != nil { fields[i] = fmt.Sprintf("Source: %v", *(v.Source)) i++ } if v.ForwardedFrom != nil { fields[i] = fmt.Sprintf("ForwardedFrom: %v", *(v.ForwardedFrom)) i++ } if v.ActivityTaskDispatchInfo != nil { fields[i] = fmt.Sprintf("ActivityTaskDispatchInfo: %v", v.ActivityTaskDispatchInfo) i++ } if v.PartitionConfig != nil { fields[i] = fmt.Sprintf("PartitionConfig: %v", v.PartitionConfig) i++ } return fmt.Sprintf("AddActivityTaskRequest{%v}", strings.Join(fields[:i], ", ")) } func _I32_EqualsPtr(lhs, rhs *int32) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _TaskSource_EqualsPtr(lhs, rhs *TaskSource) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } func _Map_String_String_Equals(lhs, rhs map[string]string) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !(lv == rv) { return false } } return true } // Equals returns true if all the fields of this AddActivityTaskRequest match the // provided AddActivityTaskRequest. // // This function performs a deep comparison. func (v *AddActivityTaskRequest) Equals(rhs *AddActivityTaskRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !_String_EqualsPtr(v.SourceDomainUUID, rhs.SourceDomainUUID) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !_I64_EqualsPtr(v.ScheduleId, rhs.ScheduleId) { return false } if !_I32_EqualsPtr(v.ScheduleToStartTimeoutSeconds, rhs.ScheduleToStartTimeoutSeconds) { return false } if !_TaskSource_EqualsPtr(v.Source, rhs.Source) { return false } if !_String_EqualsPtr(v.ForwardedFrom, rhs.ForwardedFrom) { return false } if !((v.ActivityTaskDispatchInfo == nil && rhs.ActivityTaskDispatchInfo == nil) || (v.ActivityTaskDispatchInfo != nil && rhs.ActivityTaskDispatchInfo != nil && v.ActivityTaskDispatchInfo.Equals(rhs.ActivityTaskDispatchInfo))) { return false } if !((v.PartitionConfig == nil && rhs.PartitionConfig == nil) || (v.PartitionConfig != nil && rhs.PartitionConfig != nil && _Map_String_String_Equals(v.PartitionConfig, rhs.PartitionConfig))) { return false } return true } type _Map_String_String_Zapper map[string]string // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_String_Zapper. func (m _Map_String_String_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { enc.AddString((string)(k), v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AddActivityTaskRequest. func (v *AddActivityTaskRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.SourceDomainUUID != nil { enc.AddString("sourceDomainUUID", *v.SourceDomainUUID) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.ScheduleId != nil { enc.AddInt64("scheduleId", *v.ScheduleId) } if v.ScheduleToStartTimeoutSeconds != nil { enc.AddInt32("scheduleToStartTimeoutSeconds", *v.ScheduleToStartTimeoutSeconds) } if v.Source != nil { err = multierr.Append(err, enc.AddObject("source", *v.Source)) } if v.ForwardedFrom != nil { enc.AddString("forwardedFrom", *v.ForwardedFrom) } if v.ActivityTaskDispatchInfo != nil { err = multierr.Append(err, enc.AddObject("activityTaskDispatchInfo", v.ActivityTaskDispatchInfo)) } if v.PartitionConfig != nil { err = multierr.Append(err, enc.AddObject("partitionConfig", (_Map_String_String_Zapper)(v.PartitionConfig))) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *AddActivityTaskRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *AddActivityTaskRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *AddActivityTaskRequest) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *AddActivityTaskRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetSourceDomainUUID returns the value of SourceDomainUUID if it is set or its // zero value if it is unset. func (v *AddActivityTaskRequest) GetSourceDomainUUID() (o string) { if v != nil && v.SourceDomainUUID != nil { return *v.SourceDomainUUID } return } // IsSetSourceDomainUUID returns true if SourceDomainUUID is not nil. func (v *AddActivityTaskRequest) IsSetSourceDomainUUID() bool { return v != nil && v.SourceDomainUUID != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *AddActivityTaskRequest) GetTaskList() (o *shared.TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *AddActivityTaskRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetScheduleId returns the value of ScheduleId if it is set or its // zero value if it is unset. func (v *AddActivityTaskRequest) GetScheduleId() (o int64) { if v != nil && v.ScheduleId != nil { return *v.ScheduleId } return } // IsSetScheduleId returns true if ScheduleId is not nil. func (v *AddActivityTaskRequest) IsSetScheduleId() bool { return v != nil && v.ScheduleId != nil } // GetScheduleToStartTimeoutSeconds returns the value of ScheduleToStartTimeoutSeconds if it is set or its // zero value if it is unset. func (v *AddActivityTaskRequest) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // IsSetScheduleToStartTimeoutSeconds returns true if ScheduleToStartTimeoutSeconds is not nil. func (v *AddActivityTaskRequest) IsSetScheduleToStartTimeoutSeconds() bool { return v != nil && v.ScheduleToStartTimeoutSeconds != nil } // GetSource returns the value of Source if it is set or its // zero value if it is unset. func (v *AddActivityTaskRequest) GetSource() (o TaskSource) { if v != nil && v.Source != nil { return *v.Source } return } // IsSetSource returns true if Source is not nil. func (v *AddActivityTaskRequest) IsSetSource() bool { return v != nil && v.Source != nil } // GetForwardedFrom returns the value of ForwardedFrom if it is set or its // zero value if it is unset. func (v *AddActivityTaskRequest) GetForwardedFrom() (o string) { if v != nil && v.ForwardedFrom != nil { return *v.ForwardedFrom } return } // IsSetForwardedFrom returns true if ForwardedFrom is not nil. func (v *AddActivityTaskRequest) IsSetForwardedFrom() bool { return v != nil && v.ForwardedFrom != nil } // GetActivityTaskDispatchInfo returns the value of ActivityTaskDispatchInfo if it is set or its // zero value if it is unset. func (v *AddActivityTaskRequest) GetActivityTaskDispatchInfo() (o *ActivityTaskDispatchInfo) { if v != nil && v.ActivityTaskDispatchInfo != nil { return v.ActivityTaskDispatchInfo } return } // IsSetActivityTaskDispatchInfo returns true if ActivityTaskDispatchInfo is not nil. func (v *AddActivityTaskRequest) IsSetActivityTaskDispatchInfo() bool { return v != nil && v.ActivityTaskDispatchInfo != nil } // GetPartitionConfig returns the value of PartitionConfig if it is set or its // zero value if it is unset. func (v *AddActivityTaskRequest) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // IsSetPartitionConfig returns true if PartitionConfig is not nil. func (v *AddActivityTaskRequest) IsSetPartitionConfig() bool { return v != nil && v.PartitionConfig != nil } type AddDecisionTaskRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` Execution *shared.WorkflowExecution `json:"execution,omitempty"` TaskList *shared.TaskList `json:"taskList,omitempty"` ScheduleId *int64 `json:"scheduleId,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` Source *TaskSource `json:"source,omitempty"` ForwardedFrom *string `json:"forwardedFrom,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` } // ToWire translates a AddDecisionTaskRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AddDecisionTaskRequest) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ScheduleId != nil { w, err = wire.NewValueI64(*(v.ScheduleId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ScheduleToStartTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ScheduleToStartTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Source != nil { w, err = v.Source.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 59, Value: w} i++ } if v.ForwardedFrom != nil { w, err = wire.NewValueString(*(v.ForwardedFrom)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.PartitionConfig != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.PartitionConfig)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AddDecisionTaskRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AddDecisionTaskRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AddDecisionTaskRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AddDecisionTaskRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduleId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } } case 59: if field.Value.Type() == wire.TI32 { var x TaskSource x, err = _TaskSource_Read(field.Value) v.Source = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ForwardedFrom = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TMap { v.PartitionConfig, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } // Encode serializes a AddDecisionTaskRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AddDecisionTaskRequest struct could not be encoded. func (v *AddDecisionTaskRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduleId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleToStartTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ScheduleToStartTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Source != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 59, Type: wire.TI32}); err != nil { return err } if err := v.Source.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ForwardedFrom != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ForwardedFrom)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PartitionConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.PartitionConfig, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a AddDecisionTaskRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AddDecisionTaskRequest struct could not be generated from the wire // representation. func (v *AddDecisionTaskRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduleId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } case fh.ID == 59 && fh.Type == wire.TI32: var x TaskSource x, err = _TaskSource_Decode(sr) v.Source = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ForwardedFrom = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TMap: v.PartitionConfig, err = _Map_String_String_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AddDecisionTaskRequest // struct. func (v *AddDecisionTaskRequest) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.ScheduleId != nil { fields[i] = fmt.Sprintf("ScheduleId: %v", *(v.ScheduleId)) i++ } if v.ScheduleToStartTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ScheduleToStartTimeoutSeconds: %v", *(v.ScheduleToStartTimeoutSeconds)) i++ } if v.Source != nil { fields[i] = fmt.Sprintf("Source: %v", *(v.Source)) i++ } if v.ForwardedFrom != nil { fields[i] = fmt.Sprintf("ForwardedFrom: %v", *(v.ForwardedFrom)) i++ } if v.PartitionConfig != nil { fields[i] = fmt.Sprintf("PartitionConfig: %v", v.PartitionConfig) i++ } return fmt.Sprintf("AddDecisionTaskRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AddDecisionTaskRequest match the // provided AddDecisionTaskRequest. // // This function performs a deep comparison. func (v *AddDecisionTaskRequest) Equals(rhs *AddDecisionTaskRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !_I64_EqualsPtr(v.ScheduleId, rhs.ScheduleId) { return false } if !_I32_EqualsPtr(v.ScheduleToStartTimeoutSeconds, rhs.ScheduleToStartTimeoutSeconds) { return false } if !_TaskSource_EqualsPtr(v.Source, rhs.Source) { return false } if !_String_EqualsPtr(v.ForwardedFrom, rhs.ForwardedFrom) { return false } if !((v.PartitionConfig == nil && rhs.PartitionConfig == nil) || (v.PartitionConfig != nil && rhs.PartitionConfig != nil && _Map_String_String_Equals(v.PartitionConfig, rhs.PartitionConfig))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AddDecisionTaskRequest. func (v *AddDecisionTaskRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.ScheduleId != nil { enc.AddInt64("scheduleId", *v.ScheduleId) } if v.ScheduleToStartTimeoutSeconds != nil { enc.AddInt32("scheduleToStartTimeoutSeconds", *v.ScheduleToStartTimeoutSeconds) } if v.Source != nil { err = multierr.Append(err, enc.AddObject("source", *v.Source)) } if v.ForwardedFrom != nil { enc.AddString("forwardedFrom", *v.ForwardedFrom) } if v.PartitionConfig != nil { err = multierr.Append(err, enc.AddObject("partitionConfig", (_Map_String_String_Zapper)(v.PartitionConfig))) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *AddDecisionTaskRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *AddDecisionTaskRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *AddDecisionTaskRequest) GetExecution() (o *shared.WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *AddDecisionTaskRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *AddDecisionTaskRequest) GetTaskList() (o *shared.TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *AddDecisionTaskRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetScheduleId returns the value of ScheduleId if it is set or its // zero value if it is unset. func (v *AddDecisionTaskRequest) GetScheduleId() (o int64) { if v != nil && v.ScheduleId != nil { return *v.ScheduleId } return } // IsSetScheduleId returns true if ScheduleId is not nil. func (v *AddDecisionTaskRequest) IsSetScheduleId() bool { return v != nil && v.ScheduleId != nil } // GetScheduleToStartTimeoutSeconds returns the value of ScheduleToStartTimeoutSeconds if it is set or its // zero value if it is unset. func (v *AddDecisionTaskRequest) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // IsSetScheduleToStartTimeoutSeconds returns true if ScheduleToStartTimeoutSeconds is not nil. func (v *AddDecisionTaskRequest) IsSetScheduleToStartTimeoutSeconds() bool { return v != nil && v.ScheduleToStartTimeoutSeconds != nil } // GetSource returns the value of Source if it is set or its // zero value if it is unset. func (v *AddDecisionTaskRequest) GetSource() (o TaskSource) { if v != nil && v.Source != nil { return *v.Source } return } // IsSetSource returns true if Source is not nil. func (v *AddDecisionTaskRequest) IsSetSource() bool { return v != nil && v.Source != nil } // GetForwardedFrom returns the value of ForwardedFrom if it is set or its // zero value if it is unset. func (v *AddDecisionTaskRequest) GetForwardedFrom() (o string) { if v != nil && v.ForwardedFrom != nil { return *v.ForwardedFrom } return } // IsSetForwardedFrom returns true if ForwardedFrom is not nil. func (v *AddDecisionTaskRequest) IsSetForwardedFrom() bool { return v != nil && v.ForwardedFrom != nil } // GetPartitionConfig returns the value of PartitionConfig if it is set or its // zero value if it is unset. func (v *AddDecisionTaskRequest) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // IsSetPartitionConfig returns true if PartitionConfig is not nil. func (v *AddDecisionTaskRequest) IsSetPartitionConfig() bool { return v != nil && v.PartitionConfig != nil } type CancelOutstandingPollRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` TaskListType *int32 `json:"taskListType,omitempty"` TaskList *shared.TaskList `json:"taskList,omitempty"` PollerID *string `json:"pollerID,omitempty"` } // ToWire translates a CancelOutstandingPollRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CancelOutstandingPollRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskListType != nil { w, err = wire.NewValueI32(*(v.TaskListType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.PollerID != nil { w, err = wire.NewValueString(*(v.PollerID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CancelOutstandingPollRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CancelOutstandingPollRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CancelOutstandingPollRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CancelOutstandingPollRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.TaskListType = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.PollerID = &x if err != nil { return err } } } } return nil } // Encode serializes a CancelOutstandingPollRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CancelOutstandingPollRequest struct could not be encoded. func (v *CancelOutstandingPollRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.TaskListType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PollerID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.PollerID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CancelOutstandingPollRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CancelOutstandingPollRequest struct could not be generated from the wire // representation. func (v *CancelOutstandingPollRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.TaskListType = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.PollerID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CancelOutstandingPollRequest // struct. func (v *CancelOutstandingPollRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.TaskListType != nil { fields[i] = fmt.Sprintf("TaskListType: %v", *(v.TaskListType)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.PollerID != nil { fields[i] = fmt.Sprintf("PollerID: %v", *(v.PollerID)) i++ } return fmt.Sprintf("CancelOutstandingPollRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CancelOutstandingPollRequest match the // provided CancelOutstandingPollRequest. // // This function performs a deep comparison. func (v *CancelOutstandingPollRequest) Equals(rhs *CancelOutstandingPollRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !_I32_EqualsPtr(v.TaskListType, rhs.TaskListType) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !_String_EqualsPtr(v.PollerID, rhs.PollerID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CancelOutstandingPollRequest. func (v *CancelOutstandingPollRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.TaskListType != nil { enc.AddInt32("taskListType", *v.TaskListType) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.PollerID != nil { enc.AddString("pollerID", *v.PollerID) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *CancelOutstandingPollRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *CancelOutstandingPollRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetTaskListType returns the value of TaskListType if it is set or its // zero value if it is unset. func (v *CancelOutstandingPollRequest) GetTaskListType() (o int32) { if v != nil && v.TaskListType != nil { return *v.TaskListType } return } // IsSetTaskListType returns true if TaskListType is not nil. func (v *CancelOutstandingPollRequest) IsSetTaskListType() bool { return v != nil && v.TaskListType != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *CancelOutstandingPollRequest) GetTaskList() (o *shared.TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *CancelOutstandingPollRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetPollerID returns the value of PollerID if it is set or its // zero value if it is unset. func (v *CancelOutstandingPollRequest) GetPollerID() (o string) { if v != nil && v.PollerID != nil { return *v.PollerID } return } // IsSetPollerID returns true if PollerID is not nil. func (v *CancelOutstandingPollRequest) IsSetPollerID() bool { return v != nil && v.PollerID != nil } type DescribeTaskListRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` DescRequest *shared.DescribeTaskListRequest `json:"descRequest,omitempty"` } // ToWire translates a DescribeTaskListRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeTaskListRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.DescRequest != nil { w, err = v.DescRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeTaskListRequest_Read(w wire.Value) (*shared.DescribeTaskListRequest, error) { var v shared.DescribeTaskListRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a DescribeTaskListRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeTaskListRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeTaskListRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeTaskListRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.DescRequest, err = _DescribeTaskListRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a DescribeTaskListRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeTaskListRequest struct could not be encoded. func (v *DescribeTaskListRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DescRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.DescRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeTaskListRequest_Decode(sr stream.Reader) (*shared.DescribeTaskListRequest, error) { var v shared.DescribeTaskListRequest err := v.Decode(sr) return &v, err } // Decode deserializes a DescribeTaskListRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeTaskListRequest struct could not be generated from the wire // representation. func (v *DescribeTaskListRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.DescRequest, err = _DescribeTaskListRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeTaskListRequest // struct. func (v *DescribeTaskListRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.DescRequest != nil { fields[i] = fmt.Sprintf("DescRequest: %v", v.DescRequest) i++ } return fmt.Sprintf("DescribeTaskListRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DescribeTaskListRequest match the // provided DescribeTaskListRequest. // // This function performs a deep comparison. func (v *DescribeTaskListRequest) Equals(rhs *DescribeTaskListRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.DescRequest == nil && rhs.DescRequest == nil) || (v.DescRequest != nil && rhs.DescRequest != nil && v.DescRequest.Equals(rhs.DescRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeTaskListRequest. func (v *DescribeTaskListRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.DescRequest != nil { err = multierr.Append(err, enc.AddObject("descRequest", v.DescRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *DescribeTaskListRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *DescribeTaskListRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetDescRequest returns the value of DescRequest if it is set or its // zero value if it is unset. func (v *DescribeTaskListRequest) GetDescRequest() (o *shared.DescribeTaskListRequest) { if v != nil && v.DescRequest != nil { return v.DescRequest } return } // IsSetDescRequest returns true if DescRequest is not nil. func (v *DescribeTaskListRequest) IsSetDescRequest() bool { return v != nil && v.DescRequest != nil } type ListTaskListPartitionsRequest struct { Domain *string `json:"domain,omitempty"` TaskList *shared.TaskList `json:"taskList,omitempty"` } // ToWire translates a ListTaskListPartitionsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListTaskListPartitionsRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListTaskListPartitionsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListTaskListPartitionsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListTaskListPartitionsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListTaskListPartitionsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ListTaskListPartitionsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListTaskListPartitionsRequest struct could not be encoded. func (v *ListTaskListPartitionsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListTaskListPartitionsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListTaskListPartitionsRequest struct could not be generated from the wire // representation. func (v *ListTaskListPartitionsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListTaskListPartitionsRequest // struct. func (v *ListTaskListPartitionsRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } return fmt.Sprintf("ListTaskListPartitionsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListTaskListPartitionsRequest match the // provided ListTaskListPartitionsRequest. // // This function performs a deep comparison. func (v *ListTaskListPartitionsRequest) Equals(rhs *ListTaskListPartitionsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListTaskListPartitionsRequest. func (v *ListTaskListPartitionsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ListTaskListPartitionsRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ListTaskListPartitionsRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *ListTaskListPartitionsRequest) GetTaskList() (o *shared.TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *ListTaskListPartitionsRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } type PollForActivityTaskRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` PollerID *string `json:"pollerID,omitempty"` PollRequest *shared.PollForActivityTaskRequest `json:"pollRequest,omitempty"` ForwardedFrom *string `json:"forwardedFrom,omitempty"` IsolationGroup *string `json:"isolationGroup,omitempty"` } // ToWire translates a PollForActivityTaskRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PollForActivityTaskRequest) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.PollerID != nil { w, err = wire.NewValueString(*(v.PollerID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 15, Value: w} i++ } if v.PollRequest != nil { w, err = v.PollRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ForwardedFrom != nil { w, err = wire.NewValueString(*(v.ForwardedFrom)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.IsolationGroup != nil { w, err = wire.NewValueString(*(v.IsolationGroup)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForActivityTaskRequest_Read(w wire.Value) (*shared.PollForActivityTaskRequest, error) { var v shared.PollForActivityTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a PollForActivityTaskRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PollForActivityTaskRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PollForActivityTaskRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PollForActivityTaskRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 15: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.PollerID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.PollRequest, err = _PollForActivityTaskRequest_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ForwardedFrom = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.IsolationGroup = &x if err != nil { return err } } } } return nil } // Encode serializes a PollForActivityTaskRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PollForActivityTaskRequest struct could not be encoded. func (v *PollForActivityTaskRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PollerID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 15, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.PollerID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PollRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.PollRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ForwardedFrom != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ForwardedFrom)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsolationGroup != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.IsolationGroup)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollForActivityTaskRequest_Decode(sr stream.Reader) (*shared.PollForActivityTaskRequest, error) { var v shared.PollForActivityTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a PollForActivityTaskRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PollForActivityTaskRequest struct could not be generated from the wire // representation. func (v *PollForActivityTaskRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 15 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.PollerID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.PollRequest, err = _PollForActivityTaskRequest_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ForwardedFrom = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.IsolationGroup = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PollForActivityTaskRequest // struct. func (v *PollForActivityTaskRequest) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.PollerID != nil { fields[i] = fmt.Sprintf("PollerID: %v", *(v.PollerID)) i++ } if v.PollRequest != nil { fields[i] = fmt.Sprintf("PollRequest: %v", v.PollRequest) i++ } if v.ForwardedFrom != nil { fields[i] = fmt.Sprintf("ForwardedFrom: %v", *(v.ForwardedFrom)) i++ } if v.IsolationGroup != nil { fields[i] = fmt.Sprintf("IsolationGroup: %v", *(v.IsolationGroup)) i++ } return fmt.Sprintf("PollForActivityTaskRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PollForActivityTaskRequest match the // provided PollForActivityTaskRequest. // // This function performs a deep comparison. func (v *PollForActivityTaskRequest) Equals(rhs *PollForActivityTaskRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !_String_EqualsPtr(v.PollerID, rhs.PollerID) { return false } if !((v.PollRequest == nil && rhs.PollRequest == nil) || (v.PollRequest != nil && rhs.PollRequest != nil && v.PollRequest.Equals(rhs.PollRequest))) { return false } if !_String_EqualsPtr(v.ForwardedFrom, rhs.ForwardedFrom) { return false } if !_String_EqualsPtr(v.IsolationGroup, rhs.IsolationGroup) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PollForActivityTaskRequest. func (v *PollForActivityTaskRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.PollerID != nil { enc.AddString("pollerID", *v.PollerID) } if v.PollRequest != nil { err = multierr.Append(err, enc.AddObject("pollRequest", v.PollRequest)) } if v.ForwardedFrom != nil { enc.AddString("forwardedFrom", *v.ForwardedFrom) } if v.IsolationGroup != nil { enc.AddString("isolationGroup", *v.IsolationGroup) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *PollForActivityTaskRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *PollForActivityTaskRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetPollerID returns the value of PollerID if it is set or its // zero value if it is unset. func (v *PollForActivityTaskRequest) GetPollerID() (o string) { if v != nil && v.PollerID != nil { return *v.PollerID } return } // IsSetPollerID returns true if PollerID is not nil. func (v *PollForActivityTaskRequest) IsSetPollerID() bool { return v != nil && v.PollerID != nil } // GetPollRequest returns the value of PollRequest if it is set or its // zero value if it is unset. func (v *PollForActivityTaskRequest) GetPollRequest() (o *shared.PollForActivityTaskRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // IsSetPollRequest returns true if PollRequest is not nil. func (v *PollForActivityTaskRequest) IsSetPollRequest() bool { return v != nil && v.PollRequest != nil } // GetForwardedFrom returns the value of ForwardedFrom if it is set or its // zero value if it is unset. func (v *PollForActivityTaskRequest) GetForwardedFrom() (o string) { if v != nil && v.ForwardedFrom != nil { return *v.ForwardedFrom } return } // IsSetForwardedFrom returns true if ForwardedFrom is not nil. func (v *PollForActivityTaskRequest) IsSetForwardedFrom() bool { return v != nil && v.ForwardedFrom != nil } // GetIsolationGroup returns the value of IsolationGroup if it is set or its // zero value if it is unset. func (v *PollForActivityTaskRequest) GetIsolationGroup() (o string) { if v != nil && v.IsolationGroup != nil { return *v.IsolationGroup } return } // IsSetIsolationGroup returns true if IsolationGroup is not nil. func (v *PollForActivityTaskRequest) IsSetIsolationGroup() bool { return v != nil && v.IsolationGroup != nil } type PollForDecisionTaskRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` PollerID *string `json:"pollerID,omitempty"` PollRequest *shared.PollForDecisionTaskRequest `json:"pollRequest,omitempty"` ForwardedFrom *string `json:"forwardedFrom,omitempty"` IsolationGroup *string `json:"isolationGroup,omitempty"` } // ToWire translates a PollForDecisionTaskRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PollForDecisionTaskRequest) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.PollerID != nil { w, err = wire.NewValueString(*(v.PollerID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 15, Value: w} i++ } if v.PollRequest != nil { w, err = v.PollRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ForwardedFrom != nil { w, err = wire.NewValueString(*(v.ForwardedFrom)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.IsolationGroup != nil { w, err = wire.NewValueString(*(v.IsolationGroup)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForDecisionTaskRequest_Read(w wire.Value) (*shared.PollForDecisionTaskRequest, error) { var v shared.PollForDecisionTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a PollForDecisionTaskRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PollForDecisionTaskRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PollForDecisionTaskRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PollForDecisionTaskRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 15: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.PollerID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.PollRequest, err = _PollForDecisionTaskRequest_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ForwardedFrom = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.IsolationGroup = &x if err != nil { return err } } } } return nil } // Encode serializes a PollForDecisionTaskRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PollForDecisionTaskRequest struct could not be encoded. func (v *PollForDecisionTaskRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PollerID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 15, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.PollerID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PollRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.PollRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ForwardedFrom != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ForwardedFrom)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsolationGroup != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.IsolationGroup)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollForDecisionTaskRequest_Decode(sr stream.Reader) (*shared.PollForDecisionTaskRequest, error) { var v shared.PollForDecisionTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a PollForDecisionTaskRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PollForDecisionTaskRequest struct could not be generated from the wire // representation. func (v *PollForDecisionTaskRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 15 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.PollerID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.PollRequest, err = _PollForDecisionTaskRequest_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ForwardedFrom = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.IsolationGroup = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PollForDecisionTaskRequest // struct. func (v *PollForDecisionTaskRequest) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.PollerID != nil { fields[i] = fmt.Sprintf("PollerID: %v", *(v.PollerID)) i++ } if v.PollRequest != nil { fields[i] = fmt.Sprintf("PollRequest: %v", v.PollRequest) i++ } if v.ForwardedFrom != nil { fields[i] = fmt.Sprintf("ForwardedFrom: %v", *(v.ForwardedFrom)) i++ } if v.IsolationGroup != nil { fields[i] = fmt.Sprintf("IsolationGroup: %v", *(v.IsolationGroup)) i++ } return fmt.Sprintf("PollForDecisionTaskRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PollForDecisionTaskRequest match the // provided PollForDecisionTaskRequest. // // This function performs a deep comparison. func (v *PollForDecisionTaskRequest) Equals(rhs *PollForDecisionTaskRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !_String_EqualsPtr(v.PollerID, rhs.PollerID) { return false } if !((v.PollRequest == nil && rhs.PollRequest == nil) || (v.PollRequest != nil && rhs.PollRequest != nil && v.PollRequest.Equals(rhs.PollRequest))) { return false } if !_String_EqualsPtr(v.ForwardedFrom, rhs.ForwardedFrom) { return false } if !_String_EqualsPtr(v.IsolationGroup, rhs.IsolationGroup) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PollForDecisionTaskRequest. func (v *PollForDecisionTaskRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.PollerID != nil { enc.AddString("pollerID", *v.PollerID) } if v.PollRequest != nil { err = multierr.Append(err, enc.AddObject("pollRequest", v.PollRequest)) } if v.ForwardedFrom != nil { enc.AddString("forwardedFrom", *v.ForwardedFrom) } if v.IsolationGroup != nil { enc.AddString("isolationGroup", *v.IsolationGroup) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *PollForDecisionTaskRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetPollerID returns the value of PollerID if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskRequest) GetPollerID() (o string) { if v != nil && v.PollerID != nil { return *v.PollerID } return } // IsSetPollerID returns true if PollerID is not nil. func (v *PollForDecisionTaskRequest) IsSetPollerID() bool { return v != nil && v.PollerID != nil } // GetPollRequest returns the value of PollRequest if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskRequest) GetPollRequest() (o *shared.PollForDecisionTaskRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // IsSetPollRequest returns true if PollRequest is not nil. func (v *PollForDecisionTaskRequest) IsSetPollRequest() bool { return v != nil && v.PollRequest != nil } // GetForwardedFrom returns the value of ForwardedFrom if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskRequest) GetForwardedFrom() (o string) { if v != nil && v.ForwardedFrom != nil { return *v.ForwardedFrom } return } // IsSetForwardedFrom returns true if ForwardedFrom is not nil. func (v *PollForDecisionTaskRequest) IsSetForwardedFrom() bool { return v != nil && v.ForwardedFrom != nil } // GetIsolationGroup returns the value of IsolationGroup if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskRequest) GetIsolationGroup() (o string) { if v != nil && v.IsolationGroup != nil { return *v.IsolationGroup } return } // IsSetIsolationGroup returns true if IsolationGroup is not nil. func (v *PollForDecisionTaskRequest) IsSetIsolationGroup() bool { return v != nil && v.IsolationGroup != nil } type PollForDecisionTaskResponse struct { TaskToken []byte `json:"taskToken,omitempty"` WorkflowExecution *shared.WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *shared.WorkflowType `json:"workflowType,omitempty"` PreviousStartedEventId *int64 `json:"previousStartedEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` Attempt *int64 `json:"attempt,omitempty"` NextEventId *int64 `json:"nextEventId,omitempty"` BacklogCountHint *int64 `json:"backlogCountHint,omitempty"` StickyExecutionEnabled *bool `json:"stickyExecutionEnabled,omitempty"` Query *shared.WorkflowQuery `json:"query,omitempty"` DecisionInfo *shared.TransientDecisionInfo `json:"decisionInfo,omitempty"` WorkflowExecutionTaskList *shared.TaskList `json:"WorkflowExecutionTaskList,omitempty"` EventStoreVersion *int32 `json:"eventStoreVersion,omitempty"` BranchToken []byte `json:"branchToken,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Queries map[string]*shared.WorkflowQuery `json:"queries,omitempty"` TotalHistoryBytes *int64 `json:"totalHistoryBytes,omitempty"` AutoConfigHint *shared.AutoConfigHint `json:"autoConfigHint,omitempty"` } type _Map_String_WorkflowQuery_MapItemList map[string]*shared.WorkflowQuery func (m _Map_String_WorkflowQuery_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*shared.WorkflowQuery', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_WorkflowQuery_MapItemList) Size() int { return len(m) } func (_Map_String_WorkflowQuery_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_WorkflowQuery_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_WorkflowQuery_MapItemList) Close() {} // ToWire translates a PollForDecisionTaskResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PollForDecisionTaskResponse) ToWire() (wire.Value, error) { var ( fields [19]wire.Field i int = 0 w wire.Value err error ) if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.PreviousStartedEventId != nil { w, err = wire.NewValueI64(*(v.PreviousStartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI64(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 51, Value: w} i++ } if v.NextEventId != nil { w, err = wire.NewValueI64(*(v.NextEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.BacklogCountHint != nil { w, err = wire.NewValueI64(*(v.BacklogCountHint)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 65, Value: w} i++ } if v.StickyExecutionEnabled != nil { w, err = wire.NewValueBool(*(v.StickyExecutionEnabled)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.Query != nil { w, err = v.Query.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.DecisionInfo != nil { w, err = v.DecisionInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.WorkflowExecutionTaskList != nil { w, err = v.WorkflowExecutionTaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.EventStoreVersion != nil { w, err = wire.NewValueI32(*(v.EventStoreVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.BranchToken != nil { w, err = wire.NewValueBinary(v.BranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.ScheduledTimestamp != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.StartedTimestamp != nil { w, err = wire.NewValueI64(*(v.StartedTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.Queries != nil { w, err = wire.NewValueMap(_Map_String_WorkflowQuery_MapItemList(v.Queries)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.TotalHistoryBytes != nil { w, err = wire.NewValueI64(*(v.TotalHistoryBytes)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.AutoConfigHint != nil { w, err = v.AutoConfigHint.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WorkflowQuery_Read(w wire.Value) (*shared.WorkflowQuery, error) { var v shared.WorkflowQuery err := v.FromWire(w) return &v, err } func _TransientDecisionInfo_Read(w wire.Value) (*shared.TransientDecisionInfo, error) { var v shared.TransientDecisionInfo err := v.FromWire(w) return &v, err } func _Map_String_WorkflowQuery_Read(m wire.MapItemList) (map[string]*shared.WorkflowQuery, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*shared.WorkflowQuery, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _WorkflowQuery_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } func _AutoConfigHint_Read(w wire.Value) (*shared.AutoConfigHint, error) { var v shared.AutoConfigHint err := v.FromWire(w) return &v, err } // FromWire deserializes a PollForDecisionTaskResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PollForDecisionTaskResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PollForDecisionTaskResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PollForDecisionTaskResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.PreviousStartedEventId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 51: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Attempt = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NextEventId = &x if err != nil { return err } } case 65: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.BacklogCountHint = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.StickyExecutionEnabled = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TStruct { v.Query, err = _WorkflowQuery_Read(field.Value) if err != nil { return err } } case 90: if field.Value.Type() == wire.TStruct { v.DecisionInfo, err = _TransientDecisionInfo_Read(field.Value) if err != nil { return err } } case 100: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionTaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 110: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.EventStoreVersion = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TBinary { v.BranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 130: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestamp = &x if err != nil { return err } } case 140: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTimestamp = &x if err != nil { return err } } case 150: if field.Value.Type() == wire.TMap { v.Queries, err = _Map_String_WorkflowQuery_Read(field.Value.GetMap()) if err != nil { return err } } case 160: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TotalHistoryBytes = &x if err != nil { return err } } case 170: if field.Value.Type() == wire.TStruct { v.AutoConfigHint, err = _AutoConfigHint_Read(field.Value) if err != nil { return err } } } } return nil } func _Map_String_WorkflowQuery_Encode(val map[string]*shared.WorkflowQuery, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*shared.WorkflowQuery', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a PollForDecisionTaskResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PollForDecisionTaskResponse struct could not be encoded. func (v *PollForDecisionTaskResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PreviousStartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.PreviousStartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 51, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NextEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BacklogCountHint != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 65, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.BacklogCountHint)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyExecutionEnabled != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.StickyExecutionEnabled)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Query != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TStruct}); err != nil { return err } if err := v.Query.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TStruct}); err != nil { return err } if err := v.DecisionInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionTaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionTaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EventStoreVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.EventStoreVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BranchToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.BranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Queries != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TMap}); err != nil { return err } if err := _Map_String_WorkflowQuery_Encode(v.Queries, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TotalHistoryBytes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TotalHistoryBytes)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AutoConfigHint != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TStruct}); err != nil { return err } if err := v.AutoConfigHint.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _WorkflowQuery_Decode(sr stream.Reader) (*shared.WorkflowQuery, error) { var v shared.WorkflowQuery err := v.Decode(sr) return &v, err } func _TransientDecisionInfo_Decode(sr stream.Reader) (*shared.TransientDecisionInfo, error) { var v shared.TransientDecisionInfo err := v.Decode(sr) return &v, err } func _Map_String_WorkflowQuery_Decode(sr stream.Reader) (map[string]*shared.WorkflowQuery, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*shared.WorkflowQuery, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _WorkflowQuery_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } func _AutoConfigHint_Decode(sr stream.Reader) (*shared.AutoConfigHint, error) { var v shared.AutoConfigHint err := v.Decode(sr) return &v, err } // Decode deserializes a PollForDecisionTaskResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PollForDecisionTaskResponse struct could not be generated from the wire // representation. func (v *PollForDecisionTaskResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.PreviousStartedEventId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 51 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Attempt = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NextEventId = &x if err != nil { return err } case fh.ID == 65 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.BacklogCountHint = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.StickyExecutionEnabled = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TStruct: v.Query, err = _WorkflowQuery_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TStruct: v.DecisionInfo, err = _TransientDecisionInfo_Decode(sr) if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TStruct: v.WorkflowExecutionTaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.EventStoreVersion = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TBinary: v.BranchToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestamp = &x if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTimestamp = &x if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TMap: v.Queries, err = _Map_String_WorkflowQuery_Decode(sr) if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TotalHistoryBytes = &x if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TStruct: v.AutoConfigHint, err = _AutoConfigHint_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PollForDecisionTaskResponse // struct. func (v *PollForDecisionTaskResponse) String() string { if v == nil { return "" } var fields [19]string i := 0 if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.PreviousStartedEventId != nil { fields[i] = fmt.Sprintf("PreviousStartedEventId: %v", *(v.PreviousStartedEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.NextEventId != nil { fields[i] = fmt.Sprintf("NextEventId: %v", *(v.NextEventId)) i++ } if v.BacklogCountHint != nil { fields[i] = fmt.Sprintf("BacklogCountHint: %v", *(v.BacklogCountHint)) i++ } if v.StickyExecutionEnabled != nil { fields[i] = fmt.Sprintf("StickyExecutionEnabled: %v", *(v.StickyExecutionEnabled)) i++ } if v.Query != nil { fields[i] = fmt.Sprintf("Query: %v", v.Query) i++ } if v.DecisionInfo != nil { fields[i] = fmt.Sprintf("DecisionInfo: %v", v.DecisionInfo) i++ } if v.WorkflowExecutionTaskList != nil { fields[i] = fmt.Sprintf("WorkflowExecutionTaskList: %v", v.WorkflowExecutionTaskList) i++ } if v.EventStoreVersion != nil { fields[i] = fmt.Sprintf("EventStoreVersion: %v", *(v.EventStoreVersion)) i++ } if v.BranchToken != nil { fields[i] = fmt.Sprintf("BranchToken: %v", v.BranchToken) i++ } if v.ScheduledTimestamp != nil { fields[i] = fmt.Sprintf("ScheduledTimestamp: %v", *(v.ScheduledTimestamp)) i++ } if v.StartedTimestamp != nil { fields[i] = fmt.Sprintf("StartedTimestamp: %v", *(v.StartedTimestamp)) i++ } if v.Queries != nil { fields[i] = fmt.Sprintf("Queries: %v", v.Queries) i++ } if v.TotalHistoryBytes != nil { fields[i] = fmt.Sprintf("TotalHistoryBytes: %v", *(v.TotalHistoryBytes)) i++ } if v.AutoConfigHint != nil { fields[i] = fmt.Sprintf("AutoConfigHint: %v", v.AutoConfigHint) i++ } return fmt.Sprintf("PollForDecisionTaskResponse{%v}", strings.Join(fields[:i], ", ")) } func _Bool_EqualsPtr(lhs, rhs *bool) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _Map_String_WorkflowQuery_Equals(lhs, rhs map[string]*shared.WorkflowQuery) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this PollForDecisionTaskResponse match the // provided PollForDecisionTaskResponse. // // This function performs a deep comparison. func (v *PollForDecisionTaskResponse) Equals(rhs *PollForDecisionTaskResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_I64_EqualsPtr(v.PreviousStartedEventId, rhs.PreviousStartedEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_I64_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_I64_EqualsPtr(v.NextEventId, rhs.NextEventId) { return false } if !_I64_EqualsPtr(v.BacklogCountHint, rhs.BacklogCountHint) { return false } if !_Bool_EqualsPtr(v.StickyExecutionEnabled, rhs.StickyExecutionEnabled) { return false } if !((v.Query == nil && rhs.Query == nil) || (v.Query != nil && rhs.Query != nil && v.Query.Equals(rhs.Query))) { return false } if !((v.DecisionInfo == nil && rhs.DecisionInfo == nil) || (v.DecisionInfo != nil && rhs.DecisionInfo != nil && v.DecisionInfo.Equals(rhs.DecisionInfo))) { return false } if !((v.WorkflowExecutionTaskList == nil && rhs.WorkflowExecutionTaskList == nil) || (v.WorkflowExecutionTaskList != nil && rhs.WorkflowExecutionTaskList != nil && v.WorkflowExecutionTaskList.Equals(rhs.WorkflowExecutionTaskList))) { return false } if !_I32_EqualsPtr(v.EventStoreVersion, rhs.EventStoreVersion) { return false } if !((v.BranchToken == nil && rhs.BranchToken == nil) || (v.BranchToken != nil && rhs.BranchToken != nil && bytes.Equal(v.BranchToken, rhs.BranchToken))) { return false } if !_I64_EqualsPtr(v.ScheduledTimestamp, rhs.ScheduledTimestamp) { return false } if !_I64_EqualsPtr(v.StartedTimestamp, rhs.StartedTimestamp) { return false } if !((v.Queries == nil && rhs.Queries == nil) || (v.Queries != nil && rhs.Queries != nil && _Map_String_WorkflowQuery_Equals(v.Queries, rhs.Queries))) { return false } if !_I64_EqualsPtr(v.TotalHistoryBytes, rhs.TotalHistoryBytes) { return false } if !((v.AutoConfigHint == nil && rhs.AutoConfigHint == nil) || (v.AutoConfigHint != nil && rhs.AutoConfigHint != nil && v.AutoConfigHint.Equals(rhs.AutoConfigHint))) { return false } return true } type _Map_String_WorkflowQuery_Zapper map[string]*shared.WorkflowQuery // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_WorkflowQuery_Zapper. func (m _Map_String_WorkflowQuery_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PollForDecisionTaskResponse. func (v *PollForDecisionTaskResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.PreviousStartedEventId != nil { enc.AddInt64("previousStartedEventId", *v.PreviousStartedEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.Attempt != nil { enc.AddInt64("attempt", *v.Attempt) } if v.NextEventId != nil { enc.AddInt64("nextEventId", *v.NextEventId) } if v.BacklogCountHint != nil { enc.AddInt64("backlogCountHint", *v.BacklogCountHint) } if v.StickyExecutionEnabled != nil { enc.AddBool("stickyExecutionEnabled", *v.StickyExecutionEnabled) } if v.Query != nil { err = multierr.Append(err, enc.AddObject("query", v.Query)) } if v.DecisionInfo != nil { err = multierr.Append(err, enc.AddObject("decisionInfo", v.DecisionInfo)) } if v.WorkflowExecutionTaskList != nil { err = multierr.Append(err, enc.AddObject("WorkflowExecutionTaskList", v.WorkflowExecutionTaskList)) } if v.EventStoreVersion != nil { enc.AddInt32("eventStoreVersion", *v.EventStoreVersion) } if v.BranchToken != nil { enc.AddString("branchToken", base64.StdEncoding.EncodeToString(v.BranchToken)) } if v.ScheduledTimestamp != nil { enc.AddInt64("scheduledTimestamp", *v.ScheduledTimestamp) } if v.StartedTimestamp != nil { enc.AddInt64("startedTimestamp", *v.StartedTimestamp) } if v.Queries != nil { err = multierr.Append(err, enc.AddObject("queries", (_Map_String_WorkflowQuery_Zapper)(v.Queries))) } if v.TotalHistoryBytes != nil { enc.AddInt64("totalHistoryBytes", *v.TotalHistoryBytes) } if v.AutoConfigHint != nil { err = multierr.Append(err, enc.AddObject("autoConfigHint", v.AutoConfigHint)) } return err } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *PollForDecisionTaskResponse) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetWorkflowExecution() (o *shared.WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *PollForDecisionTaskResponse) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetWorkflowType() (o *shared.WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *PollForDecisionTaskResponse) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetPreviousStartedEventId returns the value of PreviousStartedEventId if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetPreviousStartedEventId() (o int64) { if v != nil && v.PreviousStartedEventId != nil { return *v.PreviousStartedEventId } return } // IsSetPreviousStartedEventId returns true if PreviousStartedEventId is not nil. func (v *PollForDecisionTaskResponse) IsSetPreviousStartedEventId() bool { return v != nil && v.PreviousStartedEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *PollForDecisionTaskResponse) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetAttempt() (o int64) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *PollForDecisionTaskResponse) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetNextEventId returns the value of NextEventId if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetNextEventId() (o int64) { if v != nil && v.NextEventId != nil { return *v.NextEventId } return } // IsSetNextEventId returns true if NextEventId is not nil. func (v *PollForDecisionTaskResponse) IsSetNextEventId() bool { return v != nil && v.NextEventId != nil } // GetBacklogCountHint returns the value of BacklogCountHint if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetBacklogCountHint() (o int64) { if v != nil && v.BacklogCountHint != nil { return *v.BacklogCountHint } return } // IsSetBacklogCountHint returns true if BacklogCountHint is not nil. func (v *PollForDecisionTaskResponse) IsSetBacklogCountHint() bool { return v != nil && v.BacklogCountHint != nil } // GetStickyExecutionEnabled returns the value of StickyExecutionEnabled if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetStickyExecutionEnabled() (o bool) { if v != nil && v.StickyExecutionEnabled != nil { return *v.StickyExecutionEnabled } return } // IsSetStickyExecutionEnabled returns true if StickyExecutionEnabled is not nil. func (v *PollForDecisionTaskResponse) IsSetStickyExecutionEnabled() bool { return v != nil && v.StickyExecutionEnabled != nil } // GetQuery returns the value of Query if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetQuery() (o *shared.WorkflowQuery) { if v != nil && v.Query != nil { return v.Query } return } // IsSetQuery returns true if Query is not nil. func (v *PollForDecisionTaskResponse) IsSetQuery() bool { return v != nil && v.Query != nil } // GetDecisionInfo returns the value of DecisionInfo if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetDecisionInfo() (o *shared.TransientDecisionInfo) { if v != nil && v.DecisionInfo != nil { return v.DecisionInfo } return } // IsSetDecisionInfo returns true if DecisionInfo is not nil. func (v *PollForDecisionTaskResponse) IsSetDecisionInfo() bool { return v != nil && v.DecisionInfo != nil } // GetWorkflowExecutionTaskList returns the value of WorkflowExecutionTaskList if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetWorkflowExecutionTaskList() (o *shared.TaskList) { if v != nil && v.WorkflowExecutionTaskList != nil { return v.WorkflowExecutionTaskList } return } // IsSetWorkflowExecutionTaskList returns true if WorkflowExecutionTaskList is not nil. func (v *PollForDecisionTaskResponse) IsSetWorkflowExecutionTaskList() bool { return v != nil && v.WorkflowExecutionTaskList != nil } // GetEventStoreVersion returns the value of EventStoreVersion if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetEventStoreVersion() (o int32) { if v != nil && v.EventStoreVersion != nil { return *v.EventStoreVersion } return } // IsSetEventStoreVersion returns true if EventStoreVersion is not nil. func (v *PollForDecisionTaskResponse) IsSetEventStoreVersion() bool { return v != nil && v.EventStoreVersion != nil } // GetBranchToken returns the value of BranchToken if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetBranchToken() (o []byte) { if v != nil && v.BranchToken != nil { return v.BranchToken } return } // IsSetBranchToken returns true if BranchToken is not nil. func (v *PollForDecisionTaskResponse) IsSetBranchToken() bool { return v != nil && v.BranchToken != nil } // GetScheduledTimestamp returns the value of ScheduledTimestamp if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetScheduledTimestamp() (o int64) { if v != nil && v.ScheduledTimestamp != nil { return *v.ScheduledTimestamp } return } // IsSetScheduledTimestamp returns true if ScheduledTimestamp is not nil. func (v *PollForDecisionTaskResponse) IsSetScheduledTimestamp() bool { return v != nil && v.ScheduledTimestamp != nil } // GetStartedTimestamp returns the value of StartedTimestamp if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetStartedTimestamp() (o int64) { if v != nil && v.StartedTimestamp != nil { return *v.StartedTimestamp } return } // IsSetStartedTimestamp returns true if StartedTimestamp is not nil. func (v *PollForDecisionTaskResponse) IsSetStartedTimestamp() bool { return v != nil && v.StartedTimestamp != nil } // GetQueries returns the value of Queries if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetQueries() (o map[string]*shared.WorkflowQuery) { if v != nil && v.Queries != nil { return v.Queries } return } // IsSetQueries returns true if Queries is not nil. func (v *PollForDecisionTaskResponse) IsSetQueries() bool { return v != nil && v.Queries != nil } // GetTotalHistoryBytes returns the value of TotalHistoryBytes if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetTotalHistoryBytes() (o int64) { if v != nil && v.TotalHistoryBytes != nil { return *v.TotalHistoryBytes } return } // IsSetTotalHistoryBytes returns true if TotalHistoryBytes is not nil. func (v *PollForDecisionTaskResponse) IsSetTotalHistoryBytes() bool { return v != nil && v.TotalHistoryBytes != nil } // GetAutoConfigHint returns the value of AutoConfigHint if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetAutoConfigHint() (o *shared.AutoConfigHint) { if v != nil && v.AutoConfigHint != nil { return v.AutoConfigHint } return } // IsSetAutoConfigHint returns true if AutoConfigHint is not nil. func (v *PollForDecisionTaskResponse) IsSetAutoConfigHint() bool { return v != nil && v.AutoConfigHint != nil } type QueryWorkflowRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` TaskList *shared.TaskList `json:"taskList,omitempty"` QueryRequest *shared.QueryWorkflowRequest `json:"queryRequest,omitempty"` ForwardedFrom *string `json:"forwardedFrom,omitempty"` } // ToWire translates a QueryWorkflowRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *QueryWorkflowRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.QueryRequest != nil { w, err = v.QueryRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ForwardedFrom != nil { w, err = wire.NewValueString(*(v.ForwardedFrom)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryWorkflowRequest_Read(w wire.Value) (*shared.QueryWorkflowRequest, error) { var v shared.QueryWorkflowRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a QueryWorkflowRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a QueryWorkflowRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v QueryWorkflowRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *QueryWorkflowRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.QueryRequest, err = _QueryWorkflowRequest_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ForwardedFrom = &x if err != nil { return err } } } } return nil } // Encode serializes a QueryWorkflowRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a QueryWorkflowRequest struct could not be encoded. func (v *QueryWorkflowRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.QueryRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ForwardedFrom != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ForwardedFrom)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryWorkflowRequest_Decode(sr stream.Reader) (*shared.QueryWorkflowRequest, error) { var v shared.QueryWorkflowRequest err := v.Decode(sr) return &v, err } // Decode deserializes a QueryWorkflowRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a QueryWorkflowRequest struct could not be generated from the wire // representation. func (v *QueryWorkflowRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.QueryRequest, err = _QueryWorkflowRequest_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ForwardedFrom = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a QueryWorkflowRequest // struct. func (v *QueryWorkflowRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.QueryRequest != nil { fields[i] = fmt.Sprintf("QueryRequest: %v", v.QueryRequest) i++ } if v.ForwardedFrom != nil { fields[i] = fmt.Sprintf("ForwardedFrom: %v", *(v.ForwardedFrom)) i++ } return fmt.Sprintf("QueryWorkflowRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this QueryWorkflowRequest match the // provided QueryWorkflowRequest. // // This function performs a deep comparison. func (v *QueryWorkflowRequest) Equals(rhs *QueryWorkflowRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.QueryRequest == nil && rhs.QueryRequest == nil) || (v.QueryRequest != nil && rhs.QueryRequest != nil && v.QueryRequest.Equals(rhs.QueryRequest))) { return false } if !_String_EqualsPtr(v.ForwardedFrom, rhs.ForwardedFrom) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryWorkflowRequest. func (v *QueryWorkflowRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.QueryRequest != nil { err = multierr.Append(err, enc.AddObject("queryRequest", v.QueryRequest)) } if v.ForwardedFrom != nil { enc.AddString("forwardedFrom", *v.ForwardedFrom) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *QueryWorkflowRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetTaskList() (o *shared.TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *QueryWorkflowRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetQueryRequest returns the value of QueryRequest if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetQueryRequest() (o *shared.QueryWorkflowRequest) { if v != nil && v.QueryRequest != nil { return v.QueryRequest } return } // IsSetQueryRequest returns true if QueryRequest is not nil. func (v *QueryWorkflowRequest) IsSetQueryRequest() bool { return v != nil && v.QueryRequest != nil } // GetForwardedFrom returns the value of ForwardedFrom if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetForwardedFrom() (o string) { if v != nil && v.ForwardedFrom != nil { return *v.ForwardedFrom } return } // IsSetForwardedFrom returns true if ForwardedFrom is not nil. func (v *QueryWorkflowRequest) IsSetForwardedFrom() bool { return v != nil && v.ForwardedFrom != nil } type RespondQueryTaskCompletedRequest struct { DomainUUID *string `json:"domainUUID,omitempty"` TaskList *shared.TaskList `json:"taskList,omitempty"` TaskID *string `json:"taskID,omitempty"` CompletedRequest *shared.RespondQueryTaskCompletedRequest `json:"completedRequest,omitempty"` } // ToWire translates a RespondQueryTaskCompletedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondQueryTaskCompletedRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.DomainUUID != nil { w, err = wire.NewValueString(*(v.DomainUUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TaskID != nil { w, err = wire.NewValueString(*(v.TaskID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.CompletedRequest != nil { w, err = v.CompletedRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondQueryTaskCompletedRequest_Read(w wire.Value) (*shared.RespondQueryTaskCompletedRequest, error) { var v shared.RespondQueryTaskCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a RespondQueryTaskCompletedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondQueryTaskCompletedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondQueryTaskCompletedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondQueryTaskCompletedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainUUID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TaskID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.CompletedRequest, err = _RespondQueryTaskCompletedRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RespondQueryTaskCompletedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondQueryTaskCompletedRequest struct could not be encoded. func (v *RespondQueryTaskCompletedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainUUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainUUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TaskID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompletedRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.CompletedRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondQueryTaskCompletedRequest_Decode(sr stream.Reader) (*shared.RespondQueryTaskCompletedRequest, error) { var v shared.RespondQueryTaskCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a RespondQueryTaskCompletedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondQueryTaskCompletedRequest struct could not be generated from the wire // representation. func (v *RespondQueryTaskCompletedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainUUID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TaskID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.CompletedRequest, err = _RespondQueryTaskCompletedRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondQueryTaskCompletedRequest // struct. func (v *RespondQueryTaskCompletedRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.DomainUUID != nil { fields[i] = fmt.Sprintf("DomainUUID: %v", *(v.DomainUUID)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.TaskID != nil { fields[i] = fmt.Sprintf("TaskID: %v", *(v.TaskID)) i++ } if v.CompletedRequest != nil { fields[i] = fmt.Sprintf("CompletedRequest: %v", v.CompletedRequest) i++ } return fmt.Sprintf("RespondQueryTaskCompletedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondQueryTaskCompletedRequest match the // provided RespondQueryTaskCompletedRequest. // // This function performs a deep comparison. func (v *RespondQueryTaskCompletedRequest) Equals(rhs *RespondQueryTaskCompletedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainUUID, rhs.DomainUUID) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !_String_EqualsPtr(v.TaskID, rhs.TaskID) { return false } if !((v.CompletedRequest == nil && rhs.CompletedRequest == nil) || (v.CompletedRequest != nil && rhs.CompletedRequest != nil && v.CompletedRequest.Equals(rhs.CompletedRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondQueryTaskCompletedRequest. func (v *RespondQueryTaskCompletedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainUUID != nil { enc.AddString("domainUUID", *v.DomainUUID) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.TaskID != nil { enc.AddString("taskID", *v.TaskID) } if v.CompletedRequest != nil { err = multierr.Append(err, enc.AddObject("completedRequest", v.CompletedRequest)) } return err } // GetDomainUUID returns the value of DomainUUID if it is set or its // zero value if it is unset. func (v *RespondQueryTaskCompletedRequest) GetDomainUUID() (o string) { if v != nil && v.DomainUUID != nil { return *v.DomainUUID } return } // IsSetDomainUUID returns true if DomainUUID is not nil. func (v *RespondQueryTaskCompletedRequest) IsSetDomainUUID() bool { return v != nil && v.DomainUUID != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *RespondQueryTaskCompletedRequest) GetTaskList() (o *shared.TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *RespondQueryTaskCompletedRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetTaskID returns the value of TaskID if it is set or its // zero value if it is unset. func (v *RespondQueryTaskCompletedRequest) GetTaskID() (o string) { if v != nil && v.TaskID != nil { return *v.TaskID } return } // IsSetTaskID returns true if TaskID is not nil. func (v *RespondQueryTaskCompletedRequest) IsSetTaskID() bool { return v != nil && v.TaskID != nil } // GetCompletedRequest returns the value of CompletedRequest if it is set or its // zero value if it is unset. func (v *RespondQueryTaskCompletedRequest) GetCompletedRequest() (o *shared.RespondQueryTaskCompletedRequest) { if v != nil && v.CompletedRequest != nil { return v.CompletedRequest } return } // IsSetCompletedRequest returns true if CompletedRequest is not nil. func (v *RespondQueryTaskCompletedRequest) IsSetCompletedRequest() bool { return v != nil && v.CompletedRequest != nil } type TaskSource int32 const ( TaskSourceHistory TaskSource = 0 TaskSourceDbBacklog TaskSource = 1 ) // TaskSource_Values returns all recognized values of TaskSource. func TaskSource_Values() []TaskSource { return []TaskSource{ TaskSourceHistory, TaskSourceDbBacklog, } } // UnmarshalText tries to decode TaskSource from a byte slice // containing its name. // // var v TaskSource // err := v.UnmarshalText([]byte("HISTORY")) func (v *TaskSource) UnmarshalText(value []byte) error { switch s := string(value); s { case "HISTORY": *v = TaskSourceHistory return nil case "DB_BACKLOG": *v = TaskSourceDbBacklog return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "TaskSource", err) } *v = TaskSource(val) return nil } } // MarshalText encodes TaskSource to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v TaskSource) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("HISTORY"), nil case 1: return []byte("DB_BACKLOG"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskSource. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v TaskSource) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "HISTORY") case 1: enc.AddString("name", "DB_BACKLOG") } return nil } // Ptr returns a pointer to this enum value. func (v TaskSource) Ptr() *TaskSource { return &v } // Encode encodes TaskSource directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v TaskSource // return v.Encode(sWriter) func (v TaskSource) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates TaskSource into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v TaskSource) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes TaskSource from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return TaskSource(0), err // } // // var v TaskSource // if err := v.FromWire(x); err != nil { // return TaskSource(0), err // } // return v, nil func (v *TaskSource) FromWire(w wire.Value) error { *v = (TaskSource)(w.GetI32()) return nil } // Decode reads off the encoded TaskSource directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v TaskSource // if err := v.Decode(sReader); err != nil { // return TaskSource(0), err // } // return v, nil func (v *TaskSource) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (TaskSource)(i) return nil } // String returns a readable string representation of TaskSource. func (v TaskSource) String() string { w := int32(v) switch w { case 0: return "HISTORY" case 1: return "DB_BACKLOG" } return fmt.Sprintf("TaskSource(%d)", w) } // Equals returns true if this TaskSource value matches the provided // value. func (v TaskSource) Equals(rhs TaskSource) bool { return v == rhs } // MarshalJSON serializes TaskSource into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v TaskSource) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"HISTORY\""), nil case 1: return ([]byte)("\"DB_BACKLOG\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode TaskSource from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *TaskSource) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "TaskSource") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "TaskSource") } *v = (TaskSource)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "TaskSource") } } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "matching", Package: "github.com/uber/cadence/.gen/go/matching", FilePath: "matching.thrift", SHA1: "e7db050ae64980b33e3f8ed3f6a3bba3d63174b3", Includes: []*thriftreflect.ThriftModule{ shared.ThriftModule, }, Raw: rawIDL, } const rawIDL = "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\ninclude \"shared.thrift\"\n\nnamespace java com.uber.cadence.matching\n\n// TaskSource is the source from which a task was produced\nenum TaskSource {\n HISTORY, // Task produced by history service\n DB_BACKLOG // Task produced from matching db backlog\n}\n\nstruct PollForDecisionTaskRequest {\n 10: optional string domainUUID\n 15: optional string pollerID\n 20: optional shared.PollForDecisionTaskRequest pollRequest\n 30: optional string forwardedFrom\n 40: optional string isolationGroup\n}\n\nstruct PollForDecisionTaskResponse {\n 10: optional binary taskToken\n 20: optional shared.WorkflowExecution workflowExecution\n 30: optional shared.WorkflowType workflowType\n 40: optional i64 (js.type = \"Long\") previousStartedEventId\n 50: optional i64 (js.type = \"Long\") startedEventId\n 51: optional i64 (js.type = \"Long\") attempt\n 60: optional i64 (js.type = \"Long\") nextEventId\n 65: optional i64 (js.type = \"Long\") backlogCountHint\n 70: optional bool stickyExecutionEnabled\n 80: optional shared.WorkflowQuery query\n 90: optional shared.TransientDecisionInfo decisionInfo\n 100: optional shared.TaskList WorkflowExecutionTaskList\n 110: optional i32 eventStoreVersion\n 120: optional binary branchToken\n 130: optional i64 (js.type = \"Long\") scheduledTimestamp\n 140: optional i64 (js.type = \"Long\") startedTimestamp\n 150: optional map queries\n 160: optional i64 (js.type = \"Long\") totalHistoryBytes\n 170: optional shared.AutoConfigHint autoConfigHint\n}\n\nstruct PollForActivityTaskRequest {\n 10: optional string domainUUID\n 15: optional string pollerID\n 20: optional shared.PollForActivityTaskRequest pollRequest\n 30: optional string forwardedFrom\n 40: optional string isolationGroup\n}\n\nstruct AddDecisionTaskRequest {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution execution\n 30: optional shared.TaskList taskList\n 40: optional i64 (js.type = \"Long\") scheduleId\n 50: optional i32 scheduleToStartTimeoutSeconds\n 59: optional TaskSource source\n 60: optional string forwardedFrom\n 70: optional map partitionConfig\n}\n\nstruct AddActivityTaskRequest {\n 10: optional string domainUUID\n 20: optional shared.WorkflowExecution execution\n 30: optional string sourceDomainUUID\n 40: optional shared.TaskList taskList\n 50: optional i64 (js.type = \"Long\") scheduleId\n 60: optional i32 scheduleToStartTimeoutSeconds\n 69: optional TaskSource source\n 70: optional string forwardedFrom\n 80: optional ActivityTaskDispatchInfo activityTaskDispatchInfo\n 90: optional map partitionConfig\n}\n\nstruct ActivityTaskDispatchInfo {\n 10: optional shared.HistoryEvent scheduledEvent\n 20: optional i64 (js.type = \"Long\") startedTimestamp\n 30: optional i64 (js.type = \"Long\") attempt\n 40: optional i64 (js.type = \"Long\") scheduledTimestampOfThisAttempt\n 50: optional i64 (js.type = \"Long\") scheduledTimestamp\n 60: optional binary heartbeatDetails\n 70: optional shared.WorkflowType workflowType\n 80: optional string workflowDomain\n}\n\nstruct QueryWorkflowRequest {\n 10: optional string domainUUID\n 20: optional shared.TaskList taskList\n 30: optional shared.QueryWorkflowRequest queryRequest\n 40: optional string forwardedFrom\n}\n\nstruct RespondQueryTaskCompletedRequest {\n 10: optional string domainUUID\n 20: optional shared.TaskList taskList\n 30: optional string taskID\n 40: optional shared.RespondQueryTaskCompletedRequest completedRequest\n}\n\nstruct CancelOutstandingPollRequest {\n 10: optional string domainUUID\n 20: optional i32 taskListType\n 30: optional shared.TaskList taskList\n 40: optional string pollerID\n}\n\nstruct DescribeTaskListRequest {\n 10: optional string domainUUID\n 20: optional shared.DescribeTaskListRequest descRequest\n}\n\nstruct ListTaskListPartitionsRequest {\n 10: optional string domain\n 20: optional shared.TaskList taskList\n}\n\n/**\n* MatchingService API is exposed to provide support for polling from long running applications.\n* Such applications are expected to have a worker which regularly polls for DecisionTask and ActivityTask. For each\n* DecisionTask, application is expected to process the history of events for that session and respond back with next\n* decisions. For each ActivityTask, application is expected to execute the actual logic for that task and respond back\n* with completion or failure.\n**/\nservice MatchingService {\n /**\n * PollForDecisionTask is called by frontend to process DecisionTask from a specific taskList. A\n * DecisionTask is dispatched to callers for active workflow executions, with pending decisions.\n **/\n PollForDecisionTaskResponse PollForDecisionTask(1: PollForDecisionTaskRequest pollRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.LimitExceededError limitExceededError,\n 4: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * PollForActivityTask is called by frontend to process ActivityTask from a specific taskList. ActivityTask\n * is dispatched to callers whenever a ScheduleTask decision is made for a workflow execution.\n **/\n shared.PollForActivityTaskResponse PollForActivityTask(1: PollForActivityTaskRequest pollRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.LimitExceededError limitExceededError,\n 4: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * AddDecisionTask is called by the history service when a decision task is scheduled, so that it can be dispatched\n * by the MatchingEngine.\n **/\n void AddDecisionTask(1: AddDecisionTaskRequest addRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.RemoteSyncMatchedError remoteSyncMatchedError,\n 7: shared.StickyWorkerUnavailableError stickyWorkerUnavailableError,\n 8: shared.TaskListNotOwnedByHostError taskListNotOwnedByHostError,\n )\n\n /**\n * AddActivityTask is called by the history service when a decision task is scheduled, so that it can be dispatched\n * by the MatchingEngine.\n **/\n void AddActivityTask(1: AddActivityTaskRequest addRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.DomainNotActiveError domainNotActiveError,\n 6: shared.RemoteSyncMatchedError remoteSyncMatchedError,\n 7: shared.TaskListNotOwnedByHostError taskListNotOwnedByHostError,\n )\n\n /**\n * QueryWorkflow is called by frontend to query a workflow.\n **/\n shared.QueryWorkflowResponse QueryWorkflow(1: QueryWorkflowRequest queryRequest)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.QueryFailedError queryFailedError,\n 5: shared.LimitExceededError limitExceededError,\n 6: shared.ServiceBusyError serviceBusyError,\n 7: shared.StickyWorkerUnavailableError stickyWorkerUnavailableError,\n 8: shared.TaskListNotOwnedByHostError taskListNotOwnedByHostError,\n )\n\n /**\n * RespondQueryTaskCompleted is called by frontend to respond query completed.\n **/\n void RespondQueryTaskCompleted(1: RespondQueryTaskCompletedRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.LimitExceededError limitExceededError,\n 5: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * CancelOutstandingPoll is called by frontend to unblock long polls on matching for zombie pollers.\n * Our rpc stack does not support context propagation, so when a client connection goes away frontend sees\n * cancellation of context for that handler, but any corresponding calls (long-poll) to matching service does not\n * see the cancellation propagated so it can unblock corresponding long-polls on its end. This results is tasks\n * being dispatched to zombie pollers in this situation. This API is added so everytime frontend makes a long-poll\n * api call to matching it passes in a pollerID and then calls this API when it detects client connection is closed\n * to unblock long polls for this poller and prevent tasks being sent to these zombie pollers.\n **/\n void CancelOutstandingPoll(1: CancelOutstandingPollRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.ServiceBusyError serviceBusyError,\n 4: shared.TaskListNotOwnedByHostError taskListNotOwnedByHostError,\n )\n\n /**\n * DescribeTaskList returns information about the target tasklist, right now this API returns the\n * pollers which polled this tasklist in last few minutes.\n **/\n shared.DescribeTaskListResponse DescribeTaskList(1: DescribeTaskListRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n 5: shared.TaskListNotOwnedByHostError taskListNotOwnedByHostError,\n )\n\n /**\n * GetTaskListsByDomain returns the list of all the task lists for a domainName.\n **/\n shared.GetTaskListsByDomainResponse GetTaskListsByDomain(1: shared.GetTaskListsByDomainRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 3: shared.EntityNotExistsError entityNotExistError,\n 4: shared.ServiceBusyError serviceBusyError,\n )\n\n /**\n * ListTaskListPartitions returns a map of partitionKey and hostAddress for a taskList\n **/\n shared.ListTaskListPartitionsResponse ListTaskListPartitions(1: ListTaskListPartitionsRequest request)\n throws (\n 1: shared.BadRequestError badRequestError,\n 2: shared.InternalServiceError internalServiceError,\n 4: shared.ServiceBusyError serviceBusyError,\n )\n}\n" // MatchingService_AddActivityTask_Args represents the arguments for the MatchingService.AddActivityTask function. // // The arguments for AddActivityTask are sent and received over the wire as this struct. type MatchingService_AddActivityTask_Args struct { AddRequest *AddActivityTaskRequest `json:"addRequest,omitempty"` } // ToWire translates a MatchingService_AddActivityTask_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_AddActivityTask_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.AddRequest != nil { w, err = v.AddRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AddActivityTaskRequest_Read(w wire.Value) (*AddActivityTaskRequest, error) { var v AddActivityTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_AddActivityTask_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_AddActivityTask_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_AddActivityTask_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_AddActivityTask_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.AddRequest, err = _AddActivityTaskRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MatchingService_AddActivityTask_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_AddActivityTask_Args struct could not be encoded. func (v *MatchingService_AddActivityTask_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.AddRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.AddRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _AddActivityTaskRequest_Decode(sr stream.Reader) (*AddActivityTaskRequest, error) { var v AddActivityTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_AddActivityTask_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_AddActivityTask_Args struct could not be generated from the wire // representation. func (v *MatchingService_AddActivityTask_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.AddRequest, err = _AddActivityTaskRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MatchingService_AddActivityTask_Args // struct. func (v *MatchingService_AddActivityTask_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.AddRequest != nil { fields[i] = fmt.Sprintf("AddRequest: %v", v.AddRequest) i++ } return fmt.Sprintf("MatchingService_AddActivityTask_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_AddActivityTask_Args match the // provided MatchingService_AddActivityTask_Args. // // This function performs a deep comparison. func (v *MatchingService_AddActivityTask_Args) Equals(rhs *MatchingService_AddActivityTask_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.AddRequest == nil && rhs.AddRequest == nil) || (v.AddRequest != nil && rhs.AddRequest != nil && v.AddRequest.Equals(rhs.AddRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_AddActivityTask_Args. func (v *MatchingService_AddActivityTask_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.AddRequest != nil { err = multierr.Append(err, enc.AddObject("addRequest", v.AddRequest)) } return err } // GetAddRequest returns the value of AddRequest if it is set or its // zero value if it is unset. func (v *MatchingService_AddActivityTask_Args) GetAddRequest() (o *AddActivityTaskRequest) { if v != nil && v.AddRequest != nil { return v.AddRequest } return } // IsSetAddRequest returns true if AddRequest is not nil. func (v *MatchingService_AddActivityTask_Args) IsSetAddRequest() bool { return v != nil && v.AddRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "AddActivityTask" for this struct. func (v *MatchingService_AddActivityTask_Args) MethodName() string { return "AddActivityTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *MatchingService_AddActivityTask_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // MatchingService_AddActivityTask_Helper provides functions that aid in handling the // parameters and return values of the MatchingService.AddActivityTask // function. var MatchingService_AddActivityTask_Helper = struct { // Args accepts the parameters of AddActivityTask in-order and returns // the arguments struct for the function. Args func( addRequest *AddActivityTaskRequest, ) *MatchingService_AddActivityTask_Args // IsException returns true if the given error can be thrown // by AddActivityTask. // // An error can be thrown by AddActivityTask only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for AddActivityTask // given the error returned by it. The provided error may // be nil if AddActivityTask did not fail. // // This allows mapping errors returned by AddActivityTask into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // AddActivityTask // // err := AddActivityTask(args) // result, err := MatchingService_AddActivityTask_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from AddActivityTask: %v", err) // } // serialize(result) WrapResponse func(error) (*MatchingService_AddActivityTask_Result, error) // UnwrapResponse takes the result struct for AddActivityTask // and returns the erorr returned by it (if any). // // The error is non-nil only if AddActivityTask threw an // exception. // // result := deserialize(bytes) // err := MatchingService_AddActivityTask_Helper.UnwrapResponse(result) UnwrapResponse func(*MatchingService_AddActivityTask_Result) error }{} func init() { MatchingService_AddActivityTask_Helper.Args = func( addRequest *AddActivityTaskRequest, ) *MatchingService_AddActivityTask_Args { return &MatchingService_AddActivityTask_Args{ AddRequest: addRequest, } } MatchingService_AddActivityTask_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.LimitExceededError: return true case *shared.DomainNotActiveError: return true case *shared.RemoteSyncMatchedError: return true case *shared.TaskListNotOwnedByHostError: return true default: return false } } MatchingService_AddActivityTask_Helper.WrapResponse = func(err error) (*MatchingService_AddActivityTask_Result, error) { if err == nil { return &MatchingService_AddActivityTask_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddActivityTask_Result.BadRequestError") } return &MatchingService_AddActivityTask_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddActivityTask_Result.InternalServiceError") } return &MatchingService_AddActivityTask_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddActivityTask_Result.ServiceBusyError") } return &MatchingService_AddActivityTask_Result{ServiceBusyError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddActivityTask_Result.LimitExceededError") } return &MatchingService_AddActivityTask_Result{LimitExceededError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddActivityTask_Result.DomainNotActiveError") } return &MatchingService_AddActivityTask_Result{DomainNotActiveError: e}, nil case *shared.RemoteSyncMatchedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddActivityTask_Result.RemoteSyncMatchedError") } return &MatchingService_AddActivityTask_Result{RemoteSyncMatchedError: e}, nil case *shared.TaskListNotOwnedByHostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddActivityTask_Result.TaskListNotOwnedByHostError") } return &MatchingService_AddActivityTask_Result{TaskListNotOwnedByHostError: e}, nil } return nil, err } MatchingService_AddActivityTask_Helper.UnwrapResponse = func(result *MatchingService_AddActivityTask_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.RemoteSyncMatchedError != nil { err = result.RemoteSyncMatchedError return } if result.TaskListNotOwnedByHostError != nil { err = result.TaskListNotOwnedByHostError return } return } } // MatchingService_AddActivityTask_Result represents the result of a MatchingService.AddActivityTask function call. // // The result of a AddActivityTask execution is sent and received over the wire as this struct. type MatchingService_AddActivityTask_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` RemoteSyncMatchedError *shared.RemoteSyncMatchedError `json:"remoteSyncMatchedError,omitempty"` TaskListNotOwnedByHostError *shared.TaskListNotOwnedByHostError `json:"taskListNotOwnedByHostError,omitempty"` } // ToWire translates a MatchingService_AddActivityTask_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_AddActivityTask_Result) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.RemoteSyncMatchedError != nil { w, err = v.RemoteSyncMatchedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.TaskListNotOwnedByHostError != nil { w, err = v.TaskListNotOwnedByHostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("MatchingService_AddActivityTask_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _BadRequestError_Read(w wire.Value) (*shared.BadRequestError, error) { var v shared.BadRequestError err := v.FromWire(w) return &v, err } func _InternalServiceError_Read(w wire.Value) (*shared.InternalServiceError, error) { var v shared.InternalServiceError err := v.FromWire(w) return &v, err } func _ServiceBusyError_Read(w wire.Value) (*shared.ServiceBusyError, error) { var v shared.ServiceBusyError err := v.FromWire(w) return &v, err } func _LimitExceededError_Read(w wire.Value) (*shared.LimitExceededError, error) { var v shared.LimitExceededError err := v.FromWire(w) return &v, err } func _DomainNotActiveError_Read(w wire.Value) (*shared.DomainNotActiveError, error) { var v shared.DomainNotActiveError err := v.FromWire(w) return &v, err } func _RemoteSyncMatchedError_Read(w wire.Value) (*shared.RemoteSyncMatchedError, error) { var v shared.RemoteSyncMatchedError err := v.FromWire(w) return &v, err } func _TaskListNotOwnedByHostError_Read(w wire.Value) (*shared.TaskListNotOwnedByHostError, error) { var v shared.TaskListNotOwnedByHostError err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_AddActivityTask_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_AddActivityTask_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_AddActivityTask_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_AddActivityTask_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.RemoteSyncMatchedError, err = _RemoteSyncMatchedError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.TaskListNotOwnedByHostError, err = _TaskListNotOwnedByHostError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.RemoteSyncMatchedError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_AddActivityTask_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a MatchingService_AddActivityTask_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_AddActivityTask_Result struct could not be encoded. func (v *MatchingService_AddActivityTask_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RemoteSyncMatchedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.RemoteSyncMatchedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListNotOwnedByHostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.TaskListNotOwnedByHostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.RemoteSyncMatchedError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_AddActivityTask_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _BadRequestError_Decode(sr stream.Reader) (*shared.BadRequestError, error) { var v shared.BadRequestError err := v.Decode(sr) return &v, err } func _InternalServiceError_Decode(sr stream.Reader) (*shared.InternalServiceError, error) { var v shared.InternalServiceError err := v.Decode(sr) return &v, err } func _ServiceBusyError_Decode(sr stream.Reader) (*shared.ServiceBusyError, error) { var v shared.ServiceBusyError err := v.Decode(sr) return &v, err } func _LimitExceededError_Decode(sr stream.Reader) (*shared.LimitExceededError, error) { var v shared.LimitExceededError err := v.Decode(sr) return &v, err } func _DomainNotActiveError_Decode(sr stream.Reader) (*shared.DomainNotActiveError, error) { var v shared.DomainNotActiveError err := v.Decode(sr) return &v, err } func _RemoteSyncMatchedError_Decode(sr stream.Reader) (*shared.RemoteSyncMatchedError, error) { var v shared.RemoteSyncMatchedError err := v.Decode(sr) return &v, err } func _TaskListNotOwnedByHostError_Decode(sr stream.Reader) (*shared.TaskListNotOwnedByHostError, error) { var v shared.TaskListNotOwnedByHostError err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_AddActivityTask_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_AddActivityTask_Result struct could not be generated from the wire // representation. func (v *MatchingService_AddActivityTask_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.RemoteSyncMatchedError, err = _RemoteSyncMatchedError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.TaskListNotOwnedByHostError, err = _TaskListNotOwnedByHostError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.RemoteSyncMatchedError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_AddActivityTask_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a MatchingService_AddActivityTask_Result // struct. func (v *MatchingService_AddActivityTask_Result) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.RemoteSyncMatchedError != nil { fields[i] = fmt.Sprintf("RemoteSyncMatchedError: %v", v.RemoteSyncMatchedError) i++ } if v.TaskListNotOwnedByHostError != nil { fields[i] = fmt.Sprintf("TaskListNotOwnedByHostError: %v", v.TaskListNotOwnedByHostError) i++ } return fmt.Sprintf("MatchingService_AddActivityTask_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_AddActivityTask_Result match the // provided MatchingService_AddActivityTask_Result. // // This function performs a deep comparison. func (v *MatchingService_AddActivityTask_Result) Equals(rhs *MatchingService_AddActivityTask_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.RemoteSyncMatchedError == nil && rhs.RemoteSyncMatchedError == nil) || (v.RemoteSyncMatchedError != nil && rhs.RemoteSyncMatchedError != nil && v.RemoteSyncMatchedError.Equals(rhs.RemoteSyncMatchedError))) { return false } if !((v.TaskListNotOwnedByHostError == nil && rhs.TaskListNotOwnedByHostError == nil) || (v.TaskListNotOwnedByHostError != nil && rhs.TaskListNotOwnedByHostError != nil && v.TaskListNotOwnedByHostError.Equals(rhs.TaskListNotOwnedByHostError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_AddActivityTask_Result. func (v *MatchingService_AddActivityTask_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.RemoteSyncMatchedError != nil { err = multierr.Append(err, enc.AddObject("remoteSyncMatchedError", v.RemoteSyncMatchedError)) } if v.TaskListNotOwnedByHostError != nil { err = multierr.Append(err, enc.AddObject("taskListNotOwnedByHostError", v.TaskListNotOwnedByHostError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *MatchingService_AddActivityTask_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *MatchingService_AddActivityTask_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *MatchingService_AddActivityTask_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *MatchingService_AddActivityTask_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *MatchingService_AddActivityTask_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *MatchingService_AddActivityTask_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *MatchingService_AddActivityTask_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *MatchingService_AddActivityTask_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *MatchingService_AddActivityTask_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *MatchingService_AddActivityTask_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetRemoteSyncMatchedError returns the value of RemoteSyncMatchedError if it is set or its // zero value if it is unset. func (v *MatchingService_AddActivityTask_Result) GetRemoteSyncMatchedError() (o *shared.RemoteSyncMatchedError) { if v != nil && v.RemoteSyncMatchedError != nil { return v.RemoteSyncMatchedError } return } // IsSetRemoteSyncMatchedError returns true if RemoteSyncMatchedError is not nil. func (v *MatchingService_AddActivityTask_Result) IsSetRemoteSyncMatchedError() bool { return v != nil && v.RemoteSyncMatchedError != nil } // GetTaskListNotOwnedByHostError returns the value of TaskListNotOwnedByHostError if it is set or its // zero value if it is unset. func (v *MatchingService_AddActivityTask_Result) GetTaskListNotOwnedByHostError() (o *shared.TaskListNotOwnedByHostError) { if v != nil && v.TaskListNotOwnedByHostError != nil { return v.TaskListNotOwnedByHostError } return } // IsSetTaskListNotOwnedByHostError returns true if TaskListNotOwnedByHostError is not nil. func (v *MatchingService_AddActivityTask_Result) IsSetTaskListNotOwnedByHostError() bool { return v != nil && v.TaskListNotOwnedByHostError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "AddActivityTask" for this struct. func (v *MatchingService_AddActivityTask_Result) MethodName() string { return "AddActivityTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *MatchingService_AddActivityTask_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // MatchingService_AddDecisionTask_Args represents the arguments for the MatchingService.AddDecisionTask function. // // The arguments for AddDecisionTask are sent and received over the wire as this struct. type MatchingService_AddDecisionTask_Args struct { AddRequest *AddDecisionTaskRequest `json:"addRequest,omitempty"` } // ToWire translates a MatchingService_AddDecisionTask_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_AddDecisionTask_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.AddRequest != nil { w, err = v.AddRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AddDecisionTaskRequest_Read(w wire.Value) (*AddDecisionTaskRequest, error) { var v AddDecisionTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_AddDecisionTask_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_AddDecisionTask_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_AddDecisionTask_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_AddDecisionTask_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.AddRequest, err = _AddDecisionTaskRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MatchingService_AddDecisionTask_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_AddDecisionTask_Args struct could not be encoded. func (v *MatchingService_AddDecisionTask_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.AddRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.AddRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _AddDecisionTaskRequest_Decode(sr stream.Reader) (*AddDecisionTaskRequest, error) { var v AddDecisionTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_AddDecisionTask_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_AddDecisionTask_Args struct could not be generated from the wire // representation. func (v *MatchingService_AddDecisionTask_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.AddRequest, err = _AddDecisionTaskRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MatchingService_AddDecisionTask_Args // struct. func (v *MatchingService_AddDecisionTask_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.AddRequest != nil { fields[i] = fmt.Sprintf("AddRequest: %v", v.AddRequest) i++ } return fmt.Sprintf("MatchingService_AddDecisionTask_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_AddDecisionTask_Args match the // provided MatchingService_AddDecisionTask_Args. // // This function performs a deep comparison. func (v *MatchingService_AddDecisionTask_Args) Equals(rhs *MatchingService_AddDecisionTask_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.AddRequest == nil && rhs.AddRequest == nil) || (v.AddRequest != nil && rhs.AddRequest != nil && v.AddRequest.Equals(rhs.AddRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_AddDecisionTask_Args. func (v *MatchingService_AddDecisionTask_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.AddRequest != nil { err = multierr.Append(err, enc.AddObject("addRequest", v.AddRequest)) } return err } // GetAddRequest returns the value of AddRequest if it is set or its // zero value if it is unset. func (v *MatchingService_AddDecisionTask_Args) GetAddRequest() (o *AddDecisionTaskRequest) { if v != nil && v.AddRequest != nil { return v.AddRequest } return } // IsSetAddRequest returns true if AddRequest is not nil. func (v *MatchingService_AddDecisionTask_Args) IsSetAddRequest() bool { return v != nil && v.AddRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "AddDecisionTask" for this struct. func (v *MatchingService_AddDecisionTask_Args) MethodName() string { return "AddDecisionTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *MatchingService_AddDecisionTask_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // MatchingService_AddDecisionTask_Helper provides functions that aid in handling the // parameters and return values of the MatchingService.AddDecisionTask // function. var MatchingService_AddDecisionTask_Helper = struct { // Args accepts the parameters of AddDecisionTask in-order and returns // the arguments struct for the function. Args func( addRequest *AddDecisionTaskRequest, ) *MatchingService_AddDecisionTask_Args // IsException returns true if the given error can be thrown // by AddDecisionTask. // // An error can be thrown by AddDecisionTask only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for AddDecisionTask // given the error returned by it. The provided error may // be nil if AddDecisionTask did not fail. // // This allows mapping errors returned by AddDecisionTask into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // AddDecisionTask // // err := AddDecisionTask(args) // result, err := MatchingService_AddDecisionTask_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from AddDecisionTask: %v", err) // } // serialize(result) WrapResponse func(error) (*MatchingService_AddDecisionTask_Result, error) // UnwrapResponse takes the result struct for AddDecisionTask // and returns the erorr returned by it (if any). // // The error is non-nil only if AddDecisionTask threw an // exception. // // result := deserialize(bytes) // err := MatchingService_AddDecisionTask_Helper.UnwrapResponse(result) UnwrapResponse func(*MatchingService_AddDecisionTask_Result) error }{} func init() { MatchingService_AddDecisionTask_Helper.Args = func( addRequest *AddDecisionTaskRequest, ) *MatchingService_AddDecisionTask_Args { return &MatchingService_AddDecisionTask_Args{ AddRequest: addRequest, } } MatchingService_AddDecisionTask_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.LimitExceededError: return true case *shared.DomainNotActiveError: return true case *shared.RemoteSyncMatchedError: return true case *shared.StickyWorkerUnavailableError: return true case *shared.TaskListNotOwnedByHostError: return true default: return false } } MatchingService_AddDecisionTask_Helper.WrapResponse = func(err error) (*MatchingService_AddDecisionTask_Result, error) { if err == nil { return &MatchingService_AddDecisionTask_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddDecisionTask_Result.BadRequestError") } return &MatchingService_AddDecisionTask_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddDecisionTask_Result.InternalServiceError") } return &MatchingService_AddDecisionTask_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddDecisionTask_Result.ServiceBusyError") } return &MatchingService_AddDecisionTask_Result{ServiceBusyError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddDecisionTask_Result.LimitExceededError") } return &MatchingService_AddDecisionTask_Result{LimitExceededError: e}, nil case *shared.DomainNotActiveError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddDecisionTask_Result.DomainNotActiveError") } return &MatchingService_AddDecisionTask_Result{DomainNotActiveError: e}, nil case *shared.RemoteSyncMatchedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddDecisionTask_Result.RemoteSyncMatchedError") } return &MatchingService_AddDecisionTask_Result{RemoteSyncMatchedError: e}, nil case *shared.StickyWorkerUnavailableError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddDecisionTask_Result.StickyWorkerUnavailableError") } return &MatchingService_AddDecisionTask_Result{StickyWorkerUnavailableError: e}, nil case *shared.TaskListNotOwnedByHostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_AddDecisionTask_Result.TaskListNotOwnedByHostError") } return &MatchingService_AddDecisionTask_Result{TaskListNotOwnedByHostError: e}, nil } return nil, err } MatchingService_AddDecisionTask_Helper.UnwrapResponse = func(result *MatchingService_AddDecisionTask_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.DomainNotActiveError != nil { err = result.DomainNotActiveError return } if result.RemoteSyncMatchedError != nil { err = result.RemoteSyncMatchedError return } if result.StickyWorkerUnavailableError != nil { err = result.StickyWorkerUnavailableError return } if result.TaskListNotOwnedByHostError != nil { err = result.TaskListNotOwnedByHostError return } return } } // MatchingService_AddDecisionTask_Result represents the result of a MatchingService.AddDecisionTask function call. // // The result of a AddDecisionTask execution is sent and received over the wire as this struct. type MatchingService_AddDecisionTask_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` DomainNotActiveError *shared.DomainNotActiveError `json:"domainNotActiveError,omitempty"` RemoteSyncMatchedError *shared.RemoteSyncMatchedError `json:"remoteSyncMatchedError,omitempty"` StickyWorkerUnavailableError *shared.StickyWorkerUnavailableError `json:"stickyWorkerUnavailableError,omitempty"` TaskListNotOwnedByHostError *shared.TaskListNotOwnedByHostError `json:"taskListNotOwnedByHostError,omitempty"` } // ToWire translates a MatchingService_AddDecisionTask_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_AddDecisionTask_Result) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.DomainNotActiveError != nil { w, err = v.DomainNotActiveError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.RemoteSyncMatchedError != nil { w, err = v.RemoteSyncMatchedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.StickyWorkerUnavailableError != nil { w, err = v.StickyWorkerUnavailableError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.TaskListNotOwnedByHostError != nil { w, err = v.TaskListNotOwnedByHostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("MatchingService_AddDecisionTask_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StickyWorkerUnavailableError_Read(w wire.Value) (*shared.StickyWorkerUnavailableError, error) { var v shared.StickyWorkerUnavailableError err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_AddDecisionTask_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_AddDecisionTask_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_AddDecisionTask_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_AddDecisionTask_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.DomainNotActiveError, err = _DomainNotActiveError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.RemoteSyncMatchedError, err = _RemoteSyncMatchedError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.StickyWorkerUnavailableError, err = _StickyWorkerUnavailableError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.TaskListNotOwnedByHostError, err = _TaskListNotOwnedByHostError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.RemoteSyncMatchedError != nil { count++ } if v.StickyWorkerUnavailableError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_AddDecisionTask_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a MatchingService_AddDecisionTask_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_AddDecisionTask_Result struct could not be encoded. func (v *MatchingService_AddDecisionTask_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotActiveError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.DomainNotActiveError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RemoteSyncMatchedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.RemoteSyncMatchedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyWorkerUnavailableError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.StickyWorkerUnavailableError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListNotOwnedByHostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.TaskListNotOwnedByHostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.RemoteSyncMatchedError != nil { count++ } if v.StickyWorkerUnavailableError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_AddDecisionTask_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } func _StickyWorkerUnavailableError_Decode(sr stream.Reader) (*shared.StickyWorkerUnavailableError, error) { var v shared.StickyWorkerUnavailableError err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_AddDecisionTask_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_AddDecisionTask_Result struct could not be generated from the wire // representation. func (v *MatchingService_AddDecisionTask_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.DomainNotActiveError, err = _DomainNotActiveError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.RemoteSyncMatchedError, err = _RemoteSyncMatchedError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.StickyWorkerUnavailableError, err = _StickyWorkerUnavailableError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.TaskListNotOwnedByHostError, err = _TaskListNotOwnedByHostError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.DomainNotActiveError != nil { count++ } if v.RemoteSyncMatchedError != nil { count++ } if v.StickyWorkerUnavailableError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_AddDecisionTask_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a MatchingService_AddDecisionTask_Result // struct. func (v *MatchingService_AddDecisionTask_Result) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.DomainNotActiveError != nil { fields[i] = fmt.Sprintf("DomainNotActiveError: %v", v.DomainNotActiveError) i++ } if v.RemoteSyncMatchedError != nil { fields[i] = fmt.Sprintf("RemoteSyncMatchedError: %v", v.RemoteSyncMatchedError) i++ } if v.StickyWorkerUnavailableError != nil { fields[i] = fmt.Sprintf("StickyWorkerUnavailableError: %v", v.StickyWorkerUnavailableError) i++ } if v.TaskListNotOwnedByHostError != nil { fields[i] = fmt.Sprintf("TaskListNotOwnedByHostError: %v", v.TaskListNotOwnedByHostError) i++ } return fmt.Sprintf("MatchingService_AddDecisionTask_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_AddDecisionTask_Result match the // provided MatchingService_AddDecisionTask_Result. // // This function performs a deep comparison. func (v *MatchingService_AddDecisionTask_Result) Equals(rhs *MatchingService_AddDecisionTask_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.DomainNotActiveError == nil && rhs.DomainNotActiveError == nil) || (v.DomainNotActiveError != nil && rhs.DomainNotActiveError != nil && v.DomainNotActiveError.Equals(rhs.DomainNotActiveError))) { return false } if !((v.RemoteSyncMatchedError == nil && rhs.RemoteSyncMatchedError == nil) || (v.RemoteSyncMatchedError != nil && rhs.RemoteSyncMatchedError != nil && v.RemoteSyncMatchedError.Equals(rhs.RemoteSyncMatchedError))) { return false } if !((v.StickyWorkerUnavailableError == nil && rhs.StickyWorkerUnavailableError == nil) || (v.StickyWorkerUnavailableError != nil && rhs.StickyWorkerUnavailableError != nil && v.StickyWorkerUnavailableError.Equals(rhs.StickyWorkerUnavailableError))) { return false } if !((v.TaskListNotOwnedByHostError == nil && rhs.TaskListNotOwnedByHostError == nil) || (v.TaskListNotOwnedByHostError != nil && rhs.TaskListNotOwnedByHostError != nil && v.TaskListNotOwnedByHostError.Equals(rhs.TaskListNotOwnedByHostError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_AddDecisionTask_Result. func (v *MatchingService_AddDecisionTask_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.DomainNotActiveError != nil { err = multierr.Append(err, enc.AddObject("domainNotActiveError", v.DomainNotActiveError)) } if v.RemoteSyncMatchedError != nil { err = multierr.Append(err, enc.AddObject("remoteSyncMatchedError", v.RemoteSyncMatchedError)) } if v.StickyWorkerUnavailableError != nil { err = multierr.Append(err, enc.AddObject("stickyWorkerUnavailableError", v.StickyWorkerUnavailableError)) } if v.TaskListNotOwnedByHostError != nil { err = multierr.Append(err, enc.AddObject("taskListNotOwnedByHostError", v.TaskListNotOwnedByHostError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *MatchingService_AddDecisionTask_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *MatchingService_AddDecisionTask_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *MatchingService_AddDecisionTask_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *MatchingService_AddDecisionTask_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *MatchingService_AddDecisionTask_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *MatchingService_AddDecisionTask_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *MatchingService_AddDecisionTask_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *MatchingService_AddDecisionTask_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetDomainNotActiveError returns the value of DomainNotActiveError if it is set or its // zero value if it is unset. func (v *MatchingService_AddDecisionTask_Result) GetDomainNotActiveError() (o *shared.DomainNotActiveError) { if v != nil && v.DomainNotActiveError != nil { return v.DomainNotActiveError } return } // IsSetDomainNotActiveError returns true if DomainNotActiveError is not nil. func (v *MatchingService_AddDecisionTask_Result) IsSetDomainNotActiveError() bool { return v != nil && v.DomainNotActiveError != nil } // GetRemoteSyncMatchedError returns the value of RemoteSyncMatchedError if it is set or its // zero value if it is unset. func (v *MatchingService_AddDecisionTask_Result) GetRemoteSyncMatchedError() (o *shared.RemoteSyncMatchedError) { if v != nil && v.RemoteSyncMatchedError != nil { return v.RemoteSyncMatchedError } return } // IsSetRemoteSyncMatchedError returns true if RemoteSyncMatchedError is not nil. func (v *MatchingService_AddDecisionTask_Result) IsSetRemoteSyncMatchedError() bool { return v != nil && v.RemoteSyncMatchedError != nil } // GetStickyWorkerUnavailableError returns the value of StickyWorkerUnavailableError if it is set or its // zero value if it is unset. func (v *MatchingService_AddDecisionTask_Result) GetStickyWorkerUnavailableError() (o *shared.StickyWorkerUnavailableError) { if v != nil && v.StickyWorkerUnavailableError != nil { return v.StickyWorkerUnavailableError } return } // IsSetStickyWorkerUnavailableError returns true if StickyWorkerUnavailableError is not nil. func (v *MatchingService_AddDecisionTask_Result) IsSetStickyWorkerUnavailableError() bool { return v != nil && v.StickyWorkerUnavailableError != nil } // GetTaskListNotOwnedByHostError returns the value of TaskListNotOwnedByHostError if it is set or its // zero value if it is unset. func (v *MatchingService_AddDecisionTask_Result) GetTaskListNotOwnedByHostError() (o *shared.TaskListNotOwnedByHostError) { if v != nil && v.TaskListNotOwnedByHostError != nil { return v.TaskListNotOwnedByHostError } return } // IsSetTaskListNotOwnedByHostError returns true if TaskListNotOwnedByHostError is not nil. func (v *MatchingService_AddDecisionTask_Result) IsSetTaskListNotOwnedByHostError() bool { return v != nil && v.TaskListNotOwnedByHostError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "AddDecisionTask" for this struct. func (v *MatchingService_AddDecisionTask_Result) MethodName() string { return "AddDecisionTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *MatchingService_AddDecisionTask_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // MatchingService_CancelOutstandingPoll_Args represents the arguments for the MatchingService.CancelOutstandingPoll function. // // The arguments for CancelOutstandingPoll are sent and received over the wire as this struct. type MatchingService_CancelOutstandingPoll_Args struct { Request *CancelOutstandingPollRequest `json:"request,omitempty"` } // ToWire translates a MatchingService_CancelOutstandingPoll_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_CancelOutstandingPoll_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CancelOutstandingPollRequest_Read(w wire.Value) (*CancelOutstandingPollRequest, error) { var v CancelOutstandingPollRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_CancelOutstandingPoll_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_CancelOutstandingPoll_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_CancelOutstandingPoll_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_CancelOutstandingPoll_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _CancelOutstandingPollRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MatchingService_CancelOutstandingPoll_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_CancelOutstandingPoll_Args struct could not be encoded. func (v *MatchingService_CancelOutstandingPoll_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CancelOutstandingPollRequest_Decode(sr stream.Reader) (*CancelOutstandingPollRequest, error) { var v CancelOutstandingPollRequest err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_CancelOutstandingPoll_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_CancelOutstandingPoll_Args struct could not be generated from the wire // representation. func (v *MatchingService_CancelOutstandingPoll_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _CancelOutstandingPollRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MatchingService_CancelOutstandingPoll_Args // struct. func (v *MatchingService_CancelOutstandingPoll_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("MatchingService_CancelOutstandingPoll_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_CancelOutstandingPoll_Args match the // provided MatchingService_CancelOutstandingPoll_Args. // // This function performs a deep comparison. func (v *MatchingService_CancelOutstandingPoll_Args) Equals(rhs *MatchingService_CancelOutstandingPoll_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_CancelOutstandingPoll_Args. func (v *MatchingService_CancelOutstandingPoll_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *MatchingService_CancelOutstandingPoll_Args) GetRequest() (o *CancelOutstandingPollRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *MatchingService_CancelOutstandingPoll_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "CancelOutstandingPoll" for this struct. func (v *MatchingService_CancelOutstandingPoll_Args) MethodName() string { return "CancelOutstandingPoll" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *MatchingService_CancelOutstandingPoll_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // MatchingService_CancelOutstandingPoll_Helper provides functions that aid in handling the // parameters and return values of the MatchingService.CancelOutstandingPoll // function. var MatchingService_CancelOutstandingPoll_Helper = struct { // Args accepts the parameters of CancelOutstandingPoll in-order and returns // the arguments struct for the function. Args func( request *CancelOutstandingPollRequest, ) *MatchingService_CancelOutstandingPoll_Args // IsException returns true if the given error can be thrown // by CancelOutstandingPoll. // // An error can be thrown by CancelOutstandingPoll only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for CancelOutstandingPoll // given the error returned by it. The provided error may // be nil if CancelOutstandingPoll did not fail. // // This allows mapping errors returned by CancelOutstandingPoll into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // CancelOutstandingPoll // // err := CancelOutstandingPoll(args) // result, err := MatchingService_CancelOutstandingPoll_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from CancelOutstandingPoll: %v", err) // } // serialize(result) WrapResponse func(error) (*MatchingService_CancelOutstandingPoll_Result, error) // UnwrapResponse takes the result struct for CancelOutstandingPoll // and returns the erorr returned by it (if any). // // The error is non-nil only if CancelOutstandingPoll threw an // exception. // // result := deserialize(bytes) // err := MatchingService_CancelOutstandingPoll_Helper.UnwrapResponse(result) UnwrapResponse func(*MatchingService_CancelOutstandingPoll_Result) error }{} func init() { MatchingService_CancelOutstandingPoll_Helper.Args = func( request *CancelOutstandingPollRequest, ) *MatchingService_CancelOutstandingPoll_Args { return &MatchingService_CancelOutstandingPoll_Args{ Request: request, } } MatchingService_CancelOutstandingPoll_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true case *shared.TaskListNotOwnedByHostError: return true default: return false } } MatchingService_CancelOutstandingPoll_Helper.WrapResponse = func(err error) (*MatchingService_CancelOutstandingPoll_Result, error) { if err == nil { return &MatchingService_CancelOutstandingPoll_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_CancelOutstandingPoll_Result.BadRequestError") } return &MatchingService_CancelOutstandingPoll_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_CancelOutstandingPoll_Result.InternalServiceError") } return &MatchingService_CancelOutstandingPoll_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_CancelOutstandingPoll_Result.ServiceBusyError") } return &MatchingService_CancelOutstandingPoll_Result{ServiceBusyError: e}, nil case *shared.TaskListNotOwnedByHostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_CancelOutstandingPoll_Result.TaskListNotOwnedByHostError") } return &MatchingService_CancelOutstandingPoll_Result{TaskListNotOwnedByHostError: e}, nil } return nil, err } MatchingService_CancelOutstandingPoll_Helper.UnwrapResponse = func(result *MatchingService_CancelOutstandingPoll_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.TaskListNotOwnedByHostError != nil { err = result.TaskListNotOwnedByHostError return } return } } // MatchingService_CancelOutstandingPoll_Result represents the result of a MatchingService.CancelOutstandingPoll function call. // // The result of a CancelOutstandingPoll execution is sent and received over the wire as this struct. type MatchingService_CancelOutstandingPoll_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` TaskListNotOwnedByHostError *shared.TaskListNotOwnedByHostError `json:"taskListNotOwnedByHostError,omitempty"` } // ToWire translates a MatchingService_CancelOutstandingPoll_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_CancelOutstandingPoll_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.TaskListNotOwnedByHostError != nil { w, err = v.TaskListNotOwnedByHostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("MatchingService_CancelOutstandingPoll_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a MatchingService_CancelOutstandingPoll_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_CancelOutstandingPoll_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_CancelOutstandingPoll_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_CancelOutstandingPoll_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.TaskListNotOwnedByHostError, err = _TaskListNotOwnedByHostError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_CancelOutstandingPoll_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a MatchingService_CancelOutstandingPoll_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_CancelOutstandingPoll_Result struct could not be encoded. func (v *MatchingService_CancelOutstandingPoll_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListNotOwnedByHostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.TaskListNotOwnedByHostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_CancelOutstandingPoll_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a MatchingService_CancelOutstandingPoll_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_CancelOutstandingPoll_Result struct could not be generated from the wire // representation. func (v *MatchingService_CancelOutstandingPoll_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.TaskListNotOwnedByHostError, err = _TaskListNotOwnedByHostError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_CancelOutstandingPoll_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a MatchingService_CancelOutstandingPoll_Result // struct. func (v *MatchingService_CancelOutstandingPoll_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.TaskListNotOwnedByHostError != nil { fields[i] = fmt.Sprintf("TaskListNotOwnedByHostError: %v", v.TaskListNotOwnedByHostError) i++ } return fmt.Sprintf("MatchingService_CancelOutstandingPoll_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_CancelOutstandingPoll_Result match the // provided MatchingService_CancelOutstandingPoll_Result. // // This function performs a deep comparison. func (v *MatchingService_CancelOutstandingPoll_Result) Equals(rhs *MatchingService_CancelOutstandingPoll_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.TaskListNotOwnedByHostError == nil && rhs.TaskListNotOwnedByHostError == nil) || (v.TaskListNotOwnedByHostError != nil && rhs.TaskListNotOwnedByHostError != nil && v.TaskListNotOwnedByHostError.Equals(rhs.TaskListNotOwnedByHostError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_CancelOutstandingPoll_Result. func (v *MatchingService_CancelOutstandingPoll_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.TaskListNotOwnedByHostError != nil { err = multierr.Append(err, enc.AddObject("taskListNotOwnedByHostError", v.TaskListNotOwnedByHostError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *MatchingService_CancelOutstandingPoll_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *MatchingService_CancelOutstandingPoll_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *MatchingService_CancelOutstandingPoll_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *MatchingService_CancelOutstandingPoll_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *MatchingService_CancelOutstandingPoll_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *MatchingService_CancelOutstandingPoll_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetTaskListNotOwnedByHostError returns the value of TaskListNotOwnedByHostError if it is set or its // zero value if it is unset. func (v *MatchingService_CancelOutstandingPoll_Result) GetTaskListNotOwnedByHostError() (o *shared.TaskListNotOwnedByHostError) { if v != nil && v.TaskListNotOwnedByHostError != nil { return v.TaskListNotOwnedByHostError } return } // IsSetTaskListNotOwnedByHostError returns true if TaskListNotOwnedByHostError is not nil. func (v *MatchingService_CancelOutstandingPoll_Result) IsSetTaskListNotOwnedByHostError() bool { return v != nil && v.TaskListNotOwnedByHostError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "CancelOutstandingPoll" for this struct. func (v *MatchingService_CancelOutstandingPoll_Result) MethodName() string { return "CancelOutstandingPoll" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *MatchingService_CancelOutstandingPoll_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // MatchingService_DescribeTaskList_Args represents the arguments for the MatchingService.DescribeTaskList function. // // The arguments for DescribeTaskList are sent and received over the wire as this struct. type MatchingService_DescribeTaskList_Args struct { Request *DescribeTaskListRequest `json:"request,omitempty"` } // ToWire translates a MatchingService_DescribeTaskList_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_DescribeTaskList_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeTaskListRequest_1_Read(w wire.Value) (*DescribeTaskListRequest, error) { var v DescribeTaskListRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_DescribeTaskList_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_DescribeTaskList_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_DescribeTaskList_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_DescribeTaskList_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _DescribeTaskListRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MatchingService_DescribeTaskList_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_DescribeTaskList_Args struct could not be encoded. func (v *MatchingService_DescribeTaskList_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeTaskListRequest_1_Decode(sr stream.Reader) (*DescribeTaskListRequest, error) { var v DescribeTaskListRequest err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_DescribeTaskList_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_DescribeTaskList_Args struct could not be generated from the wire // representation. func (v *MatchingService_DescribeTaskList_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _DescribeTaskListRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MatchingService_DescribeTaskList_Args // struct. func (v *MatchingService_DescribeTaskList_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("MatchingService_DescribeTaskList_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_DescribeTaskList_Args match the // provided MatchingService_DescribeTaskList_Args. // // This function performs a deep comparison. func (v *MatchingService_DescribeTaskList_Args) Equals(rhs *MatchingService_DescribeTaskList_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_DescribeTaskList_Args. func (v *MatchingService_DescribeTaskList_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *MatchingService_DescribeTaskList_Args) GetRequest() (o *DescribeTaskListRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *MatchingService_DescribeTaskList_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "DescribeTaskList" for this struct. func (v *MatchingService_DescribeTaskList_Args) MethodName() string { return "DescribeTaskList" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *MatchingService_DescribeTaskList_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // MatchingService_DescribeTaskList_Helper provides functions that aid in handling the // parameters and return values of the MatchingService.DescribeTaskList // function. var MatchingService_DescribeTaskList_Helper = struct { // Args accepts the parameters of DescribeTaskList in-order and returns // the arguments struct for the function. Args func( request *DescribeTaskListRequest, ) *MatchingService_DescribeTaskList_Args // IsException returns true if the given error can be thrown // by DescribeTaskList. // // An error can be thrown by DescribeTaskList only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for DescribeTaskList // given its return value and error. // // This allows mapping values and errors returned by // DescribeTaskList into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by DescribeTaskList // // value, err := DescribeTaskList(args) // result, err := MatchingService_DescribeTaskList_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from DescribeTaskList: %v", err) // } // serialize(result) WrapResponse func(*shared.DescribeTaskListResponse, error) (*MatchingService_DescribeTaskList_Result, error) // UnwrapResponse takes the result struct for DescribeTaskList // and returns the value or error returned by it. // // The error is non-nil only if DescribeTaskList threw an // exception. // // result := deserialize(bytes) // value, err := MatchingService_DescribeTaskList_Helper.UnwrapResponse(result) UnwrapResponse func(*MatchingService_DescribeTaskList_Result) (*shared.DescribeTaskListResponse, error) }{} func init() { MatchingService_DescribeTaskList_Helper.Args = func( request *DescribeTaskListRequest, ) *MatchingService_DescribeTaskList_Args { return &MatchingService_DescribeTaskList_Args{ Request: request, } } MatchingService_DescribeTaskList_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true case *shared.TaskListNotOwnedByHostError: return true default: return false } } MatchingService_DescribeTaskList_Helper.WrapResponse = func(success *shared.DescribeTaskListResponse, err error) (*MatchingService_DescribeTaskList_Result, error) { if err == nil { return &MatchingService_DescribeTaskList_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_DescribeTaskList_Result.BadRequestError") } return &MatchingService_DescribeTaskList_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_DescribeTaskList_Result.InternalServiceError") } return &MatchingService_DescribeTaskList_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_DescribeTaskList_Result.EntityNotExistError") } return &MatchingService_DescribeTaskList_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_DescribeTaskList_Result.ServiceBusyError") } return &MatchingService_DescribeTaskList_Result{ServiceBusyError: e}, nil case *shared.TaskListNotOwnedByHostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_DescribeTaskList_Result.TaskListNotOwnedByHostError") } return &MatchingService_DescribeTaskList_Result{TaskListNotOwnedByHostError: e}, nil } return nil, err } MatchingService_DescribeTaskList_Helper.UnwrapResponse = func(result *MatchingService_DescribeTaskList_Result) (success *shared.DescribeTaskListResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.TaskListNotOwnedByHostError != nil { err = result.TaskListNotOwnedByHostError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // MatchingService_DescribeTaskList_Result represents the result of a MatchingService.DescribeTaskList function call. // // The result of a DescribeTaskList execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type MatchingService_DescribeTaskList_Result struct { // Value returned by DescribeTaskList after a successful execution. Success *shared.DescribeTaskListResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` TaskListNotOwnedByHostError *shared.TaskListNotOwnedByHostError `json:"taskListNotOwnedByHostError,omitempty"` } // ToWire translates a MatchingService_DescribeTaskList_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_DescribeTaskList_Result) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.TaskListNotOwnedByHostError != nil { w, err = v.TaskListNotOwnedByHostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("MatchingService_DescribeTaskList_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeTaskListResponse_Read(w wire.Value) (*shared.DescribeTaskListResponse, error) { var v shared.DescribeTaskListResponse err := v.FromWire(w) return &v, err } func _EntityNotExistsError_Read(w wire.Value) (*shared.EntityNotExistsError, error) { var v shared.EntityNotExistsError err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_DescribeTaskList_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_DescribeTaskList_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_DescribeTaskList_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_DescribeTaskList_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _DescribeTaskListResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.TaskListNotOwnedByHostError, err = _TaskListNotOwnedByHostError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_DescribeTaskList_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a MatchingService_DescribeTaskList_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_DescribeTaskList_Result struct could not be encoded. func (v *MatchingService_DescribeTaskList_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListNotOwnedByHostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.TaskListNotOwnedByHostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_DescribeTaskList_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _DescribeTaskListResponse_Decode(sr stream.Reader) (*shared.DescribeTaskListResponse, error) { var v shared.DescribeTaskListResponse err := v.Decode(sr) return &v, err } func _EntityNotExistsError_Decode(sr stream.Reader) (*shared.EntityNotExistsError, error) { var v shared.EntityNotExistsError err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_DescribeTaskList_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_DescribeTaskList_Result struct could not be generated from the wire // representation. func (v *MatchingService_DescribeTaskList_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _DescribeTaskListResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.TaskListNotOwnedByHostError, err = _TaskListNotOwnedByHostError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_DescribeTaskList_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a MatchingService_DescribeTaskList_Result // struct. func (v *MatchingService_DescribeTaskList_Result) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.TaskListNotOwnedByHostError != nil { fields[i] = fmt.Sprintf("TaskListNotOwnedByHostError: %v", v.TaskListNotOwnedByHostError) i++ } return fmt.Sprintf("MatchingService_DescribeTaskList_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_DescribeTaskList_Result match the // provided MatchingService_DescribeTaskList_Result. // // This function performs a deep comparison. func (v *MatchingService_DescribeTaskList_Result) Equals(rhs *MatchingService_DescribeTaskList_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.TaskListNotOwnedByHostError == nil && rhs.TaskListNotOwnedByHostError == nil) || (v.TaskListNotOwnedByHostError != nil && rhs.TaskListNotOwnedByHostError != nil && v.TaskListNotOwnedByHostError.Equals(rhs.TaskListNotOwnedByHostError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_DescribeTaskList_Result. func (v *MatchingService_DescribeTaskList_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.TaskListNotOwnedByHostError != nil { err = multierr.Append(err, enc.AddObject("taskListNotOwnedByHostError", v.TaskListNotOwnedByHostError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *MatchingService_DescribeTaskList_Result) GetSuccess() (o *shared.DescribeTaskListResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *MatchingService_DescribeTaskList_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *MatchingService_DescribeTaskList_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *MatchingService_DescribeTaskList_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *MatchingService_DescribeTaskList_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *MatchingService_DescribeTaskList_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *MatchingService_DescribeTaskList_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *MatchingService_DescribeTaskList_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *MatchingService_DescribeTaskList_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *MatchingService_DescribeTaskList_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetTaskListNotOwnedByHostError returns the value of TaskListNotOwnedByHostError if it is set or its // zero value if it is unset. func (v *MatchingService_DescribeTaskList_Result) GetTaskListNotOwnedByHostError() (o *shared.TaskListNotOwnedByHostError) { if v != nil && v.TaskListNotOwnedByHostError != nil { return v.TaskListNotOwnedByHostError } return } // IsSetTaskListNotOwnedByHostError returns true if TaskListNotOwnedByHostError is not nil. func (v *MatchingService_DescribeTaskList_Result) IsSetTaskListNotOwnedByHostError() bool { return v != nil && v.TaskListNotOwnedByHostError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "DescribeTaskList" for this struct. func (v *MatchingService_DescribeTaskList_Result) MethodName() string { return "DescribeTaskList" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *MatchingService_DescribeTaskList_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // MatchingService_GetTaskListsByDomain_Args represents the arguments for the MatchingService.GetTaskListsByDomain function. // // The arguments for GetTaskListsByDomain are sent and received over the wire as this struct. type MatchingService_GetTaskListsByDomain_Args struct { Request *shared.GetTaskListsByDomainRequest `json:"request,omitempty"` } // ToWire translates a MatchingService_GetTaskListsByDomain_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_GetTaskListsByDomain_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetTaskListsByDomainRequest_Read(w wire.Value) (*shared.GetTaskListsByDomainRequest, error) { var v shared.GetTaskListsByDomainRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_GetTaskListsByDomain_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_GetTaskListsByDomain_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_GetTaskListsByDomain_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_GetTaskListsByDomain_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _GetTaskListsByDomainRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MatchingService_GetTaskListsByDomain_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_GetTaskListsByDomain_Args struct could not be encoded. func (v *MatchingService_GetTaskListsByDomain_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _GetTaskListsByDomainRequest_Decode(sr stream.Reader) (*shared.GetTaskListsByDomainRequest, error) { var v shared.GetTaskListsByDomainRequest err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_GetTaskListsByDomain_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_GetTaskListsByDomain_Args struct could not be generated from the wire // representation. func (v *MatchingService_GetTaskListsByDomain_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _GetTaskListsByDomainRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MatchingService_GetTaskListsByDomain_Args // struct. func (v *MatchingService_GetTaskListsByDomain_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("MatchingService_GetTaskListsByDomain_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_GetTaskListsByDomain_Args match the // provided MatchingService_GetTaskListsByDomain_Args. // // This function performs a deep comparison. func (v *MatchingService_GetTaskListsByDomain_Args) Equals(rhs *MatchingService_GetTaskListsByDomain_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_GetTaskListsByDomain_Args. func (v *MatchingService_GetTaskListsByDomain_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *MatchingService_GetTaskListsByDomain_Args) GetRequest() (o *shared.GetTaskListsByDomainRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *MatchingService_GetTaskListsByDomain_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "GetTaskListsByDomain" for this struct. func (v *MatchingService_GetTaskListsByDomain_Args) MethodName() string { return "GetTaskListsByDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *MatchingService_GetTaskListsByDomain_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // MatchingService_GetTaskListsByDomain_Helper provides functions that aid in handling the // parameters and return values of the MatchingService.GetTaskListsByDomain // function. var MatchingService_GetTaskListsByDomain_Helper = struct { // Args accepts the parameters of GetTaskListsByDomain in-order and returns // the arguments struct for the function. Args func( request *shared.GetTaskListsByDomainRequest, ) *MatchingService_GetTaskListsByDomain_Args // IsException returns true if the given error can be thrown // by GetTaskListsByDomain. // // An error can be thrown by GetTaskListsByDomain only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for GetTaskListsByDomain // given its return value and error. // // This allows mapping values and errors returned by // GetTaskListsByDomain into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by GetTaskListsByDomain // // value, err := GetTaskListsByDomain(args) // result, err := MatchingService_GetTaskListsByDomain_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from GetTaskListsByDomain: %v", err) // } // serialize(result) WrapResponse func(*shared.GetTaskListsByDomainResponse, error) (*MatchingService_GetTaskListsByDomain_Result, error) // UnwrapResponse takes the result struct for GetTaskListsByDomain // and returns the value or error returned by it. // // The error is non-nil only if GetTaskListsByDomain threw an // exception. // // result := deserialize(bytes) // value, err := MatchingService_GetTaskListsByDomain_Helper.UnwrapResponse(result) UnwrapResponse func(*MatchingService_GetTaskListsByDomain_Result) (*shared.GetTaskListsByDomainResponse, error) }{} func init() { MatchingService_GetTaskListsByDomain_Helper.Args = func( request *shared.GetTaskListsByDomainRequest, ) *MatchingService_GetTaskListsByDomain_Args { return &MatchingService_GetTaskListsByDomain_Args{ Request: request, } } MatchingService_GetTaskListsByDomain_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *shared.ServiceBusyError: return true default: return false } } MatchingService_GetTaskListsByDomain_Helper.WrapResponse = func(success *shared.GetTaskListsByDomainResponse, err error) (*MatchingService_GetTaskListsByDomain_Result, error) { if err == nil { return &MatchingService_GetTaskListsByDomain_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_GetTaskListsByDomain_Result.BadRequestError") } return &MatchingService_GetTaskListsByDomain_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_GetTaskListsByDomain_Result.InternalServiceError") } return &MatchingService_GetTaskListsByDomain_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_GetTaskListsByDomain_Result.EntityNotExistError") } return &MatchingService_GetTaskListsByDomain_Result{EntityNotExistError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_GetTaskListsByDomain_Result.ServiceBusyError") } return &MatchingService_GetTaskListsByDomain_Result{ServiceBusyError: e}, nil } return nil, err } MatchingService_GetTaskListsByDomain_Helper.UnwrapResponse = func(result *MatchingService_GetTaskListsByDomain_Result) (success *shared.GetTaskListsByDomainResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // MatchingService_GetTaskListsByDomain_Result represents the result of a MatchingService.GetTaskListsByDomain function call. // // The result of a GetTaskListsByDomain execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type MatchingService_GetTaskListsByDomain_Result struct { // Value returned by GetTaskListsByDomain after a successful execution. Success *shared.GetTaskListsByDomainResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a MatchingService_GetTaskListsByDomain_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_GetTaskListsByDomain_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("MatchingService_GetTaskListsByDomain_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _GetTaskListsByDomainResponse_Read(w wire.Value) (*shared.GetTaskListsByDomainResponse, error) { var v shared.GetTaskListsByDomainResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_GetTaskListsByDomain_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_GetTaskListsByDomain_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_GetTaskListsByDomain_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_GetTaskListsByDomain_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _GetTaskListsByDomainResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_GetTaskListsByDomain_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a MatchingService_GetTaskListsByDomain_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_GetTaskListsByDomain_Result struct could not be encoded. func (v *MatchingService_GetTaskListsByDomain_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_GetTaskListsByDomain_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _GetTaskListsByDomainResponse_Decode(sr stream.Reader) (*shared.GetTaskListsByDomainResponse, error) { var v shared.GetTaskListsByDomainResponse err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_GetTaskListsByDomain_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_GetTaskListsByDomain_Result struct could not be generated from the wire // representation. func (v *MatchingService_GetTaskListsByDomain_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _GetTaskListsByDomainResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_GetTaskListsByDomain_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a MatchingService_GetTaskListsByDomain_Result // struct. func (v *MatchingService_GetTaskListsByDomain_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("MatchingService_GetTaskListsByDomain_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_GetTaskListsByDomain_Result match the // provided MatchingService_GetTaskListsByDomain_Result. // // This function performs a deep comparison. func (v *MatchingService_GetTaskListsByDomain_Result) Equals(rhs *MatchingService_GetTaskListsByDomain_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_GetTaskListsByDomain_Result. func (v *MatchingService_GetTaskListsByDomain_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *MatchingService_GetTaskListsByDomain_Result) GetSuccess() (o *shared.GetTaskListsByDomainResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *MatchingService_GetTaskListsByDomain_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *MatchingService_GetTaskListsByDomain_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *MatchingService_GetTaskListsByDomain_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *MatchingService_GetTaskListsByDomain_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *MatchingService_GetTaskListsByDomain_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *MatchingService_GetTaskListsByDomain_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *MatchingService_GetTaskListsByDomain_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *MatchingService_GetTaskListsByDomain_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *MatchingService_GetTaskListsByDomain_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "GetTaskListsByDomain" for this struct. func (v *MatchingService_GetTaskListsByDomain_Result) MethodName() string { return "GetTaskListsByDomain" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *MatchingService_GetTaskListsByDomain_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // MatchingService_ListTaskListPartitions_Args represents the arguments for the MatchingService.ListTaskListPartitions function. // // The arguments for ListTaskListPartitions are sent and received over the wire as this struct. type MatchingService_ListTaskListPartitions_Args struct { Request *ListTaskListPartitionsRequest `json:"request,omitempty"` } // ToWire translates a MatchingService_ListTaskListPartitions_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_ListTaskListPartitions_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListTaskListPartitionsRequest_Read(w wire.Value) (*ListTaskListPartitionsRequest, error) { var v ListTaskListPartitionsRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_ListTaskListPartitions_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_ListTaskListPartitions_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_ListTaskListPartitions_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_ListTaskListPartitions_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _ListTaskListPartitionsRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MatchingService_ListTaskListPartitions_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_ListTaskListPartitions_Args struct could not be encoded. func (v *MatchingService_ListTaskListPartitions_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ListTaskListPartitionsRequest_Decode(sr stream.Reader) (*ListTaskListPartitionsRequest, error) { var v ListTaskListPartitionsRequest err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_ListTaskListPartitions_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_ListTaskListPartitions_Args struct could not be generated from the wire // representation. func (v *MatchingService_ListTaskListPartitions_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _ListTaskListPartitionsRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MatchingService_ListTaskListPartitions_Args // struct. func (v *MatchingService_ListTaskListPartitions_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("MatchingService_ListTaskListPartitions_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_ListTaskListPartitions_Args match the // provided MatchingService_ListTaskListPartitions_Args. // // This function performs a deep comparison. func (v *MatchingService_ListTaskListPartitions_Args) Equals(rhs *MatchingService_ListTaskListPartitions_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_ListTaskListPartitions_Args. func (v *MatchingService_ListTaskListPartitions_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *MatchingService_ListTaskListPartitions_Args) GetRequest() (o *ListTaskListPartitionsRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *MatchingService_ListTaskListPartitions_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "ListTaskListPartitions" for this struct. func (v *MatchingService_ListTaskListPartitions_Args) MethodName() string { return "ListTaskListPartitions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *MatchingService_ListTaskListPartitions_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // MatchingService_ListTaskListPartitions_Helper provides functions that aid in handling the // parameters and return values of the MatchingService.ListTaskListPartitions // function. var MatchingService_ListTaskListPartitions_Helper = struct { // Args accepts the parameters of ListTaskListPartitions in-order and returns // the arguments struct for the function. Args func( request *ListTaskListPartitionsRequest, ) *MatchingService_ListTaskListPartitions_Args // IsException returns true if the given error can be thrown // by ListTaskListPartitions. // // An error can be thrown by ListTaskListPartitions only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for ListTaskListPartitions // given its return value and error. // // This allows mapping values and errors returned by // ListTaskListPartitions into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by ListTaskListPartitions // // value, err := ListTaskListPartitions(args) // result, err := MatchingService_ListTaskListPartitions_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from ListTaskListPartitions: %v", err) // } // serialize(result) WrapResponse func(*shared.ListTaskListPartitionsResponse, error) (*MatchingService_ListTaskListPartitions_Result, error) // UnwrapResponse takes the result struct for ListTaskListPartitions // and returns the value or error returned by it. // // The error is non-nil only if ListTaskListPartitions threw an // exception. // // result := deserialize(bytes) // value, err := MatchingService_ListTaskListPartitions_Helper.UnwrapResponse(result) UnwrapResponse func(*MatchingService_ListTaskListPartitions_Result) (*shared.ListTaskListPartitionsResponse, error) }{} func init() { MatchingService_ListTaskListPartitions_Helper.Args = func( request *ListTaskListPartitionsRequest, ) *MatchingService_ListTaskListPartitions_Args { return &MatchingService_ListTaskListPartitions_Args{ Request: request, } } MatchingService_ListTaskListPartitions_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.ServiceBusyError: return true default: return false } } MatchingService_ListTaskListPartitions_Helper.WrapResponse = func(success *shared.ListTaskListPartitionsResponse, err error) (*MatchingService_ListTaskListPartitions_Result, error) { if err == nil { return &MatchingService_ListTaskListPartitions_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_ListTaskListPartitions_Result.BadRequestError") } return &MatchingService_ListTaskListPartitions_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_ListTaskListPartitions_Result.InternalServiceError") } return &MatchingService_ListTaskListPartitions_Result{InternalServiceError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_ListTaskListPartitions_Result.ServiceBusyError") } return &MatchingService_ListTaskListPartitions_Result{ServiceBusyError: e}, nil } return nil, err } MatchingService_ListTaskListPartitions_Helper.UnwrapResponse = func(result *MatchingService_ListTaskListPartitions_Result) (success *shared.ListTaskListPartitionsResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // MatchingService_ListTaskListPartitions_Result represents the result of a MatchingService.ListTaskListPartitions function call. // // The result of a ListTaskListPartitions execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type MatchingService_ListTaskListPartitions_Result struct { // Value returned by ListTaskListPartitions after a successful execution. Success *shared.ListTaskListPartitionsResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a MatchingService_ListTaskListPartitions_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_ListTaskListPartitions_Result) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("MatchingService_ListTaskListPartitions_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListTaskListPartitionsResponse_Read(w wire.Value) (*shared.ListTaskListPartitionsResponse, error) { var v shared.ListTaskListPartitionsResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_ListTaskListPartitions_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_ListTaskListPartitions_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_ListTaskListPartitions_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_ListTaskListPartitions_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _ListTaskListPartitionsResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_ListTaskListPartitions_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a MatchingService_ListTaskListPartitions_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_ListTaskListPartitions_Result struct could not be encoded. func (v *MatchingService_ListTaskListPartitions_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_ListTaskListPartitions_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _ListTaskListPartitionsResponse_Decode(sr stream.Reader) (*shared.ListTaskListPartitionsResponse, error) { var v shared.ListTaskListPartitionsResponse err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_ListTaskListPartitions_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_ListTaskListPartitions_Result struct could not be generated from the wire // representation. func (v *MatchingService_ListTaskListPartitions_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _ListTaskListPartitionsResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_ListTaskListPartitions_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a MatchingService_ListTaskListPartitions_Result // struct. func (v *MatchingService_ListTaskListPartitions_Result) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("MatchingService_ListTaskListPartitions_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_ListTaskListPartitions_Result match the // provided MatchingService_ListTaskListPartitions_Result. // // This function performs a deep comparison. func (v *MatchingService_ListTaskListPartitions_Result) Equals(rhs *MatchingService_ListTaskListPartitions_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_ListTaskListPartitions_Result. func (v *MatchingService_ListTaskListPartitions_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *MatchingService_ListTaskListPartitions_Result) GetSuccess() (o *shared.ListTaskListPartitionsResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *MatchingService_ListTaskListPartitions_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *MatchingService_ListTaskListPartitions_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *MatchingService_ListTaskListPartitions_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *MatchingService_ListTaskListPartitions_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *MatchingService_ListTaskListPartitions_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *MatchingService_ListTaskListPartitions_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *MatchingService_ListTaskListPartitions_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "ListTaskListPartitions" for this struct. func (v *MatchingService_ListTaskListPartitions_Result) MethodName() string { return "ListTaskListPartitions" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *MatchingService_ListTaskListPartitions_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // MatchingService_PollForActivityTask_Args represents the arguments for the MatchingService.PollForActivityTask function. // // The arguments for PollForActivityTask are sent and received over the wire as this struct. type MatchingService_PollForActivityTask_Args struct { PollRequest *PollForActivityTaskRequest `json:"pollRequest,omitempty"` } // ToWire translates a MatchingService_PollForActivityTask_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_PollForActivityTask_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.PollRequest != nil { w, err = v.PollRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForActivityTaskRequest_1_Read(w wire.Value) (*PollForActivityTaskRequest, error) { var v PollForActivityTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_PollForActivityTask_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_PollForActivityTask_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_PollForActivityTask_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_PollForActivityTask_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.PollRequest, err = _PollForActivityTaskRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MatchingService_PollForActivityTask_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_PollForActivityTask_Args struct could not be encoded. func (v *MatchingService_PollForActivityTask_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.PollRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.PollRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollForActivityTaskRequest_1_Decode(sr stream.Reader) (*PollForActivityTaskRequest, error) { var v PollForActivityTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_PollForActivityTask_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_PollForActivityTask_Args struct could not be generated from the wire // representation. func (v *MatchingService_PollForActivityTask_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.PollRequest, err = _PollForActivityTaskRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MatchingService_PollForActivityTask_Args // struct. func (v *MatchingService_PollForActivityTask_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.PollRequest != nil { fields[i] = fmt.Sprintf("PollRequest: %v", v.PollRequest) i++ } return fmt.Sprintf("MatchingService_PollForActivityTask_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_PollForActivityTask_Args match the // provided MatchingService_PollForActivityTask_Args. // // This function performs a deep comparison. func (v *MatchingService_PollForActivityTask_Args) Equals(rhs *MatchingService_PollForActivityTask_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.PollRequest == nil && rhs.PollRequest == nil) || (v.PollRequest != nil && rhs.PollRequest != nil && v.PollRequest.Equals(rhs.PollRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_PollForActivityTask_Args. func (v *MatchingService_PollForActivityTask_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.PollRequest != nil { err = multierr.Append(err, enc.AddObject("pollRequest", v.PollRequest)) } return err } // GetPollRequest returns the value of PollRequest if it is set or its // zero value if it is unset. func (v *MatchingService_PollForActivityTask_Args) GetPollRequest() (o *PollForActivityTaskRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // IsSetPollRequest returns true if PollRequest is not nil. func (v *MatchingService_PollForActivityTask_Args) IsSetPollRequest() bool { return v != nil && v.PollRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "PollForActivityTask" for this struct. func (v *MatchingService_PollForActivityTask_Args) MethodName() string { return "PollForActivityTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *MatchingService_PollForActivityTask_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // MatchingService_PollForActivityTask_Helper provides functions that aid in handling the // parameters and return values of the MatchingService.PollForActivityTask // function. var MatchingService_PollForActivityTask_Helper = struct { // Args accepts the parameters of PollForActivityTask in-order and returns // the arguments struct for the function. Args func( pollRequest *PollForActivityTaskRequest, ) *MatchingService_PollForActivityTask_Args // IsException returns true if the given error can be thrown // by PollForActivityTask. // // An error can be thrown by PollForActivityTask only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for PollForActivityTask // given its return value and error. // // This allows mapping values and errors returned by // PollForActivityTask into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by PollForActivityTask // // value, err := PollForActivityTask(args) // result, err := MatchingService_PollForActivityTask_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from PollForActivityTask: %v", err) // } // serialize(result) WrapResponse func(*shared.PollForActivityTaskResponse, error) (*MatchingService_PollForActivityTask_Result, error) // UnwrapResponse takes the result struct for PollForActivityTask // and returns the value or error returned by it. // // The error is non-nil only if PollForActivityTask threw an // exception. // // result := deserialize(bytes) // value, err := MatchingService_PollForActivityTask_Helper.UnwrapResponse(result) UnwrapResponse func(*MatchingService_PollForActivityTask_Result) (*shared.PollForActivityTaskResponse, error) }{} func init() { MatchingService_PollForActivityTask_Helper.Args = func( pollRequest *PollForActivityTaskRequest, ) *MatchingService_PollForActivityTask_Args { return &MatchingService_PollForActivityTask_Args{ PollRequest: pollRequest, } } MatchingService_PollForActivityTask_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true default: return false } } MatchingService_PollForActivityTask_Helper.WrapResponse = func(success *shared.PollForActivityTaskResponse, err error) (*MatchingService_PollForActivityTask_Result, error) { if err == nil { return &MatchingService_PollForActivityTask_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_PollForActivityTask_Result.BadRequestError") } return &MatchingService_PollForActivityTask_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_PollForActivityTask_Result.InternalServiceError") } return &MatchingService_PollForActivityTask_Result{InternalServiceError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_PollForActivityTask_Result.LimitExceededError") } return &MatchingService_PollForActivityTask_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_PollForActivityTask_Result.ServiceBusyError") } return &MatchingService_PollForActivityTask_Result{ServiceBusyError: e}, nil } return nil, err } MatchingService_PollForActivityTask_Helper.UnwrapResponse = func(result *MatchingService_PollForActivityTask_Result) (success *shared.PollForActivityTaskResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // MatchingService_PollForActivityTask_Result represents the result of a MatchingService.PollForActivityTask function call. // // The result of a PollForActivityTask execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type MatchingService_PollForActivityTask_Result struct { // Value returned by PollForActivityTask after a successful execution. Success *shared.PollForActivityTaskResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a MatchingService_PollForActivityTask_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_PollForActivityTask_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("MatchingService_PollForActivityTask_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForActivityTaskResponse_Read(w wire.Value) (*shared.PollForActivityTaskResponse, error) { var v shared.PollForActivityTaskResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_PollForActivityTask_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_PollForActivityTask_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_PollForActivityTask_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_PollForActivityTask_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _PollForActivityTaskResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_PollForActivityTask_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a MatchingService_PollForActivityTask_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_PollForActivityTask_Result struct could not be encoded. func (v *MatchingService_PollForActivityTask_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_PollForActivityTask_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _PollForActivityTaskResponse_Decode(sr stream.Reader) (*shared.PollForActivityTaskResponse, error) { var v shared.PollForActivityTaskResponse err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_PollForActivityTask_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_PollForActivityTask_Result struct could not be generated from the wire // representation. func (v *MatchingService_PollForActivityTask_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _PollForActivityTaskResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_PollForActivityTask_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a MatchingService_PollForActivityTask_Result // struct. func (v *MatchingService_PollForActivityTask_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("MatchingService_PollForActivityTask_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_PollForActivityTask_Result match the // provided MatchingService_PollForActivityTask_Result. // // This function performs a deep comparison. func (v *MatchingService_PollForActivityTask_Result) Equals(rhs *MatchingService_PollForActivityTask_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_PollForActivityTask_Result. func (v *MatchingService_PollForActivityTask_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *MatchingService_PollForActivityTask_Result) GetSuccess() (o *shared.PollForActivityTaskResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *MatchingService_PollForActivityTask_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *MatchingService_PollForActivityTask_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *MatchingService_PollForActivityTask_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *MatchingService_PollForActivityTask_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *MatchingService_PollForActivityTask_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *MatchingService_PollForActivityTask_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *MatchingService_PollForActivityTask_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *MatchingService_PollForActivityTask_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *MatchingService_PollForActivityTask_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "PollForActivityTask" for this struct. func (v *MatchingService_PollForActivityTask_Result) MethodName() string { return "PollForActivityTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *MatchingService_PollForActivityTask_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // MatchingService_PollForDecisionTask_Args represents the arguments for the MatchingService.PollForDecisionTask function. // // The arguments for PollForDecisionTask are sent and received over the wire as this struct. type MatchingService_PollForDecisionTask_Args struct { PollRequest *PollForDecisionTaskRequest `json:"pollRequest,omitempty"` } // ToWire translates a MatchingService_PollForDecisionTask_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_PollForDecisionTask_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.PollRequest != nil { w, err = v.PollRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForDecisionTaskRequest_1_Read(w wire.Value) (*PollForDecisionTaskRequest, error) { var v PollForDecisionTaskRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_PollForDecisionTask_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_PollForDecisionTask_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_PollForDecisionTask_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_PollForDecisionTask_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.PollRequest, err = _PollForDecisionTaskRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MatchingService_PollForDecisionTask_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_PollForDecisionTask_Args struct could not be encoded. func (v *MatchingService_PollForDecisionTask_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.PollRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.PollRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollForDecisionTaskRequest_1_Decode(sr stream.Reader) (*PollForDecisionTaskRequest, error) { var v PollForDecisionTaskRequest err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_PollForDecisionTask_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_PollForDecisionTask_Args struct could not be generated from the wire // representation. func (v *MatchingService_PollForDecisionTask_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.PollRequest, err = _PollForDecisionTaskRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MatchingService_PollForDecisionTask_Args // struct. func (v *MatchingService_PollForDecisionTask_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.PollRequest != nil { fields[i] = fmt.Sprintf("PollRequest: %v", v.PollRequest) i++ } return fmt.Sprintf("MatchingService_PollForDecisionTask_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_PollForDecisionTask_Args match the // provided MatchingService_PollForDecisionTask_Args. // // This function performs a deep comparison. func (v *MatchingService_PollForDecisionTask_Args) Equals(rhs *MatchingService_PollForDecisionTask_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.PollRequest == nil && rhs.PollRequest == nil) || (v.PollRequest != nil && rhs.PollRequest != nil && v.PollRequest.Equals(rhs.PollRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_PollForDecisionTask_Args. func (v *MatchingService_PollForDecisionTask_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.PollRequest != nil { err = multierr.Append(err, enc.AddObject("pollRequest", v.PollRequest)) } return err } // GetPollRequest returns the value of PollRequest if it is set or its // zero value if it is unset. func (v *MatchingService_PollForDecisionTask_Args) GetPollRequest() (o *PollForDecisionTaskRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // IsSetPollRequest returns true if PollRequest is not nil. func (v *MatchingService_PollForDecisionTask_Args) IsSetPollRequest() bool { return v != nil && v.PollRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "PollForDecisionTask" for this struct. func (v *MatchingService_PollForDecisionTask_Args) MethodName() string { return "PollForDecisionTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *MatchingService_PollForDecisionTask_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // MatchingService_PollForDecisionTask_Helper provides functions that aid in handling the // parameters and return values of the MatchingService.PollForDecisionTask // function. var MatchingService_PollForDecisionTask_Helper = struct { // Args accepts the parameters of PollForDecisionTask in-order and returns // the arguments struct for the function. Args func( pollRequest *PollForDecisionTaskRequest, ) *MatchingService_PollForDecisionTask_Args // IsException returns true if the given error can be thrown // by PollForDecisionTask. // // An error can be thrown by PollForDecisionTask only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for PollForDecisionTask // given its return value and error. // // This allows mapping values and errors returned by // PollForDecisionTask into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by PollForDecisionTask // // value, err := PollForDecisionTask(args) // result, err := MatchingService_PollForDecisionTask_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from PollForDecisionTask: %v", err) // } // serialize(result) WrapResponse func(*PollForDecisionTaskResponse, error) (*MatchingService_PollForDecisionTask_Result, error) // UnwrapResponse takes the result struct for PollForDecisionTask // and returns the value or error returned by it. // // The error is non-nil only if PollForDecisionTask threw an // exception. // // result := deserialize(bytes) // value, err := MatchingService_PollForDecisionTask_Helper.UnwrapResponse(result) UnwrapResponse func(*MatchingService_PollForDecisionTask_Result) (*PollForDecisionTaskResponse, error) }{} func init() { MatchingService_PollForDecisionTask_Helper.Args = func( pollRequest *PollForDecisionTaskRequest, ) *MatchingService_PollForDecisionTask_Args { return &MatchingService_PollForDecisionTask_Args{ PollRequest: pollRequest, } } MatchingService_PollForDecisionTask_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true default: return false } } MatchingService_PollForDecisionTask_Helper.WrapResponse = func(success *PollForDecisionTaskResponse, err error) (*MatchingService_PollForDecisionTask_Result, error) { if err == nil { return &MatchingService_PollForDecisionTask_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_PollForDecisionTask_Result.BadRequestError") } return &MatchingService_PollForDecisionTask_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_PollForDecisionTask_Result.InternalServiceError") } return &MatchingService_PollForDecisionTask_Result{InternalServiceError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_PollForDecisionTask_Result.LimitExceededError") } return &MatchingService_PollForDecisionTask_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_PollForDecisionTask_Result.ServiceBusyError") } return &MatchingService_PollForDecisionTask_Result{ServiceBusyError: e}, nil } return nil, err } MatchingService_PollForDecisionTask_Helper.UnwrapResponse = func(result *MatchingService_PollForDecisionTask_Result) (success *PollForDecisionTaskResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // MatchingService_PollForDecisionTask_Result represents the result of a MatchingService.PollForDecisionTask function call. // // The result of a PollForDecisionTask execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type MatchingService_PollForDecisionTask_Result struct { // Value returned by PollForDecisionTask after a successful execution. Success *PollForDecisionTaskResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a MatchingService_PollForDecisionTask_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_PollForDecisionTask_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("MatchingService_PollForDecisionTask_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForDecisionTaskResponse_Read(w wire.Value) (*PollForDecisionTaskResponse, error) { var v PollForDecisionTaskResponse err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_PollForDecisionTask_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_PollForDecisionTask_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_PollForDecisionTask_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_PollForDecisionTask_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _PollForDecisionTaskResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_PollForDecisionTask_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a MatchingService_PollForDecisionTask_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_PollForDecisionTask_Result struct could not be encoded. func (v *MatchingService_PollForDecisionTask_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_PollForDecisionTask_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _PollForDecisionTaskResponse_Decode(sr stream.Reader) (*PollForDecisionTaskResponse, error) { var v PollForDecisionTaskResponse err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_PollForDecisionTask_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_PollForDecisionTask_Result struct could not be generated from the wire // representation. func (v *MatchingService_PollForDecisionTask_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _PollForDecisionTaskResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_PollForDecisionTask_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a MatchingService_PollForDecisionTask_Result // struct. func (v *MatchingService_PollForDecisionTask_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("MatchingService_PollForDecisionTask_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_PollForDecisionTask_Result match the // provided MatchingService_PollForDecisionTask_Result. // // This function performs a deep comparison. func (v *MatchingService_PollForDecisionTask_Result) Equals(rhs *MatchingService_PollForDecisionTask_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_PollForDecisionTask_Result. func (v *MatchingService_PollForDecisionTask_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *MatchingService_PollForDecisionTask_Result) GetSuccess() (o *PollForDecisionTaskResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *MatchingService_PollForDecisionTask_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *MatchingService_PollForDecisionTask_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *MatchingService_PollForDecisionTask_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *MatchingService_PollForDecisionTask_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *MatchingService_PollForDecisionTask_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *MatchingService_PollForDecisionTask_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *MatchingService_PollForDecisionTask_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *MatchingService_PollForDecisionTask_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *MatchingService_PollForDecisionTask_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "PollForDecisionTask" for this struct. func (v *MatchingService_PollForDecisionTask_Result) MethodName() string { return "PollForDecisionTask" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *MatchingService_PollForDecisionTask_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // MatchingService_QueryWorkflow_Args represents the arguments for the MatchingService.QueryWorkflow function. // // The arguments for QueryWorkflow are sent and received over the wire as this struct. type MatchingService_QueryWorkflow_Args struct { QueryRequest *QueryWorkflowRequest `json:"queryRequest,omitempty"` } // ToWire translates a MatchingService_QueryWorkflow_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_QueryWorkflow_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.QueryRequest != nil { w, err = v.QueryRequest.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryWorkflowRequest_1_Read(w wire.Value) (*QueryWorkflowRequest, error) { var v QueryWorkflowRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_QueryWorkflow_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_QueryWorkflow_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_QueryWorkflow_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_QueryWorkflow_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.QueryRequest, err = _QueryWorkflowRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MatchingService_QueryWorkflow_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_QueryWorkflow_Args struct could not be encoded. func (v *MatchingService_QueryWorkflow_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.QueryRequest != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.QueryRequest.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryWorkflowRequest_1_Decode(sr stream.Reader) (*QueryWorkflowRequest, error) { var v QueryWorkflowRequest err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_QueryWorkflow_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_QueryWorkflow_Args struct could not be generated from the wire // representation. func (v *MatchingService_QueryWorkflow_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.QueryRequest, err = _QueryWorkflowRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MatchingService_QueryWorkflow_Args // struct. func (v *MatchingService_QueryWorkflow_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.QueryRequest != nil { fields[i] = fmt.Sprintf("QueryRequest: %v", v.QueryRequest) i++ } return fmt.Sprintf("MatchingService_QueryWorkflow_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_QueryWorkflow_Args match the // provided MatchingService_QueryWorkflow_Args. // // This function performs a deep comparison. func (v *MatchingService_QueryWorkflow_Args) Equals(rhs *MatchingService_QueryWorkflow_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.QueryRequest == nil && rhs.QueryRequest == nil) || (v.QueryRequest != nil && rhs.QueryRequest != nil && v.QueryRequest.Equals(rhs.QueryRequest))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_QueryWorkflow_Args. func (v *MatchingService_QueryWorkflow_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.QueryRequest != nil { err = multierr.Append(err, enc.AddObject("queryRequest", v.QueryRequest)) } return err } // GetQueryRequest returns the value of QueryRequest if it is set or its // zero value if it is unset. func (v *MatchingService_QueryWorkflow_Args) GetQueryRequest() (o *QueryWorkflowRequest) { if v != nil && v.QueryRequest != nil { return v.QueryRequest } return } // IsSetQueryRequest returns true if QueryRequest is not nil. func (v *MatchingService_QueryWorkflow_Args) IsSetQueryRequest() bool { return v != nil && v.QueryRequest != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "QueryWorkflow" for this struct. func (v *MatchingService_QueryWorkflow_Args) MethodName() string { return "QueryWorkflow" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *MatchingService_QueryWorkflow_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // MatchingService_QueryWorkflow_Helper provides functions that aid in handling the // parameters and return values of the MatchingService.QueryWorkflow // function. var MatchingService_QueryWorkflow_Helper = struct { // Args accepts the parameters of QueryWorkflow in-order and returns // the arguments struct for the function. Args func( queryRequest *QueryWorkflowRequest, ) *MatchingService_QueryWorkflow_Args // IsException returns true if the given error can be thrown // by QueryWorkflow. // // An error can be thrown by QueryWorkflow only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for QueryWorkflow // given its return value and error. // // This allows mapping values and errors returned by // QueryWorkflow into a serializable result struct. // WrapResponse returns a non-nil error if the provided // error cannot be thrown by QueryWorkflow // // value, err := QueryWorkflow(args) // result, err := MatchingService_QueryWorkflow_Helper.WrapResponse(value, err) // if err != nil { // return fmt.Errorf("unexpected error from QueryWorkflow: %v", err) // } // serialize(result) WrapResponse func(*shared.QueryWorkflowResponse, error) (*MatchingService_QueryWorkflow_Result, error) // UnwrapResponse takes the result struct for QueryWorkflow // and returns the value or error returned by it. // // The error is non-nil only if QueryWorkflow threw an // exception. // // result := deserialize(bytes) // value, err := MatchingService_QueryWorkflow_Helper.UnwrapResponse(result) UnwrapResponse func(*MatchingService_QueryWorkflow_Result) (*shared.QueryWorkflowResponse, error) }{} func init() { MatchingService_QueryWorkflow_Helper.Args = func( queryRequest *QueryWorkflowRequest, ) *MatchingService_QueryWorkflow_Args { return &MatchingService_QueryWorkflow_Args{ QueryRequest: queryRequest, } } MatchingService_QueryWorkflow_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *shared.QueryFailedError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true case *shared.StickyWorkerUnavailableError: return true case *shared.TaskListNotOwnedByHostError: return true default: return false } } MatchingService_QueryWorkflow_Helper.WrapResponse = func(success *shared.QueryWorkflowResponse, err error) (*MatchingService_QueryWorkflow_Result, error) { if err == nil { return &MatchingService_QueryWorkflow_Result{Success: success}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_QueryWorkflow_Result.BadRequestError") } return &MatchingService_QueryWorkflow_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_QueryWorkflow_Result.InternalServiceError") } return &MatchingService_QueryWorkflow_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_QueryWorkflow_Result.EntityNotExistError") } return &MatchingService_QueryWorkflow_Result{EntityNotExistError: e}, nil case *shared.QueryFailedError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_QueryWorkflow_Result.QueryFailedError") } return &MatchingService_QueryWorkflow_Result{QueryFailedError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_QueryWorkflow_Result.LimitExceededError") } return &MatchingService_QueryWorkflow_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_QueryWorkflow_Result.ServiceBusyError") } return &MatchingService_QueryWorkflow_Result{ServiceBusyError: e}, nil case *shared.StickyWorkerUnavailableError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_QueryWorkflow_Result.StickyWorkerUnavailableError") } return &MatchingService_QueryWorkflow_Result{StickyWorkerUnavailableError: e}, nil case *shared.TaskListNotOwnedByHostError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_QueryWorkflow_Result.TaskListNotOwnedByHostError") } return &MatchingService_QueryWorkflow_Result{TaskListNotOwnedByHostError: e}, nil } return nil, err } MatchingService_QueryWorkflow_Helper.UnwrapResponse = func(result *MatchingService_QueryWorkflow_Result) (success *shared.QueryWorkflowResponse, err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.QueryFailedError != nil { err = result.QueryFailedError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } if result.StickyWorkerUnavailableError != nil { err = result.StickyWorkerUnavailableError return } if result.TaskListNotOwnedByHostError != nil { err = result.TaskListNotOwnedByHostError return } if result.Success != nil { success = result.Success return } err = errors.New("expected a non-void result") return } } // MatchingService_QueryWorkflow_Result represents the result of a MatchingService.QueryWorkflow function call. // // The result of a QueryWorkflow execution is sent and received over the wire as this struct. // // Success is set only if the function did not throw an exception. type MatchingService_QueryWorkflow_Result struct { // Value returned by QueryWorkflow after a successful execution. Success *shared.QueryWorkflowResponse `json:"success,omitempty"` BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` QueryFailedError *shared.QueryFailedError `json:"queryFailedError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` StickyWorkerUnavailableError *shared.StickyWorkerUnavailableError `json:"stickyWorkerUnavailableError,omitempty"` TaskListNotOwnedByHostError *shared.TaskListNotOwnedByHostError `json:"taskListNotOwnedByHostError,omitempty"` } // ToWire translates a MatchingService_QueryWorkflow_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_QueryWorkflow_Result) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Success != nil { w, err = v.Success.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 0, Value: w} i++ } if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.QueryFailedError != nil { w, err = v.QueryFailedError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.StickyWorkerUnavailableError != nil { w, err = v.StickyWorkerUnavailableError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.TaskListNotOwnedByHostError != nil { w, err = v.TaskListNotOwnedByHostError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } if i != 1 { return wire.Value{}, fmt.Errorf("MatchingService_QueryWorkflow_Result should have exactly one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryWorkflowResponse_Read(w wire.Value) (*shared.QueryWorkflowResponse, error) { var v shared.QueryWorkflowResponse err := v.FromWire(w) return &v, err } func _QueryFailedError_Read(w wire.Value) (*shared.QueryFailedError, error) { var v shared.QueryFailedError err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_QueryWorkflow_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_QueryWorkflow_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_QueryWorkflow_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_QueryWorkflow_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 0: if field.Value.Type() == wire.TStruct { v.Success, err = _QueryWorkflowResponse_Read(field.Value) if err != nil { return err } } case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.QueryFailedError, err = _QueryFailedError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 6: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } case 7: if field.Value.Type() == wire.TStruct { v.StickyWorkerUnavailableError, err = _StickyWorkerUnavailableError_Read(field.Value) if err != nil { return err } } case 8: if field.Value.Type() == wire.TStruct { v.TaskListNotOwnedByHostError, err = _TaskListNotOwnedByHostError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.QueryFailedError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.StickyWorkerUnavailableError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_QueryWorkflow_Result should have exactly one field: got %v fields", count) } return nil } // Encode serializes a MatchingService_QueryWorkflow_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_QueryWorkflow_Result struct could not be encoded. func (v *MatchingService_QueryWorkflow_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Success != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 0, Type: wire.TStruct}); err != nil { return err } if err := v.Success.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryFailedError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.QueryFailedError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyWorkerUnavailableError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TStruct}); err != nil { return err } if err := v.StickyWorkerUnavailableError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListNotOwnedByHostError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TStruct}); err != nil { return err } if err := v.TaskListNotOwnedByHostError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.QueryFailedError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.StickyWorkerUnavailableError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_QueryWorkflow_Result should have exactly one field: got %v fields", count) } return sw.WriteStructEnd() } func _QueryWorkflowResponse_Decode(sr stream.Reader) (*shared.QueryWorkflowResponse, error) { var v shared.QueryWorkflowResponse err := v.Decode(sr) return &v, err } func _QueryFailedError_Decode(sr stream.Reader) (*shared.QueryFailedError, error) { var v shared.QueryFailedError err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_QueryWorkflow_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_QueryWorkflow_Result struct could not be generated from the wire // representation. func (v *MatchingService_QueryWorkflow_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 0 && fh.Type == wire.TStruct: v.Success, err = _QueryWorkflowResponse_Decode(sr) if err != nil { return err } case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.QueryFailedError, err = _QueryFailedError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TStruct: v.StickyWorkerUnavailableError, err = _StickyWorkerUnavailableError_Decode(sr) if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TStruct: v.TaskListNotOwnedByHostError, err = _TaskListNotOwnedByHostError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.Success != nil { count++ } if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.QueryFailedError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if v.StickyWorkerUnavailableError != nil { count++ } if v.TaskListNotOwnedByHostError != nil { count++ } if count != 1 { return fmt.Errorf("MatchingService_QueryWorkflow_Result should have exactly one field: got %v fields", count) } return nil } // String returns a readable string representation of a MatchingService_QueryWorkflow_Result // struct. func (v *MatchingService_QueryWorkflow_Result) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Success != nil { fields[i] = fmt.Sprintf("Success: %v", v.Success) i++ } if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.QueryFailedError != nil { fields[i] = fmt.Sprintf("QueryFailedError: %v", v.QueryFailedError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } if v.StickyWorkerUnavailableError != nil { fields[i] = fmt.Sprintf("StickyWorkerUnavailableError: %v", v.StickyWorkerUnavailableError) i++ } if v.TaskListNotOwnedByHostError != nil { fields[i] = fmt.Sprintf("TaskListNotOwnedByHostError: %v", v.TaskListNotOwnedByHostError) i++ } return fmt.Sprintf("MatchingService_QueryWorkflow_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_QueryWorkflow_Result match the // provided MatchingService_QueryWorkflow_Result. // // This function performs a deep comparison. func (v *MatchingService_QueryWorkflow_Result) Equals(rhs *MatchingService_QueryWorkflow_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Success == nil && rhs.Success == nil) || (v.Success != nil && rhs.Success != nil && v.Success.Equals(rhs.Success))) { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.QueryFailedError == nil && rhs.QueryFailedError == nil) || (v.QueryFailedError != nil && rhs.QueryFailedError != nil && v.QueryFailedError.Equals(rhs.QueryFailedError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } if !((v.StickyWorkerUnavailableError == nil && rhs.StickyWorkerUnavailableError == nil) || (v.StickyWorkerUnavailableError != nil && rhs.StickyWorkerUnavailableError != nil && v.StickyWorkerUnavailableError.Equals(rhs.StickyWorkerUnavailableError))) { return false } if !((v.TaskListNotOwnedByHostError == nil && rhs.TaskListNotOwnedByHostError == nil) || (v.TaskListNotOwnedByHostError != nil && rhs.TaskListNotOwnedByHostError != nil && v.TaskListNotOwnedByHostError.Equals(rhs.TaskListNotOwnedByHostError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_QueryWorkflow_Result. func (v *MatchingService_QueryWorkflow_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Success != nil { err = multierr.Append(err, enc.AddObject("success", v.Success)) } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.QueryFailedError != nil { err = multierr.Append(err, enc.AddObject("queryFailedError", v.QueryFailedError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } if v.StickyWorkerUnavailableError != nil { err = multierr.Append(err, enc.AddObject("stickyWorkerUnavailableError", v.StickyWorkerUnavailableError)) } if v.TaskListNotOwnedByHostError != nil { err = multierr.Append(err, enc.AddObject("taskListNotOwnedByHostError", v.TaskListNotOwnedByHostError)) } return err } // GetSuccess returns the value of Success if it is set or its // zero value if it is unset. func (v *MatchingService_QueryWorkflow_Result) GetSuccess() (o *shared.QueryWorkflowResponse) { if v != nil && v.Success != nil { return v.Success } return } // IsSetSuccess returns true if Success is not nil. func (v *MatchingService_QueryWorkflow_Result) IsSetSuccess() bool { return v != nil && v.Success != nil } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *MatchingService_QueryWorkflow_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *MatchingService_QueryWorkflow_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *MatchingService_QueryWorkflow_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *MatchingService_QueryWorkflow_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *MatchingService_QueryWorkflow_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *MatchingService_QueryWorkflow_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetQueryFailedError returns the value of QueryFailedError if it is set or its // zero value if it is unset. func (v *MatchingService_QueryWorkflow_Result) GetQueryFailedError() (o *shared.QueryFailedError) { if v != nil && v.QueryFailedError != nil { return v.QueryFailedError } return } // IsSetQueryFailedError returns true if QueryFailedError is not nil. func (v *MatchingService_QueryWorkflow_Result) IsSetQueryFailedError() bool { return v != nil && v.QueryFailedError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *MatchingService_QueryWorkflow_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *MatchingService_QueryWorkflow_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *MatchingService_QueryWorkflow_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *MatchingService_QueryWorkflow_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // GetStickyWorkerUnavailableError returns the value of StickyWorkerUnavailableError if it is set or its // zero value if it is unset. func (v *MatchingService_QueryWorkflow_Result) GetStickyWorkerUnavailableError() (o *shared.StickyWorkerUnavailableError) { if v != nil && v.StickyWorkerUnavailableError != nil { return v.StickyWorkerUnavailableError } return } // IsSetStickyWorkerUnavailableError returns true if StickyWorkerUnavailableError is not nil. func (v *MatchingService_QueryWorkflow_Result) IsSetStickyWorkerUnavailableError() bool { return v != nil && v.StickyWorkerUnavailableError != nil } // GetTaskListNotOwnedByHostError returns the value of TaskListNotOwnedByHostError if it is set or its // zero value if it is unset. func (v *MatchingService_QueryWorkflow_Result) GetTaskListNotOwnedByHostError() (o *shared.TaskListNotOwnedByHostError) { if v != nil && v.TaskListNotOwnedByHostError != nil { return v.TaskListNotOwnedByHostError } return } // IsSetTaskListNotOwnedByHostError returns true if TaskListNotOwnedByHostError is not nil. func (v *MatchingService_QueryWorkflow_Result) IsSetTaskListNotOwnedByHostError() bool { return v != nil && v.TaskListNotOwnedByHostError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "QueryWorkflow" for this struct. func (v *MatchingService_QueryWorkflow_Result) MethodName() string { return "QueryWorkflow" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *MatchingService_QueryWorkflow_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } // MatchingService_RespondQueryTaskCompleted_Args represents the arguments for the MatchingService.RespondQueryTaskCompleted function. // // The arguments for RespondQueryTaskCompleted are sent and received over the wire as this struct. type MatchingService_RespondQueryTaskCompleted_Args struct { Request *RespondQueryTaskCompletedRequest `json:"request,omitempty"` } // ToWire translates a MatchingService_RespondQueryTaskCompleted_Args struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_RespondQueryTaskCompleted_Args) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _RespondQueryTaskCompletedRequest_1_Read(w wire.Value) (*RespondQueryTaskCompletedRequest, error) { var v RespondQueryTaskCompletedRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a MatchingService_RespondQueryTaskCompleted_Args struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_RespondQueryTaskCompleted_Args struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_RespondQueryTaskCompleted_Args // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_RespondQueryTaskCompleted_Args) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.Request, err = _RespondQueryTaskCompletedRequest_1_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MatchingService_RespondQueryTaskCompleted_Args struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_RespondQueryTaskCompleted_Args struct could not be encoded. func (v *MatchingService_RespondQueryTaskCompleted_Args) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _RespondQueryTaskCompletedRequest_1_Decode(sr stream.Reader) (*RespondQueryTaskCompletedRequest, error) { var v RespondQueryTaskCompletedRequest err := v.Decode(sr) return &v, err } // Decode deserializes a MatchingService_RespondQueryTaskCompleted_Args struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_RespondQueryTaskCompleted_Args struct could not be generated from the wire // representation. func (v *MatchingService_RespondQueryTaskCompleted_Args) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.Request, err = _RespondQueryTaskCompletedRequest_1_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MatchingService_RespondQueryTaskCompleted_Args // struct. func (v *MatchingService_RespondQueryTaskCompleted_Args) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("MatchingService_RespondQueryTaskCompleted_Args{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_RespondQueryTaskCompleted_Args match the // provided MatchingService_RespondQueryTaskCompleted_Args. // // This function performs a deep comparison. func (v *MatchingService_RespondQueryTaskCompleted_Args) Equals(rhs *MatchingService_RespondQueryTaskCompleted_Args) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_RespondQueryTaskCompleted_Args. func (v *MatchingService_RespondQueryTaskCompleted_Args) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *MatchingService_RespondQueryTaskCompleted_Args) GetRequest() (o *RespondQueryTaskCompletedRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *MatchingService_RespondQueryTaskCompleted_Args) IsSetRequest() bool { return v != nil && v.Request != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the arguments. // // This will always be "RespondQueryTaskCompleted" for this struct. func (v *MatchingService_RespondQueryTaskCompleted_Args) MethodName() string { return "RespondQueryTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Call for this struct. func (v *MatchingService_RespondQueryTaskCompleted_Args) EnvelopeType() wire.EnvelopeType { return wire.Call } // MatchingService_RespondQueryTaskCompleted_Helper provides functions that aid in handling the // parameters and return values of the MatchingService.RespondQueryTaskCompleted // function. var MatchingService_RespondQueryTaskCompleted_Helper = struct { // Args accepts the parameters of RespondQueryTaskCompleted in-order and returns // the arguments struct for the function. Args func( request *RespondQueryTaskCompletedRequest, ) *MatchingService_RespondQueryTaskCompleted_Args // IsException returns true if the given error can be thrown // by RespondQueryTaskCompleted. // // An error can be thrown by RespondQueryTaskCompleted only if the // corresponding exception type was mentioned in the 'throws' // section for it in the Thrift file. IsException func(error) bool // WrapResponse returns the result struct for RespondQueryTaskCompleted // given the error returned by it. The provided error may // be nil if RespondQueryTaskCompleted did not fail. // // This allows mapping errors returned by RespondQueryTaskCompleted into a // serializable result struct. WrapResponse returns a // non-nil error if the provided error cannot be thrown by // RespondQueryTaskCompleted // // err := RespondQueryTaskCompleted(args) // result, err := MatchingService_RespondQueryTaskCompleted_Helper.WrapResponse(err) // if err != nil { // return fmt.Errorf("unexpected error from RespondQueryTaskCompleted: %v", err) // } // serialize(result) WrapResponse func(error) (*MatchingService_RespondQueryTaskCompleted_Result, error) // UnwrapResponse takes the result struct for RespondQueryTaskCompleted // and returns the erorr returned by it (if any). // // The error is non-nil only if RespondQueryTaskCompleted threw an // exception. // // result := deserialize(bytes) // err := MatchingService_RespondQueryTaskCompleted_Helper.UnwrapResponse(result) UnwrapResponse func(*MatchingService_RespondQueryTaskCompleted_Result) error }{} func init() { MatchingService_RespondQueryTaskCompleted_Helper.Args = func( request *RespondQueryTaskCompletedRequest, ) *MatchingService_RespondQueryTaskCompleted_Args { return &MatchingService_RespondQueryTaskCompleted_Args{ Request: request, } } MatchingService_RespondQueryTaskCompleted_Helper.IsException = func(err error) bool { switch err.(type) { case *shared.BadRequestError: return true case *shared.InternalServiceError: return true case *shared.EntityNotExistsError: return true case *shared.LimitExceededError: return true case *shared.ServiceBusyError: return true default: return false } } MatchingService_RespondQueryTaskCompleted_Helper.WrapResponse = func(err error) (*MatchingService_RespondQueryTaskCompleted_Result, error) { if err == nil { return &MatchingService_RespondQueryTaskCompleted_Result{}, nil } switch e := err.(type) { case *shared.BadRequestError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_RespondQueryTaskCompleted_Result.BadRequestError") } return &MatchingService_RespondQueryTaskCompleted_Result{BadRequestError: e}, nil case *shared.InternalServiceError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_RespondQueryTaskCompleted_Result.InternalServiceError") } return &MatchingService_RespondQueryTaskCompleted_Result{InternalServiceError: e}, nil case *shared.EntityNotExistsError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_RespondQueryTaskCompleted_Result.EntityNotExistError") } return &MatchingService_RespondQueryTaskCompleted_Result{EntityNotExistError: e}, nil case *shared.LimitExceededError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_RespondQueryTaskCompleted_Result.LimitExceededError") } return &MatchingService_RespondQueryTaskCompleted_Result{LimitExceededError: e}, nil case *shared.ServiceBusyError: if e == nil { return nil, errors.New("WrapResponse received non-nil error type with nil value for MatchingService_RespondQueryTaskCompleted_Result.ServiceBusyError") } return &MatchingService_RespondQueryTaskCompleted_Result{ServiceBusyError: e}, nil } return nil, err } MatchingService_RespondQueryTaskCompleted_Helper.UnwrapResponse = func(result *MatchingService_RespondQueryTaskCompleted_Result) (err error) { if result.BadRequestError != nil { err = result.BadRequestError return } if result.InternalServiceError != nil { err = result.InternalServiceError return } if result.EntityNotExistError != nil { err = result.EntityNotExistError return } if result.LimitExceededError != nil { err = result.LimitExceededError return } if result.ServiceBusyError != nil { err = result.ServiceBusyError return } return } } // MatchingService_RespondQueryTaskCompleted_Result represents the result of a MatchingService.RespondQueryTaskCompleted function call. // // The result of a RespondQueryTaskCompleted execution is sent and received over the wire as this struct. type MatchingService_RespondQueryTaskCompleted_Result struct { BadRequestError *shared.BadRequestError `json:"badRequestError,omitempty"` InternalServiceError *shared.InternalServiceError `json:"internalServiceError,omitempty"` EntityNotExistError *shared.EntityNotExistsError `json:"entityNotExistError,omitempty"` LimitExceededError *shared.LimitExceededError `json:"limitExceededError,omitempty"` ServiceBusyError *shared.ServiceBusyError `json:"serviceBusyError,omitempty"` } // ToWire translates a MatchingService_RespondQueryTaskCompleted_Result struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MatchingService_RespondQueryTaskCompleted_Result) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.BadRequestError != nil { w, err = v.BadRequestError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.InternalServiceError != nil { w, err = v.InternalServiceError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.EntityNotExistError != nil { w, err = v.EntityNotExistError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.LimitExceededError != nil { w, err = v.LimitExceededError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.ServiceBusyError != nil { w, err = v.ServiceBusyError.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if i > 1 { return wire.Value{}, fmt.Errorf("MatchingService_RespondQueryTaskCompleted_Result should have at most one field: got %v fields", i) } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a MatchingService_RespondQueryTaskCompleted_Result struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MatchingService_RespondQueryTaskCompleted_Result struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MatchingService_RespondQueryTaskCompleted_Result // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MatchingService_RespondQueryTaskCompleted_Result) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.BadRequestError, err = _BadRequestError_Read(field.Value) if err != nil { return err } } case 2: if field.Value.Type() == wire.TStruct { v.InternalServiceError, err = _InternalServiceError_Read(field.Value) if err != nil { return err } } case 3: if field.Value.Type() == wire.TStruct { v.EntityNotExistError, err = _EntityNotExistsError_Read(field.Value) if err != nil { return err } } case 4: if field.Value.Type() == wire.TStruct { v.LimitExceededError, err = _LimitExceededError_Read(field.Value) if err != nil { return err } } case 5: if field.Value.Type() == wire.TStruct { v.ServiceBusyError, err = _ServiceBusyError_Read(field.Value) if err != nil { return err } } } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_RespondQueryTaskCompleted_Result should have at most one field: got %v fields", count) } return nil } // Encode serializes a MatchingService_RespondQueryTaskCompleted_Result struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MatchingService_RespondQueryTaskCompleted_Result struct could not be encoded. func (v *MatchingService_RespondQueryTaskCompleted_Result) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BadRequestError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.BadRequestError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InternalServiceError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TStruct}); err != nil { return err } if err := v.InternalServiceError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EntityNotExistError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TStruct}); err != nil { return err } if err := v.EntityNotExistError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LimitExceededError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TStruct}); err != nil { return err } if err := v.LimitExceededError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ServiceBusyError != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TStruct}); err != nil { return err } if err := v.ServiceBusyError.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_RespondQueryTaskCompleted_Result should have at most one field: got %v fields", count) } return sw.WriteStructEnd() } // Decode deserializes a MatchingService_RespondQueryTaskCompleted_Result struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MatchingService_RespondQueryTaskCompleted_Result struct could not be generated from the wire // representation. func (v *MatchingService_RespondQueryTaskCompleted_Result) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.BadRequestError, err = _BadRequestError_Decode(sr) if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TStruct: v.InternalServiceError, err = _InternalServiceError_Decode(sr) if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TStruct: v.EntityNotExistError, err = _EntityNotExistsError_Decode(sr) if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TStruct: v.LimitExceededError, err = _LimitExceededError_Decode(sr) if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TStruct: v.ServiceBusyError, err = _ServiceBusyError_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } count := 0 if v.BadRequestError != nil { count++ } if v.InternalServiceError != nil { count++ } if v.EntityNotExistError != nil { count++ } if v.LimitExceededError != nil { count++ } if v.ServiceBusyError != nil { count++ } if count > 1 { return fmt.Errorf("MatchingService_RespondQueryTaskCompleted_Result should have at most one field: got %v fields", count) } return nil } // String returns a readable string representation of a MatchingService_RespondQueryTaskCompleted_Result // struct. func (v *MatchingService_RespondQueryTaskCompleted_Result) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.BadRequestError != nil { fields[i] = fmt.Sprintf("BadRequestError: %v", v.BadRequestError) i++ } if v.InternalServiceError != nil { fields[i] = fmt.Sprintf("InternalServiceError: %v", v.InternalServiceError) i++ } if v.EntityNotExistError != nil { fields[i] = fmt.Sprintf("EntityNotExistError: %v", v.EntityNotExistError) i++ } if v.LimitExceededError != nil { fields[i] = fmt.Sprintf("LimitExceededError: %v", v.LimitExceededError) i++ } if v.ServiceBusyError != nil { fields[i] = fmt.Sprintf("ServiceBusyError: %v", v.ServiceBusyError) i++ } return fmt.Sprintf("MatchingService_RespondQueryTaskCompleted_Result{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MatchingService_RespondQueryTaskCompleted_Result match the // provided MatchingService_RespondQueryTaskCompleted_Result. // // This function performs a deep comparison. func (v *MatchingService_RespondQueryTaskCompleted_Result) Equals(rhs *MatchingService_RespondQueryTaskCompleted_Result) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BadRequestError == nil && rhs.BadRequestError == nil) || (v.BadRequestError != nil && rhs.BadRequestError != nil && v.BadRequestError.Equals(rhs.BadRequestError))) { return false } if !((v.InternalServiceError == nil && rhs.InternalServiceError == nil) || (v.InternalServiceError != nil && rhs.InternalServiceError != nil && v.InternalServiceError.Equals(rhs.InternalServiceError))) { return false } if !((v.EntityNotExistError == nil && rhs.EntityNotExistError == nil) || (v.EntityNotExistError != nil && rhs.EntityNotExistError != nil && v.EntityNotExistError.Equals(rhs.EntityNotExistError))) { return false } if !((v.LimitExceededError == nil && rhs.LimitExceededError == nil) || (v.LimitExceededError != nil && rhs.LimitExceededError != nil && v.LimitExceededError.Equals(rhs.LimitExceededError))) { return false } if !((v.ServiceBusyError == nil && rhs.ServiceBusyError == nil) || (v.ServiceBusyError != nil && rhs.ServiceBusyError != nil && v.ServiceBusyError.Equals(rhs.ServiceBusyError))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MatchingService_RespondQueryTaskCompleted_Result. func (v *MatchingService_RespondQueryTaskCompleted_Result) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BadRequestError != nil { err = multierr.Append(err, enc.AddObject("badRequestError", v.BadRequestError)) } if v.InternalServiceError != nil { err = multierr.Append(err, enc.AddObject("internalServiceError", v.InternalServiceError)) } if v.EntityNotExistError != nil { err = multierr.Append(err, enc.AddObject("entityNotExistError", v.EntityNotExistError)) } if v.LimitExceededError != nil { err = multierr.Append(err, enc.AddObject("limitExceededError", v.LimitExceededError)) } if v.ServiceBusyError != nil { err = multierr.Append(err, enc.AddObject("serviceBusyError", v.ServiceBusyError)) } return err } // GetBadRequestError returns the value of BadRequestError if it is set or its // zero value if it is unset. func (v *MatchingService_RespondQueryTaskCompleted_Result) GetBadRequestError() (o *shared.BadRequestError) { if v != nil && v.BadRequestError != nil { return v.BadRequestError } return } // IsSetBadRequestError returns true if BadRequestError is not nil. func (v *MatchingService_RespondQueryTaskCompleted_Result) IsSetBadRequestError() bool { return v != nil && v.BadRequestError != nil } // GetInternalServiceError returns the value of InternalServiceError if it is set or its // zero value if it is unset. func (v *MatchingService_RespondQueryTaskCompleted_Result) GetInternalServiceError() (o *shared.InternalServiceError) { if v != nil && v.InternalServiceError != nil { return v.InternalServiceError } return } // IsSetInternalServiceError returns true if InternalServiceError is not nil. func (v *MatchingService_RespondQueryTaskCompleted_Result) IsSetInternalServiceError() bool { return v != nil && v.InternalServiceError != nil } // GetEntityNotExistError returns the value of EntityNotExistError if it is set or its // zero value if it is unset. func (v *MatchingService_RespondQueryTaskCompleted_Result) GetEntityNotExistError() (o *shared.EntityNotExistsError) { if v != nil && v.EntityNotExistError != nil { return v.EntityNotExistError } return } // IsSetEntityNotExistError returns true if EntityNotExistError is not nil. func (v *MatchingService_RespondQueryTaskCompleted_Result) IsSetEntityNotExistError() bool { return v != nil && v.EntityNotExistError != nil } // GetLimitExceededError returns the value of LimitExceededError if it is set or its // zero value if it is unset. func (v *MatchingService_RespondQueryTaskCompleted_Result) GetLimitExceededError() (o *shared.LimitExceededError) { if v != nil && v.LimitExceededError != nil { return v.LimitExceededError } return } // IsSetLimitExceededError returns true if LimitExceededError is not nil. func (v *MatchingService_RespondQueryTaskCompleted_Result) IsSetLimitExceededError() bool { return v != nil && v.LimitExceededError != nil } // GetServiceBusyError returns the value of ServiceBusyError if it is set or its // zero value if it is unset. func (v *MatchingService_RespondQueryTaskCompleted_Result) GetServiceBusyError() (o *shared.ServiceBusyError) { if v != nil && v.ServiceBusyError != nil { return v.ServiceBusyError } return } // IsSetServiceBusyError returns true if ServiceBusyError is not nil. func (v *MatchingService_RespondQueryTaskCompleted_Result) IsSetServiceBusyError() bool { return v != nil && v.ServiceBusyError != nil } // MethodName returns the name of the Thrift function as specified in // the IDL, for which this struct represent the result. // // This will always be "RespondQueryTaskCompleted" for this struct. func (v *MatchingService_RespondQueryTaskCompleted_Result) MethodName() string { return "RespondQueryTaskCompleted" } // EnvelopeType returns the kind of value inside this struct. // // This will always be Reply for this struct. func (v *MatchingService_RespondQueryTaskCompleted_Result) EnvelopeType() wire.EnvelopeType { return wire.Reply } ================================================ FILE: .gen/go/matching/matchingserviceclient/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package matchingserviceclient import ( context "context" reflect "reflect" wire "go.uber.org/thriftrw/wire" yarpc "go.uber.org/yarpc" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" matching "github.com/uber/cadence/.gen/go/matching" shared "github.com/uber/cadence/.gen/go/shared" ) // Interface is a client for the MatchingService service. type Interface interface { AddActivityTask( ctx context.Context, AddRequest *matching.AddActivityTaskRequest, opts ...yarpc.CallOption, ) error AddDecisionTask( ctx context.Context, AddRequest *matching.AddDecisionTaskRequest, opts ...yarpc.CallOption, ) error CancelOutstandingPoll( ctx context.Context, Request *matching.CancelOutstandingPollRequest, opts ...yarpc.CallOption, ) error DescribeTaskList( ctx context.Context, Request *matching.DescribeTaskListRequest, opts ...yarpc.CallOption, ) (*shared.DescribeTaskListResponse, error) GetTaskListsByDomain( ctx context.Context, Request *shared.GetTaskListsByDomainRequest, opts ...yarpc.CallOption, ) (*shared.GetTaskListsByDomainResponse, error) ListTaskListPartitions( ctx context.Context, Request *matching.ListTaskListPartitionsRequest, opts ...yarpc.CallOption, ) (*shared.ListTaskListPartitionsResponse, error) PollForActivityTask( ctx context.Context, PollRequest *matching.PollForActivityTaskRequest, opts ...yarpc.CallOption, ) (*shared.PollForActivityTaskResponse, error) PollForDecisionTask( ctx context.Context, PollRequest *matching.PollForDecisionTaskRequest, opts ...yarpc.CallOption, ) (*matching.PollForDecisionTaskResponse, error) QueryWorkflow( ctx context.Context, QueryRequest *matching.QueryWorkflowRequest, opts ...yarpc.CallOption, ) (*shared.QueryWorkflowResponse, error) RespondQueryTaskCompleted( ctx context.Context, Request *matching.RespondQueryTaskCompletedRequest, opts ...yarpc.CallOption, ) error } // New builds a new client for the MatchingService service. // // client := matchingserviceclient.New(dispatcher.ClientConfig("matchingservice")) func New(c transport.ClientConfig, opts ...thrift.ClientOption) Interface { return client{ c: thrift.New(thrift.Config{ Service: "MatchingService", ClientConfig: c, }, opts...), nwc: thrift.NewNoWire(thrift.Config{ Service: "MatchingService", ClientConfig: c, }, opts...), } } func init() { yarpc.RegisterClientBuilder( func(c transport.ClientConfig, f reflect.StructField) Interface { return New(c, thrift.ClientBuilderOptions(c, f)...) }, ) } type client struct { c thrift.Client nwc thrift.NoWireClient } func (c client) AddActivityTask( ctx context.Context, _AddRequest *matching.AddActivityTaskRequest, opts ...yarpc.CallOption, ) (err error) { var result matching.MatchingService_AddActivityTask_Result args := matching.MatchingService_AddActivityTask_Helper.Args(_AddRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = matching.MatchingService_AddActivityTask_Helper.UnwrapResponse(&result) return } func (c client) AddDecisionTask( ctx context.Context, _AddRequest *matching.AddDecisionTaskRequest, opts ...yarpc.CallOption, ) (err error) { var result matching.MatchingService_AddDecisionTask_Result args := matching.MatchingService_AddDecisionTask_Helper.Args(_AddRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = matching.MatchingService_AddDecisionTask_Helper.UnwrapResponse(&result) return } func (c client) CancelOutstandingPoll( ctx context.Context, _Request *matching.CancelOutstandingPollRequest, opts ...yarpc.CallOption, ) (err error) { var result matching.MatchingService_CancelOutstandingPoll_Result args := matching.MatchingService_CancelOutstandingPoll_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = matching.MatchingService_CancelOutstandingPoll_Helper.UnwrapResponse(&result) return } func (c client) DescribeTaskList( ctx context.Context, _Request *matching.DescribeTaskListRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeTaskListResponse, err error) { var result matching.MatchingService_DescribeTaskList_Result args := matching.MatchingService_DescribeTaskList_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = matching.MatchingService_DescribeTaskList_Helper.UnwrapResponse(&result) return } func (c client) GetTaskListsByDomain( ctx context.Context, _Request *shared.GetTaskListsByDomainRequest, opts ...yarpc.CallOption, ) (success *shared.GetTaskListsByDomainResponse, err error) { var result matching.MatchingService_GetTaskListsByDomain_Result args := matching.MatchingService_GetTaskListsByDomain_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = matching.MatchingService_GetTaskListsByDomain_Helper.UnwrapResponse(&result) return } func (c client) ListTaskListPartitions( ctx context.Context, _Request *matching.ListTaskListPartitionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListTaskListPartitionsResponse, err error) { var result matching.MatchingService_ListTaskListPartitions_Result args := matching.MatchingService_ListTaskListPartitions_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = matching.MatchingService_ListTaskListPartitions_Helper.UnwrapResponse(&result) return } func (c client) PollForActivityTask( ctx context.Context, _PollRequest *matching.PollForActivityTaskRequest, opts ...yarpc.CallOption, ) (success *shared.PollForActivityTaskResponse, err error) { var result matching.MatchingService_PollForActivityTask_Result args := matching.MatchingService_PollForActivityTask_Helper.Args(_PollRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = matching.MatchingService_PollForActivityTask_Helper.UnwrapResponse(&result) return } func (c client) PollForDecisionTask( ctx context.Context, _PollRequest *matching.PollForDecisionTaskRequest, opts ...yarpc.CallOption, ) (success *matching.PollForDecisionTaskResponse, err error) { var result matching.MatchingService_PollForDecisionTask_Result args := matching.MatchingService_PollForDecisionTask_Helper.Args(_PollRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = matching.MatchingService_PollForDecisionTask_Helper.UnwrapResponse(&result) return } func (c client) QueryWorkflow( ctx context.Context, _QueryRequest *matching.QueryWorkflowRequest, opts ...yarpc.CallOption, ) (success *shared.QueryWorkflowResponse, err error) { var result matching.MatchingService_QueryWorkflow_Result args := matching.MatchingService_QueryWorkflow_Helper.Args(_QueryRequest) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } success, err = matching.MatchingService_QueryWorkflow_Helper.UnwrapResponse(&result) return } func (c client) RespondQueryTaskCompleted( ctx context.Context, _Request *matching.RespondQueryTaskCompletedRequest, opts ...yarpc.CallOption, ) (err error) { var result matching.MatchingService_RespondQueryTaskCompleted_Result args := matching.MatchingService_RespondQueryTaskCompleted_Helper.Args(_Request) if c.nwc != nil && c.nwc.Enabled() { if err = c.nwc.Call(ctx, args, &result, opts...); err != nil { return } } else { var body wire.Value if body, err = c.c.Call(ctx, args, opts...); err != nil { return } if err = result.FromWire(body); err != nil { return } } err = matching.MatchingService_RespondQueryTaskCompleted_Helper.UnwrapResponse(&result) return } ================================================ FILE: .gen/go/matching/matchingservicefx/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package matchingservicefx import ( fx "go.uber.org/fx" yarpc "go.uber.org/yarpc" transport "go.uber.org/yarpc/api/transport" restriction "go.uber.org/yarpc/api/x/restriction" thrift "go.uber.org/yarpc/encoding/thrift" matchingserviceclient "github.com/uber/cadence/.gen/go/matching/matchingserviceclient" ) // Params defines the dependencies for the MatchingService client. type Params struct { fx.In Provider yarpc.ClientConfig Restriction restriction.Checker `optional:"true"` } // Result defines the output of the MatchingService client module. It provides a // MatchingService client to an Fx application. type Result struct { fx.Out Client matchingserviceclient.Interface // We are using an fx.Out struct here instead of just returning a client // so that we can add more values or add named versions of the client in // the future without breaking any existing code. } // Client provides a MatchingService client to an Fx application using the given name // for routing. // // fx.Provide( // matchingservicefx.Client("..."), // newHandler, // ) func Client(name string, opts ...thrift.ClientOption) interface{} { return func(p Params) Result { cc := p.Provider.ClientConfig(name) if namer, ok := cc.GetUnaryOutbound().(transport.Namer); ok && p.Restriction != nil { if err := p.Restriction.Check(thrift.Encoding, namer.TransportName()); err != nil { panic(err.Error()) } } client := matchingserviceclient.New(cc, opts...) return Result{Client: client} } } ================================================ FILE: .gen/go/matching/matchingservicefx/doc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated // Package matchingservicefx provides better integration for Fx for services // implementing or calling MatchingService. // // # Clients // // If you are making requests to MatchingService, use the Client function to inject a // MatchingService client into your container. // // fx.Provide(matchingservicefx.Client("...")) // // # Servers // // If you are implementing MatchingService, provide a matchingserviceserver.Interface into // the container and use the Server function. // // Given, // // func NewMatchingServiceHandler() matchingserviceserver.Interface // // You can do the following to have the procedures of MatchingService made available // to an Fx application. // // fx.Provide( // NewMatchingServiceHandler, // matchingservicefx.Server(), // ) package matchingservicefx ================================================ FILE: .gen/go/matching/matchingservicefx/server.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package matchingservicefx import ( fx "go.uber.org/fx" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" matchingserviceserver "github.com/uber/cadence/.gen/go/matching/matchingserviceserver" ) // ServerParams defines the dependencies for the MatchingService server. type ServerParams struct { fx.In Handler matchingserviceserver.Interface } // ServerResult defines the output of MatchingService server module. It provides the // procedures of a MatchingService handler to an Fx application. // // The procedures are provided to the "yarpcfx" value group. Dig 1.2 or newer // must be used for this feature to work. type ServerResult struct { fx.Out Procedures []transport.Procedure `group:"yarpcfx"` } // Server provides procedures for MatchingService to an Fx application. It expects a // matchingservicefx.Interface to be present in the container. // // fx.Provide( // func(h *MyMatchingServiceHandler) matchingserviceserver.Interface { // return h // }, // matchingservicefx.Server(), // ) func Server(opts ...thrift.RegisterOption) interface{} { return func(p ServerParams) ServerResult { procedures := matchingserviceserver.New(p.Handler, opts...) return ServerResult{Procedures: procedures} } } ================================================ FILE: .gen/go/matching/matchingserviceserver/server.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package matchingserviceserver import ( context "context" stream "go.uber.org/thriftrw/protocol/stream" wire "go.uber.org/thriftrw/wire" transport "go.uber.org/yarpc/api/transport" thrift "go.uber.org/yarpc/encoding/thrift" yarpcerrors "go.uber.org/yarpc/yarpcerrors" matching "github.com/uber/cadence/.gen/go/matching" shared "github.com/uber/cadence/.gen/go/shared" ) // Interface is the server-side interface for the MatchingService service. type Interface interface { AddActivityTask( ctx context.Context, AddRequest *matching.AddActivityTaskRequest, ) error AddDecisionTask( ctx context.Context, AddRequest *matching.AddDecisionTaskRequest, ) error CancelOutstandingPoll( ctx context.Context, Request *matching.CancelOutstandingPollRequest, ) error DescribeTaskList( ctx context.Context, Request *matching.DescribeTaskListRequest, ) (*shared.DescribeTaskListResponse, error) GetTaskListsByDomain( ctx context.Context, Request *shared.GetTaskListsByDomainRequest, ) (*shared.GetTaskListsByDomainResponse, error) ListTaskListPartitions( ctx context.Context, Request *matching.ListTaskListPartitionsRequest, ) (*shared.ListTaskListPartitionsResponse, error) PollForActivityTask( ctx context.Context, PollRequest *matching.PollForActivityTaskRequest, ) (*shared.PollForActivityTaskResponse, error) PollForDecisionTask( ctx context.Context, PollRequest *matching.PollForDecisionTaskRequest, ) (*matching.PollForDecisionTaskResponse, error) QueryWorkflow( ctx context.Context, QueryRequest *matching.QueryWorkflowRequest, ) (*shared.QueryWorkflowResponse, error) RespondQueryTaskCompleted( ctx context.Context, Request *matching.RespondQueryTaskCompletedRequest, ) error } // New prepares an implementation of the MatchingService service for // registration. // // handler := MatchingServiceHandler{} // dispatcher.Register(matchingserviceserver.New(handler)) func New(impl Interface, opts ...thrift.RegisterOption) []transport.Procedure { h := handler{impl} service := thrift.Service{ Name: "MatchingService", Methods: []thrift.Method{ thrift.Method{ Name: "AddActivityTask", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.AddActivityTask), NoWire: addactivitytask_NoWireHandler{impl}, }, Signature: "AddActivityTask(AddRequest *matching.AddActivityTaskRequest)", ThriftModule: matching.ThriftModule, }, thrift.Method{ Name: "AddDecisionTask", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.AddDecisionTask), NoWire: adddecisiontask_NoWireHandler{impl}, }, Signature: "AddDecisionTask(AddRequest *matching.AddDecisionTaskRequest)", ThriftModule: matching.ThriftModule, }, thrift.Method{ Name: "CancelOutstandingPoll", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.CancelOutstandingPoll), NoWire: canceloutstandingpoll_NoWireHandler{impl}, }, Signature: "CancelOutstandingPoll(Request *matching.CancelOutstandingPollRequest)", ThriftModule: matching.ThriftModule, }, thrift.Method{ Name: "DescribeTaskList", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.DescribeTaskList), NoWire: describetasklist_NoWireHandler{impl}, }, Signature: "DescribeTaskList(Request *matching.DescribeTaskListRequest) (*shared.DescribeTaskListResponse)", ThriftModule: matching.ThriftModule, }, thrift.Method{ Name: "GetTaskListsByDomain", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.GetTaskListsByDomain), NoWire: gettasklistsbydomain_NoWireHandler{impl}, }, Signature: "GetTaskListsByDomain(Request *shared.GetTaskListsByDomainRequest) (*shared.GetTaskListsByDomainResponse)", ThriftModule: matching.ThriftModule, }, thrift.Method{ Name: "ListTaskListPartitions", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.ListTaskListPartitions), NoWire: listtasklistpartitions_NoWireHandler{impl}, }, Signature: "ListTaskListPartitions(Request *matching.ListTaskListPartitionsRequest) (*shared.ListTaskListPartitionsResponse)", ThriftModule: matching.ThriftModule, }, thrift.Method{ Name: "PollForActivityTask", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.PollForActivityTask), NoWire: pollforactivitytask_NoWireHandler{impl}, }, Signature: "PollForActivityTask(PollRequest *matching.PollForActivityTaskRequest) (*shared.PollForActivityTaskResponse)", ThriftModule: matching.ThriftModule, }, thrift.Method{ Name: "PollForDecisionTask", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.PollForDecisionTask), NoWire: pollfordecisiontask_NoWireHandler{impl}, }, Signature: "PollForDecisionTask(PollRequest *matching.PollForDecisionTaskRequest) (*matching.PollForDecisionTaskResponse)", ThriftModule: matching.ThriftModule, }, thrift.Method{ Name: "QueryWorkflow", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.QueryWorkflow), NoWire: queryworkflow_NoWireHandler{impl}, }, Signature: "QueryWorkflow(QueryRequest *matching.QueryWorkflowRequest) (*shared.QueryWorkflowResponse)", ThriftModule: matching.ThriftModule, }, thrift.Method{ Name: "RespondQueryTaskCompleted", HandlerSpec: thrift.HandlerSpec{ Type: transport.Unary, Unary: thrift.UnaryHandler(h.RespondQueryTaskCompleted), NoWire: respondquerytaskcompleted_NoWireHandler{impl}, }, Signature: "RespondQueryTaskCompleted(Request *matching.RespondQueryTaskCompletedRequest)", ThriftModule: matching.ThriftModule, }, }, } procedures := make([]transport.Procedure, 0, 10) procedures = append(procedures, thrift.BuildProcedures(service, opts...)...) return procedures } type handler struct{ impl Interface } type yarpcErrorNamer interface{ YARPCErrorName() string } type yarpcErrorCoder interface{ YARPCErrorCode() *yarpcerrors.Code } func (h handler) AddActivityTask(ctx context.Context, body wire.Value) (thrift.Response, error) { var args matching.MatchingService_AddActivityTask_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'MatchingService' procedure 'AddActivityTask': %w", err) } appErr := h.impl.AddActivityTask(ctx, args.AddRequest) hadError := appErr != nil result, err := matching.MatchingService_AddActivityTask_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) AddDecisionTask(ctx context.Context, body wire.Value) (thrift.Response, error) { var args matching.MatchingService_AddDecisionTask_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'MatchingService' procedure 'AddDecisionTask': %w", err) } appErr := h.impl.AddDecisionTask(ctx, args.AddRequest) hadError := appErr != nil result, err := matching.MatchingService_AddDecisionTask_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) CancelOutstandingPoll(ctx context.Context, body wire.Value) (thrift.Response, error) { var args matching.MatchingService_CancelOutstandingPoll_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'MatchingService' procedure 'CancelOutstandingPoll': %w", err) } appErr := h.impl.CancelOutstandingPoll(ctx, args.Request) hadError := appErr != nil result, err := matching.MatchingService_CancelOutstandingPoll_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) DescribeTaskList(ctx context.Context, body wire.Value) (thrift.Response, error) { var args matching.MatchingService_DescribeTaskList_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'MatchingService' procedure 'DescribeTaskList': %w", err) } success, appErr := h.impl.DescribeTaskList(ctx, args.Request) hadError := appErr != nil result, err := matching.MatchingService_DescribeTaskList_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) GetTaskListsByDomain(ctx context.Context, body wire.Value) (thrift.Response, error) { var args matching.MatchingService_GetTaskListsByDomain_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'MatchingService' procedure 'GetTaskListsByDomain': %w", err) } success, appErr := h.impl.GetTaskListsByDomain(ctx, args.Request) hadError := appErr != nil result, err := matching.MatchingService_GetTaskListsByDomain_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) ListTaskListPartitions(ctx context.Context, body wire.Value) (thrift.Response, error) { var args matching.MatchingService_ListTaskListPartitions_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'MatchingService' procedure 'ListTaskListPartitions': %w", err) } success, appErr := h.impl.ListTaskListPartitions(ctx, args.Request) hadError := appErr != nil result, err := matching.MatchingService_ListTaskListPartitions_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) PollForActivityTask(ctx context.Context, body wire.Value) (thrift.Response, error) { var args matching.MatchingService_PollForActivityTask_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'MatchingService' procedure 'PollForActivityTask': %w", err) } success, appErr := h.impl.PollForActivityTask(ctx, args.PollRequest) hadError := appErr != nil result, err := matching.MatchingService_PollForActivityTask_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) PollForDecisionTask(ctx context.Context, body wire.Value) (thrift.Response, error) { var args matching.MatchingService_PollForDecisionTask_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'MatchingService' procedure 'PollForDecisionTask': %w", err) } success, appErr := h.impl.PollForDecisionTask(ctx, args.PollRequest) hadError := appErr != nil result, err := matching.MatchingService_PollForDecisionTask_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) QueryWorkflow(ctx context.Context, body wire.Value) (thrift.Response, error) { var args matching.MatchingService_QueryWorkflow_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'MatchingService' procedure 'QueryWorkflow': %w", err) } success, appErr := h.impl.QueryWorkflow(ctx, args.QueryRequest) hadError := appErr != nil result, err := matching.MatchingService_QueryWorkflow_Helper.WrapResponse(success, appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } func (h handler) RespondQueryTaskCompleted(ctx context.Context, body wire.Value) (thrift.Response, error) { var args matching.MatchingService_RespondQueryTaskCompleted_Args if err := args.FromWire(body); err != nil { return thrift.Response{}, yarpcerrors.InvalidArgumentErrorf( "could not decode Thrift request for service 'MatchingService' procedure 'RespondQueryTaskCompleted': %w", err) } appErr := h.impl.RespondQueryTaskCompleted(ctx, args.Request) hadError := appErr != nil result, err := matching.MatchingService_RespondQueryTaskCompleted_Helper.WrapResponse(appErr) var response thrift.Response if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type addactivitytask_NoWireHandler struct{ impl Interface } func (h addactivitytask_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args matching.MatchingService_AddActivityTask_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'MatchingService' procedure 'AddActivityTask': %w", err) } appErr := h.impl.AddActivityTask(ctx, args.AddRequest) hadError := appErr != nil result, err := matching.MatchingService_AddActivityTask_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type adddecisiontask_NoWireHandler struct{ impl Interface } func (h adddecisiontask_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args matching.MatchingService_AddDecisionTask_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'MatchingService' procedure 'AddDecisionTask': %w", err) } appErr := h.impl.AddDecisionTask(ctx, args.AddRequest) hadError := appErr != nil result, err := matching.MatchingService_AddDecisionTask_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type canceloutstandingpoll_NoWireHandler struct{ impl Interface } func (h canceloutstandingpoll_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args matching.MatchingService_CancelOutstandingPoll_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'MatchingService' procedure 'CancelOutstandingPoll': %w", err) } appErr := h.impl.CancelOutstandingPoll(ctx, args.Request) hadError := appErr != nil result, err := matching.MatchingService_CancelOutstandingPoll_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type describetasklist_NoWireHandler struct{ impl Interface } func (h describetasklist_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args matching.MatchingService_DescribeTaskList_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'MatchingService' procedure 'DescribeTaskList': %w", err) } success, appErr := h.impl.DescribeTaskList(ctx, args.Request) hadError := appErr != nil result, err := matching.MatchingService_DescribeTaskList_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type gettasklistsbydomain_NoWireHandler struct{ impl Interface } func (h gettasklistsbydomain_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args matching.MatchingService_GetTaskListsByDomain_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'MatchingService' procedure 'GetTaskListsByDomain': %w", err) } success, appErr := h.impl.GetTaskListsByDomain(ctx, args.Request) hadError := appErr != nil result, err := matching.MatchingService_GetTaskListsByDomain_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type listtasklistpartitions_NoWireHandler struct{ impl Interface } func (h listtasklistpartitions_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args matching.MatchingService_ListTaskListPartitions_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'MatchingService' procedure 'ListTaskListPartitions': %w", err) } success, appErr := h.impl.ListTaskListPartitions(ctx, args.Request) hadError := appErr != nil result, err := matching.MatchingService_ListTaskListPartitions_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type pollforactivitytask_NoWireHandler struct{ impl Interface } func (h pollforactivitytask_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args matching.MatchingService_PollForActivityTask_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'MatchingService' procedure 'PollForActivityTask': %w", err) } success, appErr := h.impl.PollForActivityTask(ctx, args.PollRequest) hadError := appErr != nil result, err := matching.MatchingService_PollForActivityTask_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type pollfordecisiontask_NoWireHandler struct{ impl Interface } func (h pollfordecisiontask_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args matching.MatchingService_PollForDecisionTask_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'MatchingService' procedure 'PollForDecisionTask': %w", err) } success, appErr := h.impl.PollForDecisionTask(ctx, args.PollRequest) hadError := appErr != nil result, err := matching.MatchingService_PollForDecisionTask_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type queryworkflow_NoWireHandler struct{ impl Interface } func (h queryworkflow_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args matching.MatchingService_QueryWorkflow_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'MatchingService' procedure 'QueryWorkflow': %w", err) } success, appErr := h.impl.QueryWorkflow(ctx, args.QueryRequest) hadError := appErr != nil result, err := matching.MatchingService_QueryWorkflow_Helper.WrapResponse(success, appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } type respondquerytaskcompleted_NoWireHandler struct{ impl Interface } func (h respondquerytaskcompleted_NoWireHandler) HandleNoWire(ctx context.Context, nwc *thrift.NoWireCall) (thrift.NoWireResponse, error) { var ( args matching.MatchingService_RespondQueryTaskCompleted_Args rw stream.ResponseWriter err error ) rw, err = nwc.RequestReader.ReadRequest(ctx, nwc.EnvelopeType, nwc.Reader, &args) if err != nil { return thrift.NoWireResponse{}, yarpcerrors.InvalidArgumentErrorf( "could not decode (via no wire) Thrift request for service 'MatchingService' procedure 'RespondQueryTaskCompleted': %w", err) } appErr := h.impl.RespondQueryTaskCompleted(ctx, args.Request) hadError := appErr != nil result, err := matching.MatchingService_RespondQueryTaskCompleted_Helper.WrapResponse(appErr) response := thrift.NoWireResponse{ResponseWriter: rw} if err == nil { response.IsApplicationError = hadError response.Body = result if namer, ok := appErr.(yarpcErrorNamer); ok { response.ApplicationErrorName = namer.YARPCErrorName() } if extractor, ok := appErr.(yarpcErrorCoder); ok { response.ApplicationErrorCode = extractor.YARPCErrorCode() } if appErr != nil { response.ApplicationErrorDetails = appErr.Error() } } return response, err } ================================================ FILE: .gen/go/matching/matchingservicetest/client.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package matchingservicetest import ( context "context" gomock "github.com/golang/mock/gomock" yarpc "go.uber.org/yarpc" matching "github.com/uber/cadence/.gen/go/matching" matchingserviceclient "github.com/uber/cadence/.gen/go/matching/matchingserviceclient" shared "github.com/uber/cadence/.gen/go/shared" ) // MockClient implements a gomock-compatible mock client for service // MatchingService. type MockClient struct { ctrl *gomock.Controller recorder *_MockClientRecorder } var _ matchingserviceclient.Interface = (*MockClient)(nil) type _MockClientRecorder struct { mock *MockClient } // Build a new mock client for service MatchingService. // // mockCtrl := gomock.NewController(t) // client := matchingservicetest.NewMockClient(mockCtrl) // // Use EXPECT() to set expectations on the mock. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &_MockClientRecorder{mock} return mock } // EXPECT returns an object that allows you to define an expectation on the // MatchingService mock client. func (m *MockClient) EXPECT() *_MockClientRecorder { return m.recorder } // AddActivityTask responds to a AddActivityTask call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().AddActivityTask(gomock.Any(), ...).Return(...) // ... := client.AddActivityTask(...) func (m *MockClient) AddActivityTask( ctx context.Context, _AddRequest *matching.AddActivityTaskRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _AddRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "AddActivityTask", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) AddActivityTask( ctx interface{}, _AddRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _AddRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "AddActivityTask", args...) } // AddDecisionTask responds to a AddDecisionTask call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().AddDecisionTask(gomock.Any(), ...).Return(...) // ... := client.AddDecisionTask(...) func (m *MockClient) AddDecisionTask( ctx context.Context, _AddRequest *matching.AddDecisionTaskRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _AddRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "AddDecisionTask", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) AddDecisionTask( ctx interface{}, _AddRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _AddRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "AddDecisionTask", args...) } // CancelOutstandingPoll responds to a CancelOutstandingPoll call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().CancelOutstandingPoll(gomock.Any(), ...).Return(...) // ... := client.CancelOutstandingPoll(...) func (m *MockClient) CancelOutstandingPoll( ctx context.Context, _Request *matching.CancelOutstandingPollRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "CancelOutstandingPoll", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) CancelOutstandingPoll( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "CancelOutstandingPoll", args...) } // DescribeTaskList responds to a DescribeTaskList call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().DescribeTaskList(gomock.Any(), ...).Return(...) // ... := client.DescribeTaskList(...) func (m *MockClient) DescribeTaskList( ctx context.Context, _Request *matching.DescribeTaskListRequest, opts ...yarpc.CallOption, ) (success *shared.DescribeTaskListResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "DescribeTaskList", args...) success, _ = ret[i].(*shared.DescribeTaskListResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) DescribeTaskList( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "DescribeTaskList", args...) } // GetTaskListsByDomain responds to a GetTaskListsByDomain call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().GetTaskListsByDomain(gomock.Any(), ...).Return(...) // ... := client.GetTaskListsByDomain(...) func (m *MockClient) GetTaskListsByDomain( ctx context.Context, _Request *shared.GetTaskListsByDomainRequest, opts ...yarpc.CallOption, ) (success *shared.GetTaskListsByDomainResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "GetTaskListsByDomain", args...) success, _ = ret[i].(*shared.GetTaskListsByDomainResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) GetTaskListsByDomain( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "GetTaskListsByDomain", args...) } // ListTaskListPartitions responds to a ListTaskListPartitions call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().ListTaskListPartitions(gomock.Any(), ...).Return(...) // ... := client.ListTaskListPartitions(...) func (m *MockClient) ListTaskListPartitions( ctx context.Context, _Request *matching.ListTaskListPartitionsRequest, opts ...yarpc.CallOption, ) (success *shared.ListTaskListPartitionsResponse, err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "ListTaskListPartitions", args...) success, _ = ret[i].(*shared.ListTaskListPartitionsResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) ListTaskListPartitions( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "ListTaskListPartitions", args...) } // PollForActivityTask responds to a PollForActivityTask call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().PollForActivityTask(gomock.Any(), ...).Return(...) // ... := client.PollForActivityTask(...) func (m *MockClient) PollForActivityTask( ctx context.Context, _PollRequest *matching.PollForActivityTaskRequest, opts ...yarpc.CallOption, ) (success *shared.PollForActivityTaskResponse, err error) { args := []interface{}{ctx, _PollRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "PollForActivityTask", args...) success, _ = ret[i].(*shared.PollForActivityTaskResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) PollForActivityTask( ctx interface{}, _PollRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _PollRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "PollForActivityTask", args...) } // PollForDecisionTask responds to a PollForDecisionTask call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().PollForDecisionTask(gomock.Any(), ...).Return(...) // ... := client.PollForDecisionTask(...) func (m *MockClient) PollForDecisionTask( ctx context.Context, _PollRequest *matching.PollForDecisionTaskRequest, opts ...yarpc.CallOption, ) (success *matching.PollForDecisionTaskResponse, err error) { args := []interface{}{ctx, _PollRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "PollForDecisionTask", args...) success, _ = ret[i].(*matching.PollForDecisionTaskResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) PollForDecisionTask( ctx interface{}, _PollRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _PollRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "PollForDecisionTask", args...) } // QueryWorkflow responds to a QueryWorkflow call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().QueryWorkflow(gomock.Any(), ...).Return(...) // ... := client.QueryWorkflow(...) func (m *MockClient) QueryWorkflow( ctx context.Context, _QueryRequest *matching.QueryWorkflowRequest, opts ...yarpc.CallOption, ) (success *shared.QueryWorkflowResponse, err error) { args := []interface{}{ctx, _QueryRequest} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "QueryWorkflow", args...) success, _ = ret[i].(*shared.QueryWorkflowResponse) i++ err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) QueryWorkflow( ctx interface{}, _QueryRequest interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _QueryRequest}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "QueryWorkflow", args...) } // RespondQueryTaskCompleted responds to a RespondQueryTaskCompleted call based on the mock expectations. This // call will fail if the mock does not expect this call. Use EXPECT to expect // a call to this function. // // client.EXPECT().RespondQueryTaskCompleted(gomock.Any(), ...).Return(...) // ... := client.RespondQueryTaskCompleted(...) func (m *MockClient) RespondQueryTaskCompleted( ctx context.Context, _Request *matching.RespondQueryTaskCompletedRequest, opts ...yarpc.CallOption, ) (err error) { args := []interface{}{ctx, _Request} for _, o := range opts { args = append(args, o) } i := 0 ret := m.ctrl.Call(m, "RespondQueryTaskCompleted", args...) err, _ = ret[i].(error) return } func (mr *_MockClientRecorder) RespondQueryTaskCompleted( ctx interface{}, _Request interface{}, opts ...interface{}, ) *gomock.Call { args := append([]interface{}{ctx, _Request}, opts...) return mr.mock.ctrl.RecordCall(mr.mock, "RespondQueryTaskCompleted", args...) } ================================================ FILE: .gen/go/matching/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package matching ================================================ FILE: .gen/go/replicator/replicator.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package replicator import ( bytes "bytes" base64 "encoding/base64" json "encoding/json" fmt "fmt" math "math" strconv "strconv" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" shared "github.com/uber/cadence/.gen/go/shared" ) type DLQType int32 const ( DLQTypeReplication DLQType = 0 DLQTypeDomain DLQType = 1 ) // DLQType_Values returns all recognized values of DLQType. func DLQType_Values() []DLQType { return []DLQType{ DLQTypeReplication, DLQTypeDomain, } } // UnmarshalText tries to decode DLQType from a byte slice // containing its name. // // var v DLQType // err := v.UnmarshalText([]byte("Replication")) func (v *DLQType) UnmarshalText(value []byte) error { switch s := string(value); s { case "Replication": *v = DLQTypeReplication return nil case "Domain": *v = DLQTypeDomain return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DLQType", err) } *v = DLQType(val) return nil } } // MarshalText encodes DLQType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v DLQType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("Replication"), nil case 1: return []byte("Domain"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DLQType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v DLQType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "Replication") case 1: enc.AddString("name", "Domain") } return nil } // Ptr returns a pointer to this enum value. func (v DLQType) Ptr() *DLQType { return &v } // Encode encodes DLQType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v DLQType // return v.Encode(sWriter) func (v DLQType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates DLQType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v DLQType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes DLQType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return DLQType(0), err // } // // var v DLQType // if err := v.FromWire(x); err != nil { // return DLQType(0), err // } // return v, nil func (v *DLQType) FromWire(w wire.Value) error { *v = (DLQType)(w.GetI32()) return nil } // Decode reads off the encoded DLQType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v DLQType // if err := v.Decode(sReader); err != nil { // return DLQType(0), err // } // return v, nil func (v *DLQType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (DLQType)(i) return nil } // String returns a readable string representation of DLQType. func (v DLQType) String() string { w := int32(v) switch w { case 0: return "Replication" case 1: return "Domain" } return fmt.Sprintf("DLQType(%d)", w) } // Equals returns true if this DLQType value matches the provided // value. func (v DLQType) Equals(rhs DLQType) bool { return v == rhs } // MarshalJSON serializes DLQType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v DLQType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"Replication\""), nil case 1: return ([]byte)("\"Domain\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode DLQType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *DLQType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "DLQType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "DLQType") } *v = (DLQType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "DLQType") } } type DomainOperation int32 const ( DomainOperationCreate DomainOperation = 0 DomainOperationUpdate DomainOperation = 1 DomainOperationDelete DomainOperation = 2 ) // DomainOperation_Values returns all recognized values of DomainOperation. func DomainOperation_Values() []DomainOperation { return []DomainOperation{ DomainOperationCreate, DomainOperationUpdate, DomainOperationDelete, } } // UnmarshalText tries to decode DomainOperation from a byte slice // containing its name. // // var v DomainOperation // err := v.UnmarshalText([]byte("Create")) func (v *DomainOperation) UnmarshalText(value []byte) error { switch s := string(value); s { case "Create": *v = DomainOperationCreate return nil case "Update": *v = DomainOperationUpdate return nil case "Delete": *v = DomainOperationDelete return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DomainOperation", err) } *v = DomainOperation(val) return nil } } // MarshalText encodes DomainOperation to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v DomainOperation) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("Create"), nil case 1: return []byte("Update"), nil case 2: return []byte("Delete"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainOperation. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v DomainOperation) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "Create") case 1: enc.AddString("name", "Update") case 2: enc.AddString("name", "Delete") } return nil } // Ptr returns a pointer to this enum value. func (v DomainOperation) Ptr() *DomainOperation { return &v } // Encode encodes DomainOperation directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v DomainOperation // return v.Encode(sWriter) func (v DomainOperation) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates DomainOperation into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v DomainOperation) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes DomainOperation from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return DomainOperation(0), err // } // // var v DomainOperation // if err := v.FromWire(x); err != nil { // return DomainOperation(0), err // } // return v, nil func (v *DomainOperation) FromWire(w wire.Value) error { *v = (DomainOperation)(w.GetI32()) return nil } // Decode reads off the encoded DomainOperation directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v DomainOperation // if err := v.Decode(sReader); err != nil { // return DomainOperation(0), err // } // return v, nil func (v *DomainOperation) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (DomainOperation)(i) return nil } // String returns a readable string representation of DomainOperation. func (v DomainOperation) String() string { w := int32(v) switch w { case 0: return "Create" case 1: return "Update" case 2: return "Delete" } return fmt.Sprintf("DomainOperation(%d)", w) } // Equals returns true if this DomainOperation value matches the provided // value. func (v DomainOperation) Equals(rhs DomainOperation) bool { return v == rhs } // MarshalJSON serializes DomainOperation into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v DomainOperation) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"Create\""), nil case 1: return ([]byte)("\"Update\""), nil case 2: return ([]byte)("\"Delete\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode DomainOperation from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *DomainOperation) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "DomainOperation") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "DomainOperation") } *v = (DomainOperation)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "DomainOperation") } } type DomainTaskAttributes struct { DomainOperation *DomainOperation `json:"domainOperation,omitempty"` ID *string `json:"id,omitempty"` Info *shared.DomainInfo `json:"info,omitempty"` Config *shared.DomainConfiguration `json:"config,omitempty"` ReplicationConfig *shared.DomainReplicationConfiguration `json:"replicationConfig,omitempty"` ConfigVersion *int64 `json:"configVersion,omitempty"` FailoverVersion *int64 `json:"failoverVersion,omitempty"` PreviousFailoverVersion *int64 `json:"previousFailoverVersion,omitempty"` } // ToWire translates a DomainTaskAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DomainTaskAttributes) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.DomainOperation != nil { w, err = v.DomainOperation.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ID != nil { w, err = wire.NewValueString(*(v.ID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Info != nil { w, err = v.Info.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Config != nil { w, err = v.Config.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ReplicationConfig != nil { w, err = v.ReplicationConfig.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ConfigVersion != nil { w, err = wire.NewValueI64(*(v.ConfigVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.FailoverVersion != nil { w, err = wire.NewValueI64(*(v.FailoverVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.PreviousFailoverVersion != nil { w, err = wire.NewValueI64(*(v.PreviousFailoverVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DomainOperation_Read(w wire.Value) (DomainOperation, error) { var v DomainOperation err := v.FromWire(w) return v, err } func _DomainInfo_Read(w wire.Value) (*shared.DomainInfo, error) { var v shared.DomainInfo err := v.FromWire(w) return &v, err } func _DomainConfiguration_Read(w wire.Value) (*shared.DomainConfiguration, error) { var v shared.DomainConfiguration err := v.FromWire(w) return &v, err } func _DomainReplicationConfiguration_Read(w wire.Value) (*shared.DomainReplicationConfiguration, error) { var v shared.DomainReplicationConfiguration err := v.FromWire(w) return &v, err } // FromWire deserializes a DomainTaskAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DomainTaskAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DomainTaskAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DomainTaskAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 5: if field.Value.Type() == wire.TI32 { var x DomainOperation x, err = _DomainOperation_Read(field.Value) v.DomainOperation = &x if err != nil { return err } } case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Info, err = _DomainInfo_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.Config, err = _DomainConfiguration_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.ReplicationConfig, err = _DomainReplicationConfiguration_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ConfigVersion = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverVersion = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.PreviousFailoverVersion = &x if err != nil { return err } } } } return nil } // Encode serializes a DomainTaskAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DomainTaskAttributes struct could not be encoded. func (v *DomainTaskAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainOperation != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TI32}); err != nil { return err } if err := v.DomainOperation.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Info != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Info.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Config != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.Config.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReplicationConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.ReplicationConfig.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ConfigVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ConfigVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PreviousFailoverVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.PreviousFailoverVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DomainOperation_Decode(sr stream.Reader) (DomainOperation, error) { var v DomainOperation err := v.Decode(sr) return v, err } func _DomainInfo_Decode(sr stream.Reader) (*shared.DomainInfo, error) { var v shared.DomainInfo err := v.Decode(sr) return &v, err } func _DomainConfiguration_Decode(sr stream.Reader) (*shared.DomainConfiguration, error) { var v shared.DomainConfiguration err := v.Decode(sr) return &v, err } func _DomainReplicationConfiguration_Decode(sr stream.Reader) (*shared.DomainReplicationConfiguration, error) { var v shared.DomainReplicationConfiguration err := v.Decode(sr) return &v, err } // Decode deserializes a DomainTaskAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DomainTaskAttributes struct could not be generated from the wire // representation. func (v *DomainTaskAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 5 && fh.Type == wire.TI32: var x DomainOperation x, err = _DomainOperation_Decode(sr) v.DomainOperation = &x if err != nil { return err } case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Info, err = _DomainInfo_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.Config, err = _DomainConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.ReplicationConfig, err = _DomainReplicationConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ConfigVersion = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverVersion = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.PreviousFailoverVersion = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DomainTaskAttributes // struct. func (v *DomainTaskAttributes) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.DomainOperation != nil { fields[i] = fmt.Sprintf("DomainOperation: %v", *(v.DomainOperation)) i++ } if v.ID != nil { fields[i] = fmt.Sprintf("ID: %v", *(v.ID)) i++ } if v.Info != nil { fields[i] = fmt.Sprintf("Info: %v", v.Info) i++ } if v.Config != nil { fields[i] = fmt.Sprintf("Config: %v", v.Config) i++ } if v.ReplicationConfig != nil { fields[i] = fmt.Sprintf("ReplicationConfig: %v", v.ReplicationConfig) i++ } if v.ConfigVersion != nil { fields[i] = fmt.Sprintf("ConfigVersion: %v", *(v.ConfigVersion)) i++ } if v.FailoverVersion != nil { fields[i] = fmt.Sprintf("FailoverVersion: %v", *(v.FailoverVersion)) i++ } if v.PreviousFailoverVersion != nil { fields[i] = fmt.Sprintf("PreviousFailoverVersion: %v", *(v.PreviousFailoverVersion)) i++ } return fmt.Sprintf("DomainTaskAttributes{%v}", strings.Join(fields[:i], ", ")) } func _DomainOperation_EqualsPtr(lhs, rhs *DomainOperation) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _I64_EqualsPtr(lhs, rhs *int64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this DomainTaskAttributes match the // provided DomainTaskAttributes. // // This function performs a deep comparison. func (v *DomainTaskAttributes) Equals(rhs *DomainTaskAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_DomainOperation_EqualsPtr(v.DomainOperation, rhs.DomainOperation) { return false } if !_String_EqualsPtr(v.ID, rhs.ID) { return false } if !((v.Info == nil && rhs.Info == nil) || (v.Info != nil && rhs.Info != nil && v.Info.Equals(rhs.Info))) { return false } if !((v.Config == nil && rhs.Config == nil) || (v.Config != nil && rhs.Config != nil && v.Config.Equals(rhs.Config))) { return false } if !((v.ReplicationConfig == nil && rhs.ReplicationConfig == nil) || (v.ReplicationConfig != nil && rhs.ReplicationConfig != nil && v.ReplicationConfig.Equals(rhs.ReplicationConfig))) { return false } if !_I64_EqualsPtr(v.ConfigVersion, rhs.ConfigVersion) { return false } if !_I64_EqualsPtr(v.FailoverVersion, rhs.FailoverVersion) { return false } if !_I64_EqualsPtr(v.PreviousFailoverVersion, rhs.PreviousFailoverVersion) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainTaskAttributes. func (v *DomainTaskAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainOperation != nil { err = multierr.Append(err, enc.AddObject("domainOperation", *v.DomainOperation)) } if v.ID != nil { enc.AddString("id", *v.ID) } if v.Info != nil { err = multierr.Append(err, enc.AddObject("info", v.Info)) } if v.Config != nil { err = multierr.Append(err, enc.AddObject("config", v.Config)) } if v.ReplicationConfig != nil { err = multierr.Append(err, enc.AddObject("replicationConfig", v.ReplicationConfig)) } if v.ConfigVersion != nil { enc.AddInt64("configVersion", *v.ConfigVersion) } if v.FailoverVersion != nil { enc.AddInt64("failoverVersion", *v.FailoverVersion) } if v.PreviousFailoverVersion != nil { enc.AddInt64("previousFailoverVersion", *v.PreviousFailoverVersion) } return err } // GetDomainOperation returns the value of DomainOperation if it is set or its // zero value if it is unset. func (v *DomainTaskAttributes) GetDomainOperation() (o DomainOperation) { if v != nil && v.DomainOperation != nil { return *v.DomainOperation } return } // IsSetDomainOperation returns true if DomainOperation is not nil. func (v *DomainTaskAttributes) IsSetDomainOperation() bool { return v != nil && v.DomainOperation != nil } // GetID returns the value of ID if it is set or its // zero value if it is unset. func (v *DomainTaskAttributes) GetID() (o string) { if v != nil && v.ID != nil { return *v.ID } return } // IsSetID returns true if ID is not nil. func (v *DomainTaskAttributes) IsSetID() bool { return v != nil && v.ID != nil } // GetInfo returns the value of Info if it is set or its // zero value if it is unset. func (v *DomainTaskAttributes) GetInfo() (o *shared.DomainInfo) { if v != nil && v.Info != nil { return v.Info } return } // IsSetInfo returns true if Info is not nil. func (v *DomainTaskAttributes) IsSetInfo() bool { return v != nil && v.Info != nil } // GetConfig returns the value of Config if it is set or its // zero value if it is unset. func (v *DomainTaskAttributes) GetConfig() (o *shared.DomainConfiguration) { if v != nil && v.Config != nil { return v.Config } return } // IsSetConfig returns true if Config is not nil. func (v *DomainTaskAttributes) IsSetConfig() bool { return v != nil && v.Config != nil } // GetReplicationConfig returns the value of ReplicationConfig if it is set or its // zero value if it is unset. func (v *DomainTaskAttributes) GetReplicationConfig() (o *shared.DomainReplicationConfiguration) { if v != nil && v.ReplicationConfig != nil { return v.ReplicationConfig } return } // IsSetReplicationConfig returns true if ReplicationConfig is not nil. func (v *DomainTaskAttributes) IsSetReplicationConfig() bool { return v != nil && v.ReplicationConfig != nil } // GetConfigVersion returns the value of ConfigVersion if it is set or its // zero value if it is unset. func (v *DomainTaskAttributes) GetConfigVersion() (o int64) { if v != nil && v.ConfigVersion != nil { return *v.ConfigVersion } return } // IsSetConfigVersion returns true if ConfigVersion is not nil. func (v *DomainTaskAttributes) IsSetConfigVersion() bool { return v != nil && v.ConfigVersion != nil } // GetFailoverVersion returns the value of FailoverVersion if it is set or its // zero value if it is unset. func (v *DomainTaskAttributes) GetFailoverVersion() (o int64) { if v != nil && v.FailoverVersion != nil { return *v.FailoverVersion } return } // IsSetFailoverVersion returns true if FailoverVersion is not nil. func (v *DomainTaskAttributes) IsSetFailoverVersion() bool { return v != nil && v.FailoverVersion != nil } // GetPreviousFailoverVersion returns the value of PreviousFailoverVersion if it is set or its // zero value if it is unset. func (v *DomainTaskAttributes) GetPreviousFailoverVersion() (o int64) { if v != nil && v.PreviousFailoverVersion != nil { return *v.PreviousFailoverVersion } return } // IsSetPreviousFailoverVersion returns true if PreviousFailoverVersion is not nil. func (v *DomainTaskAttributes) IsSetPreviousFailoverVersion() bool { return v != nil && v.PreviousFailoverVersion != nil } type FailoverMarkerAttributes struct { DomainID *string `json:"domainID,omitempty"` FailoverVersion *int64 `json:"failoverVersion,omitempty"` CreationTime *int64 `json:"creationTime,omitempty"` } // ToWire translates a FailoverMarkerAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *FailoverMarkerAttributes) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.DomainID != nil { w, err = wire.NewValueString(*(v.DomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.FailoverVersion != nil { w, err = wire.NewValueI64(*(v.FailoverVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.CreationTime != nil { w, err = wire.NewValueI64(*(v.CreationTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a FailoverMarkerAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a FailoverMarkerAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v FailoverMarkerAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *FailoverMarkerAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverVersion = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CreationTime = &x if err != nil { return err } } } } return nil } // Encode serializes a FailoverMarkerAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a FailoverMarkerAttributes struct could not be encoded. func (v *FailoverMarkerAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CreationTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CreationTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a FailoverMarkerAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a FailoverMarkerAttributes struct could not be generated from the wire // representation. func (v *FailoverMarkerAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverVersion = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CreationTime = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a FailoverMarkerAttributes // struct. func (v *FailoverMarkerAttributes) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", *(v.DomainID)) i++ } if v.FailoverVersion != nil { fields[i] = fmt.Sprintf("FailoverVersion: %v", *(v.FailoverVersion)) i++ } if v.CreationTime != nil { fields[i] = fmt.Sprintf("CreationTime: %v", *(v.CreationTime)) i++ } return fmt.Sprintf("FailoverMarkerAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this FailoverMarkerAttributes match the // provided FailoverMarkerAttributes. // // This function performs a deep comparison. func (v *FailoverMarkerAttributes) Equals(rhs *FailoverMarkerAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainID, rhs.DomainID) { return false } if !_I64_EqualsPtr(v.FailoverVersion, rhs.FailoverVersion) { return false } if !_I64_EqualsPtr(v.CreationTime, rhs.CreationTime) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FailoverMarkerAttributes. func (v *FailoverMarkerAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainID != nil { enc.AddString("domainID", *v.DomainID) } if v.FailoverVersion != nil { enc.AddInt64("failoverVersion", *v.FailoverVersion) } if v.CreationTime != nil { enc.AddInt64("creationTime", *v.CreationTime) } return err } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *FailoverMarkerAttributes) GetDomainID() (o string) { if v != nil && v.DomainID != nil { return *v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *FailoverMarkerAttributes) IsSetDomainID() bool { return v != nil && v.DomainID != nil } // GetFailoverVersion returns the value of FailoverVersion if it is set or its // zero value if it is unset. func (v *FailoverMarkerAttributes) GetFailoverVersion() (o int64) { if v != nil && v.FailoverVersion != nil { return *v.FailoverVersion } return } // IsSetFailoverVersion returns true if FailoverVersion is not nil. func (v *FailoverMarkerAttributes) IsSetFailoverVersion() bool { return v != nil && v.FailoverVersion != nil } // GetCreationTime returns the value of CreationTime if it is set or its // zero value if it is unset. func (v *FailoverMarkerAttributes) GetCreationTime() (o int64) { if v != nil && v.CreationTime != nil { return *v.CreationTime } return } // IsSetCreationTime returns true if CreationTime is not nil. func (v *FailoverMarkerAttributes) IsSetCreationTime() bool { return v != nil && v.CreationTime != nil } type FailoverMarkers struct { FailoverMarkers []*FailoverMarkerAttributes `json:"failoverMarkers,omitempty"` } type _List_FailoverMarkerAttributes_ValueList []*FailoverMarkerAttributes func (v _List_FailoverMarkerAttributes_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*FailoverMarkerAttributes', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_FailoverMarkerAttributes_ValueList) Size() int { return len(v) } func (_List_FailoverMarkerAttributes_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_FailoverMarkerAttributes_ValueList) Close() {} // ToWire translates a FailoverMarkers struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *FailoverMarkers) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.FailoverMarkers != nil { w, err = wire.NewValueList(_List_FailoverMarkerAttributes_ValueList(v.FailoverMarkers)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _FailoverMarkerAttributes_Read(w wire.Value) (*FailoverMarkerAttributes, error) { var v FailoverMarkerAttributes err := v.FromWire(w) return &v, err } func _List_FailoverMarkerAttributes_Read(l wire.ValueList) ([]*FailoverMarkerAttributes, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*FailoverMarkerAttributes, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _FailoverMarkerAttributes_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a FailoverMarkers struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a FailoverMarkers struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v FailoverMarkers // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *FailoverMarkers) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.FailoverMarkers, err = _List_FailoverMarkerAttributes_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_FailoverMarkerAttributes_Encode(val []*FailoverMarkerAttributes, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*FailoverMarkerAttributes', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a FailoverMarkers struct directly into bytes, without going // through an intermediary type. // // An error is returned if a FailoverMarkers struct could not be encoded. func (v *FailoverMarkers) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FailoverMarkers != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_FailoverMarkerAttributes_Encode(v.FailoverMarkers, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _FailoverMarkerAttributes_Decode(sr stream.Reader) (*FailoverMarkerAttributes, error) { var v FailoverMarkerAttributes err := v.Decode(sr) return &v, err } func _List_FailoverMarkerAttributes_Decode(sr stream.Reader) ([]*FailoverMarkerAttributes, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*FailoverMarkerAttributes, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _FailoverMarkerAttributes_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a FailoverMarkers struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a FailoverMarkers struct could not be generated from the wire // representation. func (v *FailoverMarkers) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.FailoverMarkers, err = _List_FailoverMarkerAttributes_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a FailoverMarkers // struct. func (v *FailoverMarkers) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.FailoverMarkers != nil { fields[i] = fmt.Sprintf("FailoverMarkers: %v", v.FailoverMarkers) i++ } return fmt.Sprintf("FailoverMarkers{%v}", strings.Join(fields[:i], ", ")) } func _List_FailoverMarkerAttributes_Equals(lhs, rhs []*FailoverMarkerAttributes) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this FailoverMarkers match the // provided FailoverMarkers. // // This function performs a deep comparison. func (v *FailoverMarkers) Equals(rhs *FailoverMarkers) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.FailoverMarkers == nil && rhs.FailoverMarkers == nil) || (v.FailoverMarkers != nil && rhs.FailoverMarkers != nil && _List_FailoverMarkerAttributes_Equals(v.FailoverMarkers, rhs.FailoverMarkers))) { return false } return true } type _List_FailoverMarkerAttributes_Zapper []*FailoverMarkerAttributes // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_FailoverMarkerAttributes_Zapper. func (l _List_FailoverMarkerAttributes_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FailoverMarkers. func (v *FailoverMarkers) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FailoverMarkers != nil { err = multierr.Append(err, enc.AddArray("failoverMarkers", (_List_FailoverMarkerAttributes_Zapper)(v.FailoverMarkers))) } return err } // GetFailoverMarkers returns the value of FailoverMarkers if it is set or its // zero value if it is unset. func (v *FailoverMarkers) GetFailoverMarkers() (o []*FailoverMarkerAttributes) { if v != nil && v.FailoverMarkers != nil { return v.FailoverMarkers } return } // IsSetFailoverMarkers returns true if FailoverMarkers is not nil. func (v *FailoverMarkers) IsSetFailoverMarkers() bool { return v != nil && v.FailoverMarkers != nil } type GetDLQReplicationMessagesRequest struct { TaskInfos []*ReplicationTaskInfo `json:"taskInfos,omitempty"` } type _List_ReplicationTaskInfo_ValueList []*ReplicationTaskInfo func (v _List_ReplicationTaskInfo_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*ReplicationTaskInfo', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_ReplicationTaskInfo_ValueList) Size() int { return len(v) } func (_List_ReplicationTaskInfo_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_ReplicationTaskInfo_ValueList) Close() {} // ToWire translates a GetDLQReplicationMessagesRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetDLQReplicationMessagesRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.TaskInfos != nil { w, err = wire.NewValueList(_List_ReplicationTaskInfo_ValueList(v.TaskInfos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReplicationTaskInfo_Read(w wire.Value) (*ReplicationTaskInfo, error) { var v ReplicationTaskInfo err := v.FromWire(w) return &v, err } func _List_ReplicationTaskInfo_Read(l wire.ValueList) ([]*ReplicationTaskInfo, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*ReplicationTaskInfo, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _ReplicationTaskInfo_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a GetDLQReplicationMessagesRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetDLQReplicationMessagesRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetDLQReplicationMessagesRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetDLQReplicationMessagesRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.TaskInfos, err = _List_ReplicationTaskInfo_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_ReplicationTaskInfo_Encode(val []*ReplicationTaskInfo, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*ReplicationTaskInfo', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a GetDLQReplicationMessagesRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetDLQReplicationMessagesRequest struct could not be encoded. func (v *GetDLQReplicationMessagesRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskInfos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_ReplicationTaskInfo_Encode(v.TaskInfos, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReplicationTaskInfo_Decode(sr stream.Reader) (*ReplicationTaskInfo, error) { var v ReplicationTaskInfo err := v.Decode(sr) return &v, err } func _List_ReplicationTaskInfo_Decode(sr stream.Reader) ([]*ReplicationTaskInfo, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*ReplicationTaskInfo, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _ReplicationTaskInfo_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a GetDLQReplicationMessagesRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetDLQReplicationMessagesRequest struct could not be generated from the wire // representation. func (v *GetDLQReplicationMessagesRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.TaskInfos, err = _List_ReplicationTaskInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetDLQReplicationMessagesRequest // struct. func (v *GetDLQReplicationMessagesRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.TaskInfos != nil { fields[i] = fmt.Sprintf("TaskInfos: %v", v.TaskInfos) i++ } return fmt.Sprintf("GetDLQReplicationMessagesRequest{%v}", strings.Join(fields[:i], ", ")) } func _List_ReplicationTaskInfo_Equals(lhs, rhs []*ReplicationTaskInfo) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this GetDLQReplicationMessagesRequest match the // provided GetDLQReplicationMessagesRequest. // // This function performs a deep comparison. func (v *GetDLQReplicationMessagesRequest) Equals(rhs *GetDLQReplicationMessagesRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskInfos == nil && rhs.TaskInfos == nil) || (v.TaskInfos != nil && rhs.TaskInfos != nil && _List_ReplicationTaskInfo_Equals(v.TaskInfos, rhs.TaskInfos))) { return false } return true } type _List_ReplicationTaskInfo_Zapper []*ReplicationTaskInfo // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_ReplicationTaskInfo_Zapper. func (l _List_ReplicationTaskInfo_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetDLQReplicationMessagesRequest. func (v *GetDLQReplicationMessagesRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskInfos != nil { err = multierr.Append(err, enc.AddArray("taskInfos", (_List_ReplicationTaskInfo_Zapper)(v.TaskInfos))) } return err } // GetTaskInfos returns the value of TaskInfos if it is set or its // zero value if it is unset. func (v *GetDLQReplicationMessagesRequest) GetTaskInfos() (o []*ReplicationTaskInfo) { if v != nil && v.TaskInfos != nil { return v.TaskInfos } return } // IsSetTaskInfos returns true if TaskInfos is not nil. func (v *GetDLQReplicationMessagesRequest) IsSetTaskInfos() bool { return v != nil && v.TaskInfos != nil } type GetDLQReplicationMessagesResponse struct { ReplicationTasks []*ReplicationTask `json:"replicationTasks,omitempty"` } type _List_ReplicationTask_ValueList []*ReplicationTask func (v _List_ReplicationTask_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*ReplicationTask', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_ReplicationTask_ValueList) Size() int { return len(v) } func (_List_ReplicationTask_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_ReplicationTask_ValueList) Close() {} // ToWire translates a GetDLQReplicationMessagesResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetDLQReplicationMessagesResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ReplicationTasks != nil { w, err = wire.NewValueList(_List_ReplicationTask_ValueList(v.ReplicationTasks)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReplicationTask_Read(w wire.Value) (*ReplicationTask, error) { var v ReplicationTask err := v.FromWire(w) return &v, err } func _List_ReplicationTask_Read(l wire.ValueList) ([]*ReplicationTask, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*ReplicationTask, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _ReplicationTask_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a GetDLQReplicationMessagesResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetDLQReplicationMessagesResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetDLQReplicationMessagesResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetDLQReplicationMessagesResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.ReplicationTasks, err = _List_ReplicationTask_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_ReplicationTask_Encode(val []*ReplicationTask, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*ReplicationTask', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a GetDLQReplicationMessagesResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetDLQReplicationMessagesResponse struct could not be encoded. func (v *GetDLQReplicationMessagesResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ReplicationTasks != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_ReplicationTask_Encode(v.ReplicationTasks, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReplicationTask_Decode(sr stream.Reader) (*ReplicationTask, error) { var v ReplicationTask err := v.Decode(sr) return &v, err } func _List_ReplicationTask_Decode(sr stream.Reader) ([]*ReplicationTask, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*ReplicationTask, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _ReplicationTask_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a GetDLQReplicationMessagesResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetDLQReplicationMessagesResponse struct could not be generated from the wire // representation. func (v *GetDLQReplicationMessagesResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.ReplicationTasks, err = _List_ReplicationTask_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetDLQReplicationMessagesResponse // struct. func (v *GetDLQReplicationMessagesResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ReplicationTasks != nil { fields[i] = fmt.Sprintf("ReplicationTasks: %v", v.ReplicationTasks) i++ } return fmt.Sprintf("GetDLQReplicationMessagesResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_ReplicationTask_Equals(lhs, rhs []*ReplicationTask) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this GetDLQReplicationMessagesResponse match the // provided GetDLQReplicationMessagesResponse. // // This function performs a deep comparison. func (v *GetDLQReplicationMessagesResponse) Equals(rhs *GetDLQReplicationMessagesResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ReplicationTasks == nil && rhs.ReplicationTasks == nil) || (v.ReplicationTasks != nil && rhs.ReplicationTasks != nil && _List_ReplicationTask_Equals(v.ReplicationTasks, rhs.ReplicationTasks))) { return false } return true } type _List_ReplicationTask_Zapper []*ReplicationTask // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_ReplicationTask_Zapper. func (l _List_ReplicationTask_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetDLQReplicationMessagesResponse. func (v *GetDLQReplicationMessagesResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ReplicationTasks != nil { err = multierr.Append(err, enc.AddArray("replicationTasks", (_List_ReplicationTask_Zapper)(v.ReplicationTasks))) } return err } // GetReplicationTasks returns the value of ReplicationTasks if it is set or its // zero value if it is unset. func (v *GetDLQReplicationMessagesResponse) GetReplicationTasks() (o []*ReplicationTask) { if v != nil && v.ReplicationTasks != nil { return v.ReplicationTasks } return } // IsSetReplicationTasks returns true if ReplicationTasks is not nil. func (v *GetDLQReplicationMessagesResponse) IsSetReplicationTasks() bool { return v != nil && v.ReplicationTasks != nil } type GetDomainReplicationMessagesRequest struct { LastRetrievedMessageId *int64 `json:"lastRetrievedMessageId,omitempty"` LastProcessedMessageId *int64 `json:"lastProcessedMessageId,omitempty"` ClusterName *string `json:"clusterName,omitempty"` } // ToWire translates a GetDomainReplicationMessagesRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetDomainReplicationMessagesRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.LastRetrievedMessageId != nil { w, err = wire.NewValueI64(*(v.LastRetrievedMessageId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.LastProcessedMessageId != nil { w, err = wire.NewValueI64(*(v.LastProcessedMessageId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ClusterName != nil { w, err = wire.NewValueString(*(v.ClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a GetDomainReplicationMessagesRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetDomainReplicationMessagesRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetDomainReplicationMessagesRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetDomainReplicationMessagesRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastRetrievedMessageId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastProcessedMessageId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClusterName = &x if err != nil { return err } } } } return nil } // Encode serializes a GetDomainReplicationMessagesRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetDomainReplicationMessagesRequest struct could not be encoded. func (v *GetDomainReplicationMessagesRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.LastRetrievedMessageId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastRetrievedMessageId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastProcessedMessageId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastProcessedMessageId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a GetDomainReplicationMessagesRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetDomainReplicationMessagesRequest struct could not be generated from the wire // representation. func (v *GetDomainReplicationMessagesRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastRetrievedMessageId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastProcessedMessageId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClusterName = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetDomainReplicationMessagesRequest // struct. func (v *GetDomainReplicationMessagesRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.LastRetrievedMessageId != nil { fields[i] = fmt.Sprintf("LastRetrievedMessageId: %v", *(v.LastRetrievedMessageId)) i++ } if v.LastProcessedMessageId != nil { fields[i] = fmt.Sprintf("LastProcessedMessageId: %v", *(v.LastProcessedMessageId)) i++ } if v.ClusterName != nil { fields[i] = fmt.Sprintf("ClusterName: %v", *(v.ClusterName)) i++ } return fmt.Sprintf("GetDomainReplicationMessagesRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetDomainReplicationMessagesRequest match the // provided GetDomainReplicationMessagesRequest. // // This function performs a deep comparison. func (v *GetDomainReplicationMessagesRequest) Equals(rhs *GetDomainReplicationMessagesRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.LastRetrievedMessageId, rhs.LastRetrievedMessageId) { return false } if !_I64_EqualsPtr(v.LastProcessedMessageId, rhs.LastProcessedMessageId) { return false } if !_String_EqualsPtr(v.ClusterName, rhs.ClusterName) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetDomainReplicationMessagesRequest. func (v *GetDomainReplicationMessagesRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.LastRetrievedMessageId != nil { enc.AddInt64("lastRetrievedMessageId", *v.LastRetrievedMessageId) } if v.LastProcessedMessageId != nil { enc.AddInt64("lastProcessedMessageId", *v.LastProcessedMessageId) } if v.ClusterName != nil { enc.AddString("clusterName", *v.ClusterName) } return err } // GetLastRetrievedMessageId returns the value of LastRetrievedMessageId if it is set or its // zero value if it is unset. func (v *GetDomainReplicationMessagesRequest) GetLastRetrievedMessageId() (o int64) { if v != nil && v.LastRetrievedMessageId != nil { return *v.LastRetrievedMessageId } return } // IsSetLastRetrievedMessageId returns true if LastRetrievedMessageId is not nil. func (v *GetDomainReplicationMessagesRequest) IsSetLastRetrievedMessageId() bool { return v != nil && v.LastRetrievedMessageId != nil } // GetLastProcessedMessageId returns the value of LastProcessedMessageId if it is set or its // zero value if it is unset. func (v *GetDomainReplicationMessagesRequest) GetLastProcessedMessageId() (o int64) { if v != nil && v.LastProcessedMessageId != nil { return *v.LastProcessedMessageId } return } // IsSetLastProcessedMessageId returns true if LastProcessedMessageId is not nil. func (v *GetDomainReplicationMessagesRequest) IsSetLastProcessedMessageId() bool { return v != nil && v.LastProcessedMessageId != nil } // GetClusterName returns the value of ClusterName if it is set or its // zero value if it is unset. func (v *GetDomainReplicationMessagesRequest) GetClusterName() (o string) { if v != nil && v.ClusterName != nil { return *v.ClusterName } return } // IsSetClusterName returns true if ClusterName is not nil. func (v *GetDomainReplicationMessagesRequest) IsSetClusterName() bool { return v != nil && v.ClusterName != nil } type GetDomainReplicationMessagesResponse struct { Messages *ReplicationMessages `json:"messages,omitempty"` } // ToWire translates a GetDomainReplicationMessagesResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetDomainReplicationMessagesResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Messages != nil { w, err = v.Messages.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReplicationMessages_Read(w wire.Value) (*ReplicationMessages, error) { var v ReplicationMessages err := v.FromWire(w) return &v, err } // FromWire deserializes a GetDomainReplicationMessagesResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetDomainReplicationMessagesResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetDomainReplicationMessagesResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetDomainReplicationMessagesResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Messages, err = _ReplicationMessages_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a GetDomainReplicationMessagesResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetDomainReplicationMessagesResponse struct could not be encoded. func (v *GetDomainReplicationMessagesResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Messages != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Messages.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReplicationMessages_Decode(sr stream.Reader) (*ReplicationMessages, error) { var v ReplicationMessages err := v.Decode(sr) return &v, err } // Decode deserializes a GetDomainReplicationMessagesResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetDomainReplicationMessagesResponse struct could not be generated from the wire // representation. func (v *GetDomainReplicationMessagesResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Messages, err = _ReplicationMessages_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetDomainReplicationMessagesResponse // struct. func (v *GetDomainReplicationMessagesResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Messages != nil { fields[i] = fmt.Sprintf("Messages: %v", v.Messages) i++ } return fmt.Sprintf("GetDomainReplicationMessagesResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetDomainReplicationMessagesResponse match the // provided GetDomainReplicationMessagesResponse. // // This function performs a deep comparison. func (v *GetDomainReplicationMessagesResponse) Equals(rhs *GetDomainReplicationMessagesResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Messages == nil && rhs.Messages == nil) || (v.Messages != nil && rhs.Messages != nil && v.Messages.Equals(rhs.Messages))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetDomainReplicationMessagesResponse. func (v *GetDomainReplicationMessagesResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Messages != nil { err = multierr.Append(err, enc.AddObject("messages", v.Messages)) } return err } // GetMessages returns the value of Messages if it is set or its // zero value if it is unset. func (v *GetDomainReplicationMessagesResponse) GetMessages() (o *ReplicationMessages) { if v != nil && v.Messages != nil { return v.Messages } return } // IsSetMessages returns true if Messages is not nil. func (v *GetDomainReplicationMessagesResponse) IsSetMessages() bool { return v != nil && v.Messages != nil } type GetReplicationMessagesRequest struct { Tokens []*ReplicationToken `json:"tokens,omitempty"` ClusterName *string `json:"clusterName,omitempty"` } type _List_ReplicationToken_ValueList []*ReplicationToken func (v _List_ReplicationToken_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*ReplicationToken', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_ReplicationToken_ValueList) Size() int { return len(v) } func (_List_ReplicationToken_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_ReplicationToken_ValueList) Close() {} // ToWire translates a GetReplicationMessagesRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetReplicationMessagesRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Tokens != nil { w, err = wire.NewValueList(_List_ReplicationToken_ValueList(v.Tokens)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ClusterName != nil { w, err = wire.NewValueString(*(v.ClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReplicationToken_Read(w wire.Value) (*ReplicationToken, error) { var v ReplicationToken err := v.FromWire(w) return &v, err } func _List_ReplicationToken_Read(l wire.ValueList) ([]*ReplicationToken, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*ReplicationToken, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _ReplicationToken_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a GetReplicationMessagesRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetReplicationMessagesRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetReplicationMessagesRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetReplicationMessagesRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Tokens, err = _List_ReplicationToken_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClusterName = &x if err != nil { return err } } } } return nil } func _List_ReplicationToken_Encode(val []*ReplicationToken, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*ReplicationToken', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a GetReplicationMessagesRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetReplicationMessagesRequest struct could not be encoded. func (v *GetReplicationMessagesRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Tokens != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_ReplicationToken_Encode(v.Tokens, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReplicationToken_Decode(sr stream.Reader) (*ReplicationToken, error) { var v ReplicationToken err := v.Decode(sr) return &v, err } func _List_ReplicationToken_Decode(sr stream.Reader) ([]*ReplicationToken, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*ReplicationToken, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _ReplicationToken_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a GetReplicationMessagesRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetReplicationMessagesRequest struct could not be generated from the wire // representation. func (v *GetReplicationMessagesRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Tokens, err = _List_ReplicationToken_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClusterName = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetReplicationMessagesRequest // struct. func (v *GetReplicationMessagesRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Tokens != nil { fields[i] = fmt.Sprintf("Tokens: %v", v.Tokens) i++ } if v.ClusterName != nil { fields[i] = fmt.Sprintf("ClusterName: %v", *(v.ClusterName)) i++ } return fmt.Sprintf("GetReplicationMessagesRequest{%v}", strings.Join(fields[:i], ", ")) } func _List_ReplicationToken_Equals(lhs, rhs []*ReplicationToken) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this GetReplicationMessagesRequest match the // provided GetReplicationMessagesRequest. // // This function performs a deep comparison. func (v *GetReplicationMessagesRequest) Equals(rhs *GetReplicationMessagesRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Tokens == nil && rhs.Tokens == nil) || (v.Tokens != nil && rhs.Tokens != nil && _List_ReplicationToken_Equals(v.Tokens, rhs.Tokens))) { return false } if !_String_EqualsPtr(v.ClusterName, rhs.ClusterName) { return false } return true } type _List_ReplicationToken_Zapper []*ReplicationToken // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_ReplicationToken_Zapper. func (l _List_ReplicationToken_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetReplicationMessagesRequest. func (v *GetReplicationMessagesRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Tokens != nil { err = multierr.Append(err, enc.AddArray("tokens", (_List_ReplicationToken_Zapper)(v.Tokens))) } if v.ClusterName != nil { enc.AddString("clusterName", *v.ClusterName) } return err } // GetTokens returns the value of Tokens if it is set or its // zero value if it is unset. func (v *GetReplicationMessagesRequest) GetTokens() (o []*ReplicationToken) { if v != nil && v.Tokens != nil { return v.Tokens } return } // IsSetTokens returns true if Tokens is not nil. func (v *GetReplicationMessagesRequest) IsSetTokens() bool { return v != nil && v.Tokens != nil } // GetClusterName returns the value of ClusterName if it is set or its // zero value if it is unset. func (v *GetReplicationMessagesRequest) GetClusterName() (o string) { if v != nil && v.ClusterName != nil { return *v.ClusterName } return } // IsSetClusterName returns true if ClusterName is not nil. func (v *GetReplicationMessagesRequest) IsSetClusterName() bool { return v != nil && v.ClusterName != nil } type GetReplicationMessagesResponse struct { MessagesByShard map[int32]*ReplicationMessages `json:"messagesByShard,omitempty"` } type _Map_I32_ReplicationMessages_MapItemList map[int32]*ReplicationMessages func (m _Map_I32_ReplicationMessages_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[int32]*ReplicationMessages', key [%v]: value is nil", k) } kw, err := wire.NewValueI32(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_I32_ReplicationMessages_MapItemList) Size() int { return len(m) } func (_Map_I32_ReplicationMessages_MapItemList) KeyType() wire.Type { return wire.TI32 } func (_Map_I32_ReplicationMessages_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_I32_ReplicationMessages_MapItemList) Close() {} // ToWire translates a GetReplicationMessagesResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetReplicationMessagesResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.MessagesByShard != nil { w, err = wire.NewValueMap(_Map_I32_ReplicationMessages_MapItemList(v.MessagesByShard)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Map_I32_ReplicationMessages_Read(m wire.MapItemList) (map[int32]*ReplicationMessages, error) { if m.KeyType() != wire.TI32 { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[int32]*ReplicationMessages, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetI32(), error(nil) if err != nil { return err } v, err := _ReplicationMessages_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a GetReplicationMessagesResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetReplicationMessagesResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetReplicationMessagesResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetReplicationMessagesResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.MessagesByShard, err = _Map_I32_ReplicationMessages_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_I32_ReplicationMessages_Encode(val map[int32]*ReplicationMessages, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TI32, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[int32]*ReplicationMessages', key [%v]: value is nil", k) } if err := sw.WriteInt32(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a GetReplicationMessagesResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetReplicationMessagesResponse struct could not be encoded. func (v *GetReplicationMessagesResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.MessagesByShard != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_I32_ReplicationMessages_Encode(v.MessagesByShard, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _Map_I32_ReplicationMessages_Decode(sr stream.Reader) (map[int32]*ReplicationMessages, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TI32 || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[int32]*ReplicationMessages, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadInt32() if err != nil { return nil, err } v, err := _ReplicationMessages_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a GetReplicationMessagesResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetReplicationMessagesResponse struct could not be generated from the wire // representation. func (v *GetReplicationMessagesResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.MessagesByShard, err = _Map_I32_ReplicationMessages_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetReplicationMessagesResponse // struct. func (v *GetReplicationMessagesResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.MessagesByShard != nil { fields[i] = fmt.Sprintf("MessagesByShard: %v", v.MessagesByShard) i++ } return fmt.Sprintf("GetReplicationMessagesResponse{%v}", strings.Join(fields[:i], ", ")) } func _Map_I32_ReplicationMessages_Equals(lhs, rhs map[int32]*ReplicationMessages) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this GetReplicationMessagesResponse match the // provided GetReplicationMessagesResponse. // // This function performs a deep comparison. func (v *GetReplicationMessagesResponse) Equals(rhs *GetReplicationMessagesResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.MessagesByShard == nil && rhs.MessagesByShard == nil) || (v.MessagesByShard != nil && rhs.MessagesByShard != nil && _Map_I32_ReplicationMessages_Equals(v.MessagesByShard, rhs.MessagesByShard))) { return false } return true } type _Map_I32_ReplicationMessages_Item_Zapper struct { Key int32 Value *ReplicationMessages } // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_ReplicationMessages_Item_Zapper. func (v _Map_I32_ReplicationMessages_Item_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { enc.AddInt32("key", v.Key) err = multierr.Append(err, enc.AddObject("value", v.Value)) return err } type _Map_I32_ReplicationMessages_Zapper map[int32]*ReplicationMessages // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_ReplicationMessages_Zapper. func (m _Map_I32_ReplicationMessages_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AppendObject(_Map_I32_ReplicationMessages_Item_Zapper{Key: k, Value: v})) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetReplicationMessagesResponse. func (v *GetReplicationMessagesResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.MessagesByShard != nil { err = multierr.Append(err, enc.AddArray("messagesByShard", (_Map_I32_ReplicationMessages_Zapper)(v.MessagesByShard))) } return err } // GetMessagesByShard returns the value of MessagesByShard if it is set or its // zero value if it is unset. func (v *GetReplicationMessagesResponse) GetMessagesByShard() (o map[int32]*ReplicationMessages) { if v != nil && v.MessagesByShard != nil { return v.MessagesByShard } return } // IsSetMessagesByShard returns true if MessagesByShard is not nil. func (v *GetReplicationMessagesResponse) IsSetMessagesByShard() bool { return v != nil && v.MessagesByShard != nil } type HistoryTaskV2Attributes struct { TaskId *int64 `json:"taskId,omitempty"` DomainId *string `json:"domainId,omitempty"` WorkflowId *string `json:"workflowId,omitempty"` RunId *string `json:"runId,omitempty"` VersionHistoryItems []*shared.VersionHistoryItem `json:"versionHistoryItems,omitempty"` Events *shared.DataBlob `json:"events,omitempty"` NewRunEvents *shared.DataBlob `json:"newRunEvents,omitempty"` } type _List_VersionHistoryItem_ValueList []*shared.VersionHistoryItem func (v _List_VersionHistoryItem_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*shared.VersionHistoryItem', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_VersionHistoryItem_ValueList) Size() int { return len(v) } func (_List_VersionHistoryItem_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_VersionHistoryItem_ValueList) Close() {} // ToWire translates a HistoryTaskV2Attributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryTaskV2Attributes) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.TaskId != nil { w, err = wire.NewValueI64(*(v.TaskId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.DomainId != nil { w, err = wire.NewValueString(*(v.DomainId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.VersionHistoryItems != nil { w, err = wire.NewValueList(_List_VersionHistoryItem_ValueList(v.VersionHistoryItems)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Events != nil { w, err = v.Events.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.NewRunEvents != nil { w, err = v.NewRunEvents.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _VersionHistoryItem_Read(w wire.Value) (*shared.VersionHistoryItem, error) { var v shared.VersionHistoryItem err := v.FromWire(w) return &v, err } func _List_VersionHistoryItem_Read(l wire.ValueList) ([]*shared.VersionHistoryItem, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*shared.VersionHistoryItem, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _VersionHistoryItem_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _DataBlob_Read(w wire.Value) (*shared.DataBlob, error) { var v shared.DataBlob err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryTaskV2Attributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryTaskV2Attributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryTaskV2Attributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryTaskV2Attributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 5: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskId = &x if err != nil { return err } } case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TList { v.VersionHistoryItems, err = _List_VersionHistoryItem_Read(field.Value.GetList()) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.Events, err = _DataBlob_Read(field.Value) if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.NewRunEvents, err = _DataBlob_Read(field.Value) if err != nil { return err } } } } return nil } func _List_VersionHistoryItem_Encode(val []*shared.VersionHistoryItem, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*shared.VersionHistoryItem', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a HistoryTaskV2Attributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryTaskV2Attributes struct could not be encoded. func (v *HistoryTaskV2Attributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistoryItems != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TList}); err != nil { return err } if err := _List_VersionHistoryItem_Encode(v.VersionHistoryItems, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Events != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.Events.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NewRunEvents != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.NewRunEvents.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _VersionHistoryItem_Decode(sr stream.Reader) (*shared.VersionHistoryItem, error) { var v shared.VersionHistoryItem err := v.Decode(sr) return &v, err } func _List_VersionHistoryItem_Decode(sr stream.Reader) ([]*shared.VersionHistoryItem, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*shared.VersionHistoryItem, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _VersionHistoryItem_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _DataBlob_Decode(sr stream.Reader) (*shared.DataBlob, error) { var v shared.DataBlob err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryTaskV2Attributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryTaskV2Attributes struct could not be generated from the wire // representation. func (v *HistoryTaskV2Attributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 5 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskId = &x if err != nil { return err } case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TList: v.VersionHistoryItems, err = _List_VersionHistoryItem_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.Events, err = _DataBlob_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.NewRunEvents, err = _DataBlob_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryTaskV2Attributes // struct. func (v *HistoryTaskV2Attributes) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.TaskId != nil { fields[i] = fmt.Sprintf("TaskId: %v", *(v.TaskId)) i++ } if v.DomainId != nil { fields[i] = fmt.Sprintf("DomainId: %v", *(v.DomainId)) i++ } if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } if v.VersionHistoryItems != nil { fields[i] = fmt.Sprintf("VersionHistoryItems: %v", v.VersionHistoryItems) i++ } if v.Events != nil { fields[i] = fmt.Sprintf("Events: %v", v.Events) i++ } if v.NewRunEvents != nil { fields[i] = fmt.Sprintf("NewRunEvents: %v", v.NewRunEvents) i++ } return fmt.Sprintf("HistoryTaskV2Attributes{%v}", strings.Join(fields[:i], ", ")) } func _List_VersionHistoryItem_Equals(lhs, rhs []*shared.VersionHistoryItem) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this HistoryTaskV2Attributes match the // provided HistoryTaskV2Attributes. // // This function performs a deep comparison. func (v *HistoryTaskV2Attributes) Equals(rhs *HistoryTaskV2Attributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.TaskId, rhs.TaskId) { return false } if !_String_EqualsPtr(v.DomainId, rhs.DomainId) { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } if !((v.VersionHistoryItems == nil && rhs.VersionHistoryItems == nil) || (v.VersionHistoryItems != nil && rhs.VersionHistoryItems != nil && _List_VersionHistoryItem_Equals(v.VersionHistoryItems, rhs.VersionHistoryItems))) { return false } if !((v.Events == nil && rhs.Events == nil) || (v.Events != nil && rhs.Events != nil && v.Events.Equals(rhs.Events))) { return false } if !((v.NewRunEvents == nil && rhs.NewRunEvents == nil) || (v.NewRunEvents != nil && rhs.NewRunEvents != nil && v.NewRunEvents.Equals(rhs.NewRunEvents))) { return false } return true } type _List_VersionHistoryItem_Zapper []*shared.VersionHistoryItem // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_VersionHistoryItem_Zapper. func (l _List_VersionHistoryItem_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryTaskV2Attributes. func (v *HistoryTaskV2Attributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskId != nil { enc.AddInt64("taskId", *v.TaskId) } if v.DomainId != nil { enc.AddString("domainId", *v.DomainId) } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.RunId != nil { enc.AddString("runId", *v.RunId) } if v.VersionHistoryItems != nil { err = multierr.Append(err, enc.AddArray("versionHistoryItems", (_List_VersionHistoryItem_Zapper)(v.VersionHistoryItems))) } if v.Events != nil { err = multierr.Append(err, enc.AddObject("events", v.Events)) } if v.NewRunEvents != nil { err = multierr.Append(err, enc.AddObject("newRunEvents", v.NewRunEvents)) } return err } // GetTaskId returns the value of TaskId if it is set or its // zero value if it is unset. func (v *HistoryTaskV2Attributes) GetTaskId() (o int64) { if v != nil && v.TaskId != nil { return *v.TaskId } return } // IsSetTaskId returns true if TaskId is not nil. func (v *HistoryTaskV2Attributes) IsSetTaskId() bool { return v != nil && v.TaskId != nil } // GetDomainId returns the value of DomainId if it is set or its // zero value if it is unset. func (v *HistoryTaskV2Attributes) GetDomainId() (o string) { if v != nil && v.DomainId != nil { return *v.DomainId } return } // IsSetDomainId returns true if DomainId is not nil. func (v *HistoryTaskV2Attributes) IsSetDomainId() bool { return v != nil && v.DomainId != nil } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *HistoryTaskV2Attributes) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *HistoryTaskV2Attributes) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *HistoryTaskV2Attributes) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *HistoryTaskV2Attributes) IsSetRunId() bool { return v != nil && v.RunId != nil } // GetVersionHistoryItems returns the value of VersionHistoryItems if it is set or its // zero value if it is unset. func (v *HistoryTaskV2Attributes) GetVersionHistoryItems() (o []*shared.VersionHistoryItem) { if v != nil && v.VersionHistoryItems != nil { return v.VersionHistoryItems } return } // IsSetVersionHistoryItems returns true if VersionHistoryItems is not nil. func (v *HistoryTaskV2Attributes) IsSetVersionHistoryItems() bool { return v != nil && v.VersionHistoryItems != nil } // GetEvents returns the value of Events if it is set or its // zero value if it is unset. func (v *HistoryTaskV2Attributes) GetEvents() (o *shared.DataBlob) { if v != nil && v.Events != nil { return v.Events } return } // IsSetEvents returns true if Events is not nil. func (v *HistoryTaskV2Attributes) IsSetEvents() bool { return v != nil && v.Events != nil } // GetNewRunEvents returns the value of NewRunEvents if it is set or its // zero value if it is unset. func (v *HistoryTaskV2Attributes) GetNewRunEvents() (o *shared.DataBlob) { if v != nil && v.NewRunEvents != nil { return v.NewRunEvents } return } // IsSetNewRunEvents returns true if NewRunEvents is not nil. func (v *HistoryTaskV2Attributes) IsSetNewRunEvents() bool { return v != nil && v.NewRunEvents != nil } type MergeDLQMessagesRequest struct { Type *DLQType `json:"type,omitempty"` ShardID *int32 `json:"shardID,omitempty"` SourceCluster *string `json:"sourceCluster,omitempty"` InclusiveEndMessageID *int64 `json:"inclusiveEndMessageID,omitempty"` MaximumPageSize *int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // ToWire translates a MergeDLQMessagesRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MergeDLQMessagesRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Type != nil { w, err = v.Type.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ShardID != nil { w, err = wire.NewValueI32(*(v.ShardID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.SourceCluster != nil { w, err = wire.NewValueString(*(v.SourceCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.InclusiveEndMessageID != nil { w, err = wire.NewValueI64(*(v.InclusiveEndMessageID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.MaximumPageSize != nil { w, err = wire.NewValueI32(*(v.MaximumPageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DLQType_Read(w wire.Value) (DLQType, error) { var v DLQType err := v.FromWire(w) return v, err } // FromWire deserializes a MergeDLQMessagesRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MergeDLQMessagesRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MergeDLQMessagesRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MergeDLQMessagesRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x DLQType x, err = _DLQType_Read(field.Value) v.Type = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShardID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SourceCluster = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InclusiveEndMessageID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.MaximumPageSize = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a MergeDLQMessagesRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MergeDLQMessagesRequest struct could not be encoded. func (v *MergeDLQMessagesRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Type != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.Type.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShardID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SourceCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SourceCluster)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InclusiveEndMessageID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InclusiveEndMessageID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MaximumPageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.MaximumPageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DLQType_Decode(sr stream.Reader) (DLQType, error) { var v DLQType err := v.Decode(sr) return v, err } // Decode deserializes a MergeDLQMessagesRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MergeDLQMessagesRequest struct could not be generated from the wire // representation. func (v *MergeDLQMessagesRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x DLQType x, err = _DLQType_Decode(sr) v.Type = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShardID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SourceCluster = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InclusiveEndMessageID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.MaximumPageSize = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MergeDLQMessagesRequest // struct. func (v *MergeDLQMessagesRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Type != nil { fields[i] = fmt.Sprintf("Type: %v", *(v.Type)) i++ } if v.ShardID != nil { fields[i] = fmt.Sprintf("ShardID: %v", *(v.ShardID)) i++ } if v.SourceCluster != nil { fields[i] = fmt.Sprintf("SourceCluster: %v", *(v.SourceCluster)) i++ } if v.InclusiveEndMessageID != nil { fields[i] = fmt.Sprintf("InclusiveEndMessageID: %v", *(v.InclusiveEndMessageID)) i++ } if v.MaximumPageSize != nil { fields[i] = fmt.Sprintf("MaximumPageSize: %v", *(v.MaximumPageSize)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("MergeDLQMessagesRequest{%v}", strings.Join(fields[:i], ", ")) } func _DLQType_EqualsPtr(lhs, rhs *DLQType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } func _I32_EqualsPtr(lhs, rhs *int32) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this MergeDLQMessagesRequest match the // provided MergeDLQMessagesRequest. // // This function performs a deep comparison. func (v *MergeDLQMessagesRequest) Equals(rhs *MergeDLQMessagesRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_DLQType_EqualsPtr(v.Type, rhs.Type) { return false } if !_I32_EqualsPtr(v.ShardID, rhs.ShardID) { return false } if !_String_EqualsPtr(v.SourceCluster, rhs.SourceCluster) { return false } if !_I64_EqualsPtr(v.InclusiveEndMessageID, rhs.InclusiveEndMessageID) { return false } if !_I32_EqualsPtr(v.MaximumPageSize, rhs.MaximumPageSize) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MergeDLQMessagesRequest. func (v *MergeDLQMessagesRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Type != nil { err = multierr.Append(err, enc.AddObject("type", *v.Type)) } if v.ShardID != nil { enc.AddInt32("shardID", *v.ShardID) } if v.SourceCluster != nil { enc.AddString("sourceCluster", *v.SourceCluster) } if v.InclusiveEndMessageID != nil { enc.AddInt64("inclusiveEndMessageID", *v.InclusiveEndMessageID) } if v.MaximumPageSize != nil { enc.AddInt32("maximumPageSize", *v.MaximumPageSize) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetType returns the value of Type if it is set or its // zero value if it is unset. func (v *MergeDLQMessagesRequest) GetType() (o DLQType) { if v != nil && v.Type != nil { return *v.Type } return } // IsSetType returns true if Type is not nil. func (v *MergeDLQMessagesRequest) IsSetType() bool { return v != nil && v.Type != nil } // GetShardID returns the value of ShardID if it is set or its // zero value if it is unset. func (v *MergeDLQMessagesRequest) GetShardID() (o int32) { if v != nil && v.ShardID != nil { return *v.ShardID } return } // IsSetShardID returns true if ShardID is not nil. func (v *MergeDLQMessagesRequest) IsSetShardID() bool { return v != nil && v.ShardID != nil } // GetSourceCluster returns the value of SourceCluster if it is set or its // zero value if it is unset. func (v *MergeDLQMessagesRequest) GetSourceCluster() (o string) { if v != nil && v.SourceCluster != nil { return *v.SourceCluster } return } // IsSetSourceCluster returns true if SourceCluster is not nil. func (v *MergeDLQMessagesRequest) IsSetSourceCluster() bool { return v != nil && v.SourceCluster != nil } // GetInclusiveEndMessageID returns the value of InclusiveEndMessageID if it is set or its // zero value if it is unset. func (v *MergeDLQMessagesRequest) GetInclusiveEndMessageID() (o int64) { if v != nil && v.InclusiveEndMessageID != nil { return *v.InclusiveEndMessageID } return } // IsSetInclusiveEndMessageID returns true if InclusiveEndMessageID is not nil. func (v *MergeDLQMessagesRequest) IsSetInclusiveEndMessageID() bool { return v != nil && v.InclusiveEndMessageID != nil } // GetMaximumPageSize returns the value of MaximumPageSize if it is set or its // zero value if it is unset. func (v *MergeDLQMessagesRequest) GetMaximumPageSize() (o int32) { if v != nil && v.MaximumPageSize != nil { return *v.MaximumPageSize } return } // IsSetMaximumPageSize returns true if MaximumPageSize is not nil. func (v *MergeDLQMessagesRequest) IsSetMaximumPageSize() bool { return v != nil && v.MaximumPageSize != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *MergeDLQMessagesRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *MergeDLQMessagesRequest) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type MergeDLQMessagesResponse struct { NextPageToken []byte `json:"nextPageToken,omitempty"` } // ToWire translates a MergeDLQMessagesResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MergeDLQMessagesResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a MergeDLQMessagesResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MergeDLQMessagesResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MergeDLQMessagesResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MergeDLQMessagesResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a MergeDLQMessagesResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MergeDLQMessagesResponse struct could not be encoded. func (v *MergeDLQMessagesResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a MergeDLQMessagesResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MergeDLQMessagesResponse struct could not be generated from the wire // representation. func (v *MergeDLQMessagesResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MergeDLQMessagesResponse // struct. func (v *MergeDLQMessagesResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("MergeDLQMessagesResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MergeDLQMessagesResponse match the // provided MergeDLQMessagesResponse. // // This function performs a deep comparison. func (v *MergeDLQMessagesResponse) Equals(rhs *MergeDLQMessagesResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MergeDLQMessagesResponse. func (v *MergeDLQMessagesResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *MergeDLQMessagesResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *MergeDLQMessagesResponse) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type PurgeDLQMessagesRequest struct { Type *DLQType `json:"type,omitempty"` ShardID *int32 `json:"shardID,omitempty"` SourceCluster *string `json:"sourceCluster,omitempty"` InclusiveEndMessageID *int64 `json:"inclusiveEndMessageID,omitempty"` } // ToWire translates a PurgeDLQMessagesRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PurgeDLQMessagesRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Type != nil { w, err = v.Type.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ShardID != nil { w, err = wire.NewValueI32(*(v.ShardID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.SourceCluster != nil { w, err = wire.NewValueString(*(v.SourceCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.InclusiveEndMessageID != nil { w, err = wire.NewValueI64(*(v.InclusiveEndMessageID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a PurgeDLQMessagesRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PurgeDLQMessagesRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PurgeDLQMessagesRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PurgeDLQMessagesRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x DLQType x, err = _DLQType_Read(field.Value) v.Type = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShardID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SourceCluster = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InclusiveEndMessageID = &x if err != nil { return err } } } } return nil } // Encode serializes a PurgeDLQMessagesRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PurgeDLQMessagesRequest struct could not be encoded. func (v *PurgeDLQMessagesRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Type != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.Type.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShardID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SourceCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SourceCluster)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InclusiveEndMessageID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InclusiveEndMessageID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a PurgeDLQMessagesRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PurgeDLQMessagesRequest struct could not be generated from the wire // representation. func (v *PurgeDLQMessagesRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x DLQType x, err = _DLQType_Decode(sr) v.Type = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShardID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SourceCluster = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InclusiveEndMessageID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PurgeDLQMessagesRequest // struct. func (v *PurgeDLQMessagesRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Type != nil { fields[i] = fmt.Sprintf("Type: %v", *(v.Type)) i++ } if v.ShardID != nil { fields[i] = fmt.Sprintf("ShardID: %v", *(v.ShardID)) i++ } if v.SourceCluster != nil { fields[i] = fmt.Sprintf("SourceCluster: %v", *(v.SourceCluster)) i++ } if v.InclusiveEndMessageID != nil { fields[i] = fmt.Sprintf("InclusiveEndMessageID: %v", *(v.InclusiveEndMessageID)) i++ } return fmt.Sprintf("PurgeDLQMessagesRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PurgeDLQMessagesRequest match the // provided PurgeDLQMessagesRequest. // // This function performs a deep comparison. func (v *PurgeDLQMessagesRequest) Equals(rhs *PurgeDLQMessagesRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_DLQType_EqualsPtr(v.Type, rhs.Type) { return false } if !_I32_EqualsPtr(v.ShardID, rhs.ShardID) { return false } if !_String_EqualsPtr(v.SourceCluster, rhs.SourceCluster) { return false } if !_I64_EqualsPtr(v.InclusiveEndMessageID, rhs.InclusiveEndMessageID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PurgeDLQMessagesRequest. func (v *PurgeDLQMessagesRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Type != nil { err = multierr.Append(err, enc.AddObject("type", *v.Type)) } if v.ShardID != nil { enc.AddInt32("shardID", *v.ShardID) } if v.SourceCluster != nil { enc.AddString("sourceCluster", *v.SourceCluster) } if v.InclusiveEndMessageID != nil { enc.AddInt64("inclusiveEndMessageID", *v.InclusiveEndMessageID) } return err } // GetType returns the value of Type if it is set or its // zero value if it is unset. func (v *PurgeDLQMessagesRequest) GetType() (o DLQType) { if v != nil && v.Type != nil { return *v.Type } return } // IsSetType returns true if Type is not nil. func (v *PurgeDLQMessagesRequest) IsSetType() bool { return v != nil && v.Type != nil } // GetShardID returns the value of ShardID if it is set or its // zero value if it is unset. func (v *PurgeDLQMessagesRequest) GetShardID() (o int32) { if v != nil && v.ShardID != nil { return *v.ShardID } return } // IsSetShardID returns true if ShardID is not nil. func (v *PurgeDLQMessagesRequest) IsSetShardID() bool { return v != nil && v.ShardID != nil } // GetSourceCluster returns the value of SourceCluster if it is set or its // zero value if it is unset. func (v *PurgeDLQMessagesRequest) GetSourceCluster() (o string) { if v != nil && v.SourceCluster != nil { return *v.SourceCluster } return } // IsSetSourceCluster returns true if SourceCluster is not nil. func (v *PurgeDLQMessagesRequest) IsSetSourceCluster() bool { return v != nil && v.SourceCluster != nil } // GetInclusiveEndMessageID returns the value of InclusiveEndMessageID if it is set or its // zero value if it is unset. func (v *PurgeDLQMessagesRequest) GetInclusiveEndMessageID() (o int64) { if v != nil && v.InclusiveEndMessageID != nil { return *v.InclusiveEndMessageID } return } // IsSetInclusiveEndMessageID returns true if InclusiveEndMessageID is not nil. func (v *PurgeDLQMessagesRequest) IsSetInclusiveEndMessageID() bool { return v != nil && v.InclusiveEndMessageID != nil } type ReadDLQMessagesRequest struct { Type *DLQType `json:"type,omitempty"` ShardID *int32 `json:"shardID,omitempty"` SourceCluster *string `json:"sourceCluster,omitempty"` InclusiveEndMessageID *int64 `json:"inclusiveEndMessageID,omitempty"` MaximumPageSize *int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // ToWire translates a ReadDLQMessagesRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReadDLQMessagesRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Type != nil { w, err = v.Type.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ShardID != nil { w, err = wire.NewValueI32(*(v.ShardID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.SourceCluster != nil { w, err = wire.NewValueString(*(v.SourceCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.InclusiveEndMessageID != nil { w, err = wire.NewValueI64(*(v.InclusiveEndMessageID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.MaximumPageSize != nil { w, err = wire.NewValueI32(*(v.MaximumPageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ReadDLQMessagesRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReadDLQMessagesRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReadDLQMessagesRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReadDLQMessagesRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x DLQType x, err = _DLQType_Read(field.Value) v.Type = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShardID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SourceCluster = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InclusiveEndMessageID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.MaximumPageSize = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a ReadDLQMessagesRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReadDLQMessagesRequest struct could not be encoded. func (v *ReadDLQMessagesRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Type != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.Type.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShardID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SourceCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SourceCluster)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InclusiveEndMessageID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InclusiveEndMessageID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MaximumPageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.MaximumPageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ReadDLQMessagesRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReadDLQMessagesRequest struct could not be generated from the wire // representation. func (v *ReadDLQMessagesRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x DLQType x, err = _DLQType_Decode(sr) v.Type = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShardID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SourceCluster = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InclusiveEndMessageID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.MaximumPageSize = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReadDLQMessagesRequest // struct. func (v *ReadDLQMessagesRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Type != nil { fields[i] = fmt.Sprintf("Type: %v", *(v.Type)) i++ } if v.ShardID != nil { fields[i] = fmt.Sprintf("ShardID: %v", *(v.ShardID)) i++ } if v.SourceCluster != nil { fields[i] = fmt.Sprintf("SourceCluster: %v", *(v.SourceCluster)) i++ } if v.InclusiveEndMessageID != nil { fields[i] = fmt.Sprintf("InclusiveEndMessageID: %v", *(v.InclusiveEndMessageID)) i++ } if v.MaximumPageSize != nil { fields[i] = fmt.Sprintf("MaximumPageSize: %v", *(v.MaximumPageSize)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("ReadDLQMessagesRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ReadDLQMessagesRequest match the // provided ReadDLQMessagesRequest. // // This function performs a deep comparison. func (v *ReadDLQMessagesRequest) Equals(rhs *ReadDLQMessagesRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_DLQType_EqualsPtr(v.Type, rhs.Type) { return false } if !_I32_EqualsPtr(v.ShardID, rhs.ShardID) { return false } if !_String_EqualsPtr(v.SourceCluster, rhs.SourceCluster) { return false } if !_I64_EqualsPtr(v.InclusiveEndMessageID, rhs.InclusiveEndMessageID) { return false } if !_I32_EqualsPtr(v.MaximumPageSize, rhs.MaximumPageSize) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReadDLQMessagesRequest. func (v *ReadDLQMessagesRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Type != nil { err = multierr.Append(err, enc.AddObject("type", *v.Type)) } if v.ShardID != nil { enc.AddInt32("shardID", *v.ShardID) } if v.SourceCluster != nil { enc.AddString("sourceCluster", *v.SourceCluster) } if v.InclusiveEndMessageID != nil { enc.AddInt64("inclusiveEndMessageID", *v.InclusiveEndMessageID) } if v.MaximumPageSize != nil { enc.AddInt32("maximumPageSize", *v.MaximumPageSize) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetType returns the value of Type if it is set or its // zero value if it is unset. func (v *ReadDLQMessagesRequest) GetType() (o DLQType) { if v != nil && v.Type != nil { return *v.Type } return } // IsSetType returns true if Type is not nil. func (v *ReadDLQMessagesRequest) IsSetType() bool { return v != nil && v.Type != nil } // GetShardID returns the value of ShardID if it is set or its // zero value if it is unset. func (v *ReadDLQMessagesRequest) GetShardID() (o int32) { if v != nil && v.ShardID != nil { return *v.ShardID } return } // IsSetShardID returns true if ShardID is not nil. func (v *ReadDLQMessagesRequest) IsSetShardID() bool { return v != nil && v.ShardID != nil } // GetSourceCluster returns the value of SourceCluster if it is set or its // zero value if it is unset. func (v *ReadDLQMessagesRequest) GetSourceCluster() (o string) { if v != nil && v.SourceCluster != nil { return *v.SourceCluster } return } // IsSetSourceCluster returns true if SourceCluster is not nil. func (v *ReadDLQMessagesRequest) IsSetSourceCluster() bool { return v != nil && v.SourceCluster != nil } // GetInclusiveEndMessageID returns the value of InclusiveEndMessageID if it is set or its // zero value if it is unset. func (v *ReadDLQMessagesRequest) GetInclusiveEndMessageID() (o int64) { if v != nil && v.InclusiveEndMessageID != nil { return *v.InclusiveEndMessageID } return } // IsSetInclusiveEndMessageID returns true if InclusiveEndMessageID is not nil. func (v *ReadDLQMessagesRequest) IsSetInclusiveEndMessageID() bool { return v != nil && v.InclusiveEndMessageID != nil } // GetMaximumPageSize returns the value of MaximumPageSize if it is set or its // zero value if it is unset. func (v *ReadDLQMessagesRequest) GetMaximumPageSize() (o int32) { if v != nil && v.MaximumPageSize != nil { return *v.MaximumPageSize } return } // IsSetMaximumPageSize returns true if MaximumPageSize is not nil. func (v *ReadDLQMessagesRequest) IsSetMaximumPageSize() bool { return v != nil && v.MaximumPageSize != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ReadDLQMessagesRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ReadDLQMessagesRequest) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type ReadDLQMessagesResponse struct { Type *DLQType `json:"type,omitempty"` ReplicationTasks []*ReplicationTask `json:"replicationTasks,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` ReplicationTasksInfo []*ReplicationTaskInfo `json:"replicationTasksInfo,omitempty"` } // ToWire translates a ReadDLQMessagesResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReadDLQMessagesResponse) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Type != nil { w, err = v.Type.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ReplicationTasks != nil { w, err = wire.NewValueList(_List_ReplicationTask_ValueList(v.ReplicationTasks)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ReplicationTasksInfo != nil { w, err = wire.NewValueList(_List_ReplicationTaskInfo_ValueList(v.ReplicationTasksInfo)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ReadDLQMessagesResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReadDLQMessagesResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReadDLQMessagesResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReadDLQMessagesResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x DLQType x, err = _DLQType_Read(field.Value) v.Type = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.ReplicationTasks, err = _List_ReplicationTask_Read(field.Value.GetList()) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TList { v.ReplicationTasksInfo, err = _List_ReplicationTaskInfo_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } // Encode serializes a ReadDLQMessagesResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReadDLQMessagesResponse struct could not be encoded. func (v *ReadDLQMessagesResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Type != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.Type.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReplicationTasks != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_ReplicationTask_Encode(v.ReplicationTasks, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReplicationTasksInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TList}); err != nil { return err } if err := _List_ReplicationTaskInfo_Encode(v.ReplicationTasksInfo, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ReadDLQMessagesResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReadDLQMessagesResponse struct could not be generated from the wire // representation. func (v *ReadDLQMessagesResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x DLQType x, err = _DLQType_Decode(sr) v.Type = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.ReplicationTasks, err = _List_ReplicationTask_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TList: v.ReplicationTasksInfo, err = _List_ReplicationTaskInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReadDLQMessagesResponse // struct. func (v *ReadDLQMessagesResponse) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Type != nil { fields[i] = fmt.Sprintf("Type: %v", *(v.Type)) i++ } if v.ReplicationTasks != nil { fields[i] = fmt.Sprintf("ReplicationTasks: %v", v.ReplicationTasks) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.ReplicationTasksInfo != nil { fields[i] = fmt.Sprintf("ReplicationTasksInfo: %v", v.ReplicationTasksInfo) i++ } return fmt.Sprintf("ReadDLQMessagesResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ReadDLQMessagesResponse match the // provided ReadDLQMessagesResponse. // // This function performs a deep comparison. func (v *ReadDLQMessagesResponse) Equals(rhs *ReadDLQMessagesResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_DLQType_EqualsPtr(v.Type, rhs.Type) { return false } if !((v.ReplicationTasks == nil && rhs.ReplicationTasks == nil) || (v.ReplicationTasks != nil && rhs.ReplicationTasks != nil && _List_ReplicationTask_Equals(v.ReplicationTasks, rhs.ReplicationTasks))) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !((v.ReplicationTasksInfo == nil && rhs.ReplicationTasksInfo == nil) || (v.ReplicationTasksInfo != nil && rhs.ReplicationTasksInfo != nil && _List_ReplicationTaskInfo_Equals(v.ReplicationTasksInfo, rhs.ReplicationTasksInfo))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReadDLQMessagesResponse. func (v *ReadDLQMessagesResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Type != nil { err = multierr.Append(err, enc.AddObject("type", *v.Type)) } if v.ReplicationTasks != nil { err = multierr.Append(err, enc.AddArray("replicationTasks", (_List_ReplicationTask_Zapper)(v.ReplicationTasks))) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.ReplicationTasksInfo != nil { err = multierr.Append(err, enc.AddArray("replicationTasksInfo", (_List_ReplicationTaskInfo_Zapper)(v.ReplicationTasksInfo))) } return err } // GetType returns the value of Type if it is set or its // zero value if it is unset. func (v *ReadDLQMessagesResponse) GetType() (o DLQType) { if v != nil && v.Type != nil { return *v.Type } return } // IsSetType returns true if Type is not nil. func (v *ReadDLQMessagesResponse) IsSetType() bool { return v != nil && v.Type != nil } // GetReplicationTasks returns the value of ReplicationTasks if it is set or its // zero value if it is unset. func (v *ReadDLQMessagesResponse) GetReplicationTasks() (o []*ReplicationTask) { if v != nil && v.ReplicationTasks != nil { return v.ReplicationTasks } return } // IsSetReplicationTasks returns true if ReplicationTasks is not nil. func (v *ReadDLQMessagesResponse) IsSetReplicationTasks() bool { return v != nil && v.ReplicationTasks != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ReadDLQMessagesResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ReadDLQMessagesResponse) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetReplicationTasksInfo returns the value of ReplicationTasksInfo if it is set or its // zero value if it is unset. func (v *ReadDLQMessagesResponse) GetReplicationTasksInfo() (o []*ReplicationTaskInfo) { if v != nil && v.ReplicationTasksInfo != nil { return v.ReplicationTasksInfo } return } // IsSetReplicationTasksInfo returns true if ReplicationTasksInfo is not nil. func (v *ReadDLQMessagesResponse) IsSetReplicationTasksInfo() bool { return v != nil && v.ReplicationTasksInfo != nil } type ReplicationMessages struct { ReplicationTasks []*ReplicationTask `json:"replicationTasks,omitempty"` LastRetrievedMessageId *int64 `json:"lastRetrievedMessageId,omitempty"` HasMore *bool `json:"hasMore,omitempty"` SyncShardStatus *SyncShardStatus `json:"syncShardStatus,omitempty"` } // ToWire translates a ReplicationMessages struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReplicationMessages) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.ReplicationTasks != nil { w, err = wire.NewValueList(_List_ReplicationTask_ValueList(v.ReplicationTasks)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.LastRetrievedMessageId != nil { w, err = wire.NewValueI64(*(v.LastRetrievedMessageId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.HasMore != nil { w, err = wire.NewValueBool(*(v.HasMore)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.SyncShardStatus != nil { w, err = v.SyncShardStatus.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SyncShardStatus_Read(w wire.Value) (*SyncShardStatus, error) { var v SyncShardStatus err := v.FromWire(w) return &v, err } // FromWire deserializes a ReplicationMessages struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReplicationMessages struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReplicationMessages // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReplicationMessages) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.ReplicationTasks, err = _List_ReplicationTask_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastRetrievedMessageId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.HasMore = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.SyncShardStatus, err = _SyncShardStatus_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ReplicationMessages struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReplicationMessages struct could not be encoded. func (v *ReplicationMessages) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ReplicationTasks != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_ReplicationTask_Encode(v.ReplicationTasks, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastRetrievedMessageId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastRetrievedMessageId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HasMore != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.HasMore)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SyncShardStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.SyncShardStatus.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SyncShardStatus_Decode(sr stream.Reader) (*SyncShardStatus, error) { var v SyncShardStatus err := v.Decode(sr) return &v, err } // Decode deserializes a ReplicationMessages struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReplicationMessages struct could not be generated from the wire // representation. func (v *ReplicationMessages) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.ReplicationTasks, err = _List_ReplicationTask_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastRetrievedMessageId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.HasMore = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.SyncShardStatus, err = _SyncShardStatus_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReplicationMessages // struct. func (v *ReplicationMessages) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.ReplicationTasks != nil { fields[i] = fmt.Sprintf("ReplicationTasks: %v", v.ReplicationTasks) i++ } if v.LastRetrievedMessageId != nil { fields[i] = fmt.Sprintf("LastRetrievedMessageId: %v", *(v.LastRetrievedMessageId)) i++ } if v.HasMore != nil { fields[i] = fmt.Sprintf("HasMore: %v", *(v.HasMore)) i++ } if v.SyncShardStatus != nil { fields[i] = fmt.Sprintf("SyncShardStatus: %v", v.SyncShardStatus) i++ } return fmt.Sprintf("ReplicationMessages{%v}", strings.Join(fields[:i], ", ")) } func _Bool_EqualsPtr(lhs, rhs *bool) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ReplicationMessages match the // provided ReplicationMessages. // // This function performs a deep comparison. func (v *ReplicationMessages) Equals(rhs *ReplicationMessages) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ReplicationTasks == nil && rhs.ReplicationTasks == nil) || (v.ReplicationTasks != nil && rhs.ReplicationTasks != nil && _List_ReplicationTask_Equals(v.ReplicationTasks, rhs.ReplicationTasks))) { return false } if !_I64_EqualsPtr(v.LastRetrievedMessageId, rhs.LastRetrievedMessageId) { return false } if !_Bool_EqualsPtr(v.HasMore, rhs.HasMore) { return false } if !((v.SyncShardStatus == nil && rhs.SyncShardStatus == nil) || (v.SyncShardStatus != nil && rhs.SyncShardStatus != nil && v.SyncShardStatus.Equals(rhs.SyncShardStatus))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReplicationMessages. func (v *ReplicationMessages) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ReplicationTasks != nil { err = multierr.Append(err, enc.AddArray("replicationTasks", (_List_ReplicationTask_Zapper)(v.ReplicationTasks))) } if v.LastRetrievedMessageId != nil { enc.AddInt64("lastRetrievedMessageId", *v.LastRetrievedMessageId) } if v.HasMore != nil { enc.AddBool("hasMore", *v.HasMore) } if v.SyncShardStatus != nil { err = multierr.Append(err, enc.AddObject("syncShardStatus", v.SyncShardStatus)) } return err } // GetReplicationTasks returns the value of ReplicationTasks if it is set or its // zero value if it is unset. func (v *ReplicationMessages) GetReplicationTasks() (o []*ReplicationTask) { if v != nil && v.ReplicationTasks != nil { return v.ReplicationTasks } return } // IsSetReplicationTasks returns true if ReplicationTasks is not nil. func (v *ReplicationMessages) IsSetReplicationTasks() bool { return v != nil && v.ReplicationTasks != nil } // GetLastRetrievedMessageId returns the value of LastRetrievedMessageId if it is set or its // zero value if it is unset. func (v *ReplicationMessages) GetLastRetrievedMessageId() (o int64) { if v != nil && v.LastRetrievedMessageId != nil { return *v.LastRetrievedMessageId } return } // IsSetLastRetrievedMessageId returns true if LastRetrievedMessageId is not nil. func (v *ReplicationMessages) IsSetLastRetrievedMessageId() bool { return v != nil && v.LastRetrievedMessageId != nil } // GetHasMore returns the value of HasMore if it is set or its // zero value if it is unset. func (v *ReplicationMessages) GetHasMore() (o bool) { if v != nil && v.HasMore != nil { return *v.HasMore } return } // IsSetHasMore returns true if HasMore is not nil. func (v *ReplicationMessages) IsSetHasMore() bool { return v != nil && v.HasMore != nil } // GetSyncShardStatus returns the value of SyncShardStatus if it is set or its // zero value if it is unset. func (v *ReplicationMessages) GetSyncShardStatus() (o *SyncShardStatus) { if v != nil && v.SyncShardStatus != nil { return v.SyncShardStatus } return } // IsSetSyncShardStatus returns true if SyncShardStatus is not nil. func (v *ReplicationMessages) IsSetSyncShardStatus() bool { return v != nil && v.SyncShardStatus != nil } type ReplicationTask struct { TaskType *ReplicationTaskType `json:"taskType,omitempty"` SourceTaskId *int64 `json:"sourceTaskId,omitempty"` DomainTaskAttributes *DomainTaskAttributes `json:"domainTaskAttributes,omitempty"` SyncShardStatusTaskAttributes *SyncShardStatusTaskAttributes `json:"syncShardStatusTaskAttributes,omitempty"` SyncActivityTaskAttributes *SyncActivityTaskAttributes `json:"syncActivityTaskAttributes,omitempty"` HistoryTaskV2Attributes *HistoryTaskV2Attributes `json:"historyTaskV2Attributes,omitempty"` FailoverMarkerAttributes *FailoverMarkerAttributes `json:"failoverMarkerAttributes,omitempty"` CreationTime *int64 `json:"creationTime,omitempty"` } // ToWire translates a ReplicationTask struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReplicationTask) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.TaskType != nil { w, err = v.TaskType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.SourceTaskId != nil { w, err = wire.NewValueI64(*(v.SourceTaskId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 11, Value: w} i++ } if v.DomainTaskAttributes != nil { w, err = v.DomainTaskAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.SyncShardStatusTaskAttributes != nil { w, err = v.SyncShardStatusTaskAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.SyncActivityTaskAttributes != nil { w, err = v.SyncActivityTaskAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.HistoryTaskV2Attributes != nil { w, err = v.HistoryTaskV2Attributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.FailoverMarkerAttributes != nil { w, err = v.FailoverMarkerAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.CreationTime != nil { w, err = wire.NewValueI64(*(v.CreationTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ReplicationTaskType_Read(w wire.Value) (ReplicationTaskType, error) { var v ReplicationTaskType err := v.FromWire(w) return v, err } func _DomainTaskAttributes_Read(w wire.Value) (*DomainTaskAttributes, error) { var v DomainTaskAttributes err := v.FromWire(w) return &v, err } func _SyncShardStatusTaskAttributes_Read(w wire.Value) (*SyncShardStatusTaskAttributes, error) { var v SyncShardStatusTaskAttributes err := v.FromWire(w) return &v, err } func _SyncActivityTaskAttributes_Read(w wire.Value) (*SyncActivityTaskAttributes, error) { var v SyncActivityTaskAttributes err := v.FromWire(w) return &v, err } func _HistoryTaskV2Attributes_Read(w wire.Value) (*HistoryTaskV2Attributes, error) { var v HistoryTaskV2Attributes err := v.FromWire(w) return &v, err } // FromWire deserializes a ReplicationTask struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReplicationTask struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReplicationTask // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReplicationTask) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x ReplicationTaskType x, err = _ReplicationTaskType_Read(field.Value) v.TaskType = &x if err != nil { return err } } case 11: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.SourceTaskId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.DomainTaskAttributes, err = _DomainTaskAttributes_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.SyncShardStatusTaskAttributes, err = _SyncShardStatusTaskAttributes_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.SyncActivityTaskAttributes, err = _SyncActivityTaskAttributes_Read(field.Value) if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.HistoryTaskV2Attributes, err = _HistoryTaskV2Attributes_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TStruct { v.FailoverMarkerAttributes, err = _FailoverMarkerAttributes_Read(field.Value) if err != nil { return err } } case 90: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CreationTime = &x if err != nil { return err } } } } return nil } // Encode serializes a ReplicationTask struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReplicationTask struct could not be encoded. func (v *ReplicationTask) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.TaskType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SourceTaskId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 11, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.SourceTaskId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainTaskAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.DomainTaskAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SyncShardStatusTaskAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.SyncShardStatusTaskAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SyncActivityTaskAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.SyncActivityTaskAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryTaskV2Attributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.HistoryTaskV2Attributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverMarkerAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TStruct}); err != nil { return err } if err := v.FailoverMarkerAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CreationTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CreationTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ReplicationTaskType_Decode(sr stream.Reader) (ReplicationTaskType, error) { var v ReplicationTaskType err := v.Decode(sr) return v, err } func _DomainTaskAttributes_Decode(sr stream.Reader) (*DomainTaskAttributes, error) { var v DomainTaskAttributes err := v.Decode(sr) return &v, err } func _SyncShardStatusTaskAttributes_Decode(sr stream.Reader) (*SyncShardStatusTaskAttributes, error) { var v SyncShardStatusTaskAttributes err := v.Decode(sr) return &v, err } func _SyncActivityTaskAttributes_Decode(sr stream.Reader) (*SyncActivityTaskAttributes, error) { var v SyncActivityTaskAttributes err := v.Decode(sr) return &v, err } func _HistoryTaskV2Attributes_Decode(sr stream.Reader) (*HistoryTaskV2Attributes, error) { var v HistoryTaskV2Attributes err := v.Decode(sr) return &v, err } // Decode deserializes a ReplicationTask struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReplicationTask struct could not be generated from the wire // representation. func (v *ReplicationTask) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x ReplicationTaskType x, err = _ReplicationTaskType_Decode(sr) v.TaskType = &x if err != nil { return err } case fh.ID == 11 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.SourceTaskId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.DomainTaskAttributes, err = _DomainTaskAttributes_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.SyncShardStatusTaskAttributes, err = _SyncShardStatusTaskAttributes_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.SyncActivityTaskAttributes, err = _SyncActivityTaskAttributes_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.HistoryTaskV2Attributes, err = _HistoryTaskV2Attributes_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TStruct: v.FailoverMarkerAttributes, err = _FailoverMarkerAttributes_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CreationTime = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReplicationTask // struct. func (v *ReplicationTask) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.TaskType != nil { fields[i] = fmt.Sprintf("TaskType: %v", *(v.TaskType)) i++ } if v.SourceTaskId != nil { fields[i] = fmt.Sprintf("SourceTaskId: %v", *(v.SourceTaskId)) i++ } if v.DomainTaskAttributes != nil { fields[i] = fmt.Sprintf("DomainTaskAttributes: %v", v.DomainTaskAttributes) i++ } if v.SyncShardStatusTaskAttributes != nil { fields[i] = fmt.Sprintf("SyncShardStatusTaskAttributes: %v", v.SyncShardStatusTaskAttributes) i++ } if v.SyncActivityTaskAttributes != nil { fields[i] = fmt.Sprintf("SyncActivityTaskAttributes: %v", v.SyncActivityTaskAttributes) i++ } if v.HistoryTaskV2Attributes != nil { fields[i] = fmt.Sprintf("HistoryTaskV2Attributes: %v", v.HistoryTaskV2Attributes) i++ } if v.FailoverMarkerAttributes != nil { fields[i] = fmt.Sprintf("FailoverMarkerAttributes: %v", v.FailoverMarkerAttributes) i++ } if v.CreationTime != nil { fields[i] = fmt.Sprintf("CreationTime: %v", *(v.CreationTime)) i++ } return fmt.Sprintf("ReplicationTask{%v}", strings.Join(fields[:i], ", ")) } func _ReplicationTaskType_EqualsPtr(lhs, rhs *ReplicationTaskType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ReplicationTask match the // provided ReplicationTask. // // This function performs a deep comparison. func (v *ReplicationTask) Equals(rhs *ReplicationTask) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_ReplicationTaskType_EqualsPtr(v.TaskType, rhs.TaskType) { return false } if !_I64_EqualsPtr(v.SourceTaskId, rhs.SourceTaskId) { return false } if !((v.DomainTaskAttributes == nil && rhs.DomainTaskAttributes == nil) || (v.DomainTaskAttributes != nil && rhs.DomainTaskAttributes != nil && v.DomainTaskAttributes.Equals(rhs.DomainTaskAttributes))) { return false } if !((v.SyncShardStatusTaskAttributes == nil && rhs.SyncShardStatusTaskAttributes == nil) || (v.SyncShardStatusTaskAttributes != nil && rhs.SyncShardStatusTaskAttributes != nil && v.SyncShardStatusTaskAttributes.Equals(rhs.SyncShardStatusTaskAttributes))) { return false } if !((v.SyncActivityTaskAttributes == nil && rhs.SyncActivityTaskAttributes == nil) || (v.SyncActivityTaskAttributes != nil && rhs.SyncActivityTaskAttributes != nil && v.SyncActivityTaskAttributes.Equals(rhs.SyncActivityTaskAttributes))) { return false } if !((v.HistoryTaskV2Attributes == nil && rhs.HistoryTaskV2Attributes == nil) || (v.HistoryTaskV2Attributes != nil && rhs.HistoryTaskV2Attributes != nil && v.HistoryTaskV2Attributes.Equals(rhs.HistoryTaskV2Attributes))) { return false } if !((v.FailoverMarkerAttributes == nil && rhs.FailoverMarkerAttributes == nil) || (v.FailoverMarkerAttributes != nil && rhs.FailoverMarkerAttributes != nil && v.FailoverMarkerAttributes.Equals(rhs.FailoverMarkerAttributes))) { return false } if !_I64_EqualsPtr(v.CreationTime, rhs.CreationTime) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReplicationTask. func (v *ReplicationTask) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskType != nil { err = multierr.Append(err, enc.AddObject("taskType", *v.TaskType)) } if v.SourceTaskId != nil { enc.AddInt64("sourceTaskId", *v.SourceTaskId) } if v.DomainTaskAttributes != nil { err = multierr.Append(err, enc.AddObject("domainTaskAttributes", v.DomainTaskAttributes)) } if v.SyncShardStatusTaskAttributes != nil { err = multierr.Append(err, enc.AddObject("syncShardStatusTaskAttributes", v.SyncShardStatusTaskAttributes)) } if v.SyncActivityTaskAttributes != nil { err = multierr.Append(err, enc.AddObject("syncActivityTaskAttributes", v.SyncActivityTaskAttributes)) } if v.HistoryTaskV2Attributes != nil { err = multierr.Append(err, enc.AddObject("historyTaskV2Attributes", v.HistoryTaskV2Attributes)) } if v.FailoverMarkerAttributes != nil { err = multierr.Append(err, enc.AddObject("failoverMarkerAttributes", v.FailoverMarkerAttributes)) } if v.CreationTime != nil { enc.AddInt64("creationTime", *v.CreationTime) } return err } // GetTaskType returns the value of TaskType if it is set or its // zero value if it is unset. func (v *ReplicationTask) GetTaskType() (o ReplicationTaskType) { if v != nil && v.TaskType != nil { return *v.TaskType } return } // IsSetTaskType returns true if TaskType is not nil. func (v *ReplicationTask) IsSetTaskType() bool { return v != nil && v.TaskType != nil } // GetSourceTaskId returns the value of SourceTaskId if it is set or its // zero value if it is unset. func (v *ReplicationTask) GetSourceTaskId() (o int64) { if v != nil && v.SourceTaskId != nil { return *v.SourceTaskId } return } // IsSetSourceTaskId returns true if SourceTaskId is not nil. func (v *ReplicationTask) IsSetSourceTaskId() bool { return v != nil && v.SourceTaskId != nil } // GetDomainTaskAttributes returns the value of DomainTaskAttributes if it is set or its // zero value if it is unset. func (v *ReplicationTask) GetDomainTaskAttributes() (o *DomainTaskAttributes) { if v != nil && v.DomainTaskAttributes != nil { return v.DomainTaskAttributes } return } // IsSetDomainTaskAttributes returns true if DomainTaskAttributes is not nil. func (v *ReplicationTask) IsSetDomainTaskAttributes() bool { return v != nil && v.DomainTaskAttributes != nil } // GetSyncShardStatusTaskAttributes returns the value of SyncShardStatusTaskAttributes if it is set or its // zero value if it is unset. func (v *ReplicationTask) GetSyncShardStatusTaskAttributes() (o *SyncShardStatusTaskAttributes) { if v != nil && v.SyncShardStatusTaskAttributes != nil { return v.SyncShardStatusTaskAttributes } return } // IsSetSyncShardStatusTaskAttributes returns true if SyncShardStatusTaskAttributes is not nil. func (v *ReplicationTask) IsSetSyncShardStatusTaskAttributes() bool { return v != nil && v.SyncShardStatusTaskAttributes != nil } // GetSyncActivityTaskAttributes returns the value of SyncActivityTaskAttributes if it is set or its // zero value if it is unset. func (v *ReplicationTask) GetSyncActivityTaskAttributes() (o *SyncActivityTaskAttributes) { if v != nil && v.SyncActivityTaskAttributes != nil { return v.SyncActivityTaskAttributes } return } // IsSetSyncActivityTaskAttributes returns true if SyncActivityTaskAttributes is not nil. func (v *ReplicationTask) IsSetSyncActivityTaskAttributes() bool { return v != nil && v.SyncActivityTaskAttributes != nil } // GetHistoryTaskV2Attributes returns the value of HistoryTaskV2Attributes if it is set or its // zero value if it is unset. func (v *ReplicationTask) GetHistoryTaskV2Attributes() (o *HistoryTaskV2Attributes) { if v != nil && v.HistoryTaskV2Attributes != nil { return v.HistoryTaskV2Attributes } return } // IsSetHistoryTaskV2Attributes returns true if HistoryTaskV2Attributes is not nil. func (v *ReplicationTask) IsSetHistoryTaskV2Attributes() bool { return v != nil && v.HistoryTaskV2Attributes != nil } // GetFailoverMarkerAttributes returns the value of FailoverMarkerAttributes if it is set or its // zero value if it is unset. func (v *ReplicationTask) GetFailoverMarkerAttributes() (o *FailoverMarkerAttributes) { if v != nil && v.FailoverMarkerAttributes != nil { return v.FailoverMarkerAttributes } return } // IsSetFailoverMarkerAttributes returns true if FailoverMarkerAttributes is not nil. func (v *ReplicationTask) IsSetFailoverMarkerAttributes() bool { return v != nil && v.FailoverMarkerAttributes != nil } // GetCreationTime returns the value of CreationTime if it is set or its // zero value if it is unset. func (v *ReplicationTask) GetCreationTime() (o int64) { if v != nil && v.CreationTime != nil { return *v.CreationTime } return } // IsSetCreationTime returns true if CreationTime is not nil. func (v *ReplicationTask) IsSetCreationTime() bool { return v != nil && v.CreationTime != nil } type ReplicationTaskInfo struct { DomainID *string `json:"domainID,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID *string `json:"runID,omitempty"` TaskType *int16 `json:"taskType,omitempty"` TaskID *int64 `json:"taskID,omitempty"` Version *int64 `json:"version,omitempty"` FirstEventID *int64 `json:"firstEventID,omitempty"` NextEventID *int64 `json:"nextEventID,omitempty"` ScheduledID *int64 `json:"scheduledID,omitempty"` } // ToWire translates a ReplicationTaskInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReplicationTaskInfo) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.DomainID != nil { w, err = wire.NewValueString(*(v.DomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueString(*(v.RunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.TaskType != nil { w, err = wire.NewValueI16(*(v.TaskType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.TaskID != nil { w, err = wire.NewValueI64(*(v.TaskID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.FirstEventID != nil { w, err = wire.NewValueI64(*(v.FirstEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.NextEventID != nil { w, err = wire.NewValueI64(*(v.NextEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.ScheduledID != nil { w, err = wire.NewValueI64(*(v.ScheduledID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ReplicationTaskInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReplicationTaskInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReplicationTaskInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReplicationTaskInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.TaskType = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskID = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FirstEventID = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NextEventID = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledID = &x if err != nil { return err } } } } return nil } // Encode serializes a ReplicationTaskInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReplicationTaskInfo struct could not be encoded. func (v *ReplicationTaskInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.TaskType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FirstEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NextEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ReplicationTaskInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReplicationTaskInfo struct could not be generated from the wire // representation. func (v *ReplicationTaskInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.TaskType = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskID = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FirstEventID = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NextEventID = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReplicationTaskInfo // struct. func (v *ReplicationTaskInfo) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", *(v.DomainID)) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", *(v.RunID)) i++ } if v.TaskType != nil { fields[i] = fmt.Sprintf("TaskType: %v", *(v.TaskType)) i++ } if v.TaskID != nil { fields[i] = fmt.Sprintf("TaskID: %v", *(v.TaskID)) i++ } if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.FirstEventID != nil { fields[i] = fmt.Sprintf("FirstEventID: %v", *(v.FirstEventID)) i++ } if v.NextEventID != nil { fields[i] = fmt.Sprintf("NextEventID: %v", *(v.NextEventID)) i++ } if v.ScheduledID != nil { fields[i] = fmt.Sprintf("ScheduledID: %v", *(v.ScheduledID)) i++ } return fmt.Sprintf("ReplicationTaskInfo{%v}", strings.Join(fields[:i], ", ")) } func _I16_EqualsPtr(lhs, rhs *int16) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ReplicationTaskInfo match the // provided ReplicationTaskInfo. // // This function performs a deep comparison. func (v *ReplicationTaskInfo) Equals(rhs *ReplicationTaskInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainID, rhs.DomainID) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !_String_EqualsPtr(v.RunID, rhs.RunID) { return false } if !_I16_EqualsPtr(v.TaskType, rhs.TaskType) { return false } if !_I64_EqualsPtr(v.TaskID, rhs.TaskID) { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.FirstEventID, rhs.FirstEventID) { return false } if !_I64_EqualsPtr(v.NextEventID, rhs.NextEventID) { return false } if !_I64_EqualsPtr(v.ScheduledID, rhs.ScheduledID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReplicationTaskInfo. func (v *ReplicationTaskInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainID != nil { enc.AddString("domainID", *v.DomainID) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", *v.RunID) } if v.TaskType != nil { enc.AddInt16("taskType", *v.TaskType) } if v.TaskID != nil { enc.AddInt64("taskID", *v.TaskID) } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.FirstEventID != nil { enc.AddInt64("firstEventID", *v.FirstEventID) } if v.NextEventID != nil { enc.AddInt64("nextEventID", *v.NextEventID) } if v.ScheduledID != nil { enc.AddInt64("scheduledID", *v.ScheduledID) } return err } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetDomainID() (o string) { if v != nil && v.DomainID != nil { return *v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *ReplicationTaskInfo) IsSetDomainID() bool { return v != nil && v.DomainID != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *ReplicationTaskInfo) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetRunID() (o string) { if v != nil && v.RunID != nil { return *v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *ReplicationTaskInfo) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetTaskType returns the value of TaskType if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetTaskType() (o int16) { if v != nil && v.TaskType != nil { return *v.TaskType } return } // IsSetTaskType returns true if TaskType is not nil. func (v *ReplicationTaskInfo) IsSetTaskType() bool { return v != nil && v.TaskType != nil } // GetTaskID returns the value of TaskID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetTaskID() (o int64) { if v != nil && v.TaskID != nil { return *v.TaskID } return } // IsSetTaskID returns true if TaskID is not nil. func (v *ReplicationTaskInfo) IsSetTaskID() bool { return v != nil && v.TaskID != nil } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *ReplicationTaskInfo) IsSetVersion() bool { return v != nil && v.Version != nil } // GetFirstEventID returns the value of FirstEventID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetFirstEventID() (o int64) { if v != nil && v.FirstEventID != nil { return *v.FirstEventID } return } // IsSetFirstEventID returns true if FirstEventID is not nil. func (v *ReplicationTaskInfo) IsSetFirstEventID() bool { return v != nil && v.FirstEventID != nil } // GetNextEventID returns the value of NextEventID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetNextEventID() (o int64) { if v != nil && v.NextEventID != nil { return *v.NextEventID } return } // IsSetNextEventID returns true if NextEventID is not nil. func (v *ReplicationTaskInfo) IsSetNextEventID() bool { return v != nil && v.NextEventID != nil } // GetScheduledID returns the value of ScheduledID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetScheduledID() (o int64) { if v != nil && v.ScheduledID != nil { return *v.ScheduledID } return } // IsSetScheduledID returns true if ScheduledID is not nil. func (v *ReplicationTaskInfo) IsSetScheduledID() bool { return v != nil && v.ScheduledID != nil } type ReplicationTaskType int32 const ( ReplicationTaskTypeDomain ReplicationTaskType = 0 ReplicationTaskTypeHistory ReplicationTaskType = 1 ReplicationTaskTypeSyncShardStatus ReplicationTaskType = 2 ReplicationTaskTypeSyncActivity ReplicationTaskType = 3 ReplicationTaskTypeHistoryMetadata ReplicationTaskType = 4 ReplicationTaskTypeHistoryV2 ReplicationTaskType = 5 ReplicationTaskTypeFailoverMarker ReplicationTaskType = 6 ) // ReplicationTaskType_Values returns all recognized values of ReplicationTaskType. func ReplicationTaskType_Values() []ReplicationTaskType { return []ReplicationTaskType{ ReplicationTaskTypeDomain, ReplicationTaskTypeHistory, ReplicationTaskTypeSyncShardStatus, ReplicationTaskTypeSyncActivity, ReplicationTaskTypeHistoryMetadata, ReplicationTaskTypeHistoryV2, ReplicationTaskTypeFailoverMarker, } } // UnmarshalText tries to decode ReplicationTaskType from a byte slice // containing its name. // // var v ReplicationTaskType // err := v.UnmarshalText([]byte("Domain")) func (v *ReplicationTaskType) UnmarshalText(value []byte) error { switch s := string(value); s { case "Domain": *v = ReplicationTaskTypeDomain return nil case "History": *v = ReplicationTaskTypeHistory return nil case "SyncShardStatus": *v = ReplicationTaskTypeSyncShardStatus return nil case "SyncActivity": *v = ReplicationTaskTypeSyncActivity return nil case "HistoryMetadata": *v = ReplicationTaskTypeHistoryMetadata return nil case "HistoryV2": *v = ReplicationTaskTypeHistoryV2 return nil case "FailoverMarker": *v = ReplicationTaskTypeFailoverMarker return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ReplicationTaskType", err) } *v = ReplicationTaskType(val) return nil } } // MarshalText encodes ReplicationTaskType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v ReplicationTaskType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("Domain"), nil case 1: return []byte("History"), nil case 2: return []byte("SyncShardStatus"), nil case 3: return []byte("SyncActivity"), nil case 4: return []byte("HistoryMetadata"), nil case 5: return []byte("HistoryV2"), nil case 6: return []byte("FailoverMarker"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReplicationTaskType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v ReplicationTaskType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "Domain") case 1: enc.AddString("name", "History") case 2: enc.AddString("name", "SyncShardStatus") case 3: enc.AddString("name", "SyncActivity") case 4: enc.AddString("name", "HistoryMetadata") case 5: enc.AddString("name", "HistoryV2") case 6: enc.AddString("name", "FailoverMarker") } return nil } // Ptr returns a pointer to this enum value. func (v ReplicationTaskType) Ptr() *ReplicationTaskType { return &v } // Encode encodes ReplicationTaskType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v ReplicationTaskType // return v.Encode(sWriter) func (v ReplicationTaskType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates ReplicationTaskType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v ReplicationTaskType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes ReplicationTaskType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return ReplicationTaskType(0), err // } // // var v ReplicationTaskType // if err := v.FromWire(x); err != nil { // return ReplicationTaskType(0), err // } // return v, nil func (v *ReplicationTaskType) FromWire(w wire.Value) error { *v = (ReplicationTaskType)(w.GetI32()) return nil } // Decode reads off the encoded ReplicationTaskType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v ReplicationTaskType // if err := v.Decode(sReader); err != nil { // return ReplicationTaskType(0), err // } // return v, nil func (v *ReplicationTaskType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (ReplicationTaskType)(i) return nil } // String returns a readable string representation of ReplicationTaskType. func (v ReplicationTaskType) String() string { w := int32(v) switch w { case 0: return "Domain" case 1: return "History" case 2: return "SyncShardStatus" case 3: return "SyncActivity" case 4: return "HistoryMetadata" case 5: return "HistoryV2" case 6: return "FailoverMarker" } return fmt.Sprintf("ReplicationTaskType(%d)", w) } // Equals returns true if this ReplicationTaskType value matches the provided // value. func (v ReplicationTaskType) Equals(rhs ReplicationTaskType) bool { return v == rhs } // MarshalJSON serializes ReplicationTaskType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v ReplicationTaskType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"Domain\""), nil case 1: return ([]byte)("\"History\""), nil case 2: return ([]byte)("\"SyncShardStatus\""), nil case 3: return ([]byte)("\"SyncActivity\""), nil case 4: return ([]byte)("\"HistoryMetadata\""), nil case 5: return ([]byte)("\"HistoryV2\""), nil case 6: return ([]byte)("\"FailoverMarker\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode ReplicationTaskType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *ReplicationTaskType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "ReplicationTaskType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "ReplicationTaskType") } *v = (ReplicationTaskType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "ReplicationTaskType") } } type ReplicationToken struct { ShardID *int32 `json:"shardID,omitempty"` LastRetrievedMessageId *int64 `json:"lastRetrievedMessageId,omitempty"` LastProcessedMessageId *int64 `json:"lastProcessedMessageId,omitempty"` } // ToWire translates a ReplicationToken struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReplicationToken) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.ShardID != nil { w, err = wire.NewValueI32(*(v.ShardID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.LastRetrievedMessageId != nil { w, err = wire.NewValueI64(*(v.LastRetrievedMessageId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.LastProcessedMessageId != nil { w, err = wire.NewValueI64(*(v.LastProcessedMessageId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ReplicationToken struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReplicationToken struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReplicationToken // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReplicationToken) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShardID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastRetrievedMessageId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastProcessedMessageId = &x if err != nil { return err } } } } return nil } // Encode serializes a ReplicationToken struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReplicationToken struct could not be encoded. func (v *ReplicationToken) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ShardID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShardID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastRetrievedMessageId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastRetrievedMessageId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastProcessedMessageId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastProcessedMessageId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ReplicationToken struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReplicationToken struct could not be generated from the wire // representation. func (v *ReplicationToken) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShardID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastRetrievedMessageId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastProcessedMessageId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReplicationToken // struct. func (v *ReplicationToken) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.ShardID != nil { fields[i] = fmt.Sprintf("ShardID: %v", *(v.ShardID)) i++ } if v.LastRetrievedMessageId != nil { fields[i] = fmt.Sprintf("LastRetrievedMessageId: %v", *(v.LastRetrievedMessageId)) i++ } if v.LastProcessedMessageId != nil { fields[i] = fmt.Sprintf("LastProcessedMessageId: %v", *(v.LastProcessedMessageId)) i++ } return fmt.Sprintf("ReplicationToken{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ReplicationToken match the // provided ReplicationToken. // // This function performs a deep comparison. func (v *ReplicationToken) Equals(rhs *ReplicationToken) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.ShardID, rhs.ShardID) { return false } if !_I64_EqualsPtr(v.LastRetrievedMessageId, rhs.LastRetrievedMessageId) { return false } if !_I64_EqualsPtr(v.LastProcessedMessageId, rhs.LastProcessedMessageId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReplicationToken. func (v *ReplicationToken) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ShardID != nil { enc.AddInt32("shardID", *v.ShardID) } if v.LastRetrievedMessageId != nil { enc.AddInt64("lastRetrievedMessageId", *v.LastRetrievedMessageId) } if v.LastProcessedMessageId != nil { enc.AddInt64("lastProcessedMessageId", *v.LastProcessedMessageId) } return err } // GetShardID returns the value of ShardID if it is set or its // zero value if it is unset. func (v *ReplicationToken) GetShardID() (o int32) { if v != nil && v.ShardID != nil { return *v.ShardID } return } // IsSetShardID returns true if ShardID is not nil. func (v *ReplicationToken) IsSetShardID() bool { return v != nil && v.ShardID != nil } // GetLastRetrievedMessageId returns the value of LastRetrievedMessageId if it is set or its // zero value if it is unset. func (v *ReplicationToken) GetLastRetrievedMessageId() (o int64) { if v != nil && v.LastRetrievedMessageId != nil { return *v.LastRetrievedMessageId } return } // IsSetLastRetrievedMessageId returns true if LastRetrievedMessageId is not nil. func (v *ReplicationToken) IsSetLastRetrievedMessageId() bool { return v != nil && v.LastRetrievedMessageId != nil } // GetLastProcessedMessageId returns the value of LastProcessedMessageId if it is set or its // zero value if it is unset. func (v *ReplicationToken) GetLastProcessedMessageId() (o int64) { if v != nil && v.LastProcessedMessageId != nil { return *v.LastProcessedMessageId } return } // IsSetLastProcessedMessageId returns true if LastProcessedMessageId is not nil. func (v *ReplicationToken) IsSetLastProcessedMessageId() bool { return v != nil && v.LastProcessedMessageId != nil } type SyncActivityTaskAttributes struct { DomainId *string `json:"domainId,omitempty"` WorkflowId *string `json:"workflowId,omitempty"` RunId *string `json:"runId,omitempty"` Version *int64 `json:"version,omitempty"` ScheduledId *int64 `json:"scheduledId,omitempty"` ScheduledTime *int64 `json:"scheduledTime,omitempty"` StartedId *int64 `json:"startedId,omitempty"` StartedTime *int64 `json:"startedTime,omitempty"` LastHeartbeatTime *int64 `json:"lastHeartbeatTime,omitempty"` Details []byte `json:"details,omitempty"` Attempt *int32 `json:"attempt,omitempty"` LastFailureReason *string `json:"lastFailureReason,omitempty"` LastWorkerIdentity *string `json:"lastWorkerIdentity,omitempty"` LastFailureDetails []byte `json:"lastFailureDetails,omitempty"` VersionHistory *shared.VersionHistory `json:"versionHistory,omitempty"` } // ToWire translates a SyncActivityTaskAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SyncActivityTaskAttributes) ToWire() (wire.Value, error) { var ( fields [15]wire.Field i int = 0 w wire.Value err error ) if v.DomainId != nil { w, err = wire.NewValueString(*(v.DomainId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ScheduledId != nil { w, err = wire.NewValueI64(*(v.ScheduledId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ScheduledTime != nil { w, err = wire.NewValueI64(*(v.ScheduledTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.StartedId != nil { w, err = wire.NewValueI64(*(v.StartedId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.StartedTime != nil { w, err = wire.NewValueI64(*(v.StartedTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.LastHeartbeatTime != nil { w, err = wire.NewValueI64(*(v.LastHeartbeatTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI32(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.LastFailureReason != nil { w, err = wire.NewValueString(*(v.LastFailureReason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.LastWorkerIdentity != nil { w, err = wire.NewValueString(*(v.LastWorkerIdentity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.LastFailureDetails != nil { w, err = wire.NewValueBinary(v.LastFailureDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.VersionHistory != nil { w, err = v.VersionHistory.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _VersionHistory_Read(w wire.Value) (*shared.VersionHistory, error) { var v shared.VersionHistory err := v.FromWire(w) return &v, err } // FromWire deserializes a SyncActivityTaskAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SyncActivityTaskAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SyncActivityTaskAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SyncActivityTaskAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTime = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedId = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTime = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastHeartbeatTime = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 110: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Attempt = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.LastFailureReason = &x if err != nil { return err } } case 130: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.LastWorkerIdentity = &x if err != nil { return err } } case 140: if field.Value.Type() == wire.TBinary { v.LastFailureDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 150: if field.Value.Type() == wire.TStruct { v.VersionHistory, err = _VersionHistory_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a SyncActivityTaskAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SyncActivityTaskAttributes struct could not be encoded. func (v *SyncActivityTaskAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastHeartbeatTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastHeartbeatTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFailureReason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.LastFailureReason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastWorkerIdentity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.LastWorkerIdentity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFailureDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.LastFailureDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistory != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TStruct}); err != nil { return err } if err := v.VersionHistory.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _VersionHistory_Decode(sr stream.Reader) (*shared.VersionHistory, error) { var v shared.VersionHistory err := v.Decode(sr) return &v, err } // Decode deserializes a SyncActivityTaskAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SyncActivityTaskAttributes struct could not be generated from the wire // representation. func (v *SyncActivityTaskAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTime = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedId = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTime = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastHeartbeatTime = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Attempt = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.LastFailureReason = &x if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.LastWorkerIdentity = &x if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TBinary: v.LastFailureDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TStruct: v.VersionHistory, err = _VersionHistory_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SyncActivityTaskAttributes // struct. func (v *SyncActivityTaskAttributes) String() string { if v == nil { return "" } var fields [15]string i := 0 if v.DomainId != nil { fields[i] = fmt.Sprintf("DomainId: %v", *(v.DomainId)) i++ } if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.ScheduledId != nil { fields[i] = fmt.Sprintf("ScheduledId: %v", *(v.ScheduledId)) i++ } if v.ScheduledTime != nil { fields[i] = fmt.Sprintf("ScheduledTime: %v", *(v.ScheduledTime)) i++ } if v.StartedId != nil { fields[i] = fmt.Sprintf("StartedId: %v", *(v.StartedId)) i++ } if v.StartedTime != nil { fields[i] = fmt.Sprintf("StartedTime: %v", *(v.StartedTime)) i++ } if v.LastHeartbeatTime != nil { fields[i] = fmt.Sprintf("LastHeartbeatTime: %v", *(v.LastHeartbeatTime)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.LastFailureReason != nil { fields[i] = fmt.Sprintf("LastFailureReason: %v", *(v.LastFailureReason)) i++ } if v.LastWorkerIdentity != nil { fields[i] = fmt.Sprintf("LastWorkerIdentity: %v", *(v.LastWorkerIdentity)) i++ } if v.LastFailureDetails != nil { fields[i] = fmt.Sprintf("LastFailureDetails: %v", v.LastFailureDetails) i++ } if v.VersionHistory != nil { fields[i] = fmt.Sprintf("VersionHistory: %v", v.VersionHistory) i++ } return fmt.Sprintf("SyncActivityTaskAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SyncActivityTaskAttributes match the // provided SyncActivityTaskAttributes. // // This function performs a deep comparison. func (v *SyncActivityTaskAttributes) Equals(rhs *SyncActivityTaskAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainId, rhs.DomainId) { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.ScheduledId, rhs.ScheduledId) { return false } if !_I64_EqualsPtr(v.ScheduledTime, rhs.ScheduledTime) { return false } if !_I64_EqualsPtr(v.StartedId, rhs.StartedId) { return false } if !_I64_EqualsPtr(v.StartedTime, rhs.StartedTime) { return false } if !_I64_EqualsPtr(v.LastHeartbeatTime, rhs.LastHeartbeatTime) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_I32_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_String_EqualsPtr(v.LastFailureReason, rhs.LastFailureReason) { return false } if !_String_EqualsPtr(v.LastWorkerIdentity, rhs.LastWorkerIdentity) { return false } if !((v.LastFailureDetails == nil && rhs.LastFailureDetails == nil) || (v.LastFailureDetails != nil && rhs.LastFailureDetails != nil && bytes.Equal(v.LastFailureDetails, rhs.LastFailureDetails))) { return false } if !((v.VersionHistory == nil && rhs.VersionHistory == nil) || (v.VersionHistory != nil && rhs.VersionHistory != nil && v.VersionHistory.Equals(rhs.VersionHistory))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SyncActivityTaskAttributes. func (v *SyncActivityTaskAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainId != nil { enc.AddString("domainId", *v.DomainId) } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.RunId != nil { enc.AddString("runId", *v.RunId) } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.ScheduledId != nil { enc.AddInt64("scheduledId", *v.ScheduledId) } if v.ScheduledTime != nil { enc.AddInt64("scheduledTime", *v.ScheduledTime) } if v.StartedId != nil { enc.AddInt64("startedId", *v.StartedId) } if v.StartedTime != nil { enc.AddInt64("startedTime", *v.StartedTime) } if v.LastHeartbeatTime != nil { enc.AddInt64("lastHeartbeatTime", *v.LastHeartbeatTime) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Attempt != nil { enc.AddInt32("attempt", *v.Attempt) } if v.LastFailureReason != nil { enc.AddString("lastFailureReason", *v.LastFailureReason) } if v.LastWorkerIdentity != nil { enc.AddString("lastWorkerIdentity", *v.LastWorkerIdentity) } if v.LastFailureDetails != nil { enc.AddString("lastFailureDetails", base64.StdEncoding.EncodeToString(v.LastFailureDetails)) } if v.VersionHistory != nil { err = multierr.Append(err, enc.AddObject("versionHistory", v.VersionHistory)) } return err } // GetDomainId returns the value of DomainId if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetDomainId() (o string) { if v != nil && v.DomainId != nil { return *v.DomainId } return } // IsSetDomainId returns true if DomainId is not nil. func (v *SyncActivityTaskAttributes) IsSetDomainId() bool { return v != nil && v.DomainId != nil } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *SyncActivityTaskAttributes) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *SyncActivityTaskAttributes) IsSetRunId() bool { return v != nil && v.RunId != nil } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *SyncActivityTaskAttributes) IsSetVersion() bool { return v != nil && v.Version != nil } // GetScheduledId returns the value of ScheduledId if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetScheduledId() (o int64) { if v != nil && v.ScheduledId != nil { return *v.ScheduledId } return } // IsSetScheduledId returns true if ScheduledId is not nil. func (v *SyncActivityTaskAttributes) IsSetScheduledId() bool { return v != nil && v.ScheduledId != nil } // GetScheduledTime returns the value of ScheduledTime if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetScheduledTime() (o int64) { if v != nil && v.ScheduledTime != nil { return *v.ScheduledTime } return } // IsSetScheduledTime returns true if ScheduledTime is not nil. func (v *SyncActivityTaskAttributes) IsSetScheduledTime() bool { return v != nil && v.ScheduledTime != nil } // GetStartedId returns the value of StartedId if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetStartedId() (o int64) { if v != nil && v.StartedId != nil { return *v.StartedId } return } // IsSetStartedId returns true if StartedId is not nil. func (v *SyncActivityTaskAttributes) IsSetStartedId() bool { return v != nil && v.StartedId != nil } // GetStartedTime returns the value of StartedTime if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetStartedTime() (o int64) { if v != nil && v.StartedTime != nil { return *v.StartedTime } return } // IsSetStartedTime returns true if StartedTime is not nil. func (v *SyncActivityTaskAttributes) IsSetStartedTime() bool { return v != nil && v.StartedTime != nil } // GetLastHeartbeatTime returns the value of LastHeartbeatTime if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetLastHeartbeatTime() (o int64) { if v != nil && v.LastHeartbeatTime != nil { return *v.LastHeartbeatTime } return } // IsSetLastHeartbeatTime returns true if LastHeartbeatTime is not nil. func (v *SyncActivityTaskAttributes) IsSetLastHeartbeatTime() bool { return v != nil && v.LastHeartbeatTime != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *SyncActivityTaskAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetAttempt() (o int32) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *SyncActivityTaskAttributes) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetLastFailureReason returns the value of LastFailureReason if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetLastFailureReason() (o string) { if v != nil && v.LastFailureReason != nil { return *v.LastFailureReason } return } // IsSetLastFailureReason returns true if LastFailureReason is not nil. func (v *SyncActivityTaskAttributes) IsSetLastFailureReason() bool { return v != nil && v.LastFailureReason != nil } // GetLastWorkerIdentity returns the value of LastWorkerIdentity if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetLastWorkerIdentity() (o string) { if v != nil && v.LastWorkerIdentity != nil { return *v.LastWorkerIdentity } return } // IsSetLastWorkerIdentity returns true if LastWorkerIdentity is not nil. func (v *SyncActivityTaskAttributes) IsSetLastWorkerIdentity() bool { return v != nil && v.LastWorkerIdentity != nil } // GetLastFailureDetails returns the value of LastFailureDetails if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetLastFailureDetails() (o []byte) { if v != nil && v.LastFailureDetails != nil { return v.LastFailureDetails } return } // IsSetLastFailureDetails returns true if LastFailureDetails is not nil. func (v *SyncActivityTaskAttributes) IsSetLastFailureDetails() bool { return v != nil && v.LastFailureDetails != nil } // GetVersionHistory returns the value of VersionHistory if it is set or its // zero value if it is unset. func (v *SyncActivityTaskAttributes) GetVersionHistory() (o *shared.VersionHistory) { if v != nil && v.VersionHistory != nil { return v.VersionHistory } return } // IsSetVersionHistory returns true if VersionHistory is not nil. func (v *SyncActivityTaskAttributes) IsSetVersionHistory() bool { return v != nil && v.VersionHistory != nil } type SyncShardStatus struct { Timestamp *int64 `json:"timestamp,omitempty"` } // ToWire translates a SyncShardStatus struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SyncShardStatus) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Timestamp != nil { w, err = wire.NewValueI64(*(v.Timestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a SyncShardStatus struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SyncShardStatus struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SyncShardStatus // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SyncShardStatus) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Timestamp = &x if err != nil { return err } } } } return nil } // Encode serializes a SyncShardStatus struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SyncShardStatus struct could not be encoded. func (v *SyncShardStatus) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Timestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Timestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a SyncShardStatus struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SyncShardStatus struct could not be generated from the wire // representation. func (v *SyncShardStatus) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Timestamp = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SyncShardStatus // struct. func (v *SyncShardStatus) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Timestamp != nil { fields[i] = fmt.Sprintf("Timestamp: %v", *(v.Timestamp)) i++ } return fmt.Sprintf("SyncShardStatus{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SyncShardStatus match the // provided SyncShardStatus. // // This function performs a deep comparison. func (v *SyncShardStatus) Equals(rhs *SyncShardStatus) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.Timestamp, rhs.Timestamp) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SyncShardStatus. func (v *SyncShardStatus) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Timestamp != nil { enc.AddInt64("timestamp", *v.Timestamp) } return err } // GetTimestamp returns the value of Timestamp if it is set or its // zero value if it is unset. func (v *SyncShardStatus) GetTimestamp() (o int64) { if v != nil && v.Timestamp != nil { return *v.Timestamp } return } // IsSetTimestamp returns true if Timestamp is not nil. func (v *SyncShardStatus) IsSetTimestamp() bool { return v != nil && v.Timestamp != nil } type SyncShardStatusTaskAttributes struct { SourceCluster *string `json:"sourceCluster,omitempty"` ShardId *int64 `json:"shardId,omitempty"` Timestamp *int64 `json:"timestamp,omitempty"` } // ToWire translates a SyncShardStatusTaskAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SyncShardStatusTaskAttributes) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.SourceCluster != nil { w, err = wire.NewValueString(*(v.SourceCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ShardId != nil { w, err = wire.NewValueI64(*(v.ShardId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Timestamp != nil { w, err = wire.NewValueI64(*(v.Timestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a SyncShardStatusTaskAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SyncShardStatusTaskAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SyncShardStatusTaskAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SyncShardStatusTaskAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SourceCluster = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ShardId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Timestamp = &x if err != nil { return err } } } } return nil } // Encode serializes a SyncShardStatusTaskAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SyncShardStatusTaskAttributes struct could not be encoded. func (v *SyncShardStatusTaskAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SourceCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SourceCluster)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ShardId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Timestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Timestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a SyncShardStatusTaskAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SyncShardStatusTaskAttributes struct could not be generated from the wire // representation. func (v *SyncShardStatusTaskAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SourceCluster = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ShardId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Timestamp = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SyncShardStatusTaskAttributes // struct. func (v *SyncShardStatusTaskAttributes) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.SourceCluster != nil { fields[i] = fmt.Sprintf("SourceCluster: %v", *(v.SourceCluster)) i++ } if v.ShardId != nil { fields[i] = fmt.Sprintf("ShardId: %v", *(v.ShardId)) i++ } if v.Timestamp != nil { fields[i] = fmt.Sprintf("Timestamp: %v", *(v.Timestamp)) i++ } return fmt.Sprintf("SyncShardStatusTaskAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SyncShardStatusTaskAttributes match the // provided SyncShardStatusTaskAttributes. // // This function performs a deep comparison. func (v *SyncShardStatusTaskAttributes) Equals(rhs *SyncShardStatusTaskAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.SourceCluster, rhs.SourceCluster) { return false } if !_I64_EqualsPtr(v.ShardId, rhs.ShardId) { return false } if !_I64_EqualsPtr(v.Timestamp, rhs.Timestamp) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SyncShardStatusTaskAttributes. func (v *SyncShardStatusTaskAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SourceCluster != nil { enc.AddString("sourceCluster", *v.SourceCluster) } if v.ShardId != nil { enc.AddInt64("shardId", *v.ShardId) } if v.Timestamp != nil { enc.AddInt64("timestamp", *v.Timestamp) } return err } // GetSourceCluster returns the value of SourceCluster if it is set or its // zero value if it is unset. func (v *SyncShardStatusTaskAttributes) GetSourceCluster() (o string) { if v != nil && v.SourceCluster != nil { return *v.SourceCluster } return } // IsSetSourceCluster returns true if SourceCluster is not nil. func (v *SyncShardStatusTaskAttributes) IsSetSourceCluster() bool { return v != nil && v.SourceCluster != nil } // GetShardId returns the value of ShardId if it is set or its // zero value if it is unset. func (v *SyncShardStatusTaskAttributes) GetShardId() (o int64) { if v != nil && v.ShardId != nil { return *v.ShardId } return } // IsSetShardId returns true if ShardId is not nil. func (v *SyncShardStatusTaskAttributes) IsSetShardId() bool { return v != nil && v.ShardId != nil } // GetTimestamp returns the value of Timestamp if it is set or its // zero value if it is unset. func (v *SyncShardStatusTaskAttributes) GetTimestamp() (o int64) { if v != nil && v.Timestamp != nil { return *v.Timestamp } return } // IsSetTimestamp returns true if Timestamp is not nil. func (v *SyncShardStatusTaskAttributes) IsSetTimestamp() bool { return v != nil && v.Timestamp != nil } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "replicator", Package: "github.com/uber/cadence/.gen/go/replicator", FilePath: "replicator.thrift", SHA1: "5511dc1dfc4b6025f16d77cac580134c62303bd6", Includes: []*thriftreflect.ThriftModule{ shared.ThriftModule, }, Raw: rawIDL, } const rawIDL = "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nnamespace java com.uber.cadence.replicator\n\ninclude \"shared.thrift\"\n\nenum ReplicationTaskType {\n Domain\n History\n SyncShardStatus\n SyncActivity\n HistoryMetadata\n HistoryV2\n FailoverMarker\n}\n\nenum DomainOperation {\n Create\n Update\n Delete\n}\n\nstruct DomainTaskAttributes {\n 05: optional DomainOperation domainOperation\n 10: optional string id\n 20: optional shared.DomainInfo info\n 30: optional shared.DomainConfiguration config\n 40: optional shared.DomainReplicationConfiguration replicationConfig\n 50: optional i64 (js.type = \"Long\") configVersion\n 60: optional i64 (js.type = \"Long\") failoverVersion\n 70: optional i64 (js.type = \"Long\") previousFailoverVersion\n}\n\nstruct SyncShardStatusTaskAttributes {\n 10: optional string sourceCluster\n 20: optional i64 (js.type = \"Long\") shardId\n 30: optional i64 (js.type = \"Long\") timestamp\n}\n\nstruct SyncActivityTaskAttributes {\n 10: optional string domainId\n 20: optional string workflowId\n 30: optional string runId\n 40: optional i64 (js.type = \"Long\") version\n 50: optional i64 (js.type = \"Long\") scheduledId\n 60: optional i64 (js.type = \"Long\") scheduledTime\n 70: optional i64 (js.type = \"Long\") startedId\n 80: optional i64 (js.type = \"Long\") startedTime\n 90: optional i64 (js.type = \"Long\") lastHeartbeatTime\n 100: optional binary details\n 110: optional i32 attempt\n 120: optional string lastFailureReason\n 130: optional string lastWorkerIdentity\n 140: optional binary lastFailureDetails\n 150: optional shared.VersionHistory versionHistory\n}\n\nstruct HistoryTaskV2Attributes {\n 05: optional i64 (js.type = \"Long\") taskId\n 10: optional string domainId\n 20: optional string workflowId\n 30: optional string runId\n 40: optional list versionHistoryItems\n 50: optional shared.DataBlob events\n // new run events does not need version history since there is no prior events\n 70: optional shared.DataBlob newRunEvents\n}\n\nstruct FailoverMarkerAttributes{\n\t10: optional string domainID\n\t20: optional i64 (js.type = \"Long\") failoverVersion\n\t30: optional i64 (js.type = \"Long\") creationTime\n}\n\nstruct FailoverMarkers{\n\t10: optional list failoverMarkers\n}\n\nstruct ReplicationTask {\n 10: optional ReplicationTaskType taskType\n 11: optional i64 (js.type = \"Long\") sourceTaskId\n 20: optional DomainTaskAttributes domainTaskAttributes\n 40: optional SyncShardStatusTaskAttributes syncShardStatusTaskAttributes\n 50: optional SyncActivityTaskAttributes syncActivityTaskAttributes\n 70: optional HistoryTaskV2Attributes historyTaskV2Attributes\n 80: optional FailoverMarkerAttributes failoverMarkerAttributes\n 90: optional i64 (js.type = \"Long\") creationTime\n}\n\nstruct ReplicationToken {\n 10: optional i32 shardID\n // lastRetrivedMessageId is where the next fetch should begin with\n 20: optional i64 (js.type = \"Long\") lastRetrievedMessageId\n // lastProcessedMessageId is the last messageId that is processed on the passive side.\n // This can be different than lastRetrievedMessageId if passive side supports prefetching messages.\n 30: optional i64 (js.type = \"Long\") lastProcessedMessageId\n}\n\nstruct SyncShardStatus {\n 10: optional i64 (js.type = \"Long\") timestamp\n}\n\nstruct ReplicationMessages {\n 10: optional list replicationTasks\n // This can be different than the last taskId in the above list, because sender can decide to skip tasks (e.g. for completed workflows).\n 20: optional i64 (js.type = \"Long\") lastRetrievedMessageId\n 30: optional bool hasMore // Hint for flow control\n 40: optional SyncShardStatus syncShardStatus\n}\n\nstruct ReplicationTaskInfo {\n 10: optional string domainID\n 20: optional string workflowID\n 30: optional string runID\n 40: optional i16 taskType\n 50: optional i64 (js.type = \"Long\") taskID\n 60: optional i64 (js.type = \"Long\") version\n 70: optional i64 (js.type = \"Long\") firstEventID\n 80: optional i64 (js.type = \"Long\") nextEventID\n 90: optional i64 (js.type = \"Long\") scheduledID\n}\n\nstruct GetReplicationMessagesRequest {\n 10: optional list tokens\n 20: optional string clusterName\n}\n\nstruct GetReplicationMessagesResponse {\n 10: optional map messagesByShard\n}\n\nstruct GetDomainReplicationMessagesRequest {\n // lastRetrievedMessageId is where the next fetch should begin with\n 10: optional i64 (js.type = \"Long\") lastRetrievedMessageId\n // lastProcessedMessageId is the last messageId that is processed on the passive side.\n // This can be different than lastRetrievedMessageId if passive side supports prefetching messages.\n 20: optional i64 (js.type = \"Long\") lastProcessedMessageId\n // clusterName is the name of the pulling cluster\n 30: optional string clusterName\n}\n\nstruct GetDomainReplicationMessagesResponse {\n 10: optional ReplicationMessages messages\n}\n\nstruct GetDLQReplicationMessagesRequest {\n 10: optional list taskInfos\n}\n\nstruct GetDLQReplicationMessagesResponse {\n 10: optional list replicationTasks\n}\n\nenum DLQType {\n Replication,\n Domain,\n}\n\nstruct ReadDLQMessagesRequest{\n 10: optional DLQType type\n 20: optional i32 shardID\n 30: optional string sourceCluster\n 40: optional i64 (js.type = \"Long\") inclusiveEndMessageID\n 50: optional i32 maximumPageSize\n 60: optional binary nextPageToken\n}\n\nstruct ReadDLQMessagesResponse{\n 10: optional DLQType type\n 20: optional list replicationTasks\n 30: optional binary nextPageToken\n 40: optional list replicationTasksInfo\n}\n\nstruct PurgeDLQMessagesRequest{\n 10: optional DLQType type\n 20: optional i32 shardID\n 30: optional string sourceCluster\n 40: optional i64 (js.type = \"Long\") inclusiveEndMessageID\n}\n\nstruct MergeDLQMessagesRequest{\n 10: optional DLQType type\n 20: optional i32 shardID\n 30: optional string sourceCluster\n 40: optional i64 (js.type = \"Long\") inclusiveEndMessageID\n 50: optional i32 maximumPageSize\n 60: optional binary nextPageToken\n}\n\nstruct MergeDLQMessagesResponse{\n 10: optional binary nextPageToken\n}\n" ================================================ FILE: .gen/go/replicator/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package replicator ================================================ FILE: .gen/go/shadower/shadower.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package shadower import ( bytes "bytes" base64 "encoding/base64" json "encoding/json" fmt "fmt" math "math" strconv "strconv" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" shared "github.com/uber/cadence/.gen/go/shared" ) const ErrNonRetryableType string = "com.uber.cadence.internal.shadowing.NonRetryableException" const ErrReasonDomainNotExists string = "domain not exists" const ErrReasonInvalidQuery string = "invalid visibility query" const ErrReasonWorkflowTypeNotRegistered string = "workflow type not registered" const LocalDomainName string = "cadence-shadower" const ReplayWorkflowActivityName string = "replayWorkflowActivity" const ScanWorkflowActivityName string = "scanWorkflowActivity" const TaskList string = "cadence-shadower-tl" const WorkflowIDSuffix string = "-shadow-workflow" const WorkflowName string = "cadence-shadow-workflow" type ExitCondition struct { ExpirationIntervalInSeconds *int32 `json:"expirationIntervalInSeconds,omitempty"` ShadowCount *int32 `json:"shadowCount,omitempty"` } // ToWire translates a ExitCondition struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ExitCondition) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ExpirationIntervalInSeconds != nil { w, err = wire.NewValueI32(*(v.ExpirationIntervalInSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ShadowCount != nil { w, err = wire.NewValueI32(*(v.ShadowCount)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ExitCondition struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ExitCondition struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ExitCondition // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ExitCondition) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ExpirationIntervalInSeconds = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShadowCount = &x if err != nil { return err } } } } return nil } // Encode serializes a ExitCondition struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ExitCondition struct could not be encoded. func (v *ExitCondition) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ExpirationIntervalInSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ExpirationIntervalInSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShadowCount != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShadowCount)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ExitCondition struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ExitCondition struct could not be generated from the wire // representation. func (v *ExitCondition) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ExpirationIntervalInSeconds = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShadowCount = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ExitCondition // struct. func (v *ExitCondition) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ExpirationIntervalInSeconds != nil { fields[i] = fmt.Sprintf("ExpirationIntervalInSeconds: %v", *(v.ExpirationIntervalInSeconds)) i++ } if v.ShadowCount != nil { fields[i] = fmt.Sprintf("ShadowCount: %v", *(v.ShadowCount)) i++ } return fmt.Sprintf("ExitCondition{%v}", strings.Join(fields[:i], ", ")) } func _I32_EqualsPtr(lhs, rhs *int32) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ExitCondition match the // provided ExitCondition. // // This function performs a deep comparison. func (v *ExitCondition) Equals(rhs *ExitCondition) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.ExpirationIntervalInSeconds, rhs.ExpirationIntervalInSeconds) { return false } if !_I32_EqualsPtr(v.ShadowCount, rhs.ShadowCount) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ExitCondition. func (v *ExitCondition) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ExpirationIntervalInSeconds != nil { enc.AddInt32("expirationIntervalInSeconds", *v.ExpirationIntervalInSeconds) } if v.ShadowCount != nil { enc.AddInt32("shadowCount", *v.ShadowCount) } return err } // GetExpirationIntervalInSeconds returns the value of ExpirationIntervalInSeconds if it is set or its // zero value if it is unset. func (v *ExitCondition) GetExpirationIntervalInSeconds() (o int32) { if v != nil && v.ExpirationIntervalInSeconds != nil { return *v.ExpirationIntervalInSeconds } return } // IsSetExpirationIntervalInSeconds returns true if ExpirationIntervalInSeconds is not nil. func (v *ExitCondition) IsSetExpirationIntervalInSeconds() bool { return v != nil && v.ExpirationIntervalInSeconds != nil } // GetShadowCount returns the value of ShadowCount if it is set or its // zero value if it is unset. func (v *ExitCondition) GetShadowCount() (o int32) { if v != nil && v.ShadowCount != nil { return *v.ShadowCount } return } // IsSetShadowCount returns true if ShadowCount is not nil. func (v *ExitCondition) IsSetShadowCount() bool { return v != nil && v.ShadowCount != nil } type Mode int32 const ( ModeNormal Mode = 0 ModeContinuous Mode = 1 ) // Mode_Values returns all recognized values of Mode. func Mode_Values() []Mode { return []Mode{ ModeNormal, ModeContinuous, } } // UnmarshalText tries to decode Mode from a byte slice // containing its name. // // var v Mode // err := v.UnmarshalText([]byte("Normal")) func (v *Mode) UnmarshalText(value []byte) error { switch s := string(value); s { case "Normal": *v = ModeNormal return nil case "Continuous": *v = ModeContinuous return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "Mode", err) } *v = Mode(val) return nil } } // MarshalText encodes Mode to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v Mode) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("Normal"), nil case 1: return []byte("Continuous"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of Mode. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v Mode) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "Normal") case 1: enc.AddString("name", "Continuous") } return nil } // Ptr returns a pointer to this enum value. func (v Mode) Ptr() *Mode { return &v } // Encode encodes Mode directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v Mode // return v.Encode(sWriter) func (v Mode) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates Mode into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v Mode) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes Mode from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return Mode(0), err // } // // var v Mode // if err := v.FromWire(x); err != nil { // return Mode(0), err // } // return v, nil func (v *Mode) FromWire(w wire.Value) error { *v = (Mode)(w.GetI32()) return nil } // Decode reads off the encoded Mode directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v Mode // if err := v.Decode(sReader); err != nil { // return Mode(0), err // } // return v, nil func (v *Mode) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (Mode)(i) return nil } // String returns a readable string representation of Mode. func (v Mode) String() string { w := int32(v) switch w { case 0: return "Normal" case 1: return "Continuous" } return fmt.Sprintf("Mode(%d)", w) } // Equals returns true if this Mode value matches the provided // value. func (v Mode) Equals(rhs Mode) bool { return v == rhs } // MarshalJSON serializes Mode into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v Mode) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"Normal\""), nil case 1: return ([]byte)("\"Continuous\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode Mode from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *Mode) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "Mode") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "Mode") } *v = (Mode)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "Mode") } } type ReplayWorkflowActivityParams struct { Domain *string `json:"domain,omitempty"` Executions []*shared.WorkflowExecution `json:"executions,omitempty"` } type _List_WorkflowExecution_ValueList []*shared.WorkflowExecution func (v _List_WorkflowExecution_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*shared.WorkflowExecution', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_WorkflowExecution_ValueList) Size() int { return len(v) } func (_List_WorkflowExecution_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_WorkflowExecution_ValueList) Close() {} // ToWire translates a ReplayWorkflowActivityParams struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReplayWorkflowActivityParams) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Executions != nil { w, err = wire.NewValueList(_List_WorkflowExecution_ValueList(v.Executions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WorkflowExecution_Read(w wire.Value) (*shared.WorkflowExecution, error) { var v shared.WorkflowExecution err := v.FromWire(w) return &v, err } func _List_WorkflowExecution_Read(l wire.ValueList) ([]*shared.WorkflowExecution, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*shared.WorkflowExecution, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _WorkflowExecution_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a ReplayWorkflowActivityParams struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReplayWorkflowActivityParams struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReplayWorkflowActivityParams // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReplayWorkflowActivityParams) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Executions, err = _List_WorkflowExecution_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_WorkflowExecution_Encode(val []*shared.WorkflowExecution, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*shared.WorkflowExecution', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a ReplayWorkflowActivityParams struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReplayWorkflowActivityParams struct could not be encoded. func (v *ReplayWorkflowActivityParams) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Executions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_WorkflowExecution_Encode(v.Executions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _WorkflowExecution_Decode(sr stream.Reader) (*shared.WorkflowExecution, error) { var v shared.WorkflowExecution err := v.Decode(sr) return &v, err } func _List_WorkflowExecution_Decode(sr stream.Reader) ([]*shared.WorkflowExecution, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*shared.WorkflowExecution, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _WorkflowExecution_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ReplayWorkflowActivityParams struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReplayWorkflowActivityParams struct could not be generated from the wire // representation. func (v *ReplayWorkflowActivityParams) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Executions, err = _List_WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReplayWorkflowActivityParams // struct. func (v *ReplayWorkflowActivityParams) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Executions != nil { fields[i] = fmt.Sprintf("Executions: %v", v.Executions) i++ } return fmt.Sprintf("ReplayWorkflowActivityParams{%v}", strings.Join(fields[:i], ", ")) } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _List_WorkflowExecution_Equals(lhs, rhs []*shared.WorkflowExecution) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this ReplayWorkflowActivityParams match the // provided ReplayWorkflowActivityParams. // // This function performs a deep comparison. func (v *ReplayWorkflowActivityParams) Equals(rhs *ReplayWorkflowActivityParams) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Executions == nil && rhs.Executions == nil) || (v.Executions != nil && rhs.Executions != nil && _List_WorkflowExecution_Equals(v.Executions, rhs.Executions))) { return false } return true } type _List_WorkflowExecution_Zapper []*shared.WorkflowExecution // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_WorkflowExecution_Zapper. func (l _List_WorkflowExecution_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReplayWorkflowActivityParams. func (v *ReplayWorkflowActivityParams) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Executions != nil { err = multierr.Append(err, enc.AddArray("executions", (_List_WorkflowExecution_Zapper)(v.Executions))) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ReplayWorkflowActivityParams) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ReplayWorkflowActivityParams) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecutions returns the value of Executions if it is set or its // zero value if it is unset. func (v *ReplayWorkflowActivityParams) GetExecutions() (o []*shared.WorkflowExecution) { if v != nil && v.Executions != nil { return v.Executions } return } // IsSetExecutions returns true if Executions is not nil. func (v *ReplayWorkflowActivityParams) IsSetExecutions() bool { return v != nil && v.Executions != nil } type ReplayWorkflowActivityResult struct { Succeeded *int32 `json:"succeeded,omitempty"` Skipped *int32 `json:"skipped,omitempty"` Failed *int32 `json:"failed,omitempty"` } // ToWire translates a ReplayWorkflowActivityResult struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReplayWorkflowActivityResult) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Succeeded != nil { w, err = wire.NewValueI32(*(v.Succeeded)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Skipped != nil { w, err = wire.NewValueI32(*(v.Skipped)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Failed != nil { w, err = wire.NewValueI32(*(v.Failed)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ReplayWorkflowActivityResult struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReplayWorkflowActivityResult struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReplayWorkflowActivityResult // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReplayWorkflowActivityResult) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Succeeded = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Skipped = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Failed = &x if err != nil { return err } } } } return nil } // Encode serializes a ReplayWorkflowActivityResult struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReplayWorkflowActivityResult struct could not be encoded. func (v *ReplayWorkflowActivityResult) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Succeeded != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Succeeded)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Skipped != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Skipped)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Failed != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Failed)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ReplayWorkflowActivityResult struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReplayWorkflowActivityResult struct could not be generated from the wire // representation. func (v *ReplayWorkflowActivityResult) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Succeeded = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Skipped = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Failed = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReplayWorkflowActivityResult // struct. func (v *ReplayWorkflowActivityResult) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Succeeded != nil { fields[i] = fmt.Sprintf("Succeeded: %v", *(v.Succeeded)) i++ } if v.Skipped != nil { fields[i] = fmt.Sprintf("Skipped: %v", *(v.Skipped)) i++ } if v.Failed != nil { fields[i] = fmt.Sprintf("Failed: %v", *(v.Failed)) i++ } return fmt.Sprintf("ReplayWorkflowActivityResult{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ReplayWorkflowActivityResult match the // provided ReplayWorkflowActivityResult. // // This function performs a deep comparison. func (v *ReplayWorkflowActivityResult) Equals(rhs *ReplayWorkflowActivityResult) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.Succeeded, rhs.Succeeded) { return false } if !_I32_EqualsPtr(v.Skipped, rhs.Skipped) { return false } if !_I32_EqualsPtr(v.Failed, rhs.Failed) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReplayWorkflowActivityResult. func (v *ReplayWorkflowActivityResult) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Succeeded != nil { enc.AddInt32("succeeded", *v.Succeeded) } if v.Skipped != nil { enc.AddInt32("skipped", *v.Skipped) } if v.Failed != nil { enc.AddInt32("failed", *v.Failed) } return err } // GetSucceeded returns the value of Succeeded if it is set or its // zero value if it is unset. func (v *ReplayWorkflowActivityResult) GetSucceeded() (o int32) { if v != nil && v.Succeeded != nil { return *v.Succeeded } return } // IsSetSucceeded returns true if Succeeded is not nil. func (v *ReplayWorkflowActivityResult) IsSetSucceeded() bool { return v != nil && v.Succeeded != nil } // GetSkipped returns the value of Skipped if it is set or its // zero value if it is unset. func (v *ReplayWorkflowActivityResult) GetSkipped() (o int32) { if v != nil && v.Skipped != nil { return *v.Skipped } return } // IsSetSkipped returns true if Skipped is not nil. func (v *ReplayWorkflowActivityResult) IsSetSkipped() bool { return v != nil && v.Skipped != nil } // GetFailed returns the value of Failed if it is set or its // zero value if it is unset. func (v *ReplayWorkflowActivityResult) GetFailed() (o int32) { if v != nil && v.Failed != nil { return *v.Failed } return } // IsSetFailed returns true if Failed is not nil. func (v *ReplayWorkflowActivityResult) IsSetFailed() bool { return v != nil && v.Failed != nil } type ScanWorkflowActivityParams struct { Domain *string `json:"domain,omitempty"` WorkflowQuery *string `json:"workflowQuery,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` PageSize *int32 `json:"pageSize,omitempty"` SamplingRate *float64 `json:"samplingRate,omitempty"` } // ToWire translates a ScanWorkflowActivityParams struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ScanWorkflowActivityParams) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowQuery != nil { w, err = wire.NewValueString(*(v.WorkflowQuery)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.PageSize != nil { w, err = wire.NewValueI32(*(v.PageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.SamplingRate != nil { w, err = wire.NewValueDouble(*(v.SamplingRate)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ScanWorkflowActivityParams struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ScanWorkflowActivityParams struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ScanWorkflowActivityParams // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ScanWorkflowActivityParams) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowQuery = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.PageSize = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TDouble { var x float64 x, err = field.Value.GetDouble(), error(nil) v.SamplingRate = &x if err != nil { return err } } } } return nil } // Encode serializes a ScanWorkflowActivityParams struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ScanWorkflowActivityParams struct could not be encoded. func (v *ScanWorkflowActivityParams) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowQuery != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowQuery)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.PageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SamplingRate != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(*(v.SamplingRate)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ScanWorkflowActivityParams struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ScanWorkflowActivityParams struct could not be generated from the wire // representation. func (v *ScanWorkflowActivityParams) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowQuery = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.PageSize = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TDouble: var x float64 x, err = sr.ReadDouble() v.SamplingRate = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ScanWorkflowActivityParams // struct. func (v *ScanWorkflowActivityParams) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowQuery != nil { fields[i] = fmt.Sprintf("WorkflowQuery: %v", *(v.WorkflowQuery)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.PageSize != nil { fields[i] = fmt.Sprintf("PageSize: %v", *(v.PageSize)) i++ } if v.SamplingRate != nil { fields[i] = fmt.Sprintf("SamplingRate: %v", *(v.SamplingRate)) i++ } return fmt.Sprintf("ScanWorkflowActivityParams{%v}", strings.Join(fields[:i], ", ")) } func _Double_EqualsPtr(lhs, rhs *float64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ScanWorkflowActivityParams match the // provided ScanWorkflowActivityParams. // // This function performs a deep comparison. func (v *ScanWorkflowActivityParams) Equals(rhs *ScanWorkflowActivityParams) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowQuery, rhs.WorkflowQuery) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !_I32_EqualsPtr(v.PageSize, rhs.PageSize) { return false } if !_Double_EqualsPtr(v.SamplingRate, rhs.SamplingRate) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ScanWorkflowActivityParams. func (v *ScanWorkflowActivityParams) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowQuery != nil { enc.AddString("workflowQuery", *v.WorkflowQuery) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.PageSize != nil { enc.AddInt32("pageSize", *v.PageSize) } if v.SamplingRate != nil { enc.AddFloat64("samplingRate", *v.SamplingRate) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ScanWorkflowActivityParams) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ScanWorkflowActivityParams) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowQuery returns the value of WorkflowQuery if it is set or its // zero value if it is unset. func (v *ScanWorkflowActivityParams) GetWorkflowQuery() (o string) { if v != nil && v.WorkflowQuery != nil { return *v.WorkflowQuery } return } // IsSetWorkflowQuery returns true if WorkflowQuery is not nil. func (v *ScanWorkflowActivityParams) IsSetWorkflowQuery() bool { return v != nil && v.WorkflowQuery != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ScanWorkflowActivityParams) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ScanWorkflowActivityParams) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetPageSize returns the value of PageSize if it is set or its // zero value if it is unset. func (v *ScanWorkflowActivityParams) GetPageSize() (o int32) { if v != nil && v.PageSize != nil { return *v.PageSize } return } // IsSetPageSize returns true if PageSize is not nil. func (v *ScanWorkflowActivityParams) IsSetPageSize() bool { return v != nil && v.PageSize != nil } // GetSamplingRate returns the value of SamplingRate if it is set or its // zero value if it is unset. func (v *ScanWorkflowActivityParams) GetSamplingRate() (o float64) { if v != nil && v.SamplingRate != nil { return *v.SamplingRate } return } // IsSetSamplingRate returns true if SamplingRate is not nil. func (v *ScanWorkflowActivityParams) IsSetSamplingRate() bool { return v != nil && v.SamplingRate != nil } type ScanWorkflowActivityResult struct { Executions []*shared.WorkflowExecution `json:"executions,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // ToWire translates a ScanWorkflowActivityResult struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ScanWorkflowActivityResult) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Executions != nil { w, err = wire.NewValueList(_List_WorkflowExecution_ValueList(v.Executions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ScanWorkflowActivityResult struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ScanWorkflowActivityResult struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ScanWorkflowActivityResult // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ScanWorkflowActivityResult) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Executions, err = _List_WorkflowExecution_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a ScanWorkflowActivityResult struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ScanWorkflowActivityResult struct could not be encoded. func (v *ScanWorkflowActivityResult) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Executions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_WorkflowExecution_Encode(v.Executions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ScanWorkflowActivityResult struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ScanWorkflowActivityResult struct could not be generated from the wire // representation. func (v *ScanWorkflowActivityResult) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Executions, err = _List_WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ScanWorkflowActivityResult // struct. func (v *ScanWorkflowActivityResult) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Executions != nil { fields[i] = fmt.Sprintf("Executions: %v", v.Executions) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("ScanWorkflowActivityResult{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ScanWorkflowActivityResult match the // provided ScanWorkflowActivityResult. // // This function performs a deep comparison. func (v *ScanWorkflowActivityResult) Equals(rhs *ScanWorkflowActivityResult) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Executions == nil && rhs.Executions == nil) || (v.Executions != nil && rhs.Executions != nil && _List_WorkflowExecution_Equals(v.Executions, rhs.Executions))) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ScanWorkflowActivityResult. func (v *ScanWorkflowActivityResult) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Executions != nil { err = multierr.Append(err, enc.AddArray("executions", (_List_WorkflowExecution_Zapper)(v.Executions))) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetExecutions returns the value of Executions if it is set or its // zero value if it is unset. func (v *ScanWorkflowActivityResult) GetExecutions() (o []*shared.WorkflowExecution) { if v != nil && v.Executions != nil { return v.Executions } return } // IsSetExecutions returns true if Executions is not nil. func (v *ScanWorkflowActivityResult) IsSetExecutions() bool { return v != nil && v.Executions != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ScanWorkflowActivityResult) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ScanWorkflowActivityResult) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type WorkflowParams struct { Domain *string `json:"domain,omitempty"` TaskList *string `json:"taskList,omitempty"` WorkflowQuery *string `json:"workflowQuery,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` SamplingRate *float64 `json:"samplingRate,omitempty"` ShadowMode *Mode `json:"shadowMode,omitempty"` ExitCondition *ExitCondition `json:"exitCondition,omitempty"` Concurrency *int32 `json:"concurrency,omitempty"` LastRunResult *WorkflowResult `json:"lastRunResult,omitempty"` } // ToWire translates a WorkflowParams struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowParams) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskList != nil { w, err = wire.NewValueString(*(v.TaskList)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowQuery != nil { w, err = wire.NewValueString(*(v.WorkflowQuery)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.SamplingRate != nil { w, err = wire.NewValueDouble(*(v.SamplingRate)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ShadowMode != nil { w, err = v.ShadowMode.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.ExitCondition != nil { w, err = v.ExitCondition.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.Concurrency != nil { w, err = wire.NewValueI32(*(v.Concurrency)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.LastRunResult != nil { w, err = v.LastRunResult.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Mode_Read(w wire.Value) (Mode, error) { var v Mode err := v.FromWire(w) return v, err } func _ExitCondition_Read(w wire.Value) (*ExitCondition, error) { var v ExitCondition err := v.FromWire(w) return &v, err } func _WorkflowResult_Read(w wire.Value) (*WorkflowResult, error) { var v WorkflowResult err := v.FromWire(w) return &v, err } // FromWire deserializes a WorkflowParams struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowParams struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowParams // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowParams) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TaskList = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowQuery = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 50: if field.Value.Type() == wire.TDouble { var x float64 x, err = field.Value.GetDouble(), error(nil) v.SamplingRate = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x Mode x, err = _Mode_Read(field.Value) v.ShadowMode = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.ExitCondition, err = _ExitCondition_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Concurrency = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TStruct { v.LastRunResult, err = _WorkflowResult_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowParams struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowParams struct could not be encoded. func (v *WorkflowParams) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TaskList)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowQuery != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowQuery)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SamplingRate != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(*(v.SamplingRate)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShadowMode != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := v.ShadowMode.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExitCondition != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.ExitCondition.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Concurrency != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Concurrency)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastRunResult != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TStruct}); err != nil { return err } if err := v.LastRunResult.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _Mode_Decode(sr stream.Reader) (Mode, error) { var v Mode err := v.Decode(sr) return v, err } func _ExitCondition_Decode(sr stream.Reader) (*ExitCondition, error) { var v ExitCondition err := v.Decode(sr) return &v, err } func _WorkflowResult_Decode(sr stream.Reader) (*WorkflowResult, error) { var v WorkflowResult err := v.Decode(sr) return &v, err } // Decode deserializes a WorkflowParams struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowParams struct could not be generated from the wire // representation. func (v *WorkflowParams) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TaskList = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowQuery = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TDouble: var x float64 x, err = sr.ReadDouble() v.SamplingRate = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x Mode x, err = _Mode_Decode(sr) v.ShadowMode = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.ExitCondition, err = _ExitCondition_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Concurrency = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TStruct: v.LastRunResult, err = _WorkflowResult_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowParams // struct. func (v *WorkflowParams) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", *(v.TaskList)) i++ } if v.WorkflowQuery != nil { fields[i] = fmt.Sprintf("WorkflowQuery: %v", *(v.WorkflowQuery)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.SamplingRate != nil { fields[i] = fmt.Sprintf("SamplingRate: %v", *(v.SamplingRate)) i++ } if v.ShadowMode != nil { fields[i] = fmt.Sprintf("ShadowMode: %v", *(v.ShadowMode)) i++ } if v.ExitCondition != nil { fields[i] = fmt.Sprintf("ExitCondition: %v", v.ExitCondition) i++ } if v.Concurrency != nil { fields[i] = fmt.Sprintf("Concurrency: %v", *(v.Concurrency)) i++ } if v.LastRunResult != nil { fields[i] = fmt.Sprintf("LastRunResult: %v", v.LastRunResult) i++ } return fmt.Sprintf("WorkflowParams{%v}", strings.Join(fields[:i], ", ")) } func _Mode_EqualsPtr(lhs, rhs *Mode) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this WorkflowParams match the // provided WorkflowParams. // // This function performs a deep comparison. func (v *WorkflowParams) Equals(rhs *WorkflowParams) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.TaskList, rhs.TaskList) { return false } if !_String_EqualsPtr(v.WorkflowQuery, rhs.WorkflowQuery) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !_Double_EqualsPtr(v.SamplingRate, rhs.SamplingRate) { return false } if !_Mode_EqualsPtr(v.ShadowMode, rhs.ShadowMode) { return false } if !((v.ExitCondition == nil && rhs.ExitCondition == nil) || (v.ExitCondition != nil && rhs.ExitCondition != nil && v.ExitCondition.Equals(rhs.ExitCondition))) { return false } if !_I32_EqualsPtr(v.Concurrency, rhs.Concurrency) { return false } if !((v.LastRunResult == nil && rhs.LastRunResult == nil) || (v.LastRunResult != nil && rhs.LastRunResult != nil && v.LastRunResult.Equals(rhs.LastRunResult))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowParams. func (v *WorkflowParams) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.TaskList != nil { enc.AddString("taskList", *v.TaskList) } if v.WorkflowQuery != nil { enc.AddString("workflowQuery", *v.WorkflowQuery) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.SamplingRate != nil { enc.AddFloat64("samplingRate", *v.SamplingRate) } if v.ShadowMode != nil { err = multierr.Append(err, enc.AddObject("shadowMode", *v.ShadowMode)) } if v.ExitCondition != nil { err = multierr.Append(err, enc.AddObject("exitCondition", v.ExitCondition)) } if v.Concurrency != nil { enc.AddInt32("concurrency", *v.Concurrency) } if v.LastRunResult != nil { err = multierr.Append(err, enc.AddObject("lastRunResult", v.LastRunResult)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *WorkflowParams) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *WorkflowParams) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *WorkflowParams) GetTaskList() (o string) { if v != nil && v.TaskList != nil { return *v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *WorkflowParams) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetWorkflowQuery returns the value of WorkflowQuery if it is set or its // zero value if it is unset. func (v *WorkflowParams) GetWorkflowQuery() (o string) { if v != nil && v.WorkflowQuery != nil { return *v.WorkflowQuery } return } // IsSetWorkflowQuery returns true if WorkflowQuery is not nil. func (v *WorkflowParams) IsSetWorkflowQuery() bool { return v != nil && v.WorkflowQuery != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *WorkflowParams) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *WorkflowParams) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetSamplingRate returns the value of SamplingRate if it is set or its // zero value if it is unset. func (v *WorkflowParams) GetSamplingRate() (o float64) { if v != nil && v.SamplingRate != nil { return *v.SamplingRate } return } // IsSetSamplingRate returns true if SamplingRate is not nil. func (v *WorkflowParams) IsSetSamplingRate() bool { return v != nil && v.SamplingRate != nil } // GetShadowMode returns the value of ShadowMode if it is set or its // zero value if it is unset. func (v *WorkflowParams) GetShadowMode() (o Mode) { if v != nil && v.ShadowMode != nil { return *v.ShadowMode } return } // IsSetShadowMode returns true if ShadowMode is not nil. func (v *WorkflowParams) IsSetShadowMode() bool { return v != nil && v.ShadowMode != nil } // GetExitCondition returns the value of ExitCondition if it is set or its // zero value if it is unset. func (v *WorkflowParams) GetExitCondition() (o *ExitCondition) { if v != nil && v.ExitCondition != nil { return v.ExitCondition } return } // IsSetExitCondition returns true if ExitCondition is not nil. func (v *WorkflowParams) IsSetExitCondition() bool { return v != nil && v.ExitCondition != nil } // GetConcurrency returns the value of Concurrency if it is set or its // zero value if it is unset. func (v *WorkflowParams) GetConcurrency() (o int32) { if v != nil && v.Concurrency != nil { return *v.Concurrency } return } // IsSetConcurrency returns true if Concurrency is not nil. func (v *WorkflowParams) IsSetConcurrency() bool { return v != nil && v.Concurrency != nil } // GetLastRunResult returns the value of LastRunResult if it is set or its // zero value if it is unset. func (v *WorkflowParams) GetLastRunResult() (o *WorkflowResult) { if v != nil && v.LastRunResult != nil { return v.LastRunResult } return } // IsSetLastRunResult returns true if LastRunResult is not nil. func (v *WorkflowParams) IsSetLastRunResult() bool { return v != nil && v.LastRunResult != nil } type WorkflowResult struct { Succeeded *int32 `json:"succeeded,omitempty"` Skipped *int32 `json:"skipped,omitempty"` Failed *int32 `json:"failed,omitempty"` } // ToWire translates a WorkflowResult struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowResult) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Succeeded != nil { w, err = wire.NewValueI32(*(v.Succeeded)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Skipped != nil { w, err = wire.NewValueI32(*(v.Skipped)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Failed != nil { w, err = wire.NewValueI32(*(v.Failed)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowResult struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowResult struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowResult // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowResult) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Succeeded = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Skipped = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Failed = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowResult struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowResult struct could not be encoded. func (v *WorkflowResult) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Succeeded != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Succeeded)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Skipped != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Skipped)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Failed != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Failed)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowResult struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowResult struct could not be generated from the wire // representation. func (v *WorkflowResult) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Succeeded = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Skipped = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Failed = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowResult // struct. func (v *WorkflowResult) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Succeeded != nil { fields[i] = fmt.Sprintf("Succeeded: %v", *(v.Succeeded)) i++ } if v.Skipped != nil { fields[i] = fmt.Sprintf("Skipped: %v", *(v.Skipped)) i++ } if v.Failed != nil { fields[i] = fmt.Sprintf("Failed: %v", *(v.Failed)) i++ } return fmt.Sprintf("WorkflowResult{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowResult match the // provided WorkflowResult. // // This function performs a deep comparison. func (v *WorkflowResult) Equals(rhs *WorkflowResult) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.Succeeded, rhs.Succeeded) { return false } if !_I32_EqualsPtr(v.Skipped, rhs.Skipped) { return false } if !_I32_EqualsPtr(v.Failed, rhs.Failed) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowResult. func (v *WorkflowResult) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Succeeded != nil { enc.AddInt32("succeeded", *v.Succeeded) } if v.Skipped != nil { enc.AddInt32("skipped", *v.Skipped) } if v.Failed != nil { enc.AddInt32("failed", *v.Failed) } return err } // GetSucceeded returns the value of Succeeded if it is set or its // zero value if it is unset. func (v *WorkflowResult) GetSucceeded() (o int32) { if v != nil && v.Succeeded != nil { return *v.Succeeded } return } // IsSetSucceeded returns true if Succeeded is not nil. func (v *WorkflowResult) IsSetSucceeded() bool { return v != nil && v.Succeeded != nil } // GetSkipped returns the value of Skipped if it is set or its // zero value if it is unset. func (v *WorkflowResult) GetSkipped() (o int32) { if v != nil && v.Skipped != nil { return *v.Skipped } return } // IsSetSkipped returns true if Skipped is not nil. func (v *WorkflowResult) IsSetSkipped() bool { return v != nil && v.Skipped != nil } // GetFailed returns the value of Failed if it is set or its // zero value if it is unset. func (v *WorkflowResult) GetFailed() (o int32) { if v != nil && v.Failed != nil { return *v.Failed } return } // IsSetFailed returns true if Failed is not nil. func (v *WorkflowResult) IsSetFailed() bool { return v != nil && v.Failed != nil } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "shadower", Package: "github.com/uber/cadence/.gen/go/shadower", FilePath: "shadower.thrift", SHA1: "f29a425548641e51e02ec1978279c1e37b9df792", Includes: []*thriftreflect.ThriftModule{ shared.ThriftModule, }, Raw: rawIDL, } const rawIDL = "// Copyright (c) 2017-2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nnamespace java com.uber.cadence.shadower\n\ninclude \"shared.thrift\"\n\nconst string LocalDomainName = \"cadence-shadower\"\nconst string TaskList = \"cadence-shadower-tl\"\n\nconst string WorkflowName = \"cadence-shadow-workflow\"\n\nconst string ScanWorkflowActivityName = \"scanWorkflowActivity\"\nconst string ReplayWorkflowActivityName = \"replayWorkflowActivity\"\n\nconst string WorkflowIDSuffix = \"-shadow-workflow\"\n\nconst string ErrReasonDomainNotExists = \"domain not exists\"\nconst string ErrReasonInvalidQuery = \"invalid visibility query\"\nconst string ErrReasonWorkflowTypeNotRegistered = \"workflow type not registered\"\nconst string ErrNonRetryableType = \"com.uber.cadence.internal.shadowing.NonRetryableException\"\n\nenum Mode {\n Normal,\n Continuous,\n}\n\nstruct ExitCondition {\n 10: optional i32 expirationIntervalInSeconds\n 20: optional i32 shadowCount\n}\n\nstruct WorkflowParams {\n 10: optional string domain \n 20: optional string taskList\n 30: optional string workflowQuery\n 40: optional binary nextPageToken\n 50: optional double samplingRate\n 60: optional Mode shadowMode\n 70: optional ExitCondition exitCondition\n 80: optional i32 concurrency\n 90: optional WorkflowResult lastRunResult\n}\n\nstruct WorkflowResult {\n 10: optional i32 succeeded\n 20: optional i32 skipped\n 30: optional i32 failed\n}\n\nstruct ScanWorkflowActivityParams {\n 10: optional string domain\n 20: optional string workflowQuery\n 30: optional binary nextPageToken\n 40: optional i32 pageSize\n 50: optional double samplingRate\n}\n\nstruct ScanWorkflowActivityResult {\n 10: optional list executions\n 20: optional binary nextPageToken\n}\n\nstruct ReplayWorkflowActivityParams {\n 10: optional string domain\n 20: optional list executions\n}\n\nstruct ReplayWorkflowActivityResult {\n 10: optional i32 succeeded\n 20: optional i32 skipped\n 30: optional i32 failed\n}\n" ================================================ FILE: .gen/go/shadower/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package shadower ================================================ FILE: .gen/go/shared/shared.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package shared import ( bytes "bytes" base64 "encoding/base64" json "encoding/json" errors "errors" fmt "fmt" math "math" strconv "strconv" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" ptr "go.uber.org/thriftrw/ptr" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" ) type AccessDeniedError struct { Message string `json:"message,required"` } // ToWire translates a AccessDeniedError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AccessDeniedError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AccessDeniedError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AccessDeniedError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AccessDeniedError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AccessDeniedError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of AccessDeniedError is required") } return nil } // Encode serializes a AccessDeniedError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AccessDeniedError struct could not be encoded. func (v *AccessDeniedError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a AccessDeniedError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AccessDeniedError struct could not be generated from the wire // representation. func (v *AccessDeniedError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of AccessDeniedError is required") } return nil } // String returns a readable string representation of a AccessDeniedError // struct. func (v *AccessDeniedError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("AccessDeniedError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*AccessDeniedError) ErrorName() string { return "AccessDeniedError" } // Equals returns true if all the fields of this AccessDeniedError match the // provided AccessDeniedError. // // This function performs a deep comparison. func (v *AccessDeniedError) Equals(rhs *AccessDeniedError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AccessDeniedError. func (v *AccessDeniedError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *AccessDeniedError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *AccessDeniedError) Error() string { return v.String() } type ActiveClusterInfo struct { ActiveClusterName *string `json:"activeClusterName,omitempty"` FailoverVersion *int64 `json:"failoverVersion,omitempty"` } // ToWire translates a ActiveClusterInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActiveClusterInfo) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ActiveClusterName != nil { w, err = wire.NewValueString(*(v.ActiveClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.FailoverVersion != nil { w, err = wire.NewValueI64(*(v.FailoverVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ActiveClusterInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActiveClusterInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActiveClusterInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActiveClusterInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActiveClusterName = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverVersion = &x if err != nil { return err } } } } return nil } // Encode serializes a ActiveClusterInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActiveClusterInfo struct could not be encoded. func (v *ActiveClusterInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActiveClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActiveClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ActiveClusterInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActiveClusterInfo struct could not be generated from the wire // representation. func (v *ActiveClusterInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActiveClusterName = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverVersion = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActiveClusterInfo // struct. func (v *ActiveClusterInfo) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ActiveClusterName != nil { fields[i] = fmt.Sprintf("ActiveClusterName: %v", *(v.ActiveClusterName)) i++ } if v.FailoverVersion != nil { fields[i] = fmt.Sprintf("FailoverVersion: %v", *(v.FailoverVersion)) i++ } return fmt.Sprintf("ActiveClusterInfo{%v}", strings.Join(fields[:i], ", ")) } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _I64_EqualsPtr(lhs, rhs *int64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ActiveClusterInfo match the // provided ActiveClusterInfo. // // This function performs a deep comparison. func (v *ActiveClusterInfo) Equals(rhs *ActiveClusterInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ActiveClusterName, rhs.ActiveClusterName) { return false } if !_I64_EqualsPtr(v.FailoverVersion, rhs.FailoverVersion) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActiveClusterInfo. func (v *ActiveClusterInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActiveClusterName != nil { enc.AddString("activeClusterName", *v.ActiveClusterName) } if v.FailoverVersion != nil { enc.AddInt64("failoverVersion", *v.FailoverVersion) } return err } // GetActiveClusterName returns the value of ActiveClusterName if it is set or its // zero value if it is unset. func (v *ActiveClusterInfo) GetActiveClusterName() (o string) { if v != nil && v.ActiveClusterName != nil { return *v.ActiveClusterName } return } // IsSetActiveClusterName returns true if ActiveClusterName is not nil. func (v *ActiveClusterInfo) IsSetActiveClusterName() bool { return v != nil && v.ActiveClusterName != nil } // GetFailoverVersion returns the value of FailoverVersion if it is set or its // zero value if it is unset. func (v *ActiveClusterInfo) GetFailoverVersion() (o int64) { if v != nil && v.FailoverVersion != nil { return *v.FailoverVersion } return } // IsSetFailoverVersion returns true if FailoverVersion is not nil. func (v *ActiveClusterInfo) IsSetFailoverVersion() bool { return v != nil && v.FailoverVersion != nil } type ActiveClusterSelectionPolicy struct { ClusterAttribute *ClusterAttribute `json:"clusterAttribute,omitempty"` Strategy *ActiveClusterSelectionStrategy `json:"strategy,omitempty"` StickyRegion *string `json:"stickyRegion,omitempty"` ExternalEntityType *string `json:"externalEntityType,omitempty"` ExternalEntityKey *string `json:"externalEntityKey,omitempty"` } // ToWire translates a ActiveClusterSelectionPolicy struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActiveClusterSelectionPolicy) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.ClusterAttribute != nil { w, err = v.ClusterAttribute.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.Strategy != nil { w, err = v.Strategy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StickyRegion != nil { w, err = wire.NewValueString(*(v.StickyRegion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ExternalEntityType != nil { w, err = wire.NewValueString(*(v.ExternalEntityType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ExternalEntityKey != nil { w, err = wire.NewValueString(*(v.ExternalEntityKey)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ClusterAttribute_Read(w wire.Value) (*ClusterAttribute, error) { var v ClusterAttribute err := v.FromWire(w) return &v, err } func _ActiveClusterSelectionStrategy_Read(w wire.Value) (ActiveClusterSelectionStrategy, error) { var v ActiveClusterSelectionStrategy err := v.FromWire(w) return v, err } // FromWire deserializes a ActiveClusterSelectionPolicy struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActiveClusterSelectionPolicy struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActiveClusterSelectionPolicy // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActiveClusterSelectionPolicy) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TStruct { v.ClusterAttribute, err = _ClusterAttribute_Read(field.Value) if err != nil { return err } } case 10: if field.Value.Type() == wire.TI32 { var x ActiveClusterSelectionStrategy x, err = _ActiveClusterSelectionStrategy_Read(field.Value) v.Strategy = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.StickyRegion = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ExternalEntityType = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ExternalEntityKey = &x if err != nil { return err } } } } return nil } // Encode serializes a ActiveClusterSelectionPolicy struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActiveClusterSelectionPolicy struct could not be encoded. func (v *ActiveClusterSelectionPolicy) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ClusterAttribute != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TStruct}); err != nil { return err } if err := v.ClusterAttribute.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Strategy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.Strategy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyRegion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.StickyRegion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExternalEntityType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ExternalEntityType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExternalEntityKey != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ExternalEntityKey)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ClusterAttribute_Decode(sr stream.Reader) (*ClusterAttribute, error) { var v ClusterAttribute err := v.Decode(sr) return &v, err } func _ActiveClusterSelectionStrategy_Decode(sr stream.Reader) (ActiveClusterSelectionStrategy, error) { var v ActiveClusterSelectionStrategy err := v.Decode(sr) return v, err } // Decode deserializes a ActiveClusterSelectionPolicy struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActiveClusterSelectionPolicy struct could not be generated from the wire // representation. func (v *ActiveClusterSelectionPolicy) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TStruct: v.ClusterAttribute, err = _ClusterAttribute_Decode(sr) if err != nil { return err } case fh.ID == 10 && fh.Type == wire.TI32: var x ActiveClusterSelectionStrategy x, err = _ActiveClusterSelectionStrategy_Decode(sr) v.Strategy = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.StickyRegion = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ExternalEntityType = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ExternalEntityKey = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActiveClusterSelectionPolicy // struct. func (v *ActiveClusterSelectionPolicy) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.ClusterAttribute != nil { fields[i] = fmt.Sprintf("ClusterAttribute: %v", v.ClusterAttribute) i++ } if v.Strategy != nil { fields[i] = fmt.Sprintf("Strategy: %v", *(v.Strategy)) i++ } if v.StickyRegion != nil { fields[i] = fmt.Sprintf("StickyRegion: %v", *(v.StickyRegion)) i++ } if v.ExternalEntityType != nil { fields[i] = fmt.Sprintf("ExternalEntityType: %v", *(v.ExternalEntityType)) i++ } if v.ExternalEntityKey != nil { fields[i] = fmt.Sprintf("ExternalEntityKey: %v", *(v.ExternalEntityKey)) i++ } return fmt.Sprintf("ActiveClusterSelectionPolicy{%v}", strings.Join(fields[:i], ", ")) } func _ActiveClusterSelectionStrategy_EqualsPtr(lhs, rhs *ActiveClusterSelectionStrategy) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ActiveClusterSelectionPolicy match the // provided ActiveClusterSelectionPolicy. // // This function performs a deep comparison. func (v *ActiveClusterSelectionPolicy) Equals(rhs *ActiveClusterSelectionPolicy) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ClusterAttribute == nil && rhs.ClusterAttribute == nil) || (v.ClusterAttribute != nil && rhs.ClusterAttribute != nil && v.ClusterAttribute.Equals(rhs.ClusterAttribute))) { return false } if !_ActiveClusterSelectionStrategy_EqualsPtr(v.Strategy, rhs.Strategy) { return false } if !_String_EqualsPtr(v.StickyRegion, rhs.StickyRegion) { return false } if !_String_EqualsPtr(v.ExternalEntityType, rhs.ExternalEntityType) { return false } if !_String_EqualsPtr(v.ExternalEntityKey, rhs.ExternalEntityKey) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActiveClusterSelectionPolicy. func (v *ActiveClusterSelectionPolicy) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ClusterAttribute != nil { err = multierr.Append(err, enc.AddObject("clusterAttribute", v.ClusterAttribute)) } if v.Strategy != nil { err = multierr.Append(err, enc.AddObject("strategy", *v.Strategy)) } if v.StickyRegion != nil { enc.AddString("stickyRegion", *v.StickyRegion) } if v.ExternalEntityType != nil { enc.AddString("externalEntityType", *v.ExternalEntityType) } if v.ExternalEntityKey != nil { enc.AddString("externalEntityKey", *v.ExternalEntityKey) } return err } // GetClusterAttribute returns the value of ClusterAttribute if it is set or its // zero value if it is unset. func (v *ActiveClusterSelectionPolicy) GetClusterAttribute() (o *ClusterAttribute) { if v != nil && v.ClusterAttribute != nil { return v.ClusterAttribute } return } // IsSetClusterAttribute returns true if ClusterAttribute is not nil. func (v *ActiveClusterSelectionPolicy) IsSetClusterAttribute() bool { return v != nil && v.ClusterAttribute != nil } // GetStrategy returns the value of Strategy if it is set or its // zero value if it is unset. func (v *ActiveClusterSelectionPolicy) GetStrategy() (o ActiveClusterSelectionStrategy) { if v != nil && v.Strategy != nil { return *v.Strategy } return } // IsSetStrategy returns true if Strategy is not nil. func (v *ActiveClusterSelectionPolicy) IsSetStrategy() bool { return v != nil && v.Strategy != nil } // GetStickyRegion returns the value of StickyRegion if it is set or its // zero value if it is unset. func (v *ActiveClusterSelectionPolicy) GetStickyRegion() (o string) { if v != nil && v.StickyRegion != nil { return *v.StickyRegion } return } // IsSetStickyRegion returns true if StickyRegion is not nil. func (v *ActiveClusterSelectionPolicy) IsSetStickyRegion() bool { return v != nil && v.StickyRegion != nil } // GetExternalEntityType returns the value of ExternalEntityType if it is set or its // zero value if it is unset. func (v *ActiveClusterSelectionPolicy) GetExternalEntityType() (o string) { if v != nil && v.ExternalEntityType != nil { return *v.ExternalEntityType } return } // IsSetExternalEntityType returns true if ExternalEntityType is not nil. func (v *ActiveClusterSelectionPolicy) IsSetExternalEntityType() bool { return v != nil && v.ExternalEntityType != nil } // GetExternalEntityKey returns the value of ExternalEntityKey if it is set or its // zero value if it is unset. func (v *ActiveClusterSelectionPolicy) GetExternalEntityKey() (o string) { if v != nil && v.ExternalEntityKey != nil { return *v.ExternalEntityKey } return } // IsSetExternalEntityKey returns true if ExternalEntityKey is not nil. func (v *ActiveClusterSelectionPolicy) IsSetExternalEntityKey() bool { return v != nil && v.ExternalEntityKey != nil } type ActiveClusterSelectionStrategy int32 const ( ActiveClusterSelectionStrategyRegionSticky ActiveClusterSelectionStrategy = 0 ActiveClusterSelectionStrategyExternalEntity ActiveClusterSelectionStrategy = 1 ) // ActiveClusterSelectionStrategy_Values returns all recognized values of ActiveClusterSelectionStrategy. func ActiveClusterSelectionStrategy_Values() []ActiveClusterSelectionStrategy { return []ActiveClusterSelectionStrategy{ ActiveClusterSelectionStrategyRegionSticky, ActiveClusterSelectionStrategyExternalEntity, } } // UnmarshalText tries to decode ActiveClusterSelectionStrategy from a byte slice // containing its name. // // var v ActiveClusterSelectionStrategy // err := v.UnmarshalText([]byte("REGION_STICKY")) func (v *ActiveClusterSelectionStrategy) UnmarshalText(value []byte) error { switch s := string(value); s { case "REGION_STICKY": *v = ActiveClusterSelectionStrategyRegionSticky return nil case "EXTERNAL_ENTITY": *v = ActiveClusterSelectionStrategyExternalEntity return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ActiveClusterSelectionStrategy", err) } *v = ActiveClusterSelectionStrategy(val) return nil } } // MarshalText encodes ActiveClusterSelectionStrategy to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v ActiveClusterSelectionStrategy) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("REGION_STICKY"), nil case 1: return []byte("EXTERNAL_ENTITY"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActiveClusterSelectionStrategy. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v ActiveClusterSelectionStrategy) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "REGION_STICKY") case 1: enc.AddString("name", "EXTERNAL_ENTITY") } return nil } // Ptr returns a pointer to this enum value. func (v ActiveClusterSelectionStrategy) Ptr() *ActiveClusterSelectionStrategy { return &v } // Encode encodes ActiveClusterSelectionStrategy directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v ActiveClusterSelectionStrategy // return v.Encode(sWriter) func (v ActiveClusterSelectionStrategy) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates ActiveClusterSelectionStrategy into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v ActiveClusterSelectionStrategy) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes ActiveClusterSelectionStrategy from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return ActiveClusterSelectionStrategy(0), err // } // // var v ActiveClusterSelectionStrategy // if err := v.FromWire(x); err != nil { // return ActiveClusterSelectionStrategy(0), err // } // return v, nil func (v *ActiveClusterSelectionStrategy) FromWire(w wire.Value) error { *v = (ActiveClusterSelectionStrategy)(w.GetI32()) return nil } // Decode reads off the encoded ActiveClusterSelectionStrategy directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v ActiveClusterSelectionStrategy // if err := v.Decode(sReader); err != nil { // return ActiveClusterSelectionStrategy(0), err // } // return v, nil func (v *ActiveClusterSelectionStrategy) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (ActiveClusterSelectionStrategy)(i) return nil } // String returns a readable string representation of ActiveClusterSelectionStrategy. func (v ActiveClusterSelectionStrategy) String() string { w := int32(v) switch w { case 0: return "REGION_STICKY" case 1: return "EXTERNAL_ENTITY" } return fmt.Sprintf("ActiveClusterSelectionStrategy(%d)", w) } // Equals returns true if this ActiveClusterSelectionStrategy value matches the provided // value. func (v ActiveClusterSelectionStrategy) Equals(rhs ActiveClusterSelectionStrategy) bool { return v == rhs } // MarshalJSON serializes ActiveClusterSelectionStrategy into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v ActiveClusterSelectionStrategy) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"REGION_STICKY\""), nil case 1: return ([]byte)("\"EXTERNAL_ENTITY\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode ActiveClusterSelectionStrategy from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *ActiveClusterSelectionStrategy) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "ActiveClusterSelectionStrategy") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "ActiveClusterSelectionStrategy") } *v = (ActiveClusterSelectionStrategy)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "ActiveClusterSelectionStrategy") } } type ActiveClusters struct { ActiveClustersByRegion map[string]*ActiveClusterInfo `json:"activeClustersByRegion,omitempty"` ActiveClustersByClusterAttribute map[string]*ClusterAttributeScope `json:"activeClustersByClusterAttribute,omitempty"` } type _Map_String_ActiveClusterInfo_MapItemList map[string]*ActiveClusterInfo func (m _Map_String_ActiveClusterInfo_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*ActiveClusterInfo', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_ActiveClusterInfo_MapItemList) Size() int { return len(m) } func (_Map_String_ActiveClusterInfo_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_ActiveClusterInfo_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_ActiveClusterInfo_MapItemList) Close() {} type _Map_String_ClusterAttributeScope_MapItemList map[string]*ClusterAttributeScope func (m _Map_String_ClusterAttributeScope_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*ClusterAttributeScope', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_ClusterAttributeScope_MapItemList) Size() int { return len(m) } func (_Map_String_ClusterAttributeScope_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_ClusterAttributeScope_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_ClusterAttributeScope_MapItemList) Close() {} // ToWire translates a ActiveClusters struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActiveClusters) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ActiveClustersByRegion != nil { w, err = wire.NewValueMap(_Map_String_ActiveClusterInfo_MapItemList(v.ActiveClustersByRegion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ActiveClustersByClusterAttribute != nil { w, err = wire.NewValueMap(_Map_String_ClusterAttributeScope_MapItemList(v.ActiveClustersByClusterAttribute)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 11, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ActiveClusterInfo_Read(w wire.Value) (*ActiveClusterInfo, error) { var v ActiveClusterInfo err := v.FromWire(w) return &v, err } func _Map_String_ActiveClusterInfo_Read(m wire.MapItemList) (map[string]*ActiveClusterInfo, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*ActiveClusterInfo, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _ActiveClusterInfo_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } func _ClusterAttributeScope_Read(w wire.Value) (*ClusterAttributeScope, error) { var v ClusterAttributeScope err := v.FromWire(w) return &v, err } func _Map_String_ClusterAttributeScope_Read(m wire.MapItemList) (map[string]*ClusterAttributeScope, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*ClusterAttributeScope, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _ClusterAttributeScope_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a ActiveClusters struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActiveClusters struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActiveClusters // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActiveClusters) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.ActiveClustersByRegion, err = _Map_String_ActiveClusterInfo_Read(field.Value.GetMap()) if err != nil { return err } } case 11: if field.Value.Type() == wire.TMap { v.ActiveClustersByClusterAttribute, err = _Map_String_ClusterAttributeScope_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_ActiveClusterInfo_Encode(val map[string]*ActiveClusterInfo, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*ActiveClusterInfo', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } func _Map_String_ClusterAttributeScope_Encode(val map[string]*ClusterAttributeScope, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*ClusterAttributeScope', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a ActiveClusters struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActiveClusters struct could not be encoded. func (v *ActiveClusters) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActiveClustersByRegion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_ActiveClusterInfo_Encode(v.ActiveClustersByRegion, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClustersByClusterAttribute != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 11, Type: wire.TMap}); err != nil { return err } if err := _Map_String_ClusterAttributeScope_Encode(v.ActiveClustersByClusterAttribute, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ActiveClusterInfo_Decode(sr stream.Reader) (*ActiveClusterInfo, error) { var v ActiveClusterInfo err := v.Decode(sr) return &v, err } func _Map_String_ActiveClusterInfo_Decode(sr stream.Reader) (map[string]*ActiveClusterInfo, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*ActiveClusterInfo, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _ActiveClusterInfo_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } func _ClusterAttributeScope_Decode(sr stream.Reader) (*ClusterAttributeScope, error) { var v ClusterAttributeScope err := v.Decode(sr) return &v, err } func _Map_String_ClusterAttributeScope_Decode(sr stream.Reader) (map[string]*ClusterAttributeScope, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*ClusterAttributeScope, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _ClusterAttributeScope_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ActiveClusters struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActiveClusters struct could not be generated from the wire // representation. func (v *ActiveClusters) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.ActiveClustersByRegion, err = _Map_String_ActiveClusterInfo_Decode(sr) if err != nil { return err } case fh.ID == 11 && fh.Type == wire.TMap: v.ActiveClustersByClusterAttribute, err = _Map_String_ClusterAttributeScope_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActiveClusters // struct. func (v *ActiveClusters) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ActiveClustersByRegion != nil { fields[i] = fmt.Sprintf("ActiveClustersByRegion: %v", v.ActiveClustersByRegion) i++ } if v.ActiveClustersByClusterAttribute != nil { fields[i] = fmt.Sprintf("ActiveClustersByClusterAttribute: %v", v.ActiveClustersByClusterAttribute) i++ } return fmt.Sprintf("ActiveClusters{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_ActiveClusterInfo_Equals(lhs, rhs map[string]*ActiveClusterInfo) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } func _Map_String_ClusterAttributeScope_Equals(lhs, rhs map[string]*ClusterAttributeScope) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this ActiveClusters match the // provided ActiveClusters. // // This function performs a deep comparison. func (v *ActiveClusters) Equals(rhs *ActiveClusters) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ActiveClustersByRegion == nil && rhs.ActiveClustersByRegion == nil) || (v.ActiveClustersByRegion != nil && rhs.ActiveClustersByRegion != nil && _Map_String_ActiveClusterInfo_Equals(v.ActiveClustersByRegion, rhs.ActiveClustersByRegion))) { return false } if !((v.ActiveClustersByClusterAttribute == nil && rhs.ActiveClustersByClusterAttribute == nil) || (v.ActiveClustersByClusterAttribute != nil && rhs.ActiveClustersByClusterAttribute != nil && _Map_String_ClusterAttributeScope_Equals(v.ActiveClustersByClusterAttribute, rhs.ActiveClustersByClusterAttribute))) { return false } return true } type _Map_String_ActiveClusterInfo_Zapper map[string]*ActiveClusterInfo // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_ActiveClusterInfo_Zapper. func (m _Map_String_ActiveClusterInfo_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } type _Map_String_ClusterAttributeScope_Zapper map[string]*ClusterAttributeScope // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_ClusterAttributeScope_Zapper. func (m _Map_String_ClusterAttributeScope_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActiveClusters. func (v *ActiveClusters) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActiveClustersByRegion != nil { err = multierr.Append(err, enc.AddObject("activeClustersByRegion", (_Map_String_ActiveClusterInfo_Zapper)(v.ActiveClustersByRegion))) } if v.ActiveClustersByClusterAttribute != nil { err = multierr.Append(err, enc.AddObject("activeClustersByClusterAttribute", (_Map_String_ClusterAttributeScope_Zapper)(v.ActiveClustersByClusterAttribute))) } return err } // GetActiveClustersByRegion returns the value of ActiveClustersByRegion if it is set or its // zero value if it is unset. func (v *ActiveClusters) GetActiveClustersByRegion() (o map[string]*ActiveClusterInfo) { if v != nil && v.ActiveClustersByRegion != nil { return v.ActiveClustersByRegion } return } // IsSetActiveClustersByRegion returns true if ActiveClustersByRegion is not nil. func (v *ActiveClusters) IsSetActiveClustersByRegion() bool { return v != nil && v.ActiveClustersByRegion != nil } // GetActiveClustersByClusterAttribute returns the value of ActiveClustersByClusterAttribute if it is set or its // zero value if it is unset. func (v *ActiveClusters) GetActiveClustersByClusterAttribute() (o map[string]*ClusterAttributeScope) { if v != nil && v.ActiveClustersByClusterAttribute != nil { return v.ActiveClustersByClusterAttribute } return } // IsSetActiveClustersByClusterAttribute returns true if ActiveClustersByClusterAttribute is not nil. func (v *ActiveClusters) IsSetActiveClustersByClusterAttribute() bool { return v != nil && v.ActiveClustersByClusterAttribute != nil } type ActivityLocalDispatchInfo struct { ActivityId *string `json:"activityId,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` ScheduledTimestampOfThisAttempt *int64 `json:"scheduledTimestampOfThisAttempt,omitempty"` TaskToken []byte `json:"taskToken,omitempty"` } // ToWire translates a ActivityLocalDispatchInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityLocalDispatchInfo) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.ActivityId != nil { w, err = wire.NewValueString(*(v.ActivityId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ScheduledTimestamp != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.StartedTimestamp != nil { w, err = wire.NewValueI64(*(v.StartedTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ScheduledTimestampOfThisAttempt != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestampOfThisAttempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ActivityLocalDispatchInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityLocalDispatchInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityLocalDispatchInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityLocalDispatchInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestamp = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTimestamp = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestampOfThisAttempt = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a ActivityLocalDispatchInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityLocalDispatchInfo struct could not be encoded. func (v *ActivityLocalDispatchInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActivityId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestampOfThisAttempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestampOfThisAttempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ActivityLocalDispatchInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityLocalDispatchInfo struct could not be generated from the wire // representation. func (v *ActivityLocalDispatchInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestamp = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTimestamp = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestampOfThisAttempt = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityLocalDispatchInfo // struct. func (v *ActivityLocalDispatchInfo) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.ActivityId != nil { fields[i] = fmt.Sprintf("ActivityId: %v", *(v.ActivityId)) i++ } if v.ScheduledTimestamp != nil { fields[i] = fmt.Sprintf("ScheduledTimestamp: %v", *(v.ScheduledTimestamp)) i++ } if v.StartedTimestamp != nil { fields[i] = fmt.Sprintf("StartedTimestamp: %v", *(v.StartedTimestamp)) i++ } if v.ScheduledTimestampOfThisAttempt != nil { fields[i] = fmt.Sprintf("ScheduledTimestampOfThisAttempt: %v", *(v.ScheduledTimestampOfThisAttempt)) i++ } if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } return fmt.Sprintf("ActivityLocalDispatchInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ActivityLocalDispatchInfo match the // provided ActivityLocalDispatchInfo. // // This function performs a deep comparison. func (v *ActivityLocalDispatchInfo) Equals(rhs *ActivityLocalDispatchInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ActivityId, rhs.ActivityId) { return false } if !_I64_EqualsPtr(v.ScheduledTimestamp, rhs.ScheduledTimestamp) { return false } if !_I64_EqualsPtr(v.StartedTimestamp, rhs.StartedTimestamp) { return false } if !_I64_EqualsPtr(v.ScheduledTimestampOfThisAttempt, rhs.ScheduledTimestampOfThisAttempt) { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityLocalDispatchInfo. func (v *ActivityLocalDispatchInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActivityId != nil { enc.AddString("activityId", *v.ActivityId) } if v.ScheduledTimestamp != nil { enc.AddInt64("scheduledTimestamp", *v.ScheduledTimestamp) } if v.StartedTimestamp != nil { enc.AddInt64("startedTimestamp", *v.StartedTimestamp) } if v.ScheduledTimestampOfThisAttempt != nil { enc.AddInt64("scheduledTimestampOfThisAttempt", *v.ScheduledTimestampOfThisAttempt) } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } return err } // GetActivityId returns the value of ActivityId if it is set or its // zero value if it is unset. func (v *ActivityLocalDispatchInfo) GetActivityId() (o string) { if v != nil && v.ActivityId != nil { return *v.ActivityId } return } // IsSetActivityId returns true if ActivityId is not nil. func (v *ActivityLocalDispatchInfo) IsSetActivityId() bool { return v != nil && v.ActivityId != nil } // GetScheduledTimestamp returns the value of ScheduledTimestamp if it is set or its // zero value if it is unset. func (v *ActivityLocalDispatchInfo) GetScheduledTimestamp() (o int64) { if v != nil && v.ScheduledTimestamp != nil { return *v.ScheduledTimestamp } return } // IsSetScheduledTimestamp returns true if ScheduledTimestamp is not nil. func (v *ActivityLocalDispatchInfo) IsSetScheduledTimestamp() bool { return v != nil && v.ScheduledTimestamp != nil } // GetStartedTimestamp returns the value of StartedTimestamp if it is set or its // zero value if it is unset. func (v *ActivityLocalDispatchInfo) GetStartedTimestamp() (o int64) { if v != nil && v.StartedTimestamp != nil { return *v.StartedTimestamp } return } // IsSetStartedTimestamp returns true if StartedTimestamp is not nil. func (v *ActivityLocalDispatchInfo) IsSetStartedTimestamp() bool { return v != nil && v.StartedTimestamp != nil } // GetScheduledTimestampOfThisAttempt returns the value of ScheduledTimestampOfThisAttempt if it is set or its // zero value if it is unset. func (v *ActivityLocalDispatchInfo) GetScheduledTimestampOfThisAttempt() (o int64) { if v != nil && v.ScheduledTimestampOfThisAttempt != nil { return *v.ScheduledTimestampOfThisAttempt } return } // IsSetScheduledTimestampOfThisAttempt returns true if ScheduledTimestampOfThisAttempt is not nil. func (v *ActivityLocalDispatchInfo) IsSetScheduledTimestampOfThisAttempt() bool { return v != nil && v.ScheduledTimestampOfThisAttempt != nil } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *ActivityLocalDispatchInfo) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *ActivityLocalDispatchInfo) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } type ActivityTaskCancelRequestedEventAttributes struct { ActivityId *string `json:"activityId,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` } // ToWire translates a ActivityTaskCancelRequestedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityTaskCancelRequestedEventAttributes) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ActivityId != nil { w, err = wire.NewValueString(*(v.ActivityId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ActivityTaskCancelRequestedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityTaskCancelRequestedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityTaskCancelRequestedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityTaskCancelRequestedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a ActivityTaskCancelRequestedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityTaskCancelRequestedEventAttributes struct could not be encoded. func (v *ActivityTaskCancelRequestedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActivityId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ActivityTaskCancelRequestedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityTaskCancelRequestedEventAttributes struct could not be generated from the wire // representation. func (v *ActivityTaskCancelRequestedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityTaskCancelRequestedEventAttributes // struct. func (v *ActivityTaskCancelRequestedEventAttributes) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ActivityId != nil { fields[i] = fmt.Sprintf("ActivityId: %v", *(v.ActivityId)) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } return fmt.Sprintf("ActivityTaskCancelRequestedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ActivityTaskCancelRequestedEventAttributes match the // provided ActivityTaskCancelRequestedEventAttributes. // // This function performs a deep comparison. func (v *ActivityTaskCancelRequestedEventAttributes) Equals(rhs *ActivityTaskCancelRequestedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ActivityId, rhs.ActivityId) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityTaskCancelRequestedEventAttributes. func (v *ActivityTaskCancelRequestedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActivityId != nil { enc.AddString("activityId", *v.ActivityId) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } return err } // GetActivityId returns the value of ActivityId if it is set or its // zero value if it is unset. func (v *ActivityTaskCancelRequestedEventAttributes) GetActivityId() (o string) { if v != nil && v.ActivityId != nil { return *v.ActivityId } return } // IsSetActivityId returns true if ActivityId is not nil. func (v *ActivityTaskCancelRequestedEventAttributes) IsSetActivityId() bool { return v != nil && v.ActivityId != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskCancelRequestedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *ActivityTaskCancelRequestedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } type ActivityTaskCanceledEventAttributes struct { Details []byte `json:"details,omitempty"` LatestCancelRequestedEventId *int64 `json:"latestCancelRequestedEventId,omitempty"` ScheduledEventId *int64 `json:"scheduledEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a ActivityTaskCanceledEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityTaskCanceledEventAttributes) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.LatestCancelRequestedEventId != nil { w, err = wire.NewValueI64(*(v.LatestCancelRequestedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ScheduledEventId != nil { w, err = wire.NewValueI64(*(v.ScheduledEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ActivityTaskCanceledEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityTaskCanceledEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityTaskCanceledEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityTaskCanceledEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LatestCancelRequestedEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a ActivityTaskCanceledEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityTaskCanceledEventAttributes struct could not be encoded. func (v *ActivityTaskCanceledEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LatestCancelRequestedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LatestCancelRequestedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ActivityTaskCanceledEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityTaskCanceledEventAttributes struct could not be generated from the wire // representation. func (v *ActivityTaskCanceledEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LatestCancelRequestedEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityTaskCanceledEventAttributes // struct. func (v *ActivityTaskCanceledEventAttributes) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.LatestCancelRequestedEventId != nil { fields[i] = fmt.Sprintf("LatestCancelRequestedEventId: %v", *(v.LatestCancelRequestedEventId)) i++ } if v.ScheduledEventId != nil { fields[i] = fmt.Sprintf("ScheduledEventId: %v", *(v.ScheduledEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("ActivityTaskCanceledEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ActivityTaskCanceledEventAttributes match the // provided ActivityTaskCanceledEventAttributes. // // This function performs a deep comparison. func (v *ActivityTaskCanceledEventAttributes) Equals(rhs *ActivityTaskCanceledEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_I64_EqualsPtr(v.LatestCancelRequestedEventId, rhs.LatestCancelRequestedEventId) { return false } if !_I64_EqualsPtr(v.ScheduledEventId, rhs.ScheduledEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityTaskCanceledEventAttributes. func (v *ActivityTaskCanceledEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.LatestCancelRequestedEventId != nil { enc.AddInt64("latestCancelRequestedEventId", *v.LatestCancelRequestedEventId) } if v.ScheduledEventId != nil { enc.AddInt64("scheduledEventId", *v.ScheduledEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *ActivityTaskCanceledEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *ActivityTaskCanceledEventAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetLatestCancelRequestedEventId returns the value of LatestCancelRequestedEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskCanceledEventAttributes) GetLatestCancelRequestedEventId() (o int64) { if v != nil && v.LatestCancelRequestedEventId != nil { return *v.LatestCancelRequestedEventId } return } // IsSetLatestCancelRequestedEventId returns true if LatestCancelRequestedEventId is not nil. func (v *ActivityTaskCanceledEventAttributes) IsSetLatestCancelRequestedEventId() bool { return v != nil && v.LatestCancelRequestedEventId != nil } // GetScheduledEventId returns the value of ScheduledEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskCanceledEventAttributes) GetScheduledEventId() (o int64) { if v != nil && v.ScheduledEventId != nil { return *v.ScheduledEventId } return } // IsSetScheduledEventId returns true if ScheduledEventId is not nil. func (v *ActivityTaskCanceledEventAttributes) IsSetScheduledEventId() bool { return v != nil && v.ScheduledEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskCanceledEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *ActivityTaskCanceledEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *ActivityTaskCanceledEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *ActivityTaskCanceledEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } type ActivityTaskCompletedEventAttributes struct { Result []byte `json:"result,omitempty"` ScheduledEventId *int64 `json:"scheduledEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a ActivityTaskCompletedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityTaskCompletedEventAttributes) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Result != nil { w, err = wire.NewValueBinary(v.Result), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ScheduledEventId != nil { w, err = wire.NewValueI64(*(v.ScheduledEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ActivityTaskCompletedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityTaskCompletedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityTaskCompletedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityTaskCompletedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.Result, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a ActivityTaskCompletedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityTaskCompletedEventAttributes struct could not be encoded. func (v *ActivityTaskCompletedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Result != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Result); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ActivityTaskCompletedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityTaskCompletedEventAttributes struct could not be generated from the wire // representation. func (v *ActivityTaskCompletedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.Result, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityTaskCompletedEventAttributes // struct. func (v *ActivityTaskCompletedEventAttributes) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Result != nil { fields[i] = fmt.Sprintf("Result: %v", v.Result) i++ } if v.ScheduledEventId != nil { fields[i] = fmt.Sprintf("ScheduledEventId: %v", *(v.ScheduledEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("ActivityTaskCompletedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ActivityTaskCompletedEventAttributes match the // provided ActivityTaskCompletedEventAttributes. // // This function performs a deep comparison. func (v *ActivityTaskCompletedEventAttributes) Equals(rhs *ActivityTaskCompletedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Result == nil && rhs.Result == nil) || (v.Result != nil && rhs.Result != nil && bytes.Equal(v.Result, rhs.Result))) { return false } if !_I64_EqualsPtr(v.ScheduledEventId, rhs.ScheduledEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityTaskCompletedEventAttributes. func (v *ActivityTaskCompletedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Result != nil { enc.AddString("result", base64.StdEncoding.EncodeToString(v.Result)) } if v.ScheduledEventId != nil { enc.AddInt64("scheduledEventId", *v.ScheduledEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetResult returns the value of Result if it is set or its // zero value if it is unset. func (v *ActivityTaskCompletedEventAttributes) GetResult() (o []byte) { if v != nil && v.Result != nil { return v.Result } return } // IsSetResult returns true if Result is not nil. func (v *ActivityTaskCompletedEventAttributes) IsSetResult() bool { return v != nil && v.Result != nil } // GetScheduledEventId returns the value of ScheduledEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskCompletedEventAttributes) GetScheduledEventId() (o int64) { if v != nil && v.ScheduledEventId != nil { return *v.ScheduledEventId } return } // IsSetScheduledEventId returns true if ScheduledEventId is not nil. func (v *ActivityTaskCompletedEventAttributes) IsSetScheduledEventId() bool { return v != nil && v.ScheduledEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskCompletedEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *ActivityTaskCompletedEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *ActivityTaskCompletedEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *ActivityTaskCompletedEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } type ActivityTaskFailedEventAttributes struct { Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` ScheduledEventId *int64 `json:"scheduledEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a ActivityTaskFailedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityTaskFailedEventAttributes) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ScheduledEventId != nil { w, err = wire.NewValueI64(*(v.ScheduledEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ActivityTaskFailedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityTaskFailedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityTaskFailedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityTaskFailedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a ActivityTaskFailedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityTaskFailedEventAttributes struct could not be encoded. func (v *ActivityTaskFailedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ActivityTaskFailedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityTaskFailedEventAttributes struct could not be generated from the wire // representation. func (v *ActivityTaskFailedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityTaskFailedEventAttributes // struct. func (v *ActivityTaskFailedEventAttributes) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.ScheduledEventId != nil { fields[i] = fmt.Sprintf("ScheduledEventId: %v", *(v.ScheduledEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("ActivityTaskFailedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ActivityTaskFailedEventAttributes match the // provided ActivityTaskFailedEventAttributes. // // This function performs a deep comparison. func (v *ActivityTaskFailedEventAttributes) Equals(rhs *ActivityTaskFailedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_I64_EqualsPtr(v.ScheduledEventId, rhs.ScheduledEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityTaskFailedEventAttributes. func (v *ActivityTaskFailedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.ScheduledEventId != nil { enc.AddInt64("scheduledEventId", *v.ScheduledEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *ActivityTaskFailedEventAttributes) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *ActivityTaskFailedEventAttributes) IsSetReason() bool { return v != nil && v.Reason != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *ActivityTaskFailedEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *ActivityTaskFailedEventAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetScheduledEventId returns the value of ScheduledEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskFailedEventAttributes) GetScheduledEventId() (o int64) { if v != nil && v.ScheduledEventId != nil { return *v.ScheduledEventId } return } // IsSetScheduledEventId returns true if ScheduledEventId is not nil. func (v *ActivityTaskFailedEventAttributes) IsSetScheduledEventId() bool { return v != nil && v.ScheduledEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskFailedEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *ActivityTaskFailedEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *ActivityTaskFailedEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *ActivityTaskFailedEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } type ActivityTaskScheduledEventAttributes struct { ActivityId *string `json:"activityId,omitempty"` ActivityType *ActivityType `json:"activityType,omitempty"` Domain *string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ScheduleToCloseTimeoutSeconds *int32 `json:"scheduleToCloseTimeoutSeconds,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` StartToCloseTimeoutSeconds *int32 `json:"startToCloseTimeoutSeconds,omitempty"` HeartbeatTimeoutSeconds *int32 `json:"heartbeatTimeoutSeconds,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` Header *Header `json:"header,omitempty"` } // ToWire translates a ActivityTaskScheduledEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityTaskScheduledEventAttributes) ToWire() (wire.Value, error) { var ( fields [12]wire.Field i int = 0 w wire.Value err error ) if v.ActivityId != nil { w, err = wire.NewValueString(*(v.ActivityId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ActivityType != nil { w, err = v.ActivityType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 25, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ScheduleToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ScheduleToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 45, Value: w} i++ } if v.ScheduleToStartTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ScheduleToStartTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.StartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.StartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 55, Value: w} i++ } if v.HeartbeatTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.HeartbeatTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.RetryPolicy != nil { w, err = v.RetryPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ActivityType_Read(w wire.Value) (*ActivityType, error) { var v ActivityType err := v.FromWire(w) return &v, err } func _TaskList_Read(w wire.Value) (*TaskList, error) { var v TaskList err := v.FromWire(w) return &v, err } func _RetryPolicy_Read(w wire.Value) (*RetryPolicy, error) { var v RetryPolicy err := v.FromWire(w) return &v, err } func _Header_Read(w wire.Value) (*Header, error) { var v Header err := v.FromWire(w) return &v, err } // FromWire deserializes a ActivityTaskScheduledEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityTaskScheduledEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityTaskScheduledEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityTaskScheduledEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.ActivityType, err = _ActivityType_Read(field.Value) if err != nil { return err } } case 25: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 45: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ScheduleToCloseTimeoutSeconds = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } } case 55: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.StartToCloseTimeoutSeconds = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.HeartbeatTimeoutSeconds = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TStruct { v.RetryPolicy, err = _RetryPolicy_Read(field.Value) if err != nil { return err } } case 120: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ActivityTaskScheduledEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityTaskScheduledEventAttributes struct could not be encoded. func (v *ActivityTaskScheduledEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActivityId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 25, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 45, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ScheduleToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleToStartTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ScheduleToStartTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 55, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.StartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HeartbeatTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.HeartbeatTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TStruct}); err != nil { return err } if err := v.RetryPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ActivityType_Decode(sr stream.Reader) (*ActivityType, error) { var v ActivityType err := v.Decode(sr) return &v, err } func _TaskList_Decode(sr stream.Reader) (*TaskList, error) { var v TaskList err := v.Decode(sr) return &v, err } func _RetryPolicy_Decode(sr stream.Reader) (*RetryPolicy, error) { var v RetryPolicy err := v.Decode(sr) return &v, err } func _Header_Decode(sr stream.Reader) (*Header, error) { var v Header err := v.Decode(sr) return &v, err } // Decode deserializes a ActivityTaskScheduledEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityTaskScheduledEventAttributes struct could not be generated from the wire // representation. func (v *ActivityTaskScheduledEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.ActivityType, err = _ActivityType_Decode(sr) if err != nil { return err } case fh.ID == 25 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 45 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ScheduleToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } case fh.ID == 55 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.StartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.HeartbeatTimeoutSeconds = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TStruct: v.RetryPolicy, err = _RetryPolicy_Decode(sr) if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityTaskScheduledEventAttributes // struct. func (v *ActivityTaskScheduledEventAttributes) String() string { if v == nil { return "" } var fields [12]string i := 0 if v.ActivityId != nil { fields[i] = fmt.Sprintf("ActivityId: %v", *(v.ActivityId)) i++ } if v.ActivityType != nil { fields[i] = fmt.Sprintf("ActivityType: %v", v.ActivityType) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.ScheduleToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ScheduleToCloseTimeoutSeconds: %v", *(v.ScheduleToCloseTimeoutSeconds)) i++ } if v.ScheduleToStartTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ScheduleToStartTimeoutSeconds: %v", *(v.ScheduleToStartTimeoutSeconds)) i++ } if v.StartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("StartToCloseTimeoutSeconds: %v", *(v.StartToCloseTimeoutSeconds)) i++ } if v.HeartbeatTimeoutSeconds != nil { fields[i] = fmt.Sprintf("HeartbeatTimeoutSeconds: %v", *(v.HeartbeatTimeoutSeconds)) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.RetryPolicy != nil { fields[i] = fmt.Sprintf("RetryPolicy: %v", v.RetryPolicy) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } return fmt.Sprintf("ActivityTaskScheduledEventAttributes{%v}", strings.Join(fields[:i], ", ")) } func _I32_EqualsPtr(lhs, rhs *int32) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ActivityTaskScheduledEventAttributes match the // provided ActivityTaskScheduledEventAttributes. // // This function performs a deep comparison. func (v *ActivityTaskScheduledEventAttributes) Equals(rhs *ActivityTaskScheduledEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ActivityId, rhs.ActivityId) { return false } if !((v.ActivityType == nil && rhs.ActivityType == nil) || (v.ActivityType != nil && rhs.ActivityType != nil && v.ActivityType.Equals(rhs.ActivityType))) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_I32_EqualsPtr(v.ScheduleToCloseTimeoutSeconds, rhs.ScheduleToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.ScheduleToStartTimeoutSeconds, rhs.ScheduleToStartTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.StartToCloseTimeoutSeconds, rhs.StartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.HeartbeatTimeoutSeconds, rhs.HeartbeatTimeoutSeconds) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !((v.RetryPolicy == nil && rhs.RetryPolicy == nil) || (v.RetryPolicy != nil && rhs.RetryPolicy != nil && v.RetryPolicy.Equals(rhs.RetryPolicy))) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityTaskScheduledEventAttributes. func (v *ActivityTaskScheduledEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActivityId != nil { enc.AddString("activityId", *v.ActivityId) } if v.ActivityType != nil { err = multierr.Append(err, enc.AddObject("activityType", v.ActivityType)) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.ScheduleToCloseTimeoutSeconds != nil { enc.AddInt32("scheduleToCloseTimeoutSeconds", *v.ScheduleToCloseTimeoutSeconds) } if v.ScheduleToStartTimeoutSeconds != nil { enc.AddInt32("scheduleToStartTimeoutSeconds", *v.ScheduleToStartTimeoutSeconds) } if v.StartToCloseTimeoutSeconds != nil { enc.AddInt32("startToCloseTimeoutSeconds", *v.StartToCloseTimeoutSeconds) } if v.HeartbeatTimeoutSeconds != nil { enc.AddInt32("heartbeatTimeoutSeconds", *v.HeartbeatTimeoutSeconds) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.RetryPolicy != nil { err = multierr.Append(err, enc.AddObject("retryPolicy", v.RetryPolicy)) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } return err } // GetActivityId returns the value of ActivityId if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetActivityId() (o string) { if v != nil && v.ActivityId != nil { return *v.ActivityId } return } // IsSetActivityId returns true if ActivityId is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetActivityId() bool { return v != nil && v.ActivityId != nil } // GetActivityType returns the value of ActivityType if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetActivityType() (o *ActivityType) { if v != nil && v.ActivityType != nil { return v.ActivityType } return } // IsSetActivityType returns true if ActivityType is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetActivityType() bool { return v != nil && v.ActivityType != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetInput() bool { return v != nil && v.Input != nil } // GetScheduleToCloseTimeoutSeconds returns the value of ScheduleToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetScheduleToCloseTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToCloseTimeoutSeconds != nil { return *v.ScheduleToCloseTimeoutSeconds } return } // IsSetScheduleToCloseTimeoutSeconds returns true if ScheduleToCloseTimeoutSeconds is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetScheduleToCloseTimeoutSeconds() bool { return v != nil && v.ScheduleToCloseTimeoutSeconds != nil } // GetScheduleToStartTimeoutSeconds returns the value of ScheduleToStartTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // IsSetScheduleToStartTimeoutSeconds returns true if ScheduleToStartTimeoutSeconds is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetScheduleToStartTimeoutSeconds() bool { return v != nil && v.ScheduleToStartTimeoutSeconds != nil } // GetStartToCloseTimeoutSeconds returns the value of StartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.StartToCloseTimeoutSeconds != nil { return *v.StartToCloseTimeoutSeconds } return } // IsSetStartToCloseTimeoutSeconds returns true if StartToCloseTimeoutSeconds is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetStartToCloseTimeoutSeconds() bool { return v != nil && v.StartToCloseTimeoutSeconds != nil } // GetHeartbeatTimeoutSeconds returns the value of HeartbeatTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetHeartbeatTimeoutSeconds() (o int32) { if v != nil && v.HeartbeatTimeoutSeconds != nil { return *v.HeartbeatTimeoutSeconds } return } // IsSetHeartbeatTimeoutSeconds returns true if HeartbeatTimeoutSeconds is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetHeartbeatTimeoutSeconds() bool { return v != nil && v.HeartbeatTimeoutSeconds != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetRetryPolicy returns the value of RetryPolicy if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetRetryPolicy() (o *RetryPolicy) { if v != nil && v.RetryPolicy != nil { return v.RetryPolicy } return } // IsSetRetryPolicy returns true if RetryPolicy is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetRetryPolicy() bool { return v != nil && v.RetryPolicy != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *ActivityTaskScheduledEventAttributes) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *ActivityTaskScheduledEventAttributes) IsSetHeader() bool { return v != nil && v.Header != nil } type ActivityTaskStartedEventAttributes struct { ScheduledEventId *int64 `json:"scheduledEventId,omitempty"` Identity *string `json:"identity,omitempty"` RequestId *string `json:"requestId,omitempty"` Attempt *int32 `json:"attempt,omitempty"` LastFailureReason *string `json:"lastFailureReason,omitempty"` LastFailureDetails []byte `json:"lastFailureDetails,omitempty"` } // ToWire translates a ActivityTaskStartedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityTaskStartedEventAttributes) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.ScheduledEventId != nil { w, err = wire.NewValueI64(*(v.ScheduledEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI32(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.LastFailureReason != nil { w, err = wire.NewValueString(*(v.LastFailureReason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.LastFailureDetails != nil { w, err = wire.NewValueBinary(v.LastFailureDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ActivityTaskStartedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityTaskStartedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityTaskStartedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityTaskStartedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Attempt = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.LastFailureReason = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.LastFailureDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a ActivityTaskStartedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityTaskStartedEventAttributes struct could not be encoded. func (v *ActivityTaskStartedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ScheduledEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFailureReason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.LastFailureReason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFailureDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.LastFailureDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ActivityTaskStartedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityTaskStartedEventAttributes struct could not be generated from the wire // representation. func (v *ActivityTaskStartedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Attempt = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.LastFailureReason = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.LastFailureDetails, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityTaskStartedEventAttributes // struct. func (v *ActivityTaskStartedEventAttributes) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.ScheduledEventId != nil { fields[i] = fmt.Sprintf("ScheduledEventId: %v", *(v.ScheduledEventId)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.LastFailureReason != nil { fields[i] = fmt.Sprintf("LastFailureReason: %v", *(v.LastFailureReason)) i++ } if v.LastFailureDetails != nil { fields[i] = fmt.Sprintf("LastFailureDetails: %v", v.LastFailureDetails) i++ } return fmt.Sprintf("ActivityTaskStartedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ActivityTaskStartedEventAttributes match the // provided ActivityTaskStartedEventAttributes. // // This function performs a deep comparison. func (v *ActivityTaskStartedEventAttributes) Equals(rhs *ActivityTaskStartedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.ScheduledEventId, rhs.ScheduledEventId) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } if !_I32_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_String_EqualsPtr(v.LastFailureReason, rhs.LastFailureReason) { return false } if !((v.LastFailureDetails == nil && rhs.LastFailureDetails == nil) || (v.LastFailureDetails != nil && rhs.LastFailureDetails != nil && bytes.Equal(v.LastFailureDetails, rhs.LastFailureDetails))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityTaskStartedEventAttributes. func (v *ActivityTaskStartedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ScheduledEventId != nil { enc.AddInt64("scheduledEventId", *v.ScheduledEventId) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } if v.Attempt != nil { enc.AddInt32("attempt", *v.Attempt) } if v.LastFailureReason != nil { enc.AddString("lastFailureReason", *v.LastFailureReason) } if v.LastFailureDetails != nil { enc.AddString("lastFailureDetails", base64.StdEncoding.EncodeToString(v.LastFailureDetails)) } return err } // GetScheduledEventId returns the value of ScheduledEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskStartedEventAttributes) GetScheduledEventId() (o int64) { if v != nil && v.ScheduledEventId != nil { return *v.ScheduledEventId } return } // IsSetScheduledEventId returns true if ScheduledEventId is not nil. func (v *ActivityTaskStartedEventAttributes) IsSetScheduledEventId() bool { return v != nil && v.ScheduledEventId != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *ActivityTaskStartedEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *ActivityTaskStartedEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *ActivityTaskStartedEventAttributes) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *ActivityTaskStartedEventAttributes) IsSetRequestId() bool { return v != nil && v.RequestId != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *ActivityTaskStartedEventAttributes) GetAttempt() (o int32) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *ActivityTaskStartedEventAttributes) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetLastFailureReason returns the value of LastFailureReason if it is set or its // zero value if it is unset. func (v *ActivityTaskStartedEventAttributes) GetLastFailureReason() (o string) { if v != nil && v.LastFailureReason != nil { return *v.LastFailureReason } return } // IsSetLastFailureReason returns true if LastFailureReason is not nil. func (v *ActivityTaskStartedEventAttributes) IsSetLastFailureReason() bool { return v != nil && v.LastFailureReason != nil } // GetLastFailureDetails returns the value of LastFailureDetails if it is set or its // zero value if it is unset. func (v *ActivityTaskStartedEventAttributes) GetLastFailureDetails() (o []byte) { if v != nil && v.LastFailureDetails != nil { return v.LastFailureDetails } return } // IsSetLastFailureDetails returns true if LastFailureDetails is not nil. func (v *ActivityTaskStartedEventAttributes) IsSetLastFailureDetails() bool { return v != nil && v.LastFailureDetails != nil } type ActivityTaskTimedOutEventAttributes struct { Details []byte `json:"details,omitempty"` ScheduledEventId *int64 `json:"scheduledEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` TimeoutType *TimeoutType `json:"timeoutType,omitempty"` LastFailureReason *string `json:"lastFailureReason,omitempty"` LastFailureDetails []byte `json:"lastFailureDetails,omitempty"` } // ToWire translates a ActivityTaskTimedOutEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityTaskTimedOutEventAttributes) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.ScheduledEventId != nil { w, err = wire.NewValueI64(*(v.ScheduledEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TimeoutType != nil { w, err = v.TimeoutType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.LastFailureReason != nil { w, err = wire.NewValueString(*(v.LastFailureReason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.LastFailureDetails != nil { w, err = wire.NewValueBinary(v.LastFailureDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TimeoutType_Read(w wire.Value) (TimeoutType, error) { var v TimeoutType err := v.FromWire(w) return v, err } // FromWire deserializes a ActivityTaskTimedOutEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityTaskTimedOutEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityTaskTimedOutEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityTaskTimedOutEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 5: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x TimeoutType x, err = _TimeoutType_Read(field.Value) v.TimeoutType = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.LastFailureReason = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.LastFailureDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a ActivityTaskTimedOutEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityTaskTimedOutEventAttributes struct could not be encoded. func (v *ActivityTaskTimedOutEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimeoutType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := v.TimeoutType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFailureReason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.LastFailureReason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFailureDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.LastFailureDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TimeoutType_Decode(sr stream.Reader) (TimeoutType, error) { var v TimeoutType err := v.Decode(sr) return v, err } // Decode deserializes a ActivityTaskTimedOutEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityTaskTimedOutEventAttributes struct could not be generated from the wire // representation. func (v *ActivityTaskTimedOutEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 5 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x TimeoutType x, err = _TimeoutType_Decode(sr) v.TimeoutType = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.LastFailureReason = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.LastFailureDetails, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityTaskTimedOutEventAttributes // struct. func (v *ActivityTaskTimedOutEventAttributes) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.ScheduledEventId != nil { fields[i] = fmt.Sprintf("ScheduledEventId: %v", *(v.ScheduledEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.TimeoutType != nil { fields[i] = fmt.Sprintf("TimeoutType: %v", *(v.TimeoutType)) i++ } if v.LastFailureReason != nil { fields[i] = fmt.Sprintf("LastFailureReason: %v", *(v.LastFailureReason)) i++ } if v.LastFailureDetails != nil { fields[i] = fmt.Sprintf("LastFailureDetails: %v", v.LastFailureDetails) i++ } return fmt.Sprintf("ActivityTaskTimedOutEventAttributes{%v}", strings.Join(fields[:i], ", ")) } func _TimeoutType_EqualsPtr(lhs, rhs *TimeoutType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ActivityTaskTimedOutEventAttributes match the // provided ActivityTaskTimedOutEventAttributes. // // This function performs a deep comparison. func (v *ActivityTaskTimedOutEventAttributes) Equals(rhs *ActivityTaskTimedOutEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_I64_EqualsPtr(v.ScheduledEventId, rhs.ScheduledEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_TimeoutType_EqualsPtr(v.TimeoutType, rhs.TimeoutType) { return false } if !_String_EqualsPtr(v.LastFailureReason, rhs.LastFailureReason) { return false } if !((v.LastFailureDetails == nil && rhs.LastFailureDetails == nil) || (v.LastFailureDetails != nil && rhs.LastFailureDetails != nil && bytes.Equal(v.LastFailureDetails, rhs.LastFailureDetails))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityTaskTimedOutEventAttributes. func (v *ActivityTaskTimedOutEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.ScheduledEventId != nil { enc.AddInt64("scheduledEventId", *v.ScheduledEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.TimeoutType != nil { err = multierr.Append(err, enc.AddObject("timeoutType", *v.TimeoutType)) } if v.LastFailureReason != nil { enc.AddString("lastFailureReason", *v.LastFailureReason) } if v.LastFailureDetails != nil { enc.AddString("lastFailureDetails", base64.StdEncoding.EncodeToString(v.LastFailureDetails)) } return err } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *ActivityTaskTimedOutEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *ActivityTaskTimedOutEventAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetScheduledEventId returns the value of ScheduledEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskTimedOutEventAttributes) GetScheduledEventId() (o int64) { if v != nil && v.ScheduledEventId != nil { return *v.ScheduledEventId } return } // IsSetScheduledEventId returns true if ScheduledEventId is not nil. func (v *ActivityTaskTimedOutEventAttributes) IsSetScheduledEventId() bool { return v != nil && v.ScheduledEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *ActivityTaskTimedOutEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *ActivityTaskTimedOutEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetTimeoutType returns the value of TimeoutType if it is set or its // zero value if it is unset. func (v *ActivityTaskTimedOutEventAttributes) GetTimeoutType() (o TimeoutType) { if v != nil && v.TimeoutType != nil { return *v.TimeoutType } return } // IsSetTimeoutType returns true if TimeoutType is not nil. func (v *ActivityTaskTimedOutEventAttributes) IsSetTimeoutType() bool { return v != nil && v.TimeoutType != nil } // GetLastFailureReason returns the value of LastFailureReason if it is set or its // zero value if it is unset. func (v *ActivityTaskTimedOutEventAttributes) GetLastFailureReason() (o string) { if v != nil && v.LastFailureReason != nil { return *v.LastFailureReason } return } // IsSetLastFailureReason returns true if LastFailureReason is not nil. func (v *ActivityTaskTimedOutEventAttributes) IsSetLastFailureReason() bool { return v != nil && v.LastFailureReason != nil } // GetLastFailureDetails returns the value of LastFailureDetails if it is set or its // zero value if it is unset. func (v *ActivityTaskTimedOutEventAttributes) GetLastFailureDetails() (o []byte) { if v != nil && v.LastFailureDetails != nil { return v.LastFailureDetails } return } // IsSetLastFailureDetails returns true if LastFailureDetails is not nil. func (v *ActivityTaskTimedOutEventAttributes) IsSetLastFailureDetails() bool { return v != nil && v.LastFailureDetails != nil } type ActivityType struct { Name *string `json:"name,omitempty"` } // ToWire translates a ActivityType struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityType) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ActivityType struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityType struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityType // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityType) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } } } return nil } // Encode serializes a ActivityType struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityType struct could not be encoded. func (v *ActivityType) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ActivityType struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityType struct could not be generated from the wire // representation. func (v *ActivityType) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityType // struct. func (v *ActivityType) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } return fmt.Sprintf("ActivityType{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ActivityType match the // provided ActivityType. // // This function performs a deep comparison. func (v *ActivityType) Equals(rhs *ActivityType) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityType. func (v *ActivityType) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *ActivityType) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *ActivityType) IsSetName() bool { return v != nil && v.Name != nil } // Any is a logical duplicate of google.protobuf.Any. // // The intent of the type is the same, but it is not intended to be directly // compatible with google.protobuf.Any or any Thrift equivalent - this blob is // RPC-type agnostic by design (as the underlying data may be transported over // proto or thrift), and the data-bytes may be in any encoding. // // This is intentionally different from DataBlob, which supports only a handful // of known encodings so it can be interpreted everywhere. Any supports literally // any contents, and needs to be considered opaque until it is given to something // that is expecting it. // // See ValueType to interpret the contents. type Any struct { ValueType *string `json:"ValueType,omitempty"` Value []byte `json:"Value,omitempty"` } // ToWire translates a Any struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *Any) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ValueType != nil { w, err = wire.NewValueString(*(v.ValueType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Value != nil { w, err = wire.NewValueBinary(v.Value), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a Any struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a Any struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v Any // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *Any) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ValueType = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Value, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a Any struct directly into bytes, without going // through an intermediary type. // // An error is returned if a Any struct could not be encoded. func (v *Any) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ValueType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ValueType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Value != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Value); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a Any struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a Any struct could not be generated from the wire // representation. func (v *Any) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ValueType = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Value, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a Any // struct. func (v *Any) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ValueType != nil { fields[i] = fmt.Sprintf("ValueType: %v", *(v.ValueType)) i++ } if v.Value != nil { fields[i] = fmt.Sprintf("Value: %v", v.Value) i++ } return fmt.Sprintf("Any{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this Any match the // provided Any. // // This function performs a deep comparison. func (v *Any) Equals(rhs *Any) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ValueType, rhs.ValueType) { return false } if !((v.Value == nil && rhs.Value == nil) || (v.Value != nil && rhs.Value != nil && bytes.Equal(v.Value, rhs.Value))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of Any. func (v *Any) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ValueType != nil { enc.AddString("ValueType", *v.ValueType) } if v.Value != nil { enc.AddString("Value", base64.StdEncoding.EncodeToString(v.Value)) } return err } // GetValueType returns the value of ValueType if it is set or its // zero value if it is unset. func (v *Any) GetValueType() (o string) { if v != nil && v.ValueType != nil { return *v.ValueType } return } // IsSetValueType returns true if ValueType is not nil. func (v *Any) IsSetValueType() bool { return v != nil && v.ValueType != nil } // GetValue returns the value of Value if it is set or its // zero value if it is unset. func (v *Any) GetValue() (o []byte) { if v != nil && v.Value != nil { return v.Value } return } // IsSetValue returns true if Value is not nil. func (v *Any) IsSetValue() bool { return v != nil && v.Value != nil } type ApplyParentClosePolicyAttributes struct { ChildDomainID *string `json:"childDomainID,omitempty"` ChildWorkflowID *string `json:"childWorkflowID,omitempty"` ChildRunID *string `json:"childRunID,omitempty"` ParentClosePolicy *ParentClosePolicy `json:"parentClosePolicy,omitempty"` } // ToWire translates a ApplyParentClosePolicyAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ApplyParentClosePolicyAttributes) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.ChildDomainID != nil { w, err = wire.NewValueString(*(v.ChildDomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ChildWorkflowID != nil { w, err = wire.NewValueString(*(v.ChildWorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ChildRunID != nil { w, err = wire.NewValueString(*(v.ChildRunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ParentClosePolicy != nil { w, err = v.ParentClosePolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ParentClosePolicy_Read(w wire.Value) (ParentClosePolicy, error) { var v ParentClosePolicy err := v.FromWire(w) return v, err } // FromWire deserializes a ApplyParentClosePolicyAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ApplyParentClosePolicyAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ApplyParentClosePolicyAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ApplyParentClosePolicyAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ChildDomainID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ChildWorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ChildRunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x ParentClosePolicy x, err = _ParentClosePolicy_Read(field.Value) v.ParentClosePolicy = &x if err != nil { return err } } } } return nil } // Encode serializes a ApplyParentClosePolicyAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ApplyParentClosePolicyAttributes struct could not be encoded. func (v *ApplyParentClosePolicyAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ChildDomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ChildDomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ChildWorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ChildRunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentClosePolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := v.ParentClosePolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ParentClosePolicy_Decode(sr stream.Reader) (ParentClosePolicy, error) { var v ParentClosePolicy err := v.Decode(sr) return v, err } // Decode deserializes a ApplyParentClosePolicyAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ApplyParentClosePolicyAttributes struct could not be generated from the wire // representation. func (v *ApplyParentClosePolicyAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ChildDomainID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ChildWorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ChildRunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x ParentClosePolicy x, err = _ParentClosePolicy_Decode(sr) v.ParentClosePolicy = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ApplyParentClosePolicyAttributes // struct. func (v *ApplyParentClosePolicyAttributes) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.ChildDomainID != nil { fields[i] = fmt.Sprintf("ChildDomainID: %v", *(v.ChildDomainID)) i++ } if v.ChildWorkflowID != nil { fields[i] = fmt.Sprintf("ChildWorkflowID: %v", *(v.ChildWorkflowID)) i++ } if v.ChildRunID != nil { fields[i] = fmt.Sprintf("ChildRunID: %v", *(v.ChildRunID)) i++ } if v.ParentClosePolicy != nil { fields[i] = fmt.Sprintf("ParentClosePolicy: %v", *(v.ParentClosePolicy)) i++ } return fmt.Sprintf("ApplyParentClosePolicyAttributes{%v}", strings.Join(fields[:i], ", ")) } func _ParentClosePolicy_EqualsPtr(lhs, rhs *ParentClosePolicy) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ApplyParentClosePolicyAttributes match the // provided ApplyParentClosePolicyAttributes. // // This function performs a deep comparison. func (v *ApplyParentClosePolicyAttributes) Equals(rhs *ApplyParentClosePolicyAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ChildDomainID, rhs.ChildDomainID) { return false } if !_String_EqualsPtr(v.ChildWorkflowID, rhs.ChildWorkflowID) { return false } if !_String_EqualsPtr(v.ChildRunID, rhs.ChildRunID) { return false } if !_ParentClosePolicy_EqualsPtr(v.ParentClosePolicy, rhs.ParentClosePolicy) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ApplyParentClosePolicyAttributes. func (v *ApplyParentClosePolicyAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ChildDomainID != nil { enc.AddString("childDomainID", *v.ChildDomainID) } if v.ChildWorkflowID != nil { enc.AddString("childWorkflowID", *v.ChildWorkflowID) } if v.ChildRunID != nil { enc.AddString("childRunID", *v.ChildRunID) } if v.ParentClosePolicy != nil { err = multierr.Append(err, enc.AddObject("parentClosePolicy", *v.ParentClosePolicy)) } return err } // GetChildDomainID returns the value of ChildDomainID if it is set or its // zero value if it is unset. func (v *ApplyParentClosePolicyAttributes) GetChildDomainID() (o string) { if v != nil && v.ChildDomainID != nil { return *v.ChildDomainID } return } // IsSetChildDomainID returns true if ChildDomainID is not nil. func (v *ApplyParentClosePolicyAttributes) IsSetChildDomainID() bool { return v != nil && v.ChildDomainID != nil } // GetChildWorkflowID returns the value of ChildWorkflowID if it is set or its // zero value if it is unset. func (v *ApplyParentClosePolicyAttributes) GetChildWorkflowID() (o string) { if v != nil && v.ChildWorkflowID != nil { return *v.ChildWorkflowID } return } // IsSetChildWorkflowID returns true if ChildWorkflowID is not nil. func (v *ApplyParentClosePolicyAttributes) IsSetChildWorkflowID() bool { return v != nil && v.ChildWorkflowID != nil } // GetChildRunID returns the value of ChildRunID if it is set or its // zero value if it is unset. func (v *ApplyParentClosePolicyAttributes) GetChildRunID() (o string) { if v != nil && v.ChildRunID != nil { return *v.ChildRunID } return } // IsSetChildRunID returns true if ChildRunID is not nil. func (v *ApplyParentClosePolicyAttributes) IsSetChildRunID() bool { return v != nil && v.ChildRunID != nil } // GetParentClosePolicy returns the value of ParentClosePolicy if it is set or its // zero value if it is unset. func (v *ApplyParentClosePolicyAttributes) GetParentClosePolicy() (o ParentClosePolicy) { if v != nil && v.ParentClosePolicy != nil { return *v.ParentClosePolicy } return } // IsSetParentClosePolicy returns true if ParentClosePolicy is not nil. func (v *ApplyParentClosePolicyAttributes) IsSetParentClosePolicy() bool { return v != nil && v.ParentClosePolicy != nil } type ApplyParentClosePolicyRequest struct { Child *ApplyParentClosePolicyAttributes `json:"child,omitempty"` Status *ApplyParentClosePolicyStatus `json:"status,omitempty"` } // ToWire translates a ApplyParentClosePolicyRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ApplyParentClosePolicyRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Child != nil { w, err = v.Child.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Status != nil { w, err = v.Status.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ApplyParentClosePolicyAttributes_Read(w wire.Value) (*ApplyParentClosePolicyAttributes, error) { var v ApplyParentClosePolicyAttributes err := v.FromWire(w) return &v, err } func _ApplyParentClosePolicyStatus_Read(w wire.Value) (*ApplyParentClosePolicyStatus, error) { var v ApplyParentClosePolicyStatus err := v.FromWire(w) return &v, err } // FromWire deserializes a ApplyParentClosePolicyRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ApplyParentClosePolicyRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ApplyParentClosePolicyRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ApplyParentClosePolicyRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Child, err = _ApplyParentClosePolicyAttributes_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Status, err = _ApplyParentClosePolicyStatus_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ApplyParentClosePolicyRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ApplyParentClosePolicyRequest struct could not be encoded. func (v *ApplyParentClosePolicyRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Child != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Child.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Status != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Status.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ApplyParentClosePolicyAttributes_Decode(sr stream.Reader) (*ApplyParentClosePolicyAttributes, error) { var v ApplyParentClosePolicyAttributes err := v.Decode(sr) return &v, err } func _ApplyParentClosePolicyStatus_Decode(sr stream.Reader) (*ApplyParentClosePolicyStatus, error) { var v ApplyParentClosePolicyStatus err := v.Decode(sr) return &v, err } // Decode deserializes a ApplyParentClosePolicyRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ApplyParentClosePolicyRequest struct could not be generated from the wire // representation. func (v *ApplyParentClosePolicyRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Child, err = _ApplyParentClosePolicyAttributes_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Status, err = _ApplyParentClosePolicyStatus_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ApplyParentClosePolicyRequest // struct. func (v *ApplyParentClosePolicyRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Child != nil { fields[i] = fmt.Sprintf("Child: %v", v.Child) i++ } if v.Status != nil { fields[i] = fmt.Sprintf("Status: %v", v.Status) i++ } return fmt.Sprintf("ApplyParentClosePolicyRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ApplyParentClosePolicyRequest match the // provided ApplyParentClosePolicyRequest. // // This function performs a deep comparison. func (v *ApplyParentClosePolicyRequest) Equals(rhs *ApplyParentClosePolicyRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Child == nil && rhs.Child == nil) || (v.Child != nil && rhs.Child != nil && v.Child.Equals(rhs.Child))) { return false } if !((v.Status == nil && rhs.Status == nil) || (v.Status != nil && rhs.Status != nil && v.Status.Equals(rhs.Status))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ApplyParentClosePolicyRequest. func (v *ApplyParentClosePolicyRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Child != nil { err = multierr.Append(err, enc.AddObject("child", v.Child)) } if v.Status != nil { err = multierr.Append(err, enc.AddObject("status", v.Status)) } return err } // GetChild returns the value of Child if it is set or its // zero value if it is unset. func (v *ApplyParentClosePolicyRequest) GetChild() (o *ApplyParentClosePolicyAttributes) { if v != nil && v.Child != nil { return v.Child } return } // IsSetChild returns true if Child is not nil. func (v *ApplyParentClosePolicyRequest) IsSetChild() bool { return v != nil && v.Child != nil } // GetStatus returns the value of Status if it is set or its // zero value if it is unset. func (v *ApplyParentClosePolicyRequest) GetStatus() (o *ApplyParentClosePolicyStatus) { if v != nil && v.Status != nil { return v.Status } return } // IsSetStatus returns true if Status is not nil. func (v *ApplyParentClosePolicyRequest) IsSetStatus() bool { return v != nil && v.Status != nil } type ApplyParentClosePolicyResult struct { Child *ApplyParentClosePolicyAttributes `json:"child,omitempty"` FailedCause *CrossClusterTaskFailedCause `json:"failedCause,omitempty"` } // ToWire translates a ApplyParentClosePolicyResult struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ApplyParentClosePolicyResult) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Child != nil { w, err = v.Child.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.FailedCause != nil { w, err = v.FailedCause.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CrossClusterTaskFailedCause_Read(w wire.Value) (CrossClusterTaskFailedCause, error) { var v CrossClusterTaskFailedCause err := v.FromWire(w) return v, err } // FromWire deserializes a ApplyParentClosePolicyResult struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ApplyParentClosePolicyResult struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ApplyParentClosePolicyResult // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ApplyParentClosePolicyResult) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Child, err = _ApplyParentClosePolicyAttributes_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x CrossClusterTaskFailedCause x, err = _CrossClusterTaskFailedCause_Read(field.Value) v.FailedCause = &x if err != nil { return err } } } } return nil } // Encode serializes a ApplyParentClosePolicyResult struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ApplyParentClosePolicyResult struct could not be encoded. func (v *ApplyParentClosePolicyResult) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Child != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Child.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailedCause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := v.FailedCause.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CrossClusterTaskFailedCause_Decode(sr stream.Reader) (CrossClusterTaskFailedCause, error) { var v CrossClusterTaskFailedCause err := v.Decode(sr) return v, err } // Decode deserializes a ApplyParentClosePolicyResult struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ApplyParentClosePolicyResult struct could not be generated from the wire // representation. func (v *ApplyParentClosePolicyResult) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Child, err = _ApplyParentClosePolicyAttributes_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x CrossClusterTaskFailedCause x, err = _CrossClusterTaskFailedCause_Decode(sr) v.FailedCause = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ApplyParentClosePolicyResult // struct. func (v *ApplyParentClosePolicyResult) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Child != nil { fields[i] = fmt.Sprintf("Child: %v", v.Child) i++ } if v.FailedCause != nil { fields[i] = fmt.Sprintf("FailedCause: %v", *(v.FailedCause)) i++ } return fmt.Sprintf("ApplyParentClosePolicyResult{%v}", strings.Join(fields[:i], ", ")) } func _CrossClusterTaskFailedCause_EqualsPtr(lhs, rhs *CrossClusterTaskFailedCause) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ApplyParentClosePolicyResult match the // provided ApplyParentClosePolicyResult. // // This function performs a deep comparison. func (v *ApplyParentClosePolicyResult) Equals(rhs *ApplyParentClosePolicyResult) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Child == nil && rhs.Child == nil) || (v.Child != nil && rhs.Child != nil && v.Child.Equals(rhs.Child))) { return false } if !_CrossClusterTaskFailedCause_EqualsPtr(v.FailedCause, rhs.FailedCause) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ApplyParentClosePolicyResult. func (v *ApplyParentClosePolicyResult) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Child != nil { err = multierr.Append(err, enc.AddObject("child", v.Child)) } if v.FailedCause != nil { err = multierr.Append(err, enc.AddObject("failedCause", *v.FailedCause)) } return err } // GetChild returns the value of Child if it is set or its // zero value if it is unset. func (v *ApplyParentClosePolicyResult) GetChild() (o *ApplyParentClosePolicyAttributes) { if v != nil && v.Child != nil { return v.Child } return } // IsSetChild returns true if Child is not nil. func (v *ApplyParentClosePolicyResult) IsSetChild() bool { return v != nil && v.Child != nil } // GetFailedCause returns the value of FailedCause if it is set or its // zero value if it is unset. func (v *ApplyParentClosePolicyResult) GetFailedCause() (o CrossClusterTaskFailedCause) { if v != nil && v.FailedCause != nil { return *v.FailedCause } return } // IsSetFailedCause returns true if FailedCause is not nil. func (v *ApplyParentClosePolicyResult) IsSetFailedCause() bool { return v != nil && v.FailedCause != nil } type ApplyParentClosePolicyStatus struct { Completed *bool `json:"completed,omitempty"` FailedCause *CrossClusterTaskFailedCause `json:"failedCause,omitempty"` } // ToWire translates a ApplyParentClosePolicyStatus struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ApplyParentClosePolicyStatus) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Completed != nil { w, err = wire.NewValueBool(*(v.Completed)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.FailedCause != nil { w, err = v.FailedCause.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ApplyParentClosePolicyStatus struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ApplyParentClosePolicyStatus struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ApplyParentClosePolicyStatus // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ApplyParentClosePolicyStatus) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.Completed = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x CrossClusterTaskFailedCause x, err = _CrossClusterTaskFailedCause_Read(field.Value) v.FailedCause = &x if err != nil { return err } } } } return nil } // Encode serializes a ApplyParentClosePolicyStatus struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ApplyParentClosePolicyStatus struct could not be encoded. func (v *ApplyParentClosePolicyStatus) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Completed != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.Completed)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailedCause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := v.FailedCause.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ApplyParentClosePolicyStatus struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ApplyParentClosePolicyStatus struct could not be generated from the wire // representation. func (v *ApplyParentClosePolicyStatus) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.Completed = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x CrossClusterTaskFailedCause x, err = _CrossClusterTaskFailedCause_Decode(sr) v.FailedCause = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ApplyParentClosePolicyStatus // struct. func (v *ApplyParentClosePolicyStatus) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Completed != nil { fields[i] = fmt.Sprintf("Completed: %v", *(v.Completed)) i++ } if v.FailedCause != nil { fields[i] = fmt.Sprintf("FailedCause: %v", *(v.FailedCause)) i++ } return fmt.Sprintf("ApplyParentClosePolicyStatus{%v}", strings.Join(fields[:i], ", ")) } func _Bool_EqualsPtr(lhs, rhs *bool) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ApplyParentClosePolicyStatus match the // provided ApplyParentClosePolicyStatus. // // This function performs a deep comparison. func (v *ApplyParentClosePolicyStatus) Equals(rhs *ApplyParentClosePolicyStatus) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Bool_EqualsPtr(v.Completed, rhs.Completed) { return false } if !_CrossClusterTaskFailedCause_EqualsPtr(v.FailedCause, rhs.FailedCause) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ApplyParentClosePolicyStatus. func (v *ApplyParentClosePolicyStatus) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Completed != nil { enc.AddBool("completed", *v.Completed) } if v.FailedCause != nil { err = multierr.Append(err, enc.AddObject("failedCause", *v.FailedCause)) } return err } // GetCompleted returns the value of Completed if it is set or its // zero value if it is unset. func (v *ApplyParentClosePolicyStatus) GetCompleted() (o bool) { if v != nil && v.Completed != nil { return *v.Completed } return } // IsSetCompleted returns true if Completed is not nil. func (v *ApplyParentClosePolicyStatus) IsSetCompleted() bool { return v != nil && v.Completed != nil } // GetFailedCause returns the value of FailedCause if it is set or its // zero value if it is unset. func (v *ApplyParentClosePolicyStatus) GetFailedCause() (o CrossClusterTaskFailedCause) { if v != nil && v.FailedCause != nil { return *v.FailedCause } return } // IsSetFailedCause returns true if FailedCause is not nil. func (v *ApplyParentClosePolicyStatus) IsSetFailedCause() bool { return v != nil && v.FailedCause != nil } type ArchivalStatus int32 const ( ArchivalStatusDisabled ArchivalStatus = 0 ArchivalStatusEnabled ArchivalStatus = 1 ) // ArchivalStatus_Values returns all recognized values of ArchivalStatus. func ArchivalStatus_Values() []ArchivalStatus { return []ArchivalStatus{ ArchivalStatusDisabled, ArchivalStatusEnabled, } } // UnmarshalText tries to decode ArchivalStatus from a byte slice // containing its name. // // var v ArchivalStatus // err := v.UnmarshalText([]byte("DISABLED")) func (v *ArchivalStatus) UnmarshalText(value []byte) error { switch s := string(value); s { case "DISABLED": *v = ArchivalStatusDisabled return nil case "ENABLED": *v = ArchivalStatusEnabled return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ArchivalStatus", err) } *v = ArchivalStatus(val) return nil } } // MarshalText encodes ArchivalStatus to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v ArchivalStatus) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("DISABLED"), nil case 1: return []byte("ENABLED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ArchivalStatus. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v ArchivalStatus) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "DISABLED") case 1: enc.AddString("name", "ENABLED") } return nil } // Ptr returns a pointer to this enum value. func (v ArchivalStatus) Ptr() *ArchivalStatus { return &v } // Encode encodes ArchivalStatus directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v ArchivalStatus // return v.Encode(sWriter) func (v ArchivalStatus) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates ArchivalStatus into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v ArchivalStatus) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes ArchivalStatus from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return ArchivalStatus(0), err // } // // var v ArchivalStatus // if err := v.FromWire(x); err != nil { // return ArchivalStatus(0), err // } // return v, nil func (v *ArchivalStatus) FromWire(w wire.Value) error { *v = (ArchivalStatus)(w.GetI32()) return nil } // Decode reads off the encoded ArchivalStatus directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v ArchivalStatus // if err := v.Decode(sReader); err != nil { // return ArchivalStatus(0), err // } // return v, nil func (v *ArchivalStatus) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (ArchivalStatus)(i) return nil } // String returns a readable string representation of ArchivalStatus. func (v ArchivalStatus) String() string { w := int32(v) switch w { case 0: return "DISABLED" case 1: return "ENABLED" } return fmt.Sprintf("ArchivalStatus(%d)", w) } // Equals returns true if this ArchivalStatus value matches the provided // value. func (v ArchivalStatus) Equals(rhs ArchivalStatus) bool { return v == rhs } // MarshalJSON serializes ArchivalStatus into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v ArchivalStatus) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"DISABLED\""), nil case 1: return ([]byte)("\"ENABLED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode ArchivalStatus from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *ArchivalStatus) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "ArchivalStatus") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "ArchivalStatus") } *v = (ArchivalStatus)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "ArchivalStatus") } } type AsyncWorkflowConfiguration struct { Enabled *bool `json:"enabled,omitempty"` PredefinedQueueName *string `json:"predefinedQueueName,omitempty"` QueueType *string `json:"queueType,omitempty"` QueueConfig *DataBlob `json:"queueConfig,omitempty"` } // ToWire translates a AsyncWorkflowConfiguration struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AsyncWorkflowConfiguration) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Enabled != nil { w, err = wire.NewValueBool(*(v.Enabled)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.PredefinedQueueName != nil { w, err = wire.NewValueString(*(v.PredefinedQueueName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.QueueType != nil { w, err = wire.NewValueString(*(v.QueueType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.QueueConfig != nil { w, err = v.QueueConfig.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DataBlob_Read(w wire.Value) (*DataBlob, error) { var v DataBlob err := v.FromWire(w) return &v, err } // FromWire deserializes a AsyncWorkflowConfiguration struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AsyncWorkflowConfiguration struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AsyncWorkflowConfiguration // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AsyncWorkflowConfiguration) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.Enabled = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.PredefinedQueueName = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.QueueType = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.QueueConfig, err = _DataBlob_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a AsyncWorkflowConfiguration struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AsyncWorkflowConfiguration struct could not be encoded. func (v *AsyncWorkflowConfiguration) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Enabled != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.Enabled)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PredefinedQueueName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.PredefinedQueueName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueueType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.QueueType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueueConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.QueueConfig.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DataBlob_Decode(sr stream.Reader) (*DataBlob, error) { var v DataBlob err := v.Decode(sr) return &v, err } // Decode deserializes a AsyncWorkflowConfiguration struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AsyncWorkflowConfiguration struct could not be generated from the wire // representation. func (v *AsyncWorkflowConfiguration) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.Enabled = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.PredefinedQueueName = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.QueueType = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.QueueConfig, err = _DataBlob_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AsyncWorkflowConfiguration // struct. func (v *AsyncWorkflowConfiguration) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Enabled != nil { fields[i] = fmt.Sprintf("Enabled: %v", *(v.Enabled)) i++ } if v.PredefinedQueueName != nil { fields[i] = fmt.Sprintf("PredefinedQueueName: %v", *(v.PredefinedQueueName)) i++ } if v.QueueType != nil { fields[i] = fmt.Sprintf("QueueType: %v", *(v.QueueType)) i++ } if v.QueueConfig != nil { fields[i] = fmt.Sprintf("QueueConfig: %v", v.QueueConfig) i++ } return fmt.Sprintf("AsyncWorkflowConfiguration{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AsyncWorkflowConfiguration match the // provided AsyncWorkflowConfiguration. // // This function performs a deep comparison. func (v *AsyncWorkflowConfiguration) Equals(rhs *AsyncWorkflowConfiguration) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Bool_EqualsPtr(v.Enabled, rhs.Enabled) { return false } if !_String_EqualsPtr(v.PredefinedQueueName, rhs.PredefinedQueueName) { return false } if !_String_EqualsPtr(v.QueueType, rhs.QueueType) { return false } if !((v.QueueConfig == nil && rhs.QueueConfig == nil) || (v.QueueConfig != nil && rhs.QueueConfig != nil && v.QueueConfig.Equals(rhs.QueueConfig))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AsyncWorkflowConfiguration. func (v *AsyncWorkflowConfiguration) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Enabled != nil { enc.AddBool("enabled", *v.Enabled) } if v.PredefinedQueueName != nil { enc.AddString("predefinedQueueName", *v.PredefinedQueueName) } if v.QueueType != nil { enc.AddString("queueType", *v.QueueType) } if v.QueueConfig != nil { err = multierr.Append(err, enc.AddObject("queueConfig", v.QueueConfig)) } return err } // GetEnabled returns the value of Enabled if it is set or its // zero value if it is unset. func (v *AsyncWorkflowConfiguration) GetEnabled() (o bool) { if v != nil && v.Enabled != nil { return *v.Enabled } return } // IsSetEnabled returns true if Enabled is not nil. func (v *AsyncWorkflowConfiguration) IsSetEnabled() bool { return v != nil && v.Enabled != nil } // GetPredefinedQueueName returns the value of PredefinedQueueName if it is set or its // zero value if it is unset. func (v *AsyncWorkflowConfiguration) GetPredefinedQueueName() (o string) { if v != nil && v.PredefinedQueueName != nil { return *v.PredefinedQueueName } return } // IsSetPredefinedQueueName returns true if PredefinedQueueName is not nil. func (v *AsyncWorkflowConfiguration) IsSetPredefinedQueueName() bool { return v != nil && v.PredefinedQueueName != nil } // GetQueueType returns the value of QueueType if it is set or its // zero value if it is unset. func (v *AsyncWorkflowConfiguration) GetQueueType() (o string) { if v != nil && v.QueueType != nil { return *v.QueueType } return } // IsSetQueueType returns true if QueueType is not nil. func (v *AsyncWorkflowConfiguration) IsSetQueueType() bool { return v != nil && v.QueueType != nil } // GetQueueConfig returns the value of QueueConfig if it is set or its // zero value if it is unset. func (v *AsyncWorkflowConfiguration) GetQueueConfig() (o *DataBlob) { if v != nil && v.QueueConfig != nil { return v.QueueConfig } return } // IsSetQueueConfig returns true if QueueConfig is not nil. func (v *AsyncWorkflowConfiguration) IsSetQueueConfig() bool { return v != nil && v.QueueConfig != nil } type AutoConfigHint struct { EnableAutoConfig *bool `json:"enableAutoConfig,omitempty"` PollerWaitTimeInMs *int64 `json:"pollerWaitTimeInMs,omitempty"` } // ToWire translates a AutoConfigHint struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AutoConfigHint) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.EnableAutoConfig != nil { w, err = wire.NewValueBool(*(v.EnableAutoConfig)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.PollerWaitTimeInMs != nil { w, err = wire.NewValueI64(*(v.PollerWaitTimeInMs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a AutoConfigHint struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AutoConfigHint struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AutoConfigHint // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AutoConfigHint) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.EnableAutoConfig = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.PollerWaitTimeInMs = &x if err != nil { return err } } } } return nil } // Encode serializes a AutoConfigHint struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AutoConfigHint struct could not be encoded. func (v *AutoConfigHint) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.EnableAutoConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.EnableAutoConfig)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PollerWaitTimeInMs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.PollerWaitTimeInMs)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a AutoConfigHint struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AutoConfigHint struct could not be generated from the wire // representation. func (v *AutoConfigHint) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.EnableAutoConfig = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.PollerWaitTimeInMs = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AutoConfigHint // struct. func (v *AutoConfigHint) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.EnableAutoConfig != nil { fields[i] = fmt.Sprintf("EnableAutoConfig: %v", *(v.EnableAutoConfig)) i++ } if v.PollerWaitTimeInMs != nil { fields[i] = fmt.Sprintf("PollerWaitTimeInMs: %v", *(v.PollerWaitTimeInMs)) i++ } return fmt.Sprintf("AutoConfigHint{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this AutoConfigHint match the // provided AutoConfigHint. // // This function performs a deep comparison. func (v *AutoConfigHint) Equals(rhs *AutoConfigHint) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Bool_EqualsPtr(v.EnableAutoConfig, rhs.EnableAutoConfig) { return false } if !_I64_EqualsPtr(v.PollerWaitTimeInMs, rhs.PollerWaitTimeInMs) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AutoConfigHint. func (v *AutoConfigHint) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.EnableAutoConfig != nil { enc.AddBool("enableAutoConfig", *v.EnableAutoConfig) } if v.PollerWaitTimeInMs != nil { enc.AddInt64("pollerWaitTimeInMs", *v.PollerWaitTimeInMs) } return err } // GetEnableAutoConfig returns the value of EnableAutoConfig if it is set or its // zero value if it is unset. func (v *AutoConfigHint) GetEnableAutoConfig() (o bool) { if v != nil && v.EnableAutoConfig != nil { return *v.EnableAutoConfig } return } // IsSetEnableAutoConfig returns true if EnableAutoConfig is not nil. func (v *AutoConfigHint) IsSetEnableAutoConfig() bool { return v != nil && v.EnableAutoConfig != nil } // GetPollerWaitTimeInMs returns the value of PollerWaitTimeInMs if it is set or its // zero value if it is unset. func (v *AutoConfigHint) GetPollerWaitTimeInMs() (o int64) { if v != nil && v.PollerWaitTimeInMs != nil { return *v.PollerWaitTimeInMs } return } // IsSetPollerWaitTimeInMs returns true if PollerWaitTimeInMs is not nil. func (v *AutoConfigHint) IsSetPollerWaitTimeInMs() bool { return v != nil && v.PollerWaitTimeInMs != nil } type BadBinaries struct { Binaries map[string]*BadBinaryInfo `json:"binaries,omitempty"` } type _Map_String_BadBinaryInfo_MapItemList map[string]*BadBinaryInfo func (m _Map_String_BadBinaryInfo_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*BadBinaryInfo', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_BadBinaryInfo_MapItemList) Size() int { return len(m) } func (_Map_String_BadBinaryInfo_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_BadBinaryInfo_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_BadBinaryInfo_MapItemList) Close() {} // ToWire translates a BadBinaries struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *BadBinaries) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Binaries != nil { w, err = wire.NewValueMap(_Map_String_BadBinaryInfo_MapItemList(v.Binaries)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _BadBinaryInfo_Read(w wire.Value) (*BadBinaryInfo, error) { var v BadBinaryInfo err := v.FromWire(w) return &v, err } func _Map_String_BadBinaryInfo_Read(m wire.MapItemList) (map[string]*BadBinaryInfo, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*BadBinaryInfo, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _BadBinaryInfo_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a BadBinaries struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a BadBinaries struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v BadBinaries // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *BadBinaries) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.Binaries, err = _Map_String_BadBinaryInfo_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_BadBinaryInfo_Encode(val map[string]*BadBinaryInfo, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*BadBinaryInfo', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a BadBinaries struct directly into bytes, without going // through an intermediary type. // // An error is returned if a BadBinaries struct could not be encoded. func (v *BadBinaries) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Binaries != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_BadBinaryInfo_Encode(v.Binaries, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _BadBinaryInfo_Decode(sr stream.Reader) (*BadBinaryInfo, error) { var v BadBinaryInfo err := v.Decode(sr) return &v, err } func _Map_String_BadBinaryInfo_Decode(sr stream.Reader) (map[string]*BadBinaryInfo, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*BadBinaryInfo, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _BadBinaryInfo_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a BadBinaries struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a BadBinaries struct could not be generated from the wire // representation. func (v *BadBinaries) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.Binaries, err = _Map_String_BadBinaryInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a BadBinaries // struct. func (v *BadBinaries) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Binaries != nil { fields[i] = fmt.Sprintf("Binaries: %v", v.Binaries) i++ } return fmt.Sprintf("BadBinaries{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_BadBinaryInfo_Equals(lhs, rhs map[string]*BadBinaryInfo) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this BadBinaries match the // provided BadBinaries. // // This function performs a deep comparison. func (v *BadBinaries) Equals(rhs *BadBinaries) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Binaries == nil && rhs.Binaries == nil) || (v.Binaries != nil && rhs.Binaries != nil && _Map_String_BadBinaryInfo_Equals(v.Binaries, rhs.Binaries))) { return false } return true } type _Map_String_BadBinaryInfo_Zapper map[string]*BadBinaryInfo // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_BadBinaryInfo_Zapper. func (m _Map_String_BadBinaryInfo_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of BadBinaries. func (v *BadBinaries) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Binaries != nil { err = multierr.Append(err, enc.AddObject("binaries", (_Map_String_BadBinaryInfo_Zapper)(v.Binaries))) } return err } // GetBinaries returns the value of Binaries if it is set or its // zero value if it is unset. func (v *BadBinaries) GetBinaries() (o map[string]*BadBinaryInfo) { if v != nil && v.Binaries != nil { return v.Binaries } return } // IsSetBinaries returns true if Binaries is not nil. func (v *BadBinaries) IsSetBinaries() bool { return v != nil && v.Binaries != nil } type BadBinaryInfo struct { Reason *string `json:"reason,omitempty"` Operator *string `json:"operator,omitempty"` CreatedTimeNano *int64 `json:"createdTimeNano,omitempty"` } // ToWire translates a BadBinaryInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *BadBinaryInfo) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Operator != nil { w, err = wire.NewValueString(*(v.Operator)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.CreatedTimeNano != nil { w, err = wire.NewValueI64(*(v.CreatedTimeNano)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a BadBinaryInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a BadBinaryInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v BadBinaryInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *BadBinaryInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Operator = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CreatedTimeNano = &x if err != nil { return err } } } } return nil } // Encode serializes a BadBinaryInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a BadBinaryInfo struct could not be encoded. func (v *BadBinaryInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Operator != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Operator)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CreatedTimeNano != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CreatedTimeNano)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a BadBinaryInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a BadBinaryInfo struct could not be generated from the wire // representation. func (v *BadBinaryInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Operator = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CreatedTimeNano = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a BadBinaryInfo // struct. func (v *BadBinaryInfo) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Operator != nil { fields[i] = fmt.Sprintf("Operator: %v", *(v.Operator)) i++ } if v.CreatedTimeNano != nil { fields[i] = fmt.Sprintf("CreatedTimeNano: %v", *(v.CreatedTimeNano)) i++ } return fmt.Sprintf("BadBinaryInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this BadBinaryInfo match the // provided BadBinaryInfo. // // This function performs a deep comparison. func (v *BadBinaryInfo) Equals(rhs *BadBinaryInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !_String_EqualsPtr(v.Operator, rhs.Operator) { return false } if !_I64_EqualsPtr(v.CreatedTimeNano, rhs.CreatedTimeNano) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of BadBinaryInfo. func (v *BadBinaryInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Operator != nil { enc.AddString("operator", *v.Operator) } if v.CreatedTimeNano != nil { enc.AddInt64("createdTimeNano", *v.CreatedTimeNano) } return err } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *BadBinaryInfo) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *BadBinaryInfo) IsSetReason() bool { return v != nil && v.Reason != nil } // GetOperator returns the value of Operator if it is set or its // zero value if it is unset. func (v *BadBinaryInfo) GetOperator() (o string) { if v != nil && v.Operator != nil { return *v.Operator } return } // IsSetOperator returns true if Operator is not nil. func (v *BadBinaryInfo) IsSetOperator() bool { return v != nil && v.Operator != nil } // GetCreatedTimeNano returns the value of CreatedTimeNano if it is set or its // zero value if it is unset. func (v *BadBinaryInfo) GetCreatedTimeNano() (o int64) { if v != nil && v.CreatedTimeNano != nil { return *v.CreatedTimeNano } return } // IsSetCreatedTimeNano returns true if CreatedTimeNano is not nil. func (v *BadBinaryInfo) IsSetCreatedTimeNano() bool { return v != nil && v.CreatedTimeNano != nil } type BadRequestError struct { Message string `json:"message,required"` } // ToWire translates a BadRequestError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *BadRequestError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a BadRequestError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a BadRequestError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v BadRequestError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *BadRequestError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of BadRequestError is required") } return nil } // Encode serializes a BadRequestError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a BadRequestError struct could not be encoded. func (v *BadRequestError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a BadRequestError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a BadRequestError struct could not be generated from the wire // representation. func (v *BadRequestError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of BadRequestError is required") } return nil } // String returns a readable string representation of a BadRequestError // struct. func (v *BadRequestError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("BadRequestError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*BadRequestError) ErrorName() string { return "BadRequestError" } // Equals returns true if all the fields of this BadRequestError match the // provided BadRequestError. // // This function performs a deep comparison. func (v *BadRequestError) Equals(rhs *BadRequestError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of BadRequestError. func (v *BadRequestError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *BadRequestError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *BadRequestError) Error() string { return v.String() } type CancelExternalWorkflowExecutionFailedCause int32 const ( CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution CancelExternalWorkflowExecutionFailedCause = 0 CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted CancelExternalWorkflowExecutionFailedCause = 1 ) // CancelExternalWorkflowExecutionFailedCause_Values returns all recognized values of CancelExternalWorkflowExecutionFailedCause. func CancelExternalWorkflowExecutionFailedCause_Values() []CancelExternalWorkflowExecutionFailedCause { return []CancelExternalWorkflowExecutionFailedCause{ CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution, CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted, } } // UnmarshalText tries to decode CancelExternalWorkflowExecutionFailedCause from a byte slice // containing its name. // // var v CancelExternalWorkflowExecutionFailedCause // err := v.UnmarshalText([]byte("UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION")) func (v *CancelExternalWorkflowExecutionFailedCause) UnmarshalText(value []byte) error { switch s := string(value); s { case "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION": *v = CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution return nil case "WORKFLOW_ALREADY_COMPLETED": *v = CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "CancelExternalWorkflowExecutionFailedCause", err) } *v = CancelExternalWorkflowExecutionFailedCause(val) return nil } } // MarshalText encodes CancelExternalWorkflowExecutionFailedCause to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v CancelExternalWorkflowExecutionFailedCause) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION"), nil case 1: return []byte("WORKFLOW_ALREADY_COMPLETED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CancelExternalWorkflowExecutionFailedCause. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v CancelExternalWorkflowExecutionFailedCause) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION") case 1: enc.AddString("name", "WORKFLOW_ALREADY_COMPLETED") } return nil } // Ptr returns a pointer to this enum value. func (v CancelExternalWorkflowExecutionFailedCause) Ptr() *CancelExternalWorkflowExecutionFailedCause { return &v } // Encode encodes CancelExternalWorkflowExecutionFailedCause directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v CancelExternalWorkflowExecutionFailedCause // return v.Encode(sWriter) func (v CancelExternalWorkflowExecutionFailedCause) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates CancelExternalWorkflowExecutionFailedCause into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v CancelExternalWorkflowExecutionFailedCause) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes CancelExternalWorkflowExecutionFailedCause from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return CancelExternalWorkflowExecutionFailedCause(0), err // } // // var v CancelExternalWorkflowExecutionFailedCause // if err := v.FromWire(x); err != nil { // return CancelExternalWorkflowExecutionFailedCause(0), err // } // return v, nil func (v *CancelExternalWorkflowExecutionFailedCause) FromWire(w wire.Value) error { *v = (CancelExternalWorkflowExecutionFailedCause)(w.GetI32()) return nil } // Decode reads off the encoded CancelExternalWorkflowExecutionFailedCause directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v CancelExternalWorkflowExecutionFailedCause // if err := v.Decode(sReader); err != nil { // return CancelExternalWorkflowExecutionFailedCause(0), err // } // return v, nil func (v *CancelExternalWorkflowExecutionFailedCause) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (CancelExternalWorkflowExecutionFailedCause)(i) return nil } // String returns a readable string representation of CancelExternalWorkflowExecutionFailedCause. func (v CancelExternalWorkflowExecutionFailedCause) String() string { w := int32(v) switch w { case 0: return "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION" case 1: return "WORKFLOW_ALREADY_COMPLETED" } return fmt.Sprintf("CancelExternalWorkflowExecutionFailedCause(%d)", w) } // Equals returns true if this CancelExternalWorkflowExecutionFailedCause value matches the provided // value. func (v CancelExternalWorkflowExecutionFailedCause) Equals(rhs CancelExternalWorkflowExecutionFailedCause) bool { return v == rhs } // MarshalJSON serializes CancelExternalWorkflowExecutionFailedCause into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v CancelExternalWorkflowExecutionFailedCause) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION\""), nil case 1: return ([]byte)("\"WORKFLOW_ALREADY_COMPLETED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode CancelExternalWorkflowExecutionFailedCause from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *CancelExternalWorkflowExecutionFailedCause) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "CancelExternalWorkflowExecutionFailedCause") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "CancelExternalWorkflowExecutionFailedCause") } *v = (CancelExternalWorkflowExecutionFailedCause)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "CancelExternalWorkflowExecutionFailedCause") } } type CancelTimerDecisionAttributes struct { TimerId *string `json:"timerId,omitempty"` } // ToWire translates a CancelTimerDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CancelTimerDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.TimerId != nil { w, err = wire.NewValueString(*(v.TimerId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CancelTimerDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CancelTimerDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CancelTimerDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CancelTimerDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TimerId = &x if err != nil { return err } } } } return nil } // Encode serializes a CancelTimerDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CancelTimerDecisionAttributes struct could not be encoded. func (v *CancelTimerDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TimerId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TimerId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CancelTimerDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CancelTimerDecisionAttributes struct could not be generated from the wire // representation. func (v *CancelTimerDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TimerId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CancelTimerDecisionAttributes // struct. func (v *CancelTimerDecisionAttributes) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.TimerId != nil { fields[i] = fmt.Sprintf("TimerId: %v", *(v.TimerId)) i++ } return fmt.Sprintf("CancelTimerDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CancelTimerDecisionAttributes match the // provided CancelTimerDecisionAttributes. // // This function performs a deep comparison. func (v *CancelTimerDecisionAttributes) Equals(rhs *CancelTimerDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TimerId, rhs.TimerId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CancelTimerDecisionAttributes. func (v *CancelTimerDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TimerId != nil { enc.AddString("timerId", *v.TimerId) } return err } // GetTimerId returns the value of TimerId if it is set or its // zero value if it is unset. func (v *CancelTimerDecisionAttributes) GetTimerId() (o string) { if v != nil && v.TimerId != nil { return *v.TimerId } return } // IsSetTimerId returns true if TimerId is not nil. func (v *CancelTimerDecisionAttributes) IsSetTimerId() bool { return v != nil && v.TimerId != nil } type CancelTimerFailedEventAttributes struct { TimerId *string `json:"timerId,omitempty"` Cause *string `json:"cause,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a CancelTimerFailedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CancelTimerFailedEventAttributes) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.TimerId != nil { w, err = wire.NewValueString(*(v.TimerId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Cause != nil { w, err = wire.NewValueString(*(v.Cause)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CancelTimerFailedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CancelTimerFailedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CancelTimerFailedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CancelTimerFailedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TimerId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Cause = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a CancelTimerFailedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CancelTimerFailedEventAttributes struct could not be encoded. func (v *CancelTimerFailedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TimerId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TimerId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Cause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Cause)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CancelTimerFailedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CancelTimerFailedEventAttributes struct could not be generated from the wire // representation. func (v *CancelTimerFailedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TimerId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Cause = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CancelTimerFailedEventAttributes // struct. func (v *CancelTimerFailedEventAttributes) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.TimerId != nil { fields[i] = fmt.Sprintf("TimerId: %v", *(v.TimerId)) i++ } if v.Cause != nil { fields[i] = fmt.Sprintf("Cause: %v", *(v.Cause)) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("CancelTimerFailedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CancelTimerFailedEventAttributes match the // provided CancelTimerFailedEventAttributes. // // This function performs a deep comparison. func (v *CancelTimerFailedEventAttributes) Equals(rhs *CancelTimerFailedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TimerId, rhs.TimerId) { return false } if !_String_EqualsPtr(v.Cause, rhs.Cause) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CancelTimerFailedEventAttributes. func (v *CancelTimerFailedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TimerId != nil { enc.AddString("timerId", *v.TimerId) } if v.Cause != nil { enc.AddString("cause", *v.Cause) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetTimerId returns the value of TimerId if it is set or its // zero value if it is unset. func (v *CancelTimerFailedEventAttributes) GetTimerId() (o string) { if v != nil && v.TimerId != nil { return *v.TimerId } return } // IsSetTimerId returns true if TimerId is not nil. func (v *CancelTimerFailedEventAttributes) IsSetTimerId() bool { return v != nil && v.TimerId != nil } // GetCause returns the value of Cause if it is set or its // zero value if it is unset. func (v *CancelTimerFailedEventAttributes) GetCause() (o string) { if v != nil && v.Cause != nil { return *v.Cause } return } // IsSetCause returns true if Cause is not nil. func (v *CancelTimerFailedEventAttributes) IsSetCause() bool { return v != nil && v.Cause != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *CancelTimerFailedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *CancelTimerFailedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *CancelTimerFailedEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *CancelTimerFailedEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } type CancelWorkflowExecutionDecisionAttributes struct { Details []byte `json:"details,omitempty"` } // ToWire translates a CancelWorkflowExecutionDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CancelWorkflowExecutionDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CancelWorkflowExecutionDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CancelWorkflowExecutionDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CancelWorkflowExecutionDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CancelWorkflowExecutionDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a CancelWorkflowExecutionDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CancelWorkflowExecutionDecisionAttributes struct could not be encoded. func (v *CancelWorkflowExecutionDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CancelWorkflowExecutionDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CancelWorkflowExecutionDecisionAttributes struct could not be generated from the wire // representation. func (v *CancelWorkflowExecutionDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CancelWorkflowExecutionDecisionAttributes // struct. func (v *CancelWorkflowExecutionDecisionAttributes) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } return fmt.Sprintf("CancelWorkflowExecutionDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CancelWorkflowExecutionDecisionAttributes match the // provided CancelWorkflowExecutionDecisionAttributes. // // This function performs a deep comparison. func (v *CancelWorkflowExecutionDecisionAttributes) Equals(rhs *CancelWorkflowExecutionDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CancelWorkflowExecutionDecisionAttributes. func (v *CancelWorkflowExecutionDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } return err } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *CancelWorkflowExecutionDecisionAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *CancelWorkflowExecutionDecisionAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } type CancellationAlreadyRequestedError struct { Message string `json:"message,required"` } // ToWire translates a CancellationAlreadyRequestedError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CancellationAlreadyRequestedError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CancellationAlreadyRequestedError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CancellationAlreadyRequestedError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CancellationAlreadyRequestedError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CancellationAlreadyRequestedError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of CancellationAlreadyRequestedError is required") } return nil } // Encode serializes a CancellationAlreadyRequestedError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CancellationAlreadyRequestedError struct could not be encoded. func (v *CancellationAlreadyRequestedError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a CancellationAlreadyRequestedError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CancellationAlreadyRequestedError struct could not be generated from the wire // representation. func (v *CancellationAlreadyRequestedError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of CancellationAlreadyRequestedError is required") } return nil } // String returns a readable string representation of a CancellationAlreadyRequestedError // struct. func (v *CancellationAlreadyRequestedError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("CancellationAlreadyRequestedError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*CancellationAlreadyRequestedError) ErrorName() string { return "CancellationAlreadyRequestedError" } // Equals returns true if all the fields of this CancellationAlreadyRequestedError match the // provided CancellationAlreadyRequestedError. // // This function performs a deep comparison. func (v *CancellationAlreadyRequestedError) Equals(rhs *CancellationAlreadyRequestedError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CancellationAlreadyRequestedError. func (v *CancellationAlreadyRequestedError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *CancellationAlreadyRequestedError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *CancellationAlreadyRequestedError) Error() string { return v.String() } type ChildWorkflowExecutionCanceledEventAttributes struct { Details []byte `json:"details,omitempty"` Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` } // ToWire translates a ChildWorkflowExecutionCanceledEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ChildWorkflowExecutionCanceledEventAttributes) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WorkflowExecution_Read(w wire.Value) (*WorkflowExecution, error) { var v WorkflowExecution err := v.FromWire(w) return &v, err } func _WorkflowType_Read(w wire.Value) (*WorkflowType, error) { var v WorkflowType err := v.FromWire(w) return &v, err } // FromWire deserializes a ChildWorkflowExecutionCanceledEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ChildWorkflowExecutionCanceledEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ChildWorkflowExecutionCanceledEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ChildWorkflowExecutionCanceledEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a ChildWorkflowExecutionCanceledEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ChildWorkflowExecutionCanceledEventAttributes struct could not be encoded. func (v *ChildWorkflowExecutionCanceledEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _WorkflowExecution_Decode(sr stream.Reader) (*WorkflowExecution, error) { var v WorkflowExecution err := v.Decode(sr) return &v, err } func _WorkflowType_Decode(sr stream.Reader) (*WorkflowType, error) { var v WorkflowType err := v.Decode(sr) return &v, err } // Decode deserializes a ChildWorkflowExecutionCanceledEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ChildWorkflowExecutionCanceledEventAttributes struct could not be generated from the wire // representation. func (v *ChildWorkflowExecutionCanceledEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ChildWorkflowExecutionCanceledEventAttributes // struct. func (v *ChildWorkflowExecutionCanceledEventAttributes) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } return fmt.Sprintf("ChildWorkflowExecutionCanceledEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ChildWorkflowExecutionCanceledEventAttributes match the // provided ChildWorkflowExecutionCanceledEventAttributes. // // This function performs a deep comparison. func (v *ChildWorkflowExecutionCanceledEventAttributes) Equals(rhs *ChildWorkflowExecutionCanceledEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ChildWorkflowExecutionCanceledEventAttributes. func (v *ChildWorkflowExecutionCanceledEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } return err } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCanceledEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *ChildWorkflowExecutionCanceledEventAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCanceledEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ChildWorkflowExecutionCanceledEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCanceledEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ChildWorkflowExecutionCanceledEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCanceledEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *ChildWorkflowExecutionCanceledEventAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCanceledEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *ChildWorkflowExecutionCanceledEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCanceledEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *ChildWorkflowExecutionCanceledEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } type ChildWorkflowExecutionCompletedEventAttributes struct { Result []byte `json:"result,omitempty"` Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` } // ToWire translates a ChildWorkflowExecutionCompletedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ChildWorkflowExecutionCompletedEventAttributes) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Result != nil { w, err = wire.NewValueBinary(v.Result), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ChildWorkflowExecutionCompletedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ChildWorkflowExecutionCompletedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ChildWorkflowExecutionCompletedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ChildWorkflowExecutionCompletedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.Result, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a ChildWorkflowExecutionCompletedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ChildWorkflowExecutionCompletedEventAttributes struct could not be encoded. func (v *ChildWorkflowExecutionCompletedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Result != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Result); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ChildWorkflowExecutionCompletedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ChildWorkflowExecutionCompletedEventAttributes struct could not be generated from the wire // representation. func (v *ChildWorkflowExecutionCompletedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.Result, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ChildWorkflowExecutionCompletedEventAttributes // struct. func (v *ChildWorkflowExecutionCompletedEventAttributes) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Result != nil { fields[i] = fmt.Sprintf("Result: %v", v.Result) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } return fmt.Sprintf("ChildWorkflowExecutionCompletedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ChildWorkflowExecutionCompletedEventAttributes match the // provided ChildWorkflowExecutionCompletedEventAttributes. // // This function performs a deep comparison. func (v *ChildWorkflowExecutionCompletedEventAttributes) Equals(rhs *ChildWorkflowExecutionCompletedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Result == nil && rhs.Result == nil) || (v.Result != nil && rhs.Result != nil && bytes.Equal(v.Result, rhs.Result))) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ChildWorkflowExecutionCompletedEventAttributes. func (v *ChildWorkflowExecutionCompletedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Result != nil { enc.AddString("result", base64.StdEncoding.EncodeToString(v.Result)) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } return err } // GetResult returns the value of Result if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCompletedEventAttributes) GetResult() (o []byte) { if v != nil && v.Result != nil { return v.Result } return } // IsSetResult returns true if Result is not nil. func (v *ChildWorkflowExecutionCompletedEventAttributes) IsSetResult() bool { return v != nil && v.Result != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCompletedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ChildWorkflowExecutionCompletedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCompletedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ChildWorkflowExecutionCompletedEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCompletedEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *ChildWorkflowExecutionCompletedEventAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCompletedEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *ChildWorkflowExecutionCompletedEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionCompletedEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *ChildWorkflowExecutionCompletedEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } type ChildWorkflowExecutionFailedCause int32 const ( ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning ChildWorkflowExecutionFailedCause = 0 ) // ChildWorkflowExecutionFailedCause_Values returns all recognized values of ChildWorkflowExecutionFailedCause. func ChildWorkflowExecutionFailedCause_Values() []ChildWorkflowExecutionFailedCause { return []ChildWorkflowExecutionFailedCause{ ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning, } } // UnmarshalText tries to decode ChildWorkflowExecutionFailedCause from a byte slice // containing its name. // // var v ChildWorkflowExecutionFailedCause // err := v.UnmarshalText([]byte("WORKFLOW_ALREADY_RUNNING")) func (v *ChildWorkflowExecutionFailedCause) UnmarshalText(value []byte) error { switch s := string(value); s { case "WORKFLOW_ALREADY_RUNNING": *v = ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ChildWorkflowExecutionFailedCause", err) } *v = ChildWorkflowExecutionFailedCause(val) return nil } } // MarshalText encodes ChildWorkflowExecutionFailedCause to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v ChildWorkflowExecutionFailedCause) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("WORKFLOW_ALREADY_RUNNING"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ChildWorkflowExecutionFailedCause. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v ChildWorkflowExecutionFailedCause) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "WORKFLOW_ALREADY_RUNNING") } return nil } // Ptr returns a pointer to this enum value. func (v ChildWorkflowExecutionFailedCause) Ptr() *ChildWorkflowExecutionFailedCause { return &v } // Encode encodes ChildWorkflowExecutionFailedCause directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v ChildWorkflowExecutionFailedCause // return v.Encode(sWriter) func (v ChildWorkflowExecutionFailedCause) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates ChildWorkflowExecutionFailedCause into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v ChildWorkflowExecutionFailedCause) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes ChildWorkflowExecutionFailedCause from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return ChildWorkflowExecutionFailedCause(0), err // } // // var v ChildWorkflowExecutionFailedCause // if err := v.FromWire(x); err != nil { // return ChildWorkflowExecutionFailedCause(0), err // } // return v, nil func (v *ChildWorkflowExecutionFailedCause) FromWire(w wire.Value) error { *v = (ChildWorkflowExecutionFailedCause)(w.GetI32()) return nil } // Decode reads off the encoded ChildWorkflowExecutionFailedCause directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v ChildWorkflowExecutionFailedCause // if err := v.Decode(sReader); err != nil { // return ChildWorkflowExecutionFailedCause(0), err // } // return v, nil func (v *ChildWorkflowExecutionFailedCause) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (ChildWorkflowExecutionFailedCause)(i) return nil } // String returns a readable string representation of ChildWorkflowExecutionFailedCause. func (v ChildWorkflowExecutionFailedCause) String() string { w := int32(v) switch w { case 0: return "WORKFLOW_ALREADY_RUNNING" } return fmt.Sprintf("ChildWorkflowExecutionFailedCause(%d)", w) } // Equals returns true if this ChildWorkflowExecutionFailedCause value matches the provided // value. func (v ChildWorkflowExecutionFailedCause) Equals(rhs ChildWorkflowExecutionFailedCause) bool { return v == rhs } // MarshalJSON serializes ChildWorkflowExecutionFailedCause into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v ChildWorkflowExecutionFailedCause) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"WORKFLOW_ALREADY_RUNNING\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode ChildWorkflowExecutionFailedCause from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *ChildWorkflowExecutionFailedCause) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "ChildWorkflowExecutionFailedCause") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "ChildWorkflowExecutionFailedCause") } *v = (ChildWorkflowExecutionFailedCause)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "ChildWorkflowExecutionFailedCause") } } type ChildWorkflowExecutionFailedEventAttributes struct { Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` } // ToWire translates a ChildWorkflowExecutionFailedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ChildWorkflowExecutionFailedEventAttributes) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ChildWorkflowExecutionFailedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ChildWorkflowExecutionFailedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ChildWorkflowExecutionFailedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ChildWorkflowExecutionFailedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a ChildWorkflowExecutionFailedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ChildWorkflowExecutionFailedEventAttributes struct could not be encoded. func (v *ChildWorkflowExecutionFailedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ChildWorkflowExecutionFailedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ChildWorkflowExecutionFailedEventAttributes struct could not be generated from the wire // representation. func (v *ChildWorkflowExecutionFailedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ChildWorkflowExecutionFailedEventAttributes // struct. func (v *ChildWorkflowExecutionFailedEventAttributes) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } return fmt.Sprintf("ChildWorkflowExecutionFailedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ChildWorkflowExecutionFailedEventAttributes match the // provided ChildWorkflowExecutionFailedEventAttributes. // // This function performs a deep comparison. func (v *ChildWorkflowExecutionFailedEventAttributes) Equals(rhs *ChildWorkflowExecutionFailedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ChildWorkflowExecutionFailedEventAttributes. func (v *ChildWorkflowExecutionFailedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } return err } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionFailedEventAttributes) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *ChildWorkflowExecutionFailedEventAttributes) IsSetReason() bool { return v != nil && v.Reason != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionFailedEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *ChildWorkflowExecutionFailedEventAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionFailedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ChildWorkflowExecutionFailedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionFailedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ChildWorkflowExecutionFailedEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionFailedEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *ChildWorkflowExecutionFailedEventAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionFailedEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *ChildWorkflowExecutionFailedEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionFailedEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *ChildWorkflowExecutionFailedEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } type ChildWorkflowExecutionStartedEventAttributes struct { Domain *string `json:"domain,omitempty"` InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` Header *Header `json:"header,omitempty"` } // ToWire translates a ChildWorkflowExecutionStartedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ChildWorkflowExecutionStartedEventAttributes) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ChildWorkflowExecutionStartedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ChildWorkflowExecutionStartedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ChildWorkflowExecutionStartedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ChildWorkflowExecutionStartedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ChildWorkflowExecutionStartedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ChildWorkflowExecutionStartedEventAttributes struct could not be encoded. func (v *ChildWorkflowExecutionStartedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ChildWorkflowExecutionStartedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ChildWorkflowExecutionStartedEventAttributes struct could not be generated from the wire // representation. func (v *ChildWorkflowExecutionStartedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ChildWorkflowExecutionStartedEventAttributes // struct. func (v *ChildWorkflowExecutionStartedEventAttributes) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } return fmt.Sprintf("ChildWorkflowExecutionStartedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ChildWorkflowExecutionStartedEventAttributes match the // provided ChildWorkflowExecutionStartedEventAttributes. // // This function performs a deep comparison. func (v *ChildWorkflowExecutionStartedEventAttributes) Equals(rhs *ChildWorkflowExecutionStartedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ChildWorkflowExecutionStartedEventAttributes. func (v *ChildWorkflowExecutionStartedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionStartedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ChildWorkflowExecutionStartedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionStartedEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *ChildWorkflowExecutionStartedEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionStartedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ChildWorkflowExecutionStartedEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionStartedEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *ChildWorkflowExecutionStartedEventAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionStartedEventAttributes) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *ChildWorkflowExecutionStartedEventAttributes) IsSetHeader() bool { return v != nil && v.Header != nil } type ChildWorkflowExecutionTerminatedEventAttributes struct { Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` } // ToWire translates a ChildWorkflowExecutionTerminatedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ChildWorkflowExecutionTerminatedEventAttributes) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ChildWorkflowExecutionTerminatedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ChildWorkflowExecutionTerminatedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ChildWorkflowExecutionTerminatedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ChildWorkflowExecutionTerminatedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a ChildWorkflowExecutionTerminatedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ChildWorkflowExecutionTerminatedEventAttributes struct could not be encoded. func (v *ChildWorkflowExecutionTerminatedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ChildWorkflowExecutionTerminatedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ChildWorkflowExecutionTerminatedEventAttributes struct could not be generated from the wire // representation. func (v *ChildWorkflowExecutionTerminatedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ChildWorkflowExecutionTerminatedEventAttributes // struct. func (v *ChildWorkflowExecutionTerminatedEventAttributes) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } return fmt.Sprintf("ChildWorkflowExecutionTerminatedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ChildWorkflowExecutionTerminatedEventAttributes match the // provided ChildWorkflowExecutionTerminatedEventAttributes. // // This function performs a deep comparison. func (v *ChildWorkflowExecutionTerminatedEventAttributes) Equals(rhs *ChildWorkflowExecutionTerminatedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ChildWorkflowExecutionTerminatedEventAttributes. func (v *ChildWorkflowExecutionTerminatedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTerminatedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ChildWorkflowExecutionTerminatedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTerminatedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ChildWorkflowExecutionTerminatedEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTerminatedEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *ChildWorkflowExecutionTerminatedEventAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTerminatedEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *ChildWorkflowExecutionTerminatedEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTerminatedEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *ChildWorkflowExecutionTerminatedEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } type ChildWorkflowExecutionTimedOutEventAttributes struct { TimeoutType *TimeoutType `json:"timeoutType,omitempty"` Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` } // ToWire translates a ChildWorkflowExecutionTimedOutEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ChildWorkflowExecutionTimedOutEventAttributes) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.TimeoutType != nil { w, err = v.TimeoutType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ChildWorkflowExecutionTimedOutEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ChildWorkflowExecutionTimedOutEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ChildWorkflowExecutionTimedOutEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ChildWorkflowExecutionTimedOutEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x TimeoutType x, err = _TimeoutType_Read(field.Value) v.TimeoutType = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a ChildWorkflowExecutionTimedOutEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ChildWorkflowExecutionTimedOutEventAttributes struct could not be encoded. func (v *ChildWorkflowExecutionTimedOutEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TimeoutType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.TimeoutType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ChildWorkflowExecutionTimedOutEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ChildWorkflowExecutionTimedOutEventAttributes struct could not be generated from the wire // representation. func (v *ChildWorkflowExecutionTimedOutEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x TimeoutType x, err = _TimeoutType_Decode(sr) v.TimeoutType = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ChildWorkflowExecutionTimedOutEventAttributes // struct. func (v *ChildWorkflowExecutionTimedOutEventAttributes) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.TimeoutType != nil { fields[i] = fmt.Sprintf("TimeoutType: %v", *(v.TimeoutType)) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } return fmt.Sprintf("ChildWorkflowExecutionTimedOutEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ChildWorkflowExecutionTimedOutEventAttributes match the // provided ChildWorkflowExecutionTimedOutEventAttributes. // // This function performs a deep comparison. func (v *ChildWorkflowExecutionTimedOutEventAttributes) Equals(rhs *ChildWorkflowExecutionTimedOutEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_TimeoutType_EqualsPtr(v.TimeoutType, rhs.TimeoutType) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ChildWorkflowExecutionTimedOutEventAttributes. func (v *ChildWorkflowExecutionTimedOutEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TimeoutType != nil { err = multierr.Append(err, enc.AddObject("timeoutType", *v.TimeoutType)) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } return err } // GetTimeoutType returns the value of TimeoutType if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTimedOutEventAttributes) GetTimeoutType() (o TimeoutType) { if v != nil && v.TimeoutType != nil { return *v.TimeoutType } return } // IsSetTimeoutType returns true if TimeoutType is not nil. func (v *ChildWorkflowExecutionTimedOutEventAttributes) IsSetTimeoutType() bool { return v != nil && v.TimeoutType != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTimedOutEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ChildWorkflowExecutionTimedOutEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTimedOutEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ChildWorkflowExecutionTimedOutEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTimedOutEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *ChildWorkflowExecutionTimedOutEventAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTimedOutEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *ChildWorkflowExecutionTimedOutEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *ChildWorkflowExecutionTimedOutEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *ChildWorkflowExecutionTimedOutEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } type ClientVersionNotSupportedError struct { FeatureVersion string `json:"featureVersion,required"` ClientImpl string `json:"clientImpl,required"` SupportedVersions string `json:"supportedVersions,required"` } // ToWire translates a ClientVersionNotSupportedError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ClientVersionNotSupportedError) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.FeatureVersion), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ w, err = wire.NewValueString(v.ClientImpl), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ w, err = wire.NewValueString(v.SupportedVersions), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ClientVersionNotSupportedError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ClientVersionNotSupportedError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ClientVersionNotSupportedError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ClientVersionNotSupportedError) FromWire(w wire.Value) error { var err error featureVersionIsSet := false clientImplIsSet := false supportedVersionsIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.FeatureVersion, err = field.Value.GetString(), error(nil) if err != nil { return err } featureVersionIsSet = true } case 2: if field.Value.Type() == wire.TBinary { v.ClientImpl, err = field.Value.GetString(), error(nil) if err != nil { return err } clientImplIsSet = true } case 3: if field.Value.Type() == wire.TBinary { v.SupportedVersions, err = field.Value.GetString(), error(nil) if err != nil { return err } supportedVersionsIsSet = true } } } if !featureVersionIsSet { return errors.New("field FeatureVersion of ClientVersionNotSupportedError is required") } if !clientImplIsSet { return errors.New("field ClientImpl of ClientVersionNotSupportedError is required") } if !supportedVersionsIsSet { return errors.New("field SupportedVersions of ClientVersionNotSupportedError is required") } return nil } // Encode serializes a ClientVersionNotSupportedError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ClientVersionNotSupportedError struct could not be encoded. func (v *ClientVersionNotSupportedError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.FeatureVersion); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.ClientImpl); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.SupportedVersions); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a ClientVersionNotSupportedError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ClientVersionNotSupportedError struct could not be generated from the wire // representation. func (v *ClientVersionNotSupportedError) Decode(sr stream.Reader) error { featureVersionIsSet := false clientImplIsSet := false supportedVersionsIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.FeatureVersion, err = sr.ReadString() if err != nil { return err } featureVersionIsSet = true case fh.ID == 2 && fh.Type == wire.TBinary: v.ClientImpl, err = sr.ReadString() if err != nil { return err } clientImplIsSet = true case fh.ID == 3 && fh.Type == wire.TBinary: v.SupportedVersions, err = sr.ReadString() if err != nil { return err } supportedVersionsIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !featureVersionIsSet { return errors.New("field FeatureVersion of ClientVersionNotSupportedError is required") } if !clientImplIsSet { return errors.New("field ClientImpl of ClientVersionNotSupportedError is required") } if !supportedVersionsIsSet { return errors.New("field SupportedVersions of ClientVersionNotSupportedError is required") } return nil } // String returns a readable string representation of a ClientVersionNotSupportedError // struct. func (v *ClientVersionNotSupportedError) String() string { if v == nil { return "" } var fields [3]string i := 0 fields[i] = fmt.Sprintf("FeatureVersion: %v", v.FeatureVersion) i++ fields[i] = fmt.Sprintf("ClientImpl: %v", v.ClientImpl) i++ fields[i] = fmt.Sprintf("SupportedVersions: %v", v.SupportedVersions) i++ return fmt.Sprintf("ClientVersionNotSupportedError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*ClientVersionNotSupportedError) ErrorName() string { return "ClientVersionNotSupportedError" } // Equals returns true if all the fields of this ClientVersionNotSupportedError match the // provided ClientVersionNotSupportedError. // // This function performs a deep comparison. func (v *ClientVersionNotSupportedError) Equals(rhs *ClientVersionNotSupportedError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.FeatureVersion == rhs.FeatureVersion) { return false } if !(v.ClientImpl == rhs.ClientImpl) { return false } if !(v.SupportedVersions == rhs.SupportedVersions) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ClientVersionNotSupportedError. func (v *ClientVersionNotSupportedError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("featureVersion", v.FeatureVersion) enc.AddString("clientImpl", v.ClientImpl) enc.AddString("supportedVersions", v.SupportedVersions) return err } // GetFeatureVersion returns the value of FeatureVersion if it is set or its // zero value if it is unset. func (v *ClientVersionNotSupportedError) GetFeatureVersion() (o string) { if v != nil { o = v.FeatureVersion } return } // GetClientImpl returns the value of ClientImpl if it is set or its // zero value if it is unset. func (v *ClientVersionNotSupportedError) GetClientImpl() (o string) { if v != nil { o = v.ClientImpl } return } // GetSupportedVersions returns the value of SupportedVersions if it is set or its // zero value if it is unset. func (v *ClientVersionNotSupportedError) GetSupportedVersions() (o string) { if v != nil { o = v.SupportedVersions } return } func (v *ClientVersionNotSupportedError) Error() string { return v.String() } type CloseShardRequest struct { ShardID *int32 `json:"shardID,omitempty"` } // ToWire translates a CloseShardRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CloseShardRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ShardID != nil { w, err = wire.NewValueI32(*(v.ShardID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CloseShardRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CloseShardRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CloseShardRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CloseShardRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShardID = &x if err != nil { return err } } } } return nil } // Encode serializes a CloseShardRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CloseShardRequest struct could not be encoded. func (v *CloseShardRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ShardID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShardID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CloseShardRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CloseShardRequest struct could not be generated from the wire // representation. func (v *CloseShardRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShardID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CloseShardRequest // struct. func (v *CloseShardRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ShardID != nil { fields[i] = fmt.Sprintf("ShardID: %v", *(v.ShardID)) i++ } return fmt.Sprintf("CloseShardRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CloseShardRequest match the // provided CloseShardRequest. // // This function performs a deep comparison. func (v *CloseShardRequest) Equals(rhs *CloseShardRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.ShardID, rhs.ShardID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CloseShardRequest. func (v *CloseShardRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ShardID != nil { enc.AddInt32("shardID", *v.ShardID) } return err } // GetShardID returns the value of ShardID if it is set or its // zero value if it is unset. func (v *CloseShardRequest) GetShardID() (o int32) { if v != nil && v.ShardID != nil { return *v.ShardID } return } // IsSetShardID returns true if ShardID is not nil. func (v *CloseShardRequest) IsSetShardID() bool { return v != nil && v.ShardID != nil } type ClusterAttribute struct { Scope *string `json:"scope,omitempty"` Name *string `json:"name,omitempty"` } // ToWire translates a ClusterAttribute struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ClusterAttribute) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Scope != nil { w, err = wire.NewValueString(*(v.Scope)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ClusterAttribute struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ClusterAttribute struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ClusterAttribute // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ClusterAttribute) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Scope = &x if err != nil { return err } } case 2: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } } } return nil } // Encode serializes a ClusterAttribute struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ClusterAttribute struct could not be encoded. func (v *ClusterAttribute) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Scope != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Scope)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ClusterAttribute struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ClusterAttribute struct could not be generated from the wire // representation. func (v *ClusterAttribute) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Scope = &x if err != nil { return err } case fh.ID == 2 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ClusterAttribute // struct. func (v *ClusterAttribute) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Scope != nil { fields[i] = fmt.Sprintf("Scope: %v", *(v.Scope)) i++ } if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } return fmt.Sprintf("ClusterAttribute{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ClusterAttribute match the // provided ClusterAttribute. // // This function performs a deep comparison. func (v *ClusterAttribute) Equals(rhs *ClusterAttribute) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Scope, rhs.Scope) { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ClusterAttribute. func (v *ClusterAttribute) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Scope != nil { enc.AddString("scope", *v.Scope) } if v.Name != nil { enc.AddString("name", *v.Name) } return err } // GetScope returns the value of Scope if it is set or its // zero value if it is unset. func (v *ClusterAttribute) GetScope() (o string) { if v != nil && v.Scope != nil { return *v.Scope } return } // IsSetScope returns true if Scope is not nil. func (v *ClusterAttribute) IsSetScope() bool { return v != nil && v.Scope != nil } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *ClusterAttribute) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *ClusterAttribute) IsSetName() bool { return v != nil && v.Name != nil } type ClusterAttributeScope struct { ClusterAttributes map[string]*ActiveClusterInfo `json:"clusterAttributes,omitempty"` } // ToWire translates a ClusterAttributeScope struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ClusterAttributeScope) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ClusterAttributes != nil { w, err = wire.NewValueMap(_Map_String_ActiveClusterInfo_MapItemList(v.ClusterAttributes)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ClusterAttributeScope struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ClusterAttributeScope struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ClusterAttributeScope // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ClusterAttributeScope) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.ClusterAttributes, err = _Map_String_ActiveClusterInfo_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } // Encode serializes a ClusterAttributeScope struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ClusterAttributeScope struct could not be encoded. func (v *ClusterAttributeScope) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ClusterAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_ActiveClusterInfo_Encode(v.ClusterAttributes, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ClusterAttributeScope struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ClusterAttributeScope struct could not be generated from the wire // representation. func (v *ClusterAttributeScope) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.ClusterAttributes, err = _Map_String_ActiveClusterInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ClusterAttributeScope // struct. func (v *ClusterAttributeScope) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ClusterAttributes != nil { fields[i] = fmt.Sprintf("ClusterAttributes: %v", v.ClusterAttributes) i++ } return fmt.Sprintf("ClusterAttributeScope{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ClusterAttributeScope match the // provided ClusterAttributeScope. // // This function performs a deep comparison. func (v *ClusterAttributeScope) Equals(rhs *ClusterAttributeScope) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ClusterAttributes == nil && rhs.ClusterAttributes == nil) || (v.ClusterAttributes != nil && rhs.ClusterAttributes != nil && _Map_String_ActiveClusterInfo_Equals(v.ClusterAttributes, rhs.ClusterAttributes))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ClusterAttributeScope. func (v *ClusterAttributeScope) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ClusterAttributes != nil { err = multierr.Append(err, enc.AddObject("clusterAttributes", (_Map_String_ActiveClusterInfo_Zapper)(v.ClusterAttributes))) } return err } // GetClusterAttributes returns the value of ClusterAttributes if it is set or its // zero value if it is unset. func (v *ClusterAttributeScope) GetClusterAttributes() (o map[string]*ActiveClusterInfo) { if v != nil && v.ClusterAttributes != nil { return v.ClusterAttributes } return } // IsSetClusterAttributes returns true if ClusterAttributes is not nil. func (v *ClusterAttributeScope) IsSetClusterAttributes() bool { return v != nil && v.ClusterAttributes != nil } type ClusterFailover struct { FromCluster *ActiveClusterInfo `json:"fromCluster,omitempty"` ToCluster *ActiveClusterInfo `json:"toCluster,omitempty"` ClusterAttribute *ClusterAttribute `json:"clusterAttribute,omitempty"` } // ToWire translates a ClusterFailover struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ClusterFailover) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.FromCluster != nil { w, err = v.FromCluster.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ToCluster != nil { w, err = v.ToCluster.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ClusterAttribute != nil { w, err = v.ClusterAttribute.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ClusterFailover struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ClusterFailover struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ClusterFailover // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ClusterFailover) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.FromCluster, err = _ActiveClusterInfo_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.ToCluster, err = _ActiveClusterInfo_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ClusterAttribute, err = _ClusterAttribute_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ClusterFailover struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ClusterFailover struct could not be encoded. func (v *ClusterFailover) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FromCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.FromCluster.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ToCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.ToCluster.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClusterAttribute != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ClusterAttribute.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ClusterFailover struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ClusterFailover struct could not be generated from the wire // representation. func (v *ClusterFailover) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.FromCluster, err = _ActiveClusterInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.ToCluster, err = _ActiveClusterInfo_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ClusterAttribute, err = _ClusterAttribute_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ClusterFailover // struct. func (v *ClusterFailover) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.FromCluster != nil { fields[i] = fmt.Sprintf("FromCluster: %v", v.FromCluster) i++ } if v.ToCluster != nil { fields[i] = fmt.Sprintf("ToCluster: %v", v.ToCluster) i++ } if v.ClusterAttribute != nil { fields[i] = fmt.Sprintf("ClusterAttribute: %v", v.ClusterAttribute) i++ } return fmt.Sprintf("ClusterFailover{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ClusterFailover match the // provided ClusterFailover. // // This function performs a deep comparison. func (v *ClusterFailover) Equals(rhs *ClusterFailover) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.FromCluster == nil && rhs.FromCluster == nil) || (v.FromCluster != nil && rhs.FromCluster != nil && v.FromCluster.Equals(rhs.FromCluster))) { return false } if !((v.ToCluster == nil && rhs.ToCluster == nil) || (v.ToCluster != nil && rhs.ToCluster != nil && v.ToCluster.Equals(rhs.ToCluster))) { return false } if !((v.ClusterAttribute == nil && rhs.ClusterAttribute == nil) || (v.ClusterAttribute != nil && rhs.ClusterAttribute != nil && v.ClusterAttribute.Equals(rhs.ClusterAttribute))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ClusterFailover. func (v *ClusterFailover) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FromCluster != nil { err = multierr.Append(err, enc.AddObject("fromCluster", v.FromCluster)) } if v.ToCluster != nil { err = multierr.Append(err, enc.AddObject("toCluster", v.ToCluster)) } if v.ClusterAttribute != nil { err = multierr.Append(err, enc.AddObject("clusterAttribute", v.ClusterAttribute)) } return err } // GetFromCluster returns the value of FromCluster if it is set or its // zero value if it is unset. func (v *ClusterFailover) GetFromCluster() (o *ActiveClusterInfo) { if v != nil && v.FromCluster != nil { return v.FromCluster } return } // IsSetFromCluster returns true if FromCluster is not nil. func (v *ClusterFailover) IsSetFromCluster() bool { return v != nil && v.FromCluster != nil } // GetToCluster returns the value of ToCluster if it is set or its // zero value if it is unset. func (v *ClusterFailover) GetToCluster() (o *ActiveClusterInfo) { if v != nil && v.ToCluster != nil { return v.ToCluster } return } // IsSetToCluster returns true if ToCluster is not nil. func (v *ClusterFailover) IsSetToCluster() bool { return v != nil && v.ToCluster != nil } // GetClusterAttribute returns the value of ClusterAttribute if it is set or its // zero value if it is unset. func (v *ClusterFailover) GetClusterAttribute() (o *ClusterAttribute) { if v != nil && v.ClusterAttribute != nil { return v.ClusterAttribute } return } // IsSetClusterAttribute returns true if ClusterAttribute is not nil. func (v *ClusterFailover) IsSetClusterAttribute() bool { return v != nil && v.ClusterAttribute != nil } type ClusterInfo struct { SupportedClientVersions *SupportedClientVersions `json:"supportedClientVersions,omitempty"` } // ToWire translates a ClusterInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ClusterInfo) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.SupportedClientVersions != nil { w, err = v.SupportedClientVersions.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SupportedClientVersions_Read(w wire.Value) (*SupportedClientVersions, error) { var v SupportedClientVersions err := v.FromWire(w) return &v, err } // FromWire deserializes a ClusterInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ClusterInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ClusterInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ClusterInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.SupportedClientVersions, err = _SupportedClientVersions_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ClusterInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ClusterInfo struct could not be encoded. func (v *ClusterInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SupportedClientVersions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.SupportedClientVersions.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SupportedClientVersions_Decode(sr stream.Reader) (*SupportedClientVersions, error) { var v SupportedClientVersions err := v.Decode(sr) return &v, err } // Decode deserializes a ClusterInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ClusterInfo struct could not be generated from the wire // representation. func (v *ClusterInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.SupportedClientVersions, err = _SupportedClientVersions_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ClusterInfo // struct. func (v *ClusterInfo) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.SupportedClientVersions != nil { fields[i] = fmt.Sprintf("SupportedClientVersions: %v", v.SupportedClientVersions) i++ } return fmt.Sprintf("ClusterInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ClusterInfo match the // provided ClusterInfo. // // This function performs a deep comparison. func (v *ClusterInfo) Equals(rhs *ClusterInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SupportedClientVersions == nil && rhs.SupportedClientVersions == nil) || (v.SupportedClientVersions != nil && rhs.SupportedClientVersions != nil && v.SupportedClientVersions.Equals(rhs.SupportedClientVersions))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ClusterInfo. func (v *ClusterInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SupportedClientVersions != nil { err = multierr.Append(err, enc.AddObject("supportedClientVersions", v.SupportedClientVersions)) } return err } // GetSupportedClientVersions returns the value of SupportedClientVersions if it is set or its // zero value if it is unset. func (v *ClusterInfo) GetSupportedClientVersions() (o *SupportedClientVersions) { if v != nil && v.SupportedClientVersions != nil { return v.SupportedClientVersions } return } // IsSetSupportedClientVersions returns true if SupportedClientVersions is not nil. func (v *ClusterInfo) IsSetSupportedClientVersions() bool { return v != nil && v.SupportedClientVersions != nil } type ClusterReplicationConfiguration struct { ClusterName *string `json:"clusterName,omitempty"` } // ToWire translates a ClusterReplicationConfiguration struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ClusterReplicationConfiguration) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ClusterName != nil { w, err = wire.NewValueString(*(v.ClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ClusterReplicationConfiguration struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ClusterReplicationConfiguration struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ClusterReplicationConfiguration // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ClusterReplicationConfiguration) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClusterName = &x if err != nil { return err } } } } return nil } // Encode serializes a ClusterReplicationConfiguration struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ClusterReplicationConfiguration struct could not be encoded. func (v *ClusterReplicationConfiguration) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ClusterReplicationConfiguration struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ClusterReplicationConfiguration struct could not be generated from the wire // representation. func (v *ClusterReplicationConfiguration) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClusterName = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ClusterReplicationConfiguration // struct. func (v *ClusterReplicationConfiguration) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ClusterName != nil { fields[i] = fmt.Sprintf("ClusterName: %v", *(v.ClusterName)) i++ } return fmt.Sprintf("ClusterReplicationConfiguration{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ClusterReplicationConfiguration match the // provided ClusterReplicationConfiguration. // // This function performs a deep comparison. func (v *ClusterReplicationConfiguration) Equals(rhs *ClusterReplicationConfiguration) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ClusterName, rhs.ClusterName) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ClusterReplicationConfiguration. func (v *ClusterReplicationConfiguration) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ClusterName != nil { enc.AddString("clusterName", *v.ClusterName) } return err } // GetClusterName returns the value of ClusterName if it is set or its // zero value if it is unset. func (v *ClusterReplicationConfiguration) GetClusterName() (o string) { if v != nil && v.ClusterName != nil { return *v.ClusterName } return } // IsSetClusterName returns true if ClusterName is not nil. func (v *ClusterReplicationConfiguration) IsSetClusterName() bool { return v != nil && v.ClusterName != nil } type CompleteWorkflowExecutionDecisionAttributes struct { Result []byte `json:"result,omitempty"` } // ToWire translates a CompleteWorkflowExecutionDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CompleteWorkflowExecutionDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Result != nil { w, err = wire.NewValueBinary(v.Result), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CompleteWorkflowExecutionDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CompleteWorkflowExecutionDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CompleteWorkflowExecutionDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CompleteWorkflowExecutionDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.Result, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a CompleteWorkflowExecutionDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CompleteWorkflowExecutionDecisionAttributes struct could not be encoded. func (v *CompleteWorkflowExecutionDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Result != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Result); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CompleteWorkflowExecutionDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CompleteWorkflowExecutionDecisionAttributes struct could not be generated from the wire // representation. func (v *CompleteWorkflowExecutionDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.Result, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CompleteWorkflowExecutionDecisionAttributes // struct. func (v *CompleteWorkflowExecutionDecisionAttributes) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Result != nil { fields[i] = fmt.Sprintf("Result: %v", v.Result) i++ } return fmt.Sprintf("CompleteWorkflowExecutionDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CompleteWorkflowExecutionDecisionAttributes match the // provided CompleteWorkflowExecutionDecisionAttributes. // // This function performs a deep comparison. func (v *CompleteWorkflowExecutionDecisionAttributes) Equals(rhs *CompleteWorkflowExecutionDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Result == nil && rhs.Result == nil) || (v.Result != nil && rhs.Result != nil && bytes.Equal(v.Result, rhs.Result))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CompleteWorkflowExecutionDecisionAttributes. func (v *CompleteWorkflowExecutionDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Result != nil { enc.AddString("result", base64.StdEncoding.EncodeToString(v.Result)) } return err } // GetResult returns the value of Result if it is set or its // zero value if it is unset. func (v *CompleteWorkflowExecutionDecisionAttributes) GetResult() (o []byte) { if v != nil && v.Result != nil { return v.Result } return } // IsSetResult returns true if Result is not nil. func (v *CompleteWorkflowExecutionDecisionAttributes) IsSetResult() bool { return v != nil && v.Result != nil } type ContinueAsNewInitiator int32 const ( ContinueAsNewInitiatorDecider ContinueAsNewInitiator = 0 ContinueAsNewInitiatorRetryPolicy ContinueAsNewInitiator = 1 ContinueAsNewInitiatorCronSchedule ContinueAsNewInitiator = 2 ) // ContinueAsNewInitiator_Values returns all recognized values of ContinueAsNewInitiator. func ContinueAsNewInitiator_Values() []ContinueAsNewInitiator { return []ContinueAsNewInitiator{ ContinueAsNewInitiatorDecider, ContinueAsNewInitiatorRetryPolicy, ContinueAsNewInitiatorCronSchedule, } } // UnmarshalText tries to decode ContinueAsNewInitiator from a byte slice // containing its name. // // var v ContinueAsNewInitiator // err := v.UnmarshalText([]byte("Decider")) func (v *ContinueAsNewInitiator) UnmarshalText(value []byte) error { switch s := string(value); s { case "Decider": *v = ContinueAsNewInitiatorDecider return nil case "RetryPolicy": *v = ContinueAsNewInitiatorRetryPolicy return nil case "CronSchedule": *v = ContinueAsNewInitiatorCronSchedule return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ContinueAsNewInitiator", err) } *v = ContinueAsNewInitiator(val) return nil } } // MarshalText encodes ContinueAsNewInitiator to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v ContinueAsNewInitiator) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("Decider"), nil case 1: return []byte("RetryPolicy"), nil case 2: return []byte("CronSchedule"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ContinueAsNewInitiator. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v ContinueAsNewInitiator) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "Decider") case 1: enc.AddString("name", "RetryPolicy") case 2: enc.AddString("name", "CronSchedule") } return nil } // Ptr returns a pointer to this enum value. func (v ContinueAsNewInitiator) Ptr() *ContinueAsNewInitiator { return &v } // Encode encodes ContinueAsNewInitiator directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v ContinueAsNewInitiator // return v.Encode(sWriter) func (v ContinueAsNewInitiator) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates ContinueAsNewInitiator into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v ContinueAsNewInitiator) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes ContinueAsNewInitiator from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return ContinueAsNewInitiator(0), err // } // // var v ContinueAsNewInitiator // if err := v.FromWire(x); err != nil { // return ContinueAsNewInitiator(0), err // } // return v, nil func (v *ContinueAsNewInitiator) FromWire(w wire.Value) error { *v = (ContinueAsNewInitiator)(w.GetI32()) return nil } // Decode reads off the encoded ContinueAsNewInitiator directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v ContinueAsNewInitiator // if err := v.Decode(sReader); err != nil { // return ContinueAsNewInitiator(0), err // } // return v, nil func (v *ContinueAsNewInitiator) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (ContinueAsNewInitiator)(i) return nil } // String returns a readable string representation of ContinueAsNewInitiator. func (v ContinueAsNewInitiator) String() string { w := int32(v) switch w { case 0: return "Decider" case 1: return "RetryPolicy" case 2: return "CronSchedule" } return fmt.Sprintf("ContinueAsNewInitiator(%d)", w) } // Equals returns true if this ContinueAsNewInitiator value matches the provided // value. func (v ContinueAsNewInitiator) Equals(rhs ContinueAsNewInitiator) bool { return v == rhs } // MarshalJSON serializes ContinueAsNewInitiator into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v ContinueAsNewInitiator) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"Decider\""), nil case 1: return ([]byte)("\"RetryPolicy\""), nil case 2: return ([]byte)("\"CronSchedule\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode ContinueAsNewInitiator from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *ContinueAsNewInitiator) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "ContinueAsNewInitiator") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "ContinueAsNewInitiator") } *v = (ContinueAsNewInitiator)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "ContinueAsNewInitiator") } } type ContinueAsNewWorkflowExecutionDecisionAttributes struct { WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` BackoffStartIntervalInSeconds *int32 `json:"backoffStartIntervalInSeconds,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` Initiator *ContinueAsNewInitiator `json:"initiator,omitempty"` FailureReason *string `json:"failureReason,omitempty"` FailureDetails []byte `json:"failureDetails,omitempty"` LastCompletionResult []byte `json:"lastCompletionResult,omitempty"` CronSchedule *string `json:"cronSchedule,omitempty"` Header *Header `json:"header,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` JitterStartSeconds *int32 `json:"jitterStartSeconds,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // ToWire translates a ContinueAsNewWorkflowExecutionDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [18]wire.Field i int = 0 w wire.Value err error ) if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ExecutionStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.TaskStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.TaskStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.BackoffStartIntervalInSeconds != nil { w, err = wire.NewValueI32(*(v.BackoffStartIntervalInSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.RetryPolicy != nil { w, err = v.RetryPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.Initiator != nil { w, err = v.Initiator.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.FailureReason != nil { w, err = wire.NewValueString(*(v.FailureReason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.FailureDetails != nil { w, err = wire.NewValueBinary(v.FailureDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.LastCompletionResult != nil { w, err = wire.NewValueBinary(v.LastCompletionResult), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.CronSchedule != nil { w, err = wire.NewValueString(*(v.CronSchedule)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.Memo != nil { w, err = v.Memo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.SearchAttributes != nil { w, err = v.SearchAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.JitterStartSeconds != nil { w, err = wire.NewValueI32(*(v.JitterStartSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.CronOverlapPolicy != nil { w, err = v.CronOverlapPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } if v.ActiveClusterSelectionPolicy != nil { w, err = v.ActiveClusterSelectionPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 180, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ContinueAsNewInitiator_Read(w wire.Value) (ContinueAsNewInitiator, error) { var v ContinueAsNewInitiator err := v.FromWire(w) return v, err } func _Memo_Read(w wire.Value) (*Memo, error) { var v Memo err := v.FromWire(w) return &v, err } func _SearchAttributes_Read(w wire.Value) (*SearchAttributes, error) { var v SearchAttributes err := v.FromWire(w) return &v, err } func _CronOverlapPolicy_Read(w wire.Value) (CronOverlapPolicy, error) { var v CronOverlapPolicy err := v.FromWire(w) return v, err } func _ActiveClusterSelectionPolicy_Read(w wire.Value) (*ActiveClusterSelectionPolicy, error) { var v ActiveClusterSelectionPolicy err := v.FromWire(w) return &v, err } // FromWire deserializes a ContinueAsNewWorkflowExecutionDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ContinueAsNewWorkflowExecutionDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ContinueAsNewWorkflowExecutionDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.BackoffStartIntervalInSeconds = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.RetryPolicy, err = _RetryPolicy_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TI32 { var x ContinueAsNewInitiator x, err = _ContinueAsNewInitiator_Read(field.Value) v.Initiator = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.FailureReason = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TBinary { v.FailureDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 110: if field.Value.Type() == wire.TBinary { v.LastCompletionResult, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 120: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CronSchedule = &x if err != nil { return err } } case 130: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } case 140: if field.Value.Type() == wire.TStruct { v.Memo, err = _Memo_Read(field.Value) if err != nil { return err } } case 150: if field.Value.Type() == wire.TStruct { v.SearchAttributes, err = _SearchAttributes_Read(field.Value) if err != nil { return err } } case 160: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.JitterStartSeconds = &x if err != nil { return err } } case 170: if field.Value.Type() == wire.TI32 { var x CronOverlapPolicy x, err = _CronOverlapPolicy_Read(field.Value) v.CronOverlapPolicy = &x if err != nil { return err } } case 180: if field.Value.Type() == wire.TStruct { v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ContinueAsNewWorkflowExecutionDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ContinueAsNewWorkflowExecutionDecisionAttributes struct could not be encoded. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ExecutionStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.TaskStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BackoffStartIntervalInSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.BackoffStartIntervalInSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.RetryPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Initiator != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI32}); err != nil { return err } if err := v.Initiator.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailureReason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.FailureReason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailureDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.FailureDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastCompletionResult != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.LastCompletionResult); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronSchedule != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CronSchedule)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Memo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TStruct}); err != nil { return err } if err := v.Memo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TStruct}); err != nil { return err } if err := v.SearchAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.JitterStartSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.JitterStartSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronOverlapPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TI32}); err != nil { return err } if err := v.CronOverlapPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterSelectionPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 180, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusterSelectionPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ContinueAsNewInitiator_Decode(sr stream.Reader) (ContinueAsNewInitiator, error) { var v ContinueAsNewInitiator err := v.Decode(sr) return v, err } func _Memo_Decode(sr stream.Reader) (*Memo, error) { var v Memo err := v.Decode(sr) return &v, err } func _SearchAttributes_Decode(sr stream.Reader) (*SearchAttributes, error) { var v SearchAttributes err := v.Decode(sr) return &v, err } func _CronOverlapPolicy_Decode(sr stream.Reader) (CronOverlapPolicy, error) { var v CronOverlapPolicy err := v.Decode(sr) return v, err } func _ActiveClusterSelectionPolicy_Decode(sr stream.Reader) (*ActiveClusterSelectionPolicy, error) { var v ActiveClusterSelectionPolicy err := v.Decode(sr) return &v, err } // Decode deserializes a ContinueAsNewWorkflowExecutionDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ContinueAsNewWorkflowExecutionDecisionAttributes struct could not be generated from the wire // representation. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.BackoffStartIntervalInSeconds = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.RetryPolicy, err = _RetryPolicy_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI32: var x ContinueAsNewInitiator x, err = _ContinueAsNewInitiator_Decode(sr) v.Initiator = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.FailureReason = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TBinary: v.FailureDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TBinary: v.LastCompletionResult, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CronSchedule = &x if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TStruct: v.Memo, err = _Memo_Decode(sr) if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TStruct: v.SearchAttributes, err = _SearchAttributes_Decode(sr) if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.JitterStartSeconds = &x if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TI32: var x CronOverlapPolicy x, err = _CronOverlapPolicy_Decode(sr) v.CronOverlapPolicy = &x if err != nil { return err } case fh.ID == 180 && fh.Type == wire.TStruct: v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ContinueAsNewWorkflowExecutionDecisionAttributes // struct. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) String() string { if v == nil { return "" } var fields [18]string i := 0 if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ExecutionStartToCloseTimeoutSeconds: %v", *(v.ExecutionStartToCloseTimeoutSeconds)) i++ } if v.TaskStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("TaskStartToCloseTimeoutSeconds: %v", *(v.TaskStartToCloseTimeoutSeconds)) i++ } if v.BackoffStartIntervalInSeconds != nil { fields[i] = fmt.Sprintf("BackoffStartIntervalInSeconds: %v", *(v.BackoffStartIntervalInSeconds)) i++ } if v.RetryPolicy != nil { fields[i] = fmt.Sprintf("RetryPolicy: %v", v.RetryPolicy) i++ } if v.Initiator != nil { fields[i] = fmt.Sprintf("Initiator: %v", *(v.Initiator)) i++ } if v.FailureReason != nil { fields[i] = fmt.Sprintf("FailureReason: %v", *(v.FailureReason)) i++ } if v.FailureDetails != nil { fields[i] = fmt.Sprintf("FailureDetails: %v", v.FailureDetails) i++ } if v.LastCompletionResult != nil { fields[i] = fmt.Sprintf("LastCompletionResult: %v", v.LastCompletionResult) i++ } if v.CronSchedule != nil { fields[i] = fmt.Sprintf("CronSchedule: %v", *(v.CronSchedule)) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } if v.Memo != nil { fields[i] = fmt.Sprintf("Memo: %v", v.Memo) i++ } if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } if v.JitterStartSeconds != nil { fields[i] = fmt.Sprintf("JitterStartSeconds: %v", *(v.JitterStartSeconds)) i++ } if v.CronOverlapPolicy != nil { fields[i] = fmt.Sprintf("CronOverlapPolicy: %v", *(v.CronOverlapPolicy)) i++ } if v.ActiveClusterSelectionPolicy != nil { fields[i] = fmt.Sprintf("ActiveClusterSelectionPolicy: %v", v.ActiveClusterSelectionPolicy) i++ } return fmt.Sprintf("ContinueAsNewWorkflowExecutionDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } func _ContinueAsNewInitiator_EqualsPtr(lhs, rhs *ContinueAsNewInitiator) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } func _CronOverlapPolicy_EqualsPtr(lhs, rhs *CronOverlapPolicy) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ContinueAsNewWorkflowExecutionDecisionAttributes match the // provided ContinueAsNewWorkflowExecutionDecisionAttributes. // // This function performs a deep comparison. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) Equals(rhs *ContinueAsNewWorkflowExecutionDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_I32_EqualsPtr(v.ExecutionStartToCloseTimeoutSeconds, rhs.ExecutionStartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.TaskStartToCloseTimeoutSeconds, rhs.TaskStartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.BackoffStartIntervalInSeconds, rhs.BackoffStartIntervalInSeconds) { return false } if !((v.RetryPolicy == nil && rhs.RetryPolicy == nil) || (v.RetryPolicy != nil && rhs.RetryPolicy != nil && v.RetryPolicy.Equals(rhs.RetryPolicy))) { return false } if !_ContinueAsNewInitiator_EqualsPtr(v.Initiator, rhs.Initiator) { return false } if !_String_EqualsPtr(v.FailureReason, rhs.FailureReason) { return false } if !((v.FailureDetails == nil && rhs.FailureDetails == nil) || (v.FailureDetails != nil && rhs.FailureDetails != nil && bytes.Equal(v.FailureDetails, rhs.FailureDetails))) { return false } if !((v.LastCompletionResult == nil && rhs.LastCompletionResult == nil) || (v.LastCompletionResult != nil && rhs.LastCompletionResult != nil && bytes.Equal(v.LastCompletionResult, rhs.LastCompletionResult))) { return false } if !_String_EqualsPtr(v.CronSchedule, rhs.CronSchedule) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } if !((v.Memo == nil && rhs.Memo == nil) || (v.Memo != nil && rhs.Memo != nil && v.Memo.Equals(rhs.Memo))) { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && v.SearchAttributes.Equals(rhs.SearchAttributes))) { return false } if !_I32_EqualsPtr(v.JitterStartSeconds, rhs.JitterStartSeconds) { return false } if !_CronOverlapPolicy_EqualsPtr(v.CronOverlapPolicy, rhs.CronOverlapPolicy) { return false } if !((v.ActiveClusterSelectionPolicy == nil && rhs.ActiveClusterSelectionPolicy == nil) || (v.ActiveClusterSelectionPolicy != nil && rhs.ActiveClusterSelectionPolicy != nil && v.ActiveClusterSelectionPolicy.Equals(rhs.ActiveClusterSelectionPolicy))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ContinueAsNewWorkflowExecutionDecisionAttributes. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.ExecutionStartToCloseTimeoutSeconds != nil { enc.AddInt32("executionStartToCloseTimeoutSeconds", *v.ExecutionStartToCloseTimeoutSeconds) } if v.TaskStartToCloseTimeoutSeconds != nil { enc.AddInt32("taskStartToCloseTimeoutSeconds", *v.TaskStartToCloseTimeoutSeconds) } if v.BackoffStartIntervalInSeconds != nil { enc.AddInt32("backoffStartIntervalInSeconds", *v.BackoffStartIntervalInSeconds) } if v.RetryPolicy != nil { err = multierr.Append(err, enc.AddObject("retryPolicy", v.RetryPolicy)) } if v.Initiator != nil { err = multierr.Append(err, enc.AddObject("initiator", *v.Initiator)) } if v.FailureReason != nil { enc.AddString("failureReason", *v.FailureReason) } if v.FailureDetails != nil { enc.AddString("failureDetails", base64.StdEncoding.EncodeToString(v.FailureDetails)) } if v.LastCompletionResult != nil { enc.AddString("lastCompletionResult", base64.StdEncoding.EncodeToString(v.LastCompletionResult)) } if v.CronSchedule != nil { enc.AddString("cronSchedule", *v.CronSchedule) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } if v.Memo != nil { err = multierr.Append(err, enc.AddObject("memo", v.Memo)) } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", v.SearchAttributes)) } if v.JitterStartSeconds != nil { enc.AddInt32("jitterStartSeconds", *v.JitterStartSeconds) } if v.CronOverlapPolicy != nil { err = multierr.Append(err, enc.AddObject("cronOverlapPolicy", *v.CronOverlapPolicy)) } if v.ActiveClusterSelectionPolicy != nil { err = multierr.Append(err, enc.AddObject("activeClusterSelectionPolicy", v.ActiveClusterSelectionPolicy)) } return err } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetInput() bool { return v != nil && v.Input != nil } // GetExecutionStartToCloseTimeoutSeconds returns the value of ExecutionStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // IsSetExecutionStartToCloseTimeoutSeconds returns true if ExecutionStartToCloseTimeoutSeconds is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetExecutionStartToCloseTimeoutSeconds() bool { return v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil } // GetTaskStartToCloseTimeoutSeconds returns the value of TaskStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // IsSetTaskStartToCloseTimeoutSeconds returns true if TaskStartToCloseTimeoutSeconds is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetTaskStartToCloseTimeoutSeconds() bool { return v != nil && v.TaskStartToCloseTimeoutSeconds != nil } // GetBackoffStartIntervalInSeconds returns the value of BackoffStartIntervalInSeconds if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetBackoffStartIntervalInSeconds() (o int32) { if v != nil && v.BackoffStartIntervalInSeconds != nil { return *v.BackoffStartIntervalInSeconds } return } // IsSetBackoffStartIntervalInSeconds returns true if BackoffStartIntervalInSeconds is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetBackoffStartIntervalInSeconds() bool { return v != nil && v.BackoffStartIntervalInSeconds != nil } // GetRetryPolicy returns the value of RetryPolicy if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetRetryPolicy() (o *RetryPolicy) { if v != nil && v.RetryPolicy != nil { return v.RetryPolicy } return } // IsSetRetryPolicy returns true if RetryPolicy is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetRetryPolicy() bool { return v != nil && v.RetryPolicy != nil } // GetInitiator returns the value of Initiator if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetInitiator() (o ContinueAsNewInitiator) { if v != nil && v.Initiator != nil { return *v.Initiator } return } // IsSetInitiator returns true if Initiator is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetInitiator() bool { return v != nil && v.Initiator != nil } // GetFailureReason returns the value of FailureReason if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetFailureReason() (o string) { if v != nil && v.FailureReason != nil { return *v.FailureReason } return } // IsSetFailureReason returns true if FailureReason is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetFailureReason() bool { return v != nil && v.FailureReason != nil } // GetFailureDetails returns the value of FailureDetails if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetFailureDetails() (o []byte) { if v != nil && v.FailureDetails != nil { return v.FailureDetails } return } // IsSetFailureDetails returns true if FailureDetails is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetFailureDetails() bool { return v != nil && v.FailureDetails != nil } // GetLastCompletionResult returns the value of LastCompletionResult if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetLastCompletionResult() (o []byte) { if v != nil && v.LastCompletionResult != nil { return v.LastCompletionResult } return } // IsSetLastCompletionResult returns true if LastCompletionResult is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetLastCompletionResult() bool { return v != nil && v.LastCompletionResult != nil } // GetCronSchedule returns the value of CronSchedule if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetCronSchedule() (o string) { if v != nil && v.CronSchedule != nil { return *v.CronSchedule } return } // IsSetCronSchedule returns true if CronSchedule is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetCronSchedule() bool { return v != nil && v.CronSchedule != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetHeader() bool { return v != nil && v.Header != nil } // GetMemo returns the value of Memo if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetMemo() (o *Memo) { if v != nil && v.Memo != nil { return v.Memo } return } // IsSetMemo returns true if Memo is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetMemo() bool { return v != nil && v.Memo != nil } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } // GetJitterStartSeconds returns the value of JitterStartSeconds if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetJitterStartSeconds() (o int32) { if v != nil && v.JitterStartSeconds != nil { return *v.JitterStartSeconds } return } // IsSetJitterStartSeconds returns true if JitterStartSeconds is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetJitterStartSeconds() bool { return v != nil && v.JitterStartSeconds != nil } // GetCronOverlapPolicy returns the value of CronOverlapPolicy if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetCronOverlapPolicy() (o CronOverlapPolicy) { if v != nil && v.CronOverlapPolicy != nil { return *v.CronOverlapPolicy } return } // IsSetCronOverlapPolicy returns true if CronOverlapPolicy is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetCronOverlapPolicy() bool { return v != nil && v.CronOverlapPolicy != nil } // GetActiveClusterSelectionPolicy returns the value of ActiveClusterSelectionPolicy if it is set or its // zero value if it is unset. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetActiveClusterSelectionPolicy() (o *ActiveClusterSelectionPolicy) { if v != nil && v.ActiveClusterSelectionPolicy != nil { return v.ActiveClusterSelectionPolicy } return } // IsSetActiveClusterSelectionPolicy returns true if ActiveClusterSelectionPolicy is not nil. func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) IsSetActiveClusterSelectionPolicy() bool { return v != nil && v.ActiveClusterSelectionPolicy != nil } type CountWorkflowExecutionsRequest struct { Domain *string `json:"domain,omitempty"` Query *string `json:"query,omitempty"` } // ToWire translates a CountWorkflowExecutionsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CountWorkflowExecutionsRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Query != nil { w, err = wire.NewValueString(*(v.Query)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CountWorkflowExecutionsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CountWorkflowExecutionsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CountWorkflowExecutionsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CountWorkflowExecutionsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Query = &x if err != nil { return err } } } } return nil } // Encode serializes a CountWorkflowExecutionsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CountWorkflowExecutionsRequest struct could not be encoded. func (v *CountWorkflowExecutionsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Query != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Query)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CountWorkflowExecutionsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CountWorkflowExecutionsRequest struct could not be generated from the wire // representation. func (v *CountWorkflowExecutionsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Query = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CountWorkflowExecutionsRequest // struct. func (v *CountWorkflowExecutionsRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Query != nil { fields[i] = fmt.Sprintf("Query: %v", *(v.Query)) i++ } return fmt.Sprintf("CountWorkflowExecutionsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CountWorkflowExecutionsRequest match the // provided CountWorkflowExecutionsRequest. // // This function performs a deep comparison. func (v *CountWorkflowExecutionsRequest) Equals(rhs *CountWorkflowExecutionsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.Query, rhs.Query) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CountWorkflowExecutionsRequest. func (v *CountWorkflowExecutionsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Query != nil { enc.AddString("query", *v.Query) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *CountWorkflowExecutionsRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *CountWorkflowExecutionsRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetQuery returns the value of Query if it is set or its // zero value if it is unset. func (v *CountWorkflowExecutionsRequest) GetQuery() (o string) { if v != nil && v.Query != nil { return *v.Query } return } // IsSetQuery returns true if Query is not nil. func (v *CountWorkflowExecutionsRequest) IsSetQuery() bool { return v != nil && v.Query != nil } type CountWorkflowExecutionsResponse struct { Count *int64 `json:"count,omitempty"` } // ToWire translates a CountWorkflowExecutionsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CountWorkflowExecutionsResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Count != nil { w, err = wire.NewValueI64(*(v.Count)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CountWorkflowExecutionsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CountWorkflowExecutionsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CountWorkflowExecutionsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CountWorkflowExecutionsResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Count = &x if err != nil { return err } } } } return nil } // Encode serializes a CountWorkflowExecutionsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CountWorkflowExecutionsResponse struct could not be encoded. func (v *CountWorkflowExecutionsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Count != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Count)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CountWorkflowExecutionsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CountWorkflowExecutionsResponse struct could not be generated from the wire // representation. func (v *CountWorkflowExecutionsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Count = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CountWorkflowExecutionsResponse // struct. func (v *CountWorkflowExecutionsResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Count != nil { fields[i] = fmt.Sprintf("Count: %v", *(v.Count)) i++ } return fmt.Sprintf("CountWorkflowExecutionsResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CountWorkflowExecutionsResponse match the // provided CountWorkflowExecutionsResponse. // // This function performs a deep comparison. func (v *CountWorkflowExecutionsResponse) Equals(rhs *CountWorkflowExecutionsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.Count, rhs.Count) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CountWorkflowExecutionsResponse. func (v *CountWorkflowExecutionsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Count != nil { enc.AddInt64("count", *v.Count) } return err } // GetCount returns the value of Count if it is set or its // zero value if it is unset. func (v *CountWorkflowExecutionsResponse) GetCount() (o int64) { if v != nil && v.Count != nil { return *v.Count } return } // IsSetCount returns true if Count is not nil. func (v *CountWorkflowExecutionsResponse) IsSetCount() bool { return v != nil && v.Count != nil } type CronOverlapPolicy int32 const ( CronOverlapPolicySkipped CronOverlapPolicy = 0 CronOverlapPolicyBufferone CronOverlapPolicy = 1 ) // CronOverlapPolicy_Values returns all recognized values of CronOverlapPolicy. func CronOverlapPolicy_Values() []CronOverlapPolicy { return []CronOverlapPolicy{ CronOverlapPolicySkipped, CronOverlapPolicyBufferone, } } // UnmarshalText tries to decode CronOverlapPolicy from a byte slice // containing its name. // // var v CronOverlapPolicy // err := v.UnmarshalText([]byte("SKIPPED")) func (v *CronOverlapPolicy) UnmarshalText(value []byte) error { switch s := string(value); s { case "SKIPPED": *v = CronOverlapPolicySkipped return nil case "BUFFERONE": *v = CronOverlapPolicyBufferone return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "CronOverlapPolicy", err) } *v = CronOverlapPolicy(val) return nil } } // MarshalText encodes CronOverlapPolicy to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v CronOverlapPolicy) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("SKIPPED"), nil case 1: return []byte("BUFFERONE"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CronOverlapPolicy. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v CronOverlapPolicy) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "SKIPPED") case 1: enc.AddString("name", "BUFFERONE") } return nil } // Ptr returns a pointer to this enum value. func (v CronOverlapPolicy) Ptr() *CronOverlapPolicy { return &v } // Encode encodes CronOverlapPolicy directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v CronOverlapPolicy // return v.Encode(sWriter) func (v CronOverlapPolicy) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates CronOverlapPolicy into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v CronOverlapPolicy) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes CronOverlapPolicy from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return CronOverlapPolicy(0), err // } // // var v CronOverlapPolicy // if err := v.FromWire(x); err != nil { // return CronOverlapPolicy(0), err // } // return v, nil func (v *CronOverlapPolicy) FromWire(w wire.Value) error { *v = (CronOverlapPolicy)(w.GetI32()) return nil } // Decode reads off the encoded CronOverlapPolicy directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v CronOverlapPolicy // if err := v.Decode(sReader); err != nil { // return CronOverlapPolicy(0), err // } // return v, nil func (v *CronOverlapPolicy) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (CronOverlapPolicy)(i) return nil } // String returns a readable string representation of CronOverlapPolicy. func (v CronOverlapPolicy) String() string { w := int32(v) switch w { case 0: return "SKIPPED" case 1: return "BUFFERONE" } return fmt.Sprintf("CronOverlapPolicy(%d)", w) } // Equals returns true if this CronOverlapPolicy value matches the provided // value. func (v CronOverlapPolicy) Equals(rhs CronOverlapPolicy) bool { return v == rhs } // MarshalJSON serializes CronOverlapPolicy into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v CronOverlapPolicy) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"SKIPPED\""), nil case 1: return ([]byte)("\"BUFFERONE\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode CronOverlapPolicy from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *CronOverlapPolicy) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "CronOverlapPolicy") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "CronOverlapPolicy") } *v = (CronOverlapPolicy)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "CronOverlapPolicy") } } type CrossClusterApplyParentClosePolicyRequestAttributes struct { Children []*ApplyParentClosePolicyRequest `json:"children,omitempty"` } type _List_ApplyParentClosePolicyRequest_ValueList []*ApplyParentClosePolicyRequest func (v _List_ApplyParentClosePolicyRequest_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*ApplyParentClosePolicyRequest', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_ApplyParentClosePolicyRequest_ValueList) Size() int { return len(v) } func (_List_ApplyParentClosePolicyRequest_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_ApplyParentClosePolicyRequest_ValueList) Close() {} // ToWire translates a CrossClusterApplyParentClosePolicyRequestAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterApplyParentClosePolicyRequestAttributes) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Children != nil { w, err = wire.NewValueList(_List_ApplyParentClosePolicyRequest_ValueList(v.Children)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ApplyParentClosePolicyRequest_Read(w wire.Value) (*ApplyParentClosePolicyRequest, error) { var v ApplyParentClosePolicyRequest err := v.FromWire(w) return &v, err } func _List_ApplyParentClosePolicyRequest_Read(l wire.ValueList) ([]*ApplyParentClosePolicyRequest, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*ApplyParentClosePolicyRequest, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _ApplyParentClosePolicyRequest_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a CrossClusterApplyParentClosePolicyRequestAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterApplyParentClosePolicyRequestAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterApplyParentClosePolicyRequestAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterApplyParentClosePolicyRequestAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Children, err = _List_ApplyParentClosePolicyRequest_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_ApplyParentClosePolicyRequest_Encode(val []*ApplyParentClosePolicyRequest, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*ApplyParentClosePolicyRequest', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a CrossClusterApplyParentClosePolicyRequestAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterApplyParentClosePolicyRequestAttributes struct could not be encoded. func (v *CrossClusterApplyParentClosePolicyRequestAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Children != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_ApplyParentClosePolicyRequest_Encode(v.Children, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ApplyParentClosePolicyRequest_Decode(sr stream.Reader) (*ApplyParentClosePolicyRequest, error) { var v ApplyParentClosePolicyRequest err := v.Decode(sr) return &v, err } func _List_ApplyParentClosePolicyRequest_Decode(sr stream.Reader) ([]*ApplyParentClosePolicyRequest, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*ApplyParentClosePolicyRequest, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _ApplyParentClosePolicyRequest_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a CrossClusterApplyParentClosePolicyRequestAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterApplyParentClosePolicyRequestAttributes struct could not be generated from the wire // representation. func (v *CrossClusterApplyParentClosePolicyRequestAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Children, err = _List_ApplyParentClosePolicyRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterApplyParentClosePolicyRequestAttributes // struct. func (v *CrossClusterApplyParentClosePolicyRequestAttributes) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Children != nil { fields[i] = fmt.Sprintf("Children: %v", v.Children) i++ } return fmt.Sprintf("CrossClusterApplyParentClosePolicyRequestAttributes{%v}", strings.Join(fields[:i], ", ")) } func _List_ApplyParentClosePolicyRequest_Equals(lhs, rhs []*ApplyParentClosePolicyRequest) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this CrossClusterApplyParentClosePolicyRequestAttributes match the // provided CrossClusterApplyParentClosePolicyRequestAttributes. // // This function performs a deep comparison. func (v *CrossClusterApplyParentClosePolicyRequestAttributes) Equals(rhs *CrossClusterApplyParentClosePolicyRequestAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Children == nil && rhs.Children == nil) || (v.Children != nil && rhs.Children != nil && _List_ApplyParentClosePolicyRequest_Equals(v.Children, rhs.Children))) { return false } return true } type _List_ApplyParentClosePolicyRequest_Zapper []*ApplyParentClosePolicyRequest // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_ApplyParentClosePolicyRequest_Zapper. func (l _List_ApplyParentClosePolicyRequest_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterApplyParentClosePolicyRequestAttributes. func (v *CrossClusterApplyParentClosePolicyRequestAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Children != nil { err = multierr.Append(err, enc.AddArray("children", (_List_ApplyParentClosePolicyRequest_Zapper)(v.Children))) } return err } // GetChildren returns the value of Children if it is set or its // zero value if it is unset. func (v *CrossClusterApplyParentClosePolicyRequestAttributes) GetChildren() (o []*ApplyParentClosePolicyRequest) { if v != nil && v.Children != nil { return v.Children } return } // IsSetChildren returns true if Children is not nil. func (v *CrossClusterApplyParentClosePolicyRequestAttributes) IsSetChildren() bool { return v != nil && v.Children != nil } type CrossClusterApplyParentClosePolicyResponseAttributes struct { ChildrenStatus []*ApplyParentClosePolicyResult `json:"childrenStatus,omitempty"` } type _List_ApplyParentClosePolicyResult_ValueList []*ApplyParentClosePolicyResult func (v _List_ApplyParentClosePolicyResult_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*ApplyParentClosePolicyResult', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_ApplyParentClosePolicyResult_ValueList) Size() int { return len(v) } func (_List_ApplyParentClosePolicyResult_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_ApplyParentClosePolicyResult_ValueList) Close() {} // ToWire translates a CrossClusterApplyParentClosePolicyResponseAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterApplyParentClosePolicyResponseAttributes) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ChildrenStatus != nil { w, err = wire.NewValueList(_List_ApplyParentClosePolicyResult_ValueList(v.ChildrenStatus)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ApplyParentClosePolicyResult_Read(w wire.Value) (*ApplyParentClosePolicyResult, error) { var v ApplyParentClosePolicyResult err := v.FromWire(w) return &v, err } func _List_ApplyParentClosePolicyResult_Read(l wire.ValueList) ([]*ApplyParentClosePolicyResult, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*ApplyParentClosePolicyResult, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _ApplyParentClosePolicyResult_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a CrossClusterApplyParentClosePolicyResponseAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterApplyParentClosePolicyResponseAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterApplyParentClosePolicyResponseAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterApplyParentClosePolicyResponseAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.ChildrenStatus, err = _List_ApplyParentClosePolicyResult_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_ApplyParentClosePolicyResult_Encode(val []*ApplyParentClosePolicyResult, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*ApplyParentClosePolicyResult', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a CrossClusterApplyParentClosePolicyResponseAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterApplyParentClosePolicyResponseAttributes struct could not be encoded. func (v *CrossClusterApplyParentClosePolicyResponseAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ChildrenStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_ApplyParentClosePolicyResult_Encode(v.ChildrenStatus, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ApplyParentClosePolicyResult_Decode(sr stream.Reader) (*ApplyParentClosePolicyResult, error) { var v ApplyParentClosePolicyResult err := v.Decode(sr) return &v, err } func _List_ApplyParentClosePolicyResult_Decode(sr stream.Reader) ([]*ApplyParentClosePolicyResult, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*ApplyParentClosePolicyResult, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _ApplyParentClosePolicyResult_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a CrossClusterApplyParentClosePolicyResponseAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterApplyParentClosePolicyResponseAttributes struct could not be generated from the wire // representation. func (v *CrossClusterApplyParentClosePolicyResponseAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.ChildrenStatus, err = _List_ApplyParentClosePolicyResult_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterApplyParentClosePolicyResponseAttributes // struct. func (v *CrossClusterApplyParentClosePolicyResponseAttributes) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ChildrenStatus != nil { fields[i] = fmt.Sprintf("ChildrenStatus: %v", v.ChildrenStatus) i++ } return fmt.Sprintf("CrossClusterApplyParentClosePolicyResponseAttributes{%v}", strings.Join(fields[:i], ", ")) } func _List_ApplyParentClosePolicyResult_Equals(lhs, rhs []*ApplyParentClosePolicyResult) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this CrossClusterApplyParentClosePolicyResponseAttributes match the // provided CrossClusterApplyParentClosePolicyResponseAttributes. // // This function performs a deep comparison. func (v *CrossClusterApplyParentClosePolicyResponseAttributes) Equals(rhs *CrossClusterApplyParentClosePolicyResponseAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ChildrenStatus == nil && rhs.ChildrenStatus == nil) || (v.ChildrenStatus != nil && rhs.ChildrenStatus != nil && _List_ApplyParentClosePolicyResult_Equals(v.ChildrenStatus, rhs.ChildrenStatus))) { return false } return true } type _List_ApplyParentClosePolicyResult_Zapper []*ApplyParentClosePolicyResult // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_ApplyParentClosePolicyResult_Zapper. func (l _List_ApplyParentClosePolicyResult_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterApplyParentClosePolicyResponseAttributes. func (v *CrossClusterApplyParentClosePolicyResponseAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ChildrenStatus != nil { err = multierr.Append(err, enc.AddArray("childrenStatus", (_List_ApplyParentClosePolicyResult_Zapper)(v.ChildrenStatus))) } return err } // GetChildrenStatus returns the value of ChildrenStatus if it is set or its // zero value if it is unset. func (v *CrossClusterApplyParentClosePolicyResponseAttributes) GetChildrenStatus() (o []*ApplyParentClosePolicyResult) { if v != nil && v.ChildrenStatus != nil { return v.ChildrenStatus } return } // IsSetChildrenStatus returns true if ChildrenStatus is not nil. func (v *CrossClusterApplyParentClosePolicyResponseAttributes) IsSetChildrenStatus() bool { return v != nil && v.ChildrenStatus != nil } type CrossClusterCancelExecutionRequestAttributes struct { TargetDomainID *string `json:"targetDomainID,omitempty"` TargetWorkflowID *string `json:"targetWorkflowID,omitempty"` TargetRunID *string `json:"targetRunID,omitempty"` RequestID *string `json:"requestID,omitempty"` InitiatedEventID *int64 `json:"initiatedEventID,omitempty"` ChildWorkflowOnly *bool `json:"childWorkflowOnly,omitempty"` } // ToWire translates a CrossClusterCancelExecutionRequestAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterCancelExecutionRequestAttributes) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.TargetDomainID != nil { w, err = wire.NewValueString(*(v.TargetDomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TargetWorkflowID != nil { w, err = wire.NewValueString(*(v.TargetWorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TargetRunID != nil { w, err = wire.NewValueString(*(v.TargetRunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.RequestID != nil { w, err = wire.NewValueString(*(v.RequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.InitiatedEventID != nil { w, err = wire.NewValueI64(*(v.InitiatedEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ChildWorkflowOnly != nil { w, err = wire.NewValueBool(*(v.ChildWorkflowOnly)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CrossClusterCancelExecutionRequestAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterCancelExecutionRequestAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterCancelExecutionRequestAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterCancelExecutionRequestAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetDomainID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetWorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetRunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventID = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ChildWorkflowOnly = &x if err != nil { return err } } } } return nil } // Encode serializes a CrossClusterCancelExecutionRequestAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterCancelExecutionRequestAttributes struct could not be encoded. func (v *CrossClusterCancelExecutionRequestAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TargetDomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetDomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetWorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetWorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetRunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowOnly != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ChildWorkflowOnly)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CrossClusterCancelExecutionRequestAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterCancelExecutionRequestAttributes struct could not be generated from the wire // representation. func (v *CrossClusterCancelExecutionRequestAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetDomainID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetWorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetRunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventID = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ChildWorkflowOnly = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterCancelExecutionRequestAttributes // struct. func (v *CrossClusterCancelExecutionRequestAttributes) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.TargetDomainID != nil { fields[i] = fmt.Sprintf("TargetDomainID: %v", *(v.TargetDomainID)) i++ } if v.TargetWorkflowID != nil { fields[i] = fmt.Sprintf("TargetWorkflowID: %v", *(v.TargetWorkflowID)) i++ } if v.TargetRunID != nil { fields[i] = fmt.Sprintf("TargetRunID: %v", *(v.TargetRunID)) i++ } if v.RequestID != nil { fields[i] = fmt.Sprintf("RequestID: %v", *(v.RequestID)) i++ } if v.InitiatedEventID != nil { fields[i] = fmt.Sprintf("InitiatedEventID: %v", *(v.InitiatedEventID)) i++ } if v.ChildWorkflowOnly != nil { fields[i] = fmt.Sprintf("ChildWorkflowOnly: %v", *(v.ChildWorkflowOnly)) i++ } return fmt.Sprintf("CrossClusterCancelExecutionRequestAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CrossClusterCancelExecutionRequestAttributes match the // provided CrossClusterCancelExecutionRequestAttributes. // // This function performs a deep comparison. func (v *CrossClusterCancelExecutionRequestAttributes) Equals(rhs *CrossClusterCancelExecutionRequestAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TargetDomainID, rhs.TargetDomainID) { return false } if !_String_EqualsPtr(v.TargetWorkflowID, rhs.TargetWorkflowID) { return false } if !_String_EqualsPtr(v.TargetRunID, rhs.TargetRunID) { return false } if !_String_EqualsPtr(v.RequestID, rhs.RequestID) { return false } if !_I64_EqualsPtr(v.InitiatedEventID, rhs.InitiatedEventID) { return false } if !_Bool_EqualsPtr(v.ChildWorkflowOnly, rhs.ChildWorkflowOnly) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterCancelExecutionRequestAttributes. func (v *CrossClusterCancelExecutionRequestAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TargetDomainID != nil { enc.AddString("targetDomainID", *v.TargetDomainID) } if v.TargetWorkflowID != nil { enc.AddString("targetWorkflowID", *v.TargetWorkflowID) } if v.TargetRunID != nil { enc.AddString("targetRunID", *v.TargetRunID) } if v.RequestID != nil { enc.AddString("requestID", *v.RequestID) } if v.InitiatedEventID != nil { enc.AddInt64("initiatedEventID", *v.InitiatedEventID) } if v.ChildWorkflowOnly != nil { enc.AddBool("childWorkflowOnly", *v.ChildWorkflowOnly) } return err } // GetTargetDomainID returns the value of TargetDomainID if it is set or its // zero value if it is unset. func (v *CrossClusterCancelExecutionRequestAttributes) GetTargetDomainID() (o string) { if v != nil && v.TargetDomainID != nil { return *v.TargetDomainID } return } // IsSetTargetDomainID returns true if TargetDomainID is not nil. func (v *CrossClusterCancelExecutionRequestAttributes) IsSetTargetDomainID() bool { return v != nil && v.TargetDomainID != nil } // GetTargetWorkflowID returns the value of TargetWorkflowID if it is set or its // zero value if it is unset. func (v *CrossClusterCancelExecutionRequestAttributes) GetTargetWorkflowID() (o string) { if v != nil && v.TargetWorkflowID != nil { return *v.TargetWorkflowID } return } // IsSetTargetWorkflowID returns true if TargetWorkflowID is not nil. func (v *CrossClusterCancelExecutionRequestAttributes) IsSetTargetWorkflowID() bool { return v != nil && v.TargetWorkflowID != nil } // GetTargetRunID returns the value of TargetRunID if it is set or its // zero value if it is unset. func (v *CrossClusterCancelExecutionRequestAttributes) GetTargetRunID() (o string) { if v != nil && v.TargetRunID != nil { return *v.TargetRunID } return } // IsSetTargetRunID returns true if TargetRunID is not nil. func (v *CrossClusterCancelExecutionRequestAttributes) IsSetTargetRunID() bool { return v != nil && v.TargetRunID != nil } // GetRequestID returns the value of RequestID if it is set or its // zero value if it is unset. func (v *CrossClusterCancelExecutionRequestAttributes) GetRequestID() (o string) { if v != nil && v.RequestID != nil { return *v.RequestID } return } // IsSetRequestID returns true if RequestID is not nil. func (v *CrossClusterCancelExecutionRequestAttributes) IsSetRequestID() bool { return v != nil && v.RequestID != nil } // GetInitiatedEventID returns the value of InitiatedEventID if it is set or its // zero value if it is unset. func (v *CrossClusterCancelExecutionRequestAttributes) GetInitiatedEventID() (o int64) { if v != nil && v.InitiatedEventID != nil { return *v.InitiatedEventID } return } // IsSetInitiatedEventID returns true if InitiatedEventID is not nil. func (v *CrossClusterCancelExecutionRequestAttributes) IsSetInitiatedEventID() bool { return v != nil && v.InitiatedEventID != nil } // GetChildWorkflowOnly returns the value of ChildWorkflowOnly if it is set or its // zero value if it is unset. func (v *CrossClusterCancelExecutionRequestAttributes) GetChildWorkflowOnly() (o bool) { if v != nil && v.ChildWorkflowOnly != nil { return *v.ChildWorkflowOnly } return } // IsSetChildWorkflowOnly returns true if ChildWorkflowOnly is not nil. func (v *CrossClusterCancelExecutionRequestAttributes) IsSetChildWorkflowOnly() bool { return v != nil && v.ChildWorkflowOnly != nil } type CrossClusterCancelExecutionResponseAttributes struct { } // ToWire translates a CrossClusterCancelExecutionResponseAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterCancelExecutionResponseAttributes) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CrossClusterCancelExecutionResponseAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterCancelExecutionResponseAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterCancelExecutionResponseAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterCancelExecutionResponseAttributes) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a CrossClusterCancelExecutionResponseAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterCancelExecutionResponseAttributes struct could not be encoded. func (v *CrossClusterCancelExecutionResponseAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a CrossClusterCancelExecutionResponseAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterCancelExecutionResponseAttributes struct could not be generated from the wire // representation. func (v *CrossClusterCancelExecutionResponseAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterCancelExecutionResponseAttributes // struct. func (v *CrossClusterCancelExecutionResponseAttributes) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("CrossClusterCancelExecutionResponseAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CrossClusterCancelExecutionResponseAttributes match the // provided CrossClusterCancelExecutionResponseAttributes. // // This function performs a deep comparison. func (v *CrossClusterCancelExecutionResponseAttributes) Equals(rhs *CrossClusterCancelExecutionResponseAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterCancelExecutionResponseAttributes. func (v *CrossClusterCancelExecutionResponseAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes struct { TargetDomainID *string `json:"targetDomainID,omitempty"` TargetWorkflowID *string `json:"targetWorkflowID,omitempty"` TargetRunID *string `json:"targetRunID,omitempty"` InitiatedEventID *int64 `json:"initiatedEventID,omitempty"` CompletionEvent *HistoryEvent `json:"completionEvent,omitempty"` } // ToWire translates a CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.TargetDomainID != nil { w, err = wire.NewValueString(*(v.TargetDomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TargetWorkflowID != nil { w, err = wire.NewValueString(*(v.TargetWorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TargetRunID != nil { w, err = wire.NewValueString(*(v.TargetRunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.InitiatedEventID != nil { w, err = wire.NewValueI64(*(v.InitiatedEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.CompletionEvent != nil { w, err = v.CompletionEvent.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _HistoryEvent_Read(w wire.Value) (*HistoryEvent, error) { var v HistoryEvent err := v.FromWire(w) return &v, err } // FromWire deserializes a CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetDomainID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetWorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetRunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.CompletionEvent, err = _HistoryEvent_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes struct could not be encoded. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TargetDomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetDomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetWorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetWorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetRunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompletionEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.CompletionEvent.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _HistoryEvent_Decode(sr stream.Reader) (*HistoryEvent, error) { var v HistoryEvent err := v.Decode(sr) return &v, err } // Decode deserializes a CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes struct could not be generated from the wire // representation. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetDomainID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetWorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetRunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.CompletionEvent, err = _HistoryEvent_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes // struct. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.TargetDomainID != nil { fields[i] = fmt.Sprintf("TargetDomainID: %v", *(v.TargetDomainID)) i++ } if v.TargetWorkflowID != nil { fields[i] = fmt.Sprintf("TargetWorkflowID: %v", *(v.TargetWorkflowID)) i++ } if v.TargetRunID != nil { fields[i] = fmt.Sprintf("TargetRunID: %v", *(v.TargetRunID)) i++ } if v.InitiatedEventID != nil { fields[i] = fmt.Sprintf("InitiatedEventID: %v", *(v.InitiatedEventID)) i++ } if v.CompletionEvent != nil { fields[i] = fmt.Sprintf("CompletionEvent: %v", v.CompletionEvent) i++ } return fmt.Sprintf("CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes match the // provided CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes. // // This function performs a deep comparison. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) Equals(rhs *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TargetDomainID, rhs.TargetDomainID) { return false } if !_String_EqualsPtr(v.TargetWorkflowID, rhs.TargetWorkflowID) { return false } if !_String_EqualsPtr(v.TargetRunID, rhs.TargetRunID) { return false } if !_I64_EqualsPtr(v.InitiatedEventID, rhs.InitiatedEventID) { return false } if !((v.CompletionEvent == nil && rhs.CompletionEvent == nil) || (v.CompletionEvent != nil && rhs.CompletionEvent != nil && v.CompletionEvent.Equals(rhs.CompletionEvent))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TargetDomainID != nil { enc.AddString("targetDomainID", *v.TargetDomainID) } if v.TargetWorkflowID != nil { enc.AddString("targetWorkflowID", *v.TargetWorkflowID) } if v.TargetRunID != nil { enc.AddString("targetRunID", *v.TargetRunID) } if v.InitiatedEventID != nil { enc.AddInt64("initiatedEventID", *v.InitiatedEventID) } if v.CompletionEvent != nil { err = multierr.Append(err, enc.AddObject("completionEvent", v.CompletionEvent)) } return err } // GetTargetDomainID returns the value of TargetDomainID if it is set or its // zero value if it is unset. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) GetTargetDomainID() (o string) { if v != nil && v.TargetDomainID != nil { return *v.TargetDomainID } return } // IsSetTargetDomainID returns true if TargetDomainID is not nil. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) IsSetTargetDomainID() bool { return v != nil && v.TargetDomainID != nil } // GetTargetWorkflowID returns the value of TargetWorkflowID if it is set or its // zero value if it is unset. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) GetTargetWorkflowID() (o string) { if v != nil && v.TargetWorkflowID != nil { return *v.TargetWorkflowID } return } // IsSetTargetWorkflowID returns true if TargetWorkflowID is not nil. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) IsSetTargetWorkflowID() bool { return v != nil && v.TargetWorkflowID != nil } // GetTargetRunID returns the value of TargetRunID if it is set or its // zero value if it is unset. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) GetTargetRunID() (o string) { if v != nil && v.TargetRunID != nil { return *v.TargetRunID } return } // IsSetTargetRunID returns true if TargetRunID is not nil. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) IsSetTargetRunID() bool { return v != nil && v.TargetRunID != nil } // GetInitiatedEventID returns the value of InitiatedEventID if it is set or its // zero value if it is unset. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) GetInitiatedEventID() (o int64) { if v != nil && v.InitiatedEventID != nil { return *v.InitiatedEventID } return } // IsSetInitiatedEventID returns true if InitiatedEventID is not nil. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) IsSetInitiatedEventID() bool { return v != nil && v.InitiatedEventID != nil } // GetCompletionEvent returns the value of CompletionEvent if it is set or its // zero value if it is unset. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) GetCompletionEvent() (o *HistoryEvent) { if v != nil && v.CompletionEvent != nil { return v.CompletionEvent } return } // IsSetCompletionEvent returns true if CompletionEvent is not nil. func (v *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) IsSetCompletionEvent() bool { return v != nil && v.CompletionEvent != nil } type CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes struct { } // ToWire translates a CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes struct could not be encoded. func (v *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes struct could not be generated from the wire // representation. func (v *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes // struct. func (v *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes match the // provided CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes. // // This function performs a deep comparison. func (v *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) Equals(rhs *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes. func (v *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type CrossClusterSignalExecutionRequestAttributes struct { TargetDomainID *string `json:"targetDomainID,omitempty"` TargetWorkflowID *string `json:"targetWorkflowID,omitempty"` TargetRunID *string `json:"targetRunID,omitempty"` RequestID *string `json:"requestID,omitempty"` InitiatedEventID *int64 `json:"initiatedEventID,omitempty"` ChildWorkflowOnly *bool `json:"childWorkflowOnly,omitempty"` SignalName *string `json:"signalName,omitempty"` SignalInput []byte `json:"signalInput,omitempty"` Control []byte `json:"control,omitempty"` } // ToWire translates a CrossClusterSignalExecutionRequestAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterSignalExecutionRequestAttributes) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.TargetDomainID != nil { w, err = wire.NewValueString(*(v.TargetDomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TargetWorkflowID != nil { w, err = wire.NewValueString(*(v.TargetWorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TargetRunID != nil { w, err = wire.NewValueString(*(v.TargetRunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.RequestID != nil { w, err = wire.NewValueString(*(v.RequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.InitiatedEventID != nil { w, err = wire.NewValueI64(*(v.InitiatedEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ChildWorkflowOnly != nil { w, err = wire.NewValueBool(*(v.ChildWorkflowOnly)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.SignalName != nil { w, err = wire.NewValueString(*(v.SignalName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.SignalInput != nil { w, err = wire.NewValueBinary(v.SignalInput), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CrossClusterSignalExecutionRequestAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterSignalExecutionRequestAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterSignalExecutionRequestAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterSignalExecutionRequestAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetDomainID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetWorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetRunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventID = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ChildWorkflowOnly = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SignalName = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TBinary { v.SignalInput, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a CrossClusterSignalExecutionRequestAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterSignalExecutionRequestAttributes struct could not be encoded. func (v *CrossClusterSignalExecutionRequestAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TargetDomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetDomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetWorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetWorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetRunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowOnly != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ChildWorkflowOnly)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SignalName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalInput != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.SignalInput); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CrossClusterSignalExecutionRequestAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterSignalExecutionRequestAttributes struct could not be generated from the wire // representation. func (v *CrossClusterSignalExecutionRequestAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetDomainID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetWorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetRunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventID = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ChildWorkflowOnly = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SignalName = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TBinary: v.SignalInput, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterSignalExecutionRequestAttributes // struct. func (v *CrossClusterSignalExecutionRequestAttributes) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.TargetDomainID != nil { fields[i] = fmt.Sprintf("TargetDomainID: %v", *(v.TargetDomainID)) i++ } if v.TargetWorkflowID != nil { fields[i] = fmt.Sprintf("TargetWorkflowID: %v", *(v.TargetWorkflowID)) i++ } if v.TargetRunID != nil { fields[i] = fmt.Sprintf("TargetRunID: %v", *(v.TargetRunID)) i++ } if v.RequestID != nil { fields[i] = fmt.Sprintf("RequestID: %v", *(v.RequestID)) i++ } if v.InitiatedEventID != nil { fields[i] = fmt.Sprintf("InitiatedEventID: %v", *(v.InitiatedEventID)) i++ } if v.ChildWorkflowOnly != nil { fields[i] = fmt.Sprintf("ChildWorkflowOnly: %v", *(v.ChildWorkflowOnly)) i++ } if v.SignalName != nil { fields[i] = fmt.Sprintf("SignalName: %v", *(v.SignalName)) i++ } if v.SignalInput != nil { fields[i] = fmt.Sprintf("SignalInput: %v", v.SignalInput) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } return fmt.Sprintf("CrossClusterSignalExecutionRequestAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CrossClusterSignalExecutionRequestAttributes match the // provided CrossClusterSignalExecutionRequestAttributes. // // This function performs a deep comparison. func (v *CrossClusterSignalExecutionRequestAttributes) Equals(rhs *CrossClusterSignalExecutionRequestAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TargetDomainID, rhs.TargetDomainID) { return false } if !_String_EqualsPtr(v.TargetWorkflowID, rhs.TargetWorkflowID) { return false } if !_String_EqualsPtr(v.TargetRunID, rhs.TargetRunID) { return false } if !_String_EqualsPtr(v.RequestID, rhs.RequestID) { return false } if !_I64_EqualsPtr(v.InitiatedEventID, rhs.InitiatedEventID) { return false } if !_Bool_EqualsPtr(v.ChildWorkflowOnly, rhs.ChildWorkflowOnly) { return false } if !_String_EqualsPtr(v.SignalName, rhs.SignalName) { return false } if !((v.SignalInput == nil && rhs.SignalInput == nil) || (v.SignalInput != nil && rhs.SignalInput != nil && bytes.Equal(v.SignalInput, rhs.SignalInput))) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterSignalExecutionRequestAttributes. func (v *CrossClusterSignalExecutionRequestAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TargetDomainID != nil { enc.AddString("targetDomainID", *v.TargetDomainID) } if v.TargetWorkflowID != nil { enc.AddString("targetWorkflowID", *v.TargetWorkflowID) } if v.TargetRunID != nil { enc.AddString("targetRunID", *v.TargetRunID) } if v.RequestID != nil { enc.AddString("requestID", *v.RequestID) } if v.InitiatedEventID != nil { enc.AddInt64("initiatedEventID", *v.InitiatedEventID) } if v.ChildWorkflowOnly != nil { enc.AddBool("childWorkflowOnly", *v.ChildWorkflowOnly) } if v.SignalName != nil { enc.AddString("signalName", *v.SignalName) } if v.SignalInput != nil { enc.AddString("signalInput", base64.StdEncoding.EncodeToString(v.SignalInput)) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } return err } // GetTargetDomainID returns the value of TargetDomainID if it is set or its // zero value if it is unset. func (v *CrossClusterSignalExecutionRequestAttributes) GetTargetDomainID() (o string) { if v != nil && v.TargetDomainID != nil { return *v.TargetDomainID } return } // IsSetTargetDomainID returns true if TargetDomainID is not nil. func (v *CrossClusterSignalExecutionRequestAttributes) IsSetTargetDomainID() bool { return v != nil && v.TargetDomainID != nil } // GetTargetWorkflowID returns the value of TargetWorkflowID if it is set or its // zero value if it is unset. func (v *CrossClusterSignalExecutionRequestAttributes) GetTargetWorkflowID() (o string) { if v != nil && v.TargetWorkflowID != nil { return *v.TargetWorkflowID } return } // IsSetTargetWorkflowID returns true if TargetWorkflowID is not nil. func (v *CrossClusterSignalExecutionRequestAttributes) IsSetTargetWorkflowID() bool { return v != nil && v.TargetWorkflowID != nil } // GetTargetRunID returns the value of TargetRunID if it is set or its // zero value if it is unset. func (v *CrossClusterSignalExecutionRequestAttributes) GetTargetRunID() (o string) { if v != nil && v.TargetRunID != nil { return *v.TargetRunID } return } // IsSetTargetRunID returns true if TargetRunID is not nil. func (v *CrossClusterSignalExecutionRequestAttributes) IsSetTargetRunID() bool { return v != nil && v.TargetRunID != nil } // GetRequestID returns the value of RequestID if it is set or its // zero value if it is unset. func (v *CrossClusterSignalExecutionRequestAttributes) GetRequestID() (o string) { if v != nil && v.RequestID != nil { return *v.RequestID } return } // IsSetRequestID returns true if RequestID is not nil. func (v *CrossClusterSignalExecutionRequestAttributes) IsSetRequestID() bool { return v != nil && v.RequestID != nil } // GetInitiatedEventID returns the value of InitiatedEventID if it is set or its // zero value if it is unset. func (v *CrossClusterSignalExecutionRequestAttributes) GetInitiatedEventID() (o int64) { if v != nil && v.InitiatedEventID != nil { return *v.InitiatedEventID } return } // IsSetInitiatedEventID returns true if InitiatedEventID is not nil. func (v *CrossClusterSignalExecutionRequestAttributes) IsSetInitiatedEventID() bool { return v != nil && v.InitiatedEventID != nil } // GetChildWorkflowOnly returns the value of ChildWorkflowOnly if it is set or its // zero value if it is unset. func (v *CrossClusterSignalExecutionRequestAttributes) GetChildWorkflowOnly() (o bool) { if v != nil && v.ChildWorkflowOnly != nil { return *v.ChildWorkflowOnly } return } // IsSetChildWorkflowOnly returns true if ChildWorkflowOnly is not nil. func (v *CrossClusterSignalExecutionRequestAttributes) IsSetChildWorkflowOnly() bool { return v != nil && v.ChildWorkflowOnly != nil } // GetSignalName returns the value of SignalName if it is set or its // zero value if it is unset. func (v *CrossClusterSignalExecutionRequestAttributes) GetSignalName() (o string) { if v != nil && v.SignalName != nil { return *v.SignalName } return } // IsSetSignalName returns true if SignalName is not nil. func (v *CrossClusterSignalExecutionRequestAttributes) IsSetSignalName() bool { return v != nil && v.SignalName != nil } // GetSignalInput returns the value of SignalInput if it is set or its // zero value if it is unset. func (v *CrossClusterSignalExecutionRequestAttributes) GetSignalInput() (o []byte) { if v != nil && v.SignalInput != nil { return v.SignalInput } return } // IsSetSignalInput returns true if SignalInput is not nil. func (v *CrossClusterSignalExecutionRequestAttributes) IsSetSignalInput() bool { return v != nil && v.SignalInput != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *CrossClusterSignalExecutionRequestAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *CrossClusterSignalExecutionRequestAttributes) IsSetControl() bool { return v != nil && v.Control != nil } type CrossClusterSignalExecutionResponseAttributes struct { } // ToWire translates a CrossClusterSignalExecutionResponseAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterSignalExecutionResponseAttributes) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CrossClusterSignalExecutionResponseAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterSignalExecutionResponseAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterSignalExecutionResponseAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterSignalExecutionResponseAttributes) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a CrossClusterSignalExecutionResponseAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterSignalExecutionResponseAttributes struct could not be encoded. func (v *CrossClusterSignalExecutionResponseAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a CrossClusterSignalExecutionResponseAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterSignalExecutionResponseAttributes struct could not be generated from the wire // representation. func (v *CrossClusterSignalExecutionResponseAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterSignalExecutionResponseAttributes // struct. func (v *CrossClusterSignalExecutionResponseAttributes) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("CrossClusterSignalExecutionResponseAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CrossClusterSignalExecutionResponseAttributes match the // provided CrossClusterSignalExecutionResponseAttributes. // // This function performs a deep comparison. func (v *CrossClusterSignalExecutionResponseAttributes) Equals(rhs *CrossClusterSignalExecutionResponseAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterSignalExecutionResponseAttributes. func (v *CrossClusterSignalExecutionResponseAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type CrossClusterStartChildExecutionRequestAttributes struct { TargetDomainID *string `json:"targetDomainID,omitempty"` RequestID *string `json:"requestID,omitempty"` InitiatedEventID *int64 `json:"initiatedEventID,omitempty"` InitiatedEventAttributes *StartChildWorkflowExecutionInitiatedEventAttributes `json:"initiatedEventAttributes,omitempty"` TargetRunID *string `json:"targetRunID,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` } type _Map_String_String_MapItemList map[string]string func (m _Map_String_String_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := wire.NewValueString(v), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_String_MapItemList) Size() int { return len(m) } func (_Map_String_String_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_String_MapItemList) ValueType() wire.Type { return wire.TBinary } func (_Map_String_String_MapItemList) Close() {} // ToWire translates a CrossClusterStartChildExecutionRequestAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterStartChildExecutionRequestAttributes) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.TargetDomainID != nil { w, err = wire.NewValueString(*(v.TargetDomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.RequestID != nil { w, err = wire.NewValueString(*(v.RequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.InitiatedEventID != nil { w, err = wire.NewValueI64(*(v.InitiatedEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.InitiatedEventAttributes != nil { w, err = v.InitiatedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.TargetRunID != nil { w, err = wire.NewValueString(*(v.TargetRunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.PartitionConfig != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.PartitionConfig)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StartChildWorkflowExecutionInitiatedEventAttributes_Read(w wire.Value) (*StartChildWorkflowExecutionInitiatedEventAttributes, error) { var v StartChildWorkflowExecutionInitiatedEventAttributes err := v.FromWire(w) return &v, err } func _Map_String_String_Read(m wire.MapItemList) (map[string]string, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TBinary { return nil, nil } o := make(map[string]string, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := x.Value.GetString(), error(nil) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a CrossClusterStartChildExecutionRequestAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterStartChildExecutionRequestAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterStartChildExecutionRequestAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterStartChildExecutionRequestAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetDomainID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.InitiatedEventAttributes, err = _StartChildWorkflowExecutionInitiatedEventAttributes_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetRunID = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TMap { v.PartitionConfig, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_String_Encode(val map[string]string, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TBinary, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if err := sw.WriteString(k); err != nil { return err } if err := sw.WriteString(v); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a CrossClusterStartChildExecutionRequestAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterStartChildExecutionRequestAttributes struct could not be encoded. func (v *CrossClusterStartChildExecutionRequestAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TargetDomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetDomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.InitiatedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetRunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PartitionConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.PartitionConfig, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _StartChildWorkflowExecutionInitiatedEventAttributes_Decode(sr stream.Reader) (*StartChildWorkflowExecutionInitiatedEventAttributes, error) { var v StartChildWorkflowExecutionInitiatedEventAttributes err := v.Decode(sr) return &v, err } func _Map_String_String_Decode(sr stream.Reader) (map[string]string, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TBinary { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]string, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := sr.ReadString() if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a CrossClusterStartChildExecutionRequestAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterStartChildExecutionRequestAttributes struct could not be generated from the wire // representation. func (v *CrossClusterStartChildExecutionRequestAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetDomainID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.InitiatedEventAttributes, err = _StartChildWorkflowExecutionInitiatedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetRunID = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TMap: v.PartitionConfig, err = _Map_String_String_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterStartChildExecutionRequestAttributes // struct. func (v *CrossClusterStartChildExecutionRequestAttributes) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.TargetDomainID != nil { fields[i] = fmt.Sprintf("TargetDomainID: %v", *(v.TargetDomainID)) i++ } if v.RequestID != nil { fields[i] = fmt.Sprintf("RequestID: %v", *(v.RequestID)) i++ } if v.InitiatedEventID != nil { fields[i] = fmt.Sprintf("InitiatedEventID: %v", *(v.InitiatedEventID)) i++ } if v.InitiatedEventAttributes != nil { fields[i] = fmt.Sprintf("InitiatedEventAttributes: %v", v.InitiatedEventAttributes) i++ } if v.TargetRunID != nil { fields[i] = fmt.Sprintf("TargetRunID: %v", *(v.TargetRunID)) i++ } if v.PartitionConfig != nil { fields[i] = fmt.Sprintf("PartitionConfig: %v", v.PartitionConfig) i++ } return fmt.Sprintf("CrossClusterStartChildExecutionRequestAttributes{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_String_Equals(lhs, rhs map[string]string) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !(lv == rv) { return false } } return true } // Equals returns true if all the fields of this CrossClusterStartChildExecutionRequestAttributes match the // provided CrossClusterStartChildExecutionRequestAttributes. // // This function performs a deep comparison. func (v *CrossClusterStartChildExecutionRequestAttributes) Equals(rhs *CrossClusterStartChildExecutionRequestAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TargetDomainID, rhs.TargetDomainID) { return false } if !_String_EqualsPtr(v.RequestID, rhs.RequestID) { return false } if !_I64_EqualsPtr(v.InitiatedEventID, rhs.InitiatedEventID) { return false } if !((v.InitiatedEventAttributes == nil && rhs.InitiatedEventAttributes == nil) || (v.InitiatedEventAttributes != nil && rhs.InitiatedEventAttributes != nil && v.InitiatedEventAttributes.Equals(rhs.InitiatedEventAttributes))) { return false } if !_String_EqualsPtr(v.TargetRunID, rhs.TargetRunID) { return false } if !((v.PartitionConfig == nil && rhs.PartitionConfig == nil) || (v.PartitionConfig != nil && rhs.PartitionConfig != nil && _Map_String_String_Equals(v.PartitionConfig, rhs.PartitionConfig))) { return false } return true } type _Map_String_String_Zapper map[string]string // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_String_Zapper. func (m _Map_String_String_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { enc.AddString((string)(k), v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterStartChildExecutionRequestAttributes. func (v *CrossClusterStartChildExecutionRequestAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TargetDomainID != nil { enc.AddString("targetDomainID", *v.TargetDomainID) } if v.RequestID != nil { enc.AddString("requestID", *v.RequestID) } if v.InitiatedEventID != nil { enc.AddInt64("initiatedEventID", *v.InitiatedEventID) } if v.InitiatedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("initiatedEventAttributes", v.InitiatedEventAttributes)) } if v.TargetRunID != nil { enc.AddString("targetRunID", *v.TargetRunID) } if v.PartitionConfig != nil { err = multierr.Append(err, enc.AddObject("partitionConfig", (_Map_String_String_Zapper)(v.PartitionConfig))) } return err } // GetTargetDomainID returns the value of TargetDomainID if it is set or its // zero value if it is unset. func (v *CrossClusterStartChildExecutionRequestAttributes) GetTargetDomainID() (o string) { if v != nil && v.TargetDomainID != nil { return *v.TargetDomainID } return } // IsSetTargetDomainID returns true if TargetDomainID is not nil. func (v *CrossClusterStartChildExecutionRequestAttributes) IsSetTargetDomainID() bool { return v != nil && v.TargetDomainID != nil } // GetRequestID returns the value of RequestID if it is set or its // zero value if it is unset. func (v *CrossClusterStartChildExecutionRequestAttributes) GetRequestID() (o string) { if v != nil && v.RequestID != nil { return *v.RequestID } return } // IsSetRequestID returns true if RequestID is not nil. func (v *CrossClusterStartChildExecutionRequestAttributes) IsSetRequestID() bool { return v != nil && v.RequestID != nil } // GetInitiatedEventID returns the value of InitiatedEventID if it is set or its // zero value if it is unset. func (v *CrossClusterStartChildExecutionRequestAttributes) GetInitiatedEventID() (o int64) { if v != nil && v.InitiatedEventID != nil { return *v.InitiatedEventID } return } // IsSetInitiatedEventID returns true if InitiatedEventID is not nil. func (v *CrossClusterStartChildExecutionRequestAttributes) IsSetInitiatedEventID() bool { return v != nil && v.InitiatedEventID != nil } // GetInitiatedEventAttributes returns the value of InitiatedEventAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterStartChildExecutionRequestAttributes) GetInitiatedEventAttributes() (o *StartChildWorkflowExecutionInitiatedEventAttributes) { if v != nil && v.InitiatedEventAttributes != nil { return v.InitiatedEventAttributes } return } // IsSetInitiatedEventAttributes returns true if InitiatedEventAttributes is not nil. func (v *CrossClusterStartChildExecutionRequestAttributes) IsSetInitiatedEventAttributes() bool { return v != nil && v.InitiatedEventAttributes != nil } // GetTargetRunID returns the value of TargetRunID if it is set or its // zero value if it is unset. func (v *CrossClusterStartChildExecutionRequestAttributes) GetTargetRunID() (o string) { if v != nil && v.TargetRunID != nil { return *v.TargetRunID } return } // IsSetTargetRunID returns true if TargetRunID is not nil. func (v *CrossClusterStartChildExecutionRequestAttributes) IsSetTargetRunID() bool { return v != nil && v.TargetRunID != nil } // GetPartitionConfig returns the value of PartitionConfig if it is set or its // zero value if it is unset. func (v *CrossClusterStartChildExecutionRequestAttributes) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // IsSetPartitionConfig returns true if PartitionConfig is not nil. func (v *CrossClusterStartChildExecutionRequestAttributes) IsSetPartitionConfig() bool { return v != nil && v.PartitionConfig != nil } type CrossClusterStartChildExecutionResponseAttributes struct { RunID *string `json:"runID,omitempty"` } // ToWire translates a CrossClusterStartChildExecutionResponseAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterStartChildExecutionResponseAttributes) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.RunID != nil { w, err = wire.NewValueString(*(v.RunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CrossClusterStartChildExecutionResponseAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterStartChildExecutionResponseAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterStartChildExecutionResponseAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterStartChildExecutionResponseAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunID = &x if err != nil { return err } } } } return nil } // Encode serializes a CrossClusterStartChildExecutionResponseAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterStartChildExecutionResponseAttributes struct could not be encoded. func (v *CrossClusterStartChildExecutionResponseAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a CrossClusterStartChildExecutionResponseAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterStartChildExecutionResponseAttributes struct could not be generated from the wire // representation. func (v *CrossClusterStartChildExecutionResponseAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterStartChildExecutionResponseAttributes // struct. func (v *CrossClusterStartChildExecutionResponseAttributes) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", *(v.RunID)) i++ } return fmt.Sprintf("CrossClusterStartChildExecutionResponseAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CrossClusterStartChildExecutionResponseAttributes match the // provided CrossClusterStartChildExecutionResponseAttributes. // // This function performs a deep comparison. func (v *CrossClusterStartChildExecutionResponseAttributes) Equals(rhs *CrossClusterStartChildExecutionResponseAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.RunID, rhs.RunID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterStartChildExecutionResponseAttributes. func (v *CrossClusterStartChildExecutionResponseAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.RunID != nil { enc.AddString("runID", *v.RunID) } return err } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *CrossClusterStartChildExecutionResponseAttributes) GetRunID() (o string) { if v != nil && v.RunID != nil { return *v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *CrossClusterStartChildExecutionResponseAttributes) IsSetRunID() bool { return v != nil && v.RunID != nil } type CrossClusterTaskFailedCause int32 const ( CrossClusterTaskFailedCauseDomainNotActive CrossClusterTaskFailedCause = 0 CrossClusterTaskFailedCauseDomainNotExists CrossClusterTaskFailedCause = 1 CrossClusterTaskFailedCauseWorkflowAlreadyRunning CrossClusterTaskFailedCause = 2 CrossClusterTaskFailedCauseWorkflowNotExists CrossClusterTaskFailedCause = 3 CrossClusterTaskFailedCauseWorkflowAlreadyCompleted CrossClusterTaskFailedCause = 4 CrossClusterTaskFailedCauseUncategorized CrossClusterTaskFailedCause = 5 ) // CrossClusterTaskFailedCause_Values returns all recognized values of CrossClusterTaskFailedCause. func CrossClusterTaskFailedCause_Values() []CrossClusterTaskFailedCause { return []CrossClusterTaskFailedCause{ CrossClusterTaskFailedCauseDomainNotActive, CrossClusterTaskFailedCauseDomainNotExists, CrossClusterTaskFailedCauseWorkflowAlreadyRunning, CrossClusterTaskFailedCauseWorkflowNotExists, CrossClusterTaskFailedCauseWorkflowAlreadyCompleted, CrossClusterTaskFailedCauseUncategorized, } } // UnmarshalText tries to decode CrossClusterTaskFailedCause from a byte slice // containing its name. // // var v CrossClusterTaskFailedCause // err := v.UnmarshalText([]byte("DOMAIN_NOT_ACTIVE")) func (v *CrossClusterTaskFailedCause) UnmarshalText(value []byte) error { switch s := string(value); s { case "DOMAIN_NOT_ACTIVE": *v = CrossClusterTaskFailedCauseDomainNotActive return nil case "DOMAIN_NOT_EXISTS": *v = CrossClusterTaskFailedCauseDomainNotExists return nil case "WORKFLOW_ALREADY_RUNNING": *v = CrossClusterTaskFailedCauseWorkflowAlreadyRunning return nil case "WORKFLOW_NOT_EXISTS": *v = CrossClusterTaskFailedCauseWorkflowNotExists return nil case "WORKFLOW_ALREADY_COMPLETED": *v = CrossClusterTaskFailedCauseWorkflowAlreadyCompleted return nil case "UNCATEGORIZED": *v = CrossClusterTaskFailedCauseUncategorized return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "CrossClusterTaskFailedCause", err) } *v = CrossClusterTaskFailedCause(val) return nil } } // MarshalText encodes CrossClusterTaskFailedCause to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v CrossClusterTaskFailedCause) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("DOMAIN_NOT_ACTIVE"), nil case 1: return []byte("DOMAIN_NOT_EXISTS"), nil case 2: return []byte("WORKFLOW_ALREADY_RUNNING"), nil case 3: return []byte("WORKFLOW_NOT_EXISTS"), nil case 4: return []byte("WORKFLOW_ALREADY_COMPLETED"), nil case 5: return []byte("UNCATEGORIZED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterTaskFailedCause. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v CrossClusterTaskFailedCause) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "DOMAIN_NOT_ACTIVE") case 1: enc.AddString("name", "DOMAIN_NOT_EXISTS") case 2: enc.AddString("name", "WORKFLOW_ALREADY_RUNNING") case 3: enc.AddString("name", "WORKFLOW_NOT_EXISTS") case 4: enc.AddString("name", "WORKFLOW_ALREADY_COMPLETED") case 5: enc.AddString("name", "UNCATEGORIZED") } return nil } // Ptr returns a pointer to this enum value. func (v CrossClusterTaskFailedCause) Ptr() *CrossClusterTaskFailedCause { return &v } // Encode encodes CrossClusterTaskFailedCause directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v CrossClusterTaskFailedCause // return v.Encode(sWriter) func (v CrossClusterTaskFailedCause) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates CrossClusterTaskFailedCause into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v CrossClusterTaskFailedCause) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes CrossClusterTaskFailedCause from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return CrossClusterTaskFailedCause(0), err // } // // var v CrossClusterTaskFailedCause // if err := v.FromWire(x); err != nil { // return CrossClusterTaskFailedCause(0), err // } // return v, nil func (v *CrossClusterTaskFailedCause) FromWire(w wire.Value) error { *v = (CrossClusterTaskFailedCause)(w.GetI32()) return nil } // Decode reads off the encoded CrossClusterTaskFailedCause directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v CrossClusterTaskFailedCause // if err := v.Decode(sReader); err != nil { // return CrossClusterTaskFailedCause(0), err // } // return v, nil func (v *CrossClusterTaskFailedCause) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (CrossClusterTaskFailedCause)(i) return nil } // String returns a readable string representation of CrossClusterTaskFailedCause. func (v CrossClusterTaskFailedCause) String() string { w := int32(v) switch w { case 0: return "DOMAIN_NOT_ACTIVE" case 1: return "DOMAIN_NOT_EXISTS" case 2: return "WORKFLOW_ALREADY_RUNNING" case 3: return "WORKFLOW_NOT_EXISTS" case 4: return "WORKFLOW_ALREADY_COMPLETED" case 5: return "UNCATEGORIZED" } return fmt.Sprintf("CrossClusterTaskFailedCause(%d)", w) } // Equals returns true if this CrossClusterTaskFailedCause value matches the provided // value. func (v CrossClusterTaskFailedCause) Equals(rhs CrossClusterTaskFailedCause) bool { return v == rhs } // MarshalJSON serializes CrossClusterTaskFailedCause into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v CrossClusterTaskFailedCause) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"DOMAIN_NOT_ACTIVE\""), nil case 1: return ([]byte)("\"DOMAIN_NOT_EXISTS\""), nil case 2: return ([]byte)("\"WORKFLOW_ALREADY_RUNNING\""), nil case 3: return ([]byte)("\"WORKFLOW_NOT_EXISTS\""), nil case 4: return ([]byte)("\"WORKFLOW_ALREADY_COMPLETED\""), nil case 5: return ([]byte)("\"UNCATEGORIZED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode CrossClusterTaskFailedCause from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *CrossClusterTaskFailedCause) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "CrossClusterTaskFailedCause") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "CrossClusterTaskFailedCause") } *v = (CrossClusterTaskFailedCause)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "CrossClusterTaskFailedCause") } } type CrossClusterTaskInfo struct { DomainID *string `json:"domainID,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID *string `json:"runID,omitempty"` TaskType *CrossClusterTaskType `json:"taskType,omitempty"` TaskState *int16 `json:"taskState,omitempty"` TaskID *int64 `json:"taskID,omitempty"` VisibilityTimestamp *int64 `json:"visibilityTimestamp,omitempty"` } // ToWire translates a CrossClusterTaskInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterTaskInfo) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.DomainID != nil { w, err = wire.NewValueString(*(v.DomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueString(*(v.RunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.TaskType != nil { w, err = v.TaskType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.TaskState != nil { w, err = wire.NewValueI16(*(v.TaskState)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.TaskID != nil { w, err = wire.NewValueI64(*(v.TaskID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.VisibilityTimestamp != nil { w, err = wire.NewValueI64(*(v.VisibilityTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CrossClusterTaskType_Read(w wire.Value) (CrossClusterTaskType, error) { var v CrossClusterTaskType err := v.FromWire(w) return v, err } // FromWire deserializes a CrossClusterTaskInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterTaskInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterTaskInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterTaskInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x CrossClusterTaskType x, err = _CrossClusterTaskType_Read(field.Value) v.TaskType = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.TaskState = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskID = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.VisibilityTimestamp = &x if err != nil { return err } } } } return nil } // Encode serializes a CrossClusterTaskInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterTaskInfo struct could not be encoded. func (v *CrossClusterTaskInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := v.TaskType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskState != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.TaskState)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.VisibilityTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CrossClusterTaskType_Decode(sr stream.Reader) (CrossClusterTaskType, error) { var v CrossClusterTaskType err := v.Decode(sr) return v, err } // Decode deserializes a CrossClusterTaskInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterTaskInfo struct could not be generated from the wire // representation. func (v *CrossClusterTaskInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x CrossClusterTaskType x, err = _CrossClusterTaskType_Decode(sr) v.TaskType = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.TaskState = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskID = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.VisibilityTimestamp = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterTaskInfo // struct. func (v *CrossClusterTaskInfo) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", *(v.DomainID)) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", *(v.RunID)) i++ } if v.TaskType != nil { fields[i] = fmt.Sprintf("TaskType: %v", *(v.TaskType)) i++ } if v.TaskState != nil { fields[i] = fmt.Sprintf("TaskState: %v", *(v.TaskState)) i++ } if v.TaskID != nil { fields[i] = fmt.Sprintf("TaskID: %v", *(v.TaskID)) i++ } if v.VisibilityTimestamp != nil { fields[i] = fmt.Sprintf("VisibilityTimestamp: %v", *(v.VisibilityTimestamp)) i++ } return fmt.Sprintf("CrossClusterTaskInfo{%v}", strings.Join(fields[:i], ", ")) } func _CrossClusterTaskType_EqualsPtr(lhs, rhs *CrossClusterTaskType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } func _I16_EqualsPtr(lhs, rhs *int16) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this CrossClusterTaskInfo match the // provided CrossClusterTaskInfo. // // This function performs a deep comparison. func (v *CrossClusterTaskInfo) Equals(rhs *CrossClusterTaskInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainID, rhs.DomainID) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !_String_EqualsPtr(v.RunID, rhs.RunID) { return false } if !_CrossClusterTaskType_EqualsPtr(v.TaskType, rhs.TaskType) { return false } if !_I16_EqualsPtr(v.TaskState, rhs.TaskState) { return false } if !_I64_EqualsPtr(v.TaskID, rhs.TaskID) { return false } if !_I64_EqualsPtr(v.VisibilityTimestamp, rhs.VisibilityTimestamp) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterTaskInfo. func (v *CrossClusterTaskInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainID != nil { enc.AddString("domainID", *v.DomainID) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", *v.RunID) } if v.TaskType != nil { err = multierr.Append(err, enc.AddObject("taskType", *v.TaskType)) } if v.TaskState != nil { enc.AddInt16("taskState", *v.TaskState) } if v.TaskID != nil { enc.AddInt64("taskID", *v.TaskID) } if v.VisibilityTimestamp != nil { enc.AddInt64("visibilityTimestamp", *v.VisibilityTimestamp) } return err } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *CrossClusterTaskInfo) GetDomainID() (o string) { if v != nil && v.DomainID != nil { return *v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *CrossClusterTaskInfo) IsSetDomainID() bool { return v != nil && v.DomainID != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *CrossClusterTaskInfo) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *CrossClusterTaskInfo) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *CrossClusterTaskInfo) GetRunID() (o string) { if v != nil && v.RunID != nil { return *v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *CrossClusterTaskInfo) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetTaskType returns the value of TaskType if it is set or its // zero value if it is unset. func (v *CrossClusterTaskInfo) GetTaskType() (o CrossClusterTaskType) { if v != nil && v.TaskType != nil { return *v.TaskType } return } // IsSetTaskType returns true if TaskType is not nil. func (v *CrossClusterTaskInfo) IsSetTaskType() bool { return v != nil && v.TaskType != nil } // GetTaskState returns the value of TaskState if it is set or its // zero value if it is unset. func (v *CrossClusterTaskInfo) GetTaskState() (o int16) { if v != nil && v.TaskState != nil { return *v.TaskState } return } // IsSetTaskState returns true if TaskState is not nil. func (v *CrossClusterTaskInfo) IsSetTaskState() bool { return v != nil && v.TaskState != nil } // GetTaskID returns the value of TaskID if it is set or its // zero value if it is unset. func (v *CrossClusterTaskInfo) GetTaskID() (o int64) { if v != nil && v.TaskID != nil { return *v.TaskID } return } // IsSetTaskID returns true if TaskID is not nil. func (v *CrossClusterTaskInfo) IsSetTaskID() bool { return v != nil && v.TaskID != nil } // GetVisibilityTimestamp returns the value of VisibilityTimestamp if it is set or its // zero value if it is unset. func (v *CrossClusterTaskInfo) GetVisibilityTimestamp() (o int64) { if v != nil && v.VisibilityTimestamp != nil { return *v.VisibilityTimestamp } return } // IsSetVisibilityTimestamp returns true if VisibilityTimestamp is not nil. func (v *CrossClusterTaskInfo) IsSetVisibilityTimestamp() bool { return v != nil && v.VisibilityTimestamp != nil } type CrossClusterTaskRequest struct { TaskInfo *CrossClusterTaskInfo `json:"taskInfo,omitempty"` StartChildExecutionAttributes *CrossClusterStartChildExecutionRequestAttributes `json:"startChildExecutionAttributes,omitempty"` CancelExecutionAttributes *CrossClusterCancelExecutionRequestAttributes `json:"cancelExecutionAttributes,omitempty"` SignalExecutionAttributes *CrossClusterSignalExecutionRequestAttributes `json:"signalExecutionAttributes,omitempty"` RecordChildWorkflowExecutionCompleteAttributes *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes `json:"recordChildWorkflowExecutionCompleteAttributes,omitempty"` ApplyParentClosePolicyAttributes *CrossClusterApplyParentClosePolicyRequestAttributes `json:"applyParentClosePolicyAttributes,omitempty"` } // ToWire translates a CrossClusterTaskRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterTaskRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.TaskInfo != nil { w, err = v.TaskInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartChildExecutionAttributes != nil { w, err = v.StartChildExecutionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.CancelExecutionAttributes != nil { w, err = v.CancelExecutionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.SignalExecutionAttributes != nil { w, err = v.SignalExecutionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.RecordChildWorkflowExecutionCompleteAttributes != nil { w, err = v.RecordChildWorkflowExecutionCompleteAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ApplyParentClosePolicyAttributes != nil { w, err = v.ApplyParentClosePolicyAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CrossClusterTaskInfo_Read(w wire.Value) (*CrossClusterTaskInfo, error) { var v CrossClusterTaskInfo err := v.FromWire(w) return &v, err } func _CrossClusterStartChildExecutionRequestAttributes_Read(w wire.Value) (*CrossClusterStartChildExecutionRequestAttributes, error) { var v CrossClusterStartChildExecutionRequestAttributes err := v.FromWire(w) return &v, err } func _CrossClusterCancelExecutionRequestAttributes_Read(w wire.Value) (*CrossClusterCancelExecutionRequestAttributes, error) { var v CrossClusterCancelExecutionRequestAttributes err := v.FromWire(w) return &v, err } func _CrossClusterSignalExecutionRequestAttributes_Read(w wire.Value) (*CrossClusterSignalExecutionRequestAttributes, error) { var v CrossClusterSignalExecutionRequestAttributes err := v.FromWire(w) return &v, err } func _CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes_Read(w wire.Value) (*CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes, error) { var v CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes err := v.FromWire(w) return &v, err } func _CrossClusterApplyParentClosePolicyRequestAttributes_Read(w wire.Value) (*CrossClusterApplyParentClosePolicyRequestAttributes, error) { var v CrossClusterApplyParentClosePolicyRequestAttributes err := v.FromWire(w) return &v, err } // FromWire deserializes a CrossClusterTaskRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterTaskRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterTaskRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterTaskRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.TaskInfo, err = _CrossClusterTaskInfo_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.StartChildExecutionAttributes, err = _CrossClusterStartChildExecutionRequestAttributes_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.CancelExecutionAttributes, err = _CrossClusterCancelExecutionRequestAttributes_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.SignalExecutionAttributes, err = _CrossClusterSignalExecutionRequestAttributes_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.RecordChildWorkflowExecutionCompleteAttributes, err = _CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.ApplyParentClosePolicyAttributes, err = _CrossClusterApplyParentClosePolicyRequestAttributes_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a CrossClusterTaskRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterTaskRequest struct could not be encoded. func (v *CrossClusterTaskRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.TaskInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartChildExecutionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.StartChildExecutionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelExecutionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.CancelExecutionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalExecutionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.SignalExecutionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RecordChildWorkflowExecutionCompleteAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.RecordChildWorkflowExecutionCompleteAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ApplyParentClosePolicyAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.ApplyParentClosePolicyAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CrossClusterTaskInfo_Decode(sr stream.Reader) (*CrossClusterTaskInfo, error) { var v CrossClusterTaskInfo err := v.Decode(sr) return &v, err } func _CrossClusterStartChildExecutionRequestAttributes_Decode(sr stream.Reader) (*CrossClusterStartChildExecutionRequestAttributes, error) { var v CrossClusterStartChildExecutionRequestAttributes err := v.Decode(sr) return &v, err } func _CrossClusterCancelExecutionRequestAttributes_Decode(sr stream.Reader) (*CrossClusterCancelExecutionRequestAttributes, error) { var v CrossClusterCancelExecutionRequestAttributes err := v.Decode(sr) return &v, err } func _CrossClusterSignalExecutionRequestAttributes_Decode(sr stream.Reader) (*CrossClusterSignalExecutionRequestAttributes, error) { var v CrossClusterSignalExecutionRequestAttributes err := v.Decode(sr) return &v, err } func _CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes_Decode(sr stream.Reader) (*CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes, error) { var v CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes err := v.Decode(sr) return &v, err } func _CrossClusterApplyParentClosePolicyRequestAttributes_Decode(sr stream.Reader) (*CrossClusterApplyParentClosePolicyRequestAttributes, error) { var v CrossClusterApplyParentClosePolicyRequestAttributes err := v.Decode(sr) return &v, err } // Decode deserializes a CrossClusterTaskRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterTaskRequest struct could not be generated from the wire // representation. func (v *CrossClusterTaskRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.TaskInfo, err = _CrossClusterTaskInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.StartChildExecutionAttributes, err = _CrossClusterStartChildExecutionRequestAttributes_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.CancelExecutionAttributes, err = _CrossClusterCancelExecutionRequestAttributes_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.SignalExecutionAttributes, err = _CrossClusterSignalExecutionRequestAttributes_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.RecordChildWorkflowExecutionCompleteAttributes, err = _CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.ApplyParentClosePolicyAttributes, err = _CrossClusterApplyParentClosePolicyRequestAttributes_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterTaskRequest // struct. func (v *CrossClusterTaskRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.TaskInfo != nil { fields[i] = fmt.Sprintf("TaskInfo: %v", v.TaskInfo) i++ } if v.StartChildExecutionAttributes != nil { fields[i] = fmt.Sprintf("StartChildExecutionAttributes: %v", v.StartChildExecutionAttributes) i++ } if v.CancelExecutionAttributes != nil { fields[i] = fmt.Sprintf("CancelExecutionAttributes: %v", v.CancelExecutionAttributes) i++ } if v.SignalExecutionAttributes != nil { fields[i] = fmt.Sprintf("SignalExecutionAttributes: %v", v.SignalExecutionAttributes) i++ } if v.RecordChildWorkflowExecutionCompleteAttributes != nil { fields[i] = fmt.Sprintf("RecordChildWorkflowExecutionCompleteAttributes: %v", v.RecordChildWorkflowExecutionCompleteAttributes) i++ } if v.ApplyParentClosePolicyAttributes != nil { fields[i] = fmt.Sprintf("ApplyParentClosePolicyAttributes: %v", v.ApplyParentClosePolicyAttributes) i++ } return fmt.Sprintf("CrossClusterTaskRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CrossClusterTaskRequest match the // provided CrossClusterTaskRequest. // // This function performs a deep comparison. func (v *CrossClusterTaskRequest) Equals(rhs *CrossClusterTaskRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskInfo == nil && rhs.TaskInfo == nil) || (v.TaskInfo != nil && rhs.TaskInfo != nil && v.TaskInfo.Equals(rhs.TaskInfo))) { return false } if !((v.StartChildExecutionAttributes == nil && rhs.StartChildExecutionAttributes == nil) || (v.StartChildExecutionAttributes != nil && rhs.StartChildExecutionAttributes != nil && v.StartChildExecutionAttributes.Equals(rhs.StartChildExecutionAttributes))) { return false } if !((v.CancelExecutionAttributes == nil && rhs.CancelExecutionAttributes == nil) || (v.CancelExecutionAttributes != nil && rhs.CancelExecutionAttributes != nil && v.CancelExecutionAttributes.Equals(rhs.CancelExecutionAttributes))) { return false } if !((v.SignalExecutionAttributes == nil && rhs.SignalExecutionAttributes == nil) || (v.SignalExecutionAttributes != nil && rhs.SignalExecutionAttributes != nil && v.SignalExecutionAttributes.Equals(rhs.SignalExecutionAttributes))) { return false } if !((v.RecordChildWorkflowExecutionCompleteAttributes == nil && rhs.RecordChildWorkflowExecutionCompleteAttributes == nil) || (v.RecordChildWorkflowExecutionCompleteAttributes != nil && rhs.RecordChildWorkflowExecutionCompleteAttributes != nil && v.RecordChildWorkflowExecutionCompleteAttributes.Equals(rhs.RecordChildWorkflowExecutionCompleteAttributes))) { return false } if !((v.ApplyParentClosePolicyAttributes == nil && rhs.ApplyParentClosePolicyAttributes == nil) || (v.ApplyParentClosePolicyAttributes != nil && rhs.ApplyParentClosePolicyAttributes != nil && v.ApplyParentClosePolicyAttributes.Equals(rhs.ApplyParentClosePolicyAttributes))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterTaskRequest. func (v *CrossClusterTaskRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskInfo != nil { err = multierr.Append(err, enc.AddObject("taskInfo", v.TaskInfo)) } if v.StartChildExecutionAttributes != nil { err = multierr.Append(err, enc.AddObject("startChildExecutionAttributes", v.StartChildExecutionAttributes)) } if v.CancelExecutionAttributes != nil { err = multierr.Append(err, enc.AddObject("cancelExecutionAttributes", v.CancelExecutionAttributes)) } if v.SignalExecutionAttributes != nil { err = multierr.Append(err, enc.AddObject("signalExecutionAttributes", v.SignalExecutionAttributes)) } if v.RecordChildWorkflowExecutionCompleteAttributes != nil { err = multierr.Append(err, enc.AddObject("recordChildWorkflowExecutionCompleteAttributes", v.RecordChildWorkflowExecutionCompleteAttributes)) } if v.ApplyParentClosePolicyAttributes != nil { err = multierr.Append(err, enc.AddObject("applyParentClosePolicyAttributes", v.ApplyParentClosePolicyAttributes)) } return err } // GetTaskInfo returns the value of TaskInfo if it is set or its // zero value if it is unset. func (v *CrossClusterTaskRequest) GetTaskInfo() (o *CrossClusterTaskInfo) { if v != nil && v.TaskInfo != nil { return v.TaskInfo } return } // IsSetTaskInfo returns true if TaskInfo is not nil. func (v *CrossClusterTaskRequest) IsSetTaskInfo() bool { return v != nil && v.TaskInfo != nil } // GetStartChildExecutionAttributes returns the value of StartChildExecutionAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterTaskRequest) GetStartChildExecutionAttributes() (o *CrossClusterStartChildExecutionRequestAttributes) { if v != nil && v.StartChildExecutionAttributes != nil { return v.StartChildExecutionAttributes } return } // IsSetStartChildExecutionAttributes returns true if StartChildExecutionAttributes is not nil. func (v *CrossClusterTaskRequest) IsSetStartChildExecutionAttributes() bool { return v != nil && v.StartChildExecutionAttributes != nil } // GetCancelExecutionAttributes returns the value of CancelExecutionAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterTaskRequest) GetCancelExecutionAttributes() (o *CrossClusterCancelExecutionRequestAttributes) { if v != nil && v.CancelExecutionAttributes != nil { return v.CancelExecutionAttributes } return } // IsSetCancelExecutionAttributes returns true if CancelExecutionAttributes is not nil. func (v *CrossClusterTaskRequest) IsSetCancelExecutionAttributes() bool { return v != nil && v.CancelExecutionAttributes != nil } // GetSignalExecutionAttributes returns the value of SignalExecutionAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterTaskRequest) GetSignalExecutionAttributes() (o *CrossClusterSignalExecutionRequestAttributes) { if v != nil && v.SignalExecutionAttributes != nil { return v.SignalExecutionAttributes } return } // IsSetSignalExecutionAttributes returns true if SignalExecutionAttributes is not nil. func (v *CrossClusterTaskRequest) IsSetSignalExecutionAttributes() bool { return v != nil && v.SignalExecutionAttributes != nil } // GetRecordChildWorkflowExecutionCompleteAttributes returns the value of RecordChildWorkflowExecutionCompleteAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterTaskRequest) GetRecordChildWorkflowExecutionCompleteAttributes() (o *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) { if v != nil && v.RecordChildWorkflowExecutionCompleteAttributes != nil { return v.RecordChildWorkflowExecutionCompleteAttributes } return } // IsSetRecordChildWorkflowExecutionCompleteAttributes returns true if RecordChildWorkflowExecutionCompleteAttributes is not nil. func (v *CrossClusterTaskRequest) IsSetRecordChildWorkflowExecutionCompleteAttributes() bool { return v != nil && v.RecordChildWorkflowExecutionCompleteAttributes != nil } // GetApplyParentClosePolicyAttributes returns the value of ApplyParentClosePolicyAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterTaskRequest) GetApplyParentClosePolicyAttributes() (o *CrossClusterApplyParentClosePolicyRequestAttributes) { if v != nil && v.ApplyParentClosePolicyAttributes != nil { return v.ApplyParentClosePolicyAttributes } return } // IsSetApplyParentClosePolicyAttributes returns true if ApplyParentClosePolicyAttributes is not nil. func (v *CrossClusterTaskRequest) IsSetApplyParentClosePolicyAttributes() bool { return v != nil && v.ApplyParentClosePolicyAttributes != nil } type CrossClusterTaskResponse struct { TaskID *int64 `json:"taskID,omitempty"` TaskType *CrossClusterTaskType `json:"taskType,omitempty"` TaskState *int16 `json:"taskState,omitempty"` FailedCause *CrossClusterTaskFailedCause `json:"failedCause,omitempty"` StartChildExecutionAttributes *CrossClusterStartChildExecutionResponseAttributes `json:"startChildExecutionAttributes,omitempty"` CancelExecutionAttributes *CrossClusterCancelExecutionResponseAttributes `json:"cancelExecutionAttributes,omitempty"` SignalExecutionAttributes *CrossClusterSignalExecutionResponseAttributes `json:"signalExecutionAttributes,omitempty"` RecordChildWorkflowExecutionCompleteAttributes *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes `json:"recordChildWorkflowExecutionCompleteAttributes,omitempty"` ApplyParentClosePolicyAttributes *CrossClusterApplyParentClosePolicyResponseAttributes `json:"applyParentClosePolicyAttributes,omitempty"` } // ToWire translates a CrossClusterTaskResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CrossClusterTaskResponse) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.TaskID != nil { w, err = wire.NewValueI64(*(v.TaskID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskType != nil { w, err = v.TaskType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TaskState != nil { w, err = wire.NewValueI16(*(v.TaskState)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.FailedCause != nil { w, err = v.FailedCause.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.StartChildExecutionAttributes != nil { w, err = v.StartChildExecutionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.CancelExecutionAttributes != nil { w, err = v.CancelExecutionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.SignalExecutionAttributes != nil { w, err = v.SignalExecutionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.RecordChildWorkflowExecutionCompleteAttributes != nil { w, err = v.RecordChildWorkflowExecutionCompleteAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.ApplyParentClosePolicyAttributes != nil { w, err = v.ApplyParentClosePolicyAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CrossClusterStartChildExecutionResponseAttributes_Read(w wire.Value) (*CrossClusterStartChildExecutionResponseAttributes, error) { var v CrossClusterStartChildExecutionResponseAttributes err := v.FromWire(w) return &v, err } func _CrossClusterCancelExecutionResponseAttributes_Read(w wire.Value) (*CrossClusterCancelExecutionResponseAttributes, error) { var v CrossClusterCancelExecutionResponseAttributes err := v.FromWire(w) return &v, err } func _CrossClusterSignalExecutionResponseAttributes_Read(w wire.Value) (*CrossClusterSignalExecutionResponseAttributes, error) { var v CrossClusterSignalExecutionResponseAttributes err := v.FromWire(w) return &v, err } func _CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes_Read(w wire.Value) (*CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes, error) { var v CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes err := v.FromWire(w) return &v, err } func _CrossClusterApplyParentClosePolicyResponseAttributes_Read(w wire.Value) (*CrossClusterApplyParentClosePolicyResponseAttributes, error) { var v CrossClusterApplyParentClosePolicyResponseAttributes err := v.FromWire(w) return &v, err } // FromWire deserializes a CrossClusterTaskResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CrossClusterTaskResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CrossClusterTaskResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CrossClusterTaskResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x CrossClusterTaskType x, err = _CrossClusterTaskType_Read(field.Value) v.TaskType = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.TaskState = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x CrossClusterTaskFailedCause x, err = _CrossClusterTaskFailedCause_Read(field.Value) v.FailedCause = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.StartChildExecutionAttributes, err = _CrossClusterStartChildExecutionResponseAttributes_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.CancelExecutionAttributes, err = _CrossClusterCancelExecutionResponseAttributes_Read(field.Value) if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.SignalExecutionAttributes, err = _CrossClusterSignalExecutionResponseAttributes_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TStruct { v.RecordChildWorkflowExecutionCompleteAttributes, err = _CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes_Read(field.Value) if err != nil { return err } } case 90: if field.Value.Type() == wire.TStruct { v.ApplyParentClosePolicyAttributes, err = _CrossClusterApplyParentClosePolicyResponseAttributes_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a CrossClusterTaskResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CrossClusterTaskResponse struct could not be encoded. func (v *CrossClusterTaskResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := v.TaskType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskState != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.TaskState)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailedCause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := v.FailedCause.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartChildExecutionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.StartChildExecutionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelExecutionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.CancelExecutionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalExecutionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.SignalExecutionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RecordChildWorkflowExecutionCompleteAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TStruct}); err != nil { return err } if err := v.RecordChildWorkflowExecutionCompleteAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ApplyParentClosePolicyAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TStruct}); err != nil { return err } if err := v.ApplyParentClosePolicyAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CrossClusterStartChildExecutionResponseAttributes_Decode(sr stream.Reader) (*CrossClusterStartChildExecutionResponseAttributes, error) { var v CrossClusterStartChildExecutionResponseAttributes err := v.Decode(sr) return &v, err } func _CrossClusterCancelExecutionResponseAttributes_Decode(sr stream.Reader) (*CrossClusterCancelExecutionResponseAttributes, error) { var v CrossClusterCancelExecutionResponseAttributes err := v.Decode(sr) return &v, err } func _CrossClusterSignalExecutionResponseAttributes_Decode(sr stream.Reader) (*CrossClusterSignalExecutionResponseAttributes, error) { var v CrossClusterSignalExecutionResponseAttributes err := v.Decode(sr) return &v, err } func _CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes_Decode(sr stream.Reader) (*CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes, error) { var v CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes err := v.Decode(sr) return &v, err } func _CrossClusterApplyParentClosePolicyResponseAttributes_Decode(sr stream.Reader) (*CrossClusterApplyParentClosePolicyResponseAttributes, error) { var v CrossClusterApplyParentClosePolicyResponseAttributes err := v.Decode(sr) return &v, err } // Decode deserializes a CrossClusterTaskResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CrossClusterTaskResponse struct could not be generated from the wire // representation. func (v *CrossClusterTaskResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x CrossClusterTaskType x, err = _CrossClusterTaskType_Decode(sr) v.TaskType = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.TaskState = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x CrossClusterTaskFailedCause x, err = _CrossClusterTaskFailedCause_Decode(sr) v.FailedCause = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.StartChildExecutionAttributes, err = _CrossClusterStartChildExecutionResponseAttributes_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.CancelExecutionAttributes, err = _CrossClusterCancelExecutionResponseAttributes_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.SignalExecutionAttributes, err = _CrossClusterSignalExecutionResponseAttributes_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TStruct: v.RecordChildWorkflowExecutionCompleteAttributes, err = _CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TStruct: v.ApplyParentClosePolicyAttributes, err = _CrossClusterApplyParentClosePolicyResponseAttributes_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a CrossClusterTaskResponse // struct. func (v *CrossClusterTaskResponse) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.TaskID != nil { fields[i] = fmt.Sprintf("TaskID: %v", *(v.TaskID)) i++ } if v.TaskType != nil { fields[i] = fmt.Sprintf("TaskType: %v", *(v.TaskType)) i++ } if v.TaskState != nil { fields[i] = fmt.Sprintf("TaskState: %v", *(v.TaskState)) i++ } if v.FailedCause != nil { fields[i] = fmt.Sprintf("FailedCause: %v", *(v.FailedCause)) i++ } if v.StartChildExecutionAttributes != nil { fields[i] = fmt.Sprintf("StartChildExecutionAttributes: %v", v.StartChildExecutionAttributes) i++ } if v.CancelExecutionAttributes != nil { fields[i] = fmt.Sprintf("CancelExecutionAttributes: %v", v.CancelExecutionAttributes) i++ } if v.SignalExecutionAttributes != nil { fields[i] = fmt.Sprintf("SignalExecutionAttributes: %v", v.SignalExecutionAttributes) i++ } if v.RecordChildWorkflowExecutionCompleteAttributes != nil { fields[i] = fmt.Sprintf("RecordChildWorkflowExecutionCompleteAttributes: %v", v.RecordChildWorkflowExecutionCompleteAttributes) i++ } if v.ApplyParentClosePolicyAttributes != nil { fields[i] = fmt.Sprintf("ApplyParentClosePolicyAttributes: %v", v.ApplyParentClosePolicyAttributes) i++ } return fmt.Sprintf("CrossClusterTaskResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this CrossClusterTaskResponse match the // provided CrossClusterTaskResponse. // // This function performs a deep comparison. func (v *CrossClusterTaskResponse) Equals(rhs *CrossClusterTaskResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.TaskID, rhs.TaskID) { return false } if !_CrossClusterTaskType_EqualsPtr(v.TaskType, rhs.TaskType) { return false } if !_I16_EqualsPtr(v.TaskState, rhs.TaskState) { return false } if !_CrossClusterTaskFailedCause_EqualsPtr(v.FailedCause, rhs.FailedCause) { return false } if !((v.StartChildExecutionAttributes == nil && rhs.StartChildExecutionAttributes == nil) || (v.StartChildExecutionAttributes != nil && rhs.StartChildExecutionAttributes != nil && v.StartChildExecutionAttributes.Equals(rhs.StartChildExecutionAttributes))) { return false } if !((v.CancelExecutionAttributes == nil && rhs.CancelExecutionAttributes == nil) || (v.CancelExecutionAttributes != nil && rhs.CancelExecutionAttributes != nil && v.CancelExecutionAttributes.Equals(rhs.CancelExecutionAttributes))) { return false } if !((v.SignalExecutionAttributes == nil && rhs.SignalExecutionAttributes == nil) || (v.SignalExecutionAttributes != nil && rhs.SignalExecutionAttributes != nil && v.SignalExecutionAttributes.Equals(rhs.SignalExecutionAttributes))) { return false } if !((v.RecordChildWorkflowExecutionCompleteAttributes == nil && rhs.RecordChildWorkflowExecutionCompleteAttributes == nil) || (v.RecordChildWorkflowExecutionCompleteAttributes != nil && rhs.RecordChildWorkflowExecutionCompleteAttributes != nil && v.RecordChildWorkflowExecutionCompleteAttributes.Equals(rhs.RecordChildWorkflowExecutionCompleteAttributes))) { return false } if !((v.ApplyParentClosePolicyAttributes == nil && rhs.ApplyParentClosePolicyAttributes == nil) || (v.ApplyParentClosePolicyAttributes != nil && rhs.ApplyParentClosePolicyAttributes != nil && v.ApplyParentClosePolicyAttributes.Equals(rhs.ApplyParentClosePolicyAttributes))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterTaskResponse. func (v *CrossClusterTaskResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskID != nil { enc.AddInt64("taskID", *v.TaskID) } if v.TaskType != nil { err = multierr.Append(err, enc.AddObject("taskType", *v.TaskType)) } if v.TaskState != nil { enc.AddInt16("taskState", *v.TaskState) } if v.FailedCause != nil { err = multierr.Append(err, enc.AddObject("failedCause", *v.FailedCause)) } if v.StartChildExecutionAttributes != nil { err = multierr.Append(err, enc.AddObject("startChildExecutionAttributes", v.StartChildExecutionAttributes)) } if v.CancelExecutionAttributes != nil { err = multierr.Append(err, enc.AddObject("cancelExecutionAttributes", v.CancelExecutionAttributes)) } if v.SignalExecutionAttributes != nil { err = multierr.Append(err, enc.AddObject("signalExecutionAttributes", v.SignalExecutionAttributes)) } if v.RecordChildWorkflowExecutionCompleteAttributes != nil { err = multierr.Append(err, enc.AddObject("recordChildWorkflowExecutionCompleteAttributes", v.RecordChildWorkflowExecutionCompleteAttributes)) } if v.ApplyParentClosePolicyAttributes != nil { err = multierr.Append(err, enc.AddObject("applyParentClosePolicyAttributes", v.ApplyParentClosePolicyAttributes)) } return err } // GetTaskID returns the value of TaskID if it is set or its // zero value if it is unset. func (v *CrossClusterTaskResponse) GetTaskID() (o int64) { if v != nil && v.TaskID != nil { return *v.TaskID } return } // IsSetTaskID returns true if TaskID is not nil. func (v *CrossClusterTaskResponse) IsSetTaskID() bool { return v != nil && v.TaskID != nil } // GetTaskType returns the value of TaskType if it is set or its // zero value if it is unset. func (v *CrossClusterTaskResponse) GetTaskType() (o CrossClusterTaskType) { if v != nil && v.TaskType != nil { return *v.TaskType } return } // IsSetTaskType returns true if TaskType is not nil. func (v *CrossClusterTaskResponse) IsSetTaskType() bool { return v != nil && v.TaskType != nil } // GetTaskState returns the value of TaskState if it is set or its // zero value if it is unset. func (v *CrossClusterTaskResponse) GetTaskState() (o int16) { if v != nil && v.TaskState != nil { return *v.TaskState } return } // IsSetTaskState returns true if TaskState is not nil. func (v *CrossClusterTaskResponse) IsSetTaskState() bool { return v != nil && v.TaskState != nil } // GetFailedCause returns the value of FailedCause if it is set or its // zero value if it is unset. func (v *CrossClusterTaskResponse) GetFailedCause() (o CrossClusterTaskFailedCause) { if v != nil && v.FailedCause != nil { return *v.FailedCause } return } // IsSetFailedCause returns true if FailedCause is not nil. func (v *CrossClusterTaskResponse) IsSetFailedCause() bool { return v != nil && v.FailedCause != nil } // GetStartChildExecutionAttributes returns the value of StartChildExecutionAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterTaskResponse) GetStartChildExecutionAttributes() (o *CrossClusterStartChildExecutionResponseAttributes) { if v != nil && v.StartChildExecutionAttributes != nil { return v.StartChildExecutionAttributes } return } // IsSetStartChildExecutionAttributes returns true if StartChildExecutionAttributes is not nil. func (v *CrossClusterTaskResponse) IsSetStartChildExecutionAttributes() bool { return v != nil && v.StartChildExecutionAttributes != nil } // GetCancelExecutionAttributes returns the value of CancelExecutionAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterTaskResponse) GetCancelExecutionAttributes() (o *CrossClusterCancelExecutionResponseAttributes) { if v != nil && v.CancelExecutionAttributes != nil { return v.CancelExecutionAttributes } return } // IsSetCancelExecutionAttributes returns true if CancelExecutionAttributes is not nil. func (v *CrossClusterTaskResponse) IsSetCancelExecutionAttributes() bool { return v != nil && v.CancelExecutionAttributes != nil } // GetSignalExecutionAttributes returns the value of SignalExecutionAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterTaskResponse) GetSignalExecutionAttributes() (o *CrossClusterSignalExecutionResponseAttributes) { if v != nil && v.SignalExecutionAttributes != nil { return v.SignalExecutionAttributes } return } // IsSetSignalExecutionAttributes returns true if SignalExecutionAttributes is not nil. func (v *CrossClusterTaskResponse) IsSetSignalExecutionAttributes() bool { return v != nil && v.SignalExecutionAttributes != nil } // GetRecordChildWorkflowExecutionCompleteAttributes returns the value of RecordChildWorkflowExecutionCompleteAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterTaskResponse) GetRecordChildWorkflowExecutionCompleteAttributes() (o *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) { if v != nil && v.RecordChildWorkflowExecutionCompleteAttributes != nil { return v.RecordChildWorkflowExecutionCompleteAttributes } return } // IsSetRecordChildWorkflowExecutionCompleteAttributes returns true if RecordChildWorkflowExecutionCompleteAttributes is not nil. func (v *CrossClusterTaskResponse) IsSetRecordChildWorkflowExecutionCompleteAttributes() bool { return v != nil && v.RecordChildWorkflowExecutionCompleteAttributes != nil } // GetApplyParentClosePolicyAttributes returns the value of ApplyParentClosePolicyAttributes if it is set or its // zero value if it is unset. func (v *CrossClusterTaskResponse) GetApplyParentClosePolicyAttributes() (o *CrossClusterApplyParentClosePolicyResponseAttributes) { if v != nil && v.ApplyParentClosePolicyAttributes != nil { return v.ApplyParentClosePolicyAttributes } return } // IsSetApplyParentClosePolicyAttributes returns true if ApplyParentClosePolicyAttributes is not nil. func (v *CrossClusterTaskResponse) IsSetApplyParentClosePolicyAttributes() bool { return v != nil && v.ApplyParentClosePolicyAttributes != nil } type CrossClusterTaskType int32 const ( CrossClusterTaskTypeStartChildExecution CrossClusterTaskType = 0 CrossClusterTaskTypeCancelExecution CrossClusterTaskType = 1 CrossClusterTaskTypeSignalExecution CrossClusterTaskType = 2 CrossClusterTaskTypeRecordChildWorkflowExecutionComplete CrossClusterTaskType = 3 CrossClusterTaskTypeApplyParentClosePolicy CrossClusterTaskType = 4 ) // CrossClusterTaskType_Values returns all recognized values of CrossClusterTaskType. func CrossClusterTaskType_Values() []CrossClusterTaskType { return []CrossClusterTaskType{ CrossClusterTaskTypeStartChildExecution, CrossClusterTaskTypeCancelExecution, CrossClusterTaskTypeSignalExecution, CrossClusterTaskTypeRecordChildWorkflowExecutionComplete, CrossClusterTaskTypeApplyParentClosePolicy, } } // UnmarshalText tries to decode CrossClusterTaskType from a byte slice // containing its name. // // var v CrossClusterTaskType // err := v.UnmarshalText([]byte("StartChildExecution")) func (v *CrossClusterTaskType) UnmarshalText(value []byte) error { switch s := string(value); s { case "StartChildExecution": *v = CrossClusterTaskTypeStartChildExecution return nil case "CancelExecution": *v = CrossClusterTaskTypeCancelExecution return nil case "SignalExecution": *v = CrossClusterTaskTypeSignalExecution return nil case "RecordChildWorkflowExecutionComplete": *v = CrossClusterTaskTypeRecordChildWorkflowExecutionComplete return nil case "ApplyParentClosePolicy": *v = CrossClusterTaskTypeApplyParentClosePolicy return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "CrossClusterTaskType", err) } *v = CrossClusterTaskType(val) return nil } } // MarshalText encodes CrossClusterTaskType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v CrossClusterTaskType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("StartChildExecution"), nil case 1: return []byte("CancelExecution"), nil case 2: return []byte("SignalExecution"), nil case 3: return []byte("RecordChildWorkflowExecutionComplete"), nil case 4: return []byte("ApplyParentClosePolicy"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CrossClusterTaskType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v CrossClusterTaskType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "StartChildExecution") case 1: enc.AddString("name", "CancelExecution") case 2: enc.AddString("name", "SignalExecution") case 3: enc.AddString("name", "RecordChildWorkflowExecutionComplete") case 4: enc.AddString("name", "ApplyParentClosePolicy") } return nil } // Ptr returns a pointer to this enum value. func (v CrossClusterTaskType) Ptr() *CrossClusterTaskType { return &v } // Encode encodes CrossClusterTaskType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v CrossClusterTaskType // return v.Encode(sWriter) func (v CrossClusterTaskType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates CrossClusterTaskType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v CrossClusterTaskType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes CrossClusterTaskType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return CrossClusterTaskType(0), err // } // // var v CrossClusterTaskType // if err := v.FromWire(x); err != nil { // return CrossClusterTaskType(0), err // } // return v, nil func (v *CrossClusterTaskType) FromWire(w wire.Value) error { *v = (CrossClusterTaskType)(w.GetI32()) return nil } // Decode reads off the encoded CrossClusterTaskType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v CrossClusterTaskType // if err := v.Decode(sReader); err != nil { // return CrossClusterTaskType(0), err // } // return v, nil func (v *CrossClusterTaskType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (CrossClusterTaskType)(i) return nil } // String returns a readable string representation of CrossClusterTaskType. func (v CrossClusterTaskType) String() string { w := int32(v) switch w { case 0: return "StartChildExecution" case 1: return "CancelExecution" case 2: return "SignalExecution" case 3: return "RecordChildWorkflowExecutionComplete" case 4: return "ApplyParentClosePolicy" } return fmt.Sprintf("CrossClusterTaskType(%d)", w) } // Equals returns true if this CrossClusterTaskType value matches the provided // value. func (v CrossClusterTaskType) Equals(rhs CrossClusterTaskType) bool { return v == rhs } // MarshalJSON serializes CrossClusterTaskType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v CrossClusterTaskType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"StartChildExecution\""), nil case 1: return ([]byte)("\"CancelExecution\""), nil case 2: return ([]byte)("\"SignalExecution\""), nil case 3: return ([]byte)("\"RecordChildWorkflowExecutionComplete\""), nil case 4: return ([]byte)("\"ApplyParentClosePolicy\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode CrossClusterTaskType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *CrossClusterTaskType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "CrossClusterTaskType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "CrossClusterTaskType") } *v = (CrossClusterTaskType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "CrossClusterTaskType") } } type CurrentBranchChangedError struct { Message string `json:"message,required"` CurrentBranchToken []byte `json:"currentBranchToken,required"` } // ToWire translates a CurrentBranchChangedError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *CurrentBranchChangedError) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ if v.CurrentBranchToken == nil { return w, errors.New("field CurrentBranchToken of CurrentBranchChangedError is required") } w, err = wire.NewValueBinary(v.CurrentBranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a CurrentBranchChangedError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a CurrentBranchChangedError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v CurrentBranchChangedError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *CurrentBranchChangedError) FromWire(w wire.Value) error { var err error messageIsSet := false currentBranchTokenIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } case 20: if field.Value.Type() == wire.TBinary { v.CurrentBranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } currentBranchTokenIsSet = true } } } if !messageIsSet { return errors.New("field Message of CurrentBranchChangedError is required") } if !currentBranchTokenIsSet { return errors.New("field CurrentBranchToken of CurrentBranchChangedError is required") } return nil } // Encode serializes a CurrentBranchChangedError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a CurrentBranchChangedError struct could not be encoded. func (v *CurrentBranchChangedError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if v.CurrentBranchToken == nil { return errors.New("field CurrentBranchToken of CurrentBranchChangedError is required") } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.CurrentBranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a CurrentBranchChangedError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a CurrentBranchChangedError struct could not be generated from the wire // representation. func (v *CurrentBranchChangedError) Decode(sr stream.Reader) error { messageIsSet := false currentBranchTokenIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true case fh.ID == 20 && fh.Type == wire.TBinary: v.CurrentBranchToken, err = sr.ReadBinary() if err != nil { return err } currentBranchTokenIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of CurrentBranchChangedError is required") } if !currentBranchTokenIsSet { return errors.New("field CurrentBranchToken of CurrentBranchChangedError is required") } return nil } // String returns a readable string representation of a CurrentBranchChangedError // struct. func (v *CurrentBranchChangedError) String() string { if v == nil { return "" } var fields [2]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ fields[i] = fmt.Sprintf("CurrentBranchToken: %v", v.CurrentBranchToken) i++ return fmt.Sprintf("CurrentBranchChangedError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*CurrentBranchChangedError) ErrorName() string { return "CurrentBranchChangedError" } // Equals returns true if all the fields of this CurrentBranchChangedError match the // provided CurrentBranchChangedError. // // This function performs a deep comparison. func (v *CurrentBranchChangedError) Equals(rhs *CurrentBranchChangedError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } if !bytes.Equal(v.CurrentBranchToken, rhs.CurrentBranchToken) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of CurrentBranchChangedError. func (v *CurrentBranchChangedError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) enc.AddString("currentBranchToken", base64.StdEncoding.EncodeToString(v.CurrentBranchToken)) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *CurrentBranchChangedError) GetMessage() (o string) { if v != nil { o = v.Message } return } // GetCurrentBranchToken returns the value of CurrentBranchToken if it is set or its // zero value if it is unset. func (v *CurrentBranchChangedError) GetCurrentBranchToken() (o []byte) { if v != nil { o = v.CurrentBranchToken } return } // IsSetCurrentBranchToken returns true if CurrentBranchToken is not nil. func (v *CurrentBranchChangedError) IsSetCurrentBranchToken() bool { return v != nil && v.CurrentBranchToken != nil } func (v *CurrentBranchChangedError) Error() string { return v.String() } type DataBlob struct { EncodingType *EncodingType `json:"EncodingType,omitempty"` Data []byte `json:"Data,omitempty"` } // ToWire translates a DataBlob struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DataBlob) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.EncodingType != nil { w, err = v.EncodingType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Data != nil { w, err = wire.NewValueBinary(v.Data), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _EncodingType_Read(w wire.Value) (EncodingType, error) { var v EncodingType err := v.FromWire(w) return v, err } // FromWire deserializes a DataBlob struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DataBlob struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DataBlob // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DataBlob) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x EncodingType x, err = _EncodingType_Read(field.Value) v.EncodingType = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Data, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a DataBlob struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DataBlob struct could not be encoded. func (v *DataBlob) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.EncodingType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.EncodingType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Data != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Data); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _EncodingType_Decode(sr stream.Reader) (EncodingType, error) { var v EncodingType err := v.Decode(sr) return v, err } // Decode deserializes a DataBlob struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DataBlob struct could not be generated from the wire // representation. func (v *DataBlob) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x EncodingType x, err = _EncodingType_Decode(sr) v.EncodingType = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Data, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DataBlob // struct. func (v *DataBlob) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.EncodingType != nil { fields[i] = fmt.Sprintf("EncodingType: %v", *(v.EncodingType)) i++ } if v.Data != nil { fields[i] = fmt.Sprintf("Data: %v", v.Data) i++ } return fmt.Sprintf("DataBlob{%v}", strings.Join(fields[:i], ", ")) } func _EncodingType_EqualsPtr(lhs, rhs *EncodingType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this DataBlob match the // provided DataBlob. // // This function performs a deep comparison. func (v *DataBlob) Equals(rhs *DataBlob) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_EncodingType_EqualsPtr(v.EncodingType, rhs.EncodingType) { return false } if !((v.Data == nil && rhs.Data == nil) || (v.Data != nil && rhs.Data != nil && bytes.Equal(v.Data, rhs.Data))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DataBlob. func (v *DataBlob) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.EncodingType != nil { err = multierr.Append(err, enc.AddObject("EncodingType", *v.EncodingType)) } if v.Data != nil { enc.AddString("Data", base64.StdEncoding.EncodeToString(v.Data)) } return err } // GetEncodingType returns the value of EncodingType if it is set or its // zero value if it is unset. func (v *DataBlob) GetEncodingType() (o EncodingType) { if v != nil && v.EncodingType != nil { return *v.EncodingType } return } // IsSetEncodingType returns true if EncodingType is not nil. func (v *DataBlob) IsSetEncodingType() bool { return v != nil && v.EncodingType != nil } // GetData returns the value of Data if it is set or its // zero value if it is unset. func (v *DataBlob) GetData() (o []byte) { if v != nil && v.Data != nil { return v.Data } return } // IsSetData returns true if Data is not nil. func (v *DataBlob) IsSetData() bool { return v != nil && v.Data != nil } type Decision struct { DecisionType *DecisionType `json:"decisionType,omitempty"` ScheduleActivityTaskDecisionAttributes *ScheduleActivityTaskDecisionAttributes `json:"scheduleActivityTaskDecisionAttributes,omitempty"` StartTimerDecisionAttributes *StartTimerDecisionAttributes `json:"startTimerDecisionAttributes,omitempty"` CompleteWorkflowExecutionDecisionAttributes *CompleteWorkflowExecutionDecisionAttributes `json:"completeWorkflowExecutionDecisionAttributes,omitempty"` FailWorkflowExecutionDecisionAttributes *FailWorkflowExecutionDecisionAttributes `json:"failWorkflowExecutionDecisionAttributes,omitempty"` RequestCancelActivityTaskDecisionAttributes *RequestCancelActivityTaskDecisionAttributes `json:"requestCancelActivityTaskDecisionAttributes,omitempty"` CancelTimerDecisionAttributes *CancelTimerDecisionAttributes `json:"cancelTimerDecisionAttributes,omitempty"` CancelWorkflowExecutionDecisionAttributes *CancelWorkflowExecutionDecisionAttributes `json:"cancelWorkflowExecutionDecisionAttributes,omitempty"` RequestCancelExternalWorkflowExecutionDecisionAttributes *RequestCancelExternalWorkflowExecutionDecisionAttributes `json:"requestCancelExternalWorkflowExecutionDecisionAttributes,omitempty"` RecordMarkerDecisionAttributes *RecordMarkerDecisionAttributes `json:"recordMarkerDecisionAttributes,omitempty"` ContinueAsNewWorkflowExecutionDecisionAttributes *ContinueAsNewWorkflowExecutionDecisionAttributes `json:"continueAsNewWorkflowExecutionDecisionAttributes,omitempty"` StartChildWorkflowExecutionDecisionAttributes *StartChildWorkflowExecutionDecisionAttributes `json:"startChildWorkflowExecutionDecisionAttributes,omitempty"` SignalExternalWorkflowExecutionDecisionAttributes *SignalExternalWorkflowExecutionDecisionAttributes `json:"signalExternalWorkflowExecutionDecisionAttributes,omitempty"` UpsertWorkflowSearchAttributesDecisionAttributes *UpsertWorkflowSearchAttributesDecisionAttributes `json:"upsertWorkflowSearchAttributesDecisionAttributes,omitempty"` } // ToWire translates a Decision struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *Decision) ToWire() (wire.Value, error) { var ( fields [14]wire.Field i int = 0 w wire.Value err error ) if v.DecisionType != nil { w, err = v.DecisionType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ScheduleActivityTaskDecisionAttributes != nil { w, err = v.ScheduleActivityTaskDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.StartTimerDecisionAttributes != nil { w, err = v.StartTimerDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 25, Value: w} i++ } if v.CompleteWorkflowExecutionDecisionAttributes != nil { w, err = v.CompleteWorkflowExecutionDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.FailWorkflowExecutionDecisionAttributes != nil { w, err = v.FailWorkflowExecutionDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 35, Value: w} i++ } if v.RequestCancelActivityTaskDecisionAttributes != nil { w, err = v.RequestCancelActivityTaskDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.CancelTimerDecisionAttributes != nil { w, err = v.CancelTimerDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.CancelWorkflowExecutionDecisionAttributes != nil { w, err = v.CancelWorkflowExecutionDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.RequestCancelExternalWorkflowExecutionDecisionAttributes != nil { w, err = v.RequestCancelExternalWorkflowExecutionDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.RecordMarkerDecisionAttributes != nil { w, err = v.RecordMarkerDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.ContinueAsNewWorkflowExecutionDecisionAttributes != nil { w, err = v.ContinueAsNewWorkflowExecutionDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.StartChildWorkflowExecutionDecisionAttributes != nil { w, err = v.StartChildWorkflowExecutionDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.SignalExternalWorkflowExecutionDecisionAttributes != nil { w, err = v.SignalExternalWorkflowExecutionDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.UpsertWorkflowSearchAttributesDecisionAttributes != nil { w, err = v.UpsertWorkflowSearchAttributesDecisionAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DecisionType_Read(w wire.Value) (DecisionType, error) { var v DecisionType err := v.FromWire(w) return v, err } func _ScheduleActivityTaskDecisionAttributes_Read(w wire.Value) (*ScheduleActivityTaskDecisionAttributes, error) { var v ScheduleActivityTaskDecisionAttributes err := v.FromWire(w) return &v, err } func _StartTimerDecisionAttributes_Read(w wire.Value) (*StartTimerDecisionAttributes, error) { var v StartTimerDecisionAttributes err := v.FromWire(w) return &v, err } func _CompleteWorkflowExecutionDecisionAttributes_Read(w wire.Value) (*CompleteWorkflowExecutionDecisionAttributes, error) { var v CompleteWorkflowExecutionDecisionAttributes err := v.FromWire(w) return &v, err } func _FailWorkflowExecutionDecisionAttributes_Read(w wire.Value) (*FailWorkflowExecutionDecisionAttributes, error) { var v FailWorkflowExecutionDecisionAttributes err := v.FromWire(w) return &v, err } func _RequestCancelActivityTaskDecisionAttributes_Read(w wire.Value) (*RequestCancelActivityTaskDecisionAttributes, error) { var v RequestCancelActivityTaskDecisionAttributes err := v.FromWire(w) return &v, err } func _CancelTimerDecisionAttributes_Read(w wire.Value) (*CancelTimerDecisionAttributes, error) { var v CancelTimerDecisionAttributes err := v.FromWire(w) return &v, err } func _CancelWorkflowExecutionDecisionAttributes_Read(w wire.Value) (*CancelWorkflowExecutionDecisionAttributes, error) { var v CancelWorkflowExecutionDecisionAttributes err := v.FromWire(w) return &v, err } func _RequestCancelExternalWorkflowExecutionDecisionAttributes_Read(w wire.Value) (*RequestCancelExternalWorkflowExecutionDecisionAttributes, error) { var v RequestCancelExternalWorkflowExecutionDecisionAttributes err := v.FromWire(w) return &v, err } func _RecordMarkerDecisionAttributes_Read(w wire.Value) (*RecordMarkerDecisionAttributes, error) { var v RecordMarkerDecisionAttributes err := v.FromWire(w) return &v, err } func _ContinueAsNewWorkflowExecutionDecisionAttributes_Read(w wire.Value) (*ContinueAsNewWorkflowExecutionDecisionAttributes, error) { var v ContinueAsNewWorkflowExecutionDecisionAttributes err := v.FromWire(w) return &v, err } func _StartChildWorkflowExecutionDecisionAttributes_Read(w wire.Value) (*StartChildWorkflowExecutionDecisionAttributes, error) { var v StartChildWorkflowExecutionDecisionAttributes err := v.FromWire(w) return &v, err } func _SignalExternalWorkflowExecutionDecisionAttributes_Read(w wire.Value) (*SignalExternalWorkflowExecutionDecisionAttributes, error) { var v SignalExternalWorkflowExecutionDecisionAttributes err := v.FromWire(w) return &v, err } func _UpsertWorkflowSearchAttributesDecisionAttributes_Read(w wire.Value) (*UpsertWorkflowSearchAttributesDecisionAttributes, error) { var v UpsertWorkflowSearchAttributesDecisionAttributes err := v.FromWire(w) return &v, err } // FromWire deserializes a Decision struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a Decision struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v Decision // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *Decision) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x DecisionType x, err = _DecisionType_Read(field.Value) v.DecisionType = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.ScheduleActivityTaskDecisionAttributes, err = _ScheduleActivityTaskDecisionAttributes_Read(field.Value) if err != nil { return err } } case 25: if field.Value.Type() == wire.TStruct { v.StartTimerDecisionAttributes, err = _StartTimerDecisionAttributes_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.CompleteWorkflowExecutionDecisionAttributes, err = _CompleteWorkflowExecutionDecisionAttributes_Read(field.Value) if err != nil { return err } } case 35: if field.Value.Type() == wire.TStruct { v.FailWorkflowExecutionDecisionAttributes, err = _FailWorkflowExecutionDecisionAttributes_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.RequestCancelActivityTaskDecisionAttributes, err = _RequestCancelActivityTaskDecisionAttributes_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.CancelTimerDecisionAttributes, err = _CancelTimerDecisionAttributes_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.CancelWorkflowExecutionDecisionAttributes, err = _CancelWorkflowExecutionDecisionAttributes_Read(field.Value) if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.RequestCancelExternalWorkflowExecutionDecisionAttributes, err = _RequestCancelExternalWorkflowExecutionDecisionAttributes_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TStruct { v.RecordMarkerDecisionAttributes, err = _RecordMarkerDecisionAttributes_Read(field.Value) if err != nil { return err } } case 90: if field.Value.Type() == wire.TStruct { v.ContinueAsNewWorkflowExecutionDecisionAttributes, err = _ContinueAsNewWorkflowExecutionDecisionAttributes_Read(field.Value) if err != nil { return err } } case 100: if field.Value.Type() == wire.TStruct { v.StartChildWorkflowExecutionDecisionAttributes, err = _StartChildWorkflowExecutionDecisionAttributes_Read(field.Value) if err != nil { return err } } case 110: if field.Value.Type() == wire.TStruct { v.SignalExternalWorkflowExecutionDecisionAttributes, err = _SignalExternalWorkflowExecutionDecisionAttributes_Read(field.Value) if err != nil { return err } } case 120: if field.Value.Type() == wire.TStruct { v.UpsertWorkflowSearchAttributesDecisionAttributes, err = _UpsertWorkflowSearchAttributesDecisionAttributes_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a Decision struct directly into bytes, without going // through an intermediary type. // // An error is returned if a Decision struct could not be encoded. func (v *Decision) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DecisionType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.DecisionType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleActivityTaskDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.ScheduleActivityTaskDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartTimerDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 25, Type: wire.TStruct}); err != nil { return err } if err := v.StartTimerDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompleteWorkflowExecutionDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.CompleteWorkflowExecutionDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailWorkflowExecutionDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 35, Type: wire.TStruct}); err != nil { return err } if err := v.FailWorkflowExecutionDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestCancelActivityTaskDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.RequestCancelActivityTaskDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelTimerDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.CancelTimerDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelWorkflowExecutionDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.CancelWorkflowExecutionDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestCancelExternalWorkflowExecutionDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.RequestCancelExternalWorkflowExecutionDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RecordMarkerDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TStruct}); err != nil { return err } if err := v.RecordMarkerDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ContinueAsNewWorkflowExecutionDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TStruct}); err != nil { return err } if err := v.ContinueAsNewWorkflowExecutionDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartChildWorkflowExecutionDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TStruct}); err != nil { return err } if err := v.StartChildWorkflowExecutionDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalExternalWorkflowExecutionDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TStruct}); err != nil { return err } if err := v.SignalExternalWorkflowExecutionDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.UpsertWorkflowSearchAttributesDecisionAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TStruct}); err != nil { return err } if err := v.UpsertWorkflowSearchAttributesDecisionAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DecisionType_Decode(sr stream.Reader) (DecisionType, error) { var v DecisionType err := v.Decode(sr) return v, err } func _ScheduleActivityTaskDecisionAttributes_Decode(sr stream.Reader) (*ScheduleActivityTaskDecisionAttributes, error) { var v ScheduleActivityTaskDecisionAttributes err := v.Decode(sr) return &v, err } func _StartTimerDecisionAttributes_Decode(sr stream.Reader) (*StartTimerDecisionAttributes, error) { var v StartTimerDecisionAttributes err := v.Decode(sr) return &v, err } func _CompleteWorkflowExecutionDecisionAttributes_Decode(sr stream.Reader) (*CompleteWorkflowExecutionDecisionAttributes, error) { var v CompleteWorkflowExecutionDecisionAttributes err := v.Decode(sr) return &v, err } func _FailWorkflowExecutionDecisionAttributes_Decode(sr stream.Reader) (*FailWorkflowExecutionDecisionAttributes, error) { var v FailWorkflowExecutionDecisionAttributes err := v.Decode(sr) return &v, err } func _RequestCancelActivityTaskDecisionAttributes_Decode(sr stream.Reader) (*RequestCancelActivityTaskDecisionAttributes, error) { var v RequestCancelActivityTaskDecisionAttributes err := v.Decode(sr) return &v, err } func _CancelTimerDecisionAttributes_Decode(sr stream.Reader) (*CancelTimerDecisionAttributes, error) { var v CancelTimerDecisionAttributes err := v.Decode(sr) return &v, err } func _CancelWorkflowExecutionDecisionAttributes_Decode(sr stream.Reader) (*CancelWorkflowExecutionDecisionAttributes, error) { var v CancelWorkflowExecutionDecisionAttributes err := v.Decode(sr) return &v, err } func _RequestCancelExternalWorkflowExecutionDecisionAttributes_Decode(sr stream.Reader) (*RequestCancelExternalWorkflowExecutionDecisionAttributes, error) { var v RequestCancelExternalWorkflowExecutionDecisionAttributes err := v.Decode(sr) return &v, err } func _RecordMarkerDecisionAttributes_Decode(sr stream.Reader) (*RecordMarkerDecisionAttributes, error) { var v RecordMarkerDecisionAttributes err := v.Decode(sr) return &v, err } func _ContinueAsNewWorkflowExecutionDecisionAttributes_Decode(sr stream.Reader) (*ContinueAsNewWorkflowExecutionDecisionAttributes, error) { var v ContinueAsNewWorkflowExecutionDecisionAttributes err := v.Decode(sr) return &v, err } func _StartChildWorkflowExecutionDecisionAttributes_Decode(sr stream.Reader) (*StartChildWorkflowExecutionDecisionAttributes, error) { var v StartChildWorkflowExecutionDecisionAttributes err := v.Decode(sr) return &v, err } func _SignalExternalWorkflowExecutionDecisionAttributes_Decode(sr stream.Reader) (*SignalExternalWorkflowExecutionDecisionAttributes, error) { var v SignalExternalWorkflowExecutionDecisionAttributes err := v.Decode(sr) return &v, err } func _UpsertWorkflowSearchAttributesDecisionAttributes_Decode(sr stream.Reader) (*UpsertWorkflowSearchAttributesDecisionAttributes, error) { var v UpsertWorkflowSearchAttributesDecisionAttributes err := v.Decode(sr) return &v, err } // Decode deserializes a Decision struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a Decision struct could not be generated from the wire // representation. func (v *Decision) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x DecisionType x, err = _DecisionType_Decode(sr) v.DecisionType = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.ScheduleActivityTaskDecisionAttributes, err = _ScheduleActivityTaskDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 25 && fh.Type == wire.TStruct: v.StartTimerDecisionAttributes, err = _StartTimerDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.CompleteWorkflowExecutionDecisionAttributes, err = _CompleteWorkflowExecutionDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 35 && fh.Type == wire.TStruct: v.FailWorkflowExecutionDecisionAttributes, err = _FailWorkflowExecutionDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.RequestCancelActivityTaskDecisionAttributes, err = _RequestCancelActivityTaskDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.CancelTimerDecisionAttributes, err = _CancelTimerDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.CancelWorkflowExecutionDecisionAttributes, err = _CancelWorkflowExecutionDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.RequestCancelExternalWorkflowExecutionDecisionAttributes, err = _RequestCancelExternalWorkflowExecutionDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TStruct: v.RecordMarkerDecisionAttributes, err = _RecordMarkerDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TStruct: v.ContinueAsNewWorkflowExecutionDecisionAttributes, err = _ContinueAsNewWorkflowExecutionDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TStruct: v.StartChildWorkflowExecutionDecisionAttributes, err = _StartChildWorkflowExecutionDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TStruct: v.SignalExternalWorkflowExecutionDecisionAttributes, err = _SignalExternalWorkflowExecutionDecisionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TStruct: v.UpsertWorkflowSearchAttributesDecisionAttributes, err = _UpsertWorkflowSearchAttributesDecisionAttributes_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a Decision // struct. func (v *Decision) String() string { if v == nil { return "" } var fields [14]string i := 0 if v.DecisionType != nil { fields[i] = fmt.Sprintf("DecisionType: %v", *(v.DecisionType)) i++ } if v.ScheduleActivityTaskDecisionAttributes != nil { fields[i] = fmt.Sprintf("ScheduleActivityTaskDecisionAttributes: %v", v.ScheduleActivityTaskDecisionAttributes) i++ } if v.StartTimerDecisionAttributes != nil { fields[i] = fmt.Sprintf("StartTimerDecisionAttributes: %v", v.StartTimerDecisionAttributes) i++ } if v.CompleteWorkflowExecutionDecisionAttributes != nil { fields[i] = fmt.Sprintf("CompleteWorkflowExecutionDecisionAttributes: %v", v.CompleteWorkflowExecutionDecisionAttributes) i++ } if v.FailWorkflowExecutionDecisionAttributes != nil { fields[i] = fmt.Sprintf("FailWorkflowExecutionDecisionAttributes: %v", v.FailWorkflowExecutionDecisionAttributes) i++ } if v.RequestCancelActivityTaskDecisionAttributes != nil { fields[i] = fmt.Sprintf("RequestCancelActivityTaskDecisionAttributes: %v", v.RequestCancelActivityTaskDecisionAttributes) i++ } if v.CancelTimerDecisionAttributes != nil { fields[i] = fmt.Sprintf("CancelTimerDecisionAttributes: %v", v.CancelTimerDecisionAttributes) i++ } if v.CancelWorkflowExecutionDecisionAttributes != nil { fields[i] = fmt.Sprintf("CancelWorkflowExecutionDecisionAttributes: %v", v.CancelWorkflowExecutionDecisionAttributes) i++ } if v.RequestCancelExternalWorkflowExecutionDecisionAttributes != nil { fields[i] = fmt.Sprintf("RequestCancelExternalWorkflowExecutionDecisionAttributes: %v", v.RequestCancelExternalWorkflowExecutionDecisionAttributes) i++ } if v.RecordMarkerDecisionAttributes != nil { fields[i] = fmt.Sprintf("RecordMarkerDecisionAttributes: %v", v.RecordMarkerDecisionAttributes) i++ } if v.ContinueAsNewWorkflowExecutionDecisionAttributes != nil { fields[i] = fmt.Sprintf("ContinueAsNewWorkflowExecutionDecisionAttributes: %v", v.ContinueAsNewWorkflowExecutionDecisionAttributes) i++ } if v.StartChildWorkflowExecutionDecisionAttributes != nil { fields[i] = fmt.Sprintf("StartChildWorkflowExecutionDecisionAttributes: %v", v.StartChildWorkflowExecutionDecisionAttributes) i++ } if v.SignalExternalWorkflowExecutionDecisionAttributes != nil { fields[i] = fmt.Sprintf("SignalExternalWorkflowExecutionDecisionAttributes: %v", v.SignalExternalWorkflowExecutionDecisionAttributes) i++ } if v.UpsertWorkflowSearchAttributesDecisionAttributes != nil { fields[i] = fmt.Sprintf("UpsertWorkflowSearchAttributesDecisionAttributes: %v", v.UpsertWorkflowSearchAttributesDecisionAttributes) i++ } return fmt.Sprintf("Decision{%v}", strings.Join(fields[:i], ", ")) } func _DecisionType_EqualsPtr(lhs, rhs *DecisionType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this Decision match the // provided Decision. // // This function performs a deep comparison. func (v *Decision) Equals(rhs *Decision) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_DecisionType_EqualsPtr(v.DecisionType, rhs.DecisionType) { return false } if !((v.ScheduleActivityTaskDecisionAttributes == nil && rhs.ScheduleActivityTaskDecisionAttributes == nil) || (v.ScheduleActivityTaskDecisionAttributes != nil && rhs.ScheduleActivityTaskDecisionAttributes != nil && v.ScheduleActivityTaskDecisionAttributes.Equals(rhs.ScheduleActivityTaskDecisionAttributes))) { return false } if !((v.StartTimerDecisionAttributes == nil && rhs.StartTimerDecisionAttributes == nil) || (v.StartTimerDecisionAttributes != nil && rhs.StartTimerDecisionAttributes != nil && v.StartTimerDecisionAttributes.Equals(rhs.StartTimerDecisionAttributes))) { return false } if !((v.CompleteWorkflowExecutionDecisionAttributes == nil && rhs.CompleteWorkflowExecutionDecisionAttributes == nil) || (v.CompleteWorkflowExecutionDecisionAttributes != nil && rhs.CompleteWorkflowExecutionDecisionAttributes != nil && v.CompleteWorkflowExecutionDecisionAttributes.Equals(rhs.CompleteWorkflowExecutionDecisionAttributes))) { return false } if !((v.FailWorkflowExecutionDecisionAttributes == nil && rhs.FailWorkflowExecutionDecisionAttributes == nil) || (v.FailWorkflowExecutionDecisionAttributes != nil && rhs.FailWorkflowExecutionDecisionAttributes != nil && v.FailWorkflowExecutionDecisionAttributes.Equals(rhs.FailWorkflowExecutionDecisionAttributes))) { return false } if !((v.RequestCancelActivityTaskDecisionAttributes == nil && rhs.RequestCancelActivityTaskDecisionAttributes == nil) || (v.RequestCancelActivityTaskDecisionAttributes != nil && rhs.RequestCancelActivityTaskDecisionAttributes != nil && v.RequestCancelActivityTaskDecisionAttributes.Equals(rhs.RequestCancelActivityTaskDecisionAttributes))) { return false } if !((v.CancelTimerDecisionAttributes == nil && rhs.CancelTimerDecisionAttributes == nil) || (v.CancelTimerDecisionAttributes != nil && rhs.CancelTimerDecisionAttributes != nil && v.CancelTimerDecisionAttributes.Equals(rhs.CancelTimerDecisionAttributes))) { return false } if !((v.CancelWorkflowExecutionDecisionAttributes == nil && rhs.CancelWorkflowExecutionDecisionAttributes == nil) || (v.CancelWorkflowExecutionDecisionAttributes != nil && rhs.CancelWorkflowExecutionDecisionAttributes != nil && v.CancelWorkflowExecutionDecisionAttributes.Equals(rhs.CancelWorkflowExecutionDecisionAttributes))) { return false } if !((v.RequestCancelExternalWorkflowExecutionDecisionAttributes == nil && rhs.RequestCancelExternalWorkflowExecutionDecisionAttributes == nil) || (v.RequestCancelExternalWorkflowExecutionDecisionAttributes != nil && rhs.RequestCancelExternalWorkflowExecutionDecisionAttributes != nil && v.RequestCancelExternalWorkflowExecutionDecisionAttributes.Equals(rhs.RequestCancelExternalWorkflowExecutionDecisionAttributes))) { return false } if !((v.RecordMarkerDecisionAttributes == nil && rhs.RecordMarkerDecisionAttributes == nil) || (v.RecordMarkerDecisionAttributes != nil && rhs.RecordMarkerDecisionAttributes != nil && v.RecordMarkerDecisionAttributes.Equals(rhs.RecordMarkerDecisionAttributes))) { return false } if !((v.ContinueAsNewWorkflowExecutionDecisionAttributes == nil && rhs.ContinueAsNewWorkflowExecutionDecisionAttributes == nil) || (v.ContinueAsNewWorkflowExecutionDecisionAttributes != nil && rhs.ContinueAsNewWorkflowExecutionDecisionAttributes != nil && v.ContinueAsNewWorkflowExecutionDecisionAttributes.Equals(rhs.ContinueAsNewWorkflowExecutionDecisionAttributes))) { return false } if !((v.StartChildWorkflowExecutionDecisionAttributes == nil && rhs.StartChildWorkflowExecutionDecisionAttributes == nil) || (v.StartChildWorkflowExecutionDecisionAttributes != nil && rhs.StartChildWorkflowExecutionDecisionAttributes != nil && v.StartChildWorkflowExecutionDecisionAttributes.Equals(rhs.StartChildWorkflowExecutionDecisionAttributes))) { return false } if !((v.SignalExternalWorkflowExecutionDecisionAttributes == nil && rhs.SignalExternalWorkflowExecutionDecisionAttributes == nil) || (v.SignalExternalWorkflowExecutionDecisionAttributes != nil && rhs.SignalExternalWorkflowExecutionDecisionAttributes != nil && v.SignalExternalWorkflowExecutionDecisionAttributes.Equals(rhs.SignalExternalWorkflowExecutionDecisionAttributes))) { return false } if !((v.UpsertWorkflowSearchAttributesDecisionAttributes == nil && rhs.UpsertWorkflowSearchAttributesDecisionAttributes == nil) || (v.UpsertWorkflowSearchAttributesDecisionAttributes != nil && rhs.UpsertWorkflowSearchAttributesDecisionAttributes != nil && v.UpsertWorkflowSearchAttributesDecisionAttributes.Equals(rhs.UpsertWorkflowSearchAttributesDecisionAttributes))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of Decision. func (v *Decision) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DecisionType != nil { err = multierr.Append(err, enc.AddObject("decisionType", *v.DecisionType)) } if v.ScheduleActivityTaskDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("scheduleActivityTaskDecisionAttributes", v.ScheduleActivityTaskDecisionAttributes)) } if v.StartTimerDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("startTimerDecisionAttributes", v.StartTimerDecisionAttributes)) } if v.CompleteWorkflowExecutionDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("completeWorkflowExecutionDecisionAttributes", v.CompleteWorkflowExecutionDecisionAttributes)) } if v.FailWorkflowExecutionDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("failWorkflowExecutionDecisionAttributes", v.FailWorkflowExecutionDecisionAttributes)) } if v.RequestCancelActivityTaskDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("requestCancelActivityTaskDecisionAttributes", v.RequestCancelActivityTaskDecisionAttributes)) } if v.CancelTimerDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("cancelTimerDecisionAttributes", v.CancelTimerDecisionAttributes)) } if v.CancelWorkflowExecutionDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("cancelWorkflowExecutionDecisionAttributes", v.CancelWorkflowExecutionDecisionAttributes)) } if v.RequestCancelExternalWorkflowExecutionDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("requestCancelExternalWorkflowExecutionDecisionAttributes", v.RequestCancelExternalWorkflowExecutionDecisionAttributes)) } if v.RecordMarkerDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("recordMarkerDecisionAttributes", v.RecordMarkerDecisionAttributes)) } if v.ContinueAsNewWorkflowExecutionDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("continueAsNewWorkflowExecutionDecisionAttributes", v.ContinueAsNewWorkflowExecutionDecisionAttributes)) } if v.StartChildWorkflowExecutionDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("startChildWorkflowExecutionDecisionAttributes", v.StartChildWorkflowExecutionDecisionAttributes)) } if v.SignalExternalWorkflowExecutionDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("signalExternalWorkflowExecutionDecisionAttributes", v.SignalExternalWorkflowExecutionDecisionAttributes)) } if v.UpsertWorkflowSearchAttributesDecisionAttributes != nil { err = multierr.Append(err, enc.AddObject("upsertWorkflowSearchAttributesDecisionAttributes", v.UpsertWorkflowSearchAttributesDecisionAttributes)) } return err } // GetDecisionType returns the value of DecisionType if it is set or its // zero value if it is unset. func (v *Decision) GetDecisionType() (o DecisionType) { if v != nil && v.DecisionType != nil { return *v.DecisionType } return } // IsSetDecisionType returns true if DecisionType is not nil. func (v *Decision) IsSetDecisionType() bool { return v != nil && v.DecisionType != nil } // GetScheduleActivityTaskDecisionAttributes returns the value of ScheduleActivityTaskDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetScheduleActivityTaskDecisionAttributes() (o *ScheduleActivityTaskDecisionAttributes) { if v != nil && v.ScheduleActivityTaskDecisionAttributes != nil { return v.ScheduleActivityTaskDecisionAttributes } return } // IsSetScheduleActivityTaskDecisionAttributes returns true if ScheduleActivityTaskDecisionAttributes is not nil. func (v *Decision) IsSetScheduleActivityTaskDecisionAttributes() bool { return v != nil && v.ScheduleActivityTaskDecisionAttributes != nil } // GetStartTimerDecisionAttributes returns the value of StartTimerDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetStartTimerDecisionAttributes() (o *StartTimerDecisionAttributes) { if v != nil && v.StartTimerDecisionAttributes != nil { return v.StartTimerDecisionAttributes } return } // IsSetStartTimerDecisionAttributes returns true if StartTimerDecisionAttributes is not nil. func (v *Decision) IsSetStartTimerDecisionAttributes() bool { return v != nil && v.StartTimerDecisionAttributes != nil } // GetCompleteWorkflowExecutionDecisionAttributes returns the value of CompleteWorkflowExecutionDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetCompleteWorkflowExecutionDecisionAttributes() (o *CompleteWorkflowExecutionDecisionAttributes) { if v != nil && v.CompleteWorkflowExecutionDecisionAttributes != nil { return v.CompleteWorkflowExecutionDecisionAttributes } return } // IsSetCompleteWorkflowExecutionDecisionAttributes returns true if CompleteWorkflowExecutionDecisionAttributes is not nil. func (v *Decision) IsSetCompleteWorkflowExecutionDecisionAttributes() bool { return v != nil && v.CompleteWorkflowExecutionDecisionAttributes != nil } // GetFailWorkflowExecutionDecisionAttributes returns the value of FailWorkflowExecutionDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetFailWorkflowExecutionDecisionAttributes() (o *FailWorkflowExecutionDecisionAttributes) { if v != nil && v.FailWorkflowExecutionDecisionAttributes != nil { return v.FailWorkflowExecutionDecisionAttributes } return } // IsSetFailWorkflowExecutionDecisionAttributes returns true if FailWorkflowExecutionDecisionAttributes is not nil. func (v *Decision) IsSetFailWorkflowExecutionDecisionAttributes() bool { return v != nil && v.FailWorkflowExecutionDecisionAttributes != nil } // GetRequestCancelActivityTaskDecisionAttributes returns the value of RequestCancelActivityTaskDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetRequestCancelActivityTaskDecisionAttributes() (o *RequestCancelActivityTaskDecisionAttributes) { if v != nil && v.RequestCancelActivityTaskDecisionAttributes != nil { return v.RequestCancelActivityTaskDecisionAttributes } return } // IsSetRequestCancelActivityTaskDecisionAttributes returns true if RequestCancelActivityTaskDecisionAttributes is not nil. func (v *Decision) IsSetRequestCancelActivityTaskDecisionAttributes() bool { return v != nil && v.RequestCancelActivityTaskDecisionAttributes != nil } // GetCancelTimerDecisionAttributes returns the value of CancelTimerDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetCancelTimerDecisionAttributes() (o *CancelTimerDecisionAttributes) { if v != nil && v.CancelTimerDecisionAttributes != nil { return v.CancelTimerDecisionAttributes } return } // IsSetCancelTimerDecisionAttributes returns true if CancelTimerDecisionAttributes is not nil. func (v *Decision) IsSetCancelTimerDecisionAttributes() bool { return v != nil && v.CancelTimerDecisionAttributes != nil } // GetCancelWorkflowExecutionDecisionAttributes returns the value of CancelWorkflowExecutionDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetCancelWorkflowExecutionDecisionAttributes() (o *CancelWorkflowExecutionDecisionAttributes) { if v != nil && v.CancelWorkflowExecutionDecisionAttributes != nil { return v.CancelWorkflowExecutionDecisionAttributes } return } // IsSetCancelWorkflowExecutionDecisionAttributes returns true if CancelWorkflowExecutionDecisionAttributes is not nil. func (v *Decision) IsSetCancelWorkflowExecutionDecisionAttributes() bool { return v != nil && v.CancelWorkflowExecutionDecisionAttributes != nil } // GetRequestCancelExternalWorkflowExecutionDecisionAttributes returns the value of RequestCancelExternalWorkflowExecutionDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetRequestCancelExternalWorkflowExecutionDecisionAttributes() (o *RequestCancelExternalWorkflowExecutionDecisionAttributes) { if v != nil && v.RequestCancelExternalWorkflowExecutionDecisionAttributes != nil { return v.RequestCancelExternalWorkflowExecutionDecisionAttributes } return } // IsSetRequestCancelExternalWorkflowExecutionDecisionAttributes returns true if RequestCancelExternalWorkflowExecutionDecisionAttributes is not nil. func (v *Decision) IsSetRequestCancelExternalWorkflowExecutionDecisionAttributes() bool { return v != nil && v.RequestCancelExternalWorkflowExecutionDecisionAttributes != nil } // GetRecordMarkerDecisionAttributes returns the value of RecordMarkerDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetRecordMarkerDecisionAttributes() (o *RecordMarkerDecisionAttributes) { if v != nil && v.RecordMarkerDecisionAttributes != nil { return v.RecordMarkerDecisionAttributes } return } // IsSetRecordMarkerDecisionAttributes returns true if RecordMarkerDecisionAttributes is not nil. func (v *Decision) IsSetRecordMarkerDecisionAttributes() bool { return v != nil && v.RecordMarkerDecisionAttributes != nil } // GetContinueAsNewWorkflowExecutionDecisionAttributes returns the value of ContinueAsNewWorkflowExecutionDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetContinueAsNewWorkflowExecutionDecisionAttributes() (o *ContinueAsNewWorkflowExecutionDecisionAttributes) { if v != nil && v.ContinueAsNewWorkflowExecutionDecisionAttributes != nil { return v.ContinueAsNewWorkflowExecutionDecisionAttributes } return } // IsSetContinueAsNewWorkflowExecutionDecisionAttributes returns true if ContinueAsNewWorkflowExecutionDecisionAttributes is not nil. func (v *Decision) IsSetContinueAsNewWorkflowExecutionDecisionAttributes() bool { return v != nil && v.ContinueAsNewWorkflowExecutionDecisionAttributes != nil } // GetStartChildWorkflowExecutionDecisionAttributes returns the value of StartChildWorkflowExecutionDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetStartChildWorkflowExecutionDecisionAttributes() (o *StartChildWorkflowExecutionDecisionAttributes) { if v != nil && v.StartChildWorkflowExecutionDecisionAttributes != nil { return v.StartChildWorkflowExecutionDecisionAttributes } return } // IsSetStartChildWorkflowExecutionDecisionAttributes returns true if StartChildWorkflowExecutionDecisionAttributes is not nil. func (v *Decision) IsSetStartChildWorkflowExecutionDecisionAttributes() bool { return v != nil && v.StartChildWorkflowExecutionDecisionAttributes != nil } // GetSignalExternalWorkflowExecutionDecisionAttributes returns the value of SignalExternalWorkflowExecutionDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetSignalExternalWorkflowExecutionDecisionAttributes() (o *SignalExternalWorkflowExecutionDecisionAttributes) { if v != nil && v.SignalExternalWorkflowExecutionDecisionAttributes != nil { return v.SignalExternalWorkflowExecutionDecisionAttributes } return } // IsSetSignalExternalWorkflowExecutionDecisionAttributes returns true if SignalExternalWorkflowExecutionDecisionAttributes is not nil. func (v *Decision) IsSetSignalExternalWorkflowExecutionDecisionAttributes() bool { return v != nil && v.SignalExternalWorkflowExecutionDecisionAttributes != nil } // GetUpsertWorkflowSearchAttributesDecisionAttributes returns the value of UpsertWorkflowSearchAttributesDecisionAttributes if it is set or its // zero value if it is unset. func (v *Decision) GetUpsertWorkflowSearchAttributesDecisionAttributes() (o *UpsertWorkflowSearchAttributesDecisionAttributes) { if v != nil && v.UpsertWorkflowSearchAttributesDecisionAttributes != nil { return v.UpsertWorkflowSearchAttributesDecisionAttributes } return } // IsSetUpsertWorkflowSearchAttributesDecisionAttributes returns true if UpsertWorkflowSearchAttributesDecisionAttributes is not nil. func (v *Decision) IsSetUpsertWorkflowSearchAttributesDecisionAttributes() bool { return v != nil && v.UpsertWorkflowSearchAttributesDecisionAttributes != nil } type DecisionTaskCompletedEventAttributes struct { ExecutionContext []byte `json:"executionContext,omitempty"` ScheduledEventId *int64 `json:"scheduledEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` Identity *string `json:"identity,omitempty"` BinaryChecksum *string `json:"binaryChecksum,omitempty"` } // ToWire translates a DecisionTaskCompletedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DecisionTaskCompletedEventAttributes) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.ExecutionContext != nil { w, err = wire.NewValueBinary(v.ExecutionContext), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ScheduledEventId != nil { w, err = wire.NewValueI64(*(v.ScheduledEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.BinaryChecksum != nil { w, err = wire.NewValueString(*(v.BinaryChecksum)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DecisionTaskCompletedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DecisionTaskCompletedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DecisionTaskCompletedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DecisionTaskCompletedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.ExecutionContext, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BinaryChecksum = &x if err != nil { return err } } } } return nil } // Encode serializes a DecisionTaskCompletedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DecisionTaskCompletedEventAttributes struct could not be encoded. func (v *DecisionTaskCompletedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ExecutionContext != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.ExecutionContext); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BinaryChecksum != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BinaryChecksum)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DecisionTaskCompletedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DecisionTaskCompletedEventAttributes struct could not be generated from the wire // representation. func (v *DecisionTaskCompletedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.ExecutionContext, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BinaryChecksum = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DecisionTaskCompletedEventAttributes // struct. func (v *DecisionTaskCompletedEventAttributes) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.ExecutionContext != nil { fields[i] = fmt.Sprintf("ExecutionContext: %v", v.ExecutionContext) i++ } if v.ScheduledEventId != nil { fields[i] = fmt.Sprintf("ScheduledEventId: %v", *(v.ScheduledEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.BinaryChecksum != nil { fields[i] = fmt.Sprintf("BinaryChecksum: %v", *(v.BinaryChecksum)) i++ } return fmt.Sprintf("DecisionTaskCompletedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DecisionTaskCompletedEventAttributes match the // provided DecisionTaskCompletedEventAttributes. // // This function performs a deep comparison. func (v *DecisionTaskCompletedEventAttributes) Equals(rhs *DecisionTaskCompletedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ExecutionContext == nil && rhs.ExecutionContext == nil) || (v.ExecutionContext != nil && rhs.ExecutionContext != nil && bytes.Equal(v.ExecutionContext, rhs.ExecutionContext))) { return false } if !_I64_EqualsPtr(v.ScheduledEventId, rhs.ScheduledEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.BinaryChecksum, rhs.BinaryChecksum) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DecisionTaskCompletedEventAttributes. func (v *DecisionTaskCompletedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ExecutionContext != nil { enc.AddString("executionContext", base64.StdEncoding.EncodeToString(v.ExecutionContext)) } if v.ScheduledEventId != nil { enc.AddInt64("scheduledEventId", *v.ScheduledEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.BinaryChecksum != nil { enc.AddString("binaryChecksum", *v.BinaryChecksum) } return err } // GetExecutionContext returns the value of ExecutionContext if it is set or its // zero value if it is unset. func (v *DecisionTaskCompletedEventAttributes) GetExecutionContext() (o []byte) { if v != nil && v.ExecutionContext != nil { return v.ExecutionContext } return } // IsSetExecutionContext returns true if ExecutionContext is not nil. func (v *DecisionTaskCompletedEventAttributes) IsSetExecutionContext() bool { return v != nil && v.ExecutionContext != nil } // GetScheduledEventId returns the value of ScheduledEventId if it is set or its // zero value if it is unset. func (v *DecisionTaskCompletedEventAttributes) GetScheduledEventId() (o int64) { if v != nil && v.ScheduledEventId != nil { return *v.ScheduledEventId } return } // IsSetScheduledEventId returns true if ScheduledEventId is not nil. func (v *DecisionTaskCompletedEventAttributes) IsSetScheduledEventId() bool { return v != nil && v.ScheduledEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *DecisionTaskCompletedEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *DecisionTaskCompletedEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *DecisionTaskCompletedEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *DecisionTaskCompletedEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetBinaryChecksum returns the value of BinaryChecksum if it is set or its // zero value if it is unset. func (v *DecisionTaskCompletedEventAttributes) GetBinaryChecksum() (o string) { if v != nil && v.BinaryChecksum != nil { return *v.BinaryChecksum } return } // IsSetBinaryChecksum returns true if BinaryChecksum is not nil. func (v *DecisionTaskCompletedEventAttributes) IsSetBinaryChecksum() bool { return v != nil && v.BinaryChecksum != nil } type DecisionTaskFailedCause int32 const ( DecisionTaskFailedCauseUnhandledDecision DecisionTaskFailedCause = 0 DecisionTaskFailedCauseBadScheduleActivityAttributes DecisionTaskFailedCause = 1 DecisionTaskFailedCauseBadRequestCancelActivityAttributes DecisionTaskFailedCause = 2 DecisionTaskFailedCauseBadStartTimerAttributes DecisionTaskFailedCause = 3 DecisionTaskFailedCauseBadCancelTimerAttributes DecisionTaskFailedCause = 4 DecisionTaskFailedCauseBadRecordMarkerAttributes DecisionTaskFailedCause = 5 DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes DecisionTaskFailedCause = 6 DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes DecisionTaskFailedCause = 7 DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes DecisionTaskFailedCause = 8 DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes DecisionTaskFailedCause = 9 DecisionTaskFailedCauseBadContinueAsNewAttributes DecisionTaskFailedCause = 10 DecisionTaskFailedCauseStartTimerDuplicateID DecisionTaskFailedCause = 11 DecisionTaskFailedCauseResetStickyTasklist DecisionTaskFailedCause = 12 DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure DecisionTaskFailedCause = 13 DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes DecisionTaskFailedCause = 14 DecisionTaskFailedCauseBadStartChildExecutionAttributes DecisionTaskFailedCause = 15 DecisionTaskFailedCauseForceCloseDecision DecisionTaskFailedCause = 16 DecisionTaskFailedCauseFailoverCloseDecision DecisionTaskFailedCause = 17 DecisionTaskFailedCauseBadSignalInputSize DecisionTaskFailedCause = 18 DecisionTaskFailedCauseResetWorkflow DecisionTaskFailedCause = 19 DecisionTaskFailedCauseBadBinary DecisionTaskFailedCause = 20 DecisionTaskFailedCauseScheduleActivityDuplicateID DecisionTaskFailedCause = 21 DecisionTaskFailedCauseBadSearchAttributes DecisionTaskFailedCause = 22 ) // DecisionTaskFailedCause_Values returns all recognized values of DecisionTaskFailedCause. func DecisionTaskFailedCause_Values() []DecisionTaskFailedCause { return []DecisionTaskFailedCause{ DecisionTaskFailedCauseUnhandledDecision, DecisionTaskFailedCauseBadScheduleActivityAttributes, DecisionTaskFailedCauseBadRequestCancelActivityAttributes, DecisionTaskFailedCauseBadStartTimerAttributes, DecisionTaskFailedCauseBadCancelTimerAttributes, DecisionTaskFailedCauseBadRecordMarkerAttributes, DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes, DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes, DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes, DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes, DecisionTaskFailedCauseBadContinueAsNewAttributes, DecisionTaskFailedCauseStartTimerDuplicateID, DecisionTaskFailedCauseResetStickyTasklist, DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure, DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes, DecisionTaskFailedCauseBadStartChildExecutionAttributes, DecisionTaskFailedCauseForceCloseDecision, DecisionTaskFailedCauseFailoverCloseDecision, DecisionTaskFailedCauseBadSignalInputSize, DecisionTaskFailedCauseResetWorkflow, DecisionTaskFailedCauseBadBinary, DecisionTaskFailedCauseScheduleActivityDuplicateID, DecisionTaskFailedCauseBadSearchAttributes, } } // UnmarshalText tries to decode DecisionTaskFailedCause from a byte slice // containing its name. // // var v DecisionTaskFailedCause // err := v.UnmarshalText([]byte("UNHANDLED_DECISION")) func (v *DecisionTaskFailedCause) UnmarshalText(value []byte) error { switch s := string(value); s { case "UNHANDLED_DECISION": *v = DecisionTaskFailedCauseUnhandledDecision return nil case "BAD_SCHEDULE_ACTIVITY_ATTRIBUTES": *v = DecisionTaskFailedCauseBadScheduleActivityAttributes return nil case "BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES": *v = DecisionTaskFailedCauseBadRequestCancelActivityAttributes return nil case "BAD_START_TIMER_ATTRIBUTES": *v = DecisionTaskFailedCauseBadStartTimerAttributes return nil case "BAD_CANCEL_TIMER_ATTRIBUTES": *v = DecisionTaskFailedCauseBadCancelTimerAttributes return nil case "BAD_RECORD_MARKER_ATTRIBUTES": *v = DecisionTaskFailedCauseBadRecordMarkerAttributes return nil case "BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES": *v = DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes return nil case "BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES": *v = DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes return nil case "BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES": *v = DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes return nil case "BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES": *v = DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes return nil case "BAD_CONTINUE_AS_NEW_ATTRIBUTES": *v = DecisionTaskFailedCauseBadContinueAsNewAttributes return nil case "START_TIMER_DUPLICATE_ID": *v = DecisionTaskFailedCauseStartTimerDuplicateID return nil case "RESET_STICKY_TASKLIST": *v = DecisionTaskFailedCauseResetStickyTasklist return nil case "WORKFLOW_WORKER_UNHANDLED_FAILURE": *v = DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure return nil case "BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES": *v = DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes return nil case "BAD_START_CHILD_EXECUTION_ATTRIBUTES": *v = DecisionTaskFailedCauseBadStartChildExecutionAttributes return nil case "FORCE_CLOSE_DECISION": *v = DecisionTaskFailedCauseForceCloseDecision return nil case "FAILOVER_CLOSE_DECISION": *v = DecisionTaskFailedCauseFailoverCloseDecision return nil case "BAD_SIGNAL_INPUT_SIZE": *v = DecisionTaskFailedCauseBadSignalInputSize return nil case "RESET_WORKFLOW": *v = DecisionTaskFailedCauseResetWorkflow return nil case "BAD_BINARY": *v = DecisionTaskFailedCauseBadBinary return nil case "SCHEDULE_ACTIVITY_DUPLICATE_ID": *v = DecisionTaskFailedCauseScheduleActivityDuplicateID return nil case "BAD_SEARCH_ATTRIBUTES": *v = DecisionTaskFailedCauseBadSearchAttributes return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DecisionTaskFailedCause", err) } *v = DecisionTaskFailedCause(val) return nil } } // MarshalText encodes DecisionTaskFailedCause to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v DecisionTaskFailedCause) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("UNHANDLED_DECISION"), nil case 1: return []byte("BAD_SCHEDULE_ACTIVITY_ATTRIBUTES"), nil case 2: return []byte("BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES"), nil case 3: return []byte("BAD_START_TIMER_ATTRIBUTES"), nil case 4: return []byte("BAD_CANCEL_TIMER_ATTRIBUTES"), nil case 5: return []byte("BAD_RECORD_MARKER_ATTRIBUTES"), nil case 6: return []byte("BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES"), nil case 7: return []byte("BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES"), nil case 8: return []byte("BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES"), nil case 9: return []byte("BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES"), nil case 10: return []byte("BAD_CONTINUE_AS_NEW_ATTRIBUTES"), nil case 11: return []byte("START_TIMER_DUPLICATE_ID"), nil case 12: return []byte("RESET_STICKY_TASKLIST"), nil case 13: return []byte("WORKFLOW_WORKER_UNHANDLED_FAILURE"), nil case 14: return []byte("BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES"), nil case 15: return []byte("BAD_START_CHILD_EXECUTION_ATTRIBUTES"), nil case 16: return []byte("FORCE_CLOSE_DECISION"), nil case 17: return []byte("FAILOVER_CLOSE_DECISION"), nil case 18: return []byte("BAD_SIGNAL_INPUT_SIZE"), nil case 19: return []byte("RESET_WORKFLOW"), nil case 20: return []byte("BAD_BINARY"), nil case 21: return []byte("SCHEDULE_ACTIVITY_DUPLICATE_ID"), nil case 22: return []byte("BAD_SEARCH_ATTRIBUTES"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DecisionTaskFailedCause. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v DecisionTaskFailedCause) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "UNHANDLED_DECISION") case 1: enc.AddString("name", "BAD_SCHEDULE_ACTIVITY_ATTRIBUTES") case 2: enc.AddString("name", "BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES") case 3: enc.AddString("name", "BAD_START_TIMER_ATTRIBUTES") case 4: enc.AddString("name", "BAD_CANCEL_TIMER_ATTRIBUTES") case 5: enc.AddString("name", "BAD_RECORD_MARKER_ATTRIBUTES") case 6: enc.AddString("name", "BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES") case 7: enc.AddString("name", "BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES") case 8: enc.AddString("name", "BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES") case 9: enc.AddString("name", "BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES") case 10: enc.AddString("name", "BAD_CONTINUE_AS_NEW_ATTRIBUTES") case 11: enc.AddString("name", "START_TIMER_DUPLICATE_ID") case 12: enc.AddString("name", "RESET_STICKY_TASKLIST") case 13: enc.AddString("name", "WORKFLOW_WORKER_UNHANDLED_FAILURE") case 14: enc.AddString("name", "BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES") case 15: enc.AddString("name", "BAD_START_CHILD_EXECUTION_ATTRIBUTES") case 16: enc.AddString("name", "FORCE_CLOSE_DECISION") case 17: enc.AddString("name", "FAILOVER_CLOSE_DECISION") case 18: enc.AddString("name", "BAD_SIGNAL_INPUT_SIZE") case 19: enc.AddString("name", "RESET_WORKFLOW") case 20: enc.AddString("name", "BAD_BINARY") case 21: enc.AddString("name", "SCHEDULE_ACTIVITY_DUPLICATE_ID") case 22: enc.AddString("name", "BAD_SEARCH_ATTRIBUTES") } return nil } // Ptr returns a pointer to this enum value. func (v DecisionTaskFailedCause) Ptr() *DecisionTaskFailedCause { return &v } // Encode encodes DecisionTaskFailedCause directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v DecisionTaskFailedCause // return v.Encode(sWriter) func (v DecisionTaskFailedCause) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates DecisionTaskFailedCause into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v DecisionTaskFailedCause) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes DecisionTaskFailedCause from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return DecisionTaskFailedCause(0), err // } // // var v DecisionTaskFailedCause // if err := v.FromWire(x); err != nil { // return DecisionTaskFailedCause(0), err // } // return v, nil func (v *DecisionTaskFailedCause) FromWire(w wire.Value) error { *v = (DecisionTaskFailedCause)(w.GetI32()) return nil } // Decode reads off the encoded DecisionTaskFailedCause directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v DecisionTaskFailedCause // if err := v.Decode(sReader); err != nil { // return DecisionTaskFailedCause(0), err // } // return v, nil func (v *DecisionTaskFailedCause) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (DecisionTaskFailedCause)(i) return nil } // String returns a readable string representation of DecisionTaskFailedCause. func (v DecisionTaskFailedCause) String() string { w := int32(v) switch w { case 0: return "UNHANDLED_DECISION" case 1: return "BAD_SCHEDULE_ACTIVITY_ATTRIBUTES" case 2: return "BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES" case 3: return "BAD_START_TIMER_ATTRIBUTES" case 4: return "BAD_CANCEL_TIMER_ATTRIBUTES" case 5: return "BAD_RECORD_MARKER_ATTRIBUTES" case 6: return "BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES" case 7: return "BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES" case 8: return "BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES" case 9: return "BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES" case 10: return "BAD_CONTINUE_AS_NEW_ATTRIBUTES" case 11: return "START_TIMER_DUPLICATE_ID" case 12: return "RESET_STICKY_TASKLIST" case 13: return "WORKFLOW_WORKER_UNHANDLED_FAILURE" case 14: return "BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES" case 15: return "BAD_START_CHILD_EXECUTION_ATTRIBUTES" case 16: return "FORCE_CLOSE_DECISION" case 17: return "FAILOVER_CLOSE_DECISION" case 18: return "BAD_SIGNAL_INPUT_SIZE" case 19: return "RESET_WORKFLOW" case 20: return "BAD_BINARY" case 21: return "SCHEDULE_ACTIVITY_DUPLICATE_ID" case 22: return "BAD_SEARCH_ATTRIBUTES" } return fmt.Sprintf("DecisionTaskFailedCause(%d)", w) } // Equals returns true if this DecisionTaskFailedCause value matches the provided // value. func (v DecisionTaskFailedCause) Equals(rhs DecisionTaskFailedCause) bool { return v == rhs } // MarshalJSON serializes DecisionTaskFailedCause into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v DecisionTaskFailedCause) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"UNHANDLED_DECISION\""), nil case 1: return ([]byte)("\"BAD_SCHEDULE_ACTIVITY_ATTRIBUTES\""), nil case 2: return ([]byte)("\"BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES\""), nil case 3: return ([]byte)("\"BAD_START_TIMER_ATTRIBUTES\""), nil case 4: return ([]byte)("\"BAD_CANCEL_TIMER_ATTRIBUTES\""), nil case 5: return ([]byte)("\"BAD_RECORD_MARKER_ATTRIBUTES\""), nil case 6: return ([]byte)("\"BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES\""), nil case 7: return ([]byte)("\"BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES\""), nil case 8: return ([]byte)("\"BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES\""), nil case 9: return ([]byte)("\"BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES\""), nil case 10: return ([]byte)("\"BAD_CONTINUE_AS_NEW_ATTRIBUTES\""), nil case 11: return ([]byte)("\"START_TIMER_DUPLICATE_ID\""), nil case 12: return ([]byte)("\"RESET_STICKY_TASKLIST\""), nil case 13: return ([]byte)("\"WORKFLOW_WORKER_UNHANDLED_FAILURE\""), nil case 14: return ([]byte)("\"BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES\""), nil case 15: return ([]byte)("\"BAD_START_CHILD_EXECUTION_ATTRIBUTES\""), nil case 16: return ([]byte)("\"FORCE_CLOSE_DECISION\""), nil case 17: return ([]byte)("\"FAILOVER_CLOSE_DECISION\""), nil case 18: return ([]byte)("\"BAD_SIGNAL_INPUT_SIZE\""), nil case 19: return ([]byte)("\"RESET_WORKFLOW\""), nil case 20: return ([]byte)("\"BAD_BINARY\""), nil case 21: return ([]byte)("\"SCHEDULE_ACTIVITY_DUPLICATE_ID\""), nil case 22: return ([]byte)("\"BAD_SEARCH_ATTRIBUTES\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode DecisionTaskFailedCause from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *DecisionTaskFailedCause) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "DecisionTaskFailedCause") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "DecisionTaskFailedCause") } *v = (DecisionTaskFailedCause)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "DecisionTaskFailedCause") } } type DecisionTaskFailedEventAttributes struct { ScheduledEventId *int64 `json:"scheduledEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` Cause *DecisionTaskFailedCause `json:"cause,omitempty"` Details []byte `json:"details,omitempty"` Identity *string `json:"identity,omitempty"` Reason *string `json:"reason,omitempty"` BaseRunId *string `json:"baseRunId,omitempty"` NewRunId *string `json:"newRunId,omitempty"` ForkEventVersion *int64 `json:"forkEventVersion,omitempty"` BinaryChecksum *string `json:"binaryChecksum,omitempty"` RequestId *string `json:"requestId,omitempty"` } // ToWire translates a DecisionTaskFailedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DecisionTaskFailedEventAttributes) ToWire() (wire.Value, error) { var ( fields [11]wire.Field i int = 0 w wire.Value err error ) if v.ScheduledEventId != nil { w, err = wire.NewValueI64(*(v.ScheduledEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Cause != nil { w, err = v.Cause.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 35, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.BaseRunId != nil { w, err = wire.NewValueString(*(v.BaseRunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.NewRunId != nil { w, err = wire.NewValueString(*(v.NewRunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.ForkEventVersion != nil { w, err = wire.NewValueI64(*(v.ForkEventVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.BinaryChecksum != nil { w, err = wire.NewValueString(*(v.BinaryChecksum)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DecisionTaskFailedCause_Read(w wire.Value) (DecisionTaskFailedCause, error) { var v DecisionTaskFailedCause err := v.FromWire(w) return v, err } // FromWire deserializes a DecisionTaskFailedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DecisionTaskFailedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DecisionTaskFailedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DecisionTaskFailedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x DecisionTaskFailedCause x, err = _DecisionTaskFailedCause_Read(field.Value) v.Cause = &x if err != nil { return err } } case 35: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BaseRunId = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.NewRunId = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ForkEventVersion = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BinaryChecksum = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } } } return nil } // Encode serializes a DecisionTaskFailedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DecisionTaskFailedEventAttributes struct could not be encoded. func (v *DecisionTaskFailedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ScheduledEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Cause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := v.Cause.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 35, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BaseRunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BaseRunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NewRunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.NewRunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ForkEventVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ForkEventVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BinaryChecksum != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BinaryChecksum)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DecisionTaskFailedCause_Decode(sr stream.Reader) (DecisionTaskFailedCause, error) { var v DecisionTaskFailedCause err := v.Decode(sr) return v, err } // Decode deserializes a DecisionTaskFailedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DecisionTaskFailedEventAttributes struct could not be generated from the wire // representation. func (v *DecisionTaskFailedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x DecisionTaskFailedCause x, err = _DecisionTaskFailedCause_Decode(sr) v.Cause = &x if err != nil { return err } case fh.ID == 35 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BaseRunId = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.NewRunId = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ForkEventVersion = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BinaryChecksum = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DecisionTaskFailedEventAttributes // struct. func (v *DecisionTaskFailedEventAttributes) String() string { if v == nil { return "" } var fields [11]string i := 0 if v.ScheduledEventId != nil { fields[i] = fmt.Sprintf("ScheduledEventId: %v", *(v.ScheduledEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.Cause != nil { fields[i] = fmt.Sprintf("Cause: %v", *(v.Cause)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.BaseRunId != nil { fields[i] = fmt.Sprintf("BaseRunId: %v", *(v.BaseRunId)) i++ } if v.NewRunId != nil { fields[i] = fmt.Sprintf("NewRunId: %v", *(v.NewRunId)) i++ } if v.ForkEventVersion != nil { fields[i] = fmt.Sprintf("ForkEventVersion: %v", *(v.ForkEventVersion)) i++ } if v.BinaryChecksum != nil { fields[i] = fmt.Sprintf("BinaryChecksum: %v", *(v.BinaryChecksum)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } return fmt.Sprintf("DecisionTaskFailedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } func _DecisionTaskFailedCause_EqualsPtr(lhs, rhs *DecisionTaskFailedCause) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this DecisionTaskFailedEventAttributes match the // provided DecisionTaskFailedEventAttributes. // // This function performs a deep comparison. func (v *DecisionTaskFailedEventAttributes) Equals(rhs *DecisionTaskFailedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.ScheduledEventId, rhs.ScheduledEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_DecisionTaskFailedCause_EqualsPtr(v.Cause, rhs.Cause) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !_String_EqualsPtr(v.BaseRunId, rhs.BaseRunId) { return false } if !_String_EqualsPtr(v.NewRunId, rhs.NewRunId) { return false } if !_I64_EqualsPtr(v.ForkEventVersion, rhs.ForkEventVersion) { return false } if !_String_EqualsPtr(v.BinaryChecksum, rhs.BinaryChecksum) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DecisionTaskFailedEventAttributes. func (v *DecisionTaskFailedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ScheduledEventId != nil { enc.AddInt64("scheduledEventId", *v.ScheduledEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.Cause != nil { err = multierr.Append(err, enc.AddObject("cause", *v.Cause)) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.BaseRunId != nil { enc.AddString("baseRunId", *v.BaseRunId) } if v.NewRunId != nil { enc.AddString("newRunId", *v.NewRunId) } if v.ForkEventVersion != nil { enc.AddInt64("forkEventVersion", *v.ForkEventVersion) } if v.BinaryChecksum != nil { enc.AddString("binaryChecksum", *v.BinaryChecksum) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } return err } // GetScheduledEventId returns the value of ScheduledEventId if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetScheduledEventId() (o int64) { if v != nil && v.ScheduledEventId != nil { return *v.ScheduledEventId } return } // IsSetScheduledEventId returns true if ScheduledEventId is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetScheduledEventId() bool { return v != nil && v.ScheduledEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetCause returns the value of Cause if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetCause() (o DecisionTaskFailedCause) { if v != nil && v.Cause != nil { return *v.Cause } return } // IsSetCause returns true if Cause is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetCause() bool { return v != nil && v.Cause != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetReason() bool { return v != nil && v.Reason != nil } // GetBaseRunId returns the value of BaseRunId if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetBaseRunId() (o string) { if v != nil && v.BaseRunId != nil { return *v.BaseRunId } return } // IsSetBaseRunId returns true if BaseRunId is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetBaseRunId() bool { return v != nil && v.BaseRunId != nil } // GetNewRunId returns the value of NewRunId if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetNewRunId() (o string) { if v != nil && v.NewRunId != nil { return *v.NewRunId } return } // IsSetNewRunId returns true if NewRunId is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetNewRunId() bool { return v != nil && v.NewRunId != nil } // GetForkEventVersion returns the value of ForkEventVersion if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetForkEventVersion() (o int64) { if v != nil && v.ForkEventVersion != nil { return *v.ForkEventVersion } return } // IsSetForkEventVersion returns true if ForkEventVersion is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetForkEventVersion() bool { return v != nil && v.ForkEventVersion != nil } // GetBinaryChecksum returns the value of BinaryChecksum if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetBinaryChecksum() (o string) { if v != nil && v.BinaryChecksum != nil { return *v.BinaryChecksum } return } // IsSetBinaryChecksum returns true if BinaryChecksum is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetBinaryChecksum() bool { return v != nil && v.BinaryChecksum != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *DecisionTaskFailedEventAttributes) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *DecisionTaskFailedEventAttributes) IsSetRequestId() bool { return v != nil && v.RequestId != nil } type DecisionTaskScheduledEventAttributes struct { TaskList *TaskList `json:"taskList,omitempty"` StartToCloseTimeoutSeconds *int32 `json:"startToCloseTimeoutSeconds,omitempty"` Attempt *int64 `json:"attempt,omitempty"` } // ToWire translates a DecisionTaskScheduledEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DecisionTaskScheduledEventAttributes) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.StartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI64(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DecisionTaskScheduledEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DecisionTaskScheduledEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DecisionTaskScheduledEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DecisionTaskScheduledEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.StartToCloseTimeoutSeconds = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Attempt = &x if err != nil { return err } } } } return nil } // Encode serializes a DecisionTaskScheduledEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DecisionTaskScheduledEventAttributes struct could not be encoded. func (v *DecisionTaskScheduledEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.StartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DecisionTaskScheduledEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DecisionTaskScheduledEventAttributes struct could not be generated from the wire // representation. func (v *DecisionTaskScheduledEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.StartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Attempt = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DecisionTaskScheduledEventAttributes // struct. func (v *DecisionTaskScheduledEventAttributes) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.StartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("StartToCloseTimeoutSeconds: %v", *(v.StartToCloseTimeoutSeconds)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } return fmt.Sprintf("DecisionTaskScheduledEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DecisionTaskScheduledEventAttributes match the // provided DecisionTaskScheduledEventAttributes. // // This function performs a deep comparison. func (v *DecisionTaskScheduledEventAttributes) Equals(rhs *DecisionTaskScheduledEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !_I32_EqualsPtr(v.StartToCloseTimeoutSeconds, rhs.StartToCloseTimeoutSeconds) { return false } if !_I64_EqualsPtr(v.Attempt, rhs.Attempt) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DecisionTaskScheduledEventAttributes. func (v *DecisionTaskScheduledEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.StartToCloseTimeoutSeconds != nil { enc.AddInt32("startToCloseTimeoutSeconds", *v.StartToCloseTimeoutSeconds) } if v.Attempt != nil { enc.AddInt64("attempt", *v.Attempt) } return err } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *DecisionTaskScheduledEventAttributes) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *DecisionTaskScheduledEventAttributes) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetStartToCloseTimeoutSeconds returns the value of StartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *DecisionTaskScheduledEventAttributes) GetStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.StartToCloseTimeoutSeconds != nil { return *v.StartToCloseTimeoutSeconds } return } // IsSetStartToCloseTimeoutSeconds returns true if StartToCloseTimeoutSeconds is not nil. func (v *DecisionTaskScheduledEventAttributes) IsSetStartToCloseTimeoutSeconds() bool { return v != nil && v.StartToCloseTimeoutSeconds != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *DecisionTaskScheduledEventAttributes) GetAttempt() (o int64) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *DecisionTaskScheduledEventAttributes) IsSetAttempt() bool { return v != nil && v.Attempt != nil } type DecisionTaskStartedEventAttributes struct { ScheduledEventId *int64 `json:"scheduledEventId,omitempty"` Identity *string `json:"identity,omitempty"` RequestId *string `json:"requestId,omitempty"` } // ToWire translates a DecisionTaskStartedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DecisionTaskStartedEventAttributes) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.ScheduledEventId != nil { w, err = wire.NewValueI64(*(v.ScheduledEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DecisionTaskStartedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DecisionTaskStartedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DecisionTaskStartedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DecisionTaskStartedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } } } return nil } // Encode serializes a DecisionTaskStartedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DecisionTaskStartedEventAttributes struct could not be encoded. func (v *DecisionTaskStartedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ScheduledEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DecisionTaskStartedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DecisionTaskStartedEventAttributes struct could not be generated from the wire // representation. func (v *DecisionTaskStartedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DecisionTaskStartedEventAttributes // struct. func (v *DecisionTaskStartedEventAttributes) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.ScheduledEventId != nil { fields[i] = fmt.Sprintf("ScheduledEventId: %v", *(v.ScheduledEventId)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } return fmt.Sprintf("DecisionTaskStartedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DecisionTaskStartedEventAttributes match the // provided DecisionTaskStartedEventAttributes. // // This function performs a deep comparison. func (v *DecisionTaskStartedEventAttributes) Equals(rhs *DecisionTaskStartedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.ScheduledEventId, rhs.ScheduledEventId) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DecisionTaskStartedEventAttributes. func (v *DecisionTaskStartedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ScheduledEventId != nil { enc.AddInt64("scheduledEventId", *v.ScheduledEventId) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } return err } // GetScheduledEventId returns the value of ScheduledEventId if it is set or its // zero value if it is unset. func (v *DecisionTaskStartedEventAttributes) GetScheduledEventId() (o int64) { if v != nil && v.ScheduledEventId != nil { return *v.ScheduledEventId } return } // IsSetScheduledEventId returns true if ScheduledEventId is not nil. func (v *DecisionTaskStartedEventAttributes) IsSetScheduledEventId() bool { return v != nil && v.ScheduledEventId != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *DecisionTaskStartedEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *DecisionTaskStartedEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *DecisionTaskStartedEventAttributes) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *DecisionTaskStartedEventAttributes) IsSetRequestId() bool { return v != nil && v.RequestId != nil } type DecisionTaskTimedOutCause int32 const ( DecisionTaskTimedOutCauseTimeout DecisionTaskTimedOutCause = 0 DecisionTaskTimedOutCauseReset DecisionTaskTimedOutCause = 1 ) // DecisionTaskTimedOutCause_Values returns all recognized values of DecisionTaskTimedOutCause. func DecisionTaskTimedOutCause_Values() []DecisionTaskTimedOutCause { return []DecisionTaskTimedOutCause{ DecisionTaskTimedOutCauseTimeout, DecisionTaskTimedOutCauseReset, } } // UnmarshalText tries to decode DecisionTaskTimedOutCause from a byte slice // containing its name. // // var v DecisionTaskTimedOutCause // err := v.UnmarshalText([]byte("TIMEOUT")) func (v *DecisionTaskTimedOutCause) UnmarshalText(value []byte) error { switch s := string(value); s { case "TIMEOUT": *v = DecisionTaskTimedOutCauseTimeout return nil case "RESET": *v = DecisionTaskTimedOutCauseReset return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DecisionTaskTimedOutCause", err) } *v = DecisionTaskTimedOutCause(val) return nil } } // MarshalText encodes DecisionTaskTimedOutCause to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v DecisionTaskTimedOutCause) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("TIMEOUT"), nil case 1: return []byte("RESET"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DecisionTaskTimedOutCause. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v DecisionTaskTimedOutCause) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "TIMEOUT") case 1: enc.AddString("name", "RESET") } return nil } // Ptr returns a pointer to this enum value. func (v DecisionTaskTimedOutCause) Ptr() *DecisionTaskTimedOutCause { return &v } // Encode encodes DecisionTaskTimedOutCause directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v DecisionTaskTimedOutCause // return v.Encode(sWriter) func (v DecisionTaskTimedOutCause) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates DecisionTaskTimedOutCause into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v DecisionTaskTimedOutCause) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes DecisionTaskTimedOutCause from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return DecisionTaskTimedOutCause(0), err // } // // var v DecisionTaskTimedOutCause // if err := v.FromWire(x); err != nil { // return DecisionTaskTimedOutCause(0), err // } // return v, nil func (v *DecisionTaskTimedOutCause) FromWire(w wire.Value) error { *v = (DecisionTaskTimedOutCause)(w.GetI32()) return nil } // Decode reads off the encoded DecisionTaskTimedOutCause directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v DecisionTaskTimedOutCause // if err := v.Decode(sReader); err != nil { // return DecisionTaskTimedOutCause(0), err // } // return v, nil func (v *DecisionTaskTimedOutCause) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (DecisionTaskTimedOutCause)(i) return nil } // String returns a readable string representation of DecisionTaskTimedOutCause. func (v DecisionTaskTimedOutCause) String() string { w := int32(v) switch w { case 0: return "TIMEOUT" case 1: return "RESET" } return fmt.Sprintf("DecisionTaskTimedOutCause(%d)", w) } // Equals returns true if this DecisionTaskTimedOutCause value matches the provided // value. func (v DecisionTaskTimedOutCause) Equals(rhs DecisionTaskTimedOutCause) bool { return v == rhs } // MarshalJSON serializes DecisionTaskTimedOutCause into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v DecisionTaskTimedOutCause) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"TIMEOUT\""), nil case 1: return ([]byte)("\"RESET\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode DecisionTaskTimedOutCause from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *DecisionTaskTimedOutCause) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "DecisionTaskTimedOutCause") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "DecisionTaskTimedOutCause") } *v = (DecisionTaskTimedOutCause)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "DecisionTaskTimedOutCause") } } type DecisionTaskTimedOutEventAttributes struct { ScheduledEventId *int64 `json:"scheduledEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` TimeoutType *TimeoutType `json:"timeoutType,omitempty"` BaseRunId *string `json:"baseRunId,omitempty"` NewRunId *string `json:"newRunId,omitempty"` ForkEventVersion *int64 `json:"forkEventVersion,omitempty"` Reason *string `json:"reason,omitempty"` Cause *DecisionTaskTimedOutCause `json:"cause,omitempty"` RequestId *string `json:"requestId,omitempty"` } // ToWire translates a DecisionTaskTimedOutEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DecisionTaskTimedOutEventAttributes) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.ScheduledEventId != nil { w, err = wire.NewValueI64(*(v.ScheduledEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TimeoutType != nil { w, err = v.TimeoutType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.BaseRunId != nil { w, err = wire.NewValueString(*(v.BaseRunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.NewRunId != nil { w, err = wire.NewValueString(*(v.NewRunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ForkEventVersion != nil { w, err = wire.NewValueI64(*(v.ForkEventVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.Cause != nil { w, err = v.Cause.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DecisionTaskTimedOutCause_Read(w wire.Value) (DecisionTaskTimedOutCause, error) { var v DecisionTaskTimedOutCause err := v.FromWire(w) return v, err } // FromWire deserializes a DecisionTaskTimedOutEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DecisionTaskTimedOutEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DecisionTaskTimedOutEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DecisionTaskTimedOutEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x TimeoutType x, err = _TimeoutType_Read(field.Value) v.TimeoutType = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BaseRunId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.NewRunId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ForkEventVersion = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI32 { var x DecisionTaskTimedOutCause x, err = _DecisionTaskTimedOutCause_Read(field.Value) v.Cause = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } } } return nil } // Encode serializes a DecisionTaskTimedOutEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DecisionTaskTimedOutEventAttributes struct could not be encoded. func (v *DecisionTaskTimedOutEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ScheduledEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimeoutType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := v.TimeoutType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BaseRunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BaseRunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NewRunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.NewRunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ForkEventVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ForkEventVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Cause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI32}); err != nil { return err } if err := v.Cause.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DecisionTaskTimedOutCause_Decode(sr stream.Reader) (DecisionTaskTimedOutCause, error) { var v DecisionTaskTimedOutCause err := v.Decode(sr) return v, err } // Decode deserializes a DecisionTaskTimedOutEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DecisionTaskTimedOutEventAttributes struct could not be generated from the wire // representation. func (v *DecisionTaskTimedOutEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x TimeoutType x, err = _TimeoutType_Decode(sr) v.TimeoutType = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BaseRunId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.NewRunId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ForkEventVersion = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI32: var x DecisionTaskTimedOutCause x, err = _DecisionTaskTimedOutCause_Decode(sr) v.Cause = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DecisionTaskTimedOutEventAttributes // struct. func (v *DecisionTaskTimedOutEventAttributes) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.ScheduledEventId != nil { fields[i] = fmt.Sprintf("ScheduledEventId: %v", *(v.ScheduledEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.TimeoutType != nil { fields[i] = fmt.Sprintf("TimeoutType: %v", *(v.TimeoutType)) i++ } if v.BaseRunId != nil { fields[i] = fmt.Sprintf("BaseRunId: %v", *(v.BaseRunId)) i++ } if v.NewRunId != nil { fields[i] = fmt.Sprintf("NewRunId: %v", *(v.NewRunId)) i++ } if v.ForkEventVersion != nil { fields[i] = fmt.Sprintf("ForkEventVersion: %v", *(v.ForkEventVersion)) i++ } if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Cause != nil { fields[i] = fmt.Sprintf("Cause: %v", *(v.Cause)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } return fmt.Sprintf("DecisionTaskTimedOutEventAttributes{%v}", strings.Join(fields[:i], ", ")) } func _DecisionTaskTimedOutCause_EqualsPtr(lhs, rhs *DecisionTaskTimedOutCause) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this DecisionTaskTimedOutEventAttributes match the // provided DecisionTaskTimedOutEventAttributes. // // This function performs a deep comparison. func (v *DecisionTaskTimedOutEventAttributes) Equals(rhs *DecisionTaskTimedOutEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.ScheduledEventId, rhs.ScheduledEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_TimeoutType_EqualsPtr(v.TimeoutType, rhs.TimeoutType) { return false } if !_String_EqualsPtr(v.BaseRunId, rhs.BaseRunId) { return false } if !_String_EqualsPtr(v.NewRunId, rhs.NewRunId) { return false } if !_I64_EqualsPtr(v.ForkEventVersion, rhs.ForkEventVersion) { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !_DecisionTaskTimedOutCause_EqualsPtr(v.Cause, rhs.Cause) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DecisionTaskTimedOutEventAttributes. func (v *DecisionTaskTimedOutEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ScheduledEventId != nil { enc.AddInt64("scheduledEventId", *v.ScheduledEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.TimeoutType != nil { err = multierr.Append(err, enc.AddObject("timeoutType", *v.TimeoutType)) } if v.BaseRunId != nil { enc.AddString("baseRunId", *v.BaseRunId) } if v.NewRunId != nil { enc.AddString("newRunId", *v.NewRunId) } if v.ForkEventVersion != nil { enc.AddInt64("forkEventVersion", *v.ForkEventVersion) } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Cause != nil { err = multierr.Append(err, enc.AddObject("cause", *v.Cause)) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } return err } // GetScheduledEventId returns the value of ScheduledEventId if it is set or its // zero value if it is unset. func (v *DecisionTaskTimedOutEventAttributes) GetScheduledEventId() (o int64) { if v != nil && v.ScheduledEventId != nil { return *v.ScheduledEventId } return } // IsSetScheduledEventId returns true if ScheduledEventId is not nil. func (v *DecisionTaskTimedOutEventAttributes) IsSetScheduledEventId() bool { return v != nil && v.ScheduledEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *DecisionTaskTimedOutEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *DecisionTaskTimedOutEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetTimeoutType returns the value of TimeoutType if it is set or its // zero value if it is unset. func (v *DecisionTaskTimedOutEventAttributes) GetTimeoutType() (o TimeoutType) { if v != nil && v.TimeoutType != nil { return *v.TimeoutType } return } // IsSetTimeoutType returns true if TimeoutType is not nil. func (v *DecisionTaskTimedOutEventAttributes) IsSetTimeoutType() bool { return v != nil && v.TimeoutType != nil } // GetBaseRunId returns the value of BaseRunId if it is set or its // zero value if it is unset. func (v *DecisionTaskTimedOutEventAttributes) GetBaseRunId() (o string) { if v != nil && v.BaseRunId != nil { return *v.BaseRunId } return } // IsSetBaseRunId returns true if BaseRunId is not nil. func (v *DecisionTaskTimedOutEventAttributes) IsSetBaseRunId() bool { return v != nil && v.BaseRunId != nil } // GetNewRunId returns the value of NewRunId if it is set or its // zero value if it is unset. func (v *DecisionTaskTimedOutEventAttributes) GetNewRunId() (o string) { if v != nil && v.NewRunId != nil { return *v.NewRunId } return } // IsSetNewRunId returns true if NewRunId is not nil. func (v *DecisionTaskTimedOutEventAttributes) IsSetNewRunId() bool { return v != nil && v.NewRunId != nil } // GetForkEventVersion returns the value of ForkEventVersion if it is set or its // zero value if it is unset. func (v *DecisionTaskTimedOutEventAttributes) GetForkEventVersion() (o int64) { if v != nil && v.ForkEventVersion != nil { return *v.ForkEventVersion } return } // IsSetForkEventVersion returns true if ForkEventVersion is not nil. func (v *DecisionTaskTimedOutEventAttributes) IsSetForkEventVersion() bool { return v != nil && v.ForkEventVersion != nil } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *DecisionTaskTimedOutEventAttributes) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *DecisionTaskTimedOutEventAttributes) IsSetReason() bool { return v != nil && v.Reason != nil } // GetCause returns the value of Cause if it is set or its // zero value if it is unset. func (v *DecisionTaskTimedOutEventAttributes) GetCause() (o DecisionTaskTimedOutCause) { if v != nil && v.Cause != nil { return *v.Cause } return } // IsSetCause returns true if Cause is not nil. func (v *DecisionTaskTimedOutEventAttributes) IsSetCause() bool { return v != nil && v.Cause != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *DecisionTaskTimedOutEventAttributes) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *DecisionTaskTimedOutEventAttributes) IsSetRequestId() bool { return v != nil && v.RequestId != nil } type DecisionType int32 const ( DecisionTypeScheduleActivityTask DecisionType = 0 DecisionTypeRequestCancelActivityTask DecisionType = 1 DecisionTypeStartTimer DecisionType = 2 DecisionTypeCompleteWorkflowExecution DecisionType = 3 DecisionTypeFailWorkflowExecution DecisionType = 4 DecisionTypeCancelTimer DecisionType = 5 DecisionTypeCancelWorkflowExecution DecisionType = 6 DecisionTypeRequestCancelExternalWorkflowExecution DecisionType = 7 DecisionTypeRecordMarker DecisionType = 8 DecisionTypeContinueAsNewWorkflowExecution DecisionType = 9 DecisionTypeStartChildWorkflowExecution DecisionType = 10 DecisionTypeSignalExternalWorkflowExecution DecisionType = 11 DecisionTypeUpsertWorkflowSearchAttributes DecisionType = 12 ) // DecisionType_Values returns all recognized values of DecisionType. func DecisionType_Values() []DecisionType { return []DecisionType{ DecisionTypeScheduleActivityTask, DecisionTypeRequestCancelActivityTask, DecisionTypeStartTimer, DecisionTypeCompleteWorkflowExecution, DecisionTypeFailWorkflowExecution, DecisionTypeCancelTimer, DecisionTypeCancelWorkflowExecution, DecisionTypeRequestCancelExternalWorkflowExecution, DecisionTypeRecordMarker, DecisionTypeContinueAsNewWorkflowExecution, DecisionTypeStartChildWorkflowExecution, DecisionTypeSignalExternalWorkflowExecution, DecisionTypeUpsertWorkflowSearchAttributes, } } // UnmarshalText tries to decode DecisionType from a byte slice // containing its name. // // var v DecisionType // err := v.UnmarshalText([]byte("ScheduleActivityTask")) func (v *DecisionType) UnmarshalText(value []byte) error { switch s := string(value); s { case "ScheduleActivityTask": *v = DecisionTypeScheduleActivityTask return nil case "RequestCancelActivityTask": *v = DecisionTypeRequestCancelActivityTask return nil case "StartTimer": *v = DecisionTypeStartTimer return nil case "CompleteWorkflowExecution": *v = DecisionTypeCompleteWorkflowExecution return nil case "FailWorkflowExecution": *v = DecisionTypeFailWorkflowExecution return nil case "CancelTimer": *v = DecisionTypeCancelTimer return nil case "CancelWorkflowExecution": *v = DecisionTypeCancelWorkflowExecution return nil case "RequestCancelExternalWorkflowExecution": *v = DecisionTypeRequestCancelExternalWorkflowExecution return nil case "RecordMarker": *v = DecisionTypeRecordMarker return nil case "ContinueAsNewWorkflowExecution": *v = DecisionTypeContinueAsNewWorkflowExecution return nil case "StartChildWorkflowExecution": *v = DecisionTypeStartChildWorkflowExecution return nil case "SignalExternalWorkflowExecution": *v = DecisionTypeSignalExternalWorkflowExecution return nil case "UpsertWorkflowSearchAttributes": *v = DecisionTypeUpsertWorkflowSearchAttributes return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DecisionType", err) } *v = DecisionType(val) return nil } } // MarshalText encodes DecisionType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v DecisionType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("ScheduleActivityTask"), nil case 1: return []byte("RequestCancelActivityTask"), nil case 2: return []byte("StartTimer"), nil case 3: return []byte("CompleteWorkflowExecution"), nil case 4: return []byte("FailWorkflowExecution"), nil case 5: return []byte("CancelTimer"), nil case 6: return []byte("CancelWorkflowExecution"), nil case 7: return []byte("RequestCancelExternalWorkflowExecution"), nil case 8: return []byte("RecordMarker"), nil case 9: return []byte("ContinueAsNewWorkflowExecution"), nil case 10: return []byte("StartChildWorkflowExecution"), nil case 11: return []byte("SignalExternalWorkflowExecution"), nil case 12: return []byte("UpsertWorkflowSearchAttributes"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DecisionType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v DecisionType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "ScheduleActivityTask") case 1: enc.AddString("name", "RequestCancelActivityTask") case 2: enc.AddString("name", "StartTimer") case 3: enc.AddString("name", "CompleteWorkflowExecution") case 4: enc.AddString("name", "FailWorkflowExecution") case 5: enc.AddString("name", "CancelTimer") case 6: enc.AddString("name", "CancelWorkflowExecution") case 7: enc.AddString("name", "RequestCancelExternalWorkflowExecution") case 8: enc.AddString("name", "RecordMarker") case 9: enc.AddString("name", "ContinueAsNewWorkflowExecution") case 10: enc.AddString("name", "StartChildWorkflowExecution") case 11: enc.AddString("name", "SignalExternalWorkflowExecution") case 12: enc.AddString("name", "UpsertWorkflowSearchAttributes") } return nil } // Ptr returns a pointer to this enum value. func (v DecisionType) Ptr() *DecisionType { return &v } // Encode encodes DecisionType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v DecisionType // return v.Encode(sWriter) func (v DecisionType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates DecisionType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v DecisionType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes DecisionType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return DecisionType(0), err // } // // var v DecisionType // if err := v.FromWire(x); err != nil { // return DecisionType(0), err // } // return v, nil func (v *DecisionType) FromWire(w wire.Value) error { *v = (DecisionType)(w.GetI32()) return nil } // Decode reads off the encoded DecisionType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v DecisionType // if err := v.Decode(sReader); err != nil { // return DecisionType(0), err // } // return v, nil func (v *DecisionType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (DecisionType)(i) return nil } // String returns a readable string representation of DecisionType. func (v DecisionType) String() string { w := int32(v) switch w { case 0: return "ScheduleActivityTask" case 1: return "RequestCancelActivityTask" case 2: return "StartTimer" case 3: return "CompleteWorkflowExecution" case 4: return "FailWorkflowExecution" case 5: return "CancelTimer" case 6: return "CancelWorkflowExecution" case 7: return "RequestCancelExternalWorkflowExecution" case 8: return "RecordMarker" case 9: return "ContinueAsNewWorkflowExecution" case 10: return "StartChildWorkflowExecution" case 11: return "SignalExternalWorkflowExecution" case 12: return "UpsertWorkflowSearchAttributes" } return fmt.Sprintf("DecisionType(%d)", w) } // Equals returns true if this DecisionType value matches the provided // value. func (v DecisionType) Equals(rhs DecisionType) bool { return v == rhs } // MarshalJSON serializes DecisionType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v DecisionType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"ScheduleActivityTask\""), nil case 1: return ([]byte)("\"RequestCancelActivityTask\""), nil case 2: return ([]byte)("\"StartTimer\""), nil case 3: return ([]byte)("\"CompleteWorkflowExecution\""), nil case 4: return ([]byte)("\"FailWorkflowExecution\""), nil case 5: return ([]byte)("\"CancelTimer\""), nil case 6: return ([]byte)("\"CancelWorkflowExecution\""), nil case 7: return ([]byte)("\"RequestCancelExternalWorkflowExecution\""), nil case 8: return ([]byte)("\"RecordMarker\""), nil case 9: return ([]byte)("\"ContinueAsNewWorkflowExecution\""), nil case 10: return ([]byte)("\"StartChildWorkflowExecution\""), nil case 11: return ([]byte)("\"SignalExternalWorkflowExecution\""), nil case 12: return ([]byte)("\"UpsertWorkflowSearchAttributes\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode DecisionType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *DecisionType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "DecisionType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "DecisionType") } *v = (DecisionType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "DecisionType") } } type DeleteDomainRequest struct { Name *string `json:"name,omitempty"` SecurityToken *string `json:"securityToken,omitempty"` } // ToWire translates a DeleteDomainRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DeleteDomainRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.SecurityToken != nil { w, err = wire.NewValueString(*(v.SecurityToken)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DeleteDomainRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DeleteDomainRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DeleteDomainRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DeleteDomainRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SecurityToken = &x if err != nil { return err } } } } return nil } // Encode serializes a DeleteDomainRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DeleteDomainRequest struct could not be encoded. func (v *DeleteDomainRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SecurityToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SecurityToken)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DeleteDomainRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DeleteDomainRequest struct could not be generated from the wire // representation. func (v *DeleteDomainRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SecurityToken = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DeleteDomainRequest // struct. func (v *DeleteDomainRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.SecurityToken != nil { fields[i] = fmt.Sprintf("SecurityToken: %v", *(v.SecurityToken)) i++ } return fmt.Sprintf("DeleteDomainRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DeleteDomainRequest match the // provided DeleteDomainRequest. // // This function performs a deep comparison. func (v *DeleteDomainRequest) Equals(rhs *DeleteDomainRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !_String_EqualsPtr(v.SecurityToken, rhs.SecurityToken) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DeleteDomainRequest. func (v *DeleteDomainRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.SecurityToken != nil { enc.AddString("securityToken", *v.SecurityToken) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *DeleteDomainRequest) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *DeleteDomainRequest) IsSetName() bool { return v != nil && v.Name != nil } // GetSecurityToken returns the value of SecurityToken if it is set or its // zero value if it is unset. func (v *DeleteDomainRequest) GetSecurityToken() (o string) { if v != nil && v.SecurityToken != nil { return *v.SecurityToken } return } // IsSetSecurityToken returns true if SecurityToken is not nil. func (v *DeleteDomainRequest) IsSetSecurityToken() bool { return v != nil && v.SecurityToken != nil } type DeprecateDomainRequest struct { Name *string `json:"name,omitempty"` SecurityToken *string `json:"securityToken,omitempty"` } // ToWire translates a DeprecateDomainRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DeprecateDomainRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.SecurityToken != nil { w, err = wire.NewValueString(*(v.SecurityToken)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DeprecateDomainRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DeprecateDomainRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DeprecateDomainRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DeprecateDomainRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SecurityToken = &x if err != nil { return err } } } } return nil } // Encode serializes a DeprecateDomainRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DeprecateDomainRequest struct could not be encoded. func (v *DeprecateDomainRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SecurityToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SecurityToken)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DeprecateDomainRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DeprecateDomainRequest struct could not be generated from the wire // representation. func (v *DeprecateDomainRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SecurityToken = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DeprecateDomainRequest // struct. func (v *DeprecateDomainRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.SecurityToken != nil { fields[i] = fmt.Sprintf("SecurityToken: %v", *(v.SecurityToken)) i++ } return fmt.Sprintf("DeprecateDomainRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DeprecateDomainRequest match the // provided DeprecateDomainRequest. // // This function performs a deep comparison. func (v *DeprecateDomainRequest) Equals(rhs *DeprecateDomainRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !_String_EqualsPtr(v.SecurityToken, rhs.SecurityToken) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DeprecateDomainRequest. func (v *DeprecateDomainRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.SecurityToken != nil { enc.AddString("securityToken", *v.SecurityToken) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *DeprecateDomainRequest) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *DeprecateDomainRequest) IsSetName() bool { return v != nil && v.Name != nil } // GetSecurityToken returns the value of SecurityToken if it is set or its // zero value if it is unset. func (v *DeprecateDomainRequest) GetSecurityToken() (o string) { if v != nil && v.SecurityToken != nil { return *v.SecurityToken } return } // IsSetSecurityToken returns true if SecurityToken is not nil. func (v *DeprecateDomainRequest) IsSetSecurityToken() bool { return v != nil && v.SecurityToken != nil } type DescribeDomainRequest struct { Name *string `json:"name,omitempty"` UUID *string `json:"uuid,omitempty"` } // ToWire translates a DescribeDomainRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeDomainRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.UUID != nil { w, err = wire.NewValueString(*(v.UUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DescribeDomainRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeDomainRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeDomainRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeDomainRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.UUID = &x if err != nil { return err } } } } return nil } // Encode serializes a DescribeDomainRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeDomainRequest struct could not be encoded. func (v *DescribeDomainRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.UUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.UUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DescribeDomainRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeDomainRequest struct could not be generated from the wire // representation. func (v *DescribeDomainRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.UUID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeDomainRequest // struct. func (v *DescribeDomainRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.UUID != nil { fields[i] = fmt.Sprintf("UUID: %v", *(v.UUID)) i++ } return fmt.Sprintf("DescribeDomainRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DescribeDomainRequest match the // provided DescribeDomainRequest. // // This function performs a deep comparison. func (v *DescribeDomainRequest) Equals(rhs *DescribeDomainRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !_String_EqualsPtr(v.UUID, rhs.UUID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeDomainRequest. func (v *DescribeDomainRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.UUID != nil { enc.AddString("uuid", *v.UUID) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *DescribeDomainRequest) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *DescribeDomainRequest) IsSetName() bool { return v != nil && v.Name != nil } // GetUUID returns the value of UUID if it is set or its // zero value if it is unset. func (v *DescribeDomainRequest) GetUUID() (o string) { if v != nil && v.UUID != nil { return *v.UUID } return } // IsSetUUID returns true if UUID is not nil. func (v *DescribeDomainRequest) IsSetUUID() bool { return v != nil && v.UUID != nil } type DescribeDomainResponse struct { DomainInfo *DomainInfo `json:"domainInfo,omitempty"` Configuration *DomainConfiguration `json:"configuration,omitempty"` ReplicationConfiguration *DomainReplicationConfiguration `json:"replicationConfiguration,omitempty"` FailoverVersion *int64 `json:"failoverVersion,omitempty"` IsGlobalDomain *bool `json:"isGlobalDomain,omitempty"` FailoverInfo *FailoverInfo `json:"failoverInfo,omitempty"` } // ToWire translates a DescribeDomainResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeDomainResponse) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.DomainInfo != nil { w, err = v.DomainInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Configuration != nil { w, err = v.Configuration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ReplicationConfiguration != nil { w, err = v.ReplicationConfiguration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.FailoverVersion != nil { w, err = wire.NewValueI64(*(v.FailoverVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.IsGlobalDomain != nil { w, err = wire.NewValueBool(*(v.IsGlobalDomain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.FailoverInfo != nil { w, err = v.FailoverInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DomainInfo_Read(w wire.Value) (*DomainInfo, error) { var v DomainInfo err := v.FromWire(w) return &v, err } func _DomainConfiguration_Read(w wire.Value) (*DomainConfiguration, error) { var v DomainConfiguration err := v.FromWire(w) return &v, err } func _DomainReplicationConfiguration_Read(w wire.Value) (*DomainReplicationConfiguration, error) { var v DomainReplicationConfiguration err := v.FromWire(w) return &v, err } func _FailoverInfo_Read(w wire.Value) (*FailoverInfo, error) { var v FailoverInfo err := v.FromWire(w) return &v, err } // FromWire deserializes a DescribeDomainResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeDomainResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeDomainResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeDomainResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.DomainInfo, err = _DomainInfo_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Configuration, err = _DomainConfiguration_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ReplicationConfiguration, err = _DomainReplicationConfiguration_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverVersion = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.IsGlobalDomain = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.FailoverInfo, err = _FailoverInfo_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a DescribeDomainResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeDomainResponse struct could not be encoded. func (v *DescribeDomainResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.DomainInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Configuration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Configuration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReplicationConfiguration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ReplicationConfiguration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsGlobalDomain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.IsGlobalDomain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.FailoverInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DomainInfo_Decode(sr stream.Reader) (*DomainInfo, error) { var v DomainInfo err := v.Decode(sr) return &v, err } func _DomainConfiguration_Decode(sr stream.Reader) (*DomainConfiguration, error) { var v DomainConfiguration err := v.Decode(sr) return &v, err } func _DomainReplicationConfiguration_Decode(sr stream.Reader) (*DomainReplicationConfiguration, error) { var v DomainReplicationConfiguration err := v.Decode(sr) return &v, err } func _FailoverInfo_Decode(sr stream.Reader) (*FailoverInfo, error) { var v FailoverInfo err := v.Decode(sr) return &v, err } // Decode deserializes a DescribeDomainResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeDomainResponse struct could not be generated from the wire // representation. func (v *DescribeDomainResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.DomainInfo, err = _DomainInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Configuration, err = _DomainConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ReplicationConfiguration, err = _DomainReplicationConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverVersion = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.IsGlobalDomain = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.FailoverInfo, err = _FailoverInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeDomainResponse // struct. func (v *DescribeDomainResponse) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.DomainInfo != nil { fields[i] = fmt.Sprintf("DomainInfo: %v", v.DomainInfo) i++ } if v.Configuration != nil { fields[i] = fmt.Sprintf("Configuration: %v", v.Configuration) i++ } if v.ReplicationConfiguration != nil { fields[i] = fmt.Sprintf("ReplicationConfiguration: %v", v.ReplicationConfiguration) i++ } if v.FailoverVersion != nil { fields[i] = fmt.Sprintf("FailoverVersion: %v", *(v.FailoverVersion)) i++ } if v.IsGlobalDomain != nil { fields[i] = fmt.Sprintf("IsGlobalDomain: %v", *(v.IsGlobalDomain)) i++ } if v.FailoverInfo != nil { fields[i] = fmt.Sprintf("FailoverInfo: %v", v.FailoverInfo) i++ } return fmt.Sprintf("DescribeDomainResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DescribeDomainResponse match the // provided DescribeDomainResponse. // // This function performs a deep comparison. func (v *DescribeDomainResponse) Equals(rhs *DescribeDomainResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DomainInfo == nil && rhs.DomainInfo == nil) || (v.DomainInfo != nil && rhs.DomainInfo != nil && v.DomainInfo.Equals(rhs.DomainInfo))) { return false } if !((v.Configuration == nil && rhs.Configuration == nil) || (v.Configuration != nil && rhs.Configuration != nil && v.Configuration.Equals(rhs.Configuration))) { return false } if !((v.ReplicationConfiguration == nil && rhs.ReplicationConfiguration == nil) || (v.ReplicationConfiguration != nil && rhs.ReplicationConfiguration != nil && v.ReplicationConfiguration.Equals(rhs.ReplicationConfiguration))) { return false } if !_I64_EqualsPtr(v.FailoverVersion, rhs.FailoverVersion) { return false } if !_Bool_EqualsPtr(v.IsGlobalDomain, rhs.IsGlobalDomain) { return false } if !((v.FailoverInfo == nil && rhs.FailoverInfo == nil) || (v.FailoverInfo != nil && rhs.FailoverInfo != nil && v.FailoverInfo.Equals(rhs.FailoverInfo))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeDomainResponse. func (v *DescribeDomainResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainInfo != nil { err = multierr.Append(err, enc.AddObject("domainInfo", v.DomainInfo)) } if v.Configuration != nil { err = multierr.Append(err, enc.AddObject("configuration", v.Configuration)) } if v.ReplicationConfiguration != nil { err = multierr.Append(err, enc.AddObject("replicationConfiguration", v.ReplicationConfiguration)) } if v.FailoverVersion != nil { enc.AddInt64("failoverVersion", *v.FailoverVersion) } if v.IsGlobalDomain != nil { enc.AddBool("isGlobalDomain", *v.IsGlobalDomain) } if v.FailoverInfo != nil { err = multierr.Append(err, enc.AddObject("failoverInfo", v.FailoverInfo)) } return err } // GetDomainInfo returns the value of DomainInfo if it is set or its // zero value if it is unset. func (v *DescribeDomainResponse) GetDomainInfo() (o *DomainInfo) { if v != nil && v.DomainInfo != nil { return v.DomainInfo } return } // IsSetDomainInfo returns true if DomainInfo is not nil. func (v *DescribeDomainResponse) IsSetDomainInfo() bool { return v != nil && v.DomainInfo != nil } // GetConfiguration returns the value of Configuration if it is set or its // zero value if it is unset. func (v *DescribeDomainResponse) GetConfiguration() (o *DomainConfiguration) { if v != nil && v.Configuration != nil { return v.Configuration } return } // IsSetConfiguration returns true if Configuration is not nil. func (v *DescribeDomainResponse) IsSetConfiguration() bool { return v != nil && v.Configuration != nil } // GetReplicationConfiguration returns the value of ReplicationConfiguration if it is set or its // zero value if it is unset. func (v *DescribeDomainResponse) GetReplicationConfiguration() (o *DomainReplicationConfiguration) { if v != nil && v.ReplicationConfiguration != nil { return v.ReplicationConfiguration } return } // IsSetReplicationConfiguration returns true if ReplicationConfiguration is not nil. func (v *DescribeDomainResponse) IsSetReplicationConfiguration() bool { return v != nil && v.ReplicationConfiguration != nil } // GetFailoverVersion returns the value of FailoverVersion if it is set or its // zero value if it is unset. func (v *DescribeDomainResponse) GetFailoverVersion() (o int64) { if v != nil && v.FailoverVersion != nil { return *v.FailoverVersion } return } // IsSetFailoverVersion returns true if FailoverVersion is not nil. func (v *DescribeDomainResponse) IsSetFailoverVersion() bool { return v != nil && v.FailoverVersion != nil } // GetIsGlobalDomain returns the value of IsGlobalDomain if it is set or its // zero value if it is unset. func (v *DescribeDomainResponse) GetIsGlobalDomain() (o bool) { if v != nil && v.IsGlobalDomain != nil { return *v.IsGlobalDomain } return } // IsSetIsGlobalDomain returns true if IsGlobalDomain is not nil. func (v *DescribeDomainResponse) IsSetIsGlobalDomain() bool { return v != nil && v.IsGlobalDomain != nil } // GetFailoverInfo returns the value of FailoverInfo if it is set or its // zero value if it is unset. func (v *DescribeDomainResponse) GetFailoverInfo() (o *FailoverInfo) { if v != nil && v.FailoverInfo != nil { return v.FailoverInfo } return } // IsSetFailoverInfo returns true if FailoverInfo is not nil. func (v *DescribeDomainResponse) IsSetFailoverInfo() bool { return v != nil && v.FailoverInfo != nil } type DescribeHistoryHostRequest struct { HostAddress *string `json:"hostAddress,omitempty"` ShardIdForHost *int32 `json:"shardIdForHost,omitempty"` ExecutionForHost *WorkflowExecution `json:"executionForHost,omitempty"` } // ToWire translates a DescribeHistoryHostRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeHistoryHostRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.HostAddress != nil { w, err = wire.NewValueString(*(v.HostAddress)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ShardIdForHost != nil { w, err = wire.NewValueI32(*(v.ShardIdForHost)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ExecutionForHost != nil { w, err = v.ExecutionForHost.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DescribeHistoryHostRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeHistoryHostRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeHistoryHostRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeHistoryHostRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.HostAddress = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShardIdForHost = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ExecutionForHost, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a DescribeHistoryHostRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeHistoryHostRequest struct could not be encoded. func (v *DescribeHistoryHostRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.HostAddress != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.HostAddress)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardIdForHost != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShardIdForHost)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionForHost != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ExecutionForHost.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DescribeHistoryHostRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeHistoryHostRequest struct could not be generated from the wire // representation. func (v *DescribeHistoryHostRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.HostAddress = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShardIdForHost = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ExecutionForHost, err = _WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeHistoryHostRequest // struct. func (v *DescribeHistoryHostRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.HostAddress != nil { fields[i] = fmt.Sprintf("HostAddress: %v", *(v.HostAddress)) i++ } if v.ShardIdForHost != nil { fields[i] = fmt.Sprintf("ShardIdForHost: %v", *(v.ShardIdForHost)) i++ } if v.ExecutionForHost != nil { fields[i] = fmt.Sprintf("ExecutionForHost: %v", v.ExecutionForHost) i++ } return fmt.Sprintf("DescribeHistoryHostRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DescribeHistoryHostRequest match the // provided DescribeHistoryHostRequest. // // This function performs a deep comparison. func (v *DescribeHistoryHostRequest) Equals(rhs *DescribeHistoryHostRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.HostAddress, rhs.HostAddress) { return false } if !_I32_EqualsPtr(v.ShardIdForHost, rhs.ShardIdForHost) { return false } if !((v.ExecutionForHost == nil && rhs.ExecutionForHost == nil) || (v.ExecutionForHost != nil && rhs.ExecutionForHost != nil && v.ExecutionForHost.Equals(rhs.ExecutionForHost))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeHistoryHostRequest. func (v *DescribeHistoryHostRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.HostAddress != nil { enc.AddString("hostAddress", *v.HostAddress) } if v.ShardIdForHost != nil { enc.AddInt32("shardIdForHost", *v.ShardIdForHost) } if v.ExecutionForHost != nil { err = multierr.Append(err, enc.AddObject("executionForHost", v.ExecutionForHost)) } return err } // GetHostAddress returns the value of HostAddress if it is set or its // zero value if it is unset. func (v *DescribeHistoryHostRequest) GetHostAddress() (o string) { if v != nil && v.HostAddress != nil { return *v.HostAddress } return } // IsSetHostAddress returns true if HostAddress is not nil. func (v *DescribeHistoryHostRequest) IsSetHostAddress() bool { return v != nil && v.HostAddress != nil } // GetShardIdForHost returns the value of ShardIdForHost if it is set or its // zero value if it is unset. func (v *DescribeHistoryHostRequest) GetShardIdForHost() (o int32) { if v != nil && v.ShardIdForHost != nil { return *v.ShardIdForHost } return } // IsSetShardIdForHost returns true if ShardIdForHost is not nil. func (v *DescribeHistoryHostRequest) IsSetShardIdForHost() bool { return v != nil && v.ShardIdForHost != nil } // GetExecutionForHost returns the value of ExecutionForHost if it is set or its // zero value if it is unset. func (v *DescribeHistoryHostRequest) GetExecutionForHost() (o *WorkflowExecution) { if v != nil && v.ExecutionForHost != nil { return v.ExecutionForHost } return } // IsSetExecutionForHost returns true if ExecutionForHost is not nil. func (v *DescribeHistoryHostRequest) IsSetExecutionForHost() bool { return v != nil && v.ExecutionForHost != nil } type DescribeHistoryHostResponse struct { NumberOfShards *int32 `json:"numberOfShards,omitempty"` ShardIDs []int32 `json:"shardIDs,omitempty"` DomainCache *DomainCacheInfo `json:"domainCache,omitempty"` ShardControllerStatus *string `json:"shardControllerStatus,omitempty"` Address *string `json:"address,omitempty"` } type _List_I32_ValueList []int32 func (v _List_I32_ValueList) ForEach(f func(wire.Value) error) error { for _, x := range v { w, err := wire.NewValueI32(x), error(nil) if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_I32_ValueList) Size() int { return len(v) } func (_List_I32_ValueList) ValueType() wire.Type { return wire.TI32 } func (_List_I32_ValueList) Close() {} // ToWire translates a DescribeHistoryHostResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeHistoryHostResponse) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.NumberOfShards != nil { w, err = wire.NewValueI32(*(v.NumberOfShards)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ShardIDs != nil { w, err = wire.NewValueList(_List_I32_ValueList(v.ShardIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.DomainCache != nil { w, err = v.DomainCache.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ShardControllerStatus != nil { w, err = wire.NewValueString(*(v.ShardControllerStatus)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Address != nil { w, err = wire.NewValueString(*(v.Address)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _List_I32_Read(l wire.ValueList) ([]int32, error) { if l.ValueType() != wire.TI32 { return nil, nil } o := make([]int32, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := x.GetI32(), error(nil) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _DomainCacheInfo_Read(w wire.Value) (*DomainCacheInfo, error) { var v DomainCacheInfo err := v.FromWire(w) return &v, err } // FromWire deserializes a DescribeHistoryHostResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeHistoryHostResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeHistoryHostResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeHistoryHostResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.NumberOfShards = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.ShardIDs, err = _List_I32_Read(field.Value.GetList()) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.DomainCache, err = _DomainCacheInfo_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ShardControllerStatus = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Address = &x if err != nil { return err } } } } return nil } func _List_I32_Encode(val []int32, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TI32, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for _, v := range val { if err := sw.WriteInt32(v); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a DescribeHistoryHostResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeHistoryHostResponse struct could not be encoded. func (v *DescribeHistoryHostResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.NumberOfShards != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.NumberOfShards)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_I32_Encode(v.ShardIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainCache != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.DomainCache.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ShardControllerStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ShardControllerStatus)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Address != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Address)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _List_I32_Decode(sr stream.Reader) ([]int32, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TI32 { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]int32, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := sr.ReadInt32() if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _DomainCacheInfo_Decode(sr stream.Reader) (*DomainCacheInfo, error) { var v DomainCacheInfo err := v.Decode(sr) return &v, err } // Decode deserializes a DescribeHistoryHostResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeHistoryHostResponse struct could not be generated from the wire // representation. func (v *DescribeHistoryHostResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.NumberOfShards = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.ShardIDs, err = _List_I32_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.DomainCache, err = _DomainCacheInfo_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ShardControllerStatus = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Address = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeHistoryHostResponse // struct. func (v *DescribeHistoryHostResponse) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.NumberOfShards != nil { fields[i] = fmt.Sprintf("NumberOfShards: %v", *(v.NumberOfShards)) i++ } if v.ShardIDs != nil { fields[i] = fmt.Sprintf("ShardIDs: %v", v.ShardIDs) i++ } if v.DomainCache != nil { fields[i] = fmt.Sprintf("DomainCache: %v", v.DomainCache) i++ } if v.ShardControllerStatus != nil { fields[i] = fmt.Sprintf("ShardControllerStatus: %v", *(v.ShardControllerStatus)) i++ } if v.Address != nil { fields[i] = fmt.Sprintf("Address: %v", *(v.Address)) i++ } return fmt.Sprintf("DescribeHistoryHostResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_I32_Equals(lhs, rhs []int32) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !(lv == rv) { return false } } return true } // Equals returns true if all the fields of this DescribeHistoryHostResponse match the // provided DescribeHistoryHostResponse. // // This function performs a deep comparison. func (v *DescribeHistoryHostResponse) Equals(rhs *DescribeHistoryHostResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.NumberOfShards, rhs.NumberOfShards) { return false } if !((v.ShardIDs == nil && rhs.ShardIDs == nil) || (v.ShardIDs != nil && rhs.ShardIDs != nil && _List_I32_Equals(v.ShardIDs, rhs.ShardIDs))) { return false } if !((v.DomainCache == nil && rhs.DomainCache == nil) || (v.DomainCache != nil && rhs.DomainCache != nil && v.DomainCache.Equals(rhs.DomainCache))) { return false } if !_String_EqualsPtr(v.ShardControllerStatus, rhs.ShardControllerStatus) { return false } if !_String_EqualsPtr(v.Address, rhs.Address) { return false } return true } type _List_I32_Zapper []int32 // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_I32_Zapper. func (l _List_I32_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { enc.AppendInt32(v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeHistoryHostResponse. func (v *DescribeHistoryHostResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.NumberOfShards != nil { enc.AddInt32("numberOfShards", *v.NumberOfShards) } if v.ShardIDs != nil { err = multierr.Append(err, enc.AddArray("shardIDs", (_List_I32_Zapper)(v.ShardIDs))) } if v.DomainCache != nil { err = multierr.Append(err, enc.AddObject("domainCache", v.DomainCache)) } if v.ShardControllerStatus != nil { enc.AddString("shardControllerStatus", *v.ShardControllerStatus) } if v.Address != nil { enc.AddString("address", *v.Address) } return err } // GetNumberOfShards returns the value of NumberOfShards if it is set or its // zero value if it is unset. func (v *DescribeHistoryHostResponse) GetNumberOfShards() (o int32) { if v != nil && v.NumberOfShards != nil { return *v.NumberOfShards } return } // IsSetNumberOfShards returns true if NumberOfShards is not nil. func (v *DescribeHistoryHostResponse) IsSetNumberOfShards() bool { return v != nil && v.NumberOfShards != nil } // GetShardIDs returns the value of ShardIDs if it is set or its // zero value if it is unset. func (v *DescribeHistoryHostResponse) GetShardIDs() (o []int32) { if v != nil && v.ShardIDs != nil { return v.ShardIDs } return } // IsSetShardIDs returns true if ShardIDs is not nil. func (v *DescribeHistoryHostResponse) IsSetShardIDs() bool { return v != nil && v.ShardIDs != nil } // GetDomainCache returns the value of DomainCache if it is set or its // zero value if it is unset. func (v *DescribeHistoryHostResponse) GetDomainCache() (o *DomainCacheInfo) { if v != nil && v.DomainCache != nil { return v.DomainCache } return } // IsSetDomainCache returns true if DomainCache is not nil. func (v *DescribeHistoryHostResponse) IsSetDomainCache() bool { return v != nil && v.DomainCache != nil } // GetShardControllerStatus returns the value of ShardControllerStatus if it is set or its // zero value if it is unset. func (v *DescribeHistoryHostResponse) GetShardControllerStatus() (o string) { if v != nil && v.ShardControllerStatus != nil { return *v.ShardControllerStatus } return } // IsSetShardControllerStatus returns true if ShardControllerStatus is not nil. func (v *DescribeHistoryHostResponse) IsSetShardControllerStatus() bool { return v != nil && v.ShardControllerStatus != nil } // GetAddress returns the value of Address if it is set or its // zero value if it is unset. func (v *DescribeHistoryHostResponse) GetAddress() (o string) { if v != nil && v.Address != nil { return *v.Address } return } // IsSetAddress returns true if Address is not nil. func (v *DescribeHistoryHostResponse) IsSetAddress() bool { return v != nil && v.Address != nil } type DescribeQueueRequest struct { ShardID *int32 `json:"shardID,omitempty"` ClusterName *string `json:"clusterName,omitempty"` Type *int32 `json:"type,omitempty"` } // ToWire translates a DescribeQueueRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeQueueRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.ShardID != nil { w, err = wire.NewValueI32(*(v.ShardID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ClusterName != nil { w, err = wire.NewValueString(*(v.ClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Type != nil { w, err = wire.NewValueI32(*(v.Type)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DescribeQueueRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeQueueRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeQueueRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeQueueRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShardID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClusterName = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Type = &x if err != nil { return err } } } } return nil } // Encode serializes a DescribeQueueRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeQueueRequest struct could not be encoded. func (v *DescribeQueueRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ShardID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShardID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Type != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Type)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DescribeQueueRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeQueueRequest struct could not be generated from the wire // representation. func (v *DescribeQueueRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShardID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClusterName = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Type = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeQueueRequest // struct. func (v *DescribeQueueRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.ShardID != nil { fields[i] = fmt.Sprintf("ShardID: %v", *(v.ShardID)) i++ } if v.ClusterName != nil { fields[i] = fmt.Sprintf("ClusterName: %v", *(v.ClusterName)) i++ } if v.Type != nil { fields[i] = fmt.Sprintf("Type: %v", *(v.Type)) i++ } return fmt.Sprintf("DescribeQueueRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DescribeQueueRequest match the // provided DescribeQueueRequest. // // This function performs a deep comparison. func (v *DescribeQueueRequest) Equals(rhs *DescribeQueueRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.ShardID, rhs.ShardID) { return false } if !_String_EqualsPtr(v.ClusterName, rhs.ClusterName) { return false } if !_I32_EqualsPtr(v.Type, rhs.Type) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeQueueRequest. func (v *DescribeQueueRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ShardID != nil { enc.AddInt32("shardID", *v.ShardID) } if v.ClusterName != nil { enc.AddString("clusterName", *v.ClusterName) } if v.Type != nil { enc.AddInt32("type", *v.Type) } return err } // GetShardID returns the value of ShardID if it is set or its // zero value if it is unset. func (v *DescribeQueueRequest) GetShardID() (o int32) { if v != nil && v.ShardID != nil { return *v.ShardID } return } // IsSetShardID returns true if ShardID is not nil. func (v *DescribeQueueRequest) IsSetShardID() bool { return v != nil && v.ShardID != nil } // GetClusterName returns the value of ClusterName if it is set or its // zero value if it is unset. func (v *DescribeQueueRequest) GetClusterName() (o string) { if v != nil && v.ClusterName != nil { return *v.ClusterName } return } // IsSetClusterName returns true if ClusterName is not nil. func (v *DescribeQueueRequest) IsSetClusterName() bool { return v != nil && v.ClusterName != nil } // GetType returns the value of Type if it is set or its // zero value if it is unset. func (v *DescribeQueueRequest) GetType() (o int32) { if v != nil && v.Type != nil { return *v.Type } return } // IsSetType returns true if Type is not nil. func (v *DescribeQueueRequest) IsSetType() bool { return v != nil && v.Type != nil } type DescribeQueueResponse struct { ProcessingQueueStates []string `json:"processingQueueStates,omitempty"` } type _List_String_ValueList []string func (v _List_String_ValueList) ForEach(f func(wire.Value) error) error { for _, x := range v { w, err := wire.NewValueString(x), error(nil) if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_String_ValueList) Size() int { return len(v) } func (_List_String_ValueList) ValueType() wire.Type { return wire.TBinary } func (_List_String_ValueList) Close() {} // ToWire translates a DescribeQueueResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeQueueResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ProcessingQueueStates != nil { w, err = wire.NewValueList(_List_String_ValueList(v.ProcessingQueueStates)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _List_String_Read(l wire.ValueList) ([]string, error) { if l.ValueType() != wire.TBinary { return nil, nil } o := make([]string, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := x.GetString(), error(nil) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a DescribeQueueResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeQueueResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeQueueResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeQueueResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.ProcessingQueueStates, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_String_Encode(val []string, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TBinary, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for _, v := range val { if err := sw.WriteString(v); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a DescribeQueueResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeQueueResponse struct could not be encoded. func (v *DescribeQueueResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ProcessingQueueStates != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.ProcessingQueueStates, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _List_String_Decode(sr stream.Reader) ([]string, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TBinary { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]string, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := sr.ReadString() if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a DescribeQueueResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeQueueResponse struct could not be generated from the wire // representation. func (v *DescribeQueueResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.ProcessingQueueStates, err = _List_String_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeQueueResponse // struct. func (v *DescribeQueueResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ProcessingQueueStates != nil { fields[i] = fmt.Sprintf("ProcessingQueueStates: %v", v.ProcessingQueueStates) i++ } return fmt.Sprintf("DescribeQueueResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_String_Equals(lhs, rhs []string) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !(lv == rv) { return false } } return true } // Equals returns true if all the fields of this DescribeQueueResponse match the // provided DescribeQueueResponse. // // This function performs a deep comparison. func (v *DescribeQueueResponse) Equals(rhs *DescribeQueueResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ProcessingQueueStates == nil && rhs.ProcessingQueueStates == nil) || (v.ProcessingQueueStates != nil && rhs.ProcessingQueueStates != nil && _List_String_Equals(v.ProcessingQueueStates, rhs.ProcessingQueueStates))) { return false } return true } type _List_String_Zapper []string // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_String_Zapper. func (l _List_String_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { enc.AppendString(v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeQueueResponse. func (v *DescribeQueueResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ProcessingQueueStates != nil { err = multierr.Append(err, enc.AddArray("processingQueueStates", (_List_String_Zapper)(v.ProcessingQueueStates))) } return err } // GetProcessingQueueStates returns the value of ProcessingQueueStates if it is set or its // zero value if it is unset. func (v *DescribeQueueResponse) GetProcessingQueueStates() (o []string) { if v != nil && v.ProcessingQueueStates != nil { return v.ProcessingQueueStates } return } // IsSetProcessingQueueStates returns true if ProcessingQueueStates is not nil. func (v *DescribeQueueResponse) IsSetProcessingQueueStates() bool { return v != nil && v.ProcessingQueueStates != nil } type DescribeShardDistributionRequest struct { PageSize *int32 `json:"pageSize,omitempty"` PageID *int32 `json:"pageID,omitempty"` } // ToWire translates a DescribeShardDistributionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeShardDistributionRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.PageSize != nil { w, err = wire.NewValueI32(*(v.PageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.PageID != nil { w, err = wire.NewValueI32(*(v.PageID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DescribeShardDistributionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeShardDistributionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeShardDistributionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeShardDistributionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.PageSize = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.PageID = &x if err != nil { return err } } } } return nil } // Encode serializes a DescribeShardDistributionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeShardDistributionRequest struct could not be encoded. func (v *DescribeShardDistributionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.PageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.PageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PageID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.PageID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DescribeShardDistributionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeShardDistributionRequest struct could not be generated from the wire // representation. func (v *DescribeShardDistributionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.PageSize = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.PageID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeShardDistributionRequest // struct. func (v *DescribeShardDistributionRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.PageSize != nil { fields[i] = fmt.Sprintf("PageSize: %v", *(v.PageSize)) i++ } if v.PageID != nil { fields[i] = fmt.Sprintf("PageID: %v", *(v.PageID)) i++ } return fmt.Sprintf("DescribeShardDistributionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DescribeShardDistributionRequest match the // provided DescribeShardDistributionRequest. // // This function performs a deep comparison. func (v *DescribeShardDistributionRequest) Equals(rhs *DescribeShardDistributionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.PageSize, rhs.PageSize) { return false } if !_I32_EqualsPtr(v.PageID, rhs.PageID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeShardDistributionRequest. func (v *DescribeShardDistributionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.PageSize != nil { enc.AddInt32("pageSize", *v.PageSize) } if v.PageID != nil { enc.AddInt32("pageID", *v.PageID) } return err } // GetPageSize returns the value of PageSize if it is set or its // zero value if it is unset. func (v *DescribeShardDistributionRequest) GetPageSize() (o int32) { if v != nil && v.PageSize != nil { return *v.PageSize } return } // IsSetPageSize returns true if PageSize is not nil. func (v *DescribeShardDistributionRequest) IsSetPageSize() bool { return v != nil && v.PageSize != nil } // GetPageID returns the value of PageID if it is set or its // zero value if it is unset. func (v *DescribeShardDistributionRequest) GetPageID() (o int32) { if v != nil && v.PageID != nil { return *v.PageID } return } // IsSetPageID returns true if PageID is not nil. func (v *DescribeShardDistributionRequest) IsSetPageID() bool { return v != nil && v.PageID != nil } type DescribeShardDistributionResponse struct { NumberOfShards *int32 `json:"numberOfShards,omitempty"` Shards map[int32]string `json:"shards,omitempty"` } type _Map_I32_String_MapItemList map[int32]string func (m _Map_I32_String_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { kw, err := wire.NewValueI32(k), error(nil) if err != nil { return err } vw, err := wire.NewValueString(v), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_I32_String_MapItemList) Size() int { return len(m) } func (_Map_I32_String_MapItemList) KeyType() wire.Type { return wire.TI32 } func (_Map_I32_String_MapItemList) ValueType() wire.Type { return wire.TBinary } func (_Map_I32_String_MapItemList) Close() {} // ToWire translates a DescribeShardDistributionResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeShardDistributionResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.NumberOfShards != nil { w, err = wire.NewValueI32(*(v.NumberOfShards)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Shards != nil { w, err = wire.NewValueMap(_Map_I32_String_MapItemList(v.Shards)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Map_I32_String_Read(m wire.MapItemList) (map[int32]string, error) { if m.KeyType() != wire.TI32 { return nil, nil } if m.ValueType() != wire.TBinary { return nil, nil } o := make(map[int32]string, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetI32(), error(nil) if err != nil { return err } v, err := x.Value.GetString(), error(nil) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a DescribeShardDistributionResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeShardDistributionResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeShardDistributionResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeShardDistributionResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.NumberOfShards = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TMap { v.Shards, err = _Map_I32_String_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_I32_String_Encode(val map[int32]string, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TI32, ValueType: wire.TBinary, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if err := sw.WriteInt32(k); err != nil { return err } if err := sw.WriteString(v); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a DescribeShardDistributionResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeShardDistributionResponse struct could not be encoded. func (v *DescribeShardDistributionResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.NumberOfShards != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.NumberOfShards)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Shards != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TMap}); err != nil { return err } if err := _Map_I32_String_Encode(v.Shards, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _Map_I32_String_Decode(sr stream.Reader) (map[int32]string, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TI32 || mh.ValueType != wire.TBinary { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[int32]string, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadInt32() if err != nil { return nil, err } v, err := sr.ReadString() if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a DescribeShardDistributionResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeShardDistributionResponse struct could not be generated from the wire // representation. func (v *DescribeShardDistributionResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.NumberOfShards = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TMap: v.Shards, err = _Map_I32_String_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeShardDistributionResponse // struct. func (v *DescribeShardDistributionResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.NumberOfShards != nil { fields[i] = fmt.Sprintf("NumberOfShards: %v", *(v.NumberOfShards)) i++ } if v.Shards != nil { fields[i] = fmt.Sprintf("Shards: %v", v.Shards) i++ } return fmt.Sprintf("DescribeShardDistributionResponse{%v}", strings.Join(fields[:i], ", ")) } func _Map_I32_String_Equals(lhs, rhs map[int32]string) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !(lv == rv) { return false } } return true } // Equals returns true if all the fields of this DescribeShardDistributionResponse match the // provided DescribeShardDistributionResponse. // // This function performs a deep comparison. func (v *DescribeShardDistributionResponse) Equals(rhs *DescribeShardDistributionResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.NumberOfShards, rhs.NumberOfShards) { return false } if !((v.Shards == nil && rhs.Shards == nil) || (v.Shards != nil && rhs.Shards != nil && _Map_I32_String_Equals(v.Shards, rhs.Shards))) { return false } return true } type _Map_I32_String_Item_Zapper struct { Key int32 Value string } // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_String_Item_Zapper. func (v _Map_I32_String_Item_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { enc.AddInt32("key", v.Key) enc.AddString("value", v.Value) return err } type _Map_I32_String_Zapper map[int32]string // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_String_Zapper. func (m _Map_I32_String_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AppendObject(_Map_I32_String_Item_Zapper{Key: k, Value: v})) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeShardDistributionResponse. func (v *DescribeShardDistributionResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.NumberOfShards != nil { enc.AddInt32("numberOfShards", *v.NumberOfShards) } if v.Shards != nil { err = multierr.Append(err, enc.AddArray("shards", (_Map_I32_String_Zapper)(v.Shards))) } return err } // GetNumberOfShards returns the value of NumberOfShards if it is set or its // zero value if it is unset. func (v *DescribeShardDistributionResponse) GetNumberOfShards() (o int32) { if v != nil && v.NumberOfShards != nil { return *v.NumberOfShards } return } // IsSetNumberOfShards returns true if NumberOfShards is not nil. func (v *DescribeShardDistributionResponse) IsSetNumberOfShards() bool { return v != nil && v.NumberOfShards != nil } // GetShards returns the value of Shards if it is set or its // zero value if it is unset. func (v *DescribeShardDistributionResponse) GetShards() (o map[int32]string) { if v != nil && v.Shards != nil { return v.Shards } return } // IsSetShards returns true if Shards is not nil. func (v *DescribeShardDistributionResponse) IsSetShards() bool { return v != nil && v.Shards != nil } type DescribeTaskListRequest struct { Domain *string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` TaskListType *TaskListType `json:"taskListType,omitempty"` IncludeTaskListStatus *bool `json:"includeTaskListStatus,omitempty"` } // ToWire translates a DescribeTaskListRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeTaskListRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TaskListType != nil { w, err = v.TaskListType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.IncludeTaskListStatus != nil { w, err = wire.NewValueBool(*(v.IncludeTaskListStatus)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TaskListType_Read(w wire.Value) (TaskListType, error) { var v TaskListType err := v.FromWire(w) return v, err } // FromWire deserializes a DescribeTaskListRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeTaskListRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeTaskListRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeTaskListRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x TaskListType x, err = _TaskListType_Read(field.Value) v.TaskListType = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.IncludeTaskListStatus = &x if err != nil { return err } } } } return nil } // Encode serializes a DescribeTaskListRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeTaskListRequest struct could not be encoded. func (v *DescribeTaskListRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := v.TaskListType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IncludeTaskListStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.IncludeTaskListStatus)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TaskListType_Decode(sr stream.Reader) (TaskListType, error) { var v TaskListType err := v.Decode(sr) return v, err } // Decode deserializes a DescribeTaskListRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeTaskListRequest struct could not be generated from the wire // representation. func (v *DescribeTaskListRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x TaskListType x, err = _TaskListType_Decode(sr) v.TaskListType = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.IncludeTaskListStatus = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeTaskListRequest // struct. func (v *DescribeTaskListRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.TaskListType != nil { fields[i] = fmt.Sprintf("TaskListType: %v", *(v.TaskListType)) i++ } if v.IncludeTaskListStatus != nil { fields[i] = fmt.Sprintf("IncludeTaskListStatus: %v", *(v.IncludeTaskListStatus)) i++ } return fmt.Sprintf("DescribeTaskListRequest{%v}", strings.Join(fields[:i], ", ")) } func _TaskListType_EqualsPtr(lhs, rhs *TaskListType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this DescribeTaskListRequest match the // provided DescribeTaskListRequest. // // This function performs a deep comparison. func (v *DescribeTaskListRequest) Equals(rhs *DescribeTaskListRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !_TaskListType_EqualsPtr(v.TaskListType, rhs.TaskListType) { return false } if !_Bool_EqualsPtr(v.IncludeTaskListStatus, rhs.IncludeTaskListStatus) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeTaskListRequest. func (v *DescribeTaskListRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.TaskListType != nil { err = multierr.Append(err, enc.AddObject("taskListType", *v.TaskListType)) } if v.IncludeTaskListStatus != nil { enc.AddBool("includeTaskListStatus", *v.IncludeTaskListStatus) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *DescribeTaskListRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *DescribeTaskListRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *DescribeTaskListRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *DescribeTaskListRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetTaskListType returns the value of TaskListType if it is set or its // zero value if it is unset. func (v *DescribeTaskListRequest) GetTaskListType() (o TaskListType) { if v != nil && v.TaskListType != nil { return *v.TaskListType } return } // IsSetTaskListType returns true if TaskListType is not nil. func (v *DescribeTaskListRequest) IsSetTaskListType() bool { return v != nil && v.TaskListType != nil } // GetIncludeTaskListStatus returns the value of IncludeTaskListStatus if it is set or its // zero value if it is unset. func (v *DescribeTaskListRequest) GetIncludeTaskListStatus() (o bool) { if v != nil && v.IncludeTaskListStatus != nil { return *v.IncludeTaskListStatus } return } // IsSetIncludeTaskListStatus returns true if IncludeTaskListStatus is not nil. func (v *DescribeTaskListRequest) IsSetIncludeTaskListStatus() bool { return v != nil && v.IncludeTaskListStatus != nil } type DescribeTaskListResponse struct { Pollers []*PollerInfo `json:"pollers,omitempty"` TaskListStatus *TaskListStatus `json:"taskListStatus,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` } type _List_PollerInfo_ValueList []*PollerInfo func (v _List_PollerInfo_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*PollerInfo', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_PollerInfo_ValueList) Size() int { return len(v) } func (_List_PollerInfo_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_PollerInfo_ValueList) Close() {} // ToWire translates a DescribeTaskListResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeTaskListResponse) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Pollers != nil { w, err = wire.NewValueList(_List_PollerInfo_ValueList(v.Pollers)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskListStatus != nil { w, err = v.TaskListStatus.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollerInfo_Read(w wire.Value) (*PollerInfo, error) { var v PollerInfo err := v.FromWire(w) return &v, err } func _List_PollerInfo_Read(l wire.ValueList) ([]*PollerInfo, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*PollerInfo, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _PollerInfo_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _TaskListStatus_Read(w wire.Value) (*TaskListStatus, error) { var v TaskListStatus err := v.FromWire(w) return &v, err } // FromWire deserializes a DescribeTaskListResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeTaskListResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeTaskListResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeTaskListResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Pollers, err = _List_PollerInfo_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TaskListStatus, err = _TaskListStatus_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } } } return nil } func _List_PollerInfo_Encode(val []*PollerInfo, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*PollerInfo', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a DescribeTaskListResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeTaskListResponse struct could not be encoded. func (v *DescribeTaskListResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Pollers != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_PollerInfo_Encode(v.Pollers, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TaskListStatus.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollerInfo_Decode(sr stream.Reader) (*PollerInfo, error) { var v PollerInfo err := v.Decode(sr) return &v, err } func _List_PollerInfo_Decode(sr stream.Reader) ([]*PollerInfo, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*PollerInfo, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _PollerInfo_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _TaskListStatus_Decode(sr stream.Reader) (*TaskListStatus, error) { var v TaskListStatus err := v.Decode(sr) return &v, err } // Decode deserializes a DescribeTaskListResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeTaskListResponse struct could not be generated from the wire // representation. func (v *DescribeTaskListResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Pollers, err = _List_PollerInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TaskListStatus, err = _TaskListStatus_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeTaskListResponse // struct. func (v *DescribeTaskListResponse) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Pollers != nil { fields[i] = fmt.Sprintf("Pollers: %v", v.Pollers) i++ } if v.TaskListStatus != nil { fields[i] = fmt.Sprintf("TaskListStatus: %v", v.TaskListStatus) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } return fmt.Sprintf("DescribeTaskListResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_PollerInfo_Equals(lhs, rhs []*PollerInfo) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this DescribeTaskListResponse match the // provided DescribeTaskListResponse. // // This function performs a deep comparison. func (v *DescribeTaskListResponse) Equals(rhs *DescribeTaskListResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Pollers == nil && rhs.Pollers == nil) || (v.Pollers != nil && rhs.Pollers != nil && _List_PollerInfo_Equals(v.Pollers, rhs.Pollers))) { return false } if !((v.TaskListStatus == nil && rhs.TaskListStatus == nil) || (v.TaskListStatus != nil && rhs.TaskListStatus != nil && v.TaskListStatus.Equals(rhs.TaskListStatus))) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } return true } type _List_PollerInfo_Zapper []*PollerInfo // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_PollerInfo_Zapper. func (l _List_PollerInfo_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeTaskListResponse. func (v *DescribeTaskListResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Pollers != nil { err = multierr.Append(err, enc.AddArray("pollers", (_List_PollerInfo_Zapper)(v.Pollers))) } if v.TaskListStatus != nil { err = multierr.Append(err, enc.AddObject("taskListStatus", v.TaskListStatus)) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } return err } // GetPollers returns the value of Pollers if it is set or its // zero value if it is unset. func (v *DescribeTaskListResponse) GetPollers() (o []*PollerInfo) { if v != nil && v.Pollers != nil { return v.Pollers } return } // IsSetPollers returns true if Pollers is not nil. func (v *DescribeTaskListResponse) IsSetPollers() bool { return v != nil && v.Pollers != nil } // GetTaskListStatus returns the value of TaskListStatus if it is set or its // zero value if it is unset. func (v *DescribeTaskListResponse) GetTaskListStatus() (o *TaskListStatus) { if v != nil && v.TaskListStatus != nil { return v.TaskListStatus } return } // IsSetTaskListStatus returns true if TaskListStatus is not nil. func (v *DescribeTaskListResponse) IsSetTaskListStatus() bool { return v != nil && v.TaskListStatus != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *DescribeTaskListResponse) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *DescribeTaskListResponse) IsSetTaskList() bool { return v != nil && v.TaskList != nil } type DescribeWorkflowExecutionRequest struct { Domain *string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` QueryConsistencyLevel *QueryConsistencyLevel `json:"queryConsistencyLevel,omitempty"` } // ToWire translates a DescribeWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.QueryConsistencyLevel != nil { w, err = v.QueryConsistencyLevel.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryConsistencyLevel_Read(w wire.Value) (QueryConsistencyLevel, error) { var v QueryConsistencyLevel err := v.FromWire(w) return v, err } // FromWire deserializes a DescribeWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x QueryConsistencyLevel x, err = _QueryConsistencyLevel_Read(field.Value) v.QueryConsistencyLevel = &x if err != nil { return err } } } } return nil } // Encode serializes a DescribeWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeWorkflowExecutionRequest struct could not be encoded. func (v *DescribeWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryConsistencyLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := v.QueryConsistencyLevel.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryConsistencyLevel_Decode(sr stream.Reader) (QueryConsistencyLevel, error) { var v QueryConsistencyLevel err := v.Decode(sr) return v, err } // Decode deserializes a DescribeWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *DescribeWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x QueryConsistencyLevel x, err = _QueryConsistencyLevel_Decode(sr) v.QueryConsistencyLevel = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeWorkflowExecutionRequest // struct. func (v *DescribeWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.QueryConsistencyLevel != nil { fields[i] = fmt.Sprintf("QueryConsistencyLevel: %v", *(v.QueryConsistencyLevel)) i++ } return fmt.Sprintf("DescribeWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } func _QueryConsistencyLevel_EqualsPtr(lhs, rhs *QueryConsistencyLevel) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this DescribeWorkflowExecutionRequest match the // provided DescribeWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *DescribeWorkflowExecutionRequest) Equals(rhs *DescribeWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !_QueryConsistencyLevel_EqualsPtr(v.QueryConsistencyLevel, rhs.QueryConsistencyLevel) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeWorkflowExecutionRequest. func (v *DescribeWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.QueryConsistencyLevel != nil { err = multierr.Append(err, enc.AddObject("queryConsistencyLevel", *v.QueryConsistencyLevel)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *DescribeWorkflowExecutionRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *DescribeWorkflowExecutionRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetQueryConsistencyLevel returns the value of QueryConsistencyLevel if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionRequest) GetQueryConsistencyLevel() (o QueryConsistencyLevel) { if v != nil && v.QueryConsistencyLevel != nil { return *v.QueryConsistencyLevel } return } // IsSetQueryConsistencyLevel returns true if QueryConsistencyLevel is not nil. func (v *DescribeWorkflowExecutionRequest) IsSetQueryConsistencyLevel() bool { return v != nil && v.QueryConsistencyLevel != nil } type DescribeWorkflowExecutionResponse struct { ExecutionConfiguration *WorkflowExecutionConfiguration `json:"executionConfiguration,omitempty"` WorkflowExecutionInfo *WorkflowExecutionInfo `json:"workflowExecutionInfo,omitempty"` PendingActivities []*PendingActivityInfo `json:"pendingActivities,omitempty"` PendingChildren []*PendingChildExecutionInfo `json:"pendingChildren,omitempty"` PendingDecision *PendingDecisionInfo `json:"pendingDecision,omitempty"` } type _List_PendingActivityInfo_ValueList []*PendingActivityInfo func (v _List_PendingActivityInfo_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*PendingActivityInfo', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_PendingActivityInfo_ValueList) Size() int { return len(v) } func (_List_PendingActivityInfo_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_PendingActivityInfo_ValueList) Close() {} type _List_PendingChildExecutionInfo_ValueList []*PendingChildExecutionInfo func (v _List_PendingChildExecutionInfo_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*PendingChildExecutionInfo', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_PendingChildExecutionInfo_ValueList) Size() int { return len(v) } func (_List_PendingChildExecutionInfo_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_PendingChildExecutionInfo_ValueList) Close() {} // ToWire translates a DescribeWorkflowExecutionResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DescribeWorkflowExecutionResponse) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.ExecutionConfiguration != nil { w, err = v.ExecutionConfiguration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecutionInfo != nil { w, err = v.WorkflowExecutionInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.PendingActivities != nil { w, err = wire.NewValueList(_List_PendingActivityInfo_ValueList(v.PendingActivities)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.PendingChildren != nil { w, err = wire.NewValueList(_List_PendingChildExecutionInfo_ValueList(v.PendingChildren)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.PendingDecision != nil { w, err = v.PendingDecision.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WorkflowExecutionConfiguration_Read(w wire.Value) (*WorkflowExecutionConfiguration, error) { var v WorkflowExecutionConfiguration err := v.FromWire(w) return &v, err } func _WorkflowExecutionInfo_Read(w wire.Value) (*WorkflowExecutionInfo, error) { var v WorkflowExecutionInfo err := v.FromWire(w) return &v, err } func _PendingActivityInfo_Read(w wire.Value) (*PendingActivityInfo, error) { var v PendingActivityInfo err := v.FromWire(w) return &v, err } func _List_PendingActivityInfo_Read(l wire.ValueList) ([]*PendingActivityInfo, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*PendingActivityInfo, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _PendingActivityInfo_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _PendingChildExecutionInfo_Read(w wire.Value) (*PendingChildExecutionInfo, error) { var v PendingChildExecutionInfo err := v.FromWire(w) return &v, err } func _List_PendingChildExecutionInfo_Read(l wire.ValueList) ([]*PendingChildExecutionInfo, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*PendingChildExecutionInfo, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _PendingChildExecutionInfo_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _PendingDecisionInfo_Read(w wire.Value) (*PendingDecisionInfo, error) { var v PendingDecisionInfo err := v.FromWire(w) return &v, err } // FromWire deserializes a DescribeWorkflowExecutionResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DescribeWorkflowExecutionResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DescribeWorkflowExecutionResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DescribeWorkflowExecutionResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.ExecutionConfiguration, err = _WorkflowExecutionConfiguration_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionInfo, err = _WorkflowExecutionInfo_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TList { v.PendingActivities, err = _List_PendingActivityInfo_Read(field.Value.GetList()) if err != nil { return err } } case 40: if field.Value.Type() == wire.TList { v.PendingChildren, err = _List_PendingChildExecutionInfo_Read(field.Value.GetList()) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.PendingDecision, err = _PendingDecisionInfo_Read(field.Value) if err != nil { return err } } } } return nil } func _List_PendingActivityInfo_Encode(val []*PendingActivityInfo, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*PendingActivityInfo', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } func _List_PendingChildExecutionInfo_Encode(val []*PendingChildExecutionInfo, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*PendingChildExecutionInfo', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a DescribeWorkflowExecutionResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DescribeWorkflowExecutionResponse struct could not be encoded. func (v *DescribeWorkflowExecutionResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ExecutionConfiguration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.ExecutionConfiguration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingActivities != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TList}); err != nil { return err } if err := _List_PendingActivityInfo_Encode(v.PendingActivities, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingChildren != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TList}); err != nil { return err } if err := _List_PendingChildExecutionInfo_Encode(v.PendingChildren, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingDecision != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.PendingDecision.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _WorkflowExecutionConfiguration_Decode(sr stream.Reader) (*WorkflowExecutionConfiguration, error) { var v WorkflowExecutionConfiguration err := v.Decode(sr) return &v, err } func _WorkflowExecutionInfo_Decode(sr stream.Reader) (*WorkflowExecutionInfo, error) { var v WorkflowExecutionInfo err := v.Decode(sr) return &v, err } func _PendingActivityInfo_Decode(sr stream.Reader) (*PendingActivityInfo, error) { var v PendingActivityInfo err := v.Decode(sr) return &v, err } func _List_PendingActivityInfo_Decode(sr stream.Reader) ([]*PendingActivityInfo, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*PendingActivityInfo, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _PendingActivityInfo_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _PendingChildExecutionInfo_Decode(sr stream.Reader) (*PendingChildExecutionInfo, error) { var v PendingChildExecutionInfo err := v.Decode(sr) return &v, err } func _List_PendingChildExecutionInfo_Decode(sr stream.Reader) ([]*PendingChildExecutionInfo, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*PendingChildExecutionInfo, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _PendingChildExecutionInfo_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _PendingDecisionInfo_Decode(sr stream.Reader) (*PendingDecisionInfo, error) { var v PendingDecisionInfo err := v.Decode(sr) return &v, err } // Decode deserializes a DescribeWorkflowExecutionResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DescribeWorkflowExecutionResponse struct could not be generated from the wire // representation. func (v *DescribeWorkflowExecutionResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.ExecutionConfiguration, err = _WorkflowExecutionConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecutionInfo, err = _WorkflowExecutionInfo_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TList: v.PendingActivities, err = _List_PendingActivityInfo_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TList: v.PendingChildren, err = _List_PendingChildExecutionInfo_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.PendingDecision, err = _PendingDecisionInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DescribeWorkflowExecutionResponse // struct. func (v *DescribeWorkflowExecutionResponse) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.ExecutionConfiguration != nil { fields[i] = fmt.Sprintf("ExecutionConfiguration: %v", v.ExecutionConfiguration) i++ } if v.WorkflowExecutionInfo != nil { fields[i] = fmt.Sprintf("WorkflowExecutionInfo: %v", v.WorkflowExecutionInfo) i++ } if v.PendingActivities != nil { fields[i] = fmt.Sprintf("PendingActivities: %v", v.PendingActivities) i++ } if v.PendingChildren != nil { fields[i] = fmt.Sprintf("PendingChildren: %v", v.PendingChildren) i++ } if v.PendingDecision != nil { fields[i] = fmt.Sprintf("PendingDecision: %v", v.PendingDecision) i++ } return fmt.Sprintf("DescribeWorkflowExecutionResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_PendingActivityInfo_Equals(lhs, rhs []*PendingActivityInfo) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } func _List_PendingChildExecutionInfo_Equals(lhs, rhs []*PendingChildExecutionInfo) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this DescribeWorkflowExecutionResponse match the // provided DescribeWorkflowExecutionResponse. // // This function performs a deep comparison. func (v *DescribeWorkflowExecutionResponse) Equals(rhs *DescribeWorkflowExecutionResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ExecutionConfiguration == nil && rhs.ExecutionConfiguration == nil) || (v.ExecutionConfiguration != nil && rhs.ExecutionConfiguration != nil && v.ExecutionConfiguration.Equals(rhs.ExecutionConfiguration))) { return false } if !((v.WorkflowExecutionInfo == nil && rhs.WorkflowExecutionInfo == nil) || (v.WorkflowExecutionInfo != nil && rhs.WorkflowExecutionInfo != nil && v.WorkflowExecutionInfo.Equals(rhs.WorkflowExecutionInfo))) { return false } if !((v.PendingActivities == nil && rhs.PendingActivities == nil) || (v.PendingActivities != nil && rhs.PendingActivities != nil && _List_PendingActivityInfo_Equals(v.PendingActivities, rhs.PendingActivities))) { return false } if !((v.PendingChildren == nil && rhs.PendingChildren == nil) || (v.PendingChildren != nil && rhs.PendingChildren != nil && _List_PendingChildExecutionInfo_Equals(v.PendingChildren, rhs.PendingChildren))) { return false } if !((v.PendingDecision == nil && rhs.PendingDecision == nil) || (v.PendingDecision != nil && rhs.PendingDecision != nil && v.PendingDecision.Equals(rhs.PendingDecision))) { return false } return true } type _List_PendingActivityInfo_Zapper []*PendingActivityInfo // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_PendingActivityInfo_Zapper. func (l _List_PendingActivityInfo_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } type _List_PendingChildExecutionInfo_Zapper []*PendingChildExecutionInfo // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_PendingChildExecutionInfo_Zapper. func (l _List_PendingChildExecutionInfo_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DescribeWorkflowExecutionResponse. func (v *DescribeWorkflowExecutionResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ExecutionConfiguration != nil { err = multierr.Append(err, enc.AddObject("executionConfiguration", v.ExecutionConfiguration)) } if v.WorkflowExecutionInfo != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionInfo", v.WorkflowExecutionInfo)) } if v.PendingActivities != nil { err = multierr.Append(err, enc.AddArray("pendingActivities", (_List_PendingActivityInfo_Zapper)(v.PendingActivities))) } if v.PendingChildren != nil { err = multierr.Append(err, enc.AddArray("pendingChildren", (_List_PendingChildExecutionInfo_Zapper)(v.PendingChildren))) } if v.PendingDecision != nil { err = multierr.Append(err, enc.AddObject("pendingDecision", v.PendingDecision)) } return err } // GetExecutionConfiguration returns the value of ExecutionConfiguration if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionResponse) GetExecutionConfiguration() (o *WorkflowExecutionConfiguration) { if v != nil && v.ExecutionConfiguration != nil { return v.ExecutionConfiguration } return } // IsSetExecutionConfiguration returns true if ExecutionConfiguration is not nil. func (v *DescribeWorkflowExecutionResponse) IsSetExecutionConfiguration() bool { return v != nil && v.ExecutionConfiguration != nil } // GetWorkflowExecutionInfo returns the value of WorkflowExecutionInfo if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionResponse) GetWorkflowExecutionInfo() (o *WorkflowExecutionInfo) { if v != nil && v.WorkflowExecutionInfo != nil { return v.WorkflowExecutionInfo } return } // IsSetWorkflowExecutionInfo returns true if WorkflowExecutionInfo is not nil. func (v *DescribeWorkflowExecutionResponse) IsSetWorkflowExecutionInfo() bool { return v != nil && v.WorkflowExecutionInfo != nil } // GetPendingActivities returns the value of PendingActivities if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionResponse) GetPendingActivities() (o []*PendingActivityInfo) { if v != nil && v.PendingActivities != nil { return v.PendingActivities } return } // IsSetPendingActivities returns true if PendingActivities is not nil. func (v *DescribeWorkflowExecutionResponse) IsSetPendingActivities() bool { return v != nil && v.PendingActivities != nil } // GetPendingChildren returns the value of PendingChildren if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionResponse) GetPendingChildren() (o []*PendingChildExecutionInfo) { if v != nil && v.PendingChildren != nil { return v.PendingChildren } return } // IsSetPendingChildren returns true if PendingChildren is not nil. func (v *DescribeWorkflowExecutionResponse) IsSetPendingChildren() bool { return v != nil && v.PendingChildren != nil } // GetPendingDecision returns the value of PendingDecision if it is set or its // zero value if it is unset. func (v *DescribeWorkflowExecutionResponse) GetPendingDecision() (o *PendingDecisionInfo) { if v != nil && v.PendingDecision != nil { return v.PendingDecision } return } // IsSetPendingDecision returns true if PendingDecision is not nil. func (v *DescribeWorkflowExecutionResponse) IsSetPendingDecision() bool { return v != nil && v.PendingDecision != nil } type DiagnoseWorkflowExecutionRequest struct { Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a DiagnoseWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DiagnoseWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DiagnoseWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DiagnoseWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DiagnoseWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DiagnoseWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a DiagnoseWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DiagnoseWorkflowExecutionRequest struct could not be encoded. func (v *DiagnoseWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DiagnoseWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DiagnoseWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *DiagnoseWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DiagnoseWorkflowExecutionRequest // struct. func (v *DiagnoseWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("DiagnoseWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DiagnoseWorkflowExecutionRequest match the // provided DiagnoseWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *DiagnoseWorkflowExecutionRequest) Equals(rhs *DiagnoseWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DiagnoseWorkflowExecutionRequest. func (v *DiagnoseWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *DiagnoseWorkflowExecutionRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *DiagnoseWorkflowExecutionRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *DiagnoseWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *DiagnoseWorkflowExecutionRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *DiagnoseWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *DiagnoseWorkflowExecutionRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } type DiagnoseWorkflowExecutionResponse struct { Domain *string `json:"domain,omitempty"` DiagnosticWorkflowExecution *WorkflowExecution `json:"diagnosticWorkflowExecution,omitempty"` } // ToWire translates a DiagnoseWorkflowExecutionResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DiagnoseWorkflowExecutionResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.DiagnosticWorkflowExecution != nil { w, err = v.DiagnosticWorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DiagnoseWorkflowExecutionResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DiagnoseWorkflowExecutionResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DiagnoseWorkflowExecutionResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DiagnoseWorkflowExecutionResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.DiagnosticWorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a DiagnoseWorkflowExecutionResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DiagnoseWorkflowExecutionResponse struct could not be encoded. func (v *DiagnoseWorkflowExecutionResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DiagnosticWorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.DiagnosticWorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DiagnoseWorkflowExecutionResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DiagnoseWorkflowExecutionResponse struct could not be generated from the wire // representation. func (v *DiagnoseWorkflowExecutionResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.DiagnosticWorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DiagnoseWorkflowExecutionResponse // struct. func (v *DiagnoseWorkflowExecutionResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.DiagnosticWorkflowExecution != nil { fields[i] = fmt.Sprintf("DiagnosticWorkflowExecution: %v", v.DiagnosticWorkflowExecution) i++ } return fmt.Sprintf("DiagnoseWorkflowExecutionResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DiagnoseWorkflowExecutionResponse match the // provided DiagnoseWorkflowExecutionResponse. // // This function performs a deep comparison. func (v *DiagnoseWorkflowExecutionResponse) Equals(rhs *DiagnoseWorkflowExecutionResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.DiagnosticWorkflowExecution == nil && rhs.DiagnosticWorkflowExecution == nil) || (v.DiagnosticWorkflowExecution != nil && rhs.DiagnosticWorkflowExecution != nil && v.DiagnosticWorkflowExecution.Equals(rhs.DiagnosticWorkflowExecution))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DiagnoseWorkflowExecutionResponse. func (v *DiagnoseWorkflowExecutionResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.DiagnosticWorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("diagnosticWorkflowExecution", v.DiagnosticWorkflowExecution)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *DiagnoseWorkflowExecutionResponse) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *DiagnoseWorkflowExecutionResponse) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetDiagnosticWorkflowExecution returns the value of DiagnosticWorkflowExecution if it is set or its // zero value if it is unset. func (v *DiagnoseWorkflowExecutionResponse) GetDiagnosticWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.DiagnosticWorkflowExecution != nil { return v.DiagnosticWorkflowExecution } return } // IsSetDiagnosticWorkflowExecution returns true if DiagnosticWorkflowExecution is not nil. func (v *DiagnoseWorkflowExecutionResponse) IsSetDiagnosticWorkflowExecution() bool { return v != nil && v.DiagnosticWorkflowExecution != nil } type DomainAlreadyExistsError struct { Message string `json:"message,required"` } // ToWire translates a DomainAlreadyExistsError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DomainAlreadyExistsError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DomainAlreadyExistsError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DomainAlreadyExistsError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DomainAlreadyExistsError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DomainAlreadyExistsError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of DomainAlreadyExistsError is required") } return nil } // Encode serializes a DomainAlreadyExistsError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DomainAlreadyExistsError struct could not be encoded. func (v *DomainAlreadyExistsError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a DomainAlreadyExistsError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DomainAlreadyExistsError struct could not be generated from the wire // representation. func (v *DomainAlreadyExistsError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of DomainAlreadyExistsError is required") } return nil } // String returns a readable string representation of a DomainAlreadyExistsError // struct. func (v *DomainAlreadyExistsError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("DomainAlreadyExistsError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*DomainAlreadyExistsError) ErrorName() string { return "DomainAlreadyExistsError" } // Equals returns true if all the fields of this DomainAlreadyExistsError match the // provided DomainAlreadyExistsError. // // This function performs a deep comparison. func (v *DomainAlreadyExistsError) Equals(rhs *DomainAlreadyExistsError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainAlreadyExistsError. func (v *DomainAlreadyExistsError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *DomainAlreadyExistsError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *DomainAlreadyExistsError) Error() string { return v.String() } type DomainCacheInfo struct { NumOfItemsInCacheByID *int64 `json:"numOfItemsInCacheByID,omitempty"` NumOfItemsInCacheByName *int64 `json:"numOfItemsInCacheByName,omitempty"` } // ToWire translates a DomainCacheInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DomainCacheInfo) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.NumOfItemsInCacheByID != nil { w, err = wire.NewValueI64(*(v.NumOfItemsInCacheByID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NumOfItemsInCacheByName != nil { w, err = wire.NewValueI64(*(v.NumOfItemsInCacheByName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DomainCacheInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DomainCacheInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DomainCacheInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DomainCacheInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NumOfItemsInCacheByID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NumOfItemsInCacheByName = &x if err != nil { return err } } } } return nil } // Encode serializes a DomainCacheInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DomainCacheInfo struct could not be encoded. func (v *DomainCacheInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.NumOfItemsInCacheByID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NumOfItemsInCacheByID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NumOfItemsInCacheByName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NumOfItemsInCacheByName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DomainCacheInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DomainCacheInfo struct could not be generated from the wire // representation. func (v *DomainCacheInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NumOfItemsInCacheByID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NumOfItemsInCacheByName = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DomainCacheInfo // struct. func (v *DomainCacheInfo) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.NumOfItemsInCacheByID != nil { fields[i] = fmt.Sprintf("NumOfItemsInCacheByID: %v", *(v.NumOfItemsInCacheByID)) i++ } if v.NumOfItemsInCacheByName != nil { fields[i] = fmt.Sprintf("NumOfItemsInCacheByName: %v", *(v.NumOfItemsInCacheByName)) i++ } return fmt.Sprintf("DomainCacheInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DomainCacheInfo match the // provided DomainCacheInfo. // // This function performs a deep comparison. func (v *DomainCacheInfo) Equals(rhs *DomainCacheInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.NumOfItemsInCacheByID, rhs.NumOfItemsInCacheByID) { return false } if !_I64_EqualsPtr(v.NumOfItemsInCacheByName, rhs.NumOfItemsInCacheByName) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainCacheInfo. func (v *DomainCacheInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.NumOfItemsInCacheByID != nil { enc.AddInt64("numOfItemsInCacheByID", *v.NumOfItemsInCacheByID) } if v.NumOfItemsInCacheByName != nil { enc.AddInt64("numOfItemsInCacheByName", *v.NumOfItemsInCacheByName) } return err } // GetNumOfItemsInCacheByID returns the value of NumOfItemsInCacheByID if it is set or its // zero value if it is unset. func (v *DomainCacheInfo) GetNumOfItemsInCacheByID() (o int64) { if v != nil && v.NumOfItemsInCacheByID != nil { return *v.NumOfItemsInCacheByID } return } // IsSetNumOfItemsInCacheByID returns true if NumOfItemsInCacheByID is not nil. func (v *DomainCacheInfo) IsSetNumOfItemsInCacheByID() bool { return v != nil && v.NumOfItemsInCacheByID != nil } // GetNumOfItemsInCacheByName returns the value of NumOfItemsInCacheByName if it is set or its // zero value if it is unset. func (v *DomainCacheInfo) GetNumOfItemsInCacheByName() (o int64) { if v != nil && v.NumOfItemsInCacheByName != nil { return *v.NumOfItemsInCacheByName } return } // IsSetNumOfItemsInCacheByName returns true if NumOfItemsInCacheByName is not nil. func (v *DomainCacheInfo) IsSetNumOfItemsInCacheByName() bool { return v != nil && v.NumOfItemsInCacheByName != nil } type DomainConfiguration struct { WorkflowExecutionRetentionPeriodInDays *int32 `json:"workflowExecutionRetentionPeriodInDays,omitempty"` EmitMetric *bool `json:"emitMetric,omitempty"` Isolationgroups *IsolationGroupConfiguration `json:"isolationgroups,omitempty"` BadBinaries *BadBinaries `json:"badBinaries,omitempty"` HistoryArchivalStatus *ArchivalStatus `json:"historyArchivalStatus,omitempty"` HistoryArchivalURI *string `json:"historyArchivalURI,omitempty"` VisibilityArchivalStatus *ArchivalStatus `json:"visibilityArchivalStatus,omitempty"` VisibilityArchivalURI *string `json:"visibilityArchivalURI,omitempty"` AsyncWorkflowConfiguration *AsyncWorkflowConfiguration `json:"AsyncWorkflowConfiguration,omitempty"` } // ToWire translates a DomainConfiguration struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DomainConfiguration) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.WorkflowExecutionRetentionPeriodInDays != nil { w, err = wire.NewValueI32(*(v.WorkflowExecutionRetentionPeriodInDays)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.EmitMetric != nil { w, err = wire.NewValueBool(*(v.EmitMetric)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Isolationgroups != nil { w, err = v.Isolationgroups.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.BadBinaries != nil { w, err = v.BadBinaries.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.HistoryArchivalStatus != nil { w, err = v.HistoryArchivalStatus.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.HistoryArchivalURI != nil { w, err = wire.NewValueString(*(v.HistoryArchivalURI)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.VisibilityArchivalStatus != nil { w, err = v.VisibilityArchivalStatus.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.VisibilityArchivalURI != nil { w, err = wire.NewValueString(*(v.VisibilityArchivalURI)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.AsyncWorkflowConfiguration != nil { w, err = v.AsyncWorkflowConfiguration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _IsolationGroupConfiguration_Read(w wire.Value) (*IsolationGroupConfiguration, error) { var v IsolationGroupConfiguration err := v.FromWire(w) return &v, err } func _BadBinaries_Read(w wire.Value) (*BadBinaries, error) { var v BadBinaries err := v.FromWire(w) return &v, err } func _ArchivalStatus_Read(w wire.Value) (ArchivalStatus, error) { var v ArchivalStatus err := v.FromWire(w) return v, err } func _AsyncWorkflowConfiguration_Read(w wire.Value) (*AsyncWorkflowConfiguration, error) { var v AsyncWorkflowConfiguration err := v.FromWire(w) return &v, err } // FromWire deserializes a DomainConfiguration struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DomainConfiguration struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DomainConfiguration // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DomainConfiguration) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.WorkflowExecutionRetentionPeriodInDays = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.EmitMetric = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.Isolationgroups, err = _IsolationGroupConfiguration_Read(field.Value) if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.BadBinaries, err = _BadBinaries_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TI32 { var x ArchivalStatus x, err = _ArchivalStatus_Read(field.Value) v.HistoryArchivalStatus = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.HistoryArchivalURI = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TI32 { var x ArchivalStatus x, err = _ArchivalStatus_Read(field.Value) v.VisibilityArchivalStatus = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.VisibilityArchivalURI = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TStruct { v.AsyncWorkflowConfiguration, err = _AsyncWorkflowConfiguration_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a DomainConfiguration struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DomainConfiguration struct could not be encoded. func (v *DomainConfiguration) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.WorkflowExecutionRetentionPeriodInDays != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.WorkflowExecutionRetentionPeriodInDays)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EmitMetric != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.EmitMetric)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Isolationgroups != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.Isolationgroups.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadBinaries != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.BadBinaries.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryArchivalStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI32}); err != nil { return err } if err := v.HistoryArchivalStatus.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryArchivalURI != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.HistoryArchivalURI)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityArchivalStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TI32}); err != nil { return err } if err := v.VisibilityArchivalStatus.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityArchivalURI != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.VisibilityArchivalURI)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AsyncWorkflowConfiguration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TStruct}); err != nil { return err } if err := v.AsyncWorkflowConfiguration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _IsolationGroupConfiguration_Decode(sr stream.Reader) (*IsolationGroupConfiguration, error) { var v IsolationGroupConfiguration err := v.Decode(sr) return &v, err } func _BadBinaries_Decode(sr stream.Reader) (*BadBinaries, error) { var v BadBinaries err := v.Decode(sr) return &v, err } func _ArchivalStatus_Decode(sr stream.Reader) (ArchivalStatus, error) { var v ArchivalStatus err := v.Decode(sr) return v, err } func _AsyncWorkflowConfiguration_Decode(sr stream.Reader) (*AsyncWorkflowConfiguration, error) { var v AsyncWorkflowConfiguration err := v.Decode(sr) return &v, err } // Decode deserializes a DomainConfiguration struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DomainConfiguration struct could not be generated from the wire // representation. func (v *DomainConfiguration) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.WorkflowExecutionRetentionPeriodInDays = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.EmitMetric = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.Isolationgroups, err = _IsolationGroupConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.BadBinaries, err = _BadBinaries_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI32: var x ArchivalStatus x, err = _ArchivalStatus_Decode(sr) v.HistoryArchivalStatus = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.HistoryArchivalURI = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TI32: var x ArchivalStatus x, err = _ArchivalStatus_Decode(sr) v.VisibilityArchivalStatus = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.VisibilityArchivalURI = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TStruct: v.AsyncWorkflowConfiguration, err = _AsyncWorkflowConfiguration_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DomainConfiguration // struct. func (v *DomainConfiguration) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.WorkflowExecutionRetentionPeriodInDays != nil { fields[i] = fmt.Sprintf("WorkflowExecutionRetentionPeriodInDays: %v", *(v.WorkflowExecutionRetentionPeriodInDays)) i++ } if v.EmitMetric != nil { fields[i] = fmt.Sprintf("EmitMetric: %v", *(v.EmitMetric)) i++ } if v.Isolationgroups != nil { fields[i] = fmt.Sprintf("Isolationgroups: %v", v.Isolationgroups) i++ } if v.BadBinaries != nil { fields[i] = fmt.Sprintf("BadBinaries: %v", v.BadBinaries) i++ } if v.HistoryArchivalStatus != nil { fields[i] = fmt.Sprintf("HistoryArchivalStatus: %v", *(v.HistoryArchivalStatus)) i++ } if v.HistoryArchivalURI != nil { fields[i] = fmt.Sprintf("HistoryArchivalURI: %v", *(v.HistoryArchivalURI)) i++ } if v.VisibilityArchivalStatus != nil { fields[i] = fmt.Sprintf("VisibilityArchivalStatus: %v", *(v.VisibilityArchivalStatus)) i++ } if v.VisibilityArchivalURI != nil { fields[i] = fmt.Sprintf("VisibilityArchivalURI: %v", *(v.VisibilityArchivalURI)) i++ } if v.AsyncWorkflowConfiguration != nil { fields[i] = fmt.Sprintf("AsyncWorkflowConfiguration: %v", v.AsyncWorkflowConfiguration) i++ } return fmt.Sprintf("DomainConfiguration{%v}", strings.Join(fields[:i], ", ")) } func _ArchivalStatus_EqualsPtr(lhs, rhs *ArchivalStatus) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this DomainConfiguration match the // provided DomainConfiguration. // // This function performs a deep comparison. func (v *DomainConfiguration) Equals(rhs *DomainConfiguration) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.WorkflowExecutionRetentionPeriodInDays, rhs.WorkflowExecutionRetentionPeriodInDays) { return false } if !_Bool_EqualsPtr(v.EmitMetric, rhs.EmitMetric) { return false } if !((v.Isolationgroups == nil && rhs.Isolationgroups == nil) || (v.Isolationgroups != nil && rhs.Isolationgroups != nil && v.Isolationgroups.Equals(rhs.Isolationgroups))) { return false } if !((v.BadBinaries == nil && rhs.BadBinaries == nil) || (v.BadBinaries != nil && rhs.BadBinaries != nil && v.BadBinaries.Equals(rhs.BadBinaries))) { return false } if !_ArchivalStatus_EqualsPtr(v.HistoryArchivalStatus, rhs.HistoryArchivalStatus) { return false } if !_String_EqualsPtr(v.HistoryArchivalURI, rhs.HistoryArchivalURI) { return false } if !_ArchivalStatus_EqualsPtr(v.VisibilityArchivalStatus, rhs.VisibilityArchivalStatus) { return false } if !_String_EqualsPtr(v.VisibilityArchivalURI, rhs.VisibilityArchivalURI) { return false } if !((v.AsyncWorkflowConfiguration == nil && rhs.AsyncWorkflowConfiguration == nil) || (v.AsyncWorkflowConfiguration != nil && rhs.AsyncWorkflowConfiguration != nil && v.AsyncWorkflowConfiguration.Equals(rhs.AsyncWorkflowConfiguration))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainConfiguration. func (v *DomainConfiguration) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.WorkflowExecutionRetentionPeriodInDays != nil { enc.AddInt32("workflowExecutionRetentionPeriodInDays", *v.WorkflowExecutionRetentionPeriodInDays) } if v.EmitMetric != nil { enc.AddBool("emitMetric", *v.EmitMetric) } if v.Isolationgroups != nil { err = multierr.Append(err, enc.AddObject("isolationgroups", v.Isolationgroups)) } if v.BadBinaries != nil { err = multierr.Append(err, enc.AddObject("badBinaries", v.BadBinaries)) } if v.HistoryArchivalStatus != nil { err = multierr.Append(err, enc.AddObject("historyArchivalStatus", *v.HistoryArchivalStatus)) } if v.HistoryArchivalURI != nil { enc.AddString("historyArchivalURI", *v.HistoryArchivalURI) } if v.VisibilityArchivalStatus != nil { err = multierr.Append(err, enc.AddObject("visibilityArchivalStatus", *v.VisibilityArchivalStatus)) } if v.VisibilityArchivalURI != nil { enc.AddString("visibilityArchivalURI", *v.VisibilityArchivalURI) } if v.AsyncWorkflowConfiguration != nil { err = multierr.Append(err, enc.AddObject("AsyncWorkflowConfiguration", v.AsyncWorkflowConfiguration)) } return err } // GetWorkflowExecutionRetentionPeriodInDays returns the value of WorkflowExecutionRetentionPeriodInDays if it is set or its // zero value if it is unset. func (v *DomainConfiguration) GetWorkflowExecutionRetentionPeriodInDays() (o int32) { if v != nil && v.WorkflowExecutionRetentionPeriodInDays != nil { return *v.WorkflowExecutionRetentionPeriodInDays } return } // IsSetWorkflowExecutionRetentionPeriodInDays returns true if WorkflowExecutionRetentionPeriodInDays is not nil. func (v *DomainConfiguration) IsSetWorkflowExecutionRetentionPeriodInDays() bool { return v != nil && v.WorkflowExecutionRetentionPeriodInDays != nil } // GetEmitMetric returns the value of EmitMetric if it is set or its // zero value if it is unset. func (v *DomainConfiguration) GetEmitMetric() (o bool) { if v != nil && v.EmitMetric != nil { return *v.EmitMetric } return } // IsSetEmitMetric returns true if EmitMetric is not nil. func (v *DomainConfiguration) IsSetEmitMetric() bool { return v != nil && v.EmitMetric != nil } // GetIsolationgroups returns the value of Isolationgroups if it is set or its // zero value if it is unset. func (v *DomainConfiguration) GetIsolationgroups() (o *IsolationGroupConfiguration) { if v != nil && v.Isolationgroups != nil { return v.Isolationgroups } return } // IsSetIsolationgroups returns true if Isolationgroups is not nil. func (v *DomainConfiguration) IsSetIsolationgroups() bool { return v != nil && v.Isolationgroups != nil } // GetBadBinaries returns the value of BadBinaries if it is set or its // zero value if it is unset. func (v *DomainConfiguration) GetBadBinaries() (o *BadBinaries) { if v != nil && v.BadBinaries != nil { return v.BadBinaries } return } // IsSetBadBinaries returns true if BadBinaries is not nil. func (v *DomainConfiguration) IsSetBadBinaries() bool { return v != nil && v.BadBinaries != nil } // GetHistoryArchivalStatus returns the value of HistoryArchivalStatus if it is set or its // zero value if it is unset. func (v *DomainConfiguration) GetHistoryArchivalStatus() (o ArchivalStatus) { if v != nil && v.HistoryArchivalStatus != nil { return *v.HistoryArchivalStatus } return } // IsSetHistoryArchivalStatus returns true if HistoryArchivalStatus is not nil. func (v *DomainConfiguration) IsSetHistoryArchivalStatus() bool { return v != nil && v.HistoryArchivalStatus != nil } // GetHistoryArchivalURI returns the value of HistoryArchivalURI if it is set or its // zero value if it is unset. func (v *DomainConfiguration) GetHistoryArchivalURI() (o string) { if v != nil && v.HistoryArchivalURI != nil { return *v.HistoryArchivalURI } return } // IsSetHistoryArchivalURI returns true if HistoryArchivalURI is not nil. func (v *DomainConfiguration) IsSetHistoryArchivalURI() bool { return v != nil && v.HistoryArchivalURI != nil } // GetVisibilityArchivalStatus returns the value of VisibilityArchivalStatus if it is set or its // zero value if it is unset. func (v *DomainConfiguration) GetVisibilityArchivalStatus() (o ArchivalStatus) { if v != nil && v.VisibilityArchivalStatus != nil { return *v.VisibilityArchivalStatus } return } // IsSetVisibilityArchivalStatus returns true if VisibilityArchivalStatus is not nil. func (v *DomainConfiguration) IsSetVisibilityArchivalStatus() bool { return v != nil && v.VisibilityArchivalStatus != nil } // GetVisibilityArchivalURI returns the value of VisibilityArchivalURI if it is set or its // zero value if it is unset. func (v *DomainConfiguration) GetVisibilityArchivalURI() (o string) { if v != nil && v.VisibilityArchivalURI != nil { return *v.VisibilityArchivalURI } return } // IsSetVisibilityArchivalURI returns true if VisibilityArchivalURI is not nil. func (v *DomainConfiguration) IsSetVisibilityArchivalURI() bool { return v != nil && v.VisibilityArchivalURI != nil } // GetAsyncWorkflowConfiguration returns the value of AsyncWorkflowConfiguration if it is set or its // zero value if it is unset. func (v *DomainConfiguration) GetAsyncWorkflowConfiguration() (o *AsyncWorkflowConfiguration) { if v != nil && v.AsyncWorkflowConfiguration != nil { return v.AsyncWorkflowConfiguration } return } // IsSetAsyncWorkflowConfiguration returns true if AsyncWorkflowConfiguration is not nil. func (v *DomainConfiguration) IsSetAsyncWorkflowConfiguration() bool { return v != nil && v.AsyncWorkflowConfiguration != nil } type DomainIDPredicateAttributes struct { DomainIDs []string `json:"domainIDs,omitempty"` IsExclusive *bool `json:"isExclusive,omitempty"` } // ToWire translates a DomainIDPredicateAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DomainIDPredicateAttributes) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DomainIDs != nil { w, err = wire.NewValueList(_List_String_ValueList(v.DomainIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.IsExclusive != nil { w, err = wire.NewValueBool(*(v.IsExclusive)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DomainIDPredicateAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DomainIDPredicateAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DomainIDPredicateAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DomainIDPredicateAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.DomainIDs, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.IsExclusive = &x if err != nil { return err } } } } return nil } // Encode serializes a DomainIDPredicateAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DomainIDPredicateAttributes struct could not be encoded. func (v *DomainIDPredicateAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.DomainIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsExclusive != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.IsExclusive)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a DomainIDPredicateAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DomainIDPredicateAttributes struct could not be generated from the wire // representation. func (v *DomainIDPredicateAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.DomainIDs, err = _List_String_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.IsExclusive = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DomainIDPredicateAttributes // struct. func (v *DomainIDPredicateAttributes) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DomainIDs != nil { fields[i] = fmt.Sprintf("DomainIDs: %v", v.DomainIDs) i++ } if v.IsExclusive != nil { fields[i] = fmt.Sprintf("IsExclusive: %v", *(v.IsExclusive)) i++ } return fmt.Sprintf("DomainIDPredicateAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this DomainIDPredicateAttributes match the // provided DomainIDPredicateAttributes. // // This function performs a deep comparison. func (v *DomainIDPredicateAttributes) Equals(rhs *DomainIDPredicateAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DomainIDs == nil && rhs.DomainIDs == nil) || (v.DomainIDs != nil && rhs.DomainIDs != nil && _List_String_Equals(v.DomainIDs, rhs.DomainIDs))) { return false } if !_Bool_EqualsPtr(v.IsExclusive, rhs.IsExclusive) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainIDPredicateAttributes. func (v *DomainIDPredicateAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainIDs != nil { err = multierr.Append(err, enc.AddArray("domainIDs", (_List_String_Zapper)(v.DomainIDs))) } if v.IsExclusive != nil { enc.AddBool("isExclusive", *v.IsExclusive) } return err } // GetDomainIDs returns the value of DomainIDs if it is set or its // zero value if it is unset. func (v *DomainIDPredicateAttributes) GetDomainIDs() (o []string) { if v != nil && v.DomainIDs != nil { return v.DomainIDs } return } // IsSetDomainIDs returns true if DomainIDs is not nil. func (v *DomainIDPredicateAttributes) IsSetDomainIDs() bool { return v != nil && v.DomainIDs != nil } // GetIsExclusive returns the value of IsExclusive if it is set or its // zero value if it is unset. func (v *DomainIDPredicateAttributes) GetIsExclusive() (o bool) { if v != nil && v.IsExclusive != nil { return *v.IsExclusive } return } // IsSetIsExclusive returns true if IsExclusive is not nil. func (v *DomainIDPredicateAttributes) IsSetIsExclusive() bool { return v != nil && v.IsExclusive != nil } type DomainInfo struct { Name *string `json:"name,omitempty"` Status *DomainStatus `json:"status,omitempty"` Description *string `json:"description,omitempty"` OwnerEmail *string `json:"ownerEmail,omitempty"` Data map[string]string `json:"data,omitempty"` UUID *string `json:"uuid,omitempty"` } // ToWire translates a DomainInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DomainInfo) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Status != nil { w, err = v.Status.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Description != nil { w, err = wire.NewValueString(*(v.Description)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.OwnerEmail != nil { w, err = wire.NewValueString(*(v.OwnerEmail)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Data != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.Data)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.UUID != nil { w, err = wire.NewValueString(*(v.UUID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DomainStatus_Read(w wire.Value) (DomainStatus, error) { var v DomainStatus err := v.FromWire(w) return v, err } // FromWire deserializes a DomainInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DomainInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DomainInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DomainInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x DomainStatus x, err = _DomainStatus_Read(field.Value) v.Status = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Description = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.OwnerEmail = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TMap { v.Data, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.UUID = &x if err != nil { return err } } } } return nil } // Encode serializes a DomainInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DomainInfo struct could not be encoded. func (v *DomainInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Status != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := v.Status.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Description != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Description)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.OwnerEmail != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.OwnerEmail)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Data != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.Data, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.UUID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.UUID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DomainStatus_Decode(sr stream.Reader) (DomainStatus, error) { var v DomainStatus err := v.Decode(sr) return v, err } // Decode deserializes a DomainInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DomainInfo struct could not be generated from the wire // representation. func (v *DomainInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x DomainStatus x, err = _DomainStatus_Decode(sr) v.Status = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Description = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.OwnerEmail = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TMap: v.Data, err = _Map_String_String_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.UUID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DomainInfo // struct. func (v *DomainInfo) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.Status != nil { fields[i] = fmt.Sprintf("Status: %v", *(v.Status)) i++ } if v.Description != nil { fields[i] = fmt.Sprintf("Description: %v", *(v.Description)) i++ } if v.OwnerEmail != nil { fields[i] = fmt.Sprintf("OwnerEmail: %v", *(v.OwnerEmail)) i++ } if v.Data != nil { fields[i] = fmt.Sprintf("Data: %v", v.Data) i++ } if v.UUID != nil { fields[i] = fmt.Sprintf("UUID: %v", *(v.UUID)) i++ } return fmt.Sprintf("DomainInfo{%v}", strings.Join(fields[:i], ", ")) } func _DomainStatus_EqualsPtr(lhs, rhs *DomainStatus) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this DomainInfo match the // provided DomainInfo. // // This function performs a deep comparison. func (v *DomainInfo) Equals(rhs *DomainInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !_DomainStatus_EqualsPtr(v.Status, rhs.Status) { return false } if !_String_EqualsPtr(v.Description, rhs.Description) { return false } if !_String_EqualsPtr(v.OwnerEmail, rhs.OwnerEmail) { return false } if !((v.Data == nil && rhs.Data == nil) || (v.Data != nil && rhs.Data != nil && _Map_String_String_Equals(v.Data, rhs.Data))) { return false } if !_String_EqualsPtr(v.UUID, rhs.UUID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainInfo. func (v *DomainInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.Status != nil { err = multierr.Append(err, enc.AddObject("status", *v.Status)) } if v.Description != nil { enc.AddString("description", *v.Description) } if v.OwnerEmail != nil { enc.AddString("ownerEmail", *v.OwnerEmail) } if v.Data != nil { err = multierr.Append(err, enc.AddObject("data", (_Map_String_String_Zapper)(v.Data))) } if v.UUID != nil { enc.AddString("uuid", *v.UUID) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *DomainInfo) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *DomainInfo) IsSetName() bool { return v != nil && v.Name != nil } // GetStatus returns the value of Status if it is set or its // zero value if it is unset. func (v *DomainInfo) GetStatus() (o DomainStatus) { if v != nil && v.Status != nil { return *v.Status } return } // IsSetStatus returns true if Status is not nil. func (v *DomainInfo) IsSetStatus() bool { return v != nil && v.Status != nil } // GetDescription returns the value of Description if it is set or its // zero value if it is unset. func (v *DomainInfo) GetDescription() (o string) { if v != nil && v.Description != nil { return *v.Description } return } // IsSetDescription returns true if Description is not nil. func (v *DomainInfo) IsSetDescription() bool { return v != nil && v.Description != nil } // GetOwnerEmail returns the value of OwnerEmail if it is set or its // zero value if it is unset. func (v *DomainInfo) GetOwnerEmail() (o string) { if v != nil && v.OwnerEmail != nil { return *v.OwnerEmail } return } // IsSetOwnerEmail returns true if OwnerEmail is not nil. func (v *DomainInfo) IsSetOwnerEmail() bool { return v != nil && v.OwnerEmail != nil } // GetData returns the value of Data if it is set or its // zero value if it is unset. func (v *DomainInfo) GetData() (o map[string]string) { if v != nil && v.Data != nil { return v.Data } return } // IsSetData returns true if Data is not nil. func (v *DomainInfo) IsSetData() bool { return v != nil && v.Data != nil } // GetUUID returns the value of UUID if it is set or its // zero value if it is unset. func (v *DomainInfo) GetUUID() (o string) { if v != nil && v.UUID != nil { return *v.UUID } return } // IsSetUUID returns true if UUID is not nil. func (v *DomainInfo) IsSetUUID() bool { return v != nil && v.UUID != nil } type DomainNotActiveError struct { Message string `json:"message,required"` DomainName string `json:"domainName,required"` CurrentCluster string `json:"currentCluster,required"` ActiveCluster string `json:"activeCluster,required"` ActiveClusters []string `json:"activeClusters,required"` } // ToWire translates a DomainNotActiveError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DomainNotActiveError) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ w, err = wire.NewValueString(v.DomainName), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ w, err = wire.NewValueString(v.CurrentCluster), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ w, err = wire.NewValueString(v.ActiveCluster), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ w, err = wire.NewValueList(_List_String_ValueList(v.ActiveClusters)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a DomainNotActiveError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DomainNotActiveError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DomainNotActiveError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DomainNotActiveError) FromWire(w wire.Value) error { var err error messageIsSet := false domainNameIsSet := false currentClusterIsSet := false activeClusterIsSet := false activeClustersIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } case 2: if field.Value.Type() == wire.TBinary { v.DomainName, err = field.Value.GetString(), error(nil) if err != nil { return err } domainNameIsSet = true } case 3: if field.Value.Type() == wire.TBinary { v.CurrentCluster, err = field.Value.GetString(), error(nil) if err != nil { return err } currentClusterIsSet = true } case 4: if field.Value.Type() == wire.TBinary { v.ActiveCluster, err = field.Value.GetString(), error(nil) if err != nil { return err } activeClusterIsSet = true } case 5: if field.Value.Type() == wire.TList { v.ActiveClusters, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } activeClustersIsSet = true } } } if !messageIsSet { return errors.New("field Message of DomainNotActiveError is required") } if !domainNameIsSet { return errors.New("field DomainName of DomainNotActiveError is required") } if !currentClusterIsSet { return errors.New("field CurrentCluster of DomainNotActiveError is required") } if !activeClusterIsSet { return errors.New("field ActiveCluster of DomainNotActiveError is required") } if !activeClustersIsSet { return errors.New("field ActiveClusters of DomainNotActiveError is required") } return nil } // Encode serializes a DomainNotActiveError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DomainNotActiveError struct could not be encoded. func (v *DomainNotActiveError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.DomainName); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.CurrentCluster); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.ActiveCluster); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.ActiveClusters, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a DomainNotActiveError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DomainNotActiveError struct could not be generated from the wire // representation. func (v *DomainNotActiveError) Decode(sr stream.Reader) error { messageIsSet := false domainNameIsSet := false currentClusterIsSet := false activeClusterIsSet := false activeClustersIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true case fh.ID == 2 && fh.Type == wire.TBinary: v.DomainName, err = sr.ReadString() if err != nil { return err } domainNameIsSet = true case fh.ID == 3 && fh.Type == wire.TBinary: v.CurrentCluster, err = sr.ReadString() if err != nil { return err } currentClusterIsSet = true case fh.ID == 4 && fh.Type == wire.TBinary: v.ActiveCluster, err = sr.ReadString() if err != nil { return err } activeClusterIsSet = true case fh.ID == 5 && fh.Type == wire.TList: v.ActiveClusters, err = _List_String_Decode(sr) if err != nil { return err } activeClustersIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of DomainNotActiveError is required") } if !domainNameIsSet { return errors.New("field DomainName of DomainNotActiveError is required") } if !currentClusterIsSet { return errors.New("field CurrentCluster of DomainNotActiveError is required") } if !activeClusterIsSet { return errors.New("field ActiveCluster of DomainNotActiveError is required") } if !activeClustersIsSet { return errors.New("field ActiveClusters of DomainNotActiveError is required") } return nil } // String returns a readable string representation of a DomainNotActiveError // struct. func (v *DomainNotActiveError) String() string { if v == nil { return "" } var fields [5]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ fields[i] = fmt.Sprintf("DomainName: %v", v.DomainName) i++ fields[i] = fmt.Sprintf("CurrentCluster: %v", v.CurrentCluster) i++ fields[i] = fmt.Sprintf("ActiveCluster: %v", v.ActiveCluster) i++ fields[i] = fmt.Sprintf("ActiveClusters: %v", v.ActiveClusters) i++ return fmt.Sprintf("DomainNotActiveError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*DomainNotActiveError) ErrorName() string { return "DomainNotActiveError" } // Equals returns true if all the fields of this DomainNotActiveError match the // provided DomainNotActiveError. // // This function performs a deep comparison. func (v *DomainNotActiveError) Equals(rhs *DomainNotActiveError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } if !(v.DomainName == rhs.DomainName) { return false } if !(v.CurrentCluster == rhs.CurrentCluster) { return false } if !(v.ActiveCluster == rhs.ActiveCluster) { return false } if !_List_String_Equals(v.ActiveClusters, rhs.ActiveClusters) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainNotActiveError. func (v *DomainNotActiveError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) enc.AddString("domainName", v.DomainName) enc.AddString("currentCluster", v.CurrentCluster) enc.AddString("activeCluster", v.ActiveCluster) err = multierr.Append(err, enc.AddArray("activeClusters", (_List_String_Zapper)(v.ActiveClusters))) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *DomainNotActiveError) GetMessage() (o string) { if v != nil { o = v.Message } return } // GetDomainName returns the value of DomainName if it is set or its // zero value if it is unset. func (v *DomainNotActiveError) GetDomainName() (o string) { if v != nil { o = v.DomainName } return } // GetCurrentCluster returns the value of CurrentCluster if it is set or its // zero value if it is unset. func (v *DomainNotActiveError) GetCurrentCluster() (o string) { if v != nil { o = v.CurrentCluster } return } // GetActiveCluster returns the value of ActiveCluster if it is set or its // zero value if it is unset. func (v *DomainNotActiveError) GetActiveCluster() (o string) { if v != nil { o = v.ActiveCluster } return } // GetActiveClusters returns the value of ActiveClusters if it is set or its // zero value if it is unset. func (v *DomainNotActiveError) GetActiveClusters() (o []string) { if v != nil { o = v.ActiveClusters } return } // IsSetActiveClusters returns true if ActiveClusters is not nil. func (v *DomainNotActiveError) IsSetActiveClusters() bool { return v != nil && v.ActiveClusters != nil } func (v *DomainNotActiveError) Error() string { return v.String() } type DomainReplicationConfiguration struct { ActiveClusterName *string `json:"activeClusterName,omitempty"` Clusters []*ClusterReplicationConfiguration `json:"clusters,omitempty"` ActiveClusters *ActiveClusters `json:"activeClusters,omitempty"` } type _List_ClusterReplicationConfiguration_ValueList []*ClusterReplicationConfiguration func (v _List_ClusterReplicationConfiguration_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*ClusterReplicationConfiguration', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_ClusterReplicationConfiguration_ValueList) Size() int { return len(v) } func (_List_ClusterReplicationConfiguration_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_ClusterReplicationConfiguration_ValueList) Close() {} // ToWire translates a DomainReplicationConfiguration struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DomainReplicationConfiguration) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.ActiveClusterName != nil { w, err = wire.NewValueString(*(v.ActiveClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Clusters != nil { w, err = wire.NewValueList(_List_ClusterReplicationConfiguration_ValueList(v.Clusters)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ActiveClusters != nil { w, err = v.ActiveClusters.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ClusterReplicationConfiguration_Read(w wire.Value) (*ClusterReplicationConfiguration, error) { var v ClusterReplicationConfiguration err := v.FromWire(w) return &v, err } func _List_ClusterReplicationConfiguration_Read(l wire.ValueList) ([]*ClusterReplicationConfiguration, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*ClusterReplicationConfiguration, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _ClusterReplicationConfiguration_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _ActiveClusters_Read(w wire.Value) (*ActiveClusters, error) { var v ActiveClusters err := v.FromWire(w) return &v, err } // FromWire deserializes a DomainReplicationConfiguration struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DomainReplicationConfiguration struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DomainReplicationConfiguration // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DomainReplicationConfiguration) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActiveClusterName = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Clusters, err = _List_ClusterReplicationConfiguration_Read(field.Value.GetList()) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ActiveClusters, err = _ActiveClusters_Read(field.Value) if err != nil { return err } } } } return nil } func _List_ClusterReplicationConfiguration_Encode(val []*ClusterReplicationConfiguration, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*ClusterReplicationConfiguration', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a DomainReplicationConfiguration struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DomainReplicationConfiguration struct could not be encoded. func (v *DomainReplicationConfiguration) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActiveClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActiveClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Clusters != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_ClusterReplicationConfiguration_Encode(v.Clusters, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusters != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusters.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ClusterReplicationConfiguration_Decode(sr stream.Reader) (*ClusterReplicationConfiguration, error) { var v ClusterReplicationConfiguration err := v.Decode(sr) return &v, err } func _List_ClusterReplicationConfiguration_Decode(sr stream.Reader) ([]*ClusterReplicationConfiguration, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*ClusterReplicationConfiguration, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _ClusterReplicationConfiguration_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _ActiveClusters_Decode(sr stream.Reader) (*ActiveClusters, error) { var v ActiveClusters err := v.Decode(sr) return &v, err } // Decode deserializes a DomainReplicationConfiguration struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DomainReplicationConfiguration struct could not be generated from the wire // representation. func (v *DomainReplicationConfiguration) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActiveClusterName = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Clusters, err = _List_ClusterReplicationConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ActiveClusters, err = _ActiveClusters_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DomainReplicationConfiguration // struct. func (v *DomainReplicationConfiguration) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.ActiveClusterName != nil { fields[i] = fmt.Sprintf("ActiveClusterName: %v", *(v.ActiveClusterName)) i++ } if v.Clusters != nil { fields[i] = fmt.Sprintf("Clusters: %v", v.Clusters) i++ } if v.ActiveClusters != nil { fields[i] = fmt.Sprintf("ActiveClusters: %v", v.ActiveClusters) i++ } return fmt.Sprintf("DomainReplicationConfiguration{%v}", strings.Join(fields[:i], ", ")) } func _List_ClusterReplicationConfiguration_Equals(lhs, rhs []*ClusterReplicationConfiguration) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this DomainReplicationConfiguration match the // provided DomainReplicationConfiguration. // // This function performs a deep comparison. func (v *DomainReplicationConfiguration) Equals(rhs *DomainReplicationConfiguration) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ActiveClusterName, rhs.ActiveClusterName) { return false } if !((v.Clusters == nil && rhs.Clusters == nil) || (v.Clusters != nil && rhs.Clusters != nil && _List_ClusterReplicationConfiguration_Equals(v.Clusters, rhs.Clusters))) { return false } if !((v.ActiveClusters == nil && rhs.ActiveClusters == nil) || (v.ActiveClusters != nil && rhs.ActiveClusters != nil && v.ActiveClusters.Equals(rhs.ActiveClusters))) { return false } return true } type _List_ClusterReplicationConfiguration_Zapper []*ClusterReplicationConfiguration // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_ClusterReplicationConfiguration_Zapper. func (l _List_ClusterReplicationConfiguration_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainReplicationConfiguration. func (v *DomainReplicationConfiguration) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActiveClusterName != nil { enc.AddString("activeClusterName", *v.ActiveClusterName) } if v.Clusters != nil { err = multierr.Append(err, enc.AddArray("clusters", (_List_ClusterReplicationConfiguration_Zapper)(v.Clusters))) } if v.ActiveClusters != nil { err = multierr.Append(err, enc.AddObject("activeClusters", v.ActiveClusters)) } return err } // GetActiveClusterName returns the value of ActiveClusterName if it is set or its // zero value if it is unset. func (v *DomainReplicationConfiguration) GetActiveClusterName() (o string) { if v != nil && v.ActiveClusterName != nil { return *v.ActiveClusterName } return } // IsSetActiveClusterName returns true if ActiveClusterName is not nil. func (v *DomainReplicationConfiguration) IsSetActiveClusterName() bool { return v != nil && v.ActiveClusterName != nil } // GetClusters returns the value of Clusters if it is set or its // zero value if it is unset. func (v *DomainReplicationConfiguration) GetClusters() (o []*ClusterReplicationConfiguration) { if v != nil && v.Clusters != nil { return v.Clusters } return } // IsSetClusters returns true if Clusters is not nil. func (v *DomainReplicationConfiguration) IsSetClusters() bool { return v != nil && v.Clusters != nil } // GetActiveClusters returns the value of ActiveClusters if it is set or its // zero value if it is unset. func (v *DomainReplicationConfiguration) GetActiveClusters() (o *ActiveClusters) { if v != nil && v.ActiveClusters != nil { return v.ActiveClusters } return } // IsSetActiveClusters returns true if ActiveClusters is not nil. func (v *DomainReplicationConfiguration) IsSetActiveClusters() bool { return v != nil && v.ActiveClusters != nil } type DomainStatus int32 const ( DomainStatusRegistered DomainStatus = 0 DomainStatusDeprecated DomainStatus = 1 DomainStatusDeleted DomainStatus = 2 ) // DomainStatus_Values returns all recognized values of DomainStatus. func DomainStatus_Values() []DomainStatus { return []DomainStatus{ DomainStatusRegistered, DomainStatusDeprecated, DomainStatusDeleted, } } // UnmarshalText tries to decode DomainStatus from a byte slice // containing its name. // // var v DomainStatus // err := v.UnmarshalText([]byte("REGISTERED")) func (v *DomainStatus) UnmarshalText(value []byte) error { switch s := string(value); s { case "REGISTERED": *v = DomainStatusRegistered return nil case "DEPRECATED": *v = DomainStatusDeprecated return nil case "DELETED": *v = DomainStatusDeleted return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DomainStatus", err) } *v = DomainStatus(val) return nil } } // MarshalText encodes DomainStatus to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v DomainStatus) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("REGISTERED"), nil case 1: return []byte("DEPRECATED"), nil case 2: return []byte("DELETED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainStatus. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v DomainStatus) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "REGISTERED") case 1: enc.AddString("name", "DEPRECATED") case 2: enc.AddString("name", "DELETED") } return nil } // Ptr returns a pointer to this enum value. func (v DomainStatus) Ptr() *DomainStatus { return &v } // Encode encodes DomainStatus directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v DomainStatus // return v.Encode(sWriter) func (v DomainStatus) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates DomainStatus into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v DomainStatus) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes DomainStatus from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return DomainStatus(0), err // } // // var v DomainStatus // if err := v.FromWire(x); err != nil { // return DomainStatus(0), err // } // return v, nil func (v *DomainStatus) FromWire(w wire.Value) error { *v = (DomainStatus)(w.GetI32()) return nil } // Decode reads off the encoded DomainStatus directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v DomainStatus // if err := v.Decode(sReader); err != nil { // return DomainStatus(0), err // } // return v, nil func (v *DomainStatus) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (DomainStatus)(i) return nil } // String returns a readable string representation of DomainStatus. func (v DomainStatus) String() string { w := int32(v) switch w { case 0: return "REGISTERED" case 1: return "DEPRECATED" case 2: return "DELETED" } return fmt.Sprintf("DomainStatus(%d)", w) } // Equals returns true if this DomainStatus value matches the provided // value. func (v DomainStatus) Equals(rhs DomainStatus) bool { return v == rhs } // MarshalJSON serializes DomainStatus into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v DomainStatus) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"REGISTERED\""), nil case 1: return ([]byte)("\"DEPRECATED\""), nil case 2: return ([]byte)("\"DELETED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode DomainStatus from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *DomainStatus) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "DomainStatus") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "DomainStatus") } *v = (DomainStatus)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "DomainStatus") } } type EmptyPredicateAttributes struct { } // ToWire translates a EmptyPredicateAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *EmptyPredicateAttributes) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a EmptyPredicateAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a EmptyPredicateAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v EmptyPredicateAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *EmptyPredicateAttributes) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a EmptyPredicateAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a EmptyPredicateAttributes struct could not be encoded. func (v *EmptyPredicateAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a EmptyPredicateAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a EmptyPredicateAttributes struct could not be generated from the wire // representation. func (v *EmptyPredicateAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a EmptyPredicateAttributes // struct. func (v *EmptyPredicateAttributes) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("EmptyPredicateAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this EmptyPredicateAttributes match the // provided EmptyPredicateAttributes. // // This function performs a deep comparison. func (v *EmptyPredicateAttributes) Equals(rhs *EmptyPredicateAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of EmptyPredicateAttributes. func (v *EmptyPredicateAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type EncodingType int32 const ( EncodingTypeThriftRW EncodingType = 0 EncodingTypeJSON EncodingType = 1 ) // EncodingType_Values returns all recognized values of EncodingType. func EncodingType_Values() []EncodingType { return []EncodingType{ EncodingTypeThriftRW, EncodingTypeJSON, } } // UnmarshalText tries to decode EncodingType from a byte slice // containing its name. // // var v EncodingType // err := v.UnmarshalText([]byte("ThriftRW")) func (v *EncodingType) UnmarshalText(value []byte) error { switch s := string(value); s { case "ThriftRW": *v = EncodingTypeThriftRW return nil case "JSON": *v = EncodingTypeJSON return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "EncodingType", err) } *v = EncodingType(val) return nil } } // MarshalText encodes EncodingType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v EncodingType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("ThriftRW"), nil case 1: return []byte("JSON"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of EncodingType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v EncodingType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "ThriftRW") case 1: enc.AddString("name", "JSON") } return nil } // Ptr returns a pointer to this enum value. func (v EncodingType) Ptr() *EncodingType { return &v } // Encode encodes EncodingType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v EncodingType // return v.Encode(sWriter) func (v EncodingType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates EncodingType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v EncodingType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes EncodingType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return EncodingType(0), err // } // // var v EncodingType // if err := v.FromWire(x); err != nil { // return EncodingType(0), err // } // return v, nil func (v *EncodingType) FromWire(w wire.Value) error { *v = (EncodingType)(w.GetI32()) return nil } // Decode reads off the encoded EncodingType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v EncodingType // if err := v.Decode(sReader); err != nil { // return EncodingType(0), err // } // return v, nil func (v *EncodingType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (EncodingType)(i) return nil } // String returns a readable string representation of EncodingType. func (v EncodingType) String() string { w := int32(v) switch w { case 0: return "ThriftRW" case 1: return "JSON" } return fmt.Sprintf("EncodingType(%d)", w) } // Equals returns true if this EncodingType value matches the provided // value. func (v EncodingType) Equals(rhs EncodingType) bool { return v == rhs } // MarshalJSON serializes EncodingType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v EncodingType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"ThriftRW\""), nil case 1: return ([]byte)("\"JSON\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode EncodingType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *EncodingType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "EncodingType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "EncodingType") } *v = (EncodingType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "EncodingType") } } type EntityNotExistsError struct { Message string `json:"message,required"` CurrentCluster *string `json:"currentCluster,omitempty"` ActiveCluster *string `json:"activeCluster,omitempty"` ActiveClusters []string `json:"activeClusters,required"` } // ToWire translates a EntityNotExistsError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *EntityNotExistsError) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ if v.CurrentCluster != nil { w, err = wire.NewValueString(*(v.CurrentCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.ActiveCluster != nil { w, err = wire.NewValueString(*(v.ActiveCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } w, err = wire.NewValueList(_List_String_ValueList(v.ActiveClusters)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a EntityNotExistsError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a EntityNotExistsError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v EntityNotExistsError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *EntityNotExistsError) FromWire(w wire.Value) error { var err error messageIsSet := false activeClustersIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } case 2: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CurrentCluster = &x if err != nil { return err } } case 3: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActiveCluster = &x if err != nil { return err } } case 4: if field.Value.Type() == wire.TList { v.ActiveClusters, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } activeClustersIsSet = true } } } if !messageIsSet { return errors.New("field Message of EntityNotExistsError is required") } if !activeClustersIsSet { return errors.New("field ActiveClusters of EntityNotExistsError is required") } return nil } // Encode serializes a EntityNotExistsError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a EntityNotExistsError struct could not be encoded. func (v *EntityNotExistsError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if v.CurrentCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CurrentCluster)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActiveCluster)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.ActiveClusters, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a EntityNotExistsError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a EntityNotExistsError struct could not be generated from the wire // representation. func (v *EntityNotExistsError) Decode(sr stream.Reader) error { messageIsSet := false activeClustersIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true case fh.ID == 2 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CurrentCluster = &x if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActiveCluster = &x if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TList: v.ActiveClusters, err = _List_String_Decode(sr) if err != nil { return err } activeClustersIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of EntityNotExistsError is required") } if !activeClustersIsSet { return errors.New("field ActiveClusters of EntityNotExistsError is required") } return nil } // String returns a readable string representation of a EntityNotExistsError // struct. func (v *EntityNotExistsError) String() string { if v == nil { return "" } var fields [4]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ if v.CurrentCluster != nil { fields[i] = fmt.Sprintf("CurrentCluster: %v", *(v.CurrentCluster)) i++ } if v.ActiveCluster != nil { fields[i] = fmt.Sprintf("ActiveCluster: %v", *(v.ActiveCluster)) i++ } fields[i] = fmt.Sprintf("ActiveClusters: %v", v.ActiveClusters) i++ return fmt.Sprintf("EntityNotExistsError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*EntityNotExistsError) ErrorName() string { return "EntityNotExistsError" } // Equals returns true if all the fields of this EntityNotExistsError match the // provided EntityNotExistsError. // // This function performs a deep comparison. func (v *EntityNotExistsError) Equals(rhs *EntityNotExistsError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } if !_String_EqualsPtr(v.CurrentCluster, rhs.CurrentCluster) { return false } if !_String_EqualsPtr(v.ActiveCluster, rhs.ActiveCluster) { return false } if !_List_String_Equals(v.ActiveClusters, rhs.ActiveClusters) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of EntityNotExistsError. func (v *EntityNotExistsError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) if v.CurrentCluster != nil { enc.AddString("currentCluster", *v.CurrentCluster) } if v.ActiveCluster != nil { enc.AddString("activeCluster", *v.ActiveCluster) } err = multierr.Append(err, enc.AddArray("activeClusters", (_List_String_Zapper)(v.ActiveClusters))) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *EntityNotExistsError) GetMessage() (o string) { if v != nil { o = v.Message } return } // GetCurrentCluster returns the value of CurrentCluster if it is set or its // zero value if it is unset. func (v *EntityNotExistsError) GetCurrentCluster() (o string) { if v != nil && v.CurrentCluster != nil { return *v.CurrentCluster } return } // IsSetCurrentCluster returns true if CurrentCluster is not nil. func (v *EntityNotExistsError) IsSetCurrentCluster() bool { return v != nil && v.CurrentCluster != nil } // GetActiveCluster returns the value of ActiveCluster if it is set or its // zero value if it is unset. func (v *EntityNotExistsError) GetActiveCluster() (o string) { if v != nil && v.ActiveCluster != nil { return *v.ActiveCluster } return } // IsSetActiveCluster returns true if ActiveCluster is not nil. func (v *EntityNotExistsError) IsSetActiveCluster() bool { return v != nil && v.ActiveCluster != nil } // GetActiveClusters returns the value of ActiveClusters if it is set or its // zero value if it is unset. func (v *EntityNotExistsError) GetActiveClusters() (o []string) { if v != nil { o = v.ActiveClusters } return } // IsSetActiveClusters returns true if ActiveClusters is not nil. func (v *EntityNotExistsError) IsSetActiveClusters() bool { return v != nil && v.ActiveClusters != nil } func (v *EntityNotExistsError) Error() string { return v.String() } type EventType int32 const ( EventTypeWorkflowExecutionStarted EventType = 0 EventTypeWorkflowExecutionCompleted EventType = 1 EventTypeWorkflowExecutionFailed EventType = 2 EventTypeWorkflowExecutionTimedOut EventType = 3 EventTypeDecisionTaskScheduled EventType = 4 EventTypeDecisionTaskStarted EventType = 5 EventTypeDecisionTaskCompleted EventType = 6 EventTypeDecisionTaskTimedOut EventType = 7 EventTypeDecisionTaskFailed EventType = 8 EventTypeActivityTaskScheduled EventType = 9 EventTypeActivityTaskStarted EventType = 10 EventTypeActivityTaskCompleted EventType = 11 EventTypeActivityTaskFailed EventType = 12 EventTypeActivityTaskTimedOut EventType = 13 EventTypeActivityTaskCancelRequested EventType = 14 EventTypeRequestCancelActivityTaskFailed EventType = 15 EventTypeActivityTaskCanceled EventType = 16 EventTypeTimerStarted EventType = 17 EventTypeTimerFired EventType = 18 EventTypeCancelTimerFailed EventType = 19 EventTypeTimerCanceled EventType = 20 EventTypeWorkflowExecutionCancelRequested EventType = 21 EventTypeWorkflowExecutionCanceled EventType = 22 EventTypeRequestCancelExternalWorkflowExecutionInitiated EventType = 23 EventTypeRequestCancelExternalWorkflowExecutionFailed EventType = 24 EventTypeExternalWorkflowExecutionCancelRequested EventType = 25 EventTypeMarkerRecorded EventType = 26 EventTypeWorkflowExecutionSignaled EventType = 27 EventTypeWorkflowExecutionTerminated EventType = 28 EventTypeWorkflowExecutionContinuedAsNew EventType = 29 EventTypeStartChildWorkflowExecutionInitiated EventType = 30 EventTypeStartChildWorkflowExecutionFailed EventType = 31 EventTypeChildWorkflowExecutionStarted EventType = 32 EventTypeChildWorkflowExecutionCompleted EventType = 33 EventTypeChildWorkflowExecutionFailed EventType = 34 EventTypeChildWorkflowExecutionCanceled EventType = 35 EventTypeChildWorkflowExecutionTimedOut EventType = 36 EventTypeChildWorkflowExecutionTerminated EventType = 37 EventTypeSignalExternalWorkflowExecutionInitiated EventType = 38 EventTypeSignalExternalWorkflowExecutionFailed EventType = 39 EventTypeExternalWorkflowExecutionSignaled EventType = 40 EventTypeUpsertWorkflowSearchAttributes EventType = 41 ) // EventType_Values returns all recognized values of EventType. func EventType_Values() []EventType { return []EventType{ EventTypeWorkflowExecutionStarted, EventTypeWorkflowExecutionCompleted, EventTypeWorkflowExecutionFailed, EventTypeWorkflowExecutionTimedOut, EventTypeDecisionTaskScheduled, EventTypeDecisionTaskStarted, EventTypeDecisionTaskCompleted, EventTypeDecisionTaskTimedOut, EventTypeDecisionTaskFailed, EventTypeActivityTaskScheduled, EventTypeActivityTaskStarted, EventTypeActivityTaskCompleted, EventTypeActivityTaskFailed, EventTypeActivityTaskTimedOut, EventTypeActivityTaskCancelRequested, EventTypeRequestCancelActivityTaskFailed, EventTypeActivityTaskCanceled, EventTypeTimerStarted, EventTypeTimerFired, EventTypeCancelTimerFailed, EventTypeTimerCanceled, EventTypeWorkflowExecutionCancelRequested, EventTypeWorkflowExecutionCanceled, EventTypeRequestCancelExternalWorkflowExecutionInitiated, EventTypeRequestCancelExternalWorkflowExecutionFailed, EventTypeExternalWorkflowExecutionCancelRequested, EventTypeMarkerRecorded, EventTypeWorkflowExecutionSignaled, EventTypeWorkflowExecutionTerminated, EventTypeWorkflowExecutionContinuedAsNew, EventTypeStartChildWorkflowExecutionInitiated, EventTypeStartChildWorkflowExecutionFailed, EventTypeChildWorkflowExecutionStarted, EventTypeChildWorkflowExecutionCompleted, EventTypeChildWorkflowExecutionFailed, EventTypeChildWorkflowExecutionCanceled, EventTypeChildWorkflowExecutionTimedOut, EventTypeChildWorkflowExecutionTerminated, EventTypeSignalExternalWorkflowExecutionInitiated, EventTypeSignalExternalWorkflowExecutionFailed, EventTypeExternalWorkflowExecutionSignaled, EventTypeUpsertWorkflowSearchAttributes, } } // UnmarshalText tries to decode EventType from a byte slice // containing its name. // // var v EventType // err := v.UnmarshalText([]byte("WorkflowExecutionStarted")) func (v *EventType) UnmarshalText(value []byte) error { switch s := string(value); s { case "WorkflowExecutionStarted": *v = EventTypeWorkflowExecutionStarted return nil case "WorkflowExecutionCompleted": *v = EventTypeWorkflowExecutionCompleted return nil case "WorkflowExecutionFailed": *v = EventTypeWorkflowExecutionFailed return nil case "WorkflowExecutionTimedOut": *v = EventTypeWorkflowExecutionTimedOut return nil case "DecisionTaskScheduled": *v = EventTypeDecisionTaskScheduled return nil case "DecisionTaskStarted": *v = EventTypeDecisionTaskStarted return nil case "DecisionTaskCompleted": *v = EventTypeDecisionTaskCompleted return nil case "DecisionTaskTimedOut": *v = EventTypeDecisionTaskTimedOut return nil case "DecisionTaskFailed": *v = EventTypeDecisionTaskFailed return nil case "ActivityTaskScheduled": *v = EventTypeActivityTaskScheduled return nil case "ActivityTaskStarted": *v = EventTypeActivityTaskStarted return nil case "ActivityTaskCompleted": *v = EventTypeActivityTaskCompleted return nil case "ActivityTaskFailed": *v = EventTypeActivityTaskFailed return nil case "ActivityTaskTimedOut": *v = EventTypeActivityTaskTimedOut return nil case "ActivityTaskCancelRequested": *v = EventTypeActivityTaskCancelRequested return nil case "RequestCancelActivityTaskFailed": *v = EventTypeRequestCancelActivityTaskFailed return nil case "ActivityTaskCanceled": *v = EventTypeActivityTaskCanceled return nil case "TimerStarted": *v = EventTypeTimerStarted return nil case "TimerFired": *v = EventTypeTimerFired return nil case "CancelTimerFailed": *v = EventTypeCancelTimerFailed return nil case "TimerCanceled": *v = EventTypeTimerCanceled return nil case "WorkflowExecutionCancelRequested": *v = EventTypeWorkflowExecutionCancelRequested return nil case "WorkflowExecutionCanceled": *v = EventTypeWorkflowExecutionCanceled return nil case "RequestCancelExternalWorkflowExecutionInitiated": *v = EventTypeRequestCancelExternalWorkflowExecutionInitiated return nil case "RequestCancelExternalWorkflowExecutionFailed": *v = EventTypeRequestCancelExternalWorkflowExecutionFailed return nil case "ExternalWorkflowExecutionCancelRequested": *v = EventTypeExternalWorkflowExecutionCancelRequested return nil case "MarkerRecorded": *v = EventTypeMarkerRecorded return nil case "WorkflowExecutionSignaled": *v = EventTypeWorkflowExecutionSignaled return nil case "WorkflowExecutionTerminated": *v = EventTypeWorkflowExecutionTerminated return nil case "WorkflowExecutionContinuedAsNew": *v = EventTypeWorkflowExecutionContinuedAsNew return nil case "StartChildWorkflowExecutionInitiated": *v = EventTypeStartChildWorkflowExecutionInitiated return nil case "StartChildWorkflowExecutionFailed": *v = EventTypeStartChildWorkflowExecutionFailed return nil case "ChildWorkflowExecutionStarted": *v = EventTypeChildWorkflowExecutionStarted return nil case "ChildWorkflowExecutionCompleted": *v = EventTypeChildWorkflowExecutionCompleted return nil case "ChildWorkflowExecutionFailed": *v = EventTypeChildWorkflowExecutionFailed return nil case "ChildWorkflowExecutionCanceled": *v = EventTypeChildWorkflowExecutionCanceled return nil case "ChildWorkflowExecutionTimedOut": *v = EventTypeChildWorkflowExecutionTimedOut return nil case "ChildWorkflowExecutionTerminated": *v = EventTypeChildWorkflowExecutionTerminated return nil case "SignalExternalWorkflowExecutionInitiated": *v = EventTypeSignalExternalWorkflowExecutionInitiated return nil case "SignalExternalWorkflowExecutionFailed": *v = EventTypeSignalExternalWorkflowExecutionFailed return nil case "ExternalWorkflowExecutionSignaled": *v = EventTypeExternalWorkflowExecutionSignaled return nil case "UpsertWorkflowSearchAttributes": *v = EventTypeUpsertWorkflowSearchAttributes return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "EventType", err) } *v = EventType(val) return nil } } // MarshalText encodes EventType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v EventType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("WorkflowExecutionStarted"), nil case 1: return []byte("WorkflowExecutionCompleted"), nil case 2: return []byte("WorkflowExecutionFailed"), nil case 3: return []byte("WorkflowExecutionTimedOut"), nil case 4: return []byte("DecisionTaskScheduled"), nil case 5: return []byte("DecisionTaskStarted"), nil case 6: return []byte("DecisionTaskCompleted"), nil case 7: return []byte("DecisionTaskTimedOut"), nil case 8: return []byte("DecisionTaskFailed"), nil case 9: return []byte("ActivityTaskScheduled"), nil case 10: return []byte("ActivityTaskStarted"), nil case 11: return []byte("ActivityTaskCompleted"), nil case 12: return []byte("ActivityTaskFailed"), nil case 13: return []byte("ActivityTaskTimedOut"), nil case 14: return []byte("ActivityTaskCancelRequested"), nil case 15: return []byte("RequestCancelActivityTaskFailed"), nil case 16: return []byte("ActivityTaskCanceled"), nil case 17: return []byte("TimerStarted"), nil case 18: return []byte("TimerFired"), nil case 19: return []byte("CancelTimerFailed"), nil case 20: return []byte("TimerCanceled"), nil case 21: return []byte("WorkflowExecutionCancelRequested"), nil case 22: return []byte("WorkflowExecutionCanceled"), nil case 23: return []byte("RequestCancelExternalWorkflowExecutionInitiated"), nil case 24: return []byte("RequestCancelExternalWorkflowExecutionFailed"), nil case 25: return []byte("ExternalWorkflowExecutionCancelRequested"), nil case 26: return []byte("MarkerRecorded"), nil case 27: return []byte("WorkflowExecutionSignaled"), nil case 28: return []byte("WorkflowExecutionTerminated"), nil case 29: return []byte("WorkflowExecutionContinuedAsNew"), nil case 30: return []byte("StartChildWorkflowExecutionInitiated"), nil case 31: return []byte("StartChildWorkflowExecutionFailed"), nil case 32: return []byte("ChildWorkflowExecutionStarted"), nil case 33: return []byte("ChildWorkflowExecutionCompleted"), nil case 34: return []byte("ChildWorkflowExecutionFailed"), nil case 35: return []byte("ChildWorkflowExecutionCanceled"), nil case 36: return []byte("ChildWorkflowExecutionTimedOut"), nil case 37: return []byte("ChildWorkflowExecutionTerminated"), nil case 38: return []byte("SignalExternalWorkflowExecutionInitiated"), nil case 39: return []byte("SignalExternalWorkflowExecutionFailed"), nil case 40: return []byte("ExternalWorkflowExecutionSignaled"), nil case 41: return []byte("UpsertWorkflowSearchAttributes"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of EventType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v EventType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "WorkflowExecutionStarted") case 1: enc.AddString("name", "WorkflowExecutionCompleted") case 2: enc.AddString("name", "WorkflowExecutionFailed") case 3: enc.AddString("name", "WorkflowExecutionTimedOut") case 4: enc.AddString("name", "DecisionTaskScheduled") case 5: enc.AddString("name", "DecisionTaskStarted") case 6: enc.AddString("name", "DecisionTaskCompleted") case 7: enc.AddString("name", "DecisionTaskTimedOut") case 8: enc.AddString("name", "DecisionTaskFailed") case 9: enc.AddString("name", "ActivityTaskScheduled") case 10: enc.AddString("name", "ActivityTaskStarted") case 11: enc.AddString("name", "ActivityTaskCompleted") case 12: enc.AddString("name", "ActivityTaskFailed") case 13: enc.AddString("name", "ActivityTaskTimedOut") case 14: enc.AddString("name", "ActivityTaskCancelRequested") case 15: enc.AddString("name", "RequestCancelActivityTaskFailed") case 16: enc.AddString("name", "ActivityTaskCanceled") case 17: enc.AddString("name", "TimerStarted") case 18: enc.AddString("name", "TimerFired") case 19: enc.AddString("name", "CancelTimerFailed") case 20: enc.AddString("name", "TimerCanceled") case 21: enc.AddString("name", "WorkflowExecutionCancelRequested") case 22: enc.AddString("name", "WorkflowExecutionCanceled") case 23: enc.AddString("name", "RequestCancelExternalWorkflowExecutionInitiated") case 24: enc.AddString("name", "RequestCancelExternalWorkflowExecutionFailed") case 25: enc.AddString("name", "ExternalWorkflowExecutionCancelRequested") case 26: enc.AddString("name", "MarkerRecorded") case 27: enc.AddString("name", "WorkflowExecutionSignaled") case 28: enc.AddString("name", "WorkflowExecutionTerminated") case 29: enc.AddString("name", "WorkflowExecutionContinuedAsNew") case 30: enc.AddString("name", "StartChildWorkflowExecutionInitiated") case 31: enc.AddString("name", "StartChildWorkflowExecutionFailed") case 32: enc.AddString("name", "ChildWorkflowExecutionStarted") case 33: enc.AddString("name", "ChildWorkflowExecutionCompleted") case 34: enc.AddString("name", "ChildWorkflowExecutionFailed") case 35: enc.AddString("name", "ChildWorkflowExecutionCanceled") case 36: enc.AddString("name", "ChildWorkflowExecutionTimedOut") case 37: enc.AddString("name", "ChildWorkflowExecutionTerminated") case 38: enc.AddString("name", "SignalExternalWorkflowExecutionInitiated") case 39: enc.AddString("name", "SignalExternalWorkflowExecutionFailed") case 40: enc.AddString("name", "ExternalWorkflowExecutionSignaled") case 41: enc.AddString("name", "UpsertWorkflowSearchAttributes") } return nil } // Ptr returns a pointer to this enum value. func (v EventType) Ptr() *EventType { return &v } // Encode encodes EventType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v EventType // return v.Encode(sWriter) func (v EventType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates EventType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v EventType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes EventType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return EventType(0), err // } // // var v EventType // if err := v.FromWire(x); err != nil { // return EventType(0), err // } // return v, nil func (v *EventType) FromWire(w wire.Value) error { *v = (EventType)(w.GetI32()) return nil } // Decode reads off the encoded EventType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v EventType // if err := v.Decode(sReader); err != nil { // return EventType(0), err // } // return v, nil func (v *EventType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (EventType)(i) return nil } // String returns a readable string representation of EventType. func (v EventType) String() string { w := int32(v) switch w { case 0: return "WorkflowExecutionStarted" case 1: return "WorkflowExecutionCompleted" case 2: return "WorkflowExecutionFailed" case 3: return "WorkflowExecutionTimedOut" case 4: return "DecisionTaskScheduled" case 5: return "DecisionTaskStarted" case 6: return "DecisionTaskCompleted" case 7: return "DecisionTaskTimedOut" case 8: return "DecisionTaskFailed" case 9: return "ActivityTaskScheduled" case 10: return "ActivityTaskStarted" case 11: return "ActivityTaskCompleted" case 12: return "ActivityTaskFailed" case 13: return "ActivityTaskTimedOut" case 14: return "ActivityTaskCancelRequested" case 15: return "RequestCancelActivityTaskFailed" case 16: return "ActivityTaskCanceled" case 17: return "TimerStarted" case 18: return "TimerFired" case 19: return "CancelTimerFailed" case 20: return "TimerCanceled" case 21: return "WorkflowExecutionCancelRequested" case 22: return "WorkflowExecutionCanceled" case 23: return "RequestCancelExternalWorkflowExecutionInitiated" case 24: return "RequestCancelExternalWorkflowExecutionFailed" case 25: return "ExternalWorkflowExecutionCancelRequested" case 26: return "MarkerRecorded" case 27: return "WorkflowExecutionSignaled" case 28: return "WorkflowExecutionTerminated" case 29: return "WorkflowExecutionContinuedAsNew" case 30: return "StartChildWorkflowExecutionInitiated" case 31: return "StartChildWorkflowExecutionFailed" case 32: return "ChildWorkflowExecutionStarted" case 33: return "ChildWorkflowExecutionCompleted" case 34: return "ChildWorkflowExecutionFailed" case 35: return "ChildWorkflowExecutionCanceled" case 36: return "ChildWorkflowExecutionTimedOut" case 37: return "ChildWorkflowExecutionTerminated" case 38: return "SignalExternalWorkflowExecutionInitiated" case 39: return "SignalExternalWorkflowExecutionFailed" case 40: return "ExternalWorkflowExecutionSignaled" case 41: return "UpsertWorkflowSearchAttributes" } return fmt.Sprintf("EventType(%d)", w) } // Equals returns true if this EventType value matches the provided // value. func (v EventType) Equals(rhs EventType) bool { return v == rhs } // MarshalJSON serializes EventType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v EventType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"WorkflowExecutionStarted\""), nil case 1: return ([]byte)("\"WorkflowExecutionCompleted\""), nil case 2: return ([]byte)("\"WorkflowExecutionFailed\""), nil case 3: return ([]byte)("\"WorkflowExecutionTimedOut\""), nil case 4: return ([]byte)("\"DecisionTaskScheduled\""), nil case 5: return ([]byte)("\"DecisionTaskStarted\""), nil case 6: return ([]byte)("\"DecisionTaskCompleted\""), nil case 7: return ([]byte)("\"DecisionTaskTimedOut\""), nil case 8: return ([]byte)("\"DecisionTaskFailed\""), nil case 9: return ([]byte)("\"ActivityTaskScheduled\""), nil case 10: return ([]byte)("\"ActivityTaskStarted\""), nil case 11: return ([]byte)("\"ActivityTaskCompleted\""), nil case 12: return ([]byte)("\"ActivityTaskFailed\""), nil case 13: return ([]byte)("\"ActivityTaskTimedOut\""), nil case 14: return ([]byte)("\"ActivityTaskCancelRequested\""), nil case 15: return ([]byte)("\"RequestCancelActivityTaskFailed\""), nil case 16: return ([]byte)("\"ActivityTaskCanceled\""), nil case 17: return ([]byte)("\"TimerStarted\""), nil case 18: return ([]byte)("\"TimerFired\""), nil case 19: return ([]byte)("\"CancelTimerFailed\""), nil case 20: return ([]byte)("\"TimerCanceled\""), nil case 21: return ([]byte)("\"WorkflowExecutionCancelRequested\""), nil case 22: return ([]byte)("\"WorkflowExecutionCanceled\""), nil case 23: return ([]byte)("\"RequestCancelExternalWorkflowExecutionInitiated\""), nil case 24: return ([]byte)("\"RequestCancelExternalWorkflowExecutionFailed\""), nil case 25: return ([]byte)("\"ExternalWorkflowExecutionCancelRequested\""), nil case 26: return ([]byte)("\"MarkerRecorded\""), nil case 27: return ([]byte)("\"WorkflowExecutionSignaled\""), nil case 28: return ([]byte)("\"WorkflowExecutionTerminated\""), nil case 29: return ([]byte)("\"WorkflowExecutionContinuedAsNew\""), nil case 30: return ([]byte)("\"StartChildWorkflowExecutionInitiated\""), nil case 31: return ([]byte)("\"StartChildWorkflowExecutionFailed\""), nil case 32: return ([]byte)("\"ChildWorkflowExecutionStarted\""), nil case 33: return ([]byte)("\"ChildWorkflowExecutionCompleted\""), nil case 34: return ([]byte)("\"ChildWorkflowExecutionFailed\""), nil case 35: return ([]byte)("\"ChildWorkflowExecutionCanceled\""), nil case 36: return ([]byte)("\"ChildWorkflowExecutionTimedOut\""), nil case 37: return ([]byte)("\"ChildWorkflowExecutionTerminated\""), nil case 38: return ([]byte)("\"SignalExternalWorkflowExecutionInitiated\""), nil case 39: return ([]byte)("\"SignalExternalWorkflowExecutionFailed\""), nil case 40: return ([]byte)("\"ExternalWorkflowExecutionSignaled\""), nil case 41: return ([]byte)("\"UpsertWorkflowSearchAttributes\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode EventType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *EventType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "EventType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "EventType") } *v = (EventType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "EventType") } } type ExternalWorkflowExecutionCancelRequestedEventAttributes struct { InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` } // ToWire translates a ExternalWorkflowExecutionCancelRequestedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ExternalWorkflowExecutionCancelRequestedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ExternalWorkflowExecutionCancelRequestedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ExternalWorkflowExecutionCancelRequestedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ExternalWorkflowExecutionCancelRequestedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ExternalWorkflowExecutionCancelRequestedEventAttributes struct could not be encoded. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ExternalWorkflowExecutionCancelRequestedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ExternalWorkflowExecutionCancelRequestedEventAttributes struct could not be generated from the wire // representation. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ExternalWorkflowExecutionCancelRequestedEventAttributes // struct. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } return fmt.Sprintf("ExternalWorkflowExecutionCancelRequestedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ExternalWorkflowExecutionCancelRequestedEventAttributes match the // provided ExternalWorkflowExecutionCancelRequestedEventAttributes. // // This function performs a deep comparison. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) Equals(rhs *ExternalWorkflowExecutionCancelRequestedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ExternalWorkflowExecutionCancelRequestedEventAttributes. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } return err } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } type ExternalWorkflowExecutionSignaledEventAttributes struct { InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Control []byte `json:"control,omitempty"` } // ToWire translates a ExternalWorkflowExecutionSignaledEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ExternalWorkflowExecutionSignaledEventAttributes) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ExternalWorkflowExecutionSignaledEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ExternalWorkflowExecutionSignaledEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ExternalWorkflowExecutionSignaledEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ExternalWorkflowExecutionSignaledEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a ExternalWorkflowExecutionSignaledEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ExternalWorkflowExecutionSignaledEventAttributes struct could not be encoded. func (v *ExternalWorkflowExecutionSignaledEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ExternalWorkflowExecutionSignaledEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ExternalWorkflowExecutionSignaledEventAttributes struct could not be generated from the wire // representation. func (v *ExternalWorkflowExecutionSignaledEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ExternalWorkflowExecutionSignaledEventAttributes // struct. func (v *ExternalWorkflowExecutionSignaledEventAttributes) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } return fmt.Sprintf("ExternalWorkflowExecutionSignaledEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ExternalWorkflowExecutionSignaledEventAttributes match the // provided ExternalWorkflowExecutionSignaledEventAttributes. // // This function performs a deep comparison. func (v *ExternalWorkflowExecutionSignaledEventAttributes) Equals(rhs *ExternalWorkflowExecutionSignaledEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ExternalWorkflowExecutionSignaledEventAttributes. func (v *ExternalWorkflowExecutionSignaledEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } return err } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *ExternalWorkflowExecutionSignaledEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *ExternalWorkflowExecutionSignaledEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ExternalWorkflowExecutionSignaledEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ExternalWorkflowExecutionSignaledEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ExternalWorkflowExecutionSignaledEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ExternalWorkflowExecutionSignaledEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *ExternalWorkflowExecutionSignaledEventAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *ExternalWorkflowExecutionSignaledEventAttributes) IsSetControl() bool { return v != nil && v.Control != nil } type FailWorkflowExecutionDecisionAttributes struct { Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` } // ToWire translates a FailWorkflowExecutionDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *FailWorkflowExecutionDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a FailWorkflowExecutionDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a FailWorkflowExecutionDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v FailWorkflowExecutionDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *FailWorkflowExecutionDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a FailWorkflowExecutionDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a FailWorkflowExecutionDecisionAttributes struct could not be encoded. func (v *FailWorkflowExecutionDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a FailWorkflowExecutionDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a FailWorkflowExecutionDecisionAttributes struct could not be generated from the wire // representation. func (v *FailWorkflowExecutionDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a FailWorkflowExecutionDecisionAttributes // struct. func (v *FailWorkflowExecutionDecisionAttributes) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } return fmt.Sprintf("FailWorkflowExecutionDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this FailWorkflowExecutionDecisionAttributes match the // provided FailWorkflowExecutionDecisionAttributes. // // This function performs a deep comparison. func (v *FailWorkflowExecutionDecisionAttributes) Equals(rhs *FailWorkflowExecutionDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FailWorkflowExecutionDecisionAttributes. func (v *FailWorkflowExecutionDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } return err } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *FailWorkflowExecutionDecisionAttributes) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *FailWorkflowExecutionDecisionAttributes) IsSetReason() bool { return v != nil && v.Reason != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *FailWorkflowExecutionDecisionAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *FailWorkflowExecutionDecisionAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } type FailoverDomainRequest struct { DomainName *string `json:"domainName,omitempty"` DomainActiveClusterName *string `json:"domainActiveClusterName,omitempty"` ActiveClusters *ActiveClusters `json:"activeClusters,omitempty"` Reason *string `json:"reason,omitempty"` } // ToWire translates a FailoverDomainRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *FailoverDomainRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.DomainName != nil { w, err = wire.NewValueString(*(v.DomainName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.DomainActiveClusterName != nil { w, err = wire.NewValueString(*(v.DomainActiveClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ActiveClusters != nil { w, err = v.ActiveClusters.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a FailoverDomainRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a FailoverDomainRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v FailoverDomainRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *FailoverDomainRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainName = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainActiveClusterName = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ActiveClusters, err = _ActiveClusters_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } } } return nil } // Encode serializes a FailoverDomainRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a FailoverDomainRequest struct could not be encoded. func (v *FailoverDomainRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainActiveClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainActiveClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusters != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusters.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a FailoverDomainRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a FailoverDomainRequest struct could not be generated from the wire // representation. func (v *FailoverDomainRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainName = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainActiveClusterName = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ActiveClusters, err = _ActiveClusters_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a FailoverDomainRequest // struct. func (v *FailoverDomainRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.DomainName != nil { fields[i] = fmt.Sprintf("DomainName: %v", *(v.DomainName)) i++ } if v.DomainActiveClusterName != nil { fields[i] = fmt.Sprintf("DomainActiveClusterName: %v", *(v.DomainActiveClusterName)) i++ } if v.ActiveClusters != nil { fields[i] = fmt.Sprintf("ActiveClusters: %v", v.ActiveClusters) i++ } if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } return fmt.Sprintf("FailoverDomainRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this FailoverDomainRequest match the // provided FailoverDomainRequest. // // This function performs a deep comparison. func (v *FailoverDomainRequest) Equals(rhs *FailoverDomainRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainName, rhs.DomainName) { return false } if !_String_EqualsPtr(v.DomainActiveClusterName, rhs.DomainActiveClusterName) { return false } if !((v.ActiveClusters == nil && rhs.ActiveClusters == nil) || (v.ActiveClusters != nil && rhs.ActiveClusters != nil && v.ActiveClusters.Equals(rhs.ActiveClusters))) { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FailoverDomainRequest. func (v *FailoverDomainRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainName != nil { enc.AddString("domainName", *v.DomainName) } if v.DomainActiveClusterName != nil { enc.AddString("domainActiveClusterName", *v.DomainActiveClusterName) } if v.ActiveClusters != nil { err = multierr.Append(err, enc.AddObject("activeClusters", v.ActiveClusters)) } if v.Reason != nil { enc.AddString("reason", *v.Reason) } return err } // GetDomainName returns the value of DomainName if it is set or its // zero value if it is unset. func (v *FailoverDomainRequest) GetDomainName() (o string) { if v != nil && v.DomainName != nil { return *v.DomainName } return } // IsSetDomainName returns true if DomainName is not nil. func (v *FailoverDomainRequest) IsSetDomainName() bool { return v != nil && v.DomainName != nil } // GetDomainActiveClusterName returns the value of DomainActiveClusterName if it is set or its // zero value if it is unset. func (v *FailoverDomainRequest) GetDomainActiveClusterName() (o string) { if v != nil && v.DomainActiveClusterName != nil { return *v.DomainActiveClusterName } return } // IsSetDomainActiveClusterName returns true if DomainActiveClusterName is not nil. func (v *FailoverDomainRequest) IsSetDomainActiveClusterName() bool { return v != nil && v.DomainActiveClusterName != nil } // GetActiveClusters returns the value of ActiveClusters if it is set or its // zero value if it is unset. func (v *FailoverDomainRequest) GetActiveClusters() (o *ActiveClusters) { if v != nil && v.ActiveClusters != nil { return v.ActiveClusters } return } // IsSetActiveClusters returns true if ActiveClusters is not nil. func (v *FailoverDomainRequest) IsSetActiveClusters() bool { return v != nil && v.ActiveClusters != nil } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *FailoverDomainRequest) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *FailoverDomainRequest) IsSetReason() bool { return v != nil && v.Reason != nil } type FailoverDomainResponse struct { DomainInfo *DomainInfo `json:"domainInfo,omitempty"` Configuration *DomainConfiguration `json:"configuration,omitempty"` ReplicationConfiguration *DomainReplicationConfiguration `json:"replicationConfiguration,omitempty"` FailoverVersion *int64 `json:"failoverVersion,omitempty"` IsGlobalDomain *bool `json:"isGlobalDomain,omitempty"` } // ToWire translates a FailoverDomainResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *FailoverDomainResponse) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.DomainInfo != nil { w, err = v.DomainInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Configuration != nil { w, err = v.Configuration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ReplicationConfiguration != nil { w, err = v.ReplicationConfiguration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.FailoverVersion != nil { w, err = wire.NewValueI64(*(v.FailoverVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.IsGlobalDomain != nil { w, err = wire.NewValueBool(*(v.IsGlobalDomain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a FailoverDomainResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a FailoverDomainResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v FailoverDomainResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *FailoverDomainResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.DomainInfo, err = _DomainInfo_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Configuration, err = _DomainConfiguration_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ReplicationConfiguration, err = _DomainReplicationConfiguration_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverVersion = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.IsGlobalDomain = &x if err != nil { return err } } } } return nil } // Encode serializes a FailoverDomainResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a FailoverDomainResponse struct could not be encoded. func (v *FailoverDomainResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.DomainInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Configuration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Configuration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReplicationConfiguration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ReplicationConfiguration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsGlobalDomain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.IsGlobalDomain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a FailoverDomainResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a FailoverDomainResponse struct could not be generated from the wire // representation. func (v *FailoverDomainResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.DomainInfo, err = _DomainInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Configuration, err = _DomainConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ReplicationConfiguration, err = _DomainReplicationConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverVersion = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.IsGlobalDomain = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a FailoverDomainResponse // struct. func (v *FailoverDomainResponse) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.DomainInfo != nil { fields[i] = fmt.Sprintf("DomainInfo: %v", v.DomainInfo) i++ } if v.Configuration != nil { fields[i] = fmt.Sprintf("Configuration: %v", v.Configuration) i++ } if v.ReplicationConfiguration != nil { fields[i] = fmt.Sprintf("ReplicationConfiguration: %v", v.ReplicationConfiguration) i++ } if v.FailoverVersion != nil { fields[i] = fmt.Sprintf("FailoverVersion: %v", *(v.FailoverVersion)) i++ } if v.IsGlobalDomain != nil { fields[i] = fmt.Sprintf("IsGlobalDomain: %v", *(v.IsGlobalDomain)) i++ } return fmt.Sprintf("FailoverDomainResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this FailoverDomainResponse match the // provided FailoverDomainResponse. // // This function performs a deep comparison. func (v *FailoverDomainResponse) Equals(rhs *FailoverDomainResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DomainInfo == nil && rhs.DomainInfo == nil) || (v.DomainInfo != nil && rhs.DomainInfo != nil && v.DomainInfo.Equals(rhs.DomainInfo))) { return false } if !((v.Configuration == nil && rhs.Configuration == nil) || (v.Configuration != nil && rhs.Configuration != nil && v.Configuration.Equals(rhs.Configuration))) { return false } if !((v.ReplicationConfiguration == nil && rhs.ReplicationConfiguration == nil) || (v.ReplicationConfiguration != nil && rhs.ReplicationConfiguration != nil && v.ReplicationConfiguration.Equals(rhs.ReplicationConfiguration))) { return false } if !_I64_EqualsPtr(v.FailoverVersion, rhs.FailoverVersion) { return false } if !_Bool_EqualsPtr(v.IsGlobalDomain, rhs.IsGlobalDomain) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FailoverDomainResponse. func (v *FailoverDomainResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainInfo != nil { err = multierr.Append(err, enc.AddObject("domainInfo", v.DomainInfo)) } if v.Configuration != nil { err = multierr.Append(err, enc.AddObject("configuration", v.Configuration)) } if v.ReplicationConfiguration != nil { err = multierr.Append(err, enc.AddObject("replicationConfiguration", v.ReplicationConfiguration)) } if v.FailoverVersion != nil { enc.AddInt64("failoverVersion", *v.FailoverVersion) } if v.IsGlobalDomain != nil { enc.AddBool("isGlobalDomain", *v.IsGlobalDomain) } return err } // GetDomainInfo returns the value of DomainInfo if it is set or its // zero value if it is unset. func (v *FailoverDomainResponse) GetDomainInfo() (o *DomainInfo) { if v != nil && v.DomainInfo != nil { return v.DomainInfo } return } // IsSetDomainInfo returns true if DomainInfo is not nil. func (v *FailoverDomainResponse) IsSetDomainInfo() bool { return v != nil && v.DomainInfo != nil } // GetConfiguration returns the value of Configuration if it is set or its // zero value if it is unset. func (v *FailoverDomainResponse) GetConfiguration() (o *DomainConfiguration) { if v != nil && v.Configuration != nil { return v.Configuration } return } // IsSetConfiguration returns true if Configuration is not nil. func (v *FailoverDomainResponse) IsSetConfiguration() bool { return v != nil && v.Configuration != nil } // GetReplicationConfiguration returns the value of ReplicationConfiguration if it is set or its // zero value if it is unset. func (v *FailoverDomainResponse) GetReplicationConfiguration() (o *DomainReplicationConfiguration) { if v != nil && v.ReplicationConfiguration != nil { return v.ReplicationConfiguration } return } // IsSetReplicationConfiguration returns true if ReplicationConfiguration is not nil. func (v *FailoverDomainResponse) IsSetReplicationConfiguration() bool { return v != nil && v.ReplicationConfiguration != nil } // GetFailoverVersion returns the value of FailoverVersion if it is set or its // zero value if it is unset. func (v *FailoverDomainResponse) GetFailoverVersion() (o int64) { if v != nil && v.FailoverVersion != nil { return *v.FailoverVersion } return } // IsSetFailoverVersion returns true if FailoverVersion is not nil. func (v *FailoverDomainResponse) IsSetFailoverVersion() bool { return v != nil && v.FailoverVersion != nil } // GetIsGlobalDomain returns the value of IsGlobalDomain if it is set or its // zero value if it is unset. func (v *FailoverDomainResponse) GetIsGlobalDomain() (o bool) { if v != nil && v.IsGlobalDomain != nil { return *v.IsGlobalDomain } return } // IsSetIsGlobalDomain returns true if IsGlobalDomain is not nil. func (v *FailoverDomainResponse) IsSetIsGlobalDomain() bool { return v != nil && v.IsGlobalDomain != nil } type FailoverEvent struct { ID *string `json:"id,omitempty"` CreatedTime *int64 `json:"createdTime,omitempty"` FailoverType *FailoverType `json:"failoverType,omitempty"` ClusterFailovers []*ClusterFailover `json:"clusterFailovers,omitempty"` } type _List_ClusterFailover_ValueList []*ClusterFailover func (v _List_ClusterFailover_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*ClusterFailover', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_ClusterFailover_ValueList) Size() int { return len(v) } func (_List_ClusterFailover_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_ClusterFailover_ValueList) Close() {} // ToWire translates a FailoverEvent struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *FailoverEvent) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.ID != nil { w, err = wire.NewValueString(*(v.ID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.CreatedTime != nil { w, err = wire.NewValueI64(*(v.CreatedTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.FailoverType != nil { w, err = v.FailoverType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ClusterFailovers != nil { w, err = wire.NewValueList(_List_ClusterFailover_ValueList(v.ClusterFailovers)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _FailoverType_Read(w wire.Value) (FailoverType, error) { var v FailoverType err := v.FromWire(w) return v, err } func _ClusterFailover_Read(w wire.Value) (*ClusterFailover, error) { var v ClusterFailover err := v.FromWire(w) return &v, err } func _List_ClusterFailover_Read(l wire.ValueList) ([]*ClusterFailover, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*ClusterFailover, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _ClusterFailover_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a FailoverEvent struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a FailoverEvent struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v FailoverEvent // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *FailoverEvent) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CreatedTime = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x FailoverType x, err = _FailoverType_Read(field.Value) v.FailoverType = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TList { v.ClusterFailovers, err = _List_ClusterFailover_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_ClusterFailover_Encode(val []*ClusterFailover, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*ClusterFailover', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a FailoverEvent struct directly into bytes, without going // through an intermediary type. // // An error is returned if a FailoverEvent struct could not be encoded. func (v *FailoverEvent) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CreatedTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CreatedTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := v.FailoverType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClusterFailovers != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TList}); err != nil { return err } if err := _List_ClusterFailover_Encode(v.ClusterFailovers, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _FailoverType_Decode(sr stream.Reader) (FailoverType, error) { var v FailoverType err := v.Decode(sr) return v, err } func _ClusterFailover_Decode(sr stream.Reader) (*ClusterFailover, error) { var v ClusterFailover err := v.Decode(sr) return &v, err } func _List_ClusterFailover_Decode(sr stream.Reader) ([]*ClusterFailover, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*ClusterFailover, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _ClusterFailover_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a FailoverEvent struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a FailoverEvent struct could not be generated from the wire // representation. func (v *FailoverEvent) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CreatedTime = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x FailoverType x, err = _FailoverType_Decode(sr) v.FailoverType = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TList: v.ClusterFailovers, err = _List_ClusterFailover_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a FailoverEvent // struct. func (v *FailoverEvent) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.ID != nil { fields[i] = fmt.Sprintf("ID: %v", *(v.ID)) i++ } if v.CreatedTime != nil { fields[i] = fmt.Sprintf("CreatedTime: %v", *(v.CreatedTime)) i++ } if v.FailoverType != nil { fields[i] = fmt.Sprintf("FailoverType: %v", *(v.FailoverType)) i++ } if v.ClusterFailovers != nil { fields[i] = fmt.Sprintf("ClusterFailovers: %v", v.ClusterFailovers) i++ } return fmt.Sprintf("FailoverEvent{%v}", strings.Join(fields[:i], ", ")) } func _FailoverType_EqualsPtr(lhs, rhs *FailoverType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } func _List_ClusterFailover_Equals(lhs, rhs []*ClusterFailover) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this FailoverEvent match the // provided FailoverEvent. // // This function performs a deep comparison. func (v *FailoverEvent) Equals(rhs *FailoverEvent) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ID, rhs.ID) { return false } if !_I64_EqualsPtr(v.CreatedTime, rhs.CreatedTime) { return false } if !_FailoverType_EqualsPtr(v.FailoverType, rhs.FailoverType) { return false } if !((v.ClusterFailovers == nil && rhs.ClusterFailovers == nil) || (v.ClusterFailovers != nil && rhs.ClusterFailovers != nil && _List_ClusterFailover_Equals(v.ClusterFailovers, rhs.ClusterFailovers))) { return false } return true } type _List_ClusterFailover_Zapper []*ClusterFailover // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_ClusterFailover_Zapper. func (l _List_ClusterFailover_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FailoverEvent. func (v *FailoverEvent) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ID != nil { enc.AddString("id", *v.ID) } if v.CreatedTime != nil { enc.AddInt64("createdTime", *v.CreatedTime) } if v.FailoverType != nil { err = multierr.Append(err, enc.AddObject("failoverType", *v.FailoverType)) } if v.ClusterFailovers != nil { err = multierr.Append(err, enc.AddArray("clusterFailovers", (_List_ClusterFailover_Zapper)(v.ClusterFailovers))) } return err } // GetID returns the value of ID if it is set or its // zero value if it is unset. func (v *FailoverEvent) GetID() (o string) { if v != nil && v.ID != nil { return *v.ID } return } // IsSetID returns true if ID is not nil. func (v *FailoverEvent) IsSetID() bool { return v != nil && v.ID != nil } // GetCreatedTime returns the value of CreatedTime if it is set or its // zero value if it is unset. func (v *FailoverEvent) GetCreatedTime() (o int64) { if v != nil && v.CreatedTime != nil { return *v.CreatedTime } return } // IsSetCreatedTime returns true if CreatedTime is not nil. func (v *FailoverEvent) IsSetCreatedTime() bool { return v != nil && v.CreatedTime != nil } // GetFailoverType returns the value of FailoverType if it is set or its // zero value if it is unset. func (v *FailoverEvent) GetFailoverType() (o FailoverType) { if v != nil && v.FailoverType != nil { return *v.FailoverType } return } // IsSetFailoverType returns true if FailoverType is not nil. func (v *FailoverEvent) IsSetFailoverType() bool { return v != nil && v.FailoverType != nil } // GetClusterFailovers returns the value of ClusterFailovers if it is set or its // zero value if it is unset. func (v *FailoverEvent) GetClusterFailovers() (o []*ClusterFailover) { if v != nil && v.ClusterFailovers != nil { return v.ClusterFailovers } return } // IsSetClusterFailovers returns true if ClusterFailovers is not nil. func (v *FailoverEvent) IsSetClusterFailovers() bool { return v != nil && v.ClusterFailovers != nil } type FailoverInfo struct { FailoverVersion *int64 `json:"failoverVersion,omitempty"` FailoverStartTimestamp *int64 `json:"failoverStartTimestamp,omitempty"` FailoverExpireTimestamp *int64 `json:"failoverExpireTimestamp,omitempty"` CompletedShardCount *int32 `json:"completedShardCount,omitempty"` PendingShards []int32 `json:"pendingShards,omitempty"` } // ToWire translates a FailoverInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *FailoverInfo) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.FailoverVersion != nil { w, err = wire.NewValueI64(*(v.FailoverVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.FailoverStartTimestamp != nil { w, err = wire.NewValueI64(*(v.FailoverStartTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.FailoverExpireTimestamp != nil { w, err = wire.NewValueI64(*(v.FailoverExpireTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.CompletedShardCount != nil { w, err = wire.NewValueI32(*(v.CompletedShardCount)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.PendingShards != nil { w, err = wire.NewValueList(_List_I32_ValueList(v.PendingShards)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a FailoverInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a FailoverInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v FailoverInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *FailoverInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverVersion = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverStartTimestamp = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverExpireTimestamp = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.CompletedShardCount = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TList { v.PendingShards, err = _List_I32_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } // Encode serializes a FailoverInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a FailoverInfo struct could not be encoded. func (v *FailoverInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FailoverVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverStartTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverStartTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverExpireTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverExpireTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompletedShardCount != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.CompletedShardCount)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingShards != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TList}); err != nil { return err } if err := _List_I32_Encode(v.PendingShards, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a FailoverInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a FailoverInfo struct could not be generated from the wire // representation. func (v *FailoverInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverVersion = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverStartTimestamp = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverExpireTimestamp = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.CompletedShardCount = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TList: v.PendingShards, err = _List_I32_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a FailoverInfo // struct. func (v *FailoverInfo) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.FailoverVersion != nil { fields[i] = fmt.Sprintf("FailoverVersion: %v", *(v.FailoverVersion)) i++ } if v.FailoverStartTimestamp != nil { fields[i] = fmt.Sprintf("FailoverStartTimestamp: %v", *(v.FailoverStartTimestamp)) i++ } if v.FailoverExpireTimestamp != nil { fields[i] = fmt.Sprintf("FailoverExpireTimestamp: %v", *(v.FailoverExpireTimestamp)) i++ } if v.CompletedShardCount != nil { fields[i] = fmt.Sprintf("CompletedShardCount: %v", *(v.CompletedShardCount)) i++ } if v.PendingShards != nil { fields[i] = fmt.Sprintf("PendingShards: %v", v.PendingShards) i++ } return fmt.Sprintf("FailoverInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this FailoverInfo match the // provided FailoverInfo. // // This function performs a deep comparison. func (v *FailoverInfo) Equals(rhs *FailoverInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.FailoverVersion, rhs.FailoverVersion) { return false } if !_I64_EqualsPtr(v.FailoverStartTimestamp, rhs.FailoverStartTimestamp) { return false } if !_I64_EqualsPtr(v.FailoverExpireTimestamp, rhs.FailoverExpireTimestamp) { return false } if !_I32_EqualsPtr(v.CompletedShardCount, rhs.CompletedShardCount) { return false } if !((v.PendingShards == nil && rhs.PendingShards == nil) || (v.PendingShards != nil && rhs.PendingShards != nil && _List_I32_Equals(v.PendingShards, rhs.PendingShards))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FailoverInfo. func (v *FailoverInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FailoverVersion != nil { enc.AddInt64("failoverVersion", *v.FailoverVersion) } if v.FailoverStartTimestamp != nil { enc.AddInt64("failoverStartTimestamp", *v.FailoverStartTimestamp) } if v.FailoverExpireTimestamp != nil { enc.AddInt64("failoverExpireTimestamp", *v.FailoverExpireTimestamp) } if v.CompletedShardCount != nil { enc.AddInt32("completedShardCount", *v.CompletedShardCount) } if v.PendingShards != nil { err = multierr.Append(err, enc.AddArray("pendingShards", (_List_I32_Zapper)(v.PendingShards))) } return err } // GetFailoverVersion returns the value of FailoverVersion if it is set or its // zero value if it is unset. func (v *FailoverInfo) GetFailoverVersion() (o int64) { if v != nil && v.FailoverVersion != nil { return *v.FailoverVersion } return } // IsSetFailoverVersion returns true if FailoverVersion is not nil. func (v *FailoverInfo) IsSetFailoverVersion() bool { return v != nil && v.FailoverVersion != nil } // GetFailoverStartTimestamp returns the value of FailoverStartTimestamp if it is set or its // zero value if it is unset. func (v *FailoverInfo) GetFailoverStartTimestamp() (o int64) { if v != nil && v.FailoverStartTimestamp != nil { return *v.FailoverStartTimestamp } return } // IsSetFailoverStartTimestamp returns true if FailoverStartTimestamp is not nil. func (v *FailoverInfo) IsSetFailoverStartTimestamp() bool { return v != nil && v.FailoverStartTimestamp != nil } // GetFailoverExpireTimestamp returns the value of FailoverExpireTimestamp if it is set or its // zero value if it is unset. func (v *FailoverInfo) GetFailoverExpireTimestamp() (o int64) { if v != nil && v.FailoverExpireTimestamp != nil { return *v.FailoverExpireTimestamp } return } // IsSetFailoverExpireTimestamp returns true if FailoverExpireTimestamp is not nil. func (v *FailoverInfo) IsSetFailoverExpireTimestamp() bool { return v != nil && v.FailoverExpireTimestamp != nil } // GetCompletedShardCount returns the value of CompletedShardCount if it is set or its // zero value if it is unset. func (v *FailoverInfo) GetCompletedShardCount() (o int32) { if v != nil && v.CompletedShardCount != nil { return *v.CompletedShardCount } return } // IsSetCompletedShardCount returns true if CompletedShardCount is not nil. func (v *FailoverInfo) IsSetCompletedShardCount() bool { return v != nil && v.CompletedShardCount != nil } // GetPendingShards returns the value of PendingShards if it is set or its // zero value if it is unset. func (v *FailoverInfo) GetPendingShards() (o []int32) { if v != nil && v.PendingShards != nil { return v.PendingShards } return } // IsSetPendingShards returns true if PendingShards is not nil. func (v *FailoverInfo) IsSetPendingShards() bool { return v != nil && v.PendingShards != nil } type FailoverType int32 const ( FailoverTypeInvalid FailoverType = 0 FailoverTypeForce FailoverType = 1 FailoverTypeGraceful FailoverType = 2 ) // FailoverType_Values returns all recognized values of FailoverType. func FailoverType_Values() []FailoverType { return []FailoverType{ FailoverTypeInvalid, FailoverTypeForce, FailoverTypeGraceful, } } // UnmarshalText tries to decode FailoverType from a byte slice // containing its name. // // var v FailoverType // err := v.UnmarshalText([]byte("INVALID")) func (v *FailoverType) UnmarshalText(value []byte) error { switch s := string(value); s { case "INVALID": *v = FailoverTypeInvalid return nil case "FORCE": *v = FailoverTypeForce return nil case "GRACEFUL": *v = FailoverTypeGraceful return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "FailoverType", err) } *v = FailoverType(val) return nil } } // MarshalText encodes FailoverType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v FailoverType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("INVALID"), nil case 1: return []byte("FORCE"), nil case 2: return []byte("GRACEFUL"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FailoverType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v FailoverType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "INVALID") case 1: enc.AddString("name", "FORCE") case 2: enc.AddString("name", "GRACEFUL") } return nil } // Ptr returns a pointer to this enum value. func (v FailoverType) Ptr() *FailoverType { return &v } // Encode encodes FailoverType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v FailoverType // return v.Encode(sWriter) func (v FailoverType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates FailoverType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v FailoverType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes FailoverType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return FailoverType(0), err // } // // var v FailoverType // if err := v.FromWire(x); err != nil { // return FailoverType(0), err // } // return v, nil func (v *FailoverType) FromWire(w wire.Value) error { *v = (FailoverType)(w.GetI32()) return nil } // Decode reads off the encoded FailoverType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v FailoverType // if err := v.Decode(sReader); err != nil { // return FailoverType(0), err // } // return v, nil func (v *FailoverType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (FailoverType)(i) return nil } // String returns a readable string representation of FailoverType. func (v FailoverType) String() string { w := int32(v) switch w { case 0: return "INVALID" case 1: return "FORCE" case 2: return "GRACEFUL" } return fmt.Sprintf("FailoverType(%d)", w) } // Equals returns true if this FailoverType value matches the provided // value. func (v FailoverType) Equals(rhs FailoverType) bool { return v == rhs } // MarshalJSON serializes FailoverType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v FailoverType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"INVALID\""), nil case 1: return ([]byte)("\"FORCE\""), nil case 2: return ([]byte)("\"GRACEFUL\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode FailoverType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *FailoverType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "FailoverType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "FailoverType") } *v = (FailoverType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "FailoverType") } } type FeatureFlags struct { WorkflowExecutionAlreadyCompletedErrorEnabled *bool `json:"WorkflowExecutionAlreadyCompletedErrorEnabled,omitempty"` AutoForwardingEnabled *bool `json:"AutoForwardingEnabled,omitempty"` } // ToWire translates a FeatureFlags struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *FeatureFlags) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.WorkflowExecutionAlreadyCompletedErrorEnabled != nil { w, err = wire.NewValueBool(*(v.WorkflowExecutionAlreadyCompletedErrorEnabled)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.AutoForwardingEnabled != nil { w, err = wire.NewValueBool(*(v.AutoForwardingEnabled)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a FeatureFlags struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a FeatureFlags struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v FeatureFlags // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *FeatureFlags) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.WorkflowExecutionAlreadyCompletedErrorEnabled = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.AutoForwardingEnabled = &x if err != nil { return err } } } } return nil } // Encode serializes a FeatureFlags struct directly into bytes, without going // through an intermediary type. // // An error is returned if a FeatureFlags struct could not be encoded. func (v *FeatureFlags) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.WorkflowExecutionAlreadyCompletedErrorEnabled != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.WorkflowExecutionAlreadyCompletedErrorEnabled)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AutoForwardingEnabled != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.AutoForwardingEnabled)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a FeatureFlags struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a FeatureFlags struct could not be generated from the wire // representation. func (v *FeatureFlags) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.WorkflowExecutionAlreadyCompletedErrorEnabled = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.AutoForwardingEnabled = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a FeatureFlags // struct. func (v *FeatureFlags) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.WorkflowExecutionAlreadyCompletedErrorEnabled != nil { fields[i] = fmt.Sprintf("WorkflowExecutionAlreadyCompletedErrorEnabled: %v", *(v.WorkflowExecutionAlreadyCompletedErrorEnabled)) i++ } if v.AutoForwardingEnabled != nil { fields[i] = fmt.Sprintf("AutoForwardingEnabled: %v", *(v.AutoForwardingEnabled)) i++ } return fmt.Sprintf("FeatureFlags{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this FeatureFlags match the // provided FeatureFlags. // // This function performs a deep comparison. func (v *FeatureFlags) Equals(rhs *FeatureFlags) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Bool_EqualsPtr(v.WorkflowExecutionAlreadyCompletedErrorEnabled, rhs.WorkflowExecutionAlreadyCompletedErrorEnabled) { return false } if !_Bool_EqualsPtr(v.AutoForwardingEnabled, rhs.AutoForwardingEnabled) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FeatureFlags. func (v *FeatureFlags) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.WorkflowExecutionAlreadyCompletedErrorEnabled != nil { enc.AddBool("WorkflowExecutionAlreadyCompletedErrorEnabled", *v.WorkflowExecutionAlreadyCompletedErrorEnabled) } if v.AutoForwardingEnabled != nil { enc.AddBool("AutoForwardingEnabled", *v.AutoForwardingEnabled) } return err } // GetWorkflowExecutionAlreadyCompletedErrorEnabled returns the value of WorkflowExecutionAlreadyCompletedErrorEnabled if it is set or its // zero value if it is unset. func (v *FeatureFlags) GetWorkflowExecutionAlreadyCompletedErrorEnabled() (o bool) { if v != nil && v.WorkflowExecutionAlreadyCompletedErrorEnabled != nil { return *v.WorkflowExecutionAlreadyCompletedErrorEnabled } return } // IsSetWorkflowExecutionAlreadyCompletedErrorEnabled returns true if WorkflowExecutionAlreadyCompletedErrorEnabled is not nil. func (v *FeatureFlags) IsSetWorkflowExecutionAlreadyCompletedErrorEnabled() bool { return v != nil && v.WorkflowExecutionAlreadyCompletedErrorEnabled != nil } // GetAutoForwardingEnabled returns the value of AutoForwardingEnabled if it is set or its // zero value if it is unset. func (v *FeatureFlags) GetAutoForwardingEnabled() (o bool) { if v != nil && v.AutoForwardingEnabled != nil { return *v.AutoForwardingEnabled } return } // IsSetAutoForwardingEnabled returns true if AutoForwardingEnabled is not nil. func (v *FeatureFlags) IsSetAutoForwardingEnabled() bool { return v != nil && v.AutoForwardingEnabled != nil } type FeatureNotEnabledError struct { FeatureFlag string `json:"featureFlag,required"` } // ToWire translates a FeatureNotEnabledError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *FeatureNotEnabledError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.FeatureFlag), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a FeatureNotEnabledError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a FeatureNotEnabledError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v FeatureNotEnabledError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *FeatureNotEnabledError) FromWire(w wire.Value) error { var err error featureFlagIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.FeatureFlag, err = field.Value.GetString(), error(nil) if err != nil { return err } featureFlagIsSet = true } } } if !featureFlagIsSet { return errors.New("field FeatureFlag of FeatureNotEnabledError is required") } return nil } // Encode serializes a FeatureNotEnabledError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a FeatureNotEnabledError struct could not be encoded. func (v *FeatureNotEnabledError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.FeatureFlag); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a FeatureNotEnabledError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a FeatureNotEnabledError struct could not be generated from the wire // representation. func (v *FeatureNotEnabledError) Decode(sr stream.Reader) error { featureFlagIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.FeatureFlag, err = sr.ReadString() if err != nil { return err } featureFlagIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !featureFlagIsSet { return errors.New("field FeatureFlag of FeatureNotEnabledError is required") } return nil } // String returns a readable string representation of a FeatureNotEnabledError // struct. func (v *FeatureNotEnabledError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("FeatureFlag: %v", v.FeatureFlag) i++ return fmt.Sprintf("FeatureNotEnabledError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*FeatureNotEnabledError) ErrorName() string { return "FeatureNotEnabledError" } // Equals returns true if all the fields of this FeatureNotEnabledError match the // provided FeatureNotEnabledError. // // This function performs a deep comparison. func (v *FeatureNotEnabledError) Equals(rhs *FeatureNotEnabledError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.FeatureFlag == rhs.FeatureFlag) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of FeatureNotEnabledError. func (v *FeatureNotEnabledError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("featureFlag", v.FeatureFlag) return err } // GetFeatureFlag returns the value of FeatureFlag if it is set or its // zero value if it is unset. func (v *FeatureNotEnabledError) GetFeatureFlag() (o string) { if v != nil { o = v.FeatureFlag } return } func (v *FeatureNotEnabledError) Error() string { return v.String() } type GetCrossClusterTasksRequest struct { ShardIDs []int32 `json:"shardIDs,omitempty"` TargetCluster *string `json:"targetCluster,omitempty"` } // ToWire translates a GetCrossClusterTasksRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetCrossClusterTasksRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ShardIDs != nil { w, err = wire.NewValueList(_List_I32_ValueList(v.ShardIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TargetCluster != nil { w, err = wire.NewValueString(*(v.TargetCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a GetCrossClusterTasksRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetCrossClusterTasksRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetCrossClusterTasksRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetCrossClusterTasksRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.ShardIDs, err = _List_I32_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetCluster = &x if err != nil { return err } } } } return nil } // Encode serializes a GetCrossClusterTasksRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetCrossClusterTasksRequest struct could not be encoded. func (v *GetCrossClusterTasksRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ShardIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_I32_Encode(v.ShardIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetCluster)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a GetCrossClusterTasksRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetCrossClusterTasksRequest struct could not be generated from the wire // representation. func (v *GetCrossClusterTasksRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.ShardIDs, err = _List_I32_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetCluster = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetCrossClusterTasksRequest // struct. func (v *GetCrossClusterTasksRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ShardIDs != nil { fields[i] = fmt.Sprintf("ShardIDs: %v", v.ShardIDs) i++ } if v.TargetCluster != nil { fields[i] = fmt.Sprintf("TargetCluster: %v", *(v.TargetCluster)) i++ } return fmt.Sprintf("GetCrossClusterTasksRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetCrossClusterTasksRequest match the // provided GetCrossClusterTasksRequest. // // This function performs a deep comparison. func (v *GetCrossClusterTasksRequest) Equals(rhs *GetCrossClusterTasksRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ShardIDs == nil && rhs.ShardIDs == nil) || (v.ShardIDs != nil && rhs.ShardIDs != nil && _List_I32_Equals(v.ShardIDs, rhs.ShardIDs))) { return false } if !_String_EqualsPtr(v.TargetCluster, rhs.TargetCluster) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetCrossClusterTasksRequest. func (v *GetCrossClusterTasksRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ShardIDs != nil { err = multierr.Append(err, enc.AddArray("shardIDs", (_List_I32_Zapper)(v.ShardIDs))) } if v.TargetCluster != nil { enc.AddString("targetCluster", *v.TargetCluster) } return err } // GetShardIDs returns the value of ShardIDs if it is set or its // zero value if it is unset. func (v *GetCrossClusterTasksRequest) GetShardIDs() (o []int32) { if v != nil && v.ShardIDs != nil { return v.ShardIDs } return } // IsSetShardIDs returns true if ShardIDs is not nil. func (v *GetCrossClusterTasksRequest) IsSetShardIDs() bool { return v != nil && v.ShardIDs != nil } // GetTargetCluster returns the value of TargetCluster if it is set or its // zero value if it is unset. func (v *GetCrossClusterTasksRequest) GetTargetCluster() (o string) { if v != nil && v.TargetCluster != nil { return *v.TargetCluster } return } // IsSetTargetCluster returns true if TargetCluster is not nil. func (v *GetCrossClusterTasksRequest) IsSetTargetCluster() bool { return v != nil && v.TargetCluster != nil } type GetCrossClusterTasksResponse struct { TasksByShard map[int32][]*CrossClusterTaskRequest `json:"tasksByShard,omitempty"` FailedCauseByShard map[int32]GetTaskFailedCause `json:"failedCauseByShard,omitempty"` } type _List_CrossClusterTaskRequest_ValueList []*CrossClusterTaskRequest func (v _List_CrossClusterTaskRequest_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*CrossClusterTaskRequest', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_CrossClusterTaskRequest_ValueList) Size() int { return len(v) } func (_List_CrossClusterTaskRequest_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_CrossClusterTaskRequest_ValueList) Close() {} type _Map_I32_List_CrossClusterTaskRequest_MapItemList map[int32][]*CrossClusterTaskRequest func (m _Map_I32_List_CrossClusterTaskRequest_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[int32][]*CrossClusterTaskRequest', key [%v]: value is nil", k) } kw, err := wire.NewValueI32(k), error(nil) if err != nil { return err } vw, err := wire.NewValueList(_List_CrossClusterTaskRequest_ValueList(v)), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_I32_List_CrossClusterTaskRequest_MapItemList) Size() int { return len(m) } func (_Map_I32_List_CrossClusterTaskRequest_MapItemList) KeyType() wire.Type { return wire.TI32 } func (_Map_I32_List_CrossClusterTaskRequest_MapItemList) ValueType() wire.Type { return wire.TList } func (_Map_I32_List_CrossClusterTaskRequest_MapItemList) Close() {} type _Map_I32_GetTaskFailedCause_MapItemList map[int32]GetTaskFailedCause func (m _Map_I32_GetTaskFailedCause_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { kw, err := wire.NewValueI32(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_I32_GetTaskFailedCause_MapItemList) Size() int { return len(m) } func (_Map_I32_GetTaskFailedCause_MapItemList) KeyType() wire.Type { return wire.TI32 } func (_Map_I32_GetTaskFailedCause_MapItemList) ValueType() wire.Type { return wire.TI32 } func (_Map_I32_GetTaskFailedCause_MapItemList) Close() {} // ToWire translates a GetCrossClusterTasksResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetCrossClusterTasksResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.TasksByShard != nil { w, err = wire.NewValueMap(_Map_I32_List_CrossClusterTaskRequest_MapItemList(v.TasksByShard)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.FailedCauseByShard != nil { w, err = wire.NewValueMap(_Map_I32_GetTaskFailedCause_MapItemList(v.FailedCauseByShard)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CrossClusterTaskRequest_Read(w wire.Value) (*CrossClusterTaskRequest, error) { var v CrossClusterTaskRequest err := v.FromWire(w) return &v, err } func _List_CrossClusterTaskRequest_Read(l wire.ValueList) ([]*CrossClusterTaskRequest, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*CrossClusterTaskRequest, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _CrossClusterTaskRequest_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _Map_I32_List_CrossClusterTaskRequest_Read(m wire.MapItemList) (map[int32][]*CrossClusterTaskRequest, error) { if m.KeyType() != wire.TI32 { return nil, nil } if m.ValueType() != wire.TList { return nil, nil } o := make(map[int32][]*CrossClusterTaskRequest, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetI32(), error(nil) if err != nil { return err } v, err := _List_CrossClusterTaskRequest_Read(x.Value.GetList()) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } func _GetTaskFailedCause_Read(w wire.Value) (GetTaskFailedCause, error) { var v GetTaskFailedCause err := v.FromWire(w) return v, err } func _Map_I32_GetTaskFailedCause_Read(m wire.MapItemList) (map[int32]GetTaskFailedCause, error) { if m.KeyType() != wire.TI32 { return nil, nil } if m.ValueType() != wire.TI32 { return nil, nil } o := make(map[int32]GetTaskFailedCause, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetI32(), error(nil) if err != nil { return err } v, err := _GetTaskFailedCause_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a GetCrossClusterTasksResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetCrossClusterTasksResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetCrossClusterTasksResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetCrossClusterTasksResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.TasksByShard, err = _Map_I32_List_CrossClusterTaskRequest_Read(field.Value.GetMap()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TMap { v.FailedCauseByShard, err = _Map_I32_GetTaskFailedCause_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _List_CrossClusterTaskRequest_Encode(val []*CrossClusterTaskRequest, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*CrossClusterTaskRequest', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } func _Map_I32_List_CrossClusterTaskRequest_Encode(val map[int32][]*CrossClusterTaskRequest, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TI32, ValueType: wire.TList, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[int32][]*CrossClusterTaskRequest', key [%v]: value is nil", k) } if err := sw.WriteInt32(k); err != nil { return err } if err := _List_CrossClusterTaskRequest_Encode(v, sw); err != nil { return err } } return sw.WriteMapEnd() } func _Map_I32_GetTaskFailedCause_Encode(val map[int32]GetTaskFailedCause, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TI32, ValueType: wire.TI32, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if err := sw.WriteInt32(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a GetCrossClusterTasksResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetCrossClusterTasksResponse struct could not be encoded. func (v *GetCrossClusterTasksResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TasksByShard != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_I32_List_CrossClusterTaskRequest_Encode(v.TasksByShard, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailedCauseByShard != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TMap}); err != nil { return err } if err := _Map_I32_GetTaskFailedCause_Encode(v.FailedCauseByShard, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CrossClusterTaskRequest_Decode(sr stream.Reader) (*CrossClusterTaskRequest, error) { var v CrossClusterTaskRequest err := v.Decode(sr) return &v, err } func _List_CrossClusterTaskRequest_Decode(sr stream.Reader) ([]*CrossClusterTaskRequest, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*CrossClusterTaskRequest, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _CrossClusterTaskRequest_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _Map_I32_List_CrossClusterTaskRequest_Decode(sr stream.Reader) (map[int32][]*CrossClusterTaskRequest, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TI32 || mh.ValueType != wire.TList { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[int32][]*CrossClusterTaskRequest, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadInt32() if err != nil { return nil, err } v, err := _List_CrossClusterTaskRequest_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } func _GetTaskFailedCause_Decode(sr stream.Reader) (GetTaskFailedCause, error) { var v GetTaskFailedCause err := v.Decode(sr) return v, err } func _Map_I32_GetTaskFailedCause_Decode(sr stream.Reader) (map[int32]GetTaskFailedCause, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TI32 || mh.ValueType != wire.TI32 { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[int32]GetTaskFailedCause, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadInt32() if err != nil { return nil, err } v, err := _GetTaskFailedCause_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a GetCrossClusterTasksResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetCrossClusterTasksResponse struct could not be generated from the wire // representation. func (v *GetCrossClusterTasksResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.TasksByShard, err = _Map_I32_List_CrossClusterTaskRequest_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TMap: v.FailedCauseByShard, err = _Map_I32_GetTaskFailedCause_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetCrossClusterTasksResponse // struct. func (v *GetCrossClusterTasksResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.TasksByShard != nil { fields[i] = fmt.Sprintf("TasksByShard: %v", v.TasksByShard) i++ } if v.FailedCauseByShard != nil { fields[i] = fmt.Sprintf("FailedCauseByShard: %v", v.FailedCauseByShard) i++ } return fmt.Sprintf("GetCrossClusterTasksResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_CrossClusterTaskRequest_Equals(lhs, rhs []*CrossClusterTaskRequest) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } func _Map_I32_List_CrossClusterTaskRequest_Equals(lhs, rhs map[int32][]*CrossClusterTaskRequest) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !_List_CrossClusterTaskRequest_Equals(lv, rv) { return false } } return true } func _Map_I32_GetTaskFailedCause_Equals(lhs, rhs map[int32]GetTaskFailedCause) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this GetCrossClusterTasksResponse match the // provided GetCrossClusterTasksResponse. // // This function performs a deep comparison. func (v *GetCrossClusterTasksResponse) Equals(rhs *GetCrossClusterTasksResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TasksByShard == nil && rhs.TasksByShard == nil) || (v.TasksByShard != nil && rhs.TasksByShard != nil && _Map_I32_List_CrossClusterTaskRequest_Equals(v.TasksByShard, rhs.TasksByShard))) { return false } if !((v.FailedCauseByShard == nil && rhs.FailedCauseByShard == nil) || (v.FailedCauseByShard != nil && rhs.FailedCauseByShard != nil && _Map_I32_GetTaskFailedCause_Equals(v.FailedCauseByShard, rhs.FailedCauseByShard))) { return false } return true } type _List_CrossClusterTaskRequest_Zapper []*CrossClusterTaskRequest // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_CrossClusterTaskRequest_Zapper. func (l _List_CrossClusterTaskRequest_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } type _Map_I32_List_CrossClusterTaskRequest_Item_Zapper struct { Key int32 Value []*CrossClusterTaskRequest } // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_List_CrossClusterTaskRequest_Item_Zapper. func (v _Map_I32_List_CrossClusterTaskRequest_Item_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { enc.AddInt32("key", v.Key) err = multierr.Append(err, enc.AddArray("value", (_List_CrossClusterTaskRequest_Zapper)(v.Value))) return err } type _Map_I32_List_CrossClusterTaskRequest_Zapper map[int32][]*CrossClusterTaskRequest // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_List_CrossClusterTaskRequest_Zapper. func (m _Map_I32_List_CrossClusterTaskRequest_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AppendObject(_Map_I32_List_CrossClusterTaskRequest_Item_Zapper{Key: k, Value: v})) } return err } type _Map_I32_GetTaskFailedCause_Item_Zapper struct { Key int32 Value GetTaskFailedCause } // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_GetTaskFailedCause_Item_Zapper. func (v _Map_I32_GetTaskFailedCause_Item_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { enc.AddInt32("key", v.Key) err = multierr.Append(err, enc.AddObject("value", v.Value)) return err } type _Map_I32_GetTaskFailedCause_Zapper map[int32]GetTaskFailedCause // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_GetTaskFailedCause_Zapper. func (m _Map_I32_GetTaskFailedCause_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AppendObject(_Map_I32_GetTaskFailedCause_Item_Zapper{Key: k, Value: v})) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetCrossClusterTasksResponse. func (v *GetCrossClusterTasksResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TasksByShard != nil { err = multierr.Append(err, enc.AddArray("tasksByShard", (_Map_I32_List_CrossClusterTaskRequest_Zapper)(v.TasksByShard))) } if v.FailedCauseByShard != nil { err = multierr.Append(err, enc.AddArray("failedCauseByShard", (_Map_I32_GetTaskFailedCause_Zapper)(v.FailedCauseByShard))) } return err } // GetTasksByShard returns the value of TasksByShard if it is set or its // zero value if it is unset. func (v *GetCrossClusterTasksResponse) GetTasksByShard() (o map[int32][]*CrossClusterTaskRequest) { if v != nil && v.TasksByShard != nil { return v.TasksByShard } return } // IsSetTasksByShard returns true if TasksByShard is not nil. func (v *GetCrossClusterTasksResponse) IsSetTasksByShard() bool { return v != nil && v.TasksByShard != nil } // GetFailedCauseByShard returns the value of FailedCauseByShard if it is set or its // zero value if it is unset. func (v *GetCrossClusterTasksResponse) GetFailedCauseByShard() (o map[int32]GetTaskFailedCause) { if v != nil && v.FailedCauseByShard != nil { return v.FailedCauseByShard } return } // IsSetFailedCauseByShard returns true if FailedCauseByShard is not nil. func (v *GetCrossClusterTasksResponse) IsSetFailedCauseByShard() bool { return v != nil && v.FailedCauseByShard != nil } type GetSearchAttributesResponse struct { Keys map[string]IndexedValueType `json:"keys,omitempty"` } type _Map_String_IndexedValueType_MapItemList map[string]IndexedValueType func (m _Map_String_IndexedValueType_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_IndexedValueType_MapItemList) Size() int { return len(m) } func (_Map_String_IndexedValueType_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_IndexedValueType_MapItemList) ValueType() wire.Type { return wire.TI32 } func (_Map_String_IndexedValueType_MapItemList) Close() {} // ToWire translates a GetSearchAttributesResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetSearchAttributesResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Keys != nil { w, err = wire.NewValueMap(_Map_String_IndexedValueType_MapItemList(v.Keys)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _IndexedValueType_Read(w wire.Value) (IndexedValueType, error) { var v IndexedValueType err := v.FromWire(w) return v, err } func _Map_String_IndexedValueType_Read(m wire.MapItemList) (map[string]IndexedValueType, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TI32 { return nil, nil } o := make(map[string]IndexedValueType, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _IndexedValueType_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a GetSearchAttributesResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetSearchAttributesResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetSearchAttributesResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetSearchAttributesResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.Keys, err = _Map_String_IndexedValueType_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_IndexedValueType_Encode(val map[string]IndexedValueType, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TI32, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a GetSearchAttributesResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetSearchAttributesResponse struct could not be encoded. func (v *GetSearchAttributesResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Keys != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_IndexedValueType_Encode(v.Keys, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _IndexedValueType_Decode(sr stream.Reader) (IndexedValueType, error) { var v IndexedValueType err := v.Decode(sr) return v, err } func _Map_String_IndexedValueType_Decode(sr stream.Reader) (map[string]IndexedValueType, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TI32 { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]IndexedValueType, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _IndexedValueType_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a GetSearchAttributesResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetSearchAttributesResponse struct could not be generated from the wire // representation. func (v *GetSearchAttributesResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.Keys, err = _Map_String_IndexedValueType_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetSearchAttributesResponse // struct. func (v *GetSearchAttributesResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Keys != nil { fields[i] = fmt.Sprintf("Keys: %v", v.Keys) i++ } return fmt.Sprintf("GetSearchAttributesResponse{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_IndexedValueType_Equals(lhs, rhs map[string]IndexedValueType) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this GetSearchAttributesResponse match the // provided GetSearchAttributesResponse. // // This function performs a deep comparison. func (v *GetSearchAttributesResponse) Equals(rhs *GetSearchAttributesResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Keys == nil && rhs.Keys == nil) || (v.Keys != nil && rhs.Keys != nil && _Map_String_IndexedValueType_Equals(v.Keys, rhs.Keys))) { return false } return true } type _Map_String_IndexedValueType_Zapper map[string]IndexedValueType // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_IndexedValueType_Zapper. func (m _Map_String_IndexedValueType_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetSearchAttributesResponse. func (v *GetSearchAttributesResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Keys != nil { err = multierr.Append(err, enc.AddObject("keys", (_Map_String_IndexedValueType_Zapper)(v.Keys))) } return err } // GetKeys returns the value of Keys if it is set or its // zero value if it is unset. func (v *GetSearchAttributesResponse) GetKeys() (o map[string]IndexedValueType) { if v != nil && v.Keys != nil { return v.Keys } return } // IsSetKeys returns true if Keys is not nil. func (v *GetSearchAttributesResponse) IsSetKeys() bool { return v != nil && v.Keys != nil } type GetTaskFailedCause int32 const ( GetTaskFailedCauseServiceBusy GetTaskFailedCause = 0 GetTaskFailedCauseTimeout GetTaskFailedCause = 1 GetTaskFailedCauseShardOwnershipLost GetTaskFailedCause = 2 GetTaskFailedCauseUncategorized GetTaskFailedCause = 3 ) // GetTaskFailedCause_Values returns all recognized values of GetTaskFailedCause. func GetTaskFailedCause_Values() []GetTaskFailedCause { return []GetTaskFailedCause{ GetTaskFailedCauseServiceBusy, GetTaskFailedCauseTimeout, GetTaskFailedCauseShardOwnershipLost, GetTaskFailedCauseUncategorized, } } // UnmarshalText tries to decode GetTaskFailedCause from a byte slice // containing its name. // // var v GetTaskFailedCause // err := v.UnmarshalText([]byte("SERVICE_BUSY")) func (v *GetTaskFailedCause) UnmarshalText(value []byte) error { switch s := string(value); s { case "SERVICE_BUSY": *v = GetTaskFailedCauseServiceBusy return nil case "TIMEOUT": *v = GetTaskFailedCauseTimeout return nil case "SHARD_OWNERSHIP_LOST": *v = GetTaskFailedCauseShardOwnershipLost return nil case "UNCATEGORIZED": *v = GetTaskFailedCauseUncategorized return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "GetTaskFailedCause", err) } *v = GetTaskFailedCause(val) return nil } } // MarshalText encodes GetTaskFailedCause to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v GetTaskFailedCause) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("SERVICE_BUSY"), nil case 1: return []byte("TIMEOUT"), nil case 2: return []byte("SHARD_OWNERSHIP_LOST"), nil case 3: return []byte("UNCATEGORIZED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetTaskFailedCause. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v GetTaskFailedCause) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "SERVICE_BUSY") case 1: enc.AddString("name", "TIMEOUT") case 2: enc.AddString("name", "SHARD_OWNERSHIP_LOST") case 3: enc.AddString("name", "UNCATEGORIZED") } return nil } // Ptr returns a pointer to this enum value. func (v GetTaskFailedCause) Ptr() *GetTaskFailedCause { return &v } // Encode encodes GetTaskFailedCause directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v GetTaskFailedCause // return v.Encode(sWriter) func (v GetTaskFailedCause) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates GetTaskFailedCause into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v GetTaskFailedCause) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes GetTaskFailedCause from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return GetTaskFailedCause(0), err // } // // var v GetTaskFailedCause // if err := v.FromWire(x); err != nil { // return GetTaskFailedCause(0), err // } // return v, nil func (v *GetTaskFailedCause) FromWire(w wire.Value) error { *v = (GetTaskFailedCause)(w.GetI32()) return nil } // Decode reads off the encoded GetTaskFailedCause directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v GetTaskFailedCause // if err := v.Decode(sReader); err != nil { // return GetTaskFailedCause(0), err // } // return v, nil func (v *GetTaskFailedCause) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (GetTaskFailedCause)(i) return nil } // String returns a readable string representation of GetTaskFailedCause. func (v GetTaskFailedCause) String() string { w := int32(v) switch w { case 0: return "SERVICE_BUSY" case 1: return "TIMEOUT" case 2: return "SHARD_OWNERSHIP_LOST" case 3: return "UNCATEGORIZED" } return fmt.Sprintf("GetTaskFailedCause(%d)", w) } // Equals returns true if this GetTaskFailedCause value matches the provided // value. func (v GetTaskFailedCause) Equals(rhs GetTaskFailedCause) bool { return v == rhs } // MarshalJSON serializes GetTaskFailedCause into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v GetTaskFailedCause) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"SERVICE_BUSY\""), nil case 1: return ([]byte)("\"TIMEOUT\""), nil case 2: return ([]byte)("\"SHARD_OWNERSHIP_LOST\""), nil case 3: return ([]byte)("\"UNCATEGORIZED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode GetTaskFailedCause from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *GetTaskFailedCause) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "GetTaskFailedCause") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "GetTaskFailedCause") } *v = (GetTaskFailedCause)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "GetTaskFailedCause") } } type GetTaskListsByDomainRequest struct { DomainName *string `json:"domainName,omitempty"` } // ToWire translates a GetTaskListsByDomainRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetTaskListsByDomainRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.DomainName != nil { w, err = wire.NewValueString(*(v.DomainName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a GetTaskListsByDomainRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetTaskListsByDomainRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetTaskListsByDomainRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetTaskListsByDomainRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainName = &x if err != nil { return err } } } } return nil } // Encode serializes a GetTaskListsByDomainRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetTaskListsByDomainRequest struct could not be encoded. func (v *GetTaskListsByDomainRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a GetTaskListsByDomainRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetTaskListsByDomainRequest struct could not be generated from the wire // representation. func (v *GetTaskListsByDomainRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainName = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetTaskListsByDomainRequest // struct. func (v *GetTaskListsByDomainRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.DomainName != nil { fields[i] = fmt.Sprintf("DomainName: %v", *(v.DomainName)) i++ } return fmt.Sprintf("GetTaskListsByDomainRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this GetTaskListsByDomainRequest match the // provided GetTaskListsByDomainRequest. // // This function performs a deep comparison. func (v *GetTaskListsByDomainRequest) Equals(rhs *GetTaskListsByDomainRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainName, rhs.DomainName) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetTaskListsByDomainRequest. func (v *GetTaskListsByDomainRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainName != nil { enc.AddString("domainName", *v.DomainName) } return err } // GetDomainName returns the value of DomainName if it is set or its // zero value if it is unset. func (v *GetTaskListsByDomainRequest) GetDomainName() (o string) { if v != nil && v.DomainName != nil { return *v.DomainName } return } // IsSetDomainName returns true if DomainName is not nil. func (v *GetTaskListsByDomainRequest) IsSetDomainName() bool { return v != nil && v.DomainName != nil } type GetTaskListsByDomainResponse struct { DecisionTaskListMap map[string]*DescribeTaskListResponse `json:"decisionTaskListMap,omitempty"` ActivityTaskListMap map[string]*DescribeTaskListResponse `json:"activityTaskListMap,omitempty"` } type _Map_String_DescribeTaskListResponse_MapItemList map[string]*DescribeTaskListResponse func (m _Map_String_DescribeTaskListResponse_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*DescribeTaskListResponse', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_DescribeTaskListResponse_MapItemList) Size() int { return len(m) } func (_Map_String_DescribeTaskListResponse_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_DescribeTaskListResponse_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_DescribeTaskListResponse_MapItemList) Close() {} // ToWire translates a GetTaskListsByDomainResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetTaskListsByDomainResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DecisionTaskListMap != nil { w, err = wire.NewValueMap(_Map_String_DescribeTaskListResponse_MapItemList(v.DecisionTaskListMap)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ActivityTaskListMap != nil { w, err = wire.NewValueMap(_Map_String_DescribeTaskListResponse_MapItemList(v.ActivityTaskListMap)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeTaskListResponse_Read(w wire.Value) (*DescribeTaskListResponse, error) { var v DescribeTaskListResponse err := v.FromWire(w) return &v, err } func _Map_String_DescribeTaskListResponse_Read(m wire.MapItemList) (map[string]*DescribeTaskListResponse, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*DescribeTaskListResponse, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _DescribeTaskListResponse_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a GetTaskListsByDomainResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetTaskListsByDomainResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetTaskListsByDomainResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetTaskListsByDomainResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.DecisionTaskListMap, err = _Map_String_DescribeTaskListResponse_Read(field.Value.GetMap()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TMap { v.ActivityTaskListMap, err = _Map_String_DescribeTaskListResponse_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_DescribeTaskListResponse_Encode(val map[string]*DescribeTaskListResponse, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*DescribeTaskListResponse', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a GetTaskListsByDomainResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetTaskListsByDomainResponse struct could not be encoded. func (v *GetTaskListsByDomainResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DecisionTaskListMap != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_DescribeTaskListResponse_Encode(v.DecisionTaskListMap, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityTaskListMap != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TMap}); err != nil { return err } if err := _Map_String_DescribeTaskListResponse_Encode(v.ActivityTaskListMap, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeTaskListResponse_Decode(sr stream.Reader) (*DescribeTaskListResponse, error) { var v DescribeTaskListResponse err := v.Decode(sr) return &v, err } func _Map_String_DescribeTaskListResponse_Decode(sr stream.Reader) (map[string]*DescribeTaskListResponse, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*DescribeTaskListResponse, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _DescribeTaskListResponse_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a GetTaskListsByDomainResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetTaskListsByDomainResponse struct could not be generated from the wire // representation. func (v *GetTaskListsByDomainResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.DecisionTaskListMap, err = _Map_String_DescribeTaskListResponse_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TMap: v.ActivityTaskListMap, err = _Map_String_DescribeTaskListResponse_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetTaskListsByDomainResponse // struct. func (v *GetTaskListsByDomainResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DecisionTaskListMap != nil { fields[i] = fmt.Sprintf("DecisionTaskListMap: %v", v.DecisionTaskListMap) i++ } if v.ActivityTaskListMap != nil { fields[i] = fmt.Sprintf("ActivityTaskListMap: %v", v.ActivityTaskListMap) i++ } return fmt.Sprintf("GetTaskListsByDomainResponse{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_DescribeTaskListResponse_Equals(lhs, rhs map[string]*DescribeTaskListResponse) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this GetTaskListsByDomainResponse match the // provided GetTaskListsByDomainResponse. // // This function performs a deep comparison. func (v *GetTaskListsByDomainResponse) Equals(rhs *GetTaskListsByDomainResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DecisionTaskListMap == nil && rhs.DecisionTaskListMap == nil) || (v.DecisionTaskListMap != nil && rhs.DecisionTaskListMap != nil && _Map_String_DescribeTaskListResponse_Equals(v.DecisionTaskListMap, rhs.DecisionTaskListMap))) { return false } if !((v.ActivityTaskListMap == nil && rhs.ActivityTaskListMap == nil) || (v.ActivityTaskListMap != nil && rhs.ActivityTaskListMap != nil && _Map_String_DescribeTaskListResponse_Equals(v.ActivityTaskListMap, rhs.ActivityTaskListMap))) { return false } return true } type _Map_String_DescribeTaskListResponse_Zapper map[string]*DescribeTaskListResponse // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_DescribeTaskListResponse_Zapper. func (m _Map_String_DescribeTaskListResponse_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetTaskListsByDomainResponse. func (v *GetTaskListsByDomainResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DecisionTaskListMap != nil { err = multierr.Append(err, enc.AddObject("decisionTaskListMap", (_Map_String_DescribeTaskListResponse_Zapper)(v.DecisionTaskListMap))) } if v.ActivityTaskListMap != nil { err = multierr.Append(err, enc.AddObject("activityTaskListMap", (_Map_String_DescribeTaskListResponse_Zapper)(v.ActivityTaskListMap))) } return err } // GetDecisionTaskListMap returns the value of DecisionTaskListMap if it is set or its // zero value if it is unset. func (v *GetTaskListsByDomainResponse) GetDecisionTaskListMap() (o map[string]*DescribeTaskListResponse) { if v != nil && v.DecisionTaskListMap != nil { return v.DecisionTaskListMap } return } // IsSetDecisionTaskListMap returns true if DecisionTaskListMap is not nil. func (v *GetTaskListsByDomainResponse) IsSetDecisionTaskListMap() bool { return v != nil && v.DecisionTaskListMap != nil } // GetActivityTaskListMap returns the value of ActivityTaskListMap if it is set or its // zero value if it is unset. func (v *GetTaskListsByDomainResponse) GetActivityTaskListMap() (o map[string]*DescribeTaskListResponse) { if v != nil && v.ActivityTaskListMap != nil { return v.ActivityTaskListMap } return } // IsSetActivityTaskListMap returns true if ActivityTaskListMap is not nil. func (v *GetTaskListsByDomainResponse) IsSetActivityTaskListMap() bool { return v != nil && v.ActivityTaskListMap != nil } type GetWorkflowExecutionHistoryRequest struct { Domain *string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` MaximumPageSize *int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` WaitForNewEvent *bool `json:"waitForNewEvent,omitempty"` HistoryEventFilterType *HistoryEventFilterType `json:"HistoryEventFilterType,omitempty"` SkipArchival *bool `json:"skipArchival,omitempty"` QueryConsistencyLevel *QueryConsistencyLevel `json:"queryConsistencyLevel,omitempty"` } // ToWire translates a GetWorkflowExecutionHistoryRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetWorkflowExecutionHistoryRequest) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.MaximumPageSize != nil { w, err = wire.NewValueI32(*(v.MaximumPageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.WaitForNewEvent != nil { w, err = wire.NewValueBool(*(v.WaitForNewEvent)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.HistoryEventFilterType != nil { w, err = v.HistoryEventFilterType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.SkipArchival != nil { w, err = wire.NewValueBool(*(v.SkipArchival)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.QueryConsistencyLevel != nil { w, err = v.QueryConsistencyLevel.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _HistoryEventFilterType_Read(w wire.Value) (HistoryEventFilterType, error) { var v HistoryEventFilterType err := v.FromWire(w) return v, err } // FromWire deserializes a GetWorkflowExecutionHistoryRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetWorkflowExecutionHistoryRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetWorkflowExecutionHistoryRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetWorkflowExecutionHistoryRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.MaximumPageSize = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.WaitForNewEvent = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x HistoryEventFilterType x, err = _HistoryEventFilterType_Read(field.Value) v.HistoryEventFilterType = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.SkipArchival = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI32 { var x QueryConsistencyLevel x, err = _QueryConsistencyLevel_Read(field.Value) v.QueryConsistencyLevel = &x if err != nil { return err } } } } return nil } // Encode serializes a GetWorkflowExecutionHistoryRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetWorkflowExecutionHistoryRequest struct could not be encoded. func (v *GetWorkflowExecutionHistoryRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MaximumPageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.MaximumPageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WaitForNewEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.WaitForNewEvent)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryEventFilterType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := v.HistoryEventFilterType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SkipArchival != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.SkipArchival)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryConsistencyLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI32}); err != nil { return err } if err := v.QueryConsistencyLevel.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _HistoryEventFilterType_Decode(sr stream.Reader) (HistoryEventFilterType, error) { var v HistoryEventFilterType err := v.Decode(sr) return v, err } // Decode deserializes a GetWorkflowExecutionHistoryRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetWorkflowExecutionHistoryRequest struct could not be generated from the wire // representation. func (v *GetWorkflowExecutionHistoryRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.MaximumPageSize = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.WaitForNewEvent = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x HistoryEventFilterType x, err = _HistoryEventFilterType_Decode(sr) v.HistoryEventFilterType = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.SkipArchival = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI32: var x QueryConsistencyLevel x, err = _QueryConsistencyLevel_Decode(sr) v.QueryConsistencyLevel = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetWorkflowExecutionHistoryRequest // struct. func (v *GetWorkflowExecutionHistoryRequest) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.MaximumPageSize != nil { fields[i] = fmt.Sprintf("MaximumPageSize: %v", *(v.MaximumPageSize)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.WaitForNewEvent != nil { fields[i] = fmt.Sprintf("WaitForNewEvent: %v", *(v.WaitForNewEvent)) i++ } if v.HistoryEventFilterType != nil { fields[i] = fmt.Sprintf("HistoryEventFilterType: %v", *(v.HistoryEventFilterType)) i++ } if v.SkipArchival != nil { fields[i] = fmt.Sprintf("SkipArchival: %v", *(v.SkipArchival)) i++ } if v.QueryConsistencyLevel != nil { fields[i] = fmt.Sprintf("QueryConsistencyLevel: %v", *(v.QueryConsistencyLevel)) i++ } return fmt.Sprintf("GetWorkflowExecutionHistoryRequest{%v}", strings.Join(fields[:i], ", ")) } func _HistoryEventFilterType_EqualsPtr(lhs, rhs *HistoryEventFilterType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this GetWorkflowExecutionHistoryRequest match the // provided GetWorkflowExecutionHistoryRequest. // // This function performs a deep comparison. func (v *GetWorkflowExecutionHistoryRequest) Equals(rhs *GetWorkflowExecutionHistoryRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !_I32_EqualsPtr(v.MaximumPageSize, rhs.MaximumPageSize) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !_Bool_EqualsPtr(v.WaitForNewEvent, rhs.WaitForNewEvent) { return false } if !_HistoryEventFilterType_EqualsPtr(v.HistoryEventFilterType, rhs.HistoryEventFilterType) { return false } if !_Bool_EqualsPtr(v.SkipArchival, rhs.SkipArchival) { return false } if !_QueryConsistencyLevel_EqualsPtr(v.QueryConsistencyLevel, rhs.QueryConsistencyLevel) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetWorkflowExecutionHistoryRequest. func (v *GetWorkflowExecutionHistoryRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.MaximumPageSize != nil { enc.AddInt32("maximumPageSize", *v.MaximumPageSize) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.WaitForNewEvent != nil { enc.AddBool("waitForNewEvent", *v.WaitForNewEvent) } if v.HistoryEventFilterType != nil { err = multierr.Append(err, enc.AddObject("HistoryEventFilterType", *v.HistoryEventFilterType)) } if v.SkipArchival != nil { enc.AddBool("skipArchival", *v.SkipArchival) } if v.QueryConsistencyLevel != nil { err = multierr.Append(err, enc.AddObject("queryConsistencyLevel", *v.QueryConsistencyLevel)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *GetWorkflowExecutionHistoryRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *GetWorkflowExecutionHistoryRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetMaximumPageSize returns the value of MaximumPageSize if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryRequest) GetMaximumPageSize() (o int32) { if v != nil && v.MaximumPageSize != nil { return *v.MaximumPageSize } return } // IsSetMaximumPageSize returns true if MaximumPageSize is not nil. func (v *GetWorkflowExecutionHistoryRequest) IsSetMaximumPageSize() bool { return v != nil && v.MaximumPageSize != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *GetWorkflowExecutionHistoryRequest) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetWaitForNewEvent returns the value of WaitForNewEvent if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryRequest) GetWaitForNewEvent() (o bool) { if v != nil && v.WaitForNewEvent != nil { return *v.WaitForNewEvent } return } // IsSetWaitForNewEvent returns true if WaitForNewEvent is not nil. func (v *GetWorkflowExecutionHistoryRequest) IsSetWaitForNewEvent() bool { return v != nil && v.WaitForNewEvent != nil } // GetHistoryEventFilterType returns the value of HistoryEventFilterType if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryRequest) GetHistoryEventFilterType() (o HistoryEventFilterType) { if v != nil && v.HistoryEventFilterType != nil { return *v.HistoryEventFilterType } return } // IsSetHistoryEventFilterType returns true if HistoryEventFilterType is not nil. func (v *GetWorkflowExecutionHistoryRequest) IsSetHistoryEventFilterType() bool { return v != nil && v.HistoryEventFilterType != nil } // GetSkipArchival returns the value of SkipArchival if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryRequest) GetSkipArchival() (o bool) { if v != nil && v.SkipArchival != nil { return *v.SkipArchival } return } // IsSetSkipArchival returns true if SkipArchival is not nil. func (v *GetWorkflowExecutionHistoryRequest) IsSetSkipArchival() bool { return v != nil && v.SkipArchival != nil } // GetQueryConsistencyLevel returns the value of QueryConsistencyLevel if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryRequest) GetQueryConsistencyLevel() (o QueryConsistencyLevel) { if v != nil && v.QueryConsistencyLevel != nil { return *v.QueryConsistencyLevel } return } // IsSetQueryConsistencyLevel returns true if QueryConsistencyLevel is not nil. func (v *GetWorkflowExecutionHistoryRequest) IsSetQueryConsistencyLevel() bool { return v != nil && v.QueryConsistencyLevel != nil } type GetWorkflowExecutionHistoryResponse struct { History *History `json:"history,omitempty"` RawHistory []*DataBlob `json:"rawHistory,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` Archived *bool `json:"archived,omitempty"` } type _List_DataBlob_ValueList []*DataBlob func (v _List_DataBlob_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*DataBlob', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_DataBlob_ValueList) Size() int { return len(v) } func (_List_DataBlob_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_DataBlob_ValueList) Close() {} // ToWire translates a GetWorkflowExecutionHistoryResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *GetWorkflowExecutionHistoryResponse) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.History != nil { w, err = v.History.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.RawHistory != nil { w, err = wire.NewValueList(_List_DataBlob_ValueList(v.RawHistory)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 11, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Archived != nil { w, err = wire.NewValueBool(*(v.Archived)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _History_Read(w wire.Value) (*History, error) { var v History err := v.FromWire(w) return &v, err } func _List_DataBlob_Read(l wire.ValueList) ([]*DataBlob, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*DataBlob, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _DataBlob_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a GetWorkflowExecutionHistoryResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a GetWorkflowExecutionHistoryResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v GetWorkflowExecutionHistoryResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *GetWorkflowExecutionHistoryResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.History, err = _History_Read(field.Value) if err != nil { return err } } case 11: if field.Value.Type() == wire.TList { v.RawHistory, err = _List_DataBlob_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.Archived = &x if err != nil { return err } } } } return nil } func _List_DataBlob_Encode(val []*DataBlob, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*DataBlob', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a GetWorkflowExecutionHistoryResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a GetWorkflowExecutionHistoryResponse struct could not be encoded. func (v *GetWorkflowExecutionHistoryResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.History != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.History.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RawHistory != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 11, Type: wire.TList}); err != nil { return err } if err := _List_DataBlob_Encode(v.RawHistory, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Archived != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.Archived)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _History_Decode(sr stream.Reader) (*History, error) { var v History err := v.Decode(sr) return &v, err } func _List_DataBlob_Decode(sr stream.Reader) ([]*DataBlob, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*DataBlob, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _DataBlob_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a GetWorkflowExecutionHistoryResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a GetWorkflowExecutionHistoryResponse struct could not be generated from the wire // representation. func (v *GetWorkflowExecutionHistoryResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.History, err = _History_Decode(sr) if err != nil { return err } case fh.ID == 11 && fh.Type == wire.TList: v.RawHistory, err = _List_DataBlob_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.Archived = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a GetWorkflowExecutionHistoryResponse // struct. func (v *GetWorkflowExecutionHistoryResponse) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.History != nil { fields[i] = fmt.Sprintf("History: %v", v.History) i++ } if v.RawHistory != nil { fields[i] = fmt.Sprintf("RawHistory: %v", v.RawHistory) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.Archived != nil { fields[i] = fmt.Sprintf("Archived: %v", *(v.Archived)) i++ } return fmt.Sprintf("GetWorkflowExecutionHistoryResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_DataBlob_Equals(lhs, rhs []*DataBlob) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this GetWorkflowExecutionHistoryResponse match the // provided GetWorkflowExecutionHistoryResponse. // // This function performs a deep comparison. func (v *GetWorkflowExecutionHistoryResponse) Equals(rhs *GetWorkflowExecutionHistoryResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.History == nil && rhs.History == nil) || (v.History != nil && rhs.History != nil && v.History.Equals(rhs.History))) { return false } if !((v.RawHistory == nil && rhs.RawHistory == nil) || (v.RawHistory != nil && rhs.RawHistory != nil && _List_DataBlob_Equals(v.RawHistory, rhs.RawHistory))) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !_Bool_EqualsPtr(v.Archived, rhs.Archived) { return false } return true } type _List_DataBlob_Zapper []*DataBlob // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_DataBlob_Zapper. func (l _List_DataBlob_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of GetWorkflowExecutionHistoryResponse. func (v *GetWorkflowExecutionHistoryResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.History != nil { err = multierr.Append(err, enc.AddObject("history", v.History)) } if v.RawHistory != nil { err = multierr.Append(err, enc.AddArray("rawHistory", (_List_DataBlob_Zapper)(v.RawHistory))) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.Archived != nil { enc.AddBool("archived", *v.Archived) } return err } // GetHistory returns the value of History if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryResponse) GetHistory() (o *History) { if v != nil && v.History != nil { return v.History } return } // IsSetHistory returns true if History is not nil. func (v *GetWorkflowExecutionHistoryResponse) IsSetHistory() bool { return v != nil && v.History != nil } // GetRawHistory returns the value of RawHistory if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryResponse) GetRawHistory() (o []*DataBlob) { if v != nil && v.RawHistory != nil { return v.RawHistory } return } // IsSetRawHistory returns true if RawHistory is not nil. func (v *GetWorkflowExecutionHistoryResponse) IsSetRawHistory() bool { return v != nil && v.RawHistory != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *GetWorkflowExecutionHistoryResponse) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetArchived returns the value of Archived if it is set or its // zero value if it is unset. func (v *GetWorkflowExecutionHistoryResponse) GetArchived() (o bool) { if v != nil && v.Archived != nil { return *v.Archived } return } // IsSetArchived returns true if Archived is not nil. func (v *GetWorkflowExecutionHistoryResponse) IsSetArchived() bool { return v != nil && v.Archived != nil } type Header struct { Fields map[string][]byte `json:"fields,omitempty"` } type _Map_String_Binary_MapItemList map[string][]byte func (m _Map_String_Binary_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string][]byte', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := wire.NewValueBinary(v), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_Binary_MapItemList) Size() int { return len(m) } func (_Map_String_Binary_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_Binary_MapItemList) ValueType() wire.Type { return wire.TBinary } func (_Map_String_Binary_MapItemList) Close() {} // ToWire translates a Header struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *Header) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Fields != nil { w, err = wire.NewValueMap(_Map_String_Binary_MapItemList(v.Fields)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Map_String_Binary_Read(m wire.MapItemList) (map[string][]byte, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TBinary { return nil, nil } o := make(map[string][]byte, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := x.Value.GetBinary(), error(nil) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a Header struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a Header struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v Header // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *Header) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.Fields, err = _Map_String_Binary_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_Binary_Encode(val map[string][]byte, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TBinary, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string][]byte', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := sw.WriteBinary(v); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a Header struct directly into bytes, without going // through an intermediary type. // // An error is returned if a Header struct could not be encoded. func (v *Header) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Fields != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_Binary_Encode(v.Fields, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _Map_String_Binary_Decode(sr stream.Reader) (map[string][]byte, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TBinary { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string][]byte, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := sr.ReadBinary() if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a Header struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a Header struct could not be generated from the wire // representation. func (v *Header) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.Fields, err = _Map_String_Binary_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a Header // struct. func (v *Header) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Fields != nil { fields[i] = fmt.Sprintf("Fields: %v", v.Fields) i++ } return fmt.Sprintf("Header{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_Binary_Equals(lhs, rhs map[string][]byte) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !bytes.Equal(lv, rv) { return false } } return true } // Equals returns true if all the fields of this Header match the // provided Header. // // This function performs a deep comparison. func (v *Header) Equals(rhs *Header) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Fields == nil && rhs.Fields == nil) || (v.Fields != nil && rhs.Fields != nil && _Map_String_Binary_Equals(v.Fields, rhs.Fields))) { return false } return true } type _Map_String_Binary_Zapper map[string][]byte // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_Binary_Zapper. func (m _Map_String_Binary_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { enc.AddString((string)(k), base64.StdEncoding.EncodeToString(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of Header. func (v *Header) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Fields != nil { err = multierr.Append(err, enc.AddObject("fields", (_Map_String_Binary_Zapper)(v.Fields))) } return err } // GetFields returns the value of Fields if it is set or its // zero value if it is unset. func (v *Header) GetFields() (o map[string][]byte) { if v != nil && v.Fields != nil { return v.Fields } return } // IsSetFields returns true if Fields is not nil. func (v *Header) IsSetFields() bool { return v != nil && v.Fields != nil } type History struct { Events []*HistoryEvent `json:"events,omitempty"` } type _List_HistoryEvent_ValueList []*HistoryEvent func (v _List_HistoryEvent_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*HistoryEvent', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_HistoryEvent_ValueList) Size() int { return len(v) } func (_List_HistoryEvent_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_HistoryEvent_ValueList) Close() {} // ToWire translates a History struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *History) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Events != nil { w, err = wire.NewValueList(_List_HistoryEvent_ValueList(v.Events)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _List_HistoryEvent_Read(l wire.ValueList) ([]*HistoryEvent, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*HistoryEvent, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _HistoryEvent_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a History struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a History struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v History // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *History) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Events, err = _List_HistoryEvent_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_HistoryEvent_Encode(val []*HistoryEvent, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*HistoryEvent', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a History struct directly into bytes, without going // through an intermediary type. // // An error is returned if a History struct could not be encoded. func (v *History) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Events != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_HistoryEvent_Encode(v.Events, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _List_HistoryEvent_Decode(sr stream.Reader) ([]*HistoryEvent, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*HistoryEvent, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _HistoryEvent_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a History struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a History struct could not be generated from the wire // representation. func (v *History) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Events, err = _List_HistoryEvent_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a History // struct. func (v *History) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Events != nil { fields[i] = fmt.Sprintf("Events: %v", v.Events) i++ } return fmt.Sprintf("History{%v}", strings.Join(fields[:i], ", ")) } func _List_HistoryEvent_Equals(lhs, rhs []*HistoryEvent) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this History match the // provided History. // // This function performs a deep comparison. func (v *History) Equals(rhs *History) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Events == nil && rhs.Events == nil) || (v.Events != nil && rhs.Events != nil && _List_HistoryEvent_Equals(v.Events, rhs.Events))) { return false } return true } type _List_HistoryEvent_Zapper []*HistoryEvent // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_HistoryEvent_Zapper. func (l _List_HistoryEvent_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of History. func (v *History) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Events != nil { err = multierr.Append(err, enc.AddArray("events", (_List_HistoryEvent_Zapper)(v.Events))) } return err } // GetEvents returns the value of Events if it is set or its // zero value if it is unset. func (v *History) GetEvents() (o []*HistoryEvent) { if v != nil && v.Events != nil { return v.Events } return } // IsSetEvents returns true if Events is not nil. func (v *History) IsSetEvents() bool { return v != nil && v.Events != nil } type HistoryBranch struct { TreeID *string `json:"treeID,omitempty"` BranchID *string `json:"branchID,omitempty"` Ancestors []*HistoryBranchRange `json:"ancestors,omitempty"` } type _List_HistoryBranchRange_ValueList []*HistoryBranchRange func (v _List_HistoryBranchRange_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*HistoryBranchRange', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_HistoryBranchRange_ValueList) Size() int { return len(v) } func (_List_HistoryBranchRange_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_HistoryBranchRange_ValueList) Close() {} // ToWire translates a HistoryBranch struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryBranch) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.TreeID != nil { w, err = wire.NewValueString(*(v.TreeID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.BranchID != nil { w, err = wire.NewValueString(*(v.BranchID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Ancestors != nil { w, err = wire.NewValueList(_List_HistoryBranchRange_ValueList(v.Ancestors)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _HistoryBranchRange_Read(w wire.Value) (*HistoryBranchRange, error) { var v HistoryBranchRange err := v.FromWire(w) return &v, err } func _List_HistoryBranchRange_Read(l wire.ValueList) ([]*HistoryBranchRange, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*HistoryBranchRange, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _HistoryBranchRange_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a HistoryBranch struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryBranch struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryBranch // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryBranch) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TreeID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BranchID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TList { v.Ancestors, err = _List_HistoryBranchRange_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_HistoryBranchRange_Encode(val []*HistoryBranchRange, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*HistoryBranchRange', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a HistoryBranch struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryBranch struct could not be encoded. func (v *HistoryBranch) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TreeID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TreeID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BranchID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BranchID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Ancestors != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TList}); err != nil { return err } if err := _List_HistoryBranchRange_Encode(v.Ancestors, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _HistoryBranchRange_Decode(sr stream.Reader) (*HistoryBranchRange, error) { var v HistoryBranchRange err := v.Decode(sr) return &v, err } func _List_HistoryBranchRange_Decode(sr stream.Reader) ([]*HistoryBranchRange, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*HistoryBranchRange, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _HistoryBranchRange_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a HistoryBranch struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryBranch struct could not be generated from the wire // representation. func (v *HistoryBranch) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TreeID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BranchID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TList: v.Ancestors, err = _List_HistoryBranchRange_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryBranch // struct. func (v *HistoryBranch) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.TreeID != nil { fields[i] = fmt.Sprintf("TreeID: %v", *(v.TreeID)) i++ } if v.BranchID != nil { fields[i] = fmt.Sprintf("BranchID: %v", *(v.BranchID)) i++ } if v.Ancestors != nil { fields[i] = fmt.Sprintf("Ancestors: %v", v.Ancestors) i++ } return fmt.Sprintf("HistoryBranch{%v}", strings.Join(fields[:i], ", ")) } func _List_HistoryBranchRange_Equals(lhs, rhs []*HistoryBranchRange) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this HistoryBranch match the // provided HistoryBranch. // // This function performs a deep comparison. func (v *HistoryBranch) Equals(rhs *HistoryBranch) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TreeID, rhs.TreeID) { return false } if !_String_EqualsPtr(v.BranchID, rhs.BranchID) { return false } if !((v.Ancestors == nil && rhs.Ancestors == nil) || (v.Ancestors != nil && rhs.Ancestors != nil && _List_HistoryBranchRange_Equals(v.Ancestors, rhs.Ancestors))) { return false } return true } type _List_HistoryBranchRange_Zapper []*HistoryBranchRange // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_HistoryBranchRange_Zapper. func (l _List_HistoryBranchRange_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryBranch. func (v *HistoryBranch) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TreeID != nil { enc.AddString("treeID", *v.TreeID) } if v.BranchID != nil { enc.AddString("branchID", *v.BranchID) } if v.Ancestors != nil { err = multierr.Append(err, enc.AddArray("ancestors", (_List_HistoryBranchRange_Zapper)(v.Ancestors))) } return err } // GetTreeID returns the value of TreeID if it is set or its // zero value if it is unset. func (v *HistoryBranch) GetTreeID() (o string) { if v != nil && v.TreeID != nil { return *v.TreeID } return } // IsSetTreeID returns true if TreeID is not nil. func (v *HistoryBranch) IsSetTreeID() bool { return v != nil && v.TreeID != nil } // GetBranchID returns the value of BranchID if it is set or its // zero value if it is unset. func (v *HistoryBranch) GetBranchID() (o string) { if v != nil && v.BranchID != nil { return *v.BranchID } return } // IsSetBranchID returns true if BranchID is not nil. func (v *HistoryBranch) IsSetBranchID() bool { return v != nil && v.BranchID != nil } // GetAncestors returns the value of Ancestors if it is set or its // zero value if it is unset. func (v *HistoryBranch) GetAncestors() (o []*HistoryBranchRange) { if v != nil && v.Ancestors != nil { return v.Ancestors } return } // IsSetAncestors returns true if Ancestors is not nil. func (v *HistoryBranch) IsSetAncestors() bool { return v != nil && v.Ancestors != nil } type HistoryBranchRange struct { BranchID *string `json:"branchID,omitempty"` BeginNodeID *int64 `json:"beginNodeID,omitempty"` EndNodeID *int64 `json:"endNodeID,omitempty"` } // ToWire translates a HistoryBranchRange struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryBranchRange) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.BranchID != nil { w, err = wire.NewValueString(*(v.BranchID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.BeginNodeID != nil { w, err = wire.NewValueI64(*(v.BeginNodeID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.EndNodeID != nil { w, err = wire.NewValueI64(*(v.EndNodeID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a HistoryBranchRange struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryBranchRange struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryBranchRange // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryBranchRange) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BranchID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.BeginNodeID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EndNodeID = &x if err != nil { return err } } } } return nil } // Encode serializes a HistoryBranchRange struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryBranchRange struct could not be encoded. func (v *HistoryBranchRange) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BranchID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BranchID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BeginNodeID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.BeginNodeID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EndNodeID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EndNodeID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a HistoryBranchRange struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryBranchRange struct could not be generated from the wire // representation. func (v *HistoryBranchRange) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BranchID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.BeginNodeID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EndNodeID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryBranchRange // struct. func (v *HistoryBranchRange) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.BranchID != nil { fields[i] = fmt.Sprintf("BranchID: %v", *(v.BranchID)) i++ } if v.BeginNodeID != nil { fields[i] = fmt.Sprintf("BeginNodeID: %v", *(v.BeginNodeID)) i++ } if v.EndNodeID != nil { fields[i] = fmt.Sprintf("EndNodeID: %v", *(v.EndNodeID)) i++ } return fmt.Sprintf("HistoryBranchRange{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this HistoryBranchRange match the // provided HistoryBranchRange. // // This function performs a deep comparison. func (v *HistoryBranchRange) Equals(rhs *HistoryBranchRange) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.BranchID, rhs.BranchID) { return false } if !_I64_EqualsPtr(v.BeginNodeID, rhs.BeginNodeID) { return false } if !_I64_EqualsPtr(v.EndNodeID, rhs.EndNodeID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryBranchRange. func (v *HistoryBranchRange) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BranchID != nil { enc.AddString("branchID", *v.BranchID) } if v.BeginNodeID != nil { enc.AddInt64("beginNodeID", *v.BeginNodeID) } if v.EndNodeID != nil { enc.AddInt64("endNodeID", *v.EndNodeID) } return err } // GetBranchID returns the value of BranchID if it is set or its // zero value if it is unset. func (v *HistoryBranchRange) GetBranchID() (o string) { if v != nil && v.BranchID != nil { return *v.BranchID } return } // IsSetBranchID returns true if BranchID is not nil. func (v *HistoryBranchRange) IsSetBranchID() bool { return v != nil && v.BranchID != nil } // GetBeginNodeID returns the value of BeginNodeID if it is set or its // zero value if it is unset. func (v *HistoryBranchRange) GetBeginNodeID() (o int64) { if v != nil && v.BeginNodeID != nil { return *v.BeginNodeID } return } // IsSetBeginNodeID returns true if BeginNodeID is not nil. func (v *HistoryBranchRange) IsSetBeginNodeID() bool { return v != nil && v.BeginNodeID != nil } // GetEndNodeID returns the value of EndNodeID if it is set or its // zero value if it is unset. func (v *HistoryBranchRange) GetEndNodeID() (o int64) { if v != nil && v.EndNodeID != nil { return *v.EndNodeID } return } // IsSetEndNodeID returns true if EndNodeID is not nil. func (v *HistoryBranchRange) IsSetEndNodeID() bool { return v != nil && v.EndNodeID != nil } type HistoryEvent struct { EventId *int64 `json:"eventId,omitempty"` Timestamp *int64 `json:"timestamp,omitempty"` EventType *EventType `json:"eventType,omitempty"` Version *int64 `json:"version,omitempty"` TaskId *int64 `json:"taskId,omitempty"` WorkflowExecutionStartedEventAttributes *WorkflowExecutionStartedEventAttributes `json:"workflowExecutionStartedEventAttributes,omitempty"` WorkflowExecutionCompletedEventAttributes *WorkflowExecutionCompletedEventAttributes `json:"workflowExecutionCompletedEventAttributes,omitempty"` WorkflowExecutionFailedEventAttributes *WorkflowExecutionFailedEventAttributes `json:"workflowExecutionFailedEventAttributes,omitempty"` WorkflowExecutionTimedOutEventAttributes *WorkflowExecutionTimedOutEventAttributes `json:"workflowExecutionTimedOutEventAttributes,omitempty"` DecisionTaskScheduledEventAttributes *DecisionTaskScheduledEventAttributes `json:"decisionTaskScheduledEventAttributes,omitempty"` DecisionTaskStartedEventAttributes *DecisionTaskStartedEventAttributes `json:"decisionTaskStartedEventAttributes,omitempty"` DecisionTaskCompletedEventAttributes *DecisionTaskCompletedEventAttributes `json:"decisionTaskCompletedEventAttributes,omitempty"` DecisionTaskTimedOutEventAttributes *DecisionTaskTimedOutEventAttributes `json:"decisionTaskTimedOutEventAttributes,omitempty"` DecisionTaskFailedEventAttributes *DecisionTaskFailedEventAttributes `json:"decisionTaskFailedEventAttributes,omitempty"` ActivityTaskScheduledEventAttributes *ActivityTaskScheduledEventAttributes `json:"activityTaskScheduledEventAttributes,omitempty"` ActivityTaskStartedEventAttributes *ActivityTaskStartedEventAttributes `json:"activityTaskStartedEventAttributes,omitempty"` ActivityTaskCompletedEventAttributes *ActivityTaskCompletedEventAttributes `json:"activityTaskCompletedEventAttributes,omitempty"` ActivityTaskFailedEventAttributes *ActivityTaskFailedEventAttributes `json:"activityTaskFailedEventAttributes,omitempty"` ActivityTaskTimedOutEventAttributes *ActivityTaskTimedOutEventAttributes `json:"activityTaskTimedOutEventAttributes,omitempty"` TimerStartedEventAttributes *TimerStartedEventAttributes `json:"timerStartedEventAttributes,omitempty"` TimerFiredEventAttributes *TimerFiredEventAttributes `json:"timerFiredEventAttributes,omitempty"` ActivityTaskCancelRequestedEventAttributes *ActivityTaskCancelRequestedEventAttributes `json:"activityTaskCancelRequestedEventAttributes,omitempty"` RequestCancelActivityTaskFailedEventAttributes *RequestCancelActivityTaskFailedEventAttributes `json:"requestCancelActivityTaskFailedEventAttributes,omitempty"` ActivityTaskCanceledEventAttributes *ActivityTaskCanceledEventAttributes `json:"activityTaskCanceledEventAttributes,omitempty"` TimerCanceledEventAttributes *TimerCanceledEventAttributes `json:"timerCanceledEventAttributes,omitempty"` CancelTimerFailedEventAttributes *CancelTimerFailedEventAttributes `json:"cancelTimerFailedEventAttributes,omitempty"` MarkerRecordedEventAttributes *MarkerRecordedEventAttributes `json:"markerRecordedEventAttributes,omitempty"` WorkflowExecutionSignaledEventAttributes *WorkflowExecutionSignaledEventAttributes `json:"workflowExecutionSignaledEventAttributes,omitempty"` WorkflowExecutionTerminatedEventAttributes *WorkflowExecutionTerminatedEventAttributes `json:"workflowExecutionTerminatedEventAttributes,omitempty"` WorkflowExecutionCancelRequestedEventAttributes *WorkflowExecutionCancelRequestedEventAttributes `json:"workflowExecutionCancelRequestedEventAttributes,omitempty"` WorkflowExecutionCanceledEventAttributes *WorkflowExecutionCanceledEventAttributes `json:"workflowExecutionCanceledEventAttributes,omitempty"` RequestCancelExternalWorkflowExecutionInitiatedEventAttributes *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes `json:"requestCancelExternalWorkflowExecutionInitiatedEventAttributes,omitempty"` RequestCancelExternalWorkflowExecutionFailedEventAttributes *RequestCancelExternalWorkflowExecutionFailedEventAttributes `json:"requestCancelExternalWorkflowExecutionFailedEventAttributes,omitempty"` ExternalWorkflowExecutionCancelRequestedEventAttributes *ExternalWorkflowExecutionCancelRequestedEventAttributes `json:"externalWorkflowExecutionCancelRequestedEventAttributes,omitempty"` WorkflowExecutionContinuedAsNewEventAttributes *WorkflowExecutionContinuedAsNewEventAttributes `json:"workflowExecutionContinuedAsNewEventAttributes,omitempty"` StartChildWorkflowExecutionInitiatedEventAttributes *StartChildWorkflowExecutionInitiatedEventAttributes `json:"startChildWorkflowExecutionInitiatedEventAttributes,omitempty"` StartChildWorkflowExecutionFailedEventAttributes *StartChildWorkflowExecutionFailedEventAttributes `json:"startChildWorkflowExecutionFailedEventAttributes,omitempty"` ChildWorkflowExecutionStartedEventAttributes *ChildWorkflowExecutionStartedEventAttributes `json:"childWorkflowExecutionStartedEventAttributes,omitempty"` ChildWorkflowExecutionCompletedEventAttributes *ChildWorkflowExecutionCompletedEventAttributes `json:"childWorkflowExecutionCompletedEventAttributes,omitempty"` ChildWorkflowExecutionFailedEventAttributes *ChildWorkflowExecutionFailedEventAttributes `json:"childWorkflowExecutionFailedEventAttributes,omitempty"` ChildWorkflowExecutionCanceledEventAttributes *ChildWorkflowExecutionCanceledEventAttributes `json:"childWorkflowExecutionCanceledEventAttributes,omitempty"` ChildWorkflowExecutionTimedOutEventAttributes *ChildWorkflowExecutionTimedOutEventAttributes `json:"childWorkflowExecutionTimedOutEventAttributes,omitempty"` ChildWorkflowExecutionTerminatedEventAttributes *ChildWorkflowExecutionTerminatedEventAttributes `json:"childWorkflowExecutionTerminatedEventAttributes,omitempty"` SignalExternalWorkflowExecutionInitiatedEventAttributes *SignalExternalWorkflowExecutionInitiatedEventAttributes `json:"signalExternalWorkflowExecutionInitiatedEventAttributes,omitempty"` SignalExternalWorkflowExecutionFailedEventAttributes *SignalExternalWorkflowExecutionFailedEventAttributes `json:"signalExternalWorkflowExecutionFailedEventAttributes,omitempty"` ExternalWorkflowExecutionSignaledEventAttributes *ExternalWorkflowExecutionSignaledEventAttributes `json:"externalWorkflowExecutionSignaledEventAttributes,omitempty"` UpsertWorkflowSearchAttributesEventAttributes *UpsertWorkflowSearchAttributesEventAttributes `json:"upsertWorkflowSearchAttributesEventAttributes,omitempty"` } // ToWire translates a HistoryEvent struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryEvent) ToWire() (wire.Value, error) { var ( fields [47]wire.Field i int = 0 w wire.Value err error ) if v.EventId != nil { w, err = wire.NewValueI64(*(v.EventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Timestamp != nil { w, err = wire.NewValueI64(*(v.Timestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.EventType != nil { w, err = v.EventType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 35, Value: w} i++ } if v.TaskId != nil { w, err = wire.NewValueI64(*(v.TaskId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 36, Value: w} i++ } if v.WorkflowExecutionStartedEventAttributes != nil { w, err = v.WorkflowExecutionStartedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.WorkflowExecutionCompletedEventAttributes != nil { w, err = v.WorkflowExecutionCompletedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.WorkflowExecutionFailedEventAttributes != nil { w, err = v.WorkflowExecutionFailedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.WorkflowExecutionTimedOutEventAttributes != nil { w, err = v.WorkflowExecutionTimedOutEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.DecisionTaskScheduledEventAttributes != nil { w, err = v.DecisionTaskScheduledEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.DecisionTaskStartedEventAttributes != nil { w, err = v.DecisionTaskStartedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.DecisionTaskCompletedEventAttributes != nil { w, err = v.DecisionTaskCompletedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.DecisionTaskTimedOutEventAttributes != nil { w, err = v.DecisionTaskTimedOutEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.DecisionTaskFailedEventAttributes != nil { w, err = v.DecisionTaskFailedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.ActivityTaskScheduledEventAttributes != nil { w, err = v.ActivityTaskScheduledEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.ActivityTaskStartedEventAttributes != nil { w, err = v.ActivityTaskStartedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.ActivityTaskCompletedEventAttributes != nil { w, err = v.ActivityTaskCompletedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.ActivityTaskFailedEventAttributes != nil { w, err = v.ActivityTaskFailedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.ActivityTaskTimedOutEventAttributes != nil { w, err = v.ActivityTaskTimedOutEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } if v.TimerStartedEventAttributes != nil { w, err = v.TimerStartedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 180, Value: w} i++ } if v.TimerFiredEventAttributes != nil { w, err = v.TimerFiredEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 190, Value: w} i++ } if v.ActivityTaskCancelRequestedEventAttributes != nil { w, err = v.ActivityTaskCancelRequestedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 200, Value: w} i++ } if v.RequestCancelActivityTaskFailedEventAttributes != nil { w, err = v.RequestCancelActivityTaskFailedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 210, Value: w} i++ } if v.ActivityTaskCanceledEventAttributes != nil { w, err = v.ActivityTaskCanceledEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 220, Value: w} i++ } if v.TimerCanceledEventAttributes != nil { w, err = v.TimerCanceledEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 230, Value: w} i++ } if v.CancelTimerFailedEventAttributes != nil { w, err = v.CancelTimerFailedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 240, Value: w} i++ } if v.MarkerRecordedEventAttributes != nil { w, err = v.MarkerRecordedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 250, Value: w} i++ } if v.WorkflowExecutionSignaledEventAttributes != nil { w, err = v.WorkflowExecutionSignaledEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 260, Value: w} i++ } if v.WorkflowExecutionTerminatedEventAttributes != nil { w, err = v.WorkflowExecutionTerminatedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 270, Value: w} i++ } if v.WorkflowExecutionCancelRequestedEventAttributes != nil { w, err = v.WorkflowExecutionCancelRequestedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 280, Value: w} i++ } if v.WorkflowExecutionCanceledEventAttributes != nil { w, err = v.WorkflowExecutionCanceledEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 290, Value: w} i++ } if v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes != nil { w, err = v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 300, Value: w} i++ } if v.RequestCancelExternalWorkflowExecutionFailedEventAttributes != nil { w, err = v.RequestCancelExternalWorkflowExecutionFailedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 310, Value: w} i++ } if v.ExternalWorkflowExecutionCancelRequestedEventAttributes != nil { w, err = v.ExternalWorkflowExecutionCancelRequestedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 320, Value: w} i++ } if v.WorkflowExecutionContinuedAsNewEventAttributes != nil { w, err = v.WorkflowExecutionContinuedAsNewEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 330, Value: w} i++ } if v.StartChildWorkflowExecutionInitiatedEventAttributes != nil { w, err = v.StartChildWorkflowExecutionInitiatedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 340, Value: w} i++ } if v.StartChildWorkflowExecutionFailedEventAttributes != nil { w, err = v.StartChildWorkflowExecutionFailedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 350, Value: w} i++ } if v.ChildWorkflowExecutionStartedEventAttributes != nil { w, err = v.ChildWorkflowExecutionStartedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 360, Value: w} i++ } if v.ChildWorkflowExecutionCompletedEventAttributes != nil { w, err = v.ChildWorkflowExecutionCompletedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 370, Value: w} i++ } if v.ChildWorkflowExecutionFailedEventAttributes != nil { w, err = v.ChildWorkflowExecutionFailedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 380, Value: w} i++ } if v.ChildWorkflowExecutionCanceledEventAttributes != nil { w, err = v.ChildWorkflowExecutionCanceledEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 390, Value: w} i++ } if v.ChildWorkflowExecutionTimedOutEventAttributes != nil { w, err = v.ChildWorkflowExecutionTimedOutEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 400, Value: w} i++ } if v.ChildWorkflowExecutionTerminatedEventAttributes != nil { w, err = v.ChildWorkflowExecutionTerminatedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 410, Value: w} i++ } if v.SignalExternalWorkflowExecutionInitiatedEventAttributes != nil { w, err = v.SignalExternalWorkflowExecutionInitiatedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 420, Value: w} i++ } if v.SignalExternalWorkflowExecutionFailedEventAttributes != nil { w, err = v.SignalExternalWorkflowExecutionFailedEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 430, Value: w} i++ } if v.ExternalWorkflowExecutionSignaledEventAttributes != nil { w, err = v.ExternalWorkflowExecutionSignaledEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 440, Value: w} i++ } if v.UpsertWorkflowSearchAttributesEventAttributes != nil { w, err = v.UpsertWorkflowSearchAttributesEventAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 450, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _EventType_Read(w wire.Value) (EventType, error) { var v EventType err := v.FromWire(w) return v, err } func _WorkflowExecutionStartedEventAttributes_Read(w wire.Value) (*WorkflowExecutionStartedEventAttributes, error) { var v WorkflowExecutionStartedEventAttributes err := v.FromWire(w) return &v, err } func _WorkflowExecutionCompletedEventAttributes_Read(w wire.Value) (*WorkflowExecutionCompletedEventAttributes, error) { var v WorkflowExecutionCompletedEventAttributes err := v.FromWire(w) return &v, err } func _WorkflowExecutionFailedEventAttributes_Read(w wire.Value) (*WorkflowExecutionFailedEventAttributes, error) { var v WorkflowExecutionFailedEventAttributes err := v.FromWire(w) return &v, err } func _WorkflowExecutionTimedOutEventAttributes_Read(w wire.Value) (*WorkflowExecutionTimedOutEventAttributes, error) { var v WorkflowExecutionTimedOutEventAttributes err := v.FromWire(w) return &v, err } func _DecisionTaskScheduledEventAttributes_Read(w wire.Value) (*DecisionTaskScheduledEventAttributes, error) { var v DecisionTaskScheduledEventAttributes err := v.FromWire(w) return &v, err } func _DecisionTaskStartedEventAttributes_Read(w wire.Value) (*DecisionTaskStartedEventAttributes, error) { var v DecisionTaskStartedEventAttributes err := v.FromWire(w) return &v, err } func _DecisionTaskCompletedEventAttributes_Read(w wire.Value) (*DecisionTaskCompletedEventAttributes, error) { var v DecisionTaskCompletedEventAttributes err := v.FromWire(w) return &v, err } func _DecisionTaskTimedOutEventAttributes_Read(w wire.Value) (*DecisionTaskTimedOutEventAttributes, error) { var v DecisionTaskTimedOutEventAttributes err := v.FromWire(w) return &v, err } func _DecisionTaskFailedEventAttributes_Read(w wire.Value) (*DecisionTaskFailedEventAttributes, error) { var v DecisionTaskFailedEventAttributes err := v.FromWire(w) return &v, err } func _ActivityTaskScheduledEventAttributes_Read(w wire.Value) (*ActivityTaskScheduledEventAttributes, error) { var v ActivityTaskScheduledEventAttributes err := v.FromWire(w) return &v, err } func _ActivityTaskStartedEventAttributes_Read(w wire.Value) (*ActivityTaskStartedEventAttributes, error) { var v ActivityTaskStartedEventAttributes err := v.FromWire(w) return &v, err } func _ActivityTaskCompletedEventAttributes_Read(w wire.Value) (*ActivityTaskCompletedEventAttributes, error) { var v ActivityTaskCompletedEventAttributes err := v.FromWire(w) return &v, err } func _ActivityTaskFailedEventAttributes_Read(w wire.Value) (*ActivityTaskFailedEventAttributes, error) { var v ActivityTaskFailedEventAttributes err := v.FromWire(w) return &v, err } func _ActivityTaskTimedOutEventAttributes_Read(w wire.Value) (*ActivityTaskTimedOutEventAttributes, error) { var v ActivityTaskTimedOutEventAttributes err := v.FromWire(w) return &v, err } func _TimerStartedEventAttributes_Read(w wire.Value) (*TimerStartedEventAttributes, error) { var v TimerStartedEventAttributes err := v.FromWire(w) return &v, err } func _TimerFiredEventAttributes_Read(w wire.Value) (*TimerFiredEventAttributes, error) { var v TimerFiredEventAttributes err := v.FromWire(w) return &v, err } func _ActivityTaskCancelRequestedEventAttributes_Read(w wire.Value) (*ActivityTaskCancelRequestedEventAttributes, error) { var v ActivityTaskCancelRequestedEventAttributes err := v.FromWire(w) return &v, err } func _RequestCancelActivityTaskFailedEventAttributes_Read(w wire.Value) (*RequestCancelActivityTaskFailedEventAttributes, error) { var v RequestCancelActivityTaskFailedEventAttributes err := v.FromWire(w) return &v, err } func _ActivityTaskCanceledEventAttributes_Read(w wire.Value) (*ActivityTaskCanceledEventAttributes, error) { var v ActivityTaskCanceledEventAttributes err := v.FromWire(w) return &v, err } func _TimerCanceledEventAttributes_Read(w wire.Value) (*TimerCanceledEventAttributes, error) { var v TimerCanceledEventAttributes err := v.FromWire(w) return &v, err } func _CancelTimerFailedEventAttributes_Read(w wire.Value) (*CancelTimerFailedEventAttributes, error) { var v CancelTimerFailedEventAttributes err := v.FromWire(w) return &v, err } func _MarkerRecordedEventAttributes_Read(w wire.Value) (*MarkerRecordedEventAttributes, error) { var v MarkerRecordedEventAttributes err := v.FromWire(w) return &v, err } func _WorkflowExecutionSignaledEventAttributes_Read(w wire.Value) (*WorkflowExecutionSignaledEventAttributes, error) { var v WorkflowExecutionSignaledEventAttributes err := v.FromWire(w) return &v, err } func _WorkflowExecutionTerminatedEventAttributes_Read(w wire.Value) (*WorkflowExecutionTerminatedEventAttributes, error) { var v WorkflowExecutionTerminatedEventAttributes err := v.FromWire(w) return &v, err } func _WorkflowExecutionCancelRequestedEventAttributes_Read(w wire.Value) (*WorkflowExecutionCancelRequestedEventAttributes, error) { var v WorkflowExecutionCancelRequestedEventAttributes err := v.FromWire(w) return &v, err } func _WorkflowExecutionCanceledEventAttributes_Read(w wire.Value) (*WorkflowExecutionCanceledEventAttributes, error) { var v WorkflowExecutionCanceledEventAttributes err := v.FromWire(w) return &v, err } func _RequestCancelExternalWorkflowExecutionInitiatedEventAttributes_Read(w wire.Value) (*RequestCancelExternalWorkflowExecutionInitiatedEventAttributes, error) { var v RequestCancelExternalWorkflowExecutionInitiatedEventAttributes err := v.FromWire(w) return &v, err } func _RequestCancelExternalWorkflowExecutionFailedEventAttributes_Read(w wire.Value) (*RequestCancelExternalWorkflowExecutionFailedEventAttributes, error) { var v RequestCancelExternalWorkflowExecutionFailedEventAttributes err := v.FromWire(w) return &v, err } func _ExternalWorkflowExecutionCancelRequestedEventAttributes_Read(w wire.Value) (*ExternalWorkflowExecutionCancelRequestedEventAttributes, error) { var v ExternalWorkflowExecutionCancelRequestedEventAttributes err := v.FromWire(w) return &v, err } func _WorkflowExecutionContinuedAsNewEventAttributes_Read(w wire.Value) (*WorkflowExecutionContinuedAsNewEventAttributes, error) { var v WorkflowExecutionContinuedAsNewEventAttributes err := v.FromWire(w) return &v, err } func _StartChildWorkflowExecutionFailedEventAttributes_Read(w wire.Value) (*StartChildWorkflowExecutionFailedEventAttributes, error) { var v StartChildWorkflowExecutionFailedEventAttributes err := v.FromWire(w) return &v, err } func _ChildWorkflowExecutionStartedEventAttributes_Read(w wire.Value) (*ChildWorkflowExecutionStartedEventAttributes, error) { var v ChildWorkflowExecutionStartedEventAttributes err := v.FromWire(w) return &v, err } func _ChildWorkflowExecutionCompletedEventAttributes_Read(w wire.Value) (*ChildWorkflowExecutionCompletedEventAttributes, error) { var v ChildWorkflowExecutionCompletedEventAttributes err := v.FromWire(w) return &v, err } func _ChildWorkflowExecutionFailedEventAttributes_Read(w wire.Value) (*ChildWorkflowExecutionFailedEventAttributes, error) { var v ChildWorkflowExecutionFailedEventAttributes err := v.FromWire(w) return &v, err } func _ChildWorkflowExecutionCanceledEventAttributes_Read(w wire.Value) (*ChildWorkflowExecutionCanceledEventAttributes, error) { var v ChildWorkflowExecutionCanceledEventAttributes err := v.FromWire(w) return &v, err } func _ChildWorkflowExecutionTimedOutEventAttributes_Read(w wire.Value) (*ChildWorkflowExecutionTimedOutEventAttributes, error) { var v ChildWorkflowExecutionTimedOutEventAttributes err := v.FromWire(w) return &v, err } func _ChildWorkflowExecutionTerminatedEventAttributes_Read(w wire.Value) (*ChildWorkflowExecutionTerminatedEventAttributes, error) { var v ChildWorkflowExecutionTerminatedEventAttributes err := v.FromWire(w) return &v, err } func _SignalExternalWorkflowExecutionInitiatedEventAttributes_Read(w wire.Value) (*SignalExternalWorkflowExecutionInitiatedEventAttributes, error) { var v SignalExternalWorkflowExecutionInitiatedEventAttributes err := v.FromWire(w) return &v, err } func _SignalExternalWorkflowExecutionFailedEventAttributes_Read(w wire.Value) (*SignalExternalWorkflowExecutionFailedEventAttributes, error) { var v SignalExternalWorkflowExecutionFailedEventAttributes err := v.FromWire(w) return &v, err } func _ExternalWorkflowExecutionSignaledEventAttributes_Read(w wire.Value) (*ExternalWorkflowExecutionSignaledEventAttributes, error) { var v ExternalWorkflowExecutionSignaledEventAttributes err := v.FromWire(w) return &v, err } func _UpsertWorkflowSearchAttributesEventAttributes_Read(w wire.Value) (*UpsertWorkflowSearchAttributesEventAttributes, error) { var v UpsertWorkflowSearchAttributesEventAttributes err := v.FromWire(w) return &v, err } // FromWire deserializes a HistoryEvent struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryEvent struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryEvent // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryEvent) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Timestamp = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x EventType x, err = _EventType_Read(field.Value) v.EventType = &x if err != nil { return err } } case 35: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 36: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionStartedEventAttributes, err = _WorkflowExecutionStartedEventAttributes_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionCompletedEventAttributes, err = _WorkflowExecutionCompletedEventAttributes_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionFailedEventAttributes, err = _WorkflowExecutionFailedEventAttributes_Read(field.Value) if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionTimedOutEventAttributes, err = _WorkflowExecutionTimedOutEventAttributes_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TStruct { v.DecisionTaskScheduledEventAttributes, err = _DecisionTaskScheduledEventAttributes_Read(field.Value) if err != nil { return err } } case 90: if field.Value.Type() == wire.TStruct { v.DecisionTaskStartedEventAttributes, err = _DecisionTaskStartedEventAttributes_Read(field.Value) if err != nil { return err } } case 100: if field.Value.Type() == wire.TStruct { v.DecisionTaskCompletedEventAttributes, err = _DecisionTaskCompletedEventAttributes_Read(field.Value) if err != nil { return err } } case 110: if field.Value.Type() == wire.TStruct { v.DecisionTaskTimedOutEventAttributes, err = _DecisionTaskTimedOutEventAttributes_Read(field.Value) if err != nil { return err } } case 120: if field.Value.Type() == wire.TStruct { v.DecisionTaskFailedEventAttributes, err = _DecisionTaskFailedEventAttributes_Read(field.Value) if err != nil { return err } } case 130: if field.Value.Type() == wire.TStruct { v.ActivityTaskScheduledEventAttributes, err = _ActivityTaskScheduledEventAttributes_Read(field.Value) if err != nil { return err } } case 140: if field.Value.Type() == wire.TStruct { v.ActivityTaskStartedEventAttributes, err = _ActivityTaskStartedEventAttributes_Read(field.Value) if err != nil { return err } } case 150: if field.Value.Type() == wire.TStruct { v.ActivityTaskCompletedEventAttributes, err = _ActivityTaskCompletedEventAttributes_Read(field.Value) if err != nil { return err } } case 160: if field.Value.Type() == wire.TStruct { v.ActivityTaskFailedEventAttributes, err = _ActivityTaskFailedEventAttributes_Read(field.Value) if err != nil { return err } } case 170: if field.Value.Type() == wire.TStruct { v.ActivityTaskTimedOutEventAttributes, err = _ActivityTaskTimedOutEventAttributes_Read(field.Value) if err != nil { return err } } case 180: if field.Value.Type() == wire.TStruct { v.TimerStartedEventAttributes, err = _TimerStartedEventAttributes_Read(field.Value) if err != nil { return err } } case 190: if field.Value.Type() == wire.TStruct { v.TimerFiredEventAttributes, err = _TimerFiredEventAttributes_Read(field.Value) if err != nil { return err } } case 200: if field.Value.Type() == wire.TStruct { v.ActivityTaskCancelRequestedEventAttributes, err = _ActivityTaskCancelRequestedEventAttributes_Read(field.Value) if err != nil { return err } } case 210: if field.Value.Type() == wire.TStruct { v.RequestCancelActivityTaskFailedEventAttributes, err = _RequestCancelActivityTaskFailedEventAttributes_Read(field.Value) if err != nil { return err } } case 220: if field.Value.Type() == wire.TStruct { v.ActivityTaskCanceledEventAttributes, err = _ActivityTaskCanceledEventAttributes_Read(field.Value) if err != nil { return err } } case 230: if field.Value.Type() == wire.TStruct { v.TimerCanceledEventAttributes, err = _TimerCanceledEventAttributes_Read(field.Value) if err != nil { return err } } case 240: if field.Value.Type() == wire.TStruct { v.CancelTimerFailedEventAttributes, err = _CancelTimerFailedEventAttributes_Read(field.Value) if err != nil { return err } } case 250: if field.Value.Type() == wire.TStruct { v.MarkerRecordedEventAttributes, err = _MarkerRecordedEventAttributes_Read(field.Value) if err != nil { return err } } case 260: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionSignaledEventAttributes, err = _WorkflowExecutionSignaledEventAttributes_Read(field.Value) if err != nil { return err } } case 270: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionTerminatedEventAttributes, err = _WorkflowExecutionTerminatedEventAttributes_Read(field.Value) if err != nil { return err } } case 280: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionCancelRequestedEventAttributes, err = _WorkflowExecutionCancelRequestedEventAttributes_Read(field.Value) if err != nil { return err } } case 290: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionCanceledEventAttributes, err = _WorkflowExecutionCanceledEventAttributes_Read(field.Value) if err != nil { return err } } case 300: if field.Value.Type() == wire.TStruct { v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes, err = _RequestCancelExternalWorkflowExecutionInitiatedEventAttributes_Read(field.Value) if err != nil { return err } } case 310: if field.Value.Type() == wire.TStruct { v.RequestCancelExternalWorkflowExecutionFailedEventAttributes, err = _RequestCancelExternalWorkflowExecutionFailedEventAttributes_Read(field.Value) if err != nil { return err } } case 320: if field.Value.Type() == wire.TStruct { v.ExternalWorkflowExecutionCancelRequestedEventAttributes, err = _ExternalWorkflowExecutionCancelRequestedEventAttributes_Read(field.Value) if err != nil { return err } } case 330: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionContinuedAsNewEventAttributes, err = _WorkflowExecutionContinuedAsNewEventAttributes_Read(field.Value) if err != nil { return err } } case 340: if field.Value.Type() == wire.TStruct { v.StartChildWorkflowExecutionInitiatedEventAttributes, err = _StartChildWorkflowExecutionInitiatedEventAttributes_Read(field.Value) if err != nil { return err } } case 350: if field.Value.Type() == wire.TStruct { v.StartChildWorkflowExecutionFailedEventAttributes, err = _StartChildWorkflowExecutionFailedEventAttributes_Read(field.Value) if err != nil { return err } } case 360: if field.Value.Type() == wire.TStruct { v.ChildWorkflowExecutionStartedEventAttributes, err = _ChildWorkflowExecutionStartedEventAttributes_Read(field.Value) if err != nil { return err } } case 370: if field.Value.Type() == wire.TStruct { v.ChildWorkflowExecutionCompletedEventAttributes, err = _ChildWorkflowExecutionCompletedEventAttributes_Read(field.Value) if err != nil { return err } } case 380: if field.Value.Type() == wire.TStruct { v.ChildWorkflowExecutionFailedEventAttributes, err = _ChildWorkflowExecutionFailedEventAttributes_Read(field.Value) if err != nil { return err } } case 390: if field.Value.Type() == wire.TStruct { v.ChildWorkflowExecutionCanceledEventAttributes, err = _ChildWorkflowExecutionCanceledEventAttributes_Read(field.Value) if err != nil { return err } } case 400: if field.Value.Type() == wire.TStruct { v.ChildWorkflowExecutionTimedOutEventAttributes, err = _ChildWorkflowExecutionTimedOutEventAttributes_Read(field.Value) if err != nil { return err } } case 410: if field.Value.Type() == wire.TStruct { v.ChildWorkflowExecutionTerminatedEventAttributes, err = _ChildWorkflowExecutionTerminatedEventAttributes_Read(field.Value) if err != nil { return err } } case 420: if field.Value.Type() == wire.TStruct { v.SignalExternalWorkflowExecutionInitiatedEventAttributes, err = _SignalExternalWorkflowExecutionInitiatedEventAttributes_Read(field.Value) if err != nil { return err } } case 430: if field.Value.Type() == wire.TStruct { v.SignalExternalWorkflowExecutionFailedEventAttributes, err = _SignalExternalWorkflowExecutionFailedEventAttributes_Read(field.Value) if err != nil { return err } } case 440: if field.Value.Type() == wire.TStruct { v.ExternalWorkflowExecutionSignaledEventAttributes, err = _ExternalWorkflowExecutionSignaledEventAttributes_Read(field.Value) if err != nil { return err } } case 450: if field.Value.Type() == wire.TStruct { v.UpsertWorkflowSearchAttributesEventAttributes, err = _UpsertWorkflowSearchAttributesEventAttributes_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a HistoryEvent struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryEvent struct could not be encoded. func (v *HistoryEvent) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.EventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Timestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Timestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EventType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := v.EventType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 35, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 36, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionStartedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionStartedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionCompletedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionCompletedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionFailedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionFailedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionTimedOutEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionTimedOutEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskScheduledEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TStruct}); err != nil { return err } if err := v.DecisionTaskScheduledEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskStartedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TStruct}); err != nil { return err } if err := v.DecisionTaskStartedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TStruct}); err != nil { return err } if err := v.DecisionTaskCompletedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskTimedOutEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TStruct}); err != nil { return err } if err := v.DecisionTaskTimedOutEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskFailedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TStruct}); err != nil { return err } if err := v.DecisionTaskFailedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityTaskScheduledEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityTaskScheduledEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityTaskStartedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityTaskStartedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityTaskCompletedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityTaskCompletedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityTaskFailedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityTaskFailedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityTaskTimedOutEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityTaskTimedOutEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimerStartedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 180, Type: wire.TStruct}); err != nil { return err } if err := v.TimerStartedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimerFiredEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 190, Type: wire.TStruct}); err != nil { return err } if err := v.TimerFiredEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityTaskCancelRequestedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 200, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityTaskCancelRequestedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestCancelActivityTaskFailedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 210, Type: wire.TStruct}); err != nil { return err } if err := v.RequestCancelActivityTaskFailedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityTaskCanceledEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 220, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityTaskCanceledEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimerCanceledEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 230, Type: wire.TStruct}); err != nil { return err } if err := v.TimerCanceledEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelTimerFailedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 240, Type: wire.TStruct}); err != nil { return err } if err := v.CancelTimerFailedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MarkerRecordedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 250, Type: wire.TStruct}); err != nil { return err } if err := v.MarkerRecordedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionSignaledEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 260, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionSignaledEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionTerminatedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 270, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionTerminatedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionCancelRequestedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 280, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionCancelRequestedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionCanceledEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 290, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionCanceledEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 300, Type: wire.TStruct}); err != nil { return err } if err := v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestCancelExternalWorkflowExecutionFailedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 310, Type: wire.TStruct}); err != nil { return err } if err := v.RequestCancelExternalWorkflowExecutionFailedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExternalWorkflowExecutionCancelRequestedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 320, Type: wire.TStruct}); err != nil { return err } if err := v.ExternalWorkflowExecutionCancelRequestedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionContinuedAsNewEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 330, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionContinuedAsNewEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartChildWorkflowExecutionInitiatedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 340, Type: wire.TStruct}); err != nil { return err } if err := v.StartChildWorkflowExecutionInitiatedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartChildWorkflowExecutionFailedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 350, Type: wire.TStruct}); err != nil { return err } if err := v.StartChildWorkflowExecutionFailedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowExecutionStartedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 360, Type: wire.TStruct}); err != nil { return err } if err := v.ChildWorkflowExecutionStartedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowExecutionCompletedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 370, Type: wire.TStruct}); err != nil { return err } if err := v.ChildWorkflowExecutionCompletedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowExecutionFailedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 380, Type: wire.TStruct}); err != nil { return err } if err := v.ChildWorkflowExecutionFailedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowExecutionCanceledEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 390, Type: wire.TStruct}); err != nil { return err } if err := v.ChildWorkflowExecutionCanceledEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowExecutionTimedOutEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 400, Type: wire.TStruct}); err != nil { return err } if err := v.ChildWorkflowExecutionTimedOutEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowExecutionTerminatedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 410, Type: wire.TStruct}); err != nil { return err } if err := v.ChildWorkflowExecutionTerminatedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalExternalWorkflowExecutionInitiatedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 420, Type: wire.TStruct}); err != nil { return err } if err := v.SignalExternalWorkflowExecutionInitiatedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalExternalWorkflowExecutionFailedEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 430, Type: wire.TStruct}); err != nil { return err } if err := v.SignalExternalWorkflowExecutionFailedEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExternalWorkflowExecutionSignaledEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 440, Type: wire.TStruct}); err != nil { return err } if err := v.ExternalWorkflowExecutionSignaledEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.UpsertWorkflowSearchAttributesEventAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 450, Type: wire.TStruct}); err != nil { return err } if err := v.UpsertWorkflowSearchAttributesEventAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _EventType_Decode(sr stream.Reader) (EventType, error) { var v EventType err := v.Decode(sr) return v, err } func _WorkflowExecutionStartedEventAttributes_Decode(sr stream.Reader) (*WorkflowExecutionStartedEventAttributes, error) { var v WorkflowExecutionStartedEventAttributes err := v.Decode(sr) return &v, err } func _WorkflowExecutionCompletedEventAttributes_Decode(sr stream.Reader) (*WorkflowExecutionCompletedEventAttributes, error) { var v WorkflowExecutionCompletedEventAttributes err := v.Decode(sr) return &v, err } func _WorkflowExecutionFailedEventAttributes_Decode(sr stream.Reader) (*WorkflowExecutionFailedEventAttributes, error) { var v WorkflowExecutionFailedEventAttributes err := v.Decode(sr) return &v, err } func _WorkflowExecutionTimedOutEventAttributes_Decode(sr stream.Reader) (*WorkflowExecutionTimedOutEventAttributes, error) { var v WorkflowExecutionTimedOutEventAttributes err := v.Decode(sr) return &v, err } func _DecisionTaskScheduledEventAttributes_Decode(sr stream.Reader) (*DecisionTaskScheduledEventAttributes, error) { var v DecisionTaskScheduledEventAttributes err := v.Decode(sr) return &v, err } func _DecisionTaskStartedEventAttributes_Decode(sr stream.Reader) (*DecisionTaskStartedEventAttributes, error) { var v DecisionTaskStartedEventAttributes err := v.Decode(sr) return &v, err } func _DecisionTaskCompletedEventAttributes_Decode(sr stream.Reader) (*DecisionTaskCompletedEventAttributes, error) { var v DecisionTaskCompletedEventAttributes err := v.Decode(sr) return &v, err } func _DecisionTaskTimedOutEventAttributes_Decode(sr stream.Reader) (*DecisionTaskTimedOutEventAttributes, error) { var v DecisionTaskTimedOutEventAttributes err := v.Decode(sr) return &v, err } func _DecisionTaskFailedEventAttributes_Decode(sr stream.Reader) (*DecisionTaskFailedEventAttributes, error) { var v DecisionTaskFailedEventAttributes err := v.Decode(sr) return &v, err } func _ActivityTaskScheduledEventAttributes_Decode(sr stream.Reader) (*ActivityTaskScheduledEventAttributes, error) { var v ActivityTaskScheduledEventAttributes err := v.Decode(sr) return &v, err } func _ActivityTaskStartedEventAttributes_Decode(sr stream.Reader) (*ActivityTaskStartedEventAttributes, error) { var v ActivityTaskStartedEventAttributes err := v.Decode(sr) return &v, err } func _ActivityTaskCompletedEventAttributes_Decode(sr stream.Reader) (*ActivityTaskCompletedEventAttributes, error) { var v ActivityTaskCompletedEventAttributes err := v.Decode(sr) return &v, err } func _ActivityTaskFailedEventAttributes_Decode(sr stream.Reader) (*ActivityTaskFailedEventAttributes, error) { var v ActivityTaskFailedEventAttributes err := v.Decode(sr) return &v, err } func _ActivityTaskTimedOutEventAttributes_Decode(sr stream.Reader) (*ActivityTaskTimedOutEventAttributes, error) { var v ActivityTaskTimedOutEventAttributes err := v.Decode(sr) return &v, err } func _TimerStartedEventAttributes_Decode(sr stream.Reader) (*TimerStartedEventAttributes, error) { var v TimerStartedEventAttributes err := v.Decode(sr) return &v, err } func _TimerFiredEventAttributes_Decode(sr stream.Reader) (*TimerFiredEventAttributes, error) { var v TimerFiredEventAttributes err := v.Decode(sr) return &v, err } func _ActivityTaskCancelRequestedEventAttributes_Decode(sr stream.Reader) (*ActivityTaskCancelRequestedEventAttributes, error) { var v ActivityTaskCancelRequestedEventAttributes err := v.Decode(sr) return &v, err } func _RequestCancelActivityTaskFailedEventAttributes_Decode(sr stream.Reader) (*RequestCancelActivityTaskFailedEventAttributes, error) { var v RequestCancelActivityTaskFailedEventAttributes err := v.Decode(sr) return &v, err } func _ActivityTaskCanceledEventAttributes_Decode(sr stream.Reader) (*ActivityTaskCanceledEventAttributes, error) { var v ActivityTaskCanceledEventAttributes err := v.Decode(sr) return &v, err } func _TimerCanceledEventAttributes_Decode(sr stream.Reader) (*TimerCanceledEventAttributes, error) { var v TimerCanceledEventAttributes err := v.Decode(sr) return &v, err } func _CancelTimerFailedEventAttributes_Decode(sr stream.Reader) (*CancelTimerFailedEventAttributes, error) { var v CancelTimerFailedEventAttributes err := v.Decode(sr) return &v, err } func _MarkerRecordedEventAttributes_Decode(sr stream.Reader) (*MarkerRecordedEventAttributes, error) { var v MarkerRecordedEventAttributes err := v.Decode(sr) return &v, err } func _WorkflowExecutionSignaledEventAttributes_Decode(sr stream.Reader) (*WorkflowExecutionSignaledEventAttributes, error) { var v WorkflowExecutionSignaledEventAttributes err := v.Decode(sr) return &v, err } func _WorkflowExecutionTerminatedEventAttributes_Decode(sr stream.Reader) (*WorkflowExecutionTerminatedEventAttributes, error) { var v WorkflowExecutionTerminatedEventAttributes err := v.Decode(sr) return &v, err } func _WorkflowExecutionCancelRequestedEventAttributes_Decode(sr stream.Reader) (*WorkflowExecutionCancelRequestedEventAttributes, error) { var v WorkflowExecutionCancelRequestedEventAttributes err := v.Decode(sr) return &v, err } func _WorkflowExecutionCanceledEventAttributes_Decode(sr stream.Reader) (*WorkflowExecutionCanceledEventAttributes, error) { var v WorkflowExecutionCanceledEventAttributes err := v.Decode(sr) return &v, err } func _RequestCancelExternalWorkflowExecutionInitiatedEventAttributes_Decode(sr stream.Reader) (*RequestCancelExternalWorkflowExecutionInitiatedEventAttributes, error) { var v RequestCancelExternalWorkflowExecutionInitiatedEventAttributes err := v.Decode(sr) return &v, err } func _RequestCancelExternalWorkflowExecutionFailedEventAttributes_Decode(sr stream.Reader) (*RequestCancelExternalWorkflowExecutionFailedEventAttributes, error) { var v RequestCancelExternalWorkflowExecutionFailedEventAttributes err := v.Decode(sr) return &v, err } func _ExternalWorkflowExecutionCancelRequestedEventAttributes_Decode(sr stream.Reader) (*ExternalWorkflowExecutionCancelRequestedEventAttributes, error) { var v ExternalWorkflowExecutionCancelRequestedEventAttributes err := v.Decode(sr) return &v, err } func _WorkflowExecutionContinuedAsNewEventAttributes_Decode(sr stream.Reader) (*WorkflowExecutionContinuedAsNewEventAttributes, error) { var v WorkflowExecutionContinuedAsNewEventAttributes err := v.Decode(sr) return &v, err } func _StartChildWorkflowExecutionFailedEventAttributes_Decode(sr stream.Reader) (*StartChildWorkflowExecutionFailedEventAttributes, error) { var v StartChildWorkflowExecutionFailedEventAttributes err := v.Decode(sr) return &v, err } func _ChildWorkflowExecutionStartedEventAttributes_Decode(sr stream.Reader) (*ChildWorkflowExecutionStartedEventAttributes, error) { var v ChildWorkflowExecutionStartedEventAttributes err := v.Decode(sr) return &v, err } func _ChildWorkflowExecutionCompletedEventAttributes_Decode(sr stream.Reader) (*ChildWorkflowExecutionCompletedEventAttributes, error) { var v ChildWorkflowExecutionCompletedEventAttributes err := v.Decode(sr) return &v, err } func _ChildWorkflowExecutionFailedEventAttributes_Decode(sr stream.Reader) (*ChildWorkflowExecutionFailedEventAttributes, error) { var v ChildWorkflowExecutionFailedEventAttributes err := v.Decode(sr) return &v, err } func _ChildWorkflowExecutionCanceledEventAttributes_Decode(sr stream.Reader) (*ChildWorkflowExecutionCanceledEventAttributes, error) { var v ChildWorkflowExecutionCanceledEventAttributes err := v.Decode(sr) return &v, err } func _ChildWorkflowExecutionTimedOutEventAttributes_Decode(sr stream.Reader) (*ChildWorkflowExecutionTimedOutEventAttributes, error) { var v ChildWorkflowExecutionTimedOutEventAttributes err := v.Decode(sr) return &v, err } func _ChildWorkflowExecutionTerminatedEventAttributes_Decode(sr stream.Reader) (*ChildWorkflowExecutionTerminatedEventAttributes, error) { var v ChildWorkflowExecutionTerminatedEventAttributes err := v.Decode(sr) return &v, err } func _SignalExternalWorkflowExecutionInitiatedEventAttributes_Decode(sr stream.Reader) (*SignalExternalWorkflowExecutionInitiatedEventAttributes, error) { var v SignalExternalWorkflowExecutionInitiatedEventAttributes err := v.Decode(sr) return &v, err } func _SignalExternalWorkflowExecutionFailedEventAttributes_Decode(sr stream.Reader) (*SignalExternalWorkflowExecutionFailedEventAttributes, error) { var v SignalExternalWorkflowExecutionFailedEventAttributes err := v.Decode(sr) return &v, err } func _ExternalWorkflowExecutionSignaledEventAttributes_Decode(sr stream.Reader) (*ExternalWorkflowExecutionSignaledEventAttributes, error) { var v ExternalWorkflowExecutionSignaledEventAttributes err := v.Decode(sr) return &v, err } func _UpsertWorkflowSearchAttributesEventAttributes_Decode(sr stream.Reader) (*UpsertWorkflowSearchAttributesEventAttributes, error) { var v UpsertWorkflowSearchAttributesEventAttributes err := v.Decode(sr) return &v, err } // Decode deserializes a HistoryEvent struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryEvent struct could not be generated from the wire // representation. func (v *HistoryEvent) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Timestamp = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x EventType x, err = _EventType_Decode(sr) v.EventType = &x if err != nil { return err } case fh.ID == 35 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 36 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.WorkflowExecutionStartedEventAttributes, err = _WorkflowExecutionStartedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.WorkflowExecutionCompletedEventAttributes, err = _WorkflowExecutionCompletedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.WorkflowExecutionFailedEventAttributes, err = _WorkflowExecutionFailedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.WorkflowExecutionTimedOutEventAttributes, err = _WorkflowExecutionTimedOutEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TStruct: v.DecisionTaskScheduledEventAttributes, err = _DecisionTaskScheduledEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TStruct: v.DecisionTaskStartedEventAttributes, err = _DecisionTaskStartedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TStruct: v.DecisionTaskCompletedEventAttributes, err = _DecisionTaskCompletedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TStruct: v.DecisionTaskTimedOutEventAttributes, err = _DecisionTaskTimedOutEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TStruct: v.DecisionTaskFailedEventAttributes, err = _DecisionTaskFailedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TStruct: v.ActivityTaskScheduledEventAttributes, err = _ActivityTaskScheduledEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TStruct: v.ActivityTaskStartedEventAttributes, err = _ActivityTaskStartedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TStruct: v.ActivityTaskCompletedEventAttributes, err = _ActivityTaskCompletedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TStruct: v.ActivityTaskFailedEventAttributes, err = _ActivityTaskFailedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TStruct: v.ActivityTaskTimedOutEventAttributes, err = _ActivityTaskTimedOutEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 180 && fh.Type == wire.TStruct: v.TimerStartedEventAttributes, err = _TimerStartedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 190 && fh.Type == wire.TStruct: v.TimerFiredEventAttributes, err = _TimerFiredEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 200 && fh.Type == wire.TStruct: v.ActivityTaskCancelRequestedEventAttributes, err = _ActivityTaskCancelRequestedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 210 && fh.Type == wire.TStruct: v.RequestCancelActivityTaskFailedEventAttributes, err = _RequestCancelActivityTaskFailedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 220 && fh.Type == wire.TStruct: v.ActivityTaskCanceledEventAttributes, err = _ActivityTaskCanceledEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 230 && fh.Type == wire.TStruct: v.TimerCanceledEventAttributes, err = _TimerCanceledEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 240 && fh.Type == wire.TStruct: v.CancelTimerFailedEventAttributes, err = _CancelTimerFailedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 250 && fh.Type == wire.TStruct: v.MarkerRecordedEventAttributes, err = _MarkerRecordedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 260 && fh.Type == wire.TStruct: v.WorkflowExecutionSignaledEventAttributes, err = _WorkflowExecutionSignaledEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 270 && fh.Type == wire.TStruct: v.WorkflowExecutionTerminatedEventAttributes, err = _WorkflowExecutionTerminatedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 280 && fh.Type == wire.TStruct: v.WorkflowExecutionCancelRequestedEventAttributes, err = _WorkflowExecutionCancelRequestedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 290 && fh.Type == wire.TStruct: v.WorkflowExecutionCanceledEventAttributes, err = _WorkflowExecutionCanceledEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 300 && fh.Type == wire.TStruct: v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes, err = _RequestCancelExternalWorkflowExecutionInitiatedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 310 && fh.Type == wire.TStruct: v.RequestCancelExternalWorkflowExecutionFailedEventAttributes, err = _RequestCancelExternalWorkflowExecutionFailedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 320 && fh.Type == wire.TStruct: v.ExternalWorkflowExecutionCancelRequestedEventAttributes, err = _ExternalWorkflowExecutionCancelRequestedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 330 && fh.Type == wire.TStruct: v.WorkflowExecutionContinuedAsNewEventAttributes, err = _WorkflowExecutionContinuedAsNewEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 340 && fh.Type == wire.TStruct: v.StartChildWorkflowExecutionInitiatedEventAttributes, err = _StartChildWorkflowExecutionInitiatedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 350 && fh.Type == wire.TStruct: v.StartChildWorkflowExecutionFailedEventAttributes, err = _StartChildWorkflowExecutionFailedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 360 && fh.Type == wire.TStruct: v.ChildWorkflowExecutionStartedEventAttributes, err = _ChildWorkflowExecutionStartedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 370 && fh.Type == wire.TStruct: v.ChildWorkflowExecutionCompletedEventAttributes, err = _ChildWorkflowExecutionCompletedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 380 && fh.Type == wire.TStruct: v.ChildWorkflowExecutionFailedEventAttributes, err = _ChildWorkflowExecutionFailedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 390 && fh.Type == wire.TStruct: v.ChildWorkflowExecutionCanceledEventAttributes, err = _ChildWorkflowExecutionCanceledEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 400 && fh.Type == wire.TStruct: v.ChildWorkflowExecutionTimedOutEventAttributes, err = _ChildWorkflowExecutionTimedOutEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 410 && fh.Type == wire.TStruct: v.ChildWorkflowExecutionTerminatedEventAttributes, err = _ChildWorkflowExecutionTerminatedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 420 && fh.Type == wire.TStruct: v.SignalExternalWorkflowExecutionInitiatedEventAttributes, err = _SignalExternalWorkflowExecutionInitiatedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 430 && fh.Type == wire.TStruct: v.SignalExternalWorkflowExecutionFailedEventAttributes, err = _SignalExternalWorkflowExecutionFailedEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 440 && fh.Type == wire.TStruct: v.ExternalWorkflowExecutionSignaledEventAttributes, err = _ExternalWorkflowExecutionSignaledEventAttributes_Decode(sr) if err != nil { return err } case fh.ID == 450 && fh.Type == wire.TStruct: v.UpsertWorkflowSearchAttributesEventAttributes, err = _UpsertWorkflowSearchAttributesEventAttributes_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryEvent // struct. func (v *HistoryEvent) String() string { if v == nil { return "" } var fields [47]string i := 0 if v.EventId != nil { fields[i] = fmt.Sprintf("EventId: %v", *(v.EventId)) i++ } if v.Timestamp != nil { fields[i] = fmt.Sprintf("Timestamp: %v", *(v.Timestamp)) i++ } if v.EventType != nil { fields[i] = fmt.Sprintf("EventType: %v", *(v.EventType)) i++ } if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.TaskId != nil { fields[i] = fmt.Sprintf("TaskId: %v", *(v.TaskId)) i++ } if v.WorkflowExecutionStartedEventAttributes != nil { fields[i] = fmt.Sprintf("WorkflowExecutionStartedEventAttributes: %v", v.WorkflowExecutionStartedEventAttributes) i++ } if v.WorkflowExecutionCompletedEventAttributes != nil { fields[i] = fmt.Sprintf("WorkflowExecutionCompletedEventAttributes: %v", v.WorkflowExecutionCompletedEventAttributes) i++ } if v.WorkflowExecutionFailedEventAttributes != nil { fields[i] = fmt.Sprintf("WorkflowExecutionFailedEventAttributes: %v", v.WorkflowExecutionFailedEventAttributes) i++ } if v.WorkflowExecutionTimedOutEventAttributes != nil { fields[i] = fmt.Sprintf("WorkflowExecutionTimedOutEventAttributes: %v", v.WorkflowExecutionTimedOutEventAttributes) i++ } if v.DecisionTaskScheduledEventAttributes != nil { fields[i] = fmt.Sprintf("DecisionTaskScheduledEventAttributes: %v", v.DecisionTaskScheduledEventAttributes) i++ } if v.DecisionTaskStartedEventAttributes != nil { fields[i] = fmt.Sprintf("DecisionTaskStartedEventAttributes: %v", v.DecisionTaskStartedEventAttributes) i++ } if v.DecisionTaskCompletedEventAttributes != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventAttributes: %v", v.DecisionTaskCompletedEventAttributes) i++ } if v.DecisionTaskTimedOutEventAttributes != nil { fields[i] = fmt.Sprintf("DecisionTaskTimedOutEventAttributes: %v", v.DecisionTaskTimedOutEventAttributes) i++ } if v.DecisionTaskFailedEventAttributes != nil { fields[i] = fmt.Sprintf("DecisionTaskFailedEventAttributes: %v", v.DecisionTaskFailedEventAttributes) i++ } if v.ActivityTaskScheduledEventAttributes != nil { fields[i] = fmt.Sprintf("ActivityTaskScheduledEventAttributes: %v", v.ActivityTaskScheduledEventAttributes) i++ } if v.ActivityTaskStartedEventAttributes != nil { fields[i] = fmt.Sprintf("ActivityTaskStartedEventAttributes: %v", v.ActivityTaskStartedEventAttributes) i++ } if v.ActivityTaskCompletedEventAttributes != nil { fields[i] = fmt.Sprintf("ActivityTaskCompletedEventAttributes: %v", v.ActivityTaskCompletedEventAttributes) i++ } if v.ActivityTaskFailedEventAttributes != nil { fields[i] = fmt.Sprintf("ActivityTaskFailedEventAttributes: %v", v.ActivityTaskFailedEventAttributes) i++ } if v.ActivityTaskTimedOutEventAttributes != nil { fields[i] = fmt.Sprintf("ActivityTaskTimedOutEventAttributes: %v", v.ActivityTaskTimedOutEventAttributes) i++ } if v.TimerStartedEventAttributes != nil { fields[i] = fmt.Sprintf("TimerStartedEventAttributes: %v", v.TimerStartedEventAttributes) i++ } if v.TimerFiredEventAttributes != nil { fields[i] = fmt.Sprintf("TimerFiredEventAttributes: %v", v.TimerFiredEventAttributes) i++ } if v.ActivityTaskCancelRequestedEventAttributes != nil { fields[i] = fmt.Sprintf("ActivityTaskCancelRequestedEventAttributes: %v", v.ActivityTaskCancelRequestedEventAttributes) i++ } if v.RequestCancelActivityTaskFailedEventAttributes != nil { fields[i] = fmt.Sprintf("RequestCancelActivityTaskFailedEventAttributes: %v", v.RequestCancelActivityTaskFailedEventAttributes) i++ } if v.ActivityTaskCanceledEventAttributes != nil { fields[i] = fmt.Sprintf("ActivityTaskCanceledEventAttributes: %v", v.ActivityTaskCanceledEventAttributes) i++ } if v.TimerCanceledEventAttributes != nil { fields[i] = fmt.Sprintf("TimerCanceledEventAttributes: %v", v.TimerCanceledEventAttributes) i++ } if v.CancelTimerFailedEventAttributes != nil { fields[i] = fmt.Sprintf("CancelTimerFailedEventAttributes: %v", v.CancelTimerFailedEventAttributes) i++ } if v.MarkerRecordedEventAttributes != nil { fields[i] = fmt.Sprintf("MarkerRecordedEventAttributes: %v", v.MarkerRecordedEventAttributes) i++ } if v.WorkflowExecutionSignaledEventAttributes != nil { fields[i] = fmt.Sprintf("WorkflowExecutionSignaledEventAttributes: %v", v.WorkflowExecutionSignaledEventAttributes) i++ } if v.WorkflowExecutionTerminatedEventAttributes != nil { fields[i] = fmt.Sprintf("WorkflowExecutionTerminatedEventAttributes: %v", v.WorkflowExecutionTerminatedEventAttributes) i++ } if v.WorkflowExecutionCancelRequestedEventAttributes != nil { fields[i] = fmt.Sprintf("WorkflowExecutionCancelRequestedEventAttributes: %v", v.WorkflowExecutionCancelRequestedEventAttributes) i++ } if v.WorkflowExecutionCanceledEventAttributes != nil { fields[i] = fmt.Sprintf("WorkflowExecutionCanceledEventAttributes: %v", v.WorkflowExecutionCanceledEventAttributes) i++ } if v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes != nil { fields[i] = fmt.Sprintf("RequestCancelExternalWorkflowExecutionInitiatedEventAttributes: %v", v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) i++ } if v.RequestCancelExternalWorkflowExecutionFailedEventAttributes != nil { fields[i] = fmt.Sprintf("RequestCancelExternalWorkflowExecutionFailedEventAttributes: %v", v.RequestCancelExternalWorkflowExecutionFailedEventAttributes) i++ } if v.ExternalWorkflowExecutionCancelRequestedEventAttributes != nil { fields[i] = fmt.Sprintf("ExternalWorkflowExecutionCancelRequestedEventAttributes: %v", v.ExternalWorkflowExecutionCancelRequestedEventAttributes) i++ } if v.WorkflowExecutionContinuedAsNewEventAttributes != nil { fields[i] = fmt.Sprintf("WorkflowExecutionContinuedAsNewEventAttributes: %v", v.WorkflowExecutionContinuedAsNewEventAttributes) i++ } if v.StartChildWorkflowExecutionInitiatedEventAttributes != nil { fields[i] = fmt.Sprintf("StartChildWorkflowExecutionInitiatedEventAttributes: %v", v.StartChildWorkflowExecutionInitiatedEventAttributes) i++ } if v.StartChildWorkflowExecutionFailedEventAttributes != nil { fields[i] = fmt.Sprintf("StartChildWorkflowExecutionFailedEventAttributes: %v", v.StartChildWorkflowExecutionFailedEventAttributes) i++ } if v.ChildWorkflowExecutionStartedEventAttributes != nil { fields[i] = fmt.Sprintf("ChildWorkflowExecutionStartedEventAttributes: %v", v.ChildWorkflowExecutionStartedEventAttributes) i++ } if v.ChildWorkflowExecutionCompletedEventAttributes != nil { fields[i] = fmt.Sprintf("ChildWorkflowExecutionCompletedEventAttributes: %v", v.ChildWorkflowExecutionCompletedEventAttributes) i++ } if v.ChildWorkflowExecutionFailedEventAttributes != nil { fields[i] = fmt.Sprintf("ChildWorkflowExecutionFailedEventAttributes: %v", v.ChildWorkflowExecutionFailedEventAttributes) i++ } if v.ChildWorkflowExecutionCanceledEventAttributes != nil { fields[i] = fmt.Sprintf("ChildWorkflowExecutionCanceledEventAttributes: %v", v.ChildWorkflowExecutionCanceledEventAttributes) i++ } if v.ChildWorkflowExecutionTimedOutEventAttributes != nil { fields[i] = fmt.Sprintf("ChildWorkflowExecutionTimedOutEventAttributes: %v", v.ChildWorkflowExecutionTimedOutEventAttributes) i++ } if v.ChildWorkflowExecutionTerminatedEventAttributes != nil { fields[i] = fmt.Sprintf("ChildWorkflowExecutionTerminatedEventAttributes: %v", v.ChildWorkflowExecutionTerminatedEventAttributes) i++ } if v.SignalExternalWorkflowExecutionInitiatedEventAttributes != nil { fields[i] = fmt.Sprintf("SignalExternalWorkflowExecutionInitiatedEventAttributes: %v", v.SignalExternalWorkflowExecutionInitiatedEventAttributes) i++ } if v.SignalExternalWorkflowExecutionFailedEventAttributes != nil { fields[i] = fmt.Sprintf("SignalExternalWorkflowExecutionFailedEventAttributes: %v", v.SignalExternalWorkflowExecutionFailedEventAttributes) i++ } if v.ExternalWorkflowExecutionSignaledEventAttributes != nil { fields[i] = fmt.Sprintf("ExternalWorkflowExecutionSignaledEventAttributes: %v", v.ExternalWorkflowExecutionSignaledEventAttributes) i++ } if v.UpsertWorkflowSearchAttributesEventAttributes != nil { fields[i] = fmt.Sprintf("UpsertWorkflowSearchAttributesEventAttributes: %v", v.UpsertWorkflowSearchAttributesEventAttributes) i++ } return fmt.Sprintf("HistoryEvent{%v}", strings.Join(fields[:i], ", ")) } func _EventType_EqualsPtr(lhs, rhs *EventType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this HistoryEvent match the // provided HistoryEvent. // // This function performs a deep comparison. func (v *HistoryEvent) Equals(rhs *HistoryEvent) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.EventId, rhs.EventId) { return false } if !_I64_EqualsPtr(v.Timestamp, rhs.Timestamp) { return false } if !_EventType_EqualsPtr(v.EventType, rhs.EventType) { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.TaskId, rhs.TaskId) { return false } if !((v.WorkflowExecutionStartedEventAttributes == nil && rhs.WorkflowExecutionStartedEventAttributes == nil) || (v.WorkflowExecutionStartedEventAttributes != nil && rhs.WorkflowExecutionStartedEventAttributes != nil && v.WorkflowExecutionStartedEventAttributes.Equals(rhs.WorkflowExecutionStartedEventAttributes))) { return false } if !((v.WorkflowExecutionCompletedEventAttributes == nil && rhs.WorkflowExecutionCompletedEventAttributes == nil) || (v.WorkflowExecutionCompletedEventAttributes != nil && rhs.WorkflowExecutionCompletedEventAttributes != nil && v.WorkflowExecutionCompletedEventAttributes.Equals(rhs.WorkflowExecutionCompletedEventAttributes))) { return false } if !((v.WorkflowExecutionFailedEventAttributes == nil && rhs.WorkflowExecutionFailedEventAttributes == nil) || (v.WorkflowExecutionFailedEventAttributes != nil && rhs.WorkflowExecutionFailedEventAttributes != nil && v.WorkflowExecutionFailedEventAttributes.Equals(rhs.WorkflowExecutionFailedEventAttributes))) { return false } if !((v.WorkflowExecutionTimedOutEventAttributes == nil && rhs.WorkflowExecutionTimedOutEventAttributes == nil) || (v.WorkflowExecutionTimedOutEventAttributes != nil && rhs.WorkflowExecutionTimedOutEventAttributes != nil && v.WorkflowExecutionTimedOutEventAttributes.Equals(rhs.WorkflowExecutionTimedOutEventAttributes))) { return false } if !((v.DecisionTaskScheduledEventAttributes == nil && rhs.DecisionTaskScheduledEventAttributes == nil) || (v.DecisionTaskScheduledEventAttributes != nil && rhs.DecisionTaskScheduledEventAttributes != nil && v.DecisionTaskScheduledEventAttributes.Equals(rhs.DecisionTaskScheduledEventAttributes))) { return false } if !((v.DecisionTaskStartedEventAttributes == nil && rhs.DecisionTaskStartedEventAttributes == nil) || (v.DecisionTaskStartedEventAttributes != nil && rhs.DecisionTaskStartedEventAttributes != nil && v.DecisionTaskStartedEventAttributes.Equals(rhs.DecisionTaskStartedEventAttributes))) { return false } if !((v.DecisionTaskCompletedEventAttributes == nil && rhs.DecisionTaskCompletedEventAttributes == nil) || (v.DecisionTaskCompletedEventAttributes != nil && rhs.DecisionTaskCompletedEventAttributes != nil && v.DecisionTaskCompletedEventAttributes.Equals(rhs.DecisionTaskCompletedEventAttributes))) { return false } if !((v.DecisionTaskTimedOutEventAttributes == nil && rhs.DecisionTaskTimedOutEventAttributes == nil) || (v.DecisionTaskTimedOutEventAttributes != nil && rhs.DecisionTaskTimedOutEventAttributes != nil && v.DecisionTaskTimedOutEventAttributes.Equals(rhs.DecisionTaskTimedOutEventAttributes))) { return false } if !((v.DecisionTaskFailedEventAttributes == nil && rhs.DecisionTaskFailedEventAttributes == nil) || (v.DecisionTaskFailedEventAttributes != nil && rhs.DecisionTaskFailedEventAttributes != nil && v.DecisionTaskFailedEventAttributes.Equals(rhs.DecisionTaskFailedEventAttributes))) { return false } if !((v.ActivityTaskScheduledEventAttributes == nil && rhs.ActivityTaskScheduledEventAttributes == nil) || (v.ActivityTaskScheduledEventAttributes != nil && rhs.ActivityTaskScheduledEventAttributes != nil && v.ActivityTaskScheduledEventAttributes.Equals(rhs.ActivityTaskScheduledEventAttributes))) { return false } if !((v.ActivityTaskStartedEventAttributes == nil && rhs.ActivityTaskStartedEventAttributes == nil) || (v.ActivityTaskStartedEventAttributes != nil && rhs.ActivityTaskStartedEventAttributes != nil && v.ActivityTaskStartedEventAttributes.Equals(rhs.ActivityTaskStartedEventAttributes))) { return false } if !((v.ActivityTaskCompletedEventAttributes == nil && rhs.ActivityTaskCompletedEventAttributes == nil) || (v.ActivityTaskCompletedEventAttributes != nil && rhs.ActivityTaskCompletedEventAttributes != nil && v.ActivityTaskCompletedEventAttributes.Equals(rhs.ActivityTaskCompletedEventAttributes))) { return false } if !((v.ActivityTaskFailedEventAttributes == nil && rhs.ActivityTaskFailedEventAttributes == nil) || (v.ActivityTaskFailedEventAttributes != nil && rhs.ActivityTaskFailedEventAttributes != nil && v.ActivityTaskFailedEventAttributes.Equals(rhs.ActivityTaskFailedEventAttributes))) { return false } if !((v.ActivityTaskTimedOutEventAttributes == nil && rhs.ActivityTaskTimedOutEventAttributes == nil) || (v.ActivityTaskTimedOutEventAttributes != nil && rhs.ActivityTaskTimedOutEventAttributes != nil && v.ActivityTaskTimedOutEventAttributes.Equals(rhs.ActivityTaskTimedOutEventAttributes))) { return false } if !((v.TimerStartedEventAttributes == nil && rhs.TimerStartedEventAttributes == nil) || (v.TimerStartedEventAttributes != nil && rhs.TimerStartedEventAttributes != nil && v.TimerStartedEventAttributes.Equals(rhs.TimerStartedEventAttributes))) { return false } if !((v.TimerFiredEventAttributes == nil && rhs.TimerFiredEventAttributes == nil) || (v.TimerFiredEventAttributes != nil && rhs.TimerFiredEventAttributes != nil && v.TimerFiredEventAttributes.Equals(rhs.TimerFiredEventAttributes))) { return false } if !((v.ActivityTaskCancelRequestedEventAttributes == nil && rhs.ActivityTaskCancelRequestedEventAttributes == nil) || (v.ActivityTaskCancelRequestedEventAttributes != nil && rhs.ActivityTaskCancelRequestedEventAttributes != nil && v.ActivityTaskCancelRequestedEventAttributes.Equals(rhs.ActivityTaskCancelRequestedEventAttributes))) { return false } if !((v.RequestCancelActivityTaskFailedEventAttributes == nil && rhs.RequestCancelActivityTaskFailedEventAttributes == nil) || (v.RequestCancelActivityTaskFailedEventAttributes != nil && rhs.RequestCancelActivityTaskFailedEventAttributes != nil && v.RequestCancelActivityTaskFailedEventAttributes.Equals(rhs.RequestCancelActivityTaskFailedEventAttributes))) { return false } if !((v.ActivityTaskCanceledEventAttributes == nil && rhs.ActivityTaskCanceledEventAttributes == nil) || (v.ActivityTaskCanceledEventAttributes != nil && rhs.ActivityTaskCanceledEventAttributes != nil && v.ActivityTaskCanceledEventAttributes.Equals(rhs.ActivityTaskCanceledEventAttributes))) { return false } if !((v.TimerCanceledEventAttributes == nil && rhs.TimerCanceledEventAttributes == nil) || (v.TimerCanceledEventAttributes != nil && rhs.TimerCanceledEventAttributes != nil && v.TimerCanceledEventAttributes.Equals(rhs.TimerCanceledEventAttributes))) { return false } if !((v.CancelTimerFailedEventAttributes == nil && rhs.CancelTimerFailedEventAttributes == nil) || (v.CancelTimerFailedEventAttributes != nil && rhs.CancelTimerFailedEventAttributes != nil && v.CancelTimerFailedEventAttributes.Equals(rhs.CancelTimerFailedEventAttributes))) { return false } if !((v.MarkerRecordedEventAttributes == nil && rhs.MarkerRecordedEventAttributes == nil) || (v.MarkerRecordedEventAttributes != nil && rhs.MarkerRecordedEventAttributes != nil && v.MarkerRecordedEventAttributes.Equals(rhs.MarkerRecordedEventAttributes))) { return false } if !((v.WorkflowExecutionSignaledEventAttributes == nil && rhs.WorkflowExecutionSignaledEventAttributes == nil) || (v.WorkflowExecutionSignaledEventAttributes != nil && rhs.WorkflowExecutionSignaledEventAttributes != nil && v.WorkflowExecutionSignaledEventAttributes.Equals(rhs.WorkflowExecutionSignaledEventAttributes))) { return false } if !((v.WorkflowExecutionTerminatedEventAttributes == nil && rhs.WorkflowExecutionTerminatedEventAttributes == nil) || (v.WorkflowExecutionTerminatedEventAttributes != nil && rhs.WorkflowExecutionTerminatedEventAttributes != nil && v.WorkflowExecutionTerminatedEventAttributes.Equals(rhs.WorkflowExecutionTerminatedEventAttributes))) { return false } if !((v.WorkflowExecutionCancelRequestedEventAttributes == nil && rhs.WorkflowExecutionCancelRequestedEventAttributes == nil) || (v.WorkflowExecutionCancelRequestedEventAttributes != nil && rhs.WorkflowExecutionCancelRequestedEventAttributes != nil && v.WorkflowExecutionCancelRequestedEventAttributes.Equals(rhs.WorkflowExecutionCancelRequestedEventAttributes))) { return false } if !((v.WorkflowExecutionCanceledEventAttributes == nil && rhs.WorkflowExecutionCanceledEventAttributes == nil) || (v.WorkflowExecutionCanceledEventAttributes != nil && rhs.WorkflowExecutionCanceledEventAttributes != nil && v.WorkflowExecutionCanceledEventAttributes.Equals(rhs.WorkflowExecutionCanceledEventAttributes))) { return false } if !((v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes == nil && rhs.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes == nil) || (v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes != nil && rhs.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes != nil && v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes.Equals(rhs.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes))) { return false } if !((v.RequestCancelExternalWorkflowExecutionFailedEventAttributes == nil && rhs.RequestCancelExternalWorkflowExecutionFailedEventAttributes == nil) || (v.RequestCancelExternalWorkflowExecutionFailedEventAttributes != nil && rhs.RequestCancelExternalWorkflowExecutionFailedEventAttributes != nil && v.RequestCancelExternalWorkflowExecutionFailedEventAttributes.Equals(rhs.RequestCancelExternalWorkflowExecutionFailedEventAttributes))) { return false } if !((v.ExternalWorkflowExecutionCancelRequestedEventAttributes == nil && rhs.ExternalWorkflowExecutionCancelRequestedEventAttributes == nil) || (v.ExternalWorkflowExecutionCancelRequestedEventAttributes != nil && rhs.ExternalWorkflowExecutionCancelRequestedEventAttributes != nil && v.ExternalWorkflowExecutionCancelRequestedEventAttributes.Equals(rhs.ExternalWorkflowExecutionCancelRequestedEventAttributes))) { return false } if !((v.WorkflowExecutionContinuedAsNewEventAttributes == nil && rhs.WorkflowExecutionContinuedAsNewEventAttributes == nil) || (v.WorkflowExecutionContinuedAsNewEventAttributes != nil && rhs.WorkflowExecutionContinuedAsNewEventAttributes != nil && v.WorkflowExecutionContinuedAsNewEventAttributes.Equals(rhs.WorkflowExecutionContinuedAsNewEventAttributes))) { return false } if !((v.StartChildWorkflowExecutionInitiatedEventAttributes == nil && rhs.StartChildWorkflowExecutionInitiatedEventAttributes == nil) || (v.StartChildWorkflowExecutionInitiatedEventAttributes != nil && rhs.StartChildWorkflowExecutionInitiatedEventAttributes != nil && v.StartChildWorkflowExecutionInitiatedEventAttributes.Equals(rhs.StartChildWorkflowExecutionInitiatedEventAttributes))) { return false } if !((v.StartChildWorkflowExecutionFailedEventAttributes == nil && rhs.StartChildWorkflowExecutionFailedEventAttributes == nil) || (v.StartChildWorkflowExecutionFailedEventAttributes != nil && rhs.StartChildWorkflowExecutionFailedEventAttributes != nil && v.StartChildWorkflowExecutionFailedEventAttributes.Equals(rhs.StartChildWorkflowExecutionFailedEventAttributes))) { return false } if !((v.ChildWorkflowExecutionStartedEventAttributes == nil && rhs.ChildWorkflowExecutionStartedEventAttributes == nil) || (v.ChildWorkflowExecutionStartedEventAttributes != nil && rhs.ChildWorkflowExecutionStartedEventAttributes != nil && v.ChildWorkflowExecutionStartedEventAttributes.Equals(rhs.ChildWorkflowExecutionStartedEventAttributes))) { return false } if !((v.ChildWorkflowExecutionCompletedEventAttributes == nil && rhs.ChildWorkflowExecutionCompletedEventAttributes == nil) || (v.ChildWorkflowExecutionCompletedEventAttributes != nil && rhs.ChildWorkflowExecutionCompletedEventAttributes != nil && v.ChildWorkflowExecutionCompletedEventAttributes.Equals(rhs.ChildWorkflowExecutionCompletedEventAttributes))) { return false } if !((v.ChildWorkflowExecutionFailedEventAttributes == nil && rhs.ChildWorkflowExecutionFailedEventAttributes == nil) || (v.ChildWorkflowExecutionFailedEventAttributes != nil && rhs.ChildWorkflowExecutionFailedEventAttributes != nil && v.ChildWorkflowExecutionFailedEventAttributes.Equals(rhs.ChildWorkflowExecutionFailedEventAttributes))) { return false } if !((v.ChildWorkflowExecutionCanceledEventAttributes == nil && rhs.ChildWorkflowExecutionCanceledEventAttributes == nil) || (v.ChildWorkflowExecutionCanceledEventAttributes != nil && rhs.ChildWorkflowExecutionCanceledEventAttributes != nil && v.ChildWorkflowExecutionCanceledEventAttributes.Equals(rhs.ChildWorkflowExecutionCanceledEventAttributes))) { return false } if !((v.ChildWorkflowExecutionTimedOutEventAttributes == nil && rhs.ChildWorkflowExecutionTimedOutEventAttributes == nil) || (v.ChildWorkflowExecutionTimedOutEventAttributes != nil && rhs.ChildWorkflowExecutionTimedOutEventAttributes != nil && v.ChildWorkflowExecutionTimedOutEventAttributes.Equals(rhs.ChildWorkflowExecutionTimedOutEventAttributes))) { return false } if !((v.ChildWorkflowExecutionTerminatedEventAttributes == nil && rhs.ChildWorkflowExecutionTerminatedEventAttributes == nil) || (v.ChildWorkflowExecutionTerminatedEventAttributes != nil && rhs.ChildWorkflowExecutionTerminatedEventAttributes != nil && v.ChildWorkflowExecutionTerminatedEventAttributes.Equals(rhs.ChildWorkflowExecutionTerminatedEventAttributes))) { return false } if !((v.SignalExternalWorkflowExecutionInitiatedEventAttributes == nil && rhs.SignalExternalWorkflowExecutionInitiatedEventAttributes == nil) || (v.SignalExternalWorkflowExecutionInitiatedEventAttributes != nil && rhs.SignalExternalWorkflowExecutionInitiatedEventAttributes != nil && v.SignalExternalWorkflowExecutionInitiatedEventAttributes.Equals(rhs.SignalExternalWorkflowExecutionInitiatedEventAttributes))) { return false } if !((v.SignalExternalWorkflowExecutionFailedEventAttributes == nil && rhs.SignalExternalWorkflowExecutionFailedEventAttributes == nil) || (v.SignalExternalWorkflowExecutionFailedEventAttributes != nil && rhs.SignalExternalWorkflowExecutionFailedEventAttributes != nil && v.SignalExternalWorkflowExecutionFailedEventAttributes.Equals(rhs.SignalExternalWorkflowExecutionFailedEventAttributes))) { return false } if !((v.ExternalWorkflowExecutionSignaledEventAttributes == nil && rhs.ExternalWorkflowExecutionSignaledEventAttributes == nil) || (v.ExternalWorkflowExecutionSignaledEventAttributes != nil && rhs.ExternalWorkflowExecutionSignaledEventAttributes != nil && v.ExternalWorkflowExecutionSignaledEventAttributes.Equals(rhs.ExternalWorkflowExecutionSignaledEventAttributes))) { return false } if !((v.UpsertWorkflowSearchAttributesEventAttributes == nil && rhs.UpsertWorkflowSearchAttributesEventAttributes == nil) || (v.UpsertWorkflowSearchAttributesEventAttributes != nil && rhs.UpsertWorkflowSearchAttributesEventAttributes != nil && v.UpsertWorkflowSearchAttributesEventAttributes.Equals(rhs.UpsertWorkflowSearchAttributesEventAttributes))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryEvent. func (v *HistoryEvent) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.EventId != nil { enc.AddInt64("eventId", *v.EventId) } if v.Timestamp != nil { enc.AddInt64("timestamp", *v.Timestamp) } if v.EventType != nil { err = multierr.Append(err, enc.AddObject("eventType", *v.EventType)) } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.TaskId != nil { enc.AddInt64("taskId", *v.TaskId) } if v.WorkflowExecutionStartedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionStartedEventAttributes", v.WorkflowExecutionStartedEventAttributes)) } if v.WorkflowExecutionCompletedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionCompletedEventAttributes", v.WorkflowExecutionCompletedEventAttributes)) } if v.WorkflowExecutionFailedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionFailedEventAttributes", v.WorkflowExecutionFailedEventAttributes)) } if v.WorkflowExecutionTimedOutEventAttributes != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionTimedOutEventAttributes", v.WorkflowExecutionTimedOutEventAttributes)) } if v.DecisionTaskScheduledEventAttributes != nil { err = multierr.Append(err, enc.AddObject("decisionTaskScheduledEventAttributes", v.DecisionTaskScheduledEventAttributes)) } if v.DecisionTaskStartedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("decisionTaskStartedEventAttributes", v.DecisionTaskStartedEventAttributes)) } if v.DecisionTaskCompletedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("decisionTaskCompletedEventAttributes", v.DecisionTaskCompletedEventAttributes)) } if v.DecisionTaskTimedOutEventAttributes != nil { err = multierr.Append(err, enc.AddObject("decisionTaskTimedOutEventAttributes", v.DecisionTaskTimedOutEventAttributes)) } if v.DecisionTaskFailedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("decisionTaskFailedEventAttributes", v.DecisionTaskFailedEventAttributes)) } if v.ActivityTaskScheduledEventAttributes != nil { err = multierr.Append(err, enc.AddObject("activityTaskScheduledEventAttributes", v.ActivityTaskScheduledEventAttributes)) } if v.ActivityTaskStartedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("activityTaskStartedEventAttributes", v.ActivityTaskStartedEventAttributes)) } if v.ActivityTaskCompletedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("activityTaskCompletedEventAttributes", v.ActivityTaskCompletedEventAttributes)) } if v.ActivityTaskFailedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("activityTaskFailedEventAttributes", v.ActivityTaskFailedEventAttributes)) } if v.ActivityTaskTimedOutEventAttributes != nil { err = multierr.Append(err, enc.AddObject("activityTaskTimedOutEventAttributes", v.ActivityTaskTimedOutEventAttributes)) } if v.TimerStartedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("timerStartedEventAttributes", v.TimerStartedEventAttributes)) } if v.TimerFiredEventAttributes != nil { err = multierr.Append(err, enc.AddObject("timerFiredEventAttributes", v.TimerFiredEventAttributes)) } if v.ActivityTaskCancelRequestedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("activityTaskCancelRequestedEventAttributes", v.ActivityTaskCancelRequestedEventAttributes)) } if v.RequestCancelActivityTaskFailedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("requestCancelActivityTaskFailedEventAttributes", v.RequestCancelActivityTaskFailedEventAttributes)) } if v.ActivityTaskCanceledEventAttributes != nil { err = multierr.Append(err, enc.AddObject("activityTaskCanceledEventAttributes", v.ActivityTaskCanceledEventAttributes)) } if v.TimerCanceledEventAttributes != nil { err = multierr.Append(err, enc.AddObject("timerCanceledEventAttributes", v.TimerCanceledEventAttributes)) } if v.CancelTimerFailedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("cancelTimerFailedEventAttributes", v.CancelTimerFailedEventAttributes)) } if v.MarkerRecordedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("markerRecordedEventAttributes", v.MarkerRecordedEventAttributes)) } if v.WorkflowExecutionSignaledEventAttributes != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionSignaledEventAttributes", v.WorkflowExecutionSignaledEventAttributes)) } if v.WorkflowExecutionTerminatedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionTerminatedEventAttributes", v.WorkflowExecutionTerminatedEventAttributes)) } if v.WorkflowExecutionCancelRequestedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionCancelRequestedEventAttributes", v.WorkflowExecutionCancelRequestedEventAttributes)) } if v.WorkflowExecutionCanceledEventAttributes != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionCanceledEventAttributes", v.WorkflowExecutionCanceledEventAttributes)) } if v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("requestCancelExternalWorkflowExecutionInitiatedEventAttributes", v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes)) } if v.RequestCancelExternalWorkflowExecutionFailedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("requestCancelExternalWorkflowExecutionFailedEventAttributes", v.RequestCancelExternalWorkflowExecutionFailedEventAttributes)) } if v.ExternalWorkflowExecutionCancelRequestedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("externalWorkflowExecutionCancelRequestedEventAttributes", v.ExternalWorkflowExecutionCancelRequestedEventAttributes)) } if v.WorkflowExecutionContinuedAsNewEventAttributes != nil { err = multierr.Append(err, enc.AddObject("workflowExecutionContinuedAsNewEventAttributes", v.WorkflowExecutionContinuedAsNewEventAttributes)) } if v.StartChildWorkflowExecutionInitiatedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("startChildWorkflowExecutionInitiatedEventAttributes", v.StartChildWorkflowExecutionInitiatedEventAttributes)) } if v.StartChildWorkflowExecutionFailedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("startChildWorkflowExecutionFailedEventAttributes", v.StartChildWorkflowExecutionFailedEventAttributes)) } if v.ChildWorkflowExecutionStartedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("childWorkflowExecutionStartedEventAttributes", v.ChildWorkflowExecutionStartedEventAttributes)) } if v.ChildWorkflowExecutionCompletedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("childWorkflowExecutionCompletedEventAttributes", v.ChildWorkflowExecutionCompletedEventAttributes)) } if v.ChildWorkflowExecutionFailedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("childWorkflowExecutionFailedEventAttributes", v.ChildWorkflowExecutionFailedEventAttributes)) } if v.ChildWorkflowExecutionCanceledEventAttributes != nil { err = multierr.Append(err, enc.AddObject("childWorkflowExecutionCanceledEventAttributes", v.ChildWorkflowExecutionCanceledEventAttributes)) } if v.ChildWorkflowExecutionTimedOutEventAttributes != nil { err = multierr.Append(err, enc.AddObject("childWorkflowExecutionTimedOutEventAttributes", v.ChildWorkflowExecutionTimedOutEventAttributes)) } if v.ChildWorkflowExecutionTerminatedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("childWorkflowExecutionTerminatedEventAttributes", v.ChildWorkflowExecutionTerminatedEventAttributes)) } if v.SignalExternalWorkflowExecutionInitiatedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("signalExternalWorkflowExecutionInitiatedEventAttributes", v.SignalExternalWorkflowExecutionInitiatedEventAttributes)) } if v.SignalExternalWorkflowExecutionFailedEventAttributes != nil { err = multierr.Append(err, enc.AddObject("signalExternalWorkflowExecutionFailedEventAttributes", v.SignalExternalWorkflowExecutionFailedEventAttributes)) } if v.ExternalWorkflowExecutionSignaledEventAttributes != nil { err = multierr.Append(err, enc.AddObject("externalWorkflowExecutionSignaledEventAttributes", v.ExternalWorkflowExecutionSignaledEventAttributes)) } if v.UpsertWorkflowSearchAttributesEventAttributes != nil { err = multierr.Append(err, enc.AddObject("upsertWorkflowSearchAttributesEventAttributes", v.UpsertWorkflowSearchAttributesEventAttributes)) } return err } // GetEventId returns the value of EventId if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetEventId() (o int64) { if v != nil && v.EventId != nil { return *v.EventId } return } // IsSetEventId returns true if EventId is not nil. func (v *HistoryEvent) IsSetEventId() bool { return v != nil && v.EventId != nil } // GetTimestamp returns the value of Timestamp if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetTimestamp() (o int64) { if v != nil && v.Timestamp != nil { return *v.Timestamp } return } // IsSetTimestamp returns true if Timestamp is not nil. func (v *HistoryEvent) IsSetTimestamp() bool { return v != nil && v.Timestamp != nil } // GetEventType returns the value of EventType if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetEventType() (o EventType) { if v != nil && v.EventType != nil { return *v.EventType } return } // IsSetEventType returns true if EventType is not nil. func (v *HistoryEvent) IsSetEventType() bool { return v != nil && v.EventType != nil } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *HistoryEvent) IsSetVersion() bool { return v != nil && v.Version != nil } // GetTaskId returns the value of TaskId if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetTaskId() (o int64) { if v != nil && v.TaskId != nil { return *v.TaskId } return } // IsSetTaskId returns true if TaskId is not nil. func (v *HistoryEvent) IsSetTaskId() bool { return v != nil && v.TaskId != nil } // GetWorkflowExecutionStartedEventAttributes returns the value of WorkflowExecutionStartedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetWorkflowExecutionStartedEventAttributes() (o *WorkflowExecutionStartedEventAttributes) { if v != nil && v.WorkflowExecutionStartedEventAttributes != nil { return v.WorkflowExecutionStartedEventAttributes } return } // IsSetWorkflowExecutionStartedEventAttributes returns true if WorkflowExecutionStartedEventAttributes is not nil. func (v *HistoryEvent) IsSetWorkflowExecutionStartedEventAttributes() bool { return v != nil && v.WorkflowExecutionStartedEventAttributes != nil } // GetWorkflowExecutionCompletedEventAttributes returns the value of WorkflowExecutionCompletedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetWorkflowExecutionCompletedEventAttributes() (o *WorkflowExecutionCompletedEventAttributes) { if v != nil && v.WorkflowExecutionCompletedEventAttributes != nil { return v.WorkflowExecutionCompletedEventAttributes } return } // IsSetWorkflowExecutionCompletedEventAttributes returns true if WorkflowExecutionCompletedEventAttributes is not nil. func (v *HistoryEvent) IsSetWorkflowExecutionCompletedEventAttributes() bool { return v != nil && v.WorkflowExecutionCompletedEventAttributes != nil } // GetWorkflowExecutionFailedEventAttributes returns the value of WorkflowExecutionFailedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetWorkflowExecutionFailedEventAttributes() (o *WorkflowExecutionFailedEventAttributes) { if v != nil && v.WorkflowExecutionFailedEventAttributes != nil { return v.WorkflowExecutionFailedEventAttributes } return } // IsSetWorkflowExecutionFailedEventAttributes returns true if WorkflowExecutionFailedEventAttributes is not nil. func (v *HistoryEvent) IsSetWorkflowExecutionFailedEventAttributes() bool { return v != nil && v.WorkflowExecutionFailedEventAttributes != nil } // GetWorkflowExecutionTimedOutEventAttributes returns the value of WorkflowExecutionTimedOutEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetWorkflowExecutionTimedOutEventAttributes() (o *WorkflowExecutionTimedOutEventAttributes) { if v != nil && v.WorkflowExecutionTimedOutEventAttributes != nil { return v.WorkflowExecutionTimedOutEventAttributes } return } // IsSetWorkflowExecutionTimedOutEventAttributes returns true if WorkflowExecutionTimedOutEventAttributes is not nil. func (v *HistoryEvent) IsSetWorkflowExecutionTimedOutEventAttributes() bool { return v != nil && v.WorkflowExecutionTimedOutEventAttributes != nil } // GetDecisionTaskScheduledEventAttributes returns the value of DecisionTaskScheduledEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetDecisionTaskScheduledEventAttributes() (o *DecisionTaskScheduledEventAttributes) { if v != nil && v.DecisionTaskScheduledEventAttributes != nil { return v.DecisionTaskScheduledEventAttributes } return } // IsSetDecisionTaskScheduledEventAttributes returns true if DecisionTaskScheduledEventAttributes is not nil. func (v *HistoryEvent) IsSetDecisionTaskScheduledEventAttributes() bool { return v != nil && v.DecisionTaskScheduledEventAttributes != nil } // GetDecisionTaskStartedEventAttributes returns the value of DecisionTaskStartedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetDecisionTaskStartedEventAttributes() (o *DecisionTaskStartedEventAttributes) { if v != nil && v.DecisionTaskStartedEventAttributes != nil { return v.DecisionTaskStartedEventAttributes } return } // IsSetDecisionTaskStartedEventAttributes returns true if DecisionTaskStartedEventAttributes is not nil. func (v *HistoryEvent) IsSetDecisionTaskStartedEventAttributes() bool { return v != nil && v.DecisionTaskStartedEventAttributes != nil } // GetDecisionTaskCompletedEventAttributes returns the value of DecisionTaskCompletedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetDecisionTaskCompletedEventAttributes() (o *DecisionTaskCompletedEventAttributes) { if v != nil && v.DecisionTaskCompletedEventAttributes != nil { return v.DecisionTaskCompletedEventAttributes } return } // IsSetDecisionTaskCompletedEventAttributes returns true if DecisionTaskCompletedEventAttributes is not nil. func (v *HistoryEvent) IsSetDecisionTaskCompletedEventAttributes() bool { return v != nil && v.DecisionTaskCompletedEventAttributes != nil } // GetDecisionTaskTimedOutEventAttributes returns the value of DecisionTaskTimedOutEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetDecisionTaskTimedOutEventAttributes() (o *DecisionTaskTimedOutEventAttributes) { if v != nil && v.DecisionTaskTimedOutEventAttributes != nil { return v.DecisionTaskTimedOutEventAttributes } return } // IsSetDecisionTaskTimedOutEventAttributes returns true if DecisionTaskTimedOutEventAttributes is not nil. func (v *HistoryEvent) IsSetDecisionTaskTimedOutEventAttributes() bool { return v != nil && v.DecisionTaskTimedOutEventAttributes != nil } // GetDecisionTaskFailedEventAttributes returns the value of DecisionTaskFailedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetDecisionTaskFailedEventAttributes() (o *DecisionTaskFailedEventAttributes) { if v != nil && v.DecisionTaskFailedEventAttributes != nil { return v.DecisionTaskFailedEventAttributes } return } // IsSetDecisionTaskFailedEventAttributes returns true if DecisionTaskFailedEventAttributes is not nil. func (v *HistoryEvent) IsSetDecisionTaskFailedEventAttributes() bool { return v != nil && v.DecisionTaskFailedEventAttributes != nil } // GetActivityTaskScheduledEventAttributes returns the value of ActivityTaskScheduledEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetActivityTaskScheduledEventAttributes() (o *ActivityTaskScheduledEventAttributes) { if v != nil && v.ActivityTaskScheduledEventAttributes != nil { return v.ActivityTaskScheduledEventAttributes } return } // IsSetActivityTaskScheduledEventAttributes returns true if ActivityTaskScheduledEventAttributes is not nil. func (v *HistoryEvent) IsSetActivityTaskScheduledEventAttributes() bool { return v != nil && v.ActivityTaskScheduledEventAttributes != nil } // GetActivityTaskStartedEventAttributes returns the value of ActivityTaskStartedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetActivityTaskStartedEventAttributes() (o *ActivityTaskStartedEventAttributes) { if v != nil && v.ActivityTaskStartedEventAttributes != nil { return v.ActivityTaskStartedEventAttributes } return } // IsSetActivityTaskStartedEventAttributes returns true if ActivityTaskStartedEventAttributes is not nil. func (v *HistoryEvent) IsSetActivityTaskStartedEventAttributes() bool { return v != nil && v.ActivityTaskStartedEventAttributes != nil } // GetActivityTaskCompletedEventAttributes returns the value of ActivityTaskCompletedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetActivityTaskCompletedEventAttributes() (o *ActivityTaskCompletedEventAttributes) { if v != nil && v.ActivityTaskCompletedEventAttributes != nil { return v.ActivityTaskCompletedEventAttributes } return } // IsSetActivityTaskCompletedEventAttributes returns true if ActivityTaskCompletedEventAttributes is not nil. func (v *HistoryEvent) IsSetActivityTaskCompletedEventAttributes() bool { return v != nil && v.ActivityTaskCompletedEventAttributes != nil } // GetActivityTaskFailedEventAttributes returns the value of ActivityTaskFailedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetActivityTaskFailedEventAttributes() (o *ActivityTaskFailedEventAttributes) { if v != nil && v.ActivityTaskFailedEventAttributes != nil { return v.ActivityTaskFailedEventAttributes } return } // IsSetActivityTaskFailedEventAttributes returns true if ActivityTaskFailedEventAttributes is not nil. func (v *HistoryEvent) IsSetActivityTaskFailedEventAttributes() bool { return v != nil && v.ActivityTaskFailedEventAttributes != nil } // GetActivityTaskTimedOutEventAttributes returns the value of ActivityTaskTimedOutEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetActivityTaskTimedOutEventAttributes() (o *ActivityTaskTimedOutEventAttributes) { if v != nil && v.ActivityTaskTimedOutEventAttributes != nil { return v.ActivityTaskTimedOutEventAttributes } return } // IsSetActivityTaskTimedOutEventAttributes returns true if ActivityTaskTimedOutEventAttributes is not nil. func (v *HistoryEvent) IsSetActivityTaskTimedOutEventAttributes() bool { return v != nil && v.ActivityTaskTimedOutEventAttributes != nil } // GetTimerStartedEventAttributes returns the value of TimerStartedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetTimerStartedEventAttributes() (o *TimerStartedEventAttributes) { if v != nil && v.TimerStartedEventAttributes != nil { return v.TimerStartedEventAttributes } return } // IsSetTimerStartedEventAttributes returns true if TimerStartedEventAttributes is not nil. func (v *HistoryEvent) IsSetTimerStartedEventAttributes() bool { return v != nil && v.TimerStartedEventAttributes != nil } // GetTimerFiredEventAttributes returns the value of TimerFiredEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetTimerFiredEventAttributes() (o *TimerFiredEventAttributes) { if v != nil && v.TimerFiredEventAttributes != nil { return v.TimerFiredEventAttributes } return } // IsSetTimerFiredEventAttributes returns true if TimerFiredEventAttributes is not nil. func (v *HistoryEvent) IsSetTimerFiredEventAttributes() bool { return v != nil && v.TimerFiredEventAttributes != nil } // GetActivityTaskCancelRequestedEventAttributes returns the value of ActivityTaskCancelRequestedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetActivityTaskCancelRequestedEventAttributes() (o *ActivityTaskCancelRequestedEventAttributes) { if v != nil && v.ActivityTaskCancelRequestedEventAttributes != nil { return v.ActivityTaskCancelRequestedEventAttributes } return } // IsSetActivityTaskCancelRequestedEventAttributes returns true if ActivityTaskCancelRequestedEventAttributes is not nil. func (v *HistoryEvent) IsSetActivityTaskCancelRequestedEventAttributes() bool { return v != nil && v.ActivityTaskCancelRequestedEventAttributes != nil } // GetRequestCancelActivityTaskFailedEventAttributes returns the value of RequestCancelActivityTaskFailedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetRequestCancelActivityTaskFailedEventAttributes() (o *RequestCancelActivityTaskFailedEventAttributes) { if v != nil && v.RequestCancelActivityTaskFailedEventAttributes != nil { return v.RequestCancelActivityTaskFailedEventAttributes } return } // IsSetRequestCancelActivityTaskFailedEventAttributes returns true if RequestCancelActivityTaskFailedEventAttributes is not nil. func (v *HistoryEvent) IsSetRequestCancelActivityTaskFailedEventAttributes() bool { return v != nil && v.RequestCancelActivityTaskFailedEventAttributes != nil } // GetActivityTaskCanceledEventAttributes returns the value of ActivityTaskCanceledEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetActivityTaskCanceledEventAttributes() (o *ActivityTaskCanceledEventAttributes) { if v != nil && v.ActivityTaskCanceledEventAttributes != nil { return v.ActivityTaskCanceledEventAttributes } return } // IsSetActivityTaskCanceledEventAttributes returns true if ActivityTaskCanceledEventAttributes is not nil. func (v *HistoryEvent) IsSetActivityTaskCanceledEventAttributes() bool { return v != nil && v.ActivityTaskCanceledEventAttributes != nil } // GetTimerCanceledEventAttributes returns the value of TimerCanceledEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetTimerCanceledEventAttributes() (o *TimerCanceledEventAttributes) { if v != nil && v.TimerCanceledEventAttributes != nil { return v.TimerCanceledEventAttributes } return } // IsSetTimerCanceledEventAttributes returns true if TimerCanceledEventAttributes is not nil. func (v *HistoryEvent) IsSetTimerCanceledEventAttributes() bool { return v != nil && v.TimerCanceledEventAttributes != nil } // GetCancelTimerFailedEventAttributes returns the value of CancelTimerFailedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetCancelTimerFailedEventAttributes() (o *CancelTimerFailedEventAttributes) { if v != nil && v.CancelTimerFailedEventAttributes != nil { return v.CancelTimerFailedEventAttributes } return } // IsSetCancelTimerFailedEventAttributes returns true if CancelTimerFailedEventAttributes is not nil. func (v *HistoryEvent) IsSetCancelTimerFailedEventAttributes() bool { return v != nil && v.CancelTimerFailedEventAttributes != nil } // GetMarkerRecordedEventAttributes returns the value of MarkerRecordedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetMarkerRecordedEventAttributes() (o *MarkerRecordedEventAttributes) { if v != nil && v.MarkerRecordedEventAttributes != nil { return v.MarkerRecordedEventAttributes } return } // IsSetMarkerRecordedEventAttributes returns true if MarkerRecordedEventAttributes is not nil. func (v *HistoryEvent) IsSetMarkerRecordedEventAttributes() bool { return v != nil && v.MarkerRecordedEventAttributes != nil } // GetWorkflowExecutionSignaledEventAttributes returns the value of WorkflowExecutionSignaledEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetWorkflowExecutionSignaledEventAttributes() (o *WorkflowExecutionSignaledEventAttributes) { if v != nil && v.WorkflowExecutionSignaledEventAttributes != nil { return v.WorkflowExecutionSignaledEventAttributes } return } // IsSetWorkflowExecutionSignaledEventAttributes returns true if WorkflowExecutionSignaledEventAttributes is not nil. func (v *HistoryEvent) IsSetWorkflowExecutionSignaledEventAttributes() bool { return v != nil && v.WorkflowExecutionSignaledEventAttributes != nil } // GetWorkflowExecutionTerminatedEventAttributes returns the value of WorkflowExecutionTerminatedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetWorkflowExecutionTerminatedEventAttributes() (o *WorkflowExecutionTerminatedEventAttributes) { if v != nil && v.WorkflowExecutionTerminatedEventAttributes != nil { return v.WorkflowExecutionTerminatedEventAttributes } return } // IsSetWorkflowExecutionTerminatedEventAttributes returns true if WorkflowExecutionTerminatedEventAttributes is not nil. func (v *HistoryEvent) IsSetWorkflowExecutionTerminatedEventAttributes() bool { return v != nil && v.WorkflowExecutionTerminatedEventAttributes != nil } // GetWorkflowExecutionCancelRequestedEventAttributes returns the value of WorkflowExecutionCancelRequestedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetWorkflowExecutionCancelRequestedEventAttributes() (o *WorkflowExecutionCancelRequestedEventAttributes) { if v != nil && v.WorkflowExecutionCancelRequestedEventAttributes != nil { return v.WorkflowExecutionCancelRequestedEventAttributes } return } // IsSetWorkflowExecutionCancelRequestedEventAttributes returns true if WorkflowExecutionCancelRequestedEventAttributes is not nil. func (v *HistoryEvent) IsSetWorkflowExecutionCancelRequestedEventAttributes() bool { return v != nil && v.WorkflowExecutionCancelRequestedEventAttributes != nil } // GetWorkflowExecutionCanceledEventAttributes returns the value of WorkflowExecutionCanceledEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetWorkflowExecutionCanceledEventAttributes() (o *WorkflowExecutionCanceledEventAttributes) { if v != nil && v.WorkflowExecutionCanceledEventAttributes != nil { return v.WorkflowExecutionCanceledEventAttributes } return } // IsSetWorkflowExecutionCanceledEventAttributes returns true if WorkflowExecutionCanceledEventAttributes is not nil. func (v *HistoryEvent) IsSetWorkflowExecutionCanceledEventAttributes() bool { return v != nil && v.WorkflowExecutionCanceledEventAttributes != nil } // GetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes returns the value of RequestCancelExternalWorkflowExecutionInitiatedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes() (o *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) { if v != nil && v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes != nil { return v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes } return } // IsSetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes returns true if RequestCancelExternalWorkflowExecutionInitiatedEventAttributes is not nil. func (v *HistoryEvent) IsSetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes() bool { return v != nil && v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes != nil } // GetRequestCancelExternalWorkflowExecutionFailedEventAttributes returns the value of RequestCancelExternalWorkflowExecutionFailedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetRequestCancelExternalWorkflowExecutionFailedEventAttributes() (o *RequestCancelExternalWorkflowExecutionFailedEventAttributes) { if v != nil && v.RequestCancelExternalWorkflowExecutionFailedEventAttributes != nil { return v.RequestCancelExternalWorkflowExecutionFailedEventAttributes } return } // IsSetRequestCancelExternalWorkflowExecutionFailedEventAttributes returns true if RequestCancelExternalWorkflowExecutionFailedEventAttributes is not nil. func (v *HistoryEvent) IsSetRequestCancelExternalWorkflowExecutionFailedEventAttributes() bool { return v != nil && v.RequestCancelExternalWorkflowExecutionFailedEventAttributes != nil } // GetExternalWorkflowExecutionCancelRequestedEventAttributes returns the value of ExternalWorkflowExecutionCancelRequestedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetExternalWorkflowExecutionCancelRequestedEventAttributes() (o *ExternalWorkflowExecutionCancelRequestedEventAttributes) { if v != nil && v.ExternalWorkflowExecutionCancelRequestedEventAttributes != nil { return v.ExternalWorkflowExecutionCancelRequestedEventAttributes } return } // IsSetExternalWorkflowExecutionCancelRequestedEventAttributes returns true if ExternalWorkflowExecutionCancelRequestedEventAttributes is not nil. func (v *HistoryEvent) IsSetExternalWorkflowExecutionCancelRequestedEventAttributes() bool { return v != nil && v.ExternalWorkflowExecutionCancelRequestedEventAttributes != nil } // GetWorkflowExecutionContinuedAsNewEventAttributes returns the value of WorkflowExecutionContinuedAsNewEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetWorkflowExecutionContinuedAsNewEventAttributes() (o *WorkflowExecutionContinuedAsNewEventAttributes) { if v != nil && v.WorkflowExecutionContinuedAsNewEventAttributes != nil { return v.WorkflowExecutionContinuedAsNewEventAttributes } return } // IsSetWorkflowExecutionContinuedAsNewEventAttributes returns true if WorkflowExecutionContinuedAsNewEventAttributes is not nil. func (v *HistoryEvent) IsSetWorkflowExecutionContinuedAsNewEventAttributes() bool { return v != nil && v.WorkflowExecutionContinuedAsNewEventAttributes != nil } // GetStartChildWorkflowExecutionInitiatedEventAttributes returns the value of StartChildWorkflowExecutionInitiatedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetStartChildWorkflowExecutionInitiatedEventAttributes() (o *StartChildWorkflowExecutionInitiatedEventAttributes) { if v != nil && v.StartChildWorkflowExecutionInitiatedEventAttributes != nil { return v.StartChildWorkflowExecutionInitiatedEventAttributes } return } // IsSetStartChildWorkflowExecutionInitiatedEventAttributes returns true if StartChildWorkflowExecutionInitiatedEventAttributes is not nil. func (v *HistoryEvent) IsSetStartChildWorkflowExecutionInitiatedEventAttributes() bool { return v != nil && v.StartChildWorkflowExecutionInitiatedEventAttributes != nil } // GetStartChildWorkflowExecutionFailedEventAttributes returns the value of StartChildWorkflowExecutionFailedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetStartChildWorkflowExecutionFailedEventAttributes() (o *StartChildWorkflowExecutionFailedEventAttributes) { if v != nil && v.StartChildWorkflowExecutionFailedEventAttributes != nil { return v.StartChildWorkflowExecutionFailedEventAttributes } return } // IsSetStartChildWorkflowExecutionFailedEventAttributes returns true if StartChildWorkflowExecutionFailedEventAttributes is not nil. func (v *HistoryEvent) IsSetStartChildWorkflowExecutionFailedEventAttributes() bool { return v != nil && v.StartChildWorkflowExecutionFailedEventAttributes != nil } // GetChildWorkflowExecutionStartedEventAttributes returns the value of ChildWorkflowExecutionStartedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetChildWorkflowExecutionStartedEventAttributes() (o *ChildWorkflowExecutionStartedEventAttributes) { if v != nil && v.ChildWorkflowExecutionStartedEventAttributes != nil { return v.ChildWorkflowExecutionStartedEventAttributes } return } // IsSetChildWorkflowExecutionStartedEventAttributes returns true if ChildWorkflowExecutionStartedEventAttributes is not nil. func (v *HistoryEvent) IsSetChildWorkflowExecutionStartedEventAttributes() bool { return v != nil && v.ChildWorkflowExecutionStartedEventAttributes != nil } // GetChildWorkflowExecutionCompletedEventAttributes returns the value of ChildWorkflowExecutionCompletedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetChildWorkflowExecutionCompletedEventAttributes() (o *ChildWorkflowExecutionCompletedEventAttributes) { if v != nil && v.ChildWorkflowExecutionCompletedEventAttributes != nil { return v.ChildWorkflowExecutionCompletedEventAttributes } return } // IsSetChildWorkflowExecutionCompletedEventAttributes returns true if ChildWorkflowExecutionCompletedEventAttributes is not nil. func (v *HistoryEvent) IsSetChildWorkflowExecutionCompletedEventAttributes() bool { return v != nil && v.ChildWorkflowExecutionCompletedEventAttributes != nil } // GetChildWorkflowExecutionFailedEventAttributes returns the value of ChildWorkflowExecutionFailedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetChildWorkflowExecutionFailedEventAttributes() (o *ChildWorkflowExecutionFailedEventAttributes) { if v != nil && v.ChildWorkflowExecutionFailedEventAttributes != nil { return v.ChildWorkflowExecutionFailedEventAttributes } return } // IsSetChildWorkflowExecutionFailedEventAttributes returns true if ChildWorkflowExecutionFailedEventAttributes is not nil. func (v *HistoryEvent) IsSetChildWorkflowExecutionFailedEventAttributes() bool { return v != nil && v.ChildWorkflowExecutionFailedEventAttributes != nil } // GetChildWorkflowExecutionCanceledEventAttributes returns the value of ChildWorkflowExecutionCanceledEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetChildWorkflowExecutionCanceledEventAttributes() (o *ChildWorkflowExecutionCanceledEventAttributes) { if v != nil && v.ChildWorkflowExecutionCanceledEventAttributes != nil { return v.ChildWorkflowExecutionCanceledEventAttributes } return } // IsSetChildWorkflowExecutionCanceledEventAttributes returns true if ChildWorkflowExecutionCanceledEventAttributes is not nil. func (v *HistoryEvent) IsSetChildWorkflowExecutionCanceledEventAttributes() bool { return v != nil && v.ChildWorkflowExecutionCanceledEventAttributes != nil } // GetChildWorkflowExecutionTimedOutEventAttributes returns the value of ChildWorkflowExecutionTimedOutEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetChildWorkflowExecutionTimedOutEventAttributes() (o *ChildWorkflowExecutionTimedOutEventAttributes) { if v != nil && v.ChildWorkflowExecutionTimedOutEventAttributes != nil { return v.ChildWorkflowExecutionTimedOutEventAttributes } return } // IsSetChildWorkflowExecutionTimedOutEventAttributes returns true if ChildWorkflowExecutionTimedOutEventAttributes is not nil. func (v *HistoryEvent) IsSetChildWorkflowExecutionTimedOutEventAttributes() bool { return v != nil && v.ChildWorkflowExecutionTimedOutEventAttributes != nil } // GetChildWorkflowExecutionTerminatedEventAttributes returns the value of ChildWorkflowExecutionTerminatedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetChildWorkflowExecutionTerminatedEventAttributes() (o *ChildWorkflowExecutionTerminatedEventAttributes) { if v != nil && v.ChildWorkflowExecutionTerminatedEventAttributes != nil { return v.ChildWorkflowExecutionTerminatedEventAttributes } return } // IsSetChildWorkflowExecutionTerminatedEventAttributes returns true if ChildWorkflowExecutionTerminatedEventAttributes is not nil. func (v *HistoryEvent) IsSetChildWorkflowExecutionTerminatedEventAttributes() bool { return v != nil && v.ChildWorkflowExecutionTerminatedEventAttributes != nil } // GetSignalExternalWorkflowExecutionInitiatedEventAttributes returns the value of SignalExternalWorkflowExecutionInitiatedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetSignalExternalWorkflowExecutionInitiatedEventAttributes() (o *SignalExternalWorkflowExecutionInitiatedEventAttributes) { if v != nil && v.SignalExternalWorkflowExecutionInitiatedEventAttributes != nil { return v.SignalExternalWorkflowExecutionInitiatedEventAttributes } return } // IsSetSignalExternalWorkflowExecutionInitiatedEventAttributes returns true if SignalExternalWorkflowExecutionInitiatedEventAttributes is not nil. func (v *HistoryEvent) IsSetSignalExternalWorkflowExecutionInitiatedEventAttributes() bool { return v != nil && v.SignalExternalWorkflowExecutionInitiatedEventAttributes != nil } // GetSignalExternalWorkflowExecutionFailedEventAttributes returns the value of SignalExternalWorkflowExecutionFailedEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetSignalExternalWorkflowExecutionFailedEventAttributes() (o *SignalExternalWorkflowExecutionFailedEventAttributes) { if v != nil && v.SignalExternalWorkflowExecutionFailedEventAttributes != nil { return v.SignalExternalWorkflowExecutionFailedEventAttributes } return } // IsSetSignalExternalWorkflowExecutionFailedEventAttributes returns true if SignalExternalWorkflowExecutionFailedEventAttributes is not nil. func (v *HistoryEvent) IsSetSignalExternalWorkflowExecutionFailedEventAttributes() bool { return v != nil && v.SignalExternalWorkflowExecutionFailedEventAttributes != nil } // GetExternalWorkflowExecutionSignaledEventAttributes returns the value of ExternalWorkflowExecutionSignaledEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetExternalWorkflowExecutionSignaledEventAttributes() (o *ExternalWorkflowExecutionSignaledEventAttributes) { if v != nil && v.ExternalWorkflowExecutionSignaledEventAttributes != nil { return v.ExternalWorkflowExecutionSignaledEventAttributes } return } // IsSetExternalWorkflowExecutionSignaledEventAttributes returns true if ExternalWorkflowExecutionSignaledEventAttributes is not nil. func (v *HistoryEvent) IsSetExternalWorkflowExecutionSignaledEventAttributes() bool { return v != nil && v.ExternalWorkflowExecutionSignaledEventAttributes != nil } // GetUpsertWorkflowSearchAttributesEventAttributes returns the value of UpsertWorkflowSearchAttributesEventAttributes if it is set or its // zero value if it is unset. func (v *HistoryEvent) GetUpsertWorkflowSearchAttributesEventAttributes() (o *UpsertWorkflowSearchAttributesEventAttributes) { if v != nil && v.UpsertWorkflowSearchAttributesEventAttributes != nil { return v.UpsertWorkflowSearchAttributesEventAttributes } return } // IsSetUpsertWorkflowSearchAttributesEventAttributes returns true if UpsertWorkflowSearchAttributesEventAttributes is not nil. func (v *HistoryEvent) IsSetUpsertWorkflowSearchAttributesEventAttributes() bool { return v != nil && v.UpsertWorkflowSearchAttributesEventAttributes != nil } type HistoryEventFilterType int32 const ( HistoryEventFilterTypeAllEvent HistoryEventFilterType = 0 HistoryEventFilterTypeCloseEvent HistoryEventFilterType = 1 ) // HistoryEventFilterType_Values returns all recognized values of HistoryEventFilterType. func HistoryEventFilterType_Values() []HistoryEventFilterType { return []HistoryEventFilterType{ HistoryEventFilterTypeAllEvent, HistoryEventFilterTypeCloseEvent, } } // UnmarshalText tries to decode HistoryEventFilterType from a byte slice // containing its name. // // var v HistoryEventFilterType // err := v.UnmarshalText([]byte("ALL_EVENT")) func (v *HistoryEventFilterType) UnmarshalText(value []byte) error { switch s := string(value); s { case "ALL_EVENT": *v = HistoryEventFilterTypeAllEvent return nil case "CLOSE_EVENT": *v = HistoryEventFilterTypeCloseEvent return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "HistoryEventFilterType", err) } *v = HistoryEventFilterType(val) return nil } } // MarshalText encodes HistoryEventFilterType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v HistoryEventFilterType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("ALL_EVENT"), nil case 1: return []byte("CLOSE_EVENT"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryEventFilterType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v HistoryEventFilterType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "ALL_EVENT") case 1: enc.AddString("name", "CLOSE_EVENT") } return nil } // Ptr returns a pointer to this enum value. func (v HistoryEventFilterType) Ptr() *HistoryEventFilterType { return &v } // Encode encodes HistoryEventFilterType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v HistoryEventFilterType // return v.Encode(sWriter) func (v HistoryEventFilterType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates HistoryEventFilterType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v HistoryEventFilterType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes HistoryEventFilterType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return HistoryEventFilterType(0), err // } // // var v HistoryEventFilterType // if err := v.FromWire(x); err != nil { // return HistoryEventFilterType(0), err // } // return v, nil func (v *HistoryEventFilterType) FromWire(w wire.Value) error { *v = (HistoryEventFilterType)(w.GetI32()) return nil } // Decode reads off the encoded HistoryEventFilterType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v HistoryEventFilterType // if err := v.Decode(sReader); err != nil { // return HistoryEventFilterType(0), err // } // return v, nil func (v *HistoryEventFilterType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (HistoryEventFilterType)(i) return nil } // String returns a readable string representation of HistoryEventFilterType. func (v HistoryEventFilterType) String() string { w := int32(v) switch w { case 0: return "ALL_EVENT" case 1: return "CLOSE_EVENT" } return fmt.Sprintf("HistoryEventFilterType(%d)", w) } // Equals returns true if this HistoryEventFilterType value matches the provided // value. func (v HistoryEventFilterType) Equals(rhs HistoryEventFilterType) bool { return v == rhs } // MarshalJSON serializes HistoryEventFilterType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v HistoryEventFilterType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"ALL_EVENT\""), nil case 1: return ([]byte)("\"CLOSE_EVENT\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode HistoryEventFilterType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *HistoryEventFilterType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "HistoryEventFilterType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "HistoryEventFilterType") } *v = (HistoryEventFilterType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "HistoryEventFilterType") } } type IndexedValueType int32 const ( IndexedValueTypeString IndexedValueType = 0 IndexedValueTypeKeyword IndexedValueType = 1 IndexedValueTypeInt IndexedValueType = 2 IndexedValueTypeDouble IndexedValueType = 3 IndexedValueTypeBool IndexedValueType = 4 IndexedValueTypeDatetime IndexedValueType = 5 ) // IndexedValueType_Values returns all recognized values of IndexedValueType. func IndexedValueType_Values() []IndexedValueType { return []IndexedValueType{ IndexedValueTypeString, IndexedValueTypeKeyword, IndexedValueTypeInt, IndexedValueTypeDouble, IndexedValueTypeBool, IndexedValueTypeDatetime, } } // UnmarshalText tries to decode IndexedValueType from a byte slice // containing its name. // // var v IndexedValueType // err := v.UnmarshalText([]byte("STRING")) func (v *IndexedValueType) UnmarshalText(value []byte) error { switch s := string(value); s { case "STRING": *v = IndexedValueTypeString return nil case "KEYWORD": *v = IndexedValueTypeKeyword return nil case "INT": *v = IndexedValueTypeInt return nil case "DOUBLE": *v = IndexedValueTypeDouble return nil case "BOOL": *v = IndexedValueTypeBool return nil case "DATETIME": *v = IndexedValueTypeDatetime return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "IndexedValueType", err) } *v = IndexedValueType(val) return nil } } // MarshalText encodes IndexedValueType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v IndexedValueType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("STRING"), nil case 1: return []byte("KEYWORD"), nil case 2: return []byte("INT"), nil case 3: return []byte("DOUBLE"), nil case 4: return []byte("BOOL"), nil case 5: return []byte("DATETIME"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of IndexedValueType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v IndexedValueType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "STRING") case 1: enc.AddString("name", "KEYWORD") case 2: enc.AddString("name", "INT") case 3: enc.AddString("name", "DOUBLE") case 4: enc.AddString("name", "BOOL") case 5: enc.AddString("name", "DATETIME") } return nil } // Ptr returns a pointer to this enum value. func (v IndexedValueType) Ptr() *IndexedValueType { return &v } // Encode encodes IndexedValueType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v IndexedValueType // return v.Encode(sWriter) func (v IndexedValueType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates IndexedValueType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v IndexedValueType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes IndexedValueType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return IndexedValueType(0), err // } // // var v IndexedValueType // if err := v.FromWire(x); err != nil { // return IndexedValueType(0), err // } // return v, nil func (v *IndexedValueType) FromWire(w wire.Value) error { *v = (IndexedValueType)(w.GetI32()) return nil } // Decode reads off the encoded IndexedValueType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v IndexedValueType // if err := v.Decode(sReader); err != nil { // return IndexedValueType(0), err // } // return v, nil func (v *IndexedValueType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (IndexedValueType)(i) return nil } // String returns a readable string representation of IndexedValueType. func (v IndexedValueType) String() string { w := int32(v) switch w { case 0: return "STRING" case 1: return "KEYWORD" case 2: return "INT" case 3: return "DOUBLE" case 4: return "BOOL" case 5: return "DATETIME" } return fmt.Sprintf("IndexedValueType(%d)", w) } // Equals returns true if this IndexedValueType value matches the provided // value. func (v IndexedValueType) Equals(rhs IndexedValueType) bool { return v == rhs } // MarshalJSON serializes IndexedValueType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v IndexedValueType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"STRING\""), nil case 1: return ([]byte)("\"KEYWORD\""), nil case 2: return ([]byte)("\"INT\""), nil case 3: return ([]byte)("\"DOUBLE\""), nil case 4: return ([]byte)("\"BOOL\""), nil case 5: return ([]byte)("\"DATETIME\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode IndexedValueType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *IndexedValueType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "IndexedValueType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "IndexedValueType") } *v = (IndexedValueType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "IndexedValueType") } } type InternalDataInconsistencyError struct { Message string `json:"message,required"` } // ToWire translates a InternalDataInconsistencyError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *InternalDataInconsistencyError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a InternalDataInconsistencyError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a InternalDataInconsistencyError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v InternalDataInconsistencyError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *InternalDataInconsistencyError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of InternalDataInconsistencyError is required") } return nil } // Encode serializes a InternalDataInconsistencyError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a InternalDataInconsistencyError struct could not be encoded. func (v *InternalDataInconsistencyError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a InternalDataInconsistencyError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a InternalDataInconsistencyError struct could not be generated from the wire // representation. func (v *InternalDataInconsistencyError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of InternalDataInconsistencyError is required") } return nil } // String returns a readable string representation of a InternalDataInconsistencyError // struct. func (v *InternalDataInconsistencyError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("InternalDataInconsistencyError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*InternalDataInconsistencyError) ErrorName() string { return "InternalDataInconsistencyError" } // Equals returns true if all the fields of this InternalDataInconsistencyError match the // provided InternalDataInconsistencyError. // // This function performs a deep comparison. func (v *InternalDataInconsistencyError) Equals(rhs *InternalDataInconsistencyError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of InternalDataInconsistencyError. func (v *InternalDataInconsistencyError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *InternalDataInconsistencyError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *InternalDataInconsistencyError) Error() string { return v.String() } type InternalServiceError struct { Message string `json:"message,required"` } // ToWire translates a InternalServiceError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *InternalServiceError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a InternalServiceError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a InternalServiceError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v InternalServiceError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *InternalServiceError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of InternalServiceError is required") } return nil } // Encode serializes a InternalServiceError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a InternalServiceError struct could not be encoded. func (v *InternalServiceError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a InternalServiceError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a InternalServiceError struct could not be generated from the wire // representation. func (v *InternalServiceError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of InternalServiceError is required") } return nil } // String returns a readable string representation of a InternalServiceError // struct. func (v *InternalServiceError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("InternalServiceError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*InternalServiceError) ErrorName() string { return "InternalServiceError" } // Equals returns true if all the fields of this InternalServiceError match the // provided InternalServiceError. // // This function performs a deep comparison. func (v *InternalServiceError) Equals(rhs *InternalServiceError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of InternalServiceError. func (v *InternalServiceError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *InternalServiceError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *InternalServiceError) Error() string { return v.String() } type IsolationGroupConfiguration struct { IsolationGroups []*IsolationGroupPartition `json:"isolationGroups,omitempty"` } type _List_IsolationGroupPartition_ValueList []*IsolationGroupPartition func (v _List_IsolationGroupPartition_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*IsolationGroupPartition', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_IsolationGroupPartition_ValueList) Size() int { return len(v) } func (_List_IsolationGroupPartition_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_IsolationGroupPartition_ValueList) Close() {} // ToWire translates a IsolationGroupConfiguration struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *IsolationGroupConfiguration) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.IsolationGroups != nil { w, err = wire.NewValueList(_List_IsolationGroupPartition_ValueList(v.IsolationGroups)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _IsolationGroupPartition_Read(w wire.Value) (*IsolationGroupPartition, error) { var v IsolationGroupPartition err := v.FromWire(w) return &v, err } func _List_IsolationGroupPartition_Read(l wire.ValueList) ([]*IsolationGroupPartition, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*IsolationGroupPartition, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _IsolationGroupPartition_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a IsolationGroupConfiguration struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a IsolationGroupConfiguration struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v IsolationGroupConfiguration // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *IsolationGroupConfiguration) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.IsolationGroups, err = _List_IsolationGroupPartition_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_IsolationGroupPartition_Encode(val []*IsolationGroupPartition, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*IsolationGroupPartition', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a IsolationGroupConfiguration struct directly into bytes, without going // through an intermediary type. // // An error is returned if a IsolationGroupConfiguration struct could not be encoded. func (v *IsolationGroupConfiguration) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.IsolationGroups != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_IsolationGroupPartition_Encode(v.IsolationGroups, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _IsolationGroupPartition_Decode(sr stream.Reader) (*IsolationGroupPartition, error) { var v IsolationGroupPartition err := v.Decode(sr) return &v, err } func _List_IsolationGroupPartition_Decode(sr stream.Reader) ([]*IsolationGroupPartition, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*IsolationGroupPartition, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _IsolationGroupPartition_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a IsolationGroupConfiguration struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a IsolationGroupConfiguration struct could not be generated from the wire // representation. func (v *IsolationGroupConfiguration) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.IsolationGroups, err = _List_IsolationGroupPartition_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a IsolationGroupConfiguration // struct. func (v *IsolationGroupConfiguration) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.IsolationGroups != nil { fields[i] = fmt.Sprintf("IsolationGroups: %v", v.IsolationGroups) i++ } return fmt.Sprintf("IsolationGroupConfiguration{%v}", strings.Join(fields[:i], ", ")) } func _List_IsolationGroupPartition_Equals(lhs, rhs []*IsolationGroupPartition) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this IsolationGroupConfiguration match the // provided IsolationGroupConfiguration. // // This function performs a deep comparison. func (v *IsolationGroupConfiguration) Equals(rhs *IsolationGroupConfiguration) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.IsolationGroups == nil && rhs.IsolationGroups == nil) || (v.IsolationGroups != nil && rhs.IsolationGroups != nil && _List_IsolationGroupPartition_Equals(v.IsolationGroups, rhs.IsolationGroups))) { return false } return true } type _List_IsolationGroupPartition_Zapper []*IsolationGroupPartition // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_IsolationGroupPartition_Zapper. func (l _List_IsolationGroupPartition_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of IsolationGroupConfiguration. func (v *IsolationGroupConfiguration) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.IsolationGroups != nil { err = multierr.Append(err, enc.AddArray("isolationGroups", (_List_IsolationGroupPartition_Zapper)(v.IsolationGroups))) } return err } // GetIsolationGroups returns the value of IsolationGroups if it is set or its // zero value if it is unset. func (v *IsolationGroupConfiguration) GetIsolationGroups() (o []*IsolationGroupPartition) { if v != nil && v.IsolationGroups != nil { return v.IsolationGroups } return } // IsSetIsolationGroups returns true if IsolationGroups is not nil. func (v *IsolationGroupConfiguration) IsSetIsolationGroups() bool { return v != nil && v.IsolationGroups != nil } type IsolationGroupMetrics struct { NewTasksPerSecond *float64 `json:"newTasksPerSecond,omitempty"` PollerCount *int64 `json:"pollerCount,omitempty"` } // ToWire translates a IsolationGroupMetrics struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *IsolationGroupMetrics) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.NewTasksPerSecond != nil { w, err = wire.NewValueDouble(*(v.NewTasksPerSecond)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.PollerCount != nil { w, err = wire.NewValueI64(*(v.PollerCount)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a IsolationGroupMetrics struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a IsolationGroupMetrics struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v IsolationGroupMetrics // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *IsolationGroupMetrics) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TDouble { var x float64 x, err = field.Value.GetDouble(), error(nil) v.NewTasksPerSecond = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.PollerCount = &x if err != nil { return err } } } } return nil } // Encode serializes a IsolationGroupMetrics struct directly into bytes, without going // through an intermediary type. // // An error is returned if a IsolationGroupMetrics struct could not be encoded. func (v *IsolationGroupMetrics) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.NewTasksPerSecond != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(*(v.NewTasksPerSecond)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PollerCount != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.PollerCount)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a IsolationGroupMetrics struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a IsolationGroupMetrics struct could not be generated from the wire // representation. func (v *IsolationGroupMetrics) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TDouble: var x float64 x, err = sr.ReadDouble() v.NewTasksPerSecond = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.PollerCount = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a IsolationGroupMetrics // struct. func (v *IsolationGroupMetrics) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.NewTasksPerSecond != nil { fields[i] = fmt.Sprintf("NewTasksPerSecond: %v", *(v.NewTasksPerSecond)) i++ } if v.PollerCount != nil { fields[i] = fmt.Sprintf("PollerCount: %v", *(v.PollerCount)) i++ } return fmt.Sprintf("IsolationGroupMetrics{%v}", strings.Join(fields[:i], ", ")) } func _Double_EqualsPtr(lhs, rhs *float64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this IsolationGroupMetrics match the // provided IsolationGroupMetrics. // // This function performs a deep comparison. func (v *IsolationGroupMetrics) Equals(rhs *IsolationGroupMetrics) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Double_EqualsPtr(v.NewTasksPerSecond, rhs.NewTasksPerSecond) { return false } if !_I64_EqualsPtr(v.PollerCount, rhs.PollerCount) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of IsolationGroupMetrics. func (v *IsolationGroupMetrics) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.NewTasksPerSecond != nil { enc.AddFloat64("newTasksPerSecond", *v.NewTasksPerSecond) } if v.PollerCount != nil { enc.AddInt64("pollerCount", *v.PollerCount) } return err } // GetNewTasksPerSecond returns the value of NewTasksPerSecond if it is set or its // zero value if it is unset. func (v *IsolationGroupMetrics) GetNewTasksPerSecond() (o float64) { if v != nil && v.NewTasksPerSecond != nil { return *v.NewTasksPerSecond } return } // IsSetNewTasksPerSecond returns true if NewTasksPerSecond is not nil. func (v *IsolationGroupMetrics) IsSetNewTasksPerSecond() bool { return v != nil && v.NewTasksPerSecond != nil } // GetPollerCount returns the value of PollerCount if it is set or its // zero value if it is unset. func (v *IsolationGroupMetrics) GetPollerCount() (o int64) { if v != nil && v.PollerCount != nil { return *v.PollerCount } return } // IsSetPollerCount returns true if PollerCount is not nil. func (v *IsolationGroupMetrics) IsSetPollerCount() bool { return v != nil && v.PollerCount != nil } type IsolationGroupPartition struct { Name *string `json:"name,omitempty"` State *IsolationGroupState `json:"state,omitempty"` } // ToWire translates a IsolationGroupPartition struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *IsolationGroupPartition) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.State != nil { w, err = v.State.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _IsolationGroupState_Read(w wire.Value) (IsolationGroupState, error) { var v IsolationGroupState err := v.FromWire(w) return v, err } // FromWire deserializes a IsolationGroupPartition struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a IsolationGroupPartition struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v IsolationGroupPartition // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *IsolationGroupPartition) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x IsolationGroupState x, err = _IsolationGroupState_Read(field.Value) v.State = &x if err != nil { return err } } } } return nil } // Encode serializes a IsolationGroupPartition struct directly into bytes, without going // through an intermediary type. // // An error is returned if a IsolationGroupPartition struct could not be encoded. func (v *IsolationGroupPartition) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.State != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := v.State.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _IsolationGroupState_Decode(sr stream.Reader) (IsolationGroupState, error) { var v IsolationGroupState err := v.Decode(sr) return v, err } // Decode deserializes a IsolationGroupPartition struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a IsolationGroupPartition struct could not be generated from the wire // representation. func (v *IsolationGroupPartition) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x IsolationGroupState x, err = _IsolationGroupState_Decode(sr) v.State = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a IsolationGroupPartition // struct. func (v *IsolationGroupPartition) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.State != nil { fields[i] = fmt.Sprintf("State: %v", *(v.State)) i++ } return fmt.Sprintf("IsolationGroupPartition{%v}", strings.Join(fields[:i], ", ")) } func _IsolationGroupState_EqualsPtr(lhs, rhs *IsolationGroupState) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this IsolationGroupPartition match the // provided IsolationGroupPartition. // // This function performs a deep comparison. func (v *IsolationGroupPartition) Equals(rhs *IsolationGroupPartition) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !_IsolationGroupState_EqualsPtr(v.State, rhs.State) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of IsolationGroupPartition. func (v *IsolationGroupPartition) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.State != nil { err = multierr.Append(err, enc.AddObject("state", *v.State)) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *IsolationGroupPartition) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *IsolationGroupPartition) IsSetName() bool { return v != nil && v.Name != nil } // GetState returns the value of State if it is set or its // zero value if it is unset. func (v *IsolationGroupPartition) GetState() (o IsolationGroupState) { if v != nil && v.State != nil { return *v.State } return } // IsSetState returns true if State is not nil. func (v *IsolationGroupPartition) IsSetState() bool { return v != nil && v.State != nil } type IsolationGroupState int32 const ( IsolationGroupStateInvalid IsolationGroupState = 0 IsolationGroupStateHealthy IsolationGroupState = 1 IsolationGroupStateDrained IsolationGroupState = 2 ) // IsolationGroupState_Values returns all recognized values of IsolationGroupState. func IsolationGroupState_Values() []IsolationGroupState { return []IsolationGroupState{ IsolationGroupStateInvalid, IsolationGroupStateHealthy, IsolationGroupStateDrained, } } // UnmarshalText tries to decode IsolationGroupState from a byte slice // containing its name. // // var v IsolationGroupState // err := v.UnmarshalText([]byte("INVALID")) func (v *IsolationGroupState) UnmarshalText(value []byte) error { switch s := string(value); s { case "INVALID": *v = IsolationGroupStateInvalid return nil case "HEALTHY": *v = IsolationGroupStateHealthy return nil case "DRAINED": *v = IsolationGroupStateDrained return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "IsolationGroupState", err) } *v = IsolationGroupState(val) return nil } } // MarshalText encodes IsolationGroupState to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v IsolationGroupState) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("INVALID"), nil case 1: return []byte("HEALTHY"), nil case 2: return []byte("DRAINED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of IsolationGroupState. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v IsolationGroupState) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "INVALID") case 1: enc.AddString("name", "HEALTHY") case 2: enc.AddString("name", "DRAINED") } return nil } // Ptr returns a pointer to this enum value. func (v IsolationGroupState) Ptr() *IsolationGroupState { return &v } // Encode encodes IsolationGroupState directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v IsolationGroupState // return v.Encode(sWriter) func (v IsolationGroupState) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates IsolationGroupState into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v IsolationGroupState) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes IsolationGroupState from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return IsolationGroupState(0), err // } // // var v IsolationGroupState // if err := v.FromWire(x); err != nil { // return IsolationGroupState(0), err // } // return v, nil func (v *IsolationGroupState) FromWire(w wire.Value) error { *v = (IsolationGroupState)(w.GetI32()) return nil } // Decode reads off the encoded IsolationGroupState directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v IsolationGroupState // if err := v.Decode(sReader); err != nil { // return IsolationGroupState(0), err // } // return v, nil func (v *IsolationGroupState) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (IsolationGroupState)(i) return nil } // String returns a readable string representation of IsolationGroupState. func (v IsolationGroupState) String() string { w := int32(v) switch w { case 0: return "INVALID" case 1: return "HEALTHY" case 2: return "DRAINED" } return fmt.Sprintf("IsolationGroupState(%d)", w) } // Equals returns true if this IsolationGroupState value matches the provided // value. func (v IsolationGroupState) Equals(rhs IsolationGroupState) bool { return v == rhs } // MarshalJSON serializes IsolationGroupState into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v IsolationGroupState) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"INVALID\""), nil case 1: return ([]byte)("\"HEALTHY\""), nil case 2: return ([]byte)("\"DRAINED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode IsolationGroupState from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *IsolationGroupState) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "IsolationGroupState") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "IsolationGroupState") } *v = (IsolationGroupState)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "IsolationGroupState") } } type LimitExceededError struct { Message string `json:"message,required"` } // ToWire translates a LimitExceededError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *LimitExceededError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a LimitExceededError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a LimitExceededError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v LimitExceededError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *LimitExceededError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of LimitExceededError is required") } return nil } // Encode serializes a LimitExceededError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a LimitExceededError struct could not be encoded. func (v *LimitExceededError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a LimitExceededError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a LimitExceededError struct could not be generated from the wire // representation. func (v *LimitExceededError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of LimitExceededError is required") } return nil } // String returns a readable string representation of a LimitExceededError // struct. func (v *LimitExceededError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("LimitExceededError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*LimitExceededError) ErrorName() string { return "LimitExceededError" } // Equals returns true if all the fields of this LimitExceededError match the // provided LimitExceededError. // // This function performs a deep comparison. func (v *LimitExceededError) Equals(rhs *LimitExceededError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of LimitExceededError. func (v *LimitExceededError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *LimitExceededError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *LimitExceededError) Error() string { return v.String() } type ListArchivedWorkflowExecutionsRequest struct { Domain *string `json:"domain,omitempty"` PageSize *int32 `json:"pageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` Query *string `json:"query,omitempty"` } // ToWire translates a ListArchivedWorkflowExecutionsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListArchivedWorkflowExecutionsRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.PageSize != nil { w, err = wire.NewValueI32(*(v.PageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Query != nil { w, err = wire.NewValueString(*(v.Query)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListArchivedWorkflowExecutionsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListArchivedWorkflowExecutionsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListArchivedWorkflowExecutionsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListArchivedWorkflowExecutionsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.PageSize = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Query = &x if err != nil { return err } } } } return nil } // Encode serializes a ListArchivedWorkflowExecutionsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListArchivedWorkflowExecutionsRequest struct could not be encoded. func (v *ListArchivedWorkflowExecutionsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.PageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Query != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Query)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListArchivedWorkflowExecutionsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListArchivedWorkflowExecutionsRequest struct could not be generated from the wire // representation. func (v *ListArchivedWorkflowExecutionsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.PageSize = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Query = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListArchivedWorkflowExecutionsRequest // struct. func (v *ListArchivedWorkflowExecutionsRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.PageSize != nil { fields[i] = fmt.Sprintf("PageSize: %v", *(v.PageSize)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.Query != nil { fields[i] = fmt.Sprintf("Query: %v", *(v.Query)) i++ } return fmt.Sprintf("ListArchivedWorkflowExecutionsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListArchivedWorkflowExecutionsRequest match the // provided ListArchivedWorkflowExecutionsRequest. // // This function performs a deep comparison. func (v *ListArchivedWorkflowExecutionsRequest) Equals(rhs *ListArchivedWorkflowExecutionsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_I32_EqualsPtr(v.PageSize, rhs.PageSize) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !_String_EqualsPtr(v.Query, rhs.Query) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListArchivedWorkflowExecutionsRequest. func (v *ListArchivedWorkflowExecutionsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.PageSize != nil { enc.AddInt32("pageSize", *v.PageSize) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.Query != nil { enc.AddString("query", *v.Query) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ListArchivedWorkflowExecutionsRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ListArchivedWorkflowExecutionsRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetPageSize returns the value of PageSize if it is set or its // zero value if it is unset. func (v *ListArchivedWorkflowExecutionsRequest) GetPageSize() (o int32) { if v != nil && v.PageSize != nil { return *v.PageSize } return } // IsSetPageSize returns true if PageSize is not nil. func (v *ListArchivedWorkflowExecutionsRequest) IsSetPageSize() bool { return v != nil && v.PageSize != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListArchivedWorkflowExecutionsRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListArchivedWorkflowExecutionsRequest) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetQuery returns the value of Query if it is set or its // zero value if it is unset. func (v *ListArchivedWorkflowExecutionsRequest) GetQuery() (o string) { if v != nil && v.Query != nil { return *v.Query } return } // IsSetQuery returns true if Query is not nil. func (v *ListArchivedWorkflowExecutionsRequest) IsSetQuery() bool { return v != nil && v.Query != nil } type ListArchivedWorkflowExecutionsResponse struct { Executions []*WorkflowExecutionInfo `json:"executions,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } type _List_WorkflowExecutionInfo_ValueList []*WorkflowExecutionInfo func (v _List_WorkflowExecutionInfo_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*WorkflowExecutionInfo', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_WorkflowExecutionInfo_ValueList) Size() int { return len(v) } func (_List_WorkflowExecutionInfo_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_WorkflowExecutionInfo_ValueList) Close() {} // ToWire translates a ListArchivedWorkflowExecutionsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListArchivedWorkflowExecutionsResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Executions != nil { w, err = wire.NewValueList(_List_WorkflowExecutionInfo_ValueList(v.Executions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _List_WorkflowExecutionInfo_Read(l wire.ValueList) ([]*WorkflowExecutionInfo, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*WorkflowExecutionInfo, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _WorkflowExecutionInfo_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a ListArchivedWorkflowExecutionsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListArchivedWorkflowExecutionsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListArchivedWorkflowExecutionsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListArchivedWorkflowExecutionsResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Executions, err = _List_WorkflowExecutionInfo_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } func _List_WorkflowExecutionInfo_Encode(val []*WorkflowExecutionInfo, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*WorkflowExecutionInfo', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a ListArchivedWorkflowExecutionsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListArchivedWorkflowExecutionsResponse struct could not be encoded. func (v *ListArchivedWorkflowExecutionsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Executions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_WorkflowExecutionInfo_Encode(v.Executions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _List_WorkflowExecutionInfo_Decode(sr stream.Reader) ([]*WorkflowExecutionInfo, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*WorkflowExecutionInfo, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _WorkflowExecutionInfo_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ListArchivedWorkflowExecutionsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListArchivedWorkflowExecutionsResponse struct could not be generated from the wire // representation. func (v *ListArchivedWorkflowExecutionsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Executions, err = _List_WorkflowExecutionInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListArchivedWorkflowExecutionsResponse // struct. func (v *ListArchivedWorkflowExecutionsResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Executions != nil { fields[i] = fmt.Sprintf("Executions: %v", v.Executions) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("ListArchivedWorkflowExecutionsResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_WorkflowExecutionInfo_Equals(lhs, rhs []*WorkflowExecutionInfo) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this ListArchivedWorkflowExecutionsResponse match the // provided ListArchivedWorkflowExecutionsResponse. // // This function performs a deep comparison. func (v *ListArchivedWorkflowExecutionsResponse) Equals(rhs *ListArchivedWorkflowExecutionsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Executions == nil && rhs.Executions == nil) || (v.Executions != nil && rhs.Executions != nil && _List_WorkflowExecutionInfo_Equals(v.Executions, rhs.Executions))) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } type _List_WorkflowExecutionInfo_Zapper []*WorkflowExecutionInfo // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_WorkflowExecutionInfo_Zapper. func (l _List_WorkflowExecutionInfo_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListArchivedWorkflowExecutionsResponse. func (v *ListArchivedWorkflowExecutionsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Executions != nil { err = multierr.Append(err, enc.AddArray("executions", (_List_WorkflowExecutionInfo_Zapper)(v.Executions))) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetExecutions returns the value of Executions if it is set or its // zero value if it is unset. func (v *ListArchivedWorkflowExecutionsResponse) GetExecutions() (o []*WorkflowExecutionInfo) { if v != nil && v.Executions != nil { return v.Executions } return } // IsSetExecutions returns true if Executions is not nil. func (v *ListArchivedWorkflowExecutionsResponse) IsSetExecutions() bool { return v != nil && v.Executions != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListArchivedWorkflowExecutionsResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListArchivedWorkflowExecutionsResponse) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type ListClosedWorkflowExecutionsRequest struct { Domain *string `json:"domain,omitempty"` MaximumPageSize *int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` StartTimeFilter *StartTimeFilter `json:"StartTimeFilter,omitempty"` ExecutionFilter *WorkflowExecutionFilter `json:"executionFilter,omitempty"` TypeFilter *WorkflowTypeFilter `json:"typeFilter,omitempty"` StatusFilter *WorkflowExecutionCloseStatus `json:"statusFilter,omitempty"` } // ToWire translates a ListClosedWorkflowExecutionsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListClosedWorkflowExecutionsRequest) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.MaximumPageSize != nil { w, err = wire.NewValueI32(*(v.MaximumPageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.StartTimeFilter != nil { w, err = v.StartTimeFilter.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ExecutionFilter != nil { w, err = v.ExecutionFilter.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.TypeFilter != nil { w, err = v.TypeFilter.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.StatusFilter != nil { w, err = v.StatusFilter.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StartTimeFilter_Read(w wire.Value) (*StartTimeFilter, error) { var v StartTimeFilter err := v.FromWire(w) return &v, err } func _WorkflowExecutionFilter_Read(w wire.Value) (*WorkflowExecutionFilter, error) { var v WorkflowExecutionFilter err := v.FromWire(w) return &v, err } func _WorkflowTypeFilter_Read(w wire.Value) (*WorkflowTypeFilter, error) { var v WorkflowTypeFilter err := v.FromWire(w) return &v, err } func _WorkflowExecutionCloseStatus_Read(w wire.Value) (WorkflowExecutionCloseStatus, error) { var v WorkflowExecutionCloseStatus err := v.FromWire(w) return v, err } // FromWire deserializes a ListClosedWorkflowExecutionsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListClosedWorkflowExecutionsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListClosedWorkflowExecutionsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListClosedWorkflowExecutionsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.MaximumPageSize = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.StartTimeFilter, err = _StartTimeFilter_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.ExecutionFilter, err = _WorkflowExecutionFilter_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.TypeFilter, err = _WorkflowTypeFilter_Read(field.Value) if err != nil { return err } } case 70: if field.Value.Type() == wire.TI32 { var x WorkflowExecutionCloseStatus x, err = _WorkflowExecutionCloseStatus_Read(field.Value) v.StatusFilter = &x if err != nil { return err } } } } return nil } // Encode serializes a ListClosedWorkflowExecutionsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListClosedWorkflowExecutionsRequest struct could not be encoded. func (v *ListClosedWorkflowExecutionsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MaximumPageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.MaximumPageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartTimeFilter != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.StartTimeFilter.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionFilter != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.ExecutionFilter.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TypeFilter != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.TypeFilter.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StatusFilter != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI32}); err != nil { return err } if err := v.StatusFilter.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _StartTimeFilter_Decode(sr stream.Reader) (*StartTimeFilter, error) { var v StartTimeFilter err := v.Decode(sr) return &v, err } func _WorkflowExecutionFilter_Decode(sr stream.Reader) (*WorkflowExecutionFilter, error) { var v WorkflowExecutionFilter err := v.Decode(sr) return &v, err } func _WorkflowTypeFilter_Decode(sr stream.Reader) (*WorkflowTypeFilter, error) { var v WorkflowTypeFilter err := v.Decode(sr) return &v, err } func _WorkflowExecutionCloseStatus_Decode(sr stream.Reader) (WorkflowExecutionCloseStatus, error) { var v WorkflowExecutionCloseStatus err := v.Decode(sr) return v, err } // Decode deserializes a ListClosedWorkflowExecutionsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListClosedWorkflowExecutionsRequest struct could not be generated from the wire // representation. func (v *ListClosedWorkflowExecutionsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.MaximumPageSize = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.StartTimeFilter, err = _StartTimeFilter_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.ExecutionFilter, err = _WorkflowExecutionFilter_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.TypeFilter, err = _WorkflowTypeFilter_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI32: var x WorkflowExecutionCloseStatus x, err = _WorkflowExecutionCloseStatus_Decode(sr) v.StatusFilter = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListClosedWorkflowExecutionsRequest // struct. func (v *ListClosedWorkflowExecutionsRequest) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.MaximumPageSize != nil { fields[i] = fmt.Sprintf("MaximumPageSize: %v", *(v.MaximumPageSize)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.StartTimeFilter != nil { fields[i] = fmt.Sprintf("StartTimeFilter: %v", v.StartTimeFilter) i++ } if v.ExecutionFilter != nil { fields[i] = fmt.Sprintf("ExecutionFilter: %v", v.ExecutionFilter) i++ } if v.TypeFilter != nil { fields[i] = fmt.Sprintf("TypeFilter: %v", v.TypeFilter) i++ } if v.StatusFilter != nil { fields[i] = fmt.Sprintf("StatusFilter: %v", *(v.StatusFilter)) i++ } return fmt.Sprintf("ListClosedWorkflowExecutionsRequest{%v}", strings.Join(fields[:i], ", ")) } func _WorkflowExecutionCloseStatus_EqualsPtr(lhs, rhs *WorkflowExecutionCloseStatus) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this ListClosedWorkflowExecutionsRequest match the // provided ListClosedWorkflowExecutionsRequest. // // This function performs a deep comparison. func (v *ListClosedWorkflowExecutionsRequest) Equals(rhs *ListClosedWorkflowExecutionsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_I32_EqualsPtr(v.MaximumPageSize, rhs.MaximumPageSize) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !((v.StartTimeFilter == nil && rhs.StartTimeFilter == nil) || (v.StartTimeFilter != nil && rhs.StartTimeFilter != nil && v.StartTimeFilter.Equals(rhs.StartTimeFilter))) { return false } if !((v.ExecutionFilter == nil && rhs.ExecutionFilter == nil) || (v.ExecutionFilter != nil && rhs.ExecutionFilter != nil && v.ExecutionFilter.Equals(rhs.ExecutionFilter))) { return false } if !((v.TypeFilter == nil && rhs.TypeFilter == nil) || (v.TypeFilter != nil && rhs.TypeFilter != nil && v.TypeFilter.Equals(rhs.TypeFilter))) { return false } if !_WorkflowExecutionCloseStatus_EqualsPtr(v.StatusFilter, rhs.StatusFilter) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListClosedWorkflowExecutionsRequest. func (v *ListClosedWorkflowExecutionsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.MaximumPageSize != nil { enc.AddInt32("maximumPageSize", *v.MaximumPageSize) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.StartTimeFilter != nil { err = multierr.Append(err, enc.AddObject("StartTimeFilter", v.StartTimeFilter)) } if v.ExecutionFilter != nil { err = multierr.Append(err, enc.AddObject("executionFilter", v.ExecutionFilter)) } if v.TypeFilter != nil { err = multierr.Append(err, enc.AddObject("typeFilter", v.TypeFilter)) } if v.StatusFilter != nil { err = multierr.Append(err, enc.AddObject("statusFilter", *v.StatusFilter)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ListClosedWorkflowExecutionsRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ListClosedWorkflowExecutionsRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetMaximumPageSize returns the value of MaximumPageSize if it is set or its // zero value if it is unset. func (v *ListClosedWorkflowExecutionsRequest) GetMaximumPageSize() (o int32) { if v != nil && v.MaximumPageSize != nil { return *v.MaximumPageSize } return } // IsSetMaximumPageSize returns true if MaximumPageSize is not nil. func (v *ListClosedWorkflowExecutionsRequest) IsSetMaximumPageSize() bool { return v != nil && v.MaximumPageSize != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListClosedWorkflowExecutionsRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListClosedWorkflowExecutionsRequest) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetStartTimeFilter returns the value of StartTimeFilter if it is set or its // zero value if it is unset. func (v *ListClosedWorkflowExecutionsRequest) GetStartTimeFilter() (o *StartTimeFilter) { if v != nil && v.StartTimeFilter != nil { return v.StartTimeFilter } return } // IsSetStartTimeFilter returns true if StartTimeFilter is not nil. func (v *ListClosedWorkflowExecutionsRequest) IsSetStartTimeFilter() bool { return v != nil && v.StartTimeFilter != nil } // GetExecutionFilter returns the value of ExecutionFilter if it is set or its // zero value if it is unset. func (v *ListClosedWorkflowExecutionsRequest) GetExecutionFilter() (o *WorkflowExecutionFilter) { if v != nil && v.ExecutionFilter != nil { return v.ExecutionFilter } return } // IsSetExecutionFilter returns true if ExecutionFilter is not nil. func (v *ListClosedWorkflowExecutionsRequest) IsSetExecutionFilter() bool { return v != nil && v.ExecutionFilter != nil } // GetTypeFilter returns the value of TypeFilter if it is set or its // zero value if it is unset. func (v *ListClosedWorkflowExecutionsRequest) GetTypeFilter() (o *WorkflowTypeFilter) { if v != nil && v.TypeFilter != nil { return v.TypeFilter } return } // IsSetTypeFilter returns true if TypeFilter is not nil. func (v *ListClosedWorkflowExecutionsRequest) IsSetTypeFilter() bool { return v != nil && v.TypeFilter != nil } // GetStatusFilter returns the value of StatusFilter if it is set or its // zero value if it is unset. func (v *ListClosedWorkflowExecutionsRequest) GetStatusFilter() (o WorkflowExecutionCloseStatus) { if v != nil && v.StatusFilter != nil { return *v.StatusFilter } return } // IsSetStatusFilter returns true if StatusFilter is not nil. func (v *ListClosedWorkflowExecutionsRequest) IsSetStatusFilter() bool { return v != nil && v.StatusFilter != nil } type ListClosedWorkflowExecutionsResponse struct { Executions []*WorkflowExecutionInfo `json:"executions,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // ToWire translates a ListClosedWorkflowExecutionsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListClosedWorkflowExecutionsResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Executions != nil { w, err = wire.NewValueList(_List_WorkflowExecutionInfo_ValueList(v.Executions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListClosedWorkflowExecutionsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListClosedWorkflowExecutionsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListClosedWorkflowExecutionsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListClosedWorkflowExecutionsResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Executions, err = _List_WorkflowExecutionInfo_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a ListClosedWorkflowExecutionsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListClosedWorkflowExecutionsResponse struct could not be encoded. func (v *ListClosedWorkflowExecutionsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Executions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_WorkflowExecutionInfo_Encode(v.Executions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListClosedWorkflowExecutionsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListClosedWorkflowExecutionsResponse struct could not be generated from the wire // representation. func (v *ListClosedWorkflowExecutionsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Executions, err = _List_WorkflowExecutionInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListClosedWorkflowExecutionsResponse // struct. func (v *ListClosedWorkflowExecutionsResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Executions != nil { fields[i] = fmt.Sprintf("Executions: %v", v.Executions) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("ListClosedWorkflowExecutionsResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListClosedWorkflowExecutionsResponse match the // provided ListClosedWorkflowExecutionsResponse. // // This function performs a deep comparison. func (v *ListClosedWorkflowExecutionsResponse) Equals(rhs *ListClosedWorkflowExecutionsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Executions == nil && rhs.Executions == nil) || (v.Executions != nil && rhs.Executions != nil && _List_WorkflowExecutionInfo_Equals(v.Executions, rhs.Executions))) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListClosedWorkflowExecutionsResponse. func (v *ListClosedWorkflowExecutionsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Executions != nil { err = multierr.Append(err, enc.AddArray("executions", (_List_WorkflowExecutionInfo_Zapper)(v.Executions))) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetExecutions returns the value of Executions if it is set or its // zero value if it is unset. func (v *ListClosedWorkflowExecutionsResponse) GetExecutions() (o []*WorkflowExecutionInfo) { if v != nil && v.Executions != nil { return v.Executions } return } // IsSetExecutions returns true if Executions is not nil. func (v *ListClosedWorkflowExecutionsResponse) IsSetExecutions() bool { return v != nil && v.Executions != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListClosedWorkflowExecutionsResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListClosedWorkflowExecutionsResponse) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type ListDomainsRequest struct { PageSize *int32 `json:"pageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // ToWire translates a ListDomainsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListDomainsRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.PageSize != nil { w, err = wire.NewValueI32(*(v.PageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListDomainsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListDomainsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListDomainsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListDomainsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.PageSize = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a ListDomainsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListDomainsRequest struct could not be encoded. func (v *ListDomainsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.PageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.PageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListDomainsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListDomainsRequest struct could not be generated from the wire // representation. func (v *ListDomainsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.PageSize = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListDomainsRequest // struct. func (v *ListDomainsRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.PageSize != nil { fields[i] = fmt.Sprintf("PageSize: %v", *(v.PageSize)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("ListDomainsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListDomainsRequest match the // provided ListDomainsRequest. // // This function performs a deep comparison. func (v *ListDomainsRequest) Equals(rhs *ListDomainsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.PageSize, rhs.PageSize) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListDomainsRequest. func (v *ListDomainsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.PageSize != nil { enc.AddInt32("pageSize", *v.PageSize) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetPageSize returns the value of PageSize if it is set or its // zero value if it is unset. func (v *ListDomainsRequest) GetPageSize() (o int32) { if v != nil && v.PageSize != nil { return *v.PageSize } return } // IsSetPageSize returns true if PageSize is not nil. func (v *ListDomainsRequest) IsSetPageSize() bool { return v != nil && v.PageSize != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListDomainsRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListDomainsRequest) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type ListDomainsResponse struct { Domains []*DescribeDomainResponse `json:"domains,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } type _List_DescribeDomainResponse_ValueList []*DescribeDomainResponse func (v _List_DescribeDomainResponse_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*DescribeDomainResponse', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_DescribeDomainResponse_ValueList) Size() int { return len(v) } func (_List_DescribeDomainResponse_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_DescribeDomainResponse_ValueList) Close() {} // ToWire translates a ListDomainsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListDomainsResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domains != nil { w, err = wire.NewValueList(_List_DescribeDomainResponse_ValueList(v.Domains)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _DescribeDomainResponse_Read(w wire.Value) (*DescribeDomainResponse, error) { var v DescribeDomainResponse err := v.FromWire(w) return &v, err } func _List_DescribeDomainResponse_Read(l wire.ValueList) ([]*DescribeDomainResponse, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*DescribeDomainResponse, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _DescribeDomainResponse_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a ListDomainsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListDomainsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListDomainsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListDomainsResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Domains, err = _List_DescribeDomainResponse_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } func _List_DescribeDomainResponse_Encode(val []*DescribeDomainResponse, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*DescribeDomainResponse', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a ListDomainsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListDomainsResponse struct could not be encoded. func (v *ListDomainsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domains != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_DescribeDomainResponse_Encode(v.Domains, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _DescribeDomainResponse_Decode(sr stream.Reader) (*DescribeDomainResponse, error) { var v DescribeDomainResponse err := v.Decode(sr) return &v, err } func _List_DescribeDomainResponse_Decode(sr stream.Reader) ([]*DescribeDomainResponse, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*DescribeDomainResponse, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _DescribeDomainResponse_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ListDomainsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListDomainsResponse struct could not be generated from the wire // representation. func (v *ListDomainsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Domains, err = _List_DescribeDomainResponse_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListDomainsResponse // struct. func (v *ListDomainsResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domains != nil { fields[i] = fmt.Sprintf("Domains: %v", v.Domains) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("ListDomainsResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_DescribeDomainResponse_Equals(lhs, rhs []*DescribeDomainResponse) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this ListDomainsResponse match the // provided ListDomainsResponse. // // This function performs a deep comparison. func (v *ListDomainsResponse) Equals(rhs *ListDomainsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Domains == nil && rhs.Domains == nil) || (v.Domains != nil && rhs.Domains != nil && _List_DescribeDomainResponse_Equals(v.Domains, rhs.Domains))) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } type _List_DescribeDomainResponse_Zapper []*DescribeDomainResponse // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_DescribeDomainResponse_Zapper. func (l _List_DescribeDomainResponse_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListDomainsResponse. func (v *ListDomainsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domains != nil { err = multierr.Append(err, enc.AddArray("domains", (_List_DescribeDomainResponse_Zapper)(v.Domains))) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetDomains returns the value of Domains if it is set or its // zero value if it is unset. func (v *ListDomainsResponse) GetDomains() (o []*DescribeDomainResponse) { if v != nil && v.Domains != nil { return v.Domains } return } // IsSetDomains returns true if Domains is not nil. func (v *ListDomainsResponse) IsSetDomains() bool { return v != nil && v.Domains != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListDomainsResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListDomainsResponse) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type ListFailoverHistoryRequest struct { Filters *ListFailoverHistoryRequestFilters `json:"filters,omitempty"` Pagination *PaginationOptions `json:"pagination,omitempty"` } // ToWire translates a ListFailoverHistoryRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListFailoverHistoryRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Filters != nil { w, err = v.Filters.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Pagination != nil { w, err = v.Pagination.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ListFailoverHistoryRequestFilters_Read(w wire.Value) (*ListFailoverHistoryRequestFilters, error) { var v ListFailoverHistoryRequestFilters err := v.FromWire(w) return &v, err } func _PaginationOptions_Read(w wire.Value) (*PaginationOptions, error) { var v PaginationOptions err := v.FromWire(w) return &v, err } // FromWire deserializes a ListFailoverHistoryRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListFailoverHistoryRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListFailoverHistoryRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListFailoverHistoryRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Filters, err = _ListFailoverHistoryRequestFilters_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Pagination, err = _PaginationOptions_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ListFailoverHistoryRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListFailoverHistoryRequest struct could not be encoded. func (v *ListFailoverHistoryRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Filters != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Filters.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Pagination != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Pagination.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ListFailoverHistoryRequestFilters_Decode(sr stream.Reader) (*ListFailoverHistoryRequestFilters, error) { var v ListFailoverHistoryRequestFilters err := v.Decode(sr) return &v, err } func _PaginationOptions_Decode(sr stream.Reader) (*PaginationOptions, error) { var v PaginationOptions err := v.Decode(sr) return &v, err } // Decode deserializes a ListFailoverHistoryRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListFailoverHistoryRequest struct could not be generated from the wire // representation. func (v *ListFailoverHistoryRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Filters, err = _ListFailoverHistoryRequestFilters_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Pagination, err = _PaginationOptions_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListFailoverHistoryRequest // struct. func (v *ListFailoverHistoryRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Filters != nil { fields[i] = fmt.Sprintf("Filters: %v", v.Filters) i++ } if v.Pagination != nil { fields[i] = fmt.Sprintf("Pagination: %v", v.Pagination) i++ } return fmt.Sprintf("ListFailoverHistoryRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListFailoverHistoryRequest match the // provided ListFailoverHistoryRequest. // // This function performs a deep comparison. func (v *ListFailoverHistoryRequest) Equals(rhs *ListFailoverHistoryRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Filters == nil && rhs.Filters == nil) || (v.Filters != nil && rhs.Filters != nil && v.Filters.Equals(rhs.Filters))) { return false } if !((v.Pagination == nil && rhs.Pagination == nil) || (v.Pagination != nil && rhs.Pagination != nil && v.Pagination.Equals(rhs.Pagination))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListFailoverHistoryRequest. func (v *ListFailoverHistoryRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Filters != nil { err = multierr.Append(err, enc.AddObject("filters", v.Filters)) } if v.Pagination != nil { err = multierr.Append(err, enc.AddObject("pagination", v.Pagination)) } return err } // GetFilters returns the value of Filters if it is set or its // zero value if it is unset. func (v *ListFailoverHistoryRequest) GetFilters() (o *ListFailoverHistoryRequestFilters) { if v != nil && v.Filters != nil { return v.Filters } return } // IsSetFilters returns true if Filters is not nil. func (v *ListFailoverHistoryRequest) IsSetFilters() bool { return v != nil && v.Filters != nil } // GetPagination returns the value of Pagination if it is set or its // zero value if it is unset. func (v *ListFailoverHistoryRequest) GetPagination() (o *PaginationOptions) { if v != nil && v.Pagination != nil { return v.Pagination } return } // IsSetPagination returns true if Pagination is not nil. func (v *ListFailoverHistoryRequest) IsSetPagination() bool { return v != nil && v.Pagination != nil } type ListFailoverHistoryRequestFilters struct { DomainID *string `json:"domainID,omitempty"` } // ToWire translates a ListFailoverHistoryRequestFilters struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListFailoverHistoryRequestFilters) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.DomainID != nil { w, err = wire.NewValueString(*(v.DomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListFailoverHistoryRequestFilters struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListFailoverHistoryRequestFilters struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListFailoverHistoryRequestFilters // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListFailoverHistoryRequestFilters) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainID = &x if err != nil { return err } } } } return nil } // Encode serializes a ListFailoverHistoryRequestFilters struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListFailoverHistoryRequestFilters struct could not be encoded. func (v *ListFailoverHistoryRequestFilters) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListFailoverHistoryRequestFilters struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListFailoverHistoryRequestFilters struct could not be generated from the wire // representation. func (v *ListFailoverHistoryRequestFilters) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListFailoverHistoryRequestFilters // struct. func (v *ListFailoverHistoryRequestFilters) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", *(v.DomainID)) i++ } return fmt.Sprintf("ListFailoverHistoryRequestFilters{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListFailoverHistoryRequestFilters match the // provided ListFailoverHistoryRequestFilters. // // This function performs a deep comparison. func (v *ListFailoverHistoryRequestFilters) Equals(rhs *ListFailoverHistoryRequestFilters) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainID, rhs.DomainID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListFailoverHistoryRequestFilters. func (v *ListFailoverHistoryRequestFilters) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainID != nil { enc.AddString("domainID", *v.DomainID) } return err } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *ListFailoverHistoryRequestFilters) GetDomainID() (o string) { if v != nil && v.DomainID != nil { return *v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *ListFailoverHistoryRequestFilters) IsSetDomainID() bool { return v != nil && v.DomainID != nil } type ListFailoverHistoryResponse struct { FailoverEvents []*FailoverEvent `json:"failoverEvents,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } type _List_FailoverEvent_ValueList []*FailoverEvent func (v _List_FailoverEvent_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*FailoverEvent', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_FailoverEvent_ValueList) Size() int { return len(v) } func (_List_FailoverEvent_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_FailoverEvent_ValueList) Close() {} // ToWire translates a ListFailoverHistoryResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListFailoverHistoryResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.FailoverEvents != nil { w, err = wire.NewValueList(_List_FailoverEvent_ValueList(v.FailoverEvents)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _FailoverEvent_Read(w wire.Value) (*FailoverEvent, error) { var v FailoverEvent err := v.FromWire(w) return &v, err } func _List_FailoverEvent_Read(l wire.ValueList) ([]*FailoverEvent, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*FailoverEvent, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _FailoverEvent_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a ListFailoverHistoryResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListFailoverHistoryResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListFailoverHistoryResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListFailoverHistoryResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.FailoverEvents, err = _List_FailoverEvent_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } func _List_FailoverEvent_Encode(val []*FailoverEvent, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*FailoverEvent', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a ListFailoverHistoryResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListFailoverHistoryResponse struct could not be encoded. func (v *ListFailoverHistoryResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.FailoverEvents != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_FailoverEvent_Encode(v.FailoverEvents, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _FailoverEvent_Decode(sr stream.Reader) (*FailoverEvent, error) { var v FailoverEvent err := v.Decode(sr) return &v, err } func _List_FailoverEvent_Decode(sr stream.Reader) ([]*FailoverEvent, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*FailoverEvent, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _FailoverEvent_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ListFailoverHistoryResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListFailoverHistoryResponse struct could not be generated from the wire // representation. func (v *ListFailoverHistoryResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.FailoverEvents, err = _List_FailoverEvent_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListFailoverHistoryResponse // struct. func (v *ListFailoverHistoryResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.FailoverEvents != nil { fields[i] = fmt.Sprintf("FailoverEvents: %v", v.FailoverEvents) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("ListFailoverHistoryResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_FailoverEvent_Equals(lhs, rhs []*FailoverEvent) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this ListFailoverHistoryResponse match the // provided ListFailoverHistoryResponse. // // This function performs a deep comparison. func (v *ListFailoverHistoryResponse) Equals(rhs *ListFailoverHistoryResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.FailoverEvents == nil && rhs.FailoverEvents == nil) || (v.FailoverEvents != nil && rhs.FailoverEvents != nil && _List_FailoverEvent_Equals(v.FailoverEvents, rhs.FailoverEvents))) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } type _List_FailoverEvent_Zapper []*FailoverEvent // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_FailoverEvent_Zapper. func (l _List_FailoverEvent_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListFailoverHistoryResponse. func (v *ListFailoverHistoryResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.FailoverEvents != nil { err = multierr.Append(err, enc.AddArray("failoverEvents", (_List_FailoverEvent_Zapper)(v.FailoverEvents))) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetFailoverEvents returns the value of FailoverEvents if it is set or its // zero value if it is unset. func (v *ListFailoverHistoryResponse) GetFailoverEvents() (o []*FailoverEvent) { if v != nil && v.FailoverEvents != nil { return v.FailoverEvents } return } // IsSetFailoverEvents returns true if FailoverEvents is not nil. func (v *ListFailoverHistoryResponse) IsSetFailoverEvents() bool { return v != nil && v.FailoverEvents != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListFailoverHistoryResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListFailoverHistoryResponse) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type ListOpenWorkflowExecutionsRequest struct { Domain *string `json:"domain,omitempty"` MaximumPageSize *int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` StartTimeFilter *StartTimeFilter `json:"StartTimeFilter,omitempty"` ExecutionFilter *WorkflowExecutionFilter `json:"executionFilter,omitempty"` TypeFilter *WorkflowTypeFilter `json:"typeFilter,omitempty"` } // ToWire translates a ListOpenWorkflowExecutionsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListOpenWorkflowExecutionsRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.MaximumPageSize != nil { w, err = wire.NewValueI32(*(v.MaximumPageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.StartTimeFilter != nil { w, err = v.StartTimeFilter.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ExecutionFilter != nil { w, err = v.ExecutionFilter.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.TypeFilter != nil { w, err = v.TypeFilter.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListOpenWorkflowExecutionsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListOpenWorkflowExecutionsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListOpenWorkflowExecutionsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListOpenWorkflowExecutionsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.MaximumPageSize = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.StartTimeFilter, err = _StartTimeFilter_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.ExecutionFilter, err = _WorkflowExecutionFilter_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.TypeFilter, err = _WorkflowTypeFilter_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ListOpenWorkflowExecutionsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListOpenWorkflowExecutionsRequest struct could not be encoded. func (v *ListOpenWorkflowExecutionsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MaximumPageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.MaximumPageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartTimeFilter != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.StartTimeFilter.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionFilter != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.ExecutionFilter.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TypeFilter != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.TypeFilter.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListOpenWorkflowExecutionsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListOpenWorkflowExecutionsRequest struct could not be generated from the wire // representation. func (v *ListOpenWorkflowExecutionsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.MaximumPageSize = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.StartTimeFilter, err = _StartTimeFilter_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.ExecutionFilter, err = _WorkflowExecutionFilter_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.TypeFilter, err = _WorkflowTypeFilter_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListOpenWorkflowExecutionsRequest // struct. func (v *ListOpenWorkflowExecutionsRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.MaximumPageSize != nil { fields[i] = fmt.Sprintf("MaximumPageSize: %v", *(v.MaximumPageSize)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.StartTimeFilter != nil { fields[i] = fmt.Sprintf("StartTimeFilter: %v", v.StartTimeFilter) i++ } if v.ExecutionFilter != nil { fields[i] = fmt.Sprintf("ExecutionFilter: %v", v.ExecutionFilter) i++ } if v.TypeFilter != nil { fields[i] = fmt.Sprintf("TypeFilter: %v", v.TypeFilter) i++ } return fmt.Sprintf("ListOpenWorkflowExecutionsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListOpenWorkflowExecutionsRequest match the // provided ListOpenWorkflowExecutionsRequest. // // This function performs a deep comparison. func (v *ListOpenWorkflowExecutionsRequest) Equals(rhs *ListOpenWorkflowExecutionsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_I32_EqualsPtr(v.MaximumPageSize, rhs.MaximumPageSize) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !((v.StartTimeFilter == nil && rhs.StartTimeFilter == nil) || (v.StartTimeFilter != nil && rhs.StartTimeFilter != nil && v.StartTimeFilter.Equals(rhs.StartTimeFilter))) { return false } if !((v.ExecutionFilter == nil && rhs.ExecutionFilter == nil) || (v.ExecutionFilter != nil && rhs.ExecutionFilter != nil && v.ExecutionFilter.Equals(rhs.ExecutionFilter))) { return false } if !((v.TypeFilter == nil && rhs.TypeFilter == nil) || (v.TypeFilter != nil && rhs.TypeFilter != nil && v.TypeFilter.Equals(rhs.TypeFilter))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListOpenWorkflowExecutionsRequest. func (v *ListOpenWorkflowExecutionsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.MaximumPageSize != nil { enc.AddInt32("maximumPageSize", *v.MaximumPageSize) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.StartTimeFilter != nil { err = multierr.Append(err, enc.AddObject("StartTimeFilter", v.StartTimeFilter)) } if v.ExecutionFilter != nil { err = multierr.Append(err, enc.AddObject("executionFilter", v.ExecutionFilter)) } if v.TypeFilter != nil { err = multierr.Append(err, enc.AddObject("typeFilter", v.TypeFilter)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ListOpenWorkflowExecutionsRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ListOpenWorkflowExecutionsRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetMaximumPageSize returns the value of MaximumPageSize if it is set or its // zero value if it is unset. func (v *ListOpenWorkflowExecutionsRequest) GetMaximumPageSize() (o int32) { if v != nil && v.MaximumPageSize != nil { return *v.MaximumPageSize } return } // IsSetMaximumPageSize returns true if MaximumPageSize is not nil. func (v *ListOpenWorkflowExecutionsRequest) IsSetMaximumPageSize() bool { return v != nil && v.MaximumPageSize != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListOpenWorkflowExecutionsRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListOpenWorkflowExecutionsRequest) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetStartTimeFilter returns the value of StartTimeFilter if it is set or its // zero value if it is unset. func (v *ListOpenWorkflowExecutionsRequest) GetStartTimeFilter() (o *StartTimeFilter) { if v != nil && v.StartTimeFilter != nil { return v.StartTimeFilter } return } // IsSetStartTimeFilter returns true if StartTimeFilter is not nil. func (v *ListOpenWorkflowExecutionsRequest) IsSetStartTimeFilter() bool { return v != nil && v.StartTimeFilter != nil } // GetExecutionFilter returns the value of ExecutionFilter if it is set or its // zero value if it is unset. func (v *ListOpenWorkflowExecutionsRequest) GetExecutionFilter() (o *WorkflowExecutionFilter) { if v != nil && v.ExecutionFilter != nil { return v.ExecutionFilter } return } // IsSetExecutionFilter returns true if ExecutionFilter is not nil. func (v *ListOpenWorkflowExecutionsRequest) IsSetExecutionFilter() bool { return v != nil && v.ExecutionFilter != nil } // GetTypeFilter returns the value of TypeFilter if it is set or its // zero value if it is unset. func (v *ListOpenWorkflowExecutionsRequest) GetTypeFilter() (o *WorkflowTypeFilter) { if v != nil && v.TypeFilter != nil { return v.TypeFilter } return } // IsSetTypeFilter returns true if TypeFilter is not nil. func (v *ListOpenWorkflowExecutionsRequest) IsSetTypeFilter() bool { return v != nil && v.TypeFilter != nil } type ListOpenWorkflowExecutionsResponse struct { Executions []*WorkflowExecutionInfo `json:"executions,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // ToWire translates a ListOpenWorkflowExecutionsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListOpenWorkflowExecutionsResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Executions != nil { w, err = wire.NewValueList(_List_WorkflowExecutionInfo_ValueList(v.Executions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListOpenWorkflowExecutionsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListOpenWorkflowExecutionsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListOpenWorkflowExecutionsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListOpenWorkflowExecutionsResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Executions, err = _List_WorkflowExecutionInfo_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a ListOpenWorkflowExecutionsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListOpenWorkflowExecutionsResponse struct could not be encoded. func (v *ListOpenWorkflowExecutionsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Executions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_WorkflowExecutionInfo_Encode(v.Executions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListOpenWorkflowExecutionsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListOpenWorkflowExecutionsResponse struct could not be generated from the wire // representation. func (v *ListOpenWorkflowExecutionsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Executions, err = _List_WorkflowExecutionInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListOpenWorkflowExecutionsResponse // struct. func (v *ListOpenWorkflowExecutionsResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Executions != nil { fields[i] = fmt.Sprintf("Executions: %v", v.Executions) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("ListOpenWorkflowExecutionsResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListOpenWorkflowExecutionsResponse match the // provided ListOpenWorkflowExecutionsResponse. // // This function performs a deep comparison. func (v *ListOpenWorkflowExecutionsResponse) Equals(rhs *ListOpenWorkflowExecutionsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Executions == nil && rhs.Executions == nil) || (v.Executions != nil && rhs.Executions != nil && _List_WorkflowExecutionInfo_Equals(v.Executions, rhs.Executions))) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListOpenWorkflowExecutionsResponse. func (v *ListOpenWorkflowExecutionsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Executions != nil { err = multierr.Append(err, enc.AddArray("executions", (_List_WorkflowExecutionInfo_Zapper)(v.Executions))) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetExecutions returns the value of Executions if it is set or its // zero value if it is unset. func (v *ListOpenWorkflowExecutionsResponse) GetExecutions() (o []*WorkflowExecutionInfo) { if v != nil && v.Executions != nil { return v.Executions } return } // IsSetExecutions returns true if Executions is not nil. func (v *ListOpenWorkflowExecutionsResponse) IsSetExecutions() bool { return v != nil && v.Executions != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListOpenWorkflowExecutionsResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListOpenWorkflowExecutionsResponse) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type ListTaskListPartitionsRequest struct { Domain *string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` } // ToWire translates a ListTaskListPartitionsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListTaskListPartitionsRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListTaskListPartitionsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListTaskListPartitionsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListTaskListPartitionsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListTaskListPartitionsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ListTaskListPartitionsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListTaskListPartitionsRequest struct could not be encoded. func (v *ListTaskListPartitionsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListTaskListPartitionsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListTaskListPartitionsRequest struct could not be generated from the wire // representation. func (v *ListTaskListPartitionsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListTaskListPartitionsRequest // struct. func (v *ListTaskListPartitionsRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } return fmt.Sprintf("ListTaskListPartitionsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListTaskListPartitionsRequest match the // provided ListTaskListPartitionsRequest. // // This function performs a deep comparison. func (v *ListTaskListPartitionsRequest) Equals(rhs *ListTaskListPartitionsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListTaskListPartitionsRequest. func (v *ListTaskListPartitionsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ListTaskListPartitionsRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ListTaskListPartitionsRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *ListTaskListPartitionsRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *ListTaskListPartitionsRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } type ListTaskListPartitionsResponse struct { ActivityTaskListPartitions []*TaskListPartitionMetadata `json:"activityTaskListPartitions,omitempty"` DecisionTaskListPartitions []*TaskListPartitionMetadata `json:"decisionTaskListPartitions,omitempty"` } type _List_TaskListPartitionMetadata_ValueList []*TaskListPartitionMetadata func (v _List_TaskListPartitionMetadata_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*TaskListPartitionMetadata', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_TaskListPartitionMetadata_ValueList) Size() int { return len(v) } func (_List_TaskListPartitionMetadata_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_TaskListPartitionMetadata_ValueList) Close() {} // ToWire translates a ListTaskListPartitionsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListTaskListPartitionsResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ActivityTaskListPartitions != nil { w, err = wire.NewValueList(_List_TaskListPartitionMetadata_ValueList(v.ActivityTaskListPartitions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.DecisionTaskListPartitions != nil { w, err = wire.NewValueList(_List_TaskListPartitionMetadata_ValueList(v.DecisionTaskListPartitions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TaskListPartitionMetadata_Read(w wire.Value) (*TaskListPartitionMetadata, error) { var v TaskListPartitionMetadata err := v.FromWire(w) return &v, err } func _List_TaskListPartitionMetadata_Read(l wire.ValueList) ([]*TaskListPartitionMetadata, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*TaskListPartitionMetadata, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _TaskListPartitionMetadata_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a ListTaskListPartitionsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListTaskListPartitionsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListTaskListPartitionsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListTaskListPartitionsResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.ActivityTaskListPartitions, err = _List_TaskListPartitionMetadata_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.DecisionTaskListPartitions, err = _List_TaskListPartitionMetadata_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_TaskListPartitionMetadata_Encode(val []*TaskListPartitionMetadata, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*TaskListPartitionMetadata', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a ListTaskListPartitionsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListTaskListPartitionsResponse struct could not be encoded. func (v *ListTaskListPartitionsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActivityTaskListPartitions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_TaskListPartitionMetadata_Encode(v.ActivityTaskListPartitions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskListPartitions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_TaskListPartitionMetadata_Encode(v.DecisionTaskListPartitions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TaskListPartitionMetadata_Decode(sr stream.Reader) (*TaskListPartitionMetadata, error) { var v TaskListPartitionMetadata err := v.Decode(sr) return &v, err } func _List_TaskListPartitionMetadata_Decode(sr stream.Reader) ([]*TaskListPartitionMetadata, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*TaskListPartitionMetadata, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _TaskListPartitionMetadata_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ListTaskListPartitionsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListTaskListPartitionsResponse struct could not be generated from the wire // representation. func (v *ListTaskListPartitionsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.ActivityTaskListPartitions, err = _List_TaskListPartitionMetadata_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.DecisionTaskListPartitions, err = _List_TaskListPartitionMetadata_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListTaskListPartitionsResponse // struct. func (v *ListTaskListPartitionsResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ActivityTaskListPartitions != nil { fields[i] = fmt.Sprintf("ActivityTaskListPartitions: %v", v.ActivityTaskListPartitions) i++ } if v.DecisionTaskListPartitions != nil { fields[i] = fmt.Sprintf("DecisionTaskListPartitions: %v", v.DecisionTaskListPartitions) i++ } return fmt.Sprintf("ListTaskListPartitionsResponse{%v}", strings.Join(fields[:i], ", ")) } func _List_TaskListPartitionMetadata_Equals(lhs, rhs []*TaskListPartitionMetadata) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this ListTaskListPartitionsResponse match the // provided ListTaskListPartitionsResponse. // // This function performs a deep comparison. func (v *ListTaskListPartitionsResponse) Equals(rhs *ListTaskListPartitionsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ActivityTaskListPartitions == nil && rhs.ActivityTaskListPartitions == nil) || (v.ActivityTaskListPartitions != nil && rhs.ActivityTaskListPartitions != nil && _List_TaskListPartitionMetadata_Equals(v.ActivityTaskListPartitions, rhs.ActivityTaskListPartitions))) { return false } if !((v.DecisionTaskListPartitions == nil && rhs.DecisionTaskListPartitions == nil) || (v.DecisionTaskListPartitions != nil && rhs.DecisionTaskListPartitions != nil && _List_TaskListPartitionMetadata_Equals(v.DecisionTaskListPartitions, rhs.DecisionTaskListPartitions))) { return false } return true } type _List_TaskListPartitionMetadata_Zapper []*TaskListPartitionMetadata // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_TaskListPartitionMetadata_Zapper. func (l _List_TaskListPartitionMetadata_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListTaskListPartitionsResponse. func (v *ListTaskListPartitionsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActivityTaskListPartitions != nil { err = multierr.Append(err, enc.AddArray("activityTaskListPartitions", (_List_TaskListPartitionMetadata_Zapper)(v.ActivityTaskListPartitions))) } if v.DecisionTaskListPartitions != nil { err = multierr.Append(err, enc.AddArray("decisionTaskListPartitions", (_List_TaskListPartitionMetadata_Zapper)(v.DecisionTaskListPartitions))) } return err } // GetActivityTaskListPartitions returns the value of ActivityTaskListPartitions if it is set or its // zero value if it is unset. func (v *ListTaskListPartitionsResponse) GetActivityTaskListPartitions() (o []*TaskListPartitionMetadata) { if v != nil && v.ActivityTaskListPartitions != nil { return v.ActivityTaskListPartitions } return } // IsSetActivityTaskListPartitions returns true if ActivityTaskListPartitions is not nil. func (v *ListTaskListPartitionsResponse) IsSetActivityTaskListPartitions() bool { return v != nil && v.ActivityTaskListPartitions != nil } // GetDecisionTaskListPartitions returns the value of DecisionTaskListPartitions if it is set or its // zero value if it is unset. func (v *ListTaskListPartitionsResponse) GetDecisionTaskListPartitions() (o []*TaskListPartitionMetadata) { if v != nil && v.DecisionTaskListPartitions != nil { return v.DecisionTaskListPartitions } return } // IsSetDecisionTaskListPartitions returns true if DecisionTaskListPartitions is not nil. func (v *ListTaskListPartitionsResponse) IsSetDecisionTaskListPartitions() bool { return v != nil && v.DecisionTaskListPartitions != nil } type ListWorkflowExecutionsRequest struct { Domain *string `json:"domain,omitempty"` PageSize *int32 `json:"pageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` Query *string `json:"query,omitempty"` } // ToWire translates a ListWorkflowExecutionsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListWorkflowExecutionsRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.PageSize != nil { w, err = wire.NewValueI32(*(v.PageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Query != nil { w, err = wire.NewValueString(*(v.Query)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListWorkflowExecutionsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListWorkflowExecutionsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListWorkflowExecutionsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListWorkflowExecutionsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.PageSize = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Query = &x if err != nil { return err } } } } return nil } // Encode serializes a ListWorkflowExecutionsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListWorkflowExecutionsRequest struct could not be encoded. func (v *ListWorkflowExecutionsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.PageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Query != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Query)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListWorkflowExecutionsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListWorkflowExecutionsRequest struct could not be generated from the wire // representation. func (v *ListWorkflowExecutionsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.PageSize = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Query = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListWorkflowExecutionsRequest // struct. func (v *ListWorkflowExecutionsRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.PageSize != nil { fields[i] = fmt.Sprintf("PageSize: %v", *(v.PageSize)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.Query != nil { fields[i] = fmt.Sprintf("Query: %v", *(v.Query)) i++ } return fmt.Sprintf("ListWorkflowExecutionsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListWorkflowExecutionsRequest match the // provided ListWorkflowExecutionsRequest. // // This function performs a deep comparison. func (v *ListWorkflowExecutionsRequest) Equals(rhs *ListWorkflowExecutionsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_I32_EqualsPtr(v.PageSize, rhs.PageSize) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !_String_EqualsPtr(v.Query, rhs.Query) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListWorkflowExecutionsRequest. func (v *ListWorkflowExecutionsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.PageSize != nil { enc.AddInt32("pageSize", *v.PageSize) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.Query != nil { enc.AddString("query", *v.Query) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ListWorkflowExecutionsRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ListWorkflowExecutionsRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetPageSize returns the value of PageSize if it is set or its // zero value if it is unset. func (v *ListWorkflowExecutionsRequest) GetPageSize() (o int32) { if v != nil && v.PageSize != nil { return *v.PageSize } return } // IsSetPageSize returns true if PageSize is not nil. func (v *ListWorkflowExecutionsRequest) IsSetPageSize() bool { return v != nil && v.PageSize != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListWorkflowExecutionsRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListWorkflowExecutionsRequest) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetQuery returns the value of Query if it is set or its // zero value if it is unset. func (v *ListWorkflowExecutionsRequest) GetQuery() (o string) { if v != nil && v.Query != nil { return *v.Query } return } // IsSetQuery returns true if Query is not nil. func (v *ListWorkflowExecutionsRequest) IsSetQuery() bool { return v != nil && v.Query != nil } type ListWorkflowExecutionsResponse struct { Executions []*WorkflowExecutionInfo `json:"executions,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // ToWire translates a ListWorkflowExecutionsResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ListWorkflowExecutionsResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Executions != nil { w, err = wire.NewValueList(_List_WorkflowExecutionInfo_ValueList(v.Executions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ListWorkflowExecutionsResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ListWorkflowExecutionsResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ListWorkflowExecutionsResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ListWorkflowExecutionsResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Executions, err = _List_WorkflowExecutionInfo_Read(field.Value.GetList()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a ListWorkflowExecutionsResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ListWorkflowExecutionsResponse struct could not be encoded. func (v *ListWorkflowExecutionsResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Executions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_WorkflowExecutionInfo_Encode(v.Executions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ListWorkflowExecutionsResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ListWorkflowExecutionsResponse struct could not be generated from the wire // representation. func (v *ListWorkflowExecutionsResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Executions, err = _List_WorkflowExecutionInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ListWorkflowExecutionsResponse // struct. func (v *ListWorkflowExecutionsResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Executions != nil { fields[i] = fmt.Sprintf("Executions: %v", v.Executions) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("ListWorkflowExecutionsResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ListWorkflowExecutionsResponse match the // provided ListWorkflowExecutionsResponse. // // This function performs a deep comparison. func (v *ListWorkflowExecutionsResponse) Equals(rhs *ListWorkflowExecutionsResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Executions == nil && rhs.Executions == nil) || (v.Executions != nil && rhs.Executions != nil && _List_WorkflowExecutionInfo_Equals(v.Executions, rhs.Executions))) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ListWorkflowExecutionsResponse. func (v *ListWorkflowExecutionsResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Executions != nil { err = multierr.Append(err, enc.AddArray("executions", (_List_WorkflowExecutionInfo_Zapper)(v.Executions))) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetExecutions returns the value of Executions if it is set or its // zero value if it is unset. func (v *ListWorkflowExecutionsResponse) GetExecutions() (o []*WorkflowExecutionInfo) { if v != nil && v.Executions != nil { return v.Executions } return } // IsSetExecutions returns true if Executions is not nil. func (v *ListWorkflowExecutionsResponse) IsSetExecutions() bool { return v != nil && v.Executions != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *ListWorkflowExecutionsResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *ListWorkflowExecutionsResponse) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type MarkerRecordedEventAttributes struct { MarkerName *string `json:"markerName,omitempty"` Details []byte `json:"details,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` Header *Header `json:"header,omitempty"` } // ToWire translates a MarkerRecordedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *MarkerRecordedEventAttributes) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.MarkerName != nil { w, err = wire.NewValueString(*(v.MarkerName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a MarkerRecordedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a MarkerRecordedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v MarkerRecordedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *MarkerRecordedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.MarkerName = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a MarkerRecordedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a MarkerRecordedEventAttributes struct could not be encoded. func (v *MarkerRecordedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.MarkerName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.MarkerName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a MarkerRecordedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a MarkerRecordedEventAttributes struct could not be generated from the wire // representation. func (v *MarkerRecordedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.MarkerName = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a MarkerRecordedEventAttributes // struct. func (v *MarkerRecordedEventAttributes) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.MarkerName != nil { fields[i] = fmt.Sprintf("MarkerName: %v", *(v.MarkerName)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } return fmt.Sprintf("MarkerRecordedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this MarkerRecordedEventAttributes match the // provided MarkerRecordedEventAttributes. // // This function performs a deep comparison. func (v *MarkerRecordedEventAttributes) Equals(rhs *MarkerRecordedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.MarkerName, rhs.MarkerName) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of MarkerRecordedEventAttributes. func (v *MarkerRecordedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.MarkerName != nil { enc.AddString("markerName", *v.MarkerName) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } return err } // GetMarkerName returns the value of MarkerName if it is set or its // zero value if it is unset. func (v *MarkerRecordedEventAttributes) GetMarkerName() (o string) { if v != nil && v.MarkerName != nil { return *v.MarkerName } return } // IsSetMarkerName returns true if MarkerName is not nil. func (v *MarkerRecordedEventAttributes) IsSetMarkerName() bool { return v != nil && v.MarkerName != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *MarkerRecordedEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *MarkerRecordedEventAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *MarkerRecordedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *MarkerRecordedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *MarkerRecordedEventAttributes) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *MarkerRecordedEventAttributes) IsSetHeader() bool { return v != nil && v.Header != nil } type Memo struct { Fields map[string][]byte `json:"fields,omitempty"` } // ToWire translates a Memo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *Memo) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Fields != nil { w, err = wire.NewValueMap(_Map_String_Binary_MapItemList(v.Fields)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a Memo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a Memo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v Memo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *Memo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.Fields, err = _Map_String_Binary_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } // Encode serializes a Memo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a Memo struct could not be encoded. func (v *Memo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Fields != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_Binary_Encode(v.Fields, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a Memo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a Memo struct could not be generated from the wire // representation. func (v *Memo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.Fields, err = _Map_String_Binary_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a Memo // struct. func (v *Memo) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Fields != nil { fields[i] = fmt.Sprintf("Fields: %v", v.Fields) i++ } return fmt.Sprintf("Memo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this Memo match the // provided Memo. // // This function performs a deep comparison. func (v *Memo) Equals(rhs *Memo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Fields == nil && rhs.Fields == nil) || (v.Fields != nil && rhs.Fields != nil && _Map_String_Binary_Equals(v.Fields, rhs.Fields))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of Memo. func (v *Memo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Fields != nil { err = multierr.Append(err, enc.AddObject("fields", (_Map_String_Binary_Zapper)(v.Fields))) } return err } // GetFields returns the value of Fields if it is set or its // zero value if it is unset. func (v *Memo) GetFields() (o map[string][]byte) { if v != nil && v.Fields != nil { return v.Fields } return } // IsSetFields returns true if Fields is not nil. func (v *Memo) IsSetFields() bool { return v != nil && v.Fields != nil } type PaginationOptions struct { PageSize *int32 `json:"pageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // ToWire translates a PaginationOptions struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PaginationOptions) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.PageSize != nil { w, err = wire.NewValueI32(*(v.PageSize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a PaginationOptions struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PaginationOptions struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PaginationOptions // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PaginationOptions) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.PageSize = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a PaginationOptions struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PaginationOptions struct could not be encoded. func (v *PaginationOptions) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.PageSize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.PageSize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a PaginationOptions struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PaginationOptions struct could not be generated from the wire // representation. func (v *PaginationOptions) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.PageSize = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PaginationOptions // struct. func (v *PaginationOptions) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.PageSize != nil { fields[i] = fmt.Sprintf("PageSize: %v", *(v.PageSize)) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } return fmt.Sprintf("PaginationOptions{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PaginationOptions match the // provided PaginationOptions. // // This function performs a deep comparison. func (v *PaginationOptions) Equals(rhs *PaginationOptions) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.PageSize, rhs.PageSize) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PaginationOptions. func (v *PaginationOptions) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.PageSize != nil { enc.AddInt32("pageSize", *v.PageSize) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } return err } // GetPageSize returns the value of PageSize if it is set or its // zero value if it is unset. func (v *PaginationOptions) GetPageSize() (o int32) { if v != nil && v.PageSize != nil { return *v.PageSize } return } // IsSetPageSize returns true if PageSize is not nil. func (v *PaginationOptions) IsSetPageSize() bool { return v != nil && v.PageSize != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *PaginationOptions) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *PaginationOptions) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } type ParentClosePolicy int32 const ( ParentClosePolicyAbandon ParentClosePolicy = 0 ParentClosePolicyRequestCancel ParentClosePolicy = 1 ParentClosePolicyTerminate ParentClosePolicy = 2 ) // ParentClosePolicy_Values returns all recognized values of ParentClosePolicy. func ParentClosePolicy_Values() []ParentClosePolicy { return []ParentClosePolicy{ ParentClosePolicyAbandon, ParentClosePolicyRequestCancel, ParentClosePolicyTerminate, } } // UnmarshalText tries to decode ParentClosePolicy from a byte slice // containing its name. // // var v ParentClosePolicy // err := v.UnmarshalText([]byte("ABANDON")) func (v *ParentClosePolicy) UnmarshalText(value []byte) error { switch s := string(value); s { case "ABANDON": *v = ParentClosePolicyAbandon return nil case "REQUEST_CANCEL": *v = ParentClosePolicyRequestCancel return nil case "TERMINATE": *v = ParentClosePolicyTerminate return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ParentClosePolicy", err) } *v = ParentClosePolicy(val) return nil } } // MarshalText encodes ParentClosePolicy to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v ParentClosePolicy) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("ABANDON"), nil case 1: return []byte("REQUEST_CANCEL"), nil case 2: return []byte("TERMINATE"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ParentClosePolicy. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v ParentClosePolicy) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "ABANDON") case 1: enc.AddString("name", "REQUEST_CANCEL") case 2: enc.AddString("name", "TERMINATE") } return nil } // Ptr returns a pointer to this enum value. func (v ParentClosePolicy) Ptr() *ParentClosePolicy { return &v } // Encode encodes ParentClosePolicy directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v ParentClosePolicy // return v.Encode(sWriter) func (v ParentClosePolicy) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates ParentClosePolicy into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v ParentClosePolicy) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes ParentClosePolicy from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return ParentClosePolicy(0), err // } // // var v ParentClosePolicy // if err := v.FromWire(x); err != nil { // return ParentClosePolicy(0), err // } // return v, nil func (v *ParentClosePolicy) FromWire(w wire.Value) error { *v = (ParentClosePolicy)(w.GetI32()) return nil } // Decode reads off the encoded ParentClosePolicy directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v ParentClosePolicy // if err := v.Decode(sReader); err != nil { // return ParentClosePolicy(0), err // } // return v, nil func (v *ParentClosePolicy) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (ParentClosePolicy)(i) return nil } // String returns a readable string representation of ParentClosePolicy. func (v ParentClosePolicy) String() string { w := int32(v) switch w { case 0: return "ABANDON" case 1: return "REQUEST_CANCEL" case 2: return "TERMINATE" } return fmt.Sprintf("ParentClosePolicy(%d)", w) } // Equals returns true if this ParentClosePolicy value matches the provided // value. func (v ParentClosePolicy) Equals(rhs ParentClosePolicy) bool { return v == rhs } // MarshalJSON serializes ParentClosePolicy into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v ParentClosePolicy) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"ABANDON\""), nil case 1: return ([]byte)("\"REQUEST_CANCEL\""), nil case 2: return ([]byte)("\"TERMINATE\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode ParentClosePolicy from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *ParentClosePolicy) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "ParentClosePolicy") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "ParentClosePolicy") } *v = (ParentClosePolicy)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "ParentClosePolicy") } } type PendingActivityInfo struct { ActivityID *string `json:"activityID,omitempty"` ActivityType *ActivityType `json:"activityType,omitempty"` State *PendingActivityState `json:"state,omitempty"` HeartbeatDetails []byte `json:"heartbeatDetails,omitempty"` LastHeartbeatTimestamp *int64 `json:"lastHeartbeatTimestamp,omitempty"` LastStartedTimestamp *int64 `json:"lastStartedTimestamp,omitempty"` Attempt *int32 `json:"attempt,omitempty"` MaximumAttempts *int32 `json:"maximumAttempts,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` ExpirationTimestamp *int64 `json:"expirationTimestamp,omitempty"` LastFailureReason *string `json:"lastFailureReason,omitempty"` LastWorkerIdentity *string `json:"lastWorkerIdentity,omitempty"` LastFailureDetails []byte `json:"lastFailureDetails,omitempty"` StartedWorkerIdentity *string `json:"startedWorkerIdentity,omitempty"` ScheduleID *int64 `json:"scheduleID,omitempty"` } // ToWire translates a PendingActivityInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PendingActivityInfo) ToWire() (wire.Value, error) { var ( fields [15]wire.Field i int = 0 w wire.Value err error ) if v.ActivityID != nil { w, err = wire.NewValueString(*(v.ActivityID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ActivityType != nil { w, err = v.ActivityType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.State != nil { w, err = v.State.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.HeartbeatDetails != nil { w, err = wire.NewValueBinary(v.HeartbeatDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.LastHeartbeatTimestamp != nil { w, err = wire.NewValueI64(*(v.LastHeartbeatTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.LastStartedTimestamp != nil { w, err = wire.NewValueI64(*(v.LastStartedTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI32(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.MaximumAttempts != nil { w, err = wire.NewValueI32(*(v.MaximumAttempts)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.ScheduledTimestamp != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.ExpirationTimestamp != nil { w, err = wire.NewValueI64(*(v.ExpirationTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.LastFailureReason != nil { w, err = wire.NewValueString(*(v.LastFailureReason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.LastWorkerIdentity != nil { w, err = wire.NewValueString(*(v.LastWorkerIdentity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.LastFailureDetails != nil { w, err = wire.NewValueBinary(v.LastFailureDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.StartedWorkerIdentity != nil { w, err = wire.NewValueString(*(v.StartedWorkerIdentity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.ScheduleID != nil { w, err = wire.NewValueI64(*(v.ScheduleID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PendingActivityState_Read(w wire.Value) (PendingActivityState, error) { var v PendingActivityState err := v.FromWire(w) return v, err } // FromWire deserializes a PendingActivityInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PendingActivityInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PendingActivityInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PendingActivityInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.ActivityType, err = _ActivityType_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x PendingActivityState x, err = _PendingActivityState_Read(field.Value) v.State = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.HeartbeatDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastHeartbeatTimestamp = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastStartedTimestamp = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Attempt = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.MaximumAttempts = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestamp = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExpirationTimestamp = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.LastFailureReason = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.LastWorkerIdentity = &x if err != nil { return err } } case 130: if field.Value.Type() == wire.TBinary { v.LastFailureDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 140: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.StartedWorkerIdentity = &x if err != nil { return err } } case 150: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduleID = &x if err != nil { return err } } } } return nil } // Encode serializes a PendingActivityInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PendingActivityInfo struct could not be encoded. func (v *PendingActivityInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActivityID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.State != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := v.State.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HeartbeatDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.HeartbeatDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastHeartbeatTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastHeartbeatTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastStartedTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastStartedTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MaximumAttempts != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.MaximumAttempts)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExpirationTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExpirationTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFailureReason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.LastFailureReason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastWorkerIdentity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.LastWorkerIdentity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFailureDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.LastFailureDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedWorkerIdentity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.StartedWorkerIdentity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduleID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PendingActivityState_Decode(sr stream.Reader) (PendingActivityState, error) { var v PendingActivityState err := v.Decode(sr) return v, err } // Decode deserializes a PendingActivityInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PendingActivityInfo struct could not be generated from the wire // representation. func (v *PendingActivityInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.ActivityType, err = _ActivityType_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x PendingActivityState x, err = _PendingActivityState_Decode(sr) v.State = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.HeartbeatDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastHeartbeatTimestamp = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastStartedTimestamp = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Attempt = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.MaximumAttempts = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestamp = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExpirationTimestamp = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.LastFailureReason = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.LastWorkerIdentity = &x if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TBinary: v.LastFailureDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.StartedWorkerIdentity = &x if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduleID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PendingActivityInfo // struct. func (v *PendingActivityInfo) String() string { if v == nil { return "" } var fields [15]string i := 0 if v.ActivityID != nil { fields[i] = fmt.Sprintf("ActivityID: %v", *(v.ActivityID)) i++ } if v.ActivityType != nil { fields[i] = fmt.Sprintf("ActivityType: %v", v.ActivityType) i++ } if v.State != nil { fields[i] = fmt.Sprintf("State: %v", *(v.State)) i++ } if v.HeartbeatDetails != nil { fields[i] = fmt.Sprintf("HeartbeatDetails: %v", v.HeartbeatDetails) i++ } if v.LastHeartbeatTimestamp != nil { fields[i] = fmt.Sprintf("LastHeartbeatTimestamp: %v", *(v.LastHeartbeatTimestamp)) i++ } if v.LastStartedTimestamp != nil { fields[i] = fmt.Sprintf("LastStartedTimestamp: %v", *(v.LastStartedTimestamp)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.MaximumAttempts != nil { fields[i] = fmt.Sprintf("MaximumAttempts: %v", *(v.MaximumAttempts)) i++ } if v.ScheduledTimestamp != nil { fields[i] = fmt.Sprintf("ScheduledTimestamp: %v", *(v.ScheduledTimestamp)) i++ } if v.ExpirationTimestamp != nil { fields[i] = fmt.Sprintf("ExpirationTimestamp: %v", *(v.ExpirationTimestamp)) i++ } if v.LastFailureReason != nil { fields[i] = fmt.Sprintf("LastFailureReason: %v", *(v.LastFailureReason)) i++ } if v.LastWorkerIdentity != nil { fields[i] = fmt.Sprintf("LastWorkerIdentity: %v", *(v.LastWorkerIdentity)) i++ } if v.LastFailureDetails != nil { fields[i] = fmt.Sprintf("LastFailureDetails: %v", v.LastFailureDetails) i++ } if v.StartedWorkerIdentity != nil { fields[i] = fmt.Sprintf("StartedWorkerIdentity: %v", *(v.StartedWorkerIdentity)) i++ } if v.ScheduleID != nil { fields[i] = fmt.Sprintf("ScheduleID: %v", *(v.ScheduleID)) i++ } return fmt.Sprintf("PendingActivityInfo{%v}", strings.Join(fields[:i], ", ")) } func _PendingActivityState_EqualsPtr(lhs, rhs *PendingActivityState) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this PendingActivityInfo match the // provided PendingActivityInfo. // // This function performs a deep comparison. func (v *PendingActivityInfo) Equals(rhs *PendingActivityInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ActivityID, rhs.ActivityID) { return false } if !((v.ActivityType == nil && rhs.ActivityType == nil) || (v.ActivityType != nil && rhs.ActivityType != nil && v.ActivityType.Equals(rhs.ActivityType))) { return false } if !_PendingActivityState_EqualsPtr(v.State, rhs.State) { return false } if !((v.HeartbeatDetails == nil && rhs.HeartbeatDetails == nil) || (v.HeartbeatDetails != nil && rhs.HeartbeatDetails != nil && bytes.Equal(v.HeartbeatDetails, rhs.HeartbeatDetails))) { return false } if !_I64_EqualsPtr(v.LastHeartbeatTimestamp, rhs.LastHeartbeatTimestamp) { return false } if !_I64_EqualsPtr(v.LastStartedTimestamp, rhs.LastStartedTimestamp) { return false } if !_I32_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_I32_EqualsPtr(v.MaximumAttempts, rhs.MaximumAttempts) { return false } if !_I64_EqualsPtr(v.ScheduledTimestamp, rhs.ScheduledTimestamp) { return false } if !_I64_EqualsPtr(v.ExpirationTimestamp, rhs.ExpirationTimestamp) { return false } if !_String_EqualsPtr(v.LastFailureReason, rhs.LastFailureReason) { return false } if !_String_EqualsPtr(v.LastWorkerIdentity, rhs.LastWorkerIdentity) { return false } if !((v.LastFailureDetails == nil && rhs.LastFailureDetails == nil) || (v.LastFailureDetails != nil && rhs.LastFailureDetails != nil && bytes.Equal(v.LastFailureDetails, rhs.LastFailureDetails))) { return false } if !_String_EqualsPtr(v.StartedWorkerIdentity, rhs.StartedWorkerIdentity) { return false } if !_I64_EqualsPtr(v.ScheduleID, rhs.ScheduleID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PendingActivityInfo. func (v *PendingActivityInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActivityID != nil { enc.AddString("activityID", *v.ActivityID) } if v.ActivityType != nil { err = multierr.Append(err, enc.AddObject("activityType", v.ActivityType)) } if v.State != nil { err = multierr.Append(err, enc.AddObject("state", *v.State)) } if v.HeartbeatDetails != nil { enc.AddString("heartbeatDetails", base64.StdEncoding.EncodeToString(v.HeartbeatDetails)) } if v.LastHeartbeatTimestamp != nil { enc.AddInt64("lastHeartbeatTimestamp", *v.LastHeartbeatTimestamp) } if v.LastStartedTimestamp != nil { enc.AddInt64("lastStartedTimestamp", *v.LastStartedTimestamp) } if v.Attempt != nil { enc.AddInt32("attempt", *v.Attempt) } if v.MaximumAttempts != nil { enc.AddInt32("maximumAttempts", *v.MaximumAttempts) } if v.ScheduledTimestamp != nil { enc.AddInt64("scheduledTimestamp", *v.ScheduledTimestamp) } if v.ExpirationTimestamp != nil { enc.AddInt64("expirationTimestamp", *v.ExpirationTimestamp) } if v.LastFailureReason != nil { enc.AddString("lastFailureReason", *v.LastFailureReason) } if v.LastWorkerIdentity != nil { enc.AddString("lastWorkerIdentity", *v.LastWorkerIdentity) } if v.LastFailureDetails != nil { enc.AddString("lastFailureDetails", base64.StdEncoding.EncodeToString(v.LastFailureDetails)) } if v.StartedWorkerIdentity != nil { enc.AddString("startedWorkerIdentity", *v.StartedWorkerIdentity) } if v.ScheduleID != nil { enc.AddInt64("scheduleID", *v.ScheduleID) } return err } // GetActivityID returns the value of ActivityID if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetActivityID() (o string) { if v != nil && v.ActivityID != nil { return *v.ActivityID } return } // IsSetActivityID returns true if ActivityID is not nil. func (v *PendingActivityInfo) IsSetActivityID() bool { return v != nil && v.ActivityID != nil } // GetActivityType returns the value of ActivityType if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetActivityType() (o *ActivityType) { if v != nil && v.ActivityType != nil { return v.ActivityType } return } // IsSetActivityType returns true if ActivityType is not nil. func (v *PendingActivityInfo) IsSetActivityType() bool { return v != nil && v.ActivityType != nil } // GetState returns the value of State if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetState() (o PendingActivityState) { if v != nil && v.State != nil { return *v.State } return } // IsSetState returns true if State is not nil. func (v *PendingActivityInfo) IsSetState() bool { return v != nil && v.State != nil } // GetHeartbeatDetails returns the value of HeartbeatDetails if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetHeartbeatDetails() (o []byte) { if v != nil && v.HeartbeatDetails != nil { return v.HeartbeatDetails } return } // IsSetHeartbeatDetails returns true if HeartbeatDetails is not nil. func (v *PendingActivityInfo) IsSetHeartbeatDetails() bool { return v != nil && v.HeartbeatDetails != nil } // GetLastHeartbeatTimestamp returns the value of LastHeartbeatTimestamp if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetLastHeartbeatTimestamp() (o int64) { if v != nil && v.LastHeartbeatTimestamp != nil { return *v.LastHeartbeatTimestamp } return } // IsSetLastHeartbeatTimestamp returns true if LastHeartbeatTimestamp is not nil. func (v *PendingActivityInfo) IsSetLastHeartbeatTimestamp() bool { return v != nil && v.LastHeartbeatTimestamp != nil } // GetLastStartedTimestamp returns the value of LastStartedTimestamp if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetLastStartedTimestamp() (o int64) { if v != nil && v.LastStartedTimestamp != nil { return *v.LastStartedTimestamp } return } // IsSetLastStartedTimestamp returns true if LastStartedTimestamp is not nil. func (v *PendingActivityInfo) IsSetLastStartedTimestamp() bool { return v != nil && v.LastStartedTimestamp != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetAttempt() (o int32) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *PendingActivityInfo) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetMaximumAttempts returns the value of MaximumAttempts if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetMaximumAttempts() (o int32) { if v != nil && v.MaximumAttempts != nil { return *v.MaximumAttempts } return } // IsSetMaximumAttempts returns true if MaximumAttempts is not nil. func (v *PendingActivityInfo) IsSetMaximumAttempts() bool { return v != nil && v.MaximumAttempts != nil } // GetScheduledTimestamp returns the value of ScheduledTimestamp if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetScheduledTimestamp() (o int64) { if v != nil && v.ScheduledTimestamp != nil { return *v.ScheduledTimestamp } return } // IsSetScheduledTimestamp returns true if ScheduledTimestamp is not nil. func (v *PendingActivityInfo) IsSetScheduledTimestamp() bool { return v != nil && v.ScheduledTimestamp != nil } // GetExpirationTimestamp returns the value of ExpirationTimestamp if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetExpirationTimestamp() (o int64) { if v != nil && v.ExpirationTimestamp != nil { return *v.ExpirationTimestamp } return } // IsSetExpirationTimestamp returns true if ExpirationTimestamp is not nil. func (v *PendingActivityInfo) IsSetExpirationTimestamp() bool { return v != nil && v.ExpirationTimestamp != nil } // GetLastFailureReason returns the value of LastFailureReason if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetLastFailureReason() (o string) { if v != nil && v.LastFailureReason != nil { return *v.LastFailureReason } return } // IsSetLastFailureReason returns true if LastFailureReason is not nil. func (v *PendingActivityInfo) IsSetLastFailureReason() bool { return v != nil && v.LastFailureReason != nil } // GetLastWorkerIdentity returns the value of LastWorkerIdentity if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetLastWorkerIdentity() (o string) { if v != nil && v.LastWorkerIdentity != nil { return *v.LastWorkerIdentity } return } // IsSetLastWorkerIdentity returns true if LastWorkerIdentity is not nil. func (v *PendingActivityInfo) IsSetLastWorkerIdentity() bool { return v != nil && v.LastWorkerIdentity != nil } // GetLastFailureDetails returns the value of LastFailureDetails if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetLastFailureDetails() (o []byte) { if v != nil && v.LastFailureDetails != nil { return v.LastFailureDetails } return } // IsSetLastFailureDetails returns true if LastFailureDetails is not nil. func (v *PendingActivityInfo) IsSetLastFailureDetails() bool { return v != nil && v.LastFailureDetails != nil } // GetStartedWorkerIdentity returns the value of StartedWorkerIdentity if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetStartedWorkerIdentity() (o string) { if v != nil && v.StartedWorkerIdentity != nil { return *v.StartedWorkerIdentity } return } // IsSetStartedWorkerIdentity returns true if StartedWorkerIdentity is not nil. func (v *PendingActivityInfo) IsSetStartedWorkerIdentity() bool { return v != nil && v.StartedWorkerIdentity != nil } // GetScheduleID returns the value of ScheduleID if it is set or its // zero value if it is unset. func (v *PendingActivityInfo) GetScheduleID() (o int64) { if v != nil && v.ScheduleID != nil { return *v.ScheduleID } return } // IsSetScheduleID returns true if ScheduleID is not nil. func (v *PendingActivityInfo) IsSetScheduleID() bool { return v != nil && v.ScheduleID != nil } type PendingActivityState int32 const ( PendingActivityStateScheduled PendingActivityState = 0 PendingActivityStateStarted PendingActivityState = 1 PendingActivityStateCancelRequested PendingActivityState = 2 ) // PendingActivityState_Values returns all recognized values of PendingActivityState. func PendingActivityState_Values() []PendingActivityState { return []PendingActivityState{ PendingActivityStateScheduled, PendingActivityStateStarted, PendingActivityStateCancelRequested, } } // UnmarshalText tries to decode PendingActivityState from a byte slice // containing its name. // // var v PendingActivityState // err := v.UnmarshalText([]byte("SCHEDULED")) func (v *PendingActivityState) UnmarshalText(value []byte) error { switch s := string(value); s { case "SCHEDULED": *v = PendingActivityStateScheduled return nil case "STARTED": *v = PendingActivityStateStarted return nil case "CANCEL_REQUESTED": *v = PendingActivityStateCancelRequested return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "PendingActivityState", err) } *v = PendingActivityState(val) return nil } } // MarshalText encodes PendingActivityState to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v PendingActivityState) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("SCHEDULED"), nil case 1: return []byte("STARTED"), nil case 2: return []byte("CANCEL_REQUESTED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PendingActivityState. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v PendingActivityState) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "SCHEDULED") case 1: enc.AddString("name", "STARTED") case 2: enc.AddString("name", "CANCEL_REQUESTED") } return nil } // Ptr returns a pointer to this enum value. func (v PendingActivityState) Ptr() *PendingActivityState { return &v } // Encode encodes PendingActivityState directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v PendingActivityState // return v.Encode(sWriter) func (v PendingActivityState) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates PendingActivityState into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v PendingActivityState) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes PendingActivityState from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return PendingActivityState(0), err // } // // var v PendingActivityState // if err := v.FromWire(x); err != nil { // return PendingActivityState(0), err // } // return v, nil func (v *PendingActivityState) FromWire(w wire.Value) error { *v = (PendingActivityState)(w.GetI32()) return nil } // Decode reads off the encoded PendingActivityState directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v PendingActivityState // if err := v.Decode(sReader); err != nil { // return PendingActivityState(0), err // } // return v, nil func (v *PendingActivityState) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (PendingActivityState)(i) return nil } // String returns a readable string representation of PendingActivityState. func (v PendingActivityState) String() string { w := int32(v) switch w { case 0: return "SCHEDULED" case 1: return "STARTED" case 2: return "CANCEL_REQUESTED" } return fmt.Sprintf("PendingActivityState(%d)", w) } // Equals returns true if this PendingActivityState value matches the provided // value. func (v PendingActivityState) Equals(rhs PendingActivityState) bool { return v == rhs } // MarshalJSON serializes PendingActivityState into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v PendingActivityState) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"SCHEDULED\""), nil case 1: return ([]byte)("\"STARTED\""), nil case 2: return ([]byte)("\"CANCEL_REQUESTED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode PendingActivityState from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *PendingActivityState) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "PendingActivityState") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "PendingActivityState") } *v = (PendingActivityState)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "PendingActivityState") } } type PendingChildExecutionInfo struct { Domain *string `json:"domain,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID *string `json:"runID,omitempty"` WorkflowTypName *string `json:"workflowTypName,omitempty"` InitiatedID *int64 `json:"initiatedID,omitempty"` ParentClosePolicy *ParentClosePolicy `json:"parentClosePolicy,omitempty"` } // ToWire translates a PendingChildExecutionInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PendingChildExecutionInfo) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueString(*(v.RunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowTypName != nil { w, err = wire.NewValueString(*(v.WorkflowTypName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.InitiatedID != nil { w, err = wire.NewValueI64(*(v.InitiatedID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ParentClosePolicy != nil { w, err = v.ParentClosePolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a PendingChildExecutionInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PendingChildExecutionInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PendingChildExecutionInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PendingChildExecutionInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowTypName = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x ParentClosePolicy x, err = _ParentClosePolicy_Read(field.Value) v.ParentClosePolicy = &x if err != nil { return err } } } } return nil } // Encode serializes a PendingChildExecutionInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PendingChildExecutionInfo struct could not be encoded. func (v *PendingChildExecutionInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowTypName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowTypName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentClosePolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := v.ParentClosePolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a PendingChildExecutionInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PendingChildExecutionInfo struct could not be generated from the wire // representation. func (v *PendingChildExecutionInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowTypName = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x ParentClosePolicy x, err = _ParentClosePolicy_Decode(sr) v.ParentClosePolicy = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PendingChildExecutionInfo // struct. func (v *PendingChildExecutionInfo) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", *(v.RunID)) i++ } if v.WorkflowTypName != nil { fields[i] = fmt.Sprintf("WorkflowTypName: %v", *(v.WorkflowTypName)) i++ } if v.InitiatedID != nil { fields[i] = fmt.Sprintf("InitiatedID: %v", *(v.InitiatedID)) i++ } if v.ParentClosePolicy != nil { fields[i] = fmt.Sprintf("ParentClosePolicy: %v", *(v.ParentClosePolicy)) i++ } return fmt.Sprintf("PendingChildExecutionInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PendingChildExecutionInfo match the // provided PendingChildExecutionInfo. // // This function performs a deep comparison. func (v *PendingChildExecutionInfo) Equals(rhs *PendingChildExecutionInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !_String_EqualsPtr(v.RunID, rhs.RunID) { return false } if !_String_EqualsPtr(v.WorkflowTypName, rhs.WorkflowTypName) { return false } if !_I64_EqualsPtr(v.InitiatedID, rhs.InitiatedID) { return false } if !_ParentClosePolicy_EqualsPtr(v.ParentClosePolicy, rhs.ParentClosePolicy) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PendingChildExecutionInfo. func (v *PendingChildExecutionInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", *v.RunID) } if v.WorkflowTypName != nil { enc.AddString("workflowTypName", *v.WorkflowTypName) } if v.InitiatedID != nil { enc.AddInt64("initiatedID", *v.InitiatedID) } if v.ParentClosePolicy != nil { err = multierr.Append(err, enc.AddObject("parentClosePolicy", *v.ParentClosePolicy)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *PendingChildExecutionInfo) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *PendingChildExecutionInfo) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *PendingChildExecutionInfo) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *PendingChildExecutionInfo) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *PendingChildExecutionInfo) GetRunID() (o string) { if v != nil && v.RunID != nil { return *v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *PendingChildExecutionInfo) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetWorkflowTypName returns the value of WorkflowTypName if it is set or its // zero value if it is unset. func (v *PendingChildExecutionInfo) GetWorkflowTypName() (o string) { if v != nil && v.WorkflowTypName != nil { return *v.WorkflowTypName } return } // IsSetWorkflowTypName returns true if WorkflowTypName is not nil. func (v *PendingChildExecutionInfo) IsSetWorkflowTypName() bool { return v != nil && v.WorkflowTypName != nil } // GetInitiatedID returns the value of InitiatedID if it is set or its // zero value if it is unset. func (v *PendingChildExecutionInfo) GetInitiatedID() (o int64) { if v != nil && v.InitiatedID != nil { return *v.InitiatedID } return } // IsSetInitiatedID returns true if InitiatedID is not nil. func (v *PendingChildExecutionInfo) IsSetInitiatedID() bool { return v != nil && v.InitiatedID != nil } // GetParentClosePolicy returns the value of ParentClosePolicy if it is set or its // zero value if it is unset. func (v *PendingChildExecutionInfo) GetParentClosePolicy() (o ParentClosePolicy) { if v != nil && v.ParentClosePolicy != nil { return *v.ParentClosePolicy } return } // IsSetParentClosePolicy returns true if ParentClosePolicy is not nil. func (v *PendingChildExecutionInfo) IsSetParentClosePolicy() bool { return v != nil && v.ParentClosePolicy != nil } type PendingDecisionInfo struct { State *PendingDecisionState `json:"state,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Attempt *int64 `json:"attempt,omitempty"` OriginalScheduledTimestamp *int64 `json:"originalScheduledTimestamp,omitempty"` ScheduleID *int64 `json:"scheduleID,omitempty"` } // ToWire translates a PendingDecisionInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PendingDecisionInfo) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.State != nil { w, err = v.State.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ScheduledTimestamp != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.StartedTimestamp != nil { w, err = wire.NewValueI64(*(v.StartedTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI64(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.OriginalScheduledTimestamp != nil { w, err = wire.NewValueI64(*(v.OriginalScheduledTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ScheduleID != nil { w, err = wire.NewValueI64(*(v.ScheduleID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PendingDecisionState_Read(w wire.Value) (PendingDecisionState, error) { var v PendingDecisionState err := v.FromWire(w) return v, err } // FromWire deserializes a PendingDecisionInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PendingDecisionInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PendingDecisionInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PendingDecisionInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x PendingDecisionState x, err = _PendingDecisionState_Read(field.Value) v.State = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestamp = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTimestamp = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Attempt = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.OriginalScheduledTimestamp = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduleID = &x if err != nil { return err } } } } return nil } // Encode serializes a PendingDecisionInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PendingDecisionInfo struct could not be encoded. func (v *PendingDecisionInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.State != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.State.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.OriginalScheduledTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.OriginalScheduledTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduleID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PendingDecisionState_Decode(sr stream.Reader) (PendingDecisionState, error) { var v PendingDecisionState err := v.Decode(sr) return v, err } // Decode deserializes a PendingDecisionInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PendingDecisionInfo struct could not be generated from the wire // representation. func (v *PendingDecisionInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x PendingDecisionState x, err = _PendingDecisionState_Decode(sr) v.State = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestamp = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTimestamp = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Attempt = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.OriginalScheduledTimestamp = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduleID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PendingDecisionInfo // struct. func (v *PendingDecisionInfo) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.State != nil { fields[i] = fmt.Sprintf("State: %v", *(v.State)) i++ } if v.ScheduledTimestamp != nil { fields[i] = fmt.Sprintf("ScheduledTimestamp: %v", *(v.ScheduledTimestamp)) i++ } if v.StartedTimestamp != nil { fields[i] = fmt.Sprintf("StartedTimestamp: %v", *(v.StartedTimestamp)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.OriginalScheduledTimestamp != nil { fields[i] = fmt.Sprintf("OriginalScheduledTimestamp: %v", *(v.OriginalScheduledTimestamp)) i++ } if v.ScheduleID != nil { fields[i] = fmt.Sprintf("ScheduleID: %v", *(v.ScheduleID)) i++ } return fmt.Sprintf("PendingDecisionInfo{%v}", strings.Join(fields[:i], ", ")) } func _PendingDecisionState_EqualsPtr(lhs, rhs *PendingDecisionState) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this PendingDecisionInfo match the // provided PendingDecisionInfo. // // This function performs a deep comparison. func (v *PendingDecisionInfo) Equals(rhs *PendingDecisionInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_PendingDecisionState_EqualsPtr(v.State, rhs.State) { return false } if !_I64_EqualsPtr(v.ScheduledTimestamp, rhs.ScheduledTimestamp) { return false } if !_I64_EqualsPtr(v.StartedTimestamp, rhs.StartedTimestamp) { return false } if !_I64_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_I64_EqualsPtr(v.OriginalScheduledTimestamp, rhs.OriginalScheduledTimestamp) { return false } if !_I64_EqualsPtr(v.ScheduleID, rhs.ScheduleID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PendingDecisionInfo. func (v *PendingDecisionInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.State != nil { err = multierr.Append(err, enc.AddObject("state", *v.State)) } if v.ScheduledTimestamp != nil { enc.AddInt64("scheduledTimestamp", *v.ScheduledTimestamp) } if v.StartedTimestamp != nil { enc.AddInt64("startedTimestamp", *v.StartedTimestamp) } if v.Attempt != nil { enc.AddInt64("attempt", *v.Attempt) } if v.OriginalScheduledTimestamp != nil { enc.AddInt64("originalScheduledTimestamp", *v.OriginalScheduledTimestamp) } if v.ScheduleID != nil { enc.AddInt64("scheduleID", *v.ScheduleID) } return err } // GetState returns the value of State if it is set or its // zero value if it is unset. func (v *PendingDecisionInfo) GetState() (o PendingDecisionState) { if v != nil && v.State != nil { return *v.State } return } // IsSetState returns true if State is not nil. func (v *PendingDecisionInfo) IsSetState() bool { return v != nil && v.State != nil } // GetScheduledTimestamp returns the value of ScheduledTimestamp if it is set or its // zero value if it is unset. func (v *PendingDecisionInfo) GetScheduledTimestamp() (o int64) { if v != nil && v.ScheduledTimestamp != nil { return *v.ScheduledTimestamp } return } // IsSetScheduledTimestamp returns true if ScheduledTimestamp is not nil. func (v *PendingDecisionInfo) IsSetScheduledTimestamp() bool { return v != nil && v.ScheduledTimestamp != nil } // GetStartedTimestamp returns the value of StartedTimestamp if it is set or its // zero value if it is unset. func (v *PendingDecisionInfo) GetStartedTimestamp() (o int64) { if v != nil && v.StartedTimestamp != nil { return *v.StartedTimestamp } return } // IsSetStartedTimestamp returns true if StartedTimestamp is not nil. func (v *PendingDecisionInfo) IsSetStartedTimestamp() bool { return v != nil && v.StartedTimestamp != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *PendingDecisionInfo) GetAttempt() (o int64) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *PendingDecisionInfo) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetOriginalScheduledTimestamp returns the value of OriginalScheduledTimestamp if it is set or its // zero value if it is unset. func (v *PendingDecisionInfo) GetOriginalScheduledTimestamp() (o int64) { if v != nil && v.OriginalScheduledTimestamp != nil { return *v.OriginalScheduledTimestamp } return } // IsSetOriginalScheduledTimestamp returns true if OriginalScheduledTimestamp is not nil. func (v *PendingDecisionInfo) IsSetOriginalScheduledTimestamp() bool { return v != nil && v.OriginalScheduledTimestamp != nil } // GetScheduleID returns the value of ScheduleID if it is set or its // zero value if it is unset. func (v *PendingDecisionInfo) GetScheduleID() (o int64) { if v != nil && v.ScheduleID != nil { return *v.ScheduleID } return } // IsSetScheduleID returns true if ScheduleID is not nil. func (v *PendingDecisionInfo) IsSetScheduleID() bool { return v != nil && v.ScheduleID != nil } type PendingDecisionState int32 const ( PendingDecisionStateScheduled PendingDecisionState = 0 PendingDecisionStateStarted PendingDecisionState = 1 ) // PendingDecisionState_Values returns all recognized values of PendingDecisionState. func PendingDecisionState_Values() []PendingDecisionState { return []PendingDecisionState{ PendingDecisionStateScheduled, PendingDecisionStateStarted, } } // UnmarshalText tries to decode PendingDecisionState from a byte slice // containing its name. // // var v PendingDecisionState // err := v.UnmarshalText([]byte("SCHEDULED")) func (v *PendingDecisionState) UnmarshalText(value []byte) error { switch s := string(value); s { case "SCHEDULED": *v = PendingDecisionStateScheduled return nil case "STARTED": *v = PendingDecisionStateStarted return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "PendingDecisionState", err) } *v = PendingDecisionState(val) return nil } } // MarshalText encodes PendingDecisionState to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v PendingDecisionState) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("SCHEDULED"), nil case 1: return []byte("STARTED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PendingDecisionState. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v PendingDecisionState) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "SCHEDULED") case 1: enc.AddString("name", "STARTED") } return nil } // Ptr returns a pointer to this enum value. func (v PendingDecisionState) Ptr() *PendingDecisionState { return &v } // Encode encodes PendingDecisionState directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v PendingDecisionState // return v.Encode(sWriter) func (v PendingDecisionState) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates PendingDecisionState into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v PendingDecisionState) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes PendingDecisionState from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return PendingDecisionState(0), err // } // // var v PendingDecisionState // if err := v.FromWire(x); err != nil { // return PendingDecisionState(0), err // } // return v, nil func (v *PendingDecisionState) FromWire(w wire.Value) error { *v = (PendingDecisionState)(w.GetI32()) return nil } // Decode reads off the encoded PendingDecisionState directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v PendingDecisionState // if err := v.Decode(sReader); err != nil { // return PendingDecisionState(0), err // } // return v, nil func (v *PendingDecisionState) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (PendingDecisionState)(i) return nil } // String returns a readable string representation of PendingDecisionState. func (v PendingDecisionState) String() string { w := int32(v) switch w { case 0: return "SCHEDULED" case 1: return "STARTED" } return fmt.Sprintf("PendingDecisionState(%d)", w) } // Equals returns true if this PendingDecisionState value matches the provided // value. func (v PendingDecisionState) Equals(rhs PendingDecisionState) bool { return v == rhs } // MarshalJSON serializes PendingDecisionState into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v PendingDecisionState) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"SCHEDULED\""), nil case 1: return ([]byte)("\"STARTED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode PendingDecisionState from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *PendingDecisionState) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "PendingDecisionState") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "PendingDecisionState") } *v = (PendingDecisionState)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "PendingDecisionState") } } type PollForActivityTaskRequest struct { Domain *string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Identity *string `json:"identity,omitempty"` TaskListMetadata *TaskListMetadata `json:"taskListMetadata,omitempty"` } // ToWire translates a PollForActivityTaskRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PollForActivityTaskRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.TaskListMetadata != nil { w, err = v.TaskListMetadata.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TaskListMetadata_Read(w wire.Value) (*TaskListMetadata, error) { var v TaskListMetadata err := v.FromWire(w) return &v, err } // FromWire deserializes a PollForActivityTaskRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PollForActivityTaskRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PollForActivityTaskRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PollForActivityTaskRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.TaskListMetadata, err = _TaskListMetadata_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a PollForActivityTaskRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PollForActivityTaskRequest struct could not be encoded. func (v *PollForActivityTaskRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListMetadata != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.TaskListMetadata.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TaskListMetadata_Decode(sr stream.Reader) (*TaskListMetadata, error) { var v TaskListMetadata err := v.Decode(sr) return &v, err } // Decode deserializes a PollForActivityTaskRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PollForActivityTaskRequest struct could not be generated from the wire // representation. func (v *PollForActivityTaskRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.TaskListMetadata, err = _TaskListMetadata_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PollForActivityTaskRequest // struct. func (v *PollForActivityTaskRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.TaskListMetadata != nil { fields[i] = fmt.Sprintf("TaskListMetadata: %v", v.TaskListMetadata) i++ } return fmt.Sprintf("PollForActivityTaskRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PollForActivityTaskRequest match the // provided PollForActivityTaskRequest. // // This function performs a deep comparison. func (v *PollForActivityTaskRequest) Equals(rhs *PollForActivityTaskRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !((v.TaskListMetadata == nil && rhs.TaskListMetadata == nil) || (v.TaskListMetadata != nil && rhs.TaskListMetadata != nil && v.TaskListMetadata.Equals(rhs.TaskListMetadata))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PollForActivityTaskRequest. func (v *PollForActivityTaskRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.TaskListMetadata != nil { err = multierr.Append(err, enc.AddObject("taskListMetadata", v.TaskListMetadata)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *PollForActivityTaskRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *PollForActivityTaskRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *PollForActivityTaskRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *PollForActivityTaskRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *PollForActivityTaskRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *PollForActivityTaskRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetTaskListMetadata returns the value of TaskListMetadata if it is set or its // zero value if it is unset. func (v *PollForActivityTaskRequest) GetTaskListMetadata() (o *TaskListMetadata) { if v != nil && v.TaskListMetadata != nil { return v.TaskListMetadata } return } // IsSetTaskListMetadata returns true if TaskListMetadata is not nil. func (v *PollForActivityTaskRequest) IsSetTaskListMetadata() bool { return v != nil && v.TaskListMetadata != nil } type PollForActivityTaskResponse struct { TaskToken []byte `json:"taskToken,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` ActivityId *string `json:"activityId,omitempty"` ActivityType *ActivityType `json:"activityType,omitempty"` Input []byte `json:"input,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` ScheduleToCloseTimeoutSeconds *int32 `json:"scheduleToCloseTimeoutSeconds,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` StartToCloseTimeoutSeconds *int32 `json:"startToCloseTimeoutSeconds,omitempty"` HeartbeatTimeoutSeconds *int32 `json:"heartbeatTimeoutSeconds,omitempty"` Attempt *int32 `json:"attempt,omitempty"` ScheduledTimestampOfThisAttempt *int64 `json:"scheduledTimestampOfThisAttempt,omitempty"` HeartbeatDetails []byte `json:"heartbeatDetails,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` WorkflowDomain *string `json:"workflowDomain,omitempty"` Header *Header `json:"header,omitempty"` AutoConfigHint *AutoConfigHint `json:"autoConfigHint,omitempty"` } // ToWire translates a PollForActivityTaskResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PollForActivityTaskResponse) ToWire() (wire.Value, error) { var ( fields [17]wire.Field i int = 0 w wire.Value err error ) if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ActivityId != nil { w, err = wire.NewValueString(*(v.ActivityId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ActivityType != nil { w, err = v.ActivityType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ScheduledTimestamp != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.ScheduleToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ScheduleToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.StartedTimestamp != nil { w, err = wire.NewValueI64(*(v.StartedTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.StartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.StartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.HeartbeatTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.HeartbeatTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI32(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.ScheduledTimestampOfThisAttempt != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestampOfThisAttempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.HeartbeatDetails != nil { w, err = wire.NewValueBinary(v.HeartbeatDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.WorkflowDomain != nil { w, err = wire.NewValueString(*(v.WorkflowDomain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } if v.AutoConfigHint != nil { w, err = v.AutoConfigHint.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 180, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AutoConfigHint_Read(w wire.Value) (*AutoConfigHint, error) { var v AutoConfigHint err := v.FromWire(w) return &v, err } // FromWire deserializes a PollForActivityTaskResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PollForActivityTaskResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PollForActivityTaskResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PollForActivityTaskResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.ActivityType, err = _ActivityType_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 70: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestamp = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ScheduleToCloseTimeoutSeconds = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTimestamp = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.StartToCloseTimeoutSeconds = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.HeartbeatTimeoutSeconds = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Attempt = &x if err != nil { return err } } case 130: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestampOfThisAttempt = &x if err != nil { return err } } case 140: if field.Value.Type() == wire.TBinary { v.HeartbeatDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 150: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 160: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowDomain = &x if err != nil { return err } } case 170: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } case 180: if field.Value.Type() == wire.TStruct { v.AutoConfigHint, err = _AutoConfigHint_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a PollForActivityTaskResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PollForActivityTaskResponse struct could not be encoded. func (v *PollForActivityTaskResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ScheduleToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.StartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HeartbeatTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.HeartbeatTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestampOfThisAttempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestampOfThisAttempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HeartbeatDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.HeartbeatDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowDomain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowDomain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AutoConfigHint != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 180, Type: wire.TStruct}); err != nil { return err } if err := v.AutoConfigHint.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _AutoConfigHint_Decode(sr stream.Reader) (*AutoConfigHint, error) { var v AutoConfigHint err := v.Decode(sr) return &v, err } // Decode deserializes a PollForActivityTaskResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PollForActivityTaskResponse struct could not be generated from the wire // representation. func (v *PollForActivityTaskResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.ActivityType, err = _ActivityType_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestamp = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ScheduleToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTimestamp = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.StartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.HeartbeatTimeoutSeconds = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Attempt = &x if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestampOfThisAttempt = &x if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TBinary: v.HeartbeatDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowDomain = &x if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } case fh.ID == 180 && fh.Type == wire.TStruct: v.AutoConfigHint, err = _AutoConfigHint_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PollForActivityTaskResponse // struct. func (v *PollForActivityTaskResponse) String() string { if v == nil { return "" } var fields [17]string i := 0 if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.ActivityId != nil { fields[i] = fmt.Sprintf("ActivityId: %v", *(v.ActivityId)) i++ } if v.ActivityType != nil { fields[i] = fmt.Sprintf("ActivityType: %v", v.ActivityType) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.ScheduledTimestamp != nil { fields[i] = fmt.Sprintf("ScheduledTimestamp: %v", *(v.ScheduledTimestamp)) i++ } if v.ScheduleToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ScheduleToCloseTimeoutSeconds: %v", *(v.ScheduleToCloseTimeoutSeconds)) i++ } if v.StartedTimestamp != nil { fields[i] = fmt.Sprintf("StartedTimestamp: %v", *(v.StartedTimestamp)) i++ } if v.StartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("StartToCloseTimeoutSeconds: %v", *(v.StartToCloseTimeoutSeconds)) i++ } if v.HeartbeatTimeoutSeconds != nil { fields[i] = fmt.Sprintf("HeartbeatTimeoutSeconds: %v", *(v.HeartbeatTimeoutSeconds)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.ScheduledTimestampOfThisAttempt != nil { fields[i] = fmt.Sprintf("ScheduledTimestampOfThisAttempt: %v", *(v.ScheduledTimestampOfThisAttempt)) i++ } if v.HeartbeatDetails != nil { fields[i] = fmt.Sprintf("HeartbeatDetails: %v", v.HeartbeatDetails) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.WorkflowDomain != nil { fields[i] = fmt.Sprintf("WorkflowDomain: %v", *(v.WorkflowDomain)) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } if v.AutoConfigHint != nil { fields[i] = fmt.Sprintf("AutoConfigHint: %v", v.AutoConfigHint) i++ } return fmt.Sprintf("PollForActivityTaskResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PollForActivityTaskResponse match the // provided PollForActivityTaskResponse. // // This function performs a deep comparison. func (v *PollForActivityTaskResponse) Equals(rhs *PollForActivityTaskResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_String_EqualsPtr(v.ActivityId, rhs.ActivityId) { return false } if !((v.ActivityType == nil && rhs.ActivityType == nil) || (v.ActivityType != nil && rhs.ActivityType != nil && v.ActivityType.Equals(rhs.ActivityType))) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_I64_EqualsPtr(v.ScheduledTimestamp, rhs.ScheduledTimestamp) { return false } if !_I32_EqualsPtr(v.ScheduleToCloseTimeoutSeconds, rhs.ScheduleToCloseTimeoutSeconds) { return false } if !_I64_EqualsPtr(v.StartedTimestamp, rhs.StartedTimestamp) { return false } if !_I32_EqualsPtr(v.StartToCloseTimeoutSeconds, rhs.StartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.HeartbeatTimeoutSeconds, rhs.HeartbeatTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_I64_EqualsPtr(v.ScheduledTimestampOfThisAttempt, rhs.ScheduledTimestampOfThisAttempt) { return false } if !((v.HeartbeatDetails == nil && rhs.HeartbeatDetails == nil) || (v.HeartbeatDetails != nil && rhs.HeartbeatDetails != nil && bytes.Equal(v.HeartbeatDetails, rhs.HeartbeatDetails))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_String_EqualsPtr(v.WorkflowDomain, rhs.WorkflowDomain) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } if !((v.AutoConfigHint == nil && rhs.AutoConfigHint == nil) || (v.AutoConfigHint != nil && rhs.AutoConfigHint != nil && v.AutoConfigHint.Equals(rhs.AutoConfigHint))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PollForActivityTaskResponse. func (v *PollForActivityTaskResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.ActivityId != nil { enc.AddString("activityId", *v.ActivityId) } if v.ActivityType != nil { err = multierr.Append(err, enc.AddObject("activityType", v.ActivityType)) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.ScheduledTimestamp != nil { enc.AddInt64("scheduledTimestamp", *v.ScheduledTimestamp) } if v.ScheduleToCloseTimeoutSeconds != nil { enc.AddInt32("scheduleToCloseTimeoutSeconds", *v.ScheduleToCloseTimeoutSeconds) } if v.StartedTimestamp != nil { enc.AddInt64("startedTimestamp", *v.StartedTimestamp) } if v.StartToCloseTimeoutSeconds != nil { enc.AddInt32("startToCloseTimeoutSeconds", *v.StartToCloseTimeoutSeconds) } if v.HeartbeatTimeoutSeconds != nil { enc.AddInt32("heartbeatTimeoutSeconds", *v.HeartbeatTimeoutSeconds) } if v.Attempt != nil { enc.AddInt32("attempt", *v.Attempt) } if v.ScheduledTimestampOfThisAttempt != nil { enc.AddInt64("scheduledTimestampOfThisAttempt", *v.ScheduledTimestampOfThisAttempt) } if v.HeartbeatDetails != nil { enc.AddString("heartbeatDetails", base64.StdEncoding.EncodeToString(v.HeartbeatDetails)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.WorkflowDomain != nil { enc.AddString("workflowDomain", *v.WorkflowDomain) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } if v.AutoConfigHint != nil { err = multierr.Append(err, enc.AddObject("autoConfigHint", v.AutoConfigHint)) } return err } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *PollForActivityTaskResponse) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *PollForActivityTaskResponse) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetActivityId returns the value of ActivityId if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetActivityId() (o string) { if v != nil && v.ActivityId != nil { return *v.ActivityId } return } // IsSetActivityId returns true if ActivityId is not nil. func (v *PollForActivityTaskResponse) IsSetActivityId() bool { return v != nil && v.ActivityId != nil } // GetActivityType returns the value of ActivityType if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetActivityType() (o *ActivityType) { if v != nil && v.ActivityType != nil { return v.ActivityType } return } // IsSetActivityType returns true if ActivityType is not nil. func (v *PollForActivityTaskResponse) IsSetActivityType() bool { return v != nil && v.ActivityType != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *PollForActivityTaskResponse) IsSetInput() bool { return v != nil && v.Input != nil } // GetScheduledTimestamp returns the value of ScheduledTimestamp if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetScheduledTimestamp() (o int64) { if v != nil && v.ScheduledTimestamp != nil { return *v.ScheduledTimestamp } return } // IsSetScheduledTimestamp returns true if ScheduledTimestamp is not nil. func (v *PollForActivityTaskResponse) IsSetScheduledTimestamp() bool { return v != nil && v.ScheduledTimestamp != nil } // GetScheduleToCloseTimeoutSeconds returns the value of ScheduleToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetScheduleToCloseTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToCloseTimeoutSeconds != nil { return *v.ScheduleToCloseTimeoutSeconds } return } // IsSetScheduleToCloseTimeoutSeconds returns true if ScheduleToCloseTimeoutSeconds is not nil. func (v *PollForActivityTaskResponse) IsSetScheduleToCloseTimeoutSeconds() bool { return v != nil && v.ScheduleToCloseTimeoutSeconds != nil } // GetStartedTimestamp returns the value of StartedTimestamp if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetStartedTimestamp() (o int64) { if v != nil && v.StartedTimestamp != nil { return *v.StartedTimestamp } return } // IsSetStartedTimestamp returns true if StartedTimestamp is not nil. func (v *PollForActivityTaskResponse) IsSetStartedTimestamp() bool { return v != nil && v.StartedTimestamp != nil } // GetStartToCloseTimeoutSeconds returns the value of StartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.StartToCloseTimeoutSeconds != nil { return *v.StartToCloseTimeoutSeconds } return } // IsSetStartToCloseTimeoutSeconds returns true if StartToCloseTimeoutSeconds is not nil. func (v *PollForActivityTaskResponse) IsSetStartToCloseTimeoutSeconds() bool { return v != nil && v.StartToCloseTimeoutSeconds != nil } // GetHeartbeatTimeoutSeconds returns the value of HeartbeatTimeoutSeconds if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetHeartbeatTimeoutSeconds() (o int32) { if v != nil && v.HeartbeatTimeoutSeconds != nil { return *v.HeartbeatTimeoutSeconds } return } // IsSetHeartbeatTimeoutSeconds returns true if HeartbeatTimeoutSeconds is not nil. func (v *PollForActivityTaskResponse) IsSetHeartbeatTimeoutSeconds() bool { return v != nil && v.HeartbeatTimeoutSeconds != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetAttempt() (o int32) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *PollForActivityTaskResponse) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetScheduledTimestampOfThisAttempt returns the value of ScheduledTimestampOfThisAttempt if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetScheduledTimestampOfThisAttempt() (o int64) { if v != nil && v.ScheduledTimestampOfThisAttempt != nil { return *v.ScheduledTimestampOfThisAttempt } return } // IsSetScheduledTimestampOfThisAttempt returns true if ScheduledTimestampOfThisAttempt is not nil. func (v *PollForActivityTaskResponse) IsSetScheduledTimestampOfThisAttempt() bool { return v != nil && v.ScheduledTimestampOfThisAttempt != nil } // GetHeartbeatDetails returns the value of HeartbeatDetails if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetHeartbeatDetails() (o []byte) { if v != nil && v.HeartbeatDetails != nil { return v.HeartbeatDetails } return } // IsSetHeartbeatDetails returns true if HeartbeatDetails is not nil. func (v *PollForActivityTaskResponse) IsSetHeartbeatDetails() bool { return v != nil && v.HeartbeatDetails != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *PollForActivityTaskResponse) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetWorkflowDomain returns the value of WorkflowDomain if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetWorkflowDomain() (o string) { if v != nil && v.WorkflowDomain != nil { return *v.WorkflowDomain } return } // IsSetWorkflowDomain returns true if WorkflowDomain is not nil. func (v *PollForActivityTaskResponse) IsSetWorkflowDomain() bool { return v != nil && v.WorkflowDomain != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *PollForActivityTaskResponse) IsSetHeader() bool { return v != nil && v.Header != nil } // GetAutoConfigHint returns the value of AutoConfigHint if it is set or its // zero value if it is unset. func (v *PollForActivityTaskResponse) GetAutoConfigHint() (o *AutoConfigHint) { if v != nil && v.AutoConfigHint != nil { return v.AutoConfigHint } return } // IsSetAutoConfigHint returns true if AutoConfigHint is not nil. func (v *PollForActivityTaskResponse) IsSetAutoConfigHint() bool { return v != nil && v.AutoConfigHint != nil } type PollForDecisionTaskRequest struct { Domain *string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Identity *string `json:"identity,omitempty"` BinaryChecksum *string `json:"binaryChecksum,omitempty"` } // ToWire translates a PollForDecisionTaskRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PollForDecisionTaskRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.BinaryChecksum != nil { w, err = wire.NewValueString(*(v.BinaryChecksum)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a PollForDecisionTaskRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PollForDecisionTaskRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PollForDecisionTaskRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PollForDecisionTaskRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BinaryChecksum = &x if err != nil { return err } } } } return nil } // Encode serializes a PollForDecisionTaskRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PollForDecisionTaskRequest struct could not be encoded. func (v *PollForDecisionTaskRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BinaryChecksum != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BinaryChecksum)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a PollForDecisionTaskRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PollForDecisionTaskRequest struct could not be generated from the wire // representation. func (v *PollForDecisionTaskRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BinaryChecksum = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PollForDecisionTaskRequest // struct. func (v *PollForDecisionTaskRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.BinaryChecksum != nil { fields[i] = fmt.Sprintf("BinaryChecksum: %v", *(v.BinaryChecksum)) i++ } return fmt.Sprintf("PollForDecisionTaskRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PollForDecisionTaskRequest match the // provided PollForDecisionTaskRequest. // // This function performs a deep comparison. func (v *PollForDecisionTaskRequest) Equals(rhs *PollForDecisionTaskRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.BinaryChecksum, rhs.BinaryChecksum) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PollForDecisionTaskRequest. func (v *PollForDecisionTaskRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.BinaryChecksum != nil { enc.AddString("binaryChecksum", *v.BinaryChecksum) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *PollForDecisionTaskRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *PollForDecisionTaskRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *PollForDecisionTaskRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetBinaryChecksum returns the value of BinaryChecksum if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskRequest) GetBinaryChecksum() (o string) { if v != nil && v.BinaryChecksum != nil { return *v.BinaryChecksum } return } // IsSetBinaryChecksum returns true if BinaryChecksum is not nil. func (v *PollForDecisionTaskRequest) IsSetBinaryChecksum() bool { return v != nil && v.BinaryChecksum != nil } type PollForDecisionTaskResponse struct { TaskToken []byte `json:"taskToken,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` PreviousStartedEventId *int64 `json:"previousStartedEventId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` Attempt *int64 `json:"attempt,omitempty"` BacklogCountHint *int64 `json:"backlogCountHint,omitempty"` History *History `json:"history,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` Query *WorkflowQuery `json:"query,omitempty"` WorkflowExecutionTaskList *TaskList `json:"WorkflowExecutionTaskList,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Queries map[string]*WorkflowQuery `json:"queries,omitempty"` NextEventId *int64 `json:"nextEventId,omitempty"` TotalHistoryBytes *int64 `json:"totalHistoryBytes,omitempty"` AutoConfigHint *AutoConfigHint `json:"autoConfigHint,omitempty"` } type _Map_String_WorkflowQuery_MapItemList map[string]*WorkflowQuery func (m _Map_String_WorkflowQuery_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*WorkflowQuery', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_WorkflowQuery_MapItemList) Size() int { return len(m) } func (_Map_String_WorkflowQuery_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_WorkflowQuery_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_WorkflowQuery_MapItemList) Close() {} // ToWire translates a PollForDecisionTaskResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PollForDecisionTaskResponse) ToWire() (wire.Value, error) { var ( fields [17]wire.Field i int = 0 w wire.Value err error ) if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.PreviousStartedEventId != nil { w, err = wire.NewValueI64(*(v.PreviousStartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI64(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 51, Value: w} i++ } if v.BacklogCountHint != nil { w, err = wire.NewValueI64(*(v.BacklogCountHint)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 54, Value: w} i++ } if v.History != nil { w, err = v.History.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.NextPageToken != nil { w, err = wire.NewValueBinary(v.NextPageToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.Query != nil { w, err = v.Query.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.WorkflowExecutionTaskList != nil { w, err = v.WorkflowExecutionTaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.ScheduledTimestamp != nil { w, err = wire.NewValueI64(*(v.ScheduledTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.StartedTimestamp != nil { w, err = wire.NewValueI64(*(v.StartedTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.Queries != nil { w, err = wire.NewValueMap(_Map_String_WorkflowQuery_MapItemList(v.Queries)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.NextEventId != nil { w, err = wire.NewValueI64(*(v.NextEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.TotalHistoryBytes != nil { w, err = wire.NewValueI64(*(v.TotalHistoryBytes)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.AutoConfigHint != nil { w, err = v.AutoConfigHint.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WorkflowQuery_Read(w wire.Value) (*WorkflowQuery, error) { var v WorkflowQuery err := v.FromWire(w) return &v, err } func _Map_String_WorkflowQuery_Read(m wire.MapItemList) (map[string]*WorkflowQuery, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*WorkflowQuery, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _WorkflowQuery_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a PollForDecisionTaskResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PollForDecisionTaskResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PollForDecisionTaskResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PollForDecisionTaskResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.PreviousStartedEventId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 51: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Attempt = &x if err != nil { return err } } case 54: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.BacklogCountHint = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TStruct { v.History, err = _History_Read(field.Value) if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { v.NextPageToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 80: if field.Value.Type() == wire.TStruct { v.Query, err = _WorkflowQuery_Read(field.Value) if err != nil { return err } } case 90: if field.Value.Type() == wire.TStruct { v.WorkflowExecutionTaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 100: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimestamp = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTimestamp = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TMap { v.Queries, err = _Map_String_WorkflowQuery_Read(field.Value.GetMap()) if err != nil { return err } } case 130: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NextEventId = &x if err != nil { return err } } case 140: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TotalHistoryBytes = &x if err != nil { return err } } case 150: if field.Value.Type() == wire.TStruct { v.AutoConfigHint, err = _AutoConfigHint_Read(field.Value) if err != nil { return err } } } } return nil } func _Map_String_WorkflowQuery_Encode(val map[string]*WorkflowQuery, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*WorkflowQuery', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a PollForDecisionTaskResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PollForDecisionTaskResponse struct could not be encoded. func (v *PollForDecisionTaskResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PreviousStartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.PreviousStartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 51, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BacklogCountHint != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 54, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.BacklogCountHint)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.History != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TStruct}); err != nil { return err } if err := v.History.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextPageToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NextPageToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Query != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TStruct}); err != nil { return err } if err := v.Query.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionTaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecutionTaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Queries != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TMap}); err != nil { return err } if err := _Map_String_WorkflowQuery_Encode(v.Queries, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NextEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TotalHistoryBytes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TotalHistoryBytes)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AutoConfigHint != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TStruct}); err != nil { return err } if err := v.AutoConfigHint.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _WorkflowQuery_Decode(sr stream.Reader) (*WorkflowQuery, error) { var v WorkflowQuery err := v.Decode(sr) return &v, err } func _Map_String_WorkflowQuery_Decode(sr stream.Reader) (map[string]*WorkflowQuery, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*WorkflowQuery, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _WorkflowQuery_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a PollForDecisionTaskResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PollForDecisionTaskResponse struct could not be generated from the wire // representation. func (v *PollForDecisionTaskResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.PreviousStartedEventId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 51 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Attempt = &x if err != nil { return err } case fh.ID == 54 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.BacklogCountHint = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TStruct: v.History, err = _History_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: v.NextPageToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TStruct: v.Query, err = _WorkflowQuery_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TStruct: v.WorkflowExecutionTaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimestamp = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTimestamp = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TMap: v.Queries, err = _Map_String_WorkflowQuery_Decode(sr) if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NextEventId = &x if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TotalHistoryBytes = &x if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TStruct: v.AutoConfigHint, err = _AutoConfigHint_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PollForDecisionTaskResponse // struct. func (v *PollForDecisionTaskResponse) String() string { if v == nil { return "" } var fields [17]string i := 0 if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.PreviousStartedEventId != nil { fields[i] = fmt.Sprintf("PreviousStartedEventId: %v", *(v.PreviousStartedEventId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.BacklogCountHint != nil { fields[i] = fmt.Sprintf("BacklogCountHint: %v", *(v.BacklogCountHint)) i++ } if v.History != nil { fields[i] = fmt.Sprintf("History: %v", v.History) i++ } if v.NextPageToken != nil { fields[i] = fmt.Sprintf("NextPageToken: %v", v.NextPageToken) i++ } if v.Query != nil { fields[i] = fmt.Sprintf("Query: %v", v.Query) i++ } if v.WorkflowExecutionTaskList != nil { fields[i] = fmt.Sprintf("WorkflowExecutionTaskList: %v", v.WorkflowExecutionTaskList) i++ } if v.ScheduledTimestamp != nil { fields[i] = fmt.Sprintf("ScheduledTimestamp: %v", *(v.ScheduledTimestamp)) i++ } if v.StartedTimestamp != nil { fields[i] = fmt.Sprintf("StartedTimestamp: %v", *(v.StartedTimestamp)) i++ } if v.Queries != nil { fields[i] = fmt.Sprintf("Queries: %v", v.Queries) i++ } if v.NextEventId != nil { fields[i] = fmt.Sprintf("NextEventId: %v", *(v.NextEventId)) i++ } if v.TotalHistoryBytes != nil { fields[i] = fmt.Sprintf("TotalHistoryBytes: %v", *(v.TotalHistoryBytes)) i++ } if v.AutoConfigHint != nil { fields[i] = fmt.Sprintf("AutoConfigHint: %v", v.AutoConfigHint) i++ } return fmt.Sprintf("PollForDecisionTaskResponse{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_WorkflowQuery_Equals(lhs, rhs map[string]*WorkflowQuery) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this PollForDecisionTaskResponse match the // provided PollForDecisionTaskResponse. // // This function performs a deep comparison. func (v *PollForDecisionTaskResponse) Equals(rhs *PollForDecisionTaskResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_I64_EqualsPtr(v.PreviousStartedEventId, rhs.PreviousStartedEventId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_I64_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_I64_EqualsPtr(v.BacklogCountHint, rhs.BacklogCountHint) { return false } if !((v.History == nil && rhs.History == nil) || (v.History != nil && rhs.History != nil && v.History.Equals(rhs.History))) { return false } if !((v.NextPageToken == nil && rhs.NextPageToken == nil) || (v.NextPageToken != nil && rhs.NextPageToken != nil && bytes.Equal(v.NextPageToken, rhs.NextPageToken))) { return false } if !((v.Query == nil && rhs.Query == nil) || (v.Query != nil && rhs.Query != nil && v.Query.Equals(rhs.Query))) { return false } if !((v.WorkflowExecutionTaskList == nil && rhs.WorkflowExecutionTaskList == nil) || (v.WorkflowExecutionTaskList != nil && rhs.WorkflowExecutionTaskList != nil && v.WorkflowExecutionTaskList.Equals(rhs.WorkflowExecutionTaskList))) { return false } if !_I64_EqualsPtr(v.ScheduledTimestamp, rhs.ScheduledTimestamp) { return false } if !_I64_EqualsPtr(v.StartedTimestamp, rhs.StartedTimestamp) { return false } if !((v.Queries == nil && rhs.Queries == nil) || (v.Queries != nil && rhs.Queries != nil && _Map_String_WorkflowQuery_Equals(v.Queries, rhs.Queries))) { return false } if !_I64_EqualsPtr(v.NextEventId, rhs.NextEventId) { return false } if !_I64_EqualsPtr(v.TotalHistoryBytes, rhs.TotalHistoryBytes) { return false } if !((v.AutoConfigHint == nil && rhs.AutoConfigHint == nil) || (v.AutoConfigHint != nil && rhs.AutoConfigHint != nil && v.AutoConfigHint.Equals(rhs.AutoConfigHint))) { return false } return true } type _Map_String_WorkflowQuery_Zapper map[string]*WorkflowQuery // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_WorkflowQuery_Zapper. func (m _Map_String_WorkflowQuery_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PollForDecisionTaskResponse. func (v *PollForDecisionTaskResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.PreviousStartedEventId != nil { enc.AddInt64("previousStartedEventId", *v.PreviousStartedEventId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.Attempt != nil { enc.AddInt64("attempt", *v.Attempt) } if v.BacklogCountHint != nil { enc.AddInt64("backlogCountHint", *v.BacklogCountHint) } if v.History != nil { err = multierr.Append(err, enc.AddObject("history", v.History)) } if v.NextPageToken != nil { enc.AddString("nextPageToken", base64.StdEncoding.EncodeToString(v.NextPageToken)) } if v.Query != nil { err = multierr.Append(err, enc.AddObject("query", v.Query)) } if v.WorkflowExecutionTaskList != nil { err = multierr.Append(err, enc.AddObject("WorkflowExecutionTaskList", v.WorkflowExecutionTaskList)) } if v.ScheduledTimestamp != nil { enc.AddInt64("scheduledTimestamp", *v.ScheduledTimestamp) } if v.StartedTimestamp != nil { enc.AddInt64("startedTimestamp", *v.StartedTimestamp) } if v.Queries != nil { err = multierr.Append(err, enc.AddObject("queries", (_Map_String_WorkflowQuery_Zapper)(v.Queries))) } if v.NextEventId != nil { enc.AddInt64("nextEventId", *v.NextEventId) } if v.TotalHistoryBytes != nil { enc.AddInt64("totalHistoryBytes", *v.TotalHistoryBytes) } if v.AutoConfigHint != nil { err = multierr.Append(err, enc.AddObject("autoConfigHint", v.AutoConfigHint)) } return err } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *PollForDecisionTaskResponse) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *PollForDecisionTaskResponse) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *PollForDecisionTaskResponse) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetPreviousStartedEventId returns the value of PreviousStartedEventId if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetPreviousStartedEventId() (o int64) { if v != nil && v.PreviousStartedEventId != nil { return *v.PreviousStartedEventId } return } // IsSetPreviousStartedEventId returns true if PreviousStartedEventId is not nil. func (v *PollForDecisionTaskResponse) IsSetPreviousStartedEventId() bool { return v != nil && v.PreviousStartedEventId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *PollForDecisionTaskResponse) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetAttempt() (o int64) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *PollForDecisionTaskResponse) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetBacklogCountHint returns the value of BacklogCountHint if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetBacklogCountHint() (o int64) { if v != nil && v.BacklogCountHint != nil { return *v.BacklogCountHint } return } // IsSetBacklogCountHint returns true if BacklogCountHint is not nil. func (v *PollForDecisionTaskResponse) IsSetBacklogCountHint() bool { return v != nil && v.BacklogCountHint != nil } // GetHistory returns the value of History if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetHistory() (o *History) { if v != nil && v.History != nil { return v.History } return } // IsSetHistory returns true if History is not nil. func (v *PollForDecisionTaskResponse) IsSetHistory() bool { return v != nil && v.History != nil } // GetNextPageToken returns the value of NextPageToken if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // IsSetNextPageToken returns true if NextPageToken is not nil. func (v *PollForDecisionTaskResponse) IsSetNextPageToken() bool { return v != nil && v.NextPageToken != nil } // GetQuery returns the value of Query if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetQuery() (o *WorkflowQuery) { if v != nil && v.Query != nil { return v.Query } return } // IsSetQuery returns true if Query is not nil. func (v *PollForDecisionTaskResponse) IsSetQuery() bool { return v != nil && v.Query != nil } // GetWorkflowExecutionTaskList returns the value of WorkflowExecutionTaskList if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetWorkflowExecutionTaskList() (o *TaskList) { if v != nil && v.WorkflowExecutionTaskList != nil { return v.WorkflowExecutionTaskList } return } // IsSetWorkflowExecutionTaskList returns true if WorkflowExecutionTaskList is not nil. func (v *PollForDecisionTaskResponse) IsSetWorkflowExecutionTaskList() bool { return v != nil && v.WorkflowExecutionTaskList != nil } // GetScheduledTimestamp returns the value of ScheduledTimestamp if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetScheduledTimestamp() (o int64) { if v != nil && v.ScheduledTimestamp != nil { return *v.ScheduledTimestamp } return } // IsSetScheduledTimestamp returns true if ScheduledTimestamp is not nil. func (v *PollForDecisionTaskResponse) IsSetScheduledTimestamp() bool { return v != nil && v.ScheduledTimestamp != nil } // GetStartedTimestamp returns the value of StartedTimestamp if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetStartedTimestamp() (o int64) { if v != nil && v.StartedTimestamp != nil { return *v.StartedTimestamp } return } // IsSetStartedTimestamp returns true if StartedTimestamp is not nil. func (v *PollForDecisionTaskResponse) IsSetStartedTimestamp() bool { return v != nil && v.StartedTimestamp != nil } // GetQueries returns the value of Queries if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetQueries() (o map[string]*WorkflowQuery) { if v != nil && v.Queries != nil { return v.Queries } return } // IsSetQueries returns true if Queries is not nil. func (v *PollForDecisionTaskResponse) IsSetQueries() bool { return v != nil && v.Queries != nil } // GetNextEventId returns the value of NextEventId if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetNextEventId() (o int64) { if v != nil && v.NextEventId != nil { return *v.NextEventId } return } // IsSetNextEventId returns true if NextEventId is not nil. func (v *PollForDecisionTaskResponse) IsSetNextEventId() bool { return v != nil && v.NextEventId != nil } // GetTotalHistoryBytes returns the value of TotalHistoryBytes if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetTotalHistoryBytes() (o int64) { if v != nil && v.TotalHistoryBytes != nil { return *v.TotalHistoryBytes } return } // IsSetTotalHistoryBytes returns true if TotalHistoryBytes is not nil. func (v *PollForDecisionTaskResponse) IsSetTotalHistoryBytes() bool { return v != nil && v.TotalHistoryBytes != nil } // GetAutoConfigHint returns the value of AutoConfigHint if it is set or its // zero value if it is unset. func (v *PollForDecisionTaskResponse) GetAutoConfigHint() (o *AutoConfigHint) { if v != nil && v.AutoConfigHint != nil { return v.AutoConfigHint } return } // IsSetAutoConfigHint returns true if AutoConfigHint is not nil. func (v *PollForDecisionTaskResponse) IsSetAutoConfigHint() bool { return v != nil && v.AutoConfigHint != nil } type PollerInfo struct { LastAccessTime *int64 `json:"lastAccessTime,omitempty"` Identity *string `json:"identity,omitempty"` RatePerSecond *float64 `json:"ratePerSecond,omitempty"` } // ToWire translates a PollerInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *PollerInfo) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.LastAccessTime != nil { w, err = wire.NewValueI64(*(v.LastAccessTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RatePerSecond != nil { w, err = wire.NewValueDouble(*(v.RatePerSecond)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a PollerInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a PollerInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v PollerInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *PollerInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastAccessTime = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TDouble { var x float64 x, err = field.Value.GetDouble(), error(nil) v.RatePerSecond = &x if err != nil { return err } } } } return nil } // Encode serializes a PollerInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a PollerInfo struct could not be encoded. func (v *PollerInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.LastAccessTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastAccessTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RatePerSecond != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(*(v.RatePerSecond)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a PollerInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a PollerInfo struct could not be generated from the wire // representation. func (v *PollerInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastAccessTime = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TDouble: var x float64 x, err = sr.ReadDouble() v.RatePerSecond = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a PollerInfo // struct. func (v *PollerInfo) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.LastAccessTime != nil { fields[i] = fmt.Sprintf("LastAccessTime: %v", *(v.LastAccessTime)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.RatePerSecond != nil { fields[i] = fmt.Sprintf("RatePerSecond: %v", *(v.RatePerSecond)) i++ } return fmt.Sprintf("PollerInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this PollerInfo match the // provided PollerInfo. // // This function performs a deep comparison. func (v *PollerInfo) Equals(rhs *PollerInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.LastAccessTime, rhs.LastAccessTime) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_Double_EqualsPtr(v.RatePerSecond, rhs.RatePerSecond) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PollerInfo. func (v *PollerInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.LastAccessTime != nil { enc.AddInt64("lastAccessTime", *v.LastAccessTime) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.RatePerSecond != nil { enc.AddFloat64("ratePerSecond", *v.RatePerSecond) } return err } // GetLastAccessTime returns the value of LastAccessTime if it is set or its // zero value if it is unset. func (v *PollerInfo) GetLastAccessTime() (o int64) { if v != nil && v.LastAccessTime != nil { return *v.LastAccessTime } return } // IsSetLastAccessTime returns true if LastAccessTime is not nil. func (v *PollerInfo) IsSetLastAccessTime() bool { return v != nil && v.LastAccessTime != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *PollerInfo) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *PollerInfo) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetRatePerSecond returns the value of RatePerSecond if it is set or its // zero value if it is unset. func (v *PollerInfo) GetRatePerSecond() (o float64) { if v != nil && v.RatePerSecond != nil { return *v.RatePerSecond } return } // IsSetRatePerSecond returns true if RatePerSecond is not nil. func (v *PollerInfo) IsSetRatePerSecond() bool { return v != nil && v.RatePerSecond != nil } type Predicate struct { PredicateType *PredicateType `json:"predicateType,omitempty"` UniversalPredicateAttributes *UniversalPredicateAttributes `json:"universalPredicateAttributes,omitempty"` EmptyPredicateAttributes *EmptyPredicateAttributes `json:"emptyPredicateAttributes,omitempty"` DomainIDPredicateAttributes *DomainIDPredicateAttributes `json:"domainIDPredicateAttributes,omitempty"` } // ToWire translates a Predicate struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *Predicate) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.PredicateType != nil { w, err = v.PredicateType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.UniversalPredicateAttributes != nil { w, err = v.UniversalPredicateAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.EmptyPredicateAttributes != nil { w, err = v.EmptyPredicateAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.DomainIDPredicateAttributes != nil { w, err = v.DomainIDPredicateAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PredicateType_Read(w wire.Value) (PredicateType, error) { var v PredicateType err := v.FromWire(w) return v, err } func _UniversalPredicateAttributes_Read(w wire.Value) (*UniversalPredicateAttributes, error) { var v UniversalPredicateAttributes err := v.FromWire(w) return &v, err } func _EmptyPredicateAttributes_Read(w wire.Value) (*EmptyPredicateAttributes, error) { var v EmptyPredicateAttributes err := v.FromWire(w) return &v, err } func _DomainIDPredicateAttributes_Read(w wire.Value) (*DomainIDPredicateAttributes, error) { var v DomainIDPredicateAttributes err := v.FromWire(w) return &v, err } // FromWire deserializes a Predicate struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a Predicate struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v Predicate // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *Predicate) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x PredicateType x, err = _PredicateType_Read(field.Value) v.PredicateType = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.UniversalPredicateAttributes, err = _UniversalPredicateAttributes_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.EmptyPredicateAttributes, err = _EmptyPredicateAttributes_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.DomainIDPredicateAttributes, err = _DomainIDPredicateAttributes_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a Predicate struct directly into bytes, without going // through an intermediary type. // // An error is returned if a Predicate struct could not be encoded. func (v *Predicate) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.PredicateType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.PredicateType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.UniversalPredicateAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.UniversalPredicateAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EmptyPredicateAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.EmptyPredicateAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainIDPredicateAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.DomainIDPredicateAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PredicateType_Decode(sr stream.Reader) (PredicateType, error) { var v PredicateType err := v.Decode(sr) return v, err } func _UniversalPredicateAttributes_Decode(sr stream.Reader) (*UniversalPredicateAttributes, error) { var v UniversalPredicateAttributes err := v.Decode(sr) return &v, err } func _EmptyPredicateAttributes_Decode(sr stream.Reader) (*EmptyPredicateAttributes, error) { var v EmptyPredicateAttributes err := v.Decode(sr) return &v, err } func _DomainIDPredicateAttributes_Decode(sr stream.Reader) (*DomainIDPredicateAttributes, error) { var v DomainIDPredicateAttributes err := v.Decode(sr) return &v, err } // Decode deserializes a Predicate struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a Predicate struct could not be generated from the wire // representation. func (v *Predicate) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x PredicateType x, err = _PredicateType_Decode(sr) v.PredicateType = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.UniversalPredicateAttributes, err = _UniversalPredicateAttributes_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.EmptyPredicateAttributes, err = _EmptyPredicateAttributes_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.DomainIDPredicateAttributes, err = _DomainIDPredicateAttributes_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a Predicate // struct. func (v *Predicate) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.PredicateType != nil { fields[i] = fmt.Sprintf("PredicateType: %v", *(v.PredicateType)) i++ } if v.UniversalPredicateAttributes != nil { fields[i] = fmt.Sprintf("UniversalPredicateAttributes: %v", v.UniversalPredicateAttributes) i++ } if v.EmptyPredicateAttributes != nil { fields[i] = fmt.Sprintf("EmptyPredicateAttributes: %v", v.EmptyPredicateAttributes) i++ } if v.DomainIDPredicateAttributes != nil { fields[i] = fmt.Sprintf("DomainIDPredicateAttributes: %v", v.DomainIDPredicateAttributes) i++ } return fmt.Sprintf("Predicate{%v}", strings.Join(fields[:i], ", ")) } func _PredicateType_EqualsPtr(lhs, rhs *PredicateType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this Predicate match the // provided Predicate. // // This function performs a deep comparison. func (v *Predicate) Equals(rhs *Predicate) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_PredicateType_EqualsPtr(v.PredicateType, rhs.PredicateType) { return false } if !((v.UniversalPredicateAttributes == nil && rhs.UniversalPredicateAttributes == nil) || (v.UniversalPredicateAttributes != nil && rhs.UniversalPredicateAttributes != nil && v.UniversalPredicateAttributes.Equals(rhs.UniversalPredicateAttributes))) { return false } if !((v.EmptyPredicateAttributes == nil && rhs.EmptyPredicateAttributes == nil) || (v.EmptyPredicateAttributes != nil && rhs.EmptyPredicateAttributes != nil && v.EmptyPredicateAttributes.Equals(rhs.EmptyPredicateAttributes))) { return false } if !((v.DomainIDPredicateAttributes == nil && rhs.DomainIDPredicateAttributes == nil) || (v.DomainIDPredicateAttributes != nil && rhs.DomainIDPredicateAttributes != nil && v.DomainIDPredicateAttributes.Equals(rhs.DomainIDPredicateAttributes))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of Predicate. func (v *Predicate) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.PredicateType != nil { err = multierr.Append(err, enc.AddObject("predicateType", *v.PredicateType)) } if v.UniversalPredicateAttributes != nil { err = multierr.Append(err, enc.AddObject("universalPredicateAttributes", v.UniversalPredicateAttributes)) } if v.EmptyPredicateAttributes != nil { err = multierr.Append(err, enc.AddObject("emptyPredicateAttributes", v.EmptyPredicateAttributes)) } if v.DomainIDPredicateAttributes != nil { err = multierr.Append(err, enc.AddObject("domainIDPredicateAttributes", v.DomainIDPredicateAttributes)) } return err } // GetPredicateType returns the value of PredicateType if it is set or its // zero value if it is unset. func (v *Predicate) GetPredicateType() (o PredicateType) { if v != nil && v.PredicateType != nil { return *v.PredicateType } return } // IsSetPredicateType returns true if PredicateType is not nil. func (v *Predicate) IsSetPredicateType() bool { return v != nil && v.PredicateType != nil } // GetUniversalPredicateAttributes returns the value of UniversalPredicateAttributes if it is set or its // zero value if it is unset. func (v *Predicate) GetUniversalPredicateAttributes() (o *UniversalPredicateAttributes) { if v != nil && v.UniversalPredicateAttributes != nil { return v.UniversalPredicateAttributes } return } // IsSetUniversalPredicateAttributes returns true if UniversalPredicateAttributes is not nil. func (v *Predicate) IsSetUniversalPredicateAttributes() bool { return v != nil && v.UniversalPredicateAttributes != nil } // GetEmptyPredicateAttributes returns the value of EmptyPredicateAttributes if it is set or its // zero value if it is unset. func (v *Predicate) GetEmptyPredicateAttributes() (o *EmptyPredicateAttributes) { if v != nil && v.EmptyPredicateAttributes != nil { return v.EmptyPredicateAttributes } return } // IsSetEmptyPredicateAttributes returns true if EmptyPredicateAttributes is not nil. func (v *Predicate) IsSetEmptyPredicateAttributes() bool { return v != nil && v.EmptyPredicateAttributes != nil } // GetDomainIDPredicateAttributes returns the value of DomainIDPredicateAttributes if it is set or its // zero value if it is unset. func (v *Predicate) GetDomainIDPredicateAttributes() (o *DomainIDPredicateAttributes) { if v != nil && v.DomainIDPredicateAttributes != nil { return v.DomainIDPredicateAttributes } return } // IsSetDomainIDPredicateAttributes returns true if DomainIDPredicateAttributes is not nil. func (v *Predicate) IsSetDomainIDPredicateAttributes() bool { return v != nil && v.DomainIDPredicateAttributes != nil } type PredicateType int32 const ( PredicateTypeUniversal PredicateType = 0 PredicateTypeEmpty PredicateType = 1 PredicateTypeDomainID PredicateType = 2 ) // PredicateType_Values returns all recognized values of PredicateType. func PredicateType_Values() []PredicateType { return []PredicateType{ PredicateTypeUniversal, PredicateTypeEmpty, PredicateTypeDomainID, } } // UnmarshalText tries to decode PredicateType from a byte slice // containing its name. // // var v PredicateType // err := v.UnmarshalText([]byte("Universal")) func (v *PredicateType) UnmarshalText(value []byte) error { switch s := string(value); s { case "Universal": *v = PredicateTypeUniversal return nil case "Empty": *v = PredicateTypeEmpty return nil case "DomainID": *v = PredicateTypeDomainID return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "PredicateType", err) } *v = PredicateType(val) return nil } } // MarshalText encodes PredicateType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v PredicateType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("Universal"), nil case 1: return []byte("Empty"), nil case 2: return []byte("DomainID"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of PredicateType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v PredicateType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "Universal") case 1: enc.AddString("name", "Empty") case 2: enc.AddString("name", "DomainID") } return nil } // Ptr returns a pointer to this enum value. func (v PredicateType) Ptr() *PredicateType { return &v } // Encode encodes PredicateType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v PredicateType // return v.Encode(sWriter) func (v PredicateType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates PredicateType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v PredicateType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes PredicateType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return PredicateType(0), err // } // // var v PredicateType // if err := v.FromWire(x); err != nil { // return PredicateType(0), err // } // return v, nil func (v *PredicateType) FromWire(w wire.Value) error { *v = (PredicateType)(w.GetI32()) return nil } // Decode reads off the encoded PredicateType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v PredicateType // if err := v.Decode(sReader); err != nil { // return PredicateType(0), err // } // return v, nil func (v *PredicateType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (PredicateType)(i) return nil } // String returns a readable string representation of PredicateType. func (v PredicateType) String() string { w := int32(v) switch w { case 0: return "Universal" case 1: return "Empty" case 2: return "DomainID" } return fmt.Sprintf("PredicateType(%d)", w) } // Equals returns true if this PredicateType value matches the provided // value. func (v PredicateType) Equals(rhs PredicateType) bool { return v == rhs } // MarshalJSON serializes PredicateType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v PredicateType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"Universal\""), nil case 1: return ([]byte)("\"Empty\""), nil case 2: return ([]byte)("\"DomainID\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode PredicateType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *PredicateType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "PredicateType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "PredicateType") } *v = (PredicateType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "PredicateType") } } type QueryConsistencyLevel int32 const ( QueryConsistencyLevelEventual QueryConsistencyLevel = 0 QueryConsistencyLevelStrong QueryConsistencyLevel = 1 ) // QueryConsistencyLevel_Values returns all recognized values of QueryConsistencyLevel. func QueryConsistencyLevel_Values() []QueryConsistencyLevel { return []QueryConsistencyLevel{ QueryConsistencyLevelEventual, QueryConsistencyLevelStrong, } } // UnmarshalText tries to decode QueryConsistencyLevel from a byte slice // containing its name. // // var v QueryConsistencyLevel // err := v.UnmarshalText([]byte("EVENTUAL")) func (v *QueryConsistencyLevel) UnmarshalText(value []byte) error { switch s := string(value); s { case "EVENTUAL": *v = QueryConsistencyLevelEventual return nil case "STRONG": *v = QueryConsistencyLevelStrong return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "QueryConsistencyLevel", err) } *v = QueryConsistencyLevel(val) return nil } } // MarshalText encodes QueryConsistencyLevel to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v QueryConsistencyLevel) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("EVENTUAL"), nil case 1: return []byte("STRONG"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryConsistencyLevel. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v QueryConsistencyLevel) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "EVENTUAL") case 1: enc.AddString("name", "STRONG") } return nil } // Ptr returns a pointer to this enum value. func (v QueryConsistencyLevel) Ptr() *QueryConsistencyLevel { return &v } // Encode encodes QueryConsistencyLevel directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v QueryConsistencyLevel // return v.Encode(sWriter) func (v QueryConsistencyLevel) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates QueryConsistencyLevel into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v QueryConsistencyLevel) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes QueryConsistencyLevel from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return QueryConsistencyLevel(0), err // } // // var v QueryConsistencyLevel // if err := v.FromWire(x); err != nil { // return QueryConsistencyLevel(0), err // } // return v, nil func (v *QueryConsistencyLevel) FromWire(w wire.Value) error { *v = (QueryConsistencyLevel)(w.GetI32()) return nil } // Decode reads off the encoded QueryConsistencyLevel directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v QueryConsistencyLevel // if err := v.Decode(sReader); err != nil { // return QueryConsistencyLevel(0), err // } // return v, nil func (v *QueryConsistencyLevel) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (QueryConsistencyLevel)(i) return nil } // String returns a readable string representation of QueryConsistencyLevel. func (v QueryConsistencyLevel) String() string { w := int32(v) switch w { case 0: return "EVENTUAL" case 1: return "STRONG" } return fmt.Sprintf("QueryConsistencyLevel(%d)", w) } // Equals returns true if this QueryConsistencyLevel value matches the provided // value. func (v QueryConsistencyLevel) Equals(rhs QueryConsistencyLevel) bool { return v == rhs } // MarshalJSON serializes QueryConsistencyLevel into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v QueryConsistencyLevel) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"EVENTUAL\""), nil case 1: return ([]byte)("\"STRONG\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode QueryConsistencyLevel from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *QueryConsistencyLevel) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "QueryConsistencyLevel") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "QueryConsistencyLevel") } *v = (QueryConsistencyLevel)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "QueryConsistencyLevel") } } type QueryFailedError struct { Message string `json:"message,required"` } // ToWire translates a QueryFailedError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *QueryFailedError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a QueryFailedError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a QueryFailedError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v QueryFailedError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *QueryFailedError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of QueryFailedError is required") } return nil } // Encode serializes a QueryFailedError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a QueryFailedError struct could not be encoded. func (v *QueryFailedError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a QueryFailedError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a QueryFailedError struct could not be generated from the wire // representation. func (v *QueryFailedError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of QueryFailedError is required") } return nil } // String returns a readable string representation of a QueryFailedError // struct. func (v *QueryFailedError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("QueryFailedError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*QueryFailedError) ErrorName() string { return "QueryFailedError" } // Equals returns true if all the fields of this QueryFailedError match the // provided QueryFailedError. // // This function performs a deep comparison. func (v *QueryFailedError) Equals(rhs *QueryFailedError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryFailedError. func (v *QueryFailedError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *QueryFailedError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *QueryFailedError) Error() string { return v.String() } type QueryRejectCondition int32 const ( QueryRejectConditionNotOpen QueryRejectCondition = 0 QueryRejectConditionNotCompletedCleanly QueryRejectCondition = 1 ) // QueryRejectCondition_Values returns all recognized values of QueryRejectCondition. func QueryRejectCondition_Values() []QueryRejectCondition { return []QueryRejectCondition{ QueryRejectConditionNotOpen, QueryRejectConditionNotCompletedCleanly, } } // UnmarshalText tries to decode QueryRejectCondition from a byte slice // containing its name. // // var v QueryRejectCondition // err := v.UnmarshalText([]byte("NOT_OPEN")) func (v *QueryRejectCondition) UnmarshalText(value []byte) error { switch s := string(value); s { case "NOT_OPEN": *v = QueryRejectConditionNotOpen return nil case "NOT_COMPLETED_CLEANLY": *v = QueryRejectConditionNotCompletedCleanly return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "QueryRejectCondition", err) } *v = QueryRejectCondition(val) return nil } } // MarshalText encodes QueryRejectCondition to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v QueryRejectCondition) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("NOT_OPEN"), nil case 1: return []byte("NOT_COMPLETED_CLEANLY"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryRejectCondition. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v QueryRejectCondition) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "NOT_OPEN") case 1: enc.AddString("name", "NOT_COMPLETED_CLEANLY") } return nil } // Ptr returns a pointer to this enum value. func (v QueryRejectCondition) Ptr() *QueryRejectCondition { return &v } // Encode encodes QueryRejectCondition directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v QueryRejectCondition // return v.Encode(sWriter) func (v QueryRejectCondition) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates QueryRejectCondition into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v QueryRejectCondition) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes QueryRejectCondition from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return QueryRejectCondition(0), err // } // // var v QueryRejectCondition // if err := v.FromWire(x); err != nil { // return QueryRejectCondition(0), err // } // return v, nil func (v *QueryRejectCondition) FromWire(w wire.Value) error { *v = (QueryRejectCondition)(w.GetI32()) return nil } // Decode reads off the encoded QueryRejectCondition directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v QueryRejectCondition // if err := v.Decode(sReader); err != nil { // return QueryRejectCondition(0), err // } // return v, nil func (v *QueryRejectCondition) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (QueryRejectCondition)(i) return nil } // String returns a readable string representation of QueryRejectCondition. func (v QueryRejectCondition) String() string { w := int32(v) switch w { case 0: return "NOT_OPEN" case 1: return "NOT_COMPLETED_CLEANLY" } return fmt.Sprintf("QueryRejectCondition(%d)", w) } // Equals returns true if this QueryRejectCondition value matches the provided // value. func (v QueryRejectCondition) Equals(rhs QueryRejectCondition) bool { return v == rhs } // MarshalJSON serializes QueryRejectCondition into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v QueryRejectCondition) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"NOT_OPEN\""), nil case 1: return ([]byte)("\"NOT_COMPLETED_CLEANLY\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode QueryRejectCondition from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *QueryRejectCondition) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "QueryRejectCondition") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "QueryRejectCondition") } *v = (QueryRejectCondition)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "QueryRejectCondition") } } type QueryRejected struct { CloseStatus *WorkflowExecutionCloseStatus `json:"closeStatus,omitempty"` } // ToWire translates a QueryRejected struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *QueryRejected) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CloseStatus != nil { w, err = v.CloseStatus.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a QueryRejected struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a QueryRejected struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v QueryRejected // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *QueryRejected) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x WorkflowExecutionCloseStatus x, err = _WorkflowExecutionCloseStatus_Read(field.Value) v.CloseStatus = &x if err != nil { return err } } } } return nil } // Encode serializes a QueryRejected struct directly into bytes, without going // through an intermediary type. // // An error is returned if a QueryRejected struct could not be encoded. func (v *QueryRejected) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CloseStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.CloseStatus.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a QueryRejected struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a QueryRejected struct could not be generated from the wire // representation. func (v *QueryRejected) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x WorkflowExecutionCloseStatus x, err = _WorkflowExecutionCloseStatus_Decode(sr) v.CloseStatus = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a QueryRejected // struct. func (v *QueryRejected) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CloseStatus != nil { fields[i] = fmt.Sprintf("CloseStatus: %v", *(v.CloseStatus)) i++ } return fmt.Sprintf("QueryRejected{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this QueryRejected match the // provided QueryRejected. // // This function performs a deep comparison. func (v *QueryRejected) Equals(rhs *QueryRejected) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_WorkflowExecutionCloseStatus_EqualsPtr(v.CloseStatus, rhs.CloseStatus) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryRejected. func (v *QueryRejected) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CloseStatus != nil { err = multierr.Append(err, enc.AddObject("closeStatus", *v.CloseStatus)) } return err } // GetCloseStatus returns the value of CloseStatus if it is set or its // zero value if it is unset. func (v *QueryRejected) GetCloseStatus() (o WorkflowExecutionCloseStatus) { if v != nil && v.CloseStatus != nil { return *v.CloseStatus } return } // IsSetCloseStatus returns true if CloseStatus is not nil. func (v *QueryRejected) IsSetCloseStatus() bool { return v != nil && v.CloseStatus != nil } type QueryResultType int32 const ( QueryResultTypeAnswered QueryResultType = 0 QueryResultTypeFailed QueryResultType = 1 ) // QueryResultType_Values returns all recognized values of QueryResultType. func QueryResultType_Values() []QueryResultType { return []QueryResultType{ QueryResultTypeAnswered, QueryResultTypeFailed, } } // UnmarshalText tries to decode QueryResultType from a byte slice // containing its name. // // var v QueryResultType // err := v.UnmarshalText([]byte("ANSWERED")) func (v *QueryResultType) UnmarshalText(value []byte) error { switch s := string(value); s { case "ANSWERED": *v = QueryResultTypeAnswered return nil case "FAILED": *v = QueryResultTypeFailed return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "QueryResultType", err) } *v = QueryResultType(val) return nil } } // MarshalText encodes QueryResultType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v QueryResultType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("ANSWERED"), nil case 1: return []byte("FAILED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryResultType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v QueryResultType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "ANSWERED") case 1: enc.AddString("name", "FAILED") } return nil } // Ptr returns a pointer to this enum value. func (v QueryResultType) Ptr() *QueryResultType { return &v } // Encode encodes QueryResultType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v QueryResultType // return v.Encode(sWriter) func (v QueryResultType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates QueryResultType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v QueryResultType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes QueryResultType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return QueryResultType(0), err // } // // var v QueryResultType // if err := v.FromWire(x); err != nil { // return QueryResultType(0), err // } // return v, nil func (v *QueryResultType) FromWire(w wire.Value) error { *v = (QueryResultType)(w.GetI32()) return nil } // Decode reads off the encoded QueryResultType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v QueryResultType // if err := v.Decode(sReader); err != nil { // return QueryResultType(0), err // } // return v, nil func (v *QueryResultType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (QueryResultType)(i) return nil } // String returns a readable string representation of QueryResultType. func (v QueryResultType) String() string { w := int32(v) switch w { case 0: return "ANSWERED" case 1: return "FAILED" } return fmt.Sprintf("QueryResultType(%d)", w) } // Equals returns true if this QueryResultType value matches the provided // value. func (v QueryResultType) Equals(rhs QueryResultType) bool { return v == rhs } // MarshalJSON serializes QueryResultType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v QueryResultType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"ANSWERED\""), nil case 1: return ([]byte)("\"FAILED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode QueryResultType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *QueryResultType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "QueryResultType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "QueryResultType") } *v = (QueryResultType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "QueryResultType") } } type QueryTaskCompletedType int32 const ( QueryTaskCompletedTypeCompleted QueryTaskCompletedType = 0 QueryTaskCompletedTypeFailed QueryTaskCompletedType = 1 ) // QueryTaskCompletedType_Values returns all recognized values of QueryTaskCompletedType. func QueryTaskCompletedType_Values() []QueryTaskCompletedType { return []QueryTaskCompletedType{ QueryTaskCompletedTypeCompleted, QueryTaskCompletedTypeFailed, } } // UnmarshalText tries to decode QueryTaskCompletedType from a byte slice // containing its name. // // var v QueryTaskCompletedType // err := v.UnmarshalText([]byte("COMPLETED")) func (v *QueryTaskCompletedType) UnmarshalText(value []byte) error { switch s := string(value); s { case "COMPLETED": *v = QueryTaskCompletedTypeCompleted return nil case "FAILED": *v = QueryTaskCompletedTypeFailed return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "QueryTaskCompletedType", err) } *v = QueryTaskCompletedType(val) return nil } } // MarshalText encodes QueryTaskCompletedType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v QueryTaskCompletedType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("COMPLETED"), nil case 1: return []byte("FAILED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryTaskCompletedType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v QueryTaskCompletedType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "COMPLETED") case 1: enc.AddString("name", "FAILED") } return nil } // Ptr returns a pointer to this enum value. func (v QueryTaskCompletedType) Ptr() *QueryTaskCompletedType { return &v } // Encode encodes QueryTaskCompletedType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v QueryTaskCompletedType // return v.Encode(sWriter) func (v QueryTaskCompletedType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates QueryTaskCompletedType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v QueryTaskCompletedType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes QueryTaskCompletedType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return QueryTaskCompletedType(0), err // } // // var v QueryTaskCompletedType // if err := v.FromWire(x); err != nil { // return QueryTaskCompletedType(0), err // } // return v, nil func (v *QueryTaskCompletedType) FromWire(w wire.Value) error { *v = (QueryTaskCompletedType)(w.GetI32()) return nil } // Decode reads off the encoded QueryTaskCompletedType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v QueryTaskCompletedType // if err := v.Decode(sReader); err != nil { // return QueryTaskCompletedType(0), err // } // return v, nil func (v *QueryTaskCompletedType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (QueryTaskCompletedType)(i) return nil } // String returns a readable string representation of QueryTaskCompletedType. func (v QueryTaskCompletedType) String() string { w := int32(v) switch w { case 0: return "COMPLETED" case 1: return "FAILED" } return fmt.Sprintf("QueryTaskCompletedType(%d)", w) } // Equals returns true if this QueryTaskCompletedType value matches the provided // value. func (v QueryTaskCompletedType) Equals(rhs QueryTaskCompletedType) bool { return v == rhs } // MarshalJSON serializes QueryTaskCompletedType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v QueryTaskCompletedType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"COMPLETED\""), nil case 1: return ([]byte)("\"FAILED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode QueryTaskCompletedType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *QueryTaskCompletedType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "QueryTaskCompletedType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "QueryTaskCompletedType") } *v = (QueryTaskCompletedType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "QueryTaskCompletedType") } } type QueryWorkflowRequest struct { Domain *string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` Query *WorkflowQuery `json:"query,omitempty"` QueryRejectCondition *QueryRejectCondition `json:"queryRejectCondition,omitempty"` QueryConsistencyLevel *QueryConsistencyLevel `json:"queryConsistencyLevel,omitempty"` } // ToWire translates a QueryWorkflowRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *QueryWorkflowRequest) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Query != nil { w, err = v.Query.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.QueryRejectCondition != nil { w, err = v.QueryRejectCondition.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.QueryConsistencyLevel != nil { w, err = v.QueryConsistencyLevel.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryRejectCondition_Read(w wire.Value) (QueryRejectCondition, error) { var v QueryRejectCondition err := v.FromWire(w) return v, err } // FromWire deserializes a QueryWorkflowRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a QueryWorkflowRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v QueryWorkflowRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *QueryWorkflowRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.Query, err = _WorkflowQuery_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x QueryRejectCondition x, err = _QueryRejectCondition_Read(field.Value) v.QueryRejectCondition = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x QueryConsistencyLevel x, err = _QueryConsistencyLevel_Read(field.Value) v.QueryConsistencyLevel = &x if err != nil { return err } } } } return nil } // Encode serializes a QueryWorkflowRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a QueryWorkflowRequest struct could not be encoded. func (v *QueryWorkflowRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Query != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.Query.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryRejectCondition != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := v.QueryRejectCondition.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryConsistencyLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := v.QueryConsistencyLevel.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryRejectCondition_Decode(sr stream.Reader) (QueryRejectCondition, error) { var v QueryRejectCondition err := v.Decode(sr) return v, err } // Decode deserializes a QueryWorkflowRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a QueryWorkflowRequest struct could not be generated from the wire // representation. func (v *QueryWorkflowRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.Query, err = _WorkflowQuery_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x QueryRejectCondition x, err = _QueryRejectCondition_Decode(sr) v.QueryRejectCondition = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x QueryConsistencyLevel x, err = _QueryConsistencyLevel_Decode(sr) v.QueryConsistencyLevel = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a QueryWorkflowRequest // struct. func (v *QueryWorkflowRequest) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.Query != nil { fields[i] = fmt.Sprintf("Query: %v", v.Query) i++ } if v.QueryRejectCondition != nil { fields[i] = fmt.Sprintf("QueryRejectCondition: %v", *(v.QueryRejectCondition)) i++ } if v.QueryConsistencyLevel != nil { fields[i] = fmt.Sprintf("QueryConsistencyLevel: %v", *(v.QueryConsistencyLevel)) i++ } return fmt.Sprintf("QueryWorkflowRequest{%v}", strings.Join(fields[:i], ", ")) } func _QueryRejectCondition_EqualsPtr(lhs, rhs *QueryRejectCondition) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this QueryWorkflowRequest match the // provided QueryWorkflowRequest. // // This function performs a deep comparison. func (v *QueryWorkflowRequest) Equals(rhs *QueryWorkflowRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !((v.Query == nil && rhs.Query == nil) || (v.Query != nil && rhs.Query != nil && v.Query.Equals(rhs.Query))) { return false } if !_QueryRejectCondition_EqualsPtr(v.QueryRejectCondition, rhs.QueryRejectCondition) { return false } if !_QueryConsistencyLevel_EqualsPtr(v.QueryConsistencyLevel, rhs.QueryConsistencyLevel) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryWorkflowRequest. func (v *QueryWorkflowRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.Query != nil { err = multierr.Append(err, enc.AddObject("query", v.Query)) } if v.QueryRejectCondition != nil { err = multierr.Append(err, enc.AddObject("queryRejectCondition", *v.QueryRejectCondition)) } if v.QueryConsistencyLevel != nil { err = multierr.Append(err, enc.AddObject("queryConsistencyLevel", *v.QueryConsistencyLevel)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *QueryWorkflowRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *QueryWorkflowRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetQuery returns the value of Query if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetQuery() (o *WorkflowQuery) { if v != nil && v.Query != nil { return v.Query } return } // IsSetQuery returns true if Query is not nil. func (v *QueryWorkflowRequest) IsSetQuery() bool { return v != nil && v.Query != nil } // GetQueryRejectCondition returns the value of QueryRejectCondition if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetQueryRejectCondition() (o QueryRejectCondition) { if v != nil && v.QueryRejectCondition != nil { return *v.QueryRejectCondition } return } // IsSetQueryRejectCondition returns true if QueryRejectCondition is not nil. func (v *QueryWorkflowRequest) IsSetQueryRejectCondition() bool { return v != nil && v.QueryRejectCondition != nil } // GetQueryConsistencyLevel returns the value of QueryConsistencyLevel if it is set or its // zero value if it is unset. func (v *QueryWorkflowRequest) GetQueryConsistencyLevel() (o QueryConsistencyLevel) { if v != nil && v.QueryConsistencyLevel != nil { return *v.QueryConsistencyLevel } return } // IsSetQueryConsistencyLevel returns true if QueryConsistencyLevel is not nil. func (v *QueryWorkflowRequest) IsSetQueryConsistencyLevel() bool { return v != nil && v.QueryConsistencyLevel != nil } type QueryWorkflowResponse struct { QueryResult []byte `json:"queryResult,omitempty"` QueryRejected *QueryRejected `json:"queryRejected,omitempty"` } // ToWire translates a QueryWorkflowResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *QueryWorkflowResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.QueryResult != nil { w, err = wire.NewValueBinary(v.QueryResult), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.QueryRejected != nil { w, err = v.QueryRejected.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryRejected_Read(w wire.Value) (*QueryRejected, error) { var v QueryRejected err := v.FromWire(w) return &v, err } // FromWire deserializes a QueryWorkflowResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a QueryWorkflowResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v QueryWorkflowResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *QueryWorkflowResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.QueryResult, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.QueryRejected, err = _QueryRejected_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a QueryWorkflowResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a QueryWorkflowResponse struct could not be encoded. func (v *QueryWorkflowResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.QueryResult != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.QueryResult); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryRejected != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.QueryRejected.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryRejected_Decode(sr stream.Reader) (*QueryRejected, error) { var v QueryRejected err := v.Decode(sr) return &v, err } // Decode deserializes a QueryWorkflowResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a QueryWorkflowResponse struct could not be generated from the wire // representation. func (v *QueryWorkflowResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.QueryResult, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.QueryRejected, err = _QueryRejected_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a QueryWorkflowResponse // struct. func (v *QueryWorkflowResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.QueryResult != nil { fields[i] = fmt.Sprintf("QueryResult: %v", v.QueryResult) i++ } if v.QueryRejected != nil { fields[i] = fmt.Sprintf("QueryRejected: %v", v.QueryRejected) i++ } return fmt.Sprintf("QueryWorkflowResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this QueryWorkflowResponse match the // provided QueryWorkflowResponse. // // This function performs a deep comparison. func (v *QueryWorkflowResponse) Equals(rhs *QueryWorkflowResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.QueryResult == nil && rhs.QueryResult == nil) || (v.QueryResult != nil && rhs.QueryResult != nil && bytes.Equal(v.QueryResult, rhs.QueryResult))) { return false } if !((v.QueryRejected == nil && rhs.QueryRejected == nil) || (v.QueryRejected != nil && rhs.QueryRejected != nil && v.QueryRejected.Equals(rhs.QueryRejected))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueryWorkflowResponse. func (v *QueryWorkflowResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.QueryResult != nil { enc.AddString("queryResult", base64.StdEncoding.EncodeToString(v.QueryResult)) } if v.QueryRejected != nil { err = multierr.Append(err, enc.AddObject("queryRejected", v.QueryRejected)) } return err } // GetQueryResult returns the value of QueryResult if it is set or its // zero value if it is unset. func (v *QueryWorkflowResponse) GetQueryResult() (o []byte) { if v != nil && v.QueryResult != nil { return v.QueryResult } return } // IsSetQueryResult returns true if QueryResult is not nil. func (v *QueryWorkflowResponse) IsSetQueryResult() bool { return v != nil && v.QueryResult != nil } // GetQueryRejected returns the value of QueryRejected if it is set or its // zero value if it is unset. func (v *QueryWorkflowResponse) GetQueryRejected() (o *QueryRejected) { if v != nil && v.QueryRejected != nil { return v.QueryRejected } return } // IsSetQueryRejected returns true if QueryRejected is not nil. func (v *QueryWorkflowResponse) IsSetQueryRejected() bool { return v != nil && v.QueryRejected != nil } type QueueState struct { VirtualQueueStates map[int64]*VirtualQueueState `json:"virtualQueueStates,omitempty"` ExclusiveMaxReadLevel *TaskKey `json:"exclusiveMaxReadLevel,omitempty"` } type _Map_I64_VirtualQueueState_MapItemList map[int64]*VirtualQueueState func (m _Map_I64_VirtualQueueState_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[int64]*VirtualQueueState', key [%v]: value is nil", k) } kw, err := wire.NewValueI64(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_I64_VirtualQueueState_MapItemList) Size() int { return len(m) } func (_Map_I64_VirtualQueueState_MapItemList) KeyType() wire.Type { return wire.TI64 } func (_Map_I64_VirtualQueueState_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_I64_VirtualQueueState_MapItemList) Close() {} // ToWire translates a QueueState struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *QueueState) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.VirtualQueueStates != nil { w, err = wire.NewValueMap(_Map_I64_VirtualQueueState_MapItemList(v.VirtualQueueStates)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ExclusiveMaxReadLevel != nil { w, err = v.ExclusiveMaxReadLevel.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _VirtualQueueState_Read(w wire.Value) (*VirtualQueueState, error) { var v VirtualQueueState err := v.FromWire(w) return &v, err } func _Map_I64_VirtualQueueState_Read(m wire.MapItemList) (map[int64]*VirtualQueueState, error) { if m.KeyType() != wire.TI64 { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[int64]*VirtualQueueState, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetI64(), error(nil) if err != nil { return err } v, err := _VirtualQueueState_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } func _TaskKey_Read(w wire.Value) (*TaskKey, error) { var v TaskKey err := v.FromWire(w) return &v, err } // FromWire deserializes a QueueState struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a QueueState struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v QueueState // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *QueueState) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.VirtualQueueStates, err = _Map_I64_VirtualQueueState_Read(field.Value.GetMap()) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.ExclusiveMaxReadLevel, err = _TaskKey_Read(field.Value) if err != nil { return err } } } } return nil } func _Map_I64_VirtualQueueState_Encode(val map[int64]*VirtualQueueState, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TI64, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[int64]*VirtualQueueState', key [%v]: value is nil", k) } if err := sw.WriteInt64(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a QueueState struct directly into bytes, without going // through an intermediary type. // // An error is returned if a QueueState struct could not be encoded. func (v *QueueState) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.VirtualQueueStates != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_I64_VirtualQueueState_Encode(v.VirtualQueueStates, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExclusiveMaxReadLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.ExclusiveMaxReadLevel.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _VirtualQueueState_Decode(sr stream.Reader) (*VirtualQueueState, error) { var v VirtualQueueState err := v.Decode(sr) return &v, err } func _Map_I64_VirtualQueueState_Decode(sr stream.Reader) (map[int64]*VirtualQueueState, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TI64 || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[int64]*VirtualQueueState, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadInt64() if err != nil { return nil, err } v, err := _VirtualQueueState_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } func _TaskKey_Decode(sr stream.Reader) (*TaskKey, error) { var v TaskKey err := v.Decode(sr) return &v, err } // Decode deserializes a QueueState struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a QueueState struct could not be generated from the wire // representation. func (v *QueueState) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.VirtualQueueStates, err = _Map_I64_VirtualQueueState_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.ExclusiveMaxReadLevel, err = _TaskKey_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a QueueState // struct. func (v *QueueState) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.VirtualQueueStates != nil { fields[i] = fmt.Sprintf("VirtualQueueStates: %v", v.VirtualQueueStates) i++ } if v.ExclusiveMaxReadLevel != nil { fields[i] = fmt.Sprintf("ExclusiveMaxReadLevel: %v", v.ExclusiveMaxReadLevel) i++ } return fmt.Sprintf("QueueState{%v}", strings.Join(fields[:i], ", ")) } func _Map_I64_VirtualQueueState_Equals(lhs, rhs map[int64]*VirtualQueueState) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this QueueState match the // provided QueueState. // // This function performs a deep comparison. func (v *QueueState) Equals(rhs *QueueState) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.VirtualQueueStates == nil && rhs.VirtualQueueStates == nil) || (v.VirtualQueueStates != nil && rhs.VirtualQueueStates != nil && _Map_I64_VirtualQueueState_Equals(v.VirtualQueueStates, rhs.VirtualQueueStates))) { return false } if !((v.ExclusiveMaxReadLevel == nil && rhs.ExclusiveMaxReadLevel == nil) || (v.ExclusiveMaxReadLevel != nil && rhs.ExclusiveMaxReadLevel != nil && v.ExclusiveMaxReadLevel.Equals(rhs.ExclusiveMaxReadLevel))) { return false } return true } type _Map_I64_VirtualQueueState_Item_Zapper struct { Key int64 Value *VirtualQueueState } // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I64_VirtualQueueState_Item_Zapper. func (v _Map_I64_VirtualQueueState_Item_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { enc.AddInt64("key", v.Key) err = multierr.Append(err, enc.AddObject("value", v.Value)) return err } type _Map_I64_VirtualQueueState_Zapper map[int64]*VirtualQueueState // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I64_VirtualQueueState_Zapper. func (m _Map_I64_VirtualQueueState_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AppendObject(_Map_I64_VirtualQueueState_Item_Zapper{Key: k, Value: v})) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of QueueState. func (v *QueueState) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.VirtualQueueStates != nil { err = multierr.Append(err, enc.AddArray("virtualQueueStates", (_Map_I64_VirtualQueueState_Zapper)(v.VirtualQueueStates))) } if v.ExclusiveMaxReadLevel != nil { err = multierr.Append(err, enc.AddObject("exclusiveMaxReadLevel", v.ExclusiveMaxReadLevel)) } return err } // GetVirtualQueueStates returns the value of VirtualQueueStates if it is set or its // zero value if it is unset. func (v *QueueState) GetVirtualQueueStates() (o map[int64]*VirtualQueueState) { if v != nil && v.VirtualQueueStates != nil { return v.VirtualQueueStates } return } // IsSetVirtualQueueStates returns true if VirtualQueueStates is not nil. func (v *QueueState) IsSetVirtualQueueStates() bool { return v != nil && v.VirtualQueueStates != nil } // GetExclusiveMaxReadLevel returns the value of ExclusiveMaxReadLevel if it is set or its // zero value if it is unset. func (v *QueueState) GetExclusiveMaxReadLevel() (o *TaskKey) { if v != nil && v.ExclusiveMaxReadLevel != nil { return v.ExclusiveMaxReadLevel } return } // IsSetExclusiveMaxReadLevel returns true if ExclusiveMaxReadLevel is not nil. func (v *QueueState) IsSetExclusiveMaxReadLevel() bool { return v != nil && v.ExclusiveMaxReadLevel != nil } type ReapplyEventsRequest struct { DomainName *string `json:"domainName,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Events *DataBlob `json:"events,omitempty"` } // ToWire translates a ReapplyEventsRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReapplyEventsRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.DomainName != nil { w, err = wire.NewValueString(*(v.DomainName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Events != nil { w, err = v.Events.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ReapplyEventsRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReapplyEventsRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReapplyEventsRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReapplyEventsRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainName = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.Events, err = _DataBlob_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ReapplyEventsRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReapplyEventsRequest struct could not be encoded. func (v *ReapplyEventsRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Events != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.Events.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ReapplyEventsRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReapplyEventsRequest struct could not be generated from the wire // representation. func (v *ReapplyEventsRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainName = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.Events, err = _DataBlob_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReapplyEventsRequest // struct. func (v *ReapplyEventsRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.DomainName != nil { fields[i] = fmt.Sprintf("DomainName: %v", *(v.DomainName)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.Events != nil { fields[i] = fmt.Sprintf("Events: %v", v.Events) i++ } return fmt.Sprintf("ReapplyEventsRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ReapplyEventsRequest match the // provided ReapplyEventsRequest. // // This function performs a deep comparison. func (v *ReapplyEventsRequest) Equals(rhs *ReapplyEventsRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.DomainName, rhs.DomainName) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.Events == nil && rhs.Events == nil) || (v.Events != nil && rhs.Events != nil && v.Events.Equals(rhs.Events))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReapplyEventsRequest. func (v *ReapplyEventsRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainName != nil { enc.AddString("domainName", *v.DomainName) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.Events != nil { err = multierr.Append(err, enc.AddObject("events", v.Events)) } return err } // GetDomainName returns the value of DomainName if it is set or its // zero value if it is unset. func (v *ReapplyEventsRequest) GetDomainName() (o string) { if v != nil && v.DomainName != nil { return *v.DomainName } return } // IsSetDomainName returns true if DomainName is not nil. func (v *ReapplyEventsRequest) IsSetDomainName() bool { return v != nil && v.DomainName != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ReapplyEventsRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ReapplyEventsRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetEvents returns the value of Events if it is set or its // zero value if it is unset. func (v *ReapplyEventsRequest) GetEvents() (o *DataBlob) { if v != nil && v.Events != nil { return v.Events } return } // IsSetEvents returns true if Events is not nil. func (v *ReapplyEventsRequest) IsSetEvents() bool { return v != nil && v.Events != nil } type RecordActivityTaskHeartbeatByIDRequest struct { Domain *string `json:"domain,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID *string `json:"runID,omitempty"` ActivityID *string `json:"activityID,omitempty"` Details []byte `json:"details,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a RecordActivityTaskHeartbeatByIDRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RecordActivityTaskHeartbeatByIDRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueString(*(v.RunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ActivityID != nil { w, err = wire.NewValueString(*(v.ActivityID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RecordActivityTaskHeartbeatByIDRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RecordActivityTaskHeartbeatByIDRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RecordActivityTaskHeartbeatByIDRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RecordActivityTaskHeartbeatByIDRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a RecordActivityTaskHeartbeatByIDRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RecordActivityTaskHeartbeatByIDRequest struct could not be encoded. func (v *RecordActivityTaskHeartbeatByIDRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RecordActivityTaskHeartbeatByIDRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RecordActivityTaskHeartbeatByIDRequest struct could not be generated from the wire // representation. func (v *RecordActivityTaskHeartbeatByIDRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RecordActivityTaskHeartbeatByIDRequest // struct. func (v *RecordActivityTaskHeartbeatByIDRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", *(v.RunID)) i++ } if v.ActivityID != nil { fields[i] = fmt.Sprintf("ActivityID: %v", *(v.ActivityID)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("RecordActivityTaskHeartbeatByIDRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RecordActivityTaskHeartbeatByIDRequest match the // provided RecordActivityTaskHeartbeatByIDRequest. // // This function performs a deep comparison. func (v *RecordActivityTaskHeartbeatByIDRequest) Equals(rhs *RecordActivityTaskHeartbeatByIDRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !_String_EqualsPtr(v.RunID, rhs.RunID) { return false } if !_String_EqualsPtr(v.ActivityID, rhs.ActivityID) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RecordActivityTaskHeartbeatByIDRequest. func (v *RecordActivityTaskHeartbeatByIDRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", *v.RunID) } if v.ActivityID != nil { enc.AddString("activityID", *v.ActivityID) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatByIDRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *RecordActivityTaskHeartbeatByIDRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatByIDRequest) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *RecordActivityTaskHeartbeatByIDRequest) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatByIDRequest) GetRunID() (o string) { if v != nil && v.RunID != nil { return *v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *RecordActivityTaskHeartbeatByIDRequest) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetActivityID returns the value of ActivityID if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatByIDRequest) GetActivityID() (o string) { if v != nil && v.ActivityID != nil { return *v.ActivityID } return } // IsSetActivityID returns true if ActivityID is not nil. func (v *RecordActivityTaskHeartbeatByIDRequest) IsSetActivityID() bool { return v != nil && v.ActivityID != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatByIDRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *RecordActivityTaskHeartbeatByIDRequest) IsSetDetails() bool { return v != nil && v.Details != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatByIDRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RecordActivityTaskHeartbeatByIDRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } type RecordActivityTaskHeartbeatRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Details []byte `json:"details,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a RecordActivityTaskHeartbeatRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RecordActivityTaskHeartbeatRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RecordActivityTaskHeartbeatRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RecordActivityTaskHeartbeatRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RecordActivityTaskHeartbeatRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RecordActivityTaskHeartbeatRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a RecordActivityTaskHeartbeatRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RecordActivityTaskHeartbeatRequest struct could not be encoded. func (v *RecordActivityTaskHeartbeatRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RecordActivityTaskHeartbeatRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RecordActivityTaskHeartbeatRequest struct could not be generated from the wire // representation. func (v *RecordActivityTaskHeartbeatRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RecordActivityTaskHeartbeatRequest // struct. func (v *RecordActivityTaskHeartbeatRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("RecordActivityTaskHeartbeatRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RecordActivityTaskHeartbeatRequest match the // provided RecordActivityTaskHeartbeatRequest. // // This function performs a deep comparison. func (v *RecordActivityTaskHeartbeatRequest) Equals(rhs *RecordActivityTaskHeartbeatRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RecordActivityTaskHeartbeatRequest. func (v *RecordActivityTaskHeartbeatRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatRequest) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *RecordActivityTaskHeartbeatRequest) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *RecordActivityTaskHeartbeatRequest) IsSetDetails() bool { return v != nil && v.Details != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RecordActivityTaskHeartbeatRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } type RecordActivityTaskHeartbeatResponse struct { CancelRequested *bool `json:"cancelRequested,omitempty"` } // ToWire translates a RecordActivityTaskHeartbeatResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RecordActivityTaskHeartbeatResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.CancelRequested != nil { w, err = wire.NewValueBool(*(v.CancelRequested)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RecordActivityTaskHeartbeatResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RecordActivityTaskHeartbeatResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RecordActivityTaskHeartbeatResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RecordActivityTaskHeartbeatResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.CancelRequested = &x if err != nil { return err } } } } return nil } // Encode serializes a RecordActivityTaskHeartbeatResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RecordActivityTaskHeartbeatResponse struct could not be encoded. func (v *RecordActivityTaskHeartbeatResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CancelRequested != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.CancelRequested)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RecordActivityTaskHeartbeatResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RecordActivityTaskHeartbeatResponse struct could not be generated from the wire // representation. func (v *RecordActivityTaskHeartbeatResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.CancelRequested = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RecordActivityTaskHeartbeatResponse // struct. func (v *RecordActivityTaskHeartbeatResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.CancelRequested != nil { fields[i] = fmt.Sprintf("CancelRequested: %v", *(v.CancelRequested)) i++ } return fmt.Sprintf("RecordActivityTaskHeartbeatResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RecordActivityTaskHeartbeatResponse match the // provided RecordActivityTaskHeartbeatResponse. // // This function performs a deep comparison. func (v *RecordActivityTaskHeartbeatResponse) Equals(rhs *RecordActivityTaskHeartbeatResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Bool_EqualsPtr(v.CancelRequested, rhs.CancelRequested) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RecordActivityTaskHeartbeatResponse. func (v *RecordActivityTaskHeartbeatResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CancelRequested != nil { enc.AddBool("cancelRequested", *v.CancelRequested) } return err } // GetCancelRequested returns the value of CancelRequested if it is set or its // zero value if it is unset. func (v *RecordActivityTaskHeartbeatResponse) GetCancelRequested() (o bool) { if v != nil && v.CancelRequested != nil { return *v.CancelRequested } return } // IsSetCancelRequested returns true if CancelRequested is not nil. func (v *RecordActivityTaskHeartbeatResponse) IsSetCancelRequested() bool { return v != nil && v.CancelRequested != nil } type RecordMarkerDecisionAttributes struct { MarkerName *string `json:"markerName,omitempty"` Details []byte `json:"details,omitempty"` Header *Header `json:"header,omitempty"` } // ToWire translates a RecordMarkerDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RecordMarkerDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.MarkerName != nil { w, err = wire.NewValueString(*(v.MarkerName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RecordMarkerDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RecordMarkerDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RecordMarkerDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RecordMarkerDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.MarkerName = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RecordMarkerDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RecordMarkerDecisionAttributes struct could not be encoded. func (v *RecordMarkerDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.MarkerName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.MarkerName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RecordMarkerDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RecordMarkerDecisionAttributes struct could not be generated from the wire // representation. func (v *RecordMarkerDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.MarkerName = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RecordMarkerDecisionAttributes // struct. func (v *RecordMarkerDecisionAttributes) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.MarkerName != nil { fields[i] = fmt.Sprintf("MarkerName: %v", *(v.MarkerName)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } return fmt.Sprintf("RecordMarkerDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RecordMarkerDecisionAttributes match the // provided RecordMarkerDecisionAttributes. // // This function performs a deep comparison. func (v *RecordMarkerDecisionAttributes) Equals(rhs *RecordMarkerDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.MarkerName, rhs.MarkerName) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RecordMarkerDecisionAttributes. func (v *RecordMarkerDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.MarkerName != nil { enc.AddString("markerName", *v.MarkerName) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } return err } // GetMarkerName returns the value of MarkerName if it is set or its // zero value if it is unset. func (v *RecordMarkerDecisionAttributes) GetMarkerName() (o string) { if v != nil && v.MarkerName != nil { return *v.MarkerName } return } // IsSetMarkerName returns true if MarkerName is not nil. func (v *RecordMarkerDecisionAttributes) IsSetMarkerName() bool { return v != nil && v.MarkerName != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *RecordMarkerDecisionAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *RecordMarkerDecisionAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *RecordMarkerDecisionAttributes) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *RecordMarkerDecisionAttributes) IsSetHeader() bool { return v != nil && v.Header != nil } type RefreshWorkflowTasksRequest struct { Domain *string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` } // ToWire translates a RefreshWorkflowTasksRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RefreshWorkflowTasksRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RefreshWorkflowTasksRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RefreshWorkflowTasksRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RefreshWorkflowTasksRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RefreshWorkflowTasksRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RefreshWorkflowTasksRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RefreshWorkflowTasksRequest struct could not be encoded. func (v *RefreshWorkflowTasksRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RefreshWorkflowTasksRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RefreshWorkflowTasksRequest struct could not be generated from the wire // representation. func (v *RefreshWorkflowTasksRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RefreshWorkflowTasksRequest // struct. func (v *RefreshWorkflowTasksRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } return fmt.Sprintf("RefreshWorkflowTasksRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RefreshWorkflowTasksRequest match the // provided RefreshWorkflowTasksRequest. // // This function performs a deep comparison. func (v *RefreshWorkflowTasksRequest) Equals(rhs *RefreshWorkflowTasksRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RefreshWorkflowTasksRequest. func (v *RefreshWorkflowTasksRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *RefreshWorkflowTasksRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *RefreshWorkflowTasksRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *RefreshWorkflowTasksRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *RefreshWorkflowTasksRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } type RegisterDomainRequest struct { Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` OwnerEmail *string `json:"ownerEmail,omitempty"` WorkflowExecutionRetentionPeriodInDays *int32 `json:"workflowExecutionRetentionPeriodInDays,omitempty"` EmitMetric *bool `json:"emitMetric,omitempty"` Clusters []*ClusterReplicationConfiguration `json:"clusters,omitempty"` ActiveClusterName *string `json:"activeClusterName,omitempty"` ActiveClustersByRegion map[string]string `json:"activeClustersByRegion,omitempty"` ActiveClusters *ActiveClusters `json:"activeClusters,omitempty"` Data map[string]string `json:"data,omitempty"` SecurityToken *string `json:"securityToken,omitempty"` IsGlobalDomain *bool `json:"isGlobalDomain,omitempty"` HistoryArchivalStatus *ArchivalStatus `json:"historyArchivalStatus,omitempty"` HistoryArchivalURI *string `json:"historyArchivalURI,omitempty"` VisibilityArchivalStatus *ArchivalStatus `json:"visibilityArchivalStatus,omitempty"` VisibilityArchivalURI *string `json:"visibilityArchivalURI,omitempty"` } // Default_RegisterDomainRequest constructs a new RegisterDomainRequest struct, // pre-populating any fields with defined default values. func Default_RegisterDomainRequest() *RegisterDomainRequest { var v RegisterDomainRequest v.EmitMetric = ptr.Bool(true) return &v } // ToWire translates a RegisterDomainRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RegisterDomainRequest) ToWire() (wire.Value, error) { var ( fields [16]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Description != nil { w, err = wire.NewValueString(*(v.Description)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.OwnerEmail != nil { w, err = wire.NewValueString(*(v.OwnerEmail)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.WorkflowExecutionRetentionPeriodInDays != nil { w, err = wire.NewValueI32(*(v.WorkflowExecutionRetentionPeriodInDays)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } vEmitMetric := v.EmitMetric if vEmitMetric == nil { vEmitMetric = ptr.Bool(true) } { w, err = wire.NewValueBool(*(vEmitMetric)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Clusters != nil { w, err = wire.NewValueList(_List_ClusterReplicationConfiguration_ValueList(v.Clusters)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.ActiveClusterName != nil { w, err = wire.NewValueString(*(v.ActiveClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.ActiveClustersByRegion != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.ActiveClustersByRegion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 75, Value: w} i++ } if v.ActiveClusters != nil { w, err = v.ActiveClusters.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 76, Value: w} i++ } if v.Data != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.Data)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.SecurityToken != nil { w, err = wire.NewValueString(*(v.SecurityToken)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.IsGlobalDomain != nil { w, err = wire.NewValueBool(*(v.IsGlobalDomain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.HistoryArchivalStatus != nil { w, err = v.HistoryArchivalStatus.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.HistoryArchivalURI != nil { w, err = wire.NewValueString(*(v.HistoryArchivalURI)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.VisibilityArchivalStatus != nil { w, err = v.VisibilityArchivalStatus.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.VisibilityArchivalURI != nil { w, err = wire.NewValueString(*(v.VisibilityArchivalURI)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RegisterDomainRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RegisterDomainRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RegisterDomainRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RegisterDomainRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Description = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.OwnerEmail = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.WorkflowExecutionRetentionPeriodInDays = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.EmitMetric = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TList { v.Clusters, err = _List_ClusterReplicationConfiguration_Read(field.Value.GetList()) if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActiveClusterName = &x if err != nil { return err } } case 75: if field.Value.Type() == wire.TMap { v.ActiveClustersByRegion, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } case 76: if field.Value.Type() == wire.TStruct { v.ActiveClusters, err = _ActiveClusters_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TMap { v.Data, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SecurityToken = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.IsGlobalDomain = &x if err != nil { return err } } case 130: if field.Value.Type() == wire.TI32 { var x ArchivalStatus x, err = _ArchivalStatus_Read(field.Value) v.HistoryArchivalStatus = &x if err != nil { return err } } case 140: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.HistoryArchivalURI = &x if err != nil { return err } } case 150: if field.Value.Type() == wire.TI32 { var x ArchivalStatus x, err = _ArchivalStatus_Read(field.Value) v.VisibilityArchivalStatus = &x if err != nil { return err } } case 160: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.VisibilityArchivalURI = &x if err != nil { return err } } } } if v.EmitMetric == nil { v.EmitMetric = ptr.Bool(true) } return nil } // Encode serializes a RegisterDomainRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RegisterDomainRequest struct could not be encoded. func (v *RegisterDomainRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Description != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Description)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.OwnerEmail != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.OwnerEmail)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecutionRetentionPeriodInDays != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.WorkflowExecutionRetentionPeriodInDays)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } vEmitMetric := v.EmitMetric if vEmitMetric == nil { vEmitMetric = ptr.Bool(true) } { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(vEmitMetric)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Clusters != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TList}); err != nil { return err } if err := _List_ClusterReplicationConfiguration_Encode(v.Clusters, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActiveClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClustersByRegion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 75, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.ActiveClustersByRegion, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusters != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 76, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusters.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Data != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.Data, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SecurityToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SecurityToken)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsGlobalDomain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.IsGlobalDomain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryArchivalStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TI32}); err != nil { return err } if err := v.HistoryArchivalStatus.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryArchivalURI != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.HistoryArchivalURI)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityArchivalStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TI32}); err != nil { return err } if err := v.VisibilityArchivalStatus.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityArchivalURI != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.VisibilityArchivalURI)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RegisterDomainRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RegisterDomainRequest struct could not be generated from the wire // representation. func (v *RegisterDomainRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Description = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.OwnerEmail = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.WorkflowExecutionRetentionPeriodInDays = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.EmitMetric = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TList: v.Clusters, err = _List_ClusterReplicationConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActiveClusterName = &x if err != nil { return err } case fh.ID == 75 && fh.Type == wire.TMap: v.ActiveClustersByRegion, err = _Map_String_String_Decode(sr) if err != nil { return err } case fh.ID == 76 && fh.Type == wire.TStruct: v.ActiveClusters, err = _ActiveClusters_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TMap: v.Data, err = _Map_String_String_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SecurityToken = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.IsGlobalDomain = &x if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TI32: var x ArchivalStatus x, err = _ArchivalStatus_Decode(sr) v.HistoryArchivalStatus = &x if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.HistoryArchivalURI = &x if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TI32: var x ArchivalStatus x, err = _ArchivalStatus_Decode(sr) v.VisibilityArchivalStatus = &x if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.VisibilityArchivalURI = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if v.EmitMetric == nil { v.EmitMetric = ptr.Bool(true) } return nil } // String returns a readable string representation of a RegisterDomainRequest // struct. func (v *RegisterDomainRequest) String() string { if v == nil { return "" } var fields [16]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.Description != nil { fields[i] = fmt.Sprintf("Description: %v", *(v.Description)) i++ } if v.OwnerEmail != nil { fields[i] = fmt.Sprintf("OwnerEmail: %v", *(v.OwnerEmail)) i++ } if v.WorkflowExecutionRetentionPeriodInDays != nil { fields[i] = fmt.Sprintf("WorkflowExecutionRetentionPeriodInDays: %v", *(v.WorkflowExecutionRetentionPeriodInDays)) i++ } if v.EmitMetric != nil { fields[i] = fmt.Sprintf("EmitMetric: %v", *(v.EmitMetric)) i++ } if v.Clusters != nil { fields[i] = fmt.Sprintf("Clusters: %v", v.Clusters) i++ } if v.ActiveClusterName != nil { fields[i] = fmt.Sprintf("ActiveClusterName: %v", *(v.ActiveClusterName)) i++ } if v.ActiveClustersByRegion != nil { fields[i] = fmt.Sprintf("ActiveClustersByRegion: %v", v.ActiveClustersByRegion) i++ } if v.ActiveClusters != nil { fields[i] = fmt.Sprintf("ActiveClusters: %v", v.ActiveClusters) i++ } if v.Data != nil { fields[i] = fmt.Sprintf("Data: %v", v.Data) i++ } if v.SecurityToken != nil { fields[i] = fmt.Sprintf("SecurityToken: %v", *(v.SecurityToken)) i++ } if v.IsGlobalDomain != nil { fields[i] = fmt.Sprintf("IsGlobalDomain: %v", *(v.IsGlobalDomain)) i++ } if v.HistoryArchivalStatus != nil { fields[i] = fmt.Sprintf("HistoryArchivalStatus: %v", *(v.HistoryArchivalStatus)) i++ } if v.HistoryArchivalURI != nil { fields[i] = fmt.Sprintf("HistoryArchivalURI: %v", *(v.HistoryArchivalURI)) i++ } if v.VisibilityArchivalStatus != nil { fields[i] = fmt.Sprintf("VisibilityArchivalStatus: %v", *(v.VisibilityArchivalStatus)) i++ } if v.VisibilityArchivalURI != nil { fields[i] = fmt.Sprintf("VisibilityArchivalURI: %v", *(v.VisibilityArchivalURI)) i++ } return fmt.Sprintf("RegisterDomainRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RegisterDomainRequest match the // provided RegisterDomainRequest. // // This function performs a deep comparison. func (v *RegisterDomainRequest) Equals(rhs *RegisterDomainRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !_String_EqualsPtr(v.Description, rhs.Description) { return false } if !_String_EqualsPtr(v.OwnerEmail, rhs.OwnerEmail) { return false } if !_I32_EqualsPtr(v.WorkflowExecutionRetentionPeriodInDays, rhs.WorkflowExecutionRetentionPeriodInDays) { return false } if !_Bool_EqualsPtr(v.EmitMetric, rhs.EmitMetric) { return false } if !((v.Clusters == nil && rhs.Clusters == nil) || (v.Clusters != nil && rhs.Clusters != nil && _List_ClusterReplicationConfiguration_Equals(v.Clusters, rhs.Clusters))) { return false } if !_String_EqualsPtr(v.ActiveClusterName, rhs.ActiveClusterName) { return false } if !((v.ActiveClustersByRegion == nil && rhs.ActiveClustersByRegion == nil) || (v.ActiveClustersByRegion != nil && rhs.ActiveClustersByRegion != nil && _Map_String_String_Equals(v.ActiveClustersByRegion, rhs.ActiveClustersByRegion))) { return false } if !((v.ActiveClusters == nil && rhs.ActiveClusters == nil) || (v.ActiveClusters != nil && rhs.ActiveClusters != nil && v.ActiveClusters.Equals(rhs.ActiveClusters))) { return false } if !((v.Data == nil && rhs.Data == nil) || (v.Data != nil && rhs.Data != nil && _Map_String_String_Equals(v.Data, rhs.Data))) { return false } if !_String_EqualsPtr(v.SecurityToken, rhs.SecurityToken) { return false } if !_Bool_EqualsPtr(v.IsGlobalDomain, rhs.IsGlobalDomain) { return false } if !_ArchivalStatus_EqualsPtr(v.HistoryArchivalStatus, rhs.HistoryArchivalStatus) { return false } if !_String_EqualsPtr(v.HistoryArchivalURI, rhs.HistoryArchivalURI) { return false } if !_ArchivalStatus_EqualsPtr(v.VisibilityArchivalStatus, rhs.VisibilityArchivalStatus) { return false } if !_String_EqualsPtr(v.VisibilityArchivalURI, rhs.VisibilityArchivalURI) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RegisterDomainRequest. func (v *RegisterDomainRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.Description != nil { enc.AddString("description", *v.Description) } if v.OwnerEmail != nil { enc.AddString("ownerEmail", *v.OwnerEmail) } if v.WorkflowExecutionRetentionPeriodInDays != nil { enc.AddInt32("workflowExecutionRetentionPeriodInDays", *v.WorkflowExecutionRetentionPeriodInDays) } if v.EmitMetric != nil { enc.AddBool("emitMetric", *v.EmitMetric) } if v.Clusters != nil { err = multierr.Append(err, enc.AddArray("clusters", (_List_ClusterReplicationConfiguration_Zapper)(v.Clusters))) } if v.ActiveClusterName != nil { enc.AddString("activeClusterName", *v.ActiveClusterName) } if v.ActiveClustersByRegion != nil { err = multierr.Append(err, enc.AddObject("activeClustersByRegion", (_Map_String_String_Zapper)(v.ActiveClustersByRegion))) } if v.ActiveClusters != nil { err = multierr.Append(err, enc.AddObject("activeClusters", v.ActiveClusters)) } if v.Data != nil { err = multierr.Append(err, enc.AddObject("data", (_Map_String_String_Zapper)(v.Data))) } if v.SecurityToken != nil { enc.AddString("securityToken", *v.SecurityToken) } if v.IsGlobalDomain != nil { enc.AddBool("isGlobalDomain", *v.IsGlobalDomain) } if v.HistoryArchivalStatus != nil { err = multierr.Append(err, enc.AddObject("historyArchivalStatus", *v.HistoryArchivalStatus)) } if v.HistoryArchivalURI != nil { enc.AddString("historyArchivalURI", *v.HistoryArchivalURI) } if v.VisibilityArchivalStatus != nil { err = multierr.Append(err, enc.AddObject("visibilityArchivalStatus", *v.VisibilityArchivalStatus)) } if v.VisibilityArchivalURI != nil { enc.AddString("visibilityArchivalURI", *v.VisibilityArchivalURI) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *RegisterDomainRequest) IsSetName() bool { return v != nil && v.Name != nil } // GetDescription returns the value of Description if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetDescription() (o string) { if v != nil && v.Description != nil { return *v.Description } return } // IsSetDescription returns true if Description is not nil. func (v *RegisterDomainRequest) IsSetDescription() bool { return v != nil && v.Description != nil } // GetOwnerEmail returns the value of OwnerEmail if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetOwnerEmail() (o string) { if v != nil && v.OwnerEmail != nil { return *v.OwnerEmail } return } // IsSetOwnerEmail returns true if OwnerEmail is not nil. func (v *RegisterDomainRequest) IsSetOwnerEmail() bool { return v != nil && v.OwnerEmail != nil } // GetWorkflowExecutionRetentionPeriodInDays returns the value of WorkflowExecutionRetentionPeriodInDays if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetWorkflowExecutionRetentionPeriodInDays() (o int32) { if v != nil && v.WorkflowExecutionRetentionPeriodInDays != nil { return *v.WorkflowExecutionRetentionPeriodInDays } return } // IsSetWorkflowExecutionRetentionPeriodInDays returns true if WorkflowExecutionRetentionPeriodInDays is not nil. func (v *RegisterDomainRequest) IsSetWorkflowExecutionRetentionPeriodInDays() bool { return v != nil && v.WorkflowExecutionRetentionPeriodInDays != nil } // GetEmitMetric returns the value of EmitMetric if it is set or its // default value if it is unset. func (v *RegisterDomainRequest) GetEmitMetric() (o bool) { if v != nil && v.EmitMetric != nil { return *v.EmitMetric } o = true return } // IsSetEmitMetric returns true if EmitMetric is not nil. func (v *RegisterDomainRequest) IsSetEmitMetric() bool { return v != nil && v.EmitMetric != nil } // GetClusters returns the value of Clusters if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetClusters() (o []*ClusterReplicationConfiguration) { if v != nil && v.Clusters != nil { return v.Clusters } return } // IsSetClusters returns true if Clusters is not nil. func (v *RegisterDomainRequest) IsSetClusters() bool { return v != nil && v.Clusters != nil } // GetActiveClusterName returns the value of ActiveClusterName if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetActiveClusterName() (o string) { if v != nil && v.ActiveClusterName != nil { return *v.ActiveClusterName } return } // IsSetActiveClusterName returns true if ActiveClusterName is not nil. func (v *RegisterDomainRequest) IsSetActiveClusterName() bool { return v != nil && v.ActiveClusterName != nil } // GetActiveClustersByRegion returns the value of ActiveClustersByRegion if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetActiveClustersByRegion() (o map[string]string) { if v != nil && v.ActiveClustersByRegion != nil { return v.ActiveClustersByRegion } return } // IsSetActiveClustersByRegion returns true if ActiveClustersByRegion is not nil. func (v *RegisterDomainRequest) IsSetActiveClustersByRegion() bool { return v != nil && v.ActiveClustersByRegion != nil } // GetActiveClusters returns the value of ActiveClusters if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetActiveClusters() (o *ActiveClusters) { if v != nil && v.ActiveClusters != nil { return v.ActiveClusters } return } // IsSetActiveClusters returns true if ActiveClusters is not nil. func (v *RegisterDomainRequest) IsSetActiveClusters() bool { return v != nil && v.ActiveClusters != nil } // GetData returns the value of Data if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetData() (o map[string]string) { if v != nil && v.Data != nil { return v.Data } return } // IsSetData returns true if Data is not nil. func (v *RegisterDomainRequest) IsSetData() bool { return v != nil && v.Data != nil } // GetSecurityToken returns the value of SecurityToken if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetSecurityToken() (o string) { if v != nil && v.SecurityToken != nil { return *v.SecurityToken } return } // IsSetSecurityToken returns true if SecurityToken is not nil. func (v *RegisterDomainRequest) IsSetSecurityToken() bool { return v != nil && v.SecurityToken != nil } // GetIsGlobalDomain returns the value of IsGlobalDomain if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetIsGlobalDomain() (o bool) { if v != nil && v.IsGlobalDomain != nil { return *v.IsGlobalDomain } return } // IsSetIsGlobalDomain returns true if IsGlobalDomain is not nil. func (v *RegisterDomainRequest) IsSetIsGlobalDomain() bool { return v != nil && v.IsGlobalDomain != nil } // GetHistoryArchivalStatus returns the value of HistoryArchivalStatus if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetHistoryArchivalStatus() (o ArchivalStatus) { if v != nil && v.HistoryArchivalStatus != nil { return *v.HistoryArchivalStatus } return } // IsSetHistoryArchivalStatus returns true if HistoryArchivalStatus is not nil. func (v *RegisterDomainRequest) IsSetHistoryArchivalStatus() bool { return v != nil && v.HistoryArchivalStatus != nil } // GetHistoryArchivalURI returns the value of HistoryArchivalURI if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetHistoryArchivalURI() (o string) { if v != nil && v.HistoryArchivalURI != nil { return *v.HistoryArchivalURI } return } // IsSetHistoryArchivalURI returns true if HistoryArchivalURI is not nil. func (v *RegisterDomainRequest) IsSetHistoryArchivalURI() bool { return v != nil && v.HistoryArchivalURI != nil } // GetVisibilityArchivalStatus returns the value of VisibilityArchivalStatus if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetVisibilityArchivalStatus() (o ArchivalStatus) { if v != nil && v.VisibilityArchivalStatus != nil { return *v.VisibilityArchivalStatus } return } // IsSetVisibilityArchivalStatus returns true if VisibilityArchivalStatus is not nil. func (v *RegisterDomainRequest) IsSetVisibilityArchivalStatus() bool { return v != nil && v.VisibilityArchivalStatus != nil } // GetVisibilityArchivalURI returns the value of VisibilityArchivalURI if it is set or its // zero value if it is unset. func (v *RegisterDomainRequest) GetVisibilityArchivalURI() (o string) { if v != nil && v.VisibilityArchivalURI != nil { return *v.VisibilityArchivalURI } return } // IsSetVisibilityArchivalURI returns true if VisibilityArchivalURI is not nil. func (v *RegisterDomainRequest) IsSetVisibilityArchivalURI() bool { return v != nil && v.VisibilityArchivalURI != nil } type RemoteSyncMatchedError struct { Message string `json:"message,required"` } // ToWire translates a RemoteSyncMatchedError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RemoteSyncMatchedError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RemoteSyncMatchedError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RemoteSyncMatchedError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RemoteSyncMatchedError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RemoteSyncMatchedError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of RemoteSyncMatchedError is required") } return nil } // Encode serializes a RemoteSyncMatchedError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RemoteSyncMatchedError struct could not be encoded. func (v *RemoteSyncMatchedError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a RemoteSyncMatchedError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RemoteSyncMatchedError struct could not be generated from the wire // representation. func (v *RemoteSyncMatchedError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of RemoteSyncMatchedError is required") } return nil } // String returns a readable string representation of a RemoteSyncMatchedError // struct. func (v *RemoteSyncMatchedError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("RemoteSyncMatchedError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*RemoteSyncMatchedError) ErrorName() string { return "RemoteSyncMatchedError" } // Equals returns true if all the fields of this RemoteSyncMatchedError match the // provided RemoteSyncMatchedError. // // This function performs a deep comparison. func (v *RemoteSyncMatchedError) Equals(rhs *RemoteSyncMatchedError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RemoteSyncMatchedError. func (v *RemoteSyncMatchedError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *RemoteSyncMatchedError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *RemoteSyncMatchedError) Error() string { return v.String() } type RemoveTaskRequest struct { ShardID *int32 `json:"shardID,omitempty"` Type *int32 `json:"type,omitempty"` TaskID *int64 `json:"taskID,omitempty"` VisibilityTimestamp *int64 `json:"visibilityTimestamp,omitempty"` ClusterName *string `json:"clusterName,omitempty"` } // ToWire translates a RemoveTaskRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RemoveTaskRequest) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.ShardID != nil { w, err = wire.NewValueI32(*(v.ShardID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Type != nil { w, err = wire.NewValueI32(*(v.Type)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TaskID != nil { w, err = wire.NewValueI64(*(v.TaskID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.VisibilityTimestamp != nil { w, err = wire.NewValueI64(*(v.VisibilityTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ClusterName != nil { w, err = wire.NewValueString(*(v.ClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RemoveTaskRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RemoveTaskRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RemoveTaskRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RemoveTaskRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShardID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Type = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.VisibilityTimestamp = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClusterName = &x if err != nil { return err } } } } return nil } // Encode serializes a RemoveTaskRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RemoveTaskRequest struct could not be encoded. func (v *RemoveTaskRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ShardID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShardID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Type != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Type)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.VisibilityTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RemoveTaskRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RemoveTaskRequest struct could not be generated from the wire // representation. func (v *RemoveTaskRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShardID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Type = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.VisibilityTimestamp = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClusterName = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RemoveTaskRequest // struct. func (v *RemoveTaskRequest) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.ShardID != nil { fields[i] = fmt.Sprintf("ShardID: %v", *(v.ShardID)) i++ } if v.Type != nil { fields[i] = fmt.Sprintf("Type: %v", *(v.Type)) i++ } if v.TaskID != nil { fields[i] = fmt.Sprintf("TaskID: %v", *(v.TaskID)) i++ } if v.VisibilityTimestamp != nil { fields[i] = fmt.Sprintf("VisibilityTimestamp: %v", *(v.VisibilityTimestamp)) i++ } if v.ClusterName != nil { fields[i] = fmt.Sprintf("ClusterName: %v", *(v.ClusterName)) i++ } return fmt.Sprintf("RemoveTaskRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RemoveTaskRequest match the // provided RemoveTaskRequest. // // This function performs a deep comparison. func (v *RemoveTaskRequest) Equals(rhs *RemoveTaskRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.ShardID, rhs.ShardID) { return false } if !_I32_EqualsPtr(v.Type, rhs.Type) { return false } if !_I64_EqualsPtr(v.TaskID, rhs.TaskID) { return false } if !_I64_EqualsPtr(v.VisibilityTimestamp, rhs.VisibilityTimestamp) { return false } if !_String_EqualsPtr(v.ClusterName, rhs.ClusterName) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RemoveTaskRequest. func (v *RemoveTaskRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ShardID != nil { enc.AddInt32("shardID", *v.ShardID) } if v.Type != nil { enc.AddInt32("type", *v.Type) } if v.TaskID != nil { enc.AddInt64("taskID", *v.TaskID) } if v.VisibilityTimestamp != nil { enc.AddInt64("visibilityTimestamp", *v.VisibilityTimestamp) } if v.ClusterName != nil { enc.AddString("clusterName", *v.ClusterName) } return err } // GetShardID returns the value of ShardID if it is set or its // zero value if it is unset. func (v *RemoveTaskRequest) GetShardID() (o int32) { if v != nil && v.ShardID != nil { return *v.ShardID } return } // IsSetShardID returns true if ShardID is not nil. func (v *RemoveTaskRequest) IsSetShardID() bool { return v != nil && v.ShardID != nil } // GetType returns the value of Type if it is set or its // zero value if it is unset. func (v *RemoveTaskRequest) GetType() (o int32) { if v != nil && v.Type != nil { return *v.Type } return } // IsSetType returns true if Type is not nil. func (v *RemoveTaskRequest) IsSetType() bool { return v != nil && v.Type != nil } // GetTaskID returns the value of TaskID if it is set or its // zero value if it is unset. func (v *RemoveTaskRequest) GetTaskID() (o int64) { if v != nil && v.TaskID != nil { return *v.TaskID } return } // IsSetTaskID returns true if TaskID is not nil. func (v *RemoveTaskRequest) IsSetTaskID() bool { return v != nil && v.TaskID != nil } // GetVisibilityTimestamp returns the value of VisibilityTimestamp if it is set or its // zero value if it is unset. func (v *RemoveTaskRequest) GetVisibilityTimestamp() (o int64) { if v != nil && v.VisibilityTimestamp != nil { return *v.VisibilityTimestamp } return } // IsSetVisibilityTimestamp returns true if VisibilityTimestamp is not nil. func (v *RemoveTaskRequest) IsSetVisibilityTimestamp() bool { return v != nil && v.VisibilityTimestamp != nil } // GetClusterName returns the value of ClusterName if it is set or its // zero value if it is unset. func (v *RemoveTaskRequest) GetClusterName() (o string) { if v != nil && v.ClusterName != nil { return *v.ClusterName } return } // IsSetClusterName returns true if ClusterName is not nil. func (v *RemoveTaskRequest) IsSetClusterName() bool { return v != nil && v.ClusterName != nil } type RequestCancelActivityTaskDecisionAttributes struct { ActivityId *string `json:"activityId,omitempty"` } // ToWire translates a RequestCancelActivityTaskDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RequestCancelActivityTaskDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.ActivityId != nil { w, err = wire.NewValueString(*(v.ActivityId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RequestCancelActivityTaskDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RequestCancelActivityTaskDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RequestCancelActivityTaskDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RequestCancelActivityTaskDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityId = &x if err != nil { return err } } } } return nil } // Encode serializes a RequestCancelActivityTaskDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RequestCancelActivityTaskDecisionAttributes struct could not be encoded. func (v *RequestCancelActivityTaskDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActivityId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RequestCancelActivityTaskDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RequestCancelActivityTaskDecisionAttributes struct could not be generated from the wire // representation. func (v *RequestCancelActivityTaskDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RequestCancelActivityTaskDecisionAttributes // struct. func (v *RequestCancelActivityTaskDecisionAttributes) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.ActivityId != nil { fields[i] = fmt.Sprintf("ActivityId: %v", *(v.ActivityId)) i++ } return fmt.Sprintf("RequestCancelActivityTaskDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RequestCancelActivityTaskDecisionAttributes match the // provided RequestCancelActivityTaskDecisionAttributes. // // This function performs a deep comparison. func (v *RequestCancelActivityTaskDecisionAttributes) Equals(rhs *RequestCancelActivityTaskDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ActivityId, rhs.ActivityId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RequestCancelActivityTaskDecisionAttributes. func (v *RequestCancelActivityTaskDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActivityId != nil { enc.AddString("activityId", *v.ActivityId) } return err } // GetActivityId returns the value of ActivityId if it is set or its // zero value if it is unset. func (v *RequestCancelActivityTaskDecisionAttributes) GetActivityId() (o string) { if v != nil && v.ActivityId != nil { return *v.ActivityId } return } // IsSetActivityId returns true if ActivityId is not nil. func (v *RequestCancelActivityTaskDecisionAttributes) IsSetActivityId() bool { return v != nil && v.ActivityId != nil } type RequestCancelActivityTaskFailedEventAttributes struct { ActivityId *string `json:"activityId,omitempty"` Cause *string `json:"cause,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` } // ToWire translates a RequestCancelActivityTaskFailedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RequestCancelActivityTaskFailedEventAttributes) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.ActivityId != nil { w, err = wire.NewValueString(*(v.ActivityId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Cause != nil { w, err = wire.NewValueString(*(v.Cause)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RequestCancelActivityTaskFailedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RequestCancelActivityTaskFailedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RequestCancelActivityTaskFailedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RequestCancelActivityTaskFailedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Cause = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a RequestCancelActivityTaskFailedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RequestCancelActivityTaskFailedEventAttributes struct could not be encoded. func (v *RequestCancelActivityTaskFailedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActivityId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Cause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Cause)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RequestCancelActivityTaskFailedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RequestCancelActivityTaskFailedEventAttributes struct could not be generated from the wire // representation. func (v *RequestCancelActivityTaskFailedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Cause = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RequestCancelActivityTaskFailedEventAttributes // struct. func (v *RequestCancelActivityTaskFailedEventAttributes) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.ActivityId != nil { fields[i] = fmt.Sprintf("ActivityId: %v", *(v.ActivityId)) i++ } if v.Cause != nil { fields[i] = fmt.Sprintf("Cause: %v", *(v.Cause)) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } return fmt.Sprintf("RequestCancelActivityTaskFailedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RequestCancelActivityTaskFailedEventAttributes match the // provided RequestCancelActivityTaskFailedEventAttributes. // // This function performs a deep comparison. func (v *RequestCancelActivityTaskFailedEventAttributes) Equals(rhs *RequestCancelActivityTaskFailedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ActivityId, rhs.ActivityId) { return false } if !_String_EqualsPtr(v.Cause, rhs.Cause) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RequestCancelActivityTaskFailedEventAttributes. func (v *RequestCancelActivityTaskFailedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActivityId != nil { enc.AddString("activityId", *v.ActivityId) } if v.Cause != nil { enc.AddString("cause", *v.Cause) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } return err } // GetActivityId returns the value of ActivityId if it is set or its // zero value if it is unset. func (v *RequestCancelActivityTaskFailedEventAttributes) GetActivityId() (o string) { if v != nil && v.ActivityId != nil { return *v.ActivityId } return } // IsSetActivityId returns true if ActivityId is not nil. func (v *RequestCancelActivityTaskFailedEventAttributes) IsSetActivityId() bool { return v != nil && v.ActivityId != nil } // GetCause returns the value of Cause if it is set or its // zero value if it is unset. func (v *RequestCancelActivityTaskFailedEventAttributes) GetCause() (o string) { if v != nil && v.Cause != nil { return *v.Cause } return } // IsSetCause returns true if Cause is not nil. func (v *RequestCancelActivityTaskFailedEventAttributes) IsSetCause() bool { return v != nil && v.Cause != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *RequestCancelActivityTaskFailedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *RequestCancelActivityTaskFailedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } type RequestCancelExternalWorkflowExecutionDecisionAttributes struct { Domain *string `json:"domain,omitempty"` WorkflowId *string `json:"workflowId,omitempty"` RunId *string `json:"runId,omitempty"` Control []byte `json:"control,omitempty"` ChildWorkflowOnly *bool `json:"childWorkflowOnly,omitempty"` } // ToWire translates a RequestCancelExternalWorkflowExecutionDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ChildWorkflowOnly != nil { w, err = wire.NewValueBool(*(v.ChildWorkflowOnly)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RequestCancelExternalWorkflowExecutionDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RequestCancelExternalWorkflowExecutionDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RequestCancelExternalWorkflowExecutionDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ChildWorkflowOnly = &x if err != nil { return err } } } } return nil } // Encode serializes a RequestCancelExternalWorkflowExecutionDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RequestCancelExternalWorkflowExecutionDecisionAttributes struct could not be encoded. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowOnly != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ChildWorkflowOnly)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RequestCancelExternalWorkflowExecutionDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RequestCancelExternalWorkflowExecutionDecisionAttributes struct could not be generated from the wire // representation. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ChildWorkflowOnly = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RequestCancelExternalWorkflowExecutionDecisionAttributes // struct. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } if v.ChildWorkflowOnly != nil { fields[i] = fmt.Sprintf("ChildWorkflowOnly: %v", *(v.ChildWorkflowOnly)) i++ } return fmt.Sprintf("RequestCancelExternalWorkflowExecutionDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RequestCancelExternalWorkflowExecutionDecisionAttributes match the // provided RequestCancelExternalWorkflowExecutionDecisionAttributes. // // This function performs a deep comparison. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) Equals(rhs *RequestCancelExternalWorkflowExecutionDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } if !_Bool_EqualsPtr(v.ChildWorkflowOnly, rhs.ChildWorkflowOnly) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RequestCancelExternalWorkflowExecutionDecisionAttributes. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.RunId != nil { enc.AddString("runId", *v.RunId) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } if v.ChildWorkflowOnly != nil { enc.AddBool("childWorkflowOnly", *v.ChildWorkflowOnly) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) IsSetRunId() bool { return v != nil && v.RunId != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) IsSetControl() bool { return v != nil && v.Control != nil } // GetChildWorkflowOnly returns the value of ChildWorkflowOnly if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) GetChildWorkflowOnly() (o bool) { if v != nil && v.ChildWorkflowOnly != nil { return *v.ChildWorkflowOnly } return } // IsSetChildWorkflowOnly returns true if ChildWorkflowOnly is not nil. func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) IsSetChildWorkflowOnly() bool { return v != nil && v.ChildWorkflowOnly != nil } type RequestCancelExternalWorkflowExecutionFailedEventAttributes struct { Cause *CancelExternalWorkflowExecutionFailedCause `json:"cause,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` Control []byte `json:"control,omitempty"` } // ToWire translates a RequestCancelExternalWorkflowExecutionFailedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Cause != nil { w, err = v.Cause.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CancelExternalWorkflowExecutionFailedCause_Read(w wire.Value) (CancelExternalWorkflowExecutionFailedCause, error) { var v CancelExternalWorkflowExecutionFailedCause err := v.FromWire(w) return v, err } // FromWire deserializes a RequestCancelExternalWorkflowExecutionFailedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RequestCancelExternalWorkflowExecutionFailedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RequestCancelExternalWorkflowExecutionFailedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x CancelExternalWorkflowExecutionFailedCause x, err = _CancelExternalWorkflowExecutionFailedCause_Read(field.Value) v.Cause = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a RequestCancelExternalWorkflowExecutionFailedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RequestCancelExternalWorkflowExecutionFailedEventAttributes struct could not be encoded. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Cause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.Cause.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CancelExternalWorkflowExecutionFailedCause_Decode(sr stream.Reader) (CancelExternalWorkflowExecutionFailedCause, error) { var v CancelExternalWorkflowExecutionFailedCause err := v.Decode(sr) return v, err } // Decode deserializes a RequestCancelExternalWorkflowExecutionFailedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RequestCancelExternalWorkflowExecutionFailedEventAttributes struct could not be generated from the wire // representation. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x CancelExternalWorkflowExecutionFailedCause x, err = _CancelExternalWorkflowExecutionFailedCause_Decode(sr) v.Cause = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RequestCancelExternalWorkflowExecutionFailedEventAttributes // struct. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Cause != nil { fields[i] = fmt.Sprintf("Cause: %v", *(v.Cause)) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } return fmt.Sprintf("RequestCancelExternalWorkflowExecutionFailedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } func _CancelExternalWorkflowExecutionFailedCause_EqualsPtr(lhs, rhs *CancelExternalWorkflowExecutionFailedCause) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this RequestCancelExternalWorkflowExecutionFailedEventAttributes match the // provided RequestCancelExternalWorkflowExecutionFailedEventAttributes. // // This function performs a deep comparison. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) Equals(rhs *RequestCancelExternalWorkflowExecutionFailedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_CancelExternalWorkflowExecutionFailedCause_EqualsPtr(v.Cause, rhs.Cause) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RequestCancelExternalWorkflowExecutionFailedEventAttributes. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Cause != nil { err = multierr.Append(err, enc.AddObject("cause", *v.Cause)) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } return err } // GetCause returns the value of Cause if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) GetCause() (o CancelExternalWorkflowExecutionFailedCause) { if v != nil && v.Cause != nil { return *v.Cause } return } // IsSetCause returns true if Cause is not nil. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) IsSetCause() bool { return v != nil && v.Cause != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) IsSetControl() bool { return v != nil && v.Control != nil } type RequestCancelExternalWorkflowExecutionInitiatedEventAttributes struct { DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Control []byte `json:"control,omitempty"` ChildWorkflowOnly *bool `json:"childWorkflowOnly,omitempty"` } // ToWire translates a RequestCancelExternalWorkflowExecutionInitiatedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ChildWorkflowOnly != nil { w, err = wire.NewValueBool(*(v.ChildWorkflowOnly)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RequestCancelExternalWorkflowExecutionInitiatedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RequestCancelExternalWorkflowExecutionInitiatedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RequestCancelExternalWorkflowExecutionInitiatedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ChildWorkflowOnly = &x if err != nil { return err } } } } return nil } // Encode serializes a RequestCancelExternalWorkflowExecutionInitiatedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RequestCancelExternalWorkflowExecutionInitiatedEventAttributes struct could not be encoded. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowOnly != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ChildWorkflowOnly)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RequestCancelExternalWorkflowExecutionInitiatedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RequestCancelExternalWorkflowExecutionInitiatedEventAttributes struct could not be generated from the wire // representation. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ChildWorkflowOnly = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RequestCancelExternalWorkflowExecutionInitiatedEventAttributes // struct. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } if v.ChildWorkflowOnly != nil { fields[i] = fmt.Sprintf("ChildWorkflowOnly: %v", *(v.ChildWorkflowOnly)) i++ } return fmt.Sprintf("RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RequestCancelExternalWorkflowExecutionInitiatedEventAttributes match the // provided RequestCancelExternalWorkflowExecutionInitiatedEventAttributes. // // This function performs a deep comparison. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) Equals(rhs *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } if !_Bool_EqualsPtr(v.ChildWorkflowOnly, rhs.ChildWorkflowOnly) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RequestCancelExternalWorkflowExecutionInitiatedEventAttributes. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } if v.ChildWorkflowOnly != nil { enc.AddBool("childWorkflowOnly", *v.ChildWorkflowOnly) } return err } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) IsSetControl() bool { return v != nil && v.Control != nil } // GetChildWorkflowOnly returns the value of ChildWorkflowOnly if it is set or its // zero value if it is unset. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) GetChildWorkflowOnly() (o bool) { if v != nil && v.ChildWorkflowOnly != nil { return *v.ChildWorkflowOnly } return } // IsSetChildWorkflowOnly returns true if ChildWorkflowOnly is not nil. func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) IsSetChildWorkflowOnly() bool { return v != nil && v.ChildWorkflowOnly != nil } type RequestCancelWorkflowExecutionRequest struct { Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Identity *string `json:"identity,omitempty"` RequestId *string `json:"requestId,omitempty"` Cause *string `json:"cause,omitempty"` FirstExecutionRunID *string `json:"firstExecutionRunID,omitempty"` } // ToWire translates a RequestCancelWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RequestCancelWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Cause != nil { w, err = wire.NewValueString(*(v.Cause)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.FirstExecutionRunID != nil { w, err = wire.NewValueString(*(v.FirstExecutionRunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RequestCancelWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RequestCancelWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RequestCancelWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RequestCancelWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Cause = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.FirstExecutionRunID = &x if err != nil { return err } } } } return nil } // Encode serializes a RequestCancelWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RequestCancelWorkflowExecutionRequest struct could not be encoded. func (v *RequestCancelWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Cause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Cause)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstExecutionRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.FirstExecutionRunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RequestCancelWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RequestCancelWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *RequestCancelWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Cause = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.FirstExecutionRunID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RequestCancelWorkflowExecutionRequest // struct. func (v *RequestCancelWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } if v.Cause != nil { fields[i] = fmt.Sprintf("Cause: %v", *(v.Cause)) i++ } if v.FirstExecutionRunID != nil { fields[i] = fmt.Sprintf("FirstExecutionRunID: %v", *(v.FirstExecutionRunID)) i++ } return fmt.Sprintf("RequestCancelWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RequestCancelWorkflowExecutionRequest match the // provided RequestCancelWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *RequestCancelWorkflowExecutionRequest) Equals(rhs *RequestCancelWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } if !_String_EqualsPtr(v.Cause, rhs.Cause) { return false } if !_String_EqualsPtr(v.FirstExecutionRunID, rhs.FirstExecutionRunID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RequestCancelWorkflowExecutionRequest. func (v *RequestCancelWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } if v.Cause != nil { enc.AddString("cause", *v.Cause) } if v.FirstExecutionRunID != nil { enc.AddString("firstExecutionRunID", *v.FirstExecutionRunID) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetRequestId() bool { return v != nil && v.RequestId != nil } // GetCause returns the value of Cause if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetCause() (o string) { if v != nil && v.Cause != nil { return *v.Cause } return } // IsSetCause returns true if Cause is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetCause() bool { return v != nil && v.Cause != nil } // GetFirstExecutionRunID returns the value of FirstExecutionRunID if it is set or its // zero value if it is unset. func (v *RequestCancelWorkflowExecutionRequest) GetFirstExecutionRunID() (o string) { if v != nil && v.FirstExecutionRunID != nil { return *v.FirstExecutionRunID } return } // IsSetFirstExecutionRunID returns true if FirstExecutionRunID is not nil. func (v *RequestCancelWorkflowExecutionRequest) IsSetFirstExecutionRunID() bool { return v != nil && v.FirstExecutionRunID != nil } type ResetPointInfo struct { BinaryChecksum *string `json:"binaryChecksum,omitempty"` RunId *string `json:"runId,omitempty"` FirstDecisionCompletedId *int64 `json:"firstDecisionCompletedId,omitempty"` CreatedTimeNano *int64 `json:"createdTimeNano,omitempty"` ExpiringTimeNano *int64 `json:"expiringTimeNano,omitempty"` Resettable *bool `json:"resettable,omitempty"` } // ToWire translates a ResetPointInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResetPointInfo) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.BinaryChecksum != nil { w, err = wire.NewValueString(*(v.BinaryChecksum)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.FirstDecisionCompletedId != nil { w, err = wire.NewValueI64(*(v.FirstDecisionCompletedId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.CreatedTimeNano != nil { w, err = wire.NewValueI64(*(v.CreatedTimeNano)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ExpiringTimeNano != nil { w, err = wire.NewValueI64(*(v.ExpiringTimeNano)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Resettable != nil { w, err = wire.NewValueBool(*(v.Resettable)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ResetPointInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResetPointInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResetPointInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResetPointInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BinaryChecksum = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FirstDecisionCompletedId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CreatedTimeNano = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExpiringTimeNano = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.Resettable = &x if err != nil { return err } } } } return nil } // Encode serializes a ResetPointInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResetPointInfo struct could not be encoded. func (v *ResetPointInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BinaryChecksum != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BinaryChecksum)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstDecisionCompletedId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FirstDecisionCompletedId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CreatedTimeNano != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CreatedTimeNano)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExpiringTimeNano != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExpiringTimeNano)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Resettable != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.Resettable)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ResetPointInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResetPointInfo struct could not be generated from the wire // representation. func (v *ResetPointInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BinaryChecksum = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FirstDecisionCompletedId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CreatedTimeNano = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExpiringTimeNano = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.Resettable = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResetPointInfo // struct. func (v *ResetPointInfo) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.BinaryChecksum != nil { fields[i] = fmt.Sprintf("BinaryChecksum: %v", *(v.BinaryChecksum)) i++ } if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } if v.FirstDecisionCompletedId != nil { fields[i] = fmt.Sprintf("FirstDecisionCompletedId: %v", *(v.FirstDecisionCompletedId)) i++ } if v.CreatedTimeNano != nil { fields[i] = fmt.Sprintf("CreatedTimeNano: %v", *(v.CreatedTimeNano)) i++ } if v.ExpiringTimeNano != nil { fields[i] = fmt.Sprintf("ExpiringTimeNano: %v", *(v.ExpiringTimeNano)) i++ } if v.Resettable != nil { fields[i] = fmt.Sprintf("Resettable: %v", *(v.Resettable)) i++ } return fmt.Sprintf("ResetPointInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ResetPointInfo match the // provided ResetPointInfo. // // This function performs a deep comparison. func (v *ResetPointInfo) Equals(rhs *ResetPointInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.BinaryChecksum, rhs.BinaryChecksum) { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } if !_I64_EqualsPtr(v.FirstDecisionCompletedId, rhs.FirstDecisionCompletedId) { return false } if !_I64_EqualsPtr(v.CreatedTimeNano, rhs.CreatedTimeNano) { return false } if !_I64_EqualsPtr(v.ExpiringTimeNano, rhs.ExpiringTimeNano) { return false } if !_Bool_EqualsPtr(v.Resettable, rhs.Resettable) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResetPointInfo. func (v *ResetPointInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BinaryChecksum != nil { enc.AddString("binaryChecksum", *v.BinaryChecksum) } if v.RunId != nil { enc.AddString("runId", *v.RunId) } if v.FirstDecisionCompletedId != nil { enc.AddInt64("firstDecisionCompletedId", *v.FirstDecisionCompletedId) } if v.CreatedTimeNano != nil { enc.AddInt64("createdTimeNano", *v.CreatedTimeNano) } if v.ExpiringTimeNano != nil { enc.AddInt64("expiringTimeNano", *v.ExpiringTimeNano) } if v.Resettable != nil { enc.AddBool("resettable", *v.Resettable) } return err } // GetBinaryChecksum returns the value of BinaryChecksum if it is set or its // zero value if it is unset. func (v *ResetPointInfo) GetBinaryChecksum() (o string) { if v != nil && v.BinaryChecksum != nil { return *v.BinaryChecksum } return } // IsSetBinaryChecksum returns true if BinaryChecksum is not nil. func (v *ResetPointInfo) IsSetBinaryChecksum() bool { return v != nil && v.BinaryChecksum != nil } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *ResetPointInfo) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *ResetPointInfo) IsSetRunId() bool { return v != nil && v.RunId != nil } // GetFirstDecisionCompletedId returns the value of FirstDecisionCompletedId if it is set or its // zero value if it is unset. func (v *ResetPointInfo) GetFirstDecisionCompletedId() (o int64) { if v != nil && v.FirstDecisionCompletedId != nil { return *v.FirstDecisionCompletedId } return } // IsSetFirstDecisionCompletedId returns true if FirstDecisionCompletedId is not nil. func (v *ResetPointInfo) IsSetFirstDecisionCompletedId() bool { return v != nil && v.FirstDecisionCompletedId != nil } // GetCreatedTimeNano returns the value of CreatedTimeNano if it is set or its // zero value if it is unset. func (v *ResetPointInfo) GetCreatedTimeNano() (o int64) { if v != nil && v.CreatedTimeNano != nil { return *v.CreatedTimeNano } return } // IsSetCreatedTimeNano returns true if CreatedTimeNano is not nil. func (v *ResetPointInfo) IsSetCreatedTimeNano() bool { return v != nil && v.CreatedTimeNano != nil } // GetExpiringTimeNano returns the value of ExpiringTimeNano if it is set or its // zero value if it is unset. func (v *ResetPointInfo) GetExpiringTimeNano() (o int64) { if v != nil && v.ExpiringTimeNano != nil { return *v.ExpiringTimeNano } return } // IsSetExpiringTimeNano returns true if ExpiringTimeNano is not nil. func (v *ResetPointInfo) IsSetExpiringTimeNano() bool { return v != nil && v.ExpiringTimeNano != nil } // GetResettable returns the value of Resettable if it is set or its // zero value if it is unset. func (v *ResetPointInfo) GetResettable() (o bool) { if v != nil && v.Resettable != nil { return *v.Resettable } return } // IsSetResettable returns true if Resettable is not nil. func (v *ResetPointInfo) IsSetResettable() bool { return v != nil && v.Resettable != nil } type ResetPoints struct { Points []*ResetPointInfo `json:"points,omitempty"` } type _List_ResetPointInfo_ValueList []*ResetPointInfo func (v _List_ResetPointInfo_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*ResetPointInfo', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_ResetPointInfo_ValueList) Size() int { return len(v) } func (_List_ResetPointInfo_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_ResetPointInfo_ValueList) Close() {} // ToWire translates a ResetPoints struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResetPoints) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Points != nil { w, err = wire.NewValueList(_List_ResetPointInfo_ValueList(v.Points)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetPointInfo_Read(w wire.Value) (*ResetPointInfo, error) { var v ResetPointInfo err := v.FromWire(w) return &v, err } func _List_ResetPointInfo_Read(l wire.ValueList) ([]*ResetPointInfo, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*ResetPointInfo, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _ResetPointInfo_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a ResetPoints struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResetPoints struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResetPoints // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResetPoints) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Points, err = _List_ResetPointInfo_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_ResetPointInfo_Encode(val []*ResetPointInfo, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*ResetPointInfo', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a ResetPoints struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResetPoints struct could not be encoded. func (v *ResetPoints) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Points != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_ResetPointInfo_Encode(v.Points, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ResetPointInfo_Decode(sr stream.Reader) (*ResetPointInfo, error) { var v ResetPointInfo err := v.Decode(sr) return &v, err } func _List_ResetPointInfo_Decode(sr stream.Reader) ([]*ResetPointInfo, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*ResetPointInfo, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _ResetPointInfo_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ResetPoints struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResetPoints struct could not be generated from the wire // representation. func (v *ResetPoints) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Points, err = _List_ResetPointInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResetPoints // struct. func (v *ResetPoints) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Points != nil { fields[i] = fmt.Sprintf("Points: %v", v.Points) i++ } return fmt.Sprintf("ResetPoints{%v}", strings.Join(fields[:i], ", ")) } func _List_ResetPointInfo_Equals(lhs, rhs []*ResetPointInfo) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this ResetPoints match the // provided ResetPoints. // // This function performs a deep comparison. func (v *ResetPoints) Equals(rhs *ResetPoints) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Points == nil && rhs.Points == nil) || (v.Points != nil && rhs.Points != nil && _List_ResetPointInfo_Equals(v.Points, rhs.Points))) { return false } return true } type _List_ResetPointInfo_Zapper []*ResetPointInfo // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_ResetPointInfo_Zapper. func (l _List_ResetPointInfo_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResetPoints. func (v *ResetPoints) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Points != nil { err = multierr.Append(err, enc.AddArray("points", (_List_ResetPointInfo_Zapper)(v.Points))) } return err } // GetPoints returns the value of Points if it is set or its // zero value if it is unset. func (v *ResetPoints) GetPoints() (o []*ResetPointInfo) { if v != nil && v.Points != nil { return v.Points } return } // IsSetPoints returns true if Points is not nil. func (v *ResetPoints) IsSetPoints() bool { return v != nil && v.Points != nil } type ResetQueueRequest struct { ShardID *int32 `json:"shardID,omitempty"` ClusterName *string `json:"clusterName,omitempty"` Type *int32 `json:"type,omitempty"` } // ToWire translates a ResetQueueRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResetQueueRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.ShardID != nil { w, err = wire.NewValueI32(*(v.ShardID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ClusterName != nil { w, err = wire.NewValueString(*(v.ClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Type != nil { w, err = wire.NewValueI32(*(v.Type)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ResetQueueRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResetQueueRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResetQueueRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResetQueueRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShardID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClusterName = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Type = &x if err != nil { return err } } } } return nil } // Encode serializes a ResetQueueRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResetQueueRequest struct could not be encoded. func (v *ResetQueueRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ShardID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShardID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Type != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Type)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ResetQueueRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResetQueueRequest struct could not be generated from the wire // representation. func (v *ResetQueueRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShardID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClusterName = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Type = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResetQueueRequest // struct. func (v *ResetQueueRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.ShardID != nil { fields[i] = fmt.Sprintf("ShardID: %v", *(v.ShardID)) i++ } if v.ClusterName != nil { fields[i] = fmt.Sprintf("ClusterName: %v", *(v.ClusterName)) i++ } if v.Type != nil { fields[i] = fmt.Sprintf("Type: %v", *(v.Type)) i++ } return fmt.Sprintf("ResetQueueRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ResetQueueRequest match the // provided ResetQueueRequest. // // This function performs a deep comparison. func (v *ResetQueueRequest) Equals(rhs *ResetQueueRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.ShardID, rhs.ShardID) { return false } if !_String_EqualsPtr(v.ClusterName, rhs.ClusterName) { return false } if !_I32_EqualsPtr(v.Type, rhs.Type) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResetQueueRequest. func (v *ResetQueueRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ShardID != nil { enc.AddInt32("shardID", *v.ShardID) } if v.ClusterName != nil { enc.AddString("clusterName", *v.ClusterName) } if v.Type != nil { enc.AddInt32("type", *v.Type) } return err } // GetShardID returns the value of ShardID if it is set or its // zero value if it is unset. func (v *ResetQueueRequest) GetShardID() (o int32) { if v != nil && v.ShardID != nil { return *v.ShardID } return } // IsSetShardID returns true if ShardID is not nil. func (v *ResetQueueRequest) IsSetShardID() bool { return v != nil && v.ShardID != nil } // GetClusterName returns the value of ClusterName if it is set or its // zero value if it is unset. func (v *ResetQueueRequest) GetClusterName() (o string) { if v != nil && v.ClusterName != nil { return *v.ClusterName } return } // IsSetClusterName returns true if ClusterName is not nil. func (v *ResetQueueRequest) IsSetClusterName() bool { return v != nil && v.ClusterName != nil } // GetType returns the value of Type if it is set or its // zero value if it is unset. func (v *ResetQueueRequest) GetType() (o int32) { if v != nil && v.Type != nil { return *v.Type } return } // IsSetType returns true if Type is not nil. func (v *ResetQueueRequest) IsSetType() bool { return v != nil && v.Type != nil } type ResetStickyTaskListRequest struct { Domain *string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` } // ToWire translates a ResetStickyTaskListRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResetStickyTaskListRequest) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ResetStickyTaskListRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResetStickyTaskListRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResetStickyTaskListRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResetStickyTaskListRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a ResetStickyTaskListRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResetStickyTaskListRequest struct could not be encoded. func (v *ResetStickyTaskListRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ResetStickyTaskListRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResetStickyTaskListRequest struct could not be generated from the wire // representation. func (v *ResetStickyTaskListRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResetStickyTaskListRequest // struct. func (v *ResetStickyTaskListRequest) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } return fmt.Sprintf("ResetStickyTaskListRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ResetStickyTaskListRequest match the // provided ResetStickyTaskListRequest. // // This function performs a deep comparison. func (v *ResetStickyTaskListRequest) Equals(rhs *ResetStickyTaskListRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResetStickyTaskListRequest. func (v *ResetStickyTaskListRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ResetStickyTaskListRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ResetStickyTaskListRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *ResetStickyTaskListRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *ResetStickyTaskListRequest) IsSetExecution() bool { return v != nil && v.Execution != nil } type ResetStickyTaskListResponse struct { } // ToWire translates a ResetStickyTaskListResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResetStickyTaskListResponse) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ResetStickyTaskListResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResetStickyTaskListResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResetStickyTaskListResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResetStickyTaskListResponse) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a ResetStickyTaskListResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResetStickyTaskListResponse struct could not be encoded. func (v *ResetStickyTaskListResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a ResetStickyTaskListResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResetStickyTaskListResponse struct could not be generated from the wire // representation. func (v *ResetStickyTaskListResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResetStickyTaskListResponse // struct. func (v *ResetStickyTaskListResponse) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("ResetStickyTaskListResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ResetStickyTaskListResponse match the // provided ResetStickyTaskListResponse. // // This function performs a deep comparison. func (v *ResetStickyTaskListResponse) Equals(rhs *ResetStickyTaskListResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResetStickyTaskListResponse. func (v *ResetStickyTaskListResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type ResetWorkflowExecutionRequest struct { Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Reason *string `json:"reason,omitempty"` DecisionFinishEventId *int64 `json:"decisionFinishEventId,omitempty"` RequestId *string `json:"requestId,omitempty"` SkipSignalReapply *bool `json:"skipSignalReapply,omitempty"` } // ToWire translates a ResetWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResetWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.DecisionFinishEventId != nil { w, err = wire.NewValueI64(*(v.DecisionFinishEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.SkipSignalReapply != nil { w, err = wire.NewValueBool(*(v.SkipSignalReapply)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ResetWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResetWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResetWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResetWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionFinishEventId = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.SkipSignalReapply = &x if err != nil { return err } } } } return nil } // Encode serializes a ResetWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResetWorkflowExecutionRequest struct could not be encoded. func (v *ResetWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionFinishEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionFinishEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SkipSignalReapply != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.SkipSignalReapply)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ResetWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResetWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *ResetWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionFinishEventId = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.SkipSignalReapply = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResetWorkflowExecutionRequest // struct. func (v *ResetWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.DecisionFinishEventId != nil { fields[i] = fmt.Sprintf("DecisionFinishEventId: %v", *(v.DecisionFinishEventId)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } if v.SkipSignalReapply != nil { fields[i] = fmt.Sprintf("SkipSignalReapply: %v", *(v.SkipSignalReapply)) i++ } return fmt.Sprintf("ResetWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ResetWorkflowExecutionRequest match the // provided ResetWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *ResetWorkflowExecutionRequest) Equals(rhs *ResetWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !_I64_EqualsPtr(v.DecisionFinishEventId, rhs.DecisionFinishEventId) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } if !_Bool_EqualsPtr(v.SkipSignalReapply, rhs.SkipSignalReapply) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResetWorkflowExecutionRequest. func (v *ResetWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.DecisionFinishEventId != nil { enc.AddInt64("decisionFinishEventId", *v.DecisionFinishEventId) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } if v.SkipSignalReapply != nil { enc.AddBool("skipSignalReapply", *v.SkipSignalReapply) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ResetWorkflowExecutionRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ResetWorkflowExecutionRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *ResetWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *ResetWorkflowExecutionRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *ResetWorkflowExecutionRequest) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *ResetWorkflowExecutionRequest) IsSetReason() bool { return v != nil && v.Reason != nil } // GetDecisionFinishEventId returns the value of DecisionFinishEventId if it is set or its // zero value if it is unset. func (v *ResetWorkflowExecutionRequest) GetDecisionFinishEventId() (o int64) { if v != nil && v.DecisionFinishEventId != nil { return *v.DecisionFinishEventId } return } // IsSetDecisionFinishEventId returns true if DecisionFinishEventId is not nil. func (v *ResetWorkflowExecutionRequest) IsSetDecisionFinishEventId() bool { return v != nil && v.DecisionFinishEventId != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *ResetWorkflowExecutionRequest) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *ResetWorkflowExecutionRequest) IsSetRequestId() bool { return v != nil && v.RequestId != nil } // GetSkipSignalReapply returns the value of SkipSignalReapply if it is set or its // zero value if it is unset. func (v *ResetWorkflowExecutionRequest) GetSkipSignalReapply() (o bool) { if v != nil && v.SkipSignalReapply != nil { return *v.SkipSignalReapply } return } // IsSetSkipSignalReapply returns true if SkipSignalReapply is not nil. func (v *ResetWorkflowExecutionRequest) IsSetSkipSignalReapply() bool { return v != nil && v.SkipSignalReapply != nil } type ResetWorkflowExecutionResponse struct { RunId *string `json:"runId,omitempty"` } // ToWire translates a ResetWorkflowExecutionResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ResetWorkflowExecutionResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ResetWorkflowExecutionResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ResetWorkflowExecutionResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ResetWorkflowExecutionResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ResetWorkflowExecutionResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } } } return nil } // Encode serializes a ResetWorkflowExecutionResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ResetWorkflowExecutionResponse struct could not be encoded. func (v *ResetWorkflowExecutionResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ResetWorkflowExecutionResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ResetWorkflowExecutionResponse struct could not be generated from the wire // representation. func (v *ResetWorkflowExecutionResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ResetWorkflowExecutionResponse // struct. func (v *ResetWorkflowExecutionResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } return fmt.Sprintf("ResetWorkflowExecutionResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ResetWorkflowExecutionResponse match the // provided ResetWorkflowExecutionResponse. // // This function performs a deep comparison. func (v *ResetWorkflowExecutionResponse) Equals(rhs *ResetWorkflowExecutionResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ResetWorkflowExecutionResponse. func (v *ResetWorkflowExecutionResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.RunId != nil { enc.AddString("runId", *v.RunId) } return err } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *ResetWorkflowExecutionResponse) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *ResetWorkflowExecutionResponse) IsSetRunId() bool { return v != nil && v.RunId != nil } type RespondActivityTaskCanceledByIDRequest struct { Domain *string `json:"domain,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID *string `json:"runID,omitempty"` ActivityID *string `json:"activityID,omitempty"` Details []byte `json:"details,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a RespondActivityTaskCanceledByIDRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondActivityTaskCanceledByIDRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueString(*(v.RunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ActivityID != nil { w, err = wire.NewValueString(*(v.ActivityID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RespondActivityTaskCanceledByIDRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondActivityTaskCanceledByIDRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondActivityTaskCanceledByIDRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondActivityTaskCanceledByIDRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a RespondActivityTaskCanceledByIDRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondActivityTaskCanceledByIDRequest struct could not be encoded. func (v *RespondActivityTaskCanceledByIDRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RespondActivityTaskCanceledByIDRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondActivityTaskCanceledByIDRequest struct could not be generated from the wire // representation. func (v *RespondActivityTaskCanceledByIDRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondActivityTaskCanceledByIDRequest // struct. func (v *RespondActivityTaskCanceledByIDRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", *(v.RunID)) i++ } if v.ActivityID != nil { fields[i] = fmt.Sprintf("ActivityID: %v", *(v.ActivityID)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("RespondActivityTaskCanceledByIDRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondActivityTaskCanceledByIDRequest match the // provided RespondActivityTaskCanceledByIDRequest. // // This function performs a deep comparison. func (v *RespondActivityTaskCanceledByIDRequest) Equals(rhs *RespondActivityTaskCanceledByIDRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !_String_EqualsPtr(v.RunID, rhs.RunID) { return false } if !_String_EqualsPtr(v.ActivityID, rhs.ActivityID) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondActivityTaskCanceledByIDRequest. func (v *RespondActivityTaskCanceledByIDRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", *v.RunID) } if v.ActivityID != nil { enc.AddString("activityID", *v.ActivityID) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledByIDRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *RespondActivityTaskCanceledByIDRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledByIDRequest) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *RespondActivityTaskCanceledByIDRequest) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledByIDRequest) GetRunID() (o string) { if v != nil && v.RunID != nil { return *v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *RespondActivityTaskCanceledByIDRequest) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetActivityID returns the value of ActivityID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledByIDRequest) GetActivityID() (o string) { if v != nil && v.ActivityID != nil { return *v.ActivityID } return } // IsSetActivityID returns true if ActivityID is not nil. func (v *RespondActivityTaskCanceledByIDRequest) IsSetActivityID() bool { return v != nil && v.ActivityID != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledByIDRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *RespondActivityTaskCanceledByIDRequest) IsSetDetails() bool { return v != nil && v.Details != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledByIDRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RespondActivityTaskCanceledByIDRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } type RespondActivityTaskCanceledRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Details []byte `json:"details,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a RespondActivityTaskCanceledRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondActivityTaskCanceledRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RespondActivityTaskCanceledRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondActivityTaskCanceledRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondActivityTaskCanceledRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondActivityTaskCanceledRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a RespondActivityTaskCanceledRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondActivityTaskCanceledRequest struct could not be encoded. func (v *RespondActivityTaskCanceledRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RespondActivityTaskCanceledRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondActivityTaskCanceledRequest struct could not be generated from the wire // representation. func (v *RespondActivityTaskCanceledRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondActivityTaskCanceledRequest // struct. func (v *RespondActivityTaskCanceledRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("RespondActivityTaskCanceledRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondActivityTaskCanceledRequest match the // provided RespondActivityTaskCanceledRequest. // // This function performs a deep comparison. func (v *RespondActivityTaskCanceledRequest) Equals(rhs *RespondActivityTaskCanceledRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondActivityTaskCanceledRequest. func (v *RespondActivityTaskCanceledRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledRequest) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *RespondActivityTaskCanceledRequest) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *RespondActivityTaskCanceledRequest) IsSetDetails() bool { return v != nil && v.Details != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCanceledRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RespondActivityTaskCanceledRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } type RespondActivityTaskCompletedByIDRequest struct { Domain *string `json:"domain,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID *string `json:"runID,omitempty"` ActivityID *string `json:"activityID,omitempty"` Result []byte `json:"result,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a RespondActivityTaskCompletedByIDRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondActivityTaskCompletedByIDRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueString(*(v.RunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ActivityID != nil { w, err = wire.NewValueString(*(v.ActivityID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Result != nil { w, err = wire.NewValueBinary(v.Result), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RespondActivityTaskCompletedByIDRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondActivityTaskCompletedByIDRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondActivityTaskCompletedByIDRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondActivityTaskCompletedByIDRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Result, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a RespondActivityTaskCompletedByIDRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondActivityTaskCompletedByIDRequest struct could not be encoded. func (v *RespondActivityTaskCompletedByIDRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Result != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Result); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RespondActivityTaskCompletedByIDRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondActivityTaskCompletedByIDRequest struct could not be generated from the wire // representation. func (v *RespondActivityTaskCompletedByIDRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Result, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondActivityTaskCompletedByIDRequest // struct. func (v *RespondActivityTaskCompletedByIDRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", *(v.RunID)) i++ } if v.ActivityID != nil { fields[i] = fmt.Sprintf("ActivityID: %v", *(v.ActivityID)) i++ } if v.Result != nil { fields[i] = fmt.Sprintf("Result: %v", v.Result) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("RespondActivityTaskCompletedByIDRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondActivityTaskCompletedByIDRequest match the // provided RespondActivityTaskCompletedByIDRequest. // // This function performs a deep comparison. func (v *RespondActivityTaskCompletedByIDRequest) Equals(rhs *RespondActivityTaskCompletedByIDRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !_String_EqualsPtr(v.RunID, rhs.RunID) { return false } if !_String_EqualsPtr(v.ActivityID, rhs.ActivityID) { return false } if !((v.Result == nil && rhs.Result == nil) || (v.Result != nil && rhs.Result != nil && bytes.Equal(v.Result, rhs.Result))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondActivityTaskCompletedByIDRequest. func (v *RespondActivityTaskCompletedByIDRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", *v.RunID) } if v.ActivityID != nil { enc.AddString("activityID", *v.ActivityID) } if v.Result != nil { enc.AddString("result", base64.StdEncoding.EncodeToString(v.Result)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedByIDRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *RespondActivityTaskCompletedByIDRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedByIDRequest) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *RespondActivityTaskCompletedByIDRequest) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedByIDRequest) GetRunID() (o string) { if v != nil && v.RunID != nil { return *v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *RespondActivityTaskCompletedByIDRequest) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetActivityID returns the value of ActivityID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedByIDRequest) GetActivityID() (o string) { if v != nil && v.ActivityID != nil { return *v.ActivityID } return } // IsSetActivityID returns true if ActivityID is not nil. func (v *RespondActivityTaskCompletedByIDRequest) IsSetActivityID() bool { return v != nil && v.ActivityID != nil } // GetResult returns the value of Result if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedByIDRequest) GetResult() (o []byte) { if v != nil && v.Result != nil { return v.Result } return } // IsSetResult returns true if Result is not nil. func (v *RespondActivityTaskCompletedByIDRequest) IsSetResult() bool { return v != nil && v.Result != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedByIDRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RespondActivityTaskCompletedByIDRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } type RespondActivityTaskCompletedRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Result []byte `json:"result,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a RespondActivityTaskCompletedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondActivityTaskCompletedRequest) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Result != nil { w, err = wire.NewValueBinary(v.Result), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RespondActivityTaskCompletedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondActivityTaskCompletedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondActivityTaskCompletedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondActivityTaskCompletedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Result, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a RespondActivityTaskCompletedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondActivityTaskCompletedRequest struct could not be encoded. func (v *RespondActivityTaskCompletedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Result != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Result); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RespondActivityTaskCompletedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondActivityTaskCompletedRequest struct could not be generated from the wire // representation. func (v *RespondActivityTaskCompletedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Result, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondActivityTaskCompletedRequest // struct. func (v *RespondActivityTaskCompletedRequest) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } if v.Result != nil { fields[i] = fmt.Sprintf("Result: %v", v.Result) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("RespondActivityTaskCompletedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondActivityTaskCompletedRequest match the // provided RespondActivityTaskCompletedRequest. // // This function performs a deep comparison. func (v *RespondActivityTaskCompletedRequest) Equals(rhs *RespondActivityTaskCompletedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } if !((v.Result == nil && rhs.Result == nil) || (v.Result != nil && rhs.Result != nil && bytes.Equal(v.Result, rhs.Result))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondActivityTaskCompletedRequest. func (v *RespondActivityTaskCompletedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } if v.Result != nil { enc.AddString("result", base64.StdEncoding.EncodeToString(v.Result)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedRequest) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *RespondActivityTaskCompletedRequest) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } // GetResult returns the value of Result if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedRequest) GetResult() (o []byte) { if v != nil && v.Result != nil { return v.Result } return } // IsSetResult returns true if Result is not nil. func (v *RespondActivityTaskCompletedRequest) IsSetResult() bool { return v != nil && v.Result != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RespondActivityTaskCompletedRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RespondActivityTaskCompletedRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } type RespondActivityTaskFailedByIDRequest struct { Domain *string `json:"domain,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID *string `json:"runID,omitempty"` ActivityID *string `json:"activityID,omitempty"` Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a RespondActivityTaskFailedByIDRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondActivityTaskFailedByIDRequest) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueString(*(v.RunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ActivityID != nil { w, err = wire.NewValueString(*(v.ActivityID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RespondActivityTaskFailedByIDRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondActivityTaskFailedByIDRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondActivityTaskFailedByIDRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondActivityTaskFailedByIDRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunID = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a RespondActivityTaskFailedByIDRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondActivityTaskFailedByIDRequest struct could not be encoded. func (v *RespondActivityTaskFailedByIDRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RespondActivityTaskFailedByIDRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondActivityTaskFailedByIDRequest struct could not be generated from the wire // representation. func (v *RespondActivityTaskFailedByIDRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunID = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondActivityTaskFailedByIDRequest // struct. func (v *RespondActivityTaskFailedByIDRequest) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", *(v.RunID)) i++ } if v.ActivityID != nil { fields[i] = fmt.Sprintf("ActivityID: %v", *(v.ActivityID)) i++ } if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("RespondActivityTaskFailedByIDRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondActivityTaskFailedByIDRequest match the // provided RespondActivityTaskFailedByIDRequest. // // This function performs a deep comparison. func (v *RespondActivityTaskFailedByIDRequest) Equals(rhs *RespondActivityTaskFailedByIDRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !_String_EqualsPtr(v.RunID, rhs.RunID) { return false } if !_String_EqualsPtr(v.ActivityID, rhs.ActivityID) { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondActivityTaskFailedByIDRequest. func (v *RespondActivityTaskFailedByIDRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", *v.RunID) } if v.ActivityID != nil { enc.AddString("activityID", *v.ActivityID) } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedByIDRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *RespondActivityTaskFailedByIDRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedByIDRequest) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *RespondActivityTaskFailedByIDRequest) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedByIDRequest) GetRunID() (o string) { if v != nil && v.RunID != nil { return *v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *RespondActivityTaskFailedByIDRequest) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetActivityID returns the value of ActivityID if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedByIDRequest) GetActivityID() (o string) { if v != nil && v.ActivityID != nil { return *v.ActivityID } return } // IsSetActivityID returns true if ActivityID is not nil. func (v *RespondActivityTaskFailedByIDRequest) IsSetActivityID() bool { return v != nil && v.ActivityID != nil } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedByIDRequest) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *RespondActivityTaskFailedByIDRequest) IsSetReason() bool { return v != nil && v.Reason != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedByIDRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *RespondActivityTaskFailedByIDRequest) IsSetDetails() bool { return v != nil && v.Details != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedByIDRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RespondActivityTaskFailedByIDRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } type RespondActivityTaskFailedRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a RespondActivityTaskFailedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondActivityTaskFailedRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RespondActivityTaskFailedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondActivityTaskFailedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondActivityTaskFailedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondActivityTaskFailedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a RespondActivityTaskFailedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondActivityTaskFailedRequest struct could not be encoded. func (v *RespondActivityTaskFailedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RespondActivityTaskFailedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondActivityTaskFailedRequest struct could not be generated from the wire // representation. func (v *RespondActivityTaskFailedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondActivityTaskFailedRequest // struct. func (v *RespondActivityTaskFailedRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("RespondActivityTaskFailedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondActivityTaskFailedRequest match the // provided RespondActivityTaskFailedRequest. // // This function performs a deep comparison. func (v *RespondActivityTaskFailedRequest) Equals(rhs *RespondActivityTaskFailedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondActivityTaskFailedRequest. func (v *RespondActivityTaskFailedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedRequest) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *RespondActivityTaskFailedRequest) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedRequest) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *RespondActivityTaskFailedRequest) IsSetReason() bool { return v != nil && v.Reason != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *RespondActivityTaskFailedRequest) IsSetDetails() bool { return v != nil && v.Details != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RespondActivityTaskFailedRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RespondActivityTaskFailedRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } type RespondCrossClusterTasksCompletedRequest struct { ShardID *int32 `json:"shardID,omitempty"` TargetCluster *string `json:"targetCluster,omitempty"` TaskResponses []*CrossClusterTaskResponse `json:"taskResponses,omitempty"` FetchNewTasks *bool `json:"fetchNewTasks,omitempty"` } type _List_CrossClusterTaskResponse_ValueList []*CrossClusterTaskResponse func (v _List_CrossClusterTaskResponse_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*CrossClusterTaskResponse', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_CrossClusterTaskResponse_ValueList) Size() int { return len(v) } func (_List_CrossClusterTaskResponse_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_CrossClusterTaskResponse_ValueList) Close() {} // ToWire translates a RespondCrossClusterTasksCompletedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondCrossClusterTasksCompletedRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.ShardID != nil { w, err = wire.NewValueI32(*(v.ShardID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TargetCluster != nil { w, err = wire.NewValueString(*(v.TargetCluster)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TaskResponses != nil { w, err = wire.NewValueList(_List_CrossClusterTaskResponse_ValueList(v.TaskResponses)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.FetchNewTasks != nil { w, err = wire.NewValueBool(*(v.FetchNewTasks)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _CrossClusterTaskResponse_Read(w wire.Value) (*CrossClusterTaskResponse, error) { var v CrossClusterTaskResponse err := v.FromWire(w) return &v, err } func _List_CrossClusterTaskResponse_Read(l wire.ValueList) ([]*CrossClusterTaskResponse, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*CrossClusterTaskResponse, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _CrossClusterTaskResponse_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a RespondCrossClusterTasksCompletedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondCrossClusterTasksCompletedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondCrossClusterTasksCompletedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondCrossClusterTasksCompletedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ShardID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetCluster = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TList { v.TaskResponses, err = _List_CrossClusterTaskResponse_Read(field.Value.GetList()) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.FetchNewTasks = &x if err != nil { return err } } } } return nil } func _List_CrossClusterTaskResponse_Encode(val []*CrossClusterTaskResponse, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*CrossClusterTaskResponse', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a RespondCrossClusterTasksCompletedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondCrossClusterTasksCompletedRequest struct could not be encoded. func (v *RespondCrossClusterTasksCompletedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ShardID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ShardID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetCluster != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetCluster)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskResponses != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TList}); err != nil { return err } if err := _List_CrossClusterTaskResponse_Encode(v.TaskResponses, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FetchNewTasks != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.FetchNewTasks)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _CrossClusterTaskResponse_Decode(sr stream.Reader) (*CrossClusterTaskResponse, error) { var v CrossClusterTaskResponse err := v.Decode(sr) return &v, err } func _List_CrossClusterTaskResponse_Decode(sr stream.Reader) ([]*CrossClusterTaskResponse, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*CrossClusterTaskResponse, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _CrossClusterTaskResponse_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a RespondCrossClusterTasksCompletedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondCrossClusterTasksCompletedRequest struct could not be generated from the wire // representation. func (v *RespondCrossClusterTasksCompletedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ShardID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetCluster = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TList: v.TaskResponses, err = _List_CrossClusterTaskResponse_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.FetchNewTasks = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondCrossClusterTasksCompletedRequest // struct. func (v *RespondCrossClusterTasksCompletedRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.ShardID != nil { fields[i] = fmt.Sprintf("ShardID: %v", *(v.ShardID)) i++ } if v.TargetCluster != nil { fields[i] = fmt.Sprintf("TargetCluster: %v", *(v.TargetCluster)) i++ } if v.TaskResponses != nil { fields[i] = fmt.Sprintf("TaskResponses: %v", v.TaskResponses) i++ } if v.FetchNewTasks != nil { fields[i] = fmt.Sprintf("FetchNewTasks: %v", *(v.FetchNewTasks)) i++ } return fmt.Sprintf("RespondCrossClusterTasksCompletedRequest{%v}", strings.Join(fields[:i], ", ")) } func _List_CrossClusterTaskResponse_Equals(lhs, rhs []*CrossClusterTaskResponse) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this RespondCrossClusterTasksCompletedRequest match the // provided RespondCrossClusterTasksCompletedRequest. // // This function performs a deep comparison. func (v *RespondCrossClusterTasksCompletedRequest) Equals(rhs *RespondCrossClusterTasksCompletedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.ShardID, rhs.ShardID) { return false } if !_String_EqualsPtr(v.TargetCluster, rhs.TargetCluster) { return false } if !((v.TaskResponses == nil && rhs.TaskResponses == nil) || (v.TaskResponses != nil && rhs.TaskResponses != nil && _List_CrossClusterTaskResponse_Equals(v.TaskResponses, rhs.TaskResponses))) { return false } if !_Bool_EqualsPtr(v.FetchNewTasks, rhs.FetchNewTasks) { return false } return true } type _List_CrossClusterTaskResponse_Zapper []*CrossClusterTaskResponse // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_CrossClusterTaskResponse_Zapper. func (l _List_CrossClusterTaskResponse_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondCrossClusterTasksCompletedRequest. func (v *RespondCrossClusterTasksCompletedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ShardID != nil { enc.AddInt32("shardID", *v.ShardID) } if v.TargetCluster != nil { enc.AddString("targetCluster", *v.TargetCluster) } if v.TaskResponses != nil { err = multierr.Append(err, enc.AddArray("taskResponses", (_List_CrossClusterTaskResponse_Zapper)(v.TaskResponses))) } if v.FetchNewTasks != nil { enc.AddBool("fetchNewTasks", *v.FetchNewTasks) } return err } // GetShardID returns the value of ShardID if it is set or its // zero value if it is unset. func (v *RespondCrossClusterTasksCompletedRequest) GetShardID() (o int32) { if v != nil && v.ShardID != nil { return *v.ShardID } return } // IsSetShardID returns true if ShardID is not nil. func (v *RespondCrossClusterTasksCompletedRequest) IsSetShardID() bool { return v != nil && v.ShardID != nil } // GetTargetCluster returns the value of TargetCluster if it is set or its // zero value if it is unset. func (v *RespondCrossClusterTasksCompletedRequest) GetTargetCluster() (o string) { if v != nil && v.TargetCluster != nil { return *v.TargetCluster } return } // IsSetTargetCluster returns true if TargetCluster is not nil. func (v *RespondCrossClusterTasksCompletedRequest) IsSetTargetCluster() bool { return v != nil && v.TargetCluster != nil } // GetTaskResponses returns the value of TaskResponses if it is set or its // zero value if it is unset. func (v *RespondCrossClusterTasksCompletedRequest) GetTaskResponses() (o []*CrossClusterTaskResponse) { if v != nil && v.TaskResponses != nil { return v.TaskResponses } return } // IsSetTaskResponses returns true if TaskResponses is not nil. func (v *RespondCrossClusterTasksCompletedRequest) IsSetTaskResponses() bool { return v != nil && v.TaskResponses != nil } // GetFetchNewTasks returns the value of FetchNewTasks if it is set or its // zero value if it is unset. func (v *RespondCrossClusterTasksCompletedRequest) GetFetchNewTasks() (o bool) { if v != nil && v.FetchNewTasks != nil { return *v.FetchNewTasks } return } // IsSetFetchNewTasks returns true if FetchNewTasks is not nil. func (v *RespondCrossClusterTasksCompletedRequest) IsSetFetchNewTasks() bool { return v != nil && v.FetchNewTasks != nil } type RespondCrossClusterTasksCompletedResponse struct { Tasks []*CrossClusterTaskRequest `json:"tasks,omitempty"` } // ToWire translates a RespondCrossClusterTasksCompletedResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondCrossClusterTasksCompletedResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Tasks != nil { w, err = wire.NewValueList(_List_CrossClusterTaskRequest_ValueList(v.Tasks)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RespondCrossClusterTasksCompletedResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondCrossClusterTasksCompletedResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondCrossClusterTasksCompletedResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondCrossClusterTasksCompletedResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.Tasks, err = _List_CrossClusterTaskRequest_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } // Encode serializes a RespondCrossClusterTasksCompletedResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondCrossClusterTasksCompletedResponse struct could not be encoded. func (v *RespondCrossClusterTasksCompletedResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Tasks != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_CrossClusterTaskRequest_Encode(v.Tasks, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RespondCrossClusterTasksCompletedResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondCrossClusterTasksCompletedResponse struct could not be generated from the wire // representation. func (v *RespondCrossClusterTasksCompletedResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.Tasks, err = _List_CrossClusterTaskRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondCrossClusterTasksCompletedResponse // struct. func (v *RespondCrossClusterTasksCompletedResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Tasks != nil { fields[i] = fmt.Sprintf("Tasks: %v", v.Tasks) i++ } return fmt.Sprintf("RespondCrossClusterTasksCompletedResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondCrossClusterTasksCompletedResponse match the // provided RespondCrossClusterTasksCompletedResponse. // // This function performs a deep comparison. func (v *RespondCrossClusterTasksCompletedResponse) Equals(rhs *RespondCrossClusterTasksCompletedResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Tasks == nil && rhs.Tasks == nil) || (v.Tasks != nil && rhs.Tasks != nil && _List_CrossClusterTaskRequest_Equals(v.Tasks, rhs.Tasks))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondCrossClusterTasksCompletedResponse. func (v *RespondCrossClusterTasksCompletedResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Tasks != nil { err = multierr.Append(err, enc.AddArray("tasks", (_List_CrossClusterTaskRequest_Zapper)(v.Tasks))) } return err } // GetTasks returns the value of Tasks if it is set or its // zero value if it is unset. func (v *RespondCrossClusterTasksCompletedResponse) GetTasks() (o []*CrossClusterTaskRequest) { if v != nil && v.Tasks != nil { return v.Tasks } return } // IsSetTasks returns true if Tasks is not nil. func (v *RespondCrossClusterTasksCompletedResponse) IsSetTasks() bool { return v != nil && v.Tasks != nil } type RespondDecisionTaskCompletedRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Decisions []*Decision `json:"decisions,omitempty"` ExecutionContext []byte `json:"executionContext,omitempty"` Identity *string `json:"identity,omitempty"` StickyAttributes *StickyExecutionAttributes `json:"stickyAttributes,omitempty"` ReturnNewDecisionTask *bool `json:"returnNewDecisionTask,omitempty"` ForceCreateNewDecisionTask *bool `json:"forceCreateNewDecisionTask,omitempty"` BinaryChecksum *string `json:"binaryChecksum,omitempty"` QueryResults map[string]*WorkflowQueryResult `json:"queryResults,omitempty"` } type _List_Decision_ValueList []*Decision func (v _List_Decision_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*Decision', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_Decision_ValueList) Size() int { return len(v) } func (_List_Decision_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_Decision_ValueList) Close() {} type _Map_String_WorkflowQueryResult_MapItemList map[string]*WorkflowQueryResult func (m _Map_String_WorkflowQueryResult_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*WorkflowQueryResult', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_WorkflowQueryResult_MapItemList) Size() int { return len(m) } func (_Map_String_WorkflowQueryResult_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_WorkflowQueryResult_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_WorkflowQueryResult_MapItemList) Close() {} // ToWire translates a RespondDecisionTaskCompletedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondDecisionTaskCompletedRequest) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Decisions != nil { w, err = wire.NewValueList(_List_Decision_ValueList(v.Decisions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ExecutionContext != nil { w, err = wire.NewValueBinary(v.ExecutionContext), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.StickyAttributes != nil { w, err = v.StickyAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ReturnNewDecisionTask != nil { w, err = wire.NewValueBool(*(v.ReturnNewDecisionTask)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.ForceCreateNewDecisionTask != nil { w, err = wire.NewValueBool(*(v.ForceCreateNewDecisionTask)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.BinaryChecksum != nil { w, err = wire.NewValueString(*(v.BinaryChecksum)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.QueryResults != nil { w, err = wire.NewValueMap(_Map_String_WorkflowQueryResult_MapItemList(v.QueryResults)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Decision_Read(w wire.Value) (*Decision, error) { var v Decision err := v.FromWire(w) return &v, err } func _List_Decision_Read(l wire.ValueList) ([]*Decision, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*Decision, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _Decision_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } func _StickyExecutionAttributes_Read(w wire.Value) (*StickyExecutionAttributes, error) { var v StickyExecutionAttributes err := v.FromWire(w) return &v, err } func _WorkflowQueryResult_Read(w wire.Value) (*WorkflowQueryResult, error) { var v WorkflowQueryResult err := v.FromWire(w) return &v, err } func _Map_String_WorkflowQueryResult_Read(m wire.MapItemList) (map[string]*WorkflowQueryResult, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*WorkflowQueryResult, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _WorkflowQueryResult_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a RespondDecisionTaskCompletedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondDecisionTaskCompletedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondDecisionTaskCompletedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondDecisionTaskCompletedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Decisions, err = _List_Decision_Read(field.Value.GetList()) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.ExecutionContext, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.StickyAttributes, err = _StickyExecutionAttributes_Read(field.Value) if err != nil { return err } } case 60: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ReturnNewDecisionTask = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ForceCreateNewDecisionTask = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BinaryChecksum = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TMap { v.QueryResults, err = _Map_String_WorkflowQueryResult_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _List_Decision_Encode(val []*Decision, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*Decision', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } func _Map_String_WorkflowQueryResult_Encode(val map[string]*WorkflowQueryResult, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*WorkflowQueryResult', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a RespondDecisionTaskCompletedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondDecisionTaskCompletedRequest struct could not be encoded. func (v *RespondDecisionTaskCompletedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Decisions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_Decision_Encode(v.Decisions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionContext != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.ExecutionContext); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.StickyAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReturnNewDecisionTask != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ReturnNewDecisionTask)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ForceCreateNewDecisionTask != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ForceCreateNewDecisionTask)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BinaryChecksum != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BinaryChecksum)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryResults != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TMap}); err != nil { return err } if err := _Map_String_WorkflowQueryResult_Encode(v.QueryResults, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _Decision_Decode(sr stream.Reader) (*Decision, error) { var v Decision err := v.Decode(sr) return &v, err } func _List_Decision_Decode(sr stream.Reader) ([]*Decision, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*Decision, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _Decision_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } func _StickyExecutionAttributes_Decode(sr stream.Reader) (*StickyExecutionAttributes, error) { var v StickyExecutionAttributes err := v.Decode(sr) return &v, err } func _WorkflowQueryResult_Decode(sr stream.Reader) (*WorkflowQueryResult, error) { var v WorkflowQueryResult err := v.Decode(sr) return &v, err } func _Map_String_WorkflowQueryResult_Decode(sr stream.Reader) (map[string]*WorkflowQueryResult, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*WorkflowQueryResult, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _WorkflowQueryResult_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a RespondDecisionTaskCompletedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondDecisionTaskCompletedRequest struct could not be generated from the wire // representation. func (v *RespondDecisionTaskCompletedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Decisions, err = _List_Decision_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.ExecutionContext, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.StickyAttributes, err = _StickyExecutionAttributes_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ReturnNewDecisionTask = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ForceCreateNewDecisionTask = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BinaryChecksum = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TMap: v.QueryResults, err = _Map_String_WorkflowQueryResult_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondDecisionTaskCompletedRequest // struct. func (v *RespondDecisionTaskCompletedRequest) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } if v.Decisions != nil { fields[i] = fmt.Sprintf("Decisions: %v", v.Decisions) i++ } if v.ExecutionContext != nil { fields[i] = fmt.Sprintf("ExecutionContext: %v", v.ExecutionContext) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.StickyAttributes != nil { fields[i] = fmt.Sprintf("StickyAttributes: %v", v.StickyAttributes) i++ } if v.ReturnNewDecisionTask != nil { fields[i] = fmt.Sprintf("ReturnNewDecisionTask: %v", *(v.ReturnNewDecisionTask)) i++ } if v.ForceCreateNewDecisionTask != nil { fields[i] = fmt.Sprintf("ForceCreateNewDecisionTask: %v", *(v.ForceCreateNewDecisionTask)) i++ } if v.BinaryChecksum != nil { fields[i] = fmt.Sprintf("BinaryChecksum: %v", *(v.BinaryChecksum)) i++ } if v.QueryResults != nil { fields[i] = fmt.Sprintf("QueryResults: %v", v.QueryResults) i++ } return fmt.Sprintf("RespondDecisionTaskCompletedRequest{%v}", strings.Join(fields[:i], ", ")) } func _List_Decision_Equals(lhs, rhs []*Decision) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } func _Map_String_WorkflowQueryResult_Equals(lhs, rhs map[string]*WorkflowQueryResult) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this RespondDecisionTaskCompletedRequest match the // provided RespondDecisionTaskCompletedRequest. // // This function performs a deep comparison. func (v *RespondDecisionTaskCompletedRequest) Equals(rhs *RespondDecisionTaskCompletedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } if !((v.Decisions == nil && rhs.Decisions == nil) || (v.Decisions != nil && rhs.Decisions != nil && _List_Decision_Equals(v.Decisions, rhs.Decisions))) { return false } if !((v.ExecutionContext == nil && rhs.ExecutionContext == nil) || (v.ExecutionContext != nil && rhs.ExecutionContext != nil && bytes.Equal(v.ExecutionContext, rhs.ExecutionContext))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !((v.StickyAttributes == nil && rhs.StickyAttributes == nil) || (v.StickyAttributes != nil && rhs.StickyAttributes != nil && v.StickyAttributes.Equals(rhs.StickyAttributes))) { return false } if !_Bool_EqualsPtr(v.ReturnNewDecisionTask, rhs.ReturnNewDecisionTask) { return false } if !_Bool_EqualsPtr(v.ForceCreateNewDecisionTask, rhs.ForceCreateNewDecisionTask) { return false } if !_String_EqualsPtr(v.BinaryChecksum, rhs.BinaryChecksum) { return false } if !((v.QueryResults == nil && rhs.QueryResults == nil) || (v.QueryResults != nil && rhs.QueryResults != nil && _Map_String_WorkflowQueryResult_Equals(v.QueryResults, rhs.QueryResults))) { return false } return true } type _List_Decision_Zapper []*Decision // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_Decision_Zapper. func (l _List_Decision_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } type _Map_String_WorkflowQueryResult_Zapper map[string]*WorkflowQueryResult // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_WorkflowQueryResult_Zapper. func (m _Map_String_WorkflowQueryResult_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondDecisionTaskCompletedRequest. func (v *RespondDecisionTaskCompletedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } if v.Decisions != nil { err = multierr.Append(err, enc.AddArray("decisions", (_List_Decision_Zapper)(v.Decisions))) } if v.ExecutionContext != nil { enc.AddString("executionContext", base64.StdEncoding.EncodeToString(v.ExecutionContext)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.StickyAttributes != nil { err = multierr.Append(err, enc.AddObject("stickyAttributes", v.StickyAttributes)) } if v.ReturnNewDecisionTask != nil { enc.AddBool("returnNewDecisionTask", *v.ReturnNewDecisionTask) } if v.ForceCreateNewDecisionTask != nil { enc.AddBool("forceCreateNewDecisionTask", *v.ForceCreateNewDecisionTask) } if v.BinaryChecksum != nil { enc.AddString("binaryChecksum", *v.BinaryChecksum) } if v.QueryResults != nil { err = multierr.Append(err, enc.AddObject("queryResults", (_Map_String_WorkflowQueryResult_Zapper)(v.QueryResults))) } return err } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } // GetDecisions returns the value of Decisions if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetDecisions() (o []*Decision) { if v != nil && v.Decisions != nil { return v.Decisions } return } // IsSetDecisions returns true if Decisions is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetDecisions() bool { return v != nil && v.Decisions != nil } // GetExecutionContext returns the value of ExecutionContext if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetExecutionContext() (o []byte) { if v != nil && v.ExecutionContext != nil { return v.ExecutionContext } return } // IsSetExecutionContext returns true if ExecutionContext is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetExecutionContext() bool { return v != nil && v.ExecutionContext != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetStickyAttributes returns the value of StickyAttributes if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetStickyAttributes() (o *StickyExecutionAttributes) { if v != nil && v.StickyAttributes != nil { return v.StickyAttributes } return } // IsSetStickyAttributes returns true if StickyAttributes is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetStickyAttributes() bool { return v != nil && v.StickyAttributes != nil } // GetReturnNewDecisionTask returns the value of ReturnNewDecisionTask if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetReturnNewDecisionTask() (o bool) { if v != nil && v.ReturnNewDecisionTask != nil { return *v.ReturnNewDecisionTask } return } // IsSetReturnNewDecisionTask returns true if ReturnNewDecisionTask is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetReturnNewDecisionTask() bool { return v != nil && v.ReturnNewDecisionTask != nil } // GetForceCreateNewDecisionTask returns the value of ForceCreateNewDecisionTask if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetForceCreateNewDecisionTask() (o bool) { if v != nil && v.ForceCreateNewDecisionTask != nil { return *v.ForceCreateNewDecisionTask } return } // IsSetForceCreateNewDecisionTask returns true if ForceCreateNewDecisionTask is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetForceCreateNewDecisionTask() bool { return v != nil && v.ForceCreateNewDecisionTask != nil } // GetBinaryChecksum returns the value of BinaryChecksum if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetBinaryChecksum() (o string) { if v != nil && v.BinaryChecksum != nil { return *v.BinaryChecksum } return } // IsSetBinaryChecksum returns true if BinaryChecksum is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetBinaryChecksum() bool { return v != nil && v.BinaryChecksum != nil } // GetQueryResults returns the value of QueryResults if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedRequest) GetQueryResults() (o map[string]*WorkflowQueryResult) { if v != nil && v.QueryResults != nil { return v.QueryResults } return } // IsSetQueryResults returns true if QueryResults is not nil. func (v *RespondDecisionTaskCompletedRequest) IsSetQueryResults() bool { return v != nil && v.QueryResults != nil } type RespondDecisionTaskCompletedResponse struct { DecisionTask *PollForDecisionTaskResponse `json:"decisionTask,omitempty"` ActivitiesToDispatchLocally map[string]*ActivityLocalDispatchInfo `json:"activitiesToDispatchLocally,omitempty"` } type _Map_String_ActivityLocalDispatchInfo_MapItemList map[string]*ActivityLocalDispatchInfo func (m _Map_String_ActivityLocalDispatchInfo_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*ActivityLocalDispatchInfo', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_ActivityLocalDispatchInfo_MapItemList) Size() int { return len(m) } func (_Map_String_ActivityLocalDispatchInfo_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_ActivityLocalDispatchInfo_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_ActivityLocalDispatchInfo_MapItemList) Close() {} // ToWire translates a RespondDecisionTaskCompletedResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondDecisionTaskCompletedResponse) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DecisionTask != nil { w, err = v.DecisionTask.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ActivitiesToDispatchLocally != nil { w, err = wire.NewValueMap(_Map_String_ActivityLocalDispatchInfo_MapItemList(v.ActivitiesToDispatchLocally)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _PollForDecisionTaskResponse_Read(w wire.Value) (*PollForDecisionTaskResponse, error) { var v PollForDecisionTaskResponse err := v.FromWire(w) return &v, err } func _ActivityLocalDispatchInfo_Read(w wire.Value) (*ActivityLocalDispatchInfo, error) { var v ActivityLocalDispatchInfo err := v.FromWire(w) return &v, err } func _Map_String_ActivityLocalDispatchInfo_Read(m wire.MapItemList) (map[string]*ActivityLocalDispatchInfo, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*ActivityLocalDispatchInfo, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _ActivityLocalDispatchInfo_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a RespondDecisionTaskCompletedResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondDecisionTaskCompletedResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondDecisionTaskCompletedResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondDecisionTaskCompletedResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.DecisionTask, err = _PollForDecisionTaskResponse_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TMap { v.ActivitiesToDispatchLocally, err = _Map_String_ActivityLocalDispatchInfo_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_ActivityLocalDispatchInfo_Encode(val map[string]*ActivityLocalDispatchInfo, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*ActivityLocalDispatchInfo', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a RespondDecisionTaskCompletedResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondDecisionTaskCompletedResponse struct could not be encoded. func (v *RespondDecisionTaskCompletedResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DecisionTask != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.DecisionTask.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivitiesToDispatchLocally != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TMap}); err != nil { return err } if err := _Map_String_ActivityLocalDispatchInfo_Encode(v.ActivitiesToDispatchLocally, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _PollForDecisionTaskResponse_Decode(sr stream.Reader) (*PollForDecisionTaskResponse, error) { var v PollForDecisionTaskResponse err := v.Decode(sr) return &v, err } func _ActivityLocalDispatchInfo_Decode(sr stream.Reader) (*ActivityLocalDispatchInfo, error) { var v ActivityLocalDispatchInfo err := v.Decode(sr) return &v, err } func _Map_String_ActivityLocalDispatchInfo_Decode(sr stream.Reader) (map[string]*ActivityLocalDispatchInfo, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*ActivityLocalDispatchInfo, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _ActivityLocalDispatchInfo_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a RespondDecisionTaskCompletedResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondDecisionTaskCompletedResponse struct could not be generated from the wire // representation. func (v *RespondDecisionTaskCompletedResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.DecisionTask, err = _PollForDecisionTaskResponse_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TMap: v.ActivitiesToDispatchLocally, err = _Map_String_ActivityLocalDispatchInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondDecisionTaskCompletedResponse // struct. func (v *RespondDecisionTaskCompletedResponse) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DecisionTask != nil { fields[i] = fmt.Sprintf("DecisionTask: %v", v.DecisionTask) i++ } if v.ActivitiesToDispatchLocally != nil { fields[i] = fmt.Sprintf("ActivitiesToDispatchLocally: %v", v.ActivitiesToDispatchLocally) i++ } return fmt.Sprintf("RespondDecisionTaskCompletedResponse{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_ActivityLocalDispatchInfo_Equals(lhs, rhs map[string]*ActivityLocalDispatchInfo) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this RespondDecisionTaskCompletedResponse match the // provided RespondDecisionTaskCompletedResponse. // // This function performs a deep comparison. func (v *RespondDecisionTaskCompletedResponse) Equals(rhs *RespondDecisionTaskCompletedResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DecisionTask == nil && rhs.DecisionTask == nil) || (v.DecisionTask != nil && rhs.DecisionTask != nil && v.DecisionTask.Equals(rhs.DecisionTask))) { return false } if !((v.ActivitiesToDispatchLocally == nil && rhs.ActivitiesToDispatchLocally == nil) || (v.ActivitiesToDispatchLocally != nil && rhs.ActivitiesToDispatchLocally != nil && _Map_String_ActivityLocalDispatchInfo_Equals(v.ActivitiesToDispatchLocally, rhs.ActivitiesToDispatchLocally))) { return false } return true } type _Map_String_ActivityLocalDispatchInfo_Zapper map[string]*ActivityLocalDispatchInfo // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_ActivityLocalDispatchInfo_Zapper. func (m _Map_String_ActivityLocalDispatchInfo_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondDecisionTaskCompletedResponse. func (v *RespondDecisionTaskCompletedResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DecisionTask != nil { err = multierr.Append(err, enc.AddObject("decisionTask", v.DecisionTask)) } if v.ActivitiesToDispatchLocally != nil { err = multierr.Append(err, enc.AddObject("activitiesToDispatchLocally", (_Map_String_ActivityLocalDispatchInfo_Zapper)(v.ActivitiesToDispatchLocally))) } return err } // GetDecisionTask returns the value of DecisionTask if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedResponse) GetDecisionTask() (o *PollForDecisionTaskResponse) { if v != nil && v.DecisionTask != nil { return v.DecisionTask } return } // IsSetDecisionTask returns true if DecisionTask is not nil. func (v *RespondDecisionTaskCompletedResponse) IsSetDecisionTask() bool { return v != nil && v.DecisionTask != nil } // GetActivitiesToDispatchLocally returns the value of ActivitiesToDispatchLocally if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskCompletedResponse) GetActivitiesToDispatchLocally() (o map[string]*ActivityLocalDispatchInfo) { if v != nil && v.ActivitiesToDispatchLocally != nil { return v.ActivitiesToDispatchLocally } return } // IsSetActivitiesToDispatchLocally returns true if ActivitiesToDispatchLocally is not nil. func (v *RespondDecisionTaskCompletedResponse) IsSetActivitiesToDispatchLocally() bool { return v != nil && v.ActivitiesToDispatchLocally != nil } type RespondDecisionTaskFailedRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Cause *DecisionTaskFailedCause `json:"cause,omitempty"` Details []byte `json:"details,omitempty"` Identity *string `json:"identity,omitempty"` BinaryChecksum *string `json:"binaryChecksum,omitempty"` } // ToWire translates a RespondDecisionTaskFailedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondDecisionTaskFailedRequest) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Cause != nil { w, err = v.Cause.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.BinaryChecksum != nil { w, err = wire.NewValueString(*(v.BinaryChecksum)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RespondDecisionTaskFailedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondDecisionTaskFailedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondDecisionTaskFailedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondDecisionTaskFailedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x DecisionTaskFailedCause x, err = _DecisionTaskFailedCause_Read(field.Value) v.Cause = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BinaryChecksum = &x if err != nil { return err } } } } return nil } // Encode serializes a RespondDecisionTaskFailedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondDecisionTaskFailedRequest struct could not be encoded. func (v *RespondDecisionTaskFailedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Cause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := v.Cause.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BinaryChecksum != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BinaryChecksum)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RespondDecisionTaskFailedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondDecisionTaskFailedRequest struct could not be generated from the wire // representation. func (v *RespondDecisionTaskFailedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x DecisionTaskFailedCause x, err = _DecisionTaskFailedCause_Decode(sr) v.Cause = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BinaryChecksum = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondDecisionTaskFailedRequest // struct. func (v *RespondDecisionTaskFailedRequest) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } if v.Cause != nil { fields[i] = fmt.Sprintf("Cause: %v", *(v.Cause)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.BinaryChecksum != nil { fields[i] = fmt.Sprintf("BinaryChecksum: %v", *(v.BinaryChecksum)) i++ } return fmt.Sprintf("RespondDecisionTaskFailedRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RespondDecisionTaskFailedRequest match the // provided RespondDecisionTaskFailedRequest. // // This function performs a deep comparison. func (v *RespondDecisionTaskFailedRequest) Equals(rhs *RespondDecisionTaskFailedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } if !_DecisionTaskFailedCause_EqualsPtr(v.Cause, rhs.Cause) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.BinaryChecksum, rhs.BinaryChecksum) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondDecisionTaskFailedRequest. func (v *RespondDecisionTaskFailedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } if v.Cause != nil { err = multierr.Append(err, enc.AddObject("cause", *v.Cause)) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.BinaryChecksum != nil { enc.AddString("binaryChecksum", *v.BinaryChecksum) } return err } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskFailedRequest) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *RespondDecisionTaskFailedRequest) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } // GetCause returns the value of Cause if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskFailedRequest) GetCause() (o DecisionTaskFailedCause) { if v != nil && v.Cause != nil { return *v.Cause } return } // IsSetCause returns true if Cause is not nil. func (v *RespondDecisionTaskFailedRequest) IsSetCause() bool { return v != nil && v.Cause != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskFailedRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *RespondDecisionTaskFailedRequest) IsSetDetails() bool { return v != nil && v.Details != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskFailedRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RespondDecisionTaskFailedRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetBinaryChecksum returns the value of BinaryChecksum if it is set or its // zero value if it is unset. func (v *RespondDecisionTaskFailedRequest) GetBinaryChecksum() (o string) { if v != nil && v.BinaryChecksum != nil { return *v.BinaryChecksum } return } // IsSetBinaryChecksum returns true if BinaryChecksum is not nil. func (v *RespondDecisionTaskFailedRequest) IsSetBinaryChecksum() bool { return v != nil && v.BinaryChecksum != nil } type RespondQueryTaskCompletedRequest struct { TaskToken []byte `json:"taskToken,omitempty"` CompletedType *QueryTaskCompletedType `json:"completedType,omitempty"` QueryResult []byte `json:"queryResult,omitempty"` ErrorMessage *string `json:"errorMessage,omitempty"` WorkerVersionInfo *WorkerVersionInfo `json:"workerVersionInfo,omitempty"` } // ToWire translates a RespondQueryTaskCompletedRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RespondQueryTaskCompletedRequest) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.TaskToken != nil { w, err = wire.NewValueBinary(v.TaskToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.CompletedType != nil { w, err = v.CompletedType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.QueryResult != nil { w, err = wire.NewValueBinary(v.QueryResult), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ErrorMessage != nil { w, err = wire.NewValueString(*(v.ErrorMessage)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.WorkerVersionInfo != nil { w, err = v.WorkerVersionInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryTaskCompletedType_Read(w wire.Value) (QueryTaskCompletedType, error) { var v QueryTaskCompletedType err := v.FromWire(w) return v, err } func _WorkerVersionInfo_Read(w wire.Value) (*WorkerVersionInfo, error) { var v WorkerVersionInfo err := v.FromWire(w) return &v, err } // FromWire deserializes a RespondQueryTaskCompletedRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RespondQueryTaskCompletedRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RespondQueryTaskCompletedRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RespondQueryTaskCompletedRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.TaskToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x QueryTaskCompletedType x, err = _QueryTaskCompletedType_Read(field.Value) v.CompletedType = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.QueryResult, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ErrorMessage = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TStruct { v.WorkerVersionInfo, err = _WorkerVersionInfo_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a RespondQueryTaskCompletedRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RespondQueryTaskCompletedRequest struct could not be encoded. func (v *RespondQueryTaskCompletedRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TaskToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompletedType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := v.CompletedType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryResult != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.QueryResult); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ErrorMessage != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ErrorMessage)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkerVersionInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TStruct}); err != nil { return err } if err := v.WorkerVersionInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryTaskCompletedType_Decode(sr stream.Reader) (QueryTaskCompletedType, error) { var v QueryTaskCompletedType err := v.Decode(sr) return v, err } func _WorkerVersionInfo_Decode(sr stream.Reader) (*WorkerVersionInfo, error) { var v WorkerVersionInfo err := v.Decode(sr) return &v, err } // Decode deserializes a RespondQueryTaskCompletedRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RespondQueryTaskCompletedRequest struct could not be generated from the wire // representation. func (v *RespondQueryTaskCompletedRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.TaskToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x QueryTaskCompletedType x, err = _QueryTaskCompletedType_Decode(sr) v.CompletedType = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.QueryResult, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ErrorMessage = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TStruct: v.WorkerVersionInfo, err = _WorkerVersionInfo_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RespondQueryTaskCompletedRequest // struct. func (v *RespondQueryTaskCompletedRequest) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.TaskToken != nil { fields[i] = fmt.Sprintf("TaskToken: %v", v.TaskToken) i++ } if v.CompletedType != nil { fields[i] = fmt.Sprintf("CompletedType: %v", *(v.CompletedType)) i++ } if v.QueryResult != nil { fields[i] = fmt.Sprintf("QueryResult: %v", v.QueryResult) i++ } if v.ErrorMessage != nil { fields[i] = fmt.Sprintf("ErrorMessage: %v", *(v.ErrorMessage)) i++ } if v.WorkerVersionInfo != nil { fields[i] = fmt.Sprintf("WorkerVersionInfo: %v", v.WorkerVersionInfo) i++ } return fmt.Sprintf("RespondQueryTaskCompletedRequest{%v}", strings.Join(fields[:i], ", ")) } func _QueryTaskCompletedType_EqualsPtr(lhs, rhs *QueryTaskCompletedType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this RespondQueryTaskCompletedRequest match the // provided RespondQueryTaskCompletedRequest. // // This function performs a deep comparison. func (v *RespondQueryTaskCompletedRequest) Equals(rhs *RespondQueryTaskCompletedRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskToken == nil && rhs.TaskToken == nil) || (v.TaskToken != nil && rhs.TaskToken != nil && bytes.Equal(v.TaskToken, rhs.TaskToken))) { return false } if !_QueryTaskCompletedType_EqualsPtr(v.CompletedType, rhs.CompletedType) { return false } if !((v.QueryResult == nil && rhs.QueryResult == nil) || (v.QueryResult != nil && rhs.QueryResult != nil && bytes.Equal(v.QueryResult, rhs.QueryResult))) { return false } if !_String_EqualsPtr(v.ErrorMessage, rhs.ErrorMessage) { return false } if !((v.WorkerVersionInfo == nil && rhs.WorkerVersionInfo == nil) || (v.WorkerVersionInfo != nil && rhs.WorkerVersionInfo != nil && v.WorkerVersionInfo.Equals(rhs.WorkerVersionInfo))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RespondQueryTaskCompletedRequest. func (v *RespondQueryTaskCompletedRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskToken != nil { enc.AddString("taskToken", base64.StdEncoding.EncodeToString(v.TaskToken)) } if v.CompletedType != nil { err = multierr.Append(err, enc.AddObject("completedType", *v.CompletedType)) } if v.QueryResult != nil { enc.AddString("queryResult", base64.StdEncoding.EncodeToString(v.QueryResult)) } if v.ErrorMessage != nil { enc.AddString("errorMessage", *v.ErrorMessage) } if v.WorkerVersionInfo != nil { err = multierr.Append(err, enc.AddObject("workerVersionInfo", v.WorkerVersionInfo)) } return err } // GetTaskToken returns the value of TaskToken if it is set or its // zero value if it is unset. func (v *RespondQueryTaskCompletedRequest) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // IsSetTaskToken returns true if TaskToken is not nil. func (v *RespondQueryTaskCompletedRequest) IsSetTaskToken() bool { return v != nil && v.TaskToken != nil } // GetCompletedType returns the value of CompletedType if it is set or its // zero value if it is unset. func (v *RespondQueryTaskCompletedRequest) GetCompletedType() (o QueryTaskCompletedType) { if v != nil && v.CompletedType != nil { return *v.CompletedType } return } // IsSetCompletedType returns true if CompletedType is not nil. func (v *RespondQueryTaskCompletedRequest) IsSetCompletedType() bool { return v != nil && v.CompletedType != nil } // GetQueryResult returns the value of QueryResult if it is set or its // zero value if it is unset. func (v *RespondQueryTaskCompletedRequest) GetQueryResult() (o []byte) { if v != nil && v.QueryResult != nil { return v.QueryResult } return } // IsSetQueryResult returns true if QueryResult is not nil. func (v *RespondQueryTaskCompletedRequest) IsSetQueryResult() bool { return v != nil && v.QueryResult != nil } // GetErrorMessage returns the value of ErrorMessage if it is set or its // zero value if it is unset. func (v *RespondQueryTaskCompletedRequest) GetErrorMessage() (o string) { if v != nil && v.ErrorMessage != nil { return *v.ErrorMessage } return } // IsSetErrorMessage returns true if ErrorMessage is not nil. func (v *RespondQueryTaskCompletedRequest) IsSetErrorMessage() bool { return v != nil && v.ErrorMessage != nil } // GetWorkerVersionInfo returns the value of WorkerVersionInfo if it is set or its // zero value if it is unset. func (v *RespondQueryTaskCompletedRequest) GetWorkerVersionInfo() (o *WorkerVersionInfo) { if v != nil && v.WorkerVersionInfo != nil { return v.WorkerVersionInfo } return } // IsSetWorkerVersionInfo returns true if WorkerVersionInfo is not nil. func (v *RespondQueryTaskCompletedRequest) IsSetWorkerVersionInfo() bool { return v != nil && v.WorkerVersionInfo != nil } type RestartWorkflowExecutionRequest struct { Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Reason *string `json:"reason,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a RestartWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RestartWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RestartWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RestartWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RestartWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RestartWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a RestartWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RestartWorkflowExecutionRequest struct could not be encoded. func (v *RestartWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RestartWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RestartWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *RestartWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RestartWorkflowExecutionRequest // struct. func (v *RestartWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("RestartWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RestartWorkflowExecutionRequest match the // provided RestartWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *RestartWorkflowExecutionRequest) Equals(rhs *RestartWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RestartWorkflowExecutionRequest. func (v *RestartWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *RestartWorkflowExecutionRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *RestartWorkflowExecutionRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *RestartWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *RestartWorkflowExecutionRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *RestartWorkflowExecutionRequest) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *RestartWorkflowExecutionRequest) IsSetReason() bool { return v != nil && v.Reason != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *RestartWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *RestartWorkflowExecutionRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } type RestartWorkflowExecutionResponse struct { RunId *string `json:"runId,omitempty"` } // ToWire translates a RestartWorkflowExecutionResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RestartWorkflowExecutionResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RestartWorkflowExecutionResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RestartWorkflowExecutionResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RestartWorkflowExecutionResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RestartWorkflowExecutionResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } } } return nil } // Encode serializes a RestartWorkflowExecutionResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RestartWorkflowExecutionResponse struct could not be encoded. func (v *RestartWorkflowExecutionResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RestartWorkflowExecutionResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RestartWorkflowExecutionResponse struct could not be generated from the wire // representation. func (v *RestartWorkflowExecutionResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RestartWorkflowExecutionResponse // struct. func (v *RestartWorkflowExecutionResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } return fmt.Sprintf("RestartWorkflowExecutionResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RestartWorkflowExecutionResponse match the // provided RestartWorkflowExecutionResponse. // // This function performs a deep comparison. func (v *RestartWorkflowExecutionResponse) Equals(rhs *RestartWorkflowExecutionResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RestartWorkflowExecutionResponse. func (v *RestartWorkflowExecutionResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.RunId != nil { enc.AddString("runId", *v.RunId) } return err } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *RestartWorkflowExecutionResponse) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *RestartWorkflowExecutionResponse) IsSetRunId() bool { return v != nil && v.RunId != nil } type RetryPolicy struct { InitialIntervalInSeconds *int32 `json:"initialIntervalInSeconds,omitempty"` BackoffCoefficient *float64 `json:"backoffCoefficient,omitempty"` MaximumIntervalInSeconds *int32 `json:"maximumIntervalInSeconds,omitempty"` MaximumAttempts *int32 `json:"maximumAttempts,omitempty"` NonRetriableErrorReasons []string `json:"nonRetriableErrorReasons,omitempty"` ExpirationIntervalInSeconds *int32 `json:"expirationIntervalInSeconds,omitempty"` } // ToWire translates a RetryPolicy struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RetryPolicy) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.InitialIntervalInSeconds != nil { w, err = wire.NewValueI32(*(v.InitialIntervalInSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.BackoffCoefficient != nil { w, err = wire.NewValueDouble(*(v.BackoffCoefficient)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.MaximumIntervalInSeconds != nil { w, err = wire.NewValueI32(*(v.MaximumIntervalInSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.MaximumAttempts != nil { w, err = wire.NewValueI32(*(v.MaximumAttempts)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.NonRetriableErrorReasons != nil { w, err = wire.NewValueList(_List_String_ValueList(v.NonRetriableErrorReasons)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ExpirationIntervalInSeconds != nil { w, err = wire.NewValueI32(*(v.ExpirationIntervalInSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RetryPolicy struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RetryPolicy struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RetryPolicy // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RetryPolicy) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.InitialIntervalInSeconds = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TDouble { var x float64 x, err = field.Value.GetDouble(), error(nil) v.BackoffCoefficient = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.MaximumIntervalInSeconds = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.MaximumAttempts = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TList { v.NonRetriableErrorReasons, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ExpirationIntervalInSeconds = &x if err != nil { return err } } } } return nil } // Encode serializes a RetryPolicy struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RetryPolicy struct could not be encoded. func (v *RetryPolicy) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.InitialIntervalInSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.InitialIntervalInSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BackoffCoefficient != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(*(v.BackoffCoefficient)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MaximumIntervalInSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.MaximumIntervalInSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.MaximumAttempts != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.MaximumAttempts)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NonRetriableErrorReasons != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.NonRetriableErrorReasons, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExpirationIntervalInSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ExpirationIntervalInSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RetryPolicy struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RetryPolicy struct could not be generated from the wire // representation. func (v *RetryPolicy) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.InitialIntervalInSeconds = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TDouble: var x float64 x, err = sr.ReadDouble() v.BackoffCoefficient = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.MaximumIntervalInSeconds = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.MaximumAttempts = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TList: v.NonRetriableErrorReasons, err = _List_String_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ExpirationIntervalInSeconds = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RetryPolicy // struct. func (v *RetryPolicy) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.InitialIntervalInSeconds != nil { fields[i] = fmt.Sprintf("InitialIntervalInSeconds: %v", *(v.InitialIntervalInSeconds)) i++ } if v.BackoffCoefficient != nil { fields[i] = fmt.Sprintf("BackoffCoefficient: %v", *(v.BackoffCoefficient)) i++ } if v.MaximumIntervalInSeconds != nil { fields[i] = fmt.Sprintf("MaximumIntervalInSeconds: %v", *(v.MaximumIntervalInSeconds)) i++ } if v.MaximumAttempts != nil { fields[i] = fmt.Sprintf("MaximumAttempts: %v", *(v.MaximumAttempts)) i++ } if v.NonRetriableErrorReasons != nil { fields[i] = fmt.Sprintf("NonRetriableErrorReasons: %v", v.NonRetriableErrorReasons) i++ } if v.ExpirationIntervalInSeconds != nil { fields[i] = fmt.Sprintf("ExpirationIntervalInSeconds: %v", *(v.ExpirationIntervalInSeconds)) i++ } return fmt.Sprintf("RetryPolicy{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RetryPolicy match the // provided RetryPolicy. // // This function performs a deep comparison. func (v *RetryPolicy) Equals(rhs *RetryPolicy) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.InitialIntervalInSeconds, rhs.InitialIntervalInSeconds) { return false } if !_Double_EqualsPtr(v.BackoffCoefficient, rhs.BackoffCoefficient) { return false } if !_I32_EqualsPtr(v.MaximumIntervalInSeconds, rhs.MaximumIntervalInSeconds) { return false } if !_I32_EqualsPtr(v.MaximumAttempts, rhs.MaximumAttempts) { return false } if !((v.NonRetriableErrorReasons == nil && rhs.NonRetriableErrorReasons == nil) || (v.NonRetriableErrorReasons != nil && rhs.NonRetriableErrorReasons != nil && _List_String_Equals(v.NonRetriableErrorReasons, rhs.NonRetriableErrorReasons))) { return false } if !_I32_EqualsPtr(v.ExpirationIntervalInSeconds, rhs.ExpirationIntervalInSeconds) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RetryPolicy. func (v *RetryPolicy) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.InitialIntervalInSeconds != nil { enc.AddInt32("initialIntervalInSeconds", *v.InitialIntervalInSeconds) } if v.BackoffCoefficient != nil { enc.AddFloat64("backoffCoefficient", *v.BackoffCoefficient) } if v.MaximumIntervalInSeconds != nil { enc.AddInt32("maximumIntervalInSeconds", *v.MaximumIntervalInSeconds) } if v.MaximumAttempts != nil { enc.AddInt32("maximumAttempts", *v.MaximumAttempts) } if v.NonRetriableErrorReasons != nil { err = multierr.Append(err, enc.AddArray("nonRetriableErrorReasons", (_List_String_Zapper)(v.NonRetriableErrorReasons))) } if v.ExpirationIntervalInSeconds != nil { enc.AddInt32("expirationIntervalInSeconds", *v.ExpirationIntervalInSeconds) } return err } // GetInitialIntervalInSeconds returns the value of InitialIntervalInSeconds if it is set or its // zero value if it is unset. func (v *RetryPolicy) GetInitialIntervalInSeconds() (o int32) { if v != nil && v.InitialIntervalInSeconds != nil { return *v.InitialIntervalInSeconds } return } // IsSetInitialIntervalInSeconds returns true if InitialIntervalInSeconds is not nil. func (v *RetryPolicy) IsSetInitialIntervalInSeconds() bool { return v != nil && v.InitialIntervalInSeconds != nil } // GetBackoffCoefficient returns the value of BackoffCoefficient if it is set or its // zero value if it is unset. func (v *RetryPolicy) GetBackoffCoefficient() (o float64) { if v != nil && v.BackoffCoefficient != nil { return *v.BackoffCoefficient } return } // IsSetBackoffCoefficient returns true if BackoffCoefficient is not nil. func (v *RetryPolicy) IsSetBackoffCoefficient() bool { return v != nil && v.BackoffCoefficient != nil } // GetMaximumIntervalInSeconds returns the value of MaximumIntervalInSeconds if it is set or its // zero value if it is unset. func (v *RetryPolicy) GetMaximumIntervalInSeconds() (o int32) { if v != nil && v.MaximumIntervalInSeconds != nil { return *v.MaximumIntervalInSeconds } return } // IsSetMaximumIntervalInSeconds returns true if MaximumIntervalInSeconds is not nil. func (v *RetryPolicy) IsSetMaximumIntervalInSeconds() bool { return v != nil && v.MaximumIntervalInSeconds != nil } // GetMaximumAttempts returns the value of MaximumAttempts if it is set or its // zero value if it is unset. func (v *RetryPolicy) GetMaximumAttempts() (o int32) { if v != nil && v.MaximumAttempts != nil { return *v.MaximumAttempts } return } // IsSetMaximumAttempts returns true if MaximumAttempts is not nil. func (v *RetryPolicy) IsSetMaximumAttempts() bool { return v != nil && v.MaximumAttempts != nil } // GetNonRetriableErrorReasons returns the value of NonRetriableErrorReasons if it is set or its // zero value if it is unset. func (v *RetryPolicy) GetNonRetriableErrorReasons() (o []string) { if v != nil && v.NonRetriableErrorReasons != nil { return v.NonRetriableErrorReasons } return } // IsSetNonRetriableErrorReasons returns true if NonRetriableErrorReasons is not nil. func (v *RetryPolicy) IsSetNonRetriableErrorReasons() bool { return v != nil && v.NonRetriableErrorReasons != nil } // GetExpirationIntervalInSeconds returns the value of ExpirationIntervalInSeconds if it is set or its // zero value if it is unset. func (v *RetryPolicy) GetExpirationIntervalInSeconds() (o int32) { if v != nil && v.ExpirationIntervalInSeconds != nil { return *v.ExpirationIntervalInSeconds } return } // IsSetExpirationIntervalInSeconds returns true if ExpirationIntervalInSeconds is not nil. func (v *RetryPolicy) IsSetExpirationIntervalInSeconds() bool { return v != nil && v.ExpirationIntervalInSeconds != nil } type RetryTaskV2Error struct { Message string `json:"message,required"` DomainId *string `json:"domainId,omitempty"` WorkflowId *string `json:"workflowId,omitempty"` RunId *string `json:"runId,omitempty"` StartEventId *int64 `json:"startEventId,omitempty"` StartEventVersion *int64 `json:"startEventVersion,omitempty"` EndEventId *int64 `json:"endEventId,omitempty"` EndEventVersion *int64 `json:"endEventVersion,omitempty"` } // ToWire translates a RetryTaskV2Error struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RetryTaskV2Error) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ if v.DomainId != nil { w, err = wire.NewValueString(*(v.DomainId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ } if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 4, Value: w} i++ } if v.StartEventId != nil { w, err = wire.NewValueI64(*(v.StartEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 5, Value: w} i++ } if v.StartEventVersion != nil { w, err = wire.NewValueI64(*(v.StartEventVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 6, Value: w} i++ } if v.EndEventId != nil { w, err = wire.NewValueI64(*(v.EndEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 7, Value: w} i++ } if v.EndEventVersion != nil { w, err = wire.NewValueI64(*(v.EndEventVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 8, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RetryTaskV2Error struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RetryTaskV2Error struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RetryTaskV2Error // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RetryTaskV2Error) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } case 2: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainId = &x if err != nil { return err } } case 3: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 4: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } case 5: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartEventId = &x if err != nil { return err } } case 6: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartEventVersion = &x if err != nil { return err } } case 7: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EndEventId = &x if err != nil { return err } } case 8: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EndEventVersion = &x if err != nil { return err } } } } if !messageIsSet { return errors.New("field Message of RetryTaskV2Error is required") } return nil } // Encode serializes a RetryTaskV2Error struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RetryTaskV2Error struct could not be encoded. func (v *RetryTaskV2Error) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if v.DomainId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 4, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 5, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartEventVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 6, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartEventVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EndEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 7, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EndEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EndEventVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 8, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EndEventVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RetryTaskV2Error struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RetryTaskV2Error struct could not be generated from the wire // representation. func (v *RetryTaskV2Error) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true case fh.ID == 2 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainId = &x if err != nil { return err } case fh.ID == 3 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 4 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } case fh.ID == 5 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartEventId = &x if err != nil { return err } case fh.ID == 6 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartEventVersion = &x if err != nil { return err } case fh.ID == 7 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EndEventId = &x if err != nil { return err } case fh.ID == 8 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EndEventVersion = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of RetryTaskV2Error is required") } return nil } // String returns a readable string representation of a RetryTaskV2Error // struct. func (v *RetryTaskV2Error) String() string { if v == nil { return "" } var fields [8]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ if v.DomainId != nil { fields[i] = fmt.Sprintf("DomainId: %v", *(v.DomainId)) i++ } if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } if v.StartEventId != nil { fields[i] = fmt.Sprintf("StartEventId: %v", *(v.StartEventId)) i++ } if v.StartEventVersion != nil { fields[i] = fmt.Sprintf("StartEventVersion: %v", *(v.StartEventVersion)) i++ } if v.EndEventId != nil { fields[i] = fmt.Sprintf("EndEventId: %v", *(v.EndEventId)) i++ } if v.EndEventVersion != nil { fields[i] = fmt.Sprintf("EndEventVersion: %v", *(v.EndEventVersion)) i++ } return fmt.Sprintf("RetryTaskV2Error{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*RetryTaskV2Error) ErrorName() string { return "RetryTaskV2Error" } // Equals returns true if all the fields of this RetryTaskV2Error match the // provided RetryTaskV2Error. // // This function performs a deep comparison. func (v *RetryTaskV2Error) Equals(rhs *RetryTaskV2Error) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } if !_String_EqualsPtr(v.DomainId, rhs.DomainId) { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } if !_I64_EqualsPtr(v.StartEventId, rhs.StartEventId) { return false } if !_I64_EqualsPtr(v.StartEventVersion, rhs.StartEventVersion) { return false } if !_I64_EqualsPtr(v.EndEventId, rhs.EndEventId) { return false } if !_I64_EqualsPtr(v.EndEventVersion, rhs.EndEventVersion) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RetryTaskV2Error. func (v *RetryTaskV2Error) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) if v.DomainId != nil { enc.AddString("domainId", *v.DomainId) } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.RunId != nil { enc.AddString("runId", *v.RunId) } if v.StartEventId != nil { enc.AddInt64("startEventId", *v.StartEventId) } if v.StartEventVersion != nil { enc.AddInt64("startEventVersion", *v.StartEventVersion) } if v.EndEventId != nil { enc.AddInt64("endEventId", *v.EndEventId) } if v.EndEventVersion != nil { enc.AddInt64("endEventVersion", *v.EndEventVersion) } return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *RetryTaskV2Error) GetMessage() (o string) { if v != nil { o = v.Message } return } // GetDomainId returns the value of DomainId if it is set or its // zero value if it is unset. func (v *RetryTaskV2Error) GetDomainId() (o string) { if v != nil && v.DomainId != nil { return *v.DomainId } return } // IsSetDomainId returns true if DomainId is not nil. func (v *RetryTaskV2Error) IsSetDomainId() bool { return v != nil && v.DomainId != nil } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *RetryTaskV2Error) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *RetryTaskV2Error) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *RetryTaskV2Error) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *RetryTaskV2Error) IsSetRunId() bool { return v != nil && v.RunId != nil } // GetStartEventId returns the value of StartEventId if it is set or its // zero value if it is unset. func (v *RetryTaskV2Error) GetStartEventId() (o int64) { if v != nil && v.StartEventId != nil { return *v.StartEventId } return } // IsSetStartEventId returns true if StartEventId is not nil. func (v *RetryTaskV2Error) IsSetStartEventId() bool { return v != nil && v.StartEventId != nil } // GetStartEventVersion returns the value of StartEventVersion if it is set or its // zero value if it is unset. func (v *RetryTaskV2Error) GetStartEventVersion() (o int64) { if v != nil && v.StartEventVersion != nil { return *v.StartEventVersion } return } // IsSetStartEventVersion returns true if StartEventVersion is not nil. func (v *RetryTaskV2Error) IsSetStartEventVersion() bool { return v != nil && v.StartEventVersion != nil } // GetEndEventId returns the value of EndEventId if it is set or its // zero value if it is unset. func (v *RetryTaskV2Error) GetEndEventId() (o int64) { if v != nil && v.EndEventId != nil { return *v.EndEventId } return } // IsSetEndEventId returns true if EndEventId is not nil. func (v *RetryTaskV2Error) IsSetEndEventId() bool { return v != nil && v.EndEventId != nil } // GetEndEventVersion returns the value of EndEventVersion if it is set or its // zero value if it is unset. func (v *RetryTaskV2Error) GetEndEventVersion() (o int64) { if v != nil && v.EndEventVersion != nil { return *v.EndEventVersion } return } // IsSetEndEventVersion returns true if EndEventVersion is not nil. func (v *RetryTaskV2Error) IsSetEndEventVersion() bool { return v != nil && v.EndEventVersion != nil } func (v *RetryTaskV2Error) Error() string { return v.String() } type ScheduleActivityTaskDecisionAttributes struct { ActivityId *string `json:"activityId,omitempty"` ActivityType *ActivityType `json:"activityType,omitempty"` Domain *string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ScheduleToCloseTimeoutSeconds *int32 `json:"scheduleToCloseTimeoutSeconds,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` StartToCloseTimeoutSeconds *int32 `json:"startToCloseTimeoutSeconds,omitempty"` HeartbeatTimeoutSeconds *int32 `json:"heartbeatTimeoutSeconds,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` Header *Header `json:"header,omitempty"` RequestLocalDispatch *bool `json:"requestLocalDispatch,omitempty"` } // ToWire translates a ScheduleActivityTaskDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ScheduleActivityTaskDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [12]wire.Field i int = 0 w wire.Value err error ) if v.ActivityId != nil { w, err = wire.NewValueString(*(v.ActivityId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ActivityType != nil { w, err = v.ActivityType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 25, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ScheduleToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ScheduleToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 45, Value: w} i++ } if v.ScheduleToStartTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ScheduleToStartTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.StartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.StartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 55, Value: w} i++ } if v.HeartbeatTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.HeartbeatTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.RetryPolicy != nil { w, err = v.RetryPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.RequestLocalDispatch != nil { w, err = wire.NewValueBool(*(v.RequestLocalDispatch)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ScheduleActivityTaskDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ScheduleActivityTaskDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ScheduleActivityTaskDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ScheduleActivityTaskDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.ActivityType, err = _ActivityType_Read(field.Value) if err != nil { return err } } case 25: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 45: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ScheduleToCloseTimeoutSeconds = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } } case 55: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.StartToCloseTimeoutSeconds = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.HeartbeatTimeoutSeconds = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.RetryPolicy, err = _RetryPolicy_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } case 90: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.RequestLocalDispatch = &x if err != nil { return err } } } } return nil } // Encode serializes a ScheduleActivityTaskDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ScheduleActivityTaskDecisionAttributes struct could not be encoded. func (v *ScheduleActivityTaskDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ActivityId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.ActivityType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 25, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 45, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ScheduleToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleToStartTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ScheduleToStartTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 55, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.StartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HeartbeatTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.HeartbeatTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.RetryPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestLocalDispatch != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.RequestLocalDispatch)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ScheduleActivityTaskDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ScheduleActivityTaskDecisionAttributes struct could not be generated from the wire // representation. func (v *ScheduleActivityTaskDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.ActivityType, err = _ActivityType_Decode(sr) if err != nil { return err } case fh.ID == 25 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 45 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ScheduleToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } case fh.ID == 55 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.StartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.HeartbeatTimeoutSeconds = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.RetryPolicy, err = _RetryPolicy_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.RequestLocalDispatch = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ScheduleActivityTaskDecisionAttributes // struct. func (v *ScheduleActivityTaskDecisionAttributes) String() string { if v == nil { return "" } var fields [12]string i := 0 if v.ActivityId != nil { fields[i] = fmt.Sprintf("ActivityId: %v", *(v.ActivityId)) i++ } if v.ActivityType != nil { fields[i] = fmt.Sprintf("ActivityType: %v", v.ActivityType) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.ScheduleToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ScheduleToCloseTimeoutSeconds: %v", *(v.ScheduleToCloseTimeoutSeconds)) i++ } if v.ScheduleToStartTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ScheduleToStartTimeoutSeconds: %v", *(v.ScheduleToStartTimeoutSeconds)) i++ } if v.StartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("StartToCloseTimeoutSeconds: %v", *(v.StartToCloseTimeoutSeconds)) i++ } if v.HeartbeatTimeoutSeconds != nil { fields[i] = fmt.Sprintf("HeartbeatTimeoutSeconds: %v", *(v.HeartbeatTimeoutSeconds)) i++ } if v.RetryPolicy != nil { fields[i] = fmt.Sprintf("RetryPolicy: %v", v.RetryPolicy) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } if v.RequestLocalDispatch != nil { fields[i] = fmt.Sprintf("RequestLocalDispatch: %v", *(v.RequestLocalDispatch)) i++ } return fmt.Sprintf("ScheduleActivityTaskDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ScheduleActivityTaskDecisionAttributes match the // provided ScheduleActivityTaskDecisionAttributes. // // This function performs a deep comparison. func (v *ScheduleActivityTaskDecisionAttributes) Equals(rhs *ScheduleActivityTaskDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.ActivityId, rhs.ActivityId) { return false } if !((v.ActivityType == nil && rhs.ActivityType == nil) || (v.ActivityType != nil && rhs.ActivityType != nil && v.ActivityType.Equals(rhs.ActivityType))) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_I32_EqualsPtr(v.ScheduleToCloseTimeoutSeconds, rhs.ScheduleToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.ScheduleToStartTimeoutSeconds, rhs.ScheduleToStartTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.StartToCloseTimeoutSeconds, rhs.StartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.HeartbeatTimeoutSeconds, rhs.HeartbeatTimeoutSeconds) { return false } if !((v.RetryPolicy == nil && rhs.RetryPolicy == nil) || (v.RetryPolicy != nil && rhs.RetryPolicy != nil && v.RetryPolicy.Equals(rhs.RetryPolicy))) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } if !_Bool_EqualsPtr(v.RequestLocalDispatch, rhs.RequestLocalDispatch) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ScheduleActivityTaskDecisionAttributes. func (v *ScheduleActivityTaskDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ActivityId != nil { enc.AddString("activityId", *v.ActivityId) } if v.ActivityType != nil { err = multierr.Append(err, enc.AddObject("activityType", v.ActivityType)) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.ScheduleToCloseTimeoutSeconds != nil { enc.AddInt32("scheduleToCloseTimeoutSeconds", *v.ScheduleToCloseTimeoutSeconds) } if v.ScheduleToStartTimeoutSeconds != nil { enc.AddInt32("scheduleToStartTimeoutSeconds", *v.ScheduleToStartTimeoutSeconds) } if v.StartToCloseTimeoutSeconds != nil { enc.AddInt32("startToCloseTimeoutSeconds", *v.StartToCloseTimeoutSeconds) } if v.HeartbeatTimeoutSeconds != nil { enc.AddInt32("heartbeatTimeoutSeconds", *v.HeartbeatTimeoutSeconds) } if v.RetryPolicy != nil { err = multierr.Append(err, enc.AddObject("retryPolicy", v.RetryPolicy)) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } if v.RequestLocalDispatch != nil { enc.AddBool("requestLocalDispatch", *v.RequestLocalDispatch) } return err } // GetActivityId returns the value of ActivityId if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetActivityId() (o string) { if v != nil && v.ActivityId != nil { return *v.ActivityId } return } // IsSetActivityId returns true if ActivityId is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetActivityId() bool { return v != nil && v.ActivityId != nil } // GetActivityType returns the value of ActivityType if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetActivityType() (o *ActivityType) { if v != nil && v.ActivityType != nil { return v.ActivityType } return } // IsSetActivityType returns true if ActivityType is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetActivityType() bool { return v != nil && v.ActivityType != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetInput() bool { return v != nil && v.Input != nil } // GetScheduleToCloseTimeoutSeconds returns the value of ScheduleToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetScheduleToCloseTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToCloseTimeoutSeconds != nil { return *v.ScheduleToCloseTimeoutSeconds } return } // IsSetScheduleToCloseTimeoutSeconds returns true if ScheduleToCloseTimeoutSeconds is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetScheduleToCloseTimeoutSeconds() bool { return v != nil && v.ScheduleToCloseTimeoutSeconds != nil } // GetScheduleToStartTimeoutSeconds returns the value of ScheduleToStartTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // IsSetScheduleToStartTimeoutSeconds returns true if ScheduleToStartTimeoutSeconds is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetScheduleToStartTimeoutSeconds() bool { return v != nil && v.ScheduleToStartTimeoutSeconds != nil } // GetStartToCloseTimeoutSeconds returns the value of StartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.StartToCloseTimeoutSeconds != nil { return *v.StartToCloseTimeoutSeconds } return } // IsSetStartToCloseTimeoutSeconds returns true if StartToCloseTimeoutSeconds is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetStartToCloseTimeoutSeconds() bool { return v != nil && v.StartToCloseTimeoutSeconds != nil } // GetHeartbeatTimeoutSeconds returns the value of HeartbeatTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetHeartbeatTimeoutSeconds() (o int32) { if v != nil && v.HeartbeatTimeoutSeconds != nil { return *v.HeartbeatTimeoutSeconds } return } // IsSetHeartbeatTimeoutSeconds returns true if HeartbeatTimeoutSeconds is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetHeartbeatTimeoutSeconds() bool { return v != nil && v.HeartbeatTimeoutSeconds != nil } // GetRetryPolicy returns the value of RetryPolicy if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetRetryPolicy() (o *RetryPolicy) { if v != nil && v.RetryPolicy != nil { return v.RetryPolicy } return } // IsSetRetryPolicy returns true if RetryPolicy is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetRetryPolicy() bool { return v != nil && v.RetryPolicy != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetHeader() bool { return v != nil && v.Header != nil } // GetRequestLocalDispatch returns the value of RequestLocalDispatch if it is set or its // zero value if it is unset. func (v *ScheduleActivityTaskDecisionAttributes) GetRequestLocalDispatch() (o bool) { if v != nil && v.RequestLocalDispatch != nil { return *v.RequestLocalDispatch } return } // IsSetRequestLocalDispatch returns true if RequestLocalDispatch is not nil. func (v *ScheduleActivityTaskDecisionAttributes) IsSetRequestLocalDispatch() bool { return v != nil && v.RequestLocalDispatch != nil } type SearchAttributes struct { IndexedFields map[string][]byte `json:"indexedFields,omitempty"` } // ToWire translates a SearchAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SearchAttributes) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.IndexedFields != nil { w, err = wire.NewValueMap(_Map_String_Binary_MapItemList(v.IndexedFields)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a SearchAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SearchAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SearchAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SearchAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TMap { v.IndexedFields, err = _Map_String_Binary_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } // Encode serializes a SearchAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SearchAttributes struct could not be encoded. func (v *SearchAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.IndexedFields != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TMap}); err != nil { return err } if err := _Map_String_Binary_Encode(v.IndexedFields, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a SearchAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SearchAttributes struct could not be generated from the wire // representation. func (v *SearchAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TMap: v.IndexedFields, err = _Map_String_Binary_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SearchAttributes // struct. func (v *SearchAttributes) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.IndexedFields != nil { fields[i] = fmt.Sprintf("IndexedFields: %v", v.IndexedFields) i++ } return fmt.Sprintf("SearchAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SearchAttributes match the // provided SearchAttributes. // // This function performs a deep comparison. func (v *SearchAttributes) Equals(rhs *SearchAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.IndexedFields == nil && rhs.IndexedFields == nil) || (v.IndexedFields != nil && rhs.IndexedFields != nil && _Map_String_Binary_Equals(v.IndexedFields, rhs.IndexedFields))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SearchAttributes. func (v *SearchAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.IndexedFields != nil { err = multierr.Append(err, enc.AddObject("indexedFields", (_Map_String_Binary_Zapper)(v.IndexedFields))) } return err } // GetIndexedFields returns the value of IndexedFields if it is set or its // zero value if it is unset. func (v *SearchAttributes) GetIndexedFields() (o map[string][]byte) { if v != nil && v.IndexedFields != nil { return v.IndexedFields } return } // IsSetIndexedFields returns true if IndexedFields is not nil. func (v *SearchAttributes) IsSetIndexedFields() bool { return v != nil && v.IndexedFields != nil } type ServiceBusyError struct { Message string `json:"message,required"` Reason *string `json:"reason,omitempty"` } // ToWire translates a ServiceBusyError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ServiceBusyError) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ServiceBusyError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ServiceBusyError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ServiceBusyError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ServiceBusyError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } case 2: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } } } if !messageIsSet { return errors.New("field Message of ServiceBusyError is required") } return nil } // Encode serializes a ServiceBusyError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ServiceBusyError struct could not be encoded. func (v *ServiceBusyError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ServiceBusyError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ServiceBusyError struct could not be generated from the wire // representation. func (v *ServiceBusyError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true case fh.ID == 2 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of ServiceBusyError is required") } return nil } // String returns a readable string representation of a ServiceBusyError // struct. func (v *ServiceBusyError) String() string { if v == nil { return "" } var fields [2]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } return fmt.Sprintf("ServiceBusyError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*ServiceBusyError) ErrorName() string { return "ServiceBusyError" } // Equals returns true if all the fields of this ServiceBusyError match the // provided ServiceBusyError. // // This function performs a deep comparison. func (v *ServiceBusyError) Equals(rhs *ServiceBusyError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ServiceBusyError. func (v *ServiceBusyError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) if v.Reason != nil { enc.AddString("reason", *v.Reason) } return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *ServiceBusyError) GetMessage() (o string) { if v != nil { o = v.Message } return } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *ServiceBusyError) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *ServiceBusyError) IsSetReason() bool { return v != nil && v.Reason != nil } func (v *ServiceBusyError) Error() string { return v.String() } type SignalExternalWorkflowExecutionDecisionAttributes struct { Domain *string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` SignalName *string `json:"signalName,omitempty"` Input []byte `json:"input,omitempty"` Control []byte `json:"control,omitempty"` ChildWorkflowOnly *bool `json:"childWorkflowOnly,omitempty"` } // ToWire translates a SignalExternalWorkflowExecutionDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SignalExternalWorkflowExecutionDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.SignalName != nil { w, err = wire.NewValueString(*(v.SignalName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ChildWorkflowOnly != nil { w, err = wire.NewValueBool(*(v.ChildWorkflowOnly)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a SignalExternalWorkflowExecutionDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SignalExternalWorkflowExecutionDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SignalExternalWorkflowExecutionDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SignalExternalWorkflowExecutionDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SignalName = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ChildWorkflowOnly = &x if err != nil { return err } } } } return nil } // Encode serializes a SignalExternalWorkflowExecutionDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SignalExternalWorkflowExecutionDecisionAttributes struct could not be encoded. func (v *SignalExternalWorkflowExecutionDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SignalName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowOnly != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ChildWorkflowOnly)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a SignalExternalWorkflowExecutionDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SignalExternalWorkflowExecutionDecisionAttributes struct could not be generated from the wire // representation. func (v *SignalExternalWorkflowExecutionDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SignalName = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ChildWorkflowOnly = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SignalExternalWorkflowExecutionDecisionAttributes // struct. func (v *SignalExternalWorkflowExecutionDecisionAttributes) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.SignalName != nil { fields[i] = fmt.Sprintf("SignalName: %v", *(v.SignalName)) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } if v.ChildWorkflowOnly != nil { fields[i] = fmt.Sprintf("ChildWorkflowOnly: %v", *(v.ChildWorkflowOnly)) i++ } return fmt.Sprintf("SignalExternalWorkflowExecutionDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SignalExternalWorkflowExecutionDecisionAttributes match the // provided SignalExternalWorkflowExecutionDecisionAttributes. // // This function performs a deep comparison. func (v *SignalExternalWorkflowExecutionDecisionAttributes) Equals(rhs *SignalExternalWorkflowExecutionDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !_String_EqualsPtr(v.SignalName, rhs.SignalName) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } if !_Bool_EqualsPtr(v.ChildWorkflowOnly, rhs.ChildWorkflowOnly) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalExternalWorkflowExecutionDecisionAttributes. func (v *SignalExternalWorkflowExecutionDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.SignalName != nil { enc.AddString("signalName", *v.SignalName) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } if v.ChildWorkflowOnly != nil { enc.AddBool("childWorkflowOnly", *v.ChildWorkflowOnly) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionDecisionAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *SignalExternalWorkflowExecutionDecisionAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionDecisionAttributes) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *SignalExternalWorkflowExecutionDecisionAttributes) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetSignalName returns the value of SignalName if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionDecisionAttributes) GetSignalName() (o string) { if v != nil && v.SignalName != nil { return *v.SignalName } return } // IsSetSignalName returns true if SignalName is not nil. func (v *SignalExternalWorkflowExecutionDecisionAttributes) IsSetSignalName() bool { return v != nil && v.SignalName != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionDecisionAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *SignalExternalWorkflowExecutionDecisionAttributes) IsSetInput() bool { return v != nil && v.Input != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionDecisionAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *SignalExternalWorkflowExecutionDecisionAttributes) IsSetControl() bool { return v != nil && v.Control != nil } // GetChildWorkflowOnly returns the value of ChildWorkflowOnly if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionDecisionAttributes) GetChildWorkflowOnly() (o bool) { if v != nil && v.ChildWorkflowOnly != nil { return *v.ChildWorkflowOnly } return } // IsSetChildWorkflowOnly returns true if ChildWorkflowOnly is not nil. func (v *SignalExternalWorkflowExecutionDecisionAttributes) IsSetChildWorkflowOnly() bool { return v != nil && v.ChildWorkflowOnly != nil } type SignalExternalWorkflowExecutionFailedCause int32 const ( SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution SignalExternalWorkflowExecutionFailedCause = 0 SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted SignalExternalWorkflowExecutionFailedCause = 1 ) // SignalExternalWorkflowExecutionFailedCause_Values returns all recognized values of SignalExternalWorkflowExecutionFailedCause. func SignalExternalWorkflowExecutionFailedCause_Values() []SignalExternalWorkflowExecutionFailedCause { return []SignalExternalWorkflowExecutionFailedCause{ SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution, SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted, } } // UnmarshalText tries to decode SignalExternalWorkflowExecutionFailedCause from a byte slice // containing its name. // // var v SignalExternalWorkflowExecutionFailedCause // err := v.UnmarshalText([]byte("UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION")) func (v *SignalExternalWorkflowExecutionFailedCause) UnmarshalText(value []byte) error { switch s := string(value); s { case "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION": *v = SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution return nil case "WORKFLOW_ALREADY_COMPLETED": *v = SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "SignalExternalWorkflowExecutionFailedCause", err) } *v = SignalExternalWorkflowExecutionFailedCause(val) return nil } } // MarshalText encodes SignalExternalWorkflowExecutionFailedCause to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v SignalExternalWorkflowExecutionFailedCause) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION"), nil case 1: return []byte("WORKFLOW_ALREADY_COMPLETED"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalExternalWorkflowExecutionFailedCause. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v SignalExternalWorkflowExecutionFailedCause) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION") case 1: enc.AddString("name", "WORKFLOW_ALREADY_COMPLETED") } return nil } // Ptr returns a pointer to this enum value. func (v SignalExternalWorkflowExecutionFailedCause) Ptr() *SignalExternalWorkflowExecutionFailedCause { return &v } // Encode encodes SignalExternalWorkflowExecutionFailedCause directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v SignalExternalWorkflowExecutionFailedCause // return v.Encode(sWriter) func (v SignalExternalWorkflowExecutionFailedCause) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates SignalExternalWorkflowExecutionFailedCause into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v SignalExternalWorkflowExecutionFailedCause) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes SignalExternalWorkflowExecutionFailedCause from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return SignalExternalWorkflowExecutionFailedCause(0), err // } // // var v SignalExternalWorkflowExecutionFailedCause // if err := v.FromWire(x); err != nil { // return SignalExternalWorkflowExecutionFailedCause(0), err // } // return v, nil func (v *SignalExternalWorkflowExecutionFailedCause) FromWire(w wire.Value) error { *v = (SignalExternalWorkflowExecutionFailedCause)(w.GetI32()) return nil } // Decode reads off the encoded SignalExternalWorkflowExecutionFailedCause directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v SignalExternalWorkflowExecutionFailedCause // if err := v.Decode(sReader); err != nil { // return SignalExternalWorkflowExecutionFailedCause(0), err // } // return v, nil func (v *SignalExternalWorkflowExecutionFailedCause) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (SignalExternalWorkflowExecutionFailedCause)(i) return nil } // String returns a readable string representation of SignalExternalWorkflowExecutionFailedCause. func (v SignalExternalWorkflowExecutionFailedCause) String() string { w := int32(v) switch w { case 0: return "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION" case 1: return "WORKFLOW_ALREADY_COMPLETED" } return fmt.Sprintf("SignalExternalWorkflowExecutionFailedCause(%d)", w) } // Equals returns true if this SignalExternalWorkflowExecutionFailedCause value matches the provided // value. func (v SignalExternalWorkflowExecutionFailedCause) Equals(rhs SignalExternalWorkflowExecutionFailedCause) bool { return v == rhs } // MarshalJSON serializes SignalExternalWorkflowExecutionFailedCause into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v SignalExternalWorkflowExecutionFailedCause) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION\""), nil case 1: return ([]byte)("\"WORKFLOW_ALREADY_COMPLETED\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode SignalExternalWorkflowExecutionFailedCause from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *SignalExternalWorkflowExecutionFailedCause) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "SignalExternalWorkflowExecutionFailedCause") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "SignalExternalWorkflowExecutionFailedCause") } *v = (SignalExternalWorkflowExecutionFailedCause)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "SignalExternalWorkflowExecutionFailedCause") } } type SignalExternalWorkflowExecutionFailedEventAttributes struct { Cause *SignalExternalWorkflowExecutionFailedCause `json:"cause,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` Control []byte `json:"control,omitempty"` } // ToWire translates a SignalExternalWorkflowExecutionFailedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SignalExternalWorkflowExecutionFailedEventAttributes) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Cause != nil { w, err = v.Cause.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SignalExternalWorkflowExecutionFailedCause_Read(w wire.Value) (SignalExternalWorkflowExecutionFailedCause, error) { var v SignalExternalWorkflowExecutionFailedCause err := v.FromWire(w) return v, err } // FromWire deserializes a SignalExternalWorkflowExecutionFailedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SignalExternalWorkflowExecutionFailedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SignalExternalWorkflowExecutionFailedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SignalExternalWorkflowExecutionFailedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x SignalExternalWorkflowExecutionFailedCause x, err = _SignalExternalWorkflowExecutionFailedCause_Read(field.Value) v.Cause = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a SignalExternalWorkflowExecutionFailedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SignalExternalWorkflowExecutionFailedEventAttributes struct could not be encoded. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Cause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.Cause.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SignalExternalWorkflowExecutionFailedCause_Decode(sr stream.Reader) (SignalExternalWorkflowExecutionFailedCause, error) { var v SignalExternalWorkflowExecutionFailedCause err := v.Decode(sr) return v, err } // Decode deserializes a SignalExternalWorkflowExecutionFailedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SignalExternalWorkflowExecutionFailedEventAttributes struct could not be generated from the wire // representation. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x SignalExternalWorkflowExecutionFailedCause x, err = _SignalExternalWorkflowExecutionFailedCause_Decode(sr) v.Cause = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SignalExternalWorkflowExecutionFailedEventAttributes // struct. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Cause != nil { fields[i] = fmt.Sprintf("Cause: %v", *(v.Cause)) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } return fmt.Sprintf("SignalExternalWorkflowExecutionFailedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } func _SignalExternalWorkflowExecutionFailedCause_EqualsPtr(lhs, rhs *SignalExternalWorkflowExecutionFailedCause) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this SignalExternalWorkflowExecutionFailedEventAttributes match the // provided SignalExternalWorkflowExecutionFailedEventAttributes. // // This function performs a deep comparison. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) Equals(rhs *SignalExternalWorkflowExecutionFailedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_SignalExternalWorkflowExecutionFailedCause_EqualsPtr(v.Cause, rhs.Cause) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalExternalWorkflowExecutionFailedEventAttributes. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Cause != nil { err = multierr.Append(err, enc.AddObject("cause", *v.Cause)) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } return err } // GetCause returns the value of Cause if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) GetCause() (o SignalExternalWorkflowExecutionFailedCause) { if v != nil && v.Cause != nil { return *v.Cause } return } // IsSetCause returns true if Cause is not nil. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) IsSetCause() bool { return v != nil && v.Cause != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *SignalExternalWorkflowExecutionFailedEventAttributes) IsSetControl() bool { return v != nil && v.Control != nil } type SignalExternalWorkflowExecutionInitiatedEventAttributes struct { DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` SignalName *string `json:"signalName,omitempty"` Input []byte `json:"input,omitempty"` Control []byte `json:"control,omitempty"` ChildWorkflowOnly *bool `json:"childWorkflowOnly,omitempty"` } // ToWire translates a SignalExternalWorkflowExecutionInitiatedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.SignalName != nil { w, err = wire.NewValueString(*(v.SignalName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.ChildWorkflowOnly != nil { w, err = wire.NewValueBool(*(v.ChildWorkflowOnly)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a SignalExternalWorkflowExecutionInitiatedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SignalExternalWorkflowExecutionInitiatedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SignalExternalWorkflowExecutionInitiatedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SignalName = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 70: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.ChildWorkflowOnly = &x if err != nil { return err } } } } return nil } // Encode serializes a SignalExternalWorkflowExecutionInitiatedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SignalExternalWorkflowExecutionInitiatedEventAttributes struct could not be encoded. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SignalName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChildWorkflowOnly != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.ChildWorkflowOnly)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a SignalExternalWorkflowExecutionInitiatedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SignalExternalWorkflowExecutionInitiatedEventAttributes struct could not be generated from the wire // representation. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SignalName = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.ChildWorkflowOnly = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SignalExternalWorkflowExecutionInitiatedEventAttributes // struct. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.SignalName != nil { fields[i] = fmt.Sprintf("SignalName: %v", *(v.SignalName)) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } if v.ChildWorkflowOnly != nil { fields[i] = fmt.Sprintf("ChildWorkflowOnly: %v", *(v.ChildWorkflowOnly)) i++ } return fmt.Sprintf("SignalExternalWorkflowExecutionInitiatedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SignalExternalWorkflowExecutionInitiatedEventAttributes match the // provided SignalExternalWorkflowExecutionInitiatedEventAttributes. // // This function performs a deep comparison. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) Equals(rhs *SignalExternalWorkflowExecutionInitiatedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_String_EqualsPtr(v.SignalName, rhs.SignalName) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } if !_Bool_EqualsPtr(v.ChildWorkflowOnly, rhs.ChildWorkflowOnly) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalExternalWorkflowExecutionInitiatedEventAttributes. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.SignalName != nil { enc.AddString("signalName", *v.SignalName) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } if v.ChildWorkflowOnly != nil { enc.AddBool("childWorkflowOnly", *v.ChildWorkflowOnly) } return err } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetSignalName returns the value of SignalName if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetSignalName() (o string) { if v != nil && v.SignalName != nil { return *v.SignalName } return } // IsSetSignalName returns true if SignalName is not nil. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) IsSetSignalName() bool { return v != nil && v.SignalName != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) IsSetInput() bool { return v != nil && v.Input != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) IsSetControl() bool { return v != nil && v.Control != nil } // GetChildWorkflowOnly returns the value of ChildWorkflowOnly if it is set or its // zero value if it is unset. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetChildWorkflowOnly() (o bool) { if v != nil && v.ChildWorkflowOnly != nil { return *v.ChildWorkflowOnly } return } // IsSetChildWorkflowOnly returns true if ChildWorkflowOnly is not nil. func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) IsSetChildWorkflowOnly() bool { return v != nil && v.ChildWorkflowOnly != nil } type SignalWithStartWorkflowExecutionAsyncRequest struct { Request *SignalWithStartWorkflowExecutionRequest `json:"request,omitempty"` } // ToWire translates a SignalWithStartWorkflowExecutionAsyncRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SignalWithStartWorkflowExecutionAsyncRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _SignalWithStartWorkflowExecutionRequest_Read(w wire.Value) (*SignalWithStartWorkflowExecutionRequest, error) { var v SignalWithStartWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a SignalWithStartWorkflowExecutionAsyncRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SignalWithStartWorkflowExecutionAsyncRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SignalWithStartWorkflowExecutionAsyncRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SignalWithStartWorkflowExecutionAsyncRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Request, err = _SignalWithStartWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a SignalWithStartWorkflowExecutionAsyncRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SignalWithStartWorkflowExecutionAsyncRequest struct could not be encoded. func (v *SignalWithStartWorkflowExecutionAsyncRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _SignalWithStartWorkflowExecutionRequest_Decode(sr stream.Reader) (*SignalWithStartWorkflowExecutionRequest, error) { var v SignalWithStartWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a SignalWithStartWorkflowExecutionAsyncRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SignalWithStartWorkflowExecutionAsyncRequest struct could not be generated from the wire // representation. func (v *SignalWithStartWorkflowExecutionAsyncRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Request, err = _SignalWithStartWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SignalWithStartWorkflowExecutionAsyncRequest // struct. func (v *SignalWithStartWorkflowExecutionAsyncRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("SignalWithStartWorkflowExecutionAsyncRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SignalWithStartWorkflowExecutionAsyncRequest match the // provided SignalWithStartWorkflowExecutionAsyncRequest. // // This function performs a deep comparison. func (v *SignalWithStartWorkflowExecutionAsyncRequest) Equals(rhs *SignalWithStartWorkflowExecutionAsyncRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalWithStartWorkflowExecutionAsyncRequest. func (v *SignalWithStartWorkflowExecutionAsyncRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionAsyncRequest) GetRequest() (o *SignalWithStartWorkflowExecutionRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *SignalWithStartWorkflowExecutionAsyncRequest) IsSetRequest() bool { return v != nil && v.Request != nil } type SignalWithStartWorkflowExecutionAsyncResponse struct { } // ToWire translates a SignalWithStartWorkflowExecutionAsyncResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SignalWithStartWorkflowExecutionAsyncResponse) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a SignalWithStartWorkflowExecutionAsyncResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SignalWithStartWorkflowExecutionAsyncResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SignalWithStartWorkflowExecutionAsyncResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SignalWithStartWorkflowExecutionAsyncResponse) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a SignalWithStartWorkflowExecutionAsyncResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SignalWithStartWorkflowExecutionAsyncResponse struct could not be encoded. func (v *SignalWithStartWorkflowExecutionAsyncResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a SignalWithStartWorkflowExecutionAsyncResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SignalWithStartWorkflowExecutionAsyncResponse struct could not be generated from the wire // representation. func (v *SignalWithStartWorkflowExecutionAsyncResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SignalWithStartWorkflowExecutionAsyncResponse // struct. func (v *SignalWithStartWorkflowExecutionAsyncResponse) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("SignalWithStartWorkflowExecutionAsyncResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SignalWithStartWorkflowExecutionAsyncResponse match the // provided SignalWithStartWorkflowExecutionAsyncResponse. // // This function performs a deep comparison. func (v *SignalWithStartWorkflowExecutionAsyncResponse) Equals(rhs *SignalWithStartWorkflowExecutionAsyncResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalWithStartWorkflowExecutionAsyncResponse. func (v *SignalWithStartWorkflowExecutionAsyncResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type SignalWithStartWorkflowExecutionRequest struct { Domain *string `json:"domain,omitempty"` WorkflowId *string `json:"workflowId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` Identity *string `json:"identity,omitempty"` RequestId *string `json:"requestId,omitempty"` WorkflowIdReusePolicy *WorkflowIdReusePolicy `json:"workflowIdReusePolicy,omitempty"` SignalName *string `json:"signalName,omitempty"` SignalInput []byte `json:"signalInput,omitempty"` Control []byte `json:"control,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` CronSchedule *string `json:"cronSchedule,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` Header *Header `json:"header,omitempty"` DelayStartSeconds *int32 `json:"delayStartSeconds,omitempty"` JitterStartSeconds *int32 `json:"jitterStartSeconds,omitempty"` FirstRunAtTimestamp *int64 `json:"firstRunAtTimestamp,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // ToWire translates a SignalWithStartWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SignalWithStartWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [23]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ExecutionStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.TaskStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.TaskStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.WorkflowIdReusePolicy != nil { w, err = v.WorkflowIdReusePolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.SignalName != nil { w, err = wire.NewValueString(*(v.SignalName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.SignalInput != nil { w, err = wire.NewValueBinary(v.SignalInput), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.RetryPolicy != nil { w, err = v.RetryPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.CronSchedule != nil { w, err = wire.NewValueString(*(v.CronSchedule)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.Memo != nil { w, err = v.Memo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.SearchAttributes != nil { w, err = v.SearchAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 161, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } if v.DelayStartSeconds != nil { w, err = wire.NewValueI32(*(v.DelayStartSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 180, Value: w} i++ } if v.JitterStartSeconds != nil { w, err = wire.NewValueI32(*(v.JitterStartSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 190, Value: w} i++ } if v.FirstRunAtTimestamp != nil { w, err = wire.NewValueI64(*(v.FirstRunAtTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 200, Value: w} i++ } if v.CronOverlapPolicy != nil { w, err = v.CronOverlapPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 210, Value: w} i++ } if v.ActiveClusterSelectionPolicy != nil { w, err = v.ActiveClusterSelectionPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 220, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _WorkflowIdReusePolicy_Read(w wire.Value) (WorkflowIdReusePolicy, error) { var v WorkflowIdReusePolicy err := v.FromWire(w) return v, err } // FromWire deserializes a SignalWithStartWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SignalWithStartWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SignalWithStartWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SignalWithStartWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TI32 { var x WorkflowIdReusePolicy x, err = _WorkflowIdReusePolicy_Read(field.Value) v.WorkflowIdReusePolicy = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SignalName = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TBinary { v.SignalInput, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 130: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 140: if field.Value.Type() == wire.TStruct { v.RetryPolicy, err = _RetryPolicy_Read(field.Value) if err != nil { return err } } case 150: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CronSchedule = &x if err != nil { return err } } case 160: if field.Value.Type() == wire.TStruct { v.Memo, err = _Memo_Read(field.Value) if err != nil { return err } } case 161: if field.Value.Type() == wire.TStruct { v.SearchAttributes, err = _SearchAttributes_Read(field.Value) if err != nil { return err } } case 170: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } case 180: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.DelayStartSeconds = &x if err != nil { return err } } case 190: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.JitterStartSeconds = &x if err != nil { return err } } case 200: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FirstRunAtTimestamp = &x if err != nil { return err } } case 210: if field.Value.Type() == wire.TI32 { var x CronOverlapPolicy x, err = _CronOverlapPolicy_Read(field.Value) v.CronOverlapPolicy = &x if err != nil { return err } } case 220: if field.Value.Type() == wire.TStruct { v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a SignalWithStartWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SignalWithStartWorkflowExecutionRequest struct could not be encoded. func (v *SignalWithStartWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ExecutionStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.TaskStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowIdReusePolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TI32}); err != nil { return err } if err := v.WorkflowIdReusePolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SignalName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalInput != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.SignalInput); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TStruct}); err != nil { return err } if err := v.RetryPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronSchedule != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CronSchedule)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Memo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TStruct}); err != nil { return err } if err := v.Memo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 161, Type: wire.TStruct}); err != nil { return err } if err := v.SearchAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DelayStartSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 180, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.DelayStartSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.JitterStartSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 190, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.JitterStartSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstRunAtTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 200, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FirstRunAtTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronOverlapPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 210, Type: wire.TI32}); err != nil { return err } if err := v.CronOverlapPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterSelectionPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 220, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusterSelectionPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _WorkflowIdReusePolicy_Decode(sr stream.Reader) (WorkflowIdReusePolicy, error) { var v WorkflowIdReusePolicy err := v.Decode(sr) return v, err } // Decode deserializes a SignalWithStartWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SignalWithStartWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *SignalWithStartWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TI32: var x WorkflowIdReusePolicy x, err = _WorkflowIdReusePolicy_Decode(sr) v.WorkflowIdReusePolicy = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SignalName = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TBinary: v.SignalInput, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TStruct: v.RetryPolicy, err = _RetryPolicy_Decode(sr) if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CronSchedule = &x if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TStruct: v.Memo, err = _Memo_Decode(sr) if err != nil { return err } case fh.ID == 161 && fh.Type == wire.TStruct: v.SearchAttributes, err = _SearchAttributes_Decode(sr) if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } case fh.ID == 180 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.DelayStartSeconds = &x if err != nil { return err } case fh.ID == 190 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.JitterStartSeconds = &x if err != nil { return err } case fh.ID == 200 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FirstRunAtTimestamp = &x if err != nil { return err } case fh.ID == 210 && fh.Type == wire.TI32: var x CronOverlapPolicy x, err = _CronOverlapPolicy_Decode(sr) v.CronOverlapPolicy = &x if err != nil { return err } case fh.ID == 220 && fh.Type == wire.TStruct: v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SignalWithStartWorkflowExecutionRequest // struct. func (v *SignalWithStartWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [23]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ExecutionStartToCloseTimeoutSeconds: %v", *(v.ExecutionStartToCloseTimeoutSeconds)) i++ } if v.TaskStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("TaskStartToCloseTimeoutSeconds: %v", *(v.TaskStartToCloseTimeoutSeconds)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } if v.WorkflowIdReusePolicy != nil { fields[i] = fmt.Sprintf("WorkflowIdReusePolicy: %v", *(v.WorkflowIdReusePolicy)) i++ } if v.SignalName != nil { fields[i] = fmt.Sprintf("SignalName: %v", *(v.SignalName)) i++ } if v.SignalInput != nil { fields[i] = fmt.Sprintf("SignalInput: %v", v.SignalInput) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } if v.RetryPolicy != nil { fields[i] = fmt.Sprintf("RetryPolicy: %v", v.RetryPolicy) i++ } if v.CronSchedule != nil { fields[i] = fmt.Sprintf("CronSchedule: %v", *(v.CronSchedule)) i++ } if v.Memo != nil { fields[i] = fmt.Sprintf("Memo: %v", v.Memo) i++ } if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } if v.DelayStartSeconds != nil { fields[i] = fmt.Sprintf("DelayStartSeconds: %v", *(v.DelayStartSeconds)) i++ } if v.JitterStartSeconds != nil { fields[i] = fmt.Sprintf("JitterStartSeconds: %v", *(v.JitterStartSeconds)) i++ } if v.FirstRunAtTimestamp != nil { fields[i] = fmt.Sprintf("FirstRunAtTimestamp: %v", *(v.FirstRunAtTimestamp)) i++ } if v.CronOverlapPolicy != nil { fields[i] = fmt.Sprintf("CronOverlapPolicy: %v", *(v.CronOverlapPolicy)) i++ } if v.ActiveClusterSelectionPolicy != nil { fields[i] = fmt.Sprintf("ActiveClusterSelectionPolicy: %v", v.ActiveClusterSelectionPolicy) i++ } return fmt.Sprintf("SignalWithStartWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } func _WorkflowIdReusePolicy_EqualsPtr(lhs, rhs *WorkflowIdReusePolicy) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this SignalWithStartWorkflowExecutionRequest match the // provided SignalWithStartWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *SignalWithStartWorkflowExecutionRequest) Equals(rhs *SignalWithStartWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_I32_EqualsPtr(v.ExecutionStartToCloseTimeoutSeconds, rhs.ExecutionStartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.TaskStartToCloseTimeoutSeconds, rhs.TaskStartToCloseTimeoutSeconds) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } if !_WorkflowIdReusePolicy_EqualsPtr(v.WorkflowIdReusePolicy, rhs.WorkflowIdReusePolicy) { return false } if !_String_EqualsPtr(v.SignalName, rhs.SignalName) { return false } if !((v.SignalInput == nil && rhs.SignalInput == nil) || (v.SignalInput != nil && rhs.SignalInput != nil && bytes.Equal(v.SignalInput, rhs.SignalInput))) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } if !((v.RetryPolicy == nil && rhs.RetryPolicy == nil) || (v.RetryPolicy != nil && rhs.RetryPolicy != nil && v.RetryPolicy.Equals(rhs.RetryPolicy))) { return false } if !_String_EqualsPtr(v.CronSchedule, rhs.CronSchedule) { return false } if !((v.Memo == nil && rhs.Memo == nil) || (v.Memo != nil && rhs.Memo != nil && v.Memo.Equals(rhs.Memo))) { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && v.SearchAttributes.Equals(rhs.SearchAttributes))) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } if !_I32_EqualsPtr(v.DelayStartSeconds, rhs.DelayStartSeconds) { return false } if !_I32_EqualsPtr(v.JitterStartSeconds, rhs.JitterStartSeconds) { return false } if !_I64_EqualsPtr(v.FirstRunAtTimestamp, rhs.FirstRunAtTimestamp) { return false } if !_CronOverlapPolicy_EqualsPtr(v.CronOverlapPolicy, rhs.CronOverlapPolicy) { return false } if !((v.ActiveClusterSelectionPolicy == nil && rhs.ActiveClusterSelectionPolicy == nil) || (v.ActiveClusterSelectionPolicy != nil && rhs.ActiveClusterSelectionPolicy != nil && v.ActiveClusterSelectionPolicy.Equals(rhs.ActiveClusterSelectionPolicy))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalWithStartWorkflowExecutionRequest. func (v *SignalWithStartWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.ExecutionStartToCloseTimeoutSeconds != nil { enc.AddInt32("executionStartToCloseTimeoutSeconds", *v.ExecutionStartToCloseTimeoutSeconds) } if v.TaskStartToCloseTimeoutSeconds != nil { enc.AddInt32("taskStartToCloseTimeoutSeconds", *v.TaskStartToCloseTimeoutSeconds) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } if v.WorkflowIdReusePolicy != nil { err = multierr.Append(err, enc.AddObject("workflowIdReusePolicy", *v.WorkflowIdReusePolicy)) } if v.SignalName != nil { enc.AddString("signalName", *v.SignalName) } if v.SignalInput != nil { enc.AddString("signalInput", base64.StdEncoding.EncodeToString(v.SignalInput)) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } if v.RetryPolicy != nil { err = multierr.Append(err, enc.AddObject("retryPolicy", v.RetryPolicy)) } if v.CronSchedule != nil { enc.AddString("cronSchedule", *v.CronSchedule) } if v.Memo != nil { err = multierr.Append(err, enc.AddObject("memo", v.Memo)) } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", v.SearchAttributes)) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } if v.DelayStartSeconds != nil { enc.AddInt32("delayStartSeconds", *v.DelayStartSeconds) } if v.JitterStartSeconds != nil { enc.AddInt32("jitterStartSeconds", *v.JitterStartSeconds) } if v.FirstRunAtTimestamp != nil { enc.AddInt64("firstRunAtTimestamp", *v.FirstRunAtTimestamp) } if v.CronOverlapPolicy != nil { err = multierr.Append(err, enc.AddObject("cronOverlapPolicy", *v.CronOverlapPolicy)) } if v.ActiveClusterSelectionPolicy != nil { err = multierr.Append(err, enc.AddObject("activeClusterSelectionPolicy", v.ActiveClusterSelectionPolicy)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetInput() bool { return v != nil && v.Input != nil } // GetExecutionStartToCloseTimeoutSeconds returns the value of ExecutionStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // IsSetExecutionStartToCloseTimeoutSeconds returns true if ExecutionStartToCloseTimeoutSeconds is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetExecutionStartToCloseTimeoutSeconds() bool { return v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil } // GetTaskStartToCloseTimeoutSeconds returns the value of TaskStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // IsSetTaskStartToCloseTimeoutSeconds returns true if TaskStartToCloseTimeoutSeconds is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetTaskStartToCloseTimeoutSeconds() bool { return v != nil && v.TaskStartToCloseTimeoutSeconds != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetRequestId() bool { return v != nil && v.RequestId != nil } // GetWorkflowIdReusePolicy returns the value of WorkflowIdReusePolicy if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetWorkflowIdReusePolicy() (o WorkflowIdReusePolicy) { if v != nil && v.WorkflowIdReusePolicy != nil { return *v.WorkflowIdReusePolicy } return } // IsSetWorkflowIdReusePolicy returns true if WorkflowIdReusePolicy is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetWorkflowIdReusePolicy() bool { return v != nil && v.WorkflowIdReusePolicy != nil } // GetSignalName returns the value of SignalName if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetSignalName() (o string) { if v != nil && v.SignalName != nil { return *v.SignalName } return } // IsSetSignalName returns true if SignalName is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetSignalName() bool { return v != nil && v.SignalName != nil } // GetSignalInput returns the value of SignalInput if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetSignalInput() (o []byte) { if v != nil && v.SignalInput != nil { return v.SignalInput } return } // IsSetSignalInput returns true if SignalInput is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetSignalInput() bool { return v != nil && v.SignalInput != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetControl() bool { return v != nil && v.Control != nil } // GetRetryPolicy returns the value of RetryPolicy if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetRetryPolicy() (o *RetryPolicy) { if v != nil && v.RetryPolicy != nil { return v.RetryPolicy } return } // IsSetRetryPolicy returns true if RetryPolicy is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetRetryPolicy() bool { return v != nil && v.RetryPolicy != nil } // GetCronSchedule returns the value of CronSchedule if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetCronSchedule() (o string) { if v != nil && v.CronSchedule != nil { return *v.CronSchedule } return } // IsSetCronSchedule returns true if CronSchedule is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetCronSchedule() bool { return v != nil && v.CronSchedule != nil } // GetMemo returns the value of Memo if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetMemo() (o *Memo) { if v != nil && v.Memo != nil { return v.Memo } return } // IsSetMemo returns true if Memo is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetMemo() bool { return v != nil && v.Memo != nil } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetHeader() bool { return v != nil && v.Header != nil } // GetDelayStartSeconds returns the value of DelayStartSeconds if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetDelayStartSeconds() (o int32) { if v != nil && v.DelayStartSeconds != nil { return *v.DelayStartSeconds } return } // IsSetDelayStartSeconds returns true if DelayStartSeconds is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetDelayStartSeconds() bool { return v != nil && v.DelayStartSeconds != nil } // GetJitterStartSeconds returns the value of JitterStartSeconds if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetJitterStartSeconds() (o int32) { if v != nil && v.JitterStartSeconds != nil { return *v.JitterStartSeconds } return } // IsSetJitterStartSeconds returns true if JitterStartSeconds is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetJitterStartSeconds() bool { return v != nil && v.JitterStartSeconds != nil } // GetFirstRunAtTimestamp returns the value of FirstRunAtTimestamp if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetFirstRunAtTimestamp() (o int64) { if v != nil && v.FirstRunAtTimestamp != nil { return *v.FirstRunAtTimestamp } return } // IsSetFirstRunAtTimestamp returns true if FirstRunAtTimestamp is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetFirstRunAtTimestamp() bool { return v != nil && v.FirstRunAtTimestamp != nil } // GetCronOverlapPolicy returns the value of CronOverlapPolicy if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetCronOverlapPolicy() (o CronOverlapPolicy) { if v != nil && v.CronOverlapPolicy != nil { return *v.CronOverlapPolicy } return } // IsSetCronOverlapPolicy returns true if CronOverlapPolicy is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetCronOverlapPolicy() bool { return v != nil && v.CronOverlapPolicy != nil } // GetActiveClusterSelectionPolicy returns the value of ActiveClusterSelectionPolicy if it is set or its // zero value if it is unset. func (v *SignalWithStartWorkflowExecutionRequest) GetActiveClusterSelectionPolicy() (o *ActiveClusterSelectionPolicy) { if v != nil && v.ActiveClusterSelectionPolicy != nil { return v.ActiveClusterSelectionPolicy } return } // IsSetActiveClusterSelectionPolicy returns true if ActiveClusterSelectionPolicy is not nil. func (v *SignalWithStartWorkflowExecutionRequest) IsSetActiveClusterSelectionPolicy() bool { return v != nil && v.ActiveClusterSelectionPolicy != nil } type SignalWorkflowExecutionRequest struct { Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` SignalName *string `json:"signalName,omitempty"` Input []byte `json:"input,omitempty"` Identity *string `json:"identity,omitempty"` RequestId *string `json:"requestId,omitempty"` Control []byte `json:"control,omitempty"` } // ToWire translates a SignalWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SignalWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.SignalName != nil { w, err = wire.NewValueString(*(v.SignalName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a SignalWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SignalWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SignalWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SignalWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SignalName = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a SignalWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SignalWorkflowExecutionRequest struct could not be encoded. func (v *SignalWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SignalName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a SignalWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SignalWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *SignalWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SignalName = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SignalWorkflowExecutionRequest // struct. func (v *SignalWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.SignalName != nil { fields[i] = fmt.Sprintf("SignalName: %v", *(v.SignalName)) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } return fmt.Sprintf("SignalWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SignalWorkflowExecutionRequest match the // provided SignalWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *SignalWorkflowExecutionRequest) Equals(rhs *SignalWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_String_EqualsPtr(v.SignalName, rhs.SignalName) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalWorkflowExecutionRequest. func (v *SignalWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.SignalName != nil { enc.AddString("signalName", *v.SignalName) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *SignalWorkflowExecutionRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *SignalWorkflowExecutionRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetSignalName returns the value of SignalName if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetSignalName() (o string) { if v != nil && v.SignalName != nil { return *v.SignalName } return } // IsSetSignalName returns true if SignalName is not nil. func (v *SignalWorkflowExecutionRequest) IsSetSignalName() bool { return v != nil && v.SignalName != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *SignalWorkflowExecutionRequest) IsSetInput() bool { return v != nil && v.Input != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *SignalWorkflowExecutionRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *SignalWorkflowExecutionRequest) IsSetRequestId() bool { return v != nil && v.RequestId != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *SignalWorkflowExecutionRequest) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *SignalWorkflowExecutionRequest) IsSetControl() bool { return v != nil && v.Control != nil } type StartChildWorkflowExecutionDecisionAttributes struct { Domain *string `json:"domain,omitempty"` WorkflowId *string `json:"workflowId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` ParentClosePolicy *ParentClosePolicy `json:"parentClosePolicy,omitempty"` Control []byte `json:"control,omitempty"` WorkflowIdReusePolicy *WorkflowIdReusePolicy `json:"workflowIdReusePolicy,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` CronSchedule *string `json:"cronSchedule,omitempty"` Header *Header `json:"header,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // ToWire translates a StartChildWorkflowExecutionDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StartChildWorkflowExecutionDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [17]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ExecutionStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.TaskStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.TaskStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.ParentClosePolicy != nil { w, err = v.ParentClosePolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 81, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.WorkflowIdReusePolicy != nil { w, err = v.WorkflowIdReusePolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.RetryPolicy != nil { w, err = v.RetryPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.CronSchedule != nil { w, err = wire.NewValueString(*(v.CronSchedule)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.Memo != nil { w, err = v.Memo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.SearchAttributes != nil { w, err = v.SearchAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.CronOverlapPolicy != nil { w, err = v.CronOverlapPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.ActiveClusterSelectionPolicy != nil { w, err = v.ActiveClusterSelectionPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a StartChildWorkflowExecutionDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StartChildWorkflowExecutionDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StartChildWorkflowExecutionDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StartChildWorkflowExecutionDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 81: if field.Value.Type() == wire.TI32 { var x ParentClosePolicy x, err = _ParentClosePolicy_Read(field.Value) v.ParentClosePolicy = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 100: if field.Value.Type() == wire.TI32 { var x WorkflowIdReusePolicy x, err = _WorkflowIdReusePolicy_Read(field.Value) v.WorkflowIdReusePolicy = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TStruct { v.RetryPolicy, err = _RetryPolicy_Read(field.Value) if err != nil { return err } } case 120: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CronSchedule = &x if err != nil { return err } } case 130: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } case 140: if field.Value.Type() == wire.TStruct { v.Memo, err = _Memo_Read(field.Value) if err != nil { return err } } case 150: if field.Value.Type() == wire.TStruct { v.SearchAttributes, err = _SearchAttributes_Read(field.Value) if err != nil { return err } } case 160: if field.Value.Type() == wire.TI32 { var x CronOverlapPolicy x, err = _CronOverlapPolicy_Read(field.Value) v.CronOverlapPolicy = &x if err != nil { return err } } case 170: if field.Value.Type() == wire.TStruct { v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a StartChildWorkflowExecutionDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StartChildWorkflowExecutionDecisionAttributes struct could not be encoded. func (v *StartChildWorkflowExecutionDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ExecutionStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.TaskStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentClosePolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 81, Type: wire.TI32}); err != nil { return err } if err := v.ParentClosePolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowIdReusePolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TI32}); err != nil { return err } if err := v.WorkflowIdReusePolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TStruct}); err != nil { return err } if err := v.RetryPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronSchedule != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CronSchedule)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Memo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TStruct}); err != nil { return err } if err := v.Memo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TStruct}); err != nil { return err } if err := v.SearchAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronOverlapPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TI32}); err != nil { return err } if err := v.CronOverlapPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterSelectionPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusterSelectionPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a StartChildWorkflowExecutionDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StartChildWorkflowExecutionDecisionAttributes struct could not be generated from the wire // representation. func (v *StartChildWorkflowExecutionDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 81 && fh.Type == wire.TI32: var x ParentClosePolicy x, err = _ParentClosePolicy_Decode(sr) v.ParentClosePolicy = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TI32: var x WorkflowIdReusePolicy x, err = _WorkflowIdReusePolicy_Decode(sr) v.WorkflowIdReusePolicy = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TStruct: v.RetryPolicy, err = _RetryPolicy_Decode(sr) if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CronSchedule = &x if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TStruct: v.Memo, err = _Memo_Decode(sr) if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TStruct: v.SearchAttributes, err = _SearchAttributes_Decode(sr) if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TI32: var x CronOverlapPolicy x, err = _CronOverlapPolicy_Decode(sr) v.CronOverlapPolicy = &x if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TStruct: v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StartChildWorkflowExecutionDecisionAttributes // struct. func (v *StartChildWorkflowExecutionDecisionAttributes) String() string { if v == nil { return "" } var fields [17]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ExecutionStartToCloseTimeoutSeconds: %v", *(v.ExecutionStartToCloseTimeoutSeconds)) i++ } if v.TaskStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("TaskStartToCloseTimeoutSeconds: %v", *(v.TaskStartToCloseTimeoutSeconds)) i++ } if v.ParentClosePolicy != nil { fields[i] = fmt.Sprintf("ParentClosePolicy: %v", *(v.ParentClosePolicy)) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } if v.WorkflowIdReusePolicy != nil { fields[i] = fmt.Sprintf("WorkflowIdReusePolicy: %v", *(v.WorkflowIdReusePolicy)) i++ } if v.RetryPolicy != nil { fields[i] = fmt.Sprintf("RetryPolicy: %v", v.RetryPolicy) i++ } if v.CronSchedule != nil { fields[i] = fmt.Sprintf("CronSchedule: %v", *(v.CronSchedule)) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } if v.Memo != nil { fields[i] = fmt.Sprintf("Memo: %v", v.Memo) i++ } if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } if v.CronOverlapPolicy != nil { fields[i] = fmt.Sprintf("CronOverlapPolicy: %v", *(v.CronOverlapPolicy)) i++ } if v.ActiveClusterSelectionPolicy != nil { fields[i] = fmt.Sprintf("ActiveClusterSelectionPolicy: %v", v.ActiveClusterSelectionPolicy) i++ } return fmt.Sprintf("StartChildWorkflowExecutionDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this StartChildWorkflowExecutionDecisionAttributes match the // provided StartChildWorkflowExecutionDecisionAttributes. // // This function performs a deep comparison. func (v *StartChildWorkflowExecutionDecisionAttributes) Equals(rhs *StartChildWorkflowExecutionDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_I32_EqualsPtr(v.ExecutionStartToCloseTimeoutSeconds, rhs.ExecutionStartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.TaskStartToCloseTimeoutSeconds, rhs.TaskStartToCloseTimeoutSeconds) { return false } if !_ParentClosePolicy_EqualsPtr(v.ParentClosePolicy, rhs.ParentClosePolicy) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } if !_WorkflowIdReusePolicy_EqualsPtr(v.WorkflowIdReusePolicy, rhs.WorkflowIdReusePolicy) { return false } if !((v.RetryPolicy == nil && rhs.RetryPolicy == nil) || (v.RetryPolicy != nil && rhs.RetryPolicy != nil && v.RetryPolicy.Equals(rhs.RetryPolicy))) { return false } if !_String_EqualsPtr(v.CronSchedule, rhs.CronSchedule) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } if !((v.Memo == nil && rhs.Memo == nil) || (v.Memo != nil && rhs.Memo != nil && v.Memo.Equals(rhs.Memo))) { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && v.SearchAttributes.Equals(rhs.SearchAttributes))) { return false } if !_CronOverlapPolicy_EqualsPtr(v.CronOverlapPolicy, rhs.CronOverlapPolicy) { return false } if !((v.ActiveClusterSelectionPolicy == nil && rhs.ActiveClusterSelectionPolicy == nil) || (v.ActiveClusterSelectionPolicy != nil && rhs.ActiveClusterSelectionPolicy != nil && v.ActiveClusterSelectionPolicy.Equals(rhs.ActiveClusterSelectionPolicy))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StartChildWorkflowExecutionDecisionAttributes. func (v *StartChildWorkflowExecutionDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.ExecutionStartToCloseTimeoutSeconds != nil { enc.AddInt32("executionStartToCloseTimeoutSeconds", *v.ExecutionStartToCloseTimeoutSeconds) } if v.TaskStartToCloseTimeoutSeconds != nil { enc.AddInt32("taskStartToCloseTimeoutSeconds", *v.TaskStartToCloseTimeoutSeconds) } if v.ParentClosePolicy != nil { err = multierr.Append(err, enc.AddObject("parentClosePolicy", *v.ParentClosePolicy)) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } if v.WorkflowIdReusePolicy != nil { err = multierr.Append(err, enc.AddObject("workflowIdReusePolicy", *v.WorkflowIdReusePolicy)) } if v.RetryPolicy != nil { err = multierr.Append(err, enc.AddObject("retryPolicy", v.RetryPolicy)) } if v.CronSchedule != nil { enc.AddString("cronSchedule", *v.CronSchedule) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } if v.Memo != nil { err = multierr.Append(err, enc.AddObject("memo", v.Memo)) } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", v.SearchAttributes)) } if v.CronOverlapPolicy != nil { err = multierr.Append(err, enc.AddObject("cronOverlapPolicy", *v.CronOverlapPolicy)) } if v.ActiveClusterSelectionPolicy != nil { err = multierr.Append(err, enc.AddObject("activeClusterSelectionPolicy", v.ActiveClusterSelectionPolicy)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetInput() bool { return v != nil && v.Input != nil } // GetExecutionStartToCloseTimeoutSeconds returns the value of ExecutionStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // IsSetExecutionStartToCloseTimeoutSeconds returns true if ExecutionStartToCloseTimeoutSeconds is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetExecutionStartToCloseTimeoutSeconds() bool { return v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil } // GetTaskStartToCloseTimeoutSeconds returns the value of TaskStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // IsSetTaskStartToCloseTimeoutSeconds returns true if TaskStartToCloseTimeoutSeconds is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetTaskStartToCloseTimeoutSeconds() bool { return v != nil && v.TaskStartToCloseTimeoutSeconds != nil } // GetParentClosePolicy returns the value of ParentClosePolicy if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetParentClosePolicy() (o ParentClosePolicy) { if v != nil && v.ParentClosePolicy != nil { return *v.ParentClosePolicy } return } // IsSetParentClosePolicy returns true if ParentClosePolicy is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetParentClosePolicy() bool { return v != nil && v.ParentClosePolicy != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetControl() bool { return v != nil && v.Control != nil } // GetWorkflowIdReusePolicy returns the value of WorkflowIdReusePolicy if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetWorkflowIdReusePolicy() (o WorkflowIdReusePolicy) { if v != nil && v.WorkflowIdReusePolicy != nil { return *v.WorkflowIdReusePolicy } return } // IsSetWorkflowIdReusePolicy returns true if WorkflowIdReusePolicy is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetWorkflowIdReusePolicy() bool { return v != nil && v.WorkflowIdReusePolicy != nil } // GetRetryPolicy returns the value of RetryPolicy if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetRetryPolicy() (o *RetryPolicy) { if v != nil && v.RetryPolicy != nil { return v.RetryPolicy } return } // IsSetRetryPolicy returns true if RetryPolicy is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetRetryPolicy() bool { return v != nil && v.RetryPolicy != nil } // GetCronSchedule returns the value of CronSchedule if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetCronSchedule() (o string) { if v != nil && v.CronSchedule != nil { return *v.CronSchedule } return } // IsSetCronSchedule returns true if CronSchedule is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetCronSchedule() bool { return v != nil && v.CronSchedule != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetHeader() bool { return v != nil && v.Header != nil } // GetMemo returns the value of Memo if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetMemo() (o *Memo) { if v != nil && v.Memo != nil { return v.Memo } return } // IsSetMemo returns true if Memo is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetMemo() bool { return v != nil && v.Memo != nil } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } // GetCronOverlapPolicy returns the value of CronOverlapPolicy if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetCronOverlapPolicy() (o CronOverlapPolicy) { if v != nil && v.CronOverlapPolicy != nil { return *v.CronOverlapPolicy } return } // IsSetCronOverlapPolicy returns true if CronOverlapPolicy is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetCronOverlapPolicy() bool { return v != nil && v.CronOverlapPolicy != nil } // GetActiveClusterSelectionPolicy returns the value of ActiveClusterSelectionPolicy if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionDecisionAttributes) GetActiveClusterSelectionPolicy() (o *ActiveClusterSelectionPolicy) { if v != nil && v.ActiveClusterSelectionPolicy != nil { return v.ActiveClusterSelectionPolicy } return } // IsSetActiveClusterSelectionPolicy returns true if ActiveClusterSelectionPolicy is not nil. func (v *StartChildWorkflowExecutionDecisionAttributes) IsSetActiveClusterSelectionPolicy() bool { return v != nil && v.ActiveClusterSelectionPolicy != nil } type StartChildWorkflowExecutionFailedEventAttributes struct { Domain *string `json:"domain,omitempty"` WorkflowId *string `json:"workflowId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` Cause *ChildWorkflowExecutionFailedCause `json:"cause,omitempty"` Control []byte `json:"control,omitempty"` InitiatedEventId *int64 `json:"initiatedEventId,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` } // ToWire translates a StartChildWorkflowExecutionFailedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StartChildWorkflowExecutionFailedEventAttributes) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Cause != nil { w, err = v.Cause.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.InitiatedEventId != nil { w, err = wire.NewValueI64(*(v.InitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ChildWorkflowExecutionFailedCause_Read(w wire.Value) (ChildWorkflowExecutionFailedCause, error) { var v ChildWorkflowExecutionFailedCause err := v.FromWire(w) return v, err } // FromWire deserializes a StartChildWorkflowExecutionFailedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StartChildWorkflowExecutionFailedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StartChildWorkflowExecutionFailedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StartChildWorkflowExecutionFailedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x ChildWorkflowExecutionFailedCause x, err = _ChildWorkflowExecutionFailedCause_Read(field.Value) v.Cause = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventId = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a StartChildWorkflowExecutionFailedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StartChildWorkflowExecutionFailedEventAttributes struct could not be encoded. func (v *StartChildWorkflowExecutionFailedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Cause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := v.Cause.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ChildWorkflowExecutionFailedCause_Decode(sr stream.Reader) (ChildWorkflowExecutionFailedCause, error) { var v ChildWorkflowExecutionFailedCause err := v.Decode(sr) return v, err } // Decode deserializes a StartChildWorkflowExecutionFailedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StartChildWorkflowExecutionFailedEventAttributes struct could not be generated from the wire // representation. func (v *StartChildWorkflowExecutionFailedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x ChildWorkflowExecutionFailedCause x, err = _ChildWorkflowExecutionFailedCause_Decode(sr) v.Cause = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventId = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StartChildWorkflowExecutionFailedEventAttributes // struct. func (v *StartChildWorkflowExecutionFailedEventAttributes) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.Cause != nil { fields[i] = fmt.Sprintf("Cause: %v", *(v.Cause)) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } if v.InitiatedEventId != nil { fields[i] = fmt.Sprintf("InitiatedEventId: %v", *(v.InitiatedEventId)) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } return fmt.Sprintf("StartChildWorkflowExecutionFailedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } func _ChildWorkflowExecutionFailedCause_EqualsPtr(lhs, rhs *ChildWorkflowExecutionFailedCause) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this StartChildWorkflowExecutionFailedEventAttributes match the // provided StartChildWorkflowExecutionFailedEventAttributes. // // This function performs a deep comparison. func (v *StartChildWorkflowExecutionFailedEventAttributes) Equals(rhs *StartChildWorkflowExecutionFailedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_ChildWorkflowExecutionFailedCause_EqualsPtr(v.Cause, rhs.Cause) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } if !_I64_EqualsPtr(v.InitiatedEventId, rhs.InitiatedEventId) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StartChildWorkflowExecutionFailedEventAttributes. func (v *StartChildWorkflowExecutionFailedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.Cause != nil { err = multierr.Append(err, enc.AddObject("cause", *v.Cause)) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } if v.InitiatedEventId != nil { enc.AddInt64("initiatedEventId", *v.InitiatedEventId) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionFailedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *StartChildWorkflowExecutionFailedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionFailedEventAttributes) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *StartChildWorkflowExecutionFailedEventAttributes) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionFailedEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *StartChildWorkflowExecutionFailedEventAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetCause returns the value of Cause if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionFailedEventAttributes) GetCause() (o ChildWorkflowExecutionFailedCause) { if v != nil && v.Cause != nil { return *v.Cause } return } // IsSetCause returns true if Cause is not nil. func (v *StartChildWorkflowExecutionFailedEventAttributes) IsSetCause() bool { return v != nil && v.Cause != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionFailedEventAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *StartChildWorkflowExecutionFailedEventAttributes) IsSetControl() bool { return v != nil && v.Control != nil } // GetInitiatedEventId returns the value of InitiatedEventId if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionFailedEventAttributes) GetInitiatedEventId() (o int64) { if v != nil && v.InitiatedEventId != nil { return *v.InitiatedEventId } return } // IsSetInitiatedEventId returns true if InitiatedEventId is not nil. func (v *StartChildWorkflowExecutionFailedEventAttributes) IsSetInitiatedEventId() bool { return v != nil && v.InitiatedEventId != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionFailedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *StartChildWorkflowExecutionFailedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } type StartChildWorkflowExecutionInitiatedEventAttributes struct { Domain *string `json:"domain,omitempty"` WorkflowId *string `json:"workflowId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` ParentClosePolicy *ParentClosePolicy `json:"parentClosePolicy,omitempty"` Control []byte `json:"control,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` WorkflowIdReusePolicy *WorkflowIdReusePolicy `json:"workflowIdReusePolicy,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` CronSchedule *string `json:"cronSchedule,omitempty"` Header *Header `json:"header,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` DelayStartSeconds *int32 `json:"delayStartSeconds,omitempty"` JitterStartSeconds *int32 `json:"jitterStartSeconds,omitempty"` FirstRunAtTimestamp *int64 `json:"firstRunAtTimestamp,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // ToWire translates a StartChildWorkflowExecutionInitiatedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StartChildWorkflowExecutionInitiatedEventAttributes) ToWire() (wire.Value, error) { var ( fields [21]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ExecutionStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.TaskStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.TaskStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.ParentClosePolicy != nil { w, err = v.ParentClosePolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 81, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.WorkflowIdReusePolicy != nil { w, err = v.WorkflowIdReusePolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.RetryPolicy != nil { w, err = v.RetryPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.CronSchedule != nil { w, err = wire.NewValueString(*(v.CronSchedule)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.Memo != nil { w, err = v.Memo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.SearchAttributes != nil { w, err = v.SearchAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.DelayStartSeconds != nil { w, err = wire.NewValueI32(*(v.DelayStartSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } if v.JitterStartSeconds != nil { w, err = wire.NewValueI32(*(v.JitterStartSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 180, Value: w} i++ } if v.FirstRunAtTimestamp != nil { w, err = wire.NewValueI64(*(v.FirstRunAtTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 190, Value: w} i++ } if v.CronOverlapPolicy != nil { w, err = v.CronOverlapPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 200, Value: w} i++ } if v.ActiveClusterSelectionPolicy != nil { w, err = v.ActiveClusterSelectionPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 210, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a StartChildWorkflowExecutionInitiatedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StartChildWorkflowExecutionInitiatedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StartChildWorkflowExecutionInitiatedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StartChildWorkflowExecutionInitiatedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 81: if field.Value.Type() == wire.TI32 { var x ParentClosePolicy x, err = _ParentClosePolicy_Read(field.Value) v.ParentClosePolicy = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 100: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TI32 { var x WorkflowIdReusePolicy x, err = _WorkflowIdReusePolicy_Read(field.Value) v.WorkflowIdReusePolicy = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TStruct { v.RetryPolicy, err = _RetryPolicy_Read(field.Value) if err != nil { return err } } case 130: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CronSchedule = &x if err != nil { return err } } case 140: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } case 150: if field.Value.Type() == wire.TStruct { v.Memo, err = _Memo_Read(field.Value) if err != nil { return err } } case 160: if field.Value.Type() == wire.TStruct { v.SearchAttributes, err = _SearchAttributes_Read(field.Value) if err != nil { return err } } case 170: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.DelayStartSeconds = &x if err != nil { return err } } case 180: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.JitterStartSeconds = &x if err != nil { return err } } case 190: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FirstRunAtTimestamp = &x if err != nil { return err } } case 200: if field.Value.Type() == wire.TI32 { var x CronOverlapPolicy x, err = _CronOverlapPolicy_Read(field.Value) v.CronOverlapPolicy = &x if err != nil { return err } } case 210: if field.Value.Type() == wire.TStruct { v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a StartChildWorkflowExecutionInitiatedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StartChildWorkflowExecutionInitiatedEventAttributes struct could not be encoded. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ExecutionStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.TaskStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentClosePolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 81, Type: wire.TI32}); err != nil { return err } if err := v.ParentClosePolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowIdReusePolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TI32}); err != nil { return err } if err := v.WorkflowIdReusePolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TStruct}); err != nil { return err } if err := v.RetryPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronSchedule != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CronSchedule)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Memo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TStruct}); err != nil { return err } if err := v.Memo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TStruct}); err != nil { return err } if err := v.SearchAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DelayStartSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.DelayStartSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.JitterStartSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 180, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.JitterStartSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstRunAtTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 190, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FirstRunAtTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronOverlapPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 200, Type: wire.TI32}); err != nil { return err } if err := v.CronOverlapPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterSelectionPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 210, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusterSelectionPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a StartChildWorkflowExecutionInitiatedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StartChildWorkflowExecutionInitiatedEventAttributes struct could not be generated from the wire // representation. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 81 && fh.Type == wire.TI32: var x ParentClosePolicy x, err = _ParentClosePolicy_Decode(sr) v.ParentClosePolicy = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TI32: var x WorkflowIdReusePolicy x, err = _WorkflowIdReusePolicy_Decode(sr) v.WorkflowIdReusePolicy = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TStruct: v.RetryPolicy, err = _RetryPolicy_Decode(sr) if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CronSchedule = &x if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TStruct: v.Memo, err = _Memo_Decode(sr) if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TStruct: v.SearchAttributes, err = _SearchAttributes_Decode(sr) if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.DelayStartSeconds = &x if err != nil { return err } case fh.ID == 180 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.JitterStartSeconds = &x if err != nil { return err } case fh.ID == 190 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FirstRunAtTimestamp = &x if err != nil { return err } case fh.ID == 200 && fh.Type == wire.TI32: var x CronOverlapPolicy x, err = _CronOverlapPolicy_Decode(sr) v.CronOverlapPolicy = &x if err != nil { return err } case fh.ID == 210 && fh.Type == wire.TStruct: v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StartChildWorkflowExecutionInitiatedEventAttributes // struct. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) String() string { if v == nil { return "" } var fields [21]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ExecutionStartToCloseTimeoutSeconds: %v", *(v.ExecutionStartToCloseTimeoutSeconds)) i++ } if v.TaskStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("TaskStartToCloseTimeoutSeconds: %v", *(v.TaskStartToCloseTimeoutSeconds)) i++ } if v.ParentClosePolicy != nil { fields[i] = fmt.Sprintf("ParentClosePolicy: %v", *(v.ParentClosePolicy)) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.WorkflowIdReusePolicy != nil { fields[i] = fmt.Sprintf("WorkflowIdReusePolicy: %v", *(v.WorkflowIdReusePolicy)) i++ } if v.RetryPolicy != nil { fields[i] = fmt.Sprintf("RetryPolicy: %v", v.RetryPolicy) i++ } if v.CronSchedule != nil { fields[i] = fmt.Sprintf("CronSchedule: %v", *(v.CronSchedule)) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } if v.Memo != nil { fields[i] = fmt.Sprintf("Memo: %v", v.Memo) i++ } if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } if v.DelayStartSeconds != nil { fields[i] = fmt.Sprintf("DelayStartSeconds: %v", *(v.DelayStartSeconds)) i++ } if v.JitterStartSeconds != nil { fields[i] = fmt.Sprintf("JitterStartSeconds: %v", *(v.JitterStartSeconds)) i++ } if v.FirstRunAtTimestamp != nil { fields[i] = fmt.Sprintf("FirstRunAtTimestamp: %v", *(v.FirstRunAtTimestamp)) i++ } if v.CronOverlapPolicy != nil { fields[i] = fmt.Sprintf("CronOverlapPolicy: %v", *(v.CronOverlapPolicy)) i++ } if v.ActiveClusterSelectionPolicy != nil { fields[i] = fmt.Sprintf("ActiveClusterSelectionPolicy: %v", v.ActiveClusterSelectionPolicy) i++ } return fmt.Sprintf("StartChildWorkflowExecutionInitiatedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this StartChildWorkflowExecutionInitiatedEventAttributes match the // provided StartChildWorkflowExecutionInitiatedEventAttributes. // // This function performs a deep comparison. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) Equals(rhs *StartChildWorkflowExecutionInitiatedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_I32_EqualsPtr(v.ExecutionStartToCloseTimeoutSeconds, rhs.ExecutionStartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.TaskStartToCloseTimeoutSeconds, rhs.TaskStartToCloseTimeoutSeconds) { return false } if !_ParentClosePolicy_EqualsPtr(v.ParentClosePolicy, rhs.ParentClosePolicy) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !_WorkflowIdReusePolicy_EqualsPtr(v.WorkflowIdReusePolicy, rhs.WorkflowIdReusePolicy) { return false } if !((v.RetryPolicy == nil && rhs.RetryPolicy == nil) || (v.RetryPolicy != nil && rhs.RetryPolicy != nil && v.RetryPolicy.Equals(rhs.RetryPolicy))) { return false } if !_String_EqualsPtr(v.CronSchedule, rhs.CronSchedule) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } if !((v.Memo == nil && rhs.Memo == nil) || (v.Memo != nil && rhs.Memo != nil && v.Memo.Equals(rhs.Memo))) { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && v.SearchAttributes.Equals(rhs.SearchAttributes))) { return false } if !_I32_EqualsPtr(v.DelayStartSeconds, rhs.DelayStartSeconds) { return false } if !_I32_EqualsPtr(v.JitterStartSeconds, rhs.JitterStartSeconds) { return false } if !_I64_EqualsPtr(v.FirstRunAtTimestamp, rhs.FirstRunAtTimestamp) { return false } if !_CronOverlapPolicy_EqualsPtr(v.CronOverlapPolicy, rhs.CronOverlapPolicy) { return false } if !((v.ActiveClusterSelectionPolicy == nil && rhs.ActiveClusterSelectionPolicy == nil) || (v.ActiveClusterSelectionPolicy != nil && rhs.ActiveClusterSelectionPolicy != nil && v.ActiveClusterSelectionPolicy.Equals(rhs.ActiveClusterSelectionPolicy))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StartChildWorkflowExecutionInitiatedEventAttributes. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.ExecutionStartToCloseTimeoutSeconds != nil { enc.AddInt32("executionStartToCloseTimeoutSeconds", *v.ExecutionStartToCloseTimeoutSeconds) } if v.TaskStartToCloseTimeoutSeconds != nil { enc.AddInt32("taskStartToCloseTimeoutSeconds", *v.TaskStartToCloseTimeoutSeconds) } if v.ParentClosePolicy != nil { err = multierr.Append(err, enc.AddObject("parentClosePolicy", *v.ParentClosePolicy)) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.WorkflowIdReusePolicy != nil { err = multierr.Append(err, enc.AddObject("workflowIdReusePolicy", *v.WorkflowIdReusePolicy)) } if v.RetryPolicy != nil { err = multierr.Append(err, enc.AddObject("retryPolicy", v.RetryPolicy)) } if v.CronSchedule != nil { enc.AddString("cronSchedule", *v.CronSchedule) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } if v.Memo != nil { err = multierr.Append(err, enc.AddObject("memo", v.Memo)) } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", v.SearchAttributes)) } if v.DelayStartSeconds != nil { enc.AddInt32("delayStartSeconds", *v.DelayStartSeconds) } if v.JitterStartSeconds != nil { enc.AddInt32("jitterStartSeconds", *v.JitterStartSeconds) } if v.FirstRunAtTimestamp != nil { enc.AddInt64("firstRunAtTimestamp", *v.FirstRunAtTimestamp) } if v.CronOverlapPolicy != nil { err = multierr.Append(err, enc.AddObject("cronOverlapPolicy", *v.CronOverlapPolicy)) } if v.ActiveClusterSelectionPolicy != nil { err = multierr.Append(err, enc.AddObject("activeClusterSelectionPolicy", v.ActiveClusterSelectionPolicy)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetInput() bool { return v != nil && v.Input != nil } // GetExecutionStartToCloseTimeoutSeconds returns the value of ExecutionStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // IsSetExecutionStartToCloseTimeoutSeconds returns true if ExecutionStartToCloseTimeoutSeconds is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetExecutionStartToCloseTimeoutSeconds() bool { return v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil } // GetTaskStartToCloseTimeoutSeconds returns the value of TaskStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // IsSetTaskStartToCloseTimeoutSeconds returns true if TaskStartToCloseTimeoutSeconds is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetTaskStartToCloseTimeoutSeconds() bool { return v != nil && v.TaskStartToCloseTimeoutSeconds != nil } // GetParentClosePolicy returns the value of ParentClosePolicy if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetParentClosePolicy() (o ParentClosePolicy) { if v != nil && v.ParentClosePolicy != nil { return *v.ParentClosePolicy } return } // IsSetParentClosePolicy returns true if ParentClosePolicy is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetParentClosePolicy() bool { return v != nil && v.ParentClosePolicy != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetControl() bool { return v != nil && v.Control != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetWorkflowIdReusePolicy returns the value of WorkflowIdReusePolicy if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetWorkflowIdReusePolicy() (o WorkflowIdReusePolicy) { if v != nil && v.WorkflowIdReusePolicy != nil { return *v.WorkflowIdReusePolicy } return } // IsSetWorkflowIdReusePolicy returns true if WorkflowIdReusePolicy is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetWorkflowIdReusePolicy() bool { return v != nil && v.WorkflowIdReusePolicy != nil } // GetRetryPolicy returns the value of RetryPolicy if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetRetryPolicy() (o *RetryPolicy) { if v != nil && v.RetryPolicy != nil { return v.RetryPolicy } return } // IsSetRetryPolicy returns true if RetryPolicy is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetRetryPolicy() bool { return v != nil && v.RetryPolicy != nil } // GetCronSchedule returns the value of CronSchedule if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetCronSchedule() (o string) { if v != nil && v.CronSchedule != nil { return *v.CronSchedule } return } // IsSetCronSchedule returns true if CronSchedule is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetCronSchedule() bool { return v != nil && v.CronSchedule != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetHeader() bool { return v != nil && v.Header != nil } // GetMemo returns the value of Memo if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetMemo() (o *Memo) { if v != nil && v.Memo != nil { return v.Memo } return } // IsSetMemo returns true if Memo is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetMemo() bool { return v != nil && v.Memo != nil } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } // GetDelayStartSeconds returns the value of DelayStartSeconds if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetDelayStartSeconds() (o int32) { if v != nil && v.DelayStartSeconds != nil { return *v.DelayStartSeconds } return } // IsSetDelayStartSeconds returns true if DelayStartSeconds is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetDelayStartSeconds() bool { return v != nil && v.DelayStartSeconds != nil } // GetJitterStartSeconds returns the value of JitterStartSeconds if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetJitterStartSeconds() (o int32) { if v != nil && v.JitterStartSeconds != nil { return *v.JitterStartSeconds } return } // IsSetJitterStartSeconds returns true if JitterStartSeconds is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetJitterStartSeconds() bool { return v != nil && v.JitterStartSeconds != nil } // GetFirstRunAtTimestamp returns the value of FirstRunAtTimestamp if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetFirstRunAtTimestamp() (o int64) { if v != nil && v.FirstRunAtTimestamp != nil { return *v.FirstRunAtTimestamp } return } // IsSetFirstRunAtTimestamp returns true if FirstRunAtTimestamp is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetFirstRunAtTimestamp() bool { return v != nil && v.FirstRunAtTimestamp != nil } // GetCronOverlapPolicy returns the value of CronOverlapPolicy if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetCronOverlapPolicy() (o CronOverlapPolicy) { if v != nil && v.CronOverlapPolicy != nil { return *v.CronOverlapPolicy } return } // IsSetCronOverlapPolicy returns true if CronOverlapPolicy is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetCronOverlapPolicy() bool { return v != nil && v.CronOverlapPolicy != nil } // GetActiveClusterSelectionPolicy returns the value of ActiveClusterSelectionPolicy if it is set or its // zero value if it is unset. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetActiveClusterSelectionPolicy() (o *ActiveClusterSelectionPolicy) { if v != nil && v.ActiveClusterSelectionPolicy != nil { return v.ActiveClusterSelectionPolicy } return } // IsSetActiveClusterSelectionPolicy returns true if ActiveClusterSelectionPolicy is not nil. func (v *StartChildWorkflowExecutionInitiatedEventAttributes) IsSetActiveClusterSelectionPolicy() bool { return v != nil && v.ActiveClusterSelectionPolicy != nil } type StartTimeFilter struct { EarliestTime *int64 `json:"earliestTime,omitempty"` LatestTime *int64 `json:"latestTime,omitempty"` } // ToWire translates a StartTimeFilter struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StartTimeFilter) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.EarliestTime != nil { w, err = wire.NewValueI64(*(v.EarliestTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.LatestTime != nil { w, err = wire.NewValueI64(*(v.LatestTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a StartTimeFilter struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StartTimeFilter struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StartTimeFilter // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StartTimeFilter) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EarliestTime = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LatestTime = &x if err != nil { return err } } } } return nil } // Encode serializes a StartTimeFilter struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StartTimeFilter struct could not be encoded. func (v *StartTimeFilter) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.EarliestTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EarliestTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LatestTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LatestTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a StartTimeFilter struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StartTimeFilter struct could not be generated from the wire // representation. func (v *StartTimeFilter) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EarliestTime = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LatestTime = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StartTimeFilter // struct. func (v *StartTimeFilter) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.EarliestTime != nil { fields[i] = fmt.Sprintf("EarliestTime: %v", *(v.EarliestTime)) i++ } if v.LatestTime != nil { fields[i] = fmt.Sprintf("LatestTime: %v", *(v.LatestTime)) i++ } return fmt.Sprintf("StartTimeFilter{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this StartTimeFilter match the // provided StartTimeFilter. // // This function performs a deep comparison. func (v *StartTimeFilter) Equals(rhs *StartTimeFilter) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.EarliestTime, rhs.EarliestTime) { return false } if !_I64_EqualsPtr(v.LatestTime, rhs.LatestTime) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StartTimeFilter. func (v *StartTimeFilter) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.EarliestTime != nil { enc.AddInt64("earliestTime", *v.EarliestTime) } if v.LatestTime != nil { enc.AddInt64("latestTime", *v.LatestTime) } return err } // GetEarliestTime returns the value of EarliestTime if it is set or its // zero value if it is unset. func (v *StartTimeFilter) GetEarliestTime() (o int64) { if v != nil && v.EarliestTime != nil { return *v.EarliestTime } return } // IsSetEarliestTime returns true if EarliestTime is not nil. func (v *StartTimeFilter) IsSetEarliestTime() bool { return v != nil && v.EarliestTime != nil } // GetLatestTime returns the value of LatestTime if it is set or its // zero value if it is unset. func (v *StartTimeFilter) GetLatestTime() (o int64) { if v != nil && v.LatestTime != nil { return *v.LatestTime } return } // IsSetLatestTime returns true if LatestTime is not nil. func (v *StartTimeFilter) IsSetLatestTime() bool { return v != nil && v.LatestTime != nil } type StartTimerDecisionAttributes struct { TimerId *string `json:"timerId,omitempty"` StartToFireTimeoutSeconds *int64 `json:"startToFireTimeoutSeconds,omitempty"` } // ToWire translates a StartTimerDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StartTimerDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.TimerId != nil { w, err = wire.NewValueString(*(v.TimerId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartToFireTimeoutSeconds != nil { w, err = wire.NewValueI64(*(v.StartToFireTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a StartTimerDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StartTimerDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StartTimerDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StartTimerDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TimerId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartToFireTimeoutSeconds = &x if err != nil { return err } } } } return nil } // Encode serializes a StartTimerDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StartTimerDecisionAttributes struct could not be encoded. func (v *StartTimerDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TimerId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TimerId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartToFireTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartToFireTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a StartTimerDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StartTimerDecisionAttributes struct could not be generated from the wire // representation. func (v *StartTimerDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TimerId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartToFireTimeoutSeconds = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StartTimerDecisionAttributes // struct. func (v *StartTimerDecisionAttributes) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.TimerId != nil { fields[i] = fmt.Sprintf("TimerId: %v", *(v.TimerId)) i++ } if v.StartToFireTimeoutSeconds != nil { fields[i] = fmt.Sprintf("StartToFireTimeoutSeconds: %v", *(v.StartToFireTimeoutSeconds)) i++ } return fmt.Sprintf("StartTimerDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this StartTimerDecisionAttributes match the // provided StartTimerDecisionAttributes. // // This function performs a deep comparison. func (v *StartTimerDecisionAttributes) Equals(rhs *StartTimerDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TimerId, rhs.TimerId) { return false } if !_I64_EqualsPtr(v.StartToFireTimeoutSeconds, rhs.StartToFireTimeoutSeconds) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StartTimerDecisionAttributes. func (v *StartTimerDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TimerId != nil { enc.AddString("timerId", *v.TimerId) } if v.StartToFireTimeoutSeconds != nil { enc.AddInt64("startToFireTimeoutSeconds", *v.StartToFireTimeoutSeconds) } return err } // GetTimerId returns the value of TimerId if it is set or its // zero value if it is unset. func (v *StartTimerDecisionAttributes) GetTimerId() (o string) { if v != nil && v.TimerId != nil { return *v.TimerId } return } // IsSetTimerId returns true if TimerId is not nil. func (v *StartTimerDecisionAttributes) IsSetTimerId() bool { return v != nil && v.TimerId != nil } // GetStartToFireTimeoutSeconds returns the value of StartToFireTimeoutSeconds if it is set or its // zero value if it is unset. func (v *StartTimerDecisionAttributes) GetStartToFireTimeoutSeconds() (o int64) { if v != nil && v.StartToFireTimeoutSeconds != nil { return *v.StartToFireTimeoutSeconds } return } // IsSetStartToFireTimeoutSeconds returns true if StartToFireTimeoutSeconds is not nil. func (v *StartTimerDecisionAttributes) IsSetStartToFireTimeoutSeconds() bool { return v != nil && v.StartToFireTimeoutSeconds != nil } type StartWorkflowExecutionAsyncRequest struct { Request *StartWorkflowExecutionRequest `json:"request,omitempty"` } // ToWire translates a StartWorkflowExecutionAsyncRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StartWorkflowExecutionAsyncRequest) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Request != nil { w, err = v.Request.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _StartWorkflowExecutionRequest_Read(w wire.Value) (*StartWorkflowExecutionRequest, error) { var v StartWorkflowExecutionRequest err := v.FromWire(w) return &v, err } // FromWire deserializes a StartWorkflowExecutionAsyncRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StartWorkflowExecutionAsyncRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StartWorkflowExecutionAsyncRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StartWorkflowExecutionAsyncRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Request, err = _StartWorkflowExecutionRequest_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a StartWorkflowExecutionAsyncRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StartWorkflowExecutionAsyncRequest struct could not be encoded. func (v *StartWorkflowExecutionAsyncRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Request != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Request.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _StartWorkflowExecutionRequest_Decode(sr stream.Reader) (*StartWorkflowExecutionRequest, error) { var v StartWorkflowExecutionRequest err := v.Decode(sr) return &v, err } // Decode deserializes a StartWorkflowExecutionAsyncRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StartWorkflowExecutionAsyncRequest struct could not be generated from the wire // representation. func (v *StartWorkflowExecutionAsyncRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Request, err = _StartWorkflowExecutionRequest_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StartWorkflowExecutionAsyncRequest // struct. func (v *StartWorkflowExecutionAsyncRequest) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Request != nil { fields[i] = fmt.Sprintf("Request: %v", v.Request) i++ } return fmt.Sprintf("StartWorkflowExecutionAsyncRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this StartWorkflowExecutionAsyncRequest match the // provided StartWorkflowExecutionAsyncRequest. // // This function performs a deep comparison. func (v *StartWorkflowExecutionAsyncRequest) Equals(rhs *StartWorkflowExecutionAsyncRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Request == nil && rhs.Request == nil) || (v.Request != nil && rhs.Request != nil && v.Request.Equals(rhs.Request))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StartWorkflowExecutionAsyncRequest. func (v *StartWorkflowExecutionAsyncRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Request != nil { err = multierr.Append(err, enc.AddObject("request", v.Request)) } return err } // GetRequest returns the value of Request if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionAsyncRequest) GetRequest() (o *StartWorkflowExecutionRequest) { if v != nil && v.Request != nil { return v.Request } return } // IsSetRequest returns true if Request is not nil. func (v *StartWorkflowExecutionAsyncRequest) IsSetRequest() bool { return v != nil && v.Request != nil } type StartWorkflowExecutionAsyncResponse struct { } // ToWire translates a StartWorkflowExecutionAsyncResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StartWorkflowExecutionAsyncResponse) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a StartWorkflowExecutionAsyncResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StartWorkflowExecutionAsyncResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StartWorkflowExecutionAsyncResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StartWorkflowExecutionAsyncResponse) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a StartWorkflowExecutionAsyncResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StartWorkflowExecutionAsyncResponse struct could not be encoded. func (v *StartWorkflowExecutionAsyncResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a StartWorkflowExecutionAsyncResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StartWorkflowExecutionAsyncResponse struct could not be generated from the wire // representation. func (v *StartWorkflowExecutionAsyncResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StartWorkflowExecutionAsyncResponse // struct. func (v *StartWorkflowExecutionAsyncResponse) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("StartWorkflowExecutionAsyncResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this StartWorkflowExecutionAsyncResponse match the // provided StartWorkflowExecutionAsyncResponse. // // This function performs a deep comparison. func (v *StartWorkflowExecutionAsyncResponse) Equals(rhs *StartWorkflowExecutionAsyncResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StartWorkflowExecutionAsyncResponse. func (v *StartWorkflowExecutionAsyncResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type StartWorkflowExecutionRequest struct { Domain *string `json:"domain,omitempty"` WorkflowId *string `json:"workflowId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` Identity *string `json:"identity,omitempty"` RequestId *string `json:"requestId,omitempty"` WorkflowIdReusePolicy *WorkflowIdReusePolicy `json:"workflowIdReusePolicy,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` CronSchedule *string `json:"cronSchedule,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` Header *Header `json:"header,omitempty"` DelayStartSeconds *int32 `json:"delayStartSeconds,omitempty"` JitterStartSeconds *int32 `json:"jitterStartSeconds,omitempty"` FirstRunAtTimestamp *int64 `json:"firstRunAtTimestamp,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // ToWire translates a StartWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StartWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [20]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ExecutionStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.TaskStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.TaskStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.WorkflowIdReusePolicy != nil { w, err = v.WorkflowIdReusePolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.RetryPolicy != nil { w, err = v.RetryPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.CronSchedule != nil { w, err = wire.NewValueString(*(v.CronSchedule)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.Memo != nil { w, err = v.Memo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.SearchAttributes != nil { w, err = v.SearchAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 141, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.DelayStartSeconds != nil { w, err = wire.NewValueI32(*(v.DelayStartSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.JitterStartSeconds != nil { w, err = wire.NewValueI32(*(v.JitterStartSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } if v.FirstRunAtTimestamp != nil { w, err = wire.NewValueI64(*(v.FirstRunAtTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 180, Value: w} i++ } if v.CronOverlapPolicy != nil { w, err = v.CronOverlapPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 190, Value: w} i++ } if v.ActiveClusterSelectionPolicy != nil { w, err = v.ActiveClusterSelectionPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 200, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a StartWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StartWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StartWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StartWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TI32 { var x WorkflowIdReusePolicy x, err = _WorkflowIdReusePolicy_Read(field.Value) v.WorkflowIdReusePolicy = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TStruct { v.RetryPolicy, err = _RetryPolicy_Read(field.Value) if err != nil { return err } } case 130: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CronSchedule = &x if err != nil { return err } } case 140: if field.Value.Type() == wire.TStruct { v.Memo, err = _Memo_Read(field.Value) if err != nil { return err } } case 141: if field.Value.Type() == wire.TStruct { v.SearchAttributes, err = _SearchAttributes_Read(field.Value) if err != nil { return err } } case 150: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } case 160: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.DelayStartSeconds = &x if err != nil { return err } } case 170: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.JitterStartSeconds = &x if err != nil { return err } } case 180: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FirstRunAtTimestamp = &x if err != nil { return err } } case 190: if field.Value.Type() == wire.TI32 { var x CronOverlapPolicy x, err = _CronOverlapPolicy_Read(field.Value) v.CronOverlapPolicy = &x if err != nil { return err } } case 200: if field.Value.Type() == wire.TStruct { v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a StartWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StartWorkflowExecutionRequest struct could not be encoded. func (v *StartWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ExecutionStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.TaskStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowIdReusePolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TI32}); err != nil { return err } if err := v.WorkflowIdReusePolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TStruct}); err != nil { return err } if err := v.RetryPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronSchedule != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CronSchedule)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Memo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TStruct}); err != nil { return err } if err := v.Memo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 141, Type: wire.TStruct}); err != nil { return err } if err := v.SearchAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DelayStartSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.DelayStartSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.JitterStartSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.JitterStartSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstRunAtTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 180, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FirstRunAtTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronOverlapPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 190, Type: wire.TI32}); err != nil { return err } if err := v.CronOverlapPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterSelectionPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 200, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusterSelectionPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a StartWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StartWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *StartWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TI32: var x WorkflowIdReusePolicy x, err = _WorkflowIdReusePolicy_Decode(sr) v.WorkflowIdReusePolicy = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TStruct: v.RetryPolicy, err = _RetryPolicy_Decode(sr) if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CronSchedule = &x if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TStruct: v.Memo, err = _Memo_Decode(sr) if err != nil { return err } case fh.ID == 141 && fh.Type == wire.TStruct: v.SearchAttributes, err = _SearchAttributes_Decode(sr) if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.DelayStartSeconds = &x if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.JitterStartSeconds = &x if err != nil { return err } case fh.ID == 180 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FirstRunAtTimestamp = &x if err != nil { return err } case fh.ID == 190 && fh.Type == wire.TI32: var x CronOverlapPolicy x, err = _CronOverlapPolicy_Decode(sr) v.CronOverlapPolicy = &x if err != nil { return err } case fh.ID == 200 && fh.Type == wire.TStruct: v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StartWorkflowExecutionRequest // struct. func (v *StartWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [20]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ExecutionStartToCloseTimeoutSeconds: %v", *(v.ExecutionStartToCloseTimeoutSeconds)) i++ } if v.TaskStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("TaskStartToCloseTimeoutSeconds: %v", *(v.TaskStartToCloseTimeoutSeconds)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } if v.WorkflowIdReusePolicy != nil { fields[i] = fmt.Sprintf("WorkflowIdReusePolicy: %v", *(v.WorkflowIdReusePolicy)) i++ } if v.RetryPolicy != nil { fields[i] = fmt.Sprintf("RetryPolicy: %v", v.RetryPolicy) i++ } if v.CronSchedule != nil { fields[i] = fmt.Sprintf("CronSchedule: %v", *(v.CronSchedule)) i++ } if v.Memo != nil { fields[i] = fmt.Sprintf("Memo: %v", v.Memo) i++ } if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } if v.DelayStartSeconds != nil { fields[i] = fmt.Sprintf("DelayStartSeconds: %v", *(v.DelayStartSeconds)) i++ } if v.JitterStartSeconds != nil { fields[i] = fmt.Sprintf("JitterStartSeconds: %v", *(v.JitterStartSeconds)) i++ } if v.FirstRunAtTimestamp != nil { fields[i] = fmt.Sprintf("FirstRunAtTimestamp: %v", *(v.FirstRunAtTimestamp)) i++ } if v.CronOverlapPolicy != nil { fields[i] = fmt.Sprintf("CronOverlapPolicy: %v", *(v.CronOverlapPolicy)) i++ } if v.ActiveClusterSelectionPolicy != nil { fields[i] = fmt.Sprintf("ActiveClusterSelectionPolicy: %v", v.ActiveClusterSelectionPolicy) i++ } return fmt.Sprintf("StartWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this StartWorkflowExecutionRequest match the // provided StartWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *StartWorkflowExecutionRequest) Equals(rhs *StartWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_I32_EqualsPtr(v.ExecutionStartToCloseTimeoutSeconds, rhs.ExecutionStartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.TaskStartToCloseTimeoutSeconds, rhs.TaskStartToCloseTimeoutSeconds) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } if !_WorkflowIdReusePolicy_EqualsPtr(v.WorkflowIdReusePolicy, rhs.WorkflowIdReusePolicy) { return false } if !((v.RetryPolicy == nil && rhs.RetryPolicy == nil) || (v.RetryPolicy != nil && rhs.RetryPolicy != nil && v.RetryPolicy.Equals(rhs.RetryPolicy))) { return false } if !_String_EqualsPtr(v.CronSchedule, rhs.CronSchedule) { return false } if !((v.Memo == nil && rhs.Memo == nil) || (v.Memo != nil && rhs.Memo != nil && v.Memo.Equals(rhs.Memo))) { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && v.SearchAttributes.Equals(rhs.SearchAttributes))) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } if !_I32_EqualsPtr(v.DelayStartSeconds, rhs.DelayStartSeconds) { return false } if !_I32_EqualsPtr(v.JitterStartSeconds, rhs.JitterStartSeconds) { return false } if !_I64_EqualsPtr(v.FirstRunAtTimestamp, rhs.FirstRunAtTimestamp) { return false } if !_CronOverlapPolicy_EqualsPtr(v.CronOverlapPolicy, rhs.CronOverlapPolicy) { return false } if !((v.ActiveClusterSelectionPolicy == nil && rhs.ActiveClusterSelectionPolicy == nil) || (v.ActiveClusterSelectionPolicy != nil && rhs.ActiveClusterSelectionPolicy != nil && v.ActiveClusterSelectionPolicy.Equals(rhs.ActiveClusterSelectionPolicy))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StartWorkflowExecutionRequest. func (v *StartWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.ExecutionStartToCloseTimeoutSeconds != nil { enc.AddInt32("executionStartToCloseTimeoutSeconds", *v.ExecutionStartToCloseTimeoutSeconds) } if v.TaskStartToCloseTimeoutSeconds != nil { enc.AddInt32("taskStartToCloseTimeoutSeconds", *v.TaskStartToCloseTimeoutSeconds) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } if v.WorkflowIdReusePolicy != nil { err = multierr.Append(err, enc.AddObject("workflowIdReusePolicy", *v.WorkflowIdReusePolicy)) } if v.RetryPolicy != nil { err = multierr.Append(err, enc.AddObject("retryPolicy", v.RetryPolicy)) } if v.CronSchedule != nil { enc.AddString("cronSchedule", *v.CronSchedule) } if v.Memo != nil { err = multierr.Append(err, enc.AddObject("memo", v.Memo)) } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", v.SearchAttributes)) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } if v.DelayStartSeconds != nil { enc.AddInt32("delayStartSeconds", *v.DelayStartSeconds) } if v.JitterStartSeconds != nil { enc.AddInt32("jitterStartSeconds", *v.JitterStartSeconds) } if v.FirstRunAtTimestamp != nil { enc.AddInt64("firstRunAtTimestamp", *v.FirstRunAtTimestamp) } if v.CronOverlapPolicy != nil { err = multierr.Append(err, enc.AddObject("cronOverlapPolicy", *v.CronOverlapPolicy)) } if v.ActiveClusterSelectionPolicy != nil { err = multierr.Append(err, enc.AddObject("activeClusterSelectionPolicy", v.ActiveClusterSelectionPolicy)) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *StartWorkflowExecutionRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *StartWorkflowExecutionRequest) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *StartWorkflowExecutionRequest) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *StartWorkflowExecutionRequest) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *StartWorkflowExecutionRequest) IsSetInput() bool { return v != nil && v.Input != nil } // GetExecutionStartToCloseTimeoutSeconds returns the value of ExecutionStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // IsSetExecutionStartToCloseTimeoutSeconds returns true if ExecutionStartToCloseTimeoutSeconds is not nil. func (v *StartWorkflowExecutionRequest) IsSetExecutionStartToCloseTimeoutSeconds() bool { return v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil } // GetTaskStartToCloseTimeoutSeconds returns the value of TaskStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // IsSetTaskStartToCloseTimeoutSeconds returns true if TaskStartToCloseTimeoutSeconds is not nil. func (v *StartWorkflowExecutionRequest) IsSetTaskStartToCloseTimeoutSeconds() bool { return v != nil && v.TaskStartToCloseTimeoutSeconds != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *StartWorkflowExecutionRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *StartWorkflowExecutionRequest) IsSetRequestId() bool { return v != nil && v.RequestId != nil } // GetWorkflowIdReusePolicy returns the value of WorkflowIdReusePolicy if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetWorkflowIdReusePolicy() (o WorkflowIdReusePolicy) { if v != nil && v.WorkflowIdReusePolicy != nil { return *v.WorkflowIdReusePolicy } return } // IsSetWorkflowIdReusePolicy returns true if WorkflowIdReusePolicy is not nil. func (v *StartWorkflowExecutionRequest) IsSetWorkflowIdReusePolicy() bool { return v != nil && v.WorkflowIdReusePolicy != nil } // GetRetryPolicy returns the value of RetryPolicy if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetRetryPolicy() (o *RetryPolicy) { if v != nil && v.RetryPolicy != nil { return v.RetryPolicy } return } // IsSetRetryPolicy returns true if RetryPolicy is not nil. func (v *StartWorkflowExecutionRequest) IsSetRetryPolicy() bool { return v != nil && v.RetryPolicy != nil } // GetCronSchedule returns the value of CronSchedule if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetCronSchedule() (o string) { if v != nil && v.CronSchedule != nil { return *v.CronSchedule } return } // IsSetCronSchedule returns true if CronSchedule is not nil. func (v *StartWorkflowExecutionRequest) IsSetCronSchedule() bool { return v != nil && v.CronSchedule != nil } // GetMemo returns the value of Memo if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetMemo() (o *Memo) { if v != nil && v.Memo != nil { return v.Memo } return } // IsSetMemo returns true if Memo is not nil. func (v *StartWorkflowExecutionRequest) IsSetMemo() bool { return v != nil && v.Memo != nil } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *StartWorkflowExecutionRequest) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *StartWorkflowExecutionRequest) IsSetHeader() bool { return v != nil && v.Header != nil } // GetDelayStartSeconds returns the value of DelayStartSeconds if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetDelayStartSeconds() (o int32) { if v != nil && v.DelayStartSeconds != nil { return *v.DelayStartSeconds } return } // IsSetDelayStartSeconds returns true if DelayStartSeconds is not nil. func (v *StartWorkflowExecutionRequest) IsSetDelayStartSeconds() bool { return v != nil && v.DelayStartSeconds != nil } // GetJitterStartSeconds returns the value of JitterStartSeconds if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetJitterStartSeconds() (o int32) { if v != nil && v.JitterStartSeconds != nil { return *v.JitterStartSeconds } return } // IsSetJitterStartSeconds returns true if JitterStartSeconds is not nil. func (v *StartWorkflowExecutionRequest) IsSetJitterStartSeconds() bool { return v != nil && v.JitterStartSeconds != nil } // GetFirstRunAtTimestamp returns the value of FirstRunAtTimestamp if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetFirstRunAtTimestamp() (o int64) { if v != nil && v.FirstRunAtTimestamp != nil { return *v.FirstRunAtTimestamp } return } // IsSetFirstRunAtTimestamp returns true if FirstRunAtTimestamp is not nil. func (v *StartWorkflowExecutionRequest) IsSetFirstRunAtTimestamp() bool { return v != nil && v.FirstRunAtTimestamp != nil } // GetCronOverlapPolicy returns the value of CronOverlapPolicy if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetCronOverlapPolicy() (o CronOverlapPolicy) { if v != nil && v.CronOverlapPolicy != nil { return *v.CronOverlapPolicy } return } // IsSetCronOverlapPolicy returns true if CronOverlapPolicy is not nil. func (v *StartWorkflowExecutionRequest) IsSetCronOverlapPolicy() bool { return v != nil && v.CronOverlapPolicy != nil } // GetActiveClusterSelectionPolicy returns the value of ActiveClusterSelectionPolicy if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionRequest) GetActiveClusterSelectionPolicy() (o *ActiveClusterSelectionPolicy) { if v != nil && v.ActiveClusterSelectionPolicy != nil { return v.ActiveClusterSelectionPolicy } return } // IsSetActiveClusterSelectionPolicy returns true if ActiveClusterSelectionPolicy is not nil. func (v *StartWorkflowExecutionRequest) IsSetActiveClusterSelectionPolicy() bool { return v != nil && v.ActiveClusterSelectionPolicy != nil } type StartWorkflowExecutionResponse struct { RunId *string `json:"runId,omitempty"` } // ToWire translates a StartWorkflowExecutionResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StartWorkflowExecutionResponse) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a StartWorkflowExecutionResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StartWorkflowExecutionResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StartWorkflowExecutionResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StartWorkflowExecutionResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } } } return nil } // Encode serializes a StartWorkflowExecutionResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StartWorkflowExecutionResponse struct could not be encoded. func (v *StartWorkflowExecutionResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a StartWorkflowExecutionResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StartWorkflowExecutionResponse struct could not be generated from the wire // representation. func (v *StartWorkflowExecutionResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StartWorkflowExecutionResponse // struct. func (v *StartWorkflowExecutionResponse) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } return fmt.Sprintf("StartWorkflowExecutionResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this StartWorkflowExecutionResponse match the // provided StartWorkflowExecutionResponse. // // This function performs a deep comparison. func (v *StartWorkflowExecutionResponse) Equals(rhs *StartWorkflowExecutionResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StartWorkflowExecutionResponse. func (v *StartWorkflowExecutionResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.RunId != nil { enc.AddString("runId", *v.RunId) } return err } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *StartWorkflowExecutionResponse) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *StartWorkflowExecutionResponse) IsSetRunId() bool { return v != nil && v.RunId != nil } type StickyExecutionAttributes struct { WorkerTaskList *TaskList `json:"workerTaskList,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` } // ToWire translates a StickyExecutionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StickyExecutionAttributes) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.WorkerTaskList != nil { w, err = v.WorkerTaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ScheduleToStartTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ScheduleToStartTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a StickyExecutionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StickyExecutionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StickyExecutionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StickyExecutionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.WorkerTaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } } } } return nil } // Encode serializes a StickyExecutionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StickyExecutionAttributes struct could not be encoded. func (v *StickyExecutionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.WorkerTaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.WorkerTaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleToStartTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ScheduleToStartTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a StickyExecutionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StickyExecutionAttributes struct could not be generated from the wire // representation. func (v *StickyExecutionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.WorkerTaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a StickyExecutionAttributes // struct. func (v *StickyExecutionAttributes) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.WorkerTaskList != nil { fields[i] = fmt.Sprintf("WorkerTaskList: %v", v.WorkerTaskList) i++ } if v.ScheduleToStartTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ScheduleToStartTimeoutSeconds: %v", *(v.ScheduleToStartTimeoutSeconds)) i++ } return fmt.Sprintf("StickyExecutionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this StickyExecutionAttributes match the // provided StickyExecutionAttributes. // // This function performs a deep comparison. func (v *StickyExecutionAttributes) Equals(rhs *StickyExecutionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.WorkerTaskList == nil && rhs.WorkerTaskList == nil) || (v.WorkerTaskList != nil && rhs.WorkerTaskList != nil && v.WorkerTaskList.Equals(rhs.WorkerTaskList))) { return false } if !_I32_EqualsPtr(v.ScheduleToStartTimeoutSeconds, rhs.ScheduleToStartTimeoutSeconds) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StickyExecutionAttributes. func (v *StickyExecutionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.WorkerTaskList != nil { err = multierr.Append(err, enc.AddObject("workerTaskList", v.WorkerTaskList)) } if v.ScheduleToStartTimeoutSeconds != nil { enc.AddInt32("scheduleToStartTimeoutSeconds", *v.ScheduleToStartTimeoutSeconds) } return err } // GetWorkerTaskList returns the value of WorkerTaskList if it is set or its // zero value if it is unset. func (v *StickyExecutionAttributes) GetWorkerTaskList() (o *TaskList) { if v != nil && v.WorkerTaskList != nil { return v.WorkerTaskList } return } // IsSetWorkerTaskList returns true if WorkerTaskList is not nil. func (v *StickyExecutionAttributes) IsSetWorkerTaskList() bool { return v != nil && v.WorkerTaskList != nil } // GetScheduleToStartTimeoutSeconds returns the value of ScheduleToStartTimeoutSeconds if it is set or its // zero value if it is unset. func (v *StickyExecutionAttributes) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // IsSetScheduleToStartTimeoutSeconds returns true if ScheduleToStartTimeoutSeconds is not nil. func (v *StickyExecutionAttributes) IsSetScheduleToStartTimeoutSeconds() bool { return v != nil && v.ScheduleToStartTimeoutSeconds != nil } type StickyWorkerUnavailableError struct { Message string `json:"message,required"` } // ToWire translates a StickyWorkerUnavailableError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *StickyWorkerUnavailableError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a StickyWorkerUnavailableError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a StickyWorkerUnavailableError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v StickyWorkerUnavailableError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *StickyWorkerUnavailableError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of StickyWorkerUnavailableError is required") } return nil } // Encode serializes a StickyWorkerUnavailableError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a StickyWorkerUnavailableError struct could not be encoded. func (v *StickyWorkerUnavailableError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a StickyWorkerUnavailableError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a StickyWorkerUnavailableError struct could not be generated from the wire // representation. func (v *StickyWorkerUnavailableError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of StickyWorkerUnavailableError is required") } return nil } // String returns a readable string representation of a StickyWorkerUnavailableError // struct. func (v *StickyWorkerUnavailableError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("StickyWorkerUnavailableError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*StickyWorkerUnavailableError) ErrorName() string { return "StickyWorkerUnavailableError" } // Equals returns true if all the fields of this StickyWorkerUnavailableError match the // provided StickyWorkerUnavailableError. // // This function performs a deep comparison. func (v *StickyWorkerUnavailableError) Equals(rhs *StickyWorkerUnavailableError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of StickyWorkerUnavailableError. func (v *StickyWorkerUnavailableError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *StickyWorkerUnavailableError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *StickyWorkerUnavailableError) Error() string { return v.String() } type SupportedClientVersions struct { GoSdk *string `json:"goSdk,omitempty"` JavaSdk *string `json:"javaSdk,omitempty"` } // ToWire translates a SupportedClientVersions struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SupportedClientVersions) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.GoSdk != nil { w, err = wire.NewValueString(*(v.GoSdk)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.JavaSdk != nil { w, err = wire.NewValueString(*(v.JavaSdk)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a SupportedClientVersions struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SupportedClientVersions struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SupportedClientVersions // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SupportedClientVersions) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.GoSdk = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.JavaSdk = &x if err != nil { return err } } } } return nil } // Encode serializes a SupportedClientVersions struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SupportedClientVersions struct could not be encoded. func (v *SupportedClientVersions) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.GoSdk != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.GoSdk)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.JavaSdk != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.JavaSdk)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a SupportedClientVersions struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SupportedClientVersions struct could not be generated from the wire // representation. func (v *SupportedClientVersions) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.GoSdk = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.JavaSdk = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SupportedClientVersions // struct. func (v *SupportedClientVersions) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.GoSdk != nil { fields[i] = fmt.Sprintf("GoSdk: %v", *(v.GoSdk)) i++ } if v.JavaSdk != nil { fields[i] = fmt.Sprintf("JavaSdk: %v", *(v.JavaSdk)) i++ } return fmt.Sprintf("SupportedClientVersions{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SupportedClientVersions match the // provided SupportedClientVersions. // // This function performs a deep comparison. func (v *SupportedClientVersions) Equals(rhs *SupportedClientVersions) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.GoSdk, rhs.GoSdk) { return false } if !_String_EqualsPtr(v.JavaSdk, rhs.JavaSdk) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SupportedClientVersions. func (v *SupportedClientVersions) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.GoSdk != nil { enc.AddString("goSdk", *v.GoSdk) } if v.JavaSdk != nil { enc.AddString("javaSdk", *v.JavaSdk) } return err } // GetGoSdk returns the value of GoSdk if it is set or its // zero value if it is unset. func (v *SupportedClientVersions) GetGoSdk() (o string) { if v != nil && v.GoSdk != nil { return *v.GoSdk } return } // IsSetGoSdk returns true if GoSdk is not nil. func (v *SupportedClientVersions) IsSetGoSdk() bool { return v != nil && v.GoSdk != nil } // GetJavaSdk returns the value of JavaSdk if it is set or its // zero value if it is unset. func (v *SupportedClientVersions) GetJavaSdk() (o string) { if v != nil && v.JavaSdk != nil { return *v.JavaSdk } return } // IsSetJavaSdk returns true if JavaSdk is not nil. func (v *SupportedClientVersions) IsSetJavaSdk() bool { return v != nil && v.JavaSdk != nil } type TaskIDBlock struct { StartID *int64 `json:"startID,omitempty"` EndID *int64 `json:"endID,omitempty"` } // ToWire translates a TaskIDBlock struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskIDBlock) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.StartID != nil { w, err = wire.NewValueI64(*(v.StartID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.EndID != nil { w, err = wire.NewValueI64(*(v.EndID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TaskIDBlock struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskIDBlock struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskIDBlock // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskIDBlock) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EndID = &x if err != nil { return err } } } } return nil } // Encode serializes a TaskIDBlock struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskIDBlock struct could not be encoded. func (v *TaskIDBlock) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.StartID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EndID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EndID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TaskIDBlock struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskIDBlock struct could not be generated from the wire // representation. func (v *TaskIDBlock) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EndID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskIDBlock // struct. func (v *TaskIDBlock) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.StartID != nil { fields[i] = fmt.Sprintf("StartID: %v", *(v.StartID)) i++ } if v.EndID != nil { fields[i] = fmt.Sprintf("EndID: %v", *(v.EndID)) i++ } return fmt.Sprintf("TaskIDBlock{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TaskIDBlock match the // provided TaskIDBlock. // // This function performs a deep comparison. func (v *TaskIDBlock) Equals(rhs *TaskIDBlock) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.StartID, rhs.StartID) { return false } if !_I64_EqualsPtr(v.EndID, rhs.EndID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskIDBlock. func (v *TaskIDBlock) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.StartID != nil { enc.AddInt64("startID", *v.StartID) } if v.EndID != nil { enc.AddInt64("endID", *v.EndID) } return err } // GetStartID returns the value of StartID if it is set or its // zero value if it is unset. func (v *TaskIDBlock) GetStartID() (o int64) { if v != nil && v.StartID != nil { return *v.StartID } return } // IsSetStartID returns true if StartID is not nil. func (v *TaskIDBlock) IsSetStartID() bool { return v != nil && v.StartID != nil } // GetEndID returns the value of EndID if it is set or its // zero value if it is unset. func (v *TaskIDBlock) GetEndID() (o int64) { if v != nil && v.EndID != nil { return *v.EndID } return } // IsSetEndID returns true if EndID is not nil. func (v *TaskIDBlock) IsSetEndID() bool { return v != nil && v.EndID != nil } type TaskKey struct { ScheduledTimeNano *int64 `json:"scheduledTimeNano,omitempty"` TaskID *int64 `json:"taskID,omitempty"` } // ToWire translates a TaskKey struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskKey) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ScheduledTimeNano != nil { w, err = wire.NewValueI64(*(v.ScheduledTimeNano)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.TaskID != nil { w, err = wire.NewValueI64(*(v.TaskID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TaskKey struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskKey struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskKey // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskKey) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimeNano = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskID = &x if err != nil { return err } } } } return nil } // Encode serializes a TaskKey struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskKey struct could not be encoded. func (v *TaskKey) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ScheduledTimeNano != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimeNano)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TaskKey struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskKey struct could not be generated from the wire // representation. func (v *TaskKey) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimeNano = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskKey // struct. func (v *TaskKey) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ScheduledTimeNano != nil { fields[i] = fmt.Sprintf("ScheduledTimeNano: %v", *(v.ScheduledTimeNano)) i++ } if v.TaskID != nil { fields[i] = fmt.Sprintf("TaskID: %v", *(v.TaskID)) i++ } return fmt.Sprintf("TaskKey{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TaskKey match the // provided TaskKey. // // This function performs a deep comparison. func (v *TaskKey) Equals(rhs *TaskKey) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.ScheduledTimeNano, rhs.ScheduledTimeNano) { return false } if !_I64_EqualsPtr(v.TaskID, rhs.TaskID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskKey. func (v *TaskKey) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ScheduledTimeNano != nil { enc.AddInt64("scheduledTimeNano", *v.ScheduledTimeNano) } if v.TaskID != nil { enc.AddInt64("taskID", *v.TaskID) } return err } // GetScheduledTimeNano returns the value of ScheduledTimeNano if it is set or its // zero value if it is unset. func (v *TaskKey) GetScheduledTimeNano() (o int64) { if v != nil && v.ScheduledTimeNano != nil { return *v.ScheduledTimeNano } return } // IsSetScheduledTimeNano returns true if ScheduledTimeNano is not nil. func (v *TaskKey) IsSetScheduledTimeNano() bool { return v != nil && v.ScheduledTimeNano != nil } // GetTaskID returns the value of TaskID if it is set or its // zero value if it is unset. func (v *TaskKey) GetTaskID() (o int64) { if v != nil && v.TaskID != nil { return *v.TaskID } return } // IsSetTaskID returns true if TaskID is not nil. func (v *TaskKey) IsSetTaskID() bool { return v != nil && v.TaskID != nil } type TaskList struct { Name *string `json:"name,omitempty"` Kind *TaskListKind `json:"kind,omitempty"` } // ToWire translates a TaskList struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskList) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Kind != nil { w, err = v.Kind.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TaskListKind_Read(w wire.Value) (TaskListKind, error) { var v TaskListKind err := v.FromWire(w) return v, err } // FromWire deserializes a TaskList struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskList struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskList // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskList) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x TaskListKind x, err = _TaskListKind_Read(field.Value) v.Kind = &x if err != nil { return err } } } } return nil } // Encode serializes a TaskList struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskList struct could not be encoded. func (v *TaskList) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Kind != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := v.Kind.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TaskListKind_Decode(sr stream.Reader) (TaskListKind, error) { var v TaskListKind err := v.Decode(sr) return v, err } // Decode deserializes a TaskList struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskList struct could not be generated from the wire // representation. func (v *TaskList) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x TaskListKind x, err = _TaskListKind_Decode(sr) v.Kind = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskList // struct. func (v *TaskList) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.Kind != nil { fields[i] = fmt.Sprintf("Kind: %v", *(v.Kind)) i++ } return fmt.Sprintf("TaskList{%v}", strings.Join(fields[:i], ", ")) } func _TaskListKind_EqualsPtr(lhs, rhs *TaskListKind) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this TaskList match the // provided TaskList. // // This function performs a deep comparison. func (v *TaskList) Equals(rhs *TaskList) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !_TaskListKind_EqualsPtr(v.Kind, rhs.Kind) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskList. func (v *TaskList) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.Kind != nil { err = multierr.Append(err, enc.AddObject("kind", *v.Kind)) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *TaskList) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *TaskList) IsSetName() bool { return v != nil && v.Name != nil } // GetKind returns the value of Kind if it is set or its // zero value if it is unset. func (v *TaskList) GetKind() (o TaskListKind) { if v != nil && v.Kind != nil { return *v.Kind } return } // IsSetKind returns true if Kind is not nil. func (v *TaskList) IsSetKind() bool { return v != nil && v.Kind != nil } type TaskListKind int32 const ( TaskListKindNormal TaskListKind = 0 TaskListKindSticky TaskListKind = 1 TaskListKindEphemeral TaskListKind = 2 ) // TaskListKind_Values returns all recognized values of TaskListKind. func TaskListKind_Values() []TaskListKind { return []TaskListKind{ TaskListKindNormal, TaskListKindSticky, TaskListKindEphemeral, } } // UnmarshalText tries to decode TaskListKind from a byte slice // containing its name. // // var v TaskListKind // err := v.UnmarshalText([]byte("NORMAL")) func (v *TaskListKind) UnmarshalText(value []byte) error { switch s := string(value); s { case "NORMAL": *v = TaskListKindNormal return nil case "STICKY": *v = TaskListKindSticky return nil case "EPHEMERAL": *v = TaskListKindEphemeral return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "TaskListKind", err) } *v = TaskListKind(val) return nil } } // MarshalText encodes TaskListKind to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v TaskListKind) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("NORMAL"), nil case 1: return []byte("STICKY"), nil case 2: return []byte("EPHEMERAL"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskListKind. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v TaskListKind) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "NORMAL") case 1: enc.AddString("name", "STICKY") case 2: enc.AddString("name", "EPHEMERAL") } return nil } // Ptr returns a pointer to this enum value. func (v TaskListKind) Ptr() *TaskListKind { return &v } // Encode encodes TaskListKind directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v TaskListKind // return v.Encode(sWriter) func (v TaskListKind) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates TaskListKind into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v TaskListKind) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes TaskListKind from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return TaskListKind(0), err // } // // var v TaskListKind // if err := v.FromWire(x); err != nil { // return TaskListKind(0), err // } // return v, nil func (v *TaskListKind) FromWire(w wire.Value) error { *v = (TaskListKind)(w.GetI32()) return nil } // Decode reads off the encoded TaskListKind directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v TaskListKind // if err := v.Decode(sReader); err != nil { // return TaskListKind(0), err // } // return v, nil func (v *TaskListKind) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (TaskListKind)(i) return nil } // String returns a readable string representation of TaskListKind. func (v TaskListKind) String() string { w := int32(v) switch w { case 0: return "NORMAL" case 1: return "STICKY" case 2: return "EPHEMERAL" } return fmt.Sprintf("TaskListKind(%d)", w) } // Equals returns true if this TaskListKind value matches the provided // value. func (v TaskListKind) Equals(rhs TaskListKind) bool { return v == rhs } // MarshalJSON serializes TaskListKind into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v TaskListKind) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"NORMAL\""), nil case 1: return ([]byte)("\"STICKY\""), nil case 2: return ([]byte)("\"EPHEMERAL\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode TaskListKind from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *TaskListKind) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "TaskListKind") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "TaskListKind") } *v = (TaskListKind)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "TaskListKind") } } type TaskListMetadata struct { MaxTasksPerSecond *float64 `json:"maxTasksPerSecond,omitempty"` } // ToWire translates a TaskListMetadata struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskListMetadata) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.MaxTasksPerSecond != nil { w, err = wire.NewValueDouble(*(v.MaxTasksPerSecond)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TaskListMetadata struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskListMetadata struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskListMetadata // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskListMetadata) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TDouble { var x float64 x, err = field.Value.GetDouble(), error(nil) v.MaxTasksPerSecond = &x if err != nil { return err } } } } return nil } // Encode serializes a TaskListMetadata struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskListMetadata struct could not be encoded. func (v *TaskListMetadata) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.MaxTasksPerSecond != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(*(v.MaxTasksPerSecond)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TaskListMetadata struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskListMetadata struct could not be generated from the wire // representation. func (v *TaskListMetadata) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TDouble: var x float64 x, err = sr.ReadDouble() v.MaxTasksPerSecond = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskListMetadata // struct. func (v *TaskListMetadata) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.MaxTasksPerSecond != nil { fields[i] = fmt.Sprintf("MaxTasksPerSecond: %v", *(v.MaxTasksPerSecond)) i++ } return fmt.Sprintf("TaskListMetadata{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TaskListMetadata match the // provided TaskListMetadata. // // This function performs a deep comparison. func (v *TaskListMetadata) Equals(rhs *TaskListMetadata) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_Double_EqualsPtr(v.MaxTasksPerSecond, rhs.MaxTasksPerSecond) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskListMetadata. func (v *TaskListMetadata) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.MaxTasksPerSecond != nil { enc.AddFloat64("maxTasksPerSecond", *v.MaxTasksPerSecond) } return err } // GetMaxTasksPerSecond returns the value of MaxTasksPerSecond if it is set or its // zero value if it is unset. func (v *TaskListMetadata) GetMaxTasksPerSecond() (o float64) { if v != nil && v.MaxTasksPerSecond != nil { return *v.MaxTasksPerSecond } return } // IsSetMaxTasksPerSecond returns true if MaxTasksPerSecond is not nil. func (v *TaskListMetadata) IsSetMaxTasksPerSecond() bool { return v != nil && v.MaxTasksPerSecond != nil } type TaskListNotOwnedByHostError struct { OwnedByIdentity string `json:"ownedByIdentity,required"` MyIdentity string `json:"myIdentity,required"` TasklistName string `json:"tasklistName,required"` } // ToWire translates a TaskListNotOwnedByHostError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskListNotOwnedByHostError) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.OwnedByIdentity), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ w, err = wire.NewValueString(v.MyIdentity), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 2, Value: w} i++ w, err = wire.NewValueString(v.TasklistName), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 3, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TaskListNotOwnedByHostError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskListNotOwnedByHostError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskListNotOwnedByHostError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskListNotOwnedByHostError) FromWire(w wire.Value) error { var err error ownedByIdentityIsSet := false myIdentityIsSet := false tasklistNameIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.OwnedByIdentity, err = field.Value.GetString(), error(nil) if err != nil { return err } ownedByIdentityIsSet = true } case 2: if field.Value.Type() == wire.TBinary { v.MyIdentity, err = field.Value.GetString(), error(nil) if err != nil { return err } myIdentityIsSet = true } case 3: if field.Value.Type() == wire.TBinary { v.TasklistName, err = field.Value.GetString(), error(nil) if err != nil { return err } tasklistNameIsSet = true } } } if !ownedByIdentityIsSet { return errors.New("field OwnedByIdentity of TaskListNotOwnedByHostError is required") } if !myIdentityIsSet { return errors.New("field MyIdentity of TaskListNotOwnedByHostError is required") } if !tasklistNameIsSet { return errors.New("field TasklistName of TaskListNotOwnedByHostError is required") } return nil } // Encode serializes a TaskListNotOwnedByHostError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskListNotOwnedByHostError struct could not be encoded. func (v *TaskListNotOwnedByHostError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.OwnedByIdentity); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 2, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.MyIdentity); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 3, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.TasklistName); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a TaskListNotOwnedByHostError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskListNotOwnedByHostError struct could not be generated from the wire // representation. func (v *TaskListNotOwnedByHostError) Decode(sr stream.Reader) error { ownedByIdentityIsSet := false myIdentityIsSet := false tasklistNameIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.OwnedByIdentity, err = sr.ReadString() if err != nil { return err } ownedByIdentityIsSet = true case fh.ID == 2 && fh.Type == wire.TBinary: v.MyIdentity, err = sr.ReadString() if err != nil { return err } myIdentityIsSet = true case fh.ID == 3 && fh.Type == wire.TBinary: v.TasklistName, err = sr.ReadString() if err != nil { return err } tasklistNameIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !ownedByIdentityIsSet { return errors.New("field OwnedByIdentity of TaskListNotOwnedByHostError is required") } if !myIdentityIsSet { return errors.New("field MyIdentity of TaskListNotOwnedByHostError is required") } if !tasklistNameIsSet { return errors.New("field TasklistName of TaskListNotOwnedByHostError is required") } return nil } // String returns a readable string representation of a TaskListNotOwnedByHostError // struct. func (v *TaskListNotOwnedByHostError) String() string { if v == nil { return "" } var fields [3]string i := 0 fields[i] = fmt.Sprintf("OwnedByIdentity: %v", v.OwnedByIdentity) i++ fields[i] = fmt.Sprintf("MyIdentity: %v", v.MyIdentity) i++ fields[i] = fmt.Sprintf("TasklistName: %v", v.TasklistName) i++ return fmt.Sprintf("TaskListNotOwnedByHostError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*TaskListNotOwnedByHostError) ErrorName() string { return "TaskListNotOwnedByHostError" } // Equals returns true if all the fields of this TaskListNotOwnedByHostError match the // provided TaskListNotOwnedByHostError. // // This function performs a deep comparison. func (v *TaskListNotOwnedByHostError) Equals(rhs *TaskListNotOwnedByHostError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.OwnedByIdentity == rhs.OwnedByIdentity) { return false } if !(v.MyIdentity == rhs.MyIdentity) { return false } if !(v.TasklistName == rhs.TasklistName) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskListNotOwnedByHostError. func (v *TaskListNotOwnedByHostError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("ownedByIdentity", v.OwnedByIdentity) enc.AddString("myIdentity", v.MyIdentity) enc.AddString("tasklistName", v.TasklistName) return err } // GetOwnedByIdentity returns the value of OwnedByIdentity if it is set or its // zero value if it is unset. func (v *TaskListNotOwnedByHostError) GetOwnedByIdentity() (o string) { if v != nil { o = v.OwnedByIdentity } return } // GetMyIdentity returns the value of MyIdentity if it is set or its // zero value if it is unset. func (v *TaskListNotOwnedByHostError) GetMyIdentity() (o string) { if v != nil { o = v.MyIdentity } return } // GetTasklistName returns the value of TasklistName if it is set or its // zero value if it is unset. func (v *TaskListNotOwnedByHostError) GetTasklistName() (o string) { if v != nil { o = v.TasklistName } return } func (v *TaskListNotOwnedByHostError) Error() string { return v.String() } type TaskListPartitionMetadata struct { Key *string `json:"key,omitempty"` OwnerHostName *string `json:"ownerHostName,omitempty"` } // ToWire translates a TaskListPartitionMetadata struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskListPartitionMetadata) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Key != nil { w, err = wire.NewValueString(*(v.Key)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.OwnerHostName != nil { w, err = wire.NewValueString(*(v.OwnerHostName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TaskListPartitionMetadata struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskListPartitionMetadata struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskListPartitionMetadata // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskListPartitionMetadata) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Key = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.OwnerHostName = &x if err != nil { return err } } } } return nil } // Encode serializes a TaskListPartitionMetadata struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskListPartitionMetadata struct could not be encoded. func (v *TaskListPartitionMetadata) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Key != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Key)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.OwnerHostName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.OwnerHostName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TaskListPartitionMetadata struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskListPartitionMetadata struct could not be generated from the wire // representation. func (v *TaskListPartitionMetadata) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Key = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.OwnerHostName = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskListPartitionMetadata // struct. func (v *TaskListPartitionMetadata) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Key != nil { fields[i] = fmt.Sprintf("Key: %v", *(v.Key)) i++ } if v.OwnerHostName != nil { fields[i] = fmt.Sprintf("OwnerHostName: %v", *(v.OwnerHostName)) i++ } return fmt.Sprintf("TaskListPartitionMetadata{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TaskListPartitionMetadata match the // provided TaskListPartitionMetadata. // // This function performs a deep comparison. func (v *TaskListPartitionMetadata) Equals(rhs *TaskListPartitionMetadata) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Key, rhs.Key) { return false } if !_String_EqualsPtr(v.OwnerHostName, rhs.OwnerHostName) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskListPartitionMetadata. func (v *TaskListPartitionMetadata) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Key != nil { enc.AddString("key", *v.Key) } if v.OwnerHostName != nil { enc.AddString("ownerHostName", *v.OwnerHostName) } return err } // GetKey returns the value of Key if it is set or its // zero value if it is unset. func (v *TaskListPartitionMetadata) GetKey() (o string) { if v != nil && v.Key != nil { return *v.Key } return } // IsSetKey returns true if Key is not nil. func (v *TaskListPartitionMetadata) IsSetKey() bool { return v != nil && v.Key != nil } // GetOwnerHostName returns the value of OwnerHostName if it is set or its // zero value if it is unset. func (v *TaskListPartitionMetadata) GetOwnerHostName() (o string) { if v != nil && v.OwnerHostName != nil { return *v.OwnerHostName } return } // IsSetOwnerHostName returns true if OwnerHostName is not nil. func (v *TaskListPartitionMetadata) IsSetOwnerHostName() bool { return v != nil && v.OwnerHostName != nil } type TaskListStatus struct { BacklogCountHint *int64 `json:"backlogCountHint,omitempty"` ReadLevel *int64 `json:"readLevel,omitempty"` AckLevel *int64 `json:"ackLevel,omitempty"` RatePerSecond *float64 `json:"ratePerSecond,omitempty"` TaskIDBlock *TaskIDBlock `json:"taskIDBlock,omitempty"` IsolationGroupMetrics map[string]*IsolationGroupMetrics `json:"isolationGroupMetrics,omitempty"` NewTasksPerSecond *float64 `json:"newTasksPerSecond,omitempty"` Empty *bool `json:"empty,omitempty"` } type _Map_String_IsolationGroupMetrics_MapItemList map[string]*IsolationGroupMetrics func (m _Map_String_IsolationGroupMetrics_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string]*IsolationGroupMetrics', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_IsolationGroupMetrics_MapItemList) Size() int { return len(m) } func (_Map_String_IsolationGroupMetrics_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_IsolationGroupMetrics_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_String_IsolationGroupMetrics_MapItemList) Close() {} // ToWire translates a TaskListStatus struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskListStatus) ToWire() (wire.Value, error) { var ( fields [8]wire.Field i int = 0 w wire.Value err error ) if v.BacklogCountHint != nil { w, err = wire.NewValueI64(*(v.BacklogCountHint)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ReadLevel != nil { w, err = wire.NewValueI64(*(v.ReadLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.AckLevel != nil { w, err = wire.NewValueI64(*(v.AckLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.RatePerSecond != nil { w, err = wire.NewValueDouble(*(v.RatePerSecond)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 35, Value: w} i++ } if v.TaskIDBlock != nil { w, err = v.TaskIDBlock.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.IsolationGroupMetrics != nil { w, err = wire.NewValueMap(_Map_String_IsolationGroupMetrics_MapItemList(v.IsolationGroupMetrics)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.NewTasksPerSecond != nil { w, err = wire.NewValueDouble(*(v.NewTasksPerSecond)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.Empty != nil { w, err = wire.NewValueBool(*(v.Empty)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TaskIDBlock_Read(w wire.Value) (*TaskIDBlock, error) { var v TaskIDBlock err := v.FromWire(w) return &v, err } func _IsolationGroupMetrics_Read(w wire.Value) (*IsolationGroupMetrics, error) { var v IsolationGroupMetrics err := v.FromWire(w) return &v, err } func _Map_String_IsolationGroupMetrics_Read(m wire.MapItemList) (map[string]*IsolationGroupMetrics, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[string]*IsolationGroupMetrics, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := _IsolationGroupMetrics_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a TaskListStatus struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskListStatus struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskListStatus // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskListStatus) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.BacklogCountHint = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ReadLevel = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.AckLevel = &x if err != nil { return err } } case 35: if field.Value.Type() == wire.TDouble { var x float64 x, err = field.Value.GetDouble(), error(nil) v.RatePerSecond = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.TaskIDBlock, err = _TaskIDBlock_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TMap { v.IsolationGroupMetrics, err = _Map_String_IsolationGroupMetrics_Read(field.Value.GetMap()) if err != nil { return err } } case 60: if field.Value.Type() == wire.TDouble { var x float64 x, err = field.Value.GetDouble(), error(nil) v.NewTasksPerSecond = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.Empty = &x if err != nil { return err } } } } return nil } func _Map_String_IsolationGroupMetrics_Encode(val map[string]*IsolationGroupMetrics, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string]*IsolationGroupMetrics', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a TaskListStatus struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskListStatus struct could not be encoded. func (v *TaskListStatus) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BacklogCountHint != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.BacklogCountHint)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReadLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ReadLevel)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AckLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.AckLevel)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RatePerSecond != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 35, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(*(v.RatePerSecond)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskIDBlock != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.TaskIDBlock.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsolationGroupMetrics != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TMap}); err != nil { return err } if err := _Map_String_IsolationGroupMetrics_Encode(v.IsolationGroupMetrics, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NewTasksPerSecond != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(*(v.NewTasksPerSecond)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Empty != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.Empty)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TaskIDBlock_Decode(sr stream.Reader) (*TaskIDBlock, error) { var v TaskIDBlock err := v.Decode(sr) return &v, err } func _IsolationGroupMetrics_Decode(sr stream.Reader) (*IsolationGroupMetrics, error) { var v IsolationGroupMetrics err := v.Decode(sr) return &v, err } func _Map_String_IsolationGroupMetrics_Decode(sr stream.Reader) (map[string]*IsolationGroupMetrics, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]*IsolationGroupMetrics, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := _IsolationGroupMetrics_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a TaskListStatus struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskListStatus struct could not be generated from the wire // representation. func (v *TaskListStatus) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.BacklogCountHint = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ReadLevel = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.AckLevel = &x if err != nil { return err } case fh.ID == 35 && fh.Type == wire.TDouble: var x float64 x, err = sr.ReadDouble() v.RatePerSecond = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.TaskIDBlock, err = _TaskIDBlock_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TMap: v.IsolationGroupMetrics, err = _Map_String_IsolationGroupMetrics_Decode(sr) if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TDouble: var x float64 x, err = sr.ReadDouble() v.NewTasksPerSecond = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.Empty = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskListStatus // struct. func (v *TaskListStatus) String() string { if v == nil { return "" } var fields [8]string i := 0 if v.BacklogCountHint != nil { fields[i] = fmt.Sprintf("BacklogCountHint: %v", *(v.BacklogCountHint)) i++ } if v.ReadLevel != nil { fields[i] = fmt.Sprintf("ReadLevel: %v", *(v.ReadLevel)) i++ } if v.AckLevel != nil { fields[i] = fmt.Sprintf("AckLevel: %v", *(v.AckLevel)) i++ } if v.RatePerSecond != nil { fields[i] = fmt.Sprintf("RatePerSecond: %v", *(v.RatePerSecond)) i++ } if v.TaskIDBlock != nil { fields[i] = fmt.Sprintf("TaskIDBlock: %v", v.TaskIDBlock) i++ } if v.IsolationGroupMetrics != nil { fields[i] = fmt.Sprintf("IsolationGroupMetrics: %v", v.IsolationGroupMetrics) i++ } if v.NewTasksPerSecond != nil { fields[i] = fmt.Sprintf("NewTasksPerSecond: %v", *(v.NewTasksPerSecond)) i++ } if v.Empty != nil { fields[i] = fmt.Sprintf("Empty: %v", *(v.Empty)) i++ } return fmt.Sprintf("TaskListStatus{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_IsolationGroupMetrics_Equals(lhs, rhs map[string]*IsolationGroupMetrics) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this TaskListStatus match the // provided TaskListStatus. // // This function performs a deep comparison. func (v *TaskListStatus) Equals(rhs *TaskListStatus) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.BacklogCountHint, rhs.BacklogCountHint) { return false } if !_I64_EqualsPtr(v.ReadLevel, rhs.ReadLevel) { return false } if !_I64_EqualsPtr(v.AckLevel, rhs.AckLevel) { return false } if !_Double_EqualsPtr(v.RatePerSecond, rhs.RatePerSecond) { return false } if !((v.TaskIDBlock == nil && rhs.TaskIDBlock == nil) || (v.TaskIDBlock != nil && rhs.TaskIDBlock != nil && v.TaskIDBlock.Equals(rhs.TaskIDBlock))) { return false } if !((v.IsolationGroupMetrics == nil && rhs.IsolationGroupMetrics == nil) || (v.IsolationGroupMetrics != nil && rhs.IsolationGroupMetrics != nil && _Map_String_IsolationGroupMetrics_Equals(v.IsolationGroupMetrics, rhs.IsolationGroupMetrics))) { return false } if !_Double_EqualsPtr(v.NewTasksPerSecond, rhs.NewTasksPerSecond) { return false } if !_Bool_EqualsPtr(v.Empty, rhs.Empty) { return false } return true } type _Map_String_IsolationGroupMetrics_Zapper map[string]*IsolationGroupMetrics // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_IsolationGroupMetrics_Zapper. func (m _Map_String_IsolationGroupMetrics_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AddObject((string)(k), v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskListStatus. func (v *TaskListStatus) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BacklogCountHint != nil { enc.AddInt64("backlogCountHint", *v.BacklogCountHint) } if v.ReadLevel != nil { enc.AddInt64("readLevel", *v.ReadLevel) } if v.AckLevel != nil { enc.AddInt64("ackLevel", *v.AckLevel) } if v.RatePerSecond != nil { enc.AddFloat64("ratePerSecond", *v.RatePerSecond) } if v.TaskIDBlock != nil { err = multierr.Append(err, enc.AddObject("taskIDBlock", v.TaskIDBlock)) } if v.IsolationGroupMetrics != nil { err = multierr.Append(err, enc.AddObject("isolationGroupMetrics", (_Map_String_IsolationGroupMetrics_Zapper)(v.IsolationGroupMetrics))) } if v.NewTasksPerSecond != nil { enc.AddFloat64("newTasksPerSecond", *v.NewTasksPerSecond) } if v.Empty != nil { enc.AddBool("empty", *v.Empty) } return err } // GetBacklogCountHint returns the value of BacklogCountHint if it is set or its // zero value if it is unset. func (v *TaskListStatus) GetBacklogCountHint() (o int64) { if v != nil && v.BacklogCountHint != nil { return *v.BacklogCountHint } return } // IsSetBacklogCountHint returns true if BacklogCountHint is not nil. func (v *TaskListStatus) IsSetBacklogCountHint() bool { return v != nil && v.BacklogCountHint != nil } // GetReadLevel returns the value of ReadLevel if it is set or its // zero value if it is unset. func (v *TaskListStatus) GetReadLevel() (o int64) { if v != nil && v.ReadLevel != nil { return *v.ReadLevel } return } // IsSetReadLevel returns true if ReadLevel is not nil. func (v *TaskListStatus) IsSetReadLevel() bool { return v != nil && v.ReadLevel != nil } // GetAckLevel returns the value of AckLevel if it is set or its // zero value if it is unset. func (v *TaskListStatus) GetAckLevel() (o int64) { if v != nil && v.AckLevel != nil { return *v.AckLevel } return } // IsSetAckLevel returns true if AckLevel is not nil. func (v *TaskListStatus) IsSetAckLevel() bool { return v != nil && v.AckLevel != nil } // GetRatePerSecond returns the value of RatePerSecond if it is set or its // zero value if it is unset. func (v *TaskListStatus) GetRatePerSecond() (o float64) { if v != nil && v.RatePerSecond != nil { return *v.RatePerSecond } return } // IsSetRatePerSecond returns true if RatePerSecond is not nil. func (v *TaskListStatus) IsSetRatePerSecond() bool { return v != nil && v.RatePerSecond != nil } // GetTaskIDBlock returns the value of TaskIDBlock if it is set or its // zero value if it is unset. func (v *TaskListStatus) GetTaskIDBlock() (o *TaskIDBlock) { if v != nil && v.TaskIDBlock != nil { return v.TaskIDBlock } return } // IsSetTaskIDBlock returns true if TaskIDBlock is not nil. func (v *TaskListStatus) IsSetTaskIDBlock() bool { return v != nil && v.TaskIDBlock != nil } // GetIsolationGroupMetrics returns the value of IsolationGroupMetrics if it is set or its // zero value if it is unset. func (v *TaskListStatus) GetIsolationGroupMetrics() (o map[string]*IsolationGroupMetrics) { if v != nil && v.IsolationGroupMetrics != nil { return v.IsolationGroupMetrics } return } // IsSetIsolationGroupMetrics returns true if IsolationGroupMetrics is not nil. func (v *TaskListStatus) IsSetIsolationGroupMetrics() bool { return v != nil && v.IsolationGroupMetrics != nil } // GetNewTasksPerSecond returns the value of NewTasksPerSecond if it is set or its // zero value if it is unset. func (v *TaskListStatus) GetNewTasksPerSecond() (o float64) { if v != nil && v.NewTasksPerSecond != nil { return *v.NewTasksPerSecond } return } // IsSetNewTasksPerSecond returns true if NewTasksPerSecond is not nil. func (v *TaskListStatus) IsSetNewTasksPerSecond() bool { return v != nil && v.NewTasksPerSecond != nil } // GetEmpty returns the value of Empty if it is set or its // zero value if it is unset. func (v *TaskListStatus) GetEmpty() (o bool) { if v != nil && v.Empty != nil { return *v.Empty } return } // IsSetEmpty returns true if Empty is not nil. func (v *TaskListStatus) IsSetEmpty() bool { return v != nil && v.Empty != nil } type TaskListType int32 const ( TaskListTypeDecision TaskListType = 0 TaskListTypeActivity TaskListType = 1 ) // TaskListType_Values returns all recognized values of TaskListType. func TaskListType_Values() []TaskListType { return []TaskListType{ TaskListTypeDecision, TaskListTypeActivity, } } // UnmarshalText tries to decode TaskListType from a byte slice // containing its name. // // var v TaskListType // err := v.UnmarshalText([]byte("Decision")) func (v *TaskListType) UnmarshalText(value []byte) error { switch s := string(value); s { case "Decision": *v = TaskListTypeDecision return nil case "Activity": *v = TaskListTypeActivity return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "TaskListType", err) } *v = TaskListType(val) return nil } } // MarshalText encodes TaskListType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v TaskListType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("Decision"), nil case 1: return []byte("Activity"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskListType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v TaskListType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "Decision") case 1: enc.AddString("name", "Activity") } return nil } // Ptr returns a pointer to this enum value. func (v TaskListType) Ptr() *TaskListType { return &v } // Encode encodes TaskListType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v TaskListType // return v.Encode(sWriter) func (v TaskListType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates TaskListType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v TaskListType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes TaskListType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return TaskListType(0), err // } // // var v TaskListType // if err := v.FromWire(x); err != nil { // return TaskListType(0), err // } // return v, nil func (v *TaskListType) FromWire(w wire.Value) error { *v = (TaskListType)(w.GetI32()) return nil } // Decode reads off the encoded TaskListType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v TaskListType // if err := v.Decode(sReader); err != nil { // return TaskListType(0), err // } // return v, nil func (v *TaskListType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (TaskListType)(i) return nil } // String returns a readable string representation of TaskListType. func (v TaskListType) String() string { w := int32(v) switch w { case 0: return "Decision" case 1: return "Activity" } return fmt.Sprintf("TaskListType(%d)", w) } // Equals returns true if this TaskListType value matches the provided // value. func (v TaskListType) Equals(rhs TaskListType) bool { return v == rhs } // MarshalJSON serializes TaskListType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v TaskListType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"Decision\""), nil case 1: return ([]byte)("\"Activity\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode TaskListType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *TaskListType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "TaskListType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "TaskListType") } *v = (TaskListType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "TaskListType") } } type TaskRange struct { InclusiveMin *TaskKey `json:"inclusiveMin,omitempty"` ExclusiveMax *TaskKey `json:"exclusiveMax,omitempty"` } // ToWire translates a TaskRange struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskRange) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.InclusiveMin != nil { w, err = v.InclusiveMin.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ExclusiveMax != nil { w, err = v.ExclusiveMax.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TaskRange struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskRange struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskRange // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskRange) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.InclusiveMin, err = _TaskKey_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.ExclusiveMax, err = _TaskKey_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a TaskRange struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskRange struct could not be encoded. func (v *TaskRange) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.InclusiveMin != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.InclusiveMin.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExclusiveMax != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.ExclusiveMax.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TaskRange struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskRange struct could not be generated from the wire // representation. func (v *TaskRange) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.InclusiveMin, err = _TaskKey_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.ExclusiveMax, err = _TaskKey_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskRange // struct. func (v *TaskRange) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.InclusiveMin != nil { fields[i] = fmt.Sprintf("InclusiveMin: %v", v.InclusiveMin) i++ } if v.ExclusiveMax != nil { fields[i] = fmt.Sprintf("ExclusiveMax: %v", v.ExclusiveMax) i++ } return fmt.Sprintf("TaskRange{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TaskRange match the // provided TaskRange. // // This function performs a deep comparison. func (v *TaskRange) Equals(rhs *TaskRange) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.InclusiveMin == nil && rhs.InclusiveMin == nil) || (v.InclusiveMin != nil && rhs.InclusiveMin != nil && v.InclusiveMin.Equals(rhs.InclusiveMin))) { return false } if !((v.ExclusiveMax == nil && rhs.ExclusiveMax == nil) || (v.ExclusiveMax != nil && rhs.ExclusiveMax != nil && v.ExclusiveMax.Equals(rhs.ExclusiveMax))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskRange. func (v *TaskRange) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.InclusiveMin != nil { err = multierr.Append(err, enc.AddObject("inclusiveMin", v.InclusiveMin)) } if v.ExclusiveMax != nil { err = multierr.Append(err, enc.AddObject("exclusiveMax", v.ExclusiveMax)) } return err } // GetInclusiveMin returns the value of InclusiveMin if it is set or its // zero value if it is unset. func (v *TaskRange) GetInclusiveMin() (o *TaskKey) { if v != nil && v.InclusiveMin != nil { return v.InclusiveMin } return } // IsSetInclusiveMin returns true if InclusiveMin is not nil. func (v *TaskRange) IsSetInclusiveMin() bool { return v != nil && v.InclusiveMin != nil } // GetExclusiveMax returns the value of ExclusiveMax if it is set or its // zero value if it is unset. func (v *TaskRange) GetExclusiveMax() (o *TaskKey) { if v != nil && v.ExclusiveMax != nil { return v.ExclusiveMax } return } // IsSetExclusiveMax returns true if ExclusiveMax is not nil. func (v *TaskRange) IsSetExclusiveMax() bool { return v != nil && v.ExclusiveMax != nil } type TerminateWorkflowExecutionRequest struct { Domain *string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` Identity *string `json:"identity,omitempty"` FirstExecutionRunID *string `json:"firstExecutionRunID,omitempty"` } // ToWire translates a TerminateWorkflowExecutionRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TerminateWorkflowExecutionRequest) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Domain != nil { w, err = wire.NewValueString(*(v.Domain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowExecution != nil { w, err = v.WorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.FirstExecutionRunID != nil { w, err = wire.NewValueString(*(v.FirstExecutionRunID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TerminateWorkflowExecutionRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TerminateWorkflowExecutionRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TerminateWorkflowExecutionRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TerminateWorkflowExecutionRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Domain = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.FirstExecutionRunID = &x if err != nil { return err } } } } return nil } // Encode serializes a TerminateWorkflowExecutionRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TerminateWorkflowExecutionRequest struct could not be encoded. func (v *TerminateWorkflowExecutionRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Domain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Domain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstExecutionRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.FirstExecutionRunID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TerminateWorkflowExecutionRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TerminateWorkflowExecutionRequest struct could not be generated from the wire // representation. func (v *TerminateWorkflowExecutionRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Domain = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.FirstExecutionRunID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TerminateWorkflowExecutionRequest // struct. func (v *TerminateWorkflowExecutionRequest) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Domain != nil { fields[i] = fmt.Sprintf("Domain: %v", *(v.Domain)) i++ } if v.WorkflowExecution != nil { fields[i] = fmt.Sprintf("WorkflowExecution: %v", v.WorkflowExecution) i++ } if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.FirstExecutionRunID != nil { fields[i] = fmt.Sprintf("FirstExecutionRunID: %v", *(v.FirstExecutionRunID)) i++ } return fmt.Sprintf("TerminateWorkflowExecutionRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TerminateWorkflowExecutionRequest match the // provided TerminateWorkflowExecutionRequest. // // This function performs a deep comparison. func (v *TerminateWorkflowExecutionRequest) Equals(rhs *TerminateWorkflowExecutionRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Domain, rhs.Domain) { return false } if !((v.WorkflowExecution == nil && rhs.WorkflowExecution == nil) || (v.WorkflowExecution != nil && rhs.WorkflowExecution != nil && v.WorkflowExecution.Equals(rhs.WorkflowExecution))) { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.FirstExecutionRunID, rhs.FirstExecutionRunID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TerminateWorkflowExecutionRequest. func (v *TerminateWorkflowExecutionRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Domain != nil { enc.AddString("domain", *v.Domain) } if v.WorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("workflowExecution", v.WorkflowExecution)) } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.FirstExecutionRunID != nil { enc.AddString("firstExecutionRunID", *v.FirstExecutionRunID) } return err } // GetDomain returns the value of Domain if it is set or its // zero value if it is unset. func (v *TerminateWorkflowExecutionRequest) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // IsSetDomain returns true if Domain is not nil. func (v *TerminateWorkflowExecutionRequest) IsSetDomain() bool { return v != nil && v.Domain != nil } // GetWorkflowExecution returns the value of WorkflowExecution if it is set or its // zero value if it is unset. func (v *TerminateWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // IsSetWorkflowExecution returns true if WorkflowExecution is not nil. func (v *TerminateWorkflowExecutionRequest) IsSetWorkflowExecution() bool { return v != nil && v.WorkflowExecution != nil } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *TerminateWorkflowExecutionRequest) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *TerminateWorkflowExecutionRequest) IsSetReason() bool { return v != nil && v.Reason != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *TerminateWorkflowExecutionRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *TerminateWorkflowExecutionRequest) IsSetDetails() bool { return v != nil && v.Details != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *TerminateWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *TerminateWorkflowExecutionRequest) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetFirstExecutionRunID returns the value of FirstExecutionRunID if it is set or its // zero value if it is unset. func (v *TerminateWorkflowExecutionRequest) GetFirstExecutionRunID() (o string) { if v != nil && v.FirstExecutionRunID != nil { return *v.FirstExecutionRunID } return } // IsSetFirstExecutionRunID returns true if FirstExecutionRunID is not nil. func (v *TerminateWorkflowExecutionRequest) IsSetFirstExecutionRunID() bool { return v != nil && v.FirstExecutionRunID != nil } type TimeoutType int32 const ( TimeoutTypeStartToClose TimeoutType = 0 TimeoutTypeScheduleToStart TimeoutType = 1 TimeoutTypeScheduleToClose TimeoutType = 2 TimeoutTypeHeartbeat TimeoutType = 3 ) // TimeoutType_Values returns all recognized values of TimeoutType. func TimeoutType_Values() []TimeoutType { return []TimeoutType{ TimeoutTypeStartToClose, TimeoutTypeScheduleToStart, TimeoutTypeScheduleToClose, TimeoutTypeHeartbeat, } } // UnmarshalText tries to decode TimeoutType from a byte slice // containing its name. // // var v TimeoutType // err := v.UnmarshalText([]byte("START_TO_CLOSE")) func (v *TimeoutType) UnmarshalText(value []byte) error { switch s := string(value); s { case "START_TO_CLOSE": *v = TimeoutTypeStartToClose return nil case "SCHEDULE_TO_START": *v = TimeoutTypeScheduleToStart return nil case "SCHEDULE_TO_CLOSE": *v = TimeoutTypeScheduleToClose return nil case "HEARTBEAT": *v = TimeoutTypeHeartbeat return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "TimeoutType", err) } *v = TimeoutType(val) return nil } } // MarshalText encodes TimeoutType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v TimeoutType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("START_TO_CLOSE"), nil case 1: return []byte("SCHEDULE_TO_START"), nil case 2: return []byte("SCHEDULE_TO_CLOSE"), nil case 3: return []byte("HEARTBEAT"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TimeoutType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v TimeoutType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "START_TO_CLOSE") case 1: enc.AddString("name", "SCHEDULE_TO_START") case 2: enc.AddString("name", "SCHEDULE_TO_CLOSE") case 3: enc.AddString("name", "HEARTBEAT") } return nil } // Ptr returns a pointer to this enum value. func (v TimeoutType) Ptr() *TimeoutType { return &v } // Encode encodes TimeoutType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v TimeoutType // return v.Encode(sWriter) func (v TimeoutType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates TimeoutType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v TimeoutType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes TimeoutType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return TimeoutType(0), err // } // // var v TimeoutType // if err := v.FromWire(x); err != nil { // return TimeoutType(0), err // } // return v, nil func (v *TimeoutType) FromWire(w wire.Value) error { *v = (TimeoutType)(w.GetI32()) return nil } // Decode reads off the encoded TimeoutType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v TimeoutType // if err := v.Decode(sReader); err != nil { // return TimeoutType(0), err // } // return v, nil func (v *TimeoutType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (TimeoutType)(i) return nil } // String returns a readable string representation of TimeoutType. func (v TimeoutType) String() string { w := int32(v) switch w { case 0: return "START_TO_CLOSE" case 1: return "SCHEDULE_TO_START" case 2: return "SCHEDULE_TO_CLOSE" case 3: return "HEARTBEAT" } return fmt.Sprintf("TimeoutType(%d)", w) } // Equals returns true if this TimeoutType value matches the provided // value. func (v TimeoutType) Equals(rhs TimeoutType) bool { return v == rhs } // MarshalJSON serializes TimeoutType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v TimeoutType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"START_TO_CLOSE\""), nil case 1: return ([]byte)("\"SCHEDULE_TO_START\""), nil case 2: return ([]byte)("\"SCHEDULE_TO_CLOSE\""), nil case 3: return ([]byte)("\"HEARTBEAT\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode TimeoutType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *TimeoutType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "TimeoutType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "TimeoutType") } *v = (TimeoutType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "TimeoutType") } } type TimerCanceledEventAttributes struct { TimerId *string `json:"timerId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a TimerCanceledEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TimerCanceledEventAttributes) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.TimerId != nil { w, err = wire.NewValueString(*(v.TimerId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TimerCanceledEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TimerCanceledEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TimerCanceledEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TimerCanceledEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TimerId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a TimerCanceledEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TimerCanceledEventAttributes struct could not be encoded. func (v *TimerCanceledEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TimerId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TimerId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TimerCanceledEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TimerCanceledEventAttributes struct could not be generated from the wire // representation. func (v *TimerCanceledEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TimerId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TimerCanceledEventAttributes // struct. func (v *TimerCanceledEventAttributes) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.TimerId != nil { fields[i] = fmt.Sprintf("TimerId: %v", *(v.TimerId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("TimerCanceledEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TimerCanceledEventAttributes match the // provided TimerCanceledEventAttributes. // // This function performs a deep comparison. func (v *TimerCanceledEventAttributes) Equals(rhs *TimerCanceledEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TimerId, rhs.TimerId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TimerCanceledEventAttributes. func (v *TimerCanceledEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TimerId != nil { enc.AddString("timerId", *v.TimerId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetTimerId returns the value of TimerId if it is set or its // zero value if it is unset. func (v *TimerCanceledEventAttributes) GetTimerId() (o string) { if v != nil && v.TimerId != nil { return *v.TimerId } return } // IsSetTimerId returns true if TimerId is not nil. func (v *TimerCanceledEventAttributes) IsSetTimerId() bool { return v != nil && v.TimerId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *TimerCanceledEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *TimerCanceledEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *TimerCanceledEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *TimerCanceledEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *TimerCanceledEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *TimerCanceledEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } type TimerFiredEventAttributes struct { TimerId *string `json:"timerId,omitempty"` StartedEventId *int64 `json:"startedEventId,omitempty"` } // ToWire translates a TimerFiredEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TimerFiredEventAttributes) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.TimerId != nil { w, err = wire.NewValueString(*(v.TimerId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartedEventId != nil { w, err = wire.NewValueI64(*(v.StartedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TimerFiredEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TimerFiredEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TimerFiredEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TimerFiredEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TimerId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a TimerFiredEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TimerFiredEventAttributes struct could not be encoded. func (v *TimerFiredEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TimerId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TimerId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TimerFiredEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TimerFiredEventAttributes struct could not be generated from the wire // representation. func (v *TimerFiredEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TimerId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TimerFiredEventAttributes // struct. func (v *TimerFiredEventAttributes) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.TimerId != nil { fields[i] = fmt.Sprintf("TimerId: %v", *(v.TimerId)) i++ } if v.StartedEventId != nil { fields[i] = fmt.Sprintf("StartedEventId: %v", *(v.StartedEventId)) i++ } return fmt.Sprintf("TimerFiredEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TimerFiredEventAttributes match the // provided TimerFiredEventAttributes. // // This function performs a deep comparison. func (v *TimerFiredEventAttributes) Equals(rhs *TimerFiredEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TimerId, rhs.TimerId) { return false } if !_I64_EqualsPtr(v.StartedEventId, rhs.StartedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TimerFiredEventAttributes. func (v *TimerFiredEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TimerId != nil { enc.AddString("timerId", *v.TimerId) } if v.StartedEventId != nil { enc.AddInt64("startedEventId", *v.StartedEventId) } return err } // GetTimerId returns the value of TimerId if it is set or its // zero value if it is unset. func (v *TimerFiredEventAttributes) GetTimerId() (o string) { if v != nil && v.TimerId != nil { return *v.TimerId } return } // IsSetTimerId returns true if TimerId is not nil. func (v *TimerFiredEventAttributes) IsSetTimerId() bool { return v != nil && v.TimerId != nil } // GetStartedEventId returns the value of StartedEventId if it is set or its // zero value if it is unset. func (v *TimerFiredEventAttributes) GetStartedEventId() (o int64) { if v != nil && v.StartedEventId != nil { return *v.StartedEventId } return } // IsSetStartedEventId returns true if StartedEventId is not nil. func (v *TimerFiredEventAttributes) IsSetStartedEventId() bool { return v != nil && v.StartedEventId != nil } type TimerStartedEventAttributes struct { TimerId *string `json:"timerId,omitempty"` StartToFireTimeoutSeconds *int64 `json:"startToFireTimeoutSeconds,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` } // ToWire translates a TimerStartedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TimerStartedEventAttributes) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.TimerId != nil { w, err = wire.NewValueString(*(v.TimerId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartToFireTimeoutSeconds != nil { w, err = wire.NewValueI64(*(v.StartToFireTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TimerStartedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TimerStartedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TimerStartedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TimerStartedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TimerId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartToFireTimeoutSeconds = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a TimerStartedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TimerStartedEventAttributes struct could not be encoded. func (v *TimerStartedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TimerId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TimerId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartToFireTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartToFireTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TimerStartedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TimerStartedEventAttributes struct could not be generated from the wire // representation. func (v *TimerStartedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TimerId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartToFireTimeoutSeconds = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TimerStartedEventAttributes // struct. func (v *TimerStartedEventAttributes) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.TimerId != nil { fields[i] = fmt.Sprintf("TimerId: %v", *(v.TimerId)) i++ } if v.StartToFireTimeoutSeconds != nil { fields[i] = fmt.Sprintf("StartToFireTimeoutSeconds: %v", *(v.StartToFireTimeoutSeconds)) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } return fmt.Sprintf("TimerStartedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TimerStartedEventAttributes match the // provided TimerStartedEventAttributes. // // This function performs a deep comparison. func (v *TimerStartedEventAttributes) Equals(rhs *TimerStartedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.TimerId, rhs.TimerId) { return false } if !_I64_EqualsPtr(v.StartToFireTimeoutSeconds, rhs.StartToFireTimeoutSeconds) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TimerStartedEventAttributes. func (v *TimerStartedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TimerId != nil { enc.AddString("timerId", *v.TimerId) } if v.StartToFireTimeoutSeconds != nil { enc.AddInt64("startToFireTimeoutSeconds", *v.StartToFireTimeoutSeconds) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } return err } // GetTimerId returns the value of TimerId if it is set or its // zero value if it is unset. func (v *TimerStartedEventAttributes) GetTimerId() (o string) { if v != nil && v.TimerId != nil { return *v.TimerId } return } // IsSetTimerId returns true if TimerId is not nil. func (v *TimerStartedEventAttributes) IsSetTimerId() bool { return v != nil && v.TimerId != nil } // GetStartToFireTimeoutSeconds returns the value of StartToFireTimeoutSeconds if it is set or its // zero value if it is unset. func (v *TimerStartedEventAttributes) GetStartToFireTimeoutSeconds() (o int64) { if v != nil && v.StartToFireTimeoutSeconds != nil { return *v.StartToFireTimeoutSeconds } return } // IsSetStartToFireTimeoutSeconds returns true if StartToFireTimeoutSeconds is not nil. func (v *TimerStartedEventAttributes) IsSetStartToFireTimeoutSeconds() bool { return v != nil && v.StartToFireTimeoutSeconds != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *TimerStartedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *TimerStartedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } type TransientDecisionInfo struct { ScheduledEvent *HistoryEvent `json:"scheduledEvent,omitempty"` StartedEvent *HistoryEvent `json:"startedEvent,omitempty"` } // ToWire translates a TransientDecisionInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TransientDecisionInfo) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.ScheduledEvent != nil { w, err = v.ScheduledEvent.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartedEvent != nil { w, err = v.StartedEvent.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TransientDecisionInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TransientDecisionInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TransientDecisionInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TransientDecisionInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.ScheduledEvent, err = _HistoryEvent_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.StartedEvent, err = _HistoryEvent_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a TransientDecisionInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TransientDecisionInfo struct could not be encoded. func (v *TransientDecisionInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ScheduledEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.ScheduledEvent.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.StartedEvent.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TransientDecisionInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TransientDecisionInfo struct could not be generated from the wire // representation. func (v *TransientDecisionInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.ScheduledEvent, err = _HistoryEvent_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.StartedEvent, err = _HistoryEvent_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TransientDecisionInfo // struct. func (v *TransientDecisionInfo) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.ScheduledEvent != nil { fields[i] = fmt.Sprintf("ScheduledEvent: %v", v.ScheduledEvent) i++ } if v.StartedEvent != nil { fields[i] = fmt.Sprintf("StartedEvent: %v", v.StartedEvent) i++ } return fmt.Sprintf("TransientDecisionInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TransientDecisionInfo match the // provided TransientDecisionInfo. // // This function performs a deep comparison. func (v *TransientDecisionInfo) Equals(rhs *TransientDecisionInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ScheduledEvent == nil && rhs.ScheduledEvent == nil) || (v.ScheduledEvent != nil && rhs.ScheduledEvent != nil && v.ScheduledEvent.Equals(rhs.ScheduledEvent))) { return false } if !((v.StartedEvent == nil && rhs.StartedEvent == nil) || (v.StartedEvent != nil && rhs.StartedEvent != nil && v.StartedEvent.Equals(rhs.StartedEvent))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TransientDecisionInfo. func (v *TransientDecisionInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ScheduledEvent != nil { err = multierr.Append(err, enc.AddObject("scheduledEvent", v.ScheduledEvent)) } if v.StartedEvent != nil { err = multierr.Append(err, enc.AddObject("startedEvent", v.StartedEvent)) } return err } // GetScheduledEvent returns the value of ScheduledEvent if it is set or its // zero value if it is unset. func (v *TransientDecisionInfo) GetScheduledEvent() (o *HistoryEvent) { if v != nil && v.ScheduledEvent != nil { return v.ScheduledEvent } return } // IsSetScheduledEvent returns true if ScheduledEvent is not nil. func (v *TransientDecisionInfo) IsSetScheduledEvent() bool { return v != nil && v.ScheduledEvent != nil } // GetStartedEvent returns the value of StartedEvent if it is set or its // zero value if it is unset. func (v *TransientDecisionInfo) GetStartedEvent() (o *HistoryEvent) { if v != nil && v.StartedEvent != nil { return v.StartedEvent } return } // IsSetStartedEvent returns true if StartedEvent is not nil. func (v *TransientDecisionInfo) IsSetStartedEvent() bool { return v != nil && v.StartedEvent != nil } type UniversalPredicateAttributes struct { } // ToWire translates a UniversalPredicateAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UniversalPredicateAttributes) ToWire() (wire.Value, error) { var ( fields [0]wire.Field i int = 0 ) return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UniversalPredicateAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UniversalPredicateAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UniversalPredicateAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UniversalPredicateAttributes) FromWire(w wire.Value) error { for _, field := range w.GetStruct().Fields { switch field.ID { } } return nil } // Encode serializes a UniversalPredicateAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UniversalPredicateAttributes struct could not be encoded. func (v *UniversalPredicateAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a UniversalPredicateAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UniversalPredicateAttributes struct could not be generated from the wire // representation. func (v *UniversalPredicateAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UniversalPredicateAttributes // struct. func (v *UniversalPredicateAttributes) String() string { if v == nil { return "" } var fields [0]string i := 0 return fmt.Sprintf("UniversalPredicateAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UniversalPredicateAttributes match the // provided UniversalPredicateAttributes. // // This function performs a deep comparison. func (v *UniversalPredicateAttributes) Equals(rhs *UniversalPredicateAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UniversalPredicateAttributes. func (v *UniversalPredicateAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } return err } type UpdateDomainInfo struct { Description *string `json:"description,omitempty"` OwnerEmail *string `json:"ownerEmail,omitempty"` Data map[string]string `json:"data,omitempty"` } // ToWire translates a UpdateDomainInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpdateDomainInfo) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Description != nil { w, err = wire.NewValueString(*(v.Description)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.OwnerEmail != nil { w, err = wire.NewValueString(*(v.OwnerEmail)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Data != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.Data)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UpdateDomainInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpdateDomainInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpdateDomainInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpdateDomainInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Description = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.OwnerEmail = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TMap { v.Data, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } // Encode serializes a UpdateDomainInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpdateDomainInfo struct could not be encoded. func (v *UpdateDomainInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Description != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Description)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.OwnerEmail != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.OwnerEmail)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Data != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.Data, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a UpdateDomainInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpdateDomainInfo struct could not be generated from the wire // representation. func (v *UpdateDomainInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Description = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.OwnerEmail = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TMap: v.Data, err = _Map_String_String_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpdateDomainInfo // struct. func (v *UpdateDomainInfo) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Description != nil { fields[i] = fmt.Sprintf("Description: %v", *(v.Description)) i++ } if v.OwnerEmail != nil { fields[i] = fmt.Sprintf("OwnerEmail: %v", *(v.OwnerEmail)) i++ } if v.Data != nil { fields[i] = fmt.Sprintf("Data: %v", v.Data) i++ } return fmt.Sprintf("UpdateDomainInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpdateDomainInfo match the // provided UpdateDomainInfo. // // This function performs a deep comparison. func (v *UpdateDomainInfo) Equals(rhs *UpdateDomainInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Description, rhs.Description) { return false } if !_String_EqualsPtr(v.OwnerEmail, rhs.OwnerEmail) { return false } if !((v.Data == nil && rhs.Data == nil) || (v.Data != nil && rhs.Data != nil && _Map_String_String_Equals(v.Data, rhs.Data))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpdateDomainInfo. func (v *UpdateDomainInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Description != nil { enc.AddString("description", *v.Description) } if v.OwnerEmail != nil { enc.AddString("ownerEmail", *v.OwnerEmail) } if v.Data != nil { err = multierr.Append(err, enc.AddObject("data", (_Map_String_String_Zapper)(v.Data))) } return err } // GetDescription returns the value of Description if it is set or its // zero value if it is unset. func (v *UpdateDomainInfo) GetDescription() (o string) { if v != nil && v.Description != nil { return *v.Description } return } // IsSetDescription returns true if Description is not nil. func (v *UpdateDomainInfo) IsSetDescription() bool { return v != nil && v.Description != nil } // GetOwnerEmail returns the value of OwnerEmail if it is set or its // zero value if it is unset. func (v *UpdateDomainInfo) GetOwnerEmail() (o string) { if v != nil && v.OwnerEmail != nil { return *v.OwnerEmail } return } // IsSetOwnerEmail returns true if OwnerEmail is not nil. func (v *UpdateDomainInfo) IsSetOwnerEmail() bool { return v != nil && v.OwnerEmail != nil } // GetData returns the value of Data if it is set or its // zero value if it is unset. func (v *UpdateDomainInfo) GetData() (o map[string]string) { if v != nil && v.Data != nil { return v.Data } return } // IsSetData returns true if Data is not nil. func (v *UpdateDomainInfo) IsSetData() bool { return v != nil && v.Data != nil } type UpdateDomainRequest struct { Name *string `json:"name,omitempty"` UpdatedInfo *UpdateDomainInfo `json:"updatedInfo,omitempty"` Configuration *DomainConfiguration `json:"configuration,omitempty"` ReplicationConfiguration *DomainReplicationConfiguration `json:"replicationConfiguration,omitempty"` SecurityToken *string `json:"securityToken,omitempty"` DeleteBadBinary *string `json:"deleteBadBinary,omitempty"` FailoverTimeoutInSeconds *int32 `json:"failoverTimeoutInSeconds,omitempty"` } // ToWire translates a UpdateDomainRequest struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpdateDomainRequest) ToWire() (wire.Value, error) { var ( fields [7]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.UpdatedInfo != nil { w, err = v.UpdatedInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Configuration != nil { w, err = v.Configuration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ReplicationConfiguration != nil { w, err = v.ReplicationConfiguration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.SecurityToken != nil { w, err = wire.NewValueString(*(v.SecurityToken)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.DeleteBadBinary != nil { w, err = wire.NewValueString(*(v.DeleteBadBinary)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.FailoverTimeoutInSeconds != nil { w, err = wire.NewValueI32(*(v.FailoverTimeoutInSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _UpdateDomainInfo_Read(w wire.Value) (*UpdateDomainInfo, error) { var v UpdateDomainInfo err := v.FromWire(w) return &v, err } // FromWire deserializes a UpdateDomainRequest struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpdateDomainRequest struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpdateDomainRequest // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpdateDomainRequest) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.UpdatedInfo, err = _UpdateDomainInfo_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.Configuration, err = _DomainConfiguration_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TStruct { v.ReplicationConfiguration, err = _DomainReplicationConfiguration_Read(field.Value) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SecurityToken = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DeleteBadBinary = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.FailoverTimeoutInSeconds = &x if err != nil { return err } } } } return nil } // Encode serializes a UpdateDomainRequest struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpdateDomainRequest struct could not be encoded. func (v *UpdateDomainRequest) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.UpdatedInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.UpdatedInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Configuration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.Configuration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReplicationConfiguration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TStruct}); err != nil { return err } if err := v.ReplicationConfiguration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SecurityToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SecurityToken)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DeleteBadBinary != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DeleteBadBinary)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverTimeoutInSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.FailoverTimeoutInSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _UpdateDomainInfo_Decode(sr stream.Reader) (*UpdateDomainInfo, error) { var v UpdateDomainInfo err := v.Decode(sr) return &v, err } // Decode deserializes a UpdateDomainRequest struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpdateDomainRequest struct could not be generated from the wire // representation. func (v *UpdateDomainRequest) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.UpdatedInfo, err = _UpdateDomainInfo_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.Configuration, err = _DomainConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TStruct: v.ReplicationConfiguration, err = _DomainReplicationConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SecurityToken = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DeleteBadBinary = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.FailoverTimeoutInSeconds = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpdateDomainRequest // struct. func (v *UpdateDomainRequest) String() string { if v == nil { return "" } var fields [7]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.UpdatedInfo != nil { fields[i] = fmt.Sprintf("UpdatedInfo: %v", v.UpdatedInfo) i++ } if v.Configuration != nil { fields[i] = fmt.Sprintf("Configuration: %v", v.Configuration) i++ } if v.ReplicationConfiguration != nil { fields[i] = fmt.Sprintf("ReplicationConfiguration: %v", v.ReplicationConfiguration) i++ } if v.SecurityToken != nil { fields[i] = fmt.Sprintf("SecurityToken: %v", *(v.SecurityToken)) i++ } if v.DeleteBadBinary != nil { fields[i] = fmt.Sprintf("DeleteBadBinary: %v", *(v.DeleteBadBinary)) i++ } if v.FailoverTimeoutInSeconds != nil { fields[i] = fmt.Sprintf("FailoverTimeoutInSeconds: %v", *(v.FailoverTimeoutInSeconds)) i++ } return fmt.Sprintf("UpdateDomainRequest{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpdateDomainRequest match the // provided UpdateDomainRequest. // // This function performs a deep comparison. func (v *UpdateDomainRequest) Equals(rhs *UpdateDomainRequest) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !((v.UpdatedInfo == nil && rhs.UpdatedInfo == nil) || (v.UpdatedInfo != nil && rhs.UpdatedInfo != nil && v.UpdatedInfo.Equals(rhs.UpdatedInfo))) { return false } if !((v.Configuration == nil && rhs.Configuration == nil) || (v.Configuration != nil && rhs.Configuration != nil && v.Configuration.Equals(rhs.Configuration))) { return false } if !((v.ReplicationConfiguration == nil && rhs.ReplicationConfiguration == nil) || (v.ReplicationConfiguration != nil && rhs.ReplicationConfiguration != nil && v.ReplicationConfiguration.Equals(rhs.ReplicationConfiguration))) { return false } if !_String_EqualsPtr(v.SecurityToken, rhs.SecurityToken) { return false } if !_String_EqualsPtr(v.DeleteBadBinary, rhs.DeleteBadBinary) { return false } if !_I32_EqualsPtr(v.FailoverTimeoutInSeconds, rhs.FailoverTimeoutInSeconds) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpdateDomainRequest. func (v *UpdateDomainRequest) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.UpdatedInfo != nil { err = multierr.Append(err, enc.AddObject("updatedInfo", v.UpdatedInfo)) } if v.Configuration != nil { err = multierr.Append(err, enc.AddObject("configuration", v.Configuration)) } if v.ReplicationConfiguration != nil { err = multierr.Append(err, enc.AddObject("replicationConfiguration", v.ReplicationConfiguration)) } if v.SecurityToken != nil { enc.AddString("securityToken", *v.SecurityToken) } if v.DeleteBadBinary != nil { enc.AddString("deleteBadBinary", *v.DeleteBadBinary) } if v.FailoverTimeoutInSeconds != nil { enc.AddInt32("failoverTimeoutInSeconds", *v.FailoverTimeoutInSeconds) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *UpdateDomainRequest) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *UpdateDomainRequest) IsSetName() bool { return v != nil && v.Name != nil } // GetUpdatedInfo returns the value of UpdatedInfo if it is set or its // zero value if it is unset. func (v *UpdateDomainRequest) GetUpdatedInfo() (o *UpdateDomainInfo) { if v != nil && v.UpdatedInfo != nil { return v.UpdatedInfo } return } // IsSetUpdatedInfo returns true if UpdatedInfo is not nil. func (v *UpdateDomainRequest) IsSetUpdatedInfo() bool { return v != nil && v.UpdatedInfo != nil } // GetConfiguration returns the value of Configuration if it is set or its // zero value if it is unset. func (v *UpdateDomainRequest) GetConfiguration() (o *DomainConfiguration) { if v != nil && v.Configuration != nil { return v.Configuration } return } // IsSetConfiguration returns true if Configuration is not nil. func (v *UpdateDomainRequest) IsSetConfiguration() bool { return v != nil && v.Configuration != nil } // GetReplicationConfiguration returns the value of ReplicationConfiguration if it is set or its // zero value if it is unset. func (v *UpdateDomainRequest) GetReplicationConfiguration() (o *DomainReplicationConfiguration) { if v != nil && v.ReplicationConfiguration != nil { return v.ReplicationConfiguration } return } // IsSetReplicationConfiguration returns true if ReplicationConfiguration is not nil. func (v *UpdateDomainRequest) IsSetReplicationConfiguration() bool { return v != nil && v.ReplicationConfiguration != nil } // GetSecurityToken returns the value of SecurityToken if it is set or its // zero value if it is unset. func (v *UpdateDomainRequest) GetSecurityToken() (o string) { if v != nil && v.SecurityToken != nil { return *v.SecurityToken } return } // IsSetSecurityToken returns true if SecurityToken is not nil. func (v *UpdateDomainRequest) IsSetSecurityToken() bool { return v != nil && v.SecurityToken != nil } // GetDeleteBadBinary returns the value of DeleteBadBinary if it is set or its // zero value if it is unset. func (v *UpdateDomainRequest) GetDeleteBadBinary() (o string) { if v != nil && v.DeleteBadBinary != nil { return *v.DeleteBadBinary } return } // IsSetDeleteBadBinary returns true if DeleteBadBinary is not nil. func (v *UpdateDomainRequest) IsSetDeleteBadBinary() bool { return v != nil && v.DeleteBadBinary != nil } // GetFailoverTimeoutInSeconds returns the value of FailoverTimeoutInSeconds if it is set or its // zero value if it is unset. func (v *UpdateDomainRequest) GetFailoverTimeoutInSeconds() (o int32) { if v != nil && v.FailoverTimeoutInSeconds != nil { return *v.FailoverTimeoutInSeconds } return } // IsSetFailoverTimeoutInSeconds returns true if FailoverTimeoutInSeconds is not nil. func (v *UpdateDomainRequest) IsSetFailoverTimeoutInSeconds() bool { return v != nil && v.FailoverTimeoutInSeconds != nil } type UpdateDomainResponse struct { DomainInfo *DomainInfo `json:"domainInfo,omitempty"` Configuration *DomainConfiguration `json:"configuration,omitempty"` ReplicationConfiguration *DomainReplicationConfiguration `json:"replicationConfiguration,omitempty"` FailoverVersion *int64 `json:"failoverVersion,omitempty"` IsGlobalDomain *bool `json:"isGlobalDomain,omitempty"` } // ToWire translates a UpdateDomainResponse struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpdateDomainResponse) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.DomainInfo != nil { w, err = v.DomainInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Configuration != nil { w, err = v.Configuration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ReplicationConfiguration != nil { w, err = v.ReplicationConfiguration.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.FailoverVersion != nil { w, err = wire.NewValueI64(*(v.FailoverVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.IsGlobalDomain != nil { w, err = wire.NewValueBool(*(v.IsGlobalDomain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UpdateDomainResponse struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpdateDomainResponse struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpdateDomainResponse // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpdateDomainResponse) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.DomainInfo, err = _DomainInfo_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Configuration, err = _DomainConfiguration_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ReplicationConfiguration, err = _DomainReplicationConfiguration_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverVersion = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.IsGlobalDomain = &x if err != nil { return err } } } } return nil } // Encode serializes a UpdateDomainResponse struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpdateDomainResponse struct could not be encoded. func (v *UpdateDomainResponse) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.DomainInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Configuration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Configuration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReplicationConfiguration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ReplicationConfiguration.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsGlobalDomain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.IsGlobalDomain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a UpdateDomainResponse struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpdateDomainResponse struct could not be generated from the wire // representation. func (v *UpdateDomainResponse) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.DomainInfo, err = _DomainInfo_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Configuration, err = _DomainConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ReplicationConfiguration, err = _DomainReplicationConfiguration_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverVersion = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.IsGlobalDomain = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpdateDomainResponse // struct. func (v *UpdateDomainResponse) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.DomainInfo != nil { fields[i] = fmt.Sprintf("DomainInfo: %v", v.DomainInfo) i++ } if v.Configuration != nil { fields[i] = fmt.Sprintf("Configuration: %v", v.Configuration) i++ } if v.ReplicationConfiguration != nil { fields[i] = fmt.Sprintf("ReplicationConfiguration: %v", v.ReplicationConfiguration) i++ } if v.FailoverVersion != nil { fields[i] = fmt.Sprintf("FailoverVersion: %v", *(v.FailoverVersion)) i++ } if v.IsGlobalDomain != nil { fields[i] = fmt.Sprintf("IsGlobalDomain: %v", *(v.IsGlobalDomain)) i++ } return fmt.Sprintf("UpdateDomainResponse{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpdateDomainResponse match the // provided UpdateDomainResponse. // // This function performs a deep comparison. func (v *UpdateDomainResponse) Equals(rhs *UpdateDomainResponse) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DomainInfo == nil && rhs.DomainInfo == nil) || (v.DomainInfo != nil && rhs.DomainInfo != nil && v.DomainInfo.Equals(rhs.DomainInfo))) { return false } if !((v.Configuration == nil && rhs.Configuration == nil) || (v.Configuration != nil && rhs.Configuration != nil && v.Configuration.Equals(rhs.Configuration))) { return false } if !((v.ReplicationConfiguration == nil && rhs.ReplicationConfiguration == nil) || (v.ReplicationConfiguration != nil && rhs.ReplicationConfiguration != nil && v.ReplicationConfiguration.Equals(rhs.ReplicationConfiguration))) { return false } if !_I64_EqualsPtr(v.FailoverVersion, rhs.FailoverVersion) { return false } if !_Bool_EqualsPtr(v.IsGlobalDomain, rhs.IsGlobalDomain) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpdateDomainResponse. func (v *UpdateDomainResponse) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainInfo != nil { err = multierr.Append(err, enc.AddObject("domainInfo", v.DomainInfo)) } if v.Configuration != nil { err = multierr.Append(err, enc.AddObject("configuration", v.Configuration)) } if v.ReplicationConfiguration != nil { err = multierr.Append(err, enc.AddObject("replicationConfiguration", v.ReplicationConfiguration)) } if v.FailoverVersion != nil { enc.AddInt64("failoverVersion", *v.FailoverVersion) } if v.IsGlobalDomain != nil { enc.AddBool("isGlobalDomain", *v.IsGlobalDomain) } return err } // GetDomainInfo returns the value of DomainInfo if it is set or its // zero value if it is unset. func (v *UpdateDomainResponse) GetDomainInfo() (o *DomainInfo) { if v != nil && v.DomainInfo != nil { return v.DomainInfo } return } // IsSetDomainInfo returns true if DomainInfo is not nil. func (v *UpdateDomainResponse) IsSetDomainInfo() bool { return v != nil && v.DomainInfo != nil } // GetConfiguration returns the value of Configuration if it is set or its // zero value if it is unset. func (v *UpdateDomainResponse) GetConfiguration() (o *DomainConfiguration) { if v != nil && v.Configuration != nil { return v.Configuration } return } // IsSetConfiguration returns true if Configuration is not nil. func (v *UpdateDomainResponse) IsSetConfiguration() bool { return v != nil && v.Configuration != nil } // GetReplicationConfiguration returns the value of ReplicationConfiguration if it is set or its // zero value if it is unset. func (v *UpdateDomainResponse) GetReplicationConfiguration() (o *DomainReplicationConfiguration) { if v != nil && v.ReplicationConfiguration != nil { return v.ReplicationConfiguration } return } // IsSetReplicationConfiguration returns true if ReplicationConfiguration is not nil. func (v *UpdateDomainResponse) IsSetReplicationConfiguration() bool { return v != nil && v.ReplicationConfiguration != nil } // GetFailoverVersion returns the value of FailoverVersion if it is set or its // zero value if it is unset. func (v *UpdateDomainResponse) GetFailoverVersion() (o int64) { if v != nil && v.FailoverVersion != nil { return *v.FailoverVersion } return } // IsSetFailoverVersion returns true if FailoverVersion is not nil. func (v *UpdateDomainResponse) IsSetFailoverVersion() bool { return v != nil && v.FailoverVersion != nil } // GetIsGlobalDomain returns the value of IsGlobalDomain if it is set or its // zero value if it is unset. func (v *UpdateDomainResponse) GetIsGlobalDomain() (o bool) { if v != nil && v.IsGlobalDomain != nil { return *v.IsGlobalDomain } return } // IsSetIsGlobalDomain returns true if IsGlobalDomain is not nil. func (v *UpdateDomainResponse) IsSetIsGlobalDomain() bool { return v != nil && v.IsGlobalDomain != nil } type UpsertWorkflowSearchAttributesDecisionAttributes struct { SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` } // ToWire translates a UpsertWorkflowSearchAttributesDecisionAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpsertWorkflowSearchAttributesDecisionAttributes) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.SearchAttributes != nil { w, err = v.SearchAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UpsertWorkflowSearchAttributesDecisionAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpsertWorkflowSearchAttributesDecisionAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpsertWorkflowSearchAttributesDecisionAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpsertWorkflowSearchAttributesDecisionAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.SearchAttributes, err = _SearchAttributes_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a UpsertWorkflowSearchAttributesDecisionAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpsertWorkflowSearchAttributesDecisionAttributes struct could not be encoded. func (v *UpsertWorkflowSearchAttributesDecisionAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.SearchAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a UpsertWorkflowSearchAttributesDecisionAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpsertWorkflowSearchAttributesDecisionAttributes struct could not be generated from the wire // representation. func (v *UpsertWorkflowSearchAttributesDecisionAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.SearchAttributes, err = _SearchAttributes_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpsertWorkflowSearchAttributesDecisionAttributes // struct. func (v *UpsertWorkflowSearchAttributesDecisionAttributes) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } return fmt.Sprintf("UpsertWorkflowSearchAttributesDecisionAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpsertWorkflowSearchAttributesDecisionAttributes match the // provided UpsertWorkflowSearchAttributesDecisionAttributes. // // This function performs a deep comparison. func (v *UpsertWorkflowSearchAttributesDecisionAttributes) Equals(rhs *UpsertWorkflowSearchAttributesDecisionAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && v.SearchAttributes.Equals(rhs.SearchAttributes))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpsertWorkflowSearchAttributesDecisionAttributes. func (v *UpsertWorkflowSearchAttributesDecisionAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", v.SearchAttributes)) } return err } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *UpsertWorkflowSearchAttributesDecisionAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *UpsertWorkflowSearchAttributesDecisionAttributes) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } type UpsertWorkflowSearchAttributesEventAttributes struct { DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` } // ToWire translates a UpsertWorkflowSearchAttributesEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *UpsertWorkflowSearchAttributesEventAttributes) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.SearchAttributes != nil { w, err = v.SearchAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a UpsertWorkflowSearchAttributesEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a UpsertWorkflowSearchAttributesEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v UpsertWorkflowSearchAttributesEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *UpsertWorkflowSearchAttributesEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.SearchAttributes, err = _SearchAttributes_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a UpsertWorkflowSearchAttributesEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a UpsertWorkflowSearchAttributesEventAttributes struct could not be encoded. func (v *UpsertWorkflowSearchAttributesEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.SearchAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a UpsertWorkflowSearchAttributesEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a UpsertWorkflowSearchAttributesEventAttributes struct could not be generated from the wire // representation. func (v *UpsertWorkflowSearchAttributesEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.SearchAttributes, err = _SearchAttributes_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a UpsertWorkflowSearchAttributesEventAttributes // struct. func (v *UpsertWorkflowSearchAttributesEventAttributes) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } return fmt.Sprintf("UpsertWorkflowSearchAttributesEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this UpsertWorkflowSearchAttributesEventAttributes match the // provided UpsertWorkflowSearchAttributesEventAttributes. // // This function performs a deep comparison. func (v *UpsertWorkflowSearchAttributesEventAttributes) Equals(rhs *UpsertWorkflowSearchAttributesEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && v.SearchAttributes.Equals(rhs.SearchAttributes))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of UpsertWorkflowSearchAttributesEventAttributes. func (v *UpsertWorkflowSearchAttributesEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", v.SearchAttributes)) } return err } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *UpsertWorkflowSearchAttributesEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *UpsertWorkflowSearchAttributesEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *UpsertWorkflowSearchAttributesEventAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *UpsertWorkflowSearchAttributesEventAttributes) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } type VersionHistories struct { CurrentVersionHistoryIndex *int32 `json:"currentVersionHistoryIndex,omitempty"` Histories []*VersionHistory `json:"histories,omitempty"` } type _List_VersionHistory_ValueList []*VersionHistory func (v _List_VersionHistory_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*VersionHistory', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_VersionHistory_ValueList) Size() int { return len(v) } func (_List_VersionHistory_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_VersionHistory_ValueList) Close() {} // ToWire translates a VersionHistories struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *VersionHistories) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.CurrentVersionHistoryIndex != nil { w, err = wire.NewValueI32(*(v.CurrentVersionHistoryIndex)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Histories != nil { w, err = wire.NewValueList(_List_VersionHistory_ValueList(v.Histories)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _VersionHistory_Read(w wire.Value) (*VersionHistory, error) { var v VersionHistory err := v.FromWire(w) return &v, err } func _List_VersionHistory_Read(l wire.ValueList) ([]*VersionHistory, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*VersionHistory, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _VersionHistory_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a VersionHistories struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a VersionHistories struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v VersionHistories // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *VersionHistories) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.CurrentVersionHistoryIndex = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Histories, err = _List_VersionHistory_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_VersionHistory_Encode(val []*VersionHistory, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*VersionHistory', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a VersionHistories struct directly into bytes, without going // through an intermediary type. // // An error is returned if a VersionHistories struct could not be encoded. func (v *VersionHistories) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CurrentVersionHistoryIndex != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.CurrentVersionHistoryIndex)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Histories != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_VersionHistory_Encode(v.Histories, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _VersionHistory_Decode(sr stream.Reader) (*VersionHistory, error) { var v VersionHistory err := v.Decode(sr) return &v, err } func _List_VersionHistory_Decode(sr stream.Reader) ([]*VersionHistory, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*VersionHistory, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _VersionHistory_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a VersionHistories struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a VersionHistories struct could not be generated from the wire // representation. func (v *VersionHistories) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.CurrentVersionHistoryIndex = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Histories, err = _List_VersionHistory_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a VersionHistories // struct. func (v *VersionHistories) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.CurrentVersionHistoryIndex != nil { fields[i] = fmt.Sprintf("CurrentVersionHistoryIndex: %v", *(v.CurrentVersionHistoryIndex)) i++ } if v.Histories != nil { fields[i] = fmt.Sprintf("Histories: %v", v.Histories) i++ } return fmt.Sprintf("VersionHistories{%v}", strings.Join(fields[:i], ", ")) } func _List_VersionHistory_Equals(lhs, rhs []*VersionHistory) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this VersionHistories match the // provided VersionHistories. // // This function performs a deep comparison. func (v *VersionHistories) Equals(rhs *VersionHistories) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.CurrentVersionHistoryIndex, rhs.CurrentVersionHistoryIndex) { return false } if !((v.Histories == nil && rhs.Histories == nil) || (v.Histories != nil && rhs.Histories != nil && _List_VersionHistory_Equals(v.Histories, rhs.Histories))) { return false } return true } type _List_VersionHistory_Zapper []*VersionHistory // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_VersionHistory_Zapper. func (l _List_VersionHistory_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of VersionHistories. func (v *VersionHistories) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CurrentVersionHistoryIndex != nil { enc.AddInt32("currentVersionHistoryIndex", *v.CurrentVersionHistoryIndex) } if v.Histories != nil { err = multierr.Append(err, enc.AddArray("histories", (_List_VersionHistory_Zapper)(v.Histories))) } return err } // GetCurrentVersionHistoryIndex returns the value of CurrentVersionHistoryIndex if it is set or its // zero value if it is unset. func (v *VersionHistories) GetCurrentVersionHistoryIndex() (o int32) { if v != nil && v.CurrentVersionHistoryIndex != nil { return *v.CurrentVersionHistoryIndex } return } // IsSetCurrentVersionHistoryIndex returns true if CurrentVersionHistoryIndex is not nil. func (v *VersionHistories) IsSetCurrentVersionHistoryIndex() bool { return v != nil && v.CurrentVersionHistoryIndex != nil } // GetHistories returns the value of Histories if it is set or its // zero value if it is unset. func (v *VersionHistories) GetHistories() (o []*VersionHistory) { if v != nil && v.Histories != nil { return v.Histories } return } // IsSetHistories returns true if Histories is not nil. func (v *VersionHistories) IsSetHistories() bool { return v != nil && v.Histories != nil } type VersionHistory struct { BranchToken []byte `json:"branchToken,omitempty"` Items []*VersionHistoryItem `json:"items,omitempty"` } type _List_VersionHistoryItem_ValueList []*VersionHistoryItem func (v _List_VersionHistoryItem_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*VersionHistoryItem', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_VersionHistoryItem_ValueList) Size() int { return len(v) } func (_List_VersionHistoryItem_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_VersionHistoryItem_ValueList) Close() {} // ToWire translates a VersionHistory struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *VersionHistory) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.BranchToken != nil { w, err = wire.NewValueBinary(v.BranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Items != nil { w, err = wire.NewValueList(_List_VersionHistoryItem_ValueList(v.Items)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _VersionHistoryItem_Read(w wire.Value) (*VersionHistoryItem, error) { var v VersionHistoryItem err := v.FromWire(w) return &v, err } func _List_VersionHistoryItem_Read(l wire.ValueList) ([]*VersionHistoryItem, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*VersionHistoryItem, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _VersionHistoryItem_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a VersionHistory struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a VersionHistory struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v VersionHistory // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *VersionHistory) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.BranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TList { v.Items, err = _List_VersionHistoryItem_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_VersionHistoryItem_Encode(val []*VersionHistoryItem, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*VersionHistoryItem', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a VersionHistory struct directly into bytes, without going // through an intermediary type. // // An error is returned if a VersionHistory struct could not be encoded. func (v *VersionHistory) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.BranchToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.BranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Items != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TList}); err != nil { return err } if err := _List_VersionHistoryItem_Encode(v.Items, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _VersionHistoryItem_Decode(sr stream.Reader) (*VersionHistoryItem, error) { var v VersionHistoryItem err := v.Decode(sr) return &v, err } func _List_VersionHistoryItem_Decode(sr stream.Reader) ([]*VersionHistoryItem, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*VersionHistoryItem, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _VersionHistoryItem_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a VersionHistory struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a VersionHistory struct could not be generated from the wire // representation. func (v *VersionHistory) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.BranchToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TList: v.Items, err = _List_VersionHistoryItem_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a VersionHistory // struct. func (v *VersionHistory) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.BranchToken != nil { fields[i] = fmt.Sprintf("BranchToken: %v", v.BranchToken) i++ } if v.Items != nil { fields[i] = fmt.Sprintf("Items: %v", v.Items) i++ } return fmt.Sprintf("VersionHistory{%v}", strings.Join(fields[:i], ", ")) } func _List_VersionHistoryItem_Equals(lhs, rhs []*VersionHistoryItem) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this VersionHistory match the // provided VersionHistory. // // This function performs a deep comparison. func (v *VersionHistory) Equals(rhs *VersionHistory) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.BranchToken == nil && rhs.BranchToken == nil) || (v.BranchToken != nil && rhs.BranchToken != nil && bytes.Equal(v.BranchToken, rhs.BranchToken))) { return false } if !((v.Items == nil && rhs.Items == nil) || (v.Items != nil && rhs.Items != nil && _List_VersionHistoryItem_Equals(v.Items, rhs.Items))) { return false } return true } type _List_VersionHistoryItem_Zapper []*VersionHistoryItem // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_VersionHistoryItem_Zapper. func (l _List_VersionHistoryItem_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of VersionHistory. func (v *VersionHistory) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.BranchToken != nil { enc.AddString("branchToken", base64.StdEncoding.EncodeToString(v.BranchToken)) } if v.Items != nil { err = multierr.Append(err, enc.AddArray("items", (_List_VersionHistoryItem_Zapper)(v.Items))) } return err } // GetBranchToken returns the value of BranchToken if it is set or its // zero value if it is unset. func (v *VersionHistory) GetBranchToken() (o []byte) { if v != nil && v.BranchToken != nil { return v.BranchToken } return } // IsSetBranchToken returns true if BranchToken is not nil. func (v *VersionHistory) IsSetBranchToken() bool { return v != nil && v.BranchToken != nil } // GetItems returns the value of Items if it is set or its // zero value if it is unset. func (v *VersionHistory) GetItems() (o []*VersionHistoryItem) { if v != nil && v.Items != nil { return v.Items } return } // IsSetItems returns true if Items is not nil. func (v *VersionHistory) IsSetItems() bool { return v != nil && v.Items != nil } type VersionHistoryItem struct { EventID *int64 `json:"eventID,omitempty"` Version *int64 `json:"version,omitempty"` } // ToWire translates a VersionHistoryItem struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *VersionHistoryItem) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.EventID != nil { w, err = wire.NewValueI64(*(v.EventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a VersionHistoryItem struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a VersionHistoryItem struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v VersionHistoryItem // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *VersionHistoryItem) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EventID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } } } return nil } // Encode serializes a VersionHistoryItem struct directly into bytes, without going // through an intermediary type. // // An error is returned if a VersionHistoryItem struct could not be encoded. func (v *VersionHistoryItem) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.EventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a VersionHistoryItem struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a VersionHistoryItem struct could not be generated from the wire // representation. func (v *VersionHistoryItem) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EventID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a VersionHistoryItem // struct. func (v *VersionHistoryItem) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.EventID != nil { fields[i] = fmt.Sprintf("EventID: %v", *(v.EventID)) i++ } if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } return fmt.Sprintf("VersionHistoryItem{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this VersionHistoryItem match the // provided VersionHistoryItem. // // This function performs a deep comparison. func (v *VersionHistoryItem) Equals(rhs *VersionHistoryItem) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.EventID, rhs.EventID) { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of VersionHistoryItem. func (v *VersionHistoryItem) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.EventID != nil { enc.AddInt64("eventID", *v.EventID) } if v.Version != nil { enc.AddInt64("version", *v.Version) } return err } // GetEventID returns the value of EventID if it is set or its // zero value if it is unset. func (v *VersionHistoryItem) GetEventID() (o int64) { if v != nil && v.EventID != nil { return *v.EventID } return } // IsSetEventID returns true if EventID is not nil. func (v *VersionHistoryItem) IsSetEventID() bool { return v != nil && v.EventID != nil } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *VersionHistoryItem) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *VersionHistoryItem) IsSetVersion() bool { return v != nil && v.Version != nil } type VirtualQueueState struct { VirtualSliceStates []*VirtualSliceState `json:"virtualSliceStates,omitempty"` } type _List_VirtualSliceState_ValueList []*VirtualSliceState func (v _List_VirtualSliceState_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*VirtualSliceState', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_VirtualSliceState_ValueList) Size() int { return len(v) } func (_List_VirtualSliceState_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_VirtualSliceState_ValueList) Close() {} // ToWire translates a VirtualQueueState struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *VirtualQueueState) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.VirtualSliceStates != nil { w, err = wire.NewValueList(_List_VirtualSliceState_ValueList(v.VirtualSliceStates)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _VirtualSliceState_Read(w wire.Value) (*VirtualSliceState, error) { var v VirtualSliceState err := v.FromWire(w) return &v, err } func _List_VirtualSliceState_Read(l wire.ValueList) ([]*VirtualSliceState, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*VirtualSliceState, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _VirtualSliceState_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a VirtualQueueState struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a VirtualQueueState struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v VirtualQueueState // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *VirtualQueueState) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.VirtualSliceStates, err = _List_VirtualSliceState_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_VirtualSliceState_Encode(val []*VirtualSliceState, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*VirtualSliceState', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a VirtualQueueState struct directly into bytes, without going // through an intermediary type. // // An error is returned if a VirtualQueueState struct could not be encoded. func (v *VirtualQueueState) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.VirtualSliceStates != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_VirtualSliceState_Encode(v.VirtualSliceStates, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _VirtualSliceState_Decode(sr stream.Reader) (*VirtualSliceState, error) { var v VirtualSliceState err := v.Decode(sr) return &v, err } func _List_VirtualSliceState_Decode(sr stream.Reader) ([]*VirtualSliceState, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*VirtualSliceState, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _VirtualSliceState_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a VirtualQueueState struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a VirtualQueueState struct could not be generated from the wire // representation. func (v *VirtualQueueState) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.VirtualSliceStates, err = _List_VirtualSliceState_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a VirtualQueueState // struct. func (v *VirtualQueueState) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.VirtualSliceStates != nil { fields[i] = fmt.Sprintf("VirtualSliceStates: %v", v.VirtualSliceStates) i++ } return fmt.Sprintf("VirtualQueueState{%v}", strings.Join(fields[:i], ", ")) } func _List_VirtualSliceState_Equals(lhs, rhs []*VirtualSliceState) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this VirtualQueueState match the // provided VirtualQueueState. // // This function performs a deep comparison. func (v *VirtualQueueState) Equals(rhs *VirtualQueueState) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.VirtualSliceStates == nil && rhs.VirtualSliceStates == nil) || (v.VirtualSliceStates != nil && rhs.VirtualSliceStates != nil && _List_VirtualSliceState_Equals(v.VirtualSliceStates, rhs.VirtualSliceStates))) { return false } return true } type _List_VirtualSliceState_Zapper []*VirtualSliceState // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_VirtualSliceState_Zapper. func (l _List_VirtualSliceState_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of VirtualQueueState. func (v *VirtualQueueState) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.VirtualSliceStates != nil { err = multierr.Append(err, enc.AddArray("virtualSliceStates", (_List_VirtualSliceState_Zapper)(v.VirtualSliceStates))) } return err } // GetVirtualSliceStates returns the value of VirtualSliceStates if it is set or its // zero value if it is unset. func (v *VirtualQueueState) GetVirtualSliceStates() (o []*VirtualSliceState) { if v != nil && v.VirtualSliceStates != nil { return v.VirtualSliceStates } return } // IsSetVirtualSliceStates returns true if VirtualSliceStates is not nil. func (v *VirtualQueueState) IsSetVirtualSliceStates() bool { return v != nil && v.VirtualSliceStates != nil } type VirtualSliceState struct { TaskRange *TaskRange `json:"taskRange,omitempty"` Predicate *Predicate `json:"predicate,omitempty"` } // ToWire translates a VirtualSliceState struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *VirtualSliceState) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.TaskRange != nil { w, err = v.TaskRange.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Predicate != nil { w, err = v.Predicate.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TaskRange_Read(w wire.Value) (*TaskRange, error) { var v TaskRange err := v.FromWire(w) return &v, err } func _Predicate_Read(w wire.Value) (*Predicate, error) { var v Predicate err := v.FromWire(w) return &v, err } // FromWire deserializes a VirtualSliceState struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a VirtualSliceState struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v VirtualSliceState // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *VirtualSliceState) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.TaskRange, err = _TaskRange_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Predicate, err = _Predicate_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a VirtualSliceState struct directly into bytes, without going // through an intermediary type. // // An error is returned if a VirtualSliceState struct could not be encoded. func (v *VirtualSliceState) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskRange != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.TaskRange.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Predicate != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Predicate.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TaskRange_Decode(sr stream.Reader) (*TaskRange, error) { var v TaskRange err := v.Decode(sr) return &v, err } func _Predicate_Decode(sr stream.Reader) (*Predicate, error) { var v Predicate err := v.Decode(sr) return &v, err } // Decode deserializes a VirtualSliceState struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a VirtualSliceState struct could not be generated from the wire // representation. func (v *VirtualSliceState) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.TaskRange, err = _TaskRange_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Predicate, err = _Predicate_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a VirtualSliceState // struct. func (v *VirtualSliceState) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.TaskRange != nil { fields[i] = fmt.Sprintf("TaskRange: %v", v.TaskRange) i++ } if v.Predicate != nil { fields[i] = fmt.Sprintf("Predicate: %v", v.Predicate) i++ } return fmt.Sprintf("VirtualSliceState{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this VirtualSliceState match the // provided VirtualSliceState. // // This function performs a deep comparison. func (v *VirtualSliceState) Equals(rhs *VirtualSliceState) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskRange == nil && rhs.TaskRange == nil) || (v.TaskRange != nil && rhs.TaskRange != nil && v.TaskRange.Equals(rhs.TaskRange))) { return false } if !((v.Predicate == nil && rhs.Predicate == nil) || (v.Predicate != nil && rhs.Predicate != nil && v.Predicate.Equals(rhs.Predicate))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of VirtualSliceState. func (v *VirtualSliceState) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskRange != nil { err = multierr.Append(err, enc.AddObject("taskRange", v.TaskRange)) } if v.Predicate != nil { err = multierr.Append(err, enc.AddObject("predicate", v.Predicate)) } return err } // GetTaskRange returns the value of TaskRange if it is set or its // zero value if it is unset. func (v *VirtualSliceState) GetTaskRange() (o *TaskRange) { if v != nil && v.TaskRange != nil { return v.TaskRange } return } // IsSetTaskRange returns true if TaskRange is not nil. func (v *VirtualSliceState) IsSetTaskRange() bool { return v != nil && v.TaskRange != nil } // GetPredicate returns the value of Predicate if it is set or its // zero value if it is unset. func (v *VirtualSliceState) GetPredicate() (o *Predicate) { if v != nil && v.Predicate != nil { return v.Predicate } return } // IsSetPredicate returns true if Predicate is not nil. func (v *VirtualSliceState) IsSetPredicate() bool { return v != nil && v.Predicate != nil } type WorkerVersionInfo struct { Impl *string `json:"impl,omitempty"` FeatureVersion *string `json:"featureVersion,omitempty"` } // ToWire translates a WorkerVersionInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkerVersionInfo) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Impl != nil { w, err = wire.NewValueString(*(v.Impl)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.FeatureVersion != nil { w, err = wire.NewValueString(*(v.FeatureVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkerVersionInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkerVersionInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkerVersionInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkerVersionInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Impl = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.FeatureVersion = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkerVersionInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkerVersionInfo struct could not be encoded. func (v *WorkerVersionInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Impl != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Impl)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FeatureVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.FeatureVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkerVersionInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkerVersionInfo struct could not be generated from the wire // representation. func (v *WorkerVersionInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Impl = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.FeatureVersion = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkerVersionInfo // struct. func (v *WorkerVersionInfo) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Impl != nil { fields[i] = fmt.Sprintf("Impl: %v", *(v.Impl)) i++ } if v.FeatureVersion != nil { fields[i] = fmt.Sprintf("FeatureVersion: %v", *(v.FeatureVersion)) i++ } return fmt.Sprintf("WorkerVersionInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkerVersionInfo match the // provided WorkerVersionInfo. // // This function performs a deep comparison. func (v *WorkerVersionInfo) Equals(rhs *WorkerVersionInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Impl, rhs.Impl) { return false } if !_String_EqualsPtr(v.FeatureVersion, rhs.FeatureVersion) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkerVersionInfo. func (v *WorkerVersionInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Impl != nil { enc.AddString("impl", *v.Impl) } if v.FeatureVersion != nil { enc.AddString("featureVersion", *v.FeatureVersion) } return err } // GetImpl returns the value of Impl if it is set or its // zero value if it is unset. func (v *WorkerVersionInfo) GetImpl() (o string) { if v != nil && v.Impl != nil { return *v.Impl } return } // IsSetImpl returns true if Impl is not nil. func (v *WorkerVersionInfo) IsSetImpl() bool { return v != nil && v.Impl != nil } // GetFeatureVersion returns the value of FeatureVersion if it is set or its // zero value if it is unset. func (v *WorkerVersionInfo) GetFeatureVersion() (o string) { if v != nil && v.FeatureVersion != nil { return *v.FeatureVersion } return } // IsSetFeatureVersion returns true if FeatureVersion is not nil. func (v *WorkerVersionInfo) IsSetFeatureVersion() bool { return v != nil && v.FeatureVersion != nil } type WorkflowExecution struct { WorkflowId *string `json:"workflowId,omitempty"` RunId *string `json:"runId,omitempty"` } // ToWire translates a WorkflowExecution struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecution) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecution struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecution struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecution // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecution) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecution struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecution struct could not be encoded. func (v *WorkflowExecution) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecution struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecution struct could not be generated from the wire // representation. func (v *WorkflowExecution) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecution // struct. func (v *WorkflowExecution) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } return fmt.Sprintf("WorkflowExecution{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecution match the // provided WorkflowExecution. // // This function performs a deep comparison. func (v *WorkflowExecution) Equals(rhs *WorkflowExecution) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecution. func (v *WorkflowExecution) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.RunId != nil { enc.AddString("runId", *v.RunId) } return err } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *WorkflowExecution) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *WorkflowExecution) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *WorkflowExecution) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *WorkflowExecution) IsSetRunId() bool { return v != nil && v.RunId != nil } type WorkflowExecutionAlreadyCompletedError struct { Message string `json:"message,required"` } // ToWire translates a WorkflowExecutionAlreadyCompletedError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionAlreadyCompletedError) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) w, err = wire.NewValueString(v.Message), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 1, Value: w} i++ return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionAlreadyCompletedError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionAlreadyCompletedError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionAlreadyCompletedError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionAlreadyCompletedError) FromWire(w wire.Value) error { var err error messageIsSet := false for _, field := range w.GetStruct().Fields { switch field.ID { case 1: if field.Value.Type() == wire.TBinary { v.Message, err = field.Value.GetString(), error(nil) if err != nil { return err } messageIsSet = true } } } if !messageIsSet { return errors.New("field Message of WorkflowExecutionAlreadyCompletedError is required") } return nil } // Encode serializes a WorkflowExecutionAlreadyCompletedError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionAlreadyCompletedError struct could not be encoded. func (v *WorkflowExecutionAlreadyCompletedError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 1, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(v.Message); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionAlreadyCompletedError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionAlreadyCompletedError struct could not be generated from the wire // representation. func (v *WorkflowExecutionAlreadyCompletedError) Decode(sr stream.Reader) error { messageIsSet := false if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 1 && fh.Type == wire.TBinary: v.Message, err = sr.ReadString() if err != nil { return err } messageIsSet = true default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } if !messageIsSet { return errors.New("field Message of WorkflowExecutionAlreadyCompletedError is required") } return nil } // String returns a readable string representation of a WorkflowExecutionAlreadyCompletedError // struct. func (v *WorkflowExecutionAlreadyCompletedError) String() string { if v == nil { return "" } var fields [1]string i := 0 fields[i] = fmt.Sprintf("Message: %v", v.Message) i++ return fmt.Sprintf("WorkflowExecutionAlreadyCompletedError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*WorkflowExecutionAlreadyCompletedError) ErrorName() string { return "WorkflowExecutionAlreadyCompletedError" } // Equals returns true if all the fields of this WorkflowExecutionAlreadyCompletedError match the // provided WorkflowExecutionAlreadyCompletedError. // // This function performs a deep comparison. func (v *WorkflowExecutionAlreadyCompletedError) Equals(rhs *WorkflowExecutionAlreadyCompletedError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !(v.Message == rhs.Message) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionAlreadyCompletedError. func (v *WorkflowExecutionAlreadyCompletedError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } enc.AddString("message", v.Message) return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *WorkflowExecutionAlreadyCompletedError) GetMessage() (o string) { if v != nil { o = v.Message } return } func (v *WorkflowExecutionAlreadyCompletedError) Error() string { return v.String() } type WorkflowExecutionAlreadyStartedError struct { Message *string `json:"message,omitempty"` StartRequestId *string `json:"startRequestId,omitempty"` RunId *string `json:"runId,omitempty"` } // ToWire translates a WorkflowExecutionAlreadyStartedError struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionAlreadyStartedError) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Message != nil { w, err = wire.NewValueString(*(v.Message)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartRequestId != nil { w, err = wire.NewValueString(*(v.StartRequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionAlreadyStartedError struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionAlreadyStartedError struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionAlreadyStartedError // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionAlreadyStartedError) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Message = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.StartRequestId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionAlreadyStartedError struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionAlreadyStartedError struct could not be encoded. func (v *WorkflowExecutionAlreadyStartedError) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Message != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Message)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartRequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.StartRequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionAlreadyStartedError struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionAlreadyStartedError struct could not be generated from the wire // representation. func (v *WorkflowExecutionAlreadyStartedError) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Message = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.StartRequestId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionAlreadyStartedError // struct. func (v *WorkflowExecutionAlreadyStartedError) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Message != nil { fields[i] = fmt.Sprintf("Message: %v", *(v.Message)) i++ } if v.StartRequestId != nil { fields[i] = fmt.Sprintf("StartRequestId: %v", *(v.StartRequestId)) i++ } if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } return fmt.Sprintf("WorkflowExecutionAlreadyStartedError{%v}", strings.Join(fields[:i], ", ")) } // ErrorName is the name of this type as defined in the Thrift // file. func (*WorkflowExecutionAlreadyStartedError) ErrorName() string { return "WorkflowExecutionAlreadyStartedError" } // Equals returns true if all the fields of this WorkflowExecutionAlreadyStartedError match the // provided WorkflowExecutionAlreadyStartedError. // // This function performs a deep comparison. func (v *WorkflowExecutionAlreadyStartedError) Equals(rhs *WorkflowExecutionAlreadyStartedError) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Message, rhs.Message) { return false } if !_String_EqualsPtr(v.StartRequestId, rhs.StartRequestId) { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionAlreadyStartedError. func (v *WorkflowExecutionAlreadyStartedError) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Message != nil { enc.AddString("message", *v.Message) } if v.StartRequestId != nil { enc.AddString("startRequestId", *v.StartRequestId) } if v.RunId != nil { enc.AddString("runId", *v.RunId) } return err } // GetMessage returns the value of Message if it is set or its // zero value if it is unset. func (v *WorkflowExecutionAlreadyStartedError) GetMessage() (o string) { if v != nil && v.Message != nil { return *v.Message } return } // IsSetMessage returns true if Message is not nil. func (v *WorkflowExecutionAlreadyStartedError) IsSetMessage() bool { return v != nil && v.Message != nil } // GetStartRequestId returns the value of StartRequestId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionAlreadyStartedError) GetStartRequestId() (o string) { if v != nil && v.StartRequestId != nil { return *v.StartRequestId } return } // IsSetStartRequestId returns true if StartRequestId is not nil. func (v *WorkflowExecutionAlreadyStartedError) IsSetStartRequestId() bool { return v != nil && v.StartRequestId != nil } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionAlreadyStartedError) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *WorkflowExecutionAlreadyStartedError) IsSetRunId() bool { return v != nil && v.RunId != nil } func (v *WorkflowExecutionAlreadyStartedError) Error() string { return v.String() } type WorkflowExecutionCancelRequestedEventAttributes struct { Cause *string `json:"cause,omitempty"` ExternalInitiatedEventId *int64 `json:"externalInitiatedEventId,omitempty"` ExternalWorkflowExecution *WorkflowExecution `json:"externalWorkflowExecution,omitempty"` Identity *string `json:"identity,omitempty"` RequestId *string `json:"requestId,omitempty"` } // ToWire translates a WorkflowExecutionCancelRequestedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionCancelRequestedEventAttributes) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Cause != nil { w, err = wire.NewValueString(*(v.Cause)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ExternalInitiatedEventId != nil { w, err = wire.NewValueI64(*(v.ExternalInitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ExternalWorkflowExecution != nil { w, err = v.ExternalWorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionCancelRequestedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionCancelRequestedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionCancelRequestedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionCancelRequestedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Cause = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExternalInitiatedEventId = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.ExternalWorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionCancelRequestedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionCancelRequestedEventAttributes struct could not be encoded. func (v *WorkflowExecutionCancelRequestedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Cause != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Cause)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExternalInitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExternalInitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExternalWorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.ExternalWorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionCancelRequestedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionCancelRequestedEventAttributes struct could not be generated from the wire // representation. func (v *WorkflowExecutionCancelRequestedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Cause = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExternalInitiatedEventId = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.ExternalWorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionCancelRequestedEventAttributes // struct. func (v *WorkflowExecutionCancelRequestedEventAttributes) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Cause != nil { fields[i] = fmt.Sprintf("Cause: %v", *(v.Cause)) i++ } if v.ExternalInitiatedEventId != nil { fields[i] = fmt.Sprintf("ExternalInitiatedEventId: %v", *(v.ExternalInitiatedEventId)) i++ } if v.ExternalWorkflowExecution != nil { fields[i] = fmt.Sprintf("ExternalWorkflowExecution: %v", v.ExternalWorkflowExecution) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } return fmt.Sprintf("WorkflowExecutionCancelRequestedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionCancelRequestedEventAttributes match the // provided WorkflowExecutionCancelRequestedEventAttributes. // // This function performs a deep comparison. func (v *WorkflowExecutionCancelRequestedEventAttributes) Equals(rhs *WorkflowExecutionCancelRequestedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Cause, rhs.Cause) { return false } if !_I64_EqualsPtr(v.ExternalInitiatedEventId, rhs.ExternalInitiatedEventId) { return false } if !((v.ExternalWorkflowExecution == nil && rhs.ExternalWorkflowExecution == nil) || (v.ExternalWorkflowExecution != nil && rhs.ExternalWorkflowExecution != nil && v.ExternalWorkflowExecution.Equals(rhs.ExternalWorkflowExecution))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionCancelRequestedEventAttributes. func (v *WorkflowExecutionCancelRequestedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Cause != nil { enc.AddString("cause", *v.Cause) } if v.ExternalInitiatedEventId != nil { enc.AddInt64("externalInitiatedEventId", *v.ExternalInitiatedEventId) } if v.ExternalWorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("externalWorkflowExecution", v.ExternalWorkflowExecution)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } return err } // GetCause returns the value of Cause if it is set or its // zero value if it is unset. func (v *WorkflowExecutionCancelRequestedEventAttributes) GetCause() (o string) { if v != nil && v.Cause != nil { return *v.Cause } return } // IsSetCause returns true if Cause is not nil. func (v *WorkflowExecutionCancelRequestedEventAttributes) IsSetCause() bool { return v != nil && v.Cause != nil } // GetExternalInitiatedEventId returns the value of ExternalInitiatedEventId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionCancelRequestedEventAttributes) GetExternalInitiatedEventId() (o int64) { if v != nil && v.ExternalInitiatedEventId != nil { return *v.ExternalInitiatedEventId } return } // IsSetExternalInitiatedEventId returns true if ExternalInitiatedEventId is not nil. func (v *WorkflowExecutionCancelRequestedEventAttributes) IsSetExternalInitiatedEventId() bool { return v != nil && v.ExternalInitiatedEventId != nil } // GetExternalWorkflowExecution returns the value of ExternalWorkflowExecution if it is set or its // zero value if it is unset. func (v *WorkflowExecutionCancelRequestedEventAttributes) GetExternalWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.ExternalWorkflowExecution != nil { return v.ExternalWorkflowExecution } return } // IsSetExternalWorkflowExecution returns true if ExternalWorkflowExecution is not nil. func (v *WorkflowExecutionCancelRequestedEventAttributes) IsSetExternalWorkflowExecution() bool { return v != nil && v.ExternalWorkflowExecution != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *WorkflowExecutionCancelRequestedEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *WorkflowExecutionCancelRequestedEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionCancelRequestedEventAttributes) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *WorkflowExecutionCancelRequestedEventAttributes) IsSetRequestId() bool { return v != nil && v.RequestId != nil } type WorkflowExecutionCanceledEventAttributes struct { DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` Details []byte `json:"details,omitempty"` } // ToWire translates a WorkflowExecutionCanceledEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionCanceledEventAttributes) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionCanceledEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionCanceledEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionCanceledEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionCanceledEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionCanceledEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionCanceledEventAttributes struct could not be encoded. func (v *WorkflowExecutionCanceledEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionCanceledEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionCanceledEventAttributes struct could not be generated from the wire // representation. func (v *WorkflowExecutionCanceledEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionCanceledEventAttributes // struct. func (v *WorkflowExecutionCanceledEventAttributes) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } return fmt.Sprintf("WorkflowExecutionCanceledEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionCanceledEventAttributes match the // provided WorkflowExecutionCanceledEventAttributes. // // This function performs a deep comparison. func (v *WorkflowExecutionCanceledEventAttributes) Equals(rhs *WorkflowExecutionCanceledEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionCanceledEventAttributes. func (v *WorkflowExecutionCanceledEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } return err } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionCanceledEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *WorkflowExecutionCanceledEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *WorkflowExecutionCanceledEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *WorkflowExecutionCanceledEventAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } type WorkflowExecutionCloseStatus int32 const ( WorkflowExecutionCloseStatusCompleted WorkflowExecutionCloseStatus = 0 WorkflowExecutionCloseStatusFailed WorkflowExecutionCloseStatus = 1 WorkflowExecutionCloseStatusCanceled WorkflowExecutionCloseStatus = 2 WorkflowExecutionCloseStatusTerminated WorkflowExecutionCloseStatus = 3 WorkflowExecutionCloseStatusContinuedAsNew WorkflowExecutionCloseStatus = 4 WorkflowExecutionCloseStatusTimedOut WorkflowExecutionCloseStatus = 5 ) // WorkflowExecutionCloseStatus_Values returns all recognized values of WorkflowExecutionCloseStatus. func WorkflowExecutionCloseStatus_Values() []WorkflowExecutionCloseStatus { return []WorkflowExecutionCloseStatus{ WorkflowExecutionCloseStatusCompleted, WorkflowExecutionCloseStatusFailed, WorkflowExecutionCloseStatusCanceled, WorkflowExecutionCloseStatusTerminated, WorkflowExecutionCloseStatusContinuedAsNew, WorkflowExecutionCloseStatusTimedOut, } } // UnmarshalText tries to decode WorkflowExecutionCloseStatus from a byte slice // containing its name. // // var v WorkflowExecutionCloseStatus // err := v.UnmarshalText([]byte("COMPLETED")) func (v *WorkflowExecutionCloseStatus) UnmarshalText(value []byte) error { switch s := string(value); s { case "COMPLETED": *v = WorkflowExecutionCloseStatusCompleted return nil case "FAILED": *v = WorkflowExecutionCloseStatusFailed return nil case "CANCELED": *v = WorkflowExecutionCloseStatusCanceled return nil case "TERMINATED": *v = WorkflowExecutionCloseStatusTerminated return nil case "CONTINUED_AS_NEW": *v = WorkflowExecutionCloseStatusContinuedAsNew return nil case "TIMED_OUT": *v = WorkflowExecutionCloseStatusTimedOut return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "WorkflowExecutionCloseStatus", err) } *v = WorkflowExecutionCloseStatus(val) return nil } } // MarshalText encodes WorkflowExecutionCloseStatus to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v WorkflowExecutionCloseStatus) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("COMPLETED"), nil case 1: return []byte("FAILED"), nil case 2: return []byte("CANCELED"), nil case 3: return []byte("TERMINATED"), nil case 4: return []byte("CONTINUED_AS_NEW"), nil case 5: return []byte("TIMED_OUT"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionCloseStatus. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v WorkflowExecutionCloseStatus) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "COMPLETED") case 1: enc.AddString("name", "FAILED") case 2: enc.AddString("name", "CANCELED") case 3: enc.AddString("name", "TERMINATED") case 4: enc.AddString("name", "CONTINUED_AS_NEW") case 5: enc.AddString("name", "TIMED_OUT") } return nil } // Ptr returns a pointer to this enum value. func (v WorkflowExecutionCloseStatus) Ptr() *WorkflowExecutionCloseStatus { return &v } // Encode encodes WorkflowExecutionCloseStatus directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v WorkflowExecutionCloseStatus // return v.Encode(sWriter) func (v WorkflowExecutionCloseStatus) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates WorkflowExecutionCloseStatus into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v WorkflowExecutionCloseStatus) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes WorkflowExecutionCloseStatus from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return WorkflowExecutionCloseStatus(0), err // } // // var v WorkflowExecutionCloseStatus // if err := v.FromWire(x); err != nil { // return WorkflowExecutionCloseStatus(0), err // } // return v, nil func (v *WorkflowExecutionCloseStatus) FromWire(w wire.Value) error { *v = (WorkflowExecutionCloseStatus)(w.GetI32()) return nil } // Decode reads off the encoded WorkflowExecutionCloseStatus directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v WorkflowExecutionCloseStatus // if err := v.Decode(sReader); err != nil { // return WorkflowExecutionCloseStatus(0), err // } // return v, nil func (v *WorkflowExecutionCloseStatus) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (WorkflowExecutionCloseStatus)(i) return nil } // String returns a readable string representation of WorkflowExecutionCloseStatus. func (v WorkflowExecutionCloseStatus) String() string { w := int32(v) switch w { case 0: return "COMPLETED" case 1: return "FAILED" case 2: return "CANCELED" case 3: return "TERMINATED" case 4: return "CONTINUED_AS_NEW" case 5: return "TIMED_OUT" } return fmt.Sprintf("WorkflowExecutionCloseStatus(%d)", w) } // Equals returns true if this WorkflowExecutionCloseStatus value matches the provided // value. func (v WorkflowExecutionCloseStatus) Equals(rhs WorkflowExecutionCloseStatus) bool { return v == rhs } // MarshalJSON serializes WorkflowExecutionCloseStatus into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v WorkflowExecutionCloseStatus) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"COMPLETED\""), nil case 1: return ([]byte)("\"FAILED\""), nil case 2: return ([]byte)("\"CANCELED\""), nil case 3: return ([]byte)("\"TERMINATED\""), nil case 4: return ([]byte)("\"CONTINUED_AS_NEW\""), nil case 5: return ([]byte)("\"TIMED_OUT\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode WorkflowExecutionCloseStatus from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *WorkflowExecutionCloseStatus) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "WorkflowExecutionCloseStatus") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "WorkflowExecutionCloseStatus") } *v = (WorkflowExecutionCloseStatus)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "WorkflowExecutionCloseStatus") } } type WorkflowExecutionCompletedEventAttributes struct { Result []byte `json:"result,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` } // ToWire translates a WorkflowExecutionCompletedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionCompletedEventAttributes) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.Result != nil { w, err = wire.NewValueBinary(v.Result), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionCompletedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionCompletedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionCompletedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionCompletedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.Result, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionCompletedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionCompletedEventAttributes struct could not be encoded. func (v *WorkflowExecutionCompletedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Result != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Result); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionCompletedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionCompletedEventAttributes struct could not be generated from the wire // representation. func (v *WorkflowExecutionCompletedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.Result, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionCompletedEventAttributes // struct. func (v *WorkflowExecutionCompletedEventAttributes) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.Result != nil { fields[i] = fmt.Sprintf("Result: %v", v.Result) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } return fmt.Sprintf("WorkflowExecutionCompletedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionCompletedEventAttributes match the // provided WorkflowExecutionCompletedEventAttributes. // // This function performs a deep comparison. func (v *WorkflowExecutionCompletedEventAttributes) Equals(rhs *WorkflowExecutionCompletedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Result == nil && rhs.Result == nil) || (v.Result != nil && rhs.Result != nil && bytes.Equal(v.Result, rhs.Result))) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionCompletedEventAttributes. func (v *WorkflowExecutionCompletedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Result != nil { enc.AddString("result", base64.StdEncoding.EncodeToString(v.Result)) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } return err } // GetResult returns the value of Result if it is set or its // zero value if it is unset. func (v *WorkflowExecutionCompletedEventAttributes) GetResult() (o []byte) { if v != nil && v.Result != nil { return v.Result } return } // IsSetResult returns true if Result is not nil. func (v *WorkflowExecutionCompletedEventAttributes) IsSetResult() bool { return v != nil && v.Result != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionCompletedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *WorkflowExecutionCompletedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } type WorkflowExecutionConfiguration struct { TaskList *TaskList `json:"taskList,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` } // ToWire translates a WorkflowExecutionConfiguration struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionConfiguration) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ExecutionStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TaskStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.TaskStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionConfiguration struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionConfiguration struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionConfiguration // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionConfiguration) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionConfiguration struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionConfiguration struct could not be encoded. func (v *WorkflowExecutionConfiguration) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ExecutionStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.TaskStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionConfiguration struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionConfiguration struct could not be generated from the wire // representation. func (v *WorkflowExecutionConfiguration) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionConfiguration // struct. func (v *WorkflowExecutionConfiguration) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ExecutionStartToCloseTimeoutSeconds: %v", *(v.ExecutionStartToCloseTimeoutSeconds)) i++ } if v.TaskStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("TaskStartToCloseTimeoutSeconds: %v", *(v.TaskStartToCloseTimeoutSeconds)) i++ } return fmt.Sprintf("WorkflowExecutionConfiguration{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionConfiguration match the // provided WorkflowExecutionConfiguration. // // This function performs a deep comparison. func (v *WorkflowExecutionConfiguration) Equals(rhs *WorkflowExecutionConfiguration) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !_I32_EqualsPtr(v.ExecutionStartToCloseTimeoutSeconds, rhs.ExecutionStartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.TaskStartToCloseTimeoutSeconds, rhs.TaskStartToCloseTimeoutSeconds) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionConfiguration. func (v *WorkflowExecutionConfiguration) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.ExecutionStartToCloseTimeoutSeconds != nil { enc.AddInt32("executionStartToCloseTimeoutSeconds", *v.ExecutionStartToCloseTimeoutSeconds) } if v.TaskStartToCloseTimeoutSeconds != nil { enc.AddInt32("taskStartToCloseTimeoutSeconds", *v.TaskStartToCloseTimeoutSeconds) } return err } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *WorkflowExecutionConfiguration) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *WorkflowExecutionConfiguration) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetExecutionStartToCloseTimeoutSeconds returns the value of ExecutionStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionConfiguration) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // IsSetExecutionStartToCloseTimeoutSeconds returns true if ExecutionStartToCloseTimeoutSeconds is not nil. func (v *WorkflowExecutionConfiguration) IsSetExecutionStartToCloseTimeoutSeconds() bool { return v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil } // GetTaskStartToCloseTimeoutSeconds returns the value of TaskStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionConfiguration) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // IsSetTaskStartToCloseTimeoutSeconds returns true if TaskStartToCloseTimeoutSeconds is not nil. func (v *WorkflowExecutionConfiguration) IsSetTaskStartToCloseTimeoutSeconds() bool { return v != nil && v.TaskStartToCloseTimeoutSeconds != nil } type WorkflowExecutionContinuedAsNewEventAttributes struct { NewExecutionRunId *string `json:"newExecutionRunId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` BackoffStartIntervalInSeconds *int32 `json:"backoffStartIntervalInSeconds,omitempty"` Initiator *ContinueAsNewInitiator `json:"initiator,omitempty"` FailureReason *string `json:"failureReason,omitempty"` FailureDetails []byte `json:"failureDetails,omitempty"` LastCompletionResult []byte `json:"lastCompletionResult,omitempty"` Header *Header `json:"header,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // ToWire translates a WorkflowExecutionContinuedAsNewEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionContinuedAsNewEventAttributes) ToWire() (wire.Value, error) { var ( fields [17]wire.Field i int = 0 w wire.Value err error ) if v.NewExecutionRunId != nil { w, err = wire.NewValueString(*(v.NewExecutionRunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ExecutionStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.TaskStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.TaskStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.BackoffStartIntervalInSeconds != nil { w, err = wire.NewValueI32(*(v.BackoffStartIntervalInSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.Initiator != nil { w, err = v.Initiator.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.FailureReason != nil { w, err = wire.NewValueString(*(v.FailureReason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.FailureDetails != nil { w, err = wire.NewValueBinary(v.FailureDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.LastCompletionResult != nil { w, err = wire.NewValueBinary(v.LastCompletionResult), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.Memo != nil { w, err = v.Memo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.SearchAttributes != nil { w, err = v.SearchAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.CronOverlapPolicy != nil { w, err = v.CronOverlapPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.ActiveClusterSelectionPolicy != nil { w, err = v.ActiveClusterSelectionPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionContinuedAsNewEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionContinuedAsNewEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionContinuedAsNewEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionContinuedAsNewEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.NewExecutionRunId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.BackoffStartIntervalInSeconds = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TI32 { var x ContinueAsNewInitiator x, err = _ContinueAsNewInitiator_Read(field.Value) v.Initiator = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.FailureReason = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TBinary { v.FailureDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 120: if field.Value.Type() == wire.TBinary { v.LastCompletionResult, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 130: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } case 140: if field.Value.Type() == wire.TStruct { v.Memo, err = _Memo_Read(field.Value) if err != nil { return err } } case 150: if field.Value.Type() == wire.TStruct { v.SearchAttributes, err = _SearchAttributes_Read(field.Value) if err != nil { return err } } case 160: if field.Value.Type() == wire.TI32 { var x CronOverlapPolicy x, err = _CronOverlapPolicy_Read(field.Value) v.CronOverlapPolicy = &x if err != nil { return err } } case 170: if field.Value.Type() == wire.TStruct { v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionContinuedAsNewEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionContinuedAsNewEventAttributes struct could not be encoded. func (v *WorkflowExecutionContinuedAsNewEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.NewExecutionRunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.NewExecutionRunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ExecutionStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.TaskStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BackoffStartIntervalInSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.BackoffStartIntervalInSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Initiator != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI32}); err != nil { return err } if err := v.Initiator.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailureReason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.FailureReason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailureDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.FailureDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastCompletionResult != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.LastCompletionResult); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Memo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TStruct}); err != nil { return err } if err := v.Memo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TStruct}); err != nil { return err } if err := v.SearchAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronOverlapPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TI32}); err != nil { return err } if err := v.CronOverlapPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterSelectionPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusterSelectionPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionContinuedAsNewEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionContinuedAsNewEventAttributes struct could not be generated from the wire // representation. func (v *WorkflowExecutionContinuedAsNewEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.NewExecutionRunId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.BackoffStartIntervalInSeconds = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI32: var x ContinueAsNewInitiator x, err = _ContinueAsNewInitiator_Decode(sr) v.Initiator = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.FailureReason = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TBinary: v.FailureDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TBinary: v.LastCompletionResult, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TStruct: v.Memo, err = _Memo_Decode(sr) if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TStruct: v.SearchAttributes, err = _SearchAttributes_Decode(sr) if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TI32: var x CronOverlapPolicy x, err = _CronOverlapPolicy_Decode(sr) v.CronOverlapPolicy = &x if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TStruct: v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionContinuedAsNewEventAttributes // struct. func (v *WorkflowExecutionContinuedAsNewEventAttributes) String() string { if v == nil { return "" } var fields [17]string i := 0 if v.NewExecutionRunId != nil { fields[i] = fmt.Sprintf("NewExecutionRunId: %v", *(v.NewExecutionRunId)) i++ } if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ExecutionStartToCloseTimeoutSeconds: %v", *(v.ExecutionStartToCloseTimeoutSeconds)) i++ } if v.TaskStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("TaskStartToCloseTimeoutSeconds: %v", *(v.TaskStartToCloseTimeoutSeconds)) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } if v.BackoffStartIntervalInSeconds != nil { fields[i] = fmt.Sprintf("BackoffStartIntervalInSeconds: %v", *(v.BackoffStartIntervalInSeconds)) i++ } if v.Initiator != nil { fields[i] = fmt.Sprintf("Initiator: %v", *(v.Initiator)) i++ } if v.FailureReason != nil { fields[i] = fmt.Sprintf("FailureReason: %v", *(v.FailureReason)) i++ } if v.FailureDetails != nil { fields[i] = fmt.Sprintf("FailureDetails: %v", v.FailureDetails) i++ } if v.LastCompletionResult != nil { fields[i] = fmt.Sprintf("LastCompletionResult: %v", v.LastCompletionResult) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } if v.Memo != nil { fields[i] = fmt.Sprintf("Memo: %v", v.Memo) i++ } if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } if v.CronOverlapPolicy != nil { fields[i] = fmt.Sprintf("CronOverlapPolicy: %v", *(v.CronOverlapPolicy)) i++ } if v.ActiveClusterSelectionPolicy != nil { fields[i] = fmt.Sprintf("ActiveClusterSelectionPolicy: %v", v.ActiveClusterSelectionPolicy) i++ } return fmt.Sprintf("WorkflowExecutionContinuedAsNewEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionContinuedAsNewEventAttributes match the // provided WorkflowExecutionContinuedAsNewEventAttributes. // // This function performs a deep comparison. func (v *WorkflowExecutionContinuedAsNewEventAttributes) Equals(rhs *WorkflowExecutionContinuedAsNewEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.NewExecutionRunId, rhs.NewExecutionRunId) { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_I32_EqualsPtr(v.ExecutionStartToCloseTimeoutSeconds, rhs.ExecutionStartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.TaskStartToCloseTimeoutSeconds, rhs.TaskStartToCloseTimeoutSeconds) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } if !_I32_EqualsPtr(v.BackoffStartIntervalInSeconds, rhs.BackoffStartIntervalInSeconds) { return false } if !_ContinueAsNewInitiator_EqualsPtr(v.Initiator, rhs.Initiator) { return false } if !_String_EqualsPtr(v.FailureReason, rhs.FailureReason) { return false } if !((v.FailureDetails == nil && rhs.FailureDetails == nil) || (v.FailureDetails != nil && rhs.FailureDetails != nil && bytes.Equal(v.FailureDetails, rhs.FailureDetails))) { return false } if !((v.LastCompletionResult == nil && rhs.LastCompletionResult == nil) || (v.LastCompletionResult != nil && rhs.LastCompletionResult != nil && bytes.Equal(v.LastCompletionResult, rhs.LastCompletionResult))) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } if !((v.Memo == nil && rhs.Memo == nil) || (v.Memo != nil && rhs.Memo != nil && v.Memo.Equals(rhs.Memo))) { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && v.SearchAttributes.Equals(rhs.SearchAttributes))) { return false } if !_CronOverlapPolicy_EqualsPtr(v.CronOverlapPolicy, rhs.CronOverlapPolicy) { return false } if !((v.ActiveClusterSelectionPolicy == nil && rhs.ActiveClusterSelectionPolicy == nil) || (v.ActiveClusterSelectionPolicy != nil && rhs.ActiveClusterSelectionPolicy != nil && v.ActiveClusterSelectionPolicy.Equals(rhs.ActiveClusterSelectionPolicy))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionContinuedAsNewEventAttributes. func (v *WorkflowExecutionContinuedAsNewEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.NewExecutionRunId != nil { enc.AddString("newExecutionRunId", *v.NewExecutionRunId) } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.ExecutionStartToCloseTimeoutSeconds != nil { enc.AddInt32("executionStartToCloseTimeoutSeconds", *v.ExecutionStartToCloseTimeoutSeconds) } if v.TaskStartToCloseTimeoutSeconds != nil { enc.AddInt32("taskStartToCloseTimeoutSeconds", *v.TaskStartToCloseTimeoutSeconds) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } if v.BackoffStartIntervalInSeconds != nil { enc.AddInt32("backoffStartIntervalInSeconds", *v.BackoffStartIntervalInSeconds) } if v.Initiator != nil { err = multierr.Append(err, enc.AddObject("initiator", *v.Initiator)) } if v.FailureReason != nil { enc.AddString("failureReason", *v.FailureReason) } if v.FailureDetails != nil { enc.AddString("failureDetails", base64.StdEncoding.EncodeToString(v.FailureDetails)) } if v.LastCompletionResult != nil { enc.AddString("lastCompletionResult", base64.StdEncoding.EncodeToString(v.LastCompletionResult)) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } if v.Memo != nil { err = multierr.Append(err, enc.AddObject("memo", v.Memo)) } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", v.SearchAttributes)) } if v.CronOverlapPolicy != nil { err = multierr.Append(err, enc.AddObject("cronOverlapPolicy", *v.CronOverlapPolicy)) } if v.ActiveClusterSelectionPolicy != nil { err = multierr.Append(err, enc.AddObject("activeClusterSelectionPolicy", v.ActiveClusterSelectionPolicy)) } return err } // GetNewExecutionRunId returns the value of NewExecutionRunId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetNewExecutionRunId() (o string) { if v != nil && v.NewExecutionRunId != nil { return *v.NewExecutionRunId } return } // IsSetNewExecutionRunId returns true if NewExecutionRunId is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetNewExecutionRunId() bool { return v != nil && v.NewExecutionRunId != nil } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetInput() bool { return v != nil && v.Input != nil } // GetExecutionStartToCloseTimeoutSeconds returns the value of ExecutionStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // IsSetExecutionStartToCloseTimeoutSeconds returns true if ExecutionStartToCloseTimeoutSeconds is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetExecutionStartToCloseTimeoutSeconds() bool { return v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil } // GetTaskStartToCloseTimeoutSeconds returns the value of TaskStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // IsSetTaskStartToCloseTimeoutSeconds returns true if TaskStartToCloseTimeoutSeconds is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetTaskStartToCloseTimeoutSeconds() bool { return v != nil && v.TaskStartToCloseTimeoutSeconds != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } // GetBackoffStartIntervalInSeconds returns the value of BackoffStartIntervalInSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetBackoffStartIntervalInSeconds() (o int32) { if v != nil && v.BackoffStartIntervalInSeconds != nil { return *v.BackoffStartIntervalInSeconds } return } // IsSetBackoffStartIntervalInSeconds returns true if BackoffStartIntervalInSeconds is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetBackoffStartIntervalInSeconds() bool { return v != nil && v.BackoffStartIntervalInSeconds != nil } // GetInitiator returns the value of Initiator if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetInitiator() (o ContinueAsNewInitiator) { if v != nil && v.Initiator != nil { return *v.Initiator } return } // IsSetInitiator returns true if Initiator is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetInitiator() bool { return v != nil && v.Initiator != nil } // GetFailureReason returns the value of FailureReason if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetFailureReason() (o string) { if v != nil && v.FailureReason != nil { return *v.FailureReason } return } // IsSetFailureReason returns true if FailureReason is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetFailureReason() bool { return v != nil && v.FailureReason != nil } // GetFailureDetails returns the value of FailureDetails if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetFailureDetails() (o []byte) { if v != nil && v.FailureDetails != nil { return v.FailureDetails } return } // IsSetFailureDetails returns true if FailureDetails is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetFailureDetails() bool { return v != nil && v.FailureDetails != nil } // GetLastCompletionResult returns the value of LastCompletionResult if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetLastCompletionResult() (o []byte) { if v != nil && v.LastCompletionResult != nil { return v.LastCompletionResult } return } // IsSetLastCompletionResult returns true if LastCompletionResult is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetLastCompletionResult() bool { return v != nil && v.LastCompletionResult != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetHeader() bool { return v != nil && v.Header != nil } // GetMemo returns the value of Memo if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetMemo() (o *Memo) { if v != nil && v.Memo != nil { return v.Memo } return } // IsSetMemo returns true if Memo is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetMemo() bool { return v != nil && v.Memo != nil } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } // GetCronOverlapPolicy returns the value of CronOverlapPolicy if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetCronOverlapPolicy() (o CronOverlapPolicy) { if v != nil && v.CronOverlapPolicy != nil { return *v.CronOverlapPolicy } return } // IsSetCronOverlapPolicy returns true if CronOverlapPolicy is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetCronOverlapPolicy() bool { return v != nil && v.CronOverlapPolicy != nil } // GetActiveClusterSelectionPolicy returns the value of ActiveClusterSelectionPolicy if it is set or its // zero value if it is unset. func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetActiveClusterSelectionPolicy() (o *ActiveClusterSelectionPolicy) { if v != nil && v.ActiveClusterSelectionPolicy != nil { return v.ActiveClusterSelectionPolicy } return } // IsSetActiveClusterSelectionPolicy returns true if ActiveClusterSelectionPolicy is not nil. func (v *WorkflowExecutionContinuedAsNewEventAttributes) IsSetActiveClusterSelectionPolicy() bool { return v != nil && v.ActiveClusterSelectionPolicy != nil } type WorkflowExecutionFailedEventAttributes struct { Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` DecisionTaskCompletedEventId *int64 `json:"decisionTaskCompletedEventId,omitempty"` } // ToWire translates a WorkflowExecutionFailedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionFailedEventAttributes) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.DecisionTaskCompletedEventId != nil { w, err = wire.NewValueI64(*(v.DecisionTaskCompletedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionFailedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionFailedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionFailedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionFailedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionTaskCompletedEventId = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionFailedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionFailedEventAttributes struct could not be encoded. func (v *WorkflowExecutionFailedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskCompletedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionTaskCompletedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionFailedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionFailedEventAttributes struct could not be generated from the wire // representation. func (v *WorkflowExecutionFailedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionTaskCompletedEventId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionFailedEventAttributes // struct. func (v *WorkflowExecutionFailedEventAttributes) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.DecisionTaskCompletedEventId != nil { fields[i] = fmt.Sprintf("DecisionTaskCompletedEventId: %v", *(v.DecisionTaskCompletedEventId)) i++ } return fmt.Sprintf("WorkflowExecutionFailedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionFailedEventAttributes match the // provided WorkflowExecutionFailedEventAttributes. // // This function performs a deep comparison. func (v *WorkflowExecutionFailedEventAttributes) Equals(rhs *WorkflowExecutionFailedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_I64_EqualsPtr(v.DecisionTaskCompletedEventId, rhs.DecisionTaskCompletedEventId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionFailedEventAttributes. func (v *WorkflowExecutionFailedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.DecisionTaskCompletedEventId != nil { enc.AddInt64("decisionTaskCompletedEventId", *v.DecisionTaskCompletedEventId) } return err } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *WorkflowExecutionFailedEventAttributes) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *WorkflowExecutionFailedEventAttributes) IsSetReason() bool { return v != nil && v.Reason != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *WorkflowExecutionFailedEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *WorkflowExecutionFailedEventAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetDecisionTaskCompletedEventId returns the value of DecisionTaskCompletedEventId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionFailedEventAttributes) GetDecisionTaskCompletedEventId() (o int64) { if v != nil && v.DecisionTaskCompletedEventId != nil { return *v.DecisionTaskCompletedEventId } return } // IsSetDecisionTaskCompletedEventId returns true if DecisionTaskCompletedEventId is not nil. func (v *WorkflowExecutionFailedEventAttributes) IsSetDecisionTaskCompletedEventId() bool { return v != nil && v.DecisionTaskCompletedEventId != nil } type WorkflowExecutionFilter struct { WorkflowId *string `json:"workflowId,omitempty"` RunId *string `json:"runId,omitempty"` } // ToWire translates a WorkflowExecutionFilter struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionFilter) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.WorkflowId != nil { w, err = wire.NewValueString(*(v.WorkflowId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.RunId != nil { w, err = wire.NewValueString(*(v.RunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionFilter struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionFilter struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionFilter // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionFilter) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RunId = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionFilter struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionFilter struct could not be encoded. func (v *WorkflowExecutionFilter) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.WorkflowId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionFilter struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionFilter struct could not be generated from the wire // representation. func (v *WorkflowExecutionFilter) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RunId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionFilter // struct. func (v *WorkflowExecutionFilter) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.WorkflowId != nil { fields[i] = fmt.Sprintf("WorkflowId: %v", *(v.WorkflowId)) i++ } if v.RunId != nil { fields[i] = fmt.Sprintf("RunId: %v", *(v.RunId)) i++ } return fmt.Sprintf("WorkflowExecutionFilter{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionFilter match the // provided WorkflowExecutionFilter. // // This function performs a deep comparison. func (v *WorkflowExecutionFilter) Equals(rhs *WorkflowExecutionFilter) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.WorkflowId, rhs.WorkflowId) { return false } if !_String_EqualsPtr(v.RunId, rhs.RunId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionFilter. func (v *WorkflowExecutionFilter) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.WorkflowId != nil { enc.AddString("workflowId", *v.WorkflowId) } if v.RunId != nil { enc.AddString("runId", *v.RunId) } return err } // GetWorkflowId returns the value of WorkflowId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionFilter) GetWorkflowId() (o string) { if v != nil && v.WorkflowId != nil { return *v.WorkflowId } return } // IsSetWorkflowId returns true if WorkflowId is not nil. func (v *WorkflowExecutionFilter) IsSetWorkflowId() bool { return v != nil && v.WorkflowId != nil } // GetRunId returns the value of RunId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionFilter) GetRunId() (o string) { if v != nil && v.RunId != nil { return *v.RunId } return } // IsSetRunId returns true if RunId is not nil. func (v *WorkflowExecutionFilter) IsSetRunId() bool { return v != nil && v.RunId != nil } type WorkflowExecutionInfo struct { Execution *WorkflowExecution `json:"execution,omitempty"` Type *WorkflowType `json:"type,omitempty"` StartTime *int64 `json:"startTime,omitempty"` CloseTime *int64 `json:"closeTime,omitempty"` CloseStatus *WorkflowExecutionCloseStatus `json:"closeStatus,omitempty"` HistoryLength *int64 `json:"historyLength,omitempty"` ParentDomainId *string `json:"parentDomainId,omitempty"` ParentDomainName *string `json:"parentDomainName,omitempty"` ParentInitatedId *int64 `json:"parentInitatedId,omitempty"` ParentExecution *WorkflowExecution `json:"parentExecution,omitempty"` ExecutionTime *int64 `json:"executionTime,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` AutoResetPoints *ResetPoints `json:"autoResetPoints,omitempty"` TaskList *string `json:"taskList,omitempty"` TaskListInfo *TaskList `json:"taskListInfo,omitempty"` IsCron *bool `json:"isCron,omitempty"` UpdateTime *int64 `json:"updateTime,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` CronSchedule *string `json:"cronSchedule,omitempty"` ExecutionStatus *WorkflowExecutionStatus `json:"executionStatus,omitempty"` ScheduledExecutionTime *int64 `json:"scheduledExecutionTime,omitempty"` } // ToWire translates a WorkflowExecutionInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionInfo) ToWire() (wire.Value, error) { var ( fields [24]wire.Field i int = 0 w wire.Value err error ) if v.Execution != nil { w, err = v.Execution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Type != nil { w, err = v.Type.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.StartTime != nil { w, err = wire.NewValueI64(*(v.StartTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.CloseTime != nil { w, err = wire.NewValueI64(*(v.CloseTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.CloseStatus != nil { w, err = v.CloseStatus.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.HistoryLength != nil { w, err = wire.NewValueI64(*(v.HistoryLength)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.ParentDomainId != nil { w, err = wire.NewValueString(*(v.ParentDomainId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.ParentDomainName != nil { w, err = wire.NewValueString(*(v.ParentDomainName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 71, Value: w} i++ } if v.ParentInitatedId != nil { w, err = wire.NewValueI64(*(v.ParentInitatedId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 72, Value: w} i++ } if v.ParentExecution != nil { w, err = v.ParentExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.ExecutionTime != nil { w, err = wire.NewValueI64(*(v.ExecutionTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.Memo != nil { w, err = v.Memo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.SearchAttributes != nil { w, err = v.SearchAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 101, Value: w} i++ } if v.AutoResetPoints != nil { w, err = v.AutoResetPoints.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.TaskList != nil { w, err = wire.NewValueString(*(v.TaskList)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.TaskListInfo != nil { w, err = v.TaskListInfo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 121, Value: w} i++ } if v.IsCron != nil { w, err = wire.NewValueBool(*(v.IsCron)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.UpdateTime != nil { w, err = wire.NewValueI64(*(v.UpdateTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.PartitionConfig != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.PartitionConfig)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.CronOverlapPolicy != nil { w, err = v.CronOverlapPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.ActiveClusterSelectionPolicy != nil { w, err = v.ActiveClusterSelectionPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } if v.CronSchedule != nil { w, err = wire.NewValueString(*(v.CronSchedule)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 180, Value: w} i++ } if v.ExecutionStatus != nil { w, err = v.ExecutionStatus.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 190, Value: w} i++ } if v.ScheduledExecutionTime != nil { w, err = wire.NewValueI64(*(v.ScheduledExecutionTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 200, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _ResetPoints_Read(w wire.Value) (*ResetPoints, error) { var v ResetPoints err := v.FromWire(w) return &v, err } func _WorkflowExecutionStatus_Read(w wire.Value) (WorkflowExecutionStatus, error) { var v WorkflowExecutionStatus err := v.FromWire(w) return v, err } // FromWire deserializes a WorkflowExecutionInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.Execution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.Type, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartTime = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CloseTime = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x WorkflowExecutionCloseStatus x, err = _WorkflowExecutionCloseStatus_Read(field.Value) v.CloseStatus = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.HistoryLength = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ParentDomainId = &x if err != nil { return err } } case 71: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ParentDomainName = &x if err != nil { return err } } case 72: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ParentInitatedId = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TStruct { v.ParentExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 90: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExecutionTime = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TStruct { v.Memo, err = _Memo_Read(field.Value) if err != nil { return err } } case 101: if field.Value.Type() == wire.TStruct { v.SearchAttributes, err = _SearchAttributes_Read(field.Value) if err != nil { return err } } case 110: if field.Value.Type() == wire.TStruct { v.AutoResetPoints, err = _ResetPoints_Read(field.Value) if err != nil { return err } } case 120: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TaskList = &x if err != nil { return err } } case 121: if field.Value.Type() == wire.TStruct { v.TaskListInfo, err = _TaskList_Read(field.Value) if err != nil { return err } } case 130: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.IsCron = &x if err != nil { return err } } case 140: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.UpdateTime = &x if err != nil { return err } } case 150: if field.Value.Type() == wire.TMap { v.PartitionConfig, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } case 160: if field.Value.Type() == wire.TI32 { var x CronOverlapPolicy x, err = _CronOverlapPolicy_Read(field.Value) v.CronOverlapPolicy = &x if err != nil { return err } } case 170: if field.Value.Type() == wire.TStruct { v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Read(field.Value) if err != nil { return err } } case 180: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CronSchedule = &x if err != nil { return err } } case 190: if field.Value.Type() == wire.TI32 { var x WorkflowExecutionStatus x, err = _WorkflowExecutionStatus_Read(field.Value) v.ExecutionStatus = &x if err != nil { return err } } case 200: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledExecutionTime = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionInfo struct could not be encoded. func (v *WorkflowExecutionInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Execution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.Execution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Type != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.Type.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CloseTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CloseTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CloseStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := v.CloseStatus.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryLength != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.HistoryLength)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentDomainId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ParentDomainId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentDomainName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 71, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ParentDomainName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentInitatedId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 72, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ParentInitatedId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TStruct}); err != nil { return err } if err := v.ParentExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExecutionTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Memo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TStruct}); err != nil { return err } if err := v.Memo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 101, Type: wire.TStruct}); err != nil { return err } if err := v.SearchAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AutoResetPoints != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TStruct}); err != nil { return err } if err := v.AutoResetPoints.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TaskList)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListInfo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 121, Type: wire.TStruct}); err != nil { return err } if err := v.TaskListInfo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsCron != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.IsCron)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.UpdateTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.UpdateTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PartitionConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.PartitionConfig, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronOverlapPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TI32}); err != nil { return err } if err := v.CronOverlapPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterSelectionPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusterSelectionPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronSchedule != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 180, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CronSchedule)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 190, Type: wire.TI32}); err != nil { return err } if err := v.ExecutionStatus.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledExecutionTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 200, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledExecutionTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _ResetPoints_Decode(sr stream.Reader) (*ResetPoints, error) { var v ResetPoints err := v.Decode(sr) return &v, err } func _WorkflowExecutionStatus_Decode(sr stream.Reader) (WorkflowExecutionStatus, error) { var v WorkflowExecutionStatus err := v.Decode(sr) return v, err } // Decode deserializes a WorkflowExecutionInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionInfo struct could not be generated from the wire // representation. func (v *WorkflowExecutionInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.Execution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.Type, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartTime = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CloseTime = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x WorkflowExecutionCloseStatus x, err = _WorkflowExecutionCloseStatus_Decode(sr) v.CloseStatus = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.HistoryLength = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ParentDomainId = &x if err != nil { return err } case fh.ID == 71 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ParentDomainName = &x if err != nil { return err } case fh.ID == 72 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ParentInitatedId = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TStruct: v.ParentExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExecutionTime = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TStruct: v.Memo, err = _Memo_Decode(sr) if err != nil { return err } case fh.ID == 101 && fh.Type == wire.TStruct: v.SearchAttributes, err = _SearchAttributes_Decode(sr) if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TStruct: v.AutoResetPoints, err = _ResetPoints_Decode(sr) if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TaskList = &x if err != nil { return err } case fh.ID == 121 && fh.Type == wire.TStruct: v.TaskListInfo, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.IsCron = &x if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.UpdateTime = &x if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TMap: v.PartitionConfig, err = _Map_String_String_Decode(sr) if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TI32: var x CronOverlapPolicy x, err = _CronOverlapPolicy_Decode(sr) v.CronOverlapPolicy = &x if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TStruct: v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Decode(sr) if err != nil { return err } case fh.ID == 180 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CronSchedule = &x if err != nil { return err } case fh.ID == 190 && fh.Type == wire.TI32: var x WorkflowExecutionStatus x, err = _WorkflowExecutionStatus_Decode(sr) v.ExecutionStatus = &x if err != nil { return err } case fh.ID == 200 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledExecutionTime = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionInfo // struct. func (v *WorkflowExecutionInfo) String() string { if v == nil { return "" } var fields [24]string i := 0 if v.Execution != nil { fields[i] = fmt.Sprintf("Execution: %v", v.Execution) i++ } if v.Type != nil { fields[i] = fmt.Sprintf("Type: %v", v.Type) i++ } if v.StartTime != nil { fields[i] = fmt.Sprintf("StartTime: %v", *(v.StartTime)) i++ } if v.CloseTime != nil { fields[i] = fmt.Sprintf("CloseTime: %v", *(v.CloseTime)) i++ } if v.CloseStatus != nil { fields[i] = fmt.Sprintf("CloseStatus: %v", *(v.CloseStatus)) i++ } if v.HistoryLength != nil { fields[i] = fmt.Sprintf("HistoryLength: %v", *(v.HistoryLength)) i++ } if v.ParentDomainId != nil { fields[i] = fmt.Sprintf("ParentDomainId: %v", *(v.ParentDomainId)) i++ } if v.ParentDomainName != nil { fields[i] = fmt.Sprintf("ParentDomainName: %v", *(v.ParentDomainName)) i++ } if v.ParentInitatedId != nil { fields[i] = fmt.Sprintf("ParentInitatedId: %v", *(v.ParentInitatedId)) i++ } if v.ParentExecution != nil { fields[i] = fmt.Sprintf("ParentExecution: %v", v.ParentExecution) i++ } if v.ExecutionTime != nil { fields[i] = fmt.Sprintf("ExecutionTime: %v", *(v.ExecutionTime)) i++ } if v.Memo != nil { fields[i] = fmt.Sprintf("Memo: %v", v.Memo) i++ } if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } if v.AutoResetPoints != nil { fields[i] = fmt.Sprintf("AutoResetPoints: %v", v.AutoResetPoints) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", *(v.TaskList)) i++ } if v.TaskListInfo != nil { fields[i] = fmt.Sprintf("TaskListInfo: %v", v.TaskListInfo) i++ } if v.IsCron != nil { fields[i] = fmt.Sprintf("IsCron: %v", *(v.IsCron)) i++ } if v.UpdateTime != nil { fields[i] = fmt.Sprintf("UpdateTime: %v", *(v.UpdateTime)) i++ } if v.PartitionConfig != nil { fields[i] = fmt.Sprintf("PartitionConfig: %v", v.PartitionConfig) i++ } if v.CronOverlapPolicy != nil { fields[i] = fmt.Sprintf("CronOverlapPolicy: %v", *(v.CronOverlapPolicy)) i++ } if v.ActiveClusterSelectionPolicy != nil { fields[i] = fmt.Sprintf("ActiveClusterSelectionPolicy: %v", v.ActiveClusterSelectionPolicy) i++ } if v.CronSchedule != nil { fields[i] = fmt.Sprintf("CronSchedule: %v", *(v.CronSchedule)) i++ } if v.ExecutionStatus != nil { fields[i] = fmt.Sprintf("ExecutionStatus: %v", *(v.ExecutionStatus)) i++ } if v.ScheduledExecutionTime != nil { fields[i] = fmt.Sprintf("ScheduledExecutionTime: %v", *(v.ScheduledExecutionTime)) i++ } return fmt.Sprintf("WorkflowExecutionInfo{%v}", strings.Join(fields[:i], ", ")) } func _WorkflowExecutionStatus_EqualsPtr(lhs, rhs *WorkflowExecutionStatus) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this WorkflowExecutionInfo match the // provided WorkflowExecutionInfo. // // This function performs a deep comparison. func (v *WorkflowExecutionInfo) Equals(rhs *WorkflowExecutionInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.Execution == nil && rhs.Execution == nil) || (v.Execution != nil && rhs.Execution != nil && v.Execution.Equals(rhs.Execution))) { return false } if !((v.Type == nil && rhs.Type == nil) || (v.Type != nil && rhs.Type != nil && v.Type.Equals(rhs.Type))) { return false } if !_I64_EqualsPtr(v.StartTime, rhs.StartTime) { return false } if !_I64_EqualsPtr(v.CloseTime, rhs.CloseTime) { return false } if !_WorkflowExecutionCloseStatus_EqualsPtr(v.CloseStatus, rhs.CloseStatus) { return false } if !_I64_EqualsPtr(v.HistoryLength, rhs.HistoryLength) { return false } if !_String_EqualsPtr(v.ParentDomainId, rhs.ParentDomainId) { return false } if !_String_EqualsPtr(v.ParentDomainName, rhs.ParentDomainName) { return false } if !_I64_EqualsPtr(v.ParentInitatedId, rhs.ParentInitatedId) { return false } if !((v.ParentExecution == nil && rhs.ParentExecution == nil) || (v.ParentExecution != nil && rhs.ParentExecution != nil && v.ParentExecution.Equals(rhs.ParentExecution))) { return false } if !_I64_EqualsPtr(v.ExecutionTime, rhs.ExecutionTime) { return false } if !((v.Memo == nil && rhs.Memo == nil) || (v.Memo != nil && rhs.Memo != nil && v.Memo.Equals(rhs.Memo))) { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && v.SearchAttributes.Equals(rhs.SearchAttributes))) { return false } if !((v.AutoResetPoints == nil && rhs.AutoResetPoints == nil) || (v.AutoResetPoints != nil && rhs.AutoResetPoints != nil && v.AutoResetPoints.Equals(rhs.AutoResetPoints))) { return false } if !_String_EqualsPtr(v.TaskList, rhs.TaskList) { return false } if !((v.TaskListInfo == nil && rhs.TaskListInfo == nil) || (v.TaskListInfo != nil && rhs.TaskListInfo != nil && v.TaskListInfo.Equals(rhs.TaskListInfo))) { return false } if !_Bool_EqualsPtr(v.IsCron, rhs.IsCron) { return false } if !_I64_EqualsPtr(v.UpdateTime, rhs.UpdateTime) { return false } if !((v.PartitionConfig == nil && rhs.PartitionConfig == nil) || (v.PartitionConfig != nil && rhs.PartitionConfig != nil && _Map_String_String_Equals(v.PartitionConfig, rhs.PartitionConfig))) { return false } if !_CronOverlapPolicy_EqualsPtr(v.CronOverlapPolicy, rhs.CronOverlapPolicy) { return false } if !((v.ActiveClusterSelectionPolicy == nil && rhs.ActiveClusterSelectionPolicy == nil) || (v.ActiveClusterSelectionPolicy != nil && rhs.ActiveClusterSelectionPolicy != nil && v.ActiveClusterSelectionPolicy.Equals(rhs.ActiveClusterSelectionPolicy))) { return false } if !_String_EqualsPtr(v.CronSchedule, rhs.CronSchedule) { return false } if !_WorkflowExecutionStatus_EqualsPtr(v.ExecutionStatus, rhs.ExecutionStatus) { return false } if !_I64_EqualsPtr(v.ScheduledExecutionTime, rhs.ScheduledExecutionTime) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionInfo. func (v *WorkflowExecutionInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Execution != nil { err = multierr.Append(err, enc.AddObject("execution", v.Execution)) } if v.Type != nil { err = multierr.Append(err, enc.AddObject("type", v.Type)) } if v.StartTime != nil { enc.AddInt64("startTime", *v.StartTime) } if v.CloseTime != nil { enc.AddInt64("closeTime", *v.CloseTime) } if v.CloseStatus != nil { err = multierr.Append(err, enc.AddObject("closeStatus", *v.CloseStatus)) } if v.HistoryLength != nil { enc.AddInt64("historyLength", *v.HistoryLength) } if v.ParentDomainId != nil { enc.AddString("parentDomainId", *v.ParentDomainId) } if v.ParentDomainName != nil { enc.AddString("parentDomainName", *v.ParentDomainName) } if v.ParentInitatedId != nil { enc.AddInt64("parentInitatedId", *v.ParentInitatedId) } if v.ParentExecution != nil { err = multierr.Append(err, enc.AddObject("parentExecution", v.ParentExecution)) } if v.ExecutionTime != nil { enc.AddInt64("executionTime", *v.ExecutionTime) } if v.Memo != nil { err = multierr.Append(err, enc.AddObject("memo", v.Memo)) } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", v.SearchAttributes)) } if v.AutoResetPoints != nil { err = multierr.Append(err, enc.AddObject("autoResetPoints", v.AutoResetPoints)) } if v.TaskList != nil { enc.AddString("taskList", *v.TaskList) } if v.TaskListInfo != nil { err = multierr.Append(err, enc.AddObject("taskListInfo", v.TaskListInfo)) } if v.IsCron != nil { enc.AddBool("isCron", *v.IsCron) } if v.UpdateTime != nil { enc.AddInt64("updateTime", *v.UpdateTime) } if v.PartitionConfig != nil { err = multierr.Append(err, enc.AddObject("partitionConfig", (_Map_String_String_Zapper)(v.PartitionConfig))) } if v.CronOverlapPolicy != nil { err = multierr.Append(err, enc.AddObject("cronOverlapPolicy", *v.CronOverlapPolicy)) } if v.ActiveClusterSelectionPolicy != nil { err = multierr.Append(err, enc.AddObject("activeClusterSelectionPolicy", v.ActiveClusterSelectionPolicy)) } if v.CronSchedule != nil { enc.AddString("cronSchedule", *v.CronSchedule) } if v.ExecutionStatus != nil { err = multierr.Append(err, enc.AddObject("executionStatus", *v.ExecutionStatus)) } if v.ScheduledExecutionTime != nil { enc.AddInt64("scheduledExecutionTime", *v.ScheduledExecutionTime) } return err } // GetExecution returns the value of Execution if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // IsSetExecution returns true if Execution is not nil. func (v *WorkflowExecutionInfo) IsSetExecution() bool { return v != nil && v.Execution != nil } // GetType returns the value of Type if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetType() (o *WorkflowType) { if v != nil && v.Type != nil { return v.Type } return } // IsSetType returns true if Type is not nil. func (v *WorkflowExecutionInfo) IsSetType() bool { return v != nil && v.Type != nil } // GetStartTime returns the value of StartTime if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetStartTime() (o int64) { if v != nil && v.StartTime != nil { return *v.StartTime } return } // IsSetStartTime returns true if StartTime is not nil. func (v *WorkflowExecutionInfo) IsSetStartTime() bool { return v != nil && v.StartTime != nil } // GetCloseTime returns the value of CloseTime if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCloseTime() (o int64) { if v != nil && v.CloseTime != nil { return *v.CloseTime } return } // IsSetCloseTime returns true if CloseTime is not nil. func (v *WorkflowExecutionInfo) IsSetCloseTime() bool { return v != nil && v.CloseTime != nil } // GetCloseStatus returns the value of CloseStatus if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCloseStatus() (o WorkflowExecutionCloseStatus) { if v != nil && v.CloseStatus != nil { return *v.CloseStatus } return } // IsSetCloseStatus returns true if CloseStatus is not nil. func (v *WorkflowExecutionInfo) IsSetCloseStatus() bool { return v != nil && v.CloseStatus != nil } // GetHistoryLength returns the value of HistoryLength if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetHistoryLength() (o int64) { if v != nil && v.HistoryLength != nil { return *v.HistoryLength } return } // IsSetHistoryLength returns true if HistoryLength is not nil. func (v *WorkflowExecutionInfo) IsSetHistoryLength() bool { return v != nil && v.HistoryLength != nil } // GetParentDomainId returns the value of ParentDomainId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetParentDomainId() (o string) { if v != nil && v.ParentDomainId != nil { return *v.ParentDomainId } return } // IsSetParentDomainId returns true if ParentDomainId is not nil. func (v *WorkflowExecutionInfo) IsSetParentDomainId() bool { return v != nil && v.ParentDomainId != nil } // GetParentDomainName returns the value of ParentDomainName if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetParentDomainName() (o string) { if v != nil && v.ParentDomainName != nil { return *v.ParentDomainName } return } // IsSetParentDomainName returns true if ParentDomainName is not nil. func (v *WorkflowExecutionInfo) IsSetParentDomainName() bool { return v != nil && v.ParentDomainName != nil } // GetParentInitatedId returns the value of ParentInitatedId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetParentInitatedId() (o int64) { if v != nil && v.ParentInitatedId != nil { return *v.ParentInitatedId } return } // IsSetParentInitatedId returns true if ParentInitatedId is not nil. func (v *WorkflowExecutionInfo) IsSetParentInitatedId() bool { return v != nil && v.ParentInitatedId != nil } // GetParentExecution returns the value of ParentExecution if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetParentExecution() (o *WorkflowExecution) { if v != nil && v.ParentExecution != nil { return v.ParentExecution } return } // IsSetParentExecution returns true if ParentExecution is not nil. func (v *WorkflowExecutionInfo) IsSetParentExecution() bool { return v != nil && v.ParentExecution != nil } // GetExecutionTime returns the value of ExecutionTime if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetExecutionTime() (o int64) { if v != nil && v.ExecutionTime != nil { return *v.ExecutionTime } return } // IsSetExecutionTime returns true if ExecutionTime is not nil. func (v *WorkflowExecutionInfo) IsSetExecutionTime() bool { return v != nil && v.ExecutionTime != nil } // GetMemo returns the value of Memo if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetMemo() (o *Memo) { if v != nil && v.Memo != nil { return v.Memo } return } // IsSetMemo returns true if Memo is not nil. func (v *WorkflowExecutionInfo) IsSetMemo() bool { return v != nil && v.Memo != nil } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *WorkflowExecutionInfo) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } // GetAutoResetPoints returns the value of AutoResetPoints if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetAutoResetPoints() (o *ResetPoints) { if v != nil && v.AutoResetPoints != nil { return v.AutoResetPoints } return } // IsSetAutoResetPoints returns true if AutoResetPoints is not nil. func (v *WorkflowExecutionInfo) IsSetAutoResetPoints() bool { return v != nil && v.AutoResetPoints != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetTaskList() (o string) { if v != nil && v.TaskList != nil { return *v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *WorkflowExecutionInfo) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetTaskListInfo returns the value of TaskListInfo if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetTaskListInfo() (o *TaskList) { if v != nil && v.TaskListInfo != nil { return v.TaskListInfo } return } // IsSetTaskListInfo returns true if TaskListInfo is not nil. func (v *WorkflowExecutionInfo) IsSetTaskListInfo() bool { return v != nil && v.TaskListInfo != nil } // GetIsCron returns the value of IsCron if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetIsCron() (o bool) { if v != nil && v.IsCron != nil { return *v.IsCron } return } // IsSetIsCron returns true if IsCron is not nil. func (v *WorkflowExecutionInfo) IsSetIsCron() bool { return v != nil && v.IsCron != nil } // GetUpdateTime returns the value of UpdateTime if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetUpdateTime() (o int64) { if v != nil && v.UpdateTime != nil { return *v.UpdateTime } return } // IsSetUpdateTime returns true if UpdateTime is not nil. func (v *WorkflowExecutionInfo) IsSetUpdateTime() bool { return v != nil && v.UpdateTime != nil } // GetPartitionConfig returns the value of PartitionConfig if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // IsSetPartitionConfig returns true if PartitionConfig is not nil. func (v *WorkflowExecutionInfo) IsSetPartitionConfig() bool { return v != nil && v.PartitionConfig != nil } // GetCronOverlapPolicy returns the value of CronOverlapPolicy if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCronOverlapPolicy() (o CronOverlapPolicy) { if v != nil && v.CronOverlapPolicy != nil { return *v.CronOverlapPolicy } return } // IsSetCronOverlapPolicy returns true if CronOverlapPolicy is not nil. func (v *WorkflowExecutionInfo) IsSetCronOverlapPolicy() bool { return v != nil && v.CronOverlapPolicy != nil } // GetActiveClusterSelectionPolicy returns the value of ActiveClusterSelectionPolicy if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetActiveClusterSelectionPolicy() (o *ActiveClusterSelectionPolicy) { if v != nil && v.ActiveClusterSelectionPolicy != nil { return v.ActiveClusterSelectionPolicy } return } // IsSetActiveClusterSelectionPolicy returns true if ActiveClusterSelectionPolicy is not nil. func (v *WorkflowExecutionInfo) IsSetActiveClusterSelectionPolicy() bool { return v != nil && v.ActiveClusterSelectionPolicy != nil } // GetCronSchedule returns the value of CronSchedule if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCronSchedule() (o string) { if v != nil && v.CronSchedule != nil { return *v.CronSchedule } return } // IsSetCronSchedule returns true if CronSchedule is not nil. func (v *WorkflowExecutionInfo) IsSetCronSchedule() bool { return v != nil && v.CronSchedule != nil } // GetExecutionStatus returns the value of ExecutionStatus if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetExecutionStatus() (o WorkflowExecutionStatus) { if v != nil && v.ExecutionStatus != nil { return *v.ExecutionStatus } return } // IsSetExecutionStatus returns true if ExecutionStatus is not nil. func (v *WorkflowExecutionInfo) IsSetExecutionStatus() bool { return v != nil && v.ExecutionStatus != nil } // GetScheduledExecutionTime returns the value of ScheduledExecutionTime if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetScheduledExecutionTime() (o int64) { if v != nil && v.ScheduledExecutionTime != nil { return *v.ScheduledExecutionTime } return } // IsSetScheduledExecutionTime returns true if ScheduledExecutionTime is not nil. func (v *WorkflowExecutionInfo) IsSetScheduledExecutionTime() bool { return v != nil && v.ScheduledExecutionTime != nil } type WorkflowExecutionSignaledEventAttributes struct { SignalName *string `json:"signalName,omitempty"` Input []byte `json:"input,omitempty"` Identity *string `json:"identity,omitempty"` RequestId *string `json:"requestId,omitempty"` } // ToWire translates a WorkflowExecutionSignaledEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionSignaledEventAttributes) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.SignalName != nil { w, err = wire.NewValueString(*(v.SignalName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionSignaledEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionSignaledEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionSignaledEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionSignaledEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.SignalName = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionSignaledEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionSignaledEventAttributes struct could not be encoded. func (v *WorkflowExecutionSignaledEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.SignalName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.SignalName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionSignaledEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionSignaledEventAttributes struct could not be generated from the wire // representation. func (v *WorkflowExecutionSignaledEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.SignalName = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionSignaledEventAttributes // struct. func (v *WorkflowExecutionSignaledEventAttributes) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.SignalName != nil { fields[i] = fmt.Sprintf("SignalName: %v", *(v.SignalName)) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } return fmt.Sprintf("WorkflowExecutionSignaledEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionSignaledEventAttributes match the // provided WorkflowExecutionSignaledEventAttributes. // // This function performs a deep comparison. func (v *WorkflowExecutionSignaledEventAttributes) Equals(rhs *WorkflowExecutionSignaledEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.SignalName, rhs.SignalName) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionSignaledEventAttributes. func (v *WorkflowExecutionSignaledEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.SignalName != nil { enc.AddString("signalName", *v.SignalName) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } return err } // GetSignalName returns the value of SignalName if it is set or its // zero value if it is unset. func (v *WorkflowExecutionSignaledEventAttributes) GetSignalName() (o string) { if v != nil && v.SignalName != nil { return *v.SignalName } return } // IsSetSignalName returns true if SignalName is not nil. func (v *WorkflowExecutionSignaledEventAttributes) IsSetSignalName() bool { return v != nil && v.SignalName != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *WorkflowExecutionSignaledEventAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *WorkflowExecutionSignaledEventAttributes) IsSetInput() bool { return v != nil && v.Input != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *WorkflowExecutionSignaledEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *WorkflowExecutionSignaledEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionSignaledEventAttributes) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *WorkflowExecutionSignaledEventAttributes) IsSetRequestId() bool { return v != nil && v.RequestId != nil } type WorkflowExecutionStartedEventAttributes struct { WorkflowType *WorkflowType `json:"workflowType,omitempty"` ParentWorkflowDomain *string `json:"parentWorkflowDomain,omitempty"` ParentWorkflowExecution *WorkflowExecution `json:"parentWorkflowExecution,omitempty"` ParentInitiatedEventId *int64 `json:"parentInitiatedEventId,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` ContinuedExecutionRunId *string `json:"continuedExecutionRunId,omitempty"` Initiator *ContinueAsNewInitiator `json:"initiator,omitempty"` ContinuedFailureReason *string `json:"continuedFailureReason,omitempty"` ContinuedFailureDetails []byte `json:"continuedFailureDetails,omitempty"` LastCompletionResult []byte `json:"lastCompletionResult,omitempty"` OriginalExecutionRunId *string `json:"originalExecutionRunId,omitempty"` Identity *string `json:"identity,omitempty"` FirstExecutionRunId *string `json:"firstExecutionRunId,omitempty"` FirstScheduledTimeNano *int64 `json:"firstScheduledTimeNano,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` Attempt *int32 `json:"attempt,omitempty"` ExpirationTimestamp *int64 `json:"expirationTimestamp,omitempty"` CronSchedule *string `json:"cronSchedule,omitempty"` FirstDecisionTaskBackoffSeconds *int32 `json:"firstDecisionTaskBackoffSeconds,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` PrevAutoResetPoints *ResetPoints `json:"prevAutoResetPoints,omitempty"` Header *Header `json:"header,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` RequestId *string `json:"requestId,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // ToWire translates a WorkflowExecutionStartedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionStartedEventAttributes) ToWire() (wire.Value, error) { var ( fields [30]wire.Field i int = 0 w wire.Value err error ) if v.WorkflowType != nil { w, err = v.WorkflowType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ParentWorkflowDomain != nil { w, err = wire.NewValueString(*(v.ParentWorkflowDomain)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.ParentWorkflowExecution != nil { w, err = v.ParentWorkflowExecution.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.ParentInitiatedEventId != nil { w, err = wire.NewValueI64(*(v.ParentInitiatedEventId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.TaskList != nil { w, err = v.TaskList.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ExecutionStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.TaskStartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.TaskStartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.ContinuedExecutionRunId != nil { w, err = wire.NewValueString(*(v.ContinuedExecutionRunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 54, Value: w} i++ } if v.Initiator != nil { w, err = v.Initiator.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 55, Value: w} i++ } if v.ContinuedFailureReason != nil { w, err = wire.NewValueString(*(v.ContinuedFailureReason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 56, Value: w} i++ } if v.ContinuedFailureDetails != nil { w, err = wire.NewValueBinary(v.ContinuedFailureDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 57, Value: w} i++ } if v.LastCompletionResult != nil { w, err = wire.NewValueBinary(v.LastCompletionResult), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 58, Value: w} i++ } if v.OriginalExecutionRunId != nil { w, err = wire.NewValueString(*(v.OriginalExecutionRunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 59, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.FirstExecutionRunId != nil { w, err = wire.NewValueString(*(v.FirstExecutionRunId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 61, Value: w} i++ } if v.FirstScheduledTimeNano != nil { w, err = wire.NewValueI64(*(v.FirstScheduledTimeNano)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 62, Value: w} i++ } if v.RetryPolicy != nil { w, err = v.RetryPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI32(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.ExpirationTimestamp != nil { w, err = wire.NewValueI64(*(v.ExpirationTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.CronSchedule != nil { w, err = wire.NewValueString(*(v.CronSchedule)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.FirstDecisionTaskBackoffSeconds != nil { w, err = wire.NewValueI32(*(v.FirstDecisionTaskBackoffSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.Memo != nil { w, err = v.Memo.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.SearchAttributes != nil { w, err = v.SearchAttributes.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 121, Value: w} i++ } if v.PrevAutoResetPoints != nil { w, err = v.PrevAutoResetPoints.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 140, Value: w} i++ } if v.PartitionConfig != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.PartitionConfig)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 150, Value: w} i++ } if v.RequestId != nil { w, err = wire.NewValueString(*(v.RequestId)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 160, Value: w} i++ } if v.CronOverlapPolicy != nil { w, err = v.CronOverlapPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 170, Value: w} i++ } if v.ActiveClusterSelectionPolicy != nil { w, err = v.ActiveClusterSelectionPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 180, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionStartedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionStartedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionStartedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionStartedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TStruct { v.WorkflowType, err = _WorkflowType_Read(field.Value) if err != nil { return err } } case 12: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ParentWorkflowDomain = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TStruct { v.ParentWorkflowExecution, err = _WorkflowExecution_Read(field.Value) if err != nil { return err } } case 16: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ParentInitiatedEventId = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TStruct { v.TaskList, err = _TaskList_Read(field.Value) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } } case 54: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ContinuedExecutionRunId = &x if err != nil { return err } } case 55: if field.Value.Type() == wire.TI32 { var x ContinueAsNewInitiator x, err = _ContinueAsNewInitiator_Read(field.Value) v.Initiator = &x if err != nil { return err } } case 56: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ContinuedFailureReason = &x if err != nil { return err } } case 57: if field.Value.Type() == wire.TBinary { v.ContinuedFailureDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 58: if field.Value.Type() == wire.TBinary { v.LastCompletionResult, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 59: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.OriginalExecutionRunId = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } case 61: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.FirstExecutionRunId = &x if err != nil { return err } } case 62: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FirstScheduledTimeNano = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TStruct { v.RetryPolicy, err = _RetryPolicy_Read(field.Value) if err != nil { return err } } case 80: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Attempt = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExpirationTimestamp = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CronSchedule = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.FirstDecisionTaskBackoffSeconds = &x if err != nil { return err } } case 120: if field.Value.Type() == wire.TStruct { v.Memo, err = _Memo_Read(field.Value) if err != nil { return err } } case 121: if field.Value.Type() == wire.TStruct { v.SearchAttributes, err = _SearchAttributes_Read(field.Value) if err != nil { return err } } case 130: if field.Value.Type() == wire.TStruct { v.PrevAutoResetPoints, err = _ResetPoints_Read(field.Value) if err != nil { return err } } case 140: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } case 150: if field.Value.Type() == wire.TMap { v.PartitionConfig, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } case 160: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestId = &x if err != nil { return err } } case 170: if field.Value.Type() == wire.TI32 { var x CronOverlapPolicy x, err = _CronOverlapPolicy_Read(field.Value) v.CronOverlapPolicy = &x if err != nil { return err } } case 180: if field.Value.Type() == wire.TStruct { v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionStartedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionStartedEventAttributes struct could not be encoded. func (v *WorkflowExecutionStartedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.WorkflowType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TStruct}); err != nil { return err } if err := v.WorkflowType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentWorkflowDomain != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ParentWorkflowDomain)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentWorkflowExecution != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TStruct}); err != nil { return err } if err := v.ParentWorkflowExecution.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentInitiatedEventId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ParentInitiatedEventId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TStruct}); err != nil { return err } if err := v.TaskList.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ExecutionStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskStartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.TaskStartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ContinuedExecutionRunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 54, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ContinuedExecutionRunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Initiator != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 55, Type: wire.TI32}); err != nil { return err } if err := v.Initiator.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ContinuedFailureReason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 56, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ContinuedFailureReason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ContinuedFailureDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 57, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.ContinuedFailureDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastCompletionResult != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 58, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.LastCompletionResult); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.OriginalExecutionRunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 59, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.OriginalExecutionRunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstExecutionRunId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 61, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.FirstExecutionRunId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstScheduledTimeNano != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 62, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FirstScheduledTimeNano)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TStruct}); err != nil { return err } if err := v.RetryPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExpirationTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExpirationTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronSchedule != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CronSchedule)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstDecisionTaskBackoffSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.FirstDecisionTaskBackoffSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Memo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TStruct}); err != nil { return err } if err := v.Memo.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 121, Type: wire.TStruct}); err != nil { return err } if err := v.SearchAttributes.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PrevAutoResetPoints != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TStruct}); err != nil { return err } if err := v.PrevAutoResetPoints.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 140, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PartitionConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 150, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.PartitionConfig, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestId != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 160, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestId)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronOverlapPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 170, Type: wire.TI32}); err != nil { return err } if err := v.CronOverlapPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterSelectionPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 180, Type: wire.TStruct}); err != nil { return err } if err := v.ActiveClusterSelectionPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionStartedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionStartedEventAttributes struct could not be generated from the wire // representation. func (v *WorkflowExecutionStartedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TStruct: v.WorkflowType, err = _WorkflowType_Decode(sr) if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ParentWorkflowDomain = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TStruct: v.ParentWorkflowExecution, err = _WorkflowExecution_Decode(sr) if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ParentInitiatedEventId = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TStruct: v.TaskList, err = _TaskList_Decode(sr) if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ExecutionStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.TaskStartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 54 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ContinuedExecutionRunId = &x if err != nil { return err } case fh.ID == 55 && fh.Type == wire.TI32: var x ContinueAsNewInitiator x, err = _ContinueAsNewInitiator_Decode(sr) v.Initiator = &x if err != nil { return err } case fh.ID == 56 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ContinuedFailureReason = &x if err != nil { return err } case fh.ID == 57 && fh.Type == wire.TBinary: v.ContinuedFailureDetails, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 58 && fh.Type == wire.TBinary: v.LastCompletionResult, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 59 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.OriginalExecutionRunId = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } case fh.ID == 61 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.FirstExecutionRunId = &x if err != nil { return err } case fh.ID == 62 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FirstScheduledTimeNano = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TStruct: v.RetryPolicy, err = _RetryPolicy_Decode(sr) if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Attempt = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExpirationTimestamp = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CronSchedule = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.FirstDecisionTaskBackoffSeconds = &x if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TStruct: v.Memo, err = _Memo_Decode(sr) if err != nil { return err } case fh.ID == 121 && fh.Type == wire.TStruct: v.SearchAttributes, err = _SearchAttributes_Decode(sr) if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TStruct: v.PrevAutoResetPoints, err = _ResetPoints_Decode(sr) if err != nil { return err } case fh.ID == 140 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } case fh.ID == 150 && fh.Type == wire.TMap: v.PartitionConfig, err = _Map_String_String_Decode(sr) if err != nil { return err } case fh.ID == 160 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestId = &x if err != nil { return err } case fh.ID == 170 && fh.Type == wire.TI32: var x CronOverlapPolicy x, err = _CronOverlapPolicy_Decode(sr) v.CronOverlapPolicy = &x if err != nil { return err } case fh.ID == 180 && fh.Type == wire.TStruct: v.ActiveClusterSelectionPolicy, err = _ActiveClusterSelectionPolicy_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionStartedEventAttributes // struct. func (v *WorkflowExecutionStartedEventAttributes) String() string { if v == nil { return "" } var fields [30]string i := 0 if v.WorkflowType != nil { fields[i] = fmt.Sprintf("WorkflowType: %v", v.WorkflowType) i++ } if v.ParentWorkflowDomain != nil { fields[i] = fmt.Sprintf("ParentWorkflowDomain: %v", *(v.ParentWorkflowDomain)) i++ } if v.ParentWorkflowExecution != nil { fields[i] = fmt.Sprintf("ParentWorkflowExecution: %v", v.ParentWorkflowExecution) i++ } if v.ParentInitiatedEventId != nil { fields[i] = fmt.Sprintf("ParentInitiatedEventId: %v", *(v.ParentInitiatedEventId)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", v.TaskList) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.ExecutionStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ExecutionStartToCloseTimeoutSeconds: %v", *(v.ExecutionStartToCloseTimeoutSeconds)) i++ } if v.TaskStartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("TaskStartToCloseTimeoutSeconds: %v", *(v.TaskStartToCloseTimeoutSeconds)) i++ } if v.ContinuedExecutionRunId != nil { fields[i] = fmt.Sprintf("ContinuedExecutionRunId: %v", *(v.ContinuedExecutionRunId)) i++ } if v.Initiator != nil { fields[i] = fmt.Sprintf("Initiator: %v", *(v.Initiator)) i++ } if v.ContinuedFailureReason != nil { fields[i] = fmt.Sprintf("ContinuedFailureReason: %v", *(v.ContinuedFailureReason)) i++ } if v.ContinuedFailureDetails != nil { fields[i] = fmt.Sprintf("ContinuedFailureDetails: %v", v.ContinuedFailureDetails) i++ } if v.LastCompletionResult != nil { fields[i] = fmt.Sprintf("LastCompletionResult: %v", v.LastCompletionResult) i++ } if v.OriginalExecutionRunId != nil { fields[i] = fmt.Sprintf("OriginalExecutionRunId: %v", *(v.OriginalExecutionRunId)) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } if v.FirstExecutionRunId != nil { fields[i] = fmt.Sprintf("FirstExecutionRunId: %v", *(v.FirstExecutionRunId)) i++ } if v.FirstScheduledTimeNano != nil { fields[i] = fmt.Sprintf("FirstScheduledTimeNano: %v", *(v.FirstScheduledTimeNano)) i++ } if v.RetryPolicy != nil { fields[i] = fmt.Sprintf("RetryPolicy: %v", v.RetryPolicy) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.ExpirationTimestamp != nil { fields[i] = fmt.Sprintf("ExpirationTimestamp: %v", *(v.ExpirationTimestamp)) i++ } if v.CronSchedule != nil { fields[i] = fmt.Sprintf("CronSchedule: %v", *(v.CronSchedule)) i++ } if v.FirstDecisionTaskBackoffSeconds != nil { fields[i] = fmt.Sprintf("FirstDecisionTaskBackoffSeconds: %v", *(v.FirstDecisionTaskBackoffSeconds)) i++ } if v.Memo != nil { fields[i] = fmt.Sprintf("Memo: %v", v.Memo) i++ } if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } if v.PrevAutoResetPoints != nil { fields[i] = fmt.Sprintf("PrevAutoResetPoints: %v", v.PrevAutoResetPoints) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } if v.PartitionConfig != nil { fields[i] = fmt.Sprintf("PartitionConfig: %v", v.PartitionConfig) i++ } if v.RequestId != nil { fields[i] = fmt.Sprintf("RequestId: %v", *(v.RequestId)) i++ } if v.CronOverlapPolicy != nil { fields[i] = fmt.Sprintf("CronOverlapPolicy: %v", *(v.CronOverlapPolicy)) i++ } if v.ActiveClusterSelectionPolicy != nil { fields[i] = fmt.Sprintf("ActiveClusterSelectionPolicy: %v", v.ActiveClusterSelectionPolicy) i++ } return fmt.Sprintf("WorkflowExecutionStartedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionStartedEventAttributes match the // provided WorkflowExecutionStartedEventAttributes. // // This function performs a deep comparison. func (v *WorkflowExecutionStartedEventAttributes) Equals(rhs *WorkflowExecutionStartedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.WorkflowType == nil && rhs.WorkflowType == nil) || (v.WorkflowType != nil && rhs.WorkflowType != nil && v.WorkflowType.Equals(rhs.WorkflowType))) { return false } if !_String_EqualsPtr(v.ParentWorkflowDomain, rhs.ParentWorkflowDomain) { return false } if !((v.ParentWorkflowExecution == nil && rhs.ParentWorkflowExecution == nil) || (v.ParentWorkflowExecution != nil && rhs.ParentWorkflowExecution != nil && v.ParentWorkflowExecution.Equals(rhs.ParentWorkflowExecution))) { return false } if !_I64_EqualsPtr(v.ParentInitiatedEventId, rhs.ParentInitiatedEventId) { return false } if !((v.TaskList == nil && rhs.TaskList == nil) || (v.TaskList != nil && rhs.TaskList != nil && v.TaskList.Equals(rhs.TaskList))) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !_I32_EqualsPtr(v.ExecutionStartToCloseTimeoutSeconds, rhs.ExecutionStartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.TaskStartToCloseTimeoutSeconds, rhs.TaskStartToCloseTimeoutSeconds) { return false } if !_String_EqualsPtr(v.ContinuedExecutionRunId, rhs.ContinuedExecutionRunId) { return false } if !_ContinueAsNewInitiator_EqualsPtr(v.Initiator, rhs.Initiator) { return false } if !_String_EqualsPtr(v.ContinuedFailureReason, rhs.ContinuedFailureReason) { return false } if !((v.ContinuedFailureDetails == nil && rhs.ContinuedFailureDetails == nil) || (v.ContinuedFailureDetails != nil && rhs.ContinuedFailureDetails != nil && bytes.Equal(v.ContinuedFailureDetails, rhs.ContinuedFailureDetails))) { return false } if !((v.LastCompletionResult == nil && rhs.LastCompletionResult == nil) || (v.LastCompletionResult != nil && rhs.LastCompletionResult != nil && bytes.Equal(v.LastCompletionResult, rhs.LastCompletionResult))) { return false } if !_String_EqualsPtr(v.OriginalExecutionRunId, rhs.OriginalExecutionRunId) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } if !_String_EqualsPtr(v.FirstExecutionRunId, rhs.FirstExecutionRunId) { return false } if !_I64_EqualsPtr(v.FirstScheduledTimeNano, rhs.FirstScheduledTimeNano) { return false } if !((v.RetryPolicy == nil && rhs.RetryPolicy == nil) || (v.RetryPolicy != nil && rhs.RetryPolicy != nil && v.RetryPolicy.Equals(rhs.RetryPolicy))) { return false } if !_I32_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_I64_EqualsPtr(v.ExpirationTimestamp, rhs.ExpirationTimestamp) { return false } if !_String_EqualsPtr(v.CronSchedule, rhs.CronSchedule) { return false } if !_I32_EqualsPtr(v.FirstDecisionTaskBackoffSeconds, rhs.FirstDecisionTaskBackoffSeconds) { return false } if !((v.Memo == nil && rhs.Memo == nil) || (v.Memo != nil && rhs.Memo != nil && v.Memo.Equals(rhs.Memo))) { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && v.SearchAttributes.Equals(rhs.SearchAttributes))) { return false } if !((v.PrevAutoResetPoints == nil && rhs.PrevAutoResetPoints == nil) || (v.PrevAutoResetPoints != nil && rhs.PrevAutoResetPoints != nil && v.PrevAutoResetPoints.Equals(rhs.PrevAutoResetPoints))) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } if !((v.PartitionConfig == nil && rhs.PartitionConfig == nil) || (v.PartitionConfig != nil && rhs.PartitionConfig != nil && _Map_String_String_Equals(v.PartitionConfig, rhs.PartitionConfig))) { return false } if !_String_EqualsPtr(v.RequestId, rhs.RequestId) { return false } if !_CronOverlapPolicy_EqualsPtr(v.CronOverlapPolicy, rhs.CronOverlapPolicy) { return false } if !((v.ActiveClusterSelectionPolicy == nil && rhs.ActiveClusterSelectionPolicy == nil) || (v.ActiveClusterSelectionPolicy != nil && rhs.ActiveClusterSelectionPolicy != nil && v.ActiveClusterSelectionPolicy.Equals(rhs.ActiveClusterSelectionPolicy))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionStartedEventAttributes. func (v *WorkflowExecutionStartedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.WorkflowType != nil { err = multierr.Append(err, enc.AddObject("workflowType", v.WorkflowType)) } if v.ParentWorkflowDomain != nil { enc.AddString("parentWorkflowDomain", *v.ParentWorkflowDomain) } if v.ParentWorkflowExecution != nil { err = multierr.Append(err, enc.AddObject("parentWorkflowExecution", v.ParentWorkflowExecution)) } if v.ParentInitiatedEventId != nil { enc.AddInt64("parentInitiatedEventId", *v.ParentInitiatedEventId) } if v.TaskList != nil { err = multierr.Append(err, enc.AddObject("taskList", v.TaskList)) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.ExecutionStartToCloseTimeoutSeconds != nil { enc.AddInt32("executionStartToCloseTimeoutSeconds", *v.ExecutionStartToCloseTimeoutSeconds) } if v.TaskStartToCloseTimeoutSeconds != nil { enc.AddInt32("taskStartToCloseTimeoutSeconds", *v.TaskStartToCloseTimeoutSeconds) } if v.ContinuedExecutionRunId != nil { enc.AddString("continuedExecutionRunId", *v.ContinuedExecutionRunId) } if v.Initiator != nil { err = multierr.Append(err, enc.AddObject("initiator", *v.Initiator)) } if v.ContinuedFailureReason != nil { enc.AddString("continuedFailureReason", *v.ContinuedFailureReason) } if v.ContinuedFailureDetails != nil { enc.AddString("continuedFailureDetails", base64.StdEncoding.EncodeToString(v.ContinuedFailureDetails)) } if v.LastCompletionResult != nil { enc.AddString("lastCompletionResult", base64.StdEncoding.EncodeToString(v.LastCompletionResult)) } if v.OriginalExecutionRunId != nil { enc.AddString("originalExecutionRunId", *v.OriginalExecutionRunId) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } if v.FirstExecutionRunId != nil { enc.AddString("firstExecutionRunId", *v.FirstExecutionRunId) } if v.FirstScheduledTimeNano != nil { enc.AddInt64("firstScheduledTimeNano", *v.FirstScheduledTimeNano) } if v.RetryPolicy != nil { err = multierr.Append(err, enc.AddObject("retryPolicy", v.RetryPolicy)) } if v.Attempt != nil { enc.AddInt32("attempt", *v.Attempt) } if v.ExpirationTimestamp != nil { enc.AddInt64("expirationTimestamp", *v.ExpirationTimestamp) } if v.CronSchedule != nil { enc.AddString("cronSchedule", *v.CronSchedule) } if v.FirstDecisionTaskBackoffSeconds != nil { enc.AddInt32("firstDecisionTaskBackoffSeconds", *v.FirstDecisionTaskBackoffSeconds) } if v.Memo != nil { err = multierr.Append(err, enc.AddObject("memo", v.Memo)) } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", v.SearchAttributes)) } if v.PrevAutoResetPoints != nil { err = multierr.Append(err, enc.AddObject("prevAutoResetPoints", v.PrevAutoResetPoints)) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } if v.PartitionConfig != nil { err = multierr.Append(err, enc.AddObject("partitionConfig", (_Map_String_String_Zapper)(v.PartitionConfig))) } if v.RequestId != nil { enc.AddString("requestId", *v.RequestId) } if v.CronOverlapPolicy != nil { err = multierr.Append(err, enc.AddObject("cronOverlapPolicy", *v.CronOverlapPolicy)) } if v.ActiveClusterSelectionPolicy != nil { err = multierr.Append(err, enc.AddObject("activeClusterSelectionPolicy", v.ActiveClusterSelectionPolicy)) } return err } // GetWorkflowType returns the value of WorkflowType if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // IsSetWorkflowType returns true if WorkflowType is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetWorkflowType() bool { return v != nil && v.WorkflowType != nil } // GetParentWorkflowDomain returns the value of ParentWorkflowDomain if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetParentWorkflowDomain() (o string) { if v != nil && v.ParentWorkflowDomain != nil { return *v.ParentWorkflowDomain } return } // IsSetParentWorkflowDomain returns true if ParentWorkflowDomain is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetParentWorkflowDomain() bool { return v != nil && v.ParentWorkflowDomain != nil } // GetParentWorkflowExecution returns the value of ParentWorkflowExecution if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetParentWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.ParentWorkflowExecution != nil { return v.ParentWorkflowExecution } return } // IsSetParentWorkflowExecution returns true if ParentWorkflowExecution is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetParentWorkflowExecution() bool { return v != nil && v.ParentWorkflowExecution != nil } // GetParentInitiatedEventId returns the value of ParentInitiatedEventId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetParentInitiatedEventId() (o int64) { if v != nil && v.ParentInitiatedEventId != nil { return *v.ParentInitiatedEventId } return } // IsSetParentInitiatedEventId returns true if ParentInitiatedEventId is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetParentInitiatedEventId() bool { return v != nil && v.ParentInitiatedEventId != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetInput() bool { return v != nil && v.Input != nil } // GetExecutionStartToCloseTimeoutSeconds returns the value of ExecutionStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // IsSetExecutionStartToCloseTimeoutSeconds returns true if ExecutionStartToCloseTimeoutSeconds is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetExecutionStartToCloseTimeoutSeconds() bool { return v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil } // GetTaskStartToCloseTimeoutSeconds returns the value of TaskStartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // IsSetTaskStartToCloseTimeoutSeconds returns true if TaskStartToCloseTimeoutSeconds is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetTaskStartToCloseTimeoutSeconds() bool { return v != nil && v.TaskStartToCloseTimeoutSeconds != nil } // GetContinuedExecutionRunId returns the value of ContinuedExecutionRunId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetContinuedExecutionRunId() (o string) { if v != nil && v.ContinuedExecutionRunId != nil { return *v.ContinuedExecutionRunId } return } // IsSetContinuedExecutionRunId returns true if ContinuedExecutionRunId is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetContinuedExecutionRunId() bool { return v != nil && v.ContinuedExecutionRunId != nil } // GetInitiator returns the value of Initiator if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetInitiator() (o ContinueAsNewInitiator) { if v != nil && v.Initiator != nil { return *v.Initiator } return } // IsSetInitiator returns true if Initiator is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetInitiator() bool { return v != nil && v.Initiator != nil } // GetContinuedFailureReason returns the value of ContinuedFailureReason if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetContinuedFailureReason() (o string) { if v != nil && v.ContinuedFailureReason != nil { return *v.ContinuedFailureReason } return } // IsSetContinuedFailureReason returns true if ContinuedFailureReason is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetContinuedFailureReason() bool { return v != nil && v.ContinuedFailureReason != nil } // GetContinuedFailureDetails returns the value of ContinuedFailureDetails if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetContinuedFailureDetails() (o []byte) { if v != nil && v.ContinuedFailureDetails != nil { return v.ContinuedFailureDetails } return } // IsSetContinuedFailureDetails returns true if ContinuedFailureDetails is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetContinuedFailureDetails() bool { return v != nil && v.ContinuedFailureDetails != nil } // GetLastCompletionResult returns the value of LastCompletionResult if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetLastCompletionResult() (o []byte) { if v != nil && v.LastCompletionResult != nil { return v.LastCompletionResult } return } // IsSetLastCompletionResult returns true if LastCompletionResult is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetLastCompletionResult() bool { return v != nil && v.LastCompletionResult != nil } // GetOriginalExecutionRunId returns the value of OriginalExecutionRunId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetOriginalExecutionRunId() (o string) { if v != nil && v.OriginalExecutionRunId != nil { return *v.OriginalExecutionRunId } return } // IsSetOriginalExecutionRunId returns true if OriginalExecutionRunId is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetOriginalExecutionRunId() bool { return v != nil && v.OriginalExecutionRunId != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } // GetFirstExecutionRunId returns the value of FirstExecutionRunId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetFirstExecutionRunId() (o string) { if v != nil && v.FirstExecutionRunId != nil { return *v.FirstExecutionRunId } return } // IsSetFirstExecutionRunId returns true if FirstExecutionRunId is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetFirstExecutionRunId() bool { return v != nil && v.FirstExecutionRunId != nil } // GetFirstScheduledTimeNano returns the value of FirstScheduledTimeNano if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetFirstScheduledTimeNano() (o int64) { if v != nil && v.FirstScheduledTimeNano != nil { return *v.FirstScheduledTimeNano } return } // IsSetFirstScheduledTimeNano returns true if FirstScheduledTimeNano is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetFirstScheduledTimeNano() bool { return v != nil && v.FirstScheduledTimeNano != nil } // GetRetryPolicy returns the value of RetryPolicy if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetRetryPolicy() (o *RetryPolicy) { if v != nil && v.RetryPolicy != nil { return v.RetryPolicy } return } // IsSetRetryPolicy returns true if RetryPolicy is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetRetryPolicy() bool { return v != nil && v.RetryPolicy != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetAttempt() (o int32) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetExpirationTimestamp returns the value of ExpirationTimestamp if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetExpirationTimestamp() (o int64) { if v != nil && v.ExpirationTimestamp != nil { return *v.ExpirationTimestamp } return } // IsSetExpirationTimestamp returns true if ExpirationTimestamp is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetExpirationTimestamp() bool { return v != nil && v.ExpirationTimestamp != nil } // GetCronSchedule returns the value of CronSchedule if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetCronSchedule() (o string) { if v != nil && v.CronSchedule != nil { return *v.CronSchedule } return } // IsSetCronSchedule returns true if CronSchedule is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetCronSchedule() bool { return v != nil && v.CronSchedule != nil } // GetFirstDecisionTaskBackoffSeconds returns the value of FirstDecisionTaskBackoffSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetFirstDecisionTaskBackoffSeconds() (o int32) { if v != nil && v.FirstDecisionTaskBackoffSeconds != nil { return *v.FirstDecisionTaskBackoffSeconds } return } // IsSetFirstDecisionTaskBackoffSeconds returns true if FirstDecisionTaskBackoffSeconds is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetFirstDecisionTaskBackoffSeconds() bool { return v != nil && v.FirstDecisionTaskBackoffSeconds != nil } // GetMemo returns the value of Memo if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetMemo() (o *Memo) { if v != nil && v.Memo != nil { return v.Memo } return } // IsSetMemo returns true if Memo is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetMemo() bool { return v != nil && v.Memo != nil } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } // GetPrevAutoResetPoints returns the value of PrevAutoResetPoints if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetPrevAutoResetPoints() (o *ResetPoints) { if v != nil && v.PrevAutoResetPoints != nil { return v.PrevAutoResetPoints } return } // IsSetPrevAutoResetPoints returns true if PrevAutoResetPoints is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetPrevAutoResetPoints() bool { return v != nil && v.PrevAutoResetPoints != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetHeader() (o *Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetHeader() bool { return v != nil && v.Header != nil } // GetPartitionConfig returns the value of PartitionConfig if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // IsSetPartitionConfig returns true if PartitionConfig is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetPartitionConfig() bool { return v != nil && v.PartitionConfig != nil } // GetRequestId returns the value of RequestId if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetRequestId() (o string) { if v != nil && v.RequestId != nil { return *v.RequestId } return } // IsSetRequestId returns true if RequestId is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetRequestId() bool { return v != nil && v.RequestId != nil } // GetCronOverlapPolicy returns the value of CronOverlapPolicy if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetCronOverlapPolicy() (o CronOverlapPolicy) { if v != nil && v.CronOverlapPolicy != nil { return *v.CronOverlapPolicy } return } // IsSetCronOverlapPolicy returns true if CronOverlapPolicy is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetCronOverlapPolicy() bool { return v != nil && v.CronOverlapPolicy != nil } // GetActiveClusterSelectionPolicy returns the value of ActiveClusterSelectionPolicy if it is set or its // zero value if it is unset. func (v *WorkflowExecutionStartedEventAttributes) GetActiveClusterSelectionPolicy() (o *ActiveClusterSelectionPolicy) { if v != nil && v.ActiveClusterSelectionPolicy != nil { return v.ActiveClusterSelectionPolicy } return } // IsSetActiveClusterSelectionPolicy returns true if ActiveClusterSelectionPolicy is not nil. func (v *WorkflowExecutionStartedEventAttributes) IsSetActiveClusterSelectionPolicy() bool { return v != nil && v.ActiveClusterSelectionPolicy != nil } type WorkflowExecutionStatus int32 const ( WorkflowExecutionStatusPending WorkflowExecutionStatus = 0 WorkflowExecutionStatusStarted WorkflowExecutionStatus = 1 WorkflowExecutionStatusCompleted WorkflowExecutionStatus = 2 WorkflowExecutionStatusFailed WorkflowExecutionStatus = 3 WorkflowExecutionStatusCanceled WorkflowExecutionStatus = 4 WorkflowExecutionStatusTerminated WorkflowExecutionStatus = 5 WorkflowExecutionStatusContinuedAsNew WorkflowExecutionStatus = 6 WorkflowExecutionStatusTimedOut WorkflowExecutionStatus = 7 ) // WorkflowExecutionStatus_Values returns all recognized values of WorkflowExecutionStatus. func WorkflowExecutionStatus_Values() []WorkflowExecutionStatus { return []WorkflowExecutionStatus{ WorkflowExecutionStatusPending, WorkflowExecutionStatusStarted, WorkflowExecutionStatusCompleted, WorkflowExecutionStatusFailed, WorkflowExecutionStatusCanceled, WorkflowExecutionStatusTerminated, WorkflowExecutionStatusContinuedAsNew, WorkflowExecutionStatusTimedOut, } } // UnmarshalText tries to decode WorkflowExecutionStatus from a byte slice // containing its name. // // var v WorkflowExecutionStatus // err := v.UnmarshalText([]byte("PENDING")) func (v *WorkflowExecutionStatus) UnmarshalText(value []byte) error { switch s := string(value); s { case "PENDING": *v = WorkflowExecutionStatusPending return nil case "STARTED": *v = WorkflowExecutionStatusStarted return nil case "COMPLETED": *v = WorkflowExecutionStatusCompleted return nil case "FAILED": *v = WorkflowExecutionStatusFailed return nil case "CANCELED": *v = WorkflowExecutionStatusCanceled return nil case "TERMINATED": *v = WorkflowExecutionStatusTerminated return nil case "CONTINUED_AS_NEW": *v = WorkflowExecutionStatusContinuedAsNew return nil case "TIMED_OUT": *v = WorkflowExecutionStatusTimedOut return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "WorkflowExecutionStatus", err) } *v = WorkflowExecutionStatus(val) return nil } } // MarshalText encodes WorkflowExecutionStatus to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v WorkflowExecutionStatus) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("PENDING"), nil case 1: return []byte("STARTED"), nil case 2: return []byte("COMPLETED"), nil case 3: return []byte("FAILED"), nil case 4: return []byte("CANCELED"), nil case 5: return []byte("TERMINATED"), nil case 6: return []byte("CONTINUED_AS_NEW"), nil case 7: return []byte("TIMED_OUT"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionStatus. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v WorkflowExecutionStatus) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "PENDING") case 1: enc.AddString("name", "STARTED") case 2: enc.AddString("name", "COMPLETED") case 3: enc.AddString("name", "FAILED") case 4: enc.AddString("name", "CANCELED") case 5: enc.AddString("name", "TERMINATED") case 6: enc.AddString("name", "CONTINUED_AS_NEW") case 7: enc.AddString("name", "TIMED_OUT") } return nil } // Ptr returns a pointer to this enum value. func (v WorkflowExecutionStatus) Ptr() *WorkflowExecutionStatus { return &v } // Encode encodes WorkflowExecutionStatus directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v WorkflowExecutionStatus // return v.Encode(sWriter) func (v WorkflowExecutionStatus) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates WorkflowExecutionStatus into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v WorkflowExecutionStatus) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes WorkflowExecutionStatus from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return WorkflowExecutionStatus(0), err // } // // var v WorkflowExecutionStatus // if err := v.FromWire(x); err != nil { // return WorkflowExecutionStatus(0), err // } // return v, nil func (v *WorkflowExecutionStatus) FromWire(w wire.Value) error { *v = (WorkflowExecutionStatus)(w.GetI32()) return nil } // Decode reads off the encoded WorkflowExecutionStatus directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v WorkflowExecutionStatus // if err := v.Decode(sReader); err != nil { // return WorkflowExecutionStatus(0), err // } // return v, nil func (v *WorkflowExecutionStatus) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (WorkflowExecutionStatus)(i) return nil } // String returns a readable string representation of WorkflowExecutionStatus. func (v WorkflowExecutionStatus) String() string { w := int32(v) switch w { case 0: return "PENDING" case 1: return "STARTED" case 2: return "COMPLETED" case 3: return "FAILED" case 4: return "CANCELED" case 5: return "TERMINATED" case 6: return "CONTINUED_AS_NEW" case 7: return "TIMED_OUT" } return fmt.Sprintf("WorkflowExecutionStatus(%d)", w) } // Equals returns true if this WorkflowExecutionStatus value matches the provided // value. func (v WorkflowExecutionStatus) Equals(rhs WorkflowExecutionStatus) bool { return v == rhs } // MarshalJSON serializes WorkflowExecutionStatus into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v WorkflowExecutionStatus) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"PENDING\""), nil case 1: return ([]byte)("\"STARTED\""), nil case 2: return ([]byte)("\"COMPLETED\""), nil case 3: return ([]byte)("\"FAILED\""), nil case 4: return ([]byte)("\"CANCELED\""), nil case 5: return ([]byte)("\"TERMINATED\""), nil case 6: return ([]byte)("\"CONTINUED_AS_NEW\""), nil case 7: return ([]byte)("\"TIMED_OUT\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode WorkflowExecutionStatus from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *WorkflowExecutionStatus) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "WorkflowExecutionStatus") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "WorkflowExecutionStatus") } *v = (WorkflowExecutionStatus)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "WorkflowExecutionStatus") } } type WorkflowExecutionTerminatedEventAttributes struct { Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` Identity *string `json:"identity,omitempty"` } // ToWire translates a WorkflowExecutionTerminatedEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionTerminatedEventAttributes) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Reason != nil { w, err = wire.NewValueString(*(v.Reason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Details != nil { w, err = wire.NewValueBinary(v.Details), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.Identity != nil { w, err = wire.NewValueString(*(v.Identity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionTerminatedEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionTerminatedEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionTerminatedEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionTerminatedEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Reason = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Details, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Identity = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionTerminatedEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionTerminatedEventAttributes struct could not be encoded. func (v *WorkflowExecutionTerminatedEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Reason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Reason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Details != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Details); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Identity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Identity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionTerminatedEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionTerminatedEventAttributes struct could not be generated from the wire // representation. func (v *WorkflowExecutionTerminatedEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Reason = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Details, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Identity = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionTerminatedEventAttributes // struct. func (v *WorkflowExecutionTerminatedEventAttributes) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Reason != nil { fields[i] = fmt.Sprintf("Reason: %v", *(v.Reason)) i++ } if v.Details != nil { fields[i] = fmt.Sprintf("Details: %v", v.Details) i++ } if v.Identity != nil { fields[i] = fmt.Sprintf("Identity: %v", *(v.Identity)) i++ } return fmt.Sprintf("WorkflowExecutionTerminatedEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionTerminatedEventAttributes match the // provided WorkflowExecutionTerminatedEventAttributes. // // This function performs a deep comparison. func (v *WorkflowExecutionTerminatedEventAttributes) Equals(rhs *WorkflowExecutionTerminatedEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Reason, rhs.Reason) { return false } if !((v.Details == nil && rhs.Details == nil) || (v.Details != nil && rhs.Details != nil && bytes.Equal(v.Details, rhs.Details))) { return false } if !_String_EqualsPtr(v.Identity, rhs.Identity) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionTerminatedEventAttributes. func (v *WorkflowExecutionTerminatedEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Reason != nil { enc.AddString("reason", *v.Reason) } if v.Details != nil { enc.AddString("details", base64.StdEncoding.EncodeToString(v.Details)) } if v.Identity != nil { enc.AddString("identity", *v.Identity) } return err } // GetReason returns the value of Reason if it is set or its // zero value if it is unset. func (v *WorkflowExecutionTerminatedEventAttributes) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // IsSetReason returns true if Reason is not nil. func (v *WorkflowExecutionTerminatedEventAttributes) IsSetReason() bool { return v != nil && v.Reason != nil } // GetDetails returns the value of Details if it is set or its // zero value if it is unset. func (v *WorkflowExecutionTerminatedEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // IsSetDetails returns true if Details is not nil. func (v *WorkflowExecutionTerminatedEventAttributes) IsSetDetails() bool { return v != nil && v.Details != nil } // GetIdentity returns the value of Identity if it is set or its // zero value if it is unset. func (v *WorkflowExecutionTerminatedEventAttributes) GetIdentity() (o string) { if v != nil && v.Identity != nil { return *v.Identity } return } // IsSetIdentity returns true if Identity is not nil. func (v *WorkflowExecutionTerminatedEventAttributes) IsSetIdentity() bool { return v != nil && v.Identity != nil } type WorkflowExecutionTimedOutEventAttributes struct { TimeoutType *TimeoutType `json:"timeoutType,omitempty"` } // ToWire translates a WorkflowExecutionTimedOutEventAttributes struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionTimedOutEventAttributes) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.TimeoutType != nil { w, err = v.TimeoutType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowExecutionTimedOutEventAttributes struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionTimedOutEventAttributes struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionTimedOutEventAttributes // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionTimedOutEventAttributes) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x TimeoutType x, err = _TimeoutType_Read(field.Value) v.TimeoutType = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowExecutionTimedOutEventAttributes struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionTimedOutEventAttributes struct could not be encoded. func (v *WorkflowExecutionTimedOutEventAttributes) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TimeoutType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.TimeoutType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowExecutionTimedOutEventAttributes struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionTimedOutEventAttributes struct could not be generated from the wire // representation. func (v *WorkflowExecutionTimedOutEventAttributes) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x TimeoutType x, err = _TimeoutType_Decode(sr) v.TimeoutType = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionTimedOutEventAttributes // struct. func (v *WorkflowExecutionTimedOutEventAttributes) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.TimeoutType != nil { fields[i] = fmt.Sprintf("TimeoutType: %v", *(v.TimeoutType)) i++ } return fmt.Sprintf("WorkflowExecutionTimedOutEventAttributes{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowExecutionTimedOutEventAttributes match the // provided WorkflowExecutionTimedOutEventAttributes. // // This function performs a deep comparison. func (v *WorkflowExecutionTimedOutEventAttributes) Equals(rhs *WorkflowExecutionTimedOutEventAttributes) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_TimeoutType_EqualsPtr(v.TimeoutType, rhs.TimeoutType) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionTimedOutEventAttributes. func (v *WorkflowExecutionTimedOutEventAttributes) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TimeoutType != nil { err = multierr.Append(err, enc.AddObject("timeoutType", *v.TimeoutType)) } return err } // GetTimeoutType returns the value of TimeoutType if it is set or its // zero value if it is unset. func (v *WorkflowExecutionTimedOutEventAttributes) GetTimeoutType() (o TimeoutType) { if v != nil && v.TimeoutType != nil { return *v.TimeoutType } return } // IsSetTimeoutType returns true if TimeoutType is not nil. func (v *WorkflowExecutionTimedOutEventAttributes) IsSetTimeoutType() bool { return v != nil && v.TimeoutType != nil } type WorkflowIdReusePolicy int32 const ( WorkflowIdReusePolicyAllowDuplicateFailedOnly WorkflowIdReusePolicy = 0 WorkflowIdReusePolicyAllowDuplicate WorkflowIdReusePolicy = 1 WorkflowIdReusePolicyRejectDuplicate WorkflowIdReusePolicy = 2 WorkflowIdReusePolicyTerminateIfRunning WorkflowIdReusePolicy = 3 ) // WorkflowIdReusePolicy_Values returns all recognized values of WorkflowIdReusePolicy. func WorkflowIdReusePolicy_Values() []WorkflowIdReusePolicy { return []WorkflowIdReusePolicy{ WorkflowIdReusePolicyAllowDuplicateFailedOnly, WorkflowIdReusePolicyAllowDuplicate, WorkflowIdReusePolicyRejectDuplicate, WorkflowIdReusePolicyTerminateIfRunning, } } // UnmarshalText tries to decode WorkflowIdReusePolicy from a byte slice // containing its name. // // var v WorkflowIdReusePolicy // err := v.UnmarshalText([]byte("AllowDuplicateFailedOnly")) func (v *WorkflowIdReusePolicy) UnmarshalText(value []byte) error { switch s := string(value); s { case "AllowDuplicateFailedOnly": *v = WorkflowIdReusePolicyAllowDuplicateFailedOnly return nil case "AllowDuplicate": *v = WorkflowIdReusePolicyAllowDuplicate return nil case "RejectDuplicate": *v = WorkflowIdReusePolicyRejectDuplicate return nil case "TerminateIfRunning": *v = WorkflowIdReusePolicyTerminateIfRunning return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "WorkflowIdReusePolicy", err) } *v = WorkflowIdReusePolicy(val) return nil } } // MarshalText encodes WorkflowIdReusePolicy to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v WorkflowIdReusePolicy) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("AllowDuplicateFailedOnly"), nil case 1: return []byte("AllowDuplicate"), nil case 2: return []byte("RejectDuplicate"), nil case 3: return []byte("TerminateIfRunning"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowIdReusePolicy. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v WorkflowIdReusePolicy) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "AllowDuplicateFailedOnly") case 1: enc.AddString("name", "AllowDuplicate") case 2: enc.AddString("name", "RejectDuplicate") case 3: enc.AddString("name", "TerminateIfRunning") } return nil } // Ptr returns a pointer to this enum value. func (v WorkflowIdReusePolicy) Ptr() *WorkflowIdReusePolicy { return &v } // Encode encodes WorkflowIdReusePolicy directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v WorkflowIdReusePolicy // return v.Encode(sWriter) func (v WorkflowIdReusePolicy) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates WorkflowIdReusePolicy into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v WorkflowIdReusePolicy) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes WorkflowIdReusePolicy from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return WorkflowIdReusePolicy(0), err // } // // var v WorkflowIdReusePolicy // if err := v.FromWire(x); err != nil { // return WorkflowIdReusePolicy(0), err // } // return v, nil func (v *WorkflowIdReusePolicy) FromWire(w wire.Value) error { *v = (WorkflowIdReusePolicy)(w.GetI32()) return nil } // Decode reads off the encoded WorkflowIdReusePolicy directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v WorkflowIdReusePolicy // if err := v.Decode(sReader); err != nil { // return WorkflowIdReusePolicy(0), err // } // return v, nil func (v *WorkflowIdReusePolicy) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (WorkflowIdReusePolicy)(i) return nil } // String returns a readable string representation of WorkflowIdReusePolicy. func (v WorkflowIdReusePolicy) String() string { w := int32(v) switch w { case 0: return "AllowDuplicateFailedOnly" case 1: return "AllowDuplicate" case 2: return "RejectDuplicate" case 3: return "TerminateIfRunning" } return fmt.Sprintf("WorkflowIdReusePolicy(%d)", w) } // Equals returns true if this WorkflowIdReusePolicy value matches the provided // value. func (v WorkflowIdReusePolicy) Equals(rhs WorkflowIdReusePolicy) bool { return v == rhs } // MarshalJSON serializes WorkflowIdReusePolicy into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v WorkflowIdReusePolicy) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"AllowDuplicateFailedOnly\""), nil case 1: return ([]byte)("\"AllowDuplicate\""), nil case 2: return ([]byte)("\"RejectDuplicate\""), nil case 3: return ([]byte)("\"TerminateIfRunning\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode WorkflowIdReusePolicy from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *WorkflowIdReusePolicy) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "WorkflowIdReusePolicy") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "WorkflowIdReusePolicy") } *v = (WorkflowIdReusePolicy)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "WorkflowIdReusePolicy") } } type WorkflowQuery struct { QueryType *string `json:"queryType,omitempty"` QueryArgs []byte `json:"queryArgs,omitempty"` } // ToWire translates a WorkflowQuery struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowQuery) ToWire() (wire.Value, error) { var ( fields [2]wire.Field i int = 0 w wire.Value err error ) if v.QueryType != nil { w, err = wire.NewValueString(*(v.QueryType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.QueryArgs != nil { w, err = wire.NewValueBinary(v.QueryArgs), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowQuery struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowQuery struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowQuery // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowQuery) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.QueryType = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.QueryArgs, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a WorkflowQuery struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowQuery struct could not be encoded. func (v *WorkflowQuery) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.QueryType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.QueryType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueryArgs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.QueryArgs); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowQuery struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowQuery struct could not be generated from the wire // representation. func (v *WorkflowQuery) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.QueryType = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.QueryArgs, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowQuery // struct. func (v *WorkflowQuery) String() string { if v == nil { return "" } var fields [2]string i := 0 if v.QueryType != nil { fields[i] = fmt.Sprintf("QueryType: %v", *(v.QueryType)) i++ } if v.QueryArgs != nil { fields[i] = fmt.Sprintf("QueryArgs: %v", v.QueryArgs) i++ } return fmt.Sprintf("WorkflowQuery{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowQuery match the // provided WorkflowQuery. // // This function performs a deep comparison. func (v *WorkflowQuery) Equals(rhs *WorkflowQuery) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.QueryType, rhs.QueryType) { return false } if !((v.QueryArgs == nil && rhs.QueryArgs == nil) || (v.QueryArgs != nil && rhs.QueryArgs != nil && bytes.Equal(v.QueryArgs, rhs.QueryArgs))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowQuery. func (v *WorkflowQuery) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.QueryType != nil { enc.AddString("queryType", *v.QueryType) } if v.QueryArgs != nil { enc.AddString("queryArgs", base64.StdEncoding.EncodeToString(v.QueryArgs)) } return err } // GetQueryType returns the value of QueryType if it is set or its // zero value if it is unset. func (v *WorkflowQuery) GetQueryType() (o string) { if v != nil && v.QueryType != nil { return *v.QueryType } return } // IsSetQueryType returns true if QueryType is not nil. func (v *WorkflowQuery) IsSetQueryType() bool { return v != nil && v.QueryType != nil } // GetQueryArgs returns the value of QueryArgs if it is set or its // zero value if it is unset. func (v *WorkflowQuery) GetQueryArgs() (o []byte) { if v != nil && v.QueryArgs != nil { return v.QueryArgs } return } // IsSetQueryArgs returns true if QueryArgs is not nil. func (v *WorkflowQuery) IsSetQueryArgs() bool { return v != nil && v.QueryArgs != nil } type WorkflowQueryResult struct { ResultType *QueryResultType `json:"resultType,omitempty"` Answer []byte `json:"answer,omitempty"` ErrorMessage *string `json:"errorMessage,omitempty"` } // ToWire translates a WorkflowQueryResult struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowQueryResult) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.ResultType != nil { w, err = v.ResultType.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Answer != nil { w, err = wire.NewValueBinary(v.Answer), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ErrorMessage != nil { w, err = wire.NewValueString(*(v.ErrorMessage)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _QueryResultType_Read(w wire.Value) (QueryResultType, error) { var v QueryResultType err := v.FromWire(w) return v, err } // FromWire deserializes a WorkflowQueryResult struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowQueryResult struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowQueryResult // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowQueryResult) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x QueryResultType x, err = _QueryResultType_Read(field.Value) v.ResultType = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.Answer, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ErrorMessage = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowQueryResult struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowQueryResult struct could not be encoded. func (v *WorkflowQueryResult) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ResultType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := v.ResultType.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Answer != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Answer); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ErrorMessage != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ErrorMessage)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _QueryResultType_Decode(sr stream.Reader) (QueryResultType, error) { var v QueryResultType err := v.Decode(sr) return v, err } // Decode deserializes a WorkflowQueryResult struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowQueryResult struct could not be generated from the wire // representation. func (v *WorkflowQueryResult) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x QueryResultType x, err = _QueryResultType_Decode(sr) v.ResultType = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.Answer, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ErrorMessage = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowQueryResult // struct. func (v *WorkflowQueryResult) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.ResultType != nil { fields[i] = fmt.Sprintf("ResultType: %v", *(v.ResultType)) i++ } if v.Answer != nil { fields[i] = fmt.Sprintf("Answer: %v", v.Answer) i++ } if v.ErrorMessage != nil { fields[i] = fmt.Sprintf("ErrorMessage: %v", *(v.ErrorMessage)) i++ } return fmt.Sprintf("WorkflowQueryResult{%v}", strings.Join(fields[:i], ", ")) } func _QueryResultType_EqualsPtr(lhs, rhs *QueryResultType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this WorkflowQueryResult match the // provided WorkflowQueryResult. // // This function performs a deep comparison. func (v *WorkflowQueryResult) Equals(rhs *WorkflowQueryResult) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_QueryResultType_EqualsPtr(v.ResultType, rhs.ResultType) { return false } if !((v.Answer == nil && rhs.Answer == nil) || (v.Answer != nil && rhs.Answer != nil && bytes.Equal(v.Answer, rhs.Answer))) { return false } if !_String_EqualsPtr(v.ErrorMessage, rhs.ErrorMessage) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowQueryResult. func (v *WorkflowQueryResult) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ResultType != nil { err = multierr.Append(err, enc.AddObject("resultType", *v.ResultType)) } if v.Answer != nil { enc.AddString("answer", base64.StdEncoding.EncodeToString(v.Answer)) } if v.ErrorMessage != nil { enc.AddString("errorMessage", *v.ErrorMessage) } return err } // GetResultType returns the value of ResultType if it is set or its // zero value if it is unset. func (v *WorkflowQueryResult) GetResultType() (o QueryResultType) { if v != nil && v.ResultType != nil { return *v.ResultType } return } // IsSetResultType returns true if ResultType is not nil. func (v *WorkflowQueryResult) IsSetResultType() bool { return v != nil && v.ResultType != nil } // GetAnswer returns the value of Answer if it is set or its // zero value if it is unset. func (v *WorkflowQueryResult) GetAnswer() (o []byte) { if v != nil && v.Answer != nil { return v.Answer } return } // IsSetAnswer returns true if Answer is not nil. func (v *WorkflowQueryResult) IsSetAnswer() bool { return v != nil && v.Answer != nil } // GetErrorMessage returns the value of ErrorMessage if it is set or its // zero value if it is unset. func (v *WorkflowQueryResult) GetErrorMessage() (o string) { if v != nil && v.ErrorMessage != nil { return *v.ErrorMessage } return } // IsSetErrorMessage returns true if ErrorMessage is not nil. func (v *WorkflowQueryResult) IsSetErrorMessage() bool { return v != nil && v.ErrorMessage != nil } type WorkflowType struct { Name *string `json:"name,omitempty"` } // ToWire translates a WorkflowType struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowType) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowType struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowType struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowType // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowType) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowType struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowType struct could not be encoded. func (v *WorkflowType) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowType struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowType struct could not be generated from the wire // representation. func (v *WorkflowType) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowType // struct. func (v *WorkflowType) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } return fmt.Sprintf("WorkflowType{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowType match the // provided WorkflowType. // // This function performs a deep comparison. func (v *WorkflowType) Equals(rhs *WorkflowType) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowType. func (v *WorkflowType) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *WorkflowType) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *WorkflowType) IsSetName() bool { return v != nil && v.Name != nil } type WorkflowTypeFilter struct { Name *string `json:"name,omitempty"` } // ToWire translates a WorkflowTypeFilter struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowTypeFilter) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a WorkflowTypeFilter struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowTypeFilter struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowTypeFilter // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowTypeFilter) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } } } return nil } // Encode serializes a WorkflowTypeFilter struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowTypeFilter struct could not be encoded. func (v *WorkflowTypeFilter) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a WorkflowTypeFilter struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowTypeFilter struct could not be generated from the wire // representation. func (v *WorkflowTypeFilter) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowTypeFilter // struct. func (v *WorkflowTypeFilter) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } return fmt.Sprintf("WorkflowTypeFilter{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this WorkflowTypeFilter match the // provided WorkflowTypeFilter. // // This function performs a deep comparison. func (v *WorkflowTypeFilter) Equals(rhs *WorkflowTypeFilter) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowTypeFilter. func (v *WorkflowTypeFilter) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *WorkflowTypeFilter) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *WorkflowTypeFilter) IsSetName() bool { return v != nil && v.Name != nil } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "shared", Package: "github.com/uber/cadence/.gen/go/shared", FilePath: "shared.thrift", SHA1: "5a8ec0270fb416939964d113fdb6059662176745", Raw: rawIDL, } const rawIDL = "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nnamespace java com.uber.cadence\n\nexception BadRequestError {\n 1: required string message\n}\n\nexception InternalServiceError {\n 1: required string message\n}\n\nexception InternalDataInconsistencyError {\n 1: required string message\n}\n\nexception DomainAlreadyExistsError {\n 1: required string message\n}\n\nexception WorkflowExecutionAlreadyStartedError {\n 10: optional string message\n 20: optional string startRequestId\n 30: optional string runId\n}\n\nexception WorkflowExecutionAlreadyCompletedError {\n 1: required string message\n}\n\nexception EntityNotExistsError {\n 1: required string message\n 2: optional string currentCluster\n 3: optional string activeCluster\n 4: required list activeClusters // todo(david.porter) remove as its disused\n}\n\nexception ServiceBusyError {\n 1: required string message\n 2: optional string reason\n}\n\nexception CancellationAlreadyRequestedError {\n 1: required string message\n}\n\nexception QueryFailedError {\n 1: required string message\n}\n\nexception DomainNotActiveError {\n 1: required string message\n 2: required string domainName\n 3: required string currentCluster\n 4: required string activeCluster\n 5: required list activeClusters // todo (david.porter) remove this field as it's disused\n}\n\nexception LimitExceededError {\n 1: required string message\n}\n\nexception AccessDeniedError {\n 1: required string message\n}\n\nexception RetryTaskV2Error {\n 1: required string message\n 2: optional string domainId\n 3: optional string workflowId\n 4: optional string runId\n 5: optional i64 (js.type = \"Long\") startEventId\n 6: optional i64 (js.type = \"Long\") startEventVersion\n 7: optional i64 (js.type = \"Long\") endEventId\n 8: optional i64 (js.type = \"Long\") endEventVersion\n}\n\nexception ClientVersionNotSupportedError {\n 1: required string featureVersion\n 2: required string clientImpl\n 3: required string supportedVersions\n}\n\nexception FeatureNotEnabledError {\n 1: required string featureFlag\n}\n\nexception CurrentBranchChangedError {\n 10: required string message\n 20: required binary currentBranchToken\n}\n\nexception RemoteSyncMatchedError {\n 10: required string message\n}\n\nexception StickyWorkerUnavailableError {\n 1: required string message\n}\n\nexception TaskListNotOwnedByHostError {\n 1: required string ownedByIdentity\n 2: required string myIdentity\n 3: required string tasklistName\n}\n\nenum WorkflowIdReusePolicy {\n /*\n * allow start a workflow execution using the same workflow ID,\n * when workflow not running, and the last execution close state is in\n * [terminated, cancelled, timeouted, failed].\n */\n AllowDuplicateFailedOnly,\n /*\n * allow start a workflow execution using the same workflow ID,\n * when workflow not running.\n */\n AllowDuplicate,\n /*\n * do not allow start a workflow execution using the same workflow ID at all\n */\n RejectDuplicate,\n /*\n * if a workflow is running using the same workflow ID, terminate it and start a new one\n */\n TerminateIfRunning,\n}\n\nenum DomainStatus {\n REGISTERED,\n DEPRECATED,\n DELETED,\n}\n\nenum TimeoutType {\n START_TO_CLOSE,\n SCHEDULE_TO_START,\n SCHEDULE_TO_CLOSE,\n HEARTBEAT,\n}\n\nenum ParentClosePolicy {\n\tABANDON,\n\tREQUEST_CANCEL,\n\tTERMINATE,\n}\n\n\n// whenever this list of decision is changed\n// do change the mutableStateBuilder.go\n// function shouldBufferEvent\n// to make sure wo do the correct event ordering\nenum DecisionType {\n ScheduleActivityTask,\n RequestCancelActivityTask,\n StartTimer,\n CompleteWorkflowExecution,\n FailWorkflowExecution,\n CancelTimer,\n CancelWorkflowExecution,\n RequestCancelExternalWorkflowExecution,\n RecordMarker,\n ContinueAsNewWorkflowExecution,\n StartChildWorkflowExecution,\n SignalExternalWorkflowExecution,\n UpsertWorkflowSearchAttributes,\n}\n\nenum EventType {\n WorkflowExecutionStarted,\n WorkflowExecutionCompleted,\n WorkflowExecutionFailed,\n WorkflowExecutionTimedOut,\n DecisionTaskScheduled,\n DecisionTaskStarted,\n DecisionTaskCompleted,\n DecisionTaskTimedOut\n DecisionTaskFailed,\n ActivityTaskScheduled,\n ActivityTaskStarted,\n ActivityTaskCompleted,\n ActivityTaskFailed,\n ActivityTaskTimedOut,\n ActivityTaskCancelRequested,\n RequestCancelActivityTaskFailed,\n ActivityTaskCanceled,\n TimerStarted,\n TimerFired,\n CancelTimerFailed,\n TimerCanceled,\n WorkflowExecutionCancelRequested,\n WorkflowExecutionCanceled,\n RequestCancelExternalWorkflowExecutionInitiated,\n RequestCancelExternalWorkflowExecutionFailed,\n ExternalWorkflowExecutionCancelRequested,\n MarkerRecorded,\n WorkflowExecutionSignaled,\n WorkflowExecutionTerminated,\n WorkflowExecutionContinuedAsNew,\n StartChildWorkflowExecutionInitiated,\n StartChildWorkflowExecutionFailed,\n ChildWorkflowExecutionStarted,\n ChildWorkflowExecutionCompleted,\n ChildWorkflowExecutionFailed,\n ChildWorkflowExecutionCanceled,\n ChildWorkflowExecutionTimedOut,\n ChildWorkflowExecutionTerminated,\n SignalExternalWorkflowExecutionInitiated,\n SignalExternalWorkflowExecutionFailed,\n ExternalWorkflowExecutionSignaled,\n UpsertWorkflowSearchAttributes,\n}\n\nenum DecisionTaskFailedCause {\n UNHANDLED_DECISION,\n BAD_SCHEDULE_ACTIVITY_ATTRIBUTES,\n BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES,\n BAD_START_TIMER_ATTRIBUTES,\n BAD_CANCEL_TIMER_ATTRIBUTES,\n BAD_RECORD_MARKER_ATTRIBUTES,\n BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES,\n BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES,\n BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES,\n BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES,\n BAD_CONTINUE_AS_NEW_ATTRIBUTES,\n START_TIMER_DUPLICATE_ID,\n RESET_STICKY_TASKLIST,\n WORKFLOW_WORKER_UNHANDLED_FAILURE,\n BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES,\n BAD_START_CHILD_EXECUTION_ATTRIBUTES,\n FORCE_CLOSE_DECISION,\n FAILOVER_CLOSE_DECISION,\n BAD_SIGNAL_INPUT_SIZE,\n RESET_WORKFLOW,\n BAD_BINARY,\n SCHEDULE_ACTIVITY_DUPLICATE_ID,\n BAD_SEARCH_ATTRIBUTES,\n}\n\nenum DecisionTaskTimedOutCause {\n TIMEOUT,\n RESET,\n}\n\nenum CancelExternalWorkflowExecutionFailedCause {\n UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION,\n WORKFLOW_ALREADY_COMPLETED,\n}\n\nenum SignalExternalWorkflowExecutionFailedCause {\n UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION,\n WORKFLOW_ALREADY_COMPLETED,\n}\n\nenum ChildWorkflowExecutionFailedCause {\n WORKFLOW_ALREADY_RUNNING,\n}\n\n// TODO: when migrating to gRPC, add a running / none status,\n// currently, customer is using null / nil as an indication\n// that workflow is still running\nenum WorkflowExecutionCloseStatus {\n COMPLETED,\n FAILED,\n CANCELED,\n TERMINATED,\n CONTINUED_AS_NEW,\n TIMED_OUT,\n}\n\nenum WorkflowExecutionStatus {\n PENDING,\n STARTED,\n COMPLETED,\n FAILED,\n CANCELED,\n TERMINATED,\n CONTINUED_AS_NEW,\n TIMED_OUT,\n}\n\nenum QueryTaskCompletedType {\n COMPLETED,\n FAILED,\n}\n\nenum QueryResultType {\n ANSWERED,\n FAILED,\n}\n\nenum PendingActivityState {\n SCHEDULED,\n STARTED,\n CANCEL_REQUESTED,\n}\n\nenum PendingDecisionState {\n SCHEDULED,\n STARTED,\n}\n\nenum HistoryEventFilterType {\n ALL_EVENT,\n CLOSE_EVENT,\n}\n\nenum TaskListKind {\n NORMAL,\n STICKY,\n EPHEMERAL,\n}\n\nenum ArchivalStatus {\n DISABLED,\n ENABLED,\n}\n\nenum CronOverlapPolicy {\n SKIPPED,\n BUFFERONE,\n}\n\nenum IndexedValueType {\n STRING,\n KEYWORD,\n INT,\n DOUBLE,\n BOOL,\n DATETIME,\n}\n\nstruct Header {\n 10: optional map fields\n}\n\nstruct WorkflowType {\n 10: optional string name\n}\n\nstruct ActivityType {\n 10: optional string name\n}\n\nstruct TaskList {\n 10: optional string name\n 20: optional TaskListKind kind\n}\n\nenum EncodingType {\n ThriftRW,\n JSON,\n}\n\nenum QueryRejectCondition {\n // NOT_OPEN indicates that query should be rejected if workflow is not open\n NOT_OPEN\n // NOT_COMPLETED_CLEANLY indicates that query should be rejected if workflow did not complete cleanly\n NOT_COMPLETED_CLEANLY\n}\n\nenum QueryConsistencyLevel {\n // EVENTUAL indicates that query should be eventually consistent\n EVENTUAL\n // STRONG indicates that any events that came before query should be reflected in workflow state before running query\n STRONG\n}\n\nstruct DataBlob {\n 10: optional EncodingType EncodingType\n 20: optional binary Data\n}\n\nstruct TaskListMetadata {\n 10: optional double maxTasksPerSecond\n}\n\nstruct WorkflowExecution {\n 10: optional string workflowId\n 20: optional string runId\n}\n\nstruct Memo {\n 10: optional map fields\n}\n\nstruct SearchAttributes {\n 10: optional map indexedFields\n}\n\nstruct WorkerVersionInfo {\n 10: optional string impl\n 20: optional string featureVersion\n}\n\nstruct WorkflowExecutionInfo {\n 10: optional WorkflowExecution execution\n 20: optional WorkflowType type\n 30: optional i64 (js.type = \"Long\") startTime\n 40: optional i64 (js.type = \"Long\") closeTime\n 50: optional WorkflowExecutionCloseStatus closeStatus\n 60: optional i64 (js.type = \"Long\") historyLength\n 70: optional string parentDomainId\n 71: optional string parentDomainName\n 72: optional i64 parentInitatedId\n 80: optional WorkflowExecution parentExecution\n 90: optional i64 (js.type = \"Long\") executionTime\n 100: optional Memo memo\n 101: optional SearchAttributes searchAttributes\n 110: optional ResetPoints autoResetPoints\n 120: optional string taskList\n 121: optional TaskList taskListInfo\n 130: optional bool isCron\n 140: optional i64 (js.type = \"Long\") updateTime\n 150: optional map partitionConfig\n 160: optional CronOverlapPolicy cronOverlapPolicy\n 170: optional ActiveClusterSelectionPolicy activeClusterSelectionPolicy\n 180: optional string cronSchedule\n 190: optional WorkflowExecutionStatus executionStatus\n 200: optional i64 (js.type = \"Long\") scheduledExecutionTime\n}\n\nstruct WorkflowExecutionConfiguration {\n 10: optional TaskList taskList\n 20: optional i32 executionStartToCloseTimeoutSeconds\n 30: optional i32 taskStartToCloseTimeoutSeconds\n// 40: optional ChildPolicy childPolicy -- Removed but reserve the IDL order number\n}\n\nstruct TransientDecisionInfo {\n 10: optional HistoryEvent scheduledEvent\n 20: optional HistoryEvent startedEvent\n}\n\nstruct ScheduleActivityTaskDecisionAttributes {\n 10: optional string activityId\n 20: optional ActivityType activityType\n 25: optional string domain\n 30: optional TaskList taskList\n 40: optional binary input\n 45: optional i32 scheduleToCloseTimeoutSeconds\n 50: optional i32 scheduleToStartTimeoutSeconds\n 55: optional i32 startToCloseTimeoutSeconds\n 60: optional i32 heartbeatTimeoutSeconds\n 70: optional RetryPolicy retryPolicy\n 80: optional Header header\n 90: optional bool requestLocalDispatch\n}\n\nstruct ActivityLocalDispatchInfo{\n 10: optional string activityId\n 20: optional i64 (js.type = \"Long\") scheduledTimestamp\n 30: optional i64 (js.type = \"Long\") startedTimestamp\n 40: optional i64 (js.type = \"Long\") scheduledTimestampOfThisAttempt\n 50: optional binary taskToken\n}\n\nstruct RequestCancelActivityTaskDecisionAttributes {\n 10: optional string activityId\n}\n\nstruct StartTimerDecisionAttributes {\n 10: optional string timerId\n 20: optional i64 (js.type = \"Long\") startToFireTimeoutSeconds\n}\n\nstruct CompleteWorkflowExecutionDecisionAttributes {\n 10: optional binary result\n}\n\nstruct FailWorkflowExecutionDecisionAttributes {\n 10: optional string reason\n 20: optional binary details\n}\n\nstruct CancelTimerDecisionAttributes {\n 10: optional string timerId\n}\n\nstruct CancelWorkflowExecutionDecisionAttributes {\n 10: optional binary details\n}\n\nstruct RequestCancelExternalWorkflowExecutionDecisionAttributes {\n 10: optional string domain\n 20: optional string workflowId\n 30: optional string runId\n 40: optional binary control\n 50: optional bool childWorkflowOnly\n}\n\nstruct SignalExternalWorkflowExecutionDecisionAttributes {\n 10: optional string domain\n 20: optional WorkflowExecution execution\n 30: optional string signalName\n 40: optional binary input\n 50: optional binary control\n 60: optional bool childWorkflowOnly\n}\n\nstruct UpsertWorkflowSearchAttributesDecisionAttributes {\n 10: optional SearchAttributes searchAttributes\n}\n\nstruct RecordMarkerDecisionAttributes {\n 10: optional string markerName\n 20: optional binary details\n 30: optional Header header\n}\n\nstruct ContinueAsNewWorkflowExecutionDecisionAttributes {\n 10: optional WorkflowType workflowType\n 20: optional TaskList taskList\n 30: optional binary input\n 40: optional i32 executionStartToCloseTimeoutSeconds\n 50: optional i32 taskStartToCloseTimeoutSeconds\n 60: optional i32 backoffStartIntervalInSeconds\n 70: optional RetryPolicy retryPolicy\n 80: optional ContinueAsNewInitiator initiator\n 90: optional string failureReason\n 100: optional binary failureDetails\n 110: optional binary lastCompletionResult\n 120: optional string cronSchedule\n 130: optional Header header\n 140: optional Memo memo\n 150: optional SearchAttributes searchAttributes\n 160: optional i32 jitterStartSeconds\n 170: optional CronOverlapPolicy cronOverlapPolicy\n 180: optional ActiveClusterSelectionPolicy activeClusterSelectionPolicy\n}\n\nstruct StartChildWorkflowExecutionDecisionAttributes {\n 10: optional string domain\n 20: optional string workflowId\n 30: optional WorkflowType workflowType\n 40: optional TaskList taskList\n 50: optional binary input\n 60: optional i32 executionStartToCloseTimeoutSeconds\n 70: optional i32 taskStartToCloseTimeoutSeconds\n// 80: optional ChildPolicy childPolicy -- Removed but reserve the IDL order number\n 81: optional ParentClosePolicy parentClosePolicy\n 90: optional binary control\n 100: optional WorkflowIdReusePolicy workflowIdReusePolicy\n 110: optional RetryPolicy retryPolicy\n 120: optional string cronSchedule\n 130: optional Header header\n 140: optional Memo memo\n 150: optional SearchAttributes searchAttributes\n 160: optional CronOverlapPolicy cronOverlapPolicy\n 170: optional ActiveClusterSelectionPolicy activeClusterSelectionPolicy\n}\n\nstruct Decision {\n 10: optional DecisionType decisionType\n 20: optional ScheduleActivityTaskDecisionAttributes scheduleActivityTaskDecisionAttributes\n 25: optional StartTimerDecisionAttributes startTimerDecisionAttributes\n 30: optional CompleteWorkflowExecutionDecisionAttributes completeWorkflowExecutionDecisionAttributes\n 35: optional FailWorkflowExecutionDecisionAttributes failWorkflowExecutionDecisionAttributes\n 40: optional RequestCancelActivityTaskDecisionAttributes requestCancelActivityTaskDecisionAttributes\n 50: optional CancelTimerDecisionAttributes cancelTimerDecisionAttributes\n 60: optional CancelWorkflowExecutionDecisionAttributes cancelWorkflowExecutionDecisionAttributes\n 70: optional RequestCancelExternalWorkflowExecutionDecisionAttributes requestCancelExternalWorkflowExecutionDecisionAttributes\n 80: optional RecordMarkerDecisionAttributes recordMarkerDecisionAttributes\n 90: optional ContinueAsNewWorkflowExecutionDecisionAttributes continueAsNewWorkflowExecutionDecisionAttributes\n 100: optional StartChildWorkflowExecutionDecisionAttributes startChildWorkflowExecutionDecisionAttributes\n 110: optional SignalExternalWorkflowExecutionDecisionAttributes signalExternalWorkflowExecutionDecisionAttributes\n 120: optional UpsertWorkflowSearchAttributesDecisionAttributes upsertWorkflowSearchAttributesDecisionAttributes\n}\n\nstruct WorkflowExecutionStartedEventAttributes {\n 10: optional WorkflowType workflowType\n 12: optional string parentWorkflowDomain\n 14: optional WorkflowExecution parentWorkflowExecution\n 16: optional i64 (js.type = \"Long\") parentInitiatedEventId\n 20: optional TaskList taskList\n 30: optional binary input\n 40: optional i32 executionStartToCloseTimeoutSeconds\n 50: optional i32 taskStartToCloseTimeoutSeconds\n// 52: optional ChildPolicy childPolicy -- Removed but reserve the IDL order number\n 54: optional string continuedExecutionRunId\n 55: optional ContinueAsNewInitiator initiator\n 56: optional string continuedFailureReason\n 57: optional binary continuedFailureDetails\n 58: optional binary lastCompletionResult\n 59: optional string originalExecutionRunId // This is the runID when the WorkflowExecutionStarted event is written\n 60: optional string identity\n 61: optional string firstExecutionRunId // This is the very first runID along the chain of ContinueAsNew and Reset.\n 62: optional i64 (js.type = \"Long\") firstScheduledTimeNano\n 70: optional RetryPolicy retryPolicy\n 80: optional i32 attempt\n 90: optional i64 (js.type = \"Long\") expirationTimestamp\n 100: optional string cronSchedule\n 110: optional i32 firstDecisionTaskBackoffSeconds\n 120: optional Memo memo\n 121: optional SearchAttributes searchAttributes\n 130: optional ResetPoints prevAutoResetPoints\n 140: optional Header header\n 150: optional map partitionConfig\n 160: optional string requestId\n 170: optional CronOverlapPolicy cronOverlapPolicy\n 180: optional ActiveClusterSelectionPolicy activeClusterSelectionPolicy\n}\n\nstruct ResetPoints{\n 10: optional list points\n}\n\n struct ResetPointInfo{\n 10: optional string binaryChecksum\n 20: optional string runId\n 30: optional i64 firstDecisionCompletedId\n 40: optional i64 (js.type = \"Long\") createdTimeNano\n 50: optional i64 (js.type = \"Long\") expiringTimeNano //the time that the run is deleted due to retention\n 60: optional bool resettable // false if the resset point has pending childWFs/reqCancels/signalExternals.\n}\n\nstruct WorkflowExecutionCompletedEventAttributes {\n 10: optional binary result\n 20: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n}\n\nstruct WorkflowExecutionFailedEventAttributes {\n 10: optional string reason\n 20: optional binary details\n 30: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n}\n\nstruct WorkflowExecutionTimedOutEventAttributes {\n 10: optional TimeoutType timeoutType\n}\n\nenum ContinueAsNewInitiator {\n Decider,\n RetryPolicy,\n CronSchedule,\n}\n\nstruct WorkflowExecutionContinuedAsNewEventAttributes {\n 10: optional string newExecutionRunId\n 20: optional WorkflowType workflowType\n 30: optional TaskList taskList\n 40: optional binary input\n 50: optional i32 executionStartToCloseTimeoutSeconds\n 60: optional i32 taskStartToCloseTimeoutSeconds\n 70: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 80: optional i32 backoffStartIntervalInSeconds\n 90: optional ContinueAsNewInitiator initiator\n 100: optional string failureReason\n 110: optional binary failureDetails\n 120: optional binary lastCompletionResult\n 130: optional Header header\n 140: optional Memo memo\n 150: optional SearchAttributes searchAttributes\n 160: optional CronOverlapPolicy cronOverlapPolicy\n 170: optional ActiveClusterSelectionPolicy activeClusterSelectionPolicy\n}\n\nstruct DecisionTaskScheduledEventAttributes {\n 10: optional TaskList taskList\n 20: optional i32 startToCloseTimeoutSeconds\n 30: optional i64 (js.type = \"Long\") attempt\n}\n\nstruct DecisionTaskStartedEventAttributes {\n 10: optional i64 (js.type = \"Long\") scheduledEventId\n 20: optional string identity\n 30: optional string requestId\n}\n\nstruct DecisionTaskCompletedEventAttributes {\n 10: optional binary executionContext\n 20: optional i64 (js.type = \"Long\") scheduledEventId\n 30: optional i64 (js.type = \"Long\") startedEventId\n 40: optional string identity\n 50: optional string binaryChecksum\n}\n\nstruct DecisionTaskTimedOutEventAttributes {\n 10: optional i64 (js.type = \"Long\") scheduledEventId\n 20: optional i64 (js.type = \"Long\") startedEventId\n 30: optional TimeoutType timeoutType\n // for reset workflow\n 40: optional string baseRunId\n 50: optional string newRunId\n 60: optional i64 (js.type = \"Long\") forkEventVersion\n 70: optional string reason\n 80: optional DecisionTaskTimedOutCause cause\n 90: optional string requestId\n}\n\nstruct DecisionTaskFailedEventAttributes {\n 10: optional i64 (js.type = \"Long\") scheduledEventId\n 20: optional i64 (js.type = \"Long\") startedEventId\n 30: optional DecisionTaskFailedCause cause\n 35: optional binary details\n 40: optional string identity\n 50: optional string reason\n // for reset workflow\n 60: optional string baseRunId\n 70: optional string newRunId\n 80: optional i64 (js.type = \"Long\") forkEventVersion\n 90: optional string binaryChecksum\n 100: optional string requestId\n}\n\nstruct ActivityTaskScheduledEventAttributes {\n 10: optional string activityId\n 20: optional ActivityType activityType\n 25: optional string domain\n 30: optional TaskList taskList\n 40: optional binary input\n 45: optional i32 scheduleToCloseTimeoutSeconds\n 50: optional i32 scheduleToStartTimeoutSeconds\n 55: optional i32 startToCloseTimeoutSeconds\n 60: optional i32 heartbeatTimeoutSeconds\n 90: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 110: optional RetryPolicy retryPolicy\n 120: optional Header header\n}\n\nstruct ActivityTaskStartedEventAttributes {\n 10: optional i64 (js.type = \"Long\") scheduledEventId\n 20: optional string identity\n 30: optional string requestId\n 40: optional i32 attempt\n 50: optional string lastFailureReason\n 60: optional binary lastFailureDetails\n}\n\nstruct ActivityTaskCompletedEventAttributes {\n 10: optional binary result\n 20: optional i64 (js.type = \"Long\") scheduledEventId\n 30: optional i64 (js.type = \"Long\") startedEventId\n 40: optional string identity\n}\n\nstruct ActivityTaskFailedEventAttributes {\n 10: optional string reason\n 20: optional binary details\n 30: optional i64 (js.type = \"Long\") scheduledEventId\n 40: optional i64 (js.type = \"Long\") startedEventId\n 50: optional string identity\n}\n\nstruct ActivityTaskTimedOutEventAttributes {\n 05: optional binary details\n 10: optional i64 (js.type = \"Long\") scheduledEventId\n 20: optional i64 (js.type = \"Long\") startedEventId\n 30: optional TimeoutType timeoutType\n // For retry activity, it may have a failure before timeout. It's important to keep those information for debug.\n // Client can also provide the info for making next decision\n 40: optional string lastFailureReason\n 50: optional binary lastFailureDetails\n}\n\nstruct ActivityTaskCancelRequestedEventAttributes {\n 10: optional string activityId\n 20: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n}\n\nstruct RequestCancelActivityTaskFailedEventAttributes{\n 10: optional string activityId\n 20: optional string cause\n 30: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n}\n\nstruct ActivityTaskCanceledEventAttributes {\n 10: optional binary details\n 20: optional i64 (js.type = \"Long\") latestCancelRequestedEventId\n 30: optional i64 (js.type = \"Long\") scheduledEventId\n 40: optional i64 (js.type = \"Long\") startedEventId\n 50: optional string identity\n}\n\nstruct TimerStartedEventAttributes {\n 10: optional string timerId\n 20: optional i64 (js.type = \"Long\") startToFireTimeoutSeconds\n 30: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n}\n\nstruct TimerFiredEventAttributes {\n 10: optional string timerId\n 20: optional i64 (js.type = \"Long\") startedEventId\n}\n\nstruct TimerCanceledEventAttributes {\n 10: optional string timerId\n 20: optional i64 (js.type = \"Long\") startedEventId\n 30: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 40: optional string identity\n}\n\nstruct CancelTimerFailedEventAttributes {\n 10: optional string timerId\n 20: optional string cause\n 30: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 40: optional string identity\n}\n\nstruct WorkflowExecutionCancelRequestedEventAttributes {\n 10: optional string cause\n 20: optional i64 (js.type = \"Long\") externalInitiatedEventId\n 30: optional WorkflowExecution externalWorkflowExecution\n 40: optional string identity\n 50: optional string requestId\n}\n\nstruct WorkflowExecutionCanceledEventAttributes {\n 10: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 20: optional binary details\n}\n\nstruct MarkerRecordedEventAttributes {\n 10: optional string markerName\n 20: optional binary details\n 30: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 40: optional Header header\n}\n\nstruct WorkflowExecutionSignaledEventAttributes {\n 10: optional string signalName\n 20: optional binary input\n 30: optional string identity\n 40: optional string requestId\n}\n\nstruct WorkflowExecutionTerminatedEventAttributes {\n 10: optional string reason\n 20: optional binary details\n 30: optional string identity\n}\n\nstruct RequestCancelExternalWorkflowExecutionInitiatedEventAttributes {\n 10: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 20: optional string domain\n 30: optional WorkflowExecution workflowExecution\n 40: optional binary control\n 50: optional bool childWorkflowOnly\n}\n\nstruct RequestCancelExternalWorkflowExecutionFailedEventAttributes {\n 10: optional CancelExternalWorkflowExecutionFailedCause cause\n 20: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 30: optional string domain\n 40: optional WorkflowExecution workflowExecution\n 50: optional i64 (js.type = \"Long\") initiatedEventId\n 60: optional binary control\n}\n\nstruct ExternalWorkflowExecutionCancelRequestedEventAttributes {\n 10: optional i64 (js.type = \"Long\") initiatedEventId\n 20: optional string domain\n 30: optional WorkflowExecution workflowExecution\n}\n\nstruct SignalExternalWorkflowExecutionInitiatedEventAttributes {\n 10: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 20: optional string domain\n 30: optional WorkflowExecution workflowExecution\n 40: optional string signalName\n 50: optional binary input\n 60: optional binary control\n 70: optional bool childWorkflowOnly\n}\n\nstruct SignalExternalWorkflowExecutionFailedEventAttributes {\n 10: optional SignalExternalWorkflowExecutionFailedCause cause\n 20: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 30: optional string domain\n 40: optional WorkflowExecution workflowExecution\n 50: optional i64 (js.type = \"Long\") initiatedEventId\n 60: optional binary control\n}\n\nstruct ExternalWorkflowExecutionSignaledEventAttributes {\n 10: optional i64 (js.type = \"Long\") initiatedEventId\n 20: optional string domain\n 30: optional WorkflowExecution workflowExecution\n 40: optional binary control\n}\n\nstruct UpsertWorkflowSearchAttributesEventAttributes {\n 10: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 20: optional SearchAttributes searchAttributes\n}\n\nstruct StartChildWorkflowExecutionInitiatedEventAttributes {\n 10: optional string domain\n 20: optional string workflowId\n 30: optional WorkflowType workflowType\n 40: optional TaskList taskList\n 50: optional binary input\n 60: optional i32 executionStartToCloseTimeoutSeconds\n 70: optional i32 taskStartToCloseTimeoutSeconds\n// 80: optional ChildPolicy childPolicy -- Removed but reserve the IDL order number\n 81: optional ParentClosePolicy parentClosePolicy\n 90: optional binary control\n 100: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n 110: optional WorkflowIdReusePolicy workflowIdReusePolicy\n 120: optional RetryPolicy retryPolicy\n 130: optional string cronSchedule\n 140: optional Header header\n 150: optional Memo memo\n 160: optional SearchAttributes searchAttributes\n 170: optional i32 delayStartSeconds\n 180: optional i32 jitterStartSeconds\n 190: optional i64 (js.type = \"Long\") firstRunAtTimestamp\n 200: optional CronOverlapPolicy cronOverlapPolicy\n 210: optional ActiveClusterSelectionPolicy activeClusterSelectionPolicy\n}\n\nstruct StartChildWorkflowExecutionFailedEventAttributes {\n 10: optional string domain\n 20: optional string workflowId\n 30: optional WorkflowType workflowType\n 40: optional ChildWorkflowExecutionFailedCause cause\n 50: optional binary control\n 60: optional i64 (js.type = \"Long\") initiatedEventId\n 70: optional i64 (js.type = \"Long\") decisionTaskCompletedEventId\n}\n\nstruct ChildWorkflowExecutionStartedEventAttributes {\n 10: optional string domain\n 20: optional i64 (js.type = \"Long\") initiatedEventId\n 30: optional WorkflowExecution workflowExecution\n 40: optional WorkflowType workflowType\n 50: optional Header header\n}\n\nstruct ChildWorkflowExecutionCompletedEventAttributes {\n 10: optional binary result\n 20: optional string domain\n 30: optional WorkflowExecution workflowExecution\n 40: optional WorkflowType workflowType\n 50: optional i64 (js.type = \"Long\") initiatedEventId\n 60: optional i64 (js.type = \"Long\") startedEventId\n}\n\nstruct ChildWorkflowExecutionFailedEventAttributes {\n 10: optional string reason\n 20: optional binary details\n 30: optional string domain\n 40: optional WorkflowExecution workflowExecution\n 50: optional WorkflowType workflowType\n 60: optional i64 (js.type = \"Long\") initiatedEventId\n 70: optional i64 (js.type = \"Long\") startedEventId\n}\n\nstruct ChildWorkflowExecutionCanceledEventAttributes {\n 10: optional binary details\n 20: optional string domain\n 30: optional WorkflowExecution workflowExecution\n 40: optional WorkflowType workflowType\n 50: optional i64 (js.type = \"Long\") initiatedEventId\n 60: optional i64 (js.type = \"Long\") startedEventId\n}\n\nstruct ChildWorkflowExecutionTimedOutEventAttributes {\n 10: optional TimeoutType timeoutType\n 20: optional string domain\n 30: optional WorkflowExecution workflowExecution\n 40: optional WorkflowType workflowType\n 50: optional i64 (js.type = \"Long\") initiatedEventId\n 60: optional i64 (js.type = \"Long\") startedEventId\n}\n\nstruct ChildWorkflowExecutionTerminatedEventAttributes {\n 10: optional string domain\n 20: optional WorkflowExecution workflowExecution\n 30: optional WorkflowType workflowType\n 40: optional i64 (js.type = \"Long\") initiatedEventId\n 50: optional i64 (js.type = \"Long\") startedEventId\n}\n\nstruct HistoryEvent {\n 10: optional i64 (js.type = \"Long\") eventId\n 20: optional i64 (js.type = \"Long\") timestamp\n 30: optional EventType eventType\n 35: optional i64 (js.type = \"Long\") version\n 36: optional i64 (js.type = \"Long\") taskId\n 40: optional WorkflowExecutionStartedEventAttributes workflowExecutionStartedEventAttributes\n 50: optional WorkflowExecutionCompletedEventAttributes workflowExecutionCompletedEventAttributes\n 60: optional WorkflowExecutionFailedEventAttributes workflowExecutionFailedEventAttributes\n 70: optional WorkflowExecutionTimedOutEventAttributes workflowExecutionTimedOutEventAttributes\n 80: optional DecisionTaskScheduledEventAttributes decisionTaskScheduledEventAttributes\n 90: optional DecisionTaskStartedEventAttributes decisionTaskStartedEventAttributes\n 100: optional DecisionTaskCompletedEventAttributes decisionTaskCompletedEventAttributes\n 110: optional DecisionTaskTimedOutEventAttributes decisionTaskTimedOutEventAttributes\n 120: optional DecisionTaskFailedEventAttributes decisionTaskFailedEventAttributes\n 130: optional ActivityTaskScheduledEventAttributes activityTaskScheduledEventAttributes\n 140: optional ActivityTaskStartedEventAttributes activityTaskStartedEventAttributes\n 150: optional ActivityTaskCompletedEventAttributes activityTaskCompletedEventAttributes\n 160: optional ActivityTaskFailedEventAttributes activityTaskFailedEventAttributes\n 170: optional ActivityTaskTimedOutEventAttributes activityTaskTimedOutEventAttributes\n 180: optional TimerStartedEventAttributes timerStartedEventAttributes\n 190: optional TimerFiredEventAttributes timerFiredEventAttributes\n 200: optional ActivityTaskCancelRequestedEventAttributes activityTaskCancelRequestedEventAttributes\n 210: optional RequestCancelActivityTaskFailedEventAttributes requestCancelActivityTaskFailedEventAttributes\n 220: optional ActivityTaskCanceledEventAttributes activityTaskCanceledEventAttributes\n 230: optional TimerCanceledEventAttributes timerCanceledEventAttributes\n 240: optional CancelTimerFailedEventAttributes cancelTimerFailedEventAttributes\n 250: optional MarkerRecordedEventAttributes markerRecordedEventAttributes\n 260: optional WorkflowExecutionSignaledEventAttributes workflowExecutionSignaledEventAttributes\n 270: optional WorkflowExecutionTerminatedEventAttributes workflowExecutionTerminatedEventAttributes\n 280: optional WorkflowExecutionCancelRequestedEventAttributes workflowExecutionCancelRequestedEventAttributes\n 290: optional WorkflowExecutionCanceledEventAttributes workflowExecutionCanceledEventAttributes\n 300: optional RequestCancelExternalWorkflowExecutionInitiatedEventAttributes requestCancelExternalWorkflowExecutionInitiatedEventAttributes\n 310: optional RequestCancelExternalWorkflowExecutionFailedEventAttributes requestCancelExternalWorkflowExecutionFailedEventAttributes\n 320: optional ExternalWorkflowExecutionCancelRequestedEventAttributes externalWorkflowExecutionCancelRequestedEventAttributes\n 330: optional WorkflowExecutionContinuedAsNewEventAttributes workflowExecutionContinuedAsNewEventAttributes\n 340: optional StartChildWorkflowExecutionInitiatedEventAttributes startChildWorkflowExecutionInitiatedEventAttributes\n 350: optional StartChildWorkflowExecutionFailedEventAttributes startChildWorkflowExecutionFailedEventAttributes\n 360: optional ChildWorkflowExecutionStartedEventAttributes childWorkflowExecutionStartedEventAttributes\n 370: optional ChildWorkflowExecutionCompletedEventAttributes childWorkflowExecutionCompletedEventAttributes\n 380: optional ChildWorkflowExecutionFailedEventAttributes childWorkflowExecutionFailedEventAttributes\n 390: optional ChildWorkflowExecutionCanceledEventAttributes childWorkflowExecutionCanceledEventAttributes\n 400: optional ChildWorkflowExecutionTimedOutEventAttributes childWorkflowExecutionTimedOutEventAttributes\n 410: optional ChildWorkflowExecutionTerminatedEventAttributes childWorkflowExecutionTerminatedEventAttributes\n 420: optional SignalExternalWorkflowExecutionInitiatedEventAttributes signalExternalWorkflowExecutionInitiatedEventAttributes\n 430: optional SignalExternalWorkflowExecutionFailedEventAttributes signalExternalWorkflowExecutionFailedEventAttributes\n 440: optional ExternalWorkflowExecutionSignaledEventAttributes externalWorkflowExecutionSignaledEventAttributes\n 450: optional UpsertWorkflowSearchAttributesEventAttributes upsertWorkflowSearchAttributesEventAttributes\n}\n\nstruct History {\n 10: optional list events\n}\n\nstruct WorkflowExecutionFilter {\n 10: optional string workflowId\n 20: optional string runId\n}\n\nstruct WorkflowTypeFilter {\n 10: optional string name\n}\n\nstruct StartTimeFilter {\n 10: optional i64 (js.type = \"Long\") earliestTime\n 20: optional i64 (js.type = \"Long\") latestTime\n}\n\nstruct DomainInfo {\n 10: optional string name\n 20: optional DomainStatus status\n 30: optional string description\n 40: optional string ownerEmail\n // A key-value map for any customized purpose\n 50: optional map data\n 60: optional string uuid\n}\n\nstruct DomainConfiguration {\n 10: optional i32 workflowExecutionRetentionPeriodInDays\n 20: optional bool emitMetric\n 60: optional IsolationGroupConfiguration isolationgroups\n 70: optional BadBinaries badBinaries\n 80: optional ArchivalStatus historyArchivalStatus\n 90: optional string historyArchivalURI\n 100: optional ArchivalStatus visibilityArchivalStatus\n 110: optional string visibilityArchivalURI\n 120: optional AsyncWorkflowConfiguration AsyncWorkflowConfiguration\n}\n\nstruct FailoverInfo {\n 10: optional i64 (js.type = \"Long\") failoverVersion\n 20: optional i64 (js.type = \"Long\") failoverStartTimestamp\n 30: optional i64 (js.type = \"Long\") failoverExpireTimestamp\n 40: optional i32 completedShardCount\n 50: optional list pendingShards\n}\n\nstruct BadBinaries{\n 10: optional map binaries\n}\n\nstruct BadBinaryInfo{\n 10: optional string reason\n 20: optional string operator\n 30: optional i64 (js.type = \"Long\") createdTimeNano\n}\n\nstruct UpdateDomainInfo {\n 10: optional string description\n 20: optional string ownerEmail\n // A key-value map for any customized purpose\n 30: optional map data\n}\n\nstruct ClusterReplicationConfiguration {\n 10: optional string clusterName\n}\n\nstruct DomainReplicationConfiguration {\n // activeClusterName is the name of the active cluster for active-passive domain\n 10: optional string activeClusterName\n\n // clusters is list of all active and passive clusters of domain\n 20: optional list clusters\n\n // activeClusters contains active cluster(s) information for active-active domain\n 30: optional ActiveClusters activeClusters\n}\n\n// ClusterAttributeScope is a mapping of the cluster atribute to the scope's\n// current stae and failover version, indicating how recently the change was made\nstruct ClusterAttributeScope {\n 10: optional map clusterAttributes;\n}\n\n// activeClustersByClusterAttribute is a map of whatever subdivision of the domain chosen\n// to active cluster info for active-active domains. The key refers to the type of\n// cluster attribute and the value refers to its cluster mappings.\n//\n// For example, a request to update the domain for two locations\n//\n// UpdateDomainRequest{\n// ReplicationConfiguration: {\n// ActiveClusters: {\n// ActiveClustersByClusterAttribute: {\n// \"location\": ClusterAttributeScope{\n// \"Tokyo\": {ActiveClusterInfo: \"cluster0, FailoverVersion: 123},\n// \"Morocco\": {ActiveClusterInfo: \"cluster1\", FailoverVersion: 100},\n// }\n// }\n// }\n// }\n// }\nstruct ActiveClusters {\n 10: optional map activeClustersByRegion // todo (david.porter) remove this as it's no longer used\n 11: optional map activeClustersByClusterAttribute\n}\n\n// ActiveClusterInfo contains the configuration of active-active domain's active\n// cluster & failover version for a specific region\nstruct ActiveClusterInfo {\n 10: optional string activeClusterName\n 20: optional i64 (js.type = \"Long\") failoverVersion\n}\n\nstruct RegisterDomainRequest {\n 10: optional string name\n 20: optional string description\n 30: optional string ownerEmail\n 40: optional i32 workflowExecutionRetentionPeriodInDays\n 50: optional bool emitMetric = true\n 60: optional list clusters\n 70: optional string activeClusterName\n // todo (david.porter) remove this field as it's not going to be used\n 75: optional map activeClustersByRegion\n // activeClusters is a map of cluster-attribute name to active cluster name for active-active domain\n 76: optional ActiveClusters activeClusters\n // A key-value map for any customized purpose\n 80: optional map data\n 90: optional string securityToken\n 120: optional bool isGlobalDomain\n 130: optional ArchivalStatus historyArchivalStatus\n 140: optional string historyArchivalURI\n 150: optional ArchivalStatus visibilityArchivalStatus\n 160: optional string visibilityArchivalURI\n}\n\nstruct ListDomainsRequest {\n 10: optional i32 pageSize\n 20: optional binary nextPageToken\n}\n\nstruct ListDomainsResponse {\n 10: optional list domains\n 20: optional binary nextPageToken\n}\n\nstruct DescribeDomainRequest {\n 10: optional string name\n 20: optional string uuid\n}\n\nstruct DescribeDomainResponse {\n 10: optional DomainInfo domainInfo\n 20: optional DomainConfiguration configuration\n 30: optional DomainReplicationConfiguration replicationConfiguration\n 40: optional i64 (js.type = \"Long\") failoverVersion\n 50: optional bool isGlobalDomain\n 60: optional FailoverInfo failoverInfo\n}\n\nstruct UpdateDomainRequest {\n 10: optional string name\n 20: optional UpdateDomainInfo updatedInfo\n 30: optional DomainConfiguration configuration\n 40: optional DomainReplicationConfiguration replicationConfiguration\n 50: optional string securityToken\n 60: optional string deleteBadBinary\n 70: optional i32 failoverTimeoutInSeconds\n}\n\nstruct UpdateDomainResponse {\n 10: optional DomainInfo domainInfo\n 20: optional DomainConfiguration configuration\n 30: optional DomainReplicationConfiguration replicationConfiguration\n 40: optional i64 (js.type = \"Long\") failoverVersion\n 50: optional bool isGlobalDomain\n}\n\nstruct FailoverDomainRequest {\n 10: optional string domainName\n 20: optional string domainActiveClusterName\n // only applicable to active-active domains where\n // specific cluster-attributes are being failed over\n 30: optional ActiveClusters activeClusters\n // user-requested addition \"reason\" variable created to increase transparency around failovers\n 40: optional string reason\n}\n\nstruct FailoverDomainResponse {\n 10: optional DomainInfo domainInfo\n 20: optional DomainConfiguration configuration\n 30: optional DomainReplicationConfiguration replicationConfiguration\n 40: optional i64 (js.type = \"Long\") failoverVersion\n 50: optional bool isGlobalDomain\n}\n\nstruct DeprecateDomainRequest {\n 10: optional string name\n 20: optional string securityToken\n}\n\nstruct DeleteDomainRequest {\n 10: optional string name\n 20: optional string securityToken\n}\n\nstruct ListFailoverHistoryRequest {\n // ListFailoverHistoryRequestFilters specifies the filters to apply to the request.\n // If not provided all failover events will be returned.\n 10: optional ListFailoverHistoryRequestFilters filters\n // PaginationOptions will be used to paginate the results.\n // If not provided the first 5 events will be returned.\n 20: optional PaginationOptions pagination\n}\n\n// ListFailoverHistoryRequestFilters is used to filter the failover history.\n// It will be extended with additional filters (e.g ClusterAttributes) as the active-active feature is developed.\nstruct ListFailoverHistoryRequestFilters {\n // domain_id is the id of the domain to list failover history for.\n 10: optional string domainID\n}\n\nstruct ListFailoverHistoryResponse {\n 10: optional list failoverEvents\n // next_page_token can be passed in a subsequent request to fetch the next set of events.\n 20: optional binary nextPageToken\n}\n\nstruct FailoverEvent {\n // id of the failover event\n // Can be passed with the created time to fetch a specific event.\n 10: optional string id\n // created_time is the time the failover event was created.\n // Can be passed with the ID to fetch a specific event.\n 20: optional i64 (js.type = \"Long\") createdTime\n 30: optional FailoverType failoverType\n 40: optional list clusterFailovers\n}\n\nstruct ClusterFailover {\n 10: optional ActiveClusterInfo fromCluster\n 20: optional ActiveClusterInfo toCluster\n // cluster_attribute is the scope and name for the attribute that was failed over.\n // If the cluster_attribute is not defined this failover can be assumed to be the default ActiveCluster.\n 30: optional ClusterAttribute clusterAttribute\n}\n\nstruct StartWorkflowExecutionRequest {\n 10: optional string domain\n 20: optional string workflowId\n 30: optional WorkflowType workflowType\n 40: optional TaskList taskList\n 50: optional binary input\n 60: optional i32 executionStartToCloseTimeoutSeconds\n 70: optional i32 taskStartToCloseTimeoutSeconds\n 80: optional string identity\n 90: optional string requestId\n 100: optional WorkflowIdReusePolicy workflowIdReusePolicy\n// 110: optional ChildPolicy childPolicy -- Removed but reserve the IDL order number\n 120: optional RetryPolicy retryPolicy\n 130: optional string cronSchedule\n 140: optional Memo memo\n 141: optional SearchAttributes searchAttributes\n 150: optional Header header\n 160: optional i32 delayStartSeconds\n 170: optional i32 jitterStartSeconds\n 180: optional i64 (js.type = \"Long\") firstRunAtTimestamp\n 190: optional CronOverlapPolicy cronOverlapPolicy\n 200: optional ActiveClusterSelectionPolicy activeClusterSelectionPolicy\n}\n\nstruct StartWorkflowExecutionResponse {\n 10: optional string runId\n}\n\nstruct StartWorkflowExecutionAsyncRequest {\n 10: optional StartWorkflowExecutionRequest request\n}\n\nstruct StartWorkflowExecutionAsyncResponse {\n}\n\nstruct RestartWorkflowExecutionResponse {\n 10: optional string runId\n}\n\nstruct DiagnoseWorkflowExecutionRequest {\n 10: optional string domain\n 20: optional WorkflowExecution workflowExecution\n 30: optional string identity\n}\n\nstruct DiagnoseWorkflowExecutionResponse {\n 10: optional string domain\n 20: optional WorkflowExecution diagnosticWorkflowExecution\n}\n\nstruct PollForDecisionTaskRequest {\n 10: optional string domain\n 20: optional TaskList taskList\n 30: optional string identity\n 40: optional string binaryChecksum\n}\n\nstruct PollForDecisionTaskResponse {\n 10: optional binary taskToken\n 20: optional WorkflowExecution workflowExecution\n 30: optional WorkflowType workflowType\n 40: optional i64 (js.type = \"Long\") previousStartedEventId\n 50: optional i64 (js.type = \"Long\") startedEventId\n 51: optional i64 (js.type = 'Long') attempt\n 54: optional i64 (js.type = \"Long\") backlogCountHint\n 60: optional History history\n 70: optional binary nextPageToken\n 80: optional WorkflowQuery query\n 90: optional TaskList WorkflowExecutionTaskList\n 100: optional i64 (js.type = \"Long\") scheduledTimestamp\n 110: optional i64 (js.type = \"Long\") startedTimestamp\n 120: optional map queries\n 130: optional i64 (js.type = 'Long') nextEventId\n 140: optional i64 (js.type = 'Long') totalHistoryBytes\n 150: optional AutoConfigHint autoConfigHint\n}\n\nstruct StickyExecutionAttributes {\n 10: optional TaskList workerTaskList\n 20: optional i32 scheduleToStartTimeoutSeconds\n}\n\nstruct RespondDecisionTaskCompletedRequest {\n 10: optional binary taskToken\n 20: optional list decisions\n 30: optional binary executionContext\n 40: optional string identity\n 50: optional StickyExecutionAttributes stickyAttributes\n 60: optional bool returnNewDecisionTask\n 70: optional bool forceCreateNewDecisionTask\n 80: optional string binaryChecksum\n 90: optional map queryResults\n}\n\nstruct RespondDecisionTaskCompletedResponse {\n 10: optional PollForDecisionTaskResponse decisionTask\n 20: optional map activitiesToDispatchLocally\n}\n\nstruct RespondDecisionTaskFailedRequest {\n 10: optional binary taskToken\n 20: optional DecisionTaskFailedCause cause\n 30: optional binary details\n 40: optional string identity\n 50: optional string binaryChecksum\n}\n\nstruct PollForActivityTaskRequest {\n 10: optional string domain\n 20: optional TaskList taskList\n 30: optional string identity\n 40: optional TaskListMetadata taskListMetadata\n}\n\nstruct PollForActivityTaskResponse {\n 10: optional binary taskToken\n 20: optional WorkflowExecution workflowExecution\n 30: optional string activityId\n 40: optional ActivityType activityType\n 50: optional binary input\n 70: optional i64 (js.type = \"Long\") scheduledTimestamp\n 80: optional i32 scheduleToCloseTimeoutSeconds\n 90: optional i64 (js.type = \"Long\") startedTimestamp\n 100: optional i32 startToCloseTimeoutSeconds\n 110: optional i32 heartbeatTimeoutSeconds\n 120: optional i32 attempt\n 130: optional i64 (js.type = \"Long\") scheduledTimestampOfThisAttempt\n 140: optional binary heartbeatDetails\n 150: optional WorkflowType workflowType\n 160: optional string workflowDomain\n 170: optional Header header\n 180: optional AutoConfigHint autoConfigHint\n}\n\nstruct RecordActivityTaskHeartbeatRequest {\n 10: optional binary taskToken\n 20: optional binary details\n 30: optional string identity\n}\n\nstruct RecordActivityTaskHeartbeatByIDRequest {\n 10: optional string domain\n 20: optional string workflowID\n 30: optional string runID\n 40: optional string activityID\n 50: optional binary details\n 60: optional string identity\n}\n\nstruct RecordActivityTaskHeartbeatResponse {\n 10: optional bool cancelRequested\n}\n\nstruct RespondActivityTaskCompletedRequest {\n 10: optional binary taskToken\n 20: optional binary result\n 30: optional string identity\n}\n\nstruct RespondActivityTaskFailedRequest {\n 10: optional binary taskToken\n 20: optional string reason\n 30: optional binary details\n 40: optional string identity\n}\n\nstruct RespondActivityTaskCanceledRequest {\n 10: optional binary taskToken\n 20: optional binary details\n 30: optional string identity\n}\n\nstruct RespondActivityTaskCompletedByIDRequest {\n 10: optional string domain\n 20: optional string workflowID\n 30: optional string runID\n 40: optional string activityID\n 50: optional binary result\n 60: optional string identity\n}\n\nstruct RespondActivityTaskFailedByIDRequest {\n 10: optional string domain\n 20: optional string workflowID\n 30: optional string runID\n 40: optional string activityID\n 50: optional string reason\n 60: optional binary details\n 70: optional string identity\n}\n\nstruct RespondActivityTaskCanceledByIDRequest {\n 10: optional string domain\n 20: optional string workflowID\n 30: optional string runID\n 40: optional string activityID\n 50: optional binary details\n 60: optional string identity\n}\n\nstruct RequestCancelWorkflowExecutionRequest {\n 10: optional string domain\n 20: optional WorkflowExecution workflowExecution\n 30: optional string identity\n 40: optional string requestId\n 50: optional string cause\n 60: optional string firstExecutionRunID\n}\n\nstruct GetWorkflowExecutionHistoryRequest {\n 10: optional string domain\n 20: optional WorkflowExecution execution\n 30: optional i32 maximumPageSize\n 40: optional binary nextPageToken\n 50: optional bool waitForNewEvent\n 60: optional HistoryEventFilterType HistoryEventFilterType\n 70: optional bool skipArchival\n 80: optional QueryConsistencyLevel queryConsistencyLevel\n}\n\nstruct GetWorkflowExecutionHistoryResponse {\n 10: optional History history\n 11: optional list rawHistory\n 20: optional binary nextPageToken\n 30: optional bool archived\n}\n\nstruct SignalWorkflowExecutionRequest {\n 10: optional string domain\n 20: optional WorkflowExecution workflowExecution\n 30: optional string signalName\n 40: optional binary input\n 50: optional string identity\n 60: optional string requestId\n 70: optional binary control\n}\n\nstruct SignalWithStartWorkflowExecutionRequest {\n 10: optional string domain\n 20: optional string workflowId\n 30: optional WorkflowType workflowType\n 40: optional TaskList taskList\n 50: optional binary input\n 60: optional i32 executionStartToCloseTimeoutSeconds\n 70: optional i32 taskStartToCloseTimeoutSeconds\n 80: optional string identity\n 90: optional string requestId\n 100: optional WorkflowIdReusePolicy workflowIdReusePolicy\n 110: optional string signalName\n 120: optional binary signalInput\n 130: optional binary control\n 140: optional RetryPolicy retryPolicy\n 150: optional string cronSchedule\n 160: optional Memo memo\n 161: optional SearchAttributes searchAttributes\n 170: optional Header header\n 180: optional i32 delayStartSeconds\n 190: optional i32 jitterStartSeconds\n 200: optional i64 (js.type = \"Long\") firstRunAtTimestamp\n 210: optional CronOverlapPolicy cronOverlapPolicy\n 220: optional ActiveClusterSelectionPolicy activeClusterSelectionPolicy\n}\n\nstruct SignalWithStartWorkflowExecutionAsyncRequest {\n 10: optional SignalWithStartWorkflowExecutionRequest request\n}\n\nstruct SignalWithStartWorkflowExecutionAsyncResponse {\n}\n\nstruct RestartWorkflowExecutionRequest {\n 10: optional string domain\n 20: optional WorkflowExecution workflowExecution\n 30: optional string reason\n 40: optional string identity\n}\nstruct TerminateWorkflowExecutionRequest {\n 10: optional string domain\n 20: optional WorkflowExecution workflowExecution\n 30: optional string reason\n 40: optional binary details\n 50: optional string identity\n 60: optional string firstExecutionRunID\n}\n\nstruct ResetWorkflowExecutionRequest {\n 10: optional string domain\n 20: optional WorkflowExecution workflowExecution\n 30: optional string reason\n 40: optional i64 (js.type = \"Long\") decisionFinishEventId\n 50: optional string requestId\n 60: optional bool skipSignalReapply\n}\n\nstruct ResetWorkflowExecutionResponse {\n 10: optional string runId\n}\n\nstruct ListOpenWorkflowExecutionsRequest {\n 10: optional string domain\n 20: optional i32 maximumPageSize\n 30: optional binary nextPageToken\n 40: optional StartTimeFilter StartTimeFilter\n 50: optional WorkflowExecutionFilter executionFilter\n 60: optional WorkflowTypeFilter typeFilter\n}\n\nstruct ListOpenWorkflowExecutionsResponse {\n 10: optional list executions\n 20: optional binary nextPageToken\n}\n\nstruct ListClosedWorkflowExecutionsRequest {\n 10: optional string domain\n 20: optional i32 maximumPageSize\n 30: optional binary nextPageToken\n 40: optional StartTimeFilter StartTimeFilter\n 50: optional WorkflowExecutionFilter executionFilter\n 60: optional WorkflowTypeFilter typeFilter\n 70: optional WorkflowExecutionCloseStatus statusFilter\n}\n\nstruct ListClosedWorkflowExecutionsResponse {\n 10: optional list executions\n 20: optional binary nextPageToken\n}\n\nstruct ListWorkflowExecutionsRequest {\n 10: optional string domain\n 20: optional i32 pageSize\n 30: optional binary nextPageToken\n 40: optional string query\n}\n\nstruct ListWorkflowExecutionsResponse {\n 10: optional list executions\n 20: optional binary nextPageToken\n}\n\nstruct ListArchivedWorkflowExecutionsRequest {\n 10: optional string domain\n 20: optional i32 pageSize\n 30: optional binary nextPageToken\n 40: optional string query\n}\n\nstruct ListArchivedWorkflowExecutionsResponse {\n 10: optional list executions\n 20: optional binary nextPageToken\n}\n\nstruct CountWorkflowExecutionsRequest {\n 10: optional string domain\n 20: optional string query\n}\n\nstruct CountWorkflowExecutionsResponse {\n 10: optional i64 count\n}\n\nstruct GetSearchAttributesResponse {\n 10: optional map keys\n}\n\nstruct QueryWorkflowRequest {\n 10: optional string domain\n 20: optional WorkflowExecution execution\n 30: optional WorkflowQuery query\n // QueryRejectCondition can used to reject the query if workflow state does not satisify condition\n 40: optional QueryRejectCondition queryRejectCondition\n 50: optional QueryConsistencyLevel queryConsistencyLevel\n}\n\nstruct QueryRejected {\n 10: optional WorkflowExecutionCloseStatus closeStatus\n}\n\nstruct QueryWorkflowResponse {\n 10: optional binary queryResult\n 20: optional QueryRejected queryRejected\n}\n\nstruct WorkflowQuery {\n 10: optional string queryType\n 20: optional binary queryArgs\n}\n\nstruct ResetStickyTaskListRequest {\n 10: optional string domain\n 20: optional WorkflowExecution execution\n}\n\nstruct ResetStickyTaskListResponse {\n // The reason to keep this response is to allow returning\n // information in the future.\n}\n\nstruct RespondQueryTaskCompletedRequest {\n 10: optional binary taskToken\n 20: optional QueryTaskCompletedType completedType\n 30: optional binary queryResult\n 40: optional string errorMessage\n 50: optional WorkerVersionInfo workerVersionInfo\n}\n\nstruct WorkflowQueryResult {\n 10: optional QueryResultType resultType\n 20: optional binary answer\n 30: optional string errorMessage\n}\n\nstruct DescribeWorkflowExecutionRequest {\n 10: optional string domain\n 20: optional WorkflowExecution execution\n 30: optional QueryConsistencyLevel queryConsistencyLevel\n}\n\nstruct PendingActivityInfo {\n 10: optional string activityID\n 20: optional ActivityType activityType\n 30: optional PendingActivityState state\n 40: optional binary heartbeatDetails\n 50: optional i64 (js.type = \"Long\") lastHeartbeatTimestamp\n 60: optional i64 (js.type = \"Long\") lastStartedTimestamp\n 70: optional i32 attempt\n 80: optional i32 maximumAttempts\n 90: optional i64 (js.type = \"Long\") scheduledTimestamp\n 100: optional i64 (js.type = \"Long\") expirationTimestamp\n 110: optional string lastFailureReason\n 120: optional string lastWorkerIdentity\n 130: optional binary lastFailureDetails\n 140: optional string startedWorkerIdentity\n 150: optional i64 (js.type = \"Long\") scheduleID\n}\n\nstruct PendingDecisionInfo {\n 10: optional PendingDecisionState state\n 20: optional i64 (js.type = \"Long\") scheduledTimestamp\n 30: optional i64 (js.type = \"Long\") startedTimestamp\n 40: optional i64 attempt\n 50: optional i64 (js.type = \"Long\") originalScheduledTimestamp\n 60: optional i64 (js.type = \"Long\") scheduleID\n}\n\nstruct PendingChildExecutionInfo {\n 1: optional string domain\n 10: optional string workflowID\n 20: optional string runID\n 30: optional string workflowTypName\n 40: optional i64 (js.type = \"Long\") initiatedID\n 50: optional ParentClosePolicy parentClosePolicy\n}\n\nstruct DescribeWorkflowExecutionResponse {\n 10: optional WorkflowExecutionConfiguration executionConfiguration\n 20: optional WorkflowExecutionInfo workflowExecutionInfo\n 30: optional list pendingActivities\n 40: optional list pendingChildren\n 50: optional PendingDecisionInfo pendingDecision\n}\n\nstruct DescribeTaskListRequest {\n 10: optional string domain\n 20: optional TaskList taskList\n 30: optional TaskListType taskListType\n 40: optional bool includeTaskListStatus\n}\n\nstruct DescribeTaskListResponse {\n 10: optional list pollers\n 20: optional TaskListStatus taskListStatus\n // The TaskList being described\n 30: optional TaskList taskList\n}\n\nstruct GetTaskListsByDomainRequest {\n 10: optional string domainName\n}\n\nstruct GetTaskListsByDomainResponse {\n 10: optional map decisionTaskListMap\n 20: optional map activityTaskListMap\n}\n\nstruct ListTaskListPartitionsRequest {\n 10: optional string domain\n 20: optional TaskList taskList\n}\n\nstruct TaskListPartitionMetadata {\n 10: optional string key\n 20: optional string ownerHostName\n}\n\nstruct ListTaskListPartitionsResponse {\n 10: optional list activityTaskListPartitions\n 20: optional list decisionTaskListPartitions\n}\n\nstruct IsolationGroupMetrics {\n 10: optional double newTasksPerSecond\n 20: optional i64 (js.type = \"Long\") pollerCount\n}\n\nstruct TaskListStatus {\n 10: optional i64 (js.type = \"Long\") backlogCountHint\n 20: optional i64 (js.type = \"Long\") readLevel\n 30: optional i64 (js.type = \"Long\") ackLevel\n 35: optional double ratePerSecond\n 40: optional TaskIDBlock taskIDBlock\n 50: optional map isolationGroupMetrics\n 60: optional double newTasksPerSecond\n 70: optional bool empty\n}\n\nstruct TaskIDBlock {\n 10: optional i64 (js.type = \"Long\") startID\n 20: optional i64 (js.type = \"Long\") endID\n}\n\n//At least one of the parameters needs to be provided\nstruct DescribeHistoryHostRequest {\n 10: optional string hostAddress //ip:port\n 20: optional i32 shardIdForHost\n 30: optional WorkflowExecution executionForHost\n}\n\nstruct RemoveTaskRequest {\n 10: optional i32 shardID\n 20: optional i32 type\n 30: optional i64 (js.type = \"Long\") taskID\n 40: optional i64 (js.type = \"Long\") visibilityTimestamp\n 50: optional string clusterName\n}\n\nstruct CloseShardRequest {\n 10: optional i32 shardID\n}\n\nstruct ResetQueueRequest {\n 10: optional i32 shardID\n 20: optional string clusterName\n 30: optional i32 type\n}\n\nstruct DescribeQueueRequest {\n 10: optional i32 shardID\n 20: optional string clusterName\n 30: optional i32 type\n}\n\nstruct DescribeQueueResponse {\n 10: optional list processingQueueStates\n}\n\nstruct DescribeShardDistributionRequest {\n 10: optional i32 pageSize\n 20: optional i32 pageID\n}\n\nstruct DescribeShardDistributionResponse {\n 10: optional i32 numberOfShards\n\n // ShardID to Address (ip:port) map\n 20: optional map shards\n}\n\nstruct DescribeHistoryHostResponse{\n 10: optional i32 numberOfShards\n 20: optional list shardIDs\n 30: optional DomainCacheInfo domainCache\n 40: optional string shardControllerStatus\n 50: optional string address\n}\n\nstruct DomainCacheInfo{\n 10: optional i64 numOfItemsInCacheByID\n 20: optional i64 numOfItemsInCacheByName\n}\n\nenum TaskListType {\n /*\n * Decision type of tasklist\n */\n Decision,\n /*\n * Activity type of tasklist\n */\n Activity,\n}\n\nstruct PollerInfo {\n // Unix Nano\n 10: optional i64 (js.type = \"Long\") lastAccessTime\n 20: optional string identity\n 30: optional double ratePerSecond\n}\n\nstruct RetryPolicy {\n // Interval of the first retry. If coefficient is 1.0 then it is used for all retries.\n 10: optional i32 initialIntervalInSeconds\n\n // Coefficient used to calculate the next retry interval.\n // The next retry interval is previous interval multiplied by the coefficient.\n // Must be 1 or larger.\n 20: optional double backoffCoefficient\n\n // Maximum interval between retries. Exponential backoff leads to interval increase.\n // This value is the cap of the increase. Default is 100x of initial interval.\n 30: optional i32 maximumIntervalInSeconds\n\n // Maximum number of attempts. When exceeded the retries stop even if not expired yet.\n // Must be 1 or bigger. Default is unlimited.\n 40: optional i32 maximumAttempts\n\n // Non-Retriable errors. Will stop retrying if error matches this list.\n 50: optional list nonRetriableErrorReasons\n\n // Expiration time for the whole retry process.\n 60: optional i32 expirationIntervalInSeconds\n}\n\n// HistoryBranchRange represents a piece of range for a branch.\nstruct HistoryBranchRange{\n // branchID of original branch forked from\n 10: optional string branchID\n // beinning node for the range, inclusive\n 20: optional i64 beginNodeID\n // ending node for the range, exclusive\n 30: optional i64 endNodeID\n}\n\n// For history persistence to serialize/deserialize branch details\nstruct HistoryBranch{\n 10: optional string treeID\n 20: optional string branchID\n 30: optional list ancestors\n}\n\n// VersionHistoryItem contains signal eventID and the corresponding version\nstruct VersionHistoryItem{\n 10: optional i64 (js.type = \"Long\") eventID\n 20: optional i64 (js.type = \"Long\") version\n}\n\n// VersionHistory contains the version history of a branch\nstruct VersionHistory{\n 10: optional binary branchToken\n 20: optional list items\n}\n\n// VersionHistories contains all version histories from all branches\nstruct VersionHistories{\n 10: optional i32 currentVersionHistoryIndex\n 20: optional list histories\n}\n\n// ReapplyEventsRequest is the request for reapply events API\nstruct ReapplyEventsRequest{\n 10: optional string domainName\n 20: optional WorkflowExecution workflowExecution\n 30: optional DataBlob events\n}\n\n// SupportedClientVersions contains the support versions for client library\nstruct SupportedClientVersions{\n 10: optional string goSdk\n 20: optional string javaSdk\n}\n\n// ClusterInfo contains information about cadence cluster\nstruct ClusterInfo{\n 10: optional SupportedClientVersions supportedClientVersions\n}\n\nstruct RefreshWorkflowTasksRequest {\n 10: optional string domain\n 20: optional WorkflowExecution execution\n}\n\n// DEPRECATED: use proto definition instead\nstruct FeatureFlags {\n\t10: optional bool WorkflowExecutionAlreadyCompletedErrorEnabled\n 20: optional bool AutoForwardingEnabled\n}\n\nenum CrossClusterTaskType {\n StartChildExecution\n CancelExecution\n SignalExecution\n RecordChildWorkflowExecutionComplete\n ApplyParentClosePolicy\n}\n\nenum CrossClusterTaskFailedCause {\n DOMAIN_NOT_ACTIVE\n DOMAIN_NOT_EXISTS\n WORKFLOW_ALREADY_RUNNING\n WORKFLOW_NOT_EXISTS\n WORKFLOW_ALREADY_COMPLETED\n UNCATEGORIZED\n}\n\nenum GetTaskFailedCause {\n SERVICE_BUSY\n TIMEOUT\n SHARD_OWNERSHIP_LOST\n UNCATEGORIZED\n}\n\nstruct CrossClusterTaskInfo {\n 10: optional string domainID\n 20: optional string workflowID\n 30: optional string runID\n 40: optional CrossClusterTaskType taskType\n 50: optional i16 taskState\n 60: optional i64 (js.type = \"Long\") taskID\n 70: optional i64 (js.type = \"Long\") visibilityTimestamp\n}\n\nstruct CrossClusterStartChildExecutionRequestAttributes {\n 10: optional string targetDomainID\n 20: optional string requestID\n 30: optional i64 (js.type = \"Long\") initiatedEventID\n 40: optional StartChildWorkflowExecutionInitiatedEventAttributes initiatedEventAttributes\n // targetRunID is for scheduling first decision task\n // targetWorkflowID is available in initiatedEventAttributes\n 50: optional string targetRunID\n 60: optional map partitionConfig\n}\n\nstruct CrossClusterStartChildExecutionResponseAttributes {\n 10: optional string runID\n}\n\nstruct CrossClusterCancelExecutionRequestAttributes {\n 10: optional string targetDomainID\n 20: optional string targetWorkflowID\n 30: optional string targetRunID\n 40: optional string requestID\n 50: optional i64 (js.type = \"Long\") initiatedEventID\n 60: optional bool childWorkflowOnly\n}\n\nstruct CrossClusterCancelExecutionResponseAttributes {\n}\n\nstruct CrossClusterSignalExecutionRequestAttributes {\n 10: optional string targetDomainID\n 20: optional string targetWorkflowID\n 30: optional string targetRunID\n 40: optional string requestID\n 50: optional i64 (js.type = \"Long\") initiatedEventID\n 60: optional bool childWorkflowOnly\n 70: optional string signalName\n 80: optional binary signalInput\n 90: optional binary control\n}\n\nstruct CrossClusterSignalExecutionResponseAttributes {\n}\n\nstruct CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes {\n 10: optional string targetDomainID\n 20: optional string targetWorkflowID\n 30: optional string targetRunID\n 40: optional i64 (js.type = \"Long\") initiatedEventID\n 50: optional HistoryEvent completionEvent\n}\n\nstruct CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes {\n}\n\nstruct ApplyParentClosePolicyAttributes {\n 10: optional string childDomainID\n 20: optional string childWorkflowID\n 30: optional string childRunID\n 40: optional ParentClosePolicy parentClosePolicy\n}\n\nstruct ApplyParentClosePolicyStatus {\n 10: optional bool completed\n 20: optional CrossClusterTaskFailedCause failedCause\n}\n\nstruct ApplyParentClosePolicyRequest {\n 10: optional ApplyParentClosePolicyAttributes child\n 20: optional ApplyParentClosePolicyStatus status\n}\n\nstruct CrossClusterApplyParentClosePolicyRequestAttributes {\n 10: optional list children\n}\n\nstruct ApplyParentClosePolicyResult {\n 10: optional ApplyParentClosePolicyAttributes child\n 20: optional CrossClusterTaskFailedCause failedCause\n}\n\nstruct CrossClusterApplyParentClosePolicyResponseAttributes {\n 10: optional list childrenStatus\n}\n\nstruct CrossClusterTaskRequest {\n 10: optional CrossClusterTaskInfo taskInfo\n 20: optional CrossClusterStartChildExecutionRequestAttributes startChildExecutionAttributes\n 30: optional CrossClusterCancelExecutionRequestAttributes cancelExecutionAttributes\n 40: optional CrossClusterSignalExecutionRequestAttributes signalExecutionAttributes\n 50: optional CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes recordChildWorkflowExecutionCompleteAttributes\n 60: optional CrossClusterApplyParentClosePolicyRequestAttributes applyParentClosePolicyAttributes\n}\n\nstruct CrossClusterTaskResponse {\n 10: optional i64 (js.type = \"Long\") taskID\n 20: optional CrossClusterTaskType taskType\n 30: optional i16 taskState\n 40: optional CrossClusterTaskFailedCause failedCause\n 50: optional CrossClusterStartChildExecutionResponseAttributes startChildExecutionAttributes\n 60: optional CrossClusterCancelExecutionResponseAttributes cancelExecutionAttributes\n 70: optional CrossClusterSignalExecutionResponseAttributes signalExecutionAttributes\n 80: optional CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes recordChildWorkflowExecutionCompleteAttributes\n 90: optional CrossClusterApplyParentClosePolicyResponseAttributes applyParentClosePolicyAttributes\n}\n\nstruct GetCrossClusterTasksRequest {\n 10: optional list shardIDs\n 20: optional string targetCluster\n}\n\nstruct GetCrossClusterTasksResponse {\n 10: optional map> tasksByShard\n 20: optional map failedCauseByShard\n}\n\nstruct RespondCrossClusterTasksCompletedRequest {\n 10: optional i32 shardID\n 20: optional string targetCluster\n 30: optional list taskResponses\n 40: optional bool fetchNewTasks\n}\n\nstruct RespondCrossClusterTasksCompletedResponse {\n 10: optional list tasks\n}\n\nenum IsolationGroupState {\n INVALID,\n HEALTHY,\n DRAINED,\n}\n\nstruct IsolationGroupPartition {\n 10: optional string name\n 20: optional IsolationGroupState state\n}\n\nstruct IsolationGroupConfiguration {\n 10: optional list isolationGroups\n}\n\nstruct AsyncWorkflowConfiguration {\n 10: optional bool enabled\n // PredefinedQueueName is the name of the predefined queue in cadence server config's asyncWorkflowQueues\n 20: optional string predefinedQueueName\n // queueType is the type of the queue if predefined_queue_name is not used\n 30: optional string queueType\n // queueConfig is the configuration for the queue if predefined_queue_name is not used\n 40: optional DataBlob queueConfig\n}\n\n/**\n* Any is a logical duplicate of google.protobuf.Any.\n*\n* The intent of the type is the same, but it is not intended to be directly\n* compatible with google.protobuf.Any or any Thrift equivalent - this blob is\n* RPC-type agnostic by design (as the underlying data may be transported over\n* proto or thrift), and the data-bytes may be in any encoding.\n*\n* This is intentionally different from DataBlob, which supports only a handful\n* of known encodings so it can be interpreted everywhere. Any supports literally\n* any contents, and needs to be considered opaque until it is given to something\n* that is expecting it.\n*\n* See ValueType to interpret the contents.\n**/\nstruct Any {\n // Type-string describing value's contents, and intentionally avoiding the\n // name \"type\" as it is often a special term.\n // This should usually be a hard-coded string of some kind.\n 10: optional string ValueType\n // Arbitrarily-encoded bytes, to be deserialized by a runtime implementation.\n // The contents are described by ValueType.\n 20: optional binary Value\n}\n\nstruct AutoConfigHint {\n 10: optional bool enableAutoConfig\n 20: optional i64 pollerWaitTimeInMs\n}\n\nstruct QueueState {\n 10: optional map virtualQueueStates\n 20: optional TaskKey exclusiveMaxReadLevel\n}\n\nstruct VirtualQueueState {\n 10: optional list virtualSliceStates\n}\n\nstruct VirtualSliceState {\n 10: optional TaskRange taskRange\n 20: optional Predicate predicate\n}\n\nstruct TaskRange {\n 10: optional TaskKey inclusiveMin\n 20: optional TaskKey exclusiveMax\n}\n\nstruct TaskKey {\n 10: optional i64 scheduledTimeNano\n 20: optional i64 taskID\n}\n\n// ActiveClusterSelectionPolicy is for active-active domains, it serves as a means to select\n// the active cluster, by specifying the attribute by which to divide the workflows\n// in that domain.\nstruct ActiveClusterSelectionPolicy {\n 1: optional ClusterAttribute clusterAttribute\n\n 10: optional ActiveClusterSelectionStrategy strategy // todo (david.porter) remove these as they're not used anymore\n 20: optional string stickyRegion // todo (david.porter) remove these as they're not used anymore\n 30: optional string externalEntityType // todo (david.porter) remove these as they're not used anymore\n 40: optional string externalEntityKey // todo (david.porter) remove these as they're not used anymore\n}\n\n// ClusterAttribute is used for subdividing workflows in a domain into their active\n// and passive clusters. Examples of this might be 'region' and 'cluster1' as\n// respective region and scope fields.\n//\n// for example, a workflow may specify this in it's start request:\n//\n// StartWorkflowRequest{\n// ActiveClusterSelectionPolicy: {\n// ClusterAttribute: {\n// Scope: \"cityID\",\n// Name: \"Lisbon\"\n// }\n// }\n// }\n//\n// and this means that this workflow will be associate with the domain's cluster attribute 'Lisbon',\n// be active in the cluster that has Lisbon active and\n// failover when that cluster-attribute is set to failover.\nstruct ClusterAttribute {\n 1: optional string scope\n 2: optional string name\n}\n\n// FailoverType describes how a failover operation will be performed.\nenum FailoverType {\n INVALID\n FORCE\n GRACEFUL\n}\n\n// PaginationOptions provides common options for paginated RPCs.\nstruct PaginationOptions {\n // page_size configures the number of results to be returned as part of each page\n 10: optional i32 pageSize\n // next_page_token should be provided from a previous response to fetch the next page.\n // if empty, the first page will be returned.\n 20: optional binary nextPageToken\n}\n\n// todo (david.porter) Remove this, as it's no longer needed\n// with the active/active configuration we have\nenum ActiveClusterSelectionStrategy {\n REGION_STICKY,\n EXTERNAL_ENTITY,\n}\n\nenum PredicateType {\n Universal,\n Empty,\n DomainID,\n}\n\nstruct UniversalPredicateAttributes {}\n\nstruct EmptyPredicateAttributes {}\n\nstruct DomainIDPredicateAttributes {\n 10: optional list domainIDs\n 20: optional bool isExclusive\n}\n\nstruct Predicate {\n 10: optional PredicateType predicateType\n 20: optional UniversalPredicateAttributes universalPredicateAttributes\n 30: optional EmptyPredicateAttributes emptyPredicateAttributes\n 40: optional DomainIDPredicateAttributes domainIDPredicateAttributes\n}\n" ================================================ FILE: .gen/go/shared/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package shared import yarpcerrors "go.uber.org/yarpc/yarpcerrors" // YARPCErrorCode returns nil for AccessDeniedError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *AccessDeniedError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for AccessDeniedError. func (e *AccessDeniedError) YARPCErrorName() string { return "AccessDeniedError" } // YARPCErrorCode returns nil for BadRequestError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *BadRequestError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for BadRequestError. func (e *BadRequestError) YARPCErrorName() string { return "BadRequestError" } // YARPCErrorCode returns nil for CancellationAlreadyRequestedError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *CancellationAlreadyRequestedError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for CancellationAlreadyRequestedError. func (e *CancellationAlreadyRequestedError) YARPCErrorName() string { return "CancellationAlreadyRequestedError" } // YARPCErrorCode returns nil for ClientVersionNotSupportedError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *ClientVersionNotSupportedError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for ClientVersionNotSupportedError. func (e *ClientVersionNotSupportedError) YARPCErrorName() string { return "ClientVersionNotSupportedError" } // YARPCErrorCode returns nil for CurrentBranchChangedError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *CurrentBranchChangedError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for CurrentBranchChangedError. func (e *CurrentBranchChangedError) YARPCErrorName() string { return "CurrentBranchChangedError" } // YARPCErrorCode returns nil for DomainAlreadyExistsError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *DomainAlreadyExistsError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for DomainAlreadyExistsError. func (e *DomainAlreadyExistsError) YARPCErrorName() string { return "DomainAlreadyExistsError" } // YARPCErrorCode returns nil for DomainNotActiveError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *DomainNotActiveError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for DomainNotActiveError. func (e *DomainNotActiveError) YARPCErrorName() string { return "DomainNotActiveError" } // YARPCErrorCode returns nil for EntityNotExistsError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *EntityNotExistsError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for EntityNotExistsError. func (e *EntityNotExistsError) YARPCErrorName() string { return "EntityNotExistsError" } // YARPCErrorCode returns nil for FeatureNotEnabledError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *FeatureNotEnabledError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for FeatureNotEnabledError. func (e *FeatureNotEnabledError) YARPCErrorName() string { return "FeatureNotEnabledError" } // YARPCErrorCode returns nil for InternalDataInconsistencyError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *InternalDataInconsistencyError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for InternalDataInconsistencyError. func (e *InternalDataInconsistencyError) YARPCErrorName() string { return "InternalDataInconsistencyError" } // YARPCErrorCode returns nil for InternalServiceError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *InternalServiceError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for InternalServiceError. func (e *InternalServiceError) YARPCErrorName() string { return "InternalServiceError" } // YARPCErrorCode returns nil for LimitExceededError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *LimitExceededError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for LimitExceededError. func (e *LimitExceededError) YARPCErrorName() string { return "LimitExceededError" } // YARPCErrorCode returns nil for QueryFailedError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *QueryFailedError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for QueryFailedError. func (e *QueryFailedError) YARPCErrorName() string { return "QueryFailedError" } // YARPCErrorCode returns nil for RemoteSyncMatchedError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *RemoteSyncMatchedError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for RemoteSyncMatchedError. func (e *RemoteSyncMatchedError) YARPCErrorName() string { return "RemoteSyncMatchedError" } // YARPCErrorCode returns nil for RetryTaskV2Error. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *RetryTaskV2Error) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for RetryTaskV2Error. func (e *RetryTaskV2Error) YARPCErrorName() string { return "RetryTaskV2Error" } // YARPCErrorCode returns nil for ServiceBusyError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *ServiceBusyError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for ServiceBusyError. func (e *ServiceBusyError) YARPCErrorName() string { return "ServiceBusyError" } // YARPCErrorCode returns nil for StickyWorkerUnavailableError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *StickyWorkerUnavailableError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for StickyWorkerUnavailableError. func (e *StickyWorkerUnavailableError) YARPCErrorName() string { return "StickyWorkerUnavailableError" } // YARPCErrorCode returns nil for TaskListNotOwnedByHostError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *TaskListNotOwnedByHostError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for TaskListNotOwnedByHostError. func (e *TaskListNotOwnedByHostError) YARPCErrorName() string { return "TaskListNotOwnedByHostError" } // YARPCErrorCode returns nil for WorkflowExecutionAlreadyCompletedError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *WorkflowExecutionAlreadyCompletedError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for WorkflowExecutionAlreadyCompletedError. func (e *WorkflowExecutionAlreadyCompletedError) YARPCErrorName() string { return "WorkflowExecutionAlreadyCompletedError" } // YARPCErrorCode returns nil for WorkflowExecutionAlreadyStartedError. // // This is derived from the rpc.code annotation on the Thrift exception. func (e *WorkflowExecutionAlreadyStartedError) YARPCErrorCode() *yarpcerrors.Code { return nil } // Name is the error name for WorkflowExecutionAlreadyStartedError. func (e *WorkflowExecutionAlreadyStartedError) YARPCErrorName() string { return "WorkflowExecutionAlreadyStartedError" } ================================================ FILE: .gen/go/sqlblobs/sqlblobs.go ================================================ // Code generated by thriftrw v1.29.2. DO NOT EDIT. // @generated package sqlblobs import ( bytes "bytes" base64 "encoding/base64" json "encoding/json" fmt "fmt" math "math" strconv "strconv" strings "strings" multierr "go.uber.org/multierr" stream "go.uber.org/thriftrw/protocol/stream" thriftreflect "go.uber.org/thriftrw/thriftreflect" wire "go.uber.org/thriftrw/wire" zapcore "go.uber.org/zap/zapcore" shared "github.com/uber/cadence/.gen/go/shared" ) type ActivityInfo struct { Version *int64 `json:"version,omitempty"` ScheduledEventBatchID *int64 `json:"scheduledEventBatchID,omitempty"` ScheduledEvent []byte `json:"scheduledEvent,omitempty"` ScheduledEventEncoding *string `json:"scheduledEventEncoding,omitempty"` ScheduledTimeNanos *int64 `json:"scheduledTimeNanos,omitempty"` StartedID *int64 `json:"startedID,omitempty"` StartedEvent []byte `json:"startedEvent,omitempty"` StartedEventEncoding *string `json:"startedEventEncoding,omitempty"` StartedTimeNanos *int64 `json:"startedTimeNanos,omitempty"` ActivityID *string `json:"activityID,omitempty"` RequestID *string `json:"requestID,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` ScheduleToCloseTimeoutSeconds *int32 `json:"scheduleToCloseTimeoutSeconds,omitempty"` StartToCloseTimeoutSeconds *int32 `json:"startToCloseTimeoutSeconds,omitempty"` HeartbeatTimeoutSeconds *int32 `json:"heartbeatTimeoutSeconds,omitempty"` CancelRequested *bool `json:"cancelRequested,omitempty"` CancelRequestID *int64 `json:"cancelRequestID,omitempty"` TimerTaskStatus *int32 `json:"timerTaskStatus,omitempty"` Attempt *int32 `json:"attempt,omitempty"` TaskList *string `json:"taskList,omitempty"` TaskListKind *shared.TaskListKind `json:"taskListKind,omitempty"` StartedIdentity *string `json:"startedIdentity,omitempty"` HasRetryPolicy *bool `json:"hasRetryPolicy,omitempty"` RetryInitialIntervalSeconds *int32 `json:"retryInitialIntervalSeconds,omitempty"` RetryMaximumIntervalSeconds *int32 `json:"retryMaximumIntervalSeconds,omitempty"` RetryMaximumAttempts *int32 `json:"retryMaximumAttempts,omitempty"` RetryExpirationTimeNanos *int64 `json:"retryExpirationTimeNanos,omitempty"` RetryBackoffCoefficient *float64 `json:"retryBackoffCoefficient,omitempty"` RetryNonRetryableErrors []string `json:"retryNonRetryableErrors,omitempty"` RetryLastFailureReason *string `json:"retryLastFailureReason,omitempty"` RetryLastWorkerIdentity *string `json:"retryLastWorkerIdentity,omitempty"` RetryLastFailureDetails []byte `json:"retryLastFailureDetails,omitempty"` } type _List_String_ValueList []string func (v _List_String_ValueList) ForEach(f func(wire.Value) error) error { for _, x := range v { w, err := wire.NewValueString(x), error(nil) if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_String_ValueList) Size() int { return len(v) } func (_List_String_ValueList) ValueType() wire.Type { return wire.TBinary } func (_List_String_ValueList) Close() {} // ToWire translates a ActivityInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ActivityInfo) ToWire() (wire.Value, error) { var ( fields [32]wire.Field i int = 0 w wire.Value err error ) if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ScheduledEventBatchID != nil { w, err = wire.NewValueI64(*(v.ScheduledEventBatchID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.ScheduledEvent != nil { w, err = wire.NewValueBinary(v.ScheduledEvent), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.ScheduledEventEncoding != nil { w, err = wire.NewValueString(*(v.ScheduledEventEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.ScheduledTimeNanos != nil { w, err = wire.NewValueI64(*(v.ScheduledTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } if v.StartedID != nil { w, err = wire.NewValueI64(*(v.StartedID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.StartedEvent != nil { w, err = wire.NewValueBinary(v.StartedEvent), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 22, Value: w} i++ } if v.StartedEventEncoding != nil { w, err = wire.NewValueString(*(v.StartedEventEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 24, Value: w} i++ } if v.StartedTimeNanos != nil { w, err = wire.NewValueI64(*(v.StartedTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 26, Value: w} i++ } if v.ActivityID != nil { w, err = wire.NewValueString(*(v.ActivityID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 28, Value: w} i++ } if v.RequestID != nil { w, err = wire.NewValueString(*(v.RequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ScheduleToStartTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ScheduleToStartTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 32, Value: w} i++ } if v.ScheduleToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.ScheduleToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 34, Value: w} i++ } if v.StartToCloseTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.StartToCloseTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 36, Value: w} i++ } if v.HeartbeatTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.HeartbeatTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 38, Value: w} i++ } if v.CancelRequested != nil { w, err = wire.NewValueBool(*(v.CancelRequested)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.CancelRequestID != nil { w, err = wire.NewValueI64(*(v.CancelRequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 42, Value: w} i++ } if v.TimerTaskStatus != nil { w, err = wire.NewValueI32(*(v.TimerTaskStatus)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 44, Value: w} i++ } if v.Attempt != nil { w, err = wire.NewValueI32(*(v.Attempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 46, Value: w} i++ } if v.TaskList != nil { w, err = wire.NewValueString(*(v.TaskList)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 48, Value: w} i++ } if v.TaskListKind != nil { w, err = v.TaskListKind.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 49, Value: w} i++ } if v.StartedIdentity != nil { w, err = wire.NewValueString(*(v.StartedIdentity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.HasRetryPolicy != nil { w, err = wire.NewValueBool(*(v.HasRetryPolicy)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 52, Value: w} i++ } if v.RetryInitialIntervalSeconds != nil { w, err = wire.NewValueI32(*(v.RetryInitialIntervalSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 54, Value: w} i++ } if v.RetryMaximumIntervalSeconds != nil { w, err = wire.NewValueI32(*(v.RetryMaximumIntervalSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 56, Value: w} i++ } if v.RetryMaximumAttempts != nil { w, err = wire.NewValueI32(*(v.RetryMaximumAttempts)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 58, Value: w} i++ } if v.RetryExpirationTimeNanos != nil { w, err = wire.NewValueI64(*(v.RetryExpirationTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.RetryBackoffCoefficient != nil { w, err = wire.NewValueDouble(*(v.RetryBackoffCoefficient)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 62, Value: w} i++ } if v.RetryNonRetryableErrors != nil { w, err = wire.NewValueList(_List_String_ValueList(v.RetryNonRetryableErrors)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 64, Value: w} i++ } if v.RetryLastFailureReason != nil { w, err = wire.NewValueString(*(v.RetryLastFailureReason)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 66, Value: w} i++ } if v.RetryLastWorkerIdentity != nil { w, err = wire.NewValueString(*(v.RetryLastWorkerIdentity)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 68, Value: w} i++ } if v.RetryLastFailureDetails != nil { w, err = wire.NewValueBinary(v.RetryLastFailureDetails), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TaskListKind_Read(w wire.Value) (shared.TaskListKind, error) { var v shared.TaskListKind err := v.FromWire(w) return v, err } func _List_String_Read(l wire.ValueList) ([]string, error) { if l.ValueType() != wire.TBinary { return nil, nil } o := make([]string, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := x.GetString(), error(nil) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a ActivityInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ActivityInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ActivityInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ActivityInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledEventBatchID = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TBinary { v.ScheduledEvent, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 16: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ScheduledEventEncoding = &x if err != nil { return err } } case 18: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledTimeNanos = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedID = &x if err != nil { return err } } case 22: if field.Value.Type() == wire.TBinary { v.StartedEvent, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 24: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.StartedEventEncoding = &x if err != nil { return err } } case 26: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedTimeNanos = &x if err != nil { return err } } case 28: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActivityID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestID = &x if err != nil { return err } } case 32: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } } case 34: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ScheduleToCloseTimeoutSeconds = &x if err != nil { return err } } case 36: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.StartToCloseTimeoutSeconds = &x if err != nil { return err } } case 38: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.HeartbeatTimeoutSeconds = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.CancelRequested = &x if err != nil { return err } } case 42: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CancelRequestID = &x if err != nil { return err } } case 44: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.TimerTaskStatus = &x if err != nil { return err } } case 46: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Attempt = &x if err != nil { return err } } case 48: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TaskList = &x if err != nil { return err } } case 49: if field.Value.Type() == wire.TI32 { var x shared.TaskListKind x, err = _TaskListKind_Read(field.Value) v.TaskListKind = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.StartedIdentity = &x if err != nil { return err } } case 52: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.HasRetryPolicy = &x if err != nil { return err } } case 54: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.RetryInitialIntervalSeconds = &x if err != nil { return err } } case 56: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.RetryMaximumIntervalSeconds = &x if err != nil { return err } } case 58: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.RetryMaximumAttempts = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.RetryExpirationTimeNanos = &x if err != nil { return err } } case 62: if field.Value.Type() == wire.TDouble { var x float64 x, err = field.Value.GetDouble(), error(nil) v.RetryBackoffCoefficient = &x if err != nil { return err } } case 64: if field.Value.Type() == wire.TList { v.RetryNonRetryableErrors, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } } case 66: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RetryLastFailureReason = &x if err != nil { return err } } case 68: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RetryLastWorkerIdentity = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBinary { v.RetryLastFailureDetails, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } func _List_String_Encode(val []string, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TBinary, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for _, v := range val { if err := sw.WriteString(v); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a ActivityInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ActivityInfo struct could not be encoded. func (v *ActivityInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledEventBatchID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledEventBatchID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.ScheduledEvent); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledEventEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ScheduledEventEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 22, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.StartedEvent); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 24, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.StartedEventEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 26, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActivityID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 28, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActivityID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleToStartTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 32, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ScheduleToStartTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 34, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ScheduleToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartToCloseTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 36, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.StartToCloseTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HeartbeatTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 38, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.HeartbeatTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelRequested != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.CancelRequested)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelRequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 42, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CancelRequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimerTaskStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 44, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.TimerTaskStatus)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Attempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 46, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Attempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 48, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TaskList)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListKind != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 49, Type: wire.TI32}); err != nil { return err } if err := v.TaskListKind.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedIdentity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.StartedIdentity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HasRetryPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 52, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.HasRetryPolicy)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryInitialIntervalSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 54, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.RetryInitialIntervalSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryMaximumIntervalSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 56, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.RetryMaximumIntervalSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryMaximumAttempts != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 58, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.RetryMaximumAttempts)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryExpirationTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.RetryExpirationTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryBackoffCoefficient != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 62, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(*(v.RetryBackoffCoefficient)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryNonRetryableErrors != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 64, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.RetryNonRetryableErrors, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryLastFailureReason != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 66, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RetryLastFailureReason)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryLastWorkerIdentity != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 68, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RetryLastWorkerIdentity)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryLastFailureDetails != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.RetryLastFailureDetails); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TaskListKind_Decode(sr stream.Reader) (shared.TaskListKind, error) { var v shared.TaskListKind err := v.Decode(sr) return v, err } func _List_String_Decode(sr stream.Reader) ([]string, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TBinary { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]string, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := sr.ReadString() if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ActivityInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ActivityInfo struct could not be generated from the wire // representation. func (v *ActivityInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledEventBatchID = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TBinary: v.ScheduledEvent, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ScheduledEventEncoding = &x if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledTimeNanos = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedID = &x if err != nil { return err } case fh.ID == 22 && fh.Type == wire.TBinary: v.StartedEvent, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 24 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.StartedEventEncoding = &x if err != nil { return err } case fh.ID == 26 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedTimeNanos = &x if err != nil { return err } case fh.ID == 28 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActivityID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestID = &x if err != nil { return err } case fh.ID == 32 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ScheduleToStartTimeoutSeconds = &x if err != nil { return err } case fh.ID == 34 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ScheduleToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 36 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.StartToCloseTimeoutSeconds = &x if err != nil { return err } case fh.ID == 38 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.HeartbeatTimeoutSeconds = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.CancelRequested = &x if err != nil { return err } case fh.ID == 42 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CancelRequestID = &x if err != nil { return err } case fh.ID == 44 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.TimerTaskStatus = &x if err != nil { return err } case fh.ID == 46 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Attempt = &x if err != nil { return err } case fh.ID == 48 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TaskList = &x if err != nil { return err } case fh.ID == 49 && fh.Type == wire.TI32: var x shared.TaskListKind x, err = _TaskListKind_Decode(sr) v.TaskListKind = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.StartedIdentity = &x if err != nil { return err } case fh.ID == 52 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.HasRetryPolicy = &x if err != nil { return err } case fh.ID == 54 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.RetryInitialIntervalSeconds = &x if err != nil { return err } case fh.ID == 56 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.RetryMaximumIntervalSeconds = &x if err != nil { return err } case fh.ID == 58 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.RetryMaximumAttempts = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.RetryExpirationTimeNanos = &x if err != nil { return err } case fh.ID == 62 && fh.Type == wire.TDouble: var x float64 x, err = sr.ReadDouble() v.RetryBackoffCoefficient = &x if err != nil { return err } case fh.ID == 64 && fh.Type == wire.TList: v.RetryNonRetryableErrors, err = _List_String_Decode(sr) if err != nil { return err } case fh.ID == 66 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RetryLastFailureReason = &x if err != nil { return err } case fh.ID == 68 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RetryLastWorkerIdentity = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBinary: v.RetryLastFailureDetails, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ActivityInfo // struct. func (v *ActivityInfo) String() string { if v == nil { return "" } var fields [32]string i := 0 if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.ScheduledEventBatchID != nil { fields[i] = fmt.Sprintf("ScheduledEventBatchID: %v", *(v.ScheduledEventBatchID)) i++ } if v.ScheduledEvent != nil { fields[i] = fmt.Sprintf("ScheduledEvent: %v", v.ScheduledEvent) i++ } if v.ScheduledEventEncoding != nil { fields[i] = fmt.Sprintf("ScheduledEventEncoding: %v", *(v.ScheduledEventEncoding)) i++ } if v.ScheduledTimeNanos != nil { fields[i] = fmt.Sprintf("ScheduledTimeNanos: %v", *(v.ScheduledTimeNanos)) i++ } if v.StartedID != nil { fields[i] = fmt.Sprintf("StartedID: %v", *(v.StartedID)) i++ } if v.StartedEvent != nil { fields[i] = fmt.Sprintf("StartedEvent: %v", v.StartedEvent) i++ } if v.StartedEventEncoding != nil { fields[i] = fmt.Sprintf("StartedEventEncoding: %v", *(v.StartedEventEncoding)) i++ } if v.StartedTimeNanos != nil { fields[i] = fmt.Sprintf("StartedTimeNanos: %v", *(v.StartedTimeNanos)) i++ } if v.ActivityID != nil { fields[i] = fmt.Sprintf("ActivityID: %v", *(v.ActivityID)) i++ } if v.RequestID != nil { fields[i] = fmt.Sprintf("RequestID: %v", *(v.RequestID)) i++ } if v.ScheduleToStartTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ScheduleToStartTimeoutSeconds: %v", *(v.ScheduleToStartTimeoutSeconds)) i++ } if v.ScheduleToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("ScheduleToCloseTimeoutSeconds: %v", *(v.ScheduleToCloseTimeoutSeconds)) i++ } if v.StartToCloseTimeoutSeconds != nil { fields[i] = fmt.Sprintf("StartToCloseTimeoutSeconds: %v", *(v.StartToCloseTimeoutSeconds)) i++ } if v.HeartbeatTimeoutSeconds != nil { fields[i] = fmt.Sprintf("HeartbeatTimeoutSeconds: %v", *(v.HeartbeatTimeoutSeconds)) i++ } if v.CancelRequested != nil { fields[i] = fmt.Sprintf("CancelRequested: %v", *(v.CancelRequested)) i++ } if v.CancelRequestID != nil { fields[i] = fmt.Sprintf("CancelRequestID: %v", *(v.CancelRequestID)) i++ } if v.TimerTaskStatus != nil { fields[i] = fmt.Sprintf("TimerTaskStatus: %v", *(v.TimerTaskStatus)) i++ } if v.Attempt != nil { fields[i] = fmt.Sprintf("Attempt: %v", *(v.Attempt)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", *(v.TaskList)) i++ } if v.TaskListKind != nil { fields[i] = fmt.Sprintf("TaskListKind: %v", *(v.TaskListKind)) i++ } if v.StartedIdentity != nil { fields[i] = fmt.Sprintf("StartedIdentity: %v", *(v.StartedIdentity)) i++ } if v.HasRetryPolicy != nil { fields[i] = fmt.Sprintf("HasRetryPolicy: %v", *(v.HasRetryPolicy)) i++ } if v.RetryInitialIntervalSeconds != nil { fields[i] = fmt.Sprintf("RetryInitialIntervalSeconds: %v", *(v.RetryInitialIntervalSeconds)) i++ } if v.RetryMaximumIntervalSeconds != nil { fields[i] = fmt.Sprintf("RetryMaximumIntervalSeconds: %v", *(v.RetryMaximumIntervalSeconds)) i++ } if v.RetryMaximumAttempts != nil { fields[i] = fmt.Sprintf("RetryMaximumAttempts: %v", *(v.RetryMaximumAttempts)) i++ } if v.RetryExpirationTimeNanos != nil { fields[i] = fmt.Sprintf("RetryExpirationTimeNanos: %v", *(v.RetryExpirationTimeNanos)) i++ } if v.RetryBackoffCoefficient != nil { fields[i] = fmt.Sprintf("RetryBackoffCoefficient: %v", *(v.RetryBackoffCoefficient)) i++ } if v.RetryNonRetryableErrors != nil { fields[i] = fmt.Sprintf("RetryNonRetryableErrors: %v", v.RetryNonRetryableErrors) i++ } if v.RetryLastFailureReason != nil { fields[i] = fmt.Sprintf("RetryLastFailureReason: %v", *(v.RetryLastFailureReason)) i++ } if v.RetryLastWorkerIdentity != nil { fields[i] = fmt.Sprintf("RetryLastWorkerIdentity: %v", *(v.RetryLastWorkerIdentity)) i++ } if v.RetryLastFailureDetails != nil { fields[i] = fmt.Sprintf("RetryLastFailureDetails: %v", v.RetryLastFailureDetails) i++ } return fmt.Sprintf("ActivityInfo{%v}", strings.Join(fields[:i], ", ")) } func _I64_EqualsPtr(lhs, rhs *int64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _String_EqualsPtr(lhs, rhs *string) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _I32_EqualsPtr(lhs, rhs *int32) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _Bool_EqualsPtr(lhs, rhs *bool) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _TaskListKind_EqualsPtr(lhs, rhs *shared.TaskListKind) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } func _Double_EqualsPtr(lhs, rhs *float64) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _List_String_Equals(lhs, rhs []string) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !(lv == rv) { return false } } return true } // Equals returns true if all the fields of this ActivityInfo match the // provided ActivityInfo. // // This function performs a deep comparison. func (v *ActivityInfo) Equals(rhs *ActivityInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.ScheduledEventBatchID, rhs.ScheduledEventBatchID) { return false } if !((v.ScheduledEvent == nil && rhs.ScheduledEvent == nil) || (v.ScheduledEvent != nil && rhs.ScheduledEvent != nil && bytes.Equal(v.ScheduledEvent, rhs.ScheduledEvent))) { return false } if !_String_EqualsPtr(v.ScheduledEventEncoding, rhs.ScheduledEventEncoding) { return false } if !_I64_EqualsPtr(v.ScheduledTimeNanos, rhs.ScheduledTimeNanos) { return false } if !_I64_EqualsPtr(v.StartedID, rhs.StartedID) { return false } if !((v.StartedEvent == nil && rhs.StartedEvent == nil) || (v.StartedEvent != nil && rhs.StartedEvent != nil && bytes.Equal(v.StartedEvent, rhs.StartedEvent))) { return false } if !_String_EqualsPtr(v.StartedEventEncoding, rhs.StartedEventEncoding) { return false } if !_I64_EqualsPtr(v.StartedTimeNanos, rhs.StartedTimeNanos) { return false } if !_String_EqualsPtr(v.ActivityID, rhs.ActivityID) { return false } if !_String_EqualsPtr(v.RequestID, rhs.RequestID) { return false } if !_I32_EqualsPtr(v.ScheduleToStartTimeoutSeconds, rhs.ScheduleToStartTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.ScheduleToCloseTimeoutSeconds, rhs.ScheduleToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.StartToCloseTimeoutSeconds, rhs.StartToCloseTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.HeartbeatTimeoutSeconds, rhs.HeartbeatTimeoutSeconds) { return false } if !_Bool_EqualsPtr(v.CancelRequested, rhs.CancelRequested) { return false } if !_I64_EqualsPtr(v.CancelRequestID, rhs.CancelRequestID) { return false } if !_I32_EqualsPtr(v.TimerTaskStatus, rhs.TimerTaskStatus) { return false } if !_I32_EqualsPtr(v.Attempt, rhs.Attempt) { return false } if !_String_EqualsPtr(v.TaskList, rhs.TaskList) { return false } if !_TaskListKind_EqualsPtr(v.TaskListKind, rhs.TaskListKind) { return false } if !_String_EqualsPtr(v.StartedIdentity, rhs.StartedIdentity) { return false } if !_Bool_EqualsPtr(v.HasRetryPolicy, rhs.HasRetryPolicy) { return false } if !_I32_EqualsPtr(v.RetryInitialIntervalSeconds, rhs.RetryInitialIntervalSeconds) { return false } if !_I32_EqualsPtr(v.RetryMaximumIntervalSeconds, rhs.RetryMaximumIntervalSeconds) { return false } if !_I32_EqualsPtr(v.RetryMaximumAttempts, rhs.RetryMaximumAttempts) { return false } if !_I64_EqualsPtr(v.RetryExpirationTimeNanos, rhs.RetryExpirationTimeNanos) { return false } if !_Double_EqualsPtr(v.RetryBackoffCoefficient, rhs.RetryBackoffCoefficient) { return false } if !((v.RetryNonRetryableErrors == nil && rhs.RetryNonRetryableErrors == nil) || (v.RetryNonRetryableErrors != nil && rhs.RetryNonRetryableErrors != nil && _List_String_Equals(v.RetryNonRetryableErrors, rhs.RetryNonRetryableErrors))) { return false } if !_String_EqualsPtr(v.RetryLastFailureReason, rhs.RetryLastFailureReason) { return false } if !_String_EqualsPtr(v.RetryLastWorkerIdentity, rhs.RetryLastWorkerIdentity) { return false } if !((v.RetryLastFailureDetails == nil && rhs.RetryLastFailureDetails == nil) || (v.RetryLastFailureDetails != nil && rhs.RetryLastFailureDetails != nil && bytes.Equal(v.RetryLastFailureDetails, rhs.RetryLastFailureDetails))) { return false } return true } type _List_String_Zapper []string // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_String_Zapper. func (l _List_String_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { enc.AppendString(v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ActivityInfo. func (v *ActivityInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.ScheduledEventBatchID != nil { enc.AddInt64("scheduledEventBatchID", *v.ScheduledEventBatchID) } if v.ScheduledEvent != nil { enc.AddString("scheduledEvent", base64.StdEncoding.EncodeToString(v.ScheduledEvent)) } if v.ScheduledEventEncoding != nil { enc.AddString("scheduledEventEncoding", *v.ScheduledEventEncoding) } if v.ScheduledTimeNanos != nil { enc.AddInt64("scheduledTimeNanos", *v.ScheduledTimeNanos) } if v.StartedID != nil { enc.AddInt64("startedID", *v.StartedID) } if v.StartedEvent != nil { enc.AddString("startedEvent", base64.StdEncoding.EncodeToString(v.StartedEvent)) } if v.StartedEventEncoding != nil { enc.AddString("startedEventEncoding", *v.StartedEventEncoding) } if v.StartedTimeNanos != nil { enc.AddInt64("startedTimeNanos", *v.StartedTimeNanos) } if v.ActivityID != nil { enc.AddString("activityID", *v.ActivityID) } if v.RequestID != nil { enc.AddString("requestID", *v.RequestID) } if v.ScheduleToStartTimeoutSeconds != nil { enc.AddInt32("scheduleToStartTimeoutSeconds", *v.ScheduleToStartTimeoutSeconds) } if v.ScheduleToCloseTimeoutSeconds != nil { enc.AddInt32("scheduleToCloseTimeoutSeconds", *v.ScheduleToCloseTimeoutSeconds) } if v.StartToCloseTimeoutSeconds != nil { enc.AddInt32("startToCloseTimeoutSeconds", *v.StartToCloseTimeoutSeconds) } if v.HeartbeatTimeoutSeconds != nil { enc.AddInt32("heartbeatTimeoutSeconds", *v.HeartbeatTimeoutSeconds) } if v.CancelRequested != nil { enc.AddBool("cancelRequested", *v.CancelRequested) } if v.CancelRequestID != nil { enc.AddInt64("cancelRequestID", *v.CancelRequestID) } if v.TimerTaskStatus != nil { enc.AddInt32("timerTaskStatus", *v.TimerTaskStatus) } if v.Attempt != nil { enc.AddInt32("attempt", *v.Attempt) } if v.TaskList != nil { enc.AddString("taskList", *v.TaskList) } if v.TaskListKind != nil { err = multierr.Append(err, enc.AddObject("taskListKind", *v.TaskListKind)) } if v.StartedIdentity != nil { enc.AddString("startedIdentity", *v.StartedIdentity) } if v.HasRetryPolicy != nil { enc.AddBool("hasRetryPolicy", *v.HasRetryPolicy) } if v.RetryInitialIntervalSeconds != nil { enc.AddInt32("retryInitialIntervalSeconds", *v.RetryInitialIntervalSeconds) } if v.RetryMaximumIntervalSeconds != nil { enc.AddInt32("retryMaximumIntervalSeconds", *v.RetryMaximumIntervalSeconds) } if v.RetryMaximumAttempts != nil { enc.AddInt32("retryMaximumAttempts", *v.RetryMaximumAttempts) } if v.RetryExpirationTimeNanos != nil { enc.AddInt64("retryExpirationTimeNanos", *v.RetryExpirationTimeNanos) } if v.RetryBackoffCoefficient != nil { enc.AddFloat64("retryBackoffCoefficient", *v.RetryBackoffCoefficient) } if v.RetryNonRetryableErrors != nil { err = multierr.Append(err, enc.AddArray("retryNonRetryableErrors", (_List_String_Zapper)(v.RetryNonRetryableErrors))) } if v.RetryLastFailureReason != nil { enc.AddString("retryLastFailureReason", *v.RetryLastFailureReason) } if v.RetryLastWorkerIdentity != nil { enc.AddString("retryLastWorkerIdentity", *v.RetryLastWorkerIdentity) } if v.RetryLastFailureDetails != nil { enc.AddString("retryLastFailureDetails", base64.StdEncoding.EncodeToString(v.RetryLastFailureDetails)) } return err } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *ActivityInfo) IsSetVersion() bool { return v != nil && v.Version != nil } // GetScheduledEventBatchID returns the value of ScheduledEventBatchID if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetScheduledEventBatchID() (o int64) { if v != nil && v.ScheduledEventBatchID != nil { return *v.ScheduledEventBatchID } return } // IsSetScheduledEventBatchID returns true if ScheduledEventBatchID is not nil. func (v *ActivityInfo) IsSetScheduledEventBatchID() bool { return v != nil && v.ScheduledEventBatchID != nil } // GetScheduledEvent returns the value of ScheduledEvent if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetScheduledEvent() (o []byte) { if v != nil && v.ScheduledEvent != nil { return v.ScheduledEvent } return } // IsSetScheduledEvent returns true if ScheduledEvent is not nil. func (v *ActivityInfo) IsSetScheduledEvent() bool { return v != nil && v.ScheduledEvent != nil } // GetScheduledEventEncoding returns the value of ScheduledEventEncoding if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetScheduledEventEncoding() (o string) { if v != nil && v.ScheduledEventEncoding != nil { return *v.ScheduledEventEncoding } return } // IsSetScheduledEventEncoding returns true if ScheduledEventEncoding is not nil. func (v *ActivityInfo) IsSetScheduledEventEncoding() bool { return v != nil && v.ScheduledEventEncoding != nil } // GetScheduledTimeNanos returns the value of ScheduledTimeNanos if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetScheduledTimeNanos() (o int64) { if v != nil && v.ScheduledTimeNanos != nil { return *v.ScheduledTimeNanos } return } // IsSetScheduledTimeNanos returns true if ScheduledTimeNanos is not nil. func (v *ActivityInfo) IsSetScheduledTimeNanos() bool { return v != nil && v.ScheduledTimeNanos != nil } // GetStartedID returns the value of StartedID if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetStartedID() (o int64) { if v != nil && v.StartedID != nil { return *v.StartedID } return } // IsSetStartedID returns true if StartedID is not nil. func (v *ActivityInfo) IsSetStartedID() bool { return v != nil && v.StartedID != nil } // GetStartedEvent returns the value of StartedEvent if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetStartedEvent() (o []byte) { if v != nil && v.StartedEvent != nil { return v.StartedEvent } return } // IsSetStartedEvent returns true if StartedEvent is not nil. func (v *ActivityInfo) IsSetStartedEvent() bool { return v != nil && v.StartedEvent != nil } // GetStartedEventEncoding returns the value of StartedEventEncoding if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetStartedEventEncoding() (o string) { if v != nil && v.StartedEventEncoding != nil { return *v.StartedEventEncoding } return } // IsSetStartedEventEncoding returns true if StartedEventEncoding is not nil. func (v *ActivityInfo) IsSetStartedEventEncoding() bool { return v != nil && v.StartedEventEncoding != nil } // GetStartedTimeNanos returns the value of StartedTimeNanos if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetStartedTimeNanos() (o int64) { if v != nil && v.StartedTimeNanos != nil { return *v.StartedTimeNanos } return } // IsSetStartedTimeNanos returns true if StartedTimeNanos is not nil. func (v *ActivityInfo) IsSetStartedTimeNanos() bool { return v != nil && v.StartedTimeNanos != nil } // GetActivityID returns the value of ActivityID if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetActivityID() (o string) { if v != nil && v.ActivityID != nil { return *v.ActivityID } return } // IsSetActivityID returns true if ActivityID is not nil. func (v *ActivityInfo) IsSetActivityID() bool { return v != nil && v.ActivityID != nil } // GetRequestID returns the value of RequestID if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetRequestID() (o string) { if v != nil && v.RequestID != nil { return *v.RequestID } return } // IsSetRequestID returns true if RequestID is not nil. func (v *ActivityInfo) IsSetRequestID() bool { return v != nil && v.RequestID != nil } // GetScheduleToStartTimeoutSeconds returns the value of ScheduleToStartTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // IsSetScheduleToStartTimeoutSeconds returns true if ScheduleToStartTimeoutSeconds is not nil. func (v *ActivityInfo) IsSetScheduleToStartTimeoutSeconds() bool { return v != nil && v.ScheduleToStartTimeoutSeconds != nil } // GetScheduleToCloseTimeoutSeconds returns the value of ScheduleToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetScheduleToCloseTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToCloseTimeoutSeconds != nil { return *v.ScheduleToCloseTimeoutSeconds } return } // IsSetScheduleToCloseTimeoutSeconds returns true if ScheduleToCloseTimeoutSeconds is not nil. func (v *ActivityInfo) IsSetScheduleToCloseTimeoutSeconds() bool { return v != nil && v.ScheduleToCloseTimeoutSeconds != nil } // GetStartToCloseTimeoutSeconds returns the value of StartToCloseTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.StartToCloseTimeoutSeconds != nil { return *v.StartToCloseTimeoutSeconds } return } // IsSetStartToCloseTimeoutSeconds returns true if StartToCloseTimeoutSeconds is not nil. func (v *ActivityInfo) IsSetStartToCloseTimeoutSeconds() bool { return v != nil && v.StartToCloseTimeoutSeconds != nil } // GetHeartbeatTimeoutSeconds returns the value of HeartbeatTimeoutSeconds if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetHeartbeatTimeoutSeconds() (o int32) { if v != nil && v.HeartbeatTimeoutSeconds != nil { return *v.HeartbeatTimeoutSeconds } return } // IsSetHeartbeatTimeoutSeconds returns true if HeartbeatTimeoutSeconds is not nil. func (v *ActivityInfo) IsSetHeartbeatTimeoutSeconds() bool { return v != nil && v.HeartbeatTimeoutSeconds != nil } // GetCancelRequested returns the value of CancelRequested if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetCancelRequested() (o bool) { if v != nil && v.CancelRequested != nil { return *v.CancelRequested } return } // IsSetCancelRequested returns true if CancelRequested is not nil. func (v *ActivityInfo) IsSetCancelRequested() bool { return v != nil && v.CancelRequested != nil } // GetCancelRequestID returns the value of CancelRequestID if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetCancelRequestID() (o int64) { if v != nil && v.CancelRequestID != nil { return *v.CancelRequestID } return } // IsSetCancelRequestID returns true if CancelRequestID is not nil. func (v *ActivityInfo) IsSetCancelRequestID() bool { return v != nil && v.CancelRequestID != nil } // GetTimerTaskStatus returns the value of TimerTaskStatus if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetTimerTaskStatus() (o int32) { if v != nil && v.TimerTaskStatus != nil { return *v.TimerTaskStatus } return } // IsSetTimerTaskStatus returns true if TimerTaskStatus is not nil. func (v *ActivityInfo) IsSetTimerTaskStatus() bool { return v != nil && v.TimerTaskStatus != nil } // GetAttempt returns the value of Attempt if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetAttempt() (o int32) { if v != nil && v.Attempt != nil { return *v.Attempt } return } // IsSetAttempt returns true if Attempt is not nil. func (v *ActivityInfo) IsSetAttempt() bool { return v != nil && v.Attempt != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetTaskList() (o string) { if v != nil && v.TaskList != nil { return *v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *ActivityInfo) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetTaskListKind returns the value of TaskListKind if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetTaskListKind() (o shared.TaskListKind) { if v != nil && v.TaskListKind != nil { return *v.TaskListKind } return } // IsSetTaskListKind returns true if TaskListKind is not nil. func (v *ActivityInfo) IsSetTaskListKind() bool { return v != nil && v.TaskListKind != nil } // GetStartedIdentity returns the value of StartedIdentity if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetStartedIdentity() (o string) { if v != nil && v.StartedIdentity != nil { return *v.StartedIdentity } return } // IsSetStartedIdentity returns true if StartedIdentity is not nil. func (v *ActivityInfo) IsSetStartedIdentity() bool { return v != nil && v.StartedIdentity != nil } // GetHasRetryPolicy returns the value of HasRetryPolicy if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetHasRetryPolicy() (o bool) { if v != nil && v.HasRetryPolicy != nil { return *v.HasRetryPolicy } return } // IsSetHasRetryPolicy returns true if HasRetryPolicy is not nil. func (v *ActivityInfo) IsSetHasRetryPolicy() bool { return v != nil && v.HasRetryPolicy != nil } // GetRetryInitialIntervalSeconds returns the value of RetryInitialIntervalSeconds if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetRetryInitialIntervalSeconds() (o int32) { if v != nil && v.RetryInitialIntervalSeconds != nil { return *v.RetryInitialIntervalSeconds } return } // IsSetRetryInitialIntervalSeconds returns true if RetryInitialIntervalSeconds is not nil. func (v *ActivityInfo) IsSetRetryInitialIntervalSeconds() bool { return v != nil && v.RetryInitialIntervalSeconds != nil } // GetRetryMaximumIntervalSeconds returns the value of RetryMaximumIntervalSeconds if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetRetryMaximumIntervalSeconds() (o int32) { if v != nil && v.RetryMaximumIntervalSeconds != nil { return *v.RetryMaximumIntervalSeconds } return } // IsSetRetryMaximumIntervalSeconds returns true if RetryMaximumIntervalSeconds is not nil. func (v *ActivityInfo) IsSetRetryMaximumIntervalSeconds() bool { return v != nil && v.RetryMaximumIntervalSeconds != nil } // GetRetryMaximumAttempts returns the value of RetryMaximumAttempts if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetRetryMaximumAttempts() (o int32) { if v != nil && v.RetryMaximumAttempts != nil { return *v.RetryMaximumAttempts } return } // IsSetRetryMaximumAttempts returns true if RetryMaximumAttempts is not nil. func (v *ActivityInfo) IsSetRetryMaximumAttempts() bool { return v != nil && v.RetryMaximumAttempts != nil } // GetRetryExpirationTimeNanos returns the value of RetryExpirationTimeNanos if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetRetryExpirationTimeNanos() (o int64) { if v != nil && v.RetryExpirationTimeNanos != nil { return *v.RetryExpirationTimeNanos } return } // IsSetRetryExpirationTimeNanos returns true if RetryExpirationTimeNanos is not nil. func (v *ActivityInfo) IsSetRetryExpirationTimeNanos() bool { return v != nil && v.RetryExpirationTimeNanos != nil } // GetRetryBackoffCoefficient returns the value of RetryBackoffCoefficient if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetRetryBackoffCoefficient() (o float64) { if v != nil && v.RetryBackoffCoefficient != nil { return *v.RetryBackoffCoefficient } return } // IsSetRetryBackoffCoefficient returns true if RetryBackoffCoefficient is not nil. func (v *ActivityInfo) IsSetRetryBackoffCoefficient() bool { return v != nil && v.RetryBackoffCoefficient != nil } // GetRetryNonRetryableErrors returns the value of RetryNonRetryableErrors if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetRetryNonRetryableErrors() (o []string) { if v != nil && v.RetryNonRetryableErrors != nil { return v.RetryNonRetryableErrors } return } // IsSetRetryNonRetryableErrors returns true if RetryNonRetryableErrors is not nil. func (v *ActivityInfo) IsSetRetryNonRetryableErrors() bool { return v != nil && v.RetryNonRetryableErrors != nil } // GetRetryLastFailureReason returns the value of RetryLastFailureReason if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetRetryLastFailureReason() (o string) { if v != nil && v.RetryLastFailureReason != nil { return *v.RetryLastFailureReason } return } // IsSetRetryLastFailureReason returns true if RetryLastFailureReason is not nil. func (v *ActivityInfo) IsSetRetryLastFailureReason() bool { return v != nil && v.RetryLastFailureReason != nil } // GetRetryLastWorkerIdentity returns the value of RetryLastWorkerIdentity if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetRetryLastWorkerIdentity() (o string) { if v != nil && v.RetryLastWorkerIdentity != nil { return *v.RetryLastWorkerIdentity } return } // IsSetRetryLastWorkerIdentity returns true if RetryLastWorkerIdentity is not nil. func (v *ActivityInfo) IsSetRetryLastWorkerIdentity() bool { return v != nil && v.RetryLastWorkerIdentity != nil } // GetRetryLastFailureDetails returns the value of RetryLastFailureDetails if it is set or its // zero value if it is unset. func (v *ActivityInfo) GetRetryLastFailureDetails() (o []byte) { if v != nil && v.RetryLastFailureDetails != nil { return v.RetryLastFailureDetails } return } // IsSetRetryLastFailureDetails returns true if RetryLastFailureDetails is not nil. func (v *ActivityInfo) IsSetRetryLastFailureDetails() bool { return v != nil && v.RetryLastFailureDetails != nil } type AsyncRequestMessage struct { PartitionKey *string `json:"partitionKey,omitempty"` Type *AsyncRequestType `json:"type,omitempty"` Header *shared.Header `json:"header,omitempty"` Encoding *string `json:"encoding,omitempty"` Payload []byte `json:"payload,omitempty"` } // ToWire translates a AsyncRequestMessage struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *AsyncRequestMessage) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.PartitionKey != nil { w, err = wire.NewValueString(*(v.PartitionKey)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Type != nil { w, err = v.Type.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.Header != nil { w, err = v.Header.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.Encoding != nil { w, err = wire.NewValueString(*(v.Encoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.Payload != nil { w, err = wire.NewValueBinary(v.Payload), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _AsyncRequestType_Read(w wire.Value) (AsyncRequestType, error) { var v AsyncRequestType err := v.FromWire(w) return v, err } func _Header_Read(w wire.Value) (*shared.Header, error) { var v shared.Header err := v.FromWire(w) return &v, err } // FromWire deserializes a AsyncRequestMessage struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a AsyncRequestMessage struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v AsyncRequestMessage // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *AsyncRequestMessage) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.PartitionKey = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TI32 { var x AsyncRequestType x, err = _AsyncRequestType_Read(field.Value) v.Type = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TStruct { v.Header, err = _Header_Read(field.Value) if err != nil { return err } } case 16: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Encoding = &x if err != nil { return err } } case 18: if field.Value.Type() == wire.TBinary { v.Payload, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a AsyncRequestMessage struct directly into bytes, without going // through an intermediary type. // // An error is returned if a AsyncRequestMessage struct could not be encoded. func (v *AsyncRequestMessage) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.PartitionKey != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.PartitionKey)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Type != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TI32}); err != nil { return err } if err := v.Type.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Header != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TStruct}); err != nil { return err } if err := v.Header.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Encoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Encoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Payload != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Payload); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _AsyncRequestType_Decode(sr stream.Reader) (AsyncRequestType, error) { var v AsyncRequestType err := v.Decode(sr) return v, err } func _Header_Decode(sr stream.Reader) (*shared.Header, error) { var v shared.Header err := v.Decode(sr) return &v, err } // Decode deserializes a AsyncRequestMessage struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a AsyncRequestMessage struct could not be generated from the wire // representation. func (v *AsyncRequestMessage) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.PartitionKey = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TI32: var x AsyncRequestType x, err = _AsyncRequestType_Decode(sr) v.Type = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TStruct: v.Header, err = _Header_Decode(sr) if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Encoding = &x if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TBinary: v.Payload, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a AsyncRequestMessage // struct. func (v *AsyncRequestMessage) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.PartitionKey != nil { fields[i] = fmt.Sprintf("PartitionKey: %v", *(v.PartitionKey)) i++ } if v.Type != nil { fields[i] = fmt.Sprintf("Type: %v", *(v.Type)) i++ } if v.Header != nil { fields[i] = fmt.Sprintf("Header: %v", v.Header) i++ } if v.Encoding != nil { fields[i] = fmt.Sprintf("Encoding: %v", *(v.Encoding)) i++ } if v.Payload != nil { fields[i] = fmt.Sprintf("Payload: %v", v.Payload) i++ } return fmt.Sprintf("AsyncRequestMessage{%v}", strings.Join(fields[:i], ", ")) } func _AsyncRequestType_EqualsPtr(lhs, rhs *AsyncRequestType) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this AsyncRequestMessage match the // provided AsyncRequestMessage. // // This function performs a deep comparison. func (v *AsyncRequestMessage) Equals(rhs *AsyncRequestMessage) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.PartitionKey, rhs.PartitionKey) { return false } if !_AsyncRequestType_EqualsPtr(v.Type, rhs.Type) { return false } if !((v.Header == nil && rhs.Header == nil) || (v.Header != nil && rhs.Header != nil && v.Header.Equals(rhs.Header))) { return false } if !_String_EqualsPtr(v.Encoding, rhs.Encoding) { return false } if !((v.Payload == nil && rhs.Payload == nil) || (v.Payload != nil && rhs.Payload != nil && bytes.Equal(v.Payload, rhs.Payload))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AsyncRequestMessage. func (v *AsyncRequestMessage) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.PartitionKey != nil { enc.AddString("partitionKey", *v.PartitionKey) } if v.Type != nil { err = multierr.Append(err, enc.AddObject("type", *v.Type)) } if v.Header != nil { err = multierr.Append(err, enc.AddObject("header", v.Header)) } if v.Encoding != nil { enc.AddString("encoding", *v.Encoding) } if v.Payload != nil { enc.AddString("payload", base64.StdEncoding.EncodeToString(v.Payload)) } return err } // GetPartitionKey returns the value of PartitionKey if it is set or its // zero value if it is unset. func (v *AsyncRequestMessage) GetPartitionKey() (o string) { if v != nil && v.PartitionKey != nil { return *v.PartitionKey } return } // IsSetPartitionKey returns true if PartitionKey is not nil. func (v *AsyncRequestMessage) IsSetPartitionKey() bool { return v != nil && v.PartitionKey != nil } // GetType returns the value of Type if it is set or its // zero value if it is unset. func (v *AsyncRequestMessage) GetType() (o AsyncRequestType) { if v != nil && v.Type != nil { return *v.Type } return } // IsSetType returns true if Type is not nil. func (v *AsyncRequestMessage) IsSetType() bool { return v != nil && v.Type != nil } // GetHeader returns the value of Header if it is set or its // zero value if it is unset. func (v *AsyncRequestMessage) GetHeader() (o *shared.Header) { if v != nil && v.Header != nil { return v.Header } return } // IsSetHeader returns true if Header is not nil. func (v *AsyncRequestMessage) IsSetHeader() bool { return v != nil && v.Header != nil } // GetEncoding returns the value of Encoding if it is set or its // zero value if it is unset. func (v *AsyncRequestMessage) GetEncoding() (o string) { if v != nil && v.Encoding != nil { return *v.Encoding } return } // IsSetEncoding returns true if Encoding is not nil. func (v *AsyncRequestMessage) IsSetEncoding() bool { return v != nil && v.Encoding != nil } // GetPayload returns the value of Payload if it is set or its // zero value if it is unset. func (v *AsyncRequestMessage) GetPayload() (o []byte) { if v != nil && v.Payload != nil { return v.Payload } return } // IsSetPayload returns true if Payload is not nil. func (v *AsyncRequestMessage) IsSetPayload() bool { return v != nil && v.Payload != nil } type AsyncRequestType int32 const ( AsyncRequestTypeStartWorkflowExecutionAsyncRequest AsyncRequestType = 0 AsyncRequestTypeSignalWithStartWorkflowExecutionAsyncRequest AsyncRequestType = 1 ) // AsyncRequestType_Values returns all recognized values of AsyncRequestType. func AsyncRequestType_Values() []AsyncRequestType { return []AsyncRequestType{ AsyncRequestTypeStartWorkflowExecutionAsyncRequest, AsyncRequestTypeSignalWithStartWorkflowExecutionAsyncRequest, } } // UnmarshalText tries to decode AsyncRequestType from a byte slice // containing its name. // // var v AsyncRequestType // err := v.UnmarshalText([]byte("StartWorkflowExecutionAsyncRequest")) func (v *AsyncRequestType) UnmarshalText(value []byte) error { switch s := string(value); s { case "StartWorkflowExecutionAsyncRequest": *v = AsyncRequestTypeStartWorkflowExecutionAsyncRequest return nil case "SignalWithStartWorkflowExecutionAsyncRequest": *v = AsyncRequestTypeSignalWithStartWorkflowExecutionAsyncRequest return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "AsyncRequestType", err) } *v = AsyncRequestType(val) return nil } } // MarshalText encodes AsyncRequestType to text. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements the TextMarshaler interface. func (v AsyncRequestType) MarshalText() ([]byte, error) { switch int32(v) { case 0: return []byte("StartWorkflowExecutionAsyncRequest"), nil case 1: return []byte("SignalWithStartWorkflowExecutionAsyncRequest"), nil } return []byte(strconv.FormatInt(int64(v), 10)), nil } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of AsyncRequestType. // Enums are logged as objects, where the value is logged with key "value", and // if this value's name is known, the name is logged with key "name". func (v AsyncRequestType) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt32("value", int32(v)) switch int32(v) { case 0: enc.AddString("name", "StartWorkflowExecutionAsyncRequest") case 1: enc.AddString("name", "SignalWithStartWorkflowExecutionAsyncRequest") } return nil } // Ptr returns a pointer to this enum value. func (v AsyncRequestType) Ptr() *AsyncRequestType { return &v } // Encode encodes AsyncRequestType directly to bytes. // // sWriter := BinaryStreamer.Writer(writer) // // var v AsyncRequestType // return v.Encode(sWriter) func (v AsyncRequestType) Encode(sw stream.Writer) error { return sw.WriteInt32(int32(v)) } // ToWire translates AsyncRequestType into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // Enums are represented as 32-bit integers over the wire. func (v AsyncRequestType) ToWire() (wire.Value, error) { return wire.NewValueI32(int32(v)), nil } // FromWire deserializes AsyncRequestType from its Thrift-level // representation. // // x, err := binaryProtocol.Decode(reader, wire.TI32) // if err != nil { // return AsyncRequestType(0), err // } // // var v AsyncRequestType // if err := v.FromWire(x); err != nil { // return AsyncRequestType(0), err // } // return v, nil func (v *AsyncRequestType) FromWire(w wire.Value) error { *v = (AsyncRequestType)(w.GetI32()) return nil } // Decode reads off the encoded AsyncRequestType directly off of the wire. // // sReader := BinaryStreamer.Reader(reader) // // var v AsyncRequestType // if err := v.Decode(sReader); err != nil { // return AsyncRequestType(0), err // } // return v, nil func (v *AsyncRequestType) Decode(sr stream.Reader) error { i, err := sr.ReadInt32() if err != nil { return err } *v = (AsyncRequestType)(i) return nil } // String returns a readable string representation of AsyncRequestType. func (v AsyncRequestType) String() string { w := int32(v) switch w { case 0: return "StartWorkflowExecutionAsyncRequest" case 1: return "SignalWithStartWorkflowExecutionAsyncRequest" } return fmt.Sprintf("AsyncRequestType(%d)", w) } // Equals returns true if this AsyncRequestType value matches the provided // value. func (v AsyncRequestType) Equals(rhs AsyncRequestType) bool { return v == rhs } // MarshalJSON serializes AsyncRequestType into JSON. // // If the enum value is recognized, its name is returned. // Otherwise, its integer value is returned. // // This implements json.Marshaler. func (v AsyncRequestType) MarshalJSON() ([]byte, error) { switch int32(v) { case 0: return ([]byte)("\"StartWorkflowExecutionAsyncRequest\""), nil case 1: return ([]byte)("\"SignalWithStartWorkflowExecutionAsyncRequest\""), nil } return ([]byte)(strconv.FormatInt(int64(v), 10)), nil } // UnmarshalJSON attempts to decode AsyncRequestType from its JSON // representation. // // This implementation supports both, numeric and string inputs. If a // string is provided, it must be a known enum name. // // This implements json.Unmarshaler. func (v *AsyncRequestType) UnmarshalJSON(text []byte) error { d := json.NewDecoder(bytes.NewReader(text)) d.UseNumber() t, err := d.Token() if err != nil { return err } switch w := t.(type) { case json.Number: x, err := w.Int64() if err != nil { return err } if x > math.MaxInt32 { return fmt.Errorf("enum overflow from JSON %q for %q", text, "AsyncRequestType") } if x < math.MinInt32 { return fmt.Errorf("enum underflow from JSON %q for %q", text, "AsyncRequestType") } *v = (AsyncRequestType)(x) return nil case string: return v.UnmarshalText([]byte(w)) default: return fmt.Errorf("invalid JSON value %q (%T) to unmarshal into %q", t, t, "AsyncRequestType") } } type ChildExecutionInfo struct { Version *int64 `json:"version,omitempty"` InitiatedEventBatchID *int64 `json:"initiatedEventBatchID,omitempty"` StartedID *int64 `json:"startedID,omitempty"` InitiatedEvent []byte `json:"initiatedEvent,omitempty"` InitiatedEventEncoding *string `json:"initiatedEventEncoding,omitempty"` StartedWorkflowID *string `json:"startedWorkflowID,omitempty"` StartedRunID []byte `json:"startedRunID,omitempty"` StartedEvent []byte `json:"startedEvent,omitempty"` StartedEventEncoding *string `json:"startedEventEncoding,omitempty"` CreateRequestID *string `json:"createRequestID,omitempty"` DomainID *string `json:"domainID,omitempty"` DomainName *string `json:"domainName,omitempty"` WorkflowTypeName *string `json:"workflowTypeName,omitempty"` ParentClosePolicy *int32 `json:"parentClosePolicy,omitempty"` } // ToWire translates a ChildExecutionInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ChildExecutionInfo) ToWire() (wire.Value, error) { var ( fields [14]wire.Field i int = 0 w wire.Value err error ) if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.InitiatedEventBatchID != nil { w, err = wire.NewValueI64(*(v.InitiatedEventBatchID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.StartedID != nil { w, err = wire.NewValueI64(*(v.StartedID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.InitiatedEvent != nil { w, err = wire.NewValueBinary(v.InitiatedEvent), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.InitiatedEventEncoding != nil { w, err = wire.NewValueString(*(v.InitiatedEventEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } if v.StartedWorkflowID != nil { w, err = wire.NewValueString(*(v.StartedWorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.StartedRunID != nil { w, err = wire.NewValueBinary(v.StartedRunID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 22, Value: w} i++ } if v.StartedEvent != nil { w, err = wire.NewValueBinary(v.StartedEvent), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 24, Value: w} i++ } if v.StartedEventEncoding != nil { w, err = wire.NewValueString(*(v.StartedEventEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 26, Value: w} i++ } if v.CreateRequestID != nil { w, err = wire.NewValueString(*(v.CreateRequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 28, Value: w} i++ } if v.DomainID != nil { w, err = wire.NewValueString(*(v.DomainID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 29, Value: w} i++ } if v.DomainName != nil { w, err = wire.NewValueString(*(v.DomainName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.WorkflowTypeName != nil { w, err = wire.NewValueString(*(v.WorkflowTypeName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 32, Value: w} i++ } if v.ParentClosePolicy != nil { w, err = wire.NewValueI32(*(v.ParentClosePolicy)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 35, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ChildExecutionInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ChildExecutionInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ChildExecutionInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ChildExecutionInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventBatchID = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedID = &x if err != nil { return err } } case 16: if field.Value.Type() == wire.TBinary { v.InitiatedEvent, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 18: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.InitiatedEventEncoding = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.StartedWorkflowID = &x if err != nil { return err } } case 22: if field.Value.Type() == wire.TBinary { v.StartedRunID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 24: if field.Value.Type() == wire.TBinary { v.StartedEvent, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 26: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.StartedEventEncoding = &x if err != nil { return err } } case 28: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CreateRequestID = &x if err != nil { return err } } case 29: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DomainName = &x if err != nil { return err } } case 32: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowTypeName = &x if err != nil { return err } } case 35: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.ParentClosePolicy = &x if err != nil { return err } } } } return nil } // Encode serializes a ChildExecutionInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ChildExecutionInfo struct could not be encoded. func (v *ChildExecutionInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventBatchID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventBatchID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.InitiatedEvent); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.InitiatedEventEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedWorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.StartedWorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 22, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.StartedRunID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 24, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.StartedEvent); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedEventEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 26, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.StartedEventEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CreateRequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 28, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CreateRequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 29, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DomainName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowTypeName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 32, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowTypeName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentClosePolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 35, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.ParentClosePolicy)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ChildExecutionInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ChildExecutionInfo struct could not be generated from the wire // representation. func (v *ChildExecutionInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventBatchID = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedID = &x if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TBinary: v.InitiatedEvent, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.InitiatedEventEncoding = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.StartedWorkflowID = &x if err != nil { return err } case fh.ID == 22 && fh.Type == wire.TBinary: v.StartedRunID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 24 && fh.Type == wire.TBinary: v.StartedEvent, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 26 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.StartedEventEncoding = &x if err != nil { return err } case fh.ID == 28 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CreateRequestID = &x if err != nil { return err } case fh.ID == 29 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DomainName = &x if err != nil { return err } case fh.ID == 32 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowTypeName = &x if err != nil { return err } case fh.ID == 35 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.ParentClosePolicy = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ChildExecutionInfo // struct. func (v *ChildExecutionInfo) String() string { if v == nil { return "" } var fields [14]string i := 0 if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.InitiatedEventBatchID != nil { fields[i] = fmt.Sprintf("InitiatedEventBatchID: %v", *(v.InitiatedEventBatchID)) i++ } if v.StartedID != nil { fields[i] = fmt.Sprintf("StartedID: %v", *(v.StartedID)) i++ } if v.InitiatedEvent != nil { fields[i] = fmt.Sprintf("InitiatedEvent: %v", v.InitiatedEvent) i++ } if v.InitiatedEventEncoding != nil { fields[i] = fmt.Sprintf("InitiatedEventEncoding: %v", *(v.InitiatedEventEncoding)) i++ } if v.StartedWorkflowID != nil { fields[i] = fmt.Sprintf("StartedWorkflowID: %v", *(v.StartedWorkflowID)) i++ } if v.StartedRunID != nil { fields[i] = fmt.Sprintf("StartedRunID: %v", v.StartedRunID) i++ } if v.StartedEvent != nil { fields[i] = fmt.Sprintf("StartedEvent: %v", v.StartedEvent) i++ } if v.StartedEventEncoding != nil { fields[i] = fmt.Sprintf("StartedEventEncoding: %v", *(v.StartedEventEncoding)) i++ } if v.CreateRequestID != nil { fields[i] = fmt.Sprintf("CreateRequestID: %v", *(v.CreateRequestID)) i++ } if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", *(v.DomainID)) i++ } if v.DomainName != nil { fields[i] = fmt.Sprintf("DomainName: %v", *(v.DomainName)) i++ } if v.WorkflowTypeName != nil { fields[i] = fmt.Sprintf("WorkflowTypeName: %v", *(v.WorkflowTypeName)) i++ } if v.ParentClosePolicy != nil { fields[i] = fmt.Sprintf("ParentClosePolicy: %v", *(v.ParentClosePolicy)) i++ } return fmt.Sprintf("ChildExecutionInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ChildExecutionInfo match the // provided ChildExecutionInfo. // // This function performs a deep comparison. func (v *ChildExecutionInfo) Equals(rhs *ChildExecutionInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.InitiatedEventBatchID, rhs.InitiatedEventBatchID) { return false } if !_I64_EqualsPtr(v.StartedID, rhs.StartedID) { return false } if !((v.InitiatedEvent == nil && rhs.InitiatedEvent == nil) || (v.InitiatedEvent != nil && rhs.InitiatedEvent != nil && bytes.Equal(v.InitiatedEvent, rhs.InitiatedEvent))) { return false } if !_String_EqualsPtr(v.InitiatedEventEncoding, rhs.InitiatedEventEncoding) { return false } if !_String_EqualsPtr(v.StartedWorkflowID, rhs.StartedWorkflowID) { return false } if !((v.StartedRunID == nil && rhs.StartedRunID == nil) || (v.StartedRunID != nil && rhs.StartedRunID != nil && bytes.Equal(v.StartedRunID, rhs.StartedRunID))) { return false } if !((v.StartedEvent == nil && rhs.StartedEvent == nil) || (v.StartedEvent != nil && rhs.StartedEvent != nil && bytes.Equal(v.StartedEvent, rhs.StartedEvent))) { return false } if !_String_EqualsPtr(v.StartedEventEncoding, rhs.StartedEventEncoding) { return false } if !_String_EqualsPtr(v.CreateRequestID, rhs.CreateRequestID) { return false } if !_String_EqualsPtr(v.DomainID, rhs.DomainID) { return false } if !_String_EqualsPtr(v.DomainName, rhs.DomainName) { return false } if !_String_EqualsPtr(v.WorkflowTypeName, rhs.WorkflowTypeName) { return false } if !_I32_EqualsPtr(v.ParentClosePolicy, rhs.ParentClosePolicy) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ChildExecutionInfo. func (v *ChildExecutionInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.InitiatedEventBatchID != nil { enc.AddInt64("initiatedEventBatchID", *v.InitiatedEventBatchID) } if v.StartedID != nil { enc.AddInt64("startedID", *v.StartedID) } if v.InitiatedEvent != nil { enc.AddString("initiatedEvent", base64.StdEncoding.EncodeToString(v.InitiatedEvent)) } if v.InitiatedEventEncoding != nil { enc.AddString("initiatedEventEncoding", *v.InitiatedEventEncoding) } if v.StartedWorkflowID != nil { enc.AddString("startedWorkflowID", *v.StartedWorkflowID) } if v.StartedRunID != nil { enc.AddString("startedRunID", base64.StdEncoding.EncodeToString(v.StartedRunID)) } if v.StartedEvent != nil { enc.AddString("startedEvent", base64.StdEncoding.EncodeToString(v.StartedEvent)) } if v.StartedEventEncoding != nil { enc.AddString("startedEventEncoding", *v.StartedEventEncoding) } if v.CreateRequestID != nil { enc.AddString("createRequestID", *v.CreateRequestID) } if v.DomainID != nil { enc.AddString("domainID", *v.DomainID) } if v.DomainName != nil { enc.AddString("domainName", *v.DomainName) } if v.WorkflowTypeName != nil { enc.AddString("workflowTypeName", *v.WorkflowTypeName) } if v.ParentClosePolicy != nil { enc.AddInt32("parentClosePolicy", *v.ParentClosePolicy) } return err } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *ChildExecutionInfo) IsSetVersion() bool { return v != nil && v.Version != nil } // GetInitiatedEventBatchID returns the value of InitiatedEventBatchID if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetInitiatedEventBatchID() (o int64) { if v != nil && v.InitiatedEventBatchID != nil { return *v.InitiatedEventBatchID } return } // IsSetInitiatedEventBatchID returns true if InitiatedEventBatchID is not nil. func (v *ChildExecutionInfo) IsSetInitiatedEventBatchID() bool { return v != nil && v.InitiatedEventBatchID != nil } // GetStartedID returns the value of StartedID if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetStartedID() (o int64) { if v != nil && v.StartedID != nil { return *v.StartedID } return } // IsSetStartedID returns true if StartedID is not nil. func (v *ChildExecutionInfo) IsSetStartedID() bool { return v != nil && v.StartedID != nil } // GetInitiatedEvent returns the value of InitiatedEvent if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetInitiatedEvent() (o []byte) { if v != nil && v.InitiatedEvent != nil { return v.InitiatedEvent } return } // IsSetInitiatedEvent returns true if InitiatedEvent is not nil. func (v *ChildExecutionInfo) IsSetInitiatedEvent() bool { return v != nil && v.InitiatedEvent != nil } // GetInitiatedEventEncoding returns the value of InitiatedEventEncoding if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetInitiatedEventEncoding() (o string) { if v != nil && v.InitiatedEventEncoding != nil { return *v.InitiatedEventEncoding } return } // IsSetInitiatedEventEncoding returns true if InitiatedEventEncoding is not nil. func (v *ChildExecutionInfo) IsSetInitiatedEventEncoding() bool { return v != nil && v.InitiatedEventEncoding != nil } // GetStartedWorkflowID returns the value of StartedWorkflowID if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetStartedWorkflowID() (o string) { if v != nil && v.StartedWorkflowID != nil { return *v.StartedWorkflowID } return } // IsSetStartedWorkflowID returns true if StartedWorkflowID is not nil. func (v *ChildExecutionInfo) IsSetStartedWorkflowID() bool { return v != nil && v.StartedWorkflowID != nil } // GetStartedRunID returns the value of StartedRunID if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetStartedRunID() (o []byte) { if v != nil && v.StartedRunID != nil { return v.StartedRunID } return } // IsSetStartedRunID returns true if StartedRunID is not nil. func (v *ChildExecutionInfo) IsSetStartedRunID() bool { return v != nil && v.StartedRunID != nil } // GetStartedEvent returns the value of StartedEvent if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetStartedEvent() (o []byte) { if v != nil && v.StartedEvent != nil { return v.StartedEvent } return } // IsSetStartedEvent returns true if StartedEvent is not nil. func (v *ChildExecutionInfo) IsSetStartedEvent() bool { return v != nil && v.StartedEvent != nil } // GetStartedEventEncoding returns the value of StartedEventEncoding if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetStartedEventEncoding() (o string) { if v != nil && v.StartedEventEncoding != nil { return *v.StartedEventEncoding } return } // IsSetStartedEventEncoding returns true if StartedEventEncoding is not nil. func (v *ChildExecutionInfo) IsSetStartedEventEncoding() bool { return v != nil && v.StartedEventEncoding != nil } // GetCreateRequestID returns the value of CreateRequestID if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetCreateRequestID() (o string) { if v != nil && v.CreateRequestID != nil { return *v.CreateRequestID } return } // IsSetCreateRequestID returns true if CreateRequestID is not nil. func (v *ChildExecutionInfo) IsSetCreateRequestID() bool { return v != nil && v.CreateRequestID != nil } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetDomainID() (o string) { if v != nil && v.DomainID != nil { return *v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *ChildExecutionInfo) IsSetDomainID() bool { return v != nil && v.DomainID != nil } // GetDomainName returns the value of DomainName if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetDomainName() (o string) { if v != nil && v.DomainName != nil { return *v.DomainName } return } // IsSetDomainName returns true if DomainName is not nil. func (v *ChildExecutionInfo) IsSetDomainName() bool { return v != nil && v.DomainName != nil } // GetWorkflowTypeName returns the value of WorkflowTypeName if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetWorkflowTypeName() (o string) { if v != nil && v.WorkflowTypeName != nil { return *v.WorkflowTypeName } return } // IsSetWorkflowTypeName returns true if WorkflowTypeName is not nil. func (v *ChildExecutionInfo) IsSetWorkflowTypeName() bool { return v != nil && v.WorkflowTypeName != nil } // GetParentClosePolicy returns the value of ParentClosePolicy if it is set or its // zero value if it is unset. func (v *ChildExecutionInfo) GetParentClosePolicy() (o int32) { if v != nil && v.ParentClosePolicy != nil { return *v.ParentClosePolicy } return } // IsSetParentClosePolicy returns true if ParentClosePolicy is not nil. func (v *ChildExecutionInfo) IsSetParentClosePolicy() bool { return v != nil && v.ParentClosePolicy != nil } type DomainInfo struct { Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` Owner *string `json:"owner,omitempty"` Status *int32 `json:"status,omitempty"` RetentionDays *int16 `json:"retentionDays,omitempty"` EmitMetric *bool `json:"emitMetric,omitempty"` ArchivalBucket *string `json:"archivalBucket,omitempty"` ArchivalStatus *int16 `json:"archivalStatus,omitempty"` ConfigVersion *int64 `json:"configVersion,omitempty"` NotificationVersion *int64 `json:"notificationVersion,omitempty"` FailoverNotificationVersion *int64 `json:"failoverNotificationVersion,omitempty"` FailoverVersion *int64 `json:"failoverVersion,omitempty"` ActiveClusterName *string `json:"activeClusterName,omitempty"` Clusters []string `json:"clusters,omitempty"` Data map[string]string `json:"data,omitempty"` BadBinaries []byte `json:"badBinaries,omitempty"` BadBinariesEncoding *string `json:"badBinariesEncoding,omitempty"` HistoryArchivalStatus *int16 `json:"historyArchivalStatus,omitempty"` HistoryArchivalURI *string `json:"historyArchivalURI,omitempty"` VisibilityArchivalStatus *int16 `json:"visibilityArchivalStatus,omitempty"` VisibilityArchivalURI *string `json:"visibilityArchivalURI,omitempty"` FailoverEndTime *int64 `json:"failoverEndTime,omitempty"` PreviousFailoverVersion *int64 `json:"previousFailoverVersion,omitempty"` LastUpdatedTime *int64 `json:"lastUpdatedTime,omitempty"` IsolationGroupsConfiguration []byte `json:"isolationGroupsConfiguration,omitempty"` IsolationGroupsConfigurationEncoding *string `json:"isolationGroupsConfigurationEncoding,omitempty"` AsyncWorkflowConfiguration []byte `json:"asyncWorkflowConfiguration,omitempty"` AsyncWorkflowConfigurationEncoding *string `json:"asyncWorkflowConfigurationEncoding,omitempty"` ActiveClustersConfiguration []byte `json:"activeClustersConfiguration,omitempty"` ActiveClustersConfigurationEncoding *string `json:"activeClustersConfigurationEncoding,omitempty"` } type _Map_String_String_MapItemList map[string]string func (m _Map_String_String_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := wire.NewValueString(v), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_String_MapItemList) Size() int { return len(m) } func (_Map_String_String_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_String_MapItemList) ValueType() wire.Type { return wire.TBinary } func (_Map_String_String_MapItemList) Close() {} // ToWire translates a DomainInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *DomainInfo) ToWire() (wire.Value, error) { var ( fields [30]wire.Field i int = 0 w wire.Value err error ) if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Description != nil { w, err = wire.NewValueString(*(v.Description)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.Owner != nil { w, err = wire.NewValueString(*(v.Owner)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.Status != nil { w, err = wire.NewValueI32(*(v.Status)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.RetentionDays != nil { w, err = wire.NewValueI16(*(v.RetentionDays)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } if v.EmitMetric != nil { w, err = wire.NewValueBool(*(v.EmitMetric)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ArchivalBucket != nil { w, err = wire.NewValueString(*(v.ArchivalBucket)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 22, Value: w} i++ } if v.ArchivalStatus != nil { w, err = wire.NewValueI16(*(v.ArchivalStatus)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 24, Value: w} i++ } if v.ConfigVersion != nil { w, err = wire.NewValueI64(*(v.ConfigVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 26, Value: w} i++ } if v.NotificationVersion != nil { w, err = wire.NewValueI64(*(v.NotificationVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 28, Value: w} i++ } if v.FailoverNotificationVersion != nil { w, err = wire.NewValueI64(*(v.FailoverNotificationVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.FailoverVersion != nil { w, err = wire.NewValueI64(*(v.FailoverVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 32, Value: w} i++ } if v.ActiveClusterName != nil { w, err = wire.NewValueString(*(v.ActiveClusterName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 34, Value: w} i++ } if v.Clusters != nil { w, err = wire.NewValueList(_List_String_ValueList(v.Clusters)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 36, Value: w} i++ } if v.Data != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.Data)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 38, Value: w} i++ } if v.BadBinaries != nil { w, err = wire.NewValueBinary(v.BadBinaries), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 39, Value: w} i++ } if v.BadBinariesEncoding != nil { w, err = wire.NewValueString(*(v.BadBinariesEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.HistoryArchivalStatus != nil { w, err = wire.NewValueI16(*(v.HistoryArchivalStatus)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 42, Value: w} i++ } if v.HistoryArchivalURI != nil { w, err = wire.NewValueString(*(v.HistoryArchivalURI)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 44, Value: w} i++ } if v.VisibilityArchivalStatus != nil { w, err = wire.NewValueI16(*(v.VisibilityArchivalStatus)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 46, Value: w} i++ } if v.VisibilityArchivalURI != nil { w, err = wire.NewValueString(*(v.VisibilityArchivalURI)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 48, Value: w} i++ } if v.FailoverEndTime != nil { w, err = wire.NewValueI64(*(v.FailoverEndTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.PreviousFailoverVersion != nil { w, err = wire.NewValueI64(*(v.PreviousFailoverVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 52, Value: w} i++ } if v.LastUpdatedTime != nil { w, err = wire.NewValueI64(*(v.LastUpdatedTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 54, Value: w} i++ } if v.IsolationGroupsConfiguration != nil { w, err = wire.NewValueBinary(v.IsolationGroupsConfiguration), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 56, Value: w} i++ } if v.IsolationGroupsConfigurationEncoding != nil { w, err = wire.NewValueString(*(v.IsolationGroupsConfigurationEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 58, Value: w} i++ } if v.AsyncWorkflowConfiguration != nil { w, err = wire.NewValueBinary(v.AsyncWorkflowConfiguration), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.AsyncWorkflowConfigurationEncoding != nil { w, err = wire.NewValueString(*(v.AsyncWorkflowConfigurationEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 62, Value: w} i++ } if v.ActiveClustersConfiguration != nil { w, err = wire.NewValueBinary(v.ActiveClustersConfiguration), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 64, Value: w} i++ } if v.ActiveClustersConfigurationEncoding != nil { w, err = wire.NewValueString(*(v.ActiveClustersConfigurationEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 66, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Map_String_String_Read(m wire.MapItemList) (map[string]string, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TBinary { return nil, nil } o := make(map[string]string, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := x.Value.GetString(), error(nil) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a DomainInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a DomainInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v DomainInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *DomainInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Description = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Owner = &x if err != nil { return err } } case 16: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.Status = &x if err != nil { return err } } case 18: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.RetentionDays = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.EmitMetric = &x if err != nil { return err } } case 22: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ArchivalBucket = &x if err != nil { return err } } case 24: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.ArchivalStatus = &x if err != nil { return err } } case 26: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ConfigVersion = &x if err != nil { return err } } case 28: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NotificationVersion = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverNotificationVersion = &x if err != nil { return err } } case 32: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverVersion = &x if err != nil { return err } } case 34: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActiveClusterName = &x if err != nil { return err } } case 36: if field.Value.Type() == wire.TList { v.Clusters, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } } case 38: if field.Value.Type() == wire.TMap { v.Data, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } case 39: if field.Value.Type() == wire.TBinary { v.BadBinaries, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 40: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.BadBinariesEncoding = &x if err != nil { return err } } case 42: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.HistoryArchivalStatus = &x if err != nil { return err } } case 44: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.HistoryArchivalURI = &x if err != nil { return err } } case 46: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.VisibilityArchivalStatus = &x if err != nil { return err } } case 48: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.VisibilityArchivalURI = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FailoverEndTime = &x if err != nil { return err } } case 52: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.PreviousFailoverVersion = &x if err != nil { return err } } case 54: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastUpdatedTime = &x if err != nil { return err } } case 56: if field.Value.Type() == wire.TBinary { v.IsolationGroupsConfiguration, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 58: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.IsolationGroupsConfigurationEncoding = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.AsyncWorkflowConfiguration, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 62: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.AsyncWorkflowConfigurationEncoding = &x if err != nil { return err } } case 64: if field.Value.Type() == wire.TBinary { v.ActiveClustersConfiguration, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 66: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActiveClustersConfigurationEncoding = &x if err != nil { return err } } } } return nil } func _Map_String_String_Encode(val map[string]string, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TBinary, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if err := sw.WriteString(k); err != nil { return err } if err := sw.WriteString(v); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a DomainInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a DomainInfo struct could not be encoded. func (v *DomainInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Description != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Description)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Owner != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Owner)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Status != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.Status)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetentionDays != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.RetentionDays)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EmitMetric != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.EmitMetric)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ArchivalBucket != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 22, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ArchivalBucket)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ArchivalStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 24, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.ArchivalStatus)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ConfigVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 26, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ConfigVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NotificationVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 28, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NotificationVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverNotificationVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverNotificationVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 32, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 34, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActiveClusterName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Clusters != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 36, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.Clusters, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Data != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 38, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.Data, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadBinaries != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 39, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.BadBinaries); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BadBinariesEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.BadBinariesEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryArchivalStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 42, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.HistoryArchivalStatus)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistoryArchivalURI != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 44, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.HistoryArchivalURI)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityArchivalStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 46, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.VisibilityArchivalStatus)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityArchivalURI != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 48, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.VisibilityArchivalURI)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FailoverEndTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FailoverEndTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PreviousFailoverVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 52, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.PreviousFailoverVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastUpdatedTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 54, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastUpdatedTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsolationGroupsConfiguration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 56, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.IsolationGroupsConfiguration); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.IsolationGroupsConfigurationEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 58, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.IsolationGroupsConfigurationEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AsyncWorkflowConfiguration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.AsyncWorkflowConfiguration); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AsyncWorkflowConfigurationEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 62, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.AsyncWorkflowConfigurationEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClustersConfiguration != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 64, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.ActiveClustersConfiguration); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClustersConfigurationEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 66, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActiveClustersConfigurationEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _Map_String_String_Decode(sr stream.Reader) (map[string]string, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TBinary { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]string, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := sr.ReadString() if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a DomainInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a DomainInfo struct could not be generated from the wire // representation. func (v *DomainInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Description = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Owner = &x if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.Status = &x if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.RetentionDays = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.EmitMetric = &x if err != nil { return err } case fh.ID == 22 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ArchivalBucket = &x if err != nil { return err } case fh.ID == 24 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.ArchivalStatus = &x if err != nil { return err } case fh.ID == 26 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ConfigVersion = &x if err != nil { return err } case fh.ID == 28 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NotificationVersion = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverNotificationVersion = &x if err != nil { return err } case fh.ID == 32 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverVersion = &x if err != nil { return err } case fh.ID == 34 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActiveClusterName = &x if err != nil { return err } case fh.ID == 36 && fh.Type == wire.TList: v.Clusters, err = _List_String_Decode(sr) if err != nil { return err } case fh.ID == 38 && fh.Type == wire.TMap: v.Data, err = _Map_String_String_Decode(sr) if err != nil { return err } case fh.ID == 39 && fh.Type == wire.TBinary: v.BadBinaries, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.BadBinariesEncoding = &x if err != nil { return err } case fh.ID == 42 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.HistoryArchivalStatus = &x if err != nil { return err } case fh.ID == 44 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.HistoryArchivalURI = &x if err != nil { return err } case fh.ID == 46 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.VisibilityArchivalStatus = &x if err != nil { return err } case fh.ID == 48 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.VisibilityArchivalURI = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FailoverEndTime = &x if err != nil { return err } case fh.ID == 52 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.PreviousFailoverVersion = &x if err != nil { return err } case fh.ID == 54 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastUpdatedTime = &x if err != nil { return err } case fh.ID == 56 && fh.Type == wire.TBinary: v.IsolationGroupsConfiguration, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 58 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.IsolationGroupsConfigurationEncoding = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.AsyncWorkflowConfiguration, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 62 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.AsyncWorkflowConfigurationEncoding = &x if err != nil { return err } case fh.ID == 64 && fh.Type == wire.TBinary: v.ActiveClustersConfiguration, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 66 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActiveClustersConfigurationEncoding = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a DomainInfo // struct. func (v *DomainInfo) String() string { if v == nil { return "" } var fields [30]string i := 0 if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.Description != nil { fields[i] = fmt.Sprintf("Description: %v", *(v.Description)) i++ } if v.Owner != nil { fields[i] = fmt.Sprintf("Owner: %v", *(v.Owner)) i++ } if v.Status != nil { fields[i] = fmt.Sprintf("Status: %v", *(v.Status)) i++ } if v.RetentionDays != nil { fields[i] = fmt.Sprintf("RetentionDays: %v", *(v.RetentionDays)) i++ } if v.EmitMetric != nil { fields[i] = fmt.Sprintf("EmitMetric: %v", *(v.EmitMetric)) i++ } if v.ArchivalBucket != nil { fields[i] = fmt.Sprintf("ArchivalBucket: %v", *(v.ArchivalBucket)) i++ } if v.ArchivalStatus != nil { fields[i] = fmt.Sprintf("ArchivalStatus: %v", *(v.ArchivalStatus)) i++ } if v.ConfigVersion != nil { fields[i] = fmt.Sprintf("ConfigVersion: %v", *(v.ConfigVersion)) i++ } if v.NotificationVersion != nil { fields[i] = fmt.Sprintf("NotificationVersion: %v", *(v.NotificationVersion)) i++ } if v.FailoverNotificationVersion != nil { fields[i] = fmt.Sprintf("FailoverNotificationVersion: %v", *(v.FailoverNotificationVersion)) i++ } if v.FailoverVersion != nil { fields[i] = fmt.Sprintf("FailoverVersion: %v", *(v.FailoverVersion)) i++ } if v.ActiveClusterName != nil { fields[i] = fmt.Sprintf("ActiveClusterName: %v", *(v.ActiveClusterName)) i++ } if v.Clusters != nil { fields[i] = fmt.Sprintf("Clusters: %v", v.Clusters) i++ } if v.Data != nil { fields[i] = fmt.Sprintf("Data: %v", v.Data) i++ } if v.BadBinaries != nil { fields[i] = fmt.Sprintf("BadBinaries: %v", v.BadBinaries) i++ } if v.BadBinariesEncoding != nil { fields[i] = fmt.Sprintf("BadBinariesEncoding: %v", *(v.BadBinariesEncoding)) i++ } if v.HistoryArchivalStatus != nil { fields[i] = fmt.Sprintf("HistoryArchivalStatus: %v", *(v.HistoryArchivalStatus)) i++ } if v.HistoryArchivalURI != nil { fields[i] = fmt.Sprintf("HistoryArchivalURI: %v", *(v.HistoryArchivalURI)) i++ } if v.VisibilityArchivalStatus != nil { fields[i] = fmt.Sprintf("VisibilityArchivalStatus: %v", *(v.VisibilityArchivalStatus)) i++ } if v.VisibilityArchivalURI != nil { fields[i] = fmt.Sprintf("VisibilityArchivalURI: %v", *(v.VisibilityArchivalURI)) i++ } if v.FailoverEndTime != nil { fields[i] = fmt.Sprintf("FailoverEndTime: %v", *(v.FailoverEndTime)) i++ } if v.PreviousFailoverVersion != nil { fields[i] = fmt.Sprintf("PreviousFailoverVersion: %v", *(v.PreviousFailoverVersion)) i++ } if v.LastUpdatedTime != nil { fields[i] = fmt.Sprintf("LastUpdatedTime: %v", *(v.LastUpdatedTime)) i++ } if v.IsolationGroupsConfiguration != nil { fields[i] = fmt.Sprintf("IsolationGroupsConfiguration: %v", v.IsolationGroupsConfiguration) i++ } if v.IsolationGroupsConfigurationEncoding != nil { fields[i] = fmt.Sprintf("IsolationGroupsConfigurationEncoding: %v", *(v.IsolationGroupsConfigurationEncoding)) i++ } if v.AsyncWorkflowConfiguration != nil { fields[i] = fmt.Sprintf("AsyncWorkflowConfiguration: %v", v.AsyncWorkflowConfiguration) i++ } if v.AsyncWorkflowConfigurationEncoding != nil { fields[i] = fmt.Sprintf("AsyncWorkflowConfigurationEncoding: %v", *(v.AsyncWorkflowConfigurationEncoding)) i++ } if v.ActiveClustersConfiguration != nil { fields[i] = fmt.Sprintf("ActiveClustersConfiguration: %v", v.ActiveClustersConfiguration) i++ } if v.ActiveClustersConfigurationEncoding != nil { fields[i] = fmt.Sprintf("ActiveClustersConfigurationEncoding: %v", *(v.ActiveClustersConfigurationEncoding)) i++ } return fmt.Sprintf("DomainInfo{%v}", strings.Join(fields[:i], ", ")) } func _I16_EqualsPtr(lhs, rhs *int16) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return (x == y) } return lhs == nil && rhs == nil } func _Map_String_String_Equals(lhs, rhs map[string]string) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !(lv == rv) { return false } } return true } // Equals returns true if all the fields of this DomainInfo match the // provided DomainInfo. // // This function performs a deep comparison. func (v *DomainInfo) Equals(rhs *DomainInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !_String_EqualsPtr(v.Description, rhs.Description) { return false } if !_String_EqualsPtr(v.Owner, rhs.Owner) { return false } if !_I32_EqualsPtr(v.Status, rhs.Status) { return false } if !_I16_EqualsPtr(v.RetentionDays, rhs.RetentionDays) { return false } if !_Bool_EqualsPtr(v.EmitMetric, rhs.EmitMetric) { return false } if !_String_EqualsPtr(v.ArchivalBucket, rhs.ArchivalBucket) { return false } if !_I16_EqualsPtr(v.ArchivalStatus, rhs.ArchivalStatus) { return false } if !_I64_EqualsPtr(v.ConfigVersion, rhs.ConfigVersion) { return false } if !_I64_EqualsPtr(v.NotificationVersion, rhs.NotificationVersion) { return false } if !_I64_EqualsPtr(v.FailoverNotificationVersion, rhs.FailoverNotificationVersion) { return false } if !_I64_EqualsPtr(v.FailoverVersion, rhs.FailoverVersion) { return false } if !_String_EqualsPtr(v.ActiveClusterName, rhs.ActiveClusterName) { return false } if !((v.Clusters == nil && rhs.Clusters == nil) || (v.Clusters != nil && rhs.Clusters != nil && _List_String_Equals(v.Clusters, rhs.Clusters))) { return false } if !((v.Data == nil && rhs.Data == nil) || (v.Data != nil && rhs.Data != nil && _Map_String_String_Equals(v.Data, rhs.Data))) { return false } if !((v.BadBinaries == nil && rhs.BadBinaries == nil) || (v.BadBinaries != nil && rhs.BadBinaries != nil && bytes.Equal(v.BadBinaries, rhs.BadBinaries))) { return false } if !_String_EqualsPtr(v.BadBinariesEncoding, rhs.BadBinariesEncoding) { return false } if !_I16_EqualsPtr(v.HistoryArchivalStatus, rhs.HistoryArchivalStatus) { return false } if !_String_EqualsPtr(v.HistoryArchivalURI, rhs.HistoryArchivalURI) { return false } if !_I16_EqualsPtr(v.VisibilityArchivalStatus, rhs.VisibilityArchivalStatus) { return false } if !_String_EqualsPtr(v.VisibilityArchivalURI, rhs.VisibilityArchivalURI) { return false } if !_I64_EqualsPtr(v.FailoverEndTime, rhs.FailoverEndTime) { return false } if !_I64_EqualsPtr(v.PreviousFailoverVersion, rhs.PreviousFailoverVersion) { return false } if !_I64_EqualsPtr(v.LastUpdatedTime, rhs.LastUpdatedTime) { return false } if !((v.IsolationGroupsConfiguration == nil && rhs.IsolationGroupsConfiguration == nil) || (v.IsolationGroupsConfiguration != nil && rhs.IsolationGroupsConfiguration != nil && bytes.Equal(v.IsolationGroupsConfiguration, rhs.IsolationGroupsConfiguration))) { return false } if !_String_EqualsPtr(v.IsolationGroupsConfigurationEncoding, rhs.IsolationGroupsConfigurationEncoding) { return false } if !((v.AsyncWorkflowConfiguration == nil && rhs.AsyncWorkflowConfiguration == nil) || (v.AsyncWorkflowConfiguration != nil && rhs.AsyncWorkflowConfiguration != nil && bytes.Equal(v.AsyncWorkflowConfiguration, rhs.AsyncWorkflowConfiguration))) { return false } if !_String_EqualsPtr(v.AsyncWorkflowConfigurationEncoding, rhs.AsyncWorkflowConfigurationEncoding) { return false } if !((v.ActiveClustersConfiguration == nil && rhs.ActiveClustersConfiguration == nil) || (v.ActiveClustersConfiguration != nil && rhs.ActiveClustersConfiguration != nil && bytes.Equal(v.ActiveClustersConfiguration, rhs.ActiveClustersConfiguration))) { return false } if !_String_EqualsPtr(v.ActiveClustersConfigurationEncoding, rhs.ActiveClustersConfigurationEncoding) { return false } return true } type _Map_String_String_Zapper map[string]string // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_String_Zapper. func (m _Map_String_String_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { enc.AddString((string)(k), v) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of DomainInfo. func (v *DomainInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Name != nil { enc.AddString("name", *v.Name) } if v.Description != nil { enc.AddString("description", *v.Description) } if v.Owner != nil { enc.AddString("owner", *v.Owner) } if v.Status != nil { enc.AddInt32("status", *v.Status) } if v.RetentionDays != nil { enc.AddInt16("retentionDays", *v.RetentionDays) } if v.EmitMetric != nil { enc.AddBool("emitMetric", *v.EmitMetric) } if v.ArchivalBucket != nil { enc.AddString("archivalBucket", *v.ArchivalBucket) } if v.ArchivalStatus != nil { enc.AddInt16("archivalStatus", *v.ArchivalStatus) } if v.ConfigVersion != nil { enc.AddInt64("configVersion", *v.ConfigVersion) } if v.NotificationVersion != nil { enc.AddInt64("notificationVersion", *v.NotificationVersion) } if v.FailoverNotificationVersion != nil { enc.AddInt64("failoverNotificationVersion", *v.FailoverNotificationVersion) } if v.FailoverVersion != nil { enc.AddInt64("failoverVersion", *v.FailoverVersion) } if v.ActiveClusterName != nil { enc.AddString("activeClusterName", *v.ActiveClusterName) } if v.Clusters != nil { err = multierr.Append(err, enc.AddArray("clusters", (_List_String_Zapper)(v.Clusters))) } if v.Data != nil { err = multierr.Append(err, enc.AddObject("data", (_Map_String_String_Zapper)(v.Data))) } if v.BadBinaries != nil { enc.AddString("badBinaries", base64.StdEncoding.EncodeToString(v.BadBinaries)) } if v.BadBinariesEncoding != nil { enc.AddString("badBinariesEncoding", *v.BadBinariesEncoding) } if v.HistoryArchivalStatus != nil { enc.AddInt16("historyArchivalStatus", *v.HistoryArchivalStatus) } if v.HistoryArchivalURI != nil { enc.AddString("historyArchivalURI", *v.HistoryArchivalURI) } if v.VisibilityArchivalStatus != nil { enc.AddInt16("visibilityArchivalStatus", *v.VisibilityArchivalStatus) } if v.VisibilityArchivalURI != nil { enc.AddString("visibilityArchivalURI", *v.VisibilityArchivalURI) } if v.FailoverEndTime != nil { enc.AddInt64("failoverEndTime", *v.FailoverEndTime) } if v.PreviousFailoverVersion != nil { enc.AddInt64("previousFailoverVersion", *v.PreviousFailoverVersion) } if v.LastUpdatedTime != nil { enc.AddInt64("lastUpdatedTime", *v.LastUpdatedTime) } if v.IsolationGroupsConfiguration != nil { enc.AddString("isolationGroupsConfiguration", base64.StdEncoding.EncodeToString(v.IsolationGroupsConfiguration)) } if v.IsolationGroupsConfigurationEncoding != nil { enc.AddString("isolationGroupsConfigurationEncoding", *v.IsolationGroupsConfigurationEncoding) } if v.AsyncWorkflowConfiguration != nil { enc.AddString("asyncWorkflowConfiguration", base64.StdEncoding.EncodeToString(v.AsyncWorkflowConfiguration)) } if v.AsyncWorkflowConfigurationEncoding != nil { enc.AddString("asyncWorkflowConfigurationEncoding", *v.AsyncWorkflowConfigurationEncoding) } if v.ActiveClustersConfiguration != nil { enc.AddString("activeClustersConfiguration", base64.StdEncoding.EncodeToString(v.ActiveClustersConfiguration)) } if v.ActiveClustersConfigurationEncoding != nil { enc.AddString("activeClustersConfigurationEncoding", *v.ActiveClustersConfigurationEncoding) } return err } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *DomainInfo) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *DomainInfo) IsSetName() bool { return v != nil && v.Name != nil } // GetDescription returns the value of Description if it is set or its // zero value if it is unset. func (v *DomainInfo) GetDescription() (o string) { if v != nil && v.Description != nil { return *v.Description } return } // IsSetDescription returns true if Description is not nil. func (v *DomainInfo) IsSetDescription() bool { return v != nil && v.Description != nil } // GetOwner returns the value of Owner if it is set or its // zero value if it is unset. func (v *DomainInfo) GetOwner() (o string) { if v != nil && v.Owner != nil { return *v.Owner } return } // IsSetOwner returns true if Owner is not nil. func (v *DomainInfo) IsSetOwner() bool { return v != nil && v.Owner != nil } // GetStatus returns the value of Status if it is set or its // zero value if it is unset. func (v *DomainInfo) GetStatus() (o int32) { if v != nil && v.Status != nil { return *v.Status } return } // IsSetStatus returns true if Status is not nil. func (v *DomainInfo) IsSetStatus() bool { return v != nil && v.Status != nil } // GetRetentionDays returns the value of RetentionDays if it is set or its // zero value if it is unset. func (v *DomainInfo) GetRetentionDays() (o int16) { if v != nil && v.RetentionDays != nil { return *v.RetentionDays } return } // IsSetRetentionDays returns true if RetentionDays is not nil. func (v *DomainInfo) IsSetRetentionDays() bool { return v != nil && v.RetentionDays != nil } // GetEmitMetric returns the value of EmitMetric if it is set or its // zero value if it is unset. func (v *DomainInfo) GetEmitMetric() (o bool) { if v != nil && v.EmitMetric != nil { return *v.EmitMetric } return } // IsSetEmitMetric returns true if EmitMetric is not nil. func (v *DomainInfo) IsSetEmitMetric() bool { return v != nil && v.EmitMetric != nil } // GetArchivalBucket returns the value of ArchivalBucket if it is set or its // zero value if it is unset. func (v *DomainInfo) GetArchivalBucket() (o string) { if v != nil && v.ArchivalBucket != nil { return *v.ArchivalBucket } return } // IsSetArchivalBucket returns true if ArchivalBucket is not nil. func (v *DomainInfo) IsSetArchivalBucket() bool { return v != nil && v.ArchivalBucket != nil } // GetArchivalStatus returns the value of ArchivalStatus if it is set or its // zero value if it is unset. func (v *DomainInfo) GetArchivalStatus() (o int16) { if v != nil && v.ArchivalStatus != nil { return *v.ArchivalStatus } return } // IsSetArchivalStatus returns true if ArchivalStatus is not nil. func (v *DomainInfo) IsSetArchivalStatus() bool { return v != nil && v.ArchivalStatus != nil } // GetConfigVersion returns the value of ConfigVersion if it is set or its // zero value if it is unset. func (v *DomainInfo) GetConfigVersion() (o int64) { if v != nil && v.ConfigVersion != nil { return *v.ConfigVersion } return } // IsSetConfigVersion returns true if ConfigVersion is not nil. func (v *DomainInfo) IsSetConfigVersion() bool { return v != nil && v.ConfigVersion != nil } // GetNotificationVersion returns the value of NotificationVersion if it is set or its // zero value if it is unset. func (v *DomainInfo) GetNotificationVersion() (o int64) { if v != nil && v.NotificationVersion != nil { return *v.NotificationVersion } return } // IsSetNotificationVersion returns true if NotificationVersion is not nil. func (v *DomainInfo) IsSetNotificationVersion() bool { return v != nil && v.NotificationVersion != nil } // GetFailoverNotificationVersion returns the value of FailoverNotificationVersion if it is set or its // zero value if it is unset. func (v *DomainInfo) GetFailoverNotificationVersion() (o int64) { if v != nil && v.FailoverNotificationVersion != nil { return *v.FailoverNotificationVersion } return } // IsSetFailoverNotificationVersion returns true if FailoverNotificationVersion is not nil. func (v *DomainInfo) IsSetFailoverNotificationVersion() bool { return v != nil && v.FailoverNotificationVersion != nil } // GetFailoverVersion returns the value of FailoverVersion if it is set or its // zero value if it is unset. func (v *DomainInfo) GetFailoverVersion() (o int64) { if v != nil && v.FailoverVersion != nil { return *v.FailoverVersion } return } // IsSetFailoverVersion returns true if FailoverVersion is not nil. func (v *DomainInfo) IsSetFailoverVersion() bool { return v != nil && v.FailoverVersion != nil } // GetActiveClusterName returns the value of ActiveClusterName if it is set or its // zero value if it is unset. func (v *DomainInfo) GetActiveClusterName() (o string) { if v != nil && v.ActiveClusterName != nil { return *v.ActiveClusterName } return } // IsSetActiveClusterName returns true if ActiveClusterName is not nil. func (v *DomainInfo) IsSetActiveClusterName() bool { return v != nil && v.ActiveClusterName != nil } // GetClusters returns the value of Clusters if it is set or its // zero value if it is unset. func (v *DomainInfo) GetClusters() (o []string) { if v != nil && v.Clusters != nil { return v.Clusters } return } // IsSetClusters returns true if Clusters is not nil. func (v *DomainInfo) IsSetClusters() bool { return v != nil && v.Clusters != nil } // GetData returns the value of Data if it is set or its // zero value if it is unset. func (v *DomainInfo) GetData() (o map[string]string) { if v != nil && v.Data != nil { return v.Data } return } // IsSetData returns true if Data is not nil. func (v *DomainInfo) IsSetData() bool { return v != nil && v.Data != nil } // GetBadBinaries returns the value of BadBinaries if it is set or its // zero value if it is unset. func (v *DomainInfo) GetBadBinaries() (o []byte) { if v != nil && v.BadBinaries != nil { return v.BadBinaries } return } // IsSetBadBinaries returns true if BadBinaries is not nil. func (v *DomainInfo) IsSetBadBinaries() bool { return v != nil && v.BadBinaries != nil } // GetBadBinariesEncoding returns the value of BadBinariesEncoding if it is set or its // zero value if it is unset. func (v *DomainInfo) GetBadBinariesEncoding() (o string) { if v != nil && v.BadBinariesEncoding != nil { return *v.BadBinariesEncoding } return } // IsSetBadBinariesEncoding returns true if BadBinariesEncoding is not nil. func (v *DomainInfo) IsSetBadBinariesEncoding() bool { return v != nil && v.BadBinariesEncoding != nil } // GetHistoryArchivalStatus returns the value of HistoryArchivalStatus if it is set or its // zero value if it is unset. func (v *DomainInfo) GetHistoryArchivalStatus() (o int16) { if v != nil && v.HistoryArchivalStatus != nil { return *v.HistoryArchivalStatus } return } // IsSetHistoryArchivalStatus returns true if HistoryArchivalStatus is not nil. func (v *DomainInfo) IsSetHistoryArchivalStatus() bool { return v != nil && v.HistoryArchivalStatus != nil } // GetHistoryArchivalURI returns the value of HistoryArchivalURI if it is set or its // zero value if it is unset. func (v *DomainInfo) GetHistoryArchivalURI() (o string) { if v != nil && v.HistoryArchivalURI != nil { return *v.HistoryArchivalURI } return } // IsSetHistoryArchivalURI returns true if HistoryArchivalURI is not nil. func (v *DomainInfo) IsSetHistoryArchivalURI() bool { return v != nil && v.HistoryArchivalURI != nil } // GetVisibilityArchivalStatus returns the value of VisibilityArchivalStatus if it is set or its // zero value if it is unset. func (v *DomainInfo) GetVisibilityArchivalStatus() (o int16) { if v != nil && v.VisibilityArchivalStatus != nil { return *v.VisibilityArchivalStatus } return } // IsSetVisibilityArchivalStatus returns true if VisibilityArchivalStatus is not nil. func (v *DomainInfo) IsSetVisibilityArchivalStatus() bool { return v != nil && v.VisibilityArchivalStatus != nil } // GetVisibilityArchivalURI returns the value of VisibilityArchivalURI if it is set or its // zero value if it is unset. func (v *DomainInfo) GetVisibilityArchivalURI() (o string) { if v != nil && v.VisibilityArchivalURI != nil { return *v.VisibilityArchivalURI } return } // IsSetVisibilityArchivalURI returns true if VisibilityArchivalURI is not nil. func (v *DomainInfo) IsSetVisibilityArchivalURI() bool { return v != nil && v.VisibilityArchivalURI != nil } // GetFailoverEndTime returns the value of FailoverEndTime if it is set or its // zero value if it is unset. func (v *DomainInfo) GetFailoverEndTime() (o int64) { if v != nil && v.FailoverEndTime != nil { return *v.FailoverEndTime } return } // IsSetFailoverEndTime returns true if FailoverEndTime is not nil. func (v *DomainInfo) IsSetFailoverEndTime() bool { return v != nil && v.FailoverEndTime != nil } // GetPreviousFailoverVersion returns the value of PreviousFailoverVersion if it is set or its // zero value if it is unset. func (v *DomainInfo) GetPreviousFailoverVersion() (o int64) { if v != nil && v.PreviousFailoverVersion != nil { return *v.PreviousFailoverVersion } return } // IsSetPreviousFailoverVersion returns true if PreviousFailoverVersion is not nil. func (v *DomainInfo) IsSetPreviousFailoverVersion() bool { return v != nil && v.PreviousFailoverVersion != nil } // GetLastUpdatedTime returns the value of LastUpdatedTime if it is set or its // zero value if it is unset. func (v *DomainInfo) GetLastUpdatedTime() (o int64) { if v != nil && v.LastUpdatedTime != nil { return *v.LastUpdatedTime } return } // IsSetLastUpdatedTime returns true if LastUpdatedTime is not nil. func (v *DomainInfo) IsSetLastUpdatedTime() bool { return v != nil && v.LastUpdatedTime != nil } // GetIsolationGroupsConfiguration returns the value of IsolationGroupsConfiguration if it is set or its // zero value if it is unset. func (v *DomainInfo) GetIsolationGroupsConfiguration() (o []byte) { if v != nil && v.IsolationGroupsConfiguration != nil { return v.IsolationGroupsConfiguration } return } // IsSetIsolationGroupsConfiguration returns true if IsolationGroupsConfiguration is not nil. func (v *DomainInfo) IsSetIsolationGroupsConfiguration() bool { return v != nil && v.IsolationGroupsConfiguration != nil } // GetIsolationGroupsConfigurationEncoding returns the value of IsolationGroupsConfigurationEncoding if it is set or its // zero value if it is unset. func (v *DomainInfo) GetIsolationGroupsConfigurationEncoding() (o string) { if v != nil && v.IsolationGroupsConfigurationEncoding != nil { return *v.IsolationGroupsConfigurationEncoding } return } // IsSetIsolationGroupsConfigurationEncoding returns true if IsolationGroupsConfigurationEncoding is not nil. func (v *DomainInfo) IsSetIsolationGroupsConfigurationEncoding() bool { return v != nil && v.IsolationGroupsConfigurationEncoding != nil } // GetAsyncWorkflowConfiguration returns the value of AsyncWorkflowConfiguration if it is set or its // zero value if it is unset. func (v *DomainInfo) GetAsyncWorkflowConfiguration() (o []byte) { if v != nil && v.AsyncWorkflowConfiguration != nil { return v.AsyncWorkflowConfiguration } return } // IsSetAsyncWorkflowConfiguration returns true if AsyncWorkflowConfiguration is not nil. func (v *DomainInfo) IsSetAsyncWorkflowConfiguration() bool { return v != nil && v.AsyncWorkflowConfiguration != nil } // GetAsyncWorkflowConfigurationEncoding returns the value of AsyncWorkflowConfigurationEncoding if it is set or its // zero value if it is unset. func (v *DomainInfo) GetAsyncWorkflowConfigurationEncoding() (o string) { if v != nil && v.AsyncWorkflowConfigurationEncoding != nil { return *v.AsyncWorkflowConfigurationEncoding } return } // IsSetAsyncWorkflowConfigurationEncoding returns true if AsyncWorkflowConfigurationEncoding is not nil. func (v *DomainInfo) IsSetAsyncWorkflowConfigurationEncoding() bool { return v != nil && v.AsyncWorkflowConfigurationEncoding != nil } // GetActiveClustersConfiguration returns the value of ActiveClustersConfiguration if it is set or its // zero value if it is unset. func (v *DomainInfo) GetActiveClustersConfiguration() (o []byte) { if v != nil && v.ActiveClustersConfiguration != nil { return v.ActiveClustersConfiguration } return } // IsSetActiveClustersConfiguration returns true if ActiveClustersConfiguration is not nil. func (v *DomainInfo) IsSetActiveClustersConfiguration() bool { return v != nil && v.ActiveClustersConfiguration != nil } // GetActiveClustersConfigurationEncoding returns the value of ActiveClustersConfigurationEncoding if it is set or its // zero value if it is unset. func (v *DomainInfo) GetActiveClustersConfigurationEncoding() (o string) { if v != nil && v.ActiveClustersConfigurationEncoding != nil { return *v.ActiveClustersConfigurationEncoding } return } // IsSetActiveClustersConfigurationEncoding returns true if ActiveClustersConfigurationEncoding is not nil. func (v *DomainInfo) IsSetActiveClustersConfigurationEncoding() bool { return v != nil && v.ActiveClustersConfigurationEncoding != nil } type HistoryTreeInfo struct { CreatedTimeNanos *int64 `json:"createdTimeNanos,omitempty"` Ancestors []*shared.HistoryBranchRange `json:"ancestors,omitempty"` Info *string `json:"info,omitempty"` } type _List_HistoryBranchRange_ValueList []*shared.HistoryBranchRange func (v _List_HistoryBranchRange_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*shared.HistoryBranchRange', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_HistoryBranchRange_ValueList) Size() int { return len(v) } func (_List_HistoryBranchRange_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_HistoryBranchRange_ValueList) Close() {} // ToWire translates a HistoryTreeInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *HistoryTreeInfo) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.CreatedTimeNanos != nil { w, err = wire.NewValueI64(*(v.CreatedTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.Ancestors != nil { w, err = wire.NewValueList(_List_HistoryBranchRange_ValueList(v.Ancestors)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.Info != nil { w, err = wire.NewValueString(*(v.Info)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _HistoryBranchRange_Read(w wire.Value) (*shared.HistoryBranchRange, error) { var v shared.HistoryBranchRange err := v.FromWire(w) return &v, err } func _List_HistoryBranchRange_Read(l wire.ValueList) ([]*shared.HistoryBranchRange, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*shared.HistoryBranchRange, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _HistoryBranchRange_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a HistoryTreeInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a HistoryTreeInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v HistoryTreeInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *HistoryTreeInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CreatedTimeNanos = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TList { v.Ancestors, err = _List_HistoryBranchRange_Read(field.Value.GetList()) if err != nil { return err } } case 14: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Info = &x if err != nil { return err } } } } return nil } func _List_HistoryBranchRange_Encode(val []*shared.HistoryBranchRange, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*shared.HistoryBranchRange', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a HistoryTreeInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a HistoryTreeInfo struct could not be encoded. func (v *HistoryTreeInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.CreatedTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CreatedTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Ancestors != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TList}); err != nil { return err } if err := _List_HistoryBranchRange_Encode(v.Ancestors, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Info != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Info)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _HistoryBranchRange_Decode(sr stream.Reader) (*shared.HistoryBranchRange, error) { var v shared.HistoryBranchRange err := v.Decode(sr) return &v, err } func _List_HistoryBranchRange_Decode(sr stream.Reader) ([]*shared.HistoryBranchRange, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*shared.HistoryBranchRange, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _HistoryBranchRange_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a HistoryTreeInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a HistoryTreeInfo struct could not be generated from the wire // representation. func (v *HistoryTreeInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CreatedTimeNanos = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TList: v.Ancestors, err = _List_HistoryBranchRange_Decode(sr) if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Info = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a HistoryTreeInfo // struct. func (v *HistoryTreeInfo) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.CreatedTimeNanos != nil { fields[i] = fmt.Sprintf("CreatedTimeNanos: %v", *(v.CreatedTimeNanos)) i++ } if v.Ancestors != nil { fields[i] = fmt.Sprintf("Ancestors: %v", v.Ancestors) i++ } if v.Info != nil { fields[i] = fmt.Sprintf("Info: %v", *(v.Info)) i++ } return fmt.Sprintf("HistoryTreeInfo{%v}", strings.Join(fields[:i], ", ")) } func _List_HistoryBranchRange_Equals(lhs, rhs []*shared.HistoryBranchRange) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this HistoryTreeInfo match the // provided HistoryTreeInfo. // // This function performs a deep comparison. func (v *HistoryTreeInfo) Equals(rhs *HistoryTreeInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.CreatedTimeNanos, rhs.CreatedTimeNanos) { return false } if !((v.Ancestors == nil && rhs.Ancestors == nil) || (v.Ancestors != nil && rhs.Ancestors != nil && _List_HistoryBranchRange_Equals(v.Ancestors, rhs.Ancestors))) { return false } if !_String_EqualsPtr(v.Info, rhs.Info) { return false } return true } type _List_HistoryBranchRange_Zapper []*shared.HistoryBranchRange // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_HistoryBranchRange_Zapper. func (l _List_HistoryBranchRange_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of HistoryTreeInfo. func (v *HistoryTreeInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.CreatedTimeNanos != nil { enc.AddInt64("createdTimeNanos", *v.CreatedTimeNanos) } if v.Ancestors != nil { err = multierr.Append(err, enc.AddArray("ancestors", (_List_HistoryBranchRange_Zapper)(v.Ancestors))) } if v.Info != nil { enc.AddString("info", *v.Info) } return err } // GetCreatedTimeNanos returns the value of CreatedTimeNanos if it is set or its // zero value if it is unset. func (v *HistoryTreeInfo) GetCreatedTimeNanos() (o int64) { if v != nil && v.CreatedTimeNanos != nil { return *v.CreatedTimeNanos } return } // IsSetCreatedTimeNanos returns true if CreatedTimeNanos is not nil. func (v *HistoryTreeInfo) IsSetCreatedTimeNanos() bool { return v != nil && v.CreatedTimeNanos != nil } // GetAncestors returns the value of Ancestors if it is set or its // zero value if it is unset. func (v *HistoryTreeInfo) GetAncestors() (o []*shared.HistoryBranchRange) { if v != nil && v.Ancestors != nil { return v.Ancestors } return } // IsSetAncestors returns true if Ancestors is not nil. func (v *HistoryTreeInfo) IsSetAncestors() bool { return v != nil && v.Ancestors != nil } // GetInfo returns the value of Info if it is set or its // zero value if it is unset. func (v *HistoryTreeInfo) GetInfo() (o string) { if v != nil && v.Info != nil { return *v.Info } return } // IsSetInfo returns true if Info is not nil. func (v *HistoryTreeInfo) IsSetInfo() bool { return v != nil && v.Info != nil } type ReplicationTaskInfo struct { DomainID []byte `json:"domainID,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID []byte `json:"runID,omitempty"` TaskType *int16 `json:"taskType,omitempty"` Version *int64 `json:"version,omitempty"` FirstEventID *int64 `json:"firstEventID,omitempty"` NextEventID *int64 `json:"nextEventID,omitempty"` ScheduledID *int64 `json:"scheduledID,omitempty"` EventStoreVersion *int32 `json:"eventStoreVersion,omitempty"` NewRunEventStoreVersion *int32 `json:"newRunEventStoreVersion,omitempty"` BranchToken []byte `json:"branch_token,omitempty"` NewRunBranchToken []byte `json:"newRunBranchToken,omitempty"` CreationTime *int64 `json:"creationTime,omitempty"` } // ToWire translates a ReplicationTaskInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ReplicationTaskInfo) ToWire() (wire.Value, error) { var ( fields [13]wire.Field i int = 0 w wire.Value err error ) if v.DomainID != nil { w, err = wire.NewValueBinary(v.DomainID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueBinary(v.RunID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.TaskType != nil { w, err = wire.NewValueI16(*(v.TaskType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } if v.FirstEventID != nil { w, err = wire.NewValueI64(*(v.FirstEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.NextEventID != nil { w, err = wire.NewValueI64(*(v.NextEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 22, Value: w} i++ } if v.ScheduledID != nil { w, err = wire.NewValueI64(*(v.ScheduledID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 24, Value: w} i++ } if v.EventStoreVersion != nil { w, err = wire.NewValueI32(*(v.EventStoreVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 26, Value: w} i++ } if v.NewRunEventStoreVersion != nil { w, err = wire.NewValueI32(*(v.NewRunEventStoreVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 28, Value: w} i++ } if v.BranchToken != nil { w, err = wire.NewValueBinary(v.BranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.NewRunBranchToken != nil { w, err = wire.NewValueBinary(v.NewRunBranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 34, Value: w} i++ } if v.CreationTime != nil { w, err = wire.NewValueI64(*(v.CreationTime)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 38, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a ReplicationTaskInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ReplicationTaskInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ReplicationTaskInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ReplicationTaskInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.DomainID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 12: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TBinary { v.RunID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 16: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.TaskType = &x if err != nil { return err } } case 18: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.FirstEventID = &x if err != nil { return err } } case 22: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.NextEventID = &x if err != nil { return err } } case 24: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduledID = &x if err != nil { return err } } case 26: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.EventStoreVersion = &x if err != nil { return err } } case 28: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.NewRunEventStoreVersion = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TBinary { v.BranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 34: if field.Value.Type() == wire.TBinary { v.NewRunBranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 38: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CreationTime = &x if err != nil { return err } } } } return nil } // Encode serializes a ReplicationTaskInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ReplicationTaskInfo struct could not be encoded. func (v *ReplicationTaskInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.DomainID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.RunID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.TaskType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.FirstEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NextEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 22, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.NextEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduledID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 24, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduledID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EventStoreVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 26, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.EventStoreVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NewRunEventStoreVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 28, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.NewRunEventStoreVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.BranchToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.BranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NewRunBranchToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 34, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.NewRunBranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CreationTime != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 38, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CreationTime)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a ReplicationTaskInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ReplicationTaskInfo struct could not be generated from the wire // representation. func (v *ReplicationTaskInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.DomainID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TBinary: v.RunID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.TaskType = &x if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.FirstEventID = &x if err != nil { return err } case fh.ID == 22 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.NextEventID = &x if err != nil { return err } case fh.ID == 24 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduledID = &x if err != nil { return err } case fh.ID == 26 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.EventStoreVersion = &x if err != nil { return err } case fh.ID == 28 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.NewRunEventStoreVersion = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TBinary: v.BranchToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 34 && fh.Type == wire.TBinary: v.NewRunBranchToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 38 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CreationTime = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ReplicationTaskInfo // struct. func (v *ReplicationTaskInfo) String() string { if v == nil { return "" } var fields [13]string i := 0 if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", v.DomainID) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", v.RunID) i++ } if v.TaskType != nil { fields[i] = fmt.Sprintf("TaskType: %v", *(v.TaskType)) i++ } if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.FirstEventID != nil { fields[i] = fmt.Sprintf("FirstEventID: %v", *(v.FirstEventID)) i++ } if v.NextEventID != nil { fields[i] = fmt.Sprintf("NextEventID: %v", *(v.NextEventID)) i++ } if v.ScheduledID != nil { fields[i] = fmt.Sprintf("ScheduledID: %v", *(v.ScheduledID)) i++ } if v.EventStoreVersion != nil { fields[i] = fmt.Sprintf("EventStoreVersion: %v", *(v.EventStoreVersion)) i++ } if v.NewRunEventStoreVersion != nil { fields[i] = fmt.Sprintf("NewRunEventStoreVersion: %v", *(v.NewRunEventStoreVersion)) i++ } if v.BranchToken != nil { fields[i] = fmt.Sprintf("BranchToken: %v", v.BranchToken) i++ } if v.NewRunBranchToken != nil { fields[i] = fmt.Sprintf("NewRunBranchToken: %v", v.NewRunBranchToken) i++ } if v.CreationTime != nil { fields[i] = fmt.Sprintf("CreationTime: %v", *(v.CreationTime)) i++ } return fmt.Sprintf("ReplicationTaskInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this ReplicationTaskInfo match the // provided ReplicationTaskInfo. // // This function performs a deep comparison. func (v *ReplicationTaskInfo) Equals(rhs *ReplicationTaskInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DomainID == nil && rhs.DomainID == nil) || (v.DomainID != nil && rhs.DomainID != nil && bytes.Equal(v.DomainID, rhs.DomainID))) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !((v.RunID == nil && rhs.RunID == nil) || (v.RunID != nil && rhs.RunID != nil && bytes.Equal(v.RunID, rhs.RunID))) { return false } if !_I16_EqualsPtr(v.TaskType, rhs.TaskType) { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.FirstEventID, rhs.FirstEventID) { return false } if !_I64_EqualsPtr(v.NextEventID, rhs.NextEventID) { return false } if !_I64_EqualsPtr(v.ScheduledID, rhs.ScheduledID) { return false } if !_I32_EqualsPtr(v.EventStoreVersion, rhs.EventStoreVersion) { return false } if !_I32_EqualsPtr(v.NewRunEventStoreVersion, rhs.NewRunEventStoreVersion) { return false } if !((v.BranchToken == nil && rhs.BranchToken == nil) || (v.BranchToken != nil && rhs.BranchToken != nil && bytes.Equal(v.BranchToken, rhs.BranchToken))) { return false } if !((v.NewRunBranchToken == nil && rhs.NewRunBranchToken == nil) || (v.NewRunBranchToken != nil && rhs.NewRunBranchToken != nil && bytes.Equal(v.NewRunBranchToken, rhs.NewRunBranchToken))) { return false } if !_I64_EqualsPtr(v.CreationTime, rhs.CreationTime) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ReplicationTaskInfo. func (v *ReplicationTaskInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainID != nil { enc.AddString("domainID", base64.StdEncoding.EncodeToString(v.DomainID)) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", base64.StdEncoding.EncodeToString(v.RunID)) } if v.TaskType != nil { enc.AddInt16("taskType", *v.TaskType) } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.FirstEventID != nil { enc.AddInt64("firstEventID", *v.FirstEventID) } if v.NextEventID != nil { enc.AddInt64("nextEventID", *v.NextEventID) } if v.ScheduledID != nil { enc.AddInt64("scheduledID", *v.ScheduledID) } if v.EventStoreVersion != nil { enc.AddInt32("eventStoreVersion", *v.EventStoreVersion) } if v.NewRunEventStoreVersion != nil { enc.AddInt32("newRunEventStoreVersion", *v.NewRunEventStoreVersion) } if v.BranchToken != nil { enc.AddString("branch_token", base64.StdEncoding.EncodeToString(v.BranchToken)) } if v.NewRunBranchToken != nil { enc.AddString("newRunBranchToken", base64.StdEncoding.EncodeToString(v.NewRunBranchToken)) } if v.CreationTime != nil { enc.AddInt64("creationTime", *v.CreationTime) } return err } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetDomainID() (o []byte) { if v != nil && v.DomainID != nil { return v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *ReplicationTaskInfo) IsSetDomainID() bool { return v != nil && v.DomainID != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *ReplicationTaskInfo) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetRunID() (o []byte) { if v != nil && v.RunID != nil { return v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *ReplicationTaskInfo) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetTaskType returns the value of TaskType if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetTaskType() (o int16) { if v != nil && v.TaskType != nil { return *v.TaskType } return } // IsSetTaskType returns true if TaskType is not nil. func (v *ReplicationTaskInfo) IsSetTaskType() bool { return v != nil && v.TaskType != nil } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *ReplicationTaskInfo) IsSetVersion() bool { return v != nil && v.Version != nil } // GetFirstEventID returns the value of FirstEventID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetFirstEventID() (o int64) { if v != nil && v.FirstEventID != nil { return *v.FirstEventID } return } // IsSetFirstEventID returns true if FirstEventID is not nil. func (v *ReplicationTaskInfo) IsSetFirstEventID() bool { return v != nil && v.FirstEventID != nil } // GetNextEventID returns the value of NextEventID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetNextEventID() (o int64) { if v != nil && v.NextEventID != nil { return *v.NextEventID } return } // IsSetNextEventID returns true if NextEventID is not nil. func (v *ReplicationTaskInfo) IsSetNextEventID() bool { return v != nil && v.NextEventID != nil } // GetScheduledID returns the value of ScheduledID if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetScheduledID() (o int64) { if v != nil && v.ScheduledID != nil { return *v.ScheduledID } return } // IsSetScheduledID returns true if ScheduledID is not nil. func (v *ReplicationTaskInfo) IsSetScheduledID() bool { return v != nil && v.ScheduledID != nil } // GetEventStoreVersion returns the value of EventStoreVersion if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetEventStoreVersion() (o int32) { if v != nil && v.EventStoreVersion != nil { return *v.EventStoreVersion } return } // IsSetEventStoreVersion returns true if EventStoreVersion is not nil. func (v *ReplicationTaskInfo) IsSetEventStoreVersion() bool { return v != nil && v.EventStoreVersion != nil } // GetNewRunEventStoreVersion returns the value of NewRunEventStoreVersion if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetNewRunEventStoreVersion() (o int32) { if v != nil && v.NewRunEventStoreVersion != nil { return *v.NewRunEventStoreVersion } return } // IsSetNewRunEventStoreVersion returns true if NewRunEventStoreVersion is not nil. func (v *ReplicationTaskInfo) IsSetNewRunEventStoreVersion() bool { return v != nil && v.NewRunEventStoreVersion != nil } // GetBranchToken returns the value of BranchToken if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetBranchToken() (o []byte) { if v != nil && v.BranchToken != nil { return v.BranchToken } return } // IsSetBranchToken returns true if BranchToken is not nil. func (v *ReplicationTaskInfo) IsSetBranchToken() bool { return v != nil && v.BranchToken != nil } // GetNewRunBranchToken returns the value of NewRunBranchToken if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetNewRunBranchToken() (o []byte) { if v != nil && v.NewRunBranchToken != nil { return v.NewRunBranchToken } return } // IsSetNewRunBranchToken returns true if NewRunBranchToken is not nil. func (v *ReplicationTaskInfo) IsSetNewRunBranchToken() bool { return v != nil && v.NewRunBranchToken != nil } // GetCreationTime returns the value of CreationTime if it is set or its // zero value if it is unset. func (v *ReplicationTaskInfo) GetCreationTime() (o int64) { if v != nil && v.CreationTime != nil { return *v.CreationTime } return } // IsSetCreationTime returns true if CreationTime is not nil. func (v *ReplicationTaskInfo) IsSetCreationTime() bool { return v != nil && v.CreationTime != nil } type RequestCancelInfo struct { Version *int64 `json:"version,omitempty"` InitiatedEventBatchID *int64 `json:"initiatedEventBatchID,omitempty"` CancelRequestID *string `json:"cancelRequestID,omitempty"` } // ToWire translates a RequestCancelInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *RequestCancelInfo) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.InitiatedEventBatchID != nil { w, err = wire.NewValueI64(*(v.InitiatedEventBatchID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 11, Value: w} i++ } if v.CancelRequestID != nil { w, err = wire.NewValueString(*(v.CancelRequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a RequestCancelInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a RequestCancelInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v RequestCancelInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *RequestCancelInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 11: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventBatchID = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CancelRequestID = &x if err != nil { return err } } } } return nil } // Encode serializes a RequestCancelInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a RequestCancelInfo struct could not be encoded. func (v *RequestCancelInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventBatchID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 11, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventBatchID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelRequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CancelRequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a RequestCancelInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a RequestCancelInfo struct could not be generated from the wire // representation. func (v *RequestCancelInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 11 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventBatchID = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CancelRequestID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a RequestCancelInfo // struct. func (v *RequestCancelInfo) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.InitiatedEventBatchID != nil { fields[i] = fmt.Sprintf("InitiatedEventBatchID: %v", *(v.InitiatedEventBatchID)) i++ } if v.CancelRequestID != nil { fields[i] = fmt.Sprintf("CancelRequestID: %v", *(v.CancelRequestID)) i++ } return fmt.Sprintf("RequestCancelInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this RequestCancelInfo match the // provided RequestCancelInfo. // // This function performs a deep comparison. func (v *RequestCancelInfo) Equals(rhs *RequestCancelInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.InitiatedEventBatchID, rhs.InitiatedEventBatchID) { return false } if !_String_EqualsPtr(v.CancelRequestID, rhs.CancelRequestID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of RequestCancelInfo. func (v *RequestCancelInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.InitiatedEventBatchID != nil { enc.AddInt64("initiatedEventBatchID", *v.InitiatedEventBatchID) } if v.CancelRequestID != nil { enc.AddString("cancelRequestID", *v.CancelRequestID) } return err } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *RequestCancelInfo) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *RequestCancelInfo) IsSetVersion() bool { return v != nil && v.Version != nil } // GetInitiatedEventBatchID returns the value of InitiatedEventBatchID if it is set or its // zero value if it is unset. func (v *RequestCancelInfo) GetInitiatedEventBatchID() (o int64) { if v != nil && v.InitiatedEventBatchID != nil { return *v.InitiatedEventBatchID } return } // IsSetInitiatedEventBatchID returns true if InitiatedEventBatchID is not nil. func (v *RequestCancelInfo) IsSetInitiatedEventBatchID() bool { return v != nil && v.InitiatedEventBatchID != nil } // GetCancelRequestID returns the value of CancelRequestID if it is set or its // zero value if it is unset. func (v *RequestCancelInfo) GetCancelRequestID() (o string) { if v != nil && v.CancelRequestID != nil { return *v.CancelRequestID } return } // IsSetCancelRequestID returns true if CancelRequestID is not nil. func (v *RequestCancelInfo) IsSetCancelRequestID() bool { return v != nil && v.CancelRequestID != nil } type ShardInfo struct { StolenSinceRenew *int32 `json:"stolenSinceRenew,omitempty"` UpdatedAtNanos *int64 `json:"updatedAtNanos,omitempty"` ReplicationAckLevel *int64 `json:"replicationAckLevel,omitempty"` TransferAckLevel *int64 `json:"transferAckLevel,omitempty"` TimerAckLevelNanos *int64 `json:"timerAckLevelNanos,omitempty"` DomainNotificationVersion *int64 `json:"domainNotificationVersion,omitempty"` ClusterTransferAckLevel map[string]int64 `json:"clusterTransferAckLevel,omitempty"` ClusterTimerAckLevel map[string]int64 `json:"clusterTimerAckLevel,omitempty"` Owner *string `json:"owner,omitempty"` ClusterReplicationLevel map[string]int64 `json:"clusterReplicationLevel,omitempty"` PendingFailoverMarkers []byte `json:"pendingFailoverMarkers,omitempty"` PendingFailoverMarkersEncoding *string `json:"pendingFailoverMarkersEncoding,omitempty"` ReplicationDlqAckLevel map[string]int64 `json:"replicationDlqAckLevel,omitempty"` TransferProcessingQueueStates []byte `json:"transferProcessingQueueStates,omitempty"` TransferProcessingQueueStatesEncoding *string `json:"transferProcessingQueueStatesEncoding,omitempty"` TimerProcessingQueueStates []byte `json:"timerProcessingQueueStates,omitempty"` TimerProcessingQueueStatesEncoding *string `json:"timerProcessingQueueStatesEncoding,omitempty"` CrossClusterProcessingQueueStates []byte `json:"crossClusterProcessingQueueStates,omitempty"` CrossClusterProcessingQueueStatesEncoding *string `json:"crossClusterProcessingQueueStatesEncoding,omitempty"` QueueStates map[int32]*shared.QueueState `json:"queueStates,omitempty"` } type _Map_String_I64_MapItemList map[string]int64 func (m _Map_String_I64_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := wire.NewValueI64(v), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_I64_MapItemList) Size() int { return len(m) } func (_Map_String_I64_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_I64_MapItemList) ValueType() wire.Type { return wire.TI64 } func (_Map_String_I64_MapItemList) Close() {} type _Map_I32_QueueState_MapItemList map[int32]*shared.QueueState func (m _Map_I32_QueueState_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[int32]*shared.QueueState', key [%v]: value is nil", k) } kw, err := wire.NewValueI32(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_I32_QueueState_MapItemList) Size() int { return len(m) } func (_Map_I32_QueueState_MapItemList) KeyType() wire.Type { return wire.TI32 } func (_Map_I32_QueueState_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_I32_QueueState_MapItemList) Close() {} // ToWire translates a ShardInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *ShardInfo) ToWire() (wire.Value, error) { var ( fields [20]wire.Field i int = 0 w wire.Value err error ) if v.StolenSinceRenew != nil { w, err = wire.NewValueI32(*(v.StolenSinceRenew)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.UpdatedAtNanos != nil { w, err = wire.NewValueI64(*(v.UpdatedAtNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.ReplicationAckLevel != nil { w, err = wire.NewValueI64(*(v.ReplicationAckLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.TransferAckLevel != nil { w, err = wire.NewValueI64(*(v.TransferAckLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.TimerAckLevelNanos != nil { w, err = wire.NewValueI64(*(v.TimerAckLevelNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } if v.DomainNotificationVersion != nil { w, err = wire.NewValueI64(*(v.DomainNotificationVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 24, Value: w} i++ } if v.ClusterTransferAckLevel != nil { w, err = wire.NewValueMap(_Map_String_I64_MapItemList(v.ClusterTransferAckLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 34, Value: w} i++ } if v.ClusterTimerAckLevel != nil { w, err = wire.NewValueMap(_Map_String_I64_MapItemList(v.ClusterTimerAckLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 36, Value: w} i++ } if v.Owner != nil { w, err = wire.NewValueString(*(v.Owner)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 38, Value: w} i++ } if v.ClusterReplicationLevel != nil { w, err = wire.NewValueMap(_Map_String_I64_MapItemList(v.ClusterReplicationLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 40, Value: w} i++ } if v.PendingFailoverMarkers != nil { w, err = wire.NewValueBinary(v.PendingFailoverMarkers), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 42, Value: w} i++ } if v.PendingFailoverMarkersEncoding != nil { w, err = wire.NewValueString(*(v.PendingFailoverMarkersEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 44, Value: w} i++ } if v.ReplicationDlqAckLevel != nil { w, err = wire.NewValueMap(_Map_String_I64_MapItemList(v.ReplicationDlqAckLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 46, Value: w} i++ } if v.TransferProcessingQueueStates != nil { w, err = wire.NewValueBinary(v.TransferProcessingQueueStates), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.TransferProcessingQueueStatesEncoding != nil { w, err = wire.NewValueString(*(v.TransferProcessingQueueStatesEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 51, Value: w} i++ } if v.TimerProcessingQueueStates != nil { w, err = wire.NewValueBinary(v.TimerProcessingQueueStates), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 55, Value: w} i++ } if v.TimerProcessingQueueStatesEncoding != nil { w, err = wire.NewValueString(*(v.TimerProcessingQueueStatesEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 56, Value: w} i++ } if v.CrossClusterProcessingQueueStates != nil { w, err = wire.NewValueBinary(v.CrossClusterProcessingQueueStates), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.CrossClusterProcessingQueueStatesEncoding != nil { w, err = wire.NewValueString(*(v.CrossClusterProcessingQueueStatesEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 61, Value: w} i++ } if v.QueueStates != nil { w, err = wire.NewValueMap(_Map_I32_QueueState_MapItemList(v.QueueStates)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 64, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Map_String_I64_Read(m wire.MapItemList) (map[string]int64, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TI64 { return nil, nil } o := make(map[string]int64, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := x.Value.GetI64(), error(nil) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } func _QueueState_Read(w wire.Value) (*shared.QueueState, error) { var v shared.QueueState err := v.FromWire(w) return &v, err } func _Map_I32_QueueState_Read(m wire.MapItemList) (map[int32]*shared.QueueState, error) { if m.KeyType() != wire.TI32 { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[int32]*shared.QueueState, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetI32(), error(nil) if err != nil { return err } v, err := _QueueState_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a ShardInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a ShardInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v ShardInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *ShardInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.StolenSinceRenew = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.UpdatedAtNanos = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ReplicationAckLevel = &x if err != nil { return err } } case 16: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TransferAckLevel = &x if err != nil { return err } } case 18: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TimerAckLevelNanos = &x if err != nil { return err } } case 24: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DomainNotificationVersion = &x if err != nil { return err } } case 34: if field.Value.Type() == wire.TMap { v.ClusterTransferAckLevel, err = _Map_String_I64_Read(field.Value.GetMap()) if err != nil { return err } } case 36: if field.Value.Type() == wire.TMap { v.ClusterTimerAckLevel, err = _Map_String_I64_Read(field.Value.GetMap()) if err != nil { return err } } case 38: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Owner = &x if err != nil { return err } } case 40: if field.Value.Type() == wire.TMap { v.ClusterReplicationLevel, err = _Map_String_I64_Read(field.Value.GetMap()) if err != nil { return err } } case 42: if field.Value.Type() == wire.TBinary { v.PendingFailoverMarkers, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 44: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.PendingFailoverMarkersEncoding = &x if err != nil { return err } } case 46: if field.Value.Type() == wire.TMap { v.ReplicationDlqAckLevel, err = _Map_String_I64_Read(field.Value.GetMap()) if err != nil { return err } } case 50: if field.Value.Type() == wire.TBinary { v.TransferProcessingQueueStates, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 51: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TransferProcessingQueueStatesEncoding = &x if err != nil { return err } } case 55: if field.Value.Type() == wire.TBinary { v.TimerProcessingQueueStates, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 56: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TimerProcessingQueueStatesEncoding = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TBinary { v.CrossClusterProcessingQueueStates, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 61: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CrossClusterProcessingQueueStatesEncoding = &x if err != nil { return err } } case 64: if field.Value.Type() == wire.TMap { v.QueueStates, err = _Map_I32_QueueState_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_String_I64_Encode(val map[string]int64, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TI64, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if err := sw.WriteString(k); err != nil { return err } if err := sw.WriteInt64(v); err != nil { return err } } return sw.WriteMapEnd() } func _Map_I32_QueueState_Encode(val map[int32]*shared.QueueState, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TI32, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[int32]*shared.QueueState', key [%v]: value is nil", k) } if err := sw.WriteInt32(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a ShardInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a ShardInfo struct could not be encoded. func (v *ShardInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.StolenSinceRenew != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.StolenSinceRenew)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.UpdatedAtNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.UpdatedAtNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReplicationAckLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ReplicationAckLevel)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TransferAckLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TransferAckLevel)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimerAckLevelNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TimerAckLevelNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DomainNotificationVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 24, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DomainNotificationVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClusterTransferAckLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 34, Type: wire.TMap}); err != nil { return err } if err := _Map_String_I64_Encode(v.ClusterTransferAckLevel, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClusterTimerAckLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 36, Type: wire.TMap}); err != nil { return err } if err := _Map_String_I64_Encode(v.ClusterTimerAckLevel, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Owner != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 38, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Owner)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClusterReplicationLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 40, Type: wire.TMap}); err != nil { return err } if err := _Map_String_I64_Encode(v.ClusterReplicationLevel, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingFailoverMarkers != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 42, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.PendingFailoverMarkers); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PendingFailoverMarkersEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 44, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.PendingFailoverMarkersEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReplicationDlqAckLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 46, Type: wire.TMap}); err != nil { return err } if err := _Map_String_I64_Encode(v.ReplicationDlqAckLevel, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TransferProcessingQueueStates != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TransferProcessingQueueStates); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TransferProcessingQueueStatesEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 51, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TransferProcessingQueueStatesEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimerProcessingQueueStates != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 55, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TimerProcessingQueueStates); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimerProcessingQueueStatesEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 56, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TimerProcessingQueueStatesEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CrossClusterProcessingQueueStates != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.CrossClusterProcessingQueueStates); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CrossClusterProcessingQueueStatesEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 61, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CrossClusterProcessingQueueStatesEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.QueueStates != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 64, Type: wire.TMap}); err != nil { return err } if err := _Map_I32_QueueState_Encode(v.QueueStates, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _Map_String_I64_Decode(sr stream.Reader) (map[string]int64, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TI64 { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string]int64, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := sr.ReadInt64() if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } func _QueueState_Decode(sr stream.Reader) (*shared.QueueState, error) { var v shared.QueueState err := v.Decode(sr) return &v, err } func _Map_I32_QueueState_Decode(sr stream.Reader) (map[int32]*shared.QueueState, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TI32 || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[int32]*shared.QueueState, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadInt32() if err != nil { return nil, err } v, err := _QueueState_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a ShardInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a ShardInfo struct could not be generated from the wire // representation. func (v *ShardInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.StolenSinceRenew = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.UpdatedAtNanos = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ReplicationAckLevel = &x if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TransferAckLevel = &x if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TimerAckLevelNanos = &x if err != nil { return err } case fh.ID == 24 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DomainNotificationVersion = &x if err != nil { return err } case fh.ID == 34 && fh.Type == wire.TMap: v.ClusterTransferAckLevel, err = _Map_String_I64_Decode(sr) if err != nil { return err } case fh.ID == 36 && fh.Type == wire.TMap: v.ClusterTimerAckLevel, err = _Map_String_I64_Decode(sr) if err != nil { return err } case fh.ID == 38 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Owner = &x if err != nil { return err } case fh.ID == 40 && fh.Type == wire.TMap: v.ClusterReplicationLevel, err = _Map_String_I64_Decode(sr) if err != nil { return err } case fh.ID == 42 && fh.Type == wire.TBinary: v.PendingFailoverMarkers, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 44 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.PendingFailoverMarkersEncoding = &x if err != nil { return err } case fh.ID == 46 && fh.Type == wire.TMap: v.ReplicationDlqAckLevel, err = _Map_String_I64_Decode(sr) if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TBinary: v.TransferProcessingQueueStates, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 51 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TransferProcessingQueueStatesEncoding = &x if err != nil { return err } case fh.ID == 55 && fh.Type == wire.TBinary: v.TimerProcessingQueueStates, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 56 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TimerProcessingQueueStatesEncoding = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TBinary: v.CrossClusterProcessingQueueStates, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 61 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CrossClusterProcessingQueueStatesEncoding = &x if err != nil { return err } case fh.ID == 64 && fh.Type == wire.TMap: v.QueueStates, err = _Map_I32_QueueState_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a ShardInfo // struct. func (v *ShardInfo) String() string { if v == nil { return "" } var fields [20]string i := 0 if v.StolenSinceRenew != nil { fields[i] = fmt.Sprintf("StolenSinceRenew: %v", *(v.StolenSinceRenew)) i++ } if v.UpdatedAtNanos != nil { fields[i] = fmt.Sprintf("UpdatedAtNanos: %v", *(v.UpdatedAtNanos)) i++ } if v.ReplicationAckLevel != nil { fields[i] = fmt.Sprintf("ReplicationAckLevel: %v", *(v.ReplicationAckLevel)) i++ } if v.TransferAckLevel != nil { fields[i] = fmt.Sprintf("TransferAckLevel: %v", *(v.TransferAckLevel)) i++ } if v.TimerAckLevelNanos != nil { fields[i] = fmt.Sprintf("TimerAckLevelNanos: %v", *(v.TimerAckLevelNanos)) i++ } if v.DomainNotificationVersion != nil { fields[i] = fmt.Sprintf("DomainNotificationVersion: %v", *(v.DomainNotificationVersion)) i++ } if v.ClusterTransferAckLevel != nil { fields[i] = fmt.Sprintf("ClusterTransferAckLevel: %v", v.ClusterTransferAckLevel) i++ } if v.ClusterTimerAckLevel != nil { fields[i] = fmt.Sprintf("ClusterTimerAckLevel: %v", v.ClusterTimerAckLevel) i++ } if v.Owner != nil { fields[i] = fmt.Sprintf("Owner: %v", *(v.Owner)) i++ } if v.ClusterReplicationLevel != nil { fields[i] = fmt.Sprintf("ClusterReplicationLevel: %v", v.ClusterReplicationLevel) i++ } if v.PendingFailoverMarkers != nil { fields[i] = fmt.Sprintf("PendingFailoverMarkers: %v", v.PendingFailoverMarkers) i++ } if v.PendingFailoverMarkersEncoding != nil { fields[i] = fmt.Sprintf("PendingFailoverMarkersEncoding: %v", *(v.PendingFailoverMarkersEncoding)) i++ } if v.ReplicationDlqAckLevel != nil { fields[i] = fmt.Sprintf("ReplicationDlqAckLevel: %v", v.ReplicationDlqAckLevel) i++ } if v.TransferProcessingQueueStates != nil { fields[i] = fmt.Sprintf("TransferProcessingQueueStates: %v", v.TransferProcessingQueueStates) i++ } if v.TransferProcessingQueueStatesEncoding != nil { fields[i] = fmt.Sprintf("TransferProcessingQueueStatesEncoding: %v", *(v.TransferProcessingQueueStatesEncoding)) i++ } if v.TimerProcessingQueueStates != nil { fields[i] = fmt.Sprintf("TimerProcessingQueueStates: %v", v.TimerProcessingQueueStates) i++ } if v.TimerProcessingQueueStatesEncoding != nil { fields[i] = fmt.Sprintf("TimerProcessingQueueStatesEncoding: %v", *(v.TimerProcessingQueueStatesEncoding)) i++ } if v.CrossClusterProcessingQueueStates != nil { fields[i] = fmt.Sprintf("CrossClusterProcessingQueueStates: %v", v.CrossClusterProcessingQueueStates) i++ } if v.CrossClusterProcessingQueueStatesEncoding != nil { fields[i] = fmt.Sprintf("CrossClusterProcessingQueueStatesEncoding: %v", *(v.CrossClusterProcessingQueueStatesEncoding)) i++ } if v.QueueStates != nil { fields[i] = fmt.Sprintf("QueueStates: %v", v.QueueStates) i++ } return fmt.Sprintf("ShardInfo{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_I64_Equals(lhs, rhs map[string]int64) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !(lv == rv) { return false } } return true } func _Map_I32_QueueState_Equals(lhs, rhs map[int32]*shared.QueueState) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this ShardInfo match the // provided ShardInfo. // // This function performs a deep comparison. func (v *ShardInfo) Equals(rhs *ShardInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I32_EqualsPtr(v.StolenSinceRenew, rhs.StolenSinceRenew) { return false } if !_I64_EqualsPtr(v.UpdatedAtNanos, rhs.UpdatedAtNanos) { return false } if !_I64_EqualsPtr(v.ReplicationAckLevel, rhs.ReplicationAckLevel) { return false } if !_I64_EqualsPtr(v.TransferAckLevel, rhs.TransferAckLevel) { return false } if !_I64_EqualsPtr(v.TimerAckLevelNanos, rhs.TimerAckLevelNanos) { return false } if !_I64_EqualsPtr(v.DomainNotificationVersion, rhs.DomainNotificationVersion) { return false } if !((v.ClusterTransferAckLevel == nil && rhs.ClusterTransferAckLevel == nil) || (v.ClusterTransferAckLevel != nil && rhs.ClusterTransferAckLevel != nil && _Map_String_I64_Equals(v.ClusterTransferAckLevel, rhs.ClusterTransferAckLevel))) { return false } if !((v.ClusterTimerAckLevel == nil && rhs.ClusterTimerAckLevel == nil) || (v.ClusterTimerAckLevel != nil && rhs.ClusterTimerAckLevel != nil && _Map_String_I64_Equals(v.ClusterTimerAckLevel, rhs.ClusterTimerAckLevel))) { return false } if !_String_EqualsPtr(v.Owner, rhs.Owner) { return false } if !((v.ClusterReplicationLevel == nil && rhs.ClusterReplicationLevel == nil) || (v.ClusterReplicationLevel != nil && rhs.ClusterReplicationLevel != nil && _Map_String_I64_Equals(v.ClusterReplicationLevel, rhs.ClusterReplicationLevel))) { return false } if !((v.PendingFailoverMarkers == nil && rhs.PendingFailoverMarkers == nil) || (v.PendingFailoverMarkers != nil && rhs.PendingFailoverMarkers != nil && bytes.Equal(v.PendingFailoverMarkers, rhs.PendingFailoverMarkers))) { return false } if !_String_EqualsPtr(v.PendingFailoverMarkersEncoding, rhs.PendingFailoverMarkersEncoding) { return false } if !((v.ReplicationDlqAckLevel == nil && rhs.ReplicationDlqAckLevel == nil) || (v.ReplicationDlqAckLevel != nil && rhs.ReplicationDlqAckLevel != nil && _Map_String_I64_Equals(v.ReplicationDlqAckLevel, rhs.ReplicationDlqAckLevel))) { return false } if !((v.TransferProcessingQueueStates == nil && rhs.TransferProcessingQueueStates == nil) || (v.TransferProcessingQueueStates != nil && rhs.TransferProcessingQueueStates != nil && bytes.Equal(v.TransferProcessingQueueStates, rhs.TransferProcessingQueueStates))) { return false } if !_String_EqualsPtr(v.TransferProcessingQueueStatesEncoding, rhs.TransferProcessingQueueStatesEncoding) { return false } if !((v.TimerProcessingQueueStates == nil && rhs.TimerProcessingQueueStates == nil) || (v.TimerProcessingQueueStates != nil && rhs.TimerProcessingQueueStates != nil && bytes.Equal(v.TimerProcessingQueueStates, rhs.TimerProcessingQueueStates))) { return false } if !_String_EqualsPtr(v.TimerProcessingQueueStatesEncoding, rhs.TimerProcessingQueueStatesEncoding) { return false } if !((v.CrossClusterProcessingQueueStates == nil && rhs.CrossClusterProcessingQueueStates == nil) || (v.CrossClusterProcessingQueueStates != nil && rhs.CrossClusterProcessingQueueStates != nil && bytes.Equal(v.CrossClusterProcessingQueueStates, rhs.CrossClusterProcessingQueueStates))) { return false } if !_String_EqualsPtr(v.CrossClusterProcessingQueueStatesEncoding, rhs.CrossClusterProcessingQueueStatesEncoding) { return false } if !((v.QueueStates == nil && rhs.QueueStates == nil) || (v.QueueStates != nil && rhs.QueueStates != nil && _Map_I32_QueueState_Equals(v.QueueStates, rhs.QueueStates))) { return false } return true } type _Map_String_I64_Zapper map[string]int64 // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_I64_Zapper. func (m _Map_String_I64_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { enc.AddInt64((string)(k), v) } return err } type _Map_I32_QueueState_Item_Zapper struct { Key int32 Value *shared.QueueState } // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_QueueState_Item_Zapper. func (v _Map_I32_QueueState_Item_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { enc.AddInt32("key", v.Key) err = multierr.Append(err, enc.AddObject("value", v.Value)) return err } type _Map_I32_QueueState_Zapper map[int32]*shared.QueueState // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_QueueState_Zapper. func (m _Map_I32_QueueState_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AppendObject(_Map_I32_QueueState_Item_Zapper{Key: k, Value: v})) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of ShardInfo. func (v *ShardInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.StolenSinceRenew != nil { enc.AddInt32("stolenSinceRenew", *v.StolenSinceRenew) } if v.UpdatedAtNanos != nil { enc.AddInt64("updatedAtNanos", *v.UpdatedAtNanos) } if v.ReplicationAckLevel != nil { enc.AddInt64("replicationAckLevel", *v.ReplicationAckLevel) } if v.TransferAckLevel != nil { enc.AddInt64("transferAckLevel", *v.TransferAckLevel) } if v.TimerAckLevelNanos != nil { enc.AddInt64("timerAckLevelNanos", *v.TimerAckLevelNanos) } if v.DomainNotificationVersion != nil { enc.AddInt64("domainNotificationVersion", *v.DomainNotificationVersion) } if v.ClusterTransferAckLevel != nil { err = multierr.Append(err, enc.AddObject("clusterTransferAckLevel", (_Map_String_I64_Zapper)(v.ClusterTransferAckLevel))) } if v.ClusterTimerAckLevel != nil { err = multierr.Append(err, enc.AddObject("clusterTimerAckLevel", (_Map_String_I64_Zapper)(v.ClusterTimerAckLevel))) } if v.Owner != nil { enc.AddString("owner", *v.Owner) } if v.ClusterReplicationLevel != nil { err = multierr.Append(err, enc.AddObject("clusterReplicationLevel", (_Map_String_I64_Zapper)(v.ClusterReplicationLevel))) } if v.PendingFailoverMarkers != nil { enc.AddString("pendingFailoverMarkers", base64.StdEncoding.EncodeToString(v.PendingFailoverMarkers)) } if v.PendingFailoverMarkersEncoding != nil { enc.AddString("pendingFailoverMarkersEncoding", *v.PendingFailoverMarkersEncoding) } if v.ReplicationDlqAckLevel != nil { err = multierr.Append(err, enc.AddObject("replicationDlqAckLevel", (_Map_String_I64_Zapper)(v.ReplicationDlqAckLevel))) } if v.TransferProcessingQueueStates != nil { enc.AddString("transferProcessingQueueStates", base64.StdEncoding.EncodeToString(v.TransferProcessingQueueStates)) } if v.TransferProcessingQueueStatesEncoding != nil { enc.AddString("transferProcessingQueueStatesEncoding", *v.TransferProcessingQueueStatesEncoding) } if v.TimerProcessingQueueStates != nil { enc.AddString("timerProcessingQueueStates", base64.StdEncoding.EncodeToString(v.TimerProcessingQueueStates)) } if v.TimerProcessingQueueStatesEncoding != nil { enc.AddString("timerProcessingQueueStatesEncoding", *v.TimerProcessingQueueStatesEncoding) } if v.CrossClusterProcessingQueueStates != nil { enc.AddString("crossClusterProcessingQueueStates", base64.StdEncoding.EncodeToString(v.CrossClusterProcessingQueueStates)) } if v.CrossClusterProcessingQueueStatesEncoding != nil { enc.AddString("crossClusterProcessingQueueStatesEncoding", *v.CrossClusterProcessingQueueStatesEncoding) } if v.QueueStates != nil { err = multierr.Append(err, enc.AddArray("queueStates", (_Map_I32_QueueState_Zapper)(v.QueueStates))) } return err } // GetStolenSinceRenew returns the value of StolenSinceRenew if it is set or its // zero value if it is unset. func (v *ShardInfo) GetStolenSinceRenew() (o int32) { if v != nil && v.StolenSinceRenew != nil { return *v.StolenSinceRenew } return } // IsSetStolenSinceRenew returns true if StolenSinceRenew is not nil. func (v *ShardInfo) IsSetStolenSinceRenew() bool { return v != nil && v.StolenSinceRenew != nil } // GetUpdatedAtNanos returns the value of UpdatedAtNanos if it is set or its // zero value if it is unset. func (v *ShardInfo) GetUpdatedAtNanos() (o int64) { if v != nil && v.UpdatedAtNanos != nil { return *v.UpdatedAtNanos } return } // IsSetUpdatedAtNanos returns true if UpdatedAtNanos is not nil. func (v *ShardInfo) IsSetUpdatedAtNanos() bool { return v != nil && v.UpdatedAtNanos != nil } // GetReplicationAckLevel returns the value of ReplicationAckLevel if it is set or its // zero value if it is unset. func (v *ShardInfo) GetReplicationAckLevel() (o int64) { if v != nil && v.ReplicationAckLevel != nil { return *v.ReplicationAckLevel } return } // IsSetReplicationAckLevel returns true if ReplicationAckLevel is not nil. func (v *ShardInfo) IsSetReplicationAckLevel() bool { return v != nil && v.ReplicationAckLevel != nil } // GetTransferAckLevel returns the value of TransferAckLevel if it is set or its // zero value if it is unset. func (v *ShardInfo) GetTransferAckLevel() (o int64) { if v != nil && v.TransferAckLevel != nil { return *v.TransferAckLevel } return } // IsSetTransferAckLevel returns true if TransferAckLevel is not nil. func (v *ShardInfo) IsSetTransferAckLevel() bool { return v != nil && v.TransferAckLevel != nil } // GetTimerAckLevelNanos returns the value of TimerAckLevelNanos if it is set or its // zero value if it is unset. func (v *ShardInfo) GetTimerAckLevelNanos() (o int64) { if v != nil && v.TimerAckLevelNanos != nil { return *v.TimerAckLevelNanos } return } // IsSetTimerAckLevelNanos returns true if TimerAckLevelNanos is not nil. func (v *ShardInfo) IsSetTimerAckLevelNanos() bool { return v != nil && v.TimerAckLevelNanos != nil } // GetDomainNotificationVersion returns the value of DomainNotificationVersion if it is set or its // zero value if it is unset. func (v *ShardInfo) GetDomainNotificationVersion() (o int64) { if v != nil && v.DomainNotificationVersion != nil { return *v.DomainNotificationVersion } return } // IsSetDomainNotificationVersion returns true if DomainNotificationVersion is not nil. func (v *ShardInfo) IsSetDomainNotificationVersion() bool { return v != nil && v.DomainNotificationVersion != nil } // GetClusterTransferAckLevel returns the value of ClusterTransferAckLevel if it is set or its // zero value if it is unset. func (v *ShardInfo) GetClusterTransferAckLevel() (o map[string]int64) { if v != nil && v.ClusterTransferAckLevel != nil { return v.ClusterTransferAckLevel } return } // IsSetClusterTransferAckLevel returns true if ClusterTransferAckLevel is not nil. func (v *ShardInfo) IsSetClusterTransferAckLevel() bool { return v != nil && v.ClusterTransferAckLevel != nil } // GetClusterTimerAckLevel returns the value of ClusterTimerAckLevel if it is set or its // zero value if it is unset. func (v *ShardInfo) GetClusterTimerAckLevel() (o map[string]int64) { if v != nil && v.ClusterTimerAckLevel != nil { return v.ClusterTimerAckLevel } return } // IsSetClusterTimerAckLevel returns true if ClusterTimerAckLevel is not nil. func (v *ShardInfo) IsSetClusterTimerAckLevel() bool { return v != nil && v.ClusterTimerAckLevel != nil } // GetOwner returns the value of Owner if it is set or its // zero value if it is unset. func (v *ShardInfo) GetOwner() (o string) { if v != nil && v.Owner != nil { return *v.Owner } return } // IsSetOwner returns true if Owner is not nil. func (v *ShardInfo) IsSetOwner() bool { return v != nil && v.Owner != nil } // GetClusterReplicationLevel returns the value of ClusterReplicationLevel if it is set or its // zero value if it is unset. func (v *ShardInfo) GetClusterReplicationLevel() (o map[string]int64) { if v != nil && v.ClusterReplicationLevel != nil { return v.ClusterReplicationLevel } return } // IsSetClusterReplicationLevel returns true if ClusterReplicationLevel is not nil. func (v *ShardInfo) IsSetClusterReplicationLevel() bool { return v != nil && v.ClusterReplicationLevel != nil } // GetPendingFailoverMarkers returns the value of PendingFailoverMarkers if it is set or its // zero value if it is unset. func (v *ShardInfo) GetPendingFailoverMarkers() (o []byte) { if v != nil && v.PendingFailoverMarkers != nil { return v.PendingFailoverMarkers } return } // IsSetPendingFailoverMarkers returns true if PendingFailoverMarkers is not nil. func (v *ShardInfo) IsSetPendingFailoverMarkers() bool { return v != nil && v.PendingFailoverMarkers != nil } // GetPendingFailoverMarkersEncoding returns the value of PendingFailoverMarkersEncoding if it is set or its // zero value if it is unset. func (v *ShardInfo) GetPendingFailoverMarkersEncoding() (o string) { if v != nil && v.PendingFailoverMarkersEncoding != nil { return *v.PendingFailoverMarkersEncoding } return } // IsSetPendingFailoverMarkersEncoding returns true if PendingFailoverMarkersEncoding is not nil. func (v *ShardInfo) IsSetPendingFailoverMarkersEncoding() bool { return v != nil && v.PendingFailoverMarkersEncoding != nil } // GetReplicationDlqAckLevel returns the value of ReplicationDlqAckLevel if it is set or its // zero value if it is unset. func (v *ShardInfo) GetReplicationDlqAckLevel() (o map[string]int64) { if v != nil && v.ReplicationDlqAckLevel != nil { return v.ReplicationDlqAckLevel } return } // IsSetReplicationDlqAckLevel returns true if ReplicationDlqAckLevel is not nil. func (v *ShardInfo) IsSetReplicationDlqAckLevel() bool { return v != nil && v.ReplicationDlqAckLevel != nil } // GetTransferProcessingQueueStates returns the value of TransferProcessingQueueStates if it is set or its // zero value if it is unset. func (v *ShardInfo) GetTransferProcessingQueueStates() (o []byte) { if v != nil && v.TransferProcessingQueueStates != nil { return v.TransferProcessingQueueStates } return } // IsSetTransferProcessingQueueStates returns true if TransferProcessingQueueStates is not nil. func (v *ShardInfo) IsSetTransferProcessingQueueStates() bool { return v != nil && v.TransferProcessingQueueStates != nil } // GetTransferProcessingQueueStatesEncoding returns the value of TransferProcessingQueueStatesEncoding if it is set or its // zero value if it is unset. func (v *ShardInfo) GetTransferProcessingQueueStatesEncoding() (o string) { if v != nil && v.TransferProcessingQueueStatesEncoding != nil { return *v.TransferProcessingQueueStatesEncoding } return } // IsSetTransferProcessingQueueStatesEncoding returns true if TransferProcessingQueueStatesEncoding is not nil. func (v *ShardInfo) IsSetTransferProcessingQueueStatesEncoding() bool { return v != nil && v.TransferProcessingQueueStatesEncoding != nil } // GetTimerProcessingQueueStates returns the value of TimerProcessingQueueStates if it is set or its // zero value if it is unset. func (v *ShardInfo) GetTimerProcessingQueueStates() (o []byte) { if v != nil && v.TimerProcessingQueueStates != nil { return v.TimerProcessingQueueStates } return } // IsSetTimerProcessingQueueStates returns true if TimerProcessingQueueStates is not nil. func (v *ShardInfo) IsSetTimerProcessingQueueStates() bool { return v != nil && v.TimerProcessingQueueStates != nil } // GetTimerProcessingQueueStatesEncoding returns the value of TimerProcessingQueueStatesEncoding if it is set or its // zero value if it is unset. func (v *ShardInfo) GetTimerProcessingQueueStatesEncoding() (o string) { if v != nil && v.TimerProcessingQueueStatesEncoding != nil { return *v.TimerProcessingQueueStatesEncoding } return } // IsSetTimerProcessingQueueStatesEncoding returns true if TimerProcessingQueueStatesEncoding is not nil. func (v *ShardInfo) IsSetTimerProcessingQueueStatesEncoding() bool { return v != nil && v.TimerProcessingQueueStatesEncoding != nil } // GetCrossClusterProcessingQueueStates returns the value of CrossClusterProcessingQueueStates if it is set or its // zero value if it is unset. func (v *ShardInfo) GetCrossClusterProcessingQueueStates() (o []byte) { if v != nil && v.CrossClusterProcessingQueueStates != nil { return v.CrossClusterProcessingQueueStates } return } // IsSetCrossClusterProcessingQueueStates returns true if CrossClusterProcessingQueueStates is not nil. func (v *ShardInfo) IsSetCrossClusterProcessingQueueStates() bool { return v != nil && v.CrossClusterProcessingQueueStates != nil } // GetCrossClusterProcessingQueueStatesEncoding returns the value of CrossClusterProcessingQueueStatesEncoding if it is set or its // zero value if it is unset. func (v *ShardInfo) GetCrossClusterProcessingQueueStatesEncoding() (o string) { if v != nil && v.CrossClusterProcessingQueueStatesEncoding != nil { return *v.CrossClusterProcessingQueueStatesEncoding } return } // IsSetCrossClusterProcessingQueueStatesEncoding returns true if CrossClusterProcessingQueueStatesEncoding is not nil. func (v *ShardInfo) IsSetCrossClusterProcessingQueueStatesEncoding() bool { return v != nil && v.CrossClusterProcessingQueueStatesEncoding != nil } // GetQueueStates returns the value of QueueStates if it is set or its // zero value if it is unset. func (v *ShardInfo) GetQueueStates() (o map[int32]*shared.QueueState) { if v != nil && v.QueueStates != nil { return v.QueueStates } return } // IsSetQueueStates returns true if QueueStates is not nil. func (v *ShardInfo) IsSetQueueStates() bool { return v != nil && v.QueueStates != nil } type SignalInfo struct { Version *int64 `json:"version,omitempty"` InitiatedEventBatchID *int64 `json:"initiatedEventBatchID,omitempty"` RequestID *string `json:"requestID,omitempty"` Name *string `json:"name,omitempty"` Input []byte `json:"input,omitempty"` Control []byte `json:"control,omitempty"` } // ToWire translates a SignalInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *SignalInfo) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.InitiatedEventBatchID != nil { w, err = wire.NewValueI64(*(v.InitiatedEventBatchID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 11, Value: w} i++ } if v.RequestID != nil { w, err = wire.NewValueString(*(v.RequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.Name != nil { w, err = wire.NewValueString(*(v.Name)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.Input != nil { w, err = wire.NewValueBinary(v.Input), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.Control != nil { w, err = wire.NewValueBinary(v.Control), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a SignalInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a SignalInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v SignalInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *SignalInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 11: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedEventBatchID = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.RequestID = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.Name = &x if err != nil { return err } } case 16: if field.Value.Type() == wire.TBinary { v.Input, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 18: if field.Value.Type() == wire.TBinary { v.Control, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } } } return nil } // Encode serializes a SignalInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a SignalInfo struct could not be encoded. func (v *SignalInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedEventBatchID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 11, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedEventBatchID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.RequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Name != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.Name)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Input != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Input); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Control != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Control); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a SignalInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a SignalInfo struct could not be generated from the wire // representation. func (v *SignalInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 11 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedEventBatchID = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.RequestID = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.Name = &x if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TBinary: v.Input, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TBinary: v.Control, err = sr.ReadBinary() if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a SignalInfo // struct. func (v *SignalInfo) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.InitiatedEventBatchID != nil { fields[i] = fmt.Sprintf("InitiatedEventBatchID: %v", *(v.InitiatedEventBatchID)) i++ } if v.RequestID != nil { fields[i] = fmt.Sprintf("RequestID: %v", *(v.RequestID)) i++ } if v.Name != nil { fields[i] = fmt.Sprintf("Name: %v", *(v.Name)) i++ } if v.Input != nil { fields[i] = fmt.Sprintf("Input: %v", v.Input) i++ } if v.Control != nil { fields[i] = fmt.Sprintf("Control: %v", v.Control) i++ } return fmt.Sprintf("SignalInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this SignalInfo match the // provided SignalInfo. // // This function performs a deep comparison. func (v *SignalInfo) Equals(rhs *SignalInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.InitiatedEventBatchID, rhs.InitiatedEventBatchID) { return false } if !_String_EqualsPtr(v.RequestID, rhs.RequestID) { return false } if !_String_EqualsPtr(v.Name, rhs.Name) { return false } if !((v.Input == nil && rhs.Input == nil) || (v.Input != nil && rhs.Input != nil && bytes.Equal(v.Input, rhs.Input))) { return false } if !((v.Control == nil && rhs.Control == nil) || (v.Control != nil && rhs.Control != nil && bytes.Equal(v.Control, rhs.Control))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of SignalInfo. func (v *SignalInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.InitiatedEventBatchID != nil { enc.AddInt64("initiatedEventBatchID", *v.InitiatedEventBatchID) } if v.RequestID != nil { enc.AddString("requestID", *v.RequestID) } if v.Name != nil { enc.AddString("name", *v.Name) } if v.Input != nil { enc.AddString("input", base64.StdEncoding.EncodeToString(v.Input)) } if v.Control != nil { enc.AddString("control", base64.StdEncoding.EncodeToString(v.Control)) } return err } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *SignalInfo) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *SignalInfo) IsSetVersion() bool { return v != nil && v.Version != nil } // GetInitiatedEventBatchID returns the value of InitiatedEventBatchID if it is set or its // zero value if it is unset. func (v *SignalInfo) GetInitiatedEventBatchID() (o int64) { if v != nil && v.InitiatedEventBatchID != nil { return *v.InitiatedEventBatchID } return } // IsSetInitiatedEventBatchID returns true if InitiatedEventBatchID is not nil. func (v *SignalInfo) IsSetInitiatedEventBatchID() bool { return v != nil && v.InitiatedEventBatchID != nil } // GetRequestID returns the value of RequestID if it is set or its // zero value if it is unset. func (v *SignalInfo) GetRequestID() (o string) { if v != nil && v.RequestID != nil { return *v.RequestID } return } // IsSetRequestID returns true if RequestID is not nil. func (v *SignalInfo) IsSetRequestID() bool { return v != nil && v.RequestID != nil } // GetName returns the value of Name if it is set or its // zero value if it is unset. func (v *SignalInfo) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // IsSetName returns true if Name is not nil. func (v *SignalInfo) IsSetName() bool { return v != nil && v.Name != nil } // GetInput returns the value of Input if it is set or its // zero value if it is unset. func (v *SignalInfo) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // IsSetInput returns true if Input is not nil. func (v *SignalInfo) IsSetInput() bool { return v != nil && v.Input != nil } // GetControl returns the value of Control if it is set or its // zero value if it is unset. func (v *SignalInfo) GetControl() (o []byte) { if v != nil && v.Control != nil { return v.Control } return } // IsSetControl returns true if Control is not nil. func (v *SignalInfo) IsSetControl() bool { return v != nil && v.Control != nil } type TaskInfo struct { WorkflowID *string `json:"workflowID,omitempty"` RunID []byte `json:"runID,omitempty"` ScheduleID *int64 `json:"scheduleID,omitempty"` ExpiryTimeNanos *int64 `json:"expiryTimeNanos,omitempty"` CreatedTimeNanos *int64 `json:"createdTimeNanos,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` } // ToWire translates a TaskInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskInfo) ToWire() (wire.Value, error) { var ( fields [6]wire.Field i int = 0 w wire.Value err error ) if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueBinary(v.RunID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.ScheduleID != nil { w, err = wire.NewValueI64(*(v.ScheduleID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 13, Value: w} i++ } if v.ExpiryTimeNanos != nil { w, err = wire.NewValueI64(*(v.ExpiryTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.CreatedTimeNanos != nil { w, err = wire.NewValueI64(*(v.CreatedTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 15, Value: w} i++ } if v.PartitionConfig != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.PartitionConfig)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 17, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TaskInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TBinary { v.RunID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 13: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduleID = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExpiryTimeNanos = &x if err != nil { return err } } case 15: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CreatedTimeNanos = &x if err != nil { return err } } case 17: if field.Value.Type() == wire.TMap { v.PartitionConfig, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } // Encode serializes a TaskInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskInfo struct could not be encoded. func (v *TaskInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.RunID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 13, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduleID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExpiryTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExpiryTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CreatedTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 15, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CreatedTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PartitionConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 17, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.PartitionConfig, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TaskInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskInfo struct could not be generated from the wire // representation. func (v *TaskInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TBinary: v.RunID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 13 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduleID = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExpiryTimeNanos = &x if err != nil { return err } case fh.ID == 15 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CreatedTimeNanos = &x if err != nil { return err } case fh.ID == 17 && fh.Type == wire.TMap: v.PartitionConfig, err = _Map_String_String_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskInfo // struct. func (v *TaskInfo) String() string { if v == nil { return "" } var fields [6]string i := 0 if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", v.RunID) i++ } if v.ScheduleID != nil { fields[i] = fmt.Sprintf("ScheduleID: %v", *(v.ScheduleID)) i++ } if v.ExpiryTimeNanos != nil { fields[i] = fmt.Sprintf("ExpiryTimeNanos: %v", *(v.ExpiryTimeNanos)) i++ } if v.CreatedTimeNanos != nil { fields[i] = fmt.Sprintf("CreatedTimeNanos: %v", *(v.CreatedTimeNanos)) i++ } if v.PartitionConfig != nil { fields[i] = fmt.Sprintf("PartitionConfig: %v", v.PartitionConfig) i++ } return fmt.Sprintf("TaskInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TaskInfo match the // provided TaskInfo. // // This function performs a deep comparison. func (v *TaskInfo) Equals(rhs *TaskInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !((v.RunID == nil && rhs.RunID == nil) || (v.RunID != nil && rhs.RunID != nil && bytes.Equal(v.RunID, rhs.RunID))) { return false } if !_I64_EqualsPtr(v.ScheduleID, rhs.ScheduleID) { return false } if !_I64_EqualsPtr(v.ExpiryTimeNanos, rhs.ExpiryTimeNanos) { return false } if !_I64_EqualsPtr(v.CreatedTimeNanos, rhs.CreatedTimeNanos) { return false } if !((v.PartitionConfig == nil && rhs.PartitionConfig == nil) || (v.PartitionConfig != nil && rhs.PartitionConfig != nil && _Map_String_String_Equals(v.PartitionConfig, rhs.PartitionConfig))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskInfo. func (v *TaskInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", base64.StdEncoding.EncodeToString(v.RunID)) } if v.ScheduleID != nil { enc.AddInt64("scheduleID", *v.ScheduleID) } if v.ExpiryTimeNanos != nil { enc.AddInt64("expiryTimeNanos", *v.ExpiryTimeNanos) } if v.CreatedTimeNanos != nil { enc.AddInt64("createdTimeNanos", *v.CreatedTimeNanos) } if v.PartitionConfig != nil { err = multierr.Append(err, enc.AddObject("partitionConfig", (_Map_String_String_Zapper)(v.PartitionConfig))) } return err } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *TaskInfo) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *TaskInfo) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *TaskInfo) GetRunID() (o []byte) { if v != nil && v.RunID != nil { return v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *TaskInfo) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetScheduleID returns the value of ScheduleID if it is set or its // zero value if it is unset. func (v *TaskInfo) GetScheduleID() (o int64) { if v != nil && v.ScheduleID != nil { return *v.ScheduleID } return } // IsSetScheduleID returns true if ScheduleID is not nil. func (v *TaskInfo) IsSetScheduleID() bool { return v != nil && v.ScheduleID != nil } // GetExpiryTimeNanos returns the value of ExpiryTimeNanos if it is set or its // zero value if it is unset. func (v *TaskInfo) GetExpiryTimeNanos() (o int64) { if v != nil && v.ExpiryTimeNanos != nil { return *v.ExpiryTimeNanos } return } // IsSetExpiryTimeNanos returns true if ExpiryTimeNanos is not nil. func (v *TaskInfo) IsSetExpiryTimeNanos() bool { return v != nil && v.ExpiryTimeNanos != nil } // GetCreatedTimeNanos returns the value of CreatedTimeNanos if it is set or its // zero value if it is unset. func (v *TaskInfo) GetCreatedTimeNanos() (o int64) { if v != nil && v.CreatedTimeNanos != nil { return *v.CreatedTimeNanos } return } // IsSetCreatedTimeNanos returns true if CreatedTimeNanos is not nil. func (v *TaskInfo) IsSetCreatedTimeNanos() bool { return v != nil && v.CreatedTimeNanos != nil } // GetPartitionConfig returns the value of PartitionConfig if it is set or its // zero value if it is unset. func (v *TaskInfo) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // IsSetPartitionConfig returns true if PartitionConfig is not nil. func (v *TaskInfo) IsSetPartitionConfig() bool { return v != nil && v.PartitionConfig != nil } type TaskListInfo struct { Kind *int16 `json:"kind,omitempty"` AckLevel *int64 `json:"ackLevel,omitempty"` ExpiryTimeNanos *int64 `json:"expiryTimeNanos,omitempty"` LastUpdatedNanos *int64 `json:"lastUpdatedNanos,omitempty"` AdaptivePartitionConfig *TaskListPartitionConfig `json:"adaptivePartitionConfig,omitempty"` } // ToWire translates a TaskListInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskListInfo) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Kind != nil { w, err = wire.NewValueI16(*(v.Kind)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.AckLevel != nil { w, err = wire.NewValueI64(*(v.AckLevel)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.ExpiryTimeNanos != nil { w, err = wire.NewValueI64(*(v.ExpiryTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.LastUpdatedNanos != nil { w, err = wire.NewValueI64(*(v.LastUpdatedNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.AdaptivePartitionConfig != nil { w, err = v.AdaptivePartitionConfig.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TaskListPartitionConfig_Read(w wire.Value) (*TaskListPartitionConfig, error) { var v TaskListPartitionConfig err := v.FromWire(w) return &v, err } // FromWire deserializes a TaskListInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskListInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskListInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskListInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.Kind = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.AckLevel = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExpiryTimeNanos = &x if err != nil { return err } } case 16: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastUpdatedNanos = &x if err != nil { return err } } case 18: if field.Value.Type() == wire.TStruct { v.AdaptivePartitionConfig, err = _TaskListPartitionConfig_Read(field.Value) if err != nil { return err } } } } return nil } // Encode serializes a TaskListInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskListInfo struct could not be encoded. func (v *TaskListInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Kind != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.Kind)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AckLevel != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.AckLevel)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExpiryTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExpiryTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastUpdatedNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastUpdatedNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AdaptivePartitionConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TStruct}); err != nil { return err } if err := v.AdaptivePartitionConfig.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TaskListPartitionConfig_Decode(sr stream.Reader) (*TaskListPartitionConfig, error) { var v TaskListPartitionConfig err := v.Decode(sr) return &v, err } // Decode deserializes a TaskListInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskListInfo struct could not be generated from the wire // representation. func (v *TaskListInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.Kind = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.AckLevel = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExpiryTimeNanos = &x if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastUpdatedNanos = &x if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TStruct: v.AdaptivePartitionConfig, err = _TaskListPartitionConfig_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskListInfo // struct. func (v *TaskListInfo) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Kind != nil { fields[i] = fmt.Sprintf("Kind: %v", *(v.Kind)) i++ } if v.AckLevel != nil { fields[i] = fmt.Sprintf("AckLevel: %v", *(v.AckLevel)) i++ } if v.ExpiryTimeNanos != nil { fields[i] = fmt.Sprintf("ExpiryTimeNanos: %v", *(v.ExpiryTimeNanos)) i++ } if v.LastUpdatedNanos != nil { fields[i] = fmt.Sprintf("LastUpdatedNanos: %v", *(v.LastUpdatedNanos)) i++ } if v.AdaptivePartitionConfig != nil { fields[i] = fmt.Sprintf("AdaptivePartitionConfig: %v", v.AdaptivePartitionConfig) i++ } return fmt.Sprintf("TaskListInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TaskListInfo match the // provided TaskListInfo. // // This function performs a deep comparison. func (v *TaskListInfo) Equals(rhs *TaskListInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I16_EqualsPtr(v.Kind, rhs.Kind) { return false } if !_I64_EqualsPtr(v.AckLevel, rhs.AckLevel) { return false } if !_I64_EqualsPtr(v.ExpiryTimeNanos, rhs.ExpiryTimeNanos) { return false } if !_I64_EqualsPtr(v.LastUpdatedNanos, rhs.LastUpdatedNanos) { return false } if !((v.AdaptivePartitionConfig == nil && rhs.AdaptivePartitionConfig == nil) || (v.AdaptivePartitionConfig != nil && rhs.AdaptivePartitionConfig != nil && v.AdaptivePartitionConfig.Equals(rhs.AdaptivePartitionConfig))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskListInfo. func (v *TaskListInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Kind != nil { enc.AddInt16("kind", *v.Kind) } if v.AckLevel != nil { enc.AddInt64("ackLevel", *v.AckLevel) } if v.ExpiryTimeNanos != nil { enc.AddInt64("expiryTimeNanos", *v.ExpiryTimeNanos) } if v.LastUpdatedNanos != nil { enc.AddInt64("lastUpdatedNanos", *v.LastUpdatedNanos) } if v.AdaptivePartitionConfig != nil { err = multierr.Append(err, enc.AddObject("adaptivePartitionConfig", v.AdaptivePartitionConfig)) } return err } // GetKind returns the value of Kind if it is set or its // zero value if it is unset. func (v *TaskListInfo) GetKind() (o int16) { if v != nil && v.Kind != nil { return *v.Kind } return } // IsSetKind returns true if Kind is not nil. func (v *TaskListInfo) IsSetKind() bool { return v != nil && v.Kind != nil } // GetAckLevel returns the value of AckLevel if it is set or its // zero value if it is unset. func (v *TaskListInfo) GetAckLevel() (o int64) { if v != nil && v.AckLevel != nil { return *v.AckLevel } return } // IsSetAckLevel returns true if AckLevel is not nil. func (v *TaskListInfo) IsSetAckLevel() bool { return v != nil && v.AckLevel != nil } // GetExpiryTimeNanos returns the value of ExpiryTimeNanos if it is set or its // zero value if it is unset. func (v *TaskListInfo) GetExpiryTimeNanos() (o int64) { if v != nil && v.ExpiryTimeNanos != nil { return *v.ExpiryTimeNanos } return } // IsSetExpiryTimeNanos returns true if ExpiryTimeNanos is not nil. func (v *TaskListInfo) IsSetExpiryTimeNanos() bool { return v != nil && v.ExpiryTimeNanos != nil } // GetLastUpdatedNanos returns the value of LastUpdatedNanos if it is set or its // zero value if it is unset. func (v *TaskListInfo) GetLastUpdatedNanos() (o int64) { if v != nil && v.LastUpdatedNanos != nil { return *v.LastUpdatedNanos } return } // IsSetLastUpdatedNanos returns true if LastUpdatedNanos is not nil. func (v *TaskListInfo) IsSetLastUpdatedNanos() bool { return v != nil && v.LastUpdatedNanos != nil } // GetAdaptivePartitionConfig returns the value of AdaptivePartitionConfig if it is set or its // zero value if it is unset. func (v *TaskListInfo) GetAdaptivePartitionConfig() (o *TaskListPartitionConfig) { if v != nil && v.AdaptivePartitionConfig != nil { return v.AdaptivePartitionConfig } return } // IsSetAdaptivePartitionConfig returns true if AdaptivePartitionConfig is not nil. func (v *TaskListInfo) IsSetAdaptivePartitionConfig() bool { return v != nil && v.AdaptivePartitionConfig != nil } type TaskListPartition struct { IsolationGroups []string `json:"isolationGroups,omitempty"` } // ToWire translates a TaskListPartition struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskListPartition) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.IsolationGroups != nil { w, err = wire.NewValueList(_List_String_ValueList(v.IsolationGroups)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TaskListPartition struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskListPartition struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskListPartition // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskListPartition) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.IsolationGroups, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } // Encode serializes a TaskListPartition struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskListPartition struct could not be encoded. func (v *TaskListPartition) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.IsolationGroups != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.IsolationGroups, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TaskListPartition struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskListPartition struct could not be generated from the wire // representation. func (v *TaskListPartition) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.IsolationGroups, err = _List_String_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskListPartition // struct. func (v *TaskListPartition) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.IsolationGroups != nil { fields[i] = fmt.Sprintf("IsolationGroups: %v", v.IsolationGroups) i++ } return fmt.Sprintf("TaskListPartition{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TaskListPartition match the // provided TaskListPartition. // // This function performs a deep comparison. func (v *TaskListPartition) Equals(rhs *TaskListPartition) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.IsolationGroups == nil && rhs.IsolationGroups == nil) || (v.IsolationGroups != nil && rhs.IsolationGroups != nil && _List_String_Equals(v.IsolationGroups, rhs.IsolationGroups))) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskListPartition. func (v *TaskListPartition) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.IsolationGroups != nil { err = multierr.Append(err, enc.AddArray("isolationGroups", (_List_String_Zapper)(v.IsolationGroups))) } return err } // GetIsolationGroups returns the value of IsolationGroups if it is set or its // zero value if it is unset. func (v *TaskListPartition) GetIsolationGroups() (o []string) { if v != nil && v.IsolationGroups != nil { return v.IsolationGroups } return } // IsSetIsolationGroups returns true if IsolationGroups is not nil. func (v *TaskListPartition) IsSetIsolationGroups() bool { return v != nil && v.IsolationGroups != nil } type TaskListPartitionConfig struct { Version *int64 `json:"version,omitempty"` NumReadPartitions *int32 `json:"numReadPartitions,omitempty"` NumWritePartitions *int32 `json:"numWritePartitions,omitempty"` ReadPartitions map[int32]*TaskListPartition `json:"readPartitions,omitempty"` WritePartitions map[int32]*TaskListPartition `json:"writePartitions,omitempty"` } type _Map_I32_TaskListPartition_MapItemList map[int32]*TaskListPartition func (m _Map_I32_TaskListPartition_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[int32]*TaskListPartition', key [%v]: value is nil", k) } kw, err := wire.NewValueI32(k), error(nil) if err != nil { return err } vw, err := v.ToWire() if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_I32_TaskListPartition_MapItemList) Size() int { return len(m) } func (_Map_I32_TaskListPartition_MapItemList) KeyType() wire.Type { return wire.TI32 } func (_Map_I32_TaskListPartition_MapItemList) ValueType() wire.Type { return wire.TStruct } func (_Map_I32_TaskListPartition_MapItemList) Close() {} // ToWire translates a TaskListPartitionConfig struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TaskListPartitionConfig) ToWire() (wire.Value, error) { var ( fields [5]wire.Field i int = 0 w wire.Value err error ) if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.NumReadPartitions != nil { w, err = wire.NewValueI32(*(v.NumReadPartitions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.NumWritePartitions != nil { w, err = wire.NewValueI32(*(v.NumWritePartitions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.ReadPartitions != nil { w, err = wire.NewValueMap(_Map_I32_TaskListPartition_MapItemList(v.ReadPartitions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.WritePartitions != nil { w, err = wire.NewValueMap(_Map_I32_TaskListPartition_MapItemList(v.WritePartitions)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TaskListPartition_Read(w wire.Value) (*TaskListPartition, error) { var v TaskListPartition err := v.FromWire(w) return &v, err } func _Map_I32_TaskListPartition_Read(m wire.MapItemList) (map[int32]*TaskListPartition, error) { if m.KeyType() != wire.TI32 { return nil, nil } if m.ValueType() != wire.TStruct { return nil, nil } o := make(map[int32]*TaskListPartition, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetI32(), error(nil) if err != nil { return err } v, err := _TaskListPartition_Read(x.Value) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } // FromWire deserializes a TaskListPartitionConfig struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TaskListPartitionConfig struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TaskListPartitionConfig // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TaskListPartitionConfig) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.NumReadPartitions = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.NumWritePartitions = &x if err != nil { return err } } case 16: if field.Value.Type() == wire.TMap { v.ReadPartitions, err = _Map_I32_TaskListPartition_Read(field.Value.GetMap()) if err != nil { return err } } case 18: if field.Value.Type() == wire.TMap { v.WritePartitions, err = _Map_I32_TaskListPartition_Read(field.Value.GetMap()) if err != nil { return err } } } } return nil } func _Map_I32_TaskListPartition_Encode(val map[int32]*TaskListPartition, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TI32, ValueType: wire.TStruct, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[int32]*TaskListPartition', key [%v]: value is nil", k) } if err := sw.WriteInt32(k); err != nil { return err } if err := v.Encode(sw); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a TaskListPartitionConfig struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TaskListPartitionConfig struct could not be encoded. func (v *TaskListPartitionConfig) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NumReadPartitions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.NumReadPartitions)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.NumWritePartitions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.NumWritePartitions)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ReadPartitions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TMap}); err != nil { return err } if err := _Map_I32_TaskListPartition_Encode(v.ReadPartitions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WritePartitions != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TMap}); err != nil { return err } if err := _Map_I32_TaskListPartition_Encode(v.WritePartitions, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TaskListPartition_Decode(sr stream.Reader) (*TaskListPartition, error) { var v TaskListPartition err := v.Decode(sr) return &v, err } func _Map_I32_TaskListPartition_Decode(sr stream.Reader) (map[int32]*TaskListPartition, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TI32 || mh.ValueType != wire.TStruct { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[int32]*TaskListPartition, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadInt32() if err != nil { return nil, err } v, err := _TaskListPartition_Decode(sr) if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a TaskListPartitionConfig struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TaskListPartitionConfig struct could not be generated from the wire // representation. func (v *TaskListPartitionConfig) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.NumReadPartitions = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.NumWritePartitions = &x if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TMap: v.ReadPartitions, err = _Map_I32_TaskListPartition_Decode(sr) if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TMap: v.WritePartitions, err = _Map_I32_TaskListPartition_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TaskListPartitionConfig // struct. func (v *TaskListPartitionConfig) String() string { if v == nil { return "" } var fields [5]string i := 0 if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.NumReadPartitions != nil { fields[i] = fmt.Sprintf("NumReadPartitions: %v", *(v.NumReadPartitions)) i++ } if v.NumWritePartitions != nil { fields[i] = fmt.Sprintf("NumWritePartitions: %v", *(v.NumWritePartitions)) i++ } if v.ReadPartitions != nil { fields[i] = fmt.Sprintf("ReadPartitions: %v", v.ReadPartitions) i++ } if v.WritePartitions != nil { fields[i] = fmt.Sprintf("WritePartitions: %v", v.WritePartitions) i++ } return fmt.Sprintf("TaskListPartitionConfig{%v}", strings.Join(fields[:i], ", ")) } func _Map_I32_TaskListPartition_Equals(lhs, rhs map[int32]*TaskListPartition) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this TaskListPartitionConfig match the // provided TaskListPartitionConfig. // // This function performs a deep comparison. func (v *TaskListPartitionConfig) Equals(rhs *TaskListPartitionConfig) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I32_EqualsPtr(v.NumReadPartitions, rhs.NumReadPartitions) { return false } if !_I32_EqualsPtr(v.NumWritePartitions, rhs.NumWritePartitions) { return false } if !((v.ReadPartitions == nil && rhs.ReadPartitions == nil) || (v.ReadPartitions != nil && rhs.ReadPartitions != nil && _Map_I32_TaskListPartition_Equals(v.ReadPartitions, rhs.ReadPartitions))) { return false } if !((v.WritePartitions == nil && rhs.WritePartitions == nil) || (v.WritePartitions != nil && rhs.WritePartitions != nil && _Map_I32_TaskListPartition_Equals(v.WritePartitions, rhs.WritePartitions))) { return false } return true } type _Map_I32_TaskListPartition_Item_Zapper struct { Key int32 Value *TaskListPartition } // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_TaskListPartition_Item_Zapper. func (v _Map_I32_TaskListPartition_Item_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { enc.AddInt32("key", v.Key) err = multierr.Append(err, enc.AddObject("value", v.Value)) return err } type _Map_I32_TaskListPartition_Zapper map[int32]*TaskListPartition // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Map_I32_TaskListPartition_Zapper. func (m _Map_I32_TaskListPartition_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for k, v := range m { err = multierr.Append(err, enc.AppendObject(_Map_I32_TaskListPartition_Item_Zapper{Key: k, Value: v})) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TaskListPartitionConfig. func (v *TaskListPartitionConfig) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.NumReadPartitions != nil { enc.AddInt32("numReadPartitions", *v.NumReadPartitions) } if v.NumWritePartitions != nil { enc.AddInt32("numWritePartitions", *v.NumWritePartitions) } if v.ReadPartitions != nil { err = multierr.Append(err, enc.AddArray("readPartitions", (_Map_I32_TaskListPartition_Zapper)(v.ReadPartitions))) } if v.WritePartitions != nil { err = multierr.Append(err, enc.AddArray("writePartitions", (_Map_I32_TaskListPartition_Zapper)(v.WritePartitions))) } return err } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *TaskListPartitionConfig) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *TaskListPartitionConfig) IsSetVersion() bool { return v != nil && v.Version != nil } // GetNumReadPartitions returns the value of NumReadPartitions if it is set or its // zero value if it is unset. func (v *TaskListPartitionConfig) GetNumReadPartitions() (o int32) { if v != nil && v.NumReadPartitions != nil { return *v.NumReadPartitions } return } // IsSetNumReadPartitions returns true if NumReadPartitions is not nil. func (v *TaskListPartitionConfig) IsSetNumReadPartitions() bool { return v != nil && v.NumReadPartitions != nil } // GetNumWritePartitions returns the value of NumWritePartitions if it is set or its // zero value if it is unset. func (v *TaskListPartitionConfig) GetNumWritePartitions() (o int32) { if v != nil && v.NumWritePartitions != nil { return *v.NumWritePartitions } return } // IsSetNumWritePartitions returns true if NumWritePartitions is not nil. func (v *TaskListPartitionConfig) IsSetNumWritePartitions() bool { return v != nil && v.NumWritePartitions != nil } // GetReadPartitions returns the value of ReadPartitions if it is set or its // zero value if it is unset. func (v *TaskListPartitionConfig) GetReadPartitions() (o map[int32]*TaskListPartition) { if v != nil && v.ReadPartitions != nil { return v.ReadPartitions } return } // IsSetReadPartitions returns true if ReadPartitions is not nil. func (v *TaskListPartitionConfig) IsSetReadPartitions() bool { return v != nil && v.ReadPartitions != nil } // GetWritePartitions returns the value of WritePartitions if it is set or its // zero value if it is unset. func (v *TaskListPartitionConfig) GetWritePartitions() (o map[int32]*TaskListPartition) { if v != nil && v.WritePartitions != nil { return v.WritePartitions } return } // IsSetWritePartitions returns true if WritePartitions is not nil. func (v *TaskListPartitionConfig) IsSetWritePartitions() bool { return v != nil && v.WritePartitions != nil } type TimerInfo struct { Version *int64 `json:"version,omitempty"` StartedID *int64 `json:"startedID,omitempty"` ExpiryTimeNanos *int64 `json:"expiryTimeNanos,omitempty"` TaskID *int64 `json:"taskID,omitempty"` } // ToWire translates a TimerInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TimerInfo) ToWire() (wire.Value, error) { var ( fields [4]wire.Field i int = 0 w wire.Value err error ) if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.StartedID != nil { w, err = wire.NewValueI64(*(v.StartedID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.ExpiryTimeNanos != nil { w, err = wire.NewValueI64(*(v.ExpiryTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.TaskID != nil { w, err = wire.NewValueI64(*(v.TaskID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TimerInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TimerInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TimerInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TimerInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 12: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartedID = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ExpiryTimeNanos = &x if err != nil { return err } } case 16: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskID = &x if err != nil { return err } } } } return nil } // Encode serializes a TimerInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TimerInfo struct could not be encoded. func (v *TimerInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartedID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartedID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExpiryTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ExpiryTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TimerInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TimerInfo struct could not be generated from the wire // representation. func (v *TimerInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartedID = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ExpiryTimeNanos = &x if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskID = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TimerInfo // struct. func (v *TimerInfo) String() string { if v == nil { return "" } var fields [4]string i := 0 if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.StartedID != nil { fields[i] = fmt.Sprintf("StartedID: %v", *(v.StartedID)) i++ } if v.ExpiryTimeNanos != nil { fields[i] = fmt.Sprintf("ExpiryTimeNanos: %v", *(v.ExpiryTimeNanos)) i++ } if v.TaskID != nil { fields[i] = fmt.Sprintf("TaskID: %v", *(v.TaskID)) i++ } return fmt.Sprintf("TimerInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TimerInfo match the // provided TimerInfo. // // This function performs a deep comparison. func (v *TimerInfo) Equals(rhs *TimerInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.StartedID, rhs.StartedID) { return false } if !_I64_EqualsPtr(v.ExpiryTimeNanos, rhs.ExpiryTimeNanos) { return false } if !_I64_EqualsPtr(v.TaskID, rhs.TaskID) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TimerInfo. func (v *TimerInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.StartedID != nil { enc.AddInt64("startedID", *v.StartedID) } if v.ExpiryTimeNanos != nil { enc.AddInt64("expiryTimeNanos", *v.ExpiryTimeNanos) } if v.TaskID != nil { enc.AddInt64("taskID", *v.TaskID) } return err } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *TimerInfo) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *TimerInfo) IsSetVersion() bool { return v != nil && v.Version != nil } // GetStartedID returns the value of StartedID if it is set or its // zero value if it is unset. func (v *TimerInfo) GetStartedID() (o int64) { if v != nil && v.StartedID != nil { return *v.StartedID } return } // IsSetStartedID returns true if StartedID is not nil. func (v *TimerInfo) IsSetStartedID() bool { return v != nil && v.StartedID != nil } // GetExpiryTimeNanos returns the value of ExpiryTimeNanos if it is set or its // zero value if it is unset. func (v *TimerInfo) GetExpiryTimeNanos() (o int64) { if v != nil && v.ExpiryTimeNanos != nil { return *v.ExpiryTimeNanos } return } // IsSetExpiryTimeNanos returns true if ExpiryTimeNanos is not nil. func (v *TimerInfo) IsSetExpiryTimeNanos() bool { return v != nil && v.ExpiryTimeNanos != nil } // GetTaskID returns the value of TaskID if it is set or its // zero value if it is unset. func (v *TimerInfo) GetTaskID() (o int64) { if v != nil && v.TaskID != nil { return *v.TaskID } return } // IsSetTaskID returns true if TaskID is not nil. func (v *TimerInfo) IsSetTaskID() bool { return v != nil && v.TaskID != nil } type TimerReference struct { TaskID *int64 `json:"taskID,omitempty"` VisibilityTimestamp *int64 `json:"visibilityTimestamp,omitempty"` TimeoutType *int16 `json:"TimeoutType,omitempty"` } // ToWire translates a TimerReference struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TimerReference) ToWire() (wire.Value, error) { var ( fields [3]wire.Field i int = 0 w wire.Value err error ) if v.TaskID != nil { w, err = wire.NewValueI64(*(v.TaskID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.VisibilityTimestamp != nil { w, err = wire.NewValueI64(*(v.VisibilityTimestamp)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 11, Value: w} i++ } if v.TimeoutType != nil { w, err = wire.NewValueI16(*(v.TimeoutType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 13, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TimerReference struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TimerReference struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TimerReference // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TimerReference) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.TaskID = &x if err != nil { return err } } case 11: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.VisibilityTimestamp = &x if err != nil { return err } } case 13: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.TimeoutType = &x if err != nil { return err } } } } return nil } // Encode serializes a TimerReference struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TimerReference struct could not be encoded. func (v *TimerReference) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.TaskID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.TaskID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityTimestamp != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 11, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.VisibilityTimestamp)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimeoutType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 13, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.TimeoutType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TimerReference struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TimerReference struct could not be generated from the wire // representation. func (v *TimerReference) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.TaskID = &x if err != nil { return err } case fh.ID == 11 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.VisibilityTimestamp = &x if err != nil { return err } case fh.ID == 13 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.TimeoutType = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TimerReference // struct. func (v *TimerReference) String() string { if v == nil { return "" } var fields [3]string i := 0 if v.TaskID != nil { fields[i] = fmt.Sprintf("TaskID: %v", *(v.TaskID)) i++ } if v.VisibilityTimestamp != nil { fields[i] = fmt.Sprintf("VisibilityTimestamp: %v", *(v.VisibilityTimestamp)) i++ } if v.TimeoutType != nil { fields[i] = fmt.Sprintf("TimeoutType: %v", *(v.TimeoutType)) i++ } return fmt.Sprintf("TimerReference{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TimerReference match the // provided TimerReference. // // This function performs a deep comparison. func (v *TimerReference) Equals(rhs *TimerReference) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !_I64_EqualsPtr(v.TaskID, rhs.TaskID) { return false } if !_I64_EqualsPtr(v.VisibilityTimestamp, rhs.VisibilityTimestamp) { return false } if !_I16_EqualsPtr(v.TimeoutType, rhs.TimeoutType) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TimerReference. func (v *TimerReference) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.TaskID != nil { enc.AddInt64("taskID", *v.TaskID) } if v.VisibilityTimestamp != nil { enc.AddInt64("visibilityTimestamp", *v.VisibilityTimestamp) } if v.TimeoutType != nil { enc.AddInt16("TimeoutType", *v.TimeoutType) } return err } // GetTaskID returns the value of TaskID if it is set or its // zero value if it is unset. func (v *TimerReference) GetTaskID() (o int64) { if v != nil && v.TaskID != nil { return *v.TaskID } return } // IsSetTaskID returns true if TaskID is not nil. func (v *TimerReference) IsSetTaskID() bool { return v != nil && v.TaskID != nil } // GetVisibilityTimestamp returns the value of VisibilityTimestamp if it is set or its // zero value if it is unset. func (v *TimerReference) GetVisibilityTimestamp() (o int64) { if v != nil && v.VisibilityTimestamp != nil { return *v.VisibilityTimestamp } return } // IsSetVisibilityTimestamp returns true if VisibilityTimestamp is not nil. func (v *TimerReference) IsSetVisibilityTimestamp() bool { return v != nil && v.VisibilityTimestamp != nil } // GetTimeoutType returns the value of TimeoutType if it is set or its // zero value if it is unset. func (v *TimerReference) GetTimeoutType() (o int16) { if v != nil && v.TimeoutType != nil { return *v.TimeoutType } return } // IsSetTimeoutType returns true if TimeoutType is not nil. func (v *TimerReference) IsSetTimeoutType() bool { return v != nil && v.TimeoutType != nil } type TimerTaskInfo struct { DomainID []byte `json:"domainID,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID []byte `json:"runID,omitempty"` TaskType *int16 `json:"taskType,omitempty"` TimeoutType *int16 `json:"timeoutType,omitempty"` Version *int64 `json:"version,omitempty"` ScheduleAttempt *int64 `json:"scheduleAttempt,omitempty"` EventID *int64 `json:"eventID,omitempty"` TaskList *string `json:"taskList,omitempty"` } // ToWire translates a TimerTaskInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TimerTaskInfo) ToWire() (wire.Value, error) { var ( fields [9]wire.Field i int = 0 w wire.Value err error ) if v.DomainID != nil { w, err = wire.NewValueBinary(v.DomainID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueBinary(v.RunID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.TaskType != nil { w, err = wire.NewValueI16(*(v.TaskType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.TimeoutType != nil { w, err = wire.NewValueI16(*(v.TimeoutType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.ScheduleAttempt != nil { w, err = wire.NewValueI64(*(v.ScheduleAttempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 22, Value: w} i++ } if v.EventID != nil { w, err = wire.NewValueI64(*(v.EventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 24, Value: w} i++ } if v.TaskList != nil { w, err = wire.NewValueString(*(v.TaskList)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 26, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } // FromWire deserializes a TimerTaskInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TimerTaskInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TimerTaskInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TimerTaskInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.DomainID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 12: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TBinary { v.RunID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 16: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.TaskType = &x if err != nil { return err } } case 18: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.TimeoutType = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 22: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduleAttempt = &x if err != nil { return err } } case 24: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.EventID = &x if err != nil { return err } } case 26: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TaskList = &x if err != nil { return err } } } } return nil } // Encode serializes a TimerTaskInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TimerTaskInfo struct could not be encoded. func (v *TimerTaskInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.DomainID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.RunID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.TaskType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TimeoutType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.TimeoutType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleAttempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 22, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduleAttempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 24, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.EventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 26, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TaskList)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } // Decode deserializes a TimerTaskInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TimerTaskInfo struct could not be generated from the wire // representation. func (v *TimerTaskInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.DomainID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TBinary: v.RunID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.TaskType = &x if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.TimeoutType = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 22 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduleAttempt = &x if err != nil { return err } case fh.ID == 24 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.EventID = &x if err != nil { return err } case fh.ID == 26 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TaskList = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TimerTaskInfo // struct. func (v *TimerTaskInfo) String() string { if v == nil { return "" } var fields [9]string i := 0 if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", v.DomainID) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", v.RunID) i++ } if v.TaskType != nil { fields[i] = fmt.Sprintf("TaskType: %v", *(v.TaskType)) i++ } if v.TimeoutType != nil { fields[i] = fmt.Sprintf("TimeoutType: %v", *(v.TimeoutType)) i++ } if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.ScheduleAttempt != nil { fields[i] = fmt.Sprintf("ScheduleAttempt: %v", *(v.ScheduleAttempt)) i++ } if v.EventID != nil { fields[i] = fmt.Sprintf("EventID: %v", *(v.EventID)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", *(v.TaskList)) i++ } return fmt.Sprintf("TimerTaskInfo{%v}", strings.Join(fields[:i], ", ")) } // Equals returns true if all the fields of this TimerTaskInfo match the // provided TimerTaskInfo. // // This function performs a deep comparison. func (v *TimerTaskInfo) Equals(rhs *TimerTaskInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DomainID == nil && rhs.DomainID == nil) || (v.DomainID != nil && rhs.DomainID != nil && bytes.Equal(v.DomainID, rhs.DomainID))) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !((v.RunID == nil && rhs.RunID == nil) || (v.RunID != nil && rhs.RunID != nil && bytes.Equal(v.RunID, rhs.RunID))) { return false } if !_I16_EqualsPtr(v.TaskType, rhs.TaskType) { return false } if !_I16_EqualsPtr(v.TimeoutType, rhs.TimeoutType) { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.ScheduleAttempt, rhs.ScheduleAttempt) { return false } if !_I64_EqualsPtr(v.EventID, rhs.EventID) { return false } if !_String_EqualsPtr(v.TaskList, rhs.TaskList) { return false } return true } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TimerTaskInfo. func (v *TimerTaskInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainID != nil { enc.AddString("domainID", base64.StdEncoding.EncodeToString(v.DomainID)) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", base64.StdEncoding.EncodeToString(v.RunID)) } if v.TaskType != nil { enc.AddInt16("taskType", *v.TaskType) } if v.TimeoutType != nil { enc.AddInt16("timeoutType", *v.TimeoutType) } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.ScheduleAttempt != nil { enc.AddInt64("scheduleAttempt", *v.ScheduleAttempt) } if v.EventID != nil { enc.AddInt64("eventID", *v.EventID) } if v.TaskList != nil { enc.AddString("taskList", *v.TaskList) } return err } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *TimerTaskInfo) GetDomainID() (o []byte) { if v != nil && v.DomainID != nil { return v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *TimerTaskInfo) IsSetDomainID() bool { return v != nil && v.DomainID != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *TimerTaskInfo) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *TimerTaskInfo) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *TimerTaskInfo) GetRunID() (o []byte) { if v != nil && v.RunID != nil { return v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *TimerTaskInfo) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetTaskType returns the value of TaskType if it is set or its // zero value if it is unset. func (v *TimerTaskInfo) GetTaskType() (o int16) { if v != nil && v.TaskType != nil { return *v.TaskType } return } // IsSetTaskType returns true if TaskType is not nil. func (v *TimerTaskInfo) IsSetTaskType() bool { return v != nil && v.TaskType != nil } // GetTimeoutType returns the value of TimeoutType if it is set or its // zero value if it is unset. func (v *TimerTaskInfo) GetTimeoutType() (o int16) { if v != nil && v.TimeoutType != nil { return *v.TimeoutType } return } // IsSetTimeoutType returns true if TimeoutType is not nil. func (v *TimerTaskInfo) IsSetTimeoutType() bool { return v != nil && v.TimeoutType != nil } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *TimerTaskInfo) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *TimerTaskInfo) IsSetVersion() bool { return v != nil && v.Version != nil } // GetScheduleAttempt returns the value of ScheduleAttempt if it is set or its // zero value if it is unset. func (v *TimerTaskInfo) GetScheduleAttempt() (o int64) { if v != nil && v.ScheduleAttempt != nil { return *v.ScheduleAttempt } return } // IsSetScheduleAttempt returns true if ScheduleAttempt is not nil. func (v *TimerTaskInfo) IsSetScheduleAttempt() bool { return v != nil && v.ScheduleAttempt != nil } // GetEventID returns the value of EventID if it is set or its // zero value if it is unset. func (v *TimerTaskInfo) GetEventID() (o int64) { if v != nil && v.EventID != nil { return *v.EventID } return } // IsSetEventID returns true if EventID is not nil. func (v *TimerTaskInfo) IsSetEventID() bool { return v != nil && v.EventID != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *TimerTaskInfo) GetTaskList() (o string) { if v != nil && v.TaskList != nil { return *v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *TimerTaskInfo) IsSetTaskList() bool { return v != nil && v.TaskList != nil } type TransferTaskInfo struct { DomainID []byte `json:"domainID,omitempty"` WorkflowID *string `json:"workflowID,omitempty"` RunID []byte `json:"runID,omitempty"` TaskType *int16 `json:"taskType,omitempty"` TargetDomainID []byte `json:"targetDomainID,omitempty"` TargetWorkflowID *string `json:"targetWorkflowID,omitempty"` TargetRunID []byte `json:"targetRunID,omitempty"` TaskList *string `json:"taskList,omitempty"` TargetChildWorkflowOnly *bool `json:"targetChildWorkflowOnly,omitempty"` ScheduleID *int64 `json:"scheduleID,omitempty"` Version *int64 `json:"version,omitempty"` VisibilityTimestampNanos *int64 `json:"visibilityTimestampNanos,omitempty"` TargetDomainIDs [][]byte `json:"targetDomainIDs,omitempty"` OriginalTaskList *string `json:"originalTaskList,omitempty"` OriginalTaskListKind *shared.TaskListKind `json:"originalTaskListKind,omitempty"` } type _Set_Binary_sliceType_ValueList [][]byte func (v _Set_Binary_sliceType_ValueList) ForEach(f func(wire.Value) error) error { for _, x := range v { if x == nil { return fmt.Errorf("invalid set '[]byte': contains nil value") } w, err := wire.NewValueBinary(x), error(nil) if err != nil { return err } if err := f(w); err != nil { return err } } return nil } func (v _Set_Binary_sliceType_ValueList) Size() int { return len(v) } func (_Set_Binary_sliceType_ValueList) ValueType() wire.Type { return wire.TBinary } func (_Set_Binary_sliceType_ValueList) Close() {} // ToWire translates a TransferTaskInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *TransferTaskInfo) ToWire() (wire.Value, error) { var ( fields [15]wire.Field i int = 0 w wire.Value err error ) if v.DomainID != nil { w, err = wire.NewValueBinary(v.DomainID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.WorkflowID != nil { w, err = wire.NewValueString(*(v.WorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.RunID != nil { w, err = wire.NewValueBinary(v.RunID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.TaskType != nil { w, err = wire.NewValueI16(*(v.TaskType)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.TargetDomainID != nil { w, err = wire.NewValueBinary(v.TargetDomainID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } if v.TargetWorkflowID != nil { w, err = wire.NewValueString(*(v.TargetWorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.TargetRunID != nil { w, err = wire.NewValueBinary(v.TargetRunID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 22, Value: w} i++ } if v.TaskList != nil { w, err = wire.NewValueString(*(v.TaskList)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 24, Value: w} i++ } if v.TargetChildWorkflowOnly != nil { w, err = wire.NewValueBool(*(v.TargetChildWorkflowOnly)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 26, Value: w} i++ } if v.ScheduleID != nil { w, err = wire.NewValueI64(*(v.ScheduleID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 28, Value: w} i++ } if v.Version != nil { w, err = wire.NewValueI64(*(v.Version)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.VisibilityTimestampNanos != nil { w, err = wire.NewValueI64(*(v.VisibilityTimestampNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 32, Value: w} i++ } if v.TargetDomainIDs != nil { w, err = wire.NewValueSet(_Set_Binary_sliceType_ValueList(v.TargetDomainIDs)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 34, Value: w} i++ } if v.OriginalTaskList != nil { w, err = wire.NewValueString(*(v.OriginalTaskList)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 36, Value: w} i++ } if v.OriginalTaskListKind != nil { w, err = v.OriginalTaskListKind.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 38, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Set_Binary_sliceType_Read(s wire.ValueList) ([][]byte, error) { if s.ValueType() != wire.TBinary { return nil, nil } o := make([][]byte, 0, s.Size()) err := s.ForEach(func(x wire.Value) error { i, err := x.GetBinary(), error(nil) if err != nil { return err } o = append(o, i) return nil }) s.Close() return o, err } // FromWire deserializes a TransferTaskInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a TransferTaskInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v TransferTaskInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *TransferTaskInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.DomainID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 12: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowID = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TBinary { v.RunID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 16: if field.Value.Type() == wire.TI16 { var x int16 x, err = field.Value.GetI16(), error(nil) v.TaskType = &x if err != nil { return err } } case 18: if field.Value.Type() == wire.TBinary { v.TargetDomainID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TargetWorkflowID = &x if err != nil { return err } } case 22: if field.Value.Type() == wire.TBinary { v.TargetRunID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 24: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TaskList = &x if err != nil { return err } } case 26: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.TargetChildWorkflowOnly = &x if err != nil { return err } } case 28: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.ScheduleID = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.Version = &x if err != nil { return err } } case 32: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.VisibilityTimestampNanos = &x if err != nil { return err } } case 34: if field.Value.Type() == wire.TSet { v.TargetDomainIDs, err = _Set_Binary_sliceType_Read(field.Value.GetSet()) if err != nil { return err } } case 36: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.OriginalTaskList = &x if err != nil { return err } } case 38: if field.Value.Type() == wire.TI32 { var x shared.TaskListKind x, err = _TaskListKind_Read(field.Value) v.OriginalTaskListKind = &x if err != nil { return err } } } } return nil } func _Set_Binary_sliceType_Encode(val [][]byte, sw stream.Writer) error { sh := stream.SetHeader{ Type: wire.TBinary, Length: len(val), } if err := sw.WriteSetBegin(sh); err != nil { return err } for _, v := range val { if v == nil { return fmt.Errorf("invalid set '[]byte': contains nil value") } if err := sw.WriteBinary(v); err != nil { return err } } return sw.WriteSetEnd() } // Encode serializes a TransferTaskInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a TransferTaskInfo struct could not be encoded. func (v *TransferTaskInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.DomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.DomainID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.RunID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskType != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TI16}); err != nil { return err } if err := sw.WriteInt16(*(v.TaskType)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetDomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TargetDomainID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetWorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TargetWorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 22, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.TargetRunID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 24, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TaskList)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetChildWorkflowOnly != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 26, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.TargetChildWorkflowOnly)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ScheduleID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 28, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.ScheduleID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Version != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.Version)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VisibilityTimestampNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 32, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.VisibilityTimestampNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TargetDomainIDs != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 34, Type: wire.TSet}); err != nil { return err } if err := _Set_Binary_sliceType_Encode(v.TargetDomainIDs, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.OriginalTaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 36, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.OriginalTaskList)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.OriginalTaskListKind != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 38, Type: wire.TI32}); err != nil { return err } if err := v.OriginalTaskListKind.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _Set_Binary_sliceType_Decode(sr stream.Reader) ([][]byte, error) { sh, err := sr.ReadSetBegin() if err != nil { return nil, err } if sh.Type != wire.TBinary { for i := 0; i < sh.Length; i++ { if err := sr.Skip(sh.Type); err != nil { return nil, err } } return nil, sr.ReadSetEnd() } o := make([][]byte, 0, sh.Length) for i := 0; i < sh.Length; i++ { v, err := sr.ReadBinary() if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadSetEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a TransferTaskInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a TransferTaskInfo struct could not be generated from the wire // representation. func (v *TransferTaskInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.DomainID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowID = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TBinary: v.RunID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TI16: var x int16 x, err = sr.ReadInt16() v.TaskType = &x if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TBinary: v.TargetDomainID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TargetWorkflowID = &x if err != nil { return err } case fh.ID == 22 && fh.Type == wire.TBinary: v.TargetRunID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 24 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TaskList = &x if err != nil { return err } case fh.ID == 26 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.TargetChildWorkflowOnly = &x if err != nil { return err } case fh.ID == 28 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.ScheduleID = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.Version = &x if err != nil { return err } case fh.ID == 32 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.VisibilityTimestampNanos = &x if err != nil { return err } case fh.ID == 34 && fh.Type == wire.TSet: v.TargetDomainIDs, err = _Set_Binary_sliceType_Decode(sr) if err != nil { return err } case fh.ID == 36 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.OriginalTaskList = &x if err != nil { return err } case fh.ID == 38 && fh.Type == wire.TI32: var x shared.TaskListKind x, err = _TaskListKind_Decode(sr) v.OriginalTaskListKind = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a TransferTaskInfo // struct. func (v *TransferTaskInfo) String() string { if v == nil { return "" } var fields [15]string i := 0 if v.DomainID != nil { fields[i] = fmt.Sprintf("DomainID: %v", v.DomainID) i++ } if v.WorkflowID != nil { fields[i] = fmt.Sprintf("WorkflowID: %v", *(v.WorkflowID)) i++ } if v.RunID != nil { fields[i] = fmt.Sprintf("RunID: %v", v.RunID) i++ } if v.TaskType != nil { fields[i] = fmt.Sprintf("TaskType: %v", *(v.TaskType)) i++ } if v.TargetDomainID != nil { fields[i] = fmt.Sprintf("TargetDomainID: %v", v.TargetDomainID) i++ } if v.TargetWorkflowID != nil { fields[i] = fmt.Sprintf("TargetWorkflowID: %v", *(v.TargetWorkflowID)) i++ } if v.TargetRunID != nil { fields[i] = fmt.Sprintf("TargetRunID: %v", v.TargetRunID) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", *(v.TaskList)) i++ } if v.TargetChildWorkflowOnly != nil { fields[i] = fmt.Sprintf("TargetChildWorkflowOnly: %v", *(v.TargetChildWorkflowOnly)) i++ } if v.ScheduleID != nil { fields[i] = fmt.Sprintf("ScheduleID: %v", *(v.ScheduleID)) i++ } if v.Version != nil { fields[i] = fmt.Sprintf("Version: %v", *(v.Version)) i++ } if v.VisibilityTimestampNanos != nil { fields[i] = fmt.Sprintf("VisibilityTimestampNanos: %v", *(v.VisibilityTimestampNanos)) i++ } if v.TargetDomainIDs != nil { fields[i] = fmt.Sprintf("TargetDomainIDs: %v", v.TargetDomainIDs) i++ } if v.OriginalTaskList != nil { fields[i] = fmt.Sprintf("OriginalTaskList: %v", *(v.OriginalTaskList)) i++ } if v.OriginalTaskListKind != nil { fields[i] = fmt.Sprintf("OriginalTaskListKind: %v", *(v.OriginalTaskListKind)) i++ } return fmt.Sprintf("TransferTaskInfo{%v}", strings.Join(fields[:i], ", ")) } func _Set_Binary_sliceType_Equals(lhs, rhs [][]byte) bool { if len(lhs) != len(rhs) { return false } for _, x := range lhs { ok := false for _, y := range rhs { if bytes.Equal(x, y) { ok = true break } } if !ok { return false } } return true } // Equals returns true if all the fields of this TransferTaskInfo match the // provided TransferTaskInfo. // // This function performs a deep comparison. func (v *TransferTaskInfo) Equals(rhs *TransferTaskInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.DomainID == nil && rhs.DomainID == nil) || (v.DomainID != nil && rhs.DomainID != nil && bytes.Equal(v.DomainID, rhs.DomainID))) { return false } if !_String_EqualsPtr(v.WorkflowID, rhs.WorkflowID) { return false } if !((v.RunID == nil && rhs.RunID == nil) || (v.RunID != nil && rhs.RunID != nil && bytes.Equal(v.RunID, rhs.RunID))) { return false } if !_I16_EqualsPtr(v.TaskType, rhs.TaskType) { return false } if !((v.TargetDomainID == nil && rhs.TargetDomainID == nil) || (v.TargetDomainID != nil && rhs.TargetDomainID != nil && bytes.Equal(v.TargetDomainID, rhs.TargetDomainID))) { return false } if !_String_EqualsPtr(v.TargetWorkflowID, rhs.TargetWorkflowID) { return false } if !((v.TargetRunID == nil && rhs.TargetRunID == nil) || (v.TargetRunID != nil && rhs.TargetRunID != nil && bytes.Equal(v.TargetRunID, rhs.TargetRunID))) { return false } if !_String_EqualsPtr(v.TaskList, rhs.TaskList) { return false } if !_Bool_EqualsPtr(v.TargetChildWorkflowOnly, rhs.TargetChildWorkflowOnly) { return false } if !_I64_EqualsPtr(v.ScheduleID, rhs.ScheduleID) { return false } if !_I64_EqualsPtr(v.Version, rhs.Version) { return false } if !_I64_EqualsPtr(v.VisibilityTimestampNanos, rhs.VisibilityTimestampNanos) { return false } if !((v.TargetDomainIDs == nil && rhs.TargetDomainIDs == nil) || (v.TargetDomainIDs != nil && rhs.TargetDomainIDs != nil && _Set_Binary_sliceType_Equals(v.TargetDomainIDs, rhs.TargetDomainIDs))) { return false } if !_String_EqualsPtr(v.OriginalTaskList, rhs.OriginalTaskList) { return false } if !_TaskListKind_EqualsPtr(v.OriginalTaskListKind, rhs.OriginalTaskListKind) { return false } return true } type _Set_Binary_sliceType_Zapper [][]byte // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _Set_Binary_sliceType_Zapper. func (s _Set_Binary_sliceType_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range s { enc.AppendString(base64.StdEncoding.EncodeToString(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of TransferTaskInfo. func (v *TransferTaskInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.DomainID != nil { enc.AddString("domainID", base64.StdEncoding.EncodeToString(v.DomainID)) } if v.WorkflowID != nil { enc.AddString("workflowID", *v.WorkflowID) } if v.RunID != nil { enc.AddString("runID", base64.StdEncoding.EncodeToString(v.RunID)) } if v.TaskType != nil { enc.AddInt16("taskType", *v.TaskType) } if v.TargetDomainID != nil { enc.AddString("targetDomainID", base64.StdEncoding.EncodeToString(v.TargetDomainID)) } if v.TargetWorkflowID != nil { enc.AddString("targetWorkflowID", *v.TargetWorkflowID) } if v.TargetRunID != nil { enc.AddString("targetRunID", base64.StdEncoding.EncodeToString(v.TargetRunID)) } if v.TaskList != nil { enc.AddString("taskList", *v.TaskList) } if v.TargetChildWorkflowOnly != nil { enc.AddBool("targetChildWorkflowOnly", *v.TargetChildWorkflowOnly) } if v.ScheduleID != nil { enc.AddInt64("scheduleID", *v.ScheduleID) } if v.Version != nil { enc.AddInt64("version", *v.Version) } if v.VisibilityTimestampNanos != nil { enc.AddInt64("visibilityTimestampNanos", *v.VisibilityTimestampNanos) } if v.TargetDomainIDs != nil { err = multierr.Append(err, enc.AddArray("targetDomainIDs", (_Set_Binary_sliceType_Zapper)(v.TargetDomainIDs))) } if v.OriginalTaskList != nil { enc.AddString("originalTaskList", *v.OriginalTaskList) } if v.OriginalTaskListKind != nil { err = multierr.Append(err, enc.AddObject("originalTaskListKind", *v.OriginalTaskListKind)) } return err } // GetDomainID returns the value of DomainID if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetDomainID() (o []byte) { if v != nil && v.DomainID != nil { return v.DomainID } return } // IsSetDomainID returns true if DomainID is not nil. func (v *TransferTaskInfo) IsSetDomainID() bool { return v != nil && v.DomainID != nil } // GetWorkflowID returns the value of WorkflowID if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetWorkflowID() (o string) { if v != nil && v.WorkflowID != nil { return *v.WorkflowID } return } // IsSetWorkflowID returns true if WorkflowID is not nil. func (v *TransferTaskInfo) IsSetWorkflowID() bool { return v != nil && v.WorkflowID != nil } // GetRunID returns the value of RunID if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetRunID() (o []byte) { if v != nil && v.RunID != nil { return v.RunID } return } // IsSetRunID returns true if RunID is not nil. func (v *TransferTaskInfo) IsSetRunID() bool { return v != nil && v.RunID != nil } // GetTaskType returns the value of TaskType if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetTaskType() (o int16) { if v != nil && v.TaskType != nil { return *v.TaskType } return } // IsSetTaskType returns true if TaskType is not nil. func (v *TransferTaskInfo) IsSetTaskType() bool { return v != nil && v.TaskType != nil } // GetTargetDomainID returns the value of TargetDomainID if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetTargetDomainID() (o []byte) { if v != nil && v.TargetDomainID != nil { return v.TargetDomainID } return } // IsSetTargetDomainID returns true if TargetDomainID is not nil. func (v *TransferTaskInfo) IsSetTargetDomainID() bool { return v != nil && v.TargetDomainID != nil } // GetTargetWorkflowID returns the value of TargetWorkflowID if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetTargetWorkflowID() (o string) { if v != nil && v.TargetWorkflowID != nil { return *v.TargetWorkflowID } return } // IsSetTargetWorkflowID returns true if TargetWorkflowID is not nil. func (v *TransferTaskInfo) IsSetTargetWorkflowID() bool { return v != nil && v.TargetWorkflowID != nil } // GetTargetRunID returns the value of TargetRunID if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetTargetRunID() (o []byte) { if v != nil && v.TargetRunID != nil { return v.TargetRunID } return } // IsSetTargetRunID returns true if TargetRunID is not nil. func (v *TransferTaskInfo) IsSetTargetRunID() bool { return v != nil && v.TargetRunID != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetTaskList() (o string) { if v != nil && v.TaskList != nil { return *v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *TransferTaskInfo) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetTargetChildWorkflowOnly returns the value of TargetChildWorkflowOnly if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetTargetChildWorkflowOnly() (o bool) { if v != nil && v.TargetChildWorkflowOnly != nil { return *v.TargetChildWorkflowOnly } return } // IsSetTargetChildWorkflowOnly returns true if TargetChildWorkflowOnly is not nil. func (v *TransferTaskInfo) IsSetTargetChildWorkflowOnly() bool { return v != nil && v.TargetChildWorkflowOnly != nil } // GetScheduleID returns the value of ScheduleID if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetScheduleID() (o int64) { if v != nil && v.ScheduleID != nil { return *v.ScheduleID } return } // IsSetScheduleID returns true if ScheduleID is not nil. func (v *TransferTaskInfo) IsSetScheduleID() bool { return v != nil && v.ScheduleID != nil } // GetVersion returns the value of Version if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetVersion() (o int64) { if v != nil && v.Version != nil { return *v.Version } return } // IsSetVersion returns true if Version is not nil. func (v *TransferTaskInfo) IsSetVersion() bool { return v != nil && v.Version != nil } // GetVisibilityTimestampNanos returns the value of VisibilityTimestampNanos if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetVisibilityTimestampNanos() (o int64) { if v != nil && v.VisibilityTimestampNanos != nil { return *v.VisibilityTimestampNanos } return } // IsSetVisibilityTimestampNanos returns true if VisibilityTimestampNanos is not nil. func (v *TransferTaskInfo) IsSetVisibilityTimestampNanos() bool { return v != nil && v.VisibilityTimestampNanos != nil } // GetTargetDomainIDs returns the value of TargetDomainIDs if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetTargetDomainIDs() (o [][]byte) { if v != nil && v.TargetDomainIDs != nil { return v.TargetDomainIDs } return } // IsSetTargetDomainIDs returns true if TargetDomainIDs is not nil. func (v *TransferTaskInfo) IsSetTargetDomainIDs() bool { return v != nil && v.TargetDomainIDs != nil } // GetOriginalTaskList returns the value of OriginalTaskList if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetOriginalTaskList() (o string) { if v != nil && v.OriginalTaskList != nil { return *v.OriginalTaskList } return } // IsSetOriginalTaskList returns true if OriginalTaskList is not nil. func (v *TransferTaskInfo) IsSetOriginalTaskList() bool { return v != nil && v.OriginalTaskList != nil } // GetOriginalTaskListKind returns the value of OriginalTaskListKind if it is set or its // zero value if it is unset. func (v *TransferTaskInfo) GetOriginalTaskListKind() (o shared.TaskListKind) { if v != nil && v.OriginalTaskListKind != nil { return *v.OriginalTaskListKind } return } // IsSetOriginalTaskListKind returns true if OriginalTaskListKind is not nil. func (v *TransferTaskInfo) IsSetOriginalTaskListKind() bool { return v != nil && v.OriginalTaskListKind != nil } type WorkflowExecutionInfo struct { ParentDomainID []byte `json:"parentDomainID,omitempty"` ParentWorkflowID *string `json:"parentWorkflowID,omitempty"` ParentRunID []byte `json:"parentRunID,omitempty"` InitiatedID *int64 `json:"initiatedID,omitempty"` CompletionEventBatchID *int64 `json:"completionEventBatchID,omitempty"` CompletionEvent []byte `json:"completionEvent,omitempty"` CompletionEventEncoding *string `json:"completionEventEncoding,omitempty"` TaskList *string `json:"taskList,omitempty"` TaskListKind *shared.TaskListKind `json:"taskListKind,omitempty"` WorkflowTypeName *string `json:"workflowTypeName,omitempty"` WorkflowTimeoutSeconds *int32 `json:"workflowTimeoutSeconds,omitempty"` DecisionTaskTimeoutSeconds *int32 `json:"decisionTaskTimeoutSeconds,omitempty"` ExecutionContext []byte `json:"executionContext,omitempty"` State *int32 `json:"state,omitempty"` CloseStatus *int32 `json:"closeStatus,omitempty"` StartVersion *int64 `json:"startVersion,omitempty"` LastWriteEventID *int64 `json:"lastWriteEventID,omitempty"` LastEventTaskID *int64 `json:"lastEventTaskID,omitempty"` LastFirstEventID *int64 `json:"lastFirstEventID,omitempty"` LastProcessedEvent *int64 `json:"lastProcessedEvent,omitempty"` StartTimeNanos *int64 `json:"startTimeNanos,omitempty"` LastUpdatedTimeNanos *int64 `json:"lastUpdatedTimeNanos,omitempty"` DecisionVersion *int64 `json:"decisionVersion,omitempty"` DecisionScheduleID *int64 `json:"decisionScheduleID,omitempty"` DecisionStartedID *int64 `json:"decisionStartedID,omitempty"` DecisionTimeout *int32 `json:"decisionTimeout,omitempty"` DecisionAttempt *int64 `json:"decisionAttempt,omitempty"` DecisionStartedTimestampNanos *int64 `json:"decisionStartedTimestampNanos,omitempty"` DecisionScheduledTimestampNanos *int64 `json:"decisionScheduledTimestampNanos,omitempty"` CancelRequested *bool `json:"cancelRequested,omitempty"` DecisionOriginalScheduledTimestampNanos *int64 `json:"decisionOriginalScheduledTimestampNanos,omitempty"` CreateRequestID *string `json:"createRequestID,omitempty"` DecisionRequestID *string `json:"decisionRequestID,omitempty"` CancelRequestID *string `json:"cancelRequestID,omitempty"` StickyTaskList *string `json:"stickyTaskList,omitempty"` StickyScheduleToStartTimeout *int64 `json:"stickyScheduleToStartTimeout,omitempty"` RetryAttempt *int64 `json:"retryAttempt,omitempty"` RetryInitialIntervalSeconds *int32 `json:"retryInitialIntervalSeconds,omitempty"` RetryMaximumIntervalSeconds *int32 `json:"retryMaximumIntervalSeconds,omitempty"` RetryMaximumAttempts *int32 `json:"retryMaximumAttempts,omitempty"` RetryExpirationSeconds *int32 `json:"retryExpirationSeconds,omitempty"` RetryBackoffCoefficient *float64 `json:"retryBackoffCoefficient,omitempty"` RetryExpirationTimeNanos *int64 `json:"retryExpirationTimeNanos,omitempty"` RetryNonRetryableErrors []string `json:"retryNonRetryableErrors,omitempty"` HasRetryPolicy *bool `json:"hasRetryPolicy,omitempty"` CronSchedule *string `json:"cronSchedule,omitempty"` EventStoreVersion *int32 `json:"eventStoreVersion,omitempty"` EventBranchToken []byte `json:"eventBranchToken,omitempty"` SignalCount *int64 `json:"signalCount,omitempty"` HistorySize *int64 `json:"historySize,omitempty"` ClientLibraryVersion *string `json:"clientLibraryVersion,omitempty"` ClientFeatureVersion *string `json:"clientFeatureVersion,omitempty"` ClientImpl *string `json:"clientImpl,omitempty"` AutoResetPoints []byte `json:"autoResetPoints,omitempty"` AutoResetPointsEncoding *string `json:"autoResetPointsEncoding,omitempty"` SearchAttributes map[string][]byte `json:"searchAttributes,omitempty"` Memo map[string][]byte `json:"memo,omitempty"` VersionHistories []byte `json:"versionHistories,omitempty"` VersionHistoriesEncoding *string `json:"versionHistoriesEncoding,omitempty"` FirstExecutionRunID []byte `json:"firstExecutionRunID,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` Checksum []byte `json:"checksum,omitempty"` ChecksumEncoding *string `json:"checksumEncoding,omitempty"` CronOverlapPolicy *shared.CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy []byte `json:"activeClusterSelectionPolicy,omitempty"` ActiveClusterSelectionPolicyEncoding *string `json:"activeClusterSelectionPolicyEncoding,omitempty"` } type _Map_String_Binary_MapItemList map[string][]byte func (m _Map_String_Binary_MapItemList) ForEach(f func(wire.MapItem) error) error { for k, v := range m { if v == nil { return fmt.Errorf("invalid map 'map[string][]byte', key [%v]: value is nil", k) } kw, err := wire.NewValueString(k), error(nil) if err != nil { return err } vw, err := wire.NewValueBinary(v), error(nil) if err != nil { return err } err = f(wire.MapItem{Key: kw, Value: vw}) if err != nil { return err } } return nil } func (m _Map_String_Binary_MapItemList) Size() int { return len(m) } func (_Map_String_Binary_MapItemList) KeyType() wire.Type { return wire.TBinary } func (_Map_String_Binary_MapItemList) ValueType() wire.Type { return wire.TBinary } func (_Map_String_Binary_MapItemList) Close() {} // ToWire translates a WorkflowExecutionInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowExecutionInfo) ToWire() (wire.Value, error) { var ( fields [66]wire.Field i int = 0 w wire.Value err error ) if v.ParentDomainID != nil { w, err = wire.NewValueBinary(v.ParentDomainID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } if v.ParentWorkflowID != nil { w, err = wire.NewValueString(*(v.ParentWorkflowID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 12, Value: w} i++ } if v.ParentRunID != nil { w, err = wire.NewValueBinary(v.ParentRunID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 14, Value: w} i++ } if v.InitiatedID != nil { w, err = wire.NewValueI64(*(v.InitiatedID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 16, Value: w} i++ } if v.CompletionEventBatchID != nil { w, err = wire.NewValueI64(*(v.CompletionEventBatchID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 18, Value: w} i++ } if v.CompletionEvent != nil { w, err = wire.NewValueBinary(v.CompletionEvent), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 20, Value: w} i++ } if v.CompletionEventEncoding != nil { w, err = wire.NewValueString(*(v.CompletionEventEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 22, Value: w} i++ } if v.TaskList != nil { w, err = wire.NewValueString(*(v.TaskList)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 24, Value: w} i++ } if v.TaskListKind != nil { w, err = v.TaskListKind.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 25, Value: w} i++ } if v.WorkflowTypeName != nil { w, err = wire.NewValueString(*(v.WorkflowTypeName)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 26, Value: w} i++ } if v.WorkflowTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.WorkflowTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 28, Value: w} i++ } if v.DecisionTaskTimeoutSeconds != nil { w, err = wire.NewValueI32(*(v.DecisionTaskTimeoutSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 30, Value: w} i++ } if v.ExecutionContext != nil { w, err = wire.NewValueBinary(v.ExecutionContext), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 32, Value: w} i++ } if v.State != nil { w, err = wire.NewValueI32(*(v.State)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 34, Value: w} i++ } if v.CloseStatus != nil { w, err = wire.NewValueI32(*(v.CloseStatus)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 36, Value: w} i++ } if v.StartVersion != nil { w, err = wire.NewValueI64(*(v.StartVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 38, Value: w} i++ } if v.LastWriteEventID != nil { w, err = wire.NewValueI64(*(v.LastWriteEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 44, Value: w} i++ } if v.LastEventTaskID != nil { w, err = wire.NewValueI64(*(v.LastEventTaskID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 48, Value: w} i++ } if v.LastFirstEventID != nil { w, err = wire.NewValueI64(*(v.LastFirstEventID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 50, Value: w} i++ } if v.LastProcessedEvent != nil { w, err = wire.NewValueI64(*(v.LastProcessedEvent)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 52, Value: w} i++ } if v.StartTimeNanos != nil { w, err = wire.NewValueI64(*(v.StartTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 54, Value: w} i++ } if v.LastUpdatedTimeNanos != nil { w, err = wire.NewValueI64(*(v.LastUpdatedTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 56, Value: w} i++ } if v.DecisionVersion != nil { w, err = wire.NewValueI64(*(v.DecisionVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 58, Value: w} i++ } if v.DecisionScheduleID != nil { w, err = wire.NewValueI64(*(v.DecisionScheduleID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 60, Value: w} i++ } if v.DecisionStartedID != nil { w, err = wire.NewValueI64(*(v.DecisionStartedID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 62, Value: w} i++ } if v.DecisionTimeout != nil { w, err = wire.NewValueI32(*(v.DecisionTimeout)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 64, Value: w} i++ } if v.DecisionAttempt != nil { w, err = wire.NewValueI64(*(v.DecisionAttempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 66, Value: w} i++ } if v.DecisionStartedTimestampNanos != nil { w, err = wire.NewValueI64(*(v.DecisionStartedTimestampNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 68, Value: w} i++ } if v.DecisionScheduledTimestampNanos != nil { w, err = wire.NewValueI64(*(v.DecisionScheduledTimestampNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 69, Value: w} i++ } if v.CancelRequested != nil { w, err = wire.NewValueBool(*(v.CancelRequested)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 70, Value: w} i++ } if v.DecisionOriginalScheduledTimestampNanos != nil { w, err = wire.NewValueI64(*(v.DecisionOriginalScheduledTimestampNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 71, Value: w} i++ } if v.CreateRequestID != nil { w, err = wire.NewValueString(*(v.CreateRequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 72, Value: w} i++ } if v.DecisionRequestID != nil { w, err = wire.NewValueString(*(v.DecisionRequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 74, Value: w} i++ } if v.CancelRequestID != nil { w, err = wire.NewValueString(*(v.CancelRequestID)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 76, Value: w} i++ } if v.StickyTaskList != nil { w, err = wire.NewValueString(*(v.StickyTaskList)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 78, Value: w} i++ } if v.StickyScheduleToStartTimeout != nil { w, err = wire.NewValueI64(*(v.StickyScheduleToStartTimeout)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 80, Value: w} i++ } if v.RetryAttempt != nil { w, err = wire.NewValueI64(*(v.RetryAttempt)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 82, Value: w} i++ } if v.RetryInitialIntervalSeconds != nil { w, err = wire.NewValueI32(*(v.RetryInitialIntervalSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 84, Value: w} i++ } if v.RetryMaximumIntervalSeconds != nil { w, err = wire.NewValueI32(*(v.RetryMaximumIntervalSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 86, Value: w} i++ } if v.RetryMaximumAttempts != nil { w, err = wire.NewValueI32(*(v.RetryMaximumAttempts)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 88, Value: w} i++ } if v.RetryExpirationSeconds != nil { w, err = wire.NewValueI32(*(v.RetryExpirationSeconds)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 90, Value: w} i++ } if v.RetryBackoffCoefficient != nil { w, err = wire.NewValueDouble(*(v.RetryBackoffCoefficient)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 92, Value: w} i++ } if v.RetryExpirationTimeNanos != nil { w, err = wire.NewValueI64(*(v.RetryExpirationTimeNanos)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 94, Value: w} i++ } if v.RetryNonRetryableErrors != nil { w, err = wire.NewValueList(_List_String_ValueList(v.RetryNonRetryableErrors)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 96, Value: w} i++ } if v.HasRetryPolicy != nil { w, err = wire.NewValueBool(*(v.HasRetryPolicy)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 98, Value: w} i++ } if v.CronSchedule != nil { w, err = wire.NewValueString(*(v.CronSchedule)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 100, Value: w} i++ } if v.EventStoreVersion != nil { w, err = wire.NewValueI32(*(v.EventStoreVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 102, Value: w} i++ } if v.EventBranchToken != nil { w, err = wire.NewValueBinary(v.EventBranchToken), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 104, Value: w} i++ } if v.SignalCount != nil { w, err = wire.NewValueI64(*(v.SignalCount)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 106, Value: w} i++ } if v.HistorySize != nil { w, err = wire.NewValueI64(*(v.HistorySize)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 108, Value: w} i++ } if v.ClientLibraryVersion != nil { w, err = wire.NewValueString(*(v.ClientLibraryVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 110, Value: w} i++ } if v.ClientFeatureVersion != nil { w, err = wire.NewValueString(*(v.ClientFeatureVersion)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 112, Value: w} i++ } if v.ClientImpl != nil { w, err = wire.NewValueString(*(v.ClientImpl)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 114, Value: w} i++ } if v.AutoResetPoints != nil { w, err = wire.NewValueBinary(v.AutoResetPoints), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 115, Value: w} i++ } if v.AutoResetPointsEncoding != nil { w, err = wire.NewValueString(*(v.AutoResetPointsEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 116, Value: w} i++ } if v.SearchAttributes != nil { w, err = wire.NewValueMap(_Map_String_Binary_MapItemList(v.SearchAttributes)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 118, Value: w} i++ } if v.Memo != nil { w, err = wire.NewValueMap(_Map_String_Binary_MapItemList(v.Memo)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 120, Value: w} i++ } if v.VersionHistories != nil { w, err = wire.NewValueBinary(v.VersionHistories), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 122, Value: w} i++ } if v.VersionHistoriesEncoding != nil { w, err = wire.NewValueString(*(v.VersionHistoriesEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 124, Value: w} i++ } if v.FirstExecutionRunID != nil { w, err = wire.NewValueBinary(v.FirstExecutionRunID), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 126, Value: w} i++ } if v.PartitionConfig != nil { w, err = wire.NewValueMap(_Map_String_String_MapItemList(v.PartitionConfig)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 128, Value: w} i++ } if v.Checksum != nil { w, err = wire.NewValueBinary(v.Checksum), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 130, Value: w} i++ } if v.ChecksumEncoding != nil { w, err = wire.NewValueString(*(v.ChecksumEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 132, Value: w} i++ } if v.CronOverlapPolicy != nil { w, err = v.CronOverlapPolicy.ToWire() if err != nil { return w, err } fields[i] = wire.Field{ID: 134, Value: w} i++ } if v.ActiveClusterSelectionPolicy != nil { w, err = wire.NewValueBinary(v.ActiveClusterSelectionPolicy), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 137, Value: w} i++ } if v.ActiveClusterSelectionPolicyEncoding != nil { w, err = wire.NewValueString(*(v.ActiveClusterSelectionPolicyEncoding)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 138, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _Map_String_Binary_Read(m wire.MapItemList) (map[string][]byte, error) { if m.KeyType() != wire.TBinary { return nil, nil } if m.ValueType() != wire.TBinary { return nil, nil } o := make(map[string][]byte, m.Size()) err := m.ForEach(func(x wire.MapItem) error { k, err := x.Key.GetString(), error(nil) if err != nil { return err } v, err := x.Value.GetBinary(), error(nil) if err != nil { return err } o[k] = v return nil }) m.Close() return o, err } func _CronOverlapPolicy_Read(w wire.Value) (shared.CronOverlapPolicy, error) { var v shared.CronOverlapPolicy err := v.FromWire(w) return v, err } // FromWire deserializes a WorkflowExecutionInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowExecutionInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowExecutionInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowExecutionInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TBinary { v.ParentDomainID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 12: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ParentWorkflowID = &x if err != nil { return err } } case 14: if field.Value.Type() == wire.TBinary { v.ParentRunID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 16: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.InitiatedID = &x if err != nil { return err } } case 18: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.CompletionEventBatchID = &x if err != nil { return err } } case 20: if field.Value.Type() == wire.TBinary { v.CompletionEvent, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 22: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CompletionEventEncoding = &x if err != nil { return err } } case 24: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.TaskList = &x if err != nil { return err } } case 25: if field.Value.Type() == wire.TI32 { var x shared.TaskListKind x, err = _TaskListKind_Read(field.Value) v.TaskListKind = &x if err != nil { return err } } case 26: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.WorkflowTypeName = &x if err != nil { return err } } case 28: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.WorkflowTimeoutSeconds = &x if err != nil { return err } } case 30: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.DecisionTaskTimeoutSeconds = &x if err != nil { return err } } case 32: if field.Value.Type() == wire.TBinary { v.ExecutionContext, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 34: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.State = &x if err != nil { return err } } case 36: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.CloseStatus = &x if err != nil { return err } } case 38: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartVersion = &x if err != nil { return err } } case 44: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastWriteEventID = &x if err != nil { return err } } case 48: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastEventTaskID = &x if err != nil { return err } } case 50: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastFirstEventID = &x if err != nil { return err } } case 52: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastProcessedEvent = &x if err != nil { return err } } case 54: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StartTimeNanos = &x if err != nil { return err } } case 56: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.LastUpdatedTimeNanos = &x if err != nil { return err } } case 58: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionVersion = &x if err != nil { return err } } case 60: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionScheduleID = &x if err != nil { return err } } case 62: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionStartedID = &x if err != nil { return err } } case 64: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.DecisionTimeout = &x if err != nil { return err } } case 66: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionAttempt = &x if err != nil { return err } } case 68: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionStartedTimestampNanos = &x if err != nil { return err } } case 69: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionScheduledTimestampNanos = &x if err != nil { return err } } case 70: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.CancelRequested = &x if err != nil { return err } } case 71: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.DecisionOriginalScheduledTimestampNanos = &x if err != nil { return err } } case 72: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CreateRequestID = &x if err != nil { return err } } case 74: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.DecisionRequestID = &x if err != nil { return err } } case 76: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CancelRequestID = &x if err != nil { return err } } case 78: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.StickyTaskList = &x if err != nil { return err } } case 80: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.StickyScheduleToStartTimeout = &x if err != nil { return err } } case 82: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.RetryAttempt = &x if err != nil { return err } } case 84: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.RetryInitialIntervalSeconds = &x if err != nil { return err } } case 86: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.RetryMaximumIntervalSeconds = &x if err != nil { return err } } case 88: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.RetryMaximumAttempts = &x if err != nil { return err } } case 90: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.RetryExpirationSeconds = &x if err != nil { return err } } case 92: if field.Value.Type() == wire.TDouble { var x float64 x, err = field.Value.GetDouble(), error(nil) v.RetryBackoffCoefficient = &x if err != nil { return err } } case 94: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.RetryExpirationTimeNanos = &x if err != nil { return err } } case 96: if field.Value.Type() == wire.TList { v.RetryNonRetryableErrors, err = _List_String_Read(field.Value.GetList()) if err != nil { return err } } case 98: if field.Value.Type() == wire.TBool { var x bool x, err = field.Value.GetBool(), error(nil) v.HasRetryPolicy = &x if err != nil { return err } } case 100: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.CronSchedule = &x if err != nil { return err } } case 102: if field.Value.Type() == wire.TI32 { var x int32 x, err = field.Value.GetI32(), error(nil) v.EventStoreVersion = &x if err != nil { return err } } case 104: if field.Value.Type() == wire.TBinary { v.EventBranchToken, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 106: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.SignalCount = &x if err != nil { return err } } case 108: if field.Value.Type() == wire.TI64 { var x int64 x, err = field.Value.GetI64(), error(nil) v.HistorySize = &x if err != nil { return err } } case 110: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClientLibraryVersion = &x if err != nil { return err } } case 112: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClientFeatureVersion = &x if err != nil { return err } } case 114: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ClientImpl = &x if err != nil { return err } } case 115: if field.Value.Type() == wire.TBinary { v.AutoResetPoints, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 116: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.AutoResetPointsEncoding = &x if err != nil { return err } } case 118: if field.Value.Type() == wire.TMap { v.SearchAttributes, err = _Map_String_Binary_Read(field.Value.GetMap()) if err != nil { return err } } case 120: if field.Value.Type() == wire.TMap { v.Memo, err = _Map_String_Binary_Read(field.Value.GetMap()) if err != nil { return err } } case 122: if field.Value.Type() == wire.TBinary { v.VersionHistories, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 124: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.VersionHistoriesEncoding = &x if err != nil { return err } } case 126: if field.Value.Type() == wire.TBinary { v.FirstExecutionRunID, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 128: if field.Value.Type() == wire.TMap { v.PartitionConfig, err = _Map_String_String_Read(field.Value.GetMap()) if err != nil { return err } } case 130: if field.Value.Type() == wire.TBinary { v.Checksum, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 132: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ChecksumEncoding = &x if err != nil { return err } } case 134: if field.Value.Type() == wire.TI32 { var x shared.CronOverlapPolicy x, err = _CronOverlapPolicy_Read(field.Value) v.CronOverlapPolicy = &x if err != nil { return err } } case 137: if field.Value.Type() == wire.TBinary { v.ActiveClusterSelectionPolicy, err = field.Value.GetBinary(), error(nil) if err != nil { return err } } case 138: if field.Value.Type() == wire.TBinary { var x string x, err = field.Value.GetString(), error(nil) v.ActiveClusterSelectionPolicyEncoding = &x if err != nil { return err } } } } return nil } func _Map_String_Binary_Encode(val map[string][]byte, sw stream.Writer) error { mh := stream.MapHeader{ KeyType: wire.TBinary, ValueType: wire.TBinary, Length: len(val), } if err := sw.WriteMapBegin(mh); err != nil { return err } for k, v := range val { if v == nil { return fmt.Errorf("invalid map 'map[string][]byte', key [%v]: value is nil", k) } if err := sw.WriteString(k); err != nil { return err } if err := sw.WriteBinary(v); err != nil { return err } } return sw.WriteMapEnd() } // Encode serializes a WorkflowExecutionInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowExecutionInfo struct could not be encoded. func (v *WorkflowExecutionInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.ParentDomainID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.ParentDomainID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentWorkflowID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 12, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ParentWorkflowID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ParentRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 14, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.ParentRunID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.InitiatedID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 16, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.InitiatedID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompletionEventBatchID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 18, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.CompletionEventBatchID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompletionEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 20, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.CompletionEvent); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CompletionEventEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 22, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CompletionEventEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 24, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.TaskList)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.TaskListKind != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 25, Type: wire.TI32}); err != nil { return err } if err := v.TaskListKind.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowTypeName != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 26, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.WorkflowTypeName)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.WorkflowTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 28, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.WorkflowTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTaskTimeoutSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 30, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.DecisionTaskTimeoutSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ExecutionContext != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 32, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.ExecutionContext); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.State != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 34, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.State)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CloseStatus != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 36, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.CloseStatus)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 38, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastWriteEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 44, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastWriteEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastEventTaskID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 48, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastEventTaskID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastFirstEventID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 50, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastFirstEventID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastProcessedEvent != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 52, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastProcessedEvent)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StartTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 54, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StartTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.LastUpdatedTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 56, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.LastUpdatedTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 58, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionScheduleID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 60, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionScheduleID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionStartedID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 62, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionStartedID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionTimeout != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 64, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.DecisionTimeout)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionAttempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 66, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionAttempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionStartedTimestampNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 68, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionStartedTimestampNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionScheduledTimestampNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 69, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionScheduledTimestampNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelRequested != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 70, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.CancelRequested)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionOriginalScheduledTimestampNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 71, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.DecisionOriginalScheduledTimestampNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CreateRequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 72, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CreateRequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.DecisionRequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 74, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.DecisionRequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CancelRequestID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 76, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CancelRequestID)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyTaskList != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 78, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.StickyTaskList)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.StickyScheduleToStartTimeout != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 80, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.StickyScheduleToStartTimeout)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryAttempt != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 82, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.RetryAttempt)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryInitialIntervalSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 84, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.RetryInitialIntervalSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryMaximumIntervalSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 86, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.RetryMaximumIntervalSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryMaximumAttempts != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 88, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.RetryMaximumAttempts)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryExpirationSeconds != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 90, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.RetryExpirationSeconds)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryBackoffCoefficient != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 92, Type: wire.TDouble}); err != nil { return err } if err := sw.WriteDouble(*(v.RetryBackoffCoefficient)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryExpirationTimeNanos != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 94, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.RetryExpirationTimeNanos)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.RetryNonRetryableErrors != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 96, Type: wire.TList}); err != nil { return err } if err := _List_String_Encode(v.RetryNonRetryableErrors, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HasRetryPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 98, Type: wire.TBool}); err != nil { return err } if err := sw.WriteBool(*(v.HasRetryPolicy)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronSchedule != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 100, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.CronSchedule)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EventStoreVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 102, Type: wire.TI32}); err != nil { return err } if err := sw.WriteInt32(*(v.EventStoreVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.EventBranchToken != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 104, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.EventBranchToken); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SignalCount != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 106, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.SignalCount)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.HistorySize != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 108, Type: wire.TI64}); err != nil { return err } if err := sw.WriteInt64(*(v.HistorySize)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientLibraryVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 110, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClientLibraryVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientFeatureVersion != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 112, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClientFeatureVersion)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ClientImpl != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 114, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ClientImpl)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AutoResetPoints != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 115, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.AutoResetPoints); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.AutoResetPointsEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 116, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.AutoResetPointsEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.SearchAttributes != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 118, Type: wire.TMap}); err != nil { return err } if err := _Map_String_Binary_Encode(v.SearchAttributes, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Memo != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 120, Type: wire.TMap}); err != nil { return err } if err := _Map_String_Binary_Encode(v.Memo, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistories != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 122, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.VersionHistories); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.VersionHistoriesEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 124, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.VersionHistoriesEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.FirstExecutionRunID != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 126, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.FirstExecutionRunID); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.PartitionConfig != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 128, Type: wire.TMap}); err != nil { return err } if err := _Map_String_String_Encode(v.PartitionConfig, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.Checksum != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 130, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.Checksum); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ChecksumEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 132, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ChecksumEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.CronOverlapPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 134, Type: wire.TI32}); err != nil { return err } if err := v.CronOverlapPolicy.Encode(sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterSelectionPolicy != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 137, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteBinary(v.ActiveClusterSelectionPolicy); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } if v.ActiveClusterSelectionPolicyEncoding != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 138, Type: wire.TBinary}); err != nil { return err } if err := sw.WriteString(*(v.ActiveClusterSelectionPolicyEncoding)); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _Map_String_Binary_Decode(sr stream.Reader) (map[string][]byte, error) { mh, err := sr.ReadMapBegin() if err != nil { return nil, err } if mh.KeyType != wire.TBinary || mh.ValueType != wire.TBinary { for i := 0; i < mh.Length; i++ { if err := sr.Skip(mh.KeyType); err != nil { return nil, err } if err := sr.Skip(mh.ValueType); err != nil { return nil, err } } return nil, sr.ReadMapEnd() } o := make(map[string][]byte, mh.Length) for i := 0; i < mh.Length; i++ { k, err := sr.ReadString() if err != nil { return nil, err } v, err := sr.ReadBinary() if err != nil { return nil, err } o[k] = v } if err = sr.ReadMapEnd(); err != nil { return nil, err } return o, err } func _CronOverlapPolicy_Decode(sr stream.Reader) (shared.CronOverlapPolicy, error) { var v shared.CronOverlapPolicy err := v.Decode(sr) return v, err } // Decode deserializes a WorkflowExecutionInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowExecutionInfo struct could not be generated from the wire // representation. func (v *WorkflowExecutionInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TBinary: v.ParentDomainID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 12 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ParentWorkflowID = &x if err != nil { return err } case fh.ID == 14 && fh.Type == wire.TBinary: v.ParentRunID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 16 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.InitiatedID = &x if err != nil { return err } case fh.ID == 18 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.CompletionEventBatchID = &x if err != nil { return err } case fh.ID == 20 && fh.Type == wire.TBinary: v.CompletionEvent, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 22 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CompletionEventEncoding = &x if err != nil { return err } case fh.ID == 24 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.TaskList = &x if err != nil { return err } case fh.ID == 25 && fh.Type == wire.TI32: var x shared.TaskListKind x, err = _TaskListKind_Decode(sr) v.TaskListKind = &x if err != nil { return err } case fh.ID == 26 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.WorkflowTypeName = &x if err != nil { return err } case fh.ID == 28 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.WorkflowTimeoutSeconds = &x if err != nil { return err } case fh.ID == 30 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.DecisionTaskTimeoutSeconds = &x if err != nil { return err } case fh.ID == 32 && fh.Type == wire.TBinary: v.ExecutionContext, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 34 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.State = &x if err != nil { return err } case fh.ID == 36 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.CloseStatus = &x if err != nil { return err } case fh.ID == 38 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartVersion = &x if err != nil { return err } case fh.ID == 44 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastWriteEventID = &x if err != nil { return err } case fh.ID == 48 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastEventTaskID = &x if err != nil { return err } case fh.ID == 50 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastFirstEventID = &x if err != nil { return err } case fh.ID == 52 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastProcessedEvent = &x if err != nil { return err } case fh.ID == 54 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StartTimeNanos = &x if err != nil { return err } case fh.ID == 56 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.LastUpdatedTimeNanos = &x if err != nil { return err } case fh.ID == 58 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionVersion = &x if err != nil { return err } case fh.ID == 60 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionScheduleID = &x if err != nil { return err } case fh.ID == 62 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionStartedID = &x if err != nil { return err } case fh.ID == 64 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.DecisionTimeout = &x if err != nil { return err } case fh.ID == 66 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionAttempt = &x if err != nil { return err } case fh.ID == 68 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionStartedTimestampNanos = &x if err != nil { return err } case fh.ID == 69 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionScheduledTimestampNanos = &x if err != nil { return err } case fh.ID == 70 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.CancelRequested = &x if err != nil { return err } case fh.ID == 71 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.DecisionOriginalScheduledTimestampNanos = &x if err != nil { return err } case fh.ID == 72 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CreateRequestID = &x if err != nil { return err } case fh.ID == 74 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.DecisionRequestID = &x if err != nil { return err } case fh.ID == 76 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CancelRequestID = &x if err != nil { return err } case fh.ID == 78 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.StickyTaskList = &x if err != nil { return err } case fh.ID == 80 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.StickyScheduleToStartTimeout = &x if err != nil { return err } case fh.ID == 82 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.RetryAttempt = &x if err != nil { return err } case fh.ID == 84 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.RetryInitialIntervalSeconds = &x if err != nil { return err } case fh.ID == 86 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.RetryMaximumIntervalSeconds = &x if err != nil { return err } case fh.ID == 88 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.RetryMaximumAttempts = &x if err != nil { return err } case fh.ID == 90 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.RetryExpirationSeconds = &x if err != nil { return err } case fh.ID == 92 && fh.Type == wire.TDouble: var x float64 x, err = sr.ReadDouble() v.RetryBackoffCoefficient = &x if err != nil { return err } case fh.ID == 94 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.RetryExpirationTimeNanos = &x if err != nil { return err } case fh.ID == 96 && fh.Type == wire.TList: v.RetryNonRetryableErrors, err = _List_String_Decode(sr) if err != nil { return err } case fh.ID == 98 && fh.Type == wire.TBool: var x bool x, err = sr.ReadBool() v.HasRetryPolicy = &x if err != nil { return err } case fh.ID == 100 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.CronSchedule = &x if err != nil { return err } case fh.ID == 102 && fh.Type == wire.TI32: var x int32 x, err = sr.ReadInt32() v.EventStoreVersion = &x if err != nil { return err } case fh.ID == 104 && fh.Type == wire.TBinary: v.EventBranchToken, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 106 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.SignalCount = &x if err != nil { return err } case fh.ID == 108 && fh.Type == wire.TI64: var x int64 x, err = sr.ReadInt64() v.HistorySize = &x if err != nil { return err } case fh.ID == 110 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClientLibraryVersion = &x if err != nil { return err } case fh.ID == 112 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClientFeatureVersion = &x if err != nil { return err } case fh.ID == 114 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ClientImpl = &x if err != nil { return err } case fh.ID == 115 && fh.Type == wire.TBinary: v.AutoResetPoints, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 116 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.AutoResetPointsEncoding = &x if err != nil { return err } case fh.ID == 118 && fh.Type == wire.TMap: v.SearchAttributes, err = _Map_String_Binary_Decode(sr) if err != nil { return err } case fh.ID == 120 && fh.Type == wire.TMap: v.Memo, err = _Map_String_Binary_Decode(sr) if err != nil { return err } case fh.ID == 122 && fh.Type == wire.TBinary: v.VersionHistories, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 124 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.VersionHistoriesEncoding = &x if err != nil { return err } case fh.ID == 126 && fh.Type == wire.TBinary: v.FirstExecutionRunID, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 128 && fh.Type == wire.TMap: v.PartitionConfig, err = _Map_String_String_Decode(sr) if err != nil { return err } case fh.ID == 130 && fh.Type == wire.TBinary: v.Checksum, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 132 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ChecksumEncoding = &x if err != nil { return err } case fh.ID == 134 && fh.Type == wire.TI32: var x shared.CronOverlapPolicy x, err = _CronOverlapPolicy_Decode(sr) v.CronOverlapPolicy = &x if err != nil { return err } case fh.ID == 137 && fh.Type == wire.TBinary: v.ActiveClusterSelectionPolicy, err = sr.ReadBinary() if err != nil { return err } case fh.ID == 138 && fh.Type == wire.TBinary: var x string x, err = sr.ReadString() v.ActiveClusterSelectionPolicyEncoding = &x if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowExecutionInfo // struct. func (v *WorkflowExecutionInfo) String() string { if v == nil { return "" } var fields [66]string i := 0 if v.ParentDomainID != nil { fields[i] = fmt.Sprintf("ParentDomainID: %v", v.ParentDomainID) i++ } if v.ParentWorkflowID != nil { fields[i] = fmt.Sprintf("ParentWorkflowID: %v", *(v.ParentWorkflowID)) i++ } if v.ParentRunID != nil { fields[i] = fmt.Sprintf("ParentRunID: %v", v.ParentRunID) i++ } if v.InitiatedID != nil { fields[i] = fmt.Sprintf("InitiatedID: %v", *(v.InitiatedID)) i++ } if v.CompletionEventBatchID != nil { fields[i] = fmt.Sprintf("CompletionEventBatchID: %v", *(v.CompletionEventBatchID)) i++ } if v.CompletionEvent != nil { fields[i] = fmt.Sprintf("CompletionEvent: %v", v.CompletionEvent) i++ } if v.CompletionEventEncoding != nil { fields[i] = fmt.Sprintf("CompletionEventEncoding: %v", *(v.CompletionEventEncoding)) i++ } if v.TaskList != nil { fields[i] = fmt.Sprintf("TaskList: %v", *(v.TaskList)) i++ } if v.TaskListKind != nil { fields[i] = fmt.Sprintf("TaskListKind: %v", *(v.TaskListKind)) i++ } if v.WorkflowTypeName != nil { fields[i] = fmt.Sprintf("WorkflowTypeName: %v", *(v.WorkflowTypeName)) i++ } if v.WorkflowTimeoutSeconds != nil { fields[i] = fmt.Sprintf("WorkflowTimeoutSeconds: %v", *(v.WorkflowTimeoutSeconds)) i++ } if v.DecisionTaskTimeoutSeconds != nil { fields[i] = fmt.Sprintf("DecisionTaskTimeoutSeconds: %v", *(v.DecisionTaskTimeoutSeconds)) i++ } if v.ExecutionContext != nil { fields[i] = fmt.Sprintf("ExecutionContext: %v", v.ExecutionContext) i++ } if v.State != nil { fields[i] = fmt.Sprintf("State: %v", *(v.State)) i++ } if v.CloseStatus != nil { fields[i] = fmt.Sprintf("CloseStatus: %v", *(v.CloseStatus)) i++ } if v.StartVersion != nil { fields[i] = fmt.Sprintf("StartVersion: %v", *(v.StartVersion)) i++ } if v.LastWriteEventID != nil { fields[i] = fmt.Sprintf("LastWriteEventID: %v", *(v.LastWriteEventID)) i++ } if v.LastEventTaskID != nil { fields[i] = fmt.Sprintf("LastEventTaskID: %v", *(v.LastEventTaskID)) i++ } if v.LastFirstEventID != nil { fields[i] = fmt.Sprintf("LastFirstEventID: %v", *(v.LastFirstEventID)) i++ } if v.LastProcessedEvent != nil { fields[i] = fmt.Sprintf("LastProcessedEvent: %v", *(v.LastProcessedEvent)) i++ } if v.StartTimeNanos != nil { fields[i] = fmt.Sprintf("StartTimeNanos: %v", *(v.StartTimeNanos)) i++ } if v.LastUpdatedTimeNanos != nil { fields[i] = fmt.Sprintf("LastUpdatedTimeNanos: %v", *(v.LastUpdatedTimeNanos)) i++ } if v.DecisionVersion != nil { fields[i] = fmt.Sprintf("DecisionVersion: %v", *(v.DecisionVersion)) i++ } if v.DecisionScheduleID != nil { fields[i] = fmt.Sprintf("DecisionScheduleID: %v", *(v.DecisionScheduleID)) i++ } if v.DecisionStartedID != nil { fields[i] = fmt.Sprintf("DecisionStartedID: %v", *(v.DecisionStartedID)) i++ } if v.DecisionTimeout != nil { fields[i] = fmt.Sprintf("DecisionTimeout: %v", *(v.DecisionTimeout)) i++ } if v.DecisionAttempt != nil { fields[i] = fmt.Sprintf("DecisionAttempt: %v", *(v.DecisionAttempt)) i++ } if v.DecisionStartedTimestampNanos != nil { fields[i] = fmt.Sprintf("DecisionStartedTimestampNanos: %v", *(v.DecisionStartedTimestampNanos)) i++ } if v.DecisionScheduledTimestampNanos != nil { fields[i] = fmt.Sprintf("DecisionScheduledTimestampNanos: %v", *(v.DecisionScheduledTimestampNanos)) i++ } if v.CancelRequested != nil { fields[i] = fmt.Sprintf("CancelRequested: %v", *(v.CancelRequested)) i++ } if v.DecisionOriginalScheduledTimestampNanos != nil { fields[i] = fmt.Sprintf("DecisionOriginalScheduledTimestampNanos: %v", *(v.DecisionOriginalScheduledTimestampNanos)) i++ } if v.CreateRequestID != nil { fields[i] = fmt.Sprintf("CreateRequestID: %v", *(v.CreateRequestID)) i++ } if v.DecisionRequestID != nil { fields[i] = fmt.Sprintf("DecisionRequestID: %v", *(v.DecisionRequestID)) i++ } if v.CancelRequestID != nil { fields[i] = fmt.Sprintf("CancelRequestID: %v", *(v.CancelRequestID)) i++ } if v.StickyTaskList != nil { fields[i] = fmt.Sprintf("StickyTaskList: %v", *(v.StickyTaskList)) i++ } if v.StickyScheduleToStartTimeout != nil { fields[i] = fmt.Sprintf("StickyScheduleToStartTimeout: %v", *(v.StickyScheduleToStartTimeout)) i++ } if v.RetryAttempt != nil { fields[i] = fmt.Sprintf("RetryAttempt: %v", *(v.RetryAttempt)) i++ } if v.RetryInitialIntervalSeconds != nil { fields[i] = fmt.Sprintf("RetryInitialIntervalSeconds: %v", *(v.RetryInitialIntervalSeconds)) i++ } if v.RetryMaximumIntervalSeconds != nil { fields[i] = fmt.Sprintf("RetryMaximumIntervalSeconds: %v", *(v.RetryMaximumIntervalSeconds)) i++ } if v.RetryMaximumAttempts != nil { fields[i] = fmt.Sprintf("RetryMaximumAttempts: %v", *(v.RetryMaximumAttempts)) i++ } if v.RetryExpirationSeconds != nil { fields[i] = fmt.Sprintf("RetryExpirationSeconds: %v", *(v.RetryExpirationSeconds)) i++ } if v.RetryBackoffCoefficient != nil { fields[i] = fmt.Sprintf("RetryBackoffCoefficient: %v", *(v.RetryBackoffCoefficient)) i++ } if v.RetryExpirationTimeNanos != nil { fields[i] = fmt.Sprintf("RetryExpirationTimeNanos: %v", *(v.RetryExpirationTimeNanos)) i++ } if v.RetryNonRetryableErrors != nil { fields[i] = fmt.Sprintf("RetryNonRetryableErrors: %v", v.RetryNonRetryableErrors) i++ } if v.HasRetryPolicy != nil { fields[i] = fmt.Sprintf("HasRetryPolicy: %v", *(v.HasRetryPolicy)) i++ } if v.CronSchedule != nil { fields[i] = fmt.Sprintf("CronSchedule: %v", *(v.CronSchedule)) i++ } if v.EventStoreVersion != nil { fields[i] = fmt.Sprintf("EventStoreVersion: %v", *(v.EventStoreVersion)) i++ } if v.EventBranchToken != nil { fields[i] = fmt.Sprintf("EventBranchToken: %v", v.EventBranchToken) i++ } if v.SignalCount != nil { fields[i] = fmt.Sprintf("SignalCount: %v", *(v.SignalCount)) i++ } if v.HistorySize != nil { fields[i] = fmt.Sprintf("HistorySize: %v", *(v.HistorySize)) i++ } if v.ClientLibraryVersion != nil { fields[i] = fmt.Sprintf("ClientLibraryVersion: %v", *(v.ClientLibraryVersion)) i++ } if v.ClientFeatureVersion != nil { fields[i] = fmt.Sprintf("ClientFeatureVersion: %v", *(v.ClientFeatureVersion)) i++ } if v.ClientImpl != nil { fields[i] = fmt.Sprintf("ClientImpl: %v", *(v.ClientImpl)) i++ } if v.AutoResetPoints != nil { fields[i] = fmt.Sprintf("AutoResetPoints: %v", v.AutoResetPoints) i++ } if v.AutoResetPointsEncoding != nil { fields[i] = fmt.Sprintf("AutoResetPointsEncoding: %v", *(v.AutoResetPointsEncoding)) i++ } if v.SearchAttributes != nil { fields[i] = fmt.Sprintf("SearchAttributes: %v", v.SearchAttributes) i++ } if v.Memo != nil { fields[i] = fmt.Sprintf("Memo: %v", v.Memo) i++ } if v.VersionHistories != nil { fields[i] = fmt.Sprintf("VersionHistories: %v", v.VersionHistories) i++ } if v.VersionHistoriesEncoding != nil { fields[i] = fmt.Sprintf("VersionHistoriesEncoding: %v", *(v.VersionHistoriesEncoding)) i++ } if v.FirstExecutionRunID != nil { fields[i] = fmt.Sprintf("FirstExecutionRunID: %v", v.FirstExecutionRunID) i++ } if v.PartitionConfig != nil { fields[i] = fmt.Sprintf("PartitionConfig: %v", v.PartitionConfig) i++ } if v.Checksum != nil { fields[i] = fmt.Sprintf("Checksum: %v", v.Checksum) i++ } if v.ChecksumEncoding != nil { fields[i] = fmt.Sprintf("ChecksumEncoding: %v", *(v.ChecksumEncoding)) i++ } if v.CronOverlapPolicy != nil { fields[i] = fmt.Sprintf("CronOverlapPolicy: %v", *(v.CronOverlapPolicy)) i++ } if v.ActiveClusterSelectionPolicy != nil { fields[i] = fmt.Sprintf("ActiveClusterSelectionPolicy: %v", v.ActiveClusterSelectionPolicy) i++ } if v.ActiveClusterSelectionPolicyEncoding != nil { fields[i] = fmt.Sprintf("ActiveClusterSelectionPolicyEncoding: %v", *(v.ActiveClusterSelectionPolicyEncoding)) i++ } return fmt.Sprintf("WorkflowExecutionInfo{%v}", strings.Join(fields[:i], ", ")) } func _Map_String_Binary_Equals(lhs, rhs map[string][]byte) bool { if len(lhs) != len(rhs) { return false } for lk, lv := range lhs { rv, ok := rhs[lk] if !ok { return false } if !bytes.Equal(lv, rv) { return false } } return true } func _CronOverlapPolicy_EqualsPtr(lhs, rhs *shared.CronOverlapPolicy) bool { if lhs != nil && rhs != nil { x := *lhs y := *rhs return x.Equals(y) } return lhs == nil && rhs == nil } // Equals returns true if all the fields of this WorkflowExecutionInfo match the // provided WorkflowExecutionInfo. // // This function performs a deep comparison. func (v *WorkflowExecutionInfo) Equals(rhs *WorkflowExecutionInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.ParentDomainID == nil && rhs.ParentDomainID == nil) || (v.ParentDomainID != nil && rhs.ParentDomainID != nil && bytes.Equal(v.ParentDomainID, rhs.ParentDomainID))) { return false } if !_String_EqualsPtr(v.ParentWorkflowID, rhs.ParentWorkflowID) { return false } if !((v.ParentRunID == nil && rhs.ParentRunID == nil) || (v.ParentRunID != nil && rhs.ParentRunID != nil && bytes.Equal(v.ParentRunID, rhs.ParentRunID))) { return false } if !_I64_EqualsPtr(v.InitiatedID, rhs.InitiatedID) { return false } if !_I64_EqualsPtr(v.CompletionEventBatchID, rhs.CompletionEventBatchID) { return false } if !((v.CompletionEvent == nil && rhs.CompletionEvent == nil) || (v.CompletionEvent != nil && rhs.CompletionEvent != nil && bytes.Equal(v.CompletionEvent, rhs.CompletionEvent))) { return false } if !_String_EqualsPtr(v.CompletionEventEncoding, rhs.CompletionEventEncoding) { return false } if !_String_EqualsPtr(v.TaskList, rhs.TaskList) { return false } if !_TaskListKind_EqualsPtr(v.TaskListKind, rhs.TaskListKind) { return false } if !_String_EqualsPtr(v.WorkflowTypeName, rhs.WorkflowTypeName) { return false } if !_I32_EqualsPtr(v.WorkflowTimeoutSeconds, rhs.WorkflowTimeoutSeconds) { return false } if !_I32_EqualsPtr(v.DecisionTaskTimeoutSeconds, rhs.DecisionTaskTimeoutSeconds) { return false } if !((v.ExecutionContext == nil && rhs.ExecutionContext == nil) || (v.ExecutionContext != nil && rhs.ExecutionContext != nil && bytes.Equal(v.ExecutionContext, rhs.ExecutionContext))) { return false } if !_I32_EqualsPtr(v.State, rhs.State) { return false } if !_I32_EqualsPtr(v.CloseStatus, rhs.CloseStatus) { return false } if !_I64_EqualsPtr(v.StartVersion, rhs.StartVersion) { return false } if !_I64_EqualsPtr(v.LastWriteEventID, rhs.LastWriteEventID) { return false } if !_I64_EqualsPtr(v.LastEventTaskID, rhs.LastEventTaskID) { return false } if !_I64_EqualsPtr(v.LastFirstEventID, rhs.LastFirstEventID) { return false } if !_I64_EqualsPtr(v.LastProcessedEvent, rhs.LastProcessedEvent) { return false } if !_I64_EqualsPtr(v.StartTimeNanos, rhs.StartTimeNanos) { return false } if !_I64_EqualsPtr(v.LastUpdatedTimeNanos, rhs.LastUpdatedTimeNanos) { return false } if !_I64_EqualsPtr(v.DecisionVersion, rhs.DecisionVersion) { return false } if !_I64_EqualsPtr(v.DecisionScheduleID, rhs.DecisionScheduleID) { return false } if !_I64_EqualsPtr(v.DecisionStartedID, rhs.DecisionStartedID) { return false } if !_I32_EqualsPtr(v.DecisionTimeout, rhs.DecisionTimeout) { return false } if !_I64_EqualsPtr(v.DecisionAttempt, rhs.DecisionAttempt) { return false } if !_I64_EqualsPtr(v.DecisionStartedTimestampNanos, rhs.DecisionStartedTimestampNanos) { return false } if !_I64_EqualsPtr(v.DecisionScheduledTimestampNanos, rhs.DecisionScheduledTimestampNanos) { return false } if !_Bool_EqualsPtr(v.CancelRequested, rhs.CancelRequested) { return false } if !_I64_EqualsPtr(v.DecisionOriginalScheduledTimestampNanos, rhs.DecisionOriginalScheduledTimestampNanos) { return false } if !_String_EqualsPtr(v.CreateRequestID, rhs.CreateRequestID) { return false } if !_String_EqualsPtr(v.DecisionRequestID, rhs.DecisionRequestID) { return false } if !_String_EqualsPtr(v.CancelRequestID, rhs.CancelRequestID) { return false } if !_String_EqualsPtr(v.StickyTaskList, rhs.StickyTaskList) { return false } if !_I64_EqualsPtr(v.StickyScheduleToStartTimeout, rhs.StickyScheduleToStartTimeout) { return false } if !_I64_EqualsPtr(v.RetryAttempt, rhs.RetryAttempt) { return false } if !_I32_EqualsPtr(v.RetryInitialIntervalSeconds, rhs.RetryInitialIntervalSeconds) { return false } if !_I32_EqualsPtr(v.RetryMaximumIntervalSeconds, rhs.RetryMaximumIntervalSeconds) { return false } if !_I32_EqualsPtr(v.RetryMaximumAttempts, rhs.RetryMaximumAttempts) { return false } if !_I32_EqualsPtr(v.RetryExpirationSeconds, rhs.RetryExpirationSeconds) { return false } if !_Double_EqualsPtr(v.RetryBackoffCoefficient, rhs.RetryBackoffCoefficient) { return false } if !_I64_EqualsPtr(v.RetryExpirationTimeNanos, rhs.RetryExpirationTimeNanos) { return false } if !((v.RetryNonRetryableErrors == nil && rhs.RetryNonRetryableErrors == nil) || (v.RetryNonRetryableErrors != nil && rhs.RetryNonRetryableErrors != nil && _List_String_Equals(v.RetryNonRetryableErrors, rhs.RetryNonRetryableErrors))) { return false } if !_Bool_EqualsPtr(v.HasRetryPolicy, rhs.HasRetryPolicy) { return false } if !_String_EqualsPtr(v.CronSchedule, rhs.CronSchedule) { return false } if !_I32_EqualsPtr(v.EventStoreVersion, rhs.EventStoreVersion) { return false } if !((v.EventBranchToken == nil && rhs.EventBranchToken == nil) || (v.EventBranchToken != nil && rhs.EventBranchToken != nil && bytes.Equal(v.EventBranchToken, rhs.EventBranchToken))) { return false } if !_I64_EqualsPtr(v.SignalCount, rhs.SignalCount) { return false } if !_I64_EqualsPtr(v.HistorySize, rhs.HistorySize) { return false } if !_String_EqualsPtr(v.ClientLibraryVersion, rhs.ClientLibraryVersion) { return false } if !_String_EqualsPtr(v.ClientFeatureVersion, rhs.ClientFeatureVersion) { return false } if !_String_EqualsPtr(v.ClientImpl, rhs.ClientImpl) { return false } if !((v.AutoResetPoints == nil && rhs.AutoResetPoints == nil) || (v.AutoResetPoints != nil && rhs.AutoResetPoints != nil && bytes.Equal(v.AutoResetPoints, rhs.AutoResetPoints))) { return false } if !_String_EqualsPtr(v.AutoResetPointsEncoding, rhs.AutoResetPointsEncoding) { return false } if !((v.SearchAttributes == nil && rhs.SearchAttributes == nil) || (v.SearchAttributes != nil && rhs.SearchAttributes != nil && _Map_String_Binary_Equals(v.SearchAttributes, rhs.SearchAttributes))) { return false } if !((v.Memo == nil && rhs.Memo == nil) || (v.Memo != nil && rhs.Memo != nil && _Map_String_Binary_Equals(v.Memo, rhs.Memo))) { return false } if !((v.VersionHistories == nil && rhs.VersionHistories == nil) || (v.VersionHistories != nil && rhs.VersionHistories != nil && bytes.Equal(v.VersionHistories, rhs.VersionHistories))) { return false } if !_String_EqualsPtr(v.VersionHistoriesEncoding, rhs.VersionHistoriesEncoding) { return false } if !((v.FirstExecutionRunID == nil && rhs.FirstExecutionRunID == nil) || (v.FirstExecutionRunID != nil && rhs.FirstExecutionRunID != nil && bytes.Equal(v.FirstExecutionRunID, rhs.FirstExecutionRunID))) { return false } if !((v.PartitionConfig == nil && rhs.PartitionConfig == nil) || (v.PartitionConfig != nil && rhs.PartitionConfig != nil && _Map_String_String_Equals(v.PartitionConfig, rhs.PartitionConfig))) { return false } if !((v.Checksum == nil && rhs.Checksum == nil) || (v.Checksum != nil && rhs.Checksum != nil && bytes.Equal(v.Checksum, rhs.Checksum))) { return false } if !_String_EqualsPtr(v.ChecksumEncoding, rhs.ChecksumEncoding) { return false } if !_CronOverlapPolicy_EqualsPtr(v.CronOverlapPolicy, rhs.CronOverlapPolicy) { return false } if !((v.ActiveClusterSelectionPolicy == nil && rhs.ActiveClusterSelectionPolicy == nil) || (v.ActiveClusterSelectionPolicy != nil && rhs.ActiveClusterSelectionPolicy != nil && bytes.Equal(v.ActiveClusterSelectionPolicy, rhs.ActiveClusterSelectionPolicy))) { return false } if !_String_EqualsPtr(v.ActiveClusterSelectionPolicyEncoding, rhs.ActiveClusterSelectionPolicyEncoding) { return false } return true } type _Map_String_Binary_Zapper map[string][]byte // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of _Map_String_Binary_Zapper. func (m _Map_String_Binary_Zapper) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { for k, v := range m { enc.AddString((string)(k), base64.StdEncoding.EncodeToString(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowExecutionInfo. func (v *WorkflowExecutionInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.ParentDomainID != nil { enc.AddString("parentDomainID", base64.StdEncoding.EncodeToString(v.ParentDomainID)) } if v.ParentWorkflowID != nil { enc.AddString("parentWorkflowID", *v.ParentWorkflowID) } if v.ParentRunID != nil { enc.AddString("parentRunID", base64.StdEncoding.EncodeToString(v.ParentRunID)) } if v.InitiatedID != nil { enc.AddInt64("initiatedID", *v.InitiatedID) } if v.CompletionEventBatchID != nil { enc.AddInt64("completionEventBatchID", *v.CompletionEventBatchID) } if v.CompletionEvent != nil { enc.AddString("completionEvent", base64.StdEncoding.EncodeToString(v.CompletionEvent)) } if v.CompletionEventEncoding != nil { enc.AddString("completionEventEncoding", *v.CompletionEventEncoding) } if v.TaskList != nil { enc.AddString("taskList", *v.TaskList) } if v.TaskListKind != nil { err = multierr.Append(err, enc.AddObject("taskListKind", *v.TaskListKind)) } if v.WorkflowTypeName != nil { enc.AddString("workflowTypeName", *v.WorkflowTypeName) } if v.WorkflowTimeoutSeconds != nil { enc.AddInt32("workflowTimeoutSeconds", *v.WorkflowTimeoutSeconds) } if v.DecisionTaskTimeoutSeconds != nil { enc.AddInt32("decisionTaskTimeoutSeconds", *v.DecisionTaskTimeoutSeconds) } if v.ExecutionContext != nil { enc.AddString("executionContext", base64.StdEncoding.EncodeToString(v.ExecutionContext)) } if v.State != nil { enc.AddInt32("state", *v.State) } if v.CloseStatus != nil { enc.AddInt32("closeStatus", *v.CloseStatus) } if v.StartVersion != nil { enc.AddInt64("startVersion", *v.StartVersion) } if v.LastWriteEventID != nil { enc.AddInt64("lastWriteEventID", *v.LastWriteEventID) } if v.LastEventTaskID != nil { enc.AddInt64("lastEventTaskID", *v.LastEventTaskID) } if v.LastFirstEventID != nil { enc.AddInt64("lastFirstEventID", *v.LastFirstEventID) } if v.LastProcessedEvent != nil { enc.AddInt64("lastProcessedEvent", *v.LastProcessedEvent) } if v.StartTimeNanos != nil { enc.AddInt64("startTimeNanos", *v.StartTimeNanos) } if v.LastUpdatedTimeNanos != nil { enc.AddInt64("lastUpdatedTimeNanos", *v.LastUpdatedTimeNanos) } if v.DecisionVersion != nil { enc.AddInt64("decisionVersion", *v.DecisionVersion) } if v.DecisionScheduleID != nil { enc.AddInt64("decisionScheduleID", *v.DecisionScheduleID) } if v.DecisionStartedID != nil { enc.AddInt64("decisionStartedID", *v.DecisionStartedID) } if v.DecisionTimeout != nil { enc.AddInt32("decisionTimeout", *v.DecisionTimeout) } if v.DecisionAttempt != nil { enc.AddInt64("decisionAttempt", *v.DecisionAttempt) } if v.DecisionStartedTimestampNanos != nil { enc.AddInt64("decisionStartedTimestampNanos", *v.DecisionStartedTimestampNanos) } if v.DecisionScheduledTimestampNanos != nil { enc.AddInt64("decisionScheduledTimestampNanos", *v.DecisionScheduledTimestampNanos) } if v.CancelRequested != nil { enc.AddBool("cancelRequested", *v.CancelRequested) } if v.DecisionOriginalScheduledTimestampNanos != nil { enc.AddInt64("decisionOriginalScheduledTimestampNanos", *v.DecisionOriginalScheduledTimestampNanos) } if v.CreateRequestID != nil { enc.AddString("createRequestID", *v.CreateRequestID) } if v.DecisionRequestID != nil { enc.AddString("decisionRequestID", *v.DecisionRequestID) } if v.CancelRequestID != nil { enc.AddString("cancelRequestID", *v.CancelRequestID) } if v.StickyTaskList != nil { enc.AddString("stickyTaskList", *v.StickyTaskList) } if v.StickyScheduleToStartTimeout != nil { enc.AddInt64("stickyScheduleToStartTimeout", *v.StickyScheduleToStartTimeout) } if v.RetryAttempt != nil { enc.AddInt64("retryAttempt", *v.RetryAttempt) } if v.RetryInitialIntervalSeconds != nil { enc.AddInt32("retryInitialIntervalSeconds", *v.RetryInitialIntervalSeconds) } if v.RetryMaximumIntervalSeconds != nil { enc.AddInt32("retryMaximumIntervalSeconds", *v.RetryMaximumIntervalSeconds) } if v.RetryMaximumAttempts != nil { enc.AddInt32("retryMaximumAttempts", *v.RetryMaximumAttempts) } if v.RetryExpirationSeconds != nil { enc.AddInt32("retryExpirationSeconds", *v.RetryExpirationSeconds) } if v.RetryBackoffCoefficient != nil { enc.AddFloat64("retryBackoffCoefficient", *v.RetryBackoffCoefficient) } if v.RetryExpirationTimeNanos != nil { enc.AddInt64("retryExpirationTimeNanos", *v.RetryExpirationTimeNanos) } if v.RetryNonRetryableErrors != nil { err = multierr.Append(err, enc.AddArray("retryNonRetryableErrors", (_List_String_Zapper)(v.RetryNonRetryableErrors))) } if v.HasRetryPolicy != nil { enc.AddBool("hasRetryPolicy", *v.HasRetryPolicy) } if v.CronSchedule != nil { enc.AddString("cronSchedule", *v.CronSchedule) } if v.EventStoreVersion != nil { enc.AddInt32("eventStoreVersion", *v.EventStoreVersion) } if v.EventBranchToken != nil { enc.AddString("eventBranchToken", base64.StdEncoding.EncodeToString(v.EventBranchToken)) } if v.SignalCount != nil { enc.AddInt64("signalCount", *v.SignalCount) } if v.HistorySize != nil { enc.AddInt64("historySize", *v.HistorySize) } if v.ClientLibraryVersion != nil { enc.AddString("clientLibraryVersion", *v.ClientLibraryVersion) } if v.ClientFeatureVersion != nil { enc.AddString("clientFeatureVersion", *v.ClientFeatureVersion) } if v.ClientImpl != nil { enc.AddString("clientImpl", *v.ClientImpl) } if v.AutoResetPoints != nil { enc.AddString("autoResetPoints", base64.StdEncoding.EncodeToString(v.AutoResetPoints)) } if v.AutoResetPointsEncoding != nil { enc.AddString("autoResetPointsEncoding", *v.AutoResetPointsEncoding) } if v.SearchAttributes != nil { err = multierr.Append(err, enc.AddObject("searchAttributes", (_Map_String_Binary_Zapper)(v.SearchAttributes))) } if v.Memo != nil { err = multierr.Append(err, enc.AddObject("memo", (_Map_String_Binary_Zapper)(v.Memo))) } if v.VersionHistories != nil { enc.AddString("versionHistories", base64.StdEncoding.EncodeToString(v.VersionHistories)) } if v.VersionHistoriesEncoding != nil { enc.AddString("versionHistoriesEncoding", *v.VersionHistoriesEncoding) } if v.FirstExecutionRunID != nil { enc.AddString("firstExecutionRunID", base64.StdEncoding.EncodeToString(v.FirstExecutionRunID)) } if v.PartitionConfig != nil { err = multierr.Append(err, enc.AddObject("partitionConfig", (_Map_String_String_Zapper)(v.PartitionConfig))) } if v.Checksum != nil { enc.AddString("checksum", base64.StdEncoding.EncodeToString(v.Checksum)) } if v.ChecksumEncoding != nil { enc.AddString("checksumEncoding", *v.ChecksumEncoding) } if v.CronOverlapPolicy != nil { err = multierr.Append(err, enc.AddObject("cronOverlapPolicy", *v.CronOverlapPolicy)) } if v.ActiveClusterSelectionPolicy != nil { enc.AddString("activeClusterSelectionPolicy", base64.StdEncoding.EncodeToString(v.ActiveClusterSelectionPolicy)) } if v.ActiveClusterSelectionPolicyEncoding != nil { enc.AddString("activeClusterSelectionPolicyEncoding", *v.ActiveClusterSelectionPolicyEncoding) } return err } // GetParentDomainID returns the value of ParentDomainID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetParentDomainID() (o []byte) { if v != nil && v.ParentDomainID != nil { return v.ParentDomainID } return } // IsSetParentDomainID returns true if ParentDomainID is not nil. func (v *WorkflowExecutionInfo) IsSetParentDomainID() bool { return v != nil && v.ParentDomainID != nil } // GetParentWorkflowID returns the value of ParentWorkflowID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetParentWorkflowID() (o string) { if v != nil && v.ParentWorkflowID != nil { return *v.ParentWorkflowID } return } // IsSetParentWorkflowID returns true if ParentWorkflowID is not nil. func (v *WorkflowExecutionInfo) IsSetParentWorkflowID() bool { return v != nil && v.ParentWorkflowID != nil } // GetParentRunID returns the value of ParentRunID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetParentRunID() (o []byte) { if v != nil && v.ParentRunID != nil { return v.ParentRunID } return } // IsSetParentRunID returns true if ParentRunID is not nil. func (v *WorkflowExecutionInfo) IsSetParentRunID() bool { return v != nil && v.ParentRunID != nil } // GetInitiatedID returns the value of InitiatedID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetInitiatedID() (o int64) { if v != nil && v.InitiatedID != nil { return *v.InitiatedID } return } // IsSetInitiatedID returns true if InitiatedID is not nil. func (v *WorkflowExecutionInfo) IsSetInitiatedID() bool { return v != nil && v.InitiatedID != nil } // GetCompletionEventBatchID returns the value of CompletionEventBatchID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCompletionEventBatchID() (o int64) { if v != nil && v.CompletionEventBatchID != nil { return *v.CompletionEventBatchID } return } // IsSetCompletionEventBatchID returns true if CompletionEventBatchID is not nil. func (v *WorkflowExecutionInfo) IsSetCompletionEventBatchID() bool { return v != nil && v.CompletionEventBatchID != nil } // GetCompletionEvent returns the value of CompletionEvent if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCompletionEvent() (o []byte) { if v != nil && v.CompletionEvent != nil { return v.CompletionEvent } return } // IsSetCompletionEvent returns true if CompletionEvent is not nil. func (v *WorkflowExecutionInfo) IsSetCompletionEvent() bool { return v != nil && v.CompletionEvent != nil } // GetCompletionEventEncoding returns the value of CompletionEventEncoding if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCompletionEventEncoding() (o string) { if v != nil && v.CompletionEventEncoding != nil { return *v.CompletionEventEncoding } return } // IsSetCompletionEventEncoding returns true if CompletionEventEncoding is not nil. func (v *WorkflowExecutionInfo) IsSetCompletionEventEncoding() bool { return v != nil && v.CompletionEventEncoding != nil } // GetTaskList returns the value of TaskList if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetTaskList() (o string) { if v != nil && v.TaskList != nil { return *v.TaskList } return } // IsSetTaskList returns true if TaskList is not nil. func (v *WorkflowExecutionInfo) IsSetTaskList() bool { return v != nil && v.TaskList != nil } // GetTaskListKind returns the value of TaskListKind if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetTaskListKind() (o shared.TaskListKind) { if v != nil && v.TaskListKind != nil { return *v.TaskListKind } return } // IsSetTaskListKind returns true if TaskListKind is not nil. func (v *WorkflowExecutionInfo) IsSetTaskListKind() bool { return v != nil && v.TaskListKind != nil } // GetWorkflowTypeName returns the value of WorkflowTypeName if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetWorkflowTypeName() (o string) { if v != nil && v.WorkflowTypeName != nil { return *v.WorkflowTypeName } return } // IsSetWorkflowTypeName returns true if WorkflowTypeName is not nil. func (v *WorkflowExecutionInfo) IsSetWorkflowTypeName() bool { return v != nil && v.WorkflowTypeName != nil } // GetWorkflowTimeoutSeconds returns the value of WorkflowTimeoutSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetWorkflowTimeoutSeconds() (o int32) { if v != nil && v.WorkflowTimeoutSeconds != nil { return *v.WorkflowTimeoutSeconds } return } // IsSetWorkflowTimeoutSeconds returns true if WorkflowTimeoutSeconds is not nil. func (v *WorkflowExecutionInfo) IsSetWorkflowTimeoutSeconds() bool { return v != nil && v.WorkflowTimeoutSeconds != nil } // GetDecisionTaskTimeoutSeconds returns the value of DecisionTaskTimeoutSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetDecisionTaskTimeoutSeconds() (o int32) { if v != nil && v.DecisionTaskTimeoutSeconds != nil { return *v.DecisionTaskTimeoutSeconds } return } // IsSetDecisionTaskTimeoutSeconds returns true if DecisionTaskTimeoutSeconds is not nil. func (v *WorkflowExecutionInfo) IsSetDecisionTaskTimeoutSeconds() bool { return v != nil && v.DecisionTaskTimeoutSeconds != nil } // GetExecutionContext returns the value of ExecutionContext if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetExecutionContext() (o []byte) { if v != nil && v.ExecutionContext != nil { return v.ExecutionContext } return } // IsSetExecutionContext returns true if ExecutionContext is not nil. func (v *WorkflowExecutionInfo) IsSetExecutionContext() bool { return v != nil && v.ExecutionContext != nil } // GetState returns the value of State if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetState() (o int32) { if v != nil && v.State != nil { return *v.State } return } // IsSetState returns true if State is not nil. func (v *WorkflowExecutionInfo) IsSetState() bool { return v != nil && v.State != nil } // GetCloseStatus returns the value of CloseStatus if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCloseStatus() (o int32) { if v != nil && v.CloseStatus != nil { return *v.CloseStatus } return } // IsSetCloseStatus returns true if CloseStatus is not nil. func (v *WorkflowExecutionInfo) IsSetCloseStatus() bool { return v != nil && v.CloseStatus != nil } // GetStartVersion returns the value of StartVersion if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetStartVersion() (o int64) { if v != nil && v.StartVersion != nil { return *v.StartVersion } return } // IsSetStartVersion returns true if StartVersion is not nil. func (v *WorkflowExecutionInfo) IsSetStartVersion() bool { return v != nil && v.StartVersion != nil } // GetLastWriteEventID returns the value of LastWriteEventID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetLastWriteEventID() (o int64) { if v != nil && v.LastWriteEventID != nil { return *v.LastWriteEventID } return } // IsSetLastWriteEventID returns true if LastWriteEventID is not nil. func (v *WorkflowExecutionInfo) IsSetLastWriteEventID() bool { return v != nil && v.LastWriteEventID != nil } // GetLastEventTaskID returns the value of LastEventTaskID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetLastEventTaskID() (o int64) { if v != nil && v.LastEventTaskID != nil { return *v.LastEventTaskID } return } // IsSetLastEventTaskID returns true if LastEventTaskID is not nil. func (v *WorkflowExecutionInfo) IsSetLastEventTaskID() bool { return v != nil && v.LastEventTaskID != nil } // GetLastFirstEventID returns the value of LastFirstEventID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetLastFirstEventID() (o int64) { if v != nil && v.LastFirstEventID != nil { return *v.LastFirstEventID } return } // IsSetLastFirstEventID returns true if LastFirstEventID is not nil. func (v *WorkflowExecutionInfo) IsSetLastFirstEventID() bool { return v != nil && v.LastFirstEventID != nil } // GetLastProcessedEvent returns the value of LastProcessedEvent if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetLastProcessedEvent() (o int64) { if v != nil && v.LastProcessedEvent != nil { return *v.LastProcessedEvent } return } // IsSetLastProcessedEvent returns true if LastProcessedEvent is not nil. func (v *WorkflowExecutionInfo) IsSetLastProcessedEvent() bool { return v != nil && v.LastProcessedEvent != nil } // GetStartTimeNanos returns the value of StartTimeNanos if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetStartTimeNanos() (o int64) { if v != nil && v.StartTimeNanos != nil { return *v.StartTimeNanos } return } // IsSetStartTimeNanos returns true if StartTimeNanos is not nil. func (v *WorkflowExecutionInfo) IsSetStartTimeNanos() bool { return v != nil && v.StartTimeNanos != nil } // GetLastUpdatedTimeNanos returns the value of LastUpdatedTimeNanos if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetLastUpdatedTimeNanos() (o int64) { if v != nil && v.LastUpdatedTimeNanos != nil { return *v.LastUpdatedTimeNanos } return } // IsSetLastUpdatedTimeNanos returns true if LastUpdatedTimeNanos is not nil. func (v *WorkflowExecutionInfo) IsSetLastUpdatedTimeNanos() bool { return v != nil && v.LastUpdatedTimeNanos != nil } // GetDecisionVersion returns the value of DecisionVersion if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetDecisionVersion() (o int64) { if v != nil && v.DecisionVersion != nil { return *v.DecisionVersion } return } // IsSetDecisionVersion returns true if DecisionVersion is not nil. func (v *WorkflowExecutionInfo) IsSetDecisionVersion() bool { return v != nil && v.DecisionVersion != nil } // GetDecisionScheduleID returns the value of DecisionScheduleID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetDecisionScheduleID() (o int64) { if v != nil && v.DecisionScheduleID != nil { return *v.DecisionScheduleID } return } // IsSetDecisionScheduleID returns true if DecisionScheduleID is not nil. func (v *WorkflowExecutionInfo) IsSetDecisionScheduleID() bool { return v != nil && v.DecisionScheduleID != nil } // GetDecisionStartedID returns the value of DecisionStartedID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetDecisionStartedID() (o int64) { if v != nil && v.DecisionStartedID != nil { return *v.DecisionStartedID } return } // IsSetDecisionStartedID returns true if DecisionStartedID is not nil. func (v *WorkflowExecutionInfo) IsSetDecisionStartedID() bool { return v != nil && v.DecisionStartedID != nil } // GetDecisionTimeout returns the value of DecisionTimeout if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetDecisionTimeout() (o int32) { if v != nil && v.DecisionTimeout != nil { return *v.DecisionTimeout } return } // IsSetDecisionTimeout returns true if DecisionTimeout is not nil. func (v *WorkflowExecutionInfo) IsSetDecisionTimeout() bool { return v != nil && v.DecisionTimeout != nil } // GetDecisionAttempt returns the value of DecisionAttempt if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetDecisionAttempt() (o int64) { if v != nil && v.DecisionAttempt != nil { return *v.DecisionAttempt } return } // IsSetDecisionAttempt returns true if DecisionAttempt is not nil. func (v *WorkflowExecutionInfo) IsSetDecisionAttempt() bool { return v != nil && v.DecisionAttempt != nil } // GetDecisionStartedTimestampNanos returns the value of DecisionStartedTimestampNanos if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetDecisionStartedTimestampNanos() (o int64) { if v != nil && v.DecisionStartedTimestampNanos != nil { return *v.DecisionStartedTimestampNanos } return } // IsSetDecisionStartedTimestampNanos returns true if DecisionStartedTimestampNanos is not nil. func (v *WorkflowExecutionInfo) IsSetDecisionStartedTimestampNanos() bool { return v != nil && v.DecisionStartedTimestampNanos != nil } // GetDecisionScheduledTimestampNanos returns the value of DecisionScheduledTimestampNanos if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetDecisionScheduledTimestampNanos() (o int64) { if v != nil && v.DecisionScheduledTimestampNanos != nil { return *v.DecisionScheduledTimestampNanos } return } // IsSetDecisionScheduledTimestampNanos returns true if DecisionScheduledTimestampNanos is not nil. func (v *WorkflowExecutionInfo) IsSetDecisionScheduledTimestampNanos() bool { return v != nil && v.DecisionScheduledTimestampNanos != nil } // GetCancelRequested returns the value of CancelRequested if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCancelRequested() (o bool) { if v != nil && v.CancelRequested != nil { return *v.CancelRequested } return } // IsSetCancelRequested returns true if CancelRequested is not nil. func (v *WorkflowExecutionInfo) IsSetCancelRequested() bool { return v != nil && v.CancelRequested != nil } // GetDecisionOriginalScheduledTimestampNanos returns the value of DecisionOriginalScheduledTimestampNanos if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetDecisionOriginalScheduledTimestampNanos() (o int64) { if v != nil && v.DecisionOriginalScheduledTimestampNanos != nil { return *v.DecisionOriginalScheduledTimestampNanos } return } // IsSetDecisionOriginalScheduledTimestampNanos returns true if DecisionOriginalScheduledTimestampNanos is not nil. func (v *WorkflowExecutionInfo) IsSetDecisionOriginalScheduledTimestampNanos() bool { return v != nil && v.DecisionOriginalScheduledTimestampNanos != nil } // GetCreateRequestID returns the value of CreateRequestID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCreateRequestID() (o string) { if v != nil && v.CreateRequestID != nil { return *v.CreateRequestID } return } // IsSetCreateRequestID returns true if CreateRequestID is not nil. func (v *WorkflowExecutionInfo) IsSetCreateRequestID() bool { return v != nil && v.CreateRequestID != nil } // GetDecisionRequestID returns the value of DecisionRequestID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetDecisionRequestID() (o string) { if v != nil && v.DecisionRequestID != nil { return *v.DecisionRequestID } return } // IsSetDecisionRequestID returns true if DecisionRequestID is not nil. func (v *WorkflowExecutionInfo) IsSetDecisionRequestID() bool { return v != nil && v.DecisionRequestID != nil } // GetCancelRequestID returns the value of CancelRequestID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCancelRequestID() (o string) { if v != nil && v.CancelRequestID != nil { return *v.CancelRequestID } return } // IsSetCancelRequestID returns true if CancelRequestID is not nil. func (v *WorkflowExecutionInfo) IsSetCancelRequestID() bool { return v != nil && v.CancelRequestID != nil } // GetStickyTaskList returns the value of StickyTaskList if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetStickyTaskList() (o string) { if v != nil && v.StickyTaskList != nil { return *v.StickyTaskList } return } // IsSetStickyTaskList returns true if StickyTaskList is not nil. func (v *WorkflowExecutionInfo) IsSetStickyTaskList() bool { return v != nil && v.StickyTaskList != nil } // GetStickyScheduleToStartTimeout returns the value of StickyScheduleToStartTimeout if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetStickyScheduleToStartTimeout() (o int64) { if v != nil && v.StickyScheduleToStartTimeout != nil { return *v.StickyScheduleToStartTimeout } return } // IsSetStickyScheduleToStartTimeout returns true if StickyScheduleToStartTimeout is not nil. func (v *WorkflowExecutionInfo) IsSetStickyScheduleToStartTimeout() bool { return v != nil && v.StickyScheduleToStartTimeout != nil } // GetRetryAttempt returns the value of RetryAttempt if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetRetryAttempt() (o int64) { if v != nil && v.RetryAttempt != nil { return *v.RetryAttempt } return } // IsSetRetryAttempt returns true if RetryAttempt is not nil. func (v *WorkflowExecutionInfo) IsSetRetryAttempt() bool { return v != nil && v.RetryAttempt != nil } // GetRetryInitialIntervalSeconds returns the value of RetryInitialIntervalSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetRetryInitialIntervalSeconds() (o int32) { if v != nil && v.RetryInitialIntervalSeconds != nil { return *v.RetryInitialIntervalSeconds } return } // IsSetRetryInitialIntervalSeconds returns true if RetryInitialIntervalSeconds is not nil. func (v *WorkflowExecutionInfo) IsSetRetryInitialIntervalSeconds() bool { return v != nil && v.RetryInitialIntervalSeconds != nil } // GetRetryMaximumIntervalSeconds returns the value of RetryMaximumIntervalSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetRetryMaximumIntervalSeconds() (o int32) { if v != nil && v.RetryMaximumIntervalSeconds != nil { return *v.RetryMaximumIntervalSeconds } return } // IsSetRetryMaximumIntervalSeconds returns true if RetryMaximumIntervalSeconds is not nil. func (v *WorkflowExecutionInfo) IsSetRetryMaximumIntervalSeconds() bool { return v != nil && v.RetryMaximumIntervalSeconds != nil } // GetRetryMaximumAttempts returns the value of RetryMaximumAttempts if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetRetryMaximumAttempts() (o int32) { if v != nil && v.RetryMaximumAttempts != nil { return *v.RetryMaximumAttempts } return } // IsSetRetryMaximumAttempts returns true if RetryMaximumAttempts is not nil. func (v *WorkflowExecutionInfo) IsSetRetryMaximumAttempts() bool { return v != nil && v.RetryMaximumAttempts != nil } // GetRetryExpirationSeconds returns the value of RetryExpirationSeconds if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetRetryExpirationSeconds() (o int32) { if v != nil && v.RetryExpirationSeconds != nil { return *v.RetryExpirationSeconds } return } // IsSetRetryExpirationSeconds returns true if RetryExpirationSeconds is not nil. func (v *WorkflowExecutionInfo) IsSetRetryExpirationSeconds() bool { return v != nil && v.RetryExpirationSeconds != nil } // GetRetryBackoffCoefficient returns the value of RetryBackoffCoefficient if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetRetryBackoffCoefficient() (o float64) { if v != nil && v.RetryBackoffCoefficient != nil { return *v.RetryBackoffCoefficient } return } // IsSetRetryBackoffCoefficient returns true if RetryBackoffCoefficient is not nil. func (v *WorkflowExecutionInfo) IsSetRetryBackoffCoefficient() bool { return v != nil && v.RetryBackoffCoefficient != nil } // GetRetryExpirationTimeNanos returns the value of RetryExpirationTimeNanos if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetRetryExpirationTimeNanos() (o int64) { if v != nil && v.RetryExpirationTimeNanos != nil { return *v.RetryExpirationTimeNanos } return } // IsSetRetryExpirationTimeNanos returns true if RetryExpirationTimeNanos is not nil. func (v *WorkflowExecutionInfo) IsSetRetryExpirationTimeNanos() bool { return v != nil && v.RetryExpirationTimeNanos != nil } // GetRetryNonRetryableErrors returns the value of RetryNonRetryableErrors if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetRetryNonRetryableErrors() (o []string) { if v != nil && v.RetryNonRetryableErrors != nil { return v.RetryNonRetryableErrors } return } // IsSetRetryNonRetryableErrors returns true if RetryNonRetryableErrors is not nil. func (v *WorkflowExecutionInfo) IsSetRetryNonRetryableErrors() bool { return v != nil && v.RetryNonRetryableErrors != nil } // GetHasRetryPolicy returns the value of HasRetryPolicy if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetHasRetryPolicy() (o bool) { if v != nil && v.HasRetryPolicy != nil { return *v.HasRetryPolicy } return } // IsSetHasRetryPolicy returns true if HasRetryPolicy is not nil. func (v *WorkflowExecutionInfo) IsSetHasRetryPolicy() bool { return v != nil && v.HasRetryPolicy != nil } // GetCronSchedule returns the value of CronSchedule if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCronSchedule() (o string) { if v != nil && v.CronSchedule != nil { return *v.CronSchedule } return } // IsSetCronSchedule returns true if CronSchedule is not nil. func (v *WorkflowExecutionInfo) IsSetCronSchedule() bool { return v != nil && v.CronSchedule != nil } // GetEventStoreVersion returns the value of EventStoreVersion if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetEventStoreVersion() (o int32) { if v != nil && v.EventStoreVersion != nil { return *v.EventStoreVersion } return } // IsSetEventStoreVersion returns true if EventStoreVersion is not nil. func (v *WorkflowExecutionInfo) IsSetEventStoreVersion() bool { return v != nil && v.EventStoreVersion != nil } // GetEventBranchToken returns the value of EventBranchToken if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetEventBranchToken() (o []byte) { if v != nil && v.EventBranchToken != nil { return v.EventBranchToken } return } // IsSetEventBranchToken returns true if EventBranchToken is not nil. func (v *WorkflowExecutionInfo) IsSetEventBranchToken() bool { return v != nil && v.EventBranchToken != nil } // GetSignalCount returns the value of SignalCount if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetSignalCount() (o int64) { if v != nil && v.SignalCount != nil { return *v.SignalCount } return } // IsSetSignalCount returns true if SignalCount is not nil. func (v *WorkflowExecutionInfo) IsSetSignalCount() bool { return v != nil && v.SignalCount != nil } // GetHistorySize returns the value of HistorySize if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetHistorySize() (o int64) { if v != nil && v.HistorySize != nil { return *v.HistorySize } return } // IsSetHistorySize returns true if HistorySize is not nil. func (v *WorkflowExecutionInfo) IsSetHistorySize() bool { return v != nil && v.HistorySize != nil } // GetClientLibraryVersion returns the value of ClientLibraryVersion if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetClientLibraryVersion() (o string) { if v != nil && v.ClientLibraryVersion != nil { return *v.ClientLibraryVersion } return } // IsSetClientLibraryVersion returns true if ClientLibraryVersion is not nil. func (v *WorkflowExecutionInfo) IsSetClientLibraryVersion() bool { return v != nil && v.ClientLibraryVersion != nil } // GetClientFeatureVersion returns the value of ClientFeatureVersion if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetClientFeatureVersion() (o string) { if v != nil && v.ClientFeatureVersion != nil { return *v.ClientFeatureVersion } return } // IsSetClientFeatureVersion returns true if ClientFeatureVersion is not nil. func (v *WorkflowExecutionInfo) IsSetClientFeatureVersion() bool { return v != nil && v.ClientFeatureVersion != nil } // GetClientImpl returns the value of ClientImpl if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetClientImpl() (o string) { if v != nil && v.ClientImpl != nil { return *v.ClientImpl } return } // IsSetClientImpl returns true if ClientImpl is not nil. func (v *WorkflowExecutionInfo) IsSetClientImpl() bool { return v != nil && v.ClientImpl != nil } // GetAutoResetPoints returns the value of AutoResetPoints if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetAutoResetPoints() (o []byte) { if v != nil && v.AutoResetPoints != nil { return v.AutoResetPoints } return } // IsSetAutoResetPoints returns true if AutoResetPoints is not nil. func (v *WorkflowExecutionInfo) IsSetAutoResetPoints() bool { return v != nil && v.AutoResetPoints != nil } // GetAutoResetPointsEncoding returns the value of AutoResetPointsEncoding if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetAutoResetPointsEncoding() (o string) { if v != nil && v.AutoResetPointsEncoding != nil { return *v.AutoResetPointsEncoding } return } // IsSetAutoResetPointsEncoding returns true if AutoResetPointsEncoding is not nil. func (v *WorkflowExecutionInfo) IsSetAutoResetPointsEncoding() bool { return v != nil && v.AutoResetPointsEncoding != nil } // GetSearchAttributes returns the value of SearchAttributes if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetSearchAttributes() (o map[string][]byte) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // IsSetSearchAttributes returns true if SearchAttributes is not nil. func (v *WorkflowExecutionInfo) IsSetSearchAttributes() bool { return v != nil && v.SearchAttributes != nil } // GetMemo returns the value of Memo if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetMemo() (o map[string][]byte) { if v != nil && v.Memo != nil { return v.Memo } return } // IsSetMemo returns true if Memo is not nil. func (v *WorkflowExecutionInfo) IsSetMemo() bool { return v != nil && v.Memo != nil } // GetVersionHistories returns the value of VersionHistories if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetVersionHistories() (o []byte) { if v != nil && v.VersionHistories != nil { return v.VersionHistories } return } // IsSetVersionHistories returns true if VersionHistories is not nil. func (v *WorkflowExecutionInfo) IsSetVersionHistories() bool { return v != nil && v.VersionHistories != nil } // GetVersionHistoriesEncoding returns the value of VersionHistoriesEncoding if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetVersionHistoriesEncoding() (o string) { if v != nil && v.VersionHistoriesEncoding != nil { return *v.VersionHistoriesEncoding } return } // IsSetVersionHistoriesEncoding returns true if VersionHistoriesEncoding is not nil. func (v *WorkflowExecutionInfo) IsSetVersionHistoriesEncoding() bool { return v != nil && v.VersionHistoriesEncoding != nil } // GetFirstExecutionRunID returns the value of FirstExecutionRunID if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetFirstExecutionRunID() (o []byte) { if v != nil && v.FirstExecutionRunID != nil { return v.FirstExecutionRunID } return } // IsSetFirstExecutionRunID returns true if FirstExecutionRunID is not nil. func (v *WorkflowExecutionInfo) IsSetFirstExecutionRunID() bool { return v != nil && v.FirstExecutionRunID != nil } // GetPartitionConfig returns the value of PartitionConfig if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // IsSetPartitionConfig returns true if PartitionConfig is not nil. func (v *WorkflowExecutionInfo) IsSetPartitionConfig() bool { return v != nil && v.PartitionConfig != nil } // GetChecksum returns the value of Checksum if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetChecksum() (o []byte) { if v != nil && v.Checksum != nil { return v.Checksum } return } // IsSetChecksum returns true if Checksum is not nil. func (v *WorkflowExecutionInfo) IsSetChecksum() bool { return v != nil && v.Checksum != nil } // GetChecksumEncoding returns the value of ChecksumEncoding if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetChecksumEncoding() (o string) { if v != nil && v.ChecksumEncoding != nil { return *v.ChecksumEncoding } return } // IsSetChecksumEncoding returns true if ChecksumEncoding is not nil. func (v *WorkflowExecutionInfo) IsSetChecksumEncoding() bool { return v != nil && v.ChecksumEncoding != nil } // GetCronOverlapPolicy returns the value of CronOverlapPolicy if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetCronOverlapPolicy() (o shared.CronOverlapPolicy) { if v != nil && v.CronOverlapPolicy != nil { return *v.CronOverlapPolicy } return } // IsSetCronOverlapPolicy returns true if CronOverlapPolicy is not nil. func (v *WorkflowExecutionInfo) IsSetCronOverlapPolicy() bool { return v != nil && v.CronOverlapPolicy != nil } // GetActiveClusterSelectionPolicy returns the value of ActiveClusterSelectionPolicy if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetActiveClusterSelectionPolicy() (o []byte) { if v != nil && v.ActiveClusterSelectionPolicy != nil { return v.ActiveClusterSelectionPolicy } return } // IsSetActiveClusterSelectionPolicy returns true if ActiveClusterSelectionPolicy is not nil. func (v *WorkflowExecutionInfo) IsSetActiveClusterSelectionPolicy() bool { return v != nil && v.ActiveClusterSelectionPolicy != nil } // GetActiveClusterSelectionPolicyEncoding returns the value of ActiveClusterSelectionPolicyEncoding if it is set or its // zero value if it is unset. func (v *WorkflowExecutionInfo) GetActiveClusterSelectionPolicyEncoding() (o string) { if v != nil && v.ActiveClusterSelectionPolicyEncoding != nil { return *v.ActiveClusterSelectionPolicyEncoding } return } // IsSetActiveClusterSelectionPolicyEncoding returns true if ActiveClusterSelectionPolicyEncoding is not nil. func (v *WorkflowExecutionInfo) IsSetActiveClusterSelectionPolicyEncoding() bool { return v != nil && v.ActiveClusterSelectionPolicyEncoding != nil } type WorkflowTimerTaskInfo struct { References []*TimerReference `json:"references,omitempty"` } type _List_TimerReference_ValueList []*TimerReference func (v _List_TimerReference_ValueList) ForEach(f func(wire.Value) error) error { for i, x := range v { if x == nil { return fmt.Errorf("invalid list '[]*TimerReference', index [%v]: value is nil", i) } w, err := x.ToWire() if err != nil { return err } err = f(w) if err != nil { return err } } return nil } func (v _List_TimerReference_ValueList) Size() int { return len(v) } func (_List_TimerReference_ValueList) ValueType() wire.Type { return wire.TStruct } func (_List_TimerReference_ValueList) Close() {} // ToWire translates a WorkflowTimerTaskInfo struct into a Thrift-level intermediate // representation. This intermediate representation may be serialized // into bytes using a ThriftRW protocol implementation. // // An error is returned if the struct or any of its fields failed to // validate. // // x, err := v.ToWire() // if err != nil { // return err // } // // if err := binaryProtocol.Encode(x, writer); err != nil { // return err // } func (v *WorkflowTimerTaskInfo) ToWire() (wire.Value, error) { var ( fields [1]wire.Field i int = 0 w wire.Value err error ) if v.References != nil { w, err = wire.NewValueList(_List_TimerReference_ValueList(v.References)), error(nil) if err != nil { return w, err } fields[i] = wire.Field{ID: 10, Value: w} i++ } return wire.NewValueStruct(wire.Struct{Fields: fields[:i]}), nil } func _TimerReference_Read(w wire.Value) (*TimerReference, error) { var v TimerReference err := v.FromWire(w) return &v, err } func _List_TimerReference_Read(l wire.ValueList) ([]*TimerReference, error) { if l.ValueType() != wire.TStruct { return nil, nil } o := make([]*TimerReference, 0, l.Size()) err := l.ForEach(func(x wire.Value) error { i, err := _TimerReference_Read(x) if err != nil { return err } o = append(o, i) return nil }) l.Close() return o, err } // FromWire deserializes a WorkflowTimerTaskInfo struct from its Thrift-level // representation. The Thrift-level representation may be obtained // from a ThriftRW protocol implementation. // // An error is returned if we were unable to build a WorkflowTimerTaskInfo struct // from the provided intermediate representation. // // x, err := binaryProtocol.Decode(reader, wire.TStruct) // if err != nil { // return nil, err // } // // var v WorkflowTimerTaskInfo // if err := v.FromWire(x); err != nil { // return nil, err // } // return &v, nil func (v *WorkflowTimerTaskInfo) FromWire(w wire.Value) error { var err error for _, field := range w.GetStruct().Fields { switch field.ID { case 10: if field.Value.Type() == wire.TList { v.References, err = _List_TimerReference_Read(field.Value.GetList()) if err != nil { return err } } } } return nil } func _List_TimerReference_Encode(val []*TimerReference, sw stream.Writer) error { lh := stream.ListHeader{ Type: wire.TStruct, Length: len(val), } if err := sw.WriteListBegin(lh); err != nil { return err } for i, v := range val { if v == nil { return fmt.Errorf("invalid list '[]*TimerReference', index [%v]: value is nil", i) } if err := v.Encode(sw); err != nil { return err } } return sw.WriteListEnd() } // Encode serializes a WorkflowTimerTaskInfo struct directly into bytes, without going // through an intermediary type. // // An error is returned if a WorkflowTimerTaskInfo struct could not be encoded. func (v *WorkflowTimerTaskInfo) Encode(sw stream.Writer) error { if err := sw.WriteStructBegin(); err != nil { return err } if v.References != nil { if err := sw.WriteFieldBegin(stream.FieldHeader{ID: 10, Type: wire.TList}); err != nil { return err } if err := _List_TimerReference_Encode(v.References, sw); err != nil { return err } if err := sw.WriteFieldEnd(); err != nil { return err } } return sw.WriteStructEnd() } func _TimerReference_Decode(sr stream.Reader) (*TimerReference, error) { var v TimerReference err := v.Decode(sr) return &v, err } func _List_TimerReference_Decode(sr stream.Reader) ([]*TimerReference, error) { lh, err := sr.ReadListBegin() if err != nil { return nil, err } if lh.Type != wire.TStruct { for i := 0; i < lh.Length; i++ { if err := sr.Skip(lh.Type); err != nil { return nil, err } } return nil, sr.ReadListEnd() } o := make([]*TimerReference, 0, lh.Length) for i := 0; i < lh.Length; i++ { v, err := _TimerReference_Decode(sr) if err != nil { return nil, err } o = append(o, v) } if err = sr.ReadListEnd(); err != nil { return nil, err } return o, err } // Decode deserializes a WorkflowTimerTaskInfo struct directly from its Thrift-level // representation, without going through an intemediary type. // // An error is returned if a WorkflowTimerTaskInfo struct could not be generated from the wire // representation. func (v *WorkflowTimerTaskInfo) Decode(sr stream.Reader) error { if err := sr.ReadStructBegin(); err != nil { return err } fh, ok, err := sr.ReadFieldBegin() if err != nil { return err } for ok { switch { case fh.ID == 10 && fh.Type == wire.TList: v.References, err = _List_TimerReference_Decode(sr) if err != nil { return err } default: if err := sr.Skip(fh.Type); err != nil { return err } } if err := sr.ReadFieldEnd(); err != nil { return err } if fh, ok, err = sr.ReadFieldBegin(); err != nil { return err } } if err := sr.ReadStructEnd(); err != nil { return err } return nil } // String returns a readable string representation of a WorkflowTimerTaskInfo // struct. func (v *WorkflowTimerTaskInfo) String() string { if v == nil { return "" } var fields [1]string i := 0 if v.References != nil { fields[i] = fmt.Sprintf("References: %v", v.References) i++ } return fmt.Sprintf("WorkflowTimerTaskInfo{%v}", strings.Join(fields[:i], ", ")) } func _List_TimerReference_Equals(lhs, rhs []*TimerReference) bool { if len(lhs) != len(rhs) { return false } for i, lv := range lhs { rv := rhs[i] if !lv.Equals(rv) { return false } } return true } // Equals returns true if all the fields of this WorkflowTimerTaskInfo match the // provided WorkflowTimerTaskInfo. // // This function performs a deep comparison. func (v *WorkflowTimerTaskInfo) Equals(rhs *WorkflowTimerTaskInfo) bool { if v == nil { return rhs == nil } else if rhs == nil { return false } if !((v.References == nil && rhs.References == nil) || (v.References != nil && rhs.References != nil && _List_TimerReference_Equals(v.References, rhs.References))) { return false } return true } type _List_TimerReference_Zapper []*TimerReference // MarshalLogArray implements zapcore.ArrayMarshaler, enabling // fast logging of _List_TimerReference_Zapper. func (l _List_TimerReference_Zapper) MarshalLogArray(enc zapcore.ArrayEncoder) (err error) { for _, v := range l { err = multierr.Append(err, enc.AppendObject(v)) } return err } // MarshalLogObject implements zapcore.ObjectMarshaler, enabling // fast logging of WorkflowTimerTaskInfo. func (v *WorkflowTimerTaskInfo) MarshalLogObject(enc zapcore.ObjectEncoder) (err error) { if v == nil { return nil } if v.References != nil { err = multierr.Append(err, enc.AddArray("references", (_List_TimerReference_Zapper)(v.References))) } return err } // GetReferences returns the value of References if it is set or its // zero value if it is unset. func (v *WorkflowTimerTaskInfo) GetReferences() (o []*TimerReference) { if v != nil && v.References != nil { return v.References } return } // IsSetReferences returns true if References is not nil. func (v *WorkflowTimerTaskInfo) IsSetReferences() bool { return v != nil && v.References != nil } // ThriftModule represents the IDL file used to generate this package. var ThriftModule = &thriftreflect.ThriftModule{ Name: "sqlblobs", Package: "github.com/uber/cadence/.gen/go/sqlblobs", FilePath: "sqlblobs.thrift", SHA1: "c688f6d76c90c9e402bf15d124f588c7dee2bee3", Includes: []*thriftreflect.ThriftModule{ shared.ThriftModule, }, Raw: rawIDL, } const rawIDL = "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nnamespace java com.uber.cadence.sqlblobs\n\ninclude \"shared.thrift\"\n\nstruct ShardInfo {\n 10: optional i32 stolenSinceRenew\n 12: optional i64 (js.type = \"Long\") updatedAtNanos\n 14: optional i64 (js.type = \"Long\") replicationAckLevel\n 16: optional i64 (js.type = \"Long\") transferAckLevel\n 18: optional i64 (js.type = \"Long\") timerAckLevelNanos\n 24: optional i64 (js.type = \"Long\") domainNotificationVersion\n 34: optional map clusterTransferAckLevel\n 36: optional map clusterTimerAckLevel\n 38: optional string owner\n 40: optional map clusterReplicationLevel\n 42: optional binary pendingFailoverMarkers\n 44: optional string pendingFailoverMarkersEncoding\n 46: optional map replicationDlqAckLevel\n 50: optional binary transferProcessingQueueStates\n 51: optional string transferProcessingQueueStatesEncoding\n 55: optional binary timerProcessingQueueStates\n 56: optional string timerProcessingQueueStatesEncoding\n 60: optional binary crossClusterProcessingQueueStates\n 61: optional string crossClusterProcessingQueueStatesEncoding\n 64: optional map queueStates\n}\n\nstruct DomainInfo {\n 10: optional string name\n 12: optional string description\n 14: optional string owner\n 16: optional i32 status\n 18: optional i16 retentionDays\n 20: optional bool emitMetric\n 22: optional string archivalBucket\n 24: optional i16 archivalStatus\n 26: optional i64 (js.type = \"Long\") configVersion\n 28: optional i64 (js.type = \"Long\") notificationVersion\n 30: optional i64 (js.type = \"Long\") failoverNotificationVersion\n 32: optional i64 (js.type = \"Long\") failoverVersion\n 34: optional string activeClusterName\n 36: optional list clusters\n 38: optional map data\n 39: optional binary badBinaries\n 40: optional string badBinariesEncoding\n 42: optional i16 historyArchivalStatus\n 44: optional string historyArchivalURI\n 46: optional i16 visibilityArchivalStatus\n 48: optional string visibilityArchivalURI\n 50: optional i64 (js.type = \"Long\") failoverEndTime\n 52: optional i64 (js.type = \"Long\") previousFailoverVersion\n 54: optional i64 (js.type = \"Long\") lastUpdatedTime\n 56: optional binary isolationGroupsConfiguration\n 58: optional string isolationGroupsConfigurationEncoding\n 60: optional binary asyncWorkflowConfiguration\n 62: optional string asyncWorkflowConfigurationEncoding\n 64: optional binary activeClustersConfiguration\n 66: optional string activeClustersConfigurationEncoding\n}\n\nstruct HistoryTreeInfo {\n 10: optional i64 (js.type = \"Long\") createdTimeNanos // For fork operation to prevent race condition of leaking event data when forking branches fail. Also can be used for clean up leaked data\n 12: optional list ancestors\n 14: optional string info // For lookup back to workflow during debugging, also background cleanup when fork operation cannot finish self cleanup due to crash.\n}\n\nstruct WorkflowExecutionInfo {\n 10: optional binary parentDomainID\n 12: optional string parentWorkflowID\n 14: optional binary parentRunID\n 16: optional i64 (js.type = \"Long\") initiatedID\n 18: optional i64 (js.type = \"Long\") completionEventBatchID\n 20: optional binary completionEvent\n 22: optional string completionEventEncoding\n 24: optional string taskList\n 25: optional shared.TaskListKind taskListKind\n 26: optional string workflowTypeName\n 28: optional i32 workflowTimeoutSeconds\n 30: optional i32 decisionTaskTimeoutSeconds\n 32: optional binary executionContext\n 34: optional i32 state\n 36: optional i32 closeStatus\n 38: optional i64 (js.type = \"Long\") startVersion\n 44: optional i64 (js.type = \"Long\") lastWriteEventID\n 48: optional i64 (js.type = \"Long\") lastEventTaskID\n 50: optional i64 (js.type = \"Long\") lastFirstEventID\n 52: optional i64 (js.type = \"Long\") lastProcessedEvent\n 54: optional i64 (js.type = \"Long\") startTimeNanos\n 56: optional i64 (js.type = \"Long\") lastUpdatedTimeNanos\n 58: optional i64 (js.type = \"Long\") decisionVersion\n 60: optional i64 (js.type = \"Long\") decisionScheduleID\n 62: optional i64 (js.type = \"Long\") decisionStartedID\n 64: optional i32 decisionTimeout\n 66: optional i64 (js.type = \"Long\") decisionAttempt\n 68: optional i64 (js.type = \"Long\") decisionStartedTimestampNanos\n 69: optional i64 (js.type = \"Long\") decisionScheduledTimestampNanos\n 70: optional bool cancelRequested\n 71: optional i64 (js.type = \"Long\") decisionOriginalScheduledTimestampNanos\n 72: optional string createRequestID\n 74: optional string decisionRequestID\n 76: optional string cancelRequestID\n 78: optional string stickyTaskList\n 80: optional i64 (js.type = \"Long\") stickyScheduleToStartTimeout\n 82: optional i64 (js.type = \"Long\") retryAttempt\n 84: optional i32 retryInitialIntervalSeconds\n 86: optional i32 retryMaximumIntervalSeconds\n 88: optional i32 retryMaximumAttempts\n 90: optional i32 retryExpirationSeconds\n 92: optional double retryBackoffCoefficient\n 94: optional i64 (js.type = \"Long\") retryExpirationTimeNanos\n 96: optional list retryNonRetryableErrors\n 98: optional bool hasRetryPolicy\n 100: optional string cronSchedule\n 102: optional i32 eventStoreVersion\n 104: optional binary eventBranchToken\n 106: optional i64 (js.type = \"Long\") signalCount\n 108: optional i64 (js.type = \"Long\") historySize\n 110: optional string clientLibraryVersion\n 112: optional string clientFeatureVersion\n 114: optional string clientImpl\n 115: optional binary autoResetPoints\n 116: optional string autoResetPointsEncoding\n 118: optional map searchAttributes\n 120: optional map memo\n 122: optional binary versionHistories\n 124: optional string versionHistoriesEncoding\n 126: optional binary firstExecutionRunID\n 128: optional map partitionConfig\n 130: optional binary checksum\n 132: optional string checksumEncoding\n 134: optional shared.CronOverlapPolicy cronOverlapPolicy\n 137: optional binary activeClusterSelectionPolicy\n 138: optional string activeClusterSelectionPolicyEncoding\n}\n\nstruct ActivityInfo {\n 10: optional i64 (js.type = \"Long\") version\n 12: optional i64 (js.type = \"Long\") scheduledEventBatchID\n 14: optional binary scheduledEvent\n 16: optional string scheduledEventEncoding\n 18: optional i64 (js.type = \"Long\") scheduledTimeNanos\n 20: optional i64 (js.type = \"Long\") startedID\n 22: optional binary startedEvent\n 24: optional string startedEventEncoding\n 26: optional i64 (js.type = \"Long\") startedTimeNanos\n 28: optional string activityID\n 30: optional string requestID\n 32: optional i32 scheduleToStartTimeoutSeconds\n 34: optional i32 scheduleToCloseTimeoutSeconds\n 36: optional i32 startToCloseTimeoutSeconds\n 38: optional i32 heartbeatTimeoutSeconds\n 40: optional bool cancelRequested\n 42: optional i64 (js.type = \"Long\") cancelRequestID\n 44: optional i32 timerTaskStatus\n 46: optional i32 attempt\n 48: optional string taskList\n 49: optional shared.TaskListKind taskListKind\n 50: optional string startedIdentity\n 52: optional bool hasRetryPolicy\n 54: optional i32 retryInitialIntervalSeconds\n 56: optional i32 retryMaximumIntervalSeconds\n 58: optional i32 retryMaximumAttempts\n 60: optional i64 (js.type = \"Long\") retryExpirationTimeNanos\n 62: optional double retryBackoffCoefficient\n 64: optional list retryNonRetryableErrors\n 66: optional string retryLastFailureReason\n 68: optional string retryLastWorkerIdentity\n 70: optional binary retryLastFailureDetails\n}\n\nstruct ChildExecutionInfo {\n 10: optional i64 (js.type = \"Long\") version\n 12: optional i64 (js.type = \"Long\") initiatedEventBatchID\n 14: optional i64 (js.type = \"Long\") startedID\n 16: optional binary initiatedEvent\n 18: optional string initiatedEventEncoding\n 20: optional string startedWorkflowID\n 22: optional binary startedRunID\n 24: optional binary startedEvent\n 26: optional string startedEventEncoding\n 28: optional string createRequestID\n 29: optional string domainID\n 30: optional string domainName // deprecated\n 32: optional string workflowTypeName\n 35: optional i32 parentClosePolicy\n}\n\nstruct SignalInfo {\n 10: optional i64 (js.type = \"Long\") version\n 11: optional i64 (js.type = \"Long\") initiatedEventBatchID\n 12: optional string requestID\n 14: optional string name\n 16: optional binary input\n 18: optional binary control\n}\n\nstruct RequestCancelInfo {\n 10: optional i64 (js.type = \"Long\") version\n 11: optional i64 (js.type = \"Long\") initiatedEventBatchID\n 12: optional string cancelRequestID\n}\n\nstruct TimerInfo {\n 10: optional i64 (js.type = \"Long\") version\n 12: optional i64 (js.type = \"Long\") startedID\n 14: optional i64 (js.type = \"Long\") expiryTimeNanos\n // TaskID is a misleading variable, it actually serves\n // the purpose of indicating whether a timer task is\n // generated for this timer info\n 16: optional i64 (js.type = \"Long\") taskID\n}\n\nstruct TaskInfo {\n 10: optional string workflowID\n 12: optional binary runID\n 13: optional i64 (js.type = \"Long\") scheduleID\n 14: optional i64 (js.type = \"Long\") expiryTimeNanos\n 15: optional i64 (js.type = \"Long\") createdTimeNanos\n 17: optional map partitionConfig\n}\n\nstruct TaskListPartition {\n 10: optional list isolationGroups\n}\n\nstruct TaskListPartitionConfig {\n 10: optional i64 (js.type = \"Long\") version\n 12: optional i32 numReadPartitions\n 14: optional i32 numWritePartitions\n 16: optional map readPartitions\n 18: optional map writePartitions\n}\n\nstruct TaskListInfo {\n 10: optional i16 kind // {Normal, Sticky}\n 12: optional i64 (js.type = \"Long\") ackLevel\n 14: optional i64 (js.type = \"Long\") expiryTimeNanos\n 16: optional i64 (js.type = \"Long\") lastUpdatedNanos\n 18: optional TaskListPartitionConfig adaptivePartitionConfig\n}\n\nstruct TransferTaskInfo {\n 10: optional binary domainID\n 12: optional string workflowID\n 14: optional binary runID\n 16: optional i16 taskType\n 18: optional binary targetDomainID\n 20: optional string targetWorkflowID\n 22: optional binary targetRunID\n 24: optional string taskList\n 26: optional bool targetChildWorkflowOnly\n 28: optional i64 (js.type = \"Long\") scheduleID\n 30: optional i64 (js.type = \"Long\") version\n 32: optional i64 (js.type = \"Long\") visibilityTimestampNanos\n 34: optional set targetDomainIDs\n 36: optional string originalTaskList\n 38: optional shared.TaskListKind originalTaskListKind\n}\n\nstruct TimerTaskInfo {\n 10: optional binary domainID\n 12: optional string workflowID\n 14: optional binary runID\n 16: optional i16 taskType\n 18: optional i16 timeoutType\n 20: optional i64 (js.type = \"Long\") version\n 22: optional i64 (js.type = \"Long\") scheduleAttempt\n 24: optional i64 (js.type = \"Long\") eventID\n 26: optional string taskList\n}\n\nstruct ReplicationTaskInfo {\n 10: optional binary domainID\n 12: optional string workflowID\n 14: optional binary runID\n 16: optional i16 taskType\n 18: optional i64 (js.type = \"Long\") version\n 20: optional i64 (js.type = \"Long\") firstEventID\n 22: optional i64 (js.type = \"Long\") nextEventID\n 24: optional i64 (js.type = \"Long\") scheduledID\n 26: optional i32 eventStoreVersion\n 28: optional i32 newRunEventStoreVersion\n 30: optional binary branch_token\n 34: optional binary newRunBranchToken\n 38: optional i64 (js.type = \"Long\") creationTime\n}\n\nenum AsyncRequestType {\n StartWorkflowExecutionAsyncRequest\n SignalWithStartWorkflowExecutionAsyncRequest\n}\n\nstruct AsyncRequestMessage {\n 10: optional string partitionKey\n 12: optional AsyncRequestType type\n 14: optional shared.Header header\n 16: optional string encoding\n 18: optional binary payload\n}\n\n// a substruct on the executions record which is intended to be used to track\n// timers and other records for debugging and cleanup\nstruct WorkflowTimerTaskInfo {\n 10: optional list references\n}\n\nstruct TimerReference {\n // Primary Keys. Always required\n // a reference to the the execution table task_id\n 10: optional i64 taskID\n // a reference to the execution table visibility_ts\n 11: optional i64 (js.type = \"Long\") visibilityTimestamp\n\n // Reference fields:\n // for workflow timer values, the type of timeout\n 13: optional i16 TimeoutType\n}\n" ================================================ FILE: .gen/go/sqlblobs/types_yarpc.go ================================================ // Code generated by thriftrw-plugin-yarpc // @generated package sqlblobs ================================================ FILE: .gen/proto/history/v1/service.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/history/v1/service.proto package historyv1 import ( fmt "fmt" io "io" math "math" math_bits "math/bits" proto "github.com/gogo/protobuf/proto" types "github.com/gogo/protobuf/types" v11 "github.com/uber/cadence-idl/go/proto/admin/v1" v1 "github.com/uber/cadence-idl/go/proto/api/v1" v12 "github.com/uber/cadence/.gen/proto/shared/v1" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type StartWorkflowExecutionRequest struct { Request *v1.StartWorkflowExecutionRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` ParentExecutionInfo *v1.ParentExecutionInfo `protobuf:"bytes,3,opt,name=parent_execution_info,json=parentExecutionInfo,proto3" json:"parent_execution_info,omitempty"` Attempt int32 `protobuf:"varint,4,opt,name=attempt,proto3" json:"attempt,omitempty"` ExpirationTime *types.Timestamp `protobuf:"bytes,5,opt,name=expiration_time,json=expirationTime,proto3" json:"expiration_time,omitempty"` ContinueAsNewInitiator v1.ContinueAsNewInitiator `protobuf:"varint,6,opt,name=continue_as_new_initiator,json=continueAsNewInitiator,proto3,enum=uber.cadence.api.v1.ContinueAsNewInitiator" json:"continue_as_new_initiator,omitempty"` ContinuedFailure *v1.Failure `protobuf:"bytes,7,opt,name=continued_failure,json=continuedFailure,proto3" json:"continued_failure,omitempty"` LastCompletionResult *v1.Payload `protobuf:"bytes,8,opt,name=last_completion_result,json=lastCompletionResult,proto3" json:"last_completion_result,omitempty"` FirstDecisionTaskBackoff *types.Duration `protobuf:"bytes,9,opt,name=first_decision_task_backoff,json=firstDecisionTaskBackoff,proto3" json:"first_decision_task_backoff,omitempty"` PartitionConfig map[string]string `protobuf:"bytes,10,rep,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *StartWorkflowExecutionRequest) Reset() { *m = StartWorkflowExecutionRequest{} } func (m *StartWorkflowExecutionRequest) String() string { return proto.CompactTextString(m) } func (*StartWorkflowExecutionRequest) ProtoMessage() {} func (*StartWorkflowExecutionRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{0} } func (m *StartWorkflowExecutionRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *StartWorkflowExecutionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_StartWorkflowExecutionRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *StartWorkflowExecutionRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_StartWorkflowExecutionRequest.Merge(m, src) } func (m *StartWorkflowExecutionRequest) XXX_Size() int { return m.Size() } func (m *StartWorkflowExecutionRequest) XXX_DiscardUnknown() { xxx_messageInfo_StartWorkflowExecutionRequest.DiscardUnknown(m) } var xxx_messageInfo_StartWorkflowExecutionRequest proto.InternalMessageInfo func (m *StartWorkflowExecutionRequest) GetRequest() *v1.StartWorkflowExecutionRequest { if m != nil { return m.Request } return nil } func (m *StartWorkflowExecutionRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *StartWorkflowExecutionRequest) GetParentExecutionInfo() *v1.ParentExecutionInfo { if m != nil { return m.ParentExecutionInfo } return nil } func (m *StartWorkflowExecutionRequest) GetAttempt() int32 { if m != nil { return m.Attempt } return 0 } func (m *StartWorkflowExecutionRequest) GetExpirationTime() *types.Timestamp { if m != nil { return m.ExpirationTime } return nil } func (m *StartWorkflowExecutionRequest) GetContinueAsNewInitiator() v1.ContinueAsNewInitiator { if m != nil { return m.ContinueAsNewInitiator } return v1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_INVALID } func (m *StartWorkflowExecutionRequest) GetContinuedFailure() *v1.Failure { if m != nil { return m.ContinuedFailure } return nil } func (m *StartWorkflowExecutionRequest) GetLastCompletionResult() *v1.Payload { if m != nil { return m.LastCompletionResult } return nil } func (m *StartWorkflowExecutionRequest) GetFirstDecisionTaskBackoff() *types.Duration { if m != nil { return m.FirstDecisionTaskBackoff } return nil } func (m *StartWorkflowExecutionRequest) GetPartitionConfig() map[string]string { if m != nil { return m.PartitionConfig } return nil } type StartWorkflowExecutionResponse struct { RunId string `protobuf:"bytes,1,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *StartWorkflowExecutionResponse) Reset() { *m = StartWorkflowExecutionResponse{} } func (m *StartWorkflowExecutionResponse) String() string { return proto.CompactTextString(m) } func (*StartWorkflowExecutionResponse) ProtoMessage() {} func (*StartWorkflowExecutionResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{1} } func (m *StartWorkflowExecutionResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *StartWorkflowExecutionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_StartWorkflowExecutionResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *StartWorkflowExecutionResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_StartWorkflowExecutionResponse.Merge(m, src) } func (m *StartWorkflowExecutionResponse) XXX_Size() int { return m.Size() } func (m *StartWorkflowExecutionResponse) XXX_DiscardUnknown() { xxx_messageInfo_StartWorkflowExecutionResponse.DiscardUnknown(m) } var xxx_messageInfo_StartWorkflowExecutionResponse proto.InternalMessageInfo func (m *StartWorkflowExecutionResponse) GetRunId() string { if m != nil { return m.RunId } return "" } type SignalWorkflowExecutionRequest struct { Request *v1.SignalWorkflowExecutionRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` // workflow execution that requests this signal, for making sure // the workflow being signaled is actually a child of the workflow // making the request ExternalWorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,3,opt,name=external_workflow_execution,json=externalWorkflowExecution,proto3" json:"external_workflow_execution,omitempty"` ChildWorkflowOnly bool `protobuf:"varint,4,opt,name=child_workflow_only,json=childWorkflowOnly,proto3" json:"child_workflow_only,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SignalWorkflowExecutionRequest) Reset() { *m = SignalWorkflowExecutionRequest{} } func (m *SignalWorkflowExecutionRequest) String() string { return proto.CompactTextString(m) } func (*SignalWorkflowExecutionRequest) ProtoMessage() {} func (*SignalWorkflowExecutionRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{2} } func (m *SignalWorkflowExecutionRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *SignalWorkflowExecutionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_SignalWorkflowExecutionRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *SignalWorkflowExecutionRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_SignalWorkflowExecutionRequest.Merge(m, src) } func (m *SignalWorkflowExecutionRequest) XXX_Size() int { return m.Size() } func (m *SignalWorkflowExecutionRequest) XXX_DiscardUnknown() { xxx_messageInfo_SignalWorkflowExecutionRequest.DiscardUnknown(m) } var xxx_messageInfo_SignalWorkflowExecutionRequest proto.InternalMessageInfo func (m *SignalWorkflowExecutionRequest) GetRequest() *v1.SignalWorkflowExecutionRequest { if m != nil { return m.Request } return nil } func (m *SignalWorkflowExecutionRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *SignalWorkflowExecutionRequest) GetExternalWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.ExternalWorkflowExecution } return nil } func (m *SignalWorkflowExecutionRequest) GetChildWorkflowOnly() bool { if m != nil { return m.ChildWorkflowOnly } return false } type SignalWorkflowExecutionResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SignalWorkflowExecutionResponse) Reset() { *m = SignalWorkflowExecutionResponse{} } func (m *SignalWorkflowExecutionResponse) String() string { return proto.CompactTextString(m) } func (*SignalWorkflowExecutionResponse) ProtoMessage() {} func (*SignalWorkflowExecutionResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{3} } func (m *SignalWorkflowExecutionResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *SignalWorkflowExecutionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_SignalWorkflowExecutionResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *SignalWorkflowExecutionResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_SignalWorkflowExecutionResponse.Merge(m, src) } func (m *SignalWorkflowExecutionResponse) XXX_Size() int { return m.Size() } func (m *SignalWorkflowExecutionResponse) XXX_DiscardUnknown() { xxx_messageInfo_SignalWorkflowExecutionResponse.DiscardUnknown(m) } var xxx_messageInfo_SignalWorkflowExecutionResponse proto.InternalMessageInfo type SignalWithStartWorkflowExecutionRequest struct { Request *v1.SignalWithStartWorkflowExecutionRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` PartitionConfig map[string]string `protobuf:"bytes,3,rep,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SignalWithStartWorkflowExecutionRequest) Reset() { *m = SignalWithStartWorkflowExecutionRequest{} } func (m *SignalWithStartWorkflowExecutionRequest) String() string { return proto.CompactTextString(m) } func (*SignalWithStartWorkflowExecutionRequest) ProtoMessage() {} func (*SignalWithStartWorkflowExecutionRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{4} } func (m *SignalWithStartWorkflowExecutionRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *SignalWithStartWorkflowExecutionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_SignalWithStartWorkflowExecutionRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *SignalWithStartWorkflowExecutionRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_SignalWithStartWorkflowExecutionRequest.Merge(m, src) } func (m *SignalWithStartWorkflowExecutionRequest) XXX_Size() int { return m.Size() } func (m *SignalWithStartWorkflowExecutionRequest) XXX_DiscardUnknown() { xxx_messageInfo_SignalWithStartWorkflowExecutionRequest.DiscardUnknown(m) } var xxx_messageInfo_SignalWithStartWorkflowExecutionRequest proto.InternalMessageInfo func (m *SignalWithStartWorkflowExecutionRequest) GetRequest() *v1.SignalWithStartWorkflowExecutionRequest { if m != nil { return m.Request } return nil } func (m *SignalWithStartWorkflowExecutionRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *SignalWithStartWorkflowExecutionRequest) GetPartitionConfig() map[string]string { if m != nil { return m.PartitionConfig } return nil } type SignalWithStartWorkflowExecutionResponse struct { RunId string `protobuf:"bytes,1,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SignalWithStartWorkflowExecutionResponse) Reset() { *m = SignalWithStartWorkflowExecutionResponse{} } func (m *SignalWithStartWorkflowExecutionResponse) String() string { return proto.CompactTextString(m) } func (*SignalWithStartWorkflowExecutionResponse) ProtoMessage() {} func (*SignalWithStartWorkflowExecutionResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{5} } func (m *SignalWithStartWorkflowExecutionResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *SignalWithStartWorkflowExecutionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_SignalWithStartWorkflowExecutionResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *SignalWithStartWorkflowExecutionResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_SignalWithStartWorkflowExecutionResponse.Merge(m, src) } func (m *SignalWithStartWorkflowExecutionResponse) XXX_Size() int { return m.Size() } func (m *SignalWithStartWorkflowExecutionResponse) XXX_DiscardUnknown() { xxx_messageInfo_SignalWithStartWorkflowExecutionResponse.DiscardUnknown(m) } var xxx_messageInfo_SignalWithStartWorkflowExecutionResponse proto.InternalMessageInfo func (m *SignalWithStartWorkflowExecutionResponse) GetRunId() string { if m != nil { return m.RunId } return "" } type ResetWorkflowExecutionRequest struct { Request *v1.ResetWorkflowExecutionRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ResetWorkflowExecutionRequest) Reset() { *m = ResetWorkflowExecutionRequest{} } func (m *ResetWorkflowExecutionRequest) String() string { return proto.CompactTextString(m) } func (*ResetWorkflowExecutionRequest) ProtoMessage() {} func (*ResetWorkflowExecutionRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{6} } func (m *ResetWorkflowExecutionRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ResetWorkflowExecutionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ResetWorkflowExecutionRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ResetWorkflowExecutionRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_ResetWorkflowExecutionRequest.Merge(m, src) } func (m *ResetWorkflowExecutionRequest) XXX_Size() int { return m.Size() } func (m *ResetWorkflowExecutionRequest) XXX_DiscardUnknown() { xxx_messageInfo_ResetWorkflowExecutionRequest.DiscardUnknown(m) } var xxx_messageInfo_ResetWorkflowExecutionRequest proto.InternalMessageInfo func (m *ResetWorkflowExecutionRequest) GetRequest() *v1.ResetWorkflowExecutionRequest { if m != nil { return m.Request } return nil } func (m *ResetWorkflowExecutionRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type ResetWorkflowExecutionResponse struct { RunId string `protobuf:"bytes,1,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ResetWorkflowExecutionResponse) Reset() { *m = ResetWorkflowExecutionResponse{} } func (m *ResetWorkflowExecutionResponse) String() string { return proto.CompactTextString(m) } func (*ResetWorkflowExecutionResponse) ProtoMessage() {} func (*ResetWorkflowExecutionResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{7} } func (m *ResetWorkflowExecutionResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ResetWorkflowExecutionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ResetWorkflowExecutionResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ResetWorkflowExecutionResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_ResetWorkflowExecutionResponse.Merge(m, src) } func (m *ResetWorkflowExecutionResponse) XXX_Size() int { return m.Size() } func (m *ResetWorkflowExecutionResponse) XXX_DiscardUnknown() { xxx_messageInfo_ResetWorkflowExecutionResponse.DiscardUnknown(m) } var xxx_messageInfo_ResetWorkflowExecutionResponse proto.InternalMessageInfo func (m *ResetWorkflowExecutionResponse) GetRunId() string { if m != nil { return m.RunId } return "" } type TerminateWorkflowExecutionRequest struct { Request *v1.TerminateWorkflowExecutionRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` // workflow execution that requests this termination, for making sure // the workflow being terminated is actually a child of the workflow // making the request ExternalWorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,3,opt,name=external_workflow_execution,json=externalWorkflowExecution,proto3" json:"external_workflow_execution,omitempty"` ChildWorkflowOnly bool `protobuf:"varint,4,opt,name=child_workflow_only,json=childWorkflowOnly,proto3" json:"child_workflow_only,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *TerminateWorkflowExecutionRequest) Reset() { *m = TerminateWorkflowExecutionRequest{} } func (m *TerminateWorkflowExecutionRequest) String() string { return proto.CompactTextString(m) } func (*TerminateWorkflowExecutionRequest) ProtoMessage() {} func (*TerminateWorkflowExecutionRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{8} } func (m *TerminateWorkflowExecutionRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *TerminateWorkflowExecutionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_TerminateWorkflowExecutionRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *TerminateWorkflowExecutionRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_TerminateWorkflowExecutionRequest.Merge(m, src) } func (m *TerminateWorkflowExecutionRequest) XXX_Size() int { return m.Size() } func (m *TerminateWorkflowExecutionRequest) XXX_DiscardUnknown() { xxx_messageInfo_TerminateWorkflowExecutionRequest.DiscardUnknown(m) } var xxx_messageInfo_TerminateWorkflowExecutionRequest proto.InternalMessageInfo func (m *TerminateWorkflowExecutionRequest) GetRequest() *v1.TerminateWorkflowExecutionRequest { if m != nil { return m.Request } return nil } func (m *TerminateWorkflowExecutionRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *TerminateWorkflowExecutionRequest) GetExternalWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.ExternalWorkflowExecution } return nil } func (m *TerminateWorkflowExecutionRequest) GetChildWorkflowOnly() bool { if m != nil { return m.ChildWorkflowOnly } return false } type TerminateWorkflowExecutionResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *TerminateWorkflowExecutionResponse) Reset() { *m = TerminateWorkflowExecutionResponse{} } func (m *TerminateWorkflowExecutionResponse) String() string { return proto.CompactTextString(m) } func (*TerminateWorkflowExecutionResponse) ProtoMessage() {} func (*TerminateWorkflowExecutionResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{9} } func (m *TerminateWorkflowExecutionResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *TerminateWorkflowExecutionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_TerminateWorkflowExecutionResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *TerminateWorkflowExecutionResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_TerminateWorkflowExecutionResponse.Merge(m, src) } func (m *TerminateWorkflowExecutionResponse) XXX_Size() int { return m.Size() } func (m *TerminateWorkflowExecutionResponse) XXX_DiscardUnknown() { xxx_messageInfo_TerminateWorkflowExecutionResponse.DiscardUnknown(m) } var xxx_messageInfo_TerminateWorkflowExecutionResponse proto.InternalMessageInfo type DescribeWorkflowExecutionRequest struct { Request *v1.DescribeWorkflowExecutionRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DescribeWorkflowExecutionRequest) Reset() { *m = DescribeWorkflowExecutionRequest{} } func (m *DescribeWorkflowExecutionRequest) String() string { return proto.CompactTextString(m) } func (*DescribeWorkflowExecutionRequest) ProtoMessage() {} func (*DescribeWorkflowExecutionRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{10} } func (m *DescribeWorkflowExecutionRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *DescribeWorkflowExecutionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_DescribeWorkflowExecutionRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *DescribeWorkflowExecutionRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_DescribeWorkflowExecutionRequest.Merge(m, src) } func (m *DescribeWorkflowExecutionRequest) XXX_Size() int { return m.Size() } func (m *DescribeWorkflowExecutionRequest) XXX_DiscardUnknown() { xxx_messageInfo_DescribeWorkflowExecutionRequest.DiscardUnknown(m) } var xxx_messageInfo_DescribeWorkflowExecutionRequest proto.InternalMessageInfo func (m *DescribeWorkflowExecutionRequest) GetRequest() *v1.DescribeWorkflowExecutionRequest { if m != nil { return m.Request } return nil } func (m *DescribeWorkflowExecutionRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type DescribeWorkflowExecutionResponse struct { ExecutionConfiguration *v1.WorkflowExecutionConfiguration `protobuf:"bytes,1,opt,name=execution_configuration,json=executionConfiguration,proto3" json:"execution_configuration,omitempty"` WorkflowExecutionInfo *v1.WorkflowExecutionInfo `protobuf:"bytes,2,opt,name=workflow_execution_info,json=workflowExecutionInfo,proto3" json:"workflow_execution_info,omitempty"` PendingActivities []*v1.PendingActivityInfo `protobuf:"bytes,3,rep,name=pending_activities,json=pendingActivities,proto3" json:"pending_activities,omitempty"` PendingChildren []*v1.PendingChildExecutionInfo `protobuf:"bytes,4,rep,name=pending_children,json=pendingChildren,proto3" json:"pending_children,omitempty"` PendingDecision *v1.PendingDecisionInfo `protobuf:"bytes,5,opt,name=pending_decision,json=pendingDecision,proto3" json:"pending_decision,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DescribeWorkflowExecutionResponse) Reset() { *m = DescribeWorkflowExecutionResponse{} } func (m *DescribeWorkflowExecutionResponse) String() string { return proto.CompactTextString(m) } func (*DescribeWorkflowExecutionResponse) ProtoMessage() {} func (*DescribeWorkflowExecutionResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{11} } func (m *DescribeWorkflowExecutionResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *DescribeWorkflowExecutionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_DescribeWorkflowExecutionResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *DescribeWorkflowExecutionResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_DescribeWorkflowExecutionResponse.Merge(m, src) } func (m *DescribeWorkflowExecutionResponse) XXX_Size() int { return m.Size() } func (m *DescribeWorkflowExecutionResponse) XXX_DiscardUnknown() { xxx_messageInfo_DescribeWorkflowExecutionResponse.DiscardUnknown(m) } var xxx_messageInfo_DescribeWorkflowExecutionResponse proto.InternalMessageInfo func (m *DescribeWorkflowExecutionResponse) GetExecutionConfiguration() *v1.WorkflowExecutionConfiguration { if m != nil { return m.ExecutionConfiguration } return nil } func (m *DescribeWorkflowExecutionResponse) GetWorkflowExecutionInfo() *v1.WorkflowExecutionInfo { if m != nil { return m.WorkflowExecutionInfo } return nil } func (m *DescribeWorkflowExecutionResponse) GetPendingActivities() []*v1.PendingActivityInfo { if m != nil { return m.PendingActivities } return nil } func (m *DescribeWorkflowExecutionResponse) GetPendingChildren() []*v1.PendingChildExecutionInfo { if m != nil { return m.PendingChildren } return nil } func (m *DescribeWorkflowExecutionResponse) GetPendingDecision() *v1.PendingDecisionInfo { if m != nil { return m.PendingDecision } return nil } type QueryWorkflowRequest struct { Request *v1.QueryWorkflowRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *QueryWorkflowRequest) Reset() { *m = QueryWorkflowRequest{} } func (m *QueryWorkflowRequest) String() string { return proto.CompactTextString(m) } func (*QueryWorkflowRequest) ProtoMessage() {} func (*QueryWorkflowRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{12} } func (m *QueryWorkflowRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *QueryWorkflowRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_QueryWorkflowRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *QueryWorkflowRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_QueryWorkflowRequest.Merge(m, src) } func (m *QueryWorkflowRequest) XXX_Size() int { return m.Size() } func (m *QueryWorkflowRequest) XXX_DiscardUnknown() { xxx_messageInfo_QueryWorkflowRequest.DiscardUnknown(m) } var xxx_messageInfo_QueryWorkflowRequest proto.InternalMessageInfo func (m *QueryWorkflowRequest) GetRequest() *v1.QueryWorkflowRequest { if m != nil { return m.Request } return nil } func (m *QueryWorkflowRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type QueryWorkflowResponse struct { QueryResult *v1.Payload `protobuf:"bytes,1,opt,name=query_result,json=queryResult,proto3" json:"query_result,omitempty"` QueryRejected *v1.QueryRejected `protobuf:"bytes,2,opt,name=query_rejected,json=queryRejected,proto3" json:"query_rejected,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *QueryWorkflowResponse) Reset() { *m = QueryWorkflowResponse{} } func (m *QueryWorkflowResponse) String() string { return proto.CompactTextString(m) } func (*QueryWorkflowResponse) ProtoMessage() {} func (*QueryWorkflowResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{13} } func (m *QueryWorkflowResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *QueryWorkflowResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_QueryWorkflowResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *QueryWorkflowResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_QueryWorkflowResponse.Merge(m, src) } func (m *QueryWorkflowResponse) XXX_Size() int { return m.Size() } func (m *QueryWorkflowResponse) XXX_DiscardUnknown() { xxx_messageInfo_QueryWorkflowResponse.DiscardUnknown(m) } var xxx_messageInfo_QueryWorkflowResponse proto.InternalMessageInfo func (m *QueryWorkflowResponse) GetQueryResult() *v1.Payload { if m != nil { return m.QueryResult } return nil } func (m *QueryWorkflowResponse) GetQueryRejected() *v1.QueryRejected { if m != nil { return m.QueryRejected } return nil } type ResetStickyTaskListRequest struct { Request *v1.ResetStickyTaskListRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ResetStickyTaskListRequest) Reset() { *m = ResetStickyTaskListRequest{} } func (m *ResetStickyTaskListRequest) String() string { return proto.CompactTextString(m) } func (*ResetStickyTaskListRequest) ProtoMessage() {} func (*ResetStickyTaskListRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{14} } func (m *ResetStickyTaskListRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ResetStickyTaskListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ResetStickyTaskListRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ResetStickyTaskListRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_ResetStickyTaskListRequest.Merge(m, src) } func (m *ResetStickyTaskListRequest) XXX_Size() int { return m.Size() } func (m *ResetStickyTaskListRequest) XXX_DiscardUnknown() { xxx_messageInfo_ResetStickyTaskListRequest.DiscardUnknown(m) } var xxx_messageInfo_ResetStickyTaskListRequest proto.InternalMessageInfo func (m *ResetStickyTaskListRequest) GetRequest() *v1.ResetStickyTaskListRequest { if m != nil { return m.Request } return nil } func (m *ResetStickyTaskListRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type ResetStickyTaskListResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ResetStickyTaskListResponse) Reset() { *m = ResetStickyTaskListResponse{} } func (m *ResetStickyTaskListResponse) String() string { return proto.CompactTextString(m) } func (*ResetStickyTaskListResponse) ProtoMessage() {} func (*ResetStickyTaskListResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{15} } func (m *ResetStickyTaskListResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ResetStickyTaskListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ResetStickyTaskListResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ResetStickyTaskListResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_ResetStickyTaskListResponse.Merge(m, src) } func (m *ResetStickyTaskListResponse) XXX_Size() int { return m.Size() } func (m *ResetStickyTaskListResponse) XXX_DiscardUnknown() { xxx_messageInfo_ResetStickyTaskListResponse.DiscardUnknown(m) } var xxx_messageInfo_ResetStickyTaskListResponse proto.InternalMessageInfo type GetMutableStateRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` ExpectedNextEventId int64 `protobuf:"varint,3,opt,name=expected_next_event_id,json=expectedNextEventId,proto3" json:"expected_next_event_id,omitempty"` CurrentBranchToken []byte `protobuf:"bytes,4,opt,name=current_branch_token,json=currentBranchToken,proto3" json:"current_branch_token,omitempty"` VersionHistoryItem *v11.VersionHistoryItem `protobuf:"bytes,5,opt,name=version_history_item,json=versionHistoryItem,proto3" json:"version_history_item,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetMutableStateRequest) Reset() { *m = GetMutableStateRequest{} } func (m *GetMutableStateRequest) String() string { return proto.CompactTextString(m) } func (*GetMutableStateRequest) ProtoMessage() {} func (*GetMutableStateRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{16} } func (m *GetMutableStateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetMutableStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetMutableStateRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetMutableStateRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_GetMutableStateRequest.Merge(m, src) } func (m *GetMutableStateRequest) XXX_Size() int { return m.Size() } func (m *GetMutableStateRequest) XXX_DiscardUnknown() { xxx_messageInfo_GetMutableStateRequest.DiscardUnknown(m) } var xxx_messageInfo_GetMutableStateRequest proto.InternalMessageInfo func (m *GetMutableStateRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *GetMutableStateRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *GetMutableStateRequest) GetExpectedNextEventId() int64 { if m != nil { return m.ExpectedNextEventId } return 0 } func (m *GetMutableStateRequest) GetCurrentBranchToken() []byte { if m != nil { return m.CurrentBranchToken } return nil } func (m *GetMutableStateRequest) GetVersionHistoryItem() *v11.VersionHistoryItem { if m != nil { return m.VersionHistoryItem } return nil } type GetMutableStateResponse struct { WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,1,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` WorkflowType *v1.WorkflowType `protobuf:"bytes,2,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` NextEventId int64 `protobuf:"varint,3,opt,name=next_event_id,json=nextEventId,proto3" json:"next_event_id,omitempty"` PreviousStartedEventId *types.Int64Value `protobuf:"bytes,4,opt,name=previous_started_event_id,json=previousStartedEventId,proto3" json:"previous_started_event_id,omitempty"` LastFirstEventId int64 `protobuf:"varint,5,opt,name=last_first_event_id,json=lastFirstEventId,proto3" json:"last_first_event_id,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,6,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` StickyTaskList *v1.TaskList `protobuf:"bytes,7,opt,name=sticky_task_list,json=stickyTaskList,proto3" json:"sticky_task_list,omitempty"` ClientLibraryVersion string `protobuf:"bytes,8,opt,name=client_library_version,json=clientLibraryVersion,proto3" json:"client_library_version,omitempty"` ClientFeatureVersion string `protobuf:"bytes,9,opt,name=client_feature_version,json=clientFeatureVersion,proto3" json:"client_feature_version,omitempty"` ClientImpl string `protobuf:"bytes,10,opt,name=client_impl,json=clientImpl,proto3" json:"client_impl,omitempty"` StickyTaskListScheduleToStartTimeout *types.Duration `protobuf:"bytes,11,opt,name=sticky_task_list_schedule_to_start_timeout,json=stickyTaskListScheduleToStartTimeout,proto3" json:"sticky_task_list_schedule_to_start_timeout,omitempty"` EventStoreVersion int32 `protobuf:"varint,12,opt,name=event_store_version,json=eventStoreVersion,proto3" json:"event_store_version,omitempty"` CurrentBranchToken []byte `protobuf:"bytes,13,opt,name=current_branch_token,json=currentBranchToken,proto3" json:"current_branch_token,omitempty"` WorkflowState v12.WorkflowState `protobuf:"varint,14,opt,name=workflow_state,json=workflowState,proto3,enum=uber.cadence.shared.v1.WorkflowState" json:"workflow_state,omitempty"` WorkflowCloseState v1.WorkflowExecutionCloseStatus `protobuf:"varint,15,opt,name=workflow_close_state,json=workflowCloseState,proto3,enum=uber.cadence.api.v1.WorkflowExecutionCloseStatus" json:"workflow_close_state,omitempty"` VersionHistories *v12.VersionHistories `protobuf:"bytes,16,opt,name=version_histories,json=versionHistories,proto3" json:"version_histories,omitempty"` IsStickyTaskListEnabled bool `protobuf:"varint,17,opt,name=is_sticky_task_list_enabled,json=isStickyTaskListEnabled,proto3" json:"is_sticky_task_list_enabled,omitempty"` HistorySize int64 `protobuf:"varint,18,opt,name=history_size,json=historySize,proto3" json:"history_size,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetMutableStateResponse) Reset() { *m = GetMutableStateResponse{} } func (m *GetMutableStateResponse) String() string { return proto.CompactTextString(m) } func (*GetMutableStateResponse) ProtoMessage() {} func (*GetMutableStateResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{17} } func (m *GetMutableStateResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetMutableStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetMutableStateResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetMutableStateResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_GetMutableStateResponse.Merge(m, src) } func (m *GetMutableStateResponse) XXX_Size() int { return m.Size() } func (m *GetMutableStateResponse) XXX_DiscardUnknown() { xxx_messageInfo_GetMutableStateResponse.DiscardUnknown(m) } var xxx_messageInfo_GetMutableStateResponse proto.InternalMessageInfo func (m *GetMutableStateResponse) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *GetMutableStateResponse) GetWorkflowType() *v1.WorkflowType { if m != nil { return m.WorkflowType } return nil } func (m *GetMutableStateResponse) GetNextEventId() int64 { if m != nil { return m.NextEventId } return 0 } func (m *GetMutableStateResponse) GetPreviousStartedEventId() *types.Int64Value { if m != nil { return m.PreviousStartedEventId } return nil } func (m *GetMutableStateResponse) GetLastFirstEventId() int64 { if m != nil { return m.LastFirstEventId } return 0 } func (m *GetMutableStateResponse) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } func (m *GetMutableStateResponse) GetStickyTaskList() *v1.TaskList { if m != nil { return m.StickyTaskList } return nil } func (m *GetMutableStateResponse) GetClientLibraryVersion() string { if m != nil { return m.ClientLibraryVersion } return "" } func (m *GetMutableStateResponse) GetClientFeatureVersion() string { if m != nil { return m.ClientFeatureVersion } return "" } func (m *GetMutableStateResponse) GetClientImpl() string { if m != nil { return m.ClientImpl } return "" } func (m *GetMutableStateResponse) GetStickyTaskListScheduleToStartTimeout() *types.Duration { if m != nil { return m.StickyTaskListScheduleToStartTimeout } return nil } func (m *GetMutableStateResponse) GetEventStoreVersion() int32 { if m != nil { return m.EventStoreVersion } return 0 } func (m *GetMutableStateResponse) GetCurrentBranchToken() []byte { if m != nil { return m.CurrentBranchToken } return nil } func (m *GetMutableStateResponse) GetWorkflowState() v12.WorkflowState { if m != nil { return m.WorkflowState } return v12.WorkflowState_WORKFLOW_STATE_INVALID } func (m *GetMutableStateResponse) GetWorkflowCloseState() v1.WorkflowExecutionCloseStatus { if m != nil { return m.WorkflowCloseState } return v1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID } func (m *GetMutableStateResponse) GetVersionHistories() *v12.VersionHistories { if m != nil { return m.VersionHistories } return nil } func (m *GetMutableStateResponse) GetIsStickyTaskListEnabled() bool { if m != nil { return m.IsStickyTaskListEnabled } return false } func (m *GetMutableStateResponse) GetHistorySize() int64 { if m != nil { return m.HistorySize } return 0 } type PollMutableStateRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` ExpectedNextEventId int64 `protobuf:"varint,3,opt,name=expected_next_event_id,json=expectedNextEventId,proto3" json:"expected_next_event_id,omitempty"` CurrentBranchToken []byte `protobuf:"bytes,4,opt,name=current_branch_token,json=currentBranchToken,proto3" json:"current_branch_token,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *PollMutableStateRequest) Reset() { *m = PollMutableStateRequest{} } func (m *PollMutableStateRequest) String() string { return proto.CompactTextString(m) } func (*PollMutableStateRequest) ProtoMessage() {} func (*PollMutableStateRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{18} } func (m *PollMutableStateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *PollMutableStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_PollMutableStateRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *PollMutableStateRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_PollMutableStateRequest.Merge(m, src) } func (m *PollMutableStateRequest) XXX_Size() int { return m.Size() } func (m *PollMutableStateRequest) XXX_DiscardUnknown() { xxx_messageInfo_PollMutableStateRequest.DiscardUnknown(m) } var xxx_messageInfo_PollMutableStateRequest proto.InternalMessageInfo func (m *PollMutableStateRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *PollMutableStateRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *PollMutableStateRequest) GetExpectedNextEventId() int64 { if m != nil { return m.ExpectedNextEventId } return 0 } func (m *PollMutableStateRequest) GetCurrentBranchToken() []byte { if m != nil { return m.CurrentBranchToken } return nil } type PollMutableStateResponse struct { WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,1,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` WorkflowType *v1.WorkflowType `protobuf:"bytes,2,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` NextEventId int64 `protobuf:"varint,3,opt,name=next_event_id,json=nextEventId,proto3" json:"next_event_id,omitempty"` PreviousStartedEventId *types.Int64Value `protobuf:"bytes,4,opt,name=previous_started_event_id,json=previousStartedEventId,proto3" json:"previous_started_event_id,omitempty"` LastFirstEventId int64 `protobuf:"varint,5,opt,name=last_first_event_id,json=lastFirstEventId,proto3" json:"last_first_event_id,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,6,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` StickyTaskList *v1.TaskList `protobuf:"bytes,7,opt,name=sticky_task_list,json=stickyTaskList,proto3" json:"sticky_task_list,omitempty"` ClientLibraryVersion string `protobuf:"bytes,8,opt,name=client_library_version,json=clientLibraryVersion,proto3" json:"client_library_version,omitempty"` ClientFeatureVersion string `protobuf:"bytes,9,opt,name=client_feature_version,json=clientFeatureVersion,proto3" json:"client_feature_version,omitempty"` ClientImpl string `protobuf:"bytes,10,opt,name=client_impl,json=clientImpl,proto3" json:"client_impl,omitempty"` StickyTaskListScheduleToStartTimeout *types.Duration `protobuf:"bytes,11,opt,name=sticky_task_list_schedule_to_start_timeout,json=stickyTaskListScheduleToStartTimeout,proto3" json:"sticky_task_list_schedule_to_start_timeout,omitempty"` CurrentBranchToken []byte `protobuf:"bytes,12,opt,name=current_branch_token,json=currentBranchToken,proto3" json:"current_branch_token,omitempty"` VersionHistories *v12.VersionHistories `protobuf:"bytes,13,opt,name=version_histories,json=versionHistories,proto3" json:"version_histories,omitempty"` WorkflowState v12.WorkflowState `protobuf:"varint,14,opt,name=workflow_state,json=workflowState,proto3,enum=uber.cadence.shared.v1.WorkflowState" json:"workflow_state,omitempty"` WorkflowCloseState v1.WorkflowExecutionCloseStatus `protobuf:"varint,15,opt,name=workflow_close_state,json=workflowCloseState,proto3,enum=uber.cadence.api.v1.WorkflowExecutionCloseStatus" json:"workflow_close_state,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *PollMutableStateResponse) Reset() { *m = PollMutableStateResponse{} } func (m *PollMutableStateResponse) String() string { return proto.CompactTextString(m) } func (*PollMutableStateResponse) ProtoMessage() {} func (*PollMutableStateResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{19} } func (m *PollMutableStateResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *PollMutableStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_PollMutableStateResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *PollMutableStateResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_PollMutableStateResponse.Merge(m, src) } func (m *PollMutableStateResponse) XXX_Size() int { return m.Size() } func (m *PollMutableStateResponse) XXX_DiscardUnknown() { xxx_messageInfo_PollMutableStateResponse.DiscardUnknown(m) } var xxx_messageInfo_PollMutableStateResponse proto.InternalMessageInfo func (m *PollMutableStateResponse) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *PollMutableStateResponse) GetWorkflowType() *v1.WorkflowType { if m != nil { return m.WorkflowType } return nil } func (m *PollMutableStateResponse) GetNextEventId() int64 { if m != nil { return m.NextEventId } return 0 } func (m *PollMutableStateResponse) GetPreviousStartedEventId() *types.Int64Value { if m != nil { return m.PreviousStartedEventId } return nil } func (m *PollMutableStateResponse) GetLastFirstEventId() int64 { if m != nil { return m.LastFirstEventId } return 0 } func (m *PollMutableStateResponse) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } func (m *PollMutableStateResponse) GetStickyTaskList() *v1.TaskList { if m != nil { return m.StickyTaskList } return nil } func (m *PollMutableStateResponse) GetClientLibraryVersion() string { if m != nil { return m.ClientLibraryVersion } return "" } func (m *PollMutableStateResponse) GetClientFeatureVersion() string { if m != nil { return m.ClientFeatureVersion } return "" } func (m *PollMutableStateResponse) GetClientImpl() string { if m != nil { return m.ClientImpl } return "" } func (m *PollMutableStateResponse) GetStickyTaskListScheduleToStartTimeout() *types.Duration { if m != nil { return m.StickyTaskListScheduleToStartTimeout } return nil } func (m *PollMutableStateResponse) GetCurrentBranchToken() []byte { if m != nil { return m.CurrentBranchToken } return nil } func (m *PollMutableStateResponse) GetVersionHistories() *v12.VersionHistories { if m != nil { return m.VersionHistories } return nil } func (m *PollMutableStateResponse) GetWorkflowState() v12.WorkflowState { if m != nil { return m.WorkflowState } return v12.WorkflowState_WORKFLOW_STATE_INVALID } func (m *PollMutableStateResponse) GetWorkflowCloseState() v1.WorkflowExecutionCloseStatus { if m != nil { return m.WorkflowCloseState } return v1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID } type RecordDecisionTaskStartedRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` ScheduleId int64 `protobuf:"varint,3,opt,name=schedule_id,json=scheduleId,proto3" json:"schedule_id,omitempty"` TaskId int64 `protobuf:"varint,4,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // Unique id of each poll request. Used to ensure at most once delivery of tasks. RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` PollRequest *v1.PollForDecisionTaskRequest `protobuf:"bytes,6,opt,name=poll_request,json=pollRequest,proto3" json:"poll_request,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RecordDecisionTaskStartedRequest) Reset() { *m = RecordDecisionTaskStartedRequest{} } func (m *RecordDecisionTaskStartedRequest) String() string { return proto.CompactTextString(m) } func (*RecordDecisionTaskStartedRequest) ProtoMessage() {} func (*RecordDecisionTaskStartedRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{20} } func (m *RecordDecisionTaskStartedRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RecordDecisionTaskStartedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RecordDecisionTaskStartedRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RecordDecisionTaskStartedRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RecordDecisionTaskStartedRequest.Merge(m, src) } func (m *RecordDecisionTaskStartedRequest) XXX_Size() int { return m.Size() } func (m *RecordDecisionTaskStartedRequest) XXX_DiscardUnknown() { xxx_messageInfo_RecordDecisionTaskStartedRequest.DiscardUnknown(m) } var xxx_messageInfo_RecordDecisionTaskStartedRequest proto.InternalMessageInfo func (m *RecordDecisionTaskStartedRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *RecordDecisionTaskStartedRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *RecordDecisionTaskStartedRequest) GetScheduleId() int64 { if m != nil { return m.ScheduleId } return 0 } func (m *RecordDecisionTaskStartedRequest) GetTaskId() int64 { if m != nil { return m.TaskId } return 0 } func (m *RecordDecisionTaskStartedRequest) GetRequestId() string { if m != nil { return m.RequestId } return "" } func (m *RecordDecisionTaskStartedRequest) GetPollRequest() *v1.PollForDecisionTaskRequest { if m != nil { return m.PollRequest } return nil } type RecordDecisionTaskStartedResponse struct { WorkflowType *v1.WorkflowType `protobuf:"bytes,1,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` PreviousStartedEventId *types.Int64Value `protobuf:"bytes,2,opt,name=previous_started_event_id,json=previousStartedEventId,proto3" json:"previous_started_event_id,omitempty"` ScheduledEventId int64 `protobuf:"varint,3,opt,name=scheduled_event_id,json=scheduledEventId,proto3" json:"scheduled_event_id,omitempty"` StartedEventId int64 `protobuf:"varint,4,opt,name=started_event_id,json=startedEventId,proto3" json:"started_event_id,omitempty"` NextEventId int64 `protobuf:"varint,5,opt,name=next_event_id,json=nextEventId,proto3" json:"next_event_id,omitempty"` Attempt int32 `protobuf:"varint,6,opt,name=attempt,proto3" json:"attempt,omitempty"` StickyExecutionEnabled bool `protobuf:"varint,7,opt,name=sticky_execution_enabled,json=stickyExecutionEnabled,proto3" json:"sticky_execution_enabled,omitempty"` DecisionInfo *v12.TransientDecisionInfo `protobuf:"bytes,8,opt,name=decision_info,json=decisionInfo,proto3" json:"decision_info,omitempty"` WorkflowExecutionTaskList *v1.TaskList `protobuf:"bytes,9,opt,name=workflow_execution_task_list,json=workflowExecutionTaskList,proto3" json:"workflow_execution_task_list,omitempty"` EventStoreVersion int32 `protobuf:"varint,10,opt,name=event_store_version,json=eventStoreVersion,proto3" json:"event_store_version,omitempty"` BranchToken []byte `protobuf:"bytes,11,opt,name=branch_token,json=branchToken,proto3" json:"branch_token,omitempty"` ScheduledTime *types.Timestamp `protobuf:"bytes,12,opt,name=scheduled_time,json=scheduledTime,proto3" json:"scheduled_time,omitempty"` StartedTime *types.Timestamp `protobuf:"bytes,13,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"` Queries map[string]*v1.WorkflowQuery `protobuf:"bytes,14,rep,name=queries,proto3" json:"queries,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` HistorySize int64 `protobuf:"varint,15,opt,name=history_size,json=historySize,proto3" json:"history_size,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RecordDecisionTaskStartedResponse) Reset() { *m = RecordDecisionTaskStartedResponse{} } func (m *RecordDecisionTaskStartedResponse) String() string { return proto.CompactTextString(m) } func (*RecordDecisionTaskStartedResponse) ProtoMessage() {} func (*RecordDecisionTaskStartedResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{21} } func (m *RecordDecisionTaskStartedResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RecordDecisionTaskStartedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RecordDecisionTaskStartedResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RecordDecisionTaskStartedResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RecordDecisionTaskStartedResponse.Merge(m, src) } func (m *RecordDecisionTaskStartedResponse) XXX_Size() int { return m.Size() } func (m *RecordDecisionTaskStartedResponse) XXX_DiscardUnknown() { xxx_messageInfo_RecordDecisionTaskStartedResponse.DiscardUnknown(m) } var xxx_messageInfo_RecordDecisionTaskStartedResponse proto.InternalMessageInfo func (m *RecordDecisionTaskStartedResponse) GetWorkflowType() *v1.WorkflowType { if m != nil { return m.WorkflowType } return nil } func (m *RecordDecisionTaskStartedResponse) GetPreviousStartedEventId() *types.Int64Value { if m != nil { return m.PreviousStartedEventId } return nil } func (m *RecordDecisionTaskStartedResponse) GetScheduledEventId() int64 { if m != nil { return m.ScheduledEventId } return 0 } func (m *RecordDecisionTaskStartedResponse) GetStartedEventId() int64 { if m != nil { return m.StartedEventId } return 0 } func (m *RecordDecisionTaskStartedResponse) GetNextEventId() int64 { if m != nil { return m.NextEventId } return 0 } func (m *RecordDecisionTaskStartedResponse) GetAttempt() int32 { if m != nil { return m.Attempt } return 0 } func (m *RecordDecisionTaskStartedResponse) GetStickyExecutionEnabled() bool { if m != nil { return m.StickyExecutionEnabled } return false } func (m *RecordDecisionTaskStartedResponse) GetDecisionInfo() *v12.TransientDecisionInfo { if m != nil { return m.DecisionInfo } return nil } func (m *RecordDecisionTaskStartedResponse) GetWorkflowExecutionTaskList() *v1.TaskList { if m != nil { return m.WorkflowExecutionTaskList } return nil } func (m *RecordDecisionTaskStartedResponse) GetEventStoreVersion() int32 { if m != nil { return m.EventStoreVersion } return 0 } func (m *RecordDecisionTaskStartedResponse) GetBranchToken() []byte { if m != nil { return m.BranchToken } return nil } func (m *RecordDecisionTaskStartedResponse) GetScheduledTime() *types.Timestamp { if m != nil { return m.ScheduledTime } return nil } func (m *RecordDecisionTaskStartedResponse) GetStartedTime() *types.Timestamp { if m != nil { return m.StartedTime } return nil } func (m *RecordDecisionTaskStartedResponse) GetQueries() map[string]*v1.WorkflowQuery { if m != nil { return m.Queries } return nil } func (m *RecordDecisionTaskStartedResponse) GetHistorySize() int64 { if m != nil { return m.HistorySize } return 0 } type RecordActivityTaskStartedRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` ScheduleId int64 `protobuf:"varint,3,opt,name=schedule_id,json=scheduleId,proto3" json:"schedule_id,omitempty"` TaskId int64 `protobuf:"varint,4,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // Unique id of each poll request. Used to ensure at most once delivery of tasks. RequestId string `protobuf:"bytes,5,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` PollRequest *v1.PollForActivityTaskRequest `protobuf:"bytes,6,opt,name=poll_request,json=pollRequest,proto3" json:"poll_request,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RecordActivityTaskStartedRequest) Reset() { *m = RecordActivityTaskStartedRequest{} } func (m *RecordActivityTaskStartedRequest) String() string { return proto.CompactTextString(m) } func (*RecordActivityTaskStartedRequest) ProtoMessage() {} func (*RecordActivityTaskStartedRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{22} } func (m *RecordActivityTaskStartedRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RecordActivityTaskStartedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RecordActivityTaskStartedRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RecordActivityTaskStartedRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RecordActivityTaskStartedRequest.Merge(m, src) } func (m *RecordActivityTaskStartedRequest) XXX_Size() int { return m.Size() } func (m *RecordActivityTaskStartedRequest) XXX_DiscardUnknown() { xxx_messageInfo_RecordActivityTaskStartedRequest.DiscardUnknown(m) } var xxx_messageInfo_RecordActivityTaskStartedRequest proto.InternalMessageInfo func (m *RecordActivityTaskStartedRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *RecordActivityTaskStartedRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *RecordActivityTaskStartedRequest) GetScheduleId() int64 { if m != nil { return m.ScheduleId } return 0 } func (m *RecordActivityTaskStartedRequest) GetTaskId() int64 { if m != nil { return m.TaskId } return 0 } func (m *RecordActivityTaskStartedRequest) GetRequestId() string { if m != nil { return m.RequestId } return "" } func (m *RecordActivityTaskStartedRequest) GetPollRequest() *v1.PollForActivityTaskRequest { if m != nil { return m.PollRequest } return nil } type RecordActivityTaskStartedResponse struct { ScheduledEvent *v1.HistoryEvent `protobuf:"bytes,1,opt,name=scheduled_event,json=scheduledEvent,proto3" json:"scheduled_event,omitempty"` StartedTime *types.Timestamp `protobuf:"bytes,2,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"` Attempt int32 `protobuf:"varint,3,opt,name=attempt,proto3" json:"attempt,omitempty"` ScheduledTimeOfThisAttempt *types.Timestamp `protobuf:"bytes,4,opt,name=scheduled_time_of_this_attempt,json=scheduledTimeOfThisAttempt,proto3" json:"scheduled_time_of_this_attempt,omitempty"` HeartbeatDetails *v1.Payload `protobuf:"bytes,5,opt,name=heartbeat_details,json=heartbeatDetails,proto3" json:"heartbeat_details,omitempty"` WorkflowType *v1.WorkflowType `protobuf:"bytes,6,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` WorkflowDomain string `protobuf:"bytes,7,opt,name=workflow_domain,json=workflowDomain,proto3" json:"workflow_domain,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RecordActivityTaskStartedResponse) Reset() { *m = RecordActivityTaskStartedResponse{} } func (m *RecordActivityTaskStartedResponse) String() string { return proto.CompactTextString(m) } func (*RecordActivityTaskStartedResponse) ProtoMessage() {} func (*RecordActivityTaskStartedResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{23} } func (m *RecordActivityTaskStartedResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RecordActivityTaskStartedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RecordActivityTaskStartedResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RecordActivityTaskStartedResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RecordActivityTaskStartedResponse.Merge(m, src) } func (m *RecordActivityTaskStartedResponse) XXX_Size() int { return m.Size() } func (m *RecordActivityTaskStartedResponse) XXX_DiscardUnknown() { xxx_messageInfo_RecordActivityTaskStartedResponse.DiscardUnknown(m) } var xxx_messageInfo_RecordActivityTaskStartedResponse proto.InternalMessageInfo func (m *RecordActivityTaskStartedResponse) GetScheduledEvent() *v1.HistoryEvent { if m != nil { return m.ScheduledEvent } return nil } func (m *RecordActivityTaskStartedResponse) GetStartedTime() *types.Timestamp { if m != nil { return m.StartedTime } return nil } func (m *RecordActivityTaskStartedResponse) GetAttempt() int32 { if m != nil { return m.Attempt } return 0 } func (m *RecordActivityTaskStartedResponse) GetScheduledTimeOfThisAttempt() *types.Timestamp { if m != nil { return m.ScheduledTimeOfThisAttempt } return nil } func (m *RecordActivityTaskStartedResponse) GetHeartbeatDetails() *v1.Payload { if m != nil { return m.HeartbeatDetails } return nil } func (m *RecordActivityTaskStartedResponse) GetWorkflowType() *v1.WorkflowType { if m != nil { return m.WorkflowType } return nil } func (m *RecordActivityTaskStartedResponse) GetWorkflowDomain() string { if m != nil { return m.WorkflowDomain } return "" } type RespondDecisionTaskCompletedRequest struct { Request *v1.RespondDecisionTaskCompletedRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondDecisionTaskCompletedRequest) Reset() { *m = RespondDecisionTaskCompletedRequest{} } func (m *RespondDecisionTaskCompletedRequest) String() string { return proto.CompactTextString(m) } func (*RespondDecisionTaskCompletedRequest) ProtoMessage() {} func (*RespondDecisionTaskCompletedRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{24} } func (m *RespondDecisionTaskCompletedRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondDecisionTaskCompletedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondDecisionTaskCompletedRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondDecisionTaskCompletedRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondDecisionTaskCompletedRequest.Merge(m, src) } func (m *RespondDecisionTaskCompletedRequest) XXX_Size() int { return m.Size() } func (m *RespondDecisionTaskCompletedRequest) XXX_DiscardUnknown() { xxx_messageInfo_RespondDecisionTaskCompletedRequest.DiscardUnknown(m) } var xxx_messageInfo_RespondDecisionTaskCompletedRequest proto.InternalMessageInfo func (m *RespondDecisionTaskCompletedRequest) GetRequest() *v1.RespondDecisionTaskCompletedRequest { if m != nil { return m.Request } return nil } func (m *RespondDecisionTaskCompletedRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type RespondDecisionTaskCompletedResponse struct { StartedResponse *RecordDecisionTaskStartedResponse `protobuf:"bytes,1,opt,name=started_response,json=startedResponse,proto3" json:"started_response,omitempty"` ActivitiesToDispatchLocally map[string]*v1.ActivityLocalDispatchInfo `protobuf:"bytes,2,rep,name=activities_to_dispatch_locally,json=activitiesToDispatchLocally,proto3" json:"activities_to_dispatch_locally,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondDecisionTaskCompletedResponse) Reset() { *m = RespondDecisionTaskCompletedResponse{} } func (m *RespondDecisionTaskCompletedResponse) String() string { return proto.CompactTextString(m) } func (*RespondDecisionTaskCompletedResponse) ProtoMessage() {} func (*RespondDecisionTaskCompletedResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{25} } func (m *RespondDecisionTaskCompletedResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondDecisionTaskCompletedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondDecisionTaskCompletedResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondDecisionTaskCompletedResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondDecisionTaskCompletedResponse.Merge(m, src) } func (m *RespondDecisionTaskCompletedResponse) XXX_Size() int { return m.Size() } func (m *RespondDecisionTaskCompletedResponse) XXX_DiscardUnknown() { xxx_messageInfo_RespondDecisionTaskCompletedResponse.DiscardUnknown(m) } var xxx_messageInfo_RespondDecisionTaskCompletedResponse proto.InternalMessageInfo func (m *RespondDecisionTaskCompletedResponse) GetStartedResponse() *RecordDecisionTaskStartedResponse { if m != nil { return m.StartedResponse } return nil } func (m *RespondDecisionTaskCompletedResponse) GetActivitiesToDispatchLocally() map[string]*v1.ActivityLocalDispatchInfo { if m != nil { return m.ActivitiesToDispatchLocally } return nil } type RespondDecisionTaskFailedRequest struct { Request *v1.RespondDecisionTaskFailedRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondDecisionTaskFailedRequest) Reset() { *m = RespondDecisionTaskFailedRequest{} } func (m *RespondDecisionTaskFailedRequest) String() string { return proto.CompactTextString(m) } func (*RespondDecisionTaskFailedRequest) ProtoMessage() {} func (*RespondDecisionTaskFailedRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{26} } func (m *RespondDecisionTaskFailedRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondDecisionTaskFailedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondDecisionTaskFailedRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondDecisionTaskFailedRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondDecisionTaskFailedRequest.Merge(m, src) } func (m *RespondDecisionTaskFailedRequest) XXX_Size() int { return m.Size() } func (m *RespondDecisionTaskFailedRequest) XXX_DiscardUnknown() { xxx_messageInfo_RespondDecisionTaskFailedRequest.DiscardUnknown(m) } var xxx_messageInfo_RespondDecisionTaskFailedRequest proto.InternalMessageInfo func (m *RespondDecisionTaskFailedRequest) GetRequest() *v1.RespondDecisionTaskFailedRequest { if m != nil { return m.Request } return nil } func (m *RespondDecisionTaskFailedRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type RespondDecisionTaskFailedResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondDecisionTaskFailedResponse) Reset() { *m = RespondDecisionTaskFailedResponse{} } func (m *RespondDecisionTaskFailedResponse) String() string { return proto.CompactTextString(m) } func (*RespondDecisionTaskFailedResponse) ProtoMessage() {} func (*RespondDecisionTaskFailedResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{27} } func (m *RespondDecisionTaskFailedResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondDecisionTaskFailedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondDecisionTaskFailedResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondDecisionTaskFailedResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondDecisionTaskFailedResponse.Merge(m, src) } func (m *RespondDecisionTaskFailedResponse) XXX_Size() int { return m.Size() } func (m *RespondDecisionTaskFailedResponse) XXX_DiscardUnknown() { xxx_messageInfo_RespondDecisionTaskFailedResponse.DiscardUnknown(m) } var xxx_messageInfo_RespondDecisionTaskFailedResponse proto.InternalMessageInfo type RecordActivityTaskHeartbeatRequest struct { Request *v1.RecordActivityTaskHeartbeatRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RecordActivityTaskHeartbeatRequest) Reset() { *m = RecordActivityTaskHeartbeatRequest{} } func (m *RecordActivityTaskHeartbeatRequest) String() string { return proto.CompactTextString(m) } func (*RecordActivityTaskHeartbeatRequest) ProtoMessage() {} func (*RecordActivityTaskHeartbeatRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{28} } func (m *RecordActivityTaskHeartbeatRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RecordActivityTaskHeartbeatRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RecordActivityTaskHeartbeatRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RecordActivityTaskHeartbeatRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RecordActivityTaskHeartbeatRequest.Merge(m, src) } func (m *RecordActivityTaskHeartbeatRequest) XXX_Size() int { return m.Size() } func (m *RecordActivityTaskHeartbeatRequest) XXX_DiscardUnknown() { xxx_messageInfo_RecordActivityTaskHeartbeatRequest.DiscardUnknown(m) } var xxx_messageInfo_RecordActivityTaskHeartbeatRequest proto.InternalMessageInfo func (m *RecordActivityTaskHeartbeatRequest) GetRequest() *v1.RecordActivityTaskHeartbeatRequest { if m != nil { return m.Request } return nil } func (m *RecordActivityTaskHeartbeatRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type RecordActivityTaskHeartbeatResponse struct { CancelRequested bool `protobuf:"varint,1,opt,name=cancel_requested,json=cancelRequested,proto3" json:"cancel_requested,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RecordActivityTaskHeartbeatResponse) Reset() { *m = RecordActivityTaskHeartbeatResponse{} } func (m *RecordActivityTaskHeartbeatResponse) String() string { return proto.CompactTextString(m) } func (*RecordActivityTaskHeartbeatResponse) ProtoMessage() {} func (*RecordActivityTaskHeartbeatResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{29} } func (m *RecordActivityTaskHeartbeatResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RecordActivityTaskHeartbeatResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RecordActivityTaskHeartbeatResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RecordActivityTaskHeartbeatResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RecordActivityTaskHeartbeatResponse.Merge(m, src) } func (m *RecordActivityTaskHeartbeatResponse) XXX_Size() int { return m.Size() } func (m *RecordActivityTaskHeartbeatResponse) XXX_DiscardUnknown() { xxx_messageInfo_RecordActivityTaskHeartbeatResponse.DiscardUnknown(m) } var xxx_messageInfo_RecordActivityTaskHeartbeatResponse proto.InternalMessageInfo func (m *RecordActivityTaskHeartbeatResponse) GetCancelRequested() bool { if m != nil { return m.CancelRequested } return false } type RespondActivityTaskCompletedRequest struct { Request *v1.RespondActivityTaskCompletedRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondActivityTaskCompletedRequest) Reset() { *m = RespondActivityTaskCompletedRequest{} } func (m *RespondActivityTaskCompletedRequest) String() string { return proto.CompactTextString(m) } func (*RespondActivityTaskCompletedRequest) ProtoMessage() {} func (*RespondActivityTaskCompletedRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{30} } func (m *RespondActivityTaskCompletedRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondActivityTaskCompletedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondActivityTaskCompletedRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondActivityTaskCompletedRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondActivityTaskCompletedRequest.Merge(m, src) } func (m *RespondActivityTaskCompletedRequest) XXX_Size() int { return m.Size() } func (m *RespondActivityTaskCompletedRequest) XXX_DiscardUnknown() { xxx_messageInfo_RespondActivityTaskCompletedRequest.DiscardUnknown(m) } var xxx_messageInfo_RespondActivityTaskCompletedRequest proto.InternalMessageInfo func (m *RespondActivityTaskCompletedRequest) GetRequest() *v1.RespondActivityTaskCompletedRequest { if m != nil { return m.Request } return nil } func (m *RespondActivityTaskCompletedRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type RespondActivityTaskCompletedResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondActivityTaskCompletedResponse) Reset() { *m = RespondActivityTaskCompletedResponse{} } func (m *RespondActivityTaskCompletedResponse) String() string { return proto.CompactTextString(m) } func (*RespondActivityTaskCompletedResponse) ProtoMessage() {} func (*RespondActivityTaskCompletedResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{31} } func (m *RespondActivityTaskCompletedResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondActivityTaskCompletedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondActivityTaskCompletedResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondActivityTaskCompletedResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondActivityTaskCompletedResponse.Merge(m, src) } func (m *RespondActivityTaskCompletedResponse) XXX_Size() int { return m.Size() } func (m *RespondActivityTaskCompletedResponse) XXX_DiscardUnknown() { xxx_messageInfo_RespondActivityTaskCompletedResponse.DiscardUnknown(m) } var xxx_messageInfo_RespondActivityTaskCompletedResponse proto.InternalMessageInfo type RespondActivityTaskFailedRequest struct { Request *v1.RespondActivityTaskFailedRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondActivityTaskFailedRequest) Reset() { *m = RespondActivityTaskFailedRequest{} } func (m *RespondActivityTaskFailedRequest) String() string { return proto.CompactTextString(m) } func (*RespondActivityTaskFailedRequest) ProtoMessage() {} func (*RespondActivityTaskFailedRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{32} } func (m *RespondActivityTaskFailedRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondActivityTaskFailedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondActivityTaskFailedRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondActivityTaskFailedRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondActivityTaskFailedRequest.Merge(m, src) } func (m *RespondActivityTaskFailedRequest) XXX_Size() int { return m.Size() } func (m *RespondActivityTaskFailedRequest) XXX_DiscardUnknown() { xxx_messageInfo_RespondActivityTaskFailedRequest.DiscardUnknown(m) } var xxx_messageInfo_RespondActivityTaskFailedRequest proto.InternalMessageInfo func (m *RespondActivityTaskFailedRequest) GetRequest() *v1.RespondActivityTaskFailedRequest { if m != nil { return m.Request } return nil } func (m *RespondActivityTaskFailedRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type RespondActivityTaskFailedResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondActivityTaskFailedResponse) Reset() { *m = RespondActivityTaskFailedResponse{} } func (m *RespondActivityTaskFailedResponse) String() string { return proto.CompactTextString(m) } func (*RespondActivityTaskFailedResponse) ProtoMessage() {} func (*RespondActivityTaskFailedResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{33} } func (m *RespondActivityTaskFailedResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondActivityTaskFailedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondActivityTaskFailedResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondActivityTaskFailedResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondActivityTaskFailedResponse.Merge(m, src) } func (m *RespondActivityTaskFailedResponse) XXX_Size() int { return m.Size() } func (m *RespondActivityTaskFailedResponse) XXX_DiscardUnknown() { xxx_messageInfo_RespondActivityTaskFailedResponse.DiscardUnknown(m) } var xxx_messageInfo_RespondActivityTaskFailedResponse proto.InternalMessageInfo type RespondActivityTaskCanceledRequest struct { Request *v1.RespondActivityTaskCanceledRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondActivityTaskCanceledRequest) Reset() { *m = RespondActivityTaskCanceledRequest{} } func (m *RespondActivityTaskCanceledRequest) String() string { return proto.CompactTextString(m) } func (*RespondActivityTaskCanceledRequest) ProtoMessage() {} func (*RespondActivityTaskCanceledRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{34} } func (m *RespondActivityTaskCanceledRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondActivityTaskCanceledRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondActivityTaskCanceledRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondActivityTaskCanceledRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondActivityTaskCanceledRequest.Merge(m, src) } func (m *RespondActivityTaskCanceledRequest) XXX_Size() int { return m.Size() } func (m *RespondActivityTaskCanceledRequest) XXX_DiscardUnknown() { xxx_messageInfo_RespondActivityTaskCanceledRequest.DiscardUnknown(m) } var xxx_messageInfo_RespondActivityTaskCanceledRequest proto.InternalMessageInfo func (m *RespondActivityTaskCanceledRequest) GetRequest() *v1.RespondActivityTaskCanceledRequest { if m != nil { return m.Request } return nil } func (m *RespondActivityTaskCanceledRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type RespondActivityTaskCanceledResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondActivityTaskCanceledResponse) Reset() { *m = RespondActivityTaskCanceledResponse{} } func (m *RespondActivityTaskCanceledResponse) String() string { return proto.CompactTextString(m) } func (*RespondActivityTaskCanceledResponse) ProtoMessage() {} func (*RespondActivityTaskCanceledResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{35} } func (m *RespondActivityTaskCanceledResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondActivityTaskCanceledResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondActivityTaskCanceledResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondActivityTaskCanceledResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondActivityTaskCanceledResponse.Merge(m, src) } func (m *RespondActivityTaskCanceledResponse) XXX_Size() int { return m.Size() } func (m *RespondActivityTaskCanceledResponse) XXX_DiscardUnknown() { xxx_messageInfo_RespondActivityTaskCanceledResponse.DiscardUnknown(m) } var xxx_messageInfo_RespondActivityTaskCanceledResponse proto.InternalMessageInfo type RemoveSignalMutableStateRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RemoveSignalMutableStateRequest) Reset() { *m = RemoveSignalMutableStateRequest{} } func (m *RemoveSignalMutableStateRequest) String() string { return proto.CompactTextString(m) } func (*RemoveSignalMutableStateRequest) ProtoMessage() {} func (*RemoveSignalMutableStateRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{36} } func (m *RemoveSignalMutableStateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RemoveSignalMutableStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RemoveSignalMutableStateRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RemoveSignalMutableStateRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RemoveSignalMutableStateRequest.Merge(m, src) } func (m *RemoveSignalMutableStateRequest) XXX_Size() int { return m.Size() } func (m *RemoveSignalMutableStateRequest) XXX_DiscardUnknown() { xxx_messageInfo_RemoveSignalMutableStateRequest.DiscardUnknown(m) } var xxx_messageInfo_RemoveSignalMutableStateRequest proto.InternalMessageInfo func (m *RemoveSignalMutableStateRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *RemoveSignalMutableStateRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *RemoveSignalMutableStateRequest) GetRequestId() string { if m != nil { return m.RequestId } return "" } type RemoveSignalMutableStateResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RemoveSignalMutableStateResponse) Reset() { *m = RemoveSignalMutableStateResponse{} } func (m *RemoveSignalMutableStateResponse) String() string { return proto.CompactTextString(m) } func (*RemoveSignalMutableStateResponse) ProtoMessage() {} func (*RemoveSignalMutableStateResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{37} } func (m *RemoveSignalMutableStateResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RemoveSignalMutableStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RemoveSignalMutableStateResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RemoveSignalMutableStateResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RemoveSignalMutableStateResponse.Merge(m, src) } func (m *RemoveSignalMutableStateResponse) XXX_Size() int { return m.Size() } func (m *RemoveSignalMutableStateResponse) XXX_DiscardUnknown() { xxx_messageInfo_RemoveSignalMutableStateResponse.DiscardUnknown(m) } var xxx_messageInfo_RemoveSignalMutableStateResponse proto.InternalMessageInfo type RequestCancelWorkflowExecutionRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` CancelRequest *v1.RequestCancelWorkflowExecutionRequest `protobuf:"bytes,2,opt,name=cancel_request,json=cancelRequest,proto3" json:"cancel_request,omitempty"` // workflow execution that requests this cancellation, for making sure // the workflow being cancelled is actually a child of the workflow // making the request ExternalExecutionInfo *v1.ExternalExecutionInfo `protobuf:"bytes,3,opt,name=external_execution_info,json=externalExecutionInfo,proto3" json:"external_execution_info,omitempty"` ChildWorkflowOnly bool `protobuf:"varint,4,opt,name=child_workflow_only,json=childWorkflowOnly,proto3" json:"child_workflow_only,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RequestCancelWorkflowExecutionRequest) Reset() { *m = RequestCancelWorkflowExecutionRequest{} } func (m *RequestCancelWorkflowExecutionRequest) String() string { return proto.CompactTextString(m) } func (*RequestCancelWorkflowExecutionRequest) ProtoMessage() {} func (*RequestCancelWorkflowExecutionRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{38} } func (m *RequestCancelWorkflowExecutionRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RequestCancelWorkflowExecutionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RequestCancelWorkflowExecutionRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RequestCancelWorkflowExecutionRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RequestCancelWorkflowExecutionRequest.Merge(m, src) } func (m *RequestCancelWorkflowExecutionRequest) XXX_Size() int { return m.Size() } func (m *RequestCancelWorkflowExecutionRequest) XXX_DiscardUnknown() { xxx_messageInfo_RequestCancelWorkflowExecutionRequest.DiscardUnknown(m) } var xxx_messageInfo_RequestCancelWorkflowExecutionRequest proto.InternalMessageInfo func (m *RequestCancelWorkflowExecutionRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *RequestCancelWorkflowExecutionRequest) GetCancelRequest() *v1.RequestCancelWorkflowExecutionRequest { if m != nil { return m.CancelRequest } return nil } func (m *RequestCancelWorkflowExecutionRequest) GetExternalExecutionInfo() *v1.ExternalExecutionInfo { if m != nil { return m.ExternalExecutionInfo } return nil } func (m *RequestCancelWorkflowExecutionRequest) GetChildWorkflowOnly() bool { if m != nil { return m.ChildWorkflowOnly } return false } type RequestCancelWorkflowExecutionResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RequestCancelWorkflowExecutionResponse) Reset() { *m = RequestCancelWorkflowExecutionResponse{} } func (m *RequestCancelWorkflowExecutionResponse) String() string { return proto.CompactTextString(m) } func (*RequestCancelWorkflowExecutionResponse) ProtoMessage() {} func (*RequestCancelWorkflowExecutionResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{39} } func (m *RequestCancelWorkflowExecutionResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RequestCancelWorkflowExecutionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RequestCancelWorkflowExecutionResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RequestCancelWorkflowExecutionResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RequestCancelWorkflowExecutionResponse.Merge(m, src) } func (m *RequestCancelWorkflowExecutionResponse) XXX_Size() int { return m.Size() } func (m *RequestCancelWorkflowExecutionResponse) XXX_DiscardUnknown() { xxx_messageInfo_RequestCancelWorkflowExecutionResponse.DiscardUnknown(m) } var xxx_messageInfo_RequestCancelWorkflowExecutionResponse proto.InternalMessageInfo type ScheduleDecisionTaskRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` IsFirstDecision bool `protobuf:"varint,3,opt,name=is_first_decision,json=isFirstDecision,proto3" json:"is_first_decision,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ScheduleDecisionTaskRequest) Reset() { *m = ScheduleDecisionTaskRequest{} } func (m *ScheduleDecisionTaskRequest) String() string { return proto.CompactTextString(m) } func (*ScheduleDecisionTaskRequest) ProtoMessage() {} func (*ScheduleDecisionTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{40} } func (m *ScheduleDecisionTaskRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ScheduleDecisionTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ScheduleDecisionTaskRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ScheduleDecisionTaskRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_ScheduleDecisionTaskRequest.Merge(m, src) } func (m *ScheduleDecisionTaskRequest) XXX_Size() int { return m.Size() } func (m *ScheduleDecisionTaskRequest) XXX_DiscardUnknown() { xxx_messageInfo_ScheduleDecisionTaskRequest.DiscardUnknown(m) } var xxx_messageInfo_ScheduleDecisionTaskRequest proto.InternalMessageInfo func (m *ScheduleDecisionTaskRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *ScheduleDecisionTaskRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *ScheduleDecisionTaskRequest) GetIsFirstDecision() bool { if m != nil { return m.IsFirstDecision } return false } type ScheduleDecisionTaskResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ScheduleDecisionTaskResponse) Reset() { *m = ScheduleDecisionTaskResponse{} } func (m *ScheduleDecisionTaskResponse) String() string { return proto.CompactTextString(m) } func (*ScheduleDecisionTaskResponse) ProtoMessage() {} func (*ScheduleDecisionTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{41} } func (m *ScheduleDecisionTaskResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ScheduleDecisionTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ScheduleDecisionTaskResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ScheduleDecisionTaskResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_ScheduleDecisionTaskResponse.Merge(m, src) } func (m *ScheduleDecisionTaskResponse) XXX_Size() int { return m.Size() } func (m *ScheduleDecisionTaskResponse) XXX_DiscardUnknown() { xxx_messageInfo_ScheduleDecisionTaskResponse.DiscardUnknown(m) } var xxx_messageInfo_ScheduleDecisionTaskResponse proto.InternalMessageInfo // RecordChildExecutionCompletedRequest is used for reporting the completion of child execution to parent workflow // execution which started it. When a child execution is completed it creates this request and calls the // RecordChildExecutionCompleted API with the workflowExecution of parent. It also sets the completedExecution of the // child as it could potentially be different than the ChildExecutionStartedEvent of parent in the situation when // child creates multiple runs through ContinueAsNew before finally completing. type RecordChildExecutionCompletedRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` InitiatedId int64 `protobuf:"varint,3,opt,name=initiated_id,json=initiatedId,proto3" json:"initiated_id,omitempty"` CompletedExecution *v1.WorkflowExecution `protobuf:"bytes,4,opt,name=completed_execution,json=completedExecution,proto3" json:"completed_execution,omitempty"` CompletionEvent *v1.HistoryEvent `protobuf:"bytes,5,opt,name=completion_event,json=completionEvent,proto3" json:"completion_event,omitempty"` StartedId int64 `protobuf:"varint,6,opt,name=started_id,json=startedId,proto3" json:"started_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RecordChildExecutionCompletedRequest) Reset() { *m = RecordChildExecutionCompletedRequest{} } func (m *RecordChildExecutionCompletedRequest) String() string { return proto.CompactTextString(m) } func (*RecordChildExecutionCompletedRequest) ProtoMessage() {} func (*RecordChildExecutionCompletedRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{42} } func (m *RecordChildExecutionCompletedRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RecordChildExecutionCompletedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RecordChildExecutionCompletedRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RecordChildExecutionCompletedRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RecordChildExecutionCompletedRequest.Merge(m, src) } func (m *RecordChildExecutionCompletedRequest) XXX_Size() int { return m.Size() } func (m *RecordChildExecutionCompletedRequest) XXX_DiscardUnknown() { xxx_messageInfo_RecordChildExecutionCompletedRequest.DiscardUnknown(m) } var xxx_messageInfo_RecordChildExecutionCompletedRequest proto.InternalMessageInfo func (m *RecordChildExecutionCompletedRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *RecordChildExecutionCompletedRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *RecordChildExecutionCompletedRequest) GetInitiatedId() int64 { if m != nil { return m.InitiatedId } return 0 } func (m *RecordChildExecutionCompletedRequest) GetCompletedExecution() *v1.WorkflowExecution { if m != nil { return m.CompletedExecution } return nil } func (m *RecordChildExecutionCompletedRequest) GetCompletionEvent() *v1.HistoryEvent { if m != nil { return m.CompletionEvent } return nil } func (m *RecordChildExecutionCompletedRequest) GetStartedId() int64 { if m != nil { return m.StartedId } return 0 } type RecordChildExecutionCompletedResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RecordChildExecutionCompletedResponse) Reset() { *m = RecordChildExecutionCompletedResponse{} } func (m *RecordChildExecutionCompletedResponse) String() string { return proto.CompactTextString(m) } func (*RecordChildExecutionCompletedResponse) ProtoMessage() {} func (*RecordChildExecutionCompletedResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{43} } func (m *RecordChildExecutionCompletedResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RecordChildExecutionCompletedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RecordChildExecutionCompletedResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RecordChildExecutionCompletedResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RecordChildExecutionCompletedResponse.Merge(m, src) } func (m *RecordChildExecutionCompletedResponse) XXX_Size() int { return m.Size() } func (m *RecordChildExecutionCompletedResponse) XXX_DiscardUnknown() { xxx_messageInfo_RecordChildExecutionCompletedResponse.DiscardUnknown(m) } var xxx_messageInfo_RecordChildExecutionCompletedResponse proto.InternalMessageInfo type ReplicateEventsV2Request struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` VersionHistoryItems []*v11.VersionHistoryItem `protobuf:"bytes,3,rep,name=version_history_items,json=versionHistoryItems,proto3" json:"version_history_items,omitempty"` Events *v1.DataBlob `protobuf:"bytes,4,opt,name=events,proto3" json:"events,omitempty"` // New run events does not need version history since there is no prior events. NewRunEvents *v1.DataBlob `protobuf:"bytes,5,opt,name=new_run_events,json=newRunEvents,proto3" json:"new_run_events,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ReplicateEventsV2Request) Reset() { *m = ReplicateEventsV2Request{} } func (m *ReplicateEventsV2Request) String() string { return proto.CompactTextString(m) } func (*ReplicateEventsV2Request) ProtoMessage() {} func (*ReplicateEventsV2Request) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{44} } func (m *ReplicateEventsV2Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ReplicateEventsV2Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ReplicateEventsV2Request.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ReplicateEventsV2Request) XXX_Merge(src proto.Message) { xxx_messageInfo_ReplicateEventsV2Request.Merge(m, src) } func (m *ReplicateEventsV2Request) XXX_Size() int { return m.Size() } func (m *ReplicateEventsV2Request) XXX_DiscardUnknown() { xxx_messageInfo_ReplicateEventsV2Request.DiscardUnknown(m) } var xxx_messageInfo_ReplicateEventsV2Request proto.InternalMessageInfo func (m *ReplicateEventsV2Request) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *ReplicateEventsV2Request) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *ReplicateEventsV2Request) GetVersionHistoryItems() []*v11.VersionHistoryItem { if m != nil { return m.VersionHistoryItems } return nil } func (m *ReplicateEventsV2Request) GetEvents() *v1.DataBlob { if m != nil { return m.Events } return nil } func (m *ReplicateEventsV2Request) GetNewRunEvents() *v1.DataBlob { if m != nil { return m.NewRunEvents } return nil } type ReplicateEventsV2Response struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ReplicateEventsV2Response) Reset() { *m = ReplicateEventsV2Response{} } func (m *ReplicateEventsV2Response) String() string { return proto.CompactTextString(m) } func (*ReplicateEventsV2Response) ProtoMessage() {} func (*ReplicateEventsV2Response) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{45} } func (m *ReplicateEventsV2Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ReplicateEventsV2Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ReplicateEventsV2Response.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ReplicateEventsV2Response) XXX_Merge(src proto.Message) { xxx_messageInfo_ReplicateEventsV2Response.Merge(m, src) } func (m *ReplicateEventsV2Response) XXX_Size() int { return m.Size() } func (m *ReplicateEventsV2Response) XXX_DiscardUnknown() { xxx_messageInfo_ReplicateEventsV2Response.DiscardUnknown(m) } var xxx_messageInfo_ReplicateEventsV2Response proto.InternalMessageInfo type SyncShardStatusRequest struct { SourceCluster string `protobuf:"bytes,1,opt,name=source_cluster,json=sourceCluster,proto3" json:"source_cluster,omitempty"` ShardId int32 `protobuf:"varint,2,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` Time *types.Timestamp `protobuf:"bytes,3,opt,name=time,proto3" json:"time,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SyncShardStatusRequest) Reset() { *m = SyncShardStatusRequest{} } func (m *SyncShardStatusRequest) String() string { return proto.CompactTextString(m) } func (*SyncShardStatusRequest) ProtoMessage() {} func (*SyncShardStatusRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{46} } func (m *SyncShardStatusRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *SyncShardStatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_SyncShardStatusRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *SyncShardStatusRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_SyncShardStatusRequest.Merge(m, src) } func (m *SyncShardStatusRequest) XXX_Size() int { return m.Size() } func (m *SyncShardStatusRequest) XXX_DiscardUnknown() { xxx_messageInfo_SyncShardStatusRequest.DiscardUnknown(m) } var xxx_messageInfo_SyncShardStatusRequest proto.InternalMessageInfo func (m *SyncShardStatusRequest) GetSourceCluster() string { if m != nil { return m.SourceCluster } return "" } func (m *SyncShardStatusRequest) GetShardId() int32 { if m != nil { return m.ShardId } return 0 } func (m *SyncShardStatusRequest) GetTime() *types.Timestamp { if m != nil { return m.Time } return nil } type SyncShardStatusResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SyncShardStatusResponse) Reset() { *m = SyncShardStatusResponse{} } func (m *SyncShardStatusResponse) String() string { return proto.CompactTextString(m) } func (*SyncShardStatusResponse) ProtoMessage() {} func (*SyncShardStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{47} } func (m *SyncShardStatusResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *SyncShardStatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_SyncShardStatusResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *SyncShardStatusResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_SyncShardStatusResponse.Merge(m, src) } func (m *SyncShardStatusResponse) XXX_Size() int { return m.Size() } func (m *SyncShardStatusResponse) XXX_DiscardUnknown() { xxx_messageInfo_SyncShardStatusResponse.DiscardUnknown(m) } var xxx_messageInfo_SyncShardStatusResponse proto.InternalMessageInfo type SyncActivityRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` Version int64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` ScheduledId int64 `protobuf:"varint,4,opt,name=scheduled_id,json=scheduledId,proto3" json:"scheduled_id,omitempty"` ScheduledTime *types.Timestamp `protobuf:"bytes,5,opt,name=scheduled_time,json=scheduledTime,proto3" json:"scheduled_time,omitempty"` StartedId int64 `protobuf:"varint,6,opt,name=started_id,json=startedId,proto3" json:"started_id,omitempty"` StartedTime *types.Timestamp `protobuf:"bytes,7,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"` LastHeartbeatTime *types.Timestamp `protobuf:"bytes,8,opt,name=last_heartbeat_time,json=lastHeartbeatTime,proto3" json:"last_heartbeat_time,omitempty"` Details *v1.Payload `protobuf:"bytes,9,opt,name=details,proto3" json:"details,omitempty"` Attempt int32 `protobuf:"varint,10,opt,name=attempt,proto3" json:"attempt,omitempty"` LastFailure *v1.Failure `protobuf:"bytes,11,opt,name=last_failure,json=lastFailure,proto3" json:"last_failure,omitempty"` LastWorkerIdentity string `protobuf:"bytes,12,opt,name=last_worker_identity,json=lastWorkerIdentity,proto3" json:"last_worker_identity,omitempty"` VersionHistory *v11.VersionHistory `protobuf:"bytes,13,opt,name=version_history,json=versionHistory,proto3" json:"version_history,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SyncActivityRequest) Reset() { *m = SyncActivityRequest{} } func (m *SyncActivityRequest) String() string { return proto.CompactTextString(m) } func (*SyncActivityRequest) ProtoMessage() {} func (*SyncActivityRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{48} } func (m *SyncActivityRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *SyncActivityRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_SyncActivityRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *SyncActivityRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_SyncActivityRequest.Merge(m, src) } func (m *SyncActivityRequest) XXX_Size() int { return m.Size() } func (m *SyncActivityRequest) XXX_DiscardUnknown() { xxx_messageInfo_SyncActivityRequest.DiscardUnknown(m) } var xxx_messageInfo_SyncActivityRequest proto.InternalMessageInfo func (m *SyncActivityRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *SyncActivityRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *SyncActivityRequest) GetVersion() int64 { if m != nil { return m.Version } return 0 } func (m *SyncActivityRequest) GetScheduledId() int64 { if m != nil { return m.ScheduledId } return 0 } func (m *SyncActivityRequest) GetScheduledTime() *types.Timestamp { if m != nil { return m.ScheduledTime } return nil } func (m *SyncActivityRequest) GetStartedId() int64 { if m != nil { return m.StartedId } return 0 } func (m *SyncActivityRequest) GetStartedTime() *types.Timestamp { if m != nil { return m.StartedTime } return nil } func (m *SyncActivityRequest) GetLastHeartbeatTime() *types.Timestamp { if m != nil { return m.LastHeartbeatTime } return nil } func (m *SyncActivityRequest) GetDetails() *v1.Payload { if m != nil { return m.Details } return nil } func (m *SyncActivityRequest) GetAttempt() int32 { if m != nil { return m.Attempt } return 0 } func (m *SyncActivityRequest) GetLastFailure() *v1.Failure { if m != nil { return m.LastFailure } return nil } func (m *SyncActivityRequest) GetLastWorkerIdentity() string { if m != nil { return m.LastWorkerIdentity } return "" } func (m *SyncActivityRequest) GetVersionHistory() *v11.VersionHistory { if m != nil { return m.VersionHistory } return nil } type SyncActivityResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SyncActivityResponse) Reset() { *m = SyncActivityResponse{} } func (m *SyncActivityResponse) String() string { return proto.CompactTextString(m) } func (*SyncActivityResponse) ProtoMessage() {} func (*SyncActivityResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{49} } func (m *SyncActivityResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *SyncActivityResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_SyncActivityResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *SyncActivityResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_SyncActivityResponse.Merge(m, src) } func (m *SyncActivityResponse) XXX_Size() int { return m.Size() } func (m *SyncActivityResponse) XXX_DiscardUnknown() { xxx_messageInfo_SyncActivityResponse.DiscardUnknown(m) } var xxx_messageInfo_SyncActivityResponse proto.InternalMessageInfo type DescribeMutableStateRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DescribeMutableStateRequest) Reset() { *m = DescribeMutableStateRequest{} } func (m *DescribeMutableStateRequest) String() string { return proto.CompactTextString(m) } func (*DescribeMutableStateRequest) ProtoMessage() {} func (*DescribeMutableStateRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{50} } func (m *DescribeMutableStateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *DescribeMutableStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_DescribeMutableStateRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *DescribeMutableStateRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_DescribeMutableStateRequest.Merge(m, src) } func (m *DescribeMutableStateRequest) XXX_Size() int { return m.Size() } func (m *DescribeMutableStateRequest) XXX_DiscardUnknown() { xxx_messageInfo_DescribeMutableStateRequest.DiscardUnknown(m) } var xxx_messageInfo_DescribeMutableStateRequest proto.InternalMessageInfo func (m *DescribeMutableStateRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *DescribeMutableStateRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } type DescribeMutableStateResponse struct { MutableStateInCache string `protobuf:"bytes,1,opt,name=mutable_state_in_cache,json=mutableStateInCache,proto3" json:"mutable_state_in_cache,omitempty"` MutableStateInDatabase string `protobuf:"bytes,2,opt,name=mutable_state_in_database,json=mutableStateInDatabase,proto3" json:"mutable_state_in_database,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DescribeMutableStateResponse) Reset() { *m = DescribeMutableStateResponse{} } func (m *DescribeMutableStateResponse) String() string { return proto.CompactTextString(m) } func (*DescribeMutableStateResponse) ProtoMessage() {} func (*DescribeMutableStateResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{51} } func (m *DescribeMutableStateResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *DescribeMutableStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_DescribeMutableStateResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *DescribeMutableStateResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_DescribeMutableStateResponse.Merge(m, src) } func (m *DescribeMutableStateResponse) XXX_Size() int { return m.Size() } func (m *DescribeMutableStateResponse) XXX_DiscardUnknown() { xxx_messageInfo_DescribeMutableStateResponse.DiscardUnknown(m) } var xxx_messageInfo_DescribeMutableStateResponse proto.InternalMessageInfo func (m *DescribeMutableStateResponse) GetMutableStateInCache() string { if m != nil { return m.MutableStateInCache } return "" } func (m *DescribeMutableStateResponse) GetMutableStateInDatabase() string { if m != nil { return m.MutableStateInDatabase } return "" } type DescribeHistoryHostRequest struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DescribeHistoryHostRequest) Reset() { *m = DescribeHistoryHostRequest{} } func (m *DescribeHistoryHostRequest) String() string { return proto.CompactTextString(m) } func (*DescribeHistoryHostRequest) ProtoMessage() {} func (*DescribeHistoryHostRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{52} } func (m *DescribeHistoryHostRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *DescribeHistoryHostRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_DescribeHistoryHostRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *DescribeHistoryHostRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_DescribeHistoryHostRequest.Merge(m, src) } func (m *DescribeHistoryHostRequest) XXX_Size() int { return m.Size() } func (m *DescribeHistoryHostRequest) XXX_DiscardUnknown() { xxx_messageInfo_DescribeHistoryHostRequest.DiscardUnknown(m) } var xxx_messageInfo_DescribeHistoryHostRequest proto.InternalMessageInfo type DescribeHistoryHostResponse struct { NumberOfShards int32 `protobuf:"varint,1,opt,name=number_of_shards,json=numberOfShards,proto3" json:"number_of_shards,omitempty"` ShardIds []int32 `protobuf:"varint,2,rep,packed,name=shard_ids,json=shardIds,proto3" json:"shard_ids,omitempty"` DomainCache *v11.DomainCacheInfo `protobuf:"bytes,3,opt,name=domain_cache,json=domainCache,proto3" json:"domain_cache,omitempty"` ShardControllerStatus string `protobuf:"bytes,4,opt,name=shard_controller_status,json=shardControllerStatus,proto3" json:"shard_controller_status,omitempty"` Address string `protobuf:"bytes,5,opt,name=address,proto3" json:"address,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DescribeHistoryHostResponse) Reset() { *m = DescribeHistoryHostResponse{} } func (m *DescribeHistoryHostResponse) String() string { return proto.CompactTextString(m) } func (*DescribeHistoryHostResponse) ProtoMessage() {} func (*DescribeHistoryHostResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{53} } func (m *DescribeHistoryHostResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *DescribeHistoryHostResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_DescribeHistoryHostResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *DescribeHistoryHostResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_DescribeHistoryHostResponse.Merge(m, src) } func (m *DescribeHistoryHostResponse) XXX_Size() int { return m.Size() } func (m *DescribeHistoryHostResponse) XXX_DiscardUnknown() { xxx_messageInfo_DescribeHistoryHostResponse.DiscardUnknown(m) } var xxx_messageInfo_DescribeHistoryHostResponse proto.InternalMessageInfo func (m *DescribeHistoryHostResponse) GetNumberOfShards() int32 { if m != nil { return m.NumberOfShards } return 0 } func (m *DescribeHistoryHostResponse) GetShardIds() []int32 { if m != nil { return m.ShardIds } return nil } func (m *DescribeHistoryHostResponse) GetDomainCache() *v11.DomainCacheInfo { if m != nil { return m.DomainCache } return nil } func (m *DescribeHistoryHostResponse) GetShardControllerStatus() string { if m != nil { return m.ShardControllerStatus } return "" } func (m *DescribeHistoryHostResponse) GetAddress() string { if m != nil { return m.Address } return "" } type CloseShardRequest struct { ShardId int32 `protobuf:"varint,1,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *CloseShardRequest) Reset() { *m = CloseShardRequest{} } func (m *CloseShardRequest) String() string { return proto.CompactTextString(m) } func (*CloseShardRequest) ProtoMessage() {} func (*CloseShardRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{54} } func (m *CloseShardRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *CloseShardRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_CloseShardRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *CloseShardRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_CloseShardRequest.Merge(m, src) } func (m *CloseShardRequest) XXX_Size() int { return m.Size() } func (m *CloseShardRequest) XXX_DiscardUnknown() { xxx_messageInfo_CloseShardRequest.DiscardUnknown(m) } var xxx_messageInfo_CloseShardRequest proto.InternalMessageInfo func (m *CloseShardRequest) GetShardId() int32 { if m != nil { return m.ShardId } return 0 } type CloseShardResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *CloseShardResponse) Reset() { *m = CloseShardResponse{} } func (m *CloseShardResponse) String() string { return proto.CompactTextString(m) } func (*CloseShardResponse) ProtoMessage() {} func (*CloseShardResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{55} } func (m *CloseShardResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *CloseShardResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_CloseShardResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *CloseShardResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_CloseShardResponse.Merge(m, src) } func (m *CloseShardResponse) XXX_Size() int { return m.Size() } func (m *CloseShardResponse) XXX_DiscardUnknown() { xxx_messageInfo_CloseShardResponse.DiscardUnknown(m) } var xxx_messageInfo_CloseShardResponse proto.InternalMessageInfo type RemoveTaskRequest struct { ShardId int32 `protobuf:"varint,1,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` TaskType v11.TaskType `protobuf:"varint,2,opt,name=task_type,json=taskType,proto3,enum=uber.cadence.admin.v1.TaskType" json:"task_type,omitempty"` TaskId int64 `protobuf:"varint,3,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` VisibilityTime *types.Timestamp `protobuf:"bytes,4,opt,name=visibility_time,json=visibilityTime,proto3" json:"visibility_time,omitempty"` ClusterName string `protobuf:"bytes,5,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RemoveTaskRequest) Reset() { *m = RemoveTaskRequest{} } func (m *RemoveTaskRequest) String() string { return proto.CompactTextString(m) } func (*RemoveTaskRequest) ProtoMessage() {} func (*RemoveTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{56} } func (m *RemoveTaskRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RemoveTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RemoveTaskRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RemoveTaskRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RemoveTaskRequest.Merge(m, src) } func (m *RemoveTaskRequest) XXX_Size() int { return m.Size() } func (m *RemoveTaskRequest) XXX_DiscardUnknown() { xxx_messageInfo_RemoveTaskRequest.DiscardUnknown(m) } var xxx_messageInfo_RemoveTaskRequest proto.InternalMessageInfo func (m *RemoveTaskRequest) GetShardId() int32 { if m != nil { return m.ShardId } return 0 } func (m *RemoveTaskRequest) GetTaskType() v11.TaskType { if m != nil { return m.TaskType } return v11.TaskType_TASK_TYPE_INVALID } func (m *RemoveTaskRequest) GetTaskId() int64 { if m != nil { return m.TaskId } return 0 } func (m *RemoveTaskRequest) GetVisibilityTime() *types.Timestamp { if m != nil { return m.VisibilityTime } return nil } func (m *RemoveTaskRequest) GetClusterName() string { if m != nil { return m.ClusterName } return "" } type RemoveTaskResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RemoveTaskResponse) Reset() { *m = RemoveTaskResponse{} } func (m *RemoveTaskResponse) String() string { return proto.CompactTextString(m) } func (*RemoveTaskResponse) ProtoMessage() {} func (*RemoveTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{57} } func (m *RemoveTaskResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RemoveTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RemoveTaskResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RemoveTaskResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RemoveTaskResponse.Merge(m, src) } func (m *RemoveTaskResponse) XXX_Size() int { return m.Size() } func (m *RemoveTaskResponse) XXX_DiscardUnknown() { xxx_messageInfo_RemoveTaskResponse.DiscardUnknown(m) } var xxx_messageInfo_RemoveTaskResponse proto.InternalMessageInfo type ResetQueueRequest struct { ShardId int32 `protobuf:"varint,1,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` ClusterName string `protobuf:"bytes,2,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` TaskType v11.TaskType `protobuf:"varint,3,opt,name=task_type,json=taskType,proto3,enum=uber.cadence.admin.v1.TaskType" json:"task_type,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ResetQueueRequest) Reset() { *m = ResetQueueRequest{} } func (m *ResetQueueRequest) String() string { return proto.CompactTextString(m) } func (*ResetQueueRequest) ProtoMessage() {} func (*ResetQueueRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{58} } func (m *ResetQueueRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ResetQueueRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ResetQueueRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ResetQueueRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_ResetQueueRequest.Merge(m, src) } func (m *ResetQueueRequest) XXX_Size() int { return m.Size() } func (m *ResetQueueRequest) XXX_DiscardUnknown() { xxx_messageInfo_ResetQueueRequest.DiscardUnknown(m) } var xxx_messageInfo_ResetQueueRequest proto.InternalMessageInfo func (m *ResetQueueRequest) GetShardId() int32 { if m != nil { return m.ShardId } return 0 } func (m *ResetQueueRequest) GetClusterName() string { if m != nil { return m.ClusterName } return "" } func (m *ResetQueueRequest) GetTaskType() v11.TaskType { if m != nil { return m.TaskType } return v11.TaskType_TASK_TYPE_INVALID } type ResetQueueResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ResetQueueResponse) Reset() { *m = ResetQueueResponse{} } func (m *ResetQueueResponse) String() string { return proto.CompactTextString(m) } func (*ResetQueueResponse) ProtoMessage() {} func (*ResetQueueResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{59} } func (m *ResetQueueResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ResetQueueResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ResetQueueResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ResetQueueResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_ResetQueueResponse.Merge(m, src) } func (m *ResetQueueResponse) XXX_Size() int { return m.Size() } func (m *ResetQueueResponse) XXX_DiscardUnknown() { xxx_messageInfo_ResetQueueResponse.DiscardUnknown(m) } var xxx_messageInfo_ResetQueueResponse proto.InternalMessageInfo type DescribeQueueRequest struct { ShardId int32 `protobuf:"varint,1,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` ClusterName string `protobuf:"bytes,2,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` TaskType v11.TaskType `protobuf:"varint,3,opt,name=task_type,json=taskType,proto3,enum=uber.cadence.admin.v1.TaskType" json:"task_type,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DescribeQueueRequest) Reset() { *m = DescribeQueueRequest{} } func (m *DescribeQueueRequest) String() string { return proto.CompactTextString(m) } func (*DescribeQueueRequest) ProtoMessage() {} func (*DescribeQueueRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{60} } func (m *DescribeQueueRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *DescribeQueueRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_DescribeQueueRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *DescribeQueueRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_DescribeQueueRequest.Merge(m, src) } func (m *DescribeQueueRequest) XXX_Size() int { return m.Size() } func (m *DescribeQueueRequest) XXX_DiscardUnknown() { xxx_messageInfo_DescribeQueueRequest.DiscardUnknown(m) } var xxx_messageInfo_DescribeQueueRequest proto.InternalMessageInfo func (m *DescribeQueueRequest) GetShardId() int32 { if m != nil { return m.ShardId } return 0 } func (m *DescribeQueueRequest) GetClusterName() string { if m != nil { return m.ClusterName } return "" } func (m *DescribeQueueRequest) GetTaskType() v11.TaskType { if m != nil { return m.TaskType } return v11.TaskType_TASK_TYPE_INVALID } type DescribeQueueResponse struct { ProcessingQueueStates []string `protobuf:"bytes,1,rep,name=processing_queue_states,json=processingQueueStates,proto3" json:"processing_queue_states,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DescribeQueueResponse) Reset() { *m = DescribeQueueResponse{} } func (m *DescribeQueueResponse) String() string { return proto.CompactTextString(m) } func (*DescribeQueueResponse) ProtoMessage() {} func (*DescribeQueueResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{61} } func (m *DescribeQueueResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *DescribeQueueResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_DescribeQueueResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *DescribeQueueResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_DescribeQueueResponse.Merge(m, src) } func (m *DescribeQueueResponse) XXX_Size() int { return m.Size() } func (m *DescribeQueueResponse) XXX_DiscardUnknown() { xxx_messageInfo_DescribeQueueResponse.DiscardUnknown(m) } var xxx_messageInfo_DescribeQueueResponse proto.InternalMessageInfo func (m *DescribeQueueResponse) GetProcessingQueueStates() []string { if m != nil { return m.ProcessingQueueStates } return nil } type GetReplicationMessagesRequest struct { Tokens []*v11.ReplicationToken `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens,omitempty"` ClusterName string `protobuf:"bytes,2,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetReplicationMessagesRequest) Reset() { *m = GetReplicationMessagesRequest{} } func (m *GetReplicationMessagesRequest) String() string { return proto.CompactTextString(m) } func (*GetReplicationMessagesRequest) ProtoMessage() {} func (*GetReplicationMessagesRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{62} } func (m *GetReplicationMessagesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetReplicationMessagesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetReplicationMessagesRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetReplicationMessagesRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_GetReplicationMessagesRequest.Merge(m, src) } func (m *GetReplicationMessagesRequest) XXX_Size() int { return m.Size() } func (m *GetReplicationMessagesRequest) XXX_DiscardUnknown() { xxx_messageInfo_GetReplicationMessagesRequest.DiscardUnknown(m) } var xxx_messageInfo_GetReplicationMessagesRequest proto.InternalMessageInfo func (m *GetReplicationMessagesRequest) GetTokens() []*v11.ReplicationToken { if m != nil { return m.Tokens } return nil } func (m *GetReplicationMessagesRequest) GetClusterName() string { if m != nil { return m.ClusterName } return "" } type GetReplicationMessagesResponse struct { ShardMessages map[int32]*v11.ReplicationMessages `protobuf:"bytes,1,rep,name=shard_messages,json=shardMessages,proto3" json:"shard_messages,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetReplicationMessagesResponse) Reset() { *m = GetReplicationMessagesResponse{} } func (m *GetReplicationMessagesResponse) String() string { return proto.CompactTextString(m) } func (*GetReplicationMessagesResponse) ProtoMessage() {} func (*GetReplicationMessagesResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{63} } func (m *GetReplicationMessagesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetReplicationMessagesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetReplicationMessagesResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetReplicationMessagesResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_GetReplicationMessagesResponse.Merge(m, src) } func (m *GetReplicationMessagesResponse) XXX_Size() int { return m.Size() } func (m *GetReplicationMessagesResponse) XXX_DiscardUnknown() { xxx_messageInfo_GetReplicationMessagesResponse.DiscardUnknown(m) } var xxx_messageInfo_GetReplicationMessagesResponse proto.InternalMessageInfo func (m *GetReplicationMessagesResponse) GetShardMessages() map[int32]*v11.ReplicationMessages { if m != nil { return m.ShardMessages } return nil } type GetDLQReplicationMessagesRequest struct { TaskInfos []*v11.ReplicationTaskInfo `protobuf:"bytes,1,rep,name=task_infos,json=taskInfos,proto3" json:"task_infos,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetDLQReplicationMessagesRequest) Reset() { *m = GetDLQReplicationMessagesRequest{} } func (m *GetDLQReplicationMessagesRequest) String() string { return proto.CompactTextString(m) } func (*GetDLQReplicationMessagesRequest) ProtoMessage() {} func (*GetDLQReplicationMessagesRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{64} } func (m *GetDLQReplicationMessagesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetDLQReplicationMessagesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetDLQReplicationMessagesRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetDLQReplicationMessagesRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_GetDLQReplicationMessagesRequest.Merge(m, src) } func (m *GetDLQReplicationMessagesRequest) XXX_Size() int { return m.Size() } func (m *GetDLQReplicationMessagesRequest) XXX_DiscardUnknown() { xxx_messageInfo_GetDLQReplicationMessagesRequest.DiscardUnknown(m) } var xxx_messageInfo_GetDLQReplicationMessagesRequest proto.InternalMessageInfo func (m *GetDLQReplicationMessagesRequest) GetTaskInfos() []*v11.ReplicationTaskInfo { if m != nil { return m.TaskInfos } return nil } type GetDLQReplicationMessagesResponse struct { ReplicationTasks []*v11.ReplicationTask `protobuf:"bytes,1,rep,name=replication_tasks,json=replicationTasks,proto3" json:"replication_tasks,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetDLQReplicationMessagesResponse) Reset() { *m = GetDLQReplicationMessagesResponse{} } func (m *GetDLQReplicationMessagesResponse) String() string { return proto.CompactTextString(m) } func (*GetDLQReplicationMessagesResponse) ProtoMessage() {} func (*GetDLQReplicationMessagesResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{65} } func (m *GetDLQReplicationMessagesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetDLQReplicationMessagesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetDLQReplicationMessagesResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetDLQReplicationMessagesResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_GetDLQReplicationMessagesResponse.Merge(m, src) } func (m *GetDLQReplicationMessagesResponse) XXX_Size() int { return m.Size() } func (m *GetDLQReplicationMessagesResponse) XXX_DiscardUnknown() { xxx_messageInfo_GetDLQReplicationMessagesResponse.DiscardUnknown(m) } var xxx_messageInfo_GetDLQReplicationMessagesResponse proto.InternalMessageInfo func (m *GetDLQReplicationMessagesResponse) GetReplicationTasks() []*v11.ReplicationTask { if m != nil { return m.ReplicationTasks } return nil } type ReapplyEventsRequest struct { Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,3,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` Events *v1.DataBlob `protobuf:"bytes,4,opt,name=events,proto3" json:"events,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ReapplyEventsRequest) Reset() { *m = ReapplyEventsRequest{} } func (m *ReapplyEventsRequest) String() string { return proto.CompactTextString(m) } func (*ReapplyEventsRequest) ProtoMessage() {} func (*ReapplyEventsRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{66} } func (m *ReapplyEventsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ReapplyEventsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ReapplyEventsRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ReapplyEventsRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_ReapplyEventsRequest.Merge(m, src) } func (m *ReapplyEventsRequest) XXX_Size() int { return m.Size() } func (m *ReapplyEventsRequest) XXX_DiscardUnknown() { xxx_messageInfo_ReapplyEventsRequest.DiscardUnknown(m) } var xxx_messageInfo_ReapplyEventsRequest proto.InternalMessageInfo func (m *ReapplyEventsRequest) GetDomain() string { if m != nil { return m.Domain } return "" } func (m *ReapplyEventsRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *ReapplyEventsRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *ReapplyEventsRequest) GetEvents() *v1.DataBlob { if m != nil { return m.Events } return nil } type ReapplyEventsResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ReapplyEventsResponse) Reset() { *m = ReapplyEventsResponse{} } func (m *ReapplyEventsResponse) String() string { return proto.CompactTextString(m) } func (*ReapplyEventsResponse) ProtoMessage() {} func (*ReapplyEventsResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{67} } func (m *ReapplyEventsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ReapplyEventsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ReapplyEventsResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ReapplyEventsResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_ReapplyEventsResponse.Merge(m, src) } func (m *ReapplyEventsResponse) XXX_Size() int { return m.Size() } func (m *ReapplyEventsResponse) XXX_DiscardUnknown() { xxx_messageInfo_ReapplyEventsResponse.DiscardUnknown(m) } var xxx_messageInfo_ReapplyEventsResponse proto.InternalMessageInfo type RefreshWorkflowTasksRequest struct { Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,3,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RefreshWorkflowTasksRequest) Reset() { *m = RefreshWorkflowTasksRequest{} } func (m *RefreshWorkflowTasksRequest) String() string { return proto.CompactTextString(m) } func (*RefreshWorkflowTasksRequest) ProtoMessage() {} func (*RefreshWorkflowTasksRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{68} } func (m *RefreshWorkflowTasksRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RefreshWorkflowTasksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RefreshWorkflowTasksRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RefreshWorkflowTasksRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RefreshWorkflowTasksRequest.Merge(m, src) } func (m *RefreshWorkflowTasksRequest) XXX_Size() int { return m.Size() } func (m *RefreshWorkflowTasksRequest) XXX_DiscardUnknown() { xxx_messageInfo_RefreshWorkflowTasksRequest.DiscardUnknown(m) } var xxx_messageInfo_RefreshWorkflowTasksRequest proto.InternalMessageInfo func (m *RefreshWorkflowTasksRequest) GetDomain() string { if m != nil { return m.Domain } return "" } func (m *RefreshWorkflowTasksRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *RefreshWorkflowTasksRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } type RefreshWorkflowTasksResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RefreshWorkflowTasksResponse) Reset() { *m = RefreshWorkflowTasksResponse{} } func (m *RefreshWorkflowTasksResponse) String() string { return proto.CompactTextString(m) } func (*RefreshWorkflowTasksResponse) ProtoMessage() {} func (*RefreshWorkflowTasksResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{69} } func (m *RefreshWorkflowTasksResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RefreshWorkflowTasksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RefreshWorkflowTasksResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RefreshWorkflowTasksResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RefreshWorkflowTasksResponse.Merge(m, src) } func (m *RefreshWorkflowTasksResponse) XXX_Size() int { return m.Size() } func (m *RefreshWorkflowTasksResponse) XXX_DiscardUnknown() { xxx_messageInfo_RefreshWorkflowTasksResponse.DiscardUnknown(m) } var xxx_messageInfo_RefreshWorkflowTasksResponse proto.InternalMessageInfo type CountDLQMessagesRequest struct { ForceFetch bool `protobuf:"varint,1,opt,name=forceFetch,proto3" json:"forceFetch,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *CountDLQMessagesRequest) Reset() { *m = CountDLQMessagesRequest{} } func (m *CountDLQMessagesRequest) String() string { return proto.CompactTextString(m) } func (*CountDLQMessagesRequest) ProtoMessage() {} func (*CountDLQMessagesRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{70} } func (m *CountDLQMessagesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *CountDLQMessagesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_CountDLQMessagesRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *CountDLQMessagesRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_CountDLQMessagesRequest.Merge(m, src) } func (m *CountDLQMessagesRequest) XXX_Size() int { return m.Size() } func (m *CountDLQMessagesRequest) XXX_DiscardUnknown() { xxx_messageInfo_CountDLQMessagesRequest.DiscardUnknown(m) } var xxx_messageInfo_CountDLQMessagesRequest proto.InternalMessageInfo func (m *CountDLQMessagesRequest) GetForceFetch() bool { if m != nil { return m.ForceFetch } return false } type CountDLQMessagesResponse struct { Entries []*v11.HistoryDLQCountEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *CountDLQMessagesResponse) Reset() { *m = CountDLQMessagesResponse{} } func (m *CountDLQMessagesResponse) String() string { return proto.CompactTextString(m) } func (*CountDLQMessagesResponse) ProtoMessage() {} func (*CountDLQMessagesResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{71} } func (m *CountDLQMessagesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *CountDLQMessagesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_CountDLQMessagesResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *CountDLQMessagesResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_CountDLQMessagesResponse.Merge(m, src) } func (m *CountDLQMessagesResponse) XXX_Size() int { return m.Size() } func (m *CountDLQMessagesResponse) XXX_DiscardUnknown() { xxx_messageInfo_CountDLQMessagesResponse.DiscardUnknown(m) } var xxx_messageInfo_CountDLQMessagesResponse proto.InternalMessageInfo func (m *CountDLQMessagesResponse) GetEntries() []*v11.HistoryDLQCountEntry { if m != nil { return m.Entries } return nil } type ReadDLQMessagesRequest struct { Type v11.DLQType `protobuf:"varint,1,opt,name=type,proto3,enum=uber.cadence.admin.v1.DLQType" json:"type,omitempty"` ShardId int32 `protobuf:"varint,2,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` SourceCluster string `protobuf:"bytes,3,opt,name=source_cluster,json=sourceCluster,proto3" json:"source_cluster,omitempty"` InclusiveEndMessageId *types.Int64Value `protobuf:"bytes,4,opt,name=inclusive_end_message_id,json=inclusiveEndMessageId,proto3" json:"inclusive_end_message_id,omitempty"` PageSize int32 `protobuf:"varint,5,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` NextPageToken []byte `protobuf:"bytes,6,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ReadDLQMessagesRequest) Reset() { *m = ReadDLQMessagesRequest{} } func (m *ReadDLQMessagesRequest) String() string { return proto.CompactTextString(m) } func (*ReadDLQMessagesRequest) ProtoMessage() {} func (*ReadDLQMessagesRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{72} } func (m *ReadDLQMessagesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ReadDLQMessagesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ReadDLQMessagesRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ReadDLQMessagesRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_ReadDLQMessagesRequest.Merge(m, src) } func (m *ReadDLQMessagesRequest) XXX_Size() int { return m.Size() } func (m *ReadDLQMessagesRequest) XXX_DiscardUnknown() { xxx_messageInfo_ReadDLQMessagesRequest.DiscardUnknown(m) } var xxx_messageInfo_ReadDLQMessagesRequest proto.InternalMessageInfo func (m *ReadDLQMessagesRequest) GetType() v11.DLQType { if m != nil { return m.Type } return v11.DLQType_DLQ_TYPE_INVALID } func (m *ReadDLQMessagesRequest) GetShardId() int32 { if m != nil { return m.ShardId } return 0 } func (m *ReadDLQMessagesRequest) GetSourceCluster() string { if m != nil { return m.SourceCluster } return "" } func (m *ReadDLQMessagesRequest) GetInclusiveEndMessageId() *types.Int64Value { if m != nil { return m.InclusiveEndMessageId } return nil } func (m *ReadDLQMessagesRequest) GetPageSize() int32 { if m != nil { return m.PageSize } return 0 } func (m *ReadDLQMessagesRequest) GetNextPageToken() []byte { if m != nil { return m.NextPageToken } return nil } type ReadDLQMessagesResponse struct { Type v11.DLQType `protobuf:"varint,1,opt,name=type,proto3,enum=uber.cadence.admin.v1.DLQType" json:"type,omitempty"` ReplicationTasks []*v11.ReplicationTask `protobuf:"bytes,2,rep,name=replication_tasks,json=replicationTasks,proto3" json:"replication_tasks,omitempty"` ReplicationTasksInfo []*v11.ReplicationTaskInfo `protobuf:"bytes,3,rep,name=replication_tasks_info,json=replicationTasksInfo,proto3" json:"replication_tasks_info,omitempty"` NextPageToken []byte `protobuf:"bytes,4,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ReadDLQMessagesResponse) Reset() { *m = ReadDLQMessagesResponse{} } func (m *ReadDLQMessagesResponse) String() string { return proto.CompactTextString(m) } func (*ReadDLQMessagesResponse) ProtoMessage() {} func (*ReadDLQMessagesResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{73} } func (m *ReadDLQMessagesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ReadDLQMessagesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ReadDLQMessagesResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ReadDLQMessagesResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_ReadDLQMessagesResponse.Merge(m, src) } func (m *ReadDLQMessagesResponse) XXX_Size() int { return m.Size() } func (m *ReadDLQMessagesResponse) XXX_DiscardUnknown() { xxx_messageInfo_ReadDLQMessagesResponse.DiscardUnknown(m) } var xxx_messageInfo_ReadDLQMessagesResponse proto.InternalMessageInfo func (m *ReadDLQMessagesResponse) GetType() v11.DLQType { if m != nil { return m.Type } return v11.DLQType_DLQ_TYPE_INVALID } func (m *ReadDLQMessagesResponse) GetReplicationTasks() []*v11.ReplicationTask { if m != nil { return m.ReplicationTasks } return nil } func (m *ReadDLQMessagesResponse) GetReplicationTasksInfo() []*v11.ReplicationTaskInfo { if m != nil { return m.ReplicationTasksInfo } return nil } func (m *ReadDLQMessagesResponse) GetNextPageToken() []byte { if m != nil { return m.NextPageToken } return nil } type PurgeDLQMessagesRequest struct { Type v11.DLQType `protobuf:"varint,1,opt,name=type,proto3,enum=uber.cadence.admin.v1.DLQType" json:"type,omitempty"` ShardId int32 `protobuf:"varint,2,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` SourceCluster string `protobuf:"bytes,3,opt,name=source_cluster,json=sourceCluster,proto3" json:"source_cluster,omitempty"` InclusiveEndMessageId *types.Int64Value `protobuf:"bytes,4,opt,name=inclusive_end_message_id,json=inclusiveEndMessageId,proto3" json:"inclusive_end_message_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *PurgeDLQMessagesRequest) Reset() { *m = PurgeDLQMessagesRequest{} } func (m *PurgeDLQMessagesRequest) String() string { return proto.CompactTextString(m) } func (*PurgeDLQMessagesRequest) ProtoMessage() {} func (*PurgeDLQMessagesRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{74} } func (m *PurgeDLQMessagesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *PurgeDLQMessagesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_PurgeDLQMessagesRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *PurgeDLQMessagesRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_PurgeDLQMessagesRequest.Merge(m, src) } func (m *PurgeDLQMessagesRequest) XXX_Size() int { return m.Size() } func (m *PurgeDLQMessagesRequest) XXX_DiscardUnknown() { xxx_messageInfo_PurgeDLQMessagesRequest.DiscardUnknown(m) } var xxx_messageInfo_PurgeDLQMessagesRequest proto.InternalMessageInfo func (m *PurgeDLQMessagesRequest) GetType() v11.DLQType { if m != nil { return m.Type } return v11.DLQType_DLQ_TYPE_INVALID } func (m *PurgeDLQMessagesRequest) GetShardId() int32 { if m != nil { return m.ShardId } return 0 } func (m *PurgeDLQMessagesRequest) GetSourceCluster() string { if m != nil { return m.SourceCluster } return "" } func (m *PurgeDLQMessagesRequest) GetInclusiveEndMessageId() *types.Int64Value { if m != nil { return m.InclusiveEndMessageId } return nil } type PurgeDLQMessagesResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *PurgeDLQMessagesResponse) Reset() { *m = PurgeDLQMessagesResponse{} } func (m *PurgeDLQMessagesResponse) String() string { return proto.CompactTextString(m) } func (*PurgeDLQMessagesResponse) ProtoMessage() {} func (*PurgeDLQMessagesResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{75} } func (m *PurgeDLQMessagesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *PurgeDLQMessagesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_PurgeDLQMessagesResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *PurgeDLQMessagesResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_PurgeDLQMessagesResponse.Merge(m, src) } func (m *PurgeDLQMessagesResponse) XXX_Size() int { return m.Size() } func (m *PurgeDLQMessagesResponse) XXX_DiscardUnknown() { xxx_messageInfo_PurgeDLQMessagesResponse.DiscardUnknown(m) } var xxx_messageInfo_PurgeDLQMessagesResponse proto.InternalMessageInfo type MergeDLQMessagesRequest struct { Type v11.DLQType `protobuf:"varint,1,opt,name=type,proto3,enum=uber.cadence.admin.v1.DLQType" json:"type,omitempty"` ShardId int32 `protobuf:"varint,2,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` SourceCluster string `protobuf:"bytes,3,opt,name=source_cluster,json=sourceCluster,proto3" json:"source_cluster,omitempty"` InclusiveEndMessageId *types.Int64Value `protobuf:"bytes,4,opt,name=inclusive_end_message_id,json=inclusiveEndMessageId,proto3" json:"inclusive_end_message_id,omitempty"` PageSize int32 `protobuf:"varint,5,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` NextPageToken []byte `protobuf:"bytes,6,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *MergeDLQMessagesRequest) Reset() { *m = MergeDLQMessagesRequest{} } func (m *MergeDLQMessagesRequest) String() string { return proto.CompactTextString(m) } func (*MergeDLQMessagesRequest) ProtoMessage() {} func (*MergeDLQMessagesRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{76} } func (m *MergeDLQMessagesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *MergeDLQMessagesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_MergeDLQMessagesRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *MergeDLQMessagesRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_MergeDLQMessagesRequest.Merge(m, src) } func (m *MergeDLQMessagesRequest) XXX_Size() int { return m.Size() } func (m *MergeDLQMessagesRequest) XXX_DiscardUnknown() { xxx_messageInfo_MergeDLQMessagesRequest.DiscardUnknown(m) } var xxx_messageInfo_MergeDLQMessagesRequest proto.InternalMessageInfo func (m *MergeDLQMessagesRequest) GetType() v11.DLQType { if m != nil { return m.Type } return v11.DLQType_DLQ_TYPE_INVALID } func (m *MergeDLQMessagesRequest) GetShardId() int32 { if m != nil { return m.ShardId } return 0 } func (m *MergeDLQMessagesRequest) GetSourceCluster() string { if m != nil { return m.SourceCluster } return "" } func (m *MergeDLQMessagesRequest) GetInclusiveEndMessageId() *types.Int64Value { if m != nil { return m.InclusiveEndMessageId } return nil } func (m *MergeDLQMessagesRequest) GetPageSize() int32 { if m != nil { return m.PageSize } return 0 } func (m *MergeDLQMessagesRequest) GetNextPageToken() []byte { if m != nil { return m.NextPageToken } return nil } type MergeDLQMessagesResponse struct { NextPageToken []byte `protobuf:"bytes,1,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *MergeDLQMessagesResponse) Reset() { *m = MergeDLQMessagesResponse{} } func (m *MergeDLQMessagesResponse) String() string { return proto.CompactTextString(m) } func (*MergeDLQMessagesResponse) ProtoMessage() {} func (*MergeDLQMessagesResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{77} } func (m *MergeDLQMessagesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *MergeDLQMessagesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_MergeDLQMessagesResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *MergeDLQMessagesResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_MergeDLQMessagesResponse.Merge(m, src) } func (m *MergeDLQMessagesResponse) XXX_Size() int { return m.Size() } func (m *MergeDLQMessagesResponse) XXX_DiscardUnknown() { xxx_messageInfo_MergeDLQMessagesResponse.DiscardUnknown(m) } var xxx_messageInfo_MergeDLQMessagesResponse proto.InternalMessageInfo func (m *MergeDLQMessagesResponse) GetNextPageToken() []byte { if m != nil { return m.NextPageToken } return nil } type NotifyFailoverMarkersRequest struct { FailoverMarkerTokens []*v11.FailoverMarkerToken `protobuf:"bytes,1,rep,name=failover_marker_tokens,json=failoverMarkerTokens,proto3" json:"failover_marker_tokens,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *NotifyFailoverMarkersRequest) Reset() { *m = NotifyFailoverMarkersRequest{} } func (m *NotifyFailoverMarkersRequest) String() string { return proto.CompactTextString(m) } func (*NotifyFailoverMarkersRequest) ProtoMessage() {} func (*NotifyFailoverMarkersRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{78} } func (m *NotifyFailoverMarkersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *NotifyFailoverMarkersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_NotifyFailoverMarkersRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *NotifyFailoverMarkersRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_NotifyFailoverMarkersRequest.Merge(m, src) } func (m *NotifyFailoverMarkersRequest) XXX_Size() int { return m.Size() } func (m *NotifyFailoverMarkersRequest) XXX_DiscardUnknown() { xxx_messageInfo_NotifyFailoverMarkersRequest.DiscardUnknown(m) } var xxx_messageInfo_NotifyFailoverMarkersRequest proto.InternalMessageInfo func (m *NotifyFailoverMarkersRequest) GetFailoverMarkerTokens() []*v11.FailoverMarkerToken { if m != nil { return m.FailoverMarkerTokens } return nil } type NotifyFailoverMarkersResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *NotifyFailoverMarkersResponse) Reset() { *m = NotifyFailoverMarkersResponse{} } func (m *NotifyFailoverMarkersResponse) String() string { return proto.CompactTextString(m) } func (*NotifyFailoverMarkersResponse) ProtoMessage() {} func (*NotifyFailoverMarkersResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{79} } func (m *NotifyFailoverMarkersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *NotifyFailoverMarkersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_NotifyFailoverMarkersResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *NotifyFailoverMarkersResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_NotifyFailoverMarkersResponse.Merge(m, src) } func (m *NotifyFailoverMarkersResponse) XXX_Size() int { return m.Size() } func (m *NotifyFailoverMarkersResponse) XXX_DiscardUnknown() { xxx_messageInfo_NotifyFailoverMarkersResponse.DiscardUnknown(m) } var xxx_messageInfo_NotifyFailoverMarkersResponse proto.InternalMessageInfo type GetCrossClusterTasksRequest struct { ShardIds []int32 `protobuf:"varint,1,rep,packed,name=shard_ids,json=shardIds,proto3" json:"shard_ids,omitempty"` TargetCluster string `protobuf:"bytes,2,opt,name=target_cluster,json=targetCluster,proto3" json:"target_cluster,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetCrossClusterTasksRequest) Reset() { *m = GetCrossClusterTasksRequest{} } func (m *GetCrossClusterTasksRequest) String() string { return proto.CompactTextString(m) } func (*GetCrossClusterTasksRequest) ProtoMessage() {} func (*GetCrossClusterTasksRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{80} } func (m *GetCrossClusterTasksRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetCrossClusterTasksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetCrossClusterTasksRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetCrossClusterTasksRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_GetCrossClusterTasksRequest.Merge(m, src) } func (m *GetCrossClusterTasksRequest) XXX_Size() int { return m.Size() } func (m *GetCrossClusterTasksRequest) XXX_DiscardUnknown() { xxx_messageInfo_GetCrossClusterTasksRequest.DiscardUnknown(m) } var xxx_messageInfo_GetCrossClusterTasksRequest proto.InternalMessageInfo func (m *GetCrossClusterTasksRequest) GetShardIds() []int32 { if m != nil { return m.ShardIds } return nil } func (m *GetCrossClusterTasksRequest) GetTargetCluster() string { if m != nil { return m.TargetCluster } return "" } type GetCrossClusterTasksResponse struct { TasksByShard map[int32]*v11.CrossClusterTaskRequests `protobuf:"bytes,1,rep,name=tasks_by_shard,json=tasksByShard,proto3" json:"tasks_by_shard,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` FailedCauseByShard map[int32]v11.GetTaskFailedCause `protobuf:"bytes,2,rep,name=failed_cause_by_shard,json=failedCauseByShard,proto3" json:"failed_cause_by_shard,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3,enum=uber.cadence.admin.v1.GetTaskFailedCause"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetCrossClusterTasksResponse) Reset() { *m = GetCrossClusterTasksResponse{} } func (m *GetCrossClusterTasksResponse) String() string { return proto.CompactTextString(m) } func (*GetCrossClusterTasksResponse) ProtoMessage() {} func (*GetCrossClusterTasksResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{81} } func (m *GetCrossClusterTasksResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetCrossClusterTasksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetCrossClusterTasksResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetCrossClusterTasksResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_GetCrossClusterTasksResponse.Merge(m, src) } func (m *GetCrossClusterTasksResponse) XXX_Size() int { return m.Size() } func (m *GetCrossClusterTasksResponse) XXX_DiscardUnknown() { xxx_messageInfo_GetCrossClusterTasksResponse.DiscardUnknown(m) } var xxx_messageInfo_GetCrossClusterTasksResponse proto.InternalMessageInfo func (m *GetCrossClusterTasksResponse) GetTasksByShard() map[int32]*v11.CrossClusterTaskRequests { if m != nil { return m.TasksByShard } return nil } func (m *GetCrossClusterTasksResponse) GetFailedCauseByShard() map[int32]v11.GetTaskFailedCause { if m != nil { return m.FailedCauseByShard } return nil } type RespondCrossClusterTasksCompletedRequest struct { ShardId int32 `protobuf:"varint,1,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` TargetCluster string `protobuf:"bytes,2,opt,name=target_cluster,json=targetCluster,proto3" json:"target_cluster,omitempty"` TaskResponses []*v11.CrossClusterTaskResponse `protobuf:"bytes,3,rep,name=task_responses,json=taskResponses,proto3" json:"task_responses,omitempty"` FetchNewTasks bool `protobuf:"varint,4,opt,name=fetchNewTasks,proto3" json:"fetchNewTasks,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondCrossClusterTasksCompletedRequest) Reset() { *m = RespondCrossClusterTasksCompletedRequest{} } func (m *RespondCrossClusterTasksCompletedRequest) String() string { return proto.CompactTextString(m) } func (*RespondCrossClusterTasksCompletedRequest) ProtoMessage() {} func (*RespondCrossClusterTasksCompletedRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{82} } func (m *RespondCrossClusterTasksCompletedRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondCrossClusterTasksCompletedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondCrossClusterTasksCompletedRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondCrossClusterTasksCompletedRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondCrossClusterTasksCompletedRequest.Merge(m, src) } func (m *RespondCrossClusterTasksCompletedRequest) XXX_Size() int { return m.Size() } func (m *RespondCrossClusterTasksCompletedRequest) XXX_DiscardUnknown() { xxx_messageInfo_RespondCrossClusterTasksCompletedRequest.DiscardUnknown(m) } var xxx_messageInfo_RespondCrossClusterTasksCompletedRequest proto.InternalMessageInfo func (m *RespondCrossClusterTasksCompletedRequest) GetShardId() int32 { if m != nil { return m.ShardId } return 0 } func (m *RespondCrossClusterTasksCompletedRequest) GetTargetCluster() string { if m != nil { return m.TargetCluster } return "" } func (m *RespondCrossClusterTasksCompletedRequest) GetTaskResponses() []*v11.CrossClusterTaskResponse { if m != nil { return m.TaskResponses } return nil } func (m *RespondCrossClusterTasksCompletedRequest) GetFetchNewTasks() bool { if m != nil { return m.FetchNewTasks } return false } type RespondCrossClusterTasksCompletedResponse struct { Tasks *v11.CrossClusterTaskRequests `protobuf:"bytes,1,opt,name=tasks,proto3" json:"tasks,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondCrossClusterTasksCompletedResponse) Reset() { *m = RespondCrossClusterTasksCompletedResponse{} } func (m *RespondCrossClusterTasksCompletedResponse) String() string { return proto.CompactTextString(m) } func (*RespondCrossClusterTasksCompletedResponse) ProtoMessage() {} func (*RespondCrossClusterTasksCompletedResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{83} } func (m *RespondCrossClusterTasksCompletedResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondCrossClusterTasksCompletedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondCrossClusterTasksCompletedResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondCrossClusterTasksCompletedResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondCrossClusterTasksCompletedResponse.Merge(m, src) } func (m *RespondCrossClusterTasksCompletedResponse) XXX_Size() int { return m.Size() } func (m *RespondCrossClusterTasksCompletedResponse) XXX_DiscardUnknown() { xxx_messageInfo_RespondCrossClusterTasksCompletedResponse.DiscardUnknown(m) } var xxx_messageInfo_RespondCrossClusterTasksCompletedResponse proto.InternalMessageInfo func (m *RespondCrossClusterTasksCompletedResponse) GetTasks() *v11.CrossClusterTaskRequests { if m != nil { return m.Tasks } return nil } type GetFailoverInfoRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetFailoverInfoRequest) Reset() { *m = GetFailoverInfoRequest{} } func (m *GetFailoverInfoRequest) String() string { return proto.CompactTextString(m) } func (*GetFailoverInfoRequest) ProtoMessage() {} func (*GetFailoverInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{84} } func (m *GetFailoverInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetFailoverInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetFailoverInfoRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetFailoverInfoRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_GetFailoverInfoRequest.Merge(m, src) } func (m *GetFailoverInfoRequest) XXX_Size() int { return m.Size() } func (m *GetFailoverInfoRequest) XXX_DiscardUnknown() { xxx_messageInfo_GetFailoverInfoRequest.DiscardUnknown(m) } var xxx_messageInfo_GetFailoverInfoRequest proto.InternalMessageInfo func (m *GetFailoverInfoRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type GetFailoverInfoResponse struct { CompletedShardCount int32 `protobuf:"varint,1,opt,name=completed_shard_count,json=completedShardCount,proto3" json:"completed_shard_count,omitempty"` PendingShards []int32 `protobuf:"varint,2,rep,packed,name=pending_shards,json=pendingShards,proto3" json:"pending_shards,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetFailoverInfoResponse) Reset() { *m = GetFailoverInfoResponse{} } func (m *GetFailoverInfoResponse) String() string { return proto.CompactTextString(m) } func (*GetFailoverInfoResponse) ProtoMessage() {} func (*GetFailoverInfoResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{85} } func (m *GetFailoverInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetFailoverInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetFailoverInfoResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetFailoverInfoResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_GetFailoverInfoResponse.Merge(m, src) } func (m *GetFailoverInfoResponse) XXX_Size() int { return m.Size() } func (m *GetFailoverInfoResponse) XXX_DiscardUnknown() { xxx_messageInfo_GetFailoverInfoResponse.DiscardUnknown(m) } var xxx_messageInfo_GetFailoverInfoResponse proto.InternalMessageInfo func (m *GetFailoverInfoResponse) GetCompletedShardCount() int32 { if m != nil { return m.CompletedShardCount } return 0 } func (m *GetFailoverInfoResponse) GetPendingShards() []int32 { if m != nil { return m.PendingShards } return nil } type RatelimitUpdateRequest struct { // impl-specific data. // likely some simple top-level keys and then either: // - map // - list // // this is a single blob rather than a collection to save on // repeated serialization of the type name, and to allow impls // to choose whatever structures are most-convenient for them. Data *v12.Any `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RatelimitUpdateRequest) Reset() { *m = RatelimitUpdateRequest{} } func (m *RatelimitUpdateRequest) String() string { return proto.CompactTextString(m) } func (*RatelimitUpdateRequest) ProtoMessage() {} func (*RatelimitUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{86} } func (m *RatelimitUpdateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RatelimitUpdateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RatelimitUpdateRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RatelimitUpdateRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RatelimitUpdateRequest.Merge(m, src) } func (m *RatelimitUpdateRequest) XXX_Size() int { return m.Size() } func (m *RatelimitUpdateRequest) XXX_DiscardUnknown() { xxx_messageInfo_RatelimitUpdateRequest.DiscardUnknown(m) } var xxx_messageInfo_RatelimitUpdateRequest proto.InternalMessageInfo func (m *RatelimitUpdateRequest) GetData() *v12.Any { if m != nil { return m.Data } return nil } type RatelimitUpdateResponse struct { // impl-specific data. // // likely some simple top-level keys and then either: // - map // - list // // this is a single blob rather than a collection to save on // repeated serialization of the type name, and to allow impls // to choose whatever structures are most-convenient for them. Data *v12.Any `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RatelimitUpdateResponse) Reset() { *m = RatelimitUpdateResponse{} } func (m *RatelimitUpdateResponse) String() string { return proto.CompactTextString(m) } func (*RatelimitUpdateResponse) ProtoMessage() {} func (*RatelimitUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor_fee8ff76963a38ed, []int{87} } func (m *RatelimitUpdateResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RatelimitUpdateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RatelimitUpdateResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RatelimitUpdateResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RatelimitUpdateResponse.Merge(m, src) } func (m *RatelimitUpdateResponse) XXX_Size() int { return m.Size() } func (m *RatelimitUpdateResponse) XXX_DiscardUnknown() { xxx_messageInfo_RatelimitUpdateResponse.DiscardUnknown(m) } var xxx_messageInfo_RatelimitUpdateResponse proto.InternalMessageInfo func (m *RatelimitUpdateResponse) GetData() *v12.Any { if m != nil { return m.Data } return nil } func init() { proto.RegisterType((*StartWorkflowExecutionRequest)(nil), "uber.cadence.history.v1.StartWorkflowExecutionRequest") proto.RegisterMapType((map[string]string)(nil), "uber.cadence.history.v1.StartWorkflowExecutionRequest.PartitionConfigEntry") proto.RegisterType((*StartWorkflowExecutionResponse)(nil), "uber.cadence.history.v1.StartWorkflowExecutionResponse") proto.RegisterType((*SignalWorkflowExecutionRequest)(nil), "uber.cadence.history.v1.SignalWorkflowExecutionRequest") proto.RegisterType((*SignalWorkflowExecutionResponse)(nil), "uber.cadence.history.v1.SignalWorkflowExecutionResponse") proto.RegisterType((*SignalWithStartWorkflowExecutionRequest)(nil), "uber.cadence.history.v1.SignalWithStartWorkflowExecutionRequest") proto.RegisterMapType((map[string]string)(nil), "uber.cadence.history.v1.SignalWithStartWorkflowExecutionRequest.PartitionConfigEntry") proto.RegisterType((*SignalWithStartWorkflowExecutionResponse)(nil), "uber.cadence.history.v1.SignalWithStartWorkflowExecutionResponse") proto.RegisterType((*ResetWorkflowExecutionRequest)(nil), "uber.cadence.history.v1.ResetWorkflowExecutionRequest") proto.RegisterType((*ResetWorkflowExecutionResponse)(nil), "uber.cadence.history.v1.ResetWorkflowExecutionResponse") proto.RegisterType((*TerminateWorkflowExecutionRequest)(nil), "uber.cadence.history.v1.TerminateWorkflowExecutionRequest") proto.RegisterType((*TerminateWorkflowExecutionResponse)(nil), "uber.cadence.history.v1.TerminateWorkflowExecutionResponse") proto.RegisterType((*DescribeWorkflowExecutionRequest)(nil), "uber.cadence.history.v1.DescribeWorkflowExecutionRequest") proto.RegisterType((*DescribeWorkflowExecutionResponse)(nil), "uber.cadence.history.v1.DescribeWorkflowExecutionResponse") proto.RegisterType((*QueryWorkflowRequest)(nil), "uber.cadence.history.v1.QueryWorkflowRequest") proto.RegisterType((*QueryWorkflowResponse)(nil), "uber.cadence.history.v1.QueryWorkflowResponse") proto.RegisterType((*ResetStickyTaskListRequest)(nil), "uber.cadence.history.v1.ResetStickyTaskListRequest") proto.RegisterType((*ResetStickyTaskListResponse)(nil), "uber.cadence.history.v1.ResetStickyTaskListResponse") proto.RegisterType((*GetMutableStateRequest)(nil), "uber.cadence.history.v1.GetMutableStateRequest") proto.RegisterType((*GetMutableStateResponse)(nil), "uber.cadence.history.v1.GetMutableStateResponse") proto.RegisterType((*PollMutableStateRequest)(nil), "uber.cadence.history.v1.PollMutableStateRequest") proto.RegisterType((*PollMutableStateResponse)(nil), "uber.cadence.history.v1.PollMutableStateResponse") proto.RegisterType((*RecordDecisionTaskStartedRequest)(nil), "uber.cadence.history.v1.RecordDecisionTaskStartedRequest") proto.RegisterType((*RecordDecisionTaskStartedResponse)(nil), "uber.cadence.history.v1.RecordDecisionTaskStartedResponse") proto.RegisterMapType((map[string]*v1.WorkflowQuery)(nil), "uber.cadence.history.v1.RecordDecisionTaskStartedResponse.QueriesEntry") proto.RegisterType((*RecordActivityTaskStartedRequest)(nil), "uber.cadence.history.v1.RecordActivityTaskStartedRequest") proto.RegisterType((*RecordActivityTaskStartedResponse)(nil), "uber.cadence.history.v1.RecordActivityTaskStartedResponse") proto.RegisterType((*RespondDecisionTaskCompletedRequest)(nil), "uber.cadence.history.v1.RespondDecisionTaskCompletedRequest") proto.RegisterType((*RespondDecisionTaskCompletedResponse)(nil), "uber.cadence.history.v1.RespondDecisionTaskCompletedResponse") proto.RegisterMapType((map[string]*v1.ActivityLocalDispatchInfo)(nil), "uber.cadence.history.v1.RespondDecisionTaskCompletedResponse.ActivitiesToDispatchLocallyEntry") proto.RegisterType((*RespondDecisionTaskFailedRequest)(nil), "uber.cadence.history.v1.RespondDecisionTaskFailedRequest") proto.RegisterType((*RespondDecisionTaskFailedResponse)(nil), "uber.cadence.history.v1.RespondDecisionTaskFailedResponse") proto.RegisterType((*RecordActivityTaskHeartbeatRequest)(nil), "uber.cadence.history.v1.RecordActivityTaskHeartbeatRequest") proto.RegisterType((*RecordActivityTaskHeartbeatResponse)(nil), "uber.cadence.history.v1.RecordActivityTaskHeartbeatResponse") proto.RegisterType((*RespondActivityTaskCompletedRequest)(nil), "uber.cadence.history.v1.RespondActivityTaskCompletedRequest") proto.RegisterType((*RespondActivityTaskCompletedResponse)(nil), "uber.cadence.history.v1.RespondActivityTaskCompletedResponse") proto.RegisterType((*RespondActivityTaskFailedRequest)(nil), "uber.cadence.history.v1.RespondActivityTaskFailedRequest") proto.RegisterType((*RespondActivityTaskFailedResponse)(nil), "uber.cadence.history.v1.RespondActivityTaskFailedResponse") proto.RegisterType((*RespondActivityTaskCanceledRequest)(nil), "uber.cadence.history.v1.RespondActivityTaskCanceledRequest") proto.RegisterType((*RespondActivityTaskCanceledResponse)(nil), "uber.cadence.history.v1.RespondActivityTaskCanceledResponse") proto.RegisterType((*RemoveSignalMutableStateRequest)(nil), "uber.cadence.history.v1.RemoveSignalMutableStateRequest") proto.RegisterType((*RemoveSignalMutableStateResponse)(nil), "uber.cadence.history.v1.RemoveSignalMutableStateResponse") proto.RegisterType((*RequestCancelWorkflowExecutionRequest)(nil), "uber.cadence.history.v1.RequestCancelWorkflowExecutionRequest") proto.RegisterType((*RequestCancelWorkflowExecutionResponse)(nil), "uber.cadence.history.v1.RequestCancelWorkflowExecutionResponse") proto.RegisterType((*ScheduleDecisionTaskRequest)(nil), "uber.cadence.history.v1.ScheduleDecisionTaskRequest") proto.RegisterType((*ScheduleDecisionTaskResponse)(nil), "uber.cadence.history.v1.ScheduleDecisionTaskResponse") proto.RegisterType((*RecordChildExecutionCompletedRequest)(nil), "uber.cadence.history.v1.RecordChildExecutionCompletedRequest") proto.RegisterType((*RecordChildExecutionCompletedResponse)(nil), "uber.cadence.history.v1.RecordChildExecutionCompletedResponse") proto.RegisterType((*ReplicateEventsV2Request)(nil), "uber.cadence.history.v1.ReplicateEventsV2Request") proto.RegisterType((*ReplicateEventsV2Response)(nil), "uber.cadence.history.v1.ReplicateEventsV2Response") proto.RegisterType((*SyncShardStatusRequest)(nil), "uber.cadence.history.v1.SyncShardStatusRequest") proto.RegisterType((*SyncShardStatusResponse)(nil), "uber.cadence.history.v1.SyncShardStatusResponse") proto.RegisterType((*SyncActivityRequest)(nil), "uber.cadence.history.v1.SyncActivityRequest") proto.RegisterType((*SyncActivityResponse)(nil), "uber.cadence.history.v1.SyncActivityResponse") proto.RegisterType((*DescribeMutableStateRequest)(nil), "uber.cadence.history.v1.DescribeMutableStateRequest") proto.RegisterType((*DescribeMutableStateResponse)(nil), "uber.cadence.history.v1.DescribeMutableStateResponse") proto.RegisterType((*DescribeHistoryHostRequest)(nil), "uber.cadence.history.v1.DescribeHistoryHostRequest") proto.RegisterType((*DescribeHistoryHostResponse)(nil), "uber.cadence.history.v1.DescribeHistoryHostResponse") proto.RegisterType((*CloseShardRequest)(nil), "uber.cadence.history.v1.CloseShardRequest") proto.RegisterType((*CloseShardResponse)(nil), "uber.cadence.history.v1.CloseShardResponse") proto.RegisterType((*RemoveTaskRequest)(nil), "uber.cadence.history.v1.RemoveTaskRequest") proto.RegisterType((*RemoveTaskResponse)(nil), "uber.cadence.history.v1.RemoveTaskResponse") proto.RegisterType((*ResetQueueRequest)(nil), "uber.cadence.history.v1.ResetQueueRequest") proto.RegisterType((*ResetQueueResponse)(nil), "uber.cadence.history.v1.ResetQueueResponse") proto.RegisterType((*DescribeQueueRequest)(nil), "uber.cadence.history.v1.DescribeQueueRequest") proto.RegisterType((*DescribeQueueResponse)(nil), "uber.cadence.history.v1.DescribeQueueResponse") proto.RegisterType((*GetReplicationMessagesRequest)(nil), "uber.cadence.history.v1.GetReplicationMessagesRequest") proto.RegisterType((*GetReplicationMessagesResponse)(nil), "uber.cadence.history.v1.GetReplicationMessagesResponse") proto.RegisterMapType((map[int32]*v11.ReplicationMessages)(nil), "uber.cadence.history.v1.GetReplicationMessagesResponse.ShardMessagesEntry") proto.RegisterType((*GetDLQReplicationMessagesRequest)(nil), "uber.cadence.history.v1.GetDLQReplicationMessagesRequest") proto.RegisterType((*GetDLQReplicationMessagesResponse)(nil), "uber.cadence.history.v1.GetDLQReplicationMessagesResponse") proto.RegisterType((*ReapplyEventsRequest)(nil), "uber.cadence.history.v1.ReapplyEventsRequest") proto.RegisterType((*ReapplyEventsResponse)(nil), "uber.cadence.history.v1.ReapplyEventsResponse") proto.RegisterType((*RefreshWorkflowTasksRequest)(nil), "uber.cadence.history.v1.RefreshWorkflowTasksRequest") proto.RegisterType((*RefreshWorkflowTasksResponse)(nil), "uber.cadence.history.v1.RefreshWorkflowTasksResponse") proto.RegisterType((*CountDLQMessagesRequest)(nil), "uber.cadence.history.v1.CountDLQMessagesRequest") proto.RegisterType((*CountDLQMessagesResponse)(nil), "uber.cadence.history.v1.CountDLQMessagesResponse") proto.RegisterType((*ReadDLQMessagesRequest)(nil), "uber.cadence.history.v1.ReadDLQMessagesRequest") proto.RegisterType((*ReadDLQMessagesResponse)(nil), "uber.cadence.history.v1.ReadDLQMessagesResponse") proto.RegisterType((*PurgeDLQMessagesRequest)(nil), "uber.cadence.history.v1.PurgeDLQMessagesRequest") proto.RegisterType((*PurgeDLQMessagesResponse)(nil), "uber.cadence.history.v1.PurgeDLQMessagesResponse") proto.RegisterType((*MergeDLQMessagesRequest)(nil), "uber.cadence.history.v1.MergeDLQMessagesRequest") proto.RegisterType((*MergeDLQMessagesResponse)(nil), "uber.cadence.history.v1.MergeDLQMessagesResponse") proto.RegisterType((*NotifyFailoverMarkersRequest)(nil), "uber.cadence.history.v1.NotifyFailoverMarkersRequest") proto.RegisterType((*NotifyFailoverMarkersResponse)(nil), "uber.cadence.history.v1.NotifyFailoverMarkersResponse") proto.RegisterType((*GetCrossClusterTasksRequest)(nil), "uber.cadence.history.v1.GetCrossClusterTasksRequest") proto.RegisterType((*GetCrossClusterTasksResponse)(nil), "uber.cadence.history.v1.GetCrossClusterTasksResponse") proto.RegisterMapType((map[int32]v11.GetTaskFailedCause)(nil), "uber.cadence.history.v1.GetCrossClusterTasksResponse.FailedCauseByShardEntry") proto.RegisterMapType((map[int32]*v11.CrossClusterTaskRequests)(nil), "uber.cadence.history.v1.GetCrossClusterTasksResponse.TasksByShardEntry") proto.RegisterType((*RespondCrossClusterTasksCompletedRequest)(nil), "uber.cadence.history.v1.RespondCrossClusterTasksCompletedRequest") proto.RegisterType((*RespondCrossClusterTasksCompletedResponse)(nil), "uber.cadence.history.v1.RespondCrossClusterTasksCompletedResponse") proto.RegisterType((*GetFailoverInfoRequest)(nil), "uber.cadence.history.v1.GetFailoverInfoRequest") proto.RegisterType((*GetFailoverInfoResponse)(nil), "uber.cadence.history.v1.GetFailoverInfoResponse") proto.RegisterType((*RatelimitUpdateRequest)(nil), "uber.cadence.history.v1.RatelimitUpdateRequest") proto.RegisterType((*RatelimitUpdateResponse)(nil), "uber.cadence.history.v1.RatelimitUpdateResponse") } func init() { proto.RegisterFile("uber/cadence/history/v1/service.proto", fileDescriptor_fee8ff76963a38ed) } var fileDescriptor_fee8ff76963a38ed = []byte{ // 4981 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7c, 0x4d, 0x6c, 0x1c, 0x47, 0x76, 0x30, 0x7a, 0x46, 0xfc, 0x7b, 0x24, 0x87, 0x64, 0x89, 0x3f, 0xc3, 0xa1, 0x44, 0x91, 0x6d, 0xcb, 0xa2, 0xe5, 0xf5, 0xd0, 0xa2, 0x6c, 0x49, 0x96, 0xe5, 0xd5, 0x4a, 0xa4, 0x24, 0x8f, 0x3f, 0xfd, 0x36, 0x69, 0xf9, 0xcb, 0x9f, 0x7b, 0x9b, 0xd3, 0x35, 0x64, 0x47, 0x3d, 0xdd, 0xa3, 0xee, 0x1e, 0x52, 0xf4, 0x21, 0x70, 0xe2, 0x20, 0x40, 0x16, 0x41, 0x76, 0xb3, 0x48, 0x82, 0x00, 0x01, 0x02, 0x04, 0x1b, 0x60, 0xb1, 0x46, 0x6e, 0x09, 0x90, 0x43, 0x92, 0x53, 0x2e, 0x7b, 0xdc, 0x6b, 0x6e, 0x81, 0xb1, 0x7b, 0x48, 0x80, 0xdc, 0xf6, 0x1c, 0x04, 0xf5, 0xd3, 0x3d, 0xfd, 0x53, 0x5d, 0x3d, 0x43, 0x06, 0x91, 0xd7, 0xf1, 0x6d, 0xba, 0xaa, 0xde, 0xab, 0x57, 0xaf, 0xde, 0x7b, 0xfd, 0xfe, 0x7a, 0xe0, 0x7c, 0x77, 0x17, 0x7b, 0xeb, 0x4d, 0xc3, 0xc4, 0x4e, 0x13, 0xaf, 0xef, 0x5b, 0x7e, 0xe0, 0x7a, 0x47, 0xeb, 0x07, 0x97, 0xd6, 0x7d, 0xec, 0x1d, 0x58, 0x4d, 0x5c, 0xef, 0x78, 0x6e, 0xe0, 0xa2, 0x05, 0xb2, 0xac, 0xce, 0x97, 0xd5, 0xf9, 0xb2, 0xfa, 0xc1, 0xa5, 0xda, 0xf2, 0x9e, 0xeb, 0xee, 0xd9, 0x78, 0x9d, 0x2e, 0xdb, 0xed, 0xb6, 0xd6, 0xcd, 0xae, 0x67, 0x04, 0x96, 0xeb, 0x30, 0xc0, 0xda, 0xb9, 0xf4, 0x7c, 0x60, 0xb5, 0xb1, 0x1f, 0x18, 0xed, 0x0e, 0x5f, 0x90, 0x41, 0x70, 0xe8, 0x19, 0x9d, 0x0e, 0xf6, 0x7c, 0x3e, 0xbf, 0x92, 0x20, 0xd0, 0xe8, 0x58, 0x84, 0xb8, 0xa6, 0xdb, 0x6e, 0x47, 0x5b, 0xac, 0x8a, 0x56, 0x84, 0x24, 0x72, 0x2a, 0x44, 0x4b, 0x9e, 0x77, 0x71, 0xb4, 0x40, 0x15, 0x2d, 0x08, 0x0c, 0xff, 0x99, 0x6d, 0xf9, 0x81, 0x6c, 0xcd, 0xa1, 0xeb, 0x3d, 0x6b, 0xd9, 0xee, 0x21, 0x5f, 0x73, 0x51, 0xb4, 0x86, 0xb3, 0x52, 0x4f, 0xad, 0x5d, 0x2b, 0x5a, 0x8b, 0x3d, 0xbe, 0xf2, 0x95, 0xe4, 0x4a, 0xb3, 0x6d, 0x39, 0x94, 0x0b, 0x76, 0xd7, 0x0f, 0x8a, 0x16, 0x25, 0x19, 0xb1, 0x2a, 0x5e, 0xf4, 0xbc, 0x8b, 0xbb, 0xfc, 0xaa, 0x6b, 0x17, 0xc4, 0x4b, 0x3c, 0xdc, 0xb1, 0xad, 0x66, 0xfc, 0x6a, 0x93, 0x37, 0xe3, 0xef, 0x1b, 0x1e, 0x36, 0xc9, 0x4a, 0xc3, 0x09, 0x77, 0x7b, 0x35, 0x67, 0x45, 0x92, 0xa6, 0xf3, 0x39, 0xab, 0x92, 0xec, 0x52, 0x7f, 0x3e, 0x0c, 0x67, 0xb7, 0x03, 0xc3, 0x0b, 0x3e, 0xe6, 0xe3, 0x77, 0x5e, 0xe0, 0x66, 0x97, 0xd0, 0xa3, 0xe1, 0xe7, 0x5d, 0xec, 0x07, 0xe8, 0x3e, 0x8c, 0x78, 0xec, 0x67, 0x55, 0x59, 0x51, 0xd6, 0xc6, 0x37, 0x36, 0xea, 0x09, 0xb1, 0x35, 0x3a, 0x56, 0xfd, 0xe0, 0x52, 0x5d, 0x8a, 0x44, 0x0b, 0x51, 0xa0, 0x25, 0x18, 0x33, 0xdd, 0xb6, 0x61, 0x39, 0xba, 0x65, 0x56, 0x4b, 0x2b, 0xca, 0xda, 0x98, 0x36, 0xca, 0x06, 0x1a, 0x26, 0xfa, 0x4d, 0x98, 0xeb, 0x18, 0x1e, 0x76, 0x02, 0x1d, 0x87, 0x08, 0x74, 0xcb, 0x69, 0xb9, 0xd5, 0x32, 0xdd, 0x78, 0x4d, 0xb8, 0xf1, 0x63, 0x0a, 0x11, 0xed, 0xd8, 0x70, 0x5a, 0xae, 0x76, 0xba, 0x93, 0x1d, 0x44, 0x55, 0x18, 0x31, 0x82, 0x00, 0xb7, 0x3b, 0x41, 0xf5, 0xd4, 0x8a, 0xb2, 0x36, 0xa4, 0x85, 0x8f, 0x68, 0x13, 0xa6, 0xf0, 0x8b, 0x8e, 0xc5, 0x54, 0x4c, 0x27, 0xba, 0x54, 0x1d, 0xa2, 0x3b, 0xd6, 0xea, 0x4c, 0x8f, 0xea, 0xa1, 0x1e, 0xd5, 0x77, 0x42, 0x45, 0xd3, 0x2a, 0x3d, 0x10, 0x32, 0x88, 0x5a, 0xb0, 0xd8, 0x74, 0x9d, 0xc0, 0x72, 0xba, 0x58, 0x37, 0x7c, 0xdd, 0xc1, 0x87, 0xba, 0xe5, 0x58, 0x81, 0x65, 0x04, 0xae, 0x57, 0x1d, 0x5e, 0x51, 0xd6, 0x2a, 0x1b, 0x6f, 0x08, 0x0f, 0xb0, 0xc9, 0xa1, 0x6e, 0xf9, 0x0f, 0xf1, 0x61, 0x23, 0x04, 0xd1, 0xe6, 0x9b, 0xc2, 0x71, 0xd4, 0x80, 0x99, 0x70, 0xc6, 0xd4, 0x5b, 0x86, 0x65, 0x77, 0x3d, 0x5c, 0x1d, 0xa1, 0xe4, 0x9e, 0x11, 0xe2, 0xbf, 0xcb, 0xd6, 0x68, 0xd3, 0x11, 0x18, 0x1f, 0x41, 0x1a, 0xcc, 0xdb, 0x86, 0x1f, 0xe8, 0x4d, 0xb7, 0xdd, 0xb1, 0x31, 0x3d, 0xbc, 0x87, 0xfd, 0xae, 0x1d, 0x54, 0x47, 0x25, 0xf8, 0x1e, 0x1b, 0x47, 0xb6, 0x6b, 0x98, 0xda, 0x2c, 0x81, 0xdd, 0x8c, 0x40, 0x35, 0x0a, 0x89, 0xfe, 0x3f, 0x2c, 0xb5, 0x2c, 0xcf, 0x0f, 0x74, 0x13, 0x37, 0x2d, 0x9f, 0xf2, 0xd3, 0xf0, 0x9f, 0xe9, 0xbb, 0x46, 0xf3, 0x99, 0xdb, 0x6a, 0x55, 0xc7, 0x28, 0xe2, 0xc5, 0x0c, 0x5f, 0xb7, 0xb8, 0x81, 0xd3, 0xaa, 0x14, 0x7a, 0x8b, 0x03, 0xef, 0x18, 0xfe, 0xb3, 0xdb, 0x0c, 0x14, 0x1d, 0xc0, 0x74, 0xc7, 0xf0, 0x02, 0x8b, 0xd2, 0xd9, 0x74, 0x9d, 0x96, 0xb5, 0x57, 0x85, 0x95, 0xf2, 0xda, 0xf8, 0xc6, 0xff, 0xab, 0xe7, 0x18, 0x52, 0xb9, 0x54, 0x12, 0xd1, 0x61, 0xe8, 0x36, 0x29, 0xb6, 0x3b, 0x4e, 0xe0, 0x1d, 0x69, 0x53, 0x9d, 0xe4, 0x68, 0xed, 0x36, 0xcc, 0x8a, 0x16, 0xa2, 0x69, 0x28, 0x3f, 0xc3, 0x47, 0x54, 0x29, 0xc6, 0x34, 0xf2, 0x13, 0xcd, 0xc2, 0xd0, 0x81, 0x61, 0x77, 0x31, 0x17, 0x6c, 0xf6, 0x70, 0xbd, 0x74, 0x4d, 0x51, 0xaf, 0xc2, 0x72, 0x1e, 0x29, 0x7e, 0xc7, 0x75, 0x7c, 0x8c, 0xe6, 0x60, 0xd8, 0xeb, 0x52, 0xad, 0x60, 0x08, 0x87, 0xbc, 0xae, 0xd3, 0x30, 0xd5, 0xbf, 0x29, 0xc1, 0xf2, 0xb6, 0xb5, 0xe7, 0x18, 0x76, 0xae, 0x82, 0x3e, 0x48, 0x2b, 0xe8, 0x65, 0xb1, 0x82, 0x4a, 0xb1, 0xf4, 0xa9, 0xa1, 0x2d, 0x58, 0xc2, 0x2f, 0x02, 0xec, 0x39, 0x86, 0x1d, 0x19, 0xde, 0x9e, 0xb2, 0x72, 0x3d, 0x7d, 0x4d, 0xb8, 0x7f, 0x76, 0xe7, 0xc5, 0x10, 0x55, 0x66, 0x0a, 0xd5, 0xe1, 0x74, 0x73, 0xdf, 0xb2, 0xcd, 0xde, 0x26, 0xae, 0x63, 0x1f, 0x51, 0xbd, 0x1d, 0xd5, 0x66, 0xe8, 0x54, 0x08, 0xf4, 0xc8, 0xb1, 0x8f, 0xd4, 0x55, 0x38, 0x97, 0x7b, 0x3e, 0xc6, 0x60, 0xf5, 0x17, 0x25, 0xb8, 0xc0, 0xd7, 0x58, 0xc1, 0xbe, 0xdc, 0xe6, 0x3d, 0x4d, 0xb3, 0xf4, 0x86, 0x8c, 0xa5, 0x45, 0xe8, 0xfa, 0xe4, 0xed, 0x67, 0x8a, 0x40, 0xc0, 0xcb, 0x54, 0xc0, 0x3f, 0xca, 0x17, 0xf0, 0xfe, 0x48, 0xf8, 0x5f, 0x14, 0xf5, 0x5b, 0xb0, 0x56, 0x4c, 0x94, 0x5c, 0xe8, 0xbf, 0xa7, 0xc0, 0x59, 0x0d, 0xfb, 0xf8, 0xc4, 0x2f, 0x25, 0x29, 0x92, 0xfe, 0xae, 0x85, 0xa8, 0x6e, 0x1e, 0x1a, 0xf9, 0x29, 0xbe, 0x28, 0xc1, 0xea, 0x0e, 0xf6, 0xda, 0x96, 0x63, 0x04, 0x38, 0xf7, 0x24, 0x8f, 0xd3, 0x27, 0xb9, 0x22, 0x3c, 0x49, 0x21, 0xa2, 0x5f, 0x71, 0x05, 0x7e, 0x15, 0x54, 0xd9, 0x11, 0xb9, 0x0e, 0xff, 0x40, 0x81, 0x95, 0x2d, 0xec, 0x37, 0x3d, 0x6b, 0x37, 0x9f, 0xa3, 0x8f, 0xd2, 0x1c, 0x7d, 0x47, 0x78, 0x9c, 0x22, 0x3c, 0x7d, 0x8a, 0xc7, 0x7f, 0x95, 0x61, 0x55, 0x82, 0x8a, 0x8b, 0x88, 0x0d, 0x0b, 0x3d, 0x97, 0x86, 0xa9, 0x36, 0x7f, 0xe1, 0x49, 0x6d, 0x76, 0x06, 0xe1, 0x66, 0x1c, 0x54, 0x9b, 0xc7, 0xc2, 0x71, 0xb4, 0x0b, 0x0b, 0xd9, 0xbb, 0x65, 0x9e, 0x54, 0x89, 0xee, 0x76, 0xb1, 0xbf, 0xdd, 0xa8, 0x2f, 0x35, 0x77, 0x28, 0x1a, 0x46, 0x1f, 0x03, 0xea, 0x60, 0xc7, 0xb4, 0x9c, 0x3d, 0xdd, 0x68, 0x06, 0xd6, 0x81, 0x15, 0x58, 0xd8, 0xe7, 0xe6, 0x2a, 0xc7, 0x51, 0x63, 0xcb, 0x6f, 0xb1, 0xd5, 0x47, 0x14, 0xf9, 0x4c, 0x27, 0x31, 0x68, 0x61, 0x1f, 0xfd, 0x1a, 0x4c, 0x87, 0x88, 0xa9, 0x98, 0x78, 0xd8, 0xa9, 0x9e, 0xa2, 0x68, 0xeb, 0x32, 0xb4, 0x9b, 0x64, 0x6d, 0x92, 0xf2, 0xa9, 0x4e, 0x6c, 0xca, 0xc3, 0x0e, 0xda, 0xee, 0xa1, 0x0e, 0xbd, 0x13, 0xee, 0xe8, 0x49, 0x29, 0x0e, 0x9d, 0x91, 0x04, 0xd2, 0x70, 0x50, 0x7d, 0x01, 0xb3, 0x4f, 0x48, 0xcc, 0x13, 0x72, 0x2f, 0x14, 0xc3, 0xcd, 0xb4, 0x18, 0xbe, 0x2e, 0xdc, 0x43, 0x04, 0xdb, 0xa7, 0xe8, 0xfd, 0x48, 0x81, 0xb9, 0x14, 0x38, 0x17, 0xb7, 0x9b, 0x30, 0x41, 0xe3, 0xb0, 0xd0, 0x9d, 0x53, 0xfa, 0x70, 0xe7, 0xc6, 0x29, 0x04, 0xf7, 0xe2, 0x1a, 0x50, 0x09, 0x11, 0xfc, 0x36, 0x6e, 0x06, 0xd8, 0xe4, 0x82, 0xa3, 0xe6, 0x9f, 0x41, 0xe3, 0x2b, 0xb5, 0xc9, 0xe7, 0xf1, 0x47, 0xf5, 0xf7, 0x15, 0xa8, 0x51, 0x03, 0xba, 0x1d, 0x58, 0xcd, 0x67, 0x47, 0xc4, 0xa3, 0xbb, 0x6f, 0xf9, 0x41, 0xc8, 0xa6, 0x46, 0x9a, 0x4d, 0xeb, 0xf9, 0x96, 0x5c, 0x88, 0xa1, 0x4f, 0x66, 0x9d, 0x85, 0x25, 0x21, 0x0e, 0x6e, 0x59, 0x7e, 0x56, 0x82, 0xf9, 0x7b, 0x38, 0x78, 0xd0, 0x0d, 0x8c, 0x5d, 0x1b, 0x6f, 0x07, 0x46, 0x80, 0x35, 0x11, 0x5a, 0x25, 0x65, 0x4f, 0x3f, 0x02, 0x24, 0x30, 0xa3, 0xa5, 0x81, 0xcc, 0xe8, 0x4c, 0x46, 0xc3, 0xd0, 0x65, 0x98, 0xc7, 0x2f, 0x3a, 0x94, 0x81, 0xba, 0x83, 0x5f, 0x04, 0x3a, 0x3e, 0x20, 0x61, 0x91, 0x65, 0x52, 0x0b, 0x5d, 0xd6, 0x4e, 0x87, 0xb3, 0x0f, 0xf1, 0x8b, 0xe0, 0x0e, 0x99, 0x6b, 0x98, 0xe8, 0x2d, 0x98, 0x6d, 0x76, 0x3d, 0x1a, 0x3f, 0xed, 0x7a, 0x86, 0xd3, 0xdc, 0xd7, 0x03, 0xf7, 0x19, 0xd5, 0x1e, 0x65, 0x6d, 0x42, 0x43, 0x7c, 0xee, 0x36, 0x9d, 0xda, 0x21, 0x33, 0xe8, 0x37, 0x60, 0xf6, 0x00, 0x7b, 0xd4, 0x4b, 0xe7, 0x3e, 0x85, 0x6e, 0x05, 0xb8, 0xcd, 0x95, 0x22, 0x2d, 0xb0, 0x24, 0x68, 0x25, 0x27, 0x78, 0xca, 0x40, 0x3e, 0x60, 0x10, 0x8d, 0x00, 0xb7, 0x35, 0x74, 0x90, 0x19, 0x53, 0xff, 0x61, 0x0c, 0x16, 0x32, 0x2c, 0xe5, 0x02, 0x2a, 0x66, 0x9b, 0x72, 0x52, 0xb6, 0xdd, 0x85, 0xc9, 0x08, 0x6d, 0x70, 0xd4, 0xc1, 0xfc, 0x22, 0x56, 0xa5, 0x18, 0x77, 0x8e, 0x3a, 0x58, 0x9b, 0x38, 0x8c, 0x3d, 0x21, 0x15, 0x26, 0x45, 0x5c, 0x1f, 0x77, 0x62, 0xdc, 0x7e, 0x0a, 0x8b, 0x1d, 0x0f, 0x1f, 0x58, 0x6e, 0xd7, 0xd7, 0x7d, 0xe2, 0xe6, 0x60, 0xb3, 0xb7, 0xfe, 0x14, 0xdd, 0x77, 0x29, 0x13, 0xe6, 0x34, 0x9c, 0xe0, 0xca, 0xdb, 0x4f, 0x89, 0xaf, 0xa4, 0xcd, 0x87, 0xd0, 0xdb, 0x0c, 0x38, 0xc4, 0xfb, 0x26, 0x9c, 0xa6, 0x41, 0x19, 0x8b, 0xa2, 0x22, 0x8c, 0x43, 0x94, 0x82, 0x69, 0x32, 0x75, 0x97, 0xcc, 0x84, 0xcb, 0xaf, 0xc3, 0x18, 0x0d, 0xb0, 0x6c, 0xcb, 0x0f, 0x68, 0x98, 0x39, 0xbe, 0x71, 0x56, 0xec, 0x41, 0x84, 0x22, 0x3f, 0x1a, 0xf0, 0x5f, 0xe8, 0x1e, 0x4c, 0xfb, 0x54, 0x1d, 0xf4, 0x1e, 0x8a, 0x91, 0x7e, 0x50, 0x54, 0xfc, 0x84, 0x16, 0xa1, 0xb7, 0x61, 0xbe, 0x69, 0x5b, 0x84, 0x52, 0xdb, 0xda, 0xf5, 0x0c, 0xef, 0x48, 0xe7, 0xf2, 0x40, 0x03, 0xc9, 0x31, 0x6d, 0x96, 0xcd, 0xde, 0x67, 0x93, 0x5c, 0x7e, 0x62, 0x50, 0x2d, 0x6c, 0x04, 0x5d, 0x0f, 0x47, 0x50, 0x63, 0x71, 0xa8, 0xbb, 0x6c, 0x32, 0x84, 0x3a, 0x07, 0xe3, 0x1c, 0xca, 0x6a, 0x77, 0xec, 0x2a, 0xd0, 0xa5, 0xc0, 0x86, 0x1a, 0xed, 0x8e, 0x8d, 0x7c, 0xb8, 0x98, 0x3e, 0x95, 0xee, 0x37, 0xf7, 0xb1, 0xd9, 0xb5, 0xb1, 0x1e, 0xb8, 0xec, 0xb2, 0x68, 0x94, 0xef, 0x76, 0x83, 0xea, 0x78, 0x51, 0x40, 0xfa, 0x6a, 0xf2, 0xac, 0xdb, 0x1c, 0xd3, 0x8e, 0x4b, 0xef, 0x6d, 0x87, 0xa1, 0x21, 0xfe, 0x0e, 0xbb, 0x2a, 0x22, 0xff, 0xbd, 0x83, 0x4c, 0xd0, 0x44, 0xc3, 0x0c, 0x9d, 0xda, 0x26, 0x33, 0xe1, 0x29, 0xf2, 0x74, 0x75, 0x32, 0x57, 0x57, 0xef, 0x43, 0x25, 0x92, 0x6d, 0x9f, 0x28, 0x53, 0xb5, 0x42, 0x93, 0x0a, 0xe7, 0x93, 0x57, 0xc5, 0x32, 0x3d, 0x71, 0xf9, 0x66, 0x9a, 0x17, 0x29, 0x06, 0x7d, 0x44, 0x4d, 0x98, 0x8d, 0xb0, 0x35, 0x6d, 0xd7, 0xc7, 0x1c, 0xe7, 0x14, 0xc5, 0x79, 0xa9, 0x4f, 0x6f, 0x84, 0x00, 0x12, 0x7c, 0x5d, 0x5f, 0x8b, 0xf4, 0x39, 0x1a, 0x24, 0x5a, 0x3e, 0x93, 0x34, 0x2f, 0xc4, 0x45, 0x98, 0x16, 0xbd, 0x70, 0x7b, 0x54, 0x27, 0x8c, 0x8b, 0x85, 0x7d, 0x6d, 0xfa, 0x20, 0x35, 0x82, 0x6e, 0xc0, 0x92, 0x45, 0x74, 0x2e, 0x75, 0xc7, 0xd8, 0x21, 0x76, 0xc6, 0xac, 0xce, 0x50, 0x1f, 0x73, 0xc1, 0xf2, 0x93, 0xa6, 0xfe, 0x0e, 0x9b, 0x46, 0xab, 0x30, 0x11, 0xda, 0x3a, 0xdf, 0xfa, 0x14, 0x57, 0x11, 0x53, 0x6d, 0x3e, 0xb6, 0x6d, 0x7d, 0x8a, 0xd5, 0x5f, 0x2a, 0xb0, 0xf0, 0xd8, 0xb5, 0xed, 0xff, 0x5b, 0x6f, 0x03, 0xf5, 0xc7, 0xa3, 0x50, 0xcd, 0x1e, 0xfb, 0x1b, 0x8b, 0xfd, 0x8d, 0xc5, 0xfe, 0x3a, 0x5a, 0xec, 0x3c, 0xfd, 0x98, 0xc8, 0xb5, 0xc0, 0x42, 0x73, 0x36, 0x79, 0x62, 0x73, 0xf6, 0xab, 0x67, 0xd8, 0xd5, 0x7f, 0x29, 0xc1, 0x8a, 0x86, 0x9b, 0xae, 0x67, 0xc6, 0x13, 0xb5, 0x5c, 0x2d, 0x5e, 0xa6, 0xa5, 0x3c, 0x07, 0xe3, 0x91, 0xe0, 0x44, 0x46, 0x00, 0xc2, 0xa1, 0x86, 0x89, 0x16, 0x60, 0x84, 0xca, 0x18, 0xd7, 0xf8, 0xb2, 0x36, 0x4c, 0x1e, 0x1b, 0x26, 0x3a, 0x0b, 0xc0, 0xe3, 0x88, 0x50, 0x77, 0xc7, 0xb4, 0x31, 0x3e, 0xd2, 0x30, 0x91, 0x06, 0x13, 0x1d, 0xd7, 0xb6, 0xf5, 0x30, 0x56, 0x19, 0x96, 0xc4, 0x2a, 0xc4, 0x86, 0xde, 0x75, 0xbd, 0x38, 0x6b, 0xc2, 0x58, 0x65, 0x9c, 0x20, 0xe1, 0x0f, 0xea, 0xef, 0x8d, 0xc2, 0xaa, 0x84, 0x8b, 0xdc, 0xf0, 0x66, 0x2c, 0xa4, 0x72, 0x3c, 0x0b, 0x29, 0xb5, 0x7e, 0xa5, 0xe3, 0x5b, 0xbf, 0x6f, 0x01, 0x0a, 0xf9, 0x6b, 0xa6, 0xcd, 0xef, 0x74, 0x34, 0x13, 0xae, 0x5e, 0x23, 0x06, 0x4c, 0x60, 0x7a, 0xcb, 0xc4, 0x42, 0x25, 0xf0, 0x66, 0x2c, 0xfa, 0x50, 0xd6, 0xa2, 0xc7, 0x4a, 0x3a, 0xc3, 0xc9, 0x92, 0xce, 0x35, 0xa8, 0x72, 0x93, 0xd2, 0x4b, 0x80, 0x84, 0x0e, 0xc2, 0x08, 0x75, 0x10, 0xe6, 0xd9, 0x7c, 0x24, 0x3b, 0xa1, 0x7f, 0xa0, 0xc1, 0x64, 0x54, 0xba, 0xa0, 0x29, 0x13, 0x56, 0x0b, 0x79, 0x33, 0x4f, 0x1b, 0x77, 0x3c, 0xc3, 0xf1, 0x89, 0x29, 0x4b, 0xa4, 0x09, 0x26, 0xcc, 0xd8, 0x13, 0xfa, 0x04, 0xce, 0x08, 0x12, 0x32, 0x3d, 0x13, 0x3e, 0xd6, 0x8f, 0x09, 0x5f, 0xcc, 0x88, 0x7b, 0x64, 0xcd, 0x73, 0xbc, 0x4f, 0xc8, 0xf3, 0x3e, 0x57, 0x61, 0x22, 0x61, 0xf3, 0xc6, 0xa9, 0xcd, 0x1b, 0xdf, 0x8d, 0x19, 0xbb, 0x5b, 0x50, 0xe9, 0x5d, 0x2b, 0x2d, 0x89, 0x4d, 0x14, 0x96, 0xc4, 0x26, 0x23, 0x08, 0x5a, 0x11, 0x7b, 0x1f, 0x26, 0xc2, 0xbb, 0xa6, 0x08, 0x26, 0x0b, 0x11, 0x8c, 0xf3, 0xf5, 0x14, 0xdc, 0x80, 0x91, 0xe7, 0x5d, 0x4c, 0x8d, 0x6c, 0x85, 0xe6, 0x7f, 0xee, 0xe5, 0x66, 0xc1, 0x0b, 0xb5, 0x88, 0xa6, 0x28, 0x2c, 0xec, 0xb3, 0xbc, 0x77, 0x88, 0x37, 0xe3, 0x0b, 0x4e, 0x65, 0x7c, 0xc1, 0xda, 0x27, 0x30, 0x11, 0x87, 0x15, 0xa4, 0xc2, 0xaf, 0xc5, 0x53, 0xe1, 0x79, 0x29, 0x92, 0x50, 0x31, 0x59, 0xaa, 0x24, 0x96, 0x2e, 0xef, 0x99, 0xd2, 0x30, 0x31, 0xf6, 0x8d, 0x29, 0xcd, 0x98, 0xd2, 0x38, 0x6b, 0x84, 0xa6, 0xf4, 0xe7, 0xe5, 0xd0, 0x94, 0x0a, 0xb9, 0xc8, 0x4d, 0xe9, 0x87, 0x30, 0x95, 0x32, 0x55, 0x52, 0x63, 0xca, 0x93, 0x19, 0xd4, 0xd8, 0x68, 0x95, 0xa4, 0x29, 0xcb, 0x08, 0x77, 0x69, 0x30, 0xe1, 0x8e, 0x59, 0xae, 0x72, 0xd2, 0x72, 0x7d, 0x02, 0xcb, 0x49, 0xc5, 0xd3, 0xdd, 0x96, 0x1e, 0xec, 0x5b, 0xbe, 0x1e, 0xaf, 0x5e, 0xcb, 0xb7, 0xaa, 0x25, 0x14, 0xf1, 0x51, 0x6b, 0x67, 0xdf, 0xf2, 0x6f, 0x71, 0xfc, 0x0d, 0x98, 0xd9, 0xc7, 0x86, 0x17, 0xec, 0x62, 0x23, 0xd0, 0x4d, 0x1c, 0x18, 0x96, 0xed, 0xf3, 0x84, 0x8f, 0x3c, 0x41, 0x38, 0x1d, 0x81, 0x6d, 0x31, 0xa8, 0xec, 0xab, 0x69, 0xf8, 0x78, 0xaf, 0xa6, 0x0b, 0x30, 0x15, 0xe1, 0x61, 0x62, 0x4d, 0x6d, 0xf4, 0x98, 0x16, 0x39, 0x46, 0x5b, 0x74, 0x54, 0xfd, 0x73, 0x05, 0x5e, 0x61, 0xb7, 0x99, 0x50, 0x76, 0x5e, 0x84, 0xee, 0xe9, 0x8b, 0x96, 0x4e, 0x2a, 0x5e, 0xcb, 0x4b, 0x2a, 0x16, 0xa1, 0xea, 0x33, 0xbb, 0xf8, 0x77, 0x65, 0x78, 0x55, 0x8e, 0x8d, 0x8b, 0x20, 0xee, 0xbd, 0xff, 0x3c, 0x3e, 0xc6, 0x49, 0xbc, 0x7e, 0x7c, 0xeb, 0xa6, 0x4d, 0xf9, 0x29, 0x49, 0xff, 0x91, 0x02, 0xcb, 0xbd, 0xb4, 0x3c, 0xf1, 0xa1, 0x4d, 0xcb, 0xef, 0x18, 0x41, 0x73, 0x5f, 0xb7, 0xdd, 0xa6, 0x61, 0xdb, 0x47, 0xd5, 0x12, 0xb5, 0xa9, 0x9f, 0x48, 0x76, 0x2d, 0x3e, 0x4e, 0xbd, 0x97, 0xb7, 0xdf, 0x71, 0xb7, 0xf8, 0x0e, 0xf7, 0xd9, 0x06, 0xcc, 0xd4, 0x2e, 0x19, 0xf9, 0x2b, 0x6a, 0xbf, 0x03, 0x2b, 0x45, 0x08, 0x04, 0xf6, 0x76, 0x2b, 0x69, 0x6f, 0xc5, 0x55, 0x81, 0xd0, 0x0c, 0x50, 0x5c, 0x21, 0x62, 0xfa, 0x66, 0x8e, 0xd9, 0xde, 0x1f, 0x28, 0xc4, 0xf6, 0x66, 0x8e, 0x79, 0xd7, 0xb0, 0xec, 0x9e, 0x2c, 0xf5, 0x59, 0x4e, 0x2a, 0xc2, 0xd3, 0xa7, 0x20, 0xbd, 0x42, 0xec, 0x58, 0x2e, 0x26, 0x9e, 0xac, 0xfe, 0x53, 0x05, 0xd4, 0xac, 0xb5, 0xfb, 0x20, 0x54, 0xcf, 0x90, 0xf2, 0x27, 0x69, 0xca, 0xaf, 0xe6, 0x50, 0x5e, 0x84, 0xa9, 0x4f, 0xda, 0x1f, 0x13, 0xe5, 0x94, 0xe0, 0xe2, 0xb2, 0xf9, 0x3a, 0x4c, 0x37, 0x0d, 0xa7, 0x89, 0xa3, 0x37, 0x00, 0x66, 0xef, 0xb4, 0x51, 0x6d, 0x8a, 0x8d, 0x6b, 0xe1, 0x70, 0x5c, 0xdf, 0xe3, 0x38, 0x4f, 0xa8, 0xef, 0x32, 0x54, 0x7d, 0x1e, 0xf5, 0xb5, 0x48, 0xdd, 0x73, 0x90, 0xc5, 0x0a, 0x96, 0x82, 0x85, 0x27, 0x91, 0xb0, 0x5c, 0x3c, 0x03, 0x4b, 0x98, 0x08, 0x53, 0x42, 0xc2, 0xb2, 0x07, 0xa4, 0xf7, 0xd3, 0xa3, 0xbc, 0x6f, 0x09, 0x2b, 0xc2, 0xd4, 0x27, 0xed, 0xe7, 0xc5, 0xe2, 0x10, 0xe1, 0xe2, 0xd4, 0xff, 0xbd, 0x02, 0xe7, 0x34, 0xdc, 0x76, 0x0f, 0x30, 0xeb, 0x44, 0xf8, 0xaa, 0xe4, 0xf1, 0x92, 0x8e, 0x51, 0x39, 0xe5, 0x18, 0xa9, 0x2a, 0x91, 0x95, 0x3c, 0xaa, 0xf9, 0xd1, 0xfe, 0xb1, 0x04, 0xe7, 0xf9, 0x11, 0xd8, 0xb1, 0x73, 0xcb, 0xe0, 0xd2, 0x03, 0x1a, 0x50, 0x49, 0xea, 0x20, 0x3f, 0xdc, 0xf5, 0x9c, 0xfb, 0xeb, 0x63, 0x43, 0x6d, 0x32, 0xa1, 0xbd, 0x68, 0x17, 0x16, 0xa2, 0x4e, 0x03, 0x61, 0x3b, 0x9f, 0xb8, 0x08, 0x7d, 0x87, 0xc3, 0xa4, 0x8a, 0xd0, 0x58, 0x34, 0x3c, 0x70, 0x97, 0xc1, 0x1a, 0xbc, 0x56, 0x74, 0x16, 0xce, 0xe7, 0x7f, 0x56, 0x60, 0x29, 0x4c, 0x1c, 0x09, 0x02, 0xf9, 0x97, 0x22, 0x3e, 0x17, 0x61, 0xc6, 0xf2, 0xf5, 0x64, 0x77, 0x1d, 0xe5, 0xe5, 0xa8, 0x36, 0x65, 0xf9, 0x77, 0xe3, 0x7d, 0x73, 0xea, 0x32, 0x9c, 0x11, 0x93, 0xcf, 0xcf, 0xf7, 0x39, 0x75, 0x58, 0x88, 0xb1, 0x4e, 0x16, 0xce, 0x33, 0xa6, 0xf5, 0x65, 0x1c, 0x74, 0x15, 0x26, 0x78, 0xeb, 0x24, 0x36, 0x63, 0xb9, 0xdc, 0x68, 0xac, 0x61, 0xa2, 0x8f, 0xe1, 0x74, 0x33, 0x24, 0x35, 0xb6, 0xf5, 0xa9, 0x81, 0xb6, 0x46, 0x11, 0x8a, 0xde, 0xde, 0xf7, 0x61, 0x3a, 0xd6, 0x0e, 0xc9, 0x82, 0x84, 0xa1, 0x7e, 0x83, 0x84, 0xa9, 0x1e, 0x28, 0x8b, 0x12, 0xce, 0x02, 0x84, 0xee, 0x9e, 0x65, 0x52, 0xf7, 0xb8, 0xac, 0x8d, 0xf1, 0x91, 0x86, 0xa9, 0x5e, 0x20, 0xca, 0x2c, 0xbd, 0x04, 0x7e, 0x5d, 0xff, 0x5e, 0x82, 0xaa, 0xc6, 0x7b, 0x85, 0x31, 0x45, 0xed, 0x3f, 0xdd, 0x78, 0x99, 0x57, 0xf4, 0x5b, 0x30, 0x27, 0xaa, 0x1c, 0x87, 0x1d, 0x20, 0x03, 0x94, 0x8e, 0x4f, 0x67, 0x4b, 0xc7, 0x3e, 0x7a, 0x07, 0x86, 0x29, 0xeb, 0x7d, 0x7e, 0xa3, 0xe2, 0xd4, 0xc8, 0x96, 0x11, 0x18, 0xb7, 0x6d, 0x77, 0x57, 0xe3, 0x8b, 0xd1, 0x26, 0x54, 0x1c, 0x7c, 0xa8, 0x7b, 0x5d, 0x7e, 0x73, 0x61, 0x60, 0x53, 0x00, 0x3e, 0xe1, 0xe0, 0x43, 0xad, 0xcb, 0xae, 0xcc, 0x57, 0x97, 0x60, 0x51, 0xc0, 0x6a, 0x7e, 0x11, 0xdf, 0x53, 0x60, 0x7e, 0xfb, 0xc8, 0x69, 0x6e, 0xef, 0x1b, 0x9e, 0xc9, 0x33, 0xa4, 0xfc, 0x1a, 0xce, 0x43, 0xc5, 0x77, 0xbb, 0x5e, 0x13, 0xeb, 0xbc, 0x85, 0x9c, 0xdf, 0xc5, 0x24, 0x1b, 0xdd, 0x64, 0x83, 0x68, 0x11, 0x46, 0x7d, 0x02, 0x1c, 0xbe, 0xdf, 0x86, 0xb4, 0x11, 0xfa, 0xdc, 0x30, 0x51, 0x1d, 0x4e, 0xd1, 0x58, 0xb2, 0x5c, 0x18, 0xe0, 0xd1, 0x75, 0xea, 0x22, 0x2c, 0x64, 0x68, 0xe1, 0x74, 0xfe, 0x74, 0x08, 0x4e, 0x93, 0xb9, 0xf0, 0x3d, 0xf9, 0x32, 0x65, 0xa5, 0x0a, 0x23, 0x61, 0x46, 0x8a, 0x69, 0x72, 0xf8, 0x48, 0x14, 0xbd, 0x17, 0xeb, 0x46, 0x79, 0x84, 0x28, 0xef, 0x40, 0x78, 0x92, 0xcd, 0x43, 0x0d, 0x0d, 0x9a, 0x87, 0x92, 0x2b, 0x61, 0x26, 0x92, 0x1f, 0x19, 0x2c, 0x92, 0xff, 0x90, 0x57, 0x7f, 0x7a, 0x41, 0x35, 0xc5, 0x32, 0x5a, 0x88, 0x65, 0x86, 0x80, 0x45, 0xee, 0x31, 0xc5, 0x75, 0x05, 0x46, 0xc2, 0x88, 0x7c, 0xac, 0x8f, 0x88, 0x3c, 0x5c, 0x1c, 0xcf, 0x26, 0x40, 0x32, 0x9b, 0x70, 0x13, 0x26, 0x58, 0x6d, 0x8a, 0x37, 0x8a, 0x8f, 0xf7, 0xd1, 0x28, 0x3e, 0x4e, 0x4b, 0x56, 0xbc, 0x47, 0xfc, 0x2d, 0xa0, 0x7d, 0xde, 0xfc, 0xd3, 0x09, 0xdd, 0x32, 0xb1, 0x13, 0x58, 0xc1, 0x11, 0xcd, 0x06, 0x8e, 0x69, 0x88, 0xcc, 0x7d, 0x4c, 0xa7, 0x1a, 0x7c, 0x06, 0x3d, 0x84, 0xa9, 0x94, 0x69, 0xe0, 0x99, 0xbf, 0xf3, 0x7d, 0x19, 0x05, 0xad, 0x92, 0x34, 0x08, 0xea, 0x3c, 0xcc, 0x26, 0x25, 0x99, 0x8b, 0xf8, 0x9f, 0x28, 0xb0, 0x14, 0x76, 0xde, 0x7d, 0x45, 0x3c, 0x3c, 0xf5, 0x8f, 0x15, 0x38, 0x23, 0xa6, 0x89, 0x07, 0x3f, 0x97, 0x61, 0xbe, 0xcd, 0xc6, 0x59, 0x5d, 0x46, 0xb7, 0x1c, 0xbd, 0x69, 0x34, 0xf7, 0x31, 0xa7, 0xf0, 0x74, 0x3b, 0x06, 0xd5, 0x70, 0x36, 0xc9, 0x14, 0x7a, 0x17, 0x16, 0x33, 0x40, 0xa6, 0x11, 0x18, 0xbb, 0x86, 0x1f, 0x36, 0xe0, 0xce, 0x27, 0xe1, 0xb6, 0xf8, 0xac, 0x7a, 0x06, 0x6a, 0x21, 0x3d, 0x9c, 0x9f, 0x1f, 0xb8, 0x51, 0xeb, 0x94, 0xfa, 0xbb, 0xa5, 0x1e, 0x0b, 0x13, 0xd3, 0x9c, 0xda, 0x35, 0x98, 0x76, 0xba, 0xed, 0x5d, 0xec, 0xe9, 0x6e, 0x4b, 0xa7, 0x56, 0xca, 0xa7, 0x74, 0x0e, 0x69, 0x15, 0x36, 0xfe, 0xa8, 0x45, 0x8d, 0x8f, 0x4f, 0x98, 0x1d, 0x5a, 0x35, 0x9f, 0xa6, 0x16, 0x86, 0xb4, 0x51, 0x6e, 0xd6, 0x7c, 0xd4, 0x80, 0x09, 0x7e, 0x13, 0xec, 0xa8, 0xe2, 0x2e, 0xd3, 0x50, 0x1c, 0x58, 0xae, 0x87, 0x9e, 0x9c, 0xfa, 0x7e, 0xe3, 0x66, 0x6f, 0x00, 0x5d, 0x81, 0x05, 0xb6, 0x4f, 0xd3, 0x75, 0x02, 0xcf, 0xb5, 0x6d, 0xec, 0x51, 0x9e, 0x74, 0xd9, 0x9b, 0x62, 0x4c, 0x9b, 0xa3, 0xd3, 0x9b, 0xd1, 0x2c, 0xb3, 0x8b, 0x54, 0x43, 0x4c, 0xd3, 0xc3, 0xbe, 0xcf, 0x13, 0x92, 0xe1, 0xa3, 0x5a, 0x87, 0x19, 0x56, 0xd9, 0x22, 0x70, 0xa1, 0xec, 0xc4, 0x8d, 0xb4, 0x92, 0x30, 0xd2, 0xea, 0x2c, 0xa0, 0xf8, 0x7a, 0x2e, 0x8c, 0xff, 0xa9, 0xc0, 0x0c, 0x73, 0xde, 0xe3, 0x5e, 0x62, 0x3e, 0x1a, 0x74, 0x83, 0x57, 0x81, 0xa3, 0xa2, 0x77, 0x65, 0xe3, 0x5c, 0x0e, 0x43, 0x08, 0x46, 0x9a, 0x35, 0xa3, 0x75, 0x60, 0x9a, 0x31, 0x8b, 0xe5, 0x5e, 0xcb, 0x89, 0xdc, 0xeb, 0x26, 0x4c, 0x1d, 0x58, 0xbe, 0xb5, 0x6b, 0xd9, 0x56, 0x70, 0xc4, 0x2c, 0x51, 0x71, 0xba, 0xb0, 0xd2, 0x03, 0xa1, 0x66, 0x68, 0x15, 0x26, 0xf8, 0x2b, 0x4c, 0x77, 0x0c, 0x6e, 0x71, 0xc7, 0xb4, 0x71, 0x3e, 0xf6, 0xd0, 0x68, 0x63, 0xc2, 0x85, 0xf8, 0x71, 0x39, 0x17, 0xbe, 0x4f, 0xb9, 0xe0, 0xe3, 0xe0, 0x49, 0x17, 0x77, 0x71, 0x1f, 0x5c, 0x48, 0xef, 0x54, 0xca, 0xec, 0x94, 0x64, 0x54, 0x79, 0x40, 0x46, 0x31, 0x3a, 0x7b, 0x04, 0x71, 0x3a, 0x7f, 0xa8, 0xc0, 0x6c, 0x28, 0xf7, 0x5f, 0x19, 0x52, 0x1f, 0xc1, 0x5c, 0x8a, 0x26, 0xae, 0x85, 0x57, 0x60, 0xa1, 0xe3, 0xb9, 0x4d, 0xec, 0xfb, 0x96, 0xb3, 0xa7, 0xd3, 0xaf, 0xca, 0x98, 0x1d, 0x20, 0xca, 0x58, 0x26, 0x32, 0xdf, 0x9b, 0xa6, 0x90, 0xd4, 0x08, 0xf8, 0xea, 0xe7, 0x0a, 0x9c, 0xbd, 0x87, 0x03, 0xad, 0xf7, 0x8d, 0xd9, 0x03, 0xec, 0xfb, 0xc6, 0x1e, 0x8e, 0x5c, 0x96, 0x9b, 0x30, 0x4c, 0x0b, 0x40, 0x0c, 0xd1, 0xf8, 0xc6, 0x85, 0x1c, 0x6a, 0x63, 0x28, 0x68, 0x75, 0x48, 0xe3, 0x60, 0x7d, 0x30, 0x85, 0xd8, 0x98, 0xe5, 0x3c, 0x2a, 0xf8, 0x01, 0x9f, 0x43, 0x85, 0x71, 0xbd, 0xcd, 0x67, 0x38, 0x39, 0x1f, 0xe6, 0x26, 0x27, 0xe5, 0x08, 0xeb, 0x54, 0x37, 0xc3, 0x51, 0x96, 0x88, 0x9c, 0xf4, 0xe3, 0x63, 0x35, 0x1b, 0x50, 0x76, 0x51, 0x3c, 0xd9, 0x38, 0xc4, 0x92, 0x8d, 0xdf, 0x49, 0x26, 0x1b, 0x2f, 0x16, 0x33, 0x28, 0x22, 0x26, 0x96, 0x68, 0x6c, 0xc3, 0xca, 0x3d, 0x1c, 0x6c, 0xdd, 0x7f, 0x22, 0xb9, 0x8b, 0x06, 0x00, 0x53, 0x69, 0xa7, 0xe5, 0x86, 0x0c, 0xe8, 0x63, 0x3b, 0x22, 0x48, 0xd4, 0x4c, 0x52, 0xd1, 0x23, 0xbf, 0x7c, 0xf5, 0x05, 0xac, 0x4a, 0xb6, 0xe3, 0x4c, 0xdf, 0x86, 0x99, 0xd8, 0xd7, 0x87, 0xb4, 0x18, 0x19, 0x6e, 0xfb, 0x5a, 0x7f, 0xdb, 0x6a, 0xd3, 0x5e, 0x72, 0xc0, 0x57, 0xff, 0x55, 0x81, 0x59, 0x0d, 0x1b, 0x9d, 0x8e, 0xcd, 0x22, 0xa2, 0xe8, 0x74, 0xf3, 0x30, 0xcc, 0x33, 0xfb, 0xec, 0x3d, 0xc7, 0x9f, 0xe4, 0x1f, 0x2b, 0x88, 0x5f, 0xd2, 0xe5, 0x93, 0xfa, 0xa3, 0xc7, 0x0b, 0x2e, 0xd4, 0x05, 0x98, 0x4b, 0x1d, 0x8d, 0x5b, 0x93, 0x9f, 0x28, 0xb0, 0xa4, 0xe1, 0x96, 0x87, 0xfd, 0xfd, 0xa8, 0xc8, 0x41, 0xb8, 0xf1, 0x15, 0x3c, 0xbb, 0xba, 0x0c, 0x67, 0xc4, 0xa4, 0xf2, 0xb3, 0xbc, 0x0b, 0x0b, 0x9b, 0x6e, 0xd7, 0x21, 0xc2, 0x93, 0x16, 0xd0, 0x65, 0x80, 0x96, 0xeb, 0x35, 0xf1, 0x5d, 0x1c, 0x34, 0xf7, 0x79, 0xc6, 0x36, 0x36, 0xa2, 0x1a, 0x50, 0xcd, 0x82, 0x72, 0x61, 0xbb, 0x03, 0x23, 0xd8, 0x09, 0x68, 0x2d, 0x97, 0x89, 0xd8, 0x1b, 0x39, 0x22, 0xc6, 0xbd, 0x90, 0xad, 0xfb, 0x4f, 0x28, 0x2e, 0x5e, 0xaf, 0xe5, 0xb0, 0xea, 0x4f, 0x4a, 0x30, 0xaf, 0x61, 0xc3, 0x14, 0x50, 0xb7, 0x01, 0xa7, 0xa2, 0xee, 0x88, 0xca, 0xc6, 0x72, 0x9e, 0x6f, 0x71, 0xff, 0x09, 0xb5, 0xba, 0x74, 0xad, 0x2c, 0x14, 0xcb, 0x06, 0x73, 0x65, 0x51, 0x30, 0xb7, 0x03, 0x55, 0xcb, 0x21, 0x2b, 0xac, 0x03, 0xac, 0x63, 0x27, 0xb2, 0x60, 0x7d, 0x76, 0x94, 0xcd, 0x45, 0xc0, 0x77, 0x9c, 0xd0, 0x14, 0x35, 0x4c, 0x22, 0x18, 0x1d, 0x82, 0x84, 0xd6, 0xa4, 0x87, 0x28, 0x61, 0xa3, 0x64, 0x60, 0xdb, 0xfa, 0x14, 0xa3, 0xd7, 0x60, 0x8a, 0xf6, 0x45, 0xd0, 0x15, 0xac, 0x7c, 0x3f, 0x4c, 0xcb, 0xf7, 0xb4, 0x5d, 0xe2, 0xb1, 0xb1, 0x87, 0x59, 0x37, 0xdf, 0xdf, 0x96, 0x60, 0x21, 0xc3, 0x2b, 0x7e, 0x1d, 0xc7, 0x61, 0x96, 0xd0, 0x5e, 0x94, 0x4e, 0x66, 0x2f, 0xd0, 0x77, 0x61, 0x3e, 0x83, 0x34, 0xcc, 0x11, 0x0e, 0x6a, 0x00, 0x67, 0xd3, 0xd8, 0x69, 0x8a, 0x50, 0xc0, 0xae, 0x53, 0x22, 0x76, 0xfd, 0x42, 0x81, 0x85, 0xc7, 0x5d, 0x6f, 0x0f, 0x7f, 0xbd, 0x65, 0x4b, 0xad, 0x41, 0x35, 0x7b, 0x4c, 0xae, 0xfc, 0x5f, 0x94, 0x60, 0xe1, 0x01, 0xfe, 0xda, 0xf3, 0xe0, 0x7f, 0x46, 0xbf, 0x6e, 0x43, 0x35, 0xcb, 0x2b, 0xae, 0x5f, 0x02, 0x1c, 0x8a, 0x08, 0xc7, 0x67, 0x0a, 0x9c, 0x79, 0xe8, 0x06, 0x56, 0xeb, 0x88, 0x84, 0xdb, 0xee, 0x01, 0xf6, 0x1e, 0x18, 0x24, 0x96, 0x8e, 0xb8, 0xfe, 0x5d, 0x98, 0x6f, 0xf1, 0x19, 0xbd, 0x4d, 0xa7, 0xf4, 0x84, 0xc3, 0x96, 0xa7, 0x1f, 0x49, 0x74, 0xcc, 0x67, 0x9b, 0x6d, 0x65, 0x07, 0x7d, 0xf5, 0x1c, 0x9c, 0xcd, 0xa1, 0x80, 0x0b, 0x85, 0x01, 0x4b, 0xf7, 0x70, 0xb0, 0xe9, 0xb9, 0xbe, 0xcf, 0x6f, 0x25, 0xf1, 0x72, 0x4b, 0x04, 0x7e, 0x4a, 0x2a, 0xf0, 0x3b, 0x0f, 0x95, 0xc0, 0xf0, 0xf6, 0x70, 0x10, 0xdd, 0x32, 0x7b, 0xcd, 0x4d, 0xb2, 0x51, 0x8e, 0x4f, 0xfd, 0x65, 0x19, 0xce, 0x88, 0xf7, 0xe0, 0xfc, 0x6c, 0x13, 0x3c, 0xc4, 0x34, 0xec, 0x1e, 0xb1, 0x30, 0x94, 0x1f, 0xff, 0x9e, 0xcc, 0x41, 0xcc, 0x45, 0x47, 0x9d, 0x6f, 0xff, 0xf6, 0x11, 0x75, 0x00, 0xd9, 0x1b, 0x66, 0x22, 0x88, 0x0d, 0xa1, 0xcf, 0x14, 0x98, 0x6b, 0xd1, 0x82, 0x98, 0xde, 0x34, 0xba, 0x3e, 0xee, 0x6d, 0xcb, 0xec, 0xdd, 0x83, 0xe3, 0x6d, 0xcb, 0x6a, 0x6c, 0x9b, 0x04, 0x63, 0x62, 0x73, 0xd4, 0xca, 0x4c, 0xd4, 0x3a, 0x30, 0x93, 0xa1, 0x52, 0xe0, 0x9e, 0xde, 0x49, 0xba, 0xa7, 0xeb, 0x39, 0xe2, 0x90, 0xa6, 0x89, 0x5f, 0x5e, 0xdc, 0x47, 0xad, 0x75, 0x60, 0x21, 0x87, 0x40, 0xc1, 0xbe, 0x37, 0xe3, 0xfb, 0x56, 0x72, 0xd3, 0xbd, 0xf7, 0x70, 0xd0, 0x2b, 0x2e, 0x52, 0xbc, 0x71, 0xaf, 0xf8, 0x3f, 0x14, 0x58, 0xe3, 0xe5, 0xbc, 0x0c, 0xd3, 0x32, 0x75, 0x08, 0x49, 0x64, 0xd6, 0x9f, 0x94, 0xa1, 0xa7, 0x4c, 0x88, 0xa2, 0xbe, 0x8b, 0x30, 0x57, 0xdd, 0x3f, 0xd3, 0x78, 0xb7, 0xc5, 0x64, 0x10, 0x7b, 0xf2, 0xd1, 0xab, 0x30, 0xd9, 0x22, 0x0e, 0xd0, 0x43, 0xcc, 0x7c, 0x29, 0x5e, 0x7e, 0x4a, 0x0e, 0xaa, 0x1e, 0xbc, 0xde, 0xc7, 0x59, 0x23, 0x77, 0x69, 0x28, 0xf4, 0xc7, 0x8f, 0x77, 0xad, 0x14, 0x5a, 0x7d, 0x87, 0x7e, 0xd3, 0x16, 0x2a, 0x36, 0x7d, 0x49, 0xf6, 0x91, 0x1b, 0x53, 0x03, 0xfa, 0xdd, 0x56, 0x12, 0x2c, 0x72, 0x1c, 0xe6, 0x7a, 0x65, 0x97, 0x30, 0x11, 0xd3, 0xe5, 0x7d, 0x54, 0x43, 0x5a, 0xaf, 0x26, 0xb3, 0xcd, 0xb2, 0x30, 0x5d, 0x87, 0xe6, 0xc5, 0xc3, 0xaf, 0x2e, 0x79, 0x0a, 0x89, 0xe5, 0x87, 0x26, 0xf9, 0x28, 0xcb, 0x20, 0xa9, 0x0d, 0x98, 0xd7, 0x8c, 0x00, 0xdb, 0x56, 0xdb, 0x0a, 0x3e, 0xea, 0x98, 0xb1, 0x44, 0xde, 0x3a, 0x9c, 0x32, 0x8d, 0xc0, 0xe0, 0xcc, 0x58, 0xca, 0x6b, 0xc4, 0xbc, 0xe5, 0x1c, 0x69, 0x74, 0xa1, 0xfa, 0x21, 0x2c, 0x64, 0x50, 0xf1, 0x03, 0x0c, 0x8a, 0x6b, 0xe3, 0x9f, 0xea, 0x00, 0xdc, 0x29, 0xbd, 0xf5, 0xb8, 0x81, 0xfe, 0x50, 0x81, 0x79, 0xf1, 0x47, 0xed, 0xe8, 0xca, 0xf1, 0xfe, 0x85, 0xa2, 0x76, 0x75, 0x60, 0x38, 0x7e, 0x96, 0x3f, 0x52, 0x60, 0x21, 0xe7, 0x5f, 0x0f, 0xd0, 0xd5, 0xa2, 0x7f, 0x0c, 0xc8, 0xa3, 0xe6, 0xda, 0xe0, 0x80, 0x9c, 0x9c, 0x1f, 0x2b, 0xb0, 0x52, 0xf4, 0xe5, 0x3f, 0xfa, 0xce, 0x49, 0xff, 0xc9, 0xa0, 0x76, 0xeb, 0x04, 0x18, 0x38, 0xa5, 0xe4, 0x12, 0xc5, 0xdf, 0xf4, 0x4b, 0x2e, 0x51, 0xfa, 0x5f, 0x02, 0x92, 0x4b, 0x2c, 0xf8, 0xf3, 0x80, 0x3f, 0x53, 0xa0, 0x96, 0xff, 0xe5, 0x3b, 0xca, 0xef, 0x0a, 0x2b, 0xfc, 0x47, 0x80, 0xda, 0x7b, 0xc7, 0x82, 0xe5, 0x74, 0xfd, 0x50, 0x81, 0xc5, 0xdc, 0xef, 0xda, 0xd1, 0xbb, 0xb9, 0xa8, 0x8b, 0x3e, 0xab, 0xaf, 0x5d, 0x3f, 0x0e, 0x28, 0x27, 0xca, 0x81, 0xc9, 0xc4, 0x07, 0xcf, 0xe8, 0xcd, 0x5c, 0x64, 0xa2, 0xef, 0xaa, 0x6b, 0xf5, 0x7e, 0x97, 0xf3, 0xfd, 0x3e, 0x53, 0xe0, 0xb4, 0xe0, 0xab, 0x61, 0x74, 0x59, 0x7e, 0xdb, 0xc2, 0xef, 0x94, 0x6b, 0x6f, 0x0f, 0x06, 0xc4, 0x49, 0x08, 0x60, 0x2a, 0xf5, 0x11, 0x2d, 0x5a, 0x97, 0xb9, 0x1f, 0x82, 0x4a, 0x48, 0xed, 0xad, 0xfe, 0x01, 0xf8, 0xae, 0x87, 0x30, 0x9d, 0xfe, 0x12, 0x0c, 0xe5, 0x63, 0xc9, 0xf9, 0x56, 0xae, 0x76, 0x69, 0x00, 0x88, 0x98, 0xd8, 0xe5, 0xf6, 0x3b, 0x4a, 0xc4, 0xae, 0xe8, 0x6b, 0x94, 0xda, 0x09, 0xda, 0x2b, 0xd1, 0x5f, 0x2a, 0x70, 0x46, 0xd6, 0x0e, 0x89, 0x6e, 0x1c, 0xb3, 0x8b, 0x92, 0x91, 0xf6, 0xfe, 0x89, 0x7a, 0x30, 0x39, 0xcb, 0x72, 0x7a, 0x06, 0xa5, 0x2c, 0x93, 0x77, 0x2c, 0x4a, 0x59, 0x56, 0xd0, 0xa2, 0x18, 0xbb, 0x47, 0x41, 0x43, 0x76, 0xe1, 0x3d, 0xe6, 0xb7, 0xc2, 0x17, 0xde, 0xa3, 0xac, 0xff, 0x3b, 0x76, 0x8f, 0xc2, 0xb6, 0xbd, 0xe2, 0x7b, 0x94, 0xb5, 0x0e, 0x16, 0xdf, 0xa3, 0xb4, 0x57, 0x30, 0x7e, 0x8f, 0xd9, 0xce, 0xbc, 0xe2, 0x7b, 0xcc, 0xed, 0x0b, 0x2c, 0xbe, 0xc7, 0xfc, 0x46, 0x40, 0xf4, 0x17, 0x34, 0xb7, 0x99, 0xdb, 0x72, 0x87, 0xde, 0x1b, 0xe8, 0xcc, 0xc9, 0xa6, 0xbf, 0xda, 0x8d, 0xe3, 0x01, 0x27, 0x48, 0xcb, 0xed, 0x37, 0x95, 0x92, 0x56, 0xd4, 0xf1, 0x2a, 0x25, 0xad, 0xb8, 0xc5, 0xf5, 0xaf, 0x15, 0x58, 0x96, 0x37, 0x9a, 0xa1, 0x6f, 0x4b, 0x36, 0xe8, 0xa3, 0xdb, 0xae, 0x76, 0xf3, 0xd8, 0xf0, 0x9c, 0xc6, 0xef, 0x2b, 0x50, 0xcd, 0x6b, 0x37, 0x44, 0xd7, 0x24, 0xd8, 0xa5, 0x7d, 0x95, 0xb5, 0x77, 0x8f, 0x01, 0xc9, 0x29, 0xfa, 0x5c, 0x81, 0x59, 0x51, 0xd3, 0x1a, 0xca, 0x7f, 0x73, 0x4a, 0x5a, 0xf4, 0x6a, 0xef, 0x0c, 0x08, 0xc5, 0xa9, 0xf8, 0x2b, 0xfa, 0xe7, 0x53, 0x92, 0xa6, 0x2c, 0xf4, 0x7e, 0x81, 0x6c, 0xc8, 0x3b, 0xea, 0x6a, 0xdf, 0x3e, 0x2e, 0x38, 0x27, 0xf0, 0x53, 0x98, 0xc9, 0xf4, 0x27, 0xa1, 0x4b, 0x12, 0xa4, 0xe2, 0xb6, 0xb1, 0xda, 0xc6, 0x20, 0x20, 0x3d, 0x6f, 0x24, 0xd5, 0x71, 0x24, 0xf1, 0x46, 0xc4, 0x7d, 0x52, 0x12, 0x6f, 0x24, 0xa7, 0x99, 0x09, 0x3d, 0x83, 0x89, 0x78, 0x07, 0x08, 0xfa, 0x96, 0x14, 0x43, 0xaa, 0xe5, 0xa9, 0xf6, 0x66, 0x9f, 0xab, 0x63, 0x52, 0x28, 0x6a, 0xe1, 0x90, 0x48, 0xa1, 0xa4, 0x0b, 0x45, 0x22, 0x85, 0xd2, 0x3e, 0x11, 0xe2, 0x79, 0x0a, 0x3a, 0x33, 0x24, 0x9e, 0x67, 0x7e, 0x9b, 0x47, 0xed, 0xed, 0xc1, 0x80, 0xa2, 0x4f, 0x55, 0xa0, 0xd7, 0xe8, 0x80, 0x2e, 0xe6, 0xe2, 0xc8, 0x74, 0x4f, 0xd4, 0xde, 0xe8, 0x6b, 0x6d, 0x6f, 0x9b, 0x5e, 0x27, 0x81, 0x64, 0x9b, 0x4c, 0x77, 0x85, 0x64, 0x9b, 0x6c, 0x6b, 0x02, 0xdb, 0x26, 0x6c, 0x04, 0x90, 0x6e, 0x93, 0x6a, 0x5f, 0x90, 0x6e, 0x93, 0xee, 0x2c, 0x20, 0x11, 0x4a, 0xa2, 0x88, 0x2f, 0x89, 0x50, 0x44, 0x0d, 0x08, 0x92, 0x08, 0x45, 0xdc, 0x1b, 0x40, 0x42, 0x59, 0x71, 0x31, 0x5c, 0x12, 0xca, 0x4a, 0x9b, 0x02, 0x24, 0xa1, 0x6c, 0x41, 0x19, 0x9f, 0x38, 0x30, 0xb9, 0x75, 0x67, 0x89, 0x03, 0x53, 0x54, 0x1a, 0x97, 0x38, 0x30, 0xc5, 0x65, 0x6e, 0x07, 0x26, 0x13, 0x55, 0x5b, 0xc9, 0x85, 0x88, 0x0a, 0xd7, 0x92, 0x0b, 0x11, 0x16, 0x83, 0xa9, 0xf9, 0x10, 0x55, 0x58, 0x91, 0x2c, 0xfc, 0xcb, 0xad, 0x1d, 0x4b, 0xcc, 0x87, 0xac, 0x8c, 0x4b, 0xe2, 0xb7, 0x74, 0x2d, 0x56, 0x12, 0xbf, 0xe5, 0x54, 0x7c, 0x25, 0xf1, 0x5b, 0x6e, 0xa1, 0x37, 0x80, 0xa9, 0x54, 0xd1, 0x51, 0xf2, 0x82, 0x10, 0x97, 0x72, 0x25, 0x2f, 0x88, 0xbc, 0x7a, 0x26, 0x09, 0x57, 0x53, 0x45, 0x2d, 0x59, 0xb8, 0x2a, 0x2e, 0xf3, 0xc9, 0xc2, 0xd5, 0x9c, 0x8a, 0x19, 0xd9, 0x38, 0x5d, 0x04, 0x92, 0x6c, 0x9c, 0x53, 0x5b, 0x93, 0x6c, 0x9c, 0x5b, 0x61, 0xfa, 0x03, 0x05, 0xe6, 0x84, 0x75, 0x1b, 0x94, 0x2f, 0x31, 0xb2, 0x4a, 0x53, 0xed, 0xca, 0xa0, 0x60, 0x31, 0x79, 0x17, 0x55, 0x3d, 0x24, 0xf2, 0x2e, 0x29, 0x27, 0x49, 0xe4, 0x5d, 0x5a, 0x20, 0xfa, 0x42, 0x89, 0xbe, 0x6a, 0xca, 0x4f, 0xaf, 0xa3, 0x5b, 0x45, 0xf1, 0x46, 0x61, 0x19, 0xa2, 0x76, 0xfb, 0x24, 0x28, 0x12, 0x29, 0x9d, 0x78, 0x7e, 0x5d, 0x9e, 0xd2, 0x11, 0x24, 0xf0, 0xe5, 0x29, 0x1d, 0x61, 0xea, 0x9e, 0x68, 0x66, 0x32, 0x29, 0x2e, 0xd3, 0x4c, 0x61, 0x26, 0x5e, 0xa6, 0x99, 0xe2, 0x7c, 0xfb, 0xed, 0x3b, 0x3f, 0xfd, 0x72, 0x59, 0xf9, 0xd9, 0x97, 0xcb, 0xca, 0xbf, 0x7d, 0xb9, 0xac, 0xfc, 0xfa, 0xd5, 0x3d, 0x2b, 0xd8, 0xef, 0xee, 0xd6, 0x9b, 0x6e, 0x7b, 0x3d, 0xf1, 0xff, 0xe4, 0xf5, 0x3d, 0xec, 0xb0, 0x3f, 0xab, 0x8f, 0xfd, 0x5b, 0xfe, 0x7b, 0xfc, 0xe7, 0xc1, 0xa5, 0xdd, 0x61, 0x3a, 0x77, 0xf9, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xc2, 0x7a, 0xca, 0x50, 0x59, 0x5f, 0x00, 0x00, } func (m *StartWorkflowExecutionRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *StartWorkflowExecutionRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *StartWorkflowExecutionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.PartitionConfig) > 0 { for k := range m.PartitionConfig { v := m.PartitionConfig[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = encodeVarintService(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x52 } } if m.FirstDecisionTaskBackoff != nil { { size, err := m.FirstDecisionTaskBackoff.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x4a } if m.LastCompletionResult != nil { { size, err := m.LastCompletionResult.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x42 } if m.ContinuedFailure != nil { { size, err := m.ContinuedFailure.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x3a } if m.ContinueAsNewInitiator != 0 { i = encodeVarintService(dAtA, i, uint64(m.ContinueAsNewInitiator)) i-- dAtA[i] = 0x30 } if m.ExpirationTime != nil { { size, err := m.ExpirationTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x2a } if m.Attempt != 0 { i = encodeVarintService(dAtA, i, uint64(m.Attempt)) i-- dAtA[i] = 0x20 } if m.ParentExecutionInfo != nil { { size, err := m.ParentExecutionInfo.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *StartWorkflowExecutionResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *StartWorkflowExecutionResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *StartWorkflowExecutionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.RunId) > 0 { i -= len(m.RunId) copy(dAtA[i:], m.RunId) i = encodeVarintService(dAtA, i, uint64(len(m.RunId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SignalWorkflowExecutionRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SignalWorkflowExecutionRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *SignalWorkflowExecutionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.ChildWorkflowOnly { i-- if m.ChildWorkflowOnly { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.ExternalWorkflowExecution != nil { { size, err := m.ExternalWorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SignalWorkflowExecutionResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SignalWorkflowExecutionResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *SignalWorkflowExecutionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *SignalWithStartWorkflowExecutionRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SignalWithStartWorkflowExecutionRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *SignalWithStartWorkflowExecutionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.PartitionConfig) > 0 { for k := range m.PartitionConfig { v := m.PartitionConfig[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = encodeVarintService(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x1a } } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SignalWithStartWorkflowExecutionResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SignalWithStartWorkflowExecutionResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *SignalWithStartWorkflowExecutionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.RunId) > 0 { i -= len(m.RunId) copy(dAtA[i:], m.RunId) i = encodeVarintService(dAtA, i, uint64(len(m.RunId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ResetWorkflowExecutionRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResetWorkflowExecutionRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ResetWorkflowExecutionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ResetWorkflowExecutionResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResetWorkflowExecutionResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ResetWorkflowExecutionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.RunId) > 0 { i -= len(m.RunId) copy(dAtA[i:], m.RunId) i = encodeVarintService(dAtA, i, uint64(len(m.RunId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *TerminateWorkflowExecutionRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TerminateWorkflowExecutionRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *TerminateWorkflowExecutionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.ChildWorkflowOnly { i-- if m.ChildWorkflowOnly { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.ExternalWorkflowExecution != nil { { size, err := m.ExternalWorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *TerminateWorkflowExecutionResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TerminateWorkflowExecutionResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *TerminateWorkflowExecutionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *DescribeWorkflowExecutionRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DescribeWorkflowExecutionRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *DescribeWorkflowExecutionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DescribeWorkflowExecutionResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DescribeWorkflowExecutionResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *DescribeWorkflowExecutionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.PendingDecision != nil { { size, err := m.PendingDecision.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x2a } if len(m.PendingChildren) > 0 { for iNdEx := len(m.PendingChildren) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.PendingChildren[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } } if len(m.PendingActivities) > 0 { for iNdEx := len(m.PendingActivities) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.PendingActivities[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } } if m.WorkflowExecutionInfo != nil { { size, err := m.WorkflowExecutionInfo.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if m.ExecutionConfiguration != nil { { size, err := m.ExecutionConfiguration.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *QueryWorkflowRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *QueryWorkflowRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *QueryWorkflowRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *QueryWorkflowResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *QueryWorkflowResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *QueryWorkflowResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.QueryRejected != nil { { size, err := m.QueryRejected.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if m.QueryResult != nil { { size, err := m.QueryResult.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ResetStickyTaskListRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResetStickyTaskListRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ResetStickyTaskListRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ResetStickyTaskListResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResetStickyTaskListResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ResetStickyTaskListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *GetMutableStateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetMutableStateRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetMutableStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.VersionHistoryItem != nil { { size, err := m.VersionHistoryItem.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x2a } if len(m.CurrentBranchToken) > 0 { i -= len(m.CurrentBranchToken) copy(dAtA[i:], m.CurrentBranchToken) i = encodeVarintService(dAtA, i, uint64(len(m.CurrentBranchToken))) i-- dAtA[i] = 0x22 } if m.ExpectedNextEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ExpectedNextEventId)) i-- dAtA[i] = 0x18 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *GetMutableStateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetMutableStateResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetMutableStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.HistorySize != 0 { i = encodeVarintService(dAtA, i, uint64(m.HistorySize)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x90 } if m.IsStickyTaskListEnabled { i-- if m.IsStickyTaskListEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x88 } if m.VersionHistories != nil { { size, err := m.VersionHistories.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x82 } if m.WorkflowCloseState != 0 { i = encodeVarintService(dAtA, i, uint64(m.WorkflowCloseState)) i-- dAtA[i] = 0x78 } if m.WorkflowState != 0 { i = encodeVarintService(dAtA, i, uint64(m.WorkflowState)) i-- dAtA[i] = 0x70 } if len(m.CurrentBranchToken) > 0 { i -= len(m.CurrentBranchToken) copy(dAtA[i:], m.CurrentBranchToken) i = encodeVarintService(dAtA, i, uint64(len(m.CurrentBranchToken))) i-- dAtA[i] = 0x6a } if m.EventStoreVersion != 0 { i = encodeVarintService(dAtA, i, uint64(m.EventStoreVersion)) i-- dAtA[i] = 0x60 } if m.StickyTaskListScheduleToStartTimeout != nil { { size, err := m.StickyTaskListScheduleToStartTimeout.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x5a } if len(m.ClientImpl) > 0 { i -= len(m.ClientImpl) copy(dAtA[i:], m.ClientImpl) i = encodeVarintService(dAtA, i, uint64(len(m.ClientImpl))) i-- dAtA[i] = 0x52 } if len(m.ClientFeatureVersion) > 0 { i -= len(m.ClientFeatureVersion) copy(dAtA[i:], m.ClientFeatureVersion) i = encodeVarintService(dAtA, i, uint64(len(m.ClientFeatureVersion))) i-- dAtA[i] = 0x4a } if len(m.ClientLibraryVersion) > 0 { i -= len(m.ClientLibraryVersion) copy(dAtA[i:], m.ClientLibraryVersion) i = encodeVarintService(dAtA, i, uint64(len(m.ClientLibraryVersion))) i-- dAtA[i] = 0x42 } if m.StickyTaskList != nil { { size, err := m.StickyTaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x3a } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x32 } if m.LastFirstEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.LastFirstEventId)) i-- dAtA[i] = 0x28 } if m.PreviousStartedEventId != nil { { size, err := m.PreviousStartedEventId.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.NextEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.NextEventId)) i-- dAtA[i] = 0x18 } if m.WorkflowType != nil { { size, err := m.WorkflowType.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PollMutableStateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PollMutableStateRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *PollMutableStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.CurrentBranchToken) > 0 { i -= len(m.CurrentBranchToken) copy(dAtA[i:], m.CurrentBranchToken) i = encodeVarintService(dAtA, i, uint64(len(m.CurrentBranchToken))) i-- dAtA[i] = 0x22 } if m.ExpectedNextEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ExpectedNextEventId)) i-- dAtA[i] = 0x18 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PollMutableStateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PollMutableStateResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *PollMutableStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.WorkflowCloseState != 0 { i = encodeVarintService(dAtA, i, uint64(m.WorkflowCloseState)) i-- dAtA[i] = 0x78 } if m.WorkflowState != 0 { i = encodeVarintService(dAtA, i, uint64(m.WorkflowState)) i-- dAtA[i] = 0x70 } if m.VersionHistories != nil { { size, err := m.VersionHistories.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x6a } if len(m.CurrentBranchToken) > 0 { i -= len(m.CurrentBranchToken) copy(dAtA[i:], m.CurrentBranchToken) i = encodeVarintService(dAtA, i, uint64(len(m.CurrentBranchToken))) i-- dAtA[i] = 0x62 } if m.StickyTaskListScheduleToStartTimeout != nil { { size, err := m.StickyTaskListScheduleToStartTimeout.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x5a } if len(m.ClientImpl) > 0 { i -= len(m.ClientImpl) copy(dAtA[i:], m.ClientImpl) i = encodeVarintService(dAtA, i, uint64(len(m.ClientImpl))) i-- dAtA[i] = 0x52 } if len(m.ClientFeatureVersion) > 0 { i -= len(m.ClientFeatureVersion) copy(dAtA[i:], m.ClientFeatureVersion) i = encodeVarintService(dAtA, i, uint64(len(m.ClientFeatureVersion))) i-- dAtA[i] = 0x4a } if len(m.ClientLibraryVersion) > 0 { i -= len(m.ClientLibraryVersion) copy(dAtA[i:], m.ClientLibraryVersion) i = encodeVarintService(dAtA, i, uint64(len(m.ClientLibraryVersion))) i-- dAtA[i] = 0x42 } if m.StickyTaskList != nil { { size, err := m.StickyTaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x3a } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x32 } if m.LastFirstEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.LastFirstEventId)) i-- dAtA[i] = 0x28 } if m.PreviousStartedEventId != nil { { size, err := m.PreviousStartedEventId.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.NextEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.NextEventId)) i-- dAtA[i] = 0x18 } if m.WorkflowType != nil { { size, err := m.WorkflowType.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RecordDecisionTaskStartedRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RecordDecisionTaskStartedRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RecordDecisionTaskStartedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.PollRequest != nil { { size, err := m.PollRequest.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x32 } if len(m.RequestId) > 0 { i -= len(m.RequestId) copy(dAtA[i:], m.RequestId) i = encodeVarintService(dAtA, i, uint64(len(m.RequestId))) i-- dAtA[i] = 0x2a } if m.TaskId != 0 { i = encodeVarintService(dAtA, i, uint64(m.TaskId)) i-- dAtA[i] = 0x20 } if m.ScheduleId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ScheduleId)) i-- dAtA[i] = 0x18 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RecordDecisionTaskStartedResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RecordDecisionTaskStartedResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RecordDecisionTaskStartedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.HistorySize != 0 { i = encodeVarintService(dAtA, i, uint64(m.HistorySize)) i-- dAtA[i] = 0x78 } if len(m.Queries) > 0 { for k := range m.Queries { v := m.Queries[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x72 } } if m.StartedTime != nil { { size, err := m.StartedTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x6a } if m.ScheduledTime != nil { { size, err := m.ScheduledTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x62 } if len(m.BranchToken) > 0 { i -= len(m.BranchToken) copy(dAtA[i:], m.BranchToken) i = encodeVarintService(dAtA, i, uint64(len(m.BranchToken))) i-- dAtA[i] = 0x5a } if m.EventStoreVersion != 0 { i = encodeVarintService(dAtA, i, uint64(m.EventStoreVersion)) i-- dAtA[i] = 0x50 } if m.WorkflowExecutionTaskList != nil { { size, err := m.WorkflowExecutionTaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x4a } if m.DecisionInfo != nil { { size, err := m.DecisionInfo.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x42 } if m.StickyExecutionEnabled { i-- if m.StickyExecutionEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x38 } if m.Attempt != 0 { i = encodeVarintService(dAtA, i, uint64(m.Attempt)) i-- dAtA[i] = 0x30 } if m.NextEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.NextEventId)) i-- dAtA[i] = 0x28 } if m.StartedEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.StartedEventId)) i-- dAtA[i] = 0x20 } if m.ScheduledEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ScheduledEventId)) i-- dAtA[i] = 0x18 } if m.PreviousStartedEventId != nil { { size, err := m.PreviousStartedEventId.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if m.WorkflowType != nil { { size, err := m.WorkflowType.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RecordActivityTaskStartedRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RecordActivityTaskStartedRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RecordActivityTaskStartedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.PollRequest != nil { { size, err := m.PollRequest.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x32 } if len(m.RequestId) > 0 { i -= len(m.RequestId) copy(dAtA[i:], m.RequestId) i = encodeVarintService(dAtA, i, uint64(len(m.RequestId))) i-- dAtA[i] = 0x2a } if m.TaskId != 0 { i = encodeVarintService(dAtA, i, uint64(m.TaskId)) i-- dAtA[i] = 0x20 } if m.ScheduleId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ScheduleId)) i-- dAtA[i] = 0x18 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RecordActivityTaskStartedResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RecordActivityTaskStartedResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RecordActivityTaskStartedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.WorkflowDomain) > 0 { i -= len(m.WorkflowDomain) copy(dAtA[i:], m.WorkflowDomain) i = encodeVarintService(dAtA, i, uint64(len(m.WorkflowDomain))) i-- dAtA[i] = 0x3a } if m.WorkflowType != nil { { size, err := m.WorkflowType.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x32 } if m.HeartbeatDetails != nil { { size, err := m.HeartbeatDetails.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x2a } if m.ScheduledTimeOfThisAttempt != nil { { size, err := m.ScheduledTimeOfThisAttempt.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.Attempt != 0 { i = encodeVarintService(dAtA, i, uint64(m.Attempt)) i-- dAtA[i] = 0x18 } if m.StartedTime != nil { { size, err := m.StartedTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if m.ScheduledEvent != nil { { size, err := m.ScheduledEvent.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RespondDecisionTaskCompletedRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondDecisionTaskCompletedRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondDecisionTaskCompletedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RespondDecisionTaskCompletedResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondDecisionTaskCompletedResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondDecisionTaskCompletedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ActivitiesToDispatchLocally) > 0 { for k := range m.ActivitiesToDispatchLocally { v := m.ActivitiesToDispatchLocally[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x12 } } if m.StartedResponse != nil { { size, err := m.StartedResponse.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RespondDecisionTaskFailedRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondDecisionTaskFailedRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondDecisionTaskFailedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RespondDecisionTaskFailedResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondDecisionTaskFailedResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondDecisionTaskFailedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RecordActivityTaskHeartbeatRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RecordActivityTaskHeartbeatRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RecordActivityTaskHeartbeatRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RecordActivityTaskHeartbeatResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RecordActivityTaskHeartbeatResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RecordActivityTaskHeartbeatResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.CancelRequested { i-- if m.CancelRequested { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *RespondActivityTaskCompletedRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondActivityTaskCompletedRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondActivityTaskCompletedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RespondActivityTaskCompletedResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondActivityTaskCompletedResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondActivityTaskCompletedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RespondActivityTaskFailedRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondActivityTaskFailedRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondActivityTaskFailedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RespondActivityTaskFailedResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondActivityTaskFailedResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondActivityTaskFailedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RespondActivityTaskCanceledRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondActivityTaskCanceledRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondActivityTaskCanceledRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RespondActivityTaskCanceledResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondActivityTaskCanceledResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondActivityTaskCanceledResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RemoveSignalMutableStateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RemoveSignalMutableStateRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RemoveSignalMutableStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.RequestId) > 0 { i -= len(m.RequestId) copy(dAtA[i:], m.RequestId) i = encodeVarintService(dAtA, i, uint64(len(m.RequestId))) i-- dAtA[i] = 0x1a } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RemoveSignalMutableStateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RemoveSignalMutableStateResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RemoveSignalMutableStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RequestCancelWorkflowExecutionRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RequestCancelWorkflowExecutionRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RequestCancelWorkflowExecutionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.ChildWorkflowOnly { i-- if m.ChildWorkflowOnly { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.ExternalExecutionInfo != nil { { size, err := m.ExternalExecutionInfo.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if m.CancelRequest != nil { { size, err := m.CancelRequest.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RequestCancelWorkflowExecutionResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RequestCancelWorkflowExecutionResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RequestCancelWorkflowExecutionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *ScheduleDecisionTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ScheduleDecisionTaskRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ScheduleDecisionTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.IsFirstDecision { i-- if m.IsFirstDecision { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ScheduleDecisionTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ScheduleDecisionTaskResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ScheduleDecisionTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RecordChildExecutionCompletedRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RecordChildExecutionCompletedRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RecordChildExecutionCompletedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.StartedId != 0 { i = encodeVarintService(dAtA, i, uint64(m.StartedId)) i-- dAtA[i] = 0x30 } if m.CompletionEvent != nil { { size, err := m.CompletionEvent.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x2a } if m.CompletedExecution != nil { { size, err := m.CompletedExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.InitiatedId != 0 { i = encodeVarintService(dAtA, i, uint64(m.InitiatedId)) i-- dAtA[i] = 0x18 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RecordChildExecutionCompletedResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RecordChildExecutionCompletedResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RecordChildExecutionCompletedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *ReplicateEventsV2Request) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ReplicateEventsV2Request) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ReplicateEventsV2Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.NewRunEvents != nil { { size, err := m.NewRunEvents.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x2a } if m.Events != nil { { size, err := m.Events.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if len(m.VersionHistoryItems) > 0 { for iNdEx := len(m.VersionHistoryItems) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.VersionHistoryItems[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ReplicateEventsV2Response) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ReplicateEventsV2Response) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ReplicateEventsV2Response) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *SyncShardStatusRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SyncShardStatusRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *SyncShardStatusRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.Time != nil { { size, err := m.Time.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if m.ShardId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ShardId)) i-- dAtA[i] = 0x10 } if len(m.SourceCluster) > 0 { i -= len(m.SourceCluster) copy(dAtA[i:], m.SourceCluster) i = encodeVarintService(dAtA, i, uint64(len(m.SourceCluster))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SyncShardStatusResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SyncShardStatusResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *SyncShardStatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *SyncActivityRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SyncActivityRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *SyncActivityRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.VersionHistory != nil { { size, err := m.VersionHistory.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x6a } if len(m.LastWorkerIdentity) > 0 { i -= len(m.LastWorkerIdentity) copy(dAtA[i:], m.LastWorkerIdentity) i = encodeVarintService(dAtA, i, uint64(len(m.LastWorkerIdentity))) i-- dAtA[i] = 0x62 } if m.LastFailure != nil { { size, err := m.LastFailure.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x5a } if m.Attempt != 0 { i = encodeVarintService(dAtA, i, uint64(m.Attempt)) i-- dAtA[i] = 0x50 } if m.Details != nil { { size, err := m.Details.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x4a } if m.LastHeartbeatTime != nil { { size, err := m.LastHeartbeatTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x42 } if m.StartedTime != nil { { size, err := m.StartedTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x3a } if m.StartedId != 0 { i = encodeVarintService(dAtA, i, uint64(m.StartedId)) i-- dAtA[i] = 0x30 } if m.ScheduledTime != nil { { size, err := m.ScheduledTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x2a } if m.ScheduledId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ScheduledId)) i-- dAtA[i] = 0x20 } if m.Version != 0 { i = encodeVarintService(dAtA, i, uint64(m.Version)) i-- dAtA[i] = 0x18 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SyncActivityResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SyncActivityResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *SyncActivityResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *DescribeMutableStateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DescribeMutableStateRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *DescribeMutableStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DescribeMutableStateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DescribeMutableStateResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *DescribeMutableStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.MutableStateInDatabase) > 0 { i -= len(m.MutableStateInDatabase) copy(dAtA[i:], m.MutableStateInDatabase) i = encodeVarintService(dAtA, i, uint64(len(m.MutableStateInDatabase))) i-- dAtA[i] = 0x12 } if len(m.MutableStateInCache) > 0 { i -= len(m.MutableStateInCache) copy(dAtA[i:], m.MutableStateInCache) i = encodeVarintService(dAtA, i, uint64(len(m.MutableStateInCache))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DescribeHistoryHostRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DescribeHistoryHostRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *DescribeHistoryHostRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *DescribeHistoryHostResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DescribeHistoryHostResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *DescribeHistoryHostResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Address) > 0 { i -= len(m.Address) copy(dAtA[i:], m.Address) i = encodeVarintService(dAtA, i, uint64(len(m.Address))) i-- dAtA[i] = 0x2a } if len(m.ShardControllerStatus) > 0 { i -= len(m.ShardControllerStatus) copy(dAtA[i:], m.ShardControllerStatus) i = encodeVarintService(dAtA, i, uint64(len(m.ShardControllerStatus))) i-- dAtA[i] = 0x22 } if m.DomainCache != nil { { size, err := m.DomainCache.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if len(m.ShardIds) > 0 { dAtA83 := make([]byte, len(m.ShardIds)*10) var j82 int for _, num1 := range m.ShardIds { num := uint64(num1) for num >= 1<<7 { dAtA83[j82] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j82++ } dAtA83[j82] = uint8(num) j82++ } i -= j82 copy(dAtA[i:], dAtA83[:j82]) i = encodeVarintService(dAtA, i, uint64(j82)) i-- dAtA[i] = 0x12 } if m.NumberOfShards != 0 { i = encodeVarintService(dAtA, i, uint64(m.NumberOfShards)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *CloseShardRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CloseShardRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *CloseShardRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.ShardId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ShardId)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *CloseShardResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CloseShardResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *CloseShardResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RemoveTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RemoveTaskRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RemoveTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ClusterName) > 0 { i -= len(m.ClusterName) copy(dAtA[i:], m.ClusterName) i = encodeVarintService(dAtA, i, uint64(len(m.ClusterName))) i-- dAtA[i] = 0x2a } if m.VisibilityTime != nil { { size, err := m.VisibilityTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.TaskId != 0 { i = encodeVarintService(dAtA, i, uint64(m.TaskId)) i-- dAtA[i] = 0x18 } if m.TaskType != 0 { i = encodeVarintService(dAtA, i, uint64(m.TaskType)) i-- dAtA[i] = 0x10 } if m.ShardId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ShardId)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *RemoveTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RemoveTaskResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RemoveTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *ResetQueueRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResetQueueRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ResetQueueRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.TaskType != 0 { i = encodeVarintService(dAtA, i, uint64(m.TaskType)) i-- dAtA[i] = 0x18 } if len(m.ClusterName) > 0 { i -= len(m.ClusterName) copy(dAtA[i:], m.ClusterName) i = encodeVarintService(dAtA, i, uint64(len(m.ClusterName))) i-- dAtA[i] = 0x12 } if m.ShardId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ShardId)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ResetQueueResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResetQueueResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ResetQueueResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *DescribeQueueRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DescribeQueueRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *DescribeQueueRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.TaskType != 0 { i = encodeVarintService(dAtA, i, uint64(m.TaskType)) i-- dAtA[i] = 0x18 } if len(m.ClusterName) > 0 { i -= len(m.ClusterName) copy(dAtA[i:], m.ClusterName) i = encodeVarintService(dAtA, i, uint64(len(m.ClusterName))) i-- dAtA[i] = 0x12 } if m.ShardId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ShardId)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *DescribeQueueResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DescribeQueueResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *DescribeQueueResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ProcessingQueueStates) > 0 { for iNdEx := len(m.ProcessingQueueStates) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ProcessingQueueStates[iNdEx]) copy(dAtA[i:], m.ProcessingQueueStates[iNdEx]) i = encodeVarintService(dAtA, i, uint64(len(m.ProcessingQueueStates[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *GetReplicationMessagesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetReplicationMessagesRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetReplicationMessagesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ClusterName) > 0 { i -= len(m.ClusterName) copy(dAtA[i:], m.ClusterName) i = encodeVarintService(dAtA, i, uint64(len(m.ClusterName))) i-- dAtA[i] = 0x12 } if len(m.Tokens) > 0 { for iNdEx := len(m.Tokens) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.Tokens[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *GetReplicationMessagesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetReplicationMessagesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetReplicationMessagesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ShardMessages) > 0 { for k := range m.ShardMessages { v := m.ShardMessages[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i = encodeVarintService(dAtA, i, uint64(k)) i-- dAtA[i] = 0x8 i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *GetDLQReplicationMessagesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetDLQReplicationMessagesRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetDLQReplicationMessagesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.TaskInfos) > 0 { for iNdEx := len(m.TaskInfos) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.TaskInfos[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *GetDLQReplicationMessagesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetDLQReplicationMessagesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetDLQReplicationMessagesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ReplicationTasks) > 0 { for iNdEx := len(m.ReplicationTasks) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.ReplicationTasks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ReapplyEventsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ReapplyEventsRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ReapplyEventsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.Events != nil { { size, err := m.Events.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if len(m.Domain) > 0 { i -= len(m.Domain) copy(dAtA[i:], m.Domain) i = encodeVarintService(dAtA, i, uint64(len(m.Domain))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ReapplyEventsResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ReapplyEventsResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ReapplyEventsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RefreshWorkflowTasksRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RefreshWorkflowTasksRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RefreshWorkflowTasksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if len(m.Domain) > 0 { i -= len(m.Domain) copy(dAtA[i:], m.Domain) i = encodeVarintService(dAtA, i, uint64(len(m.Domain))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RefreshWorkflowTasksResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RefreshWorkflowTasksResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RefreshWorkflowTasksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *CountDLQMessagesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CountDLQMessagesRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *CountDLQMessagesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.ForceFetch { i-- if m.ForceFetch { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *CountDLQMessagesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CountDLQMessagesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *CountDLQMessagesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Entries) > 0 { for iNdEx := len(m.Entries) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.Entries[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ReadDLQMessagesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ReadDLQMessagesRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ReadDLQMessagesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.NextPageToken) > 0 { i -= len(m.NextPageToken) copy(dAtA[i:], m.NextPageToken) i = encodeVarintService(dAtA, i, uint64(len(m.NextPageToken))) i-- dAtA[i] = 0x32 } if m.PageSize != 0 { i = encodeVarintService(dAtA, i, uint64(m.PageSize)) i-- dAtA[i] = 0x28 } if m.InclusiveEndMessageId != nil { { size, err := m.InclusiveEndMessageId.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if len(m.SourceCluster) > 0 { i -= len(m.SourceCluster) copy(dAtA[i:], m.SourceCluster) i = encodeVarintService(dAtA, i, uint64(len(m.SourceCluster))) i-- dAtA[i] = 0x1a } if m.ShardId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ShardId)) i-- dAtA[i] = 0x10 } if m.Type != 0 { i = encodeVarintService(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ReadDLQMessagesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ReadDLQMessagesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ReadDLQMessagesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.NextPageToken) > 0 { i -= len(m.NextPageToken) copy(dAtA[i:], m.NextPageToken) i = encodeVarintService(dAtA, i, uint64(len(m.NextPageToken))) i-- dAtA[i] = 0x22 } if len(m.ReplicationTasksInfo) > 0 { for iNdEx := len(m.ReplicationTasksInfo) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.ReplicationTasksInfo[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } } if len(m.ReplicationTasks) > 0 { for iNdEx := len(m.ReplicationTasks) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.ReplicationTasks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } } if m.Type != 0 { i = encodeVarintService(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *PurgeDLQMessagesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PurgeDLQMessagesRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *PurgeDLQMessagesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.InclusiveEndMessageId != nil { { size, err := m.InclusiveEndMessageId.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if len(m.SourceCluster) > 0 { i -= len(m.SourceCluster) copy(dAtA[i:], m.SourceCluster) i = encodeVarintService(dAtA, i, uint64(len(m.SourceCluster))) i-- dAtA[i] = 0x1a } if m.ShardId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ShardId)) i-- dAtA[i] = 0x10 } if m.Type != 0 { i = encodeVarintService(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *PurgeDLQMessagesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PurgeDLQMessagesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *PurgeDLQMessagesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *MergeDLQMessagesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MergeDLQMessagesRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *MergeDLQMessagesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.NextPageToken) > 0 { i -= len(m.NextPageToken) copy(dAtA[i:], m.NextPageToken) i = encodeVarintService(dAtA, i, uint64(len(m.NextPageToken))) i-- dAtA[i] = 0x32 } if m.PageSize != 0 { i = encodeVarintService(dAtA, i, uint64(m.PageSize)) i-- dAtA[i] = 0x28 } if m.InclusiveEndMessageId != nil { { size, err := m.InclusiveEndMessageId.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if len(m.SourceCluster) > 0 { i -= len(m.SourceCluster) copy(dAtA[i:], m.SourceCluster) i = encodeVarintService(dAtA, i, uint64(len(m.SourceCluster))) i-- dAtA[i] = 0x1a } if m.ShardId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ShardId)) i-- dAtA[i] = 0x10 } if m.Type != 0 { i = encodeVarintService(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *MergeDLQMessagesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MergeDLQMessagesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *MergeDLQMessagesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.NextPageToken) > 0 { i -= len(m.NextPageToken) copy(dAtA[i:], m.NextPageToken) i = encodeVarintService(dAtA, i, uint64(len(m.NextPageToken))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NotifyFailoverMarkersRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NotifyFailoverMarkersRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *NotifyFailoverMarkersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.FailoverMarkerTokens) > 0 { for iNdEx := len(m.FailoverMarkerTokens) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.FailoverMarkerTokens[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *NotifyFailoverMarkersResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NotifyFailoverMarkersResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *NotifyFailoverMarkersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *GetCrossClusterTasksRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetCrossClusterTasksRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetCrossClusterTasksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.TargetCluster) > 0 { i -= len(m.TargetCluster) copy(dAtA[i:], m.TargetCluster) i = encodeVarintService(dAtA, i, uint64(len(m.TargetCluster))) i-- dAtA[i] = 0x12 } if len(m.ShardIds) > 0 { dAtA93 := make([]byte, len(m.ShardIds)*10) var j92 int for _, num1 := range m.ShardIds { num := uint64(num1) for num >= 1<<7 { dAtA93[j92] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j92++ } dAtA93[j92] = uint8(num) j92++ } i -= j92 copy(dAtA[i:], dAtA93[:j92]) i = encodeVarintService(dAtA, i, uint64(j92)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *GetCrossClusterTasksResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetCrossClusterTasksResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetCrossClusterTasksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.FailedCauseByShard) > 0 { for k := range m.FailedCauseByShard { v := m.FailedCauseByShard[k] baseI := i i = encodeVarintService(dAtA, i, uint64(v)) i-- dAtA[i] = 0x10 i = encodeVarintService(dAtA, i, uint64(k)) i-- dAtA[i] = 0x8 i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x12 } } if len(m.TasksByShard) > 0 { for k := range m.TasksByShard { v := m.TasksByShard[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i = encodeVarintService(dAtA, i, uint64(k)) i-- dAtA[i] = 0x8 i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *RespondCrossClusterTasksCompletedRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondCrossClusterTasksCompletedRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondCrossClusterTasksCompletedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.FetchNewTasks { i-- if m.FetchNewTasks { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if len(m.TaskResponses) > 0 { for iNdEx := len(m.TaskResponses) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.TaskResponses[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } } if len(m.TargetCluster) > 0 { i -= len(m.TargetCluster) copy(dAtA[i:], m.TargetCluster) i = encodeVarintService(dAtA, i, uint64(len(m.TargetCluster))) i-- dAtA[i] = 0x12 } if m.ShardId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ShardId)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *RespondCrossClusterTasksCompletedResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondCrossClusterTasksCompletedResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondCrossClusterTasksCompletedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.Tasks != nil { { size, err := m.Tasks.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *GetFailoverInfoRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetFailoverInfoRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetFailoverInfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *GetFailoverInfoResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetFailoverInfoResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetFailoverInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.PendingShards) > 0 { dAtA97 := make([]byte, len(m.PendingShards)*10) var j96 int for _, num1 := range m.PendingShards { num := uint64(num1) for num >= 1<<7 { dAtA97[j96] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j96++ } dAtA97[j96] = uint8(num) j96++ } i -= j96 copy(dAtA[i:], dAtA97[:j96]) i = encodeVarintService(dAtA, i, uint64(j96)) i-- dAtA[i] = 0x12 } if m.CompletedShardCount != 0 { i = encodeVarintService(dAtA, i, uint64(m.CompletedShardCount)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *RatelimitUpdateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RatelimitUpdateRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RatelimitUpdateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.Data != nil { { size, err := m.Data.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RatelimitUpdateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RatelimitUpdateResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RatelimitUpdateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.Data != nil { { size, err := m.Data.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func encodeVarintService(dAtA []byte, offset int, v uint64) int { offset -= sovService(v) base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) return base } func (m *StartWorkflowExecutionRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.ParentExecutionInfo != nil { l = m.ParentExecutionInfo.Size() n += 1 + l + sovService(uint64(l)) } if m.Attempt != 0 { n += 1 + sovService(uint64(m.Attempt)) } if m.ExpirationTime != nil { l = m.ExpirationTime.Size() n += 1 + l + sovService(uint64(l)) } if m.ContinueAsNewInitiator != 0 { n += 1 + sovService(uint64(m.ContinueAsNewInitiator)) } if m.ContinuedFailure != nil { l = m.ContinuedFailure.Size() n += 1 + l + sovService(uint64(l)) } if m.LastCompletionResult != nil { l = m.LastCompletionResult.Size() n += 1 + l + sovService(uint64(l)) } if m.FirstDecisionTaskBackoff != nil { l = m.FirstDecisionTaskBackoff.Size() n += 1 + l + sovService(uint64(l)) } if len(m.PartitionConfig) > 0 { for k, v := range m.PartitionConfig { _ = k _ = v mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + 1 + len(v) + sovService(uint64(len(v))) n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *StartWorkflowExecutionResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.RunId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *SignalWorkflowExecutionRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.ExternalWorkflowExecution != nil { l = m.ExternalWorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.ChildWorkflowOnly { n += 2 } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *SignalWorkflowExecutionResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *SignalWithStartWorkflowExecutionRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if len(m.PartitionConfig) > 0 { for k, v := range m.PartitionConfig { _ = k _ = v mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + 1 + len(v) + sovService(uint64(len(v))) n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *SignalWithStartWorkflowExecutionResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.RunId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ResetWorkflowExecutionRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ResetWorkflowExecutionResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.RunId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *TerminateWorkflowExecutionRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.ExternalWorkflowExecution != nil { l = m.ExternalWorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.ChildWorkflowOnly { n += 2 } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *TerminateWorkflowExecutionResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *DescribeWorkflowExecutionRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *DescribeWorkflowExecutionResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.ExecutionConfiguration != nil { l = m.ExecutionConfiguration.Size() n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecutionInfo != nil { l = m.WorkflowExecutionInfo.Size() n += 1 + l + sovService(uint64(l)) } if len(m.PendingActivities) > 0 { for _, e := range m.PendingActivities { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if len(m.PendingChildren) > 0 { for _, e := range m.PendingChildren { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.PendingDecision != nil { l = m.PendingDecision.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *QueryWorkflowRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *QueryWorkflowResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.QueryResult != nil { l = m.QueryResult.Size() n += 1 + l + sovService(uint64(l)) } if m.QueryRejected != nil { l = m.QueryRejected.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ResetStickyTaskListRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ResetStickyTaskListResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetMutableStateRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.ExpectedNextEventId != 0 { n += 1 + sovService(uint64(m.ExpectedNextEventId)) } l = len(m.CurrentBranchToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.VersionHistoryItem != nil { l = m.VersionHistoryItem.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetMutableStateResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.WorkflowType != nil { l = m.WorkflowType.Size() n += 1 + l + sovService(uint64(l)) } if m.NextEventId != 0 { n += 1 + sovService(uint64(m.NextEventId)) } if m.PreviousStartedEventId != nil { l = m.PreviousStartedEventId.Size() n += 1 + l + sovService(uint64(l)) } if m.LastFirstEventId != 0 { n += 1 + sovService(uint64(m.LastFirstEventId)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.StickyTaskList != nil { l = m.StickyTaskList.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.ClientLibraryVersion) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.ClientFeatureVersion) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.ClientImpl) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.StickyTaskListScheduleToStartTimeout != nil { l = m.StickyTaskListScheduleToStartTimeout.Size() n += 1 + l + sovService(uint64(l)) } if m.EventStoreVersion != 0 { n += 1 + sovService(uint64(m.EventStoreVersion)) } l = len(m.CurrentBranchToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowState != 0 { n += 1 + sovService(uint64(m.WorkflowState)) } if m.WorkflowCloseState != 0 { n += 1 + sovService(uint64(m.WorkflowCloseState)) } if m.VersionHistories != nil { l = m.VersionHistories.Size() n += 2 + l + sovService(uint64(l)) } if m.IsStickyTaskListEnabled { n += 3 } if m.HistorySize != 0 { n += 2 + sovService(uint64(m.HistorySize)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *PollMutableStateRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.ExpectedNextEventId != 0 { n += 1 + sovService(uint64(m.ExpectedNextEventId)) } l = len(m.CurrentBranchToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *PollMutableStateResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.WorkflowType != nil { l = m.WorkflowType.Size() n += 1 + l + sovService(uint64(l)) } if m.NextEventId != 0 { n += 1 + sovService(uint64(m.NextEventId)) } if m.PreviousStartedEventId != nil { l = m.PreviousStartedEventId.Size() n += 1 + l + sovService(uint64(l)) } if m.LastFirstEventId != 0 { n += 1 + sovService(uint64(m.LastFirstEventId)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.StickyTaskList != nil { l = m.StickyTaskList.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.ClientLibraryVersion) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.ClientFeatureVersion) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.ClientImpl) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.StickyTaskListScheduleToStartTimeout != nil { l = m.StickyTaskListScheduleToStartTimeout.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.CurrentBranchToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.VersionHistories != nil { l = m.VersionHistories.Size() n += 1 + l + sovService(uint64(l)) } if m.WorkflowState != 0 { n += 1 + sovService(uint64(m.WorkflowState)) } if m.WorkflowCloseState != 0 { n += 1 + sovService(uint64(m.WorkflowCloseState)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RecordDecisionTaskStartedRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.ScheduleId != 0 { n += 1 + sovService(uint64(m.ScheduleId)) } if m.TaskId != 0 { n += 1 + sovService(uint64(m.TaskId)) } l = len(m.RequestId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.PollRequest != nil { l = m.PollRequest.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RecordDecisionTaskStartedResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.WorkflowType != nil { l = m.WorkflowType.Size() n += 1 + l + sovService(uint64(l)) } if m.PreviousStartedEventId != nil { l = m.PreviousStartedEventId.Size() n += 1 + l + sovService(uint64(l)) } if m.ScheduledEventId != 0 { n += 1 + sovService(uint64(m.ScheduledEventId)) } if m.StartedEventId != 0 { n += 1 + sovService(uint64(m.StartedEventId)) } if m.NextEventId != 0 { n += 1 + sovService(uint64(m.NextEventId)) } if m.Attempt != 0 { n += 1 + sovService(uint64(m.Attempt)) } if m.StickyExecutionEnabled { n += 2 } if m.DecisionInfo != nil { l = m.DecisionInfo.Size() n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecutionTaskList != nil { l = m.WorkflowExecutionTaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.EventStoreVersion != 0 { n += 1 + sovService(uint64(m.EventStoreVersion)) } l = len(m.BranchToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.ScheduledTime != nil { l = m.ScheduledTime.Size() n += 1 + l + sovService(uint64(l)) } if m.StartedTime != nil { l = m.StartedTime.Size() n += 1 + l + sovService(uint64(l)) } if len(m.Queries) > 0 { for k, v := range m.Queries { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovService(uint64(l)) } mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + l n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.HistorySize != 0 { n += 1 + sovService(uint64(m.HistorySize)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RecordActivityTaskStartedRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.ScheduleId != 0 { n += 1 + sovService(uint64(m.ScheduleId)) } if m.TaskId != 0 { n += 1 + sovService(uint64(m.TaskId)) } l = len(m.RequestId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.PollRequest != nil { l = m.PollRequest.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RecordActivityTaskStartedResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.ScheduledEvent != nil { l = m.ScheduledEvent.Size() n += 1 + l + sovService(uint64(l)) } if m.StartedTime != nil { l = m.StartedTime.Size() n += 1 + l + sovService(uint64(l)) } if m.Attempt != 0 { n += 1 + sovService(uint64(m.Attempt)) } if m.ScheduledTimeOfThisAttempt != nil { l = m.ScheduledTimeOfThisAttempt.Size() n += 1 + l + sovService(uint64(l)) } if m.HeartbeatDetails != nil { l = m.HeartbeatDetails.Size() n += 1 + l + sovService(uint64(l)) } if m.WorkflowType != nil { l = m.WorkflowType.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.WorkflowDomain) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondDecisionTaskCompletedRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondDecisionTaskCompletedResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.StartedResponse != nil { l = m.StartedResponse.Size() n += 1 + l + sovService(uint64(l)) } if len(m.ActivitiesToDispatchLocally) > 0 { for k, v := range m.ActivitiesToDispatchLocally { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovService(uint64(l)) } mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + l n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondDecisionTaskFailedRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondDecisionTaskFailedResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RecordActivityTaskHeartbeatRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RecordActivityTaskHeartbeatResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.CancelRequested { n += 2 } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondActivityTaskCompletedRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondActivityTaskCompletedResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondActivityTaskFailedRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondActivityTaskFailedResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondActivityTaskCanceledRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondActivityTaskCanceledResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RemoveSignalMutableStateRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.RequestId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RemoveSignalMutableStateResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RequestCancelWorkflowExecutionRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.CancelRequest != nil { l = m.CancelRequest.Size() n += 1 + l + sovService(uint64(l)) } if m.ExternalExecutionInfo != nil { l = m.ExternalExecutionInfo.Size() n += 1 + l + sovService(uint64(l)) } if m.ChildWorkflowOnly { n += 2 } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RequestCancelWorkflowExecutionResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ScheduleDecisionTaskRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.IsFirstDecision { n += 2 } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ScheduleDecisionTaskResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RecordChildExecutionCompletedRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.InitiatedId != 0 { n += 1 + sovService(uint64(m.InitiatedId)) } if m.CompletedExecution != nil { l = m.CompletedExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.CompletionEvent != nil { l = m.CompletionEvent.Size() n += 1 + l + sovService(uint64(l)) } if m.StartedId != 0 { n += 1 + sovService(uint64(m.StartedId)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RecordChildExecutionCompletedResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ReplicateEventsV2Request) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if len(m.VersionHistoryItems) > 0 { for _, e := range m.VersionHistoryItems { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.Events != nil { l = m.Events.Size() n += 1 + l + sovService(uint64(l)) } if m.NewRunEvents != nil { l = m.NewRunEvents.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ReplicateEventsV2Response) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *SyncShardStatusRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.SourceCluster) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.ShardId != 0 { n += 1 + sovService(uint64(m.ShardId)) } if m.Time != nil { l = m.Time.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *SyncShardStatusResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *SyncActivityRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.Version != 0 { n += 1 + sovService(uint64(m.Version)) } if m.ScheduledId != 0 { n += 1 + sovService(uint64(m.ScheduledId)) } if m.ScheduledTime != nil { l = m.ScheduledTime.Size() n += 1 + l + sovService(uint64(l)) } if m.StartedId != 0 { n += 1 + sovService(uint64(m.StartedId)) } if m.StartedTime != nil { l = m.StartedTime.Size() n += 1 + l + sovService(uint64(l)) } if m.LastHeartbeatTime != nil { l = m.LastHeartbeatTime.Size() n += 1 + l + sovService(uint64(l)) } if m.Details != nil { l = m.Details.Size() n += 1 + l + sovService(uint64(l)) } if m.Attempt != 0 { n += 1 + sovService(uint64(m.Attempt)) } if m.LastFailure != nil { l = m.LastFailure.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.LastWorkerIdentity) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.VersionHistory != nil { l = m.VersionHistory.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *SyncActivityResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *DescribeMutableStateRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *DescribeMutableStateResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.MutableStateInCache) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.MutableStateInDatabase) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *DescribeHistoryHostRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *DescribeHistoryHostResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.NumberOfShards != 0 { n += 1 + sovService(uint64(m.NumberOfShards)) } if len(m.ShardIds) > 0 { l = 0 for _, e := range m.ShardIds { l += sovService(uint64(e)) } n += 1 + sovService(uint64(l)) + l } if m.DomainCache != nil { l = m.DomainCache.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.ShardControllerStatus) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.Address) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *CloseShardRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.ShardId != 0 { n += 1 + sovService(uint64(m.ShardId)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *CloseShardResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RemoveTaskRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.ShardId != 0 { n += 1 + sovService(uint64(m.ShardId)) } if m.TaskType != 0 { n += 1 + sovService(uint64(m.TaskType)) } if m.TaskId != 0 { n += 1 + sovService(uint64(m.TaskId)) } if m.VisibilityTime != nil { l = m.VisibilityTime.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.ClusterName) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RemoveTaskResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ResetQueueRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.ShardId != 0 { n += 1 + sovService(uint64(m.ShardId)) } l = len(m.ClusterName) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.TaskType != 0 { n += 1 + sovService(uint64(m.TaskType)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ResetQueueResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *DescribeQueueRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.ShardId != 0 { n += 1 + sovService(uint64(m.ShardId)) } l = len(m.ClusterName) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.TaskType != 0 { n += 1 + sovService(uint64(m.TaskType)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *DescribeQueueResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.ProcessingQueueStates) > 0 { for _, s := range m.ProcessingQueueStates { l = len(s) n += 1 + l + sovService(uint64(l)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetReplicationMessagesRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Tokens) > 0 { for _, e := range m.Tokens { l = e.Size() n += 1 + l + sovService(uint64(l)) } } l = len(m.ClusterName) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetReplicationMessagesResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.ShardMessages) > 0 { for k, v := range m.ShardMessages { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovService(uint64(l)) } mapEntrySize := 1 + sovService(uint64(k)) + l n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetDLQReplicationMessagesRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.TaskInfos) > 0 { for _, e := range m.TaskInfos { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetDLQReplicationMessagesResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.ReplicationTasks) > 0 { for _, e := range m.ReplicationTasks { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ReapplyEventsRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Domain) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.Events != nil { l = m.Events.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ReapplyEventsResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RefreshWorkflowTasksRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Domain) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RefreshWorkflowTasksResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *CountDLQMessagesRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.ForceFetch { n += 2 } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *CountDLQMessagesResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Entries) > 0 { for _, e := range m.Entries { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ReadDLQMessagesRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Type != 0 { n += 1 + sovService(uint64(m.Type)) } if m.ShardId != 0 { n += 1 + sovService(uint64(m.ShardId)) } l = len(m.SourceCluster) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.InclusiveEndMessageId != nil { l = m.InclusiveEndMessageId.Size() n += 1 + l + sovService(uint64(l)) } if m.PageSize != 0 { n += 1 + sovService(uint64(m.PageSize)) } l = len(m.NextPageToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ReadDLQMessagesResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Type != 0 { n += 1 + sovService(uint64(m.Type)) } if len(m.ReplicationTasks) > 0 { for _, e := range m.ReplicationTasks { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if len(m.ReplicationTasksInfo) > 0 { for _, e := range m.ReplicationTasksInfo { l = e.Size() n += 1 + l + sovService(uint64(l)) } } l = len(m.NextPageToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *PurgeDLQMessagesRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Type != 0 { n += 1 + sovService(uint64(m.Type)) } if m.ShardId != 0 { n += 1 + sovService(uint64(m.ShardId)) } l = len(m.SourceCluster) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.InclusiveEndMessageId != nil { l = m.InclusiveEndMessageId.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *PurgeDLQMessagesResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *MergeDLQMessagesRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Type != 0 { n += 1 + sovService(uint64(m.Type)) } if m.ShardId != 0 { n += 1 + sovService(uint64(m.ShardId)) } l = len(m.SourceCluster) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.InclusiveEndMessageId != nil { l = m.InclusiveEndMessageId.Size() n += 1 + l + sovService(uint64(l)) } if m.PageSize != 0 { n += 1 + sovService(uint64(m.PageSize)) } l = len(m.NextPageToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *MergeDLQMessagesResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.NextPageToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *NotifyFailoverMarkersRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.FailoverMarkerTokens) > 0 { for _, e := range m.FailoverMarkerTokens { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *NotifyFailoverMarkersResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetCrossClusterTasksRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.ShardIds) > 0 { l = 0 for _, e := range m.ShardIds { l += sovService(uint64(e)) } n += 1 + sovService(uint64(l)) + l } l = len(m.TargetCluster) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetCrossClusterTasksResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.TasksByShard) > 0 { for k, v := range m.TasksByShard { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovService(uint64(l)) } mapEntrySize := 1 + sovService(uint64(k)) + l n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if len(m.FailedCauseByShard) > 0 { for k, v := range m.FailedCauseByShard { _ = k _ = v mapEntrySize := 1 + sovService(uint64(k)) + 1 + sovService(uint64(v)) n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondCrossClusterTasksCompletedRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.ShardId != 0 { n += 1 + sovService(uint64(m.ShardId)) } l = len(m.TargetCluster) if l > 0 { n += 1 + l + sovService(uint64(l)) } if len(m.TaskResponses) > 0 { for _, e := range m.TaskResponses { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.FetchNewTasks { n += 2 } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondCrossClusterTasksCompletedResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Tasks != nil { l = m.Tasks.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetFailoverInfoRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetFailoverInfoResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.CompletedShardCount != 0 { n += 1 + sovService(uint64(m.CompletedShardCount)) } if len(m.PendingShards) > 0 { l = 0 for _, e := range m.PendingShards { l += sovService(uint64(e)) } n += 1 + sovService(uint64(l)) + l } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RatelimitUpdateRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Data != nil { l = m.Data.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RatelimitUpdateResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Data != nil { l = m.Data.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func sovService(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozService(x uint64) (n int) { return sovService(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *StartWorkflowExecutionRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: StartWorkflowExecutionRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: StartWorkflowExecutionRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.StartWorkflowExecutionRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ParentExecutionInfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ParentExecutionInfo == nil { m.ParentExecutionInfo = &v1.ParentExecutionInfo{} } if err := m.ParentExecutionInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Attempt", wireType) } m.Attempt = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Attempt |= int32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExpirationTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExpirationTime == nil { m.ExpirationTime = &types.Timestamp{} } if err := m.ExpirationTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ContinueAsNewInitiator", wireType) } m.ContinueAsNewInitiator = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ContinueAsNewInitiator |= v1.ContinueAsNewInitiator(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ContinuedFailure", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ContinuedFailure == nil { m.ContinuedFailure = &v1.Failure{} } if err := m.ContinuedFailure.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastCompletionResult", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.LastCompletionResult == nil { m.LastCompletionResult = &v1.Payload{} } if err := m.LastCompletionResult.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FirstDecisionTaskBackoff", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.FirstDecisionTaskBackoff == nil { m.FirstDecisionTaskBackoff = &types.Duration{} } if err := m.FirstDecisionTaskBackoff.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return ErrInvalidLengthService } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return ErrInvalidLengthService } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.PartitionConfig[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *StartWorkflowExecutionResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: StartWorkflowExecutionResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: StartWorkflowExecutionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RunId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.RunId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SignalWorkflowExecutionRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SignalWorkflowExecutionRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SignalWorkflowExecutionRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.SignalWorkflowExecutionRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExternalWorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExternalWorkflowExecution == nil { m.ExternalWorkflowExecution = &v1.WorkflowExecution{} } if err := m.ExternalWorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ChildWorkflowOnly", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ChildWorkflowOnly = bool(v != 0) default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SignalWorkflowExecutionResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SignalWorkflowExecutionResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SignalWorkflowExecutionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SignalWithStartWorkflowExecutionRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SignalWithStartWorkflowExecutionRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SignalWithStartWorkflowExecutionRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.SignalWithStartWorkflowExecutionRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return ErrInvalidLengthService } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return ErrInvalidLengthService } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.PartitionConfig[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SignalWithStartWorkflowExecutionResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SignalWithStartWorkflowExecutionResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SignalWithStartWorkflowExecutionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RunId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.RunId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResetWorkflowExecutionRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResetWorkflowExecutionRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResetWorkflowExecutionRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.ResetWorkflowExecutionRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResetWorkflowExecutionResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResetWorkflowExecutionResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResetWorkflowExecutionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RunId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.RunId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TerminateWorkflowExecutionRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TerminateWorkflowExecutionRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TerminateWorkflowExecutionRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.TerminateWorkflowExecutionRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExternalWorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExternalWorkflowExecution == nil { m.ExternalWorkflowExecution = &v1.WorkflowExecution{} } if err := m.ExternalWorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ChildWorkflowOnly", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ChildWorkflowOnly = bool(v != 0) default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TerminateWorkflowExecutionResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TerminateWorkflowExecutionResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TerminateWorkflowExecutionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DescribeWorkflowExecutionRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DescribeWorkflowExecutionRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DescribeWorkflowExecutionRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.DescribeWorkflowExecutionRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DescribeWorkflowExecutionResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DescribeWorkflowExecutionResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DescribeWorkflowExecutionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExecutionConfiguration", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExecutionConfiguration == nil { m.ExecutionConfiguration = &v1.WorkflowExecutionConfiguration{} } if err := m.ExecutionConfiguration.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecutionInfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecutionInfo == nil { m.WorkflowExecutionInfo = &v1.WorkflowExecutionInfo{} } if err := m.WorkflowExecutionInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PendingActivities", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.PendingActivities = append(m.PendingActivities, &v1.PendingActivityInfo{}) if err := m.PendingActivities[len(m.PendingActivities)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PendingChildren", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.PendingChildren = append(m.PendingChildren, &v1.PendingChildExecutionInfo{}) if err := m.PendingChildren[len(m.PendingChildren)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PendingDecision", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PendingDecision == nil { m.PendingDecision = &v1.PendingDecisionInfo{} } if err := m.PendingDecision.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *QueryWorkflowRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: QueryWorkflowRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: QueryWorkflowRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.QueryWorkflowRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *QueryWorkflowResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: QueryWorkflowResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: QueryWorkflowResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field QueryResult", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.QueryResult == nil { m.QueryResult = &v1.Payload{} } if err := m.QueryResult.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field QueryRejected", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.QueryRejected == nil { m.QueryRejected = &v1.QueryRejected{} } if err := m.QueryRejected.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResetStickyTaskListRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResetStickyTaskListRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResetStickyTaskListRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.ResetStickyTaskListRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResetStickyTaskListResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResetStickyTaskListResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResetStickyTaskListResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetMutableStateRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetMutableStateRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetMutableStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ExpectedNextEventId", wireType) } m.ExpectedNextEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ExpectedNextEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CurrentBranchToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.CurrentBranchToken = append(m.CurrentBranchToken[:0], dAtA[iNdEx:postIndex]...) if m.CurrentBranchToken == nil { m.CurrentBranchToken = []byte{} } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VersionHistoryItem", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.VersionHistoryItem == nil { m.VersionHistoryItem = &v11.VersionHistoryItem{} } if err := m.VersionHistoryItem.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetMutableStateResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetMutableStateResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetMutableStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowType == nil { m.WorkflowType = &v1.WorkflowType{} } if err := m.WorkflowType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NextEventId", wireType) } m.NextEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NextEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PreviousStartedEventId", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PreviousStartedEventId == nil { m.PreviousStartedEventId = &types.Int64Value{} } if err := m.PreviousStartedEventId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LastFirstEventId", wireType) } m.LastFirstEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.LastFirstEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StickyTaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StickyTaskList == nil { m.StickyTaskList = &v1.TaskList{} } if err := m.StickyTaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClientLibraryVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ClientLibraryVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClientFeatureVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ClientFeatureVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClientImpl", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ClientImpl = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StickyTaskListScheduleToStartTimeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StickyTaskListScheduleToStartTimeout == nil { m.StickyTaskListScheduleToStartTimeout = &types.Duration{} } if err := m.StickyTaskListScheduleToStartTimeout.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EventStoreVersion", wireType) } m.EventStoreVersion = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.EventStoreVersion |= int32(b&0x7F) << shift if b < 0x80 { break } } case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CurrentBranchToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.CurrentBranchToken = append(m.CurrentBranchToken[:0], dAtA[iNdEx:postIndex]...) if m.CurrentBranchToken == nil { m.CurrentBranchToken = []byte{} } iNdEx = postIndex case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowState", wireType) } m.WorkflowState = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.WorkflowState |= v12.WorkflowState(b&0x7F) << shift if b < 0x80 { break } } case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowCloseState", wireType) } m.WorkflowCloseState = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.WorkflowCloseState |= v1.WorkflowExecutionCloseStatus(b&0x7F) << shift if b < 0x80 { break } } case 16: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VersionHistories", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.VersionHistories == nil { m.VersionHistories = &v12.VersionHistories{} } if err := m.VersionHistories.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 17: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IsStickyTaskListEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.IsStickyTaskListEnabled = bool(v != 0) case 18: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HistorySize", wireType) } m.HistorySize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.HistorySize |= int64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PollMutableStateRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PollMutableStateRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PollMutableStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ExpectedNextEventId", wireType) } m.ExpectedNextEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ExpectedNextEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CurrentBranchToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.CurrentBranchToken = append(m.CurrentBranchToken[:0], dAtA[iNdEx:postIndex]...) if m.CurrentBranchToken == nil { m.CurrentBranchToken = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PollMutableStateResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PollMutableStateResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PollMutableStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowType == nil { m.WorkflowType = &v1.WorkflowType{} } if err := m.WorkflowType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NextEventId", wireType) } m.NextEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NextEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PreviousStartedEventId", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PreviousStartedEventId == nil { m.PreviousStartedEventId = &types.Int64Value{} } if err := m.PreviousStartedEventId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LastFirstEventId", wireType) } m.LastFirstEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.LastFirstEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StickyTaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StickyTaskList == nil { m.StickyTaskList = &v1.TaskList{} } if err := m.StickyTaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClientLibraryVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ClientLibraryVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClientFeatureVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ClientFeatureVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClientImpl", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ClientImpl = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StickyTaskListScheduleToStartTimeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StickyTaskListScheduleToStartTimeout == nil { m.StickyTaskListScheduleToStartTimeout = &types.Duration{} } if err := m.StickyTaskListScheduleToStartTimeout.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CurrentBranchToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.CurrentBranchToken = append(m.CurrentBranchToken[:0], dAtA[iNdEx:postIndex]...) if m.CurrentBranchToken == nil { m.CurrentBranchToken = []byte{} } iNdEx = postIndex case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VersionHistories", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.VersionHistories == nil { m.VersionHistories = &v12.VersionHistories{} } if err := m.VersionHistories.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowState", wireType) } m.WorkflowState = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.WorkflowState |= v12.WorkflowState(b&0x7F) << shift if b < 0x80 { break } } case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowCloseState", wireType) } m.WorkflowCloseState = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.WorkflowCloseState |= v1.WorkflowExecutionCloseStatus(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RecordDecisionTaskStartedRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RecordDecisionTaskStartedRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RecordDecisionTaskStartedRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduleId", wireType) } m.ScheduleId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ScheduleId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) } m.TaskId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TaskId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RequestId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.RequestId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PollRequest", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PollRequest == nil { m.PollRequest = &v1.PollForDecisionTaskRequest{} } if err := m.PollRequest.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RecordDecisionTaskStartedResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RecordDecisionTaskStartedResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RecordDecisionTaskStartedResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowType == nil { m.WorkflowType = &v1.WorkflowType{} } if err := m.WorkflowType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PreviousStartedEventId", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PreviousStartedEventId == nil { m.PreviousStartedEventId = &types.Int64Value{} } if err := m.PreviousStartedEventId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledEventId", wireType) } m.ScheduledEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ScheduledEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field StartedEventId", wireType) } m.StartedEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.StartedEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NextEventId", wireType) } m.NextEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NextEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Attempt", wireType) } m.Attempt = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Attempt |= int32(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field StickyExecutionEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.StickyExecutionEnabled = bool(v != 0) case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DecisionInfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.DecisionInfo == nil { m.DecisionInfo = &v12.TransientDecisionInfo{} } if err := m.DecisionInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecutionTaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecutionTaskList == nil { m.WorkflowExecutionTaskList = &v1.TaskList{} } if err := m.WorkflowExecutionTaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EventStoreVersion", wireType) } m.EventStoreVersion = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.EventStoreVersion |= int32(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BranchToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.BranchToken = append(m.BranchToken[:0], dAtA[iNdEx:postIndex]...) if m.BranchToken == nil { m.BranchToken = []byte{} } iNdEx = postIndex case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduledTime == nil { m.ScheduledTime = &types.Timestamp{} } if err := m.ScheduledTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StartedTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartedTime == nil { m.StartedTime = &types.Timestamp{} } if err := m.StartedTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 14: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Queries", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Queries == nil { m.Queries = make(map[string]*v1.WorkflowQuery) } var mapkey string var mapvalue *v1.WorkflowQuery for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthService } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthService } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &v1.WorkflowQuery{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Queries[mapkey] = mapvalue iNdEx = postIndex case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HistorySize", wireType) } m.HistorySize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.HistorySize |= int64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RecordActivityTaskStartedRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RecordActivityTaskStartedRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RecordActivityTaskStartedRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduleId", wireType) } m.ScheduleId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ScheduleId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) } m.TaskId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TaskId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RequestId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.RequestId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PollRequest", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PollRequest == nil { m.PollRequest = &v1.PollForActivityTaskRequest{} } if err := m.PollRequest.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RecordActivityTaskStartedResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RecordActivityTaskStartedResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RecordActivityTaskStartedResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledEvent", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduledEvent == nil { m.ScheduledEvent = &v1.HistoryEvent{} } if err := m.ScheduledEvent.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StartedTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartedTime == nil { m.StartedTime = &types.Timestamp{} } if err := m.StartedTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Attempt", wireType) } m.Attempt = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Attempt |= int32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledTimeOfThisAttempt", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduledTimeOfThisAttempt == nil { m.ScheduledTimeOfThisAttempt = &types.Timestamp{} } if err := m.ScheduledTimeOfThisAttempt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field HeartbeatDetails", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.HeartbeatDetails == nil { m.HeartbeatDetails = &v1.Payload{} } if err := m.HeartbeatDetails.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowType == nil { m.WorkflowType = &v1.WorkflowType{} } if err := m.WorkflowType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowDomain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.WorkflowDomain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondDecisionTaskCompletedRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondDecisionTaskCompletedRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondDecisionTaskCompletedRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.RespondDecisionTaskCompletedRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondDecisionTaskCompletedResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondDecisionTaskCompletedResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondDecisionTaskCompletedResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StartedResponse", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartedResponse == nil { m.StartedResponse = &RecordDecisionTaskStartedResponse{} } if err := m.StartedResponse.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActivitiesToDispatchLocally", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ActivitiesToDispatchLocally == nil { m.ActivitiesToDispatchLocally = make(map[string]*v1.ActivityLocalDispatchInfo) } var mapkey string var mapvalue *v1.ActivityLocalDispatchInfo for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthService } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthService } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &v1.ActivityLocalDispatchInfo{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ActivitiesToDispatchLocally[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondDecisionTaskFailedRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondDecisionTaskFailedRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondDecisionTaskFailedRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.RespondDecisionTaskFailedRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondDecisionTaskFailedResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondDecisionTaskFailedResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondDecisionTaskFailedResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RecordActivityTaskHeartbeatRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RecordActivityTaskHeartbeatRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RecordActivityTaskHeartbeatRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.RecordActivityTaskHeartbeatRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RecordActivityTaskHeartbeatResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RecordActivityTaskHeartbeatResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RecordActivityTaskHeartbeatResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CancelRequested", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.CancelRequested = bool(v != 0) default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondActivityTaskCompletedRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondActivityTaskCompletedRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondActivityTaskCompletedRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.RespondActivityTaskCompletedRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondActivityTaskCompletedResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondActivityTaskCompletedResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondActivityTaskCompletedResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondActivityTaskFailedRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondActivityTaskFailedRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondActivityTaskFailedRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.RespondActivityTaskFailedRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondActivityTaskFailedResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondActivityTaskFailedResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondActivityTaskFailedResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondActivityTaskCanceledRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondActivityTaskCanceledRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondActivityTaskCanceledRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.RespondActivityTaskCanceledRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondActivityTaskCanceledResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondActivityTaskCanceledResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondActivityTaskCanceledResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RemoveSignalMutableStateRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RemoveSignalMutableStateRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RemoveSignalMutableStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RequestId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.RequestId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RemoveSignalMutableStateResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RemoveSignalMutableStateResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RemoveSignalMutableStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RequestCancelWorkflowExecutionRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RequestCancelWorkflowExecutionRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RequestCancelWorkflowExecutionRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CancelRequest", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.CancelRequest == nil { m.CancelRequest = &v1.RequestCancelWorkflowExecutionRequest{} } if err := m.CancelRequest.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExternalExecutionInfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExternalExecutionInfo == nil { m.ExternalExecutionInfo = &v1.ExternalExecutionInfo{} } if err := m.ExternalExecutionInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ChildWorkflowOnly", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ChildWorkflowOnly = bool(v != 0) default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RequestCancelWorkflowExecutionResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RequestCancelWorkflowExecutionResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RequestCancelWorkflowExecutionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ScheduleDecisionTaskRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ScheduleDecisionTaskRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ScheduleDecisionTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IsFirstDecision", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.IsFirstDecision = bool(v != 0) default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ScheduleDecisionTaskResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ScheduleDecisionTaskResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ScheduleDecisionTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RecordChildExecutionCompletedRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RecordChildExecutionCompletedRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RecordChildExecutionCompletedRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field InitiatedId", wireType) } m.InitiatedId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.InitiatedId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CompletedExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.CompletedExecution == nil { m.CompletedExecution = &v1.WorkflowExecution{} } if err := m.CompletedExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CompletionEvent", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.CompletionEvent == nil { m.CompletionEvent = &v1.HistoryEvent{} } if err := m.CompletionEvent.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field StartedId", wireType) } m.StartedId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.StartedId |= int64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RecordChildExecutionCompletedResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RecordChildExecutionCompletedResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RecordChildExecutionCompletedResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ReplicateEventsV2Request) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ReplicateEventsV2Request: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ReplicateEventsV2Request: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VersionHistoryItems", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.VersionHistoryItems = append(m.VersionHistoryItems, &v11.VersionHistoryItem{}) if err := m.VersionHistoryItems[len(m.VersionHistoryItems)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Events == nil { m.Events = &v1.DataBlob{} } if err := m.Events.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NewRunEvents", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.NewRunEvents == nil { m.NewRunEvents = &v1.DataBlob{} } if err := m.NewRunEvents.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ReplicateEventsV2Response) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ReplicateEventsV2Response: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ReplicateEventsV2Response: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SyncShardStatusRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SyncShardStatusRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SyncShardStatusRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SourceCluster", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.SourceCluster = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShardId", wireType) } m.ShardId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShardId |= int32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Time == nil { m.Time = &types.Timestamp{} } if err := m.Time.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SyncShardStatusResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SyncShardStatusResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SyncShardStatusResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SyncActivityRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SyncActivityRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SyncActivityRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } m.Version = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Version |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledId", wireType) } m.ScheduledId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ScheduledId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduledTime == nil { m.ScheduledTime = &types.Timestamp{} } if err := m.ScheduledTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field StartedId", wireType) } m.StartedId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.StartedId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StartedTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartedTime == nil { m.StartedTime = &types.Timestamp{} } if err := m.StartedTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastHeartbeatTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.LastHeartbeatTime == nil { m.LastHeartbeatTime = &types.Timestamp{} } if err := m.LastHeartbeatTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Details", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Details == nil { m.Details = &v1.Payload{} } if err := m.Details.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Attempt", wireType) } m.Attempt = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Attempt |= int32(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastFailure", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.LastFailure == nil { m.LastFailure = &v1.Failure{} } if err := m.LastFailure.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastWorkerIdentity", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.LastWorkerIdentity = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VersionHistory", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.VersionHistory == nil { m.VersionHistory = &v11.VersionHistory{} } if err := m.VersionHistory.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SyncActivityResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SyncActivityResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SyncActivityResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DescribeMutableStateRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DescribeMutableStateRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DescribeMutableStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DescribeMutableStateResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DescribeMutableStateResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DescribeMutableStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MutableStateInCache", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.MutableStateInCache = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MutableStateInDatabase", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.MutableStateInDatabase = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DescribeHistoryHostRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DescribeHistoryHostRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DescribeHistoryHostRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DescribeHistoryHostResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DescribeHistoryHostResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DescribeHistoryHostResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NumberOfShards", wireType) } m.NumberOfShards = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NumberOfShards |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType == 0 { var v int32 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int32(b&0x7F) << shift if b < 0x80 { break } } m.ShardIds = append(m.ShardIds, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + packedLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int var count int for _, integer := range dAtA[iNdEx:postIndex] { if integer < 128 { count++ } } elementCount = count if elementCount != 0 && len(m.ShardIds) == 0 { m.ShardIds = make([]int32, 0, elementCount) } for iNdEx < postIndex { var v int32 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int32(b&0x7F) << shift if b < 0x80 { break } } m.ShardIds = append(m.ShardIds, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field ShardIds", wireType) } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainCache", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.DomainCache == nil { m.DomainCache = &v11.DomainCacheInfo{} } if err := m.DomainCache.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ShardControllerStatus", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ShardControllerStatus = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Address = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CloseShardRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CloseShardRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CloseShardRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShardId", wireType) } m.ShardId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShardId |= int32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CloseShardResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CloseShardResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CloseShardResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RemoveTaskRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RemoveTaskRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RemoveTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShardId", wireType) } m.ShardId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShardId |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TaskType", wireType) } m.TaskType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TaskType |= v11.TaskType(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) } m.TaskId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TaskId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VisibilityTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.VisibilityTime == nil { m.VisibilityTime = &types.Timestamp{} } if err := m.VisibilityTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RemoveTaskResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RemoveTaskResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RemoveTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResetQueueRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResetQueueRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResetQueueRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShardId", wireType) } m.ShardId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShardId |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TaskType", wireType) } m.TaskType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TaskType |= v11.TaskType(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResetQueueResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResetQueueResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResetQueueResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DescribeQueueRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DescribeQueueRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DescribeQueueRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShardId", wireType) } m.ShardId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShardId |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TaskType", wireType) } m.TaskType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TaskType |= v11.TaskType(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DescribeQueueResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DescribeQueueResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DescribeQueueResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProcessingQueueStates", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ProcessingQueueStates = append(m.ProcessingQueueStates, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetReplicationMessagesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetReplicationMessagesRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetReplicationMessagesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Tokens = append(m.Tokens, &v11.ReplicationToken{}) if err := m.Tokens[len(m.Tokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetReplicationMessagesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetReplicationMessagesResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetReplicationMessagesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ShardMessages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ShardMessages == nil { m.ShardMessages = make(map[int32]*v11.ReplicationMessages) } var mapkey int32 var mapvalue *v11.ReplicationMessages for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapkey |= int32(b&0x7F) << shift if b < 0x80 { break } } } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthService } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthService } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &v11.ReplicationMessages{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ShardMessages[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetDLQReplicationMessagesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetDLQReplicationMessagesRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetDLQReplicationMessagesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskInfos", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.TaskInfos = append(m.TaskInfos, &v11.ReplicationTaskInfo{}) if err := m.TaskInfos[len(m.TaskInfos)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetDLQReplicationMessagesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetDLQReplicationMessagesResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetDLQReplicationMessagesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ReplicationTasks", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ReplicationTasks = append(m.ReplicationTasks, &v11.ReplicationTask{}) if err := m.ReplicationTasks[len(m.ReplicationTasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ReapplyEventsRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ReapplyEventsRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ReapplyEventsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Domain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Domain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Events == nil { m.Events = &v1.DataBlob{} } if err := m.Events.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ReapplyEventsResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ReapplyEventsResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ReapplyEventsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RefreshWorkflowTasksRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RefreshWorkflowTasksRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RefreshWorkflowTasksRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Domain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Domain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RefreshWorkflowTasksResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RefreshWorkflowTasksResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RefreshWorkflowTasksResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CountDLQMessagesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CountDLQMessagesRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CountDLQMessagesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ForceFetch", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ForceFetch = bool(v != 0) default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CountDLQMessagesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CountDLQMessagesResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CountDLQMessagesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Entries", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Entries = append(m.Entries, &v11.HistoryDLQCountEntry{}) if err := m.Entries[len(m.Entries)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ReadDLQMessagesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ReadDLQMessagesRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ReadDLQMessagesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= v11.DLQType(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShardId", wireType) } m.ShardId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShardId |= int32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SourceCluster", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.SourceCluster = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InclusiveEndMessageId", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.InclusiveEndMessageId == nil { m.InclusiveEndMessageId = &types.Int64Value{} } if err := m.InclusiveEndMessageId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PageSize", wireType) } m.PageSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PageSize |= int32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NextPageToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.NextPageToken = append(m.NextPageToken[:0], dAtA[iNdEx:postIndex]...) if m.NextPageToken == nil { m.NextPageToken = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ReadDLQMessagesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ReadDLQMessagesResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ReadDLQMessagesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= v11.DLQType(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ReplicationTasks", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ReplicationTasks = append(m.ReplicationTasks, &v11.ReplicationTask{}) if err := m.ReplicationTasks[len(m.ReplicationTasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ReplicationTasksInfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ReplicationTasksInfo = append(m.ReplicationTasksInfo, &v11.ReplicationTaskInfo{}) if err := m.ReplicationTasksInfo[len(m.ReplicationTasksInfo)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NextPageToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.NextPageToken = append(m.NextPageToken[:0], dAtA[iNdEx:postIndex]...) if m.NextPageToken == nil { m.NextPageToken = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PurgeDLQMessagesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PurgeDLQMessagesRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PurgeDLQMessagesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= v11.DLQType(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShardId", wireType) } m.ShardId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShardId |= int32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SourceCluster", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.SourceCluster = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InclusiveEndMessageId", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.InclusiveEndMessageId == nil { m.InclusiveEndMessageId = &types.Int64Value{} } if err := m.InclusiveEndMessageId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PurgeDLQMessagesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PurgeDLQMessagesResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PurgeDLQMessagesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MergeDLQMessagesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MergeDLQMessagesRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MergeDLQMessagesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= v11.DLQType(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShardId", wireType) } m.ShardId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShardId |= int32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SourceCluster", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.SourceCluster = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InclusiveEndMessageId", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.InclusiveEndMessageId == nil { m.InclusiveEndMessageId = &types.Int64Value{} } if err := m.InclusiveEndMessageId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PageSize", wireType) } m.PageSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PageSize |= int32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NextPageToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.NextPageToken = append(m.NextPageToken[:0], dAtA[iNdEx:postIndex]...) if m.NextPageToken == nil { m.NextPageToken = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MergeDLQMessagesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MergeDLQMessagesResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MergeDLQMessagesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NextPageToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.NextPageToken = append(m.NextPageToken[:0], dAtA[iNdEx:postIndex]...) if m.NextPageToken == nil { m.NextPageToken = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NotifyFailoverMarkersRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NotifyFailoverMarkersRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NotifyFailoverMarkersRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FailoverMarkerTokens", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.FailoverMarkerTokens = append(m.FailoverMarkerTokens, &v11.FailoverMarkerToken{}) if err := m.FailoverMarkerTokens[len(m.FailoverMarkerTokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NotifyFailoverMarkersResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NotifyFailoverMarkersResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NotifyFailoverMarkersResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetCrossClusterTasksRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetCrossClusterTasksRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetCrossClusterTasksRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType == 0 { var v int32 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int32(b&0x7F) << shift if b < 0x80 { break } } m.ShardIds = append(m.ShardIds, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + packedLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int var count int for _, integer := range dAtA[iNdEx:postIndex] { if integer < 128 { count++ } } elementCount = count if elementCount != 0 && len(m.ShardIds) == 0 { m.ShardIds = make([]int32, 0, elementCount) } for iNdEx < postIndex { var v int32 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int32(b&0x7F) << shift if b < 0x80 { break } } m.ShardIds = append(m.ShardIds, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field ShardIds", wireType) } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TargetCluster", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.TargetCluster = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetCrossClusterTasksResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetCrossClusterTasksResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetCrossClusterTasksResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TasksByShard", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TasksByShard == nil { m.TasksByShard = make(map[int32]*v11.CrossClusterTaskRequests) } var mapkey int32 var mapvalue *v11.CrossClusterTaskRequests for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapkey |= int32(b&0x7F) << shift if b < 0x80 { break } } } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthService } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthService } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &v11.CrossClusterTaskRequests{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.TasksByShard[mapkey] = mapvalue iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FailedCauseByShard", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.FailedCauseByShard == nil { m.FailedCauseByShard = make(map[int32]v11.GetTaskFailedCause) } var mapkey int32 var mapvalue v11.GetTaskFailedCause for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapkey |= int32(b&0x7F) << shift if b < 0x80 { break } } } else if fieldNum == 2 { for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapvalue |= v11.GetTaskFailedCause(b&0x7F) << shift if b < 0x80 { break } } } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.FailedCauseByShard[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondCrossClusterTasksCompletedRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondCrossClusterTasksCompletedRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondCrossClusterTasksCompletedRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShardId", wireType) } m.ShardId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShardId |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TargetCluster", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.TargetCluster = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskResponses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.TaskResponses = append(m.TaskResponses, &v11.CrossClusterTaskResponse{}) if err := m.TaskResponses[len(m.TaskResponses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FetchNewTasks", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.FetchNewTasks = bool(v != 0) default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondCrossClusterTasksCompletedResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondCrossClusterTasksCompletedResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondCrossClusterTasksCompletedResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Tasks == nil { m.Tasks = &v11.CrossClusterTaskRequests{} } if err := m.Tasks.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetFailoverInfoRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetFailoverInfoRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetFailoverInfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetFailoverInfoResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetFailoverInfoResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetFailoverInfoResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CompletedShardCount", wireType) } m.CompletedShardCount = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CompletedShardCount |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType == 0 { var v int32 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int32(b&0x7F) << shift if b < 0x80 { break } } m.PendingShards = append(m.PendingShards, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + packedLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int var count int for _, integer := range dAtA[iNdEx:postIndex] { if integer < 128 { count++ } } elementCount = count if elementCount != 0 && len(m.PendingShards) == 0 { m.PendingShards = make([]int32, 0, elementCount) } for iNdEx < postIndex { var v int32 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int32(b&0x7F) << shift if b < 0x80 { break } } m.PendingShards = append(m.PendingShards, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field PendingShards", wireType) } default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RatelimitUpdateRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RatelimitUpdateRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RatelimitUpdateRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Data == nil { m.Data = &v12.Any{} } if err := m.Data.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RatelimitUpdateResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RatelimitUpdateResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RatelimitUpdateResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Data == nil { m.Data = &v12.Any{} } if err := m.Data.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func skipService(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowService } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } wireType := int(wire & 0x7) switch wireType { case 0: for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowService } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } iNdEx++ if dAtA[iNdEx-1] < 0x80 { break } } case 1: iNdEx += 8 case 2: var length int for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowService } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { break } } if length < 0 { return 0, ErrInvalidLengthService } iNdEx += length case 3: depth++ case 4: if depth == 0 { return 0, ErrUnexpectedEndOfGroupService } depth-- case 5: iNdEx += 4 default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } if iNdEx < 0 { return 0, ErrInvalidLengthService } if depth == 0 { return iNdEx, nil } } return 0, io.ErrUnexpectedEOF } var ( ErrInvalidLengthService = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowService = fmt.Errorf("proto: integer overflow") ErrUnexpectedEndOfGroupService = fmt.Errorf("proto: unexpected end of group") ) ================================================ FILE: .gen/proto/history/v1/service.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/history/v1/service.proto package historyv1 import ( "context" "io/ioutil" "reflect" "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" "go.uber.org/fx" "go.uber.org/yarpc" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/api/x/restriction" "go.uber.org/yarpc/encoding/protobuf" "go.uber.org/yarpc/encoding/protobuf/reflection" ) var _ = ioutil.NopCloser // HistoryAPIYARPCClient is the YARPC client-side interface for the HistoryAPI service. type HistoryAPIYARPCClient interface { StartWorkflowExecution(context.Context, *StartWorkflowExecutionRequest, ...yarpc.CallOption) (*StartWorkflowExecutionResponse, error) SignalWorkflowExecution(context.Context, *SignalWorkflowExecutionRequest, ...yarpc.CallOption) (*SignalWorkflowExecutionResponse, error) SignalWithStartWorkflowExecution(context.Context, *SignalWithStartWorkflowExecutionRequest, ...yarpc.CallOption) (*SignalWithStartWorkflowExecutionResponse, error) ResetWorkflowExecution(context.Context, *ResetWorkflowExecutionRequest, ...yarpc.CallOption) (*ResetWorkflowExecutionResponse, error) TerminateWorkflowExecution(context.Context, *TerminateWorkflowExecutionRequest, ...yarpc.CallOption) (*TerminateWorkflowExecutionResponse, error) DescribeWorkflowExecution(context.Context, *DescribeWorkflowExecutionRequest, ...yarpc.CallOption) (*DescribeWorkflowExecutionResponse, error) QueryWorkflow(context.Context, *QueryWorkflowRequest, ...yarpc.CallOption) (*QueryWorkflowResponse, error) ResetStickyTaskList(context.Context, *ResetStickyTaskListRequest, ...yarpc.CallOption) (*ResetStickyTaskListResponse, error) GetMutableState(context.Context, *GetMutableStateRequest, ...yarpc.CallOption) (*GetMutableStateResponse, error) PollMutableState(context.Context, *PollMutableStateRequest, ...yarpc.CallOption) (*PollMutableStateResponse, error) RecordDecisionTaskStarted(context.Context, *RecordDecisionTaskStartedRequest, ...yarpc.CallOption) (*RecordDecisionTaskStartedResponse, error) RespondDecisionTaskCompleted(context.Context, *RespondDecisionTaskCompletedRequest, ...yarpc.CallOption) (*RespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed(context.Context, *RespondDecisionTaskFailedRequest, ...yarpc.CallOption) (*RespondDecisionTaskFailedResponse, error) RecordActivityTaskStarted(context.Context, *RecordActivityTaskStartedRequest, ...yarpc.CallOption) (*RecordActivityTaskStartedResponse, error) RespondActivityTaskCompleted(context.Context, *RespondActivityTaskCompletedRequest, ...yarpc.CallOption) (*RespondActivityTaskCompletedResponse, error) RespondActivityTaskFailed(context.Context, *RespondActivityTaskFailedRequest, ...yarpc.CallOption) (*RespondActivityTaskFailedResponse, error) RespondActivityTaskCanceled(context.Context, *RespondActivityTaskCanceledRequest, ...yarpc.CallOption) (*RespondActivityTaskCanceledResponse, error) RecordActivityTaskHeartbeat(context.Context, *RecordActivityTaskHeartbeatRequest, ...yarpc.CallOption) (*RecordActivityTaskHeartbeatResponse, error) RequestCancelWorkflowExecution(context.Context, *RequestCancelWorkflowExecutionRequest, ...yarpc.CallOption) (*RequestCancelWorkflowExecutionResponse, error) RemoveSignalMutableState(context.Context, *RemoveSignalMutableStateRequest, ...yarpc.CallOption) (*RemoveSignalMutableStateResponse, error) ScheduleDecisionTask(context.Context, *ScheduleDecisionTaskRequest, ...yarpc.CallOption) (*ScheduleDecisionTaskResponse, error) RecordChildExecutionCompleted(context.Context, *RecordChildExecutionCompletedRequest, ...yarpc.CallOption) (*RecordChildExecutionCompletedResponse, error) ReplicateEventsV2(context.Context, *ReplicateEventsV2Request, ...yarpc.CallOption) (*ReplicateEventsV2Response, error) SyncShardStatus(context.Context, *SyncShardStatusRequest, ...yarpc.CallOption) (*SyncShardStatusResponse, error) SyncActivity(context.Context, *SyncActivityRequest, ...yarpc.CallOption) (*SyncActivityResponse, error) DescribeMutableState(context.Context, *DescribeMutableStateRequest, ...yarpc.CallOption) (*DescribeMutableStateResponse, error) DescribeHistoryHost(context.Context, *DescribeHistoryHostRequest, ...yarpc.CallOption) (*DescribeHistoryHostResponse, error) CloseShard(context.Context, *CloseShardRequest, ...yarpc.CallOption) (*CloseShardResponse, error) RemoveTask(context.Context, *RemoveTaskRequest, ...yarpc.CallOption) (*RemoveTaskResponse, error) ResetQueue(context.Context, *ResetQueueRequest, ...yarpc.CallOption) (*ResetQueueResponse, error) DescribeQueue(context.Context, *DescribeQueueRequest, ...yarpc.CallOption) (*DescribeQueueResponse, error) GetReplicationMessages(context.Context, *GetReplicationMessagesRequest, ...yarpc.CallOption) (*GetReplicationMessagesResponse, error) GetDLQReplicationMessages(context.Context, *GetDLQReplicationMessagesRequest, ...yarpc.CallOption) (*GetDLQReplicationMessagesResponse, error) ReapplyEvents(context.Context, *ReapplyEventsRequest, ...yarpc.CallOption) (*ReapplyEventsResponse, error) RefreshWorkflowTasks(context.Context, *RefreshWorkflowTasksRequest, ...yarpc.CallOption) (*RefreshWorkflowTasksResponse, error) CountDLQMessages(context.Context, *CountDLQMessagesRequest, ...yarpc.CallOption) (*CountDLQMessagesResponse, error) ReadDLQMessages(context.Context, *ReadDLQMessagesRequest, ...yarpc.CallOption) (*ReadDLQMessagesResponse, error) PurgeDLQMessages(context.Context, *PurgeDLQMessagesRequest, ...yarpc.CallOption) (*PurgeDLQMessagesResponse, error) MergeDLQMessages(context.Context, *MergeDLQMessagesRequest, ...yarpc.CallOption) (*MergeDLQMessagesResponse, error) NotifyFailoverMarkers(context.Context, *NotifyFailoverMarkersRequest, ...yarpc.CallOption) (*NotifyFailoverMarkersResponse, error) GetCrossClusterTasks(context.Context, *GetCrossClusterTasksRequest, ...yarpc.CallOption) (*GetCrossClusterTasksResponse, error) RespondCrossClusterTasksCompleted(context.Context, *RespondCrossClusterTasksCompletedRequest, ...yarpc.CallOption) (*RespondCrossClusterTasksCompletedResponse, error) GetFailoverInfo(context.Context, *GetFailoverInfoRequest, ...yarpc.CallOption) (*GetFailoverInfoResponse, error) RatelimitUpdate(context.Context, *RatelimitUpdateRequest, ...yarpc.CallOption) (*RatelimitUpdateResponse, error) } func newHistoryAPIYARPCClient(clientConfig transport.ClientConfig, anyResolver jsonpb.AnyResolver, options ...protobuf.ClientOption) HistoryAPIYARPCClient { return &_HistoryAPIYARPCCaller{protobuf.NewStreamClient( protobuf.ClientParams{ ServiceName: "uber.cadence.history.v1.HistoryAPI", ClientConfig: clientConfig, AnyResolver: anyResolver, Options: options, }, )} } // NewHistoryAPIYARPCClient builds a new YARPC client for the HistoryAPI service. func NewHistoryAPIYARPCClient(clientConfig transport.ClientConfig, options ...protobuf.ClientOption) HistoryAPIYARPCClient { return newHistoryAPIYARPCClient(clientConfig, nil, options...) } // HistoryAPIYARPCServer is the YARPC server-side interface for the HistoryAPI service. type HistoryAPIYARPCServer interface { StartWorkflowExecution(context.Context, *StartWorkflowExecutionRequest) (*StartWorkflowExecutionResponse, error) SignalWorkflowExecution(context.Context, *SignalWorkflowExecutionRequest) (*SignalWorkflowExecutionResponse, error) SignalWithStartWorkflowExecution(context.Context, *SignalWithStartWorkflowExecutionRequest) (*SignalWithStartWorkflowExecutionResponse, error) ResetWorkflowExecution(context.Context, *ResetWorkflowExecutionRequest) (*ResetWorkflowExecutionResponse, error) TerminateWorkflowExecution(context.Context, *TerminateWorkflowExecutionRequest) (*TerminateWorkflowExecutionResponse, error) DescribeWorkflowExecution(context.Context, *DescribeWorkflowExecutionRequest) (*DescribeWorkflowExecutionResponse, error) QueryWorkflow(context.Context, *QueryWorkflowRequest) (*QueryWorkflowResponse, error) ResetStickyTaskList(context.Context, *ResetStickyTaskListRequest) (*ResetStickyTaskListResponse, error) GetMutableState(context.Context, *GetMutableStateRequest) (*GetMutableStateResponse, error) PollMutableState(context.Context, *PollMutableStateRequest) (*PollMutableStateResponse, error) RecordDecisionTaskStarted(context.Context, *RecordDecisionTaskStartedRequest) (*RecordDecisionTaskStartedResponse, error) RespondDecisionTaskCompleted(context.Context, *RespondDecisionTaskCompletedRequest) (*RespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed(context.Context, *RespondDecisionTaskFailedRequest) (*RespondDecisionTaskFailedResponse, error) RecordActivityTaskStarted(context.Context, *RecordActivityTaskStartedRequest) (*RecordActivityTaskStartedResponse, error) RespondActivityTaskCompleted(context.Context, *RespondActivityTaskCompletedRequest) (*RespondActivityTaskCompletedResponse, error) RespondActivityTaskFailed(context.Context, *RespondActivityTaskFailedRequest) (*RespondActivityTaskFailedResponse, error) RespondActivityTaskCanceled(context.Context, *RespondActivityTaskCanceledRequest) (*RespondActivityTaskCanceledResponse, error) RecordActivityTaskHeartbeat(context.Context, *RecordActivityTaskHeartbeatRequest) (*RecordActivityTaskHeartbeatResponse, error) RequestCancelWorkflowExecution(context.Context, *RequestCancelWorkflowExecutionRequest) (*RequestCancelWorkflowExecutionResponse, error) RemoveSignalMutableState(context.Context, *RemoveSignalMutableStateRequest) (*RemoveSignalMutableStateResponse, error) ScheduleDecisionTask(context.Context, *ScheduleDecisionTaskRequest) (*ScheduleDecisionTaskResponse, error) RecordChildExecutionCompleted(context.Context, *RecordChildExecutionCompletedRequest) (*RecordChildExecutionCompletedResponse, error) ReplicateEventsV2(context.Context, *ReplicateEventsV2Request) (*ReplicateEventsV2Response, error) SyncShardStatus(context.Context, *SyncShardStatusRequest) (*SyncShardStatusResponse, error) SyncActivity(context.Context, *SyncActivityRequest) (*SyncActivityResponse, error) DescribeMutableState(context.Context, *DescribeMutableStateRequest) (*DescribeMutableStateResponse, error) DescribeHistoryHost(context.Context, *DescribeHistoryHostRequest) (*DescribeHistoryHostResponse, error) CloseShard(context.Context, *CloseShardRequest) (*CloseShardResponse, error) RemoveTask(context.Context, *RemoveTaskRequest) (*RemoveTaskResponse, error) ResetQueue(context.Context, *ResetQueueRequest) (*ResetQueueResponse, error) DescribeQueue(context.Context, *DescribeQueueRequest) (*DescribeQueueResponse, error) GetReplicationMessages(context.Context, *GetReplicationMessagesRequest) (*GetReplicationMessagesResponse, error) GetDLQReplicationMessages(context.Context, *GetDLQReplicationMessagesRequest) (*GetDLQReplicationMessagesResponse, error) ReapplyEvents(context.Context, *ReapplyEventsRequest) (*ReapplyEventsResponse, error) RefreshWorkflowTasks(context.Context, *RefreshWorkflowTasksRequest) (*RefreshWorkflowTasksResponse, error) CountDLQMessages(context.Context, *CountDLQMessagesRequest) (*CountDLQMessagesResponse, error) ReadDLQMessages(context.Context, *ReadDLQMessagesRequest) (*ReadDLQMessagesResponse, error) PurgeDLQMessages(context.Context, *PurgeDLQMessagesRequest) (*PurgeDLQMessagesResponse, error) MergeDLQMessages(context.Context, *MergeDLQMessagesRequest) (*MergeDLQMessagesResponse, error) NotifyFailoverMarkers(context.Context, *NotifyFailoverMarkersRequest) (*NotifyFailoverMarkersResponse, error) GetCrossClusterTasks(context.Context, *GetCrossClusterTasksRequest) (*GetCrossClusterTasksResponse, error) RespondCrossClusterTasksCompleted(context.Context, *RespondCrossClusterTasksCompletedRequest) (*RespondCrossClusterTasksCompletedResponse, error) GetFailoverInfo(context.Context, *GetFailoverInfoRequest) (*GetFailoverInfoResponse, error) RatelimitUpdate(context.Context, *RatelimitUpdateRequest) (*RatelimitUpdateResponse, error) } type buildHistoryAPIYARPCProceduresParams struct { Server HistoryAPIYARPCServer AnyResolver jsonpb.AnyResolver } func buildHistoryAPIYARPCProcedures(params buildHistoryAPIYARPCProceduresParams) []transport.Procedure { handler := &_HistoryAPIYARPCHandler{params.Server} return protobuf.BuildProcedures( protobuf.BuildProceduresParams{ ServiceName: "uber.cadence.history.v1.HistoryAPI", UnaryHandlerParams: []protobuf.BuildProceduresUnaryHandlerParams{ { MethodName: "StartWorkflowExecution", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.StartWorkflowExecution, NewRequest: newHistoryAPIServiceStartWorkflowExecutionYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "SignalWorkflowExecution", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.SignalWorkflowExecution, NewRequest: newHistoryAPIServiceSignalWorkflowExecutionYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "SignalWithStartWorkflowExecution", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.SignalWithStartWorkflowExecution, NewRequest: newHistoryAPIServiceSignalWithStartWorkflowExecutionYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "ResetWorkflowExecution", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.ResetWorkflowExecution, NewRequest: newHistoryAPIServiceResetWorkflowExecutionYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "TerminateWorkflowExecution", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.TerminateWorkflowExecution, NewRequest: newHistoryAPIServiceTerminateWorkflowExecutionYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "DescribeWorkflowExecution", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.DescribeWorkflowExecution, NewRequest: newHistoryAPIServiceDescribeWorkflowExecutionYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "QueryWorkflow", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.QueryWorkflow, NewRequest: newHistoryAPIServiceQueryWorkflowYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "ResetStickyTaskList", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.ResetStickyTaskList, NewRequest: newHistoryAPIServiceResetStickyTaskListYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "GetMutableState", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.GetMutableState, NewRequest: newHistoryAPIServiceGetMutableStateYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "PollMutableState", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.PollMutableState, NewRequest: newHistoryAPIServicePollMutableStateYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RecordDecisionTaskStarted", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RecordDecisionTaskStarted, NewRequest: newHistoryAPIServiceRecordDecisionTaskStartedYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RespondDecisionTaskCompleted", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RespondDecisionTaskCompleted, NewRequest: newHistoryAPIServiceRespondDecisionTaskCompletedYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RespondDecisionTaskFailed", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RespondDecisionTaskFailed, NewRequest: newHistoryAPIServiceRespondDecisionTaskFailedYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RecordActivityTaskStarted", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RecordActivityTaskStarted, NewRequest: newHistoryAPIServiceRecordActivityTaskStartedYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RespondActivityTaskCompleted", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RespondActivityTaskCompleted, NewRequest: newHistoryAPIServiceRespondActivityTaskCompletedYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RespondActivityTaskFailed", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RespondActivityTaskFailed, NewRequest: newHistoryAPIServiceRespondActivityTaskFailedYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RespondActivityTaskCanceled", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RespondActivityTaskCanceled, NewRequest: newHistoryAPIServiceRespondActivityTaskCanceledYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RecordActivityTaskHeartbeat", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RecordActivityTaskHeartbeat, NewRequest: newHistoryAPIServiceRecordActivityTaskHeartbeatYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RequestCancelWorkflowExecution", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RequestCancelWorkflowExecution, NewRequest: newHistoryAPIServiceRequestCancelWorkflowExecutionYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RemoveSignalMutableState", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RemoveSignalMutableState, NewRequest: newHistoryAPIServiceRemoveSignalMutableStateYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "ScheduleDecisionTask", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.ScheduleDecisionTask, NewRequest: newHistoryAPIServiceScheduleDecisionTaskYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RecordChildExecutionCompleted", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RecordChildExecutionCompleted, NewRequest: newHistoryAPIServiceRecordChildExecutionCompletedYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "ReplicateEventsV2", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.ReplicateEventsV2, NewRequest: newHistoryAPIServiceReplicateEventsV2YARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "SyncShardStatus", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.SyncShardStatus, NewRequest: newHistoryAPIServiceSyncShardStatusYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "SyncActivity", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.SyncActivity, NewRequest: newHistoryAPIServiceSyncActivityYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "DescribeMutableState", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.DescribeMutableState, NewRequest: newHistoryAPIServiceDescribeMutableStateYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "DescribeHistoryHost", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.DescribeHistoryHost, NewRequest: newHistoryAPIServiceDescribeHistoryHostYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "CloseShard", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.CloseShard, NewRequest: newHistoryAPIServiceCloseShardYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RemoveTask", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RemoveTask, NewRequest: newHistoryAPIServiceRemoveTaskYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "ResetQueue", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.ResetQueue, NewRequest: newHistoryAPIServiceResetQueueYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "DescribeQueue", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.DescribeQueue, NewRequest: newHistoryAPIServiceDescribeQueueYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "GetReplicationMessages", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.GetReplicationMessages, NewRequest: newHistoryAPIServiceGetReplicationMessagesYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "GetDLQReplicationMessages", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.GetDLQReplicationMessages, NewRequest: newHistoryAPIServiceGetDLQReplicationMessagesYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "ReapplyEvents", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.ReapplyEvents, NewRequest: newHistoryAPIServiceReapplyEventsYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RefreshWorkflowTasks", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RefreshWorkflowTasks, NewRequest: newHistoryAPIServiceRefreshWorkflowTasksYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "CountDLQMessages", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.CountDLQMessages, NewRequest: newHistoryAPIServiceCountDLQMessagesYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "ReadDLQMessages", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.ReadDLQMessages, NewRequest: newHistoryAPIServiceReadDLQMessagesYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "PurgeDLQMessages", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.PurgeDLQMessages, NewRequest: newHistoryAPIServicePurgeDLQMessagesYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "MergeDLQMessages", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.MergeDLQMessages, NewRequest: newHistoryAPIServiceMergeDLQMessagesYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "NotifyFailoverMarkers", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.NotifyFailoverMarkers, NewRequest: newHistoryAPIServiceNotifyFailoverMarkersYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "GetCrossClusterTasks", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.GetCrossClusterTasks, NewRequest: newHistoryAPIServiceGetCrossClusterTasksYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RespondCrossClusterTasksCompleted", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RespondCrossClusterTasksCompleted, NewRequest: newHistoryAPIServiceRespondCrossClusterTasksCompletedYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "GetFailoverInfo", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.GetFailoverInfo, NewRequest: newHistoryAPIServiceGetFailoverInfoYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RatelimitUpdate", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RatelimitUpdate, NewRequest: newHistoryAPIServiceRatelimitUpdateYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, }, OnewayHandlerParams: []protobuf.BuildProceduresOnewayHandlerParams{}, StreamHandlerParams: []protobuf.BuildProceduresStreamHandlerParams{}, }, ) } // BuildHistoryAPIYARPCProcedures prepares an implementation of the HistoryAPI service for YARPC registration. func BuildHistoryAPIYARPCProcedures(server HistoryAPIYARPCServer) []transport.Procedure { return buildHistoryAPIYARPCProcedures(buildHistoryAPIYARPCProceduresParams{Server: server}) } // FxHistoryAPIYARPCClientParams defines the input // for NewFxHistoryAPIYARPCClient. It provides the // paramaters to get a HistoryAPIYARPCClient in an // Fx application. type FxHistoryAPIYARPCClientParams struct { fx.In Provider yarpc.ClientConfig AnyResolver jsonpb.AnyResolver `name:"yarpcfx" optional:"true"` Restriction restriction.Checker `optional:"true"` } // FxHistoryAPIYARPCClientResult defines the output // of NewFxHistoryAPIYARPCClient. It provides a // HistoryAPIYARPCClient to an Fx application. type FxHistoryAPIYARPCClientResult struct { fx.Out Client HistoryAPIYARPCClient // We are using an fx.Out struct here instead of just returning a client // so that we can add more values or add named versions of the client in // the future without breaking any existing code. } // NewFxHistoryAPIYARPCClient provides a HistoryAPIYARPCClient // to an Fx application using the given name for routing. // // fx.Provide( // historyv1.NewFxHistoryAPIYARPCClient("service-name"), // ... // ) func NewFxHistoryAPIYARPCClient(name string, options ...protobuf.ClientOption) interface{} { return func(params FxHistoryAPIYARPCClientParams) FxHistoryAPIYARPCClientResult { cc := params.Provider.ClientConfig(name) if params.Restriction != nil { if namer, ok := cc.GetUnaryOutbound().(transport.Namer); ok { if err := params.Restriction.Check(protobuf.Encoding, namer.TransportName()); err != nil { panic(err.Error()) } } } return FxHistoryAPIYARPCClientResult{ Client: newHistoryAPIYARPCClient(cc, params.AnyResolver, options...), } } } // FxHistoryAPIYARPCProceduresParams defines the input // for NewFxHistoryAPIYARPCProcedures. It provides the // paramaters to get HistoryAPIYARPCServer procedures in an // Fx application. type FxHistoryAPIYARPCProceduresParams struct { fx.In Server HistoryAPIYARPCServer AnyResolver jsonpb.AnyResolver `name:"yarpcfx" optional:"true"` } // FxHistoryAPIYARPCProceduresResult defines the output // of NewFxHistoryAPIYARPCProcedures. It provides // HistoryAPIYARPCServer procedures to an Fx application. // // The procedures are provided to the "yarpcfx" value group. // Dig 1.2 or newer must be used for this feature to work. type FxHistoryAPIYARPCProceduresResult struct { fx.Out Procedures []transport.Procedure `group:"yarpcfx"` ReflectionMeta reflection.ServerMeta `group:"yarpcfx"` } // NewFxHistoryAPIYARPCProcedures provides HistoryAPIYARPCServer procedures to an Fx application. // It expects a HistoryAPIYARPCServer to be present in the container. // // fx.Provide( // historyv1.NewFxHistoryAPIYARPCProcedures(), // ... // ) func NewFxHistoryAPIYARPCProcedures() interface{} { return func(params FxHistoryAPIYARPCProceduresParams) FxHistoryAPIYARPCProceduresResult { return FxHistoryAPIYARPCProceduresResult{ Procedures: buildHistoryAPIYARPCProcedures(buildHistoryAPIYARPCProceduresParams{ Server: params.Server, AnyResolver: params.AnyResolver, }), ReflectionMeta: HistoryAPIReflectionMeta, } } } // HistoryAPIReflectionMeta is the reflection server metadata // required for using the gRPC reflection protocol with YARPC. // // See https://github.com/grpc/grpc/blob/master/doc/server-reflection.md. var HistoryAPIReflectionMeta = reflection.ServerMeta{ ServiceName: "uber.cadence.history.v1.HistoryAPI", FileDescriptors: yarpcFileDescriptorClosurefee8ff76963a38ed, } type _HistoryAPIYARPCCaller struct { streamClient protobuf.StreamClient } func (c *_HistoryAPIYARPCCaller) StartWorkflowExecution(ctx context.Context, request *StartWorkflowExecutionRequest, options ...yarpc.CallOption) (*StartWorkflowExecutionResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "StartWorkflowExecution", request, newHistoryAPIServiceStartWorkflowExecutionYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*StartWorkflowExecutionResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceStartWorkflowExecutionYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) SignalWorkflowExecution(ctx context.Context, request *SignalWorkflowExecutionRequest, options ...yarpc.CallOption) (*SignalWorkflowExecutionResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "SignalWorkflowExecution", request, newHistoryAPIServiceSignalWorkflowExecutionYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*SignalWorkflowExecutionResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceSignalWorkflowExecutionYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) SignalWithStartWorkflowExecution(ctx context.Context, request *SignalWithStartWorkflowExecutionRequest, options ...yarpc.CallOption) (*SignalWithStartWorkflowExecutionResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "SignalWithStartWorkflowExecution", request, newHistoryAPIServiceSignalWithStartWorkflowExecutionYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*SignalWithStartWorkflowExecutionResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceSignalWithStartWorkflowExecutionYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) ResetWorkflowExecution(ctx context.Context, request *ResetWorkflowExecutionRequest, options ...yarpc.CallOption) (*ResetWorkflowExecutionResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "ResetWorkflowExecution", request, newHistoryAPIServiceResetWorkflowExecutionYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*ResetWorkflowExecutionResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceResetWorkflowExecutionYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) TerminateWorkflowExecution(ctx context.Context, request *TerminateWorkflowExecutionRequest, options ...yarpc.CallOption) (*TerminateWorkflowExecutionResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "TerminateWorkflowExecution", request, newHistoryAPIServiceTerminateWorkflowExecutionYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*TerminateWorkflowExecutionResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceTerminateWorkflowExecutionYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) DescribeWorkflowExecution(ctx context.Context, request *DescribeWorkflowExecutionRequest, options ...yarpc.CallOption) (*DescribeWorkflowExecutionResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "DescribeWorkflowExecution", request, newHistoryAPIServiceDescribeWorkflowExecutionYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*DescribeWorkflowExecutionResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceDescribeWorkflowExecutionYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) QueryWorkflow(ctx context.Context, request *QueryWorkflowRequest, options ...yarpc.CallOption) (*QueryWorkflowResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "QueryWorkflow", request, newHistoryAPIServiceQueryWorkflowYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*QueryWorkflowResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceQueryWorkflowYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) ResetStickyTaskList(ctx context.Context, request *ResetStickyTaskListRequest, options ...yarpc.CallOption) (*ResetStickyTaskListResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "ResetStickyTaskList", request, newHistoryAPIServiceResetStickyTaskListYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*ResetStickyTaskListResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceResetStickyTaskListYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) GetMutableState(ctx context.Context, request *GetMutableStateRequest, options ...yarpc.CallOption) (*GetMutableStateResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "GetMutableState", request, newHistoryAPIServiceGetMutableStateYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*GetMutableStateResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceGetMutableStateYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) PollMutableState(ctx context.Context, request *PollMutableStateRequest, options ...yarpc.CallOption) (*PollMutableStateResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "PollMutableState", request, newHistoryAPIServicePollMutableStateYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*PollMutableStateResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServicePollMutableStateYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RecordDecisionTaskStarted(ctx context.Context, request *RecordDecisionTaskStartedRequest, options ...yarpc.CallOption) (*RecordDecisionTaskStartedResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RecordDecisionTaskStarted", request, newHistoryAPIServiceRecordDecisionTaskStartedYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RecordDecisionTaskStartedResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRecordDecisionTaskStartedYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RespondDecisionTaskCompleted(ctx context.Context, request *RespondDecisionTaskCompletedRequest, options ...yarpc.CallOption) (*RespondDecisionTaskCompletedResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RespondDecisionTaskCompleted", request, newHistoryAPIServiceRespondDecisionTaskCompletedYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RespondDecisionTaskCompletedResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondDecisionTaskCompletedYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RespondDecisionTaskFailed(ctx context.Context, request *RespondDecisionTaskFailedRequest, options ...yarpc.CallOption) (*RespondDecisionTaskFailedResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RespondDecisionTaskFailed", request, newHistoryAPIServiceRespondDecisionTaskFailedYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RespondDecisionTaskFailedResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondDecisionTaskFailedYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RecordActivityTaskStarted(ctx context.Context, request *RecordActivityTaskStartedRequest, options ...yarpc.CallOption) (*RecordActivityTaskStartedResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RecordActivityTaskStarted", request, newHistoryAPIServiceRecordActivityTaskStartedYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RecordActivityTaskStartedResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRecordActivityTaskStartedYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RespondActivityTaskCompleted(ctx context.Context, request *RespondActivityTaskCompletedRequest, options ...yarpc.CallOption) (*RespondActivityTaskCompletedResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RespondActivityTaskCompleted", request, newHistoryAPIServiceRespondActivityTaskCompletedYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RespondActivityTaskCompletedResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondActivityTaskCompletedYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RespondActivityTaskFailed(ctx context.Context, request *RespondActivityTaskFailedRequest, options ...yarpc.CallOption) (*RespondActivityTaskFailedResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RespondActivityTaskFailed", request, newHistoryAPIServiceRespondActivityTaskFailedYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RespondActivityTaskFailedResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondActivityTaskFailedYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RespondActivityTaskCanceled(ctx context.Context, request *RespondActivityTaskCanceledRequest, options ...yarpc.CallOption) (*RespondActivityTaskCanceledResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RespondActivityTaskCanceled", request, newHistoryAPIServiceRespondActivityTaskCanceledYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RespondActivityTaskCanceledResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondActivityTaskCanceledYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RecordActivityTaskHeartbeat(ctx context.Context, request *RecordActivityTaskHeartbeatRequest, options ...yarpc.CallOption) (*RecordActivityTaskHeartbeatResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RecordActivityTaskHeartbeat", request, newHistoryAPIServiceRecordActivityTaskHeartbeatYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RecordActivityTaskHeartbeatResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRecordActivityTaskHeartbeatYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RequestCancelWorkflowExecution(ctx context.Context, request *RequestCancelWorkflowExecutionRequest, options ...yarpc.CallOption) (*RequestCancelWorkflowExecutionResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RequestCancelWorkflowExecution", request, newHistoryAPIServiceRequestCancelWorkflowExecutionYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RequestCancelWorkflowExecutionResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRequestCancelWorkflowExecutionYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RemoveSignalMutableState(ctx context.Context, request *RemoveSignalMutableStateRequest, options ...yarpc.CallOption) (*RemoveSignalMutableStateResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RemoveSignalMutableState", request, newHistoryAPIServiceRemoveSignalMutableStateYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RemoveSignalMutableStateResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRemoveSignalMutableStateYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) ScheduleDecisionTask(ctx context.Context, request *ScheduleDecisionTaskRequest, options ...yarpc.CallOption) (*ScheduleDecisionTaskResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "ScheduleDecisionTask", request, newHistoryAPIServiceScheduleDecisionTaskYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*ScheduleDecisionTaskResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceScheduleDecisionTaskYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RecordChildExecutionCompleted(ctx context.Context, request *RecordChildExecutionCompletedRequest, options ...yarpc.CallOption) (*RecordChildExecutionCompletedResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RecordChildExecutionCompleted", request, newHistoryAPIServiceRecordChildExecutionCompletedYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RecordChildExecutionCompletedResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRecordChildExecutionCompletedYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) ReplicateEventsV2(ctx context.Context, request *ReplicateEventsV2Request, options ...yarpc.CallOption) (*ReplicateEventsV2Response, error) { responseMessage, err := c.streamClient.Call(ctx, "ReplicateEventsV2", request, newHistoryAPIServiceReplicateEventsV2YARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*ReplicateEventsV2Response) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceReplicateEventsV2YARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) SyncShardStatus(ctx context.Context, request *SyncShardStatusRequest, options ...yarpc.CallOption) (*SyncShardStatusResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "SyncShardStatus", request, newHistoryAPIServiceSyncShardStatusYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*SyncShardStatusResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceSyncShardStatusYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) SyncActivity(ctx context.Context, request *SyncActivityRequest, options ...yarpc.CallOption) (*SyncActivityResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "SyncActivity", request, newHistoryAPIServiceSyncActivityYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*SyncActivityResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceSyncActivityYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) DescribeMutableState(ctx context.Context, request *DescribeMutableStateRequest, options ...yarpc.CallOption) (*DescribeMutableStateResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "DescribeMutableState", request, newHistoryAPIServiceDescribeMutableStateYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*DescribeMutableStateResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceDescribeMutableStateYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) DescribeHistoryHost(ctx context.Context, request *DescribeHistoryHostRequest, options ...yarpc.CallOption) (*DescribeHistoryHostResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "DescribeHistoryHost", request, newHistoryAPIServiceDescribeHistoryHostYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*DescribeHistoryHostResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceDescribeHistoryHostYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) CloseShard(ctx context.Context, request *CloseShardRequest, options ...yarpc.CallOption) (*CloseShardResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "CloseShard", request, newHistoryAPIServiceCloseShardYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*CloseShardResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceCloseShardYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RemoveTask(ctx context.Context, request *RemoveTaskRequest, options ...yarpc.CallOption) (*RemoveTaskResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RemoveTask", request, newHistoryAPIServiceRemoveTaskYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RemoveTaskResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRemoveTaskYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) ResetQueue(ctx context.Context, request *ResetQueueRequest, options ...yarpc.CallOption) (*ResetQueueResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "ResetQueue", request, newHistoryAPIServiceResetQueueYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*ResetQueueResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceResetQueueYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) DescribeQueue(ctx context.Context, request *DescribeQueueRequest, options ...yarpc.CallOption) (*DescribeQueueResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "DescribeQueue", request, newHistoryAPIServiceDescribeQueueYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*DescribeQueueResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceDescribeQueueYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) GetReplicationMessages(ctx context.Context, request *GetReplicationMessagesRequest, options ...yarpc.CallOption) (*GetReplicationMessagesResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "GetReplicationMessages", request, newHistoryAPIServiceGetReplicationMessagesYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*GetReplicationMessagesResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceGetReplicationMessagesYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) GetDLQReplicationMessages(ctx context.Context, request *GetDLQReplicationMessagesRequest, options ...yarpc.CallOption) (*GetDLQReplicationMessagesResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "GetDLQReplicationMessages", request, newHistoryAPIServiceGetDLQReplicationMessagesYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*GetDLQReplicationMessagesResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceGetDLQReplicationMessagesYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) ReapplyEvents(ctx context.Context, request *ReapplyEventsRequest, options ...yarpc.CallOption) (*ReapplyEventsResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "ReapplyEvents", request, newHistoryAPIServiceReapplyEventsYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*ReapplyEventsResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceReapplyEventsYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RefreshWorkflowTasks(ctx context.Context, request *RefreshWorkflowTasksRequest, options ...yarpc.CallOption) (*RefreshWorkflowTasksResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RefreshWorkflowTasks", request, newHistoryAPIServiceRefreshWorkflowTasksYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RefreshWorkflowTasksResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRefreshWorkflowTasksYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) CountDLQMessages(ctx context.Context, request *CountDLQMessagesRequest, options ...yarpc.CallOption) (*CountDLQMessagesResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "CountDLQMessages", request, newHistoryAPIServiceCountDLQMessagesYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*CountDLQMessagesResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceCountDLQMessagesYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) ReadDLQMessages(ctx context.Context, request *ReadDLQMessagesRequest, options ...yarpc.CallOption) (*ReadDLQMessagesResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "ReadDLQMessages", request, newHistoryAPIServiceReadDLQMessagesYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*ReadDLQMessagesResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceReadDLQMessagesYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) PurgeDLQMessages(ctx context.Context, request *PurgeDLQMessagesRequest, options ...yarpc.CallOption) (*PurgeDLQMessagesResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "PurgeDLQMessages", request, newHistoryAPIServicePurgeDLQMessagesYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*PurgeDLQMessagesResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServicePurgeDLQMessagesYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) MergeDLQMessages(ctx context.Context, request *MergeDLQMessagesRequest, options ...yarpc.CallOption) (*MergeDLQMessagesResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "MergeDLQMessages", request, newHistoryAPIServiceMergeDLQMessagesYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*MergeDLQMessagesResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceMergeDLQMessagesYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) NotifyFailoverMarkers(ctx context.Context, request *NotifyFailoverMarkersRequest, options ...yarpc.CallOption) (*NotifyFailoverMarkersResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "NotifyFailoverMarkers", request, newHistoryAPIServiceNotifyFailoverMarkersYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*NotifyFailoverMarkersResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceNotifyFailoverMarkersYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) GetCrossClusterTasks(ctx context.Context, request *GetCrossClusterTasksRequest, options ...yarpc.CallOption) (*GetCrossClusterTasksResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "GetCrossClusterTasks", request, newHistoryAPIServiceGetCrossClusterTasksYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*GetCrossClusterTasksResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceGetCrossClusterTasksYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RespondCrossClusterTasksCompleted(ctx context.Context, request *RespondCrossClusterTasksCompletedRequest, options ...yarpc.CallOption) (*RespondCrossClusterTasksCompletedResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RespondCrossClusterTasksCompleted", request, newHistoryAPIServiceRespondCrossClusterTasksCompletedYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RespondCrossClusterTasksCompletedResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondCrossClusterTasksCompletedYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) GetFailoverInfo(ctx context.Context, request *GetFailoverInfoRequest, options ...yarpc.CallOption) (*GetFailoverInfoResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "GetFailoverInfo", request, newHistoryAPIServiceGetFailoverInfoYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*GetFailoverInfoResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceGetFailoverInfoYARPCResponse, responseMessage) } return response, err } func (c *_HistoryAPIYARPCCaller) RatelimitUpdate(ctx context.Context, request *RatelimitUpdateRequest, options ...yarpc.CallOption) (*RatelimitUpdateResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RatelimitUpdate", request, newHistoryAPIServiceRatelimitUpdateYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RatelimitUpdateResponse) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRatelimitUpdateYARPCResponse, responseMessage) } return response, err } type _HistoryAPIYARPCHandler struct { server HistoryAPIYARPCServer } func (h *_HistoryAPIYARPCHandler) StartWorkflowExecution(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *StartWorkflowExecutionRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*StartWorkflowExecutionRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceStartWorkflowExecutionYARPCRequest, requestMessage) } } response, err := h.server.StartWorkflowExecution(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) SignalWorkflowExecution(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *SignalWorkflowExecutionRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*SignalWorkflowExecutionRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceSignalWorkflowExecutionYARPCRequest, requestMessage) } } response, err := h.server.SignalWorkflowExecution(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) SignalWithStartWorkflowExecution(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *SignalWithStartWorkflowExecutionRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*SignalWithStartWorkflowExecutionRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceSignalWithStartWorkflowExecutionYARPCRequest, requestMessage) } } response, err := h.server.SignalWithStartWorkflowExecution(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) ResetWorkflowExecution(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *ResetWorkflowExecutionRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*ResetWorkflowExecutionRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceResetWorkflowExecutionYARPCRequest, requestMessage) } } response, err := h.server.ResetWorkflowExecution(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) TerminateWorkflowExecution(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *TerminateWorkflowExecutionRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*TerminateWorkflowExecutionRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceTerminateWorkflowExecutionYARPCRequest, requestMessage) } } response, err := h.server.TerminateWorkflowExecution(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) DescribeWorkflowExecution(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *DescribeWorkflowExecutionRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*DescribeWorkflowExecutionRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceDescribeWorkflowExecutionYARPCRequest, requestMessage) } } response, err := h.server.DescribeWorkflowExecution(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) QueryWorkflow(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *QueryWorkflowRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*QueryWorkflowRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceQueryWorkflowYARPCRequest, requestMessage) } } response, err := h.server.QueryWorkflow(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) ResetStickyTaskList(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *ResetStickyTaskListRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*ResetStickyTaskListRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceResetStickyTaskListYARPCRequest, requestMessage) } } response, err := h.server.ResetStickyTaskList(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) GetMutableState(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *GetMutableStateRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*GetMutableStateRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceGetMutableStateYARPCRequest, requestMessage) } } response, err := h.server.GetMutableState(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) PollMutableState(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *PollMutableStateRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*PollMutableStateRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServicePollMutableStateYARPCRequest, requestMessage) } } response, err := h.server.PollMutableState(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RecordDecisionTaskStarted(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RecordDecisionTaskStartedRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RecordDecisionTaskStartedRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRecordDecisionTaskStartedYARPCRequest, requestMessage) } } response, err := h.server.RecordDecisionTaskStarted(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RespondDecisionTaskCompleted(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RespondDecisionTaskCompletedRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RespondDecisionTaskCompletedRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondDecisionTaskCompletedYARPCRequest, requestMessage) } } response, err := h.server.RespondDecisionTaskCompleted(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RespondDecisionTaskFailed(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RespondDecisionTaskFailedRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RespondDecisionTaskFailedRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondDecisionTaskFailedYARPCRequest, requestMessage) } } response, err := h.server.RespondDecisionTaskFailed(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RecordActivityTaskStarted(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RecordActivityTaskStartedRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RecordActivityTaskStartedRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRecordActivityTaskStartedYARPCRequest, requestMessage) } } response, err := h.server.RecordActivityTaskStarted(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RespondActivityTaskCompleted(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RespondActivityTaskCompletedRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RespondActivityTaskCompletedRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondActivityTaskCompletedYARPCRequest, requestMessage) } } response, err := h.server.RespondActivityTaskCompleted(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RespondActivityTaskFailed(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RespondActivityTaskFailedRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RespondActivityTaskFailedRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondActivityTaskFailedYARPCRequest, requestMessage) } } response, err := h.server.RespondActivityTaskFailed(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RespondActivityTaskCanceled(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RespondActivityTaskCanceledRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RespondActivityTaskCanceledRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondActivityTaskCanceledYARPCRequest, requestMessage) } } response, err := h.server.RespondActivityTaskCanceled(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RecordActivityTaskHeartbeat(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RecordActivityTaskHeartbeatRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RecordActivityTaskHeartbeatRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRecordActivityTaskHeartbeatYARPCRequest, requestMessage) } } response, err := h.server.RecordActivityTaskHeartbeat(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RequestCancelWorkflowExecution(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RequestCancelWorkflowExecutionRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RequestCancelWorkflowExecutionRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRequestCancelWorkflowExecutionYARPCRequest, requestMessage) } } response, err := h.server.RequestCancelWorkflowExecution(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RemoveSignalMutableState(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RemoveSignalMutableStateRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RemoveSignalMutableStateRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRemoveSignalMutableStateYARPCRequest, requestMessage) } } response, err := h.server.RemoveSignalMutableState(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) ScheduleDecisionTask(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *ScheduleDecisionTaskRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*ScheduleDecisionTaskRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceScheduleDecisionTaskYARPCRequest, requestMessage) } } response, err := h.server.ScheduleDecisionTask(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RecordChildExecutionCompleted(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RecordChildExecutionCompletedRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RecordChildExecutionCompletedRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRecordChildExecutionCompletedYARPCRequest, requestMessage) } } response, err := h.server.RecordChildExecutionCompleted(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) ReplicateEventsV2(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *ReplicateEventsV2Request var ok bool if requestMessage != nil { request, ok = requestMessage.(*ReplicateEventsV2Request) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceReplicateEventsV2YARPCRequest, requestMessage) } } response, err := h.server.ReplicateEventsV2(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) SyncShardStatus(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *SyncShardStatusRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*SyncShardStatusRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceSyncShardStatusYARPCRequest, requestMessage) } } response, err := h.server.SyncShardStatus(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) SyncActivity(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *SyncActivityRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*SyncActivityRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceSyncActivityYARPCRequest, requestMessage) } } response, err := h.server.SyncActivity(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) DescribeMutableState(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *DescribeMutableStateRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*DescribeMutableStateRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceDescribeMutableStateYARPCRequest, requestMessage) } } response, err := h.server.DescribeMutableState(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) DescribeHistoryHost(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *DescribeHistoryHostRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*DescribeHistoryHostRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceDescribeHistoryHostYARPCRequest, requestMessage) } } response, err := h.server.DescribeHistoryHost(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) CloseShard(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *CloseShardRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*CloseShardRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceCloseShardYARPCRequest, requestMessage) } } response, err := h.server.CloseShard(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RemoveTask(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RemoveTaskRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RemoveTaskRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRemoveTaskYARPCRequest, requestMessage) } } response, err := h.server.RemoveTask(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) ResetQueue(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *ResetQueueRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*ResetQueueRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceResetQueueYARPCRequest, requestMessage) } } response, err := h.server.ResetQueue(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) DescribeQueue(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *DescribeQueueRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*DescribeQueueRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceDescribeQueueYARPCRequest, requestMessage) } } response, err := h.server.DescribeQueue(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) GetReplicationMessages(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *GetReplicationMessagesRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*GetReplicationMessagesRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceGetReplicationMessagesYARPCRequest, requestMessage) } } response, err := h.server.GetReplicationMessages(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) GetDLQReplicationMessages(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *GetDLQReplicationMessagesRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*GetDLQReplicationMessagesRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceGetDLQReplicationMessagesYARPCRequest, requestMessage) } } response, err := h.server.GetDLQReplicationMessages(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) ReapplyEvents(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *ReapplyEventsRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*ReapplyEventsRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceReapplyEventsYARPCRequest, requestMessage) } } response, err := h.server.ReapplyEvents(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RefreshWorkflowTasks(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RefreshWorkflowTasksRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RefreshWorkflowTasksRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRefreshWorkflowTasksYARPCRequest, requestMessage) } } response, err := h.server.RefreshWorkflowTasks(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) CountDLQMessages(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *CountDLQMessagesRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*CountDLQMessagesRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceCountDLQMessagesYARPCRequest, requestMessage) } } response, err := h.server.CountDLQMessages(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) ReadDLQMessages(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *ReadDLQMessagesRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*ReadDLQMessagesRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceReadDLQMessagesYARPCRequest, requestMessage) } } response, err := h.server.ReadDLQMessages(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) PurgeDLQMessages(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *PurgeDLQMessagesRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*PurgeDLQMessagesRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServicePurgeDLQMessagesYARPCRequest, requestMessage) } } response, err := h.server.PurgeDLQMessages(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) MergeDLQMessages(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *MergeDLQMessagesRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*MergeDLQMessagesRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceMergeDLQMessagesYARPCRequest, requestMessage) } } response, err := h.server.MergeDLQMessages(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) NotifyFailoverMarkers(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *NotifyFailoverMarkersRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*NotifyFailoverMarkersRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceNotifyFailoverMarkersYARPCRequest, requestMessage) } } response, err := h.server.NotifyFailoverMarkers(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) GetCrossClusterTasks(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *GetCrossClusterTasksRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*GetCrossClusterTasksRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceGetCrossClusterTasksYARPCRequest, requestMessage) } } response, err := h.server.GetCrossClusterTasks(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RespondCrossClusterTasksCompleted(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RespondCrossClusterTasksCompletedRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RespondCrossClusterTasksCompletedRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRespondCrossClusterTasksCompletedYARPCRequest, requestMessage) } } response, err := h.server.RespondCrossClusterTasksCompleted(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) GetFailoverInfo(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *GetFailoverInfoRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*GetFailoverInfoRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceGetFailoverInfoYARPCRequest, requestMessage) } } response, err := h.server.GetFailoverInfo(ctx, request) if response == nil { return nil, err } return response, err } func (h *_HistoryAPIYARPCHandler) RatelimitUpdate(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RatelimitUpdateRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RatelimitUpdateRequest) if !ok { return nil, protobuf.CastError(emptyHistoryAPIServiceRatelimitUpdateYARPCRequest, requestMessage) } } response, err := h.server.RatelimitUpdate(ctx, request) if response == nil { return nil, err } return response, err } func newHistoryAPIServiceStartWorkflowExecutionYARPCRequest() proto.Message { return &StartWorkflowExecutionRequest{} } func newHistoryAPIServiceStartWorkflowExecutionYARPCResponse() proto.Message { return &StartWorkflowExecutionResponse{} } func newHistoryAPIServiceSignalWorkflowExecutionYARPCRequest() proto.Message { return &SignalWorkflowExecutionRequest{} } func newHistoryAPIServiceSignalWorkflowExecutionYARPCResponse() proto.Message { return &SignalWorkflowExecutionResponse{} } func newHistoryAPIServiceSignalWithStartWorkflowExecutionYARPCRequest() proto.Message { return &SignalWithStartWorkflowExecutionRequest{} } func newHistoryAPIServiceSignalWithStartWorkflowExecutionYARPCResponse() proto.Message { return &SignalWithStartWorkflowExecutionResponse{} } func newHistoryAPIServiceResetWorkflowExecutionYARPCRequest() proto.Message { return &ResetWorkflowExecutionRequest{} } func newHistoryAPIServiceResetWorkflowExecutionYARPCResponse() proto.Message { return &ResetWorkflowExecutionResponse{} } func newHistoryAPIServiceTerminateWorkflowExecutionYARPCRequest() proto.Message { return &TerminateWorkflowExecutionRequest{} } func newHistoryAPIServiceTerminateWorkflowExecutionYARPCResponse() proto.Message { return &TerminateWorkflowExecutionResponse{} } func newHistoryAPIServiceDescribeWorkflowExecutionYARPCRequest() proto.Message { return &DescribeWorkflowExecutionRequest{} } func newHistoryAPIServiceDescribeWorkflowExecutionYARPCResponse() proto.Message { return &DescribeWorkflowExecutionResponse{} } func newHistoryAPIServiceQueryWorkflowYARPCRequest() proto.Message { return &QueryWorkflowRequest{} } func newHistoryAPIServiceQueryWorkflowYARPCResponse() proto.Message { return &QueryWorkflowResponse{} } func newHistoryAPIServiceResetStickyTaskListYARPCRequest() proto.Message { return &ResetStickyTaskListRequest{} } func newHistoryAPIServiceResetStickyTaskListYARPCResponse() proto.Message { return &ResetStickyTaskListResponse{} } func newHistoryAPIServiceGetMutableStateYARPCRequest() proto.Message { return &GetMutableStateRequest{} } func newHistoryAPIServiceGetMutableStateYARPCResponse() proto.Message { return &GetMutableStateResponse{} } func newHistoryAPIServicePollMutableStateYARPCRequest() proto.Message { return &PollMutableStateRequest{} } func newHistoryAPIServicePollMutableStateYARPCResponse() proto.Message { return &PollMutableStateResponse{} } func newHistoryAPIServiceRecordDecisionTaskStartedYARPCRequest() proto.Message { return &RecordDecisionTaskStartedRequest{} } func newHistoryAPIServiceRecordDecisionTaskStartedYARPCResponse() proto.Message { return &RecordDecisionTaskStartedResponse{} } func newHistoryAPIServiceRespondDecisionTaskCompletedYARPCRequest() proto.Message { return &RespondDecisionTaskCompletedRequest{} } func newHistoryAPIServiceRespondDecisionTaskCompletedYARPCResponse() proto.Message { return &RespondDecisionTaskCompletedResponse{} } func newHistoryAPIServiceRespondDecisionTaskFailedYARPCRequest() proto.Message { return &RespondDecisionTaskFailedRequest{} } func newHistoryAPIServiceRespondDecisionTaskFailedYARPCResponse() proto.Message { return &RespondDecisionTaskFailedResponse{} } func newHistoryAPIServiceRecordActivityTaskStartedYARPCRequest() proto.Message { return &RecordActivityTaskStartedRequest{} } func newHistoryAPIServiceRecordActivityTaskStartedYARPCResponse() proto.Message { return &RecordActivityTaskStartedResponse{} } func newHistoryAPIServiceRespondActivityTaskCompletedYARPCRequest() proto.Message { return &RespondActivityTaskCompletedRequest{} } func newHistoryAPIServiceRespondActivityTaskCompletedYARPCResponse() proto.Message { return &RespondActivityTaskCompletedResponse{} } func newHistoryAPIServiceRespondActivityTaskFailedYARPCRequest() proto.Message { return &RespondActivityTaskFailedRequest{} } func newHistoryAPIServiceRespondActivityTaskFailedYARPCResponse() proto.Message { return &RespondActivityTaskFailedResponse{} } func newHistoryAPIServiceRespondActivityTaskCanceledYARPCRequest() proto.Message { return &RespondActivityTaskCanceledRequest{} } func newHistoryAPIServiceRespondActivityTaskCanceledYARPCResponse() proto.Message { return &RespondActivityTaskCanceledResponse{} } func newHistoryAPIServiceRecordActivityTaskHeartbeatYARPCRequest() proto.Message { return &RecordActivityTaskHeartbeatRequest{} } func newHistoryAPIServiceRecordActivityTaskHeartbeatYARPCResponse() proto.Message { return &RecordActivityTaskHeartbeatResponse{} } func newHistoryAPIServiceRequestCancelWorkflowExecutionYARPCRequest() proto.Message { return &RequestCancelWorkflowExecutionRequest{} } func newHistoryAPIServiceRequestCancelWorkflowExecutionYARPCResponse() proto.Message { return &RequestCancelWorkflowExecutionResponse{} } func newHistoryAPIServiceRemoveSignalMutableStateYARPCRequest() proto.Message { return &RemoveSignalMutableStateRequest{} } func newHistoryAPIServiceRemoveSignalMutableStateYARPCResponse() proto.Message { return &RemoveSignalMutableStateResponse{} } func newHistoryAPIServiceScheduleDecisionTaskYARPCRequest() proto.Message { return &ScheduleDecisionTaskRequest{} } func newHistoryAPIServiceScheduleDecisionTaskYARPCResponse() proto.Message { return &ScheduleDecisionTaskResponse{} } func newHistoryAPIServiceRecordChildExecutionCompletedYARPCRequest() proto.Message { return &RecordChildExecutionCompletedRequest{} } func newHistoryAPIServiceRecordChildExecutionCompletedYARPCResponse() proto.Message { return &RecordChildExecutionCompletedResponse{} } func newHistoryAPIServiceReplicateEventsV2YARPCRequest() proto.Message { return &ReplicateEventsV2Request{} } func newHistoryAPIServiceReplicateEventsV2YARPCResponse() proto.Message { return &ReplicateEventsV2Response{} } func newHistoryAPIServiceSyncShardStatusYARPCRequest() proto.Message { return &SyncShardStatusRequest{} } func newHistoryAPIServiceSyncShardStatusYARPCResponse() proto.Message { return &SyncShardStatusResponse{} } func newHistoryAPIServiceSyncActivityYARPCRequest() proto.Message { return &SyncActivityRequest{} } func newHistoryAPIServiceSyncActivityYARPCResponse() proto.Message { return &SyncActivityResponse{} } func newHistoryAPIServiceDescribeMutableStateYARPCRequest() proto.Message { return &DescribeMutableStateRequest{} } func newHistoryAPIServiceDescribeMutableStateYARPCResponse() proto.Message { return &DescribeMutableStateResponse{} } func newHistoryAPIServiceDescribeHistoryHostYARPCRequest() proto.Message { return &DescribeHistoryHostRequest{} } func newHistoryAPIServiceDescribeHistoryHostYARPCResponse() proto.Message { return &DescribeHistoryHostResponse{} } func newHistoryAPIServiceCloseShardYARPCRequest() proto.Message { return &CloseShardRequest{} } func newHistoryAPIServiceCloseShardYARPCResponse() proto.Message { return &CloseShardResponse{} } func newHistoryAPIServiceRemoveTaskYARPCRequest() proto.Message { return &RemoveTaskRequest{} } func newHistoryAPIServiceRemoveTaskYARPCResponse() proto.Message { return &RemoveTaskResponse{} } func newHistoryAPIServiceResetQueueYARPCRequest() proto.Message { return &ResetQueueRequest{} } func newHistoryAPIServiceResetQueueYARPCResponse() proto.Message { return &ResetQueueResponse{} } func newHistoryAPIServiceDescribeQueueYARPCRequest() proto.Message { return &DescribeQueueRequest{} } func newHistoryAPIServiceDescribeQueueYARPCResponse() proto.Message { return &DescribeQueueResponse{} } func newHistoryAPIServiceGetReplicationMessagesYARPCRequest() proto.Message { return &GetReplicationMessagesRequest{} } func newHistoryAPIServiceGetReplicationMessagesYARPCResponse() proto.Message { return &GetReplicationMessagesResponse{} } func newHistoryAPIServiceGetDLQReplicationMessagesYARPCRequest() proto.Message { return &GetDLQReplicationMessagesRequest{} } func newHistoryAPIServiceGetDLQReplicationMessagesYARPCResponse() proto.Message { return &GetDLQReplicationMessagesResponse{} } func newHistoryAPIServiceReapplyEventsYARPCRequest() proto.Message { return &ReapplyEventsRequest{} } func newHistoryAPIServiceReapplyEventsYARPCResponse() proto.Message { return &ReapplyEventsResponse{} } func newHistoryAPIServiceRefreshWorkflowTasksYARPCRequest() proto.Message { return &RefreshWorkflowTasksRequest{} } func newHistoryAPIServiceRefreshWorkflowTasksYARPCResponse() proto.Message { return &RefreshWorkflowTasksResponse{} } func newHistoryAPIServiceCountDLQMessagesYARPCRequest() proto.Message { return &CountDLQMessagesRequest{} } func newHistoryAPIServiceCountDLQMessagesYARPCResponse() proto.Message { return &CountDLQMessagesResponse{} } func newHistoryAPIServiceReadDLQMessagesYARPCRequest() proto.Message { return &ReadDLQMessagesRequest{} } func newHistoryAPIServiceReadDLQMessagesYARPCResponse() proto.Message { return &ReadDLQMessagesResponse{} } func newHistoryAPIServicePurgeDLQMessagesYARPCRequest() proto.Message { return &PurgeDLQMessagesRequest{} } func newHistoryAPIServicePurgeDLQMessagesYARPCResponse() proto.Message { return &PurgeDLQMessagesResponse{} } func newHistoryAPIServiceMergeDLQMessagesYARPCRequest() proto.Message { return &MergeDLQMessagesRequest{} } func newHistoryAPIServiceMergeDLQMessagesYARPCResponse() proto.Message { return &MergeDLQMessagesResponse{} } func newHistoryAPIServiceNotifyFailoverMarkersYARPCRequest() proto.Message { return &NotifyFailoverMarkersRequest{} } func newHistoryAPIServiceNotifyFailoverMarkersYARPCResponse() proto.Message { return &NotifyFailoverMarkersResponse{} } func newHistoryAPIServiceGetCrossClusterTasksYARPCRequest() proto.Message { return &GetCrossClusterTasksRequest{} } func newHistoryAPIServiceGetCrossClusterTasksYARPCResponse() proto.Message { return &GetCrossClusterTasksResponse{} } func newHistoryAPIServiceRespondCrossClusterTasksCompletedYARPCRequest() proto.Message { return &RespondCrossClusterTasksCompletedRequest{} } func newHistoryAPIServiceRespondCrossClusterTasksCompletedYARPCResponse() proto.Message { return &RespondCrossClusterTasksCompletedResponse{} } func newHistoryAPIServiceGetFailoverInfoYARPCRequest() proto.Message { return &GetFailoverInfoRequest{} } func newHistoryAPIServiceGetFailoverInfoYARPCResponse() proto.Message { return &GetFailoverInfoResponse{} } func newHistoryAPIServiceRatelimitUpdateYARPCRequest() proto.Message { return &RatelimitUpdateRequest{} } func newHistoryAPIServiceRatelimitUpdateYARPCResponse() proto.Message { return &RatelimitUpdateResponse{} } var ( emptyHistoryAPIServiceStartWorkflowExecutionYARPCRequest = &StartWorkflowExecutionRequest{} emptyHistoryAPIServiceStartWorkflowExecutionYARPCResponse = &StartWorkflowExecutionResponse{} emptyHistoryAPIServiceSignalWorkflowExecutionYARPCRequest = &SignalWorkflowExecutionRequest{} emptyHistoryAPIServiceSignalWorkflowExecutionYARPCResponse = &SignalWorkflowExecutionResponse{} emptyHistoryAPIServiceSignalWithStartWorkflowExecutionYARPCRequest = &SignalWithStartWorkflowExecutionRequest{} emptyHistoryAPIServiceSignalWithStartWorkflowExecutionYARPCResponse = &SignalWithStartWorkflowExecutionResponse{} emptyHistoryAPIServiceResetWorkflowExecutionYARPCRequest = &ResetWorkflowExecutionRequest{} emptyHistoryAPIServiceResetWorkflowExecutionYARPCResponse = &ResetWorkflowExecutionResponse{} emptyHistoryAPIServiceTerminateWorkflowExecutionYARPCRequest = &TerminateWorkflowExecutionRequest{} emptyHistoryAPIServiceTerminateWorkflowExecutionYARPCResponse = &TerminateWorkflowExecutionResponse{} emptyHistoryAPIServiceDescribeWorkflowExecutionYARPCRequest = &DescribeWorkflowExecutionRequest{} emptyHistoryAPIServiceDescribeWorkflowExecutionYARPCResponse = &DescribeWorkflowExecutionResponse{} emptyHistoryAPIServiceQueryWorkflowYARPCRequest = &QueryWorkflowRequest{} emptyHistoryAPIServiceQueryWorkflowYARPCResponse = &QueryWorkflowResponse{} emptyHistoryAPIServiceResetStickyTaskListYARPCRequest = &ResetStickyTaskListRequest{} emptyHistoryAPIServiceResetStickyTaskListYARPCResponse = &ResetStickyTaskListResponse{} emptyHistoryAPIServiceGetMutableStateYARPCRequest = &GetMutableStateRequest{} emptyHistoryAPIServiceGetMutableStateYARPCResponse = &GetMutableStateResponse{} emptyHistoryAPIServicePollMutableStateYARPCRequest = &PollMutableStateRequest{} emptyHistoryAPIServicePollMutableStateYARPCResponse = &PollMutableStateResponse{} emptyHistoryAPIServiceRecordDecisionTaskStartedYARPCRequest = &RecordDecisionTaskStartedRequest{} emptyHistoryAPIServiceRecordDecisionTaskStartedYARPCResponse = &RecordDecisionTaskStartedResponse{} emptyHistoryAPIServiceRespondDecisionTaskCompletedYARPCRequest = &RespondDecisionTaskCompletedRequest{} emptyHistoryAPIServiceRespondDecisionTaskCompletedYARPCResponse = &RespondDecisionTaskCompletedResponse{} emptyHistoryAPIServiceRespondDecisionTaskFailedYARPCRequest = &RespondDecisionTaskFailedRequest{} emptyHistoryAPIServiceRespondDecisionTaskFailedYARPCResponse = &RespondDecisionTaskFailedResponse{} emptyHistoryAPIServiceRecordActivityTaskStartedYARPCRequest = &RecordActivityTaskStartedRequest{} emptyHistoryAPIServiceRecordActivityTaskStartedYARPCResponse = &RecordActivityTaskStartedResponse{} emptyHistoryAPIServiceRespondActivityTaskCompletedYARPCRequest = &RespondActivityTaskCompletedRequest{} emptyHistoryAPIServiceRespondActivityTaskCompletedYARPCResponse = &RespondActivityTaskCompletedResponse{} emptyHistoryAPIServiceRespondActivityTaskFailedYARPCRequest = &RespondActivityTaskFailedRequest{} emptyHistoryAPIServiceRespondActivityTaskFailedYARPCResponse = &RespondActivityTaskFailedResponse{} emptyHistoryAPIServiceRespondActivityTaskCanceledYARPCRequest = &RespondActivityTaskCanceledRequest{} emptyHistoryAPIServiceRespondActivityTaskCanceledYARPCResponse = &RespondActivityTaskCanceledResponse{} emptyHistoryAPIServiceRecordActivityTaskHeartbeatYARPCRequest = &RecordActivityTaskHeartbeatRequest{} emptyHistoryAPIServiceRecordActivityTaskHeartbeatYARPCResponse = &RecordActivityTaskHeartbeatResponse{} emptyHistoryAPIServiceRequestCancelWorkflowExecutionYARPCRequest = &RequestCancelWorkflowExecutionRequest{} emptyHistoryAPIServiceRequestCancelWorkflowExecutionYARPCResponse = &RequestCancelWorkflowExecutionResponse{} emptyHistoryAPIServiceRemoveSignalMutableStateYARPCRequest = &RemoveSignalMutableStateRequest{} emptyHistoryAPIServiceRemoveSignalMutableStateYARPCResponse = &RemoveSignalMutableStateResponse{} emptyHistoryAPIServiceScheduleDecisionTaskYARPCRequest = &ScheduleDecisionTaskRequest{} emptyHistoryAPIServiceScheduleDecisionTaskYARPCResponse = &ScheduleDecisionTaskResponse{} emptyHistoryAPIServiceRecordChildExecutionCompletedYARPCRequest = &RecordChildExecutionCompletedRequest{} emptyHistoryAPIServiceRecordChildExecutionCompletedYARPCResponse = &RecordChildExecutionCompletedResponse{} emptyHistoryAPIServiceReplicateEventsV2YARPCRequest = &ReplicateEventsV2Request{} emptyHistoryAPIServiceReplicateEventsV2YARPCResponse = &ReplicateEventsV2Response{} emptyHistoryAPIServiceSyncShardStatusYARPCRequest = &SyncShardStatusRequest{} emptyHistoryAPIServiceSyncShardStatusYARPCResponse = &SyncShardStatusResponse{} emptyHistoryAPIServiceSyncActivityYARPCRequest = &SyncActivityRequest{} emptyHistoryAPIServiceSyncActivityYARPCResponse = &SyncActivityResponse{} emptyHistoryAPIServiceDescribeMutableStateYARPCRequest = &DescribeMutableStateRequest{} emptyHistoryAPIServiceDescribeMutableStateYARPCResponse = &DescribeMutableStateResponse{} emptyHistoryAPIServiceDescribeHistoryHostYARPCRequest = &DescribeHistoryHostRequest{} emptyHistoryAPIServiceDescribeHistoryHostYARPCResponse = &DescribeHistoryHostResponse{} emptyHistoryAPIServiceCloseShardYARPCRequest = &CloseShardRequest{} emptyHistoryAPIServiceCloseShardYARPCResponse = &CloseShardResponse{} emptyHistoryAPIServiceRemoveTaskYARPCRequest = &RemoveTaskRequest{} emptyHistoryAPIServiceRemoveTaskYARPCResponse = &RemoveTaskResponse{} emptyHistoryAPIServiceResetQueueYARPCRequest = &ResetQueueRequest{} emptyHistoryAPIServiceResetQueueYARPCResponse = &ResetQueueResponse{} emptyHistoryAPIServiceDescribeQueueYARPCRequest = &DescribeQueueRequest{} emptyHistoryAPIServiceDescribeQueueYARPCResponse = &DescribeQueueResponse{} emptyHistoryAPIServiceGetReplicationMessagesYARPCRequest = &GetReplicationMessagesRequest{} emptyHistoryAPIServiceGetReplicationMessagesYARPCResponse = &GetReplicationMessagesResponse{} emptyHistoryAPIServiceGetDLQReplicationMessagesYARPCRequest = &GetDLQReplicationMessagesRequest{} emptyHistoryAPIServiceGetDLQReplicationMessagesYARPCResponse = &GetDLQReplicationMessagesResponse{} emptyHistoryAPIServiceReapplyEventsYARPCRequest = &ReapplyEventsRequest{} emptyHistoryAPIServiceReapplyEventsYARPCResponse = &ReapplyEventsResponse{} emptyHistoryAPIServiceRefreshWorkflowTasksYARPCRequest = &RefreshWorkflowTasksRequest{} emptyHistoryAPIServiceRefreshWorkflowTasksYARPCResponse = &RefreshWorkflowTasksResponse{} emptyHistoryAPIServiceCountDLQMessagesYARPCRequest = &CountDLQMessagesRequest{} emptyHistoryAPIServiceCountDLQMessagesYARPCResponse = &CountDLQMessagesResponse{} emptyHistoryAPIServiceReadDLQMessagesYARPCRequest = &ReadDLQMessagesRequest{} emptyHistoryAPIServiceReadDLQMessagesYARPCResponse = &ReadDLQMessagesResponse{} emptyHistoryAPIServicePurgeDLQMessagesYARPCRequest = &PurgeDLQMessagesRequest{} emptyHistoryAPIServicePurgeDLQMessagesYARPCResponse = &PurgeDLQMessagesResponse{} emptyHistoryAPIServiceMergeDLQMessagesYARPCRequest = &MergeDLQMessagesRequest{} emptyHistoryAPIServiceMergeDLQMessagesYARPCResponse = &MergeDLQMessagesResponse{} emptyHistoryAPIServiceNotifyFailoverMarkersYARPCRequest = &NotifyFailoverMarkersRequest{} emptyHistoryAPIServiceNotifyFailoverMarkersYARPCResponse = &NotifyFailoverMarkersResponse{} emptyHistoryAPIServiceGetCrossClusterTasksYARPCRequest = &GetCrossClusterTasksRequest{} emptyHistoryAPIServiceGetCrossClusterTasksYARPCResponse = &GetCrossClusterTasksResponse{} emptyHistoryAPIServiceRespondCrossClusterTasksCompletedYARPCRequest = &RespondCrossClusterTasksCompletedRequest{} emptyHistoryAPIServiceRespondCrossClusterTasksCompletedYARPCResponse = &RespondCrossClusterTasksCompletedResponse{} emptyHistoryAPIServiceGetFailoverInfoYARPCRequest = &GetFailoverInfoRequest{} emptyHistoryAPIServiceGetFailoverInfoYARPCResponse = &GetFailoverInfoResponse{} emptyHistoryAPIServiceRatelimitUpdateYARPCRequest = &RatelimitUpdateRequest{} emptyHistoryAPIServiceRatelimitUpdateYARPCResponse = &RatelimitUpdateResponse{} ) var yarpcFileDescriptorClosurefee8ff76963a38ed = [][]byte{ // uber/cadence/history/v1/service.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x3c, 0x4b, 0x6c, 0x1c, 0x47, 0x76, 0xe8, 0x19, 0xf1, 0xf7, 0x48, 0x0e, 0xc9, 0x12, 0x3f, 0xc3, 0xa1, 0x44, 0x91, 0xbd, 0x96, 0x44, 0xcb, 0xeb, 0xa1, 0x45, 0xd9, 0xfa, 0x59, 0x5e, 0xad, 0x44, 0x4a, 0xf2, 0x38, 0xfa, 0x36, 0x69, 0x39, 0x5f, 0xf7, 0x36, 0xa7, 0x6b, 0xc8, 0x8e, 0x7a, 0xba, 0x47, 0xdd, 0x3d, 0xa4, 0xe8, 0x43, 0xe0, 0xc4, 0x41, 0x80, 0x2c, 0x82, 0xec, 0x66, 0x91, 0x04, 0x01, 0x02, 0x04, 0x08, 0x36, 0xc0, 0x62, 0x8d, 0xdc, 0x12, 0x20, 0x87, 0x24, 0xa7, 0x5c, 0x72, 0xcc, 0x35, 0xf7, 0xdd, 0x43, 0x02, 0xe4, 0xb6, 0xe7, 0x20, 0xa8, 0x4f, 0xf7, 0xf4, 0xa7, 0xba, 0x7a, 0x86, 0x0c, 0x22, 0xaf, 0xe3, 0xdb, 0x74, 0x55, 0xbd, 0x57, 0xaf, 0x5e, 0xbd, 0xf7, 0xfa, 0xfd, 0x7a, 0xe0, 0x7c, 0x77, 0x17, 0x7b, 0xeb, 0x4d, 0xc3, 0xc4, 0x4e, 0x13, 0xaf, 0xef, 0x5b, 0x7e, 0xe0, 0x7a, 0x47, 0xeb, 0x07, 0x97, 0xd7, 0x7d, 0xec, 0x1d, 0x58, 0x4d, 0x5c, 0xef, 0x78, 0x6e, 0xe0, 0xa2, 0x05, 0xb2, 0xac, 0xce, 0x97, 0xd5, 0xf9, 0xb2, 0xfa, 0xc1, 0xe5, 0xda, 0xf2, 0x9e, 0xeb, 0xee, 0xd9, 0x78, 0x9d, 0x2e, 0xdb, 0xed, 0xb6, 0xd6, 0xcd, 0xae, 0x67, 0x04, 0x96, 0xeb, 0x30, 0xc0, 0xda, 0xb9, 0xf4, 0x7c, 0x60, 0xb5, 0xb1, 0x1f, 0x18, 0xed, 0x0e, 0x5f, 0x90, 0x41, 0x70, 0xe8, 0x19, 0x9d, 0x0e, 0xf6, 0x7c, 0x3e, 0xbf, 0x92, 0x20, 0xd0, 0xe8, 0x58, 0x84, 0xb8, 0xa6, 0xdb, 0x6e, 0x47, 0x5b, 0xac, 0x8a, 0x56, 0x84, 0x24, 0x72, 0x2a, 0x44, 0x4b, 0x5e, 0x76, 0x71, 0xb4, 0x40, 0x15, 0x2d, 0x08, 0x0c, 0xff, 0x85, 0x6d, 0xf9, 0x81, 0x6c, 0xcd, 0xa1, 0xeb, 0xbd, 0x68, 0xd9, 0xee, 0x21, 0x5f, 0x73, 0x49, 0xb4, 0x86, 0xb3, 0x52, 0x4f, 0xad, 0x5d, 0x2b, 0x5a, 0x8b, 0x3d, 0xbe, 0xf2, 0x5b, 0xc9, 0x95, 0x66, 0xdb, 0x72, 0x28, 0x17, 0xec, 0xae, 0x1f, 0x14, 0x2d, 0x4a, 0x32, 0x62, 0x55, 0xbc, 0xe8, 0x65, 0x17, 0x77, 0xf9, 0x55, 0xd7, 0x2e, 0x8a, 0x97, 0x78, 0xb8, 0x63, 0x5b, 0xcd, 0xf8, 0xd5, 0x26, 0x6f, 0xc6, 0xdf, 0x37, 0x3c, 0x6c, 0x92, 0x95, 0x86, 0x13, 0xee, 0xf6, 0x46, 0xce, 0x8a, 0x24, 0x4d, 0xe7, 0x73, 0x56, 0x25, 0xd9, 0xa5, 0xfe, 0x6c, 0x18, 0xce, 0x6e, 0x07, 0x86, 0x17, 0x7c, 0xc2, 0xc7, 0xef, 0xbd, 0xc2, 0xcd, 0x2e, 0xa1, 0x47, 0xc3, 0x2f, 0xbb, 0xd8, 0x0f, 0xd0, 0x43, 0x18, 0xf1, 0xd8, 0xcf, 0xaa, 0xb2, 0xa2, 0xac, 0x8d, 0x6f, 0x6c, 0xd4, 0x13, 0x62, 0x6b, 0x74, 0xac, 0xfa, 0xc1, 0xe5, 0xba, 0x14, 0x89, 0x16, 0xa2, 0x40, 0x4b, 0x30, 0x66, 0xba, 0x6d, 0xc3, 0x72, 0x74, 0xcb, 0xac, 0x96, 0x56, 0x94, 0xb5, 0x31, 0x6d, 0x94, 0x0d, 0x34, 0x4c, 0xf4, 0x9b, 0x30, 0xd7, 0x31, 0x3c, 0xec, 0x04, 0x3a, 0x0e, 0x11, 0xe8, 0x96, 0xd3, 0x72, 0xab, 0x65, 0xba, 0xf1, 0x9a, 0x70, 0xe3, 0xa7, 0x14, 0x22, 0xda, 0xb1, 0xe1, 0xb4, 0x5c, 0xed, 0x74, 0x27, 0x3b, 0x88, 0xaa, 0x30, 0x62, 0x04, 0x01, 0x6e, 0x77, 0x82, 0xea, 0xa9, 0x15, 0x65, 0x6d, 0x48, 0x0b, 0x1f, 0xd1, 0x26, 0x4c, 0xe1, 0x57, 0x1d, 0x8b, 0xa9, 0x98, 0x4e, 0x74, 0xa9, 0x3a, 0x44, 0x77, 0xac, 0xd5, 0x99, 0x1e, 0xd5, 0x43, 0x3d, 0xaa, 0xef, 0x84, 0x8a, 0xa6, 0x55, 0x7a, 0x20, 0x64, 0x10, 0xb5, 0x60, 0xb1, 0xe9, 0x3a, 0x81, 0xe5, 0x74, 0xb1, 0x6e, 0xf8, 0xba, 0x83, 0x0f, 0x75, 0xcb, 0xb1, 0x02, 0xcb, 0x08, 0x5c, 0xaf, 0x3a, 0xbc, 0xa2, 0xac, 0x55, 0x36, 0xde, 0x12, 0x1e, 0x60, 0x93, 0x43, 0xdd, 0xf1, 0x1f, 0xe3, 0xc3, 0x46, 0x08, 0xa2, 0xcd, 0x37, 0x85, 0xe3, 0xa8, 0x01, 0x33, 0xe1, 0x8c, 0xa9, 0xb7, 0x0c, 0xcb, 0xee, 0x7a, 0xb8, 0x3a, 0x42, 0xc9, 0x3d, 0x23, 0xc4, 0x7f, 0x9f, 0xad, 0xd1, 0xa6, 0x23, 0x30, 0x3e, 0x82, 0x34, 0x98, 0xb7, 0x0d, 0x3f, 0xd0, 0x9b, 0x6e, 0xbb, 0x63, 0x63, 0x7a, 0x78, 0x0f, 0xfb, 0x5d, 0x3b, 0xa8, 0x8e, 0x4a, 0xf0, 0x3d, 0x35, 0x8e, 0x6c, 0xd7, 0x30, 0xb5, 0x59, 0x02, 0xbb, 0x19, 0x81, 0x6a, 0x14, 0x12, 0xfd, 0x2a, 0x2c, 0xb5, 0x2c, 0xcf, 0x0f, 0x74, 0x13, 0x37, 0x2d, 0x9f, 0xf2, 0xd3, 0xf0, 0x5f, 0xe8, 0xbb, 0x46, 0xf3, 0x85, 0xdb, 0x6a, 0x55, 0xc7, 0x28, 0xe2, 0xc5, 0x0c, 0x5f, 0xb7, 0xb8, 0x81, 0xd3, 0xaa, 0x14, 0x7a, 0x8b, 0x03, 0xef, 0x18, 0xfe, 0x8b, 0xbb, 0x0c, 0x14, 0x1d, 0xc0, 0x74, 0xc7, 0xf0, 0x02, 0x8b, 0xd2, 0xd9, 0x74, 0x9d, 0x96, 0xb5, 0x57, 0x85, 0x95, 0xf2, 0xda, 0xf8, 0xc6, 0xaf, 0xd4, 0x73, 0x0c, 0xa9, 0x5c, 0x2a, 0x89, 0xe8, 0x30, 0x74, 0x9b, 0x14, 0xdb, 0x3d, 0x27, 0xf0, 0x8e, 0xb4, 0xa9, 0x4e, 0x72, 0xb4, 0x76, 0x17, 0x66, 0x45, 0x0b, 0xd1, 0x34, 0x94, 0x5f, 0xe0, 0x23, 0xaa, 0x14, 0x63, 0x1a, 0xf9, 0x89, 0x66, 0x61, 0xe8, 0xc0, 0xb0, 0xbb, 0x98, 0x0b, 0x36, 0x7b, 0xb8, 0x59, 0xba, 0xae, 0xa8, 0xd7, 0x60, 0x39, 0x8f, 0x14, 0xbf, 0xe3, 0x3a, 0x3e, 0x46, 0x73, 0x30, 0xec, 0x75, 0xa9, 0x56, 0x30, 0x84, 0x43, 0x5e, 0xd7, 0x69, 0x98, 0xea, 0xdf, 0x94, 0x60, 0x79, 0xdb, 0xda, 0x73, 0x0c, 0x3b, 0x57, 0x41, 0x1f, 0xa5, 0x15, 0xf4, 0x8a, 0x58, 0x41, 0xa5, 0x58, 0xfa, 0xd4, 0xd0, 0x16, 0x2c, 0xe1, 0x57, 0x01, 0xf6, 0x1c, 0xc3, 0x8e, 0x0c, 0x6f, 0x4f, 0x59, 0xb9, 0x9e, 0x5e, 0x10, 0xee, 0x9f, 0xdd, 0x79, 0x31, 0x44, 0x95, 0x99, 0x42, 0x75, 0x38, 0xdd, 0xdc, 0xb7, 0x6c, 0xb3, 0xb7, 0x89, 0xeb, 0xd8, 0x47, 0x54, 0x6f, 0x47, 0xb5, 0x19, 0x3a, 0x15, 0x02, 0x3d, 0x71, 0xec, 0x23, 0x75, 0x15, 0xce, 0xe5, 0x9e, 0x8f, 0x31, 0x58, 0xfd, 0x79, 0x09, 0x2e, 0xf2, 0x35, 0x56, 0xb0, 0x2f, 0xb7, 0x79, 0xcf, 0xd3, 0x2c, 0xbd, 0x25, 0x63, 0x69, 0x11, 0xba, 0x3e, 0x79, 0xfb, 0xb9, 0x22, 0x10, 0xf0, 0x32, 0x15, 0xf0, 0x8f, 0xf3, 0x05, 0xbc, 0x3f, 0x12, 0xfe, 0x0f, 0x45, 0xfd, 0x0e, 0xac, 0x15, 0x13, 0x25, 0x17, 0xfa, 0xef, 0x2b, 0x70, 0x56, 0xc3, 0x3e, 0x3e, 0xf1, 0x4b, 0x49, 0x8a, 0xa4, 0xbf, 0x6b, 0x21, 0xaa, 0x9b, 0x87, 0x46, 0x7e, 0x8a, 0x2f, 0x4b, 0xb0, 0xba, 0x83, 0xbd, 0xb6, 0xe5, 0x18, 0x01, 0xce, 0x3d, 0xc9, 0xd3, 0xf4, 0x49, 0xae, 0x0a, 0x4f, 0x52, 0x88, 0xe8, 0x97, 0x5c, 0x81, 0xdf, 0x00, 0x55, 0x76, 0x44, 0xae, 0xc3, 0x3f, 0x54, 0x60, 0x65, 0x0b, 0xfb, 0x4d, 0xcf, 0xda, 0xcd, 0xe7, 0xe8, 0x93, 0x34, 0x47, 0xdf, 0x13, 0x1e, 0xa7, 0x08, 0x4f, 0x9f, 0xe2, 0xf1, 0xdf, 0x65, 0x58, 0x95, 0xa0, 0xe2, 0x22, 0x62, 0xc3, 0x42, 0xcf, 0xa5, 0x61, 0xaa, 0xcd, 0x5f, 0x78, 0x52, 0x9b, 0x9d, 0x41, 0xb8, 0x19, 0x07, 0xd5, 0xe6, 0xb1, 0x70, 0x1c, 0xed, 0xc2, 0x42, 0xf6, 0x6e, 0x99, 0x27, 0x55, 0xa2, 0xbb, 0x5d, 0xea, 0x6f, 0x37, 0xea, 0x4b, 0xcd, 0x1d, 0x8a, 0x86, 0xd1, 0x27, 0x80, 0x3a, 0xd8, 0x31, 0x2d, 0x67, 0x4f, 0x37, 0x9a, 0x81, 0x75, 0x60, 0x05, 0x16, 0xf6, 0xb9, 0xb9, 0xca, 0x71, 0xd4, 0xd8, 0xf2, 0x3b, 0x6c, 0xf5, 0x11, 0x45, 0x3e, 0xd3, 0x49, 0x0c, 0x5a, 0xd8, 0x47, 0xbf, 0x06, 0xd3, 0x21, 0x62, 0x2a, 0x26, 0x1e, 0x76, 0xaa, 0xa7, 0x28, 0xda, 0xba, 0x0c, 0xed, 0x26, 0x59, 0x9b, 0xa4, 0x7c, 0xaa, 0x13, 0x9b, 0xf2, 0xb0, 0x83, 0xb6, 0x7b, 0xa8, 0x43, 0xef, 0x84, 0x3b, 0x7a, 0x52, 0x8a, 0x43, 0x67, 0x24, 0x81, 0x34, 0x1c, 0x54, 0x5f, 0xc1, 0xec, 0x33, 0x12, 0xf3, 0x84, 0xdc, 0x0b, 0xc5, 0x70, 0x33, 0x2d, 0x86, 0x6f, 0x0a, 0xf7, 0x10, 0xc1, 0xf6, 0x29, 0x7a, 0x3f, 0x56, 0x60, 0x2e, 0x05, 0xce, 0xc5, 0xed, 0x36, 0x4c, 0xd0, 0x38, 0x2c, 0x74, 0xe7, 0x94, 0x3e, 0xdc, 0xb9, 0x71, 0x0a, 0xc1, 0xbd, 0xb8, 0x06, 0x54, 0x42, 0x04, 0xbf, 0x8d, 0x9b, 0x01, 0x36, 0xb9, 0xe0, 0xa8, 0xf9, 0x67, 0xd0, 0xf8, 0x4a, 0x6d, 0xf2, 0x65, 0xfc, 0x51, 0xfd, 0x7d, 0x05, 0x6a, 0xd4, 0x80, 0x6e, 0x07, 0x56, 0xf3, 0xc5, 0x11, 0xf1, 0xe8, 0x1e, 0x5a, 0x7e, 0x10, 0xb2, 0xa9, 0x91, 0x66, 0xd3, 0x7a, 0xbe, 0x25, 0x17, 0x62, 0xe8, 0x93, 0x59, 0x67, 0x61, 0x49, 0x88, 0x83, 0x5b, 0x96, 0x7f, 0x2b, 0xc1, 0xfc, 0x03, 0x1c, 0x3c, 0xea, 0x06, 0xc6, 0xae, 0x8d, 0xb7, 0x03, 0x23, 0xc0, 0x9a, 0x08, 0xad, 0x92, 0xb2, 0xa7, 0x1f, 0x03, 0x12, 0x98, 0xd1, 0xd2, 0x40, 0x66, 0x74, 0x26, 0xa3, 0x61, 0xe8, 0x0a, 0xcc, 0xe3, 0x57, 0x1d, 0xca, 0x40, 0xdd, 0xc1, 0xaf, 0x02, 0x1d, 0x1f, 0x90, 0xb0, 0xc8, 0x32, 0xa9, 0x85, 0x2e, 0x6b, 0xa7, 0xc3, 0xd9, 0xc7, 0xf8, 0x55, 0x70, 0x8f, 0xcc, 0x35, 0x4c, 0xf4, 0x0e, 0xcc, 0x36, 0xbb, 0x1e, 0x8d, 0x9f, 0x76, 0x3d, 0xc3, 0x69, 0xee, 0xeb, 0x81, 0xfb, 0x82, 0x6a, 0x8f, 0xb2, 0x36, 0xa1, 0x21, 0x3e, 0x77, 0x97, 0x4e, 0xed, 0x90, 0x19, 0xf4, 0x1b, 0x30, 0x7b, 0x80, 0x3d, 0xea, 0xa5, 0x73, 0x9f, 0x42, 0xb7, 0x02, 0xdc, 0xe6, 0x4a, 0x91, 0x16, 0x58, 0x12, 0xb4, 0x92, 0x13, 0x3c, 0x67, 0x20, 0x1f, 0x32, 0x88, 0x46, 0x80, 0xdb, 0x1a, 0x3a, 0xc8, 0x8c, 0xa9, 0xff, 0x30, 0x06, 0x0b, 0x19, 0x96, 0x72, 0x01, 0x15, 0xb3, 0x4d, 0x39, 0x29, 0xdb, 0xee, 0xc3, 0x64, 0x84, 0x36, 0x38, 0xea, 0x60, 0x7e, 0x11, 0xab, 0x52, 0x8c, 0x3b, 0x47, 0x1d, 0xac, 0x4d, 0x1c, 0xc6, 0x9e, 0x90, 0x0a, 0x93, 0x22, 0xae, 0x8f, 0x3b, 0x31, 0x6e, 0x3f, 0x87, 0xc5, 0x8e, 0x87, 0x0f, 0x2c, 0xb7, 0xeb, 0xeb, 0x3e, 0x71, 0x73, 0xb0, 0xd9, 0x5b, 0x7f, 0x8a, 0xee, 0xbb, 0x94, 0x09, 0x73, 0x1a, 0x4e, 0x70, 0xf5, 0xdd, 0xe7, 0xc4, 0x57, 0xd2, 0xe6, 0x43, 0xe8, 0x6d, 0x06, 0x1c, 0xe2, 0x7d, 0x1b, 0x4e, 0xd3, 0xa0, 0x8c, 0x45, 0x51, 0x11, 0xc6, 0x21, 0x4a, 0xc1, 0x34, 0x99, 0xba, 0x4f, 0x66, 0xc2, 0xe5, 0x37, 0x61, 0x8c, 0x06, 0x58, 0xb6, 0xe5, 0x07, 0x34, 0xcc, 0x1c, 0xdf, 0x38, 0x2b, 0xf6, 0x20, 0x42, 0x91, 0x1f, 0x0d, 0xf8, 0x2f, 0xf4, 0x00, 0xa6, 0x7d, 0xaa, 0x0e, 0x7a, 0x0f, 0xc5, 0x48, 0x3f, 0x28, 0x2a, 0x7e, 0x42, 0x8b, 0xd0, 0xbb, 0x30, 0xdf, 0xb4, 0x2d, 0x42, 0xa9, 0x6d, 0xed, 0x7a, 0x86, 0x77, 0xa4, 0x73, 0x79, 0xa0, 0x81, 0xe4, 0x98, 0x36, 0xcb, 0x66, 0x1f, 0xb2, 0x49, 0x2e, 0x3f, 0x31, 0xa8, 0x16, 0x36, 0x82, 0xae, 0x87, 0x23, 0xa8, 0xb1, 0x38, 0xd4, 0x7d, 0x36, 0x19, 0x42, 0x9d, 0x83, 0x71, 0x0e, 0x65, 0xb5, 0x3b, 0x76, 0x15, 0xe8, 0x52, 0x60, 0x43, 0x8d, 0x76, 0xc7, 0x46, 0x3e, 0x5c, 0x4a, 0x9f, 0x4a, 0xf7, 0x9b, 0xfb, 0xd8, 0xec, 0xda, 0x58, 0x0f, 0x5c, 0x76, 0x59, 0x34, 0xca, 0x77, 0xbb, 0x41, 0x75, 0xbc, 0x28, 0x20, 0x7d, 0x23, 0x79, 0xd6, 0x6d, 0x8e, 0x69, 0xc7, 0xa5, 0xf7, 0xb6, 0xc3, 0xd0, 0x10, 0x7f, 0x87, 0x5d, 0x15, 0x91, 0xff, 0xde, 0x41, 0x26, 0x68, 0xa2, 0x61, 0x86, 0x4e, 0x6d, 0x93, 0x99, 0xf0, 0x14, 0x79, 0xba, 0x3a, 0x99, 0xab, 0xab, 0x0f, 0xa1, 0x12, 0xc9, 0xb6, 0x4f, 0x94, 0xa9, 0x5a, 0xa1, 0x49, 0x85, 0xf3, 0xc9, 0xab, 0x62, 0x99, 0x9e, 0xb8, 0x7c, 0x33, 0xcd, 0x8b, 0x14, 0x83, 0x3e, 0xa2, 0x26, 0xcc, 0x46, 0xd8, 0x9a, 0xb6, 0xeb, 0x63, 0x8e, 0x73, 0x8a, 0xe2, 0xbc, 0xdc, 0xa7, 0x37, 0x42, 0x00, 0x09, 0xbe, 0xae, 0xaf, 0x45, 0xfa, 0x1c, 0x0d, 0x12, 0x2d, 0x9f, 0x49, 0x9a, 0x17, 0xe2, 0x22, 0x4c, 0x8b, 0x5e, 0xb8, 0x3d, 0xaa, 0x13, 0xc6, 0xc5, 0xc2, 0xbe, 0x36, 0x7d, 0x90, 0x1a, 0x41, 0xb7, 0x60, 0xc9, 0x22, 0x3a, 0x97, 0xba, 0x63, 0xec, 0x10, 0x3b, 0x63, 0x56, 0x67, 0xa8, 0x8f, 0xb9, 0x60, 0xf9, 0x49, 0x53, 0x7f, 0x8f, 0x4d, 0xa3, 0x55, 0x98, 0x08, 0x6d, 0x9d, 0x6f, 0x7d, 0x86, 0xab, 0x88, 0xa9, 0x36, 0x1f, 0xdb, 0xb6, 0x3e, 0xc3, 0xea, 0x2f, 0x14, 0x58, 0x78, 0xea, 0xda, 0xf6, 0xff, 0xaf, 0xb7, 0x81, 0xfa, 0x93, 0x51, 0xa8, 0x66, 0x8f, 0xfd, 0x8d, 0xc5, 0xfe, 0xc6, 0x62, 0x7f, 0x1d, 0x2d, 0x76, 0x9e, 0x7e, 0x4c, 0xe4, 0x5a, 0x60, 0xa1, 0x39, 0x9b, 0x3c, 0xb1, 0x39, 0xfb, 0xe5, 0x33, 0xec, 0xea, 0xbf, 0x94, 0x60, 0x45, 0xc3, 0x4d, 0xd7, 0x33, 0xe3, 0x89, 0x5a, 0xae, 0x16, 0xaf, 0xd3, 0x52, 0x9e, 0x83, 0xf1, 0x48, 0x70, 0x22, 0x23, 0x00, 0xe1, 0x50, 0xc3, 0x44, 0x0b, 0x30, 0x42, 0x65, 0x8c, 0x6b, 0x7c, 0x59, 0x1b, 0x26, 0x8f, 0x0d, 0x13, 0x9d, 0x05, 0xe0, 0x71, 0x44, 0xa8, 0xbb, 0x63, 0xda, 0x18, 0x1f, 0x69, 0x98, 0x48, 0x83, 0x89, 0x8e, 0x6b, 0xdb, 0x7a, 0x18, 0xab, 0x0c, 0x4b, 0x62, 0x15, 0x62, 0x43, 0xef, 0xbb, 0x5e, 0x9c, 0x35, 0x61, 0xac, 0x32, 0x4e, 0x90, 0xf0, 0x07, 0xf5, 0xf7, 0x46, 0x61, 0x55, 0xc2, 0x45, 0x6e, 0x78, 0x33, 0x16, 0x52, 0x39, 0x9e, 0x85, 0x94, 0x5a, 0xbf, 0xd2, 0xf1, 0xad, 0xdf, 0xb7, 0x01, 0x85, 0xfc, 0x35, 0xd3, 0xe6, 0x77, 0x3a, 0x9a, 0x09, 0x57, 0xaf, 0x11, 0x03, 0x26, 0x30, 0xbd, 0x65, 0x62, 0xa1, 0x12, 0x78, 0x33, 0x16, 0x7d, 0x28, 0x6b, 0xd1, 0x63, 0x25, 0x9d, 0xe1, 0x64, 0x49, 0xe7, 0x3a, 0x54, 0xb9, 0x49, 0xe9, 0x25, 0x40, 0x42, 0x07, 0x61, 0x84, 0x3a, 0x08, 0xf3, 0x6c, 0x3e, 0x92, 0x9d, 0xd0, 0x3f, 0xd0, 0x60, 0x32, 0x2a, 0x5d, 0xd0, 0x94, 0x09, 0xab, 0x85, 0xbc, 0x9d, 0xa7, 0x8d, 0x3b, 0x9e, 0xe1, 0xf8, 0xc4, 0x94, 0x25, 0xd2, 0x04, 0x13, 0x66, 0xec, 0x09, 0x7d, 0x0a, 0x67, 0x04, 0x09, 0x99, 0x9e, 0x09, 0x1f, 0xeb, 0xc7, 0x84, 0x2f, 0x66, 0xc4, 0x3d, 0xb2, 0xe6, 0x39, 0xde, 0x27, 0xe4, 0x79, 0x9f, 0xab, 0x30, 0x91, 0xb0, 0x79, 0xe3, 0xd4, 0xe6, 0x8d, 0xef, 0xc6, 0x8c, 0xdd, 0x1d, 0xa8, 0xf4, 0xae, 0x95, 0x96, 0xc4, 0x26, 0x0a, 0x4b, 0x62, 0x93, 0x11, 0x04, 0xad, 0x88, 0x7d, 0x00, 0x13, 0xe1, 0x5d, 0x53, 0x04, 0x93, 0x85, 0x08, 0xc6, 0xf9, 0x7a, 0x0a, 0x6e, 0xc0, 0xc8, 0xcb, 0x2e, 0xa6, 0x46, 0xb6, 0x42, 0xf3, 0x3f, 0x0f, 0x72, 0xb3, 0xe0, 0x85, 0x5a, 0x44, 0x53, 0x14, 0x16, 0xf6, 0x59, 0xde, 0x3b, 0xc4, 0x9b, 0xf1, 0x05, 0xa7, 0x32, 0xbe, 0x60, 0xed, 0x53, 0x98, 0x88, 0xc3, 0x0a, 0x52, 0xe1, 0xd7, 0xe3, 0xa9, 0xf0, 0xbc, 0x14, 0x49, 0xa8, 0x98, 0x2c, 0x55, 0x12, 0x4b, 0x97, 0xf7, 0x4c, 0x69, 0x98, 0x18, 0xfb, 0xc6, 0x94, 0x66, 0x4c, 0x69, 0x9c, 0x35, 0x42, 0x53, 0xfa, 0xb3, 0x72, 0x68, 0x4a, 0x85, 0x5c, 0xe4, 0xa6, 0xf4, 0x23, 0x98, 0x4a, 0x99, 0x2a, 0xa9, 0x31, 0xe5, 0xc9, 0x0c, 0x6a, 0x6c, 0xb4, 0x4a, 0xd2, 0x94, 0x65, 0x84, 0xbb, 0x34, 0x98, 0x70, 0xc7, 0x2c, 0x57, 0x39, 0x69, 0xb9, 0x3e, 0x85, 0xe5, 0xa4, 0xe2, 0xe9, 0x6e, 0x4b, 0x0f, 0xf6, 0x2d, 0x5f, 0x8f, 0x57, 0xaf, 0xe5, 0x5b, 0xd5, 0x12, 0x8a, 0xf8, 0xa4, 0xb5, 0xb3, 0x6f, 0xf9, 0x77, 0x38, 0xfe, 0x06, 0xcc, 0xec, 0x63, 0xc3, 0x0b, 0x76, 0xb1, 0x11, 0xe8, 0x26, 0x0e, 0x0c, 0xcb, 0xf6, 0x79, 0xc2, 0x47, 0x9e, 0x20, 0x9c, 0x8e, 0xc0, 0xb6, 0x18, 0x54, 0xf6, 0xd5, 0x34, 0x7c, 0xbc, 0x57, 0xd3, 0x45, 0x98, 0x8a, 0xf0, 0x30, 0xb1, 0xa6, 0x36, 0x7a, 0x4c, 0x8b, 0x1c, 0xa3, 0x2d, 0x3a, 0xaa, 0xfe, 0xb9, 0x02, 0xdf, 0x62, 0xb7, 0x99, 0x50, 0x76, 0x5e, 0x84, 0xee, 0xe9, 0x8b, 0x96, 0x4e, 0x2a, 0x5e, 0xcf, 0x4b, 0x2a, 0x16, 0xa1, 0xea, 0x33, 0xbb, 0xf8, 0x77, 0x65, 0x78, 0x43, 0x8e, 0x8d, 0x8b, 0x20, 0xee, 0xbd, 0xff, 0x3c, 0x3e, 0xc6, 0x49, 0xbc, 0x79, 0x7c, 0xeb, 0xa6, 0x4d, 0xf9, 0x29, 0x49, 0xff, 0xb1, 0x02, 0xcb, 0xbd, 0xb4, 0x3c, 0xf1, 0xa1, 0x4d, 0xcb, 0xef, 0x18, 0x41, 0x73, 0x5f, 0xb7, 0xdd, 0xa6, 0x61, 0xdb, 0x47, 0xd5, 0x12, 0xb5, 0xa9, 0x9f, 0x4a, 0x76, 0x2d, 0x3e, 0x4e, 0xbd, 0x97, 0xb7, 0xdf, 0x71, 0xb7, 0xf8, 0x0e, 0x0f, 0xd9, 0x06, 0xcc, 0xd4, 0x2e, 0x19, 0xf9, 0x2b, 0x6a, 0xbf, 0x03, 0x2b, 0x45, 0x08, 0x04, 0xf6, 0x76, 0x2b, 0x69, 0x6f, 0xc5, 0x55, 0x81, 0xd0, 0x0c, 0x50, 0x5c, 0x21, 0x62, 0xfa, 0x66, 0x8e, 0xd9, 0xde, 0x1f, 0x2a, 0xc4, 0xf6, 0x66, 0x8e, 0x79, 0xdf, 0xb0, 0xec, 0x9e, 0x2c, 0xf5, 0x59, 0x4e, 0x2a, 0xc2, 0xd3, 0xa7, 0x20, 0x7d, 0x8b, 0xd8, 0xb1, 0x5c, 0x4c, 0x3c, 0x59, 0xfd, 0xa7, 0x0a, 0xa8, 0x59, 0x6b, 0xf7, 0x61, 0xa8, 0x9e, 0x21, 0xe5, 0xcf, 0xd2, 0x94, 0x5f, 0xcb, 0xa1, 0xbc, 0x08, 0x53, 0x9f, 0xb4, 0x3f, 0x25, 0xca, 0x29, 0xc1, 0xc5, 0x65, 0xf3, 0x4d, 0x98, 0x6e, 0x1a, 0x4e, 0x13, 0x47, 0x6f, 0x00, 0xcc, 0xde, 0x69, 0xa3, 0xda, 0x14, 0x1b, 0xd7, 0xc2, 0xe1, 0xb8, 0xbe, 0xc7, 0x71, 0x9e, 0x50, 0xdf, 0x65, 0xa8, 0xfa, 0x3c, 0xea, 0x85, 0x48, 0xdd, 0x73, 0x90, 0xc5, 0x0a, 0x96, 0x82, 0x85, 0x27, 0x91, 0xb0, 0x5c, 0x3c, 0x03, 0x4b, 0x98, 0x08, 0x53, 0x42, 0xc2, 0xb2, 0x07, 0xa4, 0xf7, 0xd3, 0xa3, 0xbc, 0x6f, 0x09, 0x2b, 0xc2, 0xd4, 0x27, 0xed, 0xe7, 0xc5, 0xe2, 0x10, 0xe1, 0xe2, 0xd4, 0xff, 0xbd, 0x02, 0xe7, 0x34, 0xdc, 0x76, 0x0f, 0x30, 0xeb, 0x44, 0xf8, 0xaa, 0xe4, 0xf1, 0x92, 0x8e, 0x51, 0x39, 0xe5, 0x18, 0xa9, 0x2a, 0x91, 0x95, 0x3c, 0xaa, 0xf9, 0xd1, 0xfe, 0xb1, 0x04, 0xe7, 0xf9, 0x11, 0xd8, 0xb1, 0x73, 0xcb, 0xe0, 0xd2, 0x03, 0x1a, 0x50, 0x49, 0xea, 0x20, 0x3f, 0xdc, 0xcd, 0x9c, 0xfb, 0xeb, 0x63, 0x43, 0x6d, 0x32, 0xa1, 0xbd, 0x68, 0x17, 0x16, 0xa2, 0x4e, 0x03, 0x61, 0x3b, 0x9f, 0xb8, 0x08, 0x7d, 0x8f, 0xc3, 0xa4, 0x8a, 0xd0, 0x58, 0x34, 0x3c, 0x70, 0x97, 0xc1, 0x1a, 0x5c, 0x28, 0x3a, 0x0b, 0xe7, 0xf3, 0x3f, 0x2b, 0xb0, 0x14, 0x26, 0x8e, 0x04, 0x81, 0xfc, 0x6b, 0x11, 0x9f, 0x4b, 0x30, 0x63, 0xf9, 0x7a, 0xb2, 0xbb, 0x8e, 0xf2, 0x72, 0x54, 0x9b, 0xb2, 0xfc, 0xfb, 0xf1, 0xbe, 0x39, 0x75, 0x19, 0xce, 0x88, 0xc9, 0xe7, 0xe7, 0xfb, 0x82, 0x3a, 0x2c, 0xc4, 0x58, 0x27, 0x0b, 0xe7, 0x19, 0xd3, 0xfa, 0x3a, 0x0e, 0xba, 0x0a, 0x13, 0xbc, 0x75, 0x12, 0x9b, 0xb1, 0x5c, 0x6e, 0x34, 0xd6, 0x30, 0xd1, 0x27, 0x70, 0xba, 0x19, 0x92, 0x1a, 0xdb, 0xfa, 0xd4, 0x40, 0x5b, 0xa3, 0x08, 0x45, 0x6f, 0xef, 0x87, 0x30, 0x1d, 0x6b, 0x87, 0x64, 0x41, 0xc2, 0x50, 0xbf, 0x41, 0xc2, 0x54, 0x0f, 0x94, 0x45, 0x09, 0x67, 0x01, 0x42, 0x77, 0xcf, 0x32, 0xa9, 0x7b, 0x5c, 0xd6, 0xc6, 0xf8, 0x48, 0xc3, 0x54, 0x2f, 0x12, 0x65, 0x96, 0x5e, 0x02, 0xbf, 0xae, 0xff, 0x28, 0x41, 0x55, 0xe3, 0xbd, 0xc2, 0x98, 0xa2, 0xf6, 0x9f, 0x6f, 0xbc, 0xce, 0x2b, 0xfa, 0x2d, 0x98, 0x13, 0x55, 0x8e, 0xc3, 0x0e, 0x90, 0x01, 0x4a, 0xc7, 0xa7, 0xb3, 0xa5, 0x63, 0x1f, 0xbd, 0x07, 0xc3, 0x94, 0xf5, 0x3e, 0xbf, 0x51, 0x71, 0x6a, 0x64, 0xcb, 0x08, 0x8c, 0xbb, 0xb6, 0xbb, 0xab, 0xf1, 0xc5, 0x68, 0x13, 0x2a, 0x0e, 0x3e, 0xd4, 0xbd, 0x2e, 0xbf, 0xb9, 0x30, 0xb0, 0x29, 0x00, 0x9f, 0x70, 0xf0, 0xa1, 0xd6, 0x65, 0x57, 0xe6, 0xab, 0x4b, 0xb0, 0x28, 0x60, 0x35, 0xbf, 0x88, 0xef, 0x2b, 0x30, 0xbf, 0x7d, 0xe4, 0x34, 0xb7, 0xf7, 0x0d, 0xcf, 0xe4, 0x19, 0x52, 0x7e, 0x0d, 0xe7, 0xa1, 0xe2, 0xbb, 0x5d, 0xaf, 0x89, 0x75, 0xde, 0x42, 0xce, 0xef, 0x62, 0x92, 0x8d, 0x6e, 0xb2, 0x41, 0xb4, 0x08, 0xa3, 0x3e, 0x01, 0x0e, 0xdf, 0x6f, 0x43, 0xda, 0x08, 0x7d, 0x6e, 0x98, 0xa8, 0x0e, 0xa7, 0x68, 0x2c, 0x59, 0x2e, 0x0c, 0xf0, 0xe8, 0x3a, 0x75, 0x11, 0x16, 0x32, 0xb4, 0x70, 0x3a, 0xff, 0x75, 0x08, 0x4e, 0x93, 0xb9, 0xf0, 0x3d, 0xf9, 0x3a, 0x65, 0xa5, 0x0a, 0x23, 0x61, 0x46, 0x8a, 0x69, 0x72, 0xf8, 0x48, 0x14, 0xbd, 0x17, 0xeb, 0x46, 0x79, 0x84, 0x28, 0xef, 0x40, 0x78, 0x92, 0xcd, 0x43, 0x0d, 0x0d, 0x9a, 0x87, 0x92, 0x2b, 0x61, 0x26, 0x92, 0x1f, 0x19, 0x2c, 0x92, 0xff, 0x88, 0x57, 0x7f, 0x7a, 0x41, 0x35, 0xc5, 0x32, 0x5a, 0x88, 0x65, 0x86, 0x80, 0x45, 0xee, 0x31, 0xc5, 0x75, 0x15, 0x46, 0xc2, 0x88, 0x7c, 0xac, 0x8f, 0x88, 0x3c, 0x5c, 0x1c, 0xcf, 0x26, 0x40, 0x32, 0x9b, 0x70, 0x1b, 0x26, 0x58, 0x6d, 0x8a, 0x37, 0x8a, 0x8f, 0xf7, 0xd1, 0x28, 0x3e, 0x4e, 0x4b, 0x56, 0xbc, 0x47, 0xfc, 0x1d, 0xa0, 0x7d, 0xde, 0xfc, 0xd3, 0x09, 0xdd, 0x32, 0xb1, 0x13, 0x58, 0xc1, 0x11, 0xcd, 0x06, 0x8e, 0x69, 0x88, 0xcc, 0x7d, 0x42, 0xa7, 0x1a, 0x7c, 0x06, 0x3d, 0x86, 0xa9, 0x94, 0x69, 0xe0, 0x99, 0xbf, 0xf3, 0x7d, 0x19, 0x05, 0xad, 0x92, 0x34, 0x08, 0xea, 0x3c, 0xcc, 0x26, 0x25, 0x99, 0x8b, 0xf8, 0x9f, 0x28, 0xb0, 0x14, 0x76, 0xde, 0x7d, 0x45, 0x3c, 0x3c, 0xf5, 0x8f, 0x15, 0x38, 0x23, 0xa6, 0x89, 0x07, 0x3f, 0x57, 0x60, 0xbe, 0xcd, 0xc6, 0x59, 0x5d, 0x46, 0xb7, 0x1c, 0xbd, 0x69, 0x34, 0xf7, 0x31, 0xa7, 0xf0, 0x74, 0x3b, 0x06, 0xd5, 0x70, 0x36, 0xc9, 0x14, 0xba, 0x01, 0x8b, 0x19, 0x20, 0xd3, 0x08, 0x8c, 0x5d, 0xc3, 0x0f, 0x1b, 0x70, 0xe7, 0x93, 0x70, 0x5b, 0x7c, 0x56, 0x3d, 0x03, 0xb5, 0x90, 0x1e, 0xce, 0xcf, 0x0f, 0xdd, 0xa8, 0x75, 0x4a, 0xfd, 0xdd, 0x52, 0x8f, 0x85, 0x89, 0x69, 0x4e, 0xed, 0x1a, 0x4c, 0x3b, 0xdd, 0xf6, 0x2e, 0xf6, 0x74, 0xb7, 0xa5, 0x53, 0x2b, 0xe5, 0x53, 0x3a, 0x87, 0xb4, 0x0a, 0x1b, 0x7f, 0xd2, 0xa2, 0xc6, 0xc7, 0x27, 0xcc, 0x0e, 0xad, 0x9a, 0x4f, 0x53, 0x0b, 0x43, 0xda, 0x28, 0x37, 0x6b, 0x3e, 0x6a, 0xc0, 0x04, 0xbf, 0x09, 0x76, 0x54, 0x71, 0x97, 0x69, 0x28, 0x0e, 0x2c, 0xd7, 0x43, 0x4f, 0x4e, 0x7d, 0xbf, 0x71, 0xb3, 0x37, 0x80, 0xae, 0xc2, 0x02, 0xdb, 0xa7, 0xe9, 0x3a, 0x81, 0xe7, 0xda, 0x36, 0xf6, 0x28, 0x4f, 0xba, 0xec, 0x4d, 0x31, 0xa6, 0xcd, 0xd1, 0xe9, 0xcd, 0x68, 0x96, 0xd9, 0x45, 0xaa, 0x21, 0xa6, 0xe9, 0x61, 0xdf, 0xe7, 0x09, 0xc9, 0xf0, 0x51, 0xad, 0xc3, 0x0c, 0xab, 0x6c, 0x11, 0xb8, 0x50, 0x76, 0xe2, 0x46, 0x5a, 0x49, 0x18, 0x69, 0x75, 0x16, 0x50, 0x7c, 0x3d, 0x17, 0xc6, 0xff, 0x52, 0x60, 0x86, 0x39, 0xef, 0x71, 0x2f, 0x31, 0x1f, 0x0d, 0xba, 0xc5, 0xab, 0xc0, 0x51, 0xd1, 0xbb, 0xb2, 0x71, 0x2e, 0x87, 0x21, 0x04, 0x23, 0xcd, 0x9a, 0xd1, 0x3a, 0x30, 0xcd, 0x98, 0xc5, 0x72, 0xaf, 0xe5, 0x44, 0xee, 0x75, 0x13, 0xa6, 0x0e, 0x2c, 0xdf, 0xda, 0xb5, 0x6c, 0x2b, 0x38, 0x62, 0x96, 0xa8, 0x38, 0x5d, 0x58, 0xe9, 0x81, 0x50, 0x33, 0xb4, 0x0a, 0x13, 0xfc, 0x15, 0xa6, 0x3b, 0x06, 0xb7, 0xb8, 0x63, 0xda, 0x38, 0x1f, 0x7b, 0x6c, 0xb4, 0x31, 0xe1, 0x42, 0xfc, 0xb8, 0x9c, 0x0b, 0x3f, 0xa0, 0x5c, 0xf0, 0x71, 0xf0, 0xac, 0x8b, 0xbb, 0xb8, 0x0f, 0x2e, 0xa4, 0x77, 0x2a, 0x65, 0x76, 0x4a, 0x32, 0xaa, 0x3c, 0x20, 0xa3, 0x18, 0x9d, 0x3d, 0x82, 0x38, 0x9d, 0x3f, 0x52, 0x60, 0x36, 0x94, 0xfb, 0xaf, 0x0c, 0xa9, 0x4f, 0x60, 0x2e, 0x45, 0x13, 0xd7, 0xc2, 0xab, 0xb0, 0xd0, 0xf1, 0xdc, 0x26, 0xf6, 0x7d, 0xcb, 0xd9, 0xd3, 0xe9, 0x57, 0x65, 0xcc, 0x0e, 0x10, 0x65, 0x2c, 0x13, 0x99, 0xef, 0x4d, 0x53, 0x48, 0x6a, 0x04, 0x7c, 0xf5, 0x0b, 0x05, 0xce, 0x3e, 0xc0, 0x81, 0xd6, 0xfb, 0xc6, 0xec, 0x11, 0xf6, 0x7d, 0x63, 0x0f, 0x47, 0x2e, 0xcb, 0x6d, 0x18, 0xa6, 0x05, 0x20, 0x86, 0x68, 0x7c, 0xe3, 0x62, 0x0e, 0xb5, 0x31, 0x14, 0xb4, 0x3a, 0xa4, 0x71, 0xb0, 0x3e, 0x98, 0x42, 0x6c, 0xcc, 0x72, 0x1e, 0x15, 0xfc, 0x80, 0x2f, 0xa1, 0xc2, 0xb8, 0xde, 0xe6, 0x33, 0x9c, 0x9c, 0x8f, 0x72, 0x93, 0x93, 0x72, 0x84, 0x75, 0xaa, 0x9b, 0xe1, 0x28, 0x4b, 0x44, 0x4e, 0xfa, 0xf1, 0xb1, 0x9a, 0x0d, 0x28, 0xbb, 0x28, 0x9e, 0x6c, 0x1c, 0x62, 0xc9, 0xc6, 0xef, 0x26, 0x93, 0x8d, 0x97, 0x8a, 0x19, 0x14, 0x11, 0x13, 0x4b, 0x34, 0xb6, 0x61, 0xe5, 0x01, 0x0e, 0xb6, 0x1e, 0x3e, 0x93, 0xdc, 0x45, 0x03, 0x80, 0xa9, 0xb4, 0xd3, 0x72, 0x43, 0x06, 0xf4, 0xb1, 0x1d, 0x11, 0x24, 0x6a, 0x26, 0xa9, 0xe8, 0x91, 0x5f, 0xbe, 0xfa, 0x0a, 0x56, 0x25, 0xdb, 0x71, 0xa6, 0x6f, 0xc3, 0x4c, 0xec, 0xeb, 0x43, 0x5a, 0x8c, 0x0c, 0xb7, 0xbd, 0xd0, 0xdf, 0xb6, 0xda, 0xb4, 0x97, 0x1c, 0xf0, 0xd5, 0x7f, 0x57, 0x60, 0x56, 0xc3, 0x46, 0xa7, 0x63, 0xb3, 0x88, 0x28, 0x3a, 0xdd, 0x3c, 0x0c, 0xf3, 0xcc, 0x3e, 0x7b, 0xcf, 0xf1, 0x27, 0xf9, 0xc7, 0x0a, 0xe2, 0x97, 0x74, 0xf9, 0xa4, 0xfe, 0xe8, 0xf1, 0x82, 0x0b, 0x75, 0x01, 0xe6, 0x52, 0x47, 0xe3, 0xd6, 0xe4, 0xa7, 0x0a, 0x2c, 0x69, 0xb8, 0xe5, 0x61, 0x7f, 0x3f, 0x2a, 0x72, 0x10, 0x6e, 0x7c, 0x05, 0xcf, 0xae, 0x2e, 0xc3, 0x19, 0x31, 0xa9, 0xfc, 0x2c, 0x37, 0x60, 0x61, 0xd3, 0xed, 0x3a, 0x44, 0x78, 0xd2, 0x02, 0xba, 0x0c, 0xd0, 0x72, 0xbd, 0x26, 0xbe, 0x8f, 0x83, 0xe6, 0x3e, 0xcf, 0xd8, 0xc6, 0x46, 0x54, 0x03, 0xaa, 0x59, 0x50, 0x2e, 0x6c, 0xf7, 0x60, 0x04, 0x3b, 0x01, 0xad, 0xe5, 0x32, 0x11, 0x7b, 0x2b, 0x47, 0xc4, 0xb8, 0x17, 0xb2, 0xf5, 0xf0, 0x19, 0xc5, 0xc5, 0xeb, 0xb5, 0x1c, 0x56, 0xfd, 0x69, 0x09, 0xe6, 0x35, 0x6c, 0x98, 0x02, 0xea, 0x36, 0xe0, 0x54, 0xd4, 0x1d, 0x51, 0xd9, 0x58, 0xce, 0xf3, 0x2d, 0x1e, 0x3e, 0xa3, 0x56, 0x97, 0xae, 0x95, 0x85, 0x62, 0xd9, 0x60, 0xae, 0x2c, 0x0a, 0xe6, 0x76, 0xa0, 0x6a, 0x39, 0x64, 0x85, 0x75, 0x80, 0x75, 0xec, 0x44, 0x16, 0xac, 0xcf, 0x8e, 0xb2, 0xb9, 0x08, 0xf8, 0x9e, 0x13, 0x9a, 0xa2, 0x86, 0x49, 0x04, 0xa3, 0x43, 0x90, 0xd0, 0x9a, 0xf4, 0x10, 0x25, 0x6c, 0x94, 0x0c, 0x6c, 0x5b, 0x9f, 0x61, 0x74, 0x01, 0xa6, 0x68, 0x5f, 0x04, 0x5d, 0xc1, 0xca, 0xf7, 0xc3, 0xb4, 0x7c, 0x4f, 0xdb, 0x25, 0x9e, 0x1a, 0x7b, 0x98, 0x75, 0xf3, 0xfd, 0x6d, 0x09, 0x16, 0x32, 0xbc, 0xe2, 0xd7, 0x71, 0x1c, 0x66, 0x09, 0xed, 0x45, 0xe9, 0x64, 0xf6, 0x02, 0x7d, 0x0f, 0xe6, 0x33, 0x48, 0xc3, 0x1c, 0xe1, 0xa0, 0x06, 0x70, 0x36, 0x8d, 0x9d, 0xa6, 0x08, 0x05, 0xec, 0x3a, 0x25, 0x62, 0xd7, 0xcf, 0x15, 0x58, 0x78, 0xda, 0xf5, 0xf6, 0xf0, 0xd7, 0x5b, 0xb6, 0xd4, 0x1a, 0x54, 0xb3, 0xc7, 0xe4, 0xca, 0xff, 0x65, 0x09, 0x16, 0x1e, 0xe1, 0xaf, 0x3d, 0x0f, 0xfe, 0x77, 0xf4, 0xeb, 0x2e, 0x54, 0xb3, 0xbc, 0xe2, 0xfa, 0x25, 0xc0, 0xa1, 0x88, 0x70, 0x7c, 0xae, 0xc0, 0x99, 0xc7, 0x6e, 0x60, 0xb5, 0x8e, 0x48, 0xb8, 0xed, 0x1e, 0x60, 0xef, 0x91, 0x41, 0x62, 0xe9, 0x88, 0xeb, 0xdf, 0x83, 0xf9, 0x16, 0x9f, 0xd1, 0xdb, 0x74, 0x4a, 0x4f, 0x38, 0x6c, 0x79, 0xfa, 0x91, 0x44, 0xc7, 0x7c, 0xb6, 0xd9, 0x56, 0x76, 0xd0, 0x57, 0xcf, 0xc1, 0xd9, 0x1c, 0x0a, 0xb8, 0x50, 0x18, 0xb0, 0xf4, 0x00, 0x07, 0x9b, 0x9e, 0xeb, 0xfb, 0xfc, 0x56, 0x12, 0x2f, 0xb7, 0x44, 0xe0, 0xa7, 0xa4, 0x02, 0xbf, 0xf3, 0x50, 0x09, 0x0c, 0x6f, 0x0f, 0x07, 0xd1, 0x2d, 0xb3, 0xd7, 0xdc, 0x24, 0x1b, 0xe5, 0xf8, 0xd4, 0x5f, 0x94, 0xe1, 0x8c, 0x78, 0x0f, 0xce, 0xcf, 0x36, 0xc1, 0x43, 0x4c, 0xc3, 0xee, 0x11, 0x0b, 0x43, 0xf9, 0xf1, 0x1f, 0xc8, 0x1c, 0xc4, 0x5c, 0x74, 0xd4, 0xf9, 0xf6, 0xef, 0x1e, 0x51, 0x07, 0x90, 0xbd, 0x61, 0x26, 0x82, 0xd8, 0x10, 0xfa, 0x5c, 0x81, 0xb9, 0x16, 0x2d, 0x88, 0xe9, 0x4d, 0xa3, 0xeb, 0xe3, 0xde, 0xb6, 0xcc, 0xde, 0x3d, 0x3a, 0xde, 0xb6, 0xac, 0xc6, 0xb6, 0x49, 0x30, 0x26, 0x36, 0x47, 0xad, 0xcc, 0x44, 0xad, 0x03, 0x33, 0x19, 0x2a, 0x05, 0xee, 0xe9, 0xbd, 0xa4, 0x7b, 0xba, 0x9e, 0x23, 0x0e, 0x69, 0x9a, 0xf8, 0xe5, 0xc5, 0x7d, 0xd4, 0x5a, 0x07, 0x16, 0x72, 0x08, 0x14, 0xec, 0x7b, 0x3b, 0xbe, 0x6f, 0x25, 0x37, 0xdd, 0xfb, 0x00, 0x07, 0xbd, 0xe2, 0x22, 0xc5, 0x1b, 0xf7, 0x8a, 0xff, 0x53, 0x81, 0x35, 0x5e, 0xce, 0xcb, 0x30, 0x2d, 0x53, 0x87, 0x90, 0x44, 0x66, 0xfd, 0x49, 0x19, 0x7a, 0xce, 0x84, 0x28, 0xea, 0xbb, 0x08, 0x73, 0xd5, 0xfd, 0x33, 0x8d, 0x77, 0x5b, 0x4c, 0x06, 0xb1, 0x27, 0x1f, 0xbd, 0x01, 0x93, 0x2d, 0xe2, 0x00, 0x3d, 0xc6, 0xcc, 0x97, 0xe2, 0xe5, 0xa7, 0xe4, 0xa0, 0xea, 0xc1, 0x9b, 0x7d, 0x9c, 0x35, 0x72, 0x97, 0x86, 0x42, 0x7f, 0xfc, 0x78, 0xd7, 0x4a, 0xa1, 0xd5, 0xf7, 0xe8, 0x37, 0x6d, 0xa1, 0x62, 0xd3, 0x97, 0x64, 0x1f, 0xb9, 0x31, 0x35, 0xa0, 0xdf, 0x6d, 0x25, 0xc1, 0x22, 0xc7, 0x61, 0xae, 0x57, 0x76, 0x09, 0x13, 0x31, 0x5d, 0xde, 0x47, 0x35, 0xa4, 0xf5, 0x6a, 0x32, 0xdb, 0x2c, 0x0b, 0xd3, 0x75, 0x68, 0x5e, 0x3c, 0xfc, 0xea, 0x92, 0xa7, 0x90, 0x58, 0x7e, 0x68, 0x92, 0x8f, 0xb2, 0x0c, 0x92, 0xda, 0x80, 0x79, 0xcd, 0x08, 0xb0, 0x6d, 0xb5, 0xad, 0xe0, 0xe3, 0x8e, 0x19, 0x4b, 0xe4, 0xad, 0xc3, 0x29, 0xd3, 0x08, 0x0c, 0xce, 0x8c, 0xa5, 0xbc, 0x46, 0xcc, 0x3b, 0xce, 0x91, 0x46, 0x17, 0xaa, 0x1f, 0xc1, 0x42, 0x06, 0x15, 0x3f, 0xc0, 0xa0, 0xb8, 0x36, 0xfe, 0xa9, 0x0e, 0xc0, 0x9d, 0xd2, 0x3b, 0x4f, 0x1b, 0xe8, 0x0f, 0x15, 0x98, 0x17, 0x7f, 0xd4, 0x8e, 0xae, 0x1e, 0xef, 0x5f, 0x28, 0x6a, 0xd7, 0x06, 0x86, 0xe3, 0x67, 0xf9, 0x23, 0x05, 0x16, 0x72, 0xfe, 0xf5, 0x00, 0x5d, 0x2b, 0xfa, 0xc7, 0x80, 0x3c, 0x6a, 0xae, 0x0f, 0x0e, 0xc8, 0xc9, 0xf9, 0x89, 0x02, 0x2b, 0x45, 0x5f, 0xfe, 0xa3, 0xef, 0x9e, 0xf4, 0x9f, 0x0c, 0x6a, 0x77, 0x4e, 0x80, 0x81, 0x53, 0x4a, 0x2e, 0x51, 0xfc, 0x4d, 0xbf, 0xe4, 0x12, 0xa5, 0xff, 0x25, 0x20, 0xb9, 0xc4, 0x82, 0x3f, 0x0f, 0xf8, 0x33, 0x05, 0x6a, 0xf9, 0x5f, 0xbe, 0xa3, 0xfc, 0xae, 0xb0, 0xc2, 0x7f, 0x04, 0xa8, 0xbd, 0x7f, 0x2c, 0x58, 0x4e, 0xd7, 0x8f, 0x14, 0x58, 0xcc, 0xfd, 0xae, 0x1d, 0xdd, 0xc8, 0x45, 0x5d, 0xf4, 0x59, 0x7d, 0xed, 0xe6, 0x71, 0x40, 0x39, 0x51, 0x0e, 0x4c, 0x26, 0x3e, 0x78, 0x46, 0x6f, 0xe7, 0x22, 0x13, 0x7d, 0x57, 0x5d, 0xab, 0xf7, 0xbb, 0x9c, 0xef, 0xf7, 0xb9, 0x02, 0xa7, 0x05, 0x5f, 0x0d, 0xa3, 0x2b, 0xf2, 0xdb, 0x16, 0x7e, 0xa7, 0x5c, 0x7b, 0x77, 0x30, 0x20, 0x4e, 0x42, 0x00, 0x53, 0xa9, 0x8f, 0x68, 0xd1, 0xba, 0xcc, 0xfd, 0x10, 0x54, 0x42, 0x6a, 0xef, 0xf4, 0x0f, 0xc0, 0x77, 0x3d, 0x84, 0xe9, 0xf4, 0x97, 0x60, 0x28, 0x1f, 0x4b, 0xce, 0xb7, 0x72, 0xb5, 0xcb, 0x03, 0x40, 0xc4, 0xc4, 0x2e, 0xb7, 0xdf, 0x51, 0x22, 0x76, 0x45, 0x5f, 0xa3, 0xd4, 0x4e, 0xd0, 0x5e, 0x89, 0xfe, 0x52, 0x81, 0x33, 0xb2, 0x76, 0x48, 0x74, 0xeb, 0x98, 0x5d, 0x94, 0x8c, 0xb4, 0x0f, 0x4e, 0xd4, 0x83, 0xc9, 0x59, 0x96, 0xd3, 0x33, 0x28, 0x65, 0x99, 0xbc, 0x63, 0x51, 0xca, 0xb2, 0x82, 0x16, 0xc5, 0xd8, 0x3d, 0x0a, 0x1a, 0xb2, 0x0b, 0xef, 0x31, 0xbf, 0x15, 0xbe, 0xf0, 0x1e, 0x65, 0xfd, 0xdf, 0xb1, 0x7b, 0x14, 0xb6, 0xed, 0x15, 0xdf, 0xa3, 0xac, 0x75, 0xb0, 0xf8, 0x1e, 0xa5, 0xbd, 0x82, 0xf1, 0x7b, 0xcc, 0x76, 0xe6, 0x15, 0xdf, 0x63, 0x6e, 0x5f, 0x60, 0xf1, 0x3d, 0xe6, 0x37, 0x02, 0xa2, 0xbf, 0xa0, 0xb9, 0xcd, 0xdc, 0x96, 0x3b, 0xf4, 0xfe, 0x40, 0x67, 0x4e, 0x36, 0xfd, 0xd5, 0x6e, 0x1d, 0x0f, 0x38, 0x41, 0x5a, 0x6e, 0xbf, 0xa9, 0x94, 0xb4, 0xa2, 0x8e, 0x57, 0x29, 0x69, 0xc5, 0x2d, 0xae, 0x7f, 0xad, 0xc0, 0xb2, 0xbc, 0xd1, 0x0c, 0x7d, 0x47, 0xb2, 0x41, 0x1f, 0xdd, 0x76, 0xb5, 0xdb, 0xc7, 0x86, 0xe7, 0x34, 0xfe, 0x40, 0x81, 0x6a, 0x5e, 0xbb, 0x21, 0xba, 0x2e, 0xc1, 0x2e, 0xed, 0xab, 0xac, 0xdd, 0x38, 0x06, 0x24, 0xa7, 0xe8, 0x0b, 0x05, 0x66, 0x45, 0x4d, 0x6b, 0x28, 0xff, 0xcd, 0x29, 0x69, 0xd1, 0xab, 0xbd, 0x37, 0x20, 0x14, 0xa7, 0xe2, 0xaf, 0xe8, 0x9f, 0x4f, 0x49, 0x9a, 0xb2, 0xd0, 0x07, 0x05, 0xb2, 0x21, 0xef, 0xa8, 0xab, 0x7d, 0xe7, 0xb8, 0xe0, 0x9c, 0xc0, 0xcf, 0x60, 0x26, 0xd3, 0x9f, 0x84, 0x2e, 0x4b, 0x90, 0x8a, 0xdb, 0xc6, 0x6a, 0x1b, 0x83, 0x80, 0xf4, 0xbc, 0x91, 0x54, 0xc7, 0x91, 0xc4, 0x1b, 0x11, 0xf7, 0x49, 0x49, 0xbc, 0x91, 0x9c, 0x66, 0x26, 0xf4, 0x02, 0x26, 0xe2, 0x1d, 0x20, 0xe8, 0xdb, 0x52, 0x0c, 0xa9, 0x96, 0xa7, 0xda, 0xdb, 0x7d, 0xae, 0x8e, 0x49, 0xa1, 0xa8, 0x85, 0x43, 0x22, 0x85, 0x92, 0x2e, 0x14, 0x89, 0x14, 0x4a, 0xfb, 0x44, 0x88, 0xe7, 0x29, 0xe8, 0xcc, 0x90, 0x78, 0x9e, 0xf9, 0x6d, 0x1e, 0xb5, 0x77, 0x07, 0x03, 0x8a, 0x3e, 0x55, 0x81, 0x5e, 0xa3, 0x03, 0xba, 0x94, 0x8b, 0x23, 0xd3, 0x3d, 0x51, 0x7b, 0xab, 0xaf, 0xb5, 0xbd, 0x6d, 0x7a, 0x9d, 0x04, 0x92, 0x6d, 0x32, 0xdd, 0x15, 0x92, 0x6d, 0xb2, 0xad, 0x09, 0x6c, 0x9b, 0xb0, 0x11, 0x40, 0xba, 0x4d, 0xaa, 0x7d, 0x41, 0xba, 0x4d, 0xba, 0xb3, 0x80, 0x44, 0x28, 0x89, 0x22, 0xbe, 0x24, 0x42, 0x11, 0x35, 0x20, 0x48, 0x22, 0x14, 0x71, 0x6f, 0x00, 0x09, 0x65, 0xc5, 0xc5, 0x70, 0x49, 0x28, 0x2b, 0x6d, 0x0a, 0x90, 0x84, 0xb2, 0x05, 0x65, 0x7c, 0xe2, 0xc0, 0xe4, 0xd6, 0x9d, 0x25, 0x0e, 0x4c, 0x51, 0x69, 0x5c, 0xe2, 0xc0, 0x14, 0x97, 0xb9, 0x1d, 0x98, 0x4c, 0x54, 0x6d, 0x25, 0x17, 0x22, 0x2a, 0x5c, 0x4b, 0x2e, 0x44, 0x58, 0x0c, 0xa6, 0xe6, 0x43, 0x54, 0x61, 0x45, 0xb2, 0xf0, 0x2f, 0xb7, 0x76, 0x2c, 0x31, 0x1f, 0xb2, 0x32, 0x2e, 0x89, 0xdf, 0xd2, 0xb5, 0x58, 0x49, 0xfc, 0x96, 0x53, 0xf1, 0x95, 0xc4, 0x6f, 0xb9, 0x85, 0xde, 0x00, 0xa6, 0x52, 0x45, 0x47, 0xc9, 0x0b, 0x42, 0x5c, 0xca, 0x95, 0xbc, 0x20, 0xf2, 0xea, 0x99, 0x24, 0x5c, 0x4d, 0x15, 0xb5, 0x64, 0xe1, 0xaa, 0xb8, 0xcc, 0x27, 0x0b, 0x57, 0x73, 0x2a, 0x66, 0x64, 0xe3, 0x74, 0x11, 0x48, 0xb2, 0x71, 0x4e, 0x6d, 0x4d, 0xb2, 0x71, 0x6e, 0x85, 0xe9, 0x0f, 0x14, 0x98, 0x13, 0xd6, 0x6d, 0x50, 0xbe, 0xc4, 0xc8, 0x2a, 0x4d, 0xb5, 0xab, 0x83, 0x82, 0xc5, 0xe4, 0x5d, 0x54, 0xf5, 0x90, 0xc8, 0xbb, 0xa4, 0x9c, 0x24, 0x91, 0x77, 0x69, 0x81, 0xe8, 0x4b, 0x25, 0xfa, 0xaa, 0x29, 0x3f, 0xbd, 0x8e, 0xee, 0x14, 0xc5, 0x1b, 0x85, 0x65, 0x88, 0xda, 0xdd, 0x93, 0xa0, 0x48, 0xa4, 0x74, 0xe2, 0xf9, 0x75, 0x79, 0x4a, 0x47, 0x90, 0xc0, 0x97, 0xa7, 0x74, 0x84, 0xa9, 0x7b, 0xa2, 0x99, 0xc9, 0xa4, 0xb8, 0x4c, 0x33, 0x85, 0x99, 0x78, 0x99, 0x66, 0x8a, 0xf3, 0xed, 0x77, 0x6f, 0xfc, 0xfa, 0xb5, 0x3d, 0x2b, 0xd8, 0xef, 0xee, 0xd6, 0x9b, 0x6e, 0x7b, 0x3d, 0xf1, 0x9f, 0xe4, 0xf5, 0x3d, 0xec, 0xb0, 0x3f, 0xa8, 0x8f, 0xfd, 0x43, 0xfe, 0xfb, 0xfc, 0xe7, 0xc1, 0xe5, 0xdd, 0x61, 0x3a, 0x77, 0xe5, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x55, 0xea, 0x8b, 0xf1, 0x4d, 0x5f, 0x00, 0x00, }, // google/protobuf/duration.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a, 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0x56, 0x5c, 0x1c, 0x2e, 0x50, 0x25, 0x42, 0x12, 0x5c, 0xec, 0xc5, 0xa9, 0xc9, 0xf9, 0x79, 0x29, 0xc5, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x30, 0xae, 0x90, 0x08, 0x17, 0x6b, 0x5e, 0x62, 0x5e, 0x7e, 0xb1, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x84, 0xe3, 0xd4, 0xcc, 0xc8, 0x25, 0x9c, 0x9c, 0x9f, 0xab, 0x87, 0x66, 0xa6, 0x13, 0x2f, 0xcc, 0xc4, 0x00, 0x90, 0x48, 0x00, 0x63, 0x94, 0x21, 0x54, 0x45, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x5e, 0x7e, 0x51, 0x3a, 0xc2, 0x81, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0xfa, 0xd9, 0x79, 0xf9, 0xe5, 0x79, 0x70, 0xc7, 0x16, 0x24, 0xfd, 0x60, 0x64, 0x5c, 0xc4, 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0xa2, 0x39, 0x00, 0xaa, 0x43, 0x2f, 0x3c, 0x35, 0x27, 0xc7, 0x1b, 0xa4, 0x3e, 0x04, 0xa4, 0x35, 0x89, 0x0d, 0x6c, 0x94, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xef, 0x8a, 0xb4, 0xc3, 0xfb, 0x00, 0x00, 0x00, }, // google/protobuf/timestamp.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0xd0, 0x03, 0x0b, 0x09, 0xf1, 0x43, 0x14, 0xe8, 0xc1, 0x14, 0x28, 0x59, 0x73, 0x71, 0x86, 0xc0, 0xd4, 0x08, 0x49, 0x70, 0xb1, 0x17, 0xa7, 0x26, 0xe7, 0xe7, 0xa5, 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x30, 0x07, 0xc1, 0xb8, 0x42, 0x22, 0x5c, 0xac, 0x79, 0x89, 0x79, 0xf9, 0xc5, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xac, 0x41, 0x10, 0x8e, 0x53, 0x2b, 0x23, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0xa1, 0x4e, 0x7c, 0x70, 0x23, 0x03, 0x40, 0x42, 0x01, 0x8c, 0x51, 0x46, 0x50, 0x25, 0xe9, 0xf9, 0x39, 0x89, 0x79, 0xe9, 0x7a, 0xf9, 0x45, 0xe9, 0x48, 0x6e, 0xac, 0x2c, 0x48, 0x2d, 0xd6, 0xcf, 0xce, 0xcb, 0x2f, 0xcf, 0x43, 0xb8, 0xb7, 0x20, 0xe9, 0x07, 0x23, 0xe3, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55, 0x4c, 0x72, 0xee, 0x10, 0xdd, 0x01, 0x50, 0x2d, 0x7a, 0xe1, 0xa9, 0x39, 0x39, 0xde, 0x20, 0x0d, 0x21, 0x20, 0xbd, 0x49, 0x6c, 0x60, 0xb3, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xae, 0x65, 0xce, 0x7d, 0xff, 0x00, 0x00, 0x00, }, // google/protobuf/wrappers.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x2f, 0x4a, 0x2c, 0x28, 0x48, 0x2d, 0x2a, 0xd6, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0xca, 0x5c, 0xdc, 0x2e, 0xf9, 0xa5, 0x49, 0x39, 0xa9, 0x61, 0x89, 0x39, 0xa5, 0xa9, 0x42, 0x22, 0x5c, 0xac, 0x65, 0x20, 0x86, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x63, 0x10, 0x84, 0xa3, 0xa4, 0xc4, 0xc5, 0xe5, 0x96, 0x93, 0x9f, 0x58, 0x82, 0x45, 0x0d, 0x13, 0x92, 0x1a, 0xcf, 0xbc, 0x12, 0x33, 0x13, 0x2c, 0x6a, 0x98, 0x61, 0x6a, 0x94, 0xb9, 0xb8, 0x43, 0x71, 0x29, 0x62, 0x41, 0x35, 0xc8, 0xd8, 0x08, 0x8b, 0x1a, 0x56, 0x34, 0x83, 0xb0, 0x2a, 0xe2, 0x85, 0x29, 0x52, 0xe4, 0xe2, 0x74, 0xca, 0xcf, 0xcf, 0xc1, 0xa2, 0x84, 0x03, 0xc9, 0x9c, 0xe0, 0x92, 0xa2, 0xcc, 0xbc, 0x74, 0x2c, 0x8a, 0x38, 0x91, 0x1c, 0xe4, 0x54, 0x59, 0x92, 0x5a, 0x8c, 0x45, 0x0d, 0x0f, 0x54, 0x8d, 0x53, 0x33, 0x23, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x5a, 0xf0, 0x3a, 0xf1, 0x86, 0x43, 0xc3, 0x3f, 0x00, 0x24, 0x12, 0xc0, 0x18, 0x65, 0x08, 0x55, 0x91, 0x9e, 0x9f, 0x93, 0x98, 0x97, 0xae, 0x97, 0x5f, 0x94, 0x8e, 0x88, 0xab, 0x92, 0xca, 0x82, 0xd4, 0x62, 0xfd, 0xec, 0xbc, 0xfc, 0xf2, 0x3c, 0x78, 0xbc, 0x15, 0x24, 0xfd, 0x60, 0x64, 0x5c, 0xc4, 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0xa2, 0x39, 0x00, 0xaa, 0x43, 0x2f, 0x3c, 0x35, 0x27, 0xc7, 0x1b, 0xa4, 0x3e, 0x04, 0xa4, 0x35, 0x89, 0x0d, 0x6c, 0x94, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x3c, 0x92, 0x48, 0x30, 0x06, 0x02, 0x00, 0x00, }, // uber/cadence/api/v1/common.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdd, 0x72, 0x22, 0xc7, 0x15, 0xf6, 0xc0, 0xa2, 0x9f, 0x03, 0xbb, 0x42, 0xad, 0xfd, 0x61, 0xb5, 0x5e, 0xaf, 0x16, 0x97, 0x63, 0x79, 0x2b, 0x86, 0x88, 0x4d, 0x52, 0x2e, 0x3b, 0x4e, 0x82, 0xd0, 0x48, 0x9a, 0x5d, 0x02, 0xa4, 0x99, 0x95, 0xac, 0xa4, 0xca, 0x53, 0xcd, 0x4c, 0x83, 0x3b, 0x0c, 0xd3, 0x93, 0x99, 0x1e, 0x56, 0xf8, 0x22, 0x95, 0xcb, 0xe4, 0x26, 0x8f, 0x90, 0x8b, 0xbc, 0x48, 0x1e, 0x20, 0x97, 0x79, 0x97, 0x5c, 0xa7, 0xba, 0xa7, 0x07, 0x81, 0xc2, 0x1a, 0x5f, 0xa4, 0x7c, 0x47, 0x9f, 0xf3, 0x7d, 0xa7, 0xbf, 0xd3, 0xdd, 0xe7, 0x1c, 0x06, 0x0e, 0x92, 0x01, 0x8d, 0xea, 0x2e, 0xf1, 0x68, 0xe0, 0xd2, 0x3a, 0x09, 0x59, 0x7d, 0x7a, 0x54, 0x77, 0xf9, 0x64, 0xc2, 0x83, 0x5a, 0x18, 0x71, 0xc1, 0xd1, 0x9e, 0x44, 0xd4, 0x34, 0xa2, 0x46, 0x42, 0x56, 0x9b, 0x1e, 0xed, 0x7f, 0x30, 0xe2, 0x7c, 0xe4, 0xd3, 0xba, 0x82, 0x0c, 0x92, 0x61, 0xdd, 0x4b, 0x22, 0x22, 0x58, 0x46, 0xaa, 0xbe, 0x86, 0xdd, 0x4b, 0x1e, 0x8d, 0x87, 0x3e, 0x7f, 0x6b, 0x5e, 0x53, 0x37, 0x91, 0x2e, 0xf4, 0x0c, 0x8a, 0x6f, 0xb5, 0xd1, 0x61, 0x5e, 0xc5, 0x38, 0x30, 0x0e, 0xb7, 0x31, 0x64, 0x26, 0xcb, 0x43, 0x0f, 0x60, 0x23, 0x4a, 0x02, 0xe9, 0xcb, 0x29, 0x5f, 0x21, 0x4a, 0x02, 0xcb, 0xab, 0x56, 0xa1, 0x94, 0x05, 0xb3, 0x67, 0x21, 0x45, 0x08, 0xee, 0x04, 0x64, 0x42, 0x75, 0x00, 0xf5, 0x5b, 0x62, 0x9a, 0xae, 0x60, 0x53, 0x26, 0x66, 0xef, 0xc4, 0x3c, 0x85, 0xcd, 0x1e, 0x99, 0xf9, 0x9c, 0x78, 0xd2, 0xed, 0x11, 0x41, 0x94, 0xbb, 0x84, 0xd5, 0xef, 0xea, 0x17, 0xb0, 0x79, 0x4a, 0x98, 0x9f, 0x44, 0x14, 0x3d, 0x84, 0x8d, 0x88, 0x92, 0x98, 0x07, 0x9a, 0xaf, 0x57, 0xa8, 0x02, 0x9b, 0x1e, 0x15, 0x84, 0xf9, 0xb1, 0x52, 0x58, 0xc2, 0xd9, 0xb2, 0xfa, 0x77, 0x03, 0xee, 0xfc, 0x86, 0x4e, 0x38, 0xfa, 0x12, 0x36, 0x86, 0x8c, 0xfa, 0x5e, 0x5c, 0x31, 0x0e, 0xf2, 0x87, 0xc5, 0xc6, 0x47, 0xb5, 0x15, 0xe7, 0x57, 0x93, 0xd0, 0xda, 0xa9, 0xc2, 0x99, 0x81, 0x88, 0x66, 0x58, 0x93, 0xf6, 0x2f, 0xa1, 0xb8, 0x60, 0x46, 0x65, 0xc8, 0x8f, 0xe9, 0x4c, 0xab, 0x90, 0x3f, 0x51, 0x03, 0x0a, 0x53, 0xe2, 0x27, 0x54, 0x09, 0x28, 0x36, 0xde, 0x5f, 0x19, 0x5e, 0xa7, 0x89, 0x53, 0xe8, 0xe7, 0xb9, 0xcf, 0x8c, 0xea, 0x3f, 0x0c, 0xd8, 0x38, 0xa7, 0xc4, 0xa3, 0x11, 0xfa, 0xd5, 0x2d, 0x89, 0x1f, 0xaf, 0x8c, 0x91, 0x82, 0x7f, 0x58, 0x91, 0xff, 0x36, 0xa0, 0xdc, 0xa7, 0x24, 0x72, 0xbf, 0x69, 0x0a, 0x11, 0xb1, 0x41, 0x22, 0x68, 0x8c, 0x1c, 0xb8, 0xc7, 0x02, 0x8f, 0x5e, 0x53, 0xcf, 0x59, 0x92, 0xfd, 0xd9, 0xca, 0xa8, 0xb7, 0xe9, 0x35, 0x2b, 0xe5, 0x2e, 0xe6, 0x71, 0x97, 0x2d, 0xda, 0xf6, 0xbf, 0x06, 0xf4, 0xbf, 0xa0, 0xff, 0x63, 0x56, 0x43, 0xd8, 0x3a, 0x21, 0x82, 0x1c, 0xfb, 0x7c, 0x80, 0x4e, 0xe1, 0x2e, 0x0d, 0x5c, 0xee, 0xb1, 0x60, 0xe4, 0x88, 0x59, 0x98, 0x3e, 0xd0, 0x7b, 0x8d, 0xe7, 0x2b, 0x63, 0x99, 0x1a, 0x29, 0x5f, 0x34, 0x2e, 0xd1, 0x85, 0xd5, 0xfc, 0x01, 0xe7, 0x16, 0x1e, 0x70, 0x2f, 0x2d, 0x3a, 0x1a, 0x5d, 0xd0, 0x28, 0x66, 0x3c, 0xb0, 0x82, 0x21, 0x97, 0x40, 0x36, 0x09, 0xfd, 0xac, 0x10, 0xe4, 0x6f, 0xf4, 0x31, 0xec, 0x0c, 0x29, 0x11, 0x49, 0x44, 0x9d, 0x69, 0x0a, 0xd5, 0x05, 0x77, 0x4f, 0x9b, 0x75, 0x80, 0xea, 0x6b, 0x78, 0xd4, 0x4f, 0xc2, 0x90, 0x47, 0x82, 0x7a, 0x2d, 0x9f, 0xd1, 0x40, 0x68, 0x4f, 0x2c, 0x6b, 0x75, 0xc4, 0x9d, 0xd8, 0x1b, 0xeb, 0xc8, 0x85, 0x11, 0xef, 0x7b, 0x63, 0xf4, 0x18, 0xb6, 0xfe, 0x40, 0xa6, 0x44, 0x39, 0xd2, 0x98, 0x9b, 0x72, 0xdd, 0xf7, 0xc6, 0xd5, 0x3f, 0xe7, 0xa1, 0x88, 0xa9, 0x88, 0x66, 0x3d, 0xee, 0x33, 0x77, 0x86, 0x4e, 0xa0, 0xcc, 0x02, 0x26, 0x18, 0xf1, 0x1d, 0x16, 0x08, 0x1a, 0x4d, 0x49, 0xaa, 0xb2, 0xd8, 0x78, 0x5c, 0x4b, 0xdb, 0x4b, 0x2d, 0x6b, 0x2f, 0xb5, 0x13, 0xdd, 0x5e, 0xf0, 0x8e, 0xa6, 0x58, 0x9a, 0x81, 0xea, 0xb0, 0x37, 0x20, 0xee, 0x98, 0x0f, 0x87, 0x8e, 0xcb, 0xe9, 0x70, 0xc8, 0x5c, 0x29, 0x53, 0xed, 0x6d, 0x60, 0xa4, 0x5d, 0xad, 0x1b, 0x8f, 0xdc, 0x76, 0x42, 0xae, 0xd9, 0x24, 0x99, 0xdc, 0x6c, 0x9b, 0x5f, 0xbb, 0xad, 0xa6, 0xcc, 0xb7, 0xfd, 0xe4, 0x26, 0x0a, 0x11, 0x82, 0x4e, 0x42, 0x11, 0x57, 0xee, 0x1c, 0x18, 0x87, 0x85, 0x39, 0xb4, 0xa9, 0xcd, 0xe8, 0x4b, 0x78, 0x12, 0xf0, 0xc0, 0x89, 0x64, 0xea, 0x64, 0xe0, 0x53, 0x87, 0x46, 0x11, 0x8f, 0x9c, 0xb4, 0xa5, 0xc4, 0x95, 0xc2, 0x41, 0xfe, 0x70, 0x1b, 0x57, 0x02, 0x1e, 0xe0, 0x0c, 0x61, 0x4a, 0x00, 0x4e, 0xfd, 0xe8, 0x15, 0xec, 0xd1, 0xeb, 0x90, 0xa5, 0x42, 0x6e, 0x24, 0x6f, 0xac, 0x93, 0x8c, 0x6e, 0x58, 0x99, 0xea, 0xea, 0x04, 0x1e, 0x59, 0x31, 0xf7, 0x95, 0xf1, 0x2c, 0xe2, 0x49, 0xd8, 0x23, 0x91, 0x60, 0xaa, 0x39, 0xaf, 0x68, 0x98, 0xe8, 0x97, 0x50, 0x88, 0x05, 0x11, 0xe9, 0x83, 0xbf, 0xd7, 0x38, 0x5c, 0xf9, 0x48, 0x97, 0x03, 0xf6, 0x25, 0x1e, 0xa7, 0xb4, 0xea, 0x14, 0x9e, 0x2c, 0x7b, 0x5b, 0x3c, 0x18, 0xb2, 0x91, 0x56, 0x88, 0x2e, 0xa1, 0xcc, 0x32, 0xb7, 0x33, 0x92, 0xfe, 0xac, 0xb4, 0x7f, 0xfc, 0x3d, 0x76, 0x9a, 0x4b, 0xc7, 0x3b, 0x6c, 0xc9, 0x11, 0x57, 0xff, 0x65, 0xc0, 0x7e, 0x33, 0x9e, 0x05, 0x6e, 0x36, 0x36, 0x96, 0xf7, 0xad, 0xc0, 0x26, 0x0d, 0xe4, 0x39, 0xa7, 0x33, 0x68, 0x0b, 0x67, 0x4b, 0xd4, 0x80, 0x07, 0x61, 0x44, 0x3d, 0x3a, 0x64, 0x01, 0xf5, 0x9c, 0x3f, 0x26, 0x34, 0xa1, 0x8e, 0x3a, 0x95, 0xf4, 0x29, 0xef, 0xdd, 0x38, 0x7f, 0x2b, 0x7d, 0x1d, 0x79, 0x48, 0x4f, 0x01, 0x52, 0xa0, 0x2a, 0xe7, 0xbc, 0x02, 0x6e, 0x2b, 0x8b, 0x2a, 0xd4, 0x5f, 0x43, 0x29, 0x75, 0xbb, 0x4a, 0x83, 0x7a, 0x24, 0xc5, 0xc6, 0xd3, 0x95, 0x09, 0x66, 0x5d, 0x02, 0x17, 0x15, 0x25, 0x55, 0x5d, 0xfd, 0x4f, 0x1e, 0xde, 0x57, 0xb3, 0x8d, 0xb6, 0xfc, 0x24, 0x16, 0x34, 0xea, 0x53, 0x9f, 0xba, 0x32, 0x13, 0x5d, 0x48, 0x7d, 0xd8, 0x8a, 0x45, 0x44, 0x04, 0x1d, 0xcd, 0x74, 0x3b, 0x79, 0xb9, 0x32, 0xfc, 0xea, 0x20, 0x7d, 0x4d, 0x3d, 0xce, 0x55, 0x0c, 0x3c, 0x0f, 0x84, 0xfe, 0x62, 0xc0, 0x87, 0x44, 0x11, 0x1c, 0x37, 0x65, 0x38, 0xb1, 0x60, 0xee, 0x78, 0xe6, 0x44, 0x74, 0x24, 0x2f, 0x4c, 0xe7, 0x93, 0xf6, 0xc2, 0x9f, 0x7e, 0x8f, 0x0d, 0x15, 0x1b, 0x2b, 0x72, 0x9a, 0x99, 0xdc, 0xf1, 0xfc, 0x3d, 0xfc, 0x8c, 0x7c, 0x37, 0x0c, 0xfd, 0xcd, 0x80, 0x8f, 0x6e, 0x49, 0xa1, 0xd7, 0x82, 0x46, 0x01, 0xf1, 0x1d, 0x1a, 0x08, 0x26, 0x66, 0x99, 0x98, 0xb4, 0x8e, 0x7f, 0xbe, 0x5e, 0x8c, 0xa9, 0xf9, 0xa6, 0xa2, 0x2f, 0xc9, 0x79, 0x4e, 0xd6, 0x01, 0x11, 0x86, 0xdd, 0x4c, 0x08, 0xc9, 0x06, 0x8d, 0xbe, 0xd8, 0xd5, 0xe3, 0x5e, 0x07, 0x9b, 0x4f, 0x25, 0x5c, 0x76, 0x6f, 0x59, 0x8e, 0x77, 0x61, 0x27, 0x3b, 0x7b, 0x9d, 0x4d, 0xf5, 0x17, 0x50, 0xbe, 0x4d, 0x44, 0xf7, 0xa1, 0x10, 0xbb, 0x3c, 0xcc, 0xea, 0x34, 0x5d, 0xcc, 0x8b, 0x37, 0xb7, 0xf0, 0x6f, 0xe7, 0x15, 0x3c, 0x5b, 0x73, 0xfe, 0xe8, 0x43, 0xb8, 0xbb, 0x74, 0xa7, 0x3a, 0x68, 0x29, 0x5e, 0x80, 0x7e, 0x9e, 0xab, 0x18, 0xd5, 0xbf, 0x1a, 0xf0, 0x7c, 0xed, 0xf9, 0xa1, 0x9f, 0xc0, 0xfd, 0xdb, 0xf7, 0x32, 0x1f, 0x71, 0xdb, 0xb2, 0x1f, 0x2d, 0x72, 0x54, 0x71, 0xd4, 0x64, 0x6f, 0x5b, 0x66, 0xc8, 0x99, 0x9b, 0xa6, 0xb1, 0xbb, 0x4c, 0x78, 0x4d, 0x67, 0x4a, 0xcb, 0x57, 0xb0, 0xdb, 0x23, 0x23, 0x16, 0xa8, 0x5a, 0xee, 0x86, 0x42, 0x4d, 0xa3, 0x27, 0xb0, 0x1d, 0x92, 0x11, 0x75, 0x62, 0xf6, 0x6d, 0xba, 0x5f, 0x01, 0x6f, 0x49, 0x43, 0x9f, 0x7d, 0x4b, 0xd1, 0x8f, 0x60, 0x27, 0xa0, 0xd7, 0xc2, 0x51, 0x08, 0xc1, 0xc7, 0x34, 0xd0, 0x63, 0xf3, 0xae, 0x34, 0xf7, 0xc8, 0x88, 0xda, 0xd2, 0xf8, 0xe2, 0x2d, 0x94, 0x16, 0x27, 0x2e, 0x7a, 0x0c, 0x0f, 0xcc, 0x4e, 0xab, 0x7b, 0x62, 0x75, 0xce, 0x1c, 0xfb, 0xaa, 0x67, 0x3a, 0x56, 0xe7, 0xa2, 0xd9, 0xb6, 0x4e, 0xca, 0xef, 0xa1, 0x7d, 0x78, 0xb8, 0xec, 0xb2, 0xcf, 0xb1, 0x75, 0x6a, 0xe3, 0xcb, 0xb2, 0x81, 0x1e, 0x02, 0x5a, 0xf6, 0xbd, 0xea, 0x77, 0x3b, 0xe5, 0x1c, 0xaa, 0xc0, 0xfd, 0x65, 0x7b, 0x0f, 0x77, 0xed, 0xee, 0xcb, 0x72, 0xfe, 0xc5, 0x9f, 0x60, 0x6f, 0x45, 0x17, 0x45, 0xcf, 0xe1, 0xa9, 0xd5, 0xef, 0xb6, 0x9b, 0xb6, 0xd5, 0xed, 0x38, 0x67, 0xb8, 0xfb, 0xa6, 0xe7, 0xf4, 0xed, 0xa6, 0xbd, 0xa8, 0xe3, 0x9d, 0x90, 0x73, 0xb3, 0xd9, 0xb6, 0xcf, 0xaf, 0xca, 0xc6, 0xbb, 0x21, 0x27, 0xb8, 0x69, 0x75, 0xcc, 0x93, 0x72, 0xee, 0xc5, 0x3f, 0x0d, 0xf8, 0xe0, 0xbb, 0x9b, 0x03, 0xfa, 0x14, 0x3e, 0x69, 0xb6, 0x6c, 0xeb, 0xc2, 0x74, 0x5a, 0xed, 0x37, 0x7d, 0xdb, 0xc4, 0x4e, 0xdf, 0x6c, 0x9b, 0x2d, 0x15, 0xb4, 0x6f, 0xe3, 0xa6, 0x6d, 0x9e, 0x5d, 0x2d, 0xe8, 0x7a, 0x09, 0xf5, 0xf5, 0x70, 0x6c, 0x9e, 0xa5, 0x6b, 0xab, 0xf5, 0x5a, 0x2a, 0xfd, 0x19, 0x1c, 0xad, 0x27, 0x99, 0x5f, 0xd9, 0x26, 0xee, 0x34, 0xdb, 0x8e, 0xd9, 0xb1, 0x2d, 0xfb, 0xaa, 0x9c, 0xdb, 0xcf, 0x55, 0x8c, 0x17, 0x5f, 0x43, 0x49, 0xfe, 0x77, 0xe7, 0x53, 0x1a, 0x65, 0x57, 0x77, 0xda, 0xb4, 0xda, 0xdd, 0x0b, 0x13, 0xdf, 0xbe, 0xba, 0x47, 0xb0, 0xb7, 0xec, 0x3a, 0xed, 0xe2, 0x96, 0x59, 0x36, 0xe4, 0x9d, 0x2e, 0x3b, 0xce, 0x70, 0xb3, 0x65, 0x9e, 0xbe, 0x69, 0x97, 0x73, 0xc7, 0xbf, 0x87, 0x47, 0x2e, 0x9f, 0xac, 0xaa, 0xed, 0xe3, 0x62, 0x4b, 0x7d, 0x2d, 0xf5, 0xe4, 0x00, 0xee, 0x19, 0xbf, 0x3b, 0x1a, 0x31, 0xf1, 0x4d, 0x32, 0xa8, 0xb9, 0x7c, 0x52, 0x5f, 0xfc, 0xb6, 0xfa, 0x94, 0x79, 0x7e, 0x7d, 0xc4, 0xd3, 0x2f, 0x26, 0xfd, 0xa1, 0xf5, 0x05, 0x09, 0xd9, 0xf4, 0x68, 0xb0, 0xa1, 0x6c, 0x2f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x5e, 0xe2, 0xc9, 0x06, 0x8c, 0x0d, 0x00, 0x00, }, // uber/cadence/api/v1/history.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5c, 0x5d, 0x6c, 0x1c, 0x49, 0xb5, 0xde, 0x9e, 0xb1, 0xc7, 0x9e, 0x33, 0x8e, 0x3d, 0x2e, 0x3b, 0x8e, 0xff, 0x12, 0x3b, 0x93, 0x6c, 0xe2, 0x75, 0x9c, 0x71, 0xe2, 0x64, 0x93, 0x4d, 0xb2, 0x3f, 0xd7, 0x76, 0x6c, 0xed, 0x48, 0xbe, 0x49, 0x6e, 0xc7, 0xc9, 0xde, 0x7b, 0xb5, 0xd2, 0xd0, 0xee, 0x2e, 0xc7, 0x8d, 0x67, 0xba, 0x67, 0xbb, 0x6b, 0x3c, 0x31, 0x82, 0x27, 0x1e, 0x90, 0x10, 0x2b, 0x58, 0xad, 0x90, 0x58, 0x09, 0x04, 0x42, 0x02, 0xb1, 0xfc, 0x68, 0x11, 0x08, 0xf1, 0xf7, 0x02, 0x48, 0x68, 0x91, 0x40, 0x0b, 0x4f, 0xbc, 0xf0, 0x84, 0x84, 0x10, 0xfb, 0xc6, 0x03, 0xcb, 0x33, 0xea, 0xea, 0xea, 0x99, 0xe9, 0x9e, 0xaa, 0xfe, 0x19, 0x3b, 0x59, 0xd0, 0xe6, 0x6d, 0xba, 0xfb, 0x9c, 0xd3, 0x5f, 0x55, 0x9d, 0x73, 0xea, 0xd4, 0x39, 0xa7, 0x07, 0x4e, 0xd6, 0xb7, 0xb0, 0xb5, 0xa8, 0x2a, 0x1a, 0x36, 0x54, 0xbc, 0xa8, 0xd4, 0xf4, 0xc5, 0xbd, 0x8b, 0x8b, 0x3b, 0xba, 0x4d, 0x4c, 0x6b, 0xbf, 0x58, 0xb3, 0x4c, 0x62, 0xa2, 0x11, 0x87, 0xa4, 0xc8, 0x48, 0x8a, 0x4a, 0x4d, 0x2f, 0xee, 0x5d, 0x9c, 0x3c, 0xf1, 0xc0, 0x34, 0x1f, 0x54, 0xf0, 0x22, 0x25, 0xd9, 0xaa, 0x6f, 0x2f, 0x6a, 0x75, 0x4b, 0x21, 0xba, 0x69, 0xb8, 0x4c, 0x93, 0x33, 0xc1, 0xe7, 0x44, 0xaf, 0x62, 0x9b, 0x28, 0xd5, 0x1a, 0x23, 0x98, 0xe5, 0xbd, 0x58, 0x35, 0xab, 0xd5, 0xa6, 0x88, 0x02, 0x8f, 0x82, 0x28, 0xf6, 0x6e, 0x45, 0xb7, 0x49, 0x18, 0x4d, 0xc3, 0xb4, 0x76, 0xb7, 0x2b, 0x66, 0xc3, 0xa5, 0x29, 0xdc, 0x84, 0xbe, 0x97, 0xdd, 0x01, 0xa1, 0x6b, 0x90, 0xc1, 0x7b, 0xd8, 0x20, 0xf6, 0xb8, 0x34, 0x9b, 0x9e, 0xcb, 0x2d, 0x9d, 0x2c, 0x72, 0xc6, 0x56, 0x64, 0xd4, 0x6b, 0x0e, 0xa5, 0xcc, 0x18, 0x0a, 0xef, 0x5f, 0x85, 0x81, 0xf6, 0x07, 0x68, 0x02, 0xfa, 0xe9, 0xa3, 0xb2, 0xae, 0x8d, 0x4b, 0xb3, 0xd2, 0x5c, 0x5a, 0xee, 0xa3, 0xd7, 0x25, 0x0d, 0x5d, 0x03, 0x70, 0x1f, 0x39, 0x83, 0x1e, 0x4f, 0xcd, 0x4a, 0x73, 0xb9, 0xa5, 0xc9, 0xa2, 0x3b, 0x23, 0x45, 0x6f, 0x46, 0x8a, 0x9b, 0xde, 0x8c, 0xc8, 0x59, 0x4a, 0xed, 0x5c, 0xa3, 0x71, 0xe8, 0xdb, 0xc3, 0x96, 0xad, 0x9b, 0xc6, 0x78, 0xda, 0x15, 0xca, 0x2e, 0xd1, 0x31, 0xe8, 0x73, 0x06, 0xef, 0xbc, 0xae, 0x87, 0x3e, 0xc9, 0x38, 0x97, 0x25, 0x0d, 0x7d, 0x59, 0x82, 0x73, 0xde, 0x90, 0xcb, 0xf8, 0x21, 0x56, 0xeb, 0xce, 0x3a, 0x94, 0x6d, 0xa2, 0x58, 0x04, 0x6b, 0x65, 0x17, 0x89, 0x42, 0x88, 0xa5, 0x6f, 0xd5, 0x09, 0xb6, 0xc7, 0x7b, 0x29, 0x9e, 0xe7, 0xb9, 0x43, 0x7f, 0x85, 0xc9, 0x59, 0xf3, 0xc4, 0xdc, 0x75, 0xa5, 0xd0, 0x21, 0x2f, 0x37, 0x65, 0xbc, 0xfc, 0x94, 0x7c, 0xb6, 0x11, 0x8f, 0x14, 0x7d, 0x5d, 0x82, 0xf3, 0x1c, 0x78, 0xaa, 0x59, 0xad, 0x55, 0x30, 0x17, 0x60, 0x86, 0x02, 0x7c, 0x31, 0x1e, 0xc0, 0x55, 0x4f, 0x4e, 0x27, 0xc4, 0x67, 0x1a, 0x71, 0x89, 0xd1, 0x5b, 0x12, 0xcc, 0x73, 0x40, 0x6e, 0x2b, 0x7a, 0x85, 0x87, 0xb0, 0x8f, 0x22, 0xbc, 0x11, 0x0f, 0xe1, 0x3a, 0x15, 0xd2, 0x09, 0xef, 0x4c, 0x23, 0x16, 0x25, 0xfa, 0x1a, 0x7f, 0x02, 0x1d, 0xdd, 0xd2, 0xca, 0x66, 0x9d, 0x74, 0xc2, 0xeb, 0xa7, 0xf0, 0x5e, 0x88, 0x07, 0xcf, 0x51, 0x3b, 0xed, 0x76, 0x9d, 0x74, 0x02, 0x9c, 0x6b, 0xc4, 0xa4, 0x45, 0x6f, 0x4a, 0x30, 0xa7, 0x61, 0x55, 0xb7, 0x29, 0x30, 0x47, 0x4b, 0x6d, 0x75, 0x07, 0x6b, 0x75, 0xee, 0xe4, 0x65, 0x29, 0xba, 0x6b, 0x5c, 0x74, 0x37, 0x99, 0x90, 0x4d, 0xc5, 0xde, 0xbd, 0xeb, 0x89, 0xe8, 0x44, 0x76, 0x5a, 0x8b, 0x41, 0x87, 0x5e, 0x97, 0xe0, 0x4c, 0x00, 0x95, 0xc8, 0x26, 0x80, 0x62, 0xba, 0x1a, 0x8d, 0x49, 0x64, 0x0e, 0x05, 0x2d, 0x92, 0x8a, 0x33, 0x4b, 0x21, 0x46, 0x90, 0x8b, 0x39, 0x4b, 0x21, 0xfa, 0xef, 0x9b, 0x25, 0xa1, 0xea, 0xbf, 0xd1, 0x81, 0x2a, 0x44, 0xb3, 0x06, 0x28, 0xaa, 0xe7, 0x22, 0x51, 0x89, 0x95, 0xea, 0x94, 0x16, 0x4d, 0x86, 0x3e, 0x2b, 0xc1, 0xd3, 0x7e, 0x4c, 0x22, 0x4b, 0x3c, 0x42, 0x01, 0x5d, 0x89, 0x04, 0x24, 0x32, 0xc2, 0x93, 0x5a, 0x14, 0x11, 0x5d, 0x36, 0x45, 0x25, 0xfa, 0x9e, 0x4e, 0xf6, 0x23, 0x95, 0x7b, 0x30, 0x64, 0xd9, 0x96, 0x99, 0x90, 0x28, 0xe5, 0x56, 0x62, 0xd0, 0x51, 0xe5, 0x0e, 0xa0, 0x12, 0x29, 0xf7, 0x50, 0x88, 0x72, 0xfb, 0x30, 0x09, 0x95, 0x5b, 0x89, 0xa4, 0xe2, 0xcc, 0x52, 0x88, 0x72, 0xe7, 0x63, 0xce, 0x52, 0x98, 0x72, 0x2b, 0x31, 0xe8, 0xa8, 0x22, 0xf9, 0x51, 0x89, 0x14, 0x69, 0x38, 0x44, 0x91, 0xda, 0x21, 0x09, 0x15, 0x49, 0x89, 0x22, 0xa2, 0x96, 0xe6, 0x07, 0x13, 0x62, 0x69, 0x28, 0xc4, 0xd2, 0xda, 0xf1, 0x84, 0x58, 0x9a, 0x12, 0x4d, 0x86, 0x1a, 0x70, 0xc2, 0x01, 0x61, 0x89, 0xb5, 0x67, 0x84, 0x02, 0xb9, 0xc0, 0x05, 0xe2, 0x48, 0xb5, 0x84, 0x6a, 0x33, 0x45, 0xc4, 0x8f, 0xd1, 0x6b, 0x30, 0xed, 0xbe, 0x78, 0x5b, 0xb7, 0x78, 0xaf, 0x1d, 0xa5, 0xaf, 0x2d, 0x8a, 0x5f, 0xbb, 0xee, 0xf0, 0x75, 0xbe, 0x74, 0x82, 0x88, 0x1e, 0xa2, 0x6f, 0x4a, 0xb0, 0x18, 0x50, 0x51, 0xc5, 0x50, 0x71, 0xa5, 0x6c, 0xe1, 0xd7, 0xea, 0xd8, 0xe6, 0x8e, 0xfe, 0x28, 0x85, 0xf1, 0x52, 0xb4, 0xa6, 0x52, 0x49, 0xb2, 0x27, 0xa8, 0x13, 0xd7, 0xbc, 0x12, 0x9b, 0x1a, 0xfd, 0x50, 0x82, 0xcb, 0x0c, 0x93, 0x07, 0x31, 0x9e, 0x12, 0x8f, 0x51, 0xb4, 0xab, 0x5c, 0xb4, 0xec, 0x6d, 0xee, 0xab, 0xe3, 0x68, 0x74, 0xd1, 0x4a, 0xc4, 0x81, 0xbe, 0x20, 0xc1, 0x59, 0xde, 0xf4, 0xf2, 0x80, 0x1e, 0x8b, 0xa9, 0xdd, 0xab, 0x4c, 0x42, 0x84, 0x76, 0x0b, 0xc8, 0xd0, 0x27, 0x60, 0xc6, 0x55, 0x32, 0x31, 0x92, 0x71, 0x8a, 0xe4, 0xa2, 0x58, 0xcf, 0xc4, 0x10, 0x5c, 0x05, 0x16, 0xbd, 0xfb, 0x33, 0x12, 0x9c, 0x66, 0x8b, 0xc7, 0x14, 0x5d, 0xb0, 0x68, 0x13, 0x14, 0xc1, 0xb3, 0x5c, 0x04, 0xae, 0x70, 0x57, 0xdf, 0x05, 0xcb, 0x34, 0xab, 0x46, 0xd0, 0xa0, 0x4f, 0xc1, 0x6c, 0x55, 0xb1, 0x76, 0xb1, 0x55, 0xb6, 0xb0, 0x6a, 0x5a, 0x1a, 0x0f, 0xc4, 0x24, 0x05, 0xb1, 0xc4, 0x05, 0xf1, 0xdf, 0x94, 0x59, 0x66, 0xbc, 0x9d, 0x08, 0x8e, 0x57, 0xc3, 0x08, 0xd0, 0x57, 0x25, 0x58, 0xe0, 0x9d, 0x4f, 0xf4, 0x07, 0x86, 0xc2, 0x9d, 0x90, 0xa9, 0x24, 0xe1, 0xeb, 0x5d, 0x26, 0x26, 0x4e, 0xf8, 0x2a, 0xa0, 0x45, 0xdf, 0x90, 0xa0, 0xc8, 0x8b, 0xb0, 0xb1, 0x55, 0xd5, 0x0d, 0x85, 0xeb, 0x17, 0xa6, 0x43, 0xfc, 0x42, 0x67, 0x88, 0xdd, 0x14, 0xc4, 0xf1, 0x0b, 0x8d, 0xd8, 0xd4, 0xe8, 0x47, 0x12, 0x5c, 0xe6, 0x1d, 0xa5, 0x22, 0xbd, 0xd8, 0x71, 0x8a, 0xf6, 0x66, 0xcc, 0x13, 0x55, 0x94, 0x2b, 0x5b, 0x6c, 0x24, 0x63, 0x11, 0x69, 0x80, 0xd8, 0x28, 0x4f, 0x24, 0xd1, 0x00, 0xb1, 0x81, 0xce, 0x35, 0x62, 0xd2, 0xa2, 0xbf, 0x48, 0xb0, 0x16, 0xf0, 0xb8, 0xf8, 0x21, 0xc1, 0x96, 0xa1, 0x54, 0xca, 0x1c, 0xe4, 0xba, 0xa1, 0x13, 0x9d, 0xaf, 0x18, 0x33, 0x14, 0xfa, 0xdd, 0x68, 0x17, 0xbc, 0xc6, 0xe4, 0x77, 0x8c, 0xa7, 0xe4, 0x09, 0xef, 0x1c, 0xd0, 0x8b, 0xd6, 0x81, 0x24, 0xa0, 0x3f, 0x49, 0xb0, 0x92, 0x60, 0x98, 0x22, 0x8f, 0x35, 0x4b, 0xc7, 0x78, 0xe7, 0x00, 0x63, 0x14, 0x39, 0xb3, 0x1b, 0x56, 0xf7, 0xec, 0xe8, 0x3d, 0x09, 0x5e, 0x08, 0x1b, 0x4e, 0xb4, 0x9d, 0x9c, 0xa4, 0x03, 0xdb, 0xe0, 0x0e, 0x4c, 0x08, 0x26, 0xd2, 0x5e, 0xae, 0xe2, 0xee, 0x58, 0x69, 0x1c, 0xc0, 0x4d, 0x9d, 0x18, 0x44, 0x37, 0xea, 0x58, 0x2b, 0x2b, 0x76, 0xd9, 0xc0, 0x8d, 0xce, 0x71, 0x14, 0x42, 0xe2, 0x00, 0x4e, 0x06, 0x85, 0x89, 0x5b, 0xb6, 0x6f, 0xe1, 0x06, 0x27, 0x0e, 0x68, 0x24, 0xe2, 0x40, 0xbf, 0x92, 0xe0, 0x1a, 0x8d, 0x26, 0xcb, 0xea, 0x8e, 0x5e, 0xd1, 0x12, 0xda, 0xcf, 0x29, 0x0a, 0xfd, 0x65, 0x2e, 0x74, 0x1a, 0x4a, 0xae, 0x3a, 0x42, 0x93, 0x18, 0xcd, 0x25, 0x3b, 0x39, 0x1b, 0xfa, 0xa9, 0x04, 0x57, 0x22, 0x06, 0x21, 0xb2, 0x8e, 0xd3, 0x74, 0x04, 0x6b, 0x49, 0x47, 0x20, 0x32, 0x89, 0x0b, 0x76, 0x42, 0x1e, 0xf4, 0x5d, 0x09, 0x2e, 0x0a, 0x51, 0x0b, 0xe3, 0xfc, 0xa7, 0x29, 0xec, 0x65, 0x7e, 0x18, 0xc2, 0x7d, 0xbb, 0x30, 0xf0, 0x5f, 0x50, 0x13, 0xd0, 0xa3, 0x1f, 0x48, 0x70, 0x49, 0x08, 0x37, 0xe4, 0x10, 0x79, 0x26, 0x44, 0xc9, 0xf9, 0x80, 0x43, 0x8e, 0x93, 0x45, 0x35, 0x11, 0x07, 0x7a, 0x5b, 0x82, 0x0b, 0x89, 0x35, 0xe3, 0x2c, 0x45, 0xfc, 0x5f, 0x09, 0x10, 0x8b, 0x94, 0xe2, 0x9c, 0x9a, 0x40, 0x1f, 0xde, 0x91, 0x60, 0x49, 0x3c, 0xc1, 0xc2, 0x4d, 0x78, 0x8e, 0xa2, 0x5d, 0x49, 0x32, 0xbf, 0xc2, 0x9d, 0xf8, 0xbc, 0x9a, 0x84, 0x01, 0x7d, 0x3f, 0x4c, 0x25, 0x42, 0x0e, 0xcd, 0xcf, 0x24, 0x86, 0x2c, 0x3e, 0x3e, 0x0b, 0x20, 0x8b, 0x0e, 0xd2, 0x4e, 0x6c, 0x26, 0x86, 0x1c, 0x12, 0x49, 0xce, 0x87, 0xc4, 0x66, 0x02, 0xcc, 0x21, 0xe1, 0xe4, 0xa2, 0x9a, 0x8c, 0x85, 0x6e, 0x9a, 0x6e, 0x28, 0xde, 0x6d, 0xc4, 0x73, 0x2e, 0x64, 0xd3, 0x74, 0x23, 0xee, 0x6e, 0x42, 0x9d, 0xab, 0x76, 0x77, 0xac, 0xe8, 0xd7, 0x12, 0x5c, 0x8f, 0x31, 0x20, 0x91, 0x8d, 0x2e, 0xd0, 0xd1, 0x94, 0xba, 0x19, 0x8d, 0xc8, 0x58, 0x2f, 0xdb, 0x5d, 0xf0, 0xa1, 0x9f, 0x48, 0xf0, 0x6c, 0xd8, 0x00, 0xc4, 0xe7, 0xa7, 0xf3, 0x21, 0x1b, 0x90, 0x10, 0x84, 0xf8, 0x1c, 0x75, 0x01, 0x27, 0xe4, 0xa1, 0x0e, 0xa7, 0x5e, 0xb3, 0xb1, 0x45, 0x5a, 0xc0, 0x6d, 0xac, 0x58, 0xea, 0x4e, 0x1b, 0xcc, 0x4e, 0xdc, 0xc5, 0x10, 0xeb, 0xbd, 0x47, 0xc5, 0x79, 0x08, 0xee, 0x52, 0x61, 0xad, 0x37, 0x72, 0xac, 0xb7, 0x9e, 0x84, 0x61, 0x65, 0x00, 0xa0, 0x05, 0xa4, 0xf0, 0xe7, 0x21, 0x38, 0x1b, 0x77, 0xf7, 0x5a, 0x87, 0x23, 0xcd, 0x31, 0x92, 0xfd, 0x1a, 0xa6, 0xb5, 0x40, 0x51, 0x65, 0xd1, 0x13, 0xba, 0xb9, 0x5f, 0xc3, 0xf2, 0x40, 0xa3, 0xed, 0x0a, 0xbd, 0x0a, 0x47, 0x6b, 0x8a, 0xe5, 0xcc, 0x48, 0xbb, 0xd1, 0x6d, 0x9b, 0xac, 0x7c, 0x38, 0xc7, 0x95, 0x77, 0x87, 0x72, 0xb4, 0xd9, 0xc4, 0xb6, 0x29, 0x8f, 0xd4, 0x3a, 0x6f, 0xa2, 0xeb, 0x90, 0xa5, 0x19, 0x99, 0x8a, 0x6e, 0x13, 0x5a, 0x58, 0xcc, 0x2d, 0x1d, 0xe7, 0xa7, 0x3c, 0x14, 0x7b, 0x77, 0x43, 0xb7, 0x89, 0xdc, 0x4f, 0xd8, 0x2f, 0xb4, 0x04, 0xbd, 0xba, 0x51, 0xab, 0x13, 0x5a, 0x76, 0xcc, 0x2d, 0x4d, 0x0b, 0x90, 0xec, 0x57, 0x4c, 0x45, 0x93, 0x5d, 0x52, 0xa4, 0xc0, 0x6c, 0x20, 0xe4, 0x28, 0x13, 0xb3, 0xac, 0x56, 0x4c, 0x1b, 0x53, 0xff, 0x6d, 0xd6, 0x09, 0xab, 0x43, 0x4e, 0x74, 0xd4, 0x45, 0x6f, 0xb2, 0x4a, 0xb2, 0x3c, 0x8d, 0x7d, 0x73, 0xbf, 0x69, 0xae, 0x3a, 0xfc, 0x9b, 0x2e, 0x3b, 0x7a, 0x05, 0xa6, 0x5a, 0x69, 0xef, 0x4e, 0xe9, 0x99, 0x28, 0xe9, 0xc7, 0x88, 0x97, 0xcc, 0x0e, 0x08, 0xbe, 0x01, 0x93, 0xad, 0x08, 0xbb, 0x35, 0x0a, 0xab, 0x6e, 0x94, 0x75, 0x8d, 0x96, 0xfe, 0xb2, 0xf2, 0xb1, 0x26, 0x45, 0x73, 0x9e, 0xe5, 0xba, 0x51, 0xd2, 0x50, 0x09, 0xb2, 0xcc, 0x55, 0x9a, 0x16, 0xad, 0xc3, 0x0d, 0x2e, 0x9d, 0xe3, 0xbb, 0x76, 0x26, 0x80, 0x86, 0xd0, 0x25, 0x8f, 0x45, 0x6e, 0x71, 0xa3, 0x12, 0x0c, 0xb7, 0x70, 0x38, 0xee, 0xaa, 0x6e, 0x61, 0x56, 0x3c, 0xe3, 0xaf, 0xc1, 0xba, 0x4b, 0x23, 0xe7, 0x9b, 0x6c, 0xec, 0x0e, 0x92, 0x61, 0xac, 0xa2, 0x38, 0x67, 0x3e, 0x37, 0x9c, 0xa1, 0xc3, 0xc1, 0x76, 0xbd, 0x42, 0x58, 0xe1, 0x2b, 0x7c, 0x4d, 0x47, 0x1d, 0xde, 0xd5, 0x26, 0xab, 0x4c, 0x39, 0xd1, 0x35, 0x98, 0x30, 0x2d, 0xfd, 0x81, 0xee, 0x3a, 0xda, 0xc0, 0x2c, 0xe5, 0xe8, 0x2c, 0x8d, 0x79, 0x04, 0x81, 0x49, 0x9a, 0x84, 0x7e, 0x5d, 0xc3, 0x06, 0xd1, 0xc9, 0x3e, 0xad, 0x28, 0x65, 0xe5, 0xe6, 0x35, 0xba, 0x04, 0x63, 0xdb, 0xba, 0x65, 0x93, 0x4e, 0x99, 0x47, 0x28, 0xe5, 0x08, 0x7d, 0x1a, 0x10, 0xb8, 0x0a, 0x03, 0x16, 0x26, 0xd6, 0x7e, 0xb9, 0x66, 0x56, 0x74, 0x75, 0x9f, 0x55, 0x61, 0x66, 0x05, 0x07, 0x54, 0x62, 0xed, 0xdf, 0xa1, 0x74, 0x72, 0xce, 0x6a, 0x5d, 0xa0, 0x71, 0xe8, 0x53, 0x08, 0xc1, 0xd5, 0x1a, 0xa1, 0x15, 0x93, 0x5e, 0xd9, 0xbb, 0x44, 0xab, 0x30, 0x84, 0x1f, 0xd6, 0x74, 0x57, 0x71, 0xdc, 0xa2, 0x7e, 0x3e, 0xb2, 0xa8, 0x3f, 0xd8, 0x62, 0xa1, 0x95, 0xfd, 0x53, 0x70, 0x44, 0xb5, 0x1c, 0x6b, 0x60, 0x15, 0x1d, 0x5a, 0x71, 0xc8, 0xca, 0x03, 0xce, 0x4d, 0xaf, 0xca, 0x83, 0xfe, 0x17, 0xa6, 0xdc, 0xd1, 0xfb, 0xab, 0x5f, 0x5b, 0x8a, 0xba, 0x6b, 0x6e, 0x6f, 0xb3, 0xa2, 0x40, 0x88, 0x52, 0x8f, 0x53, 0xee, 0xf6, 0xc2, 0xd7, 0x8a, 0xcb, 0x8a, 0xce, 0x43, 0x4f, 0x15, 0x57, 0x4d, 0x96, 0xce, 0x9f, 0xe0, 0x27, 0xfa, 0x70, 0xd5, 0x94, 0x29, 0x19, 0x92, 0x61, 0xb8, 0xc3, 0x63, 0xb3, 0x9c, 0xfc, 0xd3, 0xfc, 0xbd, 0x31, 0xe0, 0x61, 0xe5, 0xbc, 0x1d, 0xb8, 0x83, 0xee, 0xc1, 0x58, 0xcd, 0xc2, 0x7b, 0x65, 0xa5, 0x4e, 0x4c, 0x47, 0xff, 0x30, 0x29, 0xd7, 0x4c, 0xdd, 0x20, 0x5e, 0x96, 0x5d, 0xb4, 0x5e, 0x36, 0x26, 0x77, 0x28, 0x9d, 0x3c, 0xe2, 0xf0, 0x2f, 0xd7, 0x89, 0xd9, 0x76, 0x13, 0x5d, 0x82, 0xcc, 0x0e, 0x56, 0x34, 0x6c, 0xb1, 0xf4, 0xf7, 0x14, 0xbf, 0xa9, 0x83, 0x92, 0xc8, 0x8c, 0x14, 0x6d, 0xc0, 0xa8, 0x3b, 0xd1, 0xad, 0x5a, 0x1e, 0x5d, 0xd7, 0x63, 0x91, 0xeb, 0x8a, 0x28, 0x5f, 0xb3, 0x2e, 0x47, 0xd7, 0xf6, 0x93, 0x90, 0xaf, 0x29, 0x16, 0xd1, 0xbd, 0xe3, 0xf9, 0xb6, 0xfe, 0x60, 0x7c, 0x9c, 0x76, 0x98, 0xfc, 0xcf, 0x41, 0xda, 0x2c, 0x1c, 0xff, 0xee, 0x0a, 0x5d, 0xa5, 0x32, 0xd7, 0x0c, 0x62, 0xed, 0xcb, 0x43, 0x35, 0xff, 0x5d, 0x74, 0x1c, 0xc0, 0x4b, 0xea, 0xe8, 0x1a, 0x4d, 0x27, 0x67, 0xe5, 0x2c, 0xbb, 0x53, 0xd2, 0xd0, 0x7d, 0x18, 0xa1, 0x8a, 0x67, 0xee, 0x61, 0xab, 0xa2, 0xd4, 0x3c, 0x1b, 0x99, 0xa4, 0xce, 0xe9, 0x0c, 0xdf, 0x39, 0x59, 0xa6, 0x71, 0xdb, 0x25, 0x67, 0x96, 0x32, 0xac, 0x06, 0x6f, 0xa1, 0x87, 0x30, 0x43, 0x73, 0xf0, 0xb8, 0xac, 0x56, 0xea, 0x36, 0xc1, 0x56, 0xd9, 0xc6, 0x15, 0xac, 0xd2, 0x39, 0x60, 0xef, 0x98, 0x0a, 0x49, 0xae, 0xd3, 0x34, 0x3f, 0x5e, 0x75, 0x59, 0xef, 0x7a, 0x9c, 0xec, 0x75, 0xd3, 0x4a, 0xc8, 0xd3, 0xc9, 0x15, 0x18, 0xe5, 0xcd, 0x0c, 0xca, 0x43, 0x7a, 0x17, 0xef, 0xd3, 0x1d, 0x38, 0x2b, 0x3b, 0x3f, 0xd1, 0x28, 0xf4, 0xee, 0x29, 0x95, 0xba, 0xdb, 0x84, 0x93, 0x95, 0xdd, 0x8b, 0xeb, 0xa9, 0xe7, 0xa4, 0xc2, 0xdb, 0x12, 0x3c, 0x13, 0xff, 0xb8, 0x77, 0x19, 0x32, 0xcc, 0x61, 0x4a, 0x31, 0x1c, 0x26, 0xa3, 0x45, 0xeb, 0x30, 0x1b, 0x5e, 0xef, 0xd7, 0x35, 0x0a, 0x2c, 0x2d, 0x4f, 0x8b, 0x4b, 0xf5, 0x25, 0xad, 0xf0, 0x2d, 0x09, 0xce, 0xc4, 0x8c, 0x1a, 0xaf, 0x40, 0x9f, 0xb7, 0x55, 0x48, 0x31, 0xb6, 0x0a, 0x8f, 0xf8, 0xd0, 0xa0, 0x9a, 0x30, 0x17, 0xfb, 0xc8, 0xb4, 0x0a, 0x03, 0x6c, 0xb7, 0x6e, 0x45, 0x4e, 0x83, 0x02, 0x2f, 0xc0, 0x36, 0x67, 0x1a, 0x38, 0xe5, 0x48, 0xeb, 0xa2, 0xf0, 0x3b, 0x09, 0x4e, 0xc7, 0xe9, 0x1a, 0xf1, 0x87, 0x40, 0x52, 0xb2, 0x10, 0xe8, 0x16, 0x8c, 0x09, 0xc2, 0x8c, 0x54, 0x94, 0x47, 0x1e, 0xb1, 0x39, 0x21, 0x46, 0xdb, 0x56, 0x93, 0xf6, 0x6d, 0x35, 0x85, 0xd7, 0x25, 0x28, 0x44, 0x37, 0x9c, 0xa0, 0x05, 0x40, 0xc1, 0x26, 0x84, 0x66, 0x1b, 0x5a, 0xde, 0xf6, 0x4d, 0x41, 0x60, 0xbf, 0x4d, 0x05, 0xf6, 0x5b, 0xbf, 0xf3, 0x48, 0x07, 0x9c, 0x47, 0xe1, 0xef, 0x81, 0xe9, 0x15, 0x5a, 0x48, 0x32, 0x44, 0x73, 0x90, 0xf7, 0x27, 0xa2, 0x9a, 0xea, 0x35, 0x68, 0xb7, 0x8d, 0x38, 0x80, 0x3d, 0x1d, 0xc0, 0x7e, 0x16, 0x86, 0xb6, 0x74, 0x43, 0xb1, 0xf6, 0xcb, 0xea, 0x0e, 0x56, 0x77, 0xed, 0x7a, 0x95, 0xc6, 0xa8, 0x59, 0x79, 0xd0, 0xbd, 0xbd, 0xca, 0xee, 0xa2, 0x73, 0x30, 0xec, 0x4f, 0x9f, 0xe2, 0x87, 0x6e, 0xfc, 0x39, 0x20, 0xe7, 0x71, 0x7b, 0x56, 0x13, 0x3f, 0x24, 0x85, 0xef, 0xa4, 0xe1, 0x54, 0x8c, 0x5e, 0x96, 0x47, 0x36, 0xe2, 0xa0, 0x59, 0xa4, 0xbb, 0x30, 0x0b, 0x74, 0x02, 0x72, 0x5b, 0x8a, 0x8d, 0xbd, 0xd8, 0xc9, 0x9d, 0x96, 0xac, 0x73, 0xcb, 0x8d, 0x98, 0xa6, 0x01, 0x0c, 0xdc, 0xf0, 0x1e, 0xf7, 0xba, 0x13, 0x6b, 0xe0, 0x86, 0xfb, 0x74, 0x01, 0xd0, 0xb6, 0x69, 0xed, 0x32, 0xa4, 0x5e, 0x43, 0x62, 0xc6, 0x1d, 0x9a, 0xf3, 0x84, 0x62, 0xbd, 0xcf, 0x3a, 0x13, 0xc7, 0x1c, 0xe7, 0xa8, 0xd8, 0xa6, 0xc1, 0x82, 0x63, 0x76, 0x85, 0x6e, 0x42, 0xaf, 0xaa, 0xd4, 0x6d, 0xcc, 0xe2, 0xe0, 0x62, 0xec, 0xae, 0xa1, 0x55, 0x87, 0x4b, 0x76, 0x99, 0x03, 0x0a, 0x9a, 0x0d, 0x2a, 0xe8, 0xbb, 0x69, 0x38, 0x19, 0xd9, 0xe8, 0xf3, 0xc8, 0xd6, 0x6a, 0xc5, 0x1b, 0xa2, 0xbb, 0x48, 0x0b, 0x31, 0xfb, 0x90, 0x7c, 0x03, 0x6c, 0x73, 0xd9, 0x3d, 0x49, 0x5c, 0x76, 0xbb, 0x65, 0xf4, 0x06, 0x2c, 0x23, 0xb0, 0xfc, 0x99, 0xf0, 0xe5, 0xef, 0x8b, 0xb5, 0xfc, 0xfd, 0x82, 0xe5, 0xe7, 0x58, 0x61, 0x96, 0x6b, 0x85, 0xfe, 0x95, 0x84, 0xe0, 0x4a, 0x7e, 0x25, 0x03, 0xa7, 0xe3, 0xb4, 0x48, 0xa1, 0x19, 0xc8, 0x35, 0xfb, 0x0c, 0xd8, 0x2a, 0x66, 0x65, 0xf0, 0x6e, 0x95, 0x34, 0xe7, 0x4c, 0xde, 0x6a, 0x44, 0x70, 0x4c, 0x28, 0x15, 0x72, 0x26, 0x6f, 0xbe, 0x92, 0x9e, 0xc9, 0x95, 0xb6, 0x2b, 0x47, 0xb1, 0x35, 0xb3, 0xaa, 0xe8, 0x06, 0xf3, 0x3c, 0xec, 0xca, 0xbf, 0x95, 0xf4, 0x74, 0x79, 0x9a, 0xce, 0xc4, 0x3f, 0x4d, 0x6f, 0xc2, 0x84, 0xa7, 0xa3, 0x9d, 0x3b, 0x50, 0x5f, 0xd4, 0x0e, 0x34, 0xe6, 0xf1, 0x06, 0x36, 0xa1, 0x80, 0x54, 0xb6, 0xc1, 0x31, 0xa9, 0xfd, 0x09, 0xa4, 0xba, 0x87, 0x68, 0x26, 0x55, 0xbc, 0x55, 0x66, 0xbb, 0xda, 0x2a, 0xd7, 0x61, 0x78, 0x07, 0x2b, 0x16, 0xd9, 0xc2, 0x4a, 0x0b, 0x1d, 0x44, 0x89, 0xca, 0x37, 0x79, 0x5a, 0x72, 0xa2, 0x03, 0x9c, 0x5c, 0x74, 0x80, 0xd3, 0x71, 0xd4, 0x1c, 0xe8, 0xe6, 0xa8, 0xd9, 0x3a, 0xb2, 0x1c, 0x89, 0x7d, 0x64, 0x29, 0xfc, 0x4d, 0x82, 0x42, 0x74, 0xbb, 0xde, 0x63, 0x0b, 0x0d, 0xda, 0x83, 0x98, 0x1e, 0xff, 0x79, 0xf9, 0x25, 0x18, 0xa0, 0xe9, 0x06, 0xcf, 0xad, 0xf5, 0xc6, 0x70, 0x6b, 0x39, 0x87, 0x83, 0x5d, 0x14, 0xfe, 0x20, 0xf9, 0x5d, 0xc1, 0x21, 0xc7, 0xe5, 0xfc, 0x29, 0x4a, 0x25, 0xd8, 0x0d, 0xd2, 0x91, 0xb1, 0x4a, 0x8f, 0x7f, 0x32, 0x0b, 0xbf, 0x97, 0xe0, 0x64, 0x74, 0x0f, 0x55, 0xb7, 0xe1, 0xfb, 0x87, 0x31, 0xa2, 0x9f, 0xa7, 0xe0, 0x54, 0x8c, 0x4e, 0x44, 0x67, 0x4c, 0x1a, 0x26, 0x8a, 0x5e, 0xb1, 0x63, 0x2d, 0x92, 0x47, 0xfc, 0xc8, 0xc6, 0x14, 0x8c, 0xaf, 0x7a, 0xba, 0x89, 0xaf, 0x0e, 0xac, 0xe2, 0x5f, 0x94, 0x60, 0x3e, 0x7e, 0x03, 0x61, 0x9c, 0x3d, 0xef, 0x70, 0x0e, 0x70, 0xef, 0x48, 0x90, 0xb0, 0x55, 0x30, 0x1a, 0xdb, 0xa8, 0x17, 0x25, 0xb1, 0x53, 0xb8, 0x1b, 0xf7, 0xc4, 0x41, 0x9c, 0x8e, 0x81, 0xf8, 0xad, 0x80, 0x1e, 0x8a, 0x8a, 0x8a, 0xdd, 0xea, 0xe1, 0x3a, 0xcc, 0x56, 0x14, 0xd2, 0xd6, 0x32, 0x13, 0x6c, 0x20, 0x69, 0xcd, 0xac, 0x4b, 0xc7, 0x5b, 0x4a, 0x37, 0xaa, 0xe2, 0xe8, 0x73, 0x3a, 0x81, 0x3e, 0xf7, 0x44, 0xda, 0x68, 0x20, 0x0e, 0x2c, 0xbc, 0x27, 0xc1, 0x54, 0x48, 0x93, 0x2e, 0x9a, 0x80, 0x7e, 0xb7, 0x39, 0xb1, 0xb9, 0x6e, 0x7d, 0xf4, 0xba, 0xa4, 0xa1, 0x0d, 0x38, 0xda, 0xdc, 0xc8, 0xb7, 0x75, 0x2b, 0xc1, 0x91, 0x17, 0xb1, 0x7d, 0x7c, 0x5d, 0xb7, 0x70, 0x92, 0xed, 0x37, 0xce, 0x62, 0x7f, 0x0c, 0x26, 0x84, 0xdd, 0xbf, 0x61, 0xa3, 0x89, 0x1d, 0xd2, 0x17, 0xde, 0x95, 0x60, 0x3a, 0xac, 0xf1, 0xf3, 0x50, 0xde, 0x72, 0x58, 0xf3, 0x11, 0xea, 0xa0, 0x7f, 0x2c, 0xc1, 0x6c, 0x54, 0x03, 0x69, 0xd8, 0x68, 0x1e, 0xa9, 0xd9, 0x86, 0x6f, 0x96, 0x59, 0x48, 0xd8, 0xa7, 0x84, 0x16, 0x61, 0x94, 0xb6, 0x42, 0x05, 0xab, 0x06, 0xee, 0x98, 0x86, 0x0d, 0xdc, 0x08, 0xd4, 0x0c, 0x3a, 0x0a, 0x77, 0xa9, 0xee, 0x0a, 0x77, 0x4f, 0x4a, 0x6b, 0xf1, 0x4b, 0x6b, 0x71, 0x74, 0xa7, 0x2f, 0x86, 0xee, 0xdc, 0x86, 0x31, 0x56, 0x12, 0x61, 0x18, 0x75, 0x83, 0x60, 0x6b, 0x4f, 0xa9, 0x44, 0x9f, 0x5b, 0x46, 0x19, 0x23, 0x85, 0x57, 0x62, 0x6c, 0xfe, 0xb2, 0x5d, 0xf6, 0x40, 0x65, 0xbb, 0xb6, 0x10, 0x0e, 0x92, 0x84, 0x70, 0xe2, 0x1a, 0x5d, 0xae, 0xeb, 0x1a, 0x5d, 0xeb, 0x9c, 0x31, 0x10, 0xbf, 0x34, 0xe2, 0x55, 0x8a, 0x8e, 0x1c, 0xa0, 0x52, 0x34, 0x78, 0xb0, 0x4a, 0x91, 0xa0, 0x64, 0x31, 0xf4, 0x18, 0x4a, 0x16, 0xf9, 0x47, 0x52, 0xb2, 0x28, 0xfc, 0x55, 0x82, 0xc5, 0xa4, 0xed, 0x9f, 0x4d, 0xff, 0x2b, 0xb5, 0xfb, 0xdf, 0xb0, 0x13, 0xdb, 0x16, 0x1c, 0x6b, 0xb6, 0x8c, 0x04, 0xda, 0x08, 0x5c, 0xcf, 0x34, 0x1f, 0xda, 0x14, 0xe2, 0x6f, 0x24, 0x38, 0x8a, 0x79, 0xb7, 0x03, 0xa7, 0xc2, 0x9e, 0x60, 0x16, 0xe7, 0xdb, 0x12, 0xa7, 0x02, 0x20, 0xda, 0x4a, 0xe3, 0xf8, 0x03, 0x29, 0x86, 0x3f, 0x68, 0x0b, 0xed, 0x52, 0x09, 0x42, 0xbb, 0xc2, 0x07, 0x12, 0x1c, 0x0f, 0xfd, 0xba, 0xc1, 0x89, 0x6d, 0xd9, 0xb7, 0x13, 0x86, 0x52, 0xf5, 0x56, 0x02, 0xdc, 0x5b, 0xb7, 0x94, 0x2a, 0xee, 0xf6, 0xd5, 0x87, 0xb6, 0x8d, 0xb6, 0x4c, 0xbc, 0x27, 0x7e, 0x2a, 0xe1, 0x67, 0xbc, 0x45, 0x12, 0x75, 0xf3, 0xcc, 0x40, 0x8e, 0xf5, 0x53, 0xb5, 0x4f, 0x81, 0x7b, 0x8b, 0x4e, 0x41, 0x73, 0x17, 0x4b, 0xc5, 0xdf, 0xc5, 0xc2, 0xd2, 0xfa, 0x11, 0x1a, 0xf6, 0x25, 0x09, 0xe6, 0x13, 0x34, 0xb8, 0xb5, 0xb2, 0xd3, 0x92, 0x2f, 0x3b, 0xdd, 0xed, 0xc2, 0x85, 0x20, 0x2f, 0xfc, 0x32, 0x05, 0x2f, 0x1e, 0xac, 0xc9, 0xff, 0xd0, 0x4c, 0xa2, 0x95, 0xbb, 0x4c, 0xf9, 0x72, 0x97, 0xf7, 0x00, 0x75, 0x36, 0x93, 0x31, 0xef, 0x70, 0x26, 0x5e, 0xb1, 0x5a, 0x1e, 0xee, 0xe8, 0x08, 0x47, 0xe3, 0xd0, 0xa7, 0x9a, 0x06, 0xb1, 0xcc, 0x0a, 0x5d, 0xb0, 0x01, 0xd9, 0xbb, 0x44, 0x45, 0x18, 0x09, 0xf4, 0x45, 0x9a, 0x46, 0xc5, 0x3d, 0xa9, 0xf4, 0xcb, 0xc3, 0xbe, 0x76, 0xc5, 0xdb, 0x46, 0x65, 0xbf, 0xf0, 0x66, 0x1a, 0x6e, 0x1c, 0xe0, 0x23, 0x02, 0x74, 0xaf, 0xdd, 0x6b, 0x0e, 0x0a, 0x3e, 0xd1, 0x89, 0x25, 0xd9, 0x97, 0xa5, 0x3f, 0xa4, 0xf3, 0xb5, 0x30, 0xa7, 0xcc, 0x5f, 0x97, 0x9e, 0x83, 0xae, 0xcb, 0x02, 0xa0, 0x60, 0xeb, 0x26, 0xab, 0xf7, 0xa4, 0xe5, 0xbc, 0xee, 0x53, 0x42, 0x37, 0xa5, 0xe7, 0xad, 0x62, 0xc6, 0xb7, 0x8a, 0x85, 0x3f, 0x4a, 0x70, 0xb5, 0xcb, 0x2f, 0x20, 0x04, 0x18, 0x24, 0x01, 0x86, 0xc7, 0xab, 0xb8, 0x85, 0xcf, 0xa7, 0xe1, 0x6a, 0x97, 0x5d, 0xaa, 0xff, 0xa9, 0xb6, 0x1a, 0x70, 0xe8, 0x3d, 0x62, 0x87, 0xde, 0x1b, 0xdf, 0xa1, 0x0b, 0x55, 0x47, 0xe4, 0x00, 0xfa, 0x44, 0x0e, 0xe0, 0x73, 0x69, 0xb8, 0xdc, 0x4d, 0xa7, 0x6d, 0x3c, 0xcb, 0x8f, 0x25, 0xf9, 0x89, 0xe5, 0xb7, 0x2c, 0xff, 0x7d, 0x09, 0x2e, 0x24, 0xed, 0x1a, 0xfe, 0xb7, 0x36, 0x79, 0xf1, 0x5e, 0x55, 0xf8, 0xad, 0x04, 0xe7, 0x13, 0x75, 0x1a, 0x1f, 0x9a, 0x0b, 0xe0, 0x9e, 0xa2, 0x52, 0x07, 0x3a, 0x45, 0x15, 0xbe, 0x97, 0x83, 0x4b, 0x5d, 0x7c, 0x32, 0xd5, 0xb6, 0x1c, 0x92, 0x6f, 0x39, 0x66, 0x20, 0xd7, 0x5c, 0x0e, 0xa6, 0xf3, 0x59, 0x19, 0xbc, 0x5b, 0xbc, 0x94, 0x4a, 0xfa, 0x10, 0x52, 0x2a, 0xdd, 0xd6, 0x57, 0x7b, 0x0f, 0x37, 0xa5, 0x92, 0x79, 0xa4, 0x29, 0x95, 0xbe, 0xae, 0x53, 0x2a, 0xf7, 0x81, 0x35, 0x7c, 0x33, 0x89, 0xec, 0x18, 0xdb, 0x1f, 0x72, 0x54, 0x76, 0xbb, 0xc6, 0xa9, 0x14, 0xef, 0xa8, 0x5c, 0x0b, 0xde, 0x6a, 0x37, 0x92, 0xac, 0xdf, 0x9f, 0xc7, 0x51, 0x79, 0x88, 0xa1, 0xf2, 0x2a, 0x8c, 0xb7, 0xa9, 0x53, 0xd9, 0xc2, 0xf5, 0x16, 0xfc, 0x1c, 0x85, 0x3f, 0x1f, 0xaa, 0x38, 0x25, 0x4d, 0x76, 0x58, 0xd8, 0x10, 0x8e, 0x36, 0x78, 0xb7, 0x3b, 0xca, 0xb5, 0x47, 0xba, 0x29, 0xd7, 0x76, 0xb4, 0xee, 0x0e, 0x72, 0x5a, 0x77, 0x5b, 0x07, 0xb1, 0xa1, 0xe4, 0xb9, 0x96, 0xfc, 0x01, 0x72, 0x2d, 0xc3, 0x07, 0xcb, 0xb5, 0x5c, 0x87, 0x9c, 0x86, 0x2b, 0xca, 0xbe, 0xab, 0x9a, 0xd1, 0x2d, 0xc6, 0x40, 0xa9, 0xa9, 0x2a, 0xa2, 0xe7, 0x61, 0xe0, 0xe3, 0x3a, 0x21, 0xde, 0xdf, 0x87, 0x34, 0x9b, 0x8b, 0x85, 0xcc, 0x39, 0x97, 0xbc, 0xc9, 0xed, 0xf6, 0xe0, 0x5a, 0x75, 0xa3, 0xac, 0x10, 0xd6, 0x5e, 0x1c, 0xd6, 0x7b, 0x0b, 0x94, 0x5e, 0xae, 0x1b, 0xcb, 0x44, 0x94, 0x23, 0x3a, 0xfa, 0x18, 0x72, 0x44, 0x63, 0x8f, 0x26, 0x47, 0xf4, 0x46, 0x1a, 0x2e, 0x24, 0xfd, 0x40, 0xf4, 0xc3, 0x77, 0xd6, 0x1b, 0x5e, 0xd4, 0xe5, 0xd6, 0x51, 0xaf, 0x24, 0xfe, 0xba, 0xd1, 0x17, 0x6c, 0xb5, 0xb9, 0x9d, 0x5e, 0xbf, 0xdb, 0xe1, 0x87, 0x14, 0x19, 0x41, 0x48, 0x71, 0x48, 0x99, 0xe6, 0xc2, 0x6f, 0x52, 0xb0, 0x90, 0xe4, 0xeb, 0x57, 0xe1, 0x7a, 0xf0, 0x63, 0x99, 0xd4, 0x41, 0x63, 0x99, 0xc3, 0x5a, 0x45, 0xfe, 0xec, 0xf6, 0x08, 0x66, 0xb7, 0xe5, 0xeb, 0x7a, 0xe3, 0x27, 0x9d, 0x3e, 0x48, 0x41, 0xc2, 0xef, 0x72, 0x3f, 0x1a, 0x93, 0xc9, 0x2b, 0x1a, 0xf6, 0x72, 0x8b, 0x86, 0xad, 0x6e, 0x97, 0x4c, 0xfc, 0x6e, 0x97, 0xc2, 0x3f, 0x52, 0x70, 0xee, 0x30, 0x3c, 0xca, 0x47, 0x74, 0xd2, 0xdb, 0xea, 0x39, 0x99, 0x04, 0xf5, 0x9c, 0xc2, 0x3f, 0x53, 0x70, 0x3e, 0xd1, 0x67, 0xd2, 0x4f, 0x26, 0xbe, 0x63, 0xe2, 0xbd, 0x04, 0x6d, 0x26, 0x49, 0x52, 0xff, 0xd3, 0x69, 0xd1, 0xc4, 0x8b, 0x3a, 0x94, 0x9e, 0x4c, 0x7c, 0x68, 0x83, 0x54, 0xa6, 0x9b, 0xef, 0x32, 0x7e, 0x91, 0x82, 0xc5, 0x84, 0x9f, 0xaf, 0x3f, 0x59, 0x07, 0xdf, 0x3a, 0xcc, 0x13, 0x18, 0xa2, 0x3f, 0xd7, 0xf5, 0x0a, 0xc1, 0x16, 0x7d, 0xd5, 0x71, 0x98, 0x58, 0xbb, 0xbf, 0x76, 0x6b, 0xb3, 0xbc, 0x5e, 0xda, 0xd8, 0x5c, 0x93, 0xcb, 0x9b, 0xff, 0x77, 0x67, 0xad, 0x5c, 0xba, 0x75, 0x7f, 0x79, 0xa3, 0x74, 0x33, 0xff, 0x14, 0x9a, 0x81, 0xa9, 0xce, 0xc7, 0xcb, 0x1b, 0x1b, 0x65, 0x7a, 0x37, 0x2f, 0xa1, 0x93, 0x70, 0xbc, 0x93, 0x60, 0x75, 0xe3, 0xf6, 0xdd, 0x35, 0x46, 0x92, 0x5a, 0x79, 0x15, 0x8e, 0xa9, 0x66, 0x95, 0x37, 0x07, 0x2b, 0xde, 0x1f, 0x20, 0xdf, 0x71, 0x22, 0xf3, 0x3b, 0xd2, 0xff, 0x5f, 0x7c, 0xa0, 0x93, 0x9d, 0xfa, 0x56, 0x51, 0x35, 0xab, 0x8b, 0xed, 0x7f, 0xc4, 0x7c, 0x5e, 0xd7, 0x2a, 0x8b, 0x0f, 0x4c, 0xf7, 0xcf, 0x9f, 0xd9, 0xbf, 0x32, 0xdf, 0x50, 0x6a, 0xfa, 0xde, 0xc5, 0xad, 0x0c, 0xbd, 0x77, 0xe9, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf3, 0xc4, 0x77, 0x00, 0x78, 0x5a, 0x00, 0x00, }, // uber/cadence/api/v1/tasklist.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xdb, 0x6e, 0xdb, 0x36, 0x18, 0x9e, 0x7c, 0x68, 0x9d, 0xdf, 0x4d, 0xa2, 0xb2, 0x49, 0x63, 0xbb, 0xed, 0xe6, 0xfa, 0xa2, 0xc8, 0x8a, 0x4d, 0x46, 0xb2, 0x0d, 0x18, 0xb6, 0xa1, 0xab, 0x13, 0x1b, 0xad, 0x10, 0x27, 0x35, 0x64, 0xb5, 0x43, 0x07, 0x0c, 0x02, 0x2d, 0xb1, 0x0e, 0x67, 0x49, 0x14, 0x44, 0xca, 0xae, 0x6f, 0xf6, 0x18, 0xbb, 0xdb, 0x8b, 0xec, 0x1d, 0xf6, 0x4e, 0x03, 0x29, 0xd9, 0xf5, 0x41, 0x0d, 0xd6, 0x8b, 0xdd, 0x99, 0xff, 0xc7, 0x8f, 0xdf, 0x7f, 0xb6, 0xa0, 0x95, 0x8c, 0x48, 0xdc, 0x76, 0xb1, 0x47, 0x42, 0x97, 0xb4, 0x71, 0x44, 0xdb, 0xd3, 0x93, 0xb6, 0xc0, 0x7c, 0xe2, 0x53, 0x2e, 0x8c, 0x28, 0x66, 0x82, 0xa1, 0x7b, 0xf2, 0x8e, 0x91, 0xdd, 0x31, 0x70, 0x44, 0x8d, 0xe9, 0x49, 0xe3, 0xf3, 0x31, 0x63, 0x63, 0x9f, 0xb4, 0xd5, 0x95, 0x51, 0xf2, 0xae, 0xed, 0x25, 0x31, 0x16, 0x94, 0x85, 0x29, 0xa9, 0xf1, 0xc5, 0x26, 0x2e, 0x68, 0x40, 0xb8, 0xc0, 0x41, 0x94, 0x5d, 0xd8, 0x7a, 0x60, 0x16, 0xe3, 0x28, 0x22, 0x31, 0x4f, 0xf1, 0xd6, 0x6b, 0xa8, 0xd8, 0x98, 0x4f, 0xfa, 0x94, 0x0b, 0x84, 0xa0, 0x14, 0xe2, 0x80, 0xd4, 0xb4, 0xa6, 0x76, 0xbc, 0x63, 0xa9, 0xdf, 0xe8, 0x3b, 0x28, 0x4d, 0x68, 0xe8, 0xd5, 0x0a, 0x4d, 0xed, 0x78, 0xef, 0xf4, 0xb1, 0x91, 0xe3, 0xa4, 0xb1, 0x78, 0xe0, 0x82, 0x86, 0x9e, 0xa5, 0xae, 0xb7, 0x30, 0xe8, 0x0b, 0xeb, 0x25, 0x11, 0xd8, 0xc3, 0x02, 0xa3, 0x4b, 0x38, 0x08, 0xf0, 0x7b, 0x47, 0x86, 0xcd, 0x9d, 0x88, 0xc4, 0x0e, 0x27, 0x2e, 0x0b, 0x3d, 0x25, 0x57, 0x3d, 0x7d, 0x68, 0xa4, 0x9e, 0x1a, 0x0b, 0x4f, 0x8d, 0x2e, 0x4b, 0x46, 0x3e, 0x79, 0x83, 0xfd, 0x84, 0x58, 0x77, 0x03, 0xfc, 0x5e, 0x3e, 0xc8, 0x07, 0x24, 0x1e, 0x2a, 0x5a, 0xeb, 0x35, 0xd4, 0x17, 0x12, 0x03, 0x1c, 0x0b, 0x2a, 0xb3, 0xb2, 0xd4, 0xd2, 0xa1, 0x38, 0x21, 0xf3, 0x2c, 0x12, 0xf9, 0x13, 0x3d, 0x81, 0x7d, 0x36, 0x0b, 0x49, 0xec, 0x5c, 0x33, 0x2e, 0x1c, 0x15, 0x67, 0x41, 0xa1, 0xbb, 0xca, 0xfc, 0x92, 0x71, 0x71, 0x85, 0x03, 0xd2, 0x9a, 0xc0, 0xa1, 0xc9, 0x99, 0xaf, 0x92, 0xfc, 0x22, 0x66, 0x49, 0x74, 0x49, 0x44, 0x4c, 0x5d, 0x8e, 0xda, 0x70, 0x10, 0x92, 0x59, 0xbe, 0xfb, 0x9a, 0x75, 0x37, 0x24, 0xb3, 0x75, 0x07, 0xd1, 0x63, 0xb8, 0x13, 0x31, 0xdf, 0x27, 0xb1, 0xe3, 0xb2, 0x24, 0x14, 0x4a, 0xae, 0x68, 0x55, 0x53, 0xdb, 0xb9, 0x34, 0xb5, 0xfe, 0x2a, 0xc1, 0xde, 0x22, 0x88, 0xa1, 0xc0, 0x22, 0xe1, 0xe8, 0x2b, 0x40, 0x23, 0xec, 0x4e, 0x7c, 0x36, 0x4e, 0x69, 0xce, 0x35, 0x0d, 0x85, 0x12, 0x29, 0x5a, 0x7a, 0x86, 0x28, 0xf2, 0x4b, 0x1a, 0x0a, 0xf4, 0x08, 0x20, 0x26, 0xd8, 0x73, 0x7c, 0x32, 0x25, 0x7e, 0xa6, 0xb0, 0x23, 0x2d, 0x7d, 0x69, 0x40, 0x0f, 0x60, 0x07, 0xbb, 0x93, 0x0c, 0x2d, 0x2a, 0xb4, 0x82, 0xdd, 0x49, 0x0a, 0x3e, 0x81, 0xfd, 0x18, 0x0b, 0xb2, 0x1a, 0x4b, 0x49, 0xc5, 0xb2, 0x2b, 0xcd, 0x1f, 0xe2, 0xe8, 0xc2, 0xae, 0x0c, 0xda, 0xa1, 0x9e, 0x33, 0xf2, 0x99, 0x3b, 0xa9, 0x95, 0x55, 0xc1, 0x9a, 0x1f, 0xed, 0x05, 0xb3, 0x7b, 0x26, 0xef, 0x59, 0x55, 0x49, 0x33, 0x3d, 0x75, 0x40, 0x53, 0x38, 0xa2, 0x8b, 0xbc, 0x3a, 0x63, 0x99, 0x58, 0x27, 0x48, 0x33, 0x5b, 0xbb, 0xd5, 0x2c, 0x1e, 0x57, 0x4f, 0x9f, 0xdd, 0xd8, 0x5b, 0x69, 0x76, 0x8c, 0xdc, 0xd2, 0xf4, 0x42, 0x11, 0xcf, 0xad, 0x43, 0xfa, 0x49, 0x65, 0xbb, 0xfd, 0xb1, 0xb2, 0x1d, 0x40, 0x99, 0x04, 0x91, 0x98, 0xd7, 0x2a, 0x4d, 0xed, 0xb8, 0x62, 0xa5, 0x87, 0x86, 0x80, 0xc6, 0xc7, 0xb5, 0x73, 0xda, 0xed, 0x39, 0x94, 0xa7, 0xb2, 0x73, 0x55, 0x4d, 0xaa, 0xa7, 0x4f, 0x73, 0x83, 0xcb, 0x7d, 0xd1, 0x4a, 0x89, 0x3f, 0x14, 0xbe, 0xd7, 0x5a, 0x3f, 0x43, 0x75, 0x25, 0xa1, 0xa8, 0x0e, 0x15, 0x2e, 0x70, 0x2c, 0x1c, 0xea, 0x65, 0x1d, 0x71, 0x5b, 0x9d, 0x4d, 0x0f, 0x1d, 0xc2, 0x2d, 0x12, 0x7a, 0x12, 0x48, 0x9b, 0xa0, 0x4c, 0x42, 0xcf, 0xf4, 0x5a, 0x7f, 0x6a, 0x00, 0x03, 0xd5, 0x70, 0x66, 0xf8, 0x8e, 0xa1, 0x2e, 0xe8, 0x3e, 0xe6, 0xc2, 0xc1, 0xae, 0x4b, 0x38, 0x77, 0xe4, 0xb2, 0xc8, 0xc6, 0xaf, 0xb1, 0x35, 0x7e, 0xf6, 0x62, 0x93, 0x58, 0x7b, 0x92, 0xd3, 0x51, 0x14, 0x69, 0x44, 0x0d, 0xa8, 0x50, 0x8f, 0x84, 0x82, 0x8a, 0x79, 0x36, 0x43, 0xcb, 0x73, 0x5e, 0x53, 0x15, 0x73, 0x9a, 0xaa, 0xf5, 0xb7, 0x06, 0xf5, 0xa1, 0xa0, 0xee, 0x64, 0xde, 0x7b, 0x4f, 0xdc, 0x44, 0x26, 0xa1, 0x23, 0x44, 0x4c, 0x47, 0x89, 0x20, 0x1c, 0xbd, 0x00, 0x7d, 0xc6, 0xe2, 0x09, 0x89, 0x55, 0xdd, 0x1c, 0xb9, 0x25, 0x33, 0x3f, 0x1f, 0xdd, 0xd8, 0x25, 0xd6, 0x5e, 0x4a, 0x5b, 0xae, 0x34, 0x1b, 0xea, 0xdc, 0xbd, 0x26, 0x5e, 0xe2, 0x13, 0x47, 0x30, 0x27, 0xcd, 0x9e, 0x0c, 0x9b, 0x25, 0x22, 0x2b, 0x4d, 0x7d, 0x7b, 0xf1, 0x64, 0x3b, 0xd6, 0xba, 0xbf, 0xe0, 0xda, 0x6c, 0x28, 0x99, 0x76, 0x4a, 0x6c, 0x3d, 0x83, 0xbb, 0x5b, 0xab, 0x07, 0x7d, 0x09, 0xfa, 0x46, 0x83, 0xf3, 0x9a, 0xd6, 0x2c, 0x1e, 0xef, 0x58, 0xfb, 0xeb, 0x9d, 0xc9, 0x5b, 0xff, 0x94, 0xe0, 0x68, 0xeb, 0x81, 0x73, 0x16, 0xbe, 0xa3, 0x63, 0x54, 0x83, 0xdb, 0x53, 0x12, 0x73, 0xca, 0xc2, 0x45, 0x89, 0xb3, 0x23, 0x3a, 0x85, 0x7b, 0x61, 0x12, 0x38, 0x6a, 0xde, 0xa3, 0x05, 0x8b, 0xab, 0x28, 0xca, 0x67, 0x85, 0x9a, 0x6c, 0xe6, 0x24, 0xb0, 0x08, 0xf6, 0x96, 0x4f, 0x72, 0xf4, 0x2d, 0x1c, 0x48, 0xce, 0x2c, 0xa6, 0xb2, 0x26, 0x1f, 0x48, 0xc5, 0x25, 0x09, 0x85, 0x49, 0xf0, 0x8b, 0x84, 0x57, 0x58, 0x14, 0xf6, 0x37, 0x55, 0x4a, 0x6a, 0x46, 0x9f, 0xdf, 0x98, 0xfd, 0x8d, 0x50, 0x8c, 0x75, 0x5f, 0xd2, 0x29, 0xdd, 0x8b, 0xd7, 0x1d, 0xf4, 0x41, 0xdf, 0x72, 0xae, 0xac, 0xb4, 0x3a, 0x9f, 0xa4, 0xb5, 0x11, 0x42, 0x2a, 0xb6, 0x3f, 0x5b, 0xb7, 0x36, 0x28, 0xdc, 0xcb, 0x71, 0x6a, 0x75, 0x7c, 0xcb, 0xe9, 0xf8, 0xfe, 0xb4, 0x3e, 0xbe, 0x4f, 0xfe, 0x9b, 0x2f, 0x2b, 0xa3, 0xdb, 0xf8, 0x1d, 0x0e, 0xf2, 0x7c, 0xfa, 0x3f, 0xb4, 0x9e, 0xfe, 0x01, 0x77, 0x56, 0xff, 0x83, 0x51, 0x03, 0xee, 0xdb, 0x9d, 0xe1, 0x85, 0xd3, 0x37, 0x87, 0xb6, 0x73, 0x61, 0x5e, 0x75, 0x1d, 0xf3, 0xea, 0x4d, 0xa7, 0x6f, 0x76, 0xf5, 0xcf, 0x50, 0x1d, 0x0e, 0x37, 0xb0, 0xab, 0x57, 0xd6, 0x65, 0xa7, 0xaf, 0x6b, 0x39, 0xd0, 0xd0, 0x36, 0xcf, 0x2f, 0xde, 0xea, 0x05, 0xf4, 0x10, 0x6a, 0x1b, 0x50, 0x6f, 0xf0, 0xb2, 0x77, 0xd9, 0xb3, 0x3a, 0x7d, 0xbd, 0xf8, 0xd4, 0xfb, 0xa0, 0x6f, 0xcf, 0x23, 0xb2, 0xae, 0x6f, 0xbf, 0x1d, 0xf4, 0x56, 0xf4, 0x1f, 0xc0, 0xd1, 0x06, 0xd6, 0xed, 0x9d, 0x9b, 0x43, 0xf3, 0xd5, 0x95, 0xae, 0xe5, 0x80, 0x9d, 0x73, 0xdb, 0x7c, 0x63, 0xda, 0x6f, 0xf5, 0xc2, 0xd9, 0x6f, 0x70, 0xe4, 0xb2, 0x20, 0x2f, 0x3b, 0x67, 0xbb, 0xcb, 0xf4, 0xc8, 0x19, 0x1e, 0x68, 0xbf, 0x9e, 0x8c, 0xa9, 0xb8, 0x4e, 0x46, 0x86, 0xcb, 0x82, 0xf6, 0xea, 0xb7, 0xd7, 0xd7, 0xd4, 0xf3, 0xdb, 0x63, 0x96, 0x7e, 0x0e, 0x65, 0x1f, 0x62, 0x3f, 0xe2, 0x88, 0x4e, 0x4f, 0x46, 0xb7, 0x94, 0xed, 0x9b, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xea, 0xcc, 0x39, 0x04, 0xac, 0x09, 0x00, 0x00, }, // uber/cadence/api/v1/workflow.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0xcd, 0x72, 0xdb, 0xc8, 0xb5, 0xbe, 0x20, 0x25, 0x59, 0x3a, 0xa4, 0x24, 0xa8, 0x25, 0x59, 0xf4, 0xcf, 0xd8, 0xb2, 0x66, 0xec, 0x91, 0x79, 0xc7, 0xd2, 0xc8, 0xf3, 0x3f, 0xbe, 0x73, 0x1d, 0x08, 0x84, 0x6c, 0xd8, 0x34, 0xc8, 0x34, 0x41, 0x6b, 0x34, 0x95, 0x04, 0x05, 0x91, 0x2d, 0x09, 0x31, 0x09, 0xb0, 0x80, 0xa6, 0x6d, 0xed, 0x53, 0x95, 0x6c, 0x93, 0xd5, 0x54, 0x56, 0x79, 0x80, 0x54, 0xa5, 0x52, 0x59, 0x64, 0x95, 0xca, 0x13, 0x64, 0x9b, 0x57, 0x48, 0xe5, 0x2d, 0x52, 0xdd, 0x68, 0x90, 0x00, 0x09, 0x12, 0x74, 0x52, 0x35, 0xd9, 0x09, 0xa7, 0xbf, 0xef, 0xf4, 0xe9, 0xf3, 0xd7, 0x07, 0x10, 0x61, 0xa7, 0x7f, 0x4a, 0xfc, 0xfd, 0x96, 0xdd, 0x26, 0x6e, 0x8b, 0xec, 0xdb, 0x3d, 0x67, 0xff, 0xf5, 0xc1, 0xfe, 0x1b, 0xcf, 0x7f, 0x75, 0xd6, 0xf1, 0xde, 0xec, 0xf5, 0x7c, 0x8f, 0x7a, 0x68, 0x9d, 0x61, 0xf6, 0x04, 0x66, 0xcf, 0xee, 0x39, 0x7b, 0xaf, 0x0f, 0xae, 0xdf, 0x3a, 0xf7, 0xbc, 0xf3, 0x0e, 0xd9, 0xe7, 0x90, 0xd3, 0xfe, 0xd9, 0x7e, 0xbb, 0xef, 0xdb, 0xd4, 0xf1, 0xdc, 0x90, 0x74, 0xfd, 0xf6, 0xe8, 0x3a, 0x75, 0xba, 0x24, 0xa0, 0x76, 0xb7, 0x27, 0x00, 0xdb, 0x69, 0x3b, 0xb7, 0xbc, 0x6e, 0x77, 0xa0, 0x22, 0xd5, 0x36, 0x6a, 0x07, 0xaf, 0x3a, 0x4e, 0x40, 0x43, 0xcc, 0xce, 0xf7, 0x45, 0xd8, 0x3c, 0x16, 0xe6, 0x6a, 0x6f, 0x49, 0xab, 0xcf, 0x4c, 0xd0, 0xdd, 0x33, 0x0f, 0x35, 0x01, 0x45, 0xe7, 0xb0, 0x48, 0xb4, 0x52, 0x92, 0xb6, 0xa5, 0xdd, 0xc2, 0xc3, 0x7b, 0x7b, 0x29, 0x47, 0xda, 0x1b, 0xd3, 0x83, 0xd7, 0xde, 0x8c, 0x8a, 0xd0, 0x67, 0x30, 0x47, 0x2f, 0x7b, 0xa4, 0x94, 0xe3, 0x8a, 0xee, 0x4c, 0x55, 0x64, 0x5e, 0xf6, 0x08, 0xe6, 0x70, 0xf4, 0x15, 0x40, 0x40, 0x6d, 0x9f, 0x5a, 0xcc, 0x0d, 0xa5, 0x3c, 0x27, 0x5f, 0xdf, 0x0b, 0x7d, 0xb4, 0x17, 0xf9, 0x68, 0xcf, 0x8c, 0x7c, 0x84, 0x97, 0x38, 0x9a, 0x3d, 0x33, 0x6a, 0xab, 0xe3, 0x05, 0x24, 0xa4, 0xce, 0x65, 0x53, 0x39, 0x9a, 0x53, 0x4d, 0x28, 0x86, 0xd4, 0x80, 0xda, 0xb4, 0x1f, 0x94, 0xe6, 0xb7, 0xa5, 0xdd, 0x95, 0x87, 0x07, 0xb3, 0x9d, 0x5e, 0x65, 0xcc, 0x06, 0x27, 0xe2, 0x42, 0x6b, 0xf8, 0x80, 0xee, 0xc2, 0xca, 0x85, 0x13, 0x50, 0xcf, 0xbf, 0xb4, 0x3a, 0xc4, 0x3d, 0xa7, 0x17, 0xa5, 0x85, 0x6d, 0x69, 0x37, 0x8f, 0x97, 0x85, 0xb4, 0xca, 0x85, 0xe8, 0x27, 0xb0, 0xd9, 0xb3, 0x7d, 0xe2, 0xd2, 0xa1, 0xfb, 0x2d, 0xc7, 0x3d, 0xf3, 0x4a, 0x57, 0xf8, 0x11, 0x76, 0x53, 0xad, 0xa8, 0x73, 0x46, 0x22, 0x92, 0x78, 0xbd, 0x37, 0x2e, 0x44, 0x0a, 0xac, 0x0c, 0xd5, 0x72, 0xcf, 0x2c, 0x66, 0x7a, 0x66, 0x79, 0xc0, 0xe0, 0xde, 0x79, 0x00, 0x73, 0x5d, 0xd2, 0xf5, 0x4a, 0x4b, 0x9c, 0x78, 0x2d, 0xd5, 0x9e, 0x17, 0xa4, 0xeb, 0x61, 0x0e, 0x43, 0x18, 0xd6, 0x02, 0x62, 0xfb, 0xad, 0x0b, 0xcb, 0xa6, 0xd4, 0x77, 0x4e, 0xfb, 0x94, 0x04, 0x25, 0xe0, 0xdc, 0xbb, 0xa9, 0xdc, 0x06, 0x47, 0x2b, 0x03, 0x30, 0x96, 0x83, 0x11, 0x09, 0xaa, 0xc2, 0x9a, 0xdd, 0xa7, 0x9e, 0xe5, 0x93, 0x80, 0x50, 0xab, 0xe7, 0x39, 0x2e, 0x0d, 0x4a, 0x05, 0xae, 0x73, 0x3b, 0x55, 0x27, 0x66, 0xc0, 0x3a, 0xc7, 0xe1, 0x55, 0x46, 0x8d, 0x09, 0xd0, 0x0d, 0x58, 0x62, 0xe5, 0x61, 0xb1, 0xfa, 0x28, 0x15, 0xb7, 0xa5, 0xdd, 0x25, 0xbc, 0xc8, 0x04, 0x55, 0x27, 0xa0, 0x48, 0x85, 0x95, 0xc1, 0x62, 0x18, 0x87, 0x35, 0xbe, 0xcf, 0x7b, 0xa9, 0xfb, 0x98, 0x82, 0x86, 0x8b, 0x91, 0x02, 0xee, 0xf5, 0x2d, 0xb8, 0xe2, 0x04, 0x56, 0xcb, 0xf7, 0xdc, 0xd2, 0xf2, 0xb6, 0xb4, 0xbb, 0x88, 0x17, 0x9c, 0x40, 0xf5, 0x3d, 0x17, 0x3d, 0x82, 0x42, 0xbf, 0xd7, 0xb6, 0xa9, 0xc8, 0xd2, 0x95, 0xcc, 0x58, 0x40, 0x08, 0xe7, 0x81, 0xf8, 0x39, 0xc8, 0x3d, 0xdb, 0xa7, 0x0e, 0x8f, 0x65, 0xcb, 0x73, 0xcf, 0x9c, 0xf3, 0xd2, 0xea, 0x76, 0x7e, 0xb7, 0xf0, 0xf0, 0xf1, 0x6c, 0xa9, 0xca, 0x6c, 0x63, 0xa9, 0x13, 0xaa, 0x50, 0xb9, 0x06, 0xcd, 0xa5, 0xfe, 0x25, 0x5e, 0xed, 0x25, 0xa5, 0xe8, 0x25, 0xac, 0x33, 0xf3, 0x2d, 0xef, 0x35, 0xf1, 0x3b, 0x76, 0xcf, 0xea, 0x79, 0x1d, 0xa7, 0x75, 0x59, 0x92, 0x79, 0x65, 0xa4, 0xf7, 0x05, 0x76, 0xc0, 0x5a, 0x08, 0xaf, 0x73, 0x34, 0x5e, 0x6b, 0x8d, 0x8a, 0xd0, 0x5b, 0xb8, 0x6d, 0xb7, 0xa8, 0xf3, 0x9a, 0x58, 0xad, 0x4e, 0x3f, 0xa0, 0xc4, 0xb7, 0x02, 0xd2, 0x21, 0x2d, 0x7e, 0x24, 0xb1, 0x07, 0xe2, 0x4e, 0x49, 0xaf, 0x3e, 0x85, 0x73, 0xd5, 0x90, 0xda, 0x88, 0x98, 0x62, 0xbb, 0x9b, 0xf6, 0x94, 0x55, 0xf4, 0x3e, 0x2c, 0xf3, 0x13, 0x05, 0xad, 0x0b, 0xd2, 0xee, 0x77, 0x48, 0x69, 0x9d, 0x47, 0xbe, 0xc8, 0x84, 0x0d, 0x21, 0x43, 0xc7, 0x20, 0x0f, 0xcb, 0x45, 0x74, 0x83, 0x0d, 0x7e, 0xe6, 0x8f, 0x66, 0x73, 0xb1, 0x68, 0x04, 0xab, 0x24, 0x29, 0x40, 0x26, 0x94, 0xa2, 0x8d, 0xdb, 0xd6, 0x48, 0x45, 0x6e, 0x66, 0x66, 0xc1, 0xd5, 0x01, 0x57, 0x8b, 0x97, 0xe6, 0xf5, 0x43, 0xd8, 0x48, 0x0b, 0x27, 0x92, 0x21, 0xff, 0x8a, 0x5c, 0xf2, 0x2e, 0xbe, 0x84, 0xd9, 0x9f, 0x68, 0x03, 0xe6, 0x5f, 0xdb, 0x9d, 0x7e, 0xd8, 0x90, 0x97, 0x70, 0xf8, 0xf0, 0x75, 0xee, 0x4b, 0x69, 0xe7, 0xfb, 0x1c, 0xdc, 0x1a, 0x6f, 0x6a, 0x5c, 0x99, 0xb8, 0xaa, 0xd0, 0xd7, 0xf1, 0x82, 0x91, 0x66, 0x29, 0x87, 0x61, 0x3d, 0xd9, 0xb0, 0x9d, 0xf0, 0x28, 0xeb, 0xed, 0x9e, 0x35, 0xec, 0xd4, 0x5e, 0x9f, 0x8a, 0x4b, 0xe2, 0xda, 0x98, 0x03, 0x2a, 0xc2, 0x00, 0x7c, 0x33, 0xee, 0x4e, 0x9f, 0x9a, 0x9e, 0x1a, 0xf5, 0x6e, 0xaf, 0x4f, 0xd1, 0x31, 0xdc, 0xe0, 0xe6, 0x4d, 0xd0, 0x9e, 0xcf, 0xd2, 0xbe, 0xc5, 0xd8, 0x29, 0x8a, 0x77, 0xfe, 0x26, 0xc1, 0x7a, 0x4a, 0xa7, 0x65, 0x0d, 0xa4, 0xed, 0x75, 0x6d, 0xc7, 0xb5, 0x9c, 0xb6, 0x70, 0xf2, 0x62, 0x28, 0xd0, 0xdb, 0xe8, 0x36, 0x14, 0xc4, 0xa2, 0x6b, 0x77, 0x23, 0x7f, 0x43, 0x28, 0x32, 0xec, 0x2e, 0x99, 0x70, 0xe3, 0xe6, 0xff, 0xd3, 0x1b, 0xf7, 0x0e, 0x14, 0x1d, 0xd7, 0xa1, 0x8e, 0x4d, 0x49, 0x9b, 0xd9, 0x35, 0xc7, 0x2f, 0x9b, 0xc2, 0x40, 0xa6, 0xb7, 0x77, 0x7e, 0x2d, 0xc1, 0xa6, 0xf6, 0x96, 0x12, 0xdf, 0xb5, 0x3b, 0x3f, 0xc8, 0x14, 0x30, 0x6a, 0x53, 0x6e, 0xdc, 0xa6, 0x3f, 0x2f, 0xc0, 0x7a, 0x9d, 0xb8, 0x6d, 0xc7, 0x3d, 0xe7, 0xc5, 0xed, 0xd0, 0x4b, 0x6e, 0xd1, 0x6d, 0x28, 0xd8, 0xe2, 0x79, 0xe8, 0x65, 0x88, 0x44, 0x7a, 0x1b, 0x1d, 0xc1, 0xf2, 0x00, 0x90, 0x39, 0x6a, 0x44, 0xaa, 0xf9, 0xa8, 0x51, 0xb4, 0x63, 0x4f, 0xe8, 0x31, 0xcc, 0xb3, 0x42, 0x0f, 0xa7, 0x8d, 0x95, 0x87, 0xf7, 0xd3, 0xef, 0xdb, 0xa4, 0x85, 0xac, 0xa8, 0x09, 0x0e, 0x79, 0x48, 0x87, 0xb5, 0x0b, 0x62, 0xfb, 0xf4, 0x94, 0xd8, 0xd4, 0x6a, 0x13, 0x6a, 0x3b, 0x9d, 0x40, 0xcc, 0x1f, 0x37, 0x27, 0x5c, 0xde, 0x97, 0x1d, 0xcf, 0x6e, 0x63, 0x79, 0x40, 0xab, 0x84, 0x2c, 0xf4, 0x0c, 0xd6, 0x3b, 0x76, 0x40, 0xad, 0xa1, 0x3e, 0xde, 0x20, 0xe6, 0x33, 0x1b, 0xc4, 0x1a, 0xa3, 0x3d, 0x8d, 0x58, 0xfc, 0xb6, 0x38, 0x02, 0x2e, 0x0c, 0xab, 0x82, 0xb4, 0x43, 0x4d, 0x0b, 0x99, 0x9a, 0x56, 0x19, 0xa9, 0x11, 0x72, 0xb8, 0x9e, 0x12, 0x5c, 0xb1, 0x29, 0x25, 0xdd, 0x1e, 0xe5, 0x13, 0xc9, 0x3c, 0x8e, 0x1e, 0xd1, 0x7d, 0x90, 0xbb, 0xf6, 0x5b, 0xa7, 0xdb, 0xef, 0x5a, 0x42, 0x14, 0xf0, 0xe9, 0x62, 0x1e, 0xaf, 0x0a, 0xb9, 0x22, 0xc4, 0x6c, 0x0c, 0x19, 0xb6, 0x3f, 0x6e, 0xc9, 0x52, 0xf6, 0x18, 0x32, 0x60, 0x70, 0x3b, 0x54, 0x58, 0x25, 0x6f, 0x7b, 0x4e, 0x58, 0xb3, 0xa1, 0x0e, 0xc8, 0xd4, 0xb1, 0x32, 0xa4, 0x70, 0x25, 0x8f, 0xa1, 0xc8, 0x9d, 0x72, 0x66, 0x3b, 0x9d, 0xbe, 0x4f, 0xc4, 0x0c, 0x91, 0x1e, 0xa6, 0xa3, 0x10, 0x83, 0x0b, 0x8c, 0x21, 0x1e, 0xd0, 0xc7, 0xb0, 0xc1, 0x15, 0xb0, 0x5c, 0x27, 0xbe, 0xe5, 0xb4, 0x89, 0x4b, 0x1d, 0x7a, 0x29, 0xc6, 0x08, 0xc4, 0xd6, 0x8e, 0xf9, 0x92, 0x2e, 0x56, 0xd0, 0xe7, 0xb0, 0x15, 0x85, 0x60, 0x94, 0xb4, 0xcc, 0x49, 0x9b, 0x62, 0x79, 0x84, 0x77, 0x1b, 0x0a, 0x91, 0x03, 0x58, 0x01, 0xac, 0xf0, 0xd2, 0x81, 0x48, 0xa4, 0xb7, 0x77, 0xfe, 0x94, 0x83, 0x6b, 0x22, 0x2f, 0xd5, 0x0b, 0xa7, 0xd3, 0xfe, 0x41, 0x2a, 0xfa, 0xa3, 0x98, 0x5a, 0x56, 0x75, 0xf1, 0x26, 0x27, 0xbf, 0x89, 0x0d, 0xf4, 0xbc, 0xd5, 0x8d, 0xd6, 0x7f, 0x7e, 0xac, 0xfe, 0xd9, 0xa0, 0x21, 0xc6, 0xdf, 0xb0, 0x6b, 0x8b, 0x21, 0x60, 0x6e, 0xca, 0xa0, 0x11, 0xb6, 0x64, 0xde, 0xa9, 0xa3, 0x41, 0xa3, 0x37, 0x2a, 0x42, 0x57, 0x61, 0x21, 0xec, 0xb9, 0xbc, 0x7a, 0x96, 0xb0, 0x78, 0xda, 0xf9, 0x47, 0x6e, 0xd0, 0x6f, 0x2a, 0xa4, 0xe5, 0x04, 0x91, 0xbf, 0x06, 0x6d, 0x40, 0xca, 0x6e, 0x03, 0x11, 0x31, 0xd1, 0x06, 0xc6, 0x53, 0x3c, 0xf7, 0xae, 0x29, 0xfe, 0x0d, 0x14, 0x13, 0xd5, 0x9a, 0xfd, 0xfe, 0x53, 0x08, 0xd2, 0x2b, 0x75, 0x2e, 0x59, 0xa9, 0x18, 0xb6, 0x3c, 0xdf, 0x39, 0x77, 0x5c, 0xbb, 0x63, 0x8d, 0x18, 0x99, 0xdd, 0x5b, 0x36, 0x23, 0x6a, 0x23, 0x61, 0xec, 0x48, 0x7e, 0x2e, 0x8c, 0xe5, 0xe7, 0x5f, 0x72, 0x70, 0x2d, 0x6a, 0x98, 0x55, 0xaf, 0x65, 0x77, 0x2a, 0x4e, 0xd0, 0xb3, 0x69, 0xeb, 0x62, 0xb6, 0xfe, 0xfe, 0xdf, 0xf7, 0xe7, 0xcf, 0xe0, 0x56, 0xd2, 0x02, 0xcb, 0x3b, 0xb3, 0xe8, 0x85, 0x13, 0x58, 0x71, 0x37, 0x4f, 0x57, 0x78, 0x3d, 0x61, 0x51, 0xed, 0xcc, 0xbc, 0x70, 0x02, 0xd1, 0x15, 0xd1, 0x7b, 0x00, 0x7c, 0x6e, 0xa1, 0xde, 0x2b, 0x12, 0xa6, 0x69, 0x11, 0xf3, 0x41, 0xcb, 0x64, 0x82, 0x9d, 0x67, 0x50, 0x88, 0xbf, 0xb5, 0x3c, 0x82, 0x05, 0xf1, 0xe2, 0x23, 0xf1, 0x99, 0xff, 0xfd, 0x8c, 0x17, 0x1f, 0xfe, 0x4e, 0x28, 0x28, 0x3b, 0x7f, 0xc8, 0xc1, 0x4a, 0x72, 0x09, 0x7d, 0x08, 0xab, 0xa7, 0x8e, 0x6b, 0xfb, 0x97, 0x56, 0xeb, 0x82, 0xb4, 0x5e, 0x05, 0xfd, 0xae, 0x08, 0xc2, 0x4a, 0x28, 0x56, 0x85, 0x14, 0x6d, 0xc2, 0x82, 0xdf, 0x77, 0xa3, 0xeb, 0x7b, 0x09, 0xcf, 0xfb, 0x7d, 0x36, 0xe7, 0x7c, 0x03, 0x37, 0xce, 0x1c, 0x3f, 0x60, 0x57, 0x5e, 0x58, 0x0d, 0x56, 0xcb, 0xeb, 0xf6, 0x3a, 0x24, 0x51, 0xea, 0x25, 0x0e, 0x89, 0xea, 0x45, 0x8d, 0x00, 0x9c, 0x5e, 0x6c, 0xf9, 0xc4, 0x1e, 0xc4, 0x26, 0xdb, 0x95, 0x05, 0x81, 0x17, 0x8d, 0x7c, 0x99, 0xb7, 0x76, 0xc7, 0x3d, 0x9f, 0x35, 0x8f, 0x8b, 0x11, 0x81, 0x2b, 0xb8, 0x05, 0xc0, 0xdf, 0x26, 0xa9, 0x7d, 0xda, 0x09, 0xef, 0xc5, 0x45, 0x1c, 0x93, 0x94, 0xff, 0x28, 0xc1, 0x46, 0xda, 0xad, 0x8f, 0x76, 0xe0, 0x56, 0x5d, 0x33, 0x2a, 0xba, 0xf1, 0xc4, 0x52, 0x54, 0x53, 0x7f, 0xa9, 0x9b, 0x27, 0x56, 0xc3, 0x54, 0x4c, 0xcd, 0xd2, 0x8d, 0x97, 0x4a, 0x55, 0xaf, 0xc8, 0xff, 0x83, 0x3e, 0x80, 0xed, 0x09, 0x98, 0x86, 0xfa, 0x54, 0xab, 0x34, 0xab, 0x5a, 0x45, 0x96, 0xa6, 0x68, 0x6a, 0x98, 0x0a, 0x36, 0xb5, 0x8a, 0x9c, 0x43, 0xff, 0x0b, 0x1f, 0x4e, 0xc0, 0xa8, 0x8a, 0xa1, 0x6a, 0x55, 0x0b, 0x6b, 0x3f, 0x6e, 0x6a, 0x0d, 0x06, 0xce, 0x97, 0x7f, 0x31, 0xb4, 0x39, 0xd1, 0xa2, 0xe2, 0x3b, 0x55, 0x34, 0x55, 0x6f, 0xe8, 0x35, 0x63, 0x9a, 0xcd, 0x23, 0x98, 0x09, 0x36, 0x8f, 0xa2, 0x22, 0x9b, 0xcb, 0xbf, 0xcc, 0x0d, 0x3f, 0x36, 0xe9, 0x6d, 0x4c, 0xfa, 0x83, 0xa6, 0xfc, 0x01, 0x6c, 0x1f, 0xd7, 0xf0, 0xf3, 0xa3, 0x6a, 0xed, 0xd8, 0xd2, 0x2b, 0x16, 0xd6, 0x9a, 0x0d, 0xcd, 0xaa, 0xd7, 0xaa, 0xba, 0x7a, 0x12, 0xb3, 0xe4, 0x4b, 0xf8, 0x74, 0x22, 0x4a, 0xa9, 0x32, 0x69, 0xa5, 0x59, 0xaf, 0xea, 0x2a, 0xdb, 0xf5, 0x48, 0xd1, 0xab, 0x5a, 0xc5, 0xaa, 0x19, 0xd5, 0x13, 0x59, 0x42, 0x1f, 0xc1, 0xee, 0xac, 0x4c, 0x39, 0x87, 0x1e, 0xc0, 0xfd, 0x89, 0x68, 0xac, 0x3d, 0xd3, 0x54, 0x33, 0x06, 0xcf, 0xa3, 0x03, 0x78, 0x30, 0x11, 0x6e, 0x6a, 0xf8, 0x85, 0x6e, 0x70, 0x87, 0x1e, 0x59, 0xb8, 0x69, 0x18, 0xba, 0xf1, 0x44, 0x9e, 0x2b, 0x5f, 0xc2, 0xda, 0xd8, 0x5b, 0x31, 0xba, 0x0d, 0x37, 0x54, 0x5c, 0x33, 0xac, 0xda, 0x4b, 0x0d, 0x57, 0x95, 0xfa, 0xf8, 0xf9, 0x27, 0x00, 0x1a, 0xcf, 0xf5, 0x7a, 0x3d, 0x0a, 0x42, 0x1a, 0xe0, 0xb0, 0x79, 0x74, 0xa4, 0x61, 0xab, 0x66, 0x68, 0x72, 0xae, 0xfc, 0x3b, 0x09, 0xd6, 0xc6, 0x2e, 0x4a, 0xa6, 0xba, 0xae, 0x60, 0xcd, 0x30, 0x2d, 0xb5, 0x5a, 0x4b, 0xf3, 0xfd, 0x04, 0x80, 0x72, 0xa8, 0x18, 0x95, 0x9a, 0x21, 0x4b, 0xe8, 0x1e, 0xec, 0xa4, 0x01, 0x44, 0x1a, 0x8a, 0xac, 0x94, 0x73, 0xe8, 0x0e, 0xbc, 0x97, 0x86, 0x1b, 0x38, 0x4a, 0xce, 0x97, 0xff, 0x99, 0x83, 0x9b, 0xd3, 0x3e, 0xa7, 0xb1, 0xe4, 0x1f, 0x78, 0x5c, 0xfb, 0x56, 0x53, 0x9b, 0x26, 0x4b, 0xb7, 0x50, 0x1f, 0x4b, 0xba, 0x66, 0x23, 0x66, 0x79, 0x3c, 0x9a, 0x13, 0xc0, 0x6a, 0xed, 0x45, 0xbd, 0xaa, 0x99, 0xdc, 0x87, 0x65, 0xb8, 0x97, 0x05, 0x0f, 0x73, 0x4b, 0xce, 0x25, 0xd2, 0x6a, 0x92, 0x6a, 0x7e, 0x6e, 0x56, 0x85, 0x68, 0x0f, 0xca, 0x59, 0xe8, 0x81, 0x17, 0x2a, 0xf2, 0x1c, 0xfa, 0x14, 0x3e, 0xce, 0x36, 0xdc, 0x30, 0x75, 0xa3, 0xa9, 0x55, 0x2c, 0xa5, 0x61, 0x19, 0xda, 0xb1, 0x3c, 0x3f, 0xcb, 0x71, 0x4d, 0xfd, 0x05, 0x2b, 0x8d, 0xa6, 0x29, 0x2f, 0x94, 0x7f, 0x95, 0x87, 0xad, 0x09, 0x1f, 0x2b, 0xd0, 0x5d, 0xb8, 0x93, 0xa2, 0x6a, 0xcc, 0xc1, 0x53, 0x61, 0xa2, 0x29, 0xc8, 0xd2, 0x74, 0xd8, 0xb0, 0xb1, 0x7d, 0x08, 0xef, 0x4f, 0x86, 0x0d, 0x03, 0x95, 0x4f, 0xf4, 0x8c, 0x31, 0xa0, 0x08, 0xd1, 0x1c, 0x4b, 0xcb, 0x29, 0xea, 0xa2, 0xe0, 0xcc, 0xa3, 0x5d, 0xf8, 0x60, 0x32, 0x2e, 0x16, 0x96, 0x85, 0x09, 0x61, 0x9c, 0x14, 0x90, 0x2b, 0xd3, 0x0f, 0x34, 0x0c, 0xc5, 0x62, 0xf9, 0xaf, 0x12, 0x5c, 0x55, 0x3d, 0x97, 0x3a, 0x6e, 0x9f, 0x28, 0x81, 0x41, 0xde, 0xe8, 0xe1, 0x38, 0xec, 0xf9, 0xcc, 0x77, 0x91, 0x66, 0xa1, 0xd8, 0xd2, 0x0d, 0xdd, 0xd4, 0x15, 0xb3, 0x86, 0x93, 0x91, 0x98, 0x0c, 0x63, 0x6d, 0xb9, 0xa2, 0xe1, 0x30, 0xc5, 0x27, 0xc3, 0xb0, 0x66, 0xe2, 0x13, 0x51, 0x95, 0xe1, 0x3d, 0x33, 0x19, 0xcb, 0x9b, 0x4d, 0x74, 0x0b, 0xc8, 0xf9, 0xf2, 0xef, 0x25, 0x28, 0x88, 0x6f, 0x24, 0xfc, 0x15, 0xba, 0x04, 0x1b, 0xec, 0x80, 0xb5, 0xa6, 0x69, 0x99, 0x27, 0x75, 0x2d, 0xd9, 0x4e, 0x12, 0x2b, 0x3c, 0xfe, 0x96, 0x59, 0x0b, 0x13, 0x35, 0x6c, 0x65, 0x49, 0x80, 0xd8, 0x85, 0x61, 0x38, 0x58, 0xce, 0x4d, 0xc5, 0x84, 0x7a, 0xf2, 0xe8, 0x3a, 0x5c, 0x4d, 0x60, 0x9e, 0x6a, 0x0a, 0x36, 0x0f, 0x35, 0xc5, 0x94, 0xe7, 0xca, 0xbf, 0x95, 0xe0, 0x5a, 0x74, 0x1f, 0x9a, 0x6c, 0xbc, 0x72, 0xba, 0xa4, 0x5d, 0xeb, 0x53, 0xd5, 0xee, 0x07, 0x04, 0xdd, 0x87, 0xbb, 0x83, 0x9b, 0xcc, 0x54, 0x1a, 0xcf, 0x87, 0xb1, 0xb2, 0x54, 0x85, 0xb5, 0xf8, 0xe1, 0x69, 0x32, 0xa1, 0xc2, 0x04, 0x59, 0x62, 0xd9, 0x30, 0x1d, 0x8a, 0xb5, 0x86, 0x66, 0xca, 0xb9, 0xf2, 0xdf, 0x0b, 0xb0, 0x15, 0x37, 0x8e, 0xbd, 0x68, 0x92, 0x76, 0x68, 0xda, 0x3d, 0xd8, 0x49, 0x2a, 0x11, 0xb7, 0xdd, 0xa8, 0x5d, 0x07, 0xf0, 0x60, 0x0a, 0xae, 0x69, 0x3c, 0x55, 0x8c, 0x0a, 0x7b, 0x8e, 0x40, 0xb2, 0x84, 0x1e, 0xc3, 0xa3, 0x29, 0x94, 0x43, 0xa5, 0x32, 0xf4, 0xf2, 0x60, 0xee, 0x50, 0x4c, 0x13, 0xeb, 0x87, 0x4d, 0x53, 0x6b, 0xc8, 0x39, 0xa4, 0x81, 0x92, 0xa1, 0x20, 0x79, 0x25, 0xa4, 0xaa, 0xc9, 0xa3, 0xaf, 0xe0, 0xb3, 0x2c, 0x3b, 0xc2, 0x94, 0xd1, 0x5f, 0x68, 0x38, 0x4e, 0x9d, 0x43, 0x5f, 0xc3, 0xe7, 0x19, 0x54, 0xb1, 0xf3, 0x18, 0x77, 0x1e, 0x3d, 0x82, 0x2f, 0x32, 0xad, 0x57, 0x6b, 0xb8, 0x62, 0xbd, 0x50, 0xf0, 0xf3, 0x24, 0x79, 0x01, 0xe9, 0xa0, 0x65, 0x6d, 0x2c, 0xfa, 0x97, 0x95, 0xd2, 0x11, 0x62, 0xaa, 0xae, 0xcc, 0xe0, 0x45, 0x26, 0xc8, 0x50, 0xb3, 0x88, 0x9e, 0x80, 0x3a, 0x9b, 0x2b, 0xa6, 0x2b, 0x5a, 0x42, 0xdf, 0x82, 0xf9, 0x6e, 0x51, 0xd5, 0xbe, 0x35, 0x35, 0x6c, 0x28, 0x59, 0x9a, 0x01, 0x7d, 0x03, 0x5f, 0x65, 0x3a, 0x2d, 0xd9, 0x7f, 0x62, 0xf4, 0x02, 0xfa, 0x02, 0x3e, 0x99, 0x42, 0x8f, 0xe7, 0xc8, 0x70, 0x36, 0xd4, 0x2b, 0x72, 0x11, 0x7d, 0x06, 0x07, 0x53, 0x88, 0xbc, 0x0a, 0xad, 0x86, 0xa9, 0xab, 0xcf, 0x4f, 0xc2, 0xe5, 0xaa, 0xde, 0x30, 0xe5, 0x65, 0xf4, 0x23, 0xf8, 0xbf, 0x29, 0xb4, 0xc1, 0x61, 0xd9, 0x1f, 0x1a, 0x8e, 0x95, 0x18, 0x83, 0x35, 0xb1, 0x26, 0xaf, 0xcc, 0x10, 0x93, 0x86, 0xfe, 0x24, 0xdb, 0x73, 0xab, 0x48, 0x85, 0xc7, 0x33, 0x95, 0x88, 0xfa, 0x54, 0xaf, 0x56, 0xd2, 0x95, 0xc8, 0xe8, 0x13, 0xd8, 0x9f, 0xa2, 0xe4, 0xa8, 0x86, 0x55, 0x4d, 0x0c, 0x0f, 0x83, 0x26, 0xb1, 0x86, 0x3e, 0x87, 0x87, 0xd3, 0x48, 0x8a, 0x5e, 0x65, 0x13, 0xe8, 0x28, 0x0f, 0xb1, 0x89, 0x66, 0xb6, 0xa3, 0xeb, 0x46, 0xbd, 0x69, 0x5a, 0x0d, 0xfd, 0x3b, 0x4d, 0x5e, 0x67, 0x13, 0x4d, 0x66, 0xa4, 0x22, 0x5f, 0xc9, 0x1b, 0xe3, 0xcd, 0x78, 0x6c, 0x93, 0x43, 0xdd, 0x50, 0xf0, 0x89, 0xbc, 0x99, 0x91, 0x7b, 0xe3, 0x8d, 0x2e, 0x91, 0x42, 0x57, 0x67, 0x39, 0x8e, 0xa6, 0x60, 0xf5, 0x69, 0xdc, 0xe3, 0x5b, 0xec, 0xd6, 0xb9, 0xc3, 0xbf, 0xcb, 0x8d, 0x8d, 0x5d, 0xf1, 0x16, 0x7f, 0x00, 0x0f, 0xc2, 0xb8, 0xa5, 0x64, 0xc1, 0x84, 0x6e, 0x7f, 0x08, 0xff, 0x3f, 0x1b, 0x65, 0xb0, 0xae, 0x54, 0xb1, 0xa6, 0x54, 0x4e, 0x06, 0x2f, 0x26, 0x52, 0xf9, 0x37, 0x39, 0x28, 0xab, 0xb6, 0xdb, 0x22, 0x9d, 0xe8, 0xff, 0x01, 0x53, 0xad, 0x7c, 0x04, 0x5f, 0xcc, 0x50, 0xef, 0x13, 0xec, 0x3d, 0x86, 0xc6, 0xbb, 0x92, 0x9b, 0xc6, 0x73, 0xa3, 0x76, 0x6c, 0x4c, 0x23, 0xc8, 0x12, 0x32, 0xe0, 0xd9, 0xbb, 0x2a, 0x1e, 0x73, 0xc9, 0x70, 0xd2, 0xcc, 0x71, 0xa7, 0x34, 0x9c, 0x73, 0xfe, 0xcf, 0x91, 0xd9, 0x9c, 0x22, 0xd2, 0xf8, 0xdf, 0x73, 0xca, 0xbb, 0x92, 0x67, 0x76, 0xca, 0xbb, 0x2a, 0x9e, 0xe6, 0x94, 0xc3, 0x9f, 0xc2, 0x56, 0xcb, 0xeb, 0xa6, 0x7d, 0x6b, 0x3a, 0x5c, 0x8e, 0xdc, 0x53, 0xf7, 0x3d, 0xea, 0xd5, 0xa5, 0xef, 0x0e, 0xce, 0x1d, 0x7a, 0xd1, 0x3f, 0xdd, 0x6b, 0x79, 0xdd, 0xfd, 0xf8, 0x8f, 0x52, 0x1e, 0x38, 0xed, 0xce, 0xfe, 0xb9, 0x17, 0xfe, 0xc8, 0x45, 0xfc, 0x42, 0xe5, 0x91, 0xdd, 0x73, 0x5e, 0x1f, 0x9c, 0x2e, 0x70, 0xd9, 0x27, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x6a, 0x76, 0xd7, 0x61, 0x23, 0x00, 0x00, }, // uber/cadence/api/v1/query.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xdf, 0x6f, 0x93, 0x50, 0x18, 0x95, 0x9a, 0x2c, 0xd9, 0xb7, 0x55, 0xc9, 0x9d, 0xc6, 0xda, 0xec, 0x47, 0xd3, 0xed, 0x61, 0x69, 0x14, 0xec, 0xf4, 0x6d, 0x4f, 0x8c, 0x5e, 0x0d, 0x86, 0x01, 0x03, 0xda, 0xa5, 0x7b, 0x21, 0x94, 0x5e, 0x2b, 0x8e, 0x72, 0xf1, 0x5e, 0x68, 0xed, 0x3f, 0xe0, 0xbb, 0x7f, 0x8d, 0xff, 0x9e, 0x81, 0x52, 0x5b, 0x2d, 0x33, 0xbe, 0x7d, 0x9c, 0xef, 0x1c, 0xce, 0x39, 0xb9, 0xf9, 0xe0, 0x24, 0x1b, 0x11, 0x26, 0x07, 0xfe, 0x98, 0xc4, 0x01, 0x91, 0xfd, 0x24, 0x94, 0x67, 0x5d, 0xf9, 0x6b, 0x46, 0xd8, 0x42, 0x4a, 0x18, 0x4d, 0x29, 0x3a, 0xc8, 0x09, 0x52, 0x49, 0x90, 0xfc, 0x24, 0x94, 0x66, 0xdd, 0x66, 0xab, 0x4a, 0x15, 0xd0, 0xe9, 0x94, 0xc6, 0x4b, 0x59, 0xb3, 0x5d, 0xc5, 0x98, 0x53, 0x76, 0xff, 0x29, 0xa2, 0xf3, 0x25, 0xa7, 0x7d, 0x0f, 0xf5, 0xdb, 0x12, 0xb9, 0xc9, 0x1d, 0xd1, 0x11, 0x40, 0x61, 0xed, 0xa5, 0x8b, 0x84, 0x34, 0x84, 0x96, 0x70, 0xbe, 0x6b, 0xef, 0x16, 0x88, 0xbb, 0x48, 0x08, 0xba, 0x5c, 0xad, 0x7d, 0x36, 0xe1, 0x8d, 0x5a, 0x4b, 0x38, 0xdf, 0xbb, 0x38, 0x94, 0x2a, 0xf2, 0x49, 0x96, 0xbf, 0x88, 0xa8, 0x3f, 0x2e, 0xc5, 0x0a, 0x9b, 0xf0, 0xf6, 0x4f, 0x01, 0x0e, 0xfe, 0x70, 0xb3, 0x09, 0xcf, 0xa2, 0x14, 0x61, 0xd8, 0x63, 0xc5, 0xb4, 0x36, 0x7d, 0x72, 0x71, 0x56, 0xf9, 0xd7, 0x0d, 0x59, 0x9e, 0xc7, 0x06, 0xf6, 0x7b, 0x46, 0xef, 0x60, 0xc7, 0x8f, 0xf9, 0x9c, 0xb0, 0xff, 0xca, 0x55, 0x72, 0xd1, 0x29, 0xd4, 0x09, 0x63, 0x94, 0x79, 0x53, 0xc2, 0xb9, 0x3f, 0x21, 0x8d, 0xc7, 0x45, 0xe7, 0xfd, 0x02, 0xbc, 0x5e, 0x62, 0x6d, 0x02, 0xf5, 0xd2, 0xf9, 0x0b, 0x09, 0x52, 0x32, 0x46, 0x2e, 0xec, 0x07, 0x11, 0xe5, 0xc4, 0xe3, 0xa9, 0x9f, 0x66, 0xbc, 0xcc, 0xdc, 0xad, 0x74, 0x5c, 0x55, 0xc6, 0xdf, 0x48, 0x90, 0xa5, 0x21, 0x8d, 0xd5, 0x5c, 0xe9, 0x14, 0x42, 0x7b, 0x2f, 0x58, 0x7f, 0x74, 0x62, 0x78, 0xfa, 0x57, 0x41, 0x74, 0x04, 0x2f, 0x6f, 0xfa, 0xd8, 0x1e, 0x7a, 0x36, 0x76, 0xfa, 0xba, 0xeb, 0xb9, 0x43, 0x0b, 0x7b, 0x9a, 0x31, 0x50, 0x74, 0xad, 0x27, 0x3e, 0x42, 0xc7, 0xd0, 0xdc, 0x5e, 0x2b, 0x86, 0x73, 0x8b, 0x6d, 0xdc, 0x13, 0x05, 0x74, 0x08, 0x8d, 0xed, 0xfd, 0x7b, 0x45, 0xd3, 0x71, 0x4f, 0xac, 0x75, 0x7e, 0x08, 0xf0, 0x6c, 0xa3, 0x97, 0x4a, 0xe3, 0x71, 0x98, 0x07, 0x44, 0x6d, 0x38, 0x5e, 0xc9, 0x3e, 0x62, 0xd5, 0xf5, 0x54, 0xd3, 0xe8, 0x69, 0xae, 0x66, 0x1a, 0x1b, 0xd6, 0xa7, 0x70, 0xf2, 0x00, 0xc7, 0x30, 0x5d, 0xcf, 0xb4, 0xb0, 0x21, 0x0a, 0xe8, 0x0d, 0xbc, 0xfa, 0x07, 0x49, 0x35, 0xaf, 0x2d, 0x1d, 0xbb, 0xb8, 0xe7, 0xa9, 0x3a, 0x56, 0x0c, 0x7d, 0x28, 0xd6, 0x3a, 0xdf, 0x05, 0x78, 0x5e, 0x64, 0x52, 0x69, 0xcc, 0x43, 0x9e, 0x92, 0x38, 0x58, 0xe8, 0x64, 0x46, 0xa2, 0xb5, 0xa1, 0x6a, 0x1a, 0x8e, 0xe6, 0xb8, 0xd8, 0x50, 0x87, 0x9e, 0x8e, 0x07, 0x58, 0xdf, 0x48, 0x75, 0x06, 0xad, 0x87, 0x48, 0x78, 0x80, 0x0d, 0xb7, 0xaf, 0xe8, 0xa2, 0xb0, 0xee, 0xb7, 0xcd, 0x72, 0x5c, 0xdb, 0x34, 0x3e, 0x88, 0xb5, 0xab, 0x3b, 0x78, 0x11, 0xd0, 0x69, 0xd5, 0x8b, 0x5e, 0x41, 0x11, 0xd0, 0xca, 0x2f, 0xc8, 0x12, 0xee, 0xba, 0x93, 0x30, 0xfd, 0x9c, 0x8d, 0xa4, 0x80, 0x4e, 0xe5, 0xcd, 0x93, 0x7b, 0x1d, 0x8e, 0x23, 0x79, 0x42, 0xe5, 0xe2, 0xd2, 0xca, 0xfb, 0xbb, 0xf4, 0x93, 0x70, 0xd6, 0x1d, 0xed, 0x14, 0xd8, 0xdb, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x69, 0x28, 0x5b, 0xfb, 0x03, 0x00, 0x00, }, // uber/cadence/api/v1/service_workflow.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x5a, 0x5b, 0x6f, 0xdc, 0xc6, 0xf5, 0x07, 0x57, 0xf7, 0xb3, 0x92, 0x2c, 0x8d, 0x6e, 0xeb, 0x95, 0x75, 0xa3, 0xe3, 0xfc, 0xf5, 0x77, 0xec, 0x55, 0x25, 0xc5, 0x97, 0x38, 0x69, 0x03, 0x79, 0x6d, 0x39, 0x2a, 0xec, 0x54, 0xa5, 0x54, 0x1b, 0xed, 0x0b, 0x31, 0x22, 0x67, 0x57, 0x63, 0x71, 0x49, 0x6a, 0x38, 0x94, 0xbc, 0xc9, 0x43, 0xd1, 0x22, 0x48, 0x81, 0xa2, 0x2d, 0xfa, 0xd8, 0x02, 0x05, 0xfa, 0xd0, 0x87, 0x3c, 0x35, 0x28, 0xd0, 0xa7, 0xbe, 0x17, 0xfd, 0x1e, 0xfd, 0x04, 0x79, 0xea, 0x6b, 0x50, 0x70, 0x66, 0xb8, 0x37, 0x91, 0xdc, 0x95, 0x94, 0xc0, 0x4e, 0xdf, 0x76, 0x66, 0xce, 0xef, 0xcc, 0x99, 0x73, 0x9b, 0x33, 0x67, 0x09, 0x37, 0xc3, 0x03, 0xc2, 0xd6, 0x2c, 0x6c, 0x13, 0xd7, 0x22, 0x6b, 0xd8, 0xa7, 0x6b, 0x27, 0xeb, 0x6b, 0x01, 0x61, 0x27, 0xd4, 0x22, 0xe6, 0xa9, 0xc7, 0x8e, 0x2a, 0x8e, 0x77, 0x5a, 0xf2, 0x99, 0xc7, 0x3d, 0x34, 0x15, 0xd1, 0x96, 0x14, 0x6d, 0x09, 0xfb, 0xb4, 0x74, 0xb2, 0x5e, 0x5c, 0xac, 0x7a, 0x5e, 0xd5, 0x21, 0x6b, 0x82, 0xe4, 0x20, 0xac, 0xac, 0xd9, 0x21, 0xc3, 0x9c, 0x7a, 0xae, 0x04, 0x15, 0x97, 0x3a, 0xd7, 0x39, 0xad, 0x91, 0x80, 0xe3, 0x9a, 0xaf, 0x08, 0x96, 0x93, 0x24, 0xb0, 0xbc, 0x5a, 0xad, 0xc1, 0x62, 0x25, 0x89, 0xe2, 0x90, 0x06, 0xdc, 0x63, 0xf5, 0x78, 0x97, 0x24, 0x92, 0xe3, 0x90, 0x34, 0x08, 0xf4, 0x24, 0x02, 0x8e, 0x83, 0x23, 0x87, 0x06, 0x3c, 0x8b, 0xa6, 0x5d, 0x07, 0xfa, 0x3f, 0x35, 0x58, 0x32, 0x22, 0xf9, 0x19, 0x7f, 0xa1, 0x56, 0x1e, 0xbf, 0x22, 0x56, 0x18, 0x9d, 0xd8, 0x20, 0xc7, 0x21, 0x09, 0x38, 0x9a, 0x85, 0x41, 0xdb, 0xab, 0x61, 0xea, 0x16, 0xb4, 0x65, 0x6d, 0x75, 0xc4, 0x50, 0x23, 0xf4, 0x13, 0x40, 0x31, 0x37, 0x93, 0xc4, 0xa0, 0x42, 0x6e, 0x59, 0x5b, 0xcd, 0x6f, 0xbc, 0x5d, 0x4a, 0x50, 0x6e, 0xe9, 0xec, 0x16, 0x93, 0xa7, 0x9d, 0x53, 0xa8, 0x08, 0xc3, 0xd4, 0x26, 0x2e, 0xa7, 0xbc, 0x5e, 0xe8, 0x13, 0x1b, 0x36, 0xc6, 0x91, 0x28, 0x8c, 0xe0, 0xc0, 0x73, 0x0b, 0xfd, 0x52, 0x14, 0x39, 0xd2, 0xff, 0xaa, 0xc1, 0xf2, 0x23, 0x8a, 0xab, 0xae, 0x17, 0x90, 0xef, 0xc0, 0x39, 0xf4, 0x2f, 0x34, 0x58, 0xc9, 0x90, 0x37, 0xf0, 0x3d, 0x37, 0x20, 0xa9, 0x02, 0xbf, 0x84, 0x05, 0x5b, 0x82, 0x39, 0xb5, 0xcc, 0x4b, 0xcb, 0x3e, 0xdf, 0x64, 0x76, 0x66, 0x51, 0xff, 0x03, 0xc0, 0xc2, 0xde, 0x85, 0xdc, 0x63, 0x09, 0xf2, 0x0d, 0xd1, 0xa8, 0x2d, 0x64, 0x1a, 0x31, 0x20, 0x9e, 0xda, 0xb1, 0xd1, 0x36, 0x8c, 0x35, 0x08, 0x78, 0xdd, 0x27, 0x42, 0x4b, 0xf9, 0x8d, 0x95, 0x4c, 0xb1, 0xf7, 0xeb, 0x3e, 0x31, 0x46, 0x4f, 0x5b, 0x46, 0xe8, 0x01, 0x8c, 0x44, 0x9e, 0x6f, 0x46, 0xae, 0x2f, 0xfc, 0x22, 0xbf, 0xb1, 0x90, 0xc8, 0x63, 0x1f, 0x07, 0x47, 0x4f, 0x69, 0xc0, 0x8d, 0x61, 0xae, 0x7e, 0xa1, 0x0d, 0x18, 0xa0, 0xae, 0x1f, 0xf2, 0xc2, 0x80, 0xc0, 0x5d, 0x4b, 0xc4, 0xed, 0xe2, 0xba, 0xe3, 0x61, 0xdb, 0x90, 0xa4, 0x08, 0xc3, 0x72, 0x43, 0xd5, 0xa6, 0x08, 0x1d, 0x93, 0x7b, 0xa6, 0xe5, 0x78, 0x01, 0x31, 0xa3, 0x6c, 0xe0, 0x85, 0xbc, 0x30, 0x28, 0xd8, 0x5d, 0x2d, 0xc9, 0x6c, 0x51, 0x8a, 0xb3, 0x45, 0xe9, 0x91, 0xca, 0x26, 0xc6, 0xb5, 0x06, 0x0b, 0xa1, 0xdd, 0x7d, 0xaf, 0x1c, 0xe1, 0xf7, 0x25, 0x1c, 0xbd, 0x80, 0x79, 0x71, 0xa4, 0x14, 0xee, 0x43, 0xdd, 0xb8, 0xcf, 0x45, 0xe8, 0x24, 0xc6, 0xad, 0x4e, 0x39, 0xdc, 0x11, 0x5c, 0x0b, 0x00, 0x4c, 0xda, 0x34, 0xb2, 0xd7, 0x88, 0x58, 0x1d, 0x51, 0x33, 0x3b, 0x36, 0xb2, 0xa0, 0xd0, 0x62, 0x4f, 0x93, 0x91, 0x30, 0x20, 0xa6, 0xef, 0x39, 0xd4, 0xaa, 0x17, 0x60, 0x59, 0x5b, 0x1d, 0xdf, 0xb8, 0x99, 0x69, 0xb9, 0x1d, 0xdb, 0x88, 0x20, 0xbb, 0x02, 0x61, 0xcc, 0x9c, 0x26, 0x4d, 0xa3, 0x32, 0x8c, 0x32, 0xc2, 0x59, 0x3d, 0x66, 0x9c, 0x17, 0x27, 0x5d, 0x4e, 0x64, 0x6c, 0x44, 0x84, 0x8a, 0x5d, 0x9e, 0x35, 0x07, 0xe8, 0x3a, 0x8c, 0x59, 0x2c, 0xb2, 0x8d, 0x75, 0x48, 0xec, 0xd0, 0x21, 0x85, 0x51, 0x71, 0x96, 0xd1, 0x68, 0x72, 0x4f, 0xcd, 0xa1, 0xdb, 0xd0, 0x5f, 0x23, 0x35, 0xaf, 0x30, 0xa6, 0x74, 0x99, 0xb4, 0xc3, 0x33, 0x52, 0xf3, 0x0c, 0x41, 0x86, 0x0c, 0x98, 0x0c, 0x08, 0x66, 0xd6, 0xa1, 0x89, 0x39, 0x67, 0xf4, 0x20, 0xe4, 0x24, 0x28, 0x8c, 0x0b, 0xec, 0x8d, 0x44, 0xec, 0x9e, 0xa0, 0xde, 0x6a, 0x10, 0x1b, 0x13, 0x41, 0xc7, 0x0c, 0xda, 0x84, 0xc1, 0x43, 0x82, 0x6d, 0xc2, 0x0a, 0x57, 0x04, 0xa3, 0xf9, 0x44, 0x46, 0x1f, 0x09, 0x12, 0x43, 0x91, 0xa2, 0x07, 0x90, 0xb7, 0x89, 0x83, 0xeb, 0xd2, 0x37, 0x0a, 0x13, 0xdd, 0x5c, 0x01, 0x04, 0xb5, 0xf0, 0x05, 0xf4, 0x01, 0x8c, 0xbe, 0xa4, 0x9c, 0x13, 0xa6, 0xc0, 0x93, 0xdd, 0xc0, 0x79, 0x49, 0xde, 0x40, 0x57, 0x28, 0x0b, 0xb8, 0xc9, 0x42, 0xd7, 0xc4, 0xbc, 0x80, 0x04, 0xba, 0x78, 0x06, 0xbd, 0x1f, 0xdf, 0x88, 0x06, 0x08, 0x7a, 0x23, 0x74, 0xb7, 0x38, 0x7a, 0x0e, 0x53, 0xc2, 0x28, 0xde, 0x09, 0x61, 0x0e, 0xf6, 0x63, 0x03, 0x4f, 0x09, 0xcf, 0x49, 0x4e, 0x55, 0x65, 0xe6, 0xb9, 0x3f, 0x92, 0xe4, 0xca, 0xcc, 0x93, 0x56, 0xe7, 0x14, 0x7a, 0x05, 0x4b, 0xd8, 0xe2, 0xf4, 0x84, 0x98, 0x96, 0x13, 0x06, 0xe2, 0x6c, 0xc4, 0x21, 0x96, 0x08, 0x4e, 0xb5, 0xc7, 0xb4, 0x10, 0x74, 0x3d, 0x71, 0x8f, 0x2d, 0x81, 0x2d, 0x4b, 0xe8, 0x5e, 0x8c, 0x54, 0xdb, 0x5d, 0xc3, 0x19, 0xab, 0xfa, 0x3d, 0x58, 0x4c, 0xcb, 0x8c, 0x2a, 0x81, 0xcf, 0xc0, 0x60, 0xa4, 0x2b, 0x6a, 0xab, 0xd4, 0x38, 0xc0, 0x42, 0x77, 0xc7, 0xd6, 0x19, 0xe8, 0xc9, 0xc0, 0xad, 0xa0, 0xee, 0x5a, 0x71, 0x5e, 0x7d, 0x0a, 0x43, 0x2a, 0xf8, 0x04, 0x3a, 0xbf, 0xb1, 0x91, 0xec, 0x67, 0x59, 0xc9, 0xd9, 0x88, 0x59, 0xe8, 0x37, 0xe0, 0x7a, 0xe6, 0x9e, 0x52, 0x62, 0xfd, 0x3d, 0x58, 0x4e, 0x2f, 0x07, 0xb2, 0x4f, 0xf5, 0xaf, 0x1c, 0x2c, 0xee, 0xd1, 0xaa, 0x8b, 0x9d, 0xef, 0x42, 0x25, 0xd1, 0x9e, 0xec, 0xfa, 0x3b, 0x93, 0xdd, 0x12, 0xe4, 0x03, 0x71, 0x16, 0xd3, 0xc5, 0x35, 0x22, 0x6e, 0x87, 0x11, 0x03, 0xe4, 0xd4, 0xc7, 0xb8, 0x46, 0xd0, 0x87, 0x30, 0xaa, 0x08, 0xe4, 0xfd, 0x31, 0xd8, 0xc3, 0xfd, 0xa1, 0x58, 0xee, 0x88, 0x5b, 0xa4, 0x00, 0x43, 0x96, 0xe7, 0x72, 0xe6, 0x39, 0x22, 0x9d, 0x8f, 0x1a, 0xf1, 0x50, 0x5f, 0x81, 0xa5, 0x54, 0x3d, 0x2a, 0x33, 0x7d, 0xad, 0xc1, 0xff, 0x29, 0x1a, 0xca, 0x0f, 0xb3, 0xef, 0xe7, 0x17, 0x30, 0x26, 0xaf, 0x91, 0xcb, 0x7b, 0xd3, 0xa8, 0x60, 0x14, 0x33, 0xee, 0xd0, 0x51, 0xae, 0xab, 0x8e, 0xfa, 0x2e, 0xa1, 0xa3, 0xfe, 0x76, 0x1d, 0x6d, 0xc1, 0x6a, 0xf7, 0xf3, 0x67, 0xfb, 0xeb, 0xe7, 0x1a, 0xdc, 0xea, 0xc6, 0xa3, 0x2d, 0x20, 0x9f, 0x77, 0x06, 0xe4, 0x07, 0xc9, 0x2a, 0xec, 0xcd, 0x2e, 0xcd, 0xd0, 0x5c, 0x83, 0xdb, 0x3d, 0xca, 0xa1, 0xac, 0xff, 0x65, 0x0e, 0x16, 0x0c, 0x12, 0x90, 0x37, 0xa6, 0x64, 0x6f, 0x96, 0xe5, 0x7d, 0xad, 0x65, 0x39, 0xba, 0x07, 0x05, 0x9b, 0x58, 0x34, 0x88, 0x72, 0x71, 0x85, 0xba, 0x34, 0x38, 0x34, 0xc9, 0x09, 0x71, 0x1b, 0x21, 0xd7, 0x67, 0xcc, 0xc4, 0xeb, 0xdb, 0x62, 0xf9, 0x71, 0xb4, 0xba, 0x63, 0x77, 0x44, 0xe7, 0x40, 0x67, 0x74, 0x96, 0x60, 0x2a, 0x38, 0xa2, 0xbe, 0xa9, 0xbc, 0x8b, 0x11, 0xec, 0xfb, 0x4e, 0x5d, 0xc4, 0xe0, 0xb0, 0x31, 0x19, 0x2d, 0x49, 0x85, 0x1a, 0x72, 0x21, 0xca, 0xd4, 0x69, 0xfa, 0xca, 0xf6, 0x91, 0x3f, 0xe5, 0xe0, 0x86, 0xd2, 0x69, 0x19, 0xbb, 0x16, 0xf9, 0x5f, 0x48, 0x6d, 0xd3, 0x30, 0x60, 0xe1, 0x30, 0x88, 0x93, 0x9a, 0x1c, 0xa0, 0x4d, 0x98, 0x95, 0x97, 0x7b, 0xb3, 0xb4, 0x55, 0x0a, 0x19, 0x14, 0x64, 0x53, 0x62, 0xb5, 0x29, 0x93, 0x50, 0xcf, 0x2a, 0xbc, 0xdd, 0x4d, 0x3b, 0xca, 0x65, 0xff, 0x9e, 0x83, 0x95, 0x7d, 0xc2, 0x6a, 0xd4, 0xc5, 0x9c, 0xbc, 0xe9, 0x6e, 0x7b, 0x17, 0x86, 0x6c, 0xc2, 0x31, 0x75, 0x02, 0xf5, 0x9c, 0xc8, 0x4e, 0x59, 0x31, 0x71, 0x9b, 0x51, 0x06, 0x3a, 0x8c, 0x72, 0x21, 0xfd, 0xbe, 0x05, 0x7a, 0x96, 0xd2, 0x94, 0x6e, 0xff, 0x13, 0x3d, 0x7e, 0x49, 0x60, 0x31, 0x7a, 0xf0, 0xc6, 0xa8, 0xf6, 0x00, 0xe6, 0x44, 0xbb, 0xc2, 0xb4, 0x3c, 0x37, 0xa0, 0x01, 0x27, 0xae, 0x55, 0x37, 0x1d, 0x72, 0x42, 0x1c, 0xa1, 0xeb, 0xb4, 0xb7, 0xc2, 0x8f, 0x23, 0x4c, 0xb9, 0x09, 0x79, 0x1a, 0x21, 0x8c, 0x99, 0xe3, 0xa4, 0x69, 0xfd, 0xeb, 0x3e, 0x58, 0xc9, 0x38, 0xb7, 0x8a, 0x6c, 0x07, 0xe6, 0x9a, 0x2a, 0xb7, 0x3c, 0xb7, 0x42, 0xab, 0xaa, 0xba, 0x55, 0x59, 0x7c, 0xb3, 0xb7, 0x53, 0x96, 0x5b, 0xa1, 0xc6, 0x2c, 0x49, 0x9c, 0x8f, 0xce, 0x7d, 0x56, 0x9d, 0x26, 0x75, 0x2b, 0x9e, 0xd2, 0xe9, 0xcd, 0xde, 0x76, 0xdb, 0x71, 0x2b, 0x5e, 0xf3, 0x8d, 0xd4, 0x36, 0x8d, 0x5e, 0x00, 0xf2, 0x89, 0x6b, 0x53, 0xb7, 0x6a, 0x8a, 0xfa, 0x94, 0x72, 0x4a, 0x82, 0x42, 0xdf, 0x72, 0xdf, 0x6a, 0x7e, 0x63, 0x35, 0xd9, 0x53, 0x25, 0xf9, 0x96, 0xa4, 0xae, 0x0b, 0xe6, 0x93, 0x7e, 0xdb, 0x24, 0x25, 0x01, 0xfa, 0x29, 0x4c, 0xc4, 0x8c, 0xad, 0x43, 0xea, 0xd8, 0x8c, 0xb8, 0x85, 0x7e, 0xc1, 0xb6, 0x94, 0xc5, 0xb6, 0x1c, 0xd1, 0xb6, 0x4b, 0x7e, 0xc5, 0x6f, 0x59, 0x62, 0xc4, 0x45, 0x7b, 0x4d, 0xd6, 0x71, 0xc6, 0x57, 0x4f, 0xee, 0x4c, 0x89, 0x1f, 0x29, 0xda, 0x36, 0xa6, 0xf1, 0xa4, 0xfe, 0x59, 0x1f, 0x4c, 0x0b, 0x8f, 0x89, 0xd5, 0xf7, 0x9a, 0x9c, 0xfd, 0x3e, 0x0c, 0x08, 0x0f, 0x55, 0x05, 0x8e, 0x9e, 0xc9, 0x49, 0x08, 0x6c, 0x48, 0x00, 0x32, 0x61, 0x56, 0x86, 0x09, 0x23, 0x2f, 0x89, 0xc5, 0x23, 0xff, 0xb4, 0xa9, 0x10, 0xaa, 0x5f, 0x44, 0xc9, 0xff, 0xa7, 0x47, 0x89, 0x21, 0x10, 0xe5, 0x18, 0x60, 0x4c, 0x1f, 0x27, 0xcc, 0x66, 0xc5, 0xe1, 0xc0, 0x37, 0x15, 0x87, 0x7f, 0xd1, 0x60, 0xa6, 0xc3, 0x0c, 0x2a, 0xf6, 0x3e, 0x84, 0xd1, 0xf8, 0x78, 0x41, 0xe8, 0xc4, 0x65, 0x53, 0x97, 0x02, 0x50, 0x9d, 0x23, 0x02, 0xa0, 0x1d, 0x18, 0x6f, 0xd5, 0x0f, 0xb1, 0x95, 0xb1, 0xf4, 0x6e, 0x7a, 0x21, 0xb6, 0x31, 0x76, 0xdc, 0x3a, 0xd4, 0xbf, 0xd2, 0x60, 0x2e, 0xce, 0x16, 0x8d, 0x46, 0x50, 0x17, 0x7f, 0x69, 0xeb, 0x2c, 0xe5, 0xce, 0xd7, 0x59, 0x7a, 0x02, 0xe3, 0x0d, 0x6c, 0xb3, 0xbd, 0x35, 0x9e, 0xd2, 0xde, 0x8a, 0x19, 0xc8, 0xf6, 0x16, 0x6f, 0x19, 0x45, 0x45, 0x14, 0x75, 0x2d, 0x27, 0xb4, 0x89, 0xd9, 0x64, 0x18, 0x70, 0xcc, 0x43, 0x79, 0x3d, 0x0d, 0x1b, 0x33, 0x6a, 0x3d, 0x66, 0xb2, 0x27, 0x16, 0xf5, 0x7f, 0xe4, 0xa0, 0x70, 0xf6, 0xc4, 0xca, 0x34, 0xef, 0xc1, 0x90, 0xef, 0x39, 0x0e, 0x61, 0x41, 0x41, 0x13, 0x21, 0xbe, 0x94, 0x6c, 0x15, 0x41, 0x23, 0xc2, 0x2f, 0xa6, 0x47, 0xcf, 0x60, 0xe2, 0x8c, 0x20, 0x52, 0x39, 0xd7, 0x33, 0xcf, 0x26, 0xc5, 0x32, 0xc6, 0x79, 0xdb, 0x18, 0xbd, 0x80, 0x09, 0x1f, 0x33, 0x4e, 0x5b, 0x12, 0xb4, 0x0a, 0xa4, 0x5b, 0x99, 0xec, 0x76, 0x63, 0x90, 0xcc, 0xc0, 0xc6, 0x15, 0xbf, 0x7d, 0xe2, 0x32, 0x7d, 0x41, 0xfd, 0x0e, 0xcc, 0x3f, 0x21, 0x3c, 0x5e, 0x08, 0x1e, 0xd6, 0x1f, 0x09, 0x8f, 0xe8, 0xe2, 0x30, 0xfa, 0xef, 0xfa, 0xe1, 0x5a, 0x32, 0x4e, 0xa9, 0xfd, 0xe7, 0x30, 0xdb, 0xa8, 0x88, 0x9b, 0x4a, 0xac, 0x61, 0x5f, 0x59, 0xe1, 0x87, 0x89, 0x02, 0x66, 0xb1, 0x2c, 0xc5, 0xe9, 0x30, 0xa6, 0x78, 0x86, 0xfd, 0xc7, 0x2e, 0x67, 0x75, 0x63, 0xca, 0x3e, 0xbb, 0x12, 0x09, 0xa0, 0x2e, 0x8d, 0x7a, 0x87, 0x00, 0xb9, 0x8b, 0x0a, 0x10, 0x5f, 0x2b, 0x67, 0x05, 0xc0, 0x67, 0x57, 0x8a, 0x61, 0xe4, 0x94, 0xc9, 0x12, 0xa3, 0x09, 0xe8, 0x3b, 0x22, 0x75, 0xa5, 0xd3, 0xe8, 0x27, 0x2a, 0xc3, 0xc0, 0x09, 0x76, 0x42, 0xa2, 0x1c, 0xec, 0x76, 0xa2, 0x74, 0x69, 0x4e, 0x6e, 0x48, 0xec, 0x83, 0xdc, 0x7d, 0x2d, 0xda, 0x36, 0x4d, 0xce, 0x6f, 0x71, 0x5b, 0x3d, 0x80, 0x05, 0x11, 0xc8, 0x9d, 0x3e, 0x1b, 0x7c, 0x8b, 0xa9, 0x47, 0xff, 0x3c, 0x07, 0x8b, 0x69, 0xbb, 0x2a, 0x3f, 0x3c, 0x86, 0x85, 0x04, 0x37, 0x68, 0x44, 0x50, 0x9c, 0x14, 0x4a, 0xbd, 0x45, 0xe0, 0x33, 0xc2, 0xb1, 0x8d, 0x39, 0x36, 0x8a, 0x9d, 0x16, 0x6f, 0x6e, 0x1d, 0x6d, 0x99, 0xe0, 0xfa, 0x2d, 0x5b, 0xe6, 0x2e, 0xb6, 0x65, 0xa7, 0x97, 0x37, 0xb7, 0xd4, 0xe7, 0x60, 0xe6, 0x09, 0xe1, 0xaa, 0x7d, 0x27, 0x92, 0x98, 0x7a, 0x72, 0xff, 0x52, 0x83, 0xd9, 0xce, 0x15, 0xa5, 0x99, 0x43, 0xb8, 0x1a, 0x84, 0xbe, 0xef, 0x31, 0x4e, 0x6c, 0xd3, 0x72, 0x68, 0xf4, 0x5c, 0x3d, 0x21, 0x2c, 0x50, 0x5a, 0x49, 0xcf, 0x4b, 0x7b, 0x31, 0xaa, 0x2c, 0x40, 0xcf, 0x15, 0xc6, 0x98, 0x0b, 0x92, 0x17, 0xf4, 0xaf, 0xfa, 0x40, 0x7f, 0x92, 0xf0, 0x28, 0xfd, 0x48, 0xfe, 0x15, 0xf8, 0x9a, 0x8a, 0x99, 0x79, 0x18, 0xf1, 0x71, 0x95, 0x98, 0x01, 0xfd, 0x44, 0x5e, 0x59, 0x03, 0xc6, 0x70, 0x34, 0xb1, 0x47, 0x3f, 0x21, 0xe8, 0x6d, 0xb8, 0xe2, 0x92, 0x57, 0x91, 0xd5, 0xaa, 0xc4, 0xe4, 0xde, 0x11, 0x71, 0x55, 0x63, 0x66, 0x2c, 0x9a, 0xde, 0xc5, 0x55, 0xb2, 0x1f, 0x4d, 0xa2, 0x77, 0x00, 0x9d, 0x62, 0xca, 0xcd, 0x8a, 0xc7, 0x4c, 0x97, 0x9c, 0xca, 0x57, 0xbf, 0xa8, 0x38, 0x86, 0x8d, 0x2b, 0xd1, 0xca, 0xb6, 0xc7, 0x3e, 0x26, 0xa7, 0xe2, 0xb9, 0x8f, 0x4c, 0xb8, 0xaa, 0xfe, 0xfd, 0x54, 0xdd, 0x81, 0x0a, 0x75, 0x38, 0x61, 0xf2, 0xd2, 0x1c, 0x14, 0x97, 0xe6, 0x5b, 0x89, 0xe7, 0x11, 0xf0, 0x6d, 0x41, 0x2c, 0xee, 0xcd, 0x59, 0xc5, 0xa6, 0x63, 0x1e, 0x5d, 0x87, 0x31, 0xd1, 0x2e, 0xc0, 0xcc, 0x3a, 0xa4, 0x27, 0x58, 0x36, 0xdc, 0x86, 0x8d, 0xd1, 0x68, 0x72, 0x4b, 0xcd, 0x65, 0x55, 0x4a, 0xc3, 0xdf, 0x54, 0xa5, 0xf4, 0x6f, 0x0d, 0xae, 0x67, 0x5a, 0x5c, 0xf9, 0xe0, 0x5d, 0x18, 0x52, 0x47, 0xc9, 0x2c, 0x99, 0x62, 0x58, 0x4c, 0x8c, 0x7e, 0x00, 0x79, 0x86, 0x4f, 0xcd, 0x18, 0x2b, 0x03, 0x2a, 0x39, 0x6d, 0x3c, 0xc2, 0x1c, 0x3f, 0x74, 0xbc, 0x03, 0x03, 0x18, 0x3e, 0x55, 0x8c, 0x92, 0xcc, 0xdb, 0x97, 0x64, 0xde, 0x22, 0x0c, 0x4b, 0x5d, 0x12, 0x5b, 0x95, 0x20, 0x8d, 0xb1, 0xfe, 0x37, 0x0d, 0x46, 0xb7, 0x09, 0xe6, 0x21, 0x23, 0xdb, 0x0e, 0xae, 0x06, 0x88, 0xc2, 0x46, 0xc2, 0x93, 0x08, 0x3b, 0x8c, 0x60, 0x3b, 0xd2, 0x76, 0xcd, 0x77, 0x48, 0x14, 0x6b, 0x84, 0x31, 0x8f, 0x99, 0xc4, 0xc5, 0x07, 0x0e, 0x91, 0x6d, 0x98, 0x61, 0xe3, 0xf6, 0xd9, 0x4e, 0x98, 0xc4, 0x95, 0x63, 0xd8, 0xe3, 0x08, 0xf5, 0x58, 0x82, 0xd0, 0x1d, 0x98, 0xc5, 0x21, 0xf7, 0x2a, 0x1e, 0x3b, 0xc5, 0x4c, 0x3c, 0x36, 0x62, 0x76, 0x39, 0x59, 0x28, 0xb5, 0xaf, 0x2a, 0x98, 0xfe, 0x1b, 0x0d, 0xe6, 0x0d, 0x52, 0x61, 0x24, 0x38, 0x6c, 0xfc, 0xcd, 0x88, 0x83, 0xa3, 0xe0, 0xf5, 0x44, 0xa0, 0xbe, 0x08, 0xd7, 0x92, 0xa5, 0x91, 0xde, 0xb1, 0xf1, 0xc5, 0x14, 0xe4, 0xe3, 0x95, 0xad, 0xdd, 0x1d, 0xf4, 0x2b, 0x0d, 0x0a, 0x69, 0x4d, 0x7b, 0xf4, 0x6e, 0xca, 0x5f, 0x67, 0x99, 0x7f, 0xf9, 0x17, 0xef, 0x9c, 0x13, 0xa5, 0xfc, 0xf6, 0x17, 0x1a, 0xcc, 0x26, 0x37, 0x30, 0xd1, 0x05, 0xda, 0xcd, 0xc5, 0xcd, 0x73, 0x61, 0x94, 0x0c, 0xbf, 0xd7, 0x60, 0x3e, 0xa3, 0x89, 0x8a, 0xee, 0x9d, 0x83, 0x69, 0x6b, 0xfb, 0xb7, 0x78, 0xff, 0xfc, 0x40, 0x25, 0xd2, 0x67, 0x1a, 0xcc, 0xa5, 0x74, 0xf4, 0xd1, 0x66, 0x56, 0x0f, 0x39, 0x4d, 0x31, 0xef, 0x9e, 0x0f, 0xa4, 0xc4, 0xf8, 0xb3, 0x06, 0xcb, 0xdd, 0x1a, 0xcd, 0xe8, 0x52, 0x3d, 0xed, 0xe2, 0xf7, 0x2f, 0x88, 0x56, 0x12, 0x7e, 0xa9, 0xc1, 0x8d, 0x9e, 0x5a, 0xe1, 0x68, 0xeb, 0x42, 0x1b, 0xb5, 0xd9, 0xf3, 0xe1, 0x65, 0x58, 0xb4, 0x38, 0x7c, 0x72, 0x67, 0x39, 0xc5, 0xe1, 0x33, 0xdb, 0xf6, 0x29, 0x0e, 0xdf, 0xa5, 0x75, 0xfd, 0x47, 0x0d, 0x16, 0xb3, 0xbb, 0xb0, 0xe8, 0x41, 0x0a, 0xdf, 0x1e, 0x1a, 0xdb, 0xc5, 0xf7, 0x2f, 0x84, 0x55, 0xb2, 0xfd, 0x56, 0x83, 0x62, 0x7a, 0x07, 0x13, 0xdd, 0x4d, 0xae, 0xf5, 0xba, 0xf5, 0x89, 0x8b, 0xf7, 0xce, 0x8d, 0x53, 0xf2, 0xfc, 0x5a, 0x83, 0xab, 0xa9, 0x2d, 0x43, 0x74, 0x27, 0xb3, 0xcc, 0x4f, 0x95, 0xe6, 0xee, 0x79, 0x61, 0x4a, 0x98, 0x0a, 0x8c, 0xb5, 0xb5, 0x4d, 0x50, 0x46, 0xb7, 0xa7, 0xa3, 0xc3, 0x55, 0xbc, 0xd9, 0x0b, 0xa9, 0xda, 0xc7, 0x83, 0x89, 0xce, 0xa7, 0x0a, 0xba, 0xd5, 0xe3, 0x8b, 0x46, 0xee, 0x76, 0xbe, 0xf7, 0x0f, 0xfa, 0x14, 0xa6, 0x93, 0x1e, 0x8c, 0xe8, 0x7b, 0xe7, 0x78, 0x5b, 0xca, 0x8d, 0xd7, 0xcf, 0xfd, 0x1a, 0x15, 0x21, 0x99, 0xfc, 0xf8, 0x49, 0x09, 0xc9, 0xcc, 0xf7, 0x59, 0x4a, 0x48, 0x76, 0x79, 0x5d, 0x51, 0x18, 0x6f, 0x7f, 0x5d, 0xa0, 0x9b, 0x69, 0x07, 0x39, 0xfb, 0x38, 0x29, 0xbe, 0xd3, 0x13, 0x6d, 0xcb, 0x75, 0x97, 0x51, 0x52, 0xa6, 0x5c, 0x77, 0xdd, 0x9f, 0x1d, 0x29, 0xd7, 0x5d, 0x2f, 0xd5, 0xeb, 0xa7, 0x30, 0x9d, 0x54, 0xbf, 0xa4, 0x98, 0x3f, 0xa3, 0xf0, 0x4a, 0x31, 0x7f, 0x56, 0x71, 0x24, 0x23, 0x3c, 0xed, 0xcb, 0xba, 0xb4, 0x08, 0xef, 0xf2, 0xe5, 0x60, 0x5a, 0x84, 0x77, 0xfb, 0x80, 0xef, 0xa1, 0x0d, 0x73, 0x96, 0x57, 0x4b, 0x02, 0x3f, 0x9c, 0x8e, 0x51, 0x7b, 0xf2, 0xe3, 0xd4, 0x5d, 0xe6, 0x71, 0x6f, 0x57, 0xfb, 0xd9, 0x7a, 0x95, 0xf2, 0xc3, 0xf0, 0xa0, 0x64, 0x79, 0xb5, 0xb5, 0xd6, 0xef, 0x37, 0x6f, 0x53, 0xdb, 0x59, 0xab, 0x7a, 0xf2, 0xbb, 0x53, 0xf5, 0x31, 0xe7, 0xfb, 0xd8, 0xa7, 0x27, 0xeb, 0x07, 0x83, 0x62, 0x6e, 0xf3, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x51, 0x4a, 0x5f, 0xfc, 0x2a, 0x00, 0x00, }, // uber/cadence/api/v1/service_worker.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0x5b, 0x6f, 0xdb, 0xc8, 0xf5, 0x07, 0x7d, 0xf7, 0xf1, 0x4d, 0x1e, 0xe3, 0xef, 0xbf, 0xac, 0xdc, 0x1c, 0xa5, 0x49, 0xbc, 0x6d, 0x2a, 0x37, 0xde, 0x6d, 0x36, 0x9b, 0x6c, 0x8a, 0xfa, 0x12, 0x23, 0x2e, 0x36, 0x5b, 0x2f, 0xa3, 0x4d, 0x80, 0x2d, 0x10, 0x62, 0x44, 0x8e, 0xac, 0x81, 0x29, 0x8e, 0x42, 0x0e, 0xa5, 0xe8, 0xa5, 0x0f, 0x7d, 0xdc, 0xf6, 0xa1, 0x40, 0xd1, 0xed, 0x4b, 0x81, 0x3c, 0xf7, 0x03, 0xf4, 0xcb, 0xb4, 0x4f, 0x7d, 0xee, 0x67, 0x28, 0x50, 0x70, 0x66, 0x48, 0x51, 0xd2, 0x90, 0xba, 0xb4, 0x80, 0x17, 0xe8, 0x9b, 0x39, 0xfc, 0x9d, 0xc3, 0x33, 0xe7, 0x77, 0xe6, 0xcc, 0x6f, 0xc6, 0x82, 0xbd, 0xb0, 0x46, 0xfc, 0x7d, 0x1b, 0x3b, 0xc4, 0xb3, 0xc9, 0x3e, 0x6e, 0xd1, 0xfd, 0xf6, 0xc3, 0xfd, 0x80, 0xf8, 0x6d, 0x6a, 0x13, 0xab, 0xc3, 0xfc, 0x4b, 0xe2, 0x57, 0x5a, 0x3e, 0xe3, 0x0c, 0x6d, 0x45, 0xc8, 0x8a, 0x42, 0x56, 0x70, 0x8b, 0x56, 0xda, 0x0f, 0x4b, 0x37, 0x2f, 0x18, 0xbb, 0x70, 0xc9, 0xbe, 0x80, 0xd4, 0xc2, 0xfa, 0xbe, 0x13, 0xfa, 0x98, 0x53, 0xe6, 0x49, 0xa3, 0xd2, 0xad, 0xc1, 0xf7, 0x9c, 0x36, 0x49, 0xc0, 0x71, 0xb3, 0xa5, 0x00, 0x43, 0x0e, 0x3a, 0x3e, 0x6e, 0xb5, 0x88, 0x1f, 0xa8, 0xf7, 0xbb, 0xba, 0xf8, 0x6c, 0xd6, 0x6c, 0x26, 0x9f, 0x28, 0xeb, 0x10, 0x0e, 0xb1, 0x69, 0xd0, 0x0b, 0xe3, 0xb6, 0x0e, 0xd3, 0xa0, 0x01, 0x67, 0x7e, 0x37, 0x8e, 0x54, 0x07, 0x79, 0x17, 0x92, 0x04, 0xa0, 0xfd, 0x0e, 0xc7, 0xc1, 0xa5, 0x4b, 0x03, 0x9e, 0x87, 0x89, 0xb2, 0x58, 0x77, 0x59, 0x47, 0x62, 0xca, 0x7f, 0x35, 0xa0, 0x74, 0xce, 0x5c, 0xf7, 0x94, 0xf9, 0x27, 0x2a, 0xca, 0x2a, 0x0e, 0x2e, 0x4d, 0xf2, 0x2e, 0x24, 0x01, 0x47, 0xdb, 0xb0, 0xe0, 0xb0, 0x26, 0xa6, 0x5e, 0xd1, 0xd8, 0x35, 0xf6, 0x96, 0x4d, 0xf5, 0x84, 0x9e, 0xc0, 0x72, 0xf4, 0x31, 0x2b, 0xfa, 0x5a, 0x71, 0x66, 0xd7, 0xd8, 0x5b, 0x39, 0xb8, 0x51, 0xd1, 0x50, 0x52, 0x89, 0x9c, 0x7d, 0x41, 0x03, 0x6e, 0x2e, 0x71, 0xf5, 0x17, 0x2a, 0xc1, 0x12, 0x75, 0x88, 0xc7, 0x29, 0xef, 0x16, 0x67, 0x85, 0xd7, 0xe4, 0x19, 0xdd, 0x87, 0x8d, 0x1a, 0xf5, 0xb0, 0xdf, 0xb5, 0xec, 0x06, 0xb1, 0x2f, 0x83, 0xb0, 0x59, 0x9c, 0x13, 0x90, 0x75, 0x39, 0x7c, 0xac, 0x46, 0xcb, 0xff, 0x5a, 0x82, 0x6b, 0xda, 0xb8, 0x83, 0x16, 0xf3, 0x02, 0x82, 0x6e, 0x00, 0x88, 0x00, 0x39, 0xbb, 0x24, 0x32, 0xf8, 0x55, 0x53, 0x84, 0x5c, 0x8d, 0x06, 0xd0, 0xd7, 0x80, 0xe2, 0x44, 0x58, 0xe4, 0x3d, 0xb1, 0xc3, 0xa8, 0x4a, 0xd4, 0x44, 0xee, 0x69, 0x27, 0xf2, 0x46, 0xc1, 0x9f, 0xc7, 0x68, 0x73, 0xb3, 0x33, 0x38, 0x84, 0x4e, 0x61, 0x2d, 0x71, 0xcb, 0xbb, 0x2d, 0x22, 0xe6, 0xb7, 0x72, 0x70, 0x3b, 0xd7, 0x63, 0xb5, 0xdb, 0x22, 0xe6, 0x6a, 0x27, 0xf5, 0x84, 0x5e, 0xc3, 0x4e, 0xcb, 0x27, 0x6d, 0xca, 0xc2, 0xc0, 0x0a, 0x38, 0xf6, 0x39, 0x71, 0x2c, 0xd2, 0x26, 0x1e, 0xb7, 0xa8, 0x23, 0x12, 0xb2, 0x72, 0x70, 0xad, 0x22, 0x6b, 0xb5, 0x12, 0xd7, 0x6a, 0xe5, 0xcc, 0xe3, 0x8f, 0x3e, 0x79, 0x8d, 0xdd, 0x90, 0x98, 0xdb, 0xb1, 0xf5, 0x2b, 0x69, 0xfc, 0x3c, 0xb2, 0x3d, 0x73, 0xd0, 0x1e, 0x14, 0x86, 0xdc, 0xcd, 0xef, 0x1a, 0x7b, 0xb3, 0xe6, 0x7a, 0xd0, 0x8f, 0x2c, 0xc2, 0x22, 0xe6, 0x9c, 0x34, 0x5b, 0xbc, 0xb8, 0x20, 0x00, 0xf1, 0x23, 0x7a, 0x00, 0xa8, 0x86, 0xed, 0x4b, 0x97, 0x5d, 0x58, 0x36, 0x0b, 0x3d, 0x6e, 0x35, 0xa8, 0xc7, 0x8b, 0x8b, 0x02, 0x54, 0x50, 0x6f, 0x8e, 0xa3, 0x17, 0x2f, 0xa8, 0xc7, 0xd1, 0x23, 0x58, 0x54, 0x95, 0x5d, 0x5c, 0x12, 0x71, 0x5f, 0xd7, 0xe6, 0xe2, 0x85, 0xc4, 0x98, 0x31, 0x18, 0xdd, 0x83, 0x0d, 0x8f, 0xbc, 0xe7, 0x56, 0x0b, 0x5f, 0x10, 0x45, 0xe2, 0xb2, 0x20, 0x71, 0x2d, 0x1a, 0x3e, 0xc7, 0x17, 0x44, 0x12, 0xf9, 0x18, 0xe6, 0xc5, 0xb2, 0x28, 0x82, 0xf0, 0x5e, 0xce, 0xcd, 0xf4, 0x57, 0x11, 0xd2, 0x94, 0x06, 0xe8, 0x2d, 0x5c, 0x1f, 0x2e, 0x01, 0xab, 0x57, 0xd5, 0x2b, 0xe3, 0x54, 0xf5, 0xce, 0x50, 0x0d, 0xc4, 0xaf, 0xd0, 0x21, 0xac, 0x07, 0x76, 0x83, 0x38, 0xa1, 0x4b, 0x1c, 0x2b, 0x6a, 0x34, 0xc5, 0x55, 0xe1, 0xb1, 0x34, 0x44, 0x5c, 0x35, 0xee, 0x42, 0xe6, 0x5a, 0x62, 0x11, 0x8d, 0xa1, 0x67, 0xb0, 0x1a, 0xd3, 0x25, 0x1c, 0xac, 0x8d, 0x74, 0xb0, 0xa2, 0xf0, 0xc2, 0xfc, 0x0d, 0x2c, 0x46, 0x53, 0xa5, 0x24, 0x28, 0xae, 0xef, 0xce, 0xee, 0xad, 0x1c, 0x3c, 0xd3, 0x4e, 0x26, 0x67, 0x19, 0x55, 0xbe, 0x92, 0xf6, 0xcf, 0x3d, 0x1e, 0x91, 0xa3, 0xbc, 0xa1, 0x32, 0x08, 0x16, 0x7a, 0x35, 0xb4, 0x21, 0xd8, 0x5f, 0x89, 0x06, 0xe3, 0x02, 0xaa, 0xc0, 0x16, 0x67, 0x1c, 0xbb, 0x96, 0x62, 0xd4, 0xaa, 0x75, 0x39, 0x09, 0x8a, 0x05, 0x81, 0xdc, 0x14, 0xaf, 0x14, 0xe9, 0x47, 0xd1, 0x0b, 0xf4, 0x12, 0x0a, 0x38, 0xe4, 0xcc, 0xb2, 0x99, 0x57, 0xa7, 0x17, 0xb2, 0xa8, 0x36, 0xc5, 0x7c, 0xef, 0x68, 0xa3, 0x3e, 0x0c, 0x39, 0x3b, 0x16, 0xd8, 0xa8, 0xce, 0xcc, 0x75, 0xdc, 0xf7, 0x5c, 0x7a, 0x0b, 0xab, 0xe9, 0xd8, 0x51, 0x01, 0x66, 0x2f, 0x49, 0x57, 0x75, 0xb1, 0xe8, 0xcf, 0xa8, 0x72, 0xda, 0xd1, 0x62, 0x51, 0xab, 0x7e, 0xac, 0xca, 0x11, 0x06, 0x4f, 0x66, 0x1e, 0x1b, 0xe5, 0xbf, 0xcc, 0xc3, 0x1d, 0x99, 0x25, 0x27, 0x9d, 0xb8, 0x63, 0xd6, 0x6c, 0xb9, 0x84, 0x13, 0x27, 0x6e, 0xa0, 0x23, 0xfa, 0xd0, 0x53, 0x58, 0x8e, 0x37, 0x87, 0xa0, 0x38, 0x23, 0x48, 0xd2, 0x57, 0x5c, 0xfc, 0x11, 0xb3, 0x87, 0x47, 0x3f, 0x82, 0xcd, 0x5e, 0xe1, 0xda, 0xcc, 0xe3, 0xe4, 0x3d, 0x17, 0x1d, 0x67, 0xd5, 0x2c, 0x24, 0x2f, 0x8e, 0xe5, 0x78, 0x5f, 0xd7, 0x9d, 0x1b, 0xe8, 0xba, 0xbf, 0x82, 0xcd, 0x80, 0x53, 0xfb, 0xb2, 0x6b, 0x61, 0xce, 0x7d, 0x5a, 0x0b, 0x23, 0xa6, 0xe6, 0x45, 0x5a, 0x2a, 0xda, 0x68, 0x5e, 0x09, 0x74, 0x52, 0xf3, 0x87, 0x89, 0x95, 0x59, 0x90, 0x8e, 0x7a, 0x23, 0xe8, 0x53, 0x28, 0xfa, 0x84, 0x87, 0xbe, 0x67, 0x79, 0xa4, 0x63, 0xc5, 0xd1, 0x8b, 0x85, 0x26, 0x5a, 0xcb, 0x92, 0xf9, 0x7f, 0xf2, 0xfd, 0x97, 0xa4, 0x93, 0x4e, 0x25, 0x3a, 0x82, 0x9b, 0x75, 0xe6, 0xdb, 0xc4, 0xb2, 0x7d, 0x82, 0x39, 0xd1, 0x98, 0x2f, 0x0a, 0xf3, 0x92, 0x40, 0x1d, 0x0b, 0xd0, 0xa0, 0x0f, 0xcd, 0x7e, 0xb2, 0xa4, 0xdb, 0x4f, 0x10, 0x83, 0x35, 0xd1, 0x16, 0x2c, 0x9f, 0x04, 0xa1, 0xcb, 0x83, 0xe2, 0xb2, 0x20, 0xe3, 0x17, 0xda, 0xe9, 0x8f, 0x41, 0x7c, 0x45, 0x56, 0x8c, 0x74, 0x26, 0x97, 0xcf, 0xea, 0xbb, 0xd4, 0x50, 0x89, 0xc2, 0xe6, 0x10, 0x44, 0x53, 0xa5, 0x3f, 0xeb, 0xaf, 0xd2, 0xbd, 0x31, 0xaa, 0x54, 0x38, 0x4c, 0xd7, 0xea, 0x87, 0x59, 0xf8, 0x41, 0x7e, 0xc8, 0x6a, 0xd3, 0xfc, 0x1a, 0xd6, 0xfa, 0x13, 0x6c, 0x88, 0x8f, 0xfe, 0x64, 0xd2, 0xb6, 0x61, 0xae, 0x3a, 0x69, 0x12, 0x3e, 0x18, 0x70, 0x13, 0xdb, 0x9c, 0xb6, 0x29, 0xa7, 0x24, 0xb0, 0x38, 0xb3, 0x1c, 0x1a, 0xb4, 0x30, 0xb7, 0x1b, 0x96, 0xcb, 0x6c, 0xec, 0xba, 0x5d, 0x55, 0xfa, 0xdf, 0x4c, 0x91, 0x6d, 0xd5, 0xa8, 0x0e, 0x13, 0xff, 0x55, 0x76, 0xa2, 0xbc, 0x7f, 0x21, 0x9d, 0xcb, 0xec, 0x5f, 0xc3, 0xd9, 0x88, 0xd2, 0xaf, 0x61, 0x77, 0x94, 0x03, 0x0d, 0x37, 0x27, 0xfd, 0xdc, 0xe8, 0x97, 0x8a, 0xf2, 0xdb, 0x15, 0xbe, 0x62, 0xc7, 0x67, 0x5e, 0x9d, 0xa5, 0x19, 0xfa, 0xcd, 0x0c, 0xec, 0x6a, 0xa6, 0x79, 0x8a, 0xa9, 0x3b, 0x76, 0x2b, 0x39, 0x82, 0x79, 0x1b, 0x87, 0x81, 0x8c, 0x66, 0xfd, 0xe0, 0x41, 0x6e, 0x1b, 0xe9, 0x79, 0x3f, 0x8e, 0x6c, 0x4c, 0x69, 0x1a, 0xed, 0xd6, 0x0e, 0xe1, 0x98, 0xba, 0x81, 0x52, 0x2e, 0xfa, 0xdd, 0xfa, 0x1c, 0x77, 0x5d, 0x86, 0x1d, 0x33, 0x06, 0xe7, 0x36, 0x17, 0xcd, 0x12, 0x9c, 0xd7, 0x4a, 0xba, 0x3b, 0x70, 0x3b, 0x27, 0x07, 0x92, 0xe7, 0xf2, 0x3f, 0x7a, 0x7a, 0x35, 0xce, 0xec, 0x55, 0xea, 0xd5, 0x57, 0x80, 0x12, 0xbf, 0x56, 0x93, 0x70, 0xec, 0x60, 0x8e, 0x95, 0x42, 0xbb, 0x9b, 0xfb, 0x81, 0x97, 0x0a, 0x6c, 0x16, 0xf8, 0xc0, 0x48, 0xf9, 0x6f, 0x3d, 0x6d, 0xdb, 0x3f, 0xc7, 0x2b, 0xd5, 0xb6, 0xb7, 0x60, 0x45, 0x2d, 0xa1, 0x6e, 0xb4, 0xe5, 0xcb, 0x4c, 0x40, 0x3c, 0x74, 0xe6, 0x44, 0xe2, 0x37, 0x01, 0x08, 0xf1, 0x3b, 0x97, 0x23, 0x7e, 0x93, 0x89, 0x09, 0xf1, 0x8b, 0x53, 0x4f, 0xe8, 0x00, 0xe6, 0xa9, 0xd7, 0x0a, 0xb9, 0xda, 0x81, 0xf2, 0x4b, 0x50, 0x42, 0x35, 0x62, 0x6b, 0xe1, 0x3f, 0x15, 0x5b, 0x8b, 0x93, 0x89, 0xad, 0x2a, 0xec, 0xc4, 0xfe, 0xa2, 0x0e, 0x67, 0xbb, 0x2c, 0x20, 0xc2, 0x11, 0x0b, 0xb9, 0x92, 0xbe, 0x3b, 0x43, 0xbe, 0x4e, 0xd4, 0xf9, 0xd4, 0xdc, 0x8e, 0x6d, 0xab, 0xec, 0x38, 0xb2, 0xac, 0x4a, 0x43, 0xf4, 0x25, 0x6c, 0x8b, 0x8f, 0x0c, 0xbb, 0x5c, 0x1e, 0xe5, 0x72, 0x4b, 0x18, 0x0e, 0xf8, 0x3b, 0x85, 0xcd, 0x06, 0xc1, 0x3e, 0xaf, 0x11, 0xcc, 0x13, 0x57, 0x30, 0xca, 0x55, 0x21, 0xb1, 0x89, 0xfd, 0xa4, 0x8e, 0x07, 0x91, 0x4e, 0x9e, 0xef, 0x1d, 0x0f, 0xde, 0xc2, 0xcd, 0x7e, 0x26, 0x2c, 0x56, 0xb7, 0x78, 0x83, 0x06, 0x56, 0x6c, 0x30, 0x5a, 0x06, 0x97, 0xfa, 0x98, 0xf9, 0x65, 0xbd, 0xda, 0xa0, 0xc1, 0xa1, 0xf2, 0x7f, 0x96, 0x9e, 0x41, 0xdc, 0xac, 0xd6, 0xc6, 0xa8, 0x94, 0xde, 0x24, 0x4e, 0x54, 0xd7, 0x1a, 0x3a, 0xad, 0xad, 0x4f, 0x77, 0x5a, 0xbb, 0x0f, 0x1b, 0x89, 0x1f, 0xd5, 0x7d, 0x36, 0x64, 0x87, 0x8b, 0x87, 0x4f, 0x64, 0x17, 0xfa, 0x18, 0x16, 0x1a, 0x04, 0x3b, 0xc4, 0x17, 0x32, 0x38, 0x3a, 0xc3, 0x69, 0xcf, 0x42, 0x02, 0x62, 0x2a, 0xe8, 0x7f, 0x59, 0x18, 0x97, 0xbf, 0x33, 0x12, 0xe1, 0x9a, 0x6e, 0x2e, 0x93, 0x0a, 0xd7, 0x4f, 0x60, 0x41, 0x2a, 0x25, 0xd5, 0x58, 0xf2, 0x73, 0xaf, 0xb0, 0x79, 0xad, 0xb4, 0x7c, 0x2f, 0x11, 0x29, 0x19, 0x71, 0xa9, 0x1d, 0xe0, 0xb7, 0x33, 0x70, 0x3f, 0x0f, 0x78, 0xd4, 0x3d, 0x3b, 0x19, 0xb5, 0x1d, 0x5c, 0x55, 0x8b, 0xec, 0x65, 0x6d, 0x6e, 0xca, 0xac, 0xcd, 0x0f, 0x64, 0xed, 0x87, 0xb0, 0x37, 0x3a, 0x19, 0x2a, 0x73, 0x7f, 0x34, 0x12, 0x95, 0x91, 0x06, 0x4f, 0xa4, 0x32, 0x1e, 0xc1, 0x62, 0x1d, 0x53, 0x37, 0xf4, 0x49, 0x2e, 0xf1, 0xa7, 0x12, 0x63, 0xc6, 0xe0, 0x5c, 0xe6, 0x7b, 0x1b, 0xbf, 0x2e, 0x2c, 0x15, 0xfc, 0xb7, 0x33, 0xda, 0xfa, 0x90, 0xa8, 0xef, 0x33, 0xe7, 0xa9, 0x8c, 0xcd, 0x4d, 0x9b, 0xb1, 0x41, 0xd6, 0xef, 0xc3, 0xdd, 0x11, 0xb9, 0x50, 0x59, 0xfb, 0x93, 0x01, 0x65, 0x5d, 0x7d, 0x60, 0xcf, 0x26, 0x13, 0x91, 0x1e, 0x77, 0xda, 0x99, 0x69, 0x65, 0xe1, 0x20, 0xe9, 0x77, 0xf5, 0x6d, 0x28, 0x09, 0x4c, 0x4d, 0xe0, 0x77, 0x33, 0x70, 0x2f, 0x07, 0xf7, 0x3d, 0x27, 0x3e, 0xce, 0xda, 0xdc, 0xb4, 0x59, 0x1b, 0x24, 0xfe, 0x23, 0x7d, 0xef, 0xeb, 0xcb, 0x46, 0x1f, 0xf5, 0x36, 0xf3, 0xfb, 0xa0, 0x2f, 0xe2, 0x4d, 0xf0, 0x0a, 0xa9, 0x3f, 0x8f, 0xa8, 0xcf, 0x09, 0x4c, 0xc9, 0xdc, 0x8f, 0xa0, 0x60, 0x8b, 0x89, 0x59, 0xbe, 0x8c, 0x95, 0x38, 0x22, 0xbe, 0x25, 0x73, 0x43, 0x8e, 0x9b, 0xf1, 0xb0, 0xaa, 0x92, 0x4c, 0x97, 0xff, 0x6b, 0x55, 0x52, 0x8d, 0xaa, 0x64, 0x44, 0x36, 0x26, 0x4f, 0xf2, 0xdf, 0x7b, 0xdb, 0x87, 0xb8, 0x68, 0x98, 0x46, 0x36, 0xfc, 0x7c, 0x40, 0x36, 0x8c, 0x7f, 0x9f, 0x11, 0x6f, 0x86, 0xaf, 0x61, 0x4b, 0xfe, 0x23, 0xc8, 0x6a, 0x13, 0x5f, 0xdc, 0x54, 0x50, 0xaf, 0xce, 0xd4, 0x71, 0x35, 0x9b, 0x28, 0xe2, 0xbf, 0x96, 0x70, 0x71, 0xf4, 0xde, 0xec, 0x0c, 0x0e, 0xa5, 0x36, 0x21, 0xdd, 0xe4, 0x62, 0xed, 0x61, 0x40, 0xc9, 0x24, 0x01, 0xe1, 0xf2, 0x02, 0x2c, 0x39, 0x2c, 0x5e, 0x49, 0x6d, 0x95, 0x6f, 0xc0, 0x35, 0x6d, 0x30, 0x2a, 0x58, 0x1f, 0xd6, 0xfb, 0xb5, 0x20, 0x7a, 0x00, 0x88, 0x78, 0xb8, 0xe6, 0x12, 0x2b, 0xa5, 0x28, 0x15, 0xdd, 0x05, 0xf9, 0xa6, 0x67, 0x81, 0x0e, 0x60, 0xbb, 0xc5, 0x5c, 0x97, 0xf8, 0x56, 0x07, 0x53, 0x79, 0x5a, 0xb0, 0xa8, 0x67, 0x35, 0x65, 0x27, 0x98, 0x35, 0x91, 0x7c, 0xfb, 0x06, 0x53, 0x71, 0x2c, 0x38, 0xf3, 0x5e, 0x06, 0x07, 0xff, 0xdc, 0x80, 0x65, 0x99, 0xee, 0xc3, 0xf3, 0x33, 0xf4, 0x1e, 0xb6, 0x34, 0xb7, 0x44, 0x68, 0x7f, 0xfc, 0xfb, 0x24, 0x91, 0xd7, 0xd2, 0xc4, 0x17, 0x50, 0xe8, 0x0f, 0x06, 0x5c, 0xcf, 0xbb, 0x37, 0x42, 0x8f, 0xa7, 0xbd, 0xd8, 0x2b, 0x7d, 0x36, 0xf5, 0x25, 0x15, 0xfa, 0xd6, 0x80, 0x9d, 0xcc, 0x2b, 0x0e, 0xf4, 0xd3, 0x71, 0x1d, 0xf7, 0x09, 0xb6, 0xd2, 0xa3, 0x49, 0xcd, 0x54, 0x30, 0x3d, 0x72, 0xd2, 0x5d, 0x22, 0x9f, 0x1c, 0xcd, 0x95, 0x4b, 0x3e, 0x39, 0xda, 0xfb, 0x8b, 0x14, 0x39, 0x5a, 0xd1, 0x9a, 0x4f, 0x4e, 0xde, 0xa9, 0x25, 0x9f, 0x9c, 0xdc, 0x73, 0x05, 0xfa, 0xa0, 0x57, 0xc7, 0x7d, 0x52, 0x1a, 0x7d, 0x3e, 0xb1, 0xff, 0xd4, 0xde, 0x53, 0x7a, 0x36, 0xa5, 0xf5, 0x70, 0xf9, 0x0c, 0xcb, 0xbe, 0xfc, 0xf2, 0xc9, 0xd4, 0xfb, 0xf9, 0xe5, 0x93, 0xad, 0xc7, 0xd1, 0x77, 0x06, 0xdc, 0xc8, 0xd5, 0xa0, 0xe8, 0xb3, 0xc9, 0x3c, 0xa7, 0x13, 0xf5, 0x64, 0x1a, 0x53, 0x15, 0xd8, 0xef, 0x0d, 0xd1, 0x16, 0xb3, 0x34, 0x12, 0xfa, 0x74, 0x6c, 0x12, 0xfa, 0x45, 0x72, 0xe9, 0xf1, 0xe4, 0x86, 0x2a, 0xa4, 0x3f, 0x1b, 0x70, 0x6b, 0x84, 0x6c, 0x43, 0x4f, 0x27, 0xf5, 0x9e, 0xce, 0xd7, 0xe7, 0xd3, 0x19, 0xf7, 0x65, 0x2c, 0x53, 0x2f, 0x64, 0x66, 0x6c, 0x94, 0xb6, 0xcc, 0xcc, 0xd8, 0x68, 0xed, 0x27, 0x33, 0x96, 0x2b, 0x61, 0x32, 0x33, 0x36, 0x8e, 0x0c, 0xcc, 0xcc, 0xd8, 0x78, 0xaa, 0x29, 0xb5, 0x12, 0x87, 0xd5, 0x42, 0xfe, 0x4a, 0xcc, 0x94, 0x4e, 0xf9, 0x2b, 0x31, 0x5b, 0x94, 0x44, 0x8d, 0x5c, 0x23, 0x03, 0x32, 0x1a, 0x79, 0xb6, 0x7a, 0xc9, 0x68, 0xe4, 0x39, 0x0a, 0xe3, 0xa8, 0x06, 0xff, 0x6f, 0xb3, 0xa6, 0xce, 0xec, 0x08, 0x49, 0x15, 0xf0, 0x4a, 0xfe, 0x76, 0xe7, 0xdc, 0x67, 0x9c, 0x9d, 0x1b, 0xdf, 0x3c, 0xbc, 0xa0, 0xbc, 0x11, 0xd6, 0x2a, 0x36, 0x6b, 0xee, 0xa7, 0x7f, 0x9c, 0xf2, 0x63, 0xea, 0xb8, 0xfb, 0x17, 0x4c, 0xfe, 0xee, 0x46, 0xfd, 0x52, 0xe5, 0x29, 0x6e, 0xd1, 0xf6, 0xc3, 0xda, 0x82, 0x18, 0xfb, 0xf8, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x83, 0x33, 0x80, 0xed, 0x1b, 0x24, 0x00, 0x00, }, // uber/cadence/api/v1/decision.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x59, 0xdf, 0x6e, 0xd4, 0xc6, 0x17, 0xfe, 0x39, 0x7f, 0x36, 0xbb, 0x27, 0x1b, 0x20, 0x13, 0x08, 0x09, 0x04, 0x12, 0xf6, 0xa7, 0x42, 0x21, 0xca, 0x6e, 0x12, 0x28, 0x42, 0x80, 0x50, 0xc9, 0x42, 0x44, 0x24, 0x08, 0x91, 0x13, 0x40, 0xaa, 0x54, 0x59, 0x93, 0xf1, 0x24, 0x99, 0xc6, 0xeb, 0xd9, 0x8e, 0xc7, 0x09, 0x5b, 0xa9, 0x12, 0x57, 0x6d, 0x6f, 0xfa, 0x00, 0x95, 0x7a, 0xd5, 0x9b, 0xb6, 0x37, 0xed, 0x6d, 0xab, 0x5e, 0xf5, 0x11, 0xfa, 0x2c, 0x7d, 0x81, 0xca, 0xe3, 0xb1, 0x77, 0xb3, 0xf1, 0x7a, 0xed, 0x40, 0xb9, 0xe8, 0x5d, 0x3c, 0x3e, 0xe7, 0x9b, 0xcf, 0x73, 0xce, 0x7e, 0xe7, 0xb3, 0x03, 0x15, 0x7f, 0x9b, 0x8a, 0x1a, 0xc1, 0x36, 0x75, 0x09, 0xad, 0xe1, 0x26, 0xab, 0x1d, 0x2c, 0xd5, 0x6c, 0x4a, 0x98, 0xc7, 0xb8, 0x5b, 0x6d, 0x0a, 0x2e, 0x39, 0x9a, 0x08, 0x62, 0xaa, 0x3a, 0xa6, 0x8a, 0x9b, 0xac, 0x7a, 0xb0, 0x74, 0xe1, 0xf2, 0x2e, 0xe7, 0xbb, 0x0e, 0xad, 0xa9, 0x90, 0x6d, 0x7f, 0xa7, 0x66, 0xfb, 0x02, 0xcb, 0x38, 0xe9, 0xc2, 0x5c, 0x12, 0x30, 0xe1, 0x8d, 0x46, 0x1c, 0x91, 0xb8, 0xb5, 0xc4, 0xde, 0xbe, 0xc3, 0x3c, 0x99, 0x16, 0x73, 0xc8, 0xc5, 0xfe, 0x8e, 0xc3, 0x0f, 0xc3, 0x98, 0xca, 0x37, 0xe3, 0x50, 0x7c, 0xa4, 0x19, 0xa3, 0xef, 0x0c, 0xb8, 0xe1, 0x91, 0x3d, 0x6a, 0xfb, 0x0e, 0xb5, 0x30, 0x91, 0xec, 0x80, 0xc9, 0x96, 0x15, 0xa0, 0x5a, 0xd1, 0x53, 0x59, 0x58, 0x4a, 0xc1, 0xb6, 0x7d, 0x49, 0xbd, 0x29, 0x63, 0xce, 0xf8, 0x70, 0x74, 0xf9, 0x5e, 0x35, 0xe1, 0x09, 0xab, 0x9b, 0x1a, 0xe6, 0xa1, 0x46, 0xd9, 0xc2, 0xde, 0x7e, 0xb4, 0xcf, 0xc3, 0x18, 0xe2, 0xc9, 0xff, 0xcc, 0xab, 0x5e, 0xa6, 0x48, 0xf4, 0x05, 0xcc, 0x7a, 0x12, 0x0b, 0x69, 0x49, 0xd6, 0xa0, 0x22, 0x91, 0xcf, 0x80, 0xe2, 0xb3, 0x94, 0xcc, 0x27, 0xc8, 0xdd, 0x0a, 0x52, 0x13, 0x59, 0xcc, 0x78, 0x29, 0xf7, 0xd1, 0x4f, 0x06, 0x04, 0xa7, 0xdf, 0x74, 0xa8, 0xa4, 0x56, 0x74, 0x80, 0x16, 0x7d, 0x4d, 0x89, 0x1f, 0x14, 0x2d, 0x91, 0xcc, 0xa0, 0x22, 0xf3, 0x71, 0x22, 0x99, 0xba, 0xc6, 0x7a, 0xa5, 0xa1, 0x1e, 0x47, 0x48, 0x89, 0xdc, 0xe6, 0x49, 0xf6, 0x70, 0xf4, 0xbd, 0x01, 0xf3, 0x3b, 0x98, 0x39, 0x59, 0x69, 0x0e, 0x29, 0x9a, 0xf7, 0x13, 0x69, 0xae, 0x62, 0xe6, 0x64, 0xa3, 0x78, 0x6d, 0x27, 0x5b, 0x28, 0xfa, 0xd9, 0x80, 0x45, 0x41, 0x3f, 0xf7, 0xa9, 0x27, 0x2d, 0x82, 0x5d, 0x42, 0x9d, 0x0c, 0x7d, 0x36, 0x9c, 0x72, 0x94, 0x66, 0x08, 0x56, 0x57, 0x58, 0x7d, 0x9b, 0x6d, 0x5e, 0x64, 0x0f, 0x47, 0x5f, 0xc2, 0x9c, 0xa6, 0xd8, 0xbb, 0xe5, 0x0a, 0x8a, 0xda, 0x72, 0x72, 0x95, 0x55, 0x72, 0xef, 0x9e, 0xbb, 0x44, 0xd2, 0x02, 0xd0, 0x0f, 0x06, 0x2c, 0xe8, 0xfd, 0x33, 0xd6, 0x72, 0x44, 0x91, 0x79, 0x90, 0x42, 0x26, 0x5b, 0x35, 0xaf, 0x93, 0xac, 0xc1, 0xe8, 0x2f, 0x03, 0x1e, 0x74, 0xd5, 0x93, 0xbe, 0x96, 0x54, 0xb8, 0x38, 0x33, 0xeb, 0xa2, 0x62, 0xfd, 0xac, 0x7f, 0x75, 0x1f, 0x6b, 0xe0, 0x6c, 0x0f, 0x71, 0x47, 0x9c, 0x30, 0x17, 0xbd, 0x31, 0xe0, 0x8a, 0xa0, 0x84, 0x0b, 0xdb, 0x6a, 0x60, 0xb1, 0xdf, 0xa3, 0xf2, 0x25, 0x45, 0xfb, 0x66, 0x0f, 0xda, 0x41, 0xf6, 0x33, 0x95, 0x9c, 0x48, 0xee, 0xb2, 0x48, 0x8d, 0x40, 0xbf, 0x1b, 0x70, 0x9b, 0x70, 0x57, 0x32, 0xd7, 0xa7, 0x16, 0xf6, 0x2c, 0x97, 0x1e, 0x66, 0x3d, 0x4e, 0x50, 0xbc, 0x1e, 0xf7, 0xd0, 0x9d, 0x10, 0xf2, 0xa1, 0xb7, 0x4e, 0x0f, 0xb3, 0x1d, 0xe3, 0x22, 0xc9, 0x99, 0x83, 0x7e, 0x35, 0x60, 0x39, 0x54, 0x6a, 0xb2, 0xc7, 0x1c, 0x3b, 0x2b, 0xef, 0x51, 0xc5, 0x7b, 0xa5, 0xb7, 0x78, 0xd7, 0x03, 0xb4, 0x6c, 0xa4, 0x17, 0xbc, 0x3c, 0x09, 0xe8, 0x0f, 0x03, 0x6e, 0x7b, 0x6c, 0x37, 0xe8, 0xd9, 0xbc, 0xcd, 0x5b, 0x56, 0xac, 0x57, 0x93, 0x59, 0x2b, 0xc8, 0x7c, 0x5d, 0xbb, 0xe4, 0xe5, 0x4d, 0x42, 0xbf, 0x19, 0xf0, 0x91, 0xdf, 0xf4, 0xa8, 0x90, 0x6d, 0xd2, 0x1e, 0xc5, 0x82, 0xec, 0x75, 0x10, 0x4d, 0x24, 0x3f, 0x96, 0xd2, 0x2a, 0x2f, 0x14, 0x62, 0xb4, 0xff, 0xa6, 0xc2, 0x6b, 0x6f, 0x9a, 0xdc, 0x2a, 0x7e, 0xce, 0x9c, 0x95, 0x32, 0x40, 0x9b, 0x4e, 0xe5, 0xdb, 0x02, 0x5c, 0xcd, 0x66, 0x1b, 0xd0, 0x2c, 0x8c, 0xc6, 0x63, 0x83, 0xd9, 0xca, 0x88, 0x94, 0x4c, 0x88, 0x96, 0xd6, 0x6c, 0xb4, 0x0a, 0x63, 0xed, 0xb9, 0xd2, 0x6a, 0x52, 0xed, 0x0d, 0xae, 0x24, 0x3e, 0x6b, 0xbc, 0x59, 0xab, 0x49, 0xcd, 0x32, 0xee, 0xb8, 0x42, 0x93, 0x50, 0xb0, 0x79, 0x03, 0x33, 0x57, 0xcd, 0xf3, 0x92, 0xa9, 0xaf, 0xd0, 0x5d, 0x28, 0xa9, 0x71, 0x15, 0xb8, 0x2d, 0x3d, 0x43, 0x2f, 0x25, 0x62, 0x07, 0x0f, 0xf0, 0x94, 0x79, 0xd2, 0x2c, 0x4a, 0xfd, 0x17, 0x5a, 0x86, 0x61, 0xe6, 0x36, 0x7d, 0xa9, 0xe7, 0xda, 0x4c, 0x62, 0xde, 0x06, 0x6e, 0x39, 0x1c, 0xdb, 0x66, 0x18, 0x8a, 0xb6, 0x60, 0x3a, 0x36, 0x66, 0x92, 0x5b, 0xc4, 0xe1, 0x1e, 0x55, 0x63, 0x89, 0xfb, 0x52, 0x0f, 0xa1, 0xe9, 0x6a, 0x68, 0x2a, 0xab, 0x91, 0xa9, 0xac, 0x3e, 0xd2, 0xa6, 0xd2, 0x9c, 0x8c, 0x72, 0xb7, 0x78, 0x3d, 0xc8, 0xdc, 0x0a, 0x13, 0xbb, 0x51, 0xdb, 0xfe, 0x2a, 0x40, 0x1d, 0xc9, 0x81, 0x1a, 0xbb, 0xab, 0x00, 0x75, 0x1d, 0x26, 0x35, 0x52, 0x37, 0xd1, 0x62, 0x3f, 0xc8, 0x89, 0xd0, 0x86, 0x1d, 0x65, 0xb9, 0x0a, 0xe3, 0x7b, 0x14, 0x0b, 0xb9, 0x4d, 0x71, 0x9b, 0x5d, 0xa9, 0x1f, 0xd4, 0x99, 0x38, 0x27, 0xc2, 0xa9, 0x43, 0x59, 0x50, 0x29, 0x5a, 0x56, 0x93, 0x3b, 0x8c, 0xb4, 0xb4, 0xe2, 0xcc, 0xf5, 0x50, 0x70, 0x29, 0x5a, 0x1b, 0x2a, 0xce, 0x1c, 0x15, 0xed, 0x0b, 0x74, 0x13, 0x0a, 0x7b, 0x14, 0xdb, 0x54, 0xe8, 0x9f, 0xfe, 0xc5, 0xc4, 0xf4, 0x27, 0x2a, 0xc4, 0xd4, 0xa1, 0xe8, 0x16, 0x4c, 0x46, 0x43, 0xd2, 0xe1, 0x04, 0x3b, 0x96, 0xcd, 0xbc, 0x26, 0x96, 0x64, 0x4f, 0xfd, 0x04, 0x8b, 0xe6, 0x59, 0x7d, 0xf7, 0x69, 0x70, 0xf3, 0x91, 0xbe, 0x57, 0xf9, 0xda, 0x80, 0x99, 0x34, 0xdb, 0x8a, 0xa6, 0xa1, 0x18, 0x3a, 0x93, 0xf8, 0x27, 0x30, 0xa2, 0xae, 0xd7, 0x6c, 0xf4, 0x14, 0xce, 0xc5, 0x35, 0xd8, 0x61, 0xa2, 0x5d, 0x82, 0x81, 0x7e, 0xe7, 0x86, 0x74, 0x09, 0x56, 0x99, 0x88, 0x2a, 0x50, 0x21, 0x30, 0x9f, 0xc3, 0xb2, 0xa2, 0x5b, 0x50, 0x10, 0xd4, 0xf3, 0x1d, 0xa9, 0xdf, 0x10, 0xd2, 0x3b, 0x5c, 0xc7, 0x56, 0x30, 0x5c, 0xcb, 0x68, 0x38, 0xd1, 0x6d, 0x18, 0x09, 0x0c, 0xa7, 0x2f, 0x68, 0xea, 0x0e, 0xab, 0x61, 0x8c, 0x19, 0x05, 0x57, 0xd6, 0x61, 0x3e, 0x87, 0x5f, 0xec, 0xab, 0x32, 0x95, 0xbb, 0x70, 0x29, 0xd5, 0xe4, 0xa5, 0x54, 0xa8, 0x42, 0xe0, 0x7a, 0x66, 0x4f, 0x16, 0x3c, 0xb0, 0x4d, 0x25, 0x66, 0x8e, 0x97, 0xe9, 0x48, 0xa3, 0xe0, 0xca, 0xdf, 0x06, 0xdc, 0x39, 0xa9, 0x87, 0xea, 0xd0, 0x3e, 0xe3, 0x88, 0xf6, 0xbd, 0x00, 0x74, 0x7c, 0x3a, 0xea, 0xc6, 0xba, 0x9a, 0xc8, 0xeb, 0xd8, 0x6e, 0xe6, 0xf8, 0x61, 0xf7, 0x12, 0x9a, 0x82, 0x91, 0xc0, 0x6b, 0x08, 0xee, 0x28, 0xad, 0x2d, 0x9b, 0xd1, 0x25, 0xaa, 0xc2, 0x44, 0x97, 0x95, 0xe0, 0xae, 0xd3, 0x52, 0xb2, 0x5b, 0x34, 0xc7, 0x49, 0xe7, 0x98, 0x7f, 0xee, 0x3a, 0xad, 0xca, 0x2f, 0x06, 0x5c, 0x4e, 0xb7, 0x60, 0x41, 0x69, 0xb5, 0xb7, 0x73, 0x71, 0x83, 0x46, 0xa5, 0x0d, 0x97, 0xd6, 0x71, 0x83, 0x76, 0x9e, 0xf8, 0x40, 0x8e, 0x13, 0xef, 0xd0, 0x87, 0xc1, 0xcc, 0xfa, 0x50, 0x79, 0x03, 0xb0, 0x98, 0xd7, 0x9b, 0x05, 0x23, 0x2e, 0x3e, 0x0f, 0x35, 0xe2, 0x8c, 0x94, 0x11, 0x17, 0x01, 0x86, 0x23, 0xee, 0xb0, 0xe3, 0xea, 0xe8, 0x28, 0x1b, 0x38, 0xe1, 0x28, 0x1b, 0xcc, 0x3e, 0xca, 0x30, 0xcc, 0xb5, 0x3d, 0x55, 0x8f, 0x41, 0x31, 0xd4, 0x4f, 0xa5, 0x66, 0x62, 0x88, 0xcd, 0x84, 0x89, 0xf1, 0x0a, 0x2e, 0xaa, 0x47, 0xea, 0x81, 0x3e, 0xdc, 0x0f, 0xfd, 0x7c, 0x90, 0x9d, 0x04, 0xfc, 0x1c, 0x26, 0xb7, 0x31, 0xd9, 0xe7, 0x3b, 0x3b, 0x1a, 0x9b, 0xb9, 0x92, 0x8a, 0x03, 0xec, 0xf4, 0x9f, 0xc1, 0x67, 0x75, 0xa2, 0x82, 0x5d, 0xd3, 0x69, 0xc7, 0x66, 0xd2, 0xc8, 0x49, 0x66, 0xd2, 0x1a, 0x94, 0x98, 0xcb, 0x24, 0xc3, 0x92, 0x0b, 0x35, 0x63, 0x4f, 0x2d, 0xcf, 0xf7, 0xf7, 0xff, 0x6b, 0x51, 0x8a, 0xd9, 0xce, 0xee, 0x54, 0xd6, 0x52, 0x0e, 0x65, 0x45, 0x26, 0x4c, 0x3a, 0x38, 0x78, 0x07, 0x0c, 0xc7, 0x44, 0x50, 0x5a, 0x3d, 0x02, 0x20, 0x43, 0x67, 0x9c, 0x0d, 0x72, 0xeb, 0x71, 0xaa, 0xa9, 0x32, 0xd1, 0xff, 0x61, 0x8c, 0x88, 0xa0, 0x47, 0xb4, 0xcd, 0x50, 0x03, 0xbb, 0x64, 0x96, 0x83, 0xc5, 0xc8, 0x27, 0x9e, 0x6c, 0x1e, 0x2f, 0xc0, 0x50, 0x83, 0x36, 0xb8, 0x36, 0xc0, 0xd3, 0x89, 0x29, 0xcf, 0x68, 0x83, 0x9b, 0x2a, 0x0c, 0x99, 0x30, 0x7e, 0xcc, 0x50, 0x4f, 0x9d, 0x52, 0xb9, 0x1f, 0x24, 0x3b, 0xff, 0x2e, 0xeb, 0x6b, 0x9e, 0xf1, 0xba, 0x56, 0xd0, 0x7d, 0x28, 0x7f, 0xc6, 0xa4, 0xa4, 0x22, 0x6c, 0xa4, 0xa9, 0xd3, 0xfd, 0xfa, 0x67, 0x34, 0x0c, 0x57, 0xed, 0x83, 0x5e, 0xc2, 0x84, 0x3a, 0x1a, 0x7e, 0x40, 0x85, 0x83, 0x9b, 0x51, 0xf7, 0x9c, 0x51, 0xb5, 0x4f, 0xd6, 0xe0, 0xba, 0xe0, 0xee, 0xf3, 0x30, 0x5c, 0xf7, 0xd0, 0x38, 0xe9, 0x5e, 0x42, 0xaf, 0x61, 0x56, 0x8d, 0x37, 0x6a, 0x11, 0xc7, 0xf7, 0x14, 0x3b, 0xea, 0x50, 0xa2, 0xea, 0xa9, 0xf7, 0x18, 0x4f, 0xf9, 0xc8, 0xa6, 0xe6, 0x29, 0xad, 0x87, 0xa9, 0x9b, 0x51, 0xa6, 0xde, 0x6e, 0x06, 0xa7, 0xdc, 0xad, 0xfc, 0x58, 0x82, 0x85, 0x5c, 0xaf, 0x79, 0x3d, 0xc7, 0xd3, 0x2c, 0x8c, 0xc6, 0xba, 0xc8, 0x6c, 0xa5, 0x68, 0x25, 0x13, 0xa2, 0xa5, 0xf0, 0xdd, 0xe0, 0xa8, 0x70, 0x0e, 0xbe, 0x03, 0xe1, 0x7c, 0x0f, 0xef, 0x00, 0x59, 0x84, 0xb3, 0xf0, 0xaf, 0x0a, 0xe7, 0xc8, 0x89, 0x85, 0xf3, 0x25, 0x4c, 0x34, 0xb1, 0xa0, 0xae, 0xd4, 0x88, 0xba, 0x99, 0x8a, 0x29, 0x0d, 0xbb, 0xa1, 0xe2, 0x15, 0x4a, 0xd4, 0xb0, 0xcd, 0xee, 0xa5, 0x4e, 0xd3, 0x50, 0x3a, 0x6a, 0x1a, 0x08, 0x4c, 0x75, 0xb4, 0x81, 0x25, 0xa8, 0xdf, 0xde, 0x16, 0xd4, 0xb6, 0x37, 0x52, 0x0b, 0xbe, 0x66, 0x9b, 0x41, 0x8a, 0xde, 0xfa, 0xdc, 0x61, 0xd2, 0xf2, 0xbb, 0x79, 0xa5, 0x38, 0xa6, 0x73, 0xe5, 0x54, 0x9d, 0x1b, 0xcb, 0xaf, 0x73, 0xa7, 0xde, 0x42, 0xe7, 0x4e, 0xbf, 0x9d, 0xce, 0xfd, 0xf7, 0x94, 0xea, 0xcf, 0x01, 0x58, 0xca, 0xfd, 0x69, 0xe7, 0x7d, 0x9b, 0xe9, 0x59, 0x18, 0xd5, 0x5f, 0xb4, 0x94, 0xbf, 0x0d, 0x3f, 0x5e, 0x40, 0xb8, 0xa4, 0xfc, 0x6d, 0x2c, 0x40, 0x43, 0xd9, 0x05, 0xa8, 0xe3, 0xc7, 0x36, 0x9c, 0xc9, 0xa1, 0x17, 0x7a, 0x39, 0xf4, 0xaf, 0x0c, 0x58, 0xcc, 0xfb, 0x85, 0x29, 0xb9, 0x3d, 0x8d, 0xb7, 0x6a, 0xcf, 0x95, 0x4f, 0xe1, 0x3c, 0xe1, 0x8d, 0xa4, 0xec, 0x95, 0xb1, 0x88, 0xc2, 0x46, 0x20, 0x73, 0x1b, 0xc6, 0x27, 0x4b, 0xbb, 0x4c, 0xee, 0xf9, 0xdb, 0x55, 0xc2, 0x1b, 0xb5, 0xce, 0xff, 0xac, 0x2d, 0x30, 0xdb, 0xa9, 0xed, 0xf2, 0xf0, 0x9f, 0x79, 0xfa, 0xdf, 0x6c, 0xf7, 0x70, 0x93, 0x1d, 0x2c, 0x6d, 0x17, 0xd4, 0xda, 0xcd, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x3a, 0x34, 0x7d, 0x59, 0x29, 0x1c, 0x00, 0x00, }, // uber/cadence/admin/v1/cluster.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x6d, 0x8b, 0xd3, 0x40, 0x10, 0x26, 0x17, 0x6b, 0xdb, 0xe9, 0xe1, 0x9d, 0x8b, 0x87, 0x45, 0x11, 0x6b, 0x04, 0xa9, 0x88, 0x09, 0x3d, 0x39, 0xc4, 0x17, 0x44, 0x7a, 0x2a, 0xf6, 0x83, 0x2f, 0xac, 0xdf, 0xfc, 0x12, 0x36, 0x9b, 0x69, 0xbb, 0x5c, 0x76, 0xf7, 0xd8, 0x6c, 0x0a, 0x05, 0xff, 0x82, 0xff, 0x47, 0xff, 0x9d, 0x64, 0x37, 0x09, 0x27, 0xf6, 0xf0, 0xbe, 0xcd, 0xcc, 0x3e, 0xcf, 0xb3, 0xcf, 0xcc, 0x30, 0xf0, 0xb0, 0xca, 0xd0, 0x24, 0x9c, 0xe5, 0xa8, 0x38, 0x26, 0x2c, 0x97, 0x42, 0x25, 0x9b, 0x59, 0xc2, 0x8b, 0xaa, 0xb4, 0x68, 0xe2, 0x73, 0xa3, 0xad, 0x26, 0x47, 0x35, 0x28, 0x6e, 0x40, 0xb1, 0x03, 0xc5, 0x9b, 0x59, 0xf4, 0x08, 0x06, 0x1f, 0x75, 0x69, 0x17, 0x6a, 0xa9, 0xc9, 0x1d, 0x18, 0x88, 0x1c, 0x95, 0x15, 0x76, 0x3b, 0x0e, 0x26, 0xc1, 0x74, 0x48, 0xbb, 0x3c, 0xfa, 0x01, 0x03, 0x2a, 0xd4, 0xca, 0xe1, 0x08, 0x5c, 0x33, 0xba, 0xc0, 0x06, 0xe3, 0x62, 0xf2, 0x00, 0xf6, 0x25, 0xca, 0x0c, 0x4d, 0xca, 0x75, 0xa5, 0xec, 0x78, 0x6f, 0x12, 0x4c, 0x7b, 0x74, 0xe4, 0x6b, 0xa7, 0x75, 0x89, 0xbc, 0x80, 0xbe, 0x4f, 0xcb, 0x71, 0x38, 0x09, 0xa7, 0xa3, 0xe3, 0xfb, 0xf1, 0x4e, 0x4f, 0x71, 0x6b, 0x88, 0xb6, 0xf8, 0xe8, 0x57, 0x00, 0x37, 0x3e, 0xf9, 0x78, 0x2d, 0xce, 0x9d, 0x89, 0x39, 0xec, 0xf3, 0xca, 0x18, 0x54, 0x36, 0x5d, 0xeb, 0xd2, 0x3a, 0x33, 0x57, 0x90, 0x1c, 0x35, 0xa4, 0xba, 0x40, 0x9e, 0xc0, 0x4d, 0x83, 0x8c, 0xaf, 0x59, 0x56, 0x60, 0xda, 0x7a, 0xdb, 0x9b, 0x84, 0xd3, 0x21, 0x3d, 0xec, 0x1e, 0x9a, 0x7f, 0xc9, 0x09, 0xf4, 0x8c, 0x50, 0xab, 0xff, 0x99, 0x6f, 0xa7, 0x44, 0x3d, 0x3a, 0xfa, 0x19, 0xc0, 0xc1, 0x3b, 0x2d, 0x99, 0x50, 0xa7, 0x8c, 0xaf, 0xd1, 0x79, 0x7f, 0x09, 0x77, 0x55, 0x25, 0x53, 0xbd, 0x4c, 0x85, 0x45, 0x59, 0xa6, 0x42, 0xa5, 0xbc, 0x7e, 0x4c, 0xb3, 0x6d, 0x2a, 0x72, 0xd7, 0x4a, 0x48, 0x8f, 0x54, 0x25, 0xbf, 0x2c, 0x17, 0x35, 0x60, 0xe1, 0xb9, 0xf3, 0xed, 0x22, 0x27, 0x6f, 0xe0, 0xde, 0xa5, 0x5c, 0xc5, 0x24, 0xba, 0xc9, 0x87, 0xf4, 0xf6, 0x0e, 0xf6, 0x67, 0x26, 0x31, 0x7a, 0x0d, 0xe4, 0x2b, 0x9a, 0x52, 0x94, 0xb6, 0xf6, 0xfd, 0x0d, 0xad, 0x15, 0x6a, 0x45, 0x0e, 0x21, 0x3c, 0xc3, 0x76, 0xeb, 0x75, 0x48, 0x6e, 0x41, 0x6f, 0xc3, 0x8a, 0xca, 0xeb, 0x0d, 0xa9, 0x4f, 0xa2, 0xb7, 0x7f, 0xb1, 0x3f, 0x20, 0xb3, 0x95, 0xc1, 0x1d, 0xec, 0x31, 0xf4, 0x51, 0xd5, 0xd3, 0xcb, 0x1d, 0x7f, 0x40, 0xdb, 0x34, 0xfa, 0x1d, 0xc0, 0xc1, 0x05, 0x09, 0x37, 0x8f, 0x31, 0xf4, 0x33, 0xc6, 0xcf, 0x50, 0xe5, 0x8d, 0x46, 0x9b, 0x92, 0xf7, 0x30, 0x28, 0xbd, 0x45, 0xbf, 0x98, 0xd1, 0xf1, 0xe3, 0x4b, 0xe6, 0xfe, 0x6f, 0x53, 0xb4, 0xa3, 0xd6, 0x32, 0x4b, 0xef, 0xb5, 0x5d, 0xdf, 0x15, 0x64, 0x9a, 0xee, 0x68, 0x47, 0x9d, 0x3f, 0xff, 0x7e, 0xb2, 0x12, 0x76, 0x5d, 0x65, 0x31, 0xd7, 0x32, 0xb9, 0x78, 0x75, 0x4f, 0x45, 0x5e, 0x24, 0x2b, 0x9d, 0xb8, 0x5b, 0xeb, 0x4e, 0xf0, 0x95, 0x0b, 0x36, 0xb3, 0xec, 0xba, 0xab, 0x3f, 0xfb, 0x13, 0x00, 0x00, 0xff, 0xff, 0xaf, 0x49, 0xfd, 0x6d, 0xaa, 0x03, 0x00, 0x00, }, // uber/cadence/admin/v1/history.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2e, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x4f, 0x4c, 0xc9, 0xcd, 0xcc, 0xd3, 0x2f, 0x33, 0xd4, 0xcf, 0xc8, 0x2c, 0x2e, 0xc9, 0x2f, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x05, 0x29, 0xd2, 0x83, 0x2a, 0xd2, 0x03, 0x2b, 0xd2, 0x2b, 0x33, 0x54, 0xf2, 0xe4, 0x12, 0x0a, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0xf3, 0x80, 0x28, 0xf7, 0x2c, 0x49, 0xcd, 0x15, 0x92, 0xe4, 0xe2, 0x48, 0x2d, 0x4b, 0xcd, 0x2b, 0x89, 0xcf, 0x4c, 0x91, 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e, 0x62, 0x07, 0xf3, 0x3d, 0x53, 0x84, 0x24, 0xb8, 0xd8, 0xcb, 0x20, 0x1a, 0x24, 0x98, 0x20, 0x32, 0x50, 0xae, 0x52, 0x09, 0x17, 0x1f, 0xaa, 0x51, 0x42, 0x8a, 0x5c, 0x3c, 0x49, 0x45, 0x89, 0x79, 0xc9, 0x19, 0xf1, 0x25, 0xf9, 0xd9, 0xa9, 0x79, 0x60, 0xa3, 0x78, 0x82, 0xb8, 0x21, 0x62, 0x21, 0x20, 0x21, 0x21, 0x7b, 0x2e, 0xd6, 0xcc, 0x92, 0xd4, 0xdc, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x4d, 0x3d, 0xac, 0xce, 0xd4, 0xc3, 0x74, 0x63, 0x10, 0x44, 0x9f, 0x93, 0x79, 0x94, 0x69, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0x72, 0x48, 0xe8, 0x66, 0xa6, 0xe4, 0xe8, 0xa7, 0xe7, 0xeb, 0x83, 0xfd, 0x0f, 0x0f, 0x16, 0x6b, 0x30, 0xa3, 0xcc, 0x30, 0x89, 0x0d, 0x2c, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x44, 0x14, 0xd7, 0xd4, 0x3e, 0x01, 0x00, 0x00, }, // uber/cadence/admin/v1/queue.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x59, 0x4f, 0x6f, 0xdb, 0xc8, 0x15, 0x37, 0x25, 0xcb, 0x96, 0x9f, 0x1d, 0x9b, 0x9e, 0x24, 0x8d, 0xe2, 0xc4, 0x88, 0xa2, 0x05, 0x12, 0xc1, 0x9b, 0x48, 0xb5, 0xbd, 0x41, 0x16, 0xbb, 0x45, 0x5b, 0x9a, 0x62, 0x2c, 0xae, 0x65, 0x49, 0x18, 0x52, 0x49, 0xbc, 0x58, 0x80, 0xa0, 0xa9, 0xb1, 0x43, 0x84, 0x22, 0xb5, 0x24, 0xe5, 0x54, 0x1f, 0xa0, 0xd9, 0x7b, 0xb7, 0xd7, 0x5e, 0x8a, 0xde, 0x0a, 0xf4, 0xd4, 0x1e, 0x8a, 0x5e, 0x0b, 0xf4, 0xd4, 0x6b, 0x7b, 0xd8, 0x2f, 0xd1, 0x8f, 0x50, 0x70, 0x48, 0x4a, 0x14, 0x45, 0x59, 0x74, 0xe2, 0x02, 0x3d, 0xec, 0x4d, 0x7a, 0xfc, 0xcd, 0xe3, 0xef, 0xfd, 0x99, 0xdf, 0xbc, 0x91, 0xe0, 0xe1, 0xe0, 0x94, 0xd8, 0x55, 0x4d, 0xed, 0x12, 0x53, 0x23, 0x55, 0xb5, 0xdb, 0xd3, 0xcd, 0xea, 0xc5, 0x6e, 0xf5, 0xdb, 0x01, 0x19, 0x90, 0x4a, 0xdf, 0xb6, 0x5c, 0x0b, 0xdd, 0xf6, 0x20, 0x95, 0x00, 0x52, 0xa1, 0x90, 0xca, 0xc5, 0xee, 0xd6, 0x83, 0x73, 0xcb, 0x3a, 0x37, 0x48, 0x95, 0x82, 0x4e, 0x07, 0x67, 0x55, 0x57, 0xef, 0x11, 0xc7, 0x55, 0x7b, 0x7d, 0x7f, 0xdd, 0x56, 0x71, 0xd2, 0x75, 0x5f, 0xf7, 0x1c, 0x6b, 0x56, 0xaf, 0x67, 0x99, 0x01, 0xe2, 0x61, 0x12, 0xe2, 0x8d, 0xee, 0xb8, 0x96, 0x3d, 0x0c, 0x20, 0xa5, 0x24, 0xc8, 0x3b, 0xcb, 0x7e, 0x7b, 0x66, 0x58, 0xef, 0x7c, 0x4c, 0xe9, 0x87, 0x0c, 0xdc, 0xe2, 0x6d, 0xcb, 0x71, 0x78, 0x63, 0xe0, 0xb8, 0xc4, 0x96, 0x55, 0xe7, 0xad, 0x68, 0x9e, 0x59, 0xe8, 0x1e, 0xac, 0x74, 0xad, 0x9e, 0xaa, 0x9b, 0x8a, 0xde, 0x2d, 0x30, 0x45, 0xa6, 0xbc, 0x82, 0xf3, 0xbe, 0x41, 0xec, 0xa2, 0x0e, 0xa0, 0xd0, 0x8f, 0x42, 0x7e, 0x45, 0xb4, 0x81, 0xab, 0x5b, 0x66, 0x21, 0x53, 0x64, 0xca, 0xab, 0x7b, 0x8f, 0x2a, 0x93, 0x31, 0xf7, 0xf5, 0xca, 0xc5, 0x6e, 0xe5, 0x55, 0x00, 0x17, 0x42, 0x34, 0xde, 0x7c, 0x17, 0x37, 0xa1, 0x3a, 0xac, 0xb8, 0xaa, 0xf3, 0x56, 0x71, 0x87, 0x7d, 0x52, 0xc8, 0x16, 0x99, 0xf2, 0xfa, 0xde, 0xa7, 0x95, 0xc4, 0x0c, 0x56, 0xe2, 0x9c, 0xe5, 0x61, 0x9f, 0xe0, 0xbc, 0x1b, 0x7c, 0x42, 0xdb, 0x00, 0xd4, 0x93, 0xe3, 0xaa, 0x2e, 0x29, 0x2c, 0x16, 0x99, 0x72, 0x0e, 0x53, 0xdf, 0x92, 0x67, 0x40, 0x77, 0x60, 0x99, 0x3e, 0xd6, 0xbb, 0x85, 0x5c, 0x91, 0x29, 0x67, 0xf1, 0x92, 0xf7, 0x55, 0xec, 0xa2, 0x63, 0xb8, 0x75, 0xa1, 0x3b, 0xfa, 0xa9, 0x6e, 0xe8, 0xee, 0x50, 0x19, 0x55, 0xa5, 0xb0, 0x44, 0x43, 0xdb, 0xaa, 0xf8, 0x75, 0xab, 0x84, 0x75, 0xab, 0xc8, 0x21, 0x02, 0xdf, 0x1c, 0xaf, 0x1b, 0x19, 0x4b, 0x7f, 0x58, 0x84, 0x9f, 0x46, 0x99, 0x4a, 0xae, 0x6a, 0xbb, 0xfc, 0x1b, 0xdd, 0xe8, 0x8e, 0xf3, 0x40, 0xbe, 0x1d, 0x10, 0xc7, 0xe5, 0x5c, 0xd7, 0xd6, 0x4f, 0x07, 0x2e, 0x71, 0x50, 0x19, 0x58, 0x57, 0xb5, 0xcf, 0x89, 0xab, 0xc4, 0x0b, 0xb0, 0xee, 0xdb, 0x6b, 0x61, 0x19, 0xb6, 0x01, 0x6c, 0x7f, 0xb9, 0x87, 0xc9, 0x50, 0xcc, 0x4a, 0x60, 0x11, 0xbb, 0xe8, 0x09, 0x20, 0xdd, 0xd4, 0x5d, 0x5d, 0x75, 0x49, 0x57, 0x21, 0x17, 0xc4, 0xa4, 0xb0, 0x2c, 0x0d, 0x98, 0x1d, 0x3d, 0x11, 0xbc, 0x07, 0x62, 0x17, 0xbd, 0x67, 0x60, 0x2b, 0x0e, 0x57, 0x47, 0xac, 0x68, 0x0e, 0x57, 0xf7, 0xea, 0x89, 0xc5, 0x1d, 0x87, 0x35, 0x55, 0x66, 0x71, 0xe2, 0x35, 0xe3, 0x28, 0x71, 0x41, 0x9f, 0xf1, 0x04, 0x95, 0xe0, 0x46, 0x10, 0xbf, 0x3d, 0x30, 0xc3, 0x12, 0xad, 0xe0, 0x55, 0xdf, 0x88, 0x07, 0x5e, 0xe4, 0xdf, 0x31, 0xc0, 0xf6, 0x55, 0xdb, 0xd5, 0xbd, 0x77, 0x28, 0x9a, 0x65, 0x9e, 0xe9, 0xe7, 0x85, 0xa5, 0x62, 0xb6, 0xbc, 0xba, 0xf7, 0x4d, 0x8a, 0x8e, 0x49, 0x53, 0x87, 0x4a, 0x3b, 0xf4, 0xcf, 0x53, 0xf7, 0x82, 0xe9, 0xda, 0x43, 0xbc, 0xd1, 0x9f, 0xb4, 0x6e, 0x1d, 0xc0, 0xad, 0x24, 0x20, 0x62, 0x21, 0xfb, 0x96, 0x0c, 0x83, 0xc2, 0x79, 0x1f, 0xd1, 0x2d, 0xc8, 0x5d, 0xa8, 0xc6, 0x80, 0x04, 0x85, 0xf2, 0xbf, 0x7c, 0x91, 0xf9, 0x9c, 0x29, 0x7d, 0x05, 0xbb, 0x73, 0xd9, 0x39, 0x7d, 0xcb, 0x74, 0x48, 0x24, 0x4d, 0xb7, 0x61, 0x29, 0xc8, 0x8f, 0xff, 0x8e, 0x9c, 0xed, 0x65, 0xa6, 0xf4, 0xd7, 0x0c, 0x3c, 0x89, 0x3a, 0xe3, 0x55, 0x53, 0x23, 0xc6, 0xb5, 0xb4, 0xdb, 0x29, 0xdc, 0x0d, 0x90, 0x1f, 0xbd, 0xf9, 0xef, 0xf8, 0x8e, 0xa6, 0x1e, 0xc4, 0x5a, 0x3a, 0x9b, 0xae, 0xa5, 0x17, 0x67, 0xb4, 0x74, 0x05, 0x6e, 0x6a, 0x5e, 0x1a, 0xc7, 0x7c, 0x2d, 0xd3, 0x18, 0xd2, 0x7e, 0xca, 0xe3, 0x4d, 0x2d, 0xda, 0xb0, 0x2d, 0xd3, 0x18, 0x96, 0xaa, 0xf0, 0xf4, 0xd2, 0xd4, 0xc5, 0x6b, 0x50, 0xfa, 0x4b, 0x76, 0x32, 0xd9, 0x92, 0x7e, 0x6e, 0xaa, 0x3f, 0x26, 0x3b, 0x4d, 0xb2, 0xd1, 0x03, 0x58, 0x75, 0x68, 0xba, 0x14, 0x53, 0xed, 0x11, 0xaa, 0xb0, 0x2b, 0x18, 0x7c, 0x53, 0x53, 0xed, 0x11, 0xf4, 0x0b, 0x58, 0x0b, 0x00, 0xba, 0xd9, 0x1f, 0xb8, 0x85, 0x65, 0x1a, 0xf4, 0xfd, 0xc4, 0xa0, 0xdb, 0xea, 0xd0, 0xb0, 0xd4, 0x2e, 0x0e, 0x5c, 0x8a, 0xde, 0x02, 0x54, 0x80, 0x65, 0xcd, 0x32, 0x5d, 0xdb, 0x32, 0x0a, 0xf9, 0x22, 0x53, 0x5e, 0xc3, 0xe1, 0xd7, 0x78, 0xa1, 0xa7, 0xca, 0x36, 0x55, 0xe8, 0x7f, 0x66, 0x80, 0x8b, 0xae, 0xc0, 0x44, 0xb3, 0xec, 0x6e, 0xb2, 0xe4, 0xf1, 0x56, 0xaf, 0x6f, 0x10, 0x97, 0xfc, 0xbf, 0x57, 0xff, 0x6a, 0xc7, 0x43, 0x03, 0x58, 0xcd, 0x0f, 0xcc, 0x53, 0x5c, 0x0a, 0x0f, 0xce, 0x84, 0x87, 0x89, 0x44, 0xea, 0xfe, 0x28, 0x42, 0x97, 0xe3, 0x8d, 0xf1, 0x52, 0x6a, 0x28, 0xd5, 0xe0, 0xe0, 0xea, 0xe9, 0x9c, 0xaa, 0xca, 0x7f, 0x18, 0x28, 0x72, 0xfd, 0xbe, 0x31, 0x6c, 0xab, 0x36, 0x31, 0x5d, 0xde, 0xb0, 0x1c, 0xd2, 0xb6, 0x0c, 0x5d, 0x1b, 0x46, 0x92, 0xfe, 0x08, 0x36, 0xfc, 0xbe, 0x8c, 0xe7, 0xfc, 0x06, 0x35, 0x8f, 0x52, 0xbe, 0x03, 0x9b, 0xb1, 0xfe, 0x1d, 0x9d, 0xa9, 0x1b, 0x13, 0xdd, 0x2b, 0x76, 0x51, 0x11, 0xd6, 0x7c, 0x6c, 0xa0, 0xc0, 0xfe, 0xd6, 0x01, 0x6a, 0xf3, 0x0f, 0xa8, 0x97, 0x70, 0xb3, 0x4f, 0x49, 0x29, 0x9a, 0xc7, 0x4a, 0xe9, 0x53, 0x5a, 0x34, 0x63, 0xeb, 0x33, 0x4a, 0x37, 0x15, 0x04, 0xde, 0xec, 0xc7, 0x4d, 0xa5, 0xef, 0x19, 0xb8, 0x9f, 0x1c, 0xb2, 0x37, 0xd9, 0x0c, 0x1c, 0x74, 0x1f, 0x56, 0x82, 0x64, 0x13, 0x3f, 0xd0, 0x3c, 0x1e, 0x1b, 0x50, 0x07, 0xd6, 0xce, 0x54, 0xdd, 0x20, 0x5d, 0x45, 0x53, 0x07, 0x8e, 0x7f, 0x14, 0xad, 0xef, 0xed, 0xa5, 0x1c, 0xb2, 0x5e, 0xd0, 0xa5, 0xbc, 0xb7, 0x12, 0xaf, 0x9e, 0x8d, 0xbf, 0x94, 0xfe, 0xc6, 0xc0, 0x76, 0x32, 0xab, 0x60, 0x13, 0xa0, 0x63, 0xc8, 0xd1, 0xec, 0x50, 0x4a, 0xab, 0x7b, 0xcf, 0x67, 0xbc, 0x71, 0x5e, 0x35, 0xb1, 0xef, 0x05, 0x1d, 0xc1, 0x92, 0x43, 0xe3, 0x0d, 0x36, 0xc3, 0xfe, 0x95, 0xfc, 0xf9, 0xa9, 0xc2, 0x81, 0x8b, 0xd2, 0x77, 0x0c, 0xec, 0x47, 0x43, 0xbd, 0x34, 0x92, 0x48, 0x67, 0xb5, 0x21, 0x4f, 0xd9, 0xd8, 0xc4, 0x2c, 0x30, 0x74, 0xf6, 0xf8, 0xec, 0x4a, 0x34, 0x02, 0x8f, 0x78, 0xe4, 0xa5, 0xf4, 0xf7, 0x99, 0xd5, 0xc5, 0xc4, 0x19, 0x18, 0xd7, 0x9e, 0xc6, 0xff, 0x51, 0x3b, 0xfc, 0x96, 0x81, 0xcf, 0xd2, 0x24, 0x74, 0x6a, 0xa6, 0xf9, 0x26, 0xd8, 0xab, 0x36, 0x31, 0x95, 0xa0, 0xbe, 0x7e, 0x62, 0xf7, 0xaf, 0x98, 0x58, 0x2f, 0x59, 0x78, 0x3d, 0xf4, 0xe5, 0xd7, 0xbb, 0xf4, 0xbb, 0x65, 0xb8, 0x13, 0x8f, 0x21, 0xec, 0xcf, 0xf0, 0xea, 0xa1, 0x9b, 0x67, 0x56, 0x90, 0xdc, 0xb4, 0x57, 0x0f, 0xef, 0xba, 0xe4, 0x5f, 0x3d, 0xe8, 0xc5, 0xe9, 0x37, 0x0c, 0x14, 0x1d, 0x6f, 0x82, 0x53, 0x7c, 0x89, 0x18, 0xe9, 0x76, 0x74, 0x9a, 0xf6, 0xbb, 0xf6, 0xf0, 0x9a, 0x46, 0xd5, 0xfa, 0x02, 0xde, 0x76, 0xa6, 0x71, 0x91, 0xc4, 0xfe, 0x9a, 0x81, 0x7b, 0x1a, 0x1d, 0x67, 0x92, 0xf9, 0x64, 0x29, 0x1f, 0x3e, 0x05, 0x9f, 0x79, 0xf3, 0x64, 0x7d, 0x01, 0xdf, 0xd5, 0x26, 0x31, 0x31, 0x1e, 0xc1, 0xa1, 0x9e, 0xc8, 0x63, 0x31, 0x35, 0x8f, 0x79, 0xa3, 0x96, 0xc7, 0xc3, 0x99, 0xc4, 0x44, 0x78, 0xfc, 0x8b, 0x81, 0x2f, 0x6d, 0x7a, 0xe8, 0x28, 0x31, 0xd1, 0x1f, 0xd3, 0x0a, 0x55, 0x53, 0x09, 0x67, 0xa4, 0x08, 0xcf, 0x1c, 0xe5, 0xf9, 0x3a, 0x05, 0xcf, 0x0f, 0x9a, 0x14, 0xea, 0x0b, 0xf8, 0x99, 0xfd, 0x41, 0x23, 0xc6, 0x1f, 0x19, 0x78, 0xa2, 0x7a, 0x9b, 0x42, 0x49, 0x38, 0x7e, 0x92, 0x22, 0xf1, 0x6f, 0xb6, 0x5f, 0xa5, 0x88, 0x24, 0xa5, 0x2c, 0xd6, 0x17, 0xf0, 0x63, 0x35, 0x1d, 0xf4, 0x60, 0x0d, 0x60, 0x4c, 0xa5, 0xf4, 0xe7, 0x3c, 0x14, 0xa6, 0xf7, 0xa7, 0x2f, 0x12, 0xd1, 0x2b, 0x3b, 0x33, 0x71, 0x65, 0x9f, 0xf8, 0xd1, 0x20, 0x73, 0x7d, 0x3f, 0x1a, 0x64, 0xe3, 0x3f, 0x1a, 0xc4, 0xc5, 0x72, 0xf1, 0x5a, 0xc4, 0x12, 0x7d, 0x9f, 0x46, 0x2f, 0x72, 0x89, 0xb7, 0xef, 0xd4, 0x7a, 0x11, 0x17, 0xda, 0xf9, 0x82, 0xf1, 0x7e, 0x8e, 0x60, 0xf8, 0x6d, 0x53, 0xfb, 0x10, 0xc1, 0x48, 0x20, 0x73, 0x89, 0x62, 0xbc, 0x9f, 0xa3, 0x18, 0xcb, 0xa9, 0x89, 0xcc, 0x9d, 0xf2, 0x2f, 0x97, 0x8c, 0x7f, 0x7f, 0xa4, 0x64, 0xe4, 0x29, 0xd1, 0x93, 0x6b, 0x93, 0x8c, 0x04, 0xf6, 0x1f, 0xa8, 0x19, 0x7f, 0x62, 0xe0, 0xe9, 0x65, 0x9a, 0xe1, 0xbf, 0x29, 0x1a, 0xcb, 0x0a, 0x8d, 0xe5, 0xe8, 0x23, 0x44, 0x23, 0x81, 0x7d, 0x59, 0x4d, 0x89, 0x8d, 0xc9, 0x86, 0x95, 0xa4, 0x1a, 0x34, 0x46, 0x07, 0x49, 0x70, 0x83, 0x6e, 0xe9, 0xa0, 0x1e, 0xe1, 0x38, 0x51, 0x49, 0xb9, 0x69, 0xc3, 0x09, 0x6d, 0xcd, 0x8d, 0x38, 0xdd, 0x79, 0xcf, 0x40, 0x3e, 0x94, 0x0f, 0x74, 0x1b, 0x36, 0x65, 0x4e, 0x3a, 0x52, 0xe4, 0x93, 0xb6, 0xa0, 0x88, 0xcd, 0x97, 0x5c, 0x43, 0xac, 0xb1, 0x0b, 0xe8, 0x27, 0x80, 0xc6, 0x66, 0x19, 0x73, 0x4d, 0xe9, 0x85, 0x80, 0x59, 0x06, 0xdd, 0x84, 0x8d, 0x88, 0x5d, 0x3c, 0x16, 0x30, 0x9b, 0x41, 0x77, 0xe1, 0xf6, 0xd8, 0x88, 0x85, 0x76, 0x43, 0xe4, 0x39, 0x59, 0x6c, 0x35, 0xd9, 0x2c, 0xba, 0x07, 0x77, 0xc6, 0x8f, 0x78, 0xdc, 0x92, 0x24, 0x85, 0x6f, 0x74, 0x24, 0x59, 0xc0, 0xec, 0xe2, 0xce, 0x3f, 0x12, 0x7e, 0xbc, 0xa5, 0xa4, 0x3e, 0x81, 0x07, 0x13, 0x58, 0x25, 0x89, 0xe2, 0x2e, 0x3c, 0x9d, 0x05, 0x92, 0x64, 0x0e, 0xcb, 0x0a, 0x5f, 0x17, 0x1b, 0x35, 0x45, 0x78, 0x2d, 0xf0, 0x1d, 0xca, 0x86, 0x41, 0x4f, 0xa0, 0x3c, 0x6b, 0x09, 0xcf, 0x35, 0x79, 0xa1, 0x11, 0x41, 0x67, 0x2e, 0x43, 0x4b, 0xe2, 0x61, 0x93, 0x8b, 0xa2, 0xb3, 0xa8, 0x06, 0xbf, 0x9c, 0x85, 0xc6, 0x02, 0xdf, 0xc2, 0xb5, 0x80, 0xcf, 0xab, 0x16, 0x3e, 0x3a, 0x6a, 0xb4, 0x5e, 0x8d, 0x17, 0x2b, 0x7c, 0xeb, 0xb8, 0xdd, 0x10, 0x64, 0x81, 0x5d, 0x44, 0xcf, 0x60, 0x77, 0x96, 0x17, 0xae, 0xdd, 0x6e, 0x9c, 0x28, 0x6d, 0x0e, 0x0b, 0x4d, 0x59, 0xe1, 0x1b, 0x2d, 0x49, 0x50, 0xda, 0xad, 0x86, 0xc8, 0x9f, 0xb0, 0xb9, 0x9d, 0xdf, 0x67, 0xe1, 0xde, 0x25, 0x8a, 0x8d, 0x3e, 0x85, 0xc7, 0x09, 0x6e, 0x5f, 0x70, 0x62, 0x43, 0xa8, 0x29, 0x3c, 0xd7, 0x91, 0xa2, 0x89, 0x4d, 0xe6, 0x30, 0x01, 0xae, 0xb5, 0x8e, 0x39, 0xb1, 0xa9, 0x34, 0x5b, 0xb2, 0xc2, 0xf1, 0xb2, 0xf8, 0x52, 0x60, 0x99, 0x2b, 0x2e, 0x13, 0x5e, 0x8b, 0x92, 0x2c, 0xb1, 0x19, 0xf4, 0x33, 0xf8, 0x7c, 0xde, 0x32, 0x2f, 0x65, 0x2f, 0xbc, 0x94, 0x71, 0x0d, 0x2c, 0x70, 0xb5, 0x13, 0x05, 0x77, 0x9a, 0x4d, 0xb1, 0x79, 0xc8, 0x66, 0xd1, 0x73, 0xd8, 0x4f, 0xbd, 0x3a, 0xf2, 0xda, 0x45, 0xf4, 0x73, 0xf8, 0xe2, 0xca, 0xaf, 0x0d, 0xeb, 0x54, 0x63, 0x73, 0x33, 0xba, 0x6f, 0x62, 0x7d, 0xa7, 0xc9, 0x73, 0xb2, 0x70, 0xd8, 0xc2, 0xe2, 0xd7, 0x42, 0x8d, 0x5d, 0xda, 0xf9, 0x81, 0x01, 0x74, 0x48, 0xdc, 0x78, 0x6d, 0x1e, 0xc2, 0xf6, 0xa1, 0x20, 0x5f, 0x5a, 0x91, 0x47, 0x50, 0x4a, 0x86, 0x48, 0x02, 0x7e, 0x29, 0xf2, 0x82, 0x72, 0xd0, 0x91, 0x4e, 0x58, 0x66, 0xb6, 0x2b, 0x6f, 0xa7, 0xb6, 0x3a, 0x32, 0x9b, 0x41, 0x15, 0xd8, 0x99, 0xe1, 0xaa, 0xce, 0xe1, 0x9a, 0xd2, 0x7a, 0xd5, 0x14, 0xb0, 0x54, 0x17, 0xdb, 0x4a, 0xa3, 0x25, 0xc9, 0x6c, 0x16, 0x3d, 0x86, 0x4f, 0x92, 0xf1, 0x93, 0xd1, 0x2d, 0x1e, 0x3c, 0xff, 0xfa, 0xd9, 0xb9, 0xee, 0xbe, 0x19, 0x9c, 0x56, 0x34, 0xab, 0x57, 0x8d, 0xfe, 0x75, 0xf3, 0x54, 0xef, 0x1a, 0xd5, 0x73, 0xcb, 0xff, 0xb7, 0x68, 0xf4, 0x3f, 0xd3, 0x97, 0xf4, 0xc3, 0xc5, 0xee, 0xe9, 0x12, 0xb5, 0xef, 0xff, 0x37, 0x00, 0x00, 0xff, 0xff, 0x4b, 0xe2, 0x06, 0x07, 0x8f, 0x1a, 0x00, 0x00, }, // uber/cadence/admin/v1/replication.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0xcd, 0x6f, 0xdb, 0xc6, 0x12, 0x0f, 0x25, 0xeb, 0xc3, 0x63, 0x59, 0xa2, 0xd7, 0x4e, 0xac, 0xd8, 0x31, 0x9e, 0xa2, 0x7c, 0x39, 0x0e, 0x9e, 0xf4, 0xec, 0xbc, 0xbc, 0x4f, 0x3c, 0x04, 0x8c, 0x25, 0x3f, 0xb3, 0xf1, 0xe7, 0x8a, 0x71, 0xea, 0x02, 0x05, 0x41, 0x93, 0x6b, 0x8b, 0xb0, 0x44, 0x0a, 0xe4, 0x4a, 0x8e, 0x6e, 0xed, 0xa5, 0xa7, 0x02, 0xbd, 0xf4, 0xd6, 0x63, 0xff, 0x8d, 0x9e, 0x7b, 0x6d, 0xff, 0xa4, 0x82, 0xbb, 0x4b, 0x49, 0x94, 0x28, 0xc5, 0x41, 0x0e, 0xbd, 0x69, 0x67, 0x7e, 0x33, 0xb3, 0x3b, 0x3b, 0xfb, 0x9b, 0x11, 0xe1, 0x59, 0xf7, 0x82, 0x78, 0x55, 0xd3, 0xb0, 0x88, 0x63, 0x92, 0xaa, 0x61, 0xb5, 0x6d, 0xa7, 0xda, 0xdb, 0xae, 0x7a, 0xa4, 0xd3, 0xb2, 0x4d, 0x83, 0xda, 0xae, 0x53, 0xe9, 0x78, 0x2e, 0x75, 0xd1, 0xdd, 0x00, 0x58, 0x11, 0xc0, 0x0a, 0x03, 0x56, 0x7a, 0xdb, 0x6b, 0x7f, 0xb9, 0x72, 0xdd, 0xab, 0x16, 0xa9, 0x32, 0xd0, 0x45, 0xf7, 0xb2, 0x4a, 0xed, 0x36, 0xf1, 0xa9, 0xd1, 0xee, 0x70, 0xbb, 0xb5, 0x52, 0x34, 0x40, 0xc7, 0x0e, 0xdc, 0x9b, 0x6e, 0xbb, 0x1d, 0x7a, 0x8e, 0x47, 0x58, 0x6e, 0xdb, 0xb0, 0x43, 0xc4, 0xa3, 0xf8, 0x4d, 0x36, 0x6d, 0x9f, 0xba, 0x5e, 0x9f, 0x83, 0xca, 0x3f, 0x26, 0x60, 0x19, 0x0f, 0xb7, 0x7d, 0x48, 0x7c, 0xdf, 0xb8, 0x22, 0x3e, 0x6a, 0xc0, 0xd2, 0xc8, 0x69, 0x74, 0x6a, 0xf8, 0xd7, 0x7e, 0x51, 0x2a, 0x25, 0x37, 0x17, 0x76, 0x9e, 0x56, 0x62, 0x0f, 0x55, 0x19, 0x71, 0xa3, 0x19, 0xfe, 0x35, 0x96, 0xbd, 0xa8, 0xc0, 0x47, 0xff, 0x86, 0xfb, 0x2d, 0xc3, 0xa7, 0xba, 0x47, 0xa8, 0x67, 0x93, 0x1e, 0xb1, 0xf4, 0x36, 0x8f, 0xa7, 0xdb, 0x56, 0x31, 0x51, 0x92, 0x36, 0x93, 0xf8, 0x5e, 0x00, 0xc0, 0xa1, 0x5e, 0x6c, 0x47, 0xb5, 0xd0, 0x7d, 0xc8, 0x36, 0x0d, 0x5f, 0x6f, 0xbb, 0x1e, 0x29, 0x26, 0x4b, 0xd2, 0x66, 0x16, 0x67, 0x9a, 0x86, 0x7f, 0xe8, 0x7a, 0x04, 0x61, 0x58, 0xf2, 0xfb, 0x8e, 0xa9, 0xfb, 0x4d, 0xc3, 0xb3, 0x74, 0x9f, 0x1a, 0xb4, 0xeb, 0x17, 0xe7, 0x4a, 0xd2, 0x8c, 0xad, 0x36, 0xfa, 0x8e, 0xd9, 0x08, 0xe0, 0x0d, 0x86, 0xc6, 0x05, 0x3f, 0x2a, 0x28, 0xff, 0x90, 0x86, 0xc2, 0xd8, 0x79, 0xd0, 0xff, 0x61, 0x3e, 0x48, 0x83, 0x4e, 0xfb, 0x1d, 0x52, 0x94, 0x4a, 0xd2, 0x66, 0x7e, 0x67, 0xeb, 0x76, 0xa9, 0xd0, 0xfa, 0x1d, 0x82, 0xb3, 0x54, 0xfc, 0x42, 0x8f, 0x21, 0xef, 0xbb, 0x5d, 0xcf, 0x24, 0x2c, 0xad, 0xc3, 0xb3, 0xe7, 0xb8, 0x34, 0xb0, 0x50, 0x2d, 0xf4, 0x1a, 0x16, 0x4d, 0x8f, 0x88, 0xf4, 0xdb, 0x6d, 0x7e, 0xec, 0x85, 0x9d, 0xb5, 0x0a, 0xaf, 0x9d, 0x4a, 0x58, 0x3b, 0x15, 0x2d, 0xac, 0x1d, 0x9c, 0x0b, 0x0d, 0x02, 0x11, 0x32, 0xe1, 0x1e, 0xaf, 0x07, 0x1e, 0xc6, 0xa0, 0xd4, 0xb3, 0x2f, 0xba, 0x94, 0x84, 0xc9, 0x79, 0x31, 0x65, 0xf3, 0x35, 0x66, 0x14, 0xec, 0x42, 0x19, 0x98, 0xec, 0xdf, 0xc1, 0x2b, 0x56, 0x8c, 0x1c, 0x7d, 0x23, 0xc1, 0xc3, 0x89, 0xec, 0x4f, 0x04, 0x4c, 0xb1, 0x80, 0x7f, 0xbf, 0xdd, 0x6d, 0x4c, 0x44, 0xde, 0xf0, 0x67, 0x01, 0x50, 0x0f, 0x18, 0x40, 0x37, 0x4c, 0x6a, 0xf7, 0x6c, 0xda, 0x9f, 0x88, 0x9e, 0x66, 0xd1, 0xb7, 0x67, 0x44, 0x57, 0x84, 0xe9, 0x44, 0xe8, 0x35, 0x7f, 0xaa, 0x16, 0xb5, 0x61, 0x4d, 0xbc, 0x25, 0x1e, 0xb1, 0xb7, 0x33, 0x1a, 0x34, 0xc3, 0x82, 0x56, 0xa6, 0x04, 0xdd, 0xe7, 0x86, 0x81, 0xc7, 0xb3, 0x9d, 0x48, 0xc4, 0xd5, 0x66, 0xbc, 0x0a, 0xb9, 0xb0, 0x76, 0x69, 0xd8, 0x2d, 0xb7, 0x47, 0x3c, 0xbd, 0x6d, 0x78, 0xd7, 0xc4, 0x1b, 0x0d, 0x97, 0x65, 0xe1, 0xaa, 0x53, 0xc2, 0xed, 0x09, 0xc3, 0x43, 0x66, 0x17, 0x89, 0x57, 0xbc, 0x9c, 0xa2, 0x7b, 0x93, 0x03, 0x18, 0x06, 0x28, 0xff, 0x92, 0x80, 0x95, 0xb8, 0xca, 0x40, 0xa7, 0x20, 0x8b, 0x32, 0x73, 0x3b, 0xc4, 0x63, 0xe5, 0x27, 0x5e, 0xc7, 0xd3, 0x99, 0x05, 0x76, 0x1c, 0xa2, 0x71, 0xc1, 0x8a, 0x0a, 0x50, 0x1e, 0x12, 0xe2, 0x51, 0xcc, 0xe3, 0x84, 0x6d, 0xa1, 0x97, 0x90, 0xe6, 0x10, 0xf1, 0x06, 0xd6, 0xc7, 0x1c, 0x77, 0xec, 0xa1, 0x5b, 0x2c, 0xa0, 0xe8, 0x09, 0xe4, 0x4d, 0xd7, 0xb9, 0xb4, 0xaf, 0xf4, 0x1e, 0xf1, 0xfc, 0x60, 0x57, 0x73, 0xec, 0x95, 0x2d, 0x72, 0xe9, 0x19, 0x17, 0xa2, 0xe7, 0x20, 0x0f, 0xd2, 0x1a, 0x02, 0x53, 0x0c, 0x58, 0x08, 0xe5, 0x21, 0xf4, 0x3f, 0x70, 0xbf, 0xe3, 0x91, 0x9e, 0xed, 0x76, 0x7d, 0x7d, 0xc2, 0x26, 0xcd, 0x6c, 0x56, 0x43, 0xc0, 0x5e, 0xd4, 0xb6, 0xfc, 0x93, 0x04, 0x1b, 0x33, 0xeb, 0x3c, 0xd8, 0xaf, 0x60, 0x05, 0xb3, 0xd5, 0xf5, 0x29, 0xf1, 0x58, 0x16, 0xe7, 0xf1, 0x22, 0x97, 0xee, 0x72, 0x61, 0x40, 0x84, 0xfc, 0xa9, 0x89, 0x0c, 0xa5, 0x70, 0x86, 0xad, 0x55, 0x0b, 0xfd, 0x0b, 0xe6, 0x07, 0x7d, 0xe4, 0x16, 0x6c, 0x31, 0x04, 0x97, 0x7f, 0x4b, 0xc1, 0xda, 0xf4, 0x77, 0x80, 0xd6, 0x61, 0x5e, 0x5c, 0xb1, 0x6d, 0x89, 0x5d, 0x65, 0xb9, 0x40, 0xb5, 0xd0, 0x3b, 0x40, 0x37, 0xae, 0x77, 0x7d, 0xd9, 0x72, 0x6f, 0x74, 0xf2, 0x81, 0x98, 0x5d, 0x56, 0x01, 0x89, 0x58, 0xfe, 0xe5, 0x17, 0xf5, 0x5e, 0xc0, 0xeb, 0x21, 0x1a, 0x2f, 0xdd, 0x8c, 0x8b, 0x50, 0x11, 0x32, 0x61, 0x6a, 0x93, 0x2c, 0xb5, 0xe1, 0x12, 0x3d, 0x84, 0x9c, 0x6f, 0x36, 0x89, 0xd5, 0x6d, 0x11, 0x96, 0x05, 0x7e, 0xad, 0x0b, 0x03, 0x99, 0x6a, 0x21, 0x05, 0xf2, 0x43, 0x08, 0x23, 0xcf, 0xd4, 0x47, 0xd3, 0xb1, 0x38, 0xb0, 0x60, 0xec, 0xb9, 0x01, 0xe0, 0x53, 0xc3, 0xa3, 0x3c, 0x06, 0xbf, 0xdd, 0x79, 0x21, 0x51, 0x2d, 0xf4, 0x3f, 0xc8, 0x85, 0x6a, 0xe6, 0x3f, 0xf3, 0x51, 0xff, 0x0b, 0x02, 0xcf, 0xbc, 0x7f, 0x01, 0xcb, 0xac, 0x13, 0x36, 0x89, 0xe1, 0xd1, 0x0b, 0x62, 0x50, 0xee, 0x25, 0xfb, 0x51, 0x2f, 0x4b, 0x81, 0xd9, 0x7e, 0x68, 0xc5, 0x7c, 0xfd, 0x03, 0x32, 0x16, 0xa1, 0x86, 0xdd, 0xf2, 0x8b, 0xf3, 0xcc, 0xfe, 0x41, 0x6c, 0xd6, 0x4f, 0x8c, 0x7e, 0xcb, 0x35, 0x2c, 0x1c, 0x82, 0x83, 0x0c, 0x1b, 0x94, 0x92, 0x76, 0x87, 0x16, 0x81, 0x17, 0x92, 0x58, 0xa2, 0xd7, 0x90, 0x63, 0xbb, 0x0b, 0x8a, 0xbc, 0xeb, 0x91, 0xe2, 0xc2, 0x0c, 0xb7, 0x7b, 0x1c, 0x83, 0x17, 0x02, 0x0b, 0xb1, 0x40, 0x7f, 0x83, 0x15, 0xe6, 0x20, 0xb8, 0x56, 0xe2, 0xe9, 0xb6, 0x45, 0x1c, 0x6a, 0xd3, 0x7e, 0x31, 0xc7, 0x6a, 0x07, 0x05, 0xba, 0xf7, 0x4c, 0xa5, 0x0a, 0x0d, 0x3a, 0x82, 0x82, 0xb8, 0x5f, 0x5d, 0x10, 0x60, 0x71, 0x91, 0x45, 0x7d, 0x32, 0x85, 0x44, 0xc4, 0xc3, 0x12, 0x44, 0x8a, 0xf3, 0xbd, 0xc8, 0xba, 0xfc, 0x6d, 0x12, 0x56, 0xa7, 0x90, 0x2c, 0x5a, 0x85, 0x4c, 0xd8, 0x78, 0x25, 0x76, 0xaf, 0x69, 0xca, 0x5b, 0x6e, 0xa4, 0xce, 0x13, 0xb7, 0xaa, 0xf3, 0xe4, 0xe7, 0xd6, 0xf9, 0xd7, 0x70, 0x77, 0xec, 0xe0, 0xba, 0x4d, 0x49, 0x3b, 0x68, 0xd2, 0xc1, 0xb0, 0xf5, 0xfc, 0x56, 0xc7, 0x57, 0x29, 0x69, 0xe3, 0xe5, 0xde, 0x84, 0xcc, 0x47, 0xaf, 0x20, 0x4d, 0x7a, 0xc4, 0xa1, 0x61, 0x0f, 0xde, 0x88, 0xa7, 0x4e, 0x83, 0x1a, 0x6f, 0x5a, 0xee, 0x05, 0x16, 0x60, 0xb4, 0x0b, 0x79, 0x87, 0xdc, 0xe8, 0x5e, 0xd7, 0xd1, 0x85, 0x79, 0xfa, 0x36, 0xe6, 0x39, 0x87, 0xdc, 0xe0, 0xae, 0x53, 0x67, 0x26, 0xe5, 0x9f, 0x25, 0x28, 0x4e, 0xeb, 0x3c, 0xb3, 0x39, 0x25, 0x8e, 0x94, 0x13, 0xf1, 0xa4, 0xfc, 0xb9, 0x63, 0x52, 0xf9, 0x7b, 0x09, 0x96, 0xa3, 0xbb, 0xd4, 0xdc, 0x6b, 0xe2, 0x04, 0x1b, 0x0c, 0x89, 0x96, 0x4f, 0xbe, 0x29, 0x9c, 0x15, 0x4c, 0xeb, 0xa3, 0x2f, 0xa1, 0x30, 0xd6, 0x8c, 0x05, 0xe3, 0x7d, 0x6a, 0x07, 0xc6, 0xf9, 0x68, 0xff, 0x2d, 0xff, 0x1a, 0x1d, 0xc8, 0xd9, 0x30, 0xe8, 0x5c, 0xba, 0x7f, 0x0a, 0x07, 0xaf, 0x8f, 0x4e, 0xbc, 0x49, 0xc6, 0x11, 0xc3, 0x29, 0x76, 0xe4, 0x15, 0xcd, 0x45, 0x5e, 0xd1, 0x08, 0x73, 0xa7, 0xa2, 0xcc, 0xfd, 0x18, 0xf2, 0x97, 0xb6, 0xe7, 0x53, 0x5e, 0x53, 0x43, 0x5e, 0xcd, 0x31, 0x29, 0xab, 0x1a, 0xd5, 0x42, 0x65, 0x58, 0x74, 0xc8, 0x87, 0x11, 0x50, 0x86, 0x13, 0x7c, 0x20, 0x0c, 0x31, 0xe3, 0x3d, 0x20, 0x3b, 0xd1, 0x03, 0x82, 0xea, 0x93, 0x47, 0x13, 0xc9, 0x2e, 0x75, 0xb4, 0x7b, 0x4a, 0xd1, 0xee, 0xf9, 0x19, 0x7f, 0x4e, 0x42, 0xd3, 0x8e, 0xe7, 0x9a, 0xc4, 0xf7, 0xa3, 0xa6, 0xc9, 0xa1, 0xe9, 0x49, 0xa8, 0x1f, 0x98, 0x96, 0xdf, 0x42, 0x61, 0x6c, 0x2c, 0x88, 0xb6, 0x71, 0xe9, 0x53, 0xda, 0xb8, 0x03, 0x2b, 0xe2, 0xf1, 0xd7, 0x0e, 0x4e, 0x77, 0xdd, 0xae, 0x43, 0xeb, 0x0e, 0xf5, 0xfa, 0x68, 0x05, 0x52, 0x66, 0xb0, 0x12, 0x74, 0xc7, 0x17, 0xb3, 0x26, 0x89, 0xc9, 0x59, 0x24, 0x19, 0x33, 0x8b, 0x6c, 0xfd, 0x3e, 0x59, 0xab, 0xac, 0x34, 0x1e, 0xc2, 0x06, 0xae, 0x9f, 0x1c, 0xa8, 0xbb, 0x8a, 0xa6, 0x1e, 0x1f, 0xe9, 0x9a, 0xd2, 0x78, 0xab, 0x6b, 0xe7, 0x27, 0x75, 0x5d, 0x3d, 0x3a, 0x53, 0x0e, 0xd4, 0x9a, 0x7c, 0x07, 0x95, 0xe0, 0x41, 0x3c, 0xa4, 0x76, 0x7c, 0xa8, 0xa8, 0x47, 0xb2, 0x34, 0xdd, 0xc9, 0xbe, 0xda, 0xd0, 0x8e, 0xf1, 0xb9, 0x9c, 0x40, 0x2f, 0xe0, 0x59, 0x3c, 0xa4, 0x71, 0x7e, 0xb4, 0xab, 0x37, 0xf6, 0x15, 0x5c, 0xd3, 0x1b, 0x9a, 0xa2, 0xbd, 0x6b, 0xc8, 0x49, 0xf4, 0x0c, 0x1e, 0xcd, 0x00, 0x2b, 0xbb, 0x9a, 0x7a, 0xa6, 0x6a, 0xe7, 0xf2, 0x1c, 0xda, 0x82, 0xa7, 0x33, 0x03, 0xeb, 0x87, 0x75, 0x4d, 0xa9, 0x29, 0x9a, 0x22, 0xa7, 0xd0, 0x63, 0x28, 0xcd, 0xc6, 0x9e, 0xed, 0xc8, 0x69, 0xf4, 0x1c, 0x9e, 0xc4, 0xa3, 0xf6, 0x14, 0xf5, 0xe0, 0xf8, 0xac, 0x8e, 0xf5, 0x43, 0x05, 0xbf, 0xad, 0x63, 0x39, 0xb3, 0xf5, 0x9d, 0x04, 0x85, 0xb1, 0xf9, 0x18, 0x3d, 0x80, 0x22, 0xcf, 0x8a, 0x7e, 0x7c, 0x52, 0xc7, 0xdc, 0xc7, 0x30, 0x93, 0xeb, 0xb0, 0x3a, 0xa1, 0xdd, 0xc5, 0x75, 0x45, 0xab, 0xcb, 0x52, 0xac, 0xf2, 0xdd, 0x49, 0x2d, 0x50, 0x26, 0x62, 0x95, 0xb5, 0xfa, 0x41, 0x5d, 0xab, 0xcb, 0xc9, 0xad, 0x23, 0xc8, 0xd4, 0x0e, 0x4e, 0xd9, 0x75, 0xae, 0x80, 0x5c, 0x3b, 0x38, 0x1d, 0xbf, 0xc1, 0x22, 0xac, 0x0c, 0xa4, 0x23, 0xa7, 0x93, 0x25, 0xb4, 0x0c, 0x85, 0x81, 0x46, 0x5c, 0x67, 0xe2, 0xcd, 0x3f, 0xbf, 0x7a, 0x75, 0x65, 0xd3, 0x66, 0xf7, 0xa2, 0x62, 0xba, 0xed, 0xea, 0xe8, 0xa7, 0x89, 0xbf, 0xda, 0x56, 0xab, 0x7a, 0xe5, 0xf2, 0x8f, 0x21, 0x83, 0xef, 0x14, 0xff, 0x65, 0x3f, 0x7a, 0xdb, 0x17, 0x69, 0x26, 0x7f, 0xf9, 0x47, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x9c, 0xe5, 0x90, 0x74, 0x11, 0x00, 0x00, }, // uber/cadence/api/v1/domain.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xdd, 0x72, 0xdb, 0x44, 0x14, 0x46, 0xce, 0x0f, 0xe9, 0xb1, 0xe3, 0x38, 0x9b, 0xa4, 0x51, 0x0d, 0x43, 0x5d, 0x97, 0x32, 0xa6, 0x33, 0xc8, 0x4d, 0x60, 0xa0, 0xe5, 0x67, 0x06, 0x3b, 0x56, 0x8b, 0x99, 0x10, 0x32, 0xb2, 0x1b, 0x66, 0xe8, 0x85, 0x66, 0x2d, 0xad, 0xed, 0xa5, 0xb2, 0x56, 0xb3, 0x5a, 0xbb, 0xf5, 0x15, 0x0c, 0x3c, 0x05, 0x2f, 0xc0, 0x43, 0x70, 0xc9, 0x4b, 0x71, 0xcb, 0x68, 0xb5, 0x52, 0x2c, 0x5b, 0xf9, 0xb9, 0xe1, 0x6e, 0xf7, 0xfc, 0x7c, 0xe7, 0xec, 0xd9, 0xef, 0x9c, 0x95, 0xa0, 0x36, 0x1d, 0x10, 0xde, 0x74, 0xb0, 0x4b, 0x7c, 0x87, 0x34, 0x71, 0x40, 0x9b, 0xb3, 0xa3, 0xa6, 0xcb, 0x26, 0x98, 0xfa, 0x46, 0xc0, 0x99, 0x60, 0x68, 0x2f, 0xb2, 0x30, 0x94, 0x85, 0x81, 0x03, 0x6a, 0xcc, 0x8e, 0xaa, 0x1f, 0x8c, 0x18, 0x1b, 0x79, 0xa4, 0x29, 0x4d, 0x06, 0xd3, 0x61, 0xd3, 0x9d, 0x72, 0x2c, 0x28, 0x53, 0x4e, 0xd5, 0xfb, 0xcb, 0x7a, 0x41, 0x27, 0x24, 0x14, 0x78, 0x12, 0x28, 0x83, 0xdc, 0xb8, 0x0e, 0x9b, 0x4c, 0x12, 0x88, 0xfa, 0x5f, 0x00, 0x9b, 0x1d, 0x99, 0x08, 0x2a, 0x43, 0x81, 0xba, 0xba, 0x56, 0xd3, 0x1a, 0x77, 0xac, 0x02, 0x75, 0x11, 0x82, 0x75, 0x1f, 0x4f, 0x88, 0x5e, 0x90, 0x12, 0xb9, 0x46, 0xcf, 0x60, 0x33, 0x14, 0x58, 0x4c, 0x43, 0x7d, 0xad, 0xa6, 0x35, 0xca, 0xc7, 0x0f, 0x8c, 0x9c, 0xbc, 0x8d, 0x18, 0xb0, 0x27, 0x0d, 0x2d, 0xe5, 0x80, 0x6a, 0x50, 0x74, 0x49, 0xe8, 0x70, 0x1a, 0x44, 0x27, 0xd0, 0xd7, 0x25, 0xea, 0xa2, 0x08, 0xdd, 0x87, 0x22, 0x7b, 0xe3, 0x13, 0x6e, 0x93, 0x09, 0xa6, 0x9e, 0xbe, 0x21, 0x2d, 0x40, 0x8a, 0xcc, 0x48, 0x82, 0x9e, 0xc1, 0xba, 0x8b, 0x05, 0xd6, 0x37, 0x6b, 0x6b, 0x8d, 0xe2, 0xf1, 0xa3, 0x6b, 0x62, 0x1b, 0x1d, 0x2c, 0xb0, 0xe9, 0x0b, 0x3e, 0xb7, 0xa4, 0x0b, 0x1a, 0xc3, 0xc3, 0x37, 0x8c, 0xbf, 0x1e, 0x7a, 0xec, 0x8d, 0x4d, 0xde, 0x12, 0x67, 0x1a, 0x45, 0xb4, 0x39, 0x11, 0xc4, 0x97, 0xab, 0x80, 0x70, 0xca, 0x5c, 0xfd, 0xdd, 0x9a, 0xd6, 0x28, 0x1e, 0xdf, 0x33, 0xe2, 0xc2, 0x1a, 0x49, 0x61, 0x8d, 0x8e, 0x2a, 0xbc, 0x55, 0x4b, 0x50, 0xcc, 0x04, 0xc4, 0x4a, 0x30, 0xce, 0x25, 0x04, 0x3a, 0x81, 0xd2, 0x00, 0xbb, 0xf6, 0x80, 0xfa, 0x98, 0x53, 0x12, 0xea, 0x5b, 0x12, 0xb2, 0x96, 0x9b, 0x6c, 0x1b, 0xbb, 0x6d, 0x65, 0x67, 0x15, 0x07, 0x97, 0x1b, 0xf4, 0x0a, 0x0e, 0xc7, 0x34, 0x14, 0x8c, 0xcf, 0x6d, 0xcc, 0x9d, 0x31, 0x9d, 0x61, 0xcf, 0x56, 0x85, 0xbf, 0x23, 0x0b, 0xff, 0x30, 0x17, 0xaf, 0xa5, 0x6c, 0x55, 0xe9, 0x0f, 0x14, 0x46, 0x56, 0x8c, 0x9e, 0xc0, 0xfe, 0x0a, 0xf8, 0x94, 0x53, 0x1d, 0x64, 0xc1, 0xd1, 0x92, 0xd3, 0x4b, 0x4e, 0x11, 0x86, 0xea, 0x8c, 0x86, 0x74, 0x40, 0x3d, 0x2a, 0x56, 0x33, 0x2a, 0xde, 0x3e, 0x23, 0xfd, 0x12, 0x66, 0x29, 0xa9, 0xcf, 0xe1, 0x30, 0x2f, 0x44, 0x94, 0x57, 0x49, 0xe6, 0x75, 0xb0, 0xea, 0x1a, 0xa5, 0x66, 0xc0, 0x1e, 0x76, 0x04, 0x9d, 0x11, 0xdb, 0xf1, 0xa6, 0xa1, 0x20, 0xdc, 0x96, 0xa4, 0xdd, 0x96, 0x3e, 0xbb, 0xb1, 0xea, 0x24, 0xd6, 0x9c, 0x45, 0x0c, 0x3e, 0x87, 0x2d, 0x65, 0x18, 0xea, 0x65, 0xc9, 0xa3, 0xcf, 0x72, 0x13, 0x57, 0x3e, 0x16, 0x09, 0x3c, 0xea, 0xc8, 0xbb, 0x3f, 0x61, 0xfe, 0x90, 0x8e, 0x12, 0x22, 0xa4, 0x28, 0xe8, 0x63, 0xa8, 0x0c, 0x31, 0xf5, 0xd8, 0x8c, 0x70, 0x7b, 0x46, 0x78, 0x18, 0xb1, 0x7b, 0xa7, 0xa6, 0x35, 0xd6, 0xac, 0x9d, 0x44, 0x7e, 0x11, 0x8b, 0x51, 0x03, 0x2a, 0x34, 0xb4, 0x47, 0x1e, 0x1b, 0x60, 0xcf, 0x8e, 0xfb, 0x5f, 0xaf, 0xd4, 0xb4, 0xc6, 0x96, 0x55, 0xa6, 0xe1, 0x0b, 0x29, 0x56, 0xcd, 0xf8, 0x1c, 0xb6, 0x53, 0x50, 0xea, 0x0f, 0x99, 0xbe, 0x2b, 0x69, 0x94, 0xdf, 0x6f, 0xcf, 0x95, 0x65, 0xd7, 0x1f, 0x32, 0xab, 0x34, 0x5c, 0xd8, 0xa1, 0x57, 0x51, 0x44, 0xe6, 0xc9, 0x9c, 0xed, 0x11, 0x67, 0xd3, 0x20, 0xd4, 0x91, 0x84, 0x7a, 0x92, 0x0b, 0xd5, 0x4d, 0x8c, 0x5f, 0x44, 0xb6, 0xd9, 0x23, 0xef, 0xd0, 0x8c, 0x32, 0x44, 0x0e, 0x1c, 0xe0, 0x70, 0xee, 0x3b, 0x76, 0xda, 0x5a, 0x8e, 0x74, 0xd0, 0xf7, 0x64, 0x84, 0x66, 0x3e, 0x23, 0x22, 0x8f, 0x9f, 0x94, 0x43, 0x36, 0xc0, 0x1e, 0x5e, 0xd5, 0xa1, 0x53, 0xd8, 0xc9, 0x5e, 0x70, 0xa8, 0xef, 0x4b, 0xf8, 0x2b, 0x08, 0xb7, 0x78, 0xe3, 0xa1, 0x55, 0xce, 0x30, 0x20, 0xac, 0x7e, 0x01, 0x77, 0xd2, 0xd1, 0x80, 0x2a, 0xb0, 0xf6, 0x9a, 0xcc, 0xd5, 0xc8, 0x8b, 0x96, 0x68, 0x1f, 0x36, 0x66, 0xd8, 0x9b, 0x26, 0x43, 0x2f, 0xde, 0x7c, 0x59, 0x78, 0xaa, 0xd5, 0x3b, 0x70, 0xff, 0x06, 0x4a, 0xa0, 0x07, 0x50, 0xca, 0x70, 0x30, 0xc6, 0x2d, 0x3a, 0x97, 0xec, 0xab, 0xff, 0xad, 0x41, 0x71, 0xa1, 0xe9, 0xd1, 0xf7, 0xb0, 0x95, 0x0e, 0x0a, 0x4d, 0xb2, 0xd1, 0xb8, 0x69, 0x50, 0x18, 0xc9, 0x22, 0x1e, 0x6f, 0xa9, 0x7f, 0xd5, 0x86, 0xed, 0x8c, 0x2a, 0xe7, 0x78, 0x4f, 0x17, 0x8f, 0x57, 0x3c, 0xae, 0x5f, 0x1b, 0x6b, 0x2e, 0xe9, 0xb4, 0x50, 0x82, 0xdf, 0x35, 0xd8, 0xce, 0x28, 0xd1, 0x5d, 0xd8, 0xe4, 0x04, 0x87, 0xcc, 0x57, 0x41, 0xd4, 0x0e, 0x55, 0x61, 0x8b, 0x05, 0x84, 0x63, 0xc1, 0xb8, 0xaa, 0x64, 0xba, 0x47, 0xdf, 0x40, 0xc9, 0xe1, 0x04, 0x0b, 0xe2, 0xda, 0xd1, 0x73, 0x25, 0x1f, 0x92, 0xe2, 0x71, 0x75, 0x65, 0xe4, 0xf6, 0x93, 0xb7, 0xcc, 0x2a, 0x2a, 0xfb, 0x48, 0x52, 0xff, 0xa7, 0x00, 0xa5, 0x45, 0xbe, 0xe7, 0xb6, 0x9f, 0x96, 0xdf, 0x7e, 0x7d, 0xd0, 0x53, 0xd3, 0x50, 0x60, 0x2e, 0xec, 0xf4, 0xc1, 0x54, 0x15, 0xb9, 0x2e, 0x8d, 0xbb, 0x89, 0x6f, 0x2f, 0x72, 0x4d, 0xe5, 0xe8, 0x02, 0xee, 0xa5, 0xa8, 0xe4, 0x6d, 0x40, 0x39, 0x59, 0x80, 0xbd, 0xf9, 0x74, 0x87, 0x89, 0xb3, 0x29, 0x7d, 0x2f, 0x71, 0x8f, 0xe1, 0xc0, 0x61, 0x93, 0xc0, 0x23, 0x51, 0xa9, 0xc2, 0x31, 0xe6, 0xae, 0xed, 0xb0, 0xa9, 0x2f, 0xe4, 0xd3, 0xb9, 0x61, 0xed, 0xa5, 0xca, 0x5e, 0xa4, 0x3b, 0x89, 0x54, 0xe8, 0x11, 0x94, 0x03, 0xe2, 0xbb, 0xd4, 0x1f, 0xc5, 0x1e, 0xa1, 0xbe, 0x51, 0x5b, 0x6b, 0x6c, 0x58, 0xdb, 0x4a, 0x2a, 0x4d, 0xc3, 0xfa, 0x1f, 0xeb, 0x50, 0xce, 0x36, 0x0a, 0x1a, 0xc3, 0x2e, 0x27, 0xa3, 0x68, 0x4a, 0x08, 0x96, 0x74, 0x9a, 0xa2, 0xe4, 0xd3, 0x5b, 0x34, 0x9a, 0x61, 0x49, 0xe7, 0x3e, 0x53, 0x02, 0xc9, 0xc0, 0x76, 0x41, 0xd7, 0xac, 0x1d, 0x9e, 0xd5, 0xa0, 0x3f, 0x35, 0xf8, 0x70, 0xa9, 0xa3, 0xed, 0xc1, 0x3c, 0x1d, 0xdf, 0x58, 0x08, 0x4e, 0x07, 0x53, 0x11, 0x91, 0x34, 0x8a, 0xde, 0xbd, 0x4d, 0xf4, 0xec, 0xb6, 0x3d, 0x57, 0xab, 0x56, 0x82, 0x15, 0xf7, 0x4a, 0x0d, 0xdf, 0x60, 0x56, 0xfd, 0x05, 0xf6, 0xf3, 0x0e, 0x92, 0xd3, 0x4a, 0x5f, 0x67, 0x5b, 0xe9, 0xa3, 0x9b, 0xb3, 0x5c, 0x6a, 0xa7, 0xea, 0xaf, 0xf0, 0xe8, 0x56, 0x69, 0xe7, 0x04, 0xff, 0x36, 0x1b, 0xfc, 0xf1, 0x75, 0x2f, 0x58, 0x0a, 0xd6, 0x73, 0x58, 0x40, 0x16, 0xfb, 0xf9, 0x5f, 0x0d, 0x0e, 0x72, 0x8d, 0x50, 0x00, 0x68, 0xe5, 0x3a, 0x92, 0x01, 0xd5, 0xba, 0x7d, 0xb0, 0x15, 0xa9, 0x9a, 0x59, 0xbb, 0xce, 0xb2, 0xbc, 0xea, 0xc1, 0xdd, 0x7c, 0xe3, 0xff, 0xa3, 0xf4, 0x75, 0x1f, 0x76, 0x57, 0xf4, 0x57, 0x7d, 0x49, 0x68, 0x57, 0x7d, 0x49, 0xe4, 0x0d, 0x9e, 0x42, 0xee, 0xe0, 0x79, 0xfc, 0x9b, 0x06, 0xa5, 0xc5, 0x8f, 0x62, 0x74, 0x0f, 0x0e, 0x3a, 0x3f, 0xfe, 0xd0, 0xea, 0x9e, 0xd9, 0xbd, 0x7e, 0xab, 0xff, 0xb2, 0x67, 0x77, 0xcf, 0x2e, 0x5a, 0xa7, 0xdd, 0x4e, 0xe5, 0x1d, 0xf4, 0x3e, 0xe8, 0x59, 0x95, 0x65, 0xbe, 0xe8, 0xf6, 0xfa, 0xa6, 0x65, 0x76, 0x2a, 0xda, 0xaa, 0xb6, 0x63, 0x9e, 0x5b, 0xe6, 0x49, 0xab, 0x6f, 0x76, 0x2a, 0x85, 0x55, 0xd8, 0x8e, 0x79, 0x6a, 0x46, 0xaa, 0xb5, 0xc7, 0x63, 0x28, 0x2f, 0x7d, 0x71, 0xbd, 0x07, 0x87, 0x2d, 0xeb, 0xe4, 0xbb, 0xee, 0x45, 0xeb, 0x34, 0x37, 0x8b, 0x65, 0x65, 0xa7, 0xdb, 0x6b, 0xb5, 0x4f, 0x65, 0x16, 0x39, 0xae, 0xe6, 0x59, 0xac, 0x2c, 0xb4, 0x5f, 0xc1, 0xa1, 0xc3, 0x26, 0x79, 0x97, 0xd2, 0x2e, 0xc6, 0x45, 0x38, 0x8f, 0xa6, 0xe0, 0xb9, 0xf6, 0xf3, 0xd1, 0x88, 0x8a, 0xf1, 0x74, 0x60, 0x38, 0x6c, 0xd2, 0x5c, 0xfc, 0x53, 0xf9, 0x84, 0xba, 0x5e, 0x73, 0xc4, 0xe2, 0xff, 0x1a, 0xf5, 0xdb, 0xf2, 0x15, 0x0e, 0xe8, 0xec, 0x68, 0xb0, 0x29, 0x65, 0x9f, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0x35, 0x67, 0x87, 0x48, 0x52, 0x0d, 0x00, 0x00, }, // uber/cadence/shared/v1/any.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x28, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x2f, 0xce, 0x48, 0x2c, 0x4a, 0x4d, 0xd1, 0x2f, 0x33, 0xd4, 0x4f, 0xcc, 0xab, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x03, 0xa9, 0xd0, 0x83, 0xaa, 0xd0, 0x83, 0xa8, 0xd0, 0x2b, 0x33, 0x54, 0xb2, 0xe2, 0x62, 0x76, 0xcc, 0xab, 0x14, 0x92, 0xe5, 0xe2, 0x2a, 0x4b, 0xcc, 0x29, 0x4d, 0x8d, 0x2f, 0xa9, 0x2c, 0x48, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0xe2, 0x04, 0x8b, 0x84, 0x54, 0x16, 0xa4, 0x0a, 0x89, 0x70, 0xb1, 0x82, 0x39, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x10, 0x8e, 0x93, 0x79, 0x94, 0x69, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0x8a, 0x13, 0xf4, 0xd2, 0x53, 0xf3, 0xf4, 0xc1, 0x36, 0x23, 0x5c, 0x63, 0x0d, 0x61, 0x95, 0x19, 0x26, 0xb1, 0x81, 0x65, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x4e, 0x0a, 0xa2, 0x2e, 0xb7, 0x00, 0x00, 0x00, }, // uber/cadence/shared/v1/history.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x4f, 0x4b, 0xfb, 0x30, 0x18, 0xc7, 0xe9, 0x7e, 0xfc, 0x04, 0x33, 0xff, 0x51, 0x50, 0x46, 0x41, 0xd8, 0xa6, 0xc2, 0x4e, 0x09, 0x9d, 0x88, 0x07, 0x4f, 0xfe, 0xc5, 0x79, 0x2c, 0xe2, 0xc1, 0x4b, 0x49, 0x93, 0xc7, 0x35, 0xe0, 0x92, 0x92, 0xa4, 0xc1, 0xbd, 0x15, 0xdf, 0x82, 0x6f, 0x52, 0xd2, 0xd6, 0x8d, 0xb8, 0x8b, 0xb7, 0x3e, 0x3c, 0x9f, 0xcf, 0x97, 0x6f, 0x9f, 0xa0, 0xd3, 0xba, 0x00, 0x4d, 0x18, 0xe5, 0x20, 0x19, 0x10, 0x53, 0x52, 0x0d, 0x9c, 0xb8, 0x94, 0x94, 0xc2, 0x58, 0xa5, 0x97, 0xb8, 0xd2, 0xca, 0xaa, 0xf8, 0xc8, 0x53, 0xb8, 0xa3, 0x70, 0x4b, 0x61, 0x97, 0x26, 0xa3, 0xc0, 0xa6, 0x95, 0xd8, 0x50, 0x93, 0x93, 0x10, 0xe1, 0x0b, 0x21, 0x37, 0xa0, 0xf1, 0x57, 0x84, 0x0e, 0x9f, 0x35, 0x95, 0x46, 0x80, 0xb4, 0x77, 0xc0, 0x84, 0x11, 0x4a, 0xce, 0xe4, 0x9b, 0x8a, 0x9f, 0xd0, 0xbe, 0x61, 0x25, 0xf0, 0xfa, 0x1d, 0x78, 0x0e, 0x0e, 0xa4, 0x1d, 0x44, 0xc3, 0x68, 0xd2, 0x9f, 0x8e, 0x70, 0xd0, 0x89, 0x56, 0x02, 0xbb, 0x14, 0x3f, 0xb6, 0xb1, 0xf7, 0x1e, 0xcc, 0xf6, 0x56, 0x66, 0x33, 0xc7, 0x0f, 0x68, 0xd7, 0x58, 0xaa, 0xed, 0x2a, 0xa9, 0xf7, 0xd7, 0xa4, 0x9d, 0xce, 0x6b, 0xa6, 0xf1, 0x67, 0x84, 0x0e, 0x5e, 0x40, 0xfb, 0x8e, 0x2d, 0x25, 0xc0, 0xc4, 0xd7, 0xe8, 0x98, 0xd5, 0x5a, 0x83, 0xb4, 0xb9, 0x6b, 0x77, 0x79, 0xf7, 0x8f, 0xb9, 0x90, 0x1c, 0x3e, 0x9a, 0xda, 0xff, 0xb3, 0xa4, 0x83, 0x02, 0x7f, 0x39, 0xf3, 0x44, 0x7c, 0x8b, 0xb6, 0xcb, 0x9f, 0xbc, 0x41, 0x6f, 0xf8, 0x6f, 0xd2, 0x9f, 0x9e, 0xfd, 0xea, 0xe6, 0xcf, 0xe7, 0xdb, 0x85, 0x7a, 0xb6, 0xf6, 0x6e, 0x2e, 0x5f, 0x2f, 0xe6, 0xc2, 0x96, 0x75, 0x81, 0x99, 0x5a, 0x90, 0xe0, 0xf8, 0x78, 0x0e, 0x92, 0x34, 0x07, 0x5f, 0x3f, 0xf4, 0x55, 0xfb, 0xe5, 0xd2, 0x62, 0xab, 0xd9, 0x9c, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0xc6, 0xf4, 0xa6, 0x9c, 0x12, 0x02, 0x00, 0x00, }, // uber/cadence/shared/v1/workflow.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x2f, 0xce, 0x48, 0x2c, 0x4a, 0x4d, 0xd1, 0x2f, 0x33, 0xd4, 0x2f, 0xcf, 0x2f, 0xca, 0x4e, 0xcb, 0xc9, 0x2f, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x03, 0x29, 0xd3, 0x83, 0x2a, 0xd3, 0x83, 0x28, 0xd3, 0x2b, 0x33, 0xd4, 0xba, 0xcc, 0xc8, 0xc5, 0x1b, 0x0e, 0x55, 0x1a, 0x5c, 0x92, 0x58, 0x92, 0x2a, 0x24, 0xc5, 0x25, 0x16, 0xee, 0x1f, 0xe4, 0xed, 0xe6, 0xe3, 0x1f, 0x1e, 0x1f, 0x1c, 0xe2, 0x18, 0xe2, 0x1a, 0xef, 0xe9, 0x17, 0xe6, 0xe8, 0xe3, 0xe9, 0x22, 0xc0, 0x80, 0x45, 0xce, 0x39, 0xc8, 0xd5, 0x31, 0xc4, 0xd5, 0x45, 0x80, 0x11, 0x8b, 0x5c, 0x50, 0xa8, 0x9f, 0x9f, 0xa7, 0x9f, 0xbb, 0x00, 0x93, 0x90, 0x0c, 0x97, 0x04, 0xba, 0x3e, 0x7f, 0xdf, 0x00, 0x1f, 0x57, 0x90, 0x4e, 0x66, 0x21, 0x49, 0x2e, 0x51, 0x34, 0xd9, 0x28, 0x7f, 0x5f, 0x27, 0x4f, 0x57, 0x01, 0x16, 0x21, 0x71, 0x2e, 0x61, 0x34, 0xa9, 0x30, 0x7f, 0x4f, 0x17, 0x01, 0x56, 0xac, 0x26, 0x06, 0x05, 0x85, 0x06, 0x80, 0x4c, 0x64, 0x73, 0x32, 0x8f, 0x32, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x47, 0x09, 0x21, 0xbd, 0xf4, 0xd4, 0x3c, 0x7d, 0x70, 0x98, 0x20, 0x02, 0xcb, 0x1a, 0xc2, 0x2a, 0x33, 0x4c, 0x62, 0x03, 0xcb, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xff, 0xc3, 0x37, 0x89, 0x56, 0x01, 0x00, 0x00, }, } func init() { yarpc.RegisterClientBuilder( func(clientConfig transport.ClientConfig, structField reflect.StructField) HistoryAPIYARPCClient { return NewHistoryAPIYARPCClient(clientConfig, protobuf.ClientBuilderOptions(clientConfig, structField)...) }, ) } ================================================ FILE: .gen/proto/indexer/v1/messages.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/indexer/v1/messages.proto package indexerv1 import ( fmt "fmt" io "io" math "math" math_bits "math/bits" proto "github.com/gogo/protobuf/proto" v1 "github.com/uber/cadence-idl/go/proto/api/v1" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type MessageType int32 const ( MessageType_MESSAGE_TYPE_INVALID MessageType = 0 MessageType_MESSAGE_TYPE_INDEX MessageType = 1 MessageType_MESSAGE_TYPE_DELETE MessageType = 2 ) var MessageType_name = map[int32]string{ 0: "MESSAGE_TYPE_INVALID", 1: "MESSAGE_TYPE_INDEX", 2: "MESSAGE_TYPE_DELETE", } var MessageType_value = map[string]int32{ "MESSAGE_TYPE_INVALID": 0, "MESSAGE_TYPE_INDEX": 1, "MESSAGE_TYPE_DELETE": 2, } func (x MessageType) String() string { return proto.EnumName(MessageType_name, int32(x)) } func (MessageType) EnumDescriptor() ([]byte, []int) { return fileDescriptor_60256a432328b016, []int{0} } type Message struct { MessageType MessageType `protobuf:"varint,1,opt,name=message_type,json=messageType,proto3,enum=uber.cadence.indexer.v1.MessageType" json:"message_type,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,3,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` Version int64 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"` Fields map[string]*Field `protobuf:"bytes,5,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *Message) Reset() { *m = Message{} } func (m *Message) String() string { return proto.CompactTextString(m) } func (*Message) ProtoMessage() {} func (*Message) Descriptor() ([]byte, []int) { return fileDescriptor_60256a432328b016, []int{0} } func (m *Message) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_Message.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *Message) XXX_Merge(src proto.Message) { xxx_messageInfo_Message.Merge(m, src) } func (m *Message) XXX_Size() int { return m.Size() } func (m *Message) XXX_DiscardUnknown() { xxx_messageInfo_Message.DiscardUnknown(m) } var xxx_messageInfo_Message proto.InternalMessageInfo func (m *Message) GetMessageType() MessageType { if m != nil { return m.MessageType } return MessageType_MESSAGE_TYPE_INVALID } func (m *Message) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *Message) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *Message) GetVersion() int64 { if m != nil { return m.Version } return 0 } func (m *Message) GetFields() map[string]*Field { if m != nil { return m.Fields } return nil } type Field struct { // Types that are valid to be assigned to Data: // *Field_StringData // *Field_IntData // *Field_BoolData // *Field_BinaryData Data isField_Data `protobuf_oneof:"data"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *Field) Reset() { *m = Field{} } func (m *Field) String() string { return proto.CompactTextString(m) } func (*Field) ProtoMessage() {} func (*Field) Descriptor() ([]byte, []int) { return fileDescriptor_60256a432328b016, []int{1} } func (m *Field) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *Field) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_Field.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *Field) XXX_Merge(src proto.Message) { xxx_messageInfo_Field.Merge(m, src) } func (m *Field) XXX_Size() int { return m.Size() } func (m *Field) XXX_DiscardUnknown() { xxx_messageInfo_Field.DiscardUnknown(m) } var xxx_messageInfo_Field proto.InternalMessageInfo type isField_Data interface { isField_Data() MarshalTo([]byte) (int, error) Size() int } type Field_StringData struct { StringData string `protobuf:"bytes,1,opt,name=string_data,json=stringData,proto3,oneof" json:"string_data,omitempty"` } type Field_IntData struct { IntData int64 `protobuf:"varint,2,opt,name=int_data,json=intData,proto3,oneof" json:"int_data,omitempty"` } type Field_BoolData struct { BoolData bool `protobuf:"varint,3,opt,name=bool_data,json=boolData,proto3,oneof" json:"bool_data,omitempty"` } type Field_BinaryData struct { BinaryData []byte `protobuf:"bytes,4,opt,name=binary_data,json=binaryData,proto3,oneof" json:"binary_data,omitempty"` } func (*Field_StringData) isField_Data() {} func (*Field_IntData) isField_Data() {} func (*Field_BoolData) isField_Data() {} func (*Field_BinaryData) isField_Data() {} func (m *Field) GetData() isField_Data { if m != nil { return m.Data } return nil } func (m *Field) GetStringData() string { if x, ok := m.GetData().(*Field_StringData); ok { return x.StringData } return "" } func (m *Field) GetIntData() int64 { if x, ok := m.GetData().(*Field_IntData); ok { return x.IntData } return 0 } func (m *Field) GetBoolData() bool { if x, ok := m.GetData().(*Field_BoolData); ok { return x.BoolData } return false } func (m *Field) GetBinaryData() []byte { if x, ok := m.GetData().(*Field_BinaryData); ok { return x.BinaryData } return nil } // XXX_OneofWrappers is for the internal use of the proto package. func (*Field) XXX_OneofWrappers() []interface{} { return []interface{}{ (*Field_StringData)(nil), (*Field_IntData)(nil), (*Field_BoolData)(nil), (*Field_BinaryData)(nil), } } func init() { proto.RegisterEnum("uber.cadence.indexer.v1.MessageType", MessageType_name, MessageType_value) proto.RegisterType((*Message)(nil), "uber.cadence.indexer.v1.Message") proto.RegisterMapType((map[string]*Field)(nil), "uber.cadence.indexer.v1.Message.FieldsEntry") proto.RegisterType((*Field)(nil), "uber.cadence.indexer.v1.Field") } func init() { proto.RegisterFile("uber/cadence/indexer/v1/messages.proto", fileDescriptor_60256a432328b016) } var fileDescriptor_60256a432328b016 = []byte{ // 488 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xdf, 0x6a, 0x13, 0x41, 0x14, 0xc6, 0x33, 0xd9, 0xe6, 0xdf, 0xd9, 0x22, 0x71, 0x14, 0xbb, 0xb4, 0x18, 0xb6, 0x45, 0x4a, 0x10, 0xd9, 0x25, 0x51, 0x50, 0xf4, 0xaa, 0x25, 0x6b, 0x13, 0x68, 0x45, 0xa6, 0x51, 0x5b, 0x6f, 0x96, 0x49, 0x76, 0x1a, 0x87, 0x66, 0x67, 0xc2, 0xee, 0x24, 0xe9, 0x5e, 0xfa, 0x08, 0xbe, 0x95, 0x97, 0x3e, 0x82, 0xe4, 0x49, 0x64, 0x76, 0xb6, 0x98, 0x14, 0x4a, 0xef, 0x66, 0xbe, 0xef, 0x77, 0xbe, 0xc3, 0x9c, 0x39, 0x70, 0x38, 0x1f, 0xb1, 0xc4, 0x1f, 0xd3, 0x88, 0x89, 0x31, 0xf3, 0xb9, 0x88, 0xd8, 0x0d, 0x4b, 0xfc, 0x45, 0xc7, 0x8f, 0x59, 0x9a, 0xd2, 0x09, 0x4b, 0xbd, 0x59, 0x22, 0x95, 0xc4, 0x3b, 0x9a, 0xf3, 0x0a, 0xce, 0x2b, 0x38, 0x6f, 0xd1, 0xd9, 0x75, 0x37, 0x02, 0xe8, 0x8c, 0xeb, 0xe2, 0xb1, 0x8c, 0x63, 0x29, 0x4c, 0xe9, 0xc1, 0x4f, 0x0b, 0x6a, 0x67, 0x26, 0x0d, 0x9f, 0xc0, 0x76, 0x11, 0x1c, 0xaa, 0x6c, 0xc6, 0x1c, 0xe4, 0xa2, 0xf6, 0xa3, 0xee, 0x0b, 0xef, 0x9e, 0x74, 0xaf, 0xa8, 0x1b, 0x66, 0x33, 0x46, 0xec, 0xf8, 0xff, 0x05, 0xef, 0x41, 0x23, 0x92, 0x31, 0xe5, 0x22, 0xe4, 0x91, 0x53, 0x76, 0x51, 0xbb, 0x41, 0xea, 0x46, 0x18, 0x44, 0xf8, 0x0b, 0xe0, 0xa5, 0x4c, 0xae, 0xaf, 0xa6, 0x72, 0x19, 0xb2, 0x1b, 0x36, 0x9e, 0x2b, 0x2e, 0x85, 0x63, 0xb9, 0xa8, 0x6d, 0x77, 0x0f, 0x37, 0x7b, 0xd1, 0x19, 0xd7, 0x7d, 0xbe, 0x15, 0x78, 0x70, 0x4b, 0x93, 0xc7, 0xcb, 0xbb, 0x12, 0x76, 0xa0, 0xb6, 0x60, 0x49, 0xaa, 0xb3, 0xb6, 0x5c, 0xd4, 0xb6, 0xc8, 0xed, 0x15, 0xf7, 0xa0, 0x7a, 0xc5, 0xd9, 0x34, 0x4a, 0x9d, 0x8a, 0x6b, 0xb5, 0xed, 0xee, 0xab, 0x87, 0x1e, 0xe4, 0x7d, 0xcc, 0xf1, 0x40, 0xa8, 0x24, 0x23, 0x45, 0xed, 0xee, 0x25, 0xd8, 0x6b, 0x32, 0x6e, 0x82, 0x75, 0xcd, 0xb2, 0x7c, 0x44, 0x0d, 0xa2, 0x8f, 0xf8, 0x0d, 0x54, 0x16, 0x74, 0x3a, 0x67, 0xf9, 0x83, 0xed, 0x6e, 0xeb, 0xde, 0x2e, 0x79, 0x0c, 0x31, 0xf0, 0xfb, 0xf2, 0x3b, 0x74, 0xf0, 0x0b, 0x41, 0x25, 0x17, 0xf1, 0x3e, 0xd8, 0xa9, 0x4a, 0xb8, 0x98, 0x84, 0x11, 0x55, 0xd4, 0xa4, 0xf7, 0x4b, 0x04, 0x8c, 0xd8, 0xa3, 0x8a, 0xe2, 0x3d, 0xa8, 0x73, 0xa1, 0x8c, 0xaf, 0x3b, 0x59, 0xfd, 0x12, 0xa9, 0x71, 0xa1, 0x72, 0xf3, 0x39, 0x34, 0x46, 0x52, 0x4e, 0x8d, 0xab, 0x47, 0x5a, 0xef, 0x97, 0x48, 0x5d, 0x4b, 0xb9, 0xbd, 0x0f, 0xf6, 0x88, 0x0b, 0x9a, 0x64, 0x06, 0xd0, 0x73, 0xda, 0xd6, 0xf1, 0x46, 0xd4, 0xc8, 0x71, 0x15, 0xb6, 0xb4, 0xf7, 0xf2, 0x02, 0xec, 0xb5, 0xef, 0xc5, 0x0e, 0x3c, 0x3d, 0x0b, 0xce, 0xcf, 0x8f, 0x4e, 0x82, 0x70, 0x78, 0xf9, 0x39, 0x08, 0x07, 0x9f, 0xbe, 0x1e, 0x9d, 0x0e, 0x7a, 0xcd, 0x12, 0x7e, 0x06, 0xf8, 0x8e, 0xd3, 0x0b, 0x2e, 0x9a, 0x08, 0xef, 0xc0, 0x93, 0x0d, 0xbd, 0x17, 0x9c, 0x06, 0xc3, 0xa0, 0x59, 0x3e, 0x0e, 0x7e, 0xaf, 0x5a, 0xe8, 0xcf, 0xaa, 0x85, 0xfe, 0xae, 0x5a, 0xe8, 0xfb, 0xdb, 0x09, 0x57, 0x3f, 0xe6, 0x23, 0x6f, 0x2c, 0x63, 0x7f, 0x63, 0x59, 0xbd, 0x09, 0x13, 0x7e, 0xbe, 0xa3, 0x6b, 0x8b, 0xff, 0xa1, 0x38, 0x2e, 0x3a, 0xa3, 0x6a, 0xee, 0xbd, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0x94, 0x56, 0xcb, 0x00, 0x24, 0x03, 0x00, 0x00, } func (m *Message) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Message) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *Message) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Fields) > 0 { for k := range m.Fields { v := m.Fields[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintMessages(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i -= len(k) copy(dAtA[i:], k) i = encodeVarintMessages(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintMessages(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x2a } } if m.Version != 0 { i = encodeVarintMessages(dAtA, i, uint64(m.Version)) i-- dAtA[i] = 0x20 } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintMessages(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintMessages(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.MessageType != 0 { i = encodeVarintMessages(dAtA, i, uint64(m.MessageType)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *Field) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Field) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *Field) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.Data != nil { { size := m.Data.Size() i -= size if _, err := m.Data.MarshalTo(dAtA[i:]); err != nil { return 0, err } } } return len(dAtA) - i, nil } func (m *Field_StringData) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *Field_StringData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) i -= len(m.StringData) copy(dAtA[i:], m.StringData) i = encodeVarintMessages(dAtA, i, uint64(len(m.StringData))) i-- dAtA[i] = 0xa return len(dAtA) - i, nil } func (m *Field_IntData) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *Field_IntData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) i = encodeVarintMessages(dAtA, i, uint64(m.IntData)) i-- dAtA[i] = 0x10 return len(dAtA) - i, nil } func (m *Field_BoolData) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *Field_BoolData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) i-- if m.BoolData { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 return len(dAtA) - i, nil } func (m *Field_BinaryData) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *Field_BinaryData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) if m.BinaryData != nil { i -= len(m.BinaryData) copy(dAtA[i:], m.BinaryData) i = encodeVarintMessages(dAtA, i, uint64(len(m.BinaryData))) i-- dAtA[i] = 0x22 } return len(dAtA) - i, nil } func encodeVarintMessages(dAtA []byte, offset int, v uint64) int { offset -= sovMessages(v) base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) return base } func (m *Message) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.MessageType != 0 { n += 1 + sovMessages(uint64(m.MessageType)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovMessages(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovMessages(uint64(l)) } if m.Version != 0 { n += 1 + sovMessages(uint64(m.Version)) } if len(m.Fields) > 0 { for k, v := range m.Fields { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovMessages(uint64(l)) } mapEntrySize := 1 + len(k) + sovMessages(uint64(len(k))) + l n += mapEntrySize + 1 + sovMessages(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *Field) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Data != nil { n += m.Data.Size() } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *Field_StringData) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.StringData) n += 1 + l + sovMessages(uint64(l)) return n } func (m *Field_IntData) Size() (n int) { if m == nil { return 0 } var l int _ = l n += 1 + sovMessages(uint64(m.IntData)) return n } func (m *Field_BoolData) Size() (n int) { if m == nil { return 0 } var l int _ = l n += 2 return n } func (m *Field_BinaryData) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.BinaryData != nil { l = len(m.BinaryData) n += 1 + l + sovMessages(uint64(l)) } return n } func sovMessages(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozMessages(x uint64) (n int) { return sovMessages(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *Message) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Message: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Message: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MessageType", wireType) } m.MessageType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MessageType |= MessageType(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthMessages } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthMessages } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthMessages } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthMessages } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } m.Version = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Version |= int64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Fields", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthMessages } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthMessages } if postIndex > l { return io.ErrUnexpectedEOF } if m.Fields == nil { m.Fields = make(map[string]*Field) } var mapkey string var mapvalue *Field for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthMessages } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthMessages } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthMessages } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthMessages } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &Field{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipMessages(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthMessages } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Fields[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipMessages(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthMessages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Field) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Field: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Field: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StringData", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthMessages } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthMessages } if postIndex > l { return io.ErrUnexpectedEOF } m.Data = &Field_StringData{string(dAtA[iNdEx:postIndex])} iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IntData", wireType) } var v int64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int64(b&0x7F) << shift if b < 0x80 { break } } m.Data = &Field_IntData{v} case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BoolData", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } b := bool(v != 0) m.Data = &Field_BoolData{b} case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BinaryData", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowMessages } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthMessages } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthMessages } if postIndex > l { return io.ErrUnexpectedEOF } v := make([]byte, postIndex-iNdEx) copy(v, dAtA[iNdEx:postIndex]) m.Data = &Field_BinaryData{v} iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipMessages(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthMessages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func skipMessages(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowMessages } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } wireType := int(wire & 0x7) switch wireType { case 0: for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowMessages } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } iNdEx++ if dAtA[iNdEx-1] < 0x80 { break } } case 1: iNdEx += 8 case 2: var length int for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowMessages } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { break } } if length < 0 { return 0, ErrInvalidLengthMessages } iNdEx += length case 3: depth++ case 4: if depth == 0 { return 0, ErrUnexpectedEndOfGroupMessages } depth-- case 5: iNdEx += 4 default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } if iNdEx < 0 { return 0, ErrInvalidLengthMessages } if depth == 0 { return iNdEx, nil } } return 0, io.ErrUnexpectedEOF } var ( ErrInvalidLengthMessages = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowMessages = fmt.Errorf("proto: integer overflow") ErrUnexpectedEndOfGroupMessages = fmt.Errorf("proto: unexpected end of group") ) ================================================ FILE: .gen/proto/indexer/v1/messages.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/indexer/v1/messages.proto package indexerv1 var yarpcFileDescriptorClosure60256a432328b016 = [][]byte{ // uber/cadence/indexer/v1/messages.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x5f, 0x6b, 0xdb, 0x30, 0x14, 0xc5, 0xe3, 0xba, 0xf9, 0x77, 0x5d, 0x46, 0xa6, 0x8d, 0xd5, 0xb4, 0x6c, 0xb8, 0x65, 0x94, 0x30, 0x86, 0x4d, 0xb2, 0xc1, 0xfe, 0x3d, 0xb5, 0xd8, 0x6b, 0x02, 0xed, 0x18, 0x6a, 0xb6, 0xb5, 0x7b, 0x31, 0xb2, 0xad, 0x66, 0xa2, 0xb1, 0x64, 0x6c, 0xc5, 0xa9, 0x1f, 0xf7, 0x11, 0xf6, 0x8d, 0x87, 0x2c, 0x97, 0x25, 0x85, 0xb2, 0x37, 0xe9, 0x9c, 0xdf, 0x3d, 0x17, 0x5d, 0x5d, 0x38, 0x5a, 0x46, 0x34, 0xf7, 0x62, 0x92, 0x50, 0x1e, 0x53, 0x8f, 0xf1, 0x84, 0xde, 0xd2, 0xdc, 0x2b, 0x47, 0x5e, 0x4a, 0x8b, 0x82, 0xcc, 0x69, 0xe1, 0x66, 0xb9, 0x90, 0x02, 0xed, 0x2a, 0xce, 0x6d, 0x38, 0xb7, 0xe1, 0xdc, 0x72, 0xb4, 0xe7, 0x6c, 0x04, 0x90, 0x8c, 0xa9, 0xe2, 0x58, 0xa4, 0xa9, 0xe0, 0xba, 0xf4, 0xf0, 0xb7, 0x09, 0xdd, 0x73, 0x9d, 0x86, 0x4e, 0x61, 0xa7, 0x09, 0x0e, 0x65, 0x95, 0x51, 0xdb, 0x70, 0x8c, 0xe1, 0xa3, 0xf1, 0x4b, 0xf7, 0x81, 0x74, 0xb7, 0xa9, 0x9b, 0x55, 0x19, 0xc5, 0x56, 0xfa, 0xef, 0x82, 0xf6, 0xa1, 0x9f, 0x88, 0x94, 0x30, 0x1e, 0xb2, 0xc4, 0xde, 0x72, 0x8c, 0x61, 0x1f, 0xf7, 0xb4, 0x30, 0x4d, 0xd0, 0x37, 0x40, 0x2b, 0x91, 0xdf, 0x5c, 0x2f, 0xc4, 0x2a, 0xa4, 0xb7, 0x34, 0x5e, 0x4a, 0x26, 0xb8, 0x6d, 0x3a, 0xc6, 0xd0, 0x1a, 0x1f, 0x6d, 0xf6, 0x22, 0x19, 0x53, 0x7d, 0x7e, 0x34, 0x78, 0x70, 0x47, 0xe3, 0xc7, 0xab, 0xfb, 0x12, 0xb2, 0xa1, 0x5b, 0xd2, 0xbc, 0x50, 0x59, 0xdb, 0x8e, 0x31, 0x34, 0xf1, 0xdd, 0x15, 0xf9, 0xd0, 0xb9, 0x66, 0x74, 0x91, 0x14, 0x76, 0xdb, 0x31, 0x87, 0xd6, 0xf8, 0xf5, 0xff, 0x1e, 0xe4, 0x7e, 0xae, 0xf1, 0x80, 0xcb, 0xbc, 0xc2, 0x4d, 0xed, 0xde, 0x15, 0x58, 0x6b, 0x32, 0x1a, 0x80, 0x79, 0x43, 0xab, 0x7a, 0x44, 0x7d, 0xac, 0x8e, 0xe8, 0x2d, 0xb4, 0x4b, 0xb2, 0x58, 0xd2, 0xfa, 0xc1, 0xd6, 0xf8, 0xc5, 0x83, 0x5d, 0xea, 0x18, 0xac, 0xe1, 0x8f, 0x5b, 0xef, 0x8d, 0xc3, 0x3f, 0x06, 0xb4, 0x6b, 0x11, 0x1d, 0x80, 0x55, 0xc8, 0x9c, 0xf1, 0x79, 0x98, 0x10, 0x49, 0x74, 0xfa, 0xa4, 0x85, 0x41, 0x8b, 0x3e, 0x91, 0x04, 0xed, 0x43, 0x8f, 0x71, 0xa9, 0x7d, 0xd5, 0xc9, 0x9c, 0xb4, 0x70, 0x97, 0x71, 0x59, 0x9b, 0xcf, 0xa1, 0x1f, 0x09, 0xb1, 0xd0, 0xae, 0x1a, 0x69, 0x6f, 0xd2, 0xc2, 0x3d, 0x25, 0xd5, 0xf6, 0x01, 0x58, 0x11, 0xe3, 0x24, 0xaf, 0x34, 0xa0, 0xe6, 0xb4, 0xa3, 0xe2, 0xb5, 0xa8, 0x90, 0x93, 0x0e, 0x6c, 0x2b, 0xef, 0xd5, 0x25, 0x58, 0x6b, 0xdf, 0x8b, 0x6c, 0x78, 0x7a, 0x1e, 0x5c, 0x5c, 0x1c, 0x9f, 0x06, 0xe1, 0xec, 0xea, 0x6b, 0x10, 0x4e, 0xbf, 0x7c, 0x3f, 0x3e, 0x9b, 0xfa, 0x83, 0x16, 0x7a, 0x06, 0xe8, 0x9e, 0xe3, 0x07, 0x97, 0x03, 0x03, 0xed, 0xc2, 0x93, 0x0d, 0xdd, 0x0f, 0xce, 0x82, 0x59, 0x30, 0xd8, 0x3a, 0xf9, 0xf0, 0xf3, 0xdd, 0x9c, 0xc9, 0x5f, 0xcb, 0xc8, 0x8d, 0x45, 0xea, 0x6d, 0x2c, 0xa8, 0x3b, 0xa7, 0xdc, 0xab, 0xf7, 0x72, 0x6d, 0xd9, 0x3f, 0x35, 0xc7, 0x72, 0x14, 0x75, 0x6a, 0xef, 0xcd, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x8b, 0x53, 0x80, 0x18, 0x03, 0x00, 0x00, }, // uber/cadence/api/v1/common.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdd, 0x72, 0x22, 0xc7, 0x15, 0xf6, 0xc0, 0xa2, 0x9f, 0x03, 0xbb, 0x42, 0xad, 0xfd, 0x61, 0xb5, 0x5e, 0xaf, 0x16, 0x97, 0x63, 0x79, 0x2b, 0x86, 0x88, 0x4d, 0x52, 0x2e, 0x3b, 0x4e, 0x82, 0xd0, 0x48, 0x9a, 0x5d, 0x02, 0xa4, 0x99, 0x95, 0xac, 0xa4, 0xca, 0x53, 0xcd, 0x4c, 0x83, 0x3b, 0x0c, 0xd3, 0x93, 0x99, 0x1e, 0x56, 0xf8, 0x22, 0x95, 0xcb, 0xe4, 0x26, 0x8f, 0x90, 0x8b, 0xbc, 0x48, 0x1e, 0x20, 0x97, 0x79, 0x97, 0x5c, 0xa7, 0xba, 0xa7, 0x07, 0x81, 0xc2, 0x1a, 0x5f, 0xa4, 0x7c, 0x47, 0x9f, 0xf3, 0x7d, 0xa7, 0xbf, 0xd3, 0xdd, 0xe7, 0x1c, 0x06, 0x0e, 0x92, 0x01, 0x8d, 0xea, 0x2e, 0xf1, 0x68, 0xe0, 0xd2, 0x3a, 0x09, 0x59, 0x7d, 0x7a, 0x54, 0x77, 0xf9, 0x64, 0xc2, 0x83, 0x5a, 0x18, 0x71, 0xc1, 0xd1, 0x9e, 0x44, 0xd4, 0x34, 0xa2, 0x46, 0x42, 0x56, 0x9b, 0x1e, 0xed, 0x7f, 0x30, 0xe2, 0x7c, 0xe4, 0xd3, 0xba, 0x82, 0x0c, 0x92, 0x61, 0xdd, 0x4b, 0x22, 0x22, 0x58, 0x46, 0xaa, 0xbe, 0x86, 0xdd, 0x4b, 0x1e, 0x8d, 0x87, 0x3e, 0x7f, 0x6b, 0x5e, 0x53, 0x37, 0x91, 0x2e, 0xf4, 0x0c, 0x8a, 0x6f, 0xb5, 0xd1, 0x61, 0x5e, 0xc5, 0x38, 0x30, 0x0e, 0xb7, 0x31, 0x64, 0x26, 0xcb, 0x43, 0x0f, 0x60, 0x23, 0x4a, 0x02, 0xe9, 0xcb, 0x29, 0x5f, 0x21, 0x4a, 0x02, 0xcb, 0xab, 0x56, 0xa1, 0x94, 0x05, 0xb3, 0x67, 0x21, 0x45, 0x08, 0xee, 0x04, 0x64, 0x42, 0x75, 0x00, 0xf5, 0x5b, 0x62, 0x9a, 0xae, 0x60, 0x53, 0x26, 0x66, 0xef, 0xc4, 0x3c, 0x85, 0xcd, 0x1e, 0x99, 0xf9, 0x9c, 0x78, 0xd2, 0xed, 0x11, 0x41, 0x94, 0xbb, 0x84, 0xd5, 0xef, 0xea, 0x17, 0xb0, 0x79, 0x4a, 0x98, 0x9f, 0x44, 0x14, 0x3d, 0x84, 0x8d, 0x88, 0x92, 0x98, 0x07, 0x9a, 0xaf, 0x57, 0xa8, 0x02, 0x9b, 0x1e, 0x15, 0x84, 0xf9, 0xb1, 0x52, 0x58, 0xc2, 0xd9, 0xb2, 0xfa, 0x77, 0x03, 0xee, 0xfc, 0x86, 0x4e, 0x38, 0xfa, 0x12, 0x36, 0x86, 0x8c, 0xfa, 0x5e, 0x5c, 0x31, 0x0e, 0xf2, 0x87, 0xc5, 0xc6, 0x47, 0xb5, 0x15, 0xe7, 0x57, 0x93, 0xd0, 0xda, 0xa9, 0xc2, 0x99, 0x81, 0x88, 0x66, 0x58, 0x93, 0xf6, 0x2f, 0xa1, 0xb8, 0x60, 0x46, 0x65, 0xc8, 0x8f, 0xe9, 0x4c, 0xab, 0x90, 0x3f, 0x51, 0x03, 0x0a, 0x53, 0xe2, 0x27, 0x54, 0x09, 0x28, 0x36, 0xde, 0x5f, 0x19, 0x5e, 0xa7, 0x89, 0x53, 0xe8, 0xe7, 0xb9, 0xcf, 0x8c, 0xea, 0x3f, 0x0c, 0xd8, 0x38, 0xa7, 0xc4, 0xa3, 0x11, 0xfa, 0xd5, 0x2d, 0x89, 0x1f, 0xaf, 0x8c, 0x91, 0x82, 0x7f, 0x58, 0x91, 0xff, 0x36, 0xa0, 0xdc, 0xa7, 0x24, 0x72, 0xbf, 0x69, 0x0a, 0x11, 0xb1, 0x41, 0x22, 0x68, 0x8c, 0x1c, 0xb8, 0xc7, 0x02, 0x8f, 0x5e, 0x53, 0xcf, 0x59, 0x92, 0xfd, 0xd9, 0xca, 0xa8, 0xb7, 0xe9, 0x35, 0x2b, 0xe5, 0x2e, 0xe6, 0x71, 0x97, 0x2d, 0xda, 0xf6, 0xbf, 0x06, 0xf4, 0xbf, 0xa0, 0xff, 0x63, 0x56, 0x43, 0xd8, 0x3a, 0x21, 0x82, 0x1c, 0xfb, 0x7c, 0x80, 0x4e, 0xe1, 0x2e, 0x0d, 0x5c, 0xee, 0xb1, 0x60, 0xe4, 0x88, 0x59, 0x98, 0x3e, 0xd0, 0x7b, 0x8d, 0xe7, 0x2b, 0x63, 0x99, 0x1a, 0x29, 0x5f, 0x34, 0x2e, 0xd1, 0x85, 0xd5, 0xfc, 0x01, 0xe7, 0x16, 0x1e, 0x70, 0x2f, 0x2d, 0x3a, 0x1a, 0x5d, 0xd0, 0x28, 0x66, 0x3c, 0xb0, 0x82, 0x21, 0x97, 0x40, 0x36, 0x09, 0xfd, 0xac, 0x10, 0xe4, 0x6f, 0xf4, 0x31, 0xec, 0x0c, 0x29, 0x11, 0x49, 0x44, 0x9d, 0x69, 0x0a, 0xd5, 0x05, 0x77, 0x4f, 0x9b, 0x75, 0x80, 0xea, 0x6b, 0x78, 0xd4, 0x4f, 0xc2, 0x90, 0x47, 0x82, 0x7a, 0x2d, 0x9f, 0xd1, 0x40, 0x68, 0x4f, 0x2c, 0x6b, 0x75, 0xc4, 0x9d, 0xd8, 0x1b, 0xeb, 0xc8, 0x85, 0x11, 0xef, 0x7b, 0x63, 0xf4, 0x18, 0xb6, 0xfe, 0x40, 0xa6, 0x44, 0x39, 0xd2, 0x98, 0x9b, 0x72, 0xdd, 0xf7, 0xc6, 0xd5, 0x3f, 0xe7, 0xa1, 0x88, 0xa9, 0x88, 0x66, 0x3d, 0xee, 0x33, 0x77, 0x86, 0x4e, 0xa0, 0xcc, 0x02, 0x26, 0x18, 0xf1, 0x1d, 0x16, 0x08, 0x1a, 0x4d, 0x49, 0xaa, 0xb2, 0xd8, 0x78, 0x5c, 0x4b, 0xdb, 0x4b, 0x2d, 0x6b, 0x2f, 0xb5, 0x13, 0xdd, 0x5e, 0xf0, 0x8e, 0xa6, 0x58, 0x9a, 0x81, 0xea, 0xb0, 0x37, 0x20, 0xee, 0x98, 0x0f, 0x87, 0x8e, 0xcb, 0xe9, 0x70, 0xc8, 0x5c, 0x29, 0x53, 0xed, 0x6d, 0x60, 0xa4, 0x5d, 0xad, 0x1b, 0x8f, 0xdc, 0x76, 0x42, 0xae, 0xd9, 0x24, 0x99, 0xdc, 0x6c, 0x9b, 0x5f, 0xbb, 0xad, 0xa6, 0xcc, 0xb7, 0xfd, 0xe4, 0x26, 0x0a, 0x11, 0x82, 0x4e, 0x42, 0x11, 0x57, 0xee, 0x1c, 0x18, 0x87, 0x85, 0x39, 0xb4, 0xa9, 0xcd, 0xe8, 0x4b, 0x78, 0x12, 0xf0, 0xc0, 0x89, 0x64, 0xea, 0x64, 0xe0, 0x53, 0x87, 0x46, 0x11, 0x8f, 0x9c, 0xb4, 0xa5, 0xc4, 0x95, 0xc2, 0x41, 0xfe, 0x70, 0x1b, 0x57, 0x02, 0x1e, 0xe0, 0x0c, 0x61, 0x4a, 0x00, 0x4e, 0xfd, 0xe8, 0x15, 0xec, 0xd1, 0xeb, 0x90, 0xa5, 0x42, 0x6e, 0x24, 0x6f, 0xac, 0x93, 0x8c, 0x6e, 0x58, 0x99, 0xea, 0xea, 0x04, 0x1e, 0x59, 0x31, 0xf7, 0x95, 0xf1, 0x2c, 0xe2, 0x49, 0xd8, 0x23, 0x91, 0x60, 0xaa, 0x39, 0xaf, 0x68, 0x98, 0xe8, 0x97, 0x50, 0x88, 0x05, 0x11, 0xe9, 0x83, 0xbf, 0xd7, 0x38, 0x5c, 0xf9, 0x48, 0x97, 0x03, 0xf6, 0x25, 0x1e, 0xa7, 0xb4, 0xea, 0x14, 0x9e, 0x2c, 0x7b, 0x5b, 0x3c, 0x18, 0xb2, 0x91, 0x56, 0x88, 0x2e, 0xa1, 0xcc, 0x32, 0xb7, 0x33, 0x92, 0xfe, 0xac, 0xb4, 0x7f, 0xfc, 0x3d, 0x76, 0x9a, 0x4b, 0xc7, 0x3b, 0x6c, 0xc9, 0x11, 0x57, 0xff, 0x65, 0xc0, 0x7e, 0x33, 0x9e, 0x05, 0x6e, 0x36, 0x36, 0x96, 0xf7, 0xad, 0xc0, 0x26, 0x0d, 0xe4, 0x39, 0xa7, 0x33, 0x68, 0x0b, 0x67, 0x4b, 0xd4, 0x80, 0x07, 0x61, 0x44, 0x3d, 0x3a, 0x64, 0x01, 0xf5, 0x9c, 0x3f, 0x26, 0x34, 0xa1, 0x8e, 0x3a, 0x95, 0xf4, 0x29, 0xef, 0xdd, 0x38, 0x7f, 0x2b, 0x7d, 0x1d, 0x79, 0x48, 0x4f, 0x01, 0x52, 0xa0, 0x2a, 0xe7, 0xbc, 0x02, 0x6e, 0x2b, 0x8b, 0x2a, 0xd4, 0x5f, 0x43, 0x29, 0x75, 0xbb, 0x4a, 0x83, 0x7a, 0x24, 0xc5, 0xc6, 0xd3, 0x95, 0x09, 0x66, 0x5d, 0x02, 0x17, 0x15, 0x25, 0x55, 0x5d, 0xfd, 0x4f, 0x1e, 0xde, 0x57, 0xb3, 0x8d, 0xb6, 0xfc, 0x24, 0x16, 0x34, 0xea, 0x53, 0x9f, 0xba, 0x32, 0x13, 0x5d, 0x48, 0x7d, 0xd8, 0x8a, 0x45, 0x44, 0x04, 0x1d, 0xcd, 0x74, 0x3b, 0x79, 0xb9, 0x32, 0xfc, 0xea, 0x20, 0x7d, 0x4d, 0x3d, 0xce, 0x55, 0x0c, 0x3c, 0x0f, 0x84, 0xfe, 0x62, 0xc0, 0x87, 0x44, 0x11, 0x1c, 0x37, 0x65, 0x38, 0xb1, 0x60, 0xee, 0x78, 0xe6, 0x44, 0x74, 0x24, 0x2f, 0x4c, 0xe7, 0x93, 0xf6, 0xc2, 0x9f, 0x7e, 0x8f, 0x0d, 0x15, 0x1b, 0x2b, 0x72, 0x9a, 0x99, 0xdc, 0xf1, 0xfc, 0x3d, 0xfc, 0x8c, 0x7c, 0x37, 0x0c, 0xfd, 0xcd, 0x80, 0x8f, 0x6e, 0x49, 0xa1, 0xd7, 0x82, 0x46, 0x01, 0xf1, 0x1d, 0x1a, 0x08, 0x26, 0x66, 0x99, 0x98, 0xb4, 0x8e, 0x7f, 0xbe, 0x5e, 0x8c, 0xa9, 0xf9, 0xa6, 0xa2, 0x2f, 0xc9, 0x79, 0x4e, 0xd6, 0x01, 0x11, 0x86, 0xdd, 0x4c, 0x08, 0xc9, 0x06, 0x8d, 0xbe, 0xd8, 0xd5, 0xe3, 0x5e, 0x07, 0x9b, 0x4f, 0x25, 0x5c, 0x76, 0x6f, 0x59, 0x8e, 0x77, 0x61, 0x27, 0x3b, 0x7b, 0x9d, 0x4d, 0xf5, 0x17, 0x50, 0xbe, 0x4d, 0x44, 0xf7, 0xa1, 0x10, 0xbb, 0x3c, 0xcc, 0xea, 0x34, 0x5d, 0xcc, 0x8b, 0x37, 0xb7, 0xf0, 0x6f, 0xe7, 0x15, 0x3c, 0x5b, 0x73, 0xfe, 0xe8, 0x43, 0xb8, 0xbb, 0x74, 0xa7, 0x3a, 0x68, 0x29, 0x5e, 0x80, 0x7e, 0x9e, 0xab, 0x18, 0xd5, 0xbf, 0x1a, 0xf0, 0x7c, 0xed, 0xf9, 0xa1, 0x9f, 0xc0, 0xfd, 0xdb, 0xf7, 0x32, 0x1f, 0x71, 0xdb, 0xb2, 0x1f, 0x2d, 0x72, 0x54, 0x71, 0xd4, 0x64, 0x6f, 0x5b, 0x66, 0xc8, 0x99, 0x9b, 0xa6, 0xb1, 0xbb, 0x4c, 0x78, 0x4d, 0x67, 0x4a, 0xcb, 0x57, 0xb0, 0xdb, 0x23, 0x23, 0x16, 0xa8, 0x5a, 0xee, 0x86, 0x42, 0x4d, 0xa3, 0x27, 0xb0, 0x1d, 0x92, 0x11, 0x75, 0x62, 0xf6, 0x6d, 0xba, 0x5f, 0x01, 0x6f, 0x49, 0x43, 0x9f, 0x7d, 0x4b, 0xd1, 0x8f, 0x60, 0x27, 0xa0, 0xd7, 0xc2, 0x51, 0x08, 0xc1, 0xc7, 0x34, 0xd0, 0x63, 0xf3, 0xae, 0x34, 0xf7, 0xc8, 0x88, 0xda, 0xd2, 0xf8, 0xe2, 0x2d, 0x94, 0x16, 0x27, 0x2e, 0x7a, 0x0c, 0x0f, 0xcc, 0x4e, 0xab, 0x7b, 0x62, 0x75, 0xce, 0x1c, 0xfb, 0xaa, 0x67, 0x3a, 0x56, 0xe7, 0xa2, 0xd9, 0xb6, 0x4e, 0xca, 0xef, 0xa1, 0x7d, 0x78, 0xb8, 0xec, 0xb2, 0xcf, 0xb1, 0x75, 0x6a, 0xe3, 0xcb, 0xb2, 0x81, 0x1e, 0x02, 0x5a, 0xf6, 0xbd, 0xea, 0x77, 0x3b, 0xe5, 0x1c, 0xaa, 0xc0, 0xfd, 0x65, 0x7b, 0x0f, 0x77, 0xed, 0xee, 0xcb, 0x72, 0xfe, 0xc5, 0x9f, 0x60, 0x6f, 0x45, 0x17, 0x45, 0xcf, 0xe1, 0xa9, 0xd5, 0xef, 0xb6, 0x9b, 0xb6, 0xd5, 0xed, 0x38, 0x67, 0xb8, 0xfb, 0xa6, 0xe7, 0xf4, 0xed, 0xa6, 0xbd, 0xa8, 0xe3, 0x9d, 0x90, 0x73, 0xb3, 0xd9, 0xb6, 0xcf, 0xaf, 0xca, 0xc6, 0xbb, 0x21, 0x27, 0xb8, 0x69, 0x75, 0xcc, 0x93, 0x72, 0xee, 0xc5, 0x3f, 0x0d, 0xf8, 0xe0, 0xbb, 0x9b, 0x03, 0xfa, 0x14, 0x3e, 0x69, 0xb6, 0x6c, 0xeb, 0xc2, 0x74, 0x5a, 0xed, 0x37, 0x7d, 0xdb, 0xc4, 0x4e, 0xdf, 0x6c, 0x9b, 0x2d, 0x15, 0xb4, 0x6f, 0xe3, 0xa6, 0x6d, 0x9e, 0x5d, 0x2d, 0xe8, 0x7a, 0x09, 0xf5, 0xf5, 0x70, 0x6c, 0x9e, 0xa5, 0x6b, 0xab, 0xf5, 0x5a, 0x2a, 0xfd, 0x19, 0x1c, 0xad, 0x27, 0x99, 0x5f, 0xd9, 0x26, 0xee, 0x34, 0xdb, 0x8e, 0xd9, 0xb1, 0x2d, 0xfb, 0xaa, 0x9c, 0xdb, 0xcf, 0x55, 0x8c, 0x17, 0x5f, 0x43, 0x49, 0xfe, 0x77, 0xe7, 0x53, 0x1a, 0x65, 0x57, 0x77, 0xda, 0xb4, 0xda, 0xdd, 0x0b, 0x13, 0xdf, 0xbe, 0xba, 0x47, 0xb0, 0xb7, 0xec, 0x3a, 0xed, 0xe2, 0x96, 0x59, 0x36, 0xe4, 0x9d, 0x2e, 0x3b, 0xce, 0x70, 0xb3, 0x65, 0x9e, 0xbe, 0x69, 0x97, 0x73, 0xc7, 0xbf, 0x87, 0x47, 0x2e, 0x9f, 0xac, 0xaa, 0xed, 0xe3, 0x62, 0x4b, 0x7d, 0x2d, 0xf5, 0xe4, 0x00, 0xee, 0x19, 0xbf, 0x3b, 0x1a, 0x31, 0xf1, 0x4d, 0x32, 0xa8, 0xb9, 0x7c, 0x52, 0x5f, 0xfc, 0xb6, 0xfa, 0x94, 0x79, 0x7e, 0x7d, 0xc4, 0xd3, 0x2f, 0x26, 0xfd, 0xa1, 0xf5, 0x05, 0x09, 0xd9, 0xf4, 0x68, 0xb0, 0xa1, 0x6c, 0x2f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x5e, 0xe2, 0xc9, 0x06, 0x8c, 0x0d, 0x00, 0x00, }, // google/protobuf/duration.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a, 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0x56, 0x5c, 0x1c, 0x2e, 0x50, 0x25, 0x42, 0x12, 0x5c, 0xec, 0xc5, 0xa9, 0xc9, 0xf9, 0x79, 0x29, 0xc5, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x30, 0xae, 0x90, 0x08, 0x17, 0x6b, 0x5e, 0x62, 0x5e, 0x7e, 0xb1, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x84, 0xe3, 0xd4, 0xcc, 0xc8, 0x25, 0x9c, 0x9c, 0x9f, 0xab, 0x87, 0x66, 0xa6, 0x13, 0x2f, 0xcc, 0xc4, 0x00, 0x90, 0x48, 0x00, 0x63, 0x94, 0x21, 0x54, 0x45, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x5e, 0x7e, 0x51, 0x3a, 0xc2, 0x81, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0xfa, 0xd9, 0x79, 0xf9, 0xe5, 0x79, 0x70, 0xc7, 0x16, 0x24, 0xfd, 0x60, 0x64, 0x5c, 0xc4, 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0xa2, 0x39, 0x00, 0xaa, 0x43, 0x2f, 0x3c, 0x35, 0x27, 0xc7, 0x1b, 0xa4, 0x3e, 0x04, 0xa4, 0x35, 0x89, 0x0d, 0x6c, 0x94, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xef, 0x8a, 0xb4, 0xc3, 0xfb, 0x00, 0x00, 0x00, }, } ================================================ FILE: .gen/proto/matching/v1/service.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/matching/v1/service.proto package matchingv1 import ( encoding_binary "encoding/binary" fmt "fmt" io "io" math "math" math_bits "math/bits" proto "github.com/gogo/protobuf/proto" types "github.com/gogo/protobuf/types" v1 "github.com/uber/cadence-idl/go/proto/api/v1" v11 "github.com/uber/cadence/.gen/proto/shared/v1" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type TaskListPartition struct { IsolationGroups []string `protobuf:"bytes,1,rep,name=isolation_groups,json=isolationGroups,proto3" json:"isolation_groups,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *TaskListPartition) Reset() { *m = TaskListPartition{} } func (m *TaskListPartition) String() string { return proto.CompactTextString(m) } func (*TaskListPartition) ProtoMessage() {} func (*TaskListPartition) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{0} } func (m *TaskListPartition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *TaskListPartition) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_TaskListPartition.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *TaskListPartition) XXX_Merge(src proto.Message) { xxx_messageInfo_TaskListPartition.Merge(m, src) } func (m *TaskListPartition) XXX_Size() int { return m.Size() } func (m *TaskListPartition) XXX_DiscardUnknown() { xxx_messageInfo_TaskListPartition.DiscardUnknown(m) } var xxx_messageInfo_TaskListPartition proto.InternalMessageInfo func (m *TaskListPartition) GetIsolationGroups() []string { if m != nil { return m.IsolationGroups } return nil } type TaskListPartitionConfig struct { Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` NumReadPartitions int32 `protobuf:"varint,2,opt,name=num_read_partitions,json=numReadPartitions,proto3" json:"num_read_partitions,omitempty"` // Deprecated: Do not use. NumWritePartitions int32 `protobuf:"varint,3,opt,name=num_write_partitions,json=numWritePartitions,proto3" json:"num_write_partitions,omitempty"` // Deprecated: Do not use. ReadPartitions map[int32]*TaskListPartition `protobuf:"bytes,4,rep,name=read_partitions,json=readPartitions,proto3" json:"read_partitions,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` WritePartitions map[int32]*TaskListPartition `protobuf:"bytes,5,rep,name=write_partitions,json=writePartitions,proto3" json:"write_partitions,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *TaskListPartitionConfig) Reset() { *m = TaskListPartitionConfig{} } func (m *TaskListPartitionConfig) String() string { return proto.CompactTextString(m) } func (*TaskListPartitionConfig) ProtoMessage() {} func (*TaskListPartitionConfig) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{1} } func (m *TaskListPartitionConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *TaskListPartitionConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_TaskListPartitionConfig.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *TaskListPartitionConfig) XXX_Merge(src proto.Message) { xxx_messageInfo_TaskListPartitionConfig.Merge(m, src) } func (m *TaskListPartitionConfig) XXX_Size() int { return m.Size() } func (m *TaskListPartitionConfig) XXX_DiscardUnknown() { xxx_messageInfo_TaskListPartitionConfig.DiscardUnknown(m) } var xxx_messageInfo_TaskListPartitionConfig proto.InternalMessageInfo func (m *TaskListPartitionConfig) GetVersion() int64 { if m != nil { return m.Version } return 0 } // Deprecated: Do not use. func (m *TaskListPartitionConfig) GetNumReadPartitions() int32 { if m != nil { return m.NumReadPartitions } return 0 } // Deprecated: Do not use. func (m *TaskListPartitionConfig) GetNumWritePartitions() int32 { if m != nil { return m.NumWritePartitions } return 0 } func (m *TaskListPartitionConfig) GetReadPartitions() map[int32]*TaskListPartition { if m != nil { return m.ReadPartitions } return nil } func (m *TaskListPartitionConfig) GetWritePartitions() map[int32]*TaskListPartition { if m != nil { return m.WritePartitions } return nil } type LoadBalancerHints struct { BacklogCount int64 `protobuf:"varint,1,opt,name=backlog_count,json=backlogCount,proto3" json:"backlog_count,omitempty"` RatePerSecond float64 `protobuf:"fixed64,2,opt,name=rate_per_second,json=ratePerSecond,proto3" json:"rate_per_second,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *LoadBalancerHints) Reset() { *m = LoadBalancerHints{} } func (m *LoadBalancerHints) String() string { return proto.CompactTextString(m) } func (*LoadBalancerHints) ProtoMessage() {} func (*LoadBalancerHints) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{2} } func (m *LoadBalancerHints) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *LoadBalancerHints) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_LoadBalancerHints.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *LoadBalancerHints) XXX_Merge(src proto.Message) { xxx_messageInfo_LoadBalancerHints.Merge(m, src) } func (m *LoadBalancerHints) XXX_Size() int { return m.Size() } func (m *LoadBalancerHints) XXX_DiscardUnknown() { xxx_messageInfo_LoadBalancerHints.DiscardUnknown(m) } var xxx_messageInfo_LoadBalancerHints proto.InternalMessageInfo func (m *LoadBalancerHints) GetBacklogCount() int64 { if m != nil { return m.BacklogCount } return 0 } func (m *LoadBalancerHints) GetRatePerSecond() float64 { if m != nil { return m.RatePerSecond } return 0 } type PollForDecisionTaskRequest struct { Request *v1.PollForDecisionTaskRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` PollerId string `protobuf:"bytes,3,opt,name=poller_id,json=pollerId,proto3" json:"poller_id,omitempty"` ForwardedFrom string `protobuf:"bytes,4,opt,name=forwarded_from,json=forwardedFrom,proto3" json:"forwarded_from,omitempty"` IsolationGroup string `protobuf:"bytes,5,opt,name=isolation_group,json=isolationGroup,proto3" json:"isolation_group,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *PollForDecisionTaskRequest) Reset() { *m = PollForDecisionTaskRequest{} } func (m *PollForDecisionTaskRequest) String() string { return proto.CompactTextString(m) } func (*PollForDecisionTaskRequest) ProtoMessage() {} func (*PollForDecisionTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{3} } func (m *PollForDecisionTaskRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *PollForDecisionTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_PollForDecisionTaskRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *PollForDecisionTaskRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_PollForDecisionTaskRequest.Merge(m, src) } func (m *PollForDecisionTaskRequest) XXX_Size() int { return m.Size() } func (m *PollForDecisionTaskRequest) XXX_DiscardUnknown() { xxx_messageInfo_PollForDecisionTaskRequest.DiscardUnknown(m) } var xxx_messageInfo_PollForDecisionTaskRequest proto.InternalMessageInfo func (m *PollForDecisionTaskRequest) GetRequest() *v1.PollForDecisionTaskRequest { if m != nil { return m.Request } return nil } func (m *PollForDecisionTaskRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *PollForDecisionTaskRequest) GetPollerId() string { if m != nil { return m.PollerId } return "" } func (m *PollForDecisionTaskRequest) GetForwardedFrom() string { if m != nil { return m.ForwardedFrom } return "" } func (m *PollForDecisionTaskRequest) GetIsolationGroup() string { if m != nil { return m.IsolationGroup } return "" } type PollForDecisionTaskResponse struct { TaskToken []byte `protobuf:"bytes,1,opt,name=task_token,json=taskToken,proto3" json:"task_token,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` WorkflowType *v1.WorkflowType `protobuf:"bytes,3,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` PreviousStartedEventId *types.Int64Value `protobuf:"bytes,4,opt,name=previous_started_event_id,json=previousStartedEventId,proto3" json:"previous_started_event_id,omitempty"` StartedEventId int64 `protobuf:"varint,5,opt,name=started_event_id,json=startedEventId,proto3" json:"started_event_id,omitempty"` Attempt int32 `protobuf:"varint,6,opt,name=attempt,proto3" json:"attempt,omitempty"` NextEventId int64 `protobuf:"varint,7,opt,name=next_event_id,json=nextEventId,proto3" json:"next_event_id,omitempty"` BacklogCountHint int64 `protobuf:"varint,8,opt,name=backlog_count_hint,json=backlogCountHint,proto3" json:"backlog_count_hint,omitempty"` StickyExecutionEnabled bool `protobuf:"varint,9,opt,name=sticky_execution_enabled,json=stickyExecutionEnabled,proto3" json:"sticky_execution_enabled,omitempty"` Query *v1.WorkflowQuery `protobuf:"bytes,10,opt,name=query,proto3" json:"query,omitempty"` DecisionInfo *v11.TransientDecisionInfo `protobuf:"bytes,11,opt,name=decision_info,json=decisionInfo,proto3" json:"decision_info,omitempty"` WorkflowExecutionTaskList *v1.TaskList `protobuf:"bytes,12,opt,name=workflow_execution_task_list,json=workflowExecutionTaskList,proto3" json:"workflow_execution_task_list,omitempty"` EventStoreVersion int32 `protobuf:"varint,13,opt,name=event_store_version,json=eventStoreVersion,proto3" json:"event_store_version,omitempty"` BranchToken []byte `protobuf:"bytes,14,opt,name=branch_token,json=branchToken,proto3" json:"branch_token,omitempty"` ScheduledTime *types.Timestamp `protobuf:"bytes,15,opt,name=scheduled_time,json=scheduledTime,proto3" json:"scheduled_time,omitempty"` StartedTime *types.Timestamp `protobuf:"bytes,16,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"` Queries map[string]*v1.WorkflowQuery `protobuf:"bytes,17,rep,name=queries,proto3" json:"queries,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` TotalHistoryBytes int64 `protobuf:"varint,18,opt,name=total_history_bytes,json=totalHistoryBytes,proto3" json:"total_history_bytes,omitempty"` PartitionConfig *TaskListPartitionConfig `protobuf:"bytes,19,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` LoadBalancerHints *LoadBalancerHints `protobuf:"bytes,20,opt,name=load_balancer_hints,json=loadBalancerHints,proto3" json:"load_balancer_hints,omitempty"` AutoConfigHint *v1.AutoConfigHint `protobuf:"bytes,21,opt,name=auto_config_hint,json=autoConfigHint,proto3" json:"auto_config_hint,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *PollForDecisionTaskResponse) Reset() { *m = PollForDecisionTaskResponse{} } func (m *PollForDecisionTaskResponse) String() string { return proto.CompactTextString(m) } func (*PollForDecisionTaskResponse) ProtoMessage() {} func (*PollForDecisionTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{4} } func (m *PollForDecisionTaskResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *PollForDecisionTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_PollForDecisionTaskResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *PollForDecisionTaskResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_PollForDecisionTaskResponse.Merge(m, src) } func (m *PollForDecisionTaskResponse) XXX_Size() int { return m.Size() } func (m *PollForDecisionTaskResponse) XXX_DiscardUnknown() { xxx_messageInfo_PollForDecisionTaskResponse.DiscardUnknown(m) } var xxx_messageInfo_PollForDecisionTaskResponse proto.InternalMessageInfo func (m *PollForDecisionTaskResponse) GetTaskToken() []byte { if m != nil { return m.TaskToken } return nil } func (m *PollForDecisionTaskResponse) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *PollForDecisionTaskResponse) GetWorkflowType() *v1.WorkflowType { if m != nil { return m.WorkflowType } return nil } func (m *PollForDecisionTaskResponse) GetPreviousStartedEventId() *types.Int64Value { if m != nil { return m.PreviousStartedEventId } return nil } func (m *PollForDecisionTaskResponse) GetStartedEventId() int64 { if m != nil { return m.StartedEventId } return 0 } func (m *PollForDecisionTaskResponse) GetAttempt() int32 { if m != nil { return m.Attempt } return 0 } func (m *PollForDecisionTaskResponse) GetNextEventId() int64 { if m != nil { return m.NextEventId } return 0 } func (m *PollForDecisionTaskResponse) GetBacklogCountHint() int64 { if m != nil { return m.BacklogCountHint } return 0 } func (m *PollForDecisionTaskResponse) GetStickyExecutionEnabled() bool { if m != nil { return m.StickyExecutionEnabled } return false } func (m *PollForDecisionTaskResponse) GetQuery() *v1.WorkflowQuery { if m != nil { return m.Query } return nil } func (m *PollForDecisionTaskResponse) GetDecisionInfo() *v11.TransientDecisionInfo { if m != nil { return m.DecisionInfo } return nil } func (m *PollForDecisionTaskResponse) GetWorkflowExecutionTaskList() *v1.TaskList { if m != nil { return m.WorkflowExecutionTaskList } return nil } func (m *PollForDecisionTaskResponse) GetEventStoreVersion() int32 { if m != nil { return m.EventStoreVersion } return 0 } func (m *PollForDecisionTaskResponse) GetBranchToken() []byte { if m != nil { return m.BranchToken } return nil } func (m *PollForDecisionTaskResponse) GetScheduledTime() *types.Timestamp { if m != nil { return m.ScheduledTime } return nil } func (m *PollForDecisionTaskResponse) GetStartedTime() *types.Timestamp { if m != nil { return m.StartedTime } return nil } func (m *PollForDecisionTaskResponse) GetQueries() map[string]*v1.WorkflowQuery { if m != nil { return m.Queries } return nil } func (m *PollForDecisionTaskResponse) GetTotalHistoryBytes() int64 { if m != nil { return m.TotalHistoryBytes } return 0 } func (m *PollForDecisionTaskResponse) GetPartitionConfig() *TaskListPartitionConfig { if m != nil { return m.PartitionConfig } return nil } func (m *PollForDecisionTaskResponse) GetLoadBalancerHints() *LoadBalancerHints { if m != nil { return m.LoadBalancerHints } return nil } func (m *PollForDecisionTaskResponse) GetAutoConfigHint() *v1.AutoConfigHint { if m != nil { return m.AutoConfigHint } return nil } type PollForActivityTaskRequest struct { Request *v1.PollForActivityTaskRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` PollerId string `protobuf:"bytes,3,opt,name=poller_id,json=pollerId,proto3" json:"poller_id,omitempty"` ForwardedFrom string `protobuf:"bytes,4,opt,name=forwarded_from,json=forwardedFrom,proto3" json:"forwarded_from,omitempty"` IsolationGroup string `protobuf:"bytes,5,opt,name=isolation_group,json=isolationGroup,proto3" json:"isolation_group,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *PollForActivityTaskRequest) Reset() { *m = PollForActivityTaskRequest{} } func (m *PollForActivityTaskRequest) String() string { return proto.CompactTextString(m) } func (*PollForActivityTaskRequest) ProtoMessage() {} func (*PollForActivityTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{5} } func (m *PollForActivityTaskRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *PollForActivityTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_PollForActivityTaskRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *PollForActivityTaskRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_PollForActivityTaskRequest.Merge(m, src) } func (m *PollForActivityTaskRequest) XXX_Size() int { return m.Size() } func (m *PollForActivityTaskRequest) XXX_DiscardUnknown() { xxx_messageInfo_PollForActivityTaskRequest.DiscardUnknown(m) } var xxx_messageInfo_PollForActivityTaskRequest proto.InternalMessageInfo func (m *PollForActivityTaskRequest) GetRequest() *v1.PollForActivityTaskRequest { if m != nil { return m.Request } return nil } func (m *PollForActivityTaskRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *PollForActivityTaskRequest) GetPollerId() string { if m != nil { return m.PollerId } return "" } func (m *PollForActivityTaskRequest) GetForwardedFrom() string { if m != nil { return m.ForwardedFrom } return "" } func (m *PollForActivityTaskRequest) GetIsolationGroup() string { if m != nil { return m.IsolationGroup } return "" } type PollForActivityTaskResponse struct { TaskToken []byte `protobuf:"bytes,1,opt,name=task_token,json=taskToken,proto3" json:"task_token,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` ActivityId string `protobuf:"bytes,3,opt,name=activity_id,json=activityId,proto3" json:"activity_id,omitempty"` ActivityType *v1.ActivityType `protobuf:"bytes,4,opt,name=activity_type,json=activityType,proto3" json:"activity_type,omitempty"` Input *v1.Payload `protobuf:"bytes,5,opt,name=input,proto3" json:"input,omitempty"` ScheduledTime *types.Timestamp `protobuf:"bytes,6,opt,name=scheduled_time,json=scheduledTime,proto3" json:"scheduled_time,omitempty"` StartedTime *types.Timestamp `protobuf:"bytes,7,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"` ScheduleToCloseTimeout *types.Duration `protobuf:"bytes,8,opt,name=schedule_to_close_timeout,json=scheduleToCloseTimeout,proto3" json:"schedule_to_close_timeout,omitempty"` StartToCloseTimeout *types.Duration `protobuf:"bytes,9,opt,name=start_to_close_timeout,json=startToCloseTimeout,proto3" json:"start_to_close_timeout,omitempty"` HeartbeatTimeout *types.Duration `protobuf:"bytes,10,opt,name=heartbeat_timeout,json=heartbeatTimeout,proto3" json:"heartbeat_timeout,omitempty"` Attempt int32 `protobuf:"varint,11,opt,name=attempt,proto3" json:"attempt,omitempty"` ScheduledTimeOfThisAttempt *types.Timestamp `protobuf:"bytes,12,opt,name=scheduled_time_of_this_attempt,json=scheduledTimeOfThisAttempt,proto3" json:"scheduled_time_of_this_attempt,omitempty"` HeartbeatDetails *v1.Payload `protobuf:"bytes,13,opt,name=heartbeat_details,json=heartbeatDetails,proto3" json:"heartbeat_details,omitempty"` WorkflowType *v1.WorkflowType `protobuf:"bytes,14,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` WorkflowDomain string `protobuf:"bytes,15,opt,name=workflow_domain,json=workflowDomain,proto3" json:"workflow_domain,omitempty"` Header *v1.Header `protobuf:"bytes,16,opt,name=header,proto3" json:"header,omitempty"` LoadBalancerHints *LoadBalancerHints `protobuf:"bytes,17,opt,name=load_balancer_hints,json=loadBalancerHints,proto3" json:"load_balancer_hints,omitempty"` PartitionConfig *TaskListPartitionConfig `protobuf:"bytes,19,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` AutoConfigHint *v1.AutoConfigHint `protobuf:"bytes,20,opt,name=auto_config_hint,json=autoConfigHint,proto3" json:"auto_config_hint,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *PollForActivityTaskResponse) Reset() { *m = PollForActivityTaskResponse{} } func (m *PollForActivityTaskResponse) String() string { return proto.CompactTextString(m) } func (*PollForActivityTaskResponse) ProtoMessage() {} func (*PollForActivityTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{6} } func (m *PollForActivityTaskResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *PollForActivityTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_PollForActivityTaskResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *PollForActivityTaskResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_PollForActivityTaskResponse.Merge(m, src) } func (m *PollForActivityTaskResponse) XXX_Size() int { return m.Size() } func (m *PollForActivityTaskResponse) XXX_DiscardUnknown() { xxx_messageInfo_PollForActivityTaskResponse.DiscardUnknown(m) } var xxx_messageInfo_PollForActivityTaskResponse proto.InternalMessageInfo func (m *PollForActivityTaskResponse) GetTaskToken() []byte { if m != nil { return m.TaskToken } return nil } func (m *PollForActivityTaskResponse) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *PollForActivityTaskResponse) GetActivityId() string { if m != nil { return m.ActivityId } return "" } func (m *PollForActivityTaskResponse) GetActivityType() *v1.ActivityType { if m != nil { return m.ActivityType } return nil } func (m *PollForActivityTaskResponse) GetInput() *v1.Payload { if m != nil { return m.Input } return nil } func (m *PollForActivityTaskResponse) GetScheduledTime() *types.Timestamp { if m != nil { return m.ScheduledTime } return nil } func (m *PollForActivityTaskResponse) GetStartedTime() *types.Timestamp { if m != nil { return m.StartedTime } return nil } func (m *PollForActivityTaskResponse) GetScheduleToCloseTimeout() *types.Duration { if m != nil { return m.ScheduleToCloseTimeout } return nil } func (m *PollForActivityTaskResponse) GetStartToCloseTimeout() *types.Duration { if m != nil { return m.StartToCloseTimeout } return nil } func (m *PollForActivityTaskResponse) GetHeartbeatTimeout() *types.Duration { if m != nil { return m.HeartbeatTimeout } return nil } func (m *PollForActivityTaskResponse) GetAttempt() int32 { if m != nil { return m.Attempt } return 0 } func (m *PollForActivityTaskResponse) GetScheduledTimeOfThisAttempt() *types.Timestamp { if m != nil { return m.ScheduledTimeOfThisAttempt } return nil } func (m *PollForActivityTaskResponse) GetHeartbeatDetails() *v1.Payload { if m != nil { return m.HeartbeatDetails } return nil } func (m *PollForActivityTaskResponse) GetWorkflowType() *v1.WorkflowType { if m != nil { return m.WorkflowType } return nil } func (m *PollForActivityTaskResponse) GetWorkflowDomain() string { if m != nil { return m.WorkflowDomain } return "" } func (m *PollForActivityTaskResponse) GetHeader() *v1.Header { if m != nil { return m.Header } return nil } func (m *PollForActivityTaskResponse) GetLoadBalancerHints() *LoadBalancerHints { if m != nil { return m.LoadBalancerHints } return nil } func (m *PollForActivityTaskResponse) GetPartitionConfig() *TaskListPartitionConfig { if m != nil { return m.PartitionConfig } return nil } func (m *PollForActivityTaskResponse) GetAutoConfigHint() *v1.AutoConfigHint { if m != nil { return m.AutoConfigHint } return nil } type AddDecisionTaskRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,3,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` ScheduleId int64 `protobuf:"varint,4,opt,name=schedule_id,json=scheduleId,proto3" json:"schedule_id,omitempty"` ScheduleToStartTimeout *types.Duration `protobuf:"bytes,5,opt,name=schedule_to_start_timeout,json=scheduleToStartTimeout,proto3" json:"schedule_to_start_timeout,omitempty"` Source v11.TaskSource `protobuf:"varint,6,opt,name=source,proto3,enum=uber.cadence.shared.v1.TaskSource" json:"source,omitempty"` ForwardedFrom string `protobuf:"bytes,7,opt,name=forwarded_from,json=forwardedFrom,proto3" json:"forwarded_from,omitempty"` PartitionConfig map[string]string `protobuf:"bytes,8,rep,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *AddDecisionTaskRequest) Reset() { *m = AddDecisionTaskRequest{} } func (m *AddDecisionTaskRequest) String() string { return proto.CompactTextString(m) } func (*AddDecisionTaskRequest) ProtoMessage() {} func (*AddDecisionTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{7} } func (m *AddDecisionTaskRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *AddDecisionTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_AddDecisionTaskRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *AddDecisionTaskRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_AddDecisionTaskRequest.Merge(m, src) } func (m *AddDecisionTaskRequest) XXX_Size() int { return m.Size() } func (m *AddDecisionTaskRequest) XXX_DiscardUnknown() { xxx_messageInfo_AddDecisionTaskRequest.DiscardUnknown(m) } var xxx_messageInfo_AddDecisionTaskRequest proto.InternalMessageInfo func (m *AddDecisionTaskRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *AddDecisionTaskRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *AddDecisionTaskRequest) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } func (m *AddDecisionTaskRequest) GetScheduleId() int64 { if m != nil { return m.ScheduleId } return 0 } func (m *AddDecisionTaskRequest) GetScheduleToStartTimeout() *types.Duration { if m != nil { return m.ScheduleToStartTimeout } return nil } func (m *AddDecisionTaskRequest) GetSource() v11.TaskSource { if m != nil { return m.Source } return v11.TaskSource_TASK_SOURCE_INVALID } func (m *AddDecisionTaskRequest) GetForwardedFrom() string { if m != nil { return m.ForwardedFrom } return "" } func (m *AddDecisionTaskRequest) GetPartitionConfig() map[string]string { if m != nil { return m.PartitionConfig } return nil } type AddDecisionTaskResponse struct { PartitionConfig *TaskListPartitionConfig `protobuf:"bytes,1,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *AddDecisionTaskResponse) Reset() { *m = AddDecisionTaskResponse{} } func (m *AddDecisionTaskResponse) String() string { return proto.CompactTextString(m) } func (*AddDecisionTaskResponse) ProtoMessage() {} func (*AddDecisionTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{8} } func (m *AddDecisionTaskResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *AddDecisionTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_AddDecisionTaskResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *AddDecisionTaskResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_AddDecisionTaskResponse.Merge(m, src) } func (m *AddDecisionTaskResponse) XXX_Size() int { return m.Size() } func (m *AddDecisionTaskResponse) XXX_DiscardUnknown() { xxx_messageInfo_AddDecisionTaskResponse.DiscardUnknown(m) } var xxx_messageInfo_AddDecisionTaskResponse proto.InternalMessageInfo func (m *AddDecisionTaskResponse) GetPartitionConfig() *TaskListPartitionConfig { if m != nil { return m.PartitionConfig } return nil } type AddActivityTaskRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` SourceDomainId string `protobuf:"bytes,3,opt,name=source_domain_id,json=sourceDomainId,proto3" json:"source_domain_id,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,4,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` ScheduleId int64 `protobuf:"varint,5,opt,name=schedule_id,json=scheduleId,proto3" json:"schedule_id,omitempty"` ScheduleToStartTimeout *types.Duration `protobuf:"bytes,6,opt,name=schedule_to_start_timeout,json=scheduleToStartTimeout,proto3" json:"schedule_to_start_timeout,omitempty"` Source v11.TaskSource `protobuf:"varint,7,opt,name=source,proto3,enum=uber.cadence.shared.v1.TaskSource" json:"source,omitempty"` ForwardedFrom string `protobuf:"bytes,8,opt,name=forwarded_from,json=forwardedFrom,proto3" json:"forwarded_from,omitempty"` ActivityTaskDispatchInfo *ActivityTaskDispatchInfo `protobuf:"bytes,9,opt,name=activityTaskDispatchInfo,proto3" json:"activityTaskDispatchInfo,omitempty"` PartitionConfig map[string]string `protobuf:"bytes,10,rep,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *AddActivityTaskRequest) Reset() { *m = AddActivityTaskRequest{} } func (m *AddActivityTaskRequest) String() string { return proto.CompactTextString(m) } func (*AddActivityTaskRequest) ProtoMessage() {} func (*AddActivityTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{9} } func (m *AddActivityTaskRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *AddActivityTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_AddActivityTaskRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *AddActivityTaskRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_AddActivityTaskRequest.Merge(m, src) } func (m *AddActivityTaskRequest) XXX_Size() int { return m.Size() } func (m *AddActivityTaskRequest) XXX_DiscardUnknown() { xxx_messageInfo_AddActivityTaskRequest.DiscardUnknown(m) } var xxx_messageInfo_AddActivityTaskRequest proto.InternalMessageInfo func (m *AddActivityTaskRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *AddActivityTaskRequest) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *AddActivityTaskRequest) GetSourceDomainId() string { if m != nil { return m.SourceDomainId } return "" } func (m *AddActivityTaskRequest) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } func (m *AddActivityTaskRequest) GetScheduleId() int64 { if m != nil { return m.ScheduleId } return 0 } func (m *AddActivityTaskRequest) GetScheduleToStartTimeout() *types.Duration { if m != nil { return m.ScheduleToStartTimeout } return nil } func (m *AddActivityTaskRequest) GetSource() v11.TaskSource { if m != nil { return m.Source } return v11.TaskSource_TASK_SOURCE_INVALID } func (m *AddActivityTaskRequest) GetForwardedFrom() string { if m != nil { return m.ForwardedFrom } return "" } func (m *AddActivityTaskRequest) GetActivityTaskDispatchInfo() *ActivityTaskDispatchInfo { if m != nil { return m.ActivityTaskDispatchInfo } return nil } func (m *AddActivityTaskRequest) GetPartitionConfig() map[string]string { if m != nil { return m.PartitionConfig } return nil } type ActivityTaskDispatchInfo struct { ScheduledEvent *v1.HistoryEvent `protobuf:"bytes,1,opt,name=scheduled_event,json=scheduledEvent,proto3" json:"scheduled_event,omitempty"` StartedTime *types.Timestamp `protobuf:"bytes,2,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"` Attempt int32 `protobuf:"varint,3,opt,name=attempt,proto3" json:"attempt,omitempty"` ScheduledTimeOfThisAttempt *types.Timestamp `protobuf:"bytes,4,opt,name=scheduled_time_of_this_attempt,json=scheduledTimeOfThisAttempt,proto3" json:"scheduled_time_of_this_attempt,omitempty"` HeartbeatDetails *v1.Payload `protobuf:"bytes,5,opt,name=heartbeat_details,json=heartbeatDetails,proto3" json:"heartbeat_details,omitempty"` WorkflowType *v1.WorkflowType `protobuf:"bytes,6,opt,name=workflow_type,json=workflowType,proto3" json:"workflow_type,omitempty"` WorkflowDomain string `protobuf:"bytes,7,opt,name=workflow_domain,json=workflowDomain,proto3" json:"workflow_domain,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ActivityTaskDispatchInfo) Reset() { *m = ActivityTaskDispatchInfo{} } func (m *ActivityTaskDispatchInfo) String() string { return proto.CompactTextString(m) } func (*ActivityTaskDispatchInfo) ProtoMessage() {} func (*ActivityTaskDispatchInfo) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{10} } func (m *ActivityTaskDispatchInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ActivityTaskDispatchInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ActivityTaskDispatchInfo.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ActivityTaskDispatchInfo) XXX_Merge(src proto.Message) { xxx_messageInfo_ActivityTaskDispatchInfo.Merge(m, src) } func (m *ActivityTaskDispatchInfo) XXX_Size() int { return m.Size() } func (m *ActivityTaskDispatchInfo) XXX_DiscardUnknown() { xxx_messageInfo_ActivityTaskDispatchInfo.DiscardUnknown(m) } var xxx_messageInfo_ActivityTaskDispatchInfo proto.InternalMessageInfo func (m *ActivityTaskDispatchInfo) GetScheduledEvent() *v1.HistoryEvent { if m != nil { return m.ScheduledEvent } return nil } func (m *ActivityTaskDispatchInfo) GetStartedTime() *types.Timestamp { if m != nil { return m.StartedTime } return nil } func (m *ActivityTaskDispatchInfo) GetAttempt() int32 { if m != nil { return m.Attempt } return 0 } func (m *ActivityTaskDispatchInfo) GetScheduledTimeOfThisAttempt() *types.Timestamp { if m != nil { return m.ScheduledTimeOfThisAttempt } return nil } func (m *ActivityTaskDispatchInfo) GetHeartbeatDetails() *v1.Payload { if m != nil { return m.HeartbeatDetails } return nil } func (m *ActivityTaskDispatchInfo) GetWorkflowType() *v1.WorkflowType { if m != nil { return m.WorkflowType } return nil } func (m *ActivityTaskDispatchInfo) GetWorkflowDomain() string { if m != nil { return m.WorkflowDomain } return "" } type AddActivityTaskResponse struct { PartitionConfig *TaskListPartitionConfig `protobuf:"bytes,1,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *AddActivityTaskResponse) Reset() { *m = AddActivityTaskResponse{} } func (m *AddActivityTaskResponse) String() string { return proto.CompactTextString(m) } func (*AddActivityTaskResponse) ProtoMessage() {} func (*AddActivityTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{11} } func (m *AddActivityTaskResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *AddActivityTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_AddActivityTaskResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *AddActivityTaskResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_AddActivityTaskResponse.Merge(m, src) } func (m *AddActivityTaskResponse) XXX_Size() int { return m.Size() } func (m *AddActivityTaskResponse) XXX_DiscardUnknown() { xxx_messageInfo_AddActivityTaskResponse.DiscardUnknown(m) } var xxx_messageInfo_AddActivityTaskResponse proto.InternalMessageInfo func (m *AddActivityTaskResponse) GetPartitionConfig() *TaskListPartitionConfig { if m != nil { return m.PartitionConfig } return nil } type QueryWorkflowRequest struct { Request *v1.QueryWorkflowRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,3,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` ForwardedFrom string `protobuf:"bytes,4,opt,name=forwarded_from,json=forwardedFrom,proto3" json:"forwarded_from,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *QueryWorkflowRequest) Reset() { *m = QueryWorkflowRequest{} } func (m *QueryWorkflowRequest) String() string { return proto.CompactTextString(m) } func (*QueryWorkflowRequest) ProtoMessage() {} func (*QueryWorkflowRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{12} } func (m *QueryWorkflowRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *QueryWorkflowRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_QueryWorkflowRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *QueryWorkflowRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_QueryWorkflowRequest.Merge(m, src) } func (m *QueryWorkflowRequest) XXX_Size() int { return m.Size() } func (m *QueryWorkflowRequest) XXX_DiscardUnknown() { xxx_messageInfo_QueryWorkflowRequest.DiscardUnknown(m) } var xxx_messageInfo_QueryWorkflowRequest proto.InternalMessageInfo func (m *QueryWorkflowRequest) GetRequest() *v1.QueryWorkflowRequest { if m != nil { return m.Request } return nil } func (m *QueryWorkflowRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *QueryWorkflowRequest) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } func (m *QueryWorkflowRequest) GetForwardedFrom() string { if m != nil { return m.ForwardedFrom } return "" } type QueryWorkflowResponse struct { QueryResult *v1.Payload `protobuf:"bytes,1,opt,name=query_result,json=queryResult,proto3" json:"query_result,omitempty"` QueryRejected *v1.QueryRejected `protobuf:"bytes,2,opt,name=query_rejected,json=queryRejected,proto3" json:"query_rejected,omitempty"` PartitionConfig *v1.TaskListPartitionConfig `protobuf:"bytes,3,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *QueryWorkflowResponse) Reset() { *m = QueryWorkflowResponse{} } func (m *QueryWorkflowResponse) String() string { return proto.CompactTextString(m) } func (*QueryWorkflowResponse) ProtoMessage() {} func (*QueryWorkflowResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{13} } func (m *QueryWorkflowResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *QueryWorkflowResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_QueryWorkflowResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *QueryWorkflowResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_QueryWorkflowResponse.Merge(m, src) } func (m *QueryWorkflowResponse) XXX_Size() int { return m.Size() } func (m *QueryWorkflowResponse) XXX_DiscardUnknown() { xxx_messageInfo_QueryWorkflowResponse.DiscardUnknown(m) } var xxx_messageInfo_QueryWorkflowResponse proto.InternalMessageInfo func (m *QueryWorkflowResponse) GetQueryResult() *v1.Payload { if m != nil { return m.QueryResult } return nil } func (m *QueryWorkflowResponse) GetQueryRejected() *v1.QueryRejected { if m != nil { return m.QueryRejected } return nil } func (m *QueryWorkflowResponse) GetPartitionConfig() *v1.TaskListPartitionConfig { if m != nil { return m.PartitionConfig } return nil } type RespondQueryTaskCompletedRequest struct { Request *v1.RespondQueryTaskCompletedRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,3,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` TaskId string `protobuf:"bytes,4,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondQueryTaskCompletedRequest) Reset() { *m = RespondQueryTaskCompletedRequest{} } func (m *RespondQueryTaskCompletedRequest) String() string { return proto.CompactTextString(m) } func (*RespondQueryTaskCompletedRequest) ProtoMessage() {} func (*RespondQueryTaskCompletedRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{14} } func (m *RespondQueryTaskCompletedRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondQueryTaskCompletedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondQueryTaskCompletedRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondQueryTaskCompletedRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondQueryTaskCompletedRequest.Merge(m, src) } func (m *RespondQueryTaskCompletedRequest) XXX_Size() int { return m.Size() } func (m *RespondQueryTaskCompletedRequest) XXX_DiscardUnknown() { xxx_messageInfo_RespondQueryTaskCompletedRequest.DiscardUnknown(m) } var xxx_messageInfo_RespondQueryTaskCompletedRequest proto.InternalMessageInfo func (m *RespondQueryTaskCompletedRequest) GetRequest() *v1.RespondQueryTaskCompletedRequest { if m != nil { return m.Request } return nil } func (m *RespondQueryTaskCompletedRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *RespondQueryTaskCompletedRequest) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } func (m *RespondQueryTaskCompletedRequest) GetTaskId() string { if m != nil { return m.TaskId } return "" } type RespondQueryTaskCompletedResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RespondQueryTaskCompletedResponse) Reset() { *m = RespondQueryTaskCompletedResponse{} } func (m *RespondQueryTaskCompletedResponse) String() string { return proto.CompactTextString(m) } func (*RespondQueryTaskCompletedResponse) ProtoMessage() {} func (*RespondQueryTaskCompletedResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{15} } func (m *RespondQueryTaskCompletedResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RespondQueryTaskCompletedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RespondQueryTaskCompletedResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RespondQueryTaskCompletedResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RespondQueryTaskCompletedResponse.Merge(m, src) } func (m *RespondQueryTaskCompletedResponse) XXX_Size() int { return m.Size() } func (m *RespondQueryTaskCompletedResponse) XXX_DiscardUnknown() { xxx_messageInfo_RespondQueryTaskCompletedResponse.DiscardUnknown(m) } var xxx_messageInfo_RespondQueryTaskCompletedResponse proto.InternalMessageInfo type CancelOutstandingPollRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` PollerId string `protobuf:"bytes,2,opt,name=poller_id,json=pollerId,proto3" json:"poller_id,omitempty"` TaskListType v1.TaskListType `protobuf:"varint,3,opt,name=task_list_type,json=taskListType,proto3,enum=uber.cadence.api.v1.TaskListType" json:"task_list_type,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,4,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *CancelOutstandingPollRequest) Reset() { *m = CancelOutstandingPollRequest{} } func (m *CancelOutstandingPollRequest) String() string { return proto.CompactTextString(m) } func (*CancelOutstandingPollRequest) ProtoMessage() {} func (*CancelOutstandingPollRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{16} } func (m *CancelOutstandingPollRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *CancelOutstandingPollRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_CancelOutstandingPollRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *CancelOutstandingPollRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_CancelOutstandingPollRequest.Merge(m, src) } func (m *CancelOutstandingPollRequest) XXX_Size() int { return m.Size() } func (m *CancelOutstandingPollRequest) XXX_DiscardUnknown() { xxx_messageInfo_CancelOutstandingPollRequest.DiscardUnknown(m) } var xxx_messageInfo_CancelOutstandingPollRequest proto.InternalMessageInfo func (m *CancelOutstandingPollRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *CancelOutstandingPollRequest) GetPollerId() string { if m != nil { return m.PollerId } return "" } func (m *CancelOutstandingPollRequest) GetTaskListType() v1.TaskListType { if m != nil { return m.TaskListType } return v1.TaskListType_TASK_LIST_TYPE_INVALID } func (m *CancelOutstandingPollRequest) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } type CancelOutstandingPollResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *CancelOutstandingPollResponse) Reset() { *m = CancelOutstandingPollResponse{} } func (m *CancelOutstandingPollResponse) String() string { return proto.CompactTextString(m) } func (*CancelOutstandingPollResponse) ProtoMessage() {} func (*CancelOutstandingPollResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{17} } func (m *CancelOutstandingPollResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *CancelOutstandingPollResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_CancelOutstandingPollResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *CancelOutstandingPollResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_CancelOutstandingPollResponse.Merge(m, src) } func (m *CancelOutstandingPollResponse) XXX_Size() int { return m.Size() } func (m *CancelOutstandingPollResponse) XXX_DiscardUnknown() { xxx_messageInfo_CancelOutstandingPollResponse.DiscardUnknown(m) } var xxx_messageInfo_CancelOutstandingPollResponse proto.InternalMessageInfo type DescribeTaskListRequest struct { Request *v1.DescribeTaskListRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` DomainId string `protobuf:"bytes,2,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DescribeTaskListRequest) Reset() { *m = DescribeTaskListRequest{} } func (m *DescribeTaskListRequest) String() string { return proto.CompactTextString(m) } func (*DescribeTaskListRequest) ProtoMessage() {} func (*DescribeTaskListRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{18} } func (m *DescribeTaskListRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *DescribeTaskListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_DescribeTaskListRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *DescribeTaskListRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_DescribeTaskListRequest.Merge(m, src) } func (m *DescribeTaskListRequest) XXX_Size() int { return m.Size() } func (m *DescribeTaskListRequest) XXX_DiscardUnknown() { xxx_messageInfo_DescribeTaskListRequest.DiscardUnknown(m) } var xxx_messageInfo_DescribeTaskListRequest proto.InternalMessageInfo func (m *DescribeTaskListRequest) GetRequest() *v1.DescribeTaskListRequest { if m != nil { return m.Request } return nil } func (m *DescribeTaskListRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } type DescribeTaskListResponse struct { Pollers []*v1.PollerInfo `protobuf:"bytes,1,rep,name=pollers,proto3" json:"pollers,omitempty"` TaskListStatus *v1.TaskListStatus `protobuf:"bytes,2,opt,name=task_list_status,json=taskListStatus,proto3" json:"task_list_status,omitempty"` PartitionConfig *v1.TaskListPartitionConfig `protobuf:"bytes,3,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,4,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *DescribeTaskListResponse) Reset() { *m = DescribeTaskListResponse{} } func (m *DescribeTaskListResponse) String() string { return proto.CompactTextString(m) } func (*DescribeTaskListResponse) ProtoMessage() {} func (*DescribeTaskListResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{19} } func (m *DescribeTaskListResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *DescribeTaskListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_DescribeTaskListResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *DescribeTaskListResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_DescribeTaskListResponse.Merge(m, src) } func (m *DescribeTaskListResponse) XXX_Size() int { return m.Size() } func (m *DescribeTaskListResponse) XXX_DiscardUnknown() { xxx_messageInfo_DescribeTaskListResponse.DiscardUnknown(m) } var xxx_messageInfo_DescribeTaskListResponse proto.InternalMessageInfo func (m *DescribeTaskListResponse) GetPollers() []*v1.PollerInfo { if m != nil { return m.Pollers } return nil } func (m *DescribeTaskListResponse) GetTaskListStatus() *v1.TaskListStatus { if m != nil { return m.TaskListStatus } return nil } func (m *DescribeTaskListResponse) GetPartitionConfig() *v1.TaskListPartitionConfig { if m != nil { return m.PartitionConfig } return nil } func (m *DescribeTaskListResponse) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } type ListTaskListPartitionsRequest struct { Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,2,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ListTaskListPartitionsRequest) Reset() { *m = ListTaskListPartitionsRequest{} } func (m *ListTaskListPartitionsRequest) String() string { return proto.CompactTextString(m) } func (*ListTaskListPartitionsRequest) ProtoMessage() {} func (*ListTaskListPartitionsRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{20} } func (m *ListTaskListPartitionsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ListTaskListPartitionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ListTaskListPartitionsRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ListTaskListPartitionsRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_ListTaskListPartitionsRequest.Merge(m, src) } func (m *ListTaskListPartitionsRequest) XXX_Size() int { return m.Size() } func (m *ListTaskListPartitionsRequest) XXX_DiscardUnknown() { xxx_messageInfo_ListTaskListPartitionsRequest.DiscardUnknown(m) } var xxx_messageInfo_ListTaskListPartitionsRequest proto.InternalMessageInfo func (m *ListTaskListPartitionsRequest) GetDomain() string { if m != nil { return m.Domain } return "" } func (m *ListTaskListPartitionsRequest) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } type ListTaskListPartitionsResponse struct { ActivityTaskListPartitions []*v1.TaskListPartitionMetadata `protobuf:"bytes,1,rep,name=activity_task_list_partitions,json=activityTaskListPartitions,proto3" json:"activity_task_list_partitions,omitempty"` DecisionTaskListPartitions []*v1.TaskListPartitionMetadata `protobuf:"bytes,2,rep,name=decision_task_list_partitions,json=decisionTaskListPartitions,proto3" json:"decision_task_list_partitions,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ListTaskListPartitionsResponse) Reset() { *m = ListTaskListPartitionsResponse{} } func (m *ListTaskListPartitionsResponse) String() string { return proto.CompactTextString(m) } func (*ListTaskListPartitionsResponse) ProtoMessage() {} func (*ListTaskListPartitionsResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{21} } func (m *ListTaskListPartitionsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ListTaskListPartitionsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ListTaskListPartitionsResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ListTaskListPartitionsResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_ListTaskListPartitionsResponse.Merge(m, src) } func (m *ListTaskListPartitionsResponse) XXX_Size() int { return m.Size() } func (m *ListTaskListPartitionsResponse) XXX_DiscardUnknown() { xxx_messageInfo_ListTaskListPartitionsResponse.DiscardUnknown(m) } var xxx_messageInfo_ListTaskListPartitionsResponse proto.InternalMessageInfo func (m *ListTaskListPartitionsResponse) GetActivityTaskListPartitions() []*v1.TaskListPartitionMetadata { if m != nil { return m.ActivityTaskListPartitions } return nil } func (m *ListTaskListPartitionsResponse) GetDecisionTaskListPartitions() []*v1.TaskListPartitionMetadata { if m != nil { return m.DecisionTaskListPartitions } return nil } type GetTaskListsByDomainRequest struct { Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetTaskListsByDomainRequest) Reset() { *m = GetTaskListsByDomainRequest{} } func (m *GetTaskListsByDomainRequest) String() string { return proto.CompactTextString(m) } func (*GetTaskListsByDomainRequest) ProtoMessage() {} func (*GetTaskListsByDomainRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{22} } func (m *GetTaskListsByDomainRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetTaskListsByDomainRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetTaskListsByDomainRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetTaskListsByDomainRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_GetTaskListsByDomainRequest.Merge(m, src) } func (m *GetTaskListsByDomainRequest) XXX_Size() int { return m.Size() } func (m *GetTaskListsByDomainRequest) XXX_DiscardUnknown() { xxx_messageInfo_GetTaskListsByDomainRequest.DiscardUnknown(m) } var xxx_messageInfo_GetTaskListsByDomainRequest proto.InternalMessageInfo func (m *GetTaskListsByDomainRequest) GetDomain() string { if m != nil { return m.Domain } return "" } type GetTaskListsByDomainResponse struct { DecisionTaskListMap map[string]*DescribeTaskListResponse `protobuf:"bytes,1,rep,name=decision_task_list_map,json=decisionTaskListMap,proto3" json:"decision_task_list_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` ActivityTaskListMap map[string]*DescribeTaskListResponse `protobuf:"bytes,2,rep,name=activity_task_list_map,json=activityTaskListMap,proto3" json:"activity_task_list_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetTaskListsByDomainResponse) Reset() { *m = GetTaskListsByDomainResponse{} } func (m *GetTaskListsByDomainResponse) String() string { return proto.CompactTextString(m) } func (*GetTaskListsByDomainResponse) ProtoMessage() {} func (*GetTaskListsByDomainResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{23} } func (m *GetTaskListsByDomainResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetTaskListsByDomainResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetTaskListsByDomainResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetTaskListsByDomainResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_GetTaskListsByDomainResponse.Merge(m, src) } func (m *GetTaskListsByDomainResponse) XXX_Size() int { return m.Size() } func (m *GetTaskListsByDomainResponse) XXX_DiscardUnknown() { xxx_messageInfo_GetTaskListsByDomainResponse.DiscardUnknown(m) } var xxx_messageInfo_GetTaskListsByDomainResponse proto.InternalMessageInfo func (m *GetTaskListsByDomainResponse) GetDecisionTaskListMap() map[string]*DescribeTaskListResponse { if m != nil { return m.DecisionTaskListMap } return nil } func (m *GetTaskListsByDomainResponse) GetActivityTaskListMap() map[string]*DescribeTaskListResponse { if m != nil { return m.ActivityTaskListMap } return nil } type UpdateTaskListPartitionConfigRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,2,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` TaskListType v1.TaskListType `protobuf:"varint,3,opt,name=task_list_type,json=taskListType,proto3,enum=uber.cadence.api.v1.TaskListType" json:"task_list_type,omitempty"` PartitionConfig *v1.TaskListPartitionConfig `protobuf:"bytes,4,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *UpdateTaskListPartitionConfigRequest) Reset() { *m = UpdateTaskListPartitionConfigRequest{} } func (m *UpdateTaskListPartitionConfigRequest) String() string { return proto.CompactTextString(m) } func (*UpdateTaskListPartitionConfigRequest) ProtoMessage() {} func (*UpdateTaskListPartitionConfigRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{24} } func (m *UpdateTaskListPartitionConfigRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *UpdateTaskListPartitionConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_UpdateTaskListPartitionConfigRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *UpdateTaskListPartitionConfigRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_UpdateTaskListPartitionConfigRequest.Merge(m, src) } func (m *UpdateTaskListPartitionConfigRequest) XXX_Size() int { return m.Size() } func (m *UpdateTaskListPartitionConfigRequest) XXX_DiscardUnknown() { xxx_messageInfo_UpdateTaskListPartitionConfigRequest.DiscardUnknown(m) } var xxx_messageInfo_UpdateTaskListPartitionConfigRequest proto.InternalMessageInfo func (m *UpdateTaskListPartitionConfigRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *UpdateTaskListPartitionConfigRequest) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } func (m *UpdateTaskListPartitionConfigRequest) GetTaskListType() v1.TaskListType { if m != nil { return m.TaskListType } return v1.TaskListType_TASK_LIST_TYPE_INVALID } func (m *UpdateTaskListPartitionConfigRequest) GetPartitionConfig() *v1.TaskListPartitionConfig { if m != nil { return m.PartitionConfig } return nil } type UpdateTaskListPartitionConfigResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *UpdateTaskListPartitionConfigResponse) Reset() { *m = UpdateTaskListPartitionConfigResponse{} } func (m *UpdateTaskListPartitionConfigResponse) String() string { return proto.CompactTextString(m) } func (*UpdateTaskListPartitionConfigResponse) ProtoMessage() {} func (*UpdateTaskListPartitionConfigResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{25} } func (m *UpdateTaskListPartitionConfigResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *UpdateTaskListPartitionConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_UpdateTaskListPartitionConfigResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *UpdateTaskListPartitionConfigResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_UpdateTaskListPartitionConfigResponse.Merge(m, src) } func (m *UpdateTaskListPartitionConfigResponse) XXX_Size() int { return m.Size() } func (m *UpdateTaskListPartitionConfigResponse) XXX_DiscardUnknown() { xxx_messageInfo_UpdateTaskListPartitionConfigResponse.DiscardUnknown(m) } var xxx_messageInfo_UpdateTaskListPartitionConfigResponse proto.InternalMessageInfo type RefreshTaskListPartitionConfigRequest struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` TaskList *v1.TaskList `protobuf:"bytes,2,opt,name=task_list,json=taskList,proto3" json:"task_list,omitempty"` TaskListType v1.TaskListType `protobuf:"varint,3,opt,name=task_list_type,json=taskListType,proto3,enum=uber.cadence.api.v1.TaskListType" json:"task_list_type,omitempty"` PartitionConfig *v1.TaskListPartitionConfig `protobuf:"bytes,4,opt,name=partition_config,json=partitionConfig,proto3" json:"partition_config,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RefreshTaskListPartitionConfigRequest) Reset() { *m = RefreshTaskListPartitionConfigRequest{} } func (m *RefreshTaskListPartitionConfigRequest) String() string { return proto.CompactTextString(m) } func (*RefreshTaskListPartitionConfigRequest) ProtoMessage() {} func (*RefreshTaskListPartitionConfigRequest) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{26} } func (m *RefreshTaskListPartitionConfigRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RefreshTaskListPartitionConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RefreshTaskListPartitionConfigRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RefreshTaskListPartitionConfigRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_RefreshTaskListPartitionConfigRequest.Merge(m, src) } func (m *RefreshTaskListPartitionConfigRequest) XXX_Size() int { return m.Size() } func (m *RefreshTaskListPartitionConfigRequest) XXX_DiscardUnknown() { xxx_messageInfo_RefreshTaskListPartitionConfigRequest.DiscardUnknown(m) } var xxx_messageInfo_RefreshTaskListPartitionConfigRequest proto.InternalMessageInfo func (m *RefreshTaskListPartitionConfigRequest) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *RefreshTaskListPartitionConfigRequest) GetTaskList() *v1.TaskList { if m != nil { return m.TaskList } return nil } func (m *RefreshTaskListPartitionConfigRequest) GetTaskListType() v1.TaskListType { if m != nil { return m.TaskListType } return v1.TaskListType_TASK_LIST_TYPE_INVALID } func (m *RefreshTaskListPartitionConfigRequest) GetPartitionConfig() *v1.TaskListPartitionConfig { if m != nil { return m.PartitionConfig } return nil } type RefreshTaskListPartitionConfigResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RefreshTaskListPartitionConfigResponse) Reset() { *m = RefreshTaskListPartitionConfigResponse{} } func (m *RefreshTaskListPartitionConfigResponse) String() string { return proto.CompactTextString(m) } func (*RefreshTaskListPartitionConfigResponse) ProtoMessage() {} func (*RefreshTaskListPartitionConfigResponse) Descriptor() ([]byte, []int) { return fileDescriptor_826e827d3aabf7fc, []int{27} } func (m *RefreshTaskListPartitionConfigResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RefreshTaskListPartitionConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RefreshTaskListPartitionConfigResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RefreshTaskListPartitionConfigResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_RefreshTaskListPartitionConfigResponse.Merge(m, src) } func (m *RefreshTaskListPartitionConfigResponse) XXX_Size() int { return m.Size() } func (m *RefreshTaskListPartitionConfigResponse) XXX_DiscardUnknown() { xxx_messageInfo_RefreshTaskListPartitionConfigResponse.DiscardUnknown(m) } var xxx_messageInfo_RefreshTaskListPartitionConfigResponse proto.InternalMessageInfo func init() { proto.RegisterType((*TaskListPartition)(nil), "uber.cadence.matching.v1.TaskListPartition") proto.RegisterType((*TaskListPartitionConfig)(nil), "uber.cadence.matching.v1.TaskListPartitionConfig") proto.RegisterMapType((map[int32]*TaskListPartition)(nil), "uber.cadence.matching.v1.TaskListPartitionConfig.ReadPartitionsEntry") proto.RegisterMapType((map[int32]*TaskListPartition)(nil), "uber.cadence.matching.v1.TaskListPartitionConfig.WritePartitionsEntry") proto.RegisterType((*LoadBalancerHints)(nil), "uber.cadence.matching.v1.LoadBalancerHints") proto.RegisterType((*PollForDecisionTaskRequest)(nil), "uber.cadence.matching.v1.PollForDecisionTaskRequest") proto.RegisterType((*PollForDecisionTaskResponse)(nil), "uber.cadence.matching.v1.PollForDecisionTaskResponse") proto.RegisterMapType((map[string]*v1.WorkflowQuery)(nil), "uber.cadence.matching.v1.PollForDecisionTaskResponse.QueriesEntry") proto.RegisterType((*PollForActivityTaskRequest)(nil), "uber.cadence.matching.v1.PollForActivityTaskRequest") proto.RegisterType((*PollForActivityTaskResponse)(nil), "uber.cadence.matching.v1.PollForActivityTaskResponse") proto.RegisterType((*AddDecisionTaskRequest)(nil), "uber.cadence.matching.v1.AddDecisionTaskRequest") proto.RegisterMapType((map[string]string)(nil), "uber.cadence.matching.v1.AddDecisionTaskRequest.PartitionConfigEntry") proto.RegisterType((*AddDecisionTaskResponse)(nil), "uber.cadence.matching.v1.AddDecisionTaskResponse") proto.RegisterType((*AddActivityTaskRequest)(nil), "uber.cadence.matching.v1.AddActivityTaskRequest") proto.RegisterMapType((map[string]string)(nil), "uber.cadence.matching.v1.AddActivityTaskRequest.PartitionConfigEntry") proto.RegisterType((*ActivityTaskDispatchInfo)(nil), "uber.cadence.matching.v1.ActivityTaskDispatchInfo") proto.RegisterType((*AddActivityTaskResponse)(nil), "uber.cadence.matching.v1.AddActivityTaskResponse") proto.RegisterType((*QueryWorkflowRequest)(nil), "uber.cadence.matching.v1.QueryWorkflowRequest") proto.RegisterType((*QueryWorkflowResponse)(nil), "uber.cadence.matching.v1.QueryWorkflowResponse") proto.RegisterType((*RespondQueryTaskCompletedRequest)(nil), "uber.cadence.matching.v1.RespondQueryTaskCompletedRequest") proto.RegisterType((*RespondQueryTaskCompletedResponse)(nil), "uber.cadence.matching.v1.RespondQueryTaskCompletedResponse") proto.RegisterType((*CancelOutstandingPollRequest)(nil), "uber.cadence.matching.v1.CancelOutstandingPollRequest") proto.RegisterType((*CancelOutstandingPollResponse)(nil), "uber.cadence.matching.v1.CancelOutstandingPollResponse") proto.RegisterType((*DescribeTaskListRequest)(nil), "uber.cadence.matching.v1.DescribeTaskListRequest") proto.RegisterType((*DescribeTaskListResponse)(nil), "uber.cadence.matching.v1.DescribeTaskListResponse") proto.RegisterType((*ListTaskListPartitionsRequest)(nil), "uber.cadence.matching.v1.ListTaskListPartitionsRequest") proto.RegisterType((*ListTaskListPartitionsResponse)(nil), "uber.cadence.matching.v1.ListTaskListPartitionsResponse") proto.RegisterType((*GetTaskListsByDomainRequest)(nil), "uber.cadence.matching.v1.GetTaskListsByDomainRequest") proto.RegisterType((*GetTaskListsByDomainResponse)(nil), "uber.cadence.matching.v1.GetTaskListsByDomainResponse") proto.RegisterMapType((map[string]*DescribeTaskListResponse)(nil), "uber.cadence.matching.v1.GetTaskListsByDomainResponse.ActivityTaskListMapEntry") proto.RegisterMapType((map[string]*DescribeTaskListResponse)(nil), "uber.cadence.matching.v1.GetTaskListsByDomainResponse.DecisionTaskListMapEntry") proto.RegisterType((*UpdateTaskListPartitionConfigRequest)(nil), "uber.cadence.matching.v1.UpdateTaskListPartitionConfigRequest") proto.RegisterType((*UpdateTaskListPartitionConfigResponse)(nil), "uber.cadence.matching.v1.UpdateTaskListPartitionConfigResponse") proto.RegisterType((*RefreshTaskListPartitionConfigRequest)(nil), "uber.cadence.matching.v1.RefreshTaskListPartitionConfigRequest") proto.RegisterType((*RefreshTaskListPartitionConfigResponse)(nil), "uber.cadence.matching.v1.RefreshTaskListPartitionConfigResponse") } func init() { proto.RegisterFile("uber/cadence/matching/v1/service.proto", fileDescriptor_826e827d3aabf7fc) } var fileDescriptor_826e827d3aabf7fc = []byte{ // 2528 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcb, 0x6f, 0x1c, 0x49, 0x19, 0x57, 0x8f, 0x3d, 0x7e, 0x7c, 0x63, 0x8f, 0xed, 0xb2, 0xd7, 0xe9, 0x4c, 0x62, 0xc7, 0x99, 0x6c, 0xb2, 0x5e, 0x58, 0xc6, 0x6b, 0xef, 0x26, 0x64, 0xb3, 0x62, 0x83, 0x1f, 0x71, 0x32, 0x68, 0x43, 0xb2, 0x1d, 0x6f, 0x22, 0xc1, 0x2a, 0x4d, 0x79, 0xba, 0xec, 0x69, 0xdc, 0xd3, 0xdd, 0xe9, 0xae, 0xb6, 0x77, 0xf6, 0xc0, 0x01, 0x01, 0x42, 0xe2, 0x0a, 0x77, 0x5e, 0xff, 0x04, 0x17, 0xce, 0x1c, 0x39, 0x22, 0xad, 0x90, 0x20, 0x12, 0x7f, 0x00, 0x48, 0xdc, 0x38, 0xa0, 0x7a, 0xf4, 0x4c, 0xf7, 0x4c, 0xf5, 0x3c, 0x6c, 0x27, 0xcb, 0x81, 0x9b, 0xbb, 0xea, 0x7b, 0xd5, 0xf7, 0xfa, 0x7d, 0x55, 0x63, 0xb8, 0x11, 0xed, 0x93, 0x60, 0xad, 0x86, 0x2d, 0xe2, 0xd6, 0xc8, 0x5a, 0x03, 0xd3, 0x5a, 0xdd, 0x76, 0x0f, 0xd7, 0x8e, 0xd7, 0xd7, 0x42, 0x12, 0x1c, 0xdb, 0x35, 0x52, 0xf1, 0x03, 0x8f, 0x7a, 0x48, 0x67, 0x74, 0x15, 0x49, 0x57, 0x89, 0xe9, 0x2a, 0xc7, 0xeb, 0xa5, 0xe5, 0x43, 0xcf, 0x3b, 0x74, 0xc8, 0x1a, 0xa7, 0xdb, 0x8f, 0x0e, 0xd6, 0xac, 0x28, 0xc0, 0xd4, 0xf6, 0x5c, 0xc1, 0x59, 0xba, 0xd2, 0xb9, 0x4f, 0xed, 0x06, 0x09, 0x29, 0x6e, 0xf8, 0x92, 0xa0, 0x4b, 0xc0, 0x49, 0x80, 0x7d, 0x9f, 0x04, 0xa1, 0xdc, 0x5f, 0x49, 0x99, 0x88, 0x7d, 0x9b, 0x59, 0x57, 0xf3, 0x1a, 0x8d, 0xb6, 0x0a, 0x15, 0xc5, 0x8b, 0x88, 0x04, 0x4d, 0x49, 0x50, 0x56, 0x11, 0x50, 0x1c, 0x1e, 0x39, 0x76, 0x48, 0x25, 0xcd, 0xaa, 0x8a, 0x46, 0x3a, 0xc1, 0x3c, 0xf1, 0x82, 0x23, 0x12, 0x48, 0xca, 0xaf, 0xf5, 0xa3, 0x3c, 0x70, 0xbc, 0x13, 0x49, 0x7b, 0x55, 0x45, 0x5b, 0xb7, 0x43, 0xea, 0xb5, 0x8c, 0x7b, 0x33, 0x45, 0x12, 0xd6, 0x71, 0x40, 0xac, 0x6e, 0xaa, 0xeb, 0x19, 0x54, 0xe9, 0x53, 0x94, 0x3f, 0x82, 0xb9, 0x3d, 0x1c, 0x1e, 0x7d, 0x6c, 0x87, 0xf4, 0x31, 0x0e, 0xa8, 0xcd, 0x02, 0x81, 0xde, 0x86, 0x59, 0x3b, 0xf4, 0x1c, 0x1e, 0x15, 0xf3, 0x30, 0xf0, 0x22, 0x3f, 0xd4, 0xb5, 0x95, 0x91, 0xd5, 0x49, 0x63, 0xa6, 0xb5, 0x7e, 0x9f, 0x2f, 0x97, 0xff, 0x3e, 0x0a, 0x17, 0xba, 0x04, 0x6c, 0x7b, 0xee, 0x81, 0x7d, 0x88, 0x74, 0x18, 0x3f, 0x26, 0x41, 0x68, 0x7b, 0xae, 0xae, 0xad, 0x68, 0xab, 0x23, 0x46, 0xfc, 0x89, 0x36, 0x60, 0xde, 0x8d, 0x1a, 0x66, 0x40, 0xb0, 0x65, 0xfa, 0x31, 0x57, 0xa8, 0xe7, 0x56, 0xb4, 0xd5, 0xfc, 0x56, 0x4e, 0xd7, 0x8c, 0x39, 0x37, 0x6a, 0x18, 0x04, 0x5b, 0x2d, 0x91, 0x21, 0x7a, 0x1f, 0x16, 0x18, 0xcf, 0x49, 0x60, 0x53, 0x92, 0x64, 0x1a, 0x69, 0x31, 0x21, 0x37, 0x6a, 0x3c, 0x63, 0xdb, 0x09, 0x2e, 0x17, 0x66, 0x3a, 0xb5, 0x8c, 0xae, 0x8c, 0xac, 0x16, 0x36, 0xee, 0x55, 0xb2, 0x32, 0xb4, 0x92, 0x71, 0x9e, 0x4a, 0xda, 0xa0, 0x7b, 0x2e, 0x0d, 0x9a, 0x46, 0x31, 0x48, 0x5b, 0xf9, 0x02, 0x66, 0xbb, 0x2c, 0xcc, 0x73, 0x85, 0xbb, 0xc3, 0x2b, 0xec, 0x38, 0x8c, 0xd0, 0x38, 0x73, 0x92, 0x5e, 0x2d, 0xb9, 0x30, 0xaf, 0xb0, 0x0c, 0xcd, 0xc2, 0xc8, 0x11, 0x69, 0x72, 0xcf, 0xe7, 0x0d, 0xf6, 0x27, 0xda, 0x84, 0xfc, 0x31, 0x76, 0x22, 0xc2, 0xfd, 0x5c, 0xd8, 0xf8, 0xfa, 0x10, 0x06, 0x19, 0x82, 0xf3, 0x4e, 0xee, 0xb6, 0x56, 0xf2, 0x60, 0x41, 0x65, 0xd8, 0x2b, 0x53, 0x58, 0xfe, 0x01, 0xcc, 0x7d, 0xec, 0x61, 0x6b, 0x0b, 0x3b, 0xd8, 0xad, 0x91, 0xe0, 0x81, 0xed, 0xd2, 0x10, 0x5d, 0x83, 0xe9, 0x7d, 0x5c, 0x3b, 0x72, 0xbc, 0x43, 0xb3, 0xe6, 0x45, 0x2e, 0x95, 0x29, 0x36, 0x25, 0x17, 0xb7, 0xd9, 0x1a, 0xba, 0x01, 0x33, 0x01, 0x66, 0xc1, 0x20, 0x81, 0x19, 0x92, 0x9a, 0xe7, 0x5a, 0xdc, 0x14, 0xcd, 0x98, 0x66, 0xcb, 0x8f, 0x49, 0xf0, 0x84, 0x2f, 0x96, 0xff, 0xa9, 0x41, 0xe9, 0xb1, 0xe7, 0x38, 0xbb, 0x5e, 0xb0, 0x43, 0x6a, 0x36, 0xcb, 0x51, 0x66, 0x91, 0x41, 0x5e, 0x44, 0x24, 0xa4, 0xa8, 0x0a, 0xe3, 0x81, 0xf8, 0x93, 0x6b, 0x29, 0x6c, 0xac, 0xa5, 0x4f, 0x82, 0x7d, 0x9b, 0x1d, 0x22, 0x5b, 0x82, 0x11, 0xf3, 0xa3, 0x4b, 0x30, 0x69, 0x79, 0x0d, 0x6c, 0xbb, 0xa6, 0x2d, 0x6c, 0x99, 0x34, 0x26, 0xc4, 0x42, 0xd5, 0x62, 0x9b, 0xbe, 0xe7, 0x38, 0x24, 0x60, 0x9b, 0x23, 0x62, 0x53, 0x2c, 0x54, 0x2d, 0x74, 0x1d, 0x8a, 0x07, 0x5e, 0x70, 0x82, 0x03, 0x8b, 0x58, 0xe6, 0x41, 0xe0, 0x35, 0xf4, 0x51, 0x4e, 0x31, 0xdd, 0x5a, 0xdd, 0x0d, 0xbc, 0x06, 0x7a, 0x0b, 0x66, 0x3a, 0x6a, 0x57, 0xcf, 0x73, 0xba, 0x62, 0xba, 0x74, 0xcb, 0x7f, 0x2c, 0xc0, 0x25, 0xa5, 0xc5, 0xa1, 0xef, 0xb9, 0x21, 0x41, 0x4b, 0x00, 0xac, 0x57, 0x98, 0xd4, 0x3b, 0x22, 0xa2, 0x80, 0xa7, 0x8c, 0x49, 0xb6, 0xb2, 0xc7, 0x16, 0xd0, 0xa7, 0x80, 0xe2, 0xd6, 0x65, 0x92, 0xcf, 0x49, 0x2d, 0x62, 0x92, 0x65, 0xa0, 0x6f, 0x28, 0xdd, 0xf3, 0x4c, 0x92, 0xdf, 0x8b, 0xa9, 0x8d, 0xb9, 0x93, 0xce, 0x25, 0xb4, 0x0b, 0xd3, 0x2d, 0xb1, 0xb4, 0xe9, 0x13, 0xee, 0x86, 0xc2, 0xc6, 0xd5, 0x9e, 0x12, 0xf7, 0x9a, 0x3e, 0x31, 0xa6, 0x4e, 0x12, 0x5f, 0xe8, 0x29, 0x5c, 0xf4, 0x03, 0x72, 0x6c, 0x7b, 0x51, 0x68, 0x86, 0x14, 0x07, 0x94, 0x58, 0x26, 0x39, 0x26, 0x2e, 0x65, 0xae, 0x1d, 0xe5, 0x32, 0x2f, 0x55, 0x04, 0x90, 0x54, 0x62, 0x20, 0xa9, 0x54, 0x5d, 0x7a, 0xeb, 0xfd, 0xa7, 0x2c, 0xef, 0x8c, 0xc5, 0x98, 0xfb, 0x89, 0x60, 0xbe, 0xc7, 0x78, 0xab, 0x16, 0x5a, 0x85, 0xd9, 0x2e, 0x71, 0x79, 0x9e, 0x79, 0xc5, 0x30, 0x4d, 0xa9, 0xc3, 0x38, 0xa6, 0x94, 0x34, 0x7c, 0xaa, 0x8f, 0xf1, 0x92, 0x88, 0x3f, 0x51, 0x19, 0xa6, 0x5d, 0xf2, 0x39, 0x6d, 0x0b, 0x18, 0xe7, 0x02, 0x0a, 0x6c, 0x31, 0xe6, 0x7e, 0x07, 0x50, 0x2a, 0xbd, 0xcd, 0xba, 0xed, 0x52, 0x7d, 0x82, 0x13, 0xce, 0x26, 0x73, 0x9c, 0x55, 0x03, 0xba, 0x0d, 0x7a, 0x48, 0xed, 0xda, 0x51, 0xb3, 0x1d, 0x0a, 0x93, 0xb8, 0x78, 0xdf, 0x21, 0x96, 0x3e, 0xb9, 0xa2, 0xad, 0x4e, 0x18, 0x8b, 0x62, 0xbf, 0xe5, 0xe8, 0x7b, 0x62, 0x17, 0xdd, 0x86, 0x3c, 0x07, 0x3e, 0x1d, 0xb8, 0x4f, 0xca, 0x3d, 0xfd, 0xfc, 0x09, 0xa3, 0x34, 0x04, 0x03, 0x32, 0x60, 0xda, 0x92, 0x79, 0x63, 0xda, 0xee, 0x81, 0xa7, 0x17, 0xb8, 0x84, 0x6f, 0xa4, 0x25, 0x08, 0xe0, 0xe1, 0x25, 0x1e, 0x60, 0x37, 0xb4, 0x89, 0x4b, 0xe3, 0x6c, 0xab, 0xba, 0x07, 0x9e, 0x31, 0x65, 0x25, 0xbe, 0xd0, 0x73, 0xb8, 0xdc, 0x9d, 0x54, 0x26, 0x4f, 0x43, 0x86, 0x59, 0xfa, 0x14, 0x57, 0xb1, 0xa4, 0x34, 0x32, 0x6e, 0x21, 0xc6, 0xc5, 0xae, 0xac, 0x8a, 0xb7, 0x50, 0x05, 0xe6, 0x85, 0xd3, 0x19, 0x52, 0x12, 0x33, 0x46, 0xa7, 0x69, 0x1e, 0x9f, 0x39, 0xbe, 0xf5, 0x84, 0xed, 0x3c, 0x95, 0x38, 0x75, 0x15, 0xa6, 0xf6, 0x03, 0xec, 0xd6, 0xea, 0xb2, 0x0a, 0x8a, 0xbc, 0x0a, 0x0a, 0x62, 0x4d, 0xd4, 0xc1, 0x26, 0x14, 0xc3, 0x5a, 0x9d, 0x58, 0x91, 0x43, 0x2c, 0x93, 0x8d, 0x2a, 0xfa, 0x0c, 0x37, 0xb2, 0xd4, 0x95, 0x5d, 0x7b, 0xf1, 0x1c, 0x63, 0x4c, 0xb7, 0x38, 0xd8, 0x1a, 0xfa, 0x16, 0x4c, 0xc5, 0x39, 0xc5, 0x05, 0xcc, 0xf6, 0x15, 0x50, 0x90, 0xf4, 0x9c, 0xfd, 0x33, 0x18, 0x67, 0x11, 0xb1, 0x49, 0xa8, 0xcf, 0x71, 0xa4, 0xd9, 0xca, 0xee, 0xb3, 0x3d, 0x0a, 0xbe, 0xf2, 0x89, 0x10, 0x22, 0x50, 0x26, 0x16, 0xc9, 0x5c, 0x46, 0x3d, 0x8a, 0x1d, 0x53, 0x8e, 0x17, 0xe6, 0x7e, 0x93, 0x92, 0x50, 0x47, 0x3c, 0x13, 0xe7, 0xf8, 0xd6, 0x03, 0xb1, 0xb3, 0xc5, 0x36, 0xd0, 0x67, 0x30, 0xdb, 0x82, 0x3e, 0xb3, 0xc6, 0x71, 0x4c, 0x9f, 0xe7, 0x07, 0x5a, 0x1f, 0x1a, 0x00, 0x8d, 0x19, 0xbf, 0x63, 0xa4, 0xf8, 0x3e, 0xcc, 0x3b, 0x1e, 0xb6, 0xcc, 0x7d, 0x89, 0x05, 0xbc, 0x2c, 0x42, 0x7d, 0xa1, 0x1f, 0xbe, 0x74, 0xe1, 0x87, 0x31, 0xe7, 0x74, 0x41, 0xca, 0x43, 0x98, 0xc5, 0x11, 0xf5, 0xa4, 0xd5, 0xa2, 0xe2, 0xde, 0xe0, 0x92, 0xaf, 0x29, 0x33, 0x6e, 0x33, 0xa2, 0x9e, 0xb0, 0x8b, 0xf1, 0x1b, 0x45, 0x9c, 0xfa, 0x2e, 0x3d, 0x87, 0xa9, 0xa4, 0x4b, 0x93, 0xf8, 0x38, 0x29, 0xf0, 0xf1, 0x76, 0x1a, 0x1f, 0x07, 0x2a, 0xbe, 0x36, 0x2c, 0x26, 0x40, 0x6b, 0xb3, 0x46, 0xed, 0x63, 0x9b, 0x36, 0x4f, 0x0f, 0x5a, 0x0a, 0x09, 0xff, 0x8b, 0xa0, 0xf5, 0x2b, 0x68, 0x81, 0x56, 0xda, 0xe2, 0xaf, 0x14, 0xb4, 0xae, 0x40, 0x01, 0x4b, 0x6b, 0xda, 0x4e, 0x80, 0x78, 0xa9, 0x6a, 0x31, 0x54, 0x6b, 0x11, 0x70, 0x54, 0x1b, 0xed, 0x81, 0x6a, 0xad, 0x83, 0x71, 0x54, 0xc3, 0x89, 0x2f, 0xb4, 0x01, 0x79, 0xdb, 0xf5, 0x23, 0xca, 0xbd, 0x53, 0xd8, 0xb8, 0xac, 0x8e, 0x28, 0x6e, 0xb2, 0xdc, 0x36, 0x04, 0xa9, 0xa2, 0x41, 0x8d, 0x9d, 0xb5, 0x41, 0x8d, 0x0f, 0xd7, 0xa0, 0xf6, 0xe0, 0x62, 0x2c, 0xcf, 0x64, 0xe5, 0xe5, 0x78, 0x21, 0xe1, 0x82, 0xbc, 0x48, 0x40, 0x5a, 0x61, 0xe3, 0x62, 0x97, 0xac, 0x1d, 0x79, 0x2b, 0x34, 0x16, 0x63, 0xde, 0x3d, 0x6f, 0x9b, 0x71, 0xee, 0x09, 0x46, 0xf4, 0x5d, 0x58, 0xe4, 0x4a, 0xba, 0x45, 0x4e, 0xf6, 0x13, 0x39, 0xcf, 0x19, 0x3b, 0xe4, 0xed, 0xc2, 0x5c, 0x9d, 0xe0, 0x80, 0xee, 0x13, 0x4c, 0x5b, 0xa2, 0xa0, 0x9f, 0xa8, 0xd9, 0x16, 0x4f, 0x2c, 0x27, 0x81, 0xfb, 0x85, 0x34, 0xee, 0x3f, 0x87, 0xe5, 0x74, 0x24, 0x4c, 0xef, 0xc0, 0xa4, 0x75, 0x3b, 0x34, 0x63, 0x86, 0xa9, 0xbe, 0x8e, 0x2d, 0xa5, 0x22, 0xf3, 0xe8, 0x60, 0xaf, 0x6e, 0x87, 0x9b, 0x52, 0x7e, 0x35, 0x79, 0x02, 0x8b, 0x50, 0x6c, 0x3b, 0x21, 0xc7, 0xb6, 0x7e, 0x99, 0xd2, 0x3e, 0xc4, 0x8e, 0xe0, 0xea, 0x1e, 0xc3, 0x8a, 0xa7, 0x1b, 0xc3, 0xde, 0x82, 0x99, 0x96, 0x1c, 0xd1, 0x31, 0x38, 0x3c, 0x4e, 0x1a, 0xc5, 0x78, 0x79, 0x87, 0xaf, 0xa2, 0xf7, 0x60, 0xac, 0x4e, 0xb0, 0x45, 0x02, 0x89, 0x7e, 0x97, 0x94, 0x9a, 0x1e, 0x70, 0x12, 0x43, 0x92, 0x66, 0xa1, 0xc1, 0xdc, 0xb9, 0xa0, 0xc1, 0xab, 0x05, 0x32, 0x15, 0xd6, 0x2c, 0x9c, 0x1a, 0x6b, 0xca, 0x7f, 0x19, 0x85, 0xc5, 0x4d, 0xcb, 0x52, 0x5d, 0x5e, 0x52, 0xcd, 0x5b, 0xeb, 0x68, 0xde, 0xaf, 0xa8, 0x21, 0xde, 0x81, 0xc9, 0xf6, 0xd0, 0x36, 0x32, 0xc8, 0xd0, 0x36, 0x41, 0xe3, 0x19, 0xed, 0x0a, 0x14, 0x5a, 0xdd, 0x42, 0xce, 0xea, 0x23, 0x06, 0xc4, 0x4b, 0x55, 0xab, 0xb3, 0x9d, 0xc8, 0x26, 0x20, 0x0b, 0x36, 0x3f, 0x44, 0x3b, 0xe1, 0xa3, 0x7d, 0x5c, 0xb6, 0x77, 0x60, 0x2c, 0xf4, 0xa2, 0xa0, 0x26, 0xda, 0x63, 0xb1, 0x13, 0x8c, 0x13, 0x73, 0x2c, 0x0e, 0x8f, 0x9e, 0x70, 0x4a, 0x43, 0x72, 0x28, 0x50, 0x6e, 0x5c, 0x85, 0x72, 0xbe, 0x22, 0xa3, 0x26, 0xfa, 0x3d, 0x46, 0xa8, 0xa3, 0x5a, 0xe9, 0x48, 0x30, 0xf9, 0x34, 0xd0, 0x91, 0x65, 0xa5, 0x2d, 0x58, 0x50, 0x11, 0x2a, 0x46, 0x91, 0x85, 0xe4, 0x28, 0x32, 0x99, 0x1c, 0x33, 0x4e, 0xe0, 0x42, 0x97, 0x0d, 0x12, 0x6d, 0x55, 0x25, 0xa2, 0x9d, 0x57, 0x89, 0x94, 0xff, 0x95, 0xe7, 0x39, 0xad, 0x9a, 0x6d, 0xbe, 0x8a, 0x9c, 0x66, 0x37, 0x3f, 0x1e, 0x6e, 0xb3, 0xad, 0x5a, 0x20, 0x7d, 0x51, 0xac, 0xef, 0xc4, 0x06, 0xa4, 0xb2, 0x7f, 0xf4, 0x4c, 0xd9, 0x9f, 0x1f, 0x2e, 0xfb, 0xc7, 0xce, 0x9e, 0xfd, 0xe3, 0xe7, 0x90, 0xfd, 0x13, 0xaa, 0xec, 0x77, 0x41, 0xc7, 0x89, 0x50, 0xee, 0xd8, 0xa1, 0xcf, 0xb2, 0x82, 0xdd, 0xfb, 0x24, 0x62, 0x6f, 0xf4, 0xa8, 0x82, 0x0c, 0x4e, 0x23, 0x53, 0xa6, 0xb2, 0xda, 0x60, 0x80, 0x6a, 0x53, 0xe4, 0xdb, 0x6b, 0xac, 0xb6, 0x2f, 0x47, 0x40, 0xcf, 0x3a, 0x2c, 0xfa, 0x0e, 0xcc, 0xb4, 0x07, 0x08, 0x7e, 0x5b, 0x95, 0xe5, 0xa6, 0xc6, 0x65, 0x79, 0x2f, 0xe3, 0x4f, 0x0a, 0x46, 0x7b, 0x08, 0xe4, 0xdf, 0x5d, 0x33, 0x5d, 0x6e, 0xb8, 0x99, 0x2e, 0x31, 0xe5, 0x8c, 0x0c, 0x3b, 0xe5, 0x8c, 0x9e, 0xff, 0x94, 0x93, 0x3f, 0x9f, 0x29, 0x67, 0xec, 0xdc, 0xa6, 0x9c, 0x71, 0xd5, 0x94, 0x23, 0x7b, 0xa9, 0xf2, 0xe6, 0xf2, 0x6a, 0x7b, 0xe9, 0x97, 0x1a, 0x2c, 0xf0, 0x0b, 0x64, 0x7c, 0x8a, 0xb8, 0x93, 0x6e, 0x77, 0xde, 0x12, 0xdf, 0x56, 0x1e, 0x5e, 0xc5, 0x3b, 0xe0, 0xfd, 0xf0, 0x2c, 0xb3, 0xc0, 0x60, 0xd7, 0xc7, 0xf2, 0x7f, 0x34, 0x78, 0xa3, 0xc3, 0x42, 0xe9, 0xd5, 0xbb, 0x30, 0xc5, 0x5f, 0xab, 0xcc, 0x80, 0x84, 0x91, 0x13, 0x9f, 0xb1, 0x77, 0x9e, 0x14, 0x38, 0x87, 0xc1, 0x19, 0x50, 0x15, 0x8a, 0xb1, 0x80, 0x1f, 0x92, 0x1a, 0x25, 0x56, 0xcf, 0xbb, 0xba, 0xb8, 0xa3, 0x4b, 0x4a, 0x63, 0xfa, 0x45, 0xf2, 0x13, 0x3d, 0x53, 0x44, 0x58, 0xf8, 0xe3, 0x9d, 0x9e, 0xfe, 0xe8, 0x1b, 0xdc, 0x7f, 0x68, 0xb0, 0x22, 0x4e, 0x6c, 0x71, 0x03, 0x18, 0xe3, 0xb6, 0xd7, 0xf0, 0x1d, 0xc2, 0xac, 0x90, 0x31, 0x7a, 0xd4, 0x19, 0xe8, 0x9b, 0x4a, 0xa5, 0xfd, 0xe4, 0xbc, 0x86, 0xa0, 0x5f, 0x80, 0x71, 0xce, 0x2b, 0x87, 0xbf, 0x49, 0x63, 0x8c, 0x7d, 0x56, 0xad, 0xf2, 0x35, 0xb8, 0xda, 0xc3, 0x3c, 0x11, 0xf1, 0xf2, 0x5f, 0x35, 0xb8, 0xbc, 0xcd, 0xc6, 0x78, 0xe7, 0x51, 0x44, 0x43, 0x8a, 0x5d, 0xcb, 0x76, 0x0f, 0x1f, 0x7b, 0x8e, 0x33, 0xd0, 0xec, 0x90, 0x7a, 0xcc, 0xc8, 0x75, 0x3c, 0x66, 0xdc, 0x87, 0x62, 0xeb, 0x50, 0xed, 0xc7, 0xe9, 0x62, 0x46, 0xbf, 0x88, 0x4f, 0x26, 0xfa, 0x05, 0x4d, 0x7c, 0x9d, 0x65, 0x40, 0x28, 0x5f, 0x81, 0xa5, 0x8c, 0xe3, 0x49, 0x07, 0xfc, 0x08, 0x2e, 0xec, 0x90, 0xb0, 0x16, 0xd8, 0xfb, 0xa4, 0xc5, 0x2e, 0x8f, 0xbe, 0xdb, 0x99, 0x03, 0xea, 0xc4, 0xcb, 0x60, 0x1f, 0x2c, 0xf4, 0xe5, 0x3f, 0xe4, 0x40, 0xef, 0x96, 0x20, 0xeb, 0xf1, 0x03, 0x18, 0x17, 0xee, 0x14, 0x3f, 0x28, 0x16, 0x36, 0xae, 0x64, 0x3e, 0x4a, 0x91, 0x80, 0x03, 0x7c, 0x4c, 0xcf, 0x6e, 0x4c, 0x6d, 0xef, 0x87, 0x14, 0xd3, 0x28, 0x94, 0xb5, 0x78, 0xad, 0xa7, 0xef, 0x9e, 0x70, 0x52, 0xa3, 0x48, 0x53, 0xdf, 0xaf, 0xac, 0x1a, 0xcf, 0x14, 0xdc, 0x10, 0x96, 0x78, 0x92, 0x74, 0xea, 0x0a, 0xe3, 0x08, 0x2e, 0xc2, 0x98, 0x04, 0x18, 0x91, 0xb9, 0xf2, 0x2b, 0xad, 0x34, 0x37, 0x9c, 0xd2, 0x9f, 0xe5, 0x60, 0x39, 0x4b, 0xab, 0x0c, 0xdb, 0x0b, 0x58, 0x6a, 0xbf, 0x5f, 0xb5, 0x82, 0x90, 0xf8, 0x89, 0x53, 0x04, 0xb3, 0x32, 0x98, 0xe7, 0x1e, 0x12, 0x8a, 0x2d, 0x4c, 0xb1, 0x51, 0x4a, 0x0e, 0x6f, 0x69, 0xd5, 0x4c, 0x65, 0xeb, 0xe7, 0x05, 0xa5, 0xca, 0xdc, 0xe9, 0x54, 0x5a, 0x89, 0x8b, 0x4c, 0x5a, 0x65, 0xf9, 0x26, 0x5c, 0xba, 0x4f, 0x5a, 0x6e, 0x08, 0xb7, 0x9a, 0x02, 0xb5, 0xfb, 0xf8, 0xbe, 0xfc, 0xfb, 0x51, 0xb8, 0xac, 0xe6, 0x93, 0xde, 0xfb, 0x89, 0x06, 0x8b, 0x8a, 0xb3, 0x34, 0xb0, 0x2f, 0xfd, 0xf6, 0x28, 0x1b, 0xe1, 0x7b, 0x09, 0xae, 0xec, 0x74, 0x9c, 0xe5, 0x21, 0xf6, 0xc5, 0x68, 0x3a, 0x6f, 0x75, 0xef, 0x70, 0x33, 0x14, 0x51, 0x64, 0x66, 0xe4, 0xce, 0x64, 0xc6, 0x66, 0x47, 0x14, 0xdb, 0x66, 0xe0, 0xee, 0x9d, 0xd2, 0x17, 0xac, 0x3d, 0xa8, 0xed, 0x56, 0x4c, 0xca, 0x0f, 0xd2, 0x4f, 0xe4, 0x3d, 0xae, 0x08, 0x59, 0x3d, 0x27, 0xf9, 0xd3, 0xf5, 0x17, 0xe9, 0xe1, 0xfa, 0x75, 0xea, 0x2e, 0xff, 0x26, 0x07, 0x6f, 0x7e, 0xea, 0x5b, 0x98, 0x92, 0xac, 0x56, 0x32, 0x08, 0x40, 0x9d, 0xa1, 0xd0, 0xcf, 0x0f, 0xbf, 0x54, 0xbd, 0x73, 0xf4, 0x3c, 0x26, 0x99, 0xb7, 0xe0, 0x7a, 0x1f, 0x17, 0x49, 0x90, 0xfb, 0x6d, 0x0e, 0xae, 0x1b, 0xe4, 0x20, 0x20, 0x61, 0xfd, 0xff, 0xde, 0xcc, 0xf2, 0xe6, 0x2a, 0xdc, 0xe8, 0xe7, 0x23, 0xe1, 0xce, 0x8d, 0x7f, 0x4f, 0x41, 0xe1, 0xa1, 0xcc, 0xe7, 0xcd, 0xc7, 0x55, 0xf4, 0x63, 0x0d, 0xe6, 0x15, 0x3f, 0x15, 0xa2, 0xf7, 0x87, 0xfc, 0x65, 0x91, 0x87, 0xa0, 0x74, 0xf3, 0x54, 0xbf, 0x47, 0x26, 0x8d, 0x48, 0x16, 0xed, 0x00, 0x46, 0x28, 0xae, 0xf0, 0x03, 0x18, 0xa1, 0xbc, 0x96, 0x1d, 0xc3, 0x4c, 0xc7, 0xeb, 0x17, 0x7a, 0x77, 0xd8, 0xc7, 0xba, 0xd2, 0xfa, 0x10, 0x1c, 0x29, 0xbd, 0xa9, 0x73, 0xbf, 0x3b, 0xec, 0xb3, 0x45, 0x1f, 0xbd, 0xca, 0xf3, 0xfa, 0x30, 0x9d, 0xba, 0x49, 0xa1, 0x4a, 0xb6, 0x0c, 0xd5, 0xa5, 0xb0, 0xb4, 0x36, 0x30, 0xbd, 0xd4, 0xf8, 0x4b, 0x0d, 0x2e, 0x66, 0x8e, 0xf5, 0xe8, 0x4e, 0xb6, 0xb8, 0x7e, 0x57, 0x95, 0xd2, 0x87, 0xa7, 0xe2, 0x95, 0x66, 0xfd, 0x5c, 0x83, 0x37, 0x94, 0x83, 0x36, 0xba, 0x95, 0x2d, 0xb6, 0xd7, 0xc5, 0xa3, 0xf4, 0xcd, 0xa1, 0xf9, 0xa4, 0x29, 0x4d, 0x98, 0xed, 0x04, 0x18, 0xb4, 0x3e, 0x0c, 0x18, 0x09, 0xfd, 0xa7, 0xc0, 0x2f, 0xf4, 0x0b, 0x0d, 0x16, 0xd5, 0xb3, 0x21, 0xea, 0x71, 0x9c, 0x9e, 0x33, 0x6c, 0xe9, 0xf6, 0xf0, 0x8c, 0xd2, 0x9a, 0x9f, 0x6a, 0xb0, 0xa0, 0x9a, 0x44, 0xd0, 0xcd, 0x61, 0x27, 0x17, 0x61, 0xc9, 0xad, 0xd3, 0x0d, 0x3c, 0xe8, 0xd7, 0x1a, 0x2c, 0xf5, 0xc4, 0x29, 0xf4, 0x51, 0xb6, 0xe4, 0x41, 0x66, 0x80, 0xd2, 0xdd, 0x53, 0xf3, 0x4b, 0x13, 0x7f, 0xa7, 0xc1, 0x72, 0xef, 0xe6, 0x8f, 0xee, 0xf6, 0x2a, 0x8f, 0x01, 0xa0, 0xb5, 0xf4, 0xed, 0xd3, 0x0b, 0x10, 0x56, 0x6e, 0xdd, 0xff, 0xd3, 0xcb, 0x65, 0xed, 0xcf, 0x2f, 0x97, 0xb5, 0xbf, 0xbd, 0x5c, 0xd6, 0xbe, 0xf7, 0xc1, 0xa1, 0x4d, 0xeb, 0xd1, 0x7e, 0xa5, 0xe6, 0x35, 0xd6, 0x52, 0xff, 0xbd, 0x5a, 0x39, 0x24, 0xae, 0xf8, 0x77, 0xdf, 0xe4, 0x7f, 0x1c, 0x7f, 0x18, 0xff, 0x7d, 0xbc, 0xbe, 0x3f, 0xc6, 0x77, 0xdf, 0xfb, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x48, 0x50, 0xe3, 0xd9, 0x9f, 0x2c, 0x00, 0x00, } func (m *TaskListPartition) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TaskListPartition) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *TaskListPartition) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.IsolationGroups) > 0 { for iNdEx := len(m.IsolationGroups) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.IsolationGroups[iNdEx]) copy(dAtA[i:], m.IsolationGroups[iNdEx]) i = encodeVarintService(dAtA, i, uint64(len(m.IsolationGroups[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *TaskListPartitionConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TaskListPartitionConfig) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *TaskListPartitionConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.WritePartitions) > 0 { for k := range m.WritePartitions { v := m.WritePartitions[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i = encodeVarintService(dAtA, i, uint64(k)) i-- dAtA[i] = 0x8 i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x2a } } if len(m.ReadPartitions) > 0 { for k := range m.ReadPartitions { v := m.ReadPartitions[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i = encodeVarintService(dAtA, i, uint64(k)) i-- dAtA[i] = 0x8 i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x22 } } if m.NumWritePartitions != 0 { i = encodeVarintService(dAtA, i, uint64(m.NumWritePartitions)) i-- dAtA[i] = 0x18 } if m.NumReadPartitions != 0 { i = encodeVarintService(dAtA, i, uint64(m.NumReadPartitions)) i-- dAtA[i] = 0x10 } if m.Version != 0 { i = encodeVarintService(dAtA, i, uint64(m.Version)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *LoadBalancerHints) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LoadBalancerHints) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *LoadBalancerHints) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.RatePerSecond != 0 { i -= 8 encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.RatePerSecond)))) i-- dAtA[i] = 0x11 } if m.BacklogCount != 0 { i = encodeVarintService(dAtA, i, uint64(m.BacklogCount)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *PollForDecisionTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PollForDecisionTaskRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *PollForDecisionTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.IsolationGroup) > 0 { i -= len(m.IsolationGroup) copy(dAtA[i:], m.IsolationGroup) i = encodeVarintService(dAtA, i, uint64(len(m.IsolationGroup))) i-- dAtA[i] = 0x2a } if len(m.ForwardedFrom) > 0 { i -= len(m.ForwardedFrom) copy(dAtA[i:], m.ForwardedFrom) i = encodeVarintService(dAtA, i, uint64(len(m.ForwardedFrom))) i-- dAtA[i] = 0x22 } if len(m.PollerId) > 0 { i -= len(m.PollerId) copy(dAtA[i:], m.PollerId) i = encodeVarintService(dAtA, i, uint64(len(m.PollerId))) i-- dAtA[i] = 0x1a } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PollForDecisionTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PollForDecisionTaskResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *PollForDecisionTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.AutoConfigHint != nil { { size, err := m.AutoConfigHint.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xaa } if m.LoadBalancerHints != nil { { size, err := m.LoadBalancerHints.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa2 } if m.PartitionConfig != nil { { size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x9a } if m.TotalHistoryBytes != 0 { i = encodeVarintService(dAtA, i, uint64(m.TotalHistoryBytes)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x90 } if len(m.Queries) > 0 { for k := range m.Queries { v := m.Queries[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x8a } } if m.StartedTime != nil { { size, err := m.StartedTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x82 } if m.ScheduledTime != nil { { size, err := m.ScheduledTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x7a } if len(m.BranchToken) > 0 { i -= len(m.BranchToken) copy(dAtA[i:], m.BranchToken) i = encodeVarintService(dAtA, i, uint64(len(m.BranchToken))) i-- dAtA[i] = 0x72 } if m.EventStoreVersion != 0 { i = encodeVarintService(dAtA, i, uint64(m.EventStoreVersion)) i-- dAtA[i] = 0x68 } if m.WorkflowExecutionTaskList != nil { { size, err := m.WorkflowExecutionTaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x62 } if m.DecisionInfo != nil { { size, err := m.DecisionInfo.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x5a } if m.Query != nil { { size, err := m.Query.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x52 } if m.StickyExecutionEnabled { i-- if m.StickyExecutionEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x48 } if m.BacklogCountHint != 0 { i = encodeVarintService(dAtA, i, uint64(m.BacklogCountHint)) i-- dAtA[i] = 0x40 } if m.NextEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.NextEventId)) i-- dAtA[i] = 0x38 } if m.Attempt != 0 { i = encodeVarintService(dAtA, i, uint64(m.Attempt)) i-- dAtA[i] = 0x30 } if m.StartedEventId != 0 { i = encodeVarintService(dAtA, i, uint64(m.StartedEventId)) i-- dAtA[i] = 0x28 } if m.PreviousStartedEventId != nil { { size, err := m.PreviousStartedEventId.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.WorkflowType != nil { { size, err := m.WorkflowType.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.TaskToken) > 0 { i -= len(m.TaskToken) copy(dAtA[i:], m.TaskToken) i = encodeVarintService(dAtA, i, uint64(len(m.TaskToken))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PollForActivityTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PollForActivityTaskRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *PollForActivityTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.IsolationGroup) > 0 { i -= len(m.IsolationGroup) copy(dAtA[i:], m.IsolationGroup) i = encodeVarintService(dAtA, i, uint64(len(m.IsolationGroup))) i-- dAtA[i] = 0x2a } if len(m.ForwardedFrom) > 0 { i -= len(m.ForwardedFrom) copy(dAtA[i:], m.ForwardedFrom) i = encodeVarintService(dAtA, i, uint64(len(m.ForwardedFrom))) i-- dAtA[i] = 0x22 } if len(m.PollerId) > 0 { i -= len(m.PollerId) copy(dAtA[i:], m.PollerId) i = encodeVarintService(dAtA, i, uint64(len(m.PollerId))) i-- dAtA[i] = 0x1a } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PollForActivityTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PollForActivityTaskResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *PollForActivityTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.AutoConfigHint != nil { { size, err := m.AutoConfigHint.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa2 } if m.PartitionConfig != nil { { size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x9a } if m.LoadBalancerHints != nil { { size, err := m.LoadBalancerHints.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x8a } if m.Header != nil { { size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x82 } if len(m.WorkflowDomain) > 0 { i -= len(m.WorkflowDomain) copy(dAtA[i:], m.WorkflowDomain) i = encodeVarintService(dAtA, i, uint64(len(m.WorkflowDomain))) i-- dAtA[i] = 0x7a } if m.WorkflowType != nil { { size, err := m.WorkflowType.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x72 } if m.HeartbeatDetails != nil { { size, err := m.HeartbeatDetails.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x6a } if m.ScheduledTimeOfThisAttempt != nil { { size, err := m.ScheduledTimeOfThisAttempt.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x62 } if m.Attempt != 0 { i = encodeVarintService(dAtA, i, uint64(m.Attempt)) i-- dAtA[i] = 0x58 } if m.HeartbeatTimeout != nil { { size, err := m.HeartbeatTimeout.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x52 } if m.StartToCloseTimeout != nil { { size, err := m.StartToCloseTimeout.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x4a } if m.ScheduleToCloseTimeout != nil { { size, err := m.ScheduleToCloseTimeout.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x42 } if m.StartedTime != nil { { size, err := m.StartedTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x3a } if m.ScheduledTime != nil { { size, err := m.ScheduledTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x32 } if m.Input != nil { { size, err := m.Input.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x2a } if m.ActivityType != nil { { size, err := m.ActivityType.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if len(m.ActivityId) > 0 { i -= len(m.ActivityId) copy(dAtA[i:], m.ActivityId) i = encodeVarintService(dAtA, i, uint64(len(m.ActivityId))) i-- dAtA[i] = 0x1a } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.TaskToken) > 0 { i -= len(m.TaskToken) copy(dAtA[i:], m.TaskToken) i = encodeVarintService(dAtA, i, uint64(len(m.TaskToken))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AddDecisionTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AddDecisionTaskRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *AddDecisionTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.PartitionConfig) > 0 { for k := range m.PartitionConfig { v := m.PartitionConfig[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = encodeVarintService(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x42 } } if len(m.ForwardedFrom) > 0 { i -= len(m.ForwardedFrom) copy(dAtA[i:], m.ForwardedFrom) i = encodeVarintService(dAtA, i, uint64(len(m.ForwardedFrom))) i-- dAtA[i] = 0x3a } if m.Source != 0 { i = encodeVarintService(dAtA, i, uint64(m.Source)) i-- dAtA[i] = 0x30 } if m.ScheduleToStartTimeout != nil { { size, err := m.ScheduleToStartTimeout.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x2a } if m.ScheduleId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ScheduleId)) i-- dAtA[i] = 0x20 } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AddDecisionTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AddDecisionTaskResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *AddDecisionTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.PartitionConfig != nil { { size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AddActivityTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AddActivityTaskRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *AddActivityTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.PartitionConfig) > 0 { for k := range m.PartitionConfig { v := m.PartitionConfig[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = encodeVarintService(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x52 } } if m.ActivityTaskDispatchInfo != nil { { size, err := m.ActivityTaskDispatchInfo.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x4a } if len(m.ForwardedFrom) > 0 { i -= len(m.ForwardedFrom) copy(dAtA[i:], m.ForwardedFrom) i = encodeVarintService(dAtA, i, uint64(len(m.ForwardedFrom))) i-- dAtA[i] = 0x42 } if m.Source != 0 { i = encodeVarintService(dAtA, i, uint64(m.Source)) i-- dAtA[i] = 0x38 } if m.ScheduleToStartTimeout != nil { { size, err := m.ScheduleToStartTimeout.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x32 } if m.ScheduleId != 0 { i = encodeVarintService(dAtA, i, uint64(m.ScheduleId)) i-- dAtA[i] = 0x28 } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if len(m.SourceDomainId) > 0 { i -= len(m.SourceDomainId) copy(dAtA[i:], m.SourceDomainId) i = encodeVarintService(dAtA, i, uint64(len(m.SourceDomainId))) i-- dAtA[i] = 0x1a } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ActivityTaskDispatchInfo) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ActivityTaskDispatchInfo) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ActivityTaskDispatchInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.WorkflowDomain) > 0 { i -= len(m.WorkflowDomain) copy(dAtA[i:], m.WorkflowDomain) i = encodeVarintService(dAtA, i, uint64(len(m.WorkflowDomain))) i-- dAtA[i] = 0x3a } if m.WorkflowType != nil { { size, err := m.WorkflowType.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x32 } if m.HeartbeatDetails != nil { { size, err := m.HeartbeatDetails.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x2a } if m.ScheduledTimeOfThisAttempt != nil { { size, err := m.ScheduledTimeOfThisAttempt.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.Attempt != 0 { i = encodeVarintService(dAtA, i, uint64(m.Attempt)) i-- dAtA[i] = 0x18 } if m.StartedTime != nil { { size, err := m.StartedTime.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if m.ScheduledEvent != nil { { size, err := m.ScheduledEvent.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AddActivityTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AddActivityTaskResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *AddActivityTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.PartitionConfig != nil { { size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *QueryWorkflowRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *QueryWorkflowRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *QueryWorkflowRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ForwardedFrom) > 0 { i -= len(m.ForwardedFrom) copy(dAtA[i:], m.ForwardedFrom) i = encodeVarintService(dAtA, i, uint64(len(m.ForwardedFrom))) i-- dAtA[i] = 0x22 } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *QueryWorkflowResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *QueryWorkflowResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *QueryWorkflowResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.PartitionConfig != nil { { size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if m.QueryRejected != nil { { size, err := m.QueryRejected.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if m.QueryResult != nil { { size, err := m.QueryResult.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RespondQueryTaskCompletedRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondQueryTaskCompletedRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondQueryTaskCompletedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.TaskId) > 0 { i -= len(m.TaskId) copy(dAtA[i:], m.TaskId) i = encodeVarintService(dAtA, i, uint64(len(m.TaskId))) i-- dAtA[i] = 0x22 } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RespondQueryTaskCompletedResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RespondQueryTaskCompletedResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RespondQueryTaskCompletedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *CancelOutstandingPollRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CancelOutstandingPollRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *CancelOutstandingPollRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.TaskListType != 0 { i = encodeVarintService(dAtA, i, uint64(m.TaskListType)) i-- dAtA[i] = 0x18 } if len(m.PollerId) > 0 { i -= len(m.PollerId) copy(dAtA[i:], m.PollerId) i = encodeVarintService(dAtA, i, uint64(len(m.PollerId))) i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *CancelOutstandingPollResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CancelOutstandingPollResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *CancelOutstandingPollResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *DescribeTaskListRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DescribeTaskListRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *DescribeTaskListRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0x12 } if m.Request != nil { { size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DescribeTaskListResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DescribeTaskListResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *DescribeTaskListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.PartitionConfig != nil { { size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if m.TaskListStatus != nil { { size, err := m.TaskListStatus.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.Pollers) > 0 { for iNdEx := len(m.Pollers) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.Pollers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ListTaskListPartitionsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ListTaskListPartitionsRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ListTaskListPartitionsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.Domain) > 0 { i -= len(m.Domain) copy(dAtA[i:], m.Domain) i = encodeVarintService(dAtA, i, uint64(len(m.Domain))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ListTaskListPartitionsResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ListTaskListPartitionsResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ListTaskListPartitionsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.DecisionTaskListPartitions) > 0 { for iNdEx := len(m.DecisionTaskListPartitions) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.DecisionTaskListPartitions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } } if len(m.ActivityTaskListPartitions) > 0 { for iNdEx := len(m.ActivityTaskListPartitions) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.ActivityTaskListPartitions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *GetTaskListsByDomainRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetTaskListsByDomainRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetTaskListsByDomainRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Domain) > 0 { i -= len(m.Domain) copy(dAtA[i:], m.Domain) i = encodeVarintService(dAtA, i, uint64(len(m.Domain))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *GetTaskListsByDomainResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetTaskListsByDomainResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetTaskListsByDomainResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ActivityTaskListMap) > 0 { for k := range m.ActivityTaskListMap { v := m.ActivityTaskListMap[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x12 } } if len(m.DecisionTaskListMap) > 0 { for k := range m.DecisionTaskListMap { v := m.DecisionTaskListMap[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *UpdateTaskListPartitionConfigRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *UpdateTaskListPartitionConfigRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *UpdateTaskListPartitionConfigRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.PartitionConfig != nil { { size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.TaskListType != 0 { i = encodeVarintService(dAtA, i, uint64(m.TaskListType)) i-- dAtA[i] = 0x18 } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *UpdateTaskListPartitionConfigResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *UpdateTaskListPartitionConfigResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *UpdateTaskListPartitionConfigResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RefreshTaskListPartitionConfigRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RefreshTaskListPartitionConfigRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RefreshTaskListPartitionConfigRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.PartitionConfig != nil { { size, err := m.PartitionConfig.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.TaskListType != 0 { i = encodeVarintService(dAtA, i, uint64(m.TaskListType)) i-- dAtA[i] = 0x18 } if m.TaskList != nil { { size, err := m.TaskList.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintService(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RefreshTaskListPartitionConfigResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RefreshTaskListPartitionConfigResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RefreshTaskListPartitionConfigResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func encodeVarintService(dAtA []byte, offset int, v uint64) int { offset -= sovService(v) base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) return base } func (m *TaskListPartition) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.IsolationGroups) > 0 { for _, s := range m.IsolationGroups { l = len(s) n += 1 + l + sovService(uint64(l)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *TaskListPartitionConfig) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Version != 0 { n += 1 + sovService(uint64(m.Version)) } if m.NumReadPartitions != 0 { n += 1 + sovService(uint64(m.NumReadPartitions)) } if m.NumWritePartitions != 0 { n += 1 + sovService(uint64(m.NumWritePartitions)) } if len(m.ReadPartitions) > 0 { for k, v := range m.ReadPartitions { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovService(uint64(l)) } mapEntrySize := 1 + sovService(uint64(k)) + l n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if len(m.WritePartitions) > 0 { for k, v := range m.WritePartitions { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovService(uint64(l)) } mapEntrySize := 1 + sovService(uint64(k)) + l n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *LoadBalancerHints) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.BacklogCount != 0 { n += 1 + sovService(uint64(m.BacklogCount)) } if m.RatePerSecond != 0 { n += 9 } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *PollForDecisionTaskRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.PollerId) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.ForwardedFrom) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.IsolationGroup) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *PollForDecisionTaskResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.TaskToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.WorkflowType != nil { l = m.WorkflowType.Size() n += 1 + l + sovService(uint64(l)) } if m.PreviousStartedEventId != nil { l = m.PreviousStartedEventId.Size() n += 1 + l + sovService(uint64(l)) } if m.StartedEventId != 0 { n += 1 + sovService(uint64(m.StartedEventId)) } if m.Attempt != 0 { n += 1 + sovService(uint64(m.Attempt)) } if m.NextEventId != 0 { n += 1 + sovService(uint64(m.NextEventId)) } if m.BacklogCountHint != 0 { n += 1 + sovService(uint64(m.BacklogCountHint)) } if m.StickyExecutionEnabled { n += 2 } if m.Query != nil { l = m.Query.Size() n += 1 + l + sovService(uint64(l)) } if m.DecisionInfo != nil { l = m.DecisionInfo.Size() n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecutionTaskList != nil { l = m.WorkflowExecutionTaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.EventStoreVersion != 0 { n += 1 + sovService(uint64(m.EventStoreVersion)) } l = len(m.BranchToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.ScheduledTime != nil { l = m.ScheduledTime.Size() n += 1 + l + sovService(uint64(l)) } if m.StartedTime != nil { l = m.StartedTime.Size() n += 2 + l + sovService(uint64(l)) } if len(m.Queries) > 0 { for k, v := range m.Queries { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovService(uint64(l)) } mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + l n += mapEntrySize + 2 + sovService(uint64(mapEntrySize)) } } if m.TotalHistoryBytes != 0 { n += 2 + sovService(uint64(m.TotalHistoryBytes)) } if m.PartitionConfig != nil { l = m.PartitionConfig.Size() n += 2 + l + sovService(uint64(l)) } if m.LoadBalancerHints != nil { l = m.LoadBalancerHints.Size() n += 2 + l + sovService(uint64(l)) } if m.AutoConfigHint != nil { l = m.AutoConfigHint.Size() n += 2 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *PollForActivityTaskRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.PollerId) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.ForwardedFrom) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.IsolationGroup) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *PollForActivityTaskResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.TaskToken) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.ActivityId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.ActivityType != nil { l = m.ActivityType.Size() n += 1 + l + sovService(uint64(l)) } if m.Input != nil { l = m.Input.Size() n += 1 + l + sovService(uint64(l)) } if m.ScheduledTime != nil { l = m.ScheduledTime.Size() n += 1 + l + sovService(uint64(l)) } if m.StartedTime != nil { l = m.StartedTime.Size() n += 1 + l + sovService(uint64(l)) } if m.ScheduleToCloseTimeout != nil { l = m.ScheduleToCloseTimeout.Size() n += 1 + l + sovService(uint64(l)) } if m.StartToCloseTimeout != nil { l = m.StartToCloseTimeout.Size() n += 1 + l + sovService(uint64(l)) } if m.HeartbeatTimeout != nil { l = m.HeartbeatTimeout.Size() n += 1 + l + sovService(uint64(l)) } if m.Attempt != 0 { n += 1 + sovService(uint64(m.Attempt)) } if m.ScheduledTimeOfThisAttempt != nil { l = m.ScheduledTimeOfThisAttempt.Size() n += 1 + l + sovService(uint64(l)) } if m.HeartbeatDetails != nil { l = m.HeartbeatDetails.Size() n += 1 + l + sovService(uint64(l)) } if m.WorkflowType != nil { l = m.WorkflowType.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.WorkflowDomain) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.Header != nil { l = m.Header.Size() n += 2 + l + sovService(uint64(l)) } if m.LoadBalancerHints != nil { l = m.LoadBalancerHints.Size() n += 2 + l + sovService(uint64(l)) } if m.PartitionConfig != nil { l = m.PartitionConfig.Size() n += 2 + l + sovService(uint64(l)) } if m.AutoConfigHint != nil { l = m.AutoConfigHint.Size() n += 2 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *AddDecisionTaskRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.ScheduleId != 0 { n += 1 + sovService(uint64(m.ScheduleId)) } if m.ScheduleToStartTimeout != nil { l = m.ScheduleToStartTimeout.Size() n += 1 + l + sovService(uint64(l)) } if m.Source != 0 { n += 1 + sovService(uint64(m.Source)) } l = len(m.ForwardedFrom) if l > 0 { n += 1 + l + sovService(uint64(l)) } if len(m.PartitionConfig) > 0 { for k, v := range m.PartitionConfig { _ = k _ = v mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + 1 + len(v) + sovService(uint64(len(v))) n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *AddDecisionTaskResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.PartitionConfig != nil { l = m.PartitionConfig.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *AddActivityTaskRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.SourceDomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.ScheduleId != 0 { n += 1 + sovService(uint64(m.ScheduleId)) } if m.ScheduleToStartTimeout != nil { l = m.ScheduleToStartTimeout.Size() n += 1 + l + sovService(uint64(l)) } if m.Source != 0 { n += 1 + sovService(uint64(m.Source)) } l = len(m.ForwardedFrom) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.ActivityTaskDispatchInfo != nil { l = m.ActivityTaskDispatchInfo.Size() n += 1 + l + sovService(uint64(l)) } if len(m.PartitionConfig) > 0 { for k, v := range m.PartitionConfig { _ = k _ = v mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + 1 + len(v) + sovService(uint64(len(v))) n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ActivityTaskDispatchInfo) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.ScheduledEvent != nil { l = m.ScheduledEvent.Size() n += 1 + l + sovService(uint64(l)) } if m.StartedTime != nil { l = m.StartedTime.Size() n += 1 + l + sovService(uint64(l)) } if m.Attempt != 0 { n += 1 + sovService(uint64(m.Attempt)) } if m.ScheduledTimeOfThisAttempt != nil { l = m.ScheduledTimeOfThisAttempt.Size() n += 1 + l + sovService(uint64(l)) } if m.HeartbeatDetails != nil { l = m.HeartbeatDetails.Size() n += 1 + l + sovService(uint64(l)) } if m.WorkflowType != nil { l = m.WorkflowType.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.WorkflowDomain) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *AddActivityTaskResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.PartitionConfig != nil { l = m.PartitionConfig.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *QueryWorkflowRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.ForwardedFrom) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *QueryWorkflowResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.QueryResult != nil { l = m.QueryResult.Size() n += 1 + l + sovService(uint64(l)) } if m.QueryRejected != nil { l = m.QueryRejected.Size() n += 1 + l + sovService(uint64(l)) } if m.PartitionConfig != nil { l = m.PartitionConfig.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondQueryTaskCompletedRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.TaskId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RespondQueryTaskCompletedResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *CancelOutstandingPollRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.PollerId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.TaskListType != 0 { n += 1 + sovService(uint64(m.TaskListType)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *CancelOutstandingPollResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *DescribeTaskListRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != nil { l = m.Request.Size() n += 1 + l + sovService(uint64(l)) } l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *DescribeTaskListResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Pollers) > 0 { for _, e := range m.Pollers { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.TaskListStatus != nil { l = m.TaskListStatus.Size() n += 1 + l + sovService(uint64(l)) } if m.PartitionConfig != nil { l = m.PartitionConfig.Size() n += 1 + l + sovService(uint64(l)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ListTaskListPartitionsRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Domain) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ListTaskListPartitionsResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.ActivityTaskListPartitions) > 0 { for _, e := range m.ActivityTaskListPartitions { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if len(m.DecisionTaskListPartitions) > 0 { for _, e := range m.DecisionTaskListPartitions { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetTaskListsByDomainRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Domain) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetTaskListsByDomainResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.DecisionTaskListMap) > 0 { for k, v := range m.DecisionTaskListMap { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovService(uint64(l)) } mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + l n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if len(m.ActivityTaskListMap) > 0 { for k, v := range m.ActivityTaskListMap { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovService(uint64(l)) } mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + l n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *UpdateTaskListPartitionConfigRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.TaskListType != 0 { n += 1 + sovService(uint64(m.TaskListType)) } if m.PartitionConfig != nil { l = m.PartitionConfig.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *UpdateTaskListPartitionConfigResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RefreshTaskListPartitionConfigRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.TaskList != nil { l = m.TaskList.Size() n += 1 + l + sovService(uint64(l)) } if m.TaskListType != 0 { n += 1 + sovService(uint64(m.TaskListType)) } if m.PartitionConfig != nil { l = m.PartitionConfig.Size() n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RefreshTaskListPartitionConfigResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func sovService(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozService(x uint64) (n int) { return sovService(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *TaskListPartition) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TaskListPartition: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TaskListPartition: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IsolationGroups", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.IsolationGroups = append(m.IsolationGroups, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TaskListPartitionConfig) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TaskListPartitionConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TaskListPartitionConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } m.Version = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Version |= int64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NumReadPartitions", wireType) } m.NumReadPartitions = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NumReadPartitions |= int32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NumWritePartitions", wireType) } m.NumWritePartitions = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NumWritePartitions |= int32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ReadPartitions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ReadPartitions == nil { m.ReadPartitions = make(map[int32]*TaskListPartition) } var mapkey int32 var mapvalue *TaskListPartition for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapkey |= int32(b&0x7F) << shift if b < 0x80 { break } } } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthService } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthService } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &TaskListPartition{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ReadPartitions[mapkey] = mapvalue iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WritePartitions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WritePartitions == nil { m.WritePartitions = make(map[int32]*TaskListPartition) } var mapkey int32 var mapvalue *TaskListPartition for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapkey |= int32(b&0x7F) << shift if b < 0x80 { break } } } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthService } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthService } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &TaskListPartition{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.WritePartitions[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LoadBalancerHints) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LoadBalancerHints: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LoadBalancerHints: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BacklogCount", wireType) } m.BacklogCount = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.BacklogCount |= int64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field RatePerSecond", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.RatePerSecond = float64(math.Float64frombits(v)) default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PollForDecisionTaskRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PollForDecisionTaskRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PollForDecisionTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.PollForDecisionTaskRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PollerId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.PollerId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ForwardedFrom", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ForwardedFrom = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IsolationGroup", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.IsolationGroup = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PollForDecisionTaskResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PollForDecisionTaskResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PollForDecisionTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.TaskToken = append(m.TaskToken[:0], dAtA[iNdEx:postIndex]...) if m.TaskToken == nil { m.TaskToken = []byte{} } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowType == nil { m.WorkflowType = &v1.WorkflowType{} } if err := m.WorkflowType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PreviousStartedEventId", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PreviousStartedEventId == nil { m.PreviousStartedEventId = &types.Int64Value{} } if err := m.PreviousStartedEventId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field StartedEventId", wireType) } m.StartedEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.StartedEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Attempt", wireType) } m.Attempt = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Attempt |= int32(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NextEventId", wireType) } m.NextEventId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NextEventId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BacklogCountHint", wireType) } m.BacklogCountHint = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.BacklogCountHint |= int64(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field StickyExecutionEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.StickyExecutionEnabled = bool(v != 0) case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Query", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Query == nil { m.Query = &v1.WorkflowQuery{} } if err := m.Query.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DecisionInfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.DecisionInfo == nil { m.DecisionInfo = &v11.TransientDecisionInfo{} } if err := m.DecisionInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecutionTaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecutionTaskList == nil { m.WorkflowExecutionTaskList = &v1.TaskList{} } if err := m.WorkflowExecutionTaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EventStoreVersion", wireType) } m.EventStoreVersion = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.EventStoreVersion |= int32(b&0x7F) << shift if b < 0x80 { break } } case 14: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BranchToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.BranchToken = append(m.BranchToken[:0], dAtA[iNdEx:postIndex]...) if m.BranchToken == nil { m.BranchToken = []byte{} } iNdEx = postIndex case 15: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduledTime == nil { m.ScheduledTime = &types.Timestamp{} } if err := m.ScheduledTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 16: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StartedTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartedTime == nil { m.StartedTime = &types.Timestamp{} } if err := m.StartedTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Queries", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Queries == nil { m.Queries = make(map[string]*v1.WorkflowQuery) } var mapkey string var mapvalue *v1.WorkflowQuery for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthService } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthService } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &v1.WorkflowQuery{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Queries[mapkey] = mapvalue iNdEx = postIndex case 18: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TotalHistoryBytes", wireType) } m.TotalHistoryBytes = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TotalHistoryBytes |= int64(b&0x7F) << shift if b < 0x80 { break } } case 19: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = &TaskListPartitionConfig{} } if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 20: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LoadBalancerHints", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.LoadBalancerHints == nil { m.LoadBalancerHints = &LoadBalancerHints{} } if err := m.LoadBalancerHints.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 21: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AutoConfigHint", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.AutoConfigHint == nil { m.AutoConfigHint = &v1.AutoConfigHint{} } if err := m.AutoConfigHint.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PollForActivityTaskRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PollForActivityTaskRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PollForActivityTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.PollForActivityTaskRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PollerId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.PollerId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ForwardedFrom", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ForwardedFrom = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IsolationGroup", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.IsolationGroup = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PollForActivityTaskResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PollForActivityTaskResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PollForActivityTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.TaskToken = append(m.TaskToken[:0], dAtA[iNdEx:postIndex]...) if m.TaskToken == nil { m.TaskToken = []byte{} } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActivityId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ActivityId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActivityType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ActivityType == nil { m.ActivityType = &v1.ActivityType{} } if err := m.ActivityType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Input == nil { m.Input = &v1.Payload{} } if err := m.Input.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduledTime == nil { m.ScheduledTime = &types.Timestamp{} } if err := m.ScheduledTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StartedTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartedTime == nil { m.StartedTime = &types.Timestamp{} } if err := m.StartedTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduleToCloseTimeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduleToCloseTimeout == nil { m.ScheduleToCloseTimeout = &types.Duration{} } if err := m.ScheduleToCloseTimeout.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StartToCloseTimeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartToCloseTimeout == nil { m.StartToCloseTimeout = &types.Duration{} } if err := m.StartToCloseTimeout.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field HeartbeatTimeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.HeartbeatTimeout == nil { m.HeartbeatTimeout = &types.Duration{} } if err := m.HeartbeatTimeout.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Attempt", wireType) } m.Attempt = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Attempt |= int32(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledTimeOfThisAttempt", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduledTimeOfThisAttempt == nil { m.ScheduledTimeOfThisAttempt = &types.Timestamp{} } if err := m.ScheduledTimeOfThisAttempt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field HeartbeatDetails", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.HeartbeatDetails == nil { m.HeartbeatDetails = &v1.Payload{} } if err := m.HeartbeatDetails.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 14: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowType == nil { m.WorkflowType = &v1.WorkflowType{} } if err := m.WorkflowType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 15: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowDomain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.WorkflowDomain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 16: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Header == nil { m.Header = &v1.Header{} } if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LoadBalancerHints", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.LoadBalancerHints == nil { m.LoadBalancerHints = &LoadBalancerHints{} } if err := m.LoadBalancerHints.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 19: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = &TaskListPartitionConfig{} } if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 20: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AutoConfigHint", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.AutoConfigHint == nil { m.AutoConfigHint = &v1.AutoConfigHint{} } if err := m.AutoConfigHint.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AddDecisionTaskRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AddDecisionTaskRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AddDecisionTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduleId", wireType) } m.ScheduleId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ScheduleId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduleToStartTimeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduleToStartTimeout == nil { m.ScheduleToStartTimeout = &types.Duration{} } if err := m.ScheduleToStartTimeout.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) } m.Source = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Source |= v11.TaskSource(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ForwardedFrom", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ForwardedFrom = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return ErrInvalidLengthService } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return ErrInvalidLengthService } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.PartitionConfig[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AddDecisionTaskResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AddDecisionTaskResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AddDecisionTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = &TaskListPartitionConfig{} } if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AddActivityTaskRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AddActivityTaskRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AddActivityTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SourceDomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.SourceDomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduleId", wireType) } m.ScheduleId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ScheduleId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduleToStartTimeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduleToStartTimeout == nil { m.ScheduleToStartTimeout = &types.Duration{} } if err := m.ScheduleToStartTimeout.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) } m.Source = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Source |= v11.TaskSource(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ForwardedFrom", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ForwardedFrom = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActivityTaskDispatchInfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ActivityTaskDispatchInfo == nil { m.ActivityTaskDispatchInfo = &ActivityTaskDispatchInfo{} } if err := m.ActivityTaskDispatchInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return ErrInvalidLengthService } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return ErrInvalidLengthService } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.PartitionConfig[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ActivityTaskDispatchInfo) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ActivityTaskDispatchInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ActivityTaskDispatchInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledEvent", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduledEvent == nil { m.ScheduledEvent = &v1.HistoryEvent{} } if err := m.ScheduledEvent.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StartedTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartedTime == nil { m.StartedTime = &types.Timestamp{} } if err := m.StartedTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Attempt", wireType) } m.Attempt = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Attempt |= int32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledTimeOfThisAttempt", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduledTimeOfThisAttempt == nil { m.ScheduledTimeOfThisAttempt = &types.Timestamp{} } if err := m.ScheduledTimeOfThisAttempt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field HeartbeatDetails", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.HeartbeatDetails == nil { m.HeartbeatDetails = &v1.Payload{} } if err := m.HeartbeatDetails.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowType == nil { m.WorkflowType = &v1.WorkflowType{} } if err := m.WorkflowType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowDomain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.WorkflowDomain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AddActivityTaskResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AddActivityTaskResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AddActivityTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = &TaskListPartitionConfig{} } if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *QueryWorkflowRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: QueryWorkflowRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: QueryWorkflowRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.QueryWorkflowRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ForwardedFrom", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ForwardedFrom = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *QueryWorkflowResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: QueryWorkflowResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: QueryWorkflowResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field QueryResult", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.QueryResult == nil { m.QueryResult = &v1.Payload{} } if err := m.QueryResult.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field QueryRejected", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.QueryRejected == nil { m.QueryRejected = &v1.QueryRejected{} } if err := m.QueryRejected.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = &v1.TaskListPartitionConfig{} } if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondQueryTaskCompletedRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondQueryTaskCompletedRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondQueryTaskCompletedRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.RespondQueryTaskCompletedRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.TaskId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RespondQueryTaskCompletedResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RespondQueryTaskCompletedResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RespondQueryTaskCompletedResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CancelOutstandingPollRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CancelOutstandingPollRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CancelOutstandingPollRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PollerId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.PollerId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TaskListType", wireType) } m.TaskListType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TaskListType |= v1.TaskListType(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CancelOutstandingPollResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CancelOutstandingPollResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CancelOutstandingPollResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DescribeTaskListRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DescribeTaskListRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DescribeTaskListRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Request == nil { m.Request = &v1.DescribeTaskListRequest{} } if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DescribeTaskListResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DescribeTaskListResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DescribeTaskListResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pollers", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Pollers = append(m.Pollers, &v1.PollerInfo{}) if err := m.Pollers[len(m.Pollers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskListStatus", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskListStatus == nil { m.TaskListStatus = &v1.TaskListStatus{} } if err := m.TaskListStatus.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = &v1.TaskListPartitionConfig{} } if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ListTaskListPartitionsRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ListTaskListPartitionsRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ListTaskListPartitionsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Domain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Domain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ListTaskListPartitionsResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ListTaskListPartitionsResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ListTaskListPartitionsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActivityTaskListPartitions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ActivityTaskListPartitions = append(m.ActivityTaskListPartitions, &v1.TaskListPartitionMetadata{}) if err := m.ActivityTaskListPartitions[len(m.ActivityTaskListPartitions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DecisionTaskListPartitions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DecisionTaskListPartitions = append(m.DecisionTaskListPartitions, &v1.TaskListPartitionMetadata{}) if err := m.DecisionTaskListPartitions[len(m.DecisionTaskListPartitions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetTaskListsByDomainRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetTaskListsByDomainRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetTaskListsByDomainRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Domain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Domain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetTaskListsByDomainResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetTaskListsByDomainResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetTaskListsByDomainResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DecisionTaskListMap", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.DecisionTaskListMap == nil { m.DecisionTaskListMap = make(map[string]*DescribeTaskListResponse) } var mapkey string var mapvalue *DescribeTaskListResponse for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthService } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthService } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &DescribeTaskListResponse{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.DecisionTaskListMap[mapkey] = mapvalue iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActivityTaskListMap", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.ActivityTaskListMap == nil { m.ActivityTaskListMap = make(map[string]*DescribeTaskListResponse) } var mapkey string var mapvalue *DescribeTaskListResponse for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthService } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthService } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &DescribeTaskListResponse{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ActivityTaskListMap[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *UpdateTaskListPartitionConfigRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: UpdateTaskListPartitionConfigRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: UpdateTaskListPartitionConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TaskListType", wireType) } m.TaskListType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TaskListType |= v1.TaskListType(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = &v1.TaskListPartitionConfig{} } if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *UpdateTaskListPartitionConfigResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: UpdateTaskListPartitionConfigResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: UpdateTaskListPartitionConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RefreshTaskListPartitionConfigRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RefreshTaskListPartitionConfigRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RefreshTaskListPartitionConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskList", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.TaskList == nil { m.TaskList = &v1.TaskList{} } if err := m.TaskList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TaskListType", wireType) } m.TaskListType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TaskListType |= v1.TaskListType(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionConfig == nil { m.PartitionConfig = &v1.TaskListPartitionConfig{} } if err := m.PartitionConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RefreshTaskListPartitionConfigResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RefreshTaskListPartitionConfigResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RefreshTaskListPartitionConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func skipService(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowService } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } wireType := int(wire & 0x7) switch wireType { case 0: for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowService } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } iNdEx++ if dAtA[iNdEx-1] < 0x80 { break } } case 1: iNdEx += 8 case 2: var length int for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowService } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { break } } if length < 0 { return 0, ErrInvalidLengthService } iNdEx += length case 3: depth++ case 4: if depth == 0 { return 0, ErrUnexpectedEndOfGroupService } depth-- case 5: iNdEx += 4 default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } if iNdEx < 0 { return 0, ErrInvalidLengthService } if depth == 0 { return iNdEx, nil } } return 0, io.ErrUnexpectedEOF } var ( ErrInvalidLengthService = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowService = fmt.Errorf("proto: integer overflow") ErrUnexpectedEndOfGroupService = fmt.Errorf("proto: unexpected end of group") ) ================================================ FILE: .gen/proto/matching/v1/service.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/matching/v1/service.proto package matchingv1 import ( "context" "io/ioutil" "reflect" "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" "go.uber.org/fx" "go.uber.org/yarpc" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/api/x/restriction" "go.uber.org/yarpc/encoding/protobuf" "go.uber.org/yarpc/encoding/protobuf/reflection" ) var _ = ioutil.NopCloser // MatchingAPIYARPCClient is the YARPC client-side interface for the MatchingAPI service. type MatchingAPIYARPCClient interface { PollForDecisionTask(context.Context, *PollForDecisionTaskRequest, ...yarpc.CallOption) (*PollForDecisionTaskResponse, error) PollForActivityTask(context.Context, *PollForActivityTaskRequest, ...yarpc.CallOption) (*PollForActivityTaskResponse, error) AddDecisionTask(context.Context, *AddDecisionTaskRequest, ...yarpc.CallOption) (*AddDecisionTaskResponse, error) AddActivityTask(context.Context, *AddActivityTaskRequest, ...yarpc.CallOption) (*AddActivityTaskResponse, error) QueryWorkflow(context.Context, *QueryWorkflowRequest, ...yarpc.CallOption) (*QueryWorkflowResponse, error) RespondQueryTaskCompleted(context.Context, *RespondQueryTaskCompletedRequest, ...yarpc.CallOption) (*RespondQueryTaskCompletedResponse, error) CancelOutstandingPoll(context.Context, *CancelOutstandingPollRequest, ...yarpc.CallOption) (*CancelOutstandingPollResponse, error) DescribeTaskList(context.Context, *DescribeTaskListRequest, ...yarpc.CallOption) (*DescribeTaskListResponse, error) ListTaskListPartitions(context.Context, *ListTaskListPartitionsRequest, ...yarpc.CallOption) (*ListTaskListPartitionsResponse, error) GetTaskListsByDomain(context.Context, *GetTaskListsByDomainRequest, ...yarpc.CallOption) (*GetTaskListsByDomainResponse, error) UpdateTaskListPartitionConfig(context.Context, *UpdateTaskListPartitionConfigRequest, ...yarpc.CallOption) (*UpdateTaskListPartitionConfigResponse, error) RefreshTaskListPartitionConfig(context.Context, *RefreshTaskListPartitionConfigRequest, ...yarpc.CallOption) (*RefreshTaskListPartitionConfigResponse, error) } func newMatchingAPIYARPCClient(clientConfig transport.ClientConfig, anyResolver jsonpb.AnyResolver, options ...protobuf.ClientOption) MatchingAPIYARPCClient { return &_MatchingAPIYARPCCaller{protobuf.NewStreamClient( protobuf.ClientParams{ ServiceName: "uber.cadence.matching.v1.MatchingAPI", ClientConfig: clientConfig, AnyResolver: anyResolver, Options: options, }, )} } // NewMatchingAPIYARPCClient builds a new YARPC client for the MatchingAPI service. func NewMatchingAPIYARPCClient(clientConfig transport.ClientConfig, options ...protobuf.ClientOption) MatchingAPIYARPCClient { return newMatchingAPIYARPCClient(clientConfig, nil, options...) } // MatchingAPIYARPCServer is the YARPC server-side interface for the MatchingAPI service. type MatchingAPIYARPCServer interface { PollForDecisionTask(context.Context, *PollForDecisionTaskRequest) (*PollForDecisionTaskResponse, error) PollForActivityTask(context.Context, *PollForActivityTaskRequest) (*PollForActivityTaskResponse, error) AddDecisionTask(context.Context, *AddDecisionTaskRequest) (*AddDecisionTaskResponse, error) AddActivityTask(context.Context, *AddActivityTaskRequest) (*AddActivityTaskResponse, error) QueryWorkflow(context.Context, *QueryWorkflowRequest) (*QueryWorkflowResponse, error) RespondQueryTaskCompleted(context.Context, *RespondQueryTaskCompletedRequest) (*RespondQueryTaskCompletedResponse, error) CancelOutstandingPoll(context.Context, *CancelOutstandingPollRequest) (*CancelOutstandingPollResponse, error) DescribeTaskList(context.Context, *DescribeTaskListRequest) (*DescribeTaskListResponse, error) ListTaskListPartitions(context.Context, *ListTaskListPartitionsRequest) (*ListTaskListPartitionsResponse, error) GetTaskListsByDomain(context.Context, *GetTaskListsByDomainRequest) (*GetTaskListsByDomainResponse, error) UpdateTaskListPartitionConfig(context.Context, *UpdateTaskListPartitionConfigRequest) (*UpdateTaskListPartitionConfigResponse, error) RefreshTaskListPartitionConfig(context.Context, *RefreshTaskListPartitionConfigRequest) (*RefreshTaskListPartitionConfigResponse, error) } type buildMatchingAPIYARPCProceduresParams struct { Server MatchingAPIYARPCServer AnyResolver jsonpb.AnyResolver } func buildMatchingAPIYARPCProcedures(params buildMatchingAPIYARPCProceduresParams) []transport.Procedure { handler := &_MatchingAPIYARPCHandler{params.Server} return protobuf.BuildProcedures( protobuf.BuildProceduresParams{ ServiceName: "uber.cadence.matching.v1.MatchingAPI", UnaryHandlerParams: []protobuf.BuildProceduresUnaryHandlerParams{ { MethodName: "PollForDecisionTask", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.PollForDecisionTask, NewRequest: newMatchingAPIServicePollForDecisionTaskYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "PollForActivityTask", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.PollForActivityTask, NewRequest: newMatchingAPIServicePollForActivityTaskYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "AddDecisionTask", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.AddDecisionTask, NewRequest: newMatchingAPIServiceAddDecisionTaskYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "AddActivityTask", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.AddActivityTask, NewRequest: newMatchingAPIServiceAddActivityTaskYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "QueryWorkflow", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.QueryWorkflow, NewRequest: newMatchingAPIServiceQueryWorkflowYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RespondQueryTaskCompleted", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RespondQueryTaskCompleted, NewRequest: newMatchingAPIServiceRespondQueryTaskCompletedYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "CancelOutstandingPoll", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.CancelOutstandingPoll, NewRequest: newMatchingAPIServiceCancelOutstandingPollYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "DescribeTaskList", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.DescribeTaskList, NewRequest: newMatchingAPIServiceDescribeTaskListYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "ListTaskListPartitions", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.ListTaskListPartitions, NewRequest: newMatchingAPIServiceListTaskListPartitionsYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "GetTaskListsByDomain", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.GetTaskListsByDomain, NewRequest: newMatchingAPIServiceGetTaskListsByDomainYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "UpdateTaskListPartitionConfig", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.UpdateTaskListPartitionConfig, NewRequest: newMatchingAPIServiceUpdateTaskListPartitionConfigYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, { MethodName: "RefreshTaskListPartitionConfig", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.RefreshTaskListPartitionConfig, NewRequest: newMatchingAPIServiceRefreshTaskListPartitionConfigYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, }, OnewayHandlerParams: []protobuf.BuildProceduresOnewayHandlerParams{}, StreamHandlerParams: []protobuf.BuildProceduresStreamHandlerParams{}, }, ) } // BuildMatchingAPIYARPCProcedures prepares an implementation of the MatchingAPI service for YARPC registration. func BuildMatchingAPIYARPCProcedures(server MatchingAPIYARPCServer) []transport.Procedure { return buildMatchingAPIYARPCProcedures(buildMatchingAPIYARPCProceduresParams{Server: server}) } // FxMatchingAPIYARPCClientParams defines the input // for NewFxMatchingAPIYARPCClient. It provides the // paramaters to get a MatchingAPIYARPCClient in an // Fx application. type FxMatchingAPIYARPCClientParams struct { fx.In Provider yarpc.ClientConfig AnyResolver jsonpb.AnyResolver `name:"yarpcfx" optional:"true"` Restriction restriction.Checker `optional:"true"` } // FxMatchingAPIYARPCClientResult defines the output // of NewFxMatchingAPIYARPCClient. It provides a // MatchingAPIYARPCClient to an Fx application. type FxMatchingAPIYARPCClientResult struct { fx.Out Client MatchingAPIYARPCClient // We are using an fx.Out struct here instead of just returning a client // so that we can add more values or add named versions of the client in // the future without breaking any existing code. } // NewFxMatchingAPIYARPCClient provides a MatchingAPIYARPCClient // to an Fx application using the given name for routing. // // fx.Provide( // matchingv1.NewFxMatchingAPIYARPCClient("service-name"), // ... // ) func NewFxMatchingAPIYARPCClient(name string, options ...protobuf.ClientOption) interface{} { return func(params FxMatchingAPIYARPCClientParams) FxMatchingAPIYARPCClientResult { cc := params.Provider.ClientConfig(name) if params.Restriction != nil { if namer, ok := cc.GetUnaryOutbound().(transport.Namer); ok { if err := params.Restriction.Check(protobuf.Encoding, namer.TransportName()); err != nil { panic(err.Error()) } } } return FxMatchingAPIYARPCClientResult{ Client: newMatchingAPIYARPCClient(cc, params.AnyResolver, options...), } } } // FxMatchingAPIYARPCProceduresParams defines the input // for NewFxMatchingAPIYARPCProcedures. It provides the // paramaters to get MatchingAPIYARPCServer procedures in an // Fx application. type FxMatchingAPIYARPCProceduresParams struct { fx.In Server MatchingAPIYARPCServer AnyResolver jsonpb.AnyResolver `name:"yarpcfx" optional:"true"` } // FxMatchingAPIYARPCProceduresResult defines the output // of NewFxMatchingAPIYARPCProcedures. It provides // MatchingAPIYARPCServer procedures to an Fx application. // // The procedures are provided to the "yarpcfx" value group. // Dig 1.2 or newer must be used for this feature to work. type FxMatchingAPIYARPCProceduresResult struct { fx.Out Procedures []transport.Procedure `group:"yarpcfx"` ReflectionMeta reflection.ServerMeta `group:"yarpcfx"` } // NewFxMatchingAPIYARPCProcedures provides MatchingAPIYARPCServer procedures to an Fx application. // It expects a MatchingAPIYARPCServer to be present in the container. // // fx.Provide( // matchingv1.NewFxMatchingAPIYARPCProcedures(), // ... // ) func NewFxMatchingAPIYARPCProcedures() interface{} { return func(params FxMatchingAPIYARPCProceduresParams) FxMatchingAPIYARPCProceduresResult { return FxMatchingAPIYARPCProceduresResult{ Procedures: buildMatchingAPIYARPCProcedures(buildMatchingAPIYARPCProceduresParams{ Server: params.Server, AnyResolver: params.AnyResolver, }), ReflectionMeta: MatchingAPIReflectionMeta, } } } // MatchingAPIReflectionMeta is the reflection server metadata // required for using the gRPC reflection protocol with YARPC. // // See https://github.com/grpc/grpc/blob/master/doc/server-reflection.md. var MatchingAPIReflectionMeta = reflection.ServerMeta{ ServiceName: "uber.cadence.matching.v1.MatchingAPI", FileDescriptors: yarpcFileDescriptorClosure826e827d3aabf7fc, } type _MatchingAPIYARPCCaller struct { streamClient protobuf.StreamClient } func (c *_MatchingAPIYARPCCaller) PollForDecisionTask(ctx context.Context, request *PollForDecisionTaskRequest, options ...yarpc.CallOption) (*PollForDecisionTaskResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "PollForDecisionTask", request, newMatchingAPIServicePollForDecisionTaskYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*PollForDecisionTaskResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServicePollForDecisionTaskYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) PollForActivityTask(ctx context.Context, request *PollForActivityTaskRequest, options ...yarpc.CallOption) (*PollForActivityTaskResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "PollForActivityTask", request, newMatchingAPIServicePollForActivityTaskYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*PollForActivityTaskResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServicePollForActivityTaskYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) AddDecisionTask(ctx context.Context, request *AddDecisionTaskRequest, options ...yarpc.CallOption) (*AddDecisionTaskResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "AddDecisionTask", request, newMatchingAPIServiceAddDecisionTaskYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*AddDecisionTaskResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceAddDecisionTaskYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) AddActivityTask(ctx context.Context, request *AddActivityTaskRequest, options ...yarpc.CallOption) (*AddActivityTaskResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "AddActivityTask", request, newMatchingAPIServiceAddActivityTaskYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*AddActivityTaskResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceAddActivityTaskYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) QueryWorkflow(ctx context.Context, request *QueryWorkflowRequest, options ...yarpc.CallOption) (*QueryWorkflowResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "QueryWorkflow", request, newMatchingAPIServiceQueryWorkflowYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*QueryWorkflowResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceQueryWorkflowYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) RespondQueryTaskCompleted(ctx context.Context, request *RespondQueryTaskCompletedRequest, options ...yarpc.CallOption) (*RespondQueryTaskCompletedResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RespondQueryTaskCompleted", request, newMatchingAPIServiceRespondQueryTaskCompletedYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RespondQueryTaskCompletedResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceRespondQueryTaskCompletedYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) CancelOutstandingPoll(ctx context.Context, request *CancelOutstandingPollRequest, options ...yarpc.CallOption) (*CancelOutstandingPollResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "CancelOutstandingPoll", request, newMatchingAPIServiceCancelOutstandingPollYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*CancelOutstandingPollResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceCancelOutstandingPollYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) DescribeTaskList(ctx context.Context, request *DescribeTaskListRequest, options ...yarpc.CallOption) (*DescribeTaskListResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "DescribeTaskList", request, newMatchingAPIServiceDescribeTaskListYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*DescribeTaskListResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceDescribeTaskListYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) ListTaskListPartitions(ctx context.Context, request *ListTaskListPartitionsRequest, options ...yarpc.CallOption) (*ListTaskListPartitionsResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "ListTaskListPartitions", request, newMatchingAPIServiceListTaskListPartitionsYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*ListTaskListPartitionsResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceListTaskListPartitionsYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) GetTaskListsByDomain(ctx context.Context, request *GetTaskListsByDomainRequest, options ...yarpc.CallOption) (*GetTaskListsByDomainResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "GetTaskListsByDomain", request, newMatchingAPIServiceGetTaskListsByDomainYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*GetTaskListsByDomainResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceGetTaskListsByDomainYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) UpdateTaskListPartitionConfig(ctx context.Context, request *UpdateTaskListPartitionConfigRequest, options ...yarpc.CallOption) (*UpdateTaskListPartitionConfigResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "UpdateTaskListPartitionConfig", request, newMatchingAPIServiceUpdateTaskListPartitionConfigYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*UpdateTaskListPartitionConfigResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceUpdateTaskListPartitionConfigYARPCResponse, responseMessage) } return response, err } func (c *_MatchingAPIYARPCCaller) RefreshTaskListPartitionConfig(ctx context.Context, request *RefreshTaskListPartitionConfigRequest, options ...yarpc.CallOption) (*RefreshTaskListPartitionConfigResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "RefreshTaskListPartitionConfig", request, newMatchingAPIServiceRefreshTaskListPartitionConfigYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*RefreshTaskListPartitionConfigResponse) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceRefreshTaskListPartitionConfigYARPCResponse, responseMessage) } return response, err } type _MatchingAPIYARPCHandler struct { server MatchingAPIYARPCServer } func (h *_MatchingAPIYARPCHandler) PollForDecisionTask(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *PollForDecisionTaskRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*PollForDecisionTaskRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServicePollForDecisionTaskYARPCRequest, requestMessage) } } response, err := h.server.PollForDecisionTask(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) PollForActivityTask(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *PollForActivityTaskRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*PollForActivityTaskRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServicePollForActivityTaskYARPCRequest, requestMessage) } } response, err := h.server.PollForActivityTask(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) AddDecisionTask(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *AddDecisionTaskRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*AddDecisionTaskRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceAddDecisionTaskYARPCRequest, requestMessage) } } response, err := h.server.AddDecisionTask(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) AddActivityTask(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *AddActivityTaskRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*AddActivityTaskRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceAddActivityTaskYARPCRequest, requestMessage) } } response, err := h.server.AddActivityTask(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) QueryWorkflow(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *QueryWorkflowRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*QueryWorkflowRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceQueryWorkflowYARPCRequest, requestMessage) } } response, err := h.server.QueryWorkflow(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) RespondQueryTaskCompleted(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RespondQueryTaskCompletedRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RespondQueryTaskCompletedRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceRespondQueryTaskCompletedYARPCRequest, requestMessage) } } response, err := h.server.RespondQueryTaskCompleted(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) CancelOutstandingPoll(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *CancelOutstandingPollRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*CancelOutstandingPollRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceCancelOutstandingPollYARPCRequest, requestMessage) } } response, err := h.server.CancelOutstandingPoll(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) DescribeTaskList(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *DescribeTaskListRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*DescribeTaskListRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceDescribeTaskListYARPCRequest, requestMessage) } } response, err := h.server.DescribeTaskList(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) ListTaskListPartitions(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *ListTaskListPartitionsRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*ListTaskListPartitionsRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceListTaskListPartitionsYARPCRequest, requestMessage) } } response, err := h.server.ListTaskListPartitions(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) GetTaskListsByDomain(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *GetTaskListsByDomainRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*GetTaskListsByDomainRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceGetTaskListsByDomainYARPCRequest, requestMessage) } } response, err := h.server.GetTaskListsByDomain(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) UpdateTaskListPartitionConfig(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *UpdateTaskListPartitionConfigRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*UpdateTaskListPartitionConfigRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceUpdateTaskListPartitionConfigYARPCRequest, requestMessage) } } response, err := h.server.UpdateTaskListPartitionConfig(ctx, request) if response == nil { return nil, err } return response, err } func (h *_MatchingAPIYARPCHandler) RefreshTaskListPartitionConfig(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *RefreshTaskListPartitionConfigRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*RefreshTaskListPartitionConfigRequest) if !ok { return nil, protobuf.CastError(emptyMatchingAPIServiceRefreshTaskListPartitionConfigYARPCRequest, requestMessage) } } response, err := h.server.RefreshTaskListPartitionConfig(ctx, request) if response == nil { return nil, err } return response, err } func newMatchingAPIServicePollForDecisionTaskYARPCRequest() proto.Message { return &PollForDecisionTaskRequest{} } func newMatchingAPIServicePollForDecisionTaskYARPCResponse() proto.Message { return &PollForDecisionTaskResponse{} } func newMatchingAPIServicePollForActivityTaskYARPCRequest() proto.Message { return &PollForActivityTaskRequest{} } func newMatchingAPIServicePollForActivityTaskYARPCResponse() proto.Message { return &PollForActivityTaskResponse{} } func newMatchingAPIServiceAddDecisionTaskYARPCRequest() proto.Message { return &AddDecisionTaskRequest{} } func newMatchingAPIServiceAddDecisionTaskYARPCResponse() proto.Message { return &AddDecisionTaskResponse{} } func newMatchingAPIServiceAddActivityTaskYARPCRequest() proto.Message { return &AddActivityTaskRequest{} } func newMatchingAPIServiceAddActivityTaskYARPCResponse() proto.Message { return &AddActivityTaskResponse{} } func newMatchingAPIServiceQueryWorkflowYARPCRequest() proto.Message { return &QueryWorkflowRequest{} } func newMatchingAPIServiceQueryWorkflowYARPCResponse() proto.Message { return &QueryWorkflowResponse{} } func newMatchingAPIServiceRespondQueryTaskCompletedYARPCRequest() proto.Message { return &RespondQueryTaskCompletedRequest{} } func newMatchingAPIServiceRespondQueryTaskCompletedYARPCResponse() proto.Message { return &RespondQueryTaskCompletedResponse{} } func newMatchingAPIServiceCancelOutstandingPollYARPCRequest() proto.Message { return &CancelOutstandingPollRequest{} } func newMatchingAPIServiceCancelOutstandingPollYARPCResponse() proto.Message { return &CancelOutstandingPollResponse{} } func newMatchingAPIServiceDescribeTaskListYARPCRequest() proto.Message { return &DescribeTaskListRequest{} } func newMatchingAPIServiceDescribeTaskListYARPCResponse() proto.Message { return &DescribeTaskListResponse{} } func newMatchingAPIServiceListTaskListPartitionsYARPCRequest() proto.Message { return &ListTaskListPartitionsRequest{} } func newMatchingAPIServiceListTaskListPartitionsYARPCResponse() proto.Message { return &ListTaskListPartitionsResponse{} } func newMatchingAPIServiceGetTaskListsByDomainYARPCRequest() proto.Message { return &GetTaskListsByDomainRequest{} } func newMatchingAPIServiceGetTaskListsByDomainYARPCResponse() proto.Message { return &GetTaskListsByDomainResponse{} } func newMatchingAPIServiceUpdateTaskListPartitionConfigYARPCRequest() proto.Message { return &UpdateTaskListPartitionConfigRequest{} } func newMatchingAPIServiceUpdateTaskListPartitionConfigYARPCResponse() proto.Message { return &UpdateTaskListPartitionConfigResponse{} } func newMatchingAPIServiceRefreshTaskListPartitionConfigYARPCRequest() proto.Message { return &RefreshTaskListPartitionConfigRequest{} } func newMatchingAPIServiceRefreshTaskListPartitionConfigYARPCResponse() proto.Message { return &RefreshTaskListPartitionConfigResponse{} } var ( emptyMatchingAPIServicePollForDecisionTaskYARPCRequest = &PollForDecisionTaskRequest{} emptyMatchingAPIServicePollForDecisionTaskYARPCResponse = &PollForDecisionTaskResponse{} emptyMatchingAPIServicePollForActivityTaskYARPCRequest = &PollForActivityTaskRequest{} emptyMatchingAPIServicePollForActivityTaskYARPCResponse = &PollForActivityTaskResponse{} emptyMatchingAPIServiceAddDecisionTaskYARPCRequest = &AddDecisionTaskRequest{} emptyMatchingAPIServiceAddDecisionTaskYARPCResponse = &AddDecisionTaskResponse{} emptyMatchingAPIServiceAddActivityTaskYARPCRequest = &AddActivityTaskRequest{} emptyMatchingAPIServiceAddActivityTaskYARPCResponse = &AddActivityTaskResponse{} emptyMatchingAPIServiceQueryWorkflowYARPCRequest = &QueryWorkflowRequest{} emptyMatchingAPIServiceQueryWorkflowYARPCResponse = &QueryWorkflowResponse{} emptyMatchingAPIServiceRespondQueryTaskCompletedYARPCRequest = &RespondQueryTaskCompletedRequest{} emptyMatchingAPIServiceRespondQueryTaskCompletedYARPCResponse = &RespondQueryTaskCompletedResponse{} emptyMatchingAPIServiceCancelOutstandingPollYARPCRequest = &CancelOutstandingPollRequest{} emptyMatchingAPIServiceCancelOutstandingPollYARPCResponse = &CancelOutstandingPollResponse{} emptyMatchingAPIServiceDescribeTaskListYARPCRequest = &DescribeTaskListRequest{} emptyMatchingAPIServiceDescribeTaskListYARPCResponse = &DescribeTaskListResponse{} emptyMatchingAPIServiceListTaskListPartitionsYARPCRequest = &ListTaskListPartitionsRequest{} emptyMatchingAPIServiceListTaskListPartitionsYARPCResponse = &ListTaskListPartitionsResponse{} emptyMatchingAPIServiceGetTaskListsByDomainYARPCRequest = &GetTaskListsByDomainRequest{} emptyMatchingAPIServiceGetTaskListsByDomainYARPCResponse = &GetTaskListsByDomainResponse{} emptyMatchingAPIServiceUpdateTaskListPartitionConfigYARPCRequest = &UpdateTaskListPartitionConfigRequest{} emptyMatchingAPIServiceUpdateTaskListPartitionConfigYARPCResponse = &UpdateTaskListPartitionConfigResponse{} emptyMatchingAPIServiceRefreshTaskListPartitionConfigYARPCRequest = &RefreshTaskListPartitionConfigRequest{} emptyMatchingAPIServiceRefreshTaskListPartitionConfigYARPCResponse = &RefreshTaskListPartitionConfigResponse{} ) var yarpcFileDescriptorClosure826e827d3aabf7fc = [][]byte{ // uber/cadence/matching/v1/service.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0x49, 0x73, 0xdc, 0xc6, 0xf5, 0x2f, 0x0c, 0x39, 0x5c, 0xde, 0x90, 0x43, 0xb2, 0x49, 0x53, 0xd0, 0x48, 0x94, 0xa8, 0x91, 0x25, 0xd3, 0xff, 0xbf, 0x33, 0x34, 0x69, 0x49, 0x91, 0xa5, 0x8a, 0x15, 0x2e, 0xa2, 0x34, 0x29, 0x2b, 0x92, 0x21, 0x5a, 0xaa, 0x4a, 0x5c, 0x42, 0x9a, 0x83, 0x26, 0x07, 0x21, 0x06, 0x80, 0x80, 0x06, 0x69, 0xfa, 0x90, 0x43, 0x2a, 0x49, 0xa5, 0x2a, 0xd7, 0xe4, 0x9e, 0xed, 0x4b, 0xe4, 0x92, 0xcf, 0x91, 0x2a, 0x57, 0x0e, 0x39, 0xe4, 0x03, 0x24, 0x55, 0xb9, 0xe5, 0x90, 0xea, 0x05, 0x33, 0x00, 0xa6, 0x31, 0x0b, 0x49, 0xc9, 0x39, 0xe4, 0x46, 0x74, 0xbf, 0xad, 0xdf, 0xf6, 0x7b, 0xdd, 0x43, 0xb8, 0x19, 0xed, 0x91, 0x60, 0xb5, 0x81, 0x2d, 0xe2, 0x36, 0xc8, 0x6a, 0x0b, 0xd3, 0x46, 0xd3, 0x76, 0x0f, 0x56, 0x8f, 0xd6, 0x56, 0x43, 0x12, 0x1c, 0xd9, 0x0d, 0x52, 0xf3, 0x03, 0x8f, 0x7a, 0x48, 0x67, 0x74, 0x35, 0x49, 0x57, 0x8b, 0xe9, 0x6a, 0x47, 0x6b, 0x95, 0x2b, 0x07, 0x9e, 0x77, 0xe0, 0x90, 0x55, 0x4e, 0xb7, 0x17, 0xed, 0xaf, 0x5a, 0x51, 0x80, 0xa9, 0xed, 0xb9, 0x82, 0xb3, 0x72, 0x35, 0xbb, 0x4f, 0xed, 0x16, 0x09, 0x29, 0x6e, 0xf9, 0x92, 0xa0, 0x4b, 0xc0, 0x71, 0x80, 0x7d, 0x9f, 0x04, 0xa1, 0xdc, 0x5f, 0x4e, 0x99, 0x88, 0x7d, 0x9b, 0x59, 0xd7, 0xf0, 0x5a, 0xad, 0x8e, 0x0a, 0x15, 0xc5, 0xeb, 0x88, 0x04, 0x27, 0x92, 0xa0, 0xaa, 0x22, 0xa0, 0x38, 0x3c, 0x74, 0xec, 0x90, 0x4a, 0x9a, 0x15, 0x15, 0x8d, 0x74, 0x82, 0x79, 0xec, 0x05, 0x87, 0x24, 0x90, 0x94, 0xff, 0xd7, 0x8f, 0x72, 0xdf, 0xf1, 0x8e, 0x25, 0xed, 0x35, 0x15, 0x6d, 0xd3, 0x0e, 0xa9, 0xd7, 0x36, 0xee, 0xdd, 0x14, 0x49, 0xd8, 0xc4, 0x01, 0xb1, 0xba, 0xa9, 0x6e, 0xe4, 0x50, 0xa5, 0x4f, 0x51, 0xfd, 0x04, 0xe6, 0x76, 0x71, 0x78, 0xf8, 0xa9, 0x1d, 0xd2, 0x67, 0x38, 0xa0, 0x36, 0x0b, 0x04, 0x7a, 0x1f, 0x66, 0xed, 0xd0, 0x73, 0x78, 0x54, 0xcc, 0x83, 0xc0, 0x8b, 0xfc, 0x50, 0xd7, 0x96, 0x47, 0x56, 0x26, 0x8d, 0x99, 0xf6, 0xfa, 0x23, 0xbe, 0x5c, 0xfd, 0xdb, 0x28, 0x5c, 0xe8, 0x12, 0xb0, 0xe5, 0xb9, 0xfb, 0xf6, 0x01, 0xd2, 0x61, 0xfc, 0x88, 0x04, 0xa1, 0xed, 0xb9, 0xba, 0xb6, 0xac, 0xad, 0x8c, 0x18, 0xf1, 0x27, 0x5a, 0x87, 0x79, 0x37, 0x6a, 0x99, 0x01, 0xc1, 0x96, 0xe9, 0xc7, 0x5c, 0xa1, 0x5e, 0x58, 0xd6, 0x56, 0x8a, 0x9b, 0x05, 0x5d, 0x33, 0xe6, 0xdc, 0xa8, 0x65, 0x10, 0x6c, 0xb5, 0x45, 0x86, 0xe8, 0x16, 0x2c, 0x30, 0x9e, 0xe3, 0xc0, 0xa6, 0x24, 0xc9, 0x34, 0xd2, 0x66, 0x42, 0x6e, 0xd4, 0x7a, 0xc9, 0xb6, 0x13, 0x5c, 0x2e, 0xcc, 0x64, 0xb5, 0x8c, 0x2e, 0x8f, 0xac, 0x94, 0xd6, 0x1f, 0xd6, 0xf2, 0x32, 0xb4, 0x96, 0x73, 0x9e, 0x5a, 0xda, 0xa0, 0x87, 0x2e, 0x0d, 0x4e, 0x8c, 0x72, 0x90, 0xb6, 0xf2, 0x35, 0xcc, 0x76, 0x59, 0x58, 0xe4, 0x0a, 0x77, 0x86, 0x57, 0x98, 0x39, 0x8c, 0xd0, 0x38, 0x73, 0x9c, 0x5e, 0xad, 0xb8, 0x30, 0xaf, 0xb0, 0x0c, 0xcd, 0xc2, 0xc8, 0x21, 0x39, 0xe1, 0x9e, 0x2f, 0x1a, 0xec, 0x4f, 0xb4, 0x01, 0xc5, 0x23, 0xec, 0x44, 0x84, 0xfb, 0xb9, 0xb4, 0xfe, 0xff, 0x43, 0x18, 0x64, 0x08, 0xce, 0x7b, 0x85, 0xbb, 0x5a, 0xc5, 0x83, 0x05, 0x95, 0x61, 0x6f, 0x4c, 0x61, 0xf5, 0x47, 0x30, 0xf7, 0xa9, 0x87, 0xad, 0x4d, 0xec, 0x60, 0xb7, 0x41, 0x82, 0xc7, 0xb6, 0x4b, 0x43, 0x74, 0x1d, 0xa6, 0xf7, 0x70, 0xe3, 0xd0, 0xf1, 0x0e, 0xcc, 0x86, 0x17, 0xb9, 0x54, 0xa6, 0xd8, 0x94, 0x5c, 0xdc, 0x62, 0x6b, 0xe8, 0x26, 0xcc, 0x04, 0x98, 0x05, 0x83, 0x04, 0x66, 0x48, 0x1a, 0x9e, 0x6b, 0x71, 0x53, 0x34, 0x63, 0x9a, 0x2d, 0x3f, 0x23, 0xc1, 0x73, 0xbe, 0x58, 0xfd, 0x87, 0x06, 0x95, 0x67, 0x9e, 0xe3, 0xec, 0x78, 0xc1, 0x36, 0x69, 0xd8, 0x2c, 0x47, 0x99, 0x45, 0x06, 0x79, 0x1d, 0x91, 0x90, 0xa2, 0x3a, 0x8c, 0x07, 0xe2, 0x4f, 0xae, 0xa5, 0xb4, 0xbe, 0x9a, 0x3e, 0x09, 0xf6, 0x6d, 0x76, 0x88, 0x7c, 0x09, 0x46, 0xcc, 0x8f, 0x2e, 0xc1, 0xa4, 0xe5, 0xb5, 0xb0, 0xed, 0x9a, 0xb6, 0xb0, 0x65, 0xd2, 0x98, 0x10, 0x0b, 0x75, 0x8b, 0x6d, 0xfa, 0x9e, 0xe3, 0x90, 0x80, 0x6d, 0x8e, 0x88, 0x4d, 0xb1, 0x50, 0xb7, 0xd0, 0x0d, 0x28, 0xef, 0x7b, 0xc1, 0x31, 0x0e, 0x2c, 0x62, 0x99, 0xfb, 0x81, 0xd7, 0xd2, 0x47, 0x39, 0xc5, 0x74, 0x7b, 0x75, 0x27, 0xf0, 0x5a, 0xe8, 0x3d, 0x98, 0xc9, 0xd4, 0xae, 0x5e, 0xe4, 0x74, 0xe5, 0x74, 0xe9, 0x56, 0xff, 0x5c, 0x82, 0x4b, 0x4a, 0x8b, 0x43, 0xdf, 0x73, 0x43, 0x82, 0x96, 0x00, 0x58, 0xaf, 0x30, 0xa9, 0x77, 0x48, 0x44, 0x01, 0x4f, 0x19, 0x93, 0x6c, 0x65, 0x97, 0x2d, 0xa0, 0xcf, 0x01, 0xc5, 0xad, 0xcb, 0x24, 0x5f, 0x92, 0x46, 0xc4, 0x24, 0xcb, 0x40, 0xdf, 0x54, 0xba, 0xe7, 0xa5, 0x24, 0x7f, 0x18, 0x53, 0x1b, 0x73, 0xc7, 0xd9, 0x25, 0xb4, 0x03, 0xd3, 0x6d, 0xb1, 0xf4, 0xc4, 0x27, 0xdc, 0x0d, 0xa5, 0xf5, 0x6b, 0x3d, 0x25, 0xee, 0x9e, 0xf8, 0xc4, 0x98, 0x3a, 0x4e, 0x7c, 0xa1, 0x17, 0x70, 0xd1, 0x0f, 0xc8, 0x91, 0xed, 0x45, 0xa1, 0x19, 0x52, 0x1c, 0x50, 0x62, 0x99, 0xe4, 0x88, 0xb8, 0x94, 0xb9, 0x76, 0x94, 0xcb, 0xbc, 0x54, 0x13, 0x40, 0x52, 0x8b, 0x81, 0xa4, 0x56, 0x77, 0xe9, 0x9d, 0x5b, 0x2f, 0x58, 0xde, 0x19, 0x8b, 0x31, 0xf7, 0x73, 0xc1, 0xfc, 0x90, 0xf1, 0xd6, 0x2d, 0xb4, 0x02, 0xb3, 0x5d, 0xe2, 0x8a, 0x3c, 0xf3, 0xca, 0x61, 0x9a, 0x52, 0x87, 0x71, 0x4c, 0x29, 0x69, 0xf9, 0x54, 0x1f, 0xe3, 0x25, 0x11, 0x7f, 0xa2, 0x2a, 0x4c, 0xbb, 0xe4, 0x4b, 0xda, 0x11, 0x30, 0xce, 0x05, 0x94, 0xd8, 0x62, 0xcc, 0xfd, 0x01, 0xa0, 0x54, 0x7a, 0x9b, 0x4d, 0xdb, 0xa5, 0xfa, 0x04, 0x27, 0x9c, 0x4d, 0xe6, 0x38, 0xab, 0x06, 0x74, 0x17, 0xf4, 0x90, 0xda, 0x8d, 0xc3, 0x93, 0x4e, 0x28, 0x4c, 0xe2, 0xe2, 0x3d, 0x87, 0x58, 0xfa, 0xe4, 0xb2, 0xb6, 0x32, 0x61, 0x2c, 0x8a, 0xfd, 0xb6, 0xa3, 0x1f, 0x8a, 0x5d, 0x74, 0x17, 0x8a, 0x1c, 0xf8, 0x74, 0xe0, 0x3e, 0xa9, 0xf6, 0xf4, 0xf3, 0x67, 0x8c, 0xd2, 0x10, 0x0c, 0xc8, 0x80, 0x69, 0x4b, 0xe6, 0x8d, 0x69, 0xbb, 0xfb, 0x9e, 0x5e, 0xe2, 0x12, 0xbe, 0x95, 0x96, 0x20, 0x80, 0x87, 0x97, 0x78, 0x80, 0xdd, 0xd0, 0x26, 0x2e, 0x8d, 0xb3, 0xad, 0xee, 0xee, 0x7b, 0xc6, 0x94, 0x95, 0xf8, 0x42, 0xaf, 0xe0, 0x72, 0x77, 0x52, 0x99, 0x3c, 0x0d, 0x19, 0x66, 0xe9, 0x53, 0x5c, 0xc5, 0x92, 0xd2, 0xc8, 0xb8, 0x85, 0x18, 0x17, 0xbb, 0xb2, 0x2a, 0xde, 0x42, 0x35, 0x98, 0x17, 0x4e, 0x67, 0x48, 0x49, 0xcc, 0x18, 0x9d, 0xa6, 0x79, 0x7c, 0xe6, 0xf8, 0xd6, 0x73, 0xb6, 0xf3, 0x42, 0xe2, 0xd4, 0x35, 0x98, 0xda, 0x0b, 0xb0, 0xdb, 0x68, 0xca, 0x2a, 0x28, 0xf3, 0x2a, 0x28, 0x89, 0x35, 0x51, 0x07, 0x1b, 0x50, 0x0e, 0x1b, 0x4d, 0x62, 0x45, 0x0e, 0xb1, 0x4c, 0x36, 0xaa, 0xe8, 0x33, 0xdc, 0xc8, 0x4a, 0x57, 0x76, 0xed, 0xc6, 0x73, 0x8c, 0x31, 0xdd, 0xe6, 0x60, 0x6b, 0xe8, 0x3b, 0x30, 0x15, 0xe7, 0x14, 0x17, 0x30, 0xdb, 0x57, 0x40, 0x49, 0xd2, 0x73, 0xf6, 0x2f, 0x60, 0x9c, 0x45, 0xc4, 0x26, 0xa1, 0x3e, 0xc7, 0x91, 0x66, 0x33, 0xbf, 0xcf, 0xf6, 0x28, 0xf8, 0xda, 0x67, 0x42, 0x88, 0x40, 0x99, 0x58, 0x24, 0x73, 0x19, 0xf5, 0x28, 0x76, 0x4c, 0x39, 0x5e, 0x98, 0x7b, 0x27, 0x94, 0x84, 0x3a, 0xe2, 0x99, 0x38, 0xc7, 0xb7, 0x1e, 0x8b, 0x9d, 0x4d, 0xb6, 0x81, 0xbe, 0x80, 0xd9, 0x36, 0xf4, 0x99, 0x0d, 0x8e, 0x63, 0xfa, 0x3c, 0x3f, 0xd0, 0xda, 0xd0, 0x00, 0x68, 0xcc, 0xf8, 0x99, 0x91, 0xe2, 0x87, 0x30, 0xef, 0x78, 0xd8, 0x32, 0xf7, 0x24, 0x16, 0xf0, 0xb2, 0x08, 0xf5, 0x85, 0x7e, 0xf8, 0xd2, 0x85, 0x1f, 0xc6, 0x9c, 0xd3, 0x05, 0x29, 0x4f, 0x60, 0x16, 0x47, 0xd4, 0x93, 0x56, 0x8b, 0x8a, 0x7b, 0x87, 0x4b, 0xbe, 0xae, 0xcc, 0xb8, 0x8d, 0x88, 0x7a, 0xc2, 0x2e, 0xc6, 0x6f, 0x94, 0x71, 0xea, 0xbb, 0xf2, 0x0a, 0xa6, 0x92, 0x2e, 0x4d, 0xe2, 0xe3, 0xa4, 0xc0, 0xc7, 0xbb, 0x69, 0x7c, 0x1c, 0xa8, 0xf8, 0x3a, 0xb0, 0x98, 0x00, 0xad, 0x8d, 0x06, 0xb5, 0x8f, 0x6c, 0x7a, 0x72, 0x7a, 0xd0, 0x52, 0x48, 0xf8, 0x6f, 0x04, 0xad, 0xdf, 0x40, 0x1b, 0xb4, 0xd2, 0x16, 0x7f, 0xa3, 0xa0, 0x75, 0x15, 0x4a, 0x58, 0x5a, 0xd3, 0x71, 0x02, 0xc4, 0x4b, 0x75, 0x8b, 0xa1, 0x5a, 0x9b, 0x80, 0xa3, 0xda, 0x68, 0x0f, 0x54, 0x6b, 0x1f, 0x8c, 0xa3, 0x1a, 0x4e, 0x7c, 0xa1, 0x75, 0x28, 0xda, 0xae, 0x1f, 0x51, 0xee, 0x9d, 0xd2, 0xfa, 0x65, 0x75, 0x44, 0xf1, 0x09, 0xcb, 0x6d, 0x43, 0x90, 0x2a, 0x1a, 0xd4, 0xd8, 0x59, 0x1b, 0xd4, 0xf8, 0x70, 0x0d, 0x6a, 0x17, 0x2e, 0xc6, 0xf2, 0x4c, 0x56, 0x5e, 0x8e, 0x17, 0x12, 0x2e, 0xc8, 0x8b, 0x04, 0xa4, 0x95, 0xd6, 0x2f, 0x76, 0xc9, 0xda, 0x96, 0xb7, 0x42, 0x63, 0x31, 0xe6, 0xdd, 0xf5, 0xb6, 0x18, 0xe7, 0xae, 0x60, 0x44, 0xdf, 0x87, 0x45, 0xae, 0xa4, 0x5b, 0xe4, 0x64, 0x3f, 0x91, 0xf3, 0x9c, 0x31, 0x23, 0x6f, 0x07, 0xe6, 0x9a, 0x04, 0x07, 0x74, 0x8f, 0x60, 0xda, 0x16, 0x05, 0xfd, 0x44, 0xcd, 0xb6, 0x79, 0x62, 0x39, 0x09, 0xdc, 0x2f, 0xa5, 0x71, 0xff, 0x15, 0x5c, 0x49, 0x47, 0xc2, 0xf4, 0xf6, 0x4d, 0xda, 0xb4, 0x43, 0x33, 0x66, 0x98, 0xea, 0xeb, 0xd8, 0x4a, 0x2a, 0x32, 0x4f, 0xf7, 0x77, 0x9b, 0x76, 0xb8, 0x21, 0xe5, 0xd7, 0x93, 0x27, 0xb0, 0x08, 0xc5, 0xb6, 0x13, 0x72, 0x6c, 0xeb, 0x97, 0x29, 0x9d, 0x43, 0x6c, 0x0b, 0xae, 0xee, 0x31, 0xac, 0x7c, 0xba, 0x31, 0xec, 0x3d, 0x98, 0x69, 0xcb, 0x11, 0x1d, 0x83, 0xc3, 0xe3, 0xa4, 0x51, 0x8e, 0x97, 0xb7, 0xf9, 0x2a, 0xfa, 0x08, 0xc6, 0x9a, 0x04, 0x5b, 0x24, 0x90, 0xe8, 0x77, 0x49, 0xa9, 0xe9, 0x31, 0x27, 0x31, 0x24, 0x69, 0x1e, 0x1a, 0xcc, 0x9d, 0x0b, 0x1a, 0xbc, 0x59, 0x20, 0x53, 0x61, 0xcd, 0xc2, 0xa9, 0xb1, 0xa6, 0xfa, 0x97, 0x51, 0x58, 0xdc, 0xb0, 0x2c, 0xd5, 0xe5, 0x25, 0xd5, 0xbc, 0xb5, 0x4c, 0xf3, 0x7e, 0x43, 0x0d, 0xf1, 0x1e, 0x4c, 0x76, 0x86, 0xb6, 0x91, 0x41, 0x86, 0xb6, 0x09, 0x1a, 0xcf, 0x68, 0x57, 0xa1, 0xd4, 0xee, 0x16, 0x72, 0x56, 0x1f, 0x31, 0x20, 0x5e, 0xaa, 0x5b, 0xd9, 0x76, 0x22, 0x9b, 0x80, 0x2c, 0xd8, 0xe2, 0x10, 0xed, 0x84, 0x8f, 0xf6, 0x71, 0xd9, 0xde, 0x83, 0xb1, 0xd0, 0x8b, 0x82, 0x86, 0x68, 0x8f, 0xe5, 0x2c, 0x18, 0x27, 0xe6, 0x58, 0x1c, 0x1e, 0x3e, 0xe7, 0x94, 0x86, 0xe4, 0x50, 0xa0, 0xdc, 0xb8, 0x0a, 0xe5, 0x7c, 0x45, 0x46, 0x4d, 0xf4, 0x7b, 0x8c, 0x50, 0x47, 0xb5, 0x96, 0x49, 0x30, 0xf9, 0x34, 0x90, 0xc9, 0xb2, 0xca, 0x26, 0x2c, 0xa8, 0x08, 0x15, 0xa3, 0xc8, 0x42, 0x72, 0x14, 0x99, 0x4c, 0x8e, 0x19, 0xc7, 0x70, 0xa1, 0xcb, 0x06, 0x89, 0xb6, 0xaa, 0x12, 0xd1, 0xce, 0xab, 0x44, 0xaa, 0xff, 0x2c, 0xf2, 0x9c, 0x56, 0xcd, 0x36, 0xdf, 0x44, 0x4e, 0xb3, 0x9b, 0x1f, 0x0f, 0xb7, 0xd9, 0x51, 0x2d, 0x90, 0xbe, 0x2c, 0xd6, 0xb7, 0x63, 0x03, 0x52, 0xd9, 0x3f, 0x7a, 0xa6, 0xec, 0x2f, 0x0e, 0x97, 0xfd, 0x63, 0x67, 0xcf, 0xfe, 0xf1, 0x73, 0xc8, 0xfe, 0x09, 0x55, 0xf6, 0xbb, 0xa0, 0xe3, 0x44, 0x28, 0xb7, 0xed, 0xd0, 0x67, 0x59, 0xc1, 0xee, 0x7d, 0x12, 0xb1, 0xd7, 0x7b, 0x54, 0x41, 0x0e, 0xa7, 0x91, 0x2b, 0x53, 0x59, 0x6d, 0x30, 0x40, 0xb5, 0x29, 0xf2, 0xed, 0x2d, 0x56, 0xdb, 0xd7, 0x23, 0xa0, 0xe7, 0x1d, 0x16, 0x7d, 0x0f, 0x66, 0x3a, 0x03, 0x04, 0xbf, 0xad, 0xca, 0x72, 0x53, 0xe3, 0xb2, 0xbc, 0x97, 0xf1, 0x27, 0x05, 0xa3, 0x33, 0x04, 0xf2, 0xef, 0xae, 0x99, 0xae, 0x30, 0xdc, 0x4c, 0x97, 0x98, 0x72, 0x46, 0x86, 0x9d, 0x72, 0x46, 0xcf, 0x7f, 0xca, 0x29, 0x9e, 0xcf, 0x94, 0x33, 0x76, 0x6e, 0x53, 0xce, 0xb8, 0x6a, 0xca, 0x91, 0xbd, 0x54, 0x79, 0x73, 0x79, 0xb3, 0xbd, 0xf4, 0x6b, 0x0d, 0x16, 0xf8, 0x05, 0x32, 0x3e, 0x45, 0xdc, 0x49, 0xb7, 0xb2, 0xb7, 0xc4, 0xf7, 0x95, 0x87, 0x57, 0xf1, 0x0e, 0x78, 0x3f, 0x3c, 0xcb, 0x2c, 0x30, 0xd8, 0xf5, 0xb1, 0xfa, 0x6f, 0x0d, 0xde, 0xc9, 0x58, 0x28, 0xbd, 0xfa, 0x00, 0xa6, 0xf8, 0x6b, 0x95, 0x19, 0x90, 0x30, 0x72, 0xe2, 0x33, 0xf6, 0xce, 0x93, 0x12, 0xe7, 0x30, 0x38, 0x03, 0xaa, 0x43, 0x39, 0x16, 0xf0, 0x63, 0xd2, 0xa0, 0xc4, 0xea, 0x79, 0x57, 0x17, 0x77, 0x74, 0x49, 0x69, 0x4c, 0xbf, 0x4e, 0x7e, 0xa2, 0x97, 0x8a, 0x08, 0x0b, 0x7f, 0x7c, 0xd0, 0xd3, 0x1f, 0x7d, 0x83, 0xfb, 0x77, 0x0d, 0x96, 0xc5, 0x89, 0x2d, 0x6e, 0x00, 0x63, 0xdc, 0xf2, 0x5a, 0xbe, 0x43, 0x98, 0x15, 0x32, 0x46, 0x4f, 0xb3, 0x81, 0xbe, 0xad, 0x54, 0xda, 0x4f, 0xce, 0x5b, 0x08, 0xfa, 0x05, 0x18, 0xe7, 0xbc, 0x72, 0xf8, 0x9b, 0x34, 0xc6, 0xd8, 0x67, 0xdd, 0xaa, 0x5e, 0x87, 0x6b, 0x3d, 0xcc, 0x13, 0x11, 0xaf, 0xfe, 0x55, 0x83, 0xcb, 0x5b, 0x6c, 0x8c, 0x77, 0x9e, 0x46, 0x34, 0xa4, 0xd8, 0xb5, 0x6c, 0xf7, 0xe0, 0x99, 0xe7, 0x38, 0x03, 0xcd, 0x0e, 0xa9, 0xc7, 0x8c, 0x42, 0xe6, 0x31, 0xe3, 0x11, 0x94, 0xdb, 0x87, 0xea, 0x3c, 0x4e, 0x97, 0x73, 0xfa, 0x45, 0x7c, 0x32, 0xd1, 0x2f, 0x68, 0xe2, 0xeb, 0x2c, 0x03, 0x42, 0xf5, 0x2a, 0x2c, 0xe5, 0x1c, 0x4f, 0x3a, 0xe0, 0x27, 0x70, 0x61, 0x9b, 0x84, 0x8d, 0xc0, 0xde, 0x23, 0x6d, 0x76, 0x79, 0xf4, 0x9d, 0x6c, 0x0e, 0xa8, 0x13, 0x2f, 0x87, 0x7d, 0xb0, 0xd0, 0x57, 0xff, 0x54, 0x00, 0xbd, 0x5b, 0x82, 0xac, 0xc7, 0x8f, 0x61, 0x5c, 0xb8, 0x53, 0xfc, 0xa0, 0x58, 0x5a, 0xbf, 0x9a, 0xfb, 0x28, 0x45, 0x02, 0x0e, 0xf0, 0x31, 0x3d, 0xbb, 0x31, 0x75, 0xbc, 0x1f, 0x52, 0x4c, 0xa3, 0x50, 0xd6, 0xe2, 0xf5, 0x9e, 0xbe, 0x7b, 0xce, 0x49, 0x8d, 0x32, 0x4d, 0x7d, 0xbf, 0xb1, 0x6a, 0x3c, 0x53, 0x70, 0x43, 0x58, 0xe2, 0x49, 0x92, 0xd5, 0x15, 0xc6, 0x11, 0x5c, 0x84, 0x31, 0x09, 0x30, 0x22, 0x73, 0xe5, 0x57, 0x5a, 0x69, 0x61, 0x38, 0xa5, 0xbf, 0x28, 0xc0, 0x95, 0x3c, 0xad, 0x32, 0x6c, 0xaf, 0x61, 0xa9, 0xf3, 0x7e, 0xd5, 0x0e, 0x42, 0xe2, 0x27, 0x4e, 0x11, 0xcc, 0xda, 0x60, 0x9e, 0x7b, 0x42, 0x28, 0xb6, 0x30, 0xc5, 0x46, 0x25, 0x39, 0xbc, 0xa5, 0x55, 0x33, 0x95, 0xed, 0x9f, 0x17, 0x94, 0x2a, 0x0b, 0xa7, 0x53, 0x69, 0x25, 0x2e, 0x32, 0x69, 0x95, 0xd5, 0xdb, 0x70, 0xe9, 0x11, 0x69, 0xbb, 0x21, 0xdc, 0x3c, 0x11, 0xa8, 0xdd, 0xc7, 0xf7, 0xd5, 0x3f, 0x8e, 0xc2, 0x65, 0x35, 0x9f, 0xf4, 0xde, 0xcf, 0x34, 0x58, 0x54, 0x9c, 0xa5, 0x85, 0x7d, 0xe9, 0xb7, 0xa7, 0xf9, 0x08, 0xdf, 0x4b, 0x70, 0x6d, 0x3b, 0x73, 0x96, 0x27, 0xd8, 0x17, 0xa3, 0xe9, 0xbc, 0xd5, 0xbd, 0xc3, 0xcd, 0x50, 0x44, 0x91, 0x99, 0x51, 0x38, 0x93, 0x19, 0x1b, 0x99, 0x28, 0x76, 0xcc, 0xc0, 0xdd, 0x3b, 0x95, 0xaf, 0x58, 0x7b, 0x50, 0xdb, 0xad, 0x98, 0x94, 0x1f, 0xa7, 0x9f, 0xc8, 0x7b, 0x5c, 0x11, 0xf2, 0x7a, 0x4e, 0xf2, 0xa7, 0xeb, 0xaf, 0xd2, 0xc3, 0xf5, 0xdb, 0xd4, 0x5d, 0xfd, 0x5d, 0x01, 0xde, 0xfd, 0xdc, 0xb7, 0x30, 0x25, 0x79, 0xad, 0x64, 0x10, 0x80, 0x3a, 0x43, 0xa1, 0x9f, 0x1f, 0x7e, 0xa9, 0x7a, 0xe7, 0xe8, 0x79, 0x4c, 0x32, 0xef, 0xc1, 0x8d, 0x3e, 0x2e, 0x92, 0x20, 0xf7, 0xfb, 0x02, 0xdc, 0x30, 0xc8, 0x7e, 0x40, 0xc2, 0xe6, 0xff, 0xbc, 0x99, 0xe7, 0xcd, 0x15, 0xb8, 0xd9, 0xcf, 0x47, 0xc2, 0x9d, 0xeb, 0xff, 0x9a, 0x82, 0xd2, 0x13, 0x99, 0xcf, 0x1b, 0xcf, 0xea, 0xe8, 0xa7, 0x1a, 0xcc, 0x2b, 0x7e, 0x2a, 0x44, 0xb7, 0x86, 0xfc, 0x65, 0x91, 0x87, 0xa0, 0x72, 0xfb, 0x54, 0xbf, 0x47, 0x26, 0x8d, 0x48, 0x16, 0xed, 0x00, 0x46, 0x28, 0xae, 0xf0, 0x03, 0x18, 0xa1, 0xbc, 0x96, 0x1d, 0xc1, 0x4c, 0xe6, 0xf5, 0x0b, 0x7d, 0x38, 0xec, 0x63, 0x5d, 0x65, 0x6d, 0x08, 0x8e, 0x94, 0xde, 0xd4, 0xb9, 0x3f, 0x1c, 0xf6, 0xd9, 0xa2, 0x8f, 0x5e, 0xe5, 0x79, 0x7d, 0x98, 0x4e, 0xdd, 0xa4, 0x50, 0x2d, 0x5f, 0x86, 0xea, 0x52, 0x58, 0x59, 0x1d, 0x98, 0x5e, 0x6a, 0xfc, 0xb5, 0x06, 0x17, 0x73, 0xc7, 0x7a, 0x74, 0x2f, 0x5f, 0x5c, 0xbf, 0xab, 0x4a, 0xe5, 0xfe, 0xa9, 0x78, 0xa5, 0x59, 0xbf, 0xd4, 0xe0, 0x1d, 0xe5, 0xa0, 0x8d, 0xee, 0xe4, 0x8b, 0xed, 0x75, 0xf1, 0xa8, 0x7c, 0x7b, 0x68, 0x3e, 0x69, 0xca, 0x09, 0xcc, 0x66, 0x01, 0x06, 0xad, 0x0d, 0x03, 0x46, 0x42, 0xff, 0x29, 0xf0, 0x0b, 0xfd, 0x4a, 0x83, 0x45, 0xf5, 0x6c, 0x88, 0x7a, 0x1c, 0xa7, 0xe7, 0x0c, 0x5b, 0xb9, 0x3b, 0x3c, 0xa3, 0xb4, 0xe6, 0xe7, 0x1a, 0x2c, 0xa8, 0x26, 0x11, 0x74, 0x7b, 0xd8, 0xc9, 0x45, 0x58, 0x72, 0xe7, 0x74, 0x03, 0x0f, 0xfa, 0xad, 0x06, 0x4b, 0x3d, 0x71, 0x0a, 0x7d, 0x92, 0x2f, 0x79, 0x90, 0x19, 0xa0, 0xf2, 0xe0, 0xd4, 0xfc, 0xd2, 0xc4, 0x3f, 0x68, 0x70, 0xa5, 0x77, 0xf3, 0x47, 0x0f, 0x7a, 0x95, 0xc7, 0x00, 0xd0, 0x5a, 0xf9, 0xee, 0xe9, 0x05, 0x08, 0x2b, 0x37, 0xef, 0xff, 0xe0, 0xe3, 0x03, 0x9b, 0x36, 0xa3, 0xbd, 0x5a, 0xc3, 0x6b, 0xad, 0xa6, 0xfe, 0x63, 0xb5, 0x76, 0x40, 0x5c, 0xf1, 0x2f, 0xbe, 0xc9, 0xff, 0x32, 0xbe, 0x1f, 0xff, 0x7d, 0xb4, 0xb6, 0x37, 0xc6, 0x77, 0x3f, 0xfa, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7a, 0xe2, 0xa5, 0xb6, 0x93, 0x2c, 0x00, 0x00, }, // google/protobuf/duration.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a, 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0x56, 0x5c, 0x1c, 0x2e, 0x50, 0x25, 0x42, 0x12, 0x5c, 0xec, 0xc5, 0xa9, 0xc9, 0xf9, 0x79, 0x29, 0xc5, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x30, 0xae, 0x90, 0x08, 0x17, 0x6b, 0x5e, 0x62, 0x5e, 0x7e, 0xb1, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x84, 0xe3, 0xd4, 0xcc, 0xc8, 0x25, 0x9c, 0x9c, 0x9f, 0xab, 0x87, 0x66, 0xa6, 0x13, 0x2f, 0xcc, 0xc4, 0x00, 0x90, 0x48, 0x00, 0x63, 0x94, 0x21, 0x54, 0x45, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x5e, 0x7e, 0x51, 0x3a, 0xc2, 0x81, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0xfa, 0xd9, 0x79, 0xf9, 0xe5, 0x79, 0x70, 0xc7, 0x16, 0x24, 0xfd, 0x60, 0x64, 0x5c, 0xc4, 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0xa2, 0x39, 0x00, 0xaa, 0x43, 0x2f, 0x3c, 0x35, 0x27, 0xc7, 0x1b, 0xa4, 0x3e, 0x04, 0xa4, 0x35, 0x89, 0x0d, 0x6c, 0x94, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xef, 0x8a, 0xb4, 0xc3, 0xfb, 0x00, 0x00, 0x00, }, // google/protobuf/timestamp.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0xd0, 0x03, 0x0b, 0x09, 0xf1, 0x43, 0x14, 0xe8, 0xc1, 0x14, 0x28, 0x59, 0x73, 0x71, 0x86, 0xc0, 0xd4, 0x08, 0x49, 0x70, 0xb1, 0x17, 0xa7, 0x26, 0xe7, 0xe7, 0xa5, 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x30, 0x07, 0xc1, 0xb8, 0x42, 0x22, 0x5c, 0xac, 0x79, 0x89, 0x79, 0xf9, 0xc5, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xac, 0x41, 0x10, 0x8e, 0x53, 0x2b, 0x23, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0xa1, 0x4e, 0x7c, 0x70, 0x23, 0x03, 0x40, 0x42, 0x01, 0x8c, 0x51, 0x46, 0x50, 0x25, 0xe9, 0xf9, 0x39, 0x89, 0x79, 0xe9, 0x7a, 0xf9, 0x45, 0xe9, 0x48, 0x6e, 0xac, 0x2c, 0x48, 0x2d, 0xd6, 0xcf, 0xce, 0xcb, 0x2f, 0xcf, 0x43, 0xb8, 0xb7, 0x20, 0xe9, 0x07, 0x23, 0xe3, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55, 0x4c, 0x72, 0xee, 0x10, 0xdd, 0x01, 0x50, 0x2d, 0x7a, 0xe1, 0xa9, 0x39, 0x39, 0xde, 0x20, 0x0d, 0x21, 0x20, 0xbd, 0x49, 0x6c, 0x60, 0xb3, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xae, 0x65, 0xce, 0x7d, 0xff, 0x00, 0x00, 0x00, }, // google/protobuf/wrappers.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x2f, 0x4a, 0x2c, 0x28, 0x48, 0x2d, 0x2a, 0xd6, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0xca, 0x5c, 0xdc, 0x2e, 0xf9, 0xa5, 0x49, 0x39, 0xa9, 0x61, 0x89, 0x39, 0xa5, 0xa9, 0x42, 0x22, 0x5c, 0xac, 0x65, 0x20, 0x86, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x63, 0x10, 0x84, 0xa3, 0xa4, 0xc4, 0xc5, 0xe5, 0x96, 0x93, 0x9f, 0x58, 0x82, 0x45, 0x0d, 0x13, 0x92, 0x1a, 0xcf, 0xbc, 0x12, 0x33, 0x13, 0x2c, 0x6a, 0x98, 0x61, 0x6a, 0x94, 0xb9, 0xb8, 0x43, 0x71, 0x29, 0x62, 0x41, 0x35, 0xc8, 0xd8, 0x08, 0x8b, 0x1a, 0x56, 0x34, 0x83, 0xb0, 0x2a, 0xe2, 0x85, 0x29, 0x52, 0xe4, 0xe2, 0x74, 0xca, 0xcf, 0xcf, 0xc1, 0xa2, 0x84, 0x03, 0xc9, 0x9c, 0xe0, 0x92, 0xa2, 0xcc, 0xbc, 0x74, 0x2c, 0x8a, 0x38, 0x91, 0x1c, 0xe4, 0x54, 0x59, 0x92, 0x5a, 0x8c, 0x45, 0x0d, 0x0f, 0x54, 0x8d, 0x53, 0x33, 0x23, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x5a, 0xf0, 0x3a, 0xf1, 0x86, 0x43, 0xc3, 0x3f, 0x00, 0x24, 0x12, 0xc0, 0x18, 0x65, 0x08, 0x55, 0x91, 0x9e, 0x9f, 0x93, 0x98, 0x97, 0xae, 0x97, 0x5f, 0x94, 0x8e, 0x88, 0xab, 0x92, 0xca, 0x82, 0xd4, 0x62, 0xfd, 0xec, 0xbc, 0xfc, 0xf2, 0x3c, 0x78, 0xbc, 0x15, 0x24, 0xfd, 0x60, 0x64, 0x5c, 0xc4, 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0xa2, 0x39, 0x00, 0xaa, 0x43, 0x2f, 0x3c, 0x35, 0x27, 0xc7, 0x1b, 0xa4, 0x3e, 0x04, 0xa4, 0x35, 0x89, 0x0d, 0x6c, 0x94, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x3c, 0x92, 0x48, 0x30, 0x06, 0x02, 0x00, 0x00, }, // uber/cadence/api/v1/common.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdd, 0x72, 0x22, 0xc7, 0x15, 0xf6, 0xc0, 0xa2, 0x9f, 0x03, 0xbb, 0x42, 0xad, 0xfd, 0x61, 0xb5, 0x5e, 0xaf, 0x16, 0x97, 0x63, 0x79, 0x2b, 0x86, 0x88, 0x4d, 0x52, 0x2e, 0x3b, 0x4e, 0x82, 0xd0, 0x48, 0x9a, 0x5d, 0x02, 0xa4, 0x99, 0x95, 0xac, 0xa4, 0xca, 0x53, 0xcd, 0x4c, 0x83, 0x3b, 0x0c, 0xd3, 0x93, 0x99, 0x1e, 0x56, 0xf8, 0x22, 0x95, 0xcb, 0xe4, 0x26, 0x8f, 0x90, 0x8b, 0xbc, 0x48, 0x1e, 0x20, 0x97, 0x79, 0x97, 0x5c, 0xa7, 0xba, 0xa7, 0x07, 0x81, 0xc2, 0x1a, 0x5f, 0xa4, 0x7c, 0x47, 0x9f, 0xf3, 0x7d, 0xa7, 0xbf, 0xd3, 0xdd, 0xe7, 0x1c, 0x06, 0x0e, 0x92, 0x01, 0x8d, 0xea, 0x2e, 0xf1, 0x68, 0xe0, 0xd2, 0x3a, 0x09, 0x59, 0x7d, 0x7a, 0x54, 0x77, 0xf9, 0x64, 0xc2, 0x83, 0x5a, 0x18, 0x71, 0xc1, 0xd1, 0x9e, 0x44, 0xd4, 0x34, 0xa2, 0x46, 0x42, 0x56, 0x9b, 0x1e, 0xed, 0x7f, 0x30, 0xe2, 0x7c, 0xe4, 0xd3, 0xba, 0x82, 0x0c, 0x92, 0x61, 0xdd, 0x4b, 0x22, 0x22, 0x58, 0x46, 0xaa, 0xbe, 0x86, 0xdd, 0x4b, 0x1e, 0x8d, 0x87, 0x3e, 0x7f, 0x6b, 0x5e, 0x53, 0x37, 0x91, 0x2e, 0xf4, 0x0c, 0x8a, 0x6f, 0xb5, 0xd1, 0x61, 0x5e, 0xc5, 0x38, 0x30, 0x0e, 0xb7, 0x31, 0x64, 0x26, 0xcb, 0x43, 0x0f, 0x60, 0x23, 0x4a, 0x02, 0xe9, 0xcb, 0x29, 0x5f, 0x21, 0x4a, 0x02, 0xcb, 0xab, 0x56, 0xa1, 0x94, 0x05, 0xb3, 0x67, 0x21, 0x45, 0x08, 0xee, 0x04, 0x64, 0x42, 0x75, 0x00, 0xf5, 0x5b, 0x62, 0x9a, 0xae, 0x60, 0x53, 0x26, 0x66, 0xef, 0xc4, 0x3c, 0x85, 0xcd, 0x1e, 0x99, 0xf9, 0x9c, 0x78, 0xd2, 0xed, 0x11, 0x41, 0x94, 0xbb, 0x84, 0xd5, 0xef, 0xea, 0x17, 0xb0, 0x79, 0x4a, 0x98, 0x9f, 0x44, 0x14, 0x3d, 0x84, 0x8d, 0x88, 0x92, 0x98, 0x07, 0x9a, 0xaf, 0x57, 0xa8, 0x02, 0x9b, 0x1e, 0x15, 0x84, 0xf9, 0xb1, 0x52, 0x58, 0xc2, 0xd9, 0xb2, 0xfa, 0x77, 0x03, 0xee, 0xfc, 0x86, 0x4e, 0x38, 0xfa, 0x12, 0x36, 0x86, 0x8c, 0xfa, 0x5e, 0x5c, 0x31, 0x0e, 0xf2, 0x87, 0xc5, 0xc6, 0x47, 0xb5, 0x15, 0xe7, 0x57, 0x93, 0xd0, 0xda, 0xa9, 0xc2, 0x99, 0x81, 0x88, 0x66, 0x58, 0x93, 0xf6, 0x2f, 0xa1, 0xb8, 0x60, 0x46, 0x65, 0xc8, 0x8f, 0xe9, 0x4c, 0xab, 0x90, 0x3f, 0x51, 0x03, 0x0a, 0x53, 0xe2, 0x27, 0x54, 0x09, 0x28, 0x36, 0xde, 0x5f, 0x19, 0x5e, 0xa7, 0x89, 0x53, 0xe8, 0xe7, 0xb9, 0xcf, 0x8c, 0xea, 0x3f, 0x0c, 0xd8, 0x38, 0xa7, 0xc4, 0xa3, 0x11, 0xfa, 0xd5, 0x2d, 0x89, 0x1f, 0xaf, 0x8c, 0x91, 0x82, 0x7f, 0x58, 0x91, 0xff, 0x36, 0xa0, 0xdc, 0xa7, 0x24, 0x72, 0xbf, 0x69, 0x0a, 0x11, 0xb1, 0x41, 0x22, 0x68, 0x8c, 0x1c, 0xb8, 0xc7, 0x02, 0x8f, 0x5e, 0x53, 0xcf, 0x59, 0x92, 0xfd, 0xd9, 0xca, 0xa8, 0xb7, 0xe9, 0x35, 0x2b, 0xe5, 0x2e, 0xe6, 0x71, 0x97, 0x2d, 0xda, 0xf6, 0xbf, 0x06, 0xf4, 0xbf, 0xa0, 0xff, 0x63, 0x56, 0x43, 0xd8, 0x3a, 0x21, 0x82, 0x1c, 0xfb, 0x7c, 0x80, 0x4e, 0xe1, 0x2e, 0x0d, 0x5c, 0xee, 0xb1, 0x60, 0xe4, 0x88, 0x59, 0x98, 0x3e, 0xd0, 0x7b, 0x8d, 0xe7, 0x2b, 0x63, 0x99, 0x1a, 0x29, 0x5f, 0x34, 0x2e, 0xd1, 0x85, 0xd5, 0xfc, 0x01, 0xe7, 0x16, 0x1e, 0x70, 0x2f, 0x2d, 0x3a, 0x1a, 0x5d, 0xd0, 0x28, 0x66, 0x3c, 0xb0, 0x82, 0x21, 0x97, 0x40, 0x36, 0x09, 0xfd, 0xac, 0x10, 0xe4, 0x6f, 0xf4, 0x31, 0xec, 0x0c, 0x29, 0x11, 0x49, 0x44, 0x9d, 0x69, 0x0a, 0xd5, 0x05, 0x77, 0x4f, 0x9b, 0x75, 0x80, 0xea, 0x6b, 0x78, 0xd4, 0x4f, 0xc2, 0x90, 0x47, 0x82, 0x7a, 0x2d, 0x9f, 0xd1, 0x40, 0x68, 0x4f, 0x2c, 0x6b, 0x75, 0xc4, 0x9d, 0xd8, 0x1b, 0xeb, 0xc8, 0x85, 0x11, 0xef, 0x7b, 0x63, 0xf4, 0x18, 0xb6, 0xfe, 0x40, 0xa6, 0x44, 0x39, 0xd2, 0x98, 0x9b, 0x72, 0xdd, 0xf7, 0xc6, 0xd5, 0x3f, 0xe7, 0xa1, 0x88, 0xa9, 0x88, 0x66, 0x3d, 0xee, 0x33, 0x77, 0x86, 0x4e, 0xa0, 0xcc, 0x02, 0x26, 0x18, 0xf1, 0x1d, 0x16, 0x08, 0x1a, 0x4d, 0x49, 0xaa, 0xb2, 0xd8, 0x78, 0x5c, 0x4b, 0xdb, 0x4b, 0x2d, 0x6b, 0x2f, 0xb5, 0x13, 0xdd, 0x5e, 0xf0, 0x8e, 0xa6, 0x58, 0x9a, 0x81, 0xea, 0xb0, 0x37, 0x20, 0xee, 0x98, 0x0f, 0x87, 0x8e, 0xcb, 0xe9, 0x70, 0xc8, 0x5c, 0x29, 0x53, 0xed, 0x6d, 0x60, 0xa4, 0x5d, 0xad, 0x1b, 0x8f, 0xdc, 0x76, 0x42, 0xae, 0xd9, 0x24, 0x99, 0xdc, 0x6c, 0x9b, 0x5f, 0xbb, 0xad, 0xa6, 0xcc, 0xb7, 0xfd, 0xe4, 0x26, 0x0a, 0x11, 0x82, 0x4e, 0x42, 0x11, 0x57, 0xee, 0x1c, 0x18, 0x87, 0x85, 0x39, 0xb4, 0xa9, 0xcd, 0xe8, 0x4b, 0x78, 0x12, 0xf0, 0xc0, 0x89, 0x64, 0xea, 0x64, 0xe0, 0x53, 0x87, 0x46, 0x11, 0x8f, 0x9c, 0xb4, 0xa5, 0xc4, 0x95, 0xc2, 0x41, 0xfe, 0x70, 0x1b, 0x57, 0x02, 0x1e, 0xe0, 0x0c, 0x61, 0x4a, 0x00, 0x4e, 0xfd, 0xe8, 0x15, 0xec, 0xd1, 0xeb, 0x90, 0xa5, 0x42, 0x6e, 0x24, 0x6f, 0xac, 0x93, 0x8c, 0x6e, 0x58, 0x99, 0xea, 0xea, 0x04, 0x1e, 0x59, 0x31, 0xf7, 0x95, 0xf1, 0x2c, 0xe2, 0x49, 0xd8, 0x23, 0x91, 0x60, 0xaa, 0x39, 0xaf, 0x68, 0x98, 0xe8, 0x97, 0x50, 0x88, 0x05, 0x11, 0xe9, 0x83, 0xbf, 0xd7, 0x38, 0x5c, 0xf9, 0x48, 0x97, 0x03, 0xf6, 0x25, 0x1e, 0xa7, 0xb4, 0xea, 0x14, 0x9e, 0x2c, 0x7b, 0x5b, 0x3c, 0x18, 0xb2, 0x91, 0x56, 0x88, 0x2e, 0xa1, 0xcc, 0x32, 0xb7, 0x33, 0x92, 0xfe, 0xac, 0xb4, 0x7f, 0xfc, 0x3d, 0x76, 0x9a, 0x4b, 0xc7, 0x3b, 0x6c, 0xc9, 0x11, 0x57, 0xff, 0x65, 0xc0, 0x7e, 0x33, 0x9e, 0x05, 0x6e, 0x36, 0x36, 0x96, 0xf7, 0xad, 0xc0, 0x26, 0x0d, 0xe4, 0x39, 0xa7, 0x33, 0x68, 0x0b, 0x67, 0x4b, 0xd4, 0x80, 0x07, 0x61, 0x44, 0x3d, 0x3a, 0x64, 0x01, 0xf5, 0x9c, 0x3f, 0x26, 0x34, 0xa1, 0x8e, 0x3a, 0x95, 0xf4, 0x29, 0xef, 0xdd, 0x38, 0x7f, 0x2b, 0x7d, 0x1d, 0x79, 0x48, 0x4f, 0x01, 0x52, 0xa0, 0x2a, 0xe7, 0xbc, 0x02, 0x6e, 0x2b, 0x8b, 0x2a, 0xd4, 0x5f, 0x43, 0x29, 0x75, 0xbb, 0x4a, 0x83, 0x7a, 0x24, 0xc5, 0xc6, 0xd3, 0x95, 0x09, 0x66, 0x5d, 0x02, 0x17, 0x15, 0x25, 0x55, 0x5d, 0xfd, 0x4f, 0x1e, 0xde, 0x57, 0xb3, 0x8d, 0xb6, 0xfc, 0x24, 0x16, 0x34, 0xea, 0x53, 0x9f, 0xba, 0x32, 0x13, 0x5d, 0x48, 0x7d, 0xd8, 0x8a, 0x45, 0x44, 0x04, 0x1d, 0xcd, 0x74, 0x3b, 0x79, 0xb9, 0x32, 0xfc, 0xea, 0x20, 0x7d, 0x4d, 0x3d, 0xce, 0x55, 0x0c, 0x3c, 0x0f, 0x84, 0xfe, 0x62, 0xc0, 0x87, 0x44, 0x11, 0x1c, 0x37, 0x65, 0x38, 0xb1, 0x60, 0xee, 0x78, 0xe6, 0x44, 0x74, 0x24, 0x2f, 0x4c, 0xe7, 0x93, 0xf6, 0xc2, 0x9f, 0x7e, 0x8f, 0x0d, 0x15, 0x1b, 0x2b, 0x72, 0x9a, 0x99, 0xdc, 0xf1, 0xfc, 0x3d, 0xfc, 0x8c, 0x7c, 0x37, 0x0c, 0xfd, 0xcd, 0x80, 0x8f, 0x6e, 0x49, 0xa1, 0xd7, 0x82, 0x46, 0x01, 0xf1, 0x1d, 0x1a, 0x08, 0x26, 0x66, 0x99, 0x98, 0xb4, 0x8e, 0x7f, 0xbe, 0x5e, 0x8c, 0xa9, 0xf9, 0xa6, 0xa2, 0x2f, 0xc9, 0x79, 0x4e, 0xd6, 0x01, 0x11, 0x86, 0xdd, 0x4c, 0x08, 0xc9, 0x06, 0x8d, 0xbe, 0xd8, 0xd5, 0xe3, 0x5e, 0x07, 0x9b, 0x4f, 0x25, 0x5c, 0x76, 0x6f, 0x59, 0x8e, 0x77, 0x61, 0x27, 0x3b, 0x7b, 0x9d, 0x4d, 0xf5, 0x17, 0x50, 0xbe, 0x4d, 0x44, 0xf7, 0xa1, 0x10, 0xbb, 0x3c, 0xcc, 0xea, 0x34, 0x5d, 0xcc, 0x8b, 0x37, 0xb7, 0xf0, 0x6f, 0xe7, 0x15, 0x3c, 0x5b, 0x73, 0xfe, 0xe8, 0x43, 0xb8, 0xbb, 0x74, 0xa7, 0x3a, 0x68, 0x29, 0x5e, 0x80, 0x7e, 0x9e, 0xab, 0x18, 0xd5, 0xbf, 0x1a, 0xf0, 0x7c, 0xed, 0xf9, 0xa1, 0x9f, 0xc0, 0xfd, 0xdb, 0xf7, 0x32, 0x1f, 0x71, 0xdb, 0xb2, 0x1f, 0x2d, 0x72, 0x54, 0x71, 0xd4, 0x64, 0x6f, 0x5b, 0x66, 0xc8, 0x99, 0x9b, 0xa6, 0xb1, 0xbb, 0x4c, 0x78, 0x4d, 0x67, 0x4a, 0xcb, 0x57, 0xb0, 0xdb, 0x23, 0x23, 0x16, 0xa8, 0x5a, 0xee, 0x86, 0x42, 0x4d, 0xa3, 0x27, 0xb0, 0x1d, 0x92, 0x11, 0x75, 0x62, 0xf6, 0x6d, 0xba, 0x5f, 0x01, 0x6f, 0x49, 0x43, 0x9f, 0x7d, 0x4b, 0xd1, 0x8f, 0x60, 0x27, 0xa0, 0xd7, 0xc2, 0x51, 0x08, 0xc1, 0xc7, 0x34, 0xd0, 0x63, 0xf3, 0xae, 0x34, 0xf7, 0xc8, 0x88, 0xda, 0xd2, 0xf8, 0xe2, 0x2d, 0x94, 0x16, 0x27, 0x2e, 0x7a, 0x0c, 0x0f, 0xcc, 0x4e, 0xab, 0x7b, 0x62, 0x75, 0xce, 0x1c, 0xfb, 0xaa, 0x67, 0x3a, 0x56, 0xe7, 0xa2, 0xd9, 0xb6, 0x4e, 0xca, 0xef, 0xa1, 0x7d, 0x78, 0xb8, 0xec, 0xb2, 0xcf, 0xb1, 0x75, 0x6a, 0xe3, 0xcb, 0xb2, 0x81, 0x1e, 0x02, 0x5a, 0xf6, 0xbd, 0xea, 0x77, 0x3b, 0xe5, 0x1c, 0xaa, 0xc0, 0xfd, 0x65, 0x7b, 0x0f, 0x77, 0xed, 0xee, 0xcb, 0x72, 0xfe, 0xc5, 0x9f, 0x60, 0x6f, 0x45, 0x17, 0x45, 0xcf, 0xe1, 0xa9, 0xd5, 0xef, 0xb6, 0x9b, 0xb6, 0xd5, 0xed, 0x38, 0x67, 0xb8, 0xfb, 0xa6, 0xe7, 0xf4, 0xed, 0xa6, 0xbd, 0xa8, 0xe3, 0x9d, 0x90, 0x73, 0xb3, 0xd9, 0xb6, 0xcf, 0xaf, 0xca, 0xc6, 0xbb, 0x21, 0x27, 0xb8, 0x69, 0x75, 0xcc, 0x93, 0x72, 0xee, 0xc5, 0x3f, 0x0d, 0xf8, 0xe0, 0xbb, 0x9b, 0x03, 0xfa, 0x14, 0x3e, 0x69, 0xb6, 0x6c, 0xeb, 0xc2, 0x74, 0x5a, 0xed, 0x37, 0x7d, 0xdb, 0xc4, 0x4e, 0xdf, 0x6c, 0x9b, 0x2d, 0x15, 0xb4, 0x6f, 0xe3, 0xa6, 0x6d, 0x9e, 0x5d, 0x2d, 0xe8, 0x7a, 0x09, 0xf5, 0xf5, 0x70, 0x6c, 0x9e, 0xa5, 0x6b, 0xab, 0xf5, 0x5a, 0x2a, 0xfd, 0x19, 0x1c, 0xad, 0x27, 0x99, 0x5f, 0xd9, 0x26, 0xee, 0x34, 0xdb, 0x8e, 0xd9, 0xb1, 0x2d, 0xfb, 0xaa, 0x9c, 0xdb, 0xcf, 0x55, 0x8c, 0x17, 0x5f, 0x43, 0x49, 0xfe, 0x77, 0xe7, 0x53, 0x1a, 0x65, 0x57, 0x77, 0xda, 0xb4, 0xda, 0xdd, 0x0b, 0x13, 0xdf, 0xbe, 0xba, 0x47, 0xb0, 0xb7, 0xec, 0x3a, 0xed, 0xe2, 0x96, 0x59, 0x36, 0xe4, 0x9d, 0x2e, 0x3b, 0xce, 0x70, 0xb3, 0x65, 0x9e, 0xbe, 0x69, 0x97, 0x73, 0xc7, 0xbf, 0x87, 0x47, 0x2e, 0x9f, 0xac, 0xaa, 0xed, 0xe3, 0x62, 0x4b, 0x7d, 0x2d, 0xf5, 0xe4, 0x00, 0xee, 0x19, 0xbf, 0x3b, 0x1a, 0x31, 0xf1, 0x4d, 0x32, 0xa8, 0xb9, 0x7c, 0x52, 0x5f, 0xfc, 0xb6, 0xfa, 0x94, 0x79, 0x7e, 0x7d, 0xc4, 0xd3, 0x2f, 0x26, 0xfd, 0xa1, 0xf5, 0x05, 0x09, 0xd9, 0xf4, 0x68, 0xb0, 0xa1, 0x6c, 0x2f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x5e, 0xe2, 0xc9, 0x06, 0x8c, 0x0d, 0x00, 0x00, }, // uber/cadence/api/v1/query.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xdf, 0x6f, 0x93, 0x50, 0x18, 0x95, 0x9a, 0x2c, 0xd9, 0xb7, 0x55, 0xc9, 0x9d, 0xc6, 0xda, 0xec, 0x47, 0xd3, 0xed, 0x61, 0x69, 0x14, 0xec, 0xf4, 0x6d, 0x4f, 0x8c, 0x5e, 0x0d, 0x86, 0x01, 0x03, 0xda, 0xa5, 0x7b, 0x21, 0x94, 0x5e, 0x2b, 0x8e, 0x72, 0xf1, 0x5e, 0x68, 0xed, 0x3f, 0xe0, 0xbb, 0x7f, 0x8d, 0xff, 0x9e, 0x81, 0x52, 0x5b, 0x2d, 0x33, 0xbe, 0x7d, 0x9c, 0xef, 0x1c, 0xce, 0x39, 0xb9, 0xf9, 0xe0, 0x24, 0x1b, 0x11, 0x26, 0x07, 0xfe, 0x98, 0xc4, 0x01, 0x91, 0xfd, 0x24, 0x94, 0x67, 0x5d, 0xf9, 0x6b, 0x46, 0xd8, 0x42, 0x4a, 0x18, 0x4d, 0x29, 0x3a, 0xc8, 0x09, 0x52, 0x49, 0x90, 0xfc, 0x24, 0x94, 0x66, 0xdd, 0x66, 0xab, 0x4a, 0x15, 0xd0, 0xe9, 0x94, 0xc6, 0x4b, 0x59, 0xb3, 0x5d, 0xc5, 0x98, 0x53, 0x76, 0xff, 0x29, 0xa2, 0xf3, 0x25, 0xa7, 0x7d, 0x0f, 0xf5, 0xdb, 0x12, 0xb9, 0xc9, 0x1d, 0xd1, 0x11, 0x40, 0x61, 0xed, 0xa5, 0x8b, 0x84, 0x34, 0x84, 0x96, 0x70, 0xbe, 0x6b, 0xef, 0x16, 0x88, 0xbb, 0x48, 0x08, 0xba, 0x5c, 0xad, 0x7d, 0x36, 0xe1, 0x8d, 0x5a, 0x4b, 0x38, 0xdf, 0xbb, 0x38, 0x94, 0x2a, 0xf2, 0x49, 0x96, 0xbf, 0x88, 0xa8, 0x3f, 0x2e, 0xc5, 0x0a, 0x9b, 0xf0, 0xf6, 0x4f, 0x01, 0x0e, 0xfe, 0x70, 0xb3, 0x09, 0xcf, 0xa2, 0x14, 0x61, 0xd8, 0x63, 0xc5, 0xb4, 0x36, 0x7d, 0x72, 0x71, 0x56, 0xf9, 0xd7, 0x0d, 0x59, 0x9e, 0xc7, 0x06, 0xf6, 0x7b, 0x46, 0xef, 0x60, 0xc7, 0x8f, 0xf9, 0x9c, 0xb0, 0xff, 0xca, 0x55, 0x72, 0xd1, 0x29, 0xd4, 0x09, 0x63, 0x94, 0x79, 0x53, 0xc2, 0xb9, 0x3f, 0x21, 0x8d, 0xc7, 0x45, 0xe7, 0xfd, 0x02, 0xbc, 0x5e, 0x62, 0x6d, 0x02, 0xf5, 0xd2, 0xf9, 0x0b, 0x09, 0x52, 0x32, 0x46, 0x2e, 0xec, 0x07, 0x11, 0xe5, 0xc4, 0xe3, 0xa9, 0x9f, 0x66, 0xbc, 0xcc, 0xdc, 0xad, 0x74, 0x5c, 0x55, 0xc6, 0xdf, 0x48, 0x90, 0xa5, 0x21, 0x8d, 0xd5, 0x5c, 0xe9, 0x14, 0x42, 0x7b, 0x2f, 0x58, 0x7f, 0x74, 0x62, 0x78, 0xfa, 0x57, 0x41, 0x74, 0x04, 0x2f, 0x6f, 0xfa, 0xd8, 0x1e, 0x7a, 0x36, 0x76, 0xfa, 0xba, 0xeb, 0xb9, 0x43, 0x0b, 0x7b, 0x9a, 0x31, 0x50, 0x74, 0xad, 0x27, 0x3e, 0x42, 0xc7, 0xd0, 0xdc, 0x5e, 0x2b, 0x86, 0x73, 0x8b, 0x6d, 0xdc, 0x13, 0x05, 0x74, 0x08, 0x8d, 0xed, 0xfd, 0x7b, 0x45, 0xd3, 0x71, 0x4f, 0xac, 0x75, 0x7e, 0x08, 0xf0, 0x6c, 0xa3, 0x97, 0x4a, 0xe3, 0x71, 0x98, 0x07, 0x44, 0x6d, 0x38, 0x5e, 0xc9, 0x3e, 0x62, 0xd5, 0xf5, 0x54, 0xd3, 0xe8, 0x69, 0xae, 0x66, 0x1a, 0x1b, 0xd6, 0xa7, 0x70, 0xf2, 0x00, 0xc7, 0x30, 0x5d, 0xcf, 0xb4, 0xb0, 0x21, 0x0a, 0xe8, 0x0d, 0xbc, 0xfa, 0x07, 0x49, 0x35, 0xaf, 0x2d, 0x1d, 0xbb, 0xb8, 0xe7, 0xa9, 0x3a, 0x56, 0x0c, 0x7d, 0x28, 0xd6, 0x3a, 0xdf, 0x05, 0x78, 0x5e, 0x64, 0x52, 0x69, 0xcc, 0x43, 0x9e, 0x92, 0x38, 0x58, 0xe8, 0x64, 0x46, 0xa2, 0xb5, 0xa1, 0x6a, 0x1a, 0x8e, 0xe6, 0xb8, 0xd8, 0x50, 0x87, 0x9e, 0x8e, 0x07, 0x58, 0xdf, 0x48, 0x75, 0x06, 0xad, 0x87, 0x48, 0x78, 0x80, 0x0d, 0xb7, 0xaf, 0xe8, 0xa2, 0xb0, 0xee, 0xb7, 0xcd, 0x72, 0x5c, 0xdb, 0x34, 0x3e, 0x88, 0xb5, 0xab, 0x3b, 0x78, 0x11, 0xd0, 0x69, 0xd5, 0x8b, 0x5e, 0x41, 0x11, 0xd0, 0xca, 0x2f, 0xc8, 0x12, 0xee, 0xba, 0x93, 0x30, 0xfd, 0x9c, 0x8d, 0xa4, 0x80, 0x4e, 0xe5, 0xcd, 0x93, 0x7b, 0x1d, 0x8e, 0x23, 0x79, 0x42, 0xe5, 0xe2, 0xd2, 0xca, 0xfb, 0xbb, 0xf4, 0x93, 0x70, 0xd6, 0x1d, 0xed, 0x14, 0xd8, 0xdb, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x69, 0x28, 0x5b, 0xfb, 0x03, 0x00, 0x00, }, // uber/cadence/api/v1/workflow.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0xcd, 0x72, 0xdb, 0xc8, 0xb5, 0xbe, 0x20, 0x25, 0x59, 0x3a, 0xa4, 0x24, 0xa8, 0x25, 0x59, 0xf4, 0xcf, 0xd8, 0xb2, 0x66, 0xec, 0x91, 0x79, 0xc7, 0xd2, 0xc8, 0xf3, 0x3f, 0xbe, 0x73, 0x1d, 0x08, 0x84, 0x6c, 0xd8, 0x34, 0xc8, 0x34, 0x41, 0x6b, 0x34, 0x95, 0x04, 0x05, 0x91, 0x2d, 0x09, 0x31, 0x09, 0xb0, 0x80, 0xa6, 0x6d, 0xed, 0x53, 0x95, 0x6c, 0x93, 0xd5, 0x54, 0x56, 0x79, 0x80, 0x54, 0xa5, 0x52, 0x59, 0x64, 0x95, 0xca, 0x13, 0x64, 0x9b, 0x57, 0x48, 0xe5, 0x2d, 0x52, 0xdd, 0x68, 0x90, 0x00, 0x09, 0x12, 0x74, 0x52, 0x35, 0xd9, 0x09, 0xa7, 0xbf, 0xef, 0xf4, 0xe9, 0xf3, 0xd7, 0x07, 0x10, 0x61, 0xa7, 0x7f, 0x4a, 0xfc, 0xfd, 0x96, 0xdd, 0x26, 0x6e, 0x8b, 0xec, 0xdb, 0x3d, 0x67, 0xff, 0xf5, 0xc1, 0xfe, 0x1b, 0xcf, 0x7f, 0x75, 0xd6, 0xf1, 0xde, 0xec, 0xf5, 0x7c, 0x8f, 0x7a, 0x68, 0x9d, 0x61, 0xf6, 0x04, 0x66, 0xcf, 0xee, 0x39, 0x7b, 0xaf, 0x0f, 0xae, 0xdf, 0x3a, 0xf7, 0xbc, 0xf3, 0x0e, 0xd9, 0xe7, 0x90, 0xd3, 0xfe, 0xd9, 0x7e, 0xbb, 0xef, 0xdb, 0xd4, 0xf1, 0xdc, 0x90, 0x74, 0xfd, 0xf6, 0xe8, 0x3a, 0x75, 0xba, 0x24, 0xa0, 0x76, 0xb7, 0x27, 0x00, 0xdb, 0x69, 0x3b, 0xb7, 0xbc, 0x6e, 0x77, 0xa0, 0x22, 0xd5, 0x36, 0x6a, 0x07, 0xaf, 0x3a, 0x4e, 0x40, 0x43, 0xcc, 0xce, 0xf7, 0x45, 0xd8, 0x3c, 0x16, 0xe6, 0x6a, 0x6f, 0x49, 0xab, 0xcf, 0x4c, 0xd0, 0xdd, 0x33, 0x0f, 0x35, 0x01, 0x45, 0xe7, 0xb0, 0x48, 0xb4, 0x52, 0x92, 0xb6, 0xa5, 0xdd, 0xc2, 0xc3, 0x7b, 0x7b, 0x29, 0x47, 0xda, 0x1b, 0xd3, 0x83, 0xd7, 0xde, 0x8c, 0x8a, 0xd0, 0x67, 0x30, 0x47, 0x2f, 0x7b, 0xa4, 0x94, 0xe3, 0x8a, 0xee, 0x4c, 0x55, 0x64, 0x5e, 0xf6, 0x08, 0xe6, 0x70, 0xf4, 0x15, 0x40, 0x40, 0x6d, 0x9f, 0x5a, 0xcc, 0x0d, 0xa5, 0x3c, 0x27, 0x5f, 0xdf, 0x0b, 0x7d, 0xb4, 0x17, 0xf9, 0x68, 0xcf, 0x8c, 0x7c, 0x84, 0x97, 0x38, 0x9a, 0x3d, 0x33, 0x6a, 0xab, 0xe3, 0x05, 0x24, 0xa4, 0xce, 0x65, 0x53, 0x39, 0x9a, 0x53, 0x4d, 0x28, 0x86, 0xd4, 0x80, 0xda, 0xb4, 0x1f, 0x94, 0xe6, 0xb7, 0xa5, 0xdd, 0x95, 0x87, 0x07, 0xb3, 0x9d, 0x5e, 0x65, 0xcc, 0x06, 0x27, 0xe2, 0x42, 0x6b, 0xf8, 0x80, 0xee, 0xc2, 0xca, 0x85, 0x13, 0x50, 0xcf, 0xbf, 0xb4, 0x3a, 0xc4, 0x3d, 0xa7, 0x17, 0xa5, 0x85, 0x6d, 0x69, 0x37, 0x8f, 0x97, 0x85, 0xb4, 0xca, 0x85, 0xe8, 0x27, 0xb0, 0xd9, 0xb3, 0x7d, 0xe2, 0xd2, 0xa1, 0xfb, 0x2d, 0xc7, 0x3d, 0xf3, 0x4a, 0x57, 0xf8, 0x11, 0x76, 0x53, 0xad, 0xa8, 0x73, 0x46, 0x22, 0x92, 0x78, 0xbd, 0x37, 0x2e, 0x44, 0x0a, 0xac, 0x0c, 0xd5, 0x72, 0xcf, 0x2c, 0x66, 0x7a, 0x66, 0x79, 0xc0, 0xe0, 0xde, 0x79, 0x00, 0x73, 0x5d, 0xd2, 0xf5, 0x4a, 0x4b, 0x9c, 0x78, 0x2d, 0xd5, 0x9e, 0x17, 0xa4, 0xeb, 0x61, 0x0e, 0x43, 0x18, 0xd6, 0x02, 0x62, 0xfb, 0xad, 0x0b, 0xcb, 0xa6, 0xd4, 0x77, 0x4e, 0xfb, 0x94, 0x04, 0x25, 0xe0, 0xdc, 0xbb, 0xa9, 0xdc, 0x06, 0x47, 0x2b, 0x03, 0x30, 0x96, 0x83, 0x11, 0x09, 0xaa, 0xc2, 0x9a, 0xdd, 0xa7, 0x9e, 0xe5, 0x93, 0x80, 0x50, 0xab, 0xe7, 0x39, 0x2e, 0x0d, 0x4a, 0x05, 0xae, 0x73, 0x3b, 0x55, 0x27, 0x66, 0xc0, 0x3a, 0xc7, 0xe1, 0x55, 0x46, 0x8d, 0x09, 0xd0, 0x0d, 0x58, 0x62, 0xe5, 0x61, 0xb1, 0xfa, 0x28, 0x15, 0xb7, 0xa5, 0xdd, 0x25, 0xbc, 0xc8, 0x04, 0x55, 0x27, 0xa0, 0x48, 0x85, 0x95, 0xc1, 0x62, 0x18, 0x87, 0x35, 0xbe, 0xcf, 0x7b, 0xa9, 0xfb, 0x98, 0x82, 0x86, 0x8b, 0x91, 0x02, 0xee, 0xf5, 0x2d, 0xb8, 0xe2, 0x04, 0x56, 0xcb, 0xf7, 0xdc, 0xd2, 0xf2, 0xb6, 0xb4, 0xbb, 0x88, 0x17, 0x9c, 0x40, 0xf5, 0x3d, 0x17, 0x3d, 0x82, 0x42, 0xbf, 0xd7, 0xb6, 0xa9, 0xc8, 0xd2, 0x95, 0xcc, 0x58, 0x40, 0x08, 0xe7, 0x81, 0xf8, 0x39, 0xc8, 0x3d, 0xdb, 0xa7, 0x0e, 0x8f, 0x65, 0xcb, 0x73, 0xcf, 0x9c, 0xf3, 0xd2, 0xea, 0x76, 0x7e, 0xb7, 0xf0, 0xf0, 0xf1, 0x6c, 0xa9, 0xca, 0x6c, 0x63, 0xa9, 0x13, 0xaa, 0x50, 0xb9, 0x06, 0xcd, 0xa5, 0xfe, 0x25, 0x5e, 0xed, 0x25, 0xa5, 0xe8, 0x25, 0xac, 0x33, 0xf3, 0x2d, 0xef, 0x35, 0xf1, 0x3b, 0x76, 0xcf, 0xea, 0x79, 0x1d, 0xa7, 0x75, 0x59, 0x92, 0x79, 0x65, 0xa4, 0xf7, 0x05, 0x76, 0xc0, 0x5a, 0x08, 0xaf, 0x73, 0x34, 0x5e, 0x6b, 0x8d, 0x8a, 0xd0, 0x5b, 0xb8, 0x6d, 0xb7, 0xa8, 0xf3, 0x9a, 0x58, 0xad, 0x4e, 0x3f, 0xa0, 0xc4, 0xb7, 0x02, 0xd2, 0x21, 0x2d, 0x7e, 0x24, 0xb1, 0x07, 0xe2, 0x4e, 0x49, 0xaf, 0x3e, 0x85, 0x73, 0xd5, 0x90, 0xda, 0x88, 0x98, 0x62, 0xbb, 0x9b, 0xf6, 0x94, 0x55, 0xf4, 0x3e, 0x2c, 0xf3, 0x13, 0x05, 0xad, 0x0b, 0xd2, 0xee, 0x77, 0x48, 0x69, 0x9d, 0x47, 0xbe, 0xc8, 0x84, 0x0d, 0x21, 0x43, 0xc7, 0x20, 0x0f, 0xcb, 0x45, 0x74, 0x83, 0x0d, 0x7e, 0xe6, 0x8f, 0x66, 0x73, 0xb1, 0x68, 0x04, 0xab, 0x24, 0x29, 0x40, 0x26, 0x94, 0xa2, 0x8d, 0xdb, 0xd6, 0x48, 0x45, 0x6e, 0x66, 0x66, 0xc1, 0xd5, 0x01, 0x57, 0x8b, 0x97, 0xe6, 0xf5, 0x43, 0xd8, 0x48, 0x0b, 0x27, 0x92, 0x21, 0xff, 0x8a, 0x5c, 0xf2, 0x2e, 0xbe, 0x84, 0xd9, 0x9f, 0x68, 0x03, 0xe6, 0x5f, 0xdb, 0x9d, 0x7e, 0xd8, 0x90, 0x97, 0x70, 0xf8, 0xf0, 0x75, 0xee, 0x4b, 0x69, 0xe7, 0xfb, 0x1c, 0xdc, 0x1a, 0x6f, 0x6a, 0x5c, 0x99, 0xb8, 0xaa, 0xd0, 0xd7, 0xf1, 0x82, 0x91, 0x66, 0x29, 0x87, 0x61, 0x3d, 0xd9, 0xb0, 0x9d, 0xf0, 0x28, 0xeb, 0xed, 0x9e, 0x35, 0xec, 0xd4, 0x5e, 0x9f, 0x8a, 0x4b, 0xe2, 0xda, 0x98, 0x03, 0x2a, 0xc2, 0x00, 0x7c, 0x33, 0xee, 0x4e, 0x9f, 0x9a, 0x9e, 0x1a, 0xf5, 0x6e, 0xaf, 0x4f, 0xd1, 0x31, 0xdc, 0xe0, 0xe6, 0x4d, 0xd0, 0x9e, 0xcf, 0xd2, 0xbe, 0xc5, 0xd8, 0x29, 0x8a, 0x77, 0xfe, 0x26, 0xc1, 0x7a, 0x4a, 0xa7, 0x65, 0x0d, 0xa4, 0xed, 0x75, 0x6d, 0xc7, 0xb5, 0x9c, 0xb6, 0x70, 0xf2, 0x62, 0x28, 0xd0, 0xdb, 0xe8, 0x36, 0x14, 0xc4, 0xa2, 0x6b, 0x77, 0x23, 0x7f, 0x43, 0x28, 0x32, 0xec, 0x2e, 0x99, 0x70, 0xe3, 0xe6, 0xff, 0xd3, 0x1b, 0xf7, 0x0e, 0x14, 0x1d, 0xd7, 0xa1, 0x8e, 0x4d, 0x49, 0x9b, 0xd9, 0x35, 0xc7, 0x2f, 0x9b, 0xc2, 0x40, 0xa6, 0xb7, 0x77, 0x7e, 0x2d, 0xc1, 0xa6, 0xf6, 0x96, 0x12, 0xdf, 0xb5, 0x3b, 0x3f, 0xc8, 0x14, 0x30, 0x6a, 0x53, 0x6e, 0xdc, 0xa6, 0x3f, 0x2f, 0xc0, 0x7a, 0x9d, 0xb8, 0x6d, 0xc7, 0x3d, 0xe7, 0xc5, 0xed, 0xd0, 0x4b, 0x6e, 0xd1, 0x6d, 0x28, 0xd8, 0xe2, 0x79, 0xe8, 0x65, 0x88, 0x44, 0x7a, 0x1b, 0x1d, 0xc1, 0xf2, 0x00, 0x90, 0x39, 0x6a, 0x44, 0xaa, 0xf9, 0xa8, 0x51, 0xb4, 0x63, 0x4f, 0xe8, 0x31, 0xcc, 0xb3, 0x42, 0x0f, 0xa7, 0x8d, 0x95, 0x87, 0xf7, 0xd3, 0xef, 0xdb, 0xa4, 0x85, 0xac, 0xa8, 0x09, 0x0e, 0x79, 0x48, 0x87, 0xb5, 0x0b, 0x62, 0xfb, 0xf4, 0x94, 0xd8, 0xd4, 0x6a, 0x13, 0x6a, 0x3b, 0x9d, 0x40, 0xcc, 0x1f, 0x37, 0x27, 0x5c, 0xde, 0x97, 0x1d, 0xcf, 0x6e, 0x63, 0x79, 0x40, 0xab, 0x84, 0x2c, 0xf4, 0x0c, 0xd6, 0x3b, 0x76, 0x40, 0xad, 0xa1, 0x3e, 0xde, 0x20, 0xe6, 0x33, 0x1b, 0xc4, 0x1a, 0xa3, 0x3d, 0x8d, 0x58, 0xfc, 0xb6, 0x38, 0x02, 0x2e, 0x0c, 0xab, 0x82, 0xb4, 0x43, 0x4d, 0x0b, 0x99, 0x9a, 0x56, 0x19, 0xa9, 0x11, 0x72, 0xb8, 0x9e, 0x12, 0x5c, 0xb1, 0x29, 0x25, 0xdd, 0x1e, 0xe5, 0x13, 0xc9, 0x3c, 0x8e, 0x1e, 0xd1, 0x7d, 0x90, 0xbb, 0xf6, 0x5b, 0xa7, 0xdb, 0xef, 0x5a, 0x42, 0x14, 0xf0, 0xe9, 0x62, 0x1e, 0xaf, 0x0a, 0xb9, 0x22, 0xc4, 0x6c, 0x0c, 0x19, 0xb6, 0x3f, 0x6e, 0xc9, 0x52, 0xf6, 0x18, 0x32, 0x60, 0x70, 0x3b, 0x54, 0x58, 0x25, 0x6f, 0x7b, 0x4e, 0x58, 0xb3, 0xa1, 0x0e, 0xc8, 0xd4, 0xb1, 0x32, 0xa4, 0x70, 0x25, 0x8f, 0xa1, 0xc8, 0x9d, 0x72, 0x66, 0x3b, 0x9d, 0xbe, 0x4f, 0xc4, 0x0c, 0x91, 0x1e, 0xa6, 0xa3, 0x10, 0x83, 0x0b, 0x8c, 0x21, 0x1e, 0xd0, 0xc7, 0xb0, 0xc1, 0x15, 0xb0, 0x5c, 0x27, 0xbe, 0xe5, 0xb4, 0x89, 0x4b, 0x1d, 0x7a, 0x29, 0xc6, 0x08, 0xc4, 0xd6, 0x8e, 0xf9, 0x92, 0x2e, 0x56, 0xd0, 0xe7, 0xb0, 0x15, 0x85, 0x60, 0x94, 0xb4, 0xcc, 0x49, 0x9b, 0x62, 0x79, 0x84, 0x77, 0x1b, 0x0a, 0x91, 0x03, 0x58, 0x01, 0xac, 0xf0, 0xd2, 0x81, 0x48, 0xa4, 0xb7, 0x77, 0xfe, 0x94, 0x83, 0x6b, 0x22, 0x2f, 0xd5, 0x0b, 0xa7, 0xd3, 0xfe, 0x41, 0x2a, 0xfa, 0xa3, 0x98, 0x5a, 0x56, 0x75, 0xf1, 0x26, 0x27, 0xbf, 0x89, 0x0d, 0xf4, 0xbc, 0xd5, 0x8d, 0xd6, 0x7f, 0x7e, 0xac, 0xfe, 0xd9, 0xa0, 0x21, 0xc6, 0xdf, 0xb0, 0x6b, 0x8b, 0x21, 0x60, 0x6e, 0xca, 0xa0, 0x11, 0xb6, 0x64, 0xde, 0xa9, 0xa3, 0x41, 0xa3, 0x37, 0x2a, 0x42, 0x57, 0x61, 0x21, 0xec, 0xb9, 0xbc, 0x7a, 0x96, 0xb0, 0x78, 0xda, 0xf9, 0x47, 0x6e, 0xd0, 0x6f, 0x2a, 0xa4, 0xe5, 0x04, 0x91, 0xbf, 0x06, 0x6d, 0x40, 0xca, 0x6e, 0x03, 0x11, 0x31, 0xd1, 0x06, 0xc6, 0x53, 0x3c, 0xf7, 0xae, 0x29, 0xfe, 0x0d, 0x14, 0x13, 0xd5, 0x9a, 0xfd, 0xfe, 0x53, 0x08, 0xd2, 0x2b, 0x75, 0x2e, 0x59, 0xa9, 0x18, 0xb6, 0x3c, 0xdf, 0x39, 0x77, 0x5c, 0xbb, 0x63, 0x8d, 0x18, 0x99, 0xdd, 0x5b, 0x36, 0x23, 0x6a, 0x23, 0x61, 0xec, 0x48, 0x7e, 0x2e, 0x8c, 0xe5, 0xe7, 0x5f, 0x72, 0x70, 0x2d, 0x6a, 0x98, 0x55, 0xaf, 0x65, 0x77, 0x2a, 0x4e, 0xd0, 0xb3, 0x69, 0xeb, 0x62, 0xb6, 0xfe, 0xfe, 0xdf, 0xf7, 0xe7, 0xcf, 0xe0, 0x56, 0xd2, 0x02, 0xcb, 0x3b, 0xb3, 0xe8, 0x85, 0x13, 0x58, 0x71, 0x37, 0x4f, 0x57, 0x78, 0x3d, 0x61, 0x51, 0xed, 0xcc, 0xbc, 0x70, 0x02, 0xd1, 0x15, 0xd1, 0x7b, 0x00, 0x7c, 0x6e, 0xa1, 0xde, 0x2b, 0x12, 0xa6, 0x69, 0x11, 0xf3, 0x41, 0xcb, 0x64, 0x82, 0x9d, 0x67, 0x50, 0x88, 0xbf, 0xb5, 0x3c, 0x82, 0x05, 0xf1, 0xe2, 0x23, 0xf1, 0x99, 0xff, 0xfd, 0x8c, 0x17, 0x1f, 0xfe, 0x4e, 0x28, 0x28, 0x3b, 0x7f, 0xc8, 0xc1, 0x4a, 0x72, 0x09, 0x7d, 0x08, 0xab, 0xa7, 0x8e, 0x6b, 0xfb, 0x97, 0x56, 0xeb, 0x82, 0xb4, 0x5e, 0x05, 0xfd, 0xae, 0x08, 0xc2, 0x4a, 0x28, 0x56, 0x85, 0x14, 0x6d, 0xc2, 0x82, 0xdf, 0x77, 0xa3, 0xeb, 0x7b, 0x09, 0xcf, 0xfb, 0x7d, 0x36, 0xe7, 0x7c, 0x03, 0x37, 0xce, 0x1c, 0x3f, 0x60, 0x57, 0x5e, 0x58, 0x0d, 0x56, 0xcb, 0xeb, 0xf6, 0x3a, 0x24, 0x51, 0xea, 0x25, 0x0e, 0x89, 0xea, 0x45, 0x8d, 0x00, 0x9c, 0x5e, 0x6c, 0xf9, 0xc4, 0x1e, 0xc4, 0x26, 0xdb, 0x95, 0x05, 0x81, 0x17, 0x8d, 0x7c, 0x99, 0xb7, 0x76, 0xc7, 0x3d, 0x9f, 0x35, 0x8f, 0x8b, 0x11, 0x81, 0x2b, 0xb8, 0x05, 0xc0, 0xdf, 0x26, 0xa9, 0x7d, 0xda, 0x09, 0xef, 0xc5, 0x45, 0x1c, 0x93, 0x94, 0xff, 0x28, 0xc1, 0x46, 0xda, 0xad, 0x8f, 0x76, 0xe0, 0x56, 0x5d, 0x33, 0x2a, 0xba, 0xf1, 0xc4, 0x52, 0x54, 0x53, 0x7f, 0xa9, 0x9b, 0x27, 0x56, 0xc3, 0x54, 0x4c, 0xcd, 0xd2, 0x8d, 0x97, 0x4a, 0x55, 0xaf, 0xc8, 0xff, 0x83, 0x3e, 0x80, 0xed, 0x09, 0x98, 0x86, 0xfa, 0x54, 0xab, 0x34, 0xab, 0x5a, 0x45, 0x96, 0xa6, 0x68, 0x6a, 0x98, 0x0a, 0x36, 0xb5, 0x8a, 0x9c, 0x43, 0xff, 0x0b, 0x1f, 0x4e, 0xc0, 0xa8, 0x8a, 0xa1, 0x6a, 0x55, 0x0b, 0x6b, 0x3f, 0x6e, 0x6a, 0x0d, 0x06, 0xce, 0x97, 0x7f, 0x31, 0xb4, 0x39, 0xd1, 0xa2, 0xe2, 0x3b, 0x55, 0x34, 0x55, 0x6f, 0xe8, 0x35, 0x63, 0x9a, 0xcd, 0x23, 0x98, 0x09, 0x36, 0x8f, 0xa2, 0x22, 0x9b, 0xcb, 0xbf, 0xcc, 0x0d, 0x3f, 0x36, 0xe9, 0x6d, 0x4c, 0xfa, 0x83, 0xa6, 0xfc, 0x01, 0x6c, 0x1f, 0xd7, 0xf0, 0xf3, 0xa3, 0x6a, 0xed, 0xd8, 0xd2, 0x2b, 0x16, 0xd6, 0x9a, 0x0d, 0xcd, 0xaa, 0xd7, 0xaa, 0xba, 0x7a, 0x12, 0xb3, 0xe4, 0x4b, 0xf8, 0x74, 0x22, 0x4a, 0xa9, 0x32, 0x69, 0xa5, 0x59, 0xaf, 0xea, 0x2a, 0xdb, 0xf5, 0x48, 0xd1, 0xab, 0x5a, 0xc5, 0xaa, 0x19, 0xd5, 0x13, 0x59, 0x42, 0x1f, 0xc1, 0xee, 0xac, 0x4c, 0x39, 0x87, 0x1e, 0xc0, 0xfd, 0x89, 0x68, 0xac, 0x3d, 0xd3, 0x54, 0x33, 0x06, 0xcf, 0xa3, 0x03, 0x78, 0x30, 0x11, 0x6e, 0x6a, 0xf8, 0x85, 0x6e, 0x70, 0x87, 0x1e, 0x59, 0xb8, 0x69, 0x18, 0xba, 0xf1, 0x44, 0x9e, 0x2b, 0x5f, 0xc2, 0xda, 0xd8, 0x5b, 0x31, 0xba, 0x0d, 0x37, 0x54, 0x5c, 0x33, 0xac, 0xda, 0x4b, 0x0d, 0x57, 0x95, 0xfa, 0xf8, 0xf9, 0x27, 0x00, 0x1a, 0xcf, 0xf5, 0x7a, 0x3d, 0x0a, 0x42, 0x1a, 0xe0, 0xb0, 0x79, 0x74, 0xa4, 0x61, 0xab, 0x66, 0x68, 0x72, 0xae, 0xfc, 0x3b, 0x09, 0xd6, 0xc6, 0x2e, 0x4a, 0xa6, 0xba, 0xae, 0x60, 0xcd, 0x30, 0x2d, 0xb5, 0x5a, 0x4b, 0xf3, 0xfd, 0x04, 0x80, 0x72, 0xa8, 0x18, 0x95, 0x9a, 0x21, 0x4b, 0xe8, 0x1e, 0xec, 0xa4, 0x01, 0x44, 0x1a, 0x8a, 0xac, 0x94, 0x73, 0xe8, 0x0e, 0xbc, 0x97, 0x86, 0x1b, 0x38, 0x4a, 0xce, 0x97, 0xff, 0x99, 0x83, 0x9b, 0xd3, 0x3e, 0xa7, 0xb1, 0xe4, 0x1f, 0x78, 0x5c, 0xfb, 0x56, 0x53, 0x9b, 0x26, 0x4b, 0xb7, 0x50, 0x1f, 0x4b, 0xba, 0x66, 0x23, 0x66, 0x79, 0x3c, 0x9a, 0x13, 0xc0, 0x6a, 0xed, 0x45, 0xbd, 0xaa, 0x99, 0xdc, 0x87, 0x65, 0xb8, 0x97, 0x05, 0x0f, 0x73, 0x4b, 0xce, 0x25, 0xd2, 0x6a, 0x92, 0x6a, 0x7e, 0x6e, 0x56, 0x85, 0x68, 0x0f, 0xca, 0x59, 0xe8, 0x81, 0x17, 0x2a, 0xf2, 0x1c, 0xfa, 0x14, 0x3e, 0xce, 0x36, 0xdc, 0x30, 0x75, 0xa3, 0xa9, 0x55, 0x2c, 0xa5, 0x61, 0x19, 0xda, 0xb1, 0x3c, 0x3f, 0xcb, 0x71, 0x4d, 0xfd, 0x05, 0x2b, 0x8d, 0xa6, 0x29, 0x2f, 0x94, 0x7f, 0x95, 0x87, 0xad, 0x09, 0x1f, 0x2b, 0xd0, 0x5d, 0xb8, 0x93, 0xa2, 0x6a, 0xcc, 0xc1, 0x53, 0x61, 0xa2, 0x29, 0xc8, 0xd2, 0x74, 0xd8, 0xb0, 0xb1, 0x7d, 0x08, 0xef, 0x4f, 0x86, 0x0d, 0x03, 0x95, 0x4f, 0xf4, 0x8c, 0x31, 0xa0, 0x08, 0xd1, 0x1c, 0x4b, 0xcb, 0x29, 0xea, 0xa2, 0xe0, 0xcc, 0xa3, 0x5d, 0xf8, 0x60, 0x32, 0x2e, 0x16, 0x96, 0x85, 0x09, 0x61, 0x9c, 0x14, 0x90, 0x2b, 0xd3, 0x0f, 0x34, 0x0c, 0xc5, 0x62, 0xf9, 0xaf, 0x12, 0x5c, 0x55, 0x3d, 0x97, 0x3a, 0x6e, 0x9f, 0x28, 0x81, 0x41, 0xde, 0xe8, 0xe1, 0x38, 0xec, 0xf9, 0xcc, 0x77, 0x91, 0x66, 0xa1, 0xd8, 0xd2, 0x0d, 0xdd, 0xd4, 0x15, 0xb3, 0x86, 0x93, 0x91, 0x98, 0x0c, 0x63, 0x6d, 0xb9, 0xa2, 0xe1, 0x30, 0xc5, 0x27, 0xc3, 0xb0, 0x66, 0xe2, 0x13, 0x51, 0x95, 0xe1, 0x3d, 0x33, 0x19, 0xcb, 0x9b, 0x4d, 0x74, 0x0b, 0xc8, 0xf9, 0xf2, 0xef, 0x25, 0x28, 0x88, 0x6f, 0x24, 0xfc, 0x15, 0xba, 0x04, 0x1b, 0xec, 0x80, 0xb5, 0xa6, 0x69, 0x99, 0x27, 0x75, 0x2d, 0xd9, 0x4e, 0x12, 0x2b, 0x3c, 0xfe, 0x96, 0x59, 0x0b, 0x13, 0x35, 0x6c, 0x65, 0x49, 0x80, 0xd8, 0x85, 0x61, 0x38, 0x58, 0xce, 0x4d, 0xc5, 0x84, 0x7a, 0xf2, 0xe8, 0x3a, 0x5c, 0x4d, 0x60, 0x9e, 0x6a, 0x0a, 0x36, 0x0f, 0x35, 0xc5, 0x94, 0xe7, 0xca, 0xbf, 0x95, 0xe0, 0x5a, 0x74, 0x1f, 0x9a, 0x6c, 0xbc, 0x72, 0xba, 0xa4, 0x5d, 0xeb, 0x53, 0xd5, 0xee, 0x07, 0x04, 0xdd, 0x87, 0xbb, 0x83, 0x9b, 0xcc, 0x54, 0x1a, 0xcf, 0x87, 0xb1, 0xb2, 0x54, 0x85, 0xb5, 0xf8, 0xe1, 0x69, 0x32, 0xa1, 0xc2, 0x04, 0x59, 0x62, 0xd9, 0x30, 0x1d, 0x8a, 0xb5, 0x86, 0x66, 0xca, 0xb9, 0xf2, 0xdf, 0x0b, 0xb0, 0x15, 0x37, 0x8e, 0xbd, 0x68, 0x92, 0x76, 0x68, 0xda, 0x3d, 0xd8, 0x49, 0x2a, 0x11, 0xb7, 0xdd, 0xa8, 0x5d, 0x07, 0xf0, 0x60, 0x0a, 0xae, 0x69, 0x3c, 0x55, 0x8c, 0x0a, 0x7b, 0x8e, 0x40, 0xb2, 0x84, 0x1e, 0xc3, 0xa3, 0x29, 0x94, 0x43, 0xa5, 0x32, 0xf4, 0xf2, 0x60, 0xee, 0x50, 0x4c, 0x13, 0xeb, 0x87, 0x4d, 0x53, 0x6b, 0xc8, 0x39, 0xa4, 0x81, 0x92, 0xa1, 0x20, 0x79, 0x25, 0xa4, 0xaa, 0xc9, 0xa3, 0xaf, 0xe0, 0xb3, 0x2c, 0x3b, 0xc2, 0x94, 0xd1, 0x5f, 0x68, 0x38, 0x4e, 0x9d, 0x43, 0x5f, 0xc3, 0xe7, 0x19, 0x54, 0xb1, 0xf3, 0x18, 0x77, 0x1e, 0x3d, 0x82, 0x2f, 0x32, 0xad, 0x57, 0x6b, 0xb8, 0x62, 0xbd, 0x50, 0xf0, 0xf3, 0x24, 0x79, 0x01, 0xe9, 0xa0, 0x65, 0x6d, 0x2c, 0xfa, 0x97, 0x95, 0xd2, 0x11, 0x62, 0xaa, 0xae, 0xcc, 0xe0, 0x45, 0x26, 0xc8, 0x50, 0xb3, 0x88, 0x9e, 0x80, 0x3a, 0x9b, 0x2b, 0xa6, 0x2b, 0x5a, 0x42, 0xdf, 0x82, 0xf9, 0x6e, 0x51, 0xd5, 0xbe, 0x35, 0x35, 0x6c, 0x28, 0x59, 0x9a, 0x01, 0x7d, 0x03, 0x5f, 0x65, 0x3a, 0x2d, 0xd9, 0x7f, 0x62, 0xf4, 0x02, 0xfa, 0x02, 0x3e, 0x99, 0x42, 0x8f, 0xe7, 0xc8, 0x70, 0x36, 0xd4, 0x2b, 0x72, 0x11, 0x7d, 0x06, 0x07, 0x53, 0x88, 0xbc, 0x0a, 0xad, 0x86, 0xa9, 0xab, 0xcf, 0x4f, 0xc2, 0xe5, 0xaa, 0xde, 0x30, 0xe5, 0x65, 0xf4, 0x23, 0xf8, 0xbf, 0x29, 0xb4, 0xc1, 0x61, 0xd9, 0x1f, 0x1a, 0x8e, 0x95, 0x18, 0x83, 0x35, 0xb1, 0x26, 0xaf, 0xcc, 0x10, 0x93, 0x86, 0xfe, 0x24, 0xdb, 0x73, 0xab, 0x48, 0x85, 0xc7, 0x33, 0x95, 0x88, 0xfa, 0x54, 0xaf, 0x56, 0xd2, 0x95, 0xc8, 0xe8, 0x13, 0xd8, 0x9f, 0xa2, 0xe4, 0xa8, 0x86, 0x55, 0x4d, 0x0c, 0x0f, 0x83, 0x26, 0xb1, 0x86, 0x3e, 0x87, 0x87, 0xd3, 0x48, 0x8a, 0x5e, 0x65, 0x13, 0xe8, 0x28, 0x0f, 0xb1, 0x89, 0x66, 0xb6, 0xa3, 0xeb, 0x46, 0xbd, 0x69, 0x5a, 0x0d, 0xfd, 0x3b, 0x4d, 0x5e, 0x67, 0x13, 0x4d, 0x66, 0xa4, 0x22, 0x5f, 0xc9, 0x1b, 0xe3, 0xcd, 0x78, 0x6c, 0x93, 0x43, 0xdd, 0x50, 0xf0, 0x89, 0xbc, 0x99, 0x91, 0x7b, 0xe3, 0x8d, 0x2e, 0x91, 0x42, 0x57, 0x67, 0x39, 0x8e, 0xa6, 0x60, 0xf5, 0x69, 0xdc, 0xe3, 0x5b, 0xec, 0xd6, 0xb9, 0xc3, 0xbf, 0xcb, 0x8d, 0x8d, 0x5d, 0xf1, 0x16, 0x7f, 0x00, 0x0f, 0xc2, 0xb8, 0xa5, 0x64, 0xc1, 0x84, 0x6e, 0x7f, 0x08, 0xff, 0x3f, 0x1b, 0x65, 0xb0, 0xae, 0x54, 0xb1, 0xa6, 0x54, 0x4e, 0x06, 0x2f, 0x26, 0x52, 0xf9, 0x37, 0x39, 0x28, 0xab, 0xb6, 0xdb, 0x22, 0x9d, 0xe8, 0xff, 0x01, 0x53, 0xad, 0x7c, 0x04, 0x5f, 0xcc, 0x50, 0xef, 0x13, 0xec, 0x3d, 0x86, 0xc6, 0xbb, 0x92, 0x9b, 0xc6, 0x73, 0xa3, 0x76, 0x6c, 0x4c, 0x23, 0xc8, 0x12, 0x32, 0xe0, 0xd9, 0xbb, 0x2a, 0x1e, 0x73, 0xc9, 0x70, 0xd2, 0xcc, 0x71, 0xa7, 0x34, 0x9c, 0x73, 0xfe, 0xcf, 0x91, 0xd9, 0x9c, 0x22, 0xd2, 0xf8, 0xdf, 0x73, 0xca, 0xbb, 0x92, 0x67, 0x76, 0xca, 0xbb, 0x2a, 0x9e, 0xe6, 0x94, 0xc3, 0x9f, 0xc2, 0x56, 0xcb, 0xeb, 0xa6, 0x7d, 0x6b, 0x3a, 0x5c, 0x8e, 0xdc, 0x53, 0xf7, 0x3d, 0xea, 0xd5, 0xa5, 0xef, 0x0e, 0xce, 0x1d, 0x7a, 0xd1, 0x3f, 0xdd, 0x6b, 0x79, 0xdd, 0xfd, 0xf8, 0x8f, 0x52, 0x1e, 0x38, 0xed, 0xce, 0xfe, 0xb9, 0x17, 0xfe, 0xc8, 0x45, 0xfc, 0x42, 0xe5, 0x91, 0xdd, 0x73, 0x5e, 0x1f, 0x9c, 0x2e, 0x70, 0xd9, 0x27, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x6a, 0x76, 0xd7, 0x61, 0x23, 0x00, 0x00, }, // uber/cadence/api/v1/tasklist.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xdb, 0x6e, 0xdb, 0x36, 0x18, 0x9e, 0x7c, 0x68, 0x9d, 0xdf, 0x4d, 0xa2, 0xb2, 0x49, 0x63, 0xbb, 0xed, 0xe6, 0xfa, 0xa2, 0xc8, 0x8a, 0x4d, 0x46, 0xb2, 0x0d, 0x18, 0xb6, 0xa1, 0xab, 0x13, 0x1b, 0xad, 0x10, 0x27, 0x35, 0x64, 0xb5, 0x43, 0x07, 0x0c, 0x02, 0x2d, 0xb1, 0x0e, 0x67, 0x49, 0x14, 0x44, 0xca, 0xae, 0x6f, 0xf6, 0x18, 0xbb, 0xdb, 0x8b, 0xec, 0x1d, 0xf6, 0x4e, 0x03, 0x29, 0xd9, 0xf5, 0x41, 0x0d, 0xd6, 0x8b, 0xdd, 0x99, 0xff, 0xc7, 0x8f, 0xdf, 0x7f, 0xb6, 0xa0, 0x95, 0x8c, 0x48, 0xdc, 0x76, 0xb1, 0x47, 0x42, 0x97, 0xb4, 0x71, 0x44, 0xdb, 0xd3, 0x93, 0xb6, 0xc0, 0x7c, 0xe2, 0x53, 0x2e, 0x8c, 0x28, 0x66, 0x82, 0xa1, 0x7b, 0xf2, 0x8e, 0x91, 0xdd, 0x31, 0x70, 0x44, 0x8d, 0xe9, 0x49, 0xe3, 0xf3, 0x31, 0x63, 0x63, 0x9f, 0xb4, 0xd5, 0x95, 0x51, 0xf2, 0xae, 0xed, 0x25, 0x31, 0x16, 0x94, 0x85, 0x29, 0xa9, 0xf1, 0xc5, 0x26, 0x2e, 0x68, 0x40, 0xb8, 0xc0, 0x41, 0x94, 0x5d, 0xd8, 0x7a, 0x60, 0x16, 0xe3, 0x28, 0x22, 0x31, 0x4f, 0xf1, 0xd6, 0x6b, 0xa8, 0xd8, 0x98, 0x4f, 0xfa, 0x94, 0x0b, 0x84, 0xa0, 0x14, 0xe2, 0x80, 0xd4, 0xb4, 0xa6, 0x76, 0xbc, 0x63, 0xa9, 0xdf, 0xe8, 0x3b, 0x28, 0x4d, 0x68, 0xe8, 0xd5, 0x0a, 0x4d, 0xed, 0x78, 0xef, 0xf4, 0xb1, 0x91, 0xe3, 0xa4, 0xb1, 0x78, 0xe0, 0x82, 0x86, 0x9e, 0xa5, 0xae, 0xb7, 0x30, 0xe8, 0x0b, 0xeb, 0x25, 0x11, 0xd8, 0xc3, 0x02, 0xa3, 0x4b, 0x38, 0x08, 0xf0, 0x7b, 0x47, 0x86, 0xcd, 0x9d, 0x88, 0xc4, 0x0e, 0x27, 0x2e, 0x0b, 0x3d, 0x25, 0x57, 0x3d, 0x7d, 0x68, 0xa4, 0x9e, 0x1a, 0x0b, 0x4f, 0x8d, 0x2e, 0x4b, 0x46, 0x3e, 0x79, 0x83, 0xfd, 0x84, 0x58, 0x77, 0x03, 0xfc, 0x5e, 0x3e, 0xc8, 0x07, 0x24, 0x1e, 0x2a, 0x5a, 0xeb, 0x35, 0xd4, 0x17, 0x12, 0x03, 0x1c, 0x0b, 0x2a, 0xb3, 0xb2, 0xd4, 0xd2, 0xa1, 0x38, 0x21, 0xf3, 0x2c, 0x12, 0xf9, 0x13, 0x3d, 0x81, 0x7d, 0x36, 0x0b, 0x49, 0xec, 0x5c, 0x33, 0x2e, 0x1c, 0x15, 0x67, 0x41, 0xa1, 0xbb, 0xca, 0xfc, 0x92, 0x71, 0x71, 0x85, 0x03, 0xd2, 0x9a, 0xc0, 0xa1, 0xc9, 0x99, 0xaf, 0x92, 0xfc, 0x22, 0x66, 0x49, 0x74, 0x49, 0x44, 0x4c, 0x5d, 0x8e, 0xda, 0x70, 0x10, 0x92, 0x59, 0xbe, 0xfb, 0x9a, 0x75, 0x37, 0x24, 0xb3, 0x75, 0x07, 0xd1, 0x63, 0xb8, 0x13, 0x31, 0xdf, 0x27, 0xb1, 0xe3, 0xb2, 0x24, 0x14, 0x4a, 0xae, 0x68, 0x55, 0x53, 0xdb, 0xb9, 0x34, 0xb5, 0xfe, 0x2a, 0xc1, 0xde, 0x22, 0x88, 0xa1, 0xc0, 0x22, 0xe1, 0xe8, 0x2b, 0x40, 0x23, 0xec, 0x4e, 0x7c, 0x36, 0x4e, 0x69, 0xce, 0x35, 0x0d, 0x85, 0x12, 0x29, 0x5a, 0x7a, 0x86, 0x28, 0xf2, 0x4b, 0x1a, 0x0a, 0xf4, 0x08, 0x20, 0x26, 0xd8, 0x73, 0x7c, 0x32, 0x25, 0x7e, 0xa6, 0xb0, 0x23, 0x2d, 0x7d, 0x69, 0x40, 0x0f, 0x60, 0x07, 0xbb, 0x93, 0x0c, 0x2d, 0x2a, 0xb4, 0x82, 0xdd, 0x49, 0x0a, 0x3e, 0x81, 0xfd, 0x18, 0x0b, 0xb2, 0x1a, 0x4b, 0x49, 0xc5, 0xb2, 0x2b, 0xcd, 0x1f, 0xe2, 0xe8, 0xc2, 0xae, 0x0c, 0xda, 0xa1, 0x9e, 0x33, 0xf2, 0x99, 0x3b, 0xa9, 0x95, 0x55, 0xc1, 0x9a, 0x1f, 0xed, 0x05, 0xb3, 0x7b, 0x26, 0xef, 0x59, 0x55, 0x49, 0x33, 0x3d, 0x75, 0x40, 0x53, 0x38, 0xa2, 0x8b, 0xbc, 0x3a, 0x63, 0x99, 0x58, 0x27, 0x48, 0x33, 0x5b, 0xbb, 0xd5, 0x2c, 0x1e, 0x57, 0x4f, 0x9f, 0xdd, 0xd8, 0x5b, 0x69, 0x76, 0x8c, 0xdc, 0xd2, 0xf4, 0x42, 0x11, 0xcf, 0xad, 0x43, 0xfa, 0x49, 0x65, 0xbb, 0xfd, 0xb1, 0xb2, 0x1d, 0x40, 0x99, 0x04, 0x91, 0x98, 0xd7, 0x2a, 0x4d, 0xed, 0xb8, 0x62, 0xa5, 0x87, 0x86, 0x80, 0xc6, 0xc7, 0xb5, 0x73, 0xda, 0xed, 0x39, 0x94, 0xa7, 0xb2, 0x73, 0x55, 0x4d, 0xaa, 0xa7, 0x4f, 0x73, 0x83, 0xcb, 0x7d, 0xd1, 0x4a, 0x89, 0x3f, 0x14, 0xbe, 0xd7, 0x5a, 0x3f, 0x43, 0x75, 0x25, 0xa1, 0xa8, 0x0e, 0x15, 0x2e, 0x70, 0x2c, 0x1c, 0xea, 0x65, 0x1d, 0x71, 0x5b, 0x9d, 0x4d, 0x0f, 0x1d, 0xc2, 0x2d, 0x12, 0x7a, 0x12, 0x48, 0x9b, 0xa0, 0x4c, 0x42, 0xcf, 0xf4, 0x5a, 0x7f, 0x6a, 0x00, 0x03, 0xd5, 0x70, 0x66, 0xf8, 0x8e, 0xa1, 0x2e, 0xe8, 0x3e, 0xe6, 0xc2, 0xc1, 0xae, 0x4b, 0x38, 0x77, 0xe4, 0xb2, 0xc8, 0xc6, 0xaf, 0xb1, 0x35, 0x7e, 0xf6, 0x62, 0x93, 0x58, 0x7b, 0x92, 0xd3, 0x51, 0x14, 0x69, 0x44, 0x0d, 0xa8, 0x50, 0x8f, 0x84, 0x82, 0x8a, 0x79, 0x36, 0x43, 0xcb, 0x73, 0x5e, 0x53, 0x15, 0x73, 0x9a, 0xaa, 0xf5, 0xb7, 0x06, 0xf5, 0xa1, 0xa0, 0xee, 0x64, 0xde, 0x7b, 0x4f, 0xdc, 0x44, 0x26, 0xa1, 0x23, 0x44, 0x4c, 0x47, 0x89, 0x20, 0x1c, 0xbd, 0x00, 0x7d, 0xc6, 0xe2, 0x09, 0x89, 0x55, 0xdd, 0x1c, 0xb9, 0x25, 0x33, 0x3f, 0x1f, 0xdd, 0xd8, 0x25, 0xd6, 0x5e, 0x4a, 0x5b, 0xae, 0x34, 0x1b, 0xea, 0xdc, 0xbd, 0x26, 0x5e, 0xe2, 0x13, 0x47, 0x30, 0x27, 0xcd, 0x9e, 0x0c, 0x9b, 0x25, 0x22, 0x2b, 0x4d, 0x7d, 0x7b, 0xf1, 0x64, 0x3b, 0xd6, 0xba, 0xbf, 0xe0, 0xda, 0x6c, 0x28, 0x99, 0x76, 0x4a, 0x6c, 0x3d, 0x83, 0xbb, 0x5b, 0xab, 0x07, 0x7d, 0x09, 0xfa, 0x46, 0x83, 0xf3, 0x9a, 0xd6, 0x2c, 0x1e, 0xef, 0x58, 0xfb, 0xeb, 0x9d, 0xc9, 0x5b, 0xff, 0x94, 0xe0, 0x68, 0xeb, 0x81, 0x73, 0x16, 0xbe, 0xa3, 0x63, 0x54, 0x83, 0xdb, 0x53, 0x12, 0x73, 0xca, 0xc2, 0x45, 0x89, 0xb3, 0x23, 0x3a, 0x85, 0x7b, 0x61, 0x12, 0x38, 0x6a, 0xde, 0xa3, 0x05, 0x8b, 0xab, 0x28, 0xca, 0x67, 0x85, 0x9a, 0x6c, 0xe6, 0x24, 0xb0, 0x08, 0xf6, 0x96, 0x4f, 0x72, 0xf4, 0x2d, 0x1c, 0x48, 0xce, 0x2c, 0xa6, 0xb2, 0x26, 0x1f, 0x48, 0xc5, 0x25, 0x09, 0x85, 0x49, 0xf0, 0x8b, 0x84, 0x57, 0x58, 0x14, 0xf6, 0x37, 0x55, 0x4a, 0x6a, 0x46, 0x9f, 0xdf, 0x98, 0xfd, 0x8d, 0x50, 0x8c, 0x75, 0x5f, 0xd2, 0x29, 0xdd, 0x8b, 0xd7, 0x1d, 0xf4, 0x41, 0xdf, 0x72, 0xae, 0xac, 0xb4, 0x3a, 0x9f, 0xa4, 0xb5, 0x11, 0x42, 0x2a, 0xb6, 0x3f, 0x5b, 0xb7, 0x36, 0x28, 0xdc, 0xcb, 0x71, 0x6a, 0x75, 0x7c, 0xcb, 0xe9, 0xf8, 0xfe, 0xb4, 0x3e, 0xbe, 0x4f, 0xfe, 0x9b, 0x2f, 0x2b, 0xa3, 0xdb, 0xf8, 0x1d, 0x0e, 0xf2, 0x7c, 0xfa, 0x3f, 0xb4, 0x9e, 0xfe, 0x01, 0x77, 0x56, 0xff, 0x83, 0x51, 0x03, 0xee, 0xdb, 0x9d, 0xe1, 0x85, 0xd3, 0x37, 0x87, 0xb6, 0x73, 0x61, 0x5e, 0x75, 0x1d, 0xf3, 0xea, 0x4d, 0xa7, 0x6f, 0x76, 0xf5, 0xcf, 0x50, 0x1d, 0x0e, 0x37, 0xb0, 0xab, 0x57, 0xd6, 0x65, 0xa7, 0xaf, 0x6b, 0x39, 0xd0, 0xd0, 0x36, 0xcf, 0x2f, 0xde, 0xea, 0x05, 0xf4, 0x10, 0x6a, 0x1b, 0x50, 0x6f, 0xf0, 0xb2, 0x77, 0xd9, 0xb3, 0x3a, 0x7d, 0xbd, 0xf8, 0xd4, 0xfb, 0xa0, 0x6f, 0xcf, 0x23, 0xb2, 0xae, 0x6f, 0xbf, 0x1d, 0xf4, 0x56, 0xf4, 0x1f, 0xc0, 0xd1, 0x06, 0xd6, 0xed, 0x9d, 0x9b, 0x43, 0xf3, 0xd5, 0x95, 0xae, 0xe5, 0x80, 0x9d, 0x73, 0xdb, 0x7c, 0x63, 0xda, 0x6f, 0xf5, 0xc2, 0xd9, 0x6f, 0x70, 0xe4, 0xb2, 0x20, 0x2f, 0x3b, 0x67, 0xbb, 0xcb, 0xf4, 0xc8, 0x19, 0x1e, 0x68, 0xbf, 0x9e, 0x8c, 0xa9, 0xb8, 0x4e, 0x46, 0x86, 0xcb, 0x82, 0xf6, 0xea, 0xb7, 0xd7, 0xd7, 0xd4, 0xf3, 0xdb, 0x63, 0x96, 0x7e, 0x0e, 0x65, 0x1f, 0x62, 0x3f, 0xe2, 0x88, 0x4e, 0x4f, 0x46, 0xb7, 0x94, 0xed, 0x9b, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xea, 0xcc, 0x39, 0x04, 0xac, 0x09, 0x00, 0x00, }, // uber/cadence/api/v1/service_worker.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0x5b, 0x6f, 0xdb, 0xc8, 0xf5, 0x07, 0x7d, 0xf7, 0xf1, 0x4d, 0x1e, 0xe3, 0xef, 0xbf, 0xac, 0xdc, 0x1c, 0xa5, 0x49, 0xbc, 0x6d, 0x2a, 0x37, 0xde, 0x6d, 0x36, 0x9b, 0x6c, 0x8a, 0xfa, 0x12, 0x23, 0x2e, 0x36, 0x5b, 0x2f, 0xa3, 0x4d, 0x80, 0x2d, 0x10, 0x62, 0x44, 0x8e, 0xac, 0x81, 0x29, 0x8e, 0x42, 0x0e, 0xa5, 0xe8, 0xa5, 0x0f, 0x7d, 0xdc, 0xf6, 0xa1, 0x40, 0xd1, 0xed, 0x4b, 0x81, 0x3c, 0xf7, 0x03, 0xf4, 0xcb, 0xb4, 0x4f, 0x7d, 0xee, 0x67, 0x28, 0x50, 0x70, 0x66, 0x48, 0x51, 0xd2, 0x90, 0xba, 0xb4, 0x80, 0x17, 0xe8, 0x9b, 0x39, 0xfc, 0x9d, 0xc3, 0x33, 0xe7, 0x77, 0xe6, 0xcc, 0x6f, 0xc6, 0x82, 0xbd, 0xb0, 0x46, 0xfc, 0x7d, 0x1b, 0x3b, 0xc4, 0xb3, 0xc9, 0x3e, 0x6e, 0xd1, 0xfd, 0xf6, 0xc3, 0xfd, 0x80, 0xf8, 0x6d, 0x6a, 0x13, 0xab, 0xc3, 0xfc, 0x4b, 0xe2, 0x57, 0x5a, 0x3e, 0xe3, 0x0c, 0x6d, 0x45, 0xc8, 0x8a, 0x42, 0x56, 0x70, 0x8b, 0x56, 0xda, 0x0f, 0x4b, 0x37, 0x2f, 0x18, 0xbb, 0x70, 0xc9, 0xbe, 0x80, 0xd4, 0xc2, 0xfa, 0xbe, 0x13, 0xfa, 0x98, 0x53, 0xe6, 0x49, 0xa3, 0xd2, 0xad, 0xc1, 0xf7, 0x9c, 0x36, 0x49, 0xc0, 0x71, 0xb3, 0xa5, 0x00, 0x43, 0x0e, 0x3a, 0x3e, 0x6e, 0xb5, 0x88, 0x1f, 0xa8, 0xf7, 0xbb, 0xba, 0xf8, 0x6c, 0xd6, 0x6c, 0x26, 0x9f, 0x28, 0xeb, 0x10, 0x0e, 0xb1, 0x69, 0xd0, 0x0b, 0xe3, 0xb6, 0x0e, 0xd3, 0xa0, 0x01, 0x67, 0x7e, 0x37, 0x8e, 0x54, 0x07, 0x79, 0x17, 0x92, 0x04, 0xa0, 0xfd, 0x0e, 0xc7, 0xc1, 0xa5, 0x4b, 0x03, 0x9e, 0x87, 0x89, 0xb2, 0x58, 0x77, 0x59, 0x47, 0x62, 0xca, 0x7f, 0x35, 0xa0, 0x74, 0xce, 0x5c, 0xf7, 0x94, 0xf9, 0x27, 0x2a, 0xca, 0x2a, 0x0e, 0x2e, 0x4d, 0xf2, 0x2e, 0x24, 0x01, 0x47, 0xdb, 0xb0, 0xe0, 0xb0, 0x26, 0xa6, 0x5e, 0xd1, 0xd8, 0x35, 0xf6, 0x96, 0x4d, 0xf5, 0x84, 0x9e, 0xc0, 0x72, 0xf4, 0x31, 0x2b, 0xfa, 0x5a, 0x71, 0x66, 0xd7, 0xd8, 0x5b, 0x39, 0xb8, 0x51, 0xd1, 0x50, 0x52, 0x89, 0x9c, 0x7d, 0x41, 0x03, 0x6e, 0x2e, 0x71, 0xf5, 0x17, 0x2a, 0xc1, 0x12, 0x75, 0x88, 0xc7, 0x29, 0xef, 0x16, 0x67, 0x85, 0xd7, 0xe4, 0x19, 0xdd, 0x87, 0x8d, 0x1a, 0xf5, 0xb0, 0xdf, 0xb5, 0xec, 0x06, 0xb1, 0x2f, 0x83, 0xb0, 0x59, 0x9c, 0x13, 0x90, 0x75, 0x39, 0x7c, 0xac, 0x46, 0xcb, 0xff, 0x5a, 0x82, 0x6b, 0xda, 0xb8, 0x83, 0x16, 0xf3, 0x02, 0x82, 0x6e, 0x00, 0x88, 0x00, 0x39, 0xbb, 0x24, 0x32, 0xf8, 0x55, 0x53, 0x84, 0x5c, 0x8d, 0x06, 0xd0, 0xd7, 0x80, 0xe2, 0x44, 0x58, 0xe4, 0x3d, 0xb1, 0xc3, 0xa8, 0x4a, 0xd4, 0x44, 0xee, 0x69, 0x27, 0xf2, 0x46, 0xc1, 0x9f, 0xc7, 0x68, 0x73, 0xb3, 0x33, 0x38, 0x84, 0x4e, 0x61, 0x2d, 0x71, 0xcb, 0xbb, 0x2d, 0x22, 0xe6, 0xb7, 0x72, 0x70, 0x3b, 0xd7, 0x63, 0xb5, 0xdb, 0x22, 0xe6, 0x6a, 0x27, 0xf5, 0x84, 0x5e, 0xc3, 0x4e, 0xcb, 0x27, 0x6d, 0xca, 0xc2, 0xc0, 0x0a, 0x38, 0xf6, 0x39, 0x71, 0x2c, 0xd2, 0x26, 0x1e, 0xb7, 0xa8, 0x23, 0x12, 0xb2, 0x72, 0x70, 0xad, 0x22, 0x6b, 0xb5, 0x12, 0xd7, 0x6a, 0xe5, 0xcc, 0xe3, 0x8f, 0x3e, 0x79, 0x8d, 0xdd, 0x90, 0x98, 0xdb, 0xb1, 0xf5, 0x2b, 0x69, 0xfc, 0x3c, 0xb2, 0x3d, 0x73, 0xd0, 0x1e, 0x14, 0x86, 0xdc, 0xcd, 0xef, 0x1a, 0x7b, 0xb3, 0xe6, 0x7a, 0xd0, 0x8f, 0x2c, 0xc2, 0x22, 0xe6, 0x9c, 0x34, 0x5b, 0xbc, 0xb8, 0x20, 0x00, 0xf1, 0x23, 0x7a, 0x00, 0xa8, 0x86, 0xed, 0x4b, 0x97, 0x5d, 0x58, 0x36, 0x0b, 0x3d, 0x6e, 0x35, 0xa8, 0xc7, 0x8b, 0x8b, 0x02, 0x54, 0x50, 0x6f, 0x8e, 0xa3, 0x17, 0x2f, 0xa8, 0xc7, 0xd1, 0x23, 0x58, 0x54, 0x95, 0x5d, 0x5c, 0x12, 0x71, 0x5f, 0xd7, 0xe6, 0xe2, 0x85, 0xc4, 0x98, 0x31, 0x18, 0xdd, 0x83, 0x0d, 0x8f, 0xbc, 0xe7, 0x56, 0x0b, 0x5f, 0x10, 0x45, 0xe2, 0xb2, 0x20, 0x71, 0x2d, 0x1a, 0x3e, 0xc7, 0x17, 0x44, 0x12, 0xf9, 0x18, 0xe6, 0xc5, 0xb2, 0x28, 0x82, 0xf0, 0x5e, 0xce, 0xcd, 0xf4, 0x57, 0x11, 0xd2, 0x94, 0x06, 0xe8, 0x2d, 0x5c, 0x1f, 0x2e, 0x01, 0xab, 0x57, 0xd5, 0x2b, 0xe3, 0x54, 0xf5, 0xce, 0x50, 0x0d, 0xc4, 0xaf, 0xd0, 0x21, 0xac, 0x07, 0x76, 0x83, 0x38, 0xa1, 0x4b, 0x1c, 0x2b, 0x6a, 0x34, 0xc5, 0x55, 0xe1, 0xb1, 0x34, 0x44, 0x5c, 0x35, 0xee, 0x42, 0xe6, 0x5a, 0x62, 0x11, 0x8d, 0xa1, 0x67, 0xb0, 0x1a, 0xd3, 0x25, 0x1c, 0xac, 0x8d, 0x74, 0xb0, 0xa2, 0xf0, 0xc2, 0xfc, 0x0d, 0x2c, 0x46, 0x53, 0xa5, 0x24, 0x28, 0xae, 0xef, 0xce, 0xee, 0xad, 0x1c, 0x3c, 0xd3, 0x4e, 0x26, 0x67, 0x19, 0x55, 0xbe, 0x92, 0xf6, 0xcf, 0x3d, 0x1e, 0x91, 0xa3, 0xbc, 0xa1, 0x32, 0x08, 0x16, 0x7a, 0x35, 0xb4, 0x21, 0xd8, 0x5f, 0x89, 0x06, 0xe3, 0x02, 0xaa, 0xc0, 0x16, 0x67, 0x1c, 0xbb, 0x96, 0x62, 0xd4, 0xaa, 0x75, 0x39, 0x09, 0x8a, 0x05, 0x81, 0xdc, 0x14, 0xaf, 0x14, 0xe9, 0x47, 0xd1, 0x0b, 0xf4, 0x12, 0x0a, 0x38, 0xe4, 0xcc, 0xb2, 0x99, 0x57, 0xa7, 0x17, 0xb2, 0xa8, 0x36, 0xc5, 0x7c, 0xef, 0x68, 0xa3, 0x3e, 0x0c, 0x39, 0x3b, 0x16, 0xd8, 0xa8, 0xce, 0xcc, 0x75, 0xdc, 0xf7, 0x5c, 0x7a, 0x0b, 0xab, 0xe9, 0xd8, 0x51, 0x01, 0x66, 0x2f, 0x49, 0x57, 0x75, 0xb1, 0xe8, 0xcf, 0xa8, 0x72, 0xda, 0xd1, 0x62, 0x51, 0xab, 0x7e, 0xac, 0xca, 0x11, 0x06, 0x4f, 0x66, 0x1e, 0x1b, 0xe5, 0xbf, 0xcc, 0xc3, 0x1d, 0x99, 0x25, 0x27, 0x9d, 0xb8, 0x63, 0xd6, 0x6c, 0xb9, 0x84, 0x13, 0x27, 0x6e, 0xa0, 0x23, 0xfa, 0xd0, 0x53, 0x58, 0x8e, 0x37, 0x87, 0xa0, 0x38, 0x23, 0x48, 0xd2, 0x57, 0x5c, 0xfc, 0x11, 0xb3, 0x87, 0x47, 0x3f, 0x82, 0xcd, 0x5e, 0xe1, 0xda, 0xcc, 0xe3, 0xe4, 0x3d, 0x17, 0x1d, 0x67, 0xd5, 0x2c, 0x24, 0x2f, 0x8e, 0xe5, 0x78, 0x5f, 0xd7, 0x9d, 0x1b, 0xe8, 0xba, 0xbf, 0x82, 0xcd, 0x80, 0x53, 0xfb, 0xb2, 0x6b, 0x61, 0xce, 0x7d, 0x5a, 0x0b, 0x23, 0xa6, 0xe6, 0x45, 0x5a, 0x2a, 0xda, 0x68, 0x5e, 0x09, 0x74, 0x52, 0xf3, 0x87, 0x89, 0x95, 0x59, 0x90, 0x8e, 0x7a, 0x23, 0xe8, 0x53, 0x28, 0xfa, 0x84, 0x87, 0xbe, 0x67, 0x79, 0xa4, 0x63, 0xc5, 0xd1, 0x8b, 0x85, 0x26, 0x5a, 0xcb, 0x92, 0xf9, 0x7f, 0xf2, 0xfd, 0x97, 0xa4, 0x93, 0x4e, 0x25, 0x3a, 0x82, 0x9b, 0x75, 0xe6, 0xdb, 0xc4, 0xb2, 0x7d, 0x82, 0x39, 0xd1, 0x98, 0x2f, 0x0a, 0xf3, 0x92, 0x40, 0x1d, 0x0b, 0xd0, 0xa0, 0x0f, 0xcd, 0x7e, 0xb2, 0xa4, 0xdb, 0x4f, 0x10, 0x83, 0x35, 0xd1, 0x16, 0x2c, 0x9f, 0x04, 0xa1, 0xcb, 0x83, 0xe2, 0xb2, 0x20, 0xe3, 0x17, 0xda, 0xe9, 0x8f, 0x41, 0x7c, 0x45, 0x56, 0x8c, 0x74, 0x26, 0x97, 0xcf, 0xea, 0xbb, 0xd4, 0x50, 0x89, 0xc2, 0xe6, 0x10, 0x44, 0x53, 0xa5, 0x3f, 0xeb, 0xaf, 0xd2, 0xbd, 0x31, 0xaa, 0x54, 0x38, 0x4c, 0xd7, 0xea, 0x87, 0x59, 0xf8, 0x41, 0x7e, 0xc8, 0x6a, 0xd3, 0xfc, 0x1a, 0xd6, 0xfa, 0x13, 0x6c, 0x88, 0x8f, 0xfe, 0x64, 0xd2, 0xb6, 0x61, 0xae, 0x3a, 0x69, 0x12, 0x3e, 0x18, 0x70, 0x13, 0xdb, 0x9c, 0xb6, 0x29, 0xa7, 0x24, 0xb0, 0x38, 0xb3, 0x1c, 0x1a, 0xb4, 0x30, 0xb7, 0x1b, 0x96, 0xcb, 0x6c, 0xec, 0xba, 0x5d, 0x55, 0xfa, 0xdf, 0x4c, 0x91, 0x6d, 0xd5, 0xa8, 0x0e, 0x13, 0xff, 0x55, 0x76, 0xa2, 0xbc, 0x7f, 0x21, 0x9d, 0xcb, 0xec, 0x5f, 0xc3, 0xd9, 0x88, 0xd2, 0xaf, 0x61, 0x77, 0x94, 0x03, 0x0d, 0x37, 0x27, 0xfd, 0xdc, 0xe8, 0x97, 0x8a, 0xf2, 0xdb, 0x15, 0xbe, 0x62, 0xc7, 0x67, 0x5e, 0x9d, 0xa5, 0x19, 0xfa, 0xcd, 0x0c, 0xec, 0x6a, 0xa6, 0x79, 0x8a, 0xa9, 0x3b, 0x76, 0x2b, 0x39, 0x82, 0x79, 0x1b, 0x87, 0x81, 0x8c, 0x66, 0xfd, 0xe0, 0x41, 0x6e, 0x1b, 0xe9, 0x79, 0x3f, 0x8e, 0x6c, 0x4c, 0x69, 0x1a, 0xed, 0xd6, 0x0e, 0xe1, 0x98, 0xba, 0x81, 0x52, 0x2e, 0xfa, 0xdd, 0xfa, 0x1c, 0x77, 0x5d, 0x86, 0x1d, 0x33, 0x06, 0xe7, 0x36, 0x17, 0xcd, 0x12, 0x9c, 0xd7, 0x4a, 0xba, 0x3b, 0x70, 0x3b, 0x27, 0x07, 0x92, 0xe7, 0xf2, 0x3f, 0x7a, 0x7a, 0x35, 0xce, 0xec, 0x55, 0xea, 0xd5, 0x57, 0x80, 0x12, 0xbf, 0x56, 0x93, 0x70, 0xec, 0x60, 0x8e, 0x95, 0x42, 0xbb, 0x9b, 0xfb, 0x81, 0x97, 0x0a, 0x6c, 0x16, 0xf8, 0xc0, 0x48, 0xf9, 0x6f, 0x3d, 0x6d, 0xdb, 0x3f, 0xc7, 0x2b, 0xd5, 0xb6, 0xb7, 0x60, 0x45, 0x2d, 0xa1, 0x6e, 0xb4, 0xe5, 0xcb, 0x4c, 0x40, 0x3c, 0x74, 0xe6, 0x44, 0xe2, 0x37, 0x01, 0x08, 0xf1, 0x3b, 0x97, 0x23, 0x7e, 0x93, 0x89, 0x09, 0xf1, 0x8b, 0x53, 0x4f, 0xe8, 0x00, 0xe6, 0xa9, 0xd7, 0x0a, 0xb9, 0xda, 0x81, 0xf2, 0x4b, 0x50, 0x42, 0x35, 0x62, 0x6b, 0xe1, 0x3f, 0x15, 0x5b, 0x8b, 0x93, 0x89, 0xad, 0x2a, 0xec, 0xc4, 0xfe, 0xa2, 0x0e, 0x67, 0xbb, 0x2c, 0x20, 0xc2, 0x11, 0x0b, 0xb9, 0x92, 0xbe, 0x3b, 0x43, 0xbe, 0x4e, 0xd4, 0xf9, 0xd4, 0xdc, 0x8e, 0x6d, 0xab, 0xec, 0x38, 0xb2, 0xac, 0x4a, 0x43, 0xf4, 0x25, 0x6c, 0x8b, 0x8f, 0x0c, 0xbb, 0x5c, 0x1e, 0xe5, 0x72, 0x4b, 0x18, 0x0e, 0xf8, 0x3b, 0x85, 0xcd, 0x06, 0xc1, 0x3e, 0xaf, 0x11, 0xcc, 0x13, 0x57, 0x30, 0xca, 0x55, 0x21, 0xb1, 0x89, 0xfd, 0xa4, 0x8e, 0x07, 0x91, 0x4e, 0x9e, 0xef, 0x1d, 0x0f, 0xde, 0xc2, 0xcd, 0x7e, 0x26, 0x2c, 0x56, 0xb7, 0x78, 0x83, 0x06, 0x56, 0x6c, 0x30, 0x5a, 0x06, 0x97, 0xfa, 0x98, 0xf9, 0x65, 0xbd, 0xda, 0xa0, 0xc1, 0xa1, 0xf2, 0x7f, 0x96, 0x9e, 0x41, 0xdc, 0xac, 0xd6, 0xc6, 0xa8, 0x94, 0xde, 0x24, 0x4e, 0x54, 0xd7, 0x1a, 0x3a, 0xad, 0xad, 0x4f, 0x77, 0x5a, 0xbb, 0x0f, 0x1b, 0x89, 0x1f, 0xd5, 0x7d, 0x36, 0x64, 0x87, 0x8b, 0x87, 0x4f, 0x64, 0x17, 0xfa, 0x18, 0x16, 0x1a, 0x04, 0x3b, 0xc4, 0x17, 0x32, 0x38, 0x3a, 0xc3, 0x69, 0xcf, 0x42, 0x02, 0x62, 0x2a, 0xe8, 0x7f, 0x59, 0x18, 0x97, 0xbf, 0x33, 0x12, 0xe1, 0x9a, 0x6e, 0x2e, 0x93, 0x0a, 0xd7, 0x4f, 0x60, 0x41, 0x2a, 0x25, 0xd5, 0x58, 0xf2, 0x73, 0xaf, 0xb0, 0x79, 0xad, 0xb4, 0x7c, 0x2f, 0x11, 0x29, 0x19, 0x71, 0xa9, 0x1d, 0xe0, 0xb7, 0x33, 0x70, 0x3f, 0x0f, 0x78, 0xd4, 0x3d, 0x3b, 0x19, 0xb5, 0x1d, 0x5c, 0x55, 0x8b, 0xec, 0x65, 0x6d, 0x6e, 0xca, 0xac, 0xcd, 0x0f, 0x64, 0xed, 0x87, 0xb0, 0x37, 0x3a, 0x19, 0x2a, 0x73, 0x7f, 0x34, 0x12, 0x95, 0x91, 0x06, 0x4f, 0xa4, 0x32, 0x1e, 0xc1, 0x62, 0x1d, 0x53, 0x37, 0xf4, 0x49, 0x2e, 0xf1, 0xa7, 0x12, 0x63, 0xc6, 0xe0, 0x5c, 0xe6, 0x7b, 0x1b, 0xbf, 0x2e, 0x2c, 0x15, 0xfc, 0xb7, 0x33, 0xda, 0xfa, 0x90, 0xa8, 0xef, 0x33, 0xe7, 0xa9, 0x8c, 0xcd, 0x4d, 0x9b, 0xb1, 0x41, 0xd6, 0xef, 0xc3, 0xdd, 0x11, 0xb9, 0x50, 0x59, 0xfb, 0x93, 0x01, 0x65, 0x5d, 0x7d, 0x60, 0xcf, 0x26, 0x13, 0x91, 0x1e, 0x77, 0xda, 0x99, 0x69, 0x65, 0xe1, 0x20, 0xe9, 0x77, 0xf5, 0x6d, 0x28, 0x09, 0x4c, 0x4d, 0xe0, 0x77, 0x33, 0x70, 0x2f, 0x07, 0xf7, 0x3d, 0x27, 0x3e, 0xce, 0xda, 0xdc, 0xb4, 0x59, 0x1b, 0x24, 0xfe, 0x23, 0x7d, 0xef, 0xeb, 0xcb, 0x46, 0x1f, 0xf5, 0x36, 0xf3, 0xfb, 0xa0, 0x2f, 0xe2, 0x4d, 0xf0, 0x0a, 0xa9, 0x3f, 0x8f, 0xa8, 0xcf, 0x09, 0x4c, 0xc9, 0xdc, 0x8f, 0xa0, 0x60, 0x8b, 0x89, 0x59, 0xbe, 0x8c, 0x95, 0x38, 0x22, 0xbe, 0x25, 0x73, 0x43, 0x8e, 0x9b, 0xf1, 0xb0, 0xaa, 0x92, 0x4c, 0x97, 0xff, 0x6b, 0x55, 0x52, 0x8d, 0xaa, 0x64, 0x44, 0x36, 0x26, 0x4f, 0xf2, 0xdf, 0x7b, 0xdb, 0x87, 0xb8, 0x68, 0x98, 0x46, 0x36, 0xfc, 0x7c, 0x40, 0x36, 0x8c, 0x7f, 0x9f, 0x11, 0x6f, 0x86, 0xaf, 0x61, 0x4b, 0xfe, 0x23, 0xc8, 0x6a, 0x13, 0x5f, 0xdc, 0x54, 0x50, 0xaf, 0xce, 0xd4, 0x71, 0x35, 0x9b, 0x28, 0xe2, 0xbf, 0x96, 0x70, 0x71, 0xf4, 0xde, 0xec, 0x0c, 0x0e, 0xa5, 0x36, 0x21, 0xdd, 0xe4, 0x62, 0xed, 0x61, 0x40, 0xc9, 0x24, 0x01, 0xe1, 0xf2, 0x02, 0x2c, 0x39, 0x2c, 0x5e, 0x49, 0x6d, 0x95, 0x6f, 0xc0, 0x35, 0x6d, 0x30, 0x2a, 0x58, 0x1f, 0xd6, 0xfb, 0xb5, 0x20, 0x7a, 0x00, 0x88, 0x78, 0xb8, 0xe6, 0x12, 0x2b, 0xa5, 0x28, 0x15, 0xdd, 0x05, 0xf9, 0xa6, 0x67, 0x81, 0x0e, 0x60, 0xbb, 0xc5, 0x5c, 0x97, 0xf8, 0x56, 0x07, 0x53, 0x79, 0x5a, 0xb0, 0xa8, 0x67, 0x35, 0x65, 0x27, 0x98, 0x35, 0x91, 0x7c, 0xfb, 0x06, 0x53, 0x71, 0x2c, 0x38, 0xf3, 0x5e, 0x06, 0x07, 0xff, 0xdc, 0x80, 0x65, 0x99, 0xee, 0xc3, 0xf3, 0x33, 0xf4, 0x1e, 0xb6, 0x34, 0xb7, 0x44, 0x68, 0x7f, 0xfc, 0xfb, 0x24, 0x91, 0xd7, 0xd2, 0xc4, 0x17, 0x50, 0xe8, 0x0f, 0x06, 0x5c, 0xcf, 0xbb, 0x37, 0x42, 0x8f, 0xa7, 0xbd, 0xd8, 0x2b, 0x7d, 0x36, 0xf5, 0x25, 0x15, 0xfa, 0xd6, 0x80, 0x9d, 0xcc, 0x2b, 0x0e, 0xf4, 0xd3, 0x71, 0x1d, 0xf7, 0x09, 0xb6, 0xd2, 0xa3, 0x49, 0xcd, 0x54, 0x30, 0x3d, 0x72, 0xd2, 0x5d, 0x22, 0x9f, 0x1c, 0xcd, 0x95, 0x4b, 0x3e, 0x39, 0xda, 0xfb, 0x8b, 0x14, 0x39, 0x5a, 0xd1, 0x9a, 0x4f, 0x4e, 0xde, 0xa9, 0x25, 0x9f, 0x9c, 0xdc, 0x73, 0x05, 0xfa, 0xa0, 0x57, 0xc7, 0x7d, 0x52, 0x1a, 0x7d, 0x3e, 0xb1, 0xff, 0xd4, 0xde, 0x53, 0x7a, 0x36, 0xa5, 0xf5, 0x70, 0xf9, 0x0c, 0xcb, 0xbe, 0xfc, 0xf2, 0xc9, 0xd4, 0xfb, 0xf9, 0xe5, 0x93, 0xad, 0xc7, 0xd1, 0x77, 0x06, 0xdc, 0xc8, 0xd5, 0xa0, 0xe8, 0xb3, 0xc9, 0x3c, 0xa7, 0x13, 0xf5, 0x64, 0x1a, 0x53, 0x15, 0xd8, 0xef, 0x0d, 0xd1, 0x16, 0xb3, 0x34, 0x12, 0xfa, 0x74, 0x6c, 0x12, 0xfa, 0x45, 0x72, 0xe9, 0xf1, 0xe4, 0x86, 0x2a, 0xa4, 0x3f, 0x1b, 0x70, 0x6b, 0x84, 0x6c, 0x43, 0x4f, 0x27, 0xf5, 0x9e, 0xce, 0xd7, 0xe7, 0xd3, 0x19, 0xf7, 0x65, 0x2c, 0x53, 0x2f, 0x64, 0x66, 0x6c, 0x94, 0xb6, 0xcc, 0xcc, 0xd8, 0x68, 0xed, 0x27, 0x33, 0x96, 0x2b, 0x61, 0x32, 0x33, 0x36, 0x8e, 0x0c, 0xcc, 0xcc, 0xd8, 0x78, 0xaa, 0x29, 0xb5, 0x12, 0x87, 0xd5, 0x42, 0xfe, 0x4a, 0xcc, 0x94, 0x4e, 0xf9, 0x2b, 0x31, 0x5b, 0x94, 0x44, 0x8d, 0x5c, 0x23, 0x03, 0x32, 0x1a, 0x79, 0xb6, 0x7a, 0xc9, 0x68, 0xe4, 0x39, 0x0a, 0xe3, 0xa8, 0x06, 0xff, 0x6f, 0xb3, 0xa6, 0xce, 0xec, 0x08, 0x49, 0x15, 0xf0, 0x4a, 0xfe, 0x76, 0xe7, 0xdc, 0x67, 0x9c, 0x9d, 0x1b, 0xdf, 0x3c, 0xbc, 0xa0, 0xbc, 0x11, 0xd6, 0x2a, 0x36, 0x6b, 0xee, 0xa7, 0x7f, 0x9c, 0xf2, 0x63, 0xea, 0xb8, 0xfb, 0x17, 0x4c, 0xfe, 0xee, 0x46, 0xfd, 0x52, 0xe5, 0x29, 0x6e, 0xd1, 0xf6, 0xc3, 0xda, 0x82, 0x18, 0xfb, 0xf8, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x83, 0x33, 0x80, 0xed, 0x1b, 0x24, 0x00, 0x00, }, // uber/cadence/api/v1/decision.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x59, 0xdf, 0x6e, 0xd4, 0xc6, 0x17, 0xfe, 0x39, 0x7f, 0x36, 0xbb, 0x27, 0x1b, 0x20, 0x13, 0x08, 0x09, 0x04, 0x12, 0xf6, 0xa7, 0x42, 0x21, 0xca, 0x6e, 0x12, 0x28, 0x42, 0x80, 0x50, 0xc9, 0x42, 0x44, 0x24, 0x08, 0x91, 0x13, 0x40, 0xaa, 0x54, 0x59, 0x93, 0xf1, 0x24, 0x99, 0xc6, 0xeb, 0xd9, 0x8e, 0xc7, 0x09, 0x5b, 0xa9, 0x12, 0x57, 0x6d, 0x6f, 0xfa, 0x00, 0x95, 0x7a, 0xd5, 0x9b, 0xb6, 0x37, 0xed, 0x6d, 0xab, 0x5e, 0xf5, 0x11, 0xfa, 0x2c, 0x7d, 0x81, 0xca, 0xe3, 0xb1, 0x77, 0xb3, 0xf1, 0x7a, 0xed, 0x40, 0xb9, 0xe8, 0x5d, 0x3c, 0x3e, 0xe7, 0x9b, 0xcf, 0x73, 0xce, 0x7e, 0xe7, 0xb3, 0x03, 0x15, 0x7f, 0x9b, 0x8a, 0x1a, 0xc1, 0x36, 0x75, 0x09, 0xad, 0xe1, 0x26, 0xab, 0x1d, 0x2c, 0xd5, 0x6c, 0x4a, 0x98, 0xc7, 0xb8, 0x5b, 0x6d, 0x0a, 0x2e, 0x39, 0x9a, 0x08, 0x62, 0xaa, 0x3a, 0xa6, 0x8a, 0x9b, 0xac, 0x7a, 0xb0, 0x74, 0xe1, 0xf2, 0x2e, 0xe7, 0xbb, 0x0e, 0xad, 0xa9, 0x90, 0x6d, 0x7f, 0xa7, 0x66, 0xfb, 0x02, 0xcb, 0x38, 0xe9, 0xc2, 0x5c, 0x12, 0x30, 0xe1, 0x8d, 0x46, 0x1c, 0x91, 0xb8, 0xb5, 0xc4, 0xde, 0xbe, 0xc3, 0x3c, 0x99, 0x16, 0x73, 0xc8, 0xc5, 0xfe, 0x8e, 0xc3, 0x0f, 0xc3, 0x98, 0xca, 0x37, 0xe3, 0x50, 0x7c, 0xa4, 0x19, 0xa3, 0xef, 0x0c, 0xb8, 0xe1, 0x91, 0x3d, 0x6a, 0xfb, 0x0e, 0xb5, 0x30, 0x91, 0xec, 0x80, 0xc9, 0x96, 0x15, 0xa0, 0x5a, 0xd1, 0x53, 0x59, 0x58, 0x4a, 0xc1, 0xb6, 0x7d, 0x49, 0xbd, 0x29, 0x63, 0xce, 0xf8, 0x70, 0x74, 0xf9, 0x5e, 0x35, 0xe1, 0x09, 0xab, 0x9b, 0x1a, 0xe6, 0xa1, 0x46, 0xd9, 0xc2, 0xde, 0x7e, 0xb4, 0xcf, 0xc3, 0x18, 0xe2, 0xc9, 0xff, 0xcc, 0xab, 0x5e, 0xa6, 0x48, 0xf4, 0x05, 0xcc, 0x7a, 0x12, 0x0b, 0x69, 0x49, 0xd6, 0xa0, 0x22, 0x91, 0xcf, 0x80, 0xe2, 0xb3, 0x94, 0xcc, 0x27, 0xc8, 0xdd, 0x0a, 0x52, 0x13, 0x59, 0xcc, 0x78, 0x29, 0xf7, 0xd1, 0x4f, 0x06, 0x04, 0xa7, 0xdf, 0x74, 0xa8, 0xa4, 0x56, 0x74, 0x80, 0x16, 0x7d, 0x4d, 0x89, 0x1f, 0x14, 0x2d, 0x91, 0xcc, 0xa0, 0x22, 0xf3, 0x71, 0x22, 0x99, 0xba, 0xc6, 0x7a, 0xa5, 0xa1, 0x1e, 0x47, 0x48, 0x89, 0xdc, 0xe6, 0x49, 0xf6, 0x70, 0xf4, 0xbd, 0x01, 0xf3, 0x3b, 0x98, 0x39, 0x59, 0x69, 0x0e, 0x29, 0x9a, 0xf7, 0x13, 0x69, 0xae, 0x62, 0xe6, 0x64, 0xa3, 0x78, 0x6d, 0x27, 0x5b, 0x28, 0xfa, 0xd9, 0x80, 0x45, 0x41, 0x3f, 0xf7, 0xa9, 0x27, 0x2d, 0x82, 0x5d, 0x42, 0x9d, 0x0c, 0x7d, 0x36, 0x9c, 0x72, 0x94, 0x66, 0x08, 0x56, 0x57, 0x58, 0x7d, 0x9b, 0x6d, 0x5e, 0x64, 0x0f, 0x47, 0x5f, 0xc2, 0x9c, 0xa6, 0xd8, 0xbb, 0xe5, 0x0a, 0x8a, 0xda, 0x72, 0x72, 0x95, 0x55, 0x72, 0xef, 0x9e, 0xbb, 0x44, 0xd2, 0x02, 0xd0, 0x0f, 0x06, 0x2c, 0xe8, 0xfd, 0x33, 0xd6, 0x72, 0x44, 0x91, 0x79, 0x90, 0x42, 0x26, 0x5b, 0x35, 0xaf, 0x93, 0xac, 0xc1, 0xe8, 0x2f, 0x03, 0x1e, 0x74, 0xd5, 0x93, 0xbe, 0x96, 0x54, 0xb8, 0x38, 0x33, 0xeb, 0xa2, 0x62, 0xfd, 0xac, 0x7f, 0x75, 0x1f, 0x6b, 0xe0, 0x6c, 0x0f, 0x71, 0x47, 0x9c, 0x30, 0x17, 0xbd, 0x31, 0xe0, 0x8a, 0xa0, 0x84, 0x0b, 0xdb, 0x6a, 0x60, 0xb1, 0xdf, 0xa3, 0xf2, 0x25, 0x45, 0xfb, 0x66, 0x0f, 0xda, 0x41, 0xf6, 0x33, 0x95, 0x9c, 0x48, 0xee, 0xb2, 0x48, 0x8d, 0x40, 0xbf, 0x1b, 0x70, 0x9b, 0x70, 0x57, 0x32, 0xd7, 0xa7, 0x16, 0xf6, 0x2c, 0x97, 0x1e, 0x66, 0x3d, 0x4e, 0x50, 0xbc, 0x1e, 0xf7, 0xd0, 0x9d, 0x10, 0xf2, 0xa1, 0xb7, 0x4e, 0x0f, 0xb3, 0x1d, 0xe3, 0x22, 0xc9, 0x99, 0x83, 0x7e, 0x35, 0x60, 0x39, 0x54, 0x6a, 0xb2, 0xc7, 0x1c, 0x3b, 0x2b, 0xef, 0x51, 0xc5, 0x7b, 0xa5, 0xb7, 0x78, 0xd7, 0x03, 0xb4, 0x6c, 0xa4, 0x17, 0xbc, 0x3c, 0x09, 0xe8, 0x0f, 0x03, 0x6e, 0x7b, 0x6c, 0x37, 0xe8, 0xd9, 0xbc, 0xcd, 0x5b, 0x56, 0xac, 0x57, 0x93, 0x59, 0x2b, 0xc8, 0x7c, 0x5d, 0xbb, 0xe4, 0xe5, 0x4d, 0x42, 0xbf, 0x19, 0xf0, 0x91, 0xdf, 0xf4, 0xa8, 0x90, 0x6d, 0xd2, 0x1e, 0xc5, 0x82, 0xec, 0x75, 0x10, 0x4d, 0x24, 0x3f, 0x96, 0xd2, 0x2a, 0x2f, 0x14, 0x62, 0xb4, 0xff, 0xa6, 0xc2, 0x6b, 0x6f, 0x9a, 0xdc, 0x2a, 0x7e, 0xce, 0x9c, 0x95, 0x32, 0x40, 0x9b, 0x4e, 0xe5, 0xdb, 0x02, 0x5c, 0xcd, 0x66, 0x1b, 0xd0, 0x2c, 0x8c, 0xc6, 0x63, 0x83, 0xd9, 0xca, 0x88, 0x94, 0x4c, 0x88, 0x96, 0xd6, 0x6c, 0xb4, 0x0a, 0x63, 0xed, 0xb9, 0xd2, 0x6a, 0x52, 0xed, 0x0d, 0xae, 0x24, 0x3e, 0x6b, 0xbc, 0x59, 0xab, 0x49, 0xcd, 0x32, 0xee, 0xb8, 0x42, 0x93, 0x50, 0xb0, 0x79, 0x03, 0x33, 0x57, 0xcd, 0xf3, 0x92, 0xa9, 0xaf, 0xd0, 0x5d, 0x28, 0xa9, 0x71, 0x15, 0xb8, 0x2d, 0x3d, 0x43, 0x2f, 0x25, 0x62, 0x07, 0x0f, 0xf0, 0x94, 0x79, 0xd2, 0x2c, 0x4a, 0xfd, 0x17, 0x5a, 0x86, 0x61, 0xe6, 0x36, 0x7d, 0xa9, 0xe7, 0xda, 0x4c, 0x62, 0xde, 0x06, 0x6e, 0x39, 0x1c, 0xdb, 0x66, 0x18, 0x8a, 0xb6, 0x60, 0x3a, 0x36, 0x66, 0x92, 0x5b, 0xc4, 0xe1, 0x1e, 0x55, 0x63, 0x89, 0xfb, 0x52, 0x0f, 0xa1, 0xe9, 0x6a, 0x68, 0x2a, 0xab, 0x91, 0xa9, 0xac, 0x3e, 0xd2, 0xa6, 0xd2, 0x9c, 0x8c, 0x72, 0xb7, 0x78, 0x3d, 0xc8, 0xdc, 0x0a, 0x13, 0xbb, 0x51, 0xdb, 0xfe, 0x2a, 0x40, 0x1d, 0xc9, 0x81, 0x1a, 0xbb, 0xab, 0x00, 0x75, 0x1d, 0x26, 0x35, 0x52, 0x37, 0xd1, 0x62, 0x3f, 0xc8, 0x89, 0xd0, 0x86, 0x1d, 0x65, 0xb9, 0x0a, 0xe3, 0x7b, 0x14, 0x0b, 0xb9, 0x4d, 0x71, 0x9b, 0x5d, 0xa9, 0x1f, 0xd4, 0x99, 0x38, 0x27, 0xc2, 0xa9, 0x43, 0x59, 0x50, 0x29, 0x5a, 0x56, 0x93, 0x3b, 0x8c, 0xb4, 0xb4, 0xe2, 0xcc, 0xf5, 0x50, 0x70, 0x29, 0x5a, 0x1b, 0x2a, 0xce, 0x1c, 0x15, 0xed, 0x0b, 0x74, 0x13, 0x0a, 0x7b, 0x14, 0xdb, 0x54, 0xe8, 0x9f, 0xfe, 0xc5, 0xc4, 0xf4, 0x27, 0x2a, 0xc4, 0xd4, 0xa1, 0xe8, 0x16, 0x4c, 0x46, 0x43, 0xd2, 0xe1, 0x04, 0x3b, 0x96, 0xcd, 0xbc, 0x26, 0x96, 0x64, 0x4f, 0xfd, 0x04, 0x8b, 0xe6, 0x59, 0x7d, 0xf7, 0x69, 0x70, 0xf3, 0x91, 0xbe, 0x57, 0xf9, 0xda, 0x80, 0x99, 0x34, 0xdb, 0x8a, 0xa6, 0xa1, 0x18, 0x3a, 0x93, 0xf8, 0x27, 0x30, 0xa2, 0xae, 0xd7, 0x6c, 0xf4, 0x14, 0xce, 0xc5, 0x35, 0xd8, 0x61, 0xa2, 0x5d, 0x82, 0x81, 0x7e, 0xe7, 0x86, 0x74, 0x09, 0x56, 0x99, 0x88, 0x2a, 0x50, 0x21, 0x30, 0x9f, 0xc3, 0xb2, 0xa2, 0x5b, 0x50, 0x10, 0xd4, 0xf3, 0x1d, 0xa9, 0xdf, 0x10, 0xd2, 0x3b, 0x5c, 0xc7, 0x56, 0x30, 0x5c, 0xcb, 0x68, 0x38, 0xd1, 0x6d, 0x18, 0x09, 0x0c, 0xa7, 0x2f, 0x68, 0xea, 0x0e, 0xab, 0x61, 0x8c, 0x19, 0x05, 0x57, 0xd6, 0x61, 0x3e, 0x87, 0x5f, 0xec, 0xab, 0x32, 0x95, 0xbb, 0x70, 0x29, 0xd5, 0xe4, 0xa5, 0x54, 0xa8, 0x42, 0xe0, 0x7a, 0x66, 0x4f, 0x16, 0x3c, 0xb0, 0x4d, 0x25, 0x66, 0x8e, 0x97, 0xe9, 0x48, 0xa3, 0xe0, 0xca, 0xdf, 0x06, 0xdc, 0x39, 0xa9, 0x87, 0xea, 0xd0, 0x3e, 0xe3, 0x88, 0xf6, 0xbd, 0x00, 0x74, 0x7c, 0x3a, 0xea, 0xc6, 0xba, 0x9a, 0xc8, 0xeb, 0xd8, 0x6e, 0xe6, 0xf8, 0x61, 0xf7, 0x12, 0x9a, 0x82, 0x91, 0xc0, 0x6b, 0x08, 0xee, 0x28, 0xad, 0x2d, 0x9b, 0xd1, 0x25, 0xaa, 0xc2, 0x44, 0x97, 0x95, 0xe0, 0xae, 0xd3, 0x52, 0xb2, 0x5b, 0x34, 0xc7, 0x49, 0xe7, 0x98, 0x7f, 0xee, 0x3a, 0xad, 0xca, 0x2f, 0x06, 0x5c, 0x4e, 0xb7, 0x60, 0x41, 0x69, 0xb5, 0xb7, 0x73, 0x71, 0x83, 0x46, 0xa5, 0x0d, 0x97, 0xd6, 0x71, 0x83, 0x76, 0x9e, 0xf8, 0x40, 0x8e, 0x13, 0xef, 0xd0, 0x87, 0xc1, 0xcc, 0xfa, 0x50, 0x79, 0x03, 0xb0, 0x98, 0xd7, 0x9b, 0x05, 0x23, 0x2e, 0x3e, 0x0f, 0x35, 0xe2, 0x8c, 0x94, 0x11, 0x17, 0x01, 0x86, 0x23, 0xee, 0xb0, 0xe3, 0xea, 0xe8, 0x28, 0x1b, 0x38, 0xe1, 0x28, 0x1b, 0xcc, 0x3e, 0xca, 0x30, 0xcc, 0xb5, 0x3d, 0x55, 0x8f, 0x41, 0x31, 0xd4, 0x4f, 0xa5, 0x66, 0x62, 0x88, 0xcd, 0x84, 0x89, 0xf1, 0x0a, 0x2e, 0xaa, 0x47, 0xea, 0x81, 0x3e, 0xdc, 0x0f, 0xfd, 0x7c, 0x90, 0x9d, 0x04, 0xfc, 0x1c, 0x26, 0xb7, 0x31, 0xd9, 0xe7, 0x3b, 0x3b, 0x1a, 0x9b, 0xb9, 0x92, 0x8a, 0x03, 0xec, 0xf4, 0x9f, 0xc1, 0x67, 0x75, 0xa2, 0x82, 0x5d, 0xd3, 0x69, 0xc7, 0x66, 0xd2, 0xc8, 0x49, 0x66, 0xd2, 0x1a, 0x94, 0x98, 0xcb, 0x24, 0xc3, 0x92, 0x0b, 0x35, 0x63, 0x4f, 0x2d, 0xcf, 0xf7, 0xf7, 0xff, 0x6b, 0x51, 0x8a, 0xd9, 0xce, 0xee, 0x54, 0xd6, 0x52, 0x0e, 0x65, 0x45, 0x26, 0x4c, 0x3a, 0x38, 0x78, 0x07, 0x0c, 0xc7, 0x44, 0x50, 0x5a, 0x3d, 0x02, 0x20, 0x43, 0x67, 0x9c, 0x0d, 0x72, 0xeb, 0x71, 0xaa, 0xa9, 0x32, 0xd1, 0xff, 0x61, 0x8c, 0x88, 0xa0, 0x47, 0xb4, 0xcd, 0x50, 0x03, 0xbb, 0x64, 0x96, 0x83, 0xc5, 0xc8, 0x27, 0x9e, 0x6c, 0x1e, 0x2f, 0xc0, 0x50, 0x83, 0x36, 0xb8, 0x36, 0xc0, 0xd3, 0x89, 0x29, 0xcf, 0x68, 0x83, 0x9b, 0x2a, 0x0c, 0x99, 0x30, 0x7e, 0xcc, 0x50, 0x4f, 0x9d, 0x52, 0xb9, 0x1f, 0x24, 0x3b, 0xff, 0x2e, 0xeb, 0x6b, 0x9e, 0xf1, 0xba, 0x56, 0xd0, 0x7d, 0x28, 0x7f, 0xc6, 0xa4, 0xa4, 0x22, 0x6c, 0xa4, 0xa9, 0xd3, 0xfd, 0xfa, 0x67, 0x34, 0x0c, 0x57, 0xed, 0x83, 0x5e, 0xc2, 0x84, 0x3a, 0x1a, 0x7e, 0x40, 0x85, 0x83, 0x9b, 0x51, 0xf7, 0x9c, 0x51, 0xb5, 0x4f, 0xd6, 0xe0, 0xba, 0xe0, 0xee, 0xf3, 0x30, 0x5c, 0xf7, 0xd0, 0x38, 0xe9, 0x5e, 0x42, 0xaf, 0x61, 0x56, 0x8d, 0x37, 0x6a, 0x11, 0xc7, 0xf7, 0x14, 0x3b, 0xea, 0x50, 0xa2, 0xea, 0xa9, 0xf7, 0x18, 0x4f, 0xf9, 0xc8, 0xa6, 0xe6, 0x29, 0xad, 0x87, 0xa9, 0x9b, 0x51, 0xa6, 0xde, 0x6e, 0x06, 0xa7, 0xdc, 0xad, 0xfc, 0x58, 0x82, 0x85, 0x5c, 0xaf, 0x79, 0x3d, 0xc7, 0xd3, 0x2c, 0x8c, 0xc6, 0xba, 0xc8, 0x6c, 0xa5, 0x68, 0x25, 0x13, 0xa2, 0xa5, 0xf0, 0xdd, 0xe0, 0xa8, 0x70, 0x0e, 0xbe, 0x03, 0xe1, 0x7c, 0x0f, 0xef, 0x00, 0x59, 0x84, 0xb3, 0xf0, 0xaf, 0x0a, 0xe7, 0xc8, 0x89, 0x85, 0xf3, 0x25, 0x4c, 0x34, 0xb1, 0xa0, 0xae, 0xd4, 0x88, 0xba, 0x99, 0x8a, 0x29, 0x0d, 0xbb, 0xa1, 0xe2, 0x15, 0x4a, 0xd4, 0xb0, 0xcd, 0xee, 0xa5, 0x4e, 0xd3, 0x50, 0x3a, 0x6a, 0x1a, 0x08, 0x4c, 0x75, 0xb4, 0x81, 0x25, 0xa8, 0xdf, 0xde, 0x16, 0xd4, 0xb6, 0x37, 0x52, 0x0b, 0xbe, 0x66, 0x9b, 0x41, 0x8a, 0xde, 0xfa, 0xdc, 0x61, 0xd2, 0xf2, 0xbb, 0x79, 0xa5, 0x38, 0xa6, 0x73, 0xe5, 0x54, 0x9d, 0x1b, 0xcb, 0xaf, 0x73, 0xa7, 0xde, 0x42, 0xe7, 0x4e, 0xbf, 0x9d, 0xce, 0xfd, 0xf7, 0x94, 0xea, 0xcf, 0x01, 0x58, 0xca, 0xfd, 0x69, 0xe7, 0x7d, 0x9b, 0xe9, 0x59, 0x18, 0xd5, 0x5f, 0xb4, 0x94, 0xbf, 0x0d, 0x3f, 0x5e, 0x40, 0xb8, 0xa4, 0xfc, 0x6d, 0x2c, 0x40, 0x43, 0xd9, 0x05, 0xa8, 0xe3, 0xc7, 0x36, 0x9c, 0xc9, 0xa1, 0x17, 0x7a, 0x39, 0xf4, 0xaf, 0x0c, 0x58, 0xcc, 0xfb, 0x85, 0x29, 0xb9, 0x3d, 0x8d, 0xb7, 0x6a, 0xcf, 0x95, 0x4f, 0xe1, 0x3c, 0xe1, 0x8d, 0xa4, 0xec, 0x95, 0xb1, 0x88, 0xc2, 0x46, 0x20, 0x73, 0x1b, 0xc6, 0x27, 0x4b, 0xbb, 0x4c, 0xee, 0xf9, 0xdb, 0x55, 0xc2, 0x1b, 0xb5, 0xce, 0xff, 0xac, 0x2d, 0x30, 0xdb, 0xa9, 0xed, 0xf2, 0xf0, 0x9f, 0x79, 0xfa, 0xdf, 0x6c, 0xf7, 0x70, 0x93, 0x1d, 0x2c, 0x6d, 0x17, 0xd4, 0xda, 0xcd, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x3a, 0x34, 0x7d, 0x59, 0x29, 0x1c, 0x00, 0x00, }, // uber/cadence/api/v1/history.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5c, 0x5d, 0x6c, 0x1c, 0x49, 0xb5, 0xde, 0x9e, 0xb1, 0xc7, 0x9e, 0x33, 0x8e, 0x3d, 0x2e, 0x3b, 0x8e, 0xff, 0x12, 0x3b, 0x93, 0x6c, 0xe2, 0x75, 0x9c, 0x71, 0xe2, 0x64, 0x93, 0x4d, 0xb2, 0x3f, 0xd7, 0x76, 0x6c, 0xed, 0x48, 0xbe, 0x49, 0x6e, 0xc7, 0xc9, 0xde, 0x7b, 0xb5, 0xd2, 0xd0, 0xee, 0x2e, 0xc7, 0x8d, 0x67, 0xba, 0x67, 0xbb, 0x6b, 0x3c, 0x31, 0x82, 0x27, 0x1e, 0x90, 0x10, 0x2b, 0x58, 0xad, 0x90, 0x58, 0x09, 0x04, 0x42, 0x02, 0xb1, 0xfc, 0x68, 0x11, 0x08, 0xf1, 0xf7, 0x02, 0x48, 0x68, 0x91, 0x40, 0x0b, 0x4f, 0xbc, 0xf0, 0x84, 0x84, 0x10, 0xfb, 0xc6, 0x03, 0xcb, 0x33, 0xea, 0xea, 0xea, 0x99, 0xe9, 0x9e, 0xaa, 0xfe, 0x19, 0x3b, 0x59, 0xd0, 0xe6, 0x6d, 0xba, 0xfb, 0x9c, 0xd3, 0x5f, 0x55, 0x9d, 0x73, 0xea, 0xd4, 0x39, 0xa7, 0x07, 0x4e, 0xd6, 0xb7, 0xb0, 0xb5, 0xa8, 0x2a, 0x1a, 0x36, 0x54, 0xbc, 0xa8, 0xd4, 0xf4, 0xc5, 0xbd, 0x8b, 0x8b, 0x3b, 0xba, 0x4d, 0x4c, 0x6b, 0xbf, 0x58, 0xb3, 0x4c, 0x62, 0xa2, 0x11, 0x87, 0xa4, 0xc8, 0x48, 0x8a, 0x4a, 0x4d, 0x2f, 0xee, 0x5d, 0x9c, 0x3c, 0xf1, 0xc0, 0x34, 0x1f, 0x54, 0xf0, 0x22, 0x25, 0xd9, 0xaa, 0x6f, 0x2f, 0x6a, 0x75, 0x4b, 0x21, 0xba, 0x69, 0xb8, 0x4c, 0x93, 0x33, 0xc1, 0xe7, 0x44, 0xaf, 0x62, 0x9b, 0x28, 0xd5, 0x1a, 0x23, 0x98, 0xe5, 0xbd, 0x58, 0x35, 0xab, 0xd5, 0xa6, 0x88, 0x02, 0x8f, 0x82, 0x28, 0xf6, 0x6e, 0x45, 0xb7, 0x49, 0x18, 0x4d, 0xc3, 0xb4, 0x76, 0xb7, 0x2b, 0x66, 0xc3, 0xa5, 0x29, 0xdc, 0x84, 0xbe, 0x97, 0xdd, 0x01, 0xa1, 0x6b, 0x90, 0xc1, 0x7b, 0xd8, 0x20, 0xf6, 0xb8, 0x34, 0x9b, 0x9e, 0xcb, 0x2d, 0x9d, 0x2c, 0x72, 0xc6, 0x56, 0x64, 0xd4, 0x6b, 0x0e, 0xa5, 0xcc, 0x18, 0x0a, 0xef, 0x5f, 0x85, 0x81, 0xf6, 0x07, 0x68, 0x02, 0xfa, 0xe9, 0xa3, 0xb2, 0xae, 0x8d, 0x4b, 0xb3, 0xd2, 0x5c, 0x5a, 0xee, 0xa3, 0xd7, 0x25, 0x0d, 0x5d, 0x03, 0x70, 0x1f, 0x39, 0x83, 0x1e, 0x4f, 0xcd, 0x4a, 0x73, 0xb9, 0xa5, 0xc9, 0xa2, 0x3b, 0x23, 0x45, 0x6f, 0x46, 0x8a, 0x9b, 0xde, 0x8c, 0xc8, 0x59, 0x4a, 0xed, 0x5c, 0xa3, 0x71, 0xe8, 0xdb, 0xc3, 0x96, 0xad, 0x9b, 0xc6, 0x78, 0xda, 0x15, 0xca, 0x2e, 0xd1, 0x31, 0xe8, 0x73, 0x06, 0xef, 0xbc, 0xae, 0x87, 0x3e, 0xc9, 0x38, 0x97, 0x25, 0x0d, 0x7d, 0x59, 0x82, 0x73, 0xde, 0x90, 0xcb, 0xf8, 0x21, 0x56, 0xeb, 0xce, 0x3a, 0x94, 0x6d, 0xa2, 0x58, 0x04, 0x6b, 0x65, 0x17, 0x89, 0x42, 0x88, 0xa5, 0x6f, 0xd5, 0x09, 0xb6, 0xc7, 0x7b, 0x29, 0x9e, 0xe7, 0xb9, 0x43, 0x7f, 0x85, 0xc9, 0x59, 0xf3, 0xc4, 0xdc, 0x75, 0xa5, 0xd0, 0x21, 0x2f, 0x37, 0x65, 0xbc, 0xfc, 0x94, 0x7c, 0xb6, 0x11, 0x8f, 0x14, 0x7d, 0x5d, 0x82, 0xf3, 0x1c, 0x78, 0xaa, 0x59, 0xad, 0x55, 0x30, 0x17, 0x60, 0x86, 0x02, 0x7c, 0x31, 0x1e, 0xc0, 0x55, 0x4f, 0x4e, 0x27, 0xc4, 0x67, 0x1a, 0x71, 0x89, 0xd1, 0x5b, 0x12, 0xcc, 0x73, 0x40, 0x6e, 0x2b, 0x7a, 0x85, 0x87, 0xb0, 0x8f, 0x22, 0xbc, 0x11, 0x0f, 0xe1, 0x3a, 0x15, 0xd2, 0x09, 0xef, 0x4c, 0x23, 0x16, 0x25, 0xfa, 0x1a, 0x7f, 0x02, 0x1d, 0xdd, 0xd2, 0xca, 0x66, 0x9d, 0x74, 0xc2, 0xeb, 0xa7, 0xf0, 0x5e, 0x88, 0x07, 0xcf, 0x51, 0x3b, 0xed, 0x76, 0x9d, 0x74, 0x02, 0x9c, 0x6b, 0xc4, 0xa4, 0x45, 0x6f, 0x4a, 0x30, 0xa7, 0x61, 0x55, 0xb7, 0x29, 0x30, 0x47, 0x4b, 0x6d, 0x75, 0x07, 0x6b, 0x75, 0xee, 0xe4, 0x65, 0x29, 0xba, 0x6b, 0x5c, 0x74, 0x37, 0x99, 0x90, 0x4d, 0xc5, 0xde, 0xbd, 0xeb, 0x89, 0xe8, 0x44, 0x76, 0x5a, 0x8b, 0x41, 0x87, 0x5e, 0x97, 0xe0, 0x4c, 0x00, 0x95, 0xc8, 0x26, 0x80, 0x62, 0xba, 0x1a, 0x8d, 0x49, 0x64, 0x0e, 0x05, 0x2d, 0x92, 0x8a, 0x33, 0x4b, 0x21, 0x46, 0x90, 0x8b, 0x39, 0x4b, 0x21, 0xfa, 0xef, 0x9b, 0x25, 0xa1, 0xea, 0xbf, 0xd1, 0x81, 0x2a, 0x44, 0xb3, 0x06, 0x28, 0xaa, 0xe7, 0x22, 0x51, 0x89, 0x95, 0xea, 0x94, 0x16, 0x4d, 0x86, 0x3e, 0x2b, 0xc1, 0xd3, 0x7e, 0x4c, 0x22, 0x4b, 0x3c, 0x42, 0x01, 0x5d, 0x89, 0x04, 0x24, 0x32, 0xc2, 0x93, 0x5a, 0x14, 0x11, 0x5d, 0x36, 0x45, 0x25, 0xfa, 0x9e, 0x4e, 0xf6, 0x23, 0x95, 0x7b, 0x30, 0x64, 0xd9, 0x96, 0x99, 0x90, 0x28, 0xe5, 0x56, 0x62, 0xd0, 0x51, 0xe5, 0x0e, 0xa0, 0x12, 0x29, 0xf7, 0x50, 0x88, 0x72, 0xfb, 0x30, 0x09, 0x95, 0x5b, 0x89, 0xa4, 0xe2, 0xcc, 0x52, 0x88, 0x72, 0xe7, 0x63, 0xce, 0x52, 0x98, 0x72, 0x2b, 0x31, 0xe8, 0xa8, 0x22, 0xf9, 0x51, 0x89, 0x14, 0x69, 0x38, 0x44, 0x91, 0xda, 0x21, 0x09, 0x15, 0x49, 0x89, 0x22, 0xa2, 0x96, 0xe6, 0x07, 0x13, 0x62, 0x69, 0x28, 0xc4, 0xd2, 0xda, 0xf1, 0x84, 0x58, 0x9a, 0x12, 0x4d, 0x86, 0x1a, 0x70, 0xc2, 0x01, 0x61, 0x89, 0xb5, 0x67, 0x84, 0x02, 0xb9, 0xc0, 0x05, 0xe2, 0x48, 0xb5, 0x84, 0x6a, 0x33, 0x45, 0xc4, 0x8f, 0xd1, 0x6b, 0x30, 0xed, 0xbe, 0x78, 0x5b, 0xb7, 0x78, 0xaf, 0x1d, 0xa5, 0xaf, 0x2d, 0x8a, 0x5f, 0xbb, 0xee, 0xf0, 0x75, 0xbe, 0x74, 0x82, 0x88, 0x1e, 0xa2, 0x6f, 0x4a, 0xb0, 0x18, 0x50, 0x51, 0xc5, 0x50, 0x71, 0xa5, 0x6c, 0xe1, 0xd7, 0xea, 0xd8, 0xe6, 0x8e, 0xfe, 0x28, 0x85, 0xf1, 0x52, 0xb4, 0xa6, 0x52, 0x49, 0xb2, 0x27, 0xa8, 0x13, 0xd7, 0xbc, 0x12, 0x9b, 0x1a, 0xfd, 0x50, 0x82, 0xcb, 0x0c, 0x93, 0x07, 0x31, 0x9e, 0x12, 0x8f, 0x51, 0xb4, 0xab, 0x5c, 0xb4, 0xec, 0x6d, 0xee, 0xab, 0xe3, 0x68, 0x74, 0xd1, 0x4a, 0xc4, 0x81, 0xbe, 0x20, 0xc1, 0x59, 0xde, 0xf4, 0xf2, 0x80, 0x1e, 0x8b, 0xa9, 0xdd, 0xab, 0x4c, 0x42, 0x84, 0x76, 0x0b, 0xc8, 0xd0, 0x27, 0x60, 0xc6, 0x55, 0x32, 0x31, 0x92, 0x71, 0x8a, 0xe4, 0xa2, 0x58, 0xcf, 0xc4, 0x10, 0x5c, 0x05, 0x16, 0xbd, 0xfb, 0x33, 0x12, 0x9c, 0x66, 0x8b, 0xc7, 0x14, 0x5d, 0xb0, 0x68, 0x13, 0x14, 0xc1, 0xb3, 0x5c, 0x04, 0xae, 0x70, 0x57, 0xdf, 0x05, 0xcb, 0x34, 0xab, 0x46, 0xd0, 0xa0, 0x4f, 0xc1, 0x6c, 0x55, 0xb1, 0x76, 0xb1, 0x55, 0xb6, 0xb0, 0x6a, 0x5a, 0x1a, 0x0f, 0xc4, 0x24, 0x05, 0xb1, 0xc4, 0x05, 0xf1, 0xdf, 0x94, 0x59, 0x66, 0xbc, 0x9d, 0x08, 0x8e, 0x57, 0xc3, 0x08, 0xd0, 0x57, 0x25, 0x58, 0xe0, 0x9d, 0x4f, 0xf4, 0x07, 0x86, 0xc2, 0x9d, 0x90, 0xa9, 0x24, 0xe1, 0xeb, 0x5d, 0x26, 0x26, 0x4e, 0xf8, 0x2a, 0xa0, 0x45, 0xdf, 0x90, 0xa0, 0xc8, 0x8b, 0xb0, 0xb1, 0x55, 0xd5, 0x0d, 0x85, 0xeb, 0x17, 0xa6, 0x43, 0xfc, 0x42, 0x67, 0x88, 0xdd, 0x14, 0xc4, 0xf1, 0x0b, 0x8d, 0xd8, 0xd4, 0xe8, 0x47, 0x12, 0x5c, 0xe6, 0x1d, 0xa5, 0x22, 0xbd, 0xd8, 0x71, 0x8a, 0xf6, 0x66, 0xcc, 0x13, 0x55, 0x94, 0x2b, 0x5b, 0x6c, 0x24, 0x63, 0x11, 0x69, 0x80, 0xd8, 0x28, 0x4f, 0x24, 0xd1, 0x00, 0xb1, 0x81, 0xce, 0x35, 0x62, 0xd2, 0xa2, 0xbf, 0x48, 0xb0, 0x16, 0xf0, 0xb8, 0xf8, 0x21, 0xc1, 0x96, 0xa1, 0x54, 0xca, 0x1c, 0xe4, 0xba, 0xa1, 0x13, 0x9d, 0xaf, 0x18, 0x33, 0x14, 0xfa, 0xdd, 0x68, 0x17, 0xbc, 0xc6, 0xe4, 0x77, 0x8c, 0xa7, 0xe4, 0x09, 0xef, 0x1c, 0xd0, 0x8b, 0xd6, 0x81, 0x24, 0xa0, 0x3f, 0x49, 0xb0, 0x92, 0x60, 0x98, 0x22, 0x8f, 0x35, 0x4b, 0xc7, 0x78, 0xe7, 0x00, 0x63, 0x14, 0x39, 0xb3, 0x1b, 0x56, 0xf7, 0xec, 0xe8, 0x3d, 0x09, 0x5e, 0x08, 0x1b, 0x4e, 0xb4, 0x9d, 0x9c, 0xa4, 0x03, 0xdb, 0xe0, 0x0e, 0x4c, 0x08, 0x26, 0xd2, 0x5e, 0xae, 0xe2, 0xee, 0x58, 0x69, 0x1c, 0xc0, 0x4d, 0x9d, 0x18, 0x44, 0x37, 0xea, 0x58, 0x2b, 0x2b, 0x76, 0xd9, 0xc0, 0x8d, 0xce, 0x71, 0x14, 0x42, 0xe2, 0x00, 0x4e, 0x06, 0x85, 0x89, 0x5b, 0xb6, 0x6f, 0xe1, 0x06, 0x27, 0x0e, 0x68, 0x24, 0xe2, 0x40, 0xbf, 0x92, 0xe0, 0x1a, 0x8d, 0x26, 0xcb, 0xea, 0x8e, 0x5e, 0xd1, 0x12, 0xda, 0xcf, 0x29, 0x0a, 0xfd, 0x65, 0x2e, 0x74, 0x1a, 0x4a, 0xae, 0x3a, 0x42, 0x93, 0x18, 0xcd, 0x25, 0x3b, 0x39, 0x1b, 0xfa, 0xa9, 0x04, 0x57, 0x22, 0x06, 0x21, 0xb2, 0x8e, 0xd3, 0x74, 0x04, 0x6b, 0x49, 0x47, 0x20, 0x32, 0x89, 0x0b, 0x76, 0x42, 0x1e, 0xf4, 0x5d, 0x09, 0x2e, 0x0a, 0x51, 0x0b, 0xe3, 0xfc, 0xa7, 0x29, 0xec, 0x65, 0x7e, 0x18, 0xc2, 0x7d, 0xbb, 0x30, 0xf0, 0x5f, 0x50, 0x13, 0xd0, 0xa3, 0x1f, 0x48, 0x70, 0x49, 0x08, 0x37, 0xe4, 0x10, 0x79, 0x26, 0x44, 0xc9, 0xf9, 0x80, 0x43, 0x8e, 0x93, 0x45, 0x35, 0x11, 0x07, 0x7a, 0x5b, 0x82, 0x0b, 0x89, 0x35, 0xe3, 0x2c, 0x45, 0xfc, 0x5f, 0x09, 0x10, 0x8b, 0x94, 0xe2, 0x9c, 0x9a, 0x40, 0x1f, 0xde, 0x91, 0x60, 0x49, 0x3c, 0xc1, 0xc2, 0x4d, 0x78, 0x8e, 0xa2, 0x5d, 0x49, 0x32, 0xbf, 0xc2, 0x9d, 0xf8, 0xbc, 0x9a, 0x84, 0x01, 0x7d, 0x3f, 0x4c, 0x25, 0x42, 0x0e, 0xcd, 0xcf, 0x24, 0x86, 0x2c, 0x3e, 0x3e, 0x0b, 0x20, 0x8b, 0x0e, 0xd2, 0x4e, 0x6c, 0x26, 0x86, 0x1c, 0x12, 0x49, 0xce, 0x87, 0xc4, 0x66, 0x02, 0xcc, 0x21, 0xe1, 0xe4, 0xa2, 0x9a, 0x8c, 0x85, 0x6e, 0x9a, 0x6e, 0x28, 0xde, 0x6d, 0xc4, 0x73, 0x2e, 0x64, 0xd3, 0x74, 0x23, 0xee, 0x6e, 0x42, 0x9d, 0xab, 0x76, 0x77, 0xac, 0xe8, 0xd7, 0x12, 0x5c, 0x8f, 0x31, 0x20, 0x91, 0x8d, 0x2e, 0xd0, 0xd1, 0x94, 0xba, 0x19, 0x8d, 0xc8, 0x58, 0x2f, 0xdb, 0x5d, 0xf0, 0xa1, 0x9f, 0x48, 0xf0, 0x6c, 0xd8, 0x00, 0xc4, 0xe7, 0xa7, 0xf3, 0x21, 0x1b, 0x90, 0x10, 0x84, 0xf8, 0x1c, 0x75, 0x01, 0x27, 0xe4, 0xa1, 0x0e, 0xa7, 0x5e, 0xb3, 0xb1, 0x45, 0x5a, 0xc0, 0x6d, 0xac, 0x58, 0xea, 0x4e, 0x1b, 0xcc, 0x4e, 0xdc, 0xc5, 0x10, 0xeb, 0xbd, 0x47, 0xc5, 0x79, 0x08, 0xee, 0x52, 0x61, 0xad, 0x37, 0x72, 0xac, 0xb7, 0x9e, 0x84, 0x61, 0x65, 0x00, 0xa0, 0x05, 0xa4, 0xf0, 0xe7, 0x21, 0x38, 0x1b, 0x77, 0xf7, 0x5a, 0x87, 0x23, 0xcd, 0x31, 0x92, 0xfd, 0x1a, 0xa6, 0xb5, 0x40, 0x51, 0x65, 0xd1, 0x13, 0xba, 0xb9, 0x5f, 0xc3, 0xf2, 0x40, 0xa3, 0xed, 0x0a, 0xbd, 0x0a, 0x47, 0x6b, 0x8a, 0xe5, 0xcc, 0x48, 0xbb, 0xd1, 0x6d, 0x9b, 0xac, 0x7c, 0x38, 0xc7, 0x95, 0x77, 0x87, 0x72, 0xb4, 0xd9, 0xc4, 0xb6, 0x29, 0x8f, 0xd4, 0x3a, 0x6f, 0xa2, 0xeb, 0x90, 0xa5, 0x19, 0x99, 0x8a, 0x6e, 0x13, 0x5a, 0x58, 0xcc, 0x2d, 0x1d, 0xe7, 0xa7, 0x3c, 0x14, 0x7b, 0x77, 0x43, 0xb7, 0x89, 0xdc, 0x4f, 0xd8, 0x2f, 0xb4, 0x04, 0xbd, 0xba, 0x51, 0xab, 0x13, 0x5a, 0x76, 0xcc, 0x2d, 0x4d, 0x0b, 0x90, 0xec, 0x57, 0x4c, 0x45, 0x93, 0x5d, 0x52, 0xa4, 0xc0, 0x6c, 0x20, 0xe4, 0x28, 0x13, 0xb3, 0xac, 0x56, 0x4c, 0x1b, 0x53, 0xff, 0x6d, 0xd6, 0x09, 0xab, 0x43, 0x4e, 0x74, 0xd4, 0x45, 0x6f, 0xb2, 0x4a, 0xb2, 0x3c, 0x8d, 0x7d, 0x73, 0xbf, 0x69, 0xae, 0x3a, 0xfc, 0x9b, 0x2e, 0x3b, 0x7a, 0x05, 0xa6, 0x5a, 0x69, 0xef, 0x4e, 0xe9, 0x99, 0x28, 0xe9, 0xc7, 0x88, 0x97, 0xcc, 0x0e, 0x08, 0xbe, 0x01, 0x93, 0xad, 0x08, 0xbb, 0x35, 0x0a, 0xab, 0x6e, 0x94, 0x75, 0x8d, 0x96, 0xfe, 0xb2, 0xf2, 0xb1, 0x26, 0x45, 0x73, 0x9e, 0xe5, 0xba, 0x51, 0xd2, 0x50, 0x09, 0xb2, 0xcc, 0x55, 0x9a, 0x16, 0xad, 0xc3, 0x0d, 0x2e, 0x9d, 0xe3, 0xbb, 0x76, 0x26, 0x80, 0x86, 0xd0, 0x25, 0x8f, 0x45, 0x6e, 0x71, 0xa3, 0x12, 0x0c, 0xb7, 0x70, 0x38, 0xee, 0xaa, 0x6e, 0x61, 0x56, 0x3c, 0xe3, 0xaf, 0xc1, 0xba, 0x4b, 0x23, 0xe7, 0x9b, 0x6c, 0xec, 0x0e, 0x92, 0x61, 0xac, 0xa2, 0x38, 0x67, 0x3e, 0x37, 0x9c, 0xa1, 0xc3, 0xc1, 0x76, 0xbd, 0x42, 0x58, 0xe1, 0x2b, 0x7c, 0x4d, 0x47, 0x1d, 0xde, 0xd5, 0x26, 0xab, 0x4c, 0x39, 0xd1, 0x35, 0x98, 0x30, 0x2d, 0xfd, 0x81, 0xee, 0x3a, 0xda, 0xc0, 0x2c, 0xe5, 0xe8, 0x2c, 0x8d, 0x79, 0x04, 0x81, 0x49, 0x9a, 0x84, 0x7e, 0x5d, 0xc3, 0x06, 0xd1, 0xc9, 0x3e, 0xad, 0x28, 0x65, 0xe5, 0xe6, 0x35, 0xba, 0x04, 0x63, 0xdb, 0xba, 0x65, 0x93, 0x4e, 0x99, 0x47, 0x28, 0xe5, 0x08, 0x7d, 0x1a, 0x10, 0xb8, 0x0a, 0x03, 0x16, 0x26, 0xd6, 0x7e, 0xb9, 0x66, 0x56, 0x74, 0x75, 0x9f, 0x55, 0x61, 0x66, 0x05, 0x07, 0x54, 0x62, 0xed, 0xdf, 0xa1, 0x74, 0x72, 0xce, 0x6a, 0x5d, 0xa0, 0x71, 0xe8, 0x53, 0x08, 0xc1, 0xd5, 0x1a, 0xa1, 0x15, 0x93, 0x5e, 0xd9, 0xbb, 0x44, 0xab, 0x30, 0x84, 0x1f, 0xd6, 0x74, 0x57, 0x71, 0xdc, 0xa2, 0x7e, 0x3e, 0xb2, 0xa8, 0x3f, 0xd8, 0x62, 0xa1, 0x95, 0xfd, 0x53, 0x70, 0x44, 0xb5, 0x1c, 0x6b, 0x60, 0x15, 0x1d, 0x5a, 0x71, 0xc8, 0xca, 0x03, 0xce, 0x4d, 0xaf, 0xca, 0x83, 0xfe, 0x17, 0xa6, 0xdc, 0xd1, 0xfb, 0xab, 0x5f, 0x5b, 0x8a, 0xba, 0x6b, 0x6e, 0x6f, 0xb3, 0xa2, 0x40, 0x88, 0x52, 0x8f, 0x53, 0xee, 0xf6, 0xc2, 0xd7, 0x8a, 0xcb, 0x8a, 0xce, 0x43, 0x4f, 0x15, 0x57, 0x4d, 0x96, 0xce, 0x9f, 0xe0, 0x27, 0xfa, 0x70, 0xd5, 0x94, 0x29, 0x19, 0x92, 0x61, 0xb8, 0xc3, 0x63, 0xb3, 0x9c, 0xfc, 0xd3, 0xfc, 0xbd, 0x31, 0xe0, 0x61, 0xe5, 0xbc, 0x1d, 0xb8, 0x83, 0xee, 0xc1, 0x58, 0xcd, 0xc2, 0x7b, 0x65, 0xa5, 0x4e, 0x4c, 0x47, 0xff, 0x30, 0x29, 0xd7, 0x4c, 0xdd, 0x20, 0x5e, 0x96, 0x5d, 0xb4, 0x5e, 0x36, 0x26, 0x77, 0x28, 0x9d, 0x3c, 0xe2, 0xf0, 0x2f, 0xd7, 0x89, 0xd9, 0x76, 0x13, 0x5d, 0x82, 0xcc, 0x0e, 0x56, 0x34, 0x6c, 0xb1, 0xf4, 0xf7, 0x14, 0xbf, 0xa9, 0x83, 0x92, 0xc8, 0x8c, 0x14, 0x6d, 0xc0, 0xa8, 0x3b, 0xd1, 0xad, 0x5a, 0x1e, 0x5d, 0xd7, 0x63, 0x91, 0xeb, 0x8a, 0x28, 0x5f, 0xb3, 0x2e, 0x47, 0xd7, 0xf6, 0x93, 0x90, 0xaf, 0x29, 0x16, 0xd1, 0xbd, 0xe3, 0xf9, 0xb6, 0xfe, 0x60, 0x7c, 0x9c, 0x76, 0x98, 0xfc, 0xcf, 0x41, 0xda, 0x2c, 0x1c, 0xff, 0xee, 0x0a, 0x5d, 0xa5, 0x32, 0xd7, 0x0c, 0x62, 0xed, 0xcb, 0x43, 0x35, 0xff, 0x5d, 0x74, 0x1c, 0xc0, 0x4b, 0xea, 0xe8, 0x1a, 0x4d, 0x27, 0x67, 0xe5, 0x2c, 0xbb, 0x53, 0xd2, 0xd0, 0x7d, 0x18, 0xa1, 0x8a, 0x67, 0xee, 0x61, 0xab, 0xa2, 0xd4, 0x3c, 0x1b, 0x99, 0xa4, 0xce, 0xe9, 0x0c, 0xdf, 0x39, 0x59, 0xa6, 0x71, 0xdb, 0x25, 0x67, 0x96, 0x32, 0xac, 0x06, 0x6f, 0xa1, 0x87, 0x30, 0x43, 0x73, 0xf0, 0xb8, 0xac, 0x56, 0xea, 0x36, 0xc1, 0x56, 0xd9, 0xc6, 0x15, 0xac, 0xd2, 0x39, 0x60, 0xef, 0x98, 0x0a, 0x49, 0xae, 0xd3, 0x34, 0x3f, 0x5e, 0x75, 0x59, 0xef, 0x7a, 0x9c, 0xec, 0x75, 0xd3, 0x4a, 0xc8, 0xd3, 0xc9, 0x15, 0x18, 0xe5, 0xcd, 0x0c, 0xca, 0x43, 0x7a, 0x17, 0xef, 0xd3, 0x1d, 0x38, 0x2b, 0x3b, 0x3f, 0xd1, 0x28, 0xf4, 0xee, 0x29, 0x95, 0xba, 0xdb, 0x84, 0x93, 0x95, 0xdd, 0x8b, 0xeb, 0xa9, 0xe7, 0xa4, 0xc2, 0xdb, 0x12, 0x3c, 0x13, 0xff, 0xb8, 0x77, 0x19, 0x32, 0xcc, 0x61, 0x4a, 0x31, 0x1c, 0x26, 0xa3, 0x45, 0xeb, 0x30, 0x1b, 0x5e, 0xef, 0xd7, 0x35, 0x0a, 0x2c, 0x2d, 0x4f, 0x8b, 0x4b, 0xf5, 0x25, 0xad, 0xf0, 0x2d, 0x09, 0xce, 0xc4, 0x8c, 0x1a, 0xaf, 0x40, 0x9f, 0xb7, 0x55, 0x48, 0x31, 0xb6, 0x0a, 0x8f, 0xf8, 0xd0, 0xa0, 0x9a, 0x30, 0x17, 0xfb, 0xc8, 0xb4, 0x0a, 0x03, 0x6c, 0xb7, 0x6e, 0x45, 0x4e, 0x83, 0x02, 0x2f, 0xc0, 0x36, 0x67, 0x1a, 0x38, 0xe5, 0x48, 0xeb, 0xa2, 0xf0, 0x3b, 0x09, 0x4e, 0xc7, 0xe9, 0x1a, 0xf1, 0x87, 0x40, 0x52, 0xb2, 0x10, 0xe8, 0x16, 0x8c, 0x09, 0xc2, 0x8c, 0x54, 0x94, 0x47, 0x1e, 0xb1, 0x39, 0x21, 0x46, 0xdb, 0x56, 0x93, 0xf6, 0x6d, 0x35, 0x85, 0xd7, 0x25, 0x28, 0x44, 0x37, 0x9c, 0xa0, 0x05, 0x40, 0xc1, 0x26, 0x84, 0x66, 0x1b, 0x5a, 0xde, 0xf6, 0x4d, 0x41, 0x60, 0xbf, 0x4d, 0x05, 0xf6, 0x5b, 0xbf, 0xf3, 0x48, 0x07, 0x9c, 0x47, 0xe1, 0xef, 0x81, 0xe9, 0x15, 0x5a, 0x48, 0x32, 0x44, 0x73, 0x90, 0xf7, 0x27, 0xa2, 0x9a, 0xea, 0x35, 0x68, 0xb7, 0x8d, 0x38, 0x80, 0x3d, 0x1d, 0xc0, 0x7e, 0x16, 0x86, 0xb6, 0x74, 0x43, 0xb1, 0xf6, 0xcb, 0xea, 0x0e, 0x56, 0x77, 0xed, 0x7a, 0x95, 0xc6, 0xa8, 0x59, 0x79, 0xd0, 0xbd, 0xbd, 0xca, 0xee, 0xa2, 0x73, 0x30, 0xec, 0x4f, 0x9f, 0xe2, 0x87, 0x6e, 0xfc, 0x39, 0x20, 0xe7, 0x71, 0x7b, 0x56, 0x13, 0x3f, 0x24, 0x85, 0xef, 0xa4, 0xe1, 0x54, 0x8c, 0x5e, 0x96, 0x47, 0x36, 0xe2, 0xa0, 0x59, 0xa4, 0xbb, 0x30, 0x0b, 0x74, 0x02, 0x72, 0x5b, 0x8a, 0x8d, 0xbd, 0xd8, 0xc9, 0x9d, 0x96, 0xac, 0x73, 0xcb, 0x8d, 0x98, 0xa6, 0x01, 0x0c, 0xdc, 0xf0, 0x1e, 0xf7, 0xba, 0x13, 0x6b, 0xe0, 0x86, 0xfb, 0x74, 0x01, 0xd0, 0xb6, 0x69, 0xed, 0x32, 0xa4, 0x5e, 0x43, 0x62, 0xc6, 0x1d, 0x9a, 0xf3, 0x84, 0x62, 0xbd, 0xcf, 0x3a, 0x13, 0xc7, 0x1c, 0xe7, 0xa8, 0xd8, 0xa6, 0xc1, 0x82, 0x63, 0x76, 0x85, 0x6e, 0x42, 0xaf, 0xaa, 0xd4, 0x6d, 0xcc, 0xe2, 0xe0, 0x62, 0xec, 0xae, 0xa1, 0x55, 0x87, 0x4b, 0x76, 0x99, 0x03, 0x0a, 0x9a, 0x0d, 0x2a, 0xe8, 0xbb, 0x69, 0x38, 0x19, 0xd9, 0xe8, 0xf3, 0xc8, 0xd6, 0x6a, 0xc5, 0x1b, 0xa2, 0xbb, 0x48, 0x0b, 0x31, 0xfb, 0x90, 0x7c, 0x03, 0x6c, 0x73, 0xd9, 0x3d, 0x49, 0x5c, 0x76, 0xbb, 0x65, 0xf4, 0x06, 0x2c, 0x23, 0xb0, 0xfc, 0x99, 0xf0, 0xe5, 0xef, 0x8b, 0xb5, 0xfc, 0xfd, 0x82, 0xe5, 0xe7, 0x58, 0x61, 0x96, 0x6b, 0x85, 0xfe, 0x95, 0x84, 0xe0, 0x4a, 0x7e, 0x25, 0x03, 0xa7, 0xe3, 0xb4, 0x48, 0xa1, 0x19, 0xc8, 0x35, 0xfb, 0x0c, 0xd8, 0x2a, 0x66, 0x65, 0xf0, 0x6e, 0x95, 0x34, 0xe7, 0x4c, 0xde, 0x6a, 0x44, 0x70, 0x4c, 0x28, 0x15, 0x72, 0x26, 0x6f, 0xbe, 0x92, 0x9e, 0xc9, 0x95, 0xb6, 0x2b, 0x47, 0xb1, 0x35, 0xb3, 0xaa, 0xe8, 0x06, 0xf3, 0x3c, 0xec, 0xca, 0xbf, 0x95, 0xf4, 0x74, 0x79, 0x9a, 0xce, 0xc4, 0x3f, 0x4d, 0x6f, 0xc2, 0x84, 0xa7, 0xa3, 0x9d, 0x3b, 0x50, 0x5f, 0xd4, 0x0e, 0x34, 0xe6, 0xf1, 0x06, 0x36, 0xa1, 0x80, 0x54, 0xb6, 0xc1, 0x31, 0xa9, 0xfd, 0x09, 0xa4, 0xba, 0x87, 0x68, 0x26, 0x55, 0xbc, 0x55, 0x66, 0xbb, 0xda, 0x2a, 0xd7, 0x61, 0x78, 0x07, 0x2b, 0x16, 0xd9, 0xc2, 0x4a, 0x0b, 0x1d, 0x44, 0x89, 0xca, 0x37, 0x79, 0x5a, 0x72, 0xa2, 0x03, 0x9c, 0x5c, 0x74, 0x80, 0xd3, 0x71, 0xd4, 0x1c, 0xe8, 0xe6, 0xa8, 0xd9, 0x3a, 0xb2, 0x1c, 0x89, 0x7d, 0x64, 0x29, 0xfc, 0x4d, 0x82, 0x42, 0x74, 0xbb, 0xde, 0x63, 0x0b, 0x0d, 0xda, 0x83, 0x98, 0x1e, 0xff, 0x79, 0xf9, 0x25, 0x18, 0xa0, 0xe9, 0x06, 0xcf, 0xad, 0xf5, 0xc6, 0x70, 0x6b, 0x39, 0x87, 0x83, 0x5d, 0x14, 0xfe, 0x20, 0xf9, 0x5d, 0xc1, 0x21, 0xc7, 0xe5, 0xfc, 0x29, 0x4a, 0x25, 0xd8, 0x0d, 0xd2, 0x91, 0xb1, 0x4a, 0x8f, 0x7f, 0x32, 0x0b, 0xbf, 0x97, 0xe0, 0x64, 0x74, 0x0f, 0x55, 0xb7, 0xe1, 0xfb, 0x87, 0x31, 0xa2, 0x9f, 0xa7, 0xe0, 0x54, 0x8c, 0x4e, 0x44, 0x67, 0x4c, 0x1a, 0x26, 0x8a, 0x5e, 0xb1, 0x63, 0x2d, 0x92, 0x47, 0xfc, 0xc8, 0xc6, 0x14, 0x8c, 0xaf, 0x7a, 0xba, 0x89, 0xaf, 0x0e, 0xac, 0xe2, 0x5f, 0x94, 0x60, 0x3e, 0x7e, 0x03, 0x61, 0x9c, 0x3d, 0xef, 0x70, 0x0e, 0x70, 0xef, 0x48, 0x90, 0xb0, 0x55, 0x30, 0x1a, 0xdb, 0xa8, 0x17, 0x25, 0xb1, 0x53, 0xb8, 0x1b, 0xf7, 0xc4, 0x41, 0x9c, 0x8e, 0x81, 0xf8, 0xad, 0x80, 0x1e, 0x8a, 0x8a, 0x8a, 0xdd, 0xea, 0xe1, 0x3a, 0xcc, 0x56, 0x14, 0xd2, 0xd6, 0x32, 0x13, 0x6c, 0x20, 0x69, 0xcd, 0xac, 0x4b, 0xc7, 0x5b, 0x4a, 0x37, 0xaa, 0xe2, 0xe8, 0x73, 0x3a, 0x81, 0x3e, 0xf7, 0x44, 0xda, 0x68, 0x20, 0x0e, 0x2c, 0xbc, 0x27, 0xc1, 0x54, 0x48, 0x93, 0x2e, 0x9a, 0x80, 0x7e, 0xb7, 0x39, 0xb1, 0xb9, 0x6e, 0x7d, 0xf4, 0xba, 0xa4, 0xa1, 0x0d, 0x38, 0xda, 0xdc, 0xc8, 0xb7, 0x75, 0x2b, 0xc1, 0x91, 0x17, 0xb1, 0x7d, 0x7c, 0x5d, 0xb7, 0x70, 0x92, 0xed, 0x37, 0xce, 0x62, 0x7f, 0x0c, 0x26, 0x84, 0xdd, 0xbf, 0x61, 0xa3, 0x89, 0x1d, 0xd2, 0x17, 0xde, 0x95, 0x60, 0x3a, 0xac, 0xf1, 0xf3, 0x50, 0xde, 0x72, 0x58, 0xf3, 0x11, 0xea, 0xa0, 0x7f, 0x2c, 0xc1, 0x6c, 0x54, 0x03, 0x69, 0xd8, 0x68, 0x1e, 0xa9, 0xd9, 0x86, 0x6f, 0x96, 0x59, 0x48, 0xd8, 0xa7, 0x84, 0x16, 0x61, 0x94, 0xb6, 0x42, 0x05, 0xab, 0x06, 0xee, 0x98, 0x86, 0x0d, 0xdc, 0x08, 0xd4, 0x0c, 0x3a, 0x0a, 0x77, 0xa9, 0xee, 0x0a, 0x77, 0x4f, 0x4a, 0x6b, 0xf1, 0x4b, 0x6b, 0x71, 0x74, 0xa7, 0x2f, 0x86, 0xee, 0xdc, 0x86, 0x31, 0x56, 0x12, 0x61, 0x18, 0x75, 0x83, 0x60, 0x6b, 0x4f, 0xa9, 0x44, 0x9f, 0x5b, 0x46, 0x19, 0x23, 0x85, 0x57, 0x62, 0x6c, 0xfe, 0xb2, 0x5d, 0xf6, 0x40, 0x65, 0xbb, 0xb6, 0x10, 0x0e, 0x92, 0x84, 0x70, 0xe2, 0x1a, 0x5d, 0xae, 0xeb, 0x1a, 0x5d, 0xeb, 0x9c, 0x31, 0x10, 0xbf, 0x34, 0xe2, 0x55, 0x8a, 0x8e, 0x1c, 0xa0, 0x52, 0x34, 0x78, 0xb0, 0x4a, 0x91, 0xa0, 0x64, 0x31, 0xf4, 0x18, 0x4a, 0x16, 0xf9, 0x47, 0x52, 0xb2, 0x28, 0xfc, 0x55, 0x82, 0xc5, 0xa4, 0xed, 0x9f, 0x4d, 0xff, 0x2b, 0xb5, 0xfb, 0xdf, 0xb0, 0x13, 0xdb, 0x16, 0x1c, 0x6b, 0xb6, 0x8c, 0x04, 0xda, 0x08, 0x5c, 0xcf, 0x34, 0x1f, 0xda, 0x14, 0xe2, 0x6f, 0x24, 0x38, 0x8a, 0x79, 0xb7, 0x03, 0xa7, 0xc2, 0x9e, 0x60, 0x16, 0xe7, 0xdb, 0x12, 0xa7, 0x02, 0x20, 0xda, 0x4a, 0xe3, 0xf8, 0x03, 0x29, 0x86, 0x3f, 0x68, 0x0b, 0xed, 0x52, 0x09, 0x42, 0xbb, 0xc2, 0x07, 0x12, 0x1c, 0x0f, 0xfd, 0xba, 0xc1, 0x89, 0x6d, 0xd9, 0xb7, 0x13, 0x86, 0x52, 0xf5, 0x56, 0x02, 0xdc, 0x5b, 0xb7, 0x94, 0x2a, 0xee, 0xf6, 0xd5, 0x87, 0xb6, 0x8d, 0xb6, 0x4c, 0xbc, 0x27, 0x7e, 0x2a, 0xe1, 0x67, 0xbc, 0x45, 0x12, 0x75, 0xf3, 0xcc, 0x40, 0x8e, 0xf5, 0x53, 0xb5, 0x4f, 0x81, 0x7b, 0x8b, 0x4e, 0x41, 0x73, 0x17, 0x4b, 0xc5, 0xdf, 0xc5, 0xc2, 0xd2, 0xfa, 0x11, 0x1a, 0xf6, 0x25, 0x09, 0xe6, 0x13, 0x34, 0xb8, 0xb5, 0xb2, 0xd3, 0x92, 0x2f, 0x3b, 0xdd, 0xed, 0xc2, 0x85, 0x20, 0x2f, 0xfc, 0x32, 0x05, 0x2f, 0x1e, 0xac, 0xc9, 0xff, 0xd0, 0x4c, 0xa2, 0x95, 0xbb, 0x4c, 0xf9, 0x72, 0x97, 0xf7, 0x00, 0x75, 0x36, 0x93, 0x31, 0xef, 0x70, 0x26, 0x5e, 0xb1, 0x5a, 0x1e, 0xee, 0xe8, 0x08, 0x47, 0xe3, 0xd0, 0xa7, 0x9a, 0x06, 0xb1, 0xcc, 0x0a, 0x5d, 0xb0, 0x01, 0xd9, 0xbb, 0x44, 0x45, 0x18, 0x09, 0xf4, 0x45, 0x9a, 0x46, 0xc5, 0x3d, 0xa9, 0xf4, 0xcb, 0xc3, 0xbe, 0x76, 0xc5, 0xdb, 0x46, 0x65, 0xbf, 0xf0, 0x66, 0x1a, 0x6e, 0x1c, 0xe0, 0x23, 0x02, 0x74, 0xaf, 0xdd, 0x6b, 0x0e, 0x0a, 0x3e, 0xd1, 0x89, 0x25, 0xd9, 0x97, 0xa5, 0x3f, 0xa4, 0xf3, 0xb5, 0x30, 0xa7, 0xcc, 0x5f, 0x97, 0x9e, 0x83, 0xae, 0xcb, 0x02, 0xa0, 0x60, 0xeb, 0x26, 0xab, 0xf7, 0xa4, 0xe5, 0xbc, 0xee, 0x53, 0x42, 0x37, 0xa5, 0xe7, 0xad, 0x62, 0xc6, 0xb7, 0x8a, 0x85, 0x3f, 0x4a, 0x70, 0xb5, 0xcb, 0x2f, 0x20, 0x04, 0x18, 0x24, 0x01, 0x86, 0xc7, 0xab, 0xb8, 0x85, 0xcf, 0xa7, 0xe1, 0x6a, 0x97, 0x5d, 0xaa, 0xff, 0xa9, 0xb6, 0x1a, 0x70, 0xe8, 0x3d, 0x62, 0x87, 0xde, 0x1b, 0xdf, 0xa1, 0x0b, 0x55, 0x47, 0xe4, 0x00, 0xfa, 0x44, 0x0e, 0xe0, 0x73, 0x69, 0xb8, 0xdc, 0x4d, 0xa7, 0x6d, 0x3c, 0xcb, 0x8f, 0x25, 0xf9, 0x89, 0xe5, 0xb7, 0x2c, 0xff, 0x7d, 0x09, 0x2e, 0x24, 0xed, 0x1a, 0xfe, 0xb7, 0x36, 0x79, 0xf1, 0x5e, 0x55, 0xf8, 0xad, 0x04, 0xe7, 0x13, 0x75, 0x1a, 0x1f, 0x9a, 0x0b, 0xe0, 0x9e, 0xa2, 0x52, 0x07, 0x3a, 0x45, 0x15, 0xbe, 0x97, 0x83, 0x4b, 0x5d, 0x7c, 0x32, 0xd5, 0xb6, 0x1c, 0x92, 0x6f, 0x39, 0x66, 0x20, 0xd7, 0x5c, 0x0e, 0xa6, 0xf3, 0x59, 0x19, 0xbc, 0x5b, 0xbc, 0x94, 0x4a, 0xfa, 0x10, 0x52, 0x2a, 0xdd, 0xd6, 0x57, 0x7b, 0x0f, 0x37, 0xa5, 0x92, 0x79, 0xa4, 0x29, 0x95, 0xbe, 0xae, 0x53, 0x2a, 0xf7, 0x81, 0x35, 0x7c, 0x33, 0x89, 0xec, 0x18, 0xdb, 0x1f, 0x72, 0x54, 0x76, 0xbb, 0xc6, 0xa9, 0x14, 0xef, 0xa8, 0x5c, 0x0b, 0xde, 0x6a, 0x37, 0x92, 0xac, 0xdf, 0x9f, 0xc7, 0x51, 0x79, 0x88, 0xa1, 0xf2, 0x2a, 0x8c, 0xb7, 0xa9, 0x53, 0xd9, 0xc2, 0xf5, 0x16, 0xfc, 0x1c, 0x85, 0x3f, 0x1f, 0xaa, 0x38, 0x25, 0x4d, 0x76, 0x58, 0xd8, 0x10, 0x8e, 0x36, 0x78, 0xb7, 0x3b, 0xca, 0xb5, 0x47, 0xba, 0x29, 0xd7, 0x76, 0xb4, 0xee, 0x0e, 0x72, 0x5a, 0x77, 0x5b, 0x07, 0xb1, 0xa1, 0xe4, 0xb9, 0x96, 0xfc, 0x01, 0x72, 0x2d, 0xc3, 0x07, 0xcb, 0xb5, 0x5c, 0x87, 0x9c, 0x86, 0x2b, 0xca, 0xbe, 0xab, 0x9a, 0xd1, 0x2d, 0xc6, 0x40, 0xa9, 0xa9, 0x2a, 0xa2, 0xe7, 0x61, 0xe0, 0xe3, 0x3a, 0x21, 0xde, 0xdf, 0x87, 0x34, 0x9b, 0x8b, 0x85, 0xcc, 0x39, 0x97, 0xbc, 0xc9, 0xed, 0xf6, 0xe0, 0x5a, 0x75, 0xa3, 0xac, 0x10, 0xd6, 0x5e, 0x1c, 0xd6, 0x7b, 0x0b, 0x94, 0x5e, 0xae, 0x1b, 0xcb, 0x44, 0x94, 0x23, 0x3a, 0xfa, 0x18, 0x72, 0x44, 0x63, 0x8f, 0x26, 0x47, 0xf4, 0x46, 0x1a, 0x2e, 0x24, 0xfd, 0x40, 0xf4, 0xc3, 0x77, 0xd6, 0x1b, 0x5e, 0xd4, 0xe5, 0xd6, 0x51, 0xaf, 0x24, 0xfe, 0xba, 0xd1, 0x17, 0x6c, 0xb5, 0xb9, 0x9d, 0x5e, 0xbf, 0xdb, 0xe1, 0x87, 0x14, 0x19, 0x41, 0x48, 0x71, 0x48, 0x99, 0xe6, 0xc2, 0x6f, 0x52, 0xb0, 0x90, 0xe4, 0xeb, 0x57, 0xe1, 0x7a, 0xf0, 0x63, 0x99, 0xd4, 0x41, 0x63, 0x99, 0xc3, 0x5a, 0x45, 0xfe, 0xec, 0xf6, 0x08, 0x66, 0xb7, 0xe5, 0xeb, 0x7a, 0xe3, 0x27, 0x9d, 0x3e, 0x48, 0x41, 0xc2, 0xef, 0x72, 0x3f, 0x1a, 0x93, 0xc9, 0x2b, 0x1a, 0xf6, 0x72, 0x8b, 0x86, 0xad, 0x6e, 0x97, 0x4c, 0xfc, 0x6e, 0x97, 0xc2, 0x3f, 0x52, 0x70, 0xee, 0x30, 0x3c, 0xca, 0x47, 0x74, 0xd2, 0xdb, 0xea, 0x39, 0x99, 0x04, 0xf5, 0x9c, 0xc2, 0x3f, 0x53, 0x70, 0x3e, 0xd1, 0x67, 0xd2, 0x4f, 0x26, 0xbe, 0x63, 0xe2, 0xbd, 0x04, 0x6d, 0x26, 0x49, 0x52, 0xff, 0xd3, 0x69, 0xd1, 0xc4, 0x8b, 0x3a, 0x94, 0x9e, 0x4c, 0x7c, 0x68, 0x83, 0x54, 0xa6, 0x9b, 0xef, 0x32, 0x7e, 0x91, 0x82, 0xc5, 0x84, 0x9f, 0xaf, 0x3f, 0x59, 0x07, 0xdf, 0x3a, 0xcc, 0x13, 0x18, 0xa2, 0x3f, 0xd7, 0xf5, 0x0a, 0xc1, 0x16, 0x7d, 0xd5, 0x71, 0x98, 0x58, 0xbb, 0xbf, 0x76, 0x6b, 0xb3, 0xbc, 0x5e, 0xda, 0xd8, 0x5c, 0x93, 0xcb, 0x9b, 0xff, 0x77, 0x67, 0xad, 0x5c, 0xba, 0x75, 0x7f, 0x79, 0xa3, 0x74, 0x33, 0xff, 0x14, 0x9a, 0x81, 0xa9, 0xce, 0xc7, 0xcb, 0x1b, 0x1b, 0x65, 0x7a, 0x37, 0x2f, 0xa1, 0x93, 0x70, 0xbc, 0x93, 0x60, 0x75, 0xe3, 0xf6, 0xdd, 0x35, 0x46, 0x92, 0x5a, 0x79, 0x15, 0x8e, 0xa9, 0x66, 0x95, 0x37, 0x07, 0x2b, 0xde, 0x1f, 0x20, 0xdf, 0x71, 0x22, 0xf3, 0x3b, 0xd2, 0xff, 0x5f, 0x7c, 0xa0, 0x93, 0x9d, 0xfa, 0x56, 0x51, 0x35, 0xab, 0x8b, 0xed, 0x7f, 0xc4, 0x7c, 0x5e, 0xd7, 0x2a, 0x8b, 0x0f, 0x4c, 0xf7, 0xcf, 0x9f, 0xd9, 0xbf, 0x32, 0xdf, 0x50, 0x6a, 0xfa, 0xde, 0xc5, 0xad, 0x0c, 0xbd, 0x77, 0xe9, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf3, 0xc4, 0x77, 0x00, 0x78, 0x5a, 0x00, 0x00, }, // uber/cadence/api/v1/service_workflow.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x5a, 0x5b, 0x6f, 0xdc, 0xc6, 0xf5, 0x07, 0x57, 0xf7, 0xb3, 0x92, 0x2c, 0x8d, 0x6e, 0xeb, 0x95, 0x75, 0xa3, 0xe3, 0xfc, 0xf5, 0x77, 0xec, 0x55, 0x25, 0xc5, 0x97, 0x38, 0x69, 0x03, 0x79, 0x6d, 0x39, 0x2a, 0xec, 0x54, 0xa5, 0x54, 0x1b, 0xed, 0x0b, 0x31, 0x22, 0x67, 0x57, 0x63, 0x71, 0x49, 0x6a, 0x38, 0x94, 0xbc, 0xc9, 0x43, 0xd1, 0x22, 0x48, 0x81, 0xa2, 0x2d, 0xfa, 0xd8, 0x02, 0x05, 0xfa, 0xd0, 0x87, 0x3c, 0x35, 0x28, 0xd0, 0xa7, 0xbe, 0x17, 0xfd, 0x1e, 0xfd, 0x04, 0x79, 0xea, 0x6b, 0x50, 0x70, 0x66, 0xb8, 0x37, 0x91, 0xdc, 0x95, 0x94, 0xc0, 0x4e, 0xdf, 0x76, 0x66, 0xce, 0xef, 0xcc, 0x99, 0x73, 0x9b, 0x33, 0x67, 0x09, 0x37, 0xc3, 0x03, 0xc2, 0xd6, 0x2c, 0x6c, 0x13, 0xd7, 0x22, 0x6b, 0xd8, 0xa7, 0x6b, 0x27, 0xeb, 0x6b, 0x01, 0x61, 0x27, 0xd4, 0x22, 0xe6, 0xa9, 0xc7, 0x8e, 0x2a, 0x8e, 0x77, 0x5a, 0xf2, 0x99, 0xc7, 0x3d, 0x34, 0x15, 0xd1, 0x96, 0x14, 0x6d, 0x09, 0xfb, 0xb4, 0x74, 0xb2, 0x5e, 0x5c, 0xac, 0x7a, 0x5e, 0xd5, 0x21, 0x6b, 0x82, 0xe4, 0x20, 0xac, 0xac, 0xd9, 0x21, 0xc3, 0x9c, 0x7a, 0xae, 0x04, 0x15, 0x97, 0x3a, 0xd7, 0x39, 0xad, 0x91, 0x80, 0xe3, 0x9a, 0xaf, 0x08, 0x96, 0x93, 0x24, 0xb0, 0xbc, 0x5a, 0xad, 0xc1, 0x62, 0x25, 0x89, 0xe2, 0x90, 0x06, 0xdc, 0x63, 0xf5, 0x78, 0x97, 0x24, 0x92, 0xe3, 0x90, 0x34, 0x08, 0xf4, 0x24, 0x02, 0x8e, 0x83, 0x23, 0x87, 0x06, 0x3c, 0x8b, 0xa6, 0x5d, 0x07, 0xfa, 0x3f, 0x35, 0x58, 0x32, 0x22, 0xf9, 0x19, 0x7f, 0xa1, 0x56, 0x1e, 0xbf, 0x22, 0x56, 0x18, 0x9d, 0xd8, 0x20, 0xc7, 0x21, 0x09, 0x38, 0x9a, 0x85, 0x41, 0xdb, 0xab, 0x61, 0xea, 0x16, 0xb4, 0x65, 0x6d, 0x75, 0xc4, 0x50, 0x23, 0xf4, 0x13, 0x40, 0x31, 0x37, 0x93, 0xc4, 0xa0, 0x42, 0x6e, 0x59, 0x5b, 0xcd, 0x6f, 0xbc, 0x5d, 0x4a, 0x50, 0x6e, 0xe9, 0xec, 0x16, 0x93, 0xa7, 0x9d, 0x53, 0xa8, 0x08, 0xc3, 0xd4, 0x26, 0x2e, 0xa7, 0xbc, 0x5e, 0xe8, 0x13, 0x1b, 0x36, 0xc6, 0x91, 0x28, 0x8c, 0xe0, 0xc0, 0x73, 0x0b, 0xfd, 0x52, 0x14, 0x39, 0xd2, 0xff, 0xaa, 0xc1, 0xf2, 0x23, 0x8a, 0xab, 0xae, 0x17, 0x90, 0xef, 0xc0, 0x39, 0xf4, 0x2f, 0x34, 0x58, 0xc9, 0x90, 0x37, 0xf0, 0x3d, 0x37, 0x20, 0xa9, 0x02, 0xbf, 0x84, 0x05, 0x5b, 0x82, 0x39, 0xb5, 0xcc, 0x4b, 0xcb, 0x3e, 0xdf, 0x64, 0x76, 0x66, 0x51, 0xff, 0x03, 0xc0, 0xc2, 0xde, 0x85, 0xdc, 0x63, 0x09, 0xf2, 0x0d, 0xd1, 0xa8, 0x2d, 0x64, 0x1a, 0x31, 0x20, 0x9e, 0xda, 0xb1, 0xd1, 0x36, 0x8c, 0x35, 0x08, 0x78, 0xdd, 0x27, 0x42, 0x4b, 0xf9, 0x8d, 0x95, 0x4c, 0xb1, 0xf7, 0xeb, 0x3e, 0x31, 0x46, 0x4f, 0x5b, 0x46, 0xe8, 0x01, 0x8c, 0x44, 0x9e, 0x6f, 0x46, 0xae, 0x2f, 0xfc, 0x22, 0xbf, 0xb1, 0x90, 0xc8, 0x63, 0x1f, 0x07, 0x47, 0x4f, 0x69, 0xc0, 0x8d, 0x61, 0xae, 0x7e, 0xa1, 0x0d, 0x18, 0xa0, 0xae, 0x1f, 0xf2, 0xc2, 0x80, 0xc0, 0x5d, 0x4b, 0xc4, 0xed, 0xe2, 0xba, 0xe3, 0x61, 0xdb, 0x90, 0xa4, 0x08, 0xc3, 0x72, 0x43, 0xd5, 0xa6, 0x08, 0x1d, 0x93, 0x7b, 0xa6, 0xe5, 0x78, 0x01, 0x31, 0xa3, 0x6c, 0xe0, 0x85, 0xbc, 0x30, 0x28, 0xd8, 0x5d, 0x2d, 0xc9, 0x6c, 0x51, 0x8a, 0xb3, 0x45, 0xe9, 0x91, 0xca, 0x26, 0xc6, 0xb5, 0x06, 0x0b, 0xa1, 0xdd, 0x7d, 0xaf, 0x1c, 0xe1, 0xf7, 0x25, 0x1c, 0xbd, 0x80, 0x79, 0x71, 0xa4, 0x14, 0xee, 0x43, 0xdd, 0xb8, 0xcf, 0x45, 0xe8, 0x24, 0xc6, 0xad, 0x4e, 0x39, 0xdc, 0x11, 0x5c, 0x0b, 0x00, 0x4c, 0xda, 0x34, 0xb2, 0xd7, 0x88, 0x58, 0x1d, 0x51, 0x33, 0x3b, 0x36, 0xb2, 0xa0, 0xd0, 0x62, 0x4f, 0x93, 0x91, 0x30, 0x20, 0xa6, 0xef, 0x39, 0xd4, 0xaa, 0x17, 0x60, 0x59, 0x5b, 0x1d, 0xdf, 0xb8, 0x99, 0x69, 0xb9, 0x1d, 0xdb, 0x88, 0x20, 0xbb, 0x02, 0x61, 0xcc, 0x9c, 0x26, 0x4d, 0xa3, 0x32, 0x8c, 0x32, 0xc2, 0x59, 0x3d, 0x66, 0x9c, 0x17, 0x27, 0x5d, 0x4e, 0x64, 0x6c, 0x44, 0x84, 0x8a, 0x5d, 0x9e, 0x35, 0x07, 0xe8, 0x3a, 0x8c, 0x59, 0x2c, 0xb2, 0x8d, 0x75, 0x48, 0xec, 0xd0, 0x21, 0x85, 0x51, 0x71, 0x96, 0xd1, 0x68, 0x72, 0x4f, 0xcd, 0xa1, 0xdb, 0xd0, 0x5f, 0x23, 0x35, 0xaf, 0x30, 0xa6, 0x74, 0x99, 0xb4, 0xc3, 0x33, 0x52, 0xf3, 0x0c, 0x41, 0x86, 0x0c, 0x98, 0x0c, 0x08, 0x66, 0xd6, 0xa1, 0x89, 0x39, 0x67, 0xf4, 0x20, 0xe4, 0x24, 0x28, 0x8c, 0x0b, 0xec, 0x8d, 0x44, 0xec, 0x9e, 0xa0, 0xde, 0x6a, 0x10, 0x1b, 0x13, 0x41, 0xc7, 0x0c, 0xda, 0x84, 0xc1, 0x43, 0x82, 0x6d, 0xc2, 0x0a, 0x57, 0x04, 0xa3, 0xf9, 0x44, 0x46, 0x1f, 0x09, 0x12, 0x43, 0x91, 0xa2, 0x07, 0x90, 0xb7, 0x89, 0x83, 0xeb, 0xd2, 0x37, 0x0a, 0x13, 0xdd, 0x5c, 0x01, 0x04, 0xb5, 0xf0, 0x05, 0xf4, 0x01, 0x8c, 0xbe, 0xa4, 0x9c, 0x13, 0xa6, 0xc0, 0x93, 0xdd, 0xc0, 0x79, 0x49, 0xde, 0x40, 0x57, 0x28, 0x0b, 0xb8, 0xc9, 0x42, 0xd7, 0xc4, 0xbc, 0x80, 0x04, 0xba, 0x78, 0x06, 0xbd, 0x1f, 0xdf, 0x88, 0x06, 0x08, 0x7a, 0x23, 0x74, 0xb7, 0x38, 0x7a, 0x0e, 0x53, 0xc2, 0x28, 0xde, 0x09, 0x61, 0x0e, 0xf6, 0x63, 0x03, 0x4f, 0x09, 0xcf, 0x49, 0x4e, 0x55, 0x65, 0xe6, 0xb9, 0x3f, 0x92, 0xe4, 0xca, 0xcc, 0x93, 0x56, 0xe7, 0x14, 0x7a, 0x05, 0x4b, 0xd8, 0xe2, 0xf4, 0x84, 0x98, 0x96, 0x13, 0x06, 0xe2, 0x6c, 0xc4, 0x21, 0x96, 0x08, 0x4e, 0xb5, 0xc7, 0xb4, 0x10, 0x74, 0x3d, 0x71, 0x8f, 0x2d, 0x81, 0x2d, 0x4b, 0xe8, 0x5e, 0x8c, 0x54, 0xdb, 0x5d, 0xc3, 0x19, 0xab, 0xfa, 0x3d, 0x58, 0x4c, 0xcb, 0x8c, 0x2a, 0x81, 0xcf, 0xc0, 0x60, 0xa4, 0x2b, 0x6a, 0xab, 0xd4, 0x38, 0xc0, 0x42, 0x77, 0xc7, 0xd6, 0x19, 0xe8, 0xc9, 0xc0, 0xad, 0xa0, 0xee, 0x5a, 0x71, 0x5e, 0x7d, 0x0a, 0x43, 0x2a, 0xf8, 0x04, 0x3a, 0xbf, 0xb1, 0x91, 0xec, 0x67, 0x59, 0xc9, 0xd9, 0x88, 0x59, 0xe8, 0x37, 0xe0, 0x7a, 0xe6, 0x9e, 0x52, 0x62, 0xfd, 0x3d, 0x58, 0x4e, 0x2f, 0x07, 0xb2, 0x4f, 0xf5, 0xaf, 0x1c, 0x2c, 0xee, 0xd1, 0xaa, 0x8b, 0x9d, 0xef, 0x42, 0x25, 0xd1, 0x9e, 0xec, 0xfa, 0x3b, 0x93, 0xdd, 0x12, 0xe4, 0x03, 0x71, 0x16, 0xd3, 0xc5, 0x35, 0x22, 0x6e, 0x87, 0x11, 0x03, 0xe4, 0xd4, 0xc7, 0xb8, 0x46, 0xd0, 0x87, 0x30, 0xaa, 0x08, 0xe4, 0xfd, 0x31, 0xd8, 0xc3, 0xfd, 0xa1, 0x58, 0xee, 0x88, 0x5b, 0xa4, 0x00, 0x43, 0x96, 0xe7, 0x72, 0xe6, 0x39, 0x22, 0x9d, 0x8f, 0x1a, 0xf1, 0x50, 0x5f, 0x81, 0xa5, 0x54, 0x3d, 0x2a, 0x33, 0x7d, 0xad, 0xc1, 0xff, 0x29, 0x1a, 0xca, 0x0f, 0xb3, 0xef, 0xe7, 0x17, 0x30, 0x26, 0xaf, 0x91, 0xcb, 0x7b, 0xd3, 0xa8, 0x60, 0x14, 0x33, 0xee, 0xd0, 0x51, 0xae, 0xab, 0x8e, 0xfa, 0x2e, 0xa1, 0xa3, 0xfe, 0x76, 0x1d, 0x6d, 0xc1, 0x6a, 0xf7, 0xf3, 0x67, 0xfb, 0xeb, 0xe7, 0x1a, 0xdc, 0xea, 0xc6, 0xa3, 0x2d, 0x20, 0x9f, 0x77, 0x06, 0xe4, 0x07, 0xc9, 0x2a, 0xec, 0xcd, 0x2e, 0xcd, 0xd0, 0x5c, 0x83, 0xdb, 0x3d, 0xca, 0xa1, 0xac, 0xff, 0x65, 0x0e, 0x16, 0x0c, 0x12, 0x90, 0x37, 0xa6, 0x64, 0x6f, 0x96, 0xe5, 0x7d, 0xad, 0x65, 0x39, 0xba, 0x07, 0x05, 0x9b, 0x58, 0x34, 0x88, 0x72, 0x71, 0x85, 0xba, 0x34, 0x38, 0x34, 0xc9, 0x09, 0x71, 0x1b, 0x21, 0xd7, 0x67, 0xcc, 0xc4, 0xeb, 0xdb, 0x62, 0xf9, 0x71, 0xb4, 0xba, 0x63, 0x77, 0x44, 0xe7, 0x40, 0x67, 0x74, 0x96, 0x60, 0x2a, 0x38, 0xa2, 0xbe, 0xa9, 0xbc, 0x8b, 0x11, 0xec, 0xfb, 0x4e, 0x5d, 0xc4, 0xe0, 0xb0, 0x31, 0x19, 0x2d, 0x49, 0x85, 0x1a, 0x72, 0x21, 0xca, 0xd4, 0x69, 0xfa, 0xca, 0xf6, 0x91, 0x3f, 0xe5, 0xe0, 0x86, 0xd2, 0x69, 0x19, 0xbb, 0x16, 0xf9, 0x5f, 0x48, 0x6d, 0xd3, 0x30, 0x60, 0xe1, 0x30, 0x88, 0x93, 0x9a, 0x1c, 0xa0, 0x4d, 0x98, 0x95, 0x97, 0x7b, 0xb3, 0xb4, 0x55, 0x0a, 0x19, 0x14, 0x64, 0x53, 0x62, 0xb5, 0x29, 0x93, 0x50, 0xcf, 0x2a, 0xbc, 0xdd, 0x4d, 0x3b, 0xca, 0x65, 0xff, 0x9e, 0x83, 0x95, 0x7d, 0xc2, 0x6a, 0xd4, 0xc5, 0x9c, 0xbc, 0xe9, 0x6e, 0x7b, 0x17, 0x86, 0x6c, 0xc2, 0x31, 0x75, 0x02, 0xf5, 0x9c, 0xc8, 0x4e, 0x59, 0x31, 0x71, 0x9b, 0x51, 0x06, 0x3a, 0x8c, 0x72, 0x21, 0xfd, 0xbe, 0x05, 0x7a, 0x96, 0xd2, 0x94, 0x6e, 0xff, 0x13, 0x3d, 0x7e, 0x49, 0x60, 0x31, 0x7a, 0xf0, 0xc6, 0xa8, 0xf6, 0x00, 0xe6, 0x44, 0xbb, 0xc2, 0xb4, 0x3c, 0x37, 0xa0, 0x01, 0x27, 0xae, 0x55, 0x37, 0x1d, 0x72, 0x42, 0x1c, 0xa1, 0xeb, 0xb4, 0xb7, 0xc2, 0x8f, 0x23, 0x4c, 0xb9, 0x09, 0x79, 0x1a, 0x21, 0x8c, 0x99, 0xe3, 0xa4, 0x69, 0xfd, 0xeb, 0x3e, 0x58, 0xc9, 0x38, 0xb7, 0x8a, 0x6c, 0x07, 0xe6, 0x9a, 0x2a, 0xb7, 0x3c, 0xb7, 0x42, 0xab, 0xaa, 0xba, 0x55, 0x59, 0x7c, 0xb3, 0xb7, 0x53, 0x96, 0x5b, 0xa1, 0xc6, 0x2c, 0x49, 0x9c, 0x8f, 0xce, 0x7d, 0x56, 0x9d, 0x26, 0x75, 0x2b, 0x9e, 0xd2, 0xe9, 0xcd, 0xde, 0x76, 0xdb, 0x71, 0x2b, 0x5e, 0xf3, 0x8d, 0xd4, 0x36, 0x8d, 0x5e, 0x00, 0xf2, 0x89, 0x6b, 0x53, 0xb7, 0x6a, 0x8a, 0xfa, 0x94, 0x72, 0x4a, 0x82, 0x42, 0xdf, 0x72, 0xdf, 0x6a, 0x7e, 0x63, 0x35, 0xd9, 0x53, 0x25, 0xf9, 0x96, 0xa4, 0xae, 0x0b, 0xe6, 0x93, 0x7e, 0xdb, 0x24, 0x25, 0x01, 0xfa, 0x29, 0x4c, 0xc4, 0x8c, 0xad, 0x43, 0xea, 0xd8, 0x8c, 0xb8, 0x85, 0x7e, 0xc1, 0xb6, 0x94, 0xc5, 0xb6, 0x1c, 0xd1, 0xb6, 0x4b, 0x7e, 0xc5, 0x6f, 0x59, 0x62, 0xc4, 0x45, 0x7b, 0x4d, 0xd6, 0x71, 0xc6, 0x57, 0x4f, 0xee, 0x4c, 0x89, 0x1f, 0x29, 0xda, 0x36, 0xa6, 0xf1, 0xa4, 0xfe, 0x59, 0x1f, 0x4c, 0x0b, 0x8f, 0x89, 0xd5, 0xf7, 0x9a, 0x9c, 0xfd, 0x3e, 0x0c, 0x08, 0x0f, 0x55, 0x05, 0x8e, 0x9e, 0xc9, 0x49, 0x08, 0x6c, 0x48, 0x00, 0x32, 0x61, 0x56, 0x86, 0x09, 0x23, 0x2f, 0x89, 0xc5, 0x23, 0xff, 0xb4, 0xa9, 0x10, 0xaa, 0x5f, 0x44, 0xc9, 0xff, 0xa7, 0x47, 0x89, 0x21, 0x10, 0xe5, 0x18, 0x60, 0x4c, 0x1f, 0x27, 0xcc, 0x66, 0xc5, 0xe1, 0xc0, 0x37, 0x15, 0x87, 0x7f, 0xd1, 0x60, 0xa6, 0xc3, 0x0c, 0x2a, 0xf6, 0x3e, 0x84, 0xd1, 0xf8, 0x78, 0x41, 0xe8, 0xc4, 0x65, 0x53, 0x97, 0x02, 0x50, 0x9d, 0x23, 0x02, 0xa0, 0x1d, 0x18, 0x6f, 0xd5, 0x0f, 0xb1, 0x95, 0xb1, 0xf4, 0x6e, 0x7a, 0x21, 0xb6, 0x31, 0x76, 0xdc, 0x3a, 0xd4, 0xbf, 0xd2, 0x60, 0x2e, 0xce, 0x16, 0x8d, 0x46, 0x50, 0x17, 0x7f, 0x69, 0xeb, 0x2c, 0xe5, 0xce, 0xd7, 0x59, 0x7a, 0x02, 0xe3, 0x0d, 0x6c, 0xb3, 0xbd, 0x35, 0x9e, 0xd2, 0xde, 0x8a, 0x19, 0xc8, 0xf6, 0x16, 0x6f, 0x19, 0x45, 0x45, 0x14, 0x75, 0x2d, 0x27, 0xb4, 0x89, 0xd9, 0x64, 0x18, 0x70, 0xcc, 0x43, 0x79, 0x3d, 0x0d, 0x1b, 0x33, 0x6a, 0x3d, 0x66, 0xb2, 0x27, 0x16, 0xf5, 0x7f, 0xe4, 0xa0, 0x70, 0xf6, 0xc4, 0xca, 0x34, 0xef, 0xc1, 0x90, 0xef, 0x39, 0x0e, 0x61, 0x41, 0x41, 0x13, 0x21, 0xbe, 0x94, 0x6c, 0x15, 0x41, 0x23, 0xc2, 0x2f, 0xa6, 0x47, 0xcf, 0x60, 0xe2, 0x8c, 0x20, 0x52, 0x39, 0xd7, 0x33, 0xcf, 0x26, 0xc5, 0x32, 0xc6, 0x79, 0xdb, 0x18, 0xbd, 0x80, 0x09, 0x1f, 0x33, 0x4e, 0x5b, 0x12, 0xb4, 0x0a, 0xa4, 0x5b, 0x99, 0xec, 0x76, 0x63, 0x90, 0xcc, 0xc0, 0xc6, 0x15, 0xbf, 0x7d, 0xe2, 0x32, 0x7d, 0x41, 0xfd, 0x0e, 0xcc, 0x3f, 0x21, 0x3c, 0x5e, 0x08, 0x1e, 0xd6, 0x1f, 0x09, 0x8f, 0xe8, 0xe2, 0x30, 0xfa, 0xef, 0xfa, 0xe1, 0x5a, 0x32, 0x4e, 0xa9, 0xfd, 0xe7, 0x30, 0xdb, 0xa8, 0x88, 0x9b, 0x4a, 0xac, 0x61, 0x5f, 0x59, 0xe1, 0x87, 0x89, 0x02, 0x66, 0xb1, 0x2c, 0xc5, 0xe9, 0x30, 0xa6, 0x78, 0x86, 0xfd, 0xc7, 0x2e, 0x67, 0x75, 0x63, 0xca, 0x3e, 0xbb, 0x12, 0x09, 0xa0, 0x2e, 0x8d, 0x7a, 0x87, 0x00, 0xb9, 0x8b, 0x0a, 0x10, 0x5f, 0x2b, 0x67, 0x05, 0xc0, 0x67, 0x57, 0x8a, 0x61, 0xe4, 0x94, 0xc9, 0x12, 0xa3, 0x09, 0xe8, 0x3b, 0x22, 0x75, 0xa5, 0xd3, 0xe8, 0x27, 0x2a, 0xc3, 0xc0, 0x09, 0x76, 0x42, 0xa2, 0x1c, 0xec, 0x76, 0xa2, 0x74, 0x69, 0x4e, 0x6e, 0x48, 0xec, 0x83, 0xdc, 0x7d, 0x2d, 0xda, 0x36, 0x4d, 0xce, 0x6f, 0x71, 0x5b, 0x3d, 0x80, 0x05, 0x11, 0xc8, 0x9d, 0x3e, 0x1b, 0x7c, 0x8b, 0xa9, 0x47, 0xff, 0x3c, 0x07, 0x8b, 0x69, 0xbb, 0x2a, 0x3f, 0x3c, 0x86, 0x85, 0x04, 0x37, 0x68, 0x44, 0x50, 0x9c, 0x14, 0x4a, 0xbd, 0x45, 0xe0, 0x33, 0xc2, 0xb1, 0x8d, 0x39, 0x36, 0x8a, 0x9d, 0x16, 0x6f, 0x6e, 0x1d, 0x6d, 0x99, 0xe0, 0xfa, 0x2d, 0x5b, 0xe6, 0x2e, 0xb6, 0x65, 0xa7, 0x97, 0x37, 0xb7, 0xd4, 0xe7, 0x60, 0xe6, 0x09, 0xe1, 0xaa, 0x7d, 0x27, 0x92, 0x98, 0x7a, 0x72, 0xff, 0x52, 0x83, 0xd9, 0xce, 0x15, 0xa5, 0x99, 0x43, 0xb8, 0x1a, 0x84, 0xbe, 0xef, 0x31, 0x4e, 0x6c, 0xd3, 0x72, 0x68, 0xf4, 0x5c, 0x3d, 0x21, 0x2c, 0x50, 0x5a, 0x49, 0xcf, 0x4b, 0x7b, 0x31, 0xaa, 0x2c, 0x40, 0xcf, 0x15, 0xc6, 0x98, 0x0b, 0x92, 0x17, 0xf4, 0xaf, 0xfa, 0x40, 0x7f, 0x92, 0xf0, 0x28, 0xfd, 0x48, 0xfe, 0x15, 0xf8, 0x9a, 0x8a, 0x99, 0x79, 0x18, 0xf1, 0x71, 0x95, 0x98, 0x01, 0xfd, 0x44, 0x5e, 0x59, 0x03, 0xc6, 0x70, 0x34, 0xb1, 0x47, 0x3f, 0x21, 0xe8, 0x6d, 0xb8, 0xe2, 0x92, 0x57, 0x91, 0xd5, 0xaa, 0xc4, 0xe4, 0xde, 0x11, 0x71, 0x55, 0x63, 0x66, 0x2c, 0x9a, 0xde, 0xc5, 0x55, 0xb2, 0x1f, 0x4d, 0xa2, 0x77, 0x00, 0x9d, 0x62, 0xca, 0xcd, 0x8a, 0xc7, 0x4c, 0x97, 0x9c, 0xca, 0x57, 0xbf, 0xa8, 0x38, 0x86, 0x8d, 0x2b, 0xd1, 0xca, 0xb6, 0xc7, 0x3e, 0x26, 0xa7, 0xe2, 0xb9, 0x8f, 0x4c, 0xb8, 0xaa, 0xfe, 0xfd, 0x54, 0xdd, 0x81, 0x0a, 0x75, 0x38, 0x61, 0xf2, 0xd2, 0x1c, 0x14, 0x97, 0xe6, 0x5b, 0x89, 0xe7, 0x11, 0xf0, 0x6d, 0x41, 0x2c, 0xee, 0xcd, 0x59, 0xc5, 0xa6, 0x63, 0x1e, 0x5d, 0x87, 0x31, 0xd1, 0x2e, 0xc0, 0xcc, 0x3a, 0xa4, 0x27, 0x58, 0x36, 0xdc, 0x86, 0x8d, 0xd1, 0x68, 0x72, 0x4b, 0xcd, 0x65, 0x55, 0x4a, 0xc3, 0xdf, 0x54, 0xa5, 0xf4, 0x6f, 0x0d, 0xae, 0x67, 0x5a, 0x5c, 0xf9, 0xe0, 0x5d, 0x18, 0x52, 0x47, 0xc9, 0x2c, 0x99, 0x62, 0x58, 0x4c, 0x8c, 0x7e, 0x00, 0x79, 0x86, 0x4f, 0xcd, 0x18, 0x2b, 0x03, 0x2a, 0x39, 0x6d, 0x3c, 0xc2, 0x1c, 0x3f, 0x74, 0xbc, 0x03, 0x03, 0x18, 0x3e, 0x55, 0x8c, 0x92, 0xcc, 0xdb, 0x97, 0x64, 0xde, 0x22, 0x0c, 0x4b, 0x5d, 0x12, 0x5b, 0x95, 0x20, 0x8d, 0xb1, 0xfe, 0x37, 0x0d, 0x46, 0xb7, 0x09, 0xe6, 0x21, 0x23, 0xdb, 0x0e, 0xae, 0x06, 0x88, 0xc2, 0x46, 0xc2, 0x93, 0x08, 0x3b, 0x8c, 0x60, 0x3b, 0xd2, 0x76, 0xcd, 0x77, 0x48, 0x14, 0x6b, 0x84, 0x31, 0x8f, 0x99, 0xc4, 0xc5, 0x07, 0x0e, 0x91, 0x6d, 0x98, 0x61, 0xe3, 0xf6, 0xd9, 0x4e, 0x98, 0xc4, 0x95, 0x63, 0xd8, 0xe3, 0x08, 0xf5, 0x58, 0x82, 0xd0, 0x1d, 0x98, 0xc5, 0x21, 0xf7, 0x2a, 0x1e, 0x3b, 0xc5, 0x4c, 0x3c, 0x36, 0x62, 0x76, 0x39, 0x59, 0x28, 0xb5, 0xaf, 0x2a, 0x98, 0xfe, 0x1b, 0x0d, 0xe6, 0x0d, 0x52, 0x61, 0x24, 0x38, 0x6c, 0xfc, 0xcd, 0x88, 0x83, 0xa3, 0xe0, 0xf5, 0x44, 0xa0, 0xbe, 0x08, 0xd7, 0x92, 0xa5, 0x91, 0xde, 0xb1, 0xf1, 0xc5, 0x14, 0xe4, 0xe3, 0x95, 0xad, 0xdd, 0x1d, 0xf4, 0x2b, 0x0d, 0x0a, 0x69, 0x4d, 0x7b, 0xf4, 0x6e, 0xca, 0x5f, 0x67, 0x99, 0x7f, 0xf9, 0x17, 0xef, 0x9c, 0x13, 0xa5, 0xfc, 0xf6, 0x17, 0x1a, 0xcc, 0x26, 0x37, 0x30, 0xd1, 0x05, 0xda, 0xcd, 0xc5, 0xcd, 0x73, 0x61, 0x94, 0x0c, 0xbf, 0xd7, 0x60, 0x3e, 0xa3, 0x89, 0x8a, 0xee, 0x9d, 0x83, 0x69, 0x6b, 0xfb, 0xb7, 0x78, 0xff, 0xfc, 0x40, 0x25, 0xd2, 0x67, 0x1a, 0xcc, 0xa5, 0x74, 0xf4, 0xd1, 0x66, 0x56, 0x0f, 0x39, 0x4d, 0x31, 0xef, 0x9e, 0x0f, 0xa4, 0xc4, 0xf8, 0xb3, 0x06, 0xcb, 0xdd, 0x1a, 0xcd, 0xe8, 0x52, 0x3d, 0xed, 0xe2, 0xf7, 0x2f, 0x88, 0x56, 0x12, 0x7e, 0xa9, 0xc1, 0x8d, 0x9e, 0x5a, 0xe1, 0x68, 0xeb, 0x42, 0x1b, 0xb5, 0xd9, 0xf3, 0xe1, 0x65, 0x58, 0xb4, 0x38, 0x7c, 0x72, 0x67, 0x39, 0xc5, 0xe1, 0x33, 0xdb, 0xf6, 0x29, 0x0e, 0xdf, 0xa5, 0x75, 0xfd, 0x47, 0x0d, 0x16, 0xb3, 0xbb, 0xb0, 0xe8, 0x41, 0x0a, 0xdf, 0x1e, 0x1a, 0xdb, 0xc5, 0xf7, 0x2f, 0x84, 0x55, 0xb2, 0xfd, 0x56, 0x83, 0x62, 0x7a, 0x07, 0x13, 0xdd, 0x4d, 0xae, 0xf5, 0xba, 0xf5, 0x89, 0x8b, 0xf7, 0xce, 0x8d, 0x53, 0xf2, 0xfc, 0x5a, 0x83, 0xab, 0xa9, 0x2d, 0x43, 0x74, 0x27, 0xb3, 0xcc, 0x4f, 0x95, 0xe6, 0xee, 0x79, 0x61, 0x4a, 0x98, 0x0a, 0x8c, 0xb5, 0xb5, 0x4d, 0x50, 0x46, 0xb7, 0xa7, 0xa3, 0xc3, 0x55, 0xbc, 0xd9, 0x0b, 0xa9, 0xda, 0xc7, 0x83, 0x89, 0xce, 0xa7, 0x0a, 0xba, 0xd5, 0xe3, 0x8b, 0x46, 0xee, 0x76, 0xbe, 0xf7, 0x0f, 0xfa, 0x14, 0xa6, 0x93, 0x1e, 0x8c, 0xe8, 0x7b, 0xe7, 0x78, 0x5b, 0xca, 0x8d, 0xd7, 0xcf, 0xfd, 0x1a, 0x15, 0x21, 0x99, 0xfc, 0xf8, 0x49, 0x09, 0xc9, 0xcc, 0xf7, 0x59, 0x4a, 0x48, 0x76, 0x79, 0x5d, 0x51, 0x18, 0x6f, 0x7f, 0x5d, 0xa0, 0x9b, 0x69, 0x07, 0x39, 0xfb, 0x38, 0x29, 0xbe, 0xd3, 0x13, 0x6d, 0xcb, 0x75, 0x97, 0x51, 0x52, 0xa6, 0x5c, 0x77, 0xdd, 0x9f, 0x1d, 0x29, 0xd7, 0x5d, 0x2f, 0xd5, 0xeb, 0xa7, 0x30, 0x9d, 0x54, 0xbf, 0xa4, 0x98, 0x3f, 0xa3, 0xf0, 0x4a, 0x31, 0x7f, 0x56, 0x71, 0x24, 0x23, 0x3c, 0xed, 0xcb, 0xba, 0xb4, 0x08, 0xef, 0xf2, 0xe5, 0x60, 0x5a, 0x84, 0x77, 0xfb, 0x80, 0xef, 0xa1, 0x0d, 0x73, 0x96, 0x57, 0x4b, 0x02, 0x3f, 0x9c, 0x8e, 0x51, 0x7b, 0xf2, 0xe3, 0xd4, 0x5d, 0xe6, 0x71, 0x6f, 0x57, 0xfb, 0xd9, 0x7a, 0x95, 0xf2, 0xc3, 0xf0, 0xa0, 0x64, 0x79, 0xb5, 0xb5, 0xd6, 0xef, 0x37, 0x6f, 0x53, 0xdb, 0x59, 0xab, 0x7a, 0xf2, 0xbb, 0x53, 0xf5, 0x31, 0xe7, 0xfb, 0xd8, 0xa7, 0x27, 0xeb, 0x07, 0x83, 0x62, 0x6e, 0xf3, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x51, 0x4a, 0x5f, 0xfc, 0x2a, 0x00, 0x00, }, // uber/cadence/shared/v1/history.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x4f, 0x4b, 0xfb, 0x30, 0x18, 0xc7, 0xe9, 0x7e, 0xfc, 0x04, 0x33, 0xff, 0x51, 0x50, 0x46, 0x41, 0xd8, 0xa6, 0xc2, 0x4e, 0x09, 0x9d, 0x88, 0x07, 0x4f, 0xfe, 0xc5, 0x79, 0x2c, 0xe2, 0xc1, 0x4b, 0x49, 0x93, 0xc7, 0x35, 0xe0, 0x92, 0x92, 0xa4, 0xc1, 0xbd, 0x15, 0xdf, 0x82, 0x6f, 0x52, 0xd2, 0xd6, 0x8d, 0xb8, 0x8b, 0xb7, 0x3e, 0x3c, 0x9f, 0xcf, 0x97, 0x6f, 0x9f, 0xa0, 0xd3, 0xba, 0x00, 0x4d, 0x18, 0xe5, 0x20, 0x19, 0x10, 0x53, 0x52, 0x0d, 0x9c, 0xb8, 0x94, 0x94, 0xc2, 0x58, 0xa5, 0x97, 0xb8, 0xd2, 0xca, 0xaa, 0xf8, 0xc8, 0x53, 0xb8, 0xa3, 0x70, 0x4b, 0x61, 0x97, 0x26, 0xa3, 0xc0, 0xa6, 0x95, 0xd8, 0x50, 0x93, 0x93, 0x10, 0xe1, 0x0b, 0x21, 0x37, 0xa0, 0xf1, 0x57, 0x84, 0x0e, 0x9f, 0x35, 0x95, 0x46, 0x80, 0xb4, 0x77, 0xc0, 0x84, 0x11, 0x4a, 0xce, 0xe4, 0x9b, 0x8a, 0x9f, 0xd0, 0xbe, 0x61, 0x25, 0xf0, 0xfa, 0x1d, 0x78, 0x0e, 0x0e, 0xa4, 0x1d, 0x44, 0xc3, 0x68, 0xd2, 0x9f, 0x8e, 0x70, 0xd0, 0x89, 0x56, 0x02, 0xbb, 0x14, 0x3f, 0xb6, 0xb1, 0xf7, 0x1e, 0xcc, 0xf6, 0x56, 0x66, 0x33, 0xc7, 0x0f, 0x68, 0xd7, 0x58, 0xaa, 0xed, 0x2a, 0xa9, 0xf7, 0xd7, 0xa4, 0x9d, 0xce, 0x6b, 0xa6, 0xf1, 0x67, 0x84, 0x0e, 0x5e, 0x40, 0xfb, 0x8e, 0x2d, 0x25, 0xc0, 0xc4, 0xd7, 0xe8, 0x98, 0xd5, 0x5a, 0x83, 0xb4, 0xb9, 0x6b, 0x77, 0x79, 0xf7, 0x8f, 0xb9, 0x90, 0x1c, 0x3e, 0x9a, 0xda, 0xff, 0xb3, 0xa4, 0x83, 0x02, 0x7f, 0x39, 0xf3, 0x44, 0x7c, 0x8b, 0xb6, 0xcb, 0x9f, 0xbc, 0x41, 0x6f, 0xf8, 0x6f, 0xd2, 0x9f, 0x9e, 0xfd, 0xea, 0xe6, 0xcf, 0xe7, 0xdb, 0x85, 0x7a, 0xb6, 0xf6, 0x6e, 0x2e, 0x5f, 0x2f, 0xe6, 0xc2, 0x96, 0x75, 0x81, 0x99, 0x5a, 0x90, 0xe0, 0xf8, 0x78, 0x0e, 0x92, 0x34, 0x07, 0x5f, 0x3f, 0xf4, 0x55, 0xfb, 0xe5, 0xd2, 0x62, 0xab, 0xd9, 0x9c, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0xc6, 0xf4, 0xa6, 0x9c, 0x12, 0x02, 0x00, 0x00, }, // uber/cadence/admin/v1/history.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2e, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x4f, 0x4c, 0xc9, 0xcd, 0xcc, 0xd3, 0x2f, 0x33, 0xd4, 0xcf, 0xc8, 0x2c, 0x2e, 0xc9, 0x2f, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x05, 0x29, 0xd2, 0x83, 0x2a, 0xd2, 0x03, 0x2b, 0xd2, 0x2b, 0x33, 0x54, 0xf2, 0xe4, 0x12, 0x0a, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0xf3, 0x80, 0x28, 0xf7, 0x2c, 0x49, 0xcd, 0x15, 0x92, 0xe4, 0xe2, 0x48, 0x2d, 0x4b, 0xcd, 0x2b, 0x89, 0xcf, 0x4c, 0x91, 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e, 0x62, 0x07, 0xf3, 0x3d, 0x53, 0x84, 0x24, 0xb8, 0xd8, 0xcb, 0x20, 0x1a, 0x24, 0x98, 0x20, 0x32, 0x50, 0xae, 0x52, 0x09, 0x17, 0x1f, 0xaa, 0x51, 0x42, 0x8a, 0x5c, 0x3c, 0x49, 0x45, 0x89, 0x79, 0xc9, 0x19, 0xf1, 0x25, 0xf9, 0xd9, 0xa9, 0x79, 0x60, 0xa3, 0x78, 0x82, 0xb8, 0x21, 0x62, 0x21, 0x20, 0x21, 0x21, 0x7b, 0x2e, 0xd6, 0xcc, 0x92, 0xd4, 0xdc, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x4d, 0x3d, 0xac, 0xce, 0xd4, 0xc3, 0x74, 0x63, 0x10, 0x44, 0x9f, 0x93, 0x79, 0x94, 0x69, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0x72, 0x48, 0xe8, 0x66, 0xa6, 0xe4, 0xe8, 0xa7, 0xe7, 0xeb, 0x83, 0xfd, 0x0f, 0x0f, 0x16, 0x6b, 0x30, 0xa3, 0xcc, 0x30, 0x89, 0x0d, 0x2c, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x44, 0x14, 0xd7, 0xd4, 0x3e, 0x01, 0x00, 0x00, }, // uber/cadence/shared/v1/tasklist.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x2f, 0xce, 0x48, 0x2c, 0x4a, 0x4d, 0xd1, 0x2f, 0x33, 0xd4, 0x2f, 0x49, 0x2c, 0xce, 0xce, 0xc9, 0x2c, 0x2e, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x03, 0x29, 0xd3, 0x83, 0x2a, 0xd3, 0x83, 0x28, 0xd3, 0x2b, 0x33, 0xd4, 0x8a, 0xe2, 0xe2, 0x0a, 0x49, 0x2c, 0xce, 0x0e, 0xce, 0x2f, 0x2d, 0x4a, 0x4e, 0x15, 0x12, 0xe7, 0x12, 0x0e, 0x71, 0x0c, 0xf6, 0x8e, 0x0f, 0xf6, 0x0f, 0x0d, 0x72, 0x76, 0x8d, 0xf7, 0xf4, 0x0b, 0x73, 0xf4, 0xf1, 0x74, 0x11, 0x60, 0x40, 0x97, 0xf0, 0xf0, 0x0c, 0x0e, 0xf1, 0x0f, 0x8a, 0x14, 0x60, 0x14, 0x92, 0xe2, 0x12, 0x43, 0x96, 0x70, 0x71, 0x8a, 0x77, 0x72, 0x74, 0xf6, 0xf6, 0xf1, 0x77, 0x17, 0x60, 0x72, 0x32, 0x8f, 0x32, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x47, 0x71, 0xa7, 0x5e, 0x7a, 0x6a, 0x9e, 0x3e, 0xd8, 0x65, 0x08, 0x27, 0x5b, 0x43, 0x58, 0x65, 0x86, 0x49, 0x6c, 0x60, 0x19, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x73, 0x06, 0x1c, 0xdc, 0x00, 0x00, 0x00, }, } func init() { yarpc.RegisterClientBuilder( func(clientConfig transport.ClientConfig, structField reflect.StructField) MatchingAPIYARPCClient { return NewMatchingAPIYARPCClient(clientConfig, protobuf.ClientBuilderOptions(clientConfig, structField)...) }, ) } ================================================ FILE: .gen/proto/sharddistributor/v1/canary.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/sharddistributor/v1/canary.proto package sharddistributorv1 import ( fmt "fmt" io "io" math "math" math_bits "math/bits" proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type PingRequest struct { ShardKey string `protobuf:"bytes,1,opt,name=shard_key,json=shardKey,proto3" json:"shard_key,omitempty"` Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *PingRequest) Reset() { *m = PingRequest{} } func (m *PingRequest) String() string { return proto.CompactTextString(m) } func (*PingRequest) ProtoMessage() {} func (*PingRequest) Descriptor() ([]byte, []int) { return fileDescriptor_03b11524fa4f4f94, []int{0} } func (m *PingRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *PingRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_PingRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *PingRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_PingRequest.Merge(m, src) } func (m *PingRequest) XXX_Size() int { return m.Size() } func (m *PingRequest) XXX_DiscardUnknown() { xxx_messageInfo_PingRequest.DiscardUnknown(m) } var xxx_messageInfo_PingRequest proto.InternalMessageInfo func (m *PingRequest) GetShardKey() string { if m != nil { return m.ShardKey } return "" } func (m *PingRequest) GetNamespace() string { if m != nil { return m.Namespace } return "" } type PingResponse struct { ExecutorId string `protobuf:"bytes,1,opt,name=executor_id,json=executorId,proto3" json:"executor_id,omitempty"` OwnsShard bool `protobuf:"varint,2,opt,name=owns_shard,json=ownsShard,proto3" json:"owns_shard,omitempty"` ShardKey string `protobuf:"bytes,3,opt,name=shard_key,json=shardKey,proto3" json:"shard_key,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *PingResponse) Reset() { *m = PingResponse{} } func (m *PingResponse) String() string { return proto.CompactTextString(m) } func (*PingResponse) ProtoMessage() {} func (*PingResponse) Descriptor() ([]byte, []int) { return fileDescriptor_03b11524fa4f4f94, []int{1} } func (m *PingResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *PingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_PingResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *PingResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_PingResponse.Merge(m, src) } func (m *PingResponse) XXX_Size() int { return m.Size() } func (m *PingResponse) XXX_DiscardUnknown() { xxx_messageInfo_PingResponse.DiscardUnknown(m) } var xxx_messageInfo_PingResponse proto.InternalMessageInfo func (m *PingResponse) GetExecutorId() string { if m != nil { return m.ExecutorId } return "" } func (m *PingResponse) GetOwnsShard() bool { if m != nil { return m.OwnsShard } return false } func (m *PingResponse) GetShardKey() string { if m != nil { return m.ShardKey } return "" } func init() { proto.RegisterType((*PingRequest)(nil), "uber.cadence.sharddistributor.v1.PingRequest") proto.RegisterType((*PingResponse)(nil), "uber.cadence.sharddistributor.v1.PingResponse") } func init() { proto.RegisterFile("uber/cadence/sharddistributor/v1/canary.proto", fileDescriptor_03b11524fa4f4f94) } var fileDescriptor_03b11524fa4f4f94 = []byte{ // 290 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xbb, 0x4e, 0xc3, 0x30, 0x14, 0x86, 0x65, 0x40, 0xa8, 0x39, 0x65, 0xf2, 0x54, 0x71, 0x29, 0xa5, 0x13, 0x4b, 0x6d, 0x15, 0x46, 0x26, 0x6e, 0x12, 0x11, 0x4b, 0x15, 0x06, 0x24, 0x96, 0xc8, 0x71, 0x8e, 0xd2, 0xa8, 0xaa, 0x1d, 0xec, 0x24, 0x90, 0x57, 0xe0, 0xc9, 0x18, 0x79, 0x04, 0x94, 0x27, 0x41, 0x71, 0x83, 0x4a, 0x2a, 0x24, 0x58, 0x3f, 0x7f, 0xfa, 0x7d, 0xce, 0xf9, 0x61, 0x52, 0x44, 0x68, 0xb8, 0x14, 0x31, 0x2a, 0x89, 0xdc, 0xce, 0x85, 0x89, 0xe3, 0xd4, 0xe6, 0x26, 0x8d, 0x8a, 0x5c, 0x1b, 0x5e, 0x4e, 0xb9, 0x14, 0x4a, 0x98, 0x8a, 0x65, 0x46, 0xe7, 0x9a, 0x8e, 0x1a, 0x9d, 0xb5, 0x3a, 0xdb, 0xd4, 0x59, 0x39, 0x1d, 0xdf, 0x41, 0x7f, 0x96, 0xaa, 0x24, 0xc0, 0xe7, 0x02, 0x6d, 0x4e, 0x0f, 0xc0, 0x73, 0x56, 0xb8, 0xc0, 0x6a, 0x40, 0x46, 0xe4, 0xd4, 0x0b, 0x7a, 0x0e, 0xdc, 0x63, 0x45, 0x0f, 0xc1, 0x53, 0x62, 0x89, 0x36, 0x13, 0x12, 0x07, 0x5b, 0xee, 0x71, 0x0d, 0xc6, 0x0b, 0xd8, 0x5b, 0x25, 0xd9, 0x4c, 0x2b, 0x8b, 0xf4, 0x18, 0xfa, 0xf8, 0x8a, 0xb2, 0xf9, 0x28, 0x4c, 0xe3, 0x36, 0x0c, 0xbe, 0x91, 0x1f, 0xd3, 0x23, 0x00, 0xfd, 0xa2, 0x6c, 0xe8, 0xf2, 0x5d, 0x5e, 0x2f, 0xf0, 0x1a, 0xf2, 0xd0, 0x80, 0xee, 0x28, 0xdb, 0xdd, 0x51, 0xce, 0xde, 0x08, 0x9c, 0x38, 0xed, 0x66, 0xbd, 0xce, 0x6d, 0x1b, 0x7d, 0xed, 0x2e, 0x70, 0x39, 0xf3, 0x29, 0xc2, 0x4e, 0x33, 0x12, 0x9d, 0xb0, 0xbf, 0xee, 0xc0, 0x7e, 0x1c, 0x61, 0x9f, 0xfd, 0x57, 0x5f, 0x6d, 0x7a, 0xf5, 0xf8, 0x5e, 0x0f, 0xc9, 0x47, 0x3d, 0x24, 0x9f, 0xf5, 0x90, 0x3c, 0xf9, 0x49, 0x9a, 0xcf, 0x8b, 0x88, 0x49, 0xbd, 0xe4, 0x9d, 0xb6, 0x58, 0x82, 0x8a, 0xbb, 0x5e, 0x7e, 0x2b, 0xee, 0x62, 0x93, 0x95, 0xd3, 0x68, 0xd7, 0xd9, 0xe7, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x9d, 0xd8, 0xdd, 0xdd, 0xf6, 0x01, 0x00, 0x00, } func (m *PingRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PingRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *PingRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = encodeVarintCanary(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0x12 } if len(m.ShardKey) > 0 { i -= len(m.ShardKey) copy(dAtA[i:], m.ShardKey) i = encodeVarintCanary(dAtA, i, uint64(len(m.ShardKey))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PingResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PingResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *PingResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ShardKey) > 0 { i -= len(m.ShardKey) copy(dAtA[i:], m.ShardKey) i = encodeVarintCanary(dAtA, i, uint64(len(m.ShardKey))) i-- dAtA[i] = 0x1a } if m.OwnsShard { i-- if m.OwnsShard { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.ExecutorId) > 0 { i -= len(m.ExecutorId) copy(dAtA[i:], m.ExecutorId) i = encodeVarintCanary(dAtA, i, uint64(len(m.ExecutorId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func encodeVarintCanary(dAtA []byte, offset int, v uint64) int { offset -= sovCanary(v) base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) return base } func (m *PingRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ShardKey) if l > 0 { n += 1 + l + sovCanary(uint64(l)) } l = len(m.Namespace) if l > 0 { n += 1 + l + sovCanary(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *PingResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ExecutorId) if l > 0 { n += 1 + l + sovCanary(uint64(l)) } if m.OwnsShard { n += 2 } l = len(m.ShardKey) if l > 0 { n += 1 + l + sovCanary(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func sovCanary(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozCanary(x uint64) (n int) { return sovCanary(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *PingRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowCanary } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PingRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PingRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ShardKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowCanary } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthCanary } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthCanary } if postIndex > l { return io.ErrUnexpectedEOF } m.ShardKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowCanary } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthCanary } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthCanary } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipCanary(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCanary } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PingResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowCanary } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PingResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PingResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExecutorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowCanary } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthCanary } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthCanary } if postIndex > l { return io.ErrUnexpectedEOF } m.ExecutorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field OwnsShard", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowCanary } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.OwnsShard = bool(v != 0) case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ShardKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowCanary } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthCanary } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthCanary } if postIndex > l { return io.ErrUnexpectedEOF } m.ShardKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipCanary(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCanary } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func skipCanary(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowCanary } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } wireType := int(wire & 0x7) switch wireType { case 0: for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowCanary } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } iNdEx++ if dAtA[iNdEx-1] < 0x80 { break } } case 1: iNdEx += 8 case 2: var length int for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowCanary } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { break } } if length < 0 { return 0, ErrInvalidLengthCanary } iNdEx += length case 3: depth++ case 4: if depth == 0 { return 0, ErrUnexpectedEndOfGroupCanary } depth-- case 5: iNdEx += 4 default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } if iNdEx < 0 { return 0, ErrInvalidLengthCanary } if depth == 0 { return iNdEx, nil } } return 0, io.ErrUnexpectedEOF } var ( ErrInvalidLengthCanary = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowCanary = fmt.Errorf("proto: integer overflow") ErrUnexpectedEndOfGroupCanary = fmt.Errorf("proto: unexpected end of group") ) ================================================ FILE: .gen/proto/sharddistributor/v1/canary.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/sharddistributor/v1/canary.proto package sharddistributorv1 import ( "context" "io/ioutil" "reflect" "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" "go.uber.org/fx" "go.uber.org/yarpc" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/api/x/restriction" "go.uber.org/yarpc/encoding/protobuf" "go.uber.org/yarpc/encoding/protobuf/reflection" ) var _ = ioutil.NopCloser // ShardDistributorExecutorCanaryAPIYARPCClient is the YARPC client-side interface for the ShardDistributorExecutorCanaryAPI service. type ShardDistributorExecutorCanaryAPIYARPCClient interface { Ping(context.Context, *PingRequest, ...yarpc.CallOption) (*PingResponse, error) } func newShardDistributorExecutorCanaryAPIYARPCClient(clientConfig transport.ClientConfig, anyResolver jsonpb.AnyResolver, options ...protobuf.ClientOption) ShardDistributorExecutorCanaryAPIYARPCClient { return &_ShardDistributorExecutorCanaryAPIYARPCCaller{protobuf.NewStreamClient( protobuf.ClientParams{ ServiceName: "uber.cadence.sharddistributor.v1.ShardDistributorExecutorCanaryAPI", ClientConfig: clientConfig, AnyResolver: anyResolver, Options: options, }, )} } // NewShardDistributorExecutorCanaryAPIYARPCClient builds a new YARPC client for the ShardDistributorExecutorCanaryAPI service. func NewShardDistributorExecutorCanaryAPIYARPCClient(clientConfig transport.ClientConfig, options ...protobuf.ClientOption) ShardDistributorExecutorCanaryAPIYARPCClient { return newShardDistributorExecutorCanaryAPIYARPCClient(clientConfig, nil, options...) } // ShardDistributorExecutorCanaryAPIYARPCServer is the YARPC server-side interface for the ShardDistributorExecutorCanaryAPI service. type ShardDistributorExecutorCanaryAPIYARPCServer interface { Ping(context.Context, *PingRequest) (*PingResponse, error) } type buildShardDistributorExecutorCanaryAPIYARPCProceduresParams struct { Server ShardDistributorExecutorCanaryAPIYARPCServer AnyResolver jsonpb.AnyResolver } func buildShardDistributorExecutorCanaryAPIYARPCProcedures(params buildShardDistributorExecutorCanaryAPIYARPCProceduresParams) []transport.Procedure { handler := &_ShardDistributorExecutorCanaryAPIYARPCHandler{params.Server} return protobuf.BuildProcedures( protobuf.BuildProceduresParams{ ServiceName: "uber.cadence.sharddistributor.v1.ShardDistributorExecutorCanaryAPI", UnaryHandlerParams: []protobuf.BuildProceduresUnaryHandlerParams{ { MethodName: "Ping", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.Ping, NewRequest: newShardDistributorExecutorCanaryAPIServicePingYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, }, OnewayHandlerParams: []protobuf.BuildProceduresOnewayHandlerParams{}, StreamHandlerParams: []protobuf.BuildProceduresStreamHandlerParams{}, }, ) } // BuildShardDistributorExecutorCanaryAPIYARPCProcedures prepares an implementation of the ShardDistributorExecutorCanaryAPI service for YARPC registration. func BuildShardDistributorExecutorCanaryAPIYARPCProcedures(server ShardDistributorExecutorCanaryAPIYARPCServer) []transport.Procedure { return buildShardDistributorExecutorCanaryAPIYARPCProcedures(buildShardDistributorExecutorCanaryAPIYARPCProceduresParams{Server: server}) } // FxShardDistributorExecutorCanaryAPIYARPCClientParams defines the input // for NewFxShardDistributorExecutorCanaryAPIYARPCClient. It provides the // paramaters to get a ShardDistributorExecutorCanaryAPIYARPCClient in an // Fx application. type FxShardDistributorExecutorCanaryAPIYARPCClientParams struct { fx.In Provider yarpc.ClientConfig AnyResolver jsonpb.AnyResolver `name:"yarpcfx" optional:"true"` Restriction restriction.Checker `optional:"true"` } // FxShardDistributorExecutorCanaryAPIYARPCClientResult defines the output // of NewFxShardDistributorExecutorCanaryAPIYARPCClient. It provides a // ShardDistributorExecutorCanaryAPIYARPCClient to an Fx application. type FxShardDistributorExecutorCanaryAPIYARPCClientResult struct { fx.Out Client ShardDistributorExecutorCanaryAPIYARPCClient // We are using an fx.Out struct here instead of just returning a client // so that we can add more values or add named versions of the client in // the future without breaking any existing code. } // NewFxShardDistributorExecutorCanaryAPIYARPCClient provides a ShardDistributorExecutorCanaryAPIYARPCClient // to an Fx application using the given name for routing. // // fx.Provide( // sharddistributorv1.NewFxShardDistributorExecutorCanaryAPIYARPCClient("service-name"), // ... // ) func NewFxShardDistributorExecutorCanaryAPIYARPCClient(name string, options ...protobuf.ClientOption) interface{} { return func(params FxShardDistributorExecutorCanaryAPIYARPCClientParams) FxShardDistributorExecutorCanaryAPIYARPCClientResult { cc := params.Provider.ClientConfig(name) if params.Restriction != nil { if namer, ok := cc.GetUnaryOutbound().(transport.Namer); ok { if err := params.Restriction.Check(protobuf.Encoding, namer.TransportName()); err != nil { panic(err.Error()) } } } return FxShardDistributorExecutorCanaryAPIYARPCClientResult{ Client: newShardDistributorExecutorCanaryAPIYARPCClient(cc, params.AnyResolver, options...), } } } // FxShardDistributorExecutorCanaryAPIYARPCProceduresParams defines the input // for NewFxShardDistributorExecutorCanaryAPIYARPCProcedures. It provides the // paramaters to get ShardDistributorExecutorCanaryAPIYARPCServer procedures in an // Fx application. type FxShardDistributorExecutorCanaryAPIYARPCProceduresParams struct { fx.In Server ShardDistributorExecutorCanaryAPIYARPCServer AnyResolver jsonpb.AnyResolver `name:"yarpcfx" optional:"true"` } // FxShardDistributorExecutorCanaryAPIYARPCProceduresResult defines the output // of NewFxShardDistributorExecutorCanaryAPIYARPCProcedures. It provides // ShardDistributorExecutorCanaryAPIYARPCServer procedures to an Fx application. // // The procedures are provided to the "yarpcfx" value group. // Dig 1.2 or newer must be used for this feature to work. type FxShardDistributorExecutorCanaryAPIYARPCProceduresResult struct { fx.Out Procedures []transport.Procedure `group:"yarpcfx"` ReflectionMeta reflection.ServerMeta `group:"yarpcfx"` } // NewFxShardDistributorExecutorCanaryAPIYARPCProcedures provides ShardDistributorExecutorCanaryAPIYARPCServer procedures to an Fx application. // It expects a ShardDistributorExecutorCanaryAPIYARPCServer to be present in the container. // // fx.Provide( // sharddistributorv1.NewFxShardDistributorExecutorCanaryAPIYARPCProcedures(), // ... // ) func NewFxShardDistributorExecutorCanaryAPIYARPCProcedures() interface{} { return func(params FxShardDistributorExecutorCanaryAPIYARPCProceduresParams) FxShardDistributorExecutorCanaryAPIYARPCProceduresResult { return FxShardDistributorExecutorCanaryAPIYARPCProceduresResult{ Procedures: buildShardDistributorExecutorCanaryAPIYARPCProcedures(buildShardDistributorExecutorCanaryAPIYARPCProceduresParams{ Server: params.Server, AnyResolver: params.AnyResolver, }), ReflectionMeta: ShardDistributorExecutorCanaryAPIReflectionMeta, } } } // ShardDistributorExecutorCanaryAPIReflectionMeta is the reflection server metadata // required for using the gRPC reflection protocol with YARPC. // // See https://github.com/grpc/grpc/blob/master/doc/server-reflection.md. var ShardDistributorExecutorCanaryAPIReflectionMeta = reflection.ServerMeta{ ServiceName: "uber.cadence.sharddistributor.v1.ShardDistributorExecutorCanaryAPI", FileDescriptors: yarpcFileDescriptorClosure03b11524fa4f4f94, } type _ShardDistributorExecutorCanaryAPIYARPCCaller struct { streamClient protobuf.StreamClient } func (c *_ShardDistributorExecutorCanaryAPIYARPCCaller) Ping(ctx context.Context, request *PingRequest, options ...yarpc.CallOption) (*PingResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "Ping", request, newShardDistributorExecutorCanaryAPIServicePingYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*PingResponse) if !ok { return nil, protobuf.CastError(emptyShardDistributorExecutorCanaryAPIServicePingYARPCResponse, responseMessage) } return response, err } type _ShardDistributorExecutorCanaryAPIYARPCHandler struct { server ShardDistributorExecutorCanaryAPIYARPCServer } func (h *_ShardDistributorExecutorCanaryAPIYARPCHandler) Ping(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *PingRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*PingRequest) if !ok { return nil, protobuf.CastError(emptyShardDistributorExecutorCanaryAPIServicePingYARPCRequest, requestMessage) } } response, err := h.server.Ping(ctx, request) if response == nil { return nil, err } return response, err } func newShardDistributorExecutorCanaryAPIServicePingYARPCRequest() proto.Message { return &PingRequest{} } func newShardDistributorExecutorCanaryAPIServicePingYARPCResponse() proto.Message { return &PingResponse{} } var ( emptyShardDistributorExecutorCanaryAPIServicePingYARPCRequest = &PingRequest{} emptyShardDistributorExecutorCanaryAPIServicePingYARPCResponse = &PingResponse{} ) var yarpcFileDescriptorClosure03b11524fa4f4f94 = [][]byte{ // uber/cadence/sharddistributor/v1/canary.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xbd, 0x4e, 0xf3, 0x30, 0x14, 0x86, 0x95, 0xef, 0x43, 0xa8, 0x39, 0x65, 0xf2, 0x54, 0xf1, 0x23, 0x4a, 0x27, 0x96, 0xda, 0x0a, 0x8c, 0x4c, 0xfc, 0x49, 0x44, 0x5d, 0xaa, 0xb0, 0xb1, 0x44, 0x8e, 0x73, 0x94, 0x5a, 0x55, 0xed, 0x60, 0x3b, 0x81, 0xdc, 0x02, 0x57, 0x8d, 0xec, 0x06, 0x95, 0x54, 0x48, 0xb0, 0x3e, 0x7e, 0xf4, 0xfa, 0x9c, 0xf3, 0xc2, 0xbc, 0x29, 0xd0, 0x30, 0xc1, 0x4b, 0x54, 0x02, 0x99, 0x5d, 0x71, 0x53, 0x96, 0xd2, 0x3a, 0x23, 0x8b, 0xc6, 0x69, 0xc3, 0xda, 0x84, 0x09, 0xae, 0xb8, 0xe9, 0x68, 0x6d, 0xb4, 0xd3, 0x64, 0xea, 0x75, 0xda, 0xeb, 0x74, 0x5f, 0xa7, 0x6d, 0x32, 0x7b, 0x82, 0xf1, 0x52, 0xaa, 0x2a, 0xc3, 0xd7, 0x06, 0xad, 0x23, 0x27, 0x10, 0x07, 0x2b, 0x5f, 0x63, 0x37, 0x89, 0xa6, 0xd1, 0x65, 0x9c, 0x8d, 0x02, 0x58, 0x60, 0x47, 0x4e, 0x21, 0x56, 0x7c, 0x83, 0xb6, 0xe6, 0x02, 0x27, 0xff, 0xc2, 0xe3, 0x0e, 0xcc, 0xd6, 0x70, 0xb4, 0x4d, 0xb2, 0xb5, 0x56, 0x16, 0xc9, 0x39, 0x8c, 0xf1, 0x1d, 0x85, 0xff, 0x28, 0x97, 0x65, 0x1f, 0x06, 0x5f, 0x28, 0x2d, 0xc9, 0x19, 0x80, 0x7e, 0x53, 0x36, 0x0f, 0xf9, 0x21, 0x6f, 0x94, 0xc5, 0x9e, 0x3c, 0x7b, 0x30, 0x1c, 0xe5, 0xff, 0x70, 0x94, 0xab, 0x8f, 0x08, 0x2e, 0x82, 0xf6, 0xb0, 0x5b, 0xe7, 0xb1, 0x8f, 0xbe, 0x0f, 0x17, 0xb8, 0x5d, 0xa6, 0x04, 0xe1, 0xc0, 0x8f, 0x44, 0xe6, 0xf4, 0xb7, 0x3b, 0xd0, 0x6f, 0x47, 0x38, 0xa6, 0x7f, 0xd5, 0xb7, 0x9b, 0xde, 0x2d, 0x5e, 0xd2, 0x4a, 0xba, 0x55, 0x53, 0x50, 0xa1, 0x37, 0x6c, 0xd0, 0x10, 0xad, 0x50, 0xb1, 0xd0, 0xc5, 0x4f, 0x65, 0xdd, 0xec, 0xb3, 0x36, 0x29, 0x0e, 0x83, 0x7d, 0xfd, 0x19, 0x00, 0x00, 0xff, 0xff, 0xc5, 0x61, 0x98, 0x91, 0xea, 0x01, 0x00, 0x00, }, } func init() { yarpc.RegisterClientBuilder( func(clientConfig transport.ClientConfig, structField reflect.StructField) ShardDistributorExecutorCanaryAPIYARPCClient { return NewShardDistributorExecutorCanaryAPIYARPCClient(clientConfig, protobuf.ClientBuilderOptions(clientConfig, structField)...) }, ) } ================================================ FILE: .gen/proto/sharddistributor/v1/executor.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/sharddistributor/v1/executor.proto package sharddistributorv1 import ( encoding_binary "encoding/binary" fmt "fmt" io "io" math "math" math_bits "math/bits" proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type ExecutorStatus int32 const ( ExecutorStatus_EXECUTOR_STATUS_INVALID ExecutorStatus = 0 ExecutorStatus_EXECUTOR_STATUS_ACTIVE ExecutorStatus = 1 ExecutorStatus_EXECUTOR_STATUS_DRAINING ExecutorStatus = 2 ExecutorStatus_EXECUTOR_STATUS_DRAINED ExecutorStatus = 3 ) var ExecutorStatus_name = map[int32]string{ 0: "EXECUTOR_STATUS_INVALID", 1: "EXECUTOR_STATUS_ACTIVE", 2: "EXECUTOR_STATUS_DRAINING", 3: "EXECUTOR_STATUS_DRAINED", } var ExecutorStatus_value = map[string]int32{ "EXECUTOR_STATUS_INVALID": 0, "EXECUTOR_STATUS_ACTIVE": 1, "EXECUTOR_STATUS_DRAINING": 2, "EXECUTOR_STATUS_DRAINED": 3, } func (x ExecutorStatus) String() string { return proto.EnumName(ExecutorStatus_name, int32(x)) } func (ExecutorStatus) EnumDescriptor() ([]byte, []int) { return fileDescriptor_5aab034437d08cca, []int{0} } // We only have one status for now, but when adding // graceful handover, we will need to add more statuses. // We do not need an "inactive" status, as we will not include // inactive shards in the heartbeat request. type ShardStatus int32 const ( ShardStatus_SHARD_STATUS_INVALID ShardStatus = 0 ShardStatus_SHARD_STATUS_READY ShardStatus = 1 ShardStatus_SHARD_STATUS_DONE ShardStatus = 2 ) var ShardStatus_name = map[int32]string{ 0: "SHARD_STATUS_INVALID", 1: "SHARD_STATUS_READY", 2: "SHARD_STATUS_DONE", } var ShardStatus_value = map[string]int32{ "SHARD_STATUS_INVALID": 0, "SHARD_STATUS_READY": 1, "SHARD_STATUS_DONE": 2, } func (x ShardStatus) String() string { return proto.EnumName(ShardStatus_name, int32(x)) } func (ShardStatus) EnumDescriptor() ([]byte, []int) { return fileDescriptor_5aab034437d08cca, []int{1} } // We only have one status for now, but when adding // graceful handover, we will need to add more statuses. // We do not need an "inactive" status, as we will not include // inactive shards in the heartbeat request. type AssignmentStatus int32 const ( AssignmentStatus_ASSIGNMENT_STATUS_INVALID AssignmentStatus = 0 AssignmentStatus_ASSIGNMENT_STATUS_READY AssignmentStatus = 1 ) var AssignmentStatus_name = map[int32]string{ 0: "ASSIGNMENT_STATUS_INVALID", 1: "ASSIGNMENT_STATUS_READY", } var AssignmentStatus_value = map[string]int32{ "ASSIGNMENT_STATUS_INVALID": 0, "ASSIGNMENT_STATUS_READY": 1, } func (x AssignmentStatus) String() string { return proto.EnumName(AssignmentStatus_name, int32(x)) } func (AssignmentStatus) EnumDescriptor() ([]byte, []int) { return fileDescriptor_5aab034437d08cca, []int{2} } // We handle the migration steps from SD side type MigrationMode int32 const ( MigrationMode_MIGRATION_MODE_INVALID MigrationMode = 0 MigrationMode_MIGRATION_MODE_LOCAL_PASSTHROUGH MigrationMode = 1 MigrationMode_MIGRATION_MODE_LOCAL_PASSTHROUGH_SHADOW MigrationMode = 2 MigrationMode_MIGRATION_MODE_DISTRIBUTED_PASSTHROUGH MigrationMode = 3 MigrationMode_MIGRATION_MODE_ONBOARDED MigrationMode = 4 ) var MigrationMode_name = map[int32]string{ 0: "MIGRATION_MODE_INVALID", 1: "MIGRATION_MODE_LOCAL_PASSTHROUGH", 2: "MIGRATION_MODE_LOCAL_PASSTHROUGH_SHADOW", 3: "MIGRATION_MODE_DISTRIBUTED_PASSTHROUGH", 4: "MIGRATION_MODE_ONBOARDED", } var MigrationMode_value = map[string]int32{ "MIGRATION_MODE_INVALID": 0, "MIGRATION_MODE_LOCAL_PASSTHROUGH": 1, "MIGRATION_MODE_LOCAL_PASSTHROUGH_SHADOW": 2, "MIGRATION_MODE_DISTRIBUTED_PASSTHROUGH": 3, "MIGRATION_MODE_ONBOARDED": 4, } func (x MigrationMode) String() string { return proto.EnumName(MigrationMode_name, int32(x)) } func (MigrationMode) EnumDescriptor() ([]byte, []int) { return fileDescriptor_5aab034437d08cca, []int{3} } type HeartbeatRequest struct { Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` ExecutorId string `protobuf:"bytes,2,opt,name=executor_id,json=executorId,proto3" json:"executor_id,omitempty"` Status ExecutorStatus `protobuf:"varint,3,opt,name=status,proto3,enum=uber.cadence.sharddistributor.v1.ExecutorStatus" json:"status,omitempty"` ShardStatusReports map[string]*ShardStatusReport `protobuf:"bytes,4,rep,name=shard_status_reports,json=shardStatusReports,proto3" json:"shard_status_reports,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Metadata map[string]string `protobuf:"bytes,5,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *HeartbeatRequest) Reset() { *m = HeartbeatRequest{} } func (m *HeartbeatRequest) String() string { return proto.CompactTextString(m) } func (*HeartbeatRequest) ProtoMessage() {} func (*HeartbeatRequest) Descriptor() ([]byte, []int) { return fileDescriptor_5aab034437d08cca, []int{0} } func (m *HeartbeatRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *HeartbeatRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_HeartbeatRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *HeartbeatRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_HeartbeatRequest.Merge(m, src) } func (m *HeartbeatRequest) XXX_Size() int { return m.Size() } func (m *HeartbeatRequest) XXX_DiscardUnknown() { xxx_messageInfo_HeartbeatRequest.DiscardUnknown(m) } var xxx_messageInfo_HeartbeatRequest proto.InternalMessageInfo func (m *HeartbeatRequest) GetNamespace() string { if m != nil { return m.Namespace } return "" } func (m *HeartbeatRequest) GetExecutorId() string { if m != nil { return m.ExecutorId } return "" } func (m *HeartbeatRequest) GetStatus() ExecutorStatus { if m != nil { return m.Status } return ExecutorStatus_EXECUTOR_STATUS_INVALID } func (m *HeartbeatRequest) GetShardStatusReports() map[string]*ShardStatusReport { if m != nil { return m.ShardStatusReports } return nil } func (m *HeartbeatRequest) GetMetadata() map[string]string { if m != nil { return m.Metadata } return nil } type ShardStatusReport struct { Status ShardStatus `protobuf:"varint,1,opt,name=status,proto3,enum=uber.cadence.sharddistributor.v1.ShardStatus" json:"status,omitempty"` ShardLoad float64 `protobuf:"fixed64,2,opt,name=shard_load,json=shardLoad,proto3" json:"shard_load,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ShardStatusReport) Reset() { *m = ShardStatusReport{} } func (m *ShardStatusReport) String() string { return proto.CompactTextString(m) } func (*ShardStatusReport) ProtoMessage() {} func (*ShardStatusReport) Descriptor() ([]byte, []int) { return fileDescriptor_5aab034437d08cca, []int{1} } func (m *ShardStatusReport) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ShardStatusReport) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ShardStatusReport.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ShardStatusReport) XXX_Merge(src proto.Message) { xxx_messageInfo_ShardStatusReport.Merge(m, src) } func (m *ShardStatusReport) XXX_Size() int { return m.Size() } func (m *ShardStatusReport) XXX_DiscardUnknown() { xxx_messageInfo_ShardStatusReport.DiscardUnknown(m) } var xxx_messageInfo_ShardStatusReport proto.InternalMessageInfo func (m *ShardStatusReport) GetStatus() ShardStatus { if m != nil { return m.Status } return ShardStatus_SHARD_STATUS_INVALID } func (m *ShardStatusReport) GetShardLoad() float64 { if m != nil { return m.ShardLoad } return 0 } type HeartbeatResponse struct { ShardAssignments map[string]*ShardAssignment `protobuf:"bytes,1,rep,name=shard_assignments,json=shardAssignments,proto3" json:"shard_assignments,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` MigrationMode MigrationMode `protobuf:"varint,2,opt,name=migration_mode,json=migrationMode,proto3,enum=uber.cadence.sharddistributor.v1.MigrationMode" json:"migration_mode,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *HeartbeatResponse) Reset() { *m = HeartbeatResponse{} } func (m *HeartbeatResponse) String() string { return proto.CompactTextString(m) } func (*HeartbeatResponse) ProtoMessage() {} func (*HeartbeatResponse) Descriptor() ([]byte, []int) { return fileDescriptor_5aab034437d08cca, []int{2} } func (m *HeartbeatResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *HeartbeatResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_HeartbeatResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *HeartbeatResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_HeartbeatResponse.Merge(m, src) } func (m *HeartbeatResponse) XXX_Size() int { return m.Size() } func (m *HeartbeatResponse) XXX_DiscardUnknown() { xxx_messageInfo_HeartbeatResponse.DiscardUnknown(m) } var xxx_messageInfo_HeartbeatResponse proto.InternalMessageInfo func (m *HeartbeatResponse) GetShardAssignments() map[string]*ShardAssignment { if m != nil { return m.ShardAssignments } return nil } func (m *HeartbeatResponse) GetMigrationMode() MigrationMode { if m != nil { return m.MigrationMode } return MigrationMode_MIGRATION_MODE_INVALID } type ShardAssignment struct { Status AssignmentStatus `protobuf:"varint,1,opt,name=status,proto3,enum=uber.cadence.sharddistributor.v1.AssignmentStatus" json:"status,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ShardAssignment) Reset() { *m = ShardAssignment{} } func (m *ShardAssignment) String() string { return proto.CompactTextString(m) } func (*ShardAssignment) ProtoMessage() {} func (*ShardAssignment) Descriptor() ([]byte, []int) { return fileDescriptor_5aab034437d08cca, []int{3} } func (m *ShardAssignment) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ShardAssignment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ShardAssignment.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ShardAssignment) XXX_Merge(src proto.Message) { xxx_messageInfo_ShardAssignment.Merge(m, src) } func (m *ShardAssignment) XXX_Size() int { return m.Size() } func (m *ShardAssignment) XXX_DiscardUnknown() { xxx_messageInfo_ShardAssignment.DiscardUnknown(m) } var xxx_messageInfo_ShardAssignment proto.InternalMessageInfo func (m *ShardAssignment) GetStatus() AssignmentStatus { if m != nil { return m.Status } return AssignmentStatus_ASSIGNMENT_STATUS_INVALID } func init() { proto.RegisterEnum("uber.cadence.sharddistributor.v1.ExecutorStatus", ExecutorStatus_name, ExecutorStatus_value) proto.RegisterEnum("uber.cadence.sharddistributor.v1.ShardStatus", ShardStatus_name, ShardStatus_value) proto.RegisterEnum("uber.cadence.sharddistributor.v1.AssignmentStatus", AssignmentStatus_name, AssignmentStatus_value) proto.RegisterEnum("uber.cadence.sharddistributor.v1.MigrationMode", MigrationMode_name, MigrationMode_value) proto.RegisterType((*HeartbeatRequest)(nil), "uber.cadence.sharddistributor.v1.HeartbeatRequest") proto.RegisterMapType((map[string]string)(nil), "uber.cadence.sharddistributor.v1.HeartbeatRequest.MetadataEntry") proto.RegisterMapType((map[string]*ShardStatusReport)(nil), "uber.cadence.sharddistributor.v1.HeartbeatRequest.ShardStatusReportsEntry") proto.RegisterType((*ShardStatusReport)(nil), "uber.cadence.sharddistributor.v1.ShardStatusReport") proto.RegisterType((*HeartbeatResponse)(nil), "uber.cadence.sharddistributor.v1.HeartbeatResponse") proto.RegisterMapType((map[string]*ShardAssignment)(nil), "uber.cadence.sharddistributor.v1.HeartbeatResponse.ShardAssignmentsEntry") proto.RegisterType((*ShardAssignment)(nil), "uber.cadence.sharddistributor.v1.ShardAssignment") } func init() { proto.RegisterFile("uber/cadence/sharddistributor/v1/executor.proto", fileDescriptor_5aab034437d08cca) } var fileDescriptor_5aab034437d08cca = []byte{ // 754 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x6e, 0xda, 0x4a, 0x14, 0xbe, 0x86, 0x24, 0xba, 0x1c, 0x14, 0xae, 0x19, 0xe5, 0x87, 0x4b, 0x7e, 0x2e, 0x8a, 0xae, 0xda, 0x88, 0xaa, 0xa6, 0x90, 0x4d, 0xd5, 0x6c, 0xea, 0xc4, 0x16, 0x38, 0x02, 0x3b, 0x1a, 0x1b, 0xd2, 0x56, 0xad, 0xac, 0x01, 0x8f, 0x08, 0x6a, 0xb0, 0xa9, 0x3d, 0xa0, 0xa6, 0xea, 0xb2, 0x6f, 0xd0, 0x17, 0xe9, 0x63, 0x64, 0xd9, 0x7d, 0x37, 0x55, 0x9e, 0xa4, 0xb2, 0x0d, 0x01, 0x1b, 0x2a, 0x9a, 0xec, 0xec, 0x73, 0xce, 0xf7, 0x7d, 0xc7, 0xe7, 0x3b, 0xe3, 0x81, 0xd2, 0xb0, 0x4d, 0xdd, 0x52, 0x87, 0x58, 0xd4, 0xee, 0xd0, 0x92, 0x77, 0x49, 0x5c, 0xcb, 0xea, 0x79, 0xcc, 0xed, 0xb5, 0x87, 0xcc, 0x71, 0x4b, 0xa3, 0x72, 0x89, 0x7e, 0xa4, 0x1d, 0xff, 0x59, 0x18, 0xb8, 0x0e, 0x73, 0x50, 0xc1, 0x07, 0x08, 0x63, 0x80, 0x10, 0x07, 0x08, 0xa3, 0xf2, 0xc1, 0xb7, 0x15, 0xe0, 0x6b, 0x94, 0xb8, 0xac, 0x4d, 0x09, 0xc3, 0xf4, 0xc3, 0x90, 0x7a, 0x0c, 0xed, 0x42, 0xca, 0x26, 0x7d, 0xea, 0x0d, 0x48, 0x87, 0xe6, 0xb8, 0x02, 0x77, 0x98, 0xc2, 0xd3, 0x00, 0xfa, 0x0f, 0xd2, 0x13, 0x19, 0xb3, 0x67, 0xe5, 0x12, 0x41, 0x1e, 0x26, 0x21, 0xc5, 0x42, 0x35, 0x58, 0xf3, 0x18, 0x61, 0x43, 0x2f, 0x97, 0x2c, 0x70, 0x87, 0x99, 0xca, 0x33, 0x61, 0x59, 0x1b, 0x82, 0x3c, 0x46, 0xeb, 0x01, 0x0e, 0x8f, 0xf1, 0xe8, 0x33, 0x6c, 0x04, 0xd5, 0x66, 0xf8, 0x6e, 0xba, 0x74, 0xe0, 0xb8, 0xcc, 0xcb, 0xad, 0x14, 0x92, 0x87, 0xe9, 0xca, 0xd9, 0x72, 0xde, 0xf8, 0xa7, 0x09, 0xba, 0x5f, 0x34, 0x56, 0x09, 0xc9, 0x64, 0x9b, 0xb9, 0xd7, 0x18, 0x79, 0x73, 0x09, 0xf4, 0x16, 0xfe, 0xee, 0x53, 0x46, 0x2c, 0xc2, 0x48, 0x6e, 0x35, 0x50, 0x7c, 0xf9, 0x00, 0xc5, 0xc6, 0x98, 0x22, 0xd4, 0xb9, 0x63, 0xcc, 0x7f, 0x82, 0xed, 0xdf, 0x34, 0x83, 0x78, 0x48, 0xbe, 0xa7, 0xd7, 0xe3, 0xc9, 0xfb, 0x8f, 0x48, 0x81, 0xd5, 0x11, 0xb9, 0x1a, 0xd2, 0x60, 0xda, 0xe9, 0xca, 0xd1, 0xf2, 0x3e, 0xe6, 0xb8, 0x71, 0xc8, 0xf0, 0x22, 0xf1, 0x9c, 0xcb, 0x1f, 0xc3, 0x7a, 0xa4, 0xad, 0x05, 0x8a, 0x1b, 0xb3, 0x8a, 0xa9, 0x19, 0xf0, 0xc1, 0x35, 0x64, 0xe7, 0xc8, 0x91, 0x7c, 0xe7, 0x39, 0x17, 0x78, 0xfe, 0xf4, 0x7e, 0x1d, 0x4e, 0x0c, 0xdf, 0x03, 0x08, 0x0d, 0xbf, 0x72, 0x48, 0xb8, 0x5a, 0x1c, 0x4e, 0x05, 0x91, 0xba, 0x43, 0xac, 0x83, 0x1f, 0x09, 0xc8, 0xce, 0x0c, 0xd8, 0x1b, 0x38, 0xb6, 0x47, 0xd1, 0x08, 0xb2, 0x21, 0x88, 0x78, 0x5e, 0xaf, 0x6b, 0xf7, 0xa9, 0xcd, 0xfc, 0x36, 0x7c, 0xc3, 0x94, 0x7b, 0x19, 0x16, 0xf2, 0x85, 0x8d, 0x89, 0x53, 0xae, 0xd0, 0x39, 0xde, 0x8b, 0x85, 0x51, 0x0b, 0x32, 0xfd, 0x5e, 0xd7, 0x25, 0xac, 0xe7, 0xd8, 0x66, 0xdf, 0xb1, 0xc2, 0x59, 0x65, 0x2a, 0xa5, 0xe5, 0xa2, 0x8d, 0x09, 0xae, 0xe1, 0x58, 0x14, 0xaf, 0xf7, 0x67, 0x5f, 0xf3, 0x23, 0xd8, 0x5c, 0xd8, 0xc2, 0x02, 0x97, 0xaa, 0xd1, 0xbd, 0x28, 0xff, 0xe1, 0xd4, 0xa7, 0xcc, 0xb3, 0xc6, 0xbe, 0x83, 0x7f, 0x62, 0x59, 0x74, 0x16, 0xb3, 0xb5, 0xb2, 0x5c, 0x60, 0x8a, 0x8e, 0x7a, 0x5b, 0xfc, 0xc2, 0x41, 0x26, 0x7a, 0xce, 0xd1, 0x0e, 0x6c, 0xcb, 0xaf, 0xe4, 0xd3, 0xa6, 0xa1, 0x61, 0x53, 0x37, 0x44, 0xa3, 0xa9, 0x9b, 0x8a, 0xda, 0x12, 0xeb, 0x8a, 0xc4, 0xff, 0x85, 0xf2, 0xb0, 0x15, 0x4f, 0x8a, 0xa7, 0x86, 0xd2, 0x92, 0x79, 0x0e, 0xed, 0x42, 0x2e, 0x9e, 0x93, 0xb0, 0xa8, 0xa8, 0x8a, 0x5a, 0xe5, 0x13, 0x8b, 0x68, 0x83, 0xac, 0x2c, 0xf1, 0xc9, 0x62, 0x0b, 0xd2, 0x33, 0x9b, 0x87, 0x72, 0xb0, 0xa1, 0xd7, 0x44, 0x2c, 0xcd, 0xeb, 0x6f, 0x01, 0x8a, 0x64, 0xb0, 0x2c, 0x4a, 0xaf, 0x79, 0x0e, 0x6d, 0x42, 0x36, 0x12, 0x97, 0x34, 0x55, 0xe6, 0x13, 0x45, 0x15, 0xf8, 0xf8, 0xa7, 0xa3, 0x3d, 0xf8, 0x57, 0xd4, 0x75, 0xa5, 0xaa, 0x36, 0x64, 0xd5, 0x98, 0x57, 0xd8, 0x81, 0xed, 0xf9, 0xf4, 0x58, 0xa6, 0x78, 0xc3, 0xc1, 0x7a, 0x64, 0x4d, 0xfc, 0x81, 0x34, 0x94, 0x2a, 0x16, 0x0d, 0x45, 0x53, 0xcd, 0x86, 0x26, 0xc9, 0x33, 0x54, 0xff, 0x43, 0x21, 0x96, 0xab, 0x6b, 0xa7, 0x62, 0xdd, 0x3c, 0x17, 0x75, 0xdd, 0xa8, 0x61, 0xad, 0x59, 0xad, 0xf1, 0x1c, 0x7a, 0x02, 0x8f, 0x97, 0x55, 0x99, 0x7a, 0x4d, 0x94, 0xb4, 0x0b, 0x3e, 0x81, 0x8a, 0xf0, 0x28, 0x56, 0x2c, 0x29, 0xba, 0x81, 0x95, 0x93, 0xa6, 0x21, 0x4b, 0x11, 0xe2, 0xa4, 0xef, 0x47, 0xac, 0x56, 0x53, 0x4f, 0x34, 0x11, 0x4b, 0xb2, 0xc4, 0xaf, 0x54, 0xbe, 0x72, 0xb0, 0x13, 0xcc, 0x5c, 0x9a, 0xae, 0xca, 0x64, 0x13, 0xc4, 0x73, 0x05, 0x31, 0x48, 0xdd, 0x9d, 0x42, 0x54, 0xb9, 0xff, 0x3f, 0x36, 0x7f, 0xf4, 0x80, 0x63, 0x7e, 0x72, 0x71, 0x73, 0xbb, 0xcf, 0x7d, 0xbf, 0xdd, 0xe7, 0x7e, 0xde, 0xee, 0x73, 0x6f, 0x94, 0x6e, 0x8f, 0x5d, 0x0e, 0xdb, 0x42, 0xc7, 0xe9, 0x47, 0xaf, 0x59, 0xa1, 0x4b, 0xed, 0x52, 0x70, 0x9d, 0x2e, 0xba, 0x71, 0x8f, 0xe3, 0xb1, 0x51, 0xb9, 0xbd, 0x16, 0x54, 0x1f, 0xfd, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x33, 0x53, 0x4b, 0xe1, 0xaf, 0x07, 0x00, 0x00, } func (m *HeartbeatRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *HeartbeatRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *HeartbeatRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Metadata) > 0 { for k := range m.Metadata { v := m.Metadata[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = encodeVarintExecutor(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = encodeVarintExecutor(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintExecutor(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x2a } } if len(m.ShardStatusReports) > 0 { for k := range m.ShardStatusReports { v := m.ShardStatusReports[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintExecutor(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i -= len(k) copy(dAtA[i:], k) i = encodeVarintExecutor(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintExecutor(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x22 } } if m.Status != 0 { i = encodeVarintExecutor(dAtA, i, uint64(m.Status)) i-- dAtA[i] = 0x18 } if len(m.ExecutorId) > 0 { i -= len(m.ExecutorId) copy(dAtA[i:], m.ExecutorId) i = encodeVarintExecutor(dAtA, i, uint64(len(m.ExecutorId))) i-- dAtA[i] = 0x12 } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = encodeVarintExecutor(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ShardStatusReport) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ShardStatusReport) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ShardStatusReport) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.ShardLoad != 0 { i -= 8 encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.ShardLoad)))) i-- dAtA[i] = 0x11 } if m.Status != 0 { i = encodeVarintExecutor(dAtA, i, uint64(m.Status)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *HeartbeatResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *HeartbeatResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *HeartbeatResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.MigrationMode != 0 { i = encodeVarintExecutor(dAtA, i, uint64(m.MigrationMode)) i-- dAtA[i] = 0x10 } if len(m.ShardAssignments) > 0 { for k := range m.ShardAssignments { v := m.ShardAssignments[k] baseI := i if v != nil { { size, err := v.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintExecutor(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } i -= len(k) copy(dAtA[i:], k) i = encodeVarintExecutor(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintExecutor(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ShardAssignment) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ShardAssignment) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ShardAssignment) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.Status != 0 { i = encodeVarintExecutor(dAtA, i, uint64(m.Status)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func encodeVarintExecutor(dAtA []byte, offset int, v uint64) int { offset -= sovExecutor(v) base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) return base } func (m *HeartbeatRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Namespace) if l > 0 { n += 1 + l + sovExecutor(uint64(l)) } l = len(m.ExecutorId) if l > 0 { n += 1 + l + sovExecutor(uint64(l)) } if m.Status != 0 { n += 1 + sovExecutor(uint64(m.Status)) } if len(m.ShardStatusReports) > 0 { for k, v := range m.ShardStatusReports { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovExecutor(uint64(l)) } mapEntrySize := 1 + len(k) + sovExecutor(uint64(len(k))) + l n += mapEntrySize + 1 + sovExecutor(uint64(mapEntrySize)) } } if len(m.Metadata) > 0 { for k, v := range m.Metadata { _ = k _ = v mapEntrySize := 1 + len(k) + sovExecutor(uint64(len(k))) + 1 + len(v) + sovExecutor(uint64(len(v))) n += mapEntrySize + 1 + sovExecutor(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ShardStatusReport) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Status != 0 { n += 1 + sovExecutor(uint64(m.Status)) } if m.ShardLoad != 0 { n += 9 } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *HeartbeatResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.ShardAssignments) > 0 { for k, v := range m.ShardAssignments { _ = k _ = v l = 0 if v != nil { l = v.Size() l += 1 + sovExecutor(uint64(l)) } mapEntrySize := 1 + len(k) + sovExecutor(uint64(len(k))) + l n += mapEntrySize + 1 + sovExecutor(uint64(mapEntrySize)) } } if m.MigrationMode != 0 { n += 1 + sovExecutor(uint64(m.MigrationMode)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ShardAssignment) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.Status != 0 { n += 1 + sovExecutor(uint64(m.Status)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func sovExecutor(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozExecutor(x uint64) (n int) { return sovExecutor(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *HeartbeatRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: HeartbeatRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: HeartbeatRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthExecutor } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthExecutor } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExecutorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthExecutor } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthExecutor } if postIndex > l { return io.ErrUnexpectedEOF } m.ExecutorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } m.Status = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Status |= ExecutorStatus(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ShardStatusReports", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthExecutor } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthExecutor } if postIndex > l { return io.ErrUnexpectedEOF } if m.ShardStatusReports == nil { m.ShardStatusReports = make(map[string]*ShardStatusReport) } var mapkey string var mapvalue *ShardStatusReport for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthExecutor } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthExecutor } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthExecutor } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthExecutor } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &ShardStatusReport{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipExecutor(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthExecutor } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ShardStatusReports[mapkey] = mapvalue iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthExecutor } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthExecutor } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthExecutor } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthExecutor } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return ErrInvalidLengthExecutor } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return ErrInvalidLengthExecutor } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := skipExecutor(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthExecutor } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Metadata[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipExecutor(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthExecutor } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ShardStatusReport) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ShardStatusReport: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ShardStatusReport: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } m.Status = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Status |= ShardStatus(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field ShardLoad", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.ShardLoad = float64(math.Float64frombits(v)) default: iNdEx = preIndex skippy, err := skipExecutor(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthExecutor } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *HeartbeatResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: HeartbeatResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: HeartbeatResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ShardAssignments", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthExecutor } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthExecutor } if postIndex > l { return io.ErrUnexpectedEOF } if m.ShardAssignments == nil { m.ShardAssignments = make(map[string]*ShardAssignment) } var mapkey string var mapvalue *ShardAssignment for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthExecutor } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthExecutor } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return ErrInvalidLengthExecutor } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return ErrInvalidLengthExecutor } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &ShardAssignment{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := skipExecutor(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthExecutor } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ShardAssignments[mapkey] = mapvalue iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MigrationMode", wireType) } m.MigrationMode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MigrationMode |= MigrationMode(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := skipExecutor(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthExecutor } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ShardAssignment) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ShardAssignment: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ShardAssignment: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } m.Status = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowExecutor } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Status |= AssignmentStatus(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := skipExecutor(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthExecutor } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func skipExecutor(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowExecutor } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } wireType := int(wire & 0x7) switch wireType { case 0: for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowExecutor } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } iNdEx++ if dAtA[iNdEx-1] < 0x80 { break } } case 1: iNdEx += 8 case 2: var length int for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowExecutor } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { break } } if length < 0 { return 0, ErrInvalidLengthExecutor } iNdEx += length case 3: depth++ case 4: if depth == 0 { return 0, ErrUnexpectedEndOfGroupExecutor } depth-- case 5: iNdEx += 4 default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } if iNdEx < 0 { return 0, ErrInvalidLengthExecutor } if depth == 0 { return iNdEx, nil } } return 0, io.ErrUnexpectedEOF } var ( ErrInvalidLengthExecutor = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowExecutor = fmt.Errorf("proto: integer overflow") ErrUnexpectedEndOfGroupExecutor = fmt.Errorf("proto: unexpected end of group") ) ================================================ FILE: .gen/proto/sharddistributor/v1/executor.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/sharddistributor/v1/executor.proto package sharddistributorv1 import ( "context" "io/ioutil" "reflect" "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" "go.uber.org/fx" "go.uber.org/yarpc" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/api/x/restriction" "go.uber.org/yarpc/encoding/protobuf" "go.uber.org/yarpc/encoding/protobuf/reflection" ) var _ = ioutil.NopCloser // ShardDistributorExecutorAPIYARPCClient is the YARPC client-side interface for the ShardDistributorExecutorAPI service. type ShardDistributorExecutorAPIYARPCClient interface { Heartbeat(context.Context, *HeartbeatRequest, ...yarpc.CallOption) (*HeartbeatResponse, error) } func newShardDistributorExecutorAPIYARPCClient(clientConfig transport.ClientConfig, anyResolver jsonpb.AnyResolver, options ...protobuf.ClientOption) ShardDistributorExecutorAPIYARPCClient { return &_ShardDistributorExecutorAPIYARPCCaller{protobuf.NewStreamClient( protobuf.ClientParams{ ServiceName: "uber.cadence.sharddistributor.v1.ShardDistributorExecutorAPI", ClientConfig: clientConfig, AnyResolver: anyResolver, Options: options, }, )} } // NewShardDistributorExecutorAPIYARPCClient builds a new YARPC client for the ShardDistributorExecutorAPI service. func NewShardDistributorExecutorAPIYARPCClient(clientConfig transport.ClientConfig, options ...protobuf.ClientOption) ShardDistributorExecutorAPIYARPCClient { return newShardDistributorExecutorAPIYARPCClient(clientConfig, nil, options...) } // ShardDistributorExecutorAPIYARPCServer is the YARPC server-side interface for the ShardDistributorExecutorAPI service. type ShardDistributorExecutorAPIYARPCServer interface { Heartbeat(context.Context, *HeartbeatRequest) (*HeartbeatResponse, error) } type buildShardDistributorExecutorAPIYARPCProceduresParams struct { Server ShardDistributorExecutorAPIYARPCServer AnyResolver jsonpb.AnyResolver } func buildShardDistributorExecutorAPIYARPCProcedures(params buildShardDistributorExecutorAPIYARPCProceduresParams) []transport.Procedure { handler := &_ShardDistributorExecutorAPIYARPCHandler{params.Server} return protobuf.BuildProcedures( protobuf.BuildProceduresParams{ ServiceName: "uber.cadence.sharddistributor.v1.ShardDistributorExecutorAPI", UnaryHandlerParams: []protobuf.BuildProceduresUnaryHandlerParams{ { MethodName: "Heartbeat", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.Heartbeat, NewRequest: newShardDistributorExecutorAPIServiceHeartbeatYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, }, OnewayHandlerParams: []protobuf.BuildProceduresOnewayHandlerParams{}, StreamHandlerParams: []protobuf.BuildProceduresStreamHandlerParams{}, }, ) } // BuildShardDistributorExecutorAPIYARPCProcedures prepares an implementation of the ShardDistributorExecutorAPI service for YARPC registration. func BuildShardDistributorExecutorAPIYARPCProcedures(server ShardDistributorExecutorAPIYARPCServer) []transport.Procedure { return buildShardDistributorExecutorAPIYARPCProcedures(buildShardDistributorExecutorAPIYARPCProceduresParams{Server: server}) } // FxShardDistributorExecutorAPIYARPCClientParams defines the input // for NewFxShardDistributorExecutorAPIYARPCClient. It provides the // paramaters to get a ShardDistributorExecutorAPIYARPCClient in an // Fx application. type FxShardDistributorExecutorAPIYARPCClientParams struct { fx.In Provider yarpc.ClientConfig AnyResolver jsonpb.AnyResolver `name:"yarpcfx" optional:"true"` Restriction restriction.Checker `optional:"true"` } // FxShardDistributorExecutorAPIYARPCClientResult defines the output // of NewFxShardDistributorExecutorAPIYARPCClient. It provides a // ShardDistributorExecutorAPIYARPCClient to an Fx application. type FxShardDistributorExecutorAPIYARPCClientResult struct { fx.Out Client ShardDistributorExecutorAPIYARPCClient // We are using an fx.Out struct here instead of just returning a client // so that we can add more values or add named versions of the client in // the future without breaking any existing code. } // NewFxShardDistributorExecutorAPIYARPCClient provides a ShardDistributorExecutorAPIYARPCClient // to an Fx application using the given name for routing. // // fx.Provide( // sharddistributorv1.NewFxShardDistributorExecutorAPIYARPCClient("service-name"), // ... // ) func NewFxShardDistributorExecutorAPIYARPCClient(name string, options ...protobuf.ClientOption) interface{} { return func(params FxShardDistributorExecutorAPIYARPCClientParams) FxShardDistributorExecutorAPIYARPCClientResult { cc := params.Provider.ClientConfig(name) if params.Restriction != nil { if namer, ok := cc.GetUnaryOutbound().(transport.Namer); ok { if err := params.Restriction.Check(protobuf.Encoding, namer.TransportName()); err != nil { panic(err.Error()) } } } return FxShardDistributorExecutorAPIYARPCClientResult{ Client: newShardDistributorExecutorAPIYARPCClient(cc, params.AnyResolver, options...), } } } // FxShardDistributorExecutorAPIYARPCProceduresParams defines the input // for NewFxShardDistributorExecutorAPIYARPCProcedures. It provides the // paramaters to get ShardDistributorExecutorAPIYARPCServer procedures in an // Fx application. type FxShardDistributorExecutorAPIYARPCProceduresParams struct { fx.In Server ShardDistributorExecutorAPIYARPCServer AnyResolver jsonpb.AnyResolver `name:"yarpcfx" optional:"true"` } // FxShardDistributorExecutorAPIYARPCProceduresResult defines the output // of NewFxShardDistributorExecutorAPIYARPCProcedures. It provides // ShardDistributorExecutorAPIYARPCServer procedures to an Fx application. // // The procedures are provided to the "yarpcfx" value group. // Dig 1.2 or newer must be used for this feature to work. type FxShardDistributorExecutorAPIYARPCProceduresResult struct { fx.Out Procedures []transport.Procedure `group:"yarpcfx"` ReflectionMeta reflection.ServerMeta `group:"yarpcfx"` } // NewFxShardDistributorExecutorAPIYARPCProcedures provides ShardDistributorExecutorAPIYARPCServer procedures to an Fx application. // It expects a ShardDistributorExecutorAPIYARPCServer to be present in the container. // // fx.Provide( // sharddistributorv1.NewFxShardDistributorExecutorAPIYARPCProcedures(), // ... // ) func NewFxShardDistributorExecutorAPIYARPCProcedures() interface{} { return func(params FxShardDistributorExecutorAPIYARPCProceduresParams) FxShardDistributorExecutorAPIYARPCProceduresResult { return FxShardDistributorExecutorAPIYARPCProceduresResult{ Procedures: buildShardDistributorExecutorAPIYARPCProcedures(buildShardDistributorExecutorAPIYARPCProceduresParams{ Server: params.Server, AnyResolver: params.AnyResolver, }), ReflectionMeta: ShardDistributorExecutorAPIReflectionMeta, } } } // ShardDistributorExecutorAPIReflectionMeta is the reflection server metadata // required for using the gRPC reflection protocol with YARPC. // // See https://github.com/grpc/grpc/blob/master/doc/server-reflection.md. var ShardDistributorExecutorAPIReflectionMeta = reflection.ServerMeta{ ServiceName: "uber.cadence.sharddistributor.v1.ShardDistributorExecutorAPI", FileDescriptors: yarpcFileDescriptorClosure5aab034437d08cca, } type _ShardDistributorExecutorAPIYARPCCaller struct { streamClient protobuf.StreamClient } func (c *_ShardDistributorExecutorAPIYARPCCaller) Heartbeat(ctx context.Context, request *HeartbeatRequest, options ...yarpc.CallOption) (*HeartbeatResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "Heartbeat", request, newShardDistributorExecutorAPIServiceHeartbeatYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*HeartbeatResponse) if !ok { return nil, protobuf.CastError(emptyShardDistributorExecutorAPIServiceHeartbeatYARPCResponse, responseMessage) } return response, err } type _ShardDistributorExecutorAPIYARPCHandler struct { server ShardDistributorExecutorAPIYARPCServer } func (h *_ShardDistributorExecutorAPIYARPCHandler) Heartbeat(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *HeartbeatRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*HeartbeatRequest) if !ok { return nil, protobuf.CastError(emptyShardDistributorExecutorAPIServiceHeartbeatYARPCRequest, requestMessage) } } response, err := h.server.Heartbeat(ctx, request) if response == nil { return nil, err } return response, err } func newShardDistributorExecutorAPIServiceHeartbeatYARPCRequest() proto.Message { return &HeartbeatRequest{} } func newShardDistributorExecutorAPIServiceHeartbeatYARPCResponse() proto.Message { return &HeartbeatResponse{} } var ( emptyShardDistributorExecutorAPIServiceHeartbeatYARPCRequest = &HeartbeatRequest{} emptyShardDistributorExecutorAPIServiceHeartbeatYARPCResponse = &HeartbeatResponse{} ) var yarpcFileDescriptorClosure5aab034437d08cca = [][]byte{ // uber/cadence/sharddistributor/v1/executor.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xdd, 0x6e, 0xda, 0x48, 0x14, 0x5e, 0x43, 0x12, 0x2d, 0x07, 0x85, 0x35, 0xa3, 0xfc, 0xb0, 0x24, 0xd1, 0xa2, 0x68, 0xb5, 0x1b, 0x51, 0xd5, 0x14, 0x72, 0x53, 0x35, 0x37, 0x75, 0x62, 0x0b, 0x26, 0x05, 0x3b, 0x1a, 0x1b, 0xfa, 0xa3, 0x56, 0xd6, 0x80, 0x47, 0x04, 0x35, 0xd8, 0xd4, 0x1e, 0x50, 0x53, 0xf5, 0xb2, 0x6f, 0xd0, 0x17, 0xe9, 0x63, 0xf4, 0x1d, 0xfa, 0x32, 0x95, 0x6d, 0x08, 0xd8, 0x50, 0xd1, 0xe4, 0xce, 0x3e, 0xe7, 0x7c, 0xdf, 0x77, 0x7c, 0xbe, 0x33, 0x1e, 0xa8, 0x8c, 0xbb, 0xcc, 0xab, 0xf4, 0xa8, 0xcd, 0x9c, 0x1e, 0xab, 0xf8, 0xd7, 0xd4, 0xb3, 0xed, 0x81, 0xcf, 0xbd, 0x41, 0x77, 0xcc, 0x5d, 0xaf, 0x32, 0xa9, 0x56, 0xd8, 0x47, 0xd6, 0x0b, 0x9e, 0xa5, 0x91, 0xe7, 0x72, 0x17, 0x95, 0x02, 0x80, 0x34, 0x05, 0x48, 0x49, 0x80, 0x34, 0xa9, 0x1e, 0x7f, 0xdb, 0x00, 0xb1, 0xc1, 0xa8, 0xc7, 0xbb, 0x8c, 0x72, 0xc2, 0x3e, 0x8c, 0x99, 0xcf, 0xd1, 0x21, 0x64, 0x1c, 0x3a, 0x64, 0xfe, 0x88, 0xf6, 0x58, 0x41, 0x28, 0x09, 0x27, 0x19, 0x32, 0x0f, 0xa0, 0x7f, 0x20, 0x3b, 0x93, 0xb1, 0x06, 0x76, 0x21, 0x15, 0xe6, 0x61, 0x16, 0xc2, 0x36, 0x6a, 0xc0, 0x96, 0xcf, 0x29, 0x1f, 0xfb, 0x85, 0x74, 0x49, 0x38, 0xc9, 0xd5, 0x9e, 0x48, 0xeb, 0xda, 0x90, 0xd4, 0x29, 0xda, 0x08, 0x71, 0x64, 0x8a, 0x47, 0x9f, 0x61, 0x27, 0xac, 0xb6, 0xa2, 0x77, 0xcb, 0x63, 0x23, 0xd7, 0xe3, 0x7e, 0x61, 0xa3, 0x94, 0x3e, 0xc9, 0xd6, 0x2e, 0xd7, 0xf3, 0x26, 0x3f, 0x4d, 0x32, 0x82, 0xa2, 0xa9, 0x4a, 0x44, 0xa6, 0x3a, 0xdc, 0xbb, 0x25, 0xc8, 0x5f, 0x4a, 0xa0, 0xb7, 0xf0, 0xe7, 0x90, 0x71, 0x6a, 0x53, 0x4e, 0x0b, 0x9b, 0xa1, 0xe2, 0xf3, 0x07, 0x28, 0xb6, 0xa6, 0x14, 0x91, 0xce, 0x1d, 0x63, 0xf1, 0x13, 0xec, 0xff, 0xa2, 0x19, 0x24, 0x42, 0xfa, 0x3d, 0xbb, 0x9d, 0x4e, 0x3e, 0x78, 0x44, 0x18, 0x36, 0x27, 0xf4, 0x66, 0xcc, 0xc2, 0x69, 0x67, 0x6b, 0xa7, 0xeb, 0xfb, 0x58, 0xe2, 0x26, 0x11, 0xc3, 0xb3, 0xd4, 0x53, 0xa1, 0x78, 0x06, 0xdb, 0xb1, 0xb6, 0x56, 0x28, 0xee, 0x2c, 0x2a, 0x66, 0x16, 0xc0, 0xc7, 0xb7, 0x90, 0x5f, 0x22, 0x47, 0xea, 0x9d, 0xe7, 0x42, 0xe8, 0xf9, 0xe3, 0xfb, 0x75, 0x38, 0x33, 0xfc, 0x08, 0x20, 0x32, 0xfc, 0xc6, 0xa5, 0xd1, 0x6a, 0x09, 0x24, 0x13, 0x46, 0x9a, 0x2e, 0xb5, 0x8f, 0x7f, 0xa4, 0x20, 0xbf, 0x30, 0x60, 0x7f, 0xe4, 0x3a, 0x3e, 0x43, 0x13, 0xc8, 0x47, 0x20, 0xea, 0xfb, 0x83, 0xbe, 0x33, 0x64, 0x0e, 0x0f, 0xda, 0x08, 0x0c, 0xc3, 0xf7, 0x32, 0x2c, 0xe2, 0x8b, 0x1a, 0x93, 0xe7, 0x5c, 0x91, 0x73, 0xa2, 0x9f, 0x08, 0xa3, 0x0e, 0xe4, 0x86, 0x83, 0xbe, 0x47, 0xf9, 0xc0, 0x75, 0xac, 0xa1, 0x6b, 0x47, 0xb3, 0xca, 0xd5, 0x2a, 0xeb, 0x45, 0x5b, 0x33, 0x5c, 0xcb, 0xb5, 0x19, 0xd9, 0x1e, 0x2e, 0xbe, 0x16, 0x27, 0xb0, 0xbb, 0xb2, 0x85, 0x15, 0x2e, 0xd5, 0xe3, 0x7b, 0x51, 0xfd, 0xcd, 0xa9, 0xcf, 0x99, 0x17, 0x8d, 0x7d, 0x07, 0x7f, 0x25, 0xb2, 0xe8, 0x32, 0x61, 0x6b, 0x6d, 0xbd, 0xc0, 0x1c, 0x1d, 0xf7, 0xb6, 0xfc, 0x45, 0x80, 0x5c, 0xfc, 0x9c, 0xa3, 0x03, 0xd8, 0x57, 0x5f, 0xa9, 0x17, 0x6d, 0x53, 0x27, 0x96, 0x61, 0xca, 0x66, 0xdb, 0xb0, 0xb0, 0xd6, 0x91, 0x9b, 0x58, 0x11, 0xff, 0x40, 0x45, 0xd8, 0x4b, 0x26, 0xe5, 0x0b, 0x13, 0x77, 0x54, 0x51, 0x40, 0x87, 0x50, 0x48, 0xe6, 0x14, 0x22, 0x63, 0x0d, 0x6b, 0x75, 0x31, 0xb5, 0x8a, 0x36, 0xcc, 0xaa, 0x8a, 0x98, 0x2e, 0x77, 0x20, 0xbb, 0xb0, 0x79, 0xa8, 0x00, 0x3b, 0x46, 0x43, 0x26, 0xca, 0xb2, 0xfe, 0x1e, 0xa0, 0x58, 0x86, 0xa8, 0xb2, 0xf2, 0x5a, 0x14, 0xd0, 0x2e, 0xe4, 0x63, 0x71, 0x45, 0xd7, 0x54, 0x31, 0x55, 0xd6, 0x40, 0x4c, 0x7e, 0x3a, 0x3a, 0x82, 0xbf, 0x65, 0xc3, 0xc0, 0x75, 0xad, 0xa5, 0x6a, 0xe6, 0xb2, 0xc2, 0x01, 0xec, 0x2f, 0xa7, 0xa7, 0x32, 0xe5, 0xef, 0x02, 0x6c, 0xc7, 0xd6, 0x24, 0x18, 0x48, 0x0b, 0xd7, 0x89, 0x6c, 0x62, 0x5d, 0xb3, 0x5a, 0xba, 0xa2, 0x2e, 0x50, 0xfd, 0x0b, 0xa5, 0x44, 0xae, 0xa9, 0x5f, 0xc8, 0x4d, 0xeb, 0x4a, 0x36, 0x0c, 0xb3, 0x41, 0xf4, 0x76, 0xbd, 0x21, 0x0a, 0xe8, 0x11, 0xfc, 0xbf, 0xae, 0xca, 0x32, 0x1a, 0xb2, 0xa2, 0xbf, 0x14, 0x53, 0xa8, 0x0c, 0xff, 0x25, 0x8a, 0x15, 0x6c, 0x98, 0x04, 0x9f, 0xb7, 0x4d, 0x55, 0x89, 0x11, 0xa7, 0x03, 0x3f, 0x12, 0xb5, 0xba, 0x76, 0xae, 0xcb, 0x44, 0x51, 0x15, 0x71, 0xa3, 0xf6, 0x55, 0x80, 0x83, 0x70, 0xe6, 0xca, 0x7c, 0x55, 0x66, 0x9b, 0x20, 0x5f, 0x61, 0xc4, 0x21, 0x73, 0x77, 0x0a, 0x51, 0xed, 0xfe, 0xff, 0xd8, 0xe2, 0xe9, 0x03, 0x8e, 0xf9, 0xf9, 0x8b, 0x37, 0xb8, 0x3f, 0xe0, 0xd7, 0xe3, 0xae, 0xd4, 0x73, 0x87, 0xf1, 0xab, 0x55, 0xea, 0x33, 0xa7, 0x12, 0x5e, 0xa1, 0xab, 0x6e, 0xd9, 0xb3, 0x64, 0x6c, 0x52, 0xed, 0x6e, 0x85, 0xd5, 0xa7, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x7a, 0x3d, 0x1d, 0x21, 0xa3, 0x07, 0x00, 0x00, }, } func init() { yarpc.RegisterClientBuilder( func(clientConfig transport.ClientConfig, structField reflect.StructField) ShardDistributorExecutorAPIYARPCClient { return NewShardDistributorExecutorAPIYARPCClient(clientConfig, protobuf.ClientBuilderOptions(clientConfig, structField)...) }, ) } ================================================ FILE: .gen/proto/sharddistributor/v1/service.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/sharddistributor/v1/service.proto package sharddistributorv1 import ( fmt "fmt" io "io" math "math" math_bits "math/bits" proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type GetShardOwnerRequest struct { ShardKey string `protobuf:"bytes,1,opt,name=shard_key,json=shardKey,proto3" json:"shard_key,omitempty"` Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetShardOwnerRequest) Reset() { *m = GetShardOwnerRequest{} } func (m *GetShardOwnerRequest) String() string { return proto.CompactTextString(m) } func (*GetShardOwnerRequest) ProtoMessage() {} func (*GetShardOwnerRequest) Descriptor() ([]byte, []int) { return fileDescriptor_0055bfd59dff1f95, []int{0} } func (m *GetShardOwnerRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetShardOwnerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetShardOwnerRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetShardOwnerRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_GetShardOwnerRequest.Merge(m, src) } func (m *GetShardOwnerRequest) XXX_Size() int { return m.Size() } func (m *GetShardOwnerRequest) XXX_DiscardUnknown() { xxx_messageInfo_GetShardOwnerRequest.DiscardUnknown(m) } var xxx_messageInfo_GetShardOwnerRequest proto.InternalMessageInfo func (m *GetShardOwnerRequest) GetShardKey() string { if m != nil { return m.ShardKey } return "" } func (m *GetShardOwnerRequest) GetNamespace() string { if m != nil { return m.Namespace } return "" } type GetShardOwnerResponse struct { Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` Metadata map[string]string `protobuf:"bytes,3,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GetShardOwnerResponse) Reset() { *m = GetShardOwnerResponse{} } func (m *GetShardOwnerResponse) String() string { return proto.CompactTextString(m) } func (*GetShardOwnerResponse) ProtoMessage() {} func (*GetShardOwnerResponse) Descriptor() ([]byte, []int) { return fileDescriptor_0055bfd59dff1f95, []int{1} } func (m *GetShardOwnerResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *GetShardOwnerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_GetShardOwnerResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *GetShardOwnerResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_GetShardOwnerResponse.Merge(m, src) } func (m *GetShardOwnerResponse) XXX_Size() int { return m.Size() } func (m *GetShardOwnerResponse) XXX_DiscardUnknown() { xxx_messageInfo_GetShardOwnerResponse.DiscardUnknown(m) } var xxx_messageInfo_GetShardOwnerResponse proto.InternalMessageInfo func (m *GetShardOwnerResponse) GetOwner() string { if m != nil { return m.Owner } return "" } func (m *GetShardOwnerResponse) GetNamespace() string { if m != nil { return m.Namespace } return "" } func (m *GetShardOwnerResponse) GetMetadata() map[string]string { if m != nil { return m.Metadata } return nil } type NamespaceNotFoundError struct { Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *NamespaceNotFoundError) Reset() { *m = NamespaceNotFoundError{} } func (m *NamespaceNotFoundError) String() string { return proto.CompactTextString(m) } func (*NamespaceNotFoundError) ProtoMessage() {} func (*NamespaceNotFoundError) Descriptor() ([]byte, []int) { return fileDescriptor_0055bfd59dff1f95, []int{2} } func (m *NamespaceNotFoundError) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *NamespaceNotFoundError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_NamespaceNotFoundError.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *NamespaceNotFoundError) XXX_Merge(src proto.Message) { xxx_messageInfo_NamespaceNotFoundError.Merge(m, src) } func (m *NamespaceNotFoundError) XXX_Size() int { return m.Size() } func (m *NamespaceNotFoundError) XXX_DiscardUnknown() { xxx_messageInfo_NamespaceNotFoundError.DiscardUnknown(m) } var xxx_messageInfo_NamespaceNotFoundError proto.InternalMessageInfo func (m *NamespaceNotFoundError) GetNamespace() string { if m != nil { return m.Namespace } return "" } type ShardNotFoundError struct { Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` ShardKey string `protobuf:"bytes,2,opt,name=shard_key,json=shardKey,proto3" json:"shard_key,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ShardNotFoundError) Reset() { *m = ShardNotFoundError{} } func (m *ShardNotFoundError) String() string { return proto.CompactTextString(m) } func (*ShardNotFoundError) ProtoMessage() {} func (*ShardNotFoundError) Descriptor() ([]byte, []int) { return fileDescriptor_0055bfd59dff1f95, []int{3} } func (m *ShardNotFoundError) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ShardNotFoundError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ShardNotFoundError.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ShardNotFoundError) XXX_Merge(src proto.Message) { xxx_messageInfo_ShardNotFoundError.Merge(m, src) } func (m *ShardNotFoundError) XXX_Size() int { return m.Size() } func (m *ShardNotFoundError) XXX_DiscardUnknown() { xxx_messageInfo_ShardNotFoundError.DiscardUnknown(m) } var xxx_messageInfo_ShardNotFoundError proto.InternalMessageInfo func (m *ShardNotFoundError) GetNamespace() string { if m != nil { return m.Namespace } return "" } func (m *ShardNotFoundError) GetShardKey() string { if m != nil { return m.ShardKey } return "" } type WatchNamespaceStateRequest struct { Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *WatchNamespaceStateRequest) Reset() { *m = WatchNamespaceStateRequest{} } func (m *WatchNamespaceStateRequest) String() string { return proto.CompactTextString(m) } func (*WatchNamespaceStateRequest) ProtoMessage() {} func (*WatchNamespaceStateRequest) Descriptor() ([]byte, []int) { return fileDescriptor_0055bfd59dff1f95, []int{4} } func (m *WatchNamespaceStateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *WatchNamespaceStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_WatchNamespaceStateRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *WatchNamespaceStateRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_WatchNamespaceStateRequest.Merge(m, src) } func (m *WatchNamespaceStateRequest) XXX_Size() int { return m.Size() } func (m *WatchNamespaceStateRequest) XXX_DiscardUnknown() { xxx_messageInfo_WatchNamespaceStateRequest.DiscardUnknown(m) } var xxx_messageInfo_WatchNamespaceStateRequest proto.InternalMessageInfo func (m *WatchNamespaceStateRequest) GetNamespace() string { if m != nil { return m.Namespace } return "" } type WatchNamespaceStateResponse struct { // Executor information with assigned shards Executors []*ExecutorInfo `protobuf:"bytes,1,rep,name=executors,proto3" json:"executors,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *WatchNamespaceStateResponse) Reset() { *m = WatchNamespaceStateResponse{} } func (m *WatchNamespaceStateResponse) String() string { return proto.CompactTextString(m) } func (*WatchNamespaceStateResponse) ProtoMessage() {} func (*WatchNamespaceStateResponse) Descriptor() ([]byte, []int) { return fileDescriptor_0055bfd59dff1f95, []int{5} } func (m *WatchNamespaceStateResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *WatchNamespaceStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_WatchNamespaceStateResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *WatchNamespaceStateResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_WatchNamespaceStateResponse.Merge(m, src) } func (m *WatchNamespaceStateResponse) XXX_Size() int { return m.Size() } func (m *WatchNamespaceStateResponse) XXX_DiscardUnknown() { xxx_messageInfo_WatchNamespaceStateResponse.DiscardUnknown(m) } var xxx_messageInfo_WatchNamespaceStateResponse proto.InternalMessageInfo func (m *WatchNamespaceStateResponse) GetExecutors() []*ExecutorInfo { if m != nil { return m.Executors } return nil } type ExecutorInfo struct { ExecutorId string `protobuf:"bytes,1,opt,name=executor_id,json=executorId,proto3" json:"executor_id,omitempty"` Metadata map[string]string `protobuf:"bytes,2,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Shards []*Shard `protobuf:"bytes,3,rep,name=shards,proto3" json:"shards,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ExecutorInfo) Reset() { *m = ExecutorInfo{} } func (m *ExecutorInfo) String() string { return proto.CompactTextString(m) } func (*ExecutorInfo) ProtoMessage() {} func (*ExecutorInfo) Descriptor() ([]byte, []int) { return fileDescriptor_0055bfd59dff1f95, []int{6} } func (m *ExecutorInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ExecutorInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ExecutorInfo.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ExecutorInfo) XXX_Merge(src proto.Message) { xxx_messageInfo_ExecutorInfo.Merge(m, src) } func (m *ExecutorInfo) XXX_Size() int { return m.Size() } func (m *ExecutorInfo) XXX_DiscardUnknown() { xxx_messageInfo_ExecutorInfo.DiscardUnknown(m) } var xxx_messageInfo_ExecutorInfo proto.InternalMessageInfo func (m *ExecutorInfo) GetExecutorId() string { if m != nil { return m.ExecutorId } return "" } func (m *ExecutorInfo) GetMetadata() map[string]string { if m != nil { return m.Metadata } return nil } func (m *ExecutorInfo) GetShards() []*Shard { if m != nil { return m.Shards } return nil } type Shard struct { ShardKey string `protobuf:"bytes,1,opt,name=shard_key,json=shardKey,proto3" json:"shard_key,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *Shard) Reset() { *m = Shard{} } func (m *Shard) String() string { return proto.CompactTextString(m) } func (*Shard) ProtoMessage() {} func (*Shard) Descriptor() ([]byte, []int) { return fileDescriptor_0055bfd59dff1f95, []int{7} } func (m *Shard) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *Shard) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_Shard.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *Shard) XXX_Merge(src proto.Message) { xxx_messageInfo_Shard.Merge(m, src) } func (m *Shard) XXX_Size() int { return m.Size() } func (m *Shard) XXX_DiscardUnknown() { xxx_messageInfo_Shard.DiscardUnknown(m) } var xxx_messageInfo_Shard proto.InternalMessageInfo func (m *Shard) GetShardKey() string { if m != nil { return m.ShardKey } return "" } func init() { proto.RegisterType((*GetShardOwnerRequest)(nil), "uber.cadence.sharddistributor.v1.GetShardOwnerRequest") proto.RegisterType((*GetShardOwnerResponse)(nil), "uber.cadence.sharddistributor.v1.GetShardOwnerResponse") proto.RegisterMapType((map[string]string)(nil), "uber.cadence.sharddistributor.v1.GetShardOwnerResponse.MetadataEntry") proto.RegisterType((*NamespaceNotFoundError)(nil), "uber.cadence.sharddistributor.v1.NamespaceNotFoundError") proto.RegisterType((*ShardNotFoundError)(nil), "uber.cadence.sharddistributor.v1.ShardNotFoundError") proto.RegisterType((*WatchNamespaceStateRequest)(nil), "uber.cadence.sharddistributor.v1.WatchNamespaceStateRequest") proto.RegisterType((*WatchNamespaceStateResponse)(nil), "uber.cadence.sharddistributor.v1.WatchNamespaceStateResponse") proto.RegisterType((*ExecutorInfo)(nil), "uber.cadence.sharddistributor.v1.ExecutorInfo") proto.RegisterMapType((map[string]string)(nil), "uber.cadence.sharddistributor.v1.ExecutorInfo.MetadataEntry") proto.RegisterType((*Shard)(nil), "uber.cadence.sharddistributor.v1.Shard") } func init() { proto.RegisterFile("uber/cadence/sharddistributor/v1/service.proto", fileDescriptor_0055bfd59dff1f95) } var fileDescriptor_0055bfd59dff1f95 = []byte{ // 501 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xd1, 0x6e, 0xd3, 0x30, 0x14, 0x95, 0x53, 0x6d, 0x5a, 0xef, 0x98, 0x84, 0xbc, 0x81, 0xaa, 0x0e, 0x95, 0x2a, 0x42, 0x62, 0x4f, 0x0e, 0x1d, 0xd2, 0x40, 0x1b, 0x08, 0x81, 0x28, 0xa8, 0x02, 0x36, 0xc8, 0x1e, 0x86, 0x78, 0x99, 0xdc, 0xe4, 0xb2, 0x46, 0xa3, 0x71, 0xb1, 0x9d, 0x40, 0xdf, 0x78, 0xe3, 0x07, 0xf8, 0x0f, 0x7e, 0x83, 0x47, 0x3e, 0x01, 0xf5, 0x95, 0x9f, 0x40, 0x71, 0x9d, 0x75, 0x89, 0xba, 0x96, 0x6e, 0x6f, 0xc9, 0xbd, 0xf7, 0x1c, 0x1f, 0xfb, 0x1c, 0x1b, 0x58, 0xd2, 0x45, 0xe9, 0x05, 0x3c, 0xc4, 0x38, 0x40, 0x4f, 0xf5, 0xb8, 0x0c, 0xc3, 0x48, 0x69, 0x19, 0x75, 0x13, 0x2d, 0xa4, 0x97, 0xb6, 0x3c, 0x85, 0x32, 0x8d, 0x02, 0x64, 0x03, 0x29, 0xb4, 0xa0, 0xcd, 0x6c, 0x9e, 0xd9, 0x79, 0x56, 0x9e, 0x67, 0x69, 0xcb, 0x7d, 0x07, 0x1b, 0x2f, 0x51, 0x1f, 0x66, 0x9d, 0x83, 0x2f, 0x31, 0x4a, 0x1f, 0x3f, 0x27, 0xa8, 0x34, 0xdd, 0x84, 0xaa, 0x19, 0x3f, 0x3e, 0xc5, 0x61, 0x8d, 0x34, 0xc9, 0x56, 0xd5, 0x5f, 0x31, 0x85, 0x57, 0x38, 0xa4, 0xb7, 0xa0, 0x1a, 0xf3, 0x3e, 0xaa, 0x01, 0x0f, 0xb0, 0xe6, 0x98, 0xe6, 0xa4, 0xe0, 0xfe, 0x25, 0x70, 0xa3, 0xc4, 0xa9, 0x06, 0x22, 0x56, 0x48, 0x37, 0x60, 0x49, 0x64, 0x05, 0x4b, 0x38, 0xfe, 0x99, 0xcd, 0x46, 0x39, 0xac, 0xf4, 0x51, 0xf3, 0x90, 0x6b, 0x5e, 0xab, 0x34, 0x2b, 0x5b, 0xab, 0xdb, 0x6d, 0x36, 0x6f, 0x57, 0x6c, 0xea, 0xf2, 0xec, 0x8d, 0xe5, 0x69, 0xc7, 0x5a, 0x0e, 0xfd, 0x33, 0xda, 0xfa, 0x1e, 0xac, 0x15, 0x5a, 0xf4, 0x3a, 0x54, 0x26, 0xdb, 0xce, 0x3e, 0x33, 0xe5, 0x29, 0xff, 0x94, 0xe4, 0xfa, 0xc6, 0x3f, 0xbb, 0xce, 0x43, 0xe2, 0xee, 0xc0, 0xcd, 0xfd, 0x5c, 0xec, 0xbe, 0xd0, 0x2f, 0x44, 0x12, 0x87, 0x6d, 0x29, 0x45, 0x69, 0x5f, 0xa4, 0x7c, 0x4a, 0x07, 0x40, 0x8d, 0xc4, 0x05, 0x30, 0x45, 0x53, 0x9c, 0xa2, 0x29, 0xee, 0x2e, 0xd4, 0x8f, 0xb8, 0x0e, 0x7a, 0x67, 0x6a, 0x0e, 0x35, 0xd7, 0x98, 0xfb, 0x39, 0x5b, 0xcc, 0x29, 0x6c, 0x4e, 0xc5, 0x5a, 0xdf, 0x5e, 0x43, 0x15, 0xbf, 0x62, 0x90, 0x9d, 0xae, 0xaa, 0x11, 0x63, 0x02, 0x9b, 0x6f, 0x42, 0xdb, 0x42, 0x3a, 0xf1, 0x47, 0xe1, 0x4f, 0x08, 0xdc, 0xef, 0x0e, 0x5c, 0x3b, 0xdf, 0xa3, 0xb7, 0x61, 0x35, 0xef, 0x1e, 0x47, 0xa1, 0x55, 0x07, 0x79, 0xa9, 0x13, 0xd2, 0xf7, 0xe7, 0x32, 0xe0, 0x98, 0xe5, 0x1f, 0x2d, 0xb6, 0xfc, 0x45, 0xd6, 0xd3, 0x27, 0xb0, 0x6c, 0xb0, 0xca, 0x66, 0xeb, 0xee, 0x7c, 0x5e, 0xe3, 0x9a, 0x6f, 0x61, 0x57, 0xcb, 0xce, 0x1d, 0x58, 0x32, 0x6c, 0x33, 0x6f, 0xdb, 0xf6, 0x4f, 0x07, 0xd6, 0xcd, 0xd8, 0xf3, 0x89, 0x90, 0xa7, 0x6f, 0x3b, 0xf4, 0x1b, 0x81, 0xb5, 0x42, 0xd0, 0xe9, 0xce, 0xc2, 0x37, 0xc3, 0x84, 0xa3, 0xfe, 0xe0, 0x92, 0x37, 0x8a, 0xfe, 0x20, 0xb0, 0x3e, 0x25, 0x38, 0xf4, 0x3f, 0xec, 0xb9, 0x38, 0xab, 0xf5, 0xc7, 0x97, 0x44, 0x8f, 0x45, 0xdd, 0x23, 0xcf, 0x8e, 0x7e, 0x8d, 0x1a, 0xe4, 0xf7, 0xa8, 0x41, 0xfe, 0x8c, 0x1a, 0xe4, 0x43, 0xe7, 0x24, 0xd2, 0xbd, 0xa4, 0xcb, 0x02, 0xd1, 0xf7, 0x0a, 0xef, 0x27, 0x3b, 0xc1, 0xd8, 0x33, 0x0f, 0xe5, 0xb4, 0xa7, 0x74, 0xaf, 0x5c, 0x4b, 0x5b, 0xdd, 0x65, 0x33, 0x7d, 0xff, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xf7, 0x0c, 0x43, 0x88, 0x05, 0x00, 0x00, } func (m *GetShardOwnerRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetShardOwnerRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetShardOwnerRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = encodeVarintService(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0x12 } if len(m.ShardKey) > 0 { i -= len(m.ShardKey) copy(dAtA[i:], m.ShardKey) i = encodeVarintService(dAtA, i, uint64(len(m.ShardKey))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *GetShardOwnerResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GetShardOwnerResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *GetShardOwnerResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Metadata) > 0 { for k := range m.Metadata { v := m.Metadata[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = encodeVarintService(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x1a } } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = encodeVarintService(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0x12 } if len(m.Owner) > 0 { i -= len(m.Owner) copy(dAtA[i:], m.Owner) i = encodeVarintService(dAtA, i, uint64(len(m.Owner))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NamespaceNotFoundError) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NamespaceNotFoundError) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *NamespaceNotFoundError) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = encodeVarintService(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ShardNotFoundError) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ShardNotFoundError) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ShardNotFoundError) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ShardKey) > 0 { i -= len(m.ShardKey) copy(dAtA[i:], m.ShardKey) i = encodeVarintService(dAtA, i, uint64(len(m.ShardKey))) i-- dAtA[i] = 0x12 } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = encodeVarintService(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *WatchNamespaceStateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *WatchNamespaceStateRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *WatchNamespaceStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = encodeVarintService(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *WatchNamespaceStateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *WatchNamespaceStateResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *WatchNamespaceStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Executors) > 0 { for iNdEx := len(m.Executors) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.Executors[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ExecutorInfo) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ExecutorInfo) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ExecutorInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Shards) > 0 { for iNdEx := len(m.Shards) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.Shards[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintService(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } } if len(m.Metadata) > 0 { for k := range m.Metadata { v := m.Metadata[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = encodeVarintService(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = encodeVarintService(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = encodeVarintService(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x12 } } if len(m.ExecutorId) > 0 { i -= len(m.ExecutorId) copy(dAtA[i:], m.ExecutorId) i = encodeVarintService(dAtA, i, uint64(len(m.ExecutorId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Shard) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Shard) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *Shard) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ShardKey) > 0 { i -= len(m.ShardKey) copy(dAtA[i:], m.ShardKey) i = encodeVarintService(dAtA, i, uint64(len(m.ShardKey))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func encodeVarintService(dAtA []byte, offset int, v uint64) int { offset -= sovService(v) base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) return base } func (m *GetShardOwnerRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ShardKey) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.Namespace) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *GetShardOwnerResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Owner) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.Namespace) if l > 0 { n += 1 + l + sovService(uint64(l)) } if len(m.Metadata) > 0 { for k, v := range m.Metadata { _ = k _ = v mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + 1 + len(v) + sovService(uint64(len(v))) n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *NamespaceNotFoundError) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Namespace) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ShardNotFoundError) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Namespace) if l > 0 { n += 1 + l + sovService(uint64(l)) } l = len(m.ShardKey) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *WatchNamespaceStateRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Namespace) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *WatchNamespaceStateResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Executors) > 0 { for _, e := range m.Executors { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ExecutorInfo) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ExecutorId) if l > 0 { n += 1 + l + sovService(uint64(l)) } if len(m.Metadata) > 0 { for k, v := range m.Metadata { _ = k _ = v mapEntrySize := 1 + len(k) + sovService(uint64(len(k))) + 1 + len(v) + sovService(uint64(len(v))) n += mapEntrySize + 1 + sovService(uint64(mapEntrySize)) } } if len(m.Shards) > 0 { for _, e := range m.Shards { l = e.Size() n += 1 + l + sovService(uint64(l)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *Shard) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ShardKey) if l > 0 { n += 1 + l + sovService(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func sovService(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozService(x uint64) (n int) { return sovService(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *GetShardOwnerRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetShardOwnerRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetShardOwnerRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ShardKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ShardKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GetShardOwnerResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GetShardOwnerResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GetShardOwnerResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Owner = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return ErrInvalidLengthService } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return ErrInvalidLengthService } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Metadata[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NamespaceNotFoundError) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NamespaceNotFoundError: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NamespaceNotFoundError: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ShardNotFoundError) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ShardNotFoundError: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ShardNotFoundError: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ShardKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ShardKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *WatchNamespaceStateRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: WatchNamespaceStateRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: WatchNamespaceStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *WatchNamespaceStateResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: WatchNamespaceStateResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: WatchNamespaceStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Executors", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Executors = append(m.Executors, &ExecutorInfo{}) if err := m.Executors[len(m.Executors)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ExecutorInfo) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ExecutorInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ExecutorInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExecutorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ExecutorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return ErrInvalidLengthService } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return ErrInvalidLengthService } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return ErrInvalidLengthService } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return ErrInvalidLengthService } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Metadata[mapkey] = mapvalue iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Shards", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.Shards = append(m.Shards, &Shard{}) if err := m.Shards[len(m.Shards)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Shard) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Shard: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Shard: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ShardKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowService } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthService } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthService } if postIndex > l { return io.ErrUnexpectedEOF } m.ShardKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func skipService(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowService } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } wireType := int(wire & 0x7) switch wireType { case 0: for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowService } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } iNdEx++ if dAtA[iNdEx-1] < 0x80 { break } } case 1: iNdEx += 8 case 2: var length int for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowService } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { break } } if length < 0 { return 0, ErrInvalidLengthService } iNdEx += length case 3: depth++ case 4: if depth == 0 { return 0, ErrUnexpectedEndOfGroupService } depth-- case 5: iNdEx += 4 default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } if iNdEx < 0 { return 0, ErrInvalidLengthService } if depth == 0 { return iNdEx, nil } } return 0, io.ErrUnexpectedEOF } var ( ErrInvalidLengthService = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowService = fmt.Errorf("proto: integer overflow") ErrUnexpectedEndOfGroupService = fmt.Errorf("proto: unexpected end of group") ) ================================================ FILE: .gen/proto/sharddistributor/v1/service.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/sharddistributor/v1/service.proto package sharddistributorv1 import ( "context" "io/ioutil" "reflect" "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" "go.uber.org/fx" "go.uber.org/yarpc" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/api/x/restriction" "go.uber.org/yarpc/encoding/protobuf" "go.uber.org/yarpc/encoding/protobuf/reflection" ) var _ = ioutil.NopCloser // ShardDistributorAPIYARPCClient is the YARPC client-side interface for the ShardDistributorAPI service. type ShardDistributorAPIYARPCClient interface { GetShardOwner(context.Context, *GetShardOwnerRequest, ...yarpc.CallOption) (*GetShardOwnerResponse, error) WatchNamespaceState(context.Context, *WatchNamespaceStateRequest, ...yarpc.CallOption) (ShardDistributorAPIServiceWatchNamespaceStateYARPCClient, error) } // ShardDistributorAPIServiceWatchNamespaceStateYARPCClient receives WatchNamespaceStateResponses, returning io.EOF when the stream is complete. type ShardDistributorAPIServiceWatchNamespaceStateYARPCClient interface { Context() context.Context Recv(...yarpc.StreamOption) (*WatchNamespaceStateResponse, error) CloseSend(...yarpc.StreamOption) error } func newShardDistributorAPIYARPCClient(clientConfig transport.ClientConfig, anyResolver jsonpb.AnyResolver, options ...protobuf.ClientOption) ShardDistributorAPIYARPCClient { return &_ShardDistributorAPIYARPCCaller{protobuf.NewStreamClient( protobuf.ClientParams{ ServiceName: "uber.cadence.sharddistributor.v1.ShardDistributorAPI", ClientConfig: clientConfig, AnyResolver: anyResolver, Options: options, }, )} } // NewShardDistributorAPIYARPCClient builds a new YARPC client for the ShardDistributorAPI service. func NewShardDistributorAPIYARPCClient(clientConfig transport.ClientConfig, options ...protobuf.ClientOption) ShardDistributorAPIYARPCClient { return newShardDistributorAPIYARPCClient(clientConfig, nil, options...) } // ShardDistributorAPIYARPCServer is the YARPC server-side interface for the ShardDistributorAPI service. type ShardDistributorAPIYARPCServer interface { GetShardOwner(context.Context, *GetShardOwnerRequest) (*GetShardOwnerResponse, error) WatchNamespaceState(*WatchNamespaceStateRequest, ShardDistributorAPIServiceWatchNamespaceStateYARPCServer) error } // ShardDistributorAPIServiceWatchNamespaceStateYARPCServer sends WatchNamespaceStateResponses. type ShardDistributorAPIServiceWatchNamespaceStateYARPCServer interface { Context() context.Context Send(*WatchNamespaceStateResponse, ...yarpc.StreamOption) error } type buildShardDistributorAPIYARPCProceduresParams struct { Server ShardDistributorAPIYARPCServer AnyResolver jsonpb.AnyResolver } func buildShardDistributorAPIYARPCProcedures(params buildShardDistributorAPIYARPCProceduresParams) []transport.Procedure { handler := &_ShardDistributorAPIYARPCHandler{params.Server} return protobuf.BuildProcedures( protobuf.BuildProceduresParams{ ServiceName: "uber.cadence.sharddistributor.v1.ShardDistributorAPI", UnaryHandlerParams: []protobuf.BuildProceduresUnaryHandlerParams{ { MethodName: "GetShardOwner", Handler: protobuf.NewUnaryHandler( protobuf.UnaryHandlerParams{ Handle: handler.GetShardOwner, NewRequest: newShardDistributorAPIServiceGetShardOwnerYARPCRequest, AnyResolver: params.AnyResolver, }, ), }, }, OnewayHandlerParams: []protobuf.BuildProceduresOnewayHandlerParams{}, StreamHandlerParams: []protobuf.BuildProceduresStreamHandlerParams{ { MethodName: "WatchNamespaceState", Handler: protobuf.NewStreamHandler( protobuf.StreamHandlerParams{ Handle: handler.WatchNamespaceState, }, ), }, }, }, ) } // BuildShardDistributorAPIYARPCProcedures prepares an implementation of the ShardDistributorAPI service for YARPC registration. func BuildShardDistributorAPIYARPCProcedures(server ShardDistributorAPIYARPCServer) []transport.Procedure { return buildShardDistributorAPIYARPCProcedures(buildShardDistributorAPIYARPCProceduresParams{Server: server}) } // FxShardDistributorAPIYARPCClientParams defines the input // for NewFxShardDistributorAPIYARPCClient. It provides the // paramaters to get a ShardDistributorAPIYARPCClient in an // Fx application. type FxShardDistributorAPIYARPCClientParams struct { fx.In Provider yarpc.ClientConfig AnyResolver jsonpb.AnyResolver `name:"yarpcfx" optional:"true"` Restriction restriction.Checker `optional:"true"` } // FxShardDistributorAPIYARPCClientResult defines the output // of NewFxShardDistributorAPIYARPCClient. It provides a // ShardDistributorAPIYARPCClient to an Fx application. type FxShardDistributorAPIYARPCClientResult struct { fx.Out Client ShardDistributorAPIYARPCClient // We are using an fx.Out struct here instead of just returning a client // so that we can add more values or add named versions of the client in // the future without breaking any existing code. } // NewFxShardDistributorAPIYARPCClient provides a ShardDistributorAPIYARPCClient // to an Fx application using the given name for routing. // // fx.Provide( // sharddistributorv1.NewFxShardDistributorAPIYARPCClient("service-name"), // ... // ) func NewFxShardDistributorAPIYARPCClient(name string, options ...protobuf.ClientOption) interface{} { return func(params FxShardDistributorAPIYARPCClientParams) FxShardDistributorAPIYARPCClientResult { cc := params.Provider.ClientConfig(name) if params.Restriction != nil { if namer, ok := cc.GetUnaryOutbound().(transport.Namer); ok { if err := params.Restriction.Check(protobuf.Encoding, namer.TransportName()); err != nil { panic(err.Error()) } } } return FxShardDistributorAPIYARPCClientResult{ Client: newShardDistributorAPIYARPCClient(cc, params.AnyResolver, options...), } } } // FxShardDistributorAPIYARPCProceduresParams defines the input // for NewFxShardDistributorAPIYARPCProcedures. It provides the // paramaters to get ShardDistributorAPIYARPCServer procedures in an // Fx application. type FxShardDistributorAPIYARPCProceduresParams struct { fx.In Server ShardDistributorAPIYARPCServer AnyResolver jsonpb.AnyResolver `name:"yarpcfx" optional:"true"` } // FxShardDistributorAPIYARPCProceduresResult defines the output // of NewFxShardDistributorAPIYARPCProcedures. It provides // ShardDistributorAPIYARPCServer procedures to an Fx application. // // The procedures are provided to the "yarpcfx" value group. // Dig 1.2 or newer must be used for this feature to work. type FxShardDistributorAPIYARPCProceduresResult struct { fx.Out Procedures []transport.Procedure `group:"yarpcfx"` ReflectionMeta reflection.ServerMeta `group:"yarpcfx"` } // NewFxShardDistributorAPIYARPCProcedures provides ShardDistributorAPIYARPCServer procedures to an Fx application. // It expects a ShardDistributorAPIYARPCServer to be present in the container. // // fx.Provide( // sharddistributorv1.NewFxShardDistributorAPIYARPCProcedures(), // ... // ) func NewFxShardDistributorAPIYARPCProcedures() interface{} { return func(params FxShardDistributorAPIYARPCProceduresParams) FxShardDistributorAPIYARPCProceduresResult { return FxShardDistributorAPIYARPCProceduresResult{ Procedures: buildShardDistributorAPIYARPCProcedures(buildShardDistributorAPIYARPCProceduresParams{ Server: params.Server, AnyResolver: params.AnyResolver, }), ReflectionMeta: ShardDistributorAPIReflectionMeta, } } } // ShardDistributorAPIReflectionMeta is the reflection server metadata // required for using the gRPC reflection protocol with YARPC. // // See https://github.com/grpc/grpc/blob/master/doc/server-reflection.md. var ShardDistributorAPIReflectionMeta = reflection.ServerMeta{ ServiceName: "uber.cadence.sharddistributor.v1.ShardDistributorAPI", FileDescriptors: yarpcFileDescriptorClosure0055bfd59dff1f95, } type _ShardDistributorAPIYARPCCaller struct { streamClient protobuf.StreamClient } func (c *_ShardDistributorAPIYARPCCaller) GetShardOwner(ctx context.Context, request *GetShardOwnerRequest, options ...yarpc.CallOption) (*GetShardOwnerResponse, error) { responseMessage, err := c.streamClient.Call(ctx, "GetShardOwner", request, newShardDistributorAPIServiceGetShardOwnerYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*GetShardOwnerResponse) if !ok { return nil, protobuf.CastError(emptyShardDistributorAPIServiceGetShardOwnerYARPCResponse, responseMessage) } return response, err } func (c *_ShardDistributorAPIYARPCCaller) WatchNamespaceState(ctx context.Context, request *WatchNamespaceStateRequest, options ...yarpc.CallOption) (ShardDistributorAPIServiceWatchNamespaceStateYARPCClient, error) { stream, err := c.streamClient.CallStream(ctx, "WatchNamespaceState", options...) if err != nil { return nil, err } if err := stream.Send(request); err != nil { return nil, err } return &_ShardDistributorAPIServiceWatchNamespaceStateYARPCClient{stream: stream}, nil } type _ShardDistributorAPIYARPCHandler struct { server ShardDistributorAPIYARPCServer } func (h *_ShardDistributorAPIYARPCHandler) GetShardOwner(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { var request *GetShardOwnerRequest var ok bool if requestMessage != nil { request, ok = requestMessage.(*GetShardOwnerRequest) if !ok { return nil, protobuf.CastError(emptyShardDistributorAPIServiceGetShardOwnerYARPCRequest, requestMessage) } } response, err := h.server.GetShardOwner(ctx, request) if response == nil { return nil, err } return response, err } func (h *_ShardDistributorAPIYARPCHandler) WatchNamespaceState(serverStream *protobuf.ServerStream) error { requestMessage, err := serverStream.Receive(newShardDistributorAPIServiceWatchNamespaceStateYARPCRequest) if requestMessage == nil { return err } request, ok := requestMessage.(*WatchNamespaceStateRequest) if !ok { return protobuf.CastError(emptyShardDistributorAPIServiceWatchNamespaceStateYARPCRequest, requestMessage) } return h.server.WatchNamespaceState(request, &_ShardDistributorAPIServiceWatchNamespaceStateYARPCServer{serverStream: serverStream}) } type _ShardDistributorAPIServiceWatchNamespaceStateYARPCClient struct { stream *protobuf.ClientStream } func (c *_ShardDistributorAPIServiceWatchNamespaceStateYARPCClient) Context() context.Context { return c.stream.Context() } func (c *_ShardDistributorAPIServiceWatchNamespaceStateYARPCClient) Recv(options ...yarpc.StreamOption) (*WatchNamespaceStateResponse, error) { responseMessage, err := c.stream.Receive(newShardDistributorAPIServiceWatchNamespaceStateYARPCResponse, options...) if responseMessage == nil { return nil, err } response, ok := responseMessage.(*WatchNamespaceStateResponse) if !ok { return nil, protobuf.CastError(emptyShardDistributorAPIServiceWatchNamespaceStateYARPCResponse, responseMessage) } return response, err } func (c *_ShardDistributorAPIServiceWatchNamespaceStateYARPCClient) CloseSend(options ...yarpc.StreamOption) error { return c.stream.Close(options...) } type _ShardDistributorAPIServiceWatchNamespaceStateYARPCServer struct { serverStream *protobuf.ServerStream } func (s *_ShardDistributorAPIServiceWatchNamespaceStateYARPCServer) Context() context.Context { return s.serverStream.Context() } func (s *_ShardDistributorAPIServiceWatchNamespaceStateYARPCServer) Send(response *WatchNamespaceStateResponse, options ...yarpc.StreamOption) error { return s.serverStream.Send(response, options...) } func newShardDistributorAPIServiceGetShardOwnerYARPCRequest() proto.Message { return &GetShardOwnerRequest{} } func newShardDistributorAPIServiceGetShardOwnerYARPCResponse() proto.Message { return &GetShardOwnerResponse{} } func newShardDistributorAPIServiceWatchNamespaceStateYARPCRequest() proto.Message { return &WatchNamespaceStateRequest{} } func newShardDistributorAPIServiceWatchNamespaceStateYARPCResponse() proto.Message { return &WatchNamespaceStateResponse{} } var ( emptyShardDistributorAPIServiceGetShardOwnerYARPCRequest = &GetShardOwnerRequest{} emptyShardDistributorAPIServiceGetShardOwnerYARPCResponse = &GetShardOwnerResponse{} emptyShardDistributorAPIServiceWatchNamespaceStateYARPCRequest = &WatchNamespaceStateRequest{} emptyShardDistributorAPIServiceWatchNamespaceStateYARPCResponse = &WatchNamespaceStateResponse{} ) var yarpcFileDescriptorClosure0055bfd59dff1f95 = [][]byte{ // uber/cadence/sharddistributor/v1/service.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xd1, 0x6e, 0xd3, 0x30, 0x14, 0x95, 0x53, 0x6d, 0x5a, 0xef, 0x98, 0x84, 0xbc, 0x81, 0xaa, 0x0e, 0x89, 0x2a, 0x42, 0x62, 0x4f, 0x0e, 0x1d, 0xd2, 0x40, 0x1b, 0x08, 0x81, 0x28, 0xa8, 0x1a, 0x6c, 0x90, 0x3d, 0x80, 0x78, 0x99, 0xdc, 0xe4, 0xb2, 0x46, 0xa3, 0x76, 0xb1, 0x9d, 0x40, 0xdf, 0x78, 0xe3, 0x07, 0xf8, 0x0f, 0x3e, 0x8c, 0x9f, 0x40, 0x71, 0x9d, 0x75, 0x89, 0xb2, 0x96, 0x8e, 0xb7, 0xe4, 0xde, 0x7b, 0x8e, 0x8f, 0x7d, 0x8e, 0x0d, 0x2c, 0x1d, 0xa0, 0x0a, 0x22, 0x1e, 0xa3, 0x88, 0x30, 0xd0, 0x43, 0xae, 0xe2, 0x38, 0xd1, 0x46, 0x25, 0x83, 0xd4, 0x48, 0x15, 0x64, 0xdd, 0x40, 0xa3, 0xca, 0x92, 0x08, 0xd9, 0x58, 0x49, 0x23, 0x69, 0x27, 0x9f, 0x67, 0x6e, 0x9e, 0x55, 0xe7, 0x59, 0xd6, 0xf5, 0xdf, 0xc3, 0xd6, 0x6b, 0x34, 0x27, 0x79, 0xe7, 0xf8, 0x9b, 0x40, 0x15, 0xe2, 0xd7, 0x14, 0xb5, 0xa1, 0xdb, 0xd0, 0xb4, 0xe3, 0xa7, 0xe7, 0x38, 0x69, 0x91, 0x0e, 0xd9, 0x69, 0x86, 0x6b, 0xb6, 0x70, 0x88, 0x13, 0x7a, 0x07, 0x9a, 0x82, 0x8f, 0x50, 0x8f, 0x79, 0x84, 0x2d, 0xcf, 0x36, 0x67, 0x05, 0xff, 0x0f, 0x81, 0x5b, 0x15, 0x4e, 0x3d, 0x96, 0x42, 0x23, 0xdd, 0x82, 0x15, 0x99, 0x17, 0x1c, 0xe1, 0xf4, 0x67, 0x3e, 0x1b, 0xe5, 0xb0, 0x36, 0x42, 0xc3, 0x63, 0x6e, 0x78, 0xab, 0xd1, 0x69, 0xec, 0xac, 0xef, 0xf6, 0xd8, 0xa2, 0x5d, 0xb1, 0xda, 0xe5, 0xd9, 0x5b, 0xc7, 0xd3, 0x13, 0x46, 0x4d, 0xc2, 0x0b, 0xda, 0xf6, 0x01, 0x6c, 0x94, 0x5a, 0xf4, 0x26, 0x34, 0x66, 0xdb, 0xce, 0x3f, 0x73, 0xe5, 0x19, 0xff, 0x92, 0x16, 0xfa, 0xa6, 0x3f, 0xfb, 0xde, 0x63, 0xe2, 0xef, 0xc1, 0xed, 0xa3, 0x42, 0xec, 0x91, 0x34, 0xaf, 0x64, 0x2a, 0xe2, 0x9e, 0x52, 0xb2, 0xb2, 0x2f, 0x52, 0x3d, 0xa5, 0x63, 0xa0, 0x56, 0xe2, 0x12, 0x98, 0xb2, 0x29, 0x5e, 0xd9, 0x14, 0x7f, 0x1f, 0xda, 0x1f, 0xb8, 0x89, 0x86, 0x17, 0x6a, 0x4e, 0x0c, 0x37, 0x58, 0xf8, 0x39, 0x5f, 0xcc, 0x39, 0x6c, 0xd7, 0x62, 0x9d, 0x6f, 0x6f, 0xa0, 0x89, 0xdf, 0x31, 0xca, 0x4f, 0x57, 0xb7, 0x88, 0x35, 0x81, 0x2d, 0x36, 0xa1, 0xe7, 0x20, 0x7d, 0xf1, 0x59, 0x86, 0x33, 0x02, 0xff, 0xa7, 0x07, 0x37, 0x2e, 0xf7, 0xe8, 0x5d, 0x58, 0x2f, 0xba, 0xa7, 0x49, 0xec, 0xd4, 0x41, 0x51, 0xea, 0xc7, 0xf4, 0xe3, 0xa5, 0x0c, 0x78, 0x76, 0xf9, 0x27, 0xcb, 0x2d, 0x7f, 0x95, 0xf5, 0xf4, 0x19, 0xac, 0x5a, 0xac, 0x76, 0xd9, 0xba, 0xbf, 0x98, 0xd7, 0xba, 0x16, 0x3a, 0xd8, 0xff, 0x65, 0xe7, 0x1e, 0xac, 0x58, 0xb6, 0xb9, 0xb7, 0x6d, 0xf7, 0xb7, 0x07, 0x9b, 0x76, 0xec, 0xe5, 0x4c, 0xc8, 0xf3, 0x77, 0x7d, 0xfa, 0x83, 0xc0, 0x46, 0x29, 0xe8, 0x74, 0x6f, 0xe9, 0x9b, 0x61, 0xc3, 0xd1, 0x7e, 0x74, 0xcd, 0x1b, 0x45, 0x7f, 0x11, 0xd8, 0xac, 0x09, 0x0e, 0xfd, 0x07, 0x7b, 0xae, 0xce, 0x6a, 0xfb, 0xe9, 0x35, 0xd1, 0x53, 0x51, 0x0f, 0xc8, 0x8b, 0xc3, 0x4f, 0xfd, 0xb3, 0xc4, 0x0c, 0xd3, 0x01, 0x8b, 0xe4, 0x28, 0x28, 0xbd, 0x99, 0xec, 0x0c, 0x45, 0x60, 0x1f, 0xc7, 0xba, 0xe7, 0xf3, 0xa0, 0x5a, 0xcb, 0xba, 0x83, 0x55, 0x3b, 0xfd, 0xf0, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfd, 0x73, 0x0d, 0x95, 0x7c, 0x05, 0x00, 0x00, }, } func init() { yarpc.RegisterClientBuilder( func(clientConfig transport.ClientConfig, structField reflect.StructField) ShardDistributorAPIYARPCClient { return NewShardDistributorAPIYARPCClient(clientConfig, protobuf.ClientBuilderOptions(clientConfig, structField)...) }, ) } ================================================ FILE: .gen/proto/shared/v1/any.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/shared/v1/any.proto package sharedv1 import ( fmt "fmt" io "io" math "math" math_bits "math/bits" proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // Any is a logical duplicate of google.protobuf.Any, but is intentionally // breaking compatibility because it will not be directly used as a // google.protobuf.Any in our high-level RPC mappers. // // The intent of the type is the same, but it is not intended to be directly // compatible with google.protobuf.Any - this blob is RPC-type agnostic by // design (as the underlying data may be transported over proto or thrift), and // the data-bytes may or may not be proto-encoded. // // This is intentionally different from DataBlob, which supports only a handful // of known encodings so it can be interpreted everywhere. Any supports literally // any contents, and needs to be considered opaque until it is given to something // that is expecting it. // // See value_type to interpret the contents. type Any struct { // Type-string describing value's contents, and intentionally avoiding the // name "type" as it is often a special term. // This should usually be a hard-coded string of some kind. ValueType string `protobuf:"bytes,1,opt,name=value_type,json=valueType,proto3" json:"value_type,omitempty"` // Arbitrarily-encoded bytes, to be deserialized by a runtime implementation. // The contents are described by value_type. Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *Any) Reset() { *m = Any{} } func (m *Any) String() string { return proto.CompactTextString(m) } func (*Any) ProtoMessage() {} func (*Any) Descriptor() ([]byte, []int) { return fileDescriptor_3ed449e8097bca22, []int{0} } func (m *Any) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *Any) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_Any.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *Any) XXX_Merge(src proto.Message) { xxx_messageInfo_Any.Merge(m, src) } func (m *Any) XXX_Size() int { return m.Size() } func (m *Any) XXX_DiscardUnknown() { xxx_messageInfo_Any.DiscardUnknown(m) } var xxx_messageInfo_Any proto.InternalMessageInfo func (m *Any) GetValueType() string { if m != nil { return m.ValueType } return "" } func (m *Any) GetValue() []byte { if m != nil { return m.Value } return nil } func init() { proto.RegisterType((*Any)(nil), "uber.cadence.shared.v1.Any") } func init() { proto.RegisterFile("uber/cadence/shared/v1/any.proto", fileDescriptor_3ed449e8097bca22) } var fileDescriptor_3ed449e8097bca22 = []byte{ // 170 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x28, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x2f, 0xce, 0x48, 0x2c, 0x4a, 0x4d, 0xd1, 0x2f, 0x33, 0xd4, 0x4f, 0xcc, 0xab, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x03, 0xa9, 0xd0, 0x83, 0xaa, 0xd0, 0x83, 0xa8, 0xd0, 0x2b, 0x33, 0x54, 0xb2, 0xe2, 0x62, 0x76, 0xcc, 0xab, 0x14, 0x92, 0xe5, 0xe2, 0x2a, 0x4b, 0xcc, 0x29, 0x4d, 0x8d, 0x2f, 0xa9, 0x2c, 0x48, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0xe2, 0x04, 0x8b, 0x84, 0x54, 0x16, 0xa4, 0x0a, 0x89, 0x70, 0xb1, 0x82, 0x39, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x10, 0x8e, 0x93, 0xf3, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x18, 0x65, 0x9a, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x8f, 0xe2, 0x1c, 0xbd, 0xf4, 0xd4, 0x3c, 0x7d, 0xb0, 0x2b, 0x10, 0x2e, 0xb3, 0x86, 0xb0, 0xca, 0x0c, 0x93, 0xd8, 0xc0, 0x32, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x29, 0x98, 0x69, 0x21, 0xc3, 0x00, 0x00, 0x00, } func (m *Any) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Any) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *Any) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Value) > 0 { i -= len(m.Value) copy(dAtA[i:], m.Value) i = encodeVarintAny(dAtA, i, uint64(len(m.Value))) i-- dAtA[i] = 0x12 } if len(m.ValueType) > 0 { i -= len(m.ValueType) copy(dAtA[i:], m.ValueType) i = encodeVarintAny(dAtA, i, uint64(len(m.ValueType))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func encodeVarintAny(dAtA []byte, offset int, v uint64) int { offset -= sovAny(v) base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) return base } func (m *Any) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ValueType) if l > 0 { n += 1 + l + sovAny(uint64(l)) } l = len(m.Value) if l > 0 { n += 1 + l + sovAny(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func sovAny(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozAny(x uint64) (n int) { return sovAny(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *Any) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowAny } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Any: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Any: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ValueType", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowAny } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthAny } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthAny } if postIndex > l { return io.ErrUnexpectedEOF } m.ValueType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowAny } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthAny } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthAny } if postIndex > l { return io.ErrUnexpectedEOF } m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) if m.Value == nil { m.Value = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipAny(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAny } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func skipAny(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowAny } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } wireType := int(wire & 0x7) switch wireType { case 0: for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowAny } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } iNdEx++ if dAtA[iNdEx-1] < 0x80 { break } } case 1: iNdEx += 8 case 2: var length int for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowAny } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { break } } if length < 0 { return 0, ErrInvalidLengthAny } iNdEx += length case 3: depth++ case 4: if depth == 0 { return 0, ErrUnexpectedEndOfGroupAny } depth-- case 5: iNdEx += 4 default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } if iNdEx < 0 { return 0, ErrInvalidLengthAny } if depth == 0 { return iNdEx, nil } } return 0, io.ErrUnexpectedEOF } var ( ErrInvalidLengthAny = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowAny = fmt.Errorf("proto: integer overflow") ErrUnexpectedEndOfGroupAny = fmt.Errorf("proto: unexpected end of group") ) ================================================ FILE: .gen/proto/shared/v1/any.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/shared/v1/any.proto package sharedv1 var yarpcFileDescriptorClosure3ed449e8097bca22 = [][]byte{ // uber/cadence/shared/v1/any.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x28, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x2f, 0xce, 0x48, 0x2c, 0x4a, 0x4d, 0xd1, 0x2f, 0x33, 0xd4, 0x4f, 0xcc, 0xab, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x03, 0xa9, 0xd0, 0x83, 0xaa, 0xd0, 0x83, 0xa8, 0xd0, 0x2b, 0x33, 0x54, 0xb2, 0xe2, 0x62, 0x76, 0xcc, 0xab, 0x14, 0x92, 0xe5, 0xe2, 0x2a, 0x4b, 0xcc, 0x29, 0x4d, 0x8d, 0x2f, 0xa9, 0x2c, 0x48, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0xe2, 0x04, 0x8b, 0x84, 0x54, 0x16, 0xa4, 0x0a, 0x89, 0x70, 0xb1, 0x82, 0x39, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x10, 0x8e, 0x93, 0x79, 0x94, 0x69, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0x8a, 0x13, 0xf4, 0xd2, 0x53, 0xf3, 0xf4, 0xc1, 0x36, 0x23, 0x5c, 0x63, 0x0d, 0x61, 0x95, 0x19, 0x26, 0xb1, 0x81, 0x65, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x4e, 0x0a, 0xa2, 0x2e, 0xb7, 0x00, 0x00, 0x00, }, } ================================================ FILE: .gen/proto/shared/v1/error.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/shared/v1/error.proto package sharedv1 import ( fmt "fmt" io "io" math "math" math_bits "math/bits" proto "github.com/gogo/protobuf/proto" v11 "github.com/uber/cadence-idl/go/proto/admin/v1" v1 "github.com/uber/cadence-idl/go/proto/api/v1" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type CurrentBranchChangedError struct { CurrentBranchToken []byte `protobuf:"bytes,1,opt,name=current_branch_token,json=currentBranchToken,proto3" json:"current_branch_token,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *CurrentBranchChangedError) Reset() { *m = CurrentBranchChangedError{} } func (m *CurrentBranchChangedError) String() string { return proto.CompactTextString(m) } func (*CurrentBranchChangedError) ProtoMessage() {} func (*CurrentBranchChangedError) Descriptor() ([]byte, []int) { return fileDescriptor_3688ca0fd170c8f9, []int{0} } func (m *CurrentBranchChangedError) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *CurrentBranchChangedError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_CurrentBranchChangedError.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *CurrentBranchChangedError) XXX_Merge(src proto.Message) { xxx_messageInfo_CurrentBranchChangedError.Merge(m, src) } func (m *CurrentBranchChangedError) XXX_Size() int { return m.Size() } func (m *CurrentBranchChangedError) XXX_DiscardUnknown() { xxx_messageInfo_CurrentBranchChangedError.DiscardUnknown(m) } var xxx_messageInfo_CurrentBranchChangedError proto.InternalMessageInfo func (m *CurrentBranchChangedError) GetCurrentBranchToken() []byte { if m != nil { return m.CurrentBranchToken } return nil } type InternalDataInconsistencyError struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *InternalDataInconsistencyError) Reset() { *m = InternalDataInconsistencyError{} } func (m *InternalDataInconsistencyError) String() string { return proto.CompactTextString(m) } func (*InternalDataInconsistencyError) ProtoMessage() {} func (*InternalDataInconsistencyError) Descriptor() ([]byte, []int) { return fileDescriptor_3688ca0fd170c8f9, []int{1} } func (m *InternalDataInconsistencyError) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *InternalDataInconsistencyError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_InternalDataInconsistencyError.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *InternalDataInconsistencyError) XXX_Merge(src proto.Message) { xxx_messageInfo_InternalDataInconsistencyError.Merge(m, src) } func (m *InternalDataInconsistencyError) XXX_Size() int { return m.Size() } func (m *InternalDataInconsistencyError) XXX_DiscardUnknown() { xxx_messageInfo_InternalDataInconsistencyError.DiscardUnknown(m) } var xxx_messageInfo_InternalDataInconsistencyError proto.InternalMessageInfo type EventAlreadyStartedError struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *EventAlreadyStartedError) Reset() { *m = EventAlreadyStartedError{} } func (m *EventAlreadyStartedError) String() string { return proto.CompactTextString(m) } func (*EventAlreadyStartedError) ProtoMessage() {} func (*EventAlreadyStartedError) Descriptor() ([]byte, []int) { return fileDescriptor_3688ca0fd170c8f9, []int{2} } func (m *EventAlreadyStartedError) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *EventAlreadyStartedError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_EventAlreadyStartedError.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *EventAlreadyStartedError) XXX_Merge(src proto.Message) { xxx_messageInfo_EventAlreadyStartedError.Merge(m, src) } func (m *EventAlreadyStartedError) XXX_Size() int { return m.Size() } func (m *EventAlreadyStartedError) XXX_DiscardUnknown() { xxx_messageInfo_EventAlreadyStartedError.DiscardUnknown(m) } var xxx_messageInfo_EventAlreadyStartedError proto.InternalMessageInfo type RemoteSyncMatchedError struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RemoteSyncMatchedError) Reset() { *m = RemoteSyncMatchedError{} } func (m *RemoteSyncMatchedError) String() string { return proto.CompactTextString(m) } func (*RemoteSyncMatchedError) ProtoMessage() {} func (*RemoteSyncMatchedError) Descriptor() ([]byte, []int) { return fileDescriptor_3688ca0fd170c8f9, []int{3} } func (m *RemoteSyncMatchedError) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RemoteSyncMatchedError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RemoteSyncMatchedError.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RemoteSyncMatchedError) XXX_Merge(src proto.Message) { xxx_messageInfo_RemoteSyncMatchedError.Merge(m, src) } func (m *RemoteSyncMatchedError) XXX_Size() int { return m.Size() } func (m *RemoteSyncMatchedError) XXX_DiscardUnknown() { xxx_messageInfo_RemoteSyncMatchedError.DiscardUnknown(m) } var xxx_messageInfo_RemoteSyncMatchedError proto.InternalMessageInfo type RetryTaskV2Error struct { DomainId string `protobuf:"bytes,1,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` WorkflowExecution *v1.WorkflowExecution `protobuf:"bytes,2,opt,name=workflow_execution,json=workflowExecution,proto3" json:"workflow_execution,omitempty"` StartEvent *v11.VersionHistoryItem `protobuf:"bytes,3,opt,name=start_event,json=startEvent,proto3" json:"start_event,omitempty"` EndEvent *v11.VersionHistoryItem `protobuf:"bytes,4,opt,name=end_event,json=endEvent,proto3" json:"end_event,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *RetryTaskV2Error) Reset() { *m = RetryTaskV2Error{} } func (m *RetryTaskV2Error) String() string { return proto.CompactTextString(m) } func (*RetryTaskV2Error) ProtoMessage() {} func (*RetryTaskV2Error) Descriptor() ([]byte, []int) { return fileDescriptor_3688ca0fd170c8f9, []int{4} } func (m *RetryTaskV2Error) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *RetryTaskV2Error) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_RetryTaskV2Error.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *RetryTaskV2Error) XXX_Merge(src proto.Message) { xxx_messageInfo_RetryTaskV2Error.Merge(m, src) } func (m *RetryTaskV2Error) XXX_Size() int { return m.Size() } func (m *RetryTaskV2Error) XXX_DiscardUnknown() { xxx_messageInfo_RetryTaskV2Error.DiscardUnknown(m) } var xxx_messageInfo_RetryTaskV2Error proto.InternalMessageInfo func (m *RetryTaskV2Error) GetDomainId() string { if m != nil { return m.DomainId } return "" } func (m *RetryTaskV2Error) GetWorkflowExecution() *v1.WorkflowExecution { if m != nil { return m.WorkflowExecution } return nil } func (m *RetryTaskV2Error) GetStartEvent() *v11.VersionHistoryItem { if m != nil { return m.StartEvent } return nil } func (m *RetryTaskV2Error) GetEndEvent() *v11.VersionHistoryItem { if m != nil { return m.EndEvent } return nil } type ShardOwnershipLostError struct { Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ShardOwnershipLostError) Reset() { *m = ShardOwnershipLostError{} } func (m *ShardOwnershipLostError) String() string { return proto.CompactTextString(m) } func (*ShardOwnershipLostError) ProtoMessage() {} func (*ShardOwnershipLostError) Descriptor() ([]byte, []int) { return fileDescriptor_3688ca0fd170c8f9, []int{5} } func (m *ShardOwnershipLostError) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *ShardOwnershipLostError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_ShardOwnershipLostError.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *ShardOwnershipLostError) XXX_Merge(src proto.Message) { xxx_messageInfo_ShardOwnershipLostError.Merge(m, src) } func (m *ShardOwnershipLostError) XXX_Size() int { return m.Size() } func (m *ShardOwnershipLostError) XXX_DiscardUnknown() { xxx_messageInfo_ShardOwnershipLostError.DiscardUnknown(m) } var xxx_messageInfo_ShardOwnershipLostError proto.InternalMessageInfo func (m *ShardOwnershipLostError) GetOwner() string { if m != nil { return m.Owner } return "" } type TaskListNotOwnedByHostError struct { OwnedByIdentity string `protobuf:"bytes,1,opt,name=owned_by_identity,json=ownedByIdentity,proto3" json:"owned_by_identity,omitempty"` MyIdentity string `protobuf:"bytes,2,opt,name=my_identity,json=myIdentity,proto3" json:"my_identity,omitempty"` TaskListName string `protobuf:"bytes,3,opt,name=task_list_name,json=taskListName,proto3" json:"task_list_name,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *TaskListNotOwnedByHostError) Reset() { *m = TaskListNotOwnedByHostError{} } func (m *TaskListNotOwnedByHostError) String() string { return proto.CompactTextString(m) } func (*TaskListNotOwnedByHostError) ProtoMessage() {} func (*TaskListNotOwnedByHostError) Descriptor() ([]byte, []int) { return fileDescriptor_3688ca0fd170c8f9, []int{6} } func (m *TaskListNotOwnedByHostError) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *TaskListNotOwnedByHostError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_TaskListNotOwnedByHostError.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *TaskListNotOwnedByHostError) XXX_Merge(src proto.Message) { xxx_messageInfo_TaskListNotOwnedByHostError.Merge(m, src) } func (m *TaskListNotOwnedByHostError) XXX_Size() int { return m.Size() } func (m *TaskListNotOwnedByHostError) XXX_DiscardUnknown() { xxx_messageInfo_TaskListNotOwnedByHostError.DiscardUnknown(m) } var xxx_messageInfo_TaskListNotOwnedByHostError proto.InternalMessageInfo func (m *TaskListNotOwnedByHostError) GetOwnedByIdentity() string { if m != nil { return m.OwnedByIdentity } return "" } func (m *TaskListNotOwnedByHostError) GetMyIdentity() string { if m != nil { return m.MyIdentity } return "" } func (m *TaskListNotOwnedByHostError) GetTaskListName() string { if m != nil { return m.TaskListName } return "" } func init() { proto.RegisterType((*CurrentBranchChangedError)(nil), "uber.cadence.shared.v1.CurrentBranchChangedError") proto.RegisterType((*InternalDataInconsistencyError)(nil), "uber.cadence.shared.v1.InternalDataInconsistencyError") proto.RegisterType((*EventAlreadyStartedError)(nil), "uber.cadence.shared.v1.EventAlreadyStartedError") proto.RegisterType((*RemoteSyncMatchedError)(nil), "uber.cadence.shared.v1.RemoteSyncMatchedError") proto.RegisterType((*RetryTaskV2Error)(nil), "uber.cadence.shared.v1.RetryTaskV2Error") proto.RegisterType((*ShardOwnershipLostError)(nil), "uber.cadence.shared.v1.ShardOwnershipLostError") proto.RegisterType((*TaskListNotOwnedByHostError)(nil), "uber.cadence.shared.v1.TaskListNotOwnedByHostError") } func init() { proto.RegisterFile("uber/cadence/shared/v1/error.proto", fileDescriptor_3688ca0fd170c8f9) } var fileDescriptor_3688ca0fd170c8f9 = []byte{ // 514 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0xcd, 0x6e, 0xd3, 0x40, 0x14, 0x85, 0xe5, 0xf2, 0xa3, 0x66, 0x5a, 0x01, 0xb5, 0xaa, 0x12, 0x5a, 0x29, 0x44, 0x06, 0xa1, 0xc2, 0xc2, 0x26, 0x45, 0xac, 0x58, 0x91, 0x10, 0xd4, 0xa0, 0x16, 0x24, 0xa7, 0x14, 0x89, 0x8d, 0x35, 0x99, 0xb9, 0xc4, 0xa3, 0xc4, 0x77, 0xa2, 0x99, 0x9b, 0x04, 0xbf, 0x05, 0x6b, 0x9e, 0x88, 0x25, 0x8f, 0x80, 0xf2, 0x24, 0x68, 0x3c, 0x0e, 0x6d, 0xba, 0x63, 0x67, 0xdf, 0xfb, 0x9d, 0xe3, 0xe3, 0xa3, 0x19, 0x16, 0xcd, 0x47, 0x60, 0x12, 0xc1, 0x25, 0xa0, 0x80, 0xc4, 0xe6, 0xdc, 0x80, 0x4c, 0x16, 0x9d, 0x04, 0x8c, 0xd1, 0x26, 0x9e, 0x19, 0x4d, 0x3a, 0x3c, 0x70, 0x4c, 0x5c, 0x33, 0xb1, 0x67, 0xe2, 0x45, 0xe7, 0xb0, 0xbd, 0xa1, 0xe5, 0x33, 0xe5, 0x84, 0x42, 0x17, 0x85, 0x46, 0xaf, 0x3c, 0x7c, 0xb2, 0x49, 0xc8, 0x42, 0xa1, 0x63, 0x72, 0x65, 0x49, 0x9b, 0xd2, 0x43, 0xd1, 0x39, 0x7b, 0xd4, 0x9b, 0x1b, 0x03, 0x48, 0x5d, 0xc3, 0x51, 0xe4, 0xbd, 0x9c, 0xe3, 0x18, 0x64, 0xdf, 0x25, 0x08, 0x5f, 0xb2, 0x7d, 0xe1, 0x97, 0xd9, 0xa8, 0xda, 0x66, 0xa4, 0x27, 0x80, 0xcd, 0xa0, 0x1d, 0x1c, 0xef, 0xa6, 0xa1, 0xb8, 0x2e, 0xbc, 0x70, 0x9b, 0xa8, 0xcd, 0x5a, 0x03, 0x24, 0x30, 0xc8, 0xa7, 0xef, 0x38, 0xf1, 0x01, 0x0a, 0x8d, 0x56, 0x59, 0x02, 0x14, 0x65, 0xe5, 0x19, 0x1d, 0xb2, 0x66, 0x7f, 0x01, 0x48, 0x6f, 0xa7, 0x06, 0xb8, 0x2c, 0x87, 0xc4, 0x0d, 0xd5, 0xdf, 0x8b, 0x9a, 0xec, 0x20, 0x85, 0x42, 0x13, 0x0c, 0x4b, 0x14, 0xe7, 0x9c, 0x44, 0xbe, 0xde, 0xfc, 0xdc, 0x62, 0x0f, 0x52, 0x20, 0x53, 0x5e, 0x70, 0x3b, 0xb9, 0x3c, 0xf1, 0xf1, 0x8e, 0x58, 0x43, 0xea, 0x82, 0x2b, 0xcc, 0x94, 0xac, 0x32, 0x35, 0xd2, 0x6d, 0x3f, 0x18, 0xc8, 0xf0, 0x33, 0x0b, 0x97, 0xda, 0x4c, 0xbe, 0x4d, 0xf5, 0x32, 0x83, 0xef, 0x20, 0xe6, 0xa4, 0x34, 0x36, 0xb7, 0xda, 0xc1, 0xf1, 0xce, 0xc9, 0xb3, 0x78, 0xa3, 0x54, 0x3e, 0x53, 0xf1, 0xa2, 0x13, 0x7f, 0xa9, 0xf1, 0xfe, 0x9a, 0x4e, 0xf7, 0x96, 0x37, 0x47, 0xe1, 0x07, 0xb6, 0x63, 0x5d, 0xe4, 0x0c, 0xdc, 0x4f, 0x34, 0x6f, 0x55, 0x7e, 0xcf, 0x6f, 0xf8, 0xb9, 0xaa, 0x9d, 0xe3, 0x25, 0x18, 0xab, 0x34, 0x9e, 0xfa, 0xc6, 0x07, 0x04, 0x45, 0xca, 0x2a, 0x75, 0xd5, 0x40, 0xf8, 0x9e, 0x35, 0x00, 0x65, 0xed, 0x74, 0xfb, 0x7f, 0x9d, 0xb6, 0x01, 0x65, 0xe5, 0x13, 0x25, 0xec, 0xe1, 0x30, 0xe7, 0x46, 0x7e, 0x5a, 0x22, 0x18, 0x9b, 0xab, 0xd9, 0x99, 0xb6, 0xe4, 0x2b, 0xda, 0x67, 0x77, 0xb4, 0x9b, 0xd6, 0xf5, 0xf8, 0x97, 0xe8, 0x47, 0xc0, 0x8e, 0x5c, 0x91, 0x67, 0xca, 0xd2, 0x47, 0x4d, 0x4e, 0x27, 0xbb, 0xe5, 0xe9, 0x3f, 0xd5, 0x0b, 0xb6, 0xe7, 0x40, 0x99, 0x8d, 0xca, 0x4c, 0x49, 0x40, 0x52, 0x54, 0xd6, 0x0e, 0xf7, 0xb5, 0x87, 0x07, 0xf5, 0x38, 0x7c, 0xcc, 0x76, 0x8a, 0x6b, 0xd4, 0x56, 0x45, 0xb1, 0xe2, 0x0a, 0x78, 0xca, 0xee, 0x11, 0xb7, 0x93, 0x6c, 0xaa, 0x2c, 0x65, 0xc8, 0x0b, 0xa8, 0x4a, 0x6b, 0xa4, 0xbb, 0xb4, 0x4e, 0xc0, 0x0b, 0xe8, 0xf6, 0x7e, 0xad, 0x5a, 0xc1, 0xef, 0x55, 0x2b, 0xf8, 0xb3, 0x6a, 0x05, 0x5f, 0x5f, 0x8f, 0x15, 0xe5, 0xf3, 0x51, 0x2c, 0x74, 0x91, 0x6c, 0x9c, 0xe2, 0x78, 0x0c, 0x98, 0x54, 0x27, 0xf7, 0xea, 0xba, 0xbc, 0xf1, 0x4f, 0x8b, 0xce, 0xe8, 0x6e, 0xb5, 0x79, 0xf5, 0x37, 0x00, 0x00, 0xff, 0xff, 0x3d, 0x40, 0x81, 0x71, 0x58, 0x03, 0x00, 0x00, } func (m *CurrentBranchChangedError) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CurrentBranchChangedError) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *CurrentBranchChangedError) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.CurrentBranchToken) > 0 { i -= len(m.CurrentBranchToken) copy(dAtA[i:], m.CurrentBranchToken) i = encodeVarintError(dAtA, i, uint64(len(m.CurrentBranchToken))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *InternalDataInconsistencyError) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *InternalDataInconsistencyError) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *InternalDataInconsistencyError) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *EventAlreadyStartedError) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EventAlreadyStartedError) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *EventAlreadyStartedError) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RemoteSyncMatchedError) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RemoteSyncMatchedError) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RemoteSyncMatchedError) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } return len(dAtA) - i, nil } func (m *RetryTaskV2Error) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RetryTaskV2Error) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *RetryTaskV2Error) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.EndEvent != nil { { size, err := m.EndEvent.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintError(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x22 } if m.StartEvent != nil { { size, err := m.StartEvent.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintError(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x1a } if m.WorkflowExecution != nil { { size, err := m.WorkflowExecution.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintError(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if len(m.DomainId) > 0 { i -= len(m.DomainId) copy(dAtA[i:], m.DomainId) i = encodeVarintError(dAtA, i, uint64(len(m.DomainId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ShardOwnershipLostError) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ShardOwnershipLostError) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *ShardOwnershipLostError) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Owner) > 0 { i -= len(m.Owner) copy(dAtA[i:], m.Owner) i = encodeVarintError(dAtA, i, uint64(len(m.Owner))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *TaskListNotOwnedByHostError) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TaskListNotOwnedByHostError) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *TaskListNotOwnedByHostError) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.TaskListName) > 0 { i -= len(m.TaskListName) copy(dAtA[i:], m.TaskListName) i = encodeVarintError(dAtA, i, uint64(len(m.TaskListName))) i-- dAtA[i] = 0x1a } if len(m.MyIdentity) > 0 { i -= len(m.MyIdentity) copy(dAtA[i:], m.MyIdentity) i = encodeVarintError(dAtA, i, uint64(len(m.MyIdentity))) i-- dAtA[i] = 0x12 } if len(m.OwnedByIdentity) > 0 { i -= len(m.OwnedByIdentity) copy(dAtA[i:], m.OwnedByIdentity) i = encodeVarintError(dAtA, i, uint64(len(m.OwnedByIdentity))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func encodeVarintError(dAtA []byte, offset int, v uint64) int { offset -= sovError(v) base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) return base } func (m *CurrentBranchChangedError) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.CurrentBranchToken) if l > 0 { n += 1 + l + sovError(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *InternalDataInconsistencyError) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *EventAlreadyStartedError) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RemoteSyncMatchedError) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *RetryTaskV2Error) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DomainId) if l > 0 { n += 1 + l + sovError(uint64(l)) } if m.WorkflowExecution != nil { l = m.WorkflowExecution.Size() n += 1 + l + sovError(uint64(l)) } if m.StartEvent != nil { l = m.StartEvent.Size() n += 1 + l + sovError(uint64(l)) } if m.EndEvent != nil { l = m.EndEvent.Size() n += 1 + l + sovError(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *ShardOwnershipLostError) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Owner) if l > 0 { n += 1 + l + sovError(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *TaskListNotOwnedByHostError) Size() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.OwnedByIdentity) if l > 0 { n += 1 + l + sovError(uint64(l)) } l = len(m.MyIdentity) if l > 0 { n += 1 + l + sovError(uint64(l)) } l = len(m.TaskListName) if l > 0 { n += 1 + l + sovError(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func sovError(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozError(x uint64) (n int) { return sovError(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *CurrentBranchChangedError) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CurrentBranchChangedError: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CurrentBranchChangedError: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CurrentBranchToken", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return ErrInvalidLengthError } postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthError } if postIndex > l { return io.ErrUnexpectedEOF } m.CurrentBranchToken = append(m.CurrentBranchToken[:0], dAtA[iNdEx:postIndex]...) if m.CurrentBranchToken == nil { m.CurrentBranchToken = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipError(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthError } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *InternalDataInconsistencyError) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: InternalDataInconsistencyError: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: InternalDataInconsistencyError: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipError(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthError } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EventAlreadyStartedError) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EventAlreadyStartedError: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EventAlreadyStartedError: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipError(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthError } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RemoteSyncMatchedError) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RemoteSyncMatchedError: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RemoteSyncMatchedError: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := skipError(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthError } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RetryTaskV2Error) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RetryTaskV2Error: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RetryTaskV2Error: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DomainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthError } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthError } if postIndex > l { return io.ErrUnexpectedEOF } m.DomainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkflowExecution", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthError } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthError } if postIndex > l { return io.ErrUnexpectedEOF } if m.WorkflowExecution == nil { m.WorkflowExecution = &v1.WorkflowExecution{} } if err := m.WorkflowExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StartEvent", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthError } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthError } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartEvent == nil { m.StartEvent = &v11.VersionHistoryItem{} } if err := m.StartEvent.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EndEvent", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthError } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthError } if postIndex > l { return io.ErrUnexpectedEOF } if m.EndEvent == nil { m.EndEvent = &v11.VersionHistoryItem{} } if err := m.EndEvent.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipError(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthError } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ShardOwnershipLostError) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ShardOwnershipLostError: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ShardOwnershipLostError: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthError } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthError } if postIndex > l { return io.ErrUnexpectedEOF } m.Owner = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipError(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthError } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TaskListNotOwnedByHostError) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TaskListNotOwnedByHostError: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TaskListNotOwnedByHostError: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OwnedByIdentity", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthError } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthError } if postIndex > l { return io.ErrUnexpectedEOF } m.OwnedByIdentity = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MyIdentity", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthError } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthError } if postIndex > l { return io.ErrUnexpectedEOF } m.MyIdentity = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TaskListName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowError } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return ErrInvalidLengthError } postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthError } if postIndex > l { return io.ErrUnexpectedEOF } m.TaskListName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipError(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthError } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func skipError(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowError } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } wireType := int(wire & 0x7) switch wireType { case 0: for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowError } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } iNdEx++ if dAtA[iNdEx-1] < 0x80 { break } } case 1: iNdEx += 8 case 2: var length int for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowError } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { break } } if length < 0 { return 0, ErrInvalidLengthError } iNdEx += length case 3: depth++ case 4: if depth == 0 { return 0, ErrUnexpectedEndOfGroupError } depth-- case 5: iNdEx += 4 default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } if iNdEx < 0 { return 0, ErrInvalidLengthError } if depth == 0 { return iNdEx, nil } } return 0, io.ErrUnexpectedEOF } var ( ErrInvalidLengthError = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowError = fmt.Errorf("proto: integer overflow") ErrUnexpectedEndOfGroupError = fmt.Errorf("proto: unexpected end of group") ) ================================================ FILE: .gen/proto/shared/v1/error.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/shared/v1/error.proto package sharedv1 var yarpcFileDescriptorClosure3688ca0fd170c8f9 = [][]byte{ // uber/cadence/shared/v1/error.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x5f, 0x6f, 0xd3, 0x30, 0x14, 0xc5, 0xd5, 0xf2, 0x47, 0xab, 0x3b, 0x01, 0x8b, 0xa6, 0x51, 0x3a, 0x09, 0xaa, 0x80, 0xd0, 0xe0, 0x21, 0xa1, 0x43, 0x88, 0x07, 0x9e, 0xe8, 0x28, 0x5a, 0xd1, 0x06, 0x52, 0x3a, 0x86, 0xc4, 0x4b, 0xe4, 0xda, 0x97, 0xc6, 0x6a, 0x73, 0x5d, 0xd9, 0xb7, 0x2d, 0xf9, 0x16, 0x3c, 0xf3, 0x69, 0x91, 0xed, 0x94, 0xad, 0x7b, 0xdb, 0x5b, 0x72, 0xef, 0xef, 0x9c, 0x9c, 0x1c, 0xd9, 0x2c, 0x5e, 0x4e, 0xc0, 0xa4, 0x82, 0x4b, 0x40, 0x01, 0xa9, 0x2d, 0xb8, 0x01, 0x99, 0xae, 0xfa, 0x29, 0x18, 0xa3, 0x4d, 0xb2, 0x30, 0x9a, 0x74, 0x74, 0xe0, 0x98, 0xa4, 0x66, 0x92, 0xc0, 0x24, 0xab, 0x7e, 0xb7, 0xb7, 0xa5, 0xe5, 0x0b, 0xe5, 0x84, 0x42, 0x97, 0xa5, 0xc6, 0xa0, 0xec, 0x3e, 0xdf, 0x26, 0x64, 0xa9, 0xd0, 0x31, 0x85, 0xb2, 0xa4, 0x4d, 0x15, 0xa0, 0xf8, 0x9c, 0x3d, 0x39, 0x59, 0x1a, 0x03, 0x48, 0x03, 0xc3, 0x51, 0x14, 0x27, 0x05, 0xc7, 0x29, 0xc8, 0xa1, 0x4b, 0x10, 0xbd, 0x61, 0xfb, 0x22, 0x2c, 0xf3, 0x89, 0xdf, 0xe6, 0xa4, 0x67, 0x80, 0x9d, 0x46, 0xaf, 0x71, 0xb4, 0x9b, 0x45, 0xe2, 0xba, 0xf0, 0xc2, 0x6d, 0xe2, 0x1e, 0x7b, 0x3a, 0x42, 0x02, 0x83, 0x7c, 0xfe, 0x89, 0x13, 0x1f, 0xa1, 0xd0, 0x68, 0x95, 0x25, 0x40, 0x51, 0x79, 0xcf, 0xb8, 0xcb, 0x3a, 0xc3, 0x15, 0x20, 0x7d, 0x9c, 0x1b, 0xe0, 0xb2, 0x1a, 0x13, 0x37, 0x54, 0x7f, 0x2f, 0xee, 0xb0, 0x83, 0x0c, 0x4a, 0x4d, 0x30, 0xae, 0x50, 0x9c, 0x73, 0x12, 0xc5, 0x66, 0xf3, 0xb7, 0xc9, 0x1e, 0x65, 0x40, 0xa6, 0xba, 0xe0, 0x76, 0x76, 0x79, 0x1c, 0xe2, 0x1d, 0xb2, 0x96, 0xd4, 0x25, 0x57, 0x98, 0x2b, 0xe9, 0x33, 0xb5, 0xb2, 0x9d, 0x30, 0x18, 0xc9, 0xe8, 0x3b, 0x8b, 0xd6, 0xda, 0xcc, 0x7e, 0xcd, 0xf5, 0x3a, 0x87, 0xdf, 0x20, 0x96, 0xa4, 0x34, 0x76, 0x9a, 0xbd, 0xc6, 0x51, 0xfb, 0xf8, 0x65, 0xb2, 0x55, 0x2a, 0x5f, 0xa8, 0x64, 0xd5, 0x4f, 0x7e, 0xd4, 0xf8, 0x70, 0x43, 0x67, 0x7b, 0xeb, 0x9b, 0xa3, 0xe8, 0x0b, 0x6b, 0x5b, 0x17, 0x39, 0x07, 0xf7, 0x13, 0x9d, 0x3b, 0xde, 0xef, 0xd5, 0x0d, 0x3f, 0x57, 0xb5, 0x73, 0xbc, 0x04, 0x63, 0x95, 0xc6, 0xd3, 0xd0, 0xf8, 0x88, 0xa0, 0xcc, 0x98, 0x57, 0xfb, 0x06, 0xa2, 0xcf, 0xac, 0x05, 0x28, 0x6b, 0xa7, 0xbb, 0xb7, 0x75, 0xda, 0x01, 0x94, 0xde, 0x27, 0x4e, 0xd9, 0xe3, 0x71, 0xc1, 0x8d, 0xfc, 0xb6, 0x46, 0x30, 0xb6, 0x50, 0x8b, 0x33, 0x6d, 0x29, 0x54, 0xb4, 0xcf, 0xee, 0x69, 0x37, 0xad, 0xeb, 0x09, 0x2f, 0xf1, 0x9f, 0x06, 0x3b, 0x74, 0x45, 0x9e, 0x29, 0x4b, 0x5f, 0x35, 0x39, 0x9d, 0x1c, 0x54, 0xa7, 0xff, 0x55, 0xaf, 0xd9, 0x9e, 0x03, 0x65, 0x3e, 0xa9, 0x72, 0x25, 0x01, 0x49, 0x51, 0x55, 0x3b, 0x3c, 0xd4, 0x01, 0x1e, 0xd5, 0xe3, 0xe8, 0x19, 0x6b, 0x97, 0xd7, 0xa8, 0xa6, 0xa7, 0x58, 0x79, 0x05, 0xbc, 0x60, 0x0f, 0x88, 0xdb, 0x59, 0x3e, 0x57, 0x96, 0x72, 0xe4, 0x25, 0xf8, 0xd2, 0x5a, 0xd9, 0x2e, 0x6d, 0x12, 0xf0, 0x12, 0x06, 0xef, 0x7f, 0xbe, 0x9b, 0x2a, 0x2a, 0x96, 0x93, 0x44, 0xe8, 0x32, 0xdd, 0x3a, 0xb9, 0xc9, 0x14, 0x30, 0xf5, 0xa7, 0xf5, 0xea, 0x8a, 0x7c, 0x08, 0x4f, 0xab, 0xfe, 0xe4, 0xbe, 0xdf, 0xbc, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0x28, 0x15, 0x25, 0x92, 0x4c, 0x03, 0x00, 0x00, }, // uber/cadence/api/v1/common.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdd, 0x72, 0x22, 0xc7, 0x15, 0xf6, 0xc0, 0xa2, 0x9f, 0x03, 0xbb, 0x42, 0xad, 0xfd, 0x61, 0xb5, 0x5e, 0xaf, 0x16, 0x97, 0x63, 0x79, 0x2b, 0x86, 0x88, 0x4d, 0x52, 0x2e, 0x3b, 0x4e, 0x82, 0xd0, 0x48, 0x9a, 0x5d, 0x02, 0xa4, 0x99, 0x95, 0xac, 0xa4, 0xca, 0x53, 0xcd, 0x4c, 0x83, 0x3b, 0x0c, 0xd3, 0x93, 0x99, 0x1e, 0x56, 0xf8, 0x22, 0x95, 0xcb, 0xe4, 0x26, 0x8f, 0x90, 0x8b, 0xbc, 0x48, 0x1e, 0x20, 0x97, 0x79, 0x97, 0x5c, 0xa7, 0xba, 0xa7, 0x07, 0x81, 0xc2, 0x1a, 0x5f, 0xa4, 0x7c, 0x47, 0x9f, 0xf3, 0x7d, 0xa7, 0xbf, 0xd3, 0xdd, 0xe7, 0x1c, 0x06, 0x0e, 0x92, 0x01, 0x8d, 0xea, 0x2e, 0xf1, 0x68, 0xe0, 0xd2, 0x3a, 0x09, 0x59, 0x7d, 0x7a, 0x54, 0x77, 0xf9, 0x64, 0xc2, 0x83, 0x5a, 0x18, 0x71, 0xc1, 0xd1, 0x9e, 0x44, 0xd4, 0x34, 0xa2, 0x46, 0x42, 0x56, 0x9b, 0x1e, 0xed, 0x7f, 0x30, 0xe2, 0x7c, 0xe4, 0xd3, 0xba, 0x82, 0x0c, 0x92, 0x61, 0xdd, 0x4b, 0x22, 0x22, 0x58, 0x46, 0xaa, 0xbe, 0x86, 0xdd, 0x4b, 0x1e, 0x8d, 0x87, 0x3e, 0x7f, 0x6b, 0x5e, 0x53, 0x37, 0x91, 0x2e, 0xf4, 0x0c, 0x8a, 0x6f, 0xb5, 0xd1, 0x61, 0x5e, 0xc5, 0x38, 0x30, 0x0e, 0xb7, 0x31, 0x64, 0x26, 0xcb, 0x43, 0x0f, 0x60, 0x23, 0x4a, 0x02, 0xe9, 0xcb, 0x29, 0x5f, 0x21, 0x4a, 0x02, 0xcb, 0xab, 0x56, 0xa1, 0x94, 0x05, 0xb3, 0x67, 0x21, 0x45, 0x08, 0xee, 0x04, 0x64, 0x42, 0x75, 0x00, 0xf5, 0x5b, 0x62, 0x9a, 0xae, 0x60, 0x53, 0x26, 0x66, 0xef, 0xc4, 0x3c, 0x85, 0xcd, 0x1e, 0x99, 0xf9, 0x9c, 0x78, 0xd2, 0xed, 0x11, 0x41, 0x94, 0xbb, 0x84, 0xd5, 0xef, 0xea, 0x17, 0xb0, 0x79, 0x4a, 0x98, 0x9f, 0x44, 0x14, 0x3d, 0x84, 0x8d, 0x88, 0x92, 0x98, 0x07, 0x9a, 0xaf, 0x57, 0xa8, 0x02, 0x9b, 0x1e, 0x15, 0x84, 0xf9, 0xb1, 0x52, 0x58, 0xc2, 0xd9, 0xb2, 0xfa, 0x77, 0x03, 0xee, 0xfc, 0x86, 0x4e, 0x38, 0xfa, 0x12, 0x36, 0x86, 0x8c, 0xfa, 0x5e, 0x5c, 0x31, 0x0e, 0xf2, 0x87, 0xc5, 0xc6, 0x47, 0xb5, 0x15, 0xe7, 0x57, 0x93, 0xd0, 0xda, 0xa9, 0xc2, 0x99, 0x81, 0x88, 0x66, 0x58, 0x93, 0xf6, 0x2f, 0xa1, 0xb8, 0x60, 0x46, 0x65, 0xc8, 0x8f, 0xe9, 0x4c, 0xab, 0x90, 0x3f, 0x51, 0x03, 0x0a, 0x53, 0xe2, 0x27, 0x54, 0x09, 0x28, 0x36, 0xde, 0x5f, 0x19, 0x5e, 0xa7, 0x89, 0x53, 0xe8, 0xe7, 0xb9, 0xcf, 0x8c, 0xea, 0x3f, 0x0c, 0xd8, 0x38, 0xa7, 0xc4, 0xa3, 0x11, 0xfa, 0xd5, 0x2d, 0x89, 0x1f, 0xaf, 0x8c, 0x91, 0x82, 0x7f, 0x58, 0x91, 0xff, 0x36, 0xa0, 0xdc, 0xa7, 0x24, 0x72, 0xbf, 0x69, 0x0a, 0x11, 0xb1, 0x41, 0x22, 0x68, 0x8c, 0x1c, 0xb8, 0xc7, 0x02, 0x8f, 0x5e, 0x53, 0xcf, 0x59, 0x92, 0xfd, 0xd9, 0xca, 0xa8, 0xb7, 0xe9, 0x35, 0x2b, 0xe5, 0x2e, 0xe6, 0x71, 0x97, 0x2d, 0xda, 0xf6, 0xbf, 0x06, 0xf4, 0xbf, 0xa0, 0xff, 0x63, 0x56, 0x43, 0xd8, 0x3a, 0x21, 0x82, 0x1c, 0xfb, 0x7c, 0x80, 0x4e, 0xe1, 0x2e, 0x0d, 0x5c, 0xee, 0xb1, 0x60, 0xe4, 0x88, 0x59, 0x98, 0x3e, 0xd0, 0x7b, 0x8d, 0xe7, 0x2b, 0x63, 0x99, 0x1a, 0x29, 0x5f, 0x34, 0x2e, 0xd1, 0x85, 0xd5, 0xfc, 0x01, 0xe7, 0x16, 0x1e, 0x70, 0x2f, 0x2d, 0x3a, 0x1a, 0x5d, 0xd0, 0x28, 0x66, 0x3c, 0xb0, 0x82, 0x21, 0x97, 0x40, 0x36, 0x09, 0xfd, 0xac, 0x10, 0xe4, 0x6f, 0xf4, 0x31, 0xec, 0x0c, 0x29, 0x11, 0x49, 0x44, 0x9d, 0x69, 0x0a, 0xd5, 0x05, 0x77, 0x4f, 0x9b, 0x75, 0x80, 0xea, 0x6b, 0x78, 0xd4, 0x4f, 0xc2, 0x90, 0x47, 0x82, 0x7a, 0x2d, 0x9f, 0xd1, 0x40, 0x68, 0x4f, 0x2c, 0x6b, 0x75, 0xc4, 0x9d, 0xd8, 0x1b, 0xeb, 0xc8, 0x85, 0x11, 0xef, 0x7b, 0x63, 0xf4, 0x18, 0xb6, 0xfe, 0x40, 0xa6, 0x44, 0x39, 0xd2, 0x98, 0x9b, 0x72, 0xdd, 0xf7, 0xc6, 0xd5, 0x3f, 0xe7, 0xa1, 0x88, 0xa9, 0x88, 0x66, 0x3d, 0xee, 0x33, 0x77, 0x86, 0x4e, 0xa0, 0xcc, 0x02, 0x26, 0x18, 0xf1, 0x1d, 0x16, 0x08, 0x1a, 0x4d, 0x49, 0xaa, 0xb2, 0xd8, 0x78, 0x5c, 0x4b, 0xdb, 0x4b, 0x2d, 0x6b, 0x2f, 0xb5, 0x13, 0xdd, 0x5e, 0xf0, 0x8e, 0xa6, 0x58, 0x9a, 0x81, 0xea, 0xb0, 0x37, 0x20, 0xee, 0x98, 0x0f, 0x87, 0x8e, 0xcb, 0xe9, 0x70, 0xc8, 0x5c, 0x29, 0x53, 0xed, 0x6d, 0x60, 0xa4, 0x5d, 0xad, 0x1b, 0x8f, 0xdc, 0x76, 0x42, 0xae, 0xd9, 0x24, 0x99, 0xdc, 0x6c, 0x9b, 0x5f, 0xbb, 0xad, 0xa6, 0xcc, 0xb7, 0xfd, 0xe4, 0x26, 0x0a, 0x11, 0x82, 0x4e, 0x42, 0x11, 0x57, 0xee, 0x1c, 0x18, 0x87, 0x85, 0x39, 0xb4, 0xa9, 0xcd, 0xe8, 0x4b, 0x78, 0x12, 0xf0, 0xc0, 0x89, 0x64, 0xea, 0x64, 0xe0, 0x53, 0x87, 0x46, 0x11, 0x8f, 0x9c, 0xb4, 0xa5, 0xc4, 0x95, 0xc2, 0x41, 0xfe, 0x70, 0x1b, 0x57, 0x02, 0x1e, 0xe0, 0x0c, 0x61, 0x4a, 0x00, 0x4e, 0xfd, 0xe8, 0x15, 0xec, 0xd1, 0xeb, 0x90, 0xa5, 0x42, 0x6e, 0x24, 0x6f, 0xac, 0x93, 0x8c, 0x6e, 0x58, 0x99, 0xea, 0xea, 0x04, 0x1e, 0x59, 0x31, 0xf7, 0x95, 0xf1, 0x2c, 0xe2, 0x49, 0xd8, 0x23, 0x91, 0x60, 0xaa, 0x39, 0xaf, 0x68, 0x98, 0xe8, 0x97, 0x50, 0x88, 0x05, 0x11, 0xe9, 0x83, 0xbf, 0xd7, 0x38, 0x5c, 0xf9, 0x48, 0x97, 0x03, 0xf6, 0x25, 0x1e, 0xa7, 0xb4, 0xea, 0x14, 0x9e, 0x2c, 0x7b, 0x5b, 0x3c, 0x18, 0xb2, 0x91, 0x56, 0x88, 0x2e, 0xa1, 0xcc, 0x32, 0xb7, 0x33, 0x92, 0xfe, 0xac, 0xb4, 0x7f, 0xfc, 0x3d, 0x76, 0x9a, 0x4b, 0xc7, 0x3b, 0x6c, 0xc9, 0x11, 0x57, 0xff, 0x65, 0xc0, 0x7e, 0x33, 0x9e, 0x05, 0x6e, 0x36, 0x36, 0x96, 0xf7, 0xad, 0xc0, 0x26, 0x0d, 0xe4, 0x39, 0xa7, 0x33, 0x68, 0x0b, 0x67, 0x4b, 0xd4, 0x80, 0x07, 0x61, 0x44, 0x3d, 0x3a, 0x64, 0x01, 0xf5, 0x9c, 0x3f, 0x26, 0x34, 0xa1, 0x8e, 0x3a, 0x95, 0xf4, 0x29, 0xef, 0xdd, 0x38, 0x7f, 0x2b, 0x7d, 0x1d, 0x79, 0x48, 0x4f, 0x01, 0x52, 0xa0, 0x2a, 0xe7, 0xbc, 0x02, 0x6e, 0x2b, 0x8b, 0x2a, 0xd4, 0x5f, 0x43, 0x29, 0x75, 0xbb, 0x4a, 0x83, 0x7a, 0x24, 0xc5, 0xc6, 0xd3, 0x95, 0x09, 0x66, 0x5d, 0x02, 0x17, 0x15, 0x25, 0x55, 0x5d, 0xfd, 0x4f, 0x1e, 0xde, 0x57, 0xb3, 0x8d, 0xb6, 0xfc, 0x24, 0x16, 0x34, 0xea, 0x53, 0x9f, 0xba, 0x32, 0x13, 0x5d, 0x48, 0x7d, 0xd8, 0x8a, 0x45, 0x44, 0x04, 0x1d, 0xcd, 0x74, 0x3b, 0x79, 0xb9, 0x32, 0xfc, 0xea, 0x20, 0x7d, 0x4d, 0x3d, 0xce, 0x55, 0x0c, 0x3c, 0x0f, 0x84, 0xfe, 0x62, 0xc0, 0x87, 0x44, 0x11, 0x1c, 0x37, 0x65, 0x38, 0xb1, 0x60, 0xee, 0x78, 0xe6, 0x44, 0x74, 0x24, 0x2f, 0x4c, 0xe7, 0x93, 0xf6, 0xc2, 0x9f, 0x7e, 0x8f, 0x0d, 0x15, 0x1b, 0x2b, 0x72, 0x9a, 0x99, 0xdc, 0xf1, 0xfc, 0x3d, 0xfc, 0x8c, 0x7c, 0x37, 0x0c, 0xfd, 0xcd, 0x80, 0x8f, 0x6e, 0x49, 0xa1, 0xd7, 0x82, 0x46, 0x01, 0xf1, 0x1d, 0x1a, 0x08, 0x26, 0x66, 0x99, 0x98, 0xb4, 0x8e, 0x7f, 0xbe, 0x5e, 0x8c, 0xa9, 0xf9, 0xa6, 0xa2, 0x2f, 0xc9, 0x79, 0x4e, 0xd6, 0x01, 0x11, 0x86, 0xdd, 0x4c, 0x08, 0xc9, 0x06, 0x8d, 0xbe, 0xd8, 0xd5, 0xe3, 0x5e, 0x07, 0x9b, 0x4f, 0x25, 0x5c, 0x76, 0x6f, 0x59, 0x8e, 0x77, 0x61, 0x27, 0x3b, 0x7b, 0x9d, 0x4d, 0xf5, 0x17, 0x50, 0xbe, 0x4d, 0x44, 0xf7, 0xa1, 0x10, 0xbb, 0x3c, 0xcc, 0xea, 0x34, 0x5d, 0xcc, 0x8b, 0x37, 0xb7, 0xf0, 0x6f, 0xe7, 0x15, 0x3c, 0x5b, 0x73, 0xfe, 0xe8, 0x43, 0xb8, 0xbb, 0x74, 0xa7, 0x3a, 0x68, 0x29, 0x5e, 0x80, 0x7e, 0x9e, 0xab, 0x18, 0xd5, 0xbf, 0x1a, 0xf0, 0x7c, 0xed, 0xf9, 0xa1, 0x9f, 0xc0, 0xfd, 0xdb, 0xf7, 0x32, 0x1f, 0x71, 0xdb, 0xb2, 0x1f, 0x2d, 0x72, 0x54, 0x71, 0xd4, 0x64, 0x6f, 0x5b, 0x66, 0xc8, 0x99, 0x9b, 0xa6, 0xb1, 0xbb, 0x4c, 0x78, 0x4d, 0x67, 0x4a, 0xcb, 0x57, 0xb0, 0xdb, 0x23, 0x23, 0x16, 0xa8, 0x5a, 0xee, 0x86, 0x42, 0x4d, 0xa3, 0x27, 0xb0, 0x1d, 0x92, 0x11, 0x75, 0x62, 0xf6, 0x6d, 0xba, 0x5f, 0x01, 0x6f, 0x49, 0x43, 0x9f, 0x7d, 0x4b, 0xd1, 0x8f, 0x60, 0x27, 0xa0, 0xd7, 0xc2, 0x51, 0x08, 0xc1, 0xc7, 0x34, 0xd0, 0x63, 0xf3, 0xae, 0x34, 0xf7, 0xc8, 0x88, 0xda, 0xd2, 0xf8, 0xe2, 0x2d, 0x94, 0x16, 0x27, 0x2e, 0x7a, 0x0c, 0x0f, 0xcc, 0x4e, 0xab, 0x7b, 0x62, 0x75, 0xce, 0x1c, 0xfb, 0xaa, 0x67, 0x3a, 0x56, 0xe7, 0xa2, 0xd9, 0xb6, 0x4e, 0xca, 0xef, 0xa1, 0x7d, 0x78, 0xb8, 0xec, 0xb2, 0xcf, 0xb1, 0x75, 0x6a, 0xe3, 0xcb, 0xb2, 0x81, 0x1e, 0x02, 0x5a, 0xf6, 0xbd, 0xea, 0x77, 0x3b, 0xe5, 0x1c, 0xaa, 0xc0, 0xfd, 0x65, 0x7b, 0x0f, 0x77, 0xed, 0xee, 0xcb, 0x72, 0xfe, 0xc5, 0x9f, 0x60, 0x6f, 0x45, 0x17, 0x45, 0xcf, 0xe1, 0xa9, 0xd5, 0xef, 0xb6, 0x9b, 0xb6, 0xd5, 0xed, 0x38, 0x67, 0xb8, 0xfb, 0xa6, 0xe7, 0xf4, 0xed, 0xa6, 0xbd, 0xa8, 0xe3, 0x9d, 0x90, 0x73, 0xb3, 0xd9, 0xb6, 0xcf, 0xaf, 0xca, 0xc6, 0xbb, 0x21, 0x27, 0xb8, 0x69, 0x75, 0xcc, 0x93, 0x72, 0xee, 0xc5, 0x3f, 0x0d, 0xf8, 0xe0, 0xbb, 0x9b, 0x03, 0xfa, 0x14, 0x3e, 0x69, 0xb6, 0x6c, 0xeb, 0xc2, 0x74, 0x5a, 0xed, 0x37, 0x7d, 0xdb, 0xc4, 0x4e, 0xdf, 0x6c, 0x9b, 0x2d, 0x15, 0xb4, 0x6f, 0xe3, 0xa6, 0x6d, 0x9e, 0x5d, 0x2d, 0xe8, 0x7a, 0x09, 0xf5, 0xf5, 0x70, 0x6c, 0x9e, 0xa5, 0x6b, 0xab, 0xf5, 0x5a, 0x2a, 0xfd, 0x19, 0x1c, 0xad, 0x27, 0x99, 0x5f, 0xd9, 0x26, 0xee, 0x34, 0xdb, 0x8e, 0xd9, 0xb1, 0x2d, 0xfb, 0xaa, 0x9c, 0xdb, 0xcf, 0x55, 0x8c, 0x17, 0x5f, 0x43, 0x49, 0xfe, 0x77, 0xe7, 0x53, 0x1a, 0x65, 0x57, 0x77, 0xda, 0xb4, 0xda, 0xdd, 0x0b, 0x13, 0xdf, 0xbe, 0xba, 0x47, 0xb0, 0xb7, 0xec, 0x3a, 0xed, 0xe2, 0x96, 0x59, 0x36, 0xe4, 0x9d, 0x2e, 0x3b, 0xce, 0x70, 0xb3, 0x65, 0x9e, 0xbe, 0x69, 0x97, 0x73, 0xc7, 0xbf, 0x87, 0x47, 0x2e, 0x9f, 0xac, 0xaa, 0xed, 0xe3, 0x62, 0x4b, 0x7d, 0x2d, 0xf5, 0xe4, 0x00, 0xee, 0x19, 0xbf, 0x3b, 0x1a, 0x31, 0xf1, 0x4d, 0x32, 0xa8, 0xb9, 0x7c, 0x52, 0x5f, 0xfc, 0xb6, 0xfa, 0x94, 0x79, 0x7e, 0x7d, 0xc4, 0xd3, 0x2f, 0x26, 0xfd, 0xa1, 0xf5, 0x05, 0x09, 0xd9, 0xf4, 0x68, 0xb0, 0xa1, 0x6c, 0x2f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x5e, 0xe2, 0xc9, 0x06, 0x8c, 0x0d, 0x00, 0x00, }, // google/protobuf/duration.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a, 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0x56, 0x5c, 0x1c, 0x2e, 0x50, 0x25, 0x42, 0x12, 0x5c, 0xec, 0xc5, 0xa9, 0xc9, 0xf9, 0x79, 0x29, 0xc5, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x30, 0xae, 0x90, 0x08, 0x17, 0x6b, 0x5e, 0x62, 0x5e, 0x7e, 0xb1, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x84, 0xe3, 0xd4, 0xcc, 0xc8, 0x25, 0x9c, 0x9c, 0x9f, 0xab, 0x87, 0x66, 0xa6, 0x13, 0x2f, 0xcc, 0xc4, 0x00, 0x90, 0x48, 0x00, 0x63, 0x94, 0x21, 0x54, 0x45, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x5e, 0x7e, 0x51, 0x3a, 0xc2, 0x81, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0xfa, 0xd9, 0x79, 0xf9, 0xe5, 0x79, 0x70, 0xc7, 0x16, 0x24, 0xfd, 0x60, 0x64, 0x5c, 0xc4, 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0xa2, 0x39, 0x00, 0xaa, 0x43, 0x2f, 0x3c, 0x35, 0x27, 0xc7, 0x1b, 0xa4, 0x3e, 0x04, 0xa4, 0x35, 0x89, 0x0d, 0x6c, 0x94, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xef, 0x8a, 0xb4, 0xc3, 0xfb, 0x00, 0x00, 0x00, }, // uber/cadence/admin/v1/history.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2e, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x4f, 0x4c, 0xc9, 0xcd, 0xcc, 0xd3, 0x2f, 0x33, 0xd4, 0xcf, 0xc8, 0x2c, 0x2e, 0xc9, 0x2f, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x05, 0x29, 0xd2, 0x83, 0x2a, 0xd2, 0x03, 0x2b, 0xd2, 0x2b, 0x33, 0x54, 0xf2, 0xe4, 0x12, 0x0a, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0xf3, 0x80, 0x28, 0xf7, 0x2c, 0x49, 0xcd, 0x15, 0x92, 0xe4, 0xe2, 0x48, 0x2d, 0x4b, 0xcd, 0x2b, 0x89, 0xcf, 0x4c, 0x91, 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e, 0x62, 0x07, 0xf3, 0x3d, 0x53, 0x84, 0x24, 0xb8, 0xd8, 0xcb, 0x20, 0x1a, 0x24, 0x98, 0x20, 0x32, 0x50, 0xae, 0x52, 0x09, 0x17, 0x1f, 0xaa, 0x51, 0x42, 0x8a, 0x5c, 0x3c, 0x49, 0x45, 0x89, 0x79, 0xc9, 0x19, 0xf1, 0x25, 0xf9, 0xd9, 0xa9, 0x79, 0x60, 0xa3, 0x78, 0x82, 0xb8, 0x21, 0x62, 0x21, 0x20, 0x21, 0x21, 0x7b, 0x2e, 0xd6, 0xcc, 0x92, 0xd4, 0xdc, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x4d, 0x3d, 0xac, 0xce, 0xd4, 0xc3, 0x74, 0x63, 0x10, 0x44, 0x9f, 0x93, 0x79, 0x94, 0x69, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0x72, 0x48, 0xe8, 0x66, 0xa6, 0xe4, 0xe8, 0xa7, 0xe7, 0xeb, 0x83, 0xfd, 0x0f, 0x0f, 0x16, 0x6b, 0x30, 0xa3, 0xcc, 0x30, 0x89, 0x0d, 0x2c, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x44, 0x14, 0xd7, 0xd4, 0x3e, 0x01, 0x00, 0x00, }, } ================================================ FILE: .gen/proto/shared/v1/history.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/shared/v1/history.proto package sharedv1 import ( fmt "fmt" io "io" math "math" math_bits "math/bits" proto "github.com/gogo/protobuf/proto" v11 "github.com/uber/cadence-idl/go/proto/admin/v1" v1 "github.com/uber/cadence-idl/go/proto/api/v1" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type TransientDecisionInfo struct { ScheduledEvent *v1.HistoryEvent `protobuf:"bytes,1,opt,name=scheduled_event,json=scheduledEvent,proto3" json:"scheduled_event,omitempty"` StartedEvent *v1.HistoryEvent `protobuf:"bytes,2,opt,name=started_event,json=startedEvent,proto3" json:"started_event,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *TransientDecisionInfo) Reset() { *m = TransientDecisionInfo{} } func (m *TransientDecisionInfo) String() string { return proto.CompactTextString(m) } func (*TransientDecisionInfo) ProtoMessage() {} func (*TransientDecisionInfo) Descriptor() ([]byte, []int) { return fileDescriptor_0370c4177fcc3ee8, []int{0} } func (m *TransientDecisionInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *TransientDecisionInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_TransientDecisionInfo.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *TransientDecisionInfo) XXX_Merge(src proto.Message) { xxx_messageInfo_TransientDecisionInfo.Merge(m, src) } func (m *TransientDecisionInfo) XXX_Size() int { return m.Size() } func (m *TransientDecisionInfo) XXX_DiscardUnknown() { xxx_messageInfo_TransientDecisionInfo.DiscardUnknown(m) } var xxx_messageInfo_TransientDecisionInfo proto.InternalMessageInfo func (m *TransientDecisionInfo) GetScheduledEvent() *v1.HistoryEvent { if m != nil { return m.ScheduledEvent } return nil } func (m *TransientDecisionInfo) GetStartedEvent() *v1.HistoryEvent { if m != nil { return m.StartedEvent } return nil } // VersionHistories contains all version histories from all branches. type VersionHistories struct { CurrentVersionHistoryIndex int32 `protobuf:"varint,1,opt,name=current_version_history_index,json=currentVersionHistoryIndex,proto3" json:"current_version_history_index,omitempty"` Histories []*v11.VersionHistory `protobuf:"bytes,2,rep,name=histories,proto3" json:"histories,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *VersionHistories) Reset() { *m = VersionHistories{} } func (m *VersionHistories) String() string { return proto.CompactTextString(m) } func (*VersionHistories) ProtoMessage() {} func (*VersionHistories) Descriptor() ([]byte, []int) { return fileDescriptor_0370c4177fcc3ee8, []int{1} } func (m *VersionHistories) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *VersionHistories) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_VersionHistories.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } return b[:n], nil } } func (m *VersionHistories) XXX_Merge(src proto.Message) { xxx_messageInfo_VersionHistories.Merge(m, src) } func (m *VersionHistories) XXX_Size() int { return m.Size() } func (m *VersionHistories) XXX_DiscardUnknown() { xxx_messageInfo_VersionHistories.DiscardUnknown(m) } var xxx_messageInfo_VersionHistories proto.InternalMessageInfo func (m *VersionHistories) GetCurrentVersionHistoryIndex() int32 { if m != nil { return m.CurrentVersionHistoryIndex } return 0 } func (m *VersionHistories) GetHistories() []*v11.VersionHistory { if m != nil { return m.Histories } return nil } func init() { proto.RegisterType((*TransientDecisionInfo)(nil), "uber.cadence.shared.v1.TransientDecisionInfo") proto.RegisterType((*VersionHistories)(nil), "uber.cadence.shared.v1.VersionHistories") } func init() { proto.RegisterFile("uber/cadence/shared/v1/history.proto", fileDescriptor_0370c4177fcc3ee8) } var fileDescriptor_0370c4177fcc3ee8 = []byte{ // 314 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x4f, 0x4a, 0x03, 0x31, 0x14, 0xc6, 0x49, 0x45, 0xc1, 0xd4, 0x7f, 0x0c, 0x28, 0xa5, 0x60, 0x69, 0xab, 0x42, 0x57, 0x19, 0xa6, 0xe2, 0xca, 0x95, 0x56, 0xc5, 0xba, 0x2c, 0xe2, 0xc2, 0xcd, 0x90, 0x26, 0xcf, 0x4e, 0xc0, 0x26, 0x25, 0xc9, 0x04, 0x7b, 0x15, 0xaf, 0xe0, 0x45, 0x5c, 0x7a, 0x04, 0xe9, 0x49, 0x24, 0x33, 0x63, 0x4b, 0xec, 0xc6, 0xdd, 0x84, 0xf7, 0xfb, 0x7d, 0x7c, 0xf3, 0x1e, 0x3e, 0xcd, 0xc7, 0xa0, 0x63, 0x46, 0x39, 0x48, 0x06, 0xb1, 0xc9, 0xa8, 0x06, 0x1e, 0xbb, 0x24, 0xce, 0x84, 0xb1, 0x4a, 0xcf, 0xc9, 0x4c, 0x2b, 0xab, 0xa2, 0x23, 0x4f, 0x91, 0x8a, 0x22, 0x25, 0x45, 0x5c, 0xd2, 0xec, 0x04, 0x36, 0x9d, 0x89, 0x35, 0xb5, 0x79, 0x12, 0x22, 0x7c, 0x2a, 0xe4, 0x1a, 0xd4, 0xfd, 0x40, 0xf8, 0xf0, 0x51, 0x53, 0x69, 0x04, 0x48, 0x7b, 0x03, 0x4c, 0x18, 0xa1, 0xe4, 0x50, 0xbe, 0xa8, 0xe8, 0x01, 0xef, 0x1b, 0x96, 0x01, 0xcf, 0x5f, 0x81, 0xa7, 0xe0, 0x40, 0xda, 0x06, 0x6a, 0xa3, 0x5e, 0xbd, 0xdf, 0x21, 0x41, 0x27, 0x3a, 0x13, 0xc4, 0x25, 0xe4, 0xbe, 0x8c, 0xbd, 0xf5, 0xe0, 0x68, 0x6f, 0x69, 0x16, 0xef, 0xe8, 0x0e, 0xef, 0x1a, 0x4b, 0xb5, 0x5d, 0x26, 0xd5, 0xfe, 0x9b, 0xb4, 0x53, 0x79, 0xc5, 0xab, 0xfb, 0x8e, 0xf0, 0xc1, 0x13, 0x68, 0xdf, 0xb1, 0xa4, 0x04, 0x98, 0xe8, 0x0a, 0x1f, 0xb3, 0x5c, 0x6b, 0x90, 0x36, 0x75, 0xe5, 0x2c, 0xad, 0xfe, 0x31, 0x15, 0x92, 0xc3, 0x5b, 0x51, 0x7b, 0x73, 0xd4, 0xac, 0xa0, 0xc0, 0x9f, 0x0f, 0x3d, 0x11, 0x0d, 0xf0, 0x76, 0xf6, 0x9b, 0xd7, 0xa8, 0xb5, 0x37, 0x7a, 0xf5, 0xfe, 0xd9, 0x9f, 0x6e, 0x7e, 0x7d, 0xbe, 0x5d, 0xa8, 0x8f, 0x56, 0xde, 0xf5, 0xe0, 0x73, 0xd1, 0x42, 0x5f, 0x8b, 0x16, 0xfa, 0x5e, 0xb4, 0xd0, 0xf3, 0xc5, 0x44, 0xd8, 0x2c, 0x1f, 0x13, 0xa6, 0xa6, 0x71, 0x70, 0x08, 0x32, 0x01, 0x19, 0x17, 0xcb, 0x5f, 0x1d, 0xfd, 0xb2, 0xfc, 0x72, 0xc9, 0x78, 0xab, 0x98, 0x9c, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x3c, 0x85, 0x6e, 0xf3, 0x1e, 0x02, 0x00, 0x00, } func (m *TransientDecisionInfo) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TransientDecisionInfo) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *TransientDecisionInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if m.StartedEvent != nil { { size, err := m.StartedEvent.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintHistory(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } if m.ScheduledEvent != nil { { size, err := m.ScheduledEvent.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintHistory(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *VersionHistories) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VersionHistories) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } func (m *VersionHistories) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l if m.XXX_unrecognized != nil { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Histories) > 0 { for iNdEx := len(m.Histories) - 1; iNdEx >= 0; iNdEx-- { { size, err := m.Histories[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarintHistory(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 } } if m.CurrentVersionHistoryIndex != 0 { i = encodeVarintHistory(dAtA, i, uint64(m.CurrentVersionHistoryIndex)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func encodeVarintHistory(dAtA []byte, offset int, v uint64) int { offset -= sovHistory(v) base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) return base } func (m *TransientDecisionInfo) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.ScheduledEvent != nil { l = m.ScheduledEvent.Size() n += 1 + l + sovHistory(uint64(l)) } if m.StartedEvent != nil { l = m.StartedEvent.Size() n += 1 + l + sovHistory(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func (m *VersionHistories) Size() (n int) { if m == nil { return 0 } var l int _ = l if m.CurrentVersionHistoryIndex != 0 { n += 1 + sovHistory(uint64(m.CurrentVersionHistoryIndex)) } if len(m.Histories) > 0 { for _, e := range m.Histories { l = e.Size() n += 1 + l + sovHistory(uint64(l)) } } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } return n } func sovHistory(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozHistory(x uint64) (n int) { return sovHistory(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *TransientDecisionInfo) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowHistory } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TransientDecisionInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TransientDecisionInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ScheduledEvent", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowHistory } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthHistory } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthHistory } if postIndex > l { return io.ErrUnexpectedEOF } if m.ScheduledEvent == nil { m.ScheduledEvent = &v1.HistoryEvent{} } if err := m.ScheduledEvent.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StartedEvent", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowHistory } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthHistory } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthHistory } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartedEvent == nil { m.StartedEvent = &v1.HistoryEvent{} } if err := m.StartedEvent.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipHistory(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthHistory } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VersionHistories) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowHistory } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VersionHistories: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VersionHistories: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CurrentVersionHistoryIndex", wireType) } m.CurrentVersionHistoryIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowHistory } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CurrentVersionHistoryIndex |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Histories", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowHistory } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return ErrInvalidLengthHistory } postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthHistory } if postIndex > l { return io.ErrUnexpectedEOF } m.Histories = append(m.Histories, &v11.VersionHistory{}) if err := m.Histories[len(m.Histories)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipHistory(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthHistory } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func skipHistory(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowHistory } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } wireType := int(wire & 0x7) switch wireType { case 0: for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowHistory } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } iNdEx++ if dAtA[iNdEx-1] < 0x80 { break } } case 1: iNdEx += 8 case 2: var length int for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowHistory } if iNdEx >= l { return 0, io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { break } } if length < 0 { return 0, ErrInvalidLengthHistory } iNdEx += length case 3: depth++ case 4: if depth == 0 { return 0, ErrUnexpectedEndOfGroupHistory } depth-- case 5: iNdEx += 4 default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } if iNdEx < 0 { return 0, ErrInvalidLengthHistory } if depth == 0 { return iNdEx, nil } } return 0, io.ErrUnexpectedEOF } var ( ErrInvalidLengthHistory = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowHistory = fmt.Errorf("proto: integer overflow") ErrUnexpectedEndOfGroupHistory = fmt.Errorf("proto: unexpected end of group") ) ================================================ FILE: .gen/proto/shared/v1/history.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/shared/v1/history.proto package sharedv1 var yarpcFileDescriptorClosure0370c4177fcc3ee8 = [][]byte{ // uber/cadence/shared/v1/history.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x4f, 0x4b, 0xfb, 0x30, 0x18, 0xc7, 0xe9, 0x7e, 0xfc, 0x04, 0x33, 0xff, 0x51, 0x50, 0x46, 0x41, 0xd8, 0xa6, 0xc2, 0x4e, 0x09, 0x9d, 0x88, 0x07, 0x4f, 0xfe, 0xc5, 0x79, 0x2c, 0xe2, 0xc1, 0x4b, 0x49, 0x93, 0xc7, 0x35, 0xe0, 0x92, 0x92, 0xa4, 0xc1, 0xbd, 0x15, 0xdf, 0x82, 0x6f, 0x52, 0xd2, 0xd6, 0x8d, 0xb8, 0x8b, 0xb7, 0x3e, 0x3c, 0x9f, 0xcf, 0x97, 0x6f, 0x9f, 0xa0, 0xd3, 0xba, 0x00, 0x4d, 0x18, 0xe5, 0x20, 0x19, 0x10, 0x53, 0x52, 0x0d, 0x9c, 0xb8, 0x94, 0x94, 0xc2, 0x58, 0xa5, 0x97, 0xb8, 0xd2, 0xca, 0xaa, 0xf8, 0xc8, 0x53, 0xb8, 0xa3, 0x70, 0x4b, 0x61, 0x97, 0x26, 0xa3, 0xc0, 0xa6, 0x95, 0xd8, 0x50, 0x93, 0x93, 0x10, 0xe1, 0x0b, 0x21, 0x37, 0xa0, 0xf1, 0x57, 0x84, 0x0e, 0x9f, 0x35, 0x95, 0x46, 0x80, 0xb4, 0x77, 0xc0, 0x84, 0x11, 0x4a, 0xce, 0xe4, 0x9b, 0x8a, 0x9f, 0xd0, 0xbe, 0x61, 0x25, 0xf0, 0xfa, 0x1d, 0x78, 0x0e, 0x0e, 0xa4, 0x1d, 0x44, 0xc3, 0x68, 0xd2, 0x9f, 0x8e, 0x70, 0xd0, 0x89, 0x56, 0x02, 0xbb, 0x14, 0x3f, 0xb6, 0xb1, 0xf7, 0x1e, 0xcc, 0xf6, 0x56, 0x66, 0x33, 0xc7, 0x0f, 0x68, 0xd7, 0x58, 0xaa, 0xed, 0x2a, 0xa9, 0xf7, 0xd7, 0xa4, 0x9d, 0xce, 0x6b, 0xa6, 0xf1, 0x67, 0x84, 0x0e, 0x5e, 0x40, 0xfb, 0x8e, 0x2d, 0x25, 0xc0, 0xc4, 0xd7, 0xe8, 0x98, 0xd5, 0x5a, 0x83, 0xb4, 0xb9, 0x6b, 0x77, 0x79, 0xf7, 0x8f, 0xb9, 0x90, 0x1c, 0x3e, 0x9a, 0xda, 0xff, 0xb3, 0xa4, 0x83, 0x02, 0x7f, 0x39, 0xf3, 0x44, 0x7c, 0x8b, 0xb6, 0xcb, 0x9f, 0xbc, 0x41, 0x6f, 0xf8, 0x6f, 0xd2, 0x9f, 0x9e, 0xfd, 0xea, 0xe6, 0xcf, 0xe7, 0xdb, 0x85, 0x7a, 0xb6, 0xf6, 0x6e, 0x2e, 0x5f, 0x2f, 0xe6, 0xc2, 0x96, 0x75, 0x81, 0x99, 0x5a, 0x90, 0xe0, 0xf8, 0x78, 0x0e, 0x92, 0x34, 0x07, 0x5f, 0x3f, 0xf4, 0x55, 0xfb, 0xe5, 0xd2, 0x62, 0xab, 0xd9, 0x9c, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0xc6, 0xf4, 0xa6, 0x9c, 0x12, 0x02, 0x00, 0x00, }, // uber/cadence/api/v1/history.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5c, 0x5d, 0x6c, 0x1c, 0x49, 0xb5, 0xde, 0x9e, 0xb1, 0xc7, 0x9e, 0x33, 0x8e, 0x3d, 0x2e, 0x3b, 0x8e, 0xff, 0x12, 0x3b, 0x93, 0x6c, 0xe2, 0x75, 0x9c, 0x71, 0xe2, 0x64, 0x93, 0x4d, 0xb2, 0x3f, 0xd7, 0x76, 0x6c, 0xed, 0x48, 0xbe, 0x49, 0x6e, 0xc7, 0xc9, 0xde, 0x7b, 0xb5, 0xd2, 0xd0, 0xee, 0x2e, 0xc7, 0x8d, 0x67, 0xba, 0x67, 0xbb, 0x6b, 0x3c, 0x31, 0x82, 0x27, 0x1e, 0x90, 0x10, 0x2b, 0x58, 0xad, 0x90, 0x58, 0x09, 0x04, 0x42, 0x02, 0xb1, 0xfc, 0x68, 0x11, 0x08, 0xf1, 0xf7, 0x02, 0x48, 0x68, 0x91, 0x40, 0x0b, 0x4f, 0xbc, 0xf0, 0x84, 0x84, 0x10, 0xfb, 0xc6, 0x03, 0xcb, 0x33, 0xea, 0xea, 0xea, 0x99, 0xe9, 0x9e, 0xaa, 0xfe, 0x19, 0x3b, 0x59, 0xd0, 0xe6, 0x6d, 0xba, 0xfb, 0x9c, 0xd3, 0x5f, 0x55, 0x9d, 0x73, 0xea, 0xd4, 0x39, 0xa7, 0x07, 0x4e, 0xd6, 0xb7, 0xb0, 0xb5, 0xa8, 0x2a, 0x1a, 0x36, 0x54, 0xbc, 0xa8, 0xd4, 0xf4, 0xc5, 0xbd, 0x8b, 0x8b, 0x3b, 0xba, 0x4d, 0x4c, 0x6b, 0xbf, 0x58, 0xb3, 0x4c, 0x62, 0xa2, 0x11, 0x87, 0xa4, 0xc8, 0x48, 0x8a, 0x4a, 0x4d, 0x2f, 0xee, 0x5d, 0x9c, 0x3c, 0xf1, 0xc0, 0x34, 0x1f, 0x54, 0xf0, 0x22, 0x25, 0xd9, 0xaa, 0x6f, 0x2f, 0x6a, 0x75, 0x4b, 0x21, 0xba, 0x69, 0xb8, 0x4c, 0x93, 0x33, 0xc1, 0xe7, 0x44, 0xaf, 0x62, 0x9b, 0x28, 0xd5, 0x1a, 0x23, 0x98, 0xe5, 0xbd, 0x58, 0x35, 0xab, 0xd5, 0xa6, 0x88, 0x02, 0x8f, 0x82, 0x28, 0xf6, 0x6e, 0x45, 0xb7, 0x49, 0x18, 0x4d, 0xc3, 0xb4, 0x76, 0xb7, 0x2b, 0x66, 0xc3, 0xa5, 0x29, 0xdc, 0x84, 0xbe, 0x97, 0xdd, 0x01, 0xa1, 0x6b, 0x90, 0xc1, 0x7b, 0xd8, 0x20, 0xf6, 0xb8, 0x34, 0x9b, 0x9e, 0xcb, 0x2d, 0x9d, 0x2c, 0x72, 0xc6, 0x56, 0x64, 0xd4, 0x6b, 0x0e, 0xa5, 0xcc, 0x18, 0x0a, 0xef, 0x5f, 0x85, 0x81, 0xf6, 0x07, 0x68, 0x02, 0xfa, 0xe9, 0xa3, 0xb2, 0xae, 0x8d, 0x4b, 0xb3, 0xd2, 0x5c, 0x5a, 0xee, 0xa3, 0xd7, 0x25, 0x0d, 0x5d, 0x03, 0x70, 0x1f, 0x39, 0x83, 0x1e, 0x4f, 0xcd, 0x4a, 0x73, 0xb9, 0xa5, 0xc9, 0xa2, 0x3b, 0x23, 0x45, 0x6f, 0x46, 0x8a, 0x9b, 0xde, 0x8c, 0xc8, 0x59, 0x4a, 0xed, 0x5c, 0xa3, 0x71, 0xe8, 0xdb, 0xc3, 0x96, 0xad, 0x9b, 0xc6, 0x78, 0xda, 0x15, 0xca, 0x2e, 0xd1, 0x31, 0xe8, 0x73, 0x06, 0xef, 0xbc, 0xae, 0x87, 0x3e, 0xc9, 0x38, 0x97, 0x25, 0x0d, 0x7d, 0x59, 0x82, 0x73, 0xde, 0x90, 0xcb, 0xf8, 0x21, 0x56, 0xeb, 0xce, 0x3a, 0x94, 0x6d, 0xa2, 0x58, 0x04, 0x6b, 0x65, 0x17, 0x89, 0x42, 0x88, 0xa5, 0x6f, 0xd5, 0x09, 0xb6, 0xc7, 0x7b, 0x29, 0x9e, 0xe7, 0xb9, 0x43, 0x7f, 0x85, 0xc9, 0x59, 0xf3, 0xc4, 0xdc, 0x75, 0xa5, 0xd0, 0x21, 0x2f, 0x37, 0x65, 0xbc, 0xfc, 0x94, 0x7c, 0xb6, 0x11, 0x8f, 0x14, 0x7d, 0x5d, 0x82, 0xf3, 0x1c, 0x78, 0xaa, 0x59, 0xad, 0x55, 0x30, 0x17, 0x60, 0x86, 0x02, 0x7c, 0x31, 0x1e, 0xc0, 0x55, 0x4f, 0x4e, 0x27, 0xc4, 0x67, 0x1a, 0x71, 0x89, 0xd1, 0x5b, 0x12, 0xcc, 0x73, 0x40, 0x6e, 0x2b, 0x7a, 0x85, 0x87, 0xb0, 0x8f, 0x22, 0xbc, 0x11, 0x0f, 0xe1, 0x3a, 0x15, 0xd2, 0x09, 0xef, 0x4c, 0x23, 0x16, 0x25, 0xfa, 0x1a, 0x7f, 0x02, 0x1d, 0xdd, 0xd2, 0xca, 0x66, 0x9d, 0x74, 0xc2, 0xeb, 0xa7, 0xf0, 0x5e, 0x88, 0x07, 0xcf, 0x51, 0x3b, 0xed, 0x76, 0x9d, 0x74, 0x02, 0x9c, 0x6b, 0xc4, 0xa4, 0x45, 0x6f, 0x4a, 0x30, 0xa7, 0x61, 0x55, 0xb7, 0x29, 0x30, 0x47, 0x4b, 0x6d, 0x75, 0x07, 0x6b, 0x75, 0xee, 0xe4, 0x65, 0x29, 0xba, 0x6b, 0x5c, 0x74, 0x37, 0x99, 0x90, 0x4d, 0xc5, 0xde, 0xbd, 0xeb, 0x89, 0xe8, 0x44, 0x76, 0x5a, 0x8b, 0x41, 0x87, 0x5e, 0x97, 0xe0, 0x4c, 0x00, 0x95, 0xc8, 0x26, 0x80, 0x62, 0xba, 0x1a, 0x8d, 0x49, 0x64, 0x0e, 0x05, 0x2d, 0x92, 0x8a, 0x33, 0x4b, 0x21, 0x46, 0x90, 0x8b, 0x39, 0x4b, 0x21, 0xfa, 0xef, 0x9b, 0x25, 0xa1, 0xea, 0xbf, 0xd1, 0x81, 0x2a, 0x44, 0xb3, 0x06, 0x28, 0xaa, 0xe7, 0x22, 0x51, 0x89, 0x95, 0xea, 0x94, 0x16, 0x4d, 0x86, 0x3e, 0x2b, 0xc1, 0xd3, 0x7e, 0x4c, 0x22, 0x4b, 0x3c, 0x42, 0x01, 0x5d, 0x89, 0x04, 0x24, 0x32, 0xc2, 0x93, 0x5a, 0x14, 0x11, 0x5d, 0x36, 0x45, 0x25, 0xfa, 0x9e, 0x4e, 0xf6, 0x23, 0x95, 0x7b, 0x30, 0x64, 0xd9, 0x96, 0x99, 0x90, 0x28, 0xe5, 0x56, 0x62, 0xd0, 0x51, 0xe5, 0x0e, 0xa0, 0x12, 0x29, 0xf7, 0x50, 0x88, 0x72, 0xfb, 0x30, 0x09, 0x95, 0x5b, 0x89, 0xa4, 0xe2, 0xcc, 0x52, 0x88, 0x72, 0xe7, 0x63, 0xce, 0x52, 0x98, 0x72, 0x2b, 0x31, 0xe8, 0xa8, 0x22, 0xf9, 0x51, 0x89, 0x14, 0x69, 0x38, 0x44, 0x91, 0xda, 0x21, 0x09, 0x15, 0x49, 0x89, 0x22, 0xa2, 0x96, 0xe6, 0x07, 0x13, 0x62, 0x69, 0x28, 0xc4, 0xd2, 0xda, 0xf1, 0x84, 0x58, 0x9a, 0x12, 0x4d, 0x86, 0x1a, 0x70, 0xc2, 0x01, 0x61, 0x89, 0xb5, 0x67, 0x84, 0x02, 0xb9, 0xc0, 0x05, 0xe2, 0x48, 0xb5, 0x84, 0x6a, 0x33, 0x45, 0xc4, 0x8f, 0xd1, 0x6b, 0x30, 0xed, 0xbe, 0x78, 0x5b, 0xb7, 0x78, 0xaf, 0x1d, 0xa5, 0xaf, 0x2d, 0x8a, 0x5f, 0xbb, 0xee, 0xf0, 0x75, 0xbe, 0x74, 0x82, 0x88, 0x1e, 0xa2, 0x6f, 0x4a, 0xb0, 0x18, 0x50, 0x51, 0xc5, 0x50, 0x71, 0xa5, 0x6c, 0xe1, 0xd7, 0xea, 0xd8, 0xe6, 0x8e, 0xfe, 0x28, 0x85, 0xf1, 0x52, 0xb4, 0xa6, 0x52, 0x49, 0xb2, 0x27, 0xa8, 0x13, 0xd7, 0xbc, 0x12, 0x9b, 0x1a, 0xfd, 0x50, 0x82, 0xcb, 0x0c, 0x93, 0x07, 0x31, 0x9e, 0x12, 0x8f, 0x51, 0xb4, 0xab, 0x5c, 0xb4, 0xec, 0x6d, 0xee, 0xab, 0xe3, 0x68, 0x74, 0xd1, 0x4a, 0xc4, 0x81, 0xbe, 0x20, 0xc1, 0x59, 0xde, 0xf4, 0xf2, 0x80, 0x1e, 0x8b, 0xa9, 0xdd, 0xab, 0x4c, 0x42, 0x84, 0x76, 0x0b, 0xc8, 0xd0, 0x27, 0x60, 0xc6, 0x55, 0x32, 0x31, 0x92, 0x71, 0x8a, 0xe4, 0xa2, 0x58, 0xcf, 0xc4, 0x10, 0x5c, 0x05, 0x16, 0xbd, 0xfb, 0x33, 0x12, 0x9c, 0x66, 0x8b, 0xc7, 0x14, 0x5d, 0xb0, 0x68, 0x13, 0x14, 0xc1, 0xb3, 0x5c, 0x04, 0xae, 0x70, 0x57, 0xdf, 0x05, 0xcb, 0x34, 0xab, 0x46, 0xd0, 0xa0, 0x4f, 0xc1, 0x6c, 0x55, 0xb1, 0x76, 0xb1, 0x55, 0xb6, 0xb0, 0x6a, 0x5a, 0x1a, 0x0f, 0xc4, 0x24, 0x05, 0xb1, 0xc4, 0x05, 0xf1, 0xdf, 0x94, 0x59, 0x66, 0xbc, 0x9d, 0x08, 0x8e, 0x57, 0xc3, 0x08, 0xd0, 0x57, 0x25, 0x58, 0xe0, 0x9d, 0x4f, 0xf4, 0x07, 0x86, 0xc2, 0x9d, 0x90, 0xa9, 0x24, 0xe1, 0xeb, 0x5d, 0x26, 0x26, 0x4e, 0xf8, 0x2a, 0xa0, 0x45, 0xdf, 0x90, 0xa0, 0xc8, 0x8b, 0xb0, 0xb1, 0x55, 0xd5, 0x0d, 0x85, 0xeb, 0x17, 0xa6, 0x43, 0xfc, 0x42, 0x67, 0x88, 0xdd, 0x14, 0xc4, 0xf1, 0x0b, 0x8d, 0xd8, 0xd4, 0xe8, 0x47, 0x12, 0x5c, 0xe6, 0x1d, 0xa5, 0x22, 0xbd, 0xd8, 0x71, 0x8a, 0xf6, 0x66, 0xcc, 0x13, 0x55, 0x94, 0x2b, 0x5b, 0x6c, 0x24, 0x63, 0x11, 0x69, 0x80, 0xd8, 0x28, 0x4f, 0x24, 0xd1, 0x00, 0xb1, 0x81, 0xce, 0x35, 0x62, 0xd2, 0xa2, 0xbf, 0x48, 0xb0, 0x16, 0xf0, 0xb8, 0xf8, 0x21, 0xc1, 0x96, 0xa1, 0x54, 0xca, 0x1c, 0xe4, 0xba, 0xa1, 0x13, 0x9d, 0xaf, 0x18, 0x33, 0x14, 0xfa, 0xdd, 0x68, 0x17, 0xbc, 0xc6, 0xe4, 0x77, 0x8c, 0xa7, 0xe4, 0x09, 0xef, 0x1c, 0xd0, 0x8b, 0xd6, 0x81, 0x24, 0xa0, 0x3f, 0x49, 0xb0, 0x92, 0x60, 0x98, 0x22, 0x8f, 0x35, 0x4b, 0xc7, 0x78, 0xe7, 0x00, 0x63, 0x14, 0x39, 0xb3, 0x1b, 0x56, 0xf7, 0xec, 0xe8, 0x3d, 0x09, 0x5e, 0x08, 0x1b, 0x4e, 0xb4, 0x9d, 0x9c, 0xa4, 0x03, 0xdb, 0xe0, 0x0e, 0x4c, 0x08, 0x26, 0xd2, 0x5e, 0xae, 0xe2, 0xee, 0x58, 0x69, 0x1c, 0xc0, 0x4d, 0x9d, 0x18, 0x44, 0x37, 0xea, 0x58, 0x2b, 0x2b, 0x76, 0xd9, 0xc0, 0x8d, 0xce, 0x71, 0x14, 0x42, 0xe2, 0x00, 0x4e, 0x06, 0x85, 0x89, 0x5b, 0xb6, 0x6f, 0xe1, 0x06, 0x27, 0x0e, 0x68, 0x24, 0xe2, 0x40, 0xbf, 0x92, 0xe0, 0x1a, 0x8d, 0x26, 0xcb, 0xea, 0x8e, 0x5e, 0xd1, 0x12, 0xda, 0xcf, 0x29, 0x0a, 0xfd, 0x65, 0x2e, 0x74, 0x1a, 0x4a, 0xae, 0x3a, 0x42, 0x93, 0x18, 0xcd, 0x25, 0x3b, 0x39, 0x1b, 0xfa, 0xa9, 0x04, 0x57, 0x22, 0x06, 0x21, 0xb2, 0x8e, 0xd3, 0x74, 0x04, 0x6b, 0x49, 0x47, 0x20, 0x32, 0x89, 0x0b, 0x76, 0x42, 0x1e, 0xf4, 0x5d, 0x09, 0x2e, 0x0a, 0x51, 0x0b, 0xe3, 0xfc, 0xa7, 0x29, 0xec, 0x65, 0x7e, 0x18, 0xc2, 0x7d, 0xbb, 0x30, 0xf0, 0x5f, 0x50, 0x13, 0xd0, 0xa3, 0x1f, 0x48, 0x70, 0x49, 0x08, 0x37, 0xe4, 0x10, 0x79, 0x26, 0x44, 0xc9, 0xf9, 0x80, 0x43, 0x8e, 0x93, 0x45, 0x35, 0x11, 0x07, 0x7a, 0x5b, 0x82, 0x0b, 0x89, 0x35, 0xe3, 0x2c, 0x45, 0xfc, 0x5f, 0x09, 0x10, 0x8b, 0x94, 0xe2, 0x9c, 0x9a, 0x40, 0x1f, 0xde, 0x91, 0x60, 0x49, 0x3c, 0xc1, 0xc2, 0x4d, 0x78, 0x8e, 0xa2, 0x5d, 0x49, 0x32, 0xbf, 0xc2, 0x9d, 0xf8, 0xbc, 0x9a, 0x84, 0x01, 0x7d, 0x3f, 0x4c, 0x25, 0x42, 0x0e, 0xcd, 0xcf, 0x24, 0x86, 0x2c, 0x3e, 0x3e, 0x0b, 0x20, 0x8b, 0x0e, 0xd2, 0x4e, 0x6c, 0x26, 0x86, 0x1c, 0x12, 0x49, 0xce, 0x87, 0xc4, 0x66, 0x02, 0xcc, 0x21, 0xe1, 0xe4, 0xa2, 0x9a, 0x8c, 0x85, 0x6e, 0x9a, 0x6e, 0x28, 0xde, 0x6d, 0xc4, 0x73, 0x2e, 0x64, 0xd3, 0x74, 0x23, 0xee, 0x6e, 0x42, 0x9d, 0xab, 0x76, 0x77, 0xac, 0xe8, 0xd7, 0x12, 0x5c, 0x8f, 0x31, 0x20, 0x91, 0x8d, 0x2e, 0xd0, 0xd1, 0x94, 0xba, 0x19, 0x8d, 0xc8, 0x58, 0x2f, 0xdb, 0x5d, 0xf0, 0xa1, 0x9f, 0x48, 0xf0, 0x6c, 0xd8, 0x00, 0xc4, 0xe7, 0xa7, 0xf3, 0x21, 0x1b, 0x90, 0x10, 0x84, 0xf8, 0x1c, 0x75, 0x01, 0x27, 0xe4, 0xa1, 0x0e, 0xa7, 0x5e, 0xb3, 0xb1, 0x45, 0x5a, 0xc0, 0x6d, 0xac, 0x58, 0xea, 0x4e, 0x1b, 0xcc, 0x4e, 0xdc, 0xc5, 0x10, 0xeb, 0xbd, 0x47, 0xc5, 0x79, 0x08, 0xee, 0x52, 0x61, 0xad, 0x37, 0x72, 0xac, 0xb7, 0x9e, 0x84, 0x61, 0x65, 0x00, 0xa0, 0x05, 0xa4, 0xf0, 0xe7, 0x21, 0x38, 0x1b, 0x77, 0xf7, 0x5a, 0x87, 0x23, 0xcd, 0x31, 0x92, 0xfd, 0x1a, 0xa6, 0xb5, 0x40, 0x51, 0x65, 0xd1, 0x13, 0xba, 0xb9, 0x5f, 0xc3, 0xf2, 0x40, 0xa3, 0xed, 0x0a, 0xbd, 0x0a, 0x47, 0x6b, 0x8a, 0xe5, 0xcc, 0x48, 0xbb, 0xd1, 0x6d, 0x9b, 0xac, 0x7c, 0x38, 0xc7, 0x95, 0x77, 0x87, 0x72, 0xb4, 0xd9, 0xc4, 0xb6, 0x29, 0x8f, 0xd4, 0x3a, 0x6f, 0xa2, 0xeb, 0x90, 0xa5, 0x19, 0x99, 0x8a, 0x6e, 0x13, 0x5a, 0x58, 0xcc, 0x2d, 0x1d, 0xe7, 0xa7, 0x3c, 0x14, 0x7b, 0x77, 0x43, 0xb7, 0x89, 0xdc, 0x4f, 0xd8, 0x2f, 0xb4, 0x04, 0xbd, 0xba, 0x51, 0xab, 0x13, 0x5a, 0x76, 0xcc, 0x2d, 0x4d, 0x0b, 0x90, 0xec, 0x57, 0x4c, 0x45, 0x93, 0x5d, 0x52, 0xa4, 0xc0, 0x6c, 0x20, 0xe4, 0x28, 0x13, 0xb3, 0xac, 0x56, 0x4c, 0x1b, 0x53, 0xff, 0x6d, 0xd6, 0x09, 0xab, 0x43, 0x4e, 0x74, 0xd4, 0x45, 0x6f, 0xb2, 0x4a, 0xb2, 0x3c, 0x8d, 0x7d, 0x73, 0xbf, 0x69, 0xae, 0x3a, 0xfc, 0x9b, 0x2e, 0x3b, 0x7a, 0x05, 0xa6, 0x5a, 0x69, 0xef, 0x4e, 0xe9, 0x99, 0x28, 0xe9, 0xc7, 0x88, 0x97, 0xcc, 0x0e, 0x08, 0xbe, 0x01, 0x93, 0xad, 0x08, 0xbb, 0x35, 0x0a, 0xab, 0x6e, 0x94, 0x75, 0x8d, 0x96, 0xfe, 0xb2, 0xf2, 0xb1, 0x26, 0x45, 0x73, 0x9e, 0xe5, 0xba, 0x51, 0xd2, 0x50, 0x09, 0xb2, 0xcc, 0x55, 0x9a, 0x16, 0xad, 0xc3, 0x0d, 0x2e, 0x9d, 0xe3, 0xbb, 0x76, 0x26, 0x80, 0x86, 0xd0, 0x25, 0x8f, 0x45, 0x6e, 0x71, 0xa3, 0x12, 0x0c, 0xb7, 0x70, 0x38, 0xee, 0xaa, 0x6e, 0x61, 0x56, 0x3c, 0xe3, 0xaf, 0xc1, 0xba, 0x4b, 0x23, 0xe7, 0x9b, 0x6c, 0xec, 0x0e, 0x92, 0x61, 0xac, 0xa2, 0x38, 0x67, 0x3e, 0x37, 0x9c, 0xa1, 0xc3, 0xc1, 0x76, 0xbd, 0x42, 0x58, 0xe1, 0x2b, 0x7c, 0x4d, 0x47, 0x1d, 0xde, 0xd5, 0x26, 0xab, 0x4c, 0x39, 0xd1, 0x35, 0x98, 0x30, 0x2d, 0xfd, 0x81, 0xee, 0x3a, 0xda, 0xc0, 0x2c, 0xe5, 0xe8, 0x2c, 0x8d, 0x79, 0x04, 0x81, 0x49, 0x9a, 0x84, 0x7e, 0x5d, 0xc3, 0x06, 0xd1, 0xc9, 0x3e, 0xad, 0x28, 0x65, 0xe5, 0xe6, 0x35, 0xba, 0x04, 0x63, 0xdb, 0xba, 0x65, 0x93, 0x4e, 0x99, 0x47, 0x28, 0xe5, 0x08, 0x7d, 0x1a, 0x10, 0xb8, 0x0a, 0x03, 0x16, 0x26, 0xd6, 0x7e, 0xb9, 0x66, 0x56, 0x74, 0x75, 0x9f, 0x55, 0x61, 0x66, 0x05, 0x07, 0x54, 0x62, 0xed, 0xdf, 0xa1, 0x74, 0x72, 0xce, 0x6a, 0x5d, 0xa0, 0x71, 0xe8, 0x53, 0x08, 0xc1, 0xd5, 0x1a, 0xa1, 0x15, 0x93, 0x5e, 0xd9, 0xbb, 0x44, 0xab, 0x30, 0x84, 0x1f, 0xd6, 0x74, 0x57, 0x71, 0xdc, 0xa2, 0x7e, 0x3e, 0xb2, 0xa8, 0x3f, 0xd8, 0x62, 0xa1, 0x95, 0xfd, 0x53, 0x70, 0x44, 0xb5, 0x1c, 0x6b, 0x60, 0x15, 0x1d, 0x5a, 0x71, 0xc8, 0xca, 0x03, 0xce, 0x4d, 0xaf, 0xca, 0x83, 0xfe, 0x17, 0xa6, 0xdc, 0xd1, 0xfb, 0xab, 0x5f, 0x5b, 0x8a, 0xba, 0x6b, 0x6e, 0x6f, 0xb3, 0xa2, 0x40, 0x88, 0x52, 0x8f, 0x53, 0xee, 0xf6, 0xc2, 0xd7, 0x8a, 0xcb, 0x8a, 0xce, 0x43, 0x4f, 0x15, 0x57, 0x4d, 0x96, 0xce, 0x9f, 0xe0, 0x27, 0xfa, 0x70, 0xd5, 0x94, 0x29, 0x19, 0x92, 0x61, 0xb8, 0xc3, 0x63, 0xb3, 0x9c, 0xfc, 0xd3, 0xfc, 0xbd, 0x31, 0xe0, 0x61, 0xe5, 0xbc, 0x1d, 0xb8, 0x83, 0xee, 0xc1, 0x58, 0xcd, 0xc2, 0x7b, 0x65, 0xa5, 0x4e, 0x4c, 0x47, 0xff, 0x30, 0x29, 0xd7, 0x4c, 0xdd, 0x20, 0x5e, 0x96, 0x5d, 0xb4, 0x5e, 0x36, 0x26, 0x77, 0x28, 0x9d, 0x3c, 0xe2, 0xf0, 0x2f, 0xd7, 0x89, 0xd9, 0x76, 0x13, 0x5d, 0x82, 0xcc, 0x0e, 0x56, 0x34, 0x6c, 0xb1, 0xf4, 0xf7, 0x14, 0xbf, 0xa9, 0x83, 0x92, 0xc8, 0x8c, 0x14, 0x6d, 0xc0, 0xa8, 0x3b, 0xd1, 0xad, 0x5a, 0x1e, 0x5d, 0xd7, 0x63, 0x91, 0xeb, 0x8a, 0x28, 0x5f, 0xb3, 0x2e, 0x47, 0xd7, 0xf6, 0x93, 0x90, 0xaf, 0x29, 0x16, 0xd1, 0xbd, 0xe3, 0xf9, 0xb6, 0xfe, 0x60, 0x7c, 0x9c, 0x76, 0x98, 0xfc, 0xcf, 0x41, 0xda, 0x2c, 0x1c, 0xff, 0xee, 0x0a, 0x5d, 0xa5, 0x32, 0xd7, 0x0c, 0x62, 0xed, 0xcb, 0x43, 0x35, 0xff, 0x5d, 0x74, 0x1c, 0xc0, 0x4b, 0xea, 0xe8, 0x1a, 0x4d, 0x27, 0x67, 0xe5, 0x2c, 0xbb, 0x53, 0xd2, 0xd0, 0x7d, 0x18, 0xa1, 0x8a, 0x67, 0xee, 0x61, 0xab, 0xa2, 0xd4, 0x3c, 0x1b, 0x99, 0xa4, 0xce, 0xe9, 0x0c, 0xdf, 0x39, 0x59, 0xa6, 0x71, 0xdb, 0x25, 0x67, 0x96, 0x32, 0xac, 0x06, 0x6f, 0xa1, 0x87, 0x30, 0x43, 0x73, 0xf0, 0xb8, 0xac, 0x56, 0xea, 0x36, 0xc1, 0x56, 0xd9, 0xc6, 0x15, 0xac, 0xd2, 0x39, 0x60, 0xef, 0x98, 0x0a, 0x49, 0xae, 0xd3, 0x34, 0x3f, 0x5e, 0x75, 0x59, 0xef, 0x7a, 0x9c, 0xec, 0x75, 0xd3, 0x4a, 0xc8, 0xd3, 0xc9, 0x15, 0x18, 0xe5, 0xcd, 0x0c, 0xca, 0x43, 0x7a, 0x17, 0xef, 0xd3, 0x1d, 0x38, 0x2b, 0x3b, 0x3f, 0xd1, 0x28, 0xf4, 0xee, 0x29, 0x95, 0xba, 0xdb, 0x84, 0x93, 0x95, 0xdd, 0x8b, 0xeb, 0xa9, 0xe7, 0xa4, 0xc2, 0xdb, 0x12, 0x3c, 0x13, 0xff, 0xb8, 0x77, 0x19, 0x32, 0xcc, 0x61, 0x4a, 0x31, 0x1c, 0x26, 0xa3, 0x45, 0xeb, 0x30, 0x1b, 0x5e, 0xef, 0xd7, 0x35, 0x0a, 0x2c, 0x2d, 0x4f, 0x8b, 0x4b, 0xf5, 0x25, 0xad, 0xf0, 0x2d, 0x09, 0xce, 0xc4, 0x8c, 0x1a, 0xaf, 0x40, 0x9f, 0xb7, 0x55, 0x48, 0x31, 0xb6, 0x0a, 0x8f, 0xf8, 0xd0, 0xa0, 0x9a, 0x30, 0x17, 0xfb, 0xc8, 0xb4, 0x0a, 0x03, 0x6c, 0xb7, 0x6e, 0x45, 0x4e, 0x83, 0x02, 0x2f, 0xc0, 0x36, 0x67, 0x1a, 0x38, 0xe5, 0x48, 0xeb, 0xa2, 0xf0, 0x3b, 0x09, 0x4e, 0xc7, 0xe9, 0x1a, 0xf1, 0x87, 0x40, 0x52, 0xb2, 0x10, 0xe8, 0x16, 0x8c, 0x09, 0xc2, 0x8c, 0x54, 0x94, 0x47, 0x1e, 0xb1, 0x39, 0x21, 0x46, 0xdb, 0x56, 0x93, 0xf6, 0x6d, 0x35, 0x85, 0xd7, 0x25, 0x28, 0x44, 0x37, 0x9c, 0xa0, 0x05, 0x40, 0xc1, 0x26, 0x84, 0x66, 0x1b, 0x5a, 0xde, 0xf6, 0x4d, 0x41, 0x60, 0xbf, 0x4d, 0x05, 0xf6, 0x5b, 0xbf, 0xf3, 0x48, 0x07, 0x9c, 0x47, 0xe1, 0xef, 0x81, 0xe9, 0x15, 0x5a, 0x48, 0x32, 0x44, 0x73, 0x90, 0xf7, 0x27, 0xa2, 0x9a, 0xea, 0x35, 0x68, 0xb7, 0x8d, 0x38, 0x80, 0x3d, 0x1d, 0xc0, 0x7e, 0x16, 0x86, 0xb6, 0x74, 0x43, 0xb1, 0xf6, 0xcb, 0xea, 0x0e, 0x56, 0x77, 0xed, 0x7a, 0x95, 0xc6, 0xa8, 0x59, 0x79, 0xd0, 0xbd, 0xbd, 0xca, 0xee, 0xa2, 0x73, 0x30, 0xec, 0x4f, 0x9f, 0xe2, 0x87, 0x6e, 0xfc, 0x39, 0x20, 0xe7, 0x71, 0x7b, 0x56, 0x13, 0x3f, 0x24, 0x85, 0xef, 0xa4, 0xe1, 0x54, 0x8c, 0x5e, 0x96, 0x47, 0x36, 0xe2, 0xa0, 0x59, 0xa4, 0xbb, 0x30, 0x0b, 0x74, 0x02, 0x72, 0x5b, 0x8a, 0x8d, 0xbd, 0xd8, 0xc9, 0x9d, 0x96, 0xac, 0x73, 0xcb, 0x8d, 0x98, 0xa6, 0x01, 0x0c, 0xdc, 0xf0, 0x1e, 0xf7, 0xba, 0x13, 0x6b, 0xe0, 0x86, 0xfb, 0x74, 0x01, 0xd0, 0xb6, 0x69, 0xed, 0x32, 0xa4, 0x5e, 0x43, 0x62, 0xc6, 0x1d, 0x9a, 0xf3, 0x84, 0x62, 0xbd, 0xcf, 0x3a, 0x13, 0xc7, 0x1c, 0xe7, 0xa8, 0xd8, 0xa6, 0xc1, 0x82, 0x63, 0x76, 0x85, 0x6e, 0x42, 0xaf, 0xaa, 0xd4, 0x6d, 0xcc, 0xe2, 0xe0, 0x62, 0xec, 0xae, 0xa1, 0x55, 0x87, 0x4b, 0x76, 0x99, 0x03, 0x0a, 0x9a, 0x0d, 0x2a, 0xe8, 0xbb, 0x69, 0x38, 0x19, 0xd9, 0xe8, 0xf3, 0xc8, 0xd6, 0x6a, 0xc5, 0x1b, 0xa2, 0xbb, 0x48, 0x0b, 0x31, 0xfb, 0x90, 0x7c, 0x03, 0x6c, 0x73, 0xd9, 0x3d, 0x49, 0x5c, 0x76, 0xbb, 0x65, 0xf4, 0x06, 0x2c, 0x23, 0xb0, 0xfc, 0x99, 0xf0, 0xe5, 0xef, 0x8b, 0xb5, 0xfc, 0xfd, 0x82, 0xe5, 0xe7, 0x58, 0x61, 0x96, 0x6b, 0x85, 0xfe, 0x95, 0x84, 0xe0, 0x4a, 0x7e, 0x25, 0x03, 0xa7, 0xe3, 0xb4, 0x48, 0xa1, 0x19, 0xc8, 0x35, 0xfb, 0x0c, 0xd8, 0x2a, 0x66, 0x65, 0xf0, 0x6e, 0x95, 0x34, 0xe7, 0x4c, 0xde, 0x6a, 0x44, 0x70, 0x4c, 0x28, 0x15, 0x72, 0x26, 0x6f, 0xbe, 0x92, 0x9e, 0xc9, 0x95, 0xb6, 0x2b, 0x47, 0xb1, 0x35, 0xb3, 0xaa, 0xe8, 0x06, 0xf3, 0x3c, 0xec, 0xca, 0xbf, 0x95, 0xf4, 0x74, 0x79, 0x9a, 0xce, 0xc4, 0x3f, 0x4d, 0x6f, 0xc2, 0x84, 0xa7, 0xa3, 0x9d, 0x3b, 0x50, 0x5f, 0xd4, 0x0e, 0x34, 0xe6, 0xf1, 0x06, 0x36, 0xa1, 0x80, 0x54, 0xb6, 0xc1, 0x31, 0xa9, 0xfd, 0x09, 0xa4, 0xba, 0x87, 0x68, 0x26, 0x55, 0xbc, 0x55, 0x66, 0xbb, 0xda, 0x2a, 0xd7, 0x61, 0x78, 0x07, 0x2b, 0x16, 0xd9, 0xc2, 0x4a, 0x0b, 0x1d, 0x44, 0x89, 0xca, 0x37, 0x79, 0x5a, 0x72, 0xa2, 0x03, 0x9c, 0x5c, 0x74, 0x80, 0xd3, 0x71, 0xd4, 0x1c, 0xe8, 0xe6, 0xa8, 0xd9, 0x3a, 0xb2, 0x1c, 0x89, 0x7d, 0x64, 0x29, 0xfc, 0x4d, 0x82, 0x42, 0x74, 0xbb, 0xde, 0x63, 0x0b, 0x0d, 0xda, 0x83, 0x98, 0x1e, 0xff, 0x79, 0xf9, 0x25, 0x18, 0xa0, 0xe9, 0x06, 0xcf, 0xad, 0xf5, 0xc6, 0x70, 0x6b, 0x39, 0x87, 0x83, 0x5d, 0x14, 0xfe, 0x20, 0xf9, 0x5d, 0xc1, 0x21, 0xc7, 0xe5, 0xfc, 0x29, 0x4a, 0x25, 0xd8, 0x0d, 0xd2, 0x91, 0xb1, 0x4a, 0x8f, 0x7f, 0x32, 0x0b, 0xbf, 0x97, 0xe0, 0x64, 0x74, 0x0f, 0x55, 0xb7, 0xe1, 0xfb, 0x87, 0x31, 0xa2, 0x9f, 0xa7, 0xe0, 0x54, 0x8c, 0x4e, 0x44, 0x67, 0x4c, 0x1a, 0x26, 0x8a, 0x5e, 0xb1, 0x63, 0x2d, 0x92, 0x47, 0xfc, 0xc8, 0xc6, 0x14, 0x8c, 0xaf, 0x7a, 0xba, 0x89, 0xaf, 0x0e, 0xac, 0xe2, 0x5f, 0x94, 0x60, 0x3e, 0x7e, 0x03, 0x61, 0x9c, 0x3d, 0xef, 0x70, 0x0e, 0x70, 0xef, 0x48, 0x90, 0xb0, 0x55, 0x30, 0x1a, 0xdb, 0xa8, 0x17, 0x25, 0xb1, 0x53, 0xb8, 0x1b, 0xf7, 0xc4, 0x41, 0x9c, 0x8e, 0x81, 0xf8, 0xad, 0x80, 0x1e, 0x8a, 0x8a, 0x8a, 0xdd, 0xea, 0xe1, 0x3a, 0xcc, 0x56, 0x14, 0xd2, 0xd6, 0x32, 0x13, 0x6c, 0x20, 0x69, 0xcd, 0xac, 0x4b, 0xc7, 0x5b, 0x4a, 0x37, 0xaa, 0xe2, 0xe8, 0x73, 0x3a, 0x81, 0x3e, 0xf7, 0x44, 0xda, 0x68, 0x20, 0x0e, 0x2c, 0xbc, 0x27, 0xc1, 0x54, 0x48, 0x93, 0x2e, 0x9a, 0x80, 0x7e, 0xb7, 0x39, 0xb1, 0xb9, 0x6e, 0x7d, 0xf4, 0xba, 0xa4, 0xa1, 0x0d, 0x38, 0xda, 0xdc, 0xc8, 0xb7, 0x75, 0x2b, 0xc1, 0x91, 0x17, 0xb1, 0x7d, 0x7c, 0x5d, 0xb7, 0x70, 0x92, 0xed, 0x37, 0xce, 0x62, 0x7f, 0x0c, 0x26, 0x84, 0xdd, 0xbf, 0x61, 0xa3, 0x89, 0x1d, 0xd2, 0x17, 0xde, 0x95, 0x60, 0x3a, 0xac, 0xf1, 0xf3, 0x50, 0xde, 0x72, 0x58, 0xf3, 0x11, 0xea, 0xa0, 0x7f, 0x2c, 0xc1, 0x6c, 0x54, 0x03, 0x69, 0xd8, 0x68, 0x1e, 0xa9, 0xd9, 0x86, 0x6f, 0x96, 0x59, 0x48, 0xd8, 0xa7, 0x84, 0x16, 0x61, 0x94, 0xb6, 0x42, 0x05, 0xab, 0x06, 0xee, 0x98, 0x86, 0x0d, 0xdc, 0x08, 0xd4, 0x0c, 0x3a, 0x0a, 0x77, 0xa9, 0xee, 0x0a, 0x77, 0x4f, 0x4a, 0x6b, 0xf1, 0x4b, 0x6b, 0x71, 0x74, 0xa7, 0x2f, 0x86, 0xee, 0xdc, 0x86, 0x31, 0x56, 0x12, 0x61, 0x18, 0x75, 0x83, 0x60, 0x6b, 0x4f, 0xa9, 0x44, 0x9f, 0x5b, 0x46, 0x19, 0x23, 0x85, 0x57, 0x62, 0x6c, 0xfe, 0xb2, 0x5d, 0xf6, 0x40, 0x65, 0xbb, 0xb6, 0x10, 0x0e, 0x92, 0x84, 0x70, 0xe2, 0x1a, 0x5d, 0xae, 0xeb, 0x1a, 0x5d, 0xeb, 0x9c, 0x31, 0x10, 0xbf, 0x34, 0xe2, 0x55, 0x8a, 0x8e, 0x1c, 0xa0, 0x52, 0x34, 0x78, 0xb0, 0x4a, 0x91, 0xa0, 0x64, 0x31, 0xf4, 0x18, 0x4a, 0x16, 0xf9, 0x47, 0x52, 0xb2, 0x28, 0xfc, 0x55, 0x82, 0xc5, 0xa4, 0xed, 0x9f, 0x4d, 0xff, 0x2b, 0xb5, 0xfb, 0xdf, 0xb0, 0x13, 0xdb, 0x16, 0x1c, 0x6b, 0xb6, 0x8c, 0x04, 0xda, 0x08, 0x5c, 0xcf, 0x34, 0x1f, 0xda, 0x14, 0xe2, 0x6f, 0x24, 0x38, 0x8a, 0x79, 0xb7, 0x03, 0xa7, 0xc2, 0x9e, 0x60, 0x16, 0xe7, 0xdb, 0x12, 0xa7, 0x02, 0x20, 0xda, 0x4a, 0xe3, 0xf8, 0x03, 0x29, 0x86, 0x3f, 0x68, 0x0b, 0xed, 0x52, 0x09, 0x42, 0xbb, 0xc2, 0x07, 0x12, 0x1c, 0x0f, 0xfd, 0xba, 0xc1, 0x89, 0x6d, 0xd9, 0xb7, 0x13, 0x86, 0x52, 0xf5, 0x56, 0x02, 0xdc, 0x5b, 0xb7, 0x94, 0x2a, 0xee, 0xf6, 0xd5, 0x87, 0xb6, 0x8d, 0xb6, 0x4c, 0xbc, 0x27, 0x7e, 0x2a, 0xe1, 0x67, 0xbc, 0x45, 0x12, 0x75, 0xf3, 0xcc, 0x40, 0x8e, 0xf5, 0x53, 0xb5, 0x4f, 0x81, 0x7b, 0x8b, 0x4e, 0x41, 0x73, 0x17, 0x4b, 0xc5, 0xdf, 0xc5, 0xc2, 0xd2, 0xfa, 0x11, 0x1a, 0xf6, 0x25, 0x09, 0xe6, 0x13, 0x34, 0xb8, 0xb5, 0xb2, 0xd3, 0x92, 0x2f, 0x3b, 0xdd, 0xed, 0xc2, 0x85, 0x20, 0x2f, 0xfc, 0x32, 0x05, 0x2f, 0x1e, 0xac, 0xc9, 0xff, 0xd0, 0x4c, 0xa2, 0x95, 0xbb, 0x4c, 0xf9, 0x72, 0x97, 0xf7, 0x00, 0x75, 0x36, 0x93, 0x31, 0xef, 0x70, 0x26, 0x5e, 0xb1, 0x5a, 0x1e, 0xee, 0xe8, 0x08, 0x47, 0xe3, 0xd0, 0xa7, 0x9a, 0x06, 0xb1, 0xcc, 0x0a, 0x5d, 0xb0, 0x01, 0xd9, 0xbb, 0x44, 0x45, 0x18, 0x09, 0xf4, 0x45, 0x9a, 0x46, 0xc5, 0x3d, 0xa9, 0xf4, 0xcb, 0xc3, 0xbe, 0x76, 0xc5, 0xdb, 0x46, 0x65, 0xbf, 0xf0, 0x66, 0x1a, 0x6e, 0x1c, 0xe0, 0x23, 0x02, 0x74, 0xaf, 0xdd, 0x6b, 0x0e, 0x0a, 0x3e, 0xd1, 0x89, 0x25, 0xd9, 0x97, 0xa5, 0x3f, 0xa4, 0xf3, 0xb5, 0x30, 0xa7, 0xcc, 0x5f, 0x97, 0x9e, 0x83, 0xae, 0xcb, 0x02, 0xa0, 0x60, 0xeb, 0x26, 0xab, 0xf7, 0xa4, 0xe5, 0xbc, 0xee, 0x53, 0x42, 0x37, 0xa5, 0xe7, 0xad, 0x62, 0xc6, 0xb7, 0x8a, 0x85, 0x3f, 0x4a, 0x70, 0xb5, 0xcb, 0x2f, 0x20, 0x04, 0x18, 0x24, 0x01, 0x86, 0xc7, 0xab, 0xb8, 0x85, 0xcf, 0xa7, 0xe1, 0x6a, 0x97, 0x5d, 0xaa, 0xff, 0xa9, 0xb6, 0x1a, 0x70, 0xe8, 0x3d, 0x62, 0x87, 0xde, 0x1b, 0xdf, 0xa1, 0x0b, 0x55, 0x47, 0xe4, 0x00, 0xfa, 0x44, 0x0e, 0xe0, 0x73, 0x69, 0xb8, 0xdc, 0x4d, 0xa7, 0x6d, 0x3c, 0xcb, 0x8f, 0x25, 0xf9, 0x89, 0xe5, 0xb7, 0x2c, 0xff, 0x7d, 0x09, 0x2e, 0x24, 0xed, 0x1a, 0xfe, 0xb7, 0x36, 0x79, 0xf1, 0x5e, 0x55, 0xf8, 0xad, 0x04, 0xe7, 0x13, 0x75, 0x1a, 0x1f, 0x9a, 0x0b, 0xe0, 0x9e, 0xa2, 0x52, 0x07, 0x3a, 0x45, 0x15, 0xbe, 0x97, 0x83, 0x4b, 0x5d, 0x7c, 0x32, 0xd5, 0xb6, 0x1c, 0x92, 0x6f, 0x39, 0x66, 0x20, 0xd7, 0x5c, 0x0e, 0xa6, 0xf3, 0x59, 0x19, 0xbc, 0x5b, 0xbc, 0x94, 0x4a, 0xfa, 0x10, 0x52, 0x2a, 0xdd, 0xd6, 0x57, 0x7b, 0x0f, 0x37, 0xa5, 0x92, 0x79, 0xa4, 0x29, 0x95, 0xbe, 0xae, 0x53, 0x2a, 0xf7, 0x81, 0x35, 0x7c, 0x33, 0x89, 0xec, 0x18, 0xdb, 0x1f, 0x72, 0x54, 0x76, 0xbb, 0xc6, 0xa9, 0x14, 0xef, 0xa8, 0x5c, 0x0b, 0xde, 0x6a, 0x37, 0x92, 0xac, 0xdf, 0x9f, 0xc7, 0x51, 0x79, 0x88, 0xa1, 0xf2, 0x2a, 0x8c, 0xb7, 0xa9, 0x53, 0xd9, 0xc2, 0xf5, 0x16, 0xfc, 0x1c, 0x85, 0x3f, 0x1f, 0xaa, 0x38, 0x25, 0x4d, 0x76, 0x58, 0xd8, 0x10, 0x8e, 0x36, 0x78, 0xb7, 0x3b, 0xca, 0xb5, 0x47, 0xba, 0x29, 0xd7, 0x76, 0xb4, 0xee, 0x0e, 0x72, 0x5a, 0x77, 0x5b, 0x07, 0xb1, 0xa1, 0xe4, 0xb9, 0x96, 0xfc, 0x01, 0x72, 0x2d, 0xc3, 0x07, 0xcb, 0xb5, 0x5c, 0x87, 0x9c, 0x86, 0x2b, 0xca, 0xbe, 0xab, 0x9a, 0xd1, 0x2d, 0xc6, 0x40, 0xa9, 0xa9, 0x2a, 0xa2, 0xe7, 0x61, 0xe0, 0xe3, 0x3a, 0x21, 0xde, 0xdf, 0x87, 0x34, 0x9b, 0x8b, 0x85, 0xcc, 0x39, 0x97, 0xbc, 0xc9, 0xed, 0xf6, 0xe0, 0x5a, 0x75, 0xa3, 0xac, 0x10, 0xd6, 0x5e, 0x1c, 0xd6, 0x7b, 0x0b, 0x94, 0x5e, 0xae, 0x1b, 0xcb, 0x44, 0x94, 0x23, 0x3a, 0xfa, 0x18, 0x72, 0x44, 0x63, 0x8f, 0x26, 0x47, 0xf4, 0x46, 0x1a, 0x2e, 0x24, 0xfd, 0x40, 0xf4, 0xc3, 0x77, 0xd6, 0x1b, 0x5e, 0xd4, 0xe5, 0xd6, 0x51, 0xaf, 0x24, 0xfe, 0xba, 0xd1, 0x17, 0x6c, 0xb5, 0xb9, 0x9d, 0x5e, 0xbf, 0xdb, 0xe1, 0x87, 0x14, 0x19, 0x41, 0x48, 0x71, 0x48, 0x99, 0xe6, 0xc2, 0x6f, 0x52, 0xb0, 0x90, 0xe4, 0xeb, 0x57, 0xe1, 0x7a, 0xf0, 0x63, 0x99, 0xd4, 0x41, 0x63, 0x99, 0xc3, 0x5a, 0x45, 0xfe, 0xec, 0xf6, 0x08, 0x66, 0xb7, 0xe5, 0xeb, 0x7a, 0xe3, 0x27, 0x9d, 0x3e, 0x48, 0x41, 0xc2, 0xef, 0x72, 0x3f, 0x1a, 0x93, 0xc9, 0x2b, 0x1a, 0xf6, 0x72, 0x8b, 0x86, 0xad, 0x6e, 0x97, 0x4c, 0xfc, 0x6e, 0x97, 0xc2, 0x3f, 0x52, 0x70, 0xee, 0x30, 0x3c, 0xca, 0x47, 0x74, 0xd2, 0xdb, 0xea, 0x39, 0x99, 0x04, 0xf5, 0x9c, 0xc2, 0x3f, 0x53, 0x70, 0x3e, 0xd1, 0x67, 0xd2, 0x4f, 0x26, 0xbe, 0x63, 0xe2, 0xbd, 0x04, 0x6d, 0x26, 0x49, 0x52, 0xff, 0xd3, 0x69, 0xd1, 0xc4, 0x8b, 0x3a, 0x94, 0x9e, 0x4c, 0x7c, 0x68, 0x83, 0x54, 0xa6, 0x9b, 0xef, 0x32, 0x7e, 0x91, 0x82, 0xc5, 0x84, 0x9f, 0xaf, 0x3f, 0x59, 0x07, 0xdf, 0x3a, 0xcc, 0x13, 0x18, 0xa2, 0x3f, 0xd7, 0xf5, 0x0a, 0xc1, 0x16, 0x7d, 0xd5, 0x71, 0x98, 0x58, 0xbb, 0xbf, 0x76, 0x6b, 0xb3, 0xbc, 0x5e, 0xda, 0xd8, 0x5c, 0x93, 0xcb, 0x9b, 0xff, 0x77, 0x67, 0xad, 0x5c, 0xba, 0x75, 0x7f, 0x79, 0xa3, 0x74, 0x33, 0xff, 0x14, 0x9a, 0x81, 0xa9, 0xce, 0xc7, 0xcb, 0x1b, 0x1b, 0x65, 0x7a, 0x37, 0x2f, 0xa1, 0x93, 0x70, 0xbc, 0x93, 0x60, 0x75, 0xe3, 0xf6, 0xdd, 0x35, 0x46, 0x92, 0x5a, 0x79, 0x15, 0x8e, 0xa9, 0x66, 0x95, 0x37, 0x07, 0x2b, 0xde, 0x1f, 0x20, 0xdf, 0x71, 0x22, 0xf3, 0x3b, 0xd2, 0xff, 0x5f, 0x7c, 0xa0, 0x93, 0x9d, 0xfa, 0x56, 0x51, 0x35, 0xab, 0x8b, 0xed, 0x7f, 0xc4, 0x7c, 0x5e, 0xd7, 0x2a, 0x8b, 0x0f, 0x4c, 0xf7, 0xcf, 0x9f, 0xd9, 0xbf, 0x32, 0xdf, 0x50, 0x6a, 0xfa, 0xde, 0xc5, 0xad, 0x0c, 0xbd, 0x77, 0xe9, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf3, 0xc4, 0x77, 0x00, 0x78, 0x5a, 0x00, 0x00, }, // google/protobuf/duration.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a, 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0x56, 0x5c, 0x1c, 0x2e, 0x50, 0x25, 0x42, 0x12, 0x5c, 0xec, 0xc5, 0xa9, 0xc9, 0xf9, 0x79, 0x29, 0xc5, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x30, 0xae, 0x90, 0x08, 0x17, 0x6b, 0x5e, 0x62, 0x5e, 0x7e, 0xb1, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x84, 0xe3, 0xd4, 0xcc, 0xc8, 0x25, 0x9c, 0x9c, 0x9f, 0xab, 0x87, 0x66, 0xa6, 0x13, 0x2f, 0xcc, 0xc4, 0x00, 0x90, 0x48, 0x00, 0x63, 0x94, 0x21, 0x54, 0x45, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x5e, 0x7e, 0x51, 0x3a, 0xc2, 0x81, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0xfa, 0xd9, 0x79, 0xf9, 0xe5, 0x79, 0x70, 0xc7, 0x16, 0x24, 0xfd, 0x60, 0x64, 0x5c, 0xc4, 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0xa2, 0x39, 0x00, 0xaa, 0x43, 0x2f, 0x3c, 0x35, 0x27, 0xc7, 0x1b, 0xa4, 0x3e, 0x04, 0xa4, 0x35, 0x89, 0x0d, 0x6c, 0x94, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xef, 0x8a, 0xb4, 0xc3, 0xfb, 0x00, 0x00, 0x00, }, // google/protobuf/timestamp.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0xd0, 0x03, 0x0b, 0x09, 0xf1, 0x43, 0x14, 0xe8, 0xc1, 0x14, 0x28, 0x59, 0x73, 0x71, 0x86, 0xc0, 0xd4, 0x08, 0x49, 0x70, 0xb1, 0x17, 0xa7, 0x26, 0xe7, 0xe7, 0xa5, 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x30, 0x07, 0xc1, 0xb8, 0x42, 0x22, 0x5c, 0xac, 0x79, 0x89, 0x79, 0xf9, 0xc5, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xac, 0x41, 0x10, 0x8e, 0x53, 0x2b, 0x23, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0xa1, 0x4e, 0x7c, 0x70, 0x23, 0x03, 0x40, 0x42, 0x01, 0x8c, 0x51, 0x46, 0x50, 0x25, 0xe9, 0xf9, 0x39, 0x89, 0x79, 0xe9, 0x7a, 0xf9, 0x45, 0xe9, 0x48, 0x6e, 0xac, 0x2c, 0x48, 0x2d, 0xd6, 0xcf, 0xce, 0xcb, 0x2f, 0xcf, 0x43, 0xb8, 0xb7, 0x20, 0xe9, 0x07, 0x23, 0xe3, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55, 0x4c, 0x72, 0xee, 0x10, 0xdd, 0x01, 0x50, 0x2d, 0x7a, 0xe1, 0xa9, 0x39, 0x39, 0xde, 0x20, 0x0d, 0x21, 0x20, 0xbd, 0x49, 0x6c, 0x60, 0xb3, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xae, 0x65, 0xce, 0x7d, 0xff, 0x00, 0x00, 0x00, }, // uber/cadence/api/v1/common.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdd, 0x72, 0x22, 0xc7, 0x15, 0xf6, 0xc0, 0xa2, 0x9f, 0x03, 0xbb, 0x42, 0xad, 0xfd, 0x61, 0xb5, 0x5e, 0xaf, 0x16, 0x97, 0x63, 0x79, 0x2b, 0x86, 0x88, 0x4d, 0x52, 0x2e, 0x3b, 0x4e, 0x82, 0xd0, 0x48, 0x9a, 0x5d, 0x02, 0xa4, 0x99, 0x95, 0xac, 0xa4, 0xca, 0x53, 0xcd, 0x4c, 0x83, 0x3b, 0x0c, 0xd3, 0x93, 0x99, 0x1e, 0x56, 0xf8, 0x22, 0x95, 0xcb, 0xe4, 0x26, 0x8f, 0x90, 0x8b, 0xbc, 0x48, 0x1e, 0x20, 0x97, 0x79, 0x97, 0x5c, 0xa7, 0xba, 0xa7, 0x07, 0x81, 0xc2, 0x1a, 0x5f, 0xa4, 0x7c, 0x47, 0x9f, 0xf3, 0x7d, 0xa7, 0xbf, 0xd3, 0xdd, 0xe7, 0x1c, 0x06, 0x0e, 0x92, 0x01, 0x8d, 0xea, 0x2e, 0xf1, 0x68, 0xe0, 0xd2, 0x3a, 0x09, 0x59, 0x7d, 0x7a, 0x54, 0x77, 0xf9, 0x64, 0xc2, 0x83, 0x5a, 0x18, 0x71, 0xc1, 0xd1, 0x9e, 0x44, 0xd4, 0x34, 0xa2, 0x46, 0x42, 0x56, 0x9b, 0x1e, 0xed, 0x7f, 0x30, 0xe2, 0x7c, 0xe4, 0xd3, 0xba, 0x82, 0x0c, 0x92, 0x61, 0xdd, 0x4b, 0x22, 0x22, 0x58, 0x46, 0xaa, 0xbe, 0x86, 0xdd, 0x4b, 0x1e, 0x8d, 0x87, 0x3e, 0x7f, 0x6b, 0x5e, 0x53, 0x37, 0x91, 0x2e, 0xf4, 0x0c, 0x8a, 0x6f, 0xb5, 0xd1, 0x61, 0x5e, 0xc5, 0x38, 0x30, 0x0e, 0xb7, 0x31, 0x64, 0x26, 0xcb, 0x43, 0x0f, 0x60, 0x23, 0x4a, 0x02, 0xe9, 0xcb, 0x29, 0x5f, 0x21, 0x4a, 0x02, 0xcb, 0xab, 0x56, 0xa1, 0x94, 0x05, 0xb3, 0x67, 0x21, 0x45, 0x08, 0xee, 0x04, 0x64, 0x42, 0x75, 0x00, 0xf5, 0x5b, 0x62, 0x9a, 0xae, 0x60, 0x53, 0x26, 0x66, 0xef, 0xc4, 0x3c, 0x85, 0xcd, 0x1e, 0x99, 0xf9, 0x9c, 0x78, 0xd2, 0xed, 0x11, 0x41, 0x94, 0xbb, 0x84, 0xd5, 0xef, 0xea, 0x17, 0xb0, 0x79, 0x4a, 0x98, 0x9f, 0x44, 0x14, 0x3d, 0x84, 0x8d, 0x88, 0x92, 0x98, 0x07, 0x9a, 0xaf, 0x57, 0xa8, 0x02, 0x9b, 0x1e, 0x15, 0x84, 0xf9, 0xb1, 0x52, 0x58, 0xc2, 0xd9, 0xb2, 0xfa, 0x77, 0x03, 0xee, 0xfc, 0x86, 0x4e, 0x38, 0xfa, 0x12, 0x36, 0x86, 0x8c, 0xfa, 0x5e, 0x5c, 0x31, 0x0e, 0xf2, 0x87, 0xc5, 0xc6, 0x47, 0xb5, 0x15, 0xe7, 0x57, 0x93, 0xd0, 0xda, 0xa9, 0xc2, 0x99, 0x81, 0x88, 0x66, 0x58, 0x93, 0xf6, 0x2f, 0xa1, 0xb8, 0x60, 0x46, 0x65, 0xc8, 0x8f, 0xe9, 0x4c, 0xab, 0x90, 0x3f, 0x51, 0x03, 0x0a, 0x53, 0xe2, 0x27, 0x54, 0x09, 0x28, 0x36, 0xde, 0x5f, 0x19, 0x5e, 0xa7, 0x89, 0x53, 0xe8, 0xe7, 0xb9, 0xcf, 0x8c, 0xea, 0x3f, 0x0c, 0xd8, 0x38, 0xa7, 0xc4, 0xa3, 0x11, 0xfa, 0xd5, 0x2d, 0x89, 0x1f, 0xaf, 0x8c, 0x91, 0x82, 0x7f, 0x58, 0x91, 0xff, 0x36, 0xa0, 0xdc, 0xa7, 0x24, 0x72, 0xbf, 0x69, 0x0a, 0x11, 0xb1, 0x41, 0x22, 0x68, 0x8c, 0x1c, 0xb8, 0xc7, 0x02, 0x8f, 0x5e, 0x53, 0xcf, 0x59, 0x92, 0xfd, 0xd9, 0xca, 0xa8, 0xb7, 0xe9, 0x35, 0x2b, 0xe5, 0x2e, 0xe6, 0x71, 0x97, 0x2d, 0xda, 0xf6, 0xbf, 0x06, 0xf4, 0xbf, 0xa0, 0xff, 0x63, 0x56, 0x43, 0xd8, 0x3a, 0x21, 0x82, 0x1c, 0xfb, 0x7c, 0x80, 0x4e, 0xe1, 0x2e, 0x0d, 0x5c, 0xee, 0xb1, 0x60, 0xe4, 0x88, 0x59, 0x98, 0x3e, 0xd0, 0x7b, 0x8d, 0xe7, 0x2b, 0x63, 0x99, 0x1a, 0x29, 0x5f, 0x34, 0x2e, 0xd1, 0x85, 0xd5, 0xfc, 0x01, 0xe7, 0x16, 0x1e, 0x70, 0x2f, 0x2d, 0x3a, 0x1a, 0x5d, 0xd0, 0x28, 0x66, 0x3c, 0xb0, 0x82, 0x21, 0x97, 0x40, 0x36, 0x09, 0xfd, 0xac, 0x10, 0xe4, 0x6f, 0xf4, 0x31, 0xec, 0x0c, 0x29, 0x11, 0x49, 0x44, 0x9d, 0x69, 0x0a, 0xd5, 0x05, 0x77, 0x4f, 0x9b, 0x75, 0x80, 0xea, 0x6b, 0x78, 0xd4, 0x4f, 0xc2, 0x90, 0x47, 0x82, 0x7a, 0x2d, 0x9f, 0xd1, 0x40, 0x68, 0x4f, 0x2c, 0x6b, 0x75, 0xc4, 0x9d, 0xd8, 0x1b, 0xeb, 0xc8, 0x85, 0x11, 0xef, 0x7b, 0x63, 0xf4, 0x18, 0xb6, 0xfe, 0x40, 0xa6, 0x44, 0x39, 0xd2, 0x98, 0x9b, 0x72, 0xdd, 0xf7, 0xc6, 0xd5, 0x3f, 0xe7, 0xa1, 0x88, 0xa9, 0x88, 0x66, 0x3d, 0xee, 0x33, 0x77, 0x86, 0x4e, 0xa0, 0xcc, 0x02, 0x26, 0x18, 0xf1, 0x1d, 0x16, 0x08, 0x1a, 0x4d, 0x49, 0xaa, 0xb2, 0xd8, 0x78, 0x5c, 0x4b, 0xdb, 0x4b, 0x2d, 0x6b, 0x2f, 0xb5, 0x13, 0xdd, 0x5e, 0xf0, 0x8e, 0xa6, 0x58, 0x9a, 0x81, 0xea, 0xb0, 0x37, 0x20, 0xee, 0x98, 0x0f, 0x87, 0x8e, 0xcb, 0xe9, 0x70, 0xc8, 0x5c, 0x29, 0x53, 0xed, 0x6d, 0x60, 0xa4, 0x5d, 0xad, 0x1b, 0x8f, 0xdc, 0x76, 0x42, 0xae, 0xd9, 0x24, 0x99, 0xdc, 0x6c, 0x9b, 0x5f, 0xbb, 0xad, 0xa6, 0xcc, 0xb7, 0xfd, 0xe4, 0x26, 0x0a, 0x11, 0x82, 0x4e, 0x42, 0x11, 0x57, 0xee, 0x1c, 0x18, 0x87, 0x85, 0x39, 0xb4, 0xa9, 0xcd, 0xe8, 0x4b, 0x78, 0x12, 0xf0, 0xc0, 0x89, 0x64, 0xea, 0x64, 0xe0, 0x53, 0x87, 0x46, 0x11, 0x8f, 0x9c, 0xb4, 0xa5, 0xc4, 0x95, 0xc2, 0x41, 0xfe, 0x70, 0x1b, 0x57, 0x02, 0x1e, 0xe0, 0x0c, 0x61, 0x4a, 0x00, 0x4e, 0xfd, 0xe8, 0x15, 0xec, 0xd1, 0xeb, 0x90, 0xa5, 0x42, 0x6e, 0x24, 0x6f, 0xac, 0x93, 0x8c, 0x6e, 0x58, 0x99, 0xea, 0xea, 0x04, 0x1e, 0x59, 0x31, 0xf7, 0x95, 0xf1, 0x2c, 0xe2, 0x49, 0xd8, 0x23, 0x91, 0x60, 0xaa, 0x39, 0xaf, 0x68, 0x98, 0xe8, 0x97, 0x50, 0x88, 0x05, 0x11, 0xe9, 0x83, 0xbf, 0xd7, 0x38, 0x5c, 0xf9, 0x48, 0x97, 0x03, 0xf6, 0x25, 0x1e, 0xa7, 0xb4, 0xea, 0x14, 0x9e, 0x2c, 0x7b, 0x5b, 0x3c, 0x18, 0xb2, 0x91, 0x56, 0x88, 0x2e, 0xa1, 0xcc, 0x32, 0xb7, 0x33, 0x92, 0xfe, 0xac, 0xb4, 0x7f, 0xfc, 0x3d, 0x76, 0x9a, 0x4b, 0xc7, 0x3b, 0x6c, 0xc9, 0x11, 0x57, 0xff, 0x65, 0xc0, 0x7e, 0x33, 0x9e, 0x05, 0x6e, 0x36, 0x36, 0x96, 0xf7, 0xad, 0xc0, 0x26, 0x0d, 0xe4, 0x39, 0xa7, 0x33, 0x68, 0x0b, 0x67, 0x4b, 0xd4, 0x80, 0x07, 0x61, 0x44, 0x3d, 0x3a, 0x64, 0x01, 0xf5, 0x9c, 0x3f, 0x26, 0x34, 0xa1, 0x8e, 0x3a, 0x95, 0xf4, 0x29, 0xef, 0xdd, 0x38, 0x7f, 0x2b, 0x7d, 0x1d, 0x79, 0x48, 0x4f, 0x01, 0x52, 0xa0, 0x2a, 0xe7, 0xbc, 0x02, 0x6e, 0x2b, 0x8b, 0x2a, 0xd4, 0x5f, 0x43, 0x29, 0x75, 0xbb, 0x4a, 0x83, 0x7a, 0x24, 0xc5, 0xc6, 0xd3, 0x95, 0x09, 0x66, 0x5d, 0x02, 0x17, 0x15, 0x25, 0x55, 0x5d, 0xfd, 0x4f, 0x1e, 0xde, 0x57, 0xb3, 0x8d, 0xb6, 0xfc, 0x24, 0x16, 0x34, 0xea, 0x53, 0x9f, 0xba, 0x32, 0x13, 0x5d, 0x48, 0x7d, 0xd8, 0x8a, 0x45, 0x44, 0x04, 0x1d, 0xcd, 0x74, 0x3b, 0x79, 0xb9, 0x32, 0xfc, 0xea, 0x20, 0x7d, 0x4d, 0x3d, 0xce, 0x55, 0x0c, 0x3c, 0x0f, 0x84, 0xfe, 0x62, 0xc0, 0x87, 0x44, 0x11, 0x1c, 0x37, 0x65, 0x38, 0xb1, 0x60, 0xee, 0x78, 0xe6, 0x44, 0x74, 0x24, 0x2f, 0x4c, 0xe7, 0x93, 0xf6, 0xc2, 0x9f, 0x7e, 0x8f, 0x0d, 0x15, 0x1b, 0x2b, 0x72, 0x9a, 0x99, 0xdc, 0xf1, 0xfc, 0x3d, 0xfc, 0x8c, 0x7c, 0x37, 0x0c, 0xfd, 0xcd, 0x80, 0x8f, 0x6e, 0x49, 0xa1, 0xd7, 0x82, 0x46, 0x01, 0xf1, 0x1d, 0x1a, 0x08, 0x26, 0x66, 0x99, 0x98, 0xb4, 0x8e, 0x7f, 0xbe, 0x5e, 0x8c, 0xa9, 0xf9, 0xa6, 0xa2, 0x2f, 0xc9, 0x79, 0x4e, 0xd6, 0x01, 0x11, 0x86, 0xdd, 0x4c, 0x08, 0xc9, 0x06, 0x8d, 0xbe, 0xd8, 0xd5, 0xe3, 0x5e, 0x07, 0x9b, 0x4f, 0x25, 0x5c, 0x76, 0x6f, 0x59, 0x8e, 0x77, 0x61, 0x27, 0x3b, 0x7b, 0x9d, 0x4d, 0xf5, 0x17, 0x50, 0xbe, 0x4d, 0x44, 0xf7, 0xa1, 0x10, 0xbb, 0x3c, 0xcc, 0xea, 0x34, 0x5d, 0xcc, 0x8b, 0x37, 0xb7, 0xf0, 0x6f, 0xe7, 0x15, 0x3c, 0x5b, 0x73, 0xfe, 0xe8, 0x43, 0xb8, 0xbb, 0x74, 0xa7, 0x3a, 0x68, 0x29, 0x5e, 0x80, 0x7e, 0x9e, 0xab, 0x18, 0xd5, 0xbf, 0x1a, 0xf0, 0x7c, 0xed, 0xf9, 0xa1, 0x9f, 0xc0, 0xfd, 0xdb, 0xf7, 0x32, 0x1f, 0x71, 0xdb, 0xb2, 0x1f, 0x2d, 0x72, 0x54, 0x71, 0xd4, 0x64, 0x6f, 0x5b, 0x66, 0xc8, 0x99, 0x9b, 0xa6, 0xb1, 0xbb, 0x4c, 0x78, 0x4d, 0x67, 0x4a, 0xcb, 0x57, 0xb0, 0xdb, 0x23, 0x23, 0x16, 0xa8, 0x5a, 0xee, 0x86, 0x42, 0x4d, 0xa3, 0x27, 0xb0, 0x1d, 0x92, 0x11, 0x75, 0x62, 0xf6, 0x6d, 0xba, 0x5f, 0x01, 0x6f, 0x49, 0x43, 0x9f, 0x7d, 0x4b, 0xd1, 0x8f, 0x60, 0x27, 0xa0, 0xd7, 0xc2, 0x51, 0x08, 0xc1, 0xc7, 0x34, 0xd0, 0x63, 0xf3, 0xae, 0x34, 0xf7, 0xc8, 0x88, 0xda, 0xd2, 0xf8, 0xe2, 0x2d, 0x94, 0x16, 0x27, 0x2e, 0x7a, 0x0c, 0x0f, 0xcc, 0x4e, 0xab, 0x7b, 0x62, 0x75, 0xce, 0x1c, 0xfb, 0xaa, 0x67, 0x3a, 0x56, 0xe7, 0xa2, 0xd9, 0xb6, 0x4e, 0xca, 0xef, 0xa1, 0x7d, 0x78, 0xb8, 0xec, 0xb2, 0xcf, 0xb1, 0x75, 0x6a, 0xe3, 0xcb, 0xb2, 0x81, 0x1e, 0x02, 0x5a, 0xf6, 0xbd, 0xea, 0x77, 0x3b, 0xe5, 0x1c, 0xaa, 0xc0, 0xfd, 0x65, 0x7b, 0x0f, 0x77, 0xed, 0xee, 0xcb, 0x72, 0xfe, 0xc5, 0x9f, 0x60, 0x6f, 0x45, 0x17, 0x45, 0xcf, 0xe1, 0xa9, 0xd5, 0xef, 0xb6, 0x9b, 0xb6, 0xd5, 0xed, 0x38, 0x67, 0xb8, 0xfb, 0xa6, 0xe7, 0xf4, 0xed, 0xa6, 0xbd, 0xa8, 0xe3, 0x9d, 0x90, 0x73, 0xb3, 0xd9, 0xb6, 0xcf, 0xaf, 0xca, 0xc6, 0xbb, 0x21, 0x27, 0xb8, 0x69, 0x75, 0xcc, 0x93, 0x72, 0xee, 0xc5, 0x3f, 0x0d, 0xf8, 0xe0, 0xbb, 0x9b, 0x03, 0xfa, 0x14, 0x3e, 0x69, 0xb6, 0x6c, 0xeb, 0xc2, 0x74, 0x5a, 0xed, 0x37, 0x7d, 0xdb, 0xc4, 0x4e, 0xdf, 0x6c, 0x9b, 0x2d, 0x15, 0xb4, 0x6f, 0xe3, 0xa6, 0x6d, 0x9e, 0x5d, 0x2d, 0xe8, 0x7a, 0x09, 0xf5, 0xf5, 0x70, 0x6c, 0x9e, 0xa5, 0x6b, 0xab, 0xf5, 0x5a, 0x2a, 0xfd, 0x19, 0x1c, 0xad, 0x27, 0x99, 0x5f, 0xd9, 0x26, 0xee, 0x34, 0xdb, 0x8e, 0xd9, 0xb1, 0x2d, 0xfb, 0xaa, 0x9c, 0xdb, 0xcf, 0x55, 0x8c, 0x17, 0x5f, 0x43, 0x49, 0xfe, 0x77, 0xe7, 0x53, 0x1a, 0x65, 0x57, 0x77, 0xda, 0xb4, 0xda, 0xdd, 0x0b, 0x13, 0xdf, 0xbe, 0xba, 0x47, 0xb0, 0xb7, 0xec, 0x3a, 0xed, 0xe2, 0x96, 0x59, 0x36, 0xe4, 0x9d, 0x2e, 0x3b, 0xce, 0x70, 0xb3, 0x65, 0x9e, 0xbe, 0x69, 0x97, 0x73, 0xc7, 0xbf, 0x87, 0x47, 0x2e, 0x9f, 0xac, 0xaa, 0xed, 0xe3, 0x62, 0x4b, 0x7d, 0x2d, 0xf5, 0xe4, 0x00, 0xee, 0x19, 0xbf, 0x3b, 0x1a, 0x31, 0xf1, 0x4d, 0x32, 0xa8, 0xb9, 0x7c, 0x52, 0x5f, 0xfc, 0xb6, 0xfa, 0x94, 0x79, 0x7e, 0x7d, 0xc4, 0xd3, 0x2f, 0x26, 0xfd, 0xa1, 0xf5, 0x05, 0x09, 0xd9, 0xf4, 0x68, 0xb0, 0xa1, 0x6c, 0x2f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x5e, 0xe2, 0xc9, 0x06, 0x8c, 0x0d, 0x00, 0x00, }, // uber/cadence/api/v1/tasklist.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xdb, 0x6e, 0xdb, 0x36, 0x18, 0x9e, 0x7c, 0x68, 0x9d, 0xdf, 0x4d, 0xa2, 0xb2, 0x49, 0x63, 0xbb, 0xed, 0xe6, 0xfa, 0xa2, 0xc8, 0x8a, 0x4d, 0x46, 0xb2, 0x0d, 0x18, 0xb6, 0xa1, 0xab, 0x13, 0x1b, 0xad, 0x10, 0x27, 0x35, 0x64, 0xb5, 0x43, 0x07, 0x0c, 0x02, 0x2d, 0xb1, 0x0e, 0x67, 0x49, 0x14, 0x44, 0xca, 0xae, 0x6f, 0xf6, 0x18, 0xbb, 0xdb, 0x8b, 0xec, 0x1d, 0xf6, 0x4e, 0x03, 0x29, 0xd9, 0xf5, 0x41, 0x0d, 0xd6, 0x8b, 0xdd, 0x99, 0xff, 0xc7, 0x8f, 0xdf, 0x7f, 0xb6, 0xa0, 0x95, 0x8c, 0x48, 0xdc, 0x76, 0xb1, 0x47, 0x42, 0x97, 0xb4, 0x71, 0x44, 0xdb, 0xd3, 0x93, 0xb6, 0xc0, 0x7c, 0xe2, 0x53, 0x2e, 0x8c, 0x28, 0x66, 0x82, 0xa1, 0x7b, 0xf2, 0x8e, 0x91, 0xdd, 0x31, 0x70, 0x44, 0x8d, 0xe9, 0x49, 0xe3, 0xf3, 0x31, 0x63, 0x63, 0x9f, 0xb4, 0xd5, 0x95, 0x51, 0xf2, 0xae, 0xed, 0x25, 0x31, 0x16, 0x94, 0x85, 0x29, 0xa9, 0xf1, 0xc5, 0x26, 0x2e, 0x68, 0x40, 0xb8, 0xc0, 0x41, 0x94, 0x5d, 0xd8, 0x7a, 0x60, 0x16, 0xe3, 0x28, 0x22, 0x31, 0x4f, 0xf1, 0xd6, 0x6b, 0xa8, 0xd8, 0x98, 0x4f, 0xfa, 0x94, 0x0b, 0x84, 0xa0, 0x14, 0xe2, 0x80, 0xd4, 0xb4, 0xa6, 0x76, 0xbc, 0x63, 0xa9, 0xdf, 0xe8, 0x3b, 0x28, 0x4d, 0x68, 0xe8, 0xd5, 0x0a, 0x4d, 0xed, 0x78, 0xef, 0xf4, 0xb1, 0x91, 0xe3, 0xa4, 0xb1, 0x78, 0xe0, 0x82, 0x86, 0x9e, 0xa5, 0xae, 0xb7, 0x30, 0xe8, 0x0b, 0xeb, 0x25, 0x11, 0xd8, 0xc3, 0x02, 0xa3, 0x4b, 0x38, 0x08, 0xf0, 0x7b, 0x47, 0x86, 0xcd, 0x9d, 0x88, 0xc4, 0x0e, 0x27, 0x2e, 0x0b, 0x3d, 0x25, 0x57, 0x3d, 0x7d, 0x68, 0xa4, 0x9e, 0x1a, 0x0b, 0x4f, 0x8d, 0x2e, 0x4b, 0x46, 0x3e, 0x79, 0x83, 0xfd, 0x84, 0x58, 0x77, 0x03, 0xfc, 0x5e, 0x3e, 0xc8, 0x07, 0x24, 0x1e, 0x2a, 0x5a, 0xeb, 0x35, 0xd4, 0x17, 0x12, 0x03, 0x1c, 0x0b, 0x2a, 0xb3, 0xb2, 0xd4, 0xd2, 0xa1, 0x38, 0x21, 0xf3, 0x2c, 0x12, 0xf9, 0x13, 0x3d, 0x81, 0x7d, 0x36, 0x0b, 0x49, 0xec, 0x5c, 0x33, 0x2e, 0x1c, 0x15, 0x67, 0x41, 0xa1, 0xbb, 0xca, 0xfc, 0x92, 0x71, 0x71, 0x85, 0x03, 0xd2, 0x9a, 0xc0, 0xa1, 0xc9, 0x99, 0xaf, 0x92, 0xfc, 0x22, 0x66, 0x49, 0x74, 0x49, 0x44, 0x4c, 0x5d, 0x8e, 0xda, 0x70, 0x10, 0x92, 0x59, 0xbe, 0xfb, 0x9a, 0x75, 0x37, 0x24, 0xb3, 0x75, 0x07, 0xd1, 0x63, 0xb8, 0x13, 0x31, 0xdf, 0x27, 0xb1, 0xe3, 0xb2, 0x24, 0x14, 0x4a, 0xae, 0x68, 0x55, 0x53, 0xdb, 0xb9, 0x34, 0xb5, 0xfe, 0x2a, 0xc1, 0xde, 0x22, 0x88, 0xa1, 0xc0, 0x22, 0xe1, 0xe8, 0x2b, 0x40, 0x23, 0xec, 0x4e, 0x7c, 0x36, 0x4e, 0x69, 0xce, 0x35, 0x0d, 0x85, 0x12, 0x29, 0x5a, 0x7a, 0x86, 0x28, 0xf2, 0x4b, 0x1a, 0x0a, 0xf4, 0x08, 0x20, 0x26, 0xd8, 0x73, 0x7c, 0x32, 0x25, 0x7e, 0xa6, 0xb0, 0x23, 0x2d, 0x7d, 0x69, 0x40, 0x0f, 0x60, 0x07, 0xbb, 0x93, 0x0c, 0x2d, 0x2a, 0xb4, 0x82, 0xdd, 0x49, 0x0a, 0x3e, 0x81, 0xfd, 0x18, 0x0b, 0xb2, 0x1a, 0x4b, 0x49, 0xc5, 0xb2, 0x2b, 0xcd, 0x1f, 0xe2, 0xe8, 0xc2, 0xae, 0x0c, 0xda, 0xa1, 0x9e, 0x33, 0xf2, 0x99, 0x3b, 0xa9, 0x95, 0x55, 0xc1, 0x9a, 0x1f, 0xed, 0x05, 0xb3, 0x7b, 0x26, 0xef, 0x59, 0x55, 0x49, 0x33, 0x3d, 0x75, 0x40, 0x53, 0x38, 0xa2, 0x8b, 0xbc, 0x3a, 0x63, 0x99, 0x58, 0x27, 0x48, 0x33, 0x5b, 0xbb, 0xd5, 0x2c, 0x1e, 0x57, 0x4f, 0x9f, 0xdd, 0xd8, 0x5b, 0x69, 0x76, 0x8c, 0xdc, 0xd2, 0xf4, 0x42, 0x11, 0xcf, 0xad, 0x43, 0xfa, 0x49, 0x65, 0xbb, 0xfd, 0xb1, 0xb2, 0x1d, 0x40, 0x99, 0x04, 0x91, 0x98, 0xd7, 0x2a, 0x4d, 0xed, 0xb8, 0x62, 0xa5, 0x87, 0x86, 0x80, 0xc6, 0xc7, 0xb5, 0x73, 0xda, 0xed, 0x39, 0x94, 0xa7, 0xb2, 0x73, 0x55, 0x4d, 0xaa, 0xa7, 0x4f, 0x73, 0x83, 0xcb, 0x7d, 0xd1, 0x4a, 0x89, 0x3f, 0x14, 0xbe, 0xd7, 0x5a, 0x3f, 0x43, 0x75, 0x25, 0xa1, 0xa8, 0x0e, 0x15, 0x2e, 0x70, 0x2c, 0x1c, 0xea, 0x65, 0x1d, 0x71, 0x5b, 0x9d, 0x4d, 0x0f, 0x1d, 0xc2, 0x2d, 0x12, 0x7a, 0x12, 0x48, 0x9b, 0xa0, 0x4c, 0x42, 0xcf, 0xf4, 0x5a, 0x7f, 0x6a, 0x00, 0x03, 0xd5, 0x70, 0x66, 0xf8, 0x8e, 0xa1, 0x2e, 0xe8, 0x3e, 0xe6, 0xc2, 0xc1, 0xae, 0x4b, 0x38, 0x77, 0xe4, 0xb2, 0xc8, 0xc6, 0xaf, 0xb1, 0x35, 0x7e, 0xf6, 0x62, 0x93, 0x58, 0x7b, 0x92, 0xd3, 0x51, 0x14, 0x69, 0x44, 0x0d, 0xa8, 0x50, 0x8f, 0x84, 0x82, 0x8a, 0x79, 0x36, 0x43, 0xcb, 0x73, 0x5e, 0x53, 0x15, 0x73, 0x9a, 0xaa, 0xf5, 0xb7, 0x06, 0xf5, 0xa1, 0xa0, 0xee, 0x64, 0xde, 0x7b, 0x4f, 0xdc, 0x44, 0x26, 0xa1, 0x23, 0x44, 0x4c, 0x47, 0x89, 0x20, 0x1c, 0xbd, 0x00, 0x7d, 0xc6, 0xe2, 0x09, 0x89, 0x55, 0xdd, 0x1c, 0xb9, 0x25, 0x33, 0x3f, 0x1f, 0xdd, 0xd8, 0x25, 0xd6, 0x5e, 0x4a, 0x5b, 0xae, 0x34, 0x1b, 0xea, 0xdc, 0xbd, 0x26, 0x5e, 0xe2, 0x13, 0x47, 0x30, 0x27, 0xcd, 0x9e, 0x0c, 0x9b, 0x25, 0x22, 0x2b, 0x4d, 0x7d, 0x7b, 0xf1, 0x64, 0x3b, 0xd6, 0xba, 0xbf, 0xe0, 0xda, 0x6c, 0x28, 0x99, 0x76, 0x4a, 0x6c, 0x3d, 0x83, 0xbb, 0x5b, 0xab, 0x07, 0x7d, 0x09, 0xfa, 0x46, 0x83, 0xf3, 0x9a, 0xd6, 0x2c, 0x1e, 0xef, 0x58, 0xfb, 0xeb, 0x9d, 0xc9, 0x5b, 0xff, 0x94, 0xe0, 0x68, 0xeb, 0x81, 0x73, 0x16, 0xbe, 0xa3, 0x63, 0x54, 0x83, 0xdb, 0x53, 0x12, 0x73, 0xca, 0xc2, 0x45, 0x89, 0xb3, 0x23, 0x3a, 0x85, 0x7b, 0x61, 0x12, 0x38, 0x6a, 0xde, 0xa3, 0x05, 0x8b, 0xab, 0x28, 0xca, 0x67, 0x85, 0x9a, 0x6c, 0xe6, 0x24, 0xb0, 0x08, 0xf6, 0x96, 0x4f, 0x72, 0xf4, 0x2d, 0x1c, 0x48, 0xce, 0x2c, 0xa6, 0xb2, 0x26, 0x1f, 0x48, 0xc5, 0x25, 0x09, 0x85, 0x49, 0xf0, 0x8b, 0x84, 0x57, 0x58, 0x14, 0xf6, 0x37, 0x55, 0x4a, 0x6a, 0x46, 0x9f, 0xdf, 0x98, 0xfd, 0x8d, 0x50, 0x8c, 0x75, 0x5f, 0xd2, 0x29, 0xdd, 0x8b, 0xd7, 0x1d, 0xf4, 0x41, 0xdf, 0x72, 0xae, 0xac, 0xb4, 0x3a, 0x9f, 0xa4, 0xb5, 0x11, 0x42, 0x2a, 0xb6, 0x3f, 0x5b, 0xb7, 0x36, 0x28, 0xdc, 0xcb, 0x71, 0x6a, 0x75, 0x7c, 0xcb, 0xe9, 0xf8, 0xfe, 0xb4, 0x3e, 0xbe, 0x4f, 0xfe, 0x9b, 0x2f, 0x2b, 0xa3, 0xdb, 0xf8, 0x1d, 0x0e, 0xf2, 0x7c, 0xfa, 0x3f, 0xb4, 0x9e, 0xfe, 0x01, 0x77, 0x56, 0xff, 0x83, 0x51, 0x03, 0xee, 0xdb, 0x9d, 0xe1, 0x85, 0xd3, 0x37, 0x87, 0xb6, 0x73, 0x61, 0x5e, 0x75, 0x1d, 0xf3, 0xea, 0x4d, 0xa7, 0x6f, 0x76, 0xf5, 0xcf, 0x50, 0x1d, 0x0e, 0x37, 0xb0, 0xab, 0x57, 0xd6, 0x65, 0xa7, 0xaf, 0x6b, 0x39, 0xd0, 0xd0, 0x36, 0xcf, 0x2f, 0xde, 0xea, 0x05, 0xf4, 0x10, 0x6a, 0x1b, 0x50, 0x6f, 0xf0, 0xb2, 0x77, 0xd9, 0xb3, 0x3a, 0x7d, 0xbd, 0xf8, 0xd4, 0xfb, 0xa0, 0x6f, 0xcf, 0x23, 0xb2, 0xae, 0x6f, 0xbf, 0x1d, 0xf4, 0x56, 0xf4, 0x1f, 0xc0, 0xd1, 0x06, 0xd6, 0xed, 0x9d, 0x9b, 0x43, 0xf3, 0xd5, 0x95, 0xae, 0xe5, 0x80, 0x9d, 0x73, 0xdb, 0x7c, 0x63, 0xda, 0x6f, 0xf5, 0xc2, 0xd9, 0x6f, 0x70, 0xe4, 0xb2, 0x20, 0x2f, 0x3b, 0x67, 0xbb, 0xcb, 0xf4, 0xc8, 0x19, 0x1e, 0x68, 0xbf, 0x9e, 0x8c, 0xa9, 0xb8, 0x4e, 0x46, 0x86, 0xcb, 0x82, 0xf6, 0xea, 0xb7, 0xd7, 0xd7, 0xd4, 0xf3, 0xdb, 0x63, 0x96, 0x7e, 0x0e, 0x65, 0x1f, 0x62, 0x3f, 0xe2, 0x88, 0x4e, 0x4f, 0x46, 0xb7, 0x94, 0xed, 0x9b, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xea, 0xcc, 0x39, 0x04, 0xac, 0x09, 0x00, 0x00, }, // google/protobuf/wrappers.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x2f, 0x4a, 0x2c, 0x28, 0x48, 0x2d, 0x2a, 0xd6, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0xca, 0x5c, 0xdc, 0x2e, 0xf9, 0xa5, 0x49, 0x39, 0xa9, 0x61, 0x89, 0x39, 0xa5, 0xa9, 0x42, 0x22, 0x5c, 0xac, 0x65, 0x20, 0x86, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x63, 0x10, 0x84, 0xa3, 0xa4, 0xc4, 0xc5, 0xe5, 0x96, 0x93, 0x9f, 0x58, 0x82, 0x45, 0x0d, 0x13, 0x92, 0x1a, 0xcf, 0xbc, 0x12, 0x33, 0x13, 0x2c, 0x6a, 0x98, 0x61, 0x6a, 0x94, 0xb9, 0xb8, 0x43, 0x71, 0x29, 0x62, 0x41, 0x35, 0xc8, 0xd8, 0x08, 0x8b, 0x1a, 0x56, 0x34, 0x83, 0xb0, 0x2a, 0xe2, 0x85, 0x29, 0x52, 0xe4, 0xe2, 0x74, 0xca, 0xcf, 0xcf, 0xc1, 0xa2, 0x84, 0x03, 0xc9, 0x9c, 0xe0, 0x92, 0xa2, 0xcc, 0xbc, 0x74, 0x2c, 0x8a, 0x38, 0x91, 0x1c, 0xe4, 0x54, 0x59, 0x92, 0x5a, 0x8c, 0x45, 0x0d, 0x0f, 0x54, 0x8d, 0x53, 0x33, 0x23, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x5a, 0xf0, 0x3a, 0xf1, 0x86, 0x43, 0xc3, 0x3f, 0x00, 0x24, 0x12, 0xc0, 0x18, 0x65, 0x08, 0x55, 0x91, 0x9e, 0x9f, 0x93, 0x98, 0x97, 0xae, 0x97, 0x5f, 0x94, 0x8e, 0x88, 0xab, 0x92, 0xca, 0x82, 0xd4, 0x62, 0xfd, 0xec, 0xbc, 0xfc, 0xf2, 0x3c, 0x78, 0xbc, 0x15, 0x24, 0xfd, 0x60, 0x64, 0x5c, 0xc4, 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0xa2, 0x39, 0x00, 0xaa, 0x43, 0x2f, 0x3c, 0x35, 0x27, 0xc7, 0x1b, 0xa4, 0x3e, 0x04, 0xa4, 0x35, 0x89, 0x0d, 0x6c, 0x94, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x3c, 0x92, 0x48, 0x30, 0x06, 0x02, 0x00, 0x00, }, // uber/cadence/api/v1/workflow.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0xcd, 0x72, 0xdb, 0xc8, 0xb5, 0xbe, 0x20, 0x25, 0x59, 0x3a, 0xa4, 0x24, 0xa8, 0x25, 0x59, 0xf4, 0xcf, 0xd8, 0xb2, 0x66, 0xec, 0x91, 0x79, 0xc7, 0xd2, 0xc8, 0xf3, 0x3f, 0xbe, 0x73, 0x1d, 0x08, 0x84, 0x6c, 0xd8, 0x34, 0xc8, 0x34, 0x41, 0x6b, 0x34, 0x95, 0x04, 0x05, 0x91, 0x2d, 0x09, 0x31, 0x09, 0xb0, 0x80, 0xa6, 0x6d, 0xed, 0x53, 0x95, 0x6c, 0x93, 0xd5, 0x54, 0x56, 0x79, 0x80, 0x54, 0xa5, 0x52, 0x59, 0x64, 0x95, 0xca, 0x13, 0x64, 0x9b, 0x57, 0x48, 0xe5, 0x2d, 0x52, 0xdd, 0x68, 0x90, 0x00, 0x09, 0x12, 0x74, 0x52, 0x35, 0xd9, 0x09, 0xa7, 0xbf, 0xef, 0xf4, 0xe9, 0xf3, 0xd7, 0x07, 0x10, 0x61, 0xa7, 0x7f, 0x4a, 0xfc, 0xfd, 0x96, 0xdd, 0x26, 0x6e, 0x8b, 0xec, 0xdb, 0x3d, 0x67, 0xff, 0xf5, 0xc1, 0xfe, 0x1b, 0xcf, 0x7f, 0x75, 0xd6, 0xf1, 0xde, 0xec, 0xf5, 0x7c, 0x8f, 0x7a, 0x68, 0x9d, 0x61, 0xf6, 0x04, 0x66, 0xcf, 0xee, 0x39, 0x7b, 0xaf, 0x0f, 0xae, 0xdf, 0x3a, 0xf7, 0xbc, 0xf3, 0x0e, 0xd9, 0xe7, 0x90, 0xd3, 0xfe, 0xd9, 0x7e, 0xbb, 0xef, 0xdb, 0xd4, 0xf1, 0xdc, 0x90, 0x74, 0xfd, 0xf6, 0xe8, 0x3a, 0x75, 0xba, 0x24, 0xa0, 0x76, 0xb7, 0x27, 0x00, 0xdb, 0x69, 0x3b, 0xb7, 0xbc, 0x6e, 0x77, 0xa0, 0x22, 0xd5, 0x36, 0x6a, 0x07, 0xaf, 0x3a, 0x4e, 0x40, 0x43, 0xcc, 0xce, 0xf7, 0x45, 0xd8, 0x3c, 0x16, 0xe6, 0x6a, 0x6f, 0x49, 0xab, 0xcf, 0x4c, 0xd0, 0xdd, 0x33, 0x0f, 0x35, 0x01, 0x45, 0xe7, 0xb0, 0x48, 0xb4, 0x52, 0x92, 0xb6, 0xa5, 0xdd, 0xc2, 0xc3, 0x7b, 0x7b, 0x29, 0x47, 0xda, 0x1b, 0xd3, 0x83, 0xd7, 0xde, 0x8c, 0x8a, 0xd0, 0x67, 0x30, 0x47, 0x2f, 0x7b, 0xa4, 0x94, 0xe3, 0x8a, 0xee, 0x4c, 0x55, 0x64, 0x5e, 0xf6, 0x08, 0xe6, 0x70, 0xf4, 0x15, 0x40, 0x40, 0x6d, 0x9f, 0x5a, 0xcc, 0x0d, 0xa5, 0x3c, 0x27, 0x5f, 0xdf, 0x0b, 0x7d, 0xb4, 0x17, 0xf9, 0x68, 0xcf, 0x8c, 0x7c, 0x84, 0x97, 0x38, 0x9a, 0x3d, 0x33, 0x6a, 0xab, 0xe3, 0x05, 0x24, 0xa4, 0xce, 0x65, 0x53, 0x39, 0x9a, 0x53, 0x4d, 0x28, 0x86, 0xd4, 0x80, 0xda, 0xb4, 0x1f, 0x94, 0xe6, 0xb7, 0xa5, 0xdd, 0x95, 0x87, 0x07, 0xb3, 0x9d, 0x5e, 0x65, 0xcc, 0x06, 0x27, 0xe2, 0x42, 0x6b, 0xf8, 0x80, 0xee, 0xc2, 0xca, 0x85, 0x13, 0x50, 0xcf, 0xbf, 0xb4, 0x3a, 0xc4, 0x3d, 0xa7, 0x17, 0xa5, 0x85, 0x6d, 0x69, 0x37, 0x8f, 0x97, 0x85, 0xb4, 0xca, 0x85, 0xe8, 0x27, 0xb0, 0xd9, 0xb3, 0x7d, 0xe2, 0xd2, 0xa1, 0xfb, 0x2d, 0xc7, 0x3d, 0xf3, 0x4a, 0x57, 0xf8, 0x11, 0x76, 0x53, 0xad, 0xa8, 0x73, 0x46, 0x22, 0x92, 0x78, 0xbd, 0x37, 0x2e, 0x44, 0x0a, 0xac, 0x0c, 0xd5, 0x72, 0xcf, 0x2c, 0x66, 0x7a, 0x66, 0x79, 0xc0, 0xe0, 0xde, 0x79, 0x00, 0x73, 0x5d, 0xd2, 0xf5, 0x4a, 0x4b, 0x9c, 0x78, 0x2d, 0xd5, 0x9e, 0x17, 0xa4, 0xeb, 0x61, 0x0e, 0x43, 0x18, 0xd6, 0x02, 0x62, 0xfb, 0xad, 0x0b, 0xcb, 0xa6, 0xd4, 0x77, 0x4e, 0xfb, 0x94, 0x04, 0x25, 0xe0, 0xdc, 0xbb, 0xa9, 0xdc, 0x06, 0x47, 0x2b, 0x03, 0x30, 0x96, 0x83, 0x11, 0x09, 0xaa, 0xc2, 0x9a, 0xdd, 0xa7, 0x9e, 0xe5, 0x93, 0x80, 0x50, 0xab, 0xe7, 0x39, 0x2e, 0x0d, 0x4a, 0x05, 0xae, 0x73, 0x3b, 0x55, 0x27, 0x66, 0xc0, 0x3a, 0xc7, 0xe1, 0x55, 0x46, 0x8d, 0x09, 0xd0, 0x0d, 0x58, 0x62, 0xe5, 0x61, 0xb1, 0xfa, 0x28, 0x15, 0xb7, 0xa5, 0xdd, 0x25, 0xbc, 0xc8, 0x04, 0x55, 0x27, 0xa0, 0x48, 0x85, 0x95, 0xc1, 0x62, 0x18, 0x87, 0x35, 0xbe, 0xcf, 0x7b, 0xa9, 0xfb, 0x98, 0x82, 0x86, 0x8b, 0x91, 0x02, 0xee, 0xf5, 0x2d, 0xb8, 0xe2, 0x04, 0x56, 0xcb, 0xf7, 0xdc, 0xd2, 0xf2, 0xb6, 0xb4, 0xbb, 0x88, 0x17, 0x9c, 0x40, 0xf5, 0x3d, 0x17, 0x3d, 0x82, 0x42, 0xbf, 0xd7, 0xb6, 0xa9, 0xc8, 0xd2, 0x95, 0xcc, 0x58, 0x40, 0x08, 0xe7, 0x81, 0xf8, 0x39, 0xc8, 0x3d, 0xdb, 0xa7, 0x0e, 0x8f, 0x65, 0xcb, 0x73, 0xcf, 0x9c, 0xf3, 0xd2, 0xea, 0x76, 0x7e, 0xb7, 0xf0, 0xf0, 0xf1, 0x6c, 0xa9, 0xca, 0x6c, 0x63, 0xa9, 0x13, 0xaa, 0x50, 0xb9, 0x06, 0xcd, 0xa5, 0xfe, 0x25, 0x5e, 0xed, 0x25, 0xa5, 0xe8, 0x25, 0xac, 0x33, 0xf3, 0x2d, 0xef, 0x35, 0xf1, 0x3b, 0x76, 0xcf, 0xea, 0x79, 0x1d, 0xa7, 0x75, 0x59, 0x92, 0x79, 0x65, 0xa4, 0xf7, 0x05, 0x76, 0xc0, 0x5a, 0x08, 0xaf, 0x73, 0x34, 0x5e, 0x6b, 0x8d, 0x8a, 0xd0, 0x5b, 0xb8, 0x6d, 0xb7, 0xa8, 0xf3, 0x9a, 0x58, 0xad, 0x4e, 0x3f, 0xa0, 0xc4, 0xb7, 0x02, 0xd2, 0x21, 0x2d, 0x7e, 0x24, 0xb1, 0x07, 0xe2, 0x4e, 0x49, 0xaf, 0x3e, 0x85, 0x73, 0xd5, 0x90, 0xda, 0x88, 0x98, 0x62, 0xbb, 0x9b, 0xf6, 0x94, 0x55, 0xf4, 0x3e, 0x2c, 0xf3, 0x13, 0x05, 0xad, 0x0b, 0xd2, 0xee, 0x77, 0x48, 0x69, 0x9d, 0x47, 0xbe, 0xc8, 0x84, 0x0d, 0x21, 0x43, 0xc7, 0x20, 0x0f, 0xcb, 0x45, 0x74, 0x83, 0x0d, 0x7e, 0xe6, 0x8f, 0x66, 0x73, 0xb1, 0x68, 0x04, 0xab, 0x24, 0x29, 0x40, 0x26, 0x94, 0xa2, 0x8d, 0xdb, 0xd6, 0x48, 0x45, 0x6e, 0x66, 0x66, 0xc1, 0xd5, 0x01, 0x57, 0x8b, 0x97, 0xe6, 0xf5, 0x43, 0xd8, 0x48, 0x0b, 0x27, 0x92, 0x21, 0xff, 0x8a, 0x5c, 0xf2, 0x2e, 0xbe, 0x84, 0xd9, 0x9f, 0x68, 0x03, 0xe6, 0x5f, 0xdb, 0x9d, 0x7e, 0xd8, 0x90, 0x97, 0x70, 0xf8, 0xf0, 0x75, 0xee, 0x4b, 0x69, 0xe7, 0xfb, 0x1c, 0xdc, 0x1a, 0x6f, 0x6a, 0x5c, 0x99, 0xb8, 0xaa, 0xd0, 0xd7, 0xf1, 0x82, 0x91, 0x66, 0x29, 0x87, 0x61, 0x3d, 0xd9, 0xb0, 0x9d, 0xf0, 0x28, 0xeb, 0xed, 0x9e, 0x35, 0xec, 0xd4, 0x5e, 0x9f, 0x8a, 0x4b, 0xe2, 0xda, 0x98, 0x03, 0x2a, 0xc2, 0x00, 0x7c, 0x33, 0xee, 0x4e, 0x9f, 0x9a, 0x9e, 0x1a, 0xf5, 0x6e, 0xaf, 0x4f, 0xd1, 0x31, 0xdc, 0xe0, 0xe6, 0x4d, 0xd0, 0x9e, 0xcf, 0xd2, 0xbe, 0xc5, 0xd8, 0x29, 0x8a, 0x77, 0xfe, 0x26, 0xc1, 0x7a, 0x4a, 0xa7, 0x65, 0x0d, 0xa4, 0xed, 0x75, 0x6d, 0xc7, 0xb5, 0x9c, 0xb6, 0x70, 0xf2, 0x62, 0x28, 0xd0, 0xdb, 0xe8, 0x36, 0x14, 0xc4, 0xa2, 0x6b, 0x77, 0x23, 0x7f, 0x43, 0x28, 0x32, 0xec, 0x2e, 0x99, 0x70, 0xe3, 0xe6, 0xff, 0xd3, 0x1b, 0xf7, 0x0e, 0x14, 0x1d, 0xd7, 0xa1, 0x8e, 0x4d, 0x49, 0x9b, 0xd9, 0x35, 0xc7, 0x2f, 0x9b, 0xc2, 0x40, 0xa6, 0xb7, 0x77, 0x7e, 0x2d, 0xc1, 0xa6, 0xf6, 0x96, 0x12, 0xdf, 0xb5, 0x3b, 0x3f, 0xc8, 0x14, 0x30, 0x6a, 0x53, 0x6e, 0xdc, 0xa6, 0x3f, 0x2f, 0xc0, 0x7a, 0x9d, 0xb8, 0x6d, 0xc7, 0x3d, 0xe7, 0xc5, 0xed, 0xd0, 0x4b, 0x6e, 0xd1, 0x6d, 0x28, 0xd8, 0xe2, 0x79, 0xe8, 0x65, 0x88, 0x44, 0x7a, 0x1b, 0x1d, 0xc1, 0xf2, 0x00, 0x90, 0x39, 0x6a, 0x44, 0xaa, 0xf9, 0xa8, 0x51, 0xb4, 0x63, 0x4f, 0xe8, 0x31, 0xcc, 0xb3, 0x42, 0x0f, 0xa7, 0x8d, 0x95, 0x87, 0xf7, 0xd3, 0xef, 0xdb, 0xa4, 0x85, 0xac, 0xa8, 0x09, 0x0e, 0x79, 0x48, 0x87, 0xb5, 0x0b, 0x62, 0xfb, 0xf4, 0x94, 0xd8, 0xd4, 0x6a, 0x13, 0x6a, 0x3b, 0x9d, 0x40, 0xcc, 0x1f, 0x37, 0x27, 0x5c, 0xde, 0x97, 0x1d, 0xcf, 0x6e, 0x63, 0x79, 0x40, 0xab, 0x84, 0x2c, 0xf4, 0x0c, 0xd6, 0x3b, 0x76, 0x40, 0xad, 0xa1, 0x3e, 0xde, 0x20, 0xe6, 0x33, 0x1b, 0xc4, 0x1a, 0xa3, 0x3d, 0x8d, 0x58, 0xfc, 0xb6, 0x38, 0x02, 0x2e, 0x0c, 0xab, 0x82, 0xb4, 0x43, 0x4d, 0x0b, 0x99, 0x9a, 0x56, 0x19, 0xa9, 0x11, 0x72, 0xb8, 0x9e, 0x12, 0x5c, 0xb1, 0x29, 0x25, 0xdd, 0x1e, 0xe5, 0x13, 0xc9, 0x3c, 0x8e, 0x1e, 0xd1, 0x7d, 0x90, 0xbb, 0xf6, 0x5b, 0xa7, 0xdb, 0xef, 0x5a, 0x42, 0x14, 0xf0, 0xe9, 0x62, 0x1e, 0xaf, 0x0a, 0xb9, 0x22, 0xc4, 0x6c, 0x0c, 0x19, 0xb6, 0x3f, 0x6e, 0xc9, 0x52, 0xf6, 0x18, 0x32, 0x60, 0x70, 0x3b, 0x54, 0x58, 0x25, 0x6f, 0x7b, 0x4e, 0x58, 0xb3, 0xa1, 0x0e, 0xc8, 0xd4, 0xb1, 0x32, 0xa4, 0x70, 0x25, 0x8f, 0xa1, 0xc8, 0x9d, 0x72, 0x66, 0x3b, 0x9d, 0xbe, 0x4f, 0xc4, 0x0c, 0x91, 0x1e, 0xa6, 0xa3, 0x10, 0x83, 0x0b, 0x8c, 0x21, 0x1e, 0xd0, 0xc7, 0xb0, 0xc1, 0x15, 0xb0, 0x5c, 0x27, 0xbe, 0xe5, 0xb4, 0x89, 0x4b, 0x1d, 0x7a, 0x29, 0xc6, 0x08, 0xc4, 0xd6, 0x8e, 0xf9, 0x92, 0x2e, 0x56, 0xd0, 0xe7, 0xb0, 0x15, 0x85, 0x60, 0x94, 0xb4, 0xcc, 0x49, 0x9b, 0x62, 0x79, 0x84, 0x77, 0x1b, 0x0a, 0x91, 0x03, 0x58, 0x01, 0xac, 0xf0, 0xd2, 0x81, 0x48, 0xa4, 0xb7, 0x77, 0xfe, 0x94, 0x83, 0x6b, 0x22, 0x2f, 0xd5, 0x0b, 0xa7, 0xd3, 0xfe, 0x41, 0x2a, 0xfa, 0xa3, 0x98, 0x5a, 0x56, 0x75, 0xf1, 0x26, 0x27, 0xbf, 0x89, 0x0d, 0xf4, 0xbc, 0xd5, 0x8d, 0xd6, 0x7f, 0x7e, 0xac, 0xfe, 0xd9, 0xa0, 0x21, 0xc6, 0xdf, 0xb0, 0x6b, 0x8b, 0x21, 0x60, 0x6e, 0xca, 0xa0, 0x11, 0xb6, 0x64, 0xde, 0xa9, 0xa3, 0x41, 0xa3, 0x37, 0x2a, 0x42, 0x57, 0x61, 0x21, 0xec, 0xb9, 0xbc, 0x7a, 0x96, 0xb0, 0x78, 0xda, 0xf9, 0x47, 0x6e, 0xd0, 0x6f, 0x2a, 0xa4, 0xe5, 0x04, 0x91, 0xbf, 0x06, 0x6d, 0x40, 0xca, 0x6e, 0x03, 0x11, 0x31, 0xd1, 0x06, 0xc6, 0x53, 0x3c, 0xf7, 0xae, 0x29, 0xfe, 0x0d, 0x14, 0x13, 0xd5, 0x9a, 0xfd, 0xfe, 0x53, 0x08, 0xd2, 0x2b, 0x75, 0x2e, 0x59, 0xa9, 0x18, 0xb6, 0x3c, 0xdf, 0x39, 0x77, 0x5c, 0xbb, 0x63, 0x8d, 0x18, 0x99, 0xdd, 0x5b, 0x36, 0x23, 0x6a, 0x23, 0x61, 0xec, 0x48, 0x7e, 0x2e, 0x8c, 0xe5, 0xe7, 0x5f, 0x72, 0x70, 0x2d, 0x6a, 0x98, 0x55, 0xaf, 0x65, 0x77, 0x2a, 0x4e, 0xd0, 0xb3, 0x69, 0xeb, 0x62, 0xb6, 0xfe, 0xfe, 0xdf, 0xf7, 0xe7, 0xcf, 0xe0, 0x56, 0xd2, 0x02, 0xcb, 0x3b, 0xb3, 0xe8, 0x85, 0x13, 0x58, 0x71, 0x37, 0x4f, 0x57, 0x78, 0x3d, 0x61, 0x51, 0xed, 0xcc, 0xbc, 0x70, 0x02, 0xd1, 0x15, 0xd1, 0x7b, 0x00, 0x7c, 0x6e, 0xa1, 0xde, 0x2b, 0x12, 0xa6, 0x69, 0x11, 0xf3, 0x41, 0xcb, 0x64, 0x82, 0x9d, 0x67, 0x50, 0x88, 0xbf, 0xb5, 0x3c, 0x82, 0x05, 0xf1, 0xe2, 0x23, 0xf1, 0x99, 0xff, 0xfd, 0x8c, 0x17, 0x1f, 0xfe, 0x4e, 0x28, 0x28, 0x3b, 0x7f, 0xc8, 0xc1, 0x4a, 0x72, 0x09, 0x7d, 0x08, 0xab, 0xa7, 0x8e, 0x6b, 0xfb, 0x97, 0x56, 0xeb, 0x82, 0xb4, 0x5e, 0x05, 0xfd, 0xae, 0x08, 0xc2, 0x4a, 0x28, 0x56, 0x85, 0x14, 0x6d, 0xc2, 0x82, 0xdf, 0x77, 0xa3, 0xeb, 0x7b, 0x09, 0xcf, 0xfb, 0x7d, 0x36, 0xe7, 0x7c, 0x03, 0x37, 0xce, 0x1c, 0x3f, 0x60, 0x57, 0x5e, 0x58, 0x0d, 0x56, 0xcb, 0xeb, 0xf6, 0x3a, 0x24, 0x51, 0xea, 0x25, 0x0e, 0x89, 0xea, 0x45, 0x8d, 0x00, 0x9c, 0x5e, 0x6c, 0xf9, 0xc4, 0x1e, 0xc4, 0x26, 0xdb, 0x95, 0x05, 0x81, 0x17, 0x8d, 0x7c, 0x99, 0xb7, 0x76, 0xc7, 0x3d, 0x9f, 0x35, 0x8f, 0x8b, 0x11, 0x81, 0x2b, 0xb8, 0x05, 0xc0, 0xdf, 0x26, 0xa9, 0x7d, 0xda, 0x09, 0xef, 0xc5, 0x45, 0x1c, 0x93, 0x94, 0xff, 0x28, 0xc1, 0x46, 0xda, 0xad, 0x8f, 0x76, 0xe0, 0x56, 0x5d, 0x33, 0x2a, 0xba, 0xf1, 0xc4, 0x52, 0x54, 0x53, 0x7f, 0xa9, 0x9b, 0x27, 0x56, 0xc3, 0x54, 0x4c, 0xcd, 0xd2, 0x8d, 0x97, 0x4a, 0x55, 0xaf, 0xc8, 0xff, 0x83, 0x3e, 0x80, 0xed, 0x09, 0x98, 0x86, 0xfa, 0x54, 0xab, 0x34, 0xab, 0x5a, 0x45, 0x96, 0xa6, 0x68, 0x6a, 0x98, 0x0a, 0x36, 0xb5, 0x8a, 0x9c, 0x43, 0xff, 0x0b, 0x1f, 0x4e, 0xc0, 0xa8, 0x8a, 0xa1, 0x6a, 0x55, 0x0b, 0x6b, 0x3f, 0x6e, 0x6a, 0x0d, 0x06, 0xce, 0x97, 0x7f, 0x31, 0xb4, 0x39, 0xd1, 0xa2, 0xe2, 0x3b, 0x55, 0x34, 0x55, 0x6f, 0xe8, 0x35, 0x63, 0x9a, 0xcd, 0x23, 0x98, 0x09, 0x36, 0x8f, 0xa2, 0x22, 0x9b, 0xcb, 0xbf, 0xcc, 0x0d, 0x3f, 0x36, 0xe9, 0x6d, 0x4c, 0xfa, 0x83, 0xa6, 0xfc, 0x01, 0x6c, 0x1f, 0xd7, 0xf0, 0xf3, 0xa3, 0x6a, 0xed, 0xd8, 0xd2, 0x2b, 0x16, 0xd6, 0x9a, 0x0d, 0xcd, 0xaa, 0xd7, 0xaa, 0xba, 0x7a, 0x12, 0xb3, 0xe4, 0x4b, 0xf8, 0x74, 0x22, 0x4a, 0xa9, 0x32, 0x69, 0xa5, 0x59, 0xaf, 0xea, 0x2a, 0xdb, 0xf5, 0x48, 0xd1, 0xab, 0x5a, 0xc5, 0xaa, 0x19, 0xd5, 0x13, 0x59, 0x42, 0x1f, 0xc1, 0xee, 0xac, 0x4c, 0x39, 0x87, 0x1e, 0xc0, 0xfd, 0x89, 0x68, 0xac, 0x3d, 0xd3, 0x54, 0x33, 0x06, 0xcf, 0xa3, 0x03, 0x78, 0x30, 0x11, 0x6e, 0x6a, 0xf8, 0x85, 0x6e, 0x70, 0x87, 0x1e, 0x59, 0xb8, 0x69, 0x18, 0xba, 0xf1, 0x44, 0x9e, 0x2b, 0x5f, 0xc2, 0xda, 0xd8, 0x5b, 0x31, 0xba, 0x0d, 0x37, 0x54, 0x5c, 0x33, 0xac, 0xda, 0x4b, 0x0d, 0x57, 0x95, 0xfa, 0xf8, 0xf9, 0x27, 0x00, 0x1a, 0xcf, 0xf5, 0x7a, 0x3d, 0x0a, 0x42, 0x1a, 0xe0, 0xb0, 0x79, 0x74, 0xa4, 0x61, 0xab, 0x66, 0x68, 0x72, 0xae, 0xfc, 0x3b, 0x09, 0xd6, 0xc6, 0x2e, 0x4a, 0xa6, 0xba, 0xae, 0x60, 0xcd, 0x30, 0x2d, 0xb5, 0x5a, 0x4b, 0xf3, 0xfd, 0x04, 0x80, 0x72, 0xa8, 0x18, 0x95, 0x9a, 0x21, 0x4b, 0xe8, 0x1e, 0xec, 0xa4, 0x01, 0x44, 0x1a, 0x8a, 0xac, 0x94, 0x73, 0xe8, 0x0e, 0xbc, 0x97, 0x86, 0x1b, 0x38, 0x4a, 0xce, 0x97, 0xff, 0x99, 0x83, 0x9b, 0xd3, 0x3e, 0xa7, 0xb1, 0xe4, 0x1f, 0x78, 0x5c, 0xfb, 0x56, 0x53, 0x9b, 0x26, 0x4b, 0xb7, 0x50, 0x1f, 0x4b, 0xba, 0x66, 0x23, 0x66, 0x79, 0x3c, 0x9a, 0x13, 0xc0, 0x6a, 0xed, 0x45, 0xbd, 0xaa, 0x99, 0xdc, 0x87, 0x65, 0xb8, 0x97, 0x05, 0x0f, 0x73, 0x4b, 0xce, 0x25, 0xd2, 0x6a, 0x92, 0x6a, 0x7e, 0x6e, 0x56, 0x85, 0x68, 0x0f, 0xca, 0x59, 0xe8, 0x81, 0x17, 0x2a, 0xf2, 0x1c, 0xfa, 0x14, 0x3e, 0xce, 0x36, 0xdc, 0x30, 0x75, 0xa3, 0xa9, 0x55, 0x2c, 0xa5, 0x61, 0x19, 0xda, 0xb1, 0x3c, 0x3f, 0xcb, 0x71, 0x4d, 0xfd, 0x05, 0x2b, 0x8d, 0xa6, 0x29, 0x2f, 0x94, 0x7f, 0x95, 0x87, 0xad, 0x09, 0x1f, 0x2b, 0xd0, 0x5d, 0xb8, 0x93, 0xa2, 0x6a, 0xcc, 0xc1, 0x53, 0x61, 0xa2, 0x29, 0xc8, 0xd2, 0x74, 0xd8, 0xb0, 0xb1, 0x7d, 0x08, 0xef, 0x4f, 0x86, 0x0d, 0x03, 0x95, 0x4f, 0xf4, 0x8c, 0x31, 0xa0, 0x08, 0xd1, 0x1c, 0x4b, 0xcb, 0x29, 0xea, 0xa2, 0xe0, 0xcc, 0xa3, 0x5d, 0xf8, 0x60, 0x32, 0x2e, 0x16, 0x96, 0x85, 0x09, 0x61, 0x9c, 0x14, 0x90, 0x2b, 0xd3, 0x0f, 0x34, 0x0c, 0xc5, 0x62, 0xf9, 0xaf, 0x12, 0x5c, 0x55, 0x3d, 0x97, 0x3a, 0x6e, 0x9f, 0x28, 0x81, 0x41, 0xde, 0xe8, 0xe1, 0x38, 0xec, 0xf9, 0xcc, 0x77, 0x91, 0x66, 0xa1, 0xd8, 0xd2, 0x0d, 0xdd, 0xd4, 0x15, 0xb3, 0x86, 0x93, 0x91, 0x98, 0x0c, 0x63, 0x6d, 0xb9, 0xa2, 0xe1, 0x30, 0xc5, 0x27, 0xc3, 0xb0, 0x66, 0xe2, 0x13, 0x51, 0x95, 0xe1, 0x3d, 0x33, 0x19, 0xcb, 0x9b, 0x4d, 0x74, 0x0b, 0xc8, 0xf9, 0xf2, 0xef, 0x25, 0x28, 0x88, 0x6f, 0x24, 0xfc, 0x15, 0xba, 0x04, 0x1b, 0xec, 0x80, 0xb5, 0xa6, 0x69, 0x99, 0x27, 0x75, 0x2d, 0xd9, 0x4e, 0x12, 0x2b, 0x3c, 0xfe, 0x96, 0x59, 0x0b, 0x13, 0x35, 0x6c, 0x65, 0x49, 0x80, 0xd8, 0x85, 0x61, 0x38, 0x58, 0xce, 0x4d, 0xc5, 0x84, 0x7a, 0xf2, 0xe8, 0x3a, 0x5c, 0x4d, 0x60, 0x9e, 0x6a, 0x0a, 0x36, 0x0f, 0x35, 0xc5, 0x94, 0xe7, 0xca, 0xbf, 0x95, 0xe0, 0x5a, 0x74, 0x1f, 0x9a, 0x6c, 0xbc, 0x72, 0xba, 0xa4, 0x5d, 0xeb, 0x53, 0xd5, 0xee, 0x07, 0x04, 0xdd, 0x87, 0xbb, 0x83, 0x9b, 0xcc, 0x54, 0x1a, 0xcf, 0x87, 0xb1, 0xb2, 0x54, 0x85, 0xb5, 0xf8, 0xe1, 0x69, 0x32, 0xa1, 0xc2, 0x04, 0x59, 0x62, 0xd9, 0x30, 0x1d, 0x8a, 0xb5, 0x86, 0x66, 0xca, 0xb9, 0xf2, 0xdf, 0x0b, 0xb0, 0x15, 0x37, 0x8e, 0xbd, 0x68, 0x92, 0x76, 0x68, 0xda, 0x3d, 0xd8, 0x49, 0x2a, 0x11, 0xb7, 0xdd, 0xa8, 0x5d, 0x07, 0xf0, 0x60, 0x0a, 0xae, 0x69, 0x3c, 0x55, 0x8c, 0x0a, 0x7b, 0x8e, 0x40, 0xb2, 0x84, 0x1e, 0xc3, 0xa3, 0x29, 0x94, 0x43, 0xa5, 0x32, 0xf4, 0xf2, 0x60, 0xee, 0x50, 0x4c, 0x13, 0xeb, 0x87, 0x4d, 0x53, 0x6b, 0xc8, 0x39, 0xa4, 0x81, 0x92, 0xa1, 0x20, 0x79, 0x25, 0xa4, 0xaa, 0xc9, 0xa3, 0xaf, 0xe0, 0xb3, 0x2c, 0x3b, 0xc2, 0x94, 0xd1, 0x5f, 0x68, 0x38, 0x4e, 0x9d, 0x43, 0x5f, 0xc3, 0xe7, 0x19, 0x54, 0xb1, 0xf3, 0x18, 0x77, 0x1e, 0x3d, 0x82, 0x2f, 0x32, 0xad, 0x57, 0x6b, 0xb8, 0x62, 0xbd, 0x50, 0xf0, 0xf3, 0x24, 0x79, 0x01, 0xe9, 0xa0, 0x65, 0x6d, 0x2c, 0xfa, 0x97, 0x95, 0xd2, 0x11, 0x62, 0xaa, 0xae, 0xcc, 0xe0, 0x45, 0x26, 0xc8, 0x50, 0xb3, 0x88, 0x9e, 0x80, 0x3a, 0x9b, 0x2b, 0xa6, 0x2b, 0x5a, 0x42, 0xdf, 0x82, 0xf9, 0x6e, 0x51, 0xd5, 0xbe, 0x35, 0x35, 0x6c, 0x28, 0x59, 0x9a, 0x01, 0x7d, 0x03, 0x5f, 0x65, 0x3a, 0x2d, 0xd9, 0x7f, 0x62, 0xf4, 0x02, 0xfa, 0x02, 0x3e, 0x99, 0x42, 0x8f, 0xe7, 0xc8, 0x70, 0x36, 0xd4, 0x2b, 0x72, 0x11, 0x7d, 0x06, 0x07, 0x53, 0x88, 0xbc, 0x0a, 0xad, 0x86, 0xa9, 0xab, 0xcf, 0x4f, 0xc2, 0xe5, 0xaa, 0xde, 0x30, 0xe5, 0x65, 0xf4, 0x23, 0xf8, 0xbf, 0x29, 0xb4, 0xc1, 0x61, 0xd9, 0x1f, 0x1a, 0x8e, 0x95, 0x18, 0x83, 0x35, 0xb1, 0x26, 0xaf, 0xcc, 0x10, 0x93, 0x86, 0xfe, 0x24, 0xdb, 0x73, 0xab, 0x48, 0x85, 0xc7, 0x33, 0x95, 0x88, 0xfa, 0x54, 0xaf, 0x56, 0xd2, 0x95, 0xc8, 0xe8, 0x13, 0xd8, 0x9f, 0xa2, 0xe4, 0xa8, 0x86, 0x55, 0x4d, 0x0c, 0x0f, 0x83, 0x26, 0xb1, 0x86, 0x3e, 0x87, 0x87, 0xd3, 0x48, 0x8a, 0x5e, 0x65, 0x13, 0xe8, 0x28, 0x0f, 0xb1, 0x89, 0x66, 0xb6, 0xa3, 0xeb, 0x46, 0xbd, 0x69, 0x5a, 0x0d, 0xfd, 0x3b, 0x4d, 0x5e, 0x67, 0x13, 0x4d, 0x66, 0xa4, 0x22, 0x5f, 0xc9, 0x1b, 0xe3, 0xcd, 0x78, 0x6c, 0x93, 0x43, 0xdd, 0x50, 0xf0, 0x89, 0xbc, 0x99, 0x91, 0x7b, 0xe3, 0x8d, 0x2e, 0x91, 0x42, 0x57, 0x67, 0x39, 0x8e, 0xa6, 0x60, 0xf5, 0x69, 0xdc, 0xe3, 0x5b, 0xec, 0xd6, 0xb9, 0xc3, 0xbf, 0xcb, 0x8d, 0x8d, 0x5d, 0xf1, 0x16, 0x7f, 0x00, 0x0f, 0xc2, 0xb8, 0xa5, 0x64, 0xc1, 0x84, 0x6e, 0x7f, 0x08, 0xff, 0x3f, 0x1b, 0x65, 0xb0, 0xae, 0x54, 0xb1, 0xa6, 0x54, 0x4e, 0x06, 0x2f, 0x26, 0x52, 0xf9, 0x37, 0x39, 0x28, 0xab, 0xb6, 0xdb, 0x22, 0x9d, 0xe8, 0xff, 0x01, 0x53, 0xad, 0x7c, 0x04, 0x5f, 0xcc, 0x50, 0xef, 0x13, 0xec, 0x3d, 0x86, 0xc6, 0xbb, 0x92, 0x9b, 0xc6, 0x73, 0xa3, 0x76, 0x6c, 0x4c, 0x23, 0xc8, 0x12, 0x32, 0xe0, 0xd9, 0xbb, 0x2a, 0x1e, 0x73, 0xc9, 0x70, 0xd2, 0xcc, 0x71, 0xa7, 0x34, 0x9c, 0x73, 0xfe, 0xcf, 0x91, 0xd9, 0x9c, 0x22, 0xd2, 0xf8, 0xdf, 0x73, 0xca, 0xbb, 0x92, 0x67, 0x76, 0xca, 0xbb, 0x2a, 0x9e, 0xe6, 0x94, 0xc3, 0x9f, 0xc2, 0x56, 0xcb, 0xeb, 0xa6, 0x7d, 0x6b, 0x3a, 0x5c, 0x8e, 0xdc, 0x53, 0xf7, 0x3d, 0xea, 0xd5, 0xa5, 0xef, 0x0e, 0xce, 0x1d, 0x7a, 0xd1, 0x3f, 0xdd, 0x6b, 0x79, 0xdd, 0xfd, 0xf8, 0x8f, 0x52, 0x1e, 0x38, 0xed, 0xce, 0xfe, 0xb9, 0x17, 0xfe, 0xc8, 0x45, 0xfc, 0x42, 0xe5, 0x91, 0xdd, 0x73, 0x5e, 0x1f, 0x9c, 0x2e, 0x70, 0xd9, 0x27, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x6a, 0x76, 0xd7, 0x61, 0x23, 0x00, 0x00, }, // uber/cadence/admin/v1/history.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2e, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x4f, 0x4c, 0xc9, 0xcd, 0xcc, 0xd3, 0x2f, 0x33, 0xd4, 0xcf, 0xc8, 0x2c, 0x2e, 0xc9, 0x2f, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x05, 0x29, 0xd2, 0x83, 0x2a, 0xd2, 0x03, 0x2b, 0xd2, 0x2b, 0x33, 0x54, 0xf2, 0xe4, 0x12, 0x0a, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0xf3, 0x80, 0x28, 0xf7, 0x2c, 0x49, 0xcd, 0x15, 0x92, 0xe4, 0xe2, 0x48, 0x2d, 0x4b, 0xcd, 0x2b, 0x89, 0xcf, 0x4c, 0x91, 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e, 0x62, 0x07, 0xf3, 0x3d, 0x53, 0x84, 0x24, 0xb8, 0xd8, 0xcb, 0x20, 0x1a, 0x24, 0x98, 0x20, 0x32, 0x50, 0xae, 0x52, 0x09, 0x17, 0x1f, 0xaa, 0x51, 0x42, 0x8a, 0x5c, 0x3c, 0x49, 0x45, 0x89, 0x79, 0xc9, 0x19, 0xf1, 0x25, 0xf9, 0xd9, 0xa9, 0x79, 0x60, 0xa3, 0x78, 0x82, 0xb8, 0x21, 0x62, 0x21, 0x20, 0x21, 0x21, 0x7b, 0x2e, 0xd6, 0xcc, 0x92, 0xd4, 0xdc, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x4d, 0x3d, 0xac, 0xce, 0xd4, 0xc3, 0x74, 0x63, 0x10, 0x44, 0x9f, 0x93, 0x79, 0x94, 0x69, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0x72, 0x48, 0xe8, 0x66, 0xa6, 0xe4, 0xe8, 0xa7, 0xe7, 0xeb, 0x83, 0xfd, 0x0f, 0x0f, 0x16, 0x6b, 0x30, 0xa3, 0xcc, 0x30, 0x89, 0x0d, 0x2c, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x44, 0x14, 0xd7, 0xd4, 0x3e, 0x01, 0x00, 0x00, }, } ================================================ FILE: .gen/proto/shared/v1/tasklist.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/shared/v1/tasklist.proto package sharedv1 import ( fmt "fmt" math "math" proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // TaskSource is the source from which a task was produced. type TaskSource int32 const ( TaskSource_TASK_SOURCE_INVALID TaskSource = 0 // Task produced by history service. TaskSource_TASK_SOURCE_HISTORY TaskSource = 1 // Task produced from matching db backlog. TaskSource_TASK_SOURCE_DB_BACKLOG TaskSource = 2 ) var TaskSource_name = map[int32]string{ 0: "TASK_SOURCE_INVALID", 1: "TASK_SOURCE_HISTORY", 2: "TASK_SOURCE_DB_BACKLOG", } var TaskSource_value = map[string]int32{ "TASK_SOURCE_INVALID": 0, "TASK_SOURCE_HISTORY": 1, "TASK_SOURCE_DB_BACKLOG": 2, } func (x TaskSource) String() string { return proto.EnumName(TaskSource_name, int32(x)) } func (TaskSource) EnumDescriptor() ([]byte, []int) { return fileDescriptor_e97215b1a3d16713, []int{0} } func init() { proto.RegisterEnum("uber.cadence.shared.v1.TaskSource", TaskSource_name, TaskSource_value) } func init() { proto.RegisterFile("uber/cadence/shared/v1/tasklist.proto", fileDescriptor_e97215b1a3d16713) } var fileDescriptor_e97215b1a3d16713 = []byte{ // 193 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x2f, 0xce, 0x48, 0x2c, 0x4a, 0x4d, 0xd1, 0x2f, 0x33, 0xd4, 0x2f, 0x49, 0x2c, 0xce, 0xce, 0xc9, 0x2c, 0x2e, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x03, 0x29, 0xd3, 0x83, 0x2a, 0xd3, 0x83, 0x28, 0xd3, 0x2b, 0x33, 0xd4, 0x8a, 0xe2, 0xe2, 0x0a, 0x49, 0x2c, 0xce, 0x0e, 0xce, 0x2f, 0x2d, 0x4a, 0x4e, 0x15, 0x12, 0xe7, 0x12, 0x0e, 0x71, 0x0c, 0xf6, 0x8e, 0x0f, 0xf6, 0x0f, 0x0d, 0x72, 0x76, 0x8d, 0xf7, 0xf4, 0x0b, 0x73, 0xf4, 0xf1, 0x74, 0x11, 0x60, 0x40, 0x97, 0xf0, 0xf0, 0x0c, 0x0e, 0xf1, 0x0f, 0x8a, 0x14, 0x60, 0x14, 0x92, 0xe2, 0x12, 0x43, 0x96, 0x70, 0x71, 0x8a, 0x77, 0x72, 0x74, 0xf6, 0xf6, 0xf1, 0x77, 0x17, 0x60, 0x72, 0x72, 0x3e, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0xa3, 0x4c, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x51, 0xdc, 0xac, 0x97, 0x9e, 0x9a, 0xa7, 0x0f, 0x76, 0x25, 0xc2, 0xf9, 0xd6, 0x10, 0x56, 0x99, 0x61, 0x12, 0x1b, 0x58, 0xc6, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xc3, 0xa0, 0xac, 0xa3, 0xe8, 0x00, 0x00, 0x00, } ================================================ FILE: .gen/proto/shared/v1/tasklist.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/shared/v1/tasklist.proto package sharedv1 var yarpcFileDescriptorClosuree97215b1a3d16713 = [][]byte{ // uber/cadence/shared/v1/tasklist.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x2f, 0xce, 0x48, 0x2c, 0x4a, 0x4d, 0xd1, 0x2f, 0x33, 0xd4, 0x2f, 0x49, 0x2c, 0xce, 0xce, 0xc9, 0x2c, 0x2e, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x03, 0x29, 0xd3, 0x83, 0x2a, 0xd3, 0x83, 0x28, 0xd3, 0x2b, 0x33, 0xd4, 0x8a, 0xe2, 0xe2, 0x0a, 0x49, 0x2c, 0xce, 0x0e, 0xce, 0x2f, 0x2d, 0x4a, 0x4e, 0x15, 0x12, 0xe7, 0x12, 0x0e, 0x71, 0x0c, 0xf6, 0x8e, 0x0f, 0xf6, 0x0f, 0x0d, 0x72, 0x76, 0x8d, 0xf7, 0xf4, 0x0b, 0x73, 0xf4, 0xf1, 0x74, 0x11, 0x60, 0x40, 0x97, 0xf0, 0xf0, 0x0c, 0x0e, 0xf1, 0x0f, 0x8a, 0x14, 0x60, 0x14, 0x92, 0xe2, 0x12, 0x43, 0x96, 0x70, 0x71, 0x8a, 0x77, 0x72, 0x74, 0xf6, 0xf6, 0xf1, 0x77, 0x17, 0x60, 0x72, 0x32, 0x8f, 0x32, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x47, 0x71, 0xa7, 0x5e, 0x7a, 0x6a, 0x9e, 0x3e, 0xd8, 0x65, 0x08, 0x27, 0x5b, 0x43, 0x58, 0x65, 0x86, 0x49, 0x6c, 0x60, 0x19, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x73, 0x06, 0x1c, 0xdc, 0x00, 0x00, 0x00, }, } ================================================ FILE: .gen/proto/shared/v1/workflow.pb.go ================================================ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: uber/cadence/shared/v1/workflow.proto package sharedv1 import ( fmt "fmt" math "math" proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type WorkflowState int32 const ( WorkflowState_WORKFLOW_STATE_INVALID WorkflowState = 0 WorkflowState_WORKFLOW_STATE_CREATED WorkflowState = 1 WorkflowState_WORKFLOW_STATE_RUNNING WorkflowState = 2 WorkflowState_WORKFLOW_STATE_COMPLETED WorkflowState = 3 WorkflowState_WORKFLOW_STATE_ZOMBIE WorkflowState = 4 WorkflowState_WORKFLOW_STATE_VOID WorkflowState = 5 WorkflowState_WORKFLOW_STATE_CORRUPTED WorkflowState = 6 ) var WorkflowState_name = map[int32]string{ 0: "WORKFLOW_STATE_INVALID", 1: "WORKFLOW_STATE_CREATED", 2: "WORKFLOW_STATE_RUNNING", 3: "WORKFLOW_STATE_COMPLETED", 4: "WORKFLOW_STATE_ZOMBIE", 5: "WORKFLOW_STATE_VOID", 6: "WORKFLOW_STATE_CORRUPTED", } var WorkflowState_value = map[string]int32{ "WORKFLOW_STATE_INVALID": 0, "WORKFLOW_STATE_CREATED": 1, "WORKFLOW_STATE_RUNNING": 2, "WORKFLOW_STATE_COMPLETED": 3, "WORKFLOW_STATE_ZOMBIE": 4, "WORKFLOW_STATE_VOID": 5, "WORKFLOW_STATE_CORRUPTED": 6, } func (x WorkflowState) String() string { return proto.EnumName(WorkflowState_name, int32(x)) } func (WorkflowState) EnumDescriptor() ([]byte, []int) { return fileDescriptor_7ca73ea33aecbb95, []int{0} } func init() { proto.RegisterEnum("uber.cadence.shared.v1.WorkflowState", WorkflowState_name, WorkflowState_value) } func init() { proto.RegisterFile("uber/cadence/shared/v1/workflow.proto", fileDescriptor_7ca73ea33aecbb95) } var fileDescriptor_7ca73ea33aecbb95 = []byte{ // 239 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x2f, 0xce, 0x48, 0x2c, 0x4a, 0x4d, 0xd1, 0x2f, 0x33, 0xd4, 0x2f, 0xcf, 0x2f, 0xca, 0x4e, 0xcb, 0xc9, 0x2f, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x03, 0x29, 0xd3, 0x83, 0x2a, 0xd3, 0x83, 0x28, 0xd3, 0x2b, 0x33, 0xd4, 0xba, 0xcc, 0xc8, 0xc5, 0x1b, 0x0e, 0x55, 0x1a, 0x5c, 0x92, 0x58, 0x92, 0x2a, 0x24, 0xc5, 0x25, 0x16, 0xee, 0x1f, 0xe4, 0xed, 0xe6, 0xe3, 0x1f, 0x1e, 0x1f, 0x1c, 0xe2, 0x18, 0xe2, 0x1a, 0xef, 0xe9, 0x17, 0xe6, 0xe8, 0xe3, 0xe9, 0x22, 0xc0, 0x80, 0x45, 0xce, 0x39, 0xc8, 0xd5, 0x31, 0xc4, 0xd5, 0x45, 0x80, 0x11, 0x8b, 0x5c, 0x50, 0xa8, 0x9f, 0x9f, 0xa7, 0x9f, 0xbb, 0x00, 0x93, 0x90, 0x0c, 0x97, 0x04, 0xba, 0x3e, 0x7f, 0xdf, 0x00, 0x1f, 0x57, 0x90, 0x4e, 0x66, 0x21, 0x49, 0x2e, 0x51, 0x34, 0xd9, 0x28, 0x7f, 0x5f, 0x27, 0x4f, 0x57, 0x01, 0x16, 0x21, 0x71, 0x2e, 0x61, 0x34, 0xa9, 0x30, 0x7f, 0x4f, 0x17, 0x01, 0x56, 0xac, 0x26, 0x06, 0x05, 0x85, 0x06, 0x80, 0x4c, 0x64, 0x73, 0x72, 0x3e, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0xa3, 0x4c, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x51, 0x42, 0x4b, 0x2f, 0x3d, 0x35, 0x4f, 0x1f, 0x1c, 0x3e, 0x88, 0x80, 0xb3, 0x86, 0xb0, 0xca, 0x0c, 0x93, 0xd8, 0xc0, 0x32, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdf, 0x33, 0x81, 0xb3, 0x62, 0x01, 0x00, 0x00, } ================================================ FILE: .gen/proto/shared/v1/workflow.pb.yarpc.go ================================================ // Code generated by protoc-gen-yarpc-go. DO NOT EDIT. // source: uber/cadence/shared/v1/workflow.proto package sharedv1 var yarpcFileDescriptorClosure7ca73ea33aecbb95 = [][]byte{ // uber/cadence/shared/v1/workflow.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x4d, 0x4a, 0x2d, 0xd2, 0x4f, 0x4e, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 0x2f, 0xce, 0x48, 0x2c, 0x4a, 0x4d, 0xd1, 0x2f, 0x33, 0xd4, 0x2f, 0xcf, 0x2f, 0xca, 0x4e, 0xcb, 0xc9, 0x2f, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x03, 0x29, 0xd3, 0x83, 0x2a, 0xd3, 0x83, 0x28, 0xd3, 0x2b, 0x33, 0xd4, 0xba, 0xcc, 0xc8, 0xc5, 0x1b, 0x0e, 0x55, 0x1a, 0x5c, 0x92, 0x58, 0x92, 0x2a, 0x24, 0xc5, 0x25, 0x16, 0xee, 0x1f, 0xe4, 0xed, 0xe6, 0xe3, 0x1f, 0x1e, 0x1f, 0x1c, 0xe2, 0x18, 0xe2, 0x1a, 0xef, 0xe9, 0x17, 0xe6, 0xe8, 0xe3, 0xe9, 0x22, 0xc0, 0x80, 0x45, 0xce, 0x39, 0xc8, 0xd5, 0x31, 0xc4, 0xd5, 0x45, 0x80, 0x11, 0x8b, 0x5c, 0x50, 0xa8, 0x9f, 0x9f, 0xa7, 0x9f, 0xbb, 0x00, 0x93, 0x90, 0x0c, 0x97, 0x04, 0xba, 0x3e, 0x7f, 0xdf, 0x00, 0x1f, 0x57, 0x90, 0x4e, 0x66, 0x21, 0x49, 0x2e, 0x51, 0x34, 0xd9, 0x28, 0x7f, 0x5f, 0x27, 0x4f, 0x57, 0x01, 0x16, 0x21, 0x71, 0x2e, 0x61, 0x34, 0xa9, 0x30, 0x7f, 0x4f, 0x17, 0x01, 0x56, 0xac, 0x26, 0x06, 0x05, 0x85, 0x06, 0x80, 0x4c, 0x64, 0x73, 0x32, 0x8f, 0x32, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x47, 0x09, 0x21, 0xbd, 0xf4, 0xd4, 0x3c, 0x7d, 0x70, 0x98, 0x20, 0x02, 0xcb, 0x1a, 0xc2, 0x2a, 0x33, 0x4c, 0x62, 0x03, 0xcb, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xff, 0xc3, 0x37, 0x89, 0x56, 0x01, 0x00, 0x00, }, } ================================================ FILE: .gitar/rules/assign-maintainer-reviewer.md ================================================ --- title: Assign Maintainer Reviewer for External Contributors description: Automatically assigns a maintainer to review PRs from non-maintainers when: Pull request is opened actions: - Assign reviewer in round-robin rotation --- # Auto-assign Maintainer Reviewer When a pull request is opened by someone not listed in `.github/CODEOWNERS`, assign @natemort plus one reviewer from the following list in round-robin rotation: - @c-warren - @fimanishi - @neil-xie - @zawadzkidiana - @shijiesheng - @abhishekj720 You must modify the assignees field on the Pull Request - being a reviewer is not sufficient. Do not assign a reviewer if the PR author is already a maintainer listed in CODEOWNERS. ================================================ FILE: .gitar/rules/issue-linking-required.md ================================================ --- title: GitHub Issue Linking Requirement description: Ensures PRs link to cadence-workflow org issues with smart exemptions when: PR is opened or updated actions: Check for issue link and report requirement status --- # GitHub Issue Linking Requirement This rule ensures that pull requests link to GitHub issues from the cadence-workflow organization, providing better tracking, context, and historical linkage between code changes and their motivations. ## Overview When evaluating a pull request: 1. **Check skip conditions first** - Some PRs are exempt from this requirement 2. **Search for issue links** - Look for references in the PR description body 3. **Validate organization** - Ensure links point to cadence-workflow org 4. **Report status** - Approve silently if valid link found, or provide helpful feedback if missing ## Skip Conditions The issue linking requirement is automatically skipped when **ANY** of these conditions are true: ### 1. Small Changes (< 50 lines) - Count total lines changed: additions + deletions - PRs with fewer than 50 total lines changed are exempt - Example: Quick typo fixes, minor documentation updates ### 2. Maintenance Commits PRs with titles starting with these conventional commit prefixes are exempt: - `docs:` - Documentation changes - `chore:` - Maintenance tasks, dependency updates - `ci:` - CI/CD configuration changes - `style:` - Code formatting or style changes - `revert:` - Pull Request Reverts Examples: - ✅ Skip: `docs: Update installation instructions` - ✅ Skip: `chore: Bump dependency versions` - ✅ Skip: `ci: Add new workflow for release` - ✅ Skip: `style: Apply consistent formatting` - ❌ Don't skip: `feat: Add new authentication method` - ❌ Don't skip: `fix: Resolve memory leak in worker` ### 3. Bot-Authored PRs - PRs where the author username ends with `[bot]` - Examples: `dependabot[bot]`, `renovate[bot]`, `github-actions[bot]` ## Issue Link Detection Search the **PR description body only** for issue references in these formats: ### Accepted Formats 1. **Same repository short format:** - `#123` - Example: "Fixes #456" - Example: "Related to #789" 2. **Cross-repository format:** - `cadence-workflow/other-repo#123` - Example: "Addresses cadence-workflow/web#45" - Must reference the `cadence-workflow` organization 3. **Full URL format:** - `https://github.com/cadence-workflow/cadence/issues/123` - `https://github.com/cadence-workflow/other-repo/issues/456` - Must be from the `cadence-workflow` organization ### Not Accepted - ❌ Issues from other organizations: `other-org/repo#123` - ❌ Issue links in code comments or file content - ❌ Issue links in commit messages (must be in PR description) ## Organization Validation **CRITICAL**: All issue links must reference the `cadence-workflow` organization. How to validate: - Short format (`#123`) is implicitly within cadence-workflow if the PR is in a cadence-workflow repo - Cross-repo format must explicitly include `cadence-workflow/` - Full URLs must contain `github.com/cadence-workflow/` Examples: - ✅ Valid: `#123` (in cadence-workflow repo) - ✅ Valid: `cadence-workflow/web#45` - ✅ Valid: `https://github.com/cadence-workflow/cadence/issues/789` - ❌ Invalid: `external-org/repo#123` - ❌ Invalid: `https://github.com/other-org/repo/issues/123` ## Validation Logic ### Step 1: Check Skip Conditions ``` 1. Get PR diff stats 2. Calculate total_lines = additions + deletions 3. If total_lines < 50, skip and approve 4. Get PR title 5. If title starts with "docs:", "chore:", "ci:", or "style:", skip and approve 6. Get PR author username 7. If author ends with "[bot]", skip and approve ``` ### Step 2: Search for Issue Links ``` 1. Get PR description body 2. Search for patterns: - #\d+ - cadence-workflow/[\w-]+#\d+ - https://github.com/cadence-workflow/[\w-]+/issues/\d+ 3. For each match, extract organization name 4. Validate organization is "cadence-workflow" ``` ### Step 3: Report Status Report as part of all rules. ## Edge Cases ### Multiple Issue Links - If the PR description contains multiple valid issue links, the rule passes - Use the first valid link found for reporting purposes ### Issue Links in Code - Issue links in code comments or file content do not count - Only links in the PR description body are evaluated ### Mixed Formats - A PR can reference issues in multiple formats - The rule passes if ANY format contains a valid cadence-workflow issue link ### Case Sensitivity - Organization name matching is case-insensitive - `cadence-workflow`, `Cadence-Workflow`, and `CADENCE-WORKFLOW` are all valid ## Integration Notes This rule is complementary to the existing `pr-description-quality.md` rule: - **Description quality**: Checks content structure and completeness - **Issue linking**: Checks for specific requirement (issue reference) - Both rules can run on the same PR without conflicts - Both provide guidance without blocking PRs ================================================ FILE: .gitar/rules/pr-description-quality.md ================================================ --- title: PR Description Quality Standards description: Ensures PR descriptions meet Cadence quality criteria using guidance from PR template when: PR description is created or updated actions: Read PR template for guidance then report requirement status --- # PR Description Quality Standards When evaluating a pull request description: 1. **Read the PR template guidance** at `.github/pull_request_guidance.md` to understand the expected guidance for each section 2. Apply that guidance to evaluate the current PR description 3. Provide recommendations for how to improve the description. ## Core Principle: Why Not How From https://cbea.ms/git-commit/#why-not-how: - **"A diff shows WHAT changed, but only the description can explain WHY"** - Focus on: the problem being solved, the reasoning behind the solution, context - The code itself documents HOW - the PR description documents WHY ## Evaluation Criteria ### Required Sections (must exist with substantive content per PR template guidance) 1. **What changed?** - 1-2 line summary of WHAT changed technically - Focus on key modification, not implementation details - Template has good/bad examples 2. **Why?** - Full context and motivation - What problem does this solve? What's the use case? - What's the impact if we don't make this change? - **CRITICAL**: Technical rationale required for ALL changes (not just large ones) - Must explain WHY this implementation approach was chosen 3. **How did you test it?** - Concrete, copyable commands with exact invocations - ✅ GOOD: `go test -v ./common/types/mapper/proto -run TestFailoverDomainRequest` - ❌ BAD: "Tested locally" or "See tests/foo_test.go" - For integration tests: setup steps + commands - For canary: which canary, environment, results 4. **Potential risks** - Backward/forward compatibility concerns? - Performance impact? What could break? Safe to rollback? 5. **Release notes** - If this completes a user-facing feature, describe it - Skip for: incremental work, internal refactors, partial implementations 6. **Documentation Changes** - Config changes affecting operation-guide? - New features needing user docs? - Only mark N/A if certain no docs affected ### Quality Checks - **Skip obvious things** - Don't flag items clear from folder structure - **Skip trivial refactors** - Minor formatting/style changes don't need deep rationale - **Don't check automated items** - Issue links, CI, linting are automated ## FORBIDDEN - Never Include - ❌ "Issues Found", "Testing Evidence Quality", "Documentation Reasoning", "Summary" sections - ❌ "Note:" paragraphs or explanatory text outside recommendations - ❌ Grouping recommendations by type ## Section Names (Use EXACT Brackets) - **[What changed?]** - **[Why?]** - **[How did you test it?]** - **[Potential risks]** - **[Release notes]** - **[Documentation Changes]** ================================================ FILE: .gitattributes ================================================ .gen linguist-generated=true ================================================ FILE: .github/CODEOWNERS ================================================ # This is a comment. # Each line is a file pattern followed by one or more owners. # Order is important; the last matching pattern takes the most # precedence. * @adhityamamallan @macrotim @Assem-Uber @abhishekj720 @fimanishi @bowenxia @timl3136 @eleonoradgr @gazi-yestemirova @arzonus @c-warren @natemort @Shaddoll @neil-xie @davidporter-id-au @shijiesheng @jakobht @sankari165 @dkrotx @demirkayaender ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- ### Description A clear and concise description of what the bug is. ### Steps to Reproduce / How to Trigger Provide a minimal set of steps or a script to reproduce the issue. ### Expected Behavior What you expected to happen. ### Actual Behavior What actually happened. ### Logs / Screenshots Relevant logs, stack traces, workflow history, or screenshots. ### Environment - **Cadence server version**: Server version or build SHA - **Cadence SDK language and version** (if applicable): Language and SDK version - **Cadence web version** (if applicable): Web UI version - **DB & version**: Database type and version - **Scale**: Cluster size, workload volume, or other scale details ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: feature assignees: '' --- Use this template for feature, improvement, or refractoring requests. ### Description What you want to happen and why. ### Is this a breaking change? - [ ] Yes - [ ] No **If yes, explain why:** ### Scope of the feature (server, specific client, all clients) Clarify where this applies. ================================================ FILE: .github/ISSUE_TEMPLATE/help_support.md ================================================ --- name: QA/Help/Support about: Please use [Discussion](https://github.com/cadence-workflow/cadence/discussions) or [StackOverflow](https://stackoverflow.com/questions/tagged/cadence-workflow) for QA/Help/Support title: '' labels: '' assignees: '' --- Please use [Discussion](https://github.com/cadence-workflow/cadence/discussions) or [StackOverflow](https://stackoverflow.com/questions/tagged/cadence-workflow) for QA/Help/Support. Do NOT use issue for this. ================================================ FILE: .github/dco.yml ================================================ require: members: false ================================================ FILE: .github/pull_request_guidance.md ================================================ **What changed?** **Why?** **How did you test it?** **Potential risks** **Release notes** **Documentation Changes** ================================================ FILE: .github/pull_request_template.md ================================================ **What changed?** **Why?** **How did you test it?** **Potential risks** **Release notes** **Documentation Changes** ================================================ FILE: .github/workflows/breaking_change_pr_template.md ================================================ **Detailed Description** [In-depth description of the changes made to the schema or interfaces, specifying new fields, removed fields, or modified data structures] **Impact Analysis** - **Backward Compatibility**: [Analysis of backward compatibility] - **Forward Compatibility**: [Analysis of forward compatibility] **Testing Plan** - **Unit Tests**: [Do we have unit test covering the change?] - **Persistence Tests**: [If the change is related to a data type which is persisted, do we have persistence tests covering the change?] - **Integration Tests**: [Do we have integration test covering the change?] - **Compatibility Tests**: [Have we done tests to test the backward and forward compatibility?] **Rollout Plan** - What is the rollout plan? - Does the order of deployment matter? - Is it safe to rollback? Does the order of rollback matter? - Is there a kill switch to mitigate the impact immediately? ================================================ FILE: .github/workflows/breaking_change_reminder.yml ================================================ name: Workflow for Breaking Change Reminder on: pull_request: paths: - schema/** - client/frontend/interface.go - client/history/interface.go - client/matching/interface.go - client/admin/interface.go jobs: breaking-change-pr-template-reminder: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Fail if PR description is missing breaking change template if: steps.pr-changes.outputs.changes != '[]' run: | PR_NUMBER=${{ github.event.pull_request.number }} PR_URL="https://api.github.com/repos/${{ github.repository }}/pulls/${PR_NUMBER}" BODY=$(curl $PR_URL | jq '.body') CHECKLIST=( "Detailed Description" "Impact Analysis" "Testing Plan" "Rollout Plan" ) TEMPLATE=$(cat .github/workflows/breaking_change_pr_template.md) for i in "${CHECKLIST[@]}"; do if [[ "$BODY" == *"$i"* ]]; then continue else echo "Potential breaking changes detected! Please update the PR description to include following template:" echo "---" echo "$TEMPLATE" echo "---" exit 1 fi done ================================================ FILE: .github/workflows/ci-checks.yml ================================================ name: CI Checks on: push: pull_request: jobs: idl-submodule-points-to-master: name: IDL submodule points to master runs-on: ubuntu-latest # uses ubuntu as runner steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true # ensures git submodules are intialized and updated fetch-depth: 0 # get full history for branch checking - name: Check IDL submodule status (must point to master) run: make .idl-status golang-unit-test: name: Golang unit test runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # get full history for branch checking - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run unit test uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run unit-test bash -c "make .just-build && make cover_profile && ./scripts/github_actions/gen_coverage_metadata.sh .build/coverage/metadata.txt" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-unit-test-coverage path: | .build/coverage/*.out .build/coverage/metadata.txt golangci-lint-validate-code-is-clean: name: Golangci lint validate code is clean runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # get full history for branch checking - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run golint run: docker compose -f docker/github_actions/docker-compose.yml run coverage-report bash -c "./scripts/github_actions/golint.sh" golang-integration-test-with-cassandra: name: Golang integration test with cassandra runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # get full history for branch checking - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run integration profile for cassandra uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-cassandra bash -c "make .just-build && make cover_integration_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-cassandra-integration-coverage path: .build/coverage/*.out golang-integration-test-with-cassandra-running-history-queue-v2: name: Golang integration test with running history queue v2 runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run integration profile for cassandra running history queue v2 uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-cassandra-queue-v2 bash -c "make .just-build && make cover_integration_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-cassandra-running-history-queue-v2-integration-coverage path: .build/coverage/*.out golang-integration-test-with-cassandra-running-history-queue-v2-alert: name: Golang integration test with running history queue v2 with alert runs-on: ubuntu-latest continue-on-error: true steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run integration profile for cassandra running history queue v2 with alert uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-cassandra-queue-v2-alert bash -c "make .just-build && make cover_integration_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-cassandra-running-history-queue-v2-alert-integration-coverage path: .build/coverage/*.out golang-integration-test-with-cassandra-and-elasticsearch-v7: name: Golang integration test with cassandra and elasticsearch v7 runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # get full history for branch checking - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run integration profile for cassandra and elasticsearch v7 uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose-es7.yml run integration-test-cassandra bash -c "make .just-build && make cover_integration_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-cassandra-and-elasticsearch-v7-integration-coverage path: .build/coverage/*.out golang-integration-test-with-cassandra-and-pinot: name: Golang integration test with cassandra and pinot runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # get full history for branch checking - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run integration test with cassandra and pinot uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose-pinot.yml run integration-test-cassandra-pinot bash -c "mkdir -p .build/coverage && make .just-build && go test -timeout 600s -run ^TestPinotIntegrationSuite -tags pinotintegration -count 1 -v -coverprofile=.build/coverage/pinot.out github.com/uber/cadence/host" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-cassandra-pinot-integration-coverage path: .build/coverage/*.out golang-integration-test-with-cassandra-with-opensearch-v2: name: Golang integration test with cassandra with opensearch v2 runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # get full history for branch checking - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run integration profile for cassandra and opensearch v2 uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose-opensearch2.yml run integration-test-cassandra bash -c "make .just-build && make cover_integration_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-cassandra-and-opensearch-v2-integration-coverage path: .build/coverage/*.out golang-integration-ndc-test-with-cassandra: name: Golang integration ndc test with cassandra runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # get full history for branch checking - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run ndc profile for cassandra uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-ndc-cassandra bash -c "make .just-build && make cover_ndc_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-cassandra-ndc-integration-coverage path: .build/coverage/*.out golang-integration-test-with-mysql: name: Golang integration test with mysql runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # get full history for branch checking - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run integration profile for mysql uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-mysql bash -c "make .just-build && make cover_integration_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-mysql-integration-coverage path: .build/coverage/*.out golang-integration-ndc-test-with-mysql: name: Golang integration ndc test with mysql runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # get full history for branch checking - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run ndc profile for mysql uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-ndc-mysql bash -c "make .just-build && make cover_ndc_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-mysql-ndc-integration-coverage path: .build/coverage/*.out golang-integration-test-with-postgres: name: Golang integration test with postgres runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # get full history for branch checking - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run integration profile for postgres uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-postgres bash -c "make .just-build && make cover_integration_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-postgres-integration-coverage path: .build/coverage/*.out golang-integration-test-with-sqlite: name: Golang integration test with sqlite runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run integration profile for sqlite uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-sqlite bash -c "make cover_integration_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-sqlite-integration-coverage path: .build/coverage/*.out golang-integration-ndc-test-with-postgres: name: Golang integration ndc test with postgres runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run ndc profile for postgres uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-ndc-postgres bash -c "make .just-build && make cover_ndc_profile" - name: Upload coverage artifacts if: always() uses: actions/upload-artifact@v4 with: name: go-postgres-ndc-integration-coverage path: .build/coverage/*.out golang-async-wf-integration-test-with-kafka: name: Golang async wf integration test with kafka runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run async wf integration test with kafka uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-async-wf bash -c "make .just-build && go test -timeout 60s -run ^TestAsyncWFIntegrationSuite -tags asyncwfintegration -count 1 -v github.com/uber/cadence/host" golang-integration-test-with-etcd: if: github.event_name == 'pull_request' name: Golang integration test with etcd runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run integration test with etcd uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 30 command: | docker compose -f docker/github_actions/docker-compose.yml run integration-test-with-etcd bash -c "make .just-build && make integration_tests_etcd" ================================================ FILE: .github/workflows/codecov-on-master.yml ================================================ name: Workflow for Codecov integration on: push jobs: codecov: if: github.repository == 'cadence-workflow/cadence' runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v4 with: go-version: 1.23.4 cache: false - name: Download dependencies run: go mod download - name: Test and generate coverage report run: make cover_profile - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.5.0 # https://github.com/codecov/codecov-action with: file: .build/coverage/unit_cover.out exclude: ./ token: ${{ secrets.CODECOV_TOKEN }} slug: cadence-workflow/cadence ================================================ FILE: .github/workflows/codecov-on-pr.yml ================================================ name: Workflow for Codecov integration on: pull_request jobs: codecov: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v4 with: go-version: 1.23.4 cache: false - name: Download dependencies run: go mod download - name: Test and generate coverage report run: make cover_profile - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.5.0 # https://github.com/codecov/codecov-action with: file: .build/coverage/unit_cover.out exclude: ./ token: ${{ secrets.CODECOV_TOKEN }} slug: cadence-workflow/cadence ================================================ FILE: .github/workflows/docker_publish.yml ================================================ name: Docker Build & Push on: pull_request: push: branches: - master release: types: - released jobs: # This job exposes image_tag and push_enabled variables for other jobs. # Other jobs depends on this. see needs: meta in other jobs. meta: if: github.repository == 'cadence-workflow/cadence' runs-on: ubuntu-latest outputs: image_tag: ${{ steps.determine-image-tag.outputs.image_tag }} push_enabled: ${{ steps.determine-push.outputs.enabled }} steps: # this step determines the image tag based on the event that triggered the workflow. # it sets the image_tag output variable to either "master" or the release version. - name: Determine image tag id: determine-image-tag run: | if [[ "${{ github.event_name }}" == "push" ]]; then echo "image_tag=master" >> $GITHUB_OUTPUT elif [[ "${{ github.event_name }}" == "release" ]]; then echo "image_tag=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT else echo "image_tag=test" >> $GITHUB_OUTPUT fi # This step determines if push is enabled based on the event that triggered the workflow. Push is disabled on pull requests. - name: Determine if push is enabled id: determine-push run: | if [[ "${{ github.event_name }}" == "pull_request" ]]; then echo "enabled=false" >> $GITHUB_OUTPUT else echo "enabled=true" >> $GITHUB_OUTPUT fi push_server_image: name: Push server image to Docker Hub needs: meta runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Login to Docker Hub if: ${{ needs.meta.outputs.push_enabled == 'true' }} uses: docker/login-action@v3 with: username: ${{ secrets.CADENCE_SERVER_DOCKERHUB_USERNAME }} password: ${{ secrets.CADENCE_SERVER_DOCKERHUB_TOKEN }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64, linux/arm64 build-args: TARGET=server push: ${{ needs.meta.outputs.push_enabled == 'true' }} tags: ubercadence/server:${{ needs.meta.outputs.image_tag }} - name: Validate tag run: | echo "ubercadence/server:${{ needs.meta.outputs.image_tag }}" push_server_auto_setup_image: name: Push auto-setup image to Docker Hub needs: meta runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Login to Docker Hub if: ${{ needs.meta.outputs.push_enabled == 'true' }} uses: docker/login-action@v3 with: username: ${{ secrets.CADENCE_SERVER_DOCKERHUB_USERNAME }} password: ${{ secrets.CADENCE_SERVER_DOCKERHUB_TOKEN }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64, linux/arm64 build-args: TARGET=auto-setup push: ${{ needs.meta.outputs.push_enabled == 'true' }} tags: ubercadence/server:${{ needs.meta.outputs.image_tag }}-auto-setup - name: Validate tag run: | echo "ubercadence/server:${{ needs.meta.outputs.image_tag }}-auto-setup" push_cli_image: name: Push CLI image to Docker Hub needs: meta runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Login to Docker Hub uses: docker/login-action@v3 if: ${{ needs.meta.outputs.push_enabled == 'true' }} with: username: ${{ secrets.CADENCE_SERVER_DOCKERHUB_USERNAME }} password: ${{ secrets.CADENCE_SERVER_DOCKERHUB_TOKEN }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64, linux/arm64 build-args: TARGET=cli push: ${{ needs.meta.outputs.push_enabled == 'true' }} tags: ubercadence/cli:${{ needs.meta.outputs.image_tag }} - name: Validate tag run: | echo "ubercadence/cli:${{ needs.meta.outputs.image_tag }}" push_bench_image: name: Push bench image to Docker Hub on master pushes needs: meta runs-on: ubuntu-latest if: ${{ needs.meta.outputs.image_tag == 'master' }} steps: - name: Checkout uses: actions/checkout@v4 - name: Login to Docker Hub uses: docker/login-action@v3 if: ${{ needs.meta.outputs.push_enabled == 'true' }} with: username: ${{ secrets.CADENCE_SERVER_DOCKERHUB_USERNAME }} password: ${{ secrets.CADENCE_SERVER_DOCKERHUB_TOKEN }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64, linux/arm64 build-args: TARGET=bench tags: ubercadence/bench:master push: ${{ needs.meta.outputs.push_enabled == 'true' }} push_canary_image: name: Push canary image to Docker Hub on master pushes needs: meta runs-on: ubuntu-latest if: ${{ needs.meta.outputs.image_tag == 'master' }} steps: - name: Checkout uses: actions/checkout@v4 - name: Login to Docker Hub uses: docker/login-action@v3 if: ${{ needs.meta.outputs.push_enabled == 'true' }} with: username: ${{ secrets.CADENCE_SERVER_DOCKERHUB_USERNAME }} password: ${{ secrets.CADENCE_SERVER_DOCKERHUB_TOKEN }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64, linux/arm64 build-args: TARGET=canary tags: ubercadence/canary:master push: ${{ needs.meta.outputs.push_enabled == 'true' }} ================================================ FILE: .github/workflows/issue-validation.yml ================================================ name: Issue Validation on: issues: types: - opened - edited - labeled - unlabeled - reopened permissions: issues: write jobs: validate-issue: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Validate issue template requirements uses: actions/github-script@v7 with: script: | const fs = require("fs"); const path = require("path"); const issue = context.payload.issue; const issueNumber = issue.number; const owner = context.repo.owner; const repo = context.repo.repo; const TYPE_LABELS = ["bug", "feature", "improvement", "refractoring"]; const BUG_LABEL = "bug"; const ENHANCEMENT_LABELS = ["feature", "improvement", "refractoring"]; const NEEDS_INFO_LABEL = "needs-info"; const FALLBACK_BUG_REQUIRED = [ "Description", "Steps to Reproduce / How to Trigger", "Expected Behavior", "Actual Behavior", "Logs / Screenshots", "Environment", ]; const FALLBACK_ENHANCEMENT_REQUIRED = [ "Description", "Is this a breaking change?", "Scope of the feature (server, specific client, all clients)", ]; const normalize = (title) => title .toLowerCase() .replace(/[^\w\s]+/g, " ") .replace(/\s+/g, " ") .trim(); const labelNames = (issue.labels || []) .map((label) => (typeof label === "string" ? label : label.name)) .filter(Boolean) .map((label) => label.toLowerCase()); let hasTypeLabel = TYPE_LABELS.some((label) => labelNames.includes(label)); let isBug = labelNames.includes(BUG_LABEL); let isEnhancement = ENHANCEMENT_LABELS.some((label) => labelNames.includes(label)); let missingLabel = !hasTypeLabel; // Detect issue type from template usage and auto-apply label const detectIssueType = (body) => { if (!body) return null; const normalizedBody = body.toLowerCase(); // Check for bug template sections if (normalizedBody.includes("steps to reproduce") || normalizedBody.includes("expected behavior") || normalizedBody.includes("actual behavior")) { return "bug"; } // Check for feature template sections if (normalizedBody.includes("breaking change") || normalizedBody.includes("scope of the feature")) { return "feature"; } return null; }; const detectedType = detectIssueType(issue.body); // Auto-apply type label if missing but detected if (missingLabel && detectedType) { await github.rest.issues.addLabels({ owner, repo, issue_number: issueNumber, labels: [detectedType], }); // Refresh label check labelNames.push(detectedType); hasTypeLabel = TYPE_LABELS.some((label) => labelNames.includes(label)); isBug = labelNames.includes(BUG_LABEL); isEnhancement = ENHANCEMENT_LABELS.some((label) => labelNames.includes(label)); missingLabel = !hasTypeLabel; } const readTemplateHeadings = (fileName) => { try { const templatePath = path.join( process.env.GITHUB_WORKSPACE || process.cwd(), ".github", "ISSUE_TEMPLATE", fileName ); const contents = fs.readFileSync(templatePath, "utf8"); const headings = []; for (const line of contents.split(/\r?\n/)) { const match = line.match(/^###\s+(.+?)\s*$/); if (match?.[1]) { headings.push(match[1].trim()); } } return headings.length > 0 ? headings : null; } catch { return null; } }; const BUG_REQUIRED = readTemplateHeadings("bug_report.md") || FALLBACK_BUG_REQUIRED; const ENHANCEMENT_REQUIRED = readTemplateHeadings("feature_request.md") || FALLBACK_ENHANCEMENT_REQUIRED; const requiredSections = isBug ? BUG_REQUIRED : isEnhancement ? ENHANCEMENT_REQUIRED : []; const parseSections = (body) => { const sections = new Map(); if (!body) { return sections; } const lines = body.split(/\r?\n/); let currentKey = null; let buffer = []; const flush = () => { if (currentKey) { sections.set(currentKey, buffer.join("\n").trim()); } buffer = []; }; for (const line of lines) { const trimmed = line.trim(); const headingMatch = trimmed.match(/^#{1,6}\s*(.+?)\s*$/); const boldMatch = trimmed.match(/^\*\*(.+?)\*\*$/); const colonMatch = trimmed.match(/^(.+?):\s*$/); const title = headingMatch?.[1] || boldMatch?.[1] || colonMatch?.[1]; if (title) { flush(); currentKey = normalize(title); continue; } if (currentKey) { buffer.push(line); } } flush(); return sections; }; const hasMeaningfulContent = (content) => { if (!content) { return false; } const cleaned = content .replace(//g, "") .replace(/[\s*-]+/g, " ") .trim() .toLowerCase(); if (!cleaned) { return false; } return !["n/a", "na", "none", "tbd", "todo"].includes(cleaned); }; const sections = parseSections(issue.body || ""); const missingSections = requiredSections.filter((section) => { const content = sections.get(normalize(section)); return !hasMeaningfulContent(content); }); const ensureNeedsInfoLabelExists = async () => { try { await github.rest.issues.getLabel({ owner, repo, name: NEEDS_INFO_LABEL, }); } catch (error) { if (error.status !== 404) { throw error; } await github.rest.issues.createLabel({ owner, repo, name: NEEDS_INFO_LABEL, color: "f9d0c4", description: "Needs additional information from the reporter", }); } }; const findExistingBotComment = async () => { const comments = await github.paginate( github.rest.issues.listComments, { owner, repo, issue_number: issueNumber, per_page: 100 } ); return comments.find( (comment) => comment.user?.type === "Bot" && comment.body?.includes("") ); }; const updateOrCreateComment = async (body) => { const existing = await findExistingBotComment(); if (existing) { await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body, }); return; } await github.rest.issues.createComment({ owner, repo, issue_number: issueNumber, body, }); }; const deleteExistingComment = async () => { const existing = await findExistingBotComment(); if (!existing) { return; } await github.rest.issues.deleteComment({ owner, repo, comment_id: existing.id, }); }; if (missingLabel || missingSections.length > 0) { await ensureNeedsInfoLabelExists(); if (!labelNames.includes(NEEDS_INFO_LABEL)) { await github.rest.issues.addLabels({ owner, repo, issue_number: issueNumber, labels: [NEEDS_INFO_LABEL], }); } const templateLines = [ "Issue templates:", `- Bug report: https://github.com/${owner}/${repo}/issues/new?template=bug_report.md`, `- Feature request (use for feature, improvement, refractoring): https://github.com/${owner}/${repo}/issues/new?template=feature_request.md`, ].join("\n"); const missingLabelLine = missingLabel ? `- Add one label from: ${TYPE_LABELS.join(", ")}` : null; const missingSectionsLines = missingSections.length > 0 ? [ "Missing required sections:", ...missingSections.map((section) => `- ${section}`), ].join("\n") : null; const messageParts = [ "", "Thanks for opening this issue. To help us triage it, please provide the missing details:", missingLabelLine, missingSectionsLines, templateLines, ].filter(Boolean); await updateOrCreateComment(messageParts.join("\n")); return; } if (labelNames.includes(NEEDS_INFO_LABEL)) { await github.rest.issues.removeLabel({ owner, repo, issue_number: issueNumber, name: NEEDS_INFO_LABEL, }); } await deleteExistingComment(); ================================================ FILE: .github/workflows/replication-simulation.yml ================================================ # This workflow runs replication simulaton scenarios via GithubActions matrix strategy. # Scenarios are defined in the `simulation/replication/testdata/replication_simulation_.yaml` files. name: Replication Simulation on: push: pull_request: jobs: replication-simulation: name: Replication Simulation (${{ matrix.scenario }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: scenario: - activeactive - activeactive_child - activeactive_invalid_cluster_attribute - activeactive_same_wfid - activeactive_same_wfid_signalwithstart - activeactive_same_wfid_signalwithstart_delayed - activeactive_start_terminateifrunning - activeactive_cron - activeactive_regional_failover - activeactive_regional_failover_start_same_wfid - activeactive_regional_failover_start_same_wfid_2 - activepassive_to_activeactive - budget_manager - clusterredirection - default steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Setup Go environment uses: actions/setup-go@v5 with: go-version: '1.23.4' - name: Run simulation uses: nick-fields/retry@v3 with: max_attempts: 2 timeout_minutes: 20 command: | ./simulation/replication/run.sh --scenario ${{ matrix.scenario }} - name: Upload test logs uses: actions/upload-artifact@v4 with: name: replication-${{ matrix.scenario }}-test.log path: ./test.log ================================================ FILE: .github/workflows/semantic-pr.yml ================================================ name: Semantic Pull Request on: pull_request: types: - opened - edited - synchronize jobs: semantic-pr: name: Validate PR title follows conventional commit format runs-on: ubuntu-latest # TODO: Remove this once we commit to conventional commits continue-on-error: true steps: - name: Validate PR title id: lint_pr_title uses: amannn/action-semantic-pull-request@v6.1.1 with: # Allow standard conventional commit types types: | fix feat docs style refactor perf test chore ci build revert # TODO: Remove this once we've decided on scopes requireScope: false # Skip validation for certain labels if needed ignoreLabels: | skip-commit-format env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .gitignore ================================================ *.out *.test *.xml *.swp .idea/ *.iml *.cov *.html .gobincache/ .tmp/ .vscode/ .build/ .bin/ .cursor/ /vendor /_vendor-* /cadence .DS_Store *.log test test_xdc test_e2e test_eventsV2 test_eventsV2_xdc matching-simulator-output/ history-simulator-output/ replication-simulator-output/ **/Dockerfile.local* **/Dockerfile.light* dockerize *.crt # Executables produced by cadence repo /cadence /cadence-server /cadence-canary /cadence-bench /cadence-cassandra-tool /cadence-sql-tool /cadence-releaser /sharddistributor-canary # SQLite databases cadence.db* cadence_visibility.db* ================================================ FILE: .gitmodules ================================================ [submodule "idls"] path = idls url = https://github.com/cadence-workflow/cadence-idl.git branch = master ================================================ FILE: ADOPTERS.md ================================================ # ADOPTERS This document lists organizations, companies, and individuals that are using Cadence in production or evaluation environments. If you are using Cadence, please consider adding yourself to this list by opening a pull request or contacting the maintainers. See more instructions at the end of this page. Your support helps us understand our user community and prioritize improvements. ## Production Users ![using.png](./logos/Uber.jpg) ### Uber Technologies Uber has over 2000 use cases (Cadence domains) powered by Cadence. Here are some examples: - **Infrastructure**: service & config rollout, CD pipelines, health checks & rollbacks, DB maintenance, bad node detection / replacement, compaction, DB backup / restore, migration orchestration, ... - **ML / AI**: Agent orchestration, model training, data pipelines, fare estimation, ... - **Finance**: payments, tips, disputes, bank verification, account onboarding, promotions, discounts, partnerships, gifting, billing / invoicing, loyalty points, ... - **Product**: earner eligibility, onboarding, periodic document & compliance checks, document processing, vehicle management, inventory / catalog management, image processing, ads, repeat orders, vouchers, spending tracking, notifications, analytics, trip reports, ... There are 20+ environments running Cadence where some of them hosts 400+ domains. ![using.png](./logos/netapp-logo.jpg) ### NetApp At [NetApp](https://www.netapp.com) we use Cadence as a key component of the Instaclustr platform’s control plane architecture. Our automated maintenance system relies on Cadence to orchestrate and schedule maintenance across tens of thousands of hosts in our managed fleet. By adopting Cadence we’ve been able to offer both our engineers and customers high-throughput, fault-tolerant execution of critical fleet operations. NetApp is the intelligent data infrastructure company, providing solutions to manage any data, for any application, anywhere it is needed. Through the [Instaclustr](https://www.instaclustr.com) platform we offer fully managed Cadence, including automated provisioning of supporting infrastructure such as Apache Cassandra, Apache Kafka, and OpenSearch. ![using.png](./logos/DoorDash-logo.jpg) ### DoorDash, Inc DoorDash uses Cadence across a wide range of product and infrastructure use cases, including ETA, Fulfillment, Order Management, Storage, Catalog, Photos, Ads and more. DoorDash’ use cases take advantage of Cadence’s orchestration for long running, fault tolerant workflows. ## Evaluating / Early Adopters ## How to Add Your Organization - Fork this repository. - Edit this file (ADOPTERS.md). - Add your organization under the relevant section. ### Adding Your Logo - If you would like your organization’s logo to appear on the Cadence website, repositories and documentation: - Add your logo file (preferably in JPG/JPEG format) to the /logos directory in the repository. - Name the file using lowercase letters and hyphens (for example: acme-corp.svg). - Keep your image dimensions less than 300 pixels in width and 200 pixels in height. - Update ADOPTERS.md to include your logo and link to your organization’s website. - Logos should be submitted only by verified representatives or with permission from the organization. Open a pull request with the title: Add \ to ADOPTERS.md. ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). You can find a list of previous releases on the [github releases](https://github.com/cadence-workflow/cadence/releases) page. ## [Note] There's a new opt-in feature for autoscale of tasklist partitions. It's optional but recommended for large scale use cases. Please refer to [tasklist-partition-config.md](https://github.com/cadence-workflow/cadence/blob/master/docs/migration/tasklist-partition-config.md) for additional details on the migration and its rationale. ## [1.3.3] - 2025-08-06 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.3.3) for details ## [1.3.2] - 2025-07-03 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.3.2) for details ## [1.3.1] - 2025-06-11 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.3.1) for details ## [1.3.0] - 2025-05-14 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.3.0) for details ## [1.2.18] - 2025-04-03 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.18) for details ## [1.2.17] - 2025-03-05 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.17) for details ## [1.2.16] - 2025-02-19 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.16) for details ## [1.2.15] - 2025-01-22 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.15) for details ## [1.2.14] - 2024-11-13 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.14) for details ## [1.2.13] - 2024-09-25 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.13) for details ## [1.2.12] - 2024-08-19 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.12) for details ## [1.2.11] - 2024-07-10 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.11) for details ## [1.2.10] - 2024-06-04 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.10) for details ## [1.2.9] - 2024-05-01 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.9) for details ## [1.2.8] - 2024-03-26 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.8) for details ## [1.2.7] - 2024-02-09 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.2.7) for details ### Upgrade notes Cadence repo now has multiple submodules, the split and explanation in [PR](https://github.com/cadence-workflow/cadence/pull/5609). In principle, "plugins" are "optional" and we should not be forcing all optional dependencies on all users of any of Cadence. Splitting dependencies into choose-your-own-adventure submodules is simply good library design for the ecosystem, and it's something we should be doing more of. ## [1.2.6] - 2023-12-14 ### Added - Added range query support for Pinot json index (#5426) - Implemented GetTaskListSize method at persistence layer (#5442, #5447) - Added a framework for the Task validator service (#5446) - Added nit comments describing the Update workflow cycle (#5432) - Added log user query param (#5437) - Added CODEOWNERS file (#5453) - Added a function to evict all elements older than the cache TTL (#5464) ### Fixed - Fixed workflow replication for reset workflow (#5412) - Fixed visibility mode for admin when use Pinot visibility (#5441) - Fixed workflow started metric (#5443) - Fixed timer-fixer, unfortunately broken in 1.2.5 (#5433) - Fixed confusing comment in matching handler (#5450) ### Changed - Cassandra version is changed from 3.11 to 4.1.3 (#5461) - If your machine already has ubercadence/server:master-auto-setup image then you need to repull so it works with latest docker-compose*.yml files - Move dynamic ratelimiter to its own file (#5451) - Create and use a limiter struct instead of just passing a function (#5454) - Dynamic ratelimiter factories (#5455) - Update github action for image publishing to released (#5460) - Update matching to emit metric for tasklist backlog size (#5448) - Change variable name from SecondsSinceEpoch into EventTimeMs (#5463) ### Removed - Get rid of noisy task adding failure log in matching service (#5445) ## [1.2.5] - 2023-11-01 ### Caution: Prefer 1.2.4 or 1.2.6 if you have enabled the timer fixer. These were broken by #5361, and fixed by #5433. By default this fixer is _disabled_, so the version has not been retracted. If you have already upgraded to 1.2.5, downgrading or using 1.2.6 should restore the timer fixer. Or apply #5433 as a local patch. ### Added - Scanner / Fixer changes (#5361) - Stale-workflow detection and cleanup added to shardscanner, disabled by default. - New dynamic config to better control scanner and fixer, particularly for concrete executions. - Documentation about how scanner/fixer work and how to control them, see [the scanner readme.md](service/worker/scanner/README.md) - This also includes example config to enable the new fixer. - MigrationChecker interface to expose migration CLI (#5424) - Added Pinot as new visibility store option (#5201) - Added pinot visibility triple manager to provide options to write to both ES and Pinot. - Added pinotVisibilityStore and pinotClient to support CRUD operations for Pinot. - Added pinot integration test to set up Pinot test cluster and test Pinot functionality. ### Fixed - Fix CreateWorkflowModeContinueAsNew for SQL (#5413) - Fix CLI count&list workflows error message (#5417) - Hotfix for async matching for isolation-group redirection (#5423) - Fix closeStatus for --format flag (#5422) ### Upgrade notes - Any "concrete execution" or "timers" fixers run on upgrade may be missing the new config and those activities will have no invariants for a single run. Later runs will work normally. (#5361) ## [1.2.4] - 2023-09-27 ### Added Implement config store for MySQL (#5403) Implement config store for PostgresSQL (#5405) ### Fixed Remove database check for config store tests (#5401) Fix persistence tests setup (#5402) Retract v1.2.3 (#5406) ## [1.2.3] - 2023-09-15 ### Added Expose workflow history size and count to client (#5392) ### Fixed [cadence-cli] fix typo in input flag for parallelism (#5397) ### Changed Update config store client to support SQL database (#5395) Scaffold config store for sql plugins (#5396) Improve poller detection for isolation (#5399) ## [1.2.2] - 2023-08-29 ### Added Added a update workflow execution count metric for RI (#5386) ### Fixed Fixed nil pointer issue in domain migration command rendering (#5378) ### Changed Pass partition config and isolation group to history/matching even if isolation is disabled (#5385) ## [1.2.1] - 2023-08-24 ### Added Added guardrail to on scalable tasklist number of write partitions (#5331) Added Opensearch2 client with bulk API shared between clients (#5241) Added Filters method to dynamic config Key (#5346) Added domain migration validator command (#5369, #5374) Added docker-compose with http API enabled (#5358) Added support to enable TLS for HTTP inbounds (#5381) ### Fixed ### Changed Updates to partition config middleware (#5334) Allow to configure HTTP settings using template (#5329) Cleanup the unused watchdog code (#5096) Upgrade yarpc to v1.70.3 (#5341) upgrade mysql (#5345) change mysql schema folder v57 to v8 (#5356) Remove duplicated line (#5328) Fill IsCron for proto of WorkflowExecutionInfo (#5366) Update idls and ensure thrift fields roundtrip (#5365) Go version bump (#5367) Update Dockerfile with a proper Go version and bump alpine version (#5371) StartWorkflowExecution: validate RequestID before calling history (#5359) Update list of available frontend HTTP endpoints (#5338) Set a minimum timeout for async task dispatch based on rate limit (#5382) Validate search-attribute-key so keys are fine in advanced search (#5340) ## [1.2.0] - 2023-08-24 ### Added Added more logs around unsupported version for consistent query (#5287) Added datasource and dashboards to Grafana in Docker (#5207) Added metric for isolation task matching (#5288) Added local build instructions to Readme (#5299) Added examples for reset-batch and batch commands in help usage section (#5302) Record current worker Identity on Pending activity tasks (#5307) Support invoking RPC using HTTP and JSON (#5305) Added Worklfow start metric (#5289) Added count of workflows indicator when running the listall command and waiting it to complete (#5309) Added option to pass consistency level in the cassandra schema version validation (#5327) ### Fixed Fixed rendering for isolation-groups in the CLI (#5285) Fixed SearchAfter usage (#5311) Fixed garbage collection logic for matching tasks (#5355) Do not make poller crash if isolation group is invalid (#5372) ### Changed Removed unused metric (#5292) Show error message if requesting workflow does not exist (#5300) Parse JWT flags when creating a context (#5296) Set ReplicatorCacheCapacity to 0 (#5301) Show explicit message if command is not found (#5298) IDL update to include new field in PendingActivityInfo (#5306) Set 12.4 version for postgres containers (#5326) Extract EventColorFunction from ColorEvent (#5321) Update consistency level for cassandra visibility (#5330) Improve async-matching performance for isolation (#5363) ## [1.1.0] - 2023-08-24 ### Added Added metrics for delete workflow execution on a shard level (#5126) Added overall persistence count for shardid (#5134) Added Scanner to purge deprecated domain workflows (#5125) Added domain status check in taskfilter (#5140) Added usage for InactiveDomain Invariant (#5144, #5213) Added request body into Attributes for auditing purpose with PII (#5151) Added remaining persistence metrics that goes to a shard #5142 Added consistent query pershard metric (#5143, #5170) Added logging with workflow/domain tags (#5159) Added ShardID to valid attributes (#5161) Added Pinot docker files, table config and schema (#5163) Added Canary TLS support (#5086) Added a small test to catch issues with deadlocks (#5171) Added large workflow hot shard detection (#5166) Added thin ES clients (#5162, #5217) Added generic ES query building utilities (#5168) Added Physical sharding for NoSQL-based persistence (#5187) Added tasklist traffic metrics for non-sticky and non-forwarded tasklists (#5218, #5235) Added default value for shard_id and update_time in mysql (#5246) Added default value for shard_id and update_time in postgres (#5259) Added Hostname tag into metrics and other services (#5245) Added Domain name validation (#5250) Added isolation-group types (#5260) Added Dynamic-config type (#5261) Added isolation groups to persistence (#5270) Added helper function to store/retrieve partition config from context (#5195) Added domain handler changes for isolation group (#5274) Added isolation-group and partition libraries (#5262) Added resource implmentation (#5278) Added Admin API for zonal-isolation drain commands (#5282) Added cli operations for the zonal-isolation drains (#5283) Added metric for isolation task matching (#5288) Added some more coverage to isolation-group state handler (#5304) ### Fixed Removed circular dependencies between matching components (#5111) Fixed InsertTasks query for Cassandra (#5119) Fixed prometheus frontend label inconsistencies (#5087) Fixed samples documentation (#5088) Fixed type validation in configstore DC client value updating (#5110) Fixed the config-store handling for not-found errors (#5203) Fixed docker (#5244) Fixed thrift mapper for DomainConfiguration (#5268) Fixed panic while parsing workflows with timeouts (#5267) Fixed sticky tasklist isolation (#5308) Fixed SearchAfter usage (#5311) Fixed isolation groups domain drains (#5315) ### Changed Indexer: refactor ES processor (#5100) Moved sample logger into persistence metric client for cleaness (#5129) ES: do not set _type when using Bulk API for v7 client (#5104) ES: single interface for different ES/OpenSearch versions (#5158) ES: reduce code duplication (#5137) Updated README.md (#5064, #5251) Set poll interval for filebased dynamic config if not set (#5160) Upgraded Golang base image to 1.18 to remediate CVEs (#5035) Refactor matching integration test (#5182) Merge Activity and Decision matching tests (#5186) Update idls version (#5200) Allow registering search attributes without Advance Visibility enabled (#5185) Scaffold config store for SQL (#5239) Bench: possibility to pass frontend address using env (#5113) Remove tcheck dependency (#5247) Update internal type to adopt idl change (#5253) Update persistence layer to adopt idl update for isolation (#5254) Update history to persist partition config (#5257) Upgrades IDL to include isolation-groups (#5258) Make ESClient fields public (#5269) Remove unneeded file (#5276) Updated frontend to adopt draining isolation groups (#5225) (#5281) Updated matching to support tasklist isolation (#5280) Bumping version to 1.1.0 (#5284) Improvements on tasklist isolation (#5291) Task processing panic handling (#5294) Update ClusterNameForFailoverVersion to return error (#5293) Feature/isolation group library logging improvements (#5295) Increase number of forward tokens for isolation tasklists (#5310) Seperate token pools for tasklists with isolation (#5314) Disable isolation for sticky tasklist (#5319) Change default value of AsyncTaskDispatchTimeout (#5320) ## [1.0.0] - 2023-04-26 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v1.0.0) ## [0.23.1] - 2021-11-18 See [Release Note](https://github.com/cadence-workflow/cadence/releases/tag/v0.23.1) ## [0.21.3] - 2021-07-17 ### Added - Added GRPC support. Cadence server will accept requests on both TChannel and GRPC. With dynamic config flag `system.enableGRPCOutbound` it will also switch to GRPC communication internally between server components. ### Fixed - This change contains breaking change on user config. The masterClusterName config key is deprecated and is replaced with primaryClusterName key. (#4185) ### Changed - Bump CLI version to v0.19.0 - Change `--connect-attributes` in `cadence-sql-tool` from URL encoding to the format of k1=v1,k2=v2... - Change `--domain_data` in `cadence domain update/register` from the format of k1:v1,k2:v2... to the format of k1=v1,k2=v2... - Deprecate local domains. All new domains should be created as global domains. - Server will deep merge configuration files when loading. No need to copy the whole config section, specify only those fields that needs overriding. (#4165) ## [0.18.0] - 2021-01-22 ## [0.16.1] - 2021-01-21 ## [0.17.0] - 2021-01-13 ## [0.16.0] - 2020-12-10 ================================================ FILE: CLAUDE.md ================================================ # Development Guidelines This document contains critical information about working with this codebase. ## Core Rules - NEVER ever mention a `co-authored-by` or similar aspects. In particular, never mention the tool used to create the commit message or PR. ## Development Commands ```bash make bins # build all binaries make build # compile-check all packages + test files (no codegen, no test execution) make pr # pre-PR: tidy → go-generate → fmt → lint make pr GEN_DIR=service/history # scoped codegen (faster for single package) make test # all unit tests (excludes host/ integration tests) make test_e2e # end-to-end integration tests in host/ (requires Docker dependencies running) make lint # lint only make go-generate # regenerate mocks, enums, wrapper files go test -race -run TestFoo ./path/to/pkg/... # run a specific test ``` ## Gotchas - **`idls/` submodule**: run `git submodule update --init --recursive` after checkout or all codegen and build targets fail silently. - **Generated files**: `*_generated.go` and `*_mock.go` are produced by `make go-generate`. Edit the source `.tmpl` or interface file, then regenerate — never edit generated files directly. - **`make pr` is required before every PR**: it runs tidy → go-generate → fmt → lint in sequence. If CI shows unexpected diffs in generated files, you forgot `make pr`. Prefer to use `make pr GEN_DIR=` for faster iteration. - **Go workspace gotcha**: `go build ./...` and `go test ./...` only cover the root module. Use `make bins` and `make test` for full coverage. Use `make tidy` (not `go mod tidy`). - **IDL local testing**: To test local IDL changes before pushing, add `replace github.com/uber/cadence-idl => ./idls` to the bottom of `go.mod`. Remove before committing. - **Submodule drift**: `git submodule update --init --recursive` fixes not just post-checkout failures but also mid-development build errors after upstream IDL changes. - **SQLite for quick local dev**: No Docker required. Run `make install-schema-sqlite` then `./cadence-server --zone sqlite start` for the fastest path to a running local server. ## Coding Best Practices - **Testing**: - Prefer table-tests; plain Go tests for trivially simple cases. - Do **not** write new suite-style tests (`testify/suite`) — legacy, maintain only. - Do **not** use `github.com/stretchr/testify` mocks — use `github.com/uber-go/mock`. - All new tests should be either plain Go tests or table-tests. - Round-trip test all mappers: `ToX(FromX(item)) == item`. Fuzz-test mappers following the pattern in `common/types/mapper/proto/api_test.go` and `schedule_test.go`. - **Types**: - Never use IDL code (`.gen/go/` or `.gen/proto/`) directly in service logic. - Map to `common/types` or `common/persistence` types via mappers in `common/types/mapper/`. - Files in `.gen/` are generated from IDL — do not edit manually. ## Pull Request Guidelines PRs must follow the template in `.github/pull_request_guidance.md`. PR titles must use Conventional Commits format: `(): `. Valid types are defined in `.github/workflows/semantic-pr.yml`. Example: `feat(history): add retry logic for shard takeover`. ## Development For database setup, schema installation, and server start options: [CONTRIBUTING.md](CONTRIBUTING.md). ## Repository layout A Cadence server cluster is composed of four different services: Frontend, Matching, History and Worker(system). Here's what's in each top-level directory in this repository: * **bench/** : Benchmark and load test suite for stress-testing a Cadence cluster * **canary/** : The test code that needs to run periodically to ensure Cadence is healthy * **client/** : Client wrappers to let the four different services talk to each other * **cmd/** : The main function to build binaries for servers and CLI tools * **common/** : Basically contains all the rest of the code in Cadence server, the names of the sub folder are the topics of the packages * **config/** : Sample configuration files * **docker/** : Code/scripts to build docker images * **docs/** : Documentation * **environment/** : Test helpers for reading environment variables (DB host/port, etc.) used by integration tests * **host/** : End-to-end integration tests * **idls/** : Git submodule with Thrift and Protobuf IDL definitions; source for all generated code under `.gen/` * **internal/** : Internal Go tool dependencies (blank imports to pin codegen tools in `go.mod`) * **proto/** : Protobuf definitions for internal persistence blobs and public Cadence APIs * **schema/** : Versioned persistence schema for Cassandra/MySQL/Postgres/ElasticSearch * **scripts/** : Scripts for CI build * **service/** : Contains four sub-folders dedicated to each of the four services (frontend, history, matching, worker) * **simulation/** : Black-box simulation tests that spin up a local Docker cluster and validate complex multi-component scenarios * **tools/** : CLI tools for Cadence workflows and also schema updates for persistence ================================================ FILE: CONTRIBUTING.md ================================================ # Developing Cadence This doc is intended for contributors to Cadence backend. Thanks for considering to contribute ❤️ > 📚 **New to contributing to Cadence?** Check out our [Contributing Guide](https://cadenceworkflow.io/community/how-to-contribute/getting-started) for an overview of the contribution process across all Cadence repositories. This document contains cadence backend specific setup and development instructions. Once you go through the rest of this doc and get familiar with local development setup, take a look at the list of issues labeled with [good first issue](https://github.com/cadence-workflow/cadence/labels/good%20first%20issue). These issues are a great way to start contributing to Cadence. Later when you are more familiar with Cadence, look at issues with [up-for-grabs](https://github.com/cadence-workflow/cadence/labels/up-for-grabs). Join our community on the CNCF Slack workspace at [cloud-native.slack.com](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel to reach out and discuss issues with the team. ## Development Environment Below are the instructions of how to set up a development Environment. ### 1. Building Environment * Golang. Install on OS X with. ``` brew install go ``` * Make sure set PATH to include bin path of GO so that other executables like thriftrw can be found. ```bash # check it first echo $GOPATH # append to PATH PATH=$PATH:$GOPATH/bin # to confirm, run echo $PATH ``` * Download golang dependencies. ```bash go mod download ``` After check out and go to the Cadence repo, compile the `cadence` service and helper tools without running test: ```bash git submodule update --init --recursive make bins ``` You should be able to get all the binaries of this repo: * cadence-server: the server binary * cadence: the CLI binary * cadence-cassandra-tool: the Cassandra schema tools * cadence-sql-tool: the SQL schema tools(for now only supports MySQL and Postgres) * cadence-canary: the canary test binary * cadence-bench: the benchmark test binary :warning: Note: If running into any compiling issue >1. For proto/thrift errors, run `git submodule update --init --recursive` to fix >2. Make sure you upgrade to the latest stable version of Golang. >3. Check if this document is outdated by comparing with the building steps in [Dockerfile](https://github.com/cadence-workflow/cadence/blob/master/Dockerfile) ### 2. Setup Dependency NOTE: you may skip this section if you want to use SQLite or you have installed the dependencies in any other ways, for example, using homebrew. Cadence's core data model can be running with different persistence storages, including Cassandra,MySQL and Postgres. Please refer to [persistence documentation](https://github.com/cadence-workflow/cadence/blob/master/docs/persistence.md) if you want to learn more. Cadence's visibility data model can be running with either Cassandra/MySQL/Postgres database, or ElasticSearch+Kafka. The latter provides [advanced visibility feature](./docs/visibility-on-elasticsearch.md) We recommend to use [docker-compose](https://docs.docker.com/compose/) to start those dependencies: * If you want to start Cassandra dependency, use `./docker/dev/cassandra.yml`: ``` docker compose -f ./docker/dev/cassandra.yml up ``` You will use `CTRL+C` to stop it. Then `docker compose -f ./docker/dev/cassandra.yml down` to clean up the resources. Or to run in the background ``` docker compose -f ./docker/dev/cassandra.yml up -d ``` Also use `docker compose -f ./docker/dev/cassandra.yml down` to stop and clean up the resources. * Alternatively, use `./docker/dev/mysql.yml` for MySQL dependency. (MySQL has been updated from 5.7 to 8.0) * Alternatively, use `./docker/dev/postgres.yml` for PostgreSQL dependency * Alternatively, use `./docker/dev/cassandra-esv7-kafka.yml` for Cassandra, ElasticSearch(v7) and Kafka/ZooKeeper dependencies * Alternatively, use `./docker/dev/mysql-esv7-kafka.yml` for MySQL, ElasticSearch(v7) and Kafka/ZooKeeper dependencies * Alternatively, use `./docker/dev/cassandra-opensearch-kafka.yml` for Cassandra, OpenSearch(compatible with ElasticSearch v7) and Kafka/ZooKeeper dependencies * Alternatively, use `./docker/dev/mongo-esv7-kafka.yml` for MongoDB, ElasticSearch(v7) and Kafka/ZooKeeper dependencies ### 3. Schema installation Based on the above dependency setup, you also need to install the schemas. * If you use SQLite then run `make install-schema-sqlite` to install SQLite schemas * If you use `cassandra.yml` then run `make install-schema` to install Cassandra schemas * If you use `cassandra-esv7-kafka.yml` then run `make install-schema && make install-schema-es-v7` to install Cassandra & ElasticSearch schemas * If you use `cassandra-opensearch-kafka.yml` then run `make install-schema && make install-schema-es-opensearch` to install Cassandra & OpenSearch schemas * If you use `mysql.yml` then run `install-schema-mysql` to install MySQL schemas * If you use `postgres.yml` then run `install-schema-postgres` to install Postgres schemas * `mysql-esv7-kafka.yml` can be used for single MySQL + ElasticSearch or multiple MySQL + ElasticSearch mode * for single MySQL: run `install-schema-mysql && make install-schema-es-v7` * for multiple MySQL: run `make install-schema-multiple-mysql` which will install schemas for 4 mysql databases and ElasticSearch :warning: Note: >If you use `cassandra-esv7-kafka.yml` and start server before `make install-schema-es-v7`, ElasticSearch may create a wrong index on demand. You will have to delete the wrong index and then run the `make install-schema-es-v7` again. To delete the wrong index: ``` curl -X DELETE "http://127.0.0.1:9200/cadence-visibility-dev" ``` ### 4. Run Once you have done all above, try running the local binaries: Then you will be able to run a basic local Cadence server for development. * If you use SQLite, then run `./cadence-server --zone sqlite start`, which load , which will load `config/development.yaml` + `config/development_sqlite.yaml` as config * If you use `cassandra.yml`, then run `./cadence-server start`, which will load `config/development.yaml` as config * If you use `mysql.yml` then run `./cadence-server --zone mysql start`, which will load `config/development.yaml` + `config/development_mysql.yaml` as config * If you use `postgres.yml` then run `./cadence-server --zone postgres start` , which will load `config/development.yaml` + `config/development_postgres.yaml` as config * If you use `cassandra-esv7-kafka.yml` then run `./cadence-server --zone es_v7 start`, which will load `config/development.yaml` + `config/development_es_v7.yaml` as config * If you use `cassandra-opensearch-kafka.yml` then run `./cadence-server --zone es_opensearch start` , which will load `config/development.yaml` + `config/development_es_opensearch.yaml` as config * If you use `mysql-esv7-kafka.yaml` * To run with multiple MySQL : `./cadence-server --zone multiple_mysql start`, which will load `config/development.yaml` + `config/development_multiple_mysql.yaml` as config Then register a domain: ``` ./cadence --do samples-domain domain register ``` ### Sample Repo The sample code is available in the [Sample repo](https://github.com/cadence-workflow/cadence-samples) Then run a helloworld from [Go Client Sample](https://github.com/cadence-workflow/cadence-samples) or [Java Client Sample](https://github.com/cadence-workflow/cadence-java-samples) ``` make bins ``` will build all the samples. Then ``` ./bin/helloworld -m worker & ``` will start a worker for helloworld workflow. You will see like: ``` $./bin/helloworld -m worker & [1] 16520 2021-09-24T21:07:03.242-0700 INFO common/sample_helper.go:109 Logger created. 2021-09-24T21:07:03.243-0700 DEBUG common/factory.go:151 Creating RPC dispatcher outbound {"ServiceName": "cadence-frontend", "HostPort": "127.0.0.1:7933"} 2021-09-24T21:07:03.250-0700 INFO common/sample_helper.go:161 Domain successfully registered. {"Domain": "samples-domain"} 2021-09-24T21:07:03.291-0700 INFO internal/internal_worker.go:833 Started Workflow Worker {"Domain": "samples-domain", "TaskList": "helloWorldGroup", "WorkerID": "16520@IT-USA-25920@helloWorldGroup"} 2021-09-24T21:07:03.300-0700 INFO internal/internal_worker.go:858 Started Activity Worker {"Domain": "samples-domain", "TaskList": "helloWorldGroup", "WorkerID": "16520@IT-USA-25920@helloWorldGroup"} ``` Then ``` ./bin/helloworld ``` to start a helloworld workflow. You will see the result like : ``` $./bin/helloworld 2021-09-24T21:07:06.220-0700 INFO common/sample_helper.go:109 Logger created. 2021-09-24T21:07:06.220-0700 DEBUG common/factory.go:151 Creating RPC dispatcher outbound {"ServiceName": "cadence-frontend", "HostPort": "127.0.0.1:7933"} 2021-09-24T21:07:06.226-0700 INFO common/sample_helper.go:161 Domain successfully registered. {"Domain": "samples-domain"} 2021-09-24T21:07:06.272-0700 INFO common/sample_helper.go:195 Started Workflow {"WorkflowID": "helloworld_75cf142b-c0de-407e-9115-1d33e9b7551a", "RunID": "98a229b8-8fdd-4d1f-bf41-df00fb06f441"} 2021-09-24T21:07:06.347-0700 INFO helloworld/helloworld_workflow.go:31 helloworld workflow started {"Domain": "samples-domain", "TaskList": "helloWorldGroup", "WorkerID": "16520@IT-USA-25920@helloWorldGroup", "WorkflowType": "helloWorldWorkflow", "WorkflowID": "helloworld_75cf142b-c0de-407e-9115-1d33e9b7551a", "RunID": "98a229b8-8fdd-4d1f-bf41-df00fb06f441"} 2021-09-24T21:07:06.347-0700 DEBUG internal/internal_event_handlers.go:489 ExecuteActivity {"Domain": "samples-domain", "TaskList": "helloWorldGroup", "WorkerID": "16520@IT-USA-25920@helloWorldGroup", "WorkflowType": "helloWorldWorkflow", "WorkflowID": "helloworld_75cf142b-c0de-407e-9115-1d33e9b7551a", "RunID": "98a229b8-8fdd-4d1f-bf41-df00fb06f441", "ActivityID": "0", "ActivityType": "main.helloWorldActivity"} 2021-09-24T21:07:06.437-0700 INFO helloworld/helloworld_workflow.go:62 helloworld activity started {"Domain": "samples-domain", "TaskList": "helloWorldGroup", "WorkerID": "16520@IT-USA-25920@helloWorldGroup", "ActivityID": "0", "ActivityType": "main.helloWorldActivity", "WorkflowType": "helloWorldWorkflow", "WorkflowID": "helloworld_75cf142b-c0de-407e-9115-1d33e9b7551a", "RunID": "98a229b8-8fdd-4d1f-bf41-df00fb06f441"} 2021-09-24T21:07:06.513-0700 INFO helloworld/helloworld_workflow.go:55 Workflow completed. {"Domain": "samples-domain", "TaskList": "helloWorldGroup", "WorkerID": "16520@IT-USA-25920@helloWorldGroup", "WorkflowType": "helloWorldWorkflow", "WorkflowID": "helloworld_75cf142b-c0de-407e-9115-1d33e9b7551a", "RunID": "98a229b8-8fdd-4d1f-bf41-df00fb06f441", "Result": "Hello Cadence!"} ``` See [instructions](service/worker/README.md) for setting up replication(XDC). ## Repository layout A Cadence server cluster is composed of four different services: Frontend, Matching, History and Worker(system). Here's what's in each top-level directory in this repository: * **canary/** : The test code that needs to run periodically to ensure Cadence is healthy * **client/** : Client wrappers to let the four different services talk to each other * **cmd/** : The main function to build binaries for servers and CLI tools * **config/** : Sample configuration files * **docker/** : Code/scripts to build docker images * **docs/** : Documentation * **host/** : End-to-end integration tests * **schema/** : Versioned persistence schema for Cassandra/MySQL/Postgres/ElasticSearch * **scripts/** : Scripts for CI build * **tools/** : CLI tools for Cadence workflows and also schema updates for persistence * **service/** : Contains four sub-folders that dedicated for each of the four services * **common/** : Basically contains all the rest of the code in Cadence server, the names of the sub folder are the topics of the packages If looking at the Git history of the file/package, there should be some engineers focusing on each area/package/component that you are working on. You will probably get code review from them. Don't hesitate to ask for some early feedback or help on Slack. ## Testing & Debug To run all the package tests use the below command. This will run all the tests excluding end-to-end integration test in host/ package): ```bash make test ``` :warning: Note: > You will see some test failures because of errors connecting to MySQL/Postgres if only Cassandra is up. This is okay if you don't write any code related to persistence layer. To run all end-to-end integration tests in **host/** package: ```bash make test_e2e ``` To debug a specific test case when you see some failure, you can trigger it from an IDE, or use the command ``` go test -v -run -testify.m # example: go test -v github.com/uber/cadence/common/persistence/persistence-tests -run TestVisibilitySamplingSuite -testify.m TestListClosedWorkflowExecutions ``` ## IDL Changes If you make changes in the idls submodule and want to test them locally, you can easily do that by using go mod to use the local idls directory instead of github.com/uber/cadence-idl. Temporarily add the following to the bottom of go.mod: ```replace github.com/uber/cadence-idl => ./idls``` Or alternatively, push your idl changes to a branch in your own fork of idl repo and pull that specific commit to idl submodule. Then apply the go.mod change mentioned above. ### Using IDL Changes Once your [Pull Request](#pull-requests) has been successfully merged you can update cadence to use the new version: ```bash # Update the IDLs directory git submodule foreach git pull origin master # Update go to use the latest idl package go get github.com/uber/cadence-idl@latest ``` ## Pull Requests After all the preparation you are about to write code and make a Pull Request for the issue. ### When to open a PR You have a few options for choosing when to submit: * You can open a PR with an initial prototype with "Draft" option or with "WIP"(work in progress) in the title. This is useful when want to get some early feedback. * PR is supposed to be or near production ready. You should have fixed all styling, adding new tests if possible, and verified the change doesn't break any existing tests. * For small changes where the approach seems obvious, you can open a PR with what you believe to be production-ready or near-production-ready code. As you get more experience with how we develop code, you'll find that more PRs will begin falling into this category. ### Commit Messages And Titles of Pull Requests Overcommit adds some requirements to your commit messages. At Uber, we follow the [Chris Beams](http://chris.beams.io/posts/git-commit/) guide to writing git commit messages. Read it, follow it, learn it, love it. All commit messages are from the titles of your pull requests. So make sure follow the rules when titling them. Please don't use very generic titles like "bug fixes". All PR titles should start with UPPER case. Examples: - [Make sync activity retry multiple times before fetch history from remote](https://github.com/cadence-workflow/cadence/pull/1379) - [Enable archival config per domain](https://github.com/cadence-workflow/cadence/pull/1351) #### Code Format and Licence headers checking The project has strict rule about Golang format, import ordering and license declarations. You have to run ```bash make pr ``` which will take care of formatting for you. ### Code review We take code reviews very seriously at Cadence. Please don't be deterred if you feel like you've received some hefty feedback. That's completely normal and expected—and, if you're an external contributor, we very much appreciate your contribution! In this repository in particular, merging a PR means accepting responsibility for maintaining that code for, quite possibly, the lifetime of Cadence. To take on that reponsibility, we need to ensure that meets our strict standards for production-ready code. No one is expected to write perfect code on the first try. That's why we have code reviews in the first place! Also, don't be embarrassed when your review points out syntax errors, stray whitespace, typos, and missing docstrings! That's why we have reviews. These properties are meant to guide you in your final scan. ### Addressing feedback If someone leaves line comments on your PR without leaving a top-level "looks good to me" (LGTM), it means they feel you should address their line comments before merging. You should respond to all unresolved comments whenever you push a new revision or before you merge. Also, as you gain confidence in Go, you'll find that some of the nitpicky style feedback you get does not make for obviously better code. Don't be afraid to stick to your guns and push back. Much of coding style is subjective. ### Merging External contributors: you don't need to worry about this section. We'll merge your PR as soon as you've addressed all review feedback(you will get at least one approval) and pipeline runs are all successful(meaning all tests are passing). ================================================ FILE: Dockerfile ================================================ ARG TARGET=server # Can be used in case a proxy is necessary ARG GOPROXY # Build Cadence binaries FROM golang:1.23.4-alpine3.21 AS builder ARG RELEASE_VERSION RUN apk add --update --no-cache ca-certificates make git curl mercurial unzip bash WORKDIR /cadence # Making sure that dependency is not touched ENV GOFLAGS="-mod=readonly" # Copy go mod dependencies and try to share the module download cache COPY go.* ./ COPY cmd/server/go.* ./cmd/server/ COPY common/archiver/gcloud/go.* ./common/archiver/gcloud/ # go.work means this downloads everything, not just the top module RUN go mod download COPY . . RUN rm -fr .bin .build idls ENV CADENCE_RELEASE_VERSION=$RELEASE_VERSION # don't do anything fancy, just build. must be run separately, before building things. RUN make .just-build RUN CGO_ENABLED=0 make cadence-cassandra-tool cadence-sql-tool cadence cadence-server cadence-bench cadence-canary # Download dockerize FROM alpine:3.18 AS dockerize # appears to require `docker buildx` or an explicit `--platform` at build time ARG TARGETARCH RUN apk add --no-cache openssl ENV DOCKERIZE_VERSION=v0.9.3 RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-$TARGETARCH-$DOCKERIZE_VERSION.tar.gz \ && tar -C /usr/local/bin -xzvf dockerize-linux-$TARGETARCH-$DOCKERIZE_VERSION.tar.gz \ && rm dockerize-linux-$TARGETARCH-$DOCKERIZE_VERSION.tar.gz \ && echo "**** fix for host id mapping error ****" \ && chown root:root /usr/local/bin/dockerize # Alpine base image FROM alpine:3.18 AS alpine RUN apk add --update --no-cache ca-certificates tzdata bash curl # set up nsswitch.conf for Go's "netgo" implementation # https://github.com/gliderlabs/docker-alpine/issues/367#issuecomment-424546457 RUN [ -e /etc/nsswitch.conf ] && grep '^hosts: files dns' /etc/nsswitch.conf SHELL ["/bin/bash", "-c"] # Cadence server FROM alpine AS cadence-server ENV CADENCE_HOME=/etc/cadence RUN mkdir -p /etc/cadence COPY --from=dockerize /usr/local/bin/dockerize /usr/local/bin COPY --from=builder /cadence/cadence-cassandra-tool /usr/local/bin COPY --from=builder /cadence/cadence-sql-tool /usr/local/bin COPY --from=builder /cadence/cadence /usr/local/bin COPY --from=builder /cadence/cadence-server /usr/local/bin COPY --from=builder /cadence/schema /etc/cadence/schema COPY docker/entrypoint.sh /docker-entrypoint.sh COPY config/dynamicconfig /etc/cadence/config/dynamicconfig COPY config/credentials /etc/cadence/config/credentials COPY docker/config_template.yaml /etc/cadence/config COPY docker/start-cadence.sh /start-cadence.sh WORKDIR /etc/cadence ENV SERVICES="history,matching,frontend,worker" EXPOSE 7933 7934 7935 7939 ENTRYPOINT ["/docker-entrypoint.sh"] CMD /start-cadence.sh # All-in-one Cadence server (~450mb) FROM cadence-server AS cadence-auto-setup RUN apk add --update --no-cache ca-certificates py3-pip mysql-client RUN pip3 install setuptools wheel && pip3 install cqlsh && cqlsh --version COPY docker/start.sh /start.sh COPY docker/domain /etc/cadence/domain CMD /start.sh # Cadence CLI FROM alpine AS cadence-cli COPY --from=builder /cadence/cadence /usr/local/bin ENTRYPOINT ["cadence"] # Cadence Canary FROM alpine AS cadence-canary COPY --from=builder /cadence/cadence-canary /usr/local/bin COPY --from=builder /cadence/cadence /usr/local/bin CMD ["/usr/local/bin/cadence-canary", "--root", "/etc/cadence-canary", "start"] # Cadence Bench FROM alpine AS cadence-bench COPY --from=builder /cadence/cadence-bench /usr/local/bin COPY --from=builder /cadence/cadence /usr/local/bin CMD ["/usr/local/bin/cadence-bench", "--root", "/etc/cadence-bench", "start"] # Final image FROM cadence-${TARGET} ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: MAINTAINERS.md ================================================ # Maintainers - Ender Demirkaya (https://github.com/demirkayaender) - Jan Kisel (https://github.com/dkrotx) - Abhishek Jha (https://github.com/abhishekj720) - Adhitya Mamallan (https://github.com/adhityamamallan) - Anshuman Pandey (https://github.com/pandeyanshuman) - Assem Hafez (https://github.com/Assem-Hafez) - Bowen Xiao (https://github.com/bowenxia) - Chris Warren (https://github.com/c-warren) - Diana Zawadzki (https://github.com/zawadzkidiana) - David Porter (https://github.com/davidporter-id-au) - Eleonora Di Gregorio (https://github.com/eleonoradgr) - Felipe Imanishi (https://github.com/fimanishi) - Gaziza Yestemirova (https://github.com/gazi-yestemirova) - Jakob Haahr Taankvist (https://github.com/jakobht) - Kevin Burns (https://github.com/Bueller87) - Nate Mortensen (https://github.com/natemort) - Neil Xie (https://github.com/neil-xie) - Shijie Sheng (https://github.com/shijiesheng) - Tim Chan (https://github.com/macrotim) - Tim Li (https://github.com/timl3136) - Vsevolod Kaloshin (https://github.com/arzonus) - Zijian Chen (https://github.com/Shaddoll) ================================================ FILE: Makefile ================================================ # get rid of default behaviors, they're just noise MAKEFLAGS += --no-builtin-rules .SUFFIXES: # pipefail is very easy to forget about, and sadly not the default. # this changes that, and makes our target scripts more strict. # # make sure to include all this if overriding on the CLI, e.g.: # DO: make SHELL='bash -eoux pipefail' # DO NOT: make SHELL='bash -x' # as otherwise you will ignore all errors. SHELL = /bin/bash -e -u -o pipefail default: help # ########################################### # TL;DR DOCS: # ########################################### # - Targets should never, EVER be *actual source files*. # Always use book-keeping files in $(BUILD). # Otherwise e.g. changing git branches could confuse Make about what it needs to do. # - Similarly, prerequisites should be those book-keeping files, # not source files that are prerequisites for book-keeping. # e.g. depend on .build/fmt, not $(ALL_SRC), and not both. # - Be strict and explicit about prerequisites / order of execution / etc. # - Test your changes with `-j 27 --output-sync` or something! # - Test your changes with `make -d ...`! It should be reasonable! # temporary build products and book-keeping targets that are always good to / safe to clean. BUILD := .build # bins that are `make clean` friendly, i.e. they build quickly and do not require new downloads. # in particular this should include goimports, as it changes based on which version of go compiles it, # and few know to do more than `make clean`. BIN := $(BUILD)/bin # relatively stable build products, e.g. tools. # usually unnecessary to clean, and may require downloads to restore, so this folder is not automatically cleaned. STABLE_BIN := .bin # toolchain version we all use. # this export ensures it is a precise version rather than a minimum. # lint step ensures this matches other files. GOWORK_TOOLCHAIN := $(word 2,$(shell grep 'toolchain' go.work)) export GOTOOLCHAIN ?= $(GOWORK_TOOLCHAIN) ifneq ($(GOTOOLCHAIN),$(GOWORK_TOOLCHAIN)) # this can be useful for trying new/old versions, so don't block it $(warning warning: your Go toolchain is explicitly set to GOTOOLCHAIN=$(GOTOOLCHAIN), ignoring go.work version $(GOWORK_TOOLCHAIN)...) endif # ==================================== # book-keeping files that are used to control sequencing. # # you should use these as prerequisites in almost all cases, not the source files themselves. # these are defined in roughly the reverse order that they are executed, for easier reading. # # recipes and any other prerequisites are defined only once, further below. # ==================================== # all bins depend on: $(BUILD)/lint # note that vars that do not yet exist are empty, so any prerequisites defined below are ineffective here. $(BUILD)/lint: $(BUILD)/fmt # lint will fail if fmt fails, so fmt first $(BUILD)/proto-lint: $(BUILD)/gomod-lint: $(BUILD)/goversion-lint: $(BUILD)/fmt: $(BUILD)/codegen # formatting must occur only after all other go-file-modifications are done # $(BUILD)/copyright # $(BUILD)/copyright: $(BUILD)/codegen # must add copyright to generated code, sometimes needs re-formatting $(BUILD)/codegen: $(BUILD)/thrift $(BUILD)/protoc $(BUILD)/thrift: $(BUILD)/go_mod_check $(BUILD)/protoc: $(BUILD)/go_mod_check $(BUILD)/go_mod_check: # ==================================== # helper vars # ==================================== # a literal space value, for makefile purposes. # the full "trailing # one space after $(null)" is necessary for correct behavior, # and this strategy works in both new and old versions of make, `SPACE +=` does not. null := SPACE := $(null) # COMMA := , # set a V=1 env var for verbose output. V=0 (or unset) disables. # this is used to make two verbose flags: # - $Q, to replace ALL @ use, so CI can be reliably verbose # - $(verbose), to forward verbosity flags to commands via `$(if $(verbose),-v)` or similar # # SHELL='bash -x' is useful too, but can be more confusing to understand. V ?= 0 ifneq (0,$(V)) verbose := 1 Q := else verbose := Q := @ endif # and enforce ^ that rule: grep the makefile for line-starting @ use, error if any exist. # limit to one match because multiple look too weird. _BAD_AT_USE=$(shell grep -n -m1 '^\s*@' $(MAKEFILE_LIST)) ifneq (,$(_BAD_AT_USE)) $(warning Makefile cannot use @ to silence commands, use $$Q instead:) $(warning found on line $(_BAD_AT_USE)) $(error fix that line and try again) endif # M1 macs may need to switch back to x86, until arm releases are available EMULATE_X86 = ifeq ($(shell uname -sm),Darwin arm64) EMULATE_X86 = arch -x86_64 endif PROJECT_ROOT = github.com/uber/cadence # helper for executing bins that need other bins, just `$(BIN_PATH) the_command ...` # I'd recommend not exporting this in general, to reduce the chance of accidentally using non-versioned tools. BIN_PATH := PATH="$(abspath $(BIN)):$(abspath $(STABLE_BIN)):$$PATH" # automatically gather all source files that currently exist. # works by ignoring everything in the parens (and does not descend into matching folders) due to `-prune`, # and everything else goes to the other side of the `-o` branch, which is `-print`ed. # this is dramatically faster than a `find . | grep -v vendor` pipeline, and scales far better. FRESH_ALL_SRC = $(shell \ find . \ \( \ -path './vendor/*' \ -o -path './idls/*' \ -o -path './.build/*' \ -o -path './.bin/*' \ -o -path './.git/*' \ -o -path './.worktrees/*' \ \) \ -prune \ -o -name '*.go' \ -type f \ -print \ ) # most things can use a cached copy, e.g. all dependencies. # this will not include any files that are created during a `make` run, e.g. via protoc, # but that generally should not matter (e.g. dependencies are computed at parse time, so it # won't affect behavior either way - choose the fast option). # # if you require a fully up-to-date list, e.g. for shell commands, use FRESH_ALL_SRC instead. ALL_SRC := $(FRESH_ALL_SRC) # as lint ignores generated code, it can use the cached copy in all cases LINT_SRC := $(filter-out %_test.go ./.gen/%, $(ALL_SRC)) # ==================================== # $(BIN) targets # ==================================== # downloads and builds a go-gettable tool, versioned by go.mod, and installs # it into the build folder, named the same as the last portion of the URL. # # unfortunately go.work and `go list -modfile=sub/module/go.mod` seem to interact badly, # and some versions complain about duplicates, while others simply block it outright. # the good news is that you can just drop that and `cd` to the folder and it works. define go_build_tool $Q echo "building $(or $(2), $(notdir $(1))) from internal/tools/go.mod..." $Q cd internal/tools; GOTOOLCHAIN=auto go build -mod=readonly -o ../../$(BIN)/$(or $(2), $(notdir $(1))) $(1) endef # same as go_build_tool, but uses our main module file, not the tools one. # this is necessary / useful for tools that we are already importing in the repo, e.g. yarpc. # versions here are checked to make sure the tools version matches the service version. # # this is an imperfect check as it only checks the top-level version. # checks of other packages are handled in $(BUILD)/go_mod_check define go_mod_build_tool $Q echo "building $(or $(2), $(notdir $(1))) from go.mod..." $Q ./scripts/check-gomod-version.sh $(1) $(if $(verbose),-v) $Q GOTOOLCHAIN=auto go build -mod=readonly -o $(BIN)/$(or $(2), $(notdir $(1))) $(1) endef # utility target. # use as an order-only prerequisite for targets that do not implicitly create these folders. $(BIN) $(BUILD) $(STABLE_BIN): $Q mkdir -p $@ $(BIN)/thriftrw: go.mod go.work $(call go_mod_build_tool,go.uber.org/thriftrw) $(BIN)/thriftrw-plugin-yarpc: go.mod go.work $(call go_mod_build_tool,go.uber.org/yarpc/encoding/thrift/thriftrw-plugin-yarpc) $(BIN)/mockgen: internal/tools/go.mod go.work $(call go_build_tool,go.uber.org/mock/mockgen) $(BIN)/mockery: internal/tools/go.mod go.work $(call go_build_tool,github.com/vektra/mockery/v2,mockery) $(BIN)/enumer: internal/tools/go.mod go.work $(call go_build_tool,github.com/dmarkham/enumer) # organizes imports and reformats $(BIN)/gci: internal/tools/go.mod go.work $(call go_build_tool,github.com/daixiang0/gci) # removes unused imports and reformats $(BIN)/goimports: internal/tools/go.mod go.work $(call go_build_tool,golang.org/x/tools/cmd/goimports) $(BIN)/gowrap: go.mod go.work $(call go_build_tool,github.com/hexdigest/gowrap/cmd/gowrap) $(BIN)/revive: internal/tools/go.mod go.work $(call go_build_tool,github.com/mgechev/revive) $(BIN)/nilaway: internal/tools/go.mod go.work $(call go_build_tool,go.uber.org/nilaway/cmd/nilaway,nilaway) $(BIN)/protoc-gen-gogofast: go.mod go.work | $(BIN) $(call go_mod_build_tool,github.com/gogo/protobuf/protoc-gen-gogofast) $(BIN)/protoc-gen-yarpc-go: go.mod go.work | $(BIN) $(call go_mod_build_tool,go.uber.org/yarpc/encoding/protobuf/protoc-gen-yarpc-go) $(BUILD)/go_mod_check: go.mod internal/tools/go.mod go.work $Q # generated == used is occasionally important for gomock / mock libs in general. this is not a definite problem if violated though. $Q ./scripts/check-gomod-version.sh github.com/golang/mock/gomock $(if $(verbose),-v) $Q touch $@ # copyright header checker/writer. only requires stdlib, so no other dependencies are needed. # $(BIN)/copyright: cmd/tools/copyright/licensegen.go # $Q go build -o $@ ./cmd/tools/copyright/licensegen.go # https://docs.buf.build/ # changing BUF_VERSION will automatically download and use the specified version. BUF_VERSION = 1.47.2 OS = $(shell uname -s) ARCH = $(shell $(EMULATE_X86) uname -m) BUF_URL = https://github.com/bufbuild/buf/releases/download/v$(BUF_VERSION)/buf-$(OS)-$(shell uname -m) # use BUF_VERSION_BIN as a bin prerequisite, not "buf", so the correct version will be used. # otherwise this must be a .PHONY rule, or the buf bin / symlink could become out of date. BUF_VERSION_BIN = buf-$(BUF_VERSION) $(STABLE_BIN)/$(BUF_VERSION_BIN): | $(STABLE_BIN) $Q echo "downloading buf $(BUF_VERSION)" $Q curl -sSL $(BUF_URL) -o $@ $Q chmod +x $@ # https://www.grpc.io/docs/languages/go/quickstart/ # protoc-gen-gogofast (yarpc) are versioned via tools.go + go.mod (built above) and will be rebuilt as needed. # changing PROTOC_VERSION will automatically download and use the specified version PROTOC_VERSION = 3.14.0 PROTOC_URL = https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-$(subst Darwin,osx,$(OS))-$(ARCH).zip # the zip contains an /include folder that we need to use to learn the well-known types PROTOC_UNZIP_DIR = $(STABLE_BIN)/protoc-$(PROTOC_VERSION)-zip # use PROTOC_VERSION_BIN as a bin prerequisite, not "protoc", so the correct version will be used. # otherwise this must be a .PHONY rule, or the buf bin / symlink could become out of date. PROTOC_VERSION_BIN = protoc-$(PROTOC_VERSION) $(STABLE_BIN)/$(PROTOC_VERSION_BIN): | $(STABLE_BIN) $Q echo "downloading protoc $(PROTOC_VERSION): $(PROTOC_URL)" $Q # recover from partial success $Q rm -rf $(STABLE_BIN)/protoc.zip $(PROTOC_UNZIP_DIR) $Q # download, unzip, copy to a normal location $Q curl -sSL $(PROTOC_URL) -o $(STABLE_BIN)/protoc.zip $Q unzip -q $(STABLE_BIN)/protoc.zip -d $(PROTOC_UNZIP_DIR) $Q cp $(PROTOC_UNZIP_DIR)/bin/protoc $@ # checks that the idl submodule points to a commit on master, and that it matches the go module (which must be a pseudo version). # this is only used in an explicit CI step, because it's expected to fail when developing. # # `git ls-tree HEAD idls` is selected because this only cares about the committed/checked-out target, # not whatever the current status is, because only the committed value will exist for others. # # and last but not least: this avoids using `go` to make this check take only a couple seconds in CI, # so the whole docker container doesn't have to be prepared. .idl-status: $(Q) cd idls && \ SUBMODULE_COMMIT=$$(git rev-parse HEAD) && \ BRANCH_INFO=$$(git branch -r --contains "$$SUBMODULE_COMMIT" | head -n1) && \ if ! git branch -r --contains "$$SUBMODULE_COMMIT" | grep -q "origin/master"; then \ echo "Error: Submodule commit $$SUBMODULE_COMMIT belongs to $$BRANCH_INFO, not to master branch" && \ exit 1; \ fi $(Q) idlsha=$$(git ls-tree HEAD idls | awk '{print substr($$3,0,12)}') && \ gosha=$$(grep github.com/uber/cadence-idl go.mod | tr '-' '\n' | tail -n1) && \ if [[ "$$idlsha" != "$$gosha" ]]; then \ echo "IDL submodule sha ($$idlsha) does not match go module sha ($$gosha)." >&2 && \ echo "Make sure the IDL PR has been merged, and this PR is updated, before merging here." >&2 && \ exit 1; \ fi # ==================================== # Codegen targets # ==================================== # IDL submodule must be populated, or files will not exist -> prerequisites will be wrong -> build will fail. # Because it must exist before the makefile is parsed, this cannot be done automatically as part of a build. # Instead: call this func in targets that require the submodule to exist, so that target will not be built. # # THRIFT_FILES is just an easy identifier for "the submodule has files", others would work fine as well. define ensure_idl_submodule $(if $(THRIFT_FILES),,$(error idls/ submodule must exist, or build will fail. Run `git submodule update --init` and try again)) endef # codegen is done when thrift and protoc are done $(BUILD)/codegen: $(BUILD)/thrift $(BUILD)/protoc | $(BUILD) $Q touch $@ THRIFT_FILES := $(shell find idls -name '*.thrift') # book-keeping targets to build. one per thrift file. # idls/thrift/thing.thrift -> .build/thing.thrift # the reverse is done in the recipe. THRIFT_GEN := $(subst idls/thrift/,.build/,$(THRIFT_FILES)) # thrift is done when all sub-thrifts are done $(BUILD)/thrift: $(THRIFT_GEN) | $(BUILD) $(call ensure_idl_submodule) $Q touch $@ # how to generate each thrift book-keeping file. # # note that each generated file depends on ALL thrift files - this is necessary because they can import each other. # as --no-recurse is specified, these can be done in parallel, since output files will not overwrite each other. $(THRIFT_GEN): $(THRIFT_FILES) $(BIN)/thriftrw $(BIN)/thriftrw-plugin-yarpc | $(BUILD) $Q echo 'thriftrw for $(subst .build/,idls/thrift/,$@)...' $Q $(BIN_PATH) $(BIN)/thriftrw \ --plugin=yarpc \ --pkg-prefix=$(PROJECT_ROOT)/.gen/go \ --out=.gen/go \ --no-recurse \ $(subst .build/,idls/thrift/,$@) $Q touch $@ PROTO_ROOT := proto # output location is defined by `option go_package` in the proto files, all must stay in sync with this PROTO_OUT := .gen/proto PROTO_FILES = $(shell find -L ./$(PROTO_ROOT) -name "*.proto" | grep -v "persistenceblobs" | grep -v public) PROTO_DIRS = $(sort $(dir $(PROTO_FILES))) # protoc splits proto files into directories, otherwise protoc-gen-gogofast is complaining about inconsistent package # import paths due to multiple packages being compiled at once. # # After compilation files are moved to final location, as plugins adds additional path based on proto package. $(BUILD)/protoc: $(PROTO_FILES) $(STABLE_BIN)/$(PROTOC_VERSION_BIN) $(BIN)/protoc-gen-gogofast $(BIN)/protoc-gen-yarpc-go | $(BUILD) $(call ensure_idl_submodule) $Q mkdir -p $(PROTO_OUT) $Q echo "protoc..." $Q chmod +x $(STABLE_BIN)/$(PROTOC_VERSION_BIN) $Q $(foreach PROTO_DIR,$(PROTO_DIRS),$(EMULATE_X86) $(STABLE_BIN)/$(PROTOC_VERSION_BIN) \ --plugin $(BIN)/protoc-gen-gogofast \ --plugin $(BIN)/protoc-gen-yarpc-go \ -I=$(PROTO_ROOT)/public \ -I=$(PROTO_ROOT)/internal \ -I=$(PROTOC_UNZIP_DIR)/include \ --gogofast_out=Mgoogle/protobuf/duration.proto=github.com/gogo/protobuf/types,Mgoogle/protobuf/field_mask.proto=github.com/gogo/protobuf/types,Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types,Mgoogle/protobuf/wrappers.proto=github.com/gogo/protobuf/types,paths=source_relative:$(PROTO_OUT) \ --yarpc-go_out=$(PROTO_OUT) \ $$(find $(PROTO_DIR) -name '*.proto');\ ) $Q # This directory exists for local/github_actions but not for docker builds. $Q if [ -d "$(PROTO_OUT)/uber/cadence" ]; then \ cp -R $(PROTO_OUT)/uber/cadence/* $(PROTO_OUT)/; \ rm -r $(PROTO_OUT)/uber; \ fi $Q touch $@ # ==================================== # Rule-breaking targets intended ONLY for special cases with no good alternatives. # ==================================== # used to bypass checks when building binaries. # this is primarily intended for docker image builds, but can be used to skip things locally. .PHONY: .just-build .just-build: | $(BUILD) touch $(BUILD)/just-build # ==================================== # other intermediates # ==================================== $(BUILD)/proto-lint: $(PROTO_FILES) $(STABLE_BIN)/$(BUF_VERSION_BIN) | $(BUILD) $Q cd $(PROTO_ROOT) && ../$(STABLE_BIN)/$(BUF_VERSION_BIN) lint $Q touch $@ # lints that go modules are as expected, e.g. parent does not import submodule. # tool builds that need to be in sync with the parent are partially checked through go_mod_build_tool, but should probably be checked here too $(BUILD)/gomod-lint: go.mod internal/tools/go.mod common/archiver/gcloud/go.mod | $(BUILD) $Q echo "checking for direct submodule dependencies in root go.mod..." $Q ( \ MAIN_MODULE=$$(grep "^module " go.mod | awk '{print $$2}'); \ SUBMODULES=$$(find . -type f -name "go.mod" -not -path "./go.mod" -not -path "./idls/*" -not -path "./.worktrees/*" -exec dirname {} \; | sed 's|^\./||'); \ for submodule in $$SUBMODULES; do \ submodule_path="$$MAIN_MODULE/$$submodule"; \ if grep -q "$$submodule_path" go.mod; then \ echo "ERROR: Root go.mod directly depends on submodule: $$submodule_path" >&2; \ exit 1; \ fi; \ echo "✓ No direct dependency on $$submodule"; \ done; \ ) $Q touch $@ # note that LINT_SRC is fairly fake as a prerequisite. # it's a coarse "you probably don't need to re-lint" filter, nothing more. $(BUILD)/code-lint: $(LINT_SRC) $(BIN)/revive $(BIN)/nilaway | $(BUILD) $Q echo "lint..." $Q # non-optional vet checks. unfortunately these are not currently included in `go test`'s default behavior. $Q go vet -copylocks ./... ./common/archiver/gcloud/... $Q $(BIN)/revive -config revive.toml -exclude './vendor/...' -exclude './.gen/...' -formatter stylish ./... $Q # look for go files with "//comments", and ignore "//go:build"-style directives ("grep -n" shows "file:line: //go:build" so the regex is a bit complex) $Q bad="$$(find . -type f -name '*.go' -not -path './idls/*' -not -path './.worktrees/*' | xargs grep -n -E '^\s*//\S' | grep -E -v '^[^:]+:[^:]+:\s*//[a-z]+:[a-z]+' || true)"; \ if [ -n "$$bad" ]; then \ echo "$$bad" >&2; \ echo 'non-directive comments must have a space after the "//"' >&2; \ exit 1; \ fi $Q echo "nilaway check..." $Q GOTOOLCHAIN=go1.24.0 $(BIN)/nilaway -test=false github.com/uber/cadence/common/types/mapper/... $Q touch $@ $(BUILD)/goversion-lint: go.work Dockerfile docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} $Q echo "checking go version..." $Q # intentionally using go.work toolchain, as GOTOOLCHAIN is user-overridable $Q ./scripts/check-go-toolchain.sh $(GOWORK_TOOLCHAIN) $Q touch $@ # fmt and copyright are mutually cyclic with their inputs, so if a copyright header is modified: # - copyright -> makes changes # - fmt sees changes -> makes changes # - now copyright thinks it needs to run again (but does nothing) # - which means fmt needs to run again (but does nothing) # and now after two passes it's finally stable, because they stopped making changes. # # this is not fatal, we can just run 2x. # to be fancier though, we can detect when *both* are run, and re-touch the book-keeping files to prevent the second run. # this STRICTLY REQUIRES that `copyright` and `fmt` are mutually stable, and that copyright runs before fmt. # if either changes, this will need to change. MAYBE_TOUCH_COPYRIGHT= # use FRESH_ALL_SRC so it won't miss any generated files produced earlier. $(BUILD)/fmt: $(ALL_SRC) $(BIN)/goimports $(BIN)/gci | $(BUILD) $Q echo "removing unused imports..." $Q # goimports thrashes on internal/tools, sadly. just hide it. $Q echo $(filter-out ./internal/tools/tools.go,$(FRESH_ALL_SRC)) | xargs $(BIN)/goimports -w $Q echo "grouping imports..." $Q echo $(FRESH_ALL_SRC) | xargs $(BIN)/gci write --section standard --section 'Prefix(github.com/uber/cadence/)' --section default --section blank $Q touch $@ # $Q $(MAYBE_TOUCH_COPYRIGHT) # $(BUILD)/copyright: $(ALL_SRC) $(BIN)/copyright | $(BUILD) # $(BIN)/copyright --verifyOnly # $Q $(eval MAYBE_TOUCH_COPYRIGHT=touch $@) # $Q touch $@ # ==================================== # developer-oriented targets # # many of these share logic with other intermediates, but are useful to make .PHONY for output on demand. # as the Makefile is fast, it's reasonable to just delete the book-keeping file recursively make. # this way the effort is shared with future `make` runs. # ==================================== # "re-make" a target by deleting and re-building book-keeping target(s). # the + is necessary for parallelism flags to be propagated define remake $Q rm -f $(addprefix $(BUILD)/,$(1)) $Q +$(MAKE) --no-print-directory $(addprefix $(BUILD)/,$(1)) endef .PHONY: lint fmt copyright pr # useful to actually re-run to get output again. # reuse the intermediates for simplicity and consistency. lint: ## (Re)run the linter $(call remake,proto-lint gomod-lint code-lint goversion-lint) # intentionally not re-making, it's a bit slow and it's clear when it's unnecessary fmt: $(BUILD)/fmt ## Run `gofmt` / organize imports / etc # not identical to the intermediate target, but does provide the same codegen (or more). # copyright: $(BIN)/copyright | $(BUILD) ## Update copyright headers # $(BIN)/copyright # $Q touch $(BUILD)/copyright define make_quietly $Q echo "make $1..." $Q output=$$(mktemp); $(MAKE) $1 > $$output 2>&1 || ( cat $$output; echo -e '\nfailed `make $1`, check output above' >&2; exit 1) endef override GEN_DIR := $(patsubst %/,%,$(strip $(GEN_DIR))) GO_GENERATE_SCOPE ?= $(if $(GEN_DIR),./$(GEN_DIR)/...,./...) GO_GENERATE_MAKE_ARG = $(if $(GEN_DIR),GEN_DIR=$(GEN_DIR),) # pre-PR target to build and refresh everything pr: ## Redo all codegen and basic checks, to ensure your PR will be able to run tests. Optional: GEN_DIR=path/to/package to scope go-generate $Q $(if $(verbose),$(MAKE) tidy,$(call make_quietly,tidy)) $Q $(if $(verbose),$(MAKE) go-generate $(GO_GENERATE_MAKE_ARG),$(call make_quietly,go-generate $(GO_GENERATE_MAKE_ARG))) $Q $(if $(verbose),$(MAKE) fmt,$(call make_quietly,fmt)) $Q $(if $(verbose),$(MAKE) lint,$(call make_quietly,lint)) # $Q $(if $(verbose),$(MAKE) copyright,$(call make_quietly,copyright)) # ==================================== # binaries to build # ==================================== GOOS ?= $(shell go env GOOS) GOARCH ?= $(shell go env GOARCH) # normally, depend on lint, so a full build and check and codegen runs. # docker builds though must *not* do this, and need to rely entirely on committed code. ifeq (,$(wildcard $(BUILD)/just-build)) BINS_DEPEND_ON := $(BUILD)/lint else BINS_DEPEND_ON := $(warning !!!!! lint and codegen disabled, validations skipped !!!!!) endif BINS = TOOLS = BINS += cadence-cassandra-tool TOOLS += cadence-cassandra-tool cadence-cassandra-tool: $(BINS_DEPEND_ON) $Q echo "compiling cadence-cassandra-tool with OS: $(GOOS), ARCH: $(GOARCH)" $Q ./scripts/build-with-ldflags.sh -o $@ cmd/tools/cassandra/main.go BINS += cadence-sql-tool TOOLS += cadence-sql-tool cadence-sql-tool: $(BINS_DEPEND_ON) $Q echo "compiling cadence-sql-tool with OS: $(GOOS), ARCH: $(GOARCH)" $Q ./scripts/build-with-ldflags.sh -o $@ cmd/tools/sql/main.go BINS += cadence TOOLS += cadence cadence: $(BINS_DEPEND_ON) $Q echo "compiling cadence with OS: $(GOOS), ARCH: $(GOARCH)" $Q ./scripts/build-with-ldflags.sh -o $@ cmd/tools/cli/main.go BINS += cadence-server cadence-server: $(BINS_DEPEND_ON) $Q echo "compiling cadence-server with OS: $(GOOS), ARCH: $(GOARCH)" $Q ./scripts/build-with-ldflags.sh -o $@ cmd/server/main.go BINS += cadence-canary cadence-canary: $(BINS_DEPEND_ON) $Q echo "compiling cadence-canary with OS: $(GOOS), ARCH: $(GOARCH)" $Q ./scripts/build-with-ldflags.sh -o $@ cmd/canary/main.go BINS += sharddistributor-canary sharddistributor-canary: $(BINS_DEPEND_ON) $Q echo "compiling sharddistributor-canary with OS: $(GOOS), ARCH: $(GOARCH)" $Q ./scripts/build-with-ldflags.sh -o $@ cmd/sharddistributor-canary/main.go BINS += cadence-bench cadence-bench: $(BINS_DEPEND_ON) $Q echo "compiling cadence-bench with OS: $(GOOS), ARCH: $(GOARCH)" $Q ./scripts/build-with-ldflags.sh -o $@ cmd/bench/main.go BINS += cadence-releaser cadence-releaser: $(BINS_DEPEND_ON) $Q echo "compiling cadence-releaser with OS: $(GOOS), ARCH: $(GOARCH)" $Q ./scripts/build-with-ldflags.sh -o $@ cmd/tools/releaser/releaser.go .PHONY: go-generate bins tools release clean bins: $(BINS) ## Build all binaries, and any fast codegen needed (does not refresh wrappers or mocks) tools: $(TOOLS) go-generate: $(BIN)/mockgen $(BIN)/enumer $(BIN)/mockery $(BIN)/gowrap ## Run `go generate` to regen mocks, enums, etc $Q echo "running go generate $(GO_GENERATE_SCOPE), this takes a minute or more..." $Q # add our bins to PATH so `go generate` can find them $Q $(BIN_PATH) go generate $(if $(verbose),-v) $(GO_GENERATE_SCOPE) $Q $(MAKE) --no-print-directory fmt # $Q echo "updating copyright headers" # $Q $(MAKE) --no-print-directory copyright release: ## Re-generate generated code and run tests $(MAKE) --no-print-directory go-generate $(MAKE) --no-print-directory test build: ## `go build` all packages and tests (a quick compile check only, skips all other steps) $Q echo 'Building all packages and submodules...' $Q go build ./... $Q cd common/archiver/gcloud; go build ./... $Q cd cmd/server; go build ./... $Q # "tests" by building and then running `true`, and hides test-success output $Q echo 'Building all tests (~5x slower)...' $Q # intentionally not -race due to !race build tags $Q go test -exec /usr/bin/true ./... >/dev/null $Q cd common/archiver/gcloud; go test -exec /usr/bin/true ./... >/dev/null $Q cd cmd/server; go test -exec /usr/bin/true ./... >/dev/null tidy: ## `go mod tidy` all packages $Q # tidy in dependency order $Q go mod tidy $Q cd common/archiver/gcloud; go mod tidy || (echo "failed to tidy gcloud plugin, try manually copying go.mod contents into common/archiver/gcloud/go.mod and rerunning" >&2; exit 1) $Q cd cmd/server; go mod tidy || (echo "failed to tidy main server module, try manually copying go.mod and common/archiver/gcloud/go.mod contents into cmd/server/go.mod and rerunning" >&2; exit 1) clean: ## Clean build products and SQLite database rm -f $(BINS) rm -Rf $(BUILD) rm *.db $(if \ $(wildcard $(STABLE_BIN)/*), \ $(warning usually-stable build tools still exist, delete the $(STABLE_BIN) folder to rebuild them),) # v----- not yet cleaned up -----v .PHONY: git-submodules test bins build clean cover help TOOLS_CMD_ROOT=./cmd/tools INTEG_TEST_ROOT=./host INTEG_TEST_DIR=host INTEG_TEST_XDC_ROOT=./host/xdc INTEG_TEST_XDC_DIR=hostxdc INTEG_TEST_NDC_ROOT=./host/ndc INTEG_TEST_NDC_DIR=hostndc # INTEG_TEST_ROOT requires to use test flags that are not used in other sub directories # go test require all test package support the flags that are used in the test # So INTEG_TEST_DIRS contains all test directories of INTEG_TEST_ROOT that are not using test flags INTEG_TEST_DIRS=$$(go list $(INTEG_TEST_ROOT)/... | grep -v $(INTEG_TEST_XDC_ROOT) | grep -v $(INTEG_TEST_NDC_ROOT) | grep -v $(INTEG_TEST_ROOT)$$) # Opt out folders that shouldn't be run as part of unit tests such as integration tests, simulations. # Syntax: "folder1% folder2%" # space separated list of folders to opt out OPT_OUT_TEST_FOLDERS=./simulation% TEST_TIMEOUT ?= 20m TEST_ARG ?= -race $(if $(verbose),-v) -timeout $(TEST_TIMEOUT) # TODO to be consistent, use nosql as PERSISTENCE_TYPE and cassandra PERSISTENCE_PLUGIN # file names like integ_cassandra__cover should become integ_nosql_cassandra_cover # for https://github.com/uber/cadence/issues/3514 PERSISTENCE_TYPE ?= cassandra TEST_RUN_COUNT ?= 1 ifdef TEST_TAG override TEST_TAG := -tags $(TEST_TAG) endif # all directories with *_test.go files in them (exclude host/xdc) TEST_DIRS := $(filter-out $(INTEG_TEST_XDC_ROOT)%, $(sort $(dir $(filter %_test.go,$(ALL_SRC))))) # all tests other than end-to-end integration test fall into the pkg_test category. # ?= allows passing specific (space-separated) dirs for faster testing PKG_TEST_DIRS ?= $(filter-out $(INTEG_TEST_ROOT)% $(OPT_OUT_TEST_FOLDERS), $(TEST_DIRS)) # Code coverage output files COVER_ROOT := $(BUILD)/coverage UNIT_COVER_FILE := $(COVER_ROOT)/unit_cover.out INTEG_COVER_FILE := $(COVER_ROOT)/integ_$(PERSISTENCE_TYPE)_$(PERSISTENCE_PLUGIN)_cover.out INTEG_COVER_FILE_CASS := $(COVER_ROOT)/integ_cassandra__cover.out INTEG_COVER_FILE_MYSQL := $(COVER_ROOT)/integ_sql_mysql_cover.out INTEG_COVER_FILE_POSTGRES := $(COVER_ROOT)/integ_sql_postgres_cover.out INTEG_COVER_FILE_ETCD := $(COVER_ROOT)/integ_etcd_cover.out INTEG_NDC_COVER_FILE := $(COVER_ROOT)/integ_ndc_$(PERSISTENCE_TYPE)_$(PERSISTENCE_PLUGIN)_cover.out INTEG_NDC_COVER_FILE_CASS := $(COVER_ROOT)/integ_ndc_cassandra__cover.out INTEG_NDC_COVER_FILE_MYSQL := $(COVER_ROOT)/integ_ndc_sql_mysql_cover.out INTEG_NDC_COVER_FILE_POSTGRES := $(COVER_ROOT)/integ_ndc_sql_postgres_cover.out # Need the following option to have integration tests # count towards coverage. godoc below: # -coverpkg pkg1,pkg2,pkg3 # Apply coverage analysis in each test to the given list of packages. # The default is for each test to analyze only the package being tested. # Packages are specified as import paths. COVER_PKGS = client common host service tools # pkg -> pkg/... -> github.com/uber/cadence/pkg/... -> join with commas GOCOVERPKG_ARG := -coverpkg="$(subst $(SPACE),$(COMMA),$(addprefix $(PROJECT_ROOT)/,$(addsuffix /...,$(COVER_PKGS))))" # iterates over a list of dirs and runs go test on each one, collecting errors as it runs. # this is primarily written because it's a verbose bit of boilerplate, until we switch to `go test ./...` where possible. # CAUTION: when changing to `go test ./...`, note that this DOES NOT test submodules. Those must be run separately. define looptest $Q FAIL=""; for dir in $1; do \ go test $(TEST_ARG) -coverprofile=$@ "$$dir" $(TEST_TAG) 2>&1 | tee -a test.log || FAIL="$$FAIL $$dir"; \ done; test -z "$$FAIL" || (echo "Failed packages; $$FAIL"; exit 1) endef test: ## Build and run all tests locally $Q rm -f test $Q rm -f test.log $Q echo Running special test cases without race detector: $Q go test -v ./cmd/server/cadence/ $Q $(call looptest,$(PKG_TEST_DIRS)) test_dirs: echo $(PKG_TEST_DIRS) test_e2e: $Q rm -f test $Q rm -f test.log $Q $(call looptest,$(INTEG_TEST_ROOT)) # need to run end-to-end xdc tests with race detector off because of ringpop bug causing data race issue test_e2e_xdc: $Q rm -f test $Q rm -f test.log $Q $(call looptest,$(INTEG_TEST_XDC_ROOT)) cover_profile: $Q mkdir -p $(BUILD) $Q mkdir -p $(COVER_ROOT) $Q echo "mode: atomic" > $(UNIT_COVER_FILE) $Q echo Running special test cases without race detector: $Q go test ./cmd/server/cadence/ $Q echo Running package tests: $Q go test $(PKG_TEST_DIRS) $(TEST_ARG) -coverprofile=$(UNIT_COVER_FILE) cover_integration_profile: $Q mkdir -p $(BUILD) $Q mkdir -p $(COVER_ROOT) $Q echo "mode: atomic" > $(INTEG_COVER_FILE) $Q echo Running integration test with $(PERSISTENCE_TYPE) $(PERSISTENCE_PLUGIN) $Q mkdir -p $(BUILD)/$(INTEG_TEST_DIR) $Q time go test $(INTEG_TEST_DIRS) $(TEST_ARG) $(TEST_TAG) $(GOCOVERPKG_ARG) -coverprofile=$(BUILD)/$(INTEG_TEST_DIR)/coverage.out || exit 1; $Q cat $(BUILD)/$(INTEG_TEST_DIR)/coverage.out | grep -v "^mode: \w\+" >> $(INTEG_COVER_FILE) $Q time go test $(INTEG_TEST_ROOT) $(TEST_ARG) $(TEST_TAG) -persistenceType=$(PERSISTENCE_TYPE) -sqlPluginName=$(PERSISTENCE_PLUGIN) $(GOCOVERPKG_ARG) -coverprofile=$(BUILD)/$(INTEG_TEST_DIR)/coverage.out || exit 1; $Q cat $(BUILD)/$(INTEG_TEST_DIR)/coverage.out | grep -v "^mode: \w\+" >> $(INTEG_COVER_FILE) integration_tests_etcd: $Q mkdir -p $(BUILD) $Q mkdir -p $(COVER_ROOT) $Q echo "mode: atomic" > $(INTEG_COVER_FILE_ETCD) $Q echo "Running integration tests with etcd" $Q mkdir -p $(BUILD)/$(INTEG_TEST_DIR) $Q (ETCD_TEST_DIRS=$$(find . -name "*_test.go" -exec grep -l "testhelper.SetupStoreTestCluster\|testflags.RequireEtcd" {} \; | xargs -n1 dirname | sort | uniq); \ echo "Found etcd test directories:"; \ echo "$$ETCD_TEST_DIRS"; \ echo "Using ETCD_ENDPOINTS='$(ETCD_ENDPOINTS)'"; \ ETCD=1 ETCD_ENDPOINTS="$(ETCD_ENDPOINTS)" time go test $$ETCD_TEST_DIRS $(TEST_ARG) $(TEST_TAG) -coverprofile=$(BUILD)/$(INTEG_TEST_DIR)/coverage.out || exit 1) $Q cat $(BUILD)/$(INTEG_TEST_DIR)/coverage.out | grep -v "^mode: \w\+" >> $(INTEG_COVER_FILE_ETCD) cover_ndc_profile: $Q mkdir -p $(BUILD) $Q mkdir -p $(COVER_ROOT) $Q echo "mode: atomic" > $(INTEG_NDC_COVER_FILE) $Q echo Running integration test for 3+ dc with $(PERSISTENCE_TYPE) $(PERSISTENCE_PLUGIN) $Q mkdir -p $(BUILD)/$(INTEG_TEST_NDC_DIR) $Q time go test -timeout $(TEST_TIMEOUT) $(INTEG_TEST_NDC_ROOT) $(TEST_TAG) -persistenceType=$(PERSISTENCE_TYPE) -sqlPluginName=$(PERSISTENCE_PLUGIN) $(GOCOVERPKG_ARG) -coverprofile=$(BUILD)/$(INTEG_TEST_NDC_DIR)/coverage.out -count=$(TEST_RUN_COUNT) || exit 1; $Q cat $(BUILD)/$(INTEG_TEST_NDC_DIR)/coverage.out | grep -v "^mode: \w\+" | grep -v "mode: set" >> $(INTEG_NDC_COVER_FILE) $(COVER_ROOT)/cover.out: $(UNIT_COVER_FILE) $(INTEG_COVER_FILE_CASS) $(INTEG_COVER_FILE_MYSQL) $(INTEG_COVER_FILE_POSTGRES) $(INTEG_NDC_COVER_FILE_CASS) $(INTEG_NDC_COVER_FILE_MYSQL) $(INTEG_NDC_COVER_FILE_POSTGRES) $Q echo "mode: atomic" > $(COVER_ROOT)/cover.out cat $(UNIT_COVER_FILE) | grep -v "^mode: \w\+" | grep -vP ".gen|_generated|[Mm]ock[s]?" >> $(COVER_ROOT)/cover.out cat $(INTEG_COVER_FILE_CASS) | grep -v "^mode: \w\+" | grep -vP ".gen|_generated|[Mm]ock[s]?" >> $(COVER_ROOT)/cover.out cat $(INTEG_COVER_FILE_MYSQL) | grep -v "^mode: \w\+" | grep -vP ".gen|_generated|[Mm]ock[s]?" >> $(COVER_ROOT)/cover.out cat $(INTEG_COVER_FILE_POSTGRES) | grep -v "^mode: \w\+" | grep -vP ".gen|_generated|[Mm]ock[s]?" >> $(COVER_ROOT)/cover.out cat $(INTEG_NDC_COVER_FILE_CASS) | grep -v "^mode: \w\+" | grep -vP ".gen|_generated|[Mm]ock[s]?" >> $(COVER_ROOT)/cover.out cat $(INTEG_NDC_COVER_FILE_MYSQL) | grep -v "^mode: \w\+" | grep -vP ".gen|_generated|[Mm]ock[s]?" >> $(COVER_ROOT)/cover.out cat $(INTEG_NDC_COVER_FILE_POSTGRES) | grep -v "^mode: \w\+" | grep -vP ".gen|_generated|[Mm]ock[s]?" >> $(COVER_ROOT)/cover.out cat $(INTEG_COVER_FILE_ETCD) | grep -v "^mode: \w\+" | grep -vP ".gen|_generated|[Mm]ock[s]?" >> $(COVER_ROOT)/cover.out cover: $(COVER_ROOT)/cover.out go tool cover -html=$(COVER_ROOT)/cover.out; install-schema: cadence-cassandra-tool $Q echo installing schema ./cadence-cassandra-tool create -k cadence --rf 1 ./cadence-cassandra-tool -k cadence setup-schema -v 0.0 ./cadence-cassandra-tool -k cadence update-schema -d ./schema/cassandra/cadence/versioned ./cadence-cassandra-tool create -k cadence_visibility --rf 1 ./cadence-cassandra-tool -k cadence_visibility setup-schema -v 0.0 ./cadence-cassandra-tool -k cadence_visibility update-schema -d ./schema/cassandra/visibility/versioned $Q echo installed schema install-schema-mysql: cadence-sql-tool ./cadence-sql-tool --user root --pw cadence create --db cadence ./cadence-sql-tool --user root --pw cadence --db cadence setup-schema -v 0.0 ./cadence-sql-tool --user root --pw cadence --db cadence update-schema -d ./schema/mysql/v8/cadence/versioned ./cadence-sql-tool --user root --pw cadence create --db cadence_visibility ./cadence-sql-tool --user root --pw cadence --db cadence_visibility setup-schema -v 0.0 ./cadence-sql-tool --user root --pw cadence --db cadence_visibility update-schema -d ./schema/mysql/v8/visibility/versioned install-schema-multiple-mysql: cadence-sql-tool install-schema-es-v7 ./cadence-sql-tool --user root --pw cadence create --db cadence0 ./cadence-sql-tool --user root --pw cadence --db cadence0 setup-schema -v 0.0 ./cadence-sql-tool --user root --pw cadence --db cadence0 update-schema -d ./schema/mysql/v8/cadence/versioned ./cadence-sql-tool --user root --pw cadence create --db cadence1 ./cadence-sql-tool --user root --pw cadence --db cadence1 setup-schema -v 0.0 ./cadence-sql-tool --user root --pw cadence --db cadence1 update-schema -d ./schema/mysql/v8/cadence/versioned ./cadence-sql-tool --user root --pw cadence create --db cadence2 ./cadence-sql-tool --user root --pw cadence --db cadence2 setup-schema -v 0.0 ./cadence-sql-tool --user root --pw cadence --db cadence2 update-schema -d ./schema/mysql/v8/cadence/versioned ./cadence-sql-tool --user root --pw cadence create --db cadence3 ./cadence-sql-tool --user root --pw cadence --db cadence3 setup-schema -v 0.0 ./cadence-sql-tool --user root --pw cadence --db cadence3 update-schema -d ./schema/mysql/v8/cadence/versioned install-schema-postgres: cadence-sql-tool ./cadence-sql-tool -p 5432 -u postgres -pw cadence --pl postgres create --db cadence ./cadence-sql-tool -p 5432 -u postgres -pw cadence --pl postgres --db cadence setup -v 0.0 ./cadence-sql-tool -p 5432 -u postgres -pw cadence --pl postgres --db cadence update-schema -d ./schema/postgres/cadence/versioned ./cadence-sql-tool -p 5432 -u postgres -pw cadence --pl postgres create --db cadence_visibility ./cadence-sql-tool -p 5432 -u postgres -pw cadence --pl postgres --db cadence_visibility setup-schema -v 0.0 ./cadence-sql-tool -p 5432 -u postgres -pw cadence --pl postgres --db cadence_visibility update-schema -d ./schema/postgres/visibility/versioned install-schema-sqlite: cadence-sql-tool ./cadence-sql-tool -pl sqlite --db cadence.db setup -v 0.0 ./cadence-sql-tool -pl sqlite --db cadence.db update-schema -d ./schema/sqlite/cadence/versioned ./cadence-sql-tool -pl sqlite --db cadence_visibility.db setup -v 0.0 ./cadence-sql-tool -pl sqlite --db cadence_visibility.db update-schema -d ./schema/sqlite/visibility/versioned install-schema-es-v7: curl -X PUT "http://127.0.0.1:9200/_template/cadence-visibility-template" -H 'Content-Type: application/json' -d @./schema/elasticsearch/v7/visibility/index_template.json curl -X PUT "http://127.0.0.1:9200/cadence-visibility-dev" install-schema-es-v6: curl -X PUT "http://127.0.0.1:9200/_template/cadence-visibility-template" -H 'Content-Type: application/json' -d @./schema/elasticsearch/v6/visibility/index_template.json curl -X PUT "http://127.0.0.1:9200/cadence-visibility-dev" install-schema-es-opensearch: curl -X PUT "http://127.0.0.1:9200/_template/cadence-visibility-template" -H 'Content-Type: application/json' -d @./schema/elasticsearch/os2/visibility/index_template.json curl -X PUT "http://127.0.0.1:9200/cadence-visibility-dev" start: bins ./cadence-server start install-schema-xdc: cadence-cassandra-tool $Q echo Setting up cadence_cluster0 key space ./cadence-cassandra-tool --ep 127.0.0.1 create -k cadence_cluster0 --rf 1 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_cluster0 setup-schema -v 0.0 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_cluster0 update-schema -d ./schema/cassandra/cadence/versioned ./cadence-cassandra-tool --ep 127.0.0.1 create -k cadence_visibility_cluster0 --rf 1 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_visibility_cluster0 setup-schema -v 0.0 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_visibility_cluster0 update-schema -d ./schema/cassandra/visibility/versioned $Q echo Setting up cadence_cluster1 key space ./cadence-cassandra-tool --ep 127.0.0.1 create -k cadence_cluster1 --rf 1 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_cluster1 setup-schema -v 0.0 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_cluster1 update-schema -d ./schema/cassandra/cadence/versioned ./cadence-cassandra-tool --ep 127.0.0.1 create -k cadence_visibility_cluster1 --rf 1 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_visibility_cluster1 setup-schema -v 0.0 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_visibility_cluster1 update-schema -d ./schema/cassandra/visibility/versioned $Q echo Setting up cadence_cluster2 key space ./cadence-cassandra-tool --ep 127.0.0.1 create -k cadence_cluster2 --rf 1 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_cluster2 setup-schema -v 0.0 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_cluster2 update-schema -d ./schema/cassandra/cadence/versioned ./cadence-cassandra-tool --ep 127.0.0.1 create -k cadence_visibility_cluster2 --rf 1 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_visibility_cluster2 setup-schema -v 0.0 ./cadence-cassandra-tool --ep 127.0.0.1 -k cadence_visibility_cluster2 update-schema -d ./schema/cassandra/visibility/versioned start-xdc-cluster0: cadence-server ./cadence-server --zone xdc_cluster0 start start-xdc-cluster1: cadence-server ./cadence-server --zone xdc_cluster1 start start-xdc-cluster2: cadence-server ./cadence-server --zone xdc_cluster2 start start-canary: cadence-canary ./cadence-canary start start-sharddistributor-canary: sharddistributor-canary ./sharddistributor-canary start start-bench: cadence-bench ./cadence-bench start start-mysql: cadence-server ./cadence-server --zone mysql start start-postgres: cadence-server ./cadence-server --zone postgres start # broken up into multiple += so I can interleave comments. # this all becomes a single line of output. # you must not use single-quotes within the string in this var. JQ_DEPS_AGE = jq ' # only deal with things with updates JQ_DEPS_AGE += select(.Update) # allow additional filtering, e.g. DEPS_FILTER='$(JQ_DEPS_ONLY_DIRECT)' JQ_DEPS_AGE += $(DEPS_FILTER) # add "days between current version and latest version" JQ_DEPS_AGE += | . + {Age:(((.Update.Time | fromdate) - (.Time | fromdate))/60/60/24 | floor)} # add "days between latest version and now" JQ_DEPS_AGE += | . + {Available:((now - (.Update.Time | fromdate))/60/60/24 | floor)} # 123 days: library old_version -> new_version JQ_DEPS_AGE += | ([.Age, .Available] | max | tostring) + " days: " + .Path + " \t" + .Version + " -> " + .Update.Version JQ_DEPS_AGE += ' # remove surrounding quotes from output JQ_DEPS_AGE += --raw-output # exclude `"Indirect": true` dependencies. direct ones have no "Indirect" key at all. JQ_DEPS_ONLY_DIRECT = | select(has("Indirect") | not) deps: ## Check for dependency updates, for things that are directly imported $Q make --no-print-directory DEPS_FILTER='$(JQ_DEPS_ONLY_DIRECT)' deps-all deps-all: ## Check for all dependency updates $Q go list -u -m -json all \ | $(JQ_DEPS_AGE) \ | sort -n help: ## Prints a help message showing any specially-commented targets $Q # print the high-value ones first, so they're more visible. the "....." prefixes match the shell coloring chars $Q cat $(MAKEFILE_LIST) | grep -e "^[a-zA-Z_\-]*:.* ## .*" | awk 'BEGIN {FS = ":.*? ## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' | sort | grep -E '^.....(help|pr|bins)\b' $Q echo '-----------------------------------' $Q cat $(MAKEFILE_LIST) | grep -e "^[a-zA-Z_\-]*:.* ## .*" | awk 'BEGIN {FS = ":.*? ## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' | sort | grep -vE '^.....(help|pr|bins)\b' ================================================ FILE: NOTICE ================================================ Copyright (c) 2025 Uber Technologies, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: PROPOSALS.md ================================================ # Proposing Changes to Cadence ## Introduction The design process for changes to Cadence is modeled on the [proposal process used by the M3 project](https://github.com/m3db/proposal). ## Process - [Create an issue](https://github.com/cadence-workflow/cadence/issues/new) describing the proposal. - Like any GitHub issue, a proposal issue is followed by an initial discussion about the suggestion. For Proposal issues: - The goal of the initial discussion is to reach agreement on the next step: (1) accept, (2) decline, or (3) ask for a design doc. - The discussion is expected to be resolved in a timely manner. - If the author wants to write a design doc, then they can write one. - A lack of agreement means the author should write a design doc. - If a Proposal issue leads to a design doc: - The design doc should be presented as a Google Doc and must follow [the template](https://docs.google.com/document/d/1hpWpy5MB5l8uXfnl23lebhx-dvo79vb1jFUOyAtIHJw/edit?usp=sharing). - The design doc should be linked from the opened GitHub issue. - The design doc should only allow edit access to authors of the document. - Do not create the document from a corporate G Suite account. If you want to edit from a corporate G Suite account then first create the document from a personal Google account and grant edit access to your corporate G Suite account. - Comment access should also be accessible by the public. Make sure this is the case by clicking "Share" and "Get shareable link" ensuring you select "Anyone with the link can comment". - Once comments and revisions on the design doc wind down, there is a final discussion about the proposal. - The goal of the final discussion is to reach agreement on the next step: (1) accept or (2) decline. - The discussion is expected to be resolved in a timely manner. - Once the design doc is agreed upon, the author shall export the contents of the Google Doc as a Markdown document and submit a PR to add it to the proposal repository. - Authors can use [gdocs2md](https://github.com/mangini/gdocs2md) to convert the document but any cleanup required must be done by hand. - The design doc should be checked in at `docs/design/XYZ-shortname.md`, where `XYZ` is the GitHub issue number and `shortname` is a short name (a few dash-separated words at most). - The design doc should be added to the list of existing proposals in `docs/design/index.md` - The author (and/or other contributors) do the work as described by the "Implementation" section of the proposal. --- This project is released under the [Apache License, Version 2.0](LICENSE). ================================================ FILE: README.md ================================================ # Cadence [![Build Status](https://github.com/cadence-workflow/cadence/actions/workflows/ci-checks.yml/badge.svg)](https://github.com/cadence-workflow/cadence/actions/workflows/ci-checks.yml) [![Coverage](https://codecov.io/gh/cadence-workflow/cadence/graph/badge.svg?token=7SD244ImNF)](https://codecov.io/gh/cadence-workflow/cadence) [![Slack Status](https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social)](https://communityinviter.com/apps/cloud-native/cncf) [![Github release](https://img.shields.io/github/v/release/cadence-workflow/cadence.svg)](https://github.com/cadence-workflow/cadence/releases) [![License](https://img.shields.io/github/license/cadence-workflow/cadence.svg)](http://www.apache.org/licenses/LICENSE-2.0) Cadence Workflow is an open-source platform since 2017 for building and running scalable, fault-tolerant, and long-running workflows. This repository contains the core orchestration engine and tools including CLI, schema managment, benchmark and canary. ## Getting Started Cadence backend consists of multiple services, a database (Cassandra/MySQL/PostgreSQL) and optionally Kafka+Elasticsearch. As a user, you need a worker which contains your workflow implementation. Once you have Cadence backend and worker(s) running, you can trigger workflows by using SDKs or via CLI. 1. Start cadence backend components locally ``` docker compose -f docker/docker-compose.yml up ``` 2. Run the Samples Try out the sample recipes for [Go](https://github.com/cadence-workflow/cadence-samples) or [Java](https://github.com/cadence-workflow/cadence-java-samples). 3. Visit UI Visit http://localhost:8088 to check workflow histories and detailed traces. ### Client Libraries You can implement your workflows with one of our client libraries: - [Official Cadence Go SDK](https://github.com/cadence-workflow/cadence-go-client) - [Official Cadence Java SDK](https://github.com/cadence-workflow/cadence-java-client) There are also unofficial [Python](https://github.com/firdaus/cadence-python) and [Ruby](https://github.com/coinbase/cadence-ruby) SDKs developed by the community. You can also use [iWF](https://github.com/indeedeng/iwf) as a DSL framework on top of Cadence. ### CLI Cadence CLI can be used to operate workflows, tasklist, domain and even the clusters. You can use the following ways to install Cadence CLI: * Use brew to install CLI: `brew install cadence-workflow` * Follow the [instructions](https://github.com/cadence-workflow/cadence/discussions/4457) if you need to install older versions of CLI via homebrew. Usually this is only needed when you are running a server of a too old version. * Use docker image for CLI: `docker run --rm ubercadence/cli:` or `docker run --rm ubercadence/cli:master ` . Be sure to update your image when you want to try new features: `docker pull ubercadence/cli:master ` * Build the CLI binary yourself, check out the repo and run `make cadence` to build all tools. See [CONTRIBUTING](CONTRIBUTING.md) for prerequisite of make command. * Build the CLI image yourself, see [instructions](docker/README.md#diy-building-an-image-for-any-tag-or-branch) Cadence CLI is a powerful tool. The commands are organized by tabs. E.g. `workflow`->`batch`->`start`, or `admin`->`workflow`->`describe`. Please read the [documentation](https://cadenceworkflow.io/docs/cli/#documentation) and always try out `--help` on any tab to learn & explore. ### UI Try out [Cadence Web UI](https://github.com/cadence-workflow/cadence-web) to view your workflows on Cadence. (This is already available at localhost:8088 if you run Cadence with docker compose) ### Other binaries in this repo #### Bench/stress test workflow tools See [bench documentation](./bench/README.md). #### Periodical feature health check workflow tools(aka Canary) See [canary documentation](./canary/README.md). #### Schema tools for SQL and Cassandra The tools are for [manual setup or upgrading database schema](docs/persistence.md) * If server runs with Cassandra, Use [Cadence Cassandra tool](tools/cassandra/README.md) * If server runs with SQL database, Use [Cadence SQL tool](tools/sql/README.md) The easiest way to get the schema tool is via homebrew. `brew install cadence-workflow` also includes `cadence-sql-tool` and `cadence-cassandra-tool`. * The schema files are located at `/usr/local/etc/cadence/schema/`. * To upgrade, make sure you remove the old ElasticSearch schema first: `mv /usr/local/etc/cadence/schema/elasticsearch /usr/local/etc/cadence/schema/elasticsearch.old && brew upgrade cadence-workflow`. Otherwise ElasticSearch schemas may not be able to get updated. * Follow the [instructions](https://github.com/cadence-workflow/cadence/discussions/4457) if you need to install older versions of schema tools via homebrew. However, easier way is to use new versions of schema tools with old versions of schemas. All you need is to check out the older version of schemas from this repo. Run `git checkout v0.21.3` to get the v0.21.3 schemas in [the schema folder](/schema). ## Contributing We'd love your help in making Cadence great. Please review our [contribution guide](CONTRIBUTING.md). If you'd like to propose a new feature, first join the [CNCF Slack workspace](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel to start a discussion. Please visit our [documentation](https://cadenceworkflow.io/docs/operation-guide/) site for production/cluster setup. ### Learning Resources See Maxim's talk at [Data@Scale Conference](https://atscaleconference.com/videos/cadence-microservice-architecture-beyond-requestreply) for an architectural overview of Cadence. Visit [cadenceworkflow.io](https://cadenceworkflow.io) to learn more about Cadence. Join us in [Cadence Documentation](https://github.com/cadence-workflow/Cadence-Docs) project. Feel free to raise an Issue or Pull Request there. ### Community * [Github Discussion](https://github.com/cadence-workflow/cadence/discussions) * Best for Q&A, support/help, general discusion, and annoucement * [Github Issues](https://github.com/cadence-workflow/cadence/issues) * Best for reporting bugs and feature requests * [StackOverflow](https://stackoverflow.com/questions/tagged/cadence-workflow) * Best for Q&A and general discusion * [Slack](https://communityinviter.com/apps/cloud-native/cncf) - Join **#cadence-users** channel on CNCF Slack * Best for contributing/development discussion ## Stars over time [![Stargazers over time](https://starchart.cc/uber/cadence.svg?variant=adaptive)](https://starchart.cc/uber/cadence) ## License Apache 2.0 License, please see [LICENSE](https://github.com/cadence-workflow/cadence/blob/master/LICENSE) for details. ================================================ FILE: RELEASES.md ================================================ # Releases upgrade instruction ## Upgrade to 0.16 and above **TL;DR:** If your Cadence service is running on or above 0.14.0 and you do not have workflows running for more than 6 months. It is safe to upgrade without any operations. If your cluster has open workflows for more than 6 months, please download and run the following CLI command prior to the upgrades. Prior to release 0.16, the workflow state structure contains a field called replication state. Instead, we introduce a new field 'version histories' to manage the replication. In release 0.16, this field 'replication state' has been removed from the code path. From release 0.14, the field 'replication state' has been deprecated. If you are upgrading the service from version below 0.14.0 to 0.16.0 or you may have workflows running for more than 6 months, please consider follow the instruction to see if you have any unsupported workflows. ## Detect unsupported open workflows Run the following command based on your database. Cassandra: `cadence admin db unsupported-workflow --db_type=cassandra --db_address --db_port --username= --password= --keyspace --lower_shard_bound= --upper_shard_bound= --rps --output_filename ./cadence_scan` MySQL/Postgres: `cadence admin db unsupported-workflow --db_type= --db_address --db_port --username= --password= --db_name --lower_shard_bound= --upper_shard_bound= --rps --output_filename ./cadence_scan` **Note:** This CLI is a long-running process to scan the database for unsupported workflows. If you have TLS configurations or use customized encoding/decoding type. Please use `cadence adm db unsupported-workflow --help` to configurate the correct connection. After the CLI completes, a list of unsupported workflows will be listed in the file `cadence_scan`. For example: `cadence --address : --domain workflow reset --wid helloworld_7f2fa1fe-594e-44ad-a9e2-7ac7c5f97861 --rid e79ce972-b657-48a4-bba9-e2f2ac6424e2 --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade'` ## Reset the workflow Use reset CLI to reset these unsupported workflow. This operation is to migrate these unsupported workflow data. 1. replace the host and port with your cadence host address and port number. 2. replace the domain uuid with the domain name. To find the domain name, you can use `cadence --address : domain describe --domain_id=`. After replacing the fields with the correct values, you can copy/paste to run the CLI. This is going to reset those workflows to the last decision task completed event. **Note:** If your workflows has outstanding child workflows, you have to wait for these child workflows completion or terminate the workflows. ================================================ FILE: bench/README.md ================================================ Cadence Bench Tests =================== This README describes how to set up Cadence bench, different types of bench loads, and how to start the load. Setup ----------- ### Cadence server Bench suite is running against a Cadence server/cluster. See [documentation](https://cadenceworkflow.io/docs/operation-guide/setup/) for Cadence server cluster setup. Note that only the Basic bench test don't require Advanced Visibility. Other advanced bench tests requires Cadence server with Advanced Visibility. For local env you can run it through: - Docker: Instructions for running Cadence server through docker can be found in `docker/README.md`. Either `docker-compose-es-v7.yml` or `docker-compose-es.yml` can be used to start the server. - Build from source: Please check [CONTRIBUTING](/CONTRIBUTING.md) for how to build and run Cadence server from source. Please also make sure Kafka and ElasticSearch are running before starting the server with `./cadence-server --zone es start`. If ElasticSearch v7 is used, change the value for `--zone` flag to `es_v7`. See more [documentation here](https://cadenceworkflow.io/docs/concepts/search-workflows/). ### Bench Workers :warning: NOTE: Starting this bench worker will not automatically start a bench test. Next two sections will cover how to start and configure it. Different ways of start the bench workers: #### 1. Use docker image `ubercadence/cadence-bench:master` You can [pre-built docker-compose file](../docker/docker-compose-bench.yml) to run against local server In the `docker/` directory, run: ``` docker compose -f docker-compose-bench.yml up ``` You can modify [the bench worker config](../docker/config/bench/development.yaml) to run against a prod server cluster. Or may run it with Kubernetes, for [example](https://github.com/longquanzheng/cadence-lab/blob/master/eks/bench-deployment.yaml). NOTE: Similar to server/CLI images, the `master` image will be built and published automatically by Github on every commit onto the `master` branch. To use a different image than `master` tag. See [docker hub](https://hub.docker.com/repository/docker/ubercadence/cadence-bench) for all the images. #### 2. Build & Run the binary In the project root, build cadence bench binary: ``` make cadence-bench ``` Then start bench worker: ``` ./cadence-bench start ``` By default, it will load [the configuration in `config/bench/development.yaml`](../config/bench/development.yaml). Run `./cadence-bench -h` for details to understand the start options of how to change the loading directory if needed. Worker Configurations ---------------------- Bench workers configuration contains two parts: - **Bench**: this part controls the client side, including the bench service name, which domains bench workers are responsible for and how many taskLists each domain should use. ```yaml bench: name: "cadence-bench" # bench name domains: ["cadence-bench", "cadence-bench-sync", "cadence-bench-batch"] # it will start workers on all those domains(also try to register if not exists) numTaskLists: 3 # it will start workers listening on cadence-bench-tl-0, cadence-bench-tl-1, cadence-bench-tl-2 ``` 1. Bench workers will only poll from task lists whose name start with `cadence-bench-tl-`. If in the configuration, `numTaskLists` is specified to be 2, then workers will only listen to `cadence-bench-tl-0` and `cadence-bench-tl-1`. So make sure you use a valid task list name when starting the bench load. 2. When starting bench workers, it will try to register a **local domain with archival feature disabled** for each domain name listed in the configuration, if not already exists. If your want to test the performance of global domains and/or archival feature, please register the domains first before starting the worker. - **Cadence**: this control how bench worker should talk to Cadence server, which includes the server's service name and address. ```yaml cadence: service: "cadence-frontend" # frontend service name host: "127.0.0.1:7833" # frontend address #metrics: ... # optional detailed client side metrics like workflow latency ``` - **Metrics**: metrics configuration. Similar to server metric emitter, only M3/Statsd/Prometheus is supported. - **Log**: logging configuration. Similar to server logging configuration. Bench Load Types ----------- This section briefly describes the purpose of each bench load and provides a sample command for running the load. Detailed descriptions for each test's configuration can be found in `bench/lib/config.go` Please note that all load configurations in `config/bench` is for only local development and illustration purpose, it does not reflect the actual capability of Cadence server. ### Basic :warning: NOTE: This is the only bench test which doesn't require advanced visibility feature on the server. Make sure you set `useBasicVisibilityValidation` to true if run with basic(db) visibility. Also basicVisibilityValidation requires only one test load run in the same domain. This is because of the limitation of basic visibility now allow using workflowType and status filters at the same time. As the name suggests, this load tests the basic case of load testing. You will start a `launchWorkflow` which will execute some `launchActivities` to start `stressWorkflows`. Then the stressWorkflows running activities in sequential/parallel. Once all stressWorkflows are started, launchWorkflow will wait stressWorkflows timeout + buffer time(default to 5 mins) before checking the status of all test workflows. Two criteria must be met to pass the verification: 1. No open workflows(this means server may lose some tasks and not able to close the stressWorkflows) 2. Failed/timeouted workflows <= threshold(totalLaunchCount * failureThreshold ) The basic load can also be run in "panic" mode by setting `"panicStressWorkflow": true,` to test if server can handle large number of panic workflows (which can be caused by a bad worker deployment). Sample configuration can be found in `config/bench/basic.json` and `config/ben/basic_panic.json`. To start the test, a sample command can be ``` cadence --do wf start --tl cadence-bench-tl-0 --wt basic-load-test-workflow --dt 30 --et 3600 --if config/bench/basic.json ``` `` needs to be one of the domains in bench config (by default ./config/bench/development.yaml), e.g. `cadence-bench`. Then wait for the bench test result. ``` $cadence --do cadence-bench wf ob -w a2813321-a1bd-40c6-934f-88ad0ded6037 Progress: 1, 2021-08-20T11:49:14-07:00, WorkflowExecutionStarted 2, 2021-08-20T11:49:14-07:00, DecisionTaskScheduled ... ... 20, 2021-08-20T11:59:24-07:00, DecisionTaskStarted 21, 2021-08-20T11:59:24-07:00, DecisionTaskCompleted 22, 2021-08-20T11:59:24-07:00, WorkflowExecutionCompleted Result: Run Time: 26 seconds Status: COMPLETED Output: "TEST PASSED. Details report: timeoutCount: 0, failedCount: 0, openCount:0, launchCount: 100, maxThreshold:1" ``` The output/error result shows whether the test passes with detailed report. Configuration of basic load type. The config is passed as the launch workflow input parameter using a JSON file. ```yaml # configuration for launch workflow useBasicVisibilityValidation: use basic(db based) visibility to verify the stress workflows, default false which requires advanced visibility on the server totalLaunchCount : total number of stressWorkflows that started by the launchWorkflow waitTimeBufferInSeconds : buffer time in addition of ExecutionStartToCloseTimeoutInSeconds to wait for stressWorkflows before verification, default 300(5 minutes) routineCount : number of in-parallel launch activities that started by launchWorkflow, to start the stressWorkflows failureThreshold : the threshold of failed stressWorkflow for deciding whether or not the whole testSuite failed. maxLauncherActivityRetryCount : the max retry on launcher activity to start stress workflows, default: 5 contextTimeoutInSeconds : RPC timeout inside activities(e.g. starting a stressWorkflow) default 3s # configuration for stress workflow executionStartToCloseTimeoutInSeconds : StartToCloseTimeout of stressWorkflow, default 5m chainSequence : number of steps in the stressWorkflow concurrentCount : number of in-parallel activity(dummy activity only echo data) in a step of the stressWorkflow payloadSizeBytes : payloadSize of echo data in the dummy activity minCadenceSleepInSeconds : control sleep time between two steps in the stressWorkflow, actual sleep time = random(min,max), default: 0 maxCadenceSleepInSeconds : control sleep time between two steps in the stressWorkflow, actual sleep time = random(min,max), default: 0 panicStressWorkflow : if true, stressWorkflow will always panic, default false ``` ### Cancellation The load tests the StartWorkflowExecution and CancelWorkflowExecution sync API, and validates the number of cancelled workflows and if there's any open workflow. Sample configuration can be found in `config/bench/cancellation.json` and it can be started with ``` cadence --do wf start --tl cadence-bench-tl-0 --wt cancellation-load-test-workflow --dt 30 --et 3600 --if config/bench/cancellation.json ``` ### Signal The load tests the SignalWorkflowExecution and SignalWithStartWorkflowExecution sync API, and validates the latency of signaling, the number of successfully completed workflows and if there's any open workflow. Sample configuration can be found in `config/bench/signal.json` and it can be started with ``` cadence --do cadence-bench wf start --tl cadence-bench-tl-0 --wt signal-load-test-workflow --dt 30 --et 3600 --if config/bench/signal.json ``` ### Concurrent Execution The purpose of this load is to test when a workflow schedules a large number of activities or child workflows in a single decision batch, whether server can properly throttle the processing of this workflow without affecting the execution of workflows in other domains. It will also check if the delayed period is within limit or not and fail the test if it takes too long. A typical usage will be run this load and another load for testing sync APIs (for example, basic, cancellation or signal) in two different test suites/domains (so that they are run in parallel in two domains). Apply proper task processing throttling configuration to the domain that is running the concurrent execution test and see if tests in the other domain can still pass or not. Sample configuration can be found in `config/bench/concurrent_execution.json` and it can be started with ``` cadence --do wf start --tl cadence-bench-tl-0 --wt concurrent-execution-test-workflow --dt 30 --et 3600 --if config/bench/concurrent_execution.json ``` ### Timer This load tests if Cadence server can properly handle the case when one domain fires a large number of timers in a short period of time. Ideally timer from that domain should be throttled and delayed without affecting workflows in other domains. It will also check if the delayed period is within limit or not and fail the test if the timer latency is too high. Typical usage is the same as the concurrent execution load above. Run it in parallel with another sync API test and see if the other test can pass or not. Sample configuration can be found in `config/bench/timer.json` and it can be started with ``` cadence --do wf start --tl cadence-bench-tl-0 --wt timer-load-test-workflow --dt 30 --et 3600 --if config/bench/timer.json ``` ### Cron: Run all the workloads as a TestSuite :warning: NOTE: This requires a search attribute named `Passed` as boolean type. This search attribute should have been added to the [ES schema](/schema/elasticsearch). make sure the dynamic config also have [this search attribute (`frontend.validSearchAttributes`)](/config/dynamicconfig/development_es.yaml), so that Cadence server can recognize it. * Validate `Passed` has been successfully added in the dynamic config: ``` cadence cluster get-search-attr ``` `Cron` itself is not a test. It is responsible for running all other tests in parallel or sequential according a cron schedule. Tests in `Cron` are divided to into multiple test suites. Tests in different test suites will be run in parallel, while tests within a test suite will be run in a random sequential order. Different test suites can also be run in different domains, which provides a way for testing the multi-tenant performance of Cadence server. On the completion of each test, `Cron` will be signaled with the result of the test, which can be queried through: ``` cadence --do wf query --wid --qt test-results ``` This command will show the result of all completed tests. When all tests complete, `Cron` will update the value of the `Passed` search attribute accordingly. `Passed` will be set to `true` only when all tests have passed, and `false` otherwise. Since the last event for cron workflow is always WorkflowContinuedAsNew, this search attribute can be used to tell whether one run of `Cron` is successful or not. You can see the search attribute value by adding `--psa` flag to workflow list commands when listing `Cron` runs. A sample cron configuration is in `config/bench/cron.json`, and it can be started with ``` cadence --do wf start --tl cadence-bench-tl-0 --wt cron-test-workflow --dt 30 --et 7200 --if config/bench/cron.json ``` ================================================ FILE: bench/lib/client.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package lib import ( "context" "fmt" "time" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/client" "go.uber.org/yarpc" "go.uber.org/yarpc/transport/grpc" ) const workflowRetentionDays = 1 // CadenceClient is an abstraction on top of // the cadence library client that serves as // a union of all the client interfaces that // the library exposes type CadenceClient struct { client.Client // domainClient only exposes domain API client.DomainClient // this is the service needed to start the workers Service workflowserviceclient.Interface } // CreateDomain creates a cadence domain with the given name and description // if the domain already exist, this method silently returns success func (client CadenceClient) CreateDomain(name string, desc string, owner string) error { emitMetric := true isGlobalDomain := false retention := int32(workflowRetentionDays) req := &shared.RegisterDomainRequest{ Name: &name, Description: &desc, OwnerEmail: &owner, WorkflowExecutionRetentionPeriodInDays: &retention, EmitMetric: &emitMetric, IsGlobalDomain: &isGlobalDomain, } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() err := client.Register(ctx, req) if err != nil { if _, ok := err.(*shared.DomainAlreadyExistsError); !ok { return err } } return nil } // NewCadenceClients builds a CadenceClient for each domain from the runtimeContext func NewCadenceClients(runtime *RuntimeContext) (map[string]CadenceClient, error) { cadenceClients := make(map[string]CadenceClient) for _, domain := range runtime.Bench.Domains { client, err := NewCadenceClientForDomain(runtime, domain) if err != nil { return nil, err } cadenceClients[domain] = client } return cadenceClients, nil } // NewCadenceClientForDomain builds a CadenceClient for a specified domain based on runtimeContext func NewCadenceClientForDomain( runtime *RuntimeContext, domain string, ) (CadenceClient, error) { transport := grpc.NewTransport( grpc.ServiceName(runtime.Bench.Name), ) dispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: runtime.Bench.Name, Outbounds: yarpc.Outbounds{ runtime.Cadence.ServiceName: {Unary: transport.NewSingleOutbound(runtime.Cadence.HostNameAndPort)}, }, }) if err := dispatcher.Start(); err != nil { dispatcher.Stop() return CadenceClient{}, fmt.Errorf("failed to create outbound transport channel: %v", err) } var cadenceClient CadenceClient cadenceClient.Service = workflowserviceclient.New(dispatcher.ClientConfig(runtime.Cadence.ServiceName)) cadenceClient.Client = client.NewClient( cadenceClient.Service, domain, &client.Options{ MetricsScope: runtime.Metrics, }, ) cadenceClient.DomainClient = client.NewDomainClient( cadenceClient.Service, &client.Options{ MetricsScope: runtime.Metrics, }, ) return cadenceClient, nil } ================================================ FILE: bench/lib/config.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package lib import ( "errors" "github.com/uber/cadence/common/config" ) const ( // EnvKeyRoot the environment variable key for runtime root dir EnvKeyRoot = "CADENCE_BENCH_ROOT" // EnvKeyConfigDir the environment variable key for config dir EnvKeyConfigDir = "CADENCE_BENCH_CONFIG_DIR" // EnvKeyEnvironment is the environment variable key for environment EnvKeyEnvironment = "CADENCE_BENCH_ENVIRONMENT" // EnvKeyAvailabilityZone is the environment variable key for AZ EnvKeyAvailabilityZone = "CADENCE_BENCH_AVAILABILITY_ZONE" ) type ( // Config contains the configuration for cadence bench Config struct { Bench Bench `yaml:"bench"` Cadence Cadence `yaml:"cadence"` Log config.Logger `yaml:"log"` Metrics config.Metrics `yaml:"metrics"` } // Cadence contains the configuration for cadence service Cadence struct { ServiceName string `yaml:"service"` HostNameAndPort string `yaml:"host"` } // Bench contains the configuration for bench tests Bench struct { Name string `yaml:"name"` Domains []string `yaml:"domains"` NumTaskLists int `yaml:"numTaskLists"` } // TODO: add comment for each config field // CronTestConfig contains the configuration for running a set of // testsuites in parallel based on a cron schedule CronTestConfig struct { TestSuites []TestSuiteConfig `yaml:"testSuites"` } // TestSuiteConfig contains the configration for running a set of // tests sequentially TestSuiteConfig struct { Name string `yaml:"name"` Domain string `yaml:"domain"` Configs []AggregatedTestConfig `yaml:"configs"` } // AggregatedTestConfig contains the configration for a single test AggregatedTestConfig struct { Name string `yaml:"name"` Description string `yaml:"description"` TimeoutInSeconds int32 `yaml:"timeoutInSeconds"` Basic *BasicTestConfig `yaml:"basic"` Signal *SignalTestConfig `yaml:"signal"` Timer *TimerTestConfig `yaml:"timer"` ConcurrentExec *ConcurrentExecTestConfig `yaml:"concurrentExec"` Cancellation *CancellationTestConfig `yaml:"cancellation"` } // BasicTestConfig contains the configuration for running the Basic test scenario BasicTestConfig struct { // Launch workflow config UseBasicVisibilityValidation bool `yaml:"useBasicVisibilityValidation"` // use basic(db based) visibility to verify the stress workflows, default false which requires advanced visibility on the server TotalLaunchCount int `yaml:"totalLaunchCount"` // total number of stressWorkflows that started by the launchWorkflow RoutineCount int `yaml:"routineCount"` // number of in-parallel launch activities that started by launchWorkflow, to start the stressWorkflows FailureThreshold float64 `yaml:"failureThreshold"` // the threshold of failed stressWorkflow for deciding whether or not the whole testSuite failed. MaxLauncherActivityRetryCount int `yaml:"maxLauncherActivityRetryCount"` // the max retry on launcher activity to start stress workflows, default: 5 ContextTimeoutInSeconds int `yaml:"contextTimeoutInSeconds"` // RPC timeout inside activities(e.g. starting a stressWorkflow) default 3s WaitTimeBufferInSeconds int `yaml:"waitTimeBufferInSeconds"` // buffer time in addition of ExecutionStartToCloseTimeoutInSeconds to wait for stressWorkflows before verification, default 300(5 minutes) // Stress workflow config ExecutionStartToCloseTimeoutInSeconds int `yaml:"executionStartToCloseTimeoutInSeconds"` // StartToCloseTimeout of stressWorkflow, default 5m ChainSequence int `yaml:"chainSequence"` // number of steps in the stressWorkflow ConcurrentCount int `yaml:"concurrentCount"` // number of in-parallel activity(dummy activity only echo data) in a step of the stressWorkflow PayloadSizeBytes int `yaml:"payloadSizeBytes"` // payloadSize of echo data in the dummy activity MinCadenceSleepInSeconds int `yaml:"minCadenceSleepInSeconds"` // control sleep time between two steps in the stressWorkflow, actual sleep time = random(min,max), default: 0 MaxCadenceSleepInSeconds int `yaml:"maxCadenceSleepInSeconds"` // control sleep time between two steps in the stressWorkflow, actual sleep time = random(min,max), default: 0 PanicStressWorkflow bool `yaml:"panicStressWorkflow"` // if true, stressWorkflow will always panic, default false } // SignalTestConfig is the parameters for signalLoadTestWorkflow SignalTestConfig struct { // LoaderCount defines how many loader activities LoaderCount int `yaml:"loaderCount"` // LoadTestWorkflowCount defines how many load test workflow in total LoadTestWorkflowCount int `yaml:"loadTestWorkflowCount"` // SignalCount is the number of signals per workflow SignalCount int `yaml:"signalCount"` // SignalDataSize is the size of signal data SignalDataSize int `yaml:"signalDataSize"` // RateLimit is per loader rate limit to hit cadence server RateLimit int `yaml:"rateLimit"` WorkflowExecutionTimeoutInSeconds int `yaml:"workflowExecutionTimeoutInSeconds"` DecisionTaskTimeoutInSeconds int `yaml:"decisionTaskTimeoutInSeconds"` FailureThreshold float64 `yaml:"failureThreshold"` ProcessSignalWorkflowConfig } // ProcessSignalWorkflowConfig is the parameters to process signal workflow ProcessSignalWorkflowConfig struct { // CampaignCount is the number of local activities to be executed CampaignCount int `yaml:"campaignCount"` // ActionRate is probability that local activities result in actual action ActionRate float64 `yaml:"actionRate"` // Local activity failure rate FailureRate float64 `yaml:"failureRate"` // SignalCount before continue as new SignalBeforeContinueAsNew int `yaml:"signalBeforeContinueAsNew"` EnableRollingWindow bool `yaml:"enableRollingWindow"` ScheduleTimeNano int64 `yaml:"scheduleTimeNano"` MaxSignalDelayInSeconds int `yaml:"maxSignalDelayInSeconds"` MaxSignalDelayCount int `yaml:"maxSignalDelayCount"` } // TimerTestConfig contains the config for running timer bench test TimerTestConfig struct { // TotalTimerCount is the total number of timers to fire TotalTimerCount int `yaml:"totalTimerCount"` // TimerPerWorkflow is the number of timers in each workflow // workflow will continue execution and complete when the first timer fires // Set this number larger than one to test no-op timer case // TotalTimerCount / TimerPerWorkflow = total number of workflows TimerPerWorkflow int `yaml:"timerPerWorkflow"` // ShortestTimerDurationInSeconds after test start, the first timer will fire ShortestTimerDurationInSeconds int `yaml:"shortestTimerDurationInSeconds"` // LongestTimerDurationInSeconds after test start, the last timer will fire LongestTimerDurationInSeconds int `yaml:"longestTimerDurationInSeconds"` // MaxTimerLatencyInSeconds specifies the maximum latency for the first timer in the workflow // if a timer's latency is larger than this value, that timer will be considered as failed // if > 1% timer fire beyond this threshold, the test will fail MaxTimerLatencyInSeconds int `yaml:"maxTimerLatencyInSeconds"` // TimerTimeoutInSeconds specifies the duration beyond which a timer is considered as lost and fail the test TimerTimeoutInSeconds int `yaml:"timerTimeoutInSeconds"` // RoutineCount is the number of goroutines used for starting workflows // approx. RPS = 10 * RoutineCount // # of workflows = TotalTimerCount / TimerPerWorkflow // please make sure ShortestTimerDurationInSeconds > # of workflows / RPS, so that timer starts firing // after all workflows has been started. // please also make sure test timeout > LongestTimerDurationInSeconds + TimerTimeoutInSeconds RoutineCount int `yaml:"routineCount"` } // ConcurrentExecTestConfig contains the config for running concurrent execution test ConcurrentExecTestConfig struct { // TotalBatches is the total number of batches TotalBatches int `yaml:"totalBatches"` // Concurrency specifies the number of batches that will be run concurrently Concurrency int `yaml:"concurrency"` // BatchType specifies the type of batch, can be either "activity" or "childWorkflow", case insensitive BatchType string `yaml:"batchType"` // BatchSize specifies the number of activities or childWorkflows scheduled in a single decision batch BatchSize int `yaml:"batchSize"` // BatchPeriodInSeconds specifies the time interval between two set of batches (each set has #concurrency batches) BatchPeriodInSeconds int `yaml:"batchPeriodInSeconds"` // BatchMaxLatencyInSeconds specifies the max latency for scheduling/starting the activity or childWorkflow. // if latency is higher than this number, the corresponding activity or childWorkflow will be // considered as failed. This bench test is considered as success if: // avg(succeed activity or childWorkflow / batchSize) >= 0.99 // If any of the activity or childWorkflow returns an error (for example, execution takes longer than BatchTimeoutInSeconds), // the bench test will fail immediately BatchMaxLatencyInSeconds int `yaml:"batchMaxLatencyInSeconds"` // BatchTimeoutInSeconds specifies the timeout for each batch execution BatchTimeoutInSeconds int `yaml:"batchTimeoutInSeconds"` } // CancellationTestConfig contains the config for running workflow cancellation test CancellationTestConfig struct { // TotalLaunchCount is the total number of workflow to start // note: make sure TotalLaunchCount mod Concurrency = 0 otherwise the validation for the test will fail TotalLaunchCount int `yaml:"totalLaunchCount"` // Concurrency specifies the concurrency for start and cancel workflow execution // approx. RPS = 10 * concurrency, approx. duration = totalLaunchCount / RPS Concurrency int `yaml:"concurrency"` // ContextTimeoutInSeconds specifies the context timeout for start and cancel workflow execution call // default: 3s ContextTimeoutInSeconds int `yaml:"contextTimeoutInSeconds"` } ) func (c *Config) Validate() error { if len(c.Bench.Name) == 0 { return errors.New("missing value for bench service name") } if len(c.Bench.Domains) == 0 { return errors.New("missing value for domains property") } if c.Bench.NumTaskLists == 0 { return errors.New("number of taskLists can not be 0") } return nil } ================================================ FILE: bench/lib/config_test.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package lib import ( "testing" "github.com/stretchr/testify/suite" ) type ConfigTestSuite struct { suite.Suite } func TestConfigTestSuite(t *testing.T) { suite.Run(t, new(ConfigTestSuite)) } func (s *ConfigTestSuite) TestValidate() { testCases := []func(*Config){ func(c *Config) { c.Bench.Name = "" }, func(c *Config) { c.Bench.Domains = []string{} }, func(c *Config) { c.Bench.NumTaskLists = 0 }, } for _, tc := range testCases { config := s.buildConfig() tc(&config) s.Error(config.Validate()) } } func (s *ConfigTestSuite) buildConfig() Config { return Config{ Bench: Bench{ Name: "cadence-bench", Domains: []string{"cadence-bench"}, NumTaskLists: 1, }, } } ================================================ FILE: bench/lib/context.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package lib import ( "github.com/uber-go/tally" "go.uber.org/zap" "github.com/uber/cadence/common/log" ) const ( defaultCadenceLocalHostPort = "127.0.0.1:7933" defaultCadenceServiceName = "cadence-frontend" ) // ContextKey is an alias for string, used as context key type ContextKey string const ( // CtxKeyRuntimeContext is the name of the context key whose value is the RuntimeContext CtxKeyRuntimeContext = ContextKey("ctxKeyRuntimeCtx") // CtxKeyCadenceClient is the name of the context key for the cadence client this cadence worker listens to CtxKeyCadenceClient = ContextKey("ctxKeyCadenceClient") ) // RuntimeContext contains all of the context information // needed at cadence bench runtime type RuntimeContext struct { Bench Bench Cadence Cadence Logger *zap.Logger Metrics tally.Scope } // NewRuntimeContext builds a runtime context from the config func NewRuntimeContext(cfg *Config) (*RuntimeContext, error) { logger, err := cfg.Log.NewZapLogger() if err != nil { return nil, err } metricsScope := cfg.Metrics.NewScope(log.NewLogger(logger), cfg.Bench.Name) if cfg.Cadence.ServiceName == "" { cfg.Cadence.ServiceName = defaultCadenceServiceName } if cfg.Cadence.HostNameAndPort == "" { cfg.Cadence.HostNameAndPort = defaultCadenceLocalHostPort } return &RuntimeContext{ Bench: cfg.Bench, Cadence: cfg.Cadence, Logger: logger, Metrics: metricsScope, }, nil } ================================================ FILE: bench/lib/metrics.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package lib import ( "fmt" "time" "github.com/uber-go/tally" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) // counters go here const ( startedCount = "started" FailedCount = "failed" successCount = "succeeded" errTimeoutCount = "errors.timeout" errIncompatibleVersion = "errors.incompatibleversion" ) // latency metrics go here const ( latency = "latency" startLatency = "latency.schedule-to-start" ) const workflowVersion = 1 const workflowChangeID = "initial" // WorkflowMetricsProfile is the state that's needed to // record success/failed and latency metrics at the end // of a workflow type WorkflowMetricsProfile struct { ctx workflow.Context startTimestamp int64 Scope tally.Scope } // BeginWorkflow executes the common steps involved in all the workflow functions // It checks for workflow task version compatibility and also records the execution // in m3. This function must be the first call in every workflow function // Returns metrics scope on success, error on failure func BeginWorkflow(ctx workflow.Context, wfType string, scheduledTimeNanos int64) (*WorkflowMetricsProfile, error) { profile := recordWorkflowStart(ctx, wfType, scheduledTimeNanos) if err := checkWFVersionCompatibility(ctx); err != nil { profile.Scope.Counter(errIncompatibleVersion).Inc(1) return nil, err } return profile, nil } // End records the elapsed time and reports the latency, // success, failed counts to m3 func (profile *WorkflowMetricsProfile) End(err error) error { now := workflow.Now(profile.ctx).UnixNano() elapsed := time.Duration(now - profile.startTimestamp) return recordWorkflowEnd(profile.Scope, elapsed, err) } // RecordActivityStart emits metrics at the beginning of an activity function func RecordActivityStart( scope tally.Scope, name string, scheduledTimeNanos int64, ) (tally.Scope, tally.Stopwatch) { scope = scope.Tagged(map[string]string{"operation": name}) elapsed := max(0, time.Now().UnixNano()-scheduledTimeNanos) scope.Timer(startLatency).Record(time.Duration(elapsed)) scope.Counter(startedCount).Inc(1) sw := scope.Timer(latency).Start() return scope, sw } // RecordActivityEnd emits metrics at the end of an activity function func RecordActivityEnd( scope tally.Scope, sw tally.Stopwatch, err error, ) { sw.Stop() if err != nil { scope.Counter(FailedCount).Inc(1) return } scope.Counter(successCount).Inc(1) } // workflowMetricScope creates and returns a child metric scope with tags // that identify the current workflow type func workflowMetricScope( ctx workflow.Context, wfType string, ) tally.Scope { parent := workflow.GetMetricsScope(ctx) return parent.Tagged(map[string]string{"operation": wfType}) } // recordWorkflowStart emits metrics at the beginning of a workflow function func recordWorkflowStart( ctx workflow.Context, wfType string, scheduledTimeNanos int64, ) *WorkflowMetricsProfile { now := workflow.Now(ctx).UnixNano() scope := workflowMetricScope(ctx, wfType) elapsed := max(0, now-scheduledTimeNanos) scope.Timer(startLatency).Record(time.Duration(elapsed)) scope.Counter(startedCount).Inc(1) return &WorkflowMetricsProfile{ ctx: ctx, startTimestamp: now, Scope: scope, } } // recordWorkflowEnd emits metrics at the end of a workflow function func recordWorkflowEnd( scope tally.Scope, elapsed time.Duration, err error, ) error { scope.Timer(latency).Record(elapsed) if err == nil { scope.Counter(successCount).Inc(1) return err } scope.Counter(FailedCount).Inc(1) if _, ok := err.(*workflow.TimeoutError); ok { scope.Counter(errTimeoutCount).Inc(1) } return err } // checkWFVersionCompatibility takes a workflow.Context param and // validates that the workflow task currently being handled // is compatible with this version of the bench - this method // MUST only be called within a workflow function and it MUST // be the first line in the workflow function // Returns an error if the version is incompatible func checkWFVersionCompatibility(ctx workflow.Context) error { version := workflow.GetVersion(ctx, workflowChangeID, workflowVersion, workflowVersion) if version != workflowVersion { workflow.GetLogger(ctx).Error("workflow version mismatch", zap.Int("want", int(workflowVersion)), zap.Int("got", int(version))) return fmt.Errorf("workflow version mismatch, want=%v, got=%v", workflowVersion, version) } return nil } ================================================ FILE: bench/lib/types.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package lib type ( // Runnable is an interface for anything that exposes a Run method Runnable interface { Run() error } ) ================================================ FILE: bench/load/basic/launchWorkflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package basic import ( "context" "fmt" "math/rand" "time" "go.uber.org/cadence" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/client" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/bench/lib" "github.com/uber/cadence/bench/load/common" c "github.com/uber/cadence/common" ) const ( // TestName is the test name for basic test TestName = "basic" // LauncherWorkflowName is the workflow name for launching basic load test LauncherWorkflowName = "basic-load-test-workflow" // LauncherLaunchActivityName is the activity name for launching stress load test LauncherLaunchActivityName = "basic-load-test-launch-activity" // LauncherVerifyActivityName is the verification activity name LauncherVerifyActivityName = "basic-load-test-verify-activity" defaultStressWorkflowStartToCloseTimeout = 5 * time.Minute // default 5m may not be not enough for long running workflow defaultStressWorkflowWaitTimeBuffer = 5 * time.Minute // time buffer of waiting for stressWorkflow execution. Hence the actual waiting time is stressWorkflowStartToCloseTimeout + defaultStressWorkflowWaitTimeBuffer defaultMaxLauncherActivityRetryCount = 5 // number of retry for launcher activity ) type ( launcherActivityParams struct { RoutineID int // the ID of the launchActivity Count int // stressWorkflows to start per launchActivity Config lib.BasicTestConfig // config of this load test } verifyActivityParams struct { WorkflowStartTime int64 ListWorkflowPageSize int32 UseBasicVisibilityValidation bool } verifyActivityResult struct { OpenCount int TimeoutCount int FailedCount int } ) // RegisterLauncher registers workflows and activities for basic load launching func RegisterLauncher(w worker.Worker) { w.RegisterWorkflowWithOptions(launcherWorkflow, workflow.RegisterOptions{Name: LauncherWorkflowName}) w.RegisterActivityWithOptions(launcherActivity, activity.RegisterOptions{Name: LauncherLaunchActivityName}) w.RegisterActivityWithOptions(verifyResultActivity, activity.RegisterOptions{Name: LauncherVerifyActivityName}) } func launcherWorkflow(ctx workflow.Context, config lib.BasicTestConfig) (string, error) { logger := workflow.GetLogger(ctx).With(zap.String("Test", TestName)) if config.MaxLauncherActivityRetryCount == 0 { config.MaxLauncherActivityRetryCount = defaultMaxLauncherActivityRetryCount } if config.ContextTimeoutInSeconds == 0 { config.ContextTimeoutInSeconds = int(common.DefaultContextTimeout / time.Second) } if config.ExecutionStartToCloseTimeoutInSeconds == 0 { config.ExecutionStartToCloseTimeoutInSeconds = int(defaultStressWorkflowStartToCloseTimeout / time.Second) } if config.WaitTimeBufferInSeconds == 0 { config.WaitTimeBufferInSeconds = int(defaultStressWorkflowWaitTimeBuffer / time.Second) } workflowPerActivity := config.TotalLaunchCount / config.RoutineCount workflowTimeout := workflow.GetInfo(ctx).ExecutionStartToCloseTimeoutSeconds ao := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: time.Duration(workflowTimeout) * time.Second, HeartbeatTimeout: 20 * time.Second, RetryPolicy: &workflow.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 1, MaximumInterval: time.Second, ExpirationInterval: time.Second * time.Duration(workflowTimeout+1) * time.Second * time.Duration(config.MaxLauncherActivityRetryCount), MaximumAttempts: int32(config.MaxLauncherActivityRetryCount), }, } ctx = workflow.WithActivityOptions(ctx, ao) startTime := workflow.Now(ctx) futures := make([]workflow.Future, 0, config.RoutineCount) for i := 0; i < config.RoutineCount; i++ { actInput := launcherActivityParams{ RoutineID: i, Count: workflowPerActivity, Config: config, } f := workflow.ExecuteActivity( ctx, LauncherLaunchActivityName, actInput, ) futures = append(futures, f) } for _, f := range futures { err := f.Get(ctx, nil) if err != nil { return "", err } } workflowWaitTime := time.Duration(config.ExecutionStartToCloseTimeoutInSeconds+config.WaitTimeBufferInSeconds) * time.Second logger.Info(fmt.Sprintf("%v stressWorkflows are launched, now waiting for %v ...", config.TotalLaunchCount, workflowWaitTime)) if err := workflow.Sleep(ctx, workflowWaitTime); err != nil { return "", fmt.Errorf("launcher workflow sleep failed: %v", err) } maxTolerantFailure := int32(float64(config.TotalLaunchCount) * config.FailureThreshold) actInput := verifyActivityParams{ WorkflowStartTime: startTime.UnixNano(), ListWorkflowPageSize: maxTolerantFailure + 10, UseBasicVisibilityValidation: config.UseBasicVisibilityValidation, } actOptions := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2, MaximumAttempts: 5, }, } var result verifyActivityResult ctx = workflow.WithActivityOptions(ctx, actOptions) if err := workflow.ExecuteActivity( ctx, LauncherVerifyActivityName, actInput, ).Get(ctx, &result); err != nil { return "", err } // Passing criteria: // 1. No open workflows(this means server may lose some tasks and not able to close the stressWorkflows) // 2. Failed workflows <= threshold passed := (result.TimeoutCount+result.FailedCount) <= int(maxTolerantFailure) && result.OpenCount == 0 detailResult := fmt.Sprintf("Details report: timeoutCount: %v, failedCount: %v, openCount:%v, launchCount: %v, maxThreshold:%v", result.TimeoutCount, result.FailedCount, result.OpenCount, config.TotalLaunchCount, maxTolerantFailure) if passed { return fmt.Sprintf("TEST PASSED. %v", detailResult), nil } return "", fmt.Errorf("TEST FAILED. %v", detailResult) } func launcherActivity(ctx context.Context, params launcherActivityParams) error { info := activity.GetInfo(ctx) logger := activity.GetLogger(ctx).With(zap.String("Test", TestName)) var lastStartedID int if activity.HasHeartbeatDetails(ctx) { err := activity.GetHeartbeatDetails(ctx, &lastStartedID) if err != nil { logger.Error("failed to resume from last checkpoint...start from beginning...") } logger.Info("resume from last checkpoint", zap.Int("checkpoint", lastStartedID)) } logger.Info("Start activity routineID", zap.Int("RoutineID", params.RoutineID)) cc := ctx.Value(lib.CtxKeyCadenceClient).(lib.CadenceClient) rc := ctx.Value(lib.CtxKeyRuntimeContext).(*lib.RuntimeContext) numTaskList := rc.Bench.NumTaskLists basicTestConfig := params.Config stressWorkflowTimeout := time.Duration(basicTestConfig.ExecutionStartToCloseTimeoutInSeconds) * time.Second workflowOptions := client.StartWorkflowOptions{ ExecutionStartToCloseTimeout: stressWorkflowTimeout, } var sleepTime time.Duration if basicTestConfig.MinCadenceSleepInSeconds > 0 && basicTestConfig.MaxCadenceSleepInSeconds > 0 { minSleep := time.Duration(basicTestConfig.MinCadenceSleepInSeconds) * time.Second maxSleep := time.Duration(basicTestConfig.MaxCadenceSleepInSeconds) * time.Second jitter := rand.Float64() * float64(maxSleep-minSleep) sleepTime = minSleep + time.Duration(int64(jitter)) } stressWorkflowInput := WorkflowParams{ ChainSequence: basicTestConfig.ChainSequence, ConcurrentCount: basicTestConfig.ConcurrentCount, PayloadSizeBytes: basicTestConfig.PayloadSizeBytes, PanicWorkflow: basicTestConfig.PanicStressWorkflow, CadenceSleep: sleepTime, } for startedID := lastStartedID; startedID < params.Count; startedID++ { stressWorkflowInput.TaskListNumber = rand.Intn(numTaskList) workflowOptions.ID = fmt.Sprintf("%v-%d-%d", info.WorkflowExecution.ID, params.RoutineID, startedID) workflowOptions.TaskList = common.GetTaskListName(stressWorkflowInput.TaskListNumber) startWorkflowContext, cancelF := context.WithTimeout(context.Background(), time.Duration(basicTestConfig.ContextTimeoutInSeconds)*time.Second) we, err := cc.StartWorkflow(startWorkflowContext, workflowOptions, stressWorkflowName, stressWorkflowInput) cancelF() if err == nil { logger.Debug("Created Workflow successfully", zap.String("WorkflowID", we.ID), zap.String("RunID", we.RunID)) } else { if cadence.IsWorkflowExecutionAlreadyStartedError(err) { logger.Debug("Workflow already started in previous activity attempt") } else { logger.Error("Failed to start workflow execution", zap.Error(err)) return err } } activity.RecordHeartbeat(ctx, startedID) jitter := time.Duration(75 + rand.Intn(25)) time.Sleep(jitter * time.Millisecond) } logger.Info("finish running launcher activity", zap.Int("StartedCount", params.Count)) return nil } func verifyResultActivity( ctx context.Context, params verifyActivityParams, ) (verifyActivityResult, error) { cc := ctx.Value(lib.CtxKeyCadenceClient).(lib.CadenceClient) info := activity.GetInfo(ctx) var opens, timeouts, faileds int var err error if params.UseBasicVisibilityValidation { opens, timeouts, faileds, err = verifyByBasicVisibility(ctx, params, cc, info) } else { opens, timeouts, faileds, err = verifyByAdvancedVisibility(ctx, params, cc, info) } return verifyActivityResult{ OpenCount: opens, FailedCount: faileds, TimeoutCount: timeouts, }, err } func verifyByAdvancedVisibility(ctx context.Context, params verifyActivityParams, cc lib.CadenceClient, info activity.Info) (opens, timeouts, faileds int, err error) { openWorkflowQuery := "WorkflowType='%v' and StartTime > %v and CloseTime = missing" timeoutWorkflowQuery := "WorkflowType='%v' and CloseStatus=5 and StartTime > %v and CloseTime < %v" failedWorkflowQuery := "WorkflowType='%v' and CloseStatus=1 and StartTime > %v and CloseTime < %v" query := fmt.Sprintf( openWorkflowQuery, stressWorkflowName, params.WorkflowStartTime) request := &shared.CountWorkflowExecutionsRequest{ Domain: c.StringPtr(info.WorkflowDomain), Query: &query, } resp, err := cc.CountWorkflow(ctx, request) if err != nil { return } opens = int(resp.GetCount()) query = fmt.Sprintf( timeoutWorkflowQuery, stressWorkflowName, params.WorkflowStartTime, time.Now().UnixNano()) request = &shared.CountWorkflowExecutionsRequest{ Domain: c.StringPtr(info.WorkflowDomain), Query: &query, } resp, err = cc.CountWorkflow(ctx, request) if err != nil { return } timeouts = int(resp.GetCount()) query = fmt.Sprintf( failedWorkflowQuery, stressWorkflowName, params.WorkflowStartTime, time.Now().UnixNano()) request = &shared.CountWorkflowExecutionsRequest{ Domain: c.StringPtr(info.WorkflowDomain), Query: &query, } resp, err = cc.CountWorkflow(ctx, request) if err != nil { return } faileds = int(resp.GetCount()) return } func verifyByBasicVisibility(ctx context.Context, params verifyActivityParams, cc lib.CadenceClient, info activity.Info) (opens, timeouts, faileds int, err error) { // verify if any open workflow listOpenWorkflowRequest := &shared.ListOpenWorkflowExecutionsRequest{ Domain: c.StringPtr(info.WorkflowDomain), MaximumPageSize: ¶ms.ListWorkflowPageSize, StartTimeFilter: &shared.StartTimeFilter{ EarliestTime: c.Int64Ptr(params.WorkflowStartTime), LatestTime: c.Int64Ptr(time.Now().UnixNano()), }, TypeFilter: &shared.WorkflowTypeFilter{ Name: c.StringPtr(stressWorkflowName), }, } openWfs, err := cc.ListOpenWorkflow(ctx, listOpenWorkflowRequest) if err != nil { return } if len(openWfs.Executions) > 0 { opens = len(openWfs.Executions) } // verify if any failed workflow closeStatus := shared.WorkflowExecutionCloseStatusFailed listWorkflowRequest := &shared.ListClosedWorkflowExecutionsRequest{ Domain: c.StringPtr(info.WorkflowDomain), MaximumPageSize: ¶ms.ListWorkflowPageSize, StartTimeFilter: &shared.StartTimeFilter{ EarliestTime: c.Int64Ptr(params.WorkflowStartTime), LatestTime: c.Int64Ptr(time.Now().UnixNano()), }, StatusFilter: &closeStatus, } wfs, err := cc.ListClosedWorkflow(ctx, listWorkflowRequest) if err != nil { return } if len(wfs.Executions) > 0 { faileds = len(wfs.Executions) } // verify if any timeouted workflow closeStatus = shared.WorkflowExecutionCloseStatusTimedOut listWorkflowRequest.StatusFilter = &closeStatus wfs, err = cc.ListClosedWorkflow(ctx, listWorkflowRequest) if err != nil { return } if len(wfs.Executions) > 0 { timeouts = len(wfs.Executions) } return } ================================================ FILE: bench/load/basic/stressWorkflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package basic import ( "fmt" "time" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/bench/load/common" ) const ( stressWorkflowName = "basicStressWorkflow" ) type ( // WorkflowParams inputs to workflow. WorkflowParams struct { ChainSequence int ConcurrentCount int TaskListNumber int PayloadSizeBytes int CadenceSleep time.Duration PanicWorkflow bool } ) // RegisterWorker registers workflows and activities for basic load func RegisterWorker(w worker.Worker) { w.RegisterWorkflowWithOptions(stressWorkflowExecute, workflow.RegisterOptions{Name: stressWorkflowName}) } func stressWorkflowExecute(ctx workflow.Context, workflowInput WorkflowParams) error { if workflowInput.PanicWorkflow { panic("panic workflow load test.") } activityParams := common.EchoActivityParams{ Payload: make([]byte, workflowInput.PayloadSizeBytes), } ao := workflow.ActivityOptions{ TaskList: common.GetTaskListName(workflowInput.TaskListNumber), ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: time.Minute, HeartbeatTimeout: 20 * time.Second, } ctx = workflow.WithActivityOptions(ctx, ao) for i := 0; i < workflowInput.ChainSequence; i++ { selector := workflow.NewSelector(ctx) var activityErr error for j := 0; j < workflowInput.ConcurrentCount; j++ { selector.AddFuture(workflow.ExecuteActivity(ctx, common.EchoActivityName, activityParams), func(f workflow.Future) { err := f.Get(ctx, nil) if err != nil { workflow.GetLogger(ctx).Error("basic test stress workflow echo activity execution failed", zap.Error(err)) activityErr = err } }) } for i := 0; i < workflowInput.ConcurrentCount; i++ { selector.Select(ctx) // this will wait for one branch if activityErr != nil { return fmt.Errorf("echo activity execution failed: %v", activityErr) } } if workflowInput.CadenceSleep > 0 { if err := workflow.Sleep(ctx, workflowInput.CadenceSleep); err != nil { workflow.GetLogger(ctx).Error("cadence.Sleep() returned error", zap.Error(err)) return fmt.Errorf("stress workflow sleep failed: %v", err) } } } return nil } ================================================ FILE: bench/load/cancellation/workflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cancellation import ( "context" "fmt" "math/rand" "time" "github.com/pborman/uuid" "go.uber.org/cadence" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/client" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/bench/lib" "github.com/uber/cadence/bench/load/common" ) const ( // TestName is the test name for cancellation test TestName = "cancellation" // LauncherWorkflowName is the workflow name for launching cancellation load test LauncherWorkflowName = "cancellation-load-test-workflow" ) const ( sleepWorkflowName = "cancellation-sleep-workflow" waitDurationInMilliSeconds = 75 waitDurationJitterInMilliSeconds = 50 minActivityTimeout = time.Minute defaultDurationBeforeCancellation = 10 * time.Second defaultWorkflowSleepDuration = 20 * time.Second defaultDurationBeforeValidation = 30 * time.Second ) type ( launcherActivityResult struct { StartAvailability float64 CancelAvailability float64 } startWorkflowProgress struct { TotalStartWorkflowCall int SucceededStartWorkflowCall int WorkflowStarted int NextStartID int } ) // RegisterLauncher registers workflows for launching cancellation load func RegisterLauncher(w worker.Worker) { w.RegisterWorkflowWithOptions(launcherWorkflow, workflow.RegisterOptions{Name: LauncherWorkflowName}) w.RegisterActivity(launcherActivity) w.RegisterActivity(validationActivity) } // RegisterWorker registers workflows for cancellation test func RegisterWorker(w worker.Worker) { w.RegisterWorkflowWithOptions(sleepWorkflow, workflow.RegisterOptions{Name: sleepWorkflowName}) } func launcherWorkflow( ctx workflow.Context, config lib.CancellationTestConfig, ) error { workflowPerActivity := config.TotalLaunchCount / config.Concurrency activityStartToCloseTimeout := time.Duration(workflowPerActivity/(1000/waitDurationInMilliSeconds)) * time.Second * common.DefaultMaxRetryCount if activityStartToCloseTimeout < minActivityTimeout { activityStartToCloseTimeout = minActivityTimeout } launcherActivityOptions := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: activityStartToCloseTimeout, HeartbeatTimeout: 30 * time.Second, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2, MaximumAttempts: 10, }, } ctx = workflow.WithActivityOptions(ctx, launcherActivityOptions) startTime := workflow.Now(ctx) futures := make([]workflow.Future, 0, config.Concurrency) for i := 0; i < config.Concurrency; i++ { future := workflow.ExecuteActivity(ctx, launcherActivity, config) futures = append(futures, future) } avgStartAvailability := 0.0 avgCancelAvailability := 0.0 for _, future := range futures { var result launcherActivityResult if err := future.Get(ctx, &result); err != nil { return fmt.Errorf("launcherActivity failed: %v", err) } avgStartAvailability += result.StartAvailability avgCancelAvailability += result.CancelAvailability } avgStartAvailability /= float64(config.Concurrency) avgCancelAvailability /= float64(config.Concurrency) // validate availability if avgStartAvailability < common.DefaultAvailabilityThreshold { return fmt.Errorf("startWorkflow availability too low, required: %v, actual: %v", common.DefaultAvailabilityThreshold, avgStartAvailability) } if avgCancelAvailability < common.DefaultAvailabilityThreshold { return fmt.Errorf("cancelWorkflow availability too low, required: %v, actual: %v", common.DefaultAvailabilityThreshold, avgCancelAvailability) } // validate if there's stuck workflow using visibility records // give the system some time to propagate ES records and wait for workflow that failed to cancel to finish if err := workflow.Sleep(ctx, defaultDurationBeforeValidation); err != nil { return fmt.Errorf("launcher workflow sleep failed: %v", err) } validationActivityOptions := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2, MaximumAttempts: 5, NonRetriableErrorReasons: []string{common.ErrReasonValidationFailed}, }, } ctx = workflow.WithActivityOptions(ctx, validationActivityOptions) // move startTime backward by 10 secs to account for the time drift between worker and cadence hosts if any return workflow.ExecuteActivity(ctx, validationActivity, config, startTime.Add(-time.Second*10).UnixNano()).Get(ctx, nil) } func launcherActivity( ctx context.Context, config lib.CancellationTestConfig, ) (launcherActivityResult, error) { if config.ContextTimeoutInSeconds == 0 { config.ContextTimeoutInSeconds = int(common.DefaultContextTimeout.Seconds()) } startRPS := 1000 / waitDurationInMilliSeconds workflowIDChan := make(chan string, startRPS*int(defaultDurationBeforeCancellation.Seconds())) var startAvailability float64 go func() { startAvailability = startWorkflow(ctx, &config, workflowIDChan) }() time.Sleep(defaultDurationBeforeCancellation) cancelAvailability := cancelWorkflow(ctx, &config, workflowIDChan) return launcherActivityResult{ StartAvailability: startAvailability, CancelAvailability: cancelAvailability, }, nil } func startWorkflow( ctx context.Context, config *lib.CancellationTestConfig, workflowIDChan chan string, ) float64 { defer close(workflowIDChan) logger := activity.GetLogger(ctx) var progress startWorkflowProgress if activity.HasHeartbeatDetails(ctx) { if err := activity.GetHeartbeatDetails(ctx, &progress); err != nil { // explicitly reset value to 0 if failed to get details, in case the implementation changed the value logger.Error("Failed to get activity heartbeat details", zap.Error(err)) progress = startWorkflowProgress{ TotalStartWorkflowCall: 0, SucceededStartWorkflowCall: 0, WorkflowStarted: 0, NextStartID: 0, } } } cc := ctx.Value(lib.CtxKeyCadenceClient).(lib.CadenceClient) rc := ctx.Value(lib.CtxKeyRuntimeContext).(*lib.RuntimeContext) numTaskList := rc.Bench.NumTaskLists for i := progress.NextStartID; i < config.TotalLaunchCount/config.Concurrency; i++ { select { case <-ctx.Done(): // unable to start specified # of workflows in time. // Workflow will receive timeout error, the value we return here is irrelevant. logger.Error("Failed to start all workflows in time", zap.Any("progress", progress)) return 0 default: } startWorkflowOptions := client.StartWorkflowOptions{ ExecutionStartToCloseTimeout: 2 * defaultWorkflowSleepDuration, ID: fmt.Sprintf("%s-%s", TestName, uuid.New()), TaskList: common.GetTaskListName(rand.Intn(numTaskList)), } if err := common.RetryOp(func() error { progress.TotalStartWorkflowCall++ startCtx, cancel := context.WithTimeout(ctx, time.Duration(config.ContextTimeoutInSeconds)*time.Second) we, err := cc.StartWorkflow(startCtx, startWorkflowOptions, sleepWorkflowName, defaultWorkflowSleepDuration) cancel() if err == nil || cadence.IsWorkflowExecutionAlreadyStartedError(err) { workflowIDChan <- we.ID progress.SucceededStartWorkflowCall++ return nil } if common.IsServiceBusyError(err) { // do not count service busy as failure progress.SucceededStartWorkflowCall++ } logger.Error("Failed to start workflow execution", zap.Error(err)) return err }, nil); err == nil { progress.WorkflowStarted++ } // successfully started the workflow or gave up after several retries progress.NextStartID++ activity.RecordHeartbeat(ctx, progress) time.Sleep(time.Duration(waitDurationInMilliSeconds+rand.Intn(waitDurationJitterInMilliSeconds)) * time.Millisecond) } availability := float64(progress.SucceededStartWorkflowCall) / float64(progress.TotalStartWorkflowCall) logger.Info("Completed start workflow", zap.Float64("availability", availability), zap.Int("workflow-started", progress.WorkflowStarted)) return availability } func cancelWorkflow( ctx context.Context, config *lib.CancellationTestConfig, workflowIDChan chan string, ) float64 { logger := activity.GetLogger(ctx) totalCancelWorkflowCall := 0 succeededCancelWorkflowCall := 0 workflowCancelled := 0 cc := ctx.Value(lib.CtxKeyCadenceClient).(lib.CadenceClient) for { select { case <-ctx.Done(): // unable to start specified # of workflows in time. // Workflow will receive timeout error, the value we return here is irrelevant. logger.Error("Failed to cancel all workflows in time") return 0 case workflowID := <-workflowIDChan: if workflowID == "" { // channel has closed, all workflow cancelled availability := float64(succeededCancelWorkflowCall) / float64(totalCancelWorkflowCall) logger.Info("Completed cancel workflow", zap.Float64("availability", availability), zap.Int("workflow-cancelled", workflowCancelled)) return availability } if err := common.RetryOp(func() error { totalCancelWorkflowCall++ cancelCtx, cancel := context.WithTimeout(ctx, time.Duration(config.ContextTimeoutInSeconds)*time.Second) err := cc.CancelWorkflow(cancelCtx, workflowID, "") cancel() if err == nil || common.IsCancellationAlreadyRequestedError(err) || common.IsEntityNotExistsError(err) { succeededCancelWorkflowCall++ return nil } if common.IsServiceBusyError(err) { // do not count service busy as failure succeededCancelWorkflowCall++ } logger.Error("Failed to cancel workflow execution", zap.Error(err)) return err }, nil); err == nil { workflowCancelled++ } time.Sleep(time.Duration(waitDurationInMilliSeconds+rand.Intn(waitDurationJitterInMilliSeconds)) * time.Millisecond) } } } func validationActivity( ctx context.Context, config *lib.CancellationTestConfig, testStartTimeNanos int64, ) error { cc := ctx.Value(lib.CtxKeyCadenceClient).(lib.CadenceClient) domain := activity.GetInfo(ctx).WorkflowDomain query := fmt.Sprintf("WorkflowType = '%s' and StartTime > %v", sleepWorkflowName, testStartTimeNanos) request := &shared.CountWorkflowExecutionsRequest{ Domain: &domain, Query: &query, } // 1. check if enough workflows are started resp, err := cc.CountWorkflow(ctx, request) if err != nil { return err } totalLaunchCount := resp.GetCount() if totalLaunchCount < int64(config.TotalLaunchCount) { return cadence.NewCustomError(common.ErrReasonValidationFailed, fmt.Sprintf("Expected to start %v workflow, actual started: %v", config.TotalLaunchCount, totalLaunchCount)) } // 2. check if all workflows are closed query = fmt.Sprintf("WorkflowType = '%s' and StartTime > %v and CloseTime != missing", sleepWorkflowName, testStartTimeNanos) request.Query = &query resp, err = cc.CountWorkflow(ctx, request) if err != nil { return err } if resp.GetCount() != totalLaunchCount { return cadence.NewCustomError(common.ErrReasonValidationFailed, fmt.Sprintf("Not all workflows are closed, started: %v, closed: %v", totalLaunchCount, resp.GetCount())) } // TODO: uncomment the following check after cancellation test is rewritten. // currently if the launcherActivity failed in the middle (e.g. heartbeat timeout), // all the workflowID in memory will be lost and those workflows can't be cancelled. // 3. check if enough workflows are cancelled // query = fmt.Sprintf("WorkflowType = '%s' and StartTime > %v and CloseStatus = 2", sleepWorkflowName, startTimeNanos) // request.Query = &query // resp, err = cc.CountWorkflow(ctx, request) // if err != nil { // return err // } // if resp.GetCount() < int64(float64(totalLaunchCount)*common.DefaultAvailabilityThreshold) { // return cadence.NewCustomError(common.ErrReasonValidationFailed, fmt.Sprintf("Cancelled workflow count too low, started: %v, cancelled: %v", totalLaunchCount, resp.GetCount())) // } return nil } func sleepWorkflow( ctx workflow.Context, sleepDuration time.Duration, ) error { return workflow.Sleep(ctx, sleepDuration) } ================================================ FILE: bench/load/common/activities.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package common import ( "context" "go.uber.org/cadence/activity" "go.uber.org/cadence/worker" ) type ( // EchoActivityParams is the paramer for echoActivity EchoActivityParams struct { Payload []byte } ) const ( // EchoActivityName is the name of echoActivity EchoActivityName = "echoActivity" ) // RegisterWorker registers common activities func RegisterWorker(w worker.Worker) { w.RegisterActivityWithOptions(echoActivity, activity.RegisterOptions{Name: EchoActivityName}) } func echoActivity(ctx context.Context, activityParams EchoActivityParams) ([]byte, error) { return activityParams.Payload, nil } ================================================ FILE: bench/load/common/constants.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package common import "time" const ( // DefaultAvailabilityThreshold is the default threshold for availability DefaultAvailabilityThreshold = 0.99 // DefaultContextTimeout is the default context timeout for RPC calls DefaultContextTimeout = 3 * time.Second ) const ( // DefaultMaxRetryCount is the default max retry count DefaultMaxRetryCount = 5 // DefaultRetryBackoffDuration is the default backoff duration for retry DefaultRetryBackoffDuration = 50 * time.Millisecond ) const ( // ErrReasonValidationFailed is the failure reason for validation failure ErrReasonValidationFailed = "validation failed" // ErrReasonWorkflowNotExist is the error reason for workflow not exist ErrReasonWorkflowNotExist = "workflow not exist" ) ================================================ FILE: bench/load/common/helpers.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package common import ( "context" "fmt" "time" "go.uber.org/cadence/.gen/go/shared" "github.com/uber/cadence/bench/lib" ) const ( taskListPrefix = "cadence-bench-tl" ) // GetTaskListName returns the task list name for the given task list id func GetTaskListName(taskListNumber int) string { return fmt.Sprintf("%s-%v", taskListPrefix, taskListNumber) } // GetActivityServiceConfig returns the service config from activity context // Returns nil if the context does not contain the service config func GetActivityServiceConfig(ctx context.Context) *lib.RuntimeContext { val := ctx.Value(lib.CtxKeyRuntimeContext) if val == nil { return nil } return val.(*lib.RuntimeContext) } // IsServiceBusyError returns if the err is a ServiceBusyError func IsServiceBusyError(err error) bool { _, ok := err.(*shared.ServiceBusyError) return ok } // IsCancellationAlreadyRequestedError returns if the err is a CancellationAlreadyRequestedError func IsCancellationAlreadyRequestedError(err error) bool { _, ok := err.(*shared.CancellationAlreadyRequestedError) return ok } // IsEntityNotExistsError returns if the err is a EntityNotExistsError func IsEntityNotExistsError(err error) bool { _, ok := err.(*shared.EntityNotExistsError) return ok } // IsNonRetryableError return true if the err is considered non-retryable func IsNonRetryableError(err error) bool { if err == context.DeadlineExceeded || err == context.Canceled { return true } if IsEntityNotExistsError(err) { return true } return false } // RetryOp retries an operation based on the default retry policy func RetryOp( op func() error, isNonRetryableError func(error) bool, ) error { var err error for retryCount := 0; retryCount < DefaultMaxRetryCount; retryCount++ { if err = op(); err == nil { return nil } if isNonRetryableError != nil && isNonRetryableError(err) { return err } if retryCount < DefaultMaxRetryCount-1 { time.Sleep(DefaultRetryBackoffDuration) } } return err } ================================================ FILE: bench/load/concurrentexec/batchWorkflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package concurrentexec import ( "context" "fmt" "math/rand" "strings" "time" "go.uber.org/cadence" "go.uber.org/cadence/activity" "go.uber.org/cadence/client" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "github.com/uber/cadence/bench/lib" "github.com/uber/cadence/bench/load/common" ) const ( batchWorkflowName = "concurrent-execution-batch-workflow" ) const ( batchTypeActivity = "activity" batchTypeChildWorkflow = "childworkflow" maxSleepTimeInSeconds = 10 ) // RegisterWorker registers workflows and activities for concurrent execution test func RegisterWorker(w worker.Worker) { w.RegisterWorkflowWithOptions(batchWorkflow, workflow.RegisterOptions{Name: batchWorkflowName}) w.RegisterWorkflow(concurrentChildWorkflow) w.RegisterActivity(concurrentActivity) } func batchWorkflow( ctx workflow.Context, config lib.ConcurrentExecTestConfig, scheduledTimeNanos int64, ) (float64, error) { profile, err := lib.BeginWorkflow(ctx, batchWorkflowName, scheduledTimeNanos) if err != nil { return 0, err } now := workflow.Now(ctx).UnixNano() batchType := strings.ToLower(strings.TrimSpace(config.BatchType)) var numTaskList int if numTaskList, err = getTaskListNum(ctx); err != nil { return 0, profile.End(fmt.Errorf("failed to getTaskListNum, error: %v", err)) } futures := make([]workflow.Future, 0, config.BatchSize) for i := 0; i != config.BatchSize; i++ { switch batchType { case batchTypeActivity: futures = append(futures, scheduleActivity(ctx, numTaskList, config.BatchTimeoutInSeconds, now)) case batchTypeChildWorkflow: futures = append(futures, scheduleChildWorkflow(ctx, numTaskList, config.BatchTimeoutInSeconds, now)) default: return 0, profile.End(fmt.Errorf("unknown batch type: %v", batchType)) } } scheduledInTime := 0 maxScheduleLatency := time.Duration(config.BatchMaxLatencyInSeconds) * time.Second for _, future := range futures { var scheduleLatency time.Duration if err := future.Get(ctx, &scheduleLatency); err != nil { return 0, profile.End(fmt.Errorf("childworkflow/activity failed, error: %v", err)) } if scheduleLatency < maxScheduleLatency { scheduledInTime++ } } return float64(scheduledInTime) / float64(config.BatchSize), profile.End(nil) } func scheduleActivity( ctx workflow.Context, numTaskList int, timeoutInSeconds int, scheduledTimeNanos int64, ) workflow.Future { ao := workflow.ActivityOptions{ TaskList: common.GetTaskListName(rand.Intn(numTaskList)), ScheduleToStartTimeout: time.Duration(timeoutInSeconds) * time.Second, StartToCloseTimeout: 2 * maxSleepTimeInSeconds * time.Second, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, ExpirationInterval: time.Duration(timeoutInSeconds) * time.Second, }, } activityCtx := workflow.WithActivityOptions(ctx, ao) return workflow.ExecuteActivity(activityCtx, concurrentActivity, scheduledTimeNanos, maxSleepTimeInSeconds) } func scheduleChildWorkflow( ctx workflow.Context, numTaskList int, timeoutInSeconds int, scheduledTimeNanos int64, ) workflow.Future { cwo := workflow.ChildWorkflowOptions{ TaskList: common.GetTaskListName(rand.Intn(numTaskList)), ExecutionStartToCloseTimeout: time.Duration(timeoutInSeconds) * time.Second, TaskStartToCloseTimeout: time.Second * 10, ParentClosePolicy: client.ParentClosePolicyTerminate, } childCtx := workflow.WithChildOptions(ctx, cwo) return workflow.ExecuteChildWorkflow(childCtx, concurrentChildWorkflow, scheduledTimeNanos, maxSleepTimeInSeconds) } func concurrentActivity( ctx context.Context, scheduledTimeNanos int64, maxSleepTimeInSeconds int, ) (time.Duration, error) { var latency time.Duration if activity.GetInfo(ctx).Attempt == 0 { latency = time.Since(time.Unix(0, scheduledTimeNanos)) } time.Sleep(time.Duration(rand.Intn(maxSleepTimeInSeconds)) * time.Second) return latency, nil } func concurrentChildWorkflow( ctx workflow.Context, scheduledTimeNanos int64, maxSleepTimeInSeconds int, ) (time.Duration, error) { latency := workflow.Now(ctx).Sub(time.Unix(0, scheduledTimeNanos)) var sleepTimeInSeconds int workflow.SideEffect(ctx, func(_ workflow.Context) interface{} { return rand.Intn(maxSleepTimeInSeconds) }).Get(&sleepTimeInSeconds) if err := workflow.Sleep(ctx, time.Duration(sleepTimeInSeconds)*time.Second); err != nil { return 0, err } return latency, nil } func getTaskListNum(ctx workflow.Context) (int, error) { var numTaskList int lao := workflow.LocalActivityOptions{ ScheduleToCloseTimeout: 5 * time.Second, } laCtx := workflow.WithLocalActivityOptions(ctx, lao) if err := workflow.ExecuteLocalActivity(laCtx, func(ctx context.Context) (int, error) { return ctx.Value(lib.CtxKeyRuntimeContext).(*lib.RuntimeContext).Bench.NumTaskLists, nil }).Get(laCtx, &numTaskList); err != nil { return 0, err } return numTaskList, nil } ================================================ FILE: bench/load/concurrentexec/launchWorkflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package concurrentexec import ( "fmt" "math/rand" "strconv" "time" "go.uber.org/cadence/client" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "github.com/uber/cadence/bench/lib" "github.com/uber/cadence/bench/load/common" ) const ( // TestName is the test name for concurrent execution bench test TestName = "concurrent-execution" // LauncherWorkflowName is the workflow name for launching concurrent execution bench test LauncherWorkflowName = "concurrent-execution-test-workflow" ) type ( batchResult struct { score float64 errString string } ) // RegisterLauncher registers workflows for launching concurrent execution load func RegisterLauncher(w worker.Worker) { w.RegisterWorkflowWithOptions(concurrentExecTestWorkflow, workflow.RegisterOptions{Name: LauncherWorkflowName}) } func concurrentExecTestWorkflow( ctx workflow.Context, config lib.ConcurrentExecTestConfig, ) (float64, error) { batchCompletionCh := workflow.NewChannel(ctx) workflow.Go(ctx, func(ctx workflow.Context) { startBatchWorkflow(ctx, config, batchCompletionCh) }) totalScore := 0.0 for i := 0; i != config.TotalBatches; i++ { var result batchResult batchCompletionCh.Receive(ctx, &result) if len(result.errString) != 0 { return 0, fmt.Errorf("batch workflow execution failed: %v", result.errString) } totalScore += result.score } avgScore := totalScore / float64(config.TotalBatches) if avgScore < common.DefaultAvailabilityThreshold { return 0, fmt.Errorf("batch workflow score too low, expected: %v, actual: %v", common.DefaultAvailabilityThreshold, avgScore) } return avgScore, nil } func startBatchWorkflow( ctx workflow.Context, config lib.ConcurrentExecTestConfig, batchCompletionCh workflow.Channel, ) { var numTaskList int var err error if numTaskList, err = getTaskListNum(ctx); err != nil { batchCompletionCh.Send(ctx, batchResult{ errString: "Failed to getTaskListNum, error: " + err.Error(), }) return } numConcurrentBatches := config.TotalBatches / config.Concurrency for i := 0; i != numConcurrentBatches; i++ { workflow.Go(ctx, func(ctx workflow.Context) { startConcurrentBatches(ctx, config, batchCompletionCh, i*config.Concurrency, numTaskList) }) if i != numConcurrentBatches-1 { timer := workflow.NewTimer(ctx, time.Duration(config.BatchPeriodInSeconds)*time.Second) if err := timer.Get(ctx, nil); err != nil { batchCompletionCh.Send(ctx, batchResult{ errString: "Failed to start batch workflow, error: " + err.Error(), }) return } } } } func startConcurrentBatches( ctx workflow.Context, config lib.ConcurrentExecTestConfig, batchCompletionCh workflow.Channel, batchStartIdx int, numTaskList int, ) { parentWorkflowID := workflow.GetInfo(ctx).WorkflowExecution.ID now := workflow.Now(ctx).UnixNano() childFutures := make([]workflow.Future, 0, config.Concurrency) for batchIdx := batchStartIdx; batchIdx != batchStartIdx+config.Concurrency; batchIdx++ { cwo := workflow.ChildWorkflowOptions{ WorkflowID: parentWorkflowID + "-batch-" + strconv.Itoa(batchIdx), TaskList: common.GetTaskListName(rand.Intn(numTaskList)), ExecutionStartToCloseTimeout: time.Duration(config.BatchTimeoutInSeconds) * time.Second, TaskStartToCloseTimeout: time.Minute, ParentClosePolicy: client.ParentClosePolicyTerminate, WorkflowIDReusePolicy: client.WorkflowIDReusePolicyAllowDuplicate, } childCtx := workflow.WithChildOptions(ctx, cwo) childFuture := workflow.ExecuteChildWorkflow(childCtx, batchWorkflowName, config, now) childFutures = append(childFutures, childFuture) } for _, childFuture := range childFutures { var batchScore float64 if err := childFuture.Get(ctx, &batchScore); err != nil { batchCompletionCh.Send(ctx, batchResult{ errString: "Batch workflow failed, error: " + err.Error(), }) } else { batchCompletionCh.Send(ctx, batchResult{ score: batchScore, }) } } } ================================================ FILE: bench/load/cron/workflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cron import ( "context" "fmt" "math/rand" "strconv" "time" "go.uber.org/cadence" "go.uber.org/cadence/activity" "go.uber.org/cadence/client" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/bench/lib" "github.com/uber/cadence/bench/load/basic" "github.com/uber/cadence/bench/load/cancellation" "github.com/uber/cadence/bench/load/common" "github.com/uber/cadence/bench/load/concurrentexec" "github.com/uber/cadence/bench/load/signal" "github.com/uber/cadence/bench/load/timer" ) const ( // TestName is the test name for cron bench test TestName = "cron" ) const ( cronWorkflowName = "cron-test-workflow" cronLauncherWorkflowName = "cron-launcher-workflow" queryTypeTestResults = "test-results" testResultSignalName = "test-result" testPassedBoolSearchAttribute = "Passed" ) type ( workflowExecution struct { WorkflowID string RunID string } testResult struct { Name string Description string TestStatus string Details string } ) const ( testStatusPassed = "Passed" testStatusFailed = "Failed" ) // RegisterLauncher registers workflows for cron load launching func RegisterLauncher(w worker.Worker) { w.RegisterWorkflowWithOptions(cronWorkflow, workflow.RegisterOptions{Name: cronWorkflowName}) w.RegisterWorkflowWithOptions(launcherWorkflow, workflow.RegisterOptions{Name: cronLauncherWorkflowName}) w.RegisterActivity(launcherActivity) w.RegisterActivity(signalResultActivity) } func cronWorkflow( ctx workflow.Context, config lib.CronTestConfig, ) error { now := workflow.Now(ctx).UnixNano() profile, err := lib.BeginWorkflow(ctx, cronWorkflowName, now) if err != nil { return err } workflowTimeoutInSeconds := workflow.GetInfo(ctx).ExecutionStartToCloseTimeoutSeconds ao := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2, ExpirationInterval: time.Duration(workflowTimeoutInSeconds) * time.Second, // retry the activity until workflow timeout }, } ctx = workflow.WithActivityOptions(ctx, ao) totalTests := 0 futures := make([]workflow.Future, 0, len(config.TestSuites)) for _, testSuite := range config.TestSuites { totalTests += len(testSuite.Configs) testSuiteName := testSuite.Name future := workflow.ExecuteActivity(ctx, launcherActivity, testSuite.Domain, testSuiteName, testSuite.Configs, workflowTimeoutInSeconds) futures = append(futures, future) } for _, future := range futures { if err := future.Get(ctx, nil); err != nil { return profile.End(err) } } testResults := make([]testResult, 0, totalTests) _ = workflow.SetQueryHandler(ctx, queryTypeTestResults, func() ([]testResult, error) { return testResults, nil }) testPassed := true testResultCh := workflow.GetSignalChannel(ctx, testResultSignalName) for len(testResults) != totalTests { var result testResult testResultCh.Receive(ctx, &result) testResults = append(testResults, result) if result.TestStatus == testStatusFailed { testPassed = false } } err = workflow.UpsertSearchAttributes(ctx, map[string]interface{}{testPassedBoolSearchAttribute: testPassed}) return profile.End(err) } func launcherActivity( ctx context.Context, domain string, testSuiteName string, testConfigs []lib.AggregatedTestConfig, timeoutInSeconds int32, ) error { logger := activity.GetLogger(ctx) rc := ctx.Value(lib.CtxKeyRuntimeContext).(*lib.RuntimeContext) cc, err := lib.NewCadenceClientForDomain(rc, domain) if err != nil { logger.Error("Failed to create cadence client", zap.String("domain", domain)) return err } workflowOptions := client.StartWorkflowOptions{ ID: "cron-launcher-" + testSuiteName, TaskList: common.GetTaskListName(0), // default to use the tasklist with ID 0 ExecutionStartToCloseTimeout: time.Duration(timeoutInSeconds) * time.Second, DecisionTaskStartToCloseTimeout: time.Minute, WorkflowIDReusePolicy: client.WorkflowIDReusePolicyAllowDuplicate, } activityInfo := activity.GetInfo(ctx) _ = common.RetryOp(func() error { _, err = cc.StartWorkflow( ctx, workflowOptions, cronLauncherWorkflowName, activityInfo.WorkflowDomain, workflowExecution{ WorkflowID: activityInfo.WorkflowExecution.ID, RunID: activityInfo.WorkflowExecution.RunID, }, testSuiteName, testConfigs, time.Now().UnixNano(), ) if err == nil || cadence.IsWorkflowExecutionAlreadyStartedError(err) { // TODO: it's possible that the launcher workflow is started by another cron logger.Info("Started test suite", zap.String("test-suite", testSuiteName)) return nil } logger.Error("Failed to start test suite", zap.String("test-suite", testSuiteName)) return err }, nil) return err } func launcherWorkflow( ctx workflow.Context, parentDomain string, parentExecution workflowExecution, testSuiteName string, testConfigs []lib.AggregatedTestConfig, scheduledTimeNanos int64, ) ([]testResult, error) { profile, err := lib.BeginWorkflow(ctx, cronLauncherWorkflowName, scheduledTimeNanos) if err != nil { return nil, err } testTimeout := workflow.GetInfo(ctx).ExecutionStartToCloseTimeoutSeconds ao := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2, ExpirationInterval: time.Duration(testTimeout) * time.Second, // retry the signal activity until workflow timeout NonRetriableErrorReasons: []string{common.ErrReasonWorkflowNotExist}, }, } ctx = workflow.WithActivityOptions(ctx, ao) numTests := len(testConfigs) testResults := make([]testResult, 0, numTests) // run all tests in random order var testOrder []int err = workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} { return rand.Perm(len(testConfigs)) }).Get(&testOrder) if err != nil { for idx := range testConfigs { result := testResult{ Name: testConfigs[idx].Name, TestStatus: testStatusFailed, Details: fmt.Sprintf("Failed to generate randomized test order: %v", err.Error()), } testResults = append(testResults, result) if err := workflow.ExecuteActivity(ctx, signalResultActivity, parentDomain, parentExecution, result).Get(ctx, nil); err != nil { workflow.GetLogger(ctx).Error("Failed to signal test result", zap.Error(err)) return nil, profile.End(err) } } return testResults, nil } for _, idx := range testOrder { testConfig := testConfigs[idx] cwo := workflow.ChildWorkflowOptions{ WorkflowID: testSuiteName + "-" + testConfig.Name + "-" + strconv.Itoa(idx), TaskList: common.GetTaskListName(0), // default to use the tasklist with ID 0 ExecutionStartToCloseTimeout: time.Duration(testConfig.TimeoutInSeconds) * time.Second, TaskStartToCloseTimeout: time.Minute, ParentClosePolicy: client.ParentClosePolicyTerminate, WorkflowIDReusePolicy: client.WorkflowIDReusePolicyAllowDuplicate, } childCtx := workflow.WithChildOptions(ctx, cwo) var childFuture workflow.Future switch testConfig.Name { case basic.TestName: childFuture = workflow.ExecuteChildWorkflow(childCtx, basic.LauncherWorkflowName, *testConfig.Basic) case signal.TestName: childFuture = workflow.ExecuteChildWorkflow(childCtx, signal.LauncherWorkflowName, *testConfig.Signal) case timer.TestName: childFuture = workflow.ExecuteChildWorkflow(childCtx, timer.LauncherWorkflowName, *testConfig.Timer) case concurrentexec.TestName: childFuture = workflow.ExecuteChildWorkflow(childCtx, concurrentexec.LauncherWorkflowName, *testConfig.ConcurrentExec) case cancellation.TestName: childFuture = workflow.ExecuteChildWorkflow(childCtx, cancellation.LauncherWorkflowName, *testConfig.Cancellation) default: workflow.GetLogger(ctx).Error("Unknown test name", zap.String("test-name", testConfig.Name)) } result := testResult{ Name: testSuiteName + "::" + testConfig.Name, Description: testConfig.Description, } if childFuture == nil { result.TestStatus = testStatusFailed result.Details = "Unknown test" } else if err := childFuture.Get(childCtx, nil); err != nil { result.TestStatus = testStatusFailed result.Details = err.Error() if customErr, ok := err.(*cadence.CustomError); ok { var detailStr string if err := customErr.Details(&detailStr); err == nil { result.Details += ": " + detailStr } } } else { result.TestStatus = testStatusPassed } testResults = append(testResults, result) if err := workflow.ExecuteActivity(ctx, signalResultActivity, parentDomain, parentExecution, result).Get(ctx, nil); err != nil { workflow.GetLogger(ctx).Error("Failed to signal test result", zap.String("test-name", testConfig.Name), zap.Error(err)) return nil, profile.End(err) } } return testResults, profile.End(nil) } func signalResultActivity( ctx context.Context, targetDomain string, targetWorkflowExecution workflowExecution, result testResult, ) error { logger := activity.GetLogger(ctx) rc := ctx.Value(lib.CtxKeyRuntimeContext).(*lib.RuntimeContext) cc, err := lib.NewCadenceClientForDomain(rc, targetDomain) if err != nil { logger.Error("Failed to create cadence client", zap.String("domain", targetDomain)) return err } if err = common.RetryOp(func() error { return cc.SignalWorkflow( ctx, targetWorkflowExecution.WorkflowID, targetWorkflowExecution.RunID, testResultSignalName, result, ) }, common.IsNonRetryableError); err != nil { logger.Error("Failed to signal test result back to workflow", zap.String("domain", targetDomain), zap.String("workflowID", targetWorkflowExecution.WorkflowID), zap.String("workflowID", targetWorkflowExecution.RunID), zap.Error(err), ) return cadence.NewCustomError(common.ErrReasonWorkflowNotExist, err.Error()) } return nil } ================================================ FILE: bench/load/signal/workflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package signal import ( "context" "errors" "fmt" "math/rand" "strconv" "time" "go.uber.org/cadence" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/client" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/zap" "golang.org/x/time/rate" "github.com/uber/cadence/bench/lib" "github.com/uber/cadence/bench/load/common" c "github.com/uber/cadence/common" ) const ( // TestName is the test name TestName = "signal" // LauncherWorkflowName is the workflow name for launching load test LauncherWorkflowName = "signal-load-test-workflow" loadTestActivityName = "loadTestActivity" verifyResultActivityName = "verifyResultActivity" processSignalWorkflowName = "processSignalWorkflow" stressTestSignalName = "stressSignalName" processSignalActivityName = "processSignalActivity" exitSignalNumber = -1 failedSignalWorkflowQuery = "WorkflowType='%v' and CloseStatus=1 and StartTime > %v and CloseTime < %v" ) var ( maxPageSize = int32(10) ) // RegisterLauncher registers workflows and activities for load launching func RegisterLauncher(w worker.Worker) { w.RegisterWorkflowWithOptions(loadTestWorkflow, workflow.RegisterOptions{Name: LauncherWorkflowName}) w.RegisterActivityWithOptions(loadTestActivity, activity.RegisterOptions{Name: loadTestActivityName}) w.RegisterActivityWithOptions(verifyResultActivity, activity.RegisterOptions{Name: verifyResultActivityName}) } // RegisterWorker registers workflows and activities for sync api load func RegisterWorker(w worker.Worker) { w.RegisterWorkflowWithOptions(processSignalWorkflow, workflow.RegisterOptions{Name: processSignalWorkflowName}) w.RegisterActivityWithOptions(processSignalActivity, activity.RegisterOptions{Name: processSignalActivityName}) } type ( loadTestActivityParams struct { WorkflowExecutionTimeoutInSeconds int DecisionTaskTimeoutInSeconds int CampaignCount int ActionRate float64 FailureRate float64 StartingWorkflowID int BatchWorkflowCount int SignalCount int SignalDataSize int RateLimit int SignalCountBeforeContinueAsNew int EnableRollingWindow bool MaxSignalDelayInSeconds int MaxSignalDelayCount int } signalActivityResult struct { SucceedCount int FailureCount int } signalEvent struct { SignalNumber int Data []byte Timestamp int64 } verifyActivityParams struct { FailedWorkflowCount int64 WorkflowStartTime int64 } ) // loadTestWorkflow sends signals to the stress workflow func loadTestWorkflow(ctx workflow.Context, params lib.SignalTestConfig) error { info := workflow.GetInfo(ctx) startTime := workflow.Now(ctx) expiration := time.Duration(info.ExecutionStartToCloseTimeoutSeconds) * time.Second profile, err := lib.BeginWorkflow(ctx, LauncherWorkflowName, startTime.UnixNano()) if err != nil { return err } retryPolicy := &cadence.RetryPolicy{ InitialInterval: time.Second * 5, BackoffCoefficient: 1, // always backoff 5s MaximumInterval: time.Second * 5, ExpirationInterval: expiration, MaximumAttempts: 2000, NonRetriableErrorReasons: []string{common.ErrReasonValidationFailed}, } ao := workflow.ActivityOptions{ ScheduleToStartTimeout: expiration, StartToCloseTimeout: expiration, HeartbeatTimeout: time.Second * 30, RetryPolicy: retryPolicy, } ctx = workflow.WithActivityOptions(ctx, ao) batchSize := params.LoadTestWorkflowCount / params.LoaderCount doneCh := workflow.NewChannel(ctx) for i := 0; i < params.LoaderCount; i++ { loaderParams := loadTestActivityParams{ StartingWorkflowID: i * batchSize, BatchWorkflowCount: batchSize, WorkflowExecutionTimeoutInSeconds: params.WorkflowExecutionTimeoutInSeconds, DecisionTaskTimeoutInSeconds: params.DecisionTaskTimeoutInSeconds, CampaignCount: params.CampaignCount, ActionRate: params.ActionRate, FailureRate: params.FailureRate, RateLimit: params.RateLimit, SignalCount: params.SignalCount, SignalDataSize: params.SignalDataSize, SignalCountBeforeContinueAsNew: params.SignalBeforeContinueAsNew, EnableRollingWindow: params.EnableRollingWindow, MaxSignalDelayInSeconds: params.MaxSignalDelayInSeconds, MaxSignalDelayCount: params.MaxSignalDelayCount, } workflow.Go(ctx, func(ctx workflow.Context) { var activityResult signalActivityResult err := workflow.ExecuteActivity(ctx, loadTestActivityName, loaderParams).Get(ctx, &activityResult) if err != nil { workflow.GetLogger(ctx).Info("signal LoadTestActivity failed", zap.Error(err)) } else { workflow.GetLogger(ctx).Info("signal LoadTestActivity completed") } doneCh.Send(ctx, "done") }) } for i := 0; i < params.LoaderCount; i++ { doneCh.Receive(ctx, nil) } if err := workflow.Sleep(ctx, time.Minute*5); err != nil { profile.End(err) return err } actInput := verifyActivityParams{ FailedWorkflowCount: int64(float64(params.LoadTestWorkflowCount) * params.FailureThreshold), WorkflowStartTime: startTime.UnixNano(), } err = workflow.ExecuteActivity(ctx, verifyResultActivityName, actInput).Get(ctx, nil) return profile.End(err) } func loadTestActivity(ctx context.Context, params loadTestActivityParams) (signalActivityResult, error) { info := activity.GetInfo(ctx) logger := activity.GetLogger(ctx) cc := ctx.Value(lib.CtxKeyCadenceClient).(lib.CadenceClient) rc := ctx.Value(lib.CtxKeyRuntimeContext).(*lib.RuntimeContext) numTaskList := rc.Bench.NumTaskLists loaderID := info.WorkflowExecution.ID limiter := rate.NewLimiter(rate.Limit(params.RateLimit), 1) workflowOptions := client.StartWorkflowOptions{ ExecutionStartToCloseTimeout: time.Second * time.Duration(params.WorkflowExecutionTimeoutInSeconds), DecisionTaskStartToCloseTimeout: time.Second * time.Duration(params.DecisionTaskTimeoutInSeconds), WorkflowIDReusePolicy: client.WorkflowIDReusePolicyAllowDuplicate, } wfParams := lib.ProcessSignalWorkflowConfig{ CampaignCount: params.CampaignCount, ActionRate: params.ActionRate, FailureRate: params.FailureRate, SignalBeforeContinueAsNew: params.SignalCountBeforeContinueAsNew, MaxSignalDelayInSeconds: params.MaxSignalDelayInSeconds, MaxSignalDelayCount: params.MaxSignalDelayCount, } data := make([]byte, params.SignalDataSize) for i := 0; i < len(data); i++ { data[i] = 'a' } totalSignalCount := params.SignalCount * params.BatchWorkflowCount succeedCount, failedCount := 0, 0 wfIDs := make(map[string]struct{}) batchStartID := 0 if activity.HasHeartbeatDetails(ctx) { var finishedID int if err := activity.GetHeartbeatDetails(ctx, &finishedID); err == nil { batchStartID = finishedID + 1 logger.Info("recover from failed attempt", zap.Int("FinishedID", finishedID)) } } logger.Info("start sending signals", zap.Int32("Attempt", info.Attempt), zap.Int("StartSignalCount", batchStartID), zap.Int("TotalSignalCount", totalSignalCount), zap.Int("BatchWorkflowCount", params.BatchWorkflowCount)) wid := -1 for i := batchStartID; i < totalSignalCount; i++ { randTaskListID := rand.Intn(numTaskList) workflowOptions.TaskList = common.GetTaskListName(randTaskListID) if wid == -1 || rand.Float64() > 0.1 { // 10% chance we are going to send signal to the same workflow as last time, this is to simulate multiple // signals to same workflow within short time. wid = params.StartingWorkflowID + int(rand.Int31n(int32(params.BatchWorkflowCount))) /* If we have large number of target workflow set: We want to simulate the real scenario where signals coming to a small set of users instead of randomly to any user in the entire user set. This set of active user is moving around the glob as day time moves. So we want to simulate here is that, we define a 1 hour window around current time, and only select random user from this window. */ if params.EnableRollingWindow { wid = params.StartingWorkflowID + getRandomID(params.BatchWorkflowCount, time.Now()) } } workflowID := getStressWorkflowID(loaderID, wid) wfIDs[workflowID] = struct{}{} signal := signalEvent{SignalNumber: i, Data: data, Timestamp: time.Now().UnixNano()} if err := limiter.Wait(ctx); err != nil { if ctx.Err() != nil { return signalActivityResult{}, ctx.Err() } continue } wfParams.ScheduleTimeNano = time.Now().UnixNano() _, err := cc.SignalWithStartWorkflow(ctx, workflowID, stressTestSignalName, signal, workflowOptions, processSignalWorkflowName, wfParams) if err == nil { logger.Debug("SignalWithStartWorkflow succeed", zap.String("workflowID", workflowID)) succeedCount++ } else { logger.Error("SignalWithStartWorkflow failed", zap.Error(err)) failedCount++ } activity.RecordHeartbeat(ctx, i) if ctx.Err() != nil { return signalActivityResult{}, ctx.Err() } } // send last signal to notify the target workflow to exit exitStartID := 0 if batchStartID > totalSignalCount { // this means previous attempt failed after sending all normal signals, and failed while sending exit signal. exitStartID = batchStartID - totalSignalCount } for i := exitStartID; i < params.BatchWorkflowCount; i++ { wid := params.StartingWorkflowID + i workflowID := getStressWorkflowID(loaderID, wid) signal := signalEvent{SignalNumber: -1, Data: data} if err := limiter.Wait(ctx); err != nil { if ctx.Err() != nil { return signalActivityResult{}, ctx.Err() } continue } err := cc.SignalWorkflow(context.Background(), workflowID, "", stressTestSignalName, signal) if err == nil { logger.Debug("SignalWorkflow succeed", zap.String("workflowID", workflowID)) succeedCount++ } else { logger.Error("SignalWorkflow failed", zap.Error(err)) failedCount++ } activity.RecordHeartbeat(ctx, totalSignalCount+i) if ctx.Err() != nil { return signalActivityResult{}, ctx.Err() } } return signalActivityResult{SucceedCount: succeedCount, FailureCount: failedCount}, nil } func verifyResultActivity(ctx context.Context, params verifyActivityParams) error { cc := ctx.Value(lib.CtxKeyCadenceClient).(lib.CadenceClient) info := activity.GetInfo(ctx) // step 1. verify if any open workflow listWorkflowRequest := &shared.ListOpenWorkflowExecutionsRequest{ Domain: c.StringPtr(info.WorkflowDomain), MaximumPageSize: &maxPageSize, StartTimeFilter: &shared.StartTimeFilter{ EarliestTime: c.Int64Ptr(params.WorkflowStartTime), LatestTime: c.Int64Ptr(time.Now().UnixNano()), }, TypeFilter: &shared.WorkflowTypeFilter{ Name: c.StringPtr(processSignalWorkflowName), }, } openWorkflow, err := cc.ListOpenWorkflow(ctx, listWorkflowRequest) if err != nil { return err } if len(openWorkflow.Executions) > 0 { return cadence.NewCustomError( common.ErrReasonValidationFailed, "found open workflows after signal load test completed", ) } // step 2: check failed workflow reason query := fmt.Sprintf( failedSignalWorkflowQuery, processSignalWorkflowName, params.WorkflowStartTime, time.Now().UnixNano()) request := &shared.CountWorkflowExecutionsRequest{ Domain: c.StringPtr(info.WorkflowDomain), Query: &query, } resp, err := cc.CountWorkflow(ctx, request) if err != nil { return err } if resp.GetCount() > params.FailedWorkflowCount { return cadence.NewCustomError( common.ErrReasonValidationFailed, "found failed workflows after signal load test completed", ) } return nil } func getStressWorkflowID(loaderID string, wid int) string { return fmt.Sprintf("%v-sync-stress-%v", loaderID, wid) } func processSignalWorkflow(ctx workflow.Context, params lib.ProcessSignalWorkflowConfig) error { logger := workflow.GetLogger(ctx) info := workflow.GetInfo(ctx) profile, err := lib.BeginWorkflow(ctx, processSignalWorkflowName, params.ScheduleTimeNano) if err != nil { return err } ch := workflow.GetSignalChannel(ctx, stressTestSignalName) totalSigCount := 0 signalDelayCount := 0 main_loop: for { var signal signalEvent ch.Receive(ctx, &signal) totalSigCount++ if signal.SignalNumber == exitSignalNumber { break main_loop } err := processSignal(ctx, signal, params) if err != nil { // log the error and continue logger.Error("sync api bench test process signal failed.") } batchSigCount := 1 for ch.ReceiveAsync(&signal) { totalSigCount++ batchSigCount++ if signal.SignalNumber == exitSignalNumber { break main_loop } if workflow.Now(ctx).Sub(time.Unix(0, signal.Timestamp)) > time.Duration(params.MaxSignalDelayInSeconds)*time.Second { signalDelayCount++ if signalDelayCount > params.MaxSignalDelayCount { return fmt.Errorf(fmt.Sprintf("received %v signals are longer than %v seconds.", params.MaxSignalDelayCount, params.MaxSignalDelayInSeconds)) } } err := processSignal(ctx, signal, params) if err != nil { // log the error and continue logger.Error("sync stress workflow process signal failed.") } if batchSigCount >= 5 { logger.Info("force sleep 1s", zap.String("WorkflowID", info.WorkflowExecution.ID), zap.String("RunID", info.WorkflowExecution.RunID)) err := workflow.Sleep(ctx, time.Second) if err != nil { logger.Error("Failed sleep", zap.Error(err)) } break // continue main_loop } } if totalSigCount >= params.SignalBeforeContinueAsNew { logger.Info("ContinueAsNew", zap.Int("ProcessedSignalCount", totalSigCount)) profile.End(nil) params.ScheduleTimeNano = workflow.Now(ctx).UnixNano() return workflow.NewContinueAsNewError(ctx, processSignalWorkflowName, params) } } logger.Info("sync stress workflow completed") profile.End(nil) return nil } func processSignal(ctx workflow.Context, signal signalEvent, params lib.ProcessSignalWorkflowConfig) error { retryPolicy := &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 3, MaximumInterval: time.Minute, ExpirationInterval: time.Minute * 5, MaximumAttempts: 5, } lao := workflow.LocalActivityOptions{ ScheduleToCloseTimeout: time.Second * 3, RetryPolicy: retryPolicy, } ctx = workflow.WithLocalActivityOptions(ctx, lao) ao := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Hour, StartToCloseTimeout: time.Minute, RetryPolicy: retryPolicy, } ctx = workflow.WithActivityOptions(ctx, ao) var actionFutures []workflow.Future var trueConditions []string for i := 0; i < params.CampaignCount; i++ { var conditionMeet bool err := workflow.ExecuteLocalActivity(ctx, checkCondition, params.ActionRate, params.FailureRate).Get(ctx, &conditionMeet) if err != nil { return err } if conditionMeet { f := workflow.ExecuteActivity(ctx, processSignalActivityName, signal, params.FailureRate, workflow.Now(ctx).UnixNano()) actionFutures = append(actionFutures, f) trueConditions = append(trueConditions, strconv.Itoa(i)) } } for _, f := range actionFutures { var actionResult string err := f.Get(ctx, &actionResult) if err != nil { return err } } workflow.GetLogger(ctx).Info("Processed signal", zap.Int("SignalNumber", signal.SignalNumber), zap.Int("ActionCount", len(trueConditions))) return nil } // checkCondition is a local activity to check condition, it returns true if action needs to be taken func checkCondition( ctx context.Context, actionRate float64, failureRate float64, ) (bool, error) { info := activity.GetInfo(ctx) logger := activity.GetLogger(ctx) if info.Attempt > 0 { failureRate += 0.1 * float64(info.Attempt) // increase failure rate for retry attempt logger.Info("Retry attempt", zap.Int32("Attempt", info.Attempt), zap.Float64("FailureRate", failureRate)) } if rand.Float64() < failureRate { return false, errors.New("failed by rand") } return rand.Float64() < actionRate, nil } // processSignalActivity is a regular activity simulate actual action func processSignalActivity( ctx context.Context, evt signalEvent, failureRate float64, scheduleTimeNano int64, ) (err error) { info := activity.GetInfo(ctx) logger := activity.GetLogger(ctx) svcConfig := common.GetActivityServiceConfig(ctx) scope := svcConfig.Metrics if scope == nil { panic("metrics client is not set") } scope, stopWatch := lib.RecordActivityStart(scope, processSignalActivityName, scheduleTimeNano) defer lib.RecordActivityEnd(scope, stopWatch, err) if info.Attempt > 0 { failureRate += 0.1 * float64(info.Attempt) // incrase failure rate for retry attempt logger.Info("Retry attempt", zap.Int32("Attempt", info.Attempt), zap.Float64("FailureRate", failureRate)) } if rand.Float64() < failureRate { return errors.New("failed by rand") } return nil } var secondsInADay = 24 * 60 * 60 func getRandomID(totalCount int, now time.Time) int { r := rand.Float64() windowSize := totalCount / 24 // window size seconds := now.Second() + now.Minute()*60 + now.Hour()*3600 beginOfWindowID := int64(seconds-1800) * int64(totalCount) / int64(secondsInADay) wid := int(beginOfWindowID + int64(r*float64(windowSize))) wid = (wid + totalCount) % totalCount return wid } ================================================ FILE: bench/load/timer/launchWorkflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package timer import ( "context" "fmt" "math/rand" "time" "github.com/google/uuid" "go.uber.org/cadence" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/client" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/bench/lib" "github.com/uber/cadence/bench/load/common" ) const ( // TestName is the test name for timer test TestName = "timer" // LauncherWorkflowName is the workflow name for launching timer test LauncherWorkflowName = "timer-load-test-workflow" ) type ( launcherActivityProgress struct { WorkflowStarted int NextStartID int } ) // RegisterLauncher registers workflows and activities for timer load launching func RegisterLauncher(w worker.Worker) { w.RegisterWorkflowWithOptions(launcherWorkflow, workflow.RegisterOptions{Name: LauncherWorkflowName}) w.RegisterActivity(launcherActivity) w.RegisterActivity(validationActivity) } func launcherWorkflow(ctx workflow.Context, config lib.TimerTestConfig) (float64, error) { testTimeout := time.Duration(workflow.GetInfo(ctx).ExecutionStartToCloseTimeoutSeconds) * time.Second if testTimeout <= time.Duration(config.LongestTimerDurationInSeconds+config.TimerTimeoutInSeconds)*time.Second { return 0, cadence.NewCustomError("Test timeout too short, need to be longer than LongestTimerDuration + TimerTimeout") } totalLaunchCount := config.TotalTimerCount / config.TimerPerWorkflow workflowsPerActivity := totalLaunchCount / config.RoutineCount ao := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Hour, StartToCloseTimeout: testTimeout, HeartbeatTimeout: 20 * time.Second, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2, MaximumAttempts: 10, }, } ctx = workflow.WithActivityOptions(ctx, ao) startTime := workflow.Now(ctx) futures := make([]workflow.Future, 0, config.RoutineCount) for i := 0; i != config.RoutineCount; i++ { futures = append(futures, workflow.ExecuteActivity(ctx, launcherActivity, i, workflowsPerActivity, startTime, config)) } var totalStartedWorkflow int for _, future := range futures { var workflowStarted int if err := future.Get(ctx, &workflowStarted); err != nil { return 0, err } totalStartedWorkflow += workflowStarted } // wait until the timer fires and timerWorkflow completes waitDuration := startTime.Add(time.Duration(config.LongestTimerDurationInSeconds+config.TimerTimeoutInSeconds) * time.Second).Sub(workflow.Now(ctx)) if err := workflow.NewTimer(ctx, waitDuration).Get(ctx, nil); err != nil { return 0, err } // start the validation phase // 1. check if enough workflow are started if float64(totalStartedWorkflow) < common.DefaultAvailabilityThreshold*float64(totalLaunchCount) { return 0, cadence.NewCustomError( common.ErrReasonValidationFailed, fmt.Sprintf("Too few workflows are started. Expected: %v, actual: %v", totalLaunchCount, totalStartedWorkflow), ) } // 2. run an activity to check the status of started workflow by querying visibility records // this checks 3 things: // (1) number of workflow in visibility matches the one returned from launcher activity // (2) if all started workflow has closed (meaning fire has fired and no workflow stuck). // (3) how many timers are fired within the MaxTimerLatency limit validationActivityOptions := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2, MaximumAttempts: 5, NonRetriableErrorReasons: []string{common.ErrReasonValidationFailed}, }, } ctx = workflow.WithActivityOptions(ctx, validationActivityOptions) var firedInTimeRatio float64 if err := workflow.ExecuteActivity(ctx, validationActivity, startTime.Add(-10*time.Second).UnixNano(), totalStartedWorkflow).Get(ctx, &firedInTimeRatio); err != nil { return 0, err } return firedInTimeRatio, nil } func launcherActivity( ctx context.Context, routineID int, launchCount int, startTime time.Time, config lib.TimerTestConfig, ) (int, error) { logger := activity.GetLogger(ctx) logger.Info("Start launcher activity", zap.Int("routineID", routineID)) cadenceClient := ctx.Value(lib.CtxKeyCadenceClient).(lib.CadenceClient) runtimeContext := ctx.Value(lib.CtxKeyRuntimeContext).(*lib.RuntimeContext) numTaskList := runtimeContext.Bench.NumTaskLists input := WorkflowParams{ TimerCount: config.TimerPerWorkflow, EarliesTimerFireTime: startTime.Add(time.Duration(config.ShortestTimerDurationInSeconds) * time.Second), LatestTimerFireTime: startTime.Add(time.Duration(config.LongestTimerDurationInSeconds) * time.Second), MaxTimerLatency: time.Duration(config.MaxTimerLatencyInSeconds) * time.Second, } workflowOptions := client.StartWorkflowOptions{ ExecutionStartToCloseTimeout: 72 * time.Hour, DecisionTaskStartToCloseTimeout: 5 * time.Minute, } var progress launcherActivityProgress if activity.HasHeartbeatDetails(ctx) { if err := activity.GetHeartbeatDetails(ctx, &progress); err != nil { logger.Error("Failed to get activity heartbeat details", zap.Int("routineID", routineID), zap.Error(err)) progress = launcherActivityProgress{ WorkflowStarted: 0, NextStartID: 0, } } else { logger.Info("Successfully loaded activity progress", zap.Int("routineID", routineID), zap.Any("progress", progress)) } } for i := progress.NextStartID; i < launchCount; i++ { workflowOptions.ID = fmt.Sprintf("%s-%d-%s", TestName, routineID, uuid.New()) workflowOptions.TaskList = common.GetTaskListName(rand.Intn(numTaskList)) _ = common.RetryOp(func() error { startCtx, cancel := context.WithTimeout(ctx, common.DefaultContextTimeout) _, err := cadenceClient.StartWorkflow(startCtx, workflowOptions, timerWorkflowName, input) cancel() if err == nil || cadence.IsWorkflowExecutionAlreadyStartedError(err) { progress.WorkflowStarted++ return nil } logger.Error("Failed to start workflow execution", zap.Error(err)) return err }, nil) progress.NextStartID++ activity.RecordHeartbeat(ctx, progress) time.Sleep(time.Duration(75+rand.Intn(50)) * time.Millisecond) } logger.Info("Completed launcher workflow", zap.Int("routineID", routineID)) return progress.WorkflowStarted, nil } func validationActivity( ctx context.Context, startTimeNanos int64, startedWorkflow int, ) (float64, error) { cc := ctx.Value(lib.CtxKeyCadenceClient).(lib.CadenceClient) domain := activity.GetInfo(ctx).WorkflowDomain // 1. check if enough workflows are started query := fmt.Sprintf("WorkflowType = '%s' and StartTime > %v", timerWorkflowName, startTimeNanos) request := &shared.CountWorkflowExecutionsRequest{ Domain: &domain, Query: &query, } resp, err := cc.CountWorkflow(ctx, request) if err != nil { return 0, err } actualLaunchedCount := resp.GetCount() if actualLaunchedCount < int64(startedWorkflow) { return 0, cadence.NewCustomError(common.ErrReasonValidationFailed, fmt.Sprintf("Expected to start %v workflow, actual started: %v", startedWorkflow, actualLaunchedCount)) } // 2. check if timer has fired and workflow has closed query = fmt.Sprintf("WorkflowType = '%s' and StartTime > %v and CloseTime != missing", timerWorkflowName, startTimeNanos) request.Query = &query resp, err = cc.CountWorkflow(ctx, request) if err != nil { return 0, err } if resp.GetCount() != actualLaunchedCount { return 0, cadence.NewCustomError(common.ErrReasonValidationFailed, fmt.Sprintf("Not all workflows are closed, started: %v, closed: %v", actualLaunchedCount, resp.GetCount())) } // 3. check # of timers that are fired within max latency query = fmt.Sprintf("WorkflowType = '%s' and StartTime > %v and CloseStatus = 0", timerWorkflowName, startTimeNanos) request.Query = &query resp, err = cc.CountWorkflow(ctx, request) if err != nil { return 0, err } firedInTime := resp.GetCount() if firedInTime < int64(float64(actualLaunchedCount)*common.DefaultAvailabilityThreshold) { return 0, cadence.NewCustomError(common.ErrReasonValidationFailed, fmt.Sprintf("Too few timers fired within latency limit, expected: %v, actual: %v", actualLaunchedCount, firedInTime)) } return float64(firedInTime) / float64(actualLaunchedCount), nil } ================================================ FILE: bench/load/timer/timerWorkflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package timer import ( "fmt" "math" "math/rand" "time" "go.uber.org/cadence" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "github.com/uber/cadence/bench/load/common" ) const ( timerWorkflowName = "timerWorkflow" ) type ( // WorkflowParams inputs to workflow. WorkflowParams struct { TimerCount int EarliesTimerFireTime time.Time LatestTimerFireTime time.Time MaxTimerLatency time.Duration } ) // RegisterWorker registers workflows and activities for timer load func RegisterWorker(w worker.Worker) { w.RegisterWorkflowWithOptions(timerWorkflow, workflow.RegisterOptions{Name: timerWorkflowName}) } func timerWorkflow(ctx workflow.Context, workflowInput WorkflowParams) error { now := workflow.Now(ctx) shortestTimerDuration := workflowInput.EarliesTimerFireTime.Sub(now) timerFireWindowInSeconds := int64(workflowInput.LatestTimerFireTime.Sub(workflowInput.EarliesTimerFireTime).Seconds()) selector := workflow.NewSelector(ctx) expectedFireTime := time.Unix(0, math.MaxInt64) for i := 0; i != workflowInput.TimerCount; i++ { timerDuration := shortestTimerDuration if timerFireWindowInSeconds > 0 { var randDurationNano int64 workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} { return rand.Int63n(timerFireWindowInSeconds) }).Get(&randDurationNano) timerDuration += time.Duration(randDurationNano) * time.Second } if timerDuration < 0 { timerDuration = time.Second } fireTime := now.Add(timerDuration) if fireTime.Before(expectedFireTime) { expectedFireTime = fireTime } f := workflow.NewTimer(ctx, timerDuration) selector.AddFuture(f, func(_ workflow.Future) {}) } selector.Select(ctx) timerLatency := workflow.Now(ctx).Sub(expectedFireTime) if timerLatency > workflowInput.MaxTimerLatency { return cadence.NewCustomError("timer latency too high", fmt.Sprintf("expectedLatency: %v, actual latency: %v", workflowInput.MaxTimerLatency, timerLatency)) } ao := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Hour, StartToCloseTimeout: time.Hour, } ctx = workflow.WithActivityOptions(ctx, ao) return workflow.ExecuteActivity(ctx, common.EchoActivityName, common.EchoActivityParams{}).Get(ctx, nil) } ================================================ FILE: bench/worker.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package bench import ( "context" "fmt" "sync" "go.uber.org/cadence/worker" "go.uber.org/zap" "github.com/uber/cadence/bench/lib" "github.com/uber/cadence/bench/load/basic" "github.com/uber/cadence/bench/load/cancellation" "github.com/uber/cadence/bench/load/common" "github.com/uber/cadence/bench/load/concurrentexec" "github.com/uber/cadence/bench/load/cron" "github.com/uber/cadence/bench/load/signal" "github.com/uber/cadence/bench/load/timer" ) type ( loadTestWorker struct { clients map[string]lib.CadenceClient runtime *lib.RuntimeContext shutdownWG sync.WaitGroup } ) const ( decisionWorkerConcurrency = 20 activityWorkerConcurrency = 5000 stickyWorkflowCacheSize = 100000 ) // NewWorker builds and returns a new instance of load test worker func NewWorker(cfg *lib.Config) (lib.Runnable, error) { rc, err := lib.NewRuntimeContext(cfg) if err != nil { return nil, err } clients, err := lib.NewCadenceClients(rc) if err != nil { return nil, err } return &loadTestWorker{ runtime: rc, clients: clients, }, nil } // Run runs the worker func (w *loadTestWorker) Run() error { if err := w.createDomains(); err != nil { return fmt.Errorf("creating bench domain: %w", err) } worker.SetStickyWorkflowCacheSize(stickyWorkflowCacheSize) w.runtime.Metrics.Counter("worker.restarts").Inc(1) for _, domainName := range w.runtime.Bench.Domains { for i := 0; i < w.runtime.Bench.NumTaskLists; i++ { w.shutdownWG.Add(1) go w.runWorker(domainName, common.GetTaskListName(i)) } } w.shutdownWG.Wait() return nil } func (w *loadTestWorker) createDomains() error { for _, domainName := range w.runtime.Bench.Domains { desc := "Domain for running cadence load test" owner := "cadence-bench" if err := w.clients[domainName].CreateDomain(domainName, desc, owner); err != nil { return err } } return nil } func (w *loadTestWorker) runWorker( domainName string, taskList string, ) { defer w.shutdownWG.Done() opts := w.newWorkerOptions(domainName) worker := worker.New(w.clients[domainName].Service, domainName, taskList, opts) registerWorkers(worker) registerLaunchers(worker) if err := worker.Run(); err != nil { w.runtime.Logger.Error("Failed to start worker", zap.String("Domain", domainName), zap.String("TaskList", taskList), zap.Error(err)) } } func (w *loadTestWorker) newWorkerOptions(domainName string) worker.Options { return worker.Options{ Logger: w.runtime.Logger, MetricsScope: w.runtime.Metrics, BackgroundActivityContext: w.newActivityContext(domainName), // TODO: do we need these limits? MaxConcurrentActivityExecutionSize: activityWorkerConcurrency, MaxConcurrentDecisionTaskExecutionSize: decisionWorkerConcurrency, } } func (w *loadTestWorker) newActivityContext(domainName string) context.Context { ctx := context.Background() ctx = context.WithValue(ctx, lib.CtxKeyCadenceClient, w.clients[domainName]) return context.WithValue(ctx, lib.CtxKeyRuntimeContext, w.runtime) } func registerWorkers(w worker.Worker) { common.RegisterWorker(w) basic.RegisterWorker(w) signal.RegisterWorker(w) timer.RegisterWorker(w) concurrentexec.RegisterWorker(w) cancellation.RegisterWorker(w) } func registerLaunchers(w worker.Worker) { cron.RegisterLauncher(w) signal.RegisterLauncher(w) basic.RegisterLauncher(w) timer.RegisterLauncher(w) concurrentexec.RegisterLauncher(w) cancellation.RegisterLauncher(w) } ================================================ FILE: canary/README.md ================================================ # Periodical feature health check workflow tools(aka Canary) This README describes how to set up Cadence canary, different types of canary test cases, and how to start the canary. Setup ----------- ## Prerequisite: Cadence server Canary test suite is running against a Cadence server/cluster. See [documentation](https://cadenceworkflow.io/docs/operation-guide/setup/) for Cadence server cluster setup. Note that some tests require features like [Advanced Visibility]((https://cadenceworkflow.io/docs/concepts/search-workflows/).) and [History Archival](https://cadenceworkflow.io/docs/concepts/archival/). For local server env you can run it through: - Docker: Instructions for running Cadence server through docker can be found in `docker/README.md`. Either `docker-compose-es-v7.yml` or `docker-compose-es.yml` can be used to start the server. - Build from source: Please check [CONTRIBUTING](/CONTRIBUTING.md) for how to build and run Cadence server from source. Please also make sure Kafka and ElasticSearch are running before starting the server with `./cadence-server --zone es start`. If ElasticSearch v7 is used, change the value for `--zone` flag to `es_v7`. ## Run canary Different ways of start the canary: ### 1. Use docker image `ubercadence/cadence-canary:master` You can [pre-built docker-compose file](../docker/docker-compose-canary.yml) to run against local server In the `docker/` directory, run: ``` docker compose -f docker-compose-canary.yml up ``` This will start the canary worker and also the cron canary. You can modify [the canary worker config](../docker/config/canary/development.yaml) to run against a prod server cluster: * Use a different mode to start canary worker only for testing * Update the config to use Thrift/gRPC for communication * Use a different image than `master` tag. See [docker hub](https://hub.docker.com/repository/docker/ubercadence/cadence-canary) for all the images. Similar to server/CLI images, the `master` image will be built and published automatically by Github on every commit onto the `master` branch. ### 2. Build & Run In the project root, build cadence canary binary: ``` make cadence-canary ``` Then start canary worker & cron: ``` ./cadence-canary start ``` This is essentially the same as ``` ./cadence-canary start -mode all ``` By default, it will load [the configuration in `config/canary/development.yaml`](../config/canary/development.yaml). Run `./cadence-canary -h` for details to understand the start options of how to change the loading directory if needed. To start the worker only for manual testing certain cases: ``` ./cadence-canary start -mode worker ``` ### 3. Monitoring In production, it's recommended to monitor the result of this canary. You can use [the workflow success metric](https://github.com/cadence-workflow/cadence/blob/9336ed963ca1b5e0df7206312aa5236433e04fd9/service/history/execution/context_util.go#L138) emitted by cadence history service `workflow_success`. To monitor all the canary test cases, use `workflowType` of `workflow.sanity`. Configurations ---------------------- Canary workers configuration contains two parts: - **Canary**: this part controls which domains canary workers are responsible for what tests the sanity workflow will exclude. ```yaml canary: domains: ["cadence-canary"] # it will start workers on all those domains(also try to register if not exists) excludes: ["workflow.searchAttributes", "workflow.batch", "workflow.archival.visibility", "workflow.archival.history"] # it will exclude the three test cases. If archival is not enabled, you should exclude "workflow.archival.visibility" and"workflow.archival.history". If advanced visibility is not enabled, you should exclude "workflow.searchAttributes" and "workflow.batch". Otherwise canary will fail on those test cases. cron: cronSchedule: "@every 30s" #the schedule of cron canary, default to "@every 30s" cronExecutionTimeout: 18m #the timeout of each run of the cron execution, default to 18 minutes startJobTimeout: 9m #the timeout of each run of the sanity test suite, default to 9 minutes ``` An exception here is `HistoryArchival` and `VisibilityArchival` test cases will always use `canary-archival-domain` domain. - **Cadence**: this control how canary worker should talk to Cadence server, which includes the server's service name and address. ```yaml cadence: service: "cadence-frontend" # frontend service name address: "127.0.0.1:7833" # frontend address #host: "127.0.0.1:7933" # replace address with host if using Thrift for compatibility #tlsCaFile: "path/to/file" # give file path to TLS CA file if TLS is enabled on the Cadence server #metrics: ... # optional detailed client side metrics like workflow latency. But for monitoring, simply use server side metrics `workflow_success` is enough. ``` - **Metrics**: metrics configuration. Similar to server metric emitter, only M3/Statsd/Prometheus is supported. - **Log**: logging configuration. Similar to server logging configuration. Canary Test Cases & Starter ---------------------- ### Cron Canary (periodically running the Sanity/starter suite) The Cron workflow is not a test case. It's a top-level workflow to kick off the Sanity suite(described below) periodically. To start the cron canary: ``` ./cadence-canary start -mode cronCanary ``` For local development, you can also start the cron canary workflows along with the worker: ``` ./cadence-canary start -m all ``` The Cron Schedule is from the Configuration. However, changing the schedule requires you manually terminate the existing cron workflow to take into effect. It can be [improved](https://github.com/cadence-workflow/cadence/issues/4469) in the future. The workflowID is fixed: `"cadence.canary.cron"` ### Sanity suite (Starter for all test cases) The sanity workflow is test suite workflow. It will kick off a bunch of childWorkflows for all the test to verify that Cadence server is operating correctly. An error result of the sanity workflow indicates at least one of the test case fails. You can start the sanity workflow as one-off run: ``` cadence --do workflow start --tl canary-task-queue --et 1200 --wt workflow.sanity -i 0 ``` Or using the Cron Canary mentioned above to manage it. Then observe the progress: ``` cadence --do cadence-canary workflow ob -w <...workflowID form the start command output> ``` NOTE 1: * tasklist(tl) is fixed to `canary-task-queue` * execution timeout(et) is recommended to 20 minutes(`1200` seconds) but you can adjust it * the only required input is the scheduled unix timestamp, and `0` will uses the workflow starting time NOTE 2: This is the workflow that you should monitor for alerting. You can use [the workflow success metric](https://github.com/cadence-workflow/cadence/blob/9336ed963ca1b5e0df7206312aa5236433e04fd9/service/history/execution/context_util.go#L138) emitted by cadence history service `workflow_success`. To monitor all the canary test cases use `workflowType` of `workflow.sanity`. NOTE 3: This is [the list of the test cases](./sanity.go) that it will start all supported test cases by default if no excludes are configured. You can find [the workflow names of the tests cases in this file](./const.go) if you want to manually start certain test cases. ### Echo Echo workflow tests the very basic workflow functionality. It executes an activity to return some output and verifies it as the workflow result. To manually start an `Echo` test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.echo -i 0 ``` Then observe the progress: ``` cadence --do cadence-canary workflow ob -w <...workflowID form the start command output> ``` You can use these command for all other test cases listed below. ### Signal Signal workflow tests the signal feature. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.signal -i 0 ``` ### Visibility Visibility workflow tests the basic visibility feature. No advanced visibility needed, but advanced visibility should also support it. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.visibility -i 0 ``` ### SearchAttributes SearchAttributes workflow tests the advanced visibility feature. Make sure advanced visibility feature is configured on the server. Otherwise, it should be excluded from the sanity test suite/case. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.searchAttributes -i 0 ``` ### ConcurrentExec ConcurrentExec workflow tests executing activities concurrently. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.concurrent-execution -i 0 ``` ### Query Query workflow tests the Query feature. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.query -i 0 ``` ### Timeout Timeout workflow make sure the activity timeout is enforced. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.timeout -i 0 ``` ### LocalActivity LocalActivity workflow tests the local activity feature. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.localactivity -i 0 ``` ### Cancellation Cancellation workflowt tests cancellation feature. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.cancellation -i 0 ``` ### Retry Retry workflow tests activity retry policy. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.retry -i 0 ``` ### Reset Reset workflow tests reset feature. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.reset -i 0 ``` ### HistoryArchival HistoryArchival tests history archival feature. Make sure history archival feature is configured on the server. Otherwise, it should be excluded from the sanity test suite/case. This test case always uses `canary-archival-domain` domain. To manually start one run of this test case: ``` cadence --do canary-archival-domain workflow start --tl canary-task-queue --et 10 --wt workflow.timeout -i 0 ``` ### VisibilityArchival VisibilityArchival tests visibility archival feature. Make sure visibility feature is configured on the server. Otherwise, it should be excluded from the sanity test suite/case. This test case always uses `canary-archival-domain` domain. To manually start one run of this test case: ``` cadence --do canary-archival-domain workflow start --tl canary-task-queue --et 10 --wt workflow.timeout -i 0 ``` ### Batch Batch workflow tests the batch job feature. Make sure advanced visibility feature is configured on the server. Otherwise, it should be excluded from the sanity test suite/case. To manually start one run of this test case: ``` cadence --do <> workflow start --tl canary-task-queue --et 10 --wt workflow.batch -i 0 ``` ================================================ FILE: canary/batch.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "fmt" "time" "go.uber.org/cadence" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/client" "go.uber.org/cadence/workflow" ) func init() { registerWorkflow(batchWorkflow, wfTypeBatch) registerWorkflow(batchWorkflowParent, wfTypeBatchParent) registerWorkflow(batchWorkflowChild, wfTypeBatchChild) registerActivity(verifyBatchActivity, activityTypeVerifyBatch) registerActivity(startBatchWorkflow, activityTypeStartBatch) } const ( // TODO: to get rid of them: // after batch job has an API, we should use the API: https://github.com/uber/cadence/issues/2225 sysBatchWFTypeName = "cadence-sys-batch-workflow" systemBatcherTaskListName = "cadence-sys-batcher-tasklist" // there are two level, so totally 5*5 + 5 == 30 descendants // default batch RPS is 50, so it will takes ~1 seconds to terminate all numChildrenPerLevel = 5 ) type ( // BatchParams is from server repo // TODO: to get rid of it: // after batch job has an API, we should use the API: https://github.com/uber/cadence/issues/2225 BatchParams struct { DomainName string Query string Reason string BatchType string } ) func batchWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) domain := workflow.GetInfo(ctx).Domain profile, err := beginWorkflow(ctx, wfTypeBatch, scheduledTimeNanos) if err != nil { return err } cwo := workflow.ChildWorkflowOptions{ ExecutionStartToCloseTimeout: childWorkflowTimeout, TaskStartToCloseTimeout: decisionTaskTimeout, } ctx = workflow.WithChildOptions(ctx, cwo) var fs []workflow.ChildWorkflowFuture for i := 0; i < numChildrenPerLevel; i++ { f := workflow.ExecuteChildWorkflow(ctx, wfTypeBatchParent, scheduledTimeNanos) fs = append(fs, f) } // waiting for all workflow started for i := 0; i < numChildrenPerLevel; i++ { err := fs[i].GetChildWorkflowExecution().Get(ctx, nil) if err != nil { return profile.end(err) } } // waiting for visibility if err := workflow.Sleep(ctx, time.Second*2); err != nil { return profile.end(err) } retryPolicy := &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2, MaximumInterval: time.Second * 12, ExpirationInterval: time.Second * 3, MaximumAttempts: 4, } ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ TaskList: taskListName, ScheduleToStartTimeout: scheduleToStartTimeout, StartToCloseTimeout: activityTaskTimeout, RetryPolicy: retryPolicy, }) startTime := workflow.Now(ctx).Format(time.RFC3339) err = workflow.ExecuteActivity(ctx, activityTypeStartBatch, domain, startTime).Get(ctx, nil) if err != nil { return profile.end(err) } // waiting for visibility if err := workflow.Sleep(ctx, time.Second*2); err != nil { return profile.end(err) } err = workflow.ExecuteActivity(ctx, activityTypeVerifyBatch, domain, startTime).Get(ctx, nil) return profile.end(err) } func batchWorkflowParent(ctx workflow.Context, scheduledTimeNanos int64) error { profile, err := beginWorkflow(ctx, wfTypeBatchParent, scheduledTimeNanos) if err != nil { return err } cwo := workflow.ChildWorkflowOptions{ ExecutionStartToCloseTimeout: childWorkflowTimeout, TaskStartToCloseTimeout: decisionTaskTimeout, } ctx = workflow.WithChildOptions(ctx, cwo) var fs []workflow.ChildWorkflowFuture for i := 0; i < numChildrenPerLevel; i++ { f := workflow.ExecuteChildWorkflow(ctx, wfTypeBatchChild, scheduledTimeNanos) fs = append(fs, f) } // waiting for all workflow to finish for i := 0; i < numChildrenPerLevel; i++ { err := fs[i].Get(ctx, nil) if err != nil { return profile.end(err) } } return profile.end(err) } func batchWorkflowChild(ctx workflow.Context, scheduledTimeNanos int64) error { profile, err := beginWorkflow(ctx, wfTypeBatchChild, scheduledTimeNanos) if err != nil { return err } if err := workflow.Sleep(ctx, time.Hour); err != nil { return profile.end(err) } return profile.end(err) } func startBatchWorkflow(ctx context.Context, domain, startTime string) error { sdkClient := getContextValue(ctx, ctxKeyActivityBatcherClient).(*activityContext).cadence params := BatchParams{ DomainName: domain, Query: "WorkflowType = '" + wfTypeBatchParent + "' AND CloseTime = missing AND StartTime <'" + startTime + "' ", Reason: "batch canary", BatchType: "terminate", } options := client.StartWorkflowOptions{ ExecutionStartToCloseTimeout: childWorkflowTimeout, DecisionTaskStartToCloseTimeout: decisionTaskTimeout, TaskList: systemBatcherTaskListName, SearchAttributes: map[string]interface{}{ "CustomDomain": domain, "Operator": "admin", }, } run, err := sdkClient.ExecuteWorkflow(ctx, options, sysBatchWFTypeName, params) if err != nil { return err } err = run.Get(ctx, nil) return err } func verifyBatchActivity(ctx context.Context, domain, startTime string) error { svClient := getActivityContext(ctx).cadence.Service q1 := "WorkflowType = '" + wfTypeBatchParent + "' AND CloseTime = missing AND StartTime <'" + startTime + "' " resp, err := svClient.CountWorkflowExecutions(ctx, &shared.CountWorkflowExecutionsRequest{ Domain: &domain, Query: &q1, }) if err != nil { return err } if resp.GetCount() > 0 { return fmt.Errorf("still seeing open workflows for %v , %v, %v", wfTypeBatchParent, q1, resp.GetCount()) } q2 := "WorkflowType = '" + wfTypeBatchChild + "' AND CloseTime = missing AND StartTime <'" + startTime + "' " resp, err = svClient.CountWorkflowExecutions(ctx, &shared.CountWorkflowExecutionsRequest{ Domain: &domain, Query: &q2, }) if err != nil { return err } if resp.GetCount() > 0 { return fmt.Errorf("still seeing open workflows for %v, %v, %v", wfTypeBatchChild, q2, resp.GetCount()) } return nil } ================================================ FILE: canary/canary.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "fmt" "github.com/opentracing/opentracing-go" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/worker" "go.uber.org/zap" "github.com/uber/cadence/common/constants" ) type ( // Runnable is an interface for anything that exposes a Run method Runnable interface { Run(mode string) error } canaryImpl struct { canaryClient cadenceClient canaryDomain string crossClusterDestClient *cadenceClient archivalClient cadenceClient systemClient cadenceClient batcherClient cadenceClient runtime *RuntimeContext canaryConfig *Canary } activityContext struct { cadence cadenceClient } ) type contextKey string const ( // this context key should be the same as the one defined in // internal_worker.go, cadence go client testTagsContextKey contextKey = "cadence-testTags" ) // new returns a new instance of Canary runnable func newCanary(domain string, rc *RuntimeContext, canaryConfig *Canary) Runnable { canaryClient := newCadenceClient(domain, rc) archivalClient := newCadenceClient(archivalDomain, rc) systemClient := newCadenceClient(constants.SystemLocalDomainName, rc) batcherClient := newCadenceClient(constants.BatcherLocalDomainName, rc) var xClusterDest cadenceClient if canaryConfig.CrossClusterTestMode == CrossClusterCanaryModeFull { xClusterDest = newCadenceClient(deriveCanaryDomain(domain), rc) } return &canaryImpl{ canaryClient: canaryClient, canaryDomain: domain, crossClusterDestClient: &xClusterDest, archivalClient: archivalClient, systemClient: systemClient, batcherClient: batcherClient, runtime: rc, canaryConfig: canaryConfig, } } // Run runs the canary func (c *canaryImpl) Run(mode string) error { if mode != ModeCronCanary && mode != ModeAll && mode != ModeWorker { return fmt.Errorf("wrong mode to start canary") } var err error log := c.runtime.logger if err = c.createDomain(); err != nil { log.Error("createDomain failed", zap.Error(err)) return err } if err = c.createArchivalDomain(); err != nil { log.Error("createArchivalDomain failed", zap.Error(err)) return err } if mode == ModeAll || mode == ModeCronCanary { // start the initial cron workflow c.startCronWorkflow() } if mode == ModeAll || mode == ModeWorker { err = c.startWorker() if err != nil { log.Error("start worker failed", zap.Error(err)) return err } } return nil } func (c *canaryImpl) startWorker() error { c.runtime.logger.Info("starting canary worker...") options := worker.Options{ Logger: c.runtime.logger, MetricsScope: c.runtime.metrics, BackgroundActivityContext: c.newActivityContext(), MaxConcurrentActivityExecutionSize: activityWorkerMaxExecutors, Tracer: opentracing.GlobalTracer(), } archivalWorker := worker.New(c.archivalClient.Service, archivalDomain, archivalTaskListName, options) defer archivalWorker.Stop() if err := archivalWorker.Start(); err != nil { return err } canaryWorker := worker.New(c.canaryClient.Service, c.canaryDomain, taskListName, options) if c.canaryConfig.CrossClusterTestMode == CrossClusterCanaryModeFull { go worker.New(c.canaryClient.Service, c.canaryDomain, crossClusterSrcTasklist, options).Run() go worker.New(c.crossClusterDestClient.Service, c.getCrossClusterTargetDomain(), crossClusterDestTasklist, options).Run() } return canaryWorker.Run() } func (c *canaryImpl) startCronWorkflow() { c.runtime.logger.Info("starting canary cron workflow...") wfID := "cadence.canary.cron" opts := newWorkflowOptions(wfID, c.canaryConfig.Cron.CronExecutionTimeout) opts.CronSchedule = c.canaryConfig.Cron.CronSchedule // create the cron workflow span ctx := context.Background() span := opentracing.StartSpan("start-cron-workflow-span") defer span.Finish() ctx = opentracing.ContextWithSpan(ctx, span) c.registerMethods() _, err := c.canaryClient.StartWorkflow(ctx, opts, cronWorkflow, wfTypeSanity) if err != nil { // TODO: improvement: compare the cron schedule to decide whether or not terminating the current one // https://github.com/uber/cadence/issues/4469 if _, ok := err.(*shared.WorkflowExecutionAlreadyStartedError); !ok { c.runtime.logger.Error("error starting cron workflow", zap.Error(err)) } else { c.runtime.logger.Info("cron workflow already started, you may need to terminate and restart if cron schedule is changed...") } } } // newActivityContext builds an activity context containing // logger, metricsClient and cadenceClient func (c *canaryImpl) newActivityContext() context.Context { ctx := context.WithValue(context.Background(), ctxKeyActivityRuntime, &activityContext{cadence: c.canaryClient}) ctx = context.WithValue(ctx, ctxKeyActivityArchivalRuntime, &activityContext{cadence: c.archivalClient}) ctx = context.WithValue(ctx, ctxKeyActivitySystemClient, &activityContext{cadence: c.systemClient}) ctx = context.WithValue(ctx, ctxKeyActivityBatcherClient, &activityContext{cadence: c.batcherClient}) ctx = context.WithValue(ctx, ctxKeyConfig, c.canaryConfig) return overrideWorkerOptions(ctx) } func (c *canaryImpl) createDomain() error { name := c.canaryDomain desc := "Domain for running cadence canary workflows" owner := "cadence-canary" archivalStatus := shared.ArchivalStatusDisabled err := c.canaryClient.createDomain(name, desc, owner, &archivalStatus, true, c.canaryConfig.CanaryDomainClusters) if err != nil { return err } if c.canaryConfig.CrossClusterTestMode == CrossClusterCanaryModeFull { err := c.crossClusterDestClient.createDomain( deriveCanaryDomain(c.canaryDomain), "cross cluster canary dest domain", owner, &archivalStatus, true, c.canaryConfig.CanaryDomainClusters, ) if err != nil { return fmt.Errorf("failed to setup cross-cluster domain on canary domain registration: %w", err) } } return nil } // registers workflow methods and activities func (c *canaryImpl) registerMethods() { registerWorkflow(c.crossClusterParentWf, wfTypeCrossClusterParent) registerWorkflow(c.crossClusterChildWf, wfTypeCrossClusterChild) registerActivity(c.crossClusterSampleActivity, activityTypeCrossCluster) registerActivity(c.failoverDestDomainActivity, activityTypeCrossClusterFailover) } func (c *canaryImpl) createArchivalDomain() error { name := archivalDomain desc := "Domain used by cadence canary workflows to verify archival" owner := "cadence-canary" archivalStatus := shared.ArchivalStatusEnabled return c.archivalClient.createDomain(name, desc, owner, &archivalStatus, true, c.canaryConfig.CanaryDomainClusters) } func (c *canaryImpl) getCrossClusterTargetDomain() string { return deriveCanaryDomain(c.canaryDomain) } // Override worker options to create large number of pollers to improve the chances of activities getting sync matched // //nolint:unused func overrideWorkerOptions(ctx context.Context) context.Context { optionsOverride := make(map[string]map[string]string) optionsOverride["worker-options"] = map[string]string{ "ConcurrentPollRoutineSize": "20", } return context.WithValue(ctx, testTagsContextKey, optionsOverride) } func deriveCanaryDomain(domain string) string { return fmt.Sprintf("%s-cross-cluster", domain) } ================================================ FILE: canary/cancellation.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "errors" "time" "go.uber.org/cadence" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) const ( sleepDuration = time.Second * 10 ) func init() { registerWorkflow(cancellationWorkflow, wfTypeCancellation) registerWorkflow(cancellationExternalWorkflow, wfTypeCancellationExternal) registerActivity(cancellationActivity, activityTypeCancellation) registerActivity(cancellationChildActivity, activityTypeCancellationChild) } // cancellationWorkflow is the workflow implementation to test for cancellation of workflows func cancellationWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) domain := workflow.GetInfo(ctx).Domain profile, err := beginWorkflow(ctx, wfTypeCancellation, scheduledTimeNanos) if err != nil { return err } // this test cancel external workflow activityCtx := workflow.WithActivityOptions(ctx, newActivityOptions()) err = workflow.ExecuteActivity(activityCtx, activityTypeCancellation, workflow.Now(ctx).UnixNano()).Get(activityCtx, nil) if err != nil { workflow.GetLogger(ctx).Info("cancel workflow failed", zap.Error(err)) return profile.end(err) } // this test cancellation of child workflow, inside workflow cwo := newChildWorkflowOptions(domain, wfTypeCancellationExternal+"-child") childCtx := workflow.WithChildOptions(ctx, cwo) childCtx, cancel := workflow.WithCancel(childCtx) childFuture := workflow.ExecuteChildWorkflow(childCtx, wfTypeCancellationExternal, workflow.Now(ctx).UnixNano(), sleepDuration) if err := workflow.Sleep(ctx, 1*time.Second); err != nil { return profile.end(err) } cancel() err = childFuture.Get(childCtx, nil) if err == nil { msg := "cancellationWorkflow failed: child workflow not cancelled" workflow.GetLogger(ctx).Info(msg) return profile.end(errors.New(msg)) } if _, ok := err.(*cadence.CanceledError); !ok { workflow.GetLogger(ctx).Info("cancellationWorkflow failed: child workflow return non CanceledError", zap.Error(err)) return profile.end(err) } // this test cancellation of child workflow, outside workflow activityCancelChildTestFn := func(useRunID bool) error { cwo := newChildWorkflowOptions(domain, wfTypeCancellationExternal+"-child-outside") childCtx := workflow.WithChildOptions(ctx, cwo) childFuture := workflow.ExecuteChildWorkflow(childCtx, wfTypeCancellationExternal, workflow.Now(ctx).UnixNano(), sleepDuration) childExecution := &workflow.Execution{} err := childFuture.GetChildWorkflowExecution().Get(childCtx, childExecution) if err != nil { return err } if !useRunID { childExecution.RunID = "" } // starts a activity to cancel this child workflow activityCtx := workflow.WithActivityOptions(ctx, newActivityOptions()) err = workflow.ExecuteActivity(activityCtx, activityTypeCancellationChild, workflow.Now(ctx).UnixNano(), childExecution).Get(activityCtx, nil) if err != nil { workflow.GetLogger(ctx).Info("cancel child workflow from activity failed", zap.Error(err)) return err } err = childFuture.Get(childCtx, nil) if err == nil { msg := "cancellationWorkflow failed: child workflow not cancelled" workflow.GetLogger(ctx).Info(msg) return errors.New(msg) } if _, ok := err.(*cadence.CanceledError); !ok { workflow.GetLogger(ctx).Info("cancellationWorkflow failed: child workflow return non CanceledError", zap.Error(err)) return err } return nil } err = activityCancelChildTestFn(false) if err != nil { return profile.end(err) } err = activityCancelChildTestFn(true) if err != nil { return profile.end(err) } decisionCancelTestFn := func(useRunID bool) error { workflowIDSuffix := "-with-run-ID" if !useRunID { workflowIDSuffix = "-without-run-ID" } cwo = newChildWorkflowOptions(domain, wfTypeCancellationExternal+workflowIDSuffix) childCtx = workflow.WithChildOptions(ctx, cwo) childFuture = workflow.ExecuteChildWorkflow(childCtx, wfTypeCancellationExternal, workflow.Now(ctx).UnixNano(), sleepDuration) childExecution := &workflow.Execution{} err = childFuture.GetChildWorkflowExecution().Get(childCtx, childExecution) if err != nil { return err } if !useRunID { childExecution.RunID = "" } cancellatinFuture := workflow.RequestCancelExternalWorkflow(ctx, childExecution.ID, childExecution.RunID) err = cancellatinFuture.Get(ctx, nil) if err != nil { workflow.GetLogger(ctx).Info("cancellationWorkflow failed: fail to send cancellation to child workflow", zap.Error(err)) return err } err = childFuture.Get(childCtx, nil) if err == nil { msg := "cancellationWorkflow failed: child workflow not cancelled" workflow.GetLogger(ctx).Info(msg) return errors.New(msg) } if _, ok := err.(*cadence.CanceledError); !ok { workflow.GetLogger(ctx).Info("cancellationWorkflow failed: child workflow return non CanceledError", zap.Error(err)) return err } return nil } err = decisionCancelTestFn(false) if err != nil { return profile.end(err) } err = decisionCancelTestFn(true) return profile.end(err) } // cancellationActivity is the activity implementation to test for cancellation of non child workflow, using API func cancellationActivity(ctx context.Context, scheduledTimeNanos int64) error { scope := activity.GetMetricsScope(ctx) var err error scope, sw := recordActivityStart(scope, activityTypeCancellation, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) client := getActivityContext(ctx).cadence apiCancelTestFn := func(useRunID bool) error { opts := newWorkflowOptions(wfTypeCancellationExternal, childWorkflowTimeout) workflowRun, err := client.ExecuteWorkflow(context.Background(), opts, wfTypeCancellationExternal, scheduledTimeNanos, sleepDuration) if err != nil { return err } runID := "" if useRunID { runID = workflowRun.GetRunID() } err = client.CancelWorkflow(context.Background(), wfTypeCancellationExternal, runID) if err != nil { return err } err = workflowRun.Get(ctx, nil) if err == nil { return errors.New("cancellationWorkflow failed: non child workflow not cancelled") } if _, ok := err.(*cadence.CanceledError); !ok { return err } return nil } if err := apiCancelTestFn(false); err != nil { return err } return apiCancelTestFn(true) } // cancellationChildActivity is the activity implementation to test for cancellation of non child workflow, using API func cancellationChildActivity(ctx context.Context, scheduledTimeNanos int64, execution workflow.Execution) error { scope := activity.GetMetricsScope(ctx) var err error scope, sw := recordActivityStart(scope, activityTypeCancellationChild, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) client := getActivityContext(ctx).cadence return client.CancelWorkflow(context.Background(), execution.ID, execution.RunID) } // cancellationExternalWorkflow is the workflow implementation to test for cancellation of non child workflow func cancellationExternalWorkflow(ctx workflow.Context, scheduledTimeNanos int64, sleepDuration time.Duration) error { profile, err := beginWorkflow(ctx, wfTypeCancellationExternal, scheduledTimeNanos) if err != nil { return profile.end(err) } err = workflow.Sleep(ctx, sleepDuration) if err != nil { return profile.end(err) } return nil } ================================================ FILE: canary/client.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "time" "github.com/opentracing/opentracing-go" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/client" "go.uber.org/cadence/workflow" ) // cadenceClient is an abstraction on top of // the cadence library client that serves as // a union of all the client interfaces that // the library exposes type cadenceClient struct { Domain string client.Client // domainClient only exposes domain API client.DomainClient // this is the service needed to start the workers Service workflowserviceclient.Interface } // createDomain creates a cadence domain with the given name and description // if the domain already exist, this method silently returns success func (client *cadenceClient) createDomain(name string, desc string, owner string, archivalStatus *shared.ArchivalStatus, isGlobalDomain bool, clusters []string) error { emitMetric := true retention := int32(workflowRetentionDays) if archivalStatus != nil && *archivalStatus == shared.ArchivalStatusEnabled { retention = int32(0) } var clusterCfg []*shared.ClusterReplicationConfiguration for i := range clusters { clusterCfg = append(clusterCfg, &shared.ClusterReplicationConfiguration{ ClusterName: &clusters[i], }) } req := &shared.RegisterDomainRequest{ Name: &name, Description: &desc, OwnerEmail: &owner, Clusters: clusterCfg, WorkflowExecutionRetentionPeriodInDays: &retention, EmitMetric: &emitMetric, HistoryArchivalStatus: archivalStatus, VisibilityArchivalStatus: archivalStatus, IsGlobalDomain: &isGlobalDomain, } err := client.Register(context.Background(), req) if err != nil { if _, ok := err.(*shared.DomainAlreadyExistsError); !ok { return err } } return nil } // newCadenceClient builds a cadenceClient from the runtimeContext func newCadenceClient(domain string, runtime *RuntimeContext) cadenceClient { tracer := opentracing.GlobalTracer() cclient := client.NewClient( runtime.service, domain, &client.Options{ MetricsScope: runtime.metrics, Tracer: tracer, }, ) domainClient := client.NewDomainClient( runtime.service, &client.Options{ MetricsScope: runtime.metrics, Tracer: tracer, }, ) return cadenceClient{ Domain: domain, Client: cclient, DomainClient: domainClient, Service: runtime.service, } } // newWorkflowOptions builds workflowOptions with defaults for everything except startToCloseTimeout func newWorkflowOptions(id string, executionTimeout time.Duration) client.StartWorkflowOptions { return client.StartWorkflowOptions{ ID: id, TaskList: taskListName, ExecutionStartToCloseTimeout: executionTimeout, DecisionTaskStartToCloseTimeout: decisionTaskTimeout, WorkflowIDReusePolicy: client.WorkflowIDReusePolicyAllowDuplicate, } } // newActivityOptions builds and returns activityOptions with reasonable defaults func newActivityOptions() workflow.ActivityOptions { return workflow.ActivityOptions{ TaskList: taskListName, StartToCloseTimeout: activityTaskTimeout, ScheduleToStartTimeout: scheduleToStartTimeout, ScheduleToCloseTimeout: scheduleToStartTimeout + activityTaskTimeout, } } // newChildWorkflowOptions builds and returns childWorkflowOptions for given domain func newChildWorkflowOptions(domain string, wfID string) workflow.ChildWorkflowOptions { return workflow.ChildWorkflowOptions{ Domain: domain, WorkflowID: wfID, TaskList: taskListName, ExecutionStartToCloseTimeout: childWorkflowTimeout, TaskStartToCloseTimeout: decisionTaskTimeout, WorkflowIDReusePolicy: client.WorkflowIDReusePolicyAllowDuplicate, } } // registerWorkflow registers a workflow function with a given friendly name func registerWorkflow(workflowFunc interface{}, name string) { opts := workflow.RegisterOptions{Name: name} workflow.RegisterWithOptions(workflowFunc, opts) } // registerActivity registers an activity function with a given friendly name func registerActivity(activityFunc interface{}, name string) { opts := activity.RegisterOptions{Name: name} activity.RegisterWithOptions(activityFunc, opts) } ================================================ FILE: canary/common.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "fmt" "time" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) func absDurationDiff(d1, d2 time.Duration) time.Duration { if d1 > d2 { return d1 - d2 } return d2 - d1 } func stringPtr(v string) *string { return &v } func int32Ptr(v int32) *int32 { return &v } // getContextValue retrieves and returns the value corresponding // to the given key - panics if the key does not exist func getContextValue(ctx context.Context, key contextKey) interface{} { value := ctx.Value(key) if value == nil { panic("ctx.Value(" + key + ") returned nil") } return value } // getActivityContext retrieves and returns the activity context from the // global context passed to the activity func getActivityContext(ctx context.Context) *activityContext { return getContextValue(ctx, ctxKeyActivityRuntime).(*activityContext) } // getActivityArchivalContext retrieves and returns the activity archival context from the // global context passed to the activity func getActivityArchivalContext(ctx context.Context) *activityContext { return getContextValue(ctx, ctxKeyActivityArchivalRuntime).(*activityContext) } // checkWFVersionCompatibility takes a workflow.Context param and // validates that the workflow task currently being handled // is compatible with this version of the canary - this method // MUST only be called within a workflow function and it MUST // be the first line in the workflow function // Returns an error if the version is incompatible func checkWFVersionCompatibility(ctx workflow.Context) error { version := workflow.GetVersion(ctx, workflowChangeID, workflowVersion, workflowVersion) if version != workflowVersion { workflow.GetLogger(ctx).Error("workflow version mismatch", zap.Int("want", int(workflowVersion)), zap.Int("got", int(version))) return fmt.Errorf("workflow version mismatch, want=%v, got=%v", workflowVersion, version) } return nil } // beginWorkflow executes the common steps involved in all the workflow functions // It checks for workflow task version compatibility and also records the execution // in m3. This function must be the first call in every workflow function // Returns metrics scope on success, error on failure func beginWorkflow(ctx workflow.Context, wfType string, scheduledTimeNanos int64) (*workflowMetricsProfile, error) { profile := recordWorkflowStart(ctx, wfType, scheduledTimeNanos) if err := checkWFVersionCompatibility(ctx); err != nil { profile.scope.Counter(errIncompatibleVersion).Inc(1) return nil, profile.end(err) } return profile, nil } func concat(first string, second string) string { return first + "/" + second } func getScheduledTimeFromInputIfNonZero(ctx workflow.Context, nanos int64) int64 { if nanos == 0 { return workflow.Now(ctx).UnixNano() } return nanos } ================================================ FILE: canary/concurrentExec.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) const ( // to force cuncurrent execution of activity and pagination on history events // we need to make this number larger than the default page size, which is 1000 numConcurrentExec = 25 totalConcurrentExec = 250 ) func init() { registerWorkflow(concurrentExecWorkflow, wfTypeConcurrentExec) registerActivity(concurrentExecActivity, activityTypeConcurrentExec) } // concurrentExecWorkflow is the workflow implementation to test // 1. client side events pagination when reconstructing workflow state // 2. concurrent execution of activities func concurrentExecWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) profile, err := beginWorkflow(ctx, wfTypeConcurrentExec, scheduledTimeNanos) if err != nil { return err } selector := workflow.NewSelector(ctx) errors := make([]error, totalConcurrentExec) doActivity := func(index int) { now := workflow.Now(ctx).UnixNano() activityCtx := workflow.WithActivityOptions(ctx, newActivityOptions()) future := workflow.ExecuteActivity(activityCtx, activityTypeConcurrentExec, now) selector.AddFuture(future, func(f workflow.Future) { // do not care about the return value errors[index] = f.Get(activityCtx, nil) }) } for index := 0; index < numConcurrentExec; index++ { doActivity(index) } for index := numConcurrentExec; index < totalConcurrentExec; index++ { selector.Select(ctx) doActivity(index) } for index := 0; index < numConcurrentExec; index++ { selector.Select(ctx) } for _, err := range errors { if err != nil { workflow.GetLogger(ctx).Info("concurrentExecActivity failed", zap.Error(err)) return profile.end(err) } } return profile.end(nil) } // concurrentExecActivity is the activity implementation for concurrent execution test func concurrentExecActivity(ctx context.Context, scheduledTimeNanos int64) error { scope := activity.GetMetricsScope(ctx) var err error scope, sw := recordActivityStart(scope, activityTypeConcurrentExec, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) // canary test do not require any actual work to be done, so just return return nil } ================================================ FILE: canary/config.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "errors" "time" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/zap" "github.com/uber/cadence/common/config" ) const ( // EnvKeyRoot the environment variable key for runtime root dir EnvKeyRoot = "CADENCE_CANARY_ROOT" // EnvKeyConfigDir the environment variable key for config dir EnvKeyConfigDir = "CADENCE_CANARY_CONFIG_DIR" // EnvKeyEnvironment is the environment variable key for environment EnvKeyEnvironment = "CADENCE_CANARY_ENVIRONMENT" // EnvKeyAvailabilityZone is the environment variable key for AZ EnvKeyAvailabilityZone = "CADENCE_CANARY_AVAILABILITY_ZONE" // EnvKeyMode is the environment variable key for Mode EnvKeyMode = "CADENCE_CANARY_MODE" ) const ( // CadenceServiceName is the default service name for cadence frontend CadenceServiceName = "cadence-frontend" // CanaryServiceName is the default service name for cadence canary CanaryServiceName = "cadence-canary" // CrossClusterCanaryModeFull is a canary testing mode which tests all permutations of // the cross-cluster/domain feature CrossClusterCanaryModeFull = "test-all" ) type ( // Config contains the configurable yaml // properties for the canary runtime Config struct { Canary Canary `yaml:"canary"` Cadence Cadence `yaml:"cadence"` Log config.Logger `yaml:"log"` Metrics config.Metrics `yaml:"metrics"` } // Canary contains the configuration for canary tests Canary struct { CrossClusterTestMode string `yaml:"crossClusterTestMode"` CanaryDomainClusters []string `yaml:"canaryDomainClusters"` // the clusters to set for each domain Domains []string `yaml:"domains"` Excludes []string `yaml:"excludes"` Cron Cron `yaml:"cron"` } // Cron contains configuration for the cron workflow for canary Cron struct { CronSchedule string `yaml:"cronSchedule"` // default to "@every 30s" CronExecutionTimeout time.Duration `yaml:"cronExecutionTimeout"` // default to 18 minutes StartJobTimeout time.Duration `yaml:"startJobTimeout"` // default to 9 minutes } // Cadence contains the configuration for cadence service Cadence struct { ServiceName string `yaml:"service"` // support Thrift for backward compatibility. It will be ignored if host (gRPC) is used. ThriftHostNameAndPort string `yaml:"host"` // gRPC host name and port GRPCHostNameAndPort string `yaml:"address"` // TLS cert file if TLS is enabled on the Cadence server TLSCAFile string `yaml:"tlsCaFile"` } ) // Validate validates canary configration func (c *Config) Validate() error { if len(c.Canary.Domains) == 0 { return errors.New("missing value for domains property") } return nil } // RuntimeContext contains all the context // information needed to run the canary type RuntimeContext struct { logger *zap.Logger metrics tally.Scope service workflowserviceclient.Interface } // NewRuntimeContext builds a runtime context from the config func NewRuntimeContext( logger *zap.Logger, scope tally.Scope, service workflowserviceclient.Interface, ) *RuntimeContext { return &RuntimeContext{ logger: logger, metrics: scope, service: service, } } ================================================ FILE: canary/const.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "time" "go.uber.org/cadence/workflow" ) // global constants const ( workflowRetentionDays = int32(1) activityWorkerMaxExecutors = 256 scheduleToStartTimeout = 3 * time.Minute decisionTaskTimeout = 10 * time.Second activityTaskTimeout = 3 * time.Minute childWorkflowTimeout = 6 * time.Minute taskListName = "canary-task-queue" crossClusterSrcTasklist = "cross-cluster-src-tasklist" crossClusterDestTasklist = "cross-cluster-dest-tasklist" ctxKeyActivityRuntime = contextKey("runtime") ctxKeyActivityArchivalRuntime = contextKey("runtime-archival") ctxKeyActivitySystemClient = contextKey("system-client") ctxKeyActivityBatcherClient = contextKey("batcher-client") ctxKeyConfig = contextKey("runtime-config") archivalDomain = "canary-archival-domain" archivalTaskListName = "canary-archival-task-queue" ) // canary running modes const ( ModeAll = "all" ModeWorker = "worker" ModeCronCanary = "cronCanary" ) // workflowVersion represents the current version of every single // workflow function in this canary. Every workflow function verifies // that the decision task it is executing is compatible with this version // Bump this version whenever a backward incompatible change for any workflow // also see beingWorkflow function const workflowVersion = workflow.Version(3) const workflowChangeID = "initial version" // wfType/activityType refers to the friendly short names given to // workflows and activities - at the time of registration, these names // will be used to associate with a workflow or activity function const ( wfTypeCron = "workflow.cron" wfTypeSanity = "workflow.sanity" wfTypeEcho = "workflow.echo" wfTypeSignal = "workflow.signal" wfTypeSignalExternal = "workflow.signal.external" wfTypeVisibility = "workflow.visibility" wfTypeSearchAttributes = "workflow.searchAttributes" wfTypeConcurrentExec = "workflow.concurrent-execution" wfTypeQuery = "workflow.query" wfTypeTimeout = "workflow.timeout" wfTypeLocalActivity = "workflow.localactivity" wfTypeCancellation = "workflow.cancellation" wfTypeCancellationExternal = "workflow.cancellation.external" wfTypeRetry = "workflow.retry" wfTypeResetBase = "workflow.reset.base" wfTypeReset = "workflow.reset" wfTypeHistoryArchival = "workflow.archival.history" wfTypeVisibilityArchival = "workflow.archival.visibility" wfTypeArchivalExternal = "workflow.archival.external" wfTypeBatch = "workflow.batch" wfTypeCrossClusterParent = "workflow.CrossCluster.parent" wfTypeCrossClusterChild = "workflow.CrossCluster.child" wfTypeBatchParent = "workflow.batch.parent" wfTypeBatchChild = "workflow.batch.child" activityTypeEcho = "activity.echo" activityTypeCron = "activity.cron" activityTypeSignal = "activity.signal" activityTypeVisibility = "activity.visibility" activityTypeSearchAttributes = "activity.searchAttributes" activityTypeConcurrentExec = "activity.concurrent-execution" activityTypeQuery1 = "activity.query1" activityTypeQuery2 = "activity.query2" activityTypeTimeout = "activity.timeout" activityTypeCancellation = "activity.cancellation" activityTypeCancellationChild = "activity.cancellation.child" activityTypeRetryOnTimeout = "activity.retry-on-timeout" activityTypeRetryOnFailure = "activity.retry-on-failure" activityTypeTriggerReset = "activity.reset.trigger" activityTypeVerifyReset = "activity.reset.verify" activityTypeResetBase = "activity.reset.base" activityTypeHistoryArchival = "activity.archival.history" activityTypeVisibilityArchival = "activity.archival.visibility" activityTypeLargeResult = "activity.largeResult" activityTypeVerifyBatch = "activity.batch.verify" activityTypeStartBatch = "activity.batch.start.batch" activityTypeCrossCluster = "activity.crosscluster.sample" activityTypeCrossClusterFailover = "activity.crosscluster.failover" ) ================================================ FILE: canary/cron.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "fmt" "time" "github.com/opentracing/opentracing-go" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) var ( cronSleepTime = time.Second * 5 ) func init() { workflow.RegisterWithOptions(cronWorkflow, workflow.RegisterOptions{Name: wfTypeCron}) activity.Register(cronActivity) } // cronWorkflow is a generic cron workflow implementation that takes as input // a tuple - (jobName, JobFrequency) where jobName refers to a workflow name // and job frequency refers to the rate at which the job must be scheduled // there are several assumptions that this cron makes: // - jobFrequency is any value between 1-59 seconds // - jobName refers to a workflow function with a well-defined function signature // - every instance of job completes within 10 mins func cronWorkflow( ctx workflow.Context, jobName string) error { domain := workflow.GetInfo(ctx).Domain profile, err := beginWorkflow(ctx, wfTypeCron, workflow.Now(ctx).UnixNano()) aCtx := workflow.WithActivityOptions(ctx, newActivityOptions()) startTime := workflow.Now(ctx).UnixNano() workflow.ExecuteActivity(aCtx, cronActivity, startTime, domain, jobName) workflow.Sleep(ctx, cronSleepTime) elapsed := time.Duration(workflow.Now(ctx).UnixNano() - startTime) profile.scope.Timer(timerDriftLatency).Record(absDurationDiff(elapsed, cronSleepTime)) return err } // cronActivity starts root canary workflows at a pre-defined frequency // this activity exits automatically a minute after it is scheduled func cronActivity( ctx context.Context, scheduledTimeNanos int64, domain string, jobName string) error { scope := activity.GetMetricsScope(ctx) var err error scope, sw := recordActivityStart(scope, activityTypeCron, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) cadenceClient := getActivityContext(ctx).cadence logger := activity.GetLogger(ctx) parentID := activity.GetInfo(ctx).WorkflowExecution.ID jobID := fmt.Sprintf("%s-%s-%v", parentID, jobName, time.Now().Format(time.RFC3339)) config := getContextValue(ctx, ctxKeyConfig).(*Canary) wf, err := startJob(&cadenceClient, scope, jobID, jobName, domain, config) if err != nil { logger.Error("cronActivity: failed to start job", zap.Error(err)) if isDomainNotActiveErr(err) { return err } } else { logger.Info("cronActivity: started new job", zap.String("wfID", jobID), zap.String("runID", wf.RunID)) } return nil } func startJob( cadenceClient *cadenceClient, scope tally.Scope, jobID string, jobName string, domain string, config *Canary) (*workflow.Execution, error) { scope.Counter(startWorkflowCount).Inc(1) sw := scope.Timer(startWorkflowLatency).Start() defer sw.Stop() // start off a workflow span ctx := context.Background() span := opentracing.StartSpan(fmt.Sprintf("start-%v", jobName)) defer span.Finish() ctx = opentracing.ContextWithSpan(ctx, span) opts := newWorkflowOptions(jobID, config.Cron.StartJobTimeout) wf, err := cadenceClient.StartWorkflow(ctx, opts, jobName, time.Now().UnixNano(), domain) if err != nil { scope.Counter(startWorkflowFailureCount).Inc(1) switch err.(type) { case *shared.WorkflowExecutionAlreadyStartedError: scope.Counter(startWorkflowAlreadyStartedCount).Inc(1) case *shared.DomainNotActiveError: scope.Counter(startWorkflowDomainNotActiveCount).Inc(1) } return nil, err } scope.Counter(startWorkflowSuccessCount).Inc(1) return wf, err } func isDomainNotActiveErr(err error) bool { _, ok := err.(*shared.DomainNotActiveError) return ok } ================================================ FILE: canary/crosscluster.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "fmt" "time" "github.com/google/uuid" shared "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) // some sample data to be passed around type Data struct { Val string Iterations int } func (c canaryImpl) crossClusterParentWf(ctx workflow.Context) error { // first try launching a child workflow in another cluster, active in another region logger := workflow.GetLogger(ctx) logger.Info("starting child workflow in domain1, cluster 1") ctx1 := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{ Domain: c.getCrossClusterTargetDomain(), WorkflowID: "wf-domain-1-" + uuid.New().String(), TaskList: crossClusterDestTasklist, ExecutionStartToCloseTimeout: 1 * time.Minute, }) err := workflow.ExecuteChildWorkflow(ctx1, wfTypeCrossClusterChild, Data{Val: "test"}).Get(ctx1, nil) if err != nil { logger.Error("got error executing child workflow", zap.Error(err)) return err } logger.Info("test success - Cross-cluster cross-domain workflow completed", zap.Any("return-value", nil)) ao := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: 4 * time.Minute, } ctx = workflow.WithActivityOptions(ctx, ao) err = workflow.ExecuteActivity(ctx, c.failoverDestDomainActivity).Get(ctx, nil) if err != nil { logger.Error("error during cross-cluster failover", zap.Error(err)) return err } return nil } func (c canaryImpl) crossClusterChildWf(ctx workflow.Context, args Data) error { if args.Val != "test" { panic("wf1 did not receive expected args") } ao := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: 4 * time.Minute, } ctx = workflow.WithActivityOptions(ctx, ao) logger := workflow.GetLogger(ctx) logger.Info("workflow child wf starting activity.") err := workflow.ExecuteActivity(ctx, activityTypeCrossCluster).Get(ctx, nil) if err != nil { logger.Error("activity error", zap.Error(err)) } // make the workflow do a loop if args.Iterations == 0 { logger.Info("continuing as new") return workflow.NewContinueAsNewError(ctx, c.crossClusterChildWf, Data{ Val: args.Val, Iterations: args.Iterations + 1, }) } logger.Info("workflow child wf completed.") return nil } func (c canaryImpl) crossClusterSampleActivity(ctx context.Context) (string, error) { logger := activity.GetLogger(ctx) logger.Info("activity 1 - running") return "Hello - activity 1", nil } // fails over the Dest domain so that both types of cross cluster operations can be tested func (c canaryImpl) failoverDestDomainActivity(ctx context.Context) error { logger := activity.GetLogger(ctx) srcReplicationInfo, err := getReplicationInfo(ctx, c.canaryClient, c.canaryDomain) fmt.Println("Src replication info", srcReplicationInfo) if err != nil { return err } destReplicationInfo, err := getReplicationInfo(ctx, *c.crossClusterDestClient, c.getCrossClusterTargetDomain()) fmt.Println("dest replication info", srcReplicationInfo) if err != nil { return err } srcActiveCluster := srcReplicationInfo.GetActiveClusterName() destActiveCluster := destReplicationInfo.GetActiveClusterName() currentlyInSameCluster := srcActiveCluster == destActiveCluster // don't do this every iteration so as to not get rate-limited smallProbability := time.Now().Second() <= 10 targetDomain := c.getCrossClusterTargetDomain() if currentlyInSameCluster && smallProbability { // do a failover to move the dest to a different cluster // thereby making the next iteration of the canary be cross-cluster AND cross domain // this operation is probably nondeterministic, but that's ok in this instance for _, cluster := range destReplicationInfo.GetClusters() { if cluster.GetClusterName() != srcActiveCluster { err = c.crossClusterDestClient.DomainClient.Update(ctx, &shared.UpdateDomainRequest{ Name: &targetDomain, ReplicationConfiguration: &shared.DomainReplicationConfiguration{ActiveClusterName: cluster.ClusterName}, }) if err != nil { return fmt.Errorf("failed to update destination domain for failover in crossdomain canary test: cluster trying to move dest domain to %s, error: %w ", cluster.GetClusterName(), err) } logger.Info("failed over cross-cluster domain to ", zap.String("cluster", cluster.GetClusterName())) return nil } } } else if smallProbability { // else they're not currently in the same cluster, so fail the destination domain back to the same cluster // so the next iteration of the cross cluster canary test will be just cross domain err = c.crossClusterDestClient.DomainClient.Update(ctx, &shared.UpdateDomainRequest{ ReplicationConfiguration: &shared.DomainReplicationConfiguration{ActiveClusterName: &srcActiveCluster}, Name: &targetDomain, }) if err != nil { return fmt.Errorf("failed to update destination domain for failover in crossdomain canary test - in this instance the dest back to the same cluster as the source: %w", err) } logger.Info("failed over cross-cluster domain to ", zap.String("cluster", srcActiveCluster)) } return nil } func getReplicationInfo(ctx context.Context, client cadenceClient, domain string) (*shared.DomainReplicationConfiguration, error) { res, err := client.DomainClient.Describe(ctx, domain) if err != nil { return nil, err } return res.GetReplicationConfiguration(), nil } ================================================ FILE: canary/echo.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "errors" "math" "reflect" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) // echoInput is the input to echoActivity type echoInput struct { IntVal int64 IntPtrVal *int64 FloatVal float64 StringVal string StringPtrVal *string SliceVal []string MapVal map[string]string } // echoOutput is the output from echoActivity type echoOutput echoInput func init() { registerWorkflow(echoWorkflow, wfTypeEcho) registerActivity(echoActivity, activityTypeEcho) } // echoWorkflow is a workflow implementation which simply executes an // activity that echoes back the input as output func echoWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) profile, err := beginWorkflow(ctx, wfTypeEcho, scheduledTimeNanos) if err != nil { return err } input := newEchoInput() now := workflow.Now(ctx).UnixNano() aCtx := workflow.WithActivityOptions(ctx, newActivityOptions()) future := workflow.ExecuteActivity(aCtx, activityTypeEcho, now, input) var output echoOutput if err := future.Get(aCtx, &output); err != nil { workflow.GetLogger(ctx).Info("echoActivity failed", zap.Error(err)) return profile.end(err) } if !reflect.DeepEqual(output, echoOutput(*input)) { workflow.GetLogger(ctx).Sugar().Error("echoActivity returned wrong output", output, input) return profile.end(errors.New("echoActivity returned invalid output")) } return profile.end(nil) } // echoActivity is an activity implementation that simply returns the input as output // it also validates that the passed in input has the exepected values func echoActivity(ctx context.Context, scheduledTimeNanos int64, input *echoInput) (echoOutput, error) { scope := activity.GetMetricsScope(ctx) var err error scope, sw := recordActivityStart(scope, activityTypeEcho, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) expected := newEchoInput() if !reflect.DeepEqual(expected, input) { err = errors.New("equality check failed") return echoOutput{}, err } return echoOutput(*input), nil } func newEchoInput() *echoInput { intVal := int64(math.MaxInt64) stringVal := "canary_echo_test" return &echoInput{ IntVal: intVal, IntPtrVal: &intVal, FloatVal: math.MaxFloat64, StringVal: stringVal, StringPtrVal: &stringVal, SliceVal: []string{"canary", ".", "echoWorkflow"}, MapVal: map[string]string{"us-east-1": "dca1a", "us-west-1": "sjc1a"}, } } ================================================ FILE: canary/historyArchival.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "errors" "fmt" "math/rand" "time" "github.com/pborman/uuid" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) const ( numHistoryArchivals = 5 resultSize = 1024 // 1KB ) func init() { registerWorkflow(historyArchivalWorkflow, wfTypeHistoryArchival) registerActivity(historyArchivalActivity, activityTypeHistoryArchival) registerWorkflow(archivalExternalWorkflow, wfTypeArchivalExternal) registerActivity(largeResultActivity, activityTypeLargeResult) } func historyArchivalWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) profile, err := beginWorkflow(ctx, wfTypeHistoryArchival, scheduledTimeNanos) if err != nil { return err } ch := workflow.NewBufferedChannel(ctx, numHistoryArchivals) for i := 0; i < numHistoryArchivals; i++ { workflow.Go(ctx, func(ctx2 workflow.Context) { aCtx := workflow.WithActivityOptions(ctx2, newActivityOptions()) err := workflow.ExecuteActivity(aCtx, activityTypeHistoryArchival, workflow.Now(ctx2).UnixNano()).Get(aCtx, nil) errStr := "" if err != nil { errStr = err.Error() } ch.Send(ctx2, errStr) }) } successfulArchivalsCount := 0 for i := 0; i < numHistoryArchivals; i++ { var errStr string ch.Receive(ctx, &errStr) if errStr != "" { workflow.GetLogger(ctx).Error("at least one archival failed", zap.Int("success-count", successfulArchivalsCount), zap.String("err-string", errStr)) return profile.end(errors.New(errStr)) } successfulArchivalsCount++ } return profile.end(nil) } func historyArchivalActivity(ctx context.Context, scheduledTimeNanos int64) error { scope := activity.GetMetricsScope(ctx) var err error scope, sw := recordActivityStart(scope, activityTypeHistoryArchival, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) client := getActivityArchivalContext(ctx).cadence execution, err := executeArchivalExeternalWorkflow(ctx, client, scheduledTimeNanos) if err != nil { return err } getHistoryReq := &shared.GetWorkflowExecutionHistoryRequest{ Domain: stringPtr(archivalDomain), Execution: execution, } failureReason := "" attempts := 0 expireTime := time.Now().Add(activityTaskTimeout) for { <-time.After(5 * time.Second) if time.Now().After(expireTime) { break } attempts++ bCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) history, err := client.Service.GetWorkflowExecutionHistory(bCtx, getHistoryReq) cancel() if err != nil { failureReason = fmt.Sprintf("error accessing history, %v", err.Error()) } else if !history.GetArchived() { failureReason = "history is not archived" } else if len(history.History.Events) == 0 { failureReason = "got empty history" } else { return nil } } activity.GetLogger(ctx).Error("failed to get archived history within time limit", zap.String("failure_reason", failureReason), zap.String("domain", archivalDomain), zap.String("workflow_id", execution.GetWorkflowId()), zap.String("run_id", execution.GetRunId()), zap.Int("attempts", attempts)) return fmt.Errorf("failed to get archived history within time limit, %v", failureReason) } func executeArchivalExeternalWorkflow( ctx context.Context, client cadenceClient, scheduledTimeNanos int64, ) (*shared.WorkflowExecution, error) { workflowID := fmt.Sprintf("%v.%v", wfTypeArchivalExternal, uuid.New()) ops := newWorkflowOptions(workflowID, childWorkflowTimeout) ops.TaskList = archivalTaskListName workflowRun, err := client.ExecuteWorkflow(ctx, ops, wfTypeArchivalExternal, scheduledTimeNanos) if err != nil { return nil, err } if err := workflowRun.Get(ctx, nil); err != nil { return nil, err } return &shared.WorkflowExecution{ WorkflowId: stringPtr(workflowID), RunId: stringPtr(workflowRun.GetRunID()), }, nil } func archivalExternalWorkflow(ctx workflow.Context, scheduledTimeNanos int64) error { profile, err := beginWorkflow(ctx, wfTypeArchivalExternal, scheduledTimeNanos) if err != nil { return err } ao := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: time.Minute, } var result []byte numActs := rand.Intn(10) + 1 for i := 0; i != numActs; i++ { aCtx := workflow.WithActivityOptions(ctx, ao) if err = workflow.ExecuteActivity(aCtx, activityTypeLargeResult).Get(aCtx, &result); err != nil { break } } if err != nil { return profile.end(err) } return profile.end(nil) } func largeResultActivity() ([]byte, error) { return make([]byte, resultSize), nil } ================================================ FILE: canary/localactivity.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "math/rand" "time" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" ) func init() { registerWorkflow(localActivityWorkfow, wfTypeLocalActivity) activity.Register(activityForCondition0) activity.Register(activityForCondition1) activity.Register(activityForCondition2) activity.Register(activityForCondition3) activity.Register(activityForCondition4) } type conditionAndAction struct { // condition is a function pointer to a local activity condition interface{} // action is a function pointer to a regular activity action interface{} } var checks = []conditionAndAction{ {checkCondition0, activityForCondition0}, {checkCondition1, activityForCondition1}, {checkCondition2, activityForCondition2}, {checkCondition3, activityForCondition3}, {checkCondition4, activityForCondition4}, } func localActivityWorkfow(ctx workflow.Context, inputScheduledTimeNanos int64) (string, error) { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) profile, err := beginWorkflow(ctx, wfTypeSignal, scheduledTimeNanos) if err != nil { return "", err } logger := workflow.GetLogger(ctx) lao := workflow.LocalActivityOptions{ // use short timeout as local activity is execute as function locally. ScheduleToCloseTimeout: time.Second, } ctx = workflow.WithLocalActivityOptions(ctx, lao) var data int32 err = workflow.ExecuteLocalActivity(ctx, getConditionData).Get(ctx, &data) if err != nil { return "", profile.end(err) } logger.Sugar().Infof("Get condition data %v", data) ao := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: time.Minute, } ctx = workflow.WithActivityOptions(ctx, ao) var actionFutures []workflow.Future for i, check := range checks { var conditionMeet bool err := workflow.ExecuteLocalActivity(ctx, check.condition, data).Get(ctx, &conditionMeet) if err != nil { return "", profile.end(err) } logger.Sugar().Infof("condition meet for %v: %v", i, conditionMeet) if conditionMeet { f := workflow.ExecuteActivity(ctx, check.action) actionFutures = append(actionFutures, f) } } var processResult string for _, f := range actionFutures { var actionResult string if err := f.Get(ctx, &actionResult); err != nil { return "", profile.end(err) } if len(processResult) > 0 { processResult += " and " } processResult += actionResult } logger.Sugar().Infof("Processed condition %v: %v", data, processResult) return processResult, profile.end(nil) } func getConditionData() (int32, error) { return rand.Int31n(100), nil } func checkCondition0(ctx context.Context, data int32) (bool, error) { return data < 10, nil } func checkCondition1(ctx context.Context, data int32) (bool, error) { return data >= 90, nil } func checkCondition2(ctx context.Context, data int32) (bool, error) { return data%2 == 0, nil } func checkCondition3(ctx context.Context, data int32) (bool, error) { return data%3 == 0, nil } func checkCondition4(ctx context.Context, data int32) (bool, error) { return data%5 == 0, nil } func activityForCondition0(ctx context.Context) (string, error) { activity.GetLogger(ctx).Info("process for condition 0") return "data < 10", nil } func activityForCondition1(ctx context.Context) (string, error) { activity.GetLogger(ctx).Info("process for condition 1") return "data >= 90", nil } func activityForCondition2(ctx context.Context) (string, error) { activity.GetLogger(ctx).Info("process for condition 2") return "data%2 == 0", nil } func activityForCondition3(ctx context.Context) (string, error) { activity.GetLogger(ctx).Info("process for condition 3") return "data%3 == 0", nil } func activityForCondition4(ctx context.Context) (string, error) { activity.GetLogger(ctx).Info("process for condition 4") return "data%5 == 0", nil } ================================================ FILE: canary/metrics.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "time" "github.com/uber-go/tally" "go.uber.org/cadence/workflow" ) // counters go here const ( startedCount = "started" failedCount = "failed" successCount = "succeeded" startWorkflowCount = "startworkflow" startWorkflowSuccessCount = "startworkflow.success" startWorkflowFailureCount = "startworkflow.failures" startWorkflowAlreadyStartedCount = "startworkflow.failures.alreadystarted" startWorkflowDomainNotActiveCount = "startworkflow.failures.domainnotactive" listOpenWorkflowsCount = "list-open-workflows" listOpenWorkflowsFailureCount = "list-open-workflows.failures" listWorkflowsCount = "list-workflows" listWorkflowsFailureCount = "list-workflows.failures" listArchivedWorkflowCount = "list-archived-workflows" listArchivedWorkflowFailureCount = "list-archived-workflows.failures" getWorkflowHistoryCount = "get-workflow-history" getWorkflowHistoryFailureCount = "get-workflow-history.failures" errTimeoutCount = "errors.timeout" errIncompatibleVersion = "errors.incompatibleversion" ) // latency metrics go here const ( latency = "latency" startLatency = "latency.schedule-to-start" startWorkflowLatency = "latency.startworkflow" listOpenWorkflowsLatency = "latency.list-open-workflows" listWorkflowsLatency = "latency.list-workflows" listArchivedWorkflowsLatency = "latency.list-archived-workflows" getWorkflowHistoryLatency = "latency.get-workflow-history" timerDriftLatency = "latency.timer-drift" ) // workflowMetricsProfile is the state that's needed to // record success/failed and latency metrics at the end // of a workflow type workflowMetricsProfile struct { ctx workflow.Context startTimestamp int64 scope tally.Scope } // workflowMetricScope creates and returns a child metric scope with tags // that identify the current workflow type func workflowMetricScope(ctx workflow.Context, wfType string) tally.Scope { parent := workflow.GetMetricsScope(ctx) return parent.Tagged(map[string]string{"operation": wfType}) } // recordWorkflowStart emits metrics at the beginning of a workflow function func recordWorkflowStart(ctx workflow.Context, wfType string, scheduledTimeNanos int64) *workflowMetricsProfile { now := workflow.Now(ctx).UnixNano() scope := workflowMetricScope(ctx, wfType) elapsed := max(0, now-scheduledTimeNanos) scope.Timer(startLatency).Record(time.Duration(elapsed)) scope.Counter(startedCount).Inc(1) return &workflowMetricsProfile{ ctx: ctx, startTimestamp: now, scope: scope, } } // recordWorkflowEnd emits metrics at the end of a workflow function func recordWorkflowEnd(scope tally.Scope, elapsed time.Duration, err error) error { scope.Timer(latency).Record(elapsed) if err == nil { scope.Counter(successCount).Inc(1) return err } scope.Counter(failedCount).Inc(1) if _, ok := err.(*workflow.TimeoutError); ok { scope.Counter(errTimeoutCount).Inc(1) } return err } // end records the elapsed time and reports the latency, // success, failed counts to m3 func (profile *workflowMetricsProfile) end(err error) error { now := workflow.Now(profile.ctx).UnixNano() elapsed := time.Duration(now - profile.startTimestamp) return recordWorkflowEnd(profile.scope, elapsed, err) } // recordActivityStart emits metrics at the beginning of an activity function func recordActivityStart( scope tally.Scope, activityType string, scheduledTimeNanos int64) (tally.Scope, tally.Stopwatch) { scope = scope.Tagged(map[string]string{"operation": activityType}) elapsed := max(0, time.Now().UnixNano()-scheduledTimeNanos) scope.Timer(startLatency).Record(time.Duration(elapsed)) scope.Counter(startedCount).Inc(1) sw := scope.Timer(latency).Start() return scope, sw } // recordActivityEnd emits metrics at the end of an activity function func recordActivityEnd(scope tally.Scope, sw tally.Stopwatch, err error) { sw.Stop() if err != nil { scope.Counter(failedCount).Inc(1) return } scope.Counter(successCount).Inc(1) } ================================================ FILE: canary/query.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "fmt" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) const ( queryType = "QueryWorkflow.QueryType" queryStatusStarted = "QueryWorkflow.Status.Started" queryStatusCloseToFinished = "QueryWorkflow.Status.CloseToFinished" queryStatusQueryHandlerFailed = "QueryWorkflow.Status.QueryHandlerFailed" ) func init() { registerWorkflow(queryWorkflow, wfTypeQuery) registerActivity(queryActivity, activityTypeQuery1) registerActivity(queryActivity, activityTypeQuery2) } // queryWorkflow is the workflow implementation to test for querying workflow status func queryWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) status := queryStatusStarted err := workflow.SetQueryHandler(ctx, queryType, func() (string, error) { return status, nil }) if err != nil { status = queryStatusQueryHandlerFailed return err } profile, err := beginWorkflow(ctx, wfTypeQuery, scheduledTimeNanos) if err != nil { return err } execution := workflow.GetInfo(ctx).WorkflowExecution var queryStatus string now := workflow.Now(ctx).UnixNano() activityCtx := workflow.WithActivityOptions(ctx, newActivityOptions()) err = workflow.ExecuteActivity(activityCtx, activityTypeQuery1, now, execution).Get(activityCtx, &queryStatus) if err != nil { workflow.GetLogger(ctx).Info("queryWorkflow failed", zap.Error(err)) return profile.end(err) } if queryStatus != status { err := fmt.Errorf("queryWorkflow status expected %s, but is %s", status, queryStatus) workflow.GetLogger(ctx).Info("queryWorkflow failed, status is not expected", zap.Error(err)) return profile.end(err) } status = queryStatusCloseToFinished activityCtx = workflow.WithActivityOptions(ctx, newActivityOptions()) err = workflow.ExecuteActivity(activityCtx, activityTypeQuery2, now, execution).Get(activityCtx, &queryStatus) if err != nil { workflow.GetLogger(ctx).Info("queryWorkflow failed", zap.Error(err)) return profile.end(err) } if queryStatus != status { err := fmt.Errorf("queryWorkflow status expected %s, but is %s", status, queryStatus) workflow.GetLogger(ctx).Info("queryWorkflow failed, status is not expected", zap.Error(err)) return profile.end(err) } return profile.end(nil) } // queryActivity is the activity implementation for querying workflow status func queryActivity(ctx context.Context, scheduledTimeNanos int64, execution workflow.Execution) (string, error) { scope := activity.GetMetricsScope(ctx) var err error scope, sw := recordActivityStart(scope, activityTypeConcurrentExec, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) client := getActivityContext(ctx).cadence encodedValue, err := client.QueryWorkflow(context.Background(), execution.ID, execution.RunID, queryType) if err != nil { return "", err } var result string err = encodedValue.Get(&result) return result, err } ================================================ FILE: canary/reset.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "fmt" "time" "go.uber.org/cadence" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" ) const ( smallWait = 1 bigWait = 30 signalToTrigger = "signalToTrigger" signalBeforeReset = "signalBeforeReset" signalAfterReset = "signalAfterReset" ) func init() { registerWorkflow(resetWorkflow, wfTypeReset) registerWorkflow(resetBaseWorkflow, wfTypeResetBase) registerActivity(triggerResetActivity, activityTypeTriggerReset) registerActivity(verifyResetActivity, activityTypeVerifyReset) registerActivity(resetBaseActivity, activityTypeResetBase) } func resetWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) domain := workflow.GetInfo(ctx).Domain profile, err := beginWorkflow(ctx, wfTypeReset, scheduledTimeNanos) if err != nil { return err } info := workflow.GetInfo(ctx) cwo := newChildWorkflowOptions(domain, wfTypeResetBase+"-"+info.WorkflowExecution.RunID) baseCtx := workflow.WithChildOptions(ctx, cwo) baseFuture := workflow.ExecuteChildWorkflow(baseCtx, wfTypeResetBase, workflow.Now(ctx).UnixNano(), info.WorkflowExecution.ID, info.WorkflowExecution.RunID, domain) var baseWE workflow.Execution if err := baseFuture.GetChildWorkflowExecution().Get(baseCtx, &baseWE); err != nil { return profile.end(err) } signalFuture1 := baseFuture.SignalChildWorkflow(baseCtx, signalBeforeReset, "signalValue") err = signalFuture1.Get(baseCtx, nil) if err != nil { return profile.end(err) } // use signal to wait for baseWF to get reach reset point var value string signalCh := workflow.GetSignalChannel(ctx, signalToTrigger) signalCh.Receive(ctx, &value) signalFuture2 := baseFuture.SignalChildWorkflow(baseCtx, signalAfterReset, "signalValue") err = signalFuture2.Get(baseCtx, nil) if err != nil { return profile.end(err) } expiration := time.Duration(info.ExecutionStartToCloseTimeoutSeconds) * time.Second activityCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ TaskList: taskListName, ScheduleToStartTimeout: expiration, StartToCloseTimeout: expiration, }) var newWE workflow.Execution err = workflow.ExecuteActivity(activityCtx, activityTypeTriggerReset, domain, baseWE).Get(activityCtx, &newWE) if err != nil { return profile.end(err) } if err := workflow.Sleep(ctx, time.Duration(bigWait*2)*time.Second); err != nil { return profile.end(err) } err = workflow.ExecuteActivity(activityCtx, activityTypeVerifyReset, domain, newWE).Get(activityCtx, nil) return profile.end(err) } func resetBaseWorkflow(ctx workflow.Context, scheduledTimeNanos int64, parentID, parentRunID, domain string) error { profile, err := beginWorkflow(ctx, wfTypeResetBase, scheduledTimeNanos) if err != nil { return err } info := workflow.GetInfo(ctx) expiration := time.Duration(info.ExecutionStartToCloseTimeoutSeconds) * time.Second retryPolicy := &cadence.RetryPolicy{ InitialInterval: time.Second * 5, BackoffCoefficient: 1, MaximumInterval: time.Second * 5, ExpirationInterval: expiration, MaximumAttempts: 5, } activityCtxWithRetry := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ TaskList: taskListName, ScheduleToStartTimeout: expiration, StartToCloseTimeout: expiration, RetryPolicy: retryPolicy, }) activityCtxNoRetry := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ TaskList: taskListName, ScheduleToStartTimeout: expiration, StartToCloseTimeout: expiration, }) var f workflow.Future futures1 := []workflow.Future{} futures2 := []workflow.Future{} // completed before reset f = workflow.ExecuteActivity(activityCtxWithRetry, activityTypeResetBase, smallWait) futures1 = append(futures1, f) // started before reset f = workflow.ExecuteActivity(activityCtxNoRetry, activityTypeResetBase, bigWait) futures2 = append(futures2, f) // not started before reset(because it uses retry) f = workflow.ExecuteActivity(activityCtxWithRetry, activityTypeResetBase, bigWait) futures2 = append(futures2, f) // fired before reset f = workflow.NewTimer(ctx, time.Duration(smallWait*2)*time.Second) futures1 = append(futures1, f) // fired after reset f = workflow.NewTimer(ctx, time.Duration(bigWait)*time.Second) futures2 = append(futures2, f) // wait until first set of futures1 are done: 1 act, 1 timer for _, future := range futures1 { if err := future.Get(ctx, nil); err != nil { return profile.end(err) } } signalFuture := workflow.SignalExternalWorkflow(ctx, parentID, parentRunID, signalToTrigger, "signalValue") err = signalFuture.Get(ctx, nil) if err != nil { return profile.end(err) } var value string signalCh := workflow.GetSignalChannel(ctx, signalBeforeReset) signalCh.Receive(ctx, &value) signalCh = workflow.GetSignalChannel(ctx, signalAfterReset) signalCh.Receive(ctx, &value) return profile.end(err) } func triggerResetActivity(ctx context.Context, domain string, baseWE workflow.Execution) (workflow.Execution, error) { svClient := getActivityContext(ctx).cadence.Service reason := "reset canary" client := getActivityContext(ctx).cadence scope := activity.GetMetricsScope(ctx) resetEventID := int64(0) seenTrigger := false // reset to last decisionCompleted before signalToTrigger was sent // Since we are in the trigger activity, baseWF must have reached there events, err := getMyHistory(client, baseWE, scope) if err != nil { return workflow.Execution{}, err } for _, event := range events { if event.GetEventType() == shared.EventTypeDecisionTaskCompleted { resetEventID = event.GetEventId() } if event.GetEventType() == shared.EventTypeSignalExternalWorkflowExecutionInitiated { seenTrigger = true break } } if resetEventID == 0 || !seenTrigger { return workflow.Execution{}, fmt.Errorf("something went wrong...base workflow has not reach reset point, %v, %v", resetEventID, seenTrigger) } req := &shared.ResetWorkflowExecutionRequest{ Domain: &domain, WorkflowExecution: &shared.WorkflowExecution{ WorkflowId: &baseWE.ID, RunId: &baseWE.RunID, }, Reason: &reason, DecisionFinishEventId: &resetEventID, RequestId: &baseWE.RunID, } resp, err := svClient.ResetWorkflowExecution(ctx, req) if err != nil { return workflow.Execution{}, err } baseWE.RunID = *resp.RunId return baseWE, nil } func verifyResetActivity(ctx context.Context, domain string, newWE workflow.Execution) error { svClient := getActivityContext(ctx).cadence.Service resp, err := svClient.DescribeWorkflowExecution(ctx, &shared.DescribeWorkflowExecutionRequest{ Domain: &domain, Execution: &shared.WorkflowExecution{ WorkflowId: &newWE.ID, RunId: &newWE.RunID, }, }) if err != nil { return err } if resp.WorkflowExecutionInfo.CloseStatus == nil || resp.WorkflowExecutionInfo.GetCloseStatus() != shared.WorkflowExecutionCloseStatusCompleted { return fmt.Errorf("new execution triggered by reset is not completed") } return nil } func resetBaseActivity(ctx context.Context, waitSecs int64) error { time.Sleep(time.Second * time.Duration(waitSecs)) return nil } ================================================ FILE: canary/retry.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "errors" "time" "go.uber.org/cadence" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) var ( errRetryableActivityError = errors.New("retry me") errUnexpectedProgress = errors.New("unexpected progress") errUnexpectedResult = errors.New("unexpected result") ) func init() { registerWorkflow(retryWorkflow, wfTypeRetry) registerActivity(retryOnTimeoutActivity, activityTypeRetryOnTimeout) registerActivity(retryOnFailureActivity, activityTypeRetryOnFailure) } func retryWorkflow(ctx workflow.Context, scheduledTimeNanos int64) error { profile, err := beginWorkflow(ctx, wfTypeRetry, scheduledTimeNanos) if err != nil { return err } info := workflow.GetInfo(ctx) now := workflow.Now(ctx).UnixNano() expiration := time.Duration(info.ExecutionStartToCloseTimeoutSeconds) * time.Second retryPolicy := &cadence.RetryPolicy{ InitialInterval: time.Second * 5, BackoffCoefficient: 1, MaximumInterval: time.Second * 5, ExpirationInterval: expiration, MaximumAttempts: 5, } activityCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ TaskList: taskListName, ScheduleToStartTimeout: expiration, StartToCloseTimeout: expiration, HeartbeatTimeout: 5 * time.Second, RetryPolicy: retryPolicy, }) f1 := workflow.ExecuteActivity(activityCtx, activityTypeRetryOnTimeout, now) f2 := workflow.ExecuteActivity(activityCtx, activityTypeRetryOnFailure, now) var progress int err = f1.Get(ctx, &progress) if err != nil { workflow.GetLogger(ctx).Error("retryWorkflow failed on RetryOnTimeout", zap.Error(err)) return profile.end(err) } if progress < 200 { workflow.GetLogger(ctx).Error("Unexpected activity progress.", zap.Int("Progress", progress)) return profile.end(errUnexpectedProgress) } var result int32 err = f2.Get(ctx, &result) if err != nil { workflow.GetLogger(ctx).Error("retryWorkflow failed on RetryOnFailure", zap.Error(err)) return profile.end(err) } if result < 3 { workflow.GetLogger(ctx).Error("Unexpected activity result.", zap.Int32("Result", result)) return profile.end(errUnexpectedResult) } return profile.end(nil) } func retryOnTimeoutActivity(ctx context.Context, scheduledTimeNanos int64) (int, error) { var err error progress := 0 if activity.HasHeartbeatDetails(ctx) { err = activity.GetHeartbeatDetails(ctx, &progress) if err != nil { activity.GetLogger(ctx).Error("GetProgress failed.", zap.Error(err)) return 0, err } } info := activity.GetInfo(ctx) if info.Attempt < 3 { activity.RecordHeartbeat(ctx, info.Attempt*100) time.Sleep(2 * info.HeartbeatTimeout) } return progress, nil } func retryOnFailureActivity(ctx context.Context, scheduledTimeNanos int64) (int32, error) { info := activity.GetInfo(ctx) if info.Attempt < 3 { return 0, errRetryableActivityError } return info.Attempt, nil } ================================================ FILE: canary/runner.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "sync" "time" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/compatibility" "go.uber.org/yarpc" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/peer" "go.uber.org/yarpc/peer/hostport" "go.uber.org/yarpc/transport/grpc" "go.uber.org/yarpc/transport/tchannel" "go.uber.org/zap" "google.golang.org/grpc/credentials" "github.com/uber/cadence/common/log" ) type canaryRunner struct { *RuntimeContext config *Canary } // NewCanaryRunner creates and returns a runnable which spins // up a set of canaries based on supplied config func NewCanaryRunner(cfg *Config) (Runnable, error) { logger, err := cfg.Log.NewZapLogger() if err != nil { return nil, fmt.Errorf("failed to create logger: %v", err) } metricsScope := cfg.Metrics.NewScope(log.NewLogger(logger), "cadence-canary") if cfg.Cadence.ServiceName == "" { cfg.Cadence.ServiceName = CadenceServiceName } var dispatcher *yarpc.Dispatcher var runtimeContext *RuntimeContext if cfg.Cadence.GRPCHostNameAndPort != "" { var outbounds transport.Outbounds if cfg.Cadence.TLSCAFile != "" { caCert, err := ioutil.ReadFile(cfg.Cadence.TLSCAFile) if err != nil { logger.Fatal("Failed to load server CA certificate", zap.Error(err)) } caCertPool := x509.NewCertPool() if !caCertPool.AppendCertsFromPEM(caCert) { logger.Fatal("Failed to add server CA certificate", zap.Error(err)) } tlsConfig := tls.Config{ RootCAs: caCertPool, } tlsCreds := credentials.NewTLS(&tlsConfig) grpcTransport := grpc.NewTransport() tlsChooser := peer.NewSingle(hostport.Identify(cfg.Cadence.GRPCHostNameAndPort), grpcTransport.NewDialer(grpc.DialerCredentials(tlsCreds))) outbounds = transport.Outbounds{Unary: grpcTransport.NewOutbound(tlsChooser)} } else { outbounds = transport.Outbounds{Unary: grpc.NewTransport().NewSingleOutbound(cfg.Cadence.GRPCHostNameAndPort)} } dispatcher = yarpc.NewDispatcher(yarpc.Config{ Name: CanaryServiceName, Outbounds: yarpc.Outbounds{ cfg.Cadence.ServiceName: outbounds, }, }) clientConfig := dispatcher.ClientConfig(cfg.Cadence.ServiceName) runtimeContext = NewRuntimeContext( logger, metricsScope, compatibility.NewThrift2ProtoAdapter( apiv1.NewDomainAPIYARPCClient(clientConfig), apiv1.NewWorkflowAPIYARPCClient(clientConfig), apiv1.NewWorkerAPIYARPCClient(clientConfig), apiv1.NewVisibilityAPIYARPCClient(clientConfig), ), ) } else if cfg.Cadence.ThriftHostNameAndPort != "" { tch, err := tchannel.NewChannelTransport( tchannel.ServiceName(CanaryServiceName), ) if err != nil { return nil, fmt.Errorf("failed to create transport channel: %v", err) } dispatcher = yarpc.NewDispatcher(yarpc.Config{ Name: CanaryServiceName, Outbounds: yarpc.Outbounds{ cfg.Cadence.ServiceName: {Unary: tch.NewSingleOutbound(cfg.Cadence.ThriftHostNameAndPort)}, }, }) runtimeContext = NewRuntimeContext( logger, metricsScope, workflowserviceclient.New(dispatcher.ClientConfig(cfg.Cadence.ServiceName)), ) } else { return nil, fmt.Errorf("must specify either gRPC address(address) or Thrift address (host) in the config") } if err := dispatcher.Start(); err != nil { dispatcher.Stop() return nil, fmt.Errorf("failed to create outbound transport channel: %v", err) } return &canaryRunner{ RuntimeContext: runtimeContext, config: &cfg.Canary, }, nil } // Run runs the canaries func (r *canaryRunner) Run(mode string) error { r.metrics.Counter("restarts").Inc(1) if len(r.config.Excludes) != 0 { updateSanityChildWFList(r.config.Excludes) } if r.config.Cron.CronSchedule == "" { r.config.Cron.CronSchedule = "@every 30s" } if r.config.Cron.CronExecutionTimeout == 0 { r.config.Cron.CronExecutionTimeout = 18 * time.Minute } if r.config.Cron.StartJobTimeout == 0 { r.config.Cron.StartJobTimeout = 9 * time.Minute } var wg sync.WaitGroup for _, d := range r.config.Domains { canary := newCanary(d, r.RuntimeContext, r.config) r.logger.Info("starting canary", zap.String("domain", d)) r.execute(canary, mode, &wg) } wg.Wait() return nil } func (r *canaryRunner) execute(task Runnable, mode string, wg *sync.WaitGroup) { wg.Add(1) go func() { task.Run(mode) wg.Done() }() } func updateSanityChildWFList(excludes []string) { var temp []string for _, childName := range sanityChildWFList { if !isStringInList(childName, excludes) { temp = append(temp, childName) } } sanityChildWFList = temp } func isStringInList(str string, list []string) bool { for _, l := range list { if l == str { return true } } return false } ================================================ FILE: canary/sanity.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "fmt" "go.uber.org/cadence/workflow" "go.uber.org/multierr" "go.uber.org/zap" ) // sanityChildWFList is the list of tests / child workflows invoked by the sanity canary var sanityChildWFList = []string{ wfTypeEcho, wfTypeSignal, wfTypeVisibility, wfTypeSearchAttributes, wfTypeConcurrentExec, wfTypeQuery, wfTypeTimeout, wfTypeLocalActivity, wfTypeCancellation, wfTypeRetry, wfTypeReset, wfTypeHistoryArchival, wfTypeVisibilityArchival, wfTypeBatch, wfTypeCrossClusterParent, } func init() { registerWorkflow(sanityWorkflow, wfTypeSanity) } // sanityWorkflow represents a canary implementation that tests the // sanity of a cadence cluster - it performs probes / tests that // exercises the frontend APIs and the basic functionality of the // client library - each probe / test MUST be implemented as a // child workflow of this workflow func sanityWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) domain := workflow.GetInfo(ctx).Domain var err error profile, err := beginWorkflow(ctx, wfTypeSanity, scheduledTimeNanos) if err != nil { return err } childNames, err := getChildWorkflowNames(ctx) if err != nil { return profile.end(err) } selector, resultC := forkChildWorkflows(ctx, domain, childNames) err = joinChildWorkflows(ctx, childNames, selector, resultC) if err != nil { workflow.GetLogger(ctx).Error("sanity workflow failed", zap.Error(err)) return profile.end(err) } workflow.GetLogger(ctx).Info("sanity workflow finished successfully") return profile.end(err) } // forkChildWorkflows spawns child workflows with the given names // this method assumes that all child workflows have the same method signature func forkChildWorkflows(ctx workflow.Context, domain string, names []string) (workflow.Selector, workflow.Channel) { now := workflow.Now(ctx).UnixNano() selector := workflow.NewSelector(ctx) resultC := workflow.NewBufferedChannel(ctx, len(names)) myID := workflow.GetInfo(ctx).WorkflowExecution.ID for _, childName := range names { cwo := newChildWorkflowOptions(domain, concat(myID, childName)) childCtx := workflow.WithChildOptions(ctx, cwo) future := workflow.ExecuteChildWorkflow(childCtx, childName, now) selector.AddFuture(future, func(f workflow.Future) { if err := f.Get(ctx, nil); err != nil { workflow.GetLogger(ctx).Error("child workflow failed", zap.Error(err)) err = fmt.Errorf("child workflow %s failed: %w", childName, err) resultC.Send(ctx, err) return } resultC.Send(ctx, nil) }) } return selector, resultC } // joinChildWorkflows waits for all the given child workflows to complete // returns error even if atleast one of the child workflow returns an error func joinChildWorkflows(ctx workflow.Context, names []string, selector workflow.Selector, resultC workflow.Channel) error { var err error for i := 0; i < len(names); i++ { selector.Select(ctx) var err1 error resultC.Receive(ctx, &err1) err = multierr.Append(err, err1) } return err } // getChildWorkflowNames exist mainly to make sure replays result in // deterministic behavior for the workflow - the set of child workflows // to be spawned are recorded in history as a side effect and result // from the side effect is returned as a result func getChildWorkflowNames(ctx workflow.Context) ([]string, error) { var names []string err := workflow.SideEffect(ctx, func(workflow.Context) interface{} { return sanityChildWFList }).Get(&names) return names, err } ================================================ FILE: canary/searchAttributes.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "fmt" "time" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) const ( timeToWaitVisibilityDataOnESInDuration = 2 * time.Second ) func init() { registerWorkflow(searchAttributesWorkflow, wfTypeSearchAttributes) registerActivity(searchAttributesActivity, activityTypeSearchAttributes) } // searchAttributesWorkflow tests the search attributes apis func searchAttributesWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) var err error profile, err := beginWorkflow(ctx, wfTypeSearchAttributes, scheduledTimeNanos) if err != nil { return err } attr := map[string]interface{}{ "CustomKeywordField": "canaryTest", } err = workflow.UpsertSearchAttributes(ctx, attr) if err != nil { workflow.GetLogger(ctx).Error("searchAttributes test failed", zap.Error(err)) return profile.end(err) } execInfo := workflow.GetInfo(ctx).WorkflowExecution aCtx := workflow.WithActivityOptions(ctx, newActivityOptions()) // wait for visibility on ES if err := workflow.Sleep(ctx, timeToWaitVisibilityDataOnESInDuration); err != nil { return profile.end(err) } now := workflow.Now(ctx).UnixNano() err = workflow.ExecuteActivity(aCtx, activityTypeSearchAttributes, now, execInfo).Get(ctx, nil) if err != nil { workflow.GetLogger(ctx).Error("searchAttributes test failed", zap.Error(err)) return profile.end(err) } return profile.end(nil) } // searchAttributesActivity exercises the visibility apis func searchAttributesActivity(ctx context.Context, scheduledTimeNanos int64, parentInfo workflow.Execution) error { var err error scope := activity.GetMetricsScope(ctx) scope, sw := recordActivityStart(scope, activityTypeSearchAttributes, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) client := getActivityContext(ctx).cadence if err := listWorkflow(client, parentInfo.ID, scope); err != nil { return err } if _, err := getMyHistory(client, parentInfo, scope); err != nil { return err } return err } func listWorkflow(client cadenceClient, wfID string, scope tally.Scope) error { pageSz := int32(1) startTime := time.Now().UnixNano() - int64(timeSkewToleranceDuration) endTime := time.Now().UnixNano() + int64(timeSkewToleranceDuration) queryStr := fmt.Sprintf("WorkflowID = '%s' and CustomKeywordField = '%s' and StartTime between %d and %d", wfID, "canaryTest", startTime, endTime) request := &shared.ListWorkflowExecutionsRequest{ PageSize: &pageSz, Query: &queryStr, } scope.Counter(listWorkflowsCount).Inc(1) sw := scope.Timer(listWorkflowsLatency).Start() resp, err := client.ListWorkflow(context.Background(), request) sw.Stop() if err != nil { scope.Counter(listWorkflowsFailureCount).Inc(1) return err } if len(resp.Executions) != 1 { scope.Counter(listWorkflowsFailureCount).Inc(1) err := fmt.Errorf("listWorkflow returned %d executions, expected=1", len(resp.Executions)) return err } id := resp.Executions[0].Execution.GetWorkflowId() if id != wfID { scope.Counter(listWorkflowsFailureCount).Inc(1) err := fmt.Errorf("listWorkflow returned wrong workflow id %v", id) return err } return nil } ================================================ FILE: canary/signal.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "errors" "fmt" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) const ( signalName = "signal-name" signalValue = "canary.signal" ) func init() { registerWorkflow(signalWorkflow, wfTypeSignal) registerWorkflow(signalExternalWorkflow, wfTypeSignalExternal) registerActivity(signalActivity, activityTypeSignal) } // signalWorkflow is the workflow implementation to test SignalWorkflowExecution API // it simply spins up an activity which sends a signal to the parent func signalWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) domain := workflow.GetInfo(ctx).Domain var err error profile, err := beginWorkflow(ctx, wfTypeSignal, scheduledTimeNanos) if err != nil { return err } execInfo := workflow.GetInfo(ctx).WorkflowExecution sigName := fmt.Sprintf("sig.%v", execInfo.RunID) sigCh := workflow.GetSignalChannel(ctx, sigName) aCtx := workflow.WithActivityOptions(ctx, newActivityOptions()) err = workflow.ExecuteActivity(aCtx, activityTypeSignal, workflow.Now(ctx).UnixNano(), execInfo, sigName).Get(ctx, nil) if err != nil { workflow.GetLogger(ctx).Error("signal activity failed", zap.Error(err)) return profile.end(err) } var sigValue string sigCh.Receive(ctx, &sigValue) if sigValue != signalValue { workflow.GetLogger(ctx).Error("wrong signal value received", zap.String("value", sigValue)) return profile.end(errors.New("invalid signal value")) } sigCh.Receive(ctx, &sigValue) if sigValue != signalValue { workflow.GetLogger(ctx).Error("wrong signal value received", zap.String("value", sigValue)) return profile.end(errors.New("invalid signal value")) } cwo := newChildWorkflowOptions(domain, wfTypeSignalExternal+"-child") childCtx := workflow.WithChildOptions(ctx, cwo) childFuture := workflow.ExecuteChildWorkflow(childCtx, wfTypeSignalExternal, workflow.Now(ctx).UnixNano(), sleepDuration) signalFuture := childFuture.SignalChildWorkflow(childCtx, signalName, signalValue) err = signalFuture.Get(childCtx, nil) if err != nil { return profile.end(err) } decisionSignalTestFn := func(useRunID bool) error { workflowIDSuffix := "-with-run-ID" if !useRunID { workflowIDSuffix = "-without-run-ID" } cwo = newChildWorkflowOptions(domain, wfTypeSignalExternal+workflowIDSuffix) childCtx = workflow.WithChildOptions(ctx, cwo) childFuture = workflow.ExecuteChildWorkflow(childCtx, wfTypeSignalExternal, workflow.Now(ctx).UnixNano(), sleepDuration) childExecution := &workflow.Execution{} err = childFuture.GetChildWorkflowExecution().Get(childCtx, childExecution) if err != nil { return err } if !useRunID { childExecution.RunID = "" } signalFuture = workflow.SignalExternalWorkflow(ctx, childExecution.ID, childExecution.RunID, signalName, signalValue) err = signalFuture.Get(ctx, nil) if err != nil { workflow.GetLogger(ctx).Info("signalWorkflow failed: fail to send siganl to child workflow", zap.Error(err)) return err } err = childFuture.Get(childCtx, nil) if err != nil { workflow.GetLogger(ctx).Info("signalWorkflow failed: child workflow return err", zap.Error(err)) return err } return nil } err = decisionSignalTestFn(false) if err != nil { return profile.end(err) } err = decisionSignalTestFn(true) return profile.end(err) } // signalActivity sends a signal to the parent workflow // that created this activity func signalActivity(ctx context.Context, scheduledTimeNanos int64, execInfo workflow.Execution, signalName string) error { scope := activity.GetMetricsScope(ctx) var err error scope, sw := recordActivityStart(scope, activityTypeSignal, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) client := getActivityContext(ctx).cadence err = client.SignalWorkflow(context.Background(), execInfo.ID, execInfo.RunID, signalName, signalValue) if err != nil { return err } err = client.SignalWorkflow(context.Background(), execInfo.ID, "", signalName, signalValue) if err != nil { return err } return nil } // signalExternalWorkflow receive a signal from the parent workflow func signalExternalWorkflow(ctx workflow.Context, scheduledTimeNanos int64) error { profile, err := beginWorkflow(ctx, wfTypeSignalExternal, scheduledTimeNanos) if err != nil { return profile.end(err) } var value string signalCh := workflow.GetSignalChannel(ctx, signalName) signalCh.Receive(ctx, &value) if value != signalValue { workflow.GetLogger(ctx).Error("wrong signal value received", zap.String("value", value)) return profile.end(errors.New("invalid signal value")) } return profile.end(nil) } ================================================ FILE: canary/timeout.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "fmt" "time" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) const ( activityDelay = 4 * time.Second activityStartToCloseTimeoutThreshold = 1 * time.Second activityScheduleToStartTimeoutThreshold = 1 * time.Second activityScheduleToCloseTimeoutThreshold = activityStartToCloseTimeoutThreshold + activityScheduleToStartTimeoutThreshold ) func init() { registerWorkflow(timeoutWorkflow, wfTypeTimeout) registerActivity(timeoutActivity, activityTypeTimeout) } // timeoutWorkflow is the workflow implementation to test for querying workflow status func timeoutWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) profile, err := beginWorkflow(ctx, wfTypeConcurrentExec, scheduledTimeNanos) if err != nil { return err } now := workflow.Now(ctx).UnixNano() activityOptions := newActivityOptions() activityOptions.StartToCloseTimeout = activityStartToCloseTimeoutThreshold activityOptions.ScheduleToStartTimeout = activityScheduleToStartTimeoutThreshold activityOptions.ScheduleToCloseTimeout = activityScheduleToCloseTimeoutThreshold activityCtx := workflow.WithActivityOptions(ctx, activityOptions) activityFuture := workflow.ExecuteActivity(activityCtx, timeoutActivity, now) activityErr := activityFuture.Get(ctx, nil) if activityErr != nil { if _, ok := activityErr.(*workflow.TimeoutError); !ok { workflow.GetLogger(ctx).Info("activity timeout failed", zap.Error(activityErr)) } else { activityErr = nil } } else { activityErr = fmt.Errorf("activity timeout error expected, but no error received") workflow.GetLogger(ctx).Info("activity timeout failed", zap.Error(activityErr)) } return profile.end(activityErr) } // timeoutActivity is the activity implementation for timeout test func timeoutActivity(ctx context.Context, scheduledTimeNanos int64) error { scope := activity.GetMetricsScope(ctx) var err error scope, sw := recordActivityStart(scope, activityTypeConcurrentExec, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) timer := time.NewTimer(activityDelay) <-timer.C timer.Stop() return nil } ================================================ FILE: canary/visibility.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "errors" "fmt" "time" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) const ( timeSkewToleranceDuration = 5 * time.Minute ) func init() { registerWorkflow(visibilityWorkflow, wfTypeVisibility) registerActivity(visibilityActivity, activityTypeVisibility) } // visibilityWorkflow tests the visibility apis func visibilityWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) var err error profile, err := beginWorkflow(ctx, wfTypeVisibility, scheduledTimeNanos) if err != nil { return err } execInfo := workflow.GetInfo(ctx).WorkflowExecution aCtx := workflow.WithActivityOptions(ctx, newActivityOptions()) // wait for visibility on ES {} if err := workflow.Sleep(ctx, 2*time.Second); err != nil { return profile.end(err) } now := workflow.Now(ctx).UnixNano() err = workflow.ExecuteActivity(aCtx, activityTypeVisibility, now, execInfo).Get(ctx, nil) if err != nil { workflow.GetLogger(ctx).Error("visibility test failed", zap.Error(err)) return profile.end(err) } return profile.end(nil) } // visibilityActivity exercises the visibility apis func visibilityActivity(ctx context.Context, scheduledTimeNanos int64, parentInfo workflow.Execution) error { var err error scope := activity.GetMetricsScope(ctx) scope, sw := recordActivityStart(scope, activityTypeVisibility, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) client := getActivityContext(ctx).cadence if err := listMyWorkflow(client, parentInfo.ID, scope); err != nil { return err } if _, err := getMyHistory(client, parentInfo, scope); err != nil { return err } return err } func listMyWorkflow(client cadenceClient, wfID string, scope tally.Scope) error { pageSz := int32(1) startTime := time.Now().UnixNano() - int64(timeSkewToleranceDuration) endTime := time.Now().UnixNano() + int64(timeSkewToleranceDuration) request := &shared.ListOpenWorkflowExecutionsRequest{ MaximumPageSize: &pageSz, ExecutionFilter: &shared.WorkflowExecutionFilter{WorkflowId: &wfID}, StartTimeFilter: &shared.StartTimeFilter{ EarliestTime: &startTime, LatestTime: &endTime, }, } scope.Counter(listOpenWorkflowsCount).Inc(1) sw := scope.Timer(listOpenWorkflowsLatency).Start() resp, err := client.ListOpenWorkflow(context.Background(), request) sw.Stop() if err != nil { scope.Counter(listOpenWorkflowsFailureCount).Inc(1) return err } if len(resp.Executions) != 1 { scope.Counter(listOpenWorkflowsFailureCount).Inc(1) err := fmt.Errorf("listOpenWorkflow returned %d executions, expected=1", len(resp.Executions)) return err } id := resp.Executions[0].Execution.GetWorkflowId() if id != wfID { scope.Counter(listOpenWorkflowsFailureCount).Inc(1) err := fmt.Errorf("listOpenWorkflow returned wrong workflow id %v", id) return err } return nil } func getMyHistory(client cadenceClient, execInfo workflow.Execution, scope tally.Scope) ([]*shared.HistoryEvent, error) { scope.Counter(getWorkflowHistoryCount).Inc(1) sw := scope.Timer(getWorkflowHistoryLatency).Start() defer sw.Stop() events := []*shared.HistoryEvent{} iter := client.GetWorkflowHistory(context.Background(), execInfo.ID, execInfo.RunID, false, shared.HistoryEventFilterTypeAllEvent) for iter.HasNext() { event, err := iter.Next() if err != nil { scope.Counter(getWorkflowHistoryFailureCount).Inc(1) return nil, err } events = append(events, event) } if len(events) == 0 { return nil, errors.New("getWorkflowHistory returned history with 0 events") } return events, nil } ================================================ FILE: canary/visibilityArchival.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "errors" "fmt" "strings" "time" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" ) type ( visibilityArchivalValidator interface { shouldRun() bool getQuery(workflowID, runID, workflowType string, startTime, closeTime time.Time) string validateExecutions([]*shared.WorkflowExecutionInfo) error } filestoreVisibilityArchivalValidator struct { expectedRunID string } ) const ( schemeFilestore = "file" queryPageSize = 100 ) func init() { registerWorkflow(visibilityArchivalWorkflow, wfTypeVisibilityArchival) registerActivity(visibilityArchivalActivity, activityTypeVisibilityArchival) } func visibilityArchivalWorkflow(ctx workflow.Context, inputScheduledTimeNanos int64) error { scheduledTimeNanos := getScheduledTimeFromInputIfNonZero(ctx, inputScheduledTimeNanos) profile, err := beginWorkflow(ctx, wfTypeVisibilityArchival, scheduledTimeNanos) if err != nil { return err } if err := workflow.ExecuteActivity( workflow.WithActivityOptions(ctx, newActivityOptions()), activityTypeVisibilityArchival, workflow.Now(ctx).UnixNano(), ).Get(ctx, nil); err != nil { workflow.GetLogger(ctx).Error("failed to list archived workflows", zap.Error(err)) return profile.end(err) } return profile.end(nil) } func visibilityArchivalActivity(ctx context.Context, scheduledTimeNanos int64) error { scope := activity.GetMetricsScope(ctx) var err error scope, sw := recordActivityStart(scope, activityTypeVisibilityArchival, scheduledTimeNanos) defer recordActivityEnd(scope, sw, err) client := getActivityArchivalContext(ctx).cadence resp, err := client.Describe(ctx, archivalDomain) if err != nil { return err } if resp.Configuration != nil && resp.Configuration.GetVisibilityArchivalStatus() == shared.ArchivalStatusDisabled { return errors.New("domain not configured for visibility archival") } visArchivalURI := "" if resp.Configuration != nil { visArchivalURI = resp.Configuration.GetVisibilityArchivalURI() } var validator visibilityArchivalValidator scheme := getURIScheme(visArchivalURI) switch scheme { case schemeFilestore: validator = newFilestoreVisibilityArchivalValidator() default: return fmt.Errorf("unknown visibility archival scheme: %s", scheme) } if !validator.shouldRun() { return nil } startTime := time.Now() execution, err := executeArchivalExeternalWorkflow(ctx, client, startTime.UnixNano()) if err != nil { return err } query := validator.getQuery( execution.GetWorkflowId(), execution.GetRunId(), wfTypeArchivalExternal, startTime, time.Now(), ) for { select { case <-ctx.Done(): return ctx.Err() default: } executions, err := listarchivedWorkflow( ctx, scope, client, &shared.ListArchivedWorkflowExecutionsRequest{ Domain: stringPtr(archivalDomain), PageSize: int32Ptr(queryPageSize), Query: stringPtr(query), }, ) if err != nil && isBadRequestError(err) { return err } if err == nil && validator.validateExecutions(executions) == nil { return nil } <-time.After(5 * time.Second) } } func listarchivedWorkflow( ctx context.Context, scope tally.Scope, client cadenceClient, request *shared.ListArchivedWorkflowExecutionsRequest, ) ([]*shared.WorkflowExecutionInfo, error) { var executions []*shared.WorkflowExecutionInfo for { select { case <-ctx.Done(): return nil, ctx.Err() default: } scope.Counter(listArchivedWorkflowCount).Inc(1) sw := scope.Timer(listArchivedWorkflowsLatency).Start() response, err := client.ListArchivedWorkflow(ctx, request) sw.Stop() if err != nil { scope.Counter(listArchivedWorkflowFailureCount).Inc(1) return nil, err } if len(response.Executions) != 0 { executions = append(executions, response.Executions...) } if response.NextPageToken == nil { break } request.NextPageToken = response.NextPageToken } return executions, nil } func newFilestoreVisibilityArchivalValidator() visibilityArchivalValidator { return &filestoreVisibilityArchivalValidator{} } func (v *filestoreVisibilityArchivalValidator) shouldRun() bool { return true } func (v *filestoreVisibilityArchivalValidator) getQuery( workflowID, runID, workflowType string, startTime, closeTime time.Time, ) string { v.expectedRunID = runID return fmt.Sprintf( "WorkflowType = '%s' and WorkflowID = '%s' and CloseTime >= %v and CloseTime <= %v", workflowType, workflowID, startTime.UnixNano(), closeTime.UnixNano(), ) } func (v *filestoreVisibilityArchivalValidator) validateExecutions( executions []*shared.WorkflowExecutionInfo, ) error { if len(executions) != 1 { return fmt.Errorf("listarchivedWorkflow returned %d executions, expecting 1", len(executions)) } runID := executions[0].Execution.GetRunId() if runID != v.expectedRunID { return fmt.Errorf("listarchivedWorkflow returned wrong runID %v, expecting %s", runID, v.expectedRunID) } return nil } func getURIScheme(URI string) string { if idx := strings.Index(URI, "://"); idx != -1 { return URI[:idx] } return "" } func isBadRequestError( err error, ) bool { _, ok := err.(*shared.BadRequestError) return ok } ================================================ FILE: canary/workflow_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package canary import ( "context" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/cadence/mocks" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "github.com/uber/cadence/common/log/testlogger" ) type ( workflowTestSuite struct { suite.Suite testsuite.WorkflowTestSuite env *testsuite.TestWorkflowEnvironment } mockCadenceClient struct { client *mocks.Client domainClient *mocks.DomainClient } ) func TestWorkflowTestSuite(t *testing.T) { suite.Run(t, new(workflowTestSuite)) } func (s *workflowTestSuite) SetupTest() { s.SetLogger(testlogger.NewZap(s.T())) s.env = s.NewTestWorkflowEnvironment() s.env.Test(s.T()) } func (s *workflowTestSuite) TearDownTest() { s.env.AssertExpectations(s.T()) } func (s *workflowTestSuite) TestConcurrentExecWorkflow() { s.env.SetWorkerOptions(newTestWorkerOptions(newMockActivityContext(newMockCadenceClient()))) s.env.ExecuteWorkflow(wfTypeConcurrentExec, time.Now().UnixNano(), "") s.True(s.env.IsWorkflowCompleted()) s.NoError(s.env.GetWorkflowError()) } func (s *workflowTestSuite) TestQueryWorkflow() { // DOTO implement this test once client test framework // SetOnActivityStartedListener is guaranteed to // be executed and finished before activity started } func (s *workflowTestSuite) TestTimeout() { // DOTO implement this test once client test framework // can handle timeout error correctly } func (s *workflowTestSuite) TestSignalWorkflow() { mockClient := newMockCadenceClient() mockClient.client.On("SignalWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { arg3 := args.Get(3).(string) arg4 := args.Get(4).(string) s.env.SignalWorkflow(arg3, arg4) }).Return(nil) s.env.SetWorkerOptions(newTestWorkerOptions(newMockActivityContext(mockClient))) s.env.ExecuteWorkflow(wfTypeSignal, time.Now().UnixNano(), "") s.True(s.env.IsWorkflowCompleted()) s.NoError(s.env.GetWorkflowError()) } func (s *workflowTestSuite) TestEchoWorkflow() { s.env.SetWorkerOptions(newTestWorkerOptions(newMockActivityContext(newMockCadenceClient()))) s.env.ExecuteWorkflow(wfTypeEcho, time.Now().UnixNano(), "") s.True(s.env.IsWorkflowCompleted()) s.NoError(s.env.GetWorkflowError()) } func (s *workflowTestSuite) TestVisibilityWorkflow() { // uncomment below when race condition on s.env.SetOnActivityStartedListener is resolved // mockClient := newMockCadenceClient() // // setup the mock for visibility apis after the activity is invoked, because // // we need the workflow id and run id to construct a response for the mock // s.env.SetOnActivityStartedListener(func(activityInfo *cadence.ActivityInfo, ctx context.Context, args cadence.EncodedValues) { // if activityInfo.ActivityType.Name != activityTypeVisibility { // return // } // resp := newMockOpenWorkflowResponse(activityInfo.WorkflowExecution.ID, activityInfo.WorkflowExecution.RunID) // mockClient.client.On("ListOpenWorkflow", mock.Anything, mock.Anything).Return(resp, nil) // history := &shared.History{Events: []*shared.HistoryEvent{{}}} // mockClient.client.On("GetWorkflowHistory", mock.Anything, mock.Anything, mock.Anything).Return(history, nil) // }) // s.env.SetWorkerOptions(newTestWorkerOptions(newMockActivityContext(mockClient))) // s.env.ExecuteWorkflow(wfTypeVisibility, time.Now().UnixNano(), &ServiceConfig{}) // s.True(s.env.IsWorkflowCompleted()) // s.NoError(s.env.GetWorkflowError()) } func (s *workflowTestSuite) TestSanityWorkflow() { oldWFList := sanityChildWFList sanityChildWFList = []string{wfTypeEcho} s.env.ExecuteWorkflow(wfTypeSanity, time.Now().UnixNano(), "") sanityChildWFList = oldWFList s.True(s.env.IsWorkflowCompleted()) s.NoError(s.env.GetWorkflowError()) } func (s *workflowTestSuite) TestLocalActivityWorkflow() { s.env.OnActivity(getConditionData).Return(int32(20), nil).Once() s.env.ExecuteWorkflow(localActivityWorkfow, time.Now().UnixNano()) s.True(s.env.IsWorkflowCompleted()) s.NoError(s.env.GetWorkflowError()) var result string err := s.env.GetWorkflowResult(&result) s.NoError(err) s.Equal("data%2 == 0 and data%5 == 0", result) } func newTestWorkerOptions(ctx *activityContext) worker.Options { return worker.Options{ MetricsScope: tally.NoopScope, BackgroundActivityContext: context.WithValue(context.Background(), ctxKeyActivityRuntime, ctx), } } func newMockActivityContext(client mockCadenceClient) *activityContext { return &activityContext{ cadence: cadenceClient{ Client: client.client, DomainClient: client.domainClient, }, } } func newMockCadenceClient() mockCadenceClient { return mockCadenceClient{ client: new(mocks.Client), domainClient: new(mocks.DomainClient), } } ================================================ FILE: client/admin/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package admin import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -package admin github.com/uber/cadence/client/admin Client //go:generate gowrap gen -g -p . -i Client -t ../templates/retry.tmpl -o ../wrappers/retryable/admin_generated.go -v client=Admin //go:generate gowrap gen -g -p . -i Client -t ../templates/metered.tmpl -o ../wrappers/metered/admin_generated.go -v client=Admin //go:generate gowrap gen -g -p . -i Client -t ../templates/errorinjectors.tmpl -o ../wrappers/errorinjectors/admin_generated.go -v client=Admin //go:generate gowrap gen -g -p . -i Client -t ../templates/grpc.tmpl -o ../wrappers/grpc/admin_generated.go -v client=Admin -v package=adminv1 -v path=github.com/uber/cadence-idl/go/proto/admin/v1 -v prefix=Admin //go:generate gowrap gen -g -p . -i Client -t ../templates/thrift.tmpl -o ../wrappers/thrift/admin_generated.go -v client=Admin -v prefix=Admin //go:generate gowrap gen -g -p . -i Client -t ../templates/timeout.tmpl -o ../wrappers/timeout/admin_generated.go -v client=Admin // Client is the interface exposed by admin service client type Client interface { AddSearchAttribute(context.Context, *types.AddSearchAttributeRequest, ...yarpc.CallOption) error CloseShard(context.Context, *types.CloseShardRequest, ...yarpc.CallOption) error DescribeCluster(context.Context, ...yarpc.CallOption) (*types.DescribeClusterResponse, error) DescribeShardDistribution(context.Context, *types.DescribeShardDistributionRequest, ...yarpc.CallOption) (*types.DescribeShardDistributionResponse, error) DescribeHistoryHost(context.Context, *types.DescribeHistoryHostRequest, ...yarpc.CallOption) (*types.DescribeHistoryHostResponse, error) DescribeQueue(context.Context, *types.DescribeQueueRequest, ...yarpc.CallOption) (*types.DescribeQueueResponse, error) DescribeWorkflowExecution(context.Context, *types.AdminDescribeWorkflowExecutionRequest, ...yarpc.CallOption) (*types.AdminDescribeWorkflowExecutionResponse, error) GetDLQReplicationMessages(context.Context, *types.GetDLQReplicationMessagesRequest, ...yarpc.CallOption) (*types.GetDLQReplicationMessagesResponse, error) GetDomainReplicationMessages(context.Context, *types.GetDomainReplicationMessagesRequest, ...yarpc.CallOption) (*types.GetDomainReplicationMessagesResponse, error) GetReplicationMessages(context.Context, *types.GetReplicationMessagesRequest, ...yarpc.CallOption) (*types.GetReplicationMessagesResponse, error) GetWorkflowExecutionRawHistoryV2(context.Context, *types.GetWorkflowExecutionRawHistoryV2Request, ...yarpc.CallOption) (*types.GetWorkflowExecutionRawHistoryV2Response, error) CountDLQMessages(context.Context, *types.CountDLQMessagesRequest, ...yarpc.CallOption) (*types.CountDLQMessagesResponse, error) MergeDLQMessages(context.Context, *types.MergeDLQMessagesRequest, ...yarpc.CallOption) (*types.MergeDLQMessagesResponse, error) PurgeDLQMessages(context.Context, *types.PurgeDLQMessagesRequest, ...yarpc.CallOption) error ReadDLQMessages(context.Context, *types.ReadDLQMessagesRequest, ...yarpc.CallOption) (*types.ReadDLQMessagesResponse, error) ReapplyEvents(context.Context, *types.ReapplyEventsRequest, ...yarpc.CallOption) error RefreshWorkflowTasks(context.Context, *types.RefreshWorkflowTasksRequest, ...yarpc.CallOption) error RemoveTask(context.Context, *types.RemoveTaskRequest, ...yarpc.CallOption) error ResendReplicationTasks(context.Context, *types.ResendReplicationTasksRequest, ...yarpc.CallOption) error ResetQueue(context.Context, *types.ResetQueueRequest, ...yarpc.CallOption) error GetDynamicConfig(context.Context, *types.GetDynamicConfigRequest, ...yarpc.CallOption) (*types.GetDynamicConfigResponse, error) UpdateDynamicConfig(context.Context, *types.UpdateDynamicConfigRequest, ...yarpc.CallOption) error RestoreDynamicConfig(context.Context, *types.RestoreDynamicConfigRequest, ...yarpc.CallOption) error ListDynamicConfig(context.Context, *types.ListDynamicConfigRequest, ...yarpc.CallOption) (*types.ListDynamicConfigResponse, error) DeleteWorkflow(context.Context, *types.AdminDeleteWorkflowRequest, ...yarpc.CallOption) (*types.AdminDeleteWorkflowResponse, error) MaintainCorruptWorkflow(context.Context, *types.AdminMaintainWorkflowRequest, ...yarpc.CallOption) (*types.AdminMaintainWorkflowResponse, error) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (*types.GetGlobalIsolationGroupsResponse, error) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (*types.UpdateGlobalIsolationGroupsResponse, error) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (*types.GetDomainIsolationGroupsResponse, error) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (*types.UpdateDomainIsolationGroupsResponse, error) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (*types.GetDomainAsyncWorkflowConfiguratonResponse, error) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (*types.UpdateDomainAsyncWorkflowConfiguratonResponse, error) UpdateTaskListPartitionConfig(ctx context.Context, request *types.UpdateTaskListPartitionConfigRequest, opts ...yarpc.CallOption) (*types.UpdateTaskListPartitionConfigResponse, error) } ================================================ FILE: client/admin/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package admin -source interface.go -destination interface_mock.go -package admin github.com/uber/cadence/client/admin Client // // Package admin is a generated GoMock package. package admin import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" types "github.com/uber/cadence/common/types" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // AddSearchAttribute mocks base method. func (m *MockClient) AddSearchAttribute(arg0 context.Context, arg1 *types.AddSearchAttributeRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "AddSearchAttribute", varargs...) ret0, _ := ret[0].(error) return ret0 } // AddSearchAttribute indicates an expected call of AddSearchAttribute. func (mr *MockClientMockRecorder) AddSearchAttribute(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSearchAttribute", reflect.TypeOf((*MockClient)(nil).AddSearchAttribute), varargs...) } // CloseShard mocks base method. func (m *MockClient) CloseShard(arg0 context.Context, arg1 *types.CloseShardRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CloseShard", varargs...) ret0, _ := ret[0].(error) return ret0 } // CloseShard indicates an expected call of CloseShard. func (mr *MockClientMockRecorder) CloseShard(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseShard", reflect.TypeOf((*MockClient)(nil).CloseShard), varargs...) } // CountDLQMessages mocks base method. func (m *MockClient) CountDLQMessages(arg0 context.Context, arg1 *types.CountDLQMessagesRequest, arg2 ...yarpc.CallOption) (*types.CountDLQMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CountDLQMessages", varargs...) ret0, _ := ret[0].(*types.CountDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CountDLQMessages indicates an expected call of CountDLQMessages. func (mr *MockClientMockRecorder) CountDLQMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountDLQMessages", reflect.TypeOf((*MockClient)(nil).CountDLQMessages), varargs...) } // DeleteWorkflow mocks base method. func (m *MockClient) DeleteWorkflow(arg0 context.Context, arg1 *types.AdminDeleteWorkflowRequest, arg2 ...yarpc.CallOption) (*types.AdminDeleteWorkflowResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DeleteWorkflow", varargs...) ret0, _ := ret[0].(*types.AdminDeleteWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteWorkflow indicates an expected call of DeleteWorkflow. func (mr *MockClientMockRecorder) DeleteWorkflow(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWorkflow", reflect.TypeOf((*MockClient)(nil).DeleteWorkflow), varargs...) } // DescribeCluster mocks base method. func (m *MockClient) DescribeCluster(arg0 context.Context, arg1 ...yarpc.CallOption) (*types.DescribeClusterResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0} for _, a := range arg1 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeCluster", varargs...) ret0, _ := ret[0].(*types.DescribeClusterResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeCluster indicates an expected call of DescribeCluster. func (mr *MockClientMockRecorder) DescribeCluster(arg0 any, arg1 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0}, arg1...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeCluster", reflect.TypeOf((*MockClient)(nil).DescribeCluster), varargs...) } // DescribeHistoryHost mocks base method. func (m *MockClient) DescribeHistoryHost(arg0 context.Context, arg1 *types.DescribeHistoryHostRequest, arg2 ...yarpc.CallOption) (*types.DescribeHistoryHostResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeHistoryHost", varargs...) ret0, _ := ret[0].(*types.DescribeHistoryHostResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeHistoryHost indicates an expected call of DescribeHistoryHost. func (mr *MockClientMockRecorder) DescribeHistoryHost(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeHistoryHost", reflect.TypeOf((*MockClient)(nil).DescribeHistoryHost), varargs...) } // DescribeQueue mocks base method. func (m *MockClient) DescribeQueue(arg0 context.Context, arg1 *types.DescribeQueueRequest, arg2 ...yarpc.CallOption) (*types.DescribeQueueResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeQueue", varargs...) ret0, _ := ret[0].(*types.DescribeQueueResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeQueue indicates an expected call of DescribeQueue. func (mr *MockClientMockRecorder) DescribeQueue(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeQueue", reflect.TypeOf((*MockClient)(nil).DescribeQueue), varargs...) } // DescribeShardDistribution mocks base method. func (m *MockClient) DescribeShardDistribution(arg0 context.Context, arg1 *types.DescribeShardDistributionRequest, arg2 ...yarpc.CallOption) (*types.DescribeShardDistributionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeShardDistribution", varargs...) ret0, _ := ret[0].(*types.DescribeShardDistributionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeShardDistribution indicates an expected call of DescribeShardDistribution. func (mr *MockClientMockRecorder) DescribeShardDistribution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeShardDistribution", reflect.TypeOf((*MockClient)(nil).DescribeShardDistribution), varargs...) } // DescribeWorkflowExecution mocks base method. func (m *MockClient) DescribeWorkflowExecution(arg0 context.Context, arg1 *types.AdminDescribeWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.AdminDescribeWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.AdminDescribeWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeWorkflowExecution indicates an expected call of DescribeWorkflowExecution. func (mr *MockClientMockRecorder) DescribeWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeWorkflowExecution", reflect.TypeOf((*MockClient)(nil).DescribeWorkflowExecution), varargs...) } // GetDLQReplicationMessages mocks base method. func (m *MockClient) GetDLQReplicationMessages(arg0 context.Context, arg1 *types.GetDLQReplicationMessagesRequest, arg2 ...yarpc.CallOption) (*types.GetDLQReplicationMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetDLQReplicationMessages", varargs...) ret0, _ := ret[0].(*types.GetDLQReplicationMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDLQReplicationMessages indicates an expected call of GetDLQReplicationMessages. func (mr *MockClientMockRecorder) GetDLQReplicationMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDLQReplicationMessages", reflect.TypeOf((*MockClient)(nil).GetDLQReplicationMessages), varargs...) } // GetDomainAsyncWorkflowConfiguraton mocks base method. func (m *MockClient) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (*types.GetDomainAsyncWorkflowConfiguratonResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, request} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetDomainAsyncWorkflowConfiguraton", varargs...) ret0, _ := ret[0].(*types.GetDomainAsyncWorkflowConfiguratonResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainAsyncWorkflowConfiguraton indicates an expected call of GetDomainAsyncWorkflowConfiguraton. func (mr *MockClientMockRecorder) GetDomainAsyncWorkflowConfiguraton(ctx, request any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, request}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainAsyncWorkflowConfiguraton", reflect.TypeOf((*MockClient)(nil).GetDomainAsyncWorkflowConfiguraton), varargs...) } // GetDomainIsolationGroups mocks base method. func (m *MockClient) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (*types.GetDomainIsolationGroupsResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, request} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetDomainIsolationGroups", varargs...) ret0, _ := ret[0].(*types.GetDomainIsolationGroupsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainIsolationGroups indicates an expected call of GetDomainIsolationGroups. func (mr *MockClientMockRecorder) GetDomainIsolationGroups(ctx, request any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, request}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainIsolationGroups", reflect.TypeOf((*MockClient)(nil).GetDomainIsolationGroups), varargs...) } // GetDomainReplicationMessages mocks base method. func (m *MockClient) GetDomainReplicationMessages(arg0 context.Context, arg1 *types.GetDomainReplicationMessagesRequest, arg2 ...yarpc.CallOption) (*types.GetDomainReplicationMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetDomainReplicationMessages", varargs...) ret0, _ := ret[0].(*types.GetDomainReplicationMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainReplicationMessages indicates an expected call of GetDomainReplicationMessages. func (mr *MockClientMockRecorder) GetDomainReplicationMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainReplicationMessages", reflect.TypeOf((*MockClient)(nil).GetDomainReplicationMessages), varargs...) } // GetDynamicConfig mocks base method. func (m *MockClient) GetDynamicConfig(arg0 context.Context, arg1 *types.GetDynamicConfigRequest, arg2 ...yarpc.CallOption) (*types.GetDynamicConfigResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetDynamicConfig", varargs...) ret0, _ := ret[0].(*types.GetDynamicConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDynamicConfig indicates an expected call of GetDynamicConfig. func (mr *MockClientMockRecorder) GetDynamicConfig(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDynamicConfig", reflect.TypeOf((*MockClient)(nil).GetDynamicConfig), varargs...) } // GetGlobalIsolationGroups mocks base method. func (m *MockClient) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (*types.GetGlobalIsolationGroupsResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, request} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetGlobalIsolationGroups", varargs...) ret0, _ := ret[0].(*types.GetGlobalIsolationGroupsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetGlobalIsolationGroups indicates an expected call of GetGlobalIsolationGroups. func (mr *MockClientMockRecorder) GetGlobalIsolationGroups(ctx, request any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, request}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGlobalIsolationGroups", reflect.TypeOf((*MockClient)(nil).GetGlobalIsolationGroups), varargs...) } // GetReplicationMessages mocks base method. func (m *MockClient) GetReplicationMessages(arg0 context.Context, arg1 *types.GetReplicationMessagesRequest, arg2 ...yarpc.CallOption) (*types.GetReplicationMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetReplicationMessages", varargs...) ret0, _ := ret[0].(*types.GetReplicationMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetReplicationMessages indicates an expected call of GetReplicationMessages. func (mr *MockClientMockRecorder) GetReplicationMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationMessages", reflect.TypeOf((*MockClient)(nil).GetReplicationMessages), varargs...) } // GetWorkflowExecutionRawHistoryV2 mocks base method. func (m *MockClient) GetWorkflowExecutionRawHistoryV2(arg0 context.Context, arg1 *types.GetWorkflowExecutionRawHistoryV2Request, arg2 ...yarpc.CallOption) (*types.GetWorkflowExecutionRawHistoryV2Response, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetWorkflowExecutionRawHistoryV2", varargs...) ret0, _ := ret[0].(*types.GetWorkflowExecutionRawHistoryV2Response) ret1, _ := ret[1].(error) return ret0, ret1 } // GetWorkflowExecutionRawHistoryV2 indicates an expected call of GetWorkflowExecutionRawHistoryV2. func (mr *MockClientMockRecorder) GetWorkflowExecutionRawHistoryV2(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowExecutionRawHistoryV2", reflect.TypeOf((*MockClient)(nil).GetWorkflowExecutionRawHistoryV2), varargs...) } // ListDynamicConfig mocks base method. func (m *MockClient) ListDynamicConfig(arg0 context.Context, arg1 *types.ListDynamicConfigRequest, arg2 ...yarpc.CallOption) (*types.ListDynamicConfigResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListDynamicConfig", varargs...) ret0, _ := ret[0].(*types.ListDynamicConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListDynamicConfig indicates an expected call of ListDynamicConfig. func (mr *MockClientMockRecorder) ListDynamicConfig(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDynamicConfig", reflect.TypeOf((*MockClient)(nil).ListDynamicConfig), varargs...) } // MaintainCorruptWorkflow mocks base method. func (m *MockClient) MaintainCorruptWorkflow(arg0 context.Context, arg1 *types.AdminMaintainWorkflowRequest, arg2 ...yarpc.CallOption) (*types.AdminMaintainWorkflowResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "MaintainCorruptWorkflow", varargs...) ret0, _ := ret[0].(*types.AdminMaintainWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MaintainCorruptWorkflow indicates an expected call of MaintainCorruptWorkflow. func (mr *MockClientMockRecorder) MaintainCorruptWorkflow(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaintainCorruptWorkflow", reflect.TypeOf((*MockClient)(nil).MaintainCorruptWorkflow), varargs...) } // MergeDLQMessages mocks base method. func (m *MockClient) MergeDLQMessages(arg0 context.Context, arg1 *types.MergeDLQMessagesRequest, arg2 ...yarpc.CallOption) (*types.MergeDLQMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "MergeDLQMessages", varargs...) ret0, _ := ret[0].(*types.MergeDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MergeDLQMessages indicates an expected call of MergeDLQMessages. func (mr *MockClientMockRecorder) MergeDLQMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MergeDLQMessages", reflect.TypeOf((*MockClient)(nil).MergeDLQMessages), varargs...) } // PurgeDLQMessages mocks base method. func (m *MockClient) PurgeDLQMessages(arg0 context.Context, arg1 *types.PurgeDLQMessagesRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "PurgeDLQMessages", varargs...) ret0, _ := ret[0].(error) return ret0 } // PurgeDLQMessages indicates an expected call of PurgeDLQMessages. func (mr *MockClientMockRecorder) PurgeDLQMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PurgeDLQMessages", reflect.TypeOf((*MockClient)(nil).PurgeDLQMessages), varargs...) } // ReadDLQMessages mocks base method. func (m *MockClient) ReadDLQMessages(arg0 context.Context, arg1 *types.ReadDLQMessagesRequest, arg2 ...yarpc.CallOption) (*types.ReadDLQMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ReadDLQMessages", varargs...) ret0, _ := ret[0].(*types.ReadDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadDLQMessages indicates an expected call of ReadDLQMessages. func (mr *MockClientMockRecorder) ReadDLQMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDLQMessages", reflect.TypeOf((*MockClient)(nil).ReadDLQMessages), varargs...) } // ReapplyEvents mocks base method. func (m *MockClient) ReapplyEvents(arg0 context.Context, arg1 *types.ReapplyEventsRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ReapplyEvents", varargs...) ret0, _ := ret[0].(error) return ret0 } // ReapplyEvents indicates an expected call of ReapplyEvents. func (mr *MockClientMockRecorder) ReapplyEvents(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReapplyEvents", reflect.TypeOf((*MockClient)(nil).ReapplyEvents), varargs...) } // RefreshWorkflowTasks mocks base method. func (m *MockClient) RefreshWorkflowTasks(arg0 context.Context, arg1 *types.RefreshWorkflowTasksRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RefreshWorkflowTasks", varargs...) ret0, _ := ret[0].(error) return ret0 } // RefreshWorkflowTasks indicates an expected call of RefreshWorkflowTasks. func (mr *MockClientMockRecorder) RefreshWorkflowTasks(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshWorkflowTasks", reflect.TypeOf((*MockClient)(nil).RefreshWorkflowTasks), varargs...) } // RemoveTask mocks base method. func (m *MockClient) RemoveTask(arg0 context.Context, arg1 *types.RemoveTaskRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RemoveTask", varargs...) ret0, _ := ret[0].(error) return ret0 } // RemoveTask indicates an expected call of RemoveTask. func (mr *MockClientMockRecorder) RemoveTask(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveTask", reflect.TypeOf((*MockClient)(nil).RemoveTask), varargs...) } // ResendReplicationTasks mocks base method. func (m *MockClient) ResendReplicationTasks(arg0 context.Context, arg1 *types.ResendReplicationTasksRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ResendReplicationTasks", varargs...) ret0, _ := ret[0].(error) return ret0 } // ResendReplicationTasks indicates an expected call of ResendReplicationTasks. func (mr *MockClientMockRecorder) ResendReplicationTasks(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResendReplicationTasks", reflect.TypeOf((*MockClient)(nil).ResendReplicationTasks), varargs...) } // ResetQueue mocks base method. func (m *MockClient) ResetQueue(arg0 context.Context, arg1 *types.ResetQueueRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ResetQueue", varargs...) ret0, _ := ret[0].(error) return ret0 } // ResetQueue indicates an expected call of ResetQueue. func (mr *MockClientMockRecorder) ResetQueue(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetQueue", reflect.TypeOf((*MockClient)(nil).ResetQueue), varargs...) } // RestoreDynamicConfig mocks base method. func (m *MockClient) RestoreDynamicConfig(arg0 context.Context, arg1 *types.RestoreDynamicConfigRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RestoreDynamicConfig", varargs...) ret0, _ := ret[0].(error) return ret0 } // RestoreDynamicConfig indicates an expected call of RestoreDynamicConfig. func (mr *MockClientMockRecorder) RestoreDynamicConfig(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestoreDynamicConfig", reflect.TypeOf((*MockClient)(nil).RestoreDynamicConfig), varargs...) } // UpdateDomainAsyncWorkflowConfiguraton mocks base method. func (m *MockClient) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (*types.UpdateDomainAsyncWorkflowConfiguratonResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, request} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "UpdateDomainAsyncWorkflowConfiguraton", varargs...) ret0, _ := ret[0].(*types.UpdateDomainAsyncWorkflowConfiguratonResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomainAsyncWorkflowConfiguraton indicates an expected call of UpdateDomainAsyncWorkflowConfiguraton. func (mr *MockClientMockRecorder) UpdateDomainAsyncWorkflowConfiguraton(ctx, request any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, request}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomainAsyncWorkflowConfiguraton", reflect.TypeOf((*MockClient)(nil).UpdateDomainAsyncWorkflowConfiguraton), varargs...) } // UpdateDomainIsolationGroups mocks base method. func (m *MockClient) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (*types.UpdateDomainIsolationGroupsResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, request} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "UpdateDomainIsolationGroups", varargs...) ret0, _ := ret[0].(*types.UpdateDomainIsolationGroupsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomainIsolationGroups indicates an expected call of UpdateDomainIsolationGroups. func (mr *MockClientMockRecorder) UpdateDomainIsolationGroups(ctx, request any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, request}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomainIsolationGroups", reflect.TypeOf((*MockClient)(nil).UpdateDomainIsolationGroups), varargs...) } // UpdateDynamicConfig mocks base method. func (m *MockClient) UpdateDynamicConfig(arg0 context.Context, arg1 *types.UpdateDynamicConfigRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "UpdateDynamicConfig", varargs...) ret0, _ := ret[0].(error) return ret0 } // UpdateDynamicConfig indicates an expected call of UpdateDynamicConfig. func (mr *MockClientMockRecorder) UpdateDynamicConfig(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDynamicConfig", reflect.TypeOf((*MockClient)(nil).UpdateDynamicConfig), varargs...) } // UpdateGlobalIsolationGroups mocks base method. func (m *MockClient) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (*types.UpdateGlobalIsolationGroupsResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, request} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "UpdateGlobalIsolationGroups", varargs...) ret0, _ := ret[0].(*types.UpdateGlobalIsolationGroupsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateGlobalIsolationGroups indicates an expected call of UpdateGlobalIsolationGroups. func (mr *MockClientMockRecorder) UpdateGlobalIsolationGroups(ctx, request any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, request}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGlobalIsolationGroups", reflect.TypeOf((*MockClient)(nil).UpdateGlobalIsolationGroups), varargs...) } // UpdateTaskListPartitionConfig mocks base method. func (m *MockClient) UpdateTaskListPartitionConfig(ctx context.Context, request *types.UpdateTaskListPartitionConfigRequest, opts ...yarpc.CallOption) (*types.UpdateTaskListPartitionConfigResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, request} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "UpdateTaskListPartitionConfig", varargs...) ret0, _ := ret[0].(*types.UpdateTaskListPartitionConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskListPartitionConfig indicates an expected call of UpdateTaskListPartitionConfig. func (mr *MockClientMockRecorder) UpdateTaskListPartitionConfig(ctx, request any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, request}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListPartitionConfig", reflect.TypeOf((*MockClient)(nil).UpdateTaskListPartitionConfig), varargs...) } ================================================ FILE: client/clientBean.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination clientBean_mock.go -self_package github.com/uber/cadence/client package client import ( "fmt" "sync" "sync/atomic" "go.uber.org/yarpc" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/client/wrappers/timeout" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) type ( // Bean in an collection of clients Bean interface { GetHistoryClient() history.Client GetHistoryPeers() history.PeerResolver GetMatchingClient(domainIDToName DomainIDToNameFunc) (matching.Client, error) GetFrontendClient() frontend.Client GetShardDistributorClient() sharddistributor.Client GetShardDistributorExecutorClient() executorclient.Client GetRemoteAdminClient(cluster string) (admin.Client, error) SetRemoteAdminClient(cluster string, client admin.Client) GetRemoteFrontendClient(cluster string) (frontend.Client, error) } clientBeanImpl struct { sync.Mutex historyClient history.Client historyPeers history.PeerResolver matchingClient atomic.Value frontendClient frontend.Client shardDistributorClient sharddistributor.Client shardDistributorExecutorClient executorclient.Client remoteAdminClients map[string]admin.Client remoteFrontendClients map[string]frontend.Client factory Factory } ) // NewClientBean provides a collection of clients func NewClientBean(factory Factory, dispatcher *yarpc.Dispatcher, clusterMetadata cluster.Metadata) (Bean, error) { historyClient, historyPeers, err := factory.NewHistoryClient() if err != nil { return nil, err } remoteAdminClients := map[string]admin.Client{} remoteFrontendClients := map[string]frontend.Client{} for clusterName := range clusterMetadata.GetEnabledClusterInfo() { clientConfig := dispatcher.ClientConfig(clusterName) adminClient, err := factory.NewAdminClientWithTimeoutAndConfig( clientConfig, timeout.AdminDefaultTimeout, timeout.AdminDefaultLargeTimeout, ) if err != nil { return nil, err } frontendClient, err := factory.NewFrontendClientWithTimeoutAndConfig( clientConfig, timeout.FrontendDefaultTimeout, timeout.FrontendDefaultLongPollTimeout, ) if err != nil { return nil, err } remoteAdminClients[clusterName] = adminClient remoteFrontendClients[clusterName] = frontendClient } shardDistributorClient, err := factory.NewShardDistributorClient() if err != nil { return nil, err } shardDistributorExecutorClient, err := factory.NewShardDistributorExecutorClient() if err != nil { return nil, err } return &clientBeanImpl{ factory: factory, historyClient: historyClient, historyPeers: historyPeers, frontendClient: remoteFrontendClients[clusterMetadata.GetCurrentClusterName()], shardDistributorClient: shardDistributorClient, shardDistributorExecutorClient: shardDistributorExecutorClient, remoteAdminClients: remoteAdminClients, remoteFrontendClients: remoteFrontendClients, }, nil } func (h *clientBeanImpl) GetHistoryClient() history.Client { return h.historyClient } func (h *clientBeanImpl) GetHistoryPeers() history.PeerResolver { return h.historyPeers } func (h *clientBeanImpl) GetMatchingClient(domainIDToName DomainIDToNameFunc) (matching.Client, error) { if client := h.matchingClient.Load(); client != nil { return client.(matching.Client), nil } return h.lazyInitMatchingClient(domainIDToName) } func (h *clientBeanImpl) GetFrontendClient() frontend.Client { return h.frontendClient } func (h *clientBeanImpl) GetShardDistributorClient() sharddistributor.Client { return h.shardDistributorClient } func (h *clientBeanImpl) GetShardDistributorExecutorClient() executorclient.Client { return h.shardDistributorExecutorClient } func (h *clientBeanImpl) GetRemoteAdminClient(cluster string) (admin.Client, error) { client, ok := h.remoteAdminClients[cluster] if !ok { return nil, fmt.Errorf("unknown cluster name: %v with given cluster client map: %v", cluster, h.remoteAdminClients) } return client, nil } func (h *clientBeanImpl) SetRemoteAdminClient( cluster string, client admin.Client, ) { h.remoteAdminClients[cluster] = client } func (h *clientBeanImpl) GetRemoteFrontendClient(cluster string) (frontend.Client, error) { client, ok := h.remoteFrontendClients[cluster] if !ok { return nil, fmt.Errorf("unknown cluster name: %v with given cluster client map: %v", cluster, h.remoteFrontendClients) } return client, nil } func (h *clientBeanImpl) lazyInitMatchingClient(domainIDToName DomainIDToNameFunc) (matching.Client, error) { h.Lock() defer h.Unlock() if cached := h.matchingClient.Load(); cached != nil { return cached.(matching.Client), nil } client, err := h.factory.NewMatchingClient(domainIDToName) if err != nil { return nil, err } h.matchingClient.Store(client) return client, nil } ================================================ FILE: client/clientBean_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: clientBean.go // // Generated by this command: // // mockgen -package client -source clientBean.go -destination clientBean_mock.go -self_package github.com/uber/cadence/client // // Package client is a generated GoMock package. package client import ( reflect "reflect" gomock "go.uber.org/mock/gomock" admin "github.com/uber/cadence/client/admin" frontend "github.com/uber/cadence/client/frontend" history "github.com/uber/cadence/client/history" matching "github.com/uber/cadence/client/matching" sharddistributor "github.com/uber/cadence/client/sharddistributor" executorclient "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) // MockBean is a mock of Bean interface. type MockBean struct { ctrl *gomock.Controller recorder *MockBeanMockRecorder isgomock struct{} } // MockBeanMockRecorder is the mock recorder for MockBean. type MockBeanMockRecorder struct { mock *MockBean } // NewMockBean creates a new mock instance. func NewMockBean(ctrl *gomock.Controller) *MockBean { mock := &MockBean{ctrl: ctrl} mock.recorder = &MockBeanMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockBean) EXPECT() *MockBeanMockRecorder { return m.recorder } // GetFrontendClient mocks base method. func (m *MockBean) GetFrontendClient() frontend.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFrontendClient") ret0, _ := ret[0].(frontend.Client) return ret0 } // GetFrontendClient indicates an expected call of GetFrontendClient. func (mr *MockBeanMockRecorder) GetFrontendClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFrontendClient", reflect.TypeOf((*MockBean)(nil).GetFrontendClient)) } // GetHistoryClient mocks base method. func (m *MockBean) GetHistoryClient() history.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryClient") ret0, _ := ret[0].(history.Client) return ret0 } // GetHistoryClient indicates an expected call of GetHistoryClient. func (mr *MockBeanMockRecorder) GetHistoryClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryClient", reflect.TypeOf((*MockBean)(nil).GetHistoryClient)) } // GetHistoryPeers mocks base method. func (m *MockBean) GetHistoryPeers() history.PeerResolver { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryPeers") ret0, _ := ret[0].(history.PeerResolver) return ret0 } // GetHistoryPeers indicates an expected call of GetHistoryPeers. func (mr *MockBeanMockRecorder) GetHistoryPeers() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryPeers", reflect.TypeOf((*MockBean)(nil).GetHistoryPeers)) } // GetMatchingClient mocks base method. func (m *MockBean) GetMatchingClient(domainIDToName DomainIDToNameFunc) (matching.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMatchingClient", domainIDToName) ret0, _ := ret[0].(matching.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMatchingClient indicates an expected call of GetMatchingClient. func (mr *MockBeanMockRecorder) GetMatchingClient(domainIDToName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMatchingClient", reflect.TypeOf((*MockBean)(nil).GetMatchingClient), domainIDToName) } // GetRemoteAdminClient mocks base method. func (m *MockBean) GetRemoteAdminClient(cluster string) (admin.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRemoteAdminClient", cluster) ret0, _ := ret[0].(admin.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRemoteAdminClient indicates an expected call of GetRemoteAdminClient. func (mr *MockBeanMockRecorder) GetRemoteAdminClient(cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemoteAdminClient", reflect.TypeOf((*MockBean)(nil).GetRemoteAdminClient), cluster) } // GetRemoteFrontendClient mocks base method. func (m *MockBean) GetRemoteFrontendClient(cluster string) (frontend.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRemoteFrontendClient", cluster) ret0, _ := ret[0].(frontend.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRemoteFrontendClient indicates an expected call of GetRemoteFrontendClient. func (mr *MockBeanMockRecorder) GetRemoteFrontendClient(cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemoteFrontendClient", reflect.TypeOf((*MockBean)(nil).GetRemoteFrontendClient), cluster) } // GetShardDistributorClient mocks base method. func (m *MockBean) GetShardDistributorClient() sharddistributor.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardDistributorClient") ret0, _ := ret[0].(sharddistributor.Client) return ret0 } // GetShardDistributorClient indicates an expected call of GetShardDistributorClient. func (mr *MockBeanMockRecorder) GetShardDistributorClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardDistributorClient", reflect.TypeOf((*MockBean)(nil).GetShardDistributorClient)) } // GetShardDistributorExecutorClient mocks base method. func (m *MockBean) GetShardDistributorExecutorClient() executorclient.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardDistributorExecutorClient") ret0, _ := ret[0].(executorclient.Client) return ret0 } // GetShardDistributorExecutorClient indicates an expected call of GetShardDistributorExecutorClient. func (mr *MockBeanMockRecorder) GetShardDistributorExecutorClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardDistributorExecutorClient", reflect.TypeOf((*MockBean)(nil).GetShardDistributorExecutorClient)) } // SetRemoteAdminClient mocks base method. func (m *MockBean) SetRemoteAdminClient(cluster string, client admin.Client) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetRemoteAdminClient", cluster, client) } // SetRemoteAdminClient indicates an expected call of SetRemoteAdminClient. func (mr *MockBeanMockRecorder) SetRemoteAdminClient(cluster, client any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetRemoteAdminClient", reflect.TypeOf((*MockBean)(nil).SetRemoteAdminClient), cluster, client) } ================================================ FILE: client/clientfactory.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package client import ( "fmt" "time" adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc/api/transport" "github.com/uber/cadence/.gen/go/admin/adminserviceclient" "github.com/uber/cadence/.gen/go/cadence/workflowserviceclient" "github.com/uber/cadence/.gen/go/history/historyserviceclient" "github.com/uber/cadence/.gen/go/matching/matchingserviceclient" historyv1 "github.com/uber/cadence/.gen/proto/history/v1" matchingv1 "github.com/uber/cadence/.gen/proto/matching/v1" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/client/wrappers/errorinjectors" "github.com/uber/cadence/client/wrappers/grpc" "github.com/uber/cadence/client/wrappers/metered" "github.com/uber/cadence/client/wrappers/thrift" timeoutwrapper "github.com/uber/cadence/client/wrappers/timeout" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) type ( // Factory can be used to create RPC clients for cadence services Factory interface { NewHistoryClient() (history.Client, history.PeerResolver, error) NewMatchingClient(domainIDToName DomainIDToNameFunc) (matching.Client, error) NewHistoryClientWithTimeout(timeout time.Duration) (history.Client, history.PeerResolver, error) NewMatchingClientWithTimeout(domainIDToName DomainIDToNameFunc, timeout time.Duration, longPollTimeout time.Duration) (matching.Client, error) NewAdminClientWithTimeoutAndConfig(config transport.ClientConfig, timeout time.Duration, largeTimeout time.Duration) (admin.Client, error) NewFrontendClientWithTimeoutAndConfig(config transport.ClientConfig, timeout time.Duration, longPollTimeout time.Duration) (frontend.Client, error) NewShardDistributorClient() (sharddistributor.Client, error) NewShardDistributorClientWithTimeout(timeout time.Duration) (sharddistributor.Client, error) NewShardDistributorExecutorClient() (executorclient.Client, error) } // DomainIDToNameFunc maps a domainID to domain name. Returns error when mapping is not possible. DomainIDToNameFunc func(string) (string, error) rpcClientFactory struct { rpcFactory rpc.Factory resolver membership.Resolver metricsClient metrics.Client dynConfig *dynamicconfig.Collection numberOfHistoryShards int allIsolationGroups func() []string logger log.Logger } ) // NewRPCClientFactory creates an instance of client factory that knows how to dispatch RPC calls. func NewRPCClientFactory( rpcFactory rpc.Factory, resolver membership.Resolver, metricsClient metrics.Client, dc *dynamicconfig.Collection, numberOfHistoryShards int, allIsolationGroups func() []string, logger log.Logger, ) Factory { return &rpcClientFactory{ rpcFactory: rpcFactory, resolver: resolver, metricsClient: metricsClient, dynConfig: dc, numberOfHistoryShards: numberOfHistoryShards, allIsolationGroups: allIsolationGroups, logger: logger, } } func (cf *rpcClientFactory) NewHistoryClient() (history.Client, history.PeerResolver, error) { return cf.NewHistoryClientWithTimeout(timeoutwrapper.HistoryDefaultTimeout) } func (cf *rpcClientFactory) NewMatchingClient(domainIDToName DomainIDToNameFunc) (matching.Client, error) { return cf.NewMatchingClientWithTimeout(domainIDToName, timeoutwrapper.MatchingDefaultTimeout, timeoutwrapper.MatchingDefaultLongPollTimeout) } func (cf *rpcClientFactory) NewHistoryClientWithTimeout(timeout time.Duration) (history.Client, history.PeerResolver, error) { var rawClient history.Client var namedPort = membership.PortTchannel outboundConfig := cf.rpcFactory.GetDispatcher().ClientConfig(service.History) if rpc.IsGRPCOutbound(outboundConfig) { rawClient = grpc.NewHistoryClient(historyv1.NewHistoryAPIYARPCClient(outboundConfig)) namedPort = membership.PortGRPC } else { rawClient = thrift.NewHistoryClient(historyserviceclient.New(outboundConfig)) } peerResolver := history.NewPeerResolver(cf.numberOfHistoryShards, cf.resolver, namedPort) client := history.NewClient( cf.numberOfHistoryShards, cf.rpcFactory.GetMaxMessageSize(), rawClient, peerResolver, cf.logger, ) if errorRate := cf.dynConfig.GetFloat64Property(dynamicproperties.HistoryErrorInjectionRate)(); errorRate != 0 { client = errorinjectors.NewHistoryClient(client, errorRate, cf.logger) } if cf.metricsClient != nil { client = metered.NewHistoryClient(client, cf.metricsClient) } client = timeoutwrapper.NewHistoryClient(client, timeout) return client, peerResolver, nil } func (cf *rpcClientFactory) NewMatchingClientWithTimeout( domainIDToName DomainIDToNameFunc, timeout time.Duration, longPollTimeout time.Duration, ) (matching.Client, error) { var rawClient matching.Client var namedPort = membership.PortTchannel outboundConfig := cf.rpcFactory.GetDispatcher().ClientConfig(service.Matching) if rpc.IsGRPCOutbound(outboundConfig) { rawClient = grpc.NewMatchingClient(matchingv1.NewMatchingAPIYARPCClient(outboundConfig)) namedPort = membership.PortGRPC } else { rawClient = thrift.NewMatchingClient(matchingserviceclient.New(outboundConfig)) } peerResolver := matching.NewPeerResolver(cf.resolver, namedPort) partitionConfigProvider := matching.NewPartitionConfigProvider(cf.logger, cf.metricsClient, domainIDToName, cf.dynConfig) defaultLoadBalancer := matching.NewLoadBalancer(partitionConfigProvider) roundRobinLoadBalancer := matching.NewRoundRobinLoadBalancer(partitionConfigProvider) weightedLoadBalancer := matching.NewWeightedLoadBalancer(roundRobinLoadBalancer, partitionConfigProvider, cf.logger) igLoadBalancer := matching.NewIsolationLoadBalancer(weightedLoadBalancer, partitionConfigProvider, domainIDToName, cf.dynConfig) loadBalancers := map[string]matching.LoadBalancer{ "random": defaultLoadBalancer, "round-robin": roundRobinLoadBalancer, "weighted": weightedLoadBalancer, "isolation": igLoadBalancer, } client := matching.NewClient( rawClient, peerResolver, matching.NewMultiLoadBalancer(defaultLoadBalancer, loadBalancers, domainIDToName, cf.dynConfig, cf.logger), partitionConfigProvider, ) client = timeoutwrapper.NewMatchingClient(client, longPollTimeout, timeout) if errorRate := cf.dynConfig.GetFloat64Property(dynamicproperties.MatchingErrorInjectionRate)(); errorRate != 0 { client = errorinjectors.NewMatchingClient(client, errorRate, cf.logger) } if cf.metricsClient != nil { client = metered.NewMatchingClient(client, cf.metricsClient) } return client, nil } func (cf *rpcClientFactory) NewAdminClientWithTimeoutAndConfig( config transport.ClientConfig, timeout time.Duration, largeTimeout time.Duration, ) (admin.Client, error) { var client admin.Client if rpc.IsGRPCOutbound(config) { client = grpc.NewAdminClient(adminv1.NewAdminAPIYARPCClient(config)) } else { client = thrift.NewAdminClient(adminserviceclient.New(config)) } client = timeoutwrapper.NewAdminClient(client, largeTimeout, timeout) if errorRate := cf.dynConfig.GetFloat64Property(dynamicproperties.AdminErrorInjectionRate)(); errorRate != 0 { client = errorinjectors.NewAdminClient(client, errorRate, cf.logger) } if cf.metricsClient != nil { client = metered.NewAdminClient(client, cf.metricsClient) } return client, nil } func (cf *rpcClientFactory) NewFrontendClientWithTimeoutAndConfig( config transport.ClientConfig, timeout time.Duration, longPollTimeout time.Duration, ) (frontend.Client, error) { var client frontend.Client if rpc.IsGRPCOutbound(config) { client = grpc.NewFrontendClient( apiv1.NewDomainAPIYARPCClient(config), apiv1.NewWorkflowAPIYARPCClient(config), apiv1.NewWorkerAPIYARPCClient(config), apiv1.NewVisibilityAPIYARPCClient(config), ) } else { client = thrift.NewFrontendClient(workflowserviceclient.New(config)) } client = timeoutwrapper.NewFrontendClient(client, longPollTimeout, timeout) if errorRate := cf.dynConfig.GetFloat64Property(dynamicproperties.FrontendErrorInjectionRate)(); errorRate != 0 { client = errorinjectors.NewFrontendClient(client, errorRate, cf.logger) } if cf.metricsClient != nil { client = metered.NewFrontendClient(client, cf.metricsClient) } return client, nil } func (cf *rpcClientFactory) NewShardDistributorClient() (sharddistributor.Client, error) { return cf.NewShardDistributorClientWithTimeout(timeoutwrapper.ShardDistributorDefaultTimeout) } func (cf *rpcClientFactory) NewShardDistributorClientWithTimeout( timeout time.Duration, ) (sharddistributor.Client, error) { outboundConfig, ok := cf.rpcFactory.GetDispatcher().OutboundConfig(service.ShardDistributor) // If no outbound config is found, it means the service is not enabled, we just return nil as we don't want to // break existing configs. if !ok { return nil, nil } if !rpc.IsGRPCOutbound(outboundConfig) { return nil, fmt.Errorf("shard distributor client does not support non-GRPC outbound") } client := grpc.NewShardDistributorClient( sharddistributorv1.NewShardDistributorAPIYARPCClient(outboundConfig), ) client = timeoutwrapper.NewShardDistributorClient(client, timeout) if errorRate := cf.dynConfig.GetFloat64Property(dynamicproperties.ShardDistributorErrorInjectionRate)(); errorRate != 0 { client = errorinjectors.NewShardDistributorClient(client, errorRate, cf.logger) } if cf.metricsClient != nil { client = metered.NewShardDistributorClient(client, cf.metricsClient) } return client, nil } func (cf *rpcClientFactory) NewShardDistributorExecutorClient() (executorclient.Client, error) { outboundConfig, ok := cf.rpcFactory.GetDispatcher().OutboundConfig(service.ShardDistributor) // If no outbound config is found, it means the service is not enabled, we just return nil as we don't want to // break existing configs. if !ok { return nil, nil } if !rpc.IsGRPCOutbound(outboundConfig) { return nil, fmt.Errorf("shard distributor client does not support non-GRPC outbound") } client := grpc.NewShardDistributorExecutorClient(sharddistributorv1.NewShardDistributorExecutorAPIYARPCClient(outboundConfig)) if errorRate := cf.dynConfig.GetFloat64Property(dynamicproperties.ShardDistributorErrorInjectionRate)(); errorRate != 0 { client = errorinjectors.NewShardDistributorExecutorClient(client, errorRate, cf.logger) } if cf.metricsClient != nil { client = metered.NewShardDistributorExecutorClient(client, cf.metricsClient) } return client, nil } ================================================ FILE: client/frontend/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package frontend import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/client/frontend //go:generate gowrap gen -g -p . -i Client -t ../templates/retry.tmpl -o ../wrappers/retryable/frontend_generated.go -v client=Frontend //go:generate gowrap gen -g -p . -i Client -t ../templates/metered.tmpl -o ../wrappers/metered/frontend_generated.go -v client=Frontend //go:generate gowrap gen -g -p . -i Client -t ../templates/errorinjectors.tmpl -o ../wrappers/errorinjectors/frontend_generated.go -v client=Frontend //go:generate gowrap gen -g -p . -i Client -t ../templates/grpc.tmpl -o ../wrappers/grpc/frontend_generated.go -v client=Frontend -v package=apiv1 -v path=github.com/uber/cadence-idl/go/proto/api/v1 -v prefix= //go:generate gowrap gen -g -p . -i Client -t ../templates/thrift.tmpl -o ../wrappers/thrift/frontend_generated.go -v client=Frontend -v prefix= //go:generate gowrap gen -g -p . -i Client -t ../templates/timeout.tmpl -o ../wrappers/timeout/frontend_generated.go -v client=Frontend // Client is the interface exposed by frontend service client type Client interface { CountWorkflowExecutions(context.Context, *types.CountWorkflowExecutionsRequest, ...yarpc.CallOption) (*types.CountWorkflowExecutionsResponse, error) DeleteDomain(context.Context, *types.DeleteDomainRequest, ...yarpc.CallOption) error DeprecateDomain(context.Context, *types.DeprecateDomainRequest, ...yarpc.CallOption) error DescribeDomain(context.Context, *types.DescribeDomainRequest, ...yarpc.CallOption) (*types.DescribeDomainResponse, error) DescribeTaskList(context.Context, *types.DescribeTaskListRequest, ...yarpc.CallOption) (*types.DescribeTaskListResponse, error) DescribeWorkflowExecution(context.Context, *types.DescribeWorkflowExecutionRequest, ...yarpc.CallOption) (*types.DescribeWorkflowExecutionResponse, error) DiagnoseWorkflowExecution(context.Context, *types.DiagnoseWorkflowExecutionRequest, ...yarpc.CallOption) (*types.DiagnoseWorkflowExecutionResponse, error) GetClusterInfo(context.Context, ...yarpc.CallOption) (*types.ClusterInfo, error) GetSearchAttributes(context.Context, ...yarpc.CallOption) (*types.GetSearchAttributesResponse, error) GetWorkflowExecutionHistory(context.Context, *types.GetWorkflowExecutionHistoryRequest, ...yarpc.CallOption) (*types.GetWorkflowExecutionHistoryResponse, error) ListArchivedWorkflowExecutions(context.Context, *types.ListArchivedWorkflowExecutionsRequest, ...yarpc.CallOption) (*types.ListArchivedWorkflowExecutionsResponse, error) ListClosedWorkflowExecutions(context.Context, *types.ListClosedWorkflowExecutionsRequest, ...yarpc.CallOption) (*types.ListClosedWorkflowExecutionsResponse, error) ListDomains(context.Context, *types.ListDomainsRequest, ...yarpc.CallOption) (*types.ListDomainsResponse, error) ListOpenWorkflowExecutions(context.Context, *types.ListOpenWorkflowExecutionsRequest, ...yarpc.CallOption) (*types.ListOpenWorkflowExecutionsResponse, error) ListTaskListPartitions(context.Context, *types.ListTaskListPartitionsRequest, ...yarpc.CallOption) (*types.ListTaskListPartitionsResponse, error) GetTaskListsByDomain(context.Context, *types.GetTaskListsByDomainRequest, ...yarpc.CallOption) (*types.GetTaskListsByDomainResponse, error) RefreshWorkflowTasks(context.Context, *types.RefreshWorkflowTasksRequest, ...yarpc.CallOption) error ListWorkflowExecutions(context.Context, *types.ListWorkflowExecutionsRequest, ...yarpc.CallOption) (*types.ListWorkflowExecutionsResponse, error) PollForActivityTask(context.Context, *types.PollForActivityTaskRequest, ...yarpc.CallOption) (*types.PollForActivityTaskResponse, error) PollForDecisionTask(context.Context, *types.PollForDecisionTaskRequest, ...yarpc.CallOption) (*types.PollForDecisionTaskResponse, error) QueryWorkflow(context.Context, *types.QueryWorkflowRequest, ...yarpc.CallOption) (*types.QueryWorkflowResponse, error) RecordActivityTaskHeartbeat(context.Context, *types.RecordActivityTaskHeartbeatRequest, ...yarpc.CallOption) (*types.RecordActivityTaskHeartbeatResponse, error) RecordActivityTaskHeartbeatByID(context.Context, *types.RecordActivityTaskHeartbeatByIDRequest, ...yarpc.CallOption) (*types.RecordActivityTaskHeartbeatResponse, error) RegisterDomain(context.Context, *types.RegisterDomainRequest, ...yarpc.CallOption) error RequestCancelWorkflowExecution(context.Context, *types.RequestCancelWorkflowExecutionRequest, ...yarpc.CallOption) error ResetStickyTaskList(context.Context, *types.ResetStickyTaskListRequest, ...yarpc.CallOption) (*types.ResetStickyTaskListResponse, error) ResetWorkflowExecution(context.Context, *types.ResetWorkflowExecutionRequest, ...yarpc.CallOption) (*types.ResetWorkflowExecutionResponse, error) RespondActivityTaskCanceled(context.Context, *types.RespondActivityTaskCanceledRequest, ...yarpc.CallOption) error RespondActivityTaskCanceledByID(context.Context, *types.RespondActivityTaskCanceledByIDRequest, ...yarpc.CallOption) error RespondActivityTaskCompleted(context.Context, *types.RespondActivityTaskCompletedRequest, ...yarpc.CallOption) error RespondActivityTaskCompletedByID(context.Context, *types.RespondActivityTaskCompletedByIDRequest, ...yarpc.CallOption) error RespondActivityTaskFailed(context.Context, *types.RespondActivityTaskFailedRequest, ...yarpc.CallOption) error RespondActivityTaskFailedByID(context.Context, *types.RespondActivityTaskFailedByIDRequest, ...yarpc.CallOption) error RespondDecisionTaskCompleted(context.Context, *types.RespondDecisionTaskCompletedRequest, ...yarpc.CallOption) (*types.RespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed(context.Context, *types.RespondDecisionTaskFailedRequest, ...yarpc.CallOption) error RespondQueryTaskCompleted(context.Context, *types.RespondQueryTaskCompletedRequest, ...yarpc.CallOption) error RestartWorkflowExecution(context.Context, *types.RestartWorkflowExecutionRequest, ...yarpc.CallOption) (*types.RestartWorkflowExecutionResponse, error) ScanWorkflowExecutions(context.Context, *types.ListWorkflowExecutionsRequest, ...yarpc.CallOption) (*types.ListWorkflowExecutionsResponse, error) SignalWithStartWorkflowExecution(context.Context, *types.SignalWithStartWorkflowExecutionRequest, ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) SignalWithStartWorkflowExecutionAsync(context.Context, *types.SignalWithStartWorkflowExecutionAsyncRequest, ...yarpc.CallOption) (*types.SignalWithStartWorkflowExecutionAsyncResponse, error) SignalWorkflowExecution(context.Context, *types.SignalWorkflowExecutionRequest, ...yarpc.CallOption) error StartWorkflowExecution(context.Context, *types.StartWorkflowExecutionRequest, ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) StartWorkflowExecutionAsync(context.Context, *types.StartWorkflowExecutionAsyncRequest, ...yarpc.CallOption) (*types.StartWorkflowExecutionAsyncResponse, error) TerminateWorkflowExecution(context.Context, *types.TerminateWorkflowExecutionRequest, ...yarpc.CallOption) error UpdateDomain(context.Context, *types.UpdateDomainRequest, ...yarpc.CallOption) (*types.UpdateDomainResponse, error) FailoverDomain(context.Context, *types.FailoverDomainRequest, ...yarpc.CallOption) (*types.FailoverDomainResponse, error) ListFailoverHistory(context.Context, *types.ListFailoverHistoryRequest, ...yarpc.CallOption) (*types.ListFailoverHistoryResponse, error) } ================================================ FILE: client/frontend/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package frontend -source interface.go -destination interface_mock.go -self_package github.com/uber/cadence/client/frontend // // Package frontend is a generated GoMock package. package frontend import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" types "github.com/uber/cadence/common/types" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // CountWorkflowExecutions mocks base method. func (m *MockClient) CountWorkflowExecutions(arg0 context.Context, arg1 *types.CountWorkflowExecutionsRequest, arg2 ...yarpc.CallOption) (*types.CountWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CountWorkflowExecutions", varargs...) ret0, _ := ret[0].(*types.CountWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CountWorkflowExecutions indicates an expected call of CountWorkflowExecutions. func (mr *MockClientMockRecorder) CountWorkflowExecutions(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountWorkflowExecutions", reflect.TypeOf((*MockClient)(nil).CountWorkflowExecutions), varargs...) } // DeleteDomain mocks base method. func (m *MockClient) DeleteDomain(arg0 context.Context, arg1 *types.DeleteDomainRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DeleteDomain", varargs...) ret0, _ := ret[0].(error) return ret0 } // DeleteDomain indicates an expected call of DeleteDomain. func (mr *MockClientMockRecorder) DeleteDomain(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDomain", reflect.TypeOf((*MockClient)(nil).DeleteDomain), varargs...) } // DeprecateDomain mocks base method. func (m *MockClient) DeprecateDomain(arg0 context.Context, arg1 *types.DeprecateDomainRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DeprecateDomain", varargs...) ret0, _ := ret[0].(error) return ret0 } // DeprecateDomain indicates an expected call of DeprecateDomain. func (mr *MockClientMockRecorder) DeprecateDomain(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeprecateDomain", reflect.TypeOf((*MockClient)(nil).DeprecateDomain), varargs...) } // DescribeDomain mocks base method. func (m *MockClient) DescribeDomain(arg0 context.Context, arg1 *types.DescribeDomainRequest, arg2 ...yarpc.CallOption) (*types.DescribeDomainResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeDomain", varargs...) ret0, _ := ret[0].(*types.DescribeDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeDomain indicates an expected call of DescribeDomain. func (mr *MockClientMockRecorder) DescribeDomain(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDomain", reflect.TypeOf((*MockClient)(nil).DescribeDomain), varargs...) } // DescribeTaskList mocks base method. func (m *MockClient) DescribeTaskList(arg0 context.Context, arg1 *types.DescribeTaskListRequest, arg2 ...yarpc.CallOption) (*types.DescribeTaskListResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeTaskList", varargs...) ret0, _ := ret[0].(*types.DescribeTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeTaskList indicates an expected call of DescribeTaskList. func (mr *MockClientMockRecorder) DescribeTaskList(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTaskList", reflect.TypeOf((*MockClient)(nil).DescribeTaskList), varargs...) } // DescribeWorkflowExecution mocks base method. func (m *MockClient) DescribeWorkflowExecution(arg0 context.Context, arg1 *types.DescribeWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.DescribeWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.DescribeWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeWorkflowExecution indicates an expected call of DescribeWorkflowExecution. func (mr *MockClientMockRecorder) DescribeWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeWorkflowExecution", reflect.TypeOf((*MockClient)(nil).DescribeWorkflowExecution), varargs...) } // DiagnoseWorkflowExecution mocks base method. func (m *MockClient) DiagnoseWorkflowExecution(arg0 context.Context, arg1 *types.DiagnoseWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.DiagnoseWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DiagnoseWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.DiagnoseWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DiagnoseWorkflowExecution indicates an expected call of DiagnoseWorkflowExecution. func (mr *MockClientMockRecorder) DiagnoseWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiagnoseWorkflowExecution", reflect.TypeOf((*MockClient)(nil).DiagnoseWorkflowExecution), varargs...) } // FailoverDomain mocks base method. func (m *MockClient) FailoverDomain(arg0 context.Context, arg1 *types.FailoverDomainRequest, arg2 ...yarpc.CallOption) (*types.FailoverDomainResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "FailoverDomain", varargs...) ret0, _ := ret[0].(*types.FailoverDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // FailoverDomain indicates an expected call of FailoverDomain. func (mr *MockClientMockRecorder) FailoverDomain(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FailoverDomain", reflect.TypeOf((*MockClient)(nil).FailoverDomain), varargs...) } // GetClusterInfo mocks base method. func (m *MockClient) GetClusterInfo(arg0 context.Context, arg1 ...yarpc.CallOption) (*types.ClusterInfo, error) { m.ctrl.T.Helper() varargs := []any{arg0} for _, a := range arg1 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetClusterInfo", varargs...) ret0, _ := ret[0].(*types.ClusterInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // GetClusterInfo indicates an expected call of GetClusterInfo. func (mr *MockClientMockRecorder) GetClusterInfo(arg0 any, arg1 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0}, arg1...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterInfo", reflect.TypeOf((*MockClient)(nil).GetClusterInfo), varargs...) } // GetSearchAttributes mocks base method. func (m *MockClient) GetSearchAttributes(arg0 context.Context, arg1 ...yarpc.CallOption) (*types.GetSearchAttributesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0} for _, a := range arg1 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetSearchAttributes", varargs...) ret0, _ := ret[0].(*types.GetSearchAttributesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSearchAttributes indicates an expected call of GetSearchAttributes. func (mr *MockClientMockRecorder) GetSearchAttributes(arg0 any, arg1 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0}, arg1...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSearchAttributes", reflect.TypeOf((*MockClient)(nil).GetSearchAttributes), varargs...) } // GetTaskListsByDomain mocks base method. func (m *MockClient) GetTaskListsByDomain(arg0 context.Context, arg1 *types.GetTaskListsByDomainRequest, arg2 ...yarpc.CallOption) (*types.GetTaskListsByDomainResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetTaskListsByDomain", varargs...) ret0, _ := ret[0].(*types.GetTaskListsByDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTaskListsByDomain indicates an expected call of GetTaskListsByDomain. func (mr *MockClientMockRecorder) GetTaskListsByDomain(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskListsByDomain", reflect.TypeOf((*MockClient)(nil).GetTaskListsByDomain), varargs...) } // GetWorkflowExecutionHistory mocks base method. func (m *MockClient) GetWorkflowExecutionHistory(arg0 context.Context, arg1 *types.GetWorkflowExecutionHistoryRequest, arg2 ...yarpc.CallOption) (*types.GetWorkflowExecutionHistoryResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetWorkflowExecutionHistory", varargs...) ret0, _ := ret[0].(*types.GetWorkflowExecutionHistoryResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetWorkflowExecutionHistory indicates an expected call of GetWorkflowExecutionHistory. func (mr *MockClientMockRecorder) GetWorkflowExecutionHistory(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowExecutionHistory", reflect.TypeOf((*MockClient)(nil).GetWorkflowExecutionHistory), varargs...) } // ListArchivedWorkflowExecutions mocks base method. func (m *MockClient) ListArchivedWorkflowExecutions(arg0 context.Context, arg1 *types.ListArchivedWorkflowExecutionsRequest, arg2 ...yarpc.CallOption) (*types.ListArchivedWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListArchivedWorkflowExecutions", varargs...) ret0, _ := ret[0].(*types.ListArchivedWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListArchivedWorkflowExecutions indicates an expected call of ListArchivedWorkflowExecutions. func (mr *MockClientMockRecorder) ListArchivedWorkflowExecutions(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListArchivedWorkflowExecutions", reflect.TypeOf((*MockClient)(nil).ListArchivedWorkflowExecutions), varargs...) } // ListClosedWorkflowExecutions mocks base method. func (m *MockClient) ListClosedWorkflowExecutions(arg0 context.Context, arg1 *types.ListClosedWorkflowExecutionsRequest, arg2 ...yarpc.CallOption) (*types.ListClosedWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListClosedWorkflowExecutions", varargs...) ret0, _ := ret[0].(*types.ListClosedWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListClosedWorkflowExecutions indicates an expected call of ListClosedWorkflowExecutions. func (mr *MockClientMockRecorder) ListClosedWorkflowExecutions(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClosedWorkflowExecutions", reflect.TypeOf((*MockClient)(nil).ListClosedWorkflowExecutions), varargs...) } // ListDomains mocks base method. func (m *MockClient) ListDomains(arg0 context.Context, arg1 *types.ListDomainsRequest, arg2 ...yarpc.CallOption) (*types.ListDomainsResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListDomains", varargs...) ret0, _ := ret[0].(*types.ListDomainsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListDomains indicates an expected call of ListDomains. func (mr *MockClientMockRecorder) ListDomains(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDomains", reflect.TypeOf((*MockClient)(nil).ListDomains), varargs...) } // ListFailoverHistory mocks base method. func (m *MockClient) ListFailoverHistory(arg0 context.Context, arg1 *types.ListFailoverHistoryRequest, arg2 ...yarpc.CallOption) (*types.ListFailoverHistoryResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListFailoverHistory", varargs...) ret0, _ := ret[0].(*types.ListFailoverHistoryResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListFailoverHistory indicates an expected call of ListFailoverHistory. func (mr *MockClientMockRecorder) ListFailoverHistory(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListFailoverHistory", reflect.TypeOf((*MockClient)(nil).ListFailoverHistory), varargs...) } // ListOpenWorkflowExecutions mocks base method. func (m *MockClient) ListOpenWorkflowExecutions(arg0 context.Context, arg1 *types.ListOpenWorkflowExecutionsRequest, arg2 ...yarpc.CallOption) (*types.ListOpenWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListOpenWorkflowExecutions", varargs...) ret0, _ := ret[0].(*types.ListOpenWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListOpenWorkflowExecutions indicates an expected call of ListOpenWorkflowExecutions. func (mr *MockClientMockRecorder) ListOpenWorkflowExecutions(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOpenWorkflowExecutions", reflect.TypeOf((*MockClient)(nil).ListOpenWorkflowExecutions), varargs...) } // ListTaskListPartitions mocks base method. func (m *MockClient) ListTaskListPartitions(arg0 context.Context, arg1 *types.ListTaskListPartitionsRequest, arg2 ...yarpc.CallOption) (*types.ListTaskListPartitionsResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListTaskListPartitions", varargs...) ret0, _ := ret[0].(*types.ListTaskListPartitionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTaskListPartitions indicates an expected call of ListTaskListPartitions. func (mr *MockClientMockRecorder) ListTaskListPartitions(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTaskListPartitions", reflect.TypeOf((*MockClient)(nil).ListTaskListPartitions), varargs...) } // ListWorkflowExecutions mocks base method. func (m *MockClient) ListWorkflowExecutions(arg0 context.Context, arg1 *types.ListWorkflowExecutionsRequest, arg2 ...yarpc.CallOption) (*types.ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListWorkflowExecutions", varargs...) ret0, _ := ret[0].(*types.ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListWorkflowExecutions indicates an expected call of ListWorkflowExecutions. func (mr *MockClientMockRecorder) ListWorkflowExecutions(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListWorkflowExecutions", reflect.TypeOf((*MockClient)(nil).ListWorkflowExecutions), varargs...) } // PollForActivityTask mocks base method. func (m *MockClient) PollForActivityTask(arg0 context.Context, arg1 *types.PollForActivityTaskRequest, arg2 ...yarpc.CallOption) (*types.PollForActivityTaskResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "PollForActivityTask", varargs...) ret0, _ := ret[0].(*types.PollForActivityTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForActivityTask indicates an expected call of PollForActivityTask. func (mr *MockClientMockRecorder) PollForActivityTask(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForActivityTask", reflect.TypeOf((*MockClient)(nil).PollForActivityTask), varargs...) } // PollForDecisionTask mocks base method. func (m *MockClient) PollForDecisionTask(arg0 context.Context, arg1 *types.PollForDecisionTaskRequest, arg2 ...yarpc.CallOption) (*types.PollForDecisionTaskResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "PollForDecisionTask", varargs...) ret0, _ := ret[0].(*types.PollForDecisionTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForDecisionTask indicates an expected call of PollForDecisionTask. func (mr *MockClientMockRecorder) PollForDecisionTask(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForDecisionTask", reflect.TypeOf((*MockClient)(nil).PollForDecisionTask), varargs...) } // QueryWorkflow mocks base method. func (m *MockClient) QueryWorkflow(arg0 context.Context, arg1 *types.QueryWorkflowRequest, arg2 ...yarpc.CallOption) (*types.QueryWorkflowResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "QueryWorkflow", varargs...) ret0, _ := ret[0].(*types.QueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // QueryWorkflow indicates an expected call of QueryWorkflow. func (mr *MockClientMockRecorder) QueryWorkflow(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockClient)(nil).QueryWorkflow), varargs...) } // RecordActivityTaskHeartbeat mocks base method. func (m *MockClient) RecordActivityTaskHeartbeat(arg0 context.Context, arg1 *types.RecordActivityTaskHeartbeatRequest, arg2 ...yarpc.CallOption) (*types.RecordActivityTaskHeartbeatResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RecordActivityTaskHeartbeat", varargs...) ret0, _ := ret[0].(*types.RecordActivityTaskHeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordActivityTaskHeartbeat indicates an expected call of RecordActivityTaskHeartbeat. func (mr *MockClientMockRecorder) RecordActivityTaskHeartbeat(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordActivityTaskHeartbeat", reflect.TypeOf((*MockClient)(nil).RecordActivityTaskHeartbeat), varargs...) } // RecordActivityTaskHeartbeatByID mocks base method. func (m *MockClient) RecordActivityTaskHeartbeatByID(arg0 context.Context, arg1 *types.RecordActivityTaskHeartbeatByIDRequest, arg2 ...yarpc.CallOption) (*types.RecordActivityTaskHeartbeatResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RecordActivityTaskHeartbeatByID", varargs...) ret0, _ := ret[0].(*types.RecordActivityTaskHeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordActivityTaskHeartbeatByID indicates an expected call of RecordActivityTaskHeartbeatByID. func (mr *MockClientMockRecorder) RecordActivityTaskHeartbeatByID(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordActivityTaskHeartbeatByID", reflect.TypeOf((*MockClient)(nil).RecordActivityTaskHeartbeatByID), varargs...) } // RefreshWorkflowTasks mocks base method. func (m *MockClient) RefreshWorkflowTasks(arg0 context.Context, arg1 *types.RefreshWorkflowTasksRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RefreshWorkflowTasks", varargs...) ret0, _ := ret[0].(error) return ret0 } // RefreshWorkflowTasks indicates an expected call of RefreshWorkflowTasks. func (mr *MockClientMockRecorder) RefreshWorkflowTasks(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshWorkflowTasks", reflect.TypeOf((*MockClient)(nil).RefreshWorkflowTasks), varargs...) } // RegisterDomain mocks base method. func (m *MockClient) RegisterDomain(arg0 context.Context, arg1 *types.RegisterDomainRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RegisterDomain", varargs...) ret0, _ := ret[0].(error) return ret0 } // RegisterDomain indicates an expected call of RegisterDomain. func (mr *MockClientMockRecorder) RegisterDomain(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterDomain", reflect.TypeOf((*MockClient)(nil).RegisterDomain), varargs...) } // RequestCancelWorkflowExecution mocks base method. func (m *MockClient) RequestCancelWorkflowExecution(arg0 context.Context, arg1 *types.RequestCancelWorkflowExecutionRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RequestCancelWorkflowExecution", varargs...) ret0, _ := ret[0].(error) return ret0 } // RequestCancelWorkflowExecution indicates an expected call of RequestCancelWorkflowExecution. func (mr *MockClientMockRecorder) RequestCancelWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestCancelWorkflowExecution", reflect.TypeOf((*MockClient)(nil).RequestCancelWorkflowExecution), varargs...) } // ResetStickyTaskList mocks base method. func (m *MockClient) ResetStickyTaskList(arg0 context.Context, arg1 *types.ResetStickyTaskListRequest, arg2 ...yarpc.CallOption) (*types.ResetStickyTaskListResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ResetStickyTaskList", varargs...) ret0, _ := ret[0].(*types.ResetStickyTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetStickyTaskList indicates an expected call of ResetStickyTaskList. func (mr *MockClientMockRecorder) ResetStickyTaskList(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetStickyTaskList", reflect.TypeOf((*MockClient)(nil).ResetStickyTaskList), varargs...) } // ResetWorkflowExecution mocks base method. func (m *MockClient) ResetWorkflowExecution(arg0 context.Context, arg1 *types.ResetWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.ResetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ResetWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.ResetWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetWorkflowExecution indicates an expected call of ResetWorkflowExecution. func (mr *MockClientMockRecorder) ResetWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetWorkflowExecution", reflect.TypeOf((*MockClient)(nil).ResetWorkflowExecution), varargs...) } // RespondActivityTaskCanceled mocks base method. func (m *MockClient) RespondActivityTaskCanceled(arg0 context.Context, arg1 *types.RespondActivityTaskCanceledRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondActivityTaskCanceled", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCanceled indicates an expected call of RespondActivityTaskCanceled. func (mr *MockClientMockRecorder) RespondActivityTaskCanceled(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCanceled", reflect.TypeOf((*MockClient)(nil).RespondActivityTaskCanceled), varargs...) } // RespondActivityTaskCanceledByID mocks base method. func (m *MockClient) RespondActivityTaskCanceledByID(arg0 context.Context, arg1 *types.RespondActivityTaskCanceledByIDRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondActivityTaskCanceledByID", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCanceledByID indicates an expected call of RespondActivityTaskCanceledByID. func (mr *MockClientMockRecorder) RespondActivityTaskCanceledByID(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCanceledByID", reflect.TypeOf((*MockClient)(nil).RespondActivityTaskCanceledByID), varargs...) } // RespondActivityTaskCompleted mocks base method. func (m *MockClient) RespondActivityTaskCompleted(arg0 context.Context, arg1 *types.RespondActivityTaskCompletedRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondActivityTaskCompleted", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCompleted indicates an expected call of RespondActivityTaskCompleted. func (mr *MockClientMockRecorder) RespondActivityTaskCompleted(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCompleted", reflect.TypeOf((*MockClient)(nil).RespondActivityTaskCompleted), varargs...) } // RespondActivityTaskCompletedByID mocks base method. func (m *MockClient) RespondActivityTaskCompletedByID(arg0 context.Context, arg1 *types.RespondActivityTaskCompletedByIDRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondActivityTaskCompletedByID", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCompletedByID indicates an expected call of RespondActivityTaskCompletedByID. func (mr *MockClientMockRecorder) RespondActivityTaskCompletedByID(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCompletedByID", reflect.TypeOf((*MockClient)(nil).RespondActivityTaskCompletedByID), varargs...) } // RespondActivityTaskFailed mocks base method. func (m *MockClient) RespondActivityTaskFailed(arg0 context.Context, arg1 *types.RespondActivityTaskFailedRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondActivityTaskFailed", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskFailed indicates an expected call of RespondActivityTaskFailed. func (mr *MockClientMockRecorder) RespondActivityTaskFailed(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskFailed", reflect.TypeOf((*MockClient)(nil).RespondActivityTaskFailed), varargs...) } // RespondActivityTaskFailedByID mocks base method. func (m *MockClient) RespondActivityTaskFailedByID(arg0 context.Context, arg1 *types.RespondActivityTaskFailedByIDRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondActivityTaskFailedByID", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskFailedByID indicates an expected call of RespondActivityTaskFailedByID. func (mr *MockClientMockRecorder) RespondActivityTaskFailedByID(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskFailedByID", reflect.TypeOf((*MockClient)(nil).RespondActivityTaskFailedByID), varargs...) } // RespondDecisionTaskCompleted mocks base method. func (m *MockClient) RespondDecisionTaskCompleted(arg0 context.Context, arg1 *types.RespondDecisionTaskCompletedRequest, arg2 ...yarpc.CallOption) (*types.RespondDecisionTaskCompletedResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondDecisionTaskCompleted", varargs...) ret0, _ := ret[0].(*types.RespondDecisionTaskCompletedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RespondDecisionTaskCompleted indicates an expected call of RespondDecisionTaskCompleted. func (mr *MockClientMockRecorder) RespondDecisionTaskCompleted(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondDecisionTaskCompleted", reflect.TypeOf((*MockClient)(nil).RespondDecisionTaskCompleted), varargs...) } // RespondDecisionTaskFailed mocks base method. func (m *MockClient) RespondDecisionTaskFailed(arg0 context.Context, arg1 *types.RespondDecisionTaskFailedRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondDecisionTaskFailed", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondDecisionTaskFailed indicates an expected call of RespondDecisionTaskFailed. func (mr *MockClientMockRecorder) RespondDecisionTaskFailed(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondDecisionTaskFailed", reflect.TypeOf((*MockClient)(nil).RespondDecisionTaskFailed), varargs...) } // RespondQueryTaskCompleted mocks base method. func (m *MockClient) RespondQueryTaskCompleted(arg0 context.Context, arg1 *types.RespondQueryTaskCompletedRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondQueryTaskCompleted", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondQueryTaskCompleted indicates an expected call of RespondQueryTaskCompleted. func (mr *MockClientMockRecorder) RespondQueryTaskCompleted(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondQueryTaskCompleted", reflect.TypeOf((*MockClient)(nil).RespondQueryTaskCompleted), varargs...) } // RestartWorkflowExecution mocks base method. func (m *MockClient) RestartWorkflowExecution(arg0 context.Context, arg1 *types.RestartWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.RestartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RestartWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.RestartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RestartWorkflowExecution indicates an expected call of RestartWorkflowExecution. func (mr *MockClientMockRecorder) RestartWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestartWorkflowExecution", reflect.TypeOf((*MockClient)(nil).RestartWorkflowExecution), varargs...) } // ScanWorkflowExecutions mocks base method. func (m *MockClient) ScanWorkflowExecutions(arg0 context.Context, arg1 *types.ListWorkflowExecutionsRequest, arg2 ...yarpc.CallOption) (*types.ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ScanWorkflowExecutions", varargs...) ret0, _ := ret[0].(*types.ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ScanWorkflowExecutions indicates an expected call of ScanWorkflowExecutions. func (mr *MockClientMockRecorder) ScanWorkflowExecutions(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScanWorkflowExecutions", reflect.TypeOf((*MockClient)(nil).ScanWorkflowExecutions), varargs...) } // SignalWithStartWorkflowExecution mocks base method. func (m *MockClient) SignalWithStartWorkflowExecution(arg0 context.Context, arg1 *types.SignalWithStartWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SignalWithStartWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.StartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SignalWithStartWorkflowExecution indicates an expected call of SignalWithStartWorkflowExecution. func (mr *MockClientMockRecorder) SignalWithStartWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWithStartWorkflowExecution", reflect.TypeOf((*MockClient)(nil).SignalWithStartWorkflowExecution), varargs...) } // SignalWithStartWorkflowExecutionAsync mocks base method. func (m *MockClient) SignalWithStartWorkflowExecutionAsync(arg0 context.Context, arg1 *types.SignalWithStartWorkflowExecutionAsyncRequest, arg2 ...yarpc.CallOption) (*types.SignalWithStartWorkflowExecutionAsyncResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SignalWithStartWorkflowExecutionAsync", varargs...) ret0, _ := ret[0].(*types.SignalWithStartWorkflowExecutionAsyncResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SignalWithStartWorkflowExecutionAsync indicates an expected call of SignalWithStartWorkflowExecutionAsync. func (mr *MockClientMockRecorder) SignalWithStartWorkflowExecutionAsync(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWithStartWorkflowExecutionAsync", reflect.TypeOf((*MockClient)(nil).SignalWithStartWorkflowExecutionAsync), varargs...) } // SignalWorkflowExecution mocks base method. func (m *MockClient) SignalWorkflowExecution(arg0 context.Context, arg1 *types.SignalWorkflowExecutionRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SignalWorkflowExecution", varargs...) ret0, _ := ret[0].(error) return ret0 } // SignalWorkflowExecution indicates an expected call of SignalWorkflowExecution. func (mr *MockClientMockRecorder) SignalWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWorkflowExecution", reflect.TypeOf((*MockClient)(nil).SignalWorkflowExecution), varargs...) } // StartWorkflowExecution mocks base method. func (m *MockClient) StartWorkflowExecution(arg0 context.Context, arg1 *types.StartWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "StartWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.StartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // StartWorkflowExecution indicates an expected call of StartWorkflowExecution. func (mr *MockClientMockRecorder) StartWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartWorkflowExecution", reflect.TypeOf((*MockClient)(nil).StartWorkflowExecution), varargs...) } // StartWorkflowExecutionAsync mocks base method. func (m *MockClient) StartWorkflowExecutionAsync(arg0 context.Context, arg1 *types.StartWorkflowExecutionAsyncRequest, arg2 ...yarpc.CallOption) (*types.StartWorkflowExecutionAsyncResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "StartWorkflowExecutionAsync", varargs...) ret0, _ := ret[0].(*types.StartWorkflowExecutionAsyncResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // StartWorkflowExecutionAsync indicates an expected call of StartWorkflowExecutionAsync. func (mr *MockClientMockRecorder) StartWorkflowExecutionAsync(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartWorkflowExecutionAsync", reflect.TypeOf((*MockClient)(nil).StartWorkflowExecutionAsync), varargs...) } // TerminateWorkflowExecution mocks base method. func (m *MockClient) TerminateWorkflowExecution(arg0 context.Context, arg1 *types.TerminateWorkflowExecutionRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "TerminateWorkflowExecution", varargs...) ret0, _ := ret[0].(error) return ret0 } // TerminateWorkflowExecution indicates an expected call of TerminateWorkflowExecution. func (mr *MockClientMockRecorder) TerminateWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TerminateWorkflowExecution", reflect.TypeOf((*MockClient)(nil).TerminateWorkflowExecution), varargs...) } // UpdateDomain mocks base method. func (m *MockClient) UpdateDomain(arg0 context.Context, arg1 *types.UpdateDomainRequest, arg2 ...yarpc.CallOption) (*types.UpdateDomainResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "UpdateDomain", varargs...) ret0, _ := ret[0].(*types.UpdateDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MockClientMockRecorder) UpdateDomain(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MockClient)(nil).UpdateDomain), varargs...) } ================================================ FILE: client/history/client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package history import ( "context" "fmt" "slices" "sync" "go.uber.org/yarpc" "golang.org/x/sync/errgroup" "github.com/uber/cadence/common" "github.com/uber/cadence/common/future" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/common/types" ) var _ Client = (*clientImpl)(nil) type ( clientImpl struct { numberOfShards int rpcMaxSizeInBytes int // This value currently only used in GetReplicationMessage API tokenSerializer common.TaskTokenSerializer client Client peerResolver PeerResolver logger log.Logger } getReplicationMessagesWithSize struct { response *types.GetReplicationMessagesResponse size int peer string // earliestCreationTime of replication tasks of response earliestCreationTime *int64 } ) // NewClient creates a new history service TChannel client func NewClient( numberOfShards int, rpcMaxSizeInBytes int, client Client, peerResolver PeerResolver, logger log.Logger, ) Client { return &clientImpl{ numberOfShards: numberOfShards, rpcMaxSizeInBytes: rpcMaxSizeInBytes, tokenSerializer: common.NewJSONTaskTokenSerializer(), client: client, peerResolver: peerResolver, logger: logger, } } func (c *clientImpl) StartWorkflowExecution( ctx context.Context, request *types.HistoryStartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*types.StartWorkflowExecutionResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.StartRequest.WorkflowID) if err != nil { return nil, err } var response *types.StartWorkflowExecutionResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.StartWorkflowExecution(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) GetMutableState( ctx context.Context, request *types.GetMutableStateRequest, opts ...yarpc.CallOption, ) (*types.GetMutableStateResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.Execution.WorkflowID) if err != nil { return nil, err } var response *types.GetMutableStateResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.GetMutableState(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) PollMutableState( ctx context.Context, request *types.PollMutableStateRequest, opts ...yarpc.CallOption, ) (*types.PollMutableStateResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.Execution.WorkflowID) if err != nil { return nil, err } var response *types.PollMutableStateResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.PollMutableState(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) DescribeHistoryHost( ctx context.Context, request *types.DescribeHistoryHostRequest, opts ...yarpc.CallOption, ) (*types.DescribeHistoryHostResponse, error) { var err error var peer string if request.ShardIDForHost != nil { peer, err = c.peerResolver.FromShardID(int(request.GetShardIDForHost())) if err != nil { c.logger.Error("peer could not be resolved for host.", tag.Error(err), tag.ShardID(int(request.GetShardIDForHost()))) return nil, err } } else if request.ExecutionForHost != nil { peer, err = c.peerResolver.FromWorkflowID(request.ExecutionForHost.GetWorkflowID()) if err != nil { c.logger.Error("peer could not be resolved for workflow.", tag.Error(err), tag.WorkflowID(request.ExecutionForHost.GetWorkflowID())) return nil, err } } else { peer, err = c.peerResolver.FromHostAddress(request.GetHostAddress()) if err != nil { c.logger.Error("peer could not be resolved for address.", tag.Error(err), tag.Address(request.GetHostAddress())) return nil, err } } var response *types.DescribeHistoryHostResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.DescribeHistoryHost(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) RemoveTask( ctx context.Context, request *types.RemoveTaskRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromShardID(int(request.GetShardID())) if err != nil { return err } op := func(ctx context.Context, peer string) error { var err error err = c.client.RemoveTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) CloseShard( ctx context.Context, request *types.CloseShardRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromShardID(int(request.GetShardID())) if err != nil { return err } op := func(ctx context.Context, peer string) error { var err error err = c.client.CloseShard(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return err } return nil } func (c *clientImpl) ResetQueue( ctx context.Context, request *types.ResetQueueRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromShardID(int(request.GetShardID())) if err != nil { return err } op := func(ctx context.Context, peer string) error { var err error err = c.client.ResetQueue(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return err } return nil } func (c *clientImpl) DescribeQueue( ctx context.Context, request *types.DescribeQueueRequest, opts ...yarpc.CallOption, ) (*types.DescribeQueueResponse, error) { peer, err := c.peerResolver.FromShardID(int(request.GetShardID())) if err != nil { return nil, err } var response *types.DescribeQueueResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.DescribeQueue(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) DescribeMutableState( ctx context.Context, request *types.DescribeMutableStateRequest, opts ...yarpc.CallOption, ) (*types.DescribeMutableStateResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.Execution.WorkflowID) if err != nil { return nil, err } var response *types.DescribeMutableStateResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.DescribeMutableState(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) ResetStickyTaskList( ctx context.Context, request *types.HistoryResetStickyTaskListRequest, opts ...yarpc.CallOption, ) (*types.HistoryResetStickyTaskListResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.Execution.WorkflowID) if err != nil { return nil, err } var response *types.HistoryResetStickyTaskListResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.ResetStickyTaskList(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) DescribeWorkflowExecution( ctx context.Context, request *types.HistoryDescribeWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*types.DescribeWorkflowExecutionResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.Request.Execution.WorkflowID) if err != nil { return nil, err } var response *types.DescribeWorkflowExecutionResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.DescribeWorkflowExecution(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) RecordDecisionTaskStarted( ctx context.Context, request *types.RecordDecisionTaskStartedRequest, opts ...yarpc.CallOption, ) (*types.RecordDecisionTaskStartedResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.WorkflowExecution.WorkflowID) if err != nil { return nil, err } var response *types.RecordDecisionTaskStartedResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.RecordDecisionTaskStarted(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) RecordActivityTaskStarted( ctx context.Context, request *types.RecordActivityTaskStartedRequest, opts ...yarpc.CallOption, ) (*types.RecordActivityTaskStartedResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.WorkflowExecution.WorkflowID) if err != nil { return nil, err } var response *types.RecordActivityTaskStartedResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.RecordActivityTaskStarted(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) RespondDecisionTaskCompleted( ctx context.Context, request *types.HistoryRespondDecisionTaskCompletedRequest, opts ...yarpc.CallOption, ) (*types.HistoryRespondDecisionTaskCompletedResponse, error) { taskToken, err := c.tokenSerializer.Deserialize(request.CompleteRequest.TaskToken) if err != nil { return nil, err } peer, err := c.peerResolver.FromWorkflowID(taskToken.WorkflowID) if err != nil { return nil, err } var response *types.HistoryRespondDecisionTaskCompletedResponse op := func(ctx context.Context, peer string) error { response, err = c.client.RespondDecisionTaskCompleted(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) return response, err } func (c *clientImpl) RespondDecisionTaskFailed( ctx context.Context, request *types.HistoryRespondDecisionTaskFailedRequest, opts ...yarpc.CallOption, ) error { taskToken, err := c.tokenSerializer.Deserialize(request.FailedRequest.TaskToken) if err != nil { return err } peer, err := c.peerResolver.FromWorkflowID(taskToken.WorkflowID) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.RespondDecisionTaskFailed(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) RespondActivityTaskCompleted( ctx context.Context, request *types.HistoryRespondActivityTaskCompletedRequest, opts ...yarpc.CallOption, ) error { taskToken, err := c.tokenSerializer.Deserialize(request.CompleteRequest.TaskToken) if err != nil { return err } peer, err := c.peerResolver.FromWorkflowID(taskToken.WorkflowID) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.RespondActivityTaskCompleted(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) RespondActivityTaskFailed( ctx context.Context, request *types.HistoryRespondActivityTaskFailedRequest, opts ...yarpc.CallOption, ) error { taskToken, err := c.tokenSerializer.Deserialize(request.FailedRequest.TaskToken) if err != nil { return err } peer, err := c.peerResolver.FromWorkflowID(taskToken.WorkflowID) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.RespondActivityTaskFailed(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) RespondActivityTaskCanceled( ctx context.Context, request *types.HistoryRespondActivityTaskCanceledRequest, opts ...yarpc.CallOption, ) error { taskToken, err := c.tokenSerializer.Deserialize(request.CancelRequest.TaskToken) if err != nil { return err } peer, err := c.peerResolver.FromWorkflowID(taskToken.WorkflowID) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.RespondActivityTaskCanceled(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) RecordActivityTaskHeartbeat( ctx context.Context, request *types.HistoryRecordActivityTaskHeartbeatRequest, opts ...yarpc.CallOption, ) (*types.RecordActivityTaskHeartbeatResponse, error) { taskToken, err := c.tokenSerializer.Deserialize(request.HeartbeatRequest.TaskToken) if err != nil { return nil, err } peer, err := c.peerResolver.FromWorkflowID(taskToken.WorkflowID) if err != nil { return nil, err } var response *types.RecordActivityTaskHeartbeatResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.RecordActivityTaskHeartbeat(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) RequestCancelWorkflowExecution( ctx context.Context, request *types.HistoryRequestCancelWorkflowExecutionRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromWorkflowID(request.CancelRequest.WorkflowExecution.WorkflowID) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.RequestCancelWorkflowExecution(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } return c.executeWithRedirect(ctx, peer, op) } func (c *clientImpl) SignalWorkflowExecution( ctx context.Context, request *types.HistorySignalWorkflowExecutionRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromWorkflowID(request.SignalRequest.WorkflowExecution.WorkflowID) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.SignalWorkflowExecution(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) SignalWithStartWorkflowExecution( ctx context.Context, request *types.HistorySignalWithStartWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*types.StartWorkflowExecutionResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.SignalWithStartRequest.WorkflowID) if err != nil { return nil, err } var response *types.StartWorkflowExecutionResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.SignalWithStartWorkflowExecution(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, err } func (c *clientImpl) RemoveSignalMutableState( ctx context.Context, request *types.RemoveSignalMutableStateRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromWorkflowID(request.WorkflowExecution.WorkflowID) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.RemoveSignalMutableState(ctx, request, yarpc.WithShardKey(peer)) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) TerminateWorkflowExecution( ctx context.Context, request *types.HistoryTerminateWorkflowExecutionRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromWorkflowID(request.TerminateRequest.WorkflowExecution.WorkflowID) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.TerminateWorkflowExecution(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) ResetWorkflowExecution( ctx context.Context, request *types.HistoryResetWorkflowExecutionRequest, opts ...yarpc.CallOption, ) (*types.ResetWorkflowExecutionResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.ResetRequest.WorkflowExecution.WorkflowID) if err != nil { return nil, err } var response *types.ResetWorkflowExecutionResponse op := func(ctx context.Context, peer string) error { response, err = c.client.ResetWorkflowExecution(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, err } func (c *clientImpl) ScheduleDecisionTask( ctx context.Context, request *types.ScheduleDecisionTaskRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromWorkflowID(request.WorkflowExecution.WorkflowID) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.ScheduleDecisionTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) RecordChildExecutionCompleted( ctx context.Context, request *types.RecordChildExecutionCompletedRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromWorkflowID(request.WorkflowExecution.WorkflowID) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.RecordChildExecutionCompleted(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) ReplicateEventsV2( ctx context.Context, request *types.ReplicateEventsV2Request, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromWorkflowID(request.WorkflowExecution.GetWorkflowID()) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.ReplicateEventsV2(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) SyncShardStatus( ctx context.Context, request *types.SyncShardStatusRequest, opts ...yarpc.CallOption, ) error { // we do not have a workflow ID here, instead, we have something even better peer, err := c.peerResolver.FromShardID(int(request.GetShardID())) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.SyncShardStatus(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) SyncActivity( ctx context.Context, request *types.SyncActivityRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromWorkflowID(request.GetWorkflowID()) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.SyncActivity(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) QueryWorkflow( ctx context.Context, request *types.HistoryQueryWorkflowRequest, opts ...yarpc.CallOption, ) (*types.HistoryQueryWorkflowResponse, error) { peer, err := c.peerResolver.FromWorkflowID(request.GetRequest().GetExecution().GetWorkflowID()) if err != nil { return nil, err } var response *types.HistoryQueryWorkflowResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.QueryWorkflow(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) GetReplicationMessages( ctx context.Context, request *types.GetReplicationMessagesRequest, opts ...yarpc.CallOption, ) (*types.GetReplicationMessagesResponse, error) { requestsByPeer := make(map[string]*types.GetReplicationMessagesRequest) for _, token := range request.Tokens { peer, err := c.peerResolver.FromShardID(int(token.GetShardID())) if err != nil { return nil, err } if _, ok := requestsByPeer[peer]; !ok { requestsByPeer[peer] = &types.GetReplicationMessagesRequest{ ClusterName: request.ClusterName, } } req := requestsByPeer[peer] req.Tokens = append(req.Tokens, token) } g := &errgroup.Group{} var responseMutex sync.Mutex peerResponses := make([]*getReplicationMessagesWithSize, 0, len(requestsByPeer)) for peer, req := range requestsByPeer { peer, req := peer, req g.Go(func() (e error) { defer func() { log.CapturePanic(recover(), c.logger, &e) }() requestContext, cancel := common.CreateChildContext(ctx, 0.05) defer cancel() requestContext, responseInfo := rpc.ContextWithResponseInfo(requestContext) resp, err := c.client.GetReplicationMessages(requestContext, req, append(opts, yarpc.WithShardKey(peer))...) if err != nil { c.logger.Warn("Failed to get replication tasks from client", tag.Error(err), tag.ShardReplicationToken(req), ) // Returns service busy error to notify replication if _, ok := err.(*types.ServiceBusyError); ok { return err } return nil } responseMutex.Lock() peerResponses = append(peerResponses, &getReplicationMessagesWithSize{ response: resp, size: responseInfo.Size, peer: peer, earliestCreationTime: resp.GetEarliestCreationTime(), }) responseMutex.Unlock() return nil }) } if err := g.Wait(); err != nil { return nil, err } return c.buildGetReplicationMessagesResponse(peerResponses), nil } // buildGetReplicationMessagesResponse builds a new GetReplicationMessagesResponse from peer responses // The response can be partial if the total size of the response exceeds the max size. // In this case, responses with oldest replication tasks will be returned func (c *clientImpl) buildGetReplicationMessagesResponse(peerResponses []*getReplicationMessagesWithSize) *types.GetReplicationMessagesResponse { // Peers with large response can cause the response to exceed the max size. // In this case, we need to skip some peer responses to make sure the result response size is within the limit. // To prevent a replication lag in the future, we should return the response with the oldest replication task. // So we sort the peer responses by the earliest creation time of the replication task. // If the earliest creation time is the same, we compare the size of the response. // This will sure that shards with the oldest replication tasks will be processed first. sortGetReplicationMessageWithSize(peerResponses) response := &types.GetReplicationMessagesResponse{MessagesByShard: make(map[int32]*types.ReplicationMessages)} responseTotalSize := 0 rpcMaxResponseSize := c.rpcMaxSizeInBytes for _, resp := range peerResponses { if (responseTotalSize + resp.size) >= rpcMaxResponseSize { // Log shards that did not fit for debugging purposes for shardID := range resp.response.GetMessagesByShard() { c.logger.Warn("Replication messages did not fit in the response", tag.ShardID(int(shardID)), tag.Address(resp.peer), tag.ResponseSize(resp.size), tag.ResponseTotalSize(responseTotalSize), tag.ResponseMaxSize(rpcMaxResponseSize), ) } // return partial response if the response size exceeded supported max size // but continue with next peer response, as it may still fit continue } responseTotalSize += resp.size for shardID, tasks := range resp.response.GetMessagesByShard() { response.MessagesByShard[shardID] = tasks } } return response } // sortGetReplicationMessageWithSize sorts the peer responses by the earliest creation time of the replication tasks func sortGetReplicationMessageWithSize(peerResponses []*getReplicationMessagesWithSize) { slices.SortStableFunc(peerResponses, cmpGetReplicationMessagesWithSize) } // cmpGetReplicationMessagesWithSize compares // two getReplicationMessagesWithSize objects by earliest creation time // it can be used as a comparison func for slices.SortStableFunc // if a, b, or their earliestCreationTime is nil, slices.SortStableFunc will put them to the end of a slice // otherwise it will compare the earliestCreationTime of the replication tasks // if earliestCreationTime is equal, it will compare the size of the response func cmpGetReplicationMessagesWithSize(a, b *getReplicationMessagesWithSize) int { // a > b if a == nil || a.earliestCreationTime == nil { return 1 } // a < b if b == nil || b.earliestCreationTime == nil { return -1 } // if both are not nil, compare the creation time if *a.earliestCreationTime < *b.earliestCreationTime { return -1 } if *a.earliestCreationTime > *b.earliestCreationTime { return 1 } // if both equal, compare the size if a.size < b.size { return -1 } if a.size > b.size { return 1 } return 0 } func (c *clientImpl) GetDLQReplicationMessages( ctx context.Context, request *types.GetDLQReplicationMessagesRequest, opts ...yarpc.CallOption, ) (*types.GetDLQReplicationMessagesResponse, error) { // All workflow IDs are in the same shard per request workflowID := request.GetTaskInfos()[0].GetWorkflowID() peer, err := c.peerResolver.FromWorkflowID(workflowID) if err != nil { return nil, err } return c.client.GetDLQReplicationMessages( ctx, request, append(opts, yarpc.WithShardKey(peer))..., ) } func (c *clientImpl) ReapplyEvents( ctx context.Context, request *types.HistoryReapplyEventsRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromWorkflowID(request.GetRequest().GetWorkflowExecution().GetWorkflowID()) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.ReapplyEvents(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) CountDLQMessages( ctx context.Context, request *types.CountDLQMessagesRequest, opts ...yarpc.CallOption, ) (*types.HistoryCountDLQMessagesResponse, error) { peers, err := c.peerResolver.GetAllPeers() if err != nil { return nil, err } var mu sync.Mutex responses := make([]*types.HistoryCountDLQMessagesResponse, 0, len(peers)) g := &errgroup.Group{} for _, peer := range peers { peer := peer g.Go(func() (e error) { defer func() { log.CapturePanic(recover(), c.logger, &e) }() response, err := c.client.CountDLQMessages(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err == nil { mu.Lock() responses = append(responses, response) mu.Unlock() } return err }) } err = g.Wait() entries := map[types.HistoryDLQCountKey]int64{} for _, response := range responses { for key, count := range response.Entries { entries[key] = count } } return &types.HistoryCountDLQMessagesResponse{Entries: entries}, err } func (c *clientImpl) ReadDLQMessages( ctx context.Context, request *types.ReadDLQMessagesRequest, opts ...yarpc.CallOption, ) (*types.ReadDLQMessagesResponse, error) { peer, err := c.peerResolver.FromShardID(int(request.GetShardID())) if err != nil { return nil, err } return c.client.ReadDLQMessages(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } func (c *clientImpl) PurgeDLQMessages( ctx context.Context, request *types.PurgeDLQMessagesRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromShardID(int(request.GetShardID())) if err != nil { return err } return c.client.PurgeDLQMessages(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } func (c *clientImpl) MergeDLQMessages( ctx context.Context, request *types.MergeDLQMessagesRequest, opts ...yarpc.CallOption, ) (*types.MergeDLQMessagesResponse, error) { peer, err := c.peerResolver.FromShardID(int(request.GetShardID())) if err != nil { return nil, err } return c.client.MergeDLQMessages(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } func (c *clientImpl) RefreshWorkflowTasks( ctx context.Context, request *types.HistoryRefreshWorkflowTasksRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromWorkflowID(request.GetRequest().GetExecution().GetWorkflowID()) if err != nil { return err } op := func(ctx context.Context, peer string) error { return c.client.RefreshWorkflowTasks(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } err = c.executeWithRedirect(ctx, peer, op) return err } func (c *clientImpl) NotifyFailoverMarkers( ctx context.Context, request *types.NotifyFailoverMarkersRequest, opts ...yarpc.CallOption, ) error { requestsByPeer := make(map[string]*types.NotifyFailoverMarkersRequest) for _, token := range request.GetFailoverMarkerTokens() { marker := token.GetFailoverMarker() peer, err := c.peerResolver.FromDomainID(marker.GetDomainID()) if err != nil { return err } if _, ok := requestsByPeer[peer]; !ok { requestsByPeer[peer] = &types.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: []*types.FailoverMarkerToken{}, } } req := requestsByPeer[peer] req.FailoverMarkerTokens = append(req.FailoverMarkerTokens, token) } g := &errgroup.Group{} for peer, req := range requestsByPeer { peer, req := peer, req g.Go(func() (e error) { defer func() { log.CapturePanic(recover(), c.logger, &e) }() return c.client.NotifyFailoverMarkers(ctx, req, append(opts, yarpc.WithShardKey(peer))...) }) } return g.Wait() } func (c *clientImpl) GetCrossClusterTasks( ctx context.Context, request *types.GetCrossClusterTasksRequest, opts ...yarpc.CallOption, ) (*types.GetCrossClusterTasksResponse, error) { requestByPeer := make(map[string]*types.GetCrossClusterTasksRequest) for _, shardID := range request.GetShardIDs() { peer, err := c.peerResolver.FromShardID(int(shardID)) if err != nil { return nil, err } if _, ok := requestByPeer[peer]; !ok { requestByPeer[peer] = &types.GetCrossClusterTasksRequest{ TargetCluster: request.TargetCluster, } } requestByPeer[peer].ShardIDs = append(requestByPeer[peer].ShardIDs, shardID) } // preserve 5% timeout to return partial of the result if context is timing out ctx, cancel := common.CreateChildContext(ctx, 0.05) defer cancel() futureByPeer := make(map[string]future.Future, len(requestByPeer)) for peer, req := range requestByPeer { future, settable := future.NewFuture() go func(ctx context.Context, peer string, req *types.GetCrossClusterTasksRequest) { settable.Set(c.client.GetCrossClusterTasks(ctx, req, yarpc.WithShardKey(peer))) }(ctx, peer, req) futureByPeer[peer] = future } response := &types.GetCrossClusterTasksResponse{ TasksByShard: make(map[int32][]*types.CrossClusterTaskRequest), FailedCauseByShard: make(map[int32]types.GetTaskFailedCause), } for peer, future := range futureByPeer { var resp *types.GetCrossClusterTasksResponse if futureErr := future.Get(ctx, &resp); futureErr != nil { c.logger.Error("Failed to get cross cluster tasks", tag.Error(futureErr)) for _, failedShardID := range requestByPeer[peer].ShardIDs { response.FailedCauseByShard[failedShardID] = common.ConvertErrToGetTaskFailedCause(futureErr) } } else { for shardID, tasks := range resp.TasksByShard { response.TasksByShard[shardID] = tasks } for shardID, failedCause := range resp.FailedCauseByShard { response.FailedCauseByShard[shardID] = failedCause } } } // not using a waitGroup for created goroutines as once all futures are unblocked, // those goroutines will eventually be completed return response, nil } func (c *clientImpl) RespondCrossClusterTasksCompleted( ctx context.Context, request *types.RespondCrossClusterTasksCompletedRequest, opts ...yarpc.CallOption, ) (*types.RespondCrossClusterTasksCompletedResponse, error) { peer, err := c.peerResolver.FromShardID(int(request.GetShardID())) if err != nil { return nil, err } var response *types.RespondCrossClusterTasksCompletedResponse op := func(ctx context.Context, peer string) error { var err error response, err = c.client.RespondCrossClusterTasksCompleted(ctx, request, append(opts, yarpc.WithShardKey(peer))...) return err } err = c.executeWithRedirect(ctx, peer, op) if err != nil { return nil, err } return response, nil } func (c *clientImpl) GetFailoverInfo( ctx context.Context, request *types.GetFailoverInfoRequest, opts ...yarpc.CallOption, ) (*types.GetFailoverInfoResponse, error) { peer, err := c.peerResolver.FromDomainID(request.GetDomainID()) if err != nil { return nil, err } return c.client.GetFailoverInfo(ctx, request, append(opts, yarpc.WithShardKey(peer))...) } func (c *clientImpl) RatelimitUpdate(ctx context.Context, request *types.RatelimitUpdateRequest, opts ...yarpc.CallOption) (*types.RatelimitUpdateResponse, error) { if len(opts) == 0 { // unfortunately there is not really any way to ensure "must have a shard key option" // due to the closed nature of yarpc.CallOption's implementation, outside private-field-reading reflection. // // there are a few options to work around this, but they are currently rather high effort or // run into import cycles or similar. risk is low for now as there is only one caller, and likely // never will be others. return nil, fmt.Errorf("invalid arguments, missing yarpc.WithShardKey(peer) at a minimum") } // intentionally does not use peer-redirecting retries, as keys in this request // could end up on multiple different hosts after a peer change. return c.client.RatelimitUpdate(ctx, request, opts...) } func (c *clientImpl) executeWithRedirect( ctx context.Context, peer string, op func(ctx context.Context, peer string) error, ) error { var err error if ctx == nil { ctx = context.Background() } redirectLoop: for { err = common.IsValidContext(ctx) if err != nil { break redirectLoop } err = op(ctx, peer) if err != nil { if s, ok := err.(*types.ShardOwnershipLostError); ok { // TODO: consider emitting a metric for number of redirects peer, err = c.peerResolver.FromHostAddress(s.GetOwner()) if err != nil { return err } continue redirectLoop } } break redirectLoop } return err } ================================================ FILE: client/history/client_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package history import ( "context" "fmt" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) func TestNewClient(t *testing.T) { ctrl := gomock.NewController(t) numberOfShards := 10 rpcMaxSizeInBytes := 1024 client := NewMockClient(ctrl) peerResolver := NewMockPeerResolver(ctrl) logger := log.NewNoop() c := NewClient(numberOfShards, rpcMaxSizeInBytes, client, peerResolver, logger) assert.NotNil(t, c) } func TestClient_withResponse(t *testing.T) { tests := []struct { name string op func(Client) (any, error) mock func(*MockPeerResolver, *MockClient) want any wantError bool }{ { name: "StartWorkflowExecution", op: func(c Client) (any, error) { return c.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{ WorkflowID: "test-workflow", }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) }, want: &types.StartWorkflowExecutionResponse{}, }, { name: "GetMutableState", op: func(c Client) (any, error) { return c.GetMutableState(context.Background(), &types.GetMutableStateRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().GetMutableState(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.GetMutableStateResponse{}, nil).Times(1) }, want: &types.GetMutableStateResponse{}, }, { name: "PollMutableState", op: func(c Client) (any, error) { return c.PollMutableState(context.Background(), &types.PollMutableStateRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().PollMutableState(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.PollMutableStateResponse{}, nil).Times(1) }, want: &types.PollMutableStateResponse{}, }, { name: "ResetWorkflowExecution", op: func(c Client) (any, error) { return c.ResetWorkflowExecution(context.Background(), &types.HistoryResetWorkflowExecutionRequest{ ResetRequest: &types.ResetWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().ResetWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.ResetWorkflowExecutionResponse{}, nil).Times(1) }, want: &types.ResetWorkflowExecutionResponse{}, }, { name: "DescribeWorkflowExecution", op: func(c Client) (any, error) { return c.DescribeWorkflowExecution(context.Background(), &types.HistoryDescribeWorkflowExecutionRequest{ Request: &types.DescribeWorkflowExecutionRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.DescribeWorkflowExecutionResponse{}, nil).Times(1) }, want: &types.DescribeWorkflowExecutionResponse{}, }, { name: "RecordActivityTaskHeartbeat", op: func(c Client) (any, error) { return c.RecordActivityTaskHeartbeat(context.Background(), &types.HistoryRecordActivityTaskHeartbeatRequest{ HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: []byte(`{"workflowId": "test-workflow"}`), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.RecordActivityTaskHeartbeatResponse{}, nil).Times(1) }, want: &types.RecordActivityTaskHeartbeatResponse{}, }, { name: "RecordActivityTaskStarted", op: func(c Client) (any, error) { return c.RecordActivityTaskStarted(context.Background(), &types.RecordActivityTaskStartedRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RecordActivityTaskStarted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.RecordActivityTaskStartedResponse{}, nil).Times(1) }, want: &types.RecordActivityTaskStartedResponse{}, }, { name: "RecordDecisionTaskStarted", op: func(c Client) (any, error) { return c.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RecordDecisionTaskStarted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.RecordDecisionTaskStartedResponse{}, nil).Times(1) }, want: &types.RecordDecisionTaskStartedResponse{}, }, { name: "GetReplicationMessages", op: func(c Client) (any, error) { return c.GetReplicationMessages(context.Background(), &types.GetReplicationMessagesRequest{ Tokens: []*types.ReplicationToken{ { ShardID: 100, }, { ShardID: 101, }, { ShardID: 102, }, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromShardID(100).Return("test-peer-0", nil).Times(1) p.EXPECT().FromShardID(101).Return("test-peer-1", nil).Times(1) p.EXPECT().FromShardID(102).Return("test-peer-2", nil).Times(1) c.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-0")}). Return(&types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{100: {}}, }, nil).Times(1) c.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-1")}). Return(&types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{101: {}}, }, nil).Times(1) c.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-2")}). Return(&types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{102: {}}, }, nil).Times(1) }, want: &types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{ 100: {}, 101: {}, 102: {}, }, }, }, { name: "GetDLQReplicationMessages", op: func(c Client) (any, error) { return c.GetDLQReplicationMessages(context.Background(), &types.GetDLQReplicationMessagesRequest{ TaskInfos: []*types.ReplicationTaskInfo{ {WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().GetDLQReplicationMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.GetDLQReplicationMessagesResponse{}, nil).Times(1) }, want: &types.GetDLQReplicationMessagesResponse{}, }, { name: "ReadDLQMessages", op: func(c Client) (any, error) { return c.ReadDLQMessages(context.Background(), &types.ReadDLQMessagesRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().ReadDLQMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.ReadDLQMessagesResponse{}, nil).Times(1) }, want: &types.ReadDLQMessagesResponse{}, }, { name: "MergeDLQMessages", op: func(c Client) (any, error) { return c.MergeDLQMessages(context.Background(), &types.MergeDLQMessagesRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().MergeDLQMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.MergeDLQMessagesResponse{}, nil).Times(1) }, want: &types.MergeDLQMessagesResponse{}, }, { name: "GetFailoverInfo", op: func(c Client) (any, error) { return c.GetFailoverInfo(context.Background(), &types.GetFailoverInfoRequest{ DomainID: "test-domain", }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromDomainID("test-domain").Return("test-peer", nil).Times(1) c.EXPECT().GetFailoverInfo(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.GetFailoverInfoResponse{}, nil).Times(1) }, want: &types.GetFailoverInfoResponse{}, }, { name: "DescribeHistoryHost by host address", op: func(c Client) (any, error) { return c.DescribeHistoryHost(context.Background(), &types.DescribeHistoryHostRequest{ HostAddress: common.StringPtr("test-host"), }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromHostAddress("test-host").Return("test-peer", nil).Times(1) c.EXPECT().DescribeHistoryHost(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.DescribeHistoryHostResponse{}, nil).Times(1) }, want: &types.DescribeHistoryHostResponse{}, }, { name: "DescribeHistoryHost by workflow id", op: func(c Client) (any, error) { return c.DescribeHistoryHost(context.Background(), &types.DescribeHistoryHostRequest{ ExecutionForHost: &types.WorkflowExecution{WorkflowID: "test-workflow"}, HostAddress: common.StringPtr("test-host"), }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().DescribeHistoryHost(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.DescribeHistoryHostResponse{}, nil).Times(1) }, want: &types.DescribeHistoryHostResponse{}, }, { name: "DescribeHistoryHost by shard id", op: func(c Client) (any, error) { return c.DescribeHistoryHost(context.Background(), &types.DescribeHistoryHostRequest{ ShardIDForHost: common.Int32Ptr(123), ExecutionForHost: &types.WorkflowExecution{WorkflowID: "test-workflow"}, HostAddress: common.StringPtr("test-host"), }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().DescribeHistoryHost(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.DescribeHistoryHostResponse{}, nil).Times(1) }, want: &types.DescribeHistoryHostResponse{}, }, { name: "DescribeMutableState", op: func(c Client) (any, error) { return c.DescribeMutableState(context.Background(), &types.DescribeMutableStateRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().DescribeMutableState(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.DescribeMutableStateResponse{}, nil).Times(1) }, want: &types.DescribeMutableStateResponse{}, }, { name: "DescribeQueue", op: func(c Client) (any, error) { return c.DescribeQueue(context.Background(), &types.DescribeQueueRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().DescribeQueue(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.DescribeQueueResponse{}, nil).Times(1) }, want: &types.DescribeQueueResponse{}, }, { name: "CountDLQMessages", op: func(c Client) (any, error) { return c.CountDLQMessages(context.Background(), &types.CountDLQMessagesRequest{}) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().GetAllPeers().Return([]string{"test-peer-0", "test-peer-1"}, nil).Times(1) c.EXPECT().CountDLQMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-0")}). Return(&types.HistoryCountDLQMessagesResponse{ Entries: map[types.HistoryDLQCountKey]int64{ {ShardID: 1}: 1, }, }, nil).Times(1) c.EXPECT().CountDLQMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-1")}). Return(&types.HistoryCountDLQMessagesResponse{ Entries: map[types.HistoryDLQCountKey]int64{ {ShardID: 2}: 2, }, }, nil).Times(1) }, want: &types.HistoryCountDLQMessagesResponse{ Entries: map[types.HistoryDLQCountKey]int64{ {ShardID: 1}: 1, {ShardID: 2}: 2, }, }, }, { name: "QueryWorkflow", op: func(c Client) (any, error) { return c.QueryWorkflow(context.Background(), &types.HistoryQueryWorkflowRequest{ Request: &types.QueryWorkflowRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.HistoryQueryWorkflowResponse{}, nil).Times(1) }, want: &types.HistoryQueryWorkflowResponse{}, }, { name: "ResetStickyTaskList", op: func(c Client) (any, error) { return c.ResetStickyTaskList(context.Background(), &types.HistoryResetStickyTaskListRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().ResetStickyTaskList(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.HistoryResetStickyTaskListResponse{}, nil).Times(1) }, want: &types.HistoryResetStickyTaskListResponse{}, }, { name: "RespondDecisionTaskCompleted", op: func(c Client) (any, error) { return c.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: []byte(`{"workflowId": "test-workflow"}`), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.HistoryRespondDecisionTaskCompletedResponse{}, nil).Times(1) }, want: &types.HistoryRespondDecisionTaskCompletedResponse{}, }, { name: "RespondDecisionTaskCompleted", op: func(c Client) (any, error) { return c.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: []byte(`{"workflowId": "test-workflow"}`), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.HistoryRespondDecisionTaskCompletedResponse{}, nil).Times(1) }, want: &types.HistoryRespondDecisionTaskCompletedResponse{}, }, { name: "SignalWithStartWorkflowExecution", op: func(c Client) (any, error) { return c.SignalWithStartWorkflowExecution(context.Background(), &types.HistorySignalWithStartWorkflowExecutionRequest{ SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ WorkflowID: "test-workflow", }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) }, want: &types.StartWorkflowExecutionResponse{}, }, { name: "RatelimitUpdate", op: func(c Client) (any, error) { return c.RatelimitUpdate(context.Background(), &types.RatelimitUpdateRequest{ Any: &types.Any{ ValueType: "something", Value: []byte("data"), }, }, yarpc.WithShardKey("test-peer")) }, mock: func(p *MockPeerResolver, c *MockClient) { c.EXPECT().RatelimitUpdate(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(&types.RatelimitUpdateResponse{}, nil).Times(1) }, want: &types.RatelimitUpdateResponse{}, }, { name: "StartWorkflowExecution peer resolve failure", op: func(c Client) (any, error) { return c.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{ WorkflowID: "test-workflow", }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("", fmt.Errorf("some error")).Times(1) }, wantError: true, }, { name: "StartWorkflowExecution failure", op: func(c Client) (any, error) { return c.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{ WorkflowID: "test-workflow", }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("StartWorkflowExecution failed")).Times(1) }, wantError: true, }, { name: "StartWorkflowExecution redirected success with host lost error", op: func(c Client) (any, error) { return c.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{ WorkflowID: "test-workflow", }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer-1", nil).Times(1) p.EXPECT().FromHostAddress("host-test-peer-2").Return("test-peer-2", nil).Times(1) c.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-1")}). Return(nil, &types.ShardOwnershipLostError{ Message: "test-peer-1 lost the shard", Owner: "host-test-peer-2", }).Times(1) c.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-2")}). Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) }, want: &types.StartWorkflowExecutionResponse{}, }, { name: "StartWorkflowExecution redirected failed again with peer resolve", op: func(c Client) (any, error) { return c.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{ WorkflowID: "test-workflow", }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer-1", nil).Times(1) p.EXPECT().FromHostAddress("host-test-peer-2").Return("", fmt.Errorf("not found")).Times(1) c.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-1")}). Return(nil, &types.ShardOwnershipLostError{ Message: "test-peer-1 lost the shard", Owner: "host-test-peer-2", }).Times(1) }, wantError: true, }, { name: "StartWorkflowExecution redirected failed again with error", op: func(c Client) (any, error) { return c.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{ WorkflowID: "test-workflow", }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer-1", nil).Times(1) p.EXPECT().FromHostAddress("host-test-peer-2").Return("test-peer-2", nil).Times(1) c.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-1")}). Return(nil, &types.ShardOwnershipLostError{ Message: "test-peer-1 lost the shard", Owner: "host-test-peer-2", }).Times(1) c.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-2")}). Return(nil, fmt.Errorf("some error")).Times(1) }, wantError: true, }, { name: "GetMutableState fail", op: func(c Client) (any, error) { return c.GetMutableState(context.Background(), &types.GetMutableStateRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().GetMutableState(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("GetMutableState failed")).Times(1) }, wantError: true, }, { name: "PollMutableState fail", op: func(c Client) (any, error) { return c.PollMutableState(context.Background(), &types.PollMutableStateRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().PollMutableState(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("PollMutableState failed")).Times(1) }, wantError: true, }, { name: "ResetWorkflowExecution fail", op: func(c Client) (any, error) { return c.ResetWorkflowExecution(context.Background(), &types.HistoryResetWorkflowExecutionRequest{ ResetRequest: &types.ResetWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().ResetWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("ResetWorkflowExecution failed")).Times(1) }, wantError: true, }, { name: "DescribeWorkflowExecution fail", op: func(c Client) (any, error) { return c.DescribeWorkflowExecution(context.Background(), &types.HistoryDescribeWorkflowExecutionRequest{ Request: &types.DescribeWorkflowExecutionRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("DescribeWorkflowExecution failed")).Times(1) }, wantError: true, }, { name: "RecordActivityTaskHeartbeat fail", op: func(c Client) (any, error) { return c.RecordActivityTaskHeartbeat(context.Background(), &types.HistoryRecordActivityTaskHeartbeatRequest{ HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: []byte(`{"workflowId": "test-workflow"}`), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("RecordActivityTaskHeartbeat failed")).Times(1) }, wantError: true, }, { name: "RecordActivityTaskStarted fail", op: func(c Client) (any, error) { return c.RecordActivityTaskStarted(context.Background(), &types.RecordActivityTaskStartedRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RecordActivityTaskStarted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("RecordActivityTaskStarted failed")).Times(1) }, wantError: true, }, { name: "RecordDecisionTaskStarted fail", op: func(c Client) (any, error) { return c.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RecordDecisionTaskStarted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("RecordDecisionTaskStarted failed")).Times(1) }, wantError: true, }, { name: "GetReplicationMessages fail open on unknow error", op: func(c Client) (any, error) { return c.GetReplicationMessages(context.Background(), &types.GetReplicationMessagesRequest{ Tokens: []*types.ReplicationToken{ { ShardID: 100, }, { ShardID: 101, }, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromShardID(100).Return("test-peer-0", nil).Times(1) p.EXPECT().FromShardID(101).Return("test-peer-1", nil).Times(1) c.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-0")}). Return(&types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{100: {}}, }, nil).Times(1) c.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-1")}). Return(nil, fmt.Errorf("GetReplicationMessages failed")).Times(1) }, want: &types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{ 100: {}, }, }, wantError: false, }, { name: "GetReplicationMessages fail open on unknow error", op: func(c Client) (any, error) { return c.GetReplicationMessages(context.Background(), &types.GetReplicationMessagesRequest{ Tokens: []*types.ReplicationToken{ { ShardID: 100, }, { ShardID: 101, }, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromShardID(100).Return("test-peer-0", nil).Times(1) p.EXPECT().FromShardID(101).Return("test-peer-1", nil).Times(1) c.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-0")}). Return(&types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{100: {}}, }, nil).Times(1) c.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-1")}). Return(nil, &types.ServiceBusyError{}).Times(1) }, wantError: true, }, { name: "GetDLQReplicationMessages fail", op: func(c Client) (any, error) { return c.GetDLQReplicationMessages(context.Background(), &types.GetDLQReplicationMessagesRequest{ TaskInfos: []*types.ReplicationTaskInfo{ {WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().GetDLQReplicationMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("GetDLQReplicationMessages failed")).Times(1) }, wantError: true, }, { name: "ReadDLQMessages fail", op: func(c Client) (any, error) { return c.ReadDLQMessages(context.Background(), &types.ReadDLQMessagesRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().ReadDLQMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("ReadDLQMessages failed")).Times(1) }, wantError: true, }, { name: "MergeDLQMessages fail", op: func(c Client) (any, error) { return c.MergeDLQMessages(context.Background(), &types.MergeDLQMessagesRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().MergeDLQMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("MergeDLQMessages failed")).Times(1) }, wantError: true, }, { name: "GetFailoverInfo fail", op: func(c Client) (any, error) { return c.GetFailoverInfo(context.Background(), &types.GetFailoverInfoRequest{ DomainID: "test-domain", }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromDomainID("test-domain").Return("test-peer", nil).Times(1) c.EXPECT().GetFailoverInfo(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("GetFailoverInfo failed")).Times(1) }, wantError: true, }, { name: "DescribeMutableState fail", op: func(c Client) (any, error) { return c.DescribeMutableState(context.Background(), &types.DescribeMutableStateRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID(gomock.Any()).Return("test-peer", nil).Times(1) c.EXPECT().DescribeMutableState(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("DescribeMutableState failed")).Times(1) }, wantError: true, }, { name: "DescribeQueue fail", op: func(c Client) (any, error) { return c.DescribeQueue(context.Background(), &types.DescribeQueueRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().DescribeQueue(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("DescribeQueue failed")).Times(1) }, wantError: true, }, { name: "CountDLQMessages fail", op: func(c Client) (any, error) { return c.CountDLQMessages(context.Background(), &types.CountDLQMessagesRequest{}) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().GetAllPeers().Return([]string{"test-peer"}, nil).Times(1) c.EXPECT().CountDLQMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("CountDLQMessages failed")).Times(1) }, wantError: true, }, { name: "QueryWorkflow fail", op: func(c Client) (any, error) { return c.QueryWorkflow(context.Background(), &types.HistoryQueryWorkflowRequest{ Request: &types.QueryWorkflowRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID(gomock.Any()).Return("test-peer", nil).Times(1) c.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("QueryWorkflow failed")).Times(1) }, wantError: true, }, { name: "ResetStickyTaskList fail", op: func(c Client) (any, error) { return c.ResetStickyTaskList(context.Background(), &types.HistoryResetStickyTaskListRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().ResetStickyTaskList(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("ResetStickyTaskList failed")).Times(1) }, wantError: true, }, { name: "RespondDecisionTaskCompleted fail", op: func(c Client) (any, error) { return c.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: []byte(`{"workflowId": "test-workflow"}`), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("RespondDecisionTaskCompleted failed")).Times(1) }, wantError: true, }, { name: "RespondDecisionTaskCompleted fail", op: func(c Client) (any, error) { return c.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: []byte(`{"workflowId": "test-workflow"}`), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("RespondDecisionTaskCompleted failed")).Times(1) }, wantError: true, }, { name: "SignalWithStartWorkflowExecution fail", op: func(c Client) (any, error) { return c.SignalWithStartWorkflowExecution(context.Background(), &types.HistorySignalWithStartWorkflowExecutionRequest{ SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ WorkflowID: "test-workflow", }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // Add your mock expectations here p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil, fmt.Errorf("SignalWithStartWorkflowExecution failed")).Times(1) }, wantError: true, }, { name: "RatelimitUpdate requires explicit shard key arg", op: func(c Client) (any, error) { // same as successful call... return c.RatelimitUpdate(context.Background(), &types.RatelimitUpdateRequest{ // Peer: "", // intentionally the zero value Any: &types.Any{ ValueType: "something", Value: []byte("data"), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { // no calls expected }, wantError: true, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockClient := NewMockClient(ctrl) mockPeerResolver := NewMockPeerResolver(ctrl) c := NewClient(10, 1024, mockClient, mockPeerResolver, log.NewNoop()) tt.mock(mockPeerResolver, mockClient) res, err := tt.op(c) if tt.wantError { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.want, res) } }) } } func TestClient_withNoResponse(t *testing.T) { tests := []struct { name string op func(Client) error mock func(*MockPeerResolver, *MockClient) wantError bool }{ { name: "RefreshWorkflowTasks", op: func(c Client) error { return c.RefreshWorkflowTasks(context.Background(), &types.HistoryRefreshWorkflowTasksRequest{ Request: &types.RefreshWorkflowTasksRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RefreshWorkflowTasks(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "PurgeDLQMessages", op: func(c Client) error { return c.PurgeDLQMessages(context.Background(), &types.PurgeDLQMessagesRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().PurgeDLQMessages(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "ReapplyEvents", op: func(c Client) error { return c.ReapplyEvents(context.Background(), &types.HistoryReapplyEventsRequest{ Request: &types.ReapplyEventsRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().ReapplyEvents(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "TerminateWorkflowExecution", op: func(c Client) error { return c.TerminateWorkflowExecution(context.Background(), &types.HistoryTerminateWorkflowExecutionRequest{ TerminateRequest: &types.TerminateWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().TerminateWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "NotifyFailoverMarkers", op: func(c Client) error { return c.NotifyFailoverMarkers(context.Background(), &types.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: []*types.FailoverMarkerToken{ { FailoverMarker: &types.FailoverMarkerAttributes{ DomainID: "test-domain-0", }, }, { FailoverMarker: &types.FailoverMarkerAttributes{ DomainID: "test-domain-1", }, }, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromDomainID("test-domain-0").Return("test-peer-0", nil).Times(1) p.EXPECT().FromDomainID("test-domain-1").Return("test-peer-1", nil).Times(1) c.EXPECT().NotifyFailoverMarkers(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-0")}). Return(nil).Times(1) c.EXPECT().NotifyFailoverMarkers(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer-1")}). Return(nil).Times(1) }, }, { name: "CloseShard", op: func(c Client) error { return c.CloseShard(context.Background(), &types.CloseShardRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().CloseShard(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "RemoveTask", op: func(c Client) error { return c.RemoveTask(context.Background(), &types.RemoveTaskRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().RemoveTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "RecordChildExecutionCompleted", op: func(c Client) error { return c.RecordChildExecutionCompleted(context.Background(), &types.RecordChildExecutionCompletedRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RecordChildExecutionCompleted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "RefreshWorkflowTasks", op: func(c Client) error { return c.RefreshWorkflowTasks(context.Background(), &types.HistoryRefreshWorkflowTasksRequest{ Request: &types.RefreshWorkflowTasksRequest{ Execution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RefreshWorkflowTasks(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "RemoveSignalMutableState", op: func(c Client) error { return c.RemoveSignalMutableState(context.Background(), &types.RemoveSignalMutableStateRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RemoveSignalMutableState(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "ReplicateEventsV2", op: func(c Client) error { return c.ReplicateEventsV2(context.Background(), &types.ReplicateEventsV2Request{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().ReplicateEventsV2(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "RequestCancelWorkflowExecution", op: func(c Client) error { return c.RequestCancelWorkflowExecution(context.Background(), &types.HistoryRequestCancelWorkflowExecutionRequest{ CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "ResetQueue", op: func(c Client) error { return c.ResetQueue(context.Background(), &types.ResetQueueRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().ResetQueue(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "RespondActivityTaskCanceled", op: func(c Client) error { return c.RespondActivityTaskCanceled(context.Background(), &types.HistoryRespondActivityTaskCanceledRequest{ CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: []byte(`{"workflowId": "test-workflow"}`), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RespondActivityTaskCanceled(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "RespondActivityTaskCompleted", op: func(c Client) error { return c.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: []byte(`{"workflowId": "test-workflow"}`), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RespondActivityTaskCompleted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "RespondActivityTaskFailed", op: func(c Client) error { return c.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: []byte(`{"workflowId": "test-workflow"}`), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RespondActivityTaskFailed(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "RespondDecisionTaskFailed", op: func(c Client) error { return c.RespondDecisionTaskFailed(context.Background(), &types.HistoryRespondDecisionTaskFailedRequest{ FailedRequest: &types.RespondDecisionTaskFailedRequest{ TaskToken: []byte(`{"workflowId": "test-workflow"}`), }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().RespondDecisionTaskFailed(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "ScheduleDecisionTask", op: func(c Client) error { return c.ScheduleDecisionTask(context.Background(), &types.ScheduleDecisionTaskRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().ScheduleDecisionTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "SignalWorkflowExecution", op: func(c Client) error { return c.SignalWorkflowExecution(context.Background(), &types.HistorySignalWorkflowExecutionRequest{ SignalRequest: &types.SignalWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test-workflow"}, }, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "SyncActivity", op: func(c Client) error { return c.SyncActivity(context.Background(), &types.SyncActivityRequest{ WorkflowID: "test-workflow", }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromWorkflowID("test-workflow").Return("test-peer", nil).Times(1) c.EXPECT().SyncActivity(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, { name: "SyncShardStatus", op: func(c Client) error { return c.SyncShardStatus(context.Background(), &types.SyncShardStatusRequest{ ShardID: 123, }) }, mock: func(p *MockPeerResolver, c *MockClient) { p.EXPECT().FromShardID(123).Return("test-peer", nil).Times(1) c.EXPECT().SyncShardStatus(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("test-peer")}). Return(nil).Times(1) }, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockClient := NewMockClient(ctrl) mockPeerResolver := NewMockPeerResolver(ctrl) c := NewClient(10, 1024, mockClient, mockPeerResolver, log.NewNoop()) tt.mock(mockPeerResolver, mockClient) err := tt.op(c) if tt.wantError { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_cmpGetReplicationMessagesWithSize(t *testing.T) { for name, c := range map[string]struct { a, b *getReplicationMessagesWithSize want int }{ "both nil": { a: nil, b: nil, want: 1, }, "a time is nil, b is nil": { a: &getReplicationMessagesWithSize{earliestCreationTime: nil}, b: nil, want: 1, }, "a time is not nil, b is nil": { a: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(10)}, b: nil, want: -1, }, "a time is not nil, b time is nil": { a: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(10)}, b: &getReplicationMessagesWithSize{earliestCreationTime: nil}, want: -1, }, "a time less b time": { a: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(10)}, b: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(20)}, want: -1, }, "a time greater b time": { a: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(20)}, b: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(10)}, want: 1, }, "a size less b size": { a: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(10), size: 10}, b: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(10), size: 20}, want: -1, }, "a size greater b size": { a: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(10), size: 20}, b: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(10), size: 10}, want: 1, }, "a equal b": { a: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(10)}, b: &getReplicationMessagesWithSize{earliestCreationTime: common.Int64Ptr(10)}, want: 0, }, } { t.Run(name, func(t *testing.T) { assert.Equal(t, c.want, cmpGetReplicationMessagesWithSize(c.a, c.b)) }) } } func Test_sortGetReplicationMessageWithSize(t *testing.T) { for name, c := range map[string]struct { responses []*getReplicationMessagesWithSize want []*getReplicationMessagesWithSize }{ "empty": {}, "multiple nil, non nil earliestCreationTime": { responses: []*getReplicationMessagesWithSize{ {earliestCreationTime: nil}, {earliestCreationTime: nil}, {earliestCreationTime: common.Int64Ptr(20)}, {earliestCreationTime: common.Int64Ptr(10)}, }, want: []*getReplicationMessagesWithSize{ {earliestCreationTime: common.Int64Ptr(10)}, {earliestCreationTime: common.Int64Ptr(20)}, {earliestCreationTime: nil}, {earliestCreationTime: nil}, }, }, "multiple nil, non nil same earliestCreationTime, different size": { responses: []*getReplicationMessagesWithSize{ {earliestCreationTime: nil}, {earliestCreationTime: nil}, {earliestCreationTime: common.Int64Ptr(100), size: 50}, {earliestCreationTime: common.Int64Ptr(100), size: 30}, {earliestCreationTime: common.Int64Ptr(20)}, }, want: []*getReplicationMessagesWithSize{ {earliestCreationTime: common.Int64Ptr(20)}, {earliestCreationTime: common.Int64Ptr(100), size: 30}, {earliestCreationTime: common.Int64Ptr(100), size: 50}, {earliestCreationTime: nil}, {earliestCreationTime: nil}, }, }, } { t.Run(name, func(t *testing.T) { sortGetReplicationMessageWithSize(c.responses) assert.Equal(t, c.want, c.responses) }) } } ================================================ FILE: client/history/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package history import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -package history github.com/uber/cadence/client/history Client //go:generate gowrap gen -g -p . -i Client -t ../templates/retry.tmpl -o ../wrappers/retryable/history_generated.go -v client=History //go:generate gowrap gen -g -p . -i Client -t ../templates/metered.tmpl -o ../wrappers/metered/history_generated.go -v client=History //go:generate gowrap gen -g -p . -i Client -t ../templates/errorinjectors.tmpl -o ../wrappers/errorinjectors/history_generated.go -v client=History //go:generate gowrap gen -g -p . -i Client -t ../templates/grpc.tmpl -o ../wrappers/grpc/history_generated.go -v client=History -v package=historyv1 -v path=github.com/uber/cadence/.gen/proto/history/v1 -v prefix=History //go:generate gowrap gen -g -p . -i Client -t ../templates/thrift.tmpl -o ../wrappers/thrift/history_generated.go -v client=History -v prefix=History //go:generate gowrap gen -g -p . -i Client -t ../templates/timeout.tmpl -o ../wrappers/timeout/history_generated.go -v client=History -v exclude=GetReplicationMessages|GetDLQReplicationMessages|CountDLQMessages|ReadDLQMessages|PurgeDLQMessages|MergeDLQMessages|GetCrossClusterTasks|GetFailoverInfo // Client is the interface exposed by history service client type Client interface { CloseShard(context.Context, *types.CloseShardRequest, ...yarpc.CallOption) error DescribeHistoryHost(context.Context, *types.DescribeHistoryHostRequest, ...yarpc.CallOption) (*types.DescribeHistoryHostResponse, error) DescribeMutableState(context.Context, *types.DescribeMutableStateRequest, ...yarpc.CallOption) (*types.DescribeMutableStateResponse, error) DescribeQueue(context.Context, *types.DescribeQueueRequest, ...yarpc.CallOption) (*types.DescribeQueueResponse, error) DescribeWorkflowExecution(context.Context, *types.HistoryDescribeWorkflowExecutionRequest, ...yarpc.CallOption) (*types.DescribeWorkflowExecutionResponse, error) GetCrossClusterTasks(context.Context, *types.GetCrossClusterTasksRequest, ...yarpc.CallOption) (*types.GetCrossClusterTasksResponse, error) GetDLQReplicationMessages(context.Context, *types.GetDLQReplicationMessagesRequest, ...yarpc.CallOption) (*types.GetDLQReplicationMessagesResponse, error) CountDLQMessages(context.Context, *types.CountDLQMessagesRequest, ...yarpc.CallOption) (*types.HistoryCountDLQMessagesResponse, error) GetMutableState(context.Context, *types.GetMutableStateRequest, ...yarpc.CallOption) (*types.GetMutableStateResponse, error) GetReplicationMessages(context.Context, *types.GetReplicationMessagesRequest, ...yarpc.CallOption) (*types.GetReplicationMessagesResponse, error) MergeDLQMessages(context.Context, *types.MergeDLQMessagesRequest, ...yarpc.CallOption) (*types.MergeDLQMessagesResponse, error) NotifyFailoverMarkers(context.Context, *types.NotifyFailoverMarkersRequest, ...yarpc.CallOption) error PollMutableState(context.Context, *types.PollMutableStateRequest, ...yarpc.CallOption) (*types.PollMutableStateResponse, error) PurgeDLQMessages(context.Context, *types.PurgeDLQMessagesRequest, ...yarpc.CallOption) error QueryWorkflow(context.Context, *types.HistoryQueryWorkflowRequest, ...yarpc.CallOption) (*types.HistoryQueryWorkflowResponse, error) ReadDLQMessages(context.Context, *types.ReadDLQMessagesRequest, ...yarpc.CallOption) (*types.ReadDLQMessagesResponse, error) ReapplyEvents(context.Context, *types.HistoryReapplyEventsRequest, ...yarpc.CallOption) error RecordActivityTaskHeartbeat(context.Context, *types.HistoryRecordActivityTaskHeartbeatRequest, ...yarpc.CallOption) (*types.RecordActivityTaskHeartbeatResponse, error) RecordActivityTaskStarted(context.Context, *types.RecordActivityTaskStartedRequest, ...yarpc.CallOption) (*types.RecordActivityTaskStartedResponse, error) RecordChildExecutionCompleted(context.Context, *types.RecordChildExecutionCompletedRequest, ...yarpc.CallOption) error RecordDecisionTaskStarted(context.Context, *types.RecordDecisionTaskStartedRequest, ...yarpc.CallOption) (*types.RecordDecisionTaskStartedResponse, error) RefreshWorkflowTasks(context.Context, *types.HistoryRefreshWorkflowTasksRequest, ...yarpc.CallOption) error RemoveSignalMutableState(context.Context, *types.RemoveSignalMutableStateRequest, ...yarpc.CallOption) error RemoveTask(context.Context, *types.RemoveTaskRequest, ...yarpc.CallOption) error ReplicateEventsV2(context.Context, *types.ReplicateEventsV2Request, ...yarpc.CallOption) error RequestCancelWorkflowExecution(context.Context, *types.HistoryRequestCancelWorkflowExecutionRequest, ...yarpc.CallOption) error ResetQueue(context.Context, *types.ResetQueueRequest, ...yarpc.CallOption) error ResetStickyTaskList(context.Context, *types.HistoryResetStickyTaskListRequest, ...yarpc.CallOption) (*types.HistoryResetStickyTaskListResponse, error) ResetWorkflowExecution(context.Context, *types.HistoryResetWorkflowExecutionRequest, ...yarpc.CallOption) (*types.ResetWorkflowExecutionResponse, error) RespondActivityTaskCanceled(context.Context, *types.HistoryRespondActivityTaskCanceledRequest, ...yarpc.CallOption) error RespondActivityTaskCompleted(context.Context, *types.HistoryRespondActivityTaskCompletedRequest, ...yarpc.CallOption) error RespondActivityTaskFailed(context.Context, *types.HistoryRespondActivityTaskFailedRequest, ...yarpc.CallOption) error RespondCrossClusterTasksCompleted(context.Context, *types.RespondCrossClusterTasksCompletedRequest, ...yarpc.CallOption) (*types.RespondCrossClusterTasksCompletedResponse, error) RespondDecisionTaskCompleted(context.Context, *types.HistoryRespondDecisionTaskCompletedRequest, ...yarpc.CallOption) (*types.HistoryRespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed(context.Context, *types.HistoryRespondDecisionTaskFailedRequest, ...yarpc.CallOption) error ScheduleDecisionTask(context.Context, *types.ScheduleDecisionTaskRequest, ...yarpc.CallOption) error SignalWithStartWorkflowExecution(context.Context, *types.HistorySignalWithStartWorkflowExecutionRequest, ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) SignalWorkflowExecution(context.Context, *types.HistorySignalWorkflowExecutionRequest, ...yarpc.CallOption) error StartWorkflowExecution(context.Context, *types.HistoryStartWorkflowExecutionRequest, ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) SyncActivity(context.Context, *types.SyncActivityRequest, ...yarpc.CallOption) error SyncShardStatus(context.Context, *types.SyncShardStatusRequest, ...yarpc.CallOption) error TerminateWorkflowExecution(context.Context, *types.HistoryTerminateWorkflowExecutionRequest, ...yarpc.CallOption) error GetFailoverInfo(context.Context, *types.GetFailoverInfoRequest, ...yarpc.CallOption) (*types.GetFailoverInfoResponse, error) // RatelimitUpdate pushes usage info for the passed ratelimit keys, and requests updated weight info from aggregating hosts. // Exact semantics beyond this depend on the load-balanced ratelimit implementation. // // A peer (via yarpc.WithShardkey) MUST be determined before calling and passed in yarpc opts, // and unlike most endpoints this will NOT be forwarded to a new peer if the ring membership changes. // To correctly forward keys to the new hosts, they must be re-sharded to find their new hosts. RatelimitUpdate(ctx context.Context, request *types.RatelimitUpdateRequest, opts ...yarpc.CallOption) (*types.RatelimitUpdateResponse, error) } ================================================ FILE: client/history/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package history -source interface.go -destination interface_mock.go -package history github.com/uber/cadence/client/history Client // // Package history is a generated GoMock package. package history import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" types "github.com/uber/cadence/common/types" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // CloseShard mocks base method. func (m *MockClient) CloseShard(arg0 context.Context, arg1 *types.CloseShardRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CloseShard", varargs...) ret0, _ := ret[0].(error) return ret0 } // CloseShard indicates an expected call of CloseShard. func (mr *MockClientMockRecorder) CloseShard(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseShard", reflect.TypeOf((*MockClient)(nil).CloseShard), varargs...) } // CountDLQMessages mocks base method. func (m *MockClient) CountDLQMessages(arg0 context.Context, arg1 *types.CountDLQMessagesRequest, arg2 ...yarpc.CallOption) (*types.HistoryCountDLQMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CountDLQMessages", varargs...) ret0, _ := ret[0].(*types.HistoryCountDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CountDLQMessages indicates an expected call of CountDLQMessages. func (mr *MockClientMockRecorder) CountDLQMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountDLQMessages", reflect.TypeOf((*MockClient)(nil).CountDLQMessages), varargs...) } // DescribeHistoryHost mocks base method. func (m *MockClient) DescribeHistoryHost(arg0 context.Context, arg1 *types.DescribeHistoryHostRequest, arg2 ...yarpc.CallOption) (*types.DescribeHistoryHostResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeHistoryHost", varargs...) ret0, _ := ret[0].(*types.DescribeHistoryHostResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeHistoryHost indicates an expected call of DescribeHistoryHost. func (mr *MockClientMockRecorder) DescribeHistoryHost(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeHistoryHost", reflect.TypeOf((*MockClient)(nil).DescribeHistoryHost), varargs...) } // DescribeMutableState mocks base method. func (m *MockClient) DescribeMutableState(arg0 context.Context, arg1 *types.DescribeMutableStateRequest, arg2 ...yarpc.CallOption) (*types.DescribeMutableStateResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeMutableState", varargs...) ret0, _ := ret[0].(*types.DescribeMutableStateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeMutableState indicates an expected call of DescribeMutableState. func (mr *MockClientMockRecorder) DescribeMutableState(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeMutableState", reflect.TypeOf((*MockClient)(nil).DescribeMutableState), varargs...) } // DescribeQueue mocks base method. func (m *MockClient) DescribeQueue(arg0 context.Context, arg1 *types.DescribeQueueRequest, arg2 ...yarpc.CallOption) (*types.DescribeQueueResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeQueue", varargs...) ret0, _ := ret[0].(*types.DescribeQueueResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeQueue indicates an expected call of DescribeQueue. func (mr *MockClientMockRecorder) DescribeQueue(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeQueue", reflect.TypeOf((*MockClient)(nil).DescribeQueue), varargs...) } // DescribeWorkflowExecution mocks base method. func (m *MockClient) DescribeWorkflowExecution(arg0 context.Context, arg1 *types.HistoryDescribeWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.DescribeWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.DescribeWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeWorkflowExecution indicates an expected call of DescribeWorkflowExecution. func (mr *MockClientMockRecorder) DescribeWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeWorkflowExecution", reflect.TypeOf((*MockClient)(nil).DescribeWorkflowExecution), varargs...) } // GetCrossClusterTasks mocks base method. func (m *MockClient) GetCrossClusterTasks(arg0 context.Context, arg1 *types.GetCrossClusterTasksRequest, arg2 ...yarpc.CallOption) (*types.GetCrossClusterTasksResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetCrossClusterTasks", varargs...) ret0, _ := ret[0].(*types.GetCrossClusterTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCrossClusterTasks indicates an expected call of GetCrossClusterTasks. func (mr *MockClientMockRecorder) GetCrossClusterTasks(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCrossClusterTasks", reflect.TypeOf((*MockClient)(nil).GetCrossClusterTasks), varargs...) } // GetDLQReplicationMessages mocks base method. func (m *MockClient) GetDLQReplicationMessages(arg0 context.Context, arg1 *types.GetDLQReplicationMessagesRequest, arg2 ...yarpc.CallOption) (*types.GetDLQReplicationMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetDLQReplicationMessages", varargs...) ret0, _ := ret[0].(*types.GetDLQReplicationMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDLQReplicationMessages indicates an expected call of GetDLQReplicationMessages. func (mr *MockClientMockRecorder) GetDLQReplicationMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDLQReplicationMessages", reflect.TypeOf((*MockClient)(nil).GetDLQReplicationMessages), varargs...) } // GetFailoverInfo mocks base method. func (m *MockClient) GetFailoverInfo(arg0 context.Context, arg1 *types.GetFailoverInfoRequest, arg2 ...yarpc.CallOption) (*types.GetFailoverInfoResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetFailoverInfo", varargs...) ret0, _ := ret[0].(*types.GetFailoverInfoResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFailoverInfo indicates an expected call of GetFailoverInfo. func (mr *MockClientMockRecorder) GetFailoverInfo(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFailoverInfo", reflect.TypeOf((*MockClient)(nil).GetFailoverInfo), varargs...) } // GetMutableState mocks base method. func (m *MockClient) GetMutableState(arg0 context.Context, arg1 *types.GetMutableStateRequest, arg2 ...yarpc.CallOption) (*types.GetMutableStateResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetMutableState", varargs...) ret0, _ := ret[0].(*types.GetMutableStateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMutableState indicates an expected call of GetMutableState. func (mr *MockClientMockRecorder) GetMutableState(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMutableState", reflect.TypeOf((*MockClient)(nil).GetMutableState), varargs...) } // GetReplicationMessages mocks base method. func (m *MockClient) GetReplicationMessages(arg0 context.Context, arg1 *types.GetReplicationMessagesRequest, arg2 ...yarpc.CallOption) (*types.GetReplicationMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetReplicationMessages", varargs...) ret0, _ := ret[0].(*types.GetReplicationMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetReplicationMessages indicates an expected call of GetReplicationMessages. func (mr *MockClientMockRecorder) GetReplicationMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationMessages", reflect.TypeOf((*MockClient)(nil).GetReplicationMessages), varargs...) } // MergeDLQMessages mocks base method. func (m *MockClient) MergeDLQMessages(arg0 context.Context, arg1 *types.MergeDLQMessagesRequest, arg2 ...yarpc.CallOption) (*types.MergeDLQMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "MergeDLQMessages", varargs...) ret0, _ := ret[0].(*types.MergeDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MergeDLQMessages indicates an expected call of MergeDLQMessages. func (mr *MockClientMockRecorder) MergeDLQMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MergeDLQMessages", reflect.TypeOf((*MockClient)(nil).MergeDLQMessages), varargs...) } // NotifyFailoverMarkers mocks base method. func (m *MockClient) NotifyFailoverMarkers(arg0 context.Context, arg1 *types.NotifyFailoverMarkersRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "NotifyFailoverMarkers", varargs...) ret0, _ := ret[0].(error) return ret0 } // NotifyFailoverMarkers indicates an expected call of NotifyFailoverMarkers. func (mr *MockClientMockRecorder) NotifyFailoverMarkers(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyFailoverMarkers", reflect.TypeOf((*MockClient)(nil).NotifyFailoverMarkers), varargs...) } // PollMutableState mocks base method. func (m *MockClient) PollMutableState(arg0 context.Context, arg1 *types.PollMutableStateRequest, arg2 ...yarpc.CallOption) (*types.PollMutableStateResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "PollMutableState", varargs...) ret0, _ := ret[0].(*types.PollMutableStateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollMutableState indicates an expected call of PollMutableState. func (mr *MockClientMockRecorder) PollMutableState(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollMutableState", reflect.TypeOf((*MockClient)(nil).PollMutableState), varargs...) } // PurgeDLQMessages mocks base method. func (m *MockClient) PurgeDLQMessages(arg0 context.Context, arg1 *types.PurgeDLQMessagesRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "PurgeDLQMessages", varargs...) ret0, _ := ret[0].(error) return ret0 } // PurgeDLQMessages indicates an expected call of PurgeDLQMessages. func (mr *MockClientMockRecorder) PurgeDLQMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PurgeDLQMessages", reflect.TypeOf((*MockClient)(nil).PurgeDLQMessages), varargs...) } // QueryWorkflow mocks base method. func (m *MockClient) QueryWorkflow(arg0 context.Context, arg1 *types.HistoryQueryWorkflowRequest, arg2 ...yarpc.CallOption) (*types.HistoryQueryWorkflowResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "QueryWorkflow", varargs...) ret0, _ := ret[0].(*types.HistoryQueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // QueryWorkflow indicates an expected call of QueryWorkflow. func (mr *MockClientMockRecorder) QueryWorkflow(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockClient)(nil).QueryWorkflow), varargs...) } // RatelimitUpdate mocks base method. func (m *MockClient) RatelimitUpdate(ctx context.Context, request *types.RatelimitUpdateRequest, opts ...yarpc.CallOption) (*types.RatelimitUpdateResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, request} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RatelimitUpdate", varargs...) ret0, _ := ret[0].(*types.RatelimitUpdateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RatelimitUpdate indicates an expected call of RatelimitUpdate. func (mr *MockClientMockRecorder) RatelimitUpdate(ctx, request any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, request}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RatelimitUpdate", reflect.TypeOf((*MockClient)(nil).RatelimitUpdate), varargs...) } // ReadDLQMessages mocks base method. func (m *MockClient) ReadDLQMessages(arg0 context.Context, arg1 *types.ReadDLQMessagesRequest, arg2 ...yarpc.CallOption) (*types.ReadDLQMessagesResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ReadDLQMessages", varargs...) ret0, _ := ret[0].(*types.ReadDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadDLQMessages indicates an expected call of ReadDLQMessages. func (mr *MockClientMockRecorder) ReadDLQMessages(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDLQMessages", reflect.TypeOf((*MockClient)(nil).ReadDLQMessages), varargs...) } // ReapplyEvents mocks base method. func (m *MockClient) ReapplyEvents(arg0 context.Context, arg1 *types.HistoryReapplyEventsRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ReapplyEvents", varargs...) ret0, _ := ret[0].(error) return ret0 } // ReapplyEvents indicates an expected call of ReapplyEvents. func (mr *MockClientMockRecorder) ReapplyEvents(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReapplyEvents", reflect.TypeOf((*MockClient)(nil).ReapplyEvents), varargs...) } // RecordActivityTaskHeartbeat mocks base method. func (m *MockClient) RecordActivityTaskHeartbeat(arg0 context.Context, arg1 *types.HistoryRecordActivityTaskHeartbeatRequest, arg2 ...yarpc.CallOption) (*types.RecordActivityTaskHeartbeatResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RecordActivityTaskHeartbeat", varargs...) ret0, _ := ret[0].(*types.RecordActivityTaskHeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordActivityTaskHeartbeat indicates an expected call of RecordActivityTaskHeartbeat. func (mr *MockClientMockRecorder) RecordActivityTaskHeartbeat(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordActivityTaskHeartbeat", reflect.TypeOf((*MockClient)(nil).RecordActivityTaskHeartbeat), varargs...) } // RecordActivityTaskStarted mocks base method. func (m *MockClient) RecordActivityTaskStarted(arg0 context.Context, arg1 *types.RecordActivityTaskStartedRequest, arg2 ...yarpc.CallOption) (*types.RecordActivityTaskStartedResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RecordActivityTaskStarted", varargs...) ret0, _ := ret[0].(*types.RecordActivityTaskStartedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordActivityTaskStarted indicates an expected call of RecordActivityTaskStarted. func (mr *MockClientMockRecorder) RecordActivityTaskStarted(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordActivityTaskStarted", reflect.TypeOf((*MockClient)(nil).RecordActivityTaskStarted), varargs...) } // RecordChildExecutionCompleted mocks base method. func (m *MockClient) RecordChildExecutionCompleted(arg0 context.Context, arg1 *types.RecordChildExecutionCompletedRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RecordChildExecutionCompleted", varargs...) ret0, _ := ret[0].(error) return ret0 } // RecordChildExecutionCompleted indicates an expected call of RecordChildExecutionCompleted. func (mr *MockClientMockRecorder) RecordChildExecutionCompleted(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordChildExecutionCompleted", reflect.TypeOf((*MockClient)(nil).RecordChildExecutionCompleted), varargs...) } // RecordDecisionTaskStarted mocks base method. func (m *MockClient) RecordDecisionTaskStarted(arg0 context.Context, arg1 *types.RecordDecisionTaskStartedRequest, arg2 ...yarpc.CallOption) (*types.RecordDecisionTaskStartedResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RecordDecisionTaskStarted", varargs...) ret0, _ := ret[0].(*types.RecordDecisionTaskStartedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordDecisionTaskStarted indicates an expected call of RecordDecisionTaskStarted. func (mr *MockClientMockRecorder) RecordDecisionTaskStarted(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordDecisionTaskStarted", reflect.TypeOf((*MockClient)(nil).RecordDecisionTaskStarted), varargs...) } // RefreshWorkflowTasks mocks base method. func (m *MockClient) RefreshWorkflowTasks(arg0 context.Context, arg1 *types.HistoryRefreshWorkflowTasksRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RefreshWorkflowTasks", varargs...) ret0, _ := ret[0].(error) return ret0 } // RefreshWorkflowTasks indicates an expected call of RefreshWorkflowTasks. func (mr *MockClientMockRecorder) RefreshWorkflowTasks(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshWorkflowTasks", reflect.TypeOf((*MockClient)(nil).RefreshWorkflowTasks), varargs...) } // RemoveSignalMutableState mocks base method. func (m *MockClient) RemoveSignalMutableState(arg0 context.Context, arg1 *types.RemoveSignalMutableStateRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RemoveSignalMutableState", varargs...) ret0, _ := ret[0].(error) return ret0 } // RemoveSignalMutableState indicates an expected call of RemoveSignalMutableState. func (mr *MockClientMockRecorder) RemoveSignalMutableState(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveSignalMutableState", reflect.TypeOf((*MockClient)(nil).RemoveSignalMutableState), varargs...) } // RemoveTask mocks base method. func (m *MockClient) RemoveTask(arg0 context.Context, arg1 *types.RemoveTaskRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RemoveTask", varargs...) ret0, _ := ret[0].(error) return ret0 } // RemoveTask indicates an expected call of RemoveTask. func (mr *MockClientMockRecorder) RemoveTask(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveTask", reflect.TypeOf((*MockClient)(nil).RemoveTask), varargs...) } // ReplicateEventsV2 mocks base method. func (m *MockClient) ReplicateEventsV2(arg0 context.Context, arg1 *types.ReplicateEventsV2Request, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ReplicateEventsV2", varargs...) ret0, _ := ret[0].(error) return ret0 } // ReplicateEventsV2 indicates an expected call of ReplicateEventsV2. func (mr *MockClientMockRecorder) ReplicateEventsV2(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateEventsV2", reflect.TypeOf((*MockClient)(nil).ReplicateEventsV2), varargs...) } // RequestCancelWorkflowExecution mocks base method. func (m *MockClient) RequestCancelWorkflowExecution(arg0 context.Context, arg1 *types.HistoryRequestCancelWorkflowExecutionRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RequestCancelWorkflowExecution", varargs...) ret0, _ := ret[0].(error) return ret0 } // RequestCancelWorkflowExecution indicates an expected call of RequestCancelWorkflowExecution. func (mr *MockClientMockRecorder) RequestCancelWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestCancelWorkflowExecution", reflect.TypeOf((*MockClient)(nil).RequestCancelWorkflowExecution), varargs...) } // ResetQueue mocks base method. func (m *MockClient) ResetQueue(arg0 context.Context, arg1 *types.ResetQueueRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ResetQueue", varargs...) ret0, _ := ret[0].(error) return ret0 } // ResetQueue indicates an expected call of ResetQueue. func (mr *MockClientMockRecorder) ResetQueue(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetQueue", reflect.TypeOf((*MockClient)(nil).ResetQueue), varargs...) } // ResetStickyTaskList mocks base method. func (m *MockClient) ResetStickyTaskList(arg0 context.Context, arg1 *types.HistoryResetStickyTaskListRequest, arg2 ...yarpc.CallOption) (*types.HistoryResetStickyTaskListResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ResetStickyTaskList", varargs...) ret0, _ := ret[0].(*types.HistoryResetStickyTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetStickyTaskList indicates an expected call of ResetStickyTaskList. func (mr *MockClientMockRecorder) ResetStickyTaskList(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetStickyTaskList", reflect.TypeOf((*MockClient)(nil).ResetStickyTaskList), varargs...) } // ResetWorkflowExecution mocks base method. func (m *MockClient) ResetWorkflowExecution(arg0 context.Context, arg1 *types.HistoryResetWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.ResetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ResetWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.ResetWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetWorkflowExecution indicates an expected call of ResetWorkflowExecution. func (mr *MockClientMockRecorder) ResetWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetWorkflowExecution", reflect.TypeOf((*MockClient)(nil).ResetWorkflowExecution), varargs...) } // RespondActivityTaskCanceled mocks base method. func (m *MockClient) RespondActivityTaskCanceled(arg0 context.Context, arg1 *types.HistoryRespondActivityTaskCanceledRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondActivityTaskCanceled", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCanceled indicates an expected call of RespondActivityTaskCanceled. func (mr *MockClientMockRecorder) RespondActivityTaskCanceled(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCanceled", reflect.TypeOf((*MockClient)(nil).RespondActivityTaskCanceled), varargs...) } // RespondActivityTaskCompleted mocks base method. func (m *MockClient) RespondActivityTaskCompleted(arg0 context.Context, arg1 *types.HistoryRespondActivityTaskCompletedRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondActivityTaskCompleted", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCompleted indicates an expected call of RespondActivityTaskCompleted. func (mr *MockClientMockRecorder) RespondActivityTaskCompleted(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCompleted", reflect.TypeOf((*MockClient)(nil).RespondActivityTaskCompleted), varargs...) } // RespondActivityTaskFailed mocks base method. func (m *MockClient) RespondActivityTaskFailed(arg0 context.Context, arg1 *types.HistoryRespondActivityTaskFailedRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondActivityTaskFailed", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskFailed indicates an expected call of RespondActivityTaskFailed. func (mr *MockClientMockRecorder) RespondActivityTaskFailed(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskFailed", reflect.TypeOf((*MockClient)(nil).RespondActivityTaskFailed), varargs...) } // RespondCrossClusterTasksCompleted mocks base method. func (m *MockClient) RespondCrossClusterTasksCompleted(arg0 context.Context, arg1 *types.RespondCrossClusterTasksCompletedRequest, arg2 ...yarpc.CallOption) (*types.RespondCrossClusterTasksCompletedResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondCrossClusterTasksCompleted", varargs...) ret0, _ := ret[0].(*types.RespondCrossClusterTasksCompletedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RespondCrossClusterTasksCompleted indicates an expected call of RespondCrossClusterTasksCompleted. func (mr *MockClientMockRecorder) RespondCrossClusterTasksCompleted(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondCrossClusterTasksCompleted", reflect.TypeOf((*MockClient)(nil).RespondCrossClusterTasksCompleted), varargs...) } // RespondDecisionTaskCompleted mocks base method. func (m *MockClient) RespondDecisionTaskCompleted(arg0 context.Context, arg1 *types.HistoryRespondDecisionTaskCompletedRequest, arg2 ...yarpc.CallOption) (*types.HistoryRespondDecisionTaskCompletedResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondDecisionTaskCompleted", varargs...) ret0, _ := ret[0].(*types.HistoryRespondDecisionTaskCompletedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RespondDecisionTaskCompleted indicates an expected call of RespondDecisionTaskCompleted. func (mr *MockClientMockRecorder) RespondDecisionTaskCompleted(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondDecisionTaskCompleted", reflect.TypeOf((*MockClient)(nil).RespondDecisionTaskCompleted), varargs...) } // RespondDecisionTaskFailed mocks base method. func (m *MockClient) RespondDecisionTaskFailed(arg0 context.Context, arg1 *types.HistoryRespondDecisionTaskFailedRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondDecisionTaskFailed", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondDecisionTaskFailed indicates an expected call of RespondDecisionTaskFailed. func (mr *MockClientMockRecorder) RespondDecisionTaskFailed(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondDecisionTaskFailed", reflect.TypeOf((*MockClient)(nil).RespondDecisionTaskFailed), varargs...) } // ScheduleDecisionTask mocks base method. func (m *MockClient) ScheduleDecisionTask(arg0 context.Context, arg1 *types.ScheduleDecisionTaskRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ScheduleDecisionTask", varargs...) ret0, _ := ret[0].(error) return ret0 } // ScheduleDecisionTask indicates an expected call of ScheduleDecisionTask. func (mr *MockClientMockRecorder) ScheduleDecisionTask(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduleDecisionTask", reflect.TypeOf((*MockClient)(nil).ScheduleDecisionTask), varargs...) } // SignalWithStartWorkflowExecution mocks base method. func (m *MockClient) SignalWithStartWorkflowExecution(arg0 context.Context, arg1 *types.HistorySignalWithStartWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SignalWithStartWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.StartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SignalWithStartWorkflowExecution indicates an expected call of SignalWithStartWorkflowExecution. func (mr *MockClientMockRecorder) SignalWithStartWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWithStartWorkflowExecution", reflect.TypeOf((*MockClient)(nil).SignalWithStartWorkflowExecution), varargs...) } // SignalWorkflowExecution mocks base method. func (m *MockClient) SignalWorkflowExecution(arg0 context.Context, arg1 *types.HistorySignalWorkflowExecutionRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SignalWorkflowExecution", varargs...) ret0, _ := ret[0].(error) return ret0 } // SignalWorkflowExecution indicates an expected call of SignalWorkflowExecution. func (mr *MockClientMockRecorder) SignalWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWorkflowExecution", reflect.TypeOf((*MockClient)(nil).SignalWorkflowExecution), varargs...) } // StartWorkflowExecution mocks base method. func (m *MockClient) StartWorkflowExecution(arg0 context.Context, arg1 *types.HistoryStartWorkflowExecutionRequest, arg2 ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "StartWorkflowExecution", varargs...) ret0, _ := ret[0].(*types.StartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // StartWorkflowExecution indicates an expected call of StartWorkflowExecution. func (mr *MockClientMockRecorder) StartWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartWorkflowExecution", reflect.TypeOf((*MockClient)(nil).StartWorkflowExecution), varargs...) } // SyncActivity mocks base method. func (m *MockClient) SyncActivity(arg0 context.Context, arg1 *types.SyncActivityRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SyncActivity", varargs...) ret0, _ := ret[0].(error) return ret0 } // SyncActivity indicates an expected call of SyncActivity. func (mr *MockClientMockRecorder) SyncActivity(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncActivity", reflect.TypeOf((*MockClient)(nil).SyncActivity), varargs...) } // SyncShardStatus mocks base method. func (m *MockClient) SyncShardStatus(arg0 context.Context, arg1 *types.SyncShardStatusRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SyncShardStatus", varargs...) ret0, _ := ret[0].(error) return ret0 } // SyncShardStatus indicates an expected call of SyncShardStatus. func (mr *MockClientMockRecorder) SyncShardStatus(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncShardStatus", reflect.TypeOf((*MockClient)(nil).SyncShardStatus), varargs...) } // TerminateWorkflowExecution mocks base method. func (m *MockClient) TerminateWorkflowExecution(arg0 context.Context, arg1 *types.HistoryTerminateWorkflowExecutionRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "TerminateWorkflowExecution", varargs...) ret0, _ := ret[0].(error) return ret0 } // TerminateWorkflowExecution indicates an expected call of TerminateWorkflowExecution. func (mr *MockClientMockRecorder) TerminateWorkflowExecution(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TerminateWorkflowExecution", reflect.TypeOf((*MockClient)(nil).TerminateWorkflowExecution), varargs...) } ================================================ FILE: client/history/peer_resolver.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package history import ( "fmt" "go.uber.org/yarpc" "github.com/uber/cadence/common" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/history/lookup" ) // PeerResolver is used to resolve history peers. // Those are deployed instances of Cadence history services that participate in the cluster ring. // The resulting peer is simply an address of form ip:port where RPC calls can be routed to. // //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination peer_resolver_mock.go -package history github.com/uber/cadence/client/history PeerResolver type PeerResolver interface { FromWorkflowID(workflowID string) (string, error) FromDomainID(domainID string) (string, error) FromShardID(shardID int) (string, error) FromHostAddress(hostAddress string) (string, error) GetAllPeers() ([]string, error) // GlobalRatelimitPeers partitions the ratelimit keys into map[yarpc peer][]limits_for_peer GlobalRatelimitPeers(ratelimits []string) (ratelimitsByPeer map[Peer][]string, err error) } // Peer is used to mark a string as the routing information to a peer process. // // This is essentially the host:port address of the peer to be contacted, // but it is meant to be treated as an opaque blob until given to yarpc // via ToYarpcShardKey. type Peer string func (s Peer) ToYarpcShardKey() yarpc.CallOption { return yarpc.WithShardKey(string(s)) } type peerResolver struct { numberOfShards int resolver membership.Resolver namedPort string // grpc or tchannel, depends on yarpc configuration } // NewPeerResolver creates a new history peer resolver. func NewPeerResolver(numberOfShards int, resolver membership.Resolver, namedPort string) PeerResolver { return peerResolver{ numberOfShards: numberOfShards, resolver: resolver, namedPort: namedPort, } } // FromWorkflowID resolves the history peer responsible for a given workflowID. // WorkflowID is converted to logical shardID using a consistent hash function. // FromShardID is used for further resolving. func (pr peerResolver) FromWorkflowID(workflowID string) (string, error) { shardID := common.WorkflowIDToHistoryShard(workflowID, pr.numberOfShards) return pr.FromShardID(shardID) } // FromDomainID resolves the history peer responsible for a given domainID. // DomainID is converted to logical shardID using a consistent hash function. // FromShardID is used for further resolving. func (pr peerResolver) FromDomainID(domainID string) (string, error) { shardID := common.DomainIDToHistoryShard(domainID, pr.numberOfShards) return pr.FromShardID(shardID) } // FromShardID resolves the history peer responsible for a given logical shardID. // It uses our membership provider to lookup which instance currently owns the given shard. // FromHostAddress is used for further resolving. func (pr peerResolver) FromShardID(shardID int) (string, error) { host, err := lookup.HistoryServerByShardID(pr.resolver, shardID) if err != nil { return "", common.ToServiceTransientError(err) } peer, err := host.GetNamedAddress(pr.namedPort) return peer, common.ToServiceTransientError(err) } // FromHostAddress resolves the final history peer responsible for the given host address. // The address is formed by adding port for specified transport func (pr peerResolver) FromHostAddress(hostAddress string) (string, error) { host, err := pr.resolver.LookupByAddress(service.History, hostAddress) if err != nil { return "", common.ToServiceTransientError(err) } peer, err := host.GetNamedAddress(pr.namedPort) return peer, common.ToServiceTransientError(err) } // GetAllPeers returns all history service peers in the cluster ring. func (pr peerResolver) GetAllPeers() ([]string, error) { hosts, err := pr.resolver.Members(service.History) if err != nil { return nil, common.ToServiceTransientError(err) } peers := make([]string, 0, len(hosts)) for _, host := range hosts { peer, err := host.GetNamedAddress(pr.namedPort) if err != nil { return nil, common.ToServiceTransientError(err) } peers = append(peers, peer) } return peers, nil } func (pr peerResolver) GlobalRatelimitPeers(ratelimits []string) (map[Peer][]string, error) { // History was selected simply because it already has a ring and an internal-only API. // Any service should be fine, it just needs to be shared by both ends of the system. hosts, err := pr.resolver.Members(service.History) if err != nil { return nil, fmt.Errorf("unable to get history peers: %w", err) } if len(hosts) == 0 { // can occur when shutting down the only instance because it calls EvictSelf ASAP. // this is common in dev, but otherwise *probably* should not be possible in a healthy system. return nil, fmt.Errorf("unable to get history peers: no peers available") } results := make(map[Peer][]string, len(hosts)) initialCapacity := len(ratelimits) / len(hosts) // add a small buffer to reduce copies, as this is only an estimate initialCapacity += initialCapacity / 10 // but don't use zero, that'd be pointless if initialCapacity < 1 { initialCapacity = 1 } for _, r := range ratelimits { // figure out the destination for this ratelimit host, err := pr.resolver.Lookup(service.History, r) if err != nil { return nil, fmt.Errorf( "unable to find host for ratelimit key %q: %w", r, err) } peer, err := host.GetNamedAddress(pr.namedPort) if err != nil { // AFAICT should not happen unless the peer data is incomplete / the ring has no port info. // retry may work? eventually? return nil, fmt.Errorf( "unable to get address from host: %s, %w", host.String(), // HostInfo has a readable String(), it's good enough err, ) } // add to the peer's partition current := results[Peer(peer)] if len(current) == 0 { current = make([]string, 0, initialCapacity) } current = append(current, r) results[Peer(peer)] = current } return results, nil } ================================================ FILE: client/history/peer_resolver_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: peer_resolver.go // // Generated by this command: // // mockgen -package history -source peer_resolver.go -destination peer_resolver_mock.go -package history github.com/uber/cadence/client/history PeerResolver // // Package history is a generated GoMock package. package history import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockPeerResolver is a mock of PeerResolver interface. type MockPeerResolver struct { ctrl *gomock.Controller recorder *MockPeerResolverMockRecorder isgomock struct{} } // MockPeerResolverMockRecorder is the mock recorder for MockPeerResolver. type MockPeerResolverMockRecorder struct { mock *MockPeerResolver } // NewMockPeerResolver creates a new mock instance. func NewMockPeerResolver(ctrl *gomock.Controller) *MockPeerResolver { mock := &MockPeerResolver{ctrl: ctrl} mock.recorder = &MockPeerResolverMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPeerResolver) EXPECT() *MockPeerResolverMockRecorder { return m.recorder } // FromDomainID mocks base method. func (m *MockPeerResolver) FromDomainID(domainID string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FromDomainID", domainID) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // FromDomainID indicates an expected call of FromDomainID. func (mr *MockPeerResolverMockRecorder) FromDomainID(domainID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FromDomainID", reflect.TypeOf((*MockPeerResolver)(nil).FromDomainID), domainID) } // FromHostAddress mocks base method. func (m *MockPeerResolver) FromHostAddress(hostAddress string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FromHostAddress", hostAddress) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // FromHostAddress indicates an expected call of FromHostAddress. func (mr *MockPeerResolverMockRecorder) FromHostAddress(hostAddress any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FromHostAddress", reflect.TypeOf((*MockPeerResolver)(nil).FromHostAddress), hostAddress) } // FromShardID mocks base method. func (m *MockPeerResolver) FromShardID(shardID int) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FromShardID", shardID) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // FromShardID indicates an expected call of FromShardID. func (mr *MockPeerResolverMockRecorder) FromShardID(shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FromShardID", reflect.TypeOf((*MockPeerResolver)(nil).FromShardID), shardID) } // FromWorkflowID mocks base method. func (m *MockPeerResolver) FromWorkflowID(workflowID string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FromWorkflowID", workflowID) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // FromWorkflowID indicates an expected call of FromWorkflowID. func (mr *MockPeerResolverMockRecorder) FromWorkflowID(workflowID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FromWorkflowID", reflect.TypeOf((*MockPeerResolver)(nil).FromWorkflowID), workflowID) } // GetAllPeers mocks base method. func (m *MockPeerResolver) GetAllPeers() ([]string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllPeers") ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAllPeers indicates an expected call of GetAllPeers. func (mr *MockPeerResolverMockRecorder) GetAllPeers() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllPeers", reflect.TypeOf((*MockPeerResolver)(nil).GetAllPeers)) } // GlobalRatelimitPeers mocks base method. func (m *MockPeerResolver) GlobalRatelimitPeers(ratelimits []string) (map[Peer][]string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GlobalRatelimitPeers", ratelimits) ret0, _ := ret[0].(map[Peer][]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GlobalRatelimitPeers indicates an expected call of GlobalRatelimitPeers. func (mr *MockPeerResolverMockRecorder) GlobalRatelimitPeers(ratelimits any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GlobalRatelimitPeers", reflect.TypeOf((*MockPeerResolver)(nil).GlobalRatelimitPeers), ratelimits) } ================================================ FILE: client/history/peer_resolver_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package history import ( "errors" "slices" "testing" "github.com/dgryski/go-farm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/ringpop-go/hashring" gomock "go.uber.org/mock/gomock" "golang.org/x/exp/maps" "github.com/uber/cadence/common" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" ) func TestPeerResolver(t *testing.T) { numShards := 123 t.Run("FromIDs", func(t *testing.T) { controller := gomock.NewController(t) serviceResolver := membership.NewMockResolver(controller) serviceResolver.EXPECT().Lookup( service.History, string(rune(common.DomainIDToHistoryShard("domainID", numShards)))).Return( membership.NewDetailedHostInfo( "domainHost:123", "domainHost_123", membership.PortMap{membership.PortTchannel: 1234}), nil) serviceResolver.EXPECT().Lookup(service.History, string(rune(common.WorkflowIDToHistoryShard("workflowID", numShards)))).Return( membership.NewDetailedHostInfo( "workflowHost:123", "workflow", membership.PortMap{membership.PortTchannel: 1235, membership.PortGRPC: 1666}), nil) serviceResolver.EXPECT().Lookup(service.History, string(rune(99))).Return( membership.NewDetailedHostInfo( "shardHost:123", "shard_123", membership.PortMap{membership.PortTchannel: 1235}), nil) serviceResolver.EXPECT().Lookup(service.History, string(rune(11))).Return(membership.HostInfo{}, assert.AnError) r := NewPeerResolver(numShards, serviceResolver, membership.PortTchannel) peer, err := r.FromDomainID("domainID") assert.NoError(t, err) assert.Equal(t, "domainHost:1234", peer) peer, err = r.FromWorkflowID("workflowID") assert.NoError(t, err) assert.Equal(t, "workflowHost:1235", peer) peer, err = r.FromShardID(99) assert.NoError(t, err) assert.Equal(t, "shardHost:1235", peer) _, err = r.FromShardID(11) assert.Error(t, err) }) t.Run("FromHostAddress", func(t *testing.T) { tests := []struct { name string address string mock func(*membership.MockResolver) want string wantError bool }{ { name: "success", address: "addressHost:123", mock: func(mr *membership.MockResolver) { mr.EXPECT().LookupByAddress(service.History, "addressHost:123").Return( membership.NewDetailedHostInfo( "addressHost:123", "address", membership.PortMap{membership.PortTchannel: 1235, membership.PortGRPC: 1666}), nil, ) }, want: "addressHost:1235", }, { name: "invalid address", address: "invalid address", mock: func(mr *membership.MockResolver) { mr.EXPECT().LookupByAddress(service.History, "invalid address").Return( membership.HostInfo{}, errors.New("host not found"), ) }, wantError: true, }, { name: "fail on no port", address: "addressHost:123", mock: func(mr *membership.MockResolver) { mr.EXPECT().LookupByAddress(service.History, "addressHost:123").Return( membership.NewDetailedHostInfo( "addressHost:123", "address", membership.PortMap{}), nil, ) }, wantError: true, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { controller := gomock.NewController(t) serviceResolver := membership.NewMockResolver(controller) tt.mock(serviceResolver) r := NewPeerResolver(numShards, serviceResolver, membership.PortTchannel) res, err := r.FromHostAddress(tt.address) if tt.wantError { assert.True(t, common.IsServiceTransientError(err)) } else { assert.Equal(t, tt.want, res) assert.NoError(t, err) } }) } }) t.Run("GetAllPeers", func(t *testing.T) { tests := []struct { name string mock func(*membership.MockResolver) want []string wantError bool }{ { name: "success", mock: func(mr *membership.MockResolver) { mr.EXPECT().Members(service.History).Return( []membership.HostInfo{ membership.NewDetailedHostInfo( "host1:123", "address", membership.PortMap{membership.PortTchannel: 1235, membership.PortGRPC: 1666}), membership.NewDetailedHostInfo( "host2:123", "address", membership.PortMap{membership.PortTchannel: 1235, membership.PortGRPC: 1666}), }, nil, ) }, want: []string{"host1:1235", "host2:1235"}, }, { name: "failed on peer resolve", mock: func(mr *membership.MockResolver) { mr.EXPECT().Members(service.History).Return(nil, assert.AnError) }, wantError: true, }, { name: "failed on no port", mock: func(mr *membership.MockResolver) { mr.EXPECT().Members(service.History).Return( []membership.HostInfo{ membership.NewDetailedHostInfo( "host1:123", "address", membership.PortMap{membership.PortTchannel: 1235, membership.PortGRPC: 1666}), membership.NewDetailedHostInfo( "host2:123", "address", membership.PortMap{membership.PortGRPC: 1666}), }, nil, ) }, wantError: true, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { controller := gomock.NewController(t) serviceResolver := membership.NewMockResolver(controller) tt.mock(serviceResolver) r := NewPeerResolver(numShards, serviceResolver, membership.PortTchannel) res, err := r.GetAllPeers() if tt.wantError { assert.True(t, common.IsServiceTransientError(err)) } else { assert.ElementsMatch(t, tt.want, res) assert.NoError(t, err) } }) } }) t.Run("GlobalRatelimitPeers", func(t *testing.T) { peers := []membership.HostInfo{ membership.NewDetailedHostInfo( "abc:123", "abc-id", membership.PortMap{membership.PortTchannel: 1235}), membership.NewDetailedHostInfo( "def:456", "def-id", membership.PortMap{membership.PortTchannel: 1235}), membership.NewDetailedHostInfo( "xyz:789", "xyz-id", membership.PortMap{membership.PortTchannel: 1235}), } limitKeys := []string{ "domain-w-async", "domain-x-user", "domain-x-worker", "domain-y-worker", "domain-z-visibility", } t.Run("errs", func(t *testing.T) { t.Run("no members", func(t *testing.T) { resolver := membership.NewMockResolver(gomock.NewController(t)) resolver.EXPECT().Members(service.History).Return(nil, errors.New("no members")) r := NewPeerResolver(numShards, resolver, membership.PortTchannel) _, err := r.GlobalRatelimitPeers([]string{"test"}) assert.ErrorContains(t, err, "no members") }) t.Run("no host for key", func(t *testing.T) { resolver := membership.NewMockResolver(gomock.NewController(t)) resolver.EXPECT().Members(service.History).Return(peers, nil) // seems likely impossible, unless the service is unknown resolver.EXPECT().Lookup(service.History, gomock.Any()).Return(membership.HostInfo{}, errors.New("no host")) r := NewPeerResolver(numShards, resolver, membership.PortTchannel) _, err := r.GlobalRatelimitPeers(limitKeys) assert.ErrorContains(t, err, "no host") }) t.Run("no protocol", func(t *testing.T) { resolver := membership.NewMockResolver(gomock.NewController(t)) resolver.EXPECT().Members(service.History).Return(peers, nil) resolver.EXPECT().Lookup(service.History, gomock.Any()).Return(peers[0], nil) // request GRPC, but only configured for TChannel r := NewPeerResolver(numShards, resolver, membership.PortGRPC) _, err := r.GlobalRatelimitPeers(limitKeys) assert.ErrorContains(t, err, "unable to get address") }) }) t.Run("success", func(t *testing.T) { resolver := membership.NewMockResolver(gomock.NewController(t)) resolver.EXPECT().Members(service.History).Return(peers, nil).Times(2) // use a real hashring to resolve the keys. // partly because it's easy and removes the need to mock, // and partly because this must remain stable across hashring upgrades. ring := hashring.New(farm.Fingerprint32, 100 /* arbitrary, but affects sharding */) for _, p := range peers { ring.AddMembers(p) // casts type } resolver.EXPECT().Lookup(service.History, gomock.Any()).DoAndReturn(func(service, key string) (membership.HostInfo, error) { peer, ok := ring.Lookup(key) require.True(t, ok, "keys should always be findable") return find(t, peers, func(info membership.HostInfo) bool { return info.GetAddress() == peer }), nil }).AnyTimes() // small sanity check: sharded response should return all inputs assertAllKeysPresent := func(t *testing.T, sharded map[Peer][]string, limits []string) { responded := maps.Values(sharded) assert.ElementsMatchf(t, limits, slices.Concat(responded...), // flatten the [][]string "sharded response does not contain exactly the limits requested:\n\trequested: %v\n\tresponse: %v", limits, sharded) } // shard the keys to hosts r := NewPeerResolver(numShards, resolver, membership.PortTchannel) res, err := r.GlobalRatelimitPeers(limitKeys) require.NoError(t, err) assertAllKeysPresent(t, res, limitKeys) assert.Equal(t, map[Peer][]string{ // determined experimentally, but needs to be stable "abc:1235": {"domain-w-async", "domain-x-worker", "domain-y-worker"}, "def:1235": {"domain-x-user"}, "xyz:1235": {"domain-z-visibility"}, }, res, "") // request with another key, response should be the same but with a new key somewhere oneMoreKey := append(limitKeys, "domain-other-user") res, err = r.GlobalRatelimitPeers(oneMoreKey) require.NoError(t, err) assertAllKeysPresent(t, res, oneMoreKey) assert.Equal(t, map[Peer][]string{ "abc:1235": {"domain-w-async", "domain-x-worker", "domain-y-worker"}, "def:1235": {"domain-x-user"}, "xyz:1235": {"domain-z-visibility", "domain-other-user"}, }, res, "") }) }) } // finds a single element that matches, or fails. // surprisingly "slices" doesn't have any filter or find equivalent, only Index, // and either way I want to ensure no multi-matches. func find[T any](t *testing.T, elems []T, equals func(T) bool) T { var zero T filtered := filter(elems, equals) if len(filtered) == 0 { t.Fatalf("no matching %T elements found", zero) return zero // unreachable } if len(filtered) > 1 { t.Fatalf("found multiple matching %T elements: %v", zero, filtered) return zero // unreachable } return filtered[0] } func filter[T any](elems []T, match func(T) bool) (filtered []T) { for _, e := range elems { if match(e) { filtered = append(filtered, e) } } return filtered } ================================================ FILE: client/matching/client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package matching import ( "context" "errors" "go.uber.org/yarpc" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/future" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) var _ Client = (*clientImpl)(nil) type clientImpl struct { client Client peerResolver PeerResolver loadBalancer LoadBalancer provider PartitionConfigProvider } // NewClient creates a new history service TChannel client func NewClient( client Client, peerResolver PeerResolver, lb LoadBalancer, provider PartitionConfigProvider, ) Client { return &clientImpl{ client: client, peerResolver: peerResolver, loadBalancer: lb, provider: provider, } } func (c *clientImpl) AddActivityTask( ctx context.Context, request *types.AddActivityTaskRequest, opts ...yarpc.CallOption, ) (*types.AddActivityTaskResponse, error) { partition := c.loadBalancer.PickWritePartition( persistence.TaskListTypeActivity, request, ) originalTaskList := request.TaskList request.TaskList = &types.TaskList{ Name: partition, Kind: originalTaskList.Kind, } peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) if err != nil { return nil, err } resp, err := c.client.AddActivityTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { // ReadOnlyPartitionError indicates the partition is being drained - invalidate cache to force next request to route to root partition var readOnlyErr *types.ReadOnlyPartitionError if errors.As(err, &readOnlyErr) { c.provider.InvalidatePartitionCache(request.GetDomainUUID(), *originalTaskList, persistence.TaskListTypeActivity) } return nil, err } request.TaskList = originalTaskList c.provider.UpdatePartitionConfig( request.GetDomainUUID(), *request.TaskList, persistence.TaskListTypeActivity, resp.PartitionConfig, ) return resp, nil } func (c *clientImpl) AddDecisionTask( ctx context.Context, request *types.AddDecisionTaskRequest, opts ...yarpc.CallOption, ) (*types.AddDecisionTaskResponse, error) { partition := c.loadBalancer.PickWritePartition( persistence.TaskListTypeDecision, request, ) originalTaskList := request.TaskList request.TaskList = &types.TaskList{ Name: partition, Kind: originalTaskList.Kind, } peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) if err != nil { return nil, err } resp, err := c.client.AddDecisionTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { // ReadOnlyPartitionError indicates the partition is being drained - invalidate cache to force next request to route to root partition var readOnlyErr *types.ReadOnlyPartitionError if errors.As(err, &readOnlyErr) { c.provider.InvalidatePartitionCache(request.GetDomainUUID(), *originalTaskList, persistence.TaskListTypeDecision) } return nil, err } request.TaskList = originalTaskList c.provider.UpdatePartitionConfig( request.GetDomainUUID(), *request.TaskList, persistence.TaskListTypeDecision, resp.PartitionConfig, ) return resp, nil } func (c *clientImpl) PollForActivityTask( ctx context.Context, request *types.MatchingPollForActivityTaskRequest, opts ...yarpc.CallOption, ) (*types.MatchingPollForActivityTaskResponse, error) { partition := c.loadBalancer.PickReadPartition( persistence.TaskListTypeActivity, request, request.GetIsolationGroup(), ) originalTaskList := request.PollRequest.TaskList request.PollRequest.TaskList = &types.TaskList{ Name: partition, Kind: originalTaskList.Kind, } peer, err := c.peerResolver.FromTaskList(request.PollRequest.TaskList.GetName()) if err != nil { return nil, err } resp, err := c.client.PollForActivityTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { return nil, cadence_errors.NewPeerHostnameError(err, peer) } request.PollRequest.TaskList = originalTaskList c.provider.UpdatePartitionConfig( request.GetDomainUUID(), *request.PollRequest.GetTaskList(), persistence.TaskListTypeActivity, resp.PartitionConfig, ) c.loadBalancer.UpdateWeight( persistence.TaskListTypeActivity, request, partition, resp.LoadBalancerHints, ) // caller needs to know the actual partition for cancelling long poll, so modify the request to pass this information request.PollRequest.TaskList.Name = partition return resp, nil } func (c *clientImpl) PollForDecisionTask( ctx context.Context, request *types.MatchingPollForDecisionTaskRequest, opts ...yarpc.CallOption, ) (*types.MatchingPollForDecisionTaskResponse, error) { partition := c.loadBalancer.PickReadPartition( persistence.TaskListTypeDecision, request, request.GetIsolationGroup(), ) originalTaskList := request.PollRequest.TaskList request.PollRequest.TaskList = &types.TaskList{ Name: partition, Kind: originalTaskList.Kind, } peer, err := c.peerResolver.FromTaskList(request.PollRequest.TaskList.GetName()) if err != nil { return nil, err } resp, err := c.client.PollForDecisionTask(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { return nil, cadence_errors.NewPeerHostnameError(err, peer) } request.PollRequest.TaskList = originalTaskList c.provider.UpdatePartitionConfig( request.GetDomainUUID(), *request.PollRequest.GetTaskList(), persistence.TaskListTypeDecision, resp.PartitionConfig, ) c.loadBalancer.UpdateWeight( persistence.TaskListTypeDecision, request, partition, resp.LoadBalancerHints, ) // caller needs to know the actual partition for cancelling long poll, so modify the request to pass this information request.PollRequest.TaskList.Name = partition return resp, nil } func (c *clientImpl) QueryWorkflow( ctx context.Context, request *types.MatchingQueryWorkflowRequest, opts ...yarpc.CallOption, ) (*types.MatchingQueryWorkflowResponse, error) { partition := c.loadBalancer.PickReadPartition( persistence.TaskListTypeDecision, request, "", ) originalTaskList := request.TaskList request.TaskList = &types.TaskList{ Name: partition, Kind: originalTaskList.Kind, } peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) if err != nil { return nil, err } resp, err := c.client.QueryWorkflow(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { return nil, err } request.TaskList = originalTaskList c.provider.UpdatePartitionConfig( request.GetDomainUUID(), *request.TaskList, persistence.TaskListTypeDecision, resp.PartitionConfig, ) return resp, nil } func (c *clientImpl) RespondQueryTaskCompleted( ctx context.Context, request *types.MatchingRespondQueryTaskCompletedRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) if err != nil { return err } err = c.client.RespondQueryTaskCompleted(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { return err } return nil } func (c *clientImpl) CancelOutstandingPoll( ctx context.Context, request *types.CancelOutstandingPollRequest, opts ...yarpc.CallOption, ) error { peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) if err != nil { return err } err = c.client.CancelOutstandingPoll(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { return err } return nil } func (c *clientImpl) DescribeTaskList( ctx context.Context, request *types.MatchingDescribeTaskListRequest, opts ...yarpc.CallOption, ) (*types.DescribeTaskListResponse, error) { peer, err := c.peerResolver.FromTaskList(request.DescRequest.TaskList.GetName()) if err != nil { return nil, err } resp, err := c.client.DescribeTaskList(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { return nil, err } return resp, nil } func (c *clientImpl) ListTaskListPartitions( ctx context.Context, request *types.MatchingListTaskListPartitionsRequest, opts ...yarpc.CallOption, ) (*types.ListTaskListPartitionsResponse, error) { peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) if err != nil { return nil, err } resp, err := c.client.ListTaskListPartitions(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { return nil, err } return resp, nil } func (c *clientImpl) GetTaskListsByDomain( ctx context.Context, request *types.GetTaskListsByDomainRequest, opts ...yarpc.CallOption, ) (*types.GetTaskListsByDomainResponse, error) { peers, err := c.peerResolver.GetAllPeers() if err != nil { return nil, err } var futures []future.Future for _, peer := range peers { future, settable := future.NewFuture() settable.Set(c.client.GetTaskListsByDomain(ctx, request, append(opts, yarpc.WithShardKey(peer))...)) futures = append(futures, future) } decisionTaskListMap := make(map[string]*types.DescribeTaskListResponse) activityTaskListMap := make(map[string]*types.DescribeTaskListResponse) for i, future := range futures { var resp *types.GetTaskListsByDomainResponse if err = future.Get(ctx, &resp); err != nil { return nil, cadence_errors.NewPeerHostnameError(err, peers[i]) } for name, tl := range resp.GetDecisionTaskListMap() { if _, ok := decisionTaskListMap[name]; !ok { decisionTaskListMap[name] = tl } else { decisionTaskListMap[name].Pollers = append(decisionTaskListMap[name].Pollers, tl.GetPollers()...) } } for name, tl := range resp.GetActivityTaskListMap() { if _, ok := activityTaskListMap[name]; !ok { activityTaskListMap[name] = tl } else { activityTaskListMap[name].Pollers = append(activityTaskListMap[name].Pollers, tl.GetPollers()...) } } } return &types.GetTaskListsByDomainResponse{ DecisionTaskListMap: decisionTaskListMap, ActivityTaskListMap: activityTaskListMap, }, nil } func (c *clientImpl) UpdateTaskListPartitionConfig( ctx context.Context, request *types.MatchingUpdateTaskListPartitionConfigRequest, opts ...yarpc.CallOption, ) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) { peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) if err != nil { return nil, err } resp, err := c.client.UpdateTaskListPartitionConfig(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { return nil, err } return resp, nil } func (c *clientImpl) RefreshTaskListPartitionConfig( ctx context.Context, request *types.MatchingRefreshTaskListPartitionConfigRequest, opts ...yarpc.CallOption, ) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) { peer, err := c.peerResolver.FromTaskList(request.TaskList.GetName()) if err != nil { return nil, err } resp, err := c.client.RefreshTaskListPartitionConfig(ctx, request, append(opts, yarpc.WithShardKey(peer))...) if err != nil { return nil, err } return resp, nil } ================================================ FILE: client/matching/client_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "context" stdErrors "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const ( _testDomainUUID = "123" _testDomain = "test-domain" _testTaskList = "test-tasklist" _testPartition = "test-partition" ) type testCase struct { name string op func(Client) (any, error) mock func(*MockPeerResolver, *MockLoadBalancer, *MockClient, *MockPartitionConfigProvider) want any wantError bool validateError func(*testing.T, error) } func TestNewClient(t *testing.T) { ctrl := gomock.NewController(t) client := NewMockClient(ctrl) peerResolver := NewMockPeerResolver(ctrl) loadbalancer := NewMockLoadBalancer(ctrl) provider := NewMockPartitionConfigProvider(ctrl) c := NewClient(client, peerResolver, loadbalancer, provider) assert.NotNil(t, c) } func TestClient_withoutResponse(t *testing.T) { tests := []struct { name string op func(Client) error mock func(*MockPeerResolver, *MockLoadBalancer, *MockClient) wantError bool }{ { name: "RespondQueryTaskCompleted", op: func(c Client) error { return c.RespondQueryTaskCompleted(context.Background(), testRespondQueryTaskCompletedRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().RespondQueryTaskCompleted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil) }, }, { name: "RespondQueryTaskCompleted - Error in resolving peer", op: func(c Client) error { return c.RespondQueryTaskCompleted(context.Background(), testRespondQueryTaskCompletedRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", assert.AnError) }, wantError: true, }, { name: "RespondQueryTaskCompleted - Error while responding to completion of QueryTask", op: func(c Client) error { return c.RespondQueryTaskCompleted(context.Background(), testRespondQueryTaskCompletedRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().RespondQueryTaskCompleted(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(assert.AnError) }, wantError: true, }, { name: "CancelOutstandingPoll", op: func(c Client) error { return c.CancelOutstandingPoll(context.Background(), testCancelOutstandingPollRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().CancelOutstandingPoll(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil) }, }, { name: "CancelOutstandingPoll - Error in resolving peer", op: func(c Client) error { return c.CancelOutstandingPoll(context.Background(), testCancelOutstandingPollRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", assert.AnError) }, wantError: true, }, { name: "CancelOutstandingPoll - Error while cancelling outstanding poll", op: func(c Client) error { return c.CancelOutstandingPoll(context.Background(), testCancelOutstandingPollRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().CancelOutstandingPoll(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(assert.AnError) }, wantError: true, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { // setting up client ctrl := gomock.NewController(t) client := NewMockClient(ctrl) peerResolverMock := NewMockPeerResolver(ctrl) loadbalancerMock := NewMockLoadBalancer(ctrl) providerMock := NewMockPartitionConfigProvider(ctrl) tt.mock(peerResolverMock, loadbalancerMock, client) c := NewClient(client, peerResolverMock, loadbalancerMock, providerMock) err := tt.op(c) if tt.wantError { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestClient_withResponse(t *testing.T) { tests := []struct { name string op func(Client) (any, error) mock func(*MockPeerResolver, *MockLoadBalancer, *MockClient, *MockPartitionConfigProvider) want any wantError bool validateError func(*testing.T, error) }{ { name: "AddActivityTask", op: func(c Client) (any, error) { return c.AddActivityTask(context.Background(), testAddActivityTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickWritePartition(persistence.TaskListTypeActivity, testAddActivityTaskRequest()).Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().AddActivityTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.AddActivityTaskResponse{}, nil) mp.EXPECT().UpdatePartitionConfig(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeActivity, nil) }, want: &types.AddActivityTaskResponse{}, }, { name: "AddActivityTask - Error in resolving peer", op: func(c Client) (any, error) { return c.AddActivityTask(context.Background(), testAddActivityTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickWritePartition(persistence.TaskListTypeActivity, testAddActivityTaskRequest()).Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", assert.AnError) }, wantError: true, }, { name: "AddActivityTask - Error while adding activity task", op: func(c Client) (any, error) { return c.AddActivityTask(context.Background(), testAddActivityTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickWritePartition(persistence.TaskListTypeActivity, testAddActivityTaskRequest()).Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().AddActivityTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, assert.AnError) }, wantError: true, }, { name: "AddActivityTask - ReadOnlyPartitionError triggers cache invalidation", op: func(c Client) (any, error) { return c.AddActivityTask(context.Background(), testAddActivityTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickWritePartition(persistence.TaskListTypeActivity, testAddActivityTaskRequest()).Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().AddActivityTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, &types.ReadOnlyPartitionError{Message: "partition drained"}) mp.EXPECT().InvalidatePartitionCache(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeActivity) }, wantError: true, validateError: func(t *testing.T, err error) { var readOnlyErr *types.ReadOnlyPartitionError assert.True(t, stdErrors.As(err, &readOnlyErr)) assert.Equal(t, "partition drained", readOnlyErr.Message) }, }, { name: "AddDecisionTask", op: func(c Client) (any, error) { return c.AddDecisionTask(context.Background(), testAddDecisionTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickWritePartition(persistence.TaskListTypeDecision, testAddDecisionTaskRequest()).Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.AddDecisionTaskResponse{}, nil) mp.EXPECT().UpdatePartitionConfig(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeDecision, nil) }, want: &types.AddDecisionTaskResponse{}, }, { name: "AddDecisionTask - Error in resolving peer", op: func(c Client) (any, error) { return c.AddDecisionTask(context.Background(), testAddDecisionTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickWritePartition(persistence.TaskListTypeDecision, testAddDecisionTaskRequest()).Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", assert.AnError) }, wantError: true, }, { name: "AddDecisionTask - Error while adding decision task", op: func(c Client) (any, error) { return c.AddDecisionTask(context.Background(), testAddDecisionTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickWritePartition(persistence.TaskListTypeDecision, testAddDecisionTaskRequest()).Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, assert.AnError) }, wantError: true, }, { name: "AddDecisionTask - ReadOnlyPartitionError triggers cache invalidation", op: func(c Client) (any, error) { return c.AddDecisionTask(context.Background(), testAddDecisionTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickWritePartition(persistence.TaskListTypeDecision, testAddDecisionTaskRequest()).Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, &types.ReadOnlyPartitionError{Message: "partition drained"}) mp.EXPECT().InvalidatePartitionCache(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeDecision) }, wantError: true, validateError: func(t *testing.T, err error) { var readOnlyErr *types.ReadOnlyPartitionError assert.True(t, stdErrors.As(err, &readOnlyErr)) assert.Equal(t, "partition drained", readOnlyErr.Message) }, }, { name: "PollForActivityTask", op: func(c Client) (any, error) { return c.PollForActivityTask(context.Background(), testMatchingPollForActivityTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickReadPartition(persistence.TaskListTypeActivity, testMatchingPollForActivityTaskRequest(), "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().PollForActivityTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.MatchingPollForActivityTaskResponse{}, nil) mp.EXPECT().UpdatePartitionConfig(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeActivity, nil) balancer.EXPECT().UpdateWeight(persistence.TaskListTypeActivity, testMatchingPollForActivityTaskRequest(), _testPartition, nil) }, want: &types.MatchingPollForActivityTaskResponse{}, }, { name: "PollForActivityTask - Error in resolving peer", op: func(c Client) (any, error) { return c.PollForActivityTask(context.Background(), testMatchingPollForActivityTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickReadPartition(persistence.TaskListTypeActivity, testMatchingPollForActivityTaskRequest(), "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", assert.AnError) }, want: nil, wantError: true, }, { name: "PollForActivityTask - Error while polling for ActivityTask", op: func(c Client) (any, error) { return c.PollForActivityTask(context.Background(), testMatchingPollForActivityTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickReadPartition(persistence.TaskListTypeActivity, testMatchingPollForActivityTaskRequest(), "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().PollForActivityTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, &types.InternalServiceError{Message: "test error"}) }, want: nil, wantError: true, validateError: func(t *testing.T, err error) { var peerErr *errors.PeerHostnameError assert.True(t, stdErrors.As(err, &peerErr)) assert.Equal(t, "peer0", peerErr.PeerHostname) var internalErr *types.InternalServiceError assert.True(t, stdErrors.As(peerErr.WrappedError, &internalErr)) assert.Equal(t, "test error", internalErr.Message) }, }, { name: "PollForDecisionTask", op: func(c Client) (any, error) { return c.PollForDecisionTask(context.Background(), testMatchingPollForDecisionTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickReadPartition(persistence.TaskListTypeDecision, testMatchingPollForDecisionTaskRequest(), "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.MatchingPollForDecisionTaskResponse{}, nil) mp.EXPECT().UpdatePartitionConfig(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeDecision, nil) balancer.EXPECT().UpdateWeight(persistence.TaskListTypeDecision, testMatchingPollForDecisionTaskRequest(), _testPartition, nil) }, want: &types.MatchingPollForDecisionTaskResponse{}, }, { name: "PollForDecisionTask - Error in resolving peer", op: func(c Client) (any, error) { return c.PollForDecisionTask(context.Background(), testMatchingPollForDecisionTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickReadPartition(persistence.TaskListTypeDecision, testMatchingPollForDecisionTaskRequest(), "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", assert.AnError) }, want: nil, wantError: true, }, { name: "PollForDecisionTask - Error while polling for DecisionTask", op: func(c Client) (any, error) { return c.PollForDecisionTask(context.Background(), testMatchingPollForDecisionTaskRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickReadPartition(persistence.TaskListTypeDecision, testMatchingPollForDecisionTaskRequest(), "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, &types.InternalServiceError{Message: "test error"}) }, want: nil, wantError: true, validateError: func(t *testing.T, err error) { var peerErr *errors.PeerHostnameError assert.True(t, stdErrors.As(err, &peerErr)) assert.Equal(t, "peer0", peerErr.PeerHostname) var internalErr *types.InternalServiceError assert.True(t, stdErrors.As(peerErr.WrappedError, &internalErr)) assert.Equal(t, "test error", internalErr.Message) }, }, { name: "QueryWorkflow", op: func(c Client) (any, error) { return c.QueryWorkflow(context.Background(), testMatchingQueryWorkflowRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickReadPartition(persistence.TaskListTypeDecision, testMatchingQueryWorkflowRequest(), "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.MatchingQueryWorkflowResponse{}, nil) mp.EXPECT().UpdatePartitionConfig(_testDomainUUID, types.TaskList{Name: _testTaskList}, persistence.TaskListTypeDecision, nil) }, want: &types.MatchingQueryWorkflowResponse{}, }, { name: "QueryWorkflow - Error in resolving peer", op: func(c Client) (any, error) { return c.QueryWorkflow(context.Background(), testMatchingQueryWorkflowRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickReadPartition(persistence.TaskListTypeDecision, testMatchingQueryWorkflowRequest(), "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", assert.AnError) }, want: nil, wantError: true, }, { name: "QueryWorkflow - Error while querying workflow", op: func(c Client) (any, error) { return c.QueryWorkflow(context.Background(), testMatchingQueryWorkflowRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { balancer.EXPECT().PickReadPartition(persistence.TaskListTypeDecision, testMatchingQueryWorkflowRequest(), "").Return(_testPartition) p.EXPECT().FromTaskList(_testPartition).Return("peer0", nil) c.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, assert.AnError) }, want: nil, wantError: true, }, { name: "DescribeTaskList", op: func(c Client) (any, error) { return c.DescribeTaskList(context.Background(), testMatchingDescribeTaskListRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.DescribeTaskListResponse{}, nil) }, want: &types.DescribeTaskListResponse{}, }, { name: "DescribeTaskList - Error in resolving peer", op: func(c Client) (any, error) { return c.DescribeTaskList(context.Background(), testMatchingDescribeTaskListRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", assert.AnError) }, want: nil, wantError: true, }, { name: "DescribeTaskList - Error while describing tasklist", op: func(c Client) (any, error) { return c.DescribeTaskList(context.Background(), testMatchingDescribeTaskListRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, assert.AnError) }, want: nil, wantError: true, }, { name: "ListTaskListPartitions", op: func(c Client) (any, error) { return c.ListTaskListPartitions(context.Background(), testMatchingListTaskListPartitionsRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().ListTaskListPartitions(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.ListTaskListPartitionsResponse{}, nil) }, want: &types.ListTaskListPartitionsResponse{}, }, { name: "ListTaskListPartitions - Error in resolving peer", op: func(c Client) (any, error) { return c.ListTaskListPartitions(context.Background(), testMatchingListTaskListPartitionsRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", assert.AnError) }, want: nil, wantError: true, }, { name: "ListTaskListPartitions - Error while listing tasklist partitions", op: func(c Client) (any, error) { return c.ListTaskListPartitions(context.Background(), testMatchingListTaskListPartitionsRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().ListTaskListPartitions(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, assert.AnError) }, want: nil, wantError: true, }, { name: "GetTaskListsByDomain", op: func(c Client) (any, error) { return c.GetTaskListsByDomain(context.Background(), testGetTaskListsByDomainRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().GetAllPeers().Return([]string{"peer0", "peer1"}, nil) c.EXPECT().GetTaskListsByDomain(gomock.Any(), testGetTaskListsByDomainRequest(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.GetTaskListsByDomainResponse{}, nil) c.EXPECT().GetTaskListsByDomain(gomock.Any(), testGetTaskListsByDomainRequest(), []yarpc.CallOption{yarpc.WithShardKey("peer1")}).Return(&types.GetTaskListsByDomainResponse{}, nil) }, want: &types.GetTaskListsByDomainResponse{ DecisionTaskListMap: make(map[string]*types.DescribeTaskListResponse), ActivityTaskListMap: make(map[string]*types.DescribeTaskListResponse), }, }, { name: "GetTaskListsByDomain - Error in resolving peer", op: func(c Client) (any, error) { return c.GetTaskListsByDomain(context.Background(), testGetTaskListsByDomainRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().GetAllPeers().Return([]string{"peer0", "peer1"}, assert.AnError) }, want: nil, wantError: true, }, { name: "UpdateTaskListPartitionConfig", op: func(c Client) (any, error) { return c.UpdateTaskListPartitionConfig(context.Background(), testMatchingUpdateTaskListPartitionConfigRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.MatchingUpdateTaskListPartitionConfigResponse{}, nil) }, want: &types.MatchingUpdateTaskListPartitionConfigResponse{}, }, { name: "UpdateTaskListPartitionConfig - Error in resolving peer", op: func(c Client) (any, error) { return c.UpdateTaskListPartitionConfig(context.Background(), testMatchingUpdateTaskListPartitionConfigRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", assert.AnError) }, want: nil, wantError: true, }, { name: "UpdateTaskListPartitionConfig - Error while listing tasklist partitions", op: func(c Client) (any, error) { return c.UpdateTaskListPartitionConfig(context.Background(), testMatchingUpdateTaskListPartitionConfigRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, assert.AnError) }, want: nil, wantError: true, }, { name: "RefreshTaskListPartitionConfig", op: func(c Client) (any, error) { return c.RefreshTaskListPartitionConfig(context.Background(), testMatchingRefreshTaskListPartitionConfigRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil) }, want: &types.MatchingRefreshTaskListPartitionConfigResponse{}, }, { name: "RefreshTaskListPartitionConfig - Error in resolving peer", op: func(c Client) (any, error) { return c.RefreshTaskListPartitionConfig(context.Background(), testMatchingRefreshTaskListPartitionConfigRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", assert.AnError) }, want: nil, wantError: true, }, { name: "RefreshTaskListPartitionConfig - Error while listing tasklist partitions", op: func(c Client) (any, error) { return c.RefreshTaskListPartitionConfig(context.Background(), testMatchingRefreshTaskListPartitionConfigRequest()) }, mock: func(p *MockPeerResolver, balancer *MockLoadBalancer, c *MockClient, mp *MockPartitionConfigProvider) { p.EXPECT().FromTaskList(_testTaskList).Return("peer0", nil) c.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), gomock.Any(), []yarpc.CallOption{yarpc.WithShardKey("peer0")}).Return(nil, assert.AnError) }, want: nil, wantError: true, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { // setting up client ctrl := gomock.NewController(t) client := NewMockClient(ctrl) peerResolverMock := NewMockPeerResolver(ctrl) loadbalancerMock := NewMockLoadBalancer(ctrl) providerMock := NewMockPartitionConfigProvider(ctrl) tt.mock(peerResolverMock, loadbalancerMock, client, providerMock) c := NewClient(client, peerResolverMock, loadbalancerMock, providerMock) res, err := tt.op(c) if tt.wantError { assert.Error(t, err) assert.Nil(t, res) if tt.validateError != nil { tt.validateError(t, err) } } else { assert.NoError(t, err) assert.Equal(t, tt.want, res) } }) } } func testAddActivityTaskRequest() *types.AddActivityTaskRequest { return &types.AddActivityTaskRequest{ DomainUUID: _testDomainUUID, TaskList: &types.TaskList{Name: _testTaskList}, } } func testAddDecisionTaskRequest() *types.AddDecisionTaskRequest { return &types.AddDecisionTaskRequest{ DomainUUID: _testDomainUUID, TaskList: &types.TaskList{Name: _testTaskList}, } } func testRespondQueryTaskCompletedRequest() *types.MatchingRespondQueryTaskCompletedRequest { return &types.MatchingRespondQueryTaskCompletedRequest{ DomainUUID: _testDomainUUID, TaskList: &types.TaskList{Name: _testTaskList}, } } func testCancelOutstandingPollRequest() *types.CancelOutstandingPollRequest { return &types.CancelOutstandingPollRequest{ DomainUUID: _testDomainUUID, TaskList: &types.TaskList{Name: _testTaskList}, } } func testMatchingPollForActivityTaskRequest() *types.MatchingPollForActivityTaskRequest { return &types.MatchingPollForActivityTaskRequest{ DomainUUID: _testDomainUUID, PollRequest: &types.PollForActivityTaskRequest{TaskList: &types.TaskList{Name: _testTaskList}}, } } func testMatchingPollForDecisionTaskRequest() *types.MatchingPollForDecisionTaskRequest { return &types.MatchingPollForDecisionTaskRequest{ DomainUUID: _testDomainUUID, PollRequest: &types.PollForDecisionTaskRequest{TaskList: &types.TaskList{Name: _testTaskList}}, } } func testMatchingQueryWorkflowRequest() *types.MatchingQueryWorkflowRequest { return &types.MatchingQueryWorkflowRequest{ DomainUUID: _testDomainUUID, TaskList: &types.TaskList{Name: _testTaskList}, QueryRequest: &types.QueryWorkflowRequest{Domain: _testDomain}, } } func testMatchingDescribeTaskListRequest() *types.MatchingDescribeTaskListRequest { return &types.MatchingDescribeTaskListRequest{ DomainUUID: _testDomainUUID, DescRequest: &types.DescribeTaskListRequest{TaskList: &types.TaskList{Name: _testTaskList}}, } } func testMatchingListTaskListPartitionsRequest() *types.MatchingListTaskListPartitionsRequest { return &types.MatchingListTaskListPartitionsRequest{ Domain: _testDomainUUID, TaskList: &types.TaskList{Name: _testTaskList}, } } func testGetTaskListsByDomainRequest() *types.GetTaskListsByDomainRequest { return &types.GetTaskListsByDomainRequest{ Domain: _testDomain, } } func testMatchingUpdateTaskListPartitionConfigRequest() *types.MatchingUpdateTaskListPartitionConfigRequest { return &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: _testDomainUUID, TaskList: &types.TaskList{Name: _testTaskList}, PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, 2: { IsolationGroups: []string{"foo"}, }, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, 1: { IsolationGroups: []string{"bar"}, }, }, }, } } func testMatchingRefreshTaskListPartitionConfigRequest() *types.MatchingRefreshTaskListPartitionConfigRequest { return &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: _testDomainUUID, TaskList: &types.TaskList{Name: _testTaskList}, PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, 2: { IsolationGroups: []string{"foo"}, }, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, 1: { IsolationGroups: []string{"bar"}, }, }, }, } } ================================================ FILE: client/matching/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package matching import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -package matching github.com/uber/cadence/client/matching Client //go:generate gowrap gen -g -p . -i Client -t ../templates/retry.tmpl -o ../wrappers/retryable/matching_generated.go -v client=Matching //go:generate gowrap gen -g -p . -i Client -t ../templates/metered.tmpl -o ../wrappers/metered/matching_generated.go -v client=Matching //go:generate gowrap gen -g -p . -i Client -t ../templates/errorinjectors.tmpl -o ../wrappers/errorinjectors/matching_generated.go -v client=Matching //go:generate gowrap gen -g -p . -i Client -t ../templates/grpc.tmpl -o ../wrappers/grpc/matching_generated.go -v client=Matching -v package=matchingv1 -v path=github.com/uber/cadence/.gen/proto/matching/v1 -v prefix=Matching //go:generate gowrap gen -g -p . -i Client -t ../templates/thrift.tmpl -o ../wrappers/thrift/matching_generated.go -v client=Matching -v prefix=Matching //go:generate gowrap gen -g -p . -i Client -t ../templates/timeout.tmpl -o ../wrappers/timeout/matching_generated.go -v client=Matching // Client is the interface exposed by types service client type Client interface { AddActivityTask(context.Context, *types.AddActivityTaskRequest, ...yarpc.CallOption) (*types.AddActivityTaskResponse, error) AddDecisionTask(context.Context, *types.AddDecisionTaskRequest, ...yarpc.CallOption) (*types.AddDecisionTaskResponse, error) CancelOutstandingPoll(context.Context, *types.CancelOutstandingPollRequest, ...yarpc.CallOption) error DescribeTaskList(context.Context, *types.MatchingDescribeTaskListRequest, ...yarpc.CallOption) (*types.DescribeTaskListResponse, error) ListTaskListPartitions(context.Context, *types.MatchingListTaskListPartitionsRequest, ...yarpc.CallOption) (*types.ListTaskListPartitionsResponse, error) GetTaskListsByDomain(context.Context, *types.GetTaskListsByDomainRequest, ...yarpc.CallOption) (*types.GetTaskListsByDomainResponse, error) PollForActivityTask(context.Context, *types.MatchingPollForActivityTaskRequest, ...yarpc.CallOption) (*types.MatchingPollForActivityTaskResponse, error) PollForDecisionTask(context.Context, *types.MatchingPollForDecisionTaskRequest, ...yarpc.CallOption) (*types.MatchingPollForDecisionTaskResponse, error) QueryWorkflow(context.Context, *types.MatchingQueryWorkflowRequest, ...yarpc.CallOption) (*types.MatchingQueryWorkflowResponse, error) RespondQueryTaskCompleted(context.Context, *types.MatchingRespondQueryTaskCompletedRequest, ...yarpc.CallOption) error UpdateTaskListPartitionConfig(context.Context, *types.MatchingUpdateTaskListPartitionConfigRequest, ...yarpc.CallOption) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) RefreshTaskListPartitionConfig(context.Context, *types.MatchingRefreshTaskListPartitionConfigRequest, ...yarpc.CallOption) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) } ================================================ FILE: client/matching/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package matching -source interface.go -destination interface_mock.go -package matching github.com/uber/cadence/client/matching Client // // Package matching is a generated GoMock package. package matching import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" types "github.com/uber/cadence/common/types" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // AddActivityTask mocks base method. func (m *MockClient) AddActivityTask(arg0 context.Context, arg1 *types.AddActivityTaskRequest, arg2 ...yarpc.CallOption) (*types.AddActivityTaskResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "AddActivityTask", varargs...) ret0, _ := ret[0].(*types.AddActivityTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AddActivityTask indicates an expected call of AddActivityTask. func (mr *MockClientMockRecorder) AddActivityTask(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActivityTask", reflect.TypeOf((*MockClient)(nil).AddActivityTask), varargs...) } // AddDecisionTask mocks base method. func (m *MockClient) AddDecisionTask(arg0 context.Context, arg1 *types.AddDecisionTaskRequest, arg2 ...yarpc.CallOption) (*types.AddDecisionTaskResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "AddDecisionTask", varargs...) ret0, _ := ret[0].(*types.AddDecisionTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTask indicates an expected call of AddDecisionTask. func (mr *MockClientMockRecorder) AddDecisionTask(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTask", reflect.TypeOf((*MockClient)(nil).AddDecisionTask), varargs...) } // CancelOutstandingPoll mocks base method. func (m *MockClient) CancelOutstandingPoll(arg0 context.Context, arg1 *types.CancelOutstandingPollRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CancelOutstandingPoll", varargs...) ret0, _ := ret[0].(error) return ret0 } // CancelOutstandingPoll indicates an expected call of CancelOutstandingPoll. func (mr *MockClientMockRecorder) CancelOutstandingPoll(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelOutstandingPoll", reflect.TypeOf((*MockClient)(nil).CancelOutstandingPoll), varargs...) } // DescribeTaskList mocks base method. func (m *MockClient) DescribeTaskList(arg0 context.Context, arg1 *types.MatchingDescribeTaskListRequest, arg2 ...yarpc.CallOption) (*types.DescribeTaskListResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DescribeTaskList", varargs...) ret0, _ := ret[0].(*types.DescribeTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeTaskList indicates an expected call of DescribeTaskList. func (mr *MockClientMockRecorder) DescribeTaskList(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTaskList", reflect.TypeOf((*MockClient)(nil).DescribeTaskList), varargs...) } // GetTaskListsByDomain mocks base method. func (m *MockClient) GetTaskListsByDomain(arg0 context.Context, arg1 *types.GetTaskListsByDomainRequest, arg2 ...yarpc.CallOption) (*types.GetTaskListsByDomainResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetTaskListsByDomain", varargs...) ret0, _ := ret[0].(*types.GetTaskListsByDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTaskListsByDomain indicates an expected call of GetTaskListsByDomain. func (mr *MockClientMockRecorder) GetTaskListsByDomain(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskListsByDomain", reflect.TypeOf((*MockClient)(nil).GetTaskListsByDomain), varargs...) } // ListTaskListPartitions mocks base method. func (m *MockClient) ListTaskListPartitions(arg0 context.Context, arg1 *types.MatchingListTaskListPartitionsRequest, arg2 ...yarpc.CallOption) (*types.ListTaskListPartitionsResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListTaskListPartitions", varargs...) ret0, _ := ret[0].(*types.ListTaskListPartitionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTaskListPartitions indicates an expected call of ListTaskListPartitions. func (mr *MockClientMockRecorder) ListTaskListPartitions(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTaskListPartitions", reflect.TypeOf((*MockClient)(nil).ListTaskListPartitions), varargs...) } // PollForActivityTask mocks base method. func (m *MockClient) PollForActivityTask(arg0 context.Context, arg1 *types.MatchingPollForActivityTaskRequest, arg2 ...yarpc.CallOption) (*types.MatchingPollForActivityTaskResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "PollForActivityTask", varargs...) ret0, _ := ret[0].(*types.MatchingPollForActivityTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForActivityTask indicates an expected call of PollForActivityTask. func (mr *MockClientMockRecorder) PollForActivityTask(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForActivityTask", reflect.TypeOf((*MockClient)(nil).PollForActivityTask), varargs...) } // PollForDecisionTask mocks base method. func (m *MockClient) PollForDecisionTask(arg0 context.Context, arg1 *types.MatchingPollForDecisionTaskRequest, arg2 ...yarpc.CallOption) (*types.MatchingPollForDecisionTaskResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "PollForDecisionTask", varargs...) ret0, _ := ret[0].(*types.MatchingPollForDecisionTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForDecisionTask indicates an expected call of PollForDecisionTask. func (mr *MockClientMockRecorder) PollForDecisionTask(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForDecisionTask", reflect.TypeOf((*MockClient)(nil).PollForDecisionTask), varargs...) } // QueryWorkflow mocks base method. func (m *MockClient) QueryWorkflow(arg0 context.Context, arg1 *types.MatchingQueryWorkflowRequest, arg2 ...yarpc.CallOption) (*types.MatchingQueryWorkflowResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "QueryWorkflow", varargs...) ret0, _ := ret[0].(*types.MatchingQueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // QueryWorkflow indicates an expected call of QueryWorkflow. func (mr *MockClientMockRecorder) QueryWorkflow(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockClient)(nil).QueryWorkflow), varargs...) } // RefreshTaskListPartitionConfig mocks base method. func (m *MockClient) RefreshTaskListPartitionConfig(arg0 context.Context, arg1 *types.MatchingRefreshTaskListPartitionConfigRequest, arg2 ...yarpc.CallOption) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RefreshTaskListPartitionConfig", varargs...) ret0, _ := ret[0].(*types.MatchingRefreshTaskListPartitionConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RefreshTaskListPartitionConfig indicates an expected call of RefreshTaskListPartitionConfig. func (mr *MockClientMockRecorder) RefreshTaskListPartitionConfig(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTaskListPartitionConfig", reflect.TypeOf((*MockClient)(nil).RefreshTaskListPartitionConfig), varargs...) } // RespondQueryTaskCompleted mocks base method. func (m *MockClient) RespondQueryTaskCompleted(arg0 context.Context, arg1 *types.MatchingRespondQueryTaskCompletedRequest, arg2 ...yarpc.CallOption) error { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RespondQueryTaskCompleted", varargs...) ret0, _ := ret[0].(error) return ret0 } // RespondQueryTaskCompleted indicates an expected call of RespondQueryTaskCompleted. func (mr *MockClientMockRecorder) RespondQueryTaskCompleted(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondQueryTaskCompleted", reflect.TypeOf((*MockClient)(nil).RespondQueryTaskCompleted), varargs...) } // UpdateTaskListPartitionConfig mocks base method. func (m *MockClient) UpdateTaskListPartitionConfig(arg0 context.Context, arg1 *types.MatchingUpdateTaskListPartitionConfigRequest, arg2 ...yarpc.CallOption) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "UpdateTaskListPartitionConfig", varargs...) ret0, _ := ret[0].(*types.MatchingUpdateTaskListPartitionConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskListPartitionConfig indicates an expected call of UpdateTaskListPartitionConfig. func (mr *MockClientMockRecorder) UpdateTaskListPartitionConfig(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListPartitionConfig", reflect.TypeOf((*MockClient)(nil).UpdateTaskListPartitionConfig), varargs...) } ================================================ FILE: client/matching/isolation_loadbalancer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "math/rand" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/types" ) type isolationLoadBalancer struct { provider PartitionConfigProvider fallback WeightedLoadBalancer domainIDToName func(string) (string, error) isolationAssignmentEnabled func(string) bool } func NewIsolationLoadBalancer(fallback WeightedLoadBalancer, provider PartitionConfigProvider, domainIDToName func(string) (string, error), config *dynamicconfig.Collection) LoadBalancer { isolationAssignmentEnabled := config.GetBoolPropertyFilteredByDomain(dynamicproperties.EnablePartitionIsolationGroupAssignment) return &isolationLoadBalancer{ provider: provider, fallback: fallback, domainIDToName: domainIDToName, isolationAssignmentEnabled: isolationAssignmentEnabled, } } func (i *isolationLoadBalancer) PickWritePartition(taskListType int, req WriteRequest) string { taskList := *req.GetTaskList() domainName, err := i.domainIDToName(req.GetDomainUUID()) if err != nil || !i.isolationAssignmentEnabled(domainName) { return i.fallback.PickWritePartition(taskListType, req) } taskGroup, ok := req.GetPartitionConfig()[isolationgroup.GroupKey] if !ok || taskGroup == "" { return i.fallback.PickWritePartition(taskListType, req) } config := i.provider.GetPartitionConfig(req.GetDomainUUID(), taskList, taskListType) if len(config.WritePartitions) == 1 { return taskList.GetName() } partitions := getPartitionsForGroup(taskGroup, config.WritePartitions) if len(partitions) == 0 { return i.fallback.PickWritePartition(taskListType, req) } p := pickRandom(partitions) return getPartitionTaskListName(taskList.GetName(), p) } func (i *isolationLoadBalancer) PickReadPartition(taskListType int, req ReadRequest, isolationGroup string) string { taskList := *req.GetTaskList() domainName, err := i.domainIDToName(req.GetDomainUUID()) if err != nil || !i.isolationAssignmentEnabled(domainName) || isolationGroup == "" { return i.fallback.PickReadPartition(taskListType, req, isolationGroup) } config := i.provider.GetPartitionConfig(req.GetDomainUUID(), taskList, taskListType) if len(config.ReadPartitions) == 1 { return taskList.GetName() } partitions := getPartitionsForGroup(isolationGroup, config.ReadPartitions) if len(partitions) == 0 { return i.fallback.PickReadPartition(taskListType, req, isolationGroup) } p := partitions[0] if len(partitions) > 1 { p = i.fallback.PickBetween(req.GetDomainUUID(), taskList.GetName(), taskListType, partitions) if p == -1 { p = pickRandom(partitions) } } return getPartitionTaskListName(taskList.GetName(), p) } func (i *isolationLoadBalancer) UpdateWeight(taskListType int, req ReadRequest, partition string, info *types.LoadBalancerHints) { i.fallback.UpdateWeight(taskListType, req, partition, info) } func getPartitionsForGroup(taskGroup string, partitions map[int]*types.TaskListPartition) []int { if taskGroup == "" { return nil } var res []int for id := range len(partitions) { p := partitions[id] if partitionAcceptsGroup(p, taskGroup) { res = append(res, id) } } return res } func pickRandom(partitions []int) int { picked := rand.Intn(len(partitions)) return partitions[picked] } func partitionAcceptsGroup(partition *types.TaskListPartition, taskGroup string) bool { // Accepts all groups if len(partition.IsolationGroups) == 0 { return true } for _, ig := range partition.IsolationGroups { if ig == taskGroup { return true } } return false } ================================================ FILE: client/matching/isolation_loadbalancer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) var testDomain = "domainID" func TestIsolationPickWritePartition(t *testing.T) { tl := "tl" cases := []struct { name string group string config *types.TaskListPartitionConfig disableIsolation bool shouldFallback bool allowed []string }{ { name: "single partition", group: "a", config: &types.TaskListPartitionConfig{ WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, allowed: []string{tl}, }, { name: "single partition - isolation disabled", group: "a", config: &types.TaskListPartitionConfig{ WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, disableIsolation: true, shouldFallback: true, allowed: []string{"fallback"}, }, { name: "multiple partitions - single option", group: "b", config: &types.TaskListPartitionConfig{ WritePartitions: map[int]*types.TaskListPartition{ 0: {[]string{"a"}}, 1: {[]string{"b"}}, }, }, allowed: []string{getPartitionTaskListName(tl, 1)}, }, { name: "multiple partitions - multiple options", group: "a", config: &types.TaskListPartitionConfig{ WritePartitions: map[int]*types.TaskListPartition{ 0: {[]string{"a"}}, 1: {[]string{"a"}}, }, }, allowed: []string{tl, getPartitionTaskListName(tl, 1)}, }, { name: "multiple partitions - no match", group: "c", config: &types.TaskListPartitionConfig{ WritePartitions: map[int]*types.TaskListPartition{ 0: {[]string{"a"}}, 1: {[]string{"b"}}, }, }, shouldFallback: true, allowed: []string{"fallback"}, }, { name: "fallback - no group", config: &types.TaskListPartitionConfig{ WritePartitions: map[int]*types.TaskListPartition{ 0: {[]string{"a"}}, 1: {[]string{"b"}}, }, }, shouldFallback: true, allowed: []string{"fallback"}, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { lb, fallback := createWithMocks(t, !tc.disableIsolation, tc.config) req := &types.AddDecisionTaskRequest{ DomainUUID: testDomain, TaskList: &types.TaskList{ Name: tl, Kind: types.TaskListKindSticky.Ptr(), }, } if tc.group != "" { req.PartitionConfig = map[string]string{ isolationgroup.GroupKey: tc.group, } } if tc.shouldFallback { fallback.EXPECT().PickWritePartition(int(types.TaskListTypeDecision), req).Return("fallback").Times(1) } p := lb.PickWritePartition(0, req) assert.Contains(t, tc.allowed, p) }) } } func TestIsolationPickReadPartition(t *testing.T) { tl := "tl" cases := []struct { name string group string config *types.TaskListPartitionConfig disableIsolation bool allowance func(balancer *MockWeightedLoadBalancer) expected string }{ { name: "single partition", group: "a", config: &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, expected: tl, }, { name: "single partition - isolation disabled", group: "a", config: &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, disableIsolation: true, allowance: func(balancer *MockWeightedLoadBalancer) { balancer.EXPECT().PickReadPartition(gomock.Any(), gomock.Any(), gomock.Any()).Return("fallback") }, expected: "fallback", }, { name: "multiple partitions - single option", group: "b", config: &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {[]string{"a"}}, 1: {[]string{"b"}}, }, }, expected: getPartitionTaskListName(tl, 1), }, { name: "multiple partitions - multiple options", group: "a", config: &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {[]string{"a"}}, 1: {[]string{"a"}}, 2: {[]string{"b"}}, }, }, allowance: func(balancer *MockWeightedLoadBalancer) { balancer.EXPECT().PickBetween(testDomain, tl, 0, []int{0, 1}).Return(1) }, expected: getPartitionTaskListName(tl, 1), }, { name: "multiple partitions - no matching", group: "c", config: &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {[]string{"a"}}, 1: {[]string{"b"}}, }, }, allowance: func(balancer *MockWeightedLoadBalancer) { balancer.EXPECT().PickReadPartition(gomock.Any(), gomock.Any(), gomock.Any()).Return("fallback") }, expected: "fallback", }, { name: "fallback - no group", config: &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {[]string{"a"}}, 1: {[]string{"b"}}, }, }, allowance: func(balancer *MockWeightedLoadBalancer) { balancer.EXPECT().PickReadPartition(gomock.Any(), gomock.Any(), gomock.Any()).Return("fallback") }, expected: "fallback", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { lb, fallback := createWithMocks(t, !tc.disableIsolation, tc.config) req := &types.MatchingQueryWorkflowRequest{ DomainUUID: testDomain, TaskList: &types.TaskList{ Name: tl, Kind: types.TaskListKindSticky.Ptr(), }, } if tc.allowance != nil { tc.allowance(fallback) } actual := lb.PickReadPartition(0, req, tc.group) assert.Equal(t, tc.expected, actual) }) } } func TestIsolationGetPartitionsForGroup(t *testing.T) { cases := []struct { name string group string partitions map[int]*types.TaskListPartition expected []int }{ { name: "single partition", group: "a", partitions: map[int]*types.TaskListPartition{ 0: {[]string{"a", "b", "c"}}, }, expected: []int{0}, }, { name: "single partition - wildcard", group: "a", partitions: map[int]*types.TaskListPartition{ 0: {}, }, expected: []int{0}, }, { name: "single partition - no options", group: "a", partitions: map[int]*types.TaskListPartition{ 0: {[]string{"b"}}, }, expected: nil, }, { name: "multiple partitions - single option", group: "b", partitions: map[int]*types.TaskListPartition{ 0: {[]string{"a", "c"}}, 1: {[]string{"b"}}, }, expected: []int{1}, }, { name: "multiple partitions - multiple options", group: "b", partitions: map[int]*types.TaskListPartition{ 0: {[]string{"a", "b", "c"}}, 1: {[]string{"b"}}, 2: {[]string{"d"}}, }, expected: []int{0, 1}, }, { name: "multiple partitions - multiple options with wildcard", group: "b", partitions: map[int]*types.TaskListPartition{ 0: {[]string{"a", "c"}}, 1: {[]string{"b"}}, 2: {}, }, expected: []int{1, 2}, }, { name: "multiple partitions - no options", group: "d", partitions: map[int]*types.TaskListPartition{ 0: {[]string{"a", "c"}}, 1: {[]string{"b"}}, 2: {[]string{"c"}}, }, expected: nil, }, { name: "no group", partitions: map[int]*types.TaskListPartition{ 0: {[]string{"a", "b", "c"}}, }, expected: nil, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { actual := getPartitionsForGroup(tc.group, tc.partitions) if tc.expected == nil { assert.Nil(t, actual) } else { assert.ElementsMatch(t, tc.expected, actual) } }) } } func createWithMocks(t *testing.T, isolationEnabled bool, config *types.TaskListPartitionConfig) (*isolationLoadBalancer, *MockWeightedLoadBalancer) { ctrl := gomock.NewController(t) fallback := NewMockWeightedLoadBalancer(ctrl) cfg := NewMockPartitionConfigProvider(ctrl) cfg.EXPECT().GetPartitionConfig(gomock.Any(), gomock.Any(), gomock.Any()).Return(config).AnyTimes() dynamicClient := dynamicconfig.NewInMemoryClient() require.NoError(t, dynamicClient.UpdateValue(dynamicproperties.EnablePartitionIsolationGroupAssignment, isolationEnabled)) dc := dynamicconfig.NewCollection(dynamicClient, testlogger.New(t)) lb := NewIsolationLoadBalancer(fallback, cfg, func(s string) (string, error) { return s, nil }, dc).(*isolationLoadBalancer) return lb, fallback } ================================================ FILE: client/matching/loadbalancer.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination loadbalancer_mock.go -package matching github.com/uber/cadence/client/matching LoadBalancer,WeightedLoadBalancer package matching import ( "fmt" "math/rand" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) type ( // WriteRequest is the interface for all types of AddTask* requests WriteRequest interface { ReadRequest GetPartitionConfig() map[string]string } // ReadRequest is the interface for all types of Poll* requests ReadRequest interface { GetDomainUUID() string GetTaskList() *types.TaskList GetForwardedFrom() string } // LoadBalancer is the interface for implementers of // component that distributes add/poll api calls across // available task list partitions when possible LoadBalancer interface { // PickWritePartition returns the task list partition for adding // an activity or decision task. The input is the name of the // original task list (with no partition info). When forwardedFrom // is non-empty, this call is forwardedFrom from a child partition // to a parent partition in which case, no load balancing should be // performed PickWritePartition( taskListType int, request WriteRequest, ) string // PickReadPartition returns the task list partition to send a poller to. // Input is name of the original task list as specified by caller. When // forwardedFrom is non-empty, no load balancing should be done. PickReadPartition( taskListType int, request ReadRequest, isolationGroup string, ) string // UpdateWeight updates the weight of a task list partition. // Input is name of the original task list as specified by caller. When // the original task list is a partition, no update should be done. UpdateWeight( taskListType int, request ReadRequest, partition string, info *types.LoadBalancerHints, ) } WeightedLoadBalancer interface { LoadBalancer PickBetween(domainID, taskListName string, taskListType int, partitions []int) int } defaultLoadBalancer struct { provider PartitionConfigProvider } ) // NewLoadBalancer returns an instance of matching load balancer that // can help distribute api calls across task list partitions func NewLoadBalancer( provider PartitionConfigProvider, ) LoadBalancer { return &defaultLoadBalancer{ provider: provider, } } func (lb *defaultLoadBalancer) PickWritePartition( taskListType int, req WriteRequest, ) string { nPartitions := lb.provider.GetNumberOfWritePartitions(req.GetDomainUUID(), *req.GetTaskList(), taskListType) return lb.pickPartition(*req.GetTaskList(), req.GetForwardedFrom(), nPartitions) } func (lb *defaultLoadBalancer) PickReadPartition( taskListType int, req ReadRequest, _ string, ) string { n := lb.provider.GetNumberOfReadPartitions(req.GetDomainUUID(), *req.GetTaskList(), taskListType) return lb.pickPartition(*req.GetTaskList(), req.GetForwardedFrom(), n) } func (lb *defaultLoadBalancer) pickPartition( taskList types.TaskList, forwardedFrom string, nPartitions int, ) string { if nPartitions <= 1 { return taskList.GetName() } p := rand.Intn(nPartitions) return getPartitionTaskListName(taskList.GetName(), p) } func (lb *defaultLoadBalancer) UpdateWeight( taskListType int, req ReadRequest, partition string, info *types.LoadBalancerHints, ) { } func getPartitionTaskListName(root string, partition int) string { if partition <= 0 { return root } return fmt.Sprintf("%v%v/%v", constants.ReservedTaskListPrefix, root, partition) } ================================================ FILE: client/matching/loadbalancer_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: loadbalancer.go // // Generated by this command: // // mockgen -package matching -source loadbalancer.go -destination loadbalancer_mock.go -package matching github.com/uber/cadence/client/matching LoadBalancer,WeightedLoadBalancer // // Package matching is a generated GoMock package. package matching import ( reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockWriteRequest is a mock of WriteRequest interface. type MockWriteRequest struct { ctrl *gomock.Controller recorder *MockWriteRequestMockRecorder isgomock struct{} } // MockWriteRequestMockRecorder is the mock recorder for MockWriteRequest. type MockWriteRequestMockRecorder struct { mock *MockWriteRequest } // NewMockWriteRequest creates a new mock instance. func NewMockWriteRequest(ctrl *gomock.Controller) *MockWriteRequest { mock := &MockWriteRequest{ctrl: ctrl} mock.recorder = &MockWriteRequestMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWriteRequest) EXPECT() *MockWriteRequestMockRecorder { return m.recorder } // GetDomainUUID mocks base method. func (m *MockWriteRequest) GetDomainUUID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainUUID") ret0, _ := ret[0].(string) return ret0 } // GetDomainUUID indicates an expected call of GetDomainUUID. func (mr *MockWriteRequestMockRecorder) GetDomainUUID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainUUID", reflect.TypeOf((*MockWriteRequest)(nil).GetDomainUUID)) } // GetForwardedFrom mocks base method. func (m *MockWriteRequest) GetForwardedFrom() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetForwardedFrom") ret0, _ := ret[0].(string) return ret0 } // GetForwardedFrom indicates an expected call of GetForwardedFrom. func (mr *MockWriteRequestMockRecorder) GetForwardedFrom() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetForwardedFrom", reflect.TypeOf((*MockWriteRequest)(nil).GetForwardedFrom)) } // GetPartitionConfig mocks base method. func (m *MockWriteRequest) GetPartitionConfig() map[string]string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPartitionConfig") ret0, _ := ret[0].(map[string]string) return ret0 } // GetPartitionConfig indicates an expected call of GetPartitionConfig. func (mr *MockWriteRequestMockRecorder) GetPartitionConfig() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartitionConfig", reflect.TypeOf((*MockWriteRequest)(nil).GetPartitionConfig)) } // GetTaskList mocks base method. func (m *MockWriteRequest) GetTaskList() *types.TaskList { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskList") ret0, _ := ret[0].(*types.TaskList) return ret0 } // GetTaskList indicates an expected call of GetTaskList. func (mr *MockWriteRequestMockRecorder) GetTaskList() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskList", reflect.TypeOf((*MockWriteRequest)(nil).GetTaskList)) } // MockReadRequest is a mock of ReadRequest interface. type MockReadRequest struct { ctrl *gomock.Controller recorder *MockReadRequestMockRecorder isgomock struct{} } // MockReadRequestMockRecorder is the mock recorder for MockReadRequest. type MockReadRequestMockRecorder struct { mock *MockReadRequest } // NewMockReadRequest creates a new mock instance. func NewMockReadRequest(ctrl *gomock.Controller) *MockReadRequest { mock := &MockReadRequest{ctrl: ctrl} mock.recorder = &MockReadRequestMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockReadRequest) EXPECT() *MockReadRequestMockRecorder { return m.recorder } // GetDomainUUID mocks base method. func (m *MockReadRequest) GetDomainUUID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainUUID") ret0, _ := ret[0].(string) return ret0 } // GetDomainUUID indicates an expected call of GetDomainUUID. func (mr *MockReadRequestMockRecorder) GetDomainUUID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainUUID", reflect.TypeOf((*MockReadRequest)(nil).GetDomainUUID)) } // GetForwardedFrom mocks base method. func (m *MockReadRequest) GetForwardedFrom() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetForwardedFrom") ret0, _ := ret[0].(string) return ret0 } // GetForwardedFrom indicates an expected call of GetForwardedFrom. func (mr *MockReadRequestMockRecorder) GetForwardedFrom() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetForwardedFrom", reflect.TypeOf((*MockReadRequest)(nil).GetForwardedFrom)) } // GetTaskList mocks base method. func (m *MockReadRequest) GetTaskList() *types.TaskList { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskList") ret0, _ := ret[0].(*types.TaskList) return ret0 } // GetTaskList indicates an expected call of GetTaskList. func (mr *MockReadRequestMockRecorder) GetTaskList() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskList", reflect.TypeOf((*MockReadRequest)(nil).GetTaskList)) } // MockLoadBalancer is a mock of LoadBalancer interface. type MockLoadBalancer struct { ctrl *gomock.Controller recorder *MockLoadBalancerMockRecorder isgomock struct{} } // MockLoadBalancerMockRecorder is the mock recorder for MockLoadBalancer. type MockLoadBalancerMockRecorder struct { mock *MockLoadBalancer } // NewMockLoadBalancer creates a new mock instance. func NewMockLoadBalancer(ctrl *gomock.Controller) *MockLoadBalancer { mock := &MockLoadBalancer{ctrl: ctrl} mock.recorder = &MockLoadBalancerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockLoadBalancer) EXPECT() *MockLoadBalancerMockRecorder { return m.recorder } // PickReadPartition mocks base method. func (m *MockLoadBalancer) PickReadPartition(taskListType int, request ReadRequest, isolationGroup string) string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PickReadPartition", taskListType, request, isolationGroup) ret0, _ := ret[0].(string) return ret0 } // PickReadPartition indicates an expected call of PickReadPartition. func (mr *MockLoadBalancerMockRecorder) PickReadPartition(taskListType, request, isolationGroup any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PickReadPartition", reflect.TypeOf((*MockLoadBalancer)(nil).PickReadPartition), taskListType, request, isolationGroup) } // PickWritePartition mocks base method. func (m *MockLoadBalancer) PickWritePartition(taskListType int, request WriteRequest) string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PickWritePartition", taskListType, request) ret0, _ := ret[0].(string) return ret0 } // PickWritePartition indicates an expected call of PickWritePartition. func (mr *MockLoadBalancerMockRecorder) PickWritePartition(taskListType, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PickWritePartition", reflect.TypeOf((*MockLoadBalancer)(nil).PickWritePartition), taskListType, request) } // UpdateWeight mocks base method. func (m *MockLoadBalancer) UpdateWeight(taskListType int, request ReadRequest, partition string, info *types.LoadBalancerHints) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdateWeight", taskListType, request, partition, info) } // UpdateWeight indicates an expected call of UpdateWeight. func (mr *MockLoadBalancerMockRecorder) UpdateWeight(taskListType, request, partition, info any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWeight", reflect.TypeOf((*MockLoadBalancer)(nil).UpdateWeight), taskListType, request, partition, info) } // MockWeightedLoadBalancer is a mock of WeightedLoadBalancer interface. type MockWeightedLoadBalancer struct { ctrl *gomock.Controller recorder *MockWeightedLoadBalancerMockRecorder isgomock struct{} } // MockWeightedLoadBalancerMockRecorder is the mock recorder for MockWeightedLoadBalancer. type MockWeightedLoadBalancerMockRecorder struct { mock *MockWeightedLoadBalancer } // NewMockWeightedLoadBalancer creates a new mock instance. func NewMockWeightedLoadBalancer(ctrl *gomock.Controller) *MockWeightedLoadBalancer { mock := &MockWeightedLoadBalancer{ctrl: ctrl} mock.recorder = &MockWeightedLoadBalancerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWeightedLoadBalancer) EXPECT() *MockWeightedLoadBalancerMockRecorder { return m.recorder } // PickBetween mocks base method. func (m *MockWeightedLoadBalancer) PickBetween(domainID, taskListName string, taskListType int, partitions []int) int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PickBetween", domainID, taskListName, taskListType, partitions) ret0, _ := ret[0].(int) return ret0 } // PickBetween indicates an expected call of PickBetween. func (mr *MockWeightedLoadBalancerMockRecorder) PickBetween(domainID, taskListName, taskListType, partitions any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PickBetween", reflect.TypeOf((*MockWeightedLoadBalancer)(nil).PickBetween), domainID, taskListName, taskListType, partitions) } // PickReadPartition mocks base method. func (m *MockWeightedLoadBalancer) PickReadPartition(taskListType int, request ReadRequest, isolationGroup string) string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PickReadPartition", taskListType, request, isolationGroup) ret0, _ := ret[0].(string) return ret0 } // PickReadPartition indicates an expected call of PickReadPartition. func (mr *MockWeightedLoadBalancerMockRecorder) PickReadPartition(taskListType, request, isolationGroup any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PickReadPartition", reflect.TypeOf((*MockWeightedLoadBalancer)(nil).PickReadPartition), taskListType, request, isolationGroup) } // PickWritePartition mocks base method. func (m *MockWeightedLoadBalancer) PickWritePartition(taskListType int, request WriteRequest) string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PickWritePartition", taskListType, request) ret0, _ := ret[0].(string) return ret0 } // PickWritePartition indicates an expected call of PickWritePartition. func (mr *MockWeightedLoadBalancerMockRecorder) PickWritePartition(taskListType, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PickWritePartition", reflect.TypeOf((*MockWeightedLoadBalancer)(nil).PickWritePartition), taskListType, request) } // UpdateWeight mocks base method. func (m *MockWeightedLoadBalancer) UpdateWeight(taskListType int, request ReadRequest, partition string, info *types.LoadBalancerHints) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdateWeight", taskListType, request, partition, info) } // UpdateWeight indicates an expected call of UpdateWeight. func (mr *MockWeightedLoadBalancerMockRecorder) UpdateWeight(taskListType, request, partition, info any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWeight", reflect.TypeOf((*MockWeightedLoadBalancer)(nil).UpdateWeight), taskListType, request, partition, info) } ================================================ FILE: client/matching/loadbalancer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/types" ) func setUpMocksForLoadBalancer(t *testing.T) (*defaultLoadBalancer, *MockPartitionConfigProvider) { ctrl := gomock.NewController(t) mockProvider := NewMockPartitionConfigProvider(ctrl) return &defaultLoadBalancer{ provider: mockProvider, }, mockProvider } func Test_defaultLoadBalancer_PickWritePartition(t *testing.T) { testCases := []struct { name string forwardedFrom string taskListType int nPartitions int taskListKind types.TaskListKind expectedPartitions []string }{ { name: "single write partition, forwarded", forwardedFrom: "parent-task-list", taskListType: 0, nPartitions: 1, taskListKind: types.TaskListKindNormal, expectedPartitions: []string{"test-task-list"}, }, { name: "multiple write partitions, no forward", forwardedFrom: "", taskListType: 0, nPartitions: 3, taskListKind: types.TaskListKindNormal, expectedPartitions: []string{"test-task-list", "/__cadence_sys/test-task-list/1", "/__cadence_sys/test-task-list/2"}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Set up mocks loadBalancer, mockProvider := setUpMocksForLoadBalancer(t) mockProvider.EXPECT(). GetNumberOfWritePartitions("test-domain-id", types.TaskList{Name: "test-task-list", Kind: &tc.taskListKind}, tc.taskListType). Return(tc.nPartitions). Times(1) // Pick write partition req := &types.AddDecisionTaskRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{Name: "test-task-list", Kind: &tc.taskListKind}, ForwardedFrom: tc.forwardedFrom, } partition := loadBalancer.PickWritePartition(tc.taskListType, req) // Validate result assert.Contains(t, tc.expectedPartitions, partition) }) } } func Test_defaultLoadBalancer_PickReadPartition(t *testing.T) { testCases := []struct { name string forwardedFrom string taskListType int nPartitions int taskListKind types.TaskListKind expectedPartitions []string }{ { name: "single read partition, forwarded", forwardedFrom: "parent-task-list", taskListType: 0, nPartitions: 1, taskListKind: types.TaskListKindNormal, expectedPartitions: []string{"test-task-list"}, }, { name: "multiple read partitions, no forward", forwardedFrom: "", taskListType: 0, nPartitions: 3, taskListKind: types.TaskListKindNormal, expectedPartitions: []string{"test-task-list", "/__cadence_sys/test-task-list/1", "/__cadence_sys/test-task-list/2"}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Set up mocks loadBalancer, mockProvider := setUpMocksForLoadBalancer(t) mockProvider.EXPECT(). GetNumberOfReadPartitions("test-domain-id", types.TaskList{Name: "test-task-list", Kind: &tc.taskListKind}, tc.taskListType). Return(tc.nPartitions). Times(1) // Pick read partition req := &types.AddDecisionTaskRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{Name: "test-task-list", Kind: &tc.taskListKind}, ForwardedFrom: tc.forwardedFrom, } partition := loadBalancer.PickReadPartition(tc.taskListType, req, "") // Validate result assert.Contains(t, tc.expectedPartitions, partition) }) } } func Test_defaultLoadBalancer_UpdateWeight(t *testing.T) { t.Run("no-op for task list partitions", func(t *testing.T) { // Set up mocks loadBalancer, _ := setUpMocksForLoadBalancer(t) taskList := types.TaskList{Name: "test-task-list", Kind: types.TaskListKindNormal.Ptr()} // Call UpdateWeight, should do nothing req := &types.AddDecisionTaskRequest{ DomainUUID: "test-domain-id", TaskList: &taskList, } loadBalancer.UpdateWeight(0, req, "partition", nil) // No expectations, just ensure no-op }) } func Test_defaultLoadBalancer_pickPartition(t *testing.T) { type args struct { taskList types.TaskList forwardedFrom string nPartitions int } tests := []struct { name string args args want string }{ { name: "Test: nPartitions <= 0", args: args{ taskList: types.TaskList{ Name: "taskList4", Kind: types.TaskListKindNormal.Ptr(), }, forwardedFrom: "", nPartitions: 0, }, want: "taskList4", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { lb := &defaultLoadBalancer{} got := lb.pickPartition(tt.args.taskList, tt.args.forwardedFrom, tt.args.nPartitions) assert.Equal(t, tt.want, got) }) } } ================================================ FILE: client/matching/multi_loadbalancer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "strings" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) type ( multiLoadBalancer struct { defaultLoadBalancer LoadBalancer loadBalancers map[string]LoadBalancer domainIDToName func(string) (string, error) loadbalancerStrategy dynamicproperties.StringPropertyFnWithTaskListInfoFilters logger log.Logger } ) func NewMultiLoadBalancer( defaultLoadBalancer LoadBalancer, loadBalancers map[string]LoadBalancer, domainIDToName func(string) (string, error), dc *dynamicconfig.Collection, logger log.Logger, ) LoadBalancer { return &multiLoadBalancer{ defaultLoadBalancer: defaultLoadBalancer, loadBalancers: loadBalancers, domainIDToName: domainIDToName, loadbalancerStrategy: dc.GetStringPropertyFilteredByTaskListInfo(dynamicproperties.TasklistLoadBalancerStrategy), logger: logger, } } func (lb *multiLoadBalancer) PickWritePartition( taskListType int, req WriteRequest, ) string { if !lb.canRedirectToPartition(req) { return req.GetTaskList().GetName() } domainName, err := lb.domainIDToName(req.GetDomainUUID()) if err != nil { return lb.defaultLoadBalancer.PickWritePartition(taskListType, req) } strategy := lb.loadbalancerStrategy(domainName, req.GetTaskList().GetName(), taskListType) loadBalancer, ok := lb.loadBalancers[strategy] if !ok { lb.logger.Warn("unsupported load balancer strategy", tag.Value(strategy)) return lb.defaultLoadBalancer.PickWritePartition(taskListType, req) } return loadBalancer.PickWritePartition(taskListType, req) } func (lb *multiLoadBalancer) PickReadPartition( taskListType int, req ReadRequest, isolationGroup string, ) string { if !lb.canRedirectToPartition(req) { return req.GetTaskList().GetName() } domainName, err := lb.domainIDToName(req.GetDomainUUID()) if err != nil { return lb.defaultLoadBalancer.PickReadPartition(taskListType, req, isolationGroup) } strategy := lb.loadbalancerStrategy(domainName, req.GetTaskList().GetName(), taskListType) loadBalancer, ok := lb.loadBalancers[strategy] if !ok { lb.logger.Warn("unsupported load balancer strategy", tag.Value(strategy)) return lb.defaultLoadBalancer.PickReadPartition(taskListType, req, isolationGroup) } return loadBalancer.PickReadPartition(taskListType, req, isolationGroup) } func (lb *multiLoadBalancer) UpdateWeight( taskListType int, req ReadRequest, partition string, info *types.LoadBalancerHints, ) { if !lb.canRedirectToPartition(req) { return } domainName, err := lb.domainIDToName(req.GetDomainUUID()) if err != nil { return } strategy := lb.loadbalancerStrategy(domainName, req.GetTaskList().GetName(), taskListType) loadBalancer, ok := lb.loadBalancers[strategy] if !ok { lb.logger.Warn("unsupported load balancer strategy", tag.Value(strategy)) return } loadBalancer.UpdateWeight(taskListType, req, partition, info) } func (lb *multiLoadBalancer) canRedirectToPartition(req ReadRequest) bool { return req.GetForwardedFrom() == "" && req.GetTaskList().GetKind() == types.TaskListKindNormal && !strings.HasPrefix(req.GetTaskList().GetName(), constants.ReservedTaskListPrefix) } ================================================ FILE: client/matching/multi_loadbalancer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) func TestNewMultiLoadBalancer(t *testing.T) { ctrl := gomock.NewController(t) randomMock := NewMockLoadBalancer(ctrl) roundRobinMock := NewMockLoadBalancer(ctrl) lbs := map[string]LoadBalancer{ "random": randomMock, "round-robin": roundRobinMock, } domainIDToName := func(domainID string) (string, error) { return "testDomainName", nil } dc := dynamicconfig.NewCollection(dynamicconfig.NewNopClient(), testlogger.New(t)) lb := NewMultiLoadBalancer(randomMock, lbs, domainIDToName, dc, testlogger.New(t)) assert.NotNil(t, lb) multiLB, ok := lb.(*multiLoadBalancer) assert.NotNil(t, multiLB) assert.True(t, ok) assert.NotNil(t, multiLB.defaultLoadBalancer) assert.NotNil(t, multiLB.loadBalancers) assert.NotNil(t, multiLB.domainIDToName) assert.NotNil(t, multiLB.loadbalancerStrategy) assert.NotNil(t, multiLB.logger) } func TestMultiLoadBalancer_PickWritePartition(t *testing.T) { // Mock the domainIDToName function domainIDToName := func(domainID string) (string, error) { if domainID == "valid-domain" { return "valid-domain-name", nil } return "", errors.New("domain not found") } // Test cases tests := []struct { name string domainID string taskList types.TaskList taskListType int forwardedFrom string loadbalancerStrategy string expectedPartition string }{ { name: "random partition when domainIDToName fails", domainID: "invalid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "", loadbalancerStrategy: "random", expectedPartition: "random-partition", }, { name: "round-robin partition enabled", domainID: "valid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "", loadbalancerStrategy: "round-robin", expectedPartition: "roundrobin-partition", }, { name: "random partition when round-robin disabled", domainID: "valid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "", loadbalancerStrategy: "invalid-enum", expectedPartition: "random-partition", }, { name: "cannot repartition - forwarded", domainID: "valid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "somewhere", loadbalancerStrategy: "random", expectedPartition: "test-tasklist", }, { name: "cannot repartition - sticky", domainID: "valid-domain", taskList: types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindSticky.Ptr()}, taskListType: 1, forwardedFrom: "", loadbalancerStrategy: "random", expectedPartition: "test-tasklist", }, { name: "cannot repartition - partition", domainID: "valid-domain", taskList: types.TaskList{Name: "/__cadence_sys/test-tasklist"}, taskListType: 1, forwardedFrom: "", loadbalancerStrategy: "random", expectedPartition: "/__cadence_sys/test-tasklist", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := &types.AddDecisionTaskRequest{ DomainUUID: tt.domainID, TaskList: &tt.taskList, ForwardedFrom: tt.forwardedFrom, } // Mock behavior for random and round robin load balancers ctrl := gomock.NewController(t) // Mock the LoadBalancer interface randomMock := NewMockLoadBalancer(ctrl) roundRobinMock := NewMockLoadBalancer(ctrl) randomMock.EXPECT().PickWritePartition(tt.taskListType, req).Return("random-partition").AnyTimes() roundRobinMock.EXPECT().PickWritePartition(tt.taskListType, req).Return("roundrobin-partition").AnyTimes() loadbalancerStrategyFn := func(domainName, taskListName string, taskListType int) string { return tt.loadbalancerStrategy } // Create multiLoadBalancer lb := &multiLoadBalancer{ defaultLoadBalancer: randomMock, loadBalancers: map[string]LoadBalancer{ "round-robin": roundRobinMock, }, domainIDToName: domainIDToName, loadbalancerStrategy: loadbalancerStrategyFn, logger: testlogger.New(t), } // Call PickWritePartition and assert result partition := lb.PickWritePartition(tt.taskListType, req) assert.Equal(t, tt.expectedPartition, partition) }) } } func TestMultiLoadBalancer_PickReadPartition(t *testing.T) { // Mock the domainIDToName function domainIDToName := func(domainID string) (string, error) { if domainID == "valid-domain" { return "valid-domain-name", nil } return "", errors.New("domain not found") } // Test cases tests := []struct { name string domainID string taskList types.TaskList taskListType int forwardedFrom string loadbalancerStrategy string expectedPartition string }{ { name: "random partition when domainIDToName fails", domainID: "invalid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "", loadbalancerStrategy: "random", expectedPartition: "random-partition", }, { name: "round-robin partition enabled", domainID: "valid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "", loadbalancerStrategy: "round-robin", expectedPartition: "roundrobin-partition", }, { name: "random partition when round-robin disabled", domainID: "valid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "", loadbalancerStrategy: "invalid-enum", expectedPartition: "random-partition", }, { name: "cannot repartition - forwarded", domainID: "valid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "somewhere", loadbalancerStrategy: "random", expectedPartition: "test-tasklist", }, { name: "cannot repartition - sticky", domainID: "valid-domain", taskList: types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindSticky.Ptr()}, taskListType: 1, forwardedFrom: "", loadbalancerStrategy: "random", expectedPartition: "test-tasklist", }, { name: "cannot repartition - partition", domainID: "valid-domain", taskList: types.TaskList{Name: "/__cadence_sys/test-tasklist"}, taskListType: 1, forwardedFrom: "", loadbalancerStrategy: "random", expectedPartition: "/__cadence_sys/test-tasklist", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := &types.AddDecisionTaskRequest{ DomainUUID: tt.domainID, TaskList: &tt.taskList, ForwardedFrom: tt.forwardedFrom, } // Mock behavior for random and round robin load balancers ctrl := gomock.NewController(t) // Mock the LoadBalancer interface randomMock := NewMockLoadBalancer(ctrl) roundRobinMock := NewMockLoadBalancer(ctrl) randomMock.EXPECT().PickReadPartition(tt.taskListType, req, "").Return("random-partition").AnyTimes() roundRobinMock.EXPECT().PickReadPartition(tt.taskListType, req, "").Return("roundrobin-partition").AnyTimes() // Mock dynamic config for loadbalancer strategy loadbalancerStrategyFn := func(domainName, taskListName string, taskListType int) string { return tt.loadbalancerStrategy } // Create multiLoadBalancer lb := &multiLoadBalancer{ defaultLoadBalancer: randomMock, loadBalancers: map[string]LoadBalancer{ "round-robin": roundRobinMock, }, domainIDToName: domainIDToName, loadbalancerStrategy: loadbalancerStrategyFn, logger: testlogger.New(t), } // Call PickReadPartition and assert result partition := lb.PickReadPartition(tt.taskListType, req, "") assert.Equal(t, tt.expectedPartition, partition) }) } } func TestMultiLoadBalancer_UpdateWeight(t *testing.T) { // Mock the domainIDToName function domainIDToName := func(domainID string) (string, error) { if domainID == "valid-domain" { return "valid-domain-name", nil } return "", errors.New("domain not found") } // Test cases tests := []struct { name string domainID string taskList types.TaskList taskListType int forwardedFrom string partition string loadBalancerHints *types.LoadBalancerHints loadbalancerStrategy string shouldUpdate bool }{ { name: "do nothing when domainIDToName fails", domainID: "invalid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "", partition: "partition-1", loadBalancerHints: &types.LoadBalancerHints{ BacklogCount: 10, RatePerSecond: 1, }, loadbalancerStrategy: "random", shouldUpdate: false, }, { name: "update weight with round-robin load balancer", domainID: "valid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "", partition: "partition-2", loadBalancerHints: &types.LoadBalancerHints{ BacklogCount: 20, RatePerSecond: 2, }, loadbalancerStrategy: "round-robin", shouldUpdate: true, }, { name: "do nothing when strategy is unsupported", domainID: "valid-domain", taskList: types.TaskList{Name: "test-tasklist"}, taskListType: 1, forwardedFrom: "", partition: "partition-3", loadBalancerHints: &types.LoadBalancerHints{ BacklogCount: 30, RatePerSecond: 3, }, loadbalancerStrategy: "invalid-strategy", shouldUpdate: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := &types.AddDecisionTaskRequest{ DomainUUID: tt.domainID, TaskList: &tt.taskList, ForwardedFrom: tt.forwardedFrom, } // Mock behavior for random and round-robin load balancers ctrl := gomock.NewController(t) // Mock the LoadBalancer interface randomMock := NewMockLoadBalancer(ctrl) roundRobinMock := NewMockLoadBalancer(ctrl) if tt.shouldUpdate { roundRobinMock.EXPECT().UpdateWeight(tt.taskListType, req, tt.partition, tt.loadBalancerHints).Times(1) } else { roundRobinMock.EXPECT().UpdateWeight(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) } loadbalancerStrategyFn := func(domainName, taskListName string, taskListType int) string { return tt.loadbalancerStrategy } // Create multiLoadBalancer lb := &multiLoadBalancer{ defaultLoadBalancer: randomMock, loadBalancers: map[string]LoadBalancer{ "round-robin": roundRobinMock, }, domainIDToName: domainIDToName, loadbalancerStrategy: loadbalancerStrategyFn, logger: testlogger.New(t), } // Call UpdateWeight lb.UpdateWeight(tt.taskListType, req, tt.partition, tt.loadBalancerHints) }) } } ================================================ FILE: client/matching/partition_config_provider.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination partition_config_provider_mock.go -package matching github.com/uber/cadence/client/matching PartitionConfigProvider package matching import ( "sync" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // PartitionConfigProvider is the interface for implementers of // component that provides partition configuration for task list // partitions PartitionConfigProvider interface { // GetNumberOfReadPartitions returns the number of read partitions GetNumberOfReadPartitions(domainID string, taskList types.TaskList, taskListType int) int // GetNumberOfWritePartitions returns the number of write partitions GetNumberOfWritePartitions(domainID string, taskList types.TaskList, taskListType int) int // GetPartitionConfig returns the cached partition configuration GetPartitionConfig(domainID string, taskList types.TaskList, taskListType int) *types.TaskListPartitionConfig // UpdatePartitionConfig updates the partition configuration for a task list UpdatePartitionConfig(domainID string, taskList types.TaskList, taskListType int, config *types.TaskListPartitionConfig) // InvalidatePartitionCache invalidates the cached partition configuration for a task list InvalidatePartitionCache(domainID string, taskList types.TaskList, taskListType int) // GetMetricsClient returns the metrics client GetMetricsClient() metrics.Client // GetLogger returns the logger GetLogger() log.Logger } syncedTaskListPartitionConfig struct { sync.RWMutex types.TaskListPartitionConfig } partitionConfigProviderImpl struct { configCache cache.Cache logger log.Logger metricsClient metrics.Client domainIDToName func(string) (string, error) enableReadFromCache dynamicproperties.BoolPropertyFnWithTaskListInfoFilters nReadPartitions dynamicproperties.IntPropertyFnWithTaskListInfoFilters nWritePartitions dynamicproperties.IntPropertyFnWithTaskListInfoFilters } ) var singlePartitionConfig = createDefaultConfig(1, 1) func (c *syncedTaskListPartitionConfig) updateConfig(newConfig types.TaskListPartitionConfig) bool { c.Lock() defer c.Unlock() if c.Version < newConfig.Version { c.TaskListPartitionConfig = newConfig return true } return false } func NewPartitionConfigProvider( logger log.Logger, metricsClient metrics.Client, domainIDToName func(string) (string, error), dc *dynamicconfig.Collection, ) PartitionConfigProvider { return &partitionConfigProviderImpl{ logger: logger, metricsClient: metricsClient, domainIDToName: domainIDToName, enableReadFromCache: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicproperties.MatchingEnableGetNumberOfPartitionsFromCache), nReadPartitions: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingNumTasklistReadPartitions), nWritePartitions: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingNumTasklistWritePartitions), configCache: cache.New(&cache.Options{ TTL: 0, InitialCapacity: 100, Pin: false, MaxCount: 3000, ActivelyEvict: false, MetricsScope: metricsClient.Scope(metrics.PartitionConfigProviderScope), Logger: logger, }), } } func (p *partitionConfigProviderImpl) GetNumberOfReadPartitions(domainID string, taskList types.TaskList, taskListType int) int { config := p.GetPartitionConfig(domainID, taskList, taskListType) return len(config.ReadPartitions) } func (p *partitionConfigProviderImpl) GetNumberOfWritePartitions(domainID string, taskList types.TaskList, taskListType int) int { config := p.GetPartitionConfig(domainID, taskList, taskListType) v := config.Version w := len(config.WritePartitions) r := len(config.ReadPartitions) if w > r { p.logger.Warn("Number of write partitions exceeds number of read partitions, using number of read partitions", tag.WorkflowDomainID(domainID), tag.WorkflowTaskListName(taskList.GetName()), tag.WorkflowTaskListType(taskListType), tag.Dynamic("read-partition", r), tag.Dynamic("write-partition", w), tag.Dynamic("config-version", v)) return r } return w } func (p *partitionConfigProviderImpl) GetPartitionConfig(domainID string, taskList types.TaskList, taskListType int) *types.TaskListPartitionConfig { domainName, err := p.domainIDToName(domainID) if err != nil { return createDefaultConfig(1, 1) } if !p.enableReadFromCache(domainName, taskList.GetName(), taskListType) { nWrite := p.nWritePartitions(domainName, taskList.GetName(), taskListType) nRead := p.nReadPartitions(domainName, taskList.GetName(), taskListType) // checks to make sure number of writes never exceeds number of reads if nWrite > nRead { p.logger.Warn("Number of write partitions exceeds number of read partitions, using number of read partitions", tag.WorkflowDomainID(domainID), tag.WorkflowTaskListName(taskList.GetName()), tag.WorkflowTaskListType(taskListType), tag.Dynamic("read-partition", nRead), tag.Dynamic("write-partition", nWrite)) nWrite = nRead } return createDefaultConfig(nRead, nWrite) } c := p.getCachedPartitionConfig(domainID, taskList, taskListType) if c == nil { return singlePartitionConfig } c.RLock() config := c.TaskListPartitionConfig c.RUnlock() v := config.Version w := len(config.WritePartitions) r := len(config.ReadPartitions) scope := p.metricsClient.Scope(metrics.PartitionConfigProviderScope, metrics.DomainTag(domainName), metrics.TaskListRootPartitionTag(taskList.GetName()), getTaskListTypeTag(taskListType)) scope.UpdateGauge(metrics.TaskListPartitionConfigNumReadGauge, float64(r)) scope.UpdateGauge(metrics.TaskListPartitionConfigNumWriteGauge, float64(w)) scope.UpdateGauge(metrics.TaskListPartitionConfigVersionGauge, float64(v)) return &config } func (p *partitionConfigProviderImpl) UpdatePartitionConfig(domainID string, taskList types.TaskList, taskListType int, config *types.TaskListPartitionConfig) { if config == nil || taskList.GetKind() != types.TaskListKindNormal { return } taskListKey := key{ domainID: domainID, taskListName: taskList.Name, taskListType: taskListType, } var err error cI := p.configCache.Get(taskListKey) if cI == nil { cI, err = p.configCache.PutIfNotExist(taskListKey, &syncedTaskListPartitionConfig{TaskListPartitionConfig: *config}) if err != nil { p.logger.Error("Failed put partition config into cache", tag.Error(err)) return } } c, ok := cI.(*syncedTaskListPartitionConfig) if !ok { return } updated := c.updateConfig(*config) if updated { w := len(c.WritePartitions) r := len(c.ReadPartitions) p.logger.Info("tasklist partition config updated", tag.WorkflowDomainID(domainID), tag.WorkflowTaskListName(taskList.Name), tag.WorkflowTaskListType(taskListType), tag.Dynamic("read-partition", r), tag.Dynamic("write-partition", w), tag.Dynamic("config-version", config.Version)) } } func (p *partitionConfigProviderImpl) GetMetricsClient() metrics.Client { return p.metricsClient } func (p *partitionConfigProviderImpl) GetLogger() log.Logger { return p.logger } func (p *partitionConfigProviderImpl) InvalidatePartitionCache(domainID string, taskList types.TaskList, taskListType int) { if taskList.GetKind() != types.TaskListKindNormal { return } taskListKey := key{ domainID: domainID, taskListName: taskList.Name, taskListType: taskListType, } p.configCache.Delete(taskListKey) p.logger.Info("tasklist partition config cache invalidated", tag.WorkflowDomainID(domainID), tag.WorkflowTaskListName(taskList.Name), tag.WorkflowTaskListType(taskListType)) } func (p *partitionConfigProviderImpl) getCachedPartitionConfig(domainID string, taskList types.TaskList, taskListType int) *syncedTaskListPartitionConfig { if taskList.GetKind() != types.TaskListKindNormal { return nil } taskListKey := key{ domainID: domainID, taskListName: taskList.Name, taskListType: taskListType, } cI := p.configCache.Get(taskListKey) if cI == nil { p.logger.Debug("Partition config not found in cache", tag.WorkflowDomainID(domainID), tag.WorkflowTaskListName(taskList.Name), tag.WorkflowTaskListType(taskListType)) return nil } c, ok := cI.(*syncedTaskListPartitionConfig) if !ok { return nil } return c } func getTaskListTypeTag(taskListType int) metrics.Tag { switch taskListType { case persistence.TaskListTypeActivity: return metrics.TaskListTypeTag("activity") case persistence.TaskListTypeDecision: return metrics.TaskListTypeTag("decision") default: return metrics.TaskListTypeTag("") } } func createDefaultConfig(nRead, nWrite int) *types.TaskListPartitionConfig { read := make(map[int]*types.TaskListPartition, nRead) for i := 0; i < nRead; i++ { read[i] = &types.TaskListPartition{} } write := make(map[int]*types.TaskListPartition, nWrite) for i := 0; i < nWrite; i++ { write[i] = &types.TaskListPartition{} } return &types.TaskListPartitionConfig{ Version: 0, ReadPartitions: read, WritePartitions: write, } } ================================================ FILE: client/matching/partition_config_provider_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: partition_config_provider.go // // Generated by this command: // // mockgen -package matching -source partition_config_provider.go -destination partition_config_provider_mock.go -package matching github.com/uber/cadence/client/matching PartitionConfigProvider // // Package matching is a generated GoMock package. package matching import ( reflect "reflect" gomock "go.uber.org/mock/gomock" log "github.com/uber/cadence/common/log" metrics "github.com/uber/cadence/common/metrics" types "github.com/uber/cadence/common/types" ) // MockPartitionConfigProvider is a mock of PartitionConfigProvider interface. type MockPartitionConfigProvider struct { ctrl *gomock.Controller recorder *MockPartitionConfigProviderMockRecorder isgomock struct{} } // MockPartitionConfigProviderMockRecorder is the mock recorder for MockPartitionConfigProvider. type MockPartitionConfigProviderMockRecorder struct { mock *MockPartitionConfigProvider } // NewMockPartitionConfigProvider creates a new mock instance. func NewMockPartitionConfigProvider(ctrl *gomock.Controller) *MockPartitionConfigProvider { mock := &MockPartitionConfigProvider{ctrl: ctrl} mock.recorder = &MockPartitionConfigProviderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPartitionConfigProvider) EXPECT() *MockPartitionConfigProviderMockRecorder { return m.recorder } // GetLogger mocks base method. func (m *MockPartitionConfigProvider) GetLogger() log.Logger { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLogger") ret0, _ := ret[0].(log.Logger) return ret0 } // GetLogger indicates an expected call of GetLogger. func (mr *MockPartitionConfigProviderMockRecorder) GetLogger() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogger", reflect.TypeOf((*MockPartitionConfigProvider)(nil).GetLogger)) } // GetMetricsClient mocks base method. func (m *MockPartitionConfigProvider) GetMetricsClient() metrics.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMetricsClient") ret0, _ := ret[0].(metrics.Client) return ret0 } // GetMetricsClient indicates an expected call of GetMetricsClient. func (mr *MockPartitionConfigProviderMockRecorder) GetMetricsClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetricsClient", reflect.TypeOf((*MockPartitionConfigProvider)(nil).GetMetricsClient)) } // GetNumberOfReadPartitions mocks base method. func (m *MockPartitionConfigProvider) GetNumberOfReadPartitions(domainID string, taskList types.TaskList, taskListType int) int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNumberOfReadPartitions", domainID, taskList, taskListType) ret0, _ := ret[0].(int) return ret0 } // GetNumberOfReadPartitions indicates an expected call of GetNumberOfReadPartitions. func (mr *MockPartitionConfigProviderMockRecorder) GetNumberOfReadPartitions(domainID, taskList, taskListType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNumberOfReadPartitions", reflect.TypeOf((*MockPartitionConfigProvider)(nil).GetNumberOfReadPartitions), domainID, taskList, taskListType) } // GetNumberOfWritePartitions mocks base method. func (m *MockPartitionConfigProvider) GetNumberOfWritePartitions(domainID string, taskList types.TaskList, taskListType int) int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNumberOfWritePartitions", domainID, taskList, taskListType) ret0, _ := ret[0].(int) return ret0 } // GetNumberOfWritePartitions indicates an expected call of GetNumberOfWritePartitions. func (mr *MockPartitionConfigProviderMockRecorder) GetNumberOfWritePartitions(domainID, taskList, taskListType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNumberOfWritePartitions", reflect.TypeOf((*MockPartitionConfigProvider)(nil).GetNumberOfWritePartitions), domainID, taskList, taskListType) } // GetPartitionConfig mocks base method. func (m *MockPartitionConfigProvider) GetPartitionConfig(domainID string, taskList types.TaskList, taskListType int) *types.TaskListPartitionConfig { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPartitionConfig", domainID, taskList, taskListType) ret0, _ := ret[0].(*types.TaskListPartitionConfig) return ret0 } // GetPartitionConfig indicates an expected call of GetPartitionConfig. func (mr *MockPartitionConfigProviderMockRecorder) GetPartitionConfig(domainID, taskList, taskListType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartitionConfig", reflect.TypeOf((*MockPartitionConfigProvider)(nil).GetPartitionConfig), domainID, taskList, taskListType) } // InvalidatePartitionCache mocks base method. func (m *MockPartitionConfigProvider) InvalidatePartitionCache(domainID string, taskList types.TaskList, taskListType int) { m.ctrl.T.Helper() m.ctrl.Call(m, "InvalidatePartitionCache", domainID, taskList, taskListType) } // InvalidatePartitionCache indicates an expected call of InvalidatePartitionCache. func (mr *MockPartitionConfigProviderMockRecorder) InvalidatePartitionCache(domainID, taskList, taskListType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InvalidatePartitionCache", reflect.TypeOf((*MockPartitionConfigProvider)(nil).InvalidatePartitionCache), domainID, taskList, taskListType) } // UpdatePartitionConfig mocks base method. func (m *MockPartitionConfigProvider) UpdatePartitionConfig(domainID string, taskList types.TaskList, taskListType int, config *types.TaskListPartitionConfig) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdatePartitionConfig", domainID, taskList, taskListType, config) } // UpdatePartitionConfig indicates an expected call of UpdatePartitionConfig. func (mr *MockPartitionConfigProviderMockRecorder) UpdatePartitionConfig(domainID, taskList, taskListType, config any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePartitionConfig", reflect.TypeOf((*MockPartitionConfigProvider)(nil).UpdatePartitionConfig), domainID, taskList, taskListType, config) } ================================================ FILE: client/matching/partition_config_provider_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "golang.org/x/sync/errgroup" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) func TestSyncedTaskListPartitionConfig(t *testing.T) { c := &syncedTaskListPartitionConfig{TaskListPartitionConfig: types.TaskListPartitionConfig{Version: 50}} var g errgroup.Group for i := int64(0); i < 100; i++ { v := i g.Go(func() error { c.updateConfig(types.TaskListPartitionConfig{Version: v}) return nil }) } require.NoError(t, g.Wait()) assert.Equal(t, int64(99), c.Version) } func setUpMocksForPartitionConfigProvider(t *testing.T, enableReadFromCache bool) (*partitionConfigProviderImpl, *cache.MockCache) { ctrl := gomock.NewController(t) mockCache := cache.NewMockCache(ctrl) logger := log.NewNoop() domainIDToName := func(domainID string) (string, error) { return "test-domain", nil } return &partitionConfigProviderImpl{ configCache: mockCache, logger: logger, metricsClient: metrics.NewNoopMetricsClient(), domainIDToName: domainIDToName, enableReadFromCache: dynamicproperties.GetBoolPropertyFilteredByTaskListInfo(enableReadFromCache), nReadPartitions: dynamicproperties.GetIntPropertyFilteredByTaskListInfo(3), nWritePartitions: dynamicproperties.GetIntPropertyFilteredByTaskListInfo(5), }, mockCache } func TestNewPartitionConfigProvider(t *testing.T) { dc := dynamicconfig.NewCollection(dynamicconfig.NewNopClient(), testlogger.New(t)) logger := testlogger.New(t) domainIDToName := func(domainID string) (string, error) { return "test-domain", nil } p := NewPartitionConfigProvider(logger, metrics.NewNoopMetricsClient(), domainIDToName, dc) assert.NotNil(t, p) pImpl, ok := p.(*partitionConfigProviderImpl) assert.True(t, ok) assert.NotNil(t, pImpl) assert.Equal(t, logger, pImpl.logger) assert.NotNil(t, pImpl.configCache) assert.NotNil(t, pImpl.domainIDToName) assert.NotNil(t, pImpl.enableReadFromCache) assert.NotNil(t, pImpl.nReadPartitions) assert.NotNil(t, pImpl.nWritePartitions) } func TestGetNumberOfReadPartitions(t *testing.T) { testCases := []struct { name string taskListKind types.TaskListKind enableReadFromCache bool cachedConfigExists bool expectedPartitions int }{ { name: "get read partitions from dynamic config", taskListKind: types.TaskListKindNormal, enableReadFromCache: false, expectedPartitions: 3, }, { name: "get read partitions from cache", taskListKind: types.TaskListKindNormal, enableReadFromCache: true, cachedConfigExists: true, expectedPartitions: 4, }, { name: "get read partitions for sticky tasklist", taskListKind: types.TaskListKindSticky, enableReadFromCache: true, expectedPartitions: 1, }, { name: "cache config missing, fallback to default", taskListKind: types.TaskListKindNormal, enableReadFromCache: true, cachedConfigExists: false, expectedPartitions: 1, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { partitionProvider, mockCache := setUpMocksForPartitionConfigProvider(t, tc.enableReadFromCache) if tc.enableReadFromCache && tc.taskListKind == types.TaskListKindNormal { if tc.cachedConfigExists { mockCache.EXPECT().Get(gomock.Any()).Return(&syncedTaskListPartitionConfig{ TaskListPartitionConfig: types.TaskListPartitionConfig{ReadPartitions: partitions(4)}, }).Times(1) } else { mockCache.EXPECT().Get(gomock.Any()).Return(nil).Times(1) } } kind := tc.taskListKind taskList := types.TaskList{Name: "test-task-list", Kind: &kind} p := partitionProvider.GetNumberOfReadPartitions("test-domain-id", taskList, 0) // Validate result assert.Equal(t, tc.expectedPartitions, p) }) } } func TestGetNumberOfWritePartitions(t *testing.T) { testCases := []struct { name string taskListKind types.TaskListKind enableReadFromCache bool cachedConfigExists bool expectedPartitions int }{ { name: "get write partitions from dynamic config", taskListKind: types.TaskListKindNormal, enableReadFromCache: false, expectedPartitions: 3, // nWritePartitions is 5 but capped by nReadPartitions which is 3 }, { name: "get write partitions from cache", taskListKind: types.TaskListKindNormal, enableReadFromCache: true, cachedConfigExists: true, expectedPartitions: 2, }, { name: "get write partitions from cache for sticky tasklist", taskListKind: types.TaskListKindSticky, enableReadFromCache: true, expectedPartitions: 1, }, { name: "cache config missing, fallback to default", taskListKind: types.TaskListKindNormal, enableReadFromCache: true, cachedConfigExists: false, expectedPartitions: 1, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { partitionProvider, mockCache := setUpMocksForPartitionConfigProvider(t, tc.enableReadFromCache) if tc.enableReadFromCache && tc.taskListKind == types.TaskListKindNormal { if tc.cachedConfigExists { mockCache.EXPECT().Get(gomock.Any()).Return(&syncedTaskListPartitionConfig{ TaskListPartitionConfig: types.TaskListPartitionConfig{ReadPartitions: partitions(2), WritePartitions: partitions(5)}, }).Times(1) } else { mockCache.EXPECT().Get(gomock.Any()).Return(nil).Times(1) } } kind := tc.taskListKind taskList := types.TaskList{Name: "test-task-list", Kind: &kind} p := partitionProvider.GetNumberOfWritePartitions("test-domain-id", taskList, 0) // Validate result assert.Equal(t, tc.expectedPartitions, p) }) } } func TestGetPartitionConfig(t *testing.T) { testCases := []struct { name string taskListKind types.TaskListKind enableReadFromCache bool cachedConfigExists bool expected *types.TaskListPartitionConfig }{ { name: "get from dynamic config", taskListKind: types.TaskListKindNormal, enableReadFromCache: false, expected: createDefaultConfig(3, 3), }, { name: "get from cache", taskListKind: types.TaskListKindNormal, enableReadFromCache: true, cachedConfigExists: true, // It's wrong but it's the value we've got expected: createDefaultConfig(2, 5), }, { name: "get from cache for sticky tasklist", taskListKind: types.TaskListKindSticky, enableReadFromCache: true, expected: createDefaultConfig(1, 1), }, { name: "cache config missing, fallback to default", taskListKind: types.TaskListKindNormal, enableReadFromCache: true, cachedConfigExists: false, expected: createDefaultConfig(1, 1), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { partitionProvider, mockCache := setUpMocksForPartitionConfigProvider(t, tc.enableReadFromCache) if tc.enableReadFromCache && tc.taskListKind == types.TaskListKindNormal { if tc.cachedConfigExists { mockCache.EXPECT().Get(gomock.Any()).Return(&syncedTaskListPartitionConfig{ TaskListPartitionConfig: types.TaskListPartitionConfig{ReadPartitions: partitions(2), WritePartitions: partitions(5)}, }).Times(1) } else { mockCache.EXPECT().Get(gomock.Any()).Return(nil).Times(1) } } kind := tc.taskListKind taskList := types.TaskList{Name: "test-task-list", Kind: &kind} config := partitionProvider.GetPartitionConfig("test-domain-id", taskList, 0) // Validate result assert.Equal(t, tc.expected, config) }) } } func TestUpdatePartitionConfig(t *testing.T) { testCases := []struct { name string input *types.TaskListPartitionConfig configExists bool expectedNewConfig bool }{ { name: "config exists, update config", input: &types.TaskListPartitionConfig{Version: 2}, configExists: true, expectedNewConfig: false, }, { name: "config does not exist, create new config", input: &types.TaskListPartitionConfig{Version: 2}, configExists: false, expectedNewConfig: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { partitionProvider, mockCache := setUpMocksForPartitionConfigProvider(t, true) taskList := types.TaskList{Name: "test-task-list"} if tc.configExists { existingConfig := &syncedTaskListPartitionConfig{ TaskListPartitionConfig: types.TaskListPartitionConfig{Version: 1}, } mockCache.EXPECT().Get(gomock.Any()).Return(existingConfig).Times(1) } else { mockCache.EXPECT().Get(gomock.Any()).Return(nil).Times(1) mockCache.EXPECT().PutIfNotExist(gomock.Any(), gomock.Any()).Return(&syncedTaskListPartitionConfig{}, nil).Times(1) } partitionProvider.UpdatePartitionConfig("test-domain-id", taskList, 0, tc.input) }) } } func TestInvalidatePartitionCache(t *testing.T) { testCases := []struct { name string taskListKind types.TaskListKind expectDeleteCall bool }{ { name: "invalidate cache for normal task list", taskListKind: types.TaskListKindNormal, expectDeleteCall: true, }, { name: "skip invalidation for sticky task list", taskListKind: types.TaskListKindSticky, expectDeleteCall: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { partitionProvider, mockCache := setUpMocksForPartitionConfigProvider(t, true) kind := tc.taskListKind taskList := types.TaskList{Name: "test-task-list", Kind: &kind} if tc.expectDeleteCall { expectedKey := key{ domainID: "test-domain-id", taskListName: "test-task-list", taskListType: 0, } mockCache.EXPECT().Delete(expectedKey).Times(1) } partitionProvider.InvalidatePartitionCache("test-domain-id", taskList, 0) }) } } func partitions(num int) map[int]*types.TaskListPartition { result := make(map[int]*types.TaskListPartition, num) for i := 0; i < num; i++ { result[i] = &types.TaskListPartition{} } return result } ================================================ FILE: client/matching/peer_resolver.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package matching import ( "github.com/uber/cadence/common" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" ) // PeerResolver is used to resolve matching peers. // Those are deployed instances of Cadence matching services that participate in the cluster ring. // The resulting peer is simply an address of form ip:port where RPC calls can be routed to. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination peer_resolver_mock.go -package matching github.com/uber/cadence/client/matching PeerResolver type PeerResolver interface { FromTaskList(taskListName string) (string, error) GetAllPeers() ([]string, error) FromHostAddress(hostAddress string) (string, error) } type peerResolver struct { resolver membership.Resolver namedPort string // grpc or tchannel, depends on yarpc configuration } // NewPeerResolver creates a new matching peer resolver. func NewPeerResolver(membership membership.Resolver, namedPort string) PeerResolver { return peerResolver{ resolver: membership, namedPort: namedPort, } } // FromTaskList resolves the matching peer responsible for the given task list name. // It uses our membership provider to lookup which instance currently owns the given task list. // FromHostAddress is used for further resolving. func (pr peerResolver) FromTaskList(taskListName string) (string, error) { host, err := pr.resolver.Lookup(service.Matching, taskListName) if err != nil { return "", common.ToServiceTransientError(err) } peer, err := host.GetNamedAddress(pr.namedPort) return peer, common.ToServiceTransientError(err) } // GetAllPeers returns all matching service peers in the cluster ring. func (pr peerResolver) GetAllPeers() ([]string, error) { hosts, err := pr.resolver.Members(service.Matching) if err != nil { return nil, common.ToServiceTransientError(err) } peers := make([]string, 0, len(hosts)) for _, host := range hosts { peer, err := pr.FromHostAddress(host.GetAddress()) if err != nil { return nil, common.ToServiceTransientError(err) } peers = append(peers, peer) } return peers, nil } // FromHostAddress resolves the final matching peer responsible for the given host address. // The address may be used as is, or processed with additional address mapper. // In case of gRPC transport, the port within the address is replaced with gRPC port. func (pr peerResolver) FromHostAddress(hostAddress string) (string, error) { host, err := pr.resolver.LookupByAddress(service.Matching, hostAddress) if err != nil { return "", common.ToServiceTransientError(err) } peer, err := host.GetNamedAddress(pr.namedPort) return peer, common.ToServiceTransientError(err) } ================================================ FILE: client/matching/peer_resolver_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: peer_resolver.go // // Generated by this command: // // mockgen -package matching -source peer_resolver.go -destination peer_resolver_mock.go -package matching github.com/uber/cadence/client/matching PeerResolver // // Package matching is a generated GoMock package. package matching import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockPeerResolver is a mock of PeerResolver interface. type MockPeerResolver struct { ctrl *gomock.Controller recorder *MockPeerResolverMockRecorder isgomock struct{} } // MockPeerResolverMockRecorder is the mock recorder for MockPeerResolver. type MockPeerResolverMockRecorder struct { mock *MockPeerResolver } // NewMockPeerResolver creates a new mock instance. func NewMockPeerResolver(ctrl *gomock.Controller) *MockPeerResolver { mock := &MockPeerResolver{ctrl: ctrl} mock.recorder = &MockPeerResolverMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPeerResolver) EXPECT() *MockPeerResolverMockRecorder { return m.recorder } // FromHostAddress mocks base method. func (m *MockPeerResolver) FromHostAddress(hostAddress string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FromHostAddress", hostAddress) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // FromHostAddress indicates an expected call of FromHostAddress. func (mr *MockPeerResolverMockRecorder) FromHostAddress(hostAddress any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FromHostAddress", reflect.TypeOf((*MockPeerResolver)(nil).FromHostAddress), hostAddress) } // FromTaskList mocks base method. func (m *MockPeerResolver) FromTaskList(taskListName string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FromTaskList", taskListName) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // FromTaskList indicates an expected call of FromTaskList. func (mr *MockPeerResolverMockRecorder) FromTaskList(taskListName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FromTaskList", reflect.TypeOf((*MockPeerResolver)(nil).FromTaskList), taskListName) } // GetAllPeers mocks base method. func (m *MockPeerResolver) GetAllPeers() ([]string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllPeers") ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAllPeers indicates an expected call of GetAllPeers. func (mr *MockPeerResolverMockRecorder) GetAllPeers() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllPeers", reflect.TypeOf((*MockPeerResolver)(nil).GetAllPeers)) } ================================================ FILE: client/matching/peer_resolver_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package matching import ( "testing" "github.com/stretchr/testify/assert" gomock "go.uber.org/mock/gomock" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" ) func TestPeerResolver(t *testing.T) { controller := gomock.NewController(t) serviceResolver := membership.NewMockResolver(controller) host1 := membership.NewDetailedHostInfo( "tasklistHost:1234", "tasklistHost_1234", membership.PortMap{ membership.PortTchannel: 1234, membership.PortGRPC: 1244, }, ) host2 := membership.NewDetailedHostInfo( "tasklistHost2:1235", "tasklistHost2_1235", membership.PortMap{ membership.PortTchannel: 1235, membership.PortGRPC: 1245, }, ) serviceResolver.EXPECT().Lookup(service.Matching, "taskListA").Return( host1, nil) serviceResolver.EXPECT().Lookup(service.Matching, "invalid").Return(membership.HostInfo{}, assert.AnError) serviceResolver.EXPECT().LookupByAddress(service.Matching, "invalid address").Return(membership.HostInfo{}, assert.AnError) serviceResolver.EXPECT().Members(service.Matching).Return([]membership.HostInfo{ host1, host2, }, nil) serviceResolver.EXPECT().LookupByAddress(service.Matching, "tasklistHost2:1235").Return( host2, nil, ).AnyTimes() serviceResolver.EXPECT().LookupByAddress(service.Matching, "tasklistHost:1234").Return( host1, nil, ).AnyTimes() r := NewPeerResolver(serviceResolver, membership.PortGRPC) peer, err := r.FromTaskList("taskListA") assert.NoError(t, err) assert.Equal(t, "tasklistHost:1244", peer) _, err = r.FromTaskList("invalid") assert.Error(t, err) _, err = r.FromHostAddress("invalid address") assert.Error(t, err) peers, err := r.GetAllPeers() assert.NoError(t, err) assert.Equal(t, []string{"tasklistHost:1244", "tasklistHost2:1245"}, peers) } ================================================ FILE: client/matching/rr_loadbalancer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "sync/atomic" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type ( key struct { domainID string taskListName string taskListType int } roundRobinLoadBalancer struct { provider PartitionConfigProvider readCache cache.Cache writeCache cache.Cache pickPartitionFn func(domainName string, taskList types.TaskList, taskListType int, nPartitions int, partitionCache cache.Cache) string } ) func NewRoundRobinLoadBalancer( provider PartitionConfigProvider, ) LoadBalancer { return &roundRobinLoadBalancer{ provider: provider, readCache: cache.New(&cache.Options{ TTL: 0, InitialCapacity: 100, Pin: false, MaxCount: 3000, ActivelyEvict: false, MetricsScope: provider.GetMetricsClient().Scope(metrics.LoadBalancerScope), Logger: provider.GetLogger(), }), writeCache: cache.New(&cache.Options{ TTL: 0, InitialCapacity: 100, Pin: false, MaxCount: 3000, ActivelyEvict: false, MetricsScope: provider.GetMetricsClient().Scope(metrics.LoadBalancerScope), Logger: provider.GetLogger(), }), pickPartitionFn: pickPartition, } } func (lb *roundRobinLoadBalancer) PickWritePartition( taskListType int, req WriteRequest, ) string { nPartitions := lb.provider.GetNumberOfWritePartitions(req.GetDomainUUID(), *req.GetTaskList(), taskListType) return lb.pickPartitionFn(req.GetDomainUUID(), *req.GetTaskList(), taskListType, nPartitions, lb.writeCache) } func (lb *roundRobinLoadBalancer) PickReadPartition( taskListType int, req ReadRequest, _ string, ) string { n := lb.provider.GetNumberOfReadPartitions(req.GetDomainUUID(), *req.GetTaskList(), taskListType) return lb.pickPartitionFn(req.GetDomainUUID(), *req.GetTaskList(), taskListType, n, lb.readCache) } func pickPartition( domainID string, taskList types.TaskList, taskListType int, nPartitions int, partitionCache cache.Cache, ) string { taskListName := taskList.GetName() if nPartitions <= 1 { return taskListName } taskListKey := key{ domainID: domainID, taskListName: taskListName, taskListType: taskListType, } valI := partitionCache.Get(taskListKey) if valI == nil { val := int64(-1) var err error valI, err = partitionCache.PutIfNotExist(taskListKey, &val) if err != nil { return taskListName } } valAddr, ok := valI.(*int64) if !ok { return taskListName } p := int(atomic.AddInt64(valAddr, 1) % int64(nPartitions)) return getPartitionTaskListName(taskList.GetName(), p) } func (lb *roundRobinLoadBalancer) UpdateWeight( taskListType int, req ReadRequest, partition string, info *types.LoadBalancerHints, ) { } ================================================ FILE: client/matching/rr_loadbalancer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "fmt" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) func TestNewRoundRobinLoadBalancer(t *testing.T) { ctrl := gomock.NewController(t) p := NewMockPartitionConfigProvider(ctrl) p.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).AnyTimes() p.EXPECT().GetLogger().Return(log.NewNoop()).AnyTimes() lb := NewRoundRobinLoadBalancer(p) assert.NotNil(t, lb) rb, ok := lb.(*roundRobinLoadBalancer) assert.NotNil(t, rb) assert.True(t, ok) assert.Equal(t, p, rb.provider) } func TestPickPartition(t *testing.T) { tests := []struct { name string domainID string taskList types.TaskList taskListType int forwardedFrom string nPartitions int setupCache func(mockCache *cache.MockCache) expectedResult string }{ { name: "nPartitions <= 1", domainID: "testDomain", taskList: types.TaskList{Name: "testTaskList", Kind: types.TaskListKindNormal.Ptr()}, taskListType: 1, forwardedFrom: "", nPartitions: 1, setupCache: nil, expectedResult: "testTaskList", }, { name: "Cache miss and partitioned task list", domainID: "testDomain", taskList: types.TaskList{Name: "testTaskList", Kind: types.TaskListKindNormal.Ptr()}, taskListType: 1, forwardedFrom: "", nPartitions: 3, setupCache: func(mockCache *cache.MockCache) { mockCache.EXPECT().Get(key{ domainID: "testDomain", taskListName: "testTaskList", taskListType: 1, }).Return(nil) mockCache.EXPECT().PutIfNotExist(key{ domainID: "testDomain", taskListName: "testTaskList", taskListType: 1, }, gomock.Any()).DoAndReturn(func(key key, val interface{}) (interface{}, error) { if *val.(*int64) != -1 { panic("Expected value to be -1") } return val, nil }) }, expectedResult: "testTaskList", }, { name: "Cache error and partitioned task list", domainID: "testDomain", taskList: types.TaskList{Name: "testTaskList", Kind: types.TaskListKindNormal.Ptr()}, taskListType: 1, forwardedFrom: "", nPartitions: 3, setupCache: func(mockCache *cache.MockCache) { mockCache.EXPECT().Get(key{ domainID: "testDomain", taskListName: "testTaskList", taskListType: 1, }).Return(nil) mockCache.EXPECT().PutIfNotExist(key{ domainID: "testDomain", taskListName: "testTaskList", taskListType: 1, }, gomock.Any()).Return(nil, fmt.Errorf("cache error")) }, expectedResult: "testTaskList", }, { name: "Cache hit and partitioned task list", domainID: "testDomain", taskList: types.TaskList{Name: "testTaskList", Kind: types.TaskListKindNormal.Ptr()}, taskListType: 1, forwardedFrom: "", nPartitions: 3, setupCache: func(mockCache *cache.MockCache) { mockCache.EXPECT().Get(key{ domainID: "testDomain", taskListName: "testTaskList", taskListType: 1, }).Return(new(int64)) }, expectedResult: "/__cadence_sys/testTaskList/1", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockCache := cache.NewMockCache(ctrl) // If the test requires setting up cache behavior, call setupCache if tt.setupCache != nil { tt.setupCache(mockCache) } // Call the pickPartition function result := pickPartition( tt.domainID, tt.taskList, tt.taskListType, tt.nPartitions, mockCache, ) // Assert that the result matches the expected result assert.Equal(t, tt.expectedResult, result) }) } } func setUpMocksForRoundRobinLoadBalancer(t *testing.T, pickPartitionFn func(domainID string, taskList types.TaskList, taskListType int, nPartitions int, partitionCache cache.Cache) string) (*roundRobinLoadBalancer, *MockPartitionConfigProvider, *cache.MockCache) { ctrl := gomock.NewController(t) mockProvider := NewMockPartitionConfigProvider(ctrl) mockCache := cache.NewMockCache(ctrl) return &roundRobinLoadBalancer{ provider: mockProvider, readCache: mockCache, writeCache: mockCache, pickPartitionFn: pickPartitionFn, }, mockProvider, mockCache } func TestRoundRobinPickWritePartition(t *testing.T) { testCases := []struct { name string forwardedFrom string taskListType int nPartitions int taskListKind types.TaskListKind expectedPartition string }{ { name: "multiple write partitions, no forward", forwardedFrom: "", taskListType: 0, nPartitions: 3, taskListKind: types.TaskListKindNormal, expectedPartition: "custom-partition", // Simulated by fake pickPartitionFn }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Fake pickPartitionFn behavior fakePickPartitionFn := func(domainID string, taskList types.TaskList, taskListType int, nPartitions int, partitionCache cache.Cache) string { assert.Equal(t, "test-domain-id", domainID) assert.Equal(t, "test-task-list", taskList.Name) assert.Equal(t, tc.taskListKind, taskList.GetKind()) assert.Equal(t, tc.taskListType, taskListType) assert.Equal(t, tc.nPartitions, nPartitions) if taskList.GetKind() == types.TaskListKindSticky { return taskList.GetName() } return "custom-partition" } // Set up mocks with the fake pickPartitionFn loadBalancer, mockProvider, _ := setUpMocksForRoundRobinLoadBalancer(t, fakePickPartitionFn) mockProvider.EXPECT(). GetNumberOfWritePartitions("test-domain-id", types.TaskList{Name: "test-task-list", Kind: &tc.taskListKind}, tc.taskListType). Return(tc.nPartitions). Times(1) kind := tc.taskListKind taskList := types.TaskList{Name: "test-task-list", Kind: &kind} req := &types.AddDecisionTaskRequest{ DomainUUID: "test-domain-id", TaskList: &taskList, ForwardedFrom: tc.forwardedFrom, } partition := loadBalancer.PickWritePartition(tc.taskListType, req) // Validate result assert.Equal(t, tc.expectedPartition, partition) }) } } func TestRoundRobinPickReadPartition(t *testing.T) { testCases := []struct { name string forwardedFrom string taskListType int nPartitions int taskListKind types.TaskListKind expectedPartition string }{ { name: "multiple read partitions, no forward", forwardedFrom: "", taskListType: 0, nPartitions: 3, taskListKind: types.TaskListKindNormal, expectedPartition: "custom-partition", // Simulated by fake pickPartitionFn }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Fake pickPartitionFn behavior fakePickPartitionFn := func(domainID string, taskList types.TaskList, taskListType int, nPartitions int, partitionCache cache.Cache) string { assert.Equal(t, "test-domain-id", domainID) assert.Equal(t, "test-task-list", taskList.Name) assert.Equal(t, tc.taskListKind, taskList.GetKind()) assert.Equal(t, tc.taskListType, taskListType) assert.Equal(t, tc.nPartitions, nPartitions) if taskList.GetKind() == types.TaskListKindSticky { return taskList.GetName() } return "custom-partition" } // Set up mocks with the fake pickPartitionFn loadBalancer, mockProvider, _ := setUpMocksForRoundRobinLoadBalancer(t, fakePickPartitionFn) mockProvider.EXPECT(). GetNumberOfReadPartitions("test-domain-id", types.TaskList{Name: "test-task-list", Kind: &tc.taskListKind}, tc.taskListType). Return(tc.nPartitions). Times(1) kind := tc.taskListKind taskList := types.TaskList{Name: "test-task-list", Kind: &kind} req := &types.AddDecisionTaskRequest{ DomainUUID: "test-domain-id", TaskList: &taskList, ForwardedFrom: tc.forwardedFrom, } partition := loadBalancer.PickReadPartition(tc.taskListType, req, "") // Validate result assert.Equal(t, tc.expectedPartition, partition) }) } } ================================================ FILE: client/matching/weighted_loadbalancer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "math" "math/rand" "path" "sort" "strconv" "sync" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) const _backlogThreshold = 100 type ( weightSelector struct { sync.RWMutex weights []int64 initialized bool threshold int64 } weightedLoadBalancer struct { fallbackLoadBalancer LoadBalancer provider PartitionConfigProvider weightCache cache.Cache logger log.Logger } ) func newWeightSelector(n int, threshold int64) *weightSelector { pw := &weightSelector{weights: make([]int64, n), threshold: threshold} for i := range pw.weights { pw.weights[i] = -1 } return pw } func (pw *weightSelector) pickBetween(partitions []int) int { totalWeight := int64(0) cumulativeWeights := make([]int64, len(partitions)) pw.RLock() defer pw.RUnlock() if !pw.initialized { return -1 } shouldEnable := false for i, partitionID := range partitions { // Invalid partition specified, bail out if partitionID >= len(pw.weights) { return -1 } weight := pw.weights[partitionID] totalWeight += weight cumulativeWeights[i] = totalWeight if weight > pw.threshold { shouldEnable = true // only enable weight selection if backlog size is larger than the threshold } } if totalWeight <= 0 || !shouldEnable { return -1 } r := rand.Int63n(totalWeight) index := sort.Search(len(cumulativeWeights), func(i int) bool { return cumulativeWeights[i] > r }) return partitions[index] } func (pw *weightSelector) pick() (int, []int64) { totalWeight := int64(0) pw.RLock() defer pw.RUnlock() cumulativeWeights := make([]int64, len(pw.weights)) if !pw.initialized { return -1, cumulativeWeights } shouldDrain := false for i, w := range pw.weights { totalWeight += w cumulativeWeights[i] = totalWeight if w > pw.threshold { shouldDrain = true // only enable weight selection if backlog size is larger than the threshold } } if totalWeight <= 0 || !shouldDrain { return -1, cumulativeWeights } r := rand.Int63n(totalWeight) index := sort.Search(len(cumulativeWeights), func(i int) bool { return cumulativeWeights[i] > r }) return index, cumulativeWeights } func (pw *weightSelector) update(n, p int, weight int64) { pw.Lock() defer pw.Unlock() if n > len(pw.weights) { newWeights := make([]int64, n) copy(newWeights, pw.weights) for i := len(pw.weights); i < n; i++ { newWeights[i] = -1 } pw.weights = newWeights pw.initialized = false } else if n < len(pw.weights) { pw.weights = pw.weights[:n] } if p < n { // the opposite condition can happen when the task list is scaled down and we get an update from a drained partition pw.weights[p] = weight } for _, w := range pw.weights { if w == -1 { return } } pw.initialized = true } func NewWeightedLoadBalancer( lb LoadBalancer, provider PartitionConfigProvider, logger log.Logger, ) WeightedLoadBalancer { return &weightedLoadBalancer{ fallbackLoadBalancer: lb, provider: provider, weightCache: cache.New(&cache.Options{ TTL: 0, InitialCapacity: 100, Pin: false, MaxCount: 3000, ActivelyEvict: false, MetricsScope: provider.GetMetricsClient().Scope(metrics.LoadBalancerScope), Logger: logger, }), logger: logger, } } func (lb *weightedLoadBalancer) PickWritePartition( taskListType int, req WriteRequest, ) string { return lb.fallbackLoadBalancer.PickWritePartition(taskListType, req) } func (lb *weightedLoadBalancer) PickReadPartition( taskListType int, req ReadRequest, isolationGroup string, ) string { taskListKey := key{ domainID: req.GetDomainUUID(), taskListName: req.GetTaskList().GetName(), taskListType: taskListType, } wI := lb.weightCache.Get(taskListKey) if wI == nil { return lb.fallbackLoadBalancer.PickReadPartition(taskListType, req, isolationGroup) } w, ok := wI.(*weightSelector) if !ok { return lb.fallbackLoadBalancer.PickReadPartition(taskListType, req, isolationGroup) } p, cumulativeWeights := w.pick() lb.logger.Debug("pick read partition", tag.WorkflowDomainID(req.GetDomainUUID()), tag.WorkflowTaskListName(req.GetTaskList().Name), tag.WorkflowTaskListType(taskListType), tag.Dynamic("cumulative-weights", cumulativeWeights), tag.Dynamic("task-list-partition", p)) if p < 0 { return lb.fallbackLoadBalancer.PickReadPartition(taskListType, req, isolationGroup) } return getPartitionTaskListName(req.GetTaskList().GetName(), p) } func (lb *weightedLoadBalancer) UpdateWeight( taskListType int, req ReadRequest, partition string, info *types.LoadBalancerHints, ) { taskList := *req.GetTaskList() if info == nil { return } p := 0 if partition != taskList.GetName() { var err error p, err = strconv.Atoi(path.Base(partition)) if err != nil { return } } taskListKey := key{ domainID: req.GetDomainUUID(), taskListName: taskList.GetName(), taskListType: taskListType, } n := lb.provider.GetNumberOfReadPartitions(req.GetDomainUUID(), taskList, taskListType) if n <= 1 { lb.weightCache.Delete(taskListKey) return } wI := lb.weightCache.Get(taskListKey) if wI == nil { var err error w := newWeightSelector(n, _backlogThreshold) wI, err = lb.weightCache.PutIfNotExist(taskListKey, w) if err != nil { return } } w, ok := wI.(*weightSelector) if !ok { return } weight := calcWeightFromLoadBalancerHints(info) lb.logger.Debug("update task list partition weight", tag.WorkflowDomainID(req.GetDomainUUID()), tag.WorkflowTaskListName(taskList.GetName()), tag.WorkflowTaskListType(taskListType), tag.Dynamic("task-list-partition", p), tag.Dynamic("weight", weight), tag.Dynamic("load-balancer-hints", info)) w.update(n, p, weight) } func (lb *weightedLoadBalancer) PickBetween(domainID, taskListName string, taskListType int, partitions []int) int { if len(partitions) == 0 { return -1 } if len(partitions) == 1 { return partitions[0] } taskListKey := key{ domainID: domainID, taskListName: taskListName, taskListType: taskListType, } wI := lb.weightCache.Get(taskListKey) if wI == nil { return -1 } w, ok := wI.(*weightSelector) if !ok { return -1 } return w.pickBetween(partitions) } func calcWeightFromLoadBalancerHints(info *types.LoadBalancerHints) int64 { // according to Little's Law, the average number of tasks in the queue L = λW // where λ is the average arrival rate and W is the average wait time a task spends in the queue // here λ is the QPS and W is the average match latency which is 10ms // so the backlog hint should be backlog count + L. smoothingNumber := int64(0) qps := info.RatePerSecond if qps > 0.01 { smoothingNumber = int64(math.Ceil(qps * 0.01)) } return info.BacklogCount + smoothingNumber } ================================================ FILE: client/matching/weighted_loadbalancer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package matching import ( "math" "math/rand" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) func TestPollerWeight(t *testing.T) { n := 4 pw := newWeightSelector(n, 100) // uninitialized weights should return -1 p, cumulativeWeights := pw.pick() assert.Equal(t, -1, p) assert.Equal(t, []int64{0, 0, 0, 0}, cumulativeWeights) // all 0 weights should return -1 for i := 0; i < n; i++ { pw.update(n, i, 0) p, cumulativeWeights := pw.pick() assert.Equal(t, -1, p) assert.Equal(t, []int64{0, 0, 0, 0}, cumulativeWeights) } // if only one item has non-zero weight, always pick that item pw.update(n, 3, 400) for i := 0; i < 100; i++ { p, cumulativeWeights := pw.pick() assert.Equal(t, 3, p) assert.Equal(t, []int64{0, 0, 0, 400}, cumulativeWeights) } pw.update(n, 2, 300) pw.update(n, 1, 200) pw.update(n, 0, 100) // test pick probabilities testPickProbHelper(t, pw, time.Now().UnixNano()) // shrink size and test pick probabilities pw.update(n-1, 2, 200) testPickProbHelper(t, pw, time.Now().UnixNano()) // expand size and test pick probabilities pw.update(n, 3, 300) pw.update(n+1, 4, 400) testPickProbHelper(t, pw, time.Now().UnixNano()) // no panics pw.update(n, 4, 0) } func testPickProbHelper(t *testing.T, pw *weightSelector, seed int64) { t.Helper() rand.Seed(seed) // Collect pick results results := make(map[int]int) numPicks := 1000000 for i := 0; i < numPicks; i++ { index, _ := pw.pick() results[index]++ } // Calculate expected probabilities totalWeight := int64(0) for _, w := range pw.weights { totalWeight += w } expectedProbs := make([]float64, len(pw.weights)) for i, w := range pw.weights { expectedProbs[i] = float64(w) / float64(totalWeight) } // Check that pick results are approximately proportional to weights for i := 0; i < len(pw.weights); i++ { expectedCount := expectedProbs[i] * float64(numPicks) actualCount := float64(results[i]) delta := expectedCount * 0.02 // Allow 2% error margin if actualCount < expectedCount-delta || actualCount > expectedCount+delta { t.Errorf("Index %d: expected count approximately %.0f, got %d", i, expectedCount, results[i]) } } } func TestNewWeightedLoadBalancer(t *testing.T) { ctrl := gomock.NewController(t) roundRobinMock := NewMockLoadBalancer(ctrl) p := NewMockPartitionConfigProvider(ctrl) p.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).AnyTimes() logger := testlogger.New(t) p.EXPECT().GetLogger().Return(logger).AnyTimes() lb := NewWeightedLoadBalancer(roundRobinMock, p, logger) assert.NotNil(t, lb) weightedLB, ok := lb.(*weightedLoadBalancer) assert.NotNil(t, weightedLB) assert.True(t, ok) assert.Equal(t, roundRobinMock, weightedLB.fallbackLoadBalancer) assert.Equal(t, p, weightedLB.provider) assert.NotNil(t, weightedLB.weightCache) assert.NotNil(t, weightedLB.logger) } func TestWeightedLoadBalancer_PickWritePartition(t *testing.T) { testCases := []struct { name string domainID string taskList types.TaskList taskListType int forwardedFrom string expectedResult string setupMock func(m *MockLoadBalancer) }{ { name: "Basic case", domainID: "domainA", taskList: types.TaskList{Name: "taskListA"}, setupMock: func(m *MockLoadBalancer) { req := &types.AddDecisionTaskRequest{ DomainUUID: "domainA", TaskList: &types.TaskList{Name: "taskListA"}, ForwardedFrom: "", } m.EXPECT(). PickWritePartition(0, req). Return("partitionA") }, expectedResult: "partitionA", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockFallbackLB := NewMockLoadBalancer(ctrl) if tc.setupMock != nil { tc.setupMock(mockFallbackLB) } lb := &weightedLoadBalancer{ fallbackLoadBalancer: mockFallbackLB, } req := &types.AddDecisionTaskRequest{ DomainUUID: tc.domainID, TaskList: &tc.taskList, ForwardedFrom: tc.forwardedFrom, } result := lb.PickWritePartition(tc.taskListType, req) assert.Equal(t, tc.expectedResult, result) }) } } func TestWeightedLoadBalancer_PickReadPartition(t *testing.T) { testCases := []struct { name string domainID string taskList types.TaskList taskListType int forwardedFrom string weightCacheReturn interface{} fallbackReturn string expectedResult string expectFallbackCall bool }{ { name: "WeightCache returns nil", domainID: "domainA", taskList: types.TaskList{Name: "taskListA"}, weightCacheReturn: nil, fallbackReturn: "fallbackPartition", expectedResult: "fallbackPartition", expectFallbackCall: true, }, { name: "WeightCache returns invalid type", domainID: "domainB", taskList: types.TaskList{Name: "taskListB"}, weightCacheReturn: "invalidType", fallbackReturn: "fallbackPartition", expectedResult: "fallbackPartition", expectFallbackCall: true, }, { name: "WeightSelector pick returns negative", domainID: "domainC", taskList: types.TaskList{Name: "taskListC"}, weightCacheReturn: newWeightSelector(2, 100), fallbackReturn: "fallbackPartition", expectedResult: "fallbackPartition", expectFallbackCall: true, }, { name: "WeightSelector pick returns non-negative", domainID: "domainD", taskList: types.TaskList{Name: "taskListD"}, weightCacheReturn: func() *weightSelector { pw := newWeightSelector(2, 10) pw.update(2, 0, 0) pw.update(2, 1, 11) return pw }(), expectedResult: getPartitionTaskListName("taskListD", 1), expectFallbackCall: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { req := &types.AddDecisionTaskRequest{ DomainUUID: tc.domainID, TaskList: &tc.taskList, ForwardedFrom: tc.forwardedFrom, } ctrl := gomock.NewController(t) // Create mocks. mockWeightCache := cache.NewMockCache(ctrl) mockFallbackLoadBalancer := NewMockLoadBalancer(ctrl) // Set up the mocks. taskListKey := key{ domainID: tc.domainID, taskListName: tc.taskList.GetName(), taskListType: tc.taskListType, } mockWeightCache.EXPECT(). Get(taskListKey). Return(tc.weightCacheReturn) if tc.expectFallbackCall { mockFallbackLoadBalancer.EXPECT(). PickReadPartition(tc.taskListType, req, ""). Return(tc.fallbackReturn) } logger := testlogger.New(t) // Create the weightedLoadBalancer instance. lb := &weightedLoadBalancer{ weightCache: mockWeightCache, fallbackLoadBalancer: mockFallbackLoadBalancer, logger: logger, } // Call the method under test. result := lb.PickReadPartition(tc.taskListType, req, "") // Assert the result. assert.Equal(t, tc.expectedResult, result) }) } } func TestWeightedLoadBalancer_UpdateWeight(t *testing.T) { testCases := []struct { name string domainID string taskList types.TaskList taskListType int forwardedFrom string partition string loadBalancerHints *types.LoadBalancerHints setupMock func(*cache.MockCache, *MockPartitionConfigProvider) }{ { name: "Sticky task list", domainID: "domainA", taskList: types.TaskList{Name: "a", Kind: types.TaskListKindSticky.Ptr()}, }, { name: "forwarded request", domainID: "domainA", taskList: types.TaskList{Name: "a"}, forwardedFrom: "tasklist", }, { name: "partitioned task list", domainID: "domainA", taskList: types.TaskList{Name: "/__cadence_sys/aaa/1"}, }, { name: "nil loadBalancerHints", domainID: "domainA", taskList: types.TaskList{Name: "a"}, }, { name: "1 partition", domainID: "domainA", taskList: types.TaskList{Name: "a"}, partition: "a", loadBalancerHints: &types.LoadBalancerHints{ BacklogCount: 1, }, setupMock: func(mockCache *cache.MockCache, mockPartitionConfigProvider *MockPartitionConfigProvider) { mockPartitionConfigProvider.EXPECT().GetNumberOfReadPartitions("domainA", types.TaskList{Name: "a"}, 0).Return(1) mockCache.EXPECT().Delete(key{ domainID: "domainA", taskListName: "a", taskListType: 0, }) }, }, { name: "partition 0", domainID: "domainA", taskList: types.TaskList{Name: "a"}, partition: "a", loadBalancerHints: &types.LoadBalancerHints{ BacklogCount: 1, }, setupMock: func(mockCache *cache.MockCache, mockPartitionConfigProvider *MockPartitionConfigProvider) { mockPartitionConfigProvider.EXPECT().GetNumberOfReadPartitions("domainA", types.TaskList{Name: "a"}, 0).Return(2) mockCache.EXPECT().Get(key{ domainID: "domainA", taskListName: "a", taskListType: 0, }).Return(nil) mockCache.EXPECT().PutIfNotExist(key{ domainID: "domainA", taskListName: "a", taskListType: 0, }, newWeightSelector(2, 100)).Return(newWeightSelector(2, 100), nil) }, }, { name: "partition 1", domainID: "domainA", taskList: types.TaskList{Name: "a"}, partition: "/__cadence_sys/a/1", loadBalancerHints: &types.LoadBalancerHints{ BacklogCount: 1, }, setupMock: func(mockCache *cache.MockCache, mockPartitionConfigProvider *MockPartitionConfigProvider) { mockPartitionConfigProvider.EXPECT().GetNumberOfReadPartitions("domainA", types.TaskList{Name: "a"}, 0).Return(2) mockCache.EXPECT().Get(key{ domainID: "domainA", taskListName: "a", taskListType: 0, }).Return(newWeightSelector(2, 100)) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { req := &types.AddDecisionTaskRequest{ DomainUUID: tc.domainID, TaskList: &tc.taskList, ForwardedFrom: tc.forwardedFrom, } ctrl := gomock.NewController(t) mockWeightCache := cache.NewMockCache(ctrl) mockPartitionConfigProvider := NewMockPartitionConfigProvider(ctrl) lb := &weightedLoadBalancer{ weightCache: mockWeightCache, provider: mockPartitionConfigProvider, logger: testlogger.New(t), } if tc.setupMock != nil { tc.setupMock(mockWeightCache, mockPartitionConfigProvider) } lb.UpdateWeight(tc.taskListType, req, tc.partition, tc.loadBalancerHints) }) } } func TestCalcWeightFromLoadBalancerHints(t *testing.T) { tests := []struct { name string info types.LoadBalancerHints expected int64 }{ { name: "Zero QPS and backlog count", info: types.LoadBalancerHints{BacklogCount: 0, RatePerSecond: 0}, expected: 0, }, { name: "Small QPS below threshold", info: types.LoadBalancerHints{BacklogCount: 10, RatePerSecond: 0.005}, expected: 10, }, { name: "QPS above threshold with no backlog", info: types.LoadBalancerHints{BacklogCount: 0, RatePerSecond: 2}, expected: int64(math.Ceil(2 * 0.01)), // smoothingNumber calculation }, { name: "QPS above threshold with backlog", info: types.LoadBalancerHints{BacklogCount: 100, RatePerSecond: 5}, expected: 100 + int64(math.Ceil(5*0.01)), // backlog + smoothingNumber }, { name: "Large QPS", info: types.LoadBalancerHints{BacklogCount: 50, RatePerSecond: 100}, expected: 50 + int64(math.Ceil(100*0.01)), // backlog + smoothingNumber }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := calcWeightFromLoadBalancerHints(&tt.info) assert.Equal(t, tt.expected, result, "unexpected result for %s", tt.name) }) } } func TestPickBetween(t *testing.T) { tl := types.TaskList{Name: "a"} cases := []struct { name string hints map[int]*types.LoadBalancerHints config *types.TaskListPartitionConfig from []int allowed []int }{ { name: "unknown TL", config: createDefaultConfig(1, 1), from: []int{0, 1, 2, 3}, allowed: []int{-1}, }, { name: "no partitions specified", config: createDefaultConfig(1, 1), from: []int{}, allowed: []int{-1}, }, { name: "single partition specified", config: createDefaultConfig(1, 1), from: []int{0}, allowed: []int{0}, }, { name: "single partition", config: createDefaultConfig(1, 1), from: []int{0, 1, 2, 3}, allowed: []int{-1}, }, { name: "not initialized", config: createDefaultConfig(4, 4), from: []int{0, 1, 2, 3}, allowed: []int{-1}, }, { name: "partially initialized", config: createDefaultConfig(4, 4), hints: map[int]*types.LoadBalancerHints{ 0: { BacklogCount: 1000000, RatePerSecond: 100, }, 1: { BacklogCount: 1000000, RatePerSecond: 100, }, 2: { BacklogCount: 1000000, RatePerSecond: 100, }, }, from: []int{0, 1, 2, 3}, allowed: []int{-1}, }, { name: "fully initialized", config: createDefaultConfig(4, 4), hints: map[int]*types.LoadBalancerHints{ 0: { BacklogCount: 1000000, RatePerSecond: 100, }, 1: { BacklogCount: 1000000, RatePerSecond: 100, }, 2: { BacklogCount: 1000000, RatePerSecond: 100, }, 3: { BacklogCount: 1000000, RatePerSecond: 100, }, }, from: []int{0, 1, 2, 3}, allowed: []int{0, 1, 2, 3}, }, { name: "invalid partition", config: createDefaultConfig(4, 4), hints: map[int]*types.LoadBalancerHints{ 0: { BacklogCount: 1000000, RatePerSecond: 100, }, 1: { BacklogCount: 1000000, RatePerSecond: 100, }, 2: { BacklogCount: 1000000, RatePerSecond: 100, }, 3: { BacklogCount: 1000000, RatePerSecond: 100, }, }, from: []int{0, 1, 2, 4}, allowed: []int{-1}, }, { name: "only consider specified - below threshold", config: createDefaultConfig(4, 4), hints: map[int]*types.LoadBalancerHints{ 0: { BacklogCount: 1000000, RatePerSecond: 100, }, 1: { BacklogCount: 1, RatePerSecond: 1, }, 2: { BacklogCount: 1000000, RatePerSecond: 100, }, 3: { BacklogCount: 1, RatePerSecond: 1, }, }, from: []int{1, 3}, allowed: []int{-1}, }, { name: "only consider specified - above threshold", config: createDefaultConfig(4, 4), hints: map[int]*types.LoadBalancerHints{ 0: { BacklogCount: 1000000, RatePerSecond: 100, }, 1: { BacklogCount: _backlogThreshold, RatePerSecond: 1, }, 2: { BacklogCount: 1000000, RatePerSecond: 100, }, 3: { BacklogCount: 1, RatePerSecond: 1, }, }, from: []int{1, 3}, allowed: []int{1, 3}, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { req := &types.AddDecisionTaskRequest{ DomainUUID: testDomain, TaskList: &tl, ForwardedFrom: "", } ctrl := gomock.NewController(t) weightCache := cache.New(&cache.Options{ TTL: 0, InitialCapacity: 100, Pin: false, MaxCount: 3000, ActivelyEvict: false, MetricsScope: metrics.NoopScope, Logger: testlogger.New(t), }) mockPartitionConfigProvider := NewMockPartitionConfigProvider(ctrl) mockPartitionConfigProvider.EXPECT().GetNumberOfReadPartitions(testDomain, tl, 0).Return(len(tc.config.ReadPartitions)).Times(len(tc.hints)) lb := &weightedLoadBalancer{ weightCache: weightCache, provider: mockPartitionConfigProvider, logger: testlogger.New(t), } for partitionID, hint := range tc.hints { lb.UpdateWeight(0, req, getPartitionTaskListName(tl.Name, partitionID), hint) } actual := lb.PickBetween(testDomain, tl.Name, 0, tc.from) assert.Contains(t, tc.allowed, actual) }) } } ================================================ FILE: client/sharddistributor/interface.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sharddistributor import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/client/sharddistributor //go:generate gowrap gen -g -p . -i Client -t ../templates/retry.tmpl -o ../wrappers/retryable/sharddistributor_generated.go -v client=ShardDistributor //go:generate gowrap gen -g -p . -i Client -t ../templates/metered.tmpl -o ../wrappers/metered/sharddistributor_generated.go -v client=ShardDistributor //go:generate gowrap gen -g -p . -i Client -t ../templates/errorinjectors.tmpl -o ../wrappers/errorinjectors/sharddistributor_generated.go -v client=ShardDistributor //go:generate gowrap gen -g -p . -i Client -t ../templates/grpc.tmpl -o ../wrappers/grpc/sharddistributor_generated.go -v client=ShardDistributor -v package=sharddistributorv1 -v path=github.com/uber/cadence/.gen/proto/sharddistributor/v1 -v prefix=ShardDistributor //go:generate gowrap gen -g -p . -i Client -t ../templates/timeout.tmpl -o ../wrappers/timeout/sharddistributor_generated.go -v client=ShardDistributor type Client interface { GetShardOwner(context.Context, *types.GetShardOwnerRequest, ...yarpc.CallOption) (*types.GetShardOwnerResponse, error) WatchNamespaceState(context.Context, *types.WatchNamespaceStateRequest, ...yarpc.CallOption) (WatchNamespaceStateClient, error) } type WatchNamespaceStateClient interface { Context() context.Context Recv(...yarpc.StreamOption) (*types.WatchNamespaceStateResponse, error) CloseSend(...yarpc.StreamOption) error } ================================================ FILE: client/sharddistributor/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package sharddistributor -source interface.go -destination interface_mock.go -self_package github.com/uber/cadence/client/sharddistributor // // Package sharddistributor is a generated GoMock package. package sharddistributor import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" types "github.com/uber/cadence/common/types" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // GetShardOwner mocks base method. func (m *MockClient) GetShardOwner(arg0 context.Context, arg1 *types.GetShardOwnerRequest, arg2 ...yarpc.CallOption) (*types.GetShardOwnerResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetShardOwner", varargs...) ret0, _ := ret[0].(*types.GetShardOwnerResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetShardOwner indicates an expected call of GetShardOwner. func (mr *MockClientMockRecorder) GetShardOwner(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardOwner", reflect.TypeOf((*MockClient)(nil).GetShardOwner), varargs...) } // WatchNamespaceState mocks base method. func (m *MockClient) WatchNamespaceState(arg0 context.Context, arg1 *types.WatchNamespaceStateRequest, arg2 ...yarpc.CallOption) (WatchNamespaceStateClient, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "WatchNamespaceState", varargs...) ret0, _ := ret[0].(WatchNamespaceStateClient) ret1, _ := ret[1].(error) return ret0, ret1 } // WatchNamespaceState indicates an expected call of WatchNamespaceState. func (mr *MockClientMockRecorder) WatchNamespaceState(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WatchNamespaceState", reflect.TypeOf((*MockClient)(nil).WatchNamespaceState), varargs...) } // MockWatchNamespaceStateClient is a mock of WatchNamespaceStateClient interface. type MockWatchNamespaceStateClient struct { ctrl *gomock.Controller recorder *MockWatchNamespaceStateClientMockRecorder isgomock struct{} } // MockWatchNamespaceStateClientMockRecorder is the mock recorder for MockWatchNamespaceStateClient. type MockWatchNamespaceStateClientMockRecorder struct { mock *MockWatchNamespaceStateClient } // NewMockWatchNamespaceStateClient creates a new mock instance. func NewMockWatchNamespaceStateClient(ctrl *gomock.Controller) *MockWatchNamespaceStateClient { mock := &MockWatchNamespaceStateClient{ctrl: ctrl} mock.recorder = &MockWatchNamespaceStateClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWatchNamespaceStateClient) EXPECT() *MockWatchNamespaceStateClientMockRecorder { return m.recorder } // CloseSend mocks base method. func (m *MockWatchNamespaceStateClient) CloseSend(arg0 ...yarpc.StreamOption) error { m.ctrl.T.Helper() varargs := []any{} for _, a := range arg0 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CloseSend", varargs...) ret0, _ := ret[0].(error) return ret0 } // CloseSend indicates an expected call of CloseSend. func (mr *MockWatchNamespaceStateClientMockRecorder) CloseSend(arg0 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseSend", reflect.TypeOf((*MockWatchNamespaceStateClient)(nil).CloseSend), arg0...) } // Context mocks base method. func (m *MockWatchNamespaceStateClient) Context() context.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Context") ret0, _ := ret[0].(context.Context) return ret0 } // Context indicates an expected call of Context. func (mr *MockWatchNamespaceStateClientMockRecorder) Context() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockWatchNamespaceStateClient)(nil).Context)) } // Recv mocks base method. func (m *MockWatchNamespaceStateClient) Recv(arg0 ...yarpc.StreamOption) (*types.WatchNamespaceStateResponse, error) { m.ctrl.T.Helper() varargs := []any{} for _, a := range arg0 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Recv", varargs...) ret0, _ := ret[0].(*types.WatchNamespaceStateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Recv indicates an expected call of Recv. func (mr *MockWatchNamespaceStateClientMockRecorder) Recv(arg0 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Recv", reflect.TypeOf((*MockWatchNamespaceStateClient)(nil).Recv), arg0...) } ================================================ FILE: client/sharddistributorexecutor/executorinterface.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sharddistributorexecutor import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination executorinterface_mock.go -self_package github.com/uber/cadence/client/sharddistributorexecutor //go:generate gowrap gen -g -p . -i Client -t ../templates/retry.tmpl -o ../wrappers/retryable/sharddistributorexecutor_generated.go -v client=ShardDistributorExecutor //go:generate gowrap gen -g -p . -i Client -t ../templates/metered.tmpl -o ../wrappers/metered/sharddistributorexecutor_generated.go -v client=ShardDistributorExecutor //go:generate gowrap gen -g -p . -i Client -t ../templates/errorinjectors.tmpl -o ../wrappers/errorinjectors/sharddistributorexecutor_generated.go -v client=ShardDistributorExecutor //go:generate gowrap gen -g -p . -i Client -t ../templates/grpc.tmpl -o ../wrappers/grpc/sharddistributorexecutor_generated.go -v client=ShardDistributorExecutor -v package=apiv1 -v path=github.com/uber/cadence/proto/internal/uber/cadence/sharddistributor/v1 -v prefix=ShardDistributorExecutor //go:generate gowrap gen -g -p . -i Client -t ../templates/timeout.tmpl -o ../wrappers/timeout/sharddistributorexecutor_generated.go -v client=ShardDistributorExecutor type Client interface { Heartbeat(context.Context, *types.ExecutorHeartbeatRequest, ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) } ================================================ FILE: client/sharddistributorexecutor/executorinterface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: executorinterface.go // // Generated by this command: // // mockgen -package sharddistributorexecutor -source executorinterface.go -destination executorinterface_mock.go -self_package github.com/uber/cadence/client/sharddistributorexecutor // // Package sharddistributorexecutor is a generated GoMock package. package sharddistributorexecutor import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" types "github.com/uber/cadence/common/types" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // Heartbeat mocks base method. func (m *MockClient) Heartbeat(arg0 context.Context, arg1 *types.ExecutorHeartbeatRequest, arg2 ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Heartbeat", varargs...) ret0, _ := ret[0].(*types.ExecutorHeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Heartbeat indicates an expected call of Heartbeat. func (mr *MockClientMockRecorder) Heartbeat(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Heartbeat", reflect.TypeOf((*MockClient)(nil).Heartbeat), varargs...) } ================================================ FILE: client/templates/errorinjectors.tmpl ================================================ import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) {{$clientName := (index .Vars "client")}} {{ $decorator := (printf "%s%s" (down $clientName) .Interface.Name) }} {{ $Decorator := (printf "%s%s" $clientName .Interface.Name) }} {{$fakeErr := printf "msg%sInjectedFakeErr" $clientName}} const ( {{$fakeErr}} = "Injected fake {{down $clientName}} client error" ) // {{$decorator}} implements {{.Interface.Type}} interface instrumented with retries type {{$decorator}} struct { client {{.Interface.Type}} errorRate float64 logger log.Logger fakeErrFn func(float64) error forwardCallFn func(error) bool } // New{{$Decorator}} creates a new instance of {{$decorator}} that injects error into every call with a given rate. func New{{$Decorator}}(client {{.Interface.Type}}, errorRate float64, logger log.Logger) {{.Interface.Type}} { return &{{$decorator}}{ client: client, errorRate: errorRate, logger: logger, fakeErrFn: errors.GenerateFakeError, forwardCallFn: errors.ShouldForwardCall, } } {{range $method := .Interface.Methods}} {{- if (and $method.AcceptsContext $method.ReturnsError)}} func (c *{{$decorator}}) {{$method.Declaration}} { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { {{$method.ResultsNames}} = c.client.{{$method.Call}} } if fakeErr != nil { c.logger.Error({{$fakeErr}}, {{printf "tag.%sClientOperation%s" $clientName .Name}}, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } {{else}} func (c *{{$decorator}}) {{$method.Declaration}} { {{ $method.Pass "c.wrapped." }} } {{end}} {{end}} ================================================ FILE: client/templates/grpc.tmpl ================================================ {{$packagePath := (index .Vars "path")}} {{$package := (index .Vars "package")}} {{$prefix := (index .Vars "prefix")}} import ( "context" "go.uber.org/yarpc" {{$package}} "{{$packagePath}}" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" ) {{$interfaceName := .Interface.Name}} {{$clientName := (index .Vars "client")}} {{ $decorator := (printf "%s%s" (down $clientName) .Interface.Name) }} {{range $method := .Interface.Methods}} {{$Request := printf "%sRequest" $method.Name}} {{$Response := printf "%sResponse" $method.Name}} {{- $isStreaming := false}} {{- range $method.Results}} {{- if contains "Client" .Type}} {{- $isStreaming = true}} {{- end}} {{- end}} {{- if $isStreaming}} func (g {{$decorator}}) {{$method.Declaration}} { stream, {{(index $method.Results 1).Name}} := g.c.{{$method.Name}}({{(index $method.Params 0).Name}}, proto.From{{$prefix}}{{$Request}}({{(index $method.Params 1).Name}}), {{(index $method.Params 2).Pass}}) if {{(index $method.Results 1).Name}} != nil { return nil, proto.ToError({{(index $method.Results 1).Name}}) } return &{{lower $method.Name}}Client{stream: stream}, nil } type {{lower $method.Name}}Client struct { stream {{$package}}.{{$prefix}}APIService{{$method.Name}}YARPCClient } func (c *{{lower $method.Name}}Client) Context() context.Context { return c.stream.Context() } func (c *{{lower $method.Name}}Client) Recv(options ...yarpc.StreamOption) (*types.{{$Response}}, error) { response, err := c.stream.Recv(options...) if err != nil { return nil, proto.ToError(err) } return proto.To{{$prefix}}{{$Response}}(response), nil } func (c *{{lower $method.Name}}Client) CloseSend(options ...yarpc.StreamOption) error { return proto.ToError(c.stream.CloseSend(options...)) } {{- else}} func (g {{$decorator}}) {{$method.Declaration}} { {{- if eq (len $method.Params) 2}} {{- if eq (len $method.Results) 1}} _, {{(index $method.Results 0).Name}} = g.c.{{$method.Name}}({{(index $method.Params 0).Name}}, &{{$package}}.{{$method.Name}}Request{}, {{(index $method.Params 1).Pass}}) {{- else}} response, {{(index $method.Results 1).Name}} := g.c.{{$method.Name}}({{(index $method.Params 0).Name}}, &{{$package}}.{{$method.Name}}Request{}, {{(index $method.Params 1).Pass}}) {{- end}} {{- else}} {{- if eq (len $method.Results) 1}} _, {{(index $method.Results 0).Name}} = g.c.{{$method.Name}}({{(index $method.Params 0).Name}}, proto.From{{$prefix}}{{$Request}}({{(index $method.Params 1).Name}}), {{(index $method.Params 2).Pass}}) {{- else}} response, {{(index $method.Results 1).Name}} := g.c.{{$method.Name}}({{(index $method.Params 0).Name}}, proto.From{{$prefix}}{{$Request}}({{(index $method.Params 1).Name}}), {{(index $method.Params 2).Pass}}) {{- end}} {{- end}} {{- if eq (len $method.Results) 1}} return proto.ToError({{(index $method.Results 0).Name}}) {{- else}} return proto.To{{$prefix}}{{$Response}}(response), proto.ToError({{(index $method.Results 1).Name}}) {{- end}} } {{- end}} {{end}} ================================================ FILE: client/templates/metered.tmpl ================================================ import ( "context" "strings" "go.uber.org/yarpc" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) {{$clientName := (index .Vars "client")}} {{ $decorator := (printf "%s%s" (down $clientName) .Interface.Name) }} {{ $Decorator := (printf "%s%s" $clientName .Interface.Name) }} // {{$decorator}} implements {{.Interface.Type}} interface instrumented with retries type {{$decorator}} struct { client {{.Interface.Type}} metricsClient metrics.Client } // New{{$Decorator}} creates a new instance of {{$decorator}} with retry policy func New{{$Decorator}}(client {{.Interface.Type}}, metricsClient metrics.Client) {{.Interface.Type}} { return &{{$decorator}}{ client: client, metricsClient: metricsClient, } } {{range $method := .Interface.Methods}} func (c *{{$decorator}}) {{$method.Declaration}} { {{- $scopeName:=printf "metrics.%sClient%sScope" $clientName $method.Name }} retryCount := getRetryCountFromContext({{(index $method.Params 0).Name}}) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope({{$scopeName}}) } else { scope = c.metricsClient.Scope({{$scopeName}}, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) {{- if eq $clientName "Matching" }} c.emitForwardedFromStats(scope, {{(index $method.Params 1).Name}}) {{ end }} sw := scope.StartTimer(metrics.CadenceClientLatency) {{$method.ResultsNames}} = c.client.{{$method.Call}} sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return {{$method.ResultsNames}} } {{end}} {{- if eq $clientName "Matching" }} type forwardedRequest interface { GetForwardedFrom() string GetTaskList() *types.TaskList } func (c *{{$decorator}}) emitForwardedFromStats(scope metrics.Scope, req any) { p, ok := req.(forwardedRequest) if !ok || p.GetTaskList() == nil { return } taskList := p.GetTaskList() forwardedFrom := p.GetForwardedFrom() isChildPartition := strings.HasPrefix(taskList.GetName(), constants.ReservedTaskListPrefix) if forwardedFrom != ""{ scope.IncCounter(metrics.MatchingClientForwardedCounter) return } if isChildPartition { scope.IncCounter(metrics.MatchingClientInvalidTaskListName) } return } {{ end -}} ================================================ FILE: client/templates/retry.tmpl ================================================ import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/types" ) {{$clientName := (index .Vars "client")}} {{ $decorator := (printf "%s%s" (down $clientName) .Interface.Name) }} {{ $Decorator := (printf "%s%s" $clientName .Interface.Name) }} // {{$decorator}} implements {{.Interface.Type}} interface instrumented with retries type {{$decorator}} struct { client {{.Interface.Type}} throttleRetry *backoff.ThrottleRetry } // New{{$Decorator}} creates a new instance of {{$decorator}} with retry policy func New{{$Decorator}}(client {{.Interface.Type}}, policy backoff.RetryPolicy, isRetryable backoff.IsRetryable) {{.Interface.Type}} { return &{{$decorator}}{ client: client, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(isRetryable), ), } } {{range $method := .Interface.Methods}} {{$resultsLength := len ($method.Results)}} {{if eq $resultsLength 1}} func (c *{{$decorator}}) {{$method.Declaration}} { op := func(ctx context.Context) error { return c.client.{{$method.Call}} } return c.throttleRetry.Do(ctx, op) } {{else}} func (c *{{$decorator}}) {{$method.Declaration}} { var resp {{ (index $method.Results 0).Type }} op := func(ctx context.Context) error { var err error resp, err = c.client.{{$method.Call}} return err } err = c.throttleRetry.Do(ctx, op) return resp, err } {{end}} {{end}} ================================================ FILE: client/templates/thrift.tmpl ================================================ {{$prefix := (index .Vars "prefix")}} import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) {{$unsupportedMethods := list "CountDLQMessages" "UpdateTaskListPartitionConfig" "RefreshTaskListPartitionConfig"}} {{$interfaceName := .Interface.Name}} {{$clientName := (index .Vars "client")}} {{ $decorator := (printf "%s%s" (down $clientName) .Interface.Name) }} {{range $method := .Interface.Methods}} {{$Request := printf "%sRequest" $method.Name}} {{$Response := printf "%sResponse" $method.Name}} func (g {{$decorator}}) {{$method.Declaration}} { {{- if has $method.Name $unsupportedMethods}} return nil, thrift.ToError(&types.BadRequestError{Message: "Feature not supported on TChannel"}) {{- else if or (eq $method.Name "AddDecisionTask") (eq $method.Name "AddActivityTask")}} {{(index $method.Results 1).Name}} = g.c.{{$method.Name}}({{(index $method.Params 0).Name}}, thrift.From{{$prefix}}{{$Request}}({{(index $method.Params 1).Name}}), {{(index $method.Params 2).Pass}}) if {{(index $method.Results 1).Name}} != nil { return nil, {{(index $method.Results 1).Name}} } return &types.{{$method.Name}}Response{}, nil {{- else}} {{- if eq (len $method.Params) 2}} {{- if eq (len $method.Results) 1}} {{(index $method.Results 0).Name}} = g.c.{{$method.Call}} {{- else}} response, {{(index $method.Results 1).Name}} := g.c.{{$method.Call}} {{- end}} {{- else}} {{- if eq (len $method.Results) 1}} {{(index $method.Results 0).Name}} = g.c.{{$method.Name}}({{(index $method.Params 0).Name}}, thrift.From{{$prefix}}{{$Request}}({{(index $method.Params 1).Name}}), {{(index $method.Params 2).Pass}}) {{- else}} response, {{(index $method.Results 1).Name}} := g.c.{{$method.Name}}({{(index $method.Params 0).Name}}, thrift.From{{$prefix}}{{$Request}}({{(index $method.Params 1).Name}}), {{(index $method.Params 2).Pass}}) {{- end}} {{- end}} {{- if eq (len $method.Results) 1}} return thrift.ToError({{(index $method.Results 0).Name}}) {{- else}} return thrift.To{{$prefix}}{{$Response}}(response), thrift.ToError({{(index $method.Results 1).Name}}) {{- end}} {{- end}} } {{end}} ================================================ FILE: client/templates/timeout.tmpl ================================================ {{$ClientName := (index .Vars "client")}} {{$clientName := (down $ClientName)}} {{$interfaceName := .Interface.Name}} {{ $decorator := (printf "%s%s" (down $clientName) .Interface.Name) }} {{ $Decorator := (printf "%s%s" $ClientName .Interface.Name) }} {{$largeTimeoutAPIs := list "adminClient.GetCrossClusterTasks" "adminClient.GetReplicationMessages"}} {{$longPollTimeoutAPIs := list "frontendClient.ListArchivedWorkflowExecutions" "frontendClient.PollForActivityTask" "frontendClient.PollForDecisionTask" "matchingClient.PollForActivityTask" "matchingClient.PollForDecisionTask"}} {{$noTimeoutAPIs := list "historyClient.GetReplicationMessages" "historyClient.GetDLQReplicationMessages" "historyClient.CountDLQMessages" "historyClient.ReadDLQMessages" "historyClient.PurgeDLQMessages" "historyClient.MergeDLQMessages" "historyClient.GetCrossClusterTasks" "historyClient.GetFailoverInfo" "matchingClient.GetTaskListsByDomain" "sharddistributorClient.WatchNamespaceState"}} {{/* $fieldMap defines a map of the decorator struct fields with field name as the key and field type as the value */}} {{$fieldMap := dict }} {{ if has $ClientName (list "History" "ShardDistributor" "ShardDistributorExecutor") }} {{$fieldMap = merge $fieldMap (dict "timeout" "time.Duration" "client" .Interface.Type) }} {{ else if eq $ClientName "Admin" }} {{$fieldMap = merge $fieldMap (dict "timeout" "time.Duration" "client" .Interface.Type "largeTimeout" "time.Duration") }} {{ else }} {{$fieldMap = merge $fieldMap (dict "timeout" "time.Duration" "client" .Interface.Type "longPollTimeout" "time.Duration") }} {{ end }} import ( "context" "time" "go.uber.org/yarpc" "github.com/uber/cadence/client/{{$clientName}}" "github.com/uber/cadence/common/future" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) var _ {{.Interface.Type}} = (*{{$decorator}})(nil) // {{$decorator}} implements the {{.Interface.Type}} interface instrumented with timeouts type {{$decorator}} struct { {{ range $fieldName, $fieldType := $fieldMap -}} {{$fieldName}} {{$fieldType}} {{ end }} } // New{{$Decorator}} creates a new {{$decorator}} instance func New{{$Decorator}}( {{- range $fieldName, $fieldType := $fieldMap}} {{$fieldName}} {{$fieldType}}, {{- end }} ) {{.Interface.Type}} { return &{{$decorator}}{ {{- range $fieldName, $fieldType := $fieldMap}} {{$fieldName}}: {{$fieldName}}, {{- end }} } } {{range $method := .Interface.Methods}} func (c * {{$decorator}}) {{$method.Declaration}} { {{- if has (printf "%s.%s" $decorator $method.Name) $largeTimeoutAPIs -}} ctx, cancel := createContext(ctx, c.largeTimeout) defer cancel() {{- else if has (printf "%s.%s" $decorator $method.Name) $longPollTimeoutAPIs -}} ctx, cancel := createContext(ctx, c.longPollTimeout) defer cancel() {{- else if not (has (printf "%s.%s" $decorator $method.Name) $noTimeoutAPIs) -}} ctx, cancel := createContext(ctx, c.timeout) defer cancel() {{- end }} {{$method.Pass ("c.client.") }} } {{end}} ================================================ FILE: client/wrappers/errorinjectors/admin_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/errorinjectors.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) const ( msgAdminInjectedFakeErr = "Injected fake admin client error" ) // adminClient implements admin.Client interface instrumented with retries type adminClient struct { client admin.Client errorRate float64 logger log.Logger fakeErrFn func(float64) error forwardCallFn func(error) bool } // NewAdminClient creates a new instance of adminClient that injects error into every call with a given rate. func NewAdminClient(client admin.Client, errorRate float64, logger log.Logger) admin.Client { return &adminClient{ client: client, errorRate: errorRate, logger: logger, fakeErrFn: errors.GenerateFakeError, forwardCallFn: errors.ShouldForwardCall, } } func (c *adminClient) AddSearchAttribute(ctx context.Context, ap1 *types.AddSearchAttributeRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.AddSearchAttribute(ctx, ap1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationAddSearchAttribute, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.CloseShard(ctx, cp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationCloseShard, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (cp2 *types.CountDLQMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { cp2, err = c.client.CountDLQMessages(ctx, cp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationCountDLQMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) DeleteWorkflow(ctx context.Context, ap1 *types.AdminDeleteWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDeleteWorkflowResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { ap2, err = c.client.DeleteWorkflow(ctx, ap1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationDeleteWorkflow, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) DescribeCluster(ctx context.Context, p1 ...yarpc.CallOption) (dp1 *types.DescribeClusterResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp1, err = c.client.DescribeCluster(ctx, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationDescribeCluster, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp2, err = c.client.DescribeHistoryHost(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationDescribeHistoryHost, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp2, err = c.client.DescribeQueue(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationDescribeQueue, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) DescribeShardDistribution(ctx context.Context, dp1 *types.DescribeShardDistributionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeShardDistributionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp2, err = c.client.DescribeShardDistribution(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationDescribeShardDistribution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) DescribeWorkflowExecution(ctx context.Context, ap1 *types.AdminDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDescribeWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { ap2, err = c.client.DescribeWorkflowExecution(ctx, ap1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationDescribeWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetDLQReplicationMessages(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationGetDLQReplicationMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainAsyncWorkflowConfiguratonResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp1, err = c.client.GetDomainAsyncWorkflowConfiguraton(ctx, request, opts...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationGetDomainAsyncWorkflowConfiguraton, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainIsolationGroupsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp1, err = c.client.GetDomainIsolationGroups(ctx, request, opts...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationGetDomainIsolationGroups, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) GetDomainReplicationMessages(ctx context.Context, gp1 *types.GetDomainReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDomainReplicationMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetDomainReplicationMessages(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationGetDomainReplicationMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) GetDynamicConfig(ctx context.Context, gp1 *types.GetDynamicConfigRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDynamicConfigResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetDynamicConfig(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationGetDynamicConfig, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetGlobalIsolationGroupsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp1, err = c.client.GetGlobalIsolationGroups(ctx, request, opts...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationGetGlobalIsolationGroups, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetReplicationMessages(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationGetReplicationMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) GetWorkflowExecutionRawHistoryV2(ctx context.Context, gp1 *types.GetWorkflowExecutionRawHistoryV2Request, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionRawHistoryV2Response, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetWorkflowExecutionRawHistoryV2(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationGetWorkflowExecutionRawHistoryV2, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) ListDynamicConfig(ctx context.Context, lp1 *types.ListDynamicConfigRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDynamicConfigResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { lp2, err = c.client.ListDynamicConfig(ctx, lp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationListDynamicConfig, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) MaintainCorruptWorkflow(ctx context.Context, ap1 *types.AdminMaintainWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminMaintainWorkflowResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { ap2, err = c.client.MaintainCorruptWorkflow(ctx, ap1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationMaintainCorruptWorkflow, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { mp2, err = c.client.MergeDLQMessages(ctx, mp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationMergeDLQMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.PurgeDLQMessages(ctx, pp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationPurgeDLQMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.ReadDLQMessages(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationReadDLQMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) ReapplyEvents(ctx context.Context, rp1 *types.ReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.ReapplyEvents(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationReapplyEvents, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RefreshWorkflowTasks(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationRefreshWorkflowTasks, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RemoveTask(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationRemoveTask, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) ResendReplicationTasks(ctx context.Context, rp1 *types.ResendReplicationTasksRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.ResendReplicationTasks(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationResendReplicationTasks, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.ResetQueue(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationResetQueue, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) RestoreDynamicConfig(ctx context.Context, rp1 *types.RestoreDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RestoreDynamicConfig(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationRestoreDynamicConfig, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { up1, err = c.client.UpdateDomainAsyncWorkflowConfiguraton(ctx, request, opts...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationUpdateDomainAsyncWorkflowConfiguraton, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainIsolationGroupsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { up1, err = c.client.UpdateDomainIsolationGroups(ctx, request, opts...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationUpdateDomainIsolationGroups, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) UpdateDynamicConfig(ctx context.Context, up1 *types.UpdateDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.UpdateDynamicConfig(ctx, up1, p1...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationUpdateDynamicConfig, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateGlobalIsolationGroupsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { up1, err = c.client.UpdateGlobalIsolationGroups(ctx, request, opts...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationUpdateGlobalIsolationGroups, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *adminClient) UpdateTaskListPartitionConfig(ctx context.Context, request *types.UpdateTaskListPartitionConfigRequest, opts ...yarpc.CallOption) (up1 *types.UpdateTaskListPartitionConfigResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { up1, err = c.client.UpdateTaskListPartitionConfig(ctx, request, opts...) } if fakeErr != nil { c.logger.Error(msgAdminInjectedFakeErr, tag.AdminClientOperationUpdateTaskListPartitionConfig, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } ================================================ FILE: client/wrappers/errorinjectors/errorinjectors_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package errorinjectors import ( "context" stdErrors "errors" "fmt" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) func TestInjector(t *testing.T) { // All wrappers follow the same logic, so in this test we only test one of them. t.Run("no error is forwarded", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := admin.NewMockClient(ctrl) clientMock.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Times(1) injector := NewAdminClient(clientMock, 1, testlogger.New(t)).(*adminClient) injector.fakeErrFn = func(float64) error { return nil } injector.forwardCallFn = func(err error) bool { return err == nil } _, err := injector.DescribeWorkflowExecution(context.Background(), &types.AdminDescribeWorkflowExecutionRequest{}) assert.NoError(t, err) }) t.Run("no fake error, but client returns an error", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := admin.NewMockClient(ctrl) clientErr := fmt.Errorf("fail") clientMock.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, clientErr).Times(1) injector := NewAdminClient(clientMock, 1, testlogger.New(t)).(*adminClient) injector.fakeErrFn = func(float64) error { return nil } injector.forwardCallFn = func(err error) bool { return err == nil } _, err := injector.DescribeWorkflowExecution(context.Background(), &types.AdminDescribeWorkflowExecutionRequest{}) assert.Error(t, err) assert.True(t, stdErrors.Is(clientErr, err)) }) t.Run("fake error overrides client error", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := admin.NewMockClient(ctrl) clientErr := fmt.Errorf("fail") clientMock.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, clientErr).Times(1) injector := NewAdminClient(clientMock, 1, testlogger.New(t)).(*adminClient) injector.fakeErrFn = func(float64) error { return errors.ErrFakeServiceBusy } injector.forwardCallFn = func(err error) bool { return true } _, err := injector.DescribeWorkflowExecution(context.Background(), &types.AdminDescribeWorkflowExecutionRequest{}) assert.Error(t, err) assert.True(t, stdErrors.Is(errors.ErrFakeServiceBusy, err)) }) t.Run("failed but not forwarded", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := admin.NewMockClient(ctrl) clientMock.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Times(1) injector := NewAdminClient(clientMock, 1, testlogger.New(t)).(*adminClient) injector.fakeErrFn = func(float64) error { return errors.ErrFakeServiceBusy } injector.forwardCallFn = func(err error) bool { return true } _, err := injector.DescribeWorkflowExecution(context.Background(), &types.AdminDescribeWorkflowExecutionRequest{}) assert.Error(t, err) }) t.Run("failed and not forwarded", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := admin.NewMockClient(ctrl) injector := NewAdminClient(clientMock, 1, testlogger.New(t)).(*adminClient) injector.fakeErrFn = func(float64) error { return errors.ErrFakeServiceBusy } injector.forwardCallFn = func(err error) bool { return false } _, err := injector.DescribeWorkflowExecution(context.Background(), &types.AdminDescribeWorkflowExecutionRequest{}) assert.Error(t, err) }) } ================================================ FILE: client/wrappers/errorinjectors/frontend_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/errorinjectors.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) const ( msgFrontendInjectedFakeErr = "Injected fake frontend client error" ) // frontendClient implements frontend.Client interface instrumented with retries type frontendClient struct { client frontend.Client errorRate float64 logger log.Logger fakeErrFn func(float64) error forwardCallFn func(error) bool } // NewFrontendClient creates a new instance of frontendClient that injects error into every call with a given rate. func NewFrontendClient(client frontend.Client, errorRate float64, logger log.Logger) frontend.Client { return &frontendClient{ client: client, errorRate: errorRate, logger: logger, fakeErrFn: errors.GenerateFakeError, forwardCallFn: errors.ShouldForwardCall, } } func (c *frontendClient) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (cp2 *types.CountWorkflowExecutionsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { cp2, err = c.client.CountWorkflowExecutions(ctx, cp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationCountWorkflowExecutions, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.DeleteDomain(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationDeleteDomain, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.DeprecateDomain(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationDeprecateDomain, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeDomainResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp2, err = c.client.DescribeDomain(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationDescribeDomain, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeTaskListResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp2, err = c.client.DescribeTaskList(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationDescribeTaskList, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp2, err = c.client.DescribeWorkflowExecution(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationDescribeWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp2, err = c.client.DiagnoseWorkflowExecution(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationDiagnoseWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest, p1 ...yarpc.CallOption) (fp2 *types.FailoverDomainResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { fp2, err = c.client.FailoverDomain(ctx, fp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationFailoverDomain, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) GetClusterInfo(ctx context.Context, p1 ...yarpc.CallOption) (cp1 *types.ClusterInfo, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { cp1, err = c.client.GetClusterInfo(ctx, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationGetClusterInfo, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) GetSearchAttributes(ctx context.Context, p1 ...yarpc.CallOption) (gp1 *types.GetSearchAttributesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp1, err = c.client.GetSearchAttributes(ctx, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationGetSearchAttributes, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetTaskListsByDomain(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationGetTaskListsByDomain, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetWorkflowExecutionHistory(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationGetWorkflowExecutionHistory, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { lp2, err = c.client.ListArchivedWorkflowExecutions(ctx, lp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationListArchivedWorkflowExecutions, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { lp2, err = c.client.ListClosedWorkflowExecutions(ctx, lp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationListClosedWorkflowExecutions, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDomainsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { lp2, err = c.client.ListDomains(ctx, lp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationListDomains, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest, p1 ...yarpc.CallOption) (lp2 *types.ListFailoverHistoryResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { lp2, err = c.client.ListFailoverHistory(ctx, lp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationListFailoverHistory, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { lp2, err = c.client.ListOpenWorkflowExecutions(ctx, lp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationListOpenWorkflowExecutions, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListTaskListPartitionsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { lp2, err = c.client.ListTaskListPartitions(ctx, lp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationListTaskListPartitions, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { lp2, err = c.client.ListWorkflowExecutions(ctx, lp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationListWorkflowExecutions, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForActivityTaskResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { pp2, err = c.client.PollForActivityTask(ctx, pp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationPollForActivityTask, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForDecisionTaskResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { pp2, err = c.client.PollForDecisionTask(ctx, pp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationPollForDecisionTask, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest, p1 ...yarpc.CallOption) (qp2 *types.QueryWorkflowResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { qp2, err = c.client.QueryWorkflow(ctx, qp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationQueryWorkflow, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.RecordActivityTaskHeartbeat(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRecordActivityTaskHeartbeat, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.RecordActivityTaskHeartbeatByID(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRecordActivityTaskHeartbeatByID, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RefreshWorkflowTasks(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRefreshWorkflowTasks, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RegisterDomain(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRegisterDomain, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RequestCancelWorkflowExecution(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRequestCancelWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetStickyTaskListResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.ResetStickyTaskList(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationResetStickyTaskList, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.ResetWorkflowExecution(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationResetWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondActivityTaskCanceled(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRespondActivityTaskCanceled, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondActivityTaskCanceledByID(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRespondActivityTaskCanceledByID, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondActivityTaskCompleted(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRespondActivityTaskCompleted, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondActivityTaskCompletedByID(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRespondActivityTaskCompletedByID, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondActivityTaskFailed(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRespondActivityTaskFailed, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondActivityTaskFailedByID(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRespondActivityTaskFailedByID, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.RespondDecisionTaskCompleted(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRespondDecisionTaskCompleted, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondDecisionTaskFailed(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRespondDecisionTaskFailed, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondQueryTaskCompleted(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRespondQueryTaskCompleted, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.RestartWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.RestartWorkflowExecution(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationRestartWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { lp2, err = c.client.ScanWorkflowExecutions(ctx, lp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationScanWorkflowExecutions, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { sp2, err = c.client.SignalWithStartWorkflowExecution(ctx, sp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationSignalWithStartWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { sp2, err = c.client.SignalWithStartWorkflowExecutionAsync(ctx, sp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationSignalWithStartWorkflowExecutionAsync, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.SignalWorkflowExecution(ctx, sp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationSignalWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { sp2, err = c.client.StartWorkflowExecution(ctx, sp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationStartWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { sp2, err = c.client.StartWorkflowExecutionAsync(ctx, sp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationStartWorkflowExecutionAsync, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.TerminateWorkflowExecution(ctx, tp1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationTerminateWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *frontendClient) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest, p1 ...yarpc.CallOption) (up2 *types.UpdateDomainResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { up2, err = c.client.UpdateDomain(ctx, up1, p1...) } if fakeErr != nil { c.logger.Error(msgFrontendInjectedFakeErr, tag.FrontendClientOperationUpdateDomain, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } ================================================ FILE: client/wrappers/errorinjectors/history_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/errorinjectors.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) const ( msgHistoryInjectedFakeErr = "Injected fake history client error" ) // historyClient implements history.Client interface instrumented with retries type historyClient struct { client history.Client errorRate float64 logger log.Logger fakeErrFn func(float64) error forwardCallFn func(error) bool } // NewHistoryClient creates a new instance of historyClient that injects error into every call with a given rate. func NewHistoryClient(client history.Client, errorRate float64, logger log.Logger) history.Client { return &historyClient{ client: client, errorRate: errorRate, logger: logger, fakeErrFn: errors.GenerateFakeError, forwardCallFn: errors.ShouldForwardCall, } } func (c *historyClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.CloseShard(ctx, cp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationCloseShard, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (hp1 *types.HistoryCountDLQMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { hp1, err = c.client.CountDLQMessages(ctx, cp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationCountDLQMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp2, err = c.client.DescribeHistoryHost(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationDescribeHistoryHost, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) DescribeMutableState(ctx context.Context, dp1 *types.DescribeMutableStateRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeMutableStateResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp2, err = c.client.DescribeMutableState(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationDescribeMutableState, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp2, err = c.client.DescribeQueue(ctx, dp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationDescribeQueue, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) DescribeWorkflowExecution(ctx context.Context, hp1 *types.HistoryDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp1, err = c.client.DescribeWorkflowExecution(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationDescribeWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) GetCrossClusterTasks(ctx context.Context, gp1 *types.GetCrossClusterTasksRequest, p1 ...yarpc.CallOption) (gp2 *types.GetCrossClusterTasksResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetCrossClusterTasks(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationGetCrossClusterTasks, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetDLQReplicationMessages(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationGetDLQReplicationMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) GetFailoverInfo(ctx context.Context, gp1 *types.GetFailoverInfoRequest, p1 ...yarpc.CallOption) (gp2 *types.GetFailoverInfoResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetFailoverInfo(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationGetFailoverInfo, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) GetMutableState(ctx context.Context, gp1 *types.GetMutableStateRequest, p1 ...yarpc.CallOption) (gp2 *types.GetMutableStateResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetMutableState(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationGetMutableState, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetReplicationMessages(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationGetReplicationMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { mp2, err = c.client.MergeDLQMessages(ctx, mp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationMergeDLQMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) NotifyFailoverMarkers(ctx context.Context, np1 *types.NotifyFailoverMarkersRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.NotifyFailoverMarkers(ctx, np1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationNotifyFailoverMarkers, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) PollMutableState(ctx context.Context, pp1 *types.PollMutableStateRequest, p1 ...yarpc.CallOption) (pp2 *types.PollMutableStateResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { pp2, err = c.client.PollMutableState(ctx, pp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationPollMutableState, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.PurgeDLQMessages(ctx, pp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationPurgeDLQMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) QueryWorkflow(ctx context.Context, hp1 *types.HistoryQueryWorkflowRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryQueryWorkflowResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { hp2, err = c.client.QueryWorkflow(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationQueryWorkflow, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RatelimitUpdate(ctx context.Context, request *types.RatelimitUpdateRequest, opts ...yarpc.CallOption) (rp1 *types.RatelimitUpdateResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp1, err = c.client.RatelimitUpdate(ctx, request, opts...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRatelimitUpdate, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.ReadDLQMessages(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationReadDLQMessages, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) ReapplyEvents(ctx context.Context, hp1 *types.HistoryReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.ReapplyEvents(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationReapplyEvents, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RecordActivityTaskHeartbeat(ctx context.Context, hp1 *types.HistoryRecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp1 *types.RecordActivityTaskHeartbeatResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp1, err = c.client.RecordActivityTaskHeartbeat(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRecordActivityTaskHeartbeat, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RecordActivityTaskStarted(ctx context.Context, rp1 *types.RecordActivityTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskStartedResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.RecordActivityTaskStarted(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRecordActivityTaskStarted, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RecordChildExecutionCompleted(ctx context.Context, rp1 *types.RecordChildExecutionCompletedRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RecordChildExecutionCompleted(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRecordChildExecutionCompleted, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RecordDecisionTaskStarted(ctx context.Context, rp1 *types.RecordDecisionTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordDecisionTaskStartedResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.RecordDecisionTaskStarted(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRecordDecisionTaskStarted, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RefreshWorkflowTasks(ctx context.Context, hp1 *types.HistoryRefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RefreshWorkflowTasks(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRefreshWorkflowTasks, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RemoveSignalMutableState(ctx context.Context, rp1 *types.RemoveSignalMutableStateRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RemoveSignalMutableState(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRemoveSignalMutableState, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RemoveTask(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRemoveTask, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) ReplicateEventsV2(ctx context.Context, rp1 *types.ReplicateEventsV2Request, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.ReplicateEventsV2(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationReplicateEventsV2, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RequestCancelWorkflowExecution(ctx context.Context, hp1 *types.HistoryRequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RequestCancelWorkflowExecution(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRequestCancelWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.ResetQueue(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationResetQueue, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) ResetStickyTaskList(ctx context.Context, hp1 *types.HistoryResetStickyTaskListRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryResetStickyTaskListResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { hp2, err = c.client.ResetStickyTaskList(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationResetStickyTaskList, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) ResetWorkflowExecution(ctx context.Context, hp1 *types.HistoryResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp1 *types.ResetWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp1, err = c.client.ResetWorkflowExecution(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationResetWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RespondActivityTaskCanceled(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondActivityTaskCanceled(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRespondActivityTaskCanceled, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RespondActivityTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondActivityTaskCompleted(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRespondActivityTaskCompleted, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RespondActivityTaskFailed(ctx context.Context, hp1 *types.HistoryRespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondActivityTaskFailed(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRespondActivityTaskFailed, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RespondCrossClusterTasksCompleted(ctx context.Context, rp1 *types.RespondCrossClusterTasksCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondCrossClusterTasksCompletedResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { rp2, err = c.client.RespondCrossClusterTasksCompleted(ctx, rp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRespondCrossClusterTasksCompleted, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RespondDecisionTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryRespondDecisionTaskCompletedResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { hp2, err = c.client.RespondDecisionTaskCompleted(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRespondDecisionTaskCompleted, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) RespondDecisionTaskFailed(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondDecisionTaskFailed(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationRespondDecisionTaskFailed, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) ScheduleDecisionTask(ctx context.Context, sp1 *types.ScheduleDecisionTaskRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.ScheduleDecisionTask(ctx, sp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationScheduleDecisionTask, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) SignalWithStartWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { sp1, err = c.client.SignalWithStartWorkflowExecution(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationSignalWithStartWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) SignalWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.SignalWorkflowExecution(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationSignalWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) StartWorkflowExecution(ctx context.Context, hp1 *types.HistoryStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { sp1, err = c.client.StartWorkflowExecution(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationStartWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) SyncActivity(ctx context.Context, sp1 *types.SyncActivityRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.SyncActivity(ctx, sp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationSyncActivity, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) SyncShardStatus(ctx context.Context, sp1 *types.SyncShardStatusRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.SyncShardStatus(ctx, sp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationSyncShardStatus, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *historyClient) TerminateWorkflowExecution(ctx context.Context, hp1 *types.HistoryTerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.TerminateWorkflowExecution(ctx, hp1, p1...) } if fakeErr != nil { c.logger.Error(msgHistoryInjectedFakeErr, tag.HistoryClientOperationTerminateWorkflowExecution, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } ================================================ FILE: client/wrappers/errorinjectors/matching_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/errorinjectors.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) const ( msgMatchingInjectedFakeErr = "Injected fake matching client error" ) // matchingClient implements matching.Client interface instrumented with retries type matchingClient struct { client matching.Client errorRate float64 logger log.Logger fakeErrFn func(float64) error forwardCallFn func(error) bool } // NewMatchingClient creates a new instance of matchingClient that injects error into every call with a given rate. func NewMatchingClient(client matching.Client, errorRate float64, logger log.Logger) matching.Client { return &matchingClient{ client: client, errorRate: errorRate, logger: logger, fakeErrFn: errors.GenerateFakeError, forwardCallFn: errors.ShouldForwardCall, } } func (c *matchingClient) AddActivityTask(ctx context.Context, ap1 *types.AddActivityTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddActivityTaskResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { ap2, err = c.client.AddActivityTask(ctx, ap1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationAddActivityTask, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) AddDecisionTask(ctx context.Context, ap1 *types.AddDecisionTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddDecisionTaskResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { ap2, err = c.client.AddDecisionTask(ctx, ap1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationAddDecisionTask, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) CancelOutstandingPoll(ctx context.Context, cp1 *types.CancelOutstandingPollRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.CancelOutstandingPoll(ctx, cp1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationCancelOutstandingPoll, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) DescribeTaskList(ctx context.Context, mp1 *types.MatchingDescribeTaskListRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeTaskListResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { dp1, err = c.client.DescribeTaskList(ctx, mp1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationDescribeTaskList, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetTaskListsByDomain(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationGetTaskListsByDomain, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) ListTaskListPartitions(ctx context.Context, mp1 *types.MatchingListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp1 *types.ListTaskListPartitionsResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { lp1, err = c.client.ListTaskListPartitions(ctx, mp1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationListTaskListPartitions, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) PollForActivityTask(ctx context.Context, mp1 *types.MatchingPollForActivityTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForActivityTaskResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { mp2, err = c.client.PollForActivityTask(ctx, mp1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationPollForActivityTask, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) PollForDecisionTask(ctx context.Context, mp1 *types.MatchingPollForDecisionTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForDecisionTaskResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { mp2, err = c.client.PollForDecisionTask(ctx, mp1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationPollForDecisionTask, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQueryWorkflowRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingQueryWorkflowResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { mp2, err = c.client.QueryWorkflow(ctx, mp1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationQueryWorkflow, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { mp2, err = c.client.RefreshTaskListPartitionConfig(ctx, mp1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationRefreshTaskListPartitionConfig, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { err = c.client.RespondQueryTaskCompleted(ctx, mp1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationRespondQueryTaskCompleted, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { mp2, err = c.client.UpdateTaskListPartitionConfig(ctx, mp1, p1...) } if fakeErr != nil { c.logger.Error(msgMatchingInjectedFakeErr, tag.MatchingClientOperationUpdateTaskListPartitionConfig, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } ================================================ FILE: client/wrappers/errorinjectors/sharddistributor_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/errorinjectors.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) const ( msgShardDistributorInjectedFakeErr = "Injected fake sharddistributor client error" ) // sharddistributorClient implements sharddistributor.Client interface instrumented with retries type sharddistributorClient struct { client sharddistributor.Client errorRate float64 logger log.Logger fakeErrFn func(float64) error forwardCallFn func(error) bool } // NewShardDistributorClient creates a new instance of sharddistributorClient that injects error into every call with a given rate. func NewShardDistributorClient(client sharddistributor.Client, errorRate float64, logger log.Logger) sharddistributor.Client { return &sharddistributorClient{ client: client, errorRate: errorRate, logger: logger, fakeErrFn: errors.GenerateFakeError, forwardCallFn: errors.ShouldForwardCall, } } func (c *sharddistributorClient) GetShardOwner(ctx context.Context, gp1 *types.GetShardOwnerRequest, p1 ...yarpc.CallOption) (gp2 *types.GetShardOwnerResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { gp2, err = c.client.GetShardOwner(ctx, gp1, p1...) } if fakeErr != nil { c.logger.Error(msgShardDistributorInjectedFakeErr, tag.ShardDistributorClientOperationGetShardOwner, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } func (c *sharddistributorClient) WatchNamespaceState(ctx context.Context, wp1 *types.WatchNamespaceStateRequest, p1 ...yarpc.CallOption) (w1 sharddistributor.WatchNamespaceStateClient, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { w1, err = c.client.WatchNamespaceState(ctx, wp1, p1...) } if fakeErr != nil { c.logger.Error(msgShardDistributorInjectedFakeErr, tag.ShardDistributorClientOperationWatchNamespaceState, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } ================================================ FILE: client/wrappers/errorinjectors/sharddistributorexecutor_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/errorinjectors.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributorexecutor" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) const ( msgShardDistributorExecutorInjectedFakeErr = "Injected fake sharddistributorexecutor client error" ) // sharddistributorexecutorClient implements sharddistributorexecutor.Client interface instrumented with retries type sharddistributorexecutorClient struct { client sharddistributorexecutor.Client errorRate float64 logger log.Logger fakeErrFn func(float64) error forwardCallFn func(error) bool } // NewShardDistributorExecutorClient creates a new instance of sharddistributorexecutorClient that injects error into every call with a given rate. func NewShardDistributorExecutorClient(client sharddistributorexecutor.Client, errorRate float64, logger log.Logger) sharddistributorexecutor.Client { return &sharddistributorexecutorClient{ client: client, errorRate: errorRate, logger: logger, fakeErrFn: errors.GenerateFakeError, forwardCallFn: errors.ShouldForwardCall, } } func (c *sharddistributorexecutorClient) Heartbeat(ctx context.Context, ep1 *types.ExecutorHeartbeatRequest, p1 ...yarpc.CallOption) (ep2 *types.ExecutorHeartbeatResponse, err error) { fakeErr := c.fakeErrFn(c.errorRate) var forwardCall bool if forwardCall = c.forwardCallFn(fakeErr); forwardCall { ep2, err = c.client.Heartbeat(ctx, ep1, p1...) } if fakeErr != nil { c.logger.Error(msgShardDistributorExecutorInjectedFakeErr, tag.ShardDistributorExecutorClientOperationHeartbeat, tag.Error(fakeErr), tag.Bool(forwardCall), tag.ClientError(err), ) err = fakeErr return } return } ================================================ FILE: client/wrappers/grpc/admin_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" ) func (g adminClient) AddSearchAttribute(ctx context.Context, ap1 *types.AddSearchAttributeRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.AddSearchAttribute(ctx, proto.FromAdminAddSearchAttributeRequest(ap1), p1...) return proto.ToError(err) } func (g adminClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.CloseShard(ctx, proto.FromAdminCloseShardRequest(cp1), p1...) return proto.ToError(err) } func (g adminClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (cp2 *types.CountDLQMessagesResponse, err error) { response, err := g.c.CountDLQMessages(ctx, proto.FromAdminCountDLQMessagesRequest(cp1), p1...) return proto.ToAdminCountDLQMessagesResponse(response), proto.ToError(err) } func (g adminClient) DeleteWorkflow(ctx context.Context, ap1 *types.AdminDeleteWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDeleteWorkflowResponse, err error) { response, err := g.c.DeleteWorkflow(ctx, proto.FromAdminDeleteWorkflowRequest(ap1), p1...) return proto.ToAdminDeleteWorkflowResponse(response), proto.ToError(err) } func (g adminClient) DescribeCluster(ctx context.Context, p1 ...yarpc.CallOption) (dp1 *types.DescribeClusterResponse, err error) { response, err := g.c.DescribeCluster(ctx, &adminv1.DescribeClusterRequest{}, p1...) return proto.ToAdminDescribeClusterResponse(response), proto.ToError(err) } func (g adminClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { response, err := g.c.DescribeHistoryHost(ctx, proto.FromAdminDescribeHistoryHostRequest(dp1), p1...) return proto.ToAdminDescribeHistoryHostResponse(response), proto.ToError(err) } func (g adminClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { response, err := g.c.DescribeQueue(ctx, proto.FromAdminDescribeQueueRequest(dp1), p1...) return proto.ToAdminDescribeQueueResponse(response), proto.ToError(err) } func (g adminClient) DescribeShardDistribution(ctx context.Context, dp1 *types.DescribeShardDistributionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeShardDistributionResponse, err error) { response, err := g.c.DescribeShardDistribution(ctx, proto.FromAdminDescribeShardDistributionRequest(dp1), p1...) return proto.ToAdminDescribeShardDistributionResponse(response), proto.ToError(err) } func (g adminClient) DescribeWorkflowExecution(ctx context.Context, ap1 *types.AdminDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDescribeWorkflowExecutionResponse, err error) { response, err := g.c.DescribeWorkflowExecution(ctx, proto.FromAdminDescribeWorkflowExecutionRequest(ap1), p1...) return proto.ToAdminDescribeWorkflowExecutionResponse(response), proto.ToError(err) } func (g adminClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { response, err := g.c.GetDLQReplicationMessages(ctx, proto.FromAdminGetDLQReplicationMessagesRequest(gp1), p1...) return proto.ToAdminGetDLQReplicationMessagesResponse(response), proto.ToError(err) } func (g adminClient) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainAsyncWorkflowConfiguratonResponse, err error) { response, err := g.c.GetDomainAsyncWorkflowConfiguraton(ctx, proto.FromAdminGetDomainAsyncWorkflowConfiguratonRequest(request), opts...) return proto.ToAdminGetDomainAsyncWorkflowConfiguratonResponse(response), proto.ToError(err) } func (g adminClient) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainIsolationGroupsResponse, err error) { response, err := g.c.GetDomainIsolationGroups(ctx, proto.FromAdminGetDomainIsolationGroupsRequest(request), opts...) return proto.ToAdminGetDomainIsolationGroupsResponse(response), proto.ToError(err) } func (g adminClient) GetDomainReplicationMessages(ctx context.Context, gp1 *types.GetDomainReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDomainReplicationMessagesResponse, err error) { response, err := g.c.GetDomainReplicationMessages(ctx, proto.FromAdminGetDomainReplicationMessagesRequest(gp1), p1...) return proto.ToAdminGetDomainReplicationMessagesResponse(response), proto.ToError(err) } func (g adminClient) GetDynamicConfig(ctx context.Context, gp1 *types.GetDynamicConfigRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDynamicConfigResponse, err error) { response, err := g.c.GetDynamicConfig(ctx, proto.FromAdminGetDynamicConfigRequest(gp1), p1...) return proto.ToAdminGetDynamicConfigResponse(response), proto.ToError(err) } func (g adminClient) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetGlobalIsolationGroupsResponse, err error) { response, err := g.c.GetGlobalIsolationGroups(ctx, proto.FromAdminGetGlobalIsolationGroupsRequest(request), opts...) return proto.ToAdminGetGlobalIsolationGroupsResponse(response), proto.ToError(err) } func (g adminClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { response, err := g.c.GetReplicationMessages(ctx, proto.FromAdminGetReplicationMessagesRequest(gp1), p1...) return proto.ToAdminGetReplicationMessagesResponse(response), proto.ToError(err) } func (g adminClient) GetWorkflowExecutionRawHistoryV2(ctx context.Context, gp1 *types.GetWorkflowExecutionRawHistoryV2Request, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionRawHistoryV2Response, err error) { response, err := g.c.GetWorkflowExecutionRawHistoryV2(ctx, proto.FromAdminGetWorkflowExecutionRawHistoryV2Request(gp1), p1...) return proto.ToAdminGetWorkflowExecutionRawHistoryV2Response(response), proto.ToError(err) } func (g adminClient) ListDynamicConfig(ctx context.Context, lp1 *types.ListDynamicConfigRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDynamicConfigResponse, err error) { response, err := g.c.ListDynamicConfig(ctx, proto.FromAdminListDynamicConfigRequest(lp1), p1...) return proto.ToAdminListDynamicConfigResponse(response), proto.ToError(err) } func (g adminClient) MaintainCorruptWorkflow(ctx context.Context, ap1 *types.AdminMaintainWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminMaintainWorkflowResponse, err error) { response, err := g.c.MaintainCorruptWorkflow(ctx, proto.FromAdminMaintainCorruptWorkflowRequest(ap1), p1...) return proto.ToAdminMaintainCorruptWorkflowResponse(response), proto.ToError(err) } func (g adminClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { response, err := g.c.MergeDLQMessages(ctx, proto.FromAdminMergeDLQMessagesRequest(mp1), p1...) return proto.ToAdminMergeDLQMessagesResponse(response), proto.ToError(err) } func (g adminClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.PurgeDLQMessages(ctx, proto.FromAdminPurgeDLQMessagesRequest(pp1), p1...) return proto.ToError(err) } func (g adminClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { response, err := g.c.ReadDLQMessages(ctx, proto.FromAdminReadDLQMessagesRequest(rp1), p1...) return proto.ToAdminReadDLQMessagesResponse(response), proto.ToError(err) } func (g adminClient) ReapplyEvents(ctx context.Context, rp1 *types.ReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.ReapplyEvents(ctx, proto.FromAdminReapplyEventsRequest(rp1), p1...) return proto.ToError(err) } func (g adminClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RefreshWorkflowTasks(ctx, proto.FromAdminRefreshWorkflowTasksRequest(rp1), p1...) return proto.ToError(err) } func (g adminClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RemoveTask(ctx, proto.FromAdminRemoveTaskRequest(rp1), p1...) return proto.ToError(err) } func (g adminClient) ResendReplicationTasks(ctx context.Context, rp1 *types.ResendReplicationTasksRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.ResendReplicationTasks(ctx, proto.FromAdminResendReplicationTasksRequest(rp1), p1...) return proto.ToError(err) } func (g adminClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.ResetQueue(ctx, proto.FromAdminResetQueueRequest(rp1), p1...) return proto.ToError(err) } func (g adminClient) RestoreDynamicConfig(ctx context.Context, rp1 *types.RestoreDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RestoreDynamicConfig(ctx, proto.FromAdminRestoreDynamicConfigRequest(rp1), p1...) return proto.ToError(err) } func (g adminClient) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { response, err := g.c.UpdateDomainAsyncWorkflowConfiguraton(ctx, proto.FromAdminUpdateDomainAsyncWorkflowConfiguratonRequest(request), opts...) return proto.ToAdminUpdateDomainAsyncWorkflowConfiguratonResponse(response), proto.ToError(err) } func (g adminClient) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainIsolationGroupsResponse, err error) { response, err := g.c.UpdateDomainIsolationGroups(ctx, proto.FromAdminUpdateDomainIsolationGroupsRequest(request), opts...) return proto.ToAdminUpdateDomainIsolationGroupsResponse(response), proto.ToError(err) } func (g adminClient) UpdateDynamicConfig(ctx context.Context, up1 *types.UpdateDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.UpdateDynamicConfig(ctx, proto.FromAdminUpdateDynamicConfigRequest(up1), p1...) return proto.ToError(err) } func (g adminClient) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateGlobalIsolationGroupsResponse, err error) { response, err := g.c.UpdateGlobalIsolationGroups(ctx, proto.FromAdminUpdateGlobalIsolationGroupsRequest(request), opts...) return proto.ToAdminUpdateGlobalIsolationGroupsResponse(response), proto.ToError(err) } func (g adminClient) UpdateTaskListPartitionConfig(ctx context.Context, request *types.UpdateTaskListPartitionConfigRequest, opts ...yarpc.CallOption) (up1 *types.UpdateTaskListPartitionConfigResponse, err error) { response, err := g.c.UpdateTaskListPartitionConfig(ctx, proto.FromAdminUpdateTaskListPartitionConfigRequest(request), opts...) return proto.ToAdminUpdateTaskListPartitionConfigResponse(response), proto.ToError(err) } ================================================ FILE: client/wrappers/grpc/constructor.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package grpc import ( adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" historyv1 "github.com/uber/cadence/.gen/proto/history/v1" matchingv1 "github.com/uber/cadence/.gen/proto/matching/v1" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/client/sharddistributorexecutor" ) type ( adminClient struct { c adminv1.AdminAPIYARPCClient } frontendGRPCClientWrapper struct { apiv1.DomainAPIYARPCClient apiv1.WorkflowAPIYARPCClient apiv1.WorkerAPIYARPCClient apiv1.VisibilityAPIYARPCClient } frontendClient struct { c *frontendGRPCClientWrapper } historyClient struct { c historyv1.HistoryAPIYARPCClient } matchingClient struct { c matchingv1.MatchingAPIYARPCClient } sharddistributorClient struct { c sharddistributorv1.ShardDistributorAPIYARPCClient } sharddistributorexecutorClient struct { c sharddistributorv1.ShardDistributorExecutorAPIYARPCClient } ) func NewAdminClient(c adminv1.AdminAPIYARPCClient) admin.Client { return adminClient{c} } func NewFrontendClient( domain apiv1.DomainAPIYARPCClient, workflow apiv1.WorkflowAPIYARPCClient, worker apiv1.WorkerAPIYARPCClient, visibility apiv1.VisibilityAPIYARPCClient, ) frontend.Client { return frontendClient{&frontendGRPCClientWrapper{domain, workflow, worker, visibility}} } func NewHistoryClient(c historyv1.HistoryAPIYARPCClient) history.Client { return historyClient{c} } func NewMatchingClient(c matchingv1.MatchingAPIYARPCClient) matching.Client { return matchingClient{c} } func NewShardDistributorClient(c sharddistributorv1.ShardDistributorAPIYARPCClient) sharddistributor.Client { return sharddistributorClient{c} } func NewShardDistributorExecutorClient(c sharddistributorv1.ShardDistributorExecutorAPIYARPCClient) sharddistributorexecutor.Client { return sharddistributorexecutorClient{c} } ================================================ FILE: client/wrappers/grpc/frontend_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" ) func (g frontendClient) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (cp2 *types.CountWorkflowExecutionsResponse, err error) { response, err := g.c.CountWorkflowExecutions(ctx, proto.FromCountWorkflowExecutionsRequest(cp1), p1...) return proto.ToCountWorkflowExecutionsResponse(response), proto.ToError(err) } func (g frontendClient) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.DeleteDomain(ctx, proto.FromDeleteDomainRequest(dp1), p1...) return proto.ToError(err) } func (g frontendClient) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.DeprecateDomain(ctx, proto.FromDeprecateDomainRequest(dp1), p1...) return proto.ToError(err) } func (g frontendClient) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeDomainResponse, err error) { response, err := g.c.DescribeDomain(ctx, proto.FromDescribeDomainRequest(dp1), p1...) return proto.ToDescribeDomainResponse(response), proto.ToError(err) } func (g frontendClient) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeTaskListResponse, err error) { response, err := g.c.DescribeTaskList(ctx, proto.FromDescribeTaskListRequest(dp1), p1...) return proto.ToDescribeTaskListResponse(response), proto.ToError(err) } func (g frontendClient) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { response, err := g.c.DescribeWorkflowExecution(ctx, proto.FromDescribeWorkflowExecutionRequest(dp1), p1...) return proto.ToDescribeWorkflowExecutionResponse(response), proto.ToError(err) } func (g frontendClient) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { response, err := g.c.DiagnoseWorkflowExecution(ctx, proto.FromDiagnoseWorkflowExecutionRequest(dp1), p1...) return proto.ToDiagnoseWorkflowExecutionResponse(response), proto.ToError(err) } func (g frontendClient) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest, p1 ...yarpc.CallOption) (fp2 *types.FailoverDomainResponse, err error) { response, err := g.c.FailoverDomain(ctx, proto.FromFailoverDomainRequest(fp1), p1...) return proto.ToFailoverDomainResponse(response), proto.ToError(err) } func (g frontendClient) GetClusterInfo(ctx context.Context, p1 ...yarpc.CallOption) (cp1 *types.ClusterInfo, err error) { response, err := g.c.GetClusterInfo(ctx, &apiv1.GetClusterInfoRequest{}, p1...) return proto.ToGetClusterInfoResponse(response), proto.ToError(err) } func (g frontendClient) GetSearchAttributes(ctx context.Context, p1 ...yarpc.CallOption) (gp1 *types.GetSearchAttributesResponse, err error) { response, err := g.c.GetSearchAttributes(ctx, &apiv1.GetSearchAttributesRequest{}, p1...) return proto.ToGetSearchAttributesResponse(response), proto.ToError(err) } func (g frontendClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { response, err := g.c.GetTaskListsByDomain(ctx, proto.FromGetTaskListsByDomainRequest(gp1), p1...) return proto.ToGetTaskListsByDomainResponse(response), proto.ToError(err) } func (g frontendClient) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { response, err := g.c.GetWorkflowExecutionHistory(ctx, proto.FromGetWorkflowExecutionHistoryRequest(gp1), p1...) return proto.ToGetWorkflowExecutionHistoryResponse(response), proto.ToError(err) } func (g frontendClient) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { response, err := g.c.ListArchivedWorkflowExecutions(ctx, proto.FromListArchivedWorkflowExecutionsRequest(lp1), p1...) return proto.ToListArchivedWorkflowExecutionsResponse(response), proto.ToError(err) } func (g frontendClient) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { response, err := g.c.ListClosedWorkflowExecutions(ctx, proto.FromListClosedWorkflowExecutionsRequest(lp1), p1...) return proto.ToListClosedWorkflowExecutionsResponse(response), proto.ToError(err) } func (g frontendClient) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDomainsResponse, err error) { response, err := g.c.ListDomains(ctx, proto.FromListDomainsRequest(lp1), p1...) return proto.ToListDomainsResponse(response), proto.ToError(err) } func (g frontendClient) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest, p1 ...yarpc.CallOption) (lp2 *types.ListFailoverHistoryResponse, err error) { response, err := g.c.ListFailoverHistory(ctx, proto.FromListFailoverHistoryRequest(lp1), p1...) return proto.ToListFailoverHistoryResponse(response), proto.ToError(err) } func (g frontendClient) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { response, err := g.c.ListOpenWorkflowExecutions(ctx, proto.FromListOpenWorkflowExecutionsRequest(lp1), p1...) return proto.ToListOpenWorkflowExecutionsResponse(response), proto.ToError(err) } func (g frontendClient) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListTaskListPartitionsResponse, err error) { response, err := g.c.ListTaskListPartitions(ctx, proto.FromListTaskListPartitionsRequest(lp1), p1...) return proto.ToListTaskListPartitionsResponse(response), proto.ToError(err) } func (g frontendClient) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { response, err := g.c.ListWorkflowExecutions(ctx, proto.FromListWorkflowExecutionsRequest(lp1), p1...) return proto.ToListWorkflowExecutionsResponse(response), proto.ToError(err) } func (g frontendClient) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForActivityTaskResponse, err error) { response, err := g.c.PollForActivityTask(ctx, proto.FromPollForActivityTaskRequest(pp1), p1...) return proto.ToPollForActivityTaskResponse(response), proto.ToError(err) } func (g frontendClient) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForDecisionTaskResponse, err error) { response, err := g.c.PollForDecisionTask(ctx, proto.FromPollForDecisionTaskRequest(pp1), p1...) return proto.ToPollForDecisionTaskResponse(response), proto.ToError(err) } func (g frontendClient) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest, p1 ...yarpc.CallOption) (qp2 *types.QueryWorkflowResponse, err error) { response, err := g.c.QueryWorkflow(ctx, proto.FromQueryWorkflowRequest(qp1), p1...) return proto.ToQueryWorkflowResponse(response), proto.ToError(err) } func (g frontendClient) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { response, err := g.c.RecordActivityTaskHeartbeat(ctx, proto.FromRecordActivityTaskHeartbeatRequest(rp1), p1...) return proto.ToRecordActivityTaskHeartbeatResponse(response), proto.ToError(err) } func (g frontendClient) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { response, err := g.c.RecordActivityTaskHeartbeatByID(ctx, proto.FromRecordActivityTaskHeartbeatByIDRequest(rp1), p1...) return proto.ToRecordActivityTaskHeartbeatByIDResponse(response), proto.ToError(err) } func (g frontendClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RefreshWorkflowTasks(ctx, proto.FromRefreshWorkflowTasksRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RegisterDomain(ctx, proto.FromRegisterDomainRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RequestCancelWorkflowExecution(ctx, proto.FromRequestCancelWorkflowExecutionRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetStickyTaskListResponse, err error) { response, err := g.c.ResetStickyTaskList(ctx, proto.FromResetStickyTaskListRequest(rp1), p1...) return proto.ToResetStickyTaskListResponse(response), proto.ToError(err) } func (g frontendClient) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetWorkflowExecutionResponse, err error) { response, err := g.c.ResetWorkflowExecution(ctx, proto.FromResetWorkflowExecutionRequest(rp1), p1...) return proto.ToResetWorkflowExecutionResponse(response), proto.ToError(err) } func (g frontendClient) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondActivityTaskCanceled(ctx, proto.FromRespondActivityTaskCanceledRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondActivityTaskCanceledByID(ctx, proto.FromRespondActivityTaskCanceledByIDRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondActivityTaskCompleted(ctx, proto.FromRespondActivityTaskCompletedRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondActivityTaskCompletedByID(ctx, proto.FromRespondActivityTaskCompletedByIDRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondActivityTaskFailed(ctx, proto.FromRespondActivityTaskFailedRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondActivityTaskFailedByID(ctx, proto.FromRespondActivityTaskFailedByIDRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { response, err := g.c.RespondDecisionTaskCompleted(ctx, proto.FromRespondDecisionTaskCompletedRequest(rp1), p1...) return proto.ToRespondDecisionTaskCompletedResponse(response), proto.ToError(err) } func (g frontendClient) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondDecisionTaskFailed(ctx, proto.FromRespondDecisionTaskFailedRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondQueryTaskCompleted(ctx, proto.FromRespondQueryTaskCompletedRequest(rp1), p1...) return proto.ToError(err) } func (g frontendClient) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.RestartWorkflowExecutionResponse, err error) { response, err := g.c.RestartWorkflowExecution(ctx, proto.FromRestartWorkflowExecutionRequest(rp1), p1...) return proto.ToRestartWorkflowExecutionResponse(response), proto.ToError(err) } func (g frontendClient) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { response, err := g.c.ScanWorkflowExecutions(ctx, proto.FromScanWorkflowExecutionsRequest(lp1), p1...) return proto.ToScanWorkflowExecutionsResponse(response), proto.ToError(err) } func (g frontendClient) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { response, err := g.c.SignalWithStartWorkflowExecution(ctx, proto.FromSignalWithStartWorkflowExecutionRequest(sp1), p1...) return proto.ToSignalWithStartWorkflowExecutionResponse(response), proto.ToError(err) } func (g frontendClient) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { response, err := g.c.SignalWithStartWorkflowExecutionAsync(ctx, proto.FromSignalWithStartWorkflowExecutionAsyncRequest(sp1), p1...) return proto.ToSignalWithStartWorkflowExecutionAsyncResponse(response), proto.ToError(err) } func (g frontendClient) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.SignalWorkflowExecution(ctx, proto.FromSignalWorkflowExecutionRequest(sp1), p1...) return proto.ToError(err) } func (g frontendClient) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { response, err := g.c.StartWorkflowExecution(ctx, proto.FromStartWorkflowExecutionRequest(sp1), p1...) return proto.ToStartWorkflowExecutionResponse(response), proto.ToError(err) } func (g frontendClient) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { response, err := g.c.StartWorkflowExecutionAsync(ctx, proto.FromStartWorkflowExecutionAsyncRequest(sp1), p1...) return proto.ToStartWorkflowExecutionAsyncResponse(response), proto.ToError(err) } func (g frontendClient) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.TerminateWorkflowExecution(ctx, proto.FromTerminateWorkflowExecutionRequest(tp1), p1...) return proto.ToError(err) } func (g frontendClient) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest, p1 ...yarpc.CallOption) (up2 *types.UpdateDomainResponse, err error) { response, err := g.c.UpdateDomain(ctx, proto.FromUpdateDomainRequest(up1), p1...) return proto.ToUpdateDomainResponse(response), proto.ToError(err) } ================================================ FILE: client/wrappers/grpc/history_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" ) func (g historyClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.CloseShard(ctx, proto.FromHistoryCloseShardRequest(cp1), p1...) return proto.ToError(err) } func (g historyClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (hp1 *types.HistoryCountDLQMessagesResponse, err error) { response, err := g.c.CountDLQMessages(ctx, proto.FromHistoryCountDLQMessagesRequest(cp1), p1...) return proto.ToHistoryCountDLQMessagesResponse(response), proto.ToError(err) } func (g historyClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { response, err := g.c.DescribeHistoryHost(ctx, proto.FromHistoryDescribeHistoryHostRequest(dp1), p1...) return proto.ToHistoryDescribeHistoryHostResponse(response), proto.ToError(err) } func (g historyClient) DescribeMutableState(ctx context.Context, dp1 *types.DescribeMutableStateRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeMutableStateResponse, err error) { response, err := g.c.DescribeMutableState(ctx, proto.FromHistoryDescribeMutableStateRequest(dp1), p1...) return proto.ToHistoryDescribeMutableStateResponse(response), proto.ToError(err) } func (g historyClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { response, err := g.c.DescribeQueue(ctx, proto.FromHistoryDescribeQueueRequest(dp1), p1...) return proto.ToHistoryDescribeQueueResponse(response), proto.ToError(err) } func (g historyClient) DescribeWorkflowExecution(ctx context.Context, hp1 *types.HistoryDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeWorkflowExecutionResponse, err error) { response, err := g.c.DescribeWorkflowExecution(ctx, proto.FromHistoryDescribeWorkflowExecutionRequest(hp1), p1...) return proto.ToHistoryDescribeWorkflowExecutionResponse(response), proto.ToError(err) } func (g historyClient) GetCrossClusterTasks(ctx context.Context, gp1 *types.GetCrossClusterTasksRequest, p1 ...yarpc.CallOption) (gp2 *types.GetCrossClusterTasksResponse, err error) { response, err := g.c.GetCrossClusterTasks(ctx, proto.FromHistoryGetCrossClusterTasksRequest(gp1), p1...) return proto.ToHistoryGetCrossClusterTasksResponse(response), proto.ToError(err) } func (g historyClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { response, err := g.c.GetDLQReplicationMessages(ctx, proto.FromHistoryGetDLQReplicationMessagesRequest(gp1), p1...) return proto.ToHistoryGetDLQReplicationMessagesResponse(response), proto.ToError(err) } func (g historyClient) GetFailoverInfo(ctx context.Context, gp1 *types.GetFailoverInfoRequest, p1 ...yarpc.CallOption) (gp2 *types.GetFailoverInfoResponse, err error) { response, err := g.c.GetFailoverInfo(ctx, proto.FromHistoryGetFailoverInfoRequest(gp1), p1...) return proto.ToHistoryGetFailoverInfoResponse(response), proto.ToError(err) } func (g historyClient) GetMutableState(ctx context.Context, gp1 *types.GetMutableStateRequest, p1 ...yarpc.CallOption) (gp2 *types.GetMutableStateResponse, err error) { response, err := g.c.GetMutableState(ctx, proto.FromHistoryGetMutableStateRequest(gp1), p1...) return proto.ToHistoryGetMutableStateResponse(response), proto.ToError(err) } func (g historyClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { response, err := g.c.GetReplicationMessages(ctx, proto.FromHistoryGetReplicationMessagesRequest(gp1), p1...) return proto.ToHistoryGetReplicationMessagesResponse(response), proto.ToError(err) } func (g historyClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { response, err := g.c.MergeDLQMessages(ctx, proto.FromHistoryMergeDLQMessagesRequest(mp1), p1...) return proto.ToHistoryMergeDLQMessagesResponse(response), proto.ToError(err) } func (g historyClient) NotifyFailoverMarkers(ctx context.Context, np1 *types.NotifyFailoverMarkersRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.NotifyFailoverMarkers(ctx, proto.FromHistoryNotifyFailoverMarkersRequest(np1), p1...) return proto.ToError(err) } func (g historyClient) PollMutableState(ctx context.Context, pp1 *types.PollMutableStateRequest, p1 ...yarpc.CallOption) (pp2 *types.PollMutableStateResponse, err error) { response, err := g.c.PollMutableState(ctx, proto.FromHistoryPollMutableStateRequest(pp1), p1...) return proto.ToHistoryPollMutableStateResponse(response), proto.ToError(err) } func (g historyClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.PurgeDLQMessages(ctx, proto.FromHistoryPurgeDLQMessagesRequest(pp1), p1...) return proto.ToError(err) } func (g historyClient) QueryWorkflow(ctx context.Context, hp1 *types.HistoryQueryWorkflowRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryQueryWorkflowResponse, err error) { response, err := g.c.QueryWorkflow(ctx, proto.FromHistoryQueryWorkflowRequest(hp1), p1...) return proto.ToHistoryQueryWorkflowResponse(response), proto.ToError(err) } func (g historyClient) RatelimitUpdate(ctx context.Context, request *types.RatelimitUpdateRequest, opts ...yarpc.CallOption) (rp1 *types.RatelimitUpdateResponse, err error) { response, err := g.c.RatelimitUpdate(ctx, proto.FromHistoryRatelimitUpdateRequest(request), opts...) return proto.ToHistoryRatelimitUpdateResponse(response), proto.ToError(err) } func (g historyClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { response, err := g.c.ReadDLQMessages(ctx, proto.FromHistoryReadDLQMessagesRequest(rp1), p1...) return proto.ToHistoryReadDLQMessagesResponse(response), proto.ToError(err) } func (g historyClient) ReapplyEvents(ctx context.Context, hp1 *types.HistoryReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.ReapplyEvents(ctx, proto.FromHistoryReapplyEventsRequest(hp1), p1...) return proto.ToError(err) } func (g historyClient) RecordActivityTaskHeartbeat(ctx context.Context, hp1 *types.HistoryRecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp1 *types.RecordActivityTaskHeartbeatResponse, err error) { response, err := g.c.RecordActivityTaskHeartbeat(ctx, proto.FromHistoryRecordActivityTaskHeartbeatRequest(hp1), p1...) return proto.ToHistoryRecordActivityTaskHeartbeatResponse(response), proto.ToError(err) } func (g historyClient) RecordActivityTaskStarted(ctx context.Context, rp1 *types.RecordActivityTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskStartedResponse, err error) { response, err := g.c.RecordActivityTaskStarted(ctx, proto.FromHistoryRecordActivityTaskStartedRequest(rp1), p1...) return proto.ToHistoryRecordActivityTaskStartedResponse(response), proto.ToError(err) } func (g historyClient) RecordChildExecutionCompleted(ctx context.Context, rp1 *types.RecordChildExecutionCompletedRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RecordChildExecutionCompleted(ctx, proto.FromHistoryRecordChildExecutionCompletedRequest(rp1), p1...) return proto.ToError(err) } func (g historyClient) RecordDecisionTaskStarted(ctx context.Context, rp1 *types.RecordDecisionTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordDecisionTaskStartedResponse, err error) { response, err := g.c.RecordDecisionTaskStarted(ctx, proto.FromHistoryRecordDecisionTaskStartedRequest(rp1), p1...) return proto.ToHistoryRecordDecisionTaskStartedResponse(response), proto.ToError(err) } func (g historyClient) RefreshWorkflowTasks(ctx context.Context, hp1 *types.HistoryRefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RefreshWorkflowTasks(ctx, proto.FromHistoryRefreshWorkflowTasksRequest(hp1), p1...) return proto.ToError(err) } func (g historyClient) RemoveSignalMutableState(ctx context.Context, rp1 *types.RemoveSignalMutableStateRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RemoveSignalMutableState(ctx, proto.FromHistoryRemoveSignalMutableStateRequest(rp1), p1...) return proto.ToError(err) } func (g historyClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RemoveTask(ctx, proto.FromHistoryRemoveTaskRequest(rp1), p1...) return proto.ToError(err) } func (g historyClient) ReplicateEventsV2(ctx context.Context, rp1 *types.ReplicateEventsV2Request, p1 ...yarpc.CallOption) (err error) { _, err = g.c.ReplicateEventsV2(ctx, proto.FromHistoryReplicateEventsV2Request(rp1), p1...) return proto.ToError(err) } func (g historyClient) RequestCancelWorkflowExecution(ctx context.Context, hp1 *types.HistoryRequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RequestCancelWorkflowExecution(ctx, proto.FromHistoryRequestCancelWorkflowExecutionRequest(hp1), p1...) return proto.ToError(err) } func (g historyClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.ResetQueue(ctx, proto.FromHistoryResetQueueRequest(rp1), p1...) return proto.ToError(err) } func (g historyClient) ResetStickyTaskList(ctx context.Context, hp1 *types.HistoryResetStickyTaskListRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryResetStickyTaskListResponse, err error) { response, err := g.c.ResetStickyTaskList(ctx, proto.FromHistoryResetStickyTaskListRequest(hp1), p1...) return proto.ToHistoryResetStickyTaskListResponse(response), proto.ToError(err) } func (g historyClient) ResetWorkflowExecution(ctx context.Context, hp1 *types.HistoryResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp1 *types.ResetWorkflowExecutionResponse, err error) { response, err := g.c.ResetWorkflowExecution(ctx, proto.FromHistoryResetWorkflowExecutionRequest(hp1), p1...) return proto.ToHistoryResetWorkflowExecutionResponse(response), proto.ToError(err) } func (g historyClient) RespondActivityTaskCanceled(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondActivityTaskCanceled(ctx, proto.FromHistoryRespondActivityTaskCanceledRequest(hp1), p1...) return proto.ToError(err) } func (g historyClient) RespondActivityTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondActivityTaskCompleted(ctx, proto.FromHistoryRespondActivityTaskCompletedRequest(hp1), p1...) return proto.ToError(err) } func (g historyClient) RespondActivityTaskFailed(ctx context.Context, hp1 *types.HistoryRespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondActivityTaskFailed(ctx, proto.FromHistoryRespondActivityTaskFailedRequest(hp1), p1...) return proto.ToError(err) } func (g historyClient) RespondCrossClusterTasksCompleted(ctx context.Context, rp1 *types.RespondCrossClusterTasksCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondCrossClusterTasksCompletedResponse, err error) { response, err := g.c.RespondCrossClusterTasksCompleted(ctx, proto.FromHistoryRespondCrossClusterTasksCompletedRequest(rp1), p1...) return proto.ToHistoryRespondCrossClusterTasksCompletedResponse(response), proto.ToError(err) } func (g historyClient) RespondDecisionTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryRespondDecisionTaskCompletedResponse, err error) { response, err := g.c.RespondDecisionTaskCompleted(ctx, proto.FromHistoryRespondDecisionTaskCompletedRequest(hp1), p1...) return proto.ToHistoryRespondDecisionTaskCompletedResponse(response), proto.ToError(err) } func (g historyClient) RespondDecisionTaskFailed(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondDecisionTaskFailed(ctx, proto.FromHistoryRespondDecisionTaskFailedRequest(hp1), p1...) return proto.ToError(err) } func (g historyClient) ScheduleDecisionTask(ctx context.Context, sp1 *types.ScheduleDecisionTaskRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.ScheduleDecisionTask(ctx, proto.FromHistoryScheduleDecisionTaskRequest(sp1), p1...) return proto.ToError(err) } func (g historyClient) SignalWithStartWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { response, err := g.c.SignalWithStartWorkflowExecution(ctx, proto.FromHistorySignalWithStartWorkflowExecutionRequest(hp1), p1...) return proto.ToHistorySignalWithStartWorkflowExecutionResponse(response), proto.ToError(err) } func (g historyClient) SignalWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.SignalWorkflowExecution(ctx, proto.FromHistorySignalWorkflowExecutionRequest(hp1), p1...) return proto.ToError(err) } func (g historyClient) StartWorkflowExecution(ctx context.Context, hp1 *types.HistoryStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { response, err := g.c.StartWorkflowExecution(ctx, proto.FromHistoryStartWorkflowExecutionRequest(hp1), p1...) return proto.ToHistoryStartWorkflowExecutionResponse(response), proto.ToError(err) } func (g historyClient) SyncActivity(ctx context.Context, sp1 *types.SyncActivityRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.SyncActivity(ctx, proto.FromHistorySyncActivityRequest(sp1), p1...) return proto.ToError(err) } func (g historyClient) SyncShardStatus(ctx context.Context, sp1 *types.SyncShardStatusRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.SyncShardStatus(ctx, proto.FromHistorySyncShardStatusRequest(sp1), p1...) return proto.ToError(err) } func (g historyClient) TerminateWorkflowExecution(ctx context.Context, hp1 *types.HistoryTerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.TerminateWorkflowExecution(ctx, proto.FromHistoryTerminateWorkflowExecutionRequest(hp1), p1...) return proto.ToError(err) } ================================================ FILE: client/wrappers/grpc/matching_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" ) func (g matchingClient) AddActivityTask(ctx context.Context, ap1 *types.AddActivityTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddActivityTaskResponse, err error) { response, err := g.c.AddActivityTask(ctx, proto.FromMatchingAddActivityTaskRequest(ap1), p1...) return proto.ToMatchingAddActivityTaskResponse(response), proto.ToError(err) } func (g matchingClient) AddDecisionTask(ctx context.Context, ap1 *types.AddDecisionTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddDecisionTaskResponse, err error) { response, err := g.c.AddDecisionTask(ctx, proto.FromMatchingAddDecisionTaskRequest(ap1), p1...) return proto.ToMatchingAddDecisionTaskResponse(response), proto.ToError(err) } func (g matchingClient) CancelOutstandingPoll(ctx context.Context, cp1 *types.CancelOutstandingPollRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.CancelOutstandingPoll(ctx, proto.FromMatchingCancelOutstandingPollRequest(cp1), p1...) return proto.ToError(err) } func (g matchingClient) DescribeTaskList(ctx context.Context, mp1 *types.MatchingDescribeTaskListRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeTaskListResponse, err error) { response, err := g.c.DescribeTaskList(ctx, proto.FromMatchingDescribeTaskListRequest(mp1), p1...) return proto.ToMatchingDescribeTaskListResponse(response), proto.ToError(err) } func (g matchingClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { response, err := g.c.GetTaskListsByDomain(ctx, proto.FromMatchingGetTaskListsByDomainRequest(gp1), p1...) return proto.ToMatchingGetTaskListsByDomainResponse(response), proto.ToError(err) } func (g matchingClient) ListTaskListPartitions(ctx context.Context, mp1 *types.MatchingListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp1 *types.ListTaskListPartitionsResponse, err error) { response, err := g.c.ListTaskListPartitions(ctx, proto.FromMatchingListTaskListPartitionsRequest(mp1), p1...) return proto.ToMatchingListTaskListPartitionsResponse(response), proto.ToError(err) } func (g matchingClient) PollForActivityTask(ctx context.Context, mp1 *types.MatchingPollForActivityTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForActivityTaskResponse, err error) { response, err := g.c.PollForActivityTask(ctx, proto.FromMatchingPollForActivityTaskRequest(mp1), p1...) return proto.ToMatchingPollForActivityTaskResponse(response), proto.ToError(err) } func (g matchingClient) PollForDecisionTask(ctx context.Context, mp1 *types.MatchingPollForDecisionTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForDecisionTaskResponse, err error) { response, err := g.c.PollForDecisionTask(ctx, proto.FromMatchingPollForDecisionTaskRequest(mp1), p1...) return proto.ToMatchingPollForDecisionTaskResponse(response), proto.ToError(err) } func (g matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQueryWorkflowRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingQueryWorkflowResponse, err error) { response, err := g.c.QueryWorkflow(ctx, proto.FromMatchingQueryWorkflowRequest(mp1), p1...) return proto.ToMatchingQueryWorkflowResponse(response), proto.ToError(err) } func (g matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { response, err := g.c.RefreshTaskListPartitionConfig(ctx, proto.FromMatchingRefreshTaskListPartitionConfigRequest(mp1), p1...) return proto.ToMatchingRefreshTaskListPartitionConfigResponse(response), proto.ToError(err) } func (g matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { _, err = g.c.RespondQueryTaskCompleted(ctx, proto.FromMatchingRespondQueryTaskCompletedRequest(mp1), p1...) return proto.ToError(err) } func (g matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { response, err := g.c.UpdateTaskListPartitionConfig(ctx, proto.FromMatchingUpdateTaskListPartitionConfigRequest(mp1), p1...) return proto.ToMatchingUpdateTaskListPartitionConfigResponse(response), proto.ToError(err) } ================================================ FILE: client/wrappers/grpc/sharddistributor_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" ) func (g sharddistributorClient) GetShardOwner(ctx context.Context, gp1 *types.GetShardOwnerRequest, p1 ...yarpc.CallOption) (gp2 *types.GetShardOwnerResponse, err error) { response, err := g.c.GetShardOwner(ctx, proto.FromShardDistributorGetShardOwnerRequest(gp1), p1...) return proto.ToShardDistributorGetShardOwnerResponse(response), proto.ToError(err) } func (g sharddistributorClient) WatchNamespaceState(ctx context.Context, wp1 *types.WatchNamespaceStateRequest, p1 ...yarpc.CallOption) (w1 sharddistributor.WatchNamespaceStateClient, err error) { stream, err := g.c.WatchNamespaceState(ctx, proto.FromShardDistributorWatchNamespaceStateRequest(wp1), p1...) if err != nil { return nil, proto.ToError(err) } return &watchnamespacestateClient{stream: stream}, nil } type watchnamespacestateClient struct { stream sharddistributorv1.ShardDistributorAPIServiceWatchNamespaceStateYARPCClient } func (c *watchnamespacestateClient) Context() context.Context { return c.stream.Context() } func (c *watchnamespacestateClient) Recv(options ...yarpc.StreamOption) (*types.WatchNamespaceStateResponse, error) { response, err := c.stream.Recv(options...) if err != nil { return nil, proto.ToError(err) } return proto.ToShardDistributorWatchNamespaceStateResponse(response), nil } func (c *watchnamespacestateClient) CloseSend(options ...yarpc.StreamOption) error { return proto.ToError(c.stream.CloseSend(options...)) } ================================================ FILE: client/wrappers/grpc/sharddistributorexecutor_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" ) func (g sharddistributorexecutorClient) Heartbeat(ctx context.Context, ep1 *types.ExecutorHeartbeatRequest, p1 ...yarpc.CallOption) (ep2 *types.ExecutorHeartbeatResponse, err error) { response, err := g.c.Heartbeat(ctx, proto.FromShardDistributorExecutorHeartbeatRequest(ep1), p1...) return proto.ToShardDistributorExecutorHeartbeatResponse(response), proto.ToError(err) } ================================================ FILE: client/wrappers/metered/admin_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) // adminClient implements admin.Client interface instrumented with retries type adminClient struct { client admin.Client metricsClient metrics.Client } // NewAdminClient creates a new instance of adminClient with retry policy func NewAdminClient(client admin.Client, metricsClient metrics.Client) admin.Client { return &adminClient{ client: client, metricsClient: metricsClient, } } func (c *adminClient) AddSearchAttribute(ctx context.Context, ap1 *types.AddSearchAttributeRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientAddSearchAttributeScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientAddSearchAttributeScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.AddSearchAttribute(ctx, ap1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *adminClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientCloseShardScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientCloseShardScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.CloseShard(ctx, cp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *adminClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (cp2 *types.CountDLQMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientCountDLQMessagesScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientCountDLQMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) cp2, err = c.client.CountDLQMessages(ctx, cp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return cp2, err } func (c *adminClient) DeleteWorkflow(ctx context.Context, ap1 *types.AdminDeleteWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDeleteWorkflowResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientDeleteWorkflowScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientDeleteWorkflowScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) ap2, err = c.client.DeleteWorkflow(ctx, ap1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return ap2, err } func (c *adminClient) DescribeCluster(ctx context.Context, p1 ...yarpc.CallOption) (dp1 *types.DescribeClusterResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientDescribeClusterScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientDescribeClusterScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp1, err = c.client.DescribeCluster(ctx, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp1, err } func (c *adminClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientDescribeHistoryHostScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientDescribeHistoryHostScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp2, err = c.client.DescribeHistoryHost(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp2, err } func (c *adminClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientDescribeQueueScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientDescribeQueueScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp2, err = c.client.DescribeQueue(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp2, err } func (c *adminClient) DescribeShardDistribution(ctx context.Context, dp1 *types.DescribeShardDistributionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeShardDistributionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientDescribeShardDistributionScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientDescribeShardDistributionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp2, err = c.client.DescribeShardDistribution(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp2, err } func (c *adminClient) DescribeWorkflowExecution(ctx context.Context, ap1 *types.AdminDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDescribeWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientDescribeWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientDescribeWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) ap2, err = c.client.DescribeWorkflowExecution(ctx, ap1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return ap2, err } func (c *adminClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientGetDLQReplicationMessagesScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientGetDLQReplicationMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetDLQReplicationMessages(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *adminClient) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainAsyncWorkflowConfiguratonResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientGetDomainAsyncWorkflowConfiguratonScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientGetDomainAsyncWorkflowConfiguratonScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp1, err = c.client.GetDomainAsyncWorkflowConfiguraton(ctx, request, opts...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp1, err } func (c *adminClient) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainIsolationGroupsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientGetDomainIsolationGroupsScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientGetDomainIsolationGroupsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp1, err = c.client.GetDomainIsolationGroups(ctx, request, opts...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp1, err } func (c *adminClient) GetDomainReplicationMessages(ctx context.Context, gp1 *types.GetDomainReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDomainReplicationMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientGetDomainReplicationMessagesScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientGetDomainReplicationMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetDomainReplicationMessages(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *adminClient) GetDynamicConfig(ctx context.Context, gp1 *types.GetDynamicConfigRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDynamicConfigResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientGetDynamicConfigScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientGetDynamicConfigScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetDynamicConfig(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *adminClient) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetGlobalIsolationGroupsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientGetGlobalIsolationGroupsScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientGetGlobalIsolationGroupsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp1, err = c.client.GetGlobalIsolationGroups(ctx, request, opts...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp1, err } func (c *adminClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientGetReplicationMessagesScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientGetReplicationMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetReplicationMessages(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *adminClient) GetWorkflowExecutionRawHistoryV2(ctx context.Context, gp1 *types.GetWorkflowExecutionRawHistoryV2Request, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionRawHistoryV2Response, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientGetWorkflowExecutionRawHistoryV2Scope) } else { scope = c.metricsClient.Scope(metrics.AdminClientGetWorkflowExecutionRawHistoryV2Scope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetWorkflowExecutionRawHistoryV2(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *adminClient) ListDynamicConfig(ctx context.Context, lp1 *types.ListDynamicConfigRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDynamicConfigResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientListDynamicConfigScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientListDynamicConfigScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) lp2, err = c.client.ListDynamicConfig(ctx, lp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return lp2, err } func (c *adminClient) MaintainCorruptWorkflow(ctx context.Context, ap1 *types.AdminMaintainWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminMaintainWorkflowResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientMaintainCorruptWorkflowScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientMaintainCorruptWorkflowScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) ap2, err = c.client.MaintainCorruptWorkflow(ctx, ap1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return ap2, err } func (c *adminClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientMergeDLQMessagesScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientMergeDLQMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) mp2, err = c.client.MergeDLQMessages(ctx, mp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return mp2, err } func (c *adminClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientPurgeDLQMessagesScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientPurgeDLQMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.PurgeDLQMessages(ctx, pp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *adminClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientReadDLQMessagesScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientReadDLQMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.ReadDLQMessages(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *adminClient) ReapplyEvents(ctx context.Context, rp1 *types.ReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientReapplyEventsScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientReapplyEventsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.ReapplyEvents(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *adminClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientRefreshWorkflowTasksScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientRefreshWorkflowTasksScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RefreshWorkflowTasks(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *adminClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientRemoveTaskScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientRemoveTaskScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RemoveTask(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *adminClient) ResendReplicationTasks(ctx context.Context, rp1 *types.ResendReplicationTasksRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientResendReplicationTasksScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientResendReplicationTasksScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.ResendReplicationTasks(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *adminClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientResetQueueScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientResetQueueScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.ResetQueue(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *adminClient) RestoreDynamicConfig(ctx context.Context, rp1 *types.RestoreDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientRestoreDynamicConfigScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientRestoreDynamicConfigScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RestoreDynamicConfig(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *adminClient) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientUpdateDomainAsyncWorkflowConfiguratonScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientUpdateDomainAsyncWorkflowConfiguratonScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) up1, err = c.client.UpdateDomainAsyncWorkflowConfiguraton(ctx, request, opts...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return up1, err } func (c *adminClient) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainIsolationGroupsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientUpdateDomainIsolationGroupsScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientUpdateDomainIsolationGroupsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) up1, err = c.client.UpdateDomainIsolationGroups(ctx, request, opts...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return up1, err } func (c *adminClient) UpdateDynamicConfig(ctx context.Context, up1 *types.UpdateDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientUpdateDynamicConfigScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientUpdateDynamicConfigScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.UpdateDynamicConfig(ctx, up1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *adminClient) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateGlobalIsolationGroupsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientUpdateGlobalIsolationGroupsScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientUpdateGlobalIsolationGroupsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) up1, err = c.client.UpdateGlobalIsolationGroups(ctx, request, opts...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return up1, err } func (c *adminClient) UpdateTaskListPartitionConfig(ctx context.Context, request *types.UpdateTaskListPartitionConfigRequest, opts ...yarpc.CallOption) (up1 *types.UpdateTaskListPartitionConfigResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.AdminClientUpdateTaskListPartitionConfigScope) } else { scope = c.metricsClient.Scope(metrics.AdminClientUpdateTaskListPartitionConfigScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) up1, err = c.client.UpdateTaskListPartitionConfig(ctx, request, opts...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return up1, err } ================================================ FILE: client/wrappers/metered/base.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metered import "context" type retryCountKeyType string const retryCountKey = retryCountKeyType("retryCount") func getRetryCountFromContext(ctx context.Context) int { if retryCount, ok := ctx.Value(retryCountKey).(int); ok { return retryCount } return -1 } ================================================ FILE: client/wrappers/metered/frontend_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) // frontendClient implements frontend.Client interface instrumented with retries type frontendClient struct { client frontend.Client metricsClient metrics.Client } // NewFrontendClient creates a new instance of frontendClient with retry policy func NewFrontendClient(client frontend.Client, metricsClient metrics.Client) frontend.Client { return &frontendClient{ client: client, metricsClient: metricsClient, } } func (c *frontendClient) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (cp2 *types.CountWorkflowExecutionsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientCountWorkflowExecutionsScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientCountWorkflowExecutionsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) cp2, err = c.client.CountWorkflowExecutions(ctx, cp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return cp2, err } func (c *frontendClient) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientDeleteDomainScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientDeleteDomainScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.DeleteDomain(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientDeprecateDomainScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientDeprecateDomainScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.DeprecateDomain(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeDomainResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientDescribeDomainScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientDescribeDomainScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp2, err = c.client.DescribeDomain(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp2, err } func (c *frontendClient) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeTaskListResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientDescribeTaskListScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientDescribeTaskListScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp2, err = c.client.DescribeTaskList(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp2, err } func (c *frontendClient) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientDescribeWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientDescribeWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp2, err = c.client.DescribeWorkflowExecution(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp2, err } func (c *frontendClient) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientDiagnoseWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientDiagnoseWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp2, err = c.client.DiagnoseWorkflowExecution(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp2, err } func (c *frontendClient) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest, p1 ...yarpc.CallOption) (fp2 *types.FailoverDomainResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientFailoverDomainScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientFailoverDomainScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) fp2, err = c.client.FailoverDomain(ctx, fp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return fp2, err } func (c *frontendClient) GetClusterInfo(ctx context.Context, p1 ...yarpc.CallOption) (cp1 *types.ClusterInfo, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientGetClusterInfoScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientGetClusterInfoScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) cp1, err = c.client.GetClusterInfo(ctx, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return cp1, err } func (c *frontendClient) GetSearchAttributes(ctx context.Context, p1 ...yarpc.CallOption) (gp1 *types.GetSearchAttributesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientGetSearchAttributesScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientGetSearchAttributesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp1, err = c.client.GetSearchAttributes(ctx, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp1, err } func (c *frontendClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientGetTaskListsByDomainScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientGetTaskListsByDomainScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetTaskListsByDomain(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *frontendClient) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientGetWorkflowExecutionHistoryScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientGetWorkflowExecutionHistoryScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetWorkflowExecutionHistory(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *frontendClient) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientListArchivedWorkflowExecutionsScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientListArchivedWorkflowExecutionsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) lp2, err = c.client.ListArchivedWorkflowExecutions(ctx, lp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return lp2, err } func (c *frontendClient) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientListClosedWorkflowExecutionsScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientListClosedWorkflowExecutionsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) lp2, err = c.client.ListClosedWorkflowExecutions(ctx, lp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return lp2, err } func (c *frontendClient) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDomainsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientListDomainsScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientListDomainsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) lp2, err = c.client.ListDomains(ctx, lp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return lp2, err } func (c *frontendClient) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest, p1 ...yarpc.CallOption) (lp2 *types.ListFailoverHistoryResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientListFailoverHistoryScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientListFailoverHistoryScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) lp2, err = c.client.ListFailoverHistory(ctx, lp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return lp2, err } func (c *frontendClient) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientListOpenWorkflowExecutionsScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientListOpenWorkflowExecutionsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) lp2, err = c.client.ListOpenWorkflowExecutions(ctx, lp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return lp2, err } func (c *frontendClient) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListTaskListPartitionsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientListTaskListPartitionsScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientListTaskListPartitionsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) lp2, err = c.client.ListTaskListPartitions(ctx, lp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return lp2, err } func (c *frontendClient) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientListWorkflowExecutionsScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientListWorkflowExecutionsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) lp2, err = c.client.ListWorkflowExecutions(ctx, lp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return lp2, err } func (c *frontendClient) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForActivityTaskResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientPollForActivityTaskScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientPollForActivityTaskScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) pp2, err = c.client.PollForActivityTask(ctx, pp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return pp2, err } func (c *frontendClient) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForDecisionTaskResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientPollForDecisionTaskScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientPollForDecisionTaskScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) pp2, err = c.client.PollForDecisionTask(ctx, pp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return pp2, err } func (c *frontendClient) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest, p1 ...yarpc.CallOption) (qp2 *types.QueryWorkflowResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientQueryWorkflowScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientQueryWorkflowScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) qp2, err = c.client.QueryWorkflow(ctx, qp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return qp2, err } func (c *frontendClient) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRecordActivityTaskHeartbeatScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRecordActivityTaskHeartbeatScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.RecordActivityTaskHeartbeat(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *frontendClient) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRecordActivityTaskHeartbeatByIDScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRecordActivityTaskHeartbeatByIDScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.RecordActivityTaskHeartbeatByID(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *frontendClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRefreshWorkflowTasksScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRefreshWorkflowTasksScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RefreshWorkflowTasks(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRegisterDomainScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRegisterDomainScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RegisterDomain(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRequestCancelWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRequestCancelWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RequestCancelWorkflowExecution(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetStickyTaskListResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientResetStickyTaskListScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientResetStickyTaskListScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.ResetStickyTaskList(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *frontendClient) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientResetWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientResetWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.ResetWorkflowExecution(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *frontendClient) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskCanceledScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskCanceledScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondActivityTaskCanceled(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskCanceledByIDScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskCanceledByIDScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondActivityTaskCanceledByID(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskCompletedScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskCompletedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondActivityTaskCompleted(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskCompletedByIDScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskCompletedByIDScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondActivityTaskCompletedByID(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskFailedScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskFailedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondActivityTaskFailed(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskFailedByIDScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRespondActivityTaskFailedByIDScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondActivityTaskFailedByID(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRespondDecisionTaskCompletedScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRespondDecisionTaskCompletedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.RespondDecisionTaskCompleted(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *frontendClient) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRespondDecisionTaskFailedScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRespondDecisionTaskFailedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondDecisionTaskFailed(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRespondQueryTaskCompletedScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRespondQueryTaskCompletedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondQueryTaskCompleted(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.RestartWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientRestartWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientRestartWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.RestartWorkflowExecution(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *frontendClient) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientScanWorkflowExecutionsScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientScanWorkflowExecutionsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) lp2, err = c.client.ScanWorkflowExecutions(ctx, lp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return lp2, err } func (c *frontendClient) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientSignalWithStartWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientSignalWithStartWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) sp2, err = c.client.SignalWithStartWorkflowExecution(ctx, sp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return sp2, err } func (c *frontendClient) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientSignalWithStartWorkflowExecutionAsyncScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientSignalWithStartWorkflowExecutionAsyncScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) sp2, err = c.client.SignalWithStartWorkflowExecutionAsync(ctx, sp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return sp2, err } func (c *frontendClient) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientSignalWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientSignalWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.SignalWorkflowExecution(ctx, sp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientStartWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientStartWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) sp2, err = c.client.StartWorkflowExecution(ctx, sp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return sp2, err } func (c *frontendClient) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientStartWorkflowExecutionAsyncScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientStartWorkflowExecutionAsyncScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) sp2, err = c.client.StartWorkflowExecutionAsync(ctx, sp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return sp2, err } func (c *frontendClient) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientTerminateWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientTerminateWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.TerminateWorkflowExecution(ctx, tp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *frontendClient) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest, p1 ...yarpc.CallOption) (up2 *types.UpdateDomainResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.FrontendClientUpdateDomainScope) } else { scope = c.metricsClient.Scope(metrics.FrontendClientUpdateDomainScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) up2, err = c.client.UpdateDomain(ctx, up1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return up2, err } ================================================ FILE: client/wrappers/metered/history_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) // historyClient implements history.Client interface instrumented with retries type historyClient struct { client history.Client metricsClient metrics.Client } // NewHistoryClient creates a new instance of historyClient with retry policy func NewHistoryClient(client history.Client, metricsClient metrics.Client) history.Client { return &historyClient{ client: client, metricsClient: metricsClient, } } func (c *historyClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientCloseShardScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientCloseShardScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.CloseShard(ctx, cp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (hp1 *types.HistoryCountDLQMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientCountDLQMessagesScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientCountDLQMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) hp1, err = c.client.CountDLQMessages(ctx, cp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return hp1, err } func (c *historyClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientDescribeHistoryHostScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientDescribeHistoryHostScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp2, err = c.client.DescribeHistoryHost(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp2, err } func (c *historyClient) DescribeMutableState(ctx context.Context, dp1 *types.DescribeMutableStateRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeMutableStateResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientDescribeMutableStateScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientDescribeMutableStateScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp2, err = c.client.DescribeMutableState(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp2, err } func (c *historyClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientDescribeQueueScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientDescribeQueueScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp2, err = c.client.DescribeQueue(ctx, dp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp2, err } func (c *historyClient) DescribeWorkflowExecution(ctx context.Context, hp1 *types.HistoryDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientDescribeWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientDescribeWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) dp1, err = c.client.DescribeWorkflowExecution(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp1, err } func (c *historyClient) GetCrossClusterTasks(ctx context.Context, gp1 *types.GetCrossClusterTasksRequest, p1 ...yarpc.CallOption) (gp2 *types.GetCrossClusterTasksResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientGetCrossClusterTasksScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientGetCrossClusterTasksScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetCrossClusterTasks(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *historyClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientGetDLQReplicationMessagesScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientGetDLQReplicationMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetDLQReplicationMessages(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *historyClient) GetFailoverInfo(ctx context.Context, gp1 *types.GetFailoverInfoRequest, p1 ...yarpc.CallOption) (gp2 *types.GetFailoverInfoResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientGetFailoverInfoScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientGetFailoverInfoScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetFailoverInfo(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *historyClient) GetMutableState(ctx context.Context, gp1 *types.GetMutableStateRequest, p1 ...yarpc.CallOption) (gp2 *types.GetMutableStateResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientGetMutableStateScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientGetMutableStateScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetMutableState(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *historyClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientGetReplicationMessagesScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientGetReplicationMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetReplicationMessages(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *historyClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientMergeDLQMessagesScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientMergeDLQMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) mp2, err = c.client.MergeDLQMessages(ctx, mp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return mp2, err } func (c *historyClient) NotifyFailoverMarkers(ctx context.Context, np1 *types.NotifyFailoverMarkersRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientNotifyFailoverMarkersScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientNotifyFailoverMarkersScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.NotifyFailoverMarkers(ctx, np1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) PollMutableState(ctx context.Context, pp1 *types.PollMutableStateRequest, p1 ...yarpc.CallOption) (pp2 *types.PollMutableStateResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientPollMutableStateScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientPollMutableStateScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) pp2, err = c.client.PollMutableState(ctx, pp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return pp2, err } func (c *historyClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientPurgeDLQMessagesScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientPurgeDLQMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.PurgeDLQMessages(ctx, pp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) QueryWorkflow(ctx context.Context, hp1 *types.HistoryQueryWorkflowRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryQueryWorkflowResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientQueryWorkflowScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientQueryWorkflowScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) hp2, err = c.client.QueryWorkflow(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return hp2, err } func (c *historyClient) RatelimitUpdate(ctx context.Context, request *types.RatelimitUpdateRequest, opts ...yarpc.CallOption) (rp1 *types.RatelimitUpdateResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRatelimitUpdateScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRatelimitUpdateScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp1, err = c.client.RatelimitUpdate(ctx, request, opts...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp1, err } func (c *historyClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientReadDLQMessagesScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientReadDLQMessagesScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.ReadDLQMessages(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *historyClient) ReapplyEvents(ctx context.Context, hp1 *types.HistoryReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientReapplyEventsScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientReapplyEventsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.ReapplyEvents(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) RecordActivityTaskHeartbeat(ctx context.Context, hp1 *types.HistoryRecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp1 *types.RecordActivityTaskHeartbeatResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRecordActivityTaskHeartbeatScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRecordActivityTaskHeartbeatScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp1, err = c.client.RecordActivityTaskHeartbeat(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp1, err } func (c *historyClient) RecordActivityTaskStarted(ctx context.Context, rp1 *types.RecordActivityTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskStartedResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRecordActivityTaskStartedScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRecordActivityTaskStartedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.RecordActivityTaskStarted(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *historyClient) RecordChildExecutionCompleted(ctx context.Context, rp1 *types.RecordChildExecutionCompletedRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRecordChildExecutionCompletedScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRecordChildExecutionCompletedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RecordChildExecutionCompleted(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) RecordDecisionTaskStarted(ctx context.Context, rp1 *types.RecordDecisionTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordDecisionTaskStartedResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRecordDecisionTaskStartedScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRecordDecisionTaskStartedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.RecordDecisionTaskStarted(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *historyClient) RefreshWorkflowTasks(ctx context.Context, hp1 *types.HistoryRefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRefreshWorkflowTasksScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRefreshWorkflowTasksScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RefreshWorkflowTasks(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) RemoveSignalMutableState(ctx context.Context, rp1 *types.RemoveSignalMutableStateRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRemoveSignalMutableStateScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRemoveSignalMutableStateScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RemoveSignalMutableState(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRemoveTaskScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRemoveTaskScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RemoveTask(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) ReplicateEventsV2(ctx context.Context, rp1 *types.ReplicateEventsV2Request, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientReplicateEventsV2Scope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientReplicateEventsV2Scope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.ReplicateEventsV2(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) RequestCancelWorkflowExecution(ctx context.Context, hp1 *types.HistoryRequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRequestCancelWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRequestCancelWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RequestCancelWorkflowExecution(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientResetQueueScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientResetQueueScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.ResetQueue(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) ResetStickyTaskList(ctx context.Context, hp1 *types.HistoryResetStickyTaskListRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryResetStickyTaskListResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientResetStickyTaskListScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientResetStickyTaskListScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) hp2, err = c.client.ResetStickyTaskList(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return hp2, err } func (c *historyClient) ResetWorkflowExecution(ctx context.Context, hp1 *types.HistoryResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp1 *types.ResetWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientResetWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientResetWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp1, err = c.client.ResetWorkflowExecution(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp1, err } func (c *historyClient) RespondActivityTaskCanceled(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRespondActivityTaskCanceledScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRespondActivityTaskCanceledScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondActivityTaskCanceled(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) RespondActivityTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRespondActivityTaskCompletedScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRespondActivityTaskCompletedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondActivityTaskCompleted(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) RespondActivityTaskFailed(ctx context.Context, hp1 *types.HistoryRespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRespondActivityTaskFailedScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRespondActivityTaskFailedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondActivityTaskFailed(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) RespondCrossClusterTasksCompleted(ctx context.Context, rp1 *types.RespondCrossClusterTasksCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondCrossClusterTasksCompletedResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRespondCrossClusterTasksCompletedScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRespondCrossClusterTasksCompletedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) rp2, err = c.client.RespondCrossClusterTasksCompleted(ctx, rp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return rp2, err } func (c *historyClient) RespondDecisionTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryRespondDecisionTaskCompletedResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRespondDecisionTaskCompletedScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRespondDecisionTaskCompletedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) hp2, err = c.client.RespondDecisionTaskCompleted(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return hp2, err } func (c *historyClient) RespondDecisionTaskFailed(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientRespondDecisionTaskFailedScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientRespondDecisionTaskFailedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondDecisionTaskFailed(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) ScheduleDecisionTask(ctx context.Context, sp1 *types.ScheduleDecisionTaskRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientScheduleDecisionTaskScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientScheduleDecisionTaskScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.ScheduleDecisionTask(ctx, sp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) SignalWithStartWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientSignalWithStartWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientSignalWithStartWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) sp1, err = c.client.SignalWithStartWorkflowExecution(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return sp1, err } func (c *historyClient) SignalWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientSignalWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientSignalWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.SignalWorkflowExecution(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) StartWorkflowExecution(ctx context.Context, hp1 *types.HistoryStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientStartWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientStartWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) sp1, err = c.client.StartWorkflowExecution(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return sp1, err } func (c *historyClient) SyncActivity(ctx context.Context, sp1 *types.SyncActivityRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientSyncActivityScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientSyncActivityScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.SyncActivity(ctx, sp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) SyncShardStatus(ctx context.Context, sp1 *types.SyncShardStatusRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientSyncShardStatusScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientSyncShardStatusScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.SyncShardStatus(ctx, sp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *historyClient) TerminateWorkflowExecution(ctx context.Context, hp1 *types.HistoryTerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.HistoryClientTerminateWorkflowExecutionScope) } else { scope = c.metricsClient.Scope(metrics.HistoryClientTerminateWorkflowExecutionScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.TerminateWorkflowExecution(ctx, hp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } ================================================ FILE: client/wrappers/metered/matching_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "strings" "go.uber.org/yarpc" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) // matchingClient implements matching.Client interface instrumented with retries type matchingClient struct { client matching.Client metricsClient metrics.Client } // NewMatchingClient creates a new instance of matchingClient with retry policy func NewMatchingClient(client matching.Client, metricsClient metrics.Client) matching.Client { return &matchingClient{ client: client, metricsClient: metricsClient, } } func (c *matchingClient) AddActivityTask(ctx context.Context, ap1 *types.AddActivityTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddActivityTaskResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientAddActivityTaskScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientAddActivityTaskScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, ap1) sw := scope.StartTimer(metrics.CadenceClientLatency) ap2, err = c.client.AddActivityTask(ctx, ap1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return ap2, err } func (c *matchingClient) AddDecisionTask(ctx context.Context, ap1 *types.AddDecisionTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddDecisionTaskResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientAddDecisionTaskScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientAddDecisionTaskScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, ap1) sw := scope.StartTimer(metrics.CadenceClientLatency) ap2, err = c.client.AddDecisionTask(ctx, ap1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return ap2, err } func (c *matchingClient) CancelOutstandingPoll(ctx context.Context, cp1 *types.CancelOutstandingPollRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientCancelOutstandingPollScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientCancelOutstandingPollScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, cp1) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.CancelOutstandingPoll(ctx, cp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *matchingClient) DescribeTaskList(ctx context.Context, mp1 *types.MatchingDescribeTaskListRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeTaskListResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientDescribeTaskListScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientDescribeTaskListScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, mp1) sw := scope.StartTimer(metrics.CadenceClientLatency) dp1, err = c.client.DescribeTaskList(ctx, mp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return dp1, err } func (c *matchingClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientGetTaskListsByDomainScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientGetTaskListsByDomainScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, gp1) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetTaskListsByDomain(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *matchingClient) ListTaskListPartitions(ctx context.Context, mp1 *types.MatchingListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp1 *types.ListTaskListPartitionsResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientListTaskListPartitionsScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientListTaskListPartitionsScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, mp1) sw := scope.StartTimer(metrics.CadenceClientLatency) lp1, err = c.client.ListTaskListPartitions(ctx, mp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return lp1, err } func (c *matchingClient) PollForActivityTask(ctx context.Context, mp1 *types.MatchingPollForActivityTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForActivityTaskResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientPollForActivityTaskScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientPollForActivityTaskScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, mp1) sw := scope.StartTimer(metrics.CadenceClientLatency) mp2, err = c.client.PollForActivityTask(ctx, mp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return mp2, err } func (c *matchingClient) PollForDecisionTask(ctx context.Context, mp1 *types.MatchingPollForDecisionTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForDecisionTaskResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientPollForDecisionTaskScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientPollForDecisionTaskScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, mp1) sw := scope.StartTimer(metrics.CadenceClientLatency) mp2, err = c.client.PollForDecisionTask(ctx, mp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return mp2, err } func (c *matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQueryWorkflowRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingQueryWorkflowResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientQueryWorkflowScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientQueryWorkflowScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, mp1) sw := scope.StartTimer(metrics.CadenceClientLatency) mp2, err = c.client.QueryWorkflow(ctx, mp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return mp2, err } func (c *matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientRefreshTaskListPartitionConfigScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientRefreshTaskListPartitionConfigScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, mp1) sw := scope.StartTimer(metrics.CadenceClientLatency) mp2, err = c.client.RefreshTaskListPartitionConfig(ctx, mp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return mp2, err } func (c *matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientRespondQueryTaskCompletedScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientRespondQueryTaskCompletedScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, mp1) sw := scope.StartTimer(metrics.CadenceClientLatency) err = c.client.RespondQueryTaskCompleted(ctx, mp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return err } func (c *matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.MatchingClientUpdateTaskListPartitionConfigScope) } else { scope = c.metricsClient.Scope(metrics.MatchingClientUpdateTaskListPartitionConfigScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) c.emitForwardedFromStats(scope, mp1) sw := scope.StartTimer(metrics.CadenceClientLatency) mp2, err = c.client.UpdateTaskListPartitionConfig(ctx, mp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return mp2, err } type forwardedRequest interface { GetForwardedFrom() string GetTaskList() *types.TaskList } func (c *matchingClient) emitForwardedFromStats(scope metrics.Scope, req any) { p, ok := req.(forwardedRequest) if !ok || p.GetTaskList() == nil { return } taskList := p.GetTaskList() forwardedFrom := p.GetForwardedFrom() isChildPartition := strings.HasPrefix(taskList.GetName(), constants.ReservedTaskListPrefix) if forwardedFrom != "" { scope.IncCounter(metrics.MatchingClientForwardedCounter) return } if isChildPartition { scope.IncCounter(metrics.MatchingClientInvalidTaskListName) } return } ================================================ FILE: client/wrappers/metered/metered_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metered import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) func TestWrappers(t *testing.T) { t.Run("success", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := frontend.NewMockClient(ctrl) testScope := tally.NewTestScope("", nil) metricsClient := metrics.NewClient(testScope, metrics.ServiceIdx(0), metrics.HistogramMigration{}) clientMock.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, nil).Times(1) retryableClient := NewFrontendClient( clientMock, metricsClient) _, err := retryableClient.CountWorkflowExecutions(context.Background(), &types.CountWorkflowExecutionsRequest{}) assert.NoError(t, err) assert.Len(t, testScope.Snapshot().Counters(), 1, "there should be a single counter registered") assert.Len(t, testScope.Snapshot().Timers(), 1, "there should be a single timer registered") }) t.Run("error", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := frontend.NewMockClient(ctrl) testScope := tally.NewTestScope("", nil) metricsClient := metrics.NewClient(testScope, metrics.ServiceIdx(0), metrics.HistogramMigration{}) clientMock.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, errors.New("error")) retryableClient := NewFrontendClient( clientMock, metricsClient) _, err := retryableClient.CountWorkflowExecutions(context.Background(), &types.CountWorkflowExecutionsRequest{}) assert.Error(t, err) assert.Len(t, testScope.Snapshot().Counters(), 2, "there should be two counters registered, one for call and one for failure") }) } // Matching has a special logic that should emit a metric if a request is forwarded. func TestMatching(t *testing.T) { t.Run("forwarded", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := matching.NewMockClient(ctrl) testScope := tally.NewTestScope("", nil) metricsClient := metrics.NewClient(testScope, metrics.ServiceIdx(0), metrics.HistogramMigration{}) clientMock.EXPECT().AddActivityTask(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.AddActivityTaskResponse{}, nil).Times(1) retryableClient := NewMatchingClient( clientMock, metricsClient) _, err := retryableClient.AddActivityTask(context.Background(), &types.AddActivityTaskRequest{ ForwardedFrom: "test", TaskList: &types.TaskList{Name: "test"}, }) assert.NoError(t, err) assert.Len(t, testScope.Snapshot().Counters(), 2, "there should be two counters registered, one for call and one for forwarded") }) t.Run("forwarded poller", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := matching.NewMockClient(ctrl) testScope := tally.NewTestScope("", nil) metricsClient := metrics.NewClient(testScope, metrics.ServiceIdx(0), metrics.HistogramMigration{}) clientMock.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.MatchingPollForDecisionTaskResponse{}, nil).Times(1) retryableClient := NewMatchingClient( clientMock, metricsClient) _, err := retryableClient.PollForDecisionTask(context.Background(), &types.MatchingPollForDecisionTaskRequest{ ForwardedFrom: "test", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{Name: "test"}, }, }) assert.NoError(t, err) assert.Len(t, testScope.Snapshot().Counters(), 2, "there should be two counters registered, one for call and one for forwarded") }) t.Run("not forwarded", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := matching.NewMockClient(ctrl) testScope := tally.NewTestScope("", nil) metricsClient := metrics.NewClient(testScope, metrics.ServiceIdx(0), metrics.HistogramMigration{}) clientMock.EXPECT().AddActivityTask(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.AddActivityTaskResponse{}, nil).Times(1) retryableClient := NewMatchingClient( clientMock, metricsClient) _, err := retryableClient.AddActivityTask(context.Background(), &types.AddActivityTaskRequest{ ForwardedFrom: "", TaskList: &types.TaskList{Name: "test"}, }) assert.NoError(t, err) assert.Len(t, testScope.Snapshot().Counters(), 1, "there should be one counters registered, one for call") }) t.Run("invalid task list", func(t *testing.T) { ctrl := gomock.NewController(t) clientMock := matching.NewMockClient(ctrl) testScope := tally.NewTestScope("", nil) metricsClient := metrics.NewClient(testScope, metrics.ServiceIdx(0), metrics.HistogramMigration{}) clientMock.EXPECT().AddActivityTask(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.AddActivityTaskResponse{}, nil).Times(1) retryableClient := NewMatchingClient( clientMock, metricsClient) _, err := retryableClient.AddActivityTask(context.Background(), &types.AddActivityTaskRequest{ ForwardedFrom: "", TaskList: &types.TaskList{Name: constants.ReservedTaskListPrefix + "test"}, }) assert.NoError(t, err) assert.Len(t, testScope.Snapshot().Counters(), 2, "there should be two counters registered, one for call and one for invalid task list") }) } ================================================ FILE: client/wrappers/metered/sharddistributor_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) // sharddistributorClient implements sharddistributor.Client interface instrumented with retries type sharddistributorClient struct { client sharddistributor.Client metricsClient metrics.Client } // NewShardDistributorClient creates a new instance of sharddistributorClient with retry policy func NewShardDistributorClient(client sharddistributor.Client, metricsClient metrics.Client) sharddistributor.Client { return &sharddistributorClient{ client: client, metricsClient: metricsClient, } } func (c *sharddistributorClient) GetShardOwner(ctx context.Context, gp1 *types.GetShardOwnerRequest, p1 ...yarpc.CallOption) (gp2 *types.GetShardOwnerResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.ShardDistributorClientGetShardOwnerScope) } else { scope = c.metricsClient.Scope(metrics.ShardDistributorClientGetShardOwnerScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) gp2, err = c.client.GetShardOwner(ctx, gp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return gp2, err } func (c *sharddistributorClient) WatchNamespaceState(ctx context.Context, wp1 *types.WatchNamespaceStateRequest, p1 ...yarpc.CallOption) (w1 sharddistributor.WatchNamespaceStateClient, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.ShardDistributorClientWatchNamespaceStateScope) } else { scope = c.metricsClient.Scope(metrics.ShardDistributorClientWatchNamespaceStateScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) w1, err = c.client.WatchNamespaceState(ctx, wp1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return w1, err } ================================================ FILE: client/wrappers/metered/sharddistributorexecutor_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributorexecutor" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) // sharddistributorexecutorClient implements sharddistributorexecutor.Client interface instrumented with retries type sharddistributorexecutorClient struct { client sharddistributorexecutor.Client metricsClient metrics.Client } // NewShardDistributorExecutorClient creates a new instance of sharddistributorexecutorClient with retry policy func NewShardDistributorExecutorClient(client sharddistributorexecutor.Client, metricsClient metrics.Client) sharddistributorexecutor.Client { return &sharddistributorexecutorClient{ client: client, metricsClient: metricsClient, } } func (c *sharddistributorexecutorClient) Heartbeat(ctx context.Context, ep1 *types.ExecutorHeartbeatRequest, p1 ...yarpc.CallOption) (ep2 *types.ExecutorHeartbeatResponse, err error) { retryCount := getRetryCountFromContext(ctx) var scope metrics.Scope if retryCount == -1 { scope = c.metricsClient.Scope(metrics.ShardDistributorExecutorClientHeartbeatScope) } else { scope = c.metricsClient.Scope(metrics.ShardDistributorExecutorClientHeartbeatScope, metrics.IsRetryTag(retryCount > 0)) } scope.IncCounter(metrics.CadenceClientRequests) sw := scope.StartTimer(metrics.CadenceClientLatency) ep2, err = c.client.Heartbeat(ctx, ep1, p1...) sw.Stop() if err != nil { scope.IncCounter(metrics.CadenceClientFailures) } return ep2, err } ================================================ FILE: client/wrappers/retryable/admin_generated.go ================================================ package retryable // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/retry.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/types" ) // adminClient implements admin.Client interface instrumented with retries type adminClient struct { client admin.Client throttleRetry *backoff.ThrottleRetry } // NewAdminClient creates a new instance of adminClient with retry policy func NewAdminClient(client admin.Client, policy backoff.RetryPolicy, isRetryable backoff.IsRetryable) admin.Client { return &adminClient{ client: client, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(isRetryable), ), } } func (c *adminClient) AddSearchAttribute(ctx context.Context, ap1 *types.AddSearchAttributeRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.AddSearchAttribute(ctx, ap1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *adminClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.CloseShard(ctx, cp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *adminClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (cp2 *types.CountDLQMessagesResponse, err error) { var resp *types.CountDLQMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.CountDLQMessages(ctx, cp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) DeleteWorkflow(ctx context.Context, ap1 *types.AdminDeleteWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDeleteWorkflowResponse, err error) { var resp *types.AdminDeleteWorkflowResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DeleteWorkflow(ctx, ap1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) DescribeCluster(ctx context.Context, p1 ...yarpc.CallOption) (dp1 *types.DescribeClusterResponse, err error) { var resp *types.DescribeClusterResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeCluster(ctx, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { var resp *types.DescribeHistoryHostResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeHistoryHost(ctx, dp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { var resp *types.DescribeQueueResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeQueue(ctx, dp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) DescribeShardDistribution(ctx context.Context, dp1 *types.DescribeShardDistributionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeShardDistributionResponse, err error) { var resp *types.DescribeShardDistributionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeShardDistribution(ctx, dp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) DescribeWorkflowExecution(ctx context.Context, ap1 *types.AdminDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDescribeWorkflowExecutionResponse, err error) { var resp *types.AdminDescribeWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeWorkflowExecution(ctx, ap1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { var resp *types.GetDLQReplicationMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetDLQReplicationMessages(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainAsyncWorkflowConfiguratonResponse, err error) { var resp *types.GetDomainAsyncWorkflowConfiguratonResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetDomainAsyncWorkflowConfiguraton(ctx, request, opts...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainIsolationGroupsResponse, err error) { var resp *types.GetDomainIsolationGroupsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetDomainIsolationGroups(ctx, request, opts...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) GetDomainReplicationMessages(ctx context.Context, gp1 *types.GetDomainReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDomainReplicationMessagesResponse, err error) { var resp *types.GetDomainReplicationMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetDomainReplicationMessages(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) GetDynamicConfig(ctx context.Context, gp1 *types.GetDynamicConfigRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDynamicConfigResponse, err error) { var resp *types.GetDynamicConfigResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetDynamicConfig(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetGlobalIsolationGroupsResponse, err error) { var resp *types.GetGlobalIsolationGroupsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetGlobalIsolationGroups(ctx, request, opts...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { var resp *types.GetReplicationMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetReplicationMessages(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) GetWorkflowExecutionRawHistoryV2(ctx context.Context, gp1 *types.GetWorkflowExecutionRawHistoryV2Request, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionRawHistoryV2Response, err error) { var resp *types.GetWorkflowExecutionRawHistoryV2Response op := func(ctx context.Context) error { var err error resp, err = c.client.GetWorkflowExecutionRawHistoryV2(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) ListDynamicConfig(ctx context.Context, lp1 *types.ListDynamicConfigRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDynamicConfigResponse, err error) { var resp *types.ListDynamicConfigResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ListDynamicConfig(ctx, lp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) MaintainCorruptWorkflow(ctx context.Context, ap1 *types.AdminMaintainWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminMaintainWorkflowResponse, err error) { var resp *types.AdminMaintainWorkflowResponse op := func(ctx context.Context) error { var err error resp, err = c.client.MaintainCorruptWorkflow(ctx, ap1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { var resp *types.MergeDLQMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.MergeDLQMessages(ctx, mp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.PurgeDLQMessages(ctx, pp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *adminClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { var resp *types.ReadDLQMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ReadDLQMessages(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) ReapplyEvents(ctx context.Context, rp1 *types.ReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.ReapplyEvents(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *adminClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RefreshWorkflowTasks(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *adminClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RemoveTask(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *adminClient) ResendReplicationTasks(ctx context.Context, rp1 *types.ResendReplicationTasksRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.ResendReplicationTasks(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *adminClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.ResetQueue(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *adminClient) RestoreDynamicConfig(ctx context.Context, rp1 *types.RestoreDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RestoreDynamicConfig(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *adminClient) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { var resp *types.UpdateDomainAsyncWorkflowConfiguratonResponse op := func(ctx context.Context) error { var err error resp, err = c.client.UpdateDomainAsyncWorkflowConfiguraton(ctx, request, opts...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainIsolationGroupsResponse, err error) { var resp *types.UpdateDomainIsolationGroupsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.UpdateDomainIsolationGroups(ctx, request, opts...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) UpdateDynamicConfig(ctx context.Context, up1 *types.UpdateDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.UpdateDynamicConfig(ctx, up1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *adminClient) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateGlobalIsolationGroupsResponse, err error) { var resp *types.UpdateGlobalIsolationGroupsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.UpdateGlobalIsolationGroups(ctx, request, opts...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *adminClient) UpdateTaskListPartitionConfig(ctx context.Context, request *types.UpdateTaskListPartitionConfigRequest, opts ...yarpc.CallOption) (up1 *types.UpdateTaskListPartitionConfigResponse, err error) { var resp *types.UpdateTaskListPartitionConfigResponse op := func(ctx context.Context) error { var err error resp, err = c.client.UpdateTaskListPartitionConfig(ctx, request, opts...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } ================================================ FILE: client/wrappers/retryable/frontend_generated.go ================================================ package retryable // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/retry.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/types" ) // frontendClient implements frontend.Client interface instrumented with retries type frontendClient struct { client frontend.Client throttleRetry *backoff.ThrottleRetry } // NewFrontendClient creates a new instance of frontendClient with retry policy func NewFrontendClient(client frontend.Client, policy backoff.RetryPolicy, isRetryable backoff.IsRetryable) frontend.Client { return &frontendClient{ client: client, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(isRetryable), ), } } func (c *frontendClient) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (cp2 *types.CountWorkflowExecutionsResponse, err error) { var resp *types.CountWorkflowExecutionsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.CountWorkflowExecutions(ctx, cp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.DeleteDomain(ctx, dp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.DeprecateDomain(ctx, dp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeDomainResponse, err error) { var resp *types.DescribeDomainResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeDomain(ctx, dp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeTaskListResponse, err error) { var resp *types.DescribeTaskListResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeTaskList(ctx, dp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { var resp *types.DescribeWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeWorkflowExecution(ctx, dp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { var resp *types.DiagnoseWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DiagnoseWorkflowExecution(ctx, dp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest, p1 ...yarpc.CallOption) (fp2 *types.FailoverDomainResponse, err error) { var resp *types.FailoverDomainResponse op := func(ctx context.Context) error { var err error resp, err = c.client.FailoverDomain(ctx, fp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) GetClusterInfo(ctx context.Context, p1 ...yarpc.CallOption) (cp1 *types.ClusterInfo, err error) { var resp *types.ClusterInfo op := func(ctx context.Context) error { var err error resp, err = c.client.GetClusterInfo(ctx, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) GetSearchAttributes(ctx context.Context, p1 ...yarpc.CallOption) (gp1 *types.GetSearchAttributesResponse, err error) { var resp *types.GetSearchAttributesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetSearchAttributes(ctx, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { var resp *types.GetTaskListsByDomainResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetTaskListsByDomain(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { var resp *types.GetWorkflowExecutionHistoryResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetWorkflowExecutionHistory(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { var resp *types.ListArchivedWorkflowExecutionsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ListArchivedWorkflowExecutions(ctx, lp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { var resp *types.ListClosedWorkflowExecutionsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ListClosedWorkflowExecutions(ctx, lp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDomainsResponse, err error) { var resp *types.ListDomainsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ListDomains(ctx, lp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest, p1 ...yarpc.CallOption) (lp2 *types.ListFailoverHistoryResponse, err error) { var resp *types.ListFailoverHistoryResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ListFailoverHistory(ctx, lp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { var resp *types.ListOpenWorkflowExecutionsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ListOpenWorkflowExecutions(ctx, lp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListTaskListPartitionsResponse, err error) { var resp *types.ListTaskListPartitionsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ListTaskListPartitions(ctx, lp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { var resp *types.ListWorkflowExecutionsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ListWorkflowExecutions(ctx, lp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForActivityTaskResponse, err error) { var resp *types.PollForActivityTaskResponse op := func(ctx context.Context) error { var err error resp, err = c.client.PollForActivityTask(ctx, pp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForDecisionTaskResponse, err error) { var resp *types.PollForDecisionTaskResponse op := func(ctx context.Context) error { var err error resp, err = c.client.PollForDecisionTask(ctx, pp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest, p1 ...yarpc.CallOption) (qp2 *types.QueryWorkflowResponse, err error) { var resp *types.QueryWorkflowResponse op := func(ctx context.Context) error { var err error resp, err = c.client.QueryWorkflow(ctx, qp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { var resp *types.RecordActivityTaskHeartbeatResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RecordActivityTaskHeartbeat(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { var resp *types.RecordActivityTaskHeartbeatResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RecordActivityTaskHeartbeatByID(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RefreshWorkflowTasks(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RegisterDomain(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RequestCancelWorkflowExecution(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetStickyTaskListResponse, err error) { var resp *types.ResetStickyTaskListResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ResetStickyTaskList(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetWorkflowExecutionResponse, err error) { var resp *types.ResetWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ResetWorkflowExecution(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondActivityTaskCanceled(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondActivityTaskCanceledByID(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondActivityTaskCompleted(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondActivityTaskCompletedByID(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondActivityTaskFailed(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondActivityTaskFailedByID(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { var resp *types.RespondDecisionTaskCompletedResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RespondDecisionTaskCompleted(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondDecisionTaskFailed(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondQueryTaskCompleted(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.RestartWorkflowExecutionResponse, err error) { var resp *types.RestartWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RestartWorkflowExecution(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { var resp *types.ListWorkflowExecutionsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ScanWorkflowExecutions(ctx, lp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { var resp *types.StartWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.SignalWithStartWorkflowExecution(ctx, sp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { var resp *types.SignalWithStartWorkflowExecutionAsyncResponse op := func(ctx context.Context) error { var err error resp, err = c.client.SignalWithStartWorkflowExecutionAsync(ctx, sp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.SignalWorkflowExecution(ctx, sp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { var resp *types.StartWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.StartWorkflowExecution(ctx, sp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { var resp *types.StartWorkflowExecutionAsyncResponse op := func(ctx context.Context) error { var err error resp, err = c.client.StartWorkflowExecutionAsync(ctx, sp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *frontendClient) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.TerminateWorkflowExecution(ctx, tp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *frontendClient) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest, p1 ...yarpc.CallOption) (up2 *types.UpdateDomainResponse, err error) { var resp *types.UpdateDomainResponse op := func(ctx context.Context) error { var err error resp, err = c.client.UpdateDomain(ctx, up1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } ================================================ FILE: client/wrappers/retryable/history_generated.go ================================================ package retryable // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/retry.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/types" ) // historyClient implements history.Client interface instrumented with retries type historyClient struct { client history.Client throttleRetry *backoff.ThrottleRetry } // NewHistoryClient creates a new instance of historyClient with retry policy func NewHistoryClient(client history.Client, policy backoff.RetryPolicy, isRetryable backoff.IsRetryable) history.Client { return &historyClient{ client: client, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(isRetryable), ), } } func (c *historyClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.CloseShard(ctx, cp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (hp1 *types.HistoryCountDLQMessagesResponse, err error) { var resp *types.HistoryCountDLQMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.CountDLQMessages(ctx, cp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { var resp *types.DescribeHistoryHostResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeHistoryHost(ctx, dp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) DescribeMutableState(ctx context.Context, dp1 *types.DescribeMutableStateRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeMutableStateResponse, err error) { var resp *types.DescribeMutableStateResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeMutableState(ctx, dp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { var resp *types.DescribeQueueResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeQueue(ctx, dp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) DescribeWorkflowExecution(ctx context.Context, hp1 *types.HistoryDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeWorkflowExecutionResponse, err error) { var resp *types.DescribeWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeWorkflowExecution(ctx, hp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) GetCrossClusterTasks(ctx context.Context, gp1 *types.GetCrossClusterTasksRequest, p1 ...yarpc.CallOption) (gp2 *types.GetCrossClusterTasksResponse, err error) { var resp *types.GetCrossClusterTasksResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetCrossClusterTasks(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { var resp *types.GetDLQReplicationMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetDLQReplicationMessages(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) GetFailoverInfo(ctx context.Context, gp1 *types.GetFailoverInfoRequest, p1 ...yarpc.CallOption) (gp2 *types.GetFailoverInfoResponse, err error) { var resp *types.GetFailoverInfoResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetFailoverInfo(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) GetMutableState(ctx context.Context, gp1 *types.GetMutableStateRequest, p1 ...yarpc.CallOption) (gp2 *types.GetMutableStateResponse, err error) { var resp *types.GetMutableStateResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetMutableState(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { var resp *types.GetReplicationMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetReplicationMessages(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { var resp *types.MergeDLQMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.MergeDLQMessages(ctx, mp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) NotifyFailoverMarkers(ctx context.Context, np1 *types.NotifyFailoverMarkersRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.NotifyFailoverMarkers(ctx, np1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) PollMutableState(ctx context.Context, pp1 *types.PollMutableStateRequest, p1 ...yarpc.CallOption) (pp2 *types.PollMutableStateResponse, err error) { var resp *types.PollMutableStateResponse op := func(ctx context.Context) error { var err error resp, err = c.client.PollMutableState(ctx, pp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.PurgeDLQMessages(ctx, pp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) QueryWorkflow(ctx context.Context, hp1 *types.HistoryQueryWorkflowRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryQueryWorkflowResponse, err error) { var resp *types.HistoryQueryWorkflowResponse op := func(ctx context.Context) error { var err error resp, err = c.client.QueryWorkflow(ctx, hp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) RatelimitUpdate(ctx context.Context, request *types.RatelimitUpdateRequest, opts ...yarpc.CallOption) (rp1 *types.RatelimitUpdateResponse, err error) { var resp *types.RatelimitUpdateResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RatelimitUpdate(ctx, request, opts...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { var resp *types.ReadDLQMessagesResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ReadDLQMessages(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) ReapplyEvents(ctx context.Context, hp1 *types.HistoryReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.ReapplyEvents(ctx, hp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) RecordActivityTaskHeartbeat(ctx context.Context, hp1 *types.HistoryRecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp1 *types.RecordActivityTaskHeartbeatResponse, err error) { var resp *types.RecordActivityTaskHeartbeatResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RecordActivityTaskHeartbeat(ctx, hp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) RecordActivityTaskStarted(ctx context.Context, rp1 *types.RecordActivityTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskStartedResponse, err error) { var resp *types.RecordActivityTaskStartedResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RecordActivityTaskStarted(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) RecordChildExecutionCompleted(ctx context.Context, rp1 *types.RecordChildExecutionCompletedRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RecordChildExecutionCompleted(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) RecordDecisionTaskStarted(ctx context.Context, rp1 *types.RecordDecisionTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordDecisionTaskStartedResponse, err error) { var resp *types.RecordDecisionTaskStartedResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RecordDecisionTaskStarted(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) RefreshWorkflowTasks(ctx context.Context, hp1 *types.HistoryRefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RefreshWorkflowTasks(ctx, hp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) RemoveSignalMutableState(ctx context.Context, rp1 *types.RemoveSignalMutableStateRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RemoveSignalMutableState(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RemoveTask(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) ReplicateEventsV2(ctx context.Context, rp1 *types.ReplicateEventsV2Request, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.ReplicateEventsV2(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) RequestCancelWorkflowExecution(ctx context.Context, hp1 *types.HistoryRequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RequestCancelWorkflowExecution(ctx, hp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.ResetQueue(ctx, rp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) ResetStickyTaskList(ctx context.Context, hp1 *types.HistoryResetStickyTaskListRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryResetStickyTaskListResponse, err error) { var resp *types.HistoryResetStickyTaskListResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ResetStickyTaskList(ctx, hp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) ResetWorkflowExecution(ctx context.Context, hp1 *types.HistoryResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp1 *types.ResetWorkflowExecutionResponse, err error) { var resp *types.ResetWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ResetWorkflowExecution(ctx, hp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) RespondActivityTaskCanceled(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondActivityTaskCanceled(ctx, hp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) RespondActivityTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondActivityTaskCompleted(ctx, hp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) RespondActivityTaskFailed(ctx context.Context, hp1 *types.HistoryRespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondActivityTaskFailed(ctx, hp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) RespondCrossClusterTasksCompleted(ctx context.Context, rp1 *types.RespondCrossClusterTasksCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondCrossClusterTasksCompletedResponse, err error) { var resp *types.RespondCrossClusterTasksCompletedResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RespondCrossClusterTasksCompleted(ctx, rp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) RespondDecisionTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryRespondDecisionTaskCompletedResponse, err error) { var resp *types.HistoryRespondDecisionTaskCompletedResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RespondDecisionTaskCompleted(ctx, hp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) RespondDecisionTaskFailed(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondDecisionTaskFailed(ctx, hp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) ScheduleDecisionTask(ctx context.Context, sp1 *types.ScheduleDecisionTaskRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.ScheduleDecisionTask(ctx, sp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) SignalWithStartWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { var resp *types.StartWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.SignalWithStartWorkflowExecution(ctx, hp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) SignalWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.SignalWorkflowExecution(ctx, hp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) StartWorkflowExecution(ctx context.Context, hp1 *types.HistoryStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { var resp *types.StartWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = c.client.StartWorkflowExecution(ctx, hp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *historyClient) SyncActivity(ctx context.Context, sp1 *types.SyncActivityRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.SyncActivity(ctx, sp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) SyncShardStatus(ctx context.Context, sp1 *types.SyncShardStatusRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.SyncShardStatus(ctx, sp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *historyClient) TerminateWorkflowExecution(ctx context.Context, hp1 *types.HistoryTerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.TerminateWorkflowExecution(ctx, hp1, p1...) } return c.throttleRetry.Do(ctx, op) } ================================================ FILE: client/wrappers/retryable/matching_generated.go ================================================ package retryable // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/retry.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/types" ) // matchingClient implements matching.Client interface instrumented with retries type matchingClient struct { client matching.Client throttleRetry *backoff.ThrottleRetry } // NewMatchingClient creates a new instance of matchingClient with retry policy func NewMatchingClient(client matching.Client, policy backoff.RetryPolicy, isRetryable backoff.IsRetryable) matching.Client { return &matchingClient{ client: client, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(isRetryable), ), } } func (c *matchingClient) AddActivityTask(ctx context.Context, ap1 *types.AddActivityTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddActivityTaskResponse, err error) { var resp *types.AddActivityTaskResponse op := func(ctx context.Context) error { var err error resp, err = c.client.AddActivityTask(ctx, ap1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *matchingClient) AddDecisionTask(ctx context.Context, ap1 *types.AddDecisionTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddDecisionTaskResponse, err error) { var resp *types.AddDecisionTaskResponse op := func(ctx context.Context) error { var err error resp, err = c.client.AddDecisionTask(ctx, ap1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *matchingClient) CancelOutstandingPoll(ctx context.Context, cp1 *types.CancelOutstandingPollRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.CancelOutstandingPoll(ctx, cp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *matchingClient) DescribeTaskList(ctx context.Context, mp1 *types.MatchingDescribeTaskListRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeTaskListResponse, err error) { var resp *types.DescribeTaskListResponse op := func(ctx context.Context) error { var err error resp, err = c.client.DescribeTaskList(ctx, mp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *matchingClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { var resp *types.GetTaskListsByDomainResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetTaskListsByDomain(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *matchingClient) ListTaskListPartitions(ctx context.Context, mp1 *types.MatchingListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp1 *types.ListTaskListPartitionsResponse, err error) { var resp *types.ListTaskListPartitionsResponse op := func(ctx context.Context) error { var err error resp, err = c.client.ListTaskListPartitions(ctx, mp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *matchingClient) PollForActivityTask(ctx context.Context, mp1 *types.MatchingPollForActivityTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForActivityTaskResponse, err error) { var resp *types.MatchingPollForActivityTaskResponse op := func(ctx context.Context) error { var err error resp, err = c.client.PollForActivityTask(ctx, mp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *matchingClient) PollForDecisionTask(ctx context.Context, mp1 *types.MatchingPollForDecisionTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForDecisionTaskResponse, err error) { var resp *types.MatchingPollForDecisionTaskResponse op := func(ctx context.Context) error { var err error resp, err = c.client.PollForDecisionTask(ctx, mp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQueryWorkflowRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingQueryWorkflowResponse, err error) { var resp *types.MatchingQueryWorkflowResponse op := func(ctx context.Context) error { var err error resp, err = c.client.QueryWorkflow(ctx, mp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { var resp *types.MatchingRefreshTaskListPartitionConfigResponse op := func(ctx context.Context) error { var err error resp, err = c.client.RefreshTaskListPartitionConfig(ctx, mp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { op := func(ctx context.Context) error { return c.client.RespondQueryTaskCompleted(ctx, mp1, p1...) } return c.throttleRetry.Do(ctx, op) } func (c *matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { var resp *types.MatchingUpdateTaskListPartitionConfigResponse op := func(ctx context.Context) error { var err error resp, err = c.client.UpdateTaskListPartitionConfig(ctx, mp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } ================================================ FILE: client/wrappers/retryable/sharddistributor_generated.go ================================================ package retryable // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/retry.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/types" ) // sharddistributorClient implements sharddistributor.Client interface instrumented with retries type sharddistributorClient struct { client sharddistributor.Client throttleRetry *backoff.ThrottleRetry } // NewShardDistributorClient creates a new instance of sharddistributorClient with retry policy func NewShardDistributorClient(client sharddistributor.Client, policy backoff.RetryPolicy, isRetryable backoff.IsRetryable) sharddistributor.Client { return &sharddistributorClient{ client: client, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(isRetryable), ), } } func (c *sharddistributorClient) GetShardOwner(ctx context.Context, gp1 *types.GetShardOwnerRequest, p1 ...yarpc.CallOption) (gp2 *types.GetShardOwnerResponse, err error) { var resp *types.GetShardOwnerResponse op := func(ctx context.Context) error { var err error resp, err = c.client.GetShardOwner(ctx, gp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } func (c *sharddistributorClient) WatchNamespaceState(ctx context.Context, wp1 *types.WatchNamespaceStateRequest, p1 ...yarpc.CallOption) (w1 sharddistributor.WatchNamespaceStateClient, err error) { var resp sharddistributor.WatchNamespaceStateClient op := func(ctx context.Context) error { var err error resp, err = c.client.WatchNamespaceState(ctx, wp1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } ================================================ FILE: client/wrappers/retryable/sharddistributorexecutor_generated.go ================================================ package retryable // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/retry.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributorexecutor" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/types" ) // sharddistributorexecutorClient implements sharddistributorexecutor.Client interface instrumented with retries type sharddistributorexecutorClient struct { client sharddistributorexecutor.Client throttleRetry *backoff.ThrottleRetry } // NewShardDistributorExecutorClient creates a new instance of sharddistributorexecutorClient with retry policy func NewShardDistributorExecutorClient(client sharddistributorexecutor.Client, policy backoff.RetryPolicy, isRetryable backoff.IsRetryable) sharddistributorexecutor.Client { return &sharddistributorexecutorClient{ client: client, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(isRetryable), ), } } func (c *sharddistributorexecutorClient) Heartbeat(ctx context.Context, ep1 *types.ExecutorHeartbeatRequest, p1 ...yarpc.CallOption) (ep2 *types.ExecutorHeartbeatResponse, err error) { var resp *types.ExecutorHeartbeatResponse op := func(ctx context.Context) error { var err error resp, err = c.client.Heartbeat(ctx, ep1, p1...) return err } err = c.throttleRetry.Do(ctx, op) return resp, err } ================================================ FILE: client/wrappers/retryable/wrappers_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package retryable import ( "context" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) func TestFrontendClientRetryableError(t *testing.T) { ctrl := gomock.NewController(t) clientMock := frontend.NewMockClient(ctrl) // One failure, one success clientMock.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, &types.ServiceBusyError{ Message: "error", }).Times(1) clientMock.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, nil).Times(1) retryableClient := NewFrontendClient( clientMock, common.CreateFrontendServiceRetryPolicy(), common.IsServiceBusyError) _, err := retryableClient.CountWorkflowExecutions(context.Background(), &types.CountWorkflowExecutionsRequest{}) assert.NoError(t, err) } func TestFrontendClientNonRetryableError(t *testing.T) { ctrl := gomock.NewController(t) clientMock := frontend.NewMockClient(ctrl) // One failure, one success clientMock.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, &types.BadRequestError{ Message: "error", }).Times(1) retryableClient := NewFrontendClient( clientMock, common.CreateFrontendServiceRetryPolicy(), common.IsServiceBusyError) _, err := retryableClient.CountWorkflowExecutions(context.Background(), &types.CountWorkflowExecutionsRequest{}) assert.Error(t, err) } ================================================ FILE: client/wrappers/thrift/admin_generated.go ================================================ package thrift // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/thrift.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) func (g adminClient) AddSearchAttribute(ctx context.Context, ap1 *types.AddSearchAttributeRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.AddSearchAttribute(ctx, thrift.FromAdminAddSearchAttributeRequest(ap1), p1...) return thrift.ToError(err) } func (g adminClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.CloseShard(ctx, thrift.FromAdminCloseShardRequest(cp1), p1...) return thrift.ToError(err) } func (g adminClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (cp2 *types.CountDLQMessagesResponse, err error) { return nil, thrift.ToError(&types.BadRequestError{Message: "Feature not supported on TChannel"}) } func (g adminClient) DeleteWorkflow(ctx context.Context, ap1 *types.AdminDeleteWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDeleteWorkflowResponse, err error) { response, err := g.c.DeleteWorkflow(ctx, thrift.FromAdminDeleteWorkflowRequest(ap1), p1...) return thrift.ToAdminDeleteWorkflowResponse(response), thrift.ToError(err) } func (g adminClient) DescribeCluster(ctx context.Context, p1 ...yarpc.CallOption) (dp1 *types.DescribeClusterResponse, err error) { response, err := g.c.DescribeCluster(ctx, p1...) return thrift.ToAdminDescribeClusterResponse(response), thrift.ToError(err) } func (g adminClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { response, err := g.c.DescribeHistoryHost(ctx, thrift.FromAdminDescribeHistoryHostRequest(dp1), p1...) return thrift.ToAdminDescribeHistoryHostResponse(response), thrift.ToError(err) } func (g adminClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { response, err := g.c.DescribeQueue(ctx, thrift.FromAdminDescribeQueueRequest(dp1), p1...) return thrift.ToAdminDescribeQueueResponse(response), thrift.ToError(err) } func (g adminClient) DescribeShardDistribution(ctx context.Context, dp1 *types.DescribeShardDistributionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeShardDistributionResponse, err error) { response, err := g.c.DescribeShardDistribution(ctx, thrift.FromAdminDescribeShardDistributionRequest(dp1), p1...) return thrift.ToAdminDescribeShardDistributionResponse(response), thrift.ToError(err) } func (g adminClient) DescribeWorkflowExecution(ctx context.Context, ap1 *types.AdminDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDescribeWorkflowExecutionResponse, err error) { response, err := g.c.DescribeWorkflowExecution(ctx, thrift.FromAdminDescribeWorkflowExecutionRequest(ap1), p1...) return thrift.ToAdminDescribeWorkflowExecutionResponse(response), thrift.ToError(err) } func (g adminClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { response, err := g.c.GetDLQReplicationMessages(ctx, thrift.FromAdminGetDLQReplicationMessagesRequest(gp1), p1...) return thrift.ToAdminGetDLQReplicationMessagesResponse(response), thrift.ToError(err) } func (g adminClient) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainAsyncWorkflowConfiguratonResponse, err error) { response, err := g.c.GetDomainAsyncWorkflowConfiguraton(ctx, thrift.FromAdminGetDomainAsyncWorkflowConfiguratonRequest(request), opts...) return thrift.ToAdminGetDomainAsyncWorkflowConfiguratonResponse(response), thrift.ToError(err) } func (g adminClient) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainIsolationGroupsResponse, err error) { response, err := g.c.GetDomainIsolationGroups(ctx, thrift.FromAdminGetDomainIsolationGroupsRequest(request), opts...) return thrift.ToAdminGetDomainIsolationGroupsResponse(response), thrift.ToError(err) } func (g adminClient) GetDomainReplicationMessages(ctx context.Context, gp1 *types.GetDomainReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDomainReplicationMessagesResponse, err error) { response, err := g.c.GetDomainReplicationMessages(ctx, thrift.FromAdminGetDomainReplicationMessagesRequest(gp1), p1...) return thrift.ToAdminGetDomainReplicationMessagesResponse(response), thrift.ToError(err) } func (g adminClient) GetDynamicConfig(ctx context.Context, gp1 *types.GetDynamicConfigRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDynamicConfigResponse, err error) { response, err := g.c.GetDynamicConfig(ctx, thrift.FromAdminGetDynamicConfigRequest(gp1), p1...) return thrift.ToAdminGetDynamicConfigResponse(response), thrift.ToError(err) } func (g adminClient) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetGlobalIsolationGroupsResponse, err error) { response, err := g.c.GetGlobalIsolationGroups(ctx, thrift.FromAdminGetGlobalIsolationGroupsRequest(request), opts...) return thrift.ToAdminGetGlobalIsolationGroupsResponse(response), thrift.ToError(err) } func (g adminClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { response, err := g.c.GetReplicationMessages(ctx, thrift.FromAdminGetReplicationMessagesRequest(gp1), p1...) return thrift.ToAdminGetReplicationMessagesResponse(response), thrift.ToError(err) } func (g adminClient) GetWorkflowExecutionRawHistoryV2(ctx context.Context, gp1 *types.GetWorkflowExecutionRawHistoryV2Request, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionRawHistoryV2Response, err error) { response, err := g.c.GetWorkflowExecutionRawHistoryV2(ctx, thrift.FromAdminGetWorkflowExecutionRawHistoryV2Request(gp1), p1...) return thrift.ToAdminGetWorkflowExecutionRawHistoryV2Response(response), thrift.ToError(err) } func (g adminClient) ListDynamicConfig(ctx context.Context, lp1 *types.ListDynamicConfigRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDynamicConfigResponse, err error) { response, err := g.c.ListDynamicConfig(ctx, thrift.FromAdminListDynamicConfigRequest(lp1), p1...) return thrift.ToAdminListDynamicConfigResponse(response), thrift.ToError(err) } func (g adminClient) MaintainCorruptWorkflow(ctx context.Context, ap1 *types.AdminMaintainWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminMaintainWorkflowResponse, err error) { response, err := g.c.MaintainCorruptWorkflow(ctx, thrift.FromAdminMaintainCorruptWorkflowRequest(ap1), p1...) return thrift.ToAdminMaintainCorruptWorkflowResponse(response), thrift.ToError(err) } func (g adminClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { response, err := g.c.MergeDLQMessages(ctx, thrift.FromAdminMergeDLQMessagesRequest(mp1), p1...) return thrift.ToAdminMergeDLQMessagesResponse(response), thrift.ToError(err) } func (g adminClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.PurgeDLQMessages(ctx, thrift.FromAdminPurgeDLQMessagesRequest(pp1), p1...) return thrift.ToError(err) } func (g adminClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { response, err := g.c.ReadDLQMessages(ctx, thrift.FromAdminReadDLQMessagesRequest(rp1), p1...) return thrift.ToAdminReadDLQMessagesResponse(response), thrift.ToError(err) } func (g adminClient) ReapplyEvents(ctx context.Context, rp1 *types.ReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.ReapplyEvents(ctx, thrift.FromAdminReapplyEventsRequest(rp1), p1...) return thrift.ToError(err) } func (g adminClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RefreshWorkflowTasks(ctx, thrift.FromAdminRefreshWorkflowTasksRequest(rp1), p1...) return thrift.ToError(err) } func (g adminClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RemoveTask(ctx, thrift.FromAdminRemoveTaskRequest(rp1), p1...) return thrift.ToError(err) } func (g adminClient) ResendReplicationTasks(ctx context.Context, rp1 *types.ResendReplicationTasksRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.ResendReplicationTasks(ctx, thrift.FromAdminResendReplicationTasksRequest(rp1), p1...) return thrift.ToError(err) } func (g adminClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.ResetQueue(ctx, thrift.FromAdminResetQueueRequest(rp1), p1...) return thrift.ToError(err) } func (g adminClient) RestoreDynamicConfig(ctx context.Context, rp1 *types.RestoreDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RestoreDynamicConfig(ctx, thrift.FromAdminRestoreDynamicConfigRequest(rp1), p1...) return thrift.ToError(err) } func (g adminClient) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { response, err := g.c.UpdateDomainAsyncWorkflowConfiguraton(ctx, thrift.FromAdminUpdateDomainAsyncWorkflowConfiguratonRequest(request), opts...) return thrift.ToAdminUpdateDomainAsyncWorkflowConfiguratonResponse(response), thrift.ToError(err) } func (g adminClient) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainIsolationGroupsResponse, err error) { response, err := g.c.UpdateDomainIsolationGroups(ctx, thrift.FromAdminUpdateDomainIsolationGroupsRequest(request), opts...) return thrift.ToAdminUpdateDomainIsolationGroupsResponse(response), thrift.ToError(err) } func (g adminClient) UpdateDynamicConfig(ctx context.Context, up1 *types.UpdateDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.UpdateDynamicConfig(ctx, thrift.FromAdminUpdateDynamicConfigRequest(up1), p1...) return thrift.ToError(err) } func (g adminClient) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateGlobalIsolationGroupsResponse, err error) { response, err := g.c.UpdateGlobalIsolationGroups(ctx, thrift.FromAdminUpdateGlobalIsolationGroupsRequest(request), opts...) return thrift.ToAdminUpdateGlobalIsolationGroupsResponse(response), thrift.ToError(err) } func (g adminClient) UpdateTaskListPartitionConfig(ctx context.Context, request *types.UpdateTaskListPartitionConfigRequest, opts ...yarpc.CallOption) (up1 *types.UpdateTaskListPartitionConfigResponse, err error) { return nil, thrift.ToError(&types.BadRequestError{Message: "Feature not supported on TChannel"}) } ================================================ FILE: client/wrappers/thrift/constructor.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package thrift import ( "github.com/uber/cadence/.gen/go/admin/adminserviceclient" "github.com/uber/cadence/.gen/go/cadence/workflowserviceclient" "github.com/uber/cadence/.gen/go/history/historyserviceclient" "github.com/uber/cadence/.gen/go/matching/matchingserviceclient" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" ) type ( adminClient struct { c adminserviceclient.Interface } frontendClient struct { c workflowserviceclient.Interface } historyClient struct { c historyserviceclient.Interface } matchingClient struct { c matchingserviceclient.Interface } ) func NewAdminClient(c adminserviceclient.Interface) admin.Client { return adminClient{c} } func NewFrontendClient(c workflowserviceclient.Interface) frontend.Client { return frontendClient{c} } func NewHistoryClient(c historyserviceclient.Interface) history.Client { return historyClient{c} } func NewMatchingClient(c matchingserviceclient.Interface) matching.Client { return matchingClient{c} } ================================================ FILE: client/wrappers/thrift/frontend_generated.go ================================================ package thrift // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/thrift.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) func (g frontendClient) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (cp2 *types.CountWorkflowExecutionsResponse, err error) { response, err := g.c.CountWorkflowExecutions(ctx, thrift.FromCountWorkflowExecutionsRequest(cp1), p1...) return thrift.ToCountWorkflowExecutionsResponse(response), thrift.ToError(err) } func (g frontendClient) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.DeleteDomain(ctx, thrift.FromDeleteDomainRequest(dp1), p1...) return thrift.ToError(err) } func (g frontendClient) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.DeprecateDomain(ctx, thrift.FromDeprecateDomainRequest(dp1), p1...) return thrift.ToError(err) } func (g frontendClient) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeDomainResponse, err error) { response, err := g.c.DescribeDomain(ctx, thrift.FromDescribeDomainRequest(dp1), p1...) return thrift.ToDescribeDomainResponse(response), thrift.ToError(err) } func (g frontendClient) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeTaskListResponse, err error) { response, err := g.c.DescribeTaskList(ctx, thrift.FromDescribeTaskListRequest(dp1), p1...) return thrift.ToDescribeTaskListResponse(response), thrift.ToError(err) } func (g frontendClient) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { response, err := g.c.DescribeWorkflowExecution(ctx, thrift.FromDescribeWorkflowExecutionRequest(dp1), p1...) return thrift.ToDescribeWorkflowExecutionResponse(response), thrift.ToError(err) } func (g frontendClient) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { response, err := g.c.DiagnoseWorkflowExecution(ctx, thrift.FromDiagnoseWorkflowExecutionRequest(dp1), p1...) return thrift.ToDiagnoseWorkflowExecutionResponse(response), thrift.ToError(err) } func (g frontendClient) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest, p1 ...yarpc.CallOption) (fp2 *types.FailoverDomainResponse, err error) { response, err := g.c.FailoverDomain(ctx, thrift.FromFailoverDomainRequest(fp1), p1...) return thrift.ToFailoverDomainResponse(response), thrift.ToError(err) } func (g frontendClient) GetClusterInfo(ctx context.Context, p1 ...yarpc.CallOption) (cp1 *types.ClusterInfo, err error) { response, err := g.c.GetClusterInfo(ctx, p1...) return thrift.ToGetClusterInfoResponse(response), thrift.ToError(err) } func (g frontendClient) GetSearchAttributes(ctx context.Context, p1 ...yarpc.CallOption) (gp1 *types.GetSearchAttributesResponse, err error) { response, err := g.c.GetSearchAttributes(ctx, p1...) return thrift.ToGetSearchAttributesResponse(response), thrift.ToError(err) } func (g frontendClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { response, err := g.c.GetTaskListsByDomain(ctx, thrift.FromGetTaskListsByDomainRequest(gp1), p1...) return thrift.ToGetTaskListsByDomainResponse(response), thrift.ToError(err) } func (g frontendClient) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { response, err := g.c.GetWorkflowExecutionHistory(ctx, thrift.FromGetWorkflowExecutionHistoryRequest(gp1), p1...) return thrift.ToGetWorkflowExecutionHistoryResponse(response), thrift.ToError(err) } func (g frontendClient) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { response, err := g.c.ListArchivedWorkflowExecutions(ctx, thrift.FromListArchivedWorkflowExecutionsRequest(lp1), p1...) return thrift.ToListArchivedWorkflowExecutionsResponse(response), thrift.ToError(err) } func (g frontendClient) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { response, err := g.c.ListClosedWorkflowExecutions(ctx, thrift.FromListClosedWorkflowExecutionsRequest(lp1), p1...) return thrift.ToListClosedWorkflowExecutionsResponse(response), thrift.ToError(err) } func (g frontendClient) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDomainsResponse, err error) { response, err := g.c.ListDomains(ctx, thrift.FromListDomainsRequest(lp1), p1...) return thrift.ToListDomainsResponse(response), thrift.ToError(err) } func (g frontendClient) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest, p1 ...yarpc.CallOption) (lp2 *types.ListFailoverHistoryResponse, err error) { response, err := g.c.ListFailoverHistory(ctx, thrift.FromListFailoverHistoryRequest(lp1), p1...) return thrift.ToListFailoverHistoryResponse(response), thrift.ToError(err) } func (g frontendClient) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { response, err := g.c.ListOpenWorkflowExecutions(ctx, thrift.FromListOpenWorkflowExecutionsRequest(lp1), p1...) return thrift.ToListOpenWorkflowExecutionsResponse(response), thrift.ToError(err) } func (g frontendClient) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListTaskListPartitionsResponse, err error) { response, err := g.c.ListTaskListPartitions(ctx, thrift.FromListTaskListPartitionsRequest(lp1), p1...) return thrift.ToListTaskListPartitionsResponse(response), thrift.ToError(err) } func (g frontendClient) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { response, err := g.c.ListWorkflowExecutions(ctx, thrift.FromListWorkflowExecutionsRequest(lp1), p1...) return thrift.ToListWorkflowExecutionsResponse(response), thrift.ToError(err) } func (g frontendClient) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForActivityTaskResponse, err error) { response, err := g.c.PollForActivityTask(ctx, thrift.FromPollForActivityTaskRequest(pp1), p1...) return thrift.ToPollForActivityTaskResponse(response), thrift.ToError(err) } func (g frontendClient) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForDecisionTaskResponse, err error) { response, err := g.c.PollForDecisionTask(ctx, thrift.FromPollForDecisionTaskRequest(pp1), p1...) return thrift.ToPollForDecisionTaskResponse(response), thrift.ToError(err) } func (g frontendClient) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest, p1 ...yarpc.CallOption) (qp2 *types.QueryWorkflowResponse, err error) { response, err := g.c.QueryWorkflow(ctx, thrift.FromQueryWorkflowRequest(qp1), p1...) return thrift.ToQueryWorkflowResponse(response), thrift.ToError(err) } func (g frontendClient) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { response, err := g.c.RecordActivityTaskHeartbeat(ctx, thrift.FromRecordActivityTaskHeartbeatRequest(rp1), p1...) return thrift.ToRecordActivityTaskHeartbeatResponse(response), thrift.ToError(err) } func (g frontendClient) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { response, err := g.c.RecordActivityTaskHeartbeatByID(ctx, thrift.FromRecordActivityTaskHeartbeatByIDRequest(rp1), p1...) return thrift.ToRecordActivityTaskHeartbeatByIDResponse(response), thrift.ToError(err) } func (g frontendClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RefreshWorkflowTasks(ctx, thrift.FromRefreshWorkflowTasksRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RegisterDomain(ctx, thrift.FromRegisterDomainRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RequestCancelWorkflowExecution(ctx, thrift.FromRequestCancelWorkflowExecutionRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetStickyTaskListResponse, err error) { response, err := g.c.ResetStickyTaskList(ctx, thrift.FromResetStickyTaskListRequest(rp1), p1...) return thrift.ToResetStickyTaskListResponse(response), thrift.ToError(err) } func (g frontendClient) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetWorkflowExecutionResponse, err error) { response, err := g.c.ResetWorkflowExecution(ctx, thrift.FromResetWorkflowExecutionRequest(rp1), p1...) return thrift.ToResetWorkflowExecutionResponse(response), thrift.ToError(err) } func (g frontendClient) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondActivityTaskCanceled(ctx, thrift.FromRespondActivityTaskCanceledRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondActivityTaskCanceledByID(ctx, thrift.FromRespondActivityTaskCanceledByIDRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondActivityTaskCompleted(ctx, thrift.FromRespondActivityTaskCompletedRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondActivityTaskCompletedByID(ctx, thrift.FromRespondActivityTaskCompletedByIDRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondActivityTaskFailed(ctx, thrift.FromRespondActivityTaskFailedRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondActivityTaskFailedByID(ctx, thrift.FromRespondActivityTaskFailedByIDRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { response, err := g.c.RespondDecisionTaskCompleted(ctx, thrift.FromRespondDecisionTaskCompletedRequest(rp1), p1...) return thrift.ToRespondDecisionTaskCompletedResponse(response), thrift.ToError(err) } func (g frontendClient) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondDecisionTaskFailed(ctx, thrift.FromRespondDecisionTaskFailedRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondQueryTaskCompleted(ctx, thrift.FromRespondQueryTaskCompletedRequest(rp1), p1...) return thrift.ToError(err) } func (g frontendClient) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.RestartWorkflowExecutionResponse, err error) { response, err := g.c.RestartWorkflowExecution(ctx, thrift.FromRestartWorkflowExecutionRequest(rp1), p1...) return thrift.ToRestartWorkflowExecutionResponse(response), thrift.ToError(err) } func (g frontendClient) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { response, err := g.c.ScanWorkflowExecutions(ctx, thrift.FromScanWorkflowExecutionsRequest(lp1), p1...) return thrift.ToScanWorkflowExecutionsResponse(response), thrift.ToError(err) } func (g frontendClient) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { response, err := g.c.SignalWithStartWorkflowExecution(ctx, thrift.FromSignalWithStartWorkflowExecutionRequest(sp1), p1...) return thrift.ToSignalWithStartWorkflowExecutionResponse(response), thrift.ToError(err) } func (g frontendClient) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { response, err := g.c.SignalWithStartWorkflowExecutionAsync(ctx, thrift.FromSignalWithStartWorkflowExecutionAsyncRequest(sp1), p1...) return thrift.ToSignalWithStartWorkflowExecutionAsyncResponse(response), thrift.ToError(err) } func (g frontendClient) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.SignalWorkflowExecution(ctx, thrift.FromSignalWorkflowExecutionRequest(sp1), p1...) return thrift.ToError(err) } func (g frontendClient) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { response, err := g.c.StartWorkflowExecution(ctx, thrift.FromStartWorkflowExecutionRequest(sp1), p1...) return thrift.ToStartWorkflowExecutionResponse(response), thrift.ToError(err) } func (g frontendClient) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { response, err := g.c.StartWorkflowExecutionAsync(ctx, thrift.FromStartWorkflowExecutionAsyncRequest(sp1), p1...) return thrift.ToStartWorkflowExecutionAsyncResponse(response), thrift.ToError(err) } func (g frontendClient) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.TerminateWorkflowExecution(ctx, thrift.FromTerminateWorkflowExecutionRequest(tp1), p1...) return thrift.ToError(err) } func (g frontendClient) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest, p1 ...yarpc.CallOption) (up2 *types.UpdateDomainResponse, err error) { response, err := g.c.UpdateDomain(ctx, thrift.FromUpdateDomainRequest(up1), p1...) return thrift.ToUpdateDomainResponse(response), thrift.ToError(err) } ================================================ FILE: client/wrappers/thrift/history_generated.go ================================================ package thrift // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/thrift.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) func (g historyClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.CloseShard(ctx, thrift.FromHistoryCloseShardRequest(cp1), p1...) return thrift.ToError(err) } func (g historyClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (hp1 *types.HistoryCountDLQMessagesResponse, err error) { return nil, thrift.ToError(&types.BadRequestError{Message: "Feature not supported on TChannel"}) } func (g historyClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { response, err := g.c.DescribeHistoryHost(ctx, thrift.FromHistoryDescribeHistoryHostRequest(dp1), p1...) return thrift.ToHistoryDescribeHistoryHostResponse(response), thrift.ToError(err) } func (g historyClient) DescribeMutableState(ctx context.Context, dp1 *types.DescribeMutableStateRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeMutableStateResponse, err error) { response, err := g.c.DescribeMutableState(ctx, thrift.FromHistoryDescribeMutableStateRequest(dp1), p1...) return thrift.ToHistoryDescribeMutableStateResponse(response), thrift.ToError(err) } func (g historyClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { response, err := g.c.DescribeQueue(ctx, thrift.FromHistoryDescribeQueueRequest(dp1), p1...) return thrift.ToHistoryDescribeQueueResponse(response), thrift.ToError(err) } func (g historyClient) DescribeWorkflowExecution(ctx context.Context, hp1 *types.HistoryDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeWorkflowExecutionResponse, err error) { response, err := g.c.DescribeWorkflowExecution(ctx, thrift.FromHistoryDescribeWorkflowExecutionRequest(hp1), p1...) return thrift.ToHistoryDescribeWorkflowExecutionResponse(response), thrift.ToError(err) } func (g historyClient) GetCrossClusterTasks(ctx context.Context, gp1 *types.GetCrossClusterTasksRequest, p1 ...yarpc.CallOption) (gp2 *types.GetCrossClusterTasksResponse, err error) { response, err := g.c.GetCrossClusterTasks(ctx, thrift.FromHistoryGetCrossClusterTasksRequest(gp1), p1...) return thrift.ToHistoryGetCrossClusterTasksResponse(response), thrift.ToError(err) } func (g historyClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { response, err := g.c.GetDLQReplicationMessages(ctx, thrift.FromHistoryGetDLQReplicationMessagesRequest(gp1), p1...) return thrift.ToHistoryGetDLQReplicationMessagesResponse(response), thrift.ToError(err) } func (g historyClient) GetFailoverInfo(ctx context.Context, gp1 *types.GetFailoverInfoRequest, p1 ...yarpc.CallOption) (gp2 *types.GetFailoverInfoResponse, err error) { response, err := g.c.GetFailoverInfo(ctx, thrift.FromHistoryGetFailoverInfoRequest(gp1), p1...) return thrift.ToHistoryGetFailoverInfoResponse(response), thrift.ToError(err) } func (g historyClient) GetMutableState(ctx context.Context, gp1 *types.GetMutableStateRequest, p1 ...yarpc.CallOption) (gp2 *types.GetMutableStateResponse, err error) { response, err := g.c.GetMutableState(ctx, thrift.FromHistoryGetMutableStateRequest(gp1), p1...) return thrift.ToHistoryGetMutableStateResponse(response), thrift.ToError(err) } func (g historyClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { response, err := g.c.GetReplicationMessages(ctx, thrift.FromHistoryGetReplicationMessagesRequest(gp1), p1...) return thrift.ToHistoryGetReplicationMessagesResponse(response), thrift.ToError(err) } func (g historyClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { response, err := g.c.MergeDLQMessages(ctx, thrift.FromHistoryMergeDLQMessagesRequest(mp1), p1...) return thrift.ToHistoryMergeDLQMessagesResponse(response), thrift.ToError(err) } func (g historyClient) NotifyFailoverMarkers(ctx context.Context, np1 *types.NotifyFailoverMarkersRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.NotifyFailoverMarkers(ctx, thrift.FromHistoryNotifyFailoverMarkersRequest(np1), p1...) return thrift.ToError(err) } func (g historyClient) PollMutableState(ctx context.Context, pp1 *types.PollMutableStateRequest, p1 ...yarpc.CallOption) (pp2 *types.PollMutableStateResponse, err error) { response, err := g.c.PollMutableState(ctx, thrift.FromHistoryPollMutableStateRequest(pp1), p1...) return thrift.ToHistoryPollMutableStateResponse(response), thrift.ToError(err) } func (g historyClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.PurgeDLQMessages(ctx, thrift.FromHistoryPurgeDLQMessagesRequest(pp1), p1...) return thrift.ToError(err) } func (g historyClient) QueryWorkflow(ctx context.Context, hp1 *types.HistoryQueryWorkflowRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryQueryWorkflowResponse, err error) { response, err := g.c.QueryWorkflow(ctx, thrift.FromHistoryQueryWorkflowRequest(hp1), p1...) return thrift.ToHistoryQueryWorkflowResponse(response), thrift.ToError(err) } func (g historyClient) RatelimitUpdate(ctx context.Context, request *types.RatelimitUpdateRequest, opts ...yarpc.CallOption) (rp1 *types.RatelimitUpdateResponse, err error) { response, err := g.c.RatelimitUpdate(ctx, thrift.FromHistoryRatelimitUpdateRequest(request), opts...) return thrift.ToHistoryRatelimitUpdateResponse(response), thrift.ToError(err) } func (g historyClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { response, err := g.c.ReadDLQMessages(ctx, thrift.FromHistoryReadDLQMessagesRequest(rp1), p1...) return thrift.ToHistoryReadDLQMessagesResponse(response), thrift.ToError(err) } func (g historyClient) ReapplyEvents(ctx context.Context, hp1 *types.HistoryReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.ReapplyEvents(ctx, thrift.FromHistoryReapplyEventsRequest(hp1), p1...) return thrift.ToError(err) } func (g historyClient) RecordActivityTaskHeartbeat(ctx context.Context, hp1 *types.HistoryRecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp1 *types.RecordActivityTaskHeartbeatResponse, err error) { response, err := g.c.RecordActivityTaskHeartbeat(ctx, thrift.FromHistoryRecordActivityTaskHeartbeatRequest(hp1), p1...) return thrift.ToHistoryRecordActivityTaskHeartbeatResponse(response), thrift.ToError(err) } func (g historyClient) RecordActivityTaskStarted(ctx context.Context, rp1 *types.RecordActivityTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskStartedResponse, err error) { response, err := g.c.RecordActivityTaskStarted(ctx, thrift.FromHistoryRecordActivityTaskStartedRequest(rp1), p1...) return thrift.ToHistoryRecordActivityTaskStartedResponse(response), thrift.ToError(err) } func (g historyClient) RecordChildExecutionCompleted(ctx context.Context, rp1 *types.RecordChildExecutionCompletedRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RecordChildExecutionCompleted(ctx, thrift.FromHistoryRecordChildExecutionCompletedRequest(rp1), p1...) return thrift.ToError(err) } func (g historyClient) RecordDecisionTaskStarted(ctx context.Context, rp1 *types.RecordDecisionTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordDecisionTaskStartedResponse, err error) { response, err := g.c.RecordDecisionTaskStarted(ctx, thrift.FromHistoryRecordDecisionTaskStartedRequest(rp1), p1...) return thrift.ToHistoryRecordDecisionTaskStartedResponse(response), thrift.ToError(err) } func (g historyClient) RefreshWorkflowTasks(ctx context.Context, hp1 *types.HistoryRefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RefreshWorkflowTasks(ctx, thrift.FromHistoryRefreshWorkflowTasksRequest(hp1), p1...) return thrift.ToError(err) } func (g historyClient) RemoveSignalMutableState(ctx context.Context, rp1 *types.RemoveSignalMutableStateRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RemoveSignalMutableState(ctx, thrift.FromHistoryRemoveSignalMutableStateRequest(rp1), p1...) return thrift.ToError(err) } func (g historyClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RemoveTask(ctx, thrift.FromHistoryRemoveTaskRequest(rp1), p1...) return thrift.ToError(err) } func (g historyClient) ReplicateEventsV2(ctx context.Context, rp1 *types.ReplicateEventsV2Request, p1 ...yarpc.CallOption) (err error) { err = g.c.ReplicateEventsV2(ctx, thrift.FromHistoryReplicateEventsV2Request(rp1), p1...) return thrift.ToError(err) } func (g historyClient) RequestCancelWorkflowExecution(ctx context.Context, hp1 *types.HistoryRequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RequestCancelWorkflowExecution(ctx, thrift.FromHistoryRequestCancelWorkflowExecutionRequest(hp1), p1...) return thrift.ToError(err) } func (g historyClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.ResetQueue(ctx, thrift.FromHistoryResetQueueRequest(rp1), p1...) return thrift.ToError(err) } func (g historyClient) ResetStickyTaskList(ctx context.Context, hp1 *types.HistoryResetStickyTaskListRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryResetStickyTaskListResponse, err error) { response, err := g.c.ResetStickyTaskList(ctx, thrift.FromHistoryResetStickyTaskListRequest(hp1), p1...) return thrift.ToHistoryResetStickyTaskListResponse(response), thrift.ToError(err) } func (g historyClient) ResetWorkflowExecution(ctx context.Context, hp1 *types.HistoryResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp1 *types.ResetWorkflowExecutionResponse, err error) { response, err := g.c.ResetWorkflowExecution(ctx, thrift.FromHistoryResetWorkflowExecutionRequest(hp1), p1...) return thrift.ToHistoryResetWorkflowExecutionResponse(response), thrift.ToError(err) } func (g historyClient) RespondActivityTaskCanceled(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondActivityTaskCanceled(ctx, thrift.FromHistoryRespondActivityTaskCanceledRequest(hp1), p1...) return thrift.ToError(err) } func (g historyClient) RespondActivityTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondActivityTaskCompleted(ctx, thrift.FromHistoryRespondActivityTaskCompletedRequest(hp1), p1...) return thrift.ToError(err) } func (g historyClient) RespondActivityTaskFailed(ctx context.Context, hp1 *types.HistoryRespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondActivityTaskFailed(ctx, thrift.FromHistoryRespondActivityTaskFailedRequest(hp1), p1...) return thrift.ToError(err) } func (g historyClient) RespondCrossClusterTasksCompleted(ctx context.Context, rp1 *types.RespondCrossClusterTasksCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondCrossClusterTasksCompletedResponse, err error) { response, err := g.c.RespondCrossClusterTasksCompleted(ctx, thrift.FromHistoryRespondCrossClusterTasksCompletedRequest(rp1), p1...) return thrift.ToHistoryRespondCrossClusterTasksCompletedResponse(response), thrift.ToError(err) } func (g historyClient) RespondDecisionTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryRespondDecisionTaskCompletedResponse, err error) { response, err := g.c.RespondDecisionTaskCompleted(ctx, thrift.FromHistoryRespondDecisionTaskCompletedRequest(hp1), p1...) return thrift.ToHistoryRespondDecisionTaskCompletedResponse(response), thrift.ToError(err) } func (g historyClient) RespondDecisionTaskFailed(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondDecisionTaskFailed(ctx, thrift.FromHistoryRespondDecisionTaskFailedRequest(hp1), p1...) return thrift.ToError(err) } func (g historyClient) ScheduleDecisionTask(ctx context.Context, sp1 *types.ScheduleDecisionTaskRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.ScheduleDecisionTask(ctx, thrift.FromHistoryScheduleDecisionTaskRequest(sp1), p1...) return thrift.ToError(err) } func (g historyClient) SignalWithStartWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { response, err := g.c.SignalWithStartWorkflowExecution(ctx, thrift.FromHistorySignalWithStartWorkflowExecutionRequest(hp1), p1...) return thrift.ToHistorySignalWithStartWorkflowExecutionResponse(response), thrift.ToError(err) } func (g historyClient) SignalWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.SignalWorkflowExecution(ctx, thrift.FromHistorySignalWorkflowExecutionRequest(hp1), p1...) return thrift.ToError(err) } func (g historyClient) StartWorkflowExecution(ctx context.Context, hp1 *types.HistoryStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { response, err := g.c.StartWorkflowExecution(ctx, thrift.FromHistoryStartWorkflowExecutionRequest(hp1), p1...) return thrift.ToHistoryStartWorkflowExecutionResponse(response), thrift.ToError(err) } func (g historyClient) SyncActivity(ctx context.Context, sp1 *types.SyncActivityRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.SyncActivity(ctx, thrift.FromHistorySyncActivityRequest(sp1), p1...) return thrift.ToError(err) } func (g historyClient) SyncShardStatus(ctx context.Context, sp1 *types.SyncShardStatusRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.SyncShardStatus(ctx, thrift.FromHistorySyncShardStatusRequest(sp1), p1...) return thrift.ToError(err) } func (g historyClient) TerminateWorkflowExecution(ctx context.Context, hp1 *types.HistoryTerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.TerminateWorkflowExecution(ctx, thrift.FromHistoryTerminateWorkflowExecutionRequest(hp1), p1...) return thrift.ToError(err) } ================================================ FILE: client/wrappers/thrift/matching_generated.go ================================================ package thrift // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/thrift.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) func (g matchingClient) AddActivityTask(ctx context.Context, ap1 *types.AddActivityTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddActivityTaskResponse, err error) { err = g.c.AddActivityTask(ctx, thrift.FromMatchingAddActivityTaskRequest(ap1), p1...) if err != nil { return nil, err } return &types.AddActivityTaskResponse{}, nil } func (g matchingClient) AddDecisionTask(ctx context.Context, ap1 *types.AddDecisionTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddDecisionTaskResponse, err error) { err = g.c.AddDecisionTask(ctx, thrift.FromMatchingAddDecisionTaskRequest(ap1), p1...) if err != nil { return nil, err } return &types.AddDecisionTaskResponse{}, nil } func (g matchingClient) CancelOutstandingPoll(ctx context.Context, cp1 *types.CancelOutstandingPollRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.CancelOutstandingPoll(ctx, thrift.FromMatchingCancelOutstandingPollRequest(cp1), p1...) return thrift.ToError(err) } func (g matchingClient) DescribeTaskList(ctx context.Context, mp1 *types.MatchingDescribeTaskListRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeTaskListResponse, err error) { response, err := g.c.DescribeTaskList(ctx, thrift.FromMatchingDescribeTaskListRequest(mp1), p1...) return thrift.ToMatchingDescribeTaskListResponse(response), thrift.ToError(err) } func (g matchingClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { response, err := g.c.GetTaskListsByDomain(ctx, thrift.FromMatchingGetTaskListsByDomainRequest(gp1), p1...) return thrift.ToMatchingGetTaskListsByDomainResponse(response), thrift.ToError(err) } func (g matchingClient) ListTaskListPartitions(ctx context.Context, mp1 *types.MatchingListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp1 *types.ListTaskListPartitionsResponse, err error) { response, err := g.c.ListTaskListPartitions(ctx, thrift.FromMatchingListTaskListPartitionsRequest(mp1), p1...) return thrift.ToMatchingListTaskListPartitionsResponse(response), thrift.ToError(err) } func (g matchingClient) PollForActivityTask(ctx context.Context, mp1 *types.MatchingPollForActivityTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForActivityTaskResponse, err error) { response, err := g.c.PollForActivityTask(ctx, thrift.FromMatchingPollForActivityTaskRequest(mp1), p1...) return thrift.ToMatchingPollForActivityTaskResponse(response), thrift.ToError(err) } func (g matchingClient) PollForDecisionTask(ctx context.Context, mp1 *types.MatchingPollForDecisionTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForDecisionTaskResponse, err error) { response, err := g.c.PollForDecisionTask(ctx, thrift.FromMatchingPollForDecisionTaskRequest(mp1), p1...) return thrift.ToMatchingPollForDecisionTaskResponse(response), thrift.ToError(err) } func (g matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQueryWorkflowRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingQueryWorkflowResponse, err error) { response, err := g.c.QueryWorkflow(ctx, thrift.FromMatchingQueryWorkflowRequest(mp1), p1...) return thrift.ToMatchingQueryWorkflowResponse(response), thrift.ToError(err) } func (g matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { return nil, thrift.ToError(&types.BadRequestError{Message: "Feature not supported on TChannel"}) } func (g matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { err = g.c.RespondQueryTaskCompleted(ctx, thrift.FromMatchingRespondQueryTaskCompletedRequest(mp1), p1...) return thrift.ToError(err) } func (g matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { return nil, thrift.ToError(&types.BadRequestError{Message: "Feature not supported on TChannel"}) } ================================================ FILE: client/wrappers/timeout/admin_generated.go ================================================ package timeout // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/timeout.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "go.uber.org/yarpc" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/types" ) var _ admin.Client = (*adminClient)(nil) // adminClient implements the admin.Client interface instrumented with timeouts type adminClient struct { client admin.Client largeTimeout time.Duration timeout time.Duration } // NewAdminClient creates a new adminClient instance func NewAdminClient( client admin.Client, largeTimeout time.Duration, timeout time.Duration, ) admin.Client { return &adminClient{ client: client, largeTimeout: largeTimeout, timeout: timeout, } } func (c *adminClient) AddSearchAttribute(ctx context.Context, ap1 *types.AddSearchAttributeRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.AddSearchAttribute(ctx, ap1, p1...) } func (c *adminClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.CloseShard(ctx, cp1, p1...) } func (c *adminClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (cp2 *types.CountDLQMessagesResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.CountDLQMessages(ctx, cp1, p1...) } func (c *adminClient) DeleteWorkflow(ctx context.Context, ap1 *types.AdminDeleteWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDeleteWorkflowResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DeleteWorkflow(ctx, ap1, p1...) } func (c *adminClient) DescribeCluster(ctx context.Context, p1 ...yarpc.CallOption) (dp1 *types.DescribeClusterResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeCluster(ctx, p1...) } func (c *adminClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeHistoryHost(ctx, dp1, p1...) } func (c *adminClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeQueue(ctx, dp1, p1...) } func (c *adminClient) DescribeShardDistribution(ctx context.Context, dp1 *types.DescribeShardDistributionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeShardDistributionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeShardDistribution(ctx, dp1, p1...) } func (c *adminClient) DescribeWorkflowExecution(ctx context.Context, ap1 *types.AdminDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminDescribeWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeWorkflowExecution(ctx, ap1, p1...) } func (c *adminClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetDLQReplicationMessages(ctx, gp1, p1...) } func (c *adminClient) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.GetDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainAsyncWorkflowConfiguratonResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetDomainAsyncWorkflowConfiguraton(ctx, request, opts...) } func (c *adminClient) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetDomainIsolationGroupsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetDomainIsolationGroups(ctx, request, opts...) } func (c *adminClient) GetDomainReplicationMessages(ctx context.Context, gp1 *types.GetDomainReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDomainReplicationMessagesResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetDomainReplicationMessages(ctx, gp1, p1...) } func (c *adminClient) GetDynamicConfig(ctx context.Context, gp1 *types.GetDynamicConfigRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDynamicConfigResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetDynamicConfig(ctx, gp1, p1...) } func (c *adminClient) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (gp1 *types.GetGlobalIsolationGroupsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetGlobalIsolationGroups(ctx, request, opts...) } func (c *adminClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { ctx, cancel := createContext(ctx, c.largeTimeout) defer cancel() return c.client.GetReplicationMessages(ctx, gp1, p1...) } func (c *adminClient) GetWorkflowExecutionRawHistoryV2(ctx context.Context, gp1 *types.GetWorkflowExecutionRawHistoryV2Request, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionRawHistoryV2Response, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetWorkflowExecutionRawHistoryV2(ctx, gp1, p1...) } func (c *adminClient) ListDynamicConfig(ctx context.Context, lp1 *types.ListDynamicConfigRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDynamicConfigResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ListDynamicConfig(ctx, lp1, p1...) } func (c *adminClient) MaintainCorruptWorkflow(ctx context.Context, ap1 *types.AdminMaintainWorkflowRequest, p1 ...yarpc.CallOption) (ap2 *types.AdminMaintainWorkflowResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.MaintainCorruptWorkflow(ctx, ap1, p1...) } func (c *adminClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.MergeDLQMessages(ctx, mp1, p1...) } func (c *adminClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.PurgeDLQMessages(ctx, pp1, p1...) } func (c *adminClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ReadDLQMessages(ctx, rp1, p1...) } func (c *adminClient) ReapplyEvents(ctx context.Context, rp1 *types.ReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ReapplyEvents(ctx, rp1, p1...) } func (c *adminClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RefreshWorkflowTasks(ctx, rp1, p1...) } func (c *adminClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RemoveTask(ctx, rp1, p1...) } func (c *adminClient) ResendReplicationTasks(ctx context.Context, rp1 *types.ResendReplicationTasksRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ResendReplicationTasks(ctx, rp1, p1...) } func (c *adminClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ResetQueue(ctx, rp1, p1...) } func (c *adminClient) RestoreDynamicConfig(ctx context.Context, rp1 *types.RestoreDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RestoreDynamicConfig(ctx, rp1, p1...) } func (c *adminClient) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.UpdateDomainAsyncWorkflowConfiguratonRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.UpdateDomainAsyncWorkflowConfiguraton(ctx, request, opts...) } func (c *adminClient) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateDomainIsolationGroupsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.UpdateDomainIsolationGroups(ctx, request, opts...) } func (c *adminClient) UpdateDynamicConfig(ctx context.Context, up1 *types.UpdateDynamicConfigRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.UpdateDynamicConfig(ctx, up1, p1...) } func (c *adminClient) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest, opts ...yarpc.CallOption) (up1 *types.UpdateGlobalIsolationGroupsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.UpdateGlobalIsolationGroups(ctx, request, opts...) } func (c *adminClient) UpdateTaskListPartitionConfig(ctx context.Context, request *types.UpdateTaskListPartitionConfigRequest, opts ...yarpc.CallOption) (up1 *types.UpdateTaskListPartitionConfigResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.UpdateTaskListPartitionConfig(ctx, request, opts...) } ================================================ FILE: client/wrappers/timeout/frontend_generated.go ================================================ package timeout // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/timeout.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "go.uber.org/yarpc" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/types" ) var _ frontend.Client = (*frontendClient)(nil) // frontendClient implements the frontend.Client interface instrumented with timeouts type frontendClient struct { client frontend.Client longPollTimeout time.Duration timeout time.Duration } // NewFrontendClient creates a new frontendClient instance func NewFrontendClient( client frontend.Client, longPollTimeout time.Duration, timeout time.Duration, ) frontend.Client { return &frontendClient{ client: client, longPollTimeout: longPollTimeout, timeout: timeout, } } func (c *frontendClient) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (cp2 *types.CountWorkflowExecutionsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.CountWorkflowExecutions(ctx, cp1, p1...) } func (c *frontendClient) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DeleteDomain(ctx, dp1, p1...) } func (c *frontendClient) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DeprecateDomain(ctx, dp1, p1...) } func (c *frontendClient) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeDomainResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeDomain(ctx, dp1, p1...) } func (c *frontendClient) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeTaskListResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeTaskList(ctx, dp1, p1...) } func (c *frontendClient) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeWorkflowExecution(ctx, dp1, p1...) } func (c *frontendClient) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DiagnoseWorkflowExecution(ctx, dp1, p1...) } func (c *frontendClient) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest, p1 ...yarpc.CallOption) (fp2 *types.FailoverDomainResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.FailoverDomain(ctx, fp1, p1...) } func (c *frontendClient) GetClusterInfo(ctx context.Context, p1 ...yarpc.CallOption) (cp1 *types.ClusterInfo, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetClusterInfo(ctx, p1...) } func (c *frontendClient) GetSearchAttributes(ctx context.Context, p1 ...yarpc.CallOption) (gp1 *types.GetSearchAttributesResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetSearchAttributes(ctx, p1...) } func (c *frontendClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetTaskListsByDomain(ctx, gp1, p1...) } func (c *frontendClient) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest, p1 ...yarpc.CallOption) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetWorkflowExecutionHistory(ctx, gp1, p1...) } func (c *frontendClient) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { ctx, cancel := createContext(ctx, c.longPollTimeout) defer cancel() return c.client.ListArchivedWorkflowExecutions(ctx, lp1, p1...) } func (c *frontendClient) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ListClosedWorkflowExecutions(ctx, lp1, p1...) } func (c *frontendClient) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListDomainsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ListDomains(ctx, lp1, p1...) } func (c *frontendClient) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest, p1 ...yarpc.CallOption) (lp2 *types.ListFailoverHistoryResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ListFailoverHistory(ctx, lp1, p1...) } func (c *frontendClient) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ListOpenWorkflowExecutions(ctx, lp1, p1...) } func (c *frontendClient) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListTaskListPartitionsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ListTaskListPartitions(ctx, lp1, p1...) } func (c *frontendClient) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ListWorkflowExecutions(ctx, lp1, p1...) } func (c *frontendClient) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForActivityTaskResponse, err error) { ctx, cancel := createContext(ctx, c.longPollTimeout) defer cancel() return c.client.PollForActivityTask(ctx, pp1, p1...) } func (c *frontendClient) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest, p1 ...yarpc.CallOption) (pp2 *types.PollForDecisionTaskResponse, err error) { ctx, cancel := createContext(ctx, c.longPollTimeout) defer cancel() return c.client.PollForDecisionTask(ctx, pp1, p1...) } func (c *frontendClient) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest, p1 ...yarpc.CallOption) (qp2 *types.QueryWorkflowResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.QueryWorkflow(ctx, qp1, p1...) } func (c *frontendClient) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RecordActivityTaskHeartbeat(ctx, rp1, p1...) } func (c *frontendClient) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RecordActivityTaskHeartbeatByID(ctx, rp1, p1...) } func (c *frontendClient) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RefreshWorkflowTasks(ctx, rp1, p1...) } func (c *frontendClient) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RegisterDomain(ctx, rp1, p1...) } func (c *frontendClient) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RequestCancelWorkflowExecution(ctx, rp1, p1...) } func (c *frontendClient) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetStickyTaskListResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ResetStickyTaskList(ctx, rp1, p1...) } func (c *frontendClient) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.ResetWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ResetWorkflowExecution(ctx, rp1, p1...) } func (c *frontendClient) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondActivityTaskCanceled(ctx, rp1, p1...) } func (c *frontendClient) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondActivityTaskCanceledByID(ctx, rp1, p1...) } func (c *frontendClient) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondActivityTaskCompleted(ctx, rp1, p1...) } func (c *frontendClient) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondActivityTaskCompletedByID(ctx, rp1, p1...) } func (c *frontendClient) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondActivityTaskFailed(ctx, rp1, p1...) } func (c *frontendClient) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondActivityTaskFailedByID(ctx, rp1, p1...) } func (c *frontendClient) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondDecisionTaskCompleted(ctx, rp1, p1...) } func (c *frontendClient) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondDecisionTaskFailed(ctx, rp1, p1...) } func (c *frontendClient) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondQueryTaskCompleted(ctx, rp1, p1...) } func (c *frontendClient) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp2 *types.RestartWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RestartWorkflowExecution(ctx, rp1, p1...) } func (c *frontendClient) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest, p1 ...yarpc.CallOption) (lp2 *types.ListWorkflowExecutionsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ScanWorkflowExecutions(ctx, lp1, p1...) } func (c *frontendClient) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.SignalWithStartWorkflowExecution(ctx, sp1, p1...) } func (c *frontendClient) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.SignalWithStartWorkflowExecutionAsync(ctx, sp1, p1...) } func (c *frontendClient) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.SignalWorkflowExecution(ctx, sp1, p1...) } func (c *frontendClient) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.StartWorkflowExecution(ctx, sp1, p1...) } func (c *frontendClient) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest, p1 ...yarpc.CallOption) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.StartWorkflowExecutionAsync(ctx, sp1, p1...) } func (c *frontendClient) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.TerminateWorkflowExecution(ctx, tp1, p1...) } func (c *frontendClient) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest, p1 ...yarpc.CallOption) (up2 *types.UpdateDomainResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.UpdateDomain(ctx, up1, p1...) } ================================================ FILE: client/wrappers/timeout/history_generated.go ================================================ package timeout // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/timeout.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "go.uber.org/yarpc" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common/types" ) var _ history.Client = (*historyClient)(nil) // historyClient implements the history.Client interface instrumented with timeouts type historyClient struct { client history.Client timeout time.Duration } // NewHistoryClient creates a new historyClient instance func NewHistoryClient( client history.Client, timeout time.Duration, ) history.Client { return &historyClient{ client: client, timeout: timeout, } } func (c *historyClient) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.CloseShard(ctx, cp1, p1...) } func (c *historyClient) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest, p1 ...yarpc.CallOption) (hp1 *types.HistoryCountDLQMessagesResponse, err error) { return c.client.CountDLQMessages(ctx, cp1, p1...) } func (c *historyClient) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeHistoryHostResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeHistoryHost(ctx, dp1, p1...) } func (c *historyClient) DescribeMutableState(ctx context.Context, dp1 *types.DescribeMutableStateRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeMutableStateResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeMutableState(ctx, dp1, p1...) } func (c *historyClient) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest, p1 ...yarpc.CallOption) (dp2 *types.DescribeQueueResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeQueue(ctx, dp1, p1...) } func (c *historyClient) DescribeWorkflowExecution(ctx context.Context, hp1 *types.HistoryDescribeWorkflowExecutionRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeWorkflowExecution(ctx, hp1, p1...) } func (c *historyClient) GetCrossClusterTasks(ctx context.Context, gp1 *types.GetCrossClusterTasksRequest, p1 ...yarpc.CallOption) (gp2 *types.GetCrossClusterTasksResponse, err error) { return c.client.GetCrossClusterTasks(ctx, gp1, p1...) } func (c *historyClient) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { return c.client.GetDLQReplicationMessages(ctx, gp1, p1...) } func (c *historyClient) GetFailoverInfo(ctx context.Context, gp1 *types.GetFailoverInfoRequest, p1 ...yarpc.CallOption) (gp2 *types.GetFailoverInfoResponse, err error) { return c.client.GetFailoverInfo(ctx, gp1, p1...) } func (c *historyClient) GetMutableState(ctx context.Context, gp1 *types.GetMutableStateRequest, p1 ...yarpc.CallOption) (gp2 *types.GetMutableStateResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetMutableState(ctx, gp1, p1...) } func (c *historyClient) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest, p1 ...yarpc.CallOption) (gp2 *types.GetReplicationMessagesResponse, err error) { return c.client.GetReplicationMessages(ctx, gp1, p1...) } func (c *historyClient) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest, p1 ...yarpc.CallOption) (mp2 *types.MergeDLQMessagesResponse, err error) { return c.client.MergeDLQMessages(ctx, mp1, p1...) } func (c *historyClient) NotifyFailoverMarkers(ctx context.Context, np1 *types.NotifyFailoverMarkersRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.NotifyFailoverMarkers(ctx, np1, p1...) } func (c *historyClient) PollMutableState(ctx context.Context, pp1 *types.PollMutableStateRequest, p1 ...yarpc.CallOption) (pp2 *types.PollMutableStateResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.PollMutableState(ctx, pp1, p1...) } func (c *historyClient) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest, p1 ...yarpc.CallOption) (err error) { return c.client.PurgeDLQMessages(ctx, pp1, p1...) } func (c *historyClient) QueryWorkflow(ctx context.Context, hp1 *types.HistoryQueryWorkflowRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryQueryWorkflowResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.QueryWorkflow(ctx, hp1, p1...) } func (c *historyClient) RatelimitUpdate(ctx context.Context, request *types.RatelimitUpdateRequest, opts ...yarpc.CallOption) (rp1 *types.RatelimitUpdateResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RatelimitUpdate(ctx, request, opts...) } func (c *historyClient) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest, p1 ...yarpc.CallOption) (rp2 *types.ReadDLQMessagesResponse, err error) { return c.client.ReadDLQMessages(ctx, rp1, p1...) } func (c *historyClient) ReapplyEvents(ctx context.Context, hp1 *types.HistoryReapplyEventsRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ReapplyEvents(ctx, hp1, p1...) } func (c *historyClient) RecordActivityTaskHeartbeat(ctx context.Context, hp1 *types.HistoryRecordActivityTaskHeartbeatRequest, p1 ...yarpc.CallOption) (rp1 *types.RecordActivityTaskHeartbeatResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RecordActivityTaskHeartbeat(ctx, hp1, p1...) } func (c *historyClient) RecordActivityTaskStarted(ctx context.Context, rp1 *types.RecordActivityTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordActivityTaskStartedResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RecordActivityTaskStarted(ctx, rp1, p1...) } func (c *historyClient) RecordChildExecutionCompleted(ctx context.Context, rp1 *types.RecordChildExecutionCompletedRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RecordChildExecutionCompleted(ctx, rp1, p1...) } func (c *historyClient) RecordDecisionTaskStarted(ctx context.Context, rp1 *types.RecordDecisionTaskStartedRequest, p1 ...yarpc.CallOption) (rp2 *types.RecordDecisionTaskStartedResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RecordDecisionTaskStarted(ctx, rp1, p1...) } func (c *historyClient) RefreshWorkflowTasks(ctx context.Context, hp1 *types.HistoryRefreshWorkflowTasksRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RefreshWorkflowTasks(ctx, hp1, p1...) } func (c *historyClient) RemoveSignalMutableState(ctx context.Context, rp1 *types.RemoveSignalMutableStateRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RemoveSignalMutableState(ctx, rp1, p1...) } func (c *historyClient) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RemoveTask(ctx, rp1, p1...) } func (c *historyClient) ReplicateEventsV2(ctx context.Context, rp1 *types.ReplicateEventsV2Request, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ReplicateEventsV2(ctx, rp1, p1...) } func (c *historyClient) RequestCancelWorkflowExecution(ctx context.Context, hp1 *types.HistoryRequestCancelWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RequestCancelWorkflowExecution(ctx, hp1, p1...) } func (c *historyClient) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ResetQueue(ctx, rp1, p1...) } func (c *historyClient) ResetStickyTaskList(ctx context.Context, hp1 *types.HistoryResetStickyTaskListRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryResetStickyTaskListResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ResetStickyTaskList(ctx, hp1, p1...) } func (c *historyClient) ResetWorkflowExecution(ctx context.Context, hp1 *types.HistoryResetWorkflowExecutionRequest, p1 ...yarpc.CallOption) (rp1 *types.ResetWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ResetWorkflowExecution(ctx, hp1, p1...) } func (c *historyClient) RespondActivityTaskCanceled(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCanceledRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondActivityTaskCanceled(ctx, hp1, p1...) } func (c *historyClient) RespondActivityTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondActivityTaskCompleted(ctx, hp1, p1...) } func (c *historyClient) RespondActivityTaskFailed(ctx context.Context, hp1 *types.HistoryRespondActivityTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondActivityTaskFailed(ctx, hp1, p1...) } func (c *historyClient) RespondCrossClusterTasksCompleted(ctx context.Context, rp1 *types.RespondCrossClusterTasksCompletedRequest, p1 ...yarpc.CallOption) (rp2 *types.RespondCrossClusterTasksCompletedResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondCrossClusterTasksCompleted(ctx, rp1, p1...) } func (c *historyClient) RespondDecisionTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskCompletedRequest, p1 ...yarpc.CallOption) (hp2 *types.HistoryRespondDecisionTaskCompletedResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondDecisionTaskCompleted(ctx, hp1, p1...) } func (c *historyClient) RespondDecisionTaskFailed(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskFailedRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondDecisionTaskFailed(ctx, hp1, p1...) } func (c *historyClient) ScheduleDecisionTask(ctx context.Context, sp1 *types.ScheduleDecisionTaskRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ScheduleDecisionTask(ctx, sp1, p1...) } func (c *historyClient) SignalWithStartWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWithStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.SignalWithStartWorkflowExecution(ctx, hp1, p1...) } func (c *historyClient) SignalWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.SignalWorkflowExecution(ctx, hp1, p1...) } func (c *historyClient) StartWorkflowExecution(ctx context.Context, hp1 *types.HistoryStartWorkflowExecutionRequest, p1 ...yarpc.CallOption) (sp1 *types.StartWorkflowExecutionResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.StartWorkflowExecution(ctx, hp1, p1...) } func (c *historyClient) SyncActivity(ctx context.Context, sp1 *types.SyncActivityRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.SyncActivity(ctx, sp1, p1...) } func (c *historyClient) SyncShardStatus(ctx context.Context, sp1 *types.SyncShardStatusRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.SyncShardStatus(ctx, sp1, p1...) } func (c *historyClient) TerminateWorkflowExecution(ctx context.Context, hp1 *types.HistoryTerminateWorkflowExecutionRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.TerminateWorkflowExecution(ctx, hp1, p1...) } ================================================ FILE: client/wrappers/timeout/history_generated_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package timeout import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common/types" ) func Test_historyClient_CloseShard(t *testing.T) { type fields struct { client func(t *testing.T) history.Client timeout time.Duration } type args struct { ctx context.Context cp1 *types.CloseShardRequest p1 []yarpc.CallOption } tests := []struct { name string fields fields args args wantErr bool }{ { name: "nil context success", fields: fields{ timeout: time.Millisecond * 150, }, args: args{ ctx: nil, cp1: &types.CloseShardRequest{}, }, wantErr: false, }, { name: "context failed with timeout", fields: fields{ timeout: time.Millisecond * 50, }, args: args{ ctx: context.Background(), cp1: &types.CloseShardRequest{}, }, wantErr: true, }, { name: "invalid timeout value success", fields: fields{ timeout: -10, }, args: args{ ctx: context.Background(), cp1: &types.CloseShardRequest{}, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := history.NewMockClient(gomock.NewController(t)) m.EXPECT().CloseShard(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, r *types.CloseShardRequest, opt ...yarpc.CallOption) error { for { select { case <-ctx.Done(): return ctx.Err() case <-time.After(time.Millisecond * 100): return nil } } }) c := historyClient{ client: m, timeout: tt.fields.timeout, } if err := c.CloseShard(tt.args.ctx, tt.args.cp1, tt.args.p1...); (err != nil) != tt.wantErr { t.Errorf("CloseShard() error = %v, wantErr %v", err, tt.wantErr) } }) } } func Test_historyClient_GetReplicationMessages(t *testing.T) { t.Run("no timeout", func(t *testing.T) { m := history.NewMockClient(gomock.NewController(t)) m.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, r *types.GetReplicationMessagesRequest, opt ...yarpc.CallOption) (*types.GetReplicationMessagesResponse, error) { for { select { case <-ctx.Done(): return nil, ctx.Err() case <-time.After(time.Millisecond * 100): return &types.GetReplicationMessagesResponse{}, nil } } }) c := historyClient{ client: m, timeout: time.Millisecond * 10, } _, err := c.GetReplicationMessages(context.Background(), &types.GetReplicationMessagesRequest{}) assert.NoError(t, err) }) } ================================================ FILE: client/wrappers/timeout/matching_generated.go ================================================ package timeout // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/timeout.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "go.uber.org/yarpc" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common/types" ) var _ matching.Client = (*matchingClient)(nil) // matchingClient implements the matching.Client interface instrumented with timeouts type matchingClient struct { client matching.Client longPollTimeout time.Duration timeout time.Duration } // NewMatchingClient creates a new matchingClient instance func NewMatchingClient( client matching.Client, longPollTimeout time.Duration, timeout time.Duration, ) matching.Client { return &matchingClient{ client: client, longPollTimeout: longPollTimeout, timeout: timeout, } } func (c *matchingClient) AddActivityTask(ctx context.Context, ap1 *types.AddActivityTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddActivityTaskResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.AddActivityTask(ctx, ap1, p1...) } func (c *matchingClient) AddDecisionTask(ctx context.Context, ap1 *types.AddDecisionTaskRequest, p1 ...yarpc.CallOption) (ap2 *types.AddDecisionTaskResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.AddDecisionTask(ctx, ap1, p1...) } func (c *matchingClient) CancelOutstandingPoll(ctx context.Context, cp1 *types.CancelOutstandingPollRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.CancelOutstandingPoll(ctx, cp1, p1...) } func (c *matchingClient) DescribeTaskList(ctx context.Context, mp1 *types.MatchingDescribeTaskListRequest, p1 ...yarpc.CallOption) (dp1 *types.DescribeTaskListResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.DescribeTaskList(ctx, mp1, p1...) } func (c *matchingClient) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest, p1 ...yarpc.CallOption) (gp2 *types.GetTaskListsByDomainResponse, err error) { return c.client.GetTaskListsByDomain(ctx, gp1, p1...) } func (c *matchingClient) ListTaskListPartitions(ctx context.Context, mp1 *types.MatchingListTaskListPartitionsRequest, p1 ...yarpc.CallOption) (lp1 *types.ListTaskListPartitionsResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.ListTaskListPartitions(ctx, mp1, p1...) } func (c *matchingClient) PollForActivityTask(ctx context.Context, mp1 *types.MatchingPollForActivityTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForActivityTaskResponse, err error) { ctx, cancel := createContext(ctx, c.longPollTimeout) defer cancel() return c.client.PollForActivityTask(ctx, mp1, p1...) } func (c *matchingClient) PollForDecisionTask(ctx context.Context, mp1 *types.MatchingPollForDecisionTaskRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingPollForDecisionTaskResponse, err error) { ctx, cancel := createContext(ctx, c.longPollTimeout) defer cancel() return c.client.PollForDecisionTask(ctx, mp1, p1...) } func (c *matchingClient) QueryWorkflow(ctx context.Context, mp1 *types.MatchingQueryWorkflowRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingQueryWorkflowResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.QueryWorkflow(ctx, mp1, p1...) } func (c *matchingClient) RefreshTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingRefreshTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingRefreshTaskListPartitionConfigResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RefreshTaskListPartitionConfig(ctx, mp1, p1...) } func (c *matchingClient) RespondQueryTaskCompleted(ctx context.Context, mp1 *types.MatchingRespondQueryTaskCompletedRequest, p1 ...yarpc.CallOption) (err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.RespondQueryTaskCompleted(ctx, mp1, p1...) } func (c *matchingClient) UpdateTaskListPartitionConfig(ctx context.Context, mp1 *types.MatchingUpdateTaskListPartitionConfigRequest, p1 ...yarpc.CallOption) (mp2 *types.MatchingUpdateTaskListPartitionConfigResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.UpdateTaskListPartitionConfig(ctx, mp1, p1...) } ================================================ FILE: client/wrappers/timeout/sharddistributor_generated.go ================================================ package timeout // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/timeout.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/types" ) var _ sharddistributor.Client = (*sharddistributorClient)(nil) // sharddistributorClient implements the sharddistributor.Client interface instrumented with timeouts type sharddistributorClient struct { client sharddistributor.Client timeout time.Duration } // NewShardDistributorClient creates a new sharddistributorClient instance func NewShardDistributorClient( client sharddistributor.Client, timeout time.Duration, ) sharddistributor.Client { return &sharddistributorClient{ client: client, timeout: timeout, } } func (c *sharddistributorClient) GetShardOwner(ctx context.Context, gp1 *types.GetShardOwnerRequest, p1 ...yarpc.CallOption) (gp2 *types.GetShardOwnerResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.GetShardOwner(ctx, gp1, p1...) } func (c *sharddistributorClient) WatchNamespaceState(ctx context.Context, wp1 *types.WatchNamespaceStateRequest, p1 ...yarpc.CallOption) (w1 sharddistributor.WatchNamespaceStateClient, err error) { return c.client.WatchNamespaceState(ctx, wp1, p1...) } ================================================ FILE: client/wrappers/timeout/sharddistributorexecutor_generated.go ================================================ package timeout // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/timeout.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributorexecutor" "github.com/uber/cadence/common/types" ) var _ sharddistributorexecutor.Client = (*sharddistributorexecutorClient)(nil) // sharddistributorexecutorClient implements the sharddistributorexecutor.Client interface instrumented with timeouts type sharddistributorexecutorClient struct { client sharddistributorexecutor.Client timeout time.Duration } // NewShardDistributorExecutorClient creates a new sharddistributorexecutorClient instance func NewShardDistributorExecutorClient( client sharddistributorexecutor.Client, timeout time.Duration, ) sharddistributorexecutor.Client { return &sharddistributorexecutorClient{ client: client, timeout: timeout, } } func (c *sharddistributorexecutorClient) Heartbeat(ctx context.Context, ep1 *types.ExecutorHeartbeatRequest, p1 ...yarpc.CallOption) (ep2 *types.ExecutorHeartbeatResponse, err error) { ctx, cancel := createContext(ctx, c.timeout) defer cancel() return c.client.Heartbeat(ctx, ep1, p1...) } ================================================ FILE: client/wrappers/timeout/timeout.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package timeout import ( "context" "time" ) const ( // AdminDefaultTimeout is the admin service default timeout used to make calls AdminDefaultTimeout = 10 * time.Second // AdminDefaultLargeTimeout is the admin service large default timeout used to make calls AdminDefaultLargeTimeout = time.Minute // FrontendDefaultTimeout is the frontend service default timeout used to make calls FrontendDefaultTimeout = 10 * time.Second // FrontendDefaultLongPollTimeout is the frontend service long poll default timeout used to make calls FrontendDefaultLongPollTimeout = time.Minute * 3 // MatchingDefaultTimeout is the default timeout used to make calls MatchingDefaultTimeout = time.Minute // MatchingDefaultLongPollTimeout is the long poll default timeout used to make calls MatchingDefaultLongPollTimeout = time.Minute * 2 // HistoryDefaultTimeout is the default timeout used to make calls HistoryDefaultTimeout = time.Second * 30 // ShardDistributorDefaultTimeout is the default timeout used to make calls ShardDistributorDefaultTimeout = time.Second * 10 // ShardDistributorExecutorDefaultTimeout is the default timeout used to make calls ShardDistributorExecutorDefaultTimeout = time.Second * 10 ) func createContext(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { if parent == nil { parent = context.Background() } if timeout > 0 { return context.WithTimeout(parent, timeout) } return context.WithCancel(parent) } ================================================ FILE: cmd/bench/main.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package main import ( "fmt" "log" "os" "path" "strings" "github.com/urfave/cli/v2" "github.com/uber/cadence/bench" "github.com/uber/cadence/bench/lib" "github.com/uber/cadence/common/config" "github.com/uber/cadence/tools/common/commoncli" ) const ( flagRoot = "root" flagConfig = "config" flagEnv = "env" flagZone = "zone" ) const ( defaultRoot = "." defaultConfig = "config/bench" defaultEnv = "development" defaultZone = "" ) func startHandler(c *cli.Context) error { env := getEnvironment(c) zone := getZone(c) configDir := getConfigDir(c) log.Printf("Loading config; env=%v,zone=%v,configDir=%v\n", env, zone, configDir) var cfg lib.Config if err := config.Load(env, configDir, zone, &cfg); err != nil { return fmt.Errorf("failed to load config file: %w", err) } if err := cfg.Validate(); err != nil { return fmt.Errorf("invalid config: %w", err) } benchWorker, err := bench.NewWorker(&cfg) if err != nil { return fmt.Errorf("failed to initialize bench worker: %w", err) } if err := benchWorker.Run(); err != nil { return fmt.Errorf("failed to run bench worker: %w", err) } return nil } func getRootDir(c *cli.Context) string { rootDir := c.String(flagRoot) if len(rootDir) == 0 { var err error if rootDir, err = os.Getwd(); err != nil { rootDir = "." } } return rootDir } func getConfigDir(c *cli.Context) string { rootDir := getRootDir(c) configDir := c.String(flagConfig) return path.Join(rootDir, configDir) } func getEnvironment(c *cli.Context) string { return strings.TrimSpace(c.String(flagEnv)) } func getZone(c *cli.Context) string { return strings.TrimSpace(c.String(flagZone)) } func buildCLI() *cli.App { app := cli.NewApp() app.Name = "cadence-bench" app.Usage = "Cadence bench" app.Version = "0.0.1" app.Flags = []cli.Flag{ &cli.StringFlag{ Name: flagRoot, Aliases: []string{"r"}, Value: defaultRoot, Usage: "root directory of execution environment", EnvVars: []string{lib.EnvKeyRoot}, }, &cli.StringFlag{ Name: flagConfig, Aliases: []string{"c"}, Value: defaultConfig, Usage: "config dir path relative to root", EnvVars: []string{lib.EnvKeyConfigDir}, }, &cli.StringFlag{ Name: flagEnv, Aliases: []string{"e"}, Value: defaultEnv, Usage: "runtime environment", EnvVars: []string{lib.EnvKeyEnvironment}, }, &cli.StringFlag{ Name: flagZone, Aliases: []string{"z"}, Value: defaultZone, Usage: "availability zone", EnvVars: []string{lib.EnvKeyAvailabilityZone}, }, } app.Commands = []*cli.Command{ { Name: "start", Usage: "start cadence bench worker", Action: func(c *cli.Context) error { return startHandler(c) }, }, } return app } func main() { app := buildCLI() commoncli.ExitHandler(app.Run(os.Args)) } ================================================ FILE: cmd/canary/main.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "fmt" "log" "os" "path" "strings" "github.com/urfave/cli/v2" "github.com/uber/cadence/canary" "github.com/uber/cadence/common/config" "github.com/uber/cadence/tools/common/commoncli" ) func startHandler(c *cli.Context) error { env := getEnvironment(c) zone := getZone(c) configDir := getConfigDir(c) log.Printf("Loading config; env=%v,zone=%v,configDir=%v\n", env, zone, configDir) var cfg canary.Config if err := config.Load(env, configDir, zone, &cfg); err != nil { return fmt.Errorf("failed to load config file: %w", err) } if err := cfg.Validate(); err != nil { return fmt.Errorf("invalid config: %w", err) } mode := c.String("mode") canary, err := canary.NewCanaryRunner(&cfg) if err != nil { return fmt.Errorf("failed to initialize canary: %w", err) } if err := canary.Run(mode); err != nil { return fmt.Errorf("failed to run canary: %w", err) } return nil } func getRootDir(c *cli.Context) string { rootDir := c.String("root") if len(rootDir) == 0 { var err error if rootDir, err = os.Getwd(); err != nil { rootDir = "." } } return rootDir } func getConfigDir(c *cli.Context) string { rootDir := getRootDir(c) configDir := c.String("config") return path.Join(rootDir, configDir) } func getEnvironment(c *cli.Context) string { return strings.TrimSpace(c.String("env")) } func getZone(c *cli.Context) string { return strings.TrimSpace(c.String("zone")) } func buildCLI() *cli.App { app := cli.NewApp() app.Name = "cadence-canary" app.Usage = "Cadence canary" app.Version = "0.0.1" app.Flags = []cli.Flag{ &cli.StringFlag{ Name: "root", Aliases: []string{"r"}, Value: ".", Usage: "root directory of execution environment", EnvVars: []string{canary.EnvKeyRoot}, }, &cli.StringFlag{ Name: "config", Aliases: []string{"c"}, Value: "config/canary", Usage: "config dir path relative to root", EnvVars: []string{canary.EnvKeyConfigDir}, }, &cli.StringFlag{ Name: "env", Aliases: []string{"e"}, Value: "development", Usage: "runtime environment", EnvVars: []string{canary.EnvKeyEnvironment}, }, &cli.StringFlag{ Name: "zone", Aliases: []string{"az"}, Value: "", Usage: "availability zone", EnvVars: []string{canary.EnvKeyAvailabilityZone}, }, } app.Commands = []*cli.Command{ { Name: "start", Usage: "start cadence canary worker or cron, or both", Flags: []cli.Flag{ &cli.StringFlag{ Name: "mode", Aliases: []string{"m"}, Value: canary.ModeAll, Usage: fmt.Sprintf("%v, %v or %v", canary.ModeWorker, canary.ModeCronCanary, canary.ModeAll), EnvVars: []string{canary.EnvKeyMode}, }, }, Action: func(c *cli.Context) error { return startHandler(c) }, }, } return app } func main() { app := buildCLI() commoncli.ExitHandler(app.Run(os.Args)) } ================================================ FILE: cmd/server/README.md ================================================ This is the primary "kitchen sink" server build, which includes all first-party optional plugins, and is used as our Docker-provided binary. For the most part, this means that day-to-day library upgrades should: 1. Update the main `go.mod`'s dependencies 2. `make tidy` New submodules we want to include by default should: 1. Add a replace in this `go.mod` like others 2. Import and register it somehow 3. `make tidy` And if you have problems tidying: 1. Copy/paste all included modules into this `go.mod` (`/go.mod` + `/common/archiver/gcloud/go.mod` currently) 2. `go mod tidy` this submodule and it will probably be correct, `make build` to make sure 3. Commit to save your results! 4. `make tidy` to make sure it's stable ================================================ FILE: cmd/server/cadence/cadence.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cadence import ( "context" "errors" "fmt" stdLog "log" "os" "path/filepath" "strings" "sync" "github.com/urfave/cli/v2" "go.uber.org/fx" "go.uber.org/multierr" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/service" _ "go.uber.org/automaxprocs" // defines automaxpocs for dockerized usage. ) // validServices is the list of all valid cadence services var validServices = service.ShortNames(service.List) var defaultServices = service.ShortNames(service.ListWithRing) func isValidService(in string) bool { for _, s := range validServices { if s == in { return true } } return false } // BuildCLI is the main entry point for the cadence server func BuildCLI(releaseVersion string, gitRevision string) *cli.App { version := fmt.Sprintf(" Release version: %v \n"+ " Build commit: %v\n"+ " Max Support CLI feature version: %v \n"+ " Max Support GoSDK feature version: %v \n"+ " Max Support JavaSDK feature version: %v \n"+ " Note: Feature version is for compatibility checking between server and clients if enabled feature checking. Server is always backward compatible to older CLI versions, but not accepting newer than it can support.", releaseVersion, gitRevision, client.SupportedCLIVersion, client.SupportedGoSDKVersion, client.SupportedJavaSDKVersion) app := cli.NewApp() app.Name = "cadence" app.Usage = "Cadence server" app.Version = version app.Flags = []cli.Flag{ &cli.StringFlag{ Name: "root", Aliases: []string{"r"}, Value: ".", Usage: "root directory of execution environment", EnvVars: []string{config.EnvKeyRoot}, }, &cli.StringFlag{ Name: "config", Aliases: []string{"c"}, Value: "config", Usage: "config dir is a path relative to root, or an absolute path", EnvVars: []string{config.EnvKeyConfigDir}, }, &cli.StringFlag{ Name: "env", Aliases: []string{"e"}, Value: "development", Usage: "runtime environment", EnvVars: []string{config.EnvKeyEnvironment}, }, &cli.StringFlag{ Name: "zone", Aliases: []string{"az"}, Value: "", Usage: "availability zone", EnvVars: []string{config.EnvKeyAvailabilityZone}, }, } app.Commands = []*cli.Command{ { Name: "start", Aliases: []string{""}, Usage: "start cadence server", Flags: []cli.Flag{ &cli.StringFlag{ Name: "services", Aliases: []string{"s"}, Value: strings.Join(defaultServices, ","), Usage: "list of services to start", }, }, Action: func(c *cli.Context) error { host, err := os.Hostname() if err != nil { return fmt.Errorf("get hostname: %w", err) } appCtx := appContext{ CfgContext: config.Context{ Environment: getEnvironment(c), Zone: getZone(c), }, ConfigDir: getConfigDir(c), RootDir: getRootDir(c), HostName: host, } services := getServices(c) return runServices( services, func(serviceName string) fxAppInterface { return fx.New( fx.Module(serviceName, _commonModule, fx.Provide( func() appContext { return appCtx }, ), Module(serviceName), ), ) }, ) }, }, } return app } func runServices(services []string, appBuilder func(serviceName string) fxAppInterface) error { stoppedWg := &sync.WaitGroup{} ctx, cancel := context.WithCancel(context.Background()) defer cancel() errChan := make(chan error, len(services)) for _, serv := range services { stoppedWg.Add(1) go func(s string) { defer stoppedWg.Done() fxApp := appBuilder(s) // If any of the start hooks return an error, Start short-circuits, calls Stop, and returns the inciting error. if err := fxApp.Start(ctx); err != nil { // If any of the apps fails to start, immediately cancel the context so others will also stop. cancel() errChan <- fmt.Errorf("service %s start: %w", s, err) return } select { // Block until FX receives a shutdown signal case <-fxApp.Done(): } // Stop the application err := fxApp.Stop(ctx) if err != nil { errChan <- fmt.Errorf("service %s stop: %w", s, err) } }(serv) } go func() { stoppedWg.Wait() // After stoppedWg unblocked all services are stopped to we no longer wait for errors. close(errChan) }() var resErrors error for err := range errChan { // skip canceled errors, since they are caused by context cancelation and only focus on actual errors. if err != nil && !errors.Is(err, context.Canceled) { resErrors = multierr.Append(resErrors, err) } } if resErrors != nil { return resErrors } return nil } type appContext struct { fx.Out CfgContext config.Context ConfigDir string `name:"config-dir"` RootDir string `name:"root-dir"` HostName string `name:"hostname"` } func getEnvironment(c *cli.Context) string { return strings.TrimSpace(c.String("env")) } func getZone(c *cli.Context) string { return strings.TrimSpace(c.String("zone")) } // getServices parses the services arg from cli // and returns a list of services to start func getServices(c *cli.Context) []string { val := strings.TrimSpace(c.String("services")) tokens := strings.Split(val, ",") if len(tokens) == 0 { stdLog.Fatal("list of services is empty") } for _, t := range tokens { if !isValidService(t) { stdLog.Fatalf("invalid service `%v` in service list [%v]", t, val) } } return tokens } func getConfigDir(c *cli.Context) string { return constructPathIfNeed(getRootDir(c), c.String("config")) } func getRootDir(c *cli.Context) string { dirpath := c.String("root") if len(dirpath) == 0 { cwd, err := os.Getwd() if err != nil { stdLog.Fatalf("os.Getwd() failed, err=%v", err) } return cwd } return dirpath } // constructPathIfNeed would append the dir as the root dir // when the file wasn't absolute path. func constructPathIfNeed(dir string, file string) string { if !filepath.IsAbs(file) { return dir + "/" + file } return file } type fxAppInterface interface { Start(context.Context) error Stop(context.Context) error Done() <-chan os.Signal } ================================================ FILE: cmd/server/cadence/cadence_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cadence import ( "context" "errors" "os" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/fx" "go.uber.org/fx/fxtest" ) type CadenceSuite struct { *require.Assertions suite.Suite } func TestCadenceSuite(t *testing.T) { suite.Run(t, new(CadenceSuite)) } func (s *CadenceSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *CadenceSuite) TestIsValidService() { s.True(isValidService("history")) s.True(isValidService("matching")) s.True(isValidService("frontend")) s.False(isValidService("cadence-history")) s.False(isValidService("cadence-matching")) s.False(isValidService("cadence-frontend")) s.False(isValidService("foobar")) } func (s *CadenceSuite) TestPath() { s.Equal("foo/bar", constructPathIfNeed("foo", "bar")) s.Equal("/bar", constructPathIfNeed("foo", "/bar")) } // MockFxApp implements fxAppInterface for testing type MockFxApp struct { StartFunc func(context.Context) error StopFunc func(context.Context) error DoneFunc func() <-chan os.Signal } func (m *MockFxApp) Start(ctx context.Context) error { return m.StartFunc(ctx) } func (m *MockFxApp) Stop(ctx context.Context) error { return m.StopFunc(ctx) } func (m *MockFxApp) Done() <-chan os.Signal { return m.DoneFunc() } // TestRunServicesSuccess tests successful service execution func TestRunServicesSuccess(t *testing.T) { // Create a test application using fxtest app := fxtest.New(t, fx.Provide(func() string { return "test-service" }), fx.Invoke(func(s string) { // Just a simple component that does nothing }), ) // Create a done channel done := make(chan os.Signal, 1) // Wrap fxtest.App in our interface appInterface := &MockFxApp{ StartFunc: app.Start, StopFunc: app.Stop, DoneFunc: func() <-chan os.Signal { return done }, } // Run in a goroutine errCh := make(chan error, 1) go func() { errCh <- runServices([]string{"service1"}, func(name string) fxAppInterface { return appInterface }) }() // Give it a moment to start time.Sleep(50 * time.Millisecond) // Send signal to stop close(done) // Check result err := <-errCh assert.NoError(t, err) } // TestRunServicesStartError tests failure during service start func TestRunServicesStartError(t *testing.T) { // Create a mock app that fails on start startError := errors.New("failed to start") app := &MockFxApp{ StartFunc: func(ctx context.Context) error { return startError }, StopFunc: func(ctx context.Context) error { return nil }, DoneFunc: func() <-chan os.Signal { ch := make(chan os.Signal) return ch }, } // Run the services err := runServices([]string{"service1"}, func(name string) fxAppInterface { return app }) // Verify error was returned assert.Error(t, err) assert.True(t, errors.Is(err, startError), "Error chain should contain the original error") } // TestRunServicesStopError tests failure during service stop func TestRunServicesStopError(t *testing.T) { // Create a mock app that fails on stop stopError := errors.New("failed to stop") done := make(chan os.Signal, 1) app := &MockFxApp{ StartFunc: func(ctx context.Context) error { return nil }, StopFunc: func(ctx context.Context) error { return stopError }, DoneFunc: func() <-chan os.Signal { return done }, } // Run in a goroutine errCh := make(chan error, 1) go func() { errCh <- runServices([]string{"service1"}, func(name string) fxAppInterface { return app }) }() // Give it a moment to start time.Sleep(50 * time.Millisecond) // Signal that the service is done close(done) // Check the error err := <-errCh assert.Error(t, err) assert.True(t, errors.Is(err, stopError), "Error chain should contain the stop error") } // TestRunServicesCascadeFailure tests that when one service fails, others get stopped func TestRunServicesCascadeFailure(t *testing.T) { // We'll use this to track context cancellation var contextCancelled bool var contextMu sync.Mutex // Create two apps - one will fail, one will succeed but should be stopped startErr := errors.New("service 2 failed to start") // Track app lifecycle events app1Started := false app1Stopped := false app2Started := false // First app - will start successfully done1 := make(chan os.Signal, 1) app1 := &MockFxApp{ StartFunc: func(ctx context.Context) error { app1Started = true // Monitor for context cancellation in a goroutine go func() { <-ctx.Done() contextMu.Lock() contextCancelled = true contextMu.Unlock() // Close done channel to simulate app stopping due to context close(done1) }() return nil }, StopFunc: func(ctx context.Context) error { app1Stopped = true return nil }, DoneFunc: func() <-chan os.Signal { return done1 }, } // Second app - will fail to start app2 := &MockFxApp{ StartFunc: func(ctx context.Context) error { app2Started = true return startErr }, StopFunc: func(ctx context.Context) error { t.Fatal("App2 Stop should never be called since Start failed") return nil }, DoneFunc: func() <-chan os.Signal { ch := make(chan os.Signal) return ch // Never signals }, } // Build app provider that returns different apps for different services appProvider := func(name string) fxAppInterface { switch name { case "service1": return app1 case "service2": return app2 default: t.Fatalf("Unexpected service name: %s", name) return nil } } // Run services err := runServices([]string{"service1", "service2"}, appProvider) // Verify results require.Error(t, err, "Should return an error") assert.True(t, app1Started, "App1 should have started") assert.True(t, app2Started, "App2 should have started") assert.True(t, app1Stopped, "App1 should have been stopped due to context cancellation") // Check if context was cancelled contextMu.Lock() assert.True(t, contextCancelled, "Context should have been cancelled") contextMu.Unlock() // Check error content assert.Contains(t, err.Error(), "service 2") } ================================================ FILE: cmd/server/cadence/fx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cadence import ( "context" "fmt" "github.com/uber-go/tally" "go.uber.org/fx" "go.uber.org/zap" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock/clockfx" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicconfigfx" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/logfx" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/metrics/metricsfx" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/rpc/rpcfx" "github.com/uber/cadence/common/service" shardDistributorCfg "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/sharddistributorfx" "github.com/uber/cadence/service/sharddistributor/store/etcd" "github.com/uber/cadence/tools/cassandra" "github.com/uber/cadence/tools/sql" ) var _commonModule = fx.Options( config.Module, dynamicconfigfx.Module, logfx.Module, metricsfx.Module, clockfx.Module) // Module provides a cadence server initialization with root components. // AppParams allows to provide optional/overrides for implementation specific dependencies. func Module(serviceName string) fx.Option { if serviceName == service.ShortName(service.ShardDistributor) { return fx.Options( fx.Supply(serviceContext{ Name: serviceName, FullName: service.FullName(serviceName), }), fx.Provide(func(cfg config.Config) shardDistributorCfg.ShardDistribution { return cfg.ShardDistribution }), // Decorate both logger so all components use proper service name. fx.Decorate(func(z *zap.Logger, l log.Logger) (*zap.Logger, log.Logger) { return z.With(zap.String("service", service.ShardDistributor)), l.WithTags(tag.Service(service.ShardDistributor)) }), etcd.Module, rpcfx.Module, sharddistributorfx.Module) } return fx.Options( fx.Supply(serviceContext{ Name: serviceName, FullName: service.FullName(serviceName), }), fx.Provide(NewApp), // empty invoke so fx won't drop the application from the dependencies. fx.Invoke(func(a *App) {}), ) } type AppParams struct { fx.In Service string `name:"service"` AppContext config.Context Config config.Config Logger log.Logger LifeCycle fx.Lifecycle DynamicConfig dynamicconfig.Client Scope tally.Scope MetricsClient metrics.Client } // NewApp created a new Application from pre initalized config and logger. func NewApp(params AppParams) *App { app := &App{ cfg: params.Config, logger: params.Logger, service: params.Service, dynamicConfig: params.DynamicConfig, scope: params.Scope, metricsClient: params.MetricsClient, } params.LifeCycle.Append(fx.StartHook(app.verifySchema)) params.LifeCycle.Append(fx.StartStopHook(app.Start, app.Stop)) return app } // App is a fx application that registers itself into fx.Lifecycle and runs. // It is done implicitly, since it provides methods Start and Stop which are picked up by fx. type App struct { cfg config.Config rootDir string logger log.Logger dynamicConfig dynamicconfig.Client scope tally.Scope metricsClient metrics.Client daemon common.Daemon service string } func (a *App) Start(_ context.Context) error { a.daemon = newServer(a.service, a.cfg, a.logger, a.dynamicConfig, a.scope, a.metricsClient) a.daemon.Start() return nil } func (a *App) Stop(ctx context.Context) error { a.daemon.Stop() return nil } func (a *App) verifySchema(ctx context.Context) error { // cassandra schema version validation if err := cassandra.VerifyCompatibleVersion(a.cfg.Persistence, gocql.Quorum); err != nil { return fmt.Errorf("cassandra schema version compatibility check failed: %w", err) } // sql schema version validation if err := sql.VerifyCompatibleVersion(a.cfg.Persistence); err != nil { return fmt.Errorf("sql schema version compatibility check failed: %w", err) } return nil } type serviceContext struct { fx.Out Name string `name:"service"` FullName string `name:"service-full-name"` } ================================================ FILE: cmd/server/cadence/fx_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cadence import ( "flag" "fmt" "os" "testing" "github.com/stretchr/testify/require" "go.uber.org/fx" "go.uber.org/fx/fxtest" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/service" "github.com/uber/cadence/testflags" _ "github.com/uber/cadence/service/sharddistributor/store/etcd" // needed for shard distributor storage ) func TestFxDependencies(t *testing.T) { err := fx.ValidateApp(_commonModule, fx.Supply(appContext{ CfgContext: config.Context{ Environment: "", Zone: "", }, ConfigDir: "", RootDir: "", }), Module("")) require.NoError(t, err) } func TestFxDependenciesForShardDistributor(t *testing.T) { err := fx.ValidateApp(_commonModule, fx.Supply(appContext{ CfgContext: config.Context{ Environment: "", Zone: "", }, ConfigDir: "", RootDir: "", }), Module(service.ShortName(service.ShardDistributor))) require.NoError(t, err) } func TestShardDistributorStartStop(t *testing.T) { flag.Parse() testflags.RequireEtcd(t) wd, err := os.Getwd() require.NoError(t, err) app := fxtest.New(t, _commonModule, fx.Supply(appContext{ CfgContext: config.Context{ Environment: "development", Zone: "", }, ConfigDir: fmt.Sprintf("%s/testdata/config", wd), RootDir: "", }), Module(service.ShortName(service.ShardDistributor))) app.RequireStart().RequireStop() } ================================================ FILE: cmd/server/cadence/server.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cadence import ( "context" "fmt" "time" "github.com/startreedata/pinot-client-go/pinot" "github.com/uber-go/tally" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/compatibility" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" sharddistributorClient "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/client/wrappers/errorinjectors" "github.com/uber/cadence/client/wrappers/grpc" "github.com/uber/cadence/client/wrappers/metered" "github.com/uber/cadence/client/wrappers/retryable" timeoutwrapper "github.com/uber/cadence/client/wrappers/timeout" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/blobstore/filestore" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/isolationgroup/isolationgroupapi" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/messaging/kafka" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/peerprovider/ringpopprovider" pnt "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/frontend" "github.com/uber/cadence/service/history" "github.com/uber/cadence/service/matching" "github.com/uber/cadence/service/sharddistributor/client/spectatorclient" "github.com/uber/cadence/service/worker" diagnosticsInvariant "github.com/uber/cadence/service/worker/diagnostics/invariant" "github.com/uber/cadence/service/worker/diagnostics/invariant/failure" "github.com/uber/cadence/service/worker/diagnostics/invariant/retry" "github.com/uber/cadence/service/worker/diagnostics/invariant/timeout" ) type ( server struct { name string cfg config.Config logger log.Logger doneC chan struct{} daemon common.Daemon dynamicCfgClient dynamicconfig.Client scope tally.Scope metricsClient metrics.Client } ) // newServer returns a new instance of a daemon // that represents a cadence service func newServer(service string, cfg config.Config, logger log.Logger, dynamicCfgClient dynamicconfig.Client, scope tally.Scope, metricsClient metrics.Client) common.Daemon { return &server{ cfg: cfg, name: service, doneC: make(chan struct{}), logger: logger, dynamicCfgClient: dynamicCfgClient, scope: scope, metricsClient: metricsClient, } } // Start starts the server func (s *server) Start() { s.daemon = s.startService() } // Stop stops the server func (s *server) Stop() { if s.daemon == nil { return } select { case <-s.doneC: default: s.daemon.Stop() select { case <-s.doneC: case <-time.After(time.Minute): s.logger.Warn("timed out waiting for server to exit") } } } // startService starts a service with the given name and config func (s *server) startService() common.Daemon { svcCfg, err := s.cfg.GetServiceConfig(s.name) if err != nil { s.logger.Fatal(err.Error()) } params := resource.Params{ Name: service.FullName(s.name), Logger: s.logger.WithTags(tag.Service(service.FullName(s.name))), PersistenceConfig: s.cfg.Persistence, DynamicConfig: s.dynamicCfgClient, RPCConfig: svcCfg.RPC, } clusterGroupMetadata := s.cfg.ClusterGroupMetadata dc := dynamicconfig.NewCollection( params.DynamicConfig, params.Logger, dynamicproperties.ClusterNameFilter(clusterGroupMetadata.CurrentClusterName), ) params.MetricScope = s.scope params.MetricsClient = s.metricsClient rpcParams, err := rpc.NewParams(params.Name, &s.cfg, dc, params.Logger, params.MetricsClient) if err != nil { s.logger.Fatal("error creating rpc factory params", tag.Error(err)) } rpcParams.OutboundsBuilder = rpc.CombineOutbounds( rpcParams.OutboundsBuilder, rpc.NewCrossDCOutbounds(clusterGroupMetadata.ClusterGroup, rpc.NewDNSPeerChooserFactory(s.cfg.PublicClient.RefreshInterval, params.Logger)), ) rpcFactory := rpc.NewFactory(params.Logger, rpcParams) params.RPCFactory = rpcFactory peerProvider, err := ringpopprovider.New( params.Name, &s.cfg.Ringpop, rpcFactory.GetTChannel(), membership.PortMap{ membership.PortGRPC: svcCfg.RPC.GRPCPort, membership.PortTchannel: svcCfg.RPC.Port, }, params.Logger, ) if err != nil { s.logger.Fatal("ringpop provider failed", tag.Error(err)) } shardDistributorClient := s.createShardDistributorClient(params, dc) var spectator spectatorclient.Spectator if shardDistributorClient != nil && len(s.cfg.ShardDistributorMatchingConfig.Namespaces) > 0 { if len(s.cfg.ShardDistributorMatchingConfig.Namespaces) > 1 { s.logger.Fatal("spectator does not support multiple namespaces", tag.Value(s.cfg.ShardDistributorMatchingConfig.Namespaces)) } matchingShardDistributionMode := dc.GetStringProperty(dynamicproperties.MatchingShardDistributionMode) spectatorParams := spectatorclient.Params{ Client: shardDistributorClient, MetricsScope: params.MetricScope, Logger: params.Logger, Config: s.cfg.ShardDistributorMatchingConfig, TimeSource: clock.NewRealTimeSource(), Enabled: func() bool { return membership.ModeKey(matchingShardDistributionMode()) != membership.ModeKeyHashRing }, } namespace := s.cfg.ShardDistributorMatchingConfig.Namespaces[0].Namespace spectator, err = spectatorclient.NewSpectatorWithNamespace( spectatorParams, namespace, ) if err != nil { s.logger.Fatal("error creating spectator", tag.Error(err)) } // Start the spectator to begin watching namespace state if err := spectator.Start(context.Background()); err != nil { s.logger.Fatal("error starting spectator", tag.Error(err)) } } else { s.logger.Warn("Shard distributor client not configured, spectator will not be started") } params.HashRings = make(map[string]membership.SingleProvider) for _, s := range service.ListWithRing { params.HashRings[s] = membership.NewHashring(s, peerProvider, clock.NewRealTimeSource(), params.Logger, params.MetricsClient.Scope(metrics.HashringScope)) } wrappedRings := s.wrapHashRingsWithShardDistributor(params.HashRings, spectator, dc, params.Logger) params.MembershipResolver, err = membership.NewResolver( peerProvider, params.MetricsClient, params.Logger, wrappedRings, ) if err != nil { s.logger.Fatal("error creating membership monitor", tag.Error(err)) } params.PProfInitializer = svcCfg.PProf.NewInitializer(params.Logger) params.ClusterRedirectionPolicy = s.cfg.ClusterGroupMetadata.ClusterRedirectionPolicy params.GetIsolationGroups = getFromDynamicConfig(params, dc) params.ClusterMetadata = cluster.NewMetadata( *clusterGroupMetadata, dc.GetBoolPropertyFilteredByDomain(dynamicproperties.UseNewInitialFailoverVersion), params.MetricsClient, params.Logger, ) advancedVisMode := dc.GetStringProperty( dynamicproperties.WriteVisibilityStoreName, )() isAdvancedVisEnabled := common.IsAdvancedVisibilityWritingEnabled(advancedVisMode, params.PersistenceConfig.IsAdvancedVisibilityConfigExist()) if isAdvancedVisEnabled { params.MessagingClient = kafka.NewKafkaClient(&s.cfg.Kafka, params.MetricsClient, params.Logger, params.MetricScope, isAdvancedVisEnabled) } else { params.MessagingClient = nil } if isAdvancedVisEnabled { s.setupVisibilityClients(¶ms) } publicClientConfig := params.RPCFactory.GetDispatcher().ClientConfig(rpc.OutboundPublicClient) if rpc.IsGRPCOutbound(publicClientConfig) { params.PublicClient = compatibility.NewThrift2ProtoAdapter( apiv1.NewDomainAPIYARPCClient(publicClientConfig), apiv1.NewWorkflowAPIYARPCClient(publicClientConfig), apiv1.NewWorkerAPIYARPCClient(publicClientConfig), apiv1.NewVisibilityAPIYARPCClient(publicClientConfig), ) } else { params.PublicClient = workflowserviceclient.New(publicClientConfig) } params.ArchivalMetadata = archiver.NewArchivalMetadata( dc, s.cfg.Archival.History.Status, s.cfg.Archival.History.EnableRead, s.cfg.Archival.Visibility.Status, s.cfg.Archival.Visibility.EnableRead, &s.cfg.DomainDefaults.Archival, ) params.ArchiverProvider = provider.NewArchiverProvider(s.cfg.Archival.History.Provider, s.cfg.Archival.Visibility.Provider) params.PersistenceConfig.TransactionSizeLimit = dc.GetIntProperty(dynamicproperties.TransactionSizeLimit) params.PersistenceConfig.ErrorInjectionRate = dc.GetFloat64Property(dynamicproperties.PersistenceErrorInjectionRate) params.AuthorizationConfig = s.cfg.Authorization params.BlobstoreClient, err = filestore.NewFilestoreClient(s.cfg.Blobstore.Filestore) if err != nil { s.logger.Warn("failed to create file blobstore client, will continue startup without it: %v", tag.Error(err)) params.BlobstoreClient = nil } params.AsyncWorkflowQueueProvider, err = queue.NewAsyncQueueProvider(s.cfg.AsyncWorkflowQueues) if err != nil { s.logger.Fatal("error creating async queue provider", tag.Error(err)) } params.KafkaConfig = s.cfg.Kafka params.DiagnosticsInvariants = []diagnosticsInvariant.Invariant{timeout.NewInvariant(timeout.Params{Client: params.PublicClient}), failure.NewInvariant(), retry.NewInvariant()} params.ShardDistributorMatchingConfig = s.cfg.ShardDistributorMatchingConfig params.Logger.Info("Starting service " + s.name) var daemon common.Daemon switch params.Name { case service.Frontend: daemon, err = frontend.NewService(¶ms) case service.History: daemon, err = history.NewService(¶ms) case service.Matching: daemon, err = matching.NewService(¶ms) case service.Worker: daemon, err = worker.NewService(¶ms) default: params.Logger.Fatal("unknown service", tag.Service(params.Name)) } if err != nil { params.Logger.Fatal("Fail to start "+s.name+" service ", tag.Error(err)) } go execute(daemon, s.doneC) return daemon } func (*server) wrapHashRingsWithShardDistributor( hashRings map[string]membership.SingleProvider, spectator spectatorclient.Spectator, dc *dynamicconfig.Collection, logger log.Logger, ) map[string]membership.SingleProvider { if _, ok := hashRings[service.Matching]; ok { hashRings[service.Matching] = membership.NewShardDistributorResolver( spectator, dc.GetStringProperty(dynamicproperties.MatchingShardDistributionMode), dc.GetBoolProperty(dynamicproperties.MatchingExcludeShortLivedTaskListsFromShardManager), dc.GetIntProperty(dynamicproperties.MatchingPercentageOnboardedToShardManager), hashRings[service.Matching], logger, ) } return hashRings } func (*server) createShardDistributorClient( params resource.Params, dc *dynamicconfig.Collection, ) sharddistributorClient.Client { shardDistributorClientConfig, ok := params.RPCFactory.GetDispatcher().OutboundConfig(service.ShardDistributor) var shardDistributorClient sharddistributorClient.Client if ok { if !rpc.IsGRPCOutbound(shardDistributorClientConfig) { params.Logger.Error("shard distributor client does not support non-GRPC outbound will fail back to hashring") return nil } if shardDistributorClientConfig.Outbounds.Stream == nil { params.Logger.Error("shard distributor client does not support stream outbound will fail back to hashring") return nil } shardDistributorClient = grpc.NewShardDistributorClient( sharddistributorv1.NewShardDistributorAPIYARPCClient(shardDistributorClientConfig), ) shardDistributorClient = timeoutwrapper.NewShardDistributorClient(shardDistributorClient, timeoutwrapper.ShardDistributorDefaultTimeout) shardDistributorClient = retryable.NewShardDistributorClient( shardDistributorClient, common.CreateShardDistributorServiceRetryPolicy(), common.IsServiceTransientError, ) if errorRate := dc.GetFloat64Property(dynamicproperties.ShardDistributorErrorInjectionRate)(); errorRate != 0 { shardDistributorClient = errorinjectors.NewShardDistributorClient(shardDistributorClient, errorRate, params.Logger) } if params.MetricsClient != nil { shardDistributorClient = metered.NewShardDistributorClient(shardDistributorClient, params.MetricsClient) } } return shardDistributorClient } // execute runs the daemon in a separate go routine func execute(d common.Daemon, doneC chan struct{}) { d.Start() close(doneC) } // there are multiple circumstances: // 1. advanced visibility store == elasticsearch, use ESClient and visibilityDualManager // 2. advanced visibility store == pinot and in process of migration, use ESClient, PinotClient and and visibilityTripleManager // 3. advanced visibility store == pinot and not migrating, use PinotClient and visibilityDualManager // 4. advanced visibility store == opensearch and not migrating, this performs the same as 1, just use different version ES client and visibilityDualManager // 5. advanced visibility store == opensearch and in process of migration, use ESClient and visibilityTripleManager func (s *server) setupVisibilityClients(params *resource.Params) { advancedVisStoreKey := s.cfg.Persistence.AdvancedVisibilityStore advancedVisStore, ok := s.cfg.Persistence.DataStores[advancedVisStoreKey] if !ok { s.logger.Fatal("Cannot find advanced visibility store in config", tag.Value(advancedVisStoreKey)) } // Handle advanced visibility store based on type and migration state switch advancedVisStoreKey { case constants.PinotVisibilityStoreName: s.setupPinotClient(params, advancedVisStore) case constants.OSVisibilityStoreName: s.setupOSClient(params, advancedVisStore) default: // Assume Elasticsearch by default s.setupESClient(params) } } func (s *server) setupPinotClient(params *resource.Params, advancedVisStore config.DataStore) { params.PinotConfig = advancedVisStore.Pinot pinotBroker := params.PinotConfig.Broker pinotRawClient, err := pinot.NewFromBrokerList([]string{pinotBroker}) if err != nil || pinotRawClient == nil { s.logger.Fatal("Creating Pinot visibility client failed", tag.Error(err)) } params.PinotClient = pnt.NewPinotClient(pinotRawClient, params.Logger, params.PinotConfig) if advancedVisStore.Pinot.Migration.Enabled { s.setupESClient(params) } } func (s *server) setupESClient(params *resource.Params) { esVisibilityStore, ok := s.cfg.Persistence.DataStores[constants.ESVisibilityStoreName] if !ok { s.logger.Fatal("Cannot find Elasticsearch visibility store in config") } params.ESConfig = esVisibilityStore.ElasticSearch params.ESConfig.SetUsernamePassword() esClient, err := elasticsearch.NewGenericClient(params.ESConfig, params.Logger) if err != nil { s.logger.Fatal("Error creating Elasticsearch client", tag.Error(err)) } params.ESClient = esClient err = validateIndex(params.ESConfig) if err != nil { s.logger.Fatal("Error creating OpenSearch client", tag.Error(err)) } } func (s *server) setupOSClient(params *resource.Params, advancedVisStore config.DataStore) { // OpenSearch client setup (same structure as Elasticsearch, just version difference) // This is only for migration purposes params.OSConfig = advancedVisStore.ElasticSearch params.OSConfig.SetUsernamePassword() osClient, err := elasticsearch.NewGenericClient(params.OSConfig, params.Logger) if err != nil { s.logger.Fatal("Error creating OpenSearch client", tag.Error(err)) } params.OSClient = osClient err = validateIndex(params.OSConfig) if err != nil { s.logger.Fatal("Error creating OpenSearch client", tag.Error(err)) } if advancedVisStore.ElasticSearch.Migration.Enabled { s.setupESClient(params) } else { // to avoid code duplication, we will use es-visibility and set the version to os2 instead of using os-visibility directly params.ESConfig = advancedVisStore.ElasticSearch params.ESConfig.SetUsernamePassword() params.ESClient = osClient } } func validateIndex(config *config.ElasticSearchConfig) error { indexName, ok := config.Indices[constants.VisibilityAppName] if !ok || len(indexName) == 0 { return fmt.Errorf("visibility index is missing in config") } return nil } func getFromDynamicConfig(params resource.Params, dc *dynamicconfig.Collection) func() []string { return func() []string { res, err := isolationgroupapi.MapAllIsolationGroupsResponse(dc.GetListProperty(dynamicproperties.AllIsolationGroups)()) if err != nil { params.Logger.Error("failed to get isolation groups from config", tag.Error(err)) return nil } return res } } ================================================ FILE: cmd/server/cadence/server_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:build !race // +build !race package cadence import ( "context" "slices" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/fx/fxtest" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/sql/sqlplugin/sqlite" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" ) type ServerSuite struct { *require.Assertions suite.Suite logger log.Logger } func TestServerSuite(t *testing.T) { suite.Run(t, new(ServerSuite)) } func (s *ServerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.logger = testlogger.New(s.T()) } /* TestServerStartup tests the startup logic for the binary. When this fails, you should be able to reproduce by running "cadence-server start" If you need to run locally, make sure Cassandra is up and schema is installed(run `make install-schema`) */ func (s *ServerSuite) TestServerStartup() { env := "development" zone := "" rootDir := "../../../" configDir := constructPathIfNeed(rootDir, "config") s.T().Logf("Loading config; env=%v,zone=%v,configDir=%v\n", env, zone, configDir) var cfg config.Config err := config.Load(env, configDir, zone, &cfg) if err != nil { s.logger.Fatal("Config file corrupted.", tag.Error(err)) } // set up sqlite persistence layer and apply schema to sqlite db testBase := pt.NewTestBaseWithSQL(s.T(), sqlite.GetTestClusterOption()) cfg.Persistence = testBase.Config() testBase.Setup() s.T().Logf("config=\n%v\n", cfg.String()) cfg.DynamicConfig.FileBased.Filepath = constructPathIfNeed(rootDir, cfg.DynamicConfig.FileBased.Filepath) if err := cfg.ValidateAndFillDefaults(); err != nil { s.logger.Fatal("config validation failed", tag.Error(err)) } logger := testlogger.New(s.T()) lifecycle := fxtest.NewLifecycle(s.T()) var daemons []common.Daemon // Shard distributor should be tested separately distributorShortName := service.ShortName(service.ShardDistributor) services := slices.DeleteFunc(service.ShortNames(service.List), func(s string) bool { return s == distributorShortName }) for _, svc := range services { server := newServer(svc, cfg, logger, dynamicconfig.NewNopClient(), tally.NoopScope, metrics.NewNoopMetricsClient()) daemons = append(daemons, server) server.Start() } timer := time.NewTimer(time.Second * 10) <-timer.C s.NoError(lifecycle.Stop(context.Background())) for _, daemon := range daemons { daemon.Stop() } } func TestSettingGettingZonalIsolationGroupsFromIG(t *testing.T) { ctrl := gomock.NewController(t) client := dynamicconfig.NewMockClient(ctrl) client.EXPECT().GetListValue(dynamicproperties.AllIsolationGroups, gomock.Any()).Return([]interface{}{ "zone-1", "zone-2", }, nil) dc := dynamicconfig.NewCollection(client, log.NewNoop()) assert.NotPanics(t, func() { fn := getFromDynamicConfig(resource.Params{ Logger: log.NewNoop(), }, dc) out := fn() assert.Equal(t, []string{"zone-1", "zone-2"}, out) }) } func TestSettingGettingZonalIsolationGroupsFromIGError(t *testing.T) { ctrl := gomock.NewController(t) client := dynamicconfig.NewMockClient(ctrl) client.EXPECT().GetListValue(dynamicproperties.AllIsolationGroups, gomock.Any()).Return(nil, assert.AnError) dc := dynamicconfig.NewCollection(client, log.NewNoop()) assert.NotPanics(t, func() { getFromDynamicConfig(resource.Params{ Logger: log.NewNoop(), }, dc)() }) } ================================================ FILE: cmd/server/cadence/testdata/config/development.yaml ================================================ shardDistribution: leaderStore: storageParams: endpoints: - ${ETCD_ENDPOINTS:"localhost:2379"} store: storageParams: endpoints: - ${ETCD_ENDPOINTS:"localhost:2379"} election: leaderPeriod: 30s maxRandomDelay: 5s failedElectionCooldown: 30s namespaces: - name: cadence-matching-dev type: ephemeral mode: shadow process: period: 2s persistence: defaultStore: cass-default visibilityStore: cass-visibility numHistoryShards: 4 datastores: cass-default: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence" connectTimeout: 2s # defaults to 2s if not defined timeout: 5s # defaults to 10s if not defined consistency: LOCAL_QUORUM # default value serialConsistency: LOCAL_SERIAL # default value cass-visibility: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_visibility" services: shard-distributor: rpc: port: 7941 grpcPort: 7943 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7942 ringpop: name: cadence bootstrapMode: hosts bootstrapHosts: [ "127.0.0.1:7941"] maxJoinDuration: 30s clusterGroupMetadata: failoverVersionIncrement: 10 primaryClusterName: "cluster0" currentClusterName: "cluster0" clusterGroup: cluster0: enabled: true initialFailoverVersion: 0 newInitialFailoverVersion: 1 # migrating to this new failover version rpcAddress: "localhost:7833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" ================================================ FILE: cmd/server/go.mod ================================================ module github.com/uber/cadence/cmd/server go 1.23.0 toolchain go1.23.4 // build against the current code in the "main" (and gcloud) module, not a specific SHA. // // anyone outside this repo using this needs to ensure that both the "main" module and this module // are at the same SHA for consistency, but internally we can cheat by telling Go that it's at a // relative file path. replace github.com/uber/cadence => ../.. replace github.com/uber/cadence/common/archiver/gcloud => ../../common/archiver/gcloud require ( github.com/VividCortex/mysqlerr v1.0.0 // indirect github.com/aws/aws-sdk-go v1.54.12 // indirect github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748 // indirect github.com/cch123/elasticsql v0.0.0-20190321073543-a1a440758eb9 // indirect github.com/cristalhq/jwt/v3 v3.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/gocql/gocql v0.0.0-20211015133455-b225f9b53fa1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-version v1.2.0 // indirect github.com/iancoleman/strcase v0.2.0 // indirect github.com/jmoiron/sqlx v1.2.1-0.20200615141059-0794cb1f47ee // indirect github.com/lib/pq v1.2.0 // indirect github.com/m3db/prometheus_client_golang v0.8.1 // indirect github.com/olivere/elastic v6.2.37+incompatible // indirect github.com/olivere/elastic/v7 v7.0.21 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 // indirect github.com/robfig/cron v1.2.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/startreedata/pinot-client-go v0.2.0 // latest release supports pinot v0.12.0 which is also internal version github.com/stretchr/testify v1.10.0 github.com/uber-go/tally v3.3.15+incompatible github.com/uber/cadence-idl v0.0.0-20260313102523-57782092adb4 github.com/uber/ringpop-go v0.8.5 // indirect github.com/uber/tchannel-go v1.22.2 // indirect github.com/valyala/fastjson v1.4.1 // indirect github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/cadence v0.19.0 go.uber.org/config v1.4.0 // indirect go.uber.org/fx v1.23.0 go.uber.org/multierr v1.10.0 go.uber.org/thriftrw v1.29.2 // indirect go.uber.org/yarpc v1.70.3 // indirect go.uber.org/zap v1.26.0 golang.org/x/net v0.40.0 // indirect golang.org/x/sync v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.22.0 // indirect gonum.org/v1/gonum v0.7.0 // indirect google.golang.org/grpc v1.59.0 // indirect gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) require ( github.com/uber/cadence v0.0.0-00010101000000-000000000000 github.com/uber/cadence/common/archiver/gcloud v0.0.0-00010101000000-000000000000 go.uber.org/automaxprocs v1.6.0 go.uber.org/mock v0.5.0 ) require ( github.com/IBM/sarama v1.45.2 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/fatih/color v1.13.0 // indirect github.com/google/gofuzz v1.0.0 // indirect github.com/mattn/go-colorable v0.1.9 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/ncruces/go-sqlite3 v0.22.0 // indirect github.com/ncruces/julianday v1.0.0 // indirect github.com/opensearch-project/opensearch-go/v4 v4.1.0 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/tetratelabs/wazero v1.8.2 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect go.etcd.io/etcd/api/v3 v3.5.5 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect go.etcd.io/etcd/client/v3 v3.5.5 // indirect ) require ( cloud.google.com/go v0.110.8 // indirect cloud.google.com/go/compute v1.23.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.3 // indirect cloud.google.com/go/storage v1.30.1 // indirect github.com/BurntSushi/toml v1.3.2 // indirect github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect github.com/apache/thrift v0.17.0 // indirect github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/eapache/go-resiliency v1.7.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect github.com/eapache/queue v1.1.0 // indirect github.com/emirpasic/gods v0.0.0-20190624094223-e689965507ab // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/go-zookeeper/zk v1.0.3 // indirect github.com/gogo/googleapis v1.3.2 // indirect github.com/gogo/status v1.1.0 // indirect github.com/golang-jwt/jwt/v5 v5.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/s2a-go v0.1.4 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.4 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect github.com/jcmturner/gofork v1.7.6 // indirect github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/jessevdk/go-flags v1.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.5.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kisielk/errcheck v1.5.0 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/m3db/prometheus_client_model v0.1.0 // indirect github.com/m3db/prometheus_common v0.1.0 // indirect github.com/m3db/prometheus_procfs v0.8.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.11.1 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/uber-common/bark v1.2.1 // indirect github.com/uber-go/mapdecode v1.0.0 // indirect github.com/urfave/cli/v2 v2.27.4 github.com/xdg/stringprep v1.0.0 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/dig v1.18.0 // indirect go.uber.org/net/metrics v1.3.0 // indirect golang.org/x/crypto v0.38.0 // indirect golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/mod v0.18.0 // indirect golang.org/x/oauth2 v0.11.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.128.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.3.2 // indirect ) ================================================ FILE: cmd/server/go.sum ================================================ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME= cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go/compute v1.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0= cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/iam v1.1.3 h1:18tKG7DzydKWUnLjonWcJO6wjSCAtzh4GcRKlH/Hrzc= cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/IBM/sarama v1.45.2 h1:8m8LcMCu3REcwpa7fCP6v2fuPuzVwXDAM2DOv3CBrKw= github.com/IBM/sarama v1.45.2/go.mod h1:ppaoTcVdGv186/z6MEKsMm70A5fwJfRTpstI37kVn3Y= github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/VividCortex/mysqlerr v1.0.0 h1:5pZ2TZA+YnzPgzBfiUWGqWmKDVNBdrkf9g+DNe1Tiq8= github.com/VividCortex/mysqlerr v1.0.0/go.mod h1:xERx8E4tBhLvpjzdUyQiSfUxeMcATEQrflDAfXsqcAE= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.17.0 h1:cMd2aj52n+8VoAtvSvLn4kDC3aZ6IAkBuqWQ2IDu7wo= github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= github.com/aws/aws-sdk-go v1.34.13/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.54.12 h1:xPDB+GSBZq0rJbmDZF+EyfMbnWRyfEPcn7PZ7bJjXSw= github.com/aws/aws-sdk-go v1.54.12/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3 h1:wOysYcIdqv3WnvwqFFzrYCFALPED7qkUGaLXu359GSc= github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3/go.mod h1:UMqtWQTnOe4byzwe7Zhwh8f8s+36uszN51sJrSIZlTE= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748 h1:bXxS5/Z3/dfc8iFniQfgogNBomo0u+1//9eP+jl8GVo= github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= github.com/cch123/elasticsql v0.0.0-20190321073543-a1a440758eb9 h1:2rukpuvOpZryti4j58JHH5f0qJXxYdTYpkgNYx8iLdg= github.com/cch123/elasticsql v0.0.0-20190321073543-a1a440758eb9/go.mod h1:h4Tt1A91nOVAYsWdoxlXwKYPfxkxeTuRFkEMUQaRVBo= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cristalhq/jwt/v3 v3.1.0 h1:iLeL9VzB0SCtjCy9Kg53rMwTcrNm+GHyVcz2eUujz6s= github.com/cristalhq/jwt/v3 v3.1.0/go.mod h1:XOnIXst8ozq/esy5N1XOlSyQqBd+84fxJ99FK+1jgL8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA= github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/emirpasic/gods v0.0.0-20190624094223-e689965507ab h1:eTc1vwMHNg4WtS95PtYi3FFCKwlPjtN/Lw9IALTRtd8= github.com/emirpasic/gods v0.0.0-20190624094223-e689965507ab/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gocql/gocql v0.0.0-20211015133455-b225f9b53fa1 h1:px9qUCy/RNJNsfCam4m2IxWGxNuimkrioEF0vrrbPsg= github.com/gocql/gocql v0.0.0-20211015133455-b225f9b53fa1/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.3.2 h1:kX1es4djPJrsDhY7aZKJy7aZasdcB5oSOEphMjSB53c= github.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3-0.20190920234318-1680a479a2cf/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.4 h1:uGy6JWR/uMIILU8wbf+OkstIrNiMjGpEIyhx8f6W7s4= github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.1-0.20200615141059-0794cb1f47ee h1:59lyMGvZusByi7Rvctn8cxdVAjhiOnqCv3G5DrYApYQ= github.com/jmoiron/sqlx v1.2.1-0.20200615141059-0794cb1f47ee/go.mod h1:ClpsPFzLpSBl7MvJ+BhV0JHz4vmKRBarpvZ9644v9Oo= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/m3db/prometheus_client_golang v0.8.1 h1:t7w/tcFws81JL1j5sqmpqcOyQOpH4RDOmIe3A3fdN3w= github.com/m3db/prometheus_client_golang v0.8.1/go.mod h1:8R/f1xYhXWq59KD/mbRqoBulXejss7vYtYzWmruNUwI= github.com/m3db/prometheus_client_model v0.1.0 h1:cg1+DiuyT6x8h9voibtarkH1KT6CmsewBSaBhe8wzLo= github.com/m3db/prometheus_client_model v0.1.0/go.mod h1:Qfsxn+LypxzF+lNhak7cF7k0zxK7uB/ynGYoj80zcD4= github.com/m3db/prometheus_common v0.1.0 h1:YJu6eCIV6MQlcwND24cRG/aRkZDX1jvYbsNNs1ZYr0w= github.com/m3db/prometheus_common v0.1.0/go.mod h1:EBmDQaMAy4B8i+qsg1wMXAelLNVbp49i/JOeVszQ/rs= github.com/m3db/prometheus_procfs v0.8.1 h1:LsxWzVELhDU9sLsZTaFLCeAwCn7bC7qecZcK4zobs/g= github.com/m3db/prometheus_procfs v0.8.1/go.mod h1:N8lv8fLh3U3koZx1Bnisj60GYUMDpWb09x1R+dmMOJo= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncruces/go-sqlite3 v0.22.0 h1:FkGSBhd0TY6e66k1LVhyEpA+RnG/8QkQNed5pjIk4cs= github.com/ncruces/go-sqlite3 v0.22.0/go.mod h1:ueXOZXYZS2OFQirCU3mHneDwJm5fGKHrtccYBeGEV7M= github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M= github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g= github.com/olivere/elastic v6.2.37+incompatible h1:UfSGJem5czY+x/LqxgeCBgjDn6St+z8OnsCuxwD3L0U= github.com/olivere/elastic v6.2.37+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGeB5G1iqDKVBWLNSYW8yfJ8= github.com/olivere/elastic/v7 v7.0.21 h1:58a2pMlLketCsLyKg8kJNJG+OZIFKrSQXX6gJBpqqlg= github.com/olivere/elastic/v7 v7.0.21/go.mod h1:Kh7iIsXIBl5qRQOBFoylCsXVTtye3keQU2Y/YbR7HD8= github.com/opensearch-project/opensearch-go/v4 v4.1.0 h1:YXNaMpMU0PC7suGyP13EuczkDT3K54QajgDnLKCZAz8= github.com/opensearch-project/opensearch-go/v4 v4.1.0/go.mod h1:aSTMFGSLEoiG19US6Oo5udvWCjHap3mRcWBNV8rAFak= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pborman/uuid v0.0.0-20160209185913-a97ce2ca70fa/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs983HfUfpkw9OTFD9tbBfAViHE= github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a h1:AA9vgIBDjMHPC2McaGPojgV2dcI78ZC0TLNhYCXEKH8= github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a/go.mod h1:lzZQ3Noex5pfAy7mkAeCjcBDteYU85uWWnJ/y6gKU8k= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.8.0/go.mod h1:PC/OgXc+UN7B4ALwvn1yzVZmVwvhXp5JsbBv6wSv6i0= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.9/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/samuel/go-thrift v0.0.0-20191111193933-5165175b40af h1:EiWVfh8mr40yFZEui2oF0d45KgH48PkB2H0Z0GANvSI= github.com/samuel/go-thrift v0.0.0-20191111193933-5165175b40af/go.mod h1:Vrkh1pnjV9Bl8c3P9zH0/D4NlOHWP5d4/hF4YTULaec= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/startreedata/pinot-client-go v0.2.0 h1:Pv4W3HgxxGbB9GogRwNqfNyqPrOpScZuhQRc9kLM90A= github.com/startreedata/pinot-client-go v0.2.0/go.mod h1:vTz6Bu4dWIQIsfUoqFtgMV2QqBjeuSaDA8vxkOoYnLg= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 h1:7z3LSn867ex6VSaahyKadf4WtSsJIgne6A1WLOAGM8A= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4= github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/uber-common/bark v1.2.1 h1:cREJ9b7CpTjwZr0/5wV82fXlitoCIEHHnt9WkQ4lIk0= github.com/uber-common/bark v1.2.1/go.mod h1:g0ZuPcD7XiExKHynr93Q742G/sbrdVQkghrqLGOoFuY= github.com/uber-go/mapdecode v1.0.0 h1:euUEFM9KnuCa1OBixz1xM+FIXmpixyay5DLymceOVrU= github.com/uber-go/mapdecode v1.0.0/go.mod h1:b5nP15FwXTgpjTjeA9A2uTHXV5UJCl4arwKpP0FP1Hw= github.com/uber-go/tally v3.3.12+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU= github.com/uber-go/tally v3.3.15+incompatible h1:9hLSgNBP28CjIaDmAuRTq9qV+UZY+9PcvAkXO4nNMwg= github.com/uber-go/tally v3.3.15+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU= github.com/uber/cadence-idl v0.0.0-20211111101836-d6b70b60eb8c/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/uber/cadence-idl v0.0.0-20260313102523-57782092adb4 h1:1kAlcf7STXalFhDzv6vUz3XZtGOXQfwDF7IBKHu8GwI= github.com/uber/cadence-idl v0.0.0-20260313102523-57782092adb4/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM= github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/uber/ringpop-go v0.8.5 h1:aBa/SHmmFRcAXA63k7uBheoTL8tCmH7L+OgktB1AF/o= github.com/uber/ringpop-go v0.8.5/go.mod h1:zVI6eGO6L7pG14GkntHsSOfmUAWQ7B4lvmzly4IT4ls= github.com/uber/tchannel-go v1.16.0/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo= github.com/uber/tchannel-go v1.22.2 h1:NKA5FVESYh6Ij6V+tujK+IFZnBKDyUHdsBY264UYhgk= github.com/uber/tchannel-go v1.22.2/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo= github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= github.com/wI2L/jsondiff v0.6.0 h1:zrsH3FbfVa3JO9llxrcDy/XLkYPLgoMX6Mz3T2PP2AI= github.com/wI2L/jsondiff v0.6.0/go.mod h1:D6aQ5gKgPF9g17j+E9N7aasmU1O+XvfmWm1y8UMmNpw= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryBNl9eKOeqQ58Y/Qpo3Q9QNxKHX5uzzQ= github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8= go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= go.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI= go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/cadence v0.19.0 h1:EvDXwIJ0lAAxL2i8ne/vG/TeoJM6xkAyqgTRFmIWG+c= go.uber.org/cadence v0.19.0/go.mod h1:s91dOf0kcJbumPscRIVFV/4Xq/exhefzpXmnDiRRTxs= go.uber.org/config v1.4.0 h1:upnMPpMm6WlbZtXoasNkK4f0FhxwS+W4Iqz5oNznehQ= go.uber.org/config v1.4.0/go.mod h1:aCyrMHmUAc/s2h9sv1koP84M9ZF/4K+g2oleyESO/Ig= go.uber.org/dig v1.8.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= go.uber.org/dig v1.10.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= go.uber.org/dig v1.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw= go.uber.org/dig v1.18.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.10.0/go.mod h1:vLRicqpG/qQEzno4SYU86iCwfT95EZza+Eba0ItuxqY= go.uber.org/fx v1.13.1/go.mod h1:bREWhavnedxpJeTq9pQT53BbvwhUv7TcpsOqcH4a+3w= go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg= go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU= go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/net/metrics v1.3.0 h1:iRLPuVecNYf/wIV+mQaA4IgN8ghifu3q1B4IT6HfwyY= go.uber.org/net/metrics v1.3.0/go.mod h1:pEQrSDGNWT5IVpekWzee5//uHjI4gmgZFkobfw3bv8I= go.uber.org/thriftrw v1.25.0/go.mod h1:IcIfSeZgc59AlYb0xr0DlDKIdD7SgjnFpG9BXCPyy9g= go.uber.org/thriftrw v1.29.2 h1:pRuFLzbGvTcnYwGSjizWRHlbJUzGhu84sRiL1h1kUd8= go.uber.org/thriftrw v1.29.2/go.mod h1:YcjXveberDd28/Bs34SwHy3yu85x/jB4UA2gIcz/Eo0= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/yarpc v1.55.0/go.mod h1:V2JUPDWHYGNpvyuroYjf0KFjwvBCtcFJLuvZqv7TWA0= go.uber.org/yarpc v1.70.3 h1:yykHwzRD9/bgDtlOWoVuXbSZoU91Id2dWJO1CDSRHnI= go.uber.org/yarpc v1.70.3/go.mod h1:EH6I6K1HxBbOxZIJfhdDf+H+cvXPHmJyRvpfPqES20U= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.0.0-20170927054726-6dc17368e09b/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191104232314-dc038396d1f0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191226212025-6b505debf4bc/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117215004-fe56e6335763/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.7.0 h1:Hdks0L0hgznZLG9nzXb8vZ0rRvqNvAcgAp84y7Mwkgw= gonum.org/v1/gonum v0.7.0/go.mod h1:L02bwd0sqlsvRv41G7wGWFCsVNZFv/k1xzGIxeANHGM= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.128.0 h1:RjPESny5CnQRn9V6siglged+DZCgfu9l6mO9dkX9VOg= google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a h1:myvhA4is3vrit1a6NZCWBIwN0kNEnX21DJOJX/NvIfI= google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19 h1:WB265cn5OpO+hK3pikC9hpP1zI/KTwmyMFKloW9eOVc= gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34= honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= ================================================ FILE: cmd/server/main.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "os" "github.com/uber/cadence/cmd/server/cadence" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/tools/common/commoncli" _ "github.com/uber/cadence/common/archiver/gcloud" // needed to load the optional gcloud archiver plugin _ "github.com/uber/cadence/common/asyncworkflow/queue/kafka" // needed to load kafka asyncworkflow queue _ "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra" // needed to load cassandra plugin _ "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql/public" // needed to load the default gocql client _ "github.com/uber/cadence/common/persistence/sql/sqlplugin/mysql" // needed to load mysql plugin _ "github.com/uber/cadence/common/persistence/sql/sqlplugin/postgres" // needed to load postgres plugin _ "github.com/uber/cadence/common/persistence/sql/sqlplugin/sqlite" // needed to load sqlite plugin _ "github.com/uber/cadence/service/sharddistributor/store/etcd" // needed for shard distributor shard/heartbeat and leader election ) // main entry point for the cadence server func main() { app := cadence.BuildCLI(metrics.ReleaseVersion, metrics.Revision) commoncli.ExitHandler(app.Run(os.Args)) } ================================================ FILE: cmd/sharddistributor-canary/main.go ================================================ package main import ( "fmt" "net" "os" "time" "github.com/uber-go/tally" "github.com/urfave/cli/v2" "go.uber.org/fx" "go.uber.org/yarpc" "go.uber.org/yarpc/api/peer" "go.uber.org/yarpc/transport/grpc" "go.uber.org/zap" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/service/sharddistributor/canary" canaryConfig "github.com/uber/cadence/service/sharddistributor/canary/config" "github.com/uber/cadence/service/sharddistributor/canary/executors" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" "github.com/uber/cadence/service/sharddistributor/client/executorclient" "github.com/uber/cadence/service/sharddistributor/client/spectatorclient" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/tools/common/commoncli" ) const ( // Default configuration defaultShardDistributorEndpoint = "127.0.0.1:7943" defaultFixedNamespace = "shard-distributor-canary" defaultEphemeralNamespace = "shard-distributor-canary-ephemeral" defaultCanaryGRPCPort = 7953 // Port for canary to receive ping requests defaultNumExecutors = 1 shardDistributorServiceName = "cadence-shard-distributor" ) func runApp(c *cli.Context) { endpoint := c.String("endpoint") fixedNamespace := c.String("fixed-namespace") ephemeralNamespace := c.String("ephemeral-namespace") canaryGRPCPort := c.Int("canary-grpc-port") numFixedExecutors := c.Int("num-fixed-executors") numEphemeralExecutors := c.Int("num-ephemeral-executors") if c.IsSet("num-executors") { numExecutors := c.Int("num-executors") numFixedExecutors = numExecutors numEphemeralExecutors = numExecutors } fx.New(opts(fixedNamespace, ephemeralNamespace, endpoint, canaryGRPCPort, numFixedExecutors, numEphemeralExecutors)).Run() } func opts(fixedNamespace, ephemeralNamespace, endpoint string, canaryGRPCPort int, numFixedExecutors, numEphemeral int) fx.Option { configuration := clientcommon.Config{ Namespaces: []clientcommon.NamespaceConfig{ {Namespace: fixedNamespace, HeartBeatInterval: 1 * time.Second, MigrationMode: config.MigrationModeONBOARDED}, {Namespace: ephemeralNamespace, HeartBeatInterval: 1 * time.Second, MigrationMode: config.MigrationModeONBOARDED}, {Namespace: executors.LocalPassthroughNamespace, HeartBeatInterval: 1 * time.Second, MigrationMode: config.MigrationModeLOCALPASSTHROUGH}, {Namespace: executors.LocalPassthroughShadowNamespace, HeartBeatInterval: 1 * time.Second, MigrationMode: config.MigrationModeLOCALPASSTHROUGHSHADOW}, {Namespace: executors.DistributedPassthroughNamespace, HeartBeatInterval: 1 * time.Second, MigrationMode: config.MigrationModeDISTRIBUTEDPASSTHROUGH}, {Namespace: executors.ExternalAssignmentNamespace, HeartBeatInterval: 1 * time.Second, MigrationMode: config.MigrationModeDISTRIBUTEDPASSTHROUGH}, }, } canaryGRPCAddress := fmt.Sprintf("127.0.0.1:%d", canaryGRPCPort) // Create listener for GRPC inbound listener, err := net.Listen("tcp", canaryGRPCAddress) if err != nil { panic(err) } transport := grpc.NewTransport() executorMetadata := executorclient.ExecutorMetadata{ clientcommon.GrpcAddressMetadataKey: canaryGRPCAddress, } return fx.Options( fx.Supply( fx.Annotate(tally.NoopScope, fx.As(new(tally.Scope))), fx.Annotate(clock.NewRealTimeSource(), fx.As(new(clock.TimeSource))), configuration, transport, executorMetadata, ), fx.Provide(func(peerChooser spectatorclient.SpectatorPeerChooserInterface) yarpc.Config { return yarpc.Config{ Name: "shard-distributor-canary", Inbounds: yarpc.Inbounds{ transport.NewInbound(listener), // Listen for incoming ping requests }, Outbounds: yarpc.Outbounds{ shardDistributorServiceName: { Unary: transport.NewSingleOutbound(endpoint), Stream: transport.NewSingleOutbound(endpoint), }, // canary-to-canary outbound uses peer chooser to route to other canary instances "shard-distributor-canary": { Unary: transport.NewOutbound(peerChooser), Stream: transport.NewOutbound(peerChooser), }, }, } }), fx.Provide( func(t *grpc.Transport) peer.Transport { return t }, ), fx.Provide( yarpc.NewDispatcher, func(d *yarpc.Dispatcher) yarpc.ClientConfig { return d }, // Reprovide the dispatcher as a client config ), fx.Provide(zap.NewDevelopment), fx.Provide(log.NewLogger), // We do decorate instead of Invoke because we want to start and stop the dispatcher at the // correct time. // It will start before all dependencies are started and stop after all dependencies are stopped. // The Decorate gives fx enough information, so it can start and stop the dispatcher at the correct time. // // It is critical to start and stop the dispatcher at the correct time. // Since the executors need to // be able to send a final "drain" request to the shard distributor before the application is stopped. fx.Decorate(func( lc fx.Lifecycle, dispatcher *yarpc.Dispatcher, server sharddistributorv1.ShardDistributorExecutorCanaryAPIYARPCServer, ) *yarpc.Dispatcher { // Register canary procedures and ensure dispatcher lifecycle is managed by fx. dispatcher.Register(sharddistributorv1.BuildShardDistributorExecutorCanaryAPIYARPCProcedures(server)) lc.Append(fx.StartStopHook(dispatcher.Start, dispatcher.Stop)) return dispatcher }), // Include the canary module - it will set up spectator peer choosers and canary client canary.Module(canary.NamespacesNames{ FixedNamespace: fixedNamespace, EphemeralNamespace: ephemeralNamespace, ExternalAssignmentNamespace: executors.ExternalAssignmentNamespace, SharddistributorServiceName: shardDistributorServiceName, Config: canaryConfig.Config{ Canary: canaryConfig.CanaryConfig{ NumFixedExecutors: numFixedExecutors, NumEphemeralExecutors: numEphemeral, }, }, }), ) } func buildCLI() *cli.App { app := cli.NewApp() app.Name = "sharddistributor-canary" app.Usage = "Cadence shard distributor canary" app.Version = "0.0.1" app.Commands = []*cli.Command{ { Name: "start", Usage: "start shard distributor canary", Flags: []cli.Flag{ &cli.StringFlag{ Name: "endpoint", Aliases: []string{"e"}, Value: defaultShardDistributorEndpoint, Usage: "shard distributor endpoint address", }, &cli.StringFlag{ Name: "fixed-namespace", Value: defaultFixedNamespace, Usage: "namespace for fixed shard processing", }, &cli.StringFlag{ Name: "ephemeral-namespace", Value: defaultEphemeralNamespace, Usage: "namespace for ephemeral shard creation testing", }, &cli.IntFlag{ Name: "canary-grpc-port", Value: defaultCanaryGRPCPort, Usage: "port for canary to receive ping requests", }, &cli.IntFlag{ Name: "num-executors", Value: defaultNumExecutors, Usage: "number of executors for fixed and ephemeral to start. Overrides num-fixed-executors and num-ephemeral-executors flags", }, &cli.IntFlag{ Name: "num-fixed-executors", Value: defaultNumExecutors, Usage: "number of executors of fixed namespace to start. Don't use with num-executors", }, &cli.IntFlag{ Name: "num-ephemeral-executors", Value: defaultNumExecutors, Usage: "number of executors of ephemeral namespace to start. Don't use with num-executors", }, }, Action: func(c *cli.Context) error { runApp(c) return nil }, }, } return app } func main() { app := buildCLI() commoncli.ExitHandler(app.Run(os.Args)) } ================================================ FILE: cmd/sharddistributor-canary/main_test.go ================================================ package main import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/fx" ) func TestDependenciesAreSatisfied(t *testing.T) { assert.NoError(t, fx.ValidateApp(opts( defaultFixedNamespace, defaultEphemeralNamespace, defaultShardDistributorEndpoint, defaultCanaryGRPCPort, defaultNumExecutors, defaultNumExecutors, ))) } ================================================ FILE: cmd/tools/cassandra/main.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "os" "github.com/uber/cadence/tools/cassandra" "github.com/uber/cadence/tools/common/commoncli" _ "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql/public" // needed to load the default gocql client ) func main() { app := cassandra.BuildCLIOptions() commoncli.ExitHandler(app.Run(os.Args)) } ================================================ FILE: cmd/tools/cli/main.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "os" "go.uber.org/zap" "github.com/uber/cadence/tools/cli" "github.com/uber/cadence/tools/common/commoncli" _ "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra" // needed to load cassandra plugin _ "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql/public" // needed to load the default gocql client _ "github.com/uber/cadence/common/persistence/sql/sqlplugin/mysql" // needed to load mysql plugin _ "github.com/uber/cadence/common/persistence/sql/sqlplugin/postgres" // needed to load postgres plugin ) // Start using this CLI tool with command // See cadence/tools/cli/README.md for usage func main() { app := cli.NewCliApp(cli.NewClientFactory(must(zap.NewDevelopment()))) commoncli.ExitHandler(app.Run(os.Args)) } func must[T any](v T, err error) T { if err != nil { panic(err) } return v } ================================================ FILE: cmd/tools/copyright/licensegen.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "bufio" "errors" "flag" "fmt" "io/ioutil" "os" "path/filepath" "strings" ) type ( // task that adds license header to source // files, if they don't already exist addLicenseHeaderTask struct { license string // license header string to add config *config // root directory of the project source } // command line config params config struct { rootDir string verifyOnly bool temporalAddMode bool temporalModifyMode bool filePaths string } ) // licenseFileName is the name of the license file const licenseFileName = "LICENSE" // unique prefix that identifies a license header const licenseHeaderPrefixOld = "Copyright (c)" const licenseHeaderPrefix = "// The MIT License (MIT)" const cadenceCopyright = "// Copyright (c) 2017-2020 Uber Technologies Inc." const cadenceModificationHeader = "// Modifications Copyright (c) 2020 Uber Technologies Inc." const temporalCopyright = "// Copyright (c) 2020 Temporal Technologies, Inc." const temporalPartialCopyright = "// Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc." const firstLinesToCheck = 10 var ( // directories to be excluded dirDenylist = []string{"vendor/"} // default perms for the newly created files defaultFilePerms = os.FileMode(0644) ) // command line utility that adds license header // to the source files. Usage as follows: // // ./cmd/tools/copyright/licensegen.go func main() { var cfg config flag.StringVar(&cfg.rootDir, "rootDir", ".", "project root directory") flag.BoolVar(&cfg.verifyOnly, "verifyOnly", false, "don't automatically add headers, just verify all files") flag.BoolVar(&cfg.temporalAddMode, "temporalAddMode", false, "add copyright for new file copied from temporal") flag.BoolVar(&cfg.temporalModifyMode, "temporalModifyMode", false, "add copyright for existing file which has parts copied from temporal") flag.StringVar(&cfg.filePaths, "filePaths", "", "comma separated list of files to run temporal license on") flag.Parse() if err := verifyCfg(cfg); err != nil { fmt.Println(err) os.Exit(-1) } task := newAddLicenseHeaderTask(&cfg) if err := task.run(); err != nil { fmt.Println(err) os.Exit(-1) } } func verifyCfg(cfg config) error { if cfg.verifyOnly { if cfg.temporalModifyMode || cfg.temporalAddMode { return errors.New("invalid config, can only specify one of temporalAddMode, temporalModifyMode or verifyOnly") } } if cfg.temporalAddMode && cfg.temporalModifyMode { return errors.New("invalid config, can only specify temporalAddMode or temporalModifyMode") } if (cfg.temporalModifyMode || cfg.temporalAddMode) && len(cfg.filePaths) == 0 { return errors.New("invalid config, when running in temporalAddMode or temporalModifyMode must provide filePaths") } return nil } func newAddLicenseHeaderTask(cfg *config) *addLicenseHeaderTask { return &addLicenseHeaderTask{ config: cfg, } } func (task *addLicenseHeaderTask) run() error { data, err := ioutil.ReadFile(task.config.rootDir + "/" + licenseFileName) if err != nil { return fmt.Errorf("error reading license file, errr=%v", err.Error()) } task.license, err = commentOutLines(string(data)) if err != nil { return fmt.Errorf("copyright header failed to comment out lines, err=%v", err.Error()) } if task.config.temporalAddMode { task.license = fmt.Sprintf("%v\n\n%v\n\n%v", cadenceModificationHeader, temporalCopyright, task.license) } else if task.config.temporalModifyMode { task.license = fmt.Sprintf("%v\n\n%v\n\n%v", cadenceCopyright, temporalPartialCopyright, task.license) } if task.config.temporalModifyMode || task.config.temporalAddMode { filePaths, fileInfos, err := getFilePaths(task.config.filePaths) if err != nil { return err } for i := 0; i < len(filePaths); i++ { if err := task.handleFile(filePaths[i], fileInfos[i], nil); err != nil { return err } } return nil } task.license = fmt.Sprintf("%v\n\n%v\n\n%v", licenseHeaderPrefix, cadenceCopyright, task.license) err = filepath.Walk(task.config.rootDir, task.handleFile) if err != nil { return fmt.Errorf("copyright header check failed, err=%v", err.Error()) } return nil } func (task *addLicenseHeaderTask) handleFile(path string, fileInfo os.FileInfo, err error) error { if err != nil { return err } if fileInfo.IsDir() { if strings.HasPrefix(fileInfo.Name(), "_vendor-") || fileInfo.Name() == ".build" || fileInfo.Name() == ".bin" { return filepath.SkipDir } return nil } if !mustProcessPath(path) { return nil } if !strings.HasSuffix(fileInfo.Name(), ".go") && !strings.HasSuffix(fileInfo.Name(), ".proto") { return nil } // Used as part of the cli to write licence headers on files, does not use user supplied input so marked as nosec // #nosec f, err := os.Open(path) if err != nil { return err } ok, err := hasCopyright(f) if err != nil { return err } if err := f.Close(); err != nil { return err } if ok { if task.config.temporalModifyMode || task.config.temporalAddMode { return fmt.Errorf("when running in temporalModifyMode or temporalAddMode please first remove existing license header: %v", path) } return nil } // at this point, src file is missing the header if task.config.verifyOnly { if !isFileAutogenerated(path) { return fmt.Errorf("%v missing license header", path) } } // Used as part of the cli to write licence headers on files, does not use user supplied input so marked as nosec // #nosec data, err := ioutil.ReadFile(path) if err != nil { return err } return ioutil.WriteFile(path, []byte(task.license+string(data)), defaultFilePerms) } func hasCopyright(f *os.File) (bool, error) { scanner := bufio.NewScanner(f) lineSuccess := scanner.Scan() if !lineSuccess { return false, fmt.Errorf("fail to read first line of file %v", f.Name()) } i := 0 for i < firstLinesToCheck && lineSuccess { i++ line := strings.TrimSpace(scanner.Text()) if err := scanner.Err(); err != nil { return false, err } if lineHasCopyright(line) { return true, nil } lineSuccess = scanner.Scan() } return false, nil } func isFileAutogenerated(path string) bool { return strings.HasPrefix(path, ".gen") } func mustProcessPath(path string) bool { for _, d := range dirDenylist { if strings.HasPrefix(path, d) { return false } } return true } func commentOutLines(str string) (string, error) { var lines []string scanner := bufio.NewScanner(strings.NewReader(str)) for scanner.Scan() { text := scanner.Text() if text == "" { // do not add trailing whitespace, gofmt / goimports removes it lines = append(lines, "//\n") } else { lines = append(lines, "// "+scanner.Text()+"\n") } } lines = append(lines, "\n") if err := scanner.Err(); err != nil { return "", err } return strings.Join(lines, ""), nil } func lineHasCopyright(line string) bool { return strings.Contains(line, licenseHeaderPrefixOld) || strings.Contains(line, licenseHeaderPrefix) } func getFilePaths(filePaths string) ([]string, []os.FileInfo, error) { paths := strings.Split(filePaths, ",") var fileInfos []os.FileInfo for _, p := range paths { fileInfo, err := os.Stat(p) if err != nil { return nil, nil, err } fileInfos = append(fileInfos, fileInfo) } return paths, fileInfos, nil } ================================================ FILE: cmd/tools/releaser/internal/console/console.go ================================================ package console import ( "bufio" "context" "fmt" "io" "strings" ) // Manager handles console interactions type Manager struct { reader io.Reader writer io.Writer } // NewManager creates a new console manager func NewManager(reader io.Reader, writer io.Writer) *Manager { return &Manager{ reader: reader, writer: writer, } } // Confirm asks for user confirmation and returns true for 'y', false for 'n' func (m *Manager) Confirm(ctx context.Context, message string) (bool, error) { return m.ConfirmWithDefault(ctx, message, false) } // ConfirmWithDefault asks for user confirmation with a default value // Returns defaultValue if user just presses enter func (m *Manager) ConfirmWithDefault(ctx context.Context, message string, defaultValue bool) (bool, error) { prompt := fmt.Sprintf("%s [y/N]: ", message) if defaultValue { prompt = fmt.Sprintf("%s [Y/n]: ", message) } _, _ = fmt.Fprint(m.writer, prompt) inputChan := make(chan inputResult, 1) // Start goroutine to read input go func() { scanner := bufio.NewScanner(m.reader) if !scanner.Scan() { if err := scanner.Err(); err != nil { inputChan <- inputResult{err: fmt.Errorf("failed to read input: %w", err)} return } // EOF or no input - use default inputChan <- inputResult{text: "", err: nil} return } inputChan <- inputResult{text: scanner.Text(), err: nil} }() var result inputResult // Wait for either input or context cancellation select { case <-ctx.Done(): // Context was cancelled return false, ctx.Err() case result = <-inputChan: } // Got input from user if result.err != nil { return false, result.err } input := strings.TrimSpace(strings.ToLower(result.text)) // Empty input uses default if input == "" { return defaultValue, nil } switch input { case "y", "yes": return true, nil case "n", "no": return false, nil default: return false, fmt.Errorf("invalid input: %s", input) } } type inputResult struct { text string err error } ================================================ FILE: cmd/tools/releaser/internal/console/console_test.go ================================================ package console import ( "bytes" "context" "errors" "io" "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestConfirm(t *testing.T) { tests := []struct { name string input string expected bool hasError bool }{ {"yes response", "y\n", true, false}, {"Yes response", "Y\n", true, false}, {"yes full", "yes\n", true, false}, {"no response", "n\n", false, false}, {"No response", "N\n", false, false}, {"no full", "no\n", false, false}, {"empty input uses default false", "\n", false, false}, {"invalid input", "maybe\n", false, true}, {"whitespace input", " \n", false, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { reader := strings.NewReader(tt.input) writer := &bytes.Buffer{} manager := NewManager(reader, writer) ctx := context.Background() result, err := manager.Confirm(ctx, "Test message") if tt.hasError { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.expected, result) } // Check that prompt was written output := writer.String() assert.Contains(t, output, "Test message [y/N]:") }) } } func TestConfirmWithDefault(t *testing.T) { tests := []struct { name string input string defaultValue bool expected bool hasError bool }{ {"yes with default false", "y\n", false, true, false}, {"no with default true", "n\n", true, false, false}, {"empty uses default true", "\n", true, true, false}, {"empty uses default false", "\n", false, false, false}, {"invalid input", "invalid\n", true, false, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { reader := strings.NewReader(tt.input) writer := &bytes.Buffer{} manager := NewManager(reader, writer) ctx := context.Background() result, err := manager.ConfirmWithDefault(ctx, "Test message", tt.defaultValue) if tt.hasError { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.expected, result) } // Check prompt format based on default output := writer.String() if tt.defaultValue { assert.Contains(t, output, "[Y/n]:") } else { assert.Contains(t, output, "[y/N]:") } }) } } // blockingReader blocks on Read until unblocked type blockingReader struct { unblock chan struct{} data []byte read bool } func newBlockingReader() *blockingReader { return &blockingReader{ unblock: make(chan struct{}), data: []byte("y\n"), } } func (br *blockingReader) Read(p []byte) (n int, err error) { if br.read { return 0, io.EOF } // Block until unblocked <-br.unblock br.read = true n = copy(p, br.data) return n, nil } func (br *blockingReader) Unblock() { close(br.unblock) } func TestContextCancellation(t *testing.T) { t.Run("context cancelled before input", func(t *testing.T) { // Create a context that's already cancelled ctx, cancel := context.WithCancel(context.Background()) cancel() reader := strings.NewReader("y\n") // This won't be read due to cancellation writer := &bytes.Buffer{} manager := NewManager(reader, writer) result, err := manager.ConfirmWithDefault(ctx, "Test", false) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) assert.False(t, result) }) t.Run("context cancelled during input wait", func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) // Use a blocking reader that actually blocks reader := newBlockingReader() writer := &bytes.Buffer{} manager := NewManager(reader, writer) // Cancel context after a short delay go func() { time.Sleep(10 * time.Millisecond) cancel() }() result, err := manager.ConfirmWithDefault(ctx, "Test", false) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) assert.False(t, result) }) } // errorReader always returns an error when scanning type errorReader struct{} func (e errorReader) Read(p []byte) (n int, err error) { return 0, errors.New("read error") } func TestScannerError(t *testing.T) { reader := errorReader{} writer := &bytes.Buffer{} manager := NewManager(reader, writer) ctx := context.Background() result, err := manager.ConfirmWithDefault(ctx, "Test", false) require.Error(t, err) assert.Contains(t, err.Error(), "failed to read input") assert.False(t, result) } func TestEOFHandling(t *testing.T) { // Reader with no content (immediate EOF) reader := strings.NewReader("") writer := &bytes.Buffer{} manager := NewManager(reader, writer) ctx := context.Background() result, err := manager.ConfirmWithDefault(ctx, "Test", true) assert.NoError(t, err) assert.True(t, result, "Expected default value (true) on EOF") } ================================================ FILE: cmd/tools/releaser/internal/fs/fs.go ================================================ package fs import ( "context" "fmt" "os" "path/filepath" "golang.org/x/mod/modfile" ) // Client implements Interface type Client struct { verbose bool } func NewFileSystemClient(verbose bool) *Client { return &Client{verbose: verbose} } // FindGoModFiles reads go.work file and returns module directories func (f *Client) FindGoModFiles(ctx context.Context, root string) ([]string, error) { f.logDebug("Finding modules from go.work file") workFilePath := filepath.Join(root, "go.work") modules, err := f.parseGoWorkFile(workFilePath, root) if err != nil { return nil, fmt.Errorf("failed to parse go.work file: %w", err) } f.logDebug("Found modules from go.work: %v", modules) return modules, nil } // parseGoWorkFile parses the go.work file using the official modfile package func (f *Client) parseGoWorkFile(workFilePath, root string) ([]string, error) { workFileData, err := os.ReadFile(workFilePath) if err != nil { return nil, fmt.Errorf("failed to read go.work file: %w", err) } workFile, err := modfile.ParseWork(workFilePath, workFileData, nil) if err != nil { return nil, fmt.Errorf("failed to parse go.work file: %w", err) } modules := make([]string, 0, len(workFile.Use)) for _, use := range workFile.Use { modules = append(modules, use.Path) } return modules, nil } // resolveModulePath converts relative path to absolute path func (f *Client) resolveModulePath(modulePath, root string) string { if filepath.IsAbs(modulePath) { return modulePath } return filepath.Join(root, modulePath) } func (f *Client) logDebug(msg string, args ...interface{}) { if f.verbose { fmt.Printf("%s\n", fmt.Sprintf(msg, args...)) } } ================================================ FILE: cmd/tools/releaser/internal/git/git.go ================================================ package git import ( "bufio" "context" "fmt" "os/exec" "strings" ) // Client implements Interface type Client struct { verbose bool } func NewGitClient(verbose bool) *Client { return &Client{verbose: verbose} } func (g *Client) GetCurrentBranch(ctx context.Context) (string, error) { g.logDebug("Getting current git branch") cmd := exec.CommandContext(ctx, "git", "rev-parse", "--abbrev-ref", "HEAD") output, err := cmd.Output() if err != nil { return "", fmt.Errorf("get current branch: %w", err) } branch := strings.TrimSpace(string(output)) g.logDebug("Current branch: %s", branch) return branch, nil } func (g *Client) GetTags(ctx context.Context) ([]string, error) { g.logDebug("Fetching git tags") cmd := exec.CommandContext(ctx, "git", "tag", "-l") output, err := cmd.Output() if err != nil { return nil, fmt.Errorf("get git tags: %w", err) } var tags []string scanner := bufio.NewScanner(strings.NewReader(string(output))) for scanner.Scan() { if tag := strings.TrimSpace(scanner.Text()); tag != "" { tags = append(tags, tag) } } g.logDebug("Found git tags %v", tags) return tags, scanner.Err() } func (g *Client) CreateTag(ctx context.Context, tag string) error { g.logDebug("Creating git tag %s", tag) cmd := exec.CommandContext(ctx, "git", "tag", tag) err := cmd.Run() if err != nil { return fmt.Errorf("create tag %s: %w", tag, err) } return err } func (g *Client) PushTag(ctx context.Context, tag string) error { g.logDebug("Pushing git tag %s", tag) cmd := exec.CommandContext(ctx, "git", "push", "origin", tag) err := cmd.Run() if err != nil { return fmt.Errorf("push tag %s: %w", tag, err) } return err } func (g *Client) GetRepoRoot(ctx context.Context) (string, error) { g.logDebug("Getting repository root") cmd := exec.CommandContext(ctx, "git", "rev-parse", "--show-toplevel") output, err := cmd.Output() if err != nil { return "", fmt.Errorf("get repository root: %w", err) } root := strings.TrimSpace(string(output)) g.logDebug("Repository root %s", root) return root, nil } func (g *Client) logDebug(msg string, args ...interface{}) { if g.verbose { fmt.Printf("%s\n", fmt.Sprintf(msg, args...)) } } ================================================ FILE: cmd/tools/releaser/internal/release/release.go ================================================ package release import ( "context" "fmt" "regexp" "sort" "strconv" "strings" "github.com/Masterminds/semver/v3" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination release_mocks_test.go -self_package github.com/uber/cadence/cmd/tools/releaser/release // Git defines git operations for testing type Git interface { GetCurrentBranch(ctx context.Context) (string, error) GetTags(ctx context.Context) ([]string, error) CreateTag(ctx context.Context, tag string) error PushTag(ctx context.Context, tag string) error GetRepoRoot(ctx context.Context) (string, error) } // FS defines filesystem operations for testing type FS interface { FindGoModFiles(ctx context.Context, root string) ([]string, error) } // UserInteraction defines the interface for user interactions type UserInteraction interface { Confirm(ctx context.Context, message string) (bool, error) ConfirmWithDefault(ctx context.Context, message string, defaultValue bool) (bool, error) } var ( versionRegex = regexp.MustCompile(`v[0-9]+\.[0-9]+\.[0-9]+(?:-prerelease[0-9]+)?`) prereleaseRegex = regexp.MustCompile(`v([0-9]+\.[0-9]+\.[0-9]+)-prerelease(\d+)`) ) // Config holds the release configuration type Config struct { RepoRoot string ExcludedDirs []string RequiredBranch string Verbose bool Command string // The subcommand being executed SkipConfirmations bool ManualVersion string // Manual version override } // Manager handles the release process type Manager struct { config Config git Git fs FS interaction UserInteraction tagCache *TagCache } func NewReleaseManager(config Config, git Git, fs FS, interaction UserInteraction) *Manager { return &Manager{ config: config, git: git, fs: fs, interaction: interaction, } } func (rm *Manager) RunRelease(ctx context.Context) error { return rm.executeCommand(ctx, rm.calculateReleaseVersion, rm.validateReleaseCommand) } func (rm *Manager) RunPatch(ctx context.Context) error { return rm.executeCommand(ctx, rm.calculatePatchVersion, rm.validateMinorMajorCommand) } func (rm *Manager) RunMinor(ctx context.Context) error { return rm.executeCommand(ctx, rm.calculateMinorVersion, rm.validateMinorMajorCommand) } func (rm *Manager) RunMajor(ctx context.Context) error { return rm.executeCommand(ctx, rm.calculateMajorVersion, rm.validateMinorMajorCommand) } func (rm *Manager) RunPrerelease(ctx context.Context) error { return rm.executeCommand(ctx, rm.calculatePrereleaseVersion, nil) } // Generic command execution flow func (rm *Manager) executeCommand( ctx context.Context, calculateVersion func(string) (string, error), validate func(string) error, ) error { // Initialize tag cache if err := rm.GetKnownReleases(ctx); err != nil { return err } // Get current version currentVersion := rm.GetCurrentGlobalVersion() rm.logDebug("Current version: %s", currentVersion) var targetVersion string var err error // Check for manual version override if rm.config.ManualVersion != "" { rm.logDebug("Using manual version override: %s", rm.config.ManualVersion) targetVersion, err = rm.processManualVersion(rm.config.ManualVersion) if err != nil { return err } } else { // Run validation if provided (only for automatic version calculation) if validate != nil { if err := validate(currentVersion); err != nil { return err } } // Calculate target version automatically targetVersion, err = calculateVersion(currentVersion) if err != nil { return err } } fmt.Printf("Version transition: %s → %s\n", currentVersion, targetVersion) return rm.executeRelease(ctx, targetVersion) } // Version calculation methods for each subcommand func (rm *Manager) calculateReleaseVersion(currentVersionStr string) (string, error) { rm.logDebug("Calculating release version from: %s", currentVersionStr) currentVersion, err := semver.NewVersion(currentVersionStr) if err != nil { return "", fmt.Errorf("failed to parse current version %s: %w", currentVersionStr, err) } // Remove prerelease suffix releaseVersion := fmt.Sprintf("v%d.%d.%d", currentVersion.Major(), currentVersion.Minor(), currentVersion.Patch()) return releaseVersion, nil } // Generic version calculation that creates a closure for different version types func (rm *Manager) calculateVersionIncrement(versionType string) func(string) (string, error) { return func(currentVersionStr string) (string, error) { rm.logDebug("Calculating %s version from: %s", versionType, currentVersionStr) // First, get the base version (remove prerelease if present) baseVersion := rm.getBaseVersion(currentVersionStr) // Increment version newVersion, err := IncrementVersion(baseVersion, versionType) if err != nil { return "", err } // Create first prerelease return rm.GetNextPrereleaseVersion(newVersion) } } // Update the existing methods to use the generic function func (rm *Manager) calculateMinorVersion(currentVersionStr string) (string, error) { return rm.calculateVersionIncrement("minor")(currentVersionStr) } func (rm *Manager) calculateMajorVersion(currentVersionStr string) (string, error) { return rm.calculateVersionIncrement("major")(currentVersionStr) } func (rm *Manager) calculatePatchVersion(currentVersionStr string) (string, error) { return rm.calculateVersionIncrement("patch")(currentVersionStr) } func (rm *Manager) calculatePrereleaseVersion(currentVersionStr string) (string, error) { rm.logDebug("Calculating prerelease version from: %s", currentVersionStr) currentVersion, err := semver.NewVersion(currentVersionStr) if err != nil { return "", fmt.Errorf("failed to parse current version %s: %w", currentVersionStr, err) } baseVersionStr := fmt.Sprintf("v%d.%d.%d", currentVersion.Major(), currentVersion.Minor(), currentVersion.Patch()) return rm.GetNextPrereleaseVersion(baseVersionStr) } // Validation methods func (rm *Manager) validateReleaseCommand(currentVersion string) error { if !strings.Contains(currentVersion, "-prerelease") { return fmt.Errorf("release command requires existing prerelease version, current: %s", currentVersion) } return nil } func (rm *Manager) validateMinorMajorCommand(currentVersion string) error { if strings.Contains(currentVersion, "-prerelease") { return fmt.Errorf("minor/major commands should be run from stable versions, current: %s (consider using 'release' first)", currentVersion) } return nil } // Helper method to get base version (remove prerelease suffix) func (rm *Manager) getBaseVersion(versionStr string) string { version, err := semver.NewVersion(versionStr) if err != nil { return versionStr } return fmt.Sprintf("v%d.%d.%d", version.Major(), version.Minor(), version.Patch()) } // processManualVersion validates and normalizes manual version input func (rm *Manager) processManualVersion(manualVersion string) (string, error) { // Normalize the version (ensure v prefix, validate semver) normalizedVersion, err := NormalizeVersion(manualVersion) if err != nil { return "", fmt.Errorf("invalid manual version format: %w", err) } rm.logDebug("Manual version normalized: %s → %s", manualVersion, normalizedVersion) return normalizedVersion, nil } // Execute the actual release func (rm *Manager) executeRelease(ctx context.Context, targetVersion string) error { // Assess state state, err := rm.AssessCurrentState(ctx) if err != nil { return err } // Plan actions actions, warnings := rm.planReleaseActions(state, targetVersion) // Handle warnings and get confirmation to continue if err = rm.handleWarningsAndConfirmations(ctx, warnings); err != nil { return err } // Check for version conflicts and handle them conflictInfo, conflictErr := rm.CheckVersionExists(targetVersion, state.Modules) finalActions := actions if conflictErr != nil { fmt.Printf("❌ Version conflict detected:\n") fmt.Printf(" Existing tags: %v\n", conflictInfo.ExistingTags) fmt.Printf(" Tags to create: %v\n", conflictInfo.MissingTags) if len(conflictInfo.MissingTags) == 0 { return fmt.Errorf("all tags already exist for version %s", targetVersion) } if !rm.config.SkipConfirmations { confirmed, err := rm.interaction.ConfirmWithDefault(ctx, fmt.Sprintf("Continue and create only missing tags (%d remaining)?", len(conflictInfo.MissingTags)), false) if err != nil || !confirmed { return fmt.Errorf("operation cancelled due to version conflict") } } // Filter actions to only include missing tags finalActions = rm.filterActionsForMissingTags(actions, conflictInfo.MissingTags) fmt.Printf("✓ Will skip existing tags and create only: %v\n", conflictInfo.MissingTags) } // Show planned actions (filtered if there were conflicts) rm.ShowPlannedActions(finalActions) // Confirm tag creation if !rm.config.SkipConfirmations { tagCount := len(finalActions) / 2 // Each tag has create + push action message := fmt.Sprintf("Create %d tags?", tagCount) if conflictErr != nil { message = fmt.Sprintf("Create %d missing tags (skipping %d existing)?", len(conflictInfo.MissingTags), len(conflictInfo.ExistingTags)) } confirmed, err := rm.interaction.Confirm(ctx, message) if err != nil || !confirmed { if ctx.Err() != nil { return nil } return fmt.Errorf("tag creation cancelled") } } // Create tags (only missing ones if there were conflicts) if err = rm.executeTagCreation(ctx, finalActions); err != nil { return err } // Confirm tag pushing if !rm.config.SkipConfirmations { pushCount := 0 for _, action := range finalActions { if action.Type == ActionPushTags { pushCount++ } } message := fmt.Sprintf("Push %d tags?", pushCount) confirmed, err := rm.interaction.Confirm(ctx, message) if err != nil || !confirmed { if ctx.Err() != nil { return nil } fmt.Printf("Tags created locally but not pushed\n") if conflictErr != nil { fmt.Printf("Created: %v\n", conflictInfo.MissingTags) fmt.Printf("Skipped: %v\n", conflictInfo.ExistingTags) } return nil } } // Push tags if err = rm.executeTagPushing(ctx, finalActions); err != nil { return fmt.Errorf("push tags: %w", err) } // Success message if conflictErr != nil { fmt.Printf("✓ Release %s completed successfully\n", targetVersion) fmt.Printf(" Created: %v\n", conflictInfo.MissingTags) fmt.Printf(" Skipped: %v (already existed)\n", conflictInfo.ExistingTags) } else { fmt.Printf("✓ Release %s completed successfully\n", targetVersion) } return nil } // GetKnownReleases fetches and parses all tags once func (rm *Manager) GetKnownReleases(ctx context.Context) error { fmt.Println("Getting known releases") // Fetch raw tags once rawTags, err := rm.git.GetTags(ctx) if err != nil { return fmt.Errorf("failed to fetch tags: %w", err) } rm.tagCache = &TagCache{ AllTags: make([]ParsedTag, 0, len(rawTags)), VersionTags: make([]ParsedTag, 0, len(rawTags)), ModuleTags: make(map[string][]ParsedTag), PrereleaseCache: make(map[string][]int), } for _, rawTag := range rawTags { parsedTag := rm.parseTag(rawTag) rm.tagCache.AllTags = append(rm.tagCache.AllTags, parsedTag) // Skip not version tags if parsedTag.Version == nil { continue } rm.tagCache.VersionTags = append(rm.tagCache.VersionTags, parsedTag) // Group by module if rm.tagCache.ModuleTags[parsedTag.ModulePath] == nil { rm.tagCache.ModuleTags[parsedTag.ModulePath] = make([]ParsedTag, 0) } rm.tagCache.ModuleTags[parsedTag.ModulePath] = append( rm.tagCache.ModuleTags[parsedTag.ModulePath], parsedTag) // Cache prerelease numbers if parsedTag.IsPrerelease { baseVersion := fmt.Sprintf("v%d.%d.%d", parsedTag.Version.Major(), parsedTag.Version.Minor(), parsedTag.Version.Patch()) if rm.tagCache.PrereleaseCache[baseVersion] == nil { rm.tagCache.PrereleaseCache[baseVersion] = make([]int, 0) } rm.tagCache.PrereleaseCache[baseVersion] = append( rm.tagCache.PrereleaseCache[baseVersion], parsedTag.PrereleaseNum) } } // Sort and cache highest version if len(rm.tagCache.VersionTags) > 0 { rm.sortVersionTags() rm.tagCache.HighestVersion = rm.tagCache.VersionTags[len(rm.tagCache.VersionTags)-1].Version } rm.logDebug("Known releases total tags (%d), version_tags(%d)", len(rm.tagCache.AllTags), len(rm.tagCache.VersionTags)) return nil } func (rm *Manager) parseTag(rawTag string) ParsedTag { parsed := ParsedTag{Raw: rawTag} // Extract module path and version part if idx := strings.LastIndex(rawTag, "/v"); idx != -1 { parsed.ModulePath = rawTag[:idx] versionPart := rawTag[idx+1:] if versionRegex.MatchString(versionPart) { if version, err := semver.NewVersion(versionPart); err == nil { parsed.Version = version // Check for prerelease if matches := prereleaseRegex.FindStringSubmatch(versionPart); len(matches) > 2 { parsed.IsPrerelease = true if num, err := strconv.Atoi(matches[2]); err == nil { parsed.PrereleaseNum = num } } } } return parsed } // Root module tag if versionRegex.MatchString(rawTag) { if version, err := semver.NewVersion(rawTag); err == nil { parsed.Version = version if matches := prereleaseRegex.FindStringSubmatch(rawTag); len(matches) > 2 { parsed.IsPrerelease = true if num, err := strconv.Atoi(matches[2]); err == nil { parsed.PrereleaseNum = num } } } } return parsed } func (rm *Manager) sortVersionTags() { sort.Slice(rm.tagCache.VersionTags, func(i, j int) bool { return rm.tagCache.VersionTags[i].Version.LessThan(rm.tagCache.VersionTags[j].Version) }) } func (rm *Manager) GetCurrentGlobalVersion() string { if rm.tagCache.HighestVersion == nil { return "v0.0.0" } return "v" + rm.tagCache.HighestVersion.String() } func (rm *Manager) GetNextPrereleaseVersion(baseVersionStr string) (string, error) { // Parse base version baseVersion, err := semver.NewVersion(baseVersionStr) if err != nil { return "", fmt.Errorf("failed to parse base version %s: %w", baseVersionStr, err) } cleanBaseStr := fmt.Sprintf("v%d.%d.%d", baseVersion.Major(), baseVersion.Minor(), baseVersion.Patch()) prereleaseNumbers := rm.tagCache.PrereleaseCache[cleanBaseStr] if len(prereleaseNumbers) == 0 { return fmt.Sprintf("%s-prerelease01", cleanBaseStr), nil } // Sort and get next number sort.Ints(prereleaseNumbers) nextNum := prereleaseNumbers[len(prereleaseNumbers)-1] + 1 if nextNum > 99 { return "", fmt.Errorf("maximum prerelease number (99) exceeded, base (%s)", cleanBaseStr) } return fmt.Sprintf("%s-prerelease%02d", cleanBaseStr, nextNum), nil } func (rm *Manager) CheckVersionExists(version string, modules []Module) (VersionConflictInfo, error) { conflictInfo := VersionConflictInfo{ ExistingTags: make([]string, 0), MissingTags: make([]string, 0), } for _, module := range modules { expectedTag := version if module.Path != "" { expectedTag = module.Path + "/" + version } exists := false for _, tag := range rm.tagCache.AllTags { if tag.Raw == expectedTag { conflictInfo.ExistingTags = append(conflictInfo.ExistingTags, expectedTag) exists = true break } } if !exists { conflictInfo.MissingTags = append(conflictInfo.MissingTags, expectedTag) } } if len(conflictInfo.ExistingTags) > 0 { return conflictInfo, fmt.Errorf("some tags already exist: %v", conflictInfo.ExistingTags) } return conflictInfo, nil } // AssessCurrentState gathers repository state (assumes cache is already populated) func (rm *Manager) AssessCurrentState(ctx context.Context) (*State, error) { state := &State{} var err error // Gather information (cache should already be populated) state.CurrentBranch, err = rm.git.GetCurrentBranch(ctx) if err != nil { return nil, fmt.Errorf("get current branch: %w", err) } state.Modules, err = rm.FindModules(ctx) if err != nil { return nil, fmt.Errorf("find modules: %w", err) } state.CurrentVersion = rm.GetCurrentGlobalVersion() state.TagCache = rm.tagCache return state, nil } // ShowCurrentState displays current release state func (rm *Manager) ShowCurrentState(ctx context.Context) error { // Initialize tag cache if err := rm.GetKnownReleases(ctx); err != nil { return err } state, err := rm.AssessCurrentState(ctx) if err != nil { return err } fmt.Printf("Repository Release Status\n") fmt.Printf("========================\n") fmt.Printf("Branch: %s\n", state.CurrentBranch) fmt.Printf("Global Version: %s\n", state.CurrentVersion) fmt.Printf("\n") fmt.Printf("Modules and Versions:\n") for _, module := range state.Modules { moduleName := module.Path if moduleName == "" { moduleName = "root" } fmt.Printf(" %-20s %s\n", moduleName, module.Version) } // Show what commands are available fmt.Printf("\nAvailable Commands:\n") if strings.Contains(state.CurrentVersion, "-prerelease") { fmt.Printf(" releaser prerelease # Increment prerelease number\n") fmt.Printf(" releaser release # Promote to final release\n") fmt.Printf(" releaser release -s v1.4.0 # Override with specific version\n") } else { fmt.Printf(" releaser minor # Start new minor version cycle\n") fmt.Printf(" releaser major # Start new major version cycle\n") fmt.Printf(" releaser patch # Start new patch version cycle\n") fmt.Printf(" releaser minor -s v1.4.0-prerelease01 # Override with specific version\n") } return nil } // ShowPlannedActions displays what actions will be performed func (rm *Manager) ShowPlannedActions(actions []Action) { if len(actions) == 0 { fmt.Println("No actions planned") return } fmt.Println("\nPlanned Release Actions:") // Group actions by type for better readability createActions := make([]Action, 0) pushActions := make([]Action, 0) for _, action := range actions { switch action.Type { case ActionCreateTag: createActions = append(createActions, action) case ActionPushTags: pushActions = append(pushActions, action) } } if len(createActions) > 0 { fmt.Println("\nCreate Tags:") for _, action := range createActions { fmt.Printf(" git tag %s\n", action.Target) } } if len(pushActions) > 0 { fmt.Println("\nPush Tags:") for _, action := range pushActions { fmt.Printf(" git push origin %s\n", action.Target) } } } func (rm *Manager) planReleaseActions(state *State, targetVersion string) ([]Action, []Warning) { var actions []Action var warnings []Warning // Add actions for each module for _, module := range state.Modules { tagName := rm.getTagName(module, targetVersion) actions = append(actions, Action{ Type: ActionCreateTag, Target: tagName, Description: fmt.Sprintf("Create tag %s", tagName), }) actions = append(actions, Action{ Type: ActionPushTags, Target: tagName, Description: fmt.Sprintf("Push tag %s", tagName), }) } // Add warnings warnings = append(warnings, rm.validateWithWarnings(state, targetVersion)...) return actions, warnings } func (rm *Manager) validateWithWarnings(state *State, targetVersion string) []Warning { var warnings []Warning // Branch check -> warning if rm.config.RequiredBranch != "" && state.CurrentBranch != rm.config.RequiredBranch { warnings = append(warnings, Warning{ Type: WrongBranch, Message: fmt.Sprintf("you are not on %s", rm.config.RequiredBranch), }) } return warnings } // filterActionsForMissingTags filters actions to only include missing tags func (rm *Manager) filterActionsForMissingTags(actions []Action, missingTags []string) []Action { var filteredActions []Action // Create a set of missing tags for quick lookup missingTagSet := make(map[string]bool) for _, tag := range missingTags { missingTagSet[tag] = true } // Filter actions to only include missing tags for _, action := range actions { if missingTagSet[action.Target] { filteredActions = append(filteredActions, action) } } return filteredActions } // getLatestVersionForModule returns the latest version for a given module path func (rm *Manager) getLatestVersionForModule(modulePath string) string { // Handle case where tag cache isn't initialized yet if rm.tagCache == nil || rm.tagCache.ModuleTags == nil { return "v0.0.0" // Default for modules with no releases yet } moduleTags, exists := rm.tagCache.ModuleTags[modulePath] if !exists || len(moduleTags) == 0 { return "v0.0.0" // No releases for this module yet } // Find the latest version among all tags for this module var latestVersion *semver.Version for _, tag := range moduleTags { if tag.Version != nil { if latestVersion == nil || tag.Version.GreaterThan(latestVersion) { latestVersion = tag.Version } } } if latestVersion == nil { return "v0.0.0" } return "v" + latestVersion.String() } func (rm *Manager) handleWarningsAndConfirmations(ctx context.Context, warnings []Warning) error { for _, warning := range warnings { fmt.Printf("⚠️ %s\n", warning.Message) if rm.config.SkipConfirmations { continue } confirmed, err := rm.interaction.ConfirmWithDefault(ctx, "Continue?", false) if err != nil { return err } if !confirmed { return fmt.Errorf("operation cancelled due to: %s", warning.Message) } } return nil } func (rm *Manager) executeTagCreation(ctx context.Context, actions []Action) error { fmt.Println("Creating tags...") for _, action := range actions { if action.Type == ActionCreateTag { if err := rm.git.CreateTag(ctx, action.Target); err != nil { return fmt.Errorf("failed to create tag %s: %w", action.Target, err) } fmt.Printf("✓ Created tag %s\n", action.Target) } } return nil } func (rm *Manager) executeTagPushing(ctx context.Context, actions []Action) error { fmt.Println("Pushing tags...") for _, action := range actions { if action.Type == ActionPushTags { if err := rm.git.PushTag(ctx, action.Target); err != nil { return fmt.Errorf("failed to push tag %s: %w", action.Target, err) } fmt.Printf("✓ Pushed tag %s\n", action.Target) } } return nil } // NormalizeVersion ensures version has 'v' prefix and is valid semver func NormalizeVersion(v string) (string, error) { if !strings.HasPrefix(v, "v") { v = "v" + v } // Parse with Masterminds/semver to validate semVer, err := semver.NewVersion(v) if err != nil { return "", fmt.Errorf("invalid semantic version: %s", v) } return "v" + semVer.String(), nil } // IncrementVersion increments a version based on type func IncrementVersion(currentVersionStr, versionType string) (string, error) { // Parse the current version currentVersion, err := semver.NewVersion(currentVersionStr) if err != nil { return "", fmt.Errorf("failed to parse current version %s: %w", currentVersionStr, err) } var newVersion semver.Version switch versionType { case "major": newVersion = currentVersion.IncMajor() case "minor": newVersion = currentVersion.IncMinor() case "patch": newVersion = currentVersion.IncPatch() default: return "", fmt.Errorf("invalid version type: %s", versionType) } return "v" + newVersion.String(), nil } // FindModules discovers all Go modules in the repository func (rm *Manager) FindModules(ctx context.Context) ([]Module, error) { rm.logDebug("Discovering Go modules in path %s", rm.config.RepoRoot) goModPaths, err := rm.fs.FindGoModFiles(ctx, rm.config.RepoRoot) if err != nil { return nil, fmt.Errorf("failed to find go.mod files: %w", err) } var modules []Module seen := make(map[string]bool) for _, path := range goModPaths { // Normalize relative path if path == "." { path = "" } path = strings.TrimPrefix(path, "./") // Check if should be excluded if rm.shouldExcludeModule(path) { rm.logDebug("Excluding module %s", path) continue } // Deduplicate if seen[path] { continue } seen[path] = true // Get latest version for this module from cache latestVersion := rm.getLatestVersionForModule(path) modules = append(modules, Module{ Path: path, Version: latestVersion, }) rm.logDebug("Found module\n%s\nversion: %s\n", path, latestVersion) } return modules, nil } // shouldExcludeModule checks if a module should be excluded func (rm *Manager) shouldExcludeModule(relPath string) bool { for _, excluded := range rm.config.ExcludedDirs { if relPath == excluded || strings.HasPrefix(relPath, excluded+"/") { return true } } return false } // getTagName generates the tag name for a module and version func (rm *Manager) getTagName(module Module, version string) string { if module.Path == "" { return version } return module.Path + "/" + version } func (rm *Manager) logDebug(msg string, args ...interface{}) { if rm.config.Verbose { fmt.Printf("DEBUG: %s\n", fmt.Sprintf(msg, args...)) } } ================================================ FILE: cmd/tools/releaser/internal/release/release_mocks_test.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: release.go // // Generated by this command: // // mockgen -package release -source release.go -destination release_mocks_test.go -self_package github.com/uber/cadence/cmd/tools/releaser/release // // Package release is a generated GoMock package. package release import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockGit is a mock of Git interface. type MockGit struct { ctrl *gomock.Controller recorder *MockGitMockRecorder isgomock struct{} } // MockGitMockRecorder is the mock recorder for MockGit. type MockGitMockRecorder struct { mock *MockGit } // NewMockGit creates a new mock instance. func NewMockGit(ctrl *gomock.Controller) *MockGit { mock := &MockGit{ctrl: ctrl} mock.recorder = &MockGitMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockGit) EXPECT() *MockGitMockRecorder { return m.recorder } // CreateTag mocks base method. func (m *MockGit) CreateTag(ctx context.Context, tag string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateTag", ctx, tag) ret0, _ := ret[0].(error) return ret0 } // CreateTag indicates an expected call of CreateTag. func (mr *MockGitMockRecorder) CreateTag(ctx, tag any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTag", reflect.TypeOf((*MockGit)(nil).CreateTag), ctx, tag) } // GetCurrentBranch mocks base method. func (m *MockGit) GetCurrentBranch(ctx context.Context) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCurrentBranch", ctx) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentBranch indicates an expected call of GetCurrentBranch. func (mr *MockGitMockRecorder) GetCurrentBranch(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentBranch", reflect.TypeOf((*MockGit)(nil).GetCurrentBranch), ctx) } // GetRepoRoot mocks base method. func (m *MockGit) GetRepoRoot(ctx context.Context) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRepoRoot", ctx) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRepoRoot indicates an expected call of GetRepoRoot. func (mr *MockGitMockRecorder) GetRepoRoot(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepoRoot", reflect.TypeOf((*MockGit)(nil).GetRepoRoot), ctx) } // GetTags mocks base method. func (m *MockGit) GetTags(ctx context.Context) ([]string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTags", ctx) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTags indicates an expected call of GetTags. func (mr *MockGitMockRecorder) GetTags(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTags", reflect.TypeOf((*MockGit)(nil).GetTags), ctx) } // PushTag mocks base method. func (m *MockGit) PushTag(ctx context.Context, tag string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PushTag", ctx, tag) ret0, _ := ret[0].(error) return ret0 } // PushTag indicates an expected call of PushTag. func (mr *MockGitMockRecorder) PushTag(ctx, tag any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PushTag", reflect.TypeOf((*MockGit)(nil).PushTag), ctx, tag) } // MockFS is a mock of FS interface. type MockFS struct { ctrl *gomock.Controller recorder *MockFSMockRecorder isgomock struct{} } // MockFSMockRecorder is the mock recorder for MockFS. type MockFSMockRecorder struct { mock *MockFS } // NewMockFS creates a new mock instance. func NewMockFS(ctrl *gomock.Controller) *MockFS { mock := &MockFS{ctrl: ctrl} mock.recorder = &MockFSMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFS) EXPECT() *MockFSMockRecorder { return m.recorder } // FindGoModFiles mocks base method. func (m *MockFS) FindGoModFiles(ctx context.Context, root string) ([]string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FindGoModFiles", ctx, root) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // FindGoModFiles indicates an expected call of FindGoModFiles. func (mr *MockFSMockRecorder) FindGoModFiles(ctx, root any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindGoModFiles", reflect.TypeOf((*MockFS)(nil).FindGoModFiles), ctx, root) } // MockUserInteraction is a mock of UserInteraction interface. type MockUserInteraction struct { ctrl *gomock.Controller recorder *MockUserInteractionMockRecorder isgomock struct{} } // MockUserInteractionMockRecorder is the mock recorder for MockUserInteraction. type MockUserInteractionMockRecorder struct { mock *MockUserInteraction } // NewMockUserInteraction creates a new mock instance. func NewMockUserInteraction(ctrl *gomock.Controller) *MockUserInteraction { mock := &MockUserInteraction{ctrl: ctrl} mock.recorder = &MockUserInteractionMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockUserInteraction) EXPECT() *MockUserInteractionMockRecorder { return m.recorder } // Confirm mocks base method. func (m *MockUserInteraction) Confirm(ctx context.Context, message string) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Confirm", ctx, message) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // Confirm indicates an expected call of Confirm. func (mr *MockUserInteractionMockRecorder) Confirm(ctx, message any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Confirm", reflect.TypeOf((*MockUserInteraction)(nil).Confirm), ctx, message) } // ConfirmWithDefault mocks base method. func (m *MockUserInteraction) ConfirmWithDefault(ctx context.Context, message string, defaultValue bool) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConfirmWithDefault", ctx, message, defaultValue) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // ConfirmWithDefault indicates an expected call of ConfirmWithDefault. func (mr *MockUserInteractionMockRecorder) ConfirmWithDefault(ctx, message, defaultValue any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfirmWithDefault", reflect.TypeOf((*MockUserInteraction)(nil).ConfirmWithDefault), ctx, message, defaultValue) } ================================================ FILE: cmd/tools/releaser/internal/release/release_test.go ================================================ package release import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) func TestManager_RunRelease(t *testing.T) { tests := []struct { name string currentTags []string expectedTarget string shouldError bool errorMsg string }{ { name: "successful release from prerelease", currentTags: []string{"v1.2.3-prerelease01"}, expectedTarget: "v1.2.3", shouldError: false, }, { name: "error when no prerelease exists", currentTags: []string{"v1.2.3"}, shouldError: true, errorMsg: "release command requires existing prerelease version", }, { name: "release from higher prerelease number", currentTags: []string{"v1.2.3-prerelease05"}, expectedTarget: "v1.2.3", shouldError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: "release", SkipConfirmations: true, // Skip confirmations for testing } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup mocks mockGit.EXPECT().GetTags(gomock.Any()).Return(tt.currentTags, nil) if !tt.shouldError { mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) mockGit.EXPECT().CreateTag(gomock.Any(), tt.expectedTarget).Return(nil) mockGit.EXPECT().PushTag(gomock.Any(), tt.expectedTarget).Return(nil) } err := manager.RunRelease(context.Background()) if tt.shouldError { require.Error(t, err) assert.Contains(t, err.Error(), tt.errorMsg) } else { require.NoError(t, err) } }) } } func TestManager_RunMinor(t *testing.T) { tests := []struct { name string currentTags []string expectedTarget string shouldError bool errorMsg string }{ { name: "successful minor from stable", currentTags: []string{"v1.2.3"}, expectedTarget: "v1.3.0-prerelease01", shouldError: false, }, { name: "error when current is prerelease", currentTags: []string{"v1.2.3-prerelease01"}, shouldError: true, errorMsg: "minor/major commands should be run from stable versions", }, { name: "minor from initial version", currentTags: []string{}, expectedTarget: "v0.1.0-prerelease01", shouldError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: "minor", SkipConfirmations: true, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup mocks mockGit.EXPECT().GetTags(gomock.Any()).Return(tt.currentTags, nil) if !tt.shouldError { mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) mockGit.EXPECT().CreateTag(gomock.Any(), tt.expectedTarget).Return(nil) mockGit.EXPECT().PushTag(gomock.Any(), tt.expectedTarget).Return(nil) } err := manager.RunMinor(context.Background()) if tt.shouldError { require.Error(t, err) assert.Contains(t, err.Error(), tt.errorMsg) } else { require.NoError(t, err) } }) } } func TestManager_RunMajor(t *testing.T) { tests := []struct { name string currentTags []string expectedTarget string shouldError bool }{ { name: "successful major from stable", currentTags: []string{"v1.2.3"}, expectedTarget: "v2.0.0-prerelease01", shouldError: false, }, { name: "major from initial version", currentTags: []string{}, expectedTarget: "v1.0.0-prerelease01", shouldError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: "major", SkipConfirmations: true, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup mocks mockGit.EXPECT().GetTags(gomock.Any()).Return(tt.currentTags, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) mockGit.EXPECT().CreateTag(gomock.Any(), tt.expectedTarget).Return(nil) mockGit.EXPECT().PushTag(gomock.Any(), tt.expectedTarget).Return(nil) err := manager.RunMajor(context.Background()) require.NoError(t, err) }) } } func TestManager_RunPrerelease(t *testing.T) { tests := []struct { name string currentTags []string expectedTarget string }{ { name: "increment prerelease number", currentTags: []string{"v1.2.3-prerelease01"}, expectedTarget: "v1.2.3-prerelease02", }, { name: "create first prerelease from stable", currentTags: []string{"v1.2.3"}, expectedTarget: "v1.2.3-prerelease01", }, { name: "increment from higher prerelease", currentTags: []string{"v1.2.3-prerelease05"}, expectedTarget: "v1.2.3-prerelease06", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: "prerelease", SkipConfirmations: true, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup mocks mockGit.EXPECT().GetTags(gomock.Any()).Return(tt.currentTags, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) mockGit.EXPECT().CreateTag(gomock.Any(), tt.expectedTarget).Return(nil) mockGit.EXPECT().PushTag(gomock.Any(), tt.expectedTarget).Return(nil) err := manager.RunPrerelease(context.Background()) require.NoError(t, err) }) } } func TestManager_ManualVersionOverride(t *testing.T) { tests := []struct { name string manualVersion string currentTags []string expectedTarget string shouldError bool errorMsg string }{ { name: "valid manual version", manualVersion: "v2.5.0", currentTags: []string{"v1.2.3"}, expectedTarget: "v2.5.0", shouldError: false, }, { name: "manual version without v prefix", manualVersion: "2.5.0", currentTags: []string{"v1.2.3"}, expectedTarget: "v2.5.0", shouldError: false, }, { name: "invalid manual version", manualVersion: "invalid", currentTags: []string{"v1.2.3"}, shouldError: true, errorMsg: "invalid manual version format", }, { name: "manual prerelease version", manualVersion: "v2.5.0-prerelease03", currentTags: []string{"v1.2.3"}, expectedTarget: "v2.5.0-prerelease03", shouldError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: "release", SkipConfirmations: true, ManualVersion: tt.manualVersion, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup mocks mockGit.EXPECT().GetTags(gomock.Any()).Return(tt.currentTags, nil) if !tt.shouldError { mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) mockGit.EXPECT().CreateTag(gomock.Any(), tt.expectedTarget).Return(nil) mockGit.EXPECT().PushTag(gomock.Any(), tt.expectedTarget).Return(nil) } err := manager.RunRelease(context.Background()) if tt.shouldError { require.Error(t, err) assert.Contains(t, err.Error(), tt.errorMsg) } else { require.NoError(t, err) } }) } } func TestManager_ConflictResolution(t *testing.T) { tests := []struct { name string targetVersion string existingTags []string modules []Module expectedCreate []string expectedSkip []string shouldError bool }{ { name: "partial conflict - some modules have version", targetVersion: "v1.3.0", existingTags: []string{"moduleA/v1.3.0"}, modules: []Module{ {Path: "moduleA", Version: "v1.2.0"}, {Path: "moduleB", Version: "v1.2.0"}, {Path: "", Version: "v1.2.0"}, // root module }, expectedCreate: []string{"moduleB/v1.3.0", "v1.3.0"}, expectedSkip: []string{"moduleA/v1.3.0"}, shouldError: false, }, { name: "complete conflict - all modules have version", targetVersion: "v1.3.0", existingTags: []string{"moduleA/v1.3.0", "moduleB/v1.3.0", "v1.3.0"}, modules: []Module{ {Path: "moduleA", Version: "v1.2.0"}, {Path: "moduleB", Version: "v1.2.0"}, {Path: "", Version: "v1.2.0"}, }, expectedCreate: []string{}, expectedSkip: []string{"moduleA/v1.3.0", "moduleB/v1.3.0", "v1.3.0"}, shouldError: true, }, { name: "no conflict", targetVersion: "v1.3.0", existingTags: []string{}, modules: []Module{ {Path: "moduleA", Version: "v1.2.0"}, {Path: "moduleB", Version: "v1.2.0"}, }, expectedCreate: []string{"moduleA/v1.3.0", "moduleB/v1.3.0"}, expectedSkip: []string{}, shouldError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: "release", SkipConfirmations: true, ManualVersion: tt.targetVersion, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup tag cache manager.tagCache = &TagCache{ AllTags: make([]ParsedTag, 0), } // Parse existing tags for _, tag := range tt.existingTags { manager.tagCache.AllTags = append(manager.tagCache.AllTags, ParsedTag{Raw: tag}) } conflictInfo, err := manager.CheckVersionExists(tt.targetVersion, tt.modules) if tt.shouldError && len(tt.expectedCreate) == 0 { require.Error(t, err) assert.ElementsMatch(t, tt.expectedSkip, conflictInfo.ExistingTags) assert.Empty(t, conflictInfo.MissingTags) } else { if len(tt.expectedSkip) > 0 { require.Error(t, err) // Should have conflict error assert.ElementsMatch(t, tt.expectedSkip, conflictInfo.ExistingTags) } else { require.NoError(t, err) // No conflict } assert.ElementsMatch(t, tt.expectedCreate, conflictInfo.MissingTags) } }) } } func TestManager_MultiModuleOperations(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{"cmd", "internal/tools"}, RequiredBranch: "master", Verbose: false, Command: "minor", SkipConfirmations: true, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup mocks for multi-module repo currentTags := []string{ "v1.2.3", "service1/v1.2.3", "service2/v1.2.3", } goModPaths := []string{".", "service1", "service2", "cmd/tool", "internal/tools/helper"} expectedTags := []string{ "v1.3.0-prerelease01", "service1/v1.3.0-prerelease01", "service2/v1.3.0-prerelease01", } mockGit.EXPECT().GetTags(gomock.Any()).Return(currentTags, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return(goModPaths, nil) // Expect tag creation for non-excluded modules only for _, tag := range expectedTags { mockGit.EXPECT().CreateTag(gomock.Any(), tag).Return(nil) mockGit.EXPECT().PushTag(gomock.Any(), tag).Return(nil) } err := manager.RunMinor(context.Background()) require.NoError(t, err) } func TestManager_ErrorHandling(t *testing.T) { tests := []struct { name string setupMock func(*MockGit, *MockFS, *MockUserInteraction) expectedErr string }{ { name: "git tags fetch error", setupMock: func(mockGit *MockGit, mockFS *MockFS, mockInteraction *MockUserInteraction) { mockGit.EXPECT().GetTags(gomock.Any()).Return(nil, errors.New("git error")) }, expectedErr: "failed to fetch tags", }, { name: "git branch fetch error", setupMock: func(mockGit *MockGit, mockFS *MockFS, mockInteraction *MockUserInteraction) { mockGit.EXPECT().GetTags(gomock.Any()).Return([]string{"v1.2.3"}, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("", errors.New("branch error")) }, expectedErr: "get current branch", }, { name: "fs find modules error", setupMock: func(mockGit *MockGit, mockFS *MockFS, mockInteraction *MockUserInteraction) { mockGit.EXPECT().GetTags(gomock.Any()).Return([]string{"v1.2.3"}, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return(nil, errors.New("fs error")) }, expectedErr: "find modules", }, { name: "tag creation error", setupMock: func(mockGit *MockGit, mockFS *MockFS, mockInteraction *MockUserInteraction) { mockGit.EXPECT().GetTags(gomock.Any()).Return([]string{"v1.2.3"}, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) mockGit.EXPECT().CreateTag(gomock.Any(), "v1.3.0-prerelease01").Return(errors.New("tag error")) }, expectedErr: "failed to create tag", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: "minor", SkipConfirmations: true, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) tt.setupMock(mockGit, mockFS, mockInteraction) err := manager.RunMinor(context.Background()) require.Error(t, err) assert.Contains(t, err.Error(), tt.expectedErr) }) } } func TestManager_RunPatch(t *testing.T) { tests := []struct { name string currentTags []string expectedTarget string shouldError bool }{ { name: "successful patch from stable", currentTags: []string{"v1.2.3"}, expectedTarget: "v1.2.4-prerelease01", shouldError: false, }, { name: "patch from initial version", currentTags: []string{}, expectedTarget: "v0.0.1-prerelease01", shouldError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: "patch", SkipConfirmations: true, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup mocks mockGit.EXPECT().GetTags(gomock.Any()).Return(tt.currentTags, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) mockGit.EXPECT().CreateTag(gomock.Any(), tt.expectedTarget).Return(nil) mockGit.EXPECT().PushTag(gomock.Any(), tt.expectedTarget).Return(nil) err := manager.RunPatch(context.Background()) require.NoError(t, err) }) } } ================================================ FILE: cmd/tools/releaser/internal/release/scenario_test.go ================================================ package release import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) func TestCompleteReleaseWorkflow(t *testing.T) { tests := []struct { name string workflow []workflowStep description string }{ { name: "feature development cycle", description: "Complete feature development from minor through prerelease iterations to final release", workflow: []workflowStep{ { command: "minor", currentTags: []string{"v1.2.3"}, expectedTag: "v1.3.0-prerelease01", expectedError: false, }, { command: "prerelease", currentTags: []string{"v1.2.3", "v1.3.0-prerelease01"}, expectedTag: "v1.3.0-prerelease02", expectedError: false, }, { command: "prerelease", currentTags: []string{"v1.2.3", "v1.3.0-prerelease01", "v1.3.0-prerelease02"}, expectedTag: "v1.3.0-prerelease03", expectedError: false, }, { command: "release", currentTags: []string{"v1.2.3", "v1.3.0-prerelease01", "v1.3.0-prerelease02", "v1.3.0-prerelease03"}, expectedTag: "v1.3.0", expectedError: false, }, }, }, { name: "major version cycle", description: "Major version development cycle with breaking changes", workflow: []workflowStep{ { command: "major", currentTags: []string{"v1.3.0"}, expectedTag: "v2.0.0-prerelease01", expectedError: false, }, { command: "prerelease", currentTags: []string{"v1.3.0", "v2.0.0-prerelease01"}, expectedTag: "v2.0.0-prerelease02", expectedError: false, }, { command: "release", currentTags: []string{"v1.3.0", "v2.0.0-prerelease01", "v2.0.0-prerelease02"}, expectedTag: "v2.0.0", expectedError: false, }, }, }, { name: "initial repository setup", description: "Starting from empty repository to first release", workflow: []workflowStep{ { command: "minor", currentTags: []string{}, expectedTag: "v0.1.0-prerelease01", expectedError: false, }, { command: "release", currentTags: []string{"v0.1.0-prerelease01"}, expectedTag: "v0.1.0", expectedError: false, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Logf("Testing workflow: %s", tt.description) for i, step := range tt.workflow { t.Logf("Step %d: %s command", i+1, step.command) ctrl := gomock.NewController(t) mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: step.command, SkipConfirmations: true, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup mocks mockGit.EXPECT().GetTags(gomock.Any()).Return(step.currentTags, nil) if !step.expectedError { mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) mockGit.EXPECT().CreateTag(gomock.Any(), step.expectedTag).Return(nil) mockGit.EXPECT().PushTag(gomock.Any(), step.expectedTag).Return(nil) } // Execute command var err error switch step.command { case "minor": err = manager.RunMinor(context.Background()) case "major": err = manager.RunMajor(context.Background()) case "prerelease": err = manager.RunPrerelease(context.Background()) case "release": err = manager.RunRelease(context.Background()) } if step.expectedError { require.Error(t, err) } else { require.NoError(t, err) } ctrl.Finish() } }) } } type workflowStep struct { command string currentTags []string expectedTag string expectedError bool } func TestMultiModuleComplexScenarios(t *testing.T) { tests := []struct { name string currentTags []string modules []string command string expectedTags []string excludedModules []string }{ { name: "multi-module minor release", currentTags: []string{ "v1.2.3", "service1/v1.2.3", "service2/v1.2.3", "cmd/tool/v1.0.0", // This should be excluded }, modules: []string{".", "service1", "service2", "cmd/tool"}, command: "minor", expectedTags: []string{ "v1.3.0-prerelease01", "service1/v1.3.0-prerelease01", "service2/v1.3.0-prerelease01", }, excludedModules: []string{"cmd/tool"}, }, { name: "multi-module with version conflicts", currentTags: []string{ "v1.2.3", "service1/v1.3.0", // Already has target version "service2/v1.2.3", }, modules: []string{".", "service1", "service2"}, command: "manual", expectedTags: []string{ "v1.3.0", // Root module "service2/v1.3.0", // Service2 missing // service1/v1.3.0 should be skipped }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: tt.excludedModules, RequiredBranch: "master", Verbose: false, Command: tt.command, SkipConfirmations: true, } if tt.command == "manual" { config.ManualVersion = "v1.3.0" } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup mocks mockGit.EXPECT().GetTags(gomock.Any()).Return(tt.currentTags, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return(tt.modules, nil) // Expect tag creation only for expected tags for _, tag := range tt.expectedTags { mockGit.EXPECT().CreateTag(gomock.Any(), tag).Return(nil) mockGit.EXPECT().PushTag(gomock.Any(), tag).Return(nil) } // Execute command var err error if tt.command == "manual" { err = manager.RunRelease(context.Background()) } else { err = manager.RunMinor(context.Background()) } require.NoError(t, err) }) } } func TestErrorRecoveryScenarios(t *testing.T) { tests := []struct { name string description string setupMocks func(*MockGit, *MockFS, *MockUserInteraction) command string expectedErr string shouldRecover bool }{ { name: "partial tag creation failure", description: "Some tags created successfully, others failed", setupMocks: func(mockGit *MockGit, mockFS *MockFS, mockInteraction *MockUserInteraction) { mockGit.EXPECT().GetTags(gomock.Any()).Return([]string{"v1.2.3"}, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{".", "service1"}, nil) // First tag succeeds, second fails mockGit.EXPECT().CreateTag(gomock.Any(), "v1.3.0-prerelease01").Return(nil) mockGit.EXPECT().CreateTag(gomock.Any(), "service1/v1.3.0-prerelease01"). Return(assert.AnError) }, command: "minor", expectedErr: "failed to create tag", }, { name: "network failure during push", description: "Tags created locally but push failed", setupMocks: func(mockGit *MockGit, mockFS *MockFS, mockInteraction *MockUserInteraction) { mockGit.EXPECT().GetTags(gomock.Any()).Return([]string{"v1.2.3"}, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) // Tag creation succeeds mockGit.EXPECT().CreateTag(gomock.Any(), "v1.3.0-prerelease01").Return(nil) // Push fails mockGit.EXPECT().PushTag(gomock.Any(), "v1.3.0-prerelease01"). Return(assert.AnError) }, command: "minor", expectedErr: "push tags", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Logf("Testing scenario: %s", tt.description) ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: tt.command, SkipConfirmations: true, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) tt.setupMocks(mockGit, mockFS, mockInteraction) var err error switch tt.command { case "minor": err = manager.RunMinor(context.Background()) case "release": err = manager.RunRelease(context.Background()) } require.Error(t, err) assert.Contains(t, err.Error(), tt.expectedErr) }) } } func TestConcurrentPrereleaseScenarios(t *testing.T) { tests := []struct { name string existingTags []string expectedVersion string description string }{ { name: "gap in prerelease sequence", existingTags: []string{ "v1.3.0-prerelease01", "v1.3.0-prerelease03", // Missing 02 "v1.3.0-prerelease05", }, expectedVersion: "v1.3.0-prerelease06", // Should continue from highest description: "Should continue from highest prerelease number even with gaps", }, { name: "mixed prerelease versions", existingTags: []string{ "v1.2.0-prerelease05", "v1.3.0-prerelease01", "v1.3.0-prerelease02", "v1.4.0-prerelease01", }, expectedVersion: "v1.4.0-prerelease02", // Should work with current global version description: "Should handle mixed prerelease versions for different base versions", }, { name: "approaching prerelease limit", existingTags: []string{ "v1.3.0-prerelease98", }, expectedVersion: "v1.3.0-prerelease99", description: "Should handle high prerelease numbers approaching limit", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Logf("Testing scenario: %s", tt.description) ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: "prerelease", SkipConfirmations: true, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup mocks mockGit.EXPECT().GetTags(gomock.Any()).Return(tt.existingTags, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return("master", nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) mockGit.EXPECT().CreateTag(gomock.Any(), tt.expectedVersion).Return(nil) mockGit.EXPECT().PushTag(gomock.Any(), tt.expectedVersion).Return(nil) err := manager.RunPrerelease(context.Background()) require.NoError(t, err) }) } } func TestUserInteractionScenarios(t *testing.T) { tests := []struct { name string currentBranch string userResponses []bool // Responses to confirmation prompts skipConfirmations bool shouldComplete bool expectedActions int // Number of git operations expected }{ { name: "user confirms all warnings and actions", currentBranch: "feature-branch", // Wrong branch userResponses: []bool{true, true, true}, // Continue despite warning, create tags, push tags skipConfirmations: false, shouldComplete: true, expectedActions: 2, // create + push }, { name: "user rejects branch warning", currentBranch: "feature-branch", userResponses: []bool{false}, // Reject continuing despite warning skipConfirmations: false, shouldComplete: false, expectedActions: 0, }, { name: "user confirms warning but rejects tag creation", currentBranch: "feature-branch", userResponses: []bool{true, false}, // Continue despite warning, but reject tag creation skipConfirmations: false, shouldComplete: false, expectedActions: 0, }, { name: "user creates tags but skips push", currentBranch: "feature-branch", userResponses: []bool{true, true, false}, // Continue, create, but don't push skipConfirmations: false, shouldComplete: false, // Partial completion expectedActions: 1, // Only create }, { name: "skip all confirmations", currentBranch: "feature-branch", userResponses: []bool{}, // No interactions expected skipConfirmations: true, shouldComplete: true, expectedActions: 2, // create + push }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockGit := NewMockGit(ctrl) mockFS := NewMockFS(ctrl) mockInteraction := NewMockUserInteraction(ctrl) config := Config{ RepoRoot: "/test", ExcludedDirs: []string{}, RequiredBranch: "master", Verbose: false, Command: "minor", SkipConfirmations: tt.skipConfirmations, } manager := NewReleaseManager(config, mockGit, mockFS, mockInteraction) // Setup basic mocks mockGit.EXPECT().GetTags(gomock.Any()).Return([]string{"v1.2.3"}, nil) mockGit.EXPECT().GetCurrentBranch(gomock.Any()).Return(tt.currentBranch, nil) mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return([]string{"."}, nil) // Setup user interaction mocks responseIndex := 0 if !tt.skipConfirmations { if tt.currentBranch != "master" { // Branch warning confirmation if responseIndex < len(tt.userResponses) { mockInteraction.EXPECT(). ConfirmWithDefault(gomock.Any(), "Continue?", false). Return(tt.userResponses[responseIndex], nil) responseIndex++ if !tt.userResponses[responseIndex-1] { // User rejected, no more interactions goto executeTest } } } // Tag creation confirmation if responseIndex < len(tt.userResponses) { mockInteraction.EXPECT(). Confirm(gomock.Any(), gomock.Any()). Return(tt.userResponses[responseIndex], nil) responseIndex++ if !tt.userResponses[responseIndex-1] { // User rejected tag creation goto executeTest } } // Tag push confirmation if responseIndex < len(tt.userResponses) { mockInteraction.EXPECT(). Confirm(gomock.Any(), gomock.Any()). Return(tt.userResponses[responseIndex], nil) } } // Setup git operation mocks based on expected actions if tt.expectedActions >= 1 { mockGit.EXPECT().CreateTag(gomock.Any(), "v1.3.0-prerelease01").Return(nil) } if tt.expectedActions >= 2 { mockGit.EXPECT().PushTag(gomock.Any(), "v1.3.0-prerelease01").Return(nil) } executeTest: err := manager.RunMinor(context.Background()) if tt.shouldComplete { require.NoError(t, err) } else { // May have error or be cancelled if err != nil { t.Logf("Expected cancellation/error: %v", err) } } }) } } ================================================ FILE: cmd/tools/releaser/internal/release/types.go ================================================ package release import "github.com/Masterminds/semver/v3" // State holds the current state of the repository type State struct { CurrentBranch string Modules []Module CurrentVersion string TagCache *TagCache } // Module represents a Go module type Module struct { Path string Version string } // ParsedTag represents a single parsed git tag type ParsedTag struct { Raw string // Original tag name (e.g., "service1/v1.2.3-prerelease01") ModulePath string // Module path (e.g., "service1", "" for root) Version *semver.Version // Parsed semantic version GitSHA string // Git commit SHA IsPrerelease bool PrereleaseNum int // Extracted prerelease number (01, 02, etc.) } // TagCache holds all parsed tag information type TagCache struct { AllTags []ParsedTag VersionTags []ParsedTag // Only valid semver tags ModuleTags map[string][]ParsedTag // Tags grouped by module path PrereleaseCache map[string][]int // Base version -> prerelease numbers HighestVersion *semver.Version // Cached highest version } // VersionConflictInfo holds information about version conflicts type VersionConflictInfo struct { ExistingTags []string // Tags that already exist MissingTags []string // Tags that need to be created } // WarningType represents different types of warnings type WarningType int const ( WrongBranch WarningType = iota ExistingTags ) // Warning represents a validation warning that can be overridden type Warning struct { Type WarningType Message string } // Action represents a planned release action type Action struct { Type ActionType Target string // tag name, module path Description string GitSHA string // for existing tags } type ActionType int const ( ActionCreateTag ActionType = iota ActionPushTags ) ================================================ FILE: cmd/tools/releaser/internal/release/validate_test.go ================================================ package release import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) func TestValidateReleaseCommand(t *testing.T) { tests := []struct { name string currentVersion string shouldError bool errorMsg string }{ { name: "valid prerelease version", currentVersion: "v1.2.3-prerelease01", shouldError: false, }, { name: "valid higher prerelease", currentVersion: "v2.5.0-prerelease15", shouldError: false, }, { name: "invalid stable version", currentVersion: "v1.2.3", shouldError: true, errorMsg: "release command requires existing prerelease version", }, { name: "invalid initial version", currentVersion: "v0.0.0", shouldError: true, errorMsg: "release command requires existing prerelease version", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{} err := manager.validateReleaseCommand(tt.currentVersion) if tt.shouldError { require.Error(t, err) assert.Contains(t, err.Error(), tt.errorMsg) } else { require.NoError(t, err) } }) } } func TestValidateMinorMajorCommand(t *testing.T) { tests := []struct { name string currentVersion string shouldError bool errorMsg string }{ { name: "valid stable version", currentVersion: "v1.2.3", shouldError: false, }, { name: "valid initial version", currentVersion: "v0.0.0", shouldError: false, }, { name: "invalid prerelease version", currentVersion: "v1.2.3-prerelease01", shouldError: true, errorMsg: "minor/major commands should be run from stable versions", }, { name: "invalid higher prerelease", currentVersion: "v2.5.0-prerelease15", shouldError: true, errorMsg: "minor/major commands should be run from stable versions", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{} err := manager.validateMinorMajorCommand(tt.currentVersion) if tt.shouldError { require.Error(t, err) assert.Contains(t, err.Error(), tt.errorMsg) } else { require.NoError(t, err) } }) } } func TestProcessManualVersion(t *testing.T) { tests := []struct { name string manualVersion string expected string shouldError bool errorMsg string }{ { name: "valid version with v", manualVersion: "v1.2.3", expected: "v1.2.3", shouldError: false, }, { name: "valid version without v", manualVersion: "1.2.3", expected: "v1.2.3", shouldError: false, }, { name: "valid prerelease with v", manualVersion: "v2.0.0-prerelease01", expected: "v2.0.0-prerelease01", shouldError: false, }, { name: "valid prerelease without v", manualVersion: "2.0.0-prerelease01", expected: "v2.0.0-prerelease01", shouldError: false, }, { name: "auto-complete version format", manualVersion: "1.2", expected: "v1.2.0", shouldError: false, }, { name: "completely invalid", manualVersion: "not-a-version", shouldError: true, errorMsg: "invalid manual version format", }, { name: "empty version", manualVersion: "", shouldError: true, errorMsg: "invalid manual version format", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{} result, err := manager.processManualVersion(tt.manualVersion) if tt.shouldError { require.Error(t, err) assert.Contains(t, err.Error(), tt.errorMsg) } else { require.NoError(t, err) assert.Equal(t, tt.expected, result) } }) } } func TestShouldExcludeModule(t *testing.T) { tests := []struct { name string excludedDirs []string modulePath string shouldExclude bool }{ { name: "not excluded", excludedDirs: []string{"cmd", "internal/tools"}, modulePath: "service1", shouldExclude: false, }, { name: "exact match", excludedDirs: []string{"cmd", "internal/tools"}, modulePath: "cmd", shouldExclude: true, }, { name: "prefix match", excludedDirs: []string{"cmd", "internal/tools"}, modulePath: "cmd/tool1", shouldExclude: true, }, { name: "nested prefix match", excludedDirs: []string{"cmd", "internal/tools"}, modulePath: "internal/tools/helper", shouldExclude: true, }, { name: "partial match should not exclude", excludedDirs: []string{"cmd", "internal/tools"}, modulePath: "cmdline", shouldExclude: false, }, { name: "empty excluded dirs", excludedDirs: []string{}, modulePath: "any/path", shouldExclude: false, }, { name: "root module", excludedDirs: []string{"cmd"}, modulePath: "", shouldExclude: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{ config: Config{ ExcludedDirs: tt.excludedDirs, }, } result := manager.shouldExcludeModule(tt.modulePath) assert.Equal(t, tt.shouldExclude, result) }) } } func TestGetTagName(t *testing.T) { tests := []struct { name string module Module version string expected string }{ { name: "root module", module: Module{Path: "", Version: "v1.2.3"}, version: "v1.3.0", expected: "v1.3.0", }, { name: "submodule", module: Module{Path: "service1", Version: "v1.2.3"}, version: "v1.3.0", expected: "service1/v1.3.0", }, { name: "nested module", module: Module{Path: "services/auth", Version: "v1.2.3"}, version: "v2.0.0", expected: "services/auth/v2.0.0", }, { name: "prerelease version", module: Module{Path: "service1", Version: "v1.2.3"}, version: "v1.3.0-prerelease01", expected: "service1/v1.3.0-prerelease01", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{} result := manager.getTagName(tt.module, tt.version) assert.Equal(t, tt.expected, result) }) } } func TestGetLatestVersionForModule(t *testing.T) { tests := []struct { name string modulePath string tags []string expected string }{ { name: "no tags for module", modulePath: "service1", tags: []string{}, expected: "v0.0.0", }, { name: "single tag", modulePath: "service1", tags: []string{"service1/v1.2.3"}, expected: "v1.2.3", }, { name: "multiple tags - choose latest", modulePath: "service1", tags: []string{"service1/v1.2.3", "service1/v1.1.0", "service1/v2.0.0"}, expected: "v2.0.0", }, { name: "mix of stable and prerelease", modulePath: "service1", tags: []string{"service1/v1.2.3", "service1/v1.3.0-prerelease01"}, expected: "v1.3.0-prerelease01", }, { name: "root module", modulePath: "", tags: []string{"v1.2.3", "v1.1.0", "v2.0.0"}, expected: "v2.0.0", }, { name: "no cache initialized", modulePath: "service1", tags: nil, // Will simulate uninitialized cache expected: "v0.0.0", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{} if tt.tags != nil { // Initialize cache manager.tagCache = &TagCache{ ModuleTags: make(map[string][]ParsedTag), } // Parse and add tags for _, tag := range tt.tags { parsed := manager.parseTag(tag) if parsed.Version != nil { if manager.tagCache.ModuleTags[parsed.ModulePath] == nil { manager.tagCache.ModuleTags[parsed.ModulePath] = make([]ParsedTag, 0) } manager.tagCache.ModuleTags[parsed.ModulePath] = append( manager.tagCache.ModuleTags[parsed.ModulePath], parsed) } } } result := manager.getLatestVersionForModule(tt.modulePath) assert.Equal(t, tt.expected, result) }) } } func TestFilterActionsForMissingTags(t *testing.T) { tests := []struct { name string actions []Action missingTags []string expected []Action }{ { name: "filter some actions", actions: []Action{ {Type: ActionCreateTag, Target: "v1.3.0"}, {Type: ActionPushTags, Target: "v1.3.0"}, {Type: ActionCreateTag, Target: "service1/v1.3.0"}, {Type: ActionPushTags, Target: "service1/v1.3.0"}, {Type: ActionCreateTag, Target: "service2/v1.3.0"}, {Type: ActionPushTags, Target: "service2/v1.3.0"}, }, missingTags: []string{"v1.3.0", "service2/v1.3.0"}, expected: []Action{ {Type: ActionCreateTag, Target: "v1.3.0"}, {Type: ActionPushTags, Target: "v1.3.0"}, {Type: ActionCreateTag, Target: "service2/v1.3.0"}, {Type: ActionPushTags, Target: "service2/v1.3.0"}, }, }, { name: "no missing tags", actions: []Action{ {Type: ActionCreateTag, Target: "v1.3.0"}, {Type: ActionPushTags, Target: "v1.3.0"}, }, missingTags: []string{}, expected: []Action(nil), }, { name: "no actions", actions: []Action{}, missingTags: []string{"v1.3.0"}, expected: []Action(nil), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{} result := manager.filterActionsForMissingTags(tt.actions, tt.missingTags) assert.Equal(t, tt.expected, result) }) } } func TestValidateWithWarnings(t *testing.T) { tests := []struct { name string currentBranch string requiredBranch string expectedWarnings int expectedMessage string }{ { name: "correct branch", currentBranch: "master", requiredBranch: "master", expectedWarnings: 0, }, { name: "wrong branch", currentBranch: "feature-branch", requiredBranch: "master", expectedWarnings: 1, expectedMessage: "you are not on master", }, { name: "no required branch", currentBranch: "any-branch", requiredBranch: "", expectedWarnings: 0, }, { name: "different wrong branch", currentBranch: "develop", requiredBranch: "main", expectedWarnings: 1, expectedMessage: "you are not on main", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{ config: Config{ RequiredBranch: tt.requiredBranch, }, } state := &State{ CurrentBranch: tt.currentBranch, } warnings := manager.validateWithWarnings(state, "v1.3.0") assert.Len(t, warnings, tt.expectedWarnings) if tt.expectedWarnings > 0 { assert.Contains(t, warnings[0].Message, tt.expectedMessage) assert.Equal(t, WrongBranch, warnings[0].Type) } }) } } func TestFindModules(t *testing.T) { tests := []struct { name string goModPaths []string excludedDirs []string expected []Module }{ { name: "single root module", goModPaths: []string{"."}, excludedDirs: []string{}, expected: []Module{ {Path: "", Version: "v0.0.0"}, }, }, { name: "multiple modules", goModPaths: []string{".", "service1", "service2"}, excludedDirs: []string{}, expected: []Module{ {Path: "", Version: "v0.0.0"}, {Path: "service1", Version: "v0.0.0"}, {Path: "service2", Version: "v0.0.0"}, }, }, { name: "modules with exclusions", goModPaths: []string{".", "service1", "cmd/tool", "internal/tools/helper"}, excludedDirs: []string{"cmd", "internal/tools"}, expected: []Module{ {Path: "", Version: "v0.0.0"}, {Path: "service1", Version: "v0.0.0"}, }, }, { name: "deduplicate paths", goModPaths: []string{".", "./", "service1", "./service1"}, excludedDirs: []string{}, expected: []Module{ {Path: "", Version: "v0.0.0"}, {Path: "service1", Version: "v0.0.0"}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockFS := NewMockFS(ctrl) manager := &Manager{ config: Config{ RepoRoot: "/test", ExcludedDirs: tt.excludedDirs, }, fs: mockFS, tagCache: &TagCache{ ModuleTags: make(map[string][]ParsedTag), }, } mockFS.EXPECT().FindGoModFiles(gomock.Any(), "/test").Return(tt.goModPaths, nil) result, err := manager.FindModules(context.Background()) require.NoError(t, err) assert.ElementsMatch(t, tt.expected, result) }) } } func TestHandleWarningsAndConfirmations(t *testing.T) { tests := []struct { name string warnings []Warning skipConfirmations bool userResponses []bool shouldError bool errorMsg string }{ { name: "no warnings", warnings: []Warning{}, skipConfirmations: false, shouldError: false, }, { name: "skip confirmations with warnings", warnings: []Warning{ {Type: WrongBranch, Message: "you are not on master"}, }, skipConfirmations: true, shouldError: false, }, { name: "user confirms warning", warnings: []Warning{ {Type: WrongBranch, Message: "you are not on master"}, }, skipConfirmations: false, userResponses: []bool{true}, shouldError: false, }, { name: "user rejects warning", warnings: []Warning{ {Type: WrongBranch, Message: "you are not on master"}, }, skipConfirmations: false, userResponses: []bool{false}, shouldError: true, errorMsg: "operation cancelled due to: you are not on master", }, { name: "multiple warnings - all confirmed", warnings: []Warning{ {Type: WrongBranch, Message: "you are not on master"}, {Type: ExistingTags, Message: "some tags exist"}, }, skipConfirmations: false, userResponses: []bool{true, true}, shouldError: false, }, { name: "multiple warnings - second rejected", warnings: []Warning{ {Type: WrongBranch, Message: "you are not on master"}, {Type: ExistingTags, Message: "some tags exist"}, }, skipConfirmations: false, userResponses: []bool{true, false}, shouldError: true, errorMsg: "operation cancelled due to: some tags exist", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockInteraction := NewMockUserInteraction(ctrl) manager := &Manager{ config: Config{ SkipConfirmations: tt.skipConfirmations, }, interaction: mockInteraction, } // Setup mock expectations if !tt.skipConfirmations && len(tt.warnings) > 0 { for i, response := range tt.userResponses { if i < len(tt.warnings) { mockInteraction.EXPECT(). ConfirmWithDefault(gomock.Any(), "Continue?", false). Return(response, nil) if !response { break // Stop after first rejection } } } } err := manager.handleWarningsAndConfirmations(context.Background(), tt.warnings) if tt.shouldError { require.Error(t, err) if tt.errorMsg != "" { assert.Contains(t, err.Error(), tt.errorMsg) } } else { require.NoError(t, err) } }) } } ================================================ FILE: cmd/tools/releaser/internal/release/version_test.go ================================================ package release import ( "testing" "github.com/Masterminds/semver/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestParseTag(t *testing.T) { tests := []struct { name string rawTag string expectedModule string expectedVer string expectedPre bool expectedPreNum int shouldHaveVer bool }{ { name: "root module stable version", rawTag: "v1.2.3", expectedModule: "", expectedVer: "1.2.3", expectedPre: false, shouldHaveVer: true, }, { name: "root module prerelease", rawTag: "v1.2.3-prerelease01", expectedModule: "", expectedVer: "1.2.3-prerelease01", expectedPre: true, expectedPreNum: 1, shouldHaveVer: true, }, { name: "submodule stable version", rawTag: "service1/v1.2.3", expectedModule: "service1", expectedVer: "1.2.3", expectedPre: false, shouldHaveVer: true, }, { name: "submodule prerelease", rawTag: "service1/v1.2.3-prerelease05", expectedModule: "service1", expectedVer: "1.2.3-prerelease05", expectedPre: true, expectedPreNum: 5, shouldHaveVer: true, }, { name: "nested module", rawTag: "services/auth/v2.1.0", expectedModule: "services/auth", expectedVer: "2.1.0", expectedPre: false, shouldHaveVer: true, }, { name: "high prerelease number", rawTag: "v1.2.3-prerelease99", expectedModule: "", expectedVer: "1.2.3-prerelease99", expectedPre: true, expectedPreNum: 99, shouldHaveVer: true, }, { name: "invalid version tag", rawTag: "invalid-tag", shouldHaveVer: false, }, { name: "not a version tag", rawTag: "refs/heads/master", shouldHaveVer: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{} parsed := manager.parseTag(tt.rawTag) assert.Equal(t, tt.rawTag, parsed.Raw) assert.Equal(t, tt.expectedModule, parsed.ModulePath) assert.Equal(t, tt.expectedPre, parsed.IsPrerelease) if tt.shouldHaveVer { require.NotNil(t, parsed.Version) assert.Equal(t, tt.expectedVer, parsed.Version.String()) if tt.expectedPre { assert.Equal(t, tt.expectedPreNum, parsed.PrereleaseNum) } } else { assert.Nil(t, parsed.Version) } }) } } func TestIncrementVersion(t *testing.T) { tests := []struct { name string current string versionType string expected string shouldError bool }{ { name: "increment major", current: "v1.2.3", versionType: "major", expected: "v2.0.0", }, { name: "increment minor", current: "v1.2.3", versionType: "minor", expected: "v1.3.0", }, { name: "increment patch", current: "v1.2.3", versionType: "patch", expected: "v1.2.4", }, { name: "increment from zero", current: "v0.0.0", versionType: "minor", expected: "v0.1.0", }, { name: "increment major from high version", current: "v15.27.99", versionType: "major", expected: "v16.0.0", }, { name: "invalid version type", current: "v1.2.3", versionType: "invalid", shouldError: true, }, { name: "invalid current version", current: "invalid", versionType: "major", shouldError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := IncrementVersion(tt.current, tt.versionType) if tt.shouldError { require.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tt.expected, result) } }) } } func TestNormalizeVersion(t *testing.T) { tests := []struct { name string input string expected string shouldError bool }{ { name: "already normalized", input: "v1.2.3", expected: "v1.2.3", }, { name: "add v prefix", input: "1.2.3", expected: "v1.2.3", }, { name: "prerelease with v", input: "v1.2.3-prerelease01", expected: "v1.2.3-prerelease01", }, { name: "prerelease without v", input: "1.2.3-prerelease01", expected: "v1.2.3-prerelease01", }, { name: "invalid semver", input: "1.2", expected: "v1.2.0", }, { name: "completely invalid", input: "not-a-version", shouldError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := NormalizeVersion(tt.input) if tt.shouldError { require.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tt.expected, result) } }) } } func TestGetNextPrereleaseVersion(t *testing.T) { tests := []struct { name string baseVersion string existingPres []int expected string shouldError bool errorMsg string }{ { name: "first prerelease", baseVersion: "v1.2.3", existingPres: []int{}, expected: "v1.2.3-prerelease01", }, { name: "increment from existing", baseVersion: "v1.2.3", existingPres: []int{1, 2, 3}, expected: "v1.2.3-prerelease04", }, { name: "increment from high number", baseVersion: "v1.2.3", existingPres: []int{25}, expected: "v1.2.3-prerelease26", }, { name: "unordered existing prereleases", baseVersion: "v1.2.3", existingPres: []int{3, 1, 5, 2}, expected: "v1.2.3-prerelease06", }, { name: "max prerelease exceeded", baseVersion: "v1.2.3", existingPres: []int{99}, // Next would be 100 shouldError: true, errorMsg: "maximum prerelease number (99) exceeded", }, { name: "invalid base version", baseVersion: "invalid", shouldError: true, errorMsg: "failed to parse base version", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{ tagCache: &TagCache{ PrereleaseCache: make(map[string][]int), }, } // Setup prerelease cache if len(tt.existingPres) > 0 { // Parse base version to get the clean string if baseVer, err := semver.NewVersion(tt.baseVersion); err == nil { cleanBase := "v" + baseVer.String() manager.tagCache.PrereleaseCache[cleanBase] = tt.existingPres } } result, err := manager.GetNextPrereleaseVersion(tt.baseVersion) if tt.shouldError { require.Error(t, err) assert.Contains(t, err.Error(), tt.errorMsg) } else { require.NoError(t, err) assert.Equal(t, tt.expected, result) } }) } } func TestVersionCalculationMethods(t *testing.T) { tests := []struct { name string method string currentVersion string expected string shouldError bool errorMsg string }{ // Release version calculations { name: "release from prerelease", method: "release", currentVersion: "v1.2.3-prerelease01", expected: "v1.2.3", }, { name: "release from high prerelease", currentVersion: "v2.5.10-prerelease15", method: "release", expected: "v2.5.10", }, // Minor version calculations { name: "minor from stable", method: "minor", currentVersion: "v1.2.3", expected: "v1.3.0-prerelease01", }, { name: "minor from prerelease base", method: "minor", currentVersion: "v1.2.3-prerelease05", expected: "v1.3.0-prerelease01", }, // Major version calculations { name: "major from stable", method: "major", currentVersion: "v1.2.3", expected: "v2.0.0-prerelease01", }, { name: "major from prerelease base", method: "major", currentVersion: "v1.2.3-prerelease05", expected: "v2.0.0-prerelease01", }, // Prerelease calculations { name: "prerelease from stable", method: "prerelease", currentVersion: "v1.2.3", expected: "v1.2.3-prerelease01", }, { name: "prerelease increment", method: "prerelease", currentVersion: "v1.2.3-prerelease01", expected: "v1.2.3-prerelease02", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{ tagCache: &TagCache{ PrereleaseCache: make(map[string][]int), }, } // Setup prerelease cache for increment scenarios if tt.method == "prerelease" && tt.currentVersion == "v1.2.3-prerelease01" { manager.tagCache.PrereleaseCache["v1.2.3"] = []int{1} } var result string var err error switch tt.method { case "release": result, err = manager.calculateReleaseVersion(tt.currentVersion) case "minor": result, err = manager.calculateMinorVersion(tt.currentVersion) case "major": result, err = manager.calculateMajorVersion(tt.currentVersion) case "prerelease": result, err = manager.calculatePrereleaseVersion(tt.currentVersion) } if tt.shouldError { require.Error(t, err) assert.Contains(t, err.Error(), tt.errorMsg) } else { require.NoError(t, err) assert.Equal(t, tt.expected, result) } }) } } func TestGetBaseVersion(t *testing.T) { tests := []struct { name string input string expected string }{ { name: "stable version", input: "v1.2.3", expected: "v1.2.3", }, { name: "prerelease version", input: "v1.2.3-prerelease01", expected: "v1.2.3", }, { name: "high prerelease version", input: "v2.5.10-prerelease25", expected: "v2.5.10", }, { name: "invalid version", input: "invalid", expected: "invalid", // Should return input unchanged }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{} result := manager.getBaseVersion(tt.input) assert.Equal(t, tt.expected, result) }) } } func TestGetCurrentGlobalVersion(t *testing.T) { tests := []struct { name string versionTags []string expected string }{ { name: "no versions", versionTags: []string{}, expected: "v0.0.0", }, { name: "single version", versionTags: []string{"v1.2.3"}, expected: "v1.2.3", }, { name: "multiple versions", versionTags: []string{"v1.2.3", "v1.1.0", "v2.0.0", "v1.3.0"}, expected: "v2.0.0", }, { name: "prerelease versions", versionTags: []string{"v1.2.3", "v1.3.0-prerelease01", "v1.2.4"}, expected: "v1.3.0-prerelease01", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := &Manager{ tagCache: &TagCache{ VersionTags: make([]ParsedTag, 0), }, } // Parse and add version tags for _, tag := range tt.versionTags { parsed := manager.parseTag(tag) if parsed.Version != nil { manager.tagCache.VersionTags = append(manager.tagCache.VersionTags, parsed) } } // Sort and set highest version if len(manager.tagCache.VersionTags) > 0 { manager.sortVersionTags() manager.tagCache.HighestVersion = manager.tagCache.VersionTags[len(manager.tagCache.VersionTags)-1].Version } result := manager.GetCurrentGlobalVersion() assert.Equal(t, tt.expected, result) }) } } func TestSortVersionTags(t *testing.T) { manager := &Manager{ tagCache: &TagCache{ VersionTags: []ParsedTag{ {Raw: "v2.0.0", Version: semver.MustParse("v2.0.0")}, {Raw: "v1.2.3", Version: semver.MustParse("v1.2.3")}, {Raw: "v1.3.0-prerelease01", Version: semver.MustParse("v1.3.0-prerelease01")}, {Raw: "v1.1.0", Version: semver.MustParse("v1.1.0")}, }, }, } manager.sortVersionTags() expected := []string{ "v1.1.0", "v1.2.3", "v1.3.0-prerelease01", "v2.0.0", } actual := make([]string, len(manager.tagCache.VersionTags)) for i, tag := range manager.tagCache.VersionTags { actual[i] = tag.Raw } assert.Equal(t, expected, actual) } ================================================ FILE: cmd/tools/releaser/releaser.go ================================================ package main import ( "context" "fmt" "os" "os/signal" "github.com/urfave/cli/v2" "github.com/uber/cadence/cmd/tools/releaser/internal/console" "github.com/uber/cadence/cmd/tools/releaser/internal/fs" "github.com/uber/cadence/cmd/tools/releaser/internal/git" "github.com/uber/cadence/cmd/tools/releaser/internal/release" ) func main() { cliApp := &cli.App{ Name: "releaser", Usage: "Cadence workflow release management tool", Version: "0.1.0", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "verbose", Aliases: []string{"i"}, Usage: "Enable verbose output", }, &cli.BoolFlag{ Name: "yes", Usage: "Skip confirmation prompts", }, &cli.StringFlag{ Name: "set-version", Aliases: []string{"s"}, Usage: "Override automatic version calculation with specific version", }, }, Commands: []*cli.Command{ { Name: "release", Usage: "Promote latest prerelease to final release", Action: releaseCommand, Description: `Converts the current prerelease version to a final release. Example: v1.2.3-prerelease01 → v1.2.3 This command requires that the current version is a prerelease.`, }, { Name: "minor", Usage: "Start new minor version development cycle", Action: minorCommand, Description: `Increments the minor version and creates the first prerelease. Example: v1.2.3 → v1.3.0-prerelease01 Use this when starting development of new features.`, }, { Name: "major", Usage: "Start new major version development cycle", Action: majorCommand, Description: `Increments the major version and creates the first prerelease. Example: v1.2.3 → v2.0.0-prerelease01 Use this when introducing breaking changes.`, }, { Name: "patch", Usage: "Start new patch version development cycle", Action: patchCommand, Description: `Increments the patch version and creates the first prerelease. Example: v1.2.3 → v1.2.4-prerelease01 Use this when starting hotfix or patch development.`, }, { Name: "prerelease", Usage: "Increment prerelease number", Action: prereleaseCommand, Description: `Increments the prerelease number for iterative development. Example: v1.2.3-prerelease01 → v1.2.3-prerelease02 Use this during active development within a version cycle.`, }, { Name: "status", Usage: "Show current repository release status", Action: statusCommand, Description: `Displays current branch, version, and module information. Use this to understand the current state before making releases.`, }, }, CustomAppHelpTemplate: `NAME: {{.Name}} - {{.Usage}} USAGE: {{.HelpName}} [global options] command [command options] VERSION: {{.Version}} GLOBAL OPTIONS: {{range .VisibleFlags}}{{.}} {{end}} COMMANDS: {{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}} EXAMPLES: # Check current status {{.HelpName}} status # Development workflow {{.HelpName}} minor # Start new minor version: v1.2.3 → v1.3.0-prerelease01 {{.HelpName}} major # Start new major version: v1.2.3 → v2.0.0-prerelease01 {{.HelpName}} patch # Start new patch version: v1.2.3 → v1.2.4-prerelease01 {{.HelpName}} prerelease # Iterate: v1.3.0-prerelease01 → v1.3.0-prerelease02 {{.HelpName}} release # Finalize: v1.3.0-prerelease03 → v1.3.0 # Major version workflow {{.HelpName}} major # Start major version: v1.3.0 → v2.0.0-prerelease01 {{.HelpName}} prerelease # Iterate: v2.0.0-prerelease01 → v2.0.0-prerelease02 {{.HelpName}} release # Finalize: v2.0.0-prerelease02 → v2.0.0 # Manual version override {{.HelpName}} release --set-version v1.4.0 # Override automatic calculation SAFETY FEATURES: - Validates current version state before operations - Requires clean git working directory - Enforces releases only from master branch - Prevents creating duplicate versions - Builds and tests before creating tags - Interactive confirmations for all operations Use --yes to skip confirmation prompts for automation. `, } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() if err := cliApp.RunContext(ctx, os.Args); err != nil { _, _ = fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } } func createManager(c *cli.Context, command string) (*release.Manager, error) { cfg := release.Config{ ExcludedDirs: []string{"cmd", "internal/tools", "idls"}, RequiredBranch: "master", Verbose: c.Bool("verbose"), Command: command, SkipConfirmations: c.Bool("yes"), ManualVersion: c.String("set-version"), } gitClient := git.NewGitClient(cfg.Verbose) repo := fs.NewFileSystemClient(cfg.Verbose) interaction := console.NewManager(os.Stdout, os.Stdin) manager := release.NewReleaseManager(cfg, gitClient, repo, interaction) // Get repo root and update cfg repoRoot, err := gitClient.GetRepoRoot(c.Context) if err != nil { return nil, fmt.Errorf("failed to get repository root: %w", err) } cfg.RepoRoot = repoRoot return manager, nil } func releaseCommand(c *cli.Context) error { manager, err := createManager(c, "release") if err != nil { return cli.Exit(err.Error(), 1) } if err := manager.RunRelease(c.Context); err != nil { if c.Context.Err() != nil { return nil } return cli.Exit(err.Error(), 1) } return nil } func minorCommand(c *cli.Context) error { manager, err := createManager(c, "minor") if err != nil { return cli.Exit(err.Error(), 1) } if err := manager.RunMinor(c.Context); err != nil { if c.Context.Err() != nil { return nil } return cli.Exit(err.Error(), 1) } return nil } func majorCommand(c *cli.Context) error { manager, err := createManager(c, "major") if err != nil { return cli.Exit(err.Error(), 1) } if err := manager.RunMajor(c.Context); err != nil { if c.Context.Err() != nil { return nil } return cli.Exit(err.Error(), 1) } return nil } func patchCommand(c *cli.Context) error { manager, err := createManager(c, "patch") if err != nil { return cli.Exit(err.Error(), 1) } if err := manager.RunPatch(c.Context); err != nil { if c.Context.Err() != nil { return nil } return cli.Exit(err.Error(), 1) } return nil } func prereleaseCommand(c *cli.Context) error { manager, err := createManager(c, "prerelease") if err != nil { return cli.Exit(err.Error(), 1) } if err := manager.RunPrerelease(c.Context); err != nil { if c.Context.Err() != nil { return nil } return cli.Exit(err.Error(), 1) } return nil } func statusCommand(c *cli.Context) error { manager, err := createManager(c, "status") if err != nil { return cli.Exit(err.Error(), 1) } if err := manager.ShowCurrentState(c.Context); err != nil { if c.Context.Err() != nil { return nil } return cli.Exit(err.Error(), 1) } return nil } ================================================ FILE: cmd/tools/sql/main.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "os" "github.com/uber/cadence/tools/common/commoncli" "github.com/uber/cadence/tools/sql" _ "github.com/uber/cadence/common/persistence/sql/sqlplugin/mysql" // needed to load mysql plugin _ "github.com/uber/cadence/common/persistence/sql/sqlplugin/postgres" // needed to load postgres plugin ) func main() { commoncli.ExitHandler(sql.RunTool(os.Args)) } ================================================ FILE: codecov.yml ================================================ # Refs: # - https://docs.codecov.com/docs/common-recipe-list # - https://docs.codecov.com/docs/codecovyml-reference # # After making changes, run below command to validate # curl --data-binary @codecov.yml https://codecov.io/validate coverage: range: 80...100 round: down precision: 2 status: project: # measuring the overall project coverage default: # context, you can create multiple ones with custom titles informational: true target: 85% # specify the target coverage for each commit status # option: "auto" (compare against parent commit or pull request base) # option: "X%" a static target percentage to hit threshold: 0% # allow the coverage drop by x% before marking as failure if_ci_failed: ignore # require the CI to pass before setting the status patch: default: target: 75% # specify the target coverage for each commit status # option: "auto" (compare against parent commit or pull request base) # option: "X%" a static target percentage to hit threshold: 0% # allow the coverage drop by x% before marking as failure comment: layout: "header, files, footer" hide_project_coverage: false codecov: require_ci_to_pass: false ignore: - "**/*_cql.go" - "**/*_generated.go" - "**/*_mock.go" - "**/*_test.go" - "**/*Test.go" - "**/*_test_utils.go" - "**/constants.go" - "**/interface.go" - "**/interfaces.go" - "**/main.go" - "**/*mocks.go" - "**/mocks/**" - "**/testdata/**" - "**/testing/**" - "**/types.go" - "**/version.go" - "bench/**" - "canary/**" - "cmd/**" - "common/persistence/persistence-tests/**" - "common/domain/errors.go" - "common/log/**" - "common/metrics/**" - "common/persistence/nosql/nosqlplugin/cassandra/admin.go" - "common/persistence/nosql/nosqlplugin/dynamodb/**" - "common/persistence/nosql/nosqlplugin/mongodb/**" - "common/types/shared.go" # 8k lines of getters. Not worth testing manually but consider switching to generated code. - "host/**" - "idls/**" - "service/frontend/service.go" - "service/history/constants/test_constants.go" - "service/history/execution/mutable_state.go" - "service/history/shard/contextTest.go" - "service/history/workflow/errors.go" - "service/history/service.go" - "service/matching/service.go" - "service/matching/tasklist/testing.go" - "service/worker/service.go" - "simulation/**" - "testflags/**" - "tools/common/schema/test/**" - "tools/linter/**" - "tools/matchingsimulationcomparison/**" - "tools/cli/factory.go" ================================================ FILE: common/activecluster/execution_manager_provider_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/activecluster (interfaces: ExecutionManagerProvider) // // Generated by this command: // // mockgen -package activecluster -destination execution_manager_provider_mock.go -self_package github.com/uber/cadence/common/activecluster github.com/uber/cadence/common/activecluster ExecutionManagerProvider // // Package activecluster is a generated GoMock package. package activecluster import ( reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" ) // MockExecutionManagerProvider is a mock of ExecutionManagerProvider interface. type MockExecutionManagerProvider struct { ctrl *gomock.Controller recorder *MockExecutionManagerProviderMockRecorder isgomock struct{} } // MockExecutionManagerProviderMockRecorder is the mock recorder for MockExecutionManagerProvider. type MockExecutionManagerProviderMockRecorder struct { mock *MockExecutionManagerProvider } // NewMockExecutionManagerProvider creates a new mock instance. func NewMockExecutionManagerProvider(ctrl *gomock.Controller) *MockExecutionManagerProvider { mock := &MockExecutionManagerProvider{ctrl: ctrl} mock.recorder = &MockExecutionManagerProviderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockExecutionManagerProvider) EXPECT() *MockExecutionManagerProviderMockRecorder { return m.recorder } // GetExecutionManager mocks base method. func (m *MockExecutionManagerProvider) GetExecutionManager(shardID int) (persistence.ExecutionManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetExecutionManager", shardID) ret0, _ := ret[0].(persistence.ExecutionManager) ret1, _ := ret[1].(error) return ret0, ret1 } // GetExecutionManager indicates an expected call of GetExecutionManager. func (mr *MockExecutionManagerProviderMockRecorder) GetExecutionManager(shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExecutionManager", reflect.TypeOf((*MockExecutionManagerProvider)(nil).GetExecutionManager), shardID) } ================================================ FILE: common/activecluster/manager.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package activecluster import ( "context" "errors" "fmt" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type DomainIDToDomainFn func(id string) (*cache.DomainCacheEntry, error) const ( LookupNewWorkflowOpName = "LookupNewWorkflow" LookupWorkflowOpName = "LookupWorkflow" GetActiveClusterInfoByClusterAttributeOpName = "GetActiveClusterInfoByClusterAttribute" GetActiveClusterInfoByWorkflowOpName = "GetActiveClusterInfoByWorkflow" GetActiveClusterSelectionPolicyForWorkflowOpName = "GetActiveClusterSelectionPolicyForWorkflow" GetActiveClusterSelectionPolicyForCurrentWorkflowOpName = "GetActiveClusterSelectionPolicyForCurrentWorkflow" DomainIDToDomainFnErrorReason = "domain_id_to_name_fn_error" workflowPolicyCacheTTL = 10 * time.Second workflowPolicyCacheMaxCount = 1000 ) type managerImpl struct { domainIDToDomainFn DomainIDToDomainFn metricsCl metrics.Client logger log.Logger executionManagerProvider ExecutionManagerProvider numShards int workflowPolicyCache cache.Cache } type ManagerOption func(*managerImpl) func NewManager( domainIDToDomainFn DomainIDToDomainFn, metricsCl metrics.Client, logger log.Logger, executionManagerProvider ExecutionManagerProvider, numShards int, opts ...ManagerOption, ) (Manager, error) { m := &managerImpl{ domainIDToDomainFn: domainIDToDomainFn, metricsCl: metricsCl, logger: logger.WithTags(tag.ComponentActiveClusterManager), executionManagerProvider: executionManagerProvider, numShards: numShards, workflowPolicyCache: cache.New(&cache.Options{ TTL: workflowPolicyCacheTTL, MaxCount: workflowPolicyCacheMaxCount, ActivelyEvict: true, Pin: false, MetricsScope: metricsCl.Scope(metrics.ActiveClusterManagerWorkflowCacheScope), Logger: logger, }), } for _, opt := range opts { opt(m) } return m, nil } func (m *managerImpl) getClusterSelectionPolicy(ctx context.Context, domainID, wfID, rID string) (*types.ActiveClusterSelectionPolicy, error) { shardID := common.WorkflowIDToHistoryShard(wfID, m.numShards) executionManager, err := m.executionManagerProvider.GetExecutionManager(shardID) if err != nil { return nil, err } if rID == "" { execution, err := executionManager.GetCurrentExecution(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: wfID, }) if err != nil { return nil, err } rID = execution.RunID } // Check if the policy is already in the cache. create a key from domainID, wfID, rID key := fmt.Sprintf("%s:%s:%s", domainID, wfID, rID) cacheData := m.workflowPolicyCache.Get(key) if cacheData != nil { plcy, ok := cacheData.(*types.ActiveClusterSelectionPolicy) if ok { return plcy, nil } // This should never happen. If it does, we ignore cache value and get it from DB. m.logger.Warn(fmt.Sprintf("Cache data for key %s is of type %T, not a *types.ActiveClusterSelectionPolicy", key, cacheData)) } plcy, err := executionManager.GetActiveClusterSelectionPolicy(ctx, domainID, wfID, rID) if err != nil { return nil, err } if plcy == nil { return nil, &types.EntityNotExistsError{ Message: "active cluster selection policy not found", } } m.workflowPolicyCache.Put(key, plcy) return plcy, nil } func (m *managerImpl) getDomainAndScope(domainID, fn string) (*cache.DomainCacheEntry, metrics.Scope, error) { scope := m.metricsCl.Scope(metrics.ActiveClusterManager).Tagged(metrics.ActiveClusterLookupFnTag(fn)) d, err := m.domainIDToDomainFn(domainID) if err != nil { scope = scope.Tagged(metrics.ReasonTag(DomainIDToDomainFnErrorReason)) scope.IncCounter(metrics.ActiveClusterManagerLookupFailureCount) return nil, nil, err } scope = scope.Tagged(metrics.DomainTag(d.GetInfo().Name)) scope = scope.Tagged(metrics.IsActiveActiveDomainTag(d.GetReplicationConfig().IsActiveActive())) scope.IncCounter(metrics.ActiveClusterManagerLookupRequestCount) return d, scope, nil } func (m *managerImpl) handleError(scope metrics.Scope, err *error, start time.Time) { if err != nil && *err != nil { scope.IncCounter(metrics.ActiveClusterManagerLookupFailureCount) } else { scope.IncCounter(metrics.ActiveClusterManagerLookupSuccessCount) } scope.RecordHistogramDuration(metrics.ActiveClusterManagerLookupLatency, time.Since(start)) } func (m *managerImpl) GetActiveClusterInfoByClusterAttribute(ctx context.Context, domainID string, clusterAttribute *types.ClusterAttribute) (res *types.ActiveClusterInfo, e error) { defer func() { logFn := m.logger.Debug if e != nil { logFn = m.logger.Warn } logFn("GetActiveClusterInfoByClusterAttribute", tag.WorkflowDomainID(domainID), tag.Dynamic("clusterAttribute", clusterAttribute), tag.Dynamic("result", res), tag.Error(e), ) }() d, scope, err := m.getDomainAndScope(domainID, GetActiveClusterInfoByClusterAttributeOpName) if err != nil { return nil, err } defer m.handleError(scope, &e, time.Now()) res, ok := d.GetActiveClusterInfoByClusterAttribute(clusterAttribute) if !ok { return nil, &ClusterAttributeNotFoundError{ DomainID: domainID, ClusterAttribute: clusterAttribute, } } return res, nil } func (m *managerImpl) GetActiveClusterInfoByWorkflow(ctx context.Context, domainID, wfID, rID string) (res *types.ActiveClusterInfo, e error) { d, scope, err := m.getDomainAndScope(domainID, GetActiveClusterInfoByWorkflowOpName) if err != nil { return nil, err } defer m.handleError(scope, &e, time.Now()) if !d.GetReplicationConfig().IsActiveActive() { // Not an active-active domain. return ActiveClusterName from domain entry m.logger.Debug("GetActiveClusterInfoByWorkflow: not an active-active domain. returning ActiveClusterName from domain entry", tag.WorkflowDomainID(domainID), tag.WorkflowID(wfID), tag.WorkflowRunID(rID), tag.ActiveClusterName(d.GetReplicationConfig().ActiveClusterName), tag.FailoverVersion(d.GetFailoverVersion()), ) return &types.ActiveClusterInfo{ ActiveClusterName: d.GetReplicationConfig().ActiveClusterName, FailoverVersion: d.GetFailoverVersion(), }, nil } policy, err := m.getClusterSelectionPolicy(ctx, domainID, wfID, rID) if err != nil { var notExistsErr *types.EntityNotExistsError if !errors.As(err, ¬ExistsErr) { return nil, err } policy = &types.ActiveClusterSelectionPolicy{} } res, ok := d.GetActiveClusterInfoByClusterAttribute(policy.ClusterAttribute) if !ok { m.logger.Debug("GetActiveClusterInfoByWorkflow: cluster attribute not found", tag.WorkflowDomainID(domainID), tag.WorkflowID(wfID), tag.WorkflowRunID(rID), tag.ActiveClusterName(d.GetReplicationConfig().ActiveClusterName), tag.FailoverVersion(d.GetFailoverVersion()), ) return nil, &ClusterAttributeNotFoundError{ DomainID: domainID, ClusterAttribute: policy.ClusterAttribute, } } m.logger.Debug("GetActiveClusterInfoByWorkflow: returning active cluster info", tag.WorkflowDomainID(domainID), tag.WorkflowID(wfID), tag.WorkflowRunID(rID), tag.ActiveClusterName(res.ActiveClusterName), tag.FailoverVersion(res.FailoverVersion), ) return res, nil } func (m *managerImpl) GetActiveClusterSelectionPolicyForWorkflow(ctx context.Context, domainID, wfID, rID string) (res *types.ActiveClusterSelectionPolicy, e error) { d, scope, err := m.getDomainAndScope(domainID, GetActiveClusterSelectionPolicyForWorkflowOpName) if err != nil { return nil, err } defer m.handleError(scope, &e, time.Now()) if !d.GetReplicationConfig().IsActiveActive() { // Not an active-active domain. return ActiveClusterName from domain entry m.logger.Debug("GetActiveClusterSelectionPolicyForWorkflow: not an active-active domain. returning ActiveClusterName from domain entry", tag.WorkflowDomainID(domainID), tag.WorkflowID(wfID), tag.WorkflowRunID(rID), tag.ActiveClusterName(d.GetReplicationConfig().ActiveClusterName), tag.FailoverVersion(d.GetFailoverVersion()), ) return nil, nil } plcy, err := m.getClusterSelectionPolicy(ctx, domainID, wfID, rID) if err != nil { var notExistsErr *types.EntityNotExistsError if !errors.As(err, ¬ExistsErr) { return nil, err } return nil, nil } return plcy, nil } func (m *managerImpl) GetActiveClusterSelectionPolicyForCurrentWorkflow(ctx context.Context, domainID, wfID string) (res *types.ActiveClusterSelectionPolicy, running bool, e error) { d, scope, err := m.getDomainAndScope(domainID, GetActiveClusterSelectionPolicyForCurrentWorkflowOpName) if err != nil { return nil, false, err } defer m.handleError(scope, &e, time.Now()) if !d.GetReplicationConfig().IsActiveActive() { // Not an active-active domain. return nil m.logger.Debug("GetActiveClusterSelectionPolicyForCurrentWorkflow: not an active-active domain. returning nil", tag.WorkflowDomainID(domainID), tag.WorkflowID(wfID), ) return nil, false, nil } shardID := common.WorkflowIDToHistoryShard(wfID, m.numShards) executionManager, err := m.executionManagerProvider.GetExecutionManager(shardID) if err != nil { return nil, false, err } execution, err := executionManager.GetCurrentExecution(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: wfID, }) if err != nil { return nil, false, err } if persistence.IsWorkflowRunning(execution.State) { policy, err := m.getClusterSelectionPolicy(ctx, domainID, wfID, execution.RunID) if err != nil { return nil, false, err } return policy, true, nil } return nil, false, nil } ================================================ FILE: common/activecluster/manager_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/activecluster (interfaces: Manager) // // Generated by this command: // // mockgen -package activecluster -destination manager_mock.go -self_package github.com/uber/cadence/common/activecluster github.com/uber/cadence/common/activecluster Manager // // Package activecluster is a generated GoMock package. package activecluster import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller recorder *MockManagerMockRecorder isgomock struct{} } // MockManagerMockRecorder is the mock recorder for MockManager. type MockManagerMockRecorder struct { mock *MockManager } // NewMockManager creates a new mock instance. func NewMockManager(ctrl *gomock.Controller) *MockManager { mock := &MockManager{ctrl: ctrl} mock.recorder = &MockManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockManager) EXPECT() *MockManagerMockRecorder { return m.recorder } // GetActiveClusterInfoByClusterAttribute mocks base method. func (m *MockManager) GetActiveClusterInfoByClusterAttribute(ctx context.Context, domainID string, clusterAttribute *types.ClusterAttribute) (*types.ActiveClusterInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveClusterInfoByClusterAttribute", ctx, domainID, clusterAttribute) ret0, _ := ret[0].(*types.ActiveClusterInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // GetActiveClusterInfoByClusterAttribute indicates an expected call of GetActiveClusterInfoByClusterAttribute. func (mr *MockManagerMockRecorder) GetActiveClusterInfoByClusterAttribute(ctx, domainID, clusterAttribute any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterInfoByClusterAttribute", reflect.TypeOf((*MockManager)(nil).GetActiveClusterInfoByClusterAttribute), ctx, domainID, clusterAttribute) } // GetActiveClusterInfoByWorkflow mocks base method. func (m *MockManager) GetActiveClusterInfoByWorkflow(ctx context.Context, domainID, wfID, rID string) (*types.ActiveClusterInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveClusterInfoByWorkflow", ctx, domainID, wfID, rID) ret0, _ := ret[0].(*types.ActiveClusterInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // GetActiveClusterInfoByWorkflow indicates an expected call of GetActiveClusterInfoByWorkflow. func (mr *MockManagerMockRecorder) GetActiveClusterInfoByWorkflow(ctx, domainID, wfID, rID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterInfoByWorkflow", reflect.TypeOf((*MockManager)(nil).GetActiveClusterInfoByWorkflow), ctx, domainID, wfID, rID) } // GetActiveClusterSelectionPolicyForCurrentWorkflow mocks base method. func (m *MockManager) GetActiveClusterSelectionPolicyForCurrentWorkflow(ctx context.Context, domainID, wfID string) (*types.ActiveClusterSelectionPolicy, bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveClusterSelectionPolicyForCurrentWorkflow", ctx, domainID, wfID) ret0, _ := ret[0].(*types.ActiveClusterSelectionPolicy) ret1, _ := ret[1].(bool) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // GetActiveClusterSelectionPolicyForCurrentWorkflow indicates an expected call of GetActiveClusterSelectionPolicyForCurrentWorkflow. func (mr *MockManagerMockRecorder) GetActiveClusterSelectionPolicyForCurrentWorkflow(ctx, domainID, wfID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterSelectionPolicyForCurrentWorkflow", reflect.TypeOf((*MockManager)(nil).GetActiveClusterSelectionPolicyForCurrentWorkflow), ctx, domainID, wfID) } // GetActiveClusterSelectionPolicyForWorkflow mocks base method. func (m *MockManager) GetActiveClusterSelectionPolicyForWorkflow(ctx context.Context, domainID, wfID, rID string) (*types.ActiveClusterSelectionPolicy, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveClusterSelectionPolicyForWorkflow", ctx, domainID, wfID, rID) ret0, _ := ret[0].(*types.ActiveClusterSelectionPolicy) ret1, _ := ret[1].(error) return ret0, ret1 } // GetActiveClusterSelectionPolicyForWorkflow indicates an expected call of GetActiveClusterSelectionPolicyForWorkflow. func (mr *MockManagerMockRecorder) GetActiveClusterSelectionPolicyForWorkflow(ctx, domainID, wfID, rID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterSelectionPolicyForWorkflow", reflect.TypeOf((*MockManager)(nil).GetActiveClusterSelectionPolicyForWorkflow), ctx, domainID, wfID, rID) } ================================================ FILE: common/activecluster/manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package activecluster import ( "context" "errors" "fmt" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const ( numShards = 10 ) func TestGetActiveClusterInfoByClusterAttribute(t *testing.T) { metricsCl := metrics.NewNoopMetricsClient() logger := log.NewNoop() tests := []struct { name string clusterAttribute *types.ClusterAttribute activeClusterCfg *types.ActiveClusters domainIDToNameErr error expectedResult *types.ActiveClusterInfo expectedError string }{ { name: "nil cluster attribute - returns domain-level active cluster info", clusterAttribute: nil, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 20, }, }, }, }, }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster0", FailoverVersion: 201, // domain failover version }, }, { name: "domain ID to name function returns error", clusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, domainIDToNameErr: errors.New("failed to find domain by id"), expectedError: "failed to find domain by id", }, { name: "nil active clusters - returns ClusterAttributeNotFoundError", clusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, activeClusterCfg: nil, expectedError: "could not find cluster attribute &{region us-west} in the domain test-domain-id's active cluster config", }, { name: "nil attribute scopes - returns ClusterAttributeNotFoundError", clusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: nil, }, expectedError: "could not find cluster attribute &{region us-west} in the domain test-domain-id's active cluster config", }, { name: "scope not found - returns ClusterAttributeNotFoundError", clusterAttribute: &types.ClusterAttribute{ Scope: "datacenter", Name: "dc1", }, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, expectedError: "could not find cluster attribute &{datacenter dc1} in the domain test-domain-id's active cluster config", }, { name: "attribute name not found in scope - returns ClusterAttributeNotFoundError", clusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-central", }, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, "us-east": { ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, }, }, }, expectedError: "could not find cluster attribute &{region us-central} in the domain test-domain-id's active cluster config", }, { name: "successful lookup - returns active cluster info for region attribute", clusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, "us-east": { ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, }, }, }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, { name: "successful lookup - returns active cluster info for custom scope", clusterAttribute: &types.ClusterAttribute{ Scope: "datacenter", Name: "dc1", }, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: "cluster0", FailoverVersion: 150, }, "dc2": { ActiveClusterName: "cluster1", FailoverVersion: 250, }, }, }, }, }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster0", FailoverVersion: 150, }, }, { name: "successful lookup - multiple scopes with different attributes", clusterAttribute: &types.ClusterAttribute{ Scope: "city", Name: "seattle", }, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, "city": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "seattle": { ActiveClusterName: "cluster1", FailoverVersion: 300, }, "san_francisco": { ActiveClusterName: "cluster0", FailoverVersion: 350, }, }, }, }, }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster1", FailoverVersion: 300, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { domainIDToDomainFn := func(id string) (*cache.DomainCacheEntry, error) { return getDomainCacheEntryWithAttributeScopes(tc.activeClusterCfg), tc.domainIDToNameErr } mgr, err := NewManager( domainIDToDomainFn, metricsCl, logger, nil, numShards, ) assert.NoError(t, err) result, err := mgr.GetActiveClusterInfoByClusterAttribute(context.Background(), "test-domain-id", tc.clusterAttribute) if tc.expectedError != "" { assert.EqualError(t, err, tc.expectedError) assert.Nil(t, result) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedResult, result) } }) } } func getDomainCacheEntry(cfg *types.ActiveClusters, migratedFromActivePassive bool) *cache.DomainCacheEntry { // only thing we care in domain cache entry is the active clusters config // for domains migrated from active-passive to active-active, we set the failover version to 201 activeClusterName := "" if migratedFromActivePassive || cfg == nil { activeClusterName = "cluster0" } return cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ Name: "test-domain-id", }, nil, true, &persistence.DomainReplicationConfig{ ActiveClusters: cfg, ActiveClusterName: activeClusterName, }, 201, nil, 1, 1, 1, ) } func TestGetActiveClusterInfoByWorkflow(t *testing.T) { metricsCl := metrics.NewNoopMetricsClient() logger := log.NewNoop() tests := []struct { name string runID string activeClusterCfg *types.ActiveClusters domainIDToNameErr error mockExecutionManagerFn func(em *persistence.MockExecutionManager) mockExecutionManagerProviderFn func(emp *MockExecutionManagerProvider) cachedPolicy *types.ActiveClusterSelectionPolicy expectedResult *types.ActiveClusterInfo expectedError string }{ { name: "domain ID to name function returns error", runID: "test-run-id", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, domainIDToNameErr: errors.New("failed to find domain by id"), expectedError: "failed to find domain by id", }, { name: "execution manager provider returns error", runID: "test-run-id", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerProviderFn: func(emp *MockExecutionManagerProvider) { emp.EXPECT().GetExecutionManager(6).Return(nil, errors.New("failed to get execution manager")).AnyTimes() }, expectedError: "failed to get execution manager", }, { name: "execution manager GetActiveClusterSelectionPolicy returns error", runID: "test-run-id", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(nil, errors.New("database error")) }, expectedError: "database error", }, { name: "policy not found (EntityNotExistsError) - uses empty policy with nil cluster attribute", runID: "test-run-id", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(nil, &types.EntityNotExistsError{Message: "policy not found"}) }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster0", // domain-level active cluster FailoverVersion: 201, // domain failover version }, }, { name: "policy not found (EntityNotExistsError) - empty policy with cluster attribute not found", runID: "test-run-id", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(nil, &types.EntityNotExistsError{Message: "policy not found"}) }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster0", // domain-level active cluster FailoverVersion: 201, // domain failover version }, }, { name: "policy found but cluster attribute not found in domain config", runID: "test-run-id", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(&types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "datacenter", Name: "dc1", }, }, nil) }, expectedError: "could not find cluster attribute &{datacenter dc1} in the domain test-domain-id's active cluster config", }, { name: "successful lookup - policy found and cluster attribute exists", runID: "test-run-id", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, "us-east": { ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(&types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, }, nil) }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, { name: "successful lookup - policy from cache", runID: "test-run-id", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: "cluster0", FailoverVersion: 150, }, "dc2": { ActiveClusterName: "cluster1", FailoverVersion: 250, }, }, }, }, }, cachedPolicy: &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "datacenter", Name: "dc2", }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { // No expectations needed since policy comes from cache, but we need the provider to be non-nil }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster1", FailoverVersion: 250, }, }, { name: "successful lookup - nil cluster attribute in policy uses domain-level info", runID: "test-run-id", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(&types.ActiveClusterSelectionPolicy{ ClusterAttribute: nil, // nil cluster attribute }, nil) }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster0", // domain-level active cluster FailoverVersion: 201, // domain failover version }, }, { name: "successful lookup - multiple scopes with different attributes", runID: "test-run-id", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, "city": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "seattle": { ActiveClusterName: "cluster1", FailoverVersion: 300, }, "san_francisco": { ActiveClusterName: "cluster0", FailoverVersion: 350, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(&types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "city", Name: "seattle", }, }, nil) }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster1", FailoverVersion: 300, }, }, { name: "successful lookup - empty runID triggers GetCurrentExecution", runID: "", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, "us-east": { ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { // Expect GetCurrentExecution to be called first to get the runID em.EXPECT().GetCurrentExecution(gomock.Any(), &persistence.GetCurrentExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }).Return(&persistence.GetCurrentExecutionResponse{ RunID: "current-run-id", }, nil) // Then expect GetActiveClusterSelectionPolicy with the returned runID em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "current-run-id"). Return(&types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, }, nil) }, expectedResult: &types.ActiveClusterInfo{ ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, { name: "empty runID - GetCurrentExecution returns error", runID: "", activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { // GetCurrentExecution returns an error em.EXPECT().GetCurrentExecution(gomock.Any(), &persistence.GetCurrentExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }).Return(nil, errors.New("workflow not found")) }, expectedError: "workflow not found", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { domainIDToDomainFn := func(id string) (*cache.DomainCacheEntry, error) { return getDomainCacheEntryWithAttributeScopes(tc.activeClusterCfg), tc.domainIDToNameErr } ctrl := gomock.NewController(t) wfID := "test-workflow-id" shardID := 6 // corresponds to wfID given numShards var emProvider *MockExecutionManagerProvider if tc.mockExecutionManagerProviderFn != nil || tc.mockExecutionManagerFn != nil { emProvider = NewMockExecutionManagerProvider(ctrl) if tc.mockExecutionManagerProviderFn != nil { tc.mockExecutionManagerProviderFn(emProvider) } else { // Default case: create execution manager and set up provider em := persistence.NewMockExecutionManager(ctrl) if tc.mockExecutionManagerFn != nil { tc.mockExecutionManagerFn(em) } emProvider.EXPECT().GetExecutionManager(shardID).Return(em, nil).AnyTimes() } } mgr, err := NewManager( domainIDToDomainFn, metricsCl, logger, emProvider, numShards, ) assert.NoError(t, err) if tc.cachedPolicy != nil { key := fmt.Sprintf("%s:%s:%s", "test-domain-id", wfID, tc.runID) mgr.(*managerImpl).workflowPolicyCache.Put(key, tc.cachedPolicy) } result, err := mgr.GetActiveClusterInfoByWorkflow(context.Background(), "test-domain-id", wfID, tc.runID) if tc.expectedError != "" { assert.EqualError(t, err, tc.expectedError) assert.Nil(t, result) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedResult, result) } }) } } func getDomainCacheEntryWithAttributeScopes(cfg *types.ActiveClusters) *cache.DomainCacheEntry { // Helper function specifically for GetActiveClusterInfoByClusterAttribute tests // Always sets activeClusterName to "cluster0" for consistency return cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ Name: "test-domain-id", }, nil, true, &persistence.DomainReplicationConfig{ ActiveClusters: cfg, ActiveClusterName: "cluster0", }, 201, // domain failover version nil, 1, 1, 1, ) } func TestGetActiveClusterSelectionPolicyForCurrentWorkflow(t *testing.T) { metricsCl := metrics.NewNoopMetricsClient() logger := log.NewNoop() tests := []struct { name string activeClusterCfg *types.ActiveClusters isActiveActive bool domainIDToNameErr error mockExecutionManagerFn func(em *persistence.MockExecutionManager) mockExecutionManagerProviderFn func(emp *MockExecutionManagerProvider) cachedPolicy *types.ActiveClusterSelectionPolicy expectedPolicy *types.ActiveClusterSelectionPolicy expectedRunning bool expectedError string }{ { name: "domain ID to name function returns error", isActiveActive: true, domainIDToNameErr: errors.New("failed to find domain by id"), expectedError: "failed to find domain by id", expectedRunning: false, }, { name: "non-active-active domain returns nil", isActiveActive: false, expectedPolicy: nil, expectedRunning: false, }, { name: "execution manager provider returns error", isActiveActive: true, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerProviderFn: func(emp *MockExecutionManagerProvider) { emp.EXPECT().GetExecutionManager(6).Return(nil, errors.New("failed to get execution manager")).AnyTimes() }, expectedError: "failed to get execution manager", expectedRunning: false, }, { name: "GetCurrentExecution returns error", isActiveActive: true, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetCurrentExecution(gomock.Any(), &persistence.GetCurrentExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }).Return(nil, errors.New("workflow not found")) }, expectedError: "workflow not found", expectedRunning: false, }, { name: "workflow completed - returns nil, false", isActiveActive: true, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetCurrentExecution(gomock.Any(), &persistence.GetCurrentExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }).Return(&persistence.GetCurrentExecutionResponse{ RunID: "test-run-id", State: persistence.WorkflowStateCompleted, }, nil) }, expectedPolicy: nil, expectedRunning: false, }, { name: "workflow running - successfully returns policy", isActiveActive: true, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, "us-east": { ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetCurrentExecution(gomock.Any(), &persistence.GetCurrentExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }).Return(&persistence.GetCurrentExecutionResponse{ RunID: "test-run-id", State: persistence.WorkflowStateRunning, }, nil) em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(&types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, }, nil) }, expectedPolicy: &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, }, expectedRunning: true, }, { name: "workflow created state - successfully returns policy", isActiveActive: true, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: "cluster0", FailoverVersion: 150, }, "dc2": { ActiveClusterName: "cluster1", FailoverVersion: 250, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetCurrentExecution(gomock.Any(), &persistence.GetCurrentExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }).Return(&persistence.GetCurrentExecutionResponse{ RunID: "test-run-id", State: persistence.WorkflowStateCreated, }, nil) em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(&types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "datacenter", Name: "dc1", }, }, nil) }, expectedPolicy: &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "datacenter", Name: "dc1", }, }, expectedRunning: true, }, { name: "workflow running but GetActiveClusterSelectionPolicy fails", isActiveActive: true, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetCurrentExecution(gomock.Any(), &persistence.GetCurrentExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }).Return(&persistence.GetCurrentExecutionResponse{ RunID: "test-run-id", State: persistence.WorkflowStateRunning, }, nil) em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(nil, errors.New("database error")) }, expectedError: "database error", expectedRunning: false, }, { name: "workflow running - policy from cache", isActiveActive: true, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "city": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "seattle": { ActiveClusterName: "cluster1", FailoverVersion: 300, }, "san_francisco": { ActiveClusterName: "cluster0", FailoverVersion: 350, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetCurrentExecution(gomock.Any(), &persistence.GetCurrentExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }).Return(&persistence.GetCurrentExecutionResponse{ RunID: "test-run-id", State: persistence.WorkflowStateRunning, }, nil) // No expectation for GetActiveClusterSelectionPolicy since it comes from cache }, cachedPolicy: &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "city", Name: "seattle", }, }, expectedPolicy: &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "city", Name: "seattle", }, }, expectedRunning: true, }, { name: "workflow running with EntityNotExistsError returns error", isActiveActive: true, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetCurrentExecution(gomock.Any(), &persistence.GetCurrentExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }).Return(&persistence.GetCurrentExecutionResponse{ RunID: "test-run-id", State: persistence.WorkflowStateRunning, }, nil) em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(nil, &types.EntityNotExistsError{Message: "policy not found"}) }, expectedError: "policy not found", expectedRunning: false, }, { name: "workflow running with nil cluster attribute in policy", isActiveActive: true, activeClusterCfg: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, mockExecutionManagerFn: func(em *persistence.MockExecutionManager) { em.EXPECT().GetCurrentExecution(gomock.Any(), &persistence.GetCurrentExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }).Return(&persistence.GetCurrentExecutionResponse{ RunID: "test-run-id", State: persistence.WorkflowStateRunning, }, nil) em.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id"). Return(&types.ActiveClusterSelectionPolicy{ ClusterAttribute: nil, }, nil) }, expectedPolicy: &types.ActiveClusterSelectionPolicy{ ClusterAttribute: nil, }, expectedRunning: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { domainIDToDomainFn := func(id string) (*cache.DomainCacheEntry, error) { return getDomainCacheEntry(tc.activeClusterCfg, tc.isActiveActive), tc.domainIDToNameErr } ctrl := gomock.NewController(t) wfID := "test-workflow-id" shardID := 6 // corresponds to wfID given numShards var emProvider *MockExecutionManagerProvider if tc.mockExecutionManagerProviderFn != nil || tc.mockExecutionManagerFn != nil { emProvider = NewMockExecutionManagerProvider(ctrl) if tc.mockExecutionManagerProviderFn != nil { tc.mockExecutionManagerProviderFn(emProvider) } else { // Default case: create execution manager and set up provider em := persistence.NewMockExecutionManager(ctrl) if tc.mockExecutionManagerFn != nil { tc.mockExecutionManagerFn(em) } emProvider.EXPECT().GetExecutionManager(shardID).Return(em, nil).AnyTimes() } } mgr, err := NewManager( domainIDToDomainFn, metricsCl, logger, emProvider, numShards, ) assert.NoError(t, err) if tc.cachedPolicy != nil { // Need to get the runID from the test case to build the cache key // For cached policy tests, we assume the runID is "test-run-id" key := fmt.Sprintf("%s:%s:%s", "test-domain-id", wfID, "test-run-id") mgr.(*managerImpl).workflowPolicyCache.Put(key, tc.cachedPolicy) } policy, running, err := mgr.GetActiveClusterSelectionPolicyForCurrentWorkflow(context.Background(), "test-domain-id", wfID) if tc.expectedError != "" { assert.EqualError(t, err, tc.expectedError) assert.Nil(t, policy) assert.Equal(t, tc.expectedRunning, running) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedPolicy, policy) assert.Equal(t, tc.expectedRunning, running) } }) } } ================================================ FILE: common/activecluster/types.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package activecluster import ( "context" "fmt" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -destination manager_mock.go -self_package github.com/uber/cadence/common/activecluster github.com/uber/cadence/common/activecluster Manager //go:generate mockgen -package $GOPACKAGE -destination execution_manager_provider_mock.go -self_package github.com/uber/cadence/common/activecluster github.com/uber/cadence/common/activecluster ExecutionManagerProvider // Manager is the interface for active cluster manager. // It is used to get active cluster info by cluster attribute or workflow. type Manager interface { // GetActiveClusterInfoByClusterAttribute returns the active cluster info by cluster attribute // If clusterAttribute is nil, returns the domain-level active cluster info // If clusterAttribute is not nil and exists in the domain metadata, returns the active cluster info of the cluster attribute // If clusterAttribute is not nil and does not exist in the domain metadata, returns an error GetActiveClusterInfoByClusterAttribute(ctx context.Context, domainID string, clusterAttribute *types.ClusterAttribute) (*types.ActiveClusterInfo, error) // GetActiveClusterInfoByWorkflow returns the active cluster info by workflow // It will first look up the cluster selection policy for the workflow and then get the active cluster info by cluster attribute from the policy GetActiveClusterInfoByWorkflow(ctx context.Context, domainID, wfID, rID string) (*types.ActiveClusterInfo, error) // GetActiveClusterSelectionPolicyForWorkflow returns the active cluster selection policy for a workflow GetActiveClusterSelectionPolicyForWorkflow(ctx context.Context, domainID, wfID, rID string) (*types.ActiveClusterSelectionPolicy, error) // GetActiveClusterSelectionPolicyForCurrentWorkflow returns the active cluster selection policy for the current workflow // if the workflow is NOT closed, returns policy and true, otherwise returns nil and false GetActiveClusterSelectionPolicyForCurrentWorkflow(ctx context.Context, domainID, wfID string) (*types.ActiveClusterSelectionPolicy, bool, error) } type ExecutionManagerProvider interface { GetExecutionManager(shardID int) (persistence.ExecutionManager, error) } type ClusterAttributeNotFoundError struct { DomainID string ClusterAttribute *types.ClusterAttribute // ActiveClusterInfo *types.ActiveClusterInfo } func (e *ClusterAttributeNotFoundError) Error() string { return fmt.Sprintf("could not find cluster attribute %s in the domain %s's active cluster config", e.ClusterAttribute, e.DomainID) } ================================================ FILE: common/archiver/README.md ================================================ ## What This README explains how to add new Archiver implementations. ## Steps **Step 1: Create a new package for your implementation** Create a new directory in the `archiver` folder. The structure should look like the following: ``` ./common/archiver - filestore/ -- Filestore implementation - provider/ - provider.go -- Provider of archiver instances - yourImplementation/ - historyArchiver.go -- HistoryArchiver implementation - historyArchiver_test.go -- Unit tests for HistoryArchiver - visibilityArchiver.go -- VisibilityArchiver implementations - visibilityArchiver_test.go -- Unit tests for VisibilityArchiver ``` **Step 2: Implement the HistoryArchiver interface** ```go type HistoryArchiver interface { // Archive is used to archive a workflow history. When the context expires the method should stop trying to archive. // Implementors are free to archive however they want, including implementing retries of sub-operations. The URI defines // the resource that histories should be archived into. The implementor gets to determine how to interpret the URI. // The Archive method may or may not be automatically retried by the caller. The ArchiveOptions are used // to interact with these retries including giving the implementor the ability to cancel retries and record progress // between retry attempts. // This method will be invoked after a workflow passes its retention period. // It's possible that this method will be invoked for one workflow multiple times and potentially concurrently, // implementation should correctly handle the workflow not exist case and return nil error. Archive(context.Context, URI, *ArchiveHistoryRequest, ...ArchiveOption) error // Get is used to access an archived history. When context expires method should stop trying to fetch history. // The URI identifies the resource from which history should be accessed and it is up to the implementor to interpret this URI. // This method should thrift errors - see filestore as an example. Get(context.Context, URI, *GetHistoryRequest) (*GetHistoryResponse, error) // ValidateURI is used to define what a valid URI for an implementation is. ValidateURI(URI) error } ``` **Step 3: Implement the VisibilityArchiver interface** ```go type VisibilityArchiver interface { // Archive is used to archive one workflow visibility record. // Check the Archive() method of the HistoryArchiver interface in Step 2 for parameters' meaning and requirements. // The only difference is that the ArchiveOption parameter won't include an option for recording process. // Please make sure your implementation is lossless. If any in-memory batching mechanism is used, then those batched records will be lost during server restarts. // This method will be invoked when workflow closes. Note that because of conflict resolution, it is possible for a workflow to through the closing process multiple times, which means that this method can be invoked more than once after a workflow closes. Archive(context.Context, URI, *ArchiveVisibilityRequest, ...ArchiveOption) error // Query is used to retrieve archived visibility records. // Check the Get() method of the HistoryArchiver interface in Step 2 for parameters' meaning and requirements. // The request includes a string field called query, which describes what kind of visibility records should be returned. For example, it can be some SQL-like syntax query string. // Your implementation is responsible for parsing and validating the query, and also returning all visibility records that match the query. // Currently the maximum context timeout passed into the method is 3 minutes, so it's ok if this method takes a long time to run. Query(context.Context, URI, *QueryVisibilityRequest) (*QueryVisibilityResponse, error) // ValidateURI is used to define what a valid URI for an implementation is. ValidateURI(URI) error } ``` **Step 4: Update provider to provide access to your implementation** Modify the `./provider/provider.go` file so that the `ArchiverProvider` knows how to create an instance of your archiver. Also, add configs for you archiver to static yaml config files and modify the `HistoryArchiverProvider` and `VisibilityArchiverProvider` struct in the `../common/config.go` accordingly. ## FAQ **If my Archive method can automatically be retried by caller how can I record and access progress between retries?** ArchiverOptions is used to handle this. The following shows and example: ```go func (a *Archiver) Archive( ctx context.Context, URI string, request *ArchiveRequest, opts ...ArchiveOption, ) error { featureCatalog := GetFeatureCatalog(opts...) // this function is defined in options.go var progress progress // Check if the feature for recording progress is enabled. if featureCatalog.ProgressManager != nil { if err := featureCatalog.ProgressManager.LoadProgress(ctx, &prevProgress); err != nil { // log some error message and return error if needed. } } // Your archiver implementation... // Record current progress if featureCatalog.ProgressManager != nil { if err := featureCatalog.ProgressManager.RecordProgress(ctx, progress); err != nil { // log some error message and return error if needed. } } } ``` **If my Archive method encounters an error which is non-retryable how do I indicate that the caller should not retry?** ```go func (a *Archiver) Archive( ctx context.Context, URI string, request *ArchiveRequest, opts ...ArchiveOption, ) error { featureCatalog := GetFeatureCatalog(opts...) // this function is defined in options.go err := youArchiverImpl() if nonRetryableErr(err) { if featureCatalog.NonRetriableError != nil { return featureCatalog.NonRetriableError() // when the caller gets this error type back it will not retry anymore. } } } ``` **How does my history archiver implementation read history?** The `archiver` package provides a utility class called `HistoryIterator` which is a wrapper of `HistoryManager`. Its usage is simpler than the `HistoryManager` given in the `BootstrapContainer`, so archiver implementations can choose to use it when reading workflow histories. See the `historyIterator.go` file for more details. Sample usage can be found in the filestore historyArchiver implementation. **Should my archiver define all its own error types?** Each archiver is free to define and return any errors it wants. However many common errors which exist between archivers are already defined in `constants.go`. **Is there a generic query syntax for visibility archiver?** Currently no. But this is something we plan to do in the future. As for now, try to make your syntax similar to the one used by our advanced list workflow API. ================================================ FILE: common/archiver/URI.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import "net/url" type ( // URI identifies the archival resource to which records are written to and read from. URI interface { Scheme() string Path() string Hostname() string Port() string Username() string Password() string String() string Opaque() string Query() map[string][]string } uri struct { url *url.URL } ) // NewURI constructs a new archiver URI from string. func NewURI(s string) (URI, error) { url, err := url.ParseRequestURI(s) if err != nil { return nil, err } return &uri{url: url}, nil } func (u *uri) Scheme() string { return u.url.Scheme } func (u *uri) Path() string { return u.url.Path } func (u *uri) Hostname() string { return u.url.Hostname() } func (u *uri) Port() string { return u.url.Port() } func (u *uri) Username() string { if u.url.User == nil { return "" } return u.url.User.Username() } func (u *uri) Password() string { if u.url.User == nil { return "" } password, exist := u.url.User.Password() if !exist { return "" } return password } func (u *uri) Opaque() string { return u.url.Opaque } func (u *uri) Query() map[string][]string { return u.url.Query() } func (u *uri) String() string { return u.url.String() } ================================================ FILE: common/archiver/URI_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type ( URISuite struct { *require.Assertions suite.Suite } ) func TestURISuite(t *testing.T) { suite.Run(t, new(URISuite)) } func (s *URISuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *URISuite) TestURI() { testCases := []struct { URIString string valid bool scheme string path string hostname string port string username string password string opaque string query map[string][]string }{ { URIString: "", valid: false, }, { URIString: "some random string", valid: false, }, { URIString: "mailto:a@b.com", valid: true, scheme: "mailto", opaque: "a@b.com", }, { URIString: "test://", valid: true, scheme: "test", }, { URIString: "http://example.com/path", valid: true, scheme: "http", hostname: "example.com", path: "/path", }, { URIString: "http://example.com/path with space", valid: true, scheme: "http", hostname: "example.com", path: "/path with space", }, { URIString: "https://localhost:8080?key1=value1&key1=value2&key2=value3", valid: true, scheme: "https", hostname: "localhost", port: "8080", query: map[string][]string{ "key1": []string{"value1", "value2"}, "key2": []string{"value3"}, }, }, { URIString: "file:///absolute/path/to/dir", valid: true, scheme: "file", path: "/absolute/path/to/dir", }, { URIString: "test://person:password@host/path", valid: true, scheme: "test", hostname: "host", path: "/path", username: "person", password: "password", }, { URIString: "test:opaque?key1=value1&key1=value2&key2=value3", valid: true, scheme: "test", opaque: "opaque", query: map[string][]string{ "key1": []string{"value1", "value2"}, "key2": []string{"value3"}, }, }, } for _, tc := range testCases { URI, err := NewURI(tc.URIString) if !tc.valid { s.Error(err) continue } s.NoError(err) s.Equal(tc.scheme, URI.Scheme()) s.Equal(tc.path, URI.Path()) s.Equal(tc.hostname, URI.Hostname()) s.Equal(tc.port, URI.Port()) s.Equal(tc.username, URI.Username()) s.Equal(tc.password, URI.Password()) s.Equal(tc.opaque, URI.Opaque()) if tc.query != nil { s.Equal(tc.query, URI.Query()) } } } ================================================ FILE: common/archiver/archivalMetadata.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "fmt" "strings" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) type ( // ArchivalMetadata provides cluster level archival information ArchivalMetadata interface { GetHistoryConfig() ArchivalConfig GetVisibilityConfig() ArchivalConfig } // ArchivalConfig is an immutable representation of the archival configuration of the cluster // This config is determined at cluster startup time ArchivalConfig interface { ClusterConfiguredForArchival() bool GetClusterStatus() ArchivalStatus ReadEnabled() bool GetDomainDefaultStatus() types.ArchivalStatus GetDomainDefaultURI() string } archivalMetadata struct { historyConfig ArchivalConfig visibilityConfig ArchivalConfig } archivalConfig struct { staticClusterStatus ArchivalStatus dynamicClusterStatus dynamicproperties.StringPropertyFn staticEnableRead bool dynamicEnableRead dynamicproperties.BoolPropertyFn domainDefaultStatus types.ArchivalStatus domainDefaultURI string } // ArchivalStatus represents the archival status of the cluster ArchivalStatus int ) const ( // ArchivalDisabled means this cluster is not configured to handle archival ArchivalDisabled ArchivalStatus = iota // ArchivalPaused means this cluster is configured to handle archival but is currently not archiving // This state is not yet implemented, as of now ArchivalPaused is treated the same way as ArchivalDisabled ArchivalPaused // ArchivalEnabled means this cluster is currently archiving ArchivalEnabled ) // NewArchivalMetadata constructs a new ArchivalMetadata func NewArchivalMetadata( dc *dynamicconfig.Collection, historyStatus string, historyReadEnabled bool, visibilityStatus string, visibilityReadEnabled bool, domainDefaults *config.ArchivalDomainDefaults, ) ArchivalMetadata { historyConfig := NewArchivalConfig( historyStatus, dc.GetStringProperty(dynamicproperties.HistoryArchivalStatus), historyReadEnabled, dc.GetBoolProperty(dynamicproperties.EnableReadFromHistoryArchival), domainDefaults.History.Status, domainDefaults.History.URI, ) visibilityConfig := NewArchivalConfig( visibilityStatus, dc.GetStringProperty(dynamicproperties.VisibilityArchivalStatus), visibilityReadEnabled, dc.GetBoolProperty(dynamicproperties.EnableReadFromVisibilityArchival), domainDefaults.Visibility.Status, domainDefaults.Visibility.URI, ) return &archivalMetadata{ historyConfig: historyConfig, visibilityConfig: visibilityConfig, } } func (metadata *archivalMetadata) GetHistoryConfig() ArchivalConfig { return metadata.historyConfig } func (metadata *archivalMetadata) GetVisibilityConfig() ArchivalConfig { return metadata.visibilityConfig } // NewArchivalConfig constructs a new valid ArchivalConfig func NewArchivalConfig( staticClusterStatusStr string, dynamicClusterStatus dynamicproperties.StringPropertyFn, staticEnableRead bool, dynamicEnableRead dynamicproperties.BoolPropertyFn, domainDefaultStatusStr string, domainDefaultURI string, ) ArchivalConfig { staticClusterStatus, err := getClusterArchivalStatus(staticClusterStatusStr) if err != nil { panic(err) } domainDefaultStatus, err := getDomainArchivalStatus(domainDefaultStatusStr) if err != nil { panic(err) } return &archivalConfig{ staticClusterStatus: staticClusterStatus, dynamicClusterStatus: dynamicClusterStatus, staticEnableRead: staticEnableRead, dynamicEnableRead: dynamicEnableRead, domainDefaultStatus: domainDefaultStatus, domainDefaultURI: domainDefaultURI, } } // NewDisabledArchvialConfig returns a disabled ArchivalConfig func NewDisabledArchvialConfig() ArchivalConfig { return &archivalConfig{ staticClusterStatus: ArchivalDisabled, dynamicClusterStatus: nil, staticEnableRead: false, dynamicEnableRead: nil, domainDefaultStatus: types.ArchivalStatusDisabled, domainDefaultURI: "", } } // ClusterConfiguredForArchival returns true if cluster is configured to handle archival, false otherwise func (a *archivalConfig) ClusterConfiguredForArchival() bool { return a.GetClusterStatus() == ArchivalEnabled } func (a *archivalConfig) GetClusterStatus() ArchivalStatus { // Only check dynamic config when archival is enabled in static config. // If archival is disabled in static config, there will be no provider section in the static config // and the archiver provider can not create any archiver. Therefore, in that case, // even dynamic config says archival is enabled, we should ignore that. // Only when archival is enabled in static config, should we check if there's any difference between static config and dynamic config. if a.staticClusterStatus != ArchivalEnabled { return a.staticClusterStatus } dynamicStatusStr := a.dynamicClusterStatus() dynamicStatus, err := getClusterArchivalStatus(dynamicStatusStr) if err != nil { return ArchivalDisabled } return dynamicStatus } func (a *archivalConfig) ReadEnabled() bool { if !a.ClusterConfiguredForArchival() { return false } return a.staticEnableRead && a.dynamicEnableRead() } func (a *archivalConfig) GetDomainDefaultStatus() types.ArchivalStatus { return a.domainDefaultStatus } func (a *archivalConfig) GetDomainDefaultURI() string { return a.domainDefaultURI } func getClusterArchivalStatus(str string) (ArchivalStatus, error) { str = strings.TrimSpace(strings.ToLower(str)) switch str { case "", constants.ArchivalDisabled: return ArchivalDisabled, nil case constants.ArchivalPaused: return ArchivalPaused, nil case constants.ArchivalEnabled: return ArchivalEnabled, nil } return ArchivalDisabled, fmt.Errorf("invalid archival status of %v for cluster, valid status are: {\"\", \"disabled\", \"paused\", \"enabled\"}", str) } func getDomainArchivalStatus(str string) (types.ArchivalStatus, error) { str = strings.TrimSpace(strings.ToLower(str)) switch str { case "", constants.ArchivalDisabled: return types.ArchivalStatusDisabled, nil case constants.ArchivalEnabled: return types.ArchivalStatusEnabled, nil } return types.ArchivalStatusDisabled, fmt.Errorf("invalid archival status of %v for domain, valid status are: {\"\", \"disabled\", \"enabled\"}", str) } ================================================ FILE: common/archiver/archivalMetadata_mock.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import "github.com/stretchr/testify/mock" // MockArchivalMetadata is an autogenerated mock type for the ArchivalMetadata type type MockArchivalMetadata struct { mock.Mock } // GetHistoryConfig provides a mock function with given fields: func (_m *MockArchivalMetadata) GetHistoryConfig() ArchivalConfig { ret := _m.Called() var r0 ArchivalConfig if rf, ok := ret.Get(0).(func() ArchivalConfig); ok { r0 = rf() } else { if ret.Get(0) != nil { r0 = ret.Get(0).(ArchivalConfig) } } return r0 } // GetVisibilityConfig provides a mock function with given fields: func (_m *MockArchivalMetadata) GetVisibilityConfig() ArchivalConfig { ret := _m.Called() var r0 ArchivalConfig if rf, ok := ret.Get(0).(func() ArchivalConfig); ok { r0 = rf() } else { if ret.Get(0) != nil { r0 = ret.Get(0).(ArchivalConfig) } } return r0 } ================================================ FILE: common/archiver/constants.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import "errors" const ( // ArchiveNonRetriableErrorMsg is the log message when the Archive() method encounters a non-retriable error ArchiveNonRetriableErrorMsg = "Archive method encountered an non-retriable error." // ArchiveTransientErrorMsg is the log message when the Archive() method encounters a transient error ArchiveTransientErrorMsg = "Archive method encountered a transient error." // ArchiveSkippedInfoMsg is the log messsage when the Archive() method encounter an entity not exists error ArchiveSkippedInfoMsg = "Archive method encountered entity not exists error and skipped the archival" // ErrReasonInvalidURI is the error reason for invalid URI ErrReasonInvalidURI = "URI is invalid" // ErrReasonInvalidArchiveRequest is the error reason for invalid archive request ErrReasonInvalidArchiveRequest = "archive request is invalid" // ErrReasonConstructHistoryIterator is the error reason for failing to construct history iterator ErrReasonConstructHistoryIterator = "failed to construct history iterator" // ErrReasonReadHistory is the error reason for failing to read history ErrReasonReadHistory = "failed to read history batches" // ErrReasonHistoryMutated is the error reason for mutated history ErrReasonHistoryMutated = "history was mutated" ) var ( // ErrInvalidURI is the error for invalid URI ErrInvalidURI = errors.New("URI is invalid") // ErrURISchemeMismatch is the error for mismatch between URI scheme and archiver ErrURISchemeMismatch = errors.New("URI scheme does not match the archiver") // ErrHistoryMutated is the error for mutated history ErrHistoryMutated = errors.New("history was mutated") // ErrContextTimeout is the error for context timeout ErrContextTimeout = errors.New("archive aborted because context timed out") // ErrInvalidGetHistoryRequest is the error for invalid GetHistory request ErrInvalidGetHistoryRequest = errors.New("get archived history request is invalid") // ErrInvalidQueryVisibilityRequest is the error for invalid Query Visibility request ErrInvalidQueryVisibilityRequest = errors.New("query visiblity request is invalid") // ErrNextPageTokenCorrupted is the error for corrupted GetHistory token ErrNextPageTokenCorrupted = errors.New("next page token is corrupted") // ErrHistoryNotExist is the error for non-exist history ErrHistoryNotExist = errors.New("requested workflow history does not exist") ) ================================================ FILE: common/archiver/filestore/historyArchiver.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Filestore History Archiver will archive workflow histories to local disk. // Each Archive() request results in a file named in the format of // hash(domainID, workflowID, runID)_version.history being created in the specified // directory. Workflow histories stored in that file are encoded in JSON format. // The Get() method retrieves the archived histories from the directory specified in the // URI. It optionally takes in a NextPageToken which specifies the workflow close failover // version and the index of the first history batch that should be returned. Instead of // NextPageToken, caller can also provide a close failover version, in which case, Get() method // will return history batches starting from the beginning of that history version. If neither // of NextPageToken or close failover version is specified, the highest close failover version // will be picked. package filestore import ( "context" "errors" "os" "path" "strconv" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/util" ) const ( // URIScheme is the scheme for the filestore implementation URIScheme = "file" errEncodeHistory = "failed to encode history batches" errMakeDirectory = "failed to make directory" errWriteFile = "failed to write history to file" targetHistoryBlobSize = 2 * 1024 * 1024 // 2MB ) var ( errInvalidFileMode = errors.New("invalid file mode") errInvalidDirMode = errors.New("invalid directory mode") ) type ( historyArchiver struct { container *archiver.HistoryBootstrapContainer fileMode os.FileMode dirMode os.FileMode // only set in test code historyIterator archiver.HistoryIterator } getHistoryToken struct { CloseFailoverVersion int64 NextBatchIdx int } ) // NewHistoryArchiver creates a new archiver.HistoryArchiver based on filestore func NewHistoryArchiver( container *archiver.HistoryBootstrapContainer, config *config.FilestoreArchiver, ) (archiver.HistoryArchiver, error) { return newHistoryArchiver(container, config, nil) } func newHistoryArchiver( container *archiver.HistoryBootstrapContainer, config *config.FilestoreArchiver, historyIterator archiver.HistoryIterator, ) (*historyArchiver, error) { fileMode, err := strconv.ParseUint(config.FileMode, 0, 32) if err != nil { return nil, errInvalidFileMode } dirMode, err := strconv.ParseUint(config.DirMode, 0, 32) if err != nil { return nil, errInvalidDirMode } return &historyArchiver{ container: container, fileMode: os.FileMode(fileMode), dirMode: os.FileMode(dirMode), historyIterator: historyIterator, }, nil } func (h *historyArchiver) Archive( ctx context.Context, URI archiver.URI, request *archiver.ArchiveHistoryRequest, opts ...archiver.ArchiveOption, ) (err error) { featureCatalog := archiver.GetFeatureCatalog(opts...) defer func() { if err != nil && !persistence.IsTransientError(err) && featureCatalog.NonRetriableError != nil { err = featureCatalog.NonRetriableError() } }() logger := archiver.TagLoggerWithArchiveHistoryRequestAndURI(h.container.Logger, request, URI.String()) if err := h.ValidateURI(URI); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidURI), tag.Error(err)) return err } if err := archiver.ValidateHistoryArchiveRequest(request); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidArchiveRequest), tag.Error(err)) return err } historyIterator := h.historyIterator if historyIterator == nil { // will only be set by testing code historyIterator = archiver.NewHistoryIterator(ctx, request, h.container.HistoryV2Manager, targetHistoryBlobSize) } historyBatches := []*types.History{} for historyIterator.HasNext() { historyBlob, err := getNextHistoryBlob(ctx, historyIterator) if err != nil { if common.IsEntityNotExistsError(err) { // workflow history no longer exists, may due to duplicated archival signal // this may happen even in the middle of iterating history as two archival signals // can be processed concurrently. logger.Info(archiver.ArchiveSkippedInfoMsg) return nil } logger := logger.WithTags(tag.ArchivalArchiveFailReason(archiver.ErrReasonReadHistory), tag.Error(err)) if !persistence.IsTransientError(err) { logger.Error(archiver.ArchiveNonRetriableErrorMsg) } else { logger.Error(archiver.ArchiveTransientErrorMsg) } return err } if archiver.IsHistoryMutated(request, historyBlob.Body, *historyBlob.Header.IsLast, logger) { if !featureCatalog.ArchiveIncompleteHistory() { return archiver.ErrHistoryMutated } } historyBatches = append(historyBatches, historyBlob.Body...) } encodedHistoryBatches, err := encode(historyBatches) if err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(errEncodeHistory), tag.Error(err)) return err } dirPath := URI.Path() if err = util.MkdirAll(dirPath, h.dirMode); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(errMakeDirectory), tag.Error(err)) return err } filename := constructHistoryFilename(request.DomainID, request.WorkflowID, request.RunID, request.CloseFailoverVersion) if err := util.WriteFile(path.Join(dirPath, filename), encodedHistoryBatches, h.fileMode); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(errWriteFile), tag.Error(err)) return err } return nil } func (h *historyArchiver) Get( ctx context.Context, URI archiver.URI, request *archiver.GetHistoryRequest, ) (*archiver.GetHistoryResponse, error) { if err := h.ValidateURI(URI); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidURI.Error()} } if err := archiver.ValidateGetRequest(request); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidGetHistoryRequest.Error()} } dirPath := URI.Path() exists, err := util.DirectoryExists(dirPath) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } if !exists { return nil, &types.BadRequestError{Message: archiver.ErrHistoryNotExist.Error()} } var token *getHistoryToken if request.NextPageToken != nil { token, err = deserializeGetHistoryToken(request.NextPageToken) if err != nil { return nil, &types.BadRequestError{Message: archiver.ErrNextPageTokenCorrupted.Error()} } } else if request.CloseFailoverVersion != nil { token = &getHistoryToken{ CloseFailoverVersion: *request.CloseFailoverVersion, NextBatchIdx: 0, } } else { highestVersion, err := getHighestVersion(dirPath, request) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } token = &getHistoryToken{ CloseFailoverVersion: *highestVersion, NextBatchIdx: 0, } } filename := constructHistoryFilename(request.DomainID, request.WorkflowID, request.RunID, token.CloseFailoverVersion) filepath := path.Join(dirPath, filename) exists, err = util.FileExists(filepath) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } if !exists { return nil, &types.EntityNotExistsError{Message: archiver.ErrHistoryNotExist.Error()} } encodedHistoryBatches, err := util.ReadFile(filepath) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } historyBatches, err := decodeHistoryBatches(encodedHistoryBatches) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } historyBatches = historyBatches[token.NextBatchIdx:] response := &archiver.GetHistoryResponse{} numOfEvents := 0 numOfBatches := 0 for _, batch := range historyBatches { response.HistoryBatches = append(response.HistoryBatches, batch) numOfBatches++ numOfEvents += len(batch.Events) if numOfEvents >= request.PageSize { break } } if numOfBatches < len(historyBatches) { token.NextBatchIdx += numOfBatches nextToken, err := serializeToken(token) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } response.NextPageToken = nextToken } return response, nil } func (h *historyArchiver) ValidateURI(URI archiver.URI) error { if URI.Scheme() != URIScheme { return archiver.ErrURISchemeMismatch } return validateDirPath(URI.Path()) } func getNextHistoryBlob(ctx context.Context, historyIterator archiver.HistoryIterator) (*archiver.HistoryBlob, error) { historyBlob, err := historyIterator.Next() op := func(ctx context.Context) error { historyBlob, err = historyIterator.Next() return err } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(common.CreatePersistenceRetryPolicy()), backoff.WithRetryableError(persistence.IsTransientError), ) for err != nil { if contextExpired(ctx) { return nil, archiver.ErrContextTimeout } if !persistence.IsTransientError(err) { return nil, err } err = throttleRetry.Do(ctx, op) } return historyBlob, nil } func getHighestVersion(dirPath string, request *archiver.GetHistoryRequest) (*int64, error) { filenames, err := util.ListFilesByPrefix(dirPath, constructHistoryFilenamePrefix(request.DomainID, request.WorkflowID, request.RunID)) if err != nil { return nil, err } var highestVersion *int64 for _, filename := range filenames { version, err := extractCloseFailoverVersion(filename) if err != nil { continue } if highestVersion == nil || version > *highestVersion { highestVersion = &version } } if highestVersion == nil { return nil, archiver.ErrHistoryNotExist } return highestVersion, nil } ================================================ FILE: common/archiver/filestore/historyArchiver_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package filestore import ( "context" "errors" "io/ioutil" "os" "path" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/util" ) const ( testDomainID = "test-domain-id" testDomainName = "test-domain-name" testWorkflowID = "test-workflow-id" testRunID = "test-run-id" testNextEventID = 1800 testCloseFailoverVersion = 100 testPageSize = 100 testFileModeStr = "0666" testDirModeStr = "0766" ) var ( testBranchToken = []byte{1, 2, 3} ) type historyArchiverSuite struct { *require.Assertions suite.Suite container *archiver.HistoryBootstrapContainer testArchivalURI archiver.URI testGetDirectory string historyBatchesV1 []*types.History historyBatchesV100 []*types.History } func TestHistoryArchiverSuite(t *testing.T) { suite.Run(t, new(historyArchiverSuite)) } func (s *historyArchiverSuite) SetupSuite() { var err error s.testGetDirectory, err = ioutil.TempDir("", "TestGet") s.Require().NoError(err) s.setupHistoryDirectory() s.testArchivalURI, err = archiver.NewURI("file:///a/b/c") s.Require().NoError(err) } func (s *historyArchiverSuite) TearDownSuite() { os.RemoveAll(s.testGetDirectory) } func (s *historyArchiverSuite) SetupTest() { s.Assertions = require.New(s.T()) s.container = &archiver.HistoryBootstrapContainer{ Logger: testlogger.New(s.T()), } } func (s *historyArchiverSuite) TestValidateURI() { testCases := []struct { URI string expectedErr error }{ { URI: "wrongscheme:///a/b/c", expectedErr: archiver.ErrURISchemeMismatch, }, { URI: "file://", expectedErr: errEmptyDirectoryPath, }, { URI: "file:///a/b/c", expectedErr: nil, }, } historyArchiver := s.newTestHistoryArchiver(nil) for _, tc := range testCases { URI, err := archiver.NewURI(tc.URI) s.NoError(err) s.Equal(tc.expectedErr, historyArchiver.ValidateURI(URI)) } } func (s *historyArchiverSuite) TestArchive_Fail_InvalidURI() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } URI, err := archiver.NewURI("wrongscheme://") s.NoError(err) err = historyArchiver.Archive(context.Background(), URI, request) s.Error(err) } func (s *historyArchiverSuite) TestArchive_Fail_InvalidRequest() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: "", // an invalid request RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request) s.Error(err) } func (s *historyArchiverSuite) TestArchive_Fail_ErrorOnReadHistory() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, errors.New("some random error")), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request) s.Error(err) } func (s *historyArchiverSuite) TestArchive_Fail_TimeoutWhenReadingHistory() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, &types.ServiceBusyError{}), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err := historyArchiver.Archive(getCanceledContext(), s.testArchivalURI, request) s.Error(err) } func (s *historyArchiverSuite) TestArchive_Fail_HistoryMutated() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyBatches := []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion + 1, }, }, }, } historyBlob := &archiver.HistoryBlob{ Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(true), }, Body: historyBatches, } gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(historyBlob, nil), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request) s.Error(err) } func (s *historyArchiverSuite) TestArchive_Fail_NonRetriableErrorOption() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, errors.New("some random error")), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } nonRetryableErr := errors.New("some non-retryable error") err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request, archiver.GetNonRetriableErrorOption(nonRetryableErr)) s.Equal(nonRetryableErr, err) } func (s *historyArchiverSuite) TestArchive_Skip() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyBlob := &archiver.HistoryBlob{ Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(false), }, Body: []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, }, } gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(historyBlob, nil), historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, &types.EntityNotExistsError{Message: "workflow not found"}), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request) s.NoError(err) } func (s *historyArchiverSuite) TestArchive_Success() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyBatches := []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, { ID: constants.FirstEventID + 2, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, { Events: []*types.HistoryEvent{ { ID: testNextEventID - 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, } historyBlob := &archiver.HistoryBlob{ Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(true), }, Body: historyBatches, } gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(historyBlob, nil), historyIterator.EXPECT().HasNext().Return(false), ) dir, err := ioutil.TempDir("", "TestArchiveSingleRead") s.NoError(err) defer os.RemoveAll(dir) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } URI, err := archiver.NewURI("file://" + dir) s.NoError(err) err = historyArchiver.Archive(context.Background(), URI, request) s.NoError(err) expectedFilename := constructHistoryFilename(testDomainID, testWorkflowID, testRunID, testCloseFailoverVersion) s.assertFileExists(path.Join(dir, expectedFilename)) } func (s *historyArchiverSuite) TestGet_Fail_InvalidURI() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: 100, } URI, err := archiver.NewURI("wrongscheme://") s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.Nil(response) s.Error(err) } func (s *historyArchiverSuite) TestGet_Fail_InvalidRequest() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: 0, // pageSize should be greater than 0 } response, err := historyArchiver.Get(context.Background(), s.testArchivalURI, request) s.Nil(response) s.Error(err) s.IsType(&types.BadRequestError{}, err) } func (s *historyArchiverSuite) TestGet_Fail_DirectoryNotExist() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, } response, err := historyArchiver.Get(context.Background(), s.testArchivalURI, request) s.Nil(response) s.Error(err) s.IsType(&types.BadRequestError{}, err) } func (s *historyArchiverSuite) TestGet_Fail_InvalidToken() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, NextPageToken: []byte{'r', 'a', 'n', 'd', 'o', 'm'}, } URI, err := archiver.NewURI("file:///") s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.Nil(response) s.Error(err) s.IsType(&types.BadRequestError{}, err) } func (s *historyArchiverSuite) TestGet_Fail_FileNotExist() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, CloseFailoverVersion: common.Int64Ptr(testCloseFailoverVersion), } URI, err := archiver.NewURI("file:///") s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.Nil(response) s.Error(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *historyArchiverSuite) TestGet_Success_PickHighestVersion() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, } URI, err := archiver.NewURI("file://" + s.testGetDirectory) s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.NoError(err) s.Nil(response.NextPageToken) s.Equal(s.historyBatchesV100, response.HistoryBatches) } func (s *historyArchiverSuite) TestGet_Success_UseProvidedVersion() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, CloseFailoverVersion: common.Int64Ptr(1), } URI, err := archiver.NewURI("file://" + s.testGetDirectory) s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.NoError(err) s.Nil(response.NextPageToken) s.Equal(s.historyBatchesV1, response.HistoryBatches) } func (s *historyArchiverSuite) TestGet_Success_SmallPageSize() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: 1, CloseFailoverVersion: common.Int64Ptr(100), } combinedHistory := []*types.History{} URI, err := archiver.NewURI("file://" + s.testGetDirectory) s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.NotNil(response.NextPageToken) s.NotNil(response.HistoryBatches) s.Len(response.HistoryBatches, 1) combinedHistory = append(combinedHistory, response.HistoryBatches...) request.NextPageToken = response.NextPageToken response, err = historyArchiver.Get(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.Nil(response.NextPageToken) s.NotNil(response.HistoryBatches) s.Len(response.HistoryBatches, 1) combinedHistory = append(combinedHistory, response.HistoryBatches...) s.Equal(s.historyBatchesV100, combinedHistory) } func (s *historyArchiverSuite) TestArchiveAndGet() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyBlob := &archiver.HistoryBlob{ Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(true), }, Body: s.historyBatchesV100, } gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(historyBlob, nil), historyIterator.EXPECT().HasNext().Return(false), ) dir, err := ioutil.TempDir("", "TestArchiveAndGet") s.NoError(err) defer os.RemoveAll(dir) historyArchiver := s.newTestHistoryArchiver(historyIterator) archiveRequest := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } URI, err := archiver.NewURI("file://" + dir) s.NoError(err) err = historyArchiver.Archive(context.Background(), URI, archiveRequest) s.NoError(err) expectedFilename := constructHistoryFilename(testDomainID, testWorkflowID, testRunID, testCloseFailoverVersion) s.assertFileExists(path.Join(dir, expectedFilename)) getRequest := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, } response, err := historyArchiver.Get(context.Background(), URI, getRequest) s.NoError(err) s.NotNil(response) s.Nil(response.NextPageToken) s.Equal(s.historyBatchesV100, response.HistoryBatches) } func (s *historyArchiverSuite) newTestHistoryArchiver(historyIterator archiver.HistoryIterator) *historyArchiver { config := &config.FilestoreArchiver{ FileMode: testFileModeStr, DirMode: testDirModeStr, } archiver, err := newHistoryArchiver(s.container, config, historyIterator) s.NoError(err) return archiver } func (s *historyArchiverSuite) setupHistoryDirectory() { s.historyBatchesV1 = []*types.History{ { Events: []*types.HistoryEvent{ { ID: testNextEventID - 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: 1, }, }, }, } s.historyBatchesV100 = []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, { Events: []*types.HistoryEvent{ { ID: testNextEventID - 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, } s.writeHistoryBatchesForGetTest(s.historyBatchesV1, int64(1)) s.writeHistoryBatchesForGetTest(s.historyBatchesV100, testCloseFailoverVersion) } func (s *historyArchiverSuite) writeHistoryBatchesForGetTest(historyBatches []*types.History, version int64) { data, err := encode(historyBatches) s.Require().NoError(err) filename := constructHistoryFilename(testDomainID, testWorkflowID, testRunID, version) err = util.WriteFile(path.Join(s.testGetDirectory, filename), data, testFileMode) s.Require().NoError(err) } func (s *historyArchiverSuite) assertFileExists(filepath string) { exists, err := util.FileExists(filepath) s.NoError(err) s.True(exists) } func getCanceledContext() context.Context { ctx, cancel := context.WithCancel(context.Background()) cancel() return ctx } ================================================ FILE: common/archiver/filestore/queryParser.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source queryParser.go -destination queryParser_mock.go -mock_names Interface=MockQueryParser package filestore import ( "errors" "fmt" "strconv" "strings" "time" "github.com/xwb1989/sqlparser" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) type ( // QueryParser parses a limited SQL where clause into a struct QueryParser interface { Parse(query string) (*parsedQuery, error) } queryParser struct{} parsedQuery struct { earliestCloseTime int64 latestCloseTime int64 workflowID *string runID *string workflowTypeName *string closeStatus *types.WorkflowExecutionCloseStatus emptyResult bool } ) // All allowed fields for filtering const ( WorkflowID = "WorkflowID" RunID = "RunID" WorkflowType = "WorkflowType" CloseTime = "CloseTime" CloseStatus = "CloseStatus" ) const ( queryTemplate = "select * from dummy where %s" defaultDateTimeFormat = time.RFC3339 ) // NewQueryParser creates a new query parser for filestore func NewQueryParser() QueryParser { return &queryParser{} } func (p *queryParser) Parse(query string) (*parsedQuery, error) { stmt, err := sqlparser.Parse(fmt.Sprintf(queryTemplate, query)) if err != nil { return nil, err } whereExpr := stmt.(*sqlparser.Select).Where.Expr parsedQuery := &parsedQuery{ earliestCloseTime: 0, latestCloseTime: time.Now().UnixNano(), } if err := p.convertWhereExpr(whereExpr, parsedQuery); err != nil { return nil, err } return parsedQuery, nil } func (p *queryParser) convertWhereExpr(expr sqlparser.Expr, parsedQuery *parsedQuery) error { if expr == nil { return errors.New("where expression is nil") } switch expr := expr.(type) { case *sqlparser.ComparisonExpr: return p.convertComparisonExpr(expr, parsedQuery) case *sqlparser.AndExpr: return p.convertAndExpr(expr, parsedQuery) case *sqlparser.ParenExpr: return p.convertParenExpr(expr, parsedQuery) default: return errors.New("only comparison and \"and\" expression is supported") } } func (p *queryParser) convertParenExpr(parenExpr *sqlparser.ParenExpr, parsedQuery *parsedQuery) error { return p.convertWhereExpr(parenExpr.Expr, parsedQuery) } func (p *queryParser) convertAndExpr(andExpr *sqlparser.AndExpr, parsedQuery *parsedQuery) error { if err := p.convertWhereExpr(andExpr.Left, parsedQuery); err != nil { return err } return p.convertWhereExpr(andExpr.Right, parsedQuery) } func (p *queryParser) convertComparisonExpr(compExpr *sqlparser.ComparisonExpr, parsedQuery *parsedQuery) error { colName, ok := compExpr.Left.(*sqlparser.ColName) if !ok { return fmt.Errorf("invalid filter name: %s", sqlparser.String(compExpr.Left)) } colNameStr := sqlparser.String(colName) op := compExpr.Operator valExpr, ok := compExpr.Right.(*sqlparser.SQLVal) if !ok { return fmt.Errorf("invalid value: %s", sqlparser.String(compExpr.Right)) } valStr := sqlparser.String(valExpr) switch colNameStr { case WorkflowID: val, err := extractStringValue(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with file system", WorkflowID) } if parsedQuery.workflowID != nil && *parsedQuery.workflowID != val { parsedQuery.emptyResult = true return nil } parsedQuery.workflowID = common.StringPtr(val) case RunID: val, err := extractStringValue(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with file system", RunID) } if parsedQuery.runID != nil && *parsedQuery.runID != val { parsedQuery.emptyResult = true return nil } parsedQuery.runID = common.StringPtr(val) case WorkflowType: val, err := extractStringValue(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with file system", WorkflowType) } if parsedQuery.workflowTypeName != nil && *parsedQuery.workflowTypeName != val { parsedQuery.emptyResult = true return nil } parsedQuery.workflowTypeName = common.StringPtr(val) case CloseStatus: val, err := extractStringValue(valStr) if err != nil { // if failed to extract string value, it means user input close status as a number val = valStr } if op != "=" { return fmt.Errorf("only operator = is supported for %s with file system", CloseStatus) } status, err := convertStatusStr(val) if err != nil { return err } if parsedQuery.closeStatus != nil && *parsedQuery.closeStatus != status { parsedQuery.emptyResult = true return nil } parsedQuery.closeStatus = status.Ptr() case CloseTime: timestamp, err := convertToTimestamp(valStr) if err != nil { return err } return p.convertCloseTime(timestamp, op, parsedQuery) default: return fmt.Errorf("unknown filter name: %s", colNameStr) } return nil } func (p *queryParser) convertCloseTime(timestamp int64, op string, parsedQuery *parsedQuery) error { switch op { case "=": if err := p.convertCloseTime(timestamp, ">=", parsedQuery); err != nil { return err } if err := p.convertCloseTime(timestamp, "<=", parsedQuery); err != nil { return err } case "<": parsedQuery.latestCloseTime = min(parsedQuery.latestCloseTime, timestamp-1) case "<=": parsedQuery.latestCloseTime = min(parsedQuery.latestCloseTime, timestamp) case ">": parsedQuery.earliestCloseTime = max(parsedQuery.earliestCloseTime, timestamp+1) case ">=": parsedQuery.earliestCloseTime = max(parsedQuery.earliestCloseTime, timestamp) default: return fmt.Errorf("operator %s is not supported for close time", op) } return nil } func convertToTimestamp(timeStr string) (int64, error) { timestamp, err := strconv.ParseInt(timeStr, 10, 64) if err == nil { return timestamp, nil } timestampStr, err := extractStringValue(timeStr) if err != nil { return 0, err } parsedTime, err := time.Parse(defaultDateTimeFormat, timestampStr) if err != nil { return 0, err } return parsedTime.UnixNano(), nil } func convertStatusStr(statusStr string) (types.WorkflowExecutionCloseStatus, error) { statusStr = strings.ToLower(strings.TrimSpace(statusStr)) switch statusStr { case "completed", strconv.Itoa(int(types.WorkflowExecutionCloseStatusCompleted)): return types.WorkflowExecutionCloseStatusCompleted, nil case "failed", strconv.Itoa(int(types.WorkflowExecutionCloseStatusFailed)): return types.WorkflowExecutionCloseStatusFailed, nil case "canceled", strconv.Itoa(int(types.WorkflowExecutionCloseStatusCanceled)): return types.WorkflowExecutionCloseStatusCanceled, nil case "terminated", strconv.Itoa(int(types.WorkflowExecutionCloseStatusTerminated)): return types.WorkflowExecutionCloseStatusTerminated, nil case "continuedasnew", "continued_as_new", strconv.Itoa(int(types.WorkflowExecutionCloseStatusContinuedAsNew)): return types.WorkflowExecutionCloseStatusContinuedAsNew, nil case "timedout", "timed_out", strconv.Itoa(int(types.WorkflowExecutionCloseStatusTimedOut)): return types.WorkflowExecutionCloseStatusTimedOut, nil default: return 0, fmt.Errorf("unknown workflow close status: %s", statusStr) } } func extractStringValue(s string) (string, error) { if len(s) >= 2 && s[0] == '\'' && s[len(s)-1] == '\'' { return s[1 : len(s)-1], nil } return "", fmt.Errorf("value %s is not a string value", s) } ================================================ FILE: common/archiver/filestore/queryParser_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: queryParser.go // // Generated by this command: // // mockgen -package filestore -source queryParser.go -destination queryParser_mock.go -mock_names Interface=MockQueryParser // // Package filestore is a generated GoMock package. package filestore import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockQueryParser is a mock of QueryParser interface. type MockQueryParser struct { ctrl *gomock.Controller recorder *MockQueryParserMockRecorder isgomock struct{} } // MockQueryParserMockRecorder is the mock recorder for MockQueryParser. type MockQueryParserMockRecorder struct { mock *MockQueryParser } // NewMockQueryParser creates a new mock instance. func NewMockQueryParser(ctrl *gomock.Controller) *MockQueryParser { mock := &MockQueryParser{ctrl: ctrl} mock.recorder = &MockQueryParserMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQueryParser) EXPECT() *MockQueryParserMockRecorder { return m.recorder } // Parse mocks base method. func (m *MockQueryParser) Parse(query string) (*parsedQuery, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Parse", query) ret0, _ := ret[0].(*parsedQuery) ret1, _ := ret[1].(error) return ret0, ret1 } // Parse indicates an expected call of Parse. func (mr *MockQueryParserMockRecorder) Parse(query any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parse", reflect.TypeOf((*MockQueryParser)(nil).Parse), query) } ================================================ FILE: common/archiver/filestore/queryParser_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package filestore import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) type queryParserSuite struct { *require.Assertions suite.Suite parser QueryParser } func TestQueryParserSuite(t *testing.T) { suite.Run(t, new(queryParserSuite)) } func (s *queryParserSuite) SetupTest() { s.Assertions = require.New(s.T()) s.parser = NewQueryParser() } func (s *queryParserSuite) TestParseWorkflowID_RunID_WorkflowType() { testCases := []struct { query string expectErr bool parsedQuery *parsedQuery }{ { query: "WorkflowID = \"random workflowID\"", expectErr: false, parsedQuery: &parsedQuery{ workflowID: common.StringPtr("random workflowID"), }, }, { query: "WorkflowID = \"random workflowID\" and WorkflowID = \"random workflowID\"", expectErr: false, parsedQuery: &parsedQuery{ workflowID: common.StringPtr("random workflowID"), }, }, { query: "RunID = \"random runID\"", expectErr: false, parsedQuery: &parsedQuery{ runID: common.StringPtr("random runID"), }, }, { query: "WorkflowType = \"random typeName\"", expectErr: false, parsedQuery: &parsedQuery{ workflowTypeName: common.StringPtr("random typeName"), }, }, { query: "WorkflowID = 'random workflowID'", expectErr: false, parsedQuery: &parsedQuery{ workflowID: common.StringPtr("random workflowID"), }, }, { query: "WorkflowType = 'random typeName' and WorkflowType = \"another typeName\"", expectErr: false, parsedQuery: &parsedQuery{ emptyResult: true, }, }, { query: "WorkflowType = 'random typeName' and (WorkflowID = \"random workflowID\" and RunID='random runID')", expectErr: false, parsedQuery: &parsedQuery{ workflowID: common.StringPtr("random workflowID"), runID: common.StringPtr("random runID"), workflowTypeName: common.StringPtr("random typeName"), }, }, { query: "runID = random workflowID", expectErr: true, }, { query: "WorkflowID = \"random workflowID\" or WorkflowID = \"another workflowID\"", expectErr: true, }, { query: "WorkflowID = \"random workflowID\" or runID = \"random runID\"", expectErr: true, }, { query: "workflowid = \"random workflowID\"", expectErr: true, }, { query: "runID > \"random workflowID\"", expectErr: true, }, } for _, tc := range testCases { parsedQuery, err := s.parser.Parse(tc.query) if tc.expectErr { s.Error(err) continue } s.NoError(err) s.Equal(tc.parsedQuery.emptyResult, parsedQuery.emptyResult) if !tc.parsedQuery.emptyResult { s.Equal(tc.parsedQuery.workflowID, parsedQuery.workflowID) s.Equal(tc.parsedQuery.runID, parsedQuery.runID) s.Equal(tc.parsedQuery.workflowTypeName, parsedQuery.workflowTypeName) } } } func (s *queryParserSuite) TestParseCloseStatus() { testCases := []struct { query string expectErr bool parsedQuery *parsedQuery }{ { query: "CloseStatus = \"Completed\"", expectErr: false, parsedQuery: &parsedQuery{ closeStatus: types.WorkflowExecutionCloseStatusCompleted.Ptr(), }, }, { query: "CloseStatus = 'continuedasnew'", expectErr: false, parsedQuery: &parsedQuery{ closeStatus: types.WorkflowExecutionCloseStatusContinuedAsNew.Ptr(), }, }, { query: "CloseStatus = 'TIMED_OUT'", expectErr: false, parsedQuery: &parsedQuery{ closeStatus: types.WorkflowExecutionCloseStatusTimedOut.Ptr(), }, }, { query: "CloseStatus = 'Failed' and CloseStatus = \"Failed\"", expectErr: false, parsedQuery: &parsedQuery{ closeStatus: types.WorkflowExecutionCloseStatusFailed.Ptr(), }, }, { query: "(CloseStatus = 'Timedout' and CloseStatus = \"canceled\")", expectErr: false, parsedQuery: &parsedQuery{ emptyResult: true, }, }, { query: "closeStatus = \"Failed\"", expectErr: true, }, { query: "CloseStatus = \"Failed\" or CloseStatus = \"Failed\"", expectErr: true, }, { query: "CloseStatus = \"unknown\"", expectErr: true, }, { query: "CloseStatus > \"Failed\"", expectErr: true, }, { query: "CloseStatus = 1", expectErr: false, parsedQuery: &parsedQuery{ closeStatus: types.WorkflowExecutionCloseStatusFailed.Ptr(), }, }, { query: "CloseStatus = 10", expectErr: true, }, } for _, tc := range testCases { parsedQuery, err := s.parser.Parse(tc.query) if tc.expectErr { s.Error(err) continue } s.NoError(err) s.Equal(tc.parsedQuery.emptyResult, parsedQuery.emptyResult) if !tc.parsedQuery.emptyResult { s.Equal(tc.parsedQuery.closeStatus, parsedQuery.closeStatus) } } } func (s *queryParserSuite) TestParseCloseTime() { testCases := []struct { query string expectErr bool parsedQuery *parsedQuery }{ { query: "CloseTime <= 1000", expectErr: false, parsedQuery: &parsedQuery{ earliestCloseTime: 0, latestCloseTime: 1000, }, }, { query: "CloseTime < 2000 and CloseTime <= 1000 and CloseTime > 300", expectErr: false, parsedQuery: &parsedQuery{ earliestCloseTime: 301, latestCloseTime: 1000, }, }, { query: "CloseTime = 2000 and (CloseTime > 1000 and CloseTime <= 9999)", expectErr: false, parsedQuery: &parsedQuery{ earliestCloseTime: 2000, latestCloseTime: 2000, }, }, { query: "CloseTime <= \"2019-01-01T11:11:11Z\" and CloseTime >= 1000000", expectErr: false, parsedQuery: &parsedQuery{ earliestCloseTime: 1000000, latestCloseTime: 1546341071000000000, }, }, { query: "closeTime = 2000", expectErr: true, }, { query: "CloseTime > \"2019-01-01 00:00:00\"", expectErr: true, }, { query: "CloseStatus > 2000 or CloseStatus < 1000", expectErr: true, }, } for _, tc := range testCases { parsedQuery, err := s.parser.Parse(tc.query) if tc.expectErr { s.Error(err) continue } s.NoError(err) s.Equal(tc.parsedQuery.emptyResult, parsedQuery.emptyResult) if !tc.parsedQuery.emptyResult { s.Equal(tc.parsedQuery.earliestCloseTime, parsedQuery.earliestCloseTime) s.Equal(tc.parsedQuery.latestCloseTime, parsedQuery.latestCloseTime) } } } func (s *queryParserSuite) TestParse() { testCases := []struct { query string expectErr bool parsedQuery *parsedQuery }{ { query: "CloseTime <= \"2019-01-01T11:11:11Z\" and WorkflowID = 'random workflowID'", expectErr: false, parsedQuery: &parsedQuery{ earliestCloseTime: 0, latestCloseTime: 1546341071000000000, workflowID: common.StringPtr("random workflowID"), }, }, { query: "CloseTime > 1999 and CloseTime < 10000 and RunID = 'random runID' and CloseStatus = 'Failed'", expectErr: false, parsedQuery: &parsedQuery{ earliestCloseTime: 2000, latestCloseTime: 9999, runID: common.StringPtr("random runID"), closeStatus: types.WorkflowExecutionCloseStatusFailed.Ptr(), }, }, { query: "CloseTime > 2001 and CloseTime < 10000 and (RunID = 'random runID') and CloseStatus = 'Failed' and (RunID = 'another ID')", expectErr: false, parsedQuery: &parsedQuery{ emptyResult: true, }, }, } for _, tc := range testCases { parsedQuery, err := s.parser.Parse(tc.query) if tc.expectErr { s.Error(err) continue } s.NoError(err) s.Equal(tc.parsedQuery.emptyResult, parsedQuery.emptyResult) if !tc.parsedQuery.emptyResult { s.Equal(tc.parsedQuery, parsedQuery) } } } ================================================ FILE: common/archiver/filestore/util.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package filestore import ( "context" "encoding/json" "errors" "fmt" "os" "strconv" "strings" "github.com/dgryski/go-farm" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/util" ) var ( errEmptyDirectoryPath = errors.New("directory path is empty") ) // encoding & decoding util func encode(v interface{}) ([]byte, error) { return json.Marshal(v) } func decodeHistoryBatches(data []byte) ([]*types.History, error) { historyBatches := []*types.History{} err := json.Unmarshal(data, &historyBatches) if err != nil { return nil, err } return historyBatches, nil } func decodeVisibilityRecord(data []byte) (*visibilityRecord, error) { record := &visibilityRecord{} err := json.Unmarshal(data, record) if err != nil { return nil, err } return record, nil } func serializeToken(token interface{}) ([]byte, error) { if token == nil { return nil, nil } return json.Marshal(token) } func deserializeGetHistoryToken(bytes []byte) (*getHistoryToken, error) { token := &getHistoryToken{} err := json.Unmarshal(bytes, token) return token, err } func deserializeQueryVisibilityToken(bytes []byte) (*queryVisibilityToken, error) { token := &queryVisibilityToken{} err := json.Unmarshal(bytes, token) return token, err } // File name construction func constructHistoryFilename(domainID, workflowID, runID string, version int64) string { combinedHash := constructHistoryFilenamePrefix(domainID, workflowID, runID) return fmt.Sprintf("%s_%v.history", combinedHash, version) } func constructHistoryFilenamePrefix(domainID, workflowID, runID string) string { return strings.Join([]string{hash(domainID), hash(workflowID), hash(runID)}, "") } func constructVisibilityFilename(closeTimestamp int64, runID string) string { return fmt.Sprintf("%v_%s.visibility", closeTimestamp, hash(runID)) } func hash(s string) string { return fmt.Sprintf("%v", farm.Fingerprint64([]byte(s))) } // Validation func validateDirPath(dirPath string) error { if len(dirPath) == 0 { return errEmptyDirectoryPath } info, err := os.Stat(dirPath) if os.IsNotExist(err) { return nil } if err != nil { return err } if !info.IsDir() { return util.ErrDirectoryExpected } return nil } // Misc. func extractCloseFailoverVersion(filename string) (int64, error) { filenameParts := strings.FieldsFunc(filename, func(r rune) bool { return r == '_' || r == '.' }) if len(filenameParts) != 3 { return -1, errors.New("unknown filename structure") } return strconv.ParseInt(filenameParts[1], 10, 64) } func contextExpired(ctx context.Context) bool { select { case <-ctx.Done(): return true default: return false } } ================================================ FILE: common/archiver/filestore/util_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package filestore import ( "io/ioutil" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/util" ) const ( testFileMode = os.FileMode(0600) testDirMode = os.FileMode(0700) ) type UtilSuite struct { *require.Assertions suite.Suite } func TestUtilSuite(t *testing.T) { suite.Run(t, new(UtilSuite)) } func (s *UtilSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *UtilSuite) TestEncodeDecodeHistoryBatches() { historyBatches := []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, Version: 1, }, }, }, { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: 1, }, { ID: constants.FirstEventID + 2, Version: 2, DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ Identity: "some random identity", }, }, }, }, } encodedHistoryBatches, err := encode(historyBatches) s.NoError(err) decodedHistoryBatches, err := decodeHistoryBatches(encodedHistoryBatches) s.NoError(err) s.Equal(historyBatches, decodedHistoryBatches) } func (s *UtilSuite) TestValidateDirPath() { dir := s.T().TempDir() s.assertDirectoryExists(dir) filename := "test-file-name" s.createFile(dir, filename) fpath := filepath.Join(dir, filename) testCases := []struct { dirPath string expectedErr error }{ { dirPath: "", expectedErr: errEmptyDirectoryPath, }, { dirPath: "/absolute/path", expectedErr: nil, }, { dirPath: "relative/path", expectedErr: nil, }, { dirPath: dir, expectedErr: nil, }, { dirPath: fpath, expectedErr: util.ErrDirectoryExpected, }, } for _, tc := range testCases { s.Equal(tc.expectedErr, validateDirPath(tc.dirPath)) } } func (s *UtilSuite) TestconstructHistoryFilename() { testCases := []struct { domainID string workflowID string runID string closeFailoverVersion int64 expectBuiltName string }{ { domainID: "testDomainID", workflowID: "testWorkflowID", runID: "testRunID", closeFailoverVersion: 5, expectBuiltName: "17971674567288329890367046253745284795510285995943906173973_5.history", }, } for _, tc := range testCases { filename := constructHistoryFilename(tc.domainID, tc.workflowID, tc.runID, tc.closeFailoverVersion) s.Equal(tc.expectBuiltName, filename) } } func (s *UtilSuite) TestExtractCloseFailoverVersion() { testCases := []struct { filename string expectedVersion int64 expectedErr bool }{ { filename: "17971674567288329890367046253745284795510285995943906173973_5.history", expectedVersion: 5, expectedErr: false, }, { filename: "history", expectedErr: true, }, { filename: "some.random.filename", expectedErr: true, }, { filename: "some-random_101.filename", expectedVersion: 101, expectedErr: false, }, { filename: "random_-100.filename", expectedVersion: -100, expectedErr: false, }, } for _, tc := range testCases { version, err := extractCloseFailoverVersion(tc.filename) if tc.expectedErr { s.Error(err) } else { s.NoError(err) s.Equal(tc.expectedVersion, version) } } } func (s *UtilSuite) TestSerializeDeserializeGetHistoryToken() { token := &getHistoryToken{ CloseFailoverVersion: 101, NextBatchIdx: 20, } serializedToken, err := serializeToken(token) s.Nil(err) deserializedToken, err := deserializeGetHistoryToken(serializedToken) s.Nil(err) s.Equal(token, deserializedToken) } func (s *UtilSuite) createFile(dir string, filename string) { err := ioutil.WriteFile(filepath.Join(dir, filename), []byte("file contents"), testFileMode) s.Nil(err) } func (s *UtilSuite) assertDirectoryExists(path string) { exists, err := util.DirectoryExists(path) s.NoError(err) s.True(exists) } ================================================ FILE: common/archiver/filestore/visibilityArchiver.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package filestore import ( "context" "fmt" "os" "path" "sort" "strconv" "strings" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/util" ) const ( errEncodeVisibilityRecord = "failed to encode visibility record" ) type ( visibilityArchiver struct { container *archiver.VisibilityBootstrapContainer fileMode os.FileMode dirMode os.FileMode queryParser QueryParser } queryVisibilityToken struct { LastCloseTime int64 LastRunID string } visibilityRecord archiver.ArchiveVisibilityRequest queryVisibilityRequest struct { domainID string pageSize int nextPageToken []byte parsedQuery *parsedQuery } ) // NewVisibilityArchiver creates a new archiver.VisibilityArchiver based on filestore func NewVisibilityArchiver( container *archiver.VisibilityBootstrapContainer, config *config.FilestoreArchiver, ) (archiver.VisibilityArchiver, error) { fileMode, err := strconv.ParseUint(config.FileMode, 0, 32) if err != nil { return nil, errInvalidFileMode } dirMode, err := strconv.ParseUint(config.DirMode, 0, 32) if err != nil { return nil, errInvalidDirMode } return &visibilityArchiver{ container: container, fileMode: os.FileMode(fileMode), dirMode: os.FileMode(dirMode), queryParser: NewQueryParser(), }, nil } func (v *visibilityArchiver) Archive( ctx context.Context, URI archiver.URI, request *archiver.ArchiveVisibilityRequest, opts ...archiver.ArchiveOption, ) (err error) { featureCatalog := archiver.GetFeatureCatalog(opts...) defer func() { if err != nil && featureCatalog.NonRetriableError != nil { err = featureCatalog.NonRetriableError() } }() logger := archiver.TagLoggerWithArchiveVisibilityRequestAndURI(v.container.Logger, request, URI.String()) if err := v.ValidateURI(URI); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidURI), tag.Error(err)) return err } if err := archiver.ValidateVisibilityArchivalRequest(request); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidArchiveRequest), tag.Error(err)) return err } dirPath := path.Join(URI.Path(), request.DomainID) if err = util.MkdirAll(dirPath, v.dirMode); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(errMakeDirectory), tag.Error(err)) return err } encodedVisibilityRecord, err := encode(request) if err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(errEncodeVisibilityRecord), tag.Error(err)) return err } // The filename has the format: closeTimestamp_hash(runID).visibility // This format allows the archiver to sort all records without reading the file contents filename := constructVisibilityFilename(request.CloseTimestamp, request.RunID) if err := util.WriteFile(path.Join(dirPath, filename), encodedVisibilityRecord, v.fileMode); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(errWriteFile), tag.Error(err)) return err } return nil } func (v *visibilityArchiver) Query( ctx context.Context, URI archiver.URI, request *archiver.QueryVisibilityRequest, ) (*archiver.QueryVisibilityResponse, error) { if err := v.ValidateURI(URI); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidURI.Error()} } if err := archiver.ValidateQueryRequest(request); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidQueryVisibilityRequest.Error()} } parsedQuery, err := v.queryParser.Parse(request.Query) if err != nil { return nil, &types.BadRequestError{Message: err.Error()} } if parsedQuery.emptyResult { return &archiver.QueryVisibilityResponse{}, nil } return v.query(ctx, URI, &queryVisibilityRequest{ domainID: request.DomainID, pageSize: request.PageSize, nextPageToken: request.NextPageToken, parsedQuery: parsedQuery, }) } func (v *visibilityArchiver) query( ctx context.Context, URI archiver.URI, request *queryVisibilityRequest, ) (*archiver.QueryVisibilityResponse, error) { var token *queryVisibilityToken if request.nextPageToken != nil { var err error token, err = deserializeQueryVisibilityToken(request.nextPageToken) if err != nil { return nil, &types.BadRequestError{Message: archiver.ErrNextPageTokenCorrupted.Error()} } } dirPath := path.Join(URI.Path(), request.domainID) exists, err := util.DirectoryExists(dirPath) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } if !exists { return &archiver.QueryVisibilityResponse{}, nil } files, err := util.ListFiles(dirPath) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } files, err = sortAndFilterFiles(files, token) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } if len(files) == 0 { return &archiver.QueryVisibilityResponse{}, nil } response := &archiver.QueryVisibilityResponse{} for idx, file := range files { encodedRecord, err := util.ReadFile(path.Join(dirPath, file)) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } record, err := decodeVisibilityRecord(encodedRecord) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } if record.CloseTimestamp < request.parsedQuery.earliestCloseTime { break } if matchQuery(record, request.parsedQuery) { response.Executions = append(response.Executions, convertToExecutionInfo(record)) if len(response.Executions) == request.pageSize { if idx != len(files) { newToken := &queryVisibilityToken{ LastCloseTime: record.CloseTimestamp, LastRunID: record.RunID, } encodedToken, err := serializeToken(newToken) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } response.NextPageToken = encodedToken } break } } } return response, nil } func (v *visibilityArchiver) ValidateURI(URI archiver.URI) error { if URI.Scheme() != URIScheme { return archiver.ErrURISchemeMismatch } return validateDirPath((URI.Path())) } type parsedVisFilename struct { name string closeTime int64 hashedRunID string } // sortAndFilterFiles sort visibility record file names based on close timestamp (desc) and use hashed runID to break ties. // if a nextPageToken is give, it only returns filenames that have a smaller close timestamp func sortAndFilterFiles(filenames []string, token *queryVisibilityToken) ([]string, error) { var parsedFilenames []*parsedVisFilename for _, name := range filenames { pieces := strings.FieldsFunc(name, func(r rune) bool { return r == '_' || r == '.' }) if len(pieces) != 3 { return nil, fmt.Errorf("failed to parse visibility filename %s", name) } closeTime, err := strconv.ParseInt(pieces[0], 10, 64) if err != nil { return nil, fmt.Errorf("failed to parse visibility filename %s", name) } parsedFilenames = append(parsedFilenames, &parsedVisFilename{ name: name, closeTime: closeTime, hashedRunID: pieces[1], }) } sort.Slice(parsedFilenames, func(i, j int) bool { if parsedFilenames[i].closeTime == parsedFilenames[j].closeTime { return parsedFilenames[i].hashedRunID > parsedFilenames[j].hashedRunID } return parsedFilenames[i].closeTime > parsedFilenames[j].closeTime }) startIdx := 0 if token != nil { LastHashedRunID := hash(token.LastRunID) startIdx = sort.Search(len(parsedFilenames), func(i int) bool { if parsedFilenames[i].closeTime == token.LastCloseTime { return parsedFilenames[i].hashedRunID < LastHashedRunID } return parsedFilenames[i].closeTime < token.LastCloseTime }) } if startIdx == len(parsedFilenames) { return []string{}, nil } var filteredFilenames []string for _, parsedFilename := range parsedFilenames[startIdx:] { filteredFilenames = append(filteredFilenames, parsedFilename.name) } return filteredFilenames, nil } func matchQuery(record *visibilityRecord, query *parsedQuery) bool { if record.CloseTimestamp < query.earliestCloseTime || record.CloseTimestamp > query.latestCloseTime { return false } if query.workflowID != nil && record.WorkflowID != *query.workflowID { return false } if query.runID != nil && record.RunID != *query.runID { return false } if query.workflowTypeName != nil && record.WorkflowTypeName != *query.workflowTypeName { return false } if query.closeStatus != nil && record.CloseStatus != *query.closeStatus { return false } return true } func convertToExecutionInfo(record *visibilityRecord) *types.WorkflowExecutionInfo { return &types.WorkflowExecutionInfo{ Execution: &types.WorkflowExecution{ WorkflowID: record.WorkflowID, RunID: record.RunID, }, Type: &types.WorkflowType{ Name: record.WorkflowTypeName, }, StartTime: common.Int64Ptr(record.StartTimestamp), ExecutionTime: common.Int64Ptr(record.ExecutionTimestamp), CloseTime: common.Int64Ptr(record.CloseTimestamp), CloseStatus: record.CloseStatus.Ptr(), HistoryLength: record.HistoryLength, Memo: record.Memo, SearchAttributes: &types.SearchAttributes{ IndexedFields: archiver.ConvertSearchAttrToBytes(record.SearchAttributes), }, } } ================================================ FILE: common/archiver/filestore/visibilityArchiver_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package filestore import ( "context" "encoding/json" "errors" "os" "path" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/util" ) const ( testWorkflowTypeName = "test-workflow-type" ) type visibilityArchiverSuite struct { *require.Assertions suite.Suite container *archiver.VisibilityBootstrapContainer testArchivalURI archiver.URI testQueryDirectory string visibilityRecords []*visibilityRecord controller *gomock.Controller } func TestVisibilityArchiverSuite(t *testing.T) { suite.Run(t, new(visibilityArchiverSuite)) } func (s *visibilityArchiverSuite) SetupSuite() { var err error s.testQueryDirectory = s.T().TempDir() s.setupVisibilityDirectory() s.testArchivalURI, err = archiver.NewURI("file:///a/b/c") s.Require().NoError(err) } func (s *visibilityArchiverSuite) SetupTest() { s.Assertions = require.New(s.T()) s.container = &archiver.VisibilityBootstrapContainer{ Logger: testlogger.New(s.T()), } s.controller = gomock.NewController(s.T()) } func (s *visibilityArchiverSuite) TearDownTest() { s.controller.Finish() } func (s *visibilityArchiverSuite) TestValidateURI() { testCases := []struct { URI string expectedErr error }{ { URI: "wrongscheme:///a/b/c", expectedErr: archiver.ErrURISchemeMismatch, }, { URI: "file://", expectedErr: errEmptyDirectoryPath, }, { URI: "file:///a/b/c", expectedErr: nil, }, } visibilityArchiver := s.newTestVisibilityArchiver() for _, tc := range testCases { URI, err := archiver.NewURI(tc.URI) s.NoError(err) s.Equal(tc.expectedErr, visibilityArchiver.ValidateURI(URI)) } } func (s *visibilityArchiverSuite) TestArchive_Fail_InvalidURI() { visibilityArchiver := s.newTestVisibilityArchiver() URI, err := archiver.NewURI("wrongscheme://") s.NoError(err) request := &archiver.ArchiveVisibilityRequest{ DomainName: testDomainName, DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, StartTimestamp: time.Now().UnixNano(), ExecutionTimestamp: 0, // workflow without backoff CloseTimestamp: time.Now().UnixNano(), CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: int64(101), } err = visibilityArchiver.Archive(context.Background(), URI, request) s.Error(err) } func (s *visibilityArchiverSuite) TestArchive_Fail_InvalidRequest() { visibilityArchiver := s.newTestVisibilityArchiver() err := visibilityArchiver.Archive(context.Background(), s.testArchivalURI, &archiver.ArchiveVisibilityRequest{}) s.Error(err) } func (s *visibilityArchiverSuite) TestArchive_Fail_NonRetriableErrorOption() { visibilityArchiver := s.newTestVisibilityArchiver() nonRetriableErr := errors.New("some non-retryable error") err := visibilityArchiver.Archive( context.Background(), s.testArchivalURI, &archiver.ArchiveVisibilityRequest{}, archiver.GetNonRetriableErrorOption(nonRetriableErr), ) s.Equal(nonRetriableErr, err) } func (s *visibilityArchiverSuite) TestArchive_Success() { dir := s.T().TempDir() visibilityArchiver := s.newTestVisibilityArchiver() closeTimestamp := time.Now() request := &archiver.ArchiveVisibilityRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, StartTimestamp: closeTimestamp.Add(-time.Hour).UnixNano(), ExecutionTimestamp: 0, // workflow without backoff CloseTimestamp: closeTimestamp.UnixNano(), CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: int64(101), Memo: &types.Memo{ Fields: map[string][]byte{ "testFields": []byte{1, 2, 3}, }, }, SearchAttributes: map[string]string{ "testAttribute": "456", }, } URI, err := archiver.NewURI("file://" + dir) s.NoError(err) err = visibilityArchiver.Archive(context.Background(), URI, request) s.NoError(err) expectedFilename := constructVisibilityFilename(closeTimestamp.UnixNano(), testRunID) filepath := path.Join(dir, testDomainID, expectedFilename) s.assertFileExists(filepath) data, err := util.ReadFile(filepath) s.NoError(err) archivedRecord := &archiver.ArchiveVisibilityRequest{} err = json.Unmarshal(data, archivedRecord) s.NoError(err) s.Equal(request, archivedRecord) } func (s *visibilityArchiverSuite) TestMatchQuery() { testCases := []struct { query *parsedQuery record *visibilityRecord shouldMatch bool }{ { query: &parsedQuery{ earliestCloseTime: int64(1000), latestCloseTime: int64(12345), }, record: &visibilityRecord{ CloseTimestamp: int64(1999), }, shouldMatch: true, }, { query: &parsedQuery{ earliestCloseTime: int64(1000), latestCloseTime: int64(12345), }, record: &visibilityRecord{ CloseTimestamp: int64(999), }, shouldMatch: false, }, { query: &parsedQuery{ earliestCloseTime: int64(1000), latestCloseTime: int64(12345), workflowID: common.StringPtr("random workflowID"), }, record: &visibilityRecord{ CloseTimestamp: int64(2000), }, shouldMatch: false, }, { query: &parsedQuery{ earliestCloseTime: int64(1000), latestCloseTime: int64(12345), workflowID: common.StringPtr("random workflowID"), runID: common.StringPtr("random runID"), }, record: &visibilityRecord{ CloseTimestamp: int64(12345), WorkflowID: "random workflowID", RunID: "random runID", WorkflowTypeName: "random type name", }, shouldMatch: true, }, { query: &parsedQuery{ earliestCloseTime: int64(1000), latestCloseTime: int64(12345), workflowTypeName: common.StringPtr("some random type name"), }, record: &visibilityRecord{ CloseTimestamp: int64(12345), }, shouldMatch: false, }, { query: &parsedQuery{ earliestCloseTime: int64(1000), latestCloseTime: int64(12345), workflowTypeName: common.StringPtr("some random type name"), closeStatus: types.WorkflowExecutionCloseStatusContinuedAsNew.Ptr(), }, record: &visibilityRecord{ CloseTimestamp: int64(12345), CloseStatus: types.WorkflowExecutionCloseStatusContinuedAsNew, WorkflowTypeName: "some random type name", }, shouldMatch: true, }, } for _, tc := range testCases { s.Equal(tc.shouldMatch, matchQuery(tc.record, tc.query)) } } func (s *visibilityArchiverSuite) TestSortAndFilterFiles() { testCases := []struct { filenames []string token *queryVisibilityToken expectedResult []string }{ { filenames: []string{"9_12345.vis", "5_0.vis", "9_54321.vis", "1000_654.vis", "1000_78.vis"}, expectedResult: []string{"1000_78.vis", "1000_654.vis", "9_54321.vis", "9_12345.vis", "5_0.vis"}, }, { filenames: []string{"9_12345.vis", "5_0.vis", "9_54321.vis", "1000_654.vis", "1000_78.vis"}, token: &queryVisibilityToken{ LastCloseTime: 3, }, expectedResult: []string{}, }, { filenames: []string{"9_12345.vis", "5_0.vis", "9_54321.vis", "1000_654.vis", "1000_78.vis"}, token: &queryVisibilityToken{ LastCloseTime: 999, }, expectedResult: []string{"9_54321.vis", "9_12345.vis", "5_0.vis"}, }, { filenames: []string{"9_12345.vis", "5_0.vis", "9_54321.vis", "1000_654.vis", "1000_78.vis"}, token: &queryVisibilityToken{ LastCloseTime: 5, }, expectedResult: []string{"5_0.vis"}, }, } for _, tc := range testCases { result, err := sortAndFilterFiles(tc.filenames, tc.token) s.NoError(err) s.Equal(tc.expectedResult, result) } } func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidURI() { visibilityArchiver := s.newTestVisibilityArchiver() URI, err := archiver.NewURI("wrongscheme://") s.NoError(err) request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 1, } response, err := visibilityArchiver.Query(context.Background(), URI, request) s.Error(err) s.Nil(response) } func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidRequest() { visibilityArchiver := s.newTestVisibilityArchiver() response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, &archiver.QueryVisibilityRequest{}) s.Error(err) s.Nil(response) } func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidQuery() { visibilityArchiver := s.newTestVisibilityArchiver() mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(nil, errors.New("invalid query")) visibilityArchiver.queryParser = mockParser response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, &archiver.QueryVisibilityRequest{ DomainID: "some random domainID", PageSize: 10, Query: "some invalid query", }) s.Error(err) s.Nil(response) } func (s *visibilityArchiverSuite) TestQuery_Success_DirectoryNotExist() { visibilityArchiver := s.newTestVisibilityArchiver() mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ earliestCloseTime: int64(1), latestCloseTime: int64(101), }, nil) visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, Query: "parsed by mockParser", PageSize: 1, } response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, request) s.NoError(err) s.NotNil(response) s.Empty(response.Executions) s.Empty(response.NextPageToken) } func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidToken() { visibilityArchiver := s.newTestVisibilityArchiver() mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ earliestCloseTime: int64(1), latestCloseTime: int64(101), }, nil) visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, Query: "parsed by mockParser", PageSize: 1, NextPageToken: []byte{1, 2, 3}, } response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, request) s.Error(err) s.Nil(response) } func (s *visibilityArchiverSuite) TestQuery_Success_NoNextPageToken() { visibilityArchiver := s.newTestVisibilityArchiver() mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ earliestCloseTime: int64(1), latestCloseTime: int64(10001), workflowID: common.StringPtr(testWorkflowID), }, nil) visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 10, Query: "parsed by mockParser", } URI, err := archiver.NewURI("file://" + s.testQueryDirectory) s.NoError(err) response, err := visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.Nil(response.NextPageToken) s.Len(response.Executions, 1) s.Equal(convertToExecutionInfo(s.visibilityRecords[0]), response.Executions[0]) } func (s *visibilityArchiverSuite) TestQuery_Success_SmallPageSize() { visibilityArchiver := s.newTestVisibilityArchiver() mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ earliestCloseTime: int64(1), latestCloseTime: int64(10001), closeStatus: types.WorkflowExecutionCloseStatusFailed.Ptr(), }, nil).AnyTimes() visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 2, Query: "parsed by mockParser", } URI, err := archiver.NewURI("file://" + s.testQueryDirectory) s.NoError(err) response, err := visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.NotNil(response.NextPageToken) s.Len(response.Executions, 2) s.Equal(convertToExecutionInfo(s.visibilityRecords[0]), response.Executions[0]) s.Equal(convertToExecutionInfo(s.visibilityRecords[1]), response.Executions[1]) request.NextPageToken = response.NextPageToken response, err = visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.Nil(response.NextPageToken) s.Len(response.Executions, 1) s.Equal(convertToExecutionInfo(s.visibilityRecords[3]), response.Executions[0]) } func (s *visibilityArchiverSuite) TestArchiveAndQuery() { dir := s.T().TempDir() visibilityArchiver := s.newTestVisibilityArchiver() mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ earliestCloseTime: int64(10), latestCloseTime: int64(10001), closeStatus: types.WorkflowExecutionCloseStatusFailed.Ptr(), }, nil).AnyTimes() visibilityArchiver.queryParser = mockParser URI, err := archiver.NewURI("file://" + dir) s.NoError(err) for _, record := range s.visibilityRecords { err := visibilityArchiver.Archive(context.Background(), URI, (*archiver.ArchiveVisibilityRequest)(record)) s.NoError(err) } request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 1, Query: "parsed by mockParser", } executions := []*types.WorkflowExecutionInfo{} for len(executions) == 0 || request.NextPageToken != nil { response, err := visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) executions = append(executions, response.Executions...) request.NextPageToken = response.NextPageToken } s.Len(executions, 2) s.Equal(convertToExecutionInfo(s.visibilityRecords[0]), executions[0]) s.Equal(convertToExecutionInfo(s.visibilityRecords[1]), executions[1]) } func (s *visibilityArchiverSuite) newTestVisibilityArchiver() *visibilityArchiver { config := &config.FilestoreArchiver{ FileMode: testFileModeStr, DirMode: testDirModeStr, } archiver, err := NewVisibilityArchiver(s.container, config) s.NoError(err) return archiver.(*visibilityArchiver) } func (s *visibilityArchiverSuite) setupVisibilityDirectory() { s.visibilityRecords = []*visibilityRecord{ { DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, StartTimestamp: 1, CloseTimestamp: 10000, CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: 101, }, { DomainID: testDomainID, DomainName: testDomainName, WorkflowID: "some random workflow ID", RunID: "some random run ID", WorkflowTypeName: testWorkflowTypeName, StartTimestamp: 2, ExecutionTimestamp: 0, CloseTimestamp: 1000, CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: 123, }, { DomainID: testDomainID, DomainName: testDomainName, WorkflowID: "another workflow ID", RunID: "another run ID", WorkflowTypeName: testWorkflowTypeName, StartTimestamp: 3, ExecutionTimestamp: 0, CloseTimestamp: 10, CloseStatus: types.WorkflowExecutionCloseStatusContinuedAsNew, HistoryLength: 456, }, { DomainID: testDomainID, DomainName: testDomainName, WorkflowID: "and another workflow ID", RunID: "and another run ID", WorkflowTypeName: testWorkflowTypeName, StartTimestamp: 3, ExecutionTimestamp: 0, CloseTimestamp: 5, CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: 456, }, { DomainID: "some random domain ID", DomainName: "some random domain name", WorkflowID: "another workflow ID", RunID: "another run ID", WorkflowTypeName: testWorkflowTypeName, StartTimestamp: 3, ExecutionTimestamp: 0, CloseTimestamp: 10000, CloseStatus: types.WorkflowExecutionCloseStatusContinuedAsNew, HistoryLength: 456, }, } for _, record := range s.visibilityRecords { s.writeVisibilityRecordForQueryTest(record) } } func (s *visibilityArchiverSuite) writeVisibilityRecordForQueryTest(record *visibilityRecord) { data, err := encode(record) s.Require().NoError(err) filename := constructVisibilityFilename(record.CloseTimestamp, record.RunID) s.Require().NoError(os.MkdirAll(path.Join(s.testQueryDirectory, record.DomainID), testDirMode)) err = util.WriteFile(path.Join(s.testQueryDirectory, record.DomainID, filename), data, testFileMode) s.Require().NoError(err) } func (s *visibilityArchiverSuite) assertFileExists(filepath string) { exists, err := util.FileExists(filepath) s.NoError(err) s.True(exists) } ================================================ FILE: common/archiver/gcloud/README.md ================================================ # Google Storage blobstore ## Configuration See https://cloud.google.com/docs/authentication/production to understand how is made the authentication against google cloud storage Nowdays we support three different ways in order to let Cadence know where your google keyfile credentials are located * Cadence archival deployment.yaml configuration file * `GOOGLE_APPLICATION_CREDENTIALS` environment variable * Google default credentials location If more than one credentials location is given, then Cadence will resolve the conflicts by the following priority: `GOOGLE_APPLICATION_CREDENTIALS > Cadencen archival deployment.yaml > Google default credentials` Be sure that you have created your bucket first, and have enought rights in order to read/write over your bucket. ### Gcloud Archival example Enabling archival is done by using the configuration below. `credentialsPath` is required but could be empty "" in order to allow a Google default credentials. ``` archival: history: status: "enabled" enableRead: true provider: gstorage: credentialsPath: "/tmp/keyfile.json" visibility: status: "enabled" enableRead: true provider: gstorage: credentialsPath: "/tmp/keyfile.json" domainDefaults: archival: history: status: "enabled" URI: "gs://my-bucket-cad/cadence_archival/development" visibility: status: "enabled" URI: "gs://my-bucket-cad/cadence_archival/visibility" ``` ## Visibility query syntax You can query the visibility store by using the `cadence workflow listarchived` command The syntax for the query is based on SQL Supported column names are - WorkflowType *String* - WorkflowID *String* - StartTime *Date* - CloseTime *Date* - SearchPrecision *String - Day, Hour, Minute, Second* One of these fields are required, StartTime or CloseTime and they are mutually exclusive and also SearchPrecision. Searching for a record will be done in times in the UTC timezone SearchPrecision specifies what range you want to search for records. If you use `SearchPrecision = 'Day'` it will search all records starting from `2020-01-21T00:00:00Z` to `2020-01-21T59:59:59Z` ### Limitations - The only operator supported is `=` - Currently It's not possible to guarantee the resulSet order, specially if the pageSize it's fullfilled. ### Example *Searches the first 20 records for a given day 2020-01-21* `./cadence --do samples-domain workflow listarchived -ps="20" -q "StartTime = '2020-01-21T00:00:00Z' AND SearchPrecision='Day'"` ## Archival query syntax Once you have a workflowId and a runId you can retrieve your workflow history. example: `./cadence --do samples-domain workflow show -w workflow-id -r runId` ================================================ FILE: common/archiver/gcloud/connector/client.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package connector import ( "bytes" "context" "errors" "io" "io/ioutil" "os" "cloud.google.com/go/storage" "google.golang.org/api/iterator" "github.com/uber/cadence/common/archiver" ) var ( // ErrBucketNotFound is non retriable error that is thrown when the bucket doesn't exist ErrBucketNotFound = errors.New("bucket not found") errObjectNotFound = errors.New("object not found") ) type ( // Precondition is a function that allow you to filter a query result. // If subject match params conditions then return true, else return false. Precondition func(subject interface{}) bool // Client is a wrapper around Google cloud storages client library. Client interface { Upload(ctx context.Context, URI archiver.URI, fileName string, file []byte) error Get(ctx context.Context, URI archiver.URI, file string) ([]byte, error) Query(ctx context.Context, URI archiver.URI, fileNamePrefix string) ([]string, error) QueryWithFilters(ctx context.Context, URI archiver.URI, fileNamePrefix string, pageSize, offset int, filters []Precondition) ([]string, bool, int, error) Exist(ctx context.Context, URI archiver.URI, fileName string) (bool, error) } // Config structure used to parse from the storage-provider yaml nodes in [github.com/uber/cadence/common/config.HistoryArchiverProvider] // and [github.com/uber/cadence/common/config.VisibilityArchiverProvider] and Config struct { CredentialsPath string `yaml:"credentialsPath"` } storageWrapper struct { client GcloudStorageClient } ) // NewClient return a Cadence gcloudstorage.Client based on default google service account credentials (ScopeFullControl required). // Bucket must be created by Iaas scripts, in other words, this library doesn't create the required Bucket. // Optionally you can set your credential path through the "GOOGLE_APPLICATION_CREDENTIALS" environment variable or through cadence config file. // You can find more info about "Google Setting Up Authentication for Server to Server Production Applications" under the following link // https://cloud.google.com/docs/authentication/production func NewClient(ctx context.Context, config Config) (Client, error) { if credentialsPath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"); credentialsPath != "" { clientDelegate, err := newClientDelegateWithCredentials(ctx, credentialsPath) return &storageWrapper{client: clientDelegate}, err } if config.CredentialsPath != "" { clientDelegate, err := newClientDelegateWithCredentials(ctx, config.CredentialsPath) return &storageWrapper{client: clientDelegate}, err } clientDelegate, err := newDefaultClientDelegate(ctx) return &storageWrapper{client: clientDelegate}, err } // NewClientWithParams return a gcloudstorage.Client based on input parameters func NewClientWithParams(clientD GcloudStorageClient) (Client, error) { return &storageWrapper{client: clientD}, nil } // Upload push a file to gcloud storage bucket (sinkPath) // example: // Upload(ctx, mockBucketHandleClient, "gs://my-bucket-cad/cadence_archival/development", "45273645-fileName.history", fileReader) func (s *storageWrapper) Upload(ctx context.Context, URI archiver.URI, fileName string, file []byte) (err error) { bucket := s.client.Bucket(URI.Hostname()) writer := bucket.Object(formatSinkPath(URI.Path()) + "/" + fileName).NewWriter(ctx) _, err = io.Copy(writer, bytes.NewReader(file)) if err == nil { err = writer.Close() } return err } // Exist check if a bucket or an object exist // If fileName is empty, then 'Exist' function will only check if the given bucket exist. func (s *storageWrapper) Exist(ctx context.Context, URI archiver.URI, fileName string) (exists bool, err error) { bucket := s.client.Bucket(URI.Hostname()) if _, err := bucket.Attrs(ctx); err != nil { return false, err } if fileName == "" { return true, nil } if _, err = bucket.Object(fileName).Attrs(ctx); err != nil { return false, errObjectNotFound } return true, nil } // Get retrieve a file func (s *storageWrapper) Get(ctx context.Context, URI archiver.URI, fileName string) ([]byte, error) { bucket := s.client.Bucket(URI.Hostname()) reader, err := bucket.Object(formatSinkPath(URI.Path()) + "/" + fileName).NewReader(ctx) if err == nil { defer reader.Close() return ioutil.ReadAll(reader) } return nil, err } // Query, retieves file names by provided storage query func (s *storageWrapper) Query(ctx context.Context, URI archiver.URI, fileNamePrefix string) (fileNames []string, err error) { fileNames = make([]string, 0) bucket := s.client.Bucket(URI.Hostname()) var attrs = new(storage.ObjectAttrs) it := bucket.Objects(ctx, &storage.Query{ Prefix: formatSinkPath(URI.Path()) + "/" + fileNamePrefix, }) for { attrs, err = it.Next() if err == iterator.Done { return fileNames, nil } fileNames = append(fileNames, attrs.Name) } } // QueryWithFilter, retieves filenames that match filter parameters. PageSize is optional, 0 means all records. func (s *storageWrapper) QueryWithFilters(ctx context.Context, URI archiver.URI, fileNamePrefix string, pageSize, offset int, filters []Precondition) ([]string, bool, int, error) { var err error currentPos := offset resultSet := make([]string, 0) bucket := s.client.Bucket(URI.Hostname()) var attrs = new(storage.ObjectAttrs) it := bucket.Objects(ctx, &storage.Query{ Prefix: formatSinkPath(URI.Path()) + "/" + fileNamePrefix, }) for { attrs, err = it.Next() if err == iterator.Done { return resultSet, true, currentPos, nil } if completed := isPageCompleted(pageSize, len(resultSet)); completed { return resultSet, completed, currentPos, err } valid := true for _, f := range filters { if valid = f(attrs.Name); !valid { break } } if valid { if offset > 0 { offset-- continue } // if match parsedQuery criteria and current cursor position is the last known position (offset is zero), append fileName to resultSet resultSet = append(resultSet, attrs.Name) currentPos++ } } } func isPageCompleted(pageSize, currentPosition int) bool { return pageSize != 0 && currentPosition > 0 && pageSize <= currentPosition } func formatSinkPath(sinkPath string) string { return sinkPath[1:] } ================================================ FILE: common/archiver/gcloud/connector/clientDelegate.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package connector import ( "context" "io/ioutil" "cloud.google.com/go/storage" "golang.org/x/oauth2/google" "google.golang.org/api/option" ) type ( // GcloudStorageClient is an interface that expose some methods from gcloud storage client GcloudStorageClient interface { Bucket(URI string) BucketHandleWrapper } clientDelegate struct { nativeClient *storage.Client } ) type ( // BucketHandleWrapper is an interface that expose some methods from gcloud storage bucket BucketHandleWrapper interface { Object(name string) ObjectHandleWrapper Objects(ctx context.Context, q *storage.Query) ObjectIteratorWrapper Attrs(ctx context.Context) (*storage.BucketAttrs, error) } bucketDelegate struct { bucket *storage.BucketHandle } ) type ( // ObjectHandleWrapper is an interface that expose some methods from gcloud storage object ObjectHandleWrapper interface { NewWriter(ctx context.Context) WriterWrapper NewReader(ctx context.Context) (ReaderWrapper, error) Attrs(ctx context.Context) (*storage.ObjectAttrs, error) } objectDelegate struct { object *storage.ObjectHandle } ) type ( // WriterWrapper is an interface that expose some methods from gcloud storage writer WriterWrapper interface { Close() error Write(p []byte) (n int, err error) CloseWithError(err error) error } writerDelegate struct { writer *storage.Writer } ) type ( // ReaderWrapper is an interface that expose some methods from gcloud storage reader ReaderWrapper interface { Close() error Read(p []byte) (int, error) } readerDelegate struct { reader *storage.Reader } ) type ( // ObjectIteratorWrapper is an interface that expose some methods from gcloud storage objectIterator ObjectIteratorWrapper interface { Next() (*storage.ObjectAttrs, error) } ) func newDefaultClientDelegate(ctx context.Context) (*clientDelegate, error) { nativeClient, err := storage.NewClient(ctx) return &clientDelegate{nativeClient: nativeClient}, err } func newClientDelegateWithCredentials(ctx context.Context, credentialsPath string) (*clientDelegate, error) { jsonKey, err := ioutil.ReadFile(credentialsPath) if err != nil { return newDefaultClientDelegate(ctx) } conf, err := google.JWTConfigFromJSON(jsonKey, storage.ScopeFullControl) if err != nil { return newDefaultClientDelegate(ctx) } nativeClient, err := storage.NewClient(ctx, option.WithTokenSource(conf.TokenSource(ctx))) return &clientDelegate{nativeClient: nativeClient}, err } // Bucket returns a BucketHandle, which provides operations on the named bucket. // This call does not perform any network operations. // // The supplied name must contain only lowercase letters, numbers, dashes, // underscores, and dots. The full specification for valid bucket names can be // found at: // // https://cloud.google.com/storage/docs/bucket-naming func (c *clientDelegate) Bucket(bucketName string) BucketHandleWrapper { return &bucketDelegate{bucket: c.nativeClient.Bucket(bucketName)} } // Object returns an ObjectHandle, which provides operations on the named object. // This call does not perform any network operations. // // name must consist entirely of valid UTF-8-encoded runes. The full specification // for valid object names can be found at: // // https://cloud.google.com/storage/docs/bucket-naming func (b *bucketDelegate) Object(name string) ObjectHandleWrapper { return &objectDelegate{object: b.bucket.Object(name)} } // Objects returns an iterator over the objects in the bucket that match the Query q. // If q is nil, no filtering is done. func (b *bucketDelegate) Objects(ctx context.Context, q *storage.Query) ObjectIteratorWrapper { return b.bucket.Objects(ctx, q) } // Attrs returns the metadata for the bucket. func (b *bucketDelegate) Attrs(ctx context.Context) (*storage.BucketAttrs, error) { return b.bucket.Attrs(ctx) } // NewWriter returns a storage Writer that writes to the GCS object // associated with this ObjectHandle. // // A new object will be created unless an object with this name already exists. // Otherwise any previous object with the same name will be replaced. // The object will not be available (and any previous object will remain) // until Close has been called. // // Attributes can be set on the object by modifying the returned Writer's // ObjectAttrs field before the first call to Write. If no ContentType // attribute is specified, the content type will be automatically sniffed // using net/http.DetectContentType. // // It is the caller's responsibility to call Close when writing is done. To // stop writing without saving the data, cancel the context. func (o *objectDelegate) NewWriter(ctx context.Context) WriterWrapper { return &writerDelegate{writer: o.object.NewWriter(ctx)} } // NewReader creates a new Reader to read the contents of the // object. // ErrObjectNotExist will be returned if the object is not found. // // The caller must call Close on the returned Reader when done reading. func (o *objectDelegate) NewReader(ctx context.Context) (ReaderWrapper, error) { r, err := o.object.NewReader(ctx) return &readerDelegate{reader: r}, err } func (o *objectDelegate) Attrs(ctx context.Context) (attrs *storage.ObjectAttrs, err error) { return o.object.Attrs(ctx) } // Close completes the write operation and flushes any buffered data. // If Close doesn't return an error, metadata about the written object // can be retrieved by calling Attrs. func (w *writerDelegate) Close() error { return w.writer.Close() } // Write appends to w. It implements the io.Writer interface. // // Since writes happen asynchronously, Write may return a nil // error even though the write failed (or will fail). Always // use the error returned from Writer.Close to determine if // the upload was successful. func (w *writerDelegate) Write(p []byte) (int, error) { return w.writer.Write(p) } // CloseWithError aborts the write operation with the provided error. // CloseWithError always returns nil. // // Deprecated: cancel the context passed to NewWriter instead. func (w *writerDelegate) CloseWithError(err error) error { return w.writer.CloseWithError(err) } // Close closes the Reader. It must be called when done reading. func (r *readerDelegate) Close() error { return r.reader.Close() } func (r *readerDelegate) Read(p []byte) (int, error) { return r.reader.Read(p) } ================================================ FILE: common/archiver/gcloud/connector/client_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package connector_test import ( "context" "encoding/json" "errors" "io" "io/ioutil" "os" "strings" "testing" "cloud.google.com/go/storage" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "google.golang.org/api/iterator" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/gcloud/connector" "github.com/uber/cadence/common/archiver/gcloud/connector/mocks" ) func (s *clientSuite) SetupTest() { s.Assertions = require.New(s.T()) file, _ := json.MarshalIndent(&fakeData{data: "example"}, "", " ") os.MkdirAll("/tmp/cadence_archival/development", os.ModePerm) s.Require().NoError(ioutil.WriteFile("/tmp/cadence_archival/development/myfile.history", file, 0644)) } func (s *clientSuite) TearDownTest() { os.Remove("/tmp/cadence_archival/development/myfile.history") } func TestClientSuite(t *testing.T) { suite.Run(t, new(clientSuite)) } type clientSuite struct { *require.Assertions suite.Suite } type fakeData struct { data string } func (s *clientSuite) TestUpload() { ctx := context.Background() mockStorageClient := &mocks.GcloudStorageClient{} mockBucketHandleClient := &mocks.BucketHandleWrapper{} mockObjectHandler := &mocks.ObjectHandleWrapper{} mockWriter := &mocks.WriterWrapper{} storageWrapper, _ := connector.NewClientWithParams(mockStorageClient) mockStorageClient.On("Bucket", "my-bucket-cad").Return(mockBucketHandleClient).Times(1) mockBucketHandleClient.On("Object", "cadence_archival/development/myfile.history").Return(mockObjectHandler).Times(1) mockObjectHandler.On("NewWriter", ctx).Return(mockWriter).Times(1) mockWriter.On("Write", mock.Anything).Return(2, nil).Times(2) mockWriter.On("Close").Return(nil).Times(1) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") s.Require().NoError(err) err = storageWrapper.Upload(ctx, URI, "myfile.history", []byte("{}")) s.Require().NoError(err) } func (s *clientSuite) TestUploadWriterCloseError() { ctx := context.Background() mockStorageClient := &mocks.GcloudStorageClient{} mockBucketHandleClient := &mocks.BucketHandleWrapper{} mockObjectHandler := &mocks.ObjectHandleWrapper{} mockWriter := &mocks.WriterWrapper{} storageWrapper, _ := connector.NewClientWithParams(mockStorageClient) mockStorageClient.On("Bucket", "my-bucket-cad").Return(mockBucketHandleClient).Times(1) mockBucketHandleClient.On("Object", "cadence_archival/development/myfile.history").Return(mockObjectHandler).Times(1) mockObjectHandler.On("NewWriter", ctx).Return(mockWriter).Times(1) mockWriter.On("Write", mock.Anything).Return(2, nil).Times(2) mockWriter.On("Close").Return(errors.New("Not Found")).Times(1) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") s.Require().NoError(err) err = storageWrapper.Upload(ctx, URI, "myfile.history", []byte("{}")) s.Require().EqualError(err, "Not Found") } func (s *clientSuite) TestExist() { ctx := context.Background() testCases := []struct { callContext context.Context URI string fileName string bucketName string bucketExists bool objectExists bool bucketExpectedError error objectExpectedError error }{ { callContext: ctx, URI: "gs://my-bucket-cad/cadence_archival/development", fileName: "", bucketName: "my-bucket-cad", bucketExists: true, objectExists: false, bucketExpectedError: nil, objectExpectedError: nil, }, { callContext: ctx, URI: "gs://my-bucket-cad/cadence_archival/development", fileName: "", bucketName: "my-bucket-cad", bucketExists: false, objectExists: false, bucketExpectedError: errors.New("bucket not found"), objectExpectedError: nil, }, { callContext: ctx, URI: "gs://my-bucket-cad/cadence_archival/development", fileName: "myfile.history", bucketName: "my-bucket-cad", bucketExists: true, objectExists: true, bucketExpectedError: nil, objectExpectedError: nil, }, { callContext: ctx, URI: "gs://my-bucket-cad/cadence_archival/development", fileName: "myfile.history", bucketName: "my-bucket-cad", bucketExists: true, objectExists: false, bucketExpectedError: nil, objectExpectedError: errors.New("object not found"), }, } for _, tc := range testCases { mockStorageClient := &mocks.GcloudStorageClient{} mockBucketHandleClient := &mocks.BucketHandleWrapper{} mockObjectHandler := &mocks.ObjectHandleWrapper{} mockStorageClient.On("Bucket", tc.bucketName).Return(mockBucketHandleClient).Times(1) mockBucketHandleClient.On("Attrs", tc.callContext).Return(nil, tc.bucketExpectedError).Times(1) mockBucketHandleClient.On("Object", tc.fileName).Return(mockObjectHandler).Times(1) mockObjectHandler.On("Attrs", tc.callContext).Return(nil, tc.objectExpectedError).Times(1) URI, _ := archiver.NewURI(tc.URI) storageWrapper, _ := connector.NewClientWithParams(mockStorageClient) exists, err := storageWrapper.Exist(tc.callContext, URI, tc.fileName) if tc.bucketExists && tc.fileName == "" { s.Require().NoError(err) s.True(exists) } else if !tc.bucketExists { s.Require().Error(err) s.Require().EqualError(err, "bucket not found") s.False(exists) } else if tc.bucketExists && tc.fileName != "" && tc.objectExists { s.Require().NoError(err) s.True(exists) } else if tc.bucketExists && tc.fileName != "" && !tc.objectExists { s.Require().Error(err) s.Require().EqualError(err, "object not found") s.False(exists) } } } func (s *clientSuite) TestGet() { ctx := context.Background() mockStorageClient := &mocks.GcloudStorageClient{} mockBucketHandleClient := &mocks.BucketHandleWrapper{} mockObjectHandler := &mocks.ObjectHandleWrapper{} mockReader := &mocks.ReaderWrapper{} storageWrapper, _ := connector.NewClientWithParams(mockStorageClient) mockStorageClient.On("Bucket", "my-bucket-cad").Return(mockBucketHandleClient).Times(1) mockBucketHandleClient.On("Object", "cadence_archival/development/myfile.history").Return(mockObjectHandler).Times(1) mockObjectHandler.On("NewReader", ctx).Return(mockReader, nil).Times(1) mockReader.On("Read", mock.Anything).Return(2, io.EOF).Times(2) mockReader.On("Close").Return(nil).Times(1) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") s.Require().NoError(err) _, err = storageWrapper.Get(ctx, URI, "myfile.history") s.Require().NoError(err) } func (s *clientSuite) TestWrongGoogleCredentialsPath() { ctx := context.Background() s.T().Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/Wrong/path") _, err := connector.NewClient(ctx, connector.Config{}) // config is ignored, prefers env var s.Require().Error(err) } func (s *clientSuite) TestQuery() { ctx := context.Background() mockBucketHandleClient := &mocks.BucketHandleWrapper{} mockStorageClient := &mocks.GcloudStorageClient{} mockObjectIterator := &mocks.ObjectIteratorWrapper{} storageWrapper, _ := connector.NewClientWithParams(mockStorageClient) attr := new(storage.ObjectAttrs) attr.Name = "fileName_01" mockStorageClient.On("Bucket", "my-bucket-cad").Return(mockBucketHandleClient).Times(1) mockBucketHandleClient.On("Objects", ctx, mock.Anything).Return(mockObjectIterator).Times(1) mockIterator := 0 mockObjectIterator.On("Next").Return(func() *storage.ObjectAttrs { mockIterator++ if mockIterator == 1 { return attr } return nil }, func() error { if mockIterator == 1 { return nil } return iterator.Done }).Times(2) var fileNames []string URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") s.Require().NoError(err) fileNames, err = storageWrapper.Query(ctx, URI, "7478875943689868082123907395549832634615673687049942026838") s.Require().NoError(err) s.Equal(strings.Join(fileNames, ", "), "fileName_01") } func (s *clientSuite) TestQueryWithFilter() { ctx := context.Background() mockBucketHandleClient := &mocks.BucketHandleWrapper{} mockStorageClient := &mocks.GcloudStorageClient{} mockObjectIterator := &mocks.ObjectIteratorWrapper{} storageWrapper, _ := connector.NewClientWithParams(mockStorageClient) attr := new(storage.ObjectAttrs) attr.Name = "closeTimeout_2020-02-27T09:42:28Z_12851121011173788097_4418294404690464320_15619178330501475177.visibility" attrInvalid := new(storage.ObjectAttrs) attrInvalid.Name = "closeTimeout_2020-02-27T09:42:28Z_12851121011173788097_4418294404690464321_15619178330501475177.visibility" mockStorageClient.On("Bucket", "my-bucket-cad").Return(mockBucketHandleClient).Times(1) mockBucketHandleClient.On("Objects", ctx, mock.Anything).Return(mockObjectIterator).Times(1) mockIterator := 0 mockObjectIterator.On("Next").Return(func() *storage.ObjectAttrs { mockIterator++ switch mockIterator { case 1: return attr case 2: return attrInvalid default: return nil } }, func() error { switch mockIterator { case 1: return nil case 2: return nil default: return iterator.Done } }).Times(3) var fileNames []string URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") s.Require().NoError(err) fileNames, _, _, err = storageWrapper.QueryWithFilters(ctx, URI, "closeTimeout_2020-02-27T09:42:28Z", 0, 0, []connector.Precondition{newWorkflowIDPrecondition("4418294404690464320")}) s.Require().NoError(err) s.Equal(strings.Join(fileNames, ", "), "closeTimeout_2020-02-27T09:42:28Z_12851121011173788097_4418294404690464320_15619178330501475177.visibility") } func newWorkflowIDPrecondition(workflowID string) connector.Precondition { return func(subject interface{}) bool { if workflowID == "" { return true } fileName, ok := subject.(string) if !ok { return false } if strings.Contains(fileName, workflowID) { fileNameParts := strings.Split(fileName, "_") if len(fileNameParts) != 5 { return true } return strings.Contains(fileName, fileNameParts[3]) } return false } } ================================================ FILE: common/archiver/gcloud/connector/mocks/BucketHandleWrapper.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import ( context "context" storage "cloud.google.com/go/storage" mock "github.com/stretchr/testify/mock" connector "github.com/uber/cadence/common/archiver/gcloud/connector" ) // BucketHandleWrapper is an autogenerated mock type for the BucketHandleWrapper type type BucketHandleWrapper struct { mock.Mock } // Attrs provides a mock function with given fields: ctx func (_m *BucketHandleWrapper) Attrs(ctx context.Context) (*storage.BucketAttrs, error) { ret := _m.Called(ctx) var r0 *storage.BucketAttrs if rf, ok := ret.Get(0).(func(context.Context) *storage.BucketAttrs); ok { r0 = rf(ctx) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*storage.BucketAttrs) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { r1 = ret.Error(1) } return r0, r1 } // Object provides a mock function with given fields: name func (_m *BucketHandleWrapper) Object(name string) connector.ObjectHandleWrapper { ret := _m.Called(name) var r0 connector.ObjectHandleWrapper if rf, ok := ret.Get(0).(func(string) connector.ObjectHandleWrapper); ok { r0 = rf(name) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(connector.ObjectHandleWrapper) } } return r0 } // Objects provides a mock function with given fields: ctx, q func (_m *BucketHandleWrapper) Objects(ctx context.Context, q *storage.Query) connector.ObjectIteratorWrapper { ret := _m.Called(ctx, q) var r0 connector.ObjectIteratorWrapper if rf, ok := ret.Get(0).(func(context.Context, *storage.Query) connector.ObjectIteratorWrapper); ok { r0 = rf(ctx, q) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(connector.ObjectIteratorWrapper) } } return r0 } ================================================ FILE: common/archiver/gcloud/connector/mocks/Client.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import ( context "context" mock "github.com/stretchr/testify/mock" archiver "github.com/uber/cadence/common/archiver" connector "github.com/uber/cadence/common/archiver/gcloud/connector" ) // Client is an autogenerated mock type for the Client type type Client struct { mock.Mock } // Exist provides a mock function with given fields: ctx, URI, fileName func (_m *Client) Exist(ctx context.Context, URI archiver.URI, fileName string) (bool, error) { ret := _m.Called(ctx, URI, fileName) var r0 bool if rf, ok := ret.Get(0).(func(context.Context, archiver.URI, string) bool); ok { r0 = rf(ctx, URI, fileName) } else { r0 = ret.Get(0).(bool) } var r1 error if rf, ok := ret.Get(1).(func(context.Context, archiver.URI, string) error); ok { r1 = rf(ctx, URI, fileName) } else { r1 = ret.Error(1) } return r0, r1 } // Get provides a mock function with given fields: ctx, URI, file func (_m *Client) Get(ctx context.Context, URI archiver.URI, file string) ([]byte, error) { ret := _m.Called(ctx, URI, file) var r0 []byte if rf, ok := ret.Get(0).(func(context.Context, archiver.URI, string) []byte); ok { r0 = rf(ctx, URI, file) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]byte) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, archiver.URI, string) error); ok { r1 = rf(ctx, URI, file) } else { r1 = ret.Error(1) } return r0, r1 } // Query provides a mock function with given fields: ctx, URI, fileNamePrefix func (_m *Client) Query(ctx context.Context, URI archiver.URI, fileNamePrefix string) ([]string, error) { ret := _m.Called(ctx, URI, fileNamePrefix) var r0 []string if rf, ok := ret.Get(0).(func(context.Context, archiver.URI, string) []string); ok { r0 = rf(ctx, URI, fileNamePrefix) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]string) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, archiver.URI, string) error); ok { r1 = rf(ctx, URI, fileNamePrefix) } else { r1 = ret.Error(1) } return r0, r1 } // QueryWithFilters provides a mock function with given fields: ctx, URI, fileNamePrefix, pageSize, offset, filters func (_m *Client) QueryWithFilters(ctx context.Context, URI archiver.URI, fileNamePrefix string, pageSize int, offset int, filters []connector.Precondition) ([]string, bool, int, error) { ret := _m.Called(ctx, URI, fileNamePrefix, pageSize, offset, filters) var r0 []string if rf, ok := ret.Get(0).(func(context.Context, archiver.URI, string, int, int, []connector.Precondition) []string); ok { r0 = rf(ctx, URI, fileNamePrefix, pageSize, offset, filters) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]string) } } var r1 bool if rf, ok := ret.Get(1).(func(context.Context, archiver.URI, string, int, int, []connector.Precondition) bool); ok { r1 = rf(ctx, URI, fileNamePrefix, pageSize, offset, filters) } else { r1 = ret.Get(1).(bool) } var r2 int if rf, ok := ret.Get(2).(func(context.Context, archiver.URI, string, int, int, []connector.Precondition) int); ok { r2 = rf(ctx, URI, fileNamePrefix, pageSize, offset, filters) } else { r2 = ret.Get(2).(int) } var r3 error if rf, ok := ret.Get(3).(func(context.Context, archiver.URI, string, int, int, []connector.Precondition) error); ok { r3 = rf(ctx, URI, fileNamePrefix, pageSize, offset, filters) } else { r3 = ret.Error(3) } return r0, r1, r2, r3 } // Upload provides a mock function with given fields: ctx, URI, fileName, file func (_m *Client) Upload(ctx context.Context, URI archiver.URI, fileName string, file []byte) error { ret := _m.Called(ctx, URI, fileName, file) var r0 error if rf, ok := ret.Get(0).(func(context.Context, archiver.URI, string, []byte) error); ok { r0 = rf(ctx, URI, fileName, file) } else { r0 = ret.Error(0) } return r0 } ================================================ FILE: common/archiver/gcloud/connector/mocks/GcloudStorageClient.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import ( mock "github.com/stretchr/testify/mock" connector "github.com/uber/cadence/common/archiver/gcloud/connector" ) // GcloudStorageClient is an autogenerated mock type for the GcloudStorageClient type type GcloudStorageClient struct { mock.Mock } // Bucket provides a mock function with given fields: URI func (_m *GcloudStorageClient) Bucket(URI string) connector.BucketHandleWrapper { ret := _m.Called(URI) var r0 connector.BucketHandleWrapper if rf, ok := ret.Get(0).(func(string) connector.BucketHandleWrapper); ok { r0 = rf(URI) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(connector.BucketHandleWrapper) } } return r0 } ================================================ FILE: common/archiver/gcloud/connector/mocks/ObjectHandleWrapper.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import ( context "context" storage "cloud.google.com/go/storage" mock "github.com/stretchr/testify/mock" connector "github.com/uber/cadence/common/archiver/gcloud/connector" ) // ObjectHandleWrapper is an autogenerated mock type for the ObjectHandleWrapper type type ObjectHandleWrapper struct { mock.Mock } // Attrs provides a mock function with given fields: ctx func (_m *ObjectHandleWrapper) Attrs(ctx context.Context) (*storage.ObjectAttrs, error) { ret := _m.Called(ctx) var r0 *storage.ObjectAttrs if rf, ok := ret.Get(0).(func(context.Context) *storage.ObjectAttrs); ok { r0 = rf(ctx) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*storage.ObjectAttrs) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { r1 = ret.Error(1) } return r0, r1 } // NewReader provides a mock function with given fields: ctx func (_m *ObjectHandleWrapper) NewReader(ctx context.Context) (connector.ReaderWrapper, error) { ret := _m.Called(ctx) var r0 connector.ReaderWrapper if rf, ok := ret.Get(0).(func(context.Context) connector.ReaderWrapper); ok { r0 = rf(ctx) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(connector.ReaderWrapper) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { r1 = ret.Error(1) } return r0, r1 } // NewWriter provides a mock function with given fields: ctx func (_m *ObjectHandleWrapper) NewWriter(ctx context.Context) connector.WriterWrapper { ret := _m.Called(ctx) var r0 connector.WriterWrapper if rf, ok := ret.Get(0).(func(context.Context) connector.WriterWrapper); ok { r0 = rf(ctx) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(connector.WriterWrapper) } } return r0 } ================================================ FILE: common/archiver/gcloud/connector/mocks/ObjectIteratorWrapper.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import ( storage "cloud.google.com/go/storage" mock "github.com/stretchr/testify/mock" ) // ObjectIteratorWrapper is an autogenerated mock type for the ObjectIteratorWrapper type type ObjectIteratorWrapper struct { mock.Mock } // Next provides a mock function with given fields: func (_m *ObjectIteratorWrapper) Next() (*storage.ObjectAttrs, error) { ret := _m.Called() var r0 *storage.ObjectAttrs if rf, ok := ret.Get(0).(func() *storage.ObjectAttrs); ok { r0 = rf() } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*storage.ObjectAttrs) } } var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { r1 = ret.Error(1) } return r0, r1 } ================================================ FILE: common/archiver/gcloud/connector/mocks/ReaderWrapper.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import mock "github.com/stretchr/testify/mock" // ReaderWrapper is an autogenerated mock type for the ReaderWrapper type type ReaderWrapper struct { mock.Mock } // Close provides a mock function with given fields: func (_m *ReaderWrapper) Close() error { ret := _m.Called() var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() } else { r0 = ret.Error(0) } return r0 } // Read provides a mock function with given fields: p func (_m *ReaderWrapper) Read(p []byte) (int, error) { ret := _m.Called(p) var r0 int if rf, ok := ret.Get(0).(func([]byte) int); ok { r0 = rf(p) } else { r0 = ret.Get(0).(int) } var r1 error if rf, ok := ret.Get(1).(func([]byte) error); ok { r1 = rf(p) } else { r1 = ret.Error(1) } return r0, r1 } ================================================ FILE: common/archiver/gcloud/connector/mocks/WriterWrapper.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import mock "github.com/stretchr/testify/mock" // WriterWrapper is an autogenerated mock type for the WriterWrapper type type WriterWrapper struct { mock.Mock } // Close provides a mock function with given fields: func (_m *WriterWrapper) Close() error { ret := _m.Called() var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() } else { r0 = ret.Error(0) } return r0 } // CloseWithError provides a mock function with given fields: err func (_m *WriterWrapper) CloseWithError(err error) error { ret := _m.Called(err) var r0 error if rf, ok := ret.Get(0).(func(error) error); ok { r0 = rf(err) } else { r0 = ret.Error(0) } return r0 } // Write provides a mock function with given fields: p func (_m *WriterWrapper) Write(p []byte) (int, error) { ret := _m.Called(p) var r0 int if rf, ok := ret.Get(0).(func([]byte) int); ok { r0 = rf(p) } else { r0 = ret.Get(0).(int) } var r1 error if rf, ok := ret.Get(1).(func([]byte) error); ok { r1 = rf(p) } else { r1 = ret.Error(1) } return r0, r1 } ================================================ FILE: common/archiver/gcloud/go.mod ================================================ module github.com/uber/cadence/common/archiver/gcloud go 1.23.0 toolchain go1.23.4 // build against the current code in the "main" module, not a specific SHA. // // anyone outside this repo using this needs to ensure that both the "main" module and this module // are at the same SHA for consistency, but internally we can cheat by telling Go that it's at a // relative file path. replace github.com/uber/cadence => ../../.. // ringpop-go and tchannel-go depends on older version of thrift, yarpc brings up newer version replace github.com/apache/thrift => github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7 require ( github.com/aws/aws-sdk-go v1.54.12 // indirect github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748 // indirect github.com/cristalhq/jwt/v3 v3.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/m3db/prometheus_client_golang v0.8.1 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 // indirect github.com/robfig/cron v1.2.0 // indirect github.com/stretchr/testify v1.10.0 github.com/uber-go/tally v3.3.15+incompatible github.com/uber/ringpop-go v0.8.5 // indirect github.com/uber/tchannel-go v1.22.2 // indirect github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 go.uber.org/atomic v1.10.0 // indirect go.uber.org/cadence v0.19.0 // indirect go.uber.org/config v1.4.0 // indirect go.uber.org/multierr v1.10.0 // indirect go.uber.org/thriftrw v1.29.2 // indirect go.uber.org/yarpc v1.70.3 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/net v0.40.0 // indirect golang.org/x/sync v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.22.0 // indirect google.golang.org/grpc v1.59.0 // indirect gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) require ( cloud.google.com/go/storage v1.30.1 github.com/uber/cadence v0.0.0-00010101000000-000000000000 go.uber.org/mock v0.5.0 golang.org/x/oauth2 v0.11.0 google.golang.org/api v0.128.0 ) require ( cloud.google.com/go v0.110.8 // indirect cloud.google.com/go/compute v1.23.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.3 // indirect github.com/BurntSushi/toml v1.3.2 // indirect github.com/apache/thrift v0.17.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/gogo/googleapis v1.3.2 // indirect github.com/gogo/status v1.1.0 // indirect github.com/golang-jwt/jwt/v5 v5.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gofuzz v1.0.0 // indirect github.com/google/s2a-go v0.1.4 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.4 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.5.0 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/m3db/prometheus_client_model v0.1.0 // indirect github.com/m3db/prometheus_common v0.1.0 // indirect github.com/m3db/prometheus_procfs v0.8.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.11.1 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/uber-go/mapdecode v1.0.0 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/dig v1.18.0 // indirect go.uber.org/fx v1.23.0 // indirect go.uber.org/net/metrics v1.3.0 // indirect golang.org/x/crypto v0.38.0 // indirect golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/mod v0.18.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.3.2 // indirect ) ================================================ FILE: common/archiver/gcloud/go.sum ================================================ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME= cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go/compute v1.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0= cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/iam v1.1.3 h1:18tKG7DzydKWUnLjonWcJO6wjSCAtzh4GcRKlH/Hrzc= cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7 h1:Fv9bK1Q+ly/ROk4aJsVMeuIwPel4bEnD8EPiI91nZMg= github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/aws/aws-sdk-go v1.54.12 h1:xPDB+GSBZq0rJbmDZF+EyfMbnWRyfEPcn7PZ7bJjXSw= github.com/aws/aws-sdk-go v1.54.12/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748 h1:bXxS5/Z3/dfc8iFniQfgogNBomo0u+1//9eP+jl8GVo= github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cristalhq/jwt/v3 v3.1.0 h1:iLeL9VzB0SCtjCy9Kg53rMwTcrNm+GHyVcz2eUujz6s= github.com/cristalhq/jwt/v3 v3.1.0/go.mod h1:XOnIXst8ozq/esy5N1XOlSyQqBd+84fxJ99FK+1jgL8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.3.2 h1:kX1es4djPJrsDhY7aZKJy7aZasdcB5oSOEphMjSB53c= github.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3-0.20190920234318-1680a479a2cf/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.4 h1:uGy6JWR/uMIILU8wbf+OkstIrNiMjGpEIyhx8f6W7s4= github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/m3db/prometheus_client_golang v0.8.1 h1:t7w/tcFws81JL1j5sqmpqcOyQOpH4RDOmIe3A3fdN3w= github.com/m3db/prometheus_client_golang v0.8.1/go.mod h1:8R/f1xYhXWq59KD/mbRqoBulXejss7vYtYzWmruNUwI= github.com/m3db/prometheus_client_model v0.1.0 h1:cg1+DiuyT6x8h9voibtarkH1KT6CmsewBSaBhe8wzLo= github.com/m3db/prometheus_client_model v0.1.0/go.mod h1:Qfsxn+LypxzF+lNhak7cF7k0zxK7uB/ynGYoj80zcD4= github.com/m3db/prometheus_common v0.1.0 h1:YJu6eCIV6MQlcwND24cRG/aRkZDX1jvYbsNNs1ZYr0w= github.com/m3db/prometheus_common v0.1.0/go.mod h1:EBmDQaMAy4B8i+qsg1wMXAelLNVbp49i/JOeVszQ/rs= github.com/m3db/prometheus_procfs v0.8.1 h1:LsxWzVELhDU9sLsZTaFLCeAwCn7bC7qecZcK4zobs/g= github.com/m3db/prometheus_procfs v0.8.1/go.mod h1:N8lv8fLh3U3koZx1Bnisj60GYUMDpWb09x1R+dmMOJo= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pborman/uuid v0.0.0-20160209185913-a97ce2ca70fa/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs983HfUfpkw9OTFD9tbBfAViHE= github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a h1:AA9vgIBDjMHPC2McaGPojgV2dcI78ZC0TLNhYCXEKH8= github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a/go.mod h1:lzZQ3Noex5pfAy7mkAeCjcBDteYU85uWWnJ/y6gKU8k= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.8.0/go.mod h1:PC/OgXc+UN7B4ALwvn1yzVZmVwvhXp5JsbBv6wSv6i0= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.9/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/samuel/go-thrift v0.0.0-20191111193933-5165175b40af h1:EiWVfh8mr40yFZEui2oF0d45KgH48PkB2H0Z0GANvSI= github.com/samuel/go-thrift v0.0.0-20191111193933-5165175b40af/go.mod h1:Vrkh1pnjV9Bl8c3P9zH0/D4NlOHWP5d4/hF4YTULaec= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 h1:7z3LSn867ex6VSaahyKadf4WtSsJIgne6A1WLOAGM8A= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/uber-common/bark v1.2.1/go.mod h1:g0ZuPcD7XiExKHynr93Q742G/sbrdVQkghrqLGOoFuY= github.com/uber-go/mapdecode v1.0.0 h1:euUEFM9KnuCa1OBixz1xM+FIXmpixyay5DLymceOVrU= github.com/uber-go/mapdecode v1.0.0/go.mod h1:b5nP15FwXTgpjTjeA9A2uTHXV5UJCl4arwKpP0FP1Hw= github.com/uber-go/tally v3.3.12+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU= github.com/uber-go/tally v3.3.15+incompatible h1:9hLSgNBP28CjIaDmAuRTq9qV+UZY+9PcvAkXO4nNMwg= github.com/uber-go/tally v3.3.15+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU= github.com/uber/cadence-idl v0.0.0-20211111101836-d6b70b60eb8c/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM= github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/uber/ringpop-go v0.8.5 h1:aBa/SHmmFRcAXA63k7uBheoTL8tCmH7L+OgktB1AF/o= github.com/uber/ringpop-go v0.8.5/go.mod h1:zVI6eGO6L7pG14GkntHsSOfmUAWQ7B4lvmzly4IT4ls= github.com/uber/tchannel-go v1.16.0/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo= github.com/uber/tchannel-go v1.22.2 h1:NKA5FVESYh6Ij6V+tujK+IFZnBKDyUHdsBY264UYhgk= github.com/uber/tchannel-go v1.22.2/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo= github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryBNl9eKOeqQ58Y/Qpo3Q9QNxKHX5uzzQ= github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/cadence v0.19.0 h1:EvDXwIJ0lAAxL2i8ne/vG/TeoJM6xkAyqgTRFmIWG+c= go.uber.org/cadence v0.19.0/go.mod h1:s91dOf0kcJbumPscRIVFV/4Xq/exhefzpXmnDiRRTxs= go.uber.org/config v1.4.0 h1:upnMPpMm6WlbZtXoasNkK4f0FhxwS+W4Iqz5oNznehQ= go.uber.org/config v1.4.0/go.mod h1:aCyrMHmUAc/s2h9sv1koP84M9ZF/4K+g2oleyESO/Ig= go.uber.org/dig v1.8.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= go.uber.org/dig v1.10.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= go.uber.org/dig v1.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw= go.uber.org/dig v1.18.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.10.0/go.mod h1:vLRicqpG/qQEzno4SYU86iCwfT95EZza+Eba0ItuxqY= go.uber.org/fx v1.13.1/go.mod h1:bREWhavnedxpJeTq9pQT53BbvwhUv7TcpsOqcH4a+3w= go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg= go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU= go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/net/metrics v1.3.0 h1:iRLPuVecNYf/wIV+mQaA4IgN8ghifu3q1B4IT6HfwyY= go.uber.org/net/metrics v1.3.0/go.mod h1:pEQrSDGNWT5IVpekWzee5//uHjI4gmgZFkobfw3bv8I= go.uber.org/thriftrw v1.25.0/go.mod h1:IcIfSeZgc59AlYb0xr0DlDKIdD7SgjnFpG9BXCPyy9g= go.uber.org/thriftrw v1.29.2 h1:pRuFLzbGvTcnYwGSjizWRHlbJUzGhu84sRiL1h1kUd8= go.uber.org/thriftrw v1.29.2/go.mod h1:YcjXveberDd28/Bs34SwHy3yu85x/jB4UA2gIcz/Eo0= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/yarpc v1.55.0/go.mod h1:V2JUPDWHYGNpvyuroYjf0KFjwvBCtcFJLuvZqv7TWA0= go.uber.org/yarpc v1.70.3 h1:yykHwzRD9/bgDtlOWoVuXbSZoU91Id2dWJO1CDSRHnI= go.uber.org/yarpc v1.70.3/go.mod h1:EH6I6K1HxBbOxZIJfhdDf+H+cvXPHmJyRvpfPqES20U= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.0.0-20170927054726-6dc17368e09b/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191104232314-dc038396d1f0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191226212025-6b505debf4bc/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117215004-fe56e6335763/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.128.0 h1:RjPESny5CnQRn9V6siglged+DZCgfu9l6mO9dkX9VOg= google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a h1:myvhA4is3vrit1a6NZCWBIwN0kNEnX21DJOJX/NvIfI= google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19 h1:WB265cn5OpO+hK3pikC9hpP1zI/KTwmyMFKloW9eOVc= gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34= honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= ================================================ FILE: common/archiver/gcloud/historyArchiver.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gcloud import ( "context" "encoding/binary" "errors" "path/filepath" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/gcloud/connector" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) var ( errUploadNonRetriable = errors.New("upload non-retriable error") ) const ( // URIScheme is the scheme for the gcloud storage implementation URIScheme = "gs" ConfigKey = "gstorage" targetHistoryBlobSize = 2 * 1024 * 1024 // 2MB errEncodeHistory = "failed to encode history batches" errBucketHistory = "failed to get google storage bucket handle" errWriteFile = "failed to write history to google storage" ) type historyArchiver struct { container *archiver.HistoryBootstrapContainer gcloudStorage connector.Client // only set in test code historyIterator archiver.HistoryIterator } type progress struct { CurrentPageNumber int IteratorState []byte } type getHistoryToken struct { CloseFailoverVersion int64 HighestPart int CurrentPart int BatchIdxOffset int } // NewHistoryArchiver creates a new gcloud storage HistoryArchiver func NewHistoryArchiver( container *archiver.HistoryBootstrapContainer, config connector.Config, ) (archiver.HistoryArchiver, error) { storage, err := connector.NewClient(context.Background(), config) if err == nil { return newHistoryArchiver(container, nil, storage), nil } return nil, err } func newHistoryArchiver( container *archiver.HistoryBootstrapContainer, historyIterator archiver.HistoryIterator, storage connector.Client, ) archiver.HistoryArchiver { return &historyArchiver{ container: container, gcloudStorage: storage, historyIterator: historyIterator, } } // Archive is used to archive a workflow history. When the context expires the method should stop trying to archive. // Implementors are free to archive however they want, including implementing retries of sub-operations. The URI defines // the resource that histories should be archived into. The implementor gets to determine how to interpret the URI. // The Archive method may or may not be automatically retried by the caller. The ArchiveOptions are used // to interact with these retries including giving the implementor the ability to cancel retries and record progress // between retry attempts. // This method will be invoked after a workflow passes its retention period. func (h *historyArchiver) Archive(ctx context.Context, URI archiver.URI, request *archiver.ArchiveHistoryRequest, opts ...archiver.ArchiveOption) (err error) { scope := h.container.MetricsClient.Scope(metrics.HistoryArchiverScope, metrics.DomainTag(request.DomainName)) featureCatalog := archiver.GetFeatureCatalog(opts...) sw := scope.StartTimer(metrics.CadenceLatency) defer func() { sw.Stop() if err != nil { if err.Error() != errUploadNonRetriable.Error() { scope.IncCounter(metrics.HistoryArchiverArchiveTransientErrorCount) return } scope.IncCounter(metrics.HistoryArchiverArchiveNonRetryableErrorCount) if featureCatalog.NonRetriableError != nil { err = featureCatalog.NonRetriableError() } } }() logger := archiver.TagLoggerWithArchiveHistoryRequestAndURI(h.container.Logger, request, URI.String()) if err := h.ValidateURI(URI); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidURI), tag.Error(err)) return errUploadNonRetriable } if err := archiver.ValidateHistoryArchiveRequest(request); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidArchiveRequest), tag.Error(err)) return errUploadNonRetriable } var totalUploadSize int64 historyIterator := h.historyIterator var progress progress if historyIterator == nil { // will only be set by testing code historyIterator, _ = loadHistoryIterator(ctx, request, h.container.HistoryV2Manager, featureCatalog, &progress) } for historyIterator.HasNext() { part := progress.CurrentPageNumber historyBlob, err := getNextHistoryBlob(ctx, historyIterator) if err != nil { if common.IsEntityNotExistsError(err) { // workflow history no longer exists, may due to duplicated archival signal // this may happen even in the middle of iterating history as two archival signals // can be processed concurrently. logger.Info(archiver.ArchiveSkippedInfoMsg) scope.IncCounter(metrics.HistoryArchiverDuplicateArchivalsCount) return nil } logger := logger.WithTags(tag.ArchivalArchiveFailReason(archiver.ErrReasonReadHistory), tag.Error(err)) if !persistence.IsTransientError(err) { logger.Error(archiver.ArchiveNonRetriableErrorMsg) return errUploadNonRetriable } logger.Error(archiver.ArchiveTransientErrorMsg) return err } if archiver.IsHistoryMutated(request, historyBlob.Body, *historyBlob.Header.IsLast, logger) { if !featureCatalog.ArchiveIncompleteHistory() { return archiver.ErrHistoryMutated } } encodedHistoryPart, err := encode(historyBlob.Body) if err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(errEncodeHistory), tag.Error(err)) return errUploadNonRetriable } filename := constructHistoryFilenameMultipart(request.DomainID, request.WorkflowID, request.RunID, request.CloseFailoverVersion, part) if exist, _ := h.gcloudStorage.Exist(ctx, URI, filename); !exist { if err := h.gcloudStorage.Upload(ctx, URI, filename, encodedHistoryPart); err != nil { logger.Error(archiver.ArchiveTransientErrorMsg, tag.ArchivalArchiveFailReason(errWriteFile), tag.Error(err)) scope.IncCounter(metrics.HistoryArchiverArchiveTransientErrorCount) return err } totalUploadSize = totalUploadSize + int64(binary.Size(encodedHistoryPart)) } saveHistoryIteratorState(ctx, featureCatalog, historyIterator, part, &progress) } scope.AddCounter(metrics.HistoryArchiverTotalUploadSize, totalUploadSize) scope.AddCounter(metrics.HistoryArchiverHistorySize, totalUploadSize) scope.IncCounter(metrics.HistoryArchiverArchiveSuccessCount) return } // Get is used to access an archived history. When context expires method should stop trying to fetch history. // The URI identifies the resource from which history should be accessed and it is up to the implementor to interpret this URI. // This method should thrift errors - see filestore as an example. func (h *historyArchiver) Get(ctx context.Context, URI archiver.URI, request *archiver.GetHistoryRequest) (*archiver.GetHistoryResponse, error) { err := h.ValidateURI(URI) if err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidURI.Error()} } if err := archiver.ValidateGetRequest(request); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidGetHistoryRequest.Error()} } var token *getHistoryToken if request.NextPageToken != nil { token, err = deserializeGetHistoryToken(request.NextPageToken) if err != nil { return nil, &types.BadRequestError{Message: archiver.ErrNextPageTokenCorrupted.Error()} } } else { highestVersion, historyhighestPart, historyCurrentPart, err := h.getHighestVersion(ctx, URI, request) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } token = &getHistoryToken{ CloseFailoverVersion: *highestVersion, HighestPart: *historyhighestPart, CurrentPart: *historyCurrentPart, BatchIdxOffset: 0, } } response := &archiver.GetHistoryResponse{} response.HistoryBatches = []*types.History{} numOfEvents := 0 outer: for token.CurrentPart <= token.HighestPart { filename := constructHistoryFilenameMultipart(request.DomainID, request.WorkflowID, request.RunID, token.CloseFailoverVersion, token.CurrentPart) encodedHistoryBatches, err := h.gcloudStorage.Get(ctx, URI, filename) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } if encodedHistoryBatches == nil { return nil, &types.InternalServiceError{Message: "Fail retrieving history file: " + URI.String() + "/" + filename} } batches, err := decodeHistoryBatches(encodedHistoryBatches) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } // trim the batches in the beginning based on token.BatchIdxOffset batches = batches[token.BatchIdxOffset:] for idx, batch := range batches { response.HistoryBatches = append(response.HistoryBatches, batch) token.BatchIdxOffset++ numOfEvents += len(batch.Events) if numOfEvents >= request.PageSize { if idx == len(batches)-1 { // handle the edge case where page size is meeted after adding the last batch token.BatchIdxOffset = 0 token.CurrentPart++ } break outer } } // reset the offset to 0 as we will read a new page token.BatchIdxOffset = 0 token.CurrentPart++ } if token.CurrentPart <= token.HighestPart { nextToken, err := serializeToken(token) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } response.NextPageToken = nextToken } return response, nil } // ValidateURI is used to define what a valid URI for an implementation is. func (h *historyArchiver) ValidateURI(URI archiver.URI) (err error) { if err = h.validateURI(URI); err == nil { _, err = h.gcloudStorage.Exist(context.Background(), URI, "") } return } func (h *historyArchiver) validateURI(URI archiver.URI) (err error) { if URI.Scheme() != URIScheme { return archiver.ErrURISchemeMismatch } if URI.Path() == "" || URI.Hostname() == "" { return archiver.ErrInvalidURI } return } func getNextHistoryBlob(ctx context.Context, historyIterator archiver.HistoryIterator) (*archiver.HistoryBlob, error) { historyBlob, err := historyIterator.Next() op := func(ctx context.Context) error { historyBlob, err = historyIterator.Next() return err } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(common.CreatePersistenceRetryPolicy()), backoff.WithRetryableError(persistence.IsTransientError), ) for err != nil { if contextExpired(ctx) { return nil, archiver.ErrContextTimeout } if !persistence.IsTransientError(err) { return nil, err } err = throttleRetry.Do(ctx, op) } return historyBlob, nil } // with XDC(global domain) concept, archival may write different history with the same RunID, with different failoverVersion. // In that case, the history/runID with the highest failoverVersion wins. // getHighestVersion look up all archived files to find the highest failoverVersion. // Since a history is written into different parts in this archival implementation, it also returns the highest and lowest partVersionID. func (h *historyArchiver) getHighestVersion(ctx context.Context, URI archiver.URI, request *archiver.GetHistoryRequest) (*int64, *int, *int, error) { filenames, err := h.gcloudStorage.Query(ctx, URI, constructHistoryFilenamePrefix(request.DomainID, request.WorkflowID, request.RunID)) if err != nil { return nil, nil, nil, err } var highestVersion *int64 var highestVersionPart *int var lowestVersionPart *int for _, filename := range filenames { version, partVersionID, err := extractCloseFailoverVersion(filepath.Base(filename)) if err != nil || (request.CloseFailoverVersion != nil && version != *request.CloseFailoverVersion) { continue } if highestVersion == nil || version > *highestVersion { highestVersion = &version highestVersionPart = new(int) lowestVersionPart = new(int) } if *highestVersion == version { if highestVersionPart == nil || partVersionID > *highestVersionPart { highestVersionPart = &partVersionID } if lowestVersionPart == nil || partVersionID < *lowestVersionPart { lowestVersionPart = &partVersionID } } } if highestVersion == nil { return nil, nil, nil, archiver.ErrHistoryNotExist } return highestVersion, highestVersionPart, lowestVersionPart, nil } func loadHistoryIterator(ctx context.Context, request *archiver.ArchiveHistoryRequest, historyManager persistence.HistoryManager, featureCatalog *archiver.ArchiveFeatureCatalog, progress *progress) (historyIterator archiver.HistoryIterator, err error) { defer func() { if err != nil || historyIterator == nil { historyIterator, err = archiver.NewHistoryIteratorFromState(ctx, request, historyManager, targetHistoryBlobSize, nil) } }() if featureCatalog.ProgressManager != nil { if featureCatalog.ProgressManager.HasProgress(ctx) { err = featureCatalog.ProgressManager.LoadProgress(ctx, &progress) if err == nil { historyIterator, err = archiver.NewHistoryIteratorFromState(ctx, request, historyManager, targetHistoryBlobSize, progress.IteratorState) } } } return } func saveHistoryIteratorState(ctx context.Context, featureCatalog *archiver.ArchiveFeatureCatalog, historyIterator archiver.HistoryIterator, currentPartNum int, progress *progress) (err error) { var state []byte if featureCatalog.ProgressManager != nil { state, err = historyIterator.GetState() if err == nil { progress.CurrentPageNumber = currentPartNum + 1 progress.IteratorState = state err = featureCatalog.ProgressManager.RecordProgress(ctx, progress) } } return err } ================================================ FILE: common/archiver/gcloud/historyArchiver_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gcloud import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/gcloud/connector" "github.com/uber/cadence/common/archiver/gcloud/connector/mocks" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) const ( testDomainID = "test-domain-id" testDomainName = "test-domain-name" testWorkflowID = "test-workflow-id" testRunID = "test-run-id" testNextEventID = 1800 testCloseFailoverVersion = 100 testPageSize = 100 exampleHistoryRecord = `[{"events":[{"eventId":1,"timestamp":1576800090315103000,"eventType":"WorkflowExecutionStarted","version":-24,"taskId":5242897,"workflowExecutionStartedEventAttributes":{"workflowType":{"name":"MobileOnlyWorkflow::processMobileOnly"},"taskList":{"name":"MobileOnly"},"input":"eyJkbmkiOiI0ODY5NGJmZi04MTU2LTRjZDEtYTYzZi0wZTM0ZDBlYzljMWEiLCJjYXRlZ29yeSI6InVwZGF0ZV9jbGllbnQiLCJhZ2UiOjE4LCJzY29yZSI6MCwic3RhdHVzIjoicGVuZGluZyIsImRlc2NyaXB0aW9uIjoiTG9yZW0gSXBzdW0gaXMgc2ltcGx5IGR1bW15IHRleHQgb2YgdGhlIHByaW50aW5nIGFuZCB0eXBlc2V0dGluZyBpbmR1c3RyeS4gTG9yZW0gSXBzdW0gaGFzIGJlZW4gdGhlIGluZHVzdHJ5XHUwMDI3cyBzdGFuZGFyZCBkdW1teSB0ZXh0IGV2ZXIgc2luY2UgdGhlIDE1MDBzLCB3aGVuIGFuIHVua25vd24gcHJpbnRlciB0b29rIGEgZ2FsbGV5IG9mIHR5cGUgYW5kIHNjcmFtYmxlZCBpdCB0byBtYWtlIGEgdHlwZSBzcGVjaW1lbiBib29rLkl0IGhhcyBzdXJ2aXZlZCBub3Qgb25seSBmaXZlIGNlbnR1cmllcywgYnV0IGFsc28gdGhlIGxlYXAgaW50byBlbGVjdHJvbmljIHR5cGVzZXR0aW5nLCByZW1haW5pbmcgZXNzZW50aWFsbHkgdW5jaGFuZ2VkLkl0IHdhcyBwb3B1bGFyaXNlZCBpbiB0aGUgMTk2MHMgd2l0aCB0aGUgcmVsZWFzZSBvZiBMZXRyYXNldCBzaGVldHMgY29udGFpbmluZyBMb3JlbSBJcHN1bSBwYXNzYWdlcywgYW5kIG1vcmUgcmVjZW50bHkgd2l0aCBkZXNrdG9wIHB1Ymxpc2hpbmcgc29mdHdhcmUgbGlrZSBBbGR1cyBQYWdlTWFrZXIgaW5jbHVkaW5nIHZlcnNpb25zIG9mIExvcmVtIElwc3VtLkl0IGlzIGEgbG9uZyBlc3RhYmxpc2hlZCBmYWN0IHRoYXQgYSByZWFkZXIgd2lsbCBiZSBkaXN0cmFjdGVkIGJ5IHRoZSByZWFkYWJsZSBjb250ZW50IG9mIGEgcGFnZSB3aGVuIGxvb2tpbmcgYXQgaXRzIGxheW91dC4gVGhlIHBvaW50IG9mIHVzaW5nIExvcmVtIElwc3VtIGlzIHRoYXQgaXQgaGFzIGEgbW9yZS1vci1sZXNzIG5vcm1hbCBkaXN0cmlidXRpb24gb2YgbGV0dGVycywgYXMgb3Bwb3NlZCB0byB1c2luZyBcdTAwMjdDb250ZW50IGhlcmUsIGNvbnRlbnQgaGVyZVx1MDAyNywgbWFraW5nIGl0IGxvb2sgbGlrZSByZWFkYWJsZSBFbmdsaXNoLiJ9","executionStartToCloseTimeoutSeconds":300,"taskStartToCloseTimeoutSeconds":60,"originalExecutionRunId":"1fd5d4c8-1590-4a0a-8027-535e8729de8e","identity":"","firstExecutionRunId":"1fd5d4c8-1590-4a0a-8027-535e8729de8e","attempt":0,"firstDecisionTaskBackoffSeconds":0}}]}]` twoEventsExampleHistoryRecord = `[{"events":[{"eventId":1,"timestamp":1576800090315103000,"eventType":"WorkflowExecutionStarted","version":-24,"taskId":5242897,"workflowExecutionStartedEventAttributes":{"workflowType":{"name":"MobileOnlyWorkflow::processMobileOnly"},"taskList":{"name":"MobileOnly"},"input":"eyJkbmkiOiI0ODY5NGJmZi04MTU2LTRjZDEtYTYzZi0wZTM0ZDBlYzljMWEiLCJjYXRlZ29yeSI6InVwZGF0ZV9jbGllbnQiLCJhZ2UiOjE4LCJzY29yZSI6MCwic3RhdHVzIjoicGVuZGluZyIsImRlc2NyaXB0aW9uIjoiTG9yZW0gSXBzdW0gaXMgc2ltcGx5IGR1bW15IHRleHQgb2YgdGhlIHByaW50aW5nIGFuZCB0eXBlc2V0dGluZyBpbmR1c3RyeS4gTG9yZW0gSXBzdW0gaGFzIGJlZW4gdGhlIGluZHVzdHJ5XHUwMDI3cyBzdGFuZGFyZCBkdW1teSB0ZXh0IGV2ZXIgc2luY2UgdGhlIDE1MDBzLCB3aGVuIGFuIHVua25vd24gcHJpbnRlciB0b29rIGEgZ2FsbGV5IG9mIHR5cGUgYW5kIHNjcmFtYmxlZCBpdCB0byBtYWtlIGEgdHlwZSBzcGVjaW1lbiBib29rLkl0IGhhcyBzdXJ2aXZlZCBub3Qgb25seSBmaXZlIGNlbnR1cmllcywgYnV0IGFsc28gdGhlIGxlYXAgaW50byBlbGVjdHJvbmljIHR5cGVzZXR0aW5nLCByZW1haW5pbmcgZXNzZW50aWFsbHkgdW5jaGFuZ2VkLkl0IHdhcyBwb3B1bGFyaXNlZCBpbiB0aGUgMTk2MHMgd2l0aCB0aGUgcmVsZWFzZSBvZiBMZXRyYXNldCBzaGVldHMgY29udGFpbmluZyBMb3JlbSBJcHN1bSBwYXNzYWdlcywgYW5kIG1vcmUgcmVjZW50bHkgd2l0aCBkZXNrdG9wIHB1Ymxpc2hpbmcgc29mdHdhcmUgbGlrZSBBbGR1cyBQYWdlTWFrZXIgaW5jbHVkaW5nIHZlcnNpb25zIG9mIExvcmVtIElwc3VtLkl0IGlzIGEgbG9uZyBlc3RhYmxpc2hlZCBmYWN0IHRoYXQgYSByZWFkZXIgd2lsbCBiZSBkaXN0cmFjdGVkIGJ5IHRoZSByZWFkYWJsZSBjb250ZW50IG9mIGEgcGFnZSB3aGVuIGxvb2tpbmcgYXQgaXRzIGxheW91dC4gVGhlIHBvaW50IG9mIHVzaW5nIExvcmVtIElwc3VtIGlzIHRoYXQgaXQgaGFzIGEgbW9yZS1vci1sZXNzIG5vcm1hbCBkaXN0cmlidXRpb24gb2YgbGV0dGVycywgYXMgb3Bwb3NlZCB0byB1c2luZyBcdTAwMjdDb250ZW50IGhlcmUsIGNvbnRlbnQgaGVyZVx1MDAyNywgbWFraW5nIGl0IGxvb2sgbGlrZSByZWFkYWJsZSBFbmdsaXNoLiJ9","executionStartToCloseTimeoutSeconds":300,"taskStartToCloseTimeoutSeconds":60,"originalExecutionRunId":"1fd5d4c8-1590-4a0a-8027-535e8729de8e","identity":"","firstExecutionRunId":"1fd5d4c8-1590-4a0a-8027-535e8729de8e","attempt":0,"firstDecisionTaskBackoffSeconds":0}}, {"eventId":2,"timestamp":1576800090315103000,"eventType":"WorkflowExecutionStarted","version":-24,"taskId":5242897,"workflowExecutionStartedEventAttributes":{"workflowType":{"name":"MobileOnlyWorkflow::processMobileOnly"},"taskList":{"name":"MobileOnly"},"input":"eyJkbmkiOiI0ODY5NGJmZi04MTU2LTRjZDEtYTYzZi0wZTM0ZDBlYzljMWEiLCJjYXRlZ29yeSI6InVwZGF0ZV9jbGllbnQiLCJhZ2UiOjE4LCJzY29yZSI6MCwic3RhdHVzIjoicGVuZGluZyIsImRlc2NyaXB0aW9uIjoiTG9yZW0gSXBzdW0gaXMgc2ltcGx5IGR1bW15IHRleHQgb2YgdGhlIHByaW50aW5nIGFuZCB0eXBlc2V0dGluZyBpbmR1c3RyeS4gTG9yZW0gSXBzdW0gaGFzIGJlZW4gdGhlIGluZHVzdHJ5XHUwMDI3cyBzdGFuZGFyZCBkdW1teSB0ZXh0IGV2ZXIgc2luY2UgdGhlIDE1MDBzLCB3aGVuIGFuIHVua25vd24gcHJpbnRlciB0b29rIGEgZ2FsbGV5IG9mIHR5cGUgYW5kIHNjcmFtYmxlZCBpdCB0byBtYWtlIGEgdHlwZSBzcGVjaW1lbiBib29rLkl0IGhhcyBzdXJ2aXZlZCBub3Qgb25seSBmaXZlIGNlbnR1cmllcywgYnV0IGFsc28gdGhlIGxlYXAgaW50byBlbGVjdHJvbmljIHR5cGVzZXR0aW5nLCByZW1haW5pbmcgZXNzZW50aWFsbHkgdW5jaGFuZ2VkLkl0IHdhcyBwb3B1bGFyaXNlZCBpbiB0aGUgMTk2MHMgd2l0aCB0aGUgcmVsZWFzZSBvZiBMZXRyYXNldCBzaGVldHMgY29udGFpbmluZyBMb3JlbSBJcHN1bSBwYXNzYWdlcywgYW5kIG1vcmUgcmVjZW50bHkgd2l0aCBkZXNrdG9wIHB1Ymxpc2hpbmcgc29mdHdhcmUgbGlrZSBBbGR1cyBQYWdlTWFrZXIgaW5jbHVkaW5nIHZlcnNpb25zIG9mIExvcmVtIElwc3VtLkl0IGlzIGEgbG9uZyBlc3RhYmxpc2hlZCBmYWN0IHRoYXQgYSByZWFkZXIgd2lsbCBiZSBkaXN0cmFjdGVkIGJ5IHRoZSByZWFkYWJsZSBjb250ZW50IG9mIGEgcGFnZSB3aGVuIGxvb2tpbmcgYXQgaXRzIGxheW91dC4gVGhlIHBvaW50IG9mIHVzaW5nIExvcmVtIElwc3VtIGlzIHRoYXQgaXQgaGFzIGEgbW9yZS1vci1sZXNzIG5vcm1hbCBkaXN0cmlidXRpb24gb2YgbGV0dGVycywgYXMgb3Bwb3NlZCB0byB1c2luZyBcdTAwMjdDb250ZW50IGhlcmUsIGNvbnRlbnQgaGVyZVx1MDAyNywgbWFraW5nIGl0IGxvb2sgbGlrZSByZWFkYWJsZSBFbmdsaXNoLiJ9","executionStartToCloseTimeoutSeconds":300,"taskStartToCloseTimeoutSeconds":60,"originalExecutionRunId":"1fd5d4c8-1590-4a0a-8027-535e8729de8e","identity":"","firstExecutionRunId":"1fd5d4c8-1590-4a0a-8027-535e8729de8e","attempt":0,"firstDecisionTaskBackoffSeconds":0}}]}]` ) var ( testBranchToken = []byte{1, 2, 3} ) func (h *historyArchiverSuite) SetupTest() { h.Assertions = require.New(h.T()) h.container = &archiver.HistoryBootstrapContainer{ Logger: testlogger.New(h.T()), MetricsClient: metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), } h.testArchivalURI, _ = archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") } func TestHistoryArchiverSuite(t *testing.T) { suite.Run(t, new(historyArchiverSuite)) } type historyArchiverSuite struct { *require.Assertions suite.Suite container *archiver.HistoryBootstrapContainer testArchivalURI archiver.URI } func getCanceledContext() context.Context { ctx, cancel := context.WithCancel(context.Background()) cancel() return ctx } func (h *historyArchiverSuite) TestValidateURI() { ctx := context.Background() testCases := []struct { URI string expectedErr error }{ { URI: "wrongscheme:///a/b/c", expectedErr: archiver.ErrURISchemeMismatch, }, { URI: "gs:my-bucket-cad/cadence_archival/development", expectedErr: archiver.ErrInvalidURI, }, { URI: "gs://", expectedErr: archiver.ErrInvalidURI, }, { URI: "gs://my-bucket-cad", expectedErr: archiver.ErrInvalidURI, }, { URI: "gs:/my-bucket-cad/cadence_archival/development", expectedErr: archiver.ErrInvalidURI, }, { URI: "gs://my-bucket-cad/cadence_archival/development", expectedErr: nil, }, } storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, mock.Anything, "").Return(false, nil) historyArchiver := new(historyArchiver) historyArchiver.gcloudStorage = storageWrapper for _, tc := range testCases { URI, err := archiver.NewURI(tc.URI) h.NoError(err) h.Equal(tc.expectedErr, historyArchiver.ValidateURI(URI)) } } func (h *historyArchiverSuite) TestArchive_Fail_InvalidURI() { mockStorageClient := &mocks.GcloudStorageClient{} storageWrapper, _ := connector.NewClientWithParams(mockStorageClient) mockCtrl := gomock.NewController(h.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } URI, err := archiver.NewURI("wrongscheme://") h.NoError(err) err = historyArchiver.Archive(context.Background(), URI, request) h.Error(err) } func (h *historyArchiverSuite) TestArchive_Fail_InvalidRequest() { ctx := context.Background() URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") h.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, URI, "").Return(true, nil).Times(1) mockCtrl := gomock.NewController(h.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: "", RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err = historyArchiver.Archive(ctx, h.testArchivalURI, request) h.Error(err) } func (h *historyArchiverSuite) TestArchive_Fail_ErrorOnReadHistory() { ctx := context.Background() URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") h.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, URI, "").Return(true, nil).Times(1) mockCtrl := gomock.NewController(h.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, errors.New("some random error")), ) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err = historyArchiver.Archive(ctx, h.testArchivalURI, request) h.Error(err) } func (h *historyArchiverSuite) TestArchive_Fail_TimeoutWhenReadingHistory() { ctx := getCanceledContext() mockCtrl := gomock.NewController(h.T()) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", mock.Anything, mock.Anything, "").Return(true, nil).Times(1) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, &types.ServiceBusyError{}), ) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err := historyArchiver.Archive(ctx, h.testArchivalURI, request) h.Error(err) } func (h *historyArchiverSuite) TestArchive_Fail_HistoryMutated() { ctx := context.Background() mockCtrl := gomock.NewController(h.T()) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") h.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, URI, "").Return(true, nil).Times(1) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyBatches := []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion + 1, }, }, }, } historyBlob := &archiver.HistoryBlob{ Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(true), }, Body: historyBatches, } gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(historyBlob, nil), ) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err = historyArchiver.Archive(ctx, h.testArchivalURI, request) h.Error(err) } func (h *historyArchiverSuite) TestArchive_Fail_NonRetriableErrorOption() { ctx := context.Background() mockCtrl := gomock.NewController(h.T()) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") h.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, URI, "").Return(true, nil).Times(1) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, errors.New("upload non-retriable error")), ) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err = historyArchiver.Archive(ctx, h.testArchivalURI, request, archiver.GetNonRetriableErrorOption(errUploadNonRetriable)) h.Equal(errUploadNonRetriable, err) } func (h *historyArchiverSuite) TestArchive_Skip() { ctx := context.Background() mockCtrl := gomock.NewController(h.T()) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") h.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, URI, mock.Anything).Return(true, nil) storageWrapper.On("Upload", ctx, URI, mock.Anything, mock.Anything).Return(nil) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyBlob := &archiver.HistoryBlob{ Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(false), }, Body: []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, }, } gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(historyBlob, nil), historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, &types.EntityNotExistsError{Message: "workflow not found"}), ) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err = historyArchiver.Archive(ctx, h.testArchivalURI, request) h.NoError(err) } func (h *historyArchiverSuite) TestArchive_Success() { ctx := context.Background() mockCtrl := gomock.NewController(h.T()) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, URI, mock.Anything).Return(false, nil) storageWrapper.On("Upload", ctx, URI, mock.Anything, mock.Anything).Return(nil) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyBatches := []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, { ID: constants.FirstEventID + 2, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, { Events: []*types.HistoryEvent{ { ID: testNextEventID - 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, } historyBlob := &archiver.HistoryBlob{ Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(true), }, Body: historyBatches, } gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(historyBlob, nil), historyIterator.EXPECT().HasNext().Return(false), ) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } h.NoError(err) err = historyArchiver.Archive(ctx, URI, request) h.NoError(err) } func (h *historyArchiverSuite) TestGet_Fail_InvalidURI() { ctx := context.Background() mockCtrl := gomock.NewController(h.T()) mockStorageClient := &mocks.GcloudStorageClient{} storageWrapper, _ := connector.NewClientWithParams(mockStorageClient) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: 100, } URI, err := archiver.NewURI("wrongscheme://") h.NoError(err) response, err := historyArchiver.Get(ctx, URI, request) h.Nil(response) h.Error(err) } func (h *historyArchiverSuite) TestGet_Fail_InvalidToken() { ctx := context.Background() mockCtrl := gomock.NewController(h.T()) mockStorageClient := &mocks.GcloudStorageClient{} storageWrapper, _ := connector.NewClientWithParams(mockStorageClient) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, NextPageToken: []byte{'r', 'a', 'n', 'd', 'o', 'm'}, } URI, err := archiver.NewURI("gs:///") h.NoError(err) response, err := historyArchiver.Get(ctx, URI, request) h.Nil(response) h.Error(err) h.IsType(&types.BadRequestError{}, err) } func (h *historyArchiverSuite) TestGet_Success_PickHighestVersion() { ctx := context.Background() mockCtrl := gomock.NewController(h.T()) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, URI, "").Return(true, nil).Times(1) storageWrapper.On("Query", ctx, URI, mock.Anything).Return([]string{"905702227796330300141628222723188294514017512010591354159_-24_0.history", "905702227796330300141628222723188294514017512010591354159_-25_0.history"}, nil).Times(1) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_0.history").Return([]byte(exampleHistoryRecord), nil) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, } h.NoError(err) response, err := historyArchiver.Get(ctx, URI, request) h.NoError(err) h.Nil(response.NextPageToken) } func (h *historyArchiverSuite) TestGet_Success_UseProvidedVersion() { ctx := context.Background() mockCtrl := gomock.NewController(h.T()) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, URI, "").Return(true, nil).Times(1) storageWrapper.On("Query", ctx, URI, "71817125141568232911739672280485489488911532452831150339470").Return([]string{"905702227796330300141628222723188294514017512010591354159_-24_0.history", "905702227796330300141628222723188294514017512010591354159_-25_0.history"}, nil).Times(1) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-25_0.history").Return([]byte(exampleHistoryRecord), nil) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, CloseFailoverVersion: common.Int64Ptr(-25), } h.NoError(err) response, err := historyArchiver.Get(ctx, URI, request) h.NoError(err) h.Nil(response.NextPageToken) } func (h *historyArchiverSuite) TestGet_Success_PageSize() { ctx := context.Background() mockCtrl := gomock.NewController(h.T()) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, URI, "").Return(true, nil).Times(1) storageWrapper.On("Query", ctx, URI, "71817125141568232911739672280485489488911532452831150339470").Return([]string{"905702227796330300141628222723188294514017512010591354159_-24_0.history", "905702227796330300141628222723188294514017512010591354159_-24_1.history", "905702227796330300141628222723188294514017512010591354159_-24_2.history", "905702227796330300141628222723188294514017512010591354159_-24_3.history", "905702227796330300141628222723188294514017512010591354159_-25_0.history"}, nil).Times(1) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_0.history").Return([]byte(exampleHistoryRecord), nil) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_1.history").Return([]byte(exampleHistoryRecord), nil) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_2.history").Return([]byte(exampleHistoryRecord), nil) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_3.history").Return([]byte(exampleHistoryRecord), nil) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: 2, } h.NoError(err) response, err := historyArchiver.Get(ctx, URI, request) h.NoError(err) h.NotNil(response.NextPageToken) h.EqualValues(len(response.HistoryBatches), 2) } func (h *historyArchiverSuite) TestGet_Success_FromToken() { ctx := context.Background() mockCtrl := gomock.NewController(h.T()) URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/development") h.Require().NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", ctx, URI, "").Return(true, nil).Times(1) storageWrapper.On("Query", ctx, URI, "71817125141568232911739672280485489488911532452831150339470").Return([]string{"905702227796330300141628222723188294514017512010591354159_-24_0.history", "905702227796330300141628222723188294514017512010591354159_-24_1.history", "905702227796330300141628222723188294514017512010591354159_-24_2.history", "905702227796330300141628222723188294514017512010591354159_-24_3.history", "905702227796330300141628222723188294514017512010591354159_-25_0.history"}, nil).Times(1) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_0.history").Return([]byte(exampleHistoryRecord), nil) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_1.history").Return([]byte(exampleHistoryRecord), nil) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_2.history").Return([]byte(exampleHistoryRecord), nil) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_3.history").Return([]byte(twoEventsExampleHistoryRecord), nil) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_4.history").Return([]byte(exampleHistoryRecord), nil) storageWrapper.On("Get", ctx, URI, "71817125141568232911739672280485489488911532452831150339470_-24_5.history").Return([]byte(exampleHistoryRecord), nil) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyArchiver := newHistoryArchiver(h.container, historyIterator, storageWrapper) token := &getHistoryToken{ CloseFailoverVersion: -24, HighestPart: 5, CurrentPart: 2, BatchIdxOffset: 0, } nextPageToken, err := serializeToken(token) h.NoError(err) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: 4, NextPageToken: nextPageToken, } h.NoError(err) response, err := historyArchiver.Get(ctx, URI, request) h.NoError(err) h.NotNil(response.NextPageToken) token, err = deserializeGetHistoryToken(response.NextPageToken) h.NoError(err) h.EqualValues(5, token.HighestPart) h.EqualValues(5, token.CurrentPart) h.EqualValues(3, len(response.HistoryBatches)) numOfEvents := 0 for _, batch := range response.HistoryBatches { numOfEvents += len(batch.Events) } h.EqualValues(4, numOfEvents) } ================================================ FILE: common/archiver/gcloud/init.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package gcloud import ( "fmt" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/gcloud/connector" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/config" ) func init() { // register default providers, ideally remove this and trigger manually during startup must := func(err error) { if err != nil { panic(fmt.Errorf("failed to register gcloud archivers: %w", err)) } } must(provider.RegisterHistoryArchiver(URIScheme, ConfigKey, func(cfg *config.YamlNode, container *archiver.HistoryBootstrapContainer) (archiver.HistoryArchiver, error) { var out connector.Config if err := cfg.Decode(&out); err != nil { return nil, fmt.Errorf("bad config: %w", err) } return NewHistoryArchiver(container, out) })) must(provider.RegisterVisibilityArchiver(URIScheme, ConfigKey, func(cfg *config.YamlNode, container *archiver.VisibilityBootstrapContainer) (archiver.VisibilityArchiver, error) { var out connector.Config if err := cfg.Decode(&out); err != nil { return nil, fmt.Errorf("bad config: %w", err) } return NewVisibilityArchiver(container, out) })) } ================================================ FILE: common/archiver/gcloud/queryParser.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source queryParser.go -destination queryParser_mock.go -mock_names Interface=MockQueryParser package gcloud import ( "errors" "fmt" "strconv" "time" "github.com/xwb1989/sqlparser" "github.com/uber/cadence/common" ) type ( // QueryParser parses a limited SQL where clause into a struct QueryParser interface { Parse(query string) (*parsedQuery, error) } queryParser struct{} parsedQuery struct { workflowID *string workflowType *string startTime int64 closeTime int64 searchPrecision *string runID *string emptyResult bool } ) // All allowed fields for filtering const ( WorkflowID = "WorkflowID" RunID = "RunID" WorkflowType = "WorkflowType" CloseTime = "CloseTime" StartTime = "StartTime" CloseStatus = "CloseStatus" SearchPrecision = "SearchPrecision" ) // Precision specific values const ( PrecisionDay = "Day" PrecisionHour = "Hour" PrecisionMinute = "Minute" PrecisionSecond = "Second" ) const ( queryTemplate = "select * from dummy where %s" defaultDateTimeFormat = time.RFC3339 ) // NewQueryParser creates a new query parser for filestore func NewQueryParser() QueryParser { return &queryParser{} } func (p *queryParser) Parse(query string) (*parsedQuery, error) { stmt, err := sqlparser.Parse(fmt.Sprintf(queryTemplate, query)) if err != nil { return nil, err } whereExpr := stmt.(*sqlparser.Select).Where.Expr parsedQuery := &parsedQuery{} if err := p.convertWhereExpr(whereExpr, parsedQuery); err != nil { return nil, err } if (parsedQuery.closeTime == 0 && parsedQuery.startTime == 0) || (parsedQuery.closeTime != 0 && parsedQuery.startTime != 0) { return nil, errors.New("requires a StartTime or CloseTime") } if parsedQuery.searchPrecision == nil { return nil, errors.New("SearchPrecision is required when searching for a StartTime or CloseTime") } return parsedQuery, nil } func (p *queryParser) convertWhereExpr(expr sqlparser.Expr, parsedQuery *parsedQuery) error { if expr == nil { return errors.New("where expression is nil") } switch expr := expr.(type) { case *sqlparser.ComparisonExpr: return p.convertComparisonExpr(expr, parsedQuery) case *sqlparser.AndExpr: return p.convertAndExpr(expr, parsedQuery) case *sqlparser.ParenExpr: return p.convertParenExpr(expr, parsedQuery) default: return errors.New("only comparison and \"and\" expression is supported") } } func (p *queryParser) convertParenExpr(parenExpr *sqlparser.ParenExpr, parsedQuery *parsedQuery) error { return p.convertWhereExpr(parenExpr.Expr, parsedQuery) } func (p *queryParser) convertAndExpr(andExpr *sqlparser.AndExpr, parsedQuery *parsedQuery) error { if err := p.convertWhereExpr(andExpr.Left, parsedQuery); err != nil { return err } return p.convertWhereExpr(andExpr.Right, parsedQuery) } func (p *queryParser) convertComparisonExpr(compExpr *sqlparser.ComparisonExpr, parsedQuery *parsedQuery) error { colName, ok := compExpr.Left.(*sqlparser.ColName) if !ok { return fmt.Errorf("invalid filter name: %s", sqlparser.String(compExpr.Left)) } colNameStr := sqlparser.String(colName) op := compExpr.Operator valExpr, ok := compExpr.Right.(*sqlparser.SQLVal) if !ok { return fmt.Errorf("invalid value: %s", sqlparser.String(compExpr.Right)) } valStr := sqlparser.String(valExpr) switch colNameStr { case WorkflowID: val, err := extractStringValue(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Google Cloud Storage", WorkflowID) } if parsedQuery.workflowID != nil && *parsedQuery.workflowID != val { parsedQuery.emptyResult = true return nil } parsedQuery.workflowID = common.StringPtr(val) case RunID: val, err := extractStringValue(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Google Cloud Storage", RunID) } if parsedQuery.runID != nil && *parsedQuery.runID != val { parsedQuery.emptyResult = true return nil } parsedQuery.runID = common.StringPtr(val) case CloseTime: timestamp, err := convertToTimestamp(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Google Cloud Storage", CloseTime) } parsedQuery.closeTime = timestamp case StartTime: timestamp, err := convertToTimestamp(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Google Cloud Storage", StartTime) } parsedQuery.startTime = timestamp case WorkflowType: val, err := extractStringValue(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Google Cloud Storage", WorkflowType) } if parsedQuery.workflowType != nil && *parsedQuery.workflowType != val { parsedQuery.emptyResult = true return nil } parsedQuery.workflowType = common.StringPtr(val) case SearchPrecision: val, err := extractStringValue(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Google Cloud Storage", SearchPrecision) } if parsedQuery.searchPrecision != nil && *parsedQuery.searchPrecision != val { return fmt.Errorf("only one expression is allowed for %s", SearchPrecision) } switch val { case PrecisionDay: case PrecisionHour: case PrecisionMinute: case PrecisionSecond: default: return fmt.Errorf("invalid value for %s: %s", SearchPrecision, val) } parsedQuery.searchPrecision = common.StringPtr(val) default: return fmt.Errorf("unknown filter name: %s", colNameStr) } return nil } func convertToTimestamp(timeStr string) (int64, error) { timestamp, err := strconv.ParseInt(timeStr, 10, 64) if err == nil { return timestamp, nil } timestampStr, err := extractStringValue(timeStr) if err != nil { return 0, err } parsedTime, err := time.Parse(defaultDateTimeFormat, timestampStr) if err != nil { return 0, err } return parsedTime.UnixNano(), nil } func extractStringValue(s string) (string, error) { if len(s) >= 2 && s[0] == '\'' && s[len(s)-1] == '\'' { return s[1 : len(s)-1], nil } return "", fmt.Errorf("value %s is not a string value", s) } ================================================ FILE: common/archiver/gcloud/queryParser_mock.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Code generated by MockGen. DO NOT EDIT. // Source: queryParser.go // Package gcloud is a generated GoMock package. package gcloud import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockQueryParser is a mock of QueryParser interface. type MockQueryParser struct { ctrl *gomock.Controller recorder *MockQueryParserMockRecorder } // MockQueryParserMockRecorder is the mock recorder for MockQueryParser. type MockQueryParserMockRecorder struct { mock *MockQueryParser } // NewMockQueryParser creates a new mock instance. func NewMockQueryParser(ctrl *gomock.Controller) *MockQueryParser { mock := &MockQueryParser{ctrl: ctrl} mock.recorder = &MockQueryParserMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQueryParser) EXPECT() *MockQueryParserMockRecorder { return m.recorder } // Parse mocks base method. func (m *MockQueryParser) Parse(query string) (*parsedQuery, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Parse", query) ret0, _ := ret[0].(*parsedQuery) ret1, _ := ret[1].(error) return ret0, ret1 } // Parse indicates an expected call of Parse. func (mr *MockQueryParserMockRecorder) Parse(query interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parse", reflect.TypeOf((*MockQueryParser)(nil).Parse), query) } ================================================ FILE: common/archiver/gcloud/util.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gcloud import ( "context" "encoding/json" "errors" "fmt" "strconv" "strings" "time" "github.com/dgryski/go-farm" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/gcloud/connector" "github.com/uber/cadence/common/types" ) func encode(v interface{}) ([]byte, error) { return json.Marshal(v) } func decodeHistoryBatches(data []byte) ([]*types.History, error) { historyBatches := []*types.History{} err := json.Unmarshal(data, &historyBatches) if err != nil { return nil, err } return historyBatches, nil } func constructHistoryFilenameMultipart(domainID, workflowID, runID string, version int64, partNumber int) string { combinedHash := constructHistoryFilenamePrefix(domainID, workflowID, runID) return fmt.Sprintf("%s_%v_%v.history", combinedHash, version, partNumber) } func constructHistoryFilenamePrefix(domainID, workflowID, runID string) string { return strings.Join([]string{hash(domainID), hash(workflowID), hash(runID)}, "") } func constructVisibilityFilenamePrefix(domainID, tag string) string { return fmt.Sprintf("%s/%s", domainID, tag) } func constructTimeBasedSearchKey(domainID, tag string, timestamp int64, precision string) string { t := time.Unix(0, timestamp).In(time.UTC) var timeFormat = "" switch precision { case PrecisionSecond: timeFormat = ":05" fallthrough case PrecisionMinute: timeFormat = ":04" + timeFormat fallthrough case PrecisionHour: timeFormat = "15" + timeFormat fallthrough case PrecisionDay: timeFormat = "2006-01-02T" + timeFormat } return fmt.Sprintf("%s_%s", constructVisibilityFilenamePrefix(domainID, tag), t.Format(timeFormat)) } func hash(s string) (result string) { if s != "" { return fmt.Sprintf("%v", farm.Fingerprint64([]byte(s))) } return } func contextExpired(ctx context.Context) bool { select { case <-ctx.Done(): return true default: return false } } func deserializeGetHistoryToken(bytes []byte) (*getHistoryToken, error) { token := &getHistoryToken{} err := json.Unmarshal(bytes, token) return token, err } func extractCloseFailoverVersion(filename string) (int64, int, error) { filenameParts := strings.FieldsFunc(filename, func(r rune) bool { return r == '_' || r == '.' }) if len(filenameParts) != 4 { return -1, 0, errors.New("unknown filename structure") } failoverVersion, err := strconv.ParseInt(filenameParts[1], 10, 64) if err != nil { return -1, 0, err } highestPart, err := strconv.Atoi(filenameParts[2]) return failoverVersion, highestPart, err } func serializeToken(token interface{}) ([]byte, error) { if token == nil { return nil, nil } return json.Marshal(token) } func decodeVisibilityRecord(data []byte) (*visibilityRecord, error) { record := &visibilityRecord{} err := json.Unmarshal(data, record) if err != nil { return nil, err } return record, nil } func constructVisibilityFilename(domain, workflowTypeName, workflowID, runID, tag string, timestamp int64) string { t := time.Unix(0, timestamp).In(time.UTC) prefix := constructVisibilityFilenamePrefix(domain, tag) return fmt.Sprintf("%s_%s_%s_%s_%s.visibility", prefix, t.Format(time.RFC3339), hash(workflowTypeName), hash(workflowID), hash(runID)) } func deserializeQueryVisibilityToken(bytes []byte) (*queryVisibilityToken, error) { token := &queryVisibilityToken{} err := json.Unmarshal(bytes, token) return token, err } func convertToExecutionInfo(record *visibilityRecord) *types.WorkflowExecutionInfo { return &types.WorkflowExecutionInfo{ Execution: &types.WorkflowExecution{ WorkflowID: record.WorkflowID, RunID: record.RunID, }, Type: &types.WorkflowType{ Name: record.WorkflowTypeName, }, StartTime: common.Int64Ptr(record.StartTimestamp), ExecutionTime: common.Int64Ptr(record.ExecutionTimestamp), CloseTime: common.Int64Ptr(record.CloseTimestamp), CloseStatus: record.CloseStatus.Ptr(), HistoryLength: record.HistoryLength, Memo: record.Memo, SearchAttributes: &types.SearchAttributes{ IndexedFields: archiver.ConvertSearchAttrToBytes(record.SearchAttributes), }, } } func newRunIDPrecondition(runID string) connector.Precondition { return func(subject interface{}) bool { if runID == "" { return true } fileName, ok := subject.(string) if !ok { return false } if strings.Contains(fileName, runID) { fileNameParts := strings.Split(fileName, "_") if len(fileNameParts) != 5 { return true } return strings.Contains(fileName, fileNameParts[4]) } return false } } func newWorkflowIDPrecondition(workflowID string) connector.Precondition { return func(subject interface{}) bool { if workflowID == "" { return true } fileName, ok := subject.(string) if !ok { return false } if strings.Contains(fileName, workflowID) { fileNameParts := strings.Split(fileName, "_") if len(fileNameParts) != 5 { return true } return strings.Contains(fileName, fileNameParts[3]) } return false } } func newWorkflowTypeNamePrecondition(workflowTypeName string) connector.Precondition { return func(subject interface{}) bool { if workflowTypeName == "" { return true } fileName, ok := subject.(string) if !ok { return false } if strings.Contains(fileName, workflowTypeName) { fileNameParts := strings.Split(fileName, "_") if len(fileNameParts) != 5 { return true } return strings.Contains(fileName, fileNameParts[2]) } return false } } func isRetryableError(err error) (retryable bool) { switch err.Error() { case connector.ErrBucketNotFound.Error(), archiver.ErrURISchemeMismatch.Error(), archiver.ErrInvalidURI.Error(): retryable = false default: retryable = true } return } ================================================ FILE: common/archiver/gcloud/util_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gcloud import ( "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) func (s *utilSuite) SetupTest() { s.Assertions = require.New(s.T()) } func TestUtilSuite(t *testing.T) { suite.Run(t, new(utilSuite)) } type utilSuite struct { *require.Assertions suite.Suite } func (s *utilSuite) TestEncodeDecodeHistoryBatches() { historyBatches := []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, Version: 1, }, }, }, { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: 1, }, { ID: constants.FirstEventID + 2, Version: 2, DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ Identity: "some random identity", }, }, }, }, } encodedHistoryBatches, err := encode(historyBatches) s.NoError(err) decodedHistoryBatches, err := decodeHistoryBatches(encodedHistoryBatches) s.NoError(err) s.Equal(historyBatches, decodedHistoryBatches) } func (s *utilSuite) TestconstructHistoryFilename() { testCases := []struct { domainID string workflowID string runID string closeFailoverVersion int64 expectBuiltName string }{ { domainID: "testDomainID", workflowID: "testWorkflowID", runID: "testRunID", closeFailoverVersion: 5, expectBuiltName: "17971674567288329890367046253745284795510285995943906173973_5_0.history", }, } for _, tc := range testCases { filename := constructHistoryFilenameMultipart(tc.domainID, tc.workflowID, tc.runID, tc.closeFailoverVersion, 0) s.Equal(tc.expectBuiltName, filename) } } func (s *utilSuite) TestSerializeDeserializeGetHistoryToken() { token := &getHistoryToken{ CloseFailoverVersion: 101, BatchIdxOffset: 20, } serializedToken, err := serializeToken(token) s.Nil(err) deserializedToken, err := deserializeGetHistoryToken(serializedToken) s.Nil(err) s.Equal(token, deserializedToken) } func (s *utilSuite) TestConstructHistoryFilenamePrefix() { s.Equal("28646288347718592068344541402884576509131521284625246243", constructHistoryFilenamePrefix("domainID", "workflowID", "runID")) } func (s *utilSuite) TestConstructHistoryFilenameMultipart() { s.Equal("28646288347718592068344541402884576509131521284625246243_-24_0.history", constructHistoryFilenameMultipart("domainID", "workflowID", "runID", -24, 0)) } func (s *utilSuite) TestConstructVisibilityFilenamePrefix() { s.Equal("domainID/startTimeout", constructVisibilityFilenamePrefix("domainID", indexKeyStartTimeout)) } func (s *utilSuite) TestConstructTimeBasedSearchKey() { s.Equal("domainID/startTimeout_1970-01-01T", constructTimeBasedSearchKey("domainID", indexKeyStartTimeout, 1580819141, "Day")) } func (s *utilSuite) TestConstructVisibilityFilename() { s.Equal("domainID/startTimeout_1970-01-01T00:24:32Z_4346151385925082125_8344541402884576509_131521284625246243.visibility", constructVisibilityFilename("domainID", "workflowTypeName", "workflowID", "runID", indexKeyStartTimeout, 1472313624305)) } func (s *utilSuite) TestWorkflowIdPrecondition() { testCases := []struct { workflowID string fileName string expectedResult bool }{ { workflowID: "4418294404690464320", fileName: "closeTimeout_2020-02-27T09:42:28Z_12851121011173788097_4418294404690464320_15619178330501475177.visibility", expectedResult: true, }, { workflowID: "testWorkflowID", fileName: "closeTimeout_2020-02-27T09:42:28Z_12851121011173788097_4418294404690464320_15619178330501475177.visibility", expectedResult: false, }, { workflowID: "", fileName: "closeTimeout_2020-02-27T09:42:28Z_12851121011173788097_4418294404690464320_15619178330501475177.visibility", expectedResult: true, }, } for _, testCase := range testCases { s.Equal(newWorkflowIDPrecondition(testCase.workflowID)(testCase.fileName), testCase.expectedResult) } } func (s *utilSuite) TestRunIdPrecondition() { testCases := []struct { workflowID string runID string fileName string expectedResult bool }{ { workflowID: "4418294404690464320", runID: "15619178330501475177", fileName: "closeTimeout_2020-02-27T09:42:28Z_12851121011173788097_4418294404690464320_15619178330501475177.visibility", expectedResult: true, }, { workflowID: "4418294404690464320", runID: "15619178330501475177", fileName: "closeTimeout_2020-02-27T09:42:28Z_12851121011173788097_4418294404690464320_unkonwnRunID.visibility", expectedResult: false, }, { workflowID: "4418294404690464320", runID: "", fileName: "closeTimeout_2020-02-27T09:42:28Z_12851121011173788097_4418294404690464320_unkonwnRunID.visibility", expectedResult: true, }, } for _, testCase := range testCases { s.Equal(newRunIDPrecondition(testCase.runID)(testCase.fileName), testCase.expectedResult) } } func (s *utilSuite) TestWorkflowTypeNamePrecondition() { testCases := []struct { workflowID string runID string workflowTypeName string fileName string expectedResult bool }{ { workflowID: "4418294404690464320", runID: "15619178330501475177", workflowTypeName: "12851121011173788097", fileName: "closeTimeout_2020-02-27T09:42:28Z_12851121011173788097_4418294404690464320_15619178330501475177.visibility", expectedResult: true, }, { workflowID: "4418294404690464320", runID: "15619178330501475177", workflowTypeName: "12851121011173788097", fileName: "closeTimeout_2020-02-27T09:42:28Z_12851121011173788098_4418294404690464320_15619178330501475177.visibility", expectedResult: false, }, { workflowID: "4418294404690464320", runID: "15619178330501475177", workflowTypeName: "", fileName: "closeTimeout_2020-02-27T09:42:28Z_unkownWorkflowTypeName_4418294404690464320_15619178330501475177.visibility", expectedResult: true, }, } for _, testCase := range testCases { s.Equal(newWorkflowTypeNamePrecondition(testCase.workflowTypeName)(testCase.fileName), testCase.expectedResult) } } ================================================ FILE: common/archiver/gcloud/visibilityArchiver.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gcloud import ( "context" "errors" "fmt" "path/filepath" "time" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/gcloud/connector" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) const ( errEncodeVisibilityRecord = "failed to encode visibility record" indexKeyStartTimeout = "startTimeout" indexKeyCloseTimeout = "closeTimeout" timeoutInSeconds = 5 ) var ( errRetriable = errors.New("retriable error") ) type ( visibilityArchiver struct { container *archiver.VisibilityBootstrapContainer gcloudStorage connector.Client queryParser QueryParser } queryVisibilityToken struct { Offset int } visibilityRecord archiver.ArchiveVisibilityRequest queryVisibilityRequest struct { domainID string pageSize int nextPageToken []byte parsedQuery *parsedQuery } ) func newVisibilityArchiver(container *archiver.VisibilityBootstrapContainer, storage connector.Client) *visibilityArchiver { return &visibilityArchiver{ container: container, gcloudStorage: storage, queryParser: NewQueryParser(), } } // NewVisibilityArchiver creates a new archiver.VisibilityArchiver based on filestore func NewVisibilityArchiver(container *archiver.VisibilityBootstrapContainer, config connector.Config) (archiver.VisibilityArchiver, error) { storage, err := connector.NewClient(context.Background(), config) return newVisibilityArchiver(container, storage), err } // Archive is used to archive one workflow visibility record. // Check the Archive() method of the HistoryArchiver interface in Step 2 for parameters' meaning and requirements. // The only difference is that the ArchiveOption parameter won't include an option for recording process. // Please make sure your implementation is lossless. If any in-memory batching mechanism is used, then those batched records will be lost during server restarts. // This method will be invoked when workflow closes. Note that because of conflict resolution, it is possible for a workflow to through the closing process multiple times, which means that this method can be invoked more than once after a workflow closes. func (v *visibilityArchiver) Archive(ctx context.Context, URI archiver.URI, request *archiver.ArchiveVisibilityRequest, opts ...archiver.ArchiveOption) (err error) { scope := v.container.MetricsClient.Scope(metrics.HistoryArchiverScope, metrics.DomainTag(request.DomainName)) featureCatalog := archiver.GetFeatureCatalog(opts...) sw := scope.StartTimer(metrics.CadenceLatency) defer func() { sw.Stop() if err != nil { if isRetryableError(err) { scope.IncCounter(metrics.VisibilityArchiverArchiveTransientErrorCount) } else { scope.IncCounter(metrics.VisibilityArchiverArchiveNonRetryableErrorCount) if featureCatalog.NonRetriableError != nil { err = featureCatalog.NonRetriableError() } } } }() logger := archiver.TagLoggerWithArchiveVisibilityRequestAndURI(v.container.Logger, request, URI.String()) if err := v.ValidateURI(URI); err != nil { if isRetryableError(err) { logger.Error(archiver.ArchiveTransientErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidURI), tag.Error(err)) return err } logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidURI), tag.Error(err)) return err } if err := archiver.ValidateVisibilityArchivalRequest(request); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidArchiveRequest), tag.Error(err)) return err } encodedVisibilityRecord, err := encode(request) if err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(errEncodeVisibilityRecord), tag.Error(err)) return err } // The filename has the format: closeTimestamp_hash(runID).visibility // This format allows the archiver to sort all records without reading the file contents filename := constructVisibilityFilename(request.DomainID, request.WorkflowTypeName, request.WorkflowID, request.RunID, indexKeyCloseTimeout, request.CloseTimestamp) if err := v.gcloudStorage.Upload(ctx, URI, filename, encodedVisibilityRecord); err != nil { logger.Error(archiver.ArchiveTransientErrorMsg, tag.ArchivalArchiveFailReason(errWriteFile), tag.Error(err)) return errRetriable } filename = constructVisibilityFilename(request.DomainID, request.WorkflowTypeName, request.WorkflowID, request.RunID, indexKeyStartTimeout, request.StartTimestamp) if err := v.gcloudStorage.Upload(ctx, URI, filename, encodedVisibilityRecord); err != nil { logger.Error(archiver.ArchiveTransientErrorMsg, tag.ArchivalArchiveFailReason(errWriteFile), tag.Error(err)) return errRetriable } scope.IncCounter(metrics.VisibilityArchiveSuccessCount) return nil } // Query is used to retrieve archived visibility records. // Check the Get() method of the HistoryArchiver interface in Step 2 for parameters' meaning and requirements. // The request includes a string field called query, which describes what kind of visibility records should be returned. For example, it can be some SQL-like syntax query string. // Your implementation is responsible for parsing and validating the query, and also returning all visibility records that match the query. // Currently the maximum context timeout passed into the method is 3 minutes, so it's ok if this method takes a long time to run. func (v *visibilityArchiver) Query(ctx context.Context, URI archiver.URI, request *archiver.QueryVisibilityRequest) (*archiver.QueryVisibilityResponse, error) { if err := v.ValidateURI(URI); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidURI.Error()} } if err := archiver.ValidateQueryRequest(request); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidQueryVisibilityRequest.Error()} } parsedQuery, err := v.queryParser.Parse(request.Query) if err != nil { return nil, &types.BadRequestError{Message: err.Error()} } if parsedQuery.emptyResult { return &archiver.QueryVisibilityResponse{}, nil } return v.query(ctx, URI, &queryVisibilityRequest{ domainID: request.DomainID, pageSize: request.PageSize, nextPageToken: request.NextPageToken, parsedQuery: parsedQuery, }) } func (v *visibilityArchiver) query(ctx context.Context, URI archiver.URI, request *queryVisibilityRequest) (*archiver.QueryVisibilityResponse, error) { token := new(queryVisibilityToken) if request.nextPageToken != nil { var err error token, err = deserializeQueryVisibilityToken(request.nextPageToken) if err != nil { return nil, &types.BadRequestError{Message: archiver.ErrNextPageTokenCorrupted.Error()} } } var prefix = constructVisibilityFilenamePrefix(request.domainID, indexKeyCloseTimeout) if request.parsedQuery.closeTime != 0 { prefix = constructTimeBasedSearchKey(request.domainID, indexKeyCloseTimeout, request.parsedQuery.closeTime, *request.parsedQuery.searchPrecision) } if request.parsedQuery.startTime != 0 { prefix = constructTimeBasedSearchKey(request.domainID, indexKeyStartTimeout, request.parsedQuery.startTime, *request.parsedQuery.searchPrecision) } filters := make([]connector.Precondition, 0) if request.parsedQuery.workflowID != nil { filters = append(filters, newWorkflowIDPrecondition(hash(*request.parsedQuery.workflowID))) } if request.parsedQuery.runID != nil { filters = append(filters, newWorkflowIDPrecondition(hash(*request.parsedQuery.runID))) } if request.parsedQuery.workflowType != nil { filters = append(filters, newWorkflowIDPrecondition(hash(*request.parsedQuery.workflowType))) } filenames, completed, currentCursorPos, err := v.gcloudStorage.QueryWithFilters(ctx, URI, prefix, request.pageSize, token.Offset, filters) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } response := &archiver.QueryVisibilityResponse{} for _, file := range filenames { encodedRecord, err := v.gcloudStorage.Get(ctx, URI, fmt.Sprintf("%s/%s", request.domainID, filepath.Base(file))) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } record, err := decodeVisibilityRecord(encodedRecord) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } response.Executions = append(response.Executions, convertToExecutionInfo(record)) } if !completed { newToken := &queryVisibilityToken{ Offset: currentCursorPos, } encodedToken, err := serializeToken(newToken) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } response.NextPageToken = encodedToken } return response, nil } // ValidateURI is used to define what a valid URI for an implementation is. func (v *visibilityArchiver) ValidateURI(URI archiver.URI) (err error) { ctx, cancel := context.WithTimeout(context.Background(), timeoutInSeconds*time.Second) defer cancel() if err = v.validateURI(URI); err == nil { _, err = v.gcloudStorage.Exist(ctx, URI, "") } return } func (v *visibilityArchiver) validateURI(URI archiver.URI) (err error) { if URI.Scheme() != URIScheme { return archiver.ErrURISchemeMismatch } if URI.Path() == "" || URI.Hostname() == "" { return archiver.ErrInvalidURI } return } ================================================ FILE: common/archiver/gcloud/visibilityArchiver_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gcloud import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/gcloud/connector/mocks" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) const ( testWorkflowTypeName = "test-workflow-type" exampleVisibilityRecord = `{"DomainID":"test-domain-id","DomainName":"test-domain-name","WorkflowID":"test-workflow-id","RunID":"test-run-id","WorkflowTypeName":"test-workflow-type","StartTimestamp":1580896574804475000,"ExecutionTimestamp":0,"CloseTimestamp":1580896575946478000,"CloseStatus":"COMPLETED","HistoryLength":36,"Memo":null,"SearchAttributes":{},"HistoryArchivalURI":"gs://my-bucket-cad/cadence_archival/development"}` ) func (s *visibilityArchiverSuite) SetupTest() { s.Assertions = require.New(s.T()) s.container = &archiver.VisibilityBootstrapContainer{ Logger: testlogger.New(s.T()), MetricsClient: metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), } s.expectedVisibilityRecords = []*visibilityRecord{ { DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, StartTimestamp: 1580896574804475000, CloseTimestamp: 1580896575946478000, CloseStatus: types.WorkflowExecutionCloseStatusCompleted, HistoryLength: 36, }, } } func TestVisibilityArchiverSuiteSuite(t *testing.T) { suite.Run(t, new(visibilityArchiverSuite)) } type visibilityArchiverSuite struct { *require.Assertions suite.Suite container *archiver.VisibilityBootstrapContainer expectedVisibilityRecords []*visibilityRecord } func (s *visibilityArchiverSuite) TestValidateVisibilityURI() { testCases := []struct { URI string expectedErr error }{ { URI: "wrongscheme:///a/b/c", expectedErr: archiver.ErrURISchemeMismatch, }, { URI: "gs:my-bucket-cad/cadence_archival/visibility", expectedErr: archiver.ErrInvalidURI, }, { URI: "gs://", expectedErr: archiver.ErrInvalidURI, }, { URI: "gs://my-bucket-cad", expectedErr: archiver.ErrInvalidURI, }, { URI: "gs:/my-bucket-cad/cadence_archival/visibility", expectedErr: archiver.ErrInvalidURI, }, { URI: "gs://my-bucket-cad/cadence_archival/visibility", expectedErr: nil, }, } storageWrapper := &mocks.Client{} storageWrapper.On("Exist", mock.Anything, mock.Anything, "").Return(false, nil) visibilityArchiver := new(visibilityArchiver) visibilityArchiver.gcloudStorage = storageWrapper for _, tc := range testCases { URI, err := archiver.NewURI(tc.URI) s.NoError(err) s.Equal(tc.expectedErr, visibilityArchiver.ValidateURI(URI)) } } func (s *visibilityArchiverSuite) TestArchive_Fail_InvalidVisibilityURI() { ctx := context.Background() URI, err := archiver.NewURI("wrongscheme://") s.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", mock.Anything, URI, "").Return(true, nil).Times(1) visibilityArchiver := newVisibilityArchiver(s.container, storageWrapper) s.NoError(err) request := &archiver.ArchiveVisibilityRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, } err = visibilityArchiver.Archive(ctx, URI, request) s.Error(err) } func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidVisibilityURI() { ctx := context.Background() URI, err := archiver.NewURI("wrongscheme://") s.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", mock.Anything, URI, "").Return(true, nil).Times(1) visibilityArchiver := newVisibilityArchiver(s.container, storageWrapper) s.NoError(err) request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 10, Query: "WorkflowType='type::example' AND CloseTime='2020-02-05T11:00:00Z' AND SearchPrecision='Day'", } _, err = visibilityArchiver.Query(ctx, URI, request) s.Error(err) } func (s *visibilityArchiverSuite) TestVisibilityArchive() { ctx := context.Background() URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/visibility") s.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", mock.Anything, URI, mock.Anything).Return(false, nil) storageWrapper.On("Upload", mock.Anything, URI, mock.Anything, mock.Anything).Return(nil) visibilityArchiver := newVisibilityArchiver(s.container, storageWrapper) s.NoError(err) request := &archiver.ArchiveVisibilityRequest{ DomainName: testDomainName, DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, StartTimestamp: time.Now().UnixNano(), ExecutionTimestamp: 0, // workflow without backoff CloseTimestamp: time.Now().UnixNano(), CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: int64(101), } err = visibilityArchiver.Archive(ctx, URI, request) s.NoError(err) } func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidQuery() { ctx := context.Background() URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/visibility") s.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", mock.Anything, URI, mock.Anything).Return(false, nil) visibilityArchiver := newVisibilityArchiver(s.container, storageWrapper) s.NoError(err) mockCtrl := gomock.NewController(s.T()) mockParser := NewMockQueryParser(mockCtrl) mockParser.EXPECT().Parse(gomock.Any()).Return(nil, errors.New("invalid query")) visibilityArchiver.queryParser = mockParser response, err := visibilityArchiver.Query(ctx, URI, &archiver.QueryVisibilityRequest{ DomainID: "some random domainID", PageSize: 10, Query: "some invalid query", }) s.Error(err) s.Nil(response) } func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidToken() { URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/visibility") s.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", mock.Anything, URI, mock.Anything).Return(false, nil) visibilityArchiver := newVisibilityArchiver(s.container, storageWrapper) s.NoError(err) mockCtrl := gomock.NewController(s.T()) mockParser := NewMockQueryParser(mockCtrl) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ closeTime: int64(101), startTime: int64(1), }, nil) visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, Query: "parsed by mockParser", PageSize: 1, NextPageToken: []byte{1, 2, 3}, } response, err := visibilityArchiver.Query(context.Background(), URI, request) s.Error(err) s.Nil(response) } func (s *visibilityArchiverSuite) TestQuery_Success_NoNextPageToken() { ctx := context.Background() URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/visibility") s.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", mock.Anything, URI, mock.Anything).Return(false, nil) storageWrapper.On("QueryWithFilters", mock.Anything, URI, mock.Anything, 10, 0, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{"closeTimeout_2020-02-05T09:56:14Z_test-workflow-id_MobileOnlyWorkflow::processMobileOnly_test-run-id.visibility"}, true, 1, nil).Times(1) storageWrapper.On("Get", mock.Anything, URI, "test-domain-id/closeTimeout_2020-02-05T09:56:14Z_test-workflow-id_MobileOnlyWorkflow::processMobileOnly_test-run-id.visibility").Return([]byte(exampleVisibilityRecord), nil) visibilityArchiver := newVisibilityArchiver(s.container, storageWrapper) s.NoError(err) mockCtrl := gomock.NewController(s.T()) mockParser := NewMockQueryParser(mockCtrl) dayPrecision := string("Day") mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ closeTime: int64(101), searchPrecision: &dayPrecision, workflowType: common.StringPtr("MobileOnlyWorkflow::processMobileOnly"), workflowID: common.StringPtr(testWorkflowID), runID: common.StringPtr(testRunID), }, nil) visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 10, Query: "parsed by mockParser", } response, err := visibilityArchiver.Query(ctx, URI, request) s.NoError(err) s.NotNil(response) s.Nil(response.NextPageToken) s.Len(response.Executions, 1) s.Equal(convertToExecutionInfo(s.expectedVisibilityRecords[0]), response.Executions[0]) } func (s *visibilityArchiverSuite) TestQuery_Success_SmallPageSize() { pageSize := 2 ctx := context.Background() URI, err := archiver.NewURI("gs://my-bucket-cad/cadence_archival/visibility") s.NoError(err) storageWrapper := &mocks.Client{} storageWrapper.On("Exist", mock.Anything, URI, mock.Anything).Return(false, nil) storageWrapper.On("QueryWithFilters", mock.Anything, URI, mock.Anything, pageSize, 0, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{"closeTimeout_2020-02-05T09:56:14Z_test-workflow-id_MobileOnlyWorkflow::processMobileOnly_test-run-id.visibility", "closeTimeout_2020-02-05T09:56:15Z_test-workflow-id_MobileOnlyWorkflow::processMobileOnly_test-run-id.visibility"}, false, 1, nil).Times(2) storageWrapper.On("QueryWithFilters", mock.Anything, URI, mock.Anything, pageSize, 1, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{"closeTimeout_2020-02-05T09:56:16Z_test-workflow-id_MobileOnlyWorkflow::processMobileOnly_test-run-id.visibility"}, true, 2, nil).Times(2) storageWrapper.On("Get", mock.Anything, URI, "test-domain-id/closeTimeout_2020-02-05T09:56:14Z_test-workflow-id_MobileOnlyWorkflow::processMobileOnly_test-run-id.visibility").Return([]byte(exampleVisibilityRecord), nil) storageWrapper.On("Get", mock.Anything, URI, "test-domain-id/closeTimeout_2020-02-05T09:56:15Z_test-workflow-id_MobileOnlyWorkflow::processMobileOnly_test-run-id.visibility").Return([]byte(exampleVisibilityRecord), nil) storageWrapper.On("Get", mock.Anything, URI, "test-domain-id/closeTimeout_2020-02-05T09:56:16Z_test-workflow-id_MobileOnlyWorkflow::processMobileOnly_test-run-id.visibility").Return([]byte(exampleVisibilityRecord), nil) visibilityArchiver := newVisibilityArchiver(s.container, storageWrapper) s.NoError(err) mockCtrl := gomock.NewController(s.T()) mockParser := NewMockQueryParser(mockCtrl) dayPrecision := string("Day") mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ closeTime: int64(101), searchPrecision: &dayPrecision, workflowType: common.StringPtr("MobileOnlyWorkflow::processMobileOnly"), workflowID: common.StringPtr(testWorkflowID), runID: common.StringPtr(testRunID), }, nil).AnyTimes() visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: pageSize, Query: "parsed by mockParser", } response, err := visibilityArchiver.Query(ctx, URI, request) s.NoError(err) s.NotNil(response) s.NotNil(response.NextPageToken) s.Len(response.Executions, 2) s.Equal(convertToExecutionInfo(s.expectedVisibilityRecords[0]), response.Executions[0]) s.Equal(convertToExecutionInfo(s.expectedVisibilityRecords[0]), response.Executions[1]) request.NextPageToken = response.NextPageToken response, err = visibilityArchiver.Query(ctx, URI, request) s.NoError(err) s.NotNil(response) s.Nil(response.NextPageToken) s.Len(response.Executions, 1) s.Equal(convertToExecutionInfo(s.expectedVisibilityRecords[0]), response.Executions[0]) } ================================================ FILE: common/archiver/historyIterator.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination historyIterator_mock.go -self_package github.com/uber/cadence/common/archiver package archiver import ( "context" "encoding/json" "errors" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" persistenceutils "github.com/uber/cadence/common/persistence/persistence-utils" "github.com/uber/cadence/common/types" ) const ( historyPageSize = 250 ) type ( // HistoryIterator is used to get history batches HistoryIterator interface { Next() (*HistoryBlob, error) HasNext() bool GetState() ([]byte, error) } // HistoryBlobHeader is the header attached to all history blobs HistoryBlobHeader struct { DomainName *string `json:"domain_name,omitempty"` DomainID *string `json:"domain_id,omitempty"` WorkflowID *string `json:"workflow_id,omitempty"` RunID *string `json:"run_id,omitempty"` IsLast *bool `json:"is_last,omitempty"` FirstFailoverVersion *int64 `json:"first_failover_version,omitempty"` LastFailoverVersion *int64 `json:"last_failover_version,omitempty"` FirstEventID *int64 `json:"first_event_id,omitempty"` LastEventID *int64 `json:"last_event_id,omitempty"` EventCount *int64 `json:"event_count,omitempty"` } // HistoryBlob is the serializable data that forms the body of a blob HistoryBlob struct { Header *HistoryBlobHeader `json:"header"` Body []*types.History `json:"body"` } historyIteratorState struct { NextEventID int64 FinishedIteration bool } historyIterator struct { historyIteratorState ctx context.Context request *ArchiveHistoryRequest historyV2Manager persistence.HistoryManager sizeEstimator SizeEstimator historyPageSize int targetHistoryBlobSize int } ) var ( errIteratorDepleted = errors.New("iterator is depleted") ) // NewHistoryIterator returns a new HistoryIterator func NewHistoryIterator( ctx context.Context, request *ArchiveHistoryRequest, historyV2Manager persistence.HistoryManager, targetHistoryBlobSize int, ) HistoryIterator { return newHistoryIterator(ctx, request, historyV2Manager, targetHistoryBlobSize) } // NewHistoryIteratorFromState returns a new HistoryIterator with specified state func NewHistoryIteratorFromState( ctx context.Context, request *ArchiveHistoryRequest, historyV2Manager persistence.HistoryManager, targetHistoryBlobSize int, initialState []byte, ) (HistoryIterator, error) { it := newHistoryIterator(ctx, request, historyV2Manager, targetHistoryBlobSize) if initialState == nil { return it, nil } if err := it.reset(initialState); err != nil { return nil, err } return it, nil } func newHistoryIterator( ctx context.Context, request *ArchiveHistoryRequest, historyV2Manager persistence.HistoryManager, targetHistoryBlobSize int, ) *historyIterator { return &historyIterator{ historyIteratorState: historyIteratorState{ NextEventID: constants.FirstEventID, FinishedIteration: false, }, ctx: ctx, request: request, historyV2Manager: historyV2Manager, historyPageSize: historyPageSize, targetHistoryBlobSize: targetHistoryBlobSize, sizeEstimator: NewJSONSizeEstimator(), } } func (i *historyIterator) Next() (*HistoryBlob, error) { if !i.HasNext() { return nil, errIteratorDepleted } historyBatches, newIterState, err := i.readHistoryBatches(i.ctx, i.NextEventID) if err != nil { return nil, err } i.historyIteratorState = newIterState firstEvent := historyBatches[0].Events[0] lastBatch := historyBatches[len(historyBatches)-1] lastEvent := lastBatch.Events[len(lastBatch.Events)-1] eventCount := int64(0) for _, batch := range historyBatches { eventCount += int64(len(batch.Events)) } header := &HistoryBlobHeader{ DomainName: common.StringPtr(i.request.DomainName), DomainID: common.StringPtr(i.request.DomainID), WorkflowID: common.StringPtr(i.request.WorkflowID), RunID: common.StringPtr(i.request.RunID), IsLast: common.BoolPtr(i.FinishedIteration), FirstFailoverVersion: common.Int64Ptr(firstEvent.Version), LastFailoverVersion: common.Int64Ptr(lastEvent.Version), FirstEventID: common.Int64Ptr(firstEvent.ID), LastEventID: common.Int64Ptr(lastEvent.ID), EventCount: common.Int64Ptr(eventCount), } return &HistoryBlob{ Header: header, Body: historyBatches, }, nil } // HasNext returns true if there are more items to iterate over. func (i *historyIterator) HasNext() bool { return !i.FinishedIteration } // GetState returns the encoded iterator state func (i *historyIterator) GetState() ([]byte, error) { return json.Marshal(i.historyIteratorState) } func (i *historyIterator) readHistoryBatches(ctx context.Context, firstEventID int64) ([]*types.History, historyIteratorState, error) { size := 0 targetSize := i.targetHistoryBlobSize var historyBatches []*types.History newIterState := historyIteratorState{} for size < targetSize { currHistoryBatches, err := i.readHistory(ctx, firstEventID) if _, ok := err.(*types.EntityNotExistsError); ok && firstEventID != constants.FirstEventID { newIterState.FinishedIteration = true return historyBatches, newIterState, nil } if err != nil { return nil, newIterState, err } for idx, batch := range currHistoryBatches { historyBatchSize, err := i.sizeEstimator.EstimateSize(batch) if err != nil { return nil, newIterState, err } size += historyBatchSize historyBatches = append(historyBatches, batch) firstEventID = batch.Events[len(batch.Events)-1].ID + 1 // In case targetSize is satisfied before reaching the end of current set of batches, return immediately. // Otherwise, we need to look ahead to see if there's more history batches. if size >= targetSize && idx != len(currHistoryBatches)-1 { newIterState.FinishedIteration = false newIterState.NextEventID = firstEventID return historyBatches, newIterState, nil } } } // If you are here, it means the target size is met after adding the last batch of read history. // We need to check if there's more history batches. _, err := i.readHistory(ctx, firstEventID) if _, ok := err.(*types.EntityNotExistsError); ok && firstEventID != constants.FirstEventID { newIterState.FinishedIteration = true return historyBatches, newIterState, nil } if err != nil { return nil, newIterState, err } newIterState.FinishedIteration = false newIterState.NextEventID = firstEventID return historyBatches, newIterState, nil } func (i *historyIterator) readHistory(ctx context.Context, firstEventID int64) ([]*types.History, error) { req := &persistence.ReadHistoryBranchRequest{ BranchToken: i.request.BranchToken, MinEventID: firstEventID, MaxEventID: constants.EndEventID, PageSize: i.historyPageSize, ShardID: common.IntPtr(i.request.ShardID), DomainName: i.request.DomainName, } historyBatches, _, _, err := persistenceutils.ReadFullPageV2EventsByBatch(ctx, i.historyV2Manager, req) return historyBatches, err } // reset resets iterator to a certain state given its encoded representation // if it returns an error, the operation will have no effect on the iterator func (i *historyIterator) reset(stateToken []byte) error { var iteratorState historyIteratorState if err := json.Unmarshal(stateToken, &iteratorState); err != nil { return err } i.historyIteratorState = iteratorState return nil } type ( // SizeEstimator is used to estimate the size of any object SizeEstimator interface { EstimateSize(v interface{}) (int, error) } jsonSizeEstimator struct{} ) func (e *jsonSizeEstimator) EstimateSize(v interface{}) (int, error) { data, err := json.Marshal(v) if err != nil { return 0, err } return len(data), nil } // NewJSONSizeEstimator returns a new SizeEstimator which uses json encoding to // estimate size func NewJSONSizeEstimator() SizeEstimator { return &jsonSizeEstimator{} } ================================================ FILE: common/archiver/historyIterator_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: historyIterator.go // // Generated by this command: // // mockgen -package archiver -source historyIterator.go -destination historyIterator_mock.go -self_package github.com/uber/cadence/common/archiver // // Package archiver is a generated GoMock package. package archiver import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockHistoryIterator is a mock of HistoryIterator interface. type MockHistoryIterator struct { ctrl *gomock.Controller recorder *MockHistoryIteratorMockRecorder isgomock struct{} } // MockHistoryIteratorMockRecorder is the mock recorder for MockHistoryIterator. type MockHistoryIteratorMockRecorder struct { mock *MockHistoryIterator } // NewMockHistoryIterator creates a new mock instance. func NewMockHistoryIterator(ctrl *gomock.Controller) *MockHistoryIterator { mock := &MockHistoryIterator{ctrl: ctrl} mock.recorder = &MockHistoryIteratorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHistoryIterator) EXPECT() *MockHistoryIteratorMockRecorder { return m.recorder } // GetState mocks base method. func (m *MockHistoryIterator) GetState() ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetState") ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // GetState indicates an expected call of GetState. func (mr *MockHistoryIteratorMockRecorder) GetState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetState", reflect.TypeOf((*MockHistoryIterator)(nil).GetState)) } // HasNext mocks base method. func (m *MockHistoryIterator) HasNext() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasNext") ret0, _ := ret[0].(bool) return ret0 } // HasNext indicates an expected call of HasNext. func (mr *MockHistoryIteratorMockRecorder) HasNext() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasNext", reflect.TypeOf((*MockHistoryIterator)(nil).HasNext)) } // Next mocks base method. func (m *MockHistoryIterator) Next() (*HistoryBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Next") ret0, _ := ret[0].(*HistoryBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // Next indicates an expected call of Next. func (mr *MockHistoryIteratorMockRecorder) Next() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Next", reflect.TypeOf((*MockHistoryIterator)(nil).Next)) } // MockSizeEstimator is a mock of SizeEstimator interface. type MockSizeEstimator struct { ctrl *gomock.Controller recorder *MockSizeEstimatorMockRecorder isgomock struct{} } // MockSizeEstimatorMockRecorder is the mock recorder for MockSizeEstimator. type MockSizeEstimatorMockRecorder struct { mock *MockSizeEstimator } // NewMockSizeEstimator creates a new mock instance. func NewMockSizeEstimator(ctrl *gomock.Controller) *MockSizeEstimator { mock := &MockSizeEstimator{ctrl: ctrl} mock.recorder = &MockSizeEstimatorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSizeEstimator) EXPECT() *MockSizeEstimatorMockRecorder { return m.recorder } // EstimateSize mocks base method. func (m *MockSizeEstimator) EstimateSize(v any) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EstimateSize", v) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // EstimateSize indicates an expected call of EstimateSize. func (mr *MockSizeEstimatorMockRecorder) EstimateSize(v any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimateSize", reflect.TypeOf((*MockSizeEstimator)(nil).EstimateSize), v) } ================================================ FILE: common/archiver/historyIterator_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "context" "errors" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const ( testDomainID = "test-domain-id" testDomainName = "test-domain-name" testWorkflowID = "test-workflow-id" testRunID = "test-run-id" testShardID = 1 testNextEventID = 1800 testCloseFailoverVersion = 100 testDefaultPersistencePageSize = 250 testDefaultTargetHistoryBlobSize = 2 * 1024 * 124 testDefaultHistoryEventSize = 50 ) var ( testBranchToken = []byte{1, 2, 3} ) type ( HistoryIteratorSuite struct { *require.Assertions suite.Suite } page struct { firstbatchIdx int numBatches int firstEventFailoverVersion int64 lastEventFailoverVersion int64 } testSizeEstimator struct{} ) func (e *testSizeEstimator) EstimateSize(v interface{}) (int, error) { historyBatch, ok := v.(*types.History) if !ok { return -1, errors.New("test size estimator only estimate the size of history batches") } return testDefaultHistoryEventSize * len(historyBatch.Events), nil } func newTestSizeEstimator() SizeEstimator { return &testSizeEstimator{} } func TestHistoryIteratorSuite(t *testing.T) { suite.Run(t, new(HistoryIteratorSuite)) } func (s *HistoryIteratorSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *HistoryIteratorSuite) TestReadHistory_Failed_EventsV2() { mockHistoryV2Manager := &mocks.HistoryV2Manager{} mockHistoryV2Manager.On("ReadHistoryBranchByBatch", mock.Anything, mock.Anything).Return(nil, errors.New("got error reading history branch")) itr := s.constructTestHistoryIterator(mockHistoryV2Manager, testDefaultTargetHistoryBlobSize, nil) history, err := itr.readHistory(context.Background(), constants.FirstEventID) s.Error(err) s.Nil(history) mockHistoryV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestReadHistory_Success_EventsV2() { mockHistoryV2Manager := &mocks.HistoryV2Manager{} resp := persistence.ReadHistoryBranchByBatchResponse{ History: []*types.History{}, NextPageToken: []byte{}, } mockHistoryV2Manager.On("ReadHistoryBranchByBatch", mock.Anything, mock.Anything).Return(&resp, nil) itr := s.constructTestHistoryIterator(mockHistoryV2Manager, testDefaultTargetHistoryBlobSize, nil) history, err := itr.readHistory(context.Background(), constants.FirstEventID) s.NoError(err) s.NotNil(history) mockHistoryV2Manager.AssertExpectations(s.T()) } // In the following test: // batchInfo represents # of events for each history batch. // page represents the metadata of the set of history batches that should be requested by the iterator // and returned by the history manager. Each page specifies the index of the first history batch it should // return, # of batches to return and first/last event failover version for the set of batches returned. // Note that is possible that a history batch is contained in multiple pages. func (s *HistoryIteratorSuite) TestReadHistoryBatches_Fail_FirstCallToReadHistoryGivesError() { batchInfo := []int{1} pages := []page{ { firstbatchIdx: 0, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, } historyV2Manager := s.constructMockHistoryV2Manager(batchInfo, 0, false, pages...) itr := s.constructTestHistoryIterator(historyV2Manager, testDefaultTargetHistoryBlobSize, nil) startingIteratorState := s.copyIteratorState(itr) events, nextIterState, err := itr.readHistoryBatches(context.Background(), constants.FirstEventID) s.Nil(events) s.False(nextIterState.FinishedIteration) s.Zero(nextIterState.NextEventID) s.Error(err) s.assertStateMatches(startingIteratorState, itr) historyV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestReadHistoryBatches_Fail_NonFirstCallToReadHistoryGivesError() { batchInfo := []int{1, 1} pages := []page{ { firstbatchIdx: 0, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 1, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, } historyV2Manager := s.constructMockHistoryV2Manager(batchInfo, 1, false, pages...) itr := s.constructTestHistoryIterator(historyV2Manager, testDefaultTargetHistoryBlobSize, nil) startingIteratorState := s.copyIteratorState(itr) events, nextIterState, err := itr.readHistoryBatches(context.Background(), constants.FirstEventID) s.Nil(events) s.False(nextIterState.FinishedIteration) s.Zero(nextIterState.NextEventID) s.Error(err) s.assertStateMatches(startingIteratorState, itr) historyV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestReadHistoryBatches_Success_ReadToHistoryEnd() { batchInfo := []int{1, 2, 1, 1, 1, 3, 3, 1, 3} pages := []page{ { firstbatchIdx: 0, numBatches: 3, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 3, numBatches: 2, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 5, numBatches: 4, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, } historyV2Manager := s.constructMockHistoryV2Manager(batchInfo, -1, true, pages...) // ensure target history batches size is greater than total history length to ensure all of history is read itr := s.constructTestHistoryIterator(historyV2Manager, 20*testDefaultHistoryEventSize, nil) startingIteratorState := s.copyIteratorState(itr) history, nextIterState, err := itr.readHistoryBatches(context.Background(), constants.FirstEventID) s.NotNil(history) s.Len(history, 9) s.True(nextIterState.FinishedIteration) s.Zero(nextIterState.NextEventID) s.NoError(err) s.assertStateMatches(startingIteratorState, itr) historyV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestReadHistoryBatches_Success_TargetSizeSatisfiedWithoutReadingToEnd() { batchInfo := []int{1, 2, 1, 1, 1, 3, 3, 1, 3} pages := []page{ { firstbatchIdx: 0, numBatches: 3, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 3, numBatches: 2, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 5, numBatches: 4, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, } historyV2Manager := s.constructMockHistoryV2Manager(batchInfo, -1, false, pages...) // ensure target history batches is smaller than full length of history so that not all of history is read itr := s.constructTestHistoryIterator(historyV2Manager, 11*testDefaultHistoryEventSize, nil) startingIteratorState := s.copyIteratorState(itr) history, nextIterState, err := itr.readHistoryBatches(context.Background(), constants.FirstEventID) s.NotNil(history) s.Len(history, 7) s.False(nextIterState.FinishedIteration) s.Equal(int64(13), nextIterState.NextEventID) s.NoError(err) s.assertStateMatches(startingIteratorState, itr) historyV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestReadHistoryBatches_Success_ReadExactlyToHistoryEnd() { batchInfo := []int{1, 2, 1, 1, 1, 3, 3, 1, 3} pages := []page{ { firstbatchIdx: 0, numBatches: 3, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 3, numBatches: 2, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 5, numBatches: 4, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, } historyV2Manager := s.constructMockHistoryV2Manager(batchInfo, -1, true, pages...) // ensure target history batches size is equal to the full length of history so that all of history is read itr := s.constructTestHistoryIterator(historyV2Manager, 16*testDefaultHistoryEventSize, nil) startingIteratorState := s.copyIteratorState(itr) history, nextIterState, err := itr.readHistoryBatches(context.Background(), constants.FirstEventID) s.NotNil(history) s.Len(history, 9) s.True(nextIterState.FinishedIteration) s.Zero(nextIterState.NextEventID) s.NoError(err) s.assertStateMatches(startingIteratorState, itr) historyV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestReadHistoryBatches_Success_ReadPageMultipleTimes() { batchInfo := []int{1, 3, 2} pages := []page{ { firstbatchIdx: 0, numBatches: 3, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 2, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, } historyV2Manager := s.constructMockHistoryV2Manager(batchInfo, -1, true, pages...) // ensure target history batches is very small so that one page needs multiple read itr := s.constructTestHistoryIterator(historyV2Manager, 2*testDefaultHistoryEventSize, nil) startingIteratorState := s.copyIteratorState(itr) history, nextIterState, err := itr.readHistoryBatches(context.Background(), constants.FirstEventID) s.NotNil(history) s.Len(history, 2) s.False(nextIterState.FinishedIteration) s.Equal(int64(5), nextIterState.NextEventID) s.NoError(err) s.assertStateMatches(startingIteratorState, itr) history, nextIterState, err = itr.readHistoryBatches(context.Background(), nextIterState.NextEventID) s.NotNil(history) s.Len(history, 1) s.True(nextIterState.FinishedIteration) s.Zero(nextIterState.NextEventID) s.NoError(err) s.assertStateMatches(startingIteratorState, itr) historyV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestNext_Fail_IteratorDepleted() { batchInfo := []int{1, 3, 2, 1, 2, 3, 4} pages := []page{ { firstbatchIdx: 0, numBatches: 2, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 2, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 2, }, { firstbatchIdx: 3, numBatches: 4, firstEventFailoverVersion: 2, lastEventFailoverVersion: 5, }, } historyV2Manager := s.constructMockHistoryV2Manager(batchInfo, -1, true, pages...) // set target history batches such that a single call to next will read all of history itr := s.constructTestHistoryIterator(historyV2Manager, 16*testDefaultHistoryEventSize, nil) blob, err := itr.Next() s.Nil(err) expectedIteratorState := historyIteratorState{ // when iteration is finished page token is not advanced FinishedIteration: true, NextEventID: 0, } s.assertStateMatches(expectedIteratorState, itr) s.NotNil(blob) expectedHeader := &HistoryBlobHeader{ DomainName: common.StringPtr(testDomainName), DomainID: common.StringPtr(testDomainID), WorkflowID: common.StringPtr(testWorkflowID), RunID: common.StringPtr(testRunID), IsLast: common.BoolPtr(true), FirstFailoverVersion: common.Int64Ptr(1), LastFailoverVersion: common.Int64Ptr(5), FirstEventID: common.Int64Ptr(constants.FirstEventID), LastEventID: common.Int64Ptr(16), EventCount: common.Int64Ptr(16), } s.Equal(expectedHeader, blob.Header) s.Len(blob.Body, 7) s.NoError(err) s.False(itr.HasNext()) blob, err = itr.Next() s.Equal(err, errIteratorDepleted) s.Nil(blob) s.assertStateMatches(expectedIteratorState, itr) historyV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestNext_Fail_ReturnErrOnSecondCallToNext() { batchInfo := []int{1, 3, 2, 1, 3, 2} pages := []page{ { firstbatchIdx: 0, numBatches: 2, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 2, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 3, numBatches: 2, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 5, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, } historyV2Manager := s.constructMockHistoryV2Manager(batchInfo, 3, false, pages...) // set target blob size such that the first two pages are read for blob one without error, third page will return error itr := s.constructTestHistoryIterator(historyV2Manager, 6*testDefaultHistoryEventSize, nil) blob, err := itr.Next() expectedIteratorState := historyIteratorState{ FinishedIteration: false, NextEventID: 7, } s.assertStateMatches(expectedIteratorState, itr) s.NotNil(blob) expectedHeader := &HistoryBlobHeader{ DomainName: common.StringPtr(testDomainName), DomainID: common.StringPtr(testDomainID), WorkflowID: common.StringPtr(testWorkflowID), RunID: common.StringPtr(testRunID), IsLast: common.BoolPtr(false), FirstFailoverVersion: common.Int64Ptr(1), LastFailoverVersion: common.Int64Ptr(1), FirstEventID: common.Int64Ptr(constants.FirstEventID), LastEventID: common.Int64Ptr(6), EventCount: common.Int64Ptr(6), } s.Equal(expectedHeader, blob.Header) s.NoError(err) s.True(itr.HasNext()) blob, err = itr.Next() s.Error(err) s.Nil(blob) s.assertStateMatches(expectedIteratorState, itr) historyV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestNext_Success_TenCallsToNext() { batchInfo := []int{} for i := 0; i < 100; i++ { batchInfo = append(batchInfo, []int{1, 2, 3, 4, 4, 3, 2, 1}...) } var pages []page for i := 0; i < 100; i++ { p := page{ firstbatchIdx: i * 8, numBatches: 8, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, } pages = append(pages, p) } historyV2Manager := s.constructMockHistoryV2Manager(batchInfo, -1, true, pages...) // set target blob size size such that every 10 persistence pages is one group of history batches itr := s.constructTestHistoryIterator(historyV2Manager, 20*10*testDefaultHistoryEventSize, nil) expectedIteratorState := historyIteratorState{ FinishedIteration: false, NextEventID: constants.FirstEventID, } for i := 0; i < 10; i++ { s.assertStateMatches(expectedIteratorState, itr) s.True(itr.HasNext()) blob, err := itr.Next() s.NoError(err) s.NotNil(blob) expectedHeader := &HistoryBlobHeader{ DomainName: common.StringPtr(testDomainName), DomainID: common.StringPtr(testDomainID), WorkflowID: common.StringPtr(testWorkflowID), RunID: common.StringPtr(testRunID), IsLast: common.BoolPtr(false), FirstFailoverVersion: common.Int64Ptr(1), LastFailoverVersion: common.Int64Ptr(1), FirstEventID: common.Int64Ptr(constants.FirstEventID + int64(i*200)), LastEventID: common.Int64Ptr(int64(200 + (i * 200))), EventCount: common.Int64Ptr(200), } if i == 9 { expectedHeader.IsLast = common.BoolPtr(true) } s.Equal(expectedHeader, blob.Header) if i < 9 { expectedIteratorState.FinishedIteration = false expectedIteratorState.NextEventID = int64(200*(i+1) + 1) } else { expectedIteratorState.NextEventID = 0 expectedIteratorState.FinishedIteration = true } } s.assertStateMatches(expectedIteratorState, itr) s.False(itr.HasNext()) historyV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestNext_Success_SameHistoryDifferentPage() { batchInfo := []int{2, 4, 4, 3, 2, 1, 1, 2} pages := []page{ { firstbatchIdx: 0, numBatches: 3, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 2, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 3, numBatches: 2, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 4, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 5, numBatches: 3, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, } eventsPerRead := 6 targetBlobSize := eventsPerRead * testDefaultHistoryEventSize historyV2Manager := s.constructMockHistoryV2Manager(batchInfo, -1, true, pages...) itr1 := s.constructTestHistoryIterator(historyV2Manager, targetBlobSize, nil) pages = []page{ { firstbatchIdx: 0, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 1, numBatches: 3, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 2, numBatches: 1, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 3, numBatches: 5, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, { firstbatchIdx: 4, numBatches: 4, firstEventFailoverVersion: 1, lastEventFailoverVersion: 1, }, } historyV2Manager = s.constructMockHistoryV2Manager(batchInfo, -1, true, pages...) itr2 := s.constructTestHistoryIterator(historyV2Manager, targetBlobSize, nil) totalPages := 3 expectedFirstEventID := []int64{1, 7, 14} for i := 0; i != totalPages; i++ { s.True(itr1.HasNext()) history1, err := itr1.Next() s.NoError(err) s.True(itr2.HasNext()) history2, err := itr2.Next() s.NoError(err) s.Equal(history1.Header, history2.Header) s.Equal(len(history1.Body), len(history2.Body)) s.Equal(expectedFirstEventID[i], history1.Body[0].Events[0].ID) s.Equal(expectedFirstEventID[i], history2.Body[0].Events[0].ID) } expectedIteratorState := historyIteratorState{ NextEventID: 0, FinishedIteration: true, } s.assertStateMatches(expectedIteratorState, itr1) s.assertStateMatches(expectedIteratorState, itr2) s.False(itr1.HasNext()) s.False(itr2.HasNext()) historyV2Manager.AssertExpectations(s.T()) } func (s *HistoryIteratorSuite) TestNewIteratorWithState() { itr := s.constructTestHistoryIterator(nil, testDefaultTargetHistoryBlobSize, nil) testIteratorState := historyIteratorState{ FinishedIteration: true, NextEventID: 4, } itr.historyIteratorState = testIteratorState stateToken, err := itr.GetState() s.NoError(err) newItr := s.constructTestHistoryIterator(nil, testDefaultTargetHistoryBlobSize, stateToken) s.assertStateMatches(testIteratorState, newItr) } func (s *HistoryIteratorSuite) constructMockHistoryV2Manager(batchInfo []int, returnErrorOnPage int, addNotExistCall bool, pages ...page) *mocks.HistoryV2Manager { mockHistoryV2Manager := &mocks.HistoryV2Manager{} firstEventIDs := []int64{constants.FirstEventID} for i, batchSize := range batchInfo { firstEventIDs = append(firstEventIDs, firstEventIDs[i]+int64(batchSize)) } for i, p := range pages { req := &persistence.ReadHistoryBranchRequest{ BranchToken: testBranchToken, MinEventID: firstEventIDs[p.firstbatchIdx], MaxEventID: constants.EndEventID, PageSize: testDefaultPersistencePageSize, ShardID: common.IntPtr(testShardID), DomainName: testDomainName, } if returnErrorOnPage == i { mockHistoryV2Manager.On("ReadHistoryBranchByBatch", mock.Anything, req).Return(nil, errors.New("got error getting workflow execution history")) return mockHistoryV2Manager } resp := &persistence.ReadHistoryBranchByBatchResponse{ History: s.constructHistoryBatches(batchInfo, p, firstEventIDs[p.firstbatchIdx]), } mockHistoryV2Manager.On("ReadHistoryBranchByBatch", mock.Anything, req).Return(resp, nil) } if addNotExistCall { req := &persistence.ReadHistoryBranchRequest{ BranchToken: testBranchToken, MinEventID: firstEventIDs[len(firstEventIDs)-1], MaxEventID: constants.EndEventID, PageSize: testDefaultPersistencePageSize, ShardID: common.IntPtr(testShardID), DomainName: testDomainName, } mockHistoryV2Manager.On("ReadHistoryBranchByBatch", mock.Anything, req).Return(nil, &types.EntityNotExistsError{Message: "Reach the end"}) } return mockHistoryV2Manager } func (s *HistoryIteratorSuite) copyIteratorState(itr *historyIterator) historyIteratorState { return itr.historyIteratorState } func (s *HistoryIteratorSuite) assertStateMatches(expected historyIteratorState, itr *historyIterator) { s.Equal(expected.NextEventID, itr.NextEventID) s.Equal(expected.FinishedIteration, itr.FinishedIteration) } func (s *HistoryIteratorSuite) constructHistoryBatches(batchInfo []int, page page, firstEventID int64) []*types.History { batches := []*types.History{} eventsID := firstEventID for batchIdx, numEvents := range batchInfo[page.firstbatchIdx : page.firstbatchIdx+page.numBatches] { events := []*types.HistoryEvent{} for i := 0; i < numEvents; i++ { event := &types.HistoryEvent{ ID: eventsID, Version: page.firstEventFailoverVersion, } eventsID++ if batchIdx == page.numBatches-1 { event.Version = page.lastEventFailoverVersion } events = append(events, event) } batches = append(batches, &types.History{ Events: events, }) } return batches } func (s *HistoryIteratorSuite) constructTestHistoryIterator( mockHistoryV2Manager *mocks.HistoryV2Manager, targetHistoryBlobSize int, initialState []byte, ) *historyIterator { request := &ArchiveHistoryRequest{ ShardID: testShardID, DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } itr := newHistoryIterator(context.Background(), request, mockHistoryV2Manager, targetHistoryBlobSize) if initialState != nil { err := itr.reset(initialState) s.NoError(err) } itr.sizeEstimator = newTestSizeEstimator() return itr } ================================================ FILE: common/archiver/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "context" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // ArchiveHistoryRequest is request to Archive workflow history ArchiveHistoryRequest struct { ShardID int DomainID string DomainName string WorkflowID string RunID string BranchToken []byte NextEventID int64 CloseFailoverVersion int64 } // GetHistoryRequest is the request to Get archived history GetHistoryRequest struct { DomainID string WorkflowID string RunID string CloseFailoverVersion *int64 NextPageToken []byte PageSize int } // GetHistoryResponse is the response of Get archived history GetHistoryResponse struct { HistoryBatches []*types.History NextPageToken []byte } // HistoryBootstrapContainer contains components needed by all history Archiver implementations HistoryBootstrapContainer struct { HistoryV2Manager persistence.HistoryManager Logger log.Logger MetricsClient metrics.Client ClusterMetadata cluster.Metadata DomainCache cache.DomainCache } // HistoryArchiver is used to archive history and read archived history HistoryArchiver interface { Archive(context.Context, URI, *ArchiveHistoryRequest, ...ArchiveOption) error Get(context.Context, URI, *GetHistoryRequest) (*GetHistoryResponse, error) ValidateURI(URI) error } // VisibilityBootstrapContainer contains components needed by all visibility Archiver implementations VisibilityBootstrapContainer struct { Logger log.Logger MetricsClient metrics.Client ClusterMetadata cluster.Metadata DomainCache cache.DomainCache } // ArchiveVisibilityRequest is request to Archive single workflow visibility record ArchiveVisibilityRequest struct { DomainID string DomainName string // doesn't need to be archived WorkflowID string RunID string WorkflowTypeName string StartTimestamp int64 ExecutionTimestamp int64 CloseTimestamp int64 CloseStatus types.WorkflowExecutionCloseStatus HistoryLength int64 Memo *types.Memo SearchAttributes map[string]string HistoryArchivalURI string } // QueryVisibilityRequest is the request to query archived visibility records QueryVisibilityRequest struct { DomainID string PageSize int NextPageToken []byte Query string } // QueryVisibilityResponse is the response of querying archived visibility records QueryVisibilityResponse struct { Executions []*types.WorkflowExecutionInfo NextPageToken []byte } // VisibilityArchiver is used to archive visibility and read archived visibility VisibilityArchiver interface { Archive(context.Context, URI, *ArchiveVisibilityRequest, ...ArchiveOption) error Query(context.Context, URI, *QueryVisibilityRequest) (*QueryVisibilityResponse, error) ValidateURI(URI) error } ) ================================================ FILE: common/archiver/interface_mock.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package archiver import ( context "context" mock "github.com/stretchr/testify/mock" ) // HistoryArchiverMock is an autogenerated mock type for the HistoryArchiver type type HistoryArchiverMock struct { mock.Mock } // Archive provides a mock function with given fields: ctx, uri, request, opts func (_m *HistoryArchiverMock) Archive(ctx context.Context, uri URI, request *ArchiveHistoryRequest, opts ...ArchiveOption) error { _va := make([]interface{}, len(opts)) for _i := range opts { _va[_i] = opts[_i] } var _ca []interface{} _ca = append(_ca, ctx, uri, request) _ca = append(_ca, _va...) ret := _m.Called(_ca...) var r0 error if rf, ok := ret.Get(0).(func(context.Context, URI, *ArchiveHistoryRequest, ...ArchiveOption) error); ok { r0 = rf(ctx, uri, request, opts...) } else { r0 = ret.Error(0) } return r0 } // Get provides a mock function with given fields: ctx, uri, request func (_m *HistoryArchiverMock) Get(ctx context.Context, uri URI, request *GetHistoryRequest) (*GetHistoryResponse, error) { ret := _m.Called(ctx, uri, request) var r0 *GetHistoryResponse if rf, ok := ret.Get(0).(func(context.Context, URI, *GetHistoryRequest) *GetHistoryResponse); ok { r0 = rf(ctx, uri, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*GetHistoryResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, URI, *GetHistoryRequest) error); ok { r1 = rf(ctx, uri, request) } else { r1 = ret.Error(1) } return r0, r1 } // ValidateURI provides a mock function with given fields: uri func (_m *HistoryArchiverMock) ValidateURI(uri URI) error { ret := _m.Called(uri) var r0 error if rf, ok := ret.Get(0).(func(URI) error); ok { r0 = rf(uri) } else { r0 = ret.Error(0) } return r0 } // VisibilityArchiverMock is an autogenerated mock type for the VisibilityArchiver type type VisibilityArchiverMock struct { mock.Mock } // Archive provides a mock function with given fields: _a0, _a1, _a2, _a3 func (_m *VisibilityArchiverMock) Archive(_a0 context.Context, _a1 URI, _a2 *ArchiveVisibilityRequest, _a3 ...ArchiveOption) error { _va := make([]interface{}, len(_a3)) for _i := range _a3 { _va[_i] = _a3[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1, _a2) _ca = append(_ca, _va...) ret := _m.Called(_ca...) var r0 error if rf, ok := ret.Get(0).(func(context.Context, URI, *ArchiveVisibilityRequest, ...ArchiveOption) error); ok { r0 = rf(_a0, _a1, _a2, _a3...) } else { r0 = ret.Error(0) } return r0 } // Query provides a mock function with given fields: _a0, _a1, _a2 func (_m *VisibilityArchiverMock) Query(_a0 context.Context, _a1 URI, _a2 *QueryVisibilityRequest) (*QueryVisibilityResponse, error) { ret := _m.Called(_a0, _a1, _a2) var r0 *QueryVisibilityResponse if rf, ok := ret.Get(0).(func(context.Context, URI, *QueryVisibilityRequest) *QueryVisibilityResponse); ok { r0 = rf(_a0, _a1, _a2) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*QueryVisibilityResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, URI, *QueryVisibilityRequest) error); ok { r1 = rf(_a0, _a1, _a2) } else { r1 = ret.Error(1) } return r0, r1 } // ValidateURI provides a mock function with given fields: uri func (_m *VisibilityArchiverMock) ValidateURI(uri URI) error { ret := _m.Called(uri) var r0 error if rf, ok := ret.Get(0).(func(URI) error); ok { r0 = rf(uri) } else { r0 = ret.Error(0) } return r0 } ================================================ FILE: common/archiver/options.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "context" "errors" "go.uber.org/cadence/activity" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) type ( // ArchiveOption is used to provide options for adding features to // the Archive method of History/Visibility Archiver ArchiveOption func(featureCatalog *ArchiveFeatureCatalog) // ArchiveFeatureCatalog is a collection features for the Archive method of // History/Visibility Archiver ArchiveFeatureCatalog struct { ProgressManager ProgressManager NonRetriableError NonRetriableError ArchiveIncompleteHistory dynamicproperties.BoolPropertyFn } // NonRetriableError returns an error indicating archiver has encountered an non-retriable error NonRetriableError func() error // ProgressManager is used to record and load archive progress ProgressManager interface { RecordProgress(ctx context.Context, progress interface{}) error LoadProgress(ctx context.Context, valuePtr interface{}) error HasProgress(ctx context.Context) bool } ) // GetFeatureCatalog applies all the ArchiveOptions to the catalog and returns the catalog. // It should be called inside the Archive method. func GetFeatureCatalog(opts ...ArchiveOption) *ArchiveFeatureCatalog { catalog := &ArchiveFeatureCatalog{ ArchiveIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), } for _, opt := range opts { opt(catalog) } return catalog } // GetHeartbeatArchiveOption returns an ArchiveOption for enabling heartbeating. // It should be used when the Archive method is invoked inside an activity. func GetHeartbeatArchiveOption() ArchiveOption { return func(catalog *ArchiveFeatureCatalog) { catalog.ProgressManager = &heartbeatProgressManager{} } } type heartbeatProgressManager struct{} func (h *heartbeatProgressManager) RecordProgress(ctx context.Context, progress interface{}) error { activity.RecordHeartbeat(ctx, progress) return nil } func (h *heartbeatProgressManager) LoadProgress(ctx context.Context, valuePtr interface{}) error { if !h.HasProgress(ctx) { return errors.New("no progress information in the context") } return activity.GetHeartbeatDetails(ctx, valuePtr) } func (h *heartbeatProgressManager) HasProgress(ctx context.Context) bool { return activity.HasHeartbeatDetails(ctx) } // GetNonRetriableErrorOption returns an ArchiveOption so that archiver knows what should // be returned when an non-retryable error is encountered. func GetNonRetriableErrorOption(nonRetryableErr error) ArchiveOption { return func(catalog *ArchiveFeatureCatalog) { catalog.NonRetriableError = func() error { return nonRetryableErr } } } // GetArchivingIncompleteHistoryOption returns an ArchiveOption so that archiver would archive incomplete history func GetArchivingIncompleteHistoryOption(allow dynamicproperties.BoolPropertyFn) ArchiveOption { return func(catalog *ArchiveFeatureCatalog) { catalog.ArchiveIncompleteHistory = allow } } ================================================ FILE: common/archiver/provider/init.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package provider import ( "fmt" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/filestore" "github.com/uber/cadence/common/archiver/s3store" "github.com/uber/cadence/common/config" ) func init() { // TODO: ideally remove this and handle per-instance registration during startup somehow, // as globals and inits have consistently caused issues. // // For now though, it's replacing a hard-coded switch statement, so an init func // is the most straightforward and should-be-identical conversion. must := func(err error) { if err != nil { panic(fmt.Errorf("failed to register default provider: %w", err)) } } must(RegisterHistoryArchiver(filestore.URIScheme, config.FilestoreConfig, func(cfg *config.YamlNode, container *archiver.HistoryBootstrapContainer) (archiver.HistoryArchiver, error) { var out *config.FilestoreArchiver if err := cfg.Decode(&out); err != nil { return nil, fmt.Errorf("bad config: %w", err) } return filestore.NewHistoryArchiver(container, out) })) must(RegisterHistoryArchiver(s3store.URIScheme, config.S3storeConfig, func(cfg *config.YamlNode, container *archiver.HistoryBootstrapContainer) (archiver.HistoryArchiver, error) { var out *config.S3Archiver if err := cfg.Decode(&out); err != nil { return nil, fmt.Errorf("bad config: %w", err) } return s3store.NewHistoryArchiver(container, out) })) must(RegisterVisibilityArchiver(filestore.URIScheme, config.FilestoreConfig, func(cfg *config.YamlNode, container *archiver.VisibilityBootstrapContainer) (archiver.VisibilityArchiver, error) { var out *config.FilestoreArchiver if err := cfg.Decode(&out); err != nil { return nil, fmt.Errorf("bad config: %w", err) } return filestore.NewVisibilityArchiver(container, out) })) must(RegisterVisibilityArchiver(s3store.URIScheme, config.S3storeConfig, func(cfg *config.YamlNode, container *archiver.VisibilityBootstrapContainer) (archiver.VisibilityArchiver, error) { var out *config.S3Archiver if err := cfg.Decode(&out); err != nil { return nil, fmt.Errorf("bad config: %w", err) } return s3store.NewVisibilityArchiver(container, out) })) } ================================================ FILE: common/archiver/provider/noop_provider.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package provider import ( "context" "github.com/uber/cadence/common/archiver" ) type noOpArchiverProvider struct{} func NewNoOpArchiverProvider() ArchiverProvider { return &noOpArchiverProvider{} } func (*noOpArchiverProvider) RegisterBootstrapContainer( serviceName string, historyContainer *archiver.HistoryBootstrapContainer, visibilityContainter *archiver.VisibilityBootstrapContainer, ) error { return nil } func (*noOpArchiverProvider) GetHistoryArchiver(scheme, serviceName string) (archiver.HistoryArchiver, error) { return &noOpHistoryArchiver{}, nil } func (*noOpArchiverProvider) GetVisibilityArchiver(scheme, serviceName string) (archiver.VisibilityArchiver, error) { return &noOpVisibilityArchiver{}, nil } type noOpHistoryArchiver struct{} func (*noOpHistoryArchiver) Archive(context.Context, archiver.URI, *archiver.ArchiveHistoryRequest, ...archiver.ArchiveOption) error { return nil } func (*noOpHistoryArchiver) Get(context.Context, archiver.URI, *archiver.GetHistoryRequest) (*archiver.GetHistoryResponse, error) { return &archiver.GetHistoryResponse{}, nil } func (*noOpHistoryArchiver) ValidateURI(archiver.URI) error { return nil } type noOpVisibilityArchiver struct{} func (*noOpVisibilityArchiver) Archive(context.Context, archiver.URI, *archiver.ArchiveVisibilityRequest, ...archiver.ArchiveOption) error { return nil } func (*noOpVisibilityArchiver) Query(context.Context, archiver.URI, *archiver.QueryVisibilityRequest) (*archiver.QueryVisibilityResponse, error) { return &archiver.QueryVisibilityResponse{}, nil } func (*noOpVisibilityArchiver) ValidateURI(archiver.URI) error { return nil } ================================================ FILE: common/archiver/provider/provider.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package provider import ( "errors" "fmt" "sync" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/syncmap" ) //go:generate mockgen -package=$GOPACKAGE -destination=provider_mock.go -self_package=github.com/uber/cadence/common/archiver/provider github.com/uber/cadence/common/archiver/provider ArchiverProvider var ( // ErrUnknownScheme is the error for unknown archiver scheme ErrUnknownScheme = errors.New("unknown archiver scheme") // ErrNotSupported is the error for not supported archiver implementation ErrNotSupported = errors.New("archiver provider not supported") // ErrBootstrapContainerNotFound is the error for unable to find the bootstrap container given serviceName ErrBootstrapContainerNotFound = errors.New("unable to find bootstrap container for the given service name") // ErrArchiverConfigNotFound is the error for unable to find the config for an archiver given scheme ErrArchiverConfigNotFound = errors.New("unable to find archiver config for the given scheme") // ErrBootstrapContainerAlreadyRegistered is the error for registering multiple containers for the same serviceName ErrBootstrapContainerAlreadyRegistered = errors.New("bootstrap container has already been registered") ) type ( // ArchiverProvider returns history or visibility archiver based on the scheme and serviceName. // The archiver for each combination of scheme and serviceName will be created only once and cached. ArchiverProvider interface { RegisterBootstrapContainer( serviceName string, historyContainer *archiver.HistoryBootstrapContainer, visibilityContainter *archiver.VisibilityBootstrapContainer, ) error GetHistoryArchiver(scheme, serviceName string) (archiver.HistoryArchiver, error) GetVisibilityArchiver(scheme, serviceName string) (archiver.VisibilityArchiver, error) } archiverProvider struct { sync.RWMutex historyArchiverConfigs config.HistoryArchiverProvider visibilityArchiverConfigs config.VisibilityArchiverProvider // Key for the container is just serviceName historyContainers map[string]*archiver.HistoryBootstrapContainer visibilityContainers map[string]*archiver.VisibilityBootstrapContainer // Key for the archiver is scheme + serviceName historyArchivers map[string]archiver.HistoryArchiver visibilityArchivers map[string]archiver.VisibilityArchiver } historyConstructor struct { fn func(cfg *config.YamlNode, container *archiver.HistoryBootstrapContainer) (archiver.HistoryArchiver, error) // yaml key where this config exists, under archival.history.provider. // This almost certainly should be the same as the scheme, but that'll need more work. configKey string } visibilityConstructor struct { fn func(cfg *config.YamlNode, container *archiver.VisibilityBootstrapContainer) (archiver.VisibilityArchiver, error) // yaml key where this config exists, under archival.visibility.provider. // This almost certainly should be the same as the scheme, but that'll need more work. configKey string } ) var ( historyConstructors = syncmap.New[string, historyConstructor]() visibilityConstructors = syncmap.New[string, visibilityConstructor]() ) func RegisterHistoryArchiver(scheme, configKey string, constructor func(cfg *config.YamlNode, container *archiver.HistoryBootstrapContainer) (archiver.HistoryArchiver, error)) error { inserted := historyConstructors.Put(scheme, historyConstructor{ fn: constructor, configKey: configKey, }) if !inserted { return fmt.Errorf("history archiver already registered for scheme %q", scheme) } return nil } func RegisterVisibilityArchiver(scheme, configKey string, constructor func(cfg *config.YamlNode, container *archiver.VisibilityBootstrapContainer) (archiver.VisibilityArchiver, error)) error { inserted := visibilityConstructors.Put(scheme, visibilityConstructor{ fn: constructor, configKey: configKey, }) if !inserted { return fmt.Errorf("visibility archiver already registered for scheme %q", scheme) } return nil } // NewArchiverProvider returns a new Archiver provider func NewArchiverProvider( historyArchiverConfigs config.HistoryArchiverProvider, visibilityArchiverConfigs config.VisibilityArchiverProvider, ) ArchiverProvider { return &archiverProvider{ historyArchiverConfigs: historyArchiverConfigs, visibilityArchiverConfigs: visibilityArchiverConfigs, historyContainers: make(map[string]*archiver.HistoryBootstrapContainer), visibilityContainers: make(map[string]*archiver.VisibilityBootstrapContainer), historyArchivers: make(map[string]archiver.HistoryArchiver), visibilityArchivers: make(map[string]archiver.VisibilityArchiver), } } // RegisterBootstrapContainer stores the given bootstrap container given the serviceName // The container should be registered when a service starts up and before GetArchiver() is ever called. // Later calls to GetArchiver() will used the registered container to initialize new archivers. // If the container for a service has already registered, and this method is invoked for that service again // with an non-nil container, an error will be returned. func (p *archiverProvider) RegisterBootstrapContainer( serviceName string, historyContainer *archiver.HistoryBootstrapContainer, visibilityContainter *archiver.VisibilityBootstrapContainer, ) error { p.Lock() defer p.Unlock() if _, ok := p.historyContainers[serviceName]; ok && historyContainer != nil { return ErrBootstrapContainerAlreadyRegistered } if _, ok := p.visibilityContainers[serviceName]; ok && visibilityContainter != nil { return ErrBootstrapContainerAlreadyRegistered } if historyContainer != nil { p.historyContainers[serviceName] = historyContainer } if visibilityContainter != nil { p.visibilityContainers[serviceName] = visibilityContainter } return nil } func (p *archiverProvider) GetHistoryArchiver(scheme, serviceName string) (historyArchiver archiver.HistoryArchiver, err error) { archiverKey := p.getArchiverKey(scheme, serviceName) p.RLock() if historyArchiver, ok := p.historyArchivers[archiverKey]; ok { p.RUnlock() return historyArchiver, nil } p.RUnlock() container, ok := p.historyContainers[serviceName] if !ok { return nil, ErrBootstrapContainerNotFound } constructor, ok := historyConstructors.Get(scheme) if !ok { return nil, fmt.Errorf("no history archiver constructor for scheme %q", scheme) } cfg, ok := p.historyArchiverConfigs[constructor.configKey] if !ok { return nil, fmt.Errorf("no history archiver config for scheme %q, config key %q", scheme, constructor.configKey) } historyArchiver, err = constructor.fn(cfg, container) if err != nil { return nil, fmt.Errorf("history archiver constructor failed for scheme %q, config key %q: err: %w", scheme, constructor.configKey, err) } p.Lock() defer p.Unlock() if existingHistoryArchiver, ok := p.historyArchivers[archiverKey]; ok { return existingHistoryArchiver, nil } p.historyArchivers[archiverKey] = historyArchiver return historyArchiver, nil } func (p *archiverProvider) GetVisibilityArchiver(scheme, serviceName string) (archiver.VisibilityArchiver, error) { archiverKey := p.getArchiverKey(scheme, serviceName) p.RLock() if visibilityArchiver, ok := p.visibilityArchivers[archiverKey]; ok { p.RUnlock() return visibilityArchiver, nil } p.RUnlock() container, ok := p.visibilityContainers[serviceName] if !ok { return nil, ErrBootstrapContainerNotFound } constructor, ok := visibilityConstructors.Get(scheme) if !ok { return nil, fmt.Errorf("no visibility archiver constructor for scheme %q", scheme) } cfg, ok := p.visibilityArchiverConfigs[constructor.configKey] if !ok { return nil, fmt.Errorf("no visibility archiver config for scheme %q, config key %q", scheme, constructor.configKey) } visibilityArchiver, err := constructor.fn(cfg, container) if err != nil { return nil, fmt.Errorf("visibility archiver constructor failed for scheme %q, config key %q: err: %w", scheme, constructor.configKey, err) } p.Lock() defer p.Unlock() if existingVisibilityArchiver, ok := p.visibilityArchivers[archiverKey]; ok { return existingVisibilityArchiver, nil } p.visibilityArchivers[archiverKey] = visibilityArchiver return visibilityArchiver, nil } func (p *archiverProvider) getArchiverKey(scheme, serviceName string) string { return scheme + ":" + serviceName } ================================================ FILE: common/archiver/provider/provider_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/archiver/provider (interfaces: ArchiverProvider) // // Generated by this command: // // mockgen -package=provider -destination=provider_mock.go -self_package=github.com/uber/cadence/common/archiver/provider github.com/uber/cadence/common/archiver/provider ArchiverProvider // // Package provider is a generated GoMock package. package provider import ( reflect "reflect" gomock "go.uber.org/mock/gomock" archiver "github.com/uber/cadence/common/archiver" ) // MockArchiverProvider is a mock of ArchiverProvider interface. type MockArchiverProvider struct { ctrl *gomock.Controller recorder *MockArchiverProviderMockRecorder isgomock struct{} } // MockArchiverProviderMockRecorder is the mock recorder for MockArchiverProvider. type MockArchiverProviderMockRecorder struct { mock *MockArchiverProvider } // NewMockArchiverProvider creates a new mock instance. func NewMockArchiverProvider(ctrl *gomock.Controller) *MockArchiverProvider { mock := &MockArchiverProvider{ctrl: ctrl} mock.recorder = &MockArchiverProviderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockArchiverProvider) EXPECT() *MockArchiverProviderMockRecorder { return m.recorder } // GetHistoryArchiver mocks base method. func (m *MockArchiverProvider) GetHistoryArchiver(scheme, serviceName string) (archiver.HistoryArchiver, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryArchiver", scheme, serviceName) ret0, _ := ret[0].(archiver.HistoryArchiver) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHistoryArchiver indicates an expected call of GetHistoryArchiver. func (mr *MockArchiverProviderMockRecorder) GetHistoryArchiver(scheme, serviceName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryArchiver", reflect.TypeOf((*MockArchiverProvider)(nil).GetHistoryArchiver), scheme, serviceName) } // GetVisibilityArchiver mocks base method. func (m *MockArchiverProvider) GetVisibilityArchiver(scheme, serviceName string) (archiver.VisibilityArchiver, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVisibilityArchiver", scheme, serviceName) ret0, _ := ret[0].(archiver.VisibilityArchiver) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVisibilityArchiver indicates an expected call of GetVisibilityArchiver. func (mr *MockArchiverProviderMockRecorder) GetVisibilityArchiver(scheme, serviceName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVisibilityArchiver", reflect.TypeOf((*MockArchiverProvider)(nil).GetVisibilityArchiver), scheme, serviceName) } // RegisterBootstrapContainer mocks base method. func (m *MockArchiverProvider) RegisterBootstrapContainer(serviceName string, historyContainer *archiver.HistoryBootstrapContainer, visibilityContainter *archiver.VisibilityBootstrapContainer) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RegisterBootstrapContainer", serviceName, historyContainer, visibilityContainter) ret0, _ := ret[0].(error) return ret0 } // RegisterBootstrapContainer indicates an expected call of RegisterBootstrapContainer. func (mr *MockArchiverProviderMockRecorder) RegisterBootstrapContainer(serviceName, historyContainer, visibilityContainter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterBootstrapContainer", reflect.TypeOf((*MockArchiverProvider)(nil).RegisterBootstrapContainer), serviceName, historyContainer, visibilityContainter) } ================================================ FILE: common/archiver/s3store/README.md ================================================ # Amazon S3 blobstore ## Configuration See https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials on how to set up authentication against s3 Enabling archival is done by using the configuration below. `Region` and `bucket URI` are required ``` archival: history: status: "enabled" enableRead: true provider: s3store: region: "us-east-1" visibility: status: "enabled" enableRead: true provider: s3store: region: "us-east-1" domainDefaults: archival: history: status: "enabled" URI: "s3://" visibility: status: "enabled" URI: "s3://" ``` ## Visibility query syntax You can query the visibility store by using the `cadence workflow listarchived` command The syntax for the query is based on SQL Supported column names are - WorkflowID *String* - WorkflowTypeName *String* - StartTime *Date* - CloseTime *Date* - SearchPrecision *String - Day, Hour, Minute, Second* WorkflowID or WorkflowTypeName is required. If filtering on date use StartTime or CloseTime in combination with SearchPrecision. Searching for a record will be done in times in the UTC timezone SearchPrecision specifies what range you want to search for records. If you use `SearchPrecision = 'Day'` it will search all records starting from `2020-01-21T00:00:00Z` to `2020-01-21T59:59:59Z` ### Limitations - The only operator supported is `=` due to how records are stored in s3. ### Example *Searches for all records done in day 2020-01-21 with the specified workflow id* `./cadence --do samples-domain workflow listarchived -q "StartTime = '2020-01-21T00:00:00Z' AND WorkflowID='workflow-id' AND SearchPrecision='Day'"` ## Storage in S3 Workflow runs are stored in s3 using the following structure ``` s3://// history// visibility/ workflowTypeName// startTimeout/2020-01-21T16:16:11Z/ closeTimeout/2020-01-21T16:16:11Z/ workflowID// startTimeout/2020-01-21T16:16:11Z/ closeTimeout/2020-01-21T16:16:11Z/ ``` ## Using localstack for local development 1. Install awscli from [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html) 2. Install localstack from [here](https://github.com/localstack/localstack#installing) 3. Launch localstack with `SERVICES=s3 localstack start` 4. Create a bucket using `aws --endpoint-url=http://localhost:4572 s3 mb s3://cadence-development` 5. Configure archival and domainDefaults with the following configuration ``` archival: history: status: "enabled" enableRead: true provider: s3store: region: "us-east-1" endpoint: "http://127.0.0.1:4572" s3ForcePathStyle: true visibility: status: "enabled" enableRead: true provider: s3store: region: "us-east-1" endpoint: "http://127.0.0.1:4572" s3ForcePathStyle: true domainDefaults: archival: history: status: "enabled" URI: "s3://cadence-development" visibility: status: "enabled" URI: "s3://cadence-development" ``` ================================================ FILE: common/archiver/s3store/historyArchiver.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // S3 History Archiver will archive workflow histories to amazon s3 package s3store import ( "context" "encoding/binary" "errors" "strconv" "strings" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3iface" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const ( // URIScheme is the scheme for the s3 implementation URIScheme = "s3" errEncodeHistory = "failed to encode history batches" errWriteKey = "failed to write history to s3" defaultBlobstoreTimeout = 60 * time.Second targetHistoryBlobSize = 2 * 1024 * 1024 // 2MB ) var ( errNoBucketSpecified = errors.New("no bucket specified") errBucketNotExists = errors.New("requested bucket does not exist") errEmptyAwsRegion = errors.New("empty aws region") ) type ( historyArchiver struct { container *archiver.HistoryBootstrapContainer s3cli s3iface.S3API // only set in test code historyIterator archiver.HistoryIterator } getHistoryToken struct { CloseFailoverVersion int64 BatchIdx int } uploadProgress struct { BatchIdx int IteratorState []byte uploadedSize int64 historySize int64 } ) // NewHistoryArchiver creates a new archiver.HistoryArchiver based on s3 func NewHistoryArchiver( container *archiver.HistoryBootstrapContainer, config *config.S3Archiver, ) (archiver.HistoryArchiver, error) { return newHistoryArchiver(container, config, nil) } func newHistoryArchiver( container *archiver.HistoryBootstrapContainer, config *config.S3Archiver, historyIterator archiver.HistoryIterator, ) (*historyArchiver, error) { if len(config.Region) == 0 { return nil, errEmptyAwsRegion } s3Config := &aws.Config{ Endpoint: config.Endpoint, Region: aws.String(config.Region), S3ForcePathStyle: aws.Bool(config.S3ForcePathStyle), } sess, err := session.NewSession(s3Config) if err != nil { return nil, err } return &historyArchiver{ container: container, s3cli: s3.New(sess), historyIterator: historyIterator, }, nil } func (h *historyArchiver) Archive( ctx context.Context, URI archiver.URI, request *archiver.ArchiveHistoryRequest, opts ...archiver.ArchiveOption, ) (err error) { scope := h.container.MetricsClient.Scope(metrics.HistoryArchiverScope, metrics.DomainTag(request.DomainName)) featureCatalog := archiver.GetFeatureCatalog(opts...) sw := scope.StartTimer(metrics.CadenceLatency) defer func() { sw.Stop() if err != nil { if persistence.IsTransientError(err) || isRetryableError(err) { scope.IncCounter(metrics.HistoryArchiverArchiveTransientErrorCount) } else { scope.IncCounter(metrics.HistoryArchiverArchiveNonRetryableErrorCount) if featureCatalog.NonRetriableError != nil { err = featureCatalog.NonRetriableError() } } } }() logger := archiver.TagLoggerWithArchiveHistoryRequestAndURI(h.container.Logger, request, URI.String()) if err := softValidateURI(URI); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidURI), tag.Error(err)) return err } if err := archiver.ValidateHistoryArchiveRequest(request); err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiver.ErrReasonInvalidArchiveRequest), tag.Error(err)) return err } var progress uploadProgress historyIterator := h.historyIterator if historyIterator == nil { // will only be set by testing code historyIterator = loadHistoryIterator(ctx, request, h.container.HistoryV2Manager, featureCatalog, &progress) } for historyIterator.HasNext() { historyBlob, err := getNextHistoryBlob(ctx, historyIterator) if err != nil { if common.IsEntityNotExistsError(err) { // workflow history no longer exists, may due to duplicated archival signal // this may happen even in the middle of iterating history as two archival signals // can be processed concurrently. logger.Info(archiver.ArchiveSkippedInfoMsg) scope.IncCounter(metrics.HistoryArchiverDuplicateArchivalsCount) return nil } logger := logger.WithTags(tag.ArchivalArchiveFailReason(archiver.ErrReasonReadHistory), tag.Error(err)) if persistence.IsTransientError(err) { logger.Error(archiver.ArchiveTransientErrorMsg) } else { logger.Error(archiver.ArchiveNonRetriableErrorMsg) } return err } if archiver.IsHistoryMutated(request, historyBlob.Body, *historyBlob.Header.IsLast, logger) { if !featureCatalog.ArchiveIncompleteHistory() { return archiver.ErrHistoryMutated } } encodedHistoryBlob, err := encode(historyBlob) if err != nil { logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(errEncodeHistory), tag.Error(err)) return err } key := constructHistoryKey(URI.Path(), request.DomainID, request.WorkflowID, request.RunID, request.CloseFailoverVersion, progress.BatchIdx) exists, err := keyExists(ctx, h.s3cli, URI, key) if err != nil { logger := logger.WithTags(tag.ArchivalArchiveFailReason(errWriteKey), tag.Error(err)) if isRetryableError(err) { logger.Error(archiver.ArchiveTransientErrorMsg) } else { logger.Error(archiver.ArchiveNonRetriableErrorMsg) } return err } blobSize := int64(binary.Size(encodedHistoryBlob)) if exists { scope.IncCounter(metrics.HistoryArchiverBlobExistsCount) } else { if err := upload(ctx, h.s3cli, URI, key, encodedHistoryBlob); err != nil { logger := logger.WithTags(tag.ArchivalArchiveFailReason(errWriteKey), tag.Error(err)) if isRetryableError(err) { logger.Error(archiver.ArchiveTransientErrorMsg) } else { logger.Error(archiver.ArchiveNonRetriableErrorMsg) } return err } progress.uploadedSize += blobSize scope.RecordTimer(metrics.HistoryArchiverBlobSize, time.Duration(blobSize)) } progress.historySize += blobSize progress.BatchIdx = progress.BatchIdx + 1 saveHistoryIteratorState(ctx, featureCatalog, historyIterator, &progress) } scope.RecordTimer(metrics.HistoryArchiverTotalUploadSize, time.Duration(progress.uploadedSize)) scope.RecordTimer(metrics.HistoryArchiverHistorySize, time.Duration(progress.historySize)) scope.IncCounter(metrics.HistoryArchiverArchiveSuccessCount) return nil } func loadHistoryIterator(ctx context.Context, request *archiver.ArchiveHistoryRequest, historyManager persistence.HistoryManager, featureCatalog *archiver.ArchiveFeatureCatalog, progress *uploadProgress) (historyIterator archiver.HistoryIterator) { if featureCatalog.ProgressManager != nil { if featureCatalog.ProgressManager.HasProgress(ctx) { err := featureCatalog.ProgressManager.LoadProgress(ctx, progress) if err == nil { historyIterator, err := archiver.NewHistoryIteratorFromState(ctx, request, historyManager, targetHistoryBlobSize, progress.IteratorState) if err == nil { return historyIterator } } progress.IteratorState = nil progress.BatchIdx = 0 progress.historySize = 0 progress.uploadedSize = 0 } } return archiver.NewHistoryIterator(ctx, request, historyManager, targetHistoryBlobSize) } func saveHistoryIteratorState(ctx context.Context, featureCatalog *archiver.ArchiveFeatureCatalog, historyIterator archiver.HistoryIterator, progress *uploadProgress) { // Saving history state is a best effort operation. Ignore errors and continue if featureCatalog.ProgressManager != nil { state, err := historyIterator.GetState() if err != nil { return } progress.IteratorState = state err = featureCatalog.ProgressManager.RecordProgress(ctx, progress) if err != nil { return } } } func (h *historyArchiver) Get( ctx context.Context, URI archiver.URI, request *archiver.GetHistoryRequest, ) (*archiver.GetHistoryResponse, error) { if err := softValidateURI(URI); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidURI.Error()} } if err := archiver.ValidateGetRequest(request); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidGetHistoryRequest.Error()} } var err error var token *getHistoryToken if request.NextPageToken != nil { token, err = deserializeGetHistoryToken(request.NextPageToken) if err != nil { return nil, &types.BadRequestError{Message: archiver.ErrNextPageTokenCorrupted.Error()} } } else if request.CloseFailoverVersion != nil { token = &getHistoryToken{ CloseFailoverVersion: *request.CloseFailoverVersion, } } else { highestVersion, err := h.getHighestVersion(ctx, URI, request) if err != nil { return nil, &types.BadRequestError{Message: err.Error()} } token = &getHistoryToken{ CloseFailoverVersion: *highestVersion, } } response := &archiver.GetHistoryResponse{} numOfEvents := 0 isTruncated := false for { if numOfEvents >= request.PageSize { isTruncated = true break } key := constructHistoryKey(URI.Path(), request.DomainID, request.WorkflowID, request.RunID, token.CloseFailoverVersion, token.BatchIdx) encodedRecord, err := download(ctx, h.s3cli, URI, key) if err != nil { if isRetryableError(err) { return nil, &types.InternalServiceError{Message: err.Error()} } switch err.(type) { case *types.BadRequestError, *types.InternalServiceError, *types.EntityNotExistsError: return nil, err default: return nil, &types.InternalServiceError{Message: err.Error()} } } historyBlob, err := decodeHistoryBlob(encodedRecord) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } for _, batch := range historyBlob.Body { response.HistoryBatches = append(response.HistoryBatches, batch) numOfEvents += len(batch.Events) } if *historyBlob.Header.IsLast { break } token.BatchIdx++ } if isTruncated { nextToken, err := serializeToken(token) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } response.NextPageToken = nextToken } return response, nil } func (h *historyArchiver) ValidateURI(URI archiver.URI) error { err := softValidateURI(URI) if err != nil { return err } return bucketExists(context.TODO(), h.s3cli, URI) } func getNextHistoryBlob(ctx context.Context, historyIterator archiver.HistoryIterator) (*archiver.HistoryBlob, error) { historyBlob, err := historyIterator.Next() op := func(ctx context.Context) error { historyBlob, err = historyIterator.Next() return err } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(common.CreatePersistenceRetryPolicy()), backoff.WithRetryableError(persistence.IsTransientError), ) for err != nil { if contextExpired(ctx) { return nil, archiver.ErrContextTimeout } if !persistence.IsTransientError(err) { return nil, err } err = throttleRetry.Do(ctx, op) } return historyBlob, nil } // with XDC(global domain) concept, archival may write different history with the same RunID, with different failoverVersion. // In that case, the history/runID with the highest failoverVersion wins. // getHighestVersion look up all archived files to find the highest failoverVersion. func (h *historyArchiver) getHighestVersion(ctx context.Context, URI archiver.URI, request *archiver.GetHistoryRequest) (*int64, error) { ctx, cancel := ensureContextTimeout(ctx) defer cancel() var prefix = constructHistoryKeyPrefix(URI.Path(), request.DomainID, request.WorkflowID, request.RunID) + "/" results, err := h.s3cli.ListObjectsV2WithContext(ctx, &s3.ListObjectsV2Input{ Bucket: aws.String(URI.Hostname()), Prefix: aws.String(prefix), Delimiter: aws.String("/"), }) if err != nil { if aerr, ok := err.(awserr.Error); ok && aerr.Code() == s3.ErrCodeNoSuchBucket { return nil, &types.BadRequestError{Message: errBucketNotExists.Error()} } return nil, err } var highestVersion *int64 for _, v := range results.CommonPrefixes { var version int64 version, err = strconv.ParseInt(strings.Replace(strings.Replace(*v.Prefix, prefix, "", 1), "/", "", 1), 10, 64) if err != nil { continue } if highestVersion == nil || version > *highestVersion { highestVersion = &version } } if highestVersion == nil { return nil, archiver.ErrHistoryNotExist } return highestVersion, nil } func isRetryableError(err error) bool { if err == nil { return false } if aerr, ok := err.(awserr.Error); ok { return isStatusCodeRetryable(aerr) || request.IsErrorRetryable(aerr) || request.IsErrorThrottle(aerr) } return false } func isStatusCodeRetryable(err error) bool { if aerr, ok := err.(awserr.Error); ok { if rerr, ok := err.(awserr.RequestFailure); ok { if rerr.StatusCode() == 429 { return true } if rerr.StatusCode() >= 500 && rerr.StatusCode() != 501 { return true } } return isStatusCodeRetryable(aerr.OrigErr()) } return false } ================================================ FILE: common/archiver/s3store/historyArchiver_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package s3store import ( "bytes" "context" "errors" "fmt" "io/ioutil" "sort" "strconv" "strings" "testing" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/service/s3" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/s3store/mocks" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) const ( testDomainID = "test-domain-id" testDomainName = "test-domain-name" testWorkflowID = "test-workflow-id" testRunID = "test-run-id" testNextEventID = 1800 testCloseFailoverVersion = 100 testPageSize = 100 testBucket = "test-bucket" testBucketURI = "s3://test-bucket" ) var ( testBranchToken = []byte{1, 2, 3} ) type historyArchiverSuite struct { *require.Assertions suite.Suite s3cli *mocks.S3API container *archiver.HistoryBootstrapContainer testArchivalURI archiver.URI historyBatchesV1 []*archiver.HistoryBlob historyBatchesV100 []*archiver.HistoryBlob } func TestHistoryArchiverSuite(t *testing.T) { suite.Run(t, new(historyArchiverSuite)) } func (s *historyArchiverSuite) SetupSuite() { var err error s.s3cli = &mocks.S3API{} setupFsEmulation(s.s3cli) s.setupHistoryDirectory() s.testArchivalURI, err = archiver.NewURI(testBucketURI) s.Require().NoError(err) } func (s *historyArchiverSuite) TearDownSuite() { } func (s *historyArchiverSuite) SetupTest() { scope := tally.NewTestScope("test", nil) s.Assertions = require.New(s.T()) s.container = &archiver.HistoryBootstrapContainer{ Logger: testlogger.New(s.T()), MetricsClient: metrics.NewClient(scope, metrics.History, metrics.HistogramMigration{}), } } func setupFsEmulation(s3cli *mocks.S3API) { fs := make(map[string][]byte) putObjectFn := func(_ aws.Context, input *s3.PutObjectInput, _ ...request.Option) *s3.PutObjectOutput { buf := new(bytes.Buffer) buf.ReadFrom(input.Body) fs[*input.Bucket+*input.Key] = buf.Bytes() return &s3.PutObjectOutput{} } getObjectFn := func(_ aws.Context, input *s3.GetObjectInput, _ ...request.Option) *s3.GetObjectOutput { return &s3.GetObjectOutput{ Body: ioutil.NopCloser(bytes.NewReader(fs[*input.Bucket+*input.Key])), } } s3cli.On("ListObjectsV2WithContext", mock.Anything, mock.Anything). Return(func(_ context.Context, input *s3.ListObjectsV2Input, opts ...request.Option) *s3.ListObjectsV2Output { objects := make([]*s3.Object, 0) commonPrefixMap := map[string]bool{} for k := range fs { if strings.HasPrefix(k, *input.Bucket+*input.Prefix) { key := k[len(*input.Bucket):] keyWithoutPrefix := key[len(*input.Prefix):] index := strings.Index(keyWithoutPrefix, "/") if index == -1 || input.Delimiter == nil { objects = append(objects, &s3.Object{ Key: aws.String(key), }) } else { commonPrefixMap[key[:len(*input.Prefix)+index]] = true } } } commonPrefixes := make([]*s3.CommonPrefix, 0) for k := range commonPrefixMap { commonPrefixes = append(commonPrefixes, &s3.CommonPrefix{ Prefix: aws.String(k), }) } sort.SliceStable(objects, func(i, j int) bool { return *objects[i].Key < *objects[j].Key }) maxKeys := 1000 if input.MaxKeys != nil { maxKeys = int(*input.MaxKeys) } start := 0 if input.ContinuationToken != nil { start, _ = strconv.Atoi(*input.ContinuationToken) } if input.StartAfter != nil { for k, v := range objects { if *input.StartAfter == *v.Key { start = k + 1 } } } isTruncated := false var nextContinuationToken *string if len(objects) > start+maxKeys { isTruncated = true nextContinuationToken = common.StringPtr(fmt.Sprintf("%d", start+maxKeys)) objects = objects[start : start+maxKeys] } else { objects = objects[start:] } if input.StartAfter != nil { for k, v := range commonPrefixes { if *input.StartAfter == *v.Prefix { start = k + 1 } } } if len(commonPrefixes) > start+maxKeys { isTruncated = true nextContinuationToken = common.StringPtr(fmt.Sprintf("%d", start+maxKeys)) commonPrefixes = commonPrefixes[start : start+maxKeys] } else if len(commonPrefixes) > 0 { commonPrefixes = commonPrefixes[start:] } return &s3.ListObjectsV2Output{ CommonPrefixes: commonPrefixes, Contents: objects, IsTruncated: &isTruncated, NextContinuationToken: nextContinuationToken, } }, nil) s3cli.On("PutObjectWithContext", mock.Anything, mock.Anything).Return(putObjectFn, nil) s3cli.On("HeadObjectWithContext", mock.Anything, mock.MatchedBy(func(input *s3.HeadObjectInput) bool { _, ok := fs[*input.Bucket+*input.Key] return !ok })).Return(nil, awserr.New("NotFound", "", nil)) s3cli.On("HeadObjectWithContext", mock.Anything, mock.Anything).Return(&s3.HeadObjectOutput{}, nil) s3cli.On("GetObjectWithContext", mock.Anything, mock.MatchedBy(func(input *s3.GetObjectInput) bool { _, ok := fs[*input.Bucket+*input.Key] return !ok })).Return(nil, awserr.New(s3.ErrCodeNoSuchKey, "", nil)) s3cli.On("GetObjectWithContext", mock.Anything, mock.Anything).Return(getObjectFn, nil) } func (s *historyArchiverSuite) TestValidateURI() { testCases := []struct { URI string expectedErr error }{ { URI: "wrongscheme:///a/b/c", expectedErr: archiver.ErrURISchemeMismatch, }, { URI: "s3://", expectedErr: errNoBucketSpecified, }, { URI: "s3://bucket/a/b/c", expectedErr: errBucketNotExists, }, { URI: testBucketURI, expectedErr: nil, }, } s.s3cli.On("HeadBucketWithContext", mock.Anything, mock.MatchedBy(func(input *s3.HeadBucketInput) bool { return *input.Bucket != s.testArchivalURI.Hostname() })).Return(nil, awserr.New("NotFound", "", nil)) s.s3cli.On("HeadBucketWithContext", mock.Anything, mock.Anything).Return(&s3.HeadBucketOutput{}, nil) historyArchiver := s.newTestHistoryArchiver(nil) for _, tc := range testCases { URI, err := archiver.NewURI(tc.URI) s.NoError(err) s.Equal(tc.expectedErr, historyArchiver.ValidateURI(URI)) } } func (s *historyArchiverSuite) TestArchive_Fail_InvalidURI() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } URI, err := archiver.NewURI("wrongscheme://") s.NoError(err) err = historyArchiver.Archive(context.Background(), URI, request) s.Error(err) } func (s *historyArchiverSuite) TestArchive_Fail_InvalidRequest() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: "", // an invalid request RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request) s.Error(err) } func (s *historyArchiverSuite) TestArchive_Fail_ErrorOnReadHistory() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, errors.New("some random error")), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request) s.Error(err) } func (s *historyArchiverSuite) TestArchive_Fail_TimeoutWhenReadingHistory() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, &types.ServiceBusyError{}), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err := historyArchiver.Archive(getCanceledContext(), s.testArchivalURI, request) s.Error(err) } func (s *historyArchiverSuite) TestArchive_Fail_HistoryMutated() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyBatches := []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion + 1, }, }, }, } historyBlob := &archiver.HistoryBlob{ Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(true), }, Body: historyBatches, } gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(historyBlob, nil), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request) s.Error(err) } func (s *historyArchiverSuite) TestArchive_Fail_NonRetriableErrorOption() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, errors.New("some random error")), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } nonRetryableErr := errors.New("some non-retryable error") err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request, archiver.GetNonRetriableErrorOption(nonRetryableErr)) s.Equal(nonRetryableErr, err) } func (s *historyArchiverSuite) TestArchive_Skip() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyBlob := &archiver.HistoryBlob{ Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(false), }, Body: []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, }, } gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(historyBlob, nil), historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(nil, &types.EntityNotExistsError{Message: "workflow not found"}), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } URI, err := archiver.NewURI(testBucketURI + "/TestArchive_Skip") s.NoError(err) err = historyArchiver.Archive(context.Background(), URI, request) s.NoError(err) expectedkey := constructHistoryKey("", testDomainID, testWorkflowID, testRunID, testCloseFailoverVersion, 0) s.assertKeyExists(expectedkey) } func (s *historyArchiverSuite) TestArchive_Success() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) historyBatches := []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, { ID: constants.FirstEventID + 2, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, { Events: []*types.HistoryEvent{ { ID: testNextEventID - 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, } historyBlob := &archiver.HistoryBlob{ Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(true), }, Body: historyBatches, } gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(historyBlob, nil), historyIterator.EXPECT().HasNext().Return(false), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) request := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } URI, err := archiver.NewURI(testBucketURI + "/TestArchive_Success") s.NoError(err) err = historyArchiver.Archive(context.Background(), URI, request) s.NoError(err) expectedkey := constructHistoryKey("", testDomainID, testWorkflowID, testRunID, testCloseFailoverVersion, 0) s.assertKeyExists(expectedkey) } func (s *historyArchiverSuite) TestGet_Fail_InvalidURI() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: 100, } URI, err := archiver.NewURI("wrongscheme://") s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.Nil(response) s.Error(err) } func (s *historyArchiverSuite) TestGet_Fail_InvalidRequest() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: 0, // pageSize should be greater than 0 } response, err := historyArchiver.Get(context.Background(), s.testArchivalURI, request) s.Nil(response) s.Error(err) s.IsType(&types.BadRequestError{}, err) } func (s *historyArchiverSuite) TestGet_Fail_InvalidToken() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, NextPageToken: []byte{'r', 'a', 'n', 'd', 'o', 'm'}, } URI, err := archiver.NewURI(testBucketURI) s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.Nil(response) s.Error(err) s.IsType(&types.BadRequestError{}, err) } func (s *historyArchiverSuite) TestGet_Fail_KeyNotExist() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, CloseFailoverVersion: common.Int64Ptr(testCloseFailoverVersion), } URI, err := archiver.NewURI("s3://test-bucket/non-existent") s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.Nil(response) s.Error(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *historyArchiverSuite) TestGet_Success_PickHighestVersion() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, } URI, err := archiver.NewURI(testBucketURI) s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.NoError(err) s.Nil(response.NextPageToken) s.Equal(append(s.historyBatchesV100[0].Body, s.historyBatchesV100[1].Body...), response.HistoryBatches) } func (s *historyArchiverSuite) TestGet_Success_UseProvidedVersion() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, CloseFailoverVersion: common.Int64Ptr(1), } URI, err := archiver.NewURI(testBucketURI) s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.NoError(err) s.Nil(response.NextPageToken) s.Equal(s.historyBatchesV1[0].Body, response.HistoryBatches) } func (s *historyArchiverSuite) TestGet_Success_SmallPageSize() { historyArchiver := s.newTestHistoryArchiver(nil) request := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: 1, CloseFailoverVersion: common.Int64Ptr(100), } combinedHistory := []*types.History{} URI, err := archiver.NewURI(testBucketURI) s.NoError(err) response, err := historyArchiver.Get(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.NotNil(response.NextPageToken) s.NotNil(response.HistoryBatches) s.Len(response.HistoryBatches, 1) combinedHistory = append(combinedHistory, response.HistoryBatches...) request.NextPageToken = response.NextPageToken response, err = historyArchiver.Get(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.Nil(response.NextPageToken) s.NotNil(response.HistoryBatches) s.Len(response.HistoryBatches, 1) combinedHistory = append(combinedHistory, response.HistoryBatches...) s.Equal(append(s.historyBatchesV100[0].Body, s.historyBatchesV100[1].Body...), combinedHistory) } func (s *historyArchiverSuite) TestArchiveAndGet() { mockCtrl := gomock.NewController(s.T()) historyIterator := archiver.NewMockHistoryIterator(mockCtrl) gomock.InOrder( historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(s.historyBatchesV100[0], nil), historyIterator.EXPECT().HasNext().Return(true), historyIterator.EXPECT().Next().Return(s.historyBatchesV100[1], nil), historyIterator.EXPECT().HasNext().Return(false), ) historyArchiver := s.newTestHistoryArchiver(historyIterator) archiveRequest := &archiver.ArchiveHistoryRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, } URI, err := archiver.NewURI(testBucketURI + "/TestArchiveAndGet") s.NoError(err) err = historyArchiver.Archive(context.Background(), URI, archiveRequest) s.NoError(err) getRequest := &archiver.GetHistoryRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, PageSize: testPageSize, } response, err := historyArchiver.Get(context.Background(), URI, getRequest) s.NoError(err) s.NotNil(response) s.Nil(response.NextPageToken) s.Equal(append(s.historyBatchesV100[0].Body, s.historyBatchesV100[1].Body...), response.HistoryBatches) } func (s *historyArchiverSuite) newTestHistoryArchiver(historyIterator archiver.HistoryIterator) *historyArchiver { // config := &config.S3Archiver{} // archiver, err := newHistoryArchiver(s.container, config, historyIterator) archiver := &historyArchiver{ container: s.container, s3cli: s.s3cli, historyIterator: historyIterator, } return archiver } func (s *historyArchiverSuite) setupHistoryDirectory() { s.historyBatchesV1 = []*archiver.HistoryBlob{ { Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(true), }, Body: []*types.History{ { Events: []*types.HistoryEvent{ { ID: testNextEventID - 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: 1, }, }, }, }, }, } s.historyBatchesV100 = []*archiver.HistoryBlob{ { Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(false), }, Body: []*types.History{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, { ID: constants.FirstEventID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, }, }, { Header: &archiver.HistoryBlobHeader{ IsLast: common.BoolPtr(true), }, Body: []*types.History{ { Events: []*types.HistoryEvent{ { ID: testNextEventID - 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: testCloseFailoverVersion, }, }, }, }, }, } s.writeHistoryBatchesForGetTest(s.historyBatchesV1, int64(1)) s.writeHistoryBatchesForGetTest(s.historyBatchesV100, testCloseFailoverVersion) } func (s *historyArchiverSuite) writeHistoryBatchesForGetTest(historyBatches []*archiver.HistoryBlob, version int64) { for i, batch := range historyBatches { data, err := encode(batch) s.Require().NoError(err) key := constructHistoryKey("", testDomainID, testWorkflowID, testRunID, version, i) _, err = s.s3cli.PutObjectWithContext(context.Background(), &s3.PutObjectInput{ Bucket: aws.String(testBucket), Key: aws.String(key), Body: bytes.NewReader(data), }) s.Require().NoError(err) } } func (s *historyArchiverSuite) assertKeyExists(key string) { _, err := s.s3cli.GetObjectWithContext(context.Background(), &s3.GetObjectInput{ Bucket: aws.String(testBucket), Key: aws.String(key), }) s.NoError(err) } func getCanceledContext() context.Context { ctx, cancel := context.WithCancel(context.Background()) cancel() return ctx } ================================================ FILE: common/archiver/s3store/mocks/S3API_generate.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package mocks //go:generate mockery --srcpkg github.com/aws/aws-sdk-go/service/s3/s3iface --quiet --name S3API --output . --filename s3_api_mock.go ================================================ FILE: common/archiver/s3store/mocks/s3_api_mock.go ================================================ // Code generated by mockery v2.53.5. DO NOT EDIT. package mocks import ( context "context" request "github.com/aws/aws-sdk-go/aws/request" s3 "github.com/aws/aws-sdk-go/service/s3" mock "github.com/stretchr/testify/mock" ) // S3API is an autogenerated mock type for the S3API type type S3API struct { mock.Mock } // AbortMultipartUpload provides a mock function with given fields: _a0 func (_m *S3API) AbortMultipartUpload(_a0 *s3.AbortMultipartUploadInput) (*s3.AbortMultipartUploadOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for AbortMultipartUpload") } var r0 *s3.AbortMultipartUploadOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.AbortMultipartUploadInput) (*s3.AbortMultipartUploadOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.AbortMultipartUploadInput) *s3.AbortMultipartUploadOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.AbortMultipartUploadOutput) } } if rf, ok := ret.Get(1).(func(*s3.AbortMultipartUploadInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // AbortMultipartUploadRequest provides a mock function with given fields: _a0 func (_m *S3API) AbortMultipartUploadRequest(_a0 *s3.AbortMultipartUploadInput) (*request.Request, *s3.AbortMultipartUploadOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for AbortMultipartUploadRequest") } var r0 *request.Request var r1 *s3.AbortMultipartUploadOutput if rf, ok := ret.Get(0).(func(*s3.AbortMultipartUploadInput) (*request.Request, *s3.AbortMultipartUploadOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.AbortMultipartUploadInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.AbortMultipartUploadInput) *s3.AbortMultipartUploadOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.AbortMultipartUploadOutput) } } return r0, r1 } // AbortMultipartUploadWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) AbortMultipartUploadWithContext(_a0 context.Context, _a1 *s3.AbortMultipartUploadInput, _a2 ...request.Option) (*s3.AbortMultipartUploadOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for AbortMultipartUploadWithContext") } var r0 *s3.AbortMultipartUploadOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.AbortMultipartUploadInput, ...request.Option) (*s3.AbortMultipartUploadOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.AbortMultipartUploadInput, ...request.Option) *s3.AbortMultipartUploadOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.AbortMultipartUploadOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.AbortMultipartUploadInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // CompleteMultipartUpload provides a mock function with given fields: _a0 func (_m *S3API) CompleteMultipartUpload(_a0 *s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for CompleteMultipartUpload") } var r0 *s3.CompleteMultipartUploadOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.CompleteMultipartUploadInput) *s3.CompleteMultipartUploadOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.CompleteMultipartUploadOutput) } } if rf, ok := ret.Get(1).(func(*s3.CompleteMultipartUploadInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // CompleteMultipartUploadRequest provides a mock function with given fields: _a0 func (_m *S3API) CompleteMultipartUploadRequest(_a0 *s3.CompleteMultipartUploadInput) (*request.Request, *s3.CompleteMultipartUploadOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for CompleteMultipartUploadRequest") } var r0 *request.Request var r1 *s3.CompleteMultipartUploadOutput if rf, ok := ret.Get(0).(func(*s3.CompleteMultipartUploadInput) (*request.Request, *s3.CompleteMultipartUploadOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.CompleteMultipartUploadInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.CompleteMultipartUploadInput) *s3.CompleteMultipartUploadOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.CompleteMultipartUploadOutput) } } return r0, r1 } // CompleteMultipartUploadWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) CompleteMultipartUploadWithContext(_a0 context.Context, _a1 *s3.CompleteMultipartUploadInput, _a2 ...request.Option) (*s3.CompleteMultipartUploadOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for CompleteMultipartUploadWithContext") } var r0 *s3.CompleteMultipartUploadOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.CompleteMultipartUploadInput, ...request.Option) (*s3.CompleteMultipartUploadOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.CompleteMultipartUploadInput, ...request.Option) *s3.CompleteMultipartUploadOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.CompleteMultipartUploadOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.CompleteMultipartUploadInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // CopyObject provides a mock function with given fields: _a0 func (_m *S3API) CopyObject(_a0 *s3.CopyObjectInput) (*s3.CopyObjectOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for CopyObject") } var r0 *s3.CopyObjectOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.CopyObjectInput) (*s3.CopyObjectOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.CopyObjectInput) *s3.CopyObjectOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.CopyObjectOutput) } } if rf, ok := ret.Get(1).(func(*s3.CopyObjectInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // CopyObjectRequest provides a mock function with given fields: _a0 func (_m *S3API) CopyObjectRequest(_a0 *s3.CopyObjectInput) (*request.Request, *s3.CopyObjectOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for CopyObjectRequest") } var r0 *request.Request var r1 *s3.CopyObjectOutput if rf, ok := ret.Get(0).(func(*s3.CopyObjectInput) (*request.Request, *s3.CopyObjectOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.CopyObjectInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.CopyObjectInput) *s3.CopyObjectOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.CopyObjectOutput) } } return r0, r1 } // CopyObjectWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) CopyObjectWithContext(_a0 context.Context, _a1 *s3.CopyObjectInput, _a2 ...request.Option) (*s3.CopyObjectOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for CopyObjectWithContext") } var r0 *s3.CopyObjectOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.CopyObjectInput, ...request.Option) (*s3.CopyObjectOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.CopyObjectInput, ...request.Option) *s3.CopyObjectOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.CopyObjectOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.CopyObjectInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // CreateBucket provides a mock function with given fields: _a0 func (_m *S3API) CreateBucket(_a0 *s3.CreateBucketInput) (*s3.CreateBucketOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for CreateBucket") } var r0 *s3.CreateBucketOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.CreateBucketInput) (*s3.CreateBucketOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.CreateBucketInput) *s3.CreateBucketOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.CreateBucketOutput) } } if rf, ok := ret.Get(1).(func(*s3.CreateBucketInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // CreateBucketRequest provides a mock function with given fields: _a0 func (_m *S3API) CreateBucketRequest(_a0 *s3.CreateBucketInput) (*request.Request, *s3.CreateBucketOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for CreateBucketRequest") } var r0 *request.Request var r1 *s3.CreateBucketOutput if rf, ok := ret.Get(0).(func(*s3.CreateBucketInput) (*request.Request, *s3.CreateBucketOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.CreateBucketInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.CreateBucketInput) *s3.CreateBucketOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.CreateBucketOutput) } } return r0, r1 } // CreateBucketWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) CreateBucketWithContext(_a0 context.Context, _a1 *s3.CreateBucketInput, _a2 ...request.Option) (*s3.CreateBucketOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for CreateBucketWithContext") } var r0 *s3.CreateBucketOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.CreateBucketInput, ...request.Option) (*s3.CreateBucketOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.CreateBucketInput, ...request.Option) *s3.CreateBucketOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.CreateBucketOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.CreateBucketInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // CreateMultipartUpload provides a mock function with given fields: _a0 func (_m *S3API) CreateMultipartUpload(_a0 *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for CreateMultipartUpload") } var r0 *s3.CreateMultipartUploadOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.CreateMultipartUploadInput) *s3.CreateMultipartUploadOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.CreateMultipartUploadOutput) } } if rf, ok := ret.Get(1).(func(*s3.CreateMultipartUploadInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // CreateMultipartUploadRequest provides a mock function with given fields: _a0 func (_m *S3API) CreateMultipartUploadRequest(_a0 *s3.CreateMultipartUploadInput) (*request.Request, *s3.CreateMultipartUploadOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for CreateMultipartUploadRequest") } var r0 *request.Request var r1 *s3.CreateMultipartUploadOutput if rf, ok := ret.Get(0).(func(*s3.CreateMultipartUploadInput) (*request.Request, *s3.CreateMultipartUploadOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.CreateMultipartUploadInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.CreateMultipartUploadInput) *s3.CreateMultipartUploadOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.CreateMultipartUploadOutput) } } return r0, r1 } // CreateMultipartUploadWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) CreateMultipartUploadWithContext(_a0 context.Context, _a1 *s3.CreateMultipartUploadInput, _a2 ...request.Option) (*s3.CreateMultipartUploadOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for CreateMultipartUploadWithContext") } var r0 *s3.CreateMultipartUploadOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.CreateMultipartUploadInput, ...request.Option) (*s3.CreateMultipartUploadOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.CreateMultipartUploadInput, ...request.Option) *s3.CreateMultipartUploadOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.CreateMultipartUploadOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.CreateMultipartUploadInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // CreateSession provides a mock function with given fields: _a0 func (_m *S3API) CreateSession(_a0 *s3.CreateSessionInput) (*s3.CreateSessionOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for CreateSession") } var r0 *s3.CreateSessionOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.CreateSessionInput) (*s3.CreateSessionOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.CreateSessionInput) *s3.CreateSessionOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.CreateSessionOutput) } } if rf, ok := ret.Get(1).(func(*s3.CreateSessionInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // CreateSessionRequest provides a mock function with given fields: _a0 func (_m *S3API) CreateSessionRequest(_a0 *s3.CreateSessionInput) (*request.Request, *s3.CreateSessionOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for CreateSessionRequest") } var r0 *request.Request var r1 *s3.CreateSessionOutput if rf, ok := ret.Get(0).(func(*s3.CreateSessionInput) (*request.Request, *s3.CreateSessionOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.CreateSessionInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.CreateSessionInput) *s3.CreateSessionOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.CreateSessionOutput) } } return r0, r1 } // CreateSessionWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) CreateSessionWithContext(_a0 context.Context, _a1 *s3.CreateSessionInput, _a2 ...request.Option) (*s3.CreateSessionOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for CreateSessionWithContext") } var r0 *s3.CreateSessionOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.CreateSessionInput, ...request.Option) (*s3.CreateSessionOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.CreateSessionInput, ...request.Option) *s3.CreateSessionOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.CreateSessionOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.CreateSessionInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucket provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucket(_a0 *s3.DeleteBucketInput) (*s3.DeleteBucketOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucket") } var r0 *s3.DeleteBucketOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketInput) (*s3.DeleteBucketOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketInput) *s3.DeleteBucketOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketAnalyticsConfiguration provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketAnalyticsConfiguration(_a0 *s3.DeleteBucketAnalyticsConfigurationInput) (*s3.DeleteBucketAnalyticsConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketAnalyticsConfiguration") } var r0 *s3.DeleteBucketAnalyticsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketAnalyticsConfigurationInput) (*s3.DeleteBucketAnalyticsConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketAnalyticsConfigurationInput) *s3.DeleteBucketAnalyticsConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketAnalyticsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketAnalyticsConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketAnalyticsConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketAnalyticsConfigurationRequest(_a0 *s3.DeleteBucketAnalyticsConfigurationInput) (*request.Request, *s3.DeleteBucketAnalyticsConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketAnalyticsConfigurationRequest") } var r0 *request.Request var r1 *s3.DeleteBucketAnalyticsConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketAnalyticsConfigurationInput) (*request.Request, *s3.DeleteBucketAnalyticsConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketAnalyticsConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketAnalyticsConfigurationInput) *s3.DeleteBucketAnalyticsConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketAnalyticsConfigurationOutput) } } return r0, r1 } // DeleteBucketAnalyticsConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketAnalyticsConfigurationWithContext(_a0 context.Context, _a1 *s3.DeleteBucketAnalyticsConfigurationInput, _a2 ...request.Option) (*s3.DeleteBucketAnalyticsConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketAnalyticsConfigurationWithContext") } var r0 *s3.DeleteBucketAnalyticsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketAnalyticsConfigurationInput, ...request.Option) (*s3.DeleteBucketAnalyticsConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketAnalyticsConfigurationInput, ...request.Option) *s3.DeleteBucketAnalyticsConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketAnalyticsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketAnalyticsConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketCors provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketCors(_a0 *s3.DeleteBucketCorsInput) (*s3.DeleteBucketCorsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketCors") } var r0 *s3.DeleteBucketCorsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketCorsInput) (*s3.DeleteBucketCorsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketCorsInput) *s3.DeleteBucketCorsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketCorsOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketCorsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketCorsRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketCorsRequest(_a0 *s3.DeleteBucketCorsInput) (*request.Request, *s3.DeleteBucketCorsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketCorsRequest") } var r0 *request.Request var r1 *s3.DeleteBucketCorsOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketCorsInput) (*request.Request, *s3.DeleteBucketCorsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketCorsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketCorsInput) *s3.DeleteBucketCorsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketCorsOutput) } } return r0, r1 } // DeleteBucketCorsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketCorsWithContext(_a0 context.Context, _a1 *s3.DeleteBucketCorsInput, _a2 ...request.Option) (*s3.DeleteBucketCorsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketCorsWithContext") } var r0 *s3.DeleteBucketCorsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketCorsInput, ...request.Option) (*s3.DeleteBucketCorsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketCorsInput, ...request.Option) *s3.DeleteBucketCorsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketCorsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketCorsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketEncryption provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketEncryption(_a0 *s3.DeleteBucketEncryptionInput) (*s3.DeleteBucketEncryptionOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketEncryption") } var r0 *s3.DeleteBucketEncryptionOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketEncryptionInput) (*s3.DeleteBucketEncryptionOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketEncryptionInput) *s3.DeleteBucketEncryptionOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketEncryptionOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketEncryptionInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketEncryptionRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketEncryptionRequest(_a0 *s3.DeleteBucketEncryptionInput) (*request.Request, *s3.DeleteBucketEncryptionOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketEncryptionRequest") } var r0 *request.Request var r1 *s3.DeleteBucketEncryptionOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketEncryptionInput) (*request.Request, *s3.DeleteBucketEncryptionOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketEncryptionInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketEncryptionInput) *s3.DeleteBucketEncryptionOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketEncryptionOutput) } } return r0, r1 } // DeleteBucketEncryptionWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketEncryptionWithContext(_a0 context.Context, _a1 *s3.DeleteBucketEncryptionInput, _a2 ...request.Option) (*s3.DeleteBucketEncryptionOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketEncryptionWithContext") } var r0 *s3.DeleteBucketEncryptionOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketEncryptionInput, ...request.Option) (*s3.DeleteBucketEncryptionOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketEncryptionInput, ...request.Option) *s3.DeleteBucketEncryptionOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketEncryptionOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketEncryptionInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketIntelligentTieringConfiguration provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketIntelligentTieringConfiguration(_a0 *s3.DeleteBucketIntelligentTieringConfigurationInput) (*s3.DeleteBucketIntelligentTieringConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketIntelligentTieringConfiguration") } var r0 *s3.DeleteBucketIntelligentTieringConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketIntelligentTieringConfigurationInput) (*s3.DeleteBucketIntelligentTieringConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketIntelligentTieringConfigurationInput) *s3.DeleteBucketIntelligentTieringConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketIntelligentTieringConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketIntelligentTieringConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketIntelligentTieringConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketIntelligentTieringConfigurationRequest(_a0 *s3.DeleteBucketIntelligentTieringConfigurationInput) (*request.Request, *s3.DeleteBucketIntelligentTieringConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketIntelligentTieringConfigurationRequest") } var r0 *request.Request var r1 *s3.DeleteBucketIntelligentTieringConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketIntelligentTieringConfigurationInput) (*request.Request, *s3.DeleteBucketIntelligentTieringConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketIntelligentTieringConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketIntelligentTieringConfigurationInput) *s3.DeleteBucketIntelligentTieringConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketIntelligentTieringConfigurationOutput) } } return r0, r1 } // DeleteBucketIntelligentTieringConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketIntelligentTieringConfigurationWithContext(_a0 context.Context, _a1 *s3.DeleteBucketIntelligentTieringConfigurationInput, _a2 ...request.Option) (*s3.DeleteBucketIntelligentTieringConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketIntelligentTieringConfigurationWithContext") } var r0 *s3.DeleteBucketIntelligentTieringConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketIntelligentTieringConfigurationInput, ...request.Option) (*s3.DeleteBucketIntelligentTieringConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketIntelligentTieringConfigurationInput, ...request.Option) *s3.DeleteBucketIntelligentTieringConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketIntelligentTieringConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketIntelligentTieringConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketInventoryConfiguration provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketInventoryConfiguration(_a0 *s3.DeleteBucketInventoryConfigurationInput) (*s3.DeleteBucketInventoryConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketInventoryConfiguration") } var r0 *s3.DeleteBucketInventoryConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketInventoryConfigurationInput) (*s3.DeleteBucketInventoryConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketInventoryConfigurationInput) *s3.DeleteBucketInventoryConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketInventoryConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketInventoryConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketInventoryConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketInventoryConfigurationRequest(_a0 *s3.DeleteBucketInventoryConfigurationInput) (*request.Request, *s3.DeleteBucketInventoryConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketInventoryConfigurationRequest") } var r0 *request.Request var r1 *s3.DeleteBucketInventoryConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketInventoryConfigurationInput) (*request.Request, *s3.DeleteBucketInventoryConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketInventoryConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketInventoryConfigurationInput) *s3.DeleteBucketInventoryConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketInventoryConfigurationOutput) } } return r0, r1 } // DeleteBucketInventoryConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketInventoryConfigurationWithContext(_a0 context.Context, _a1 *s3.DeleteBucketInventoryConfigurationInput, _a2 ...request.Option) (*s3.DeleteBucketInventoryConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketInventoryConfigurationWithContext") } var r0 *s3.DeleteBucketInventoryConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketInventoryConfigurationInput, ...request.Option) (*s3.DeleteBucketInventoryConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketInventoryConfigurationInput, ...request.Option) *s3.DeleteBucketInventoryConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketInventoryConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketInventoryConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketLifecycle provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketLifecycle(_a0 *s3.DeleteBucketLifecycleInput) (*s3.DeleteBucketLifecycleOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketLifecycle") } var r0 *s3.DeleteBucketLifecycleOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketLifecycleInput) (*s3.DeleteBucketLifecycleOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketLifecycleInput) *s3.DeleteBucketLifecycleOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketLifecycleOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketLifecycleInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketLifecycleRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketLifecycleRequest(_a0 *s3.DeleteBucketLifecycleInput) (*request.Request, *s3.DeleteBucketLifecycleOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketLifecycleRequest") } var r0 *request.Request var r1 *s3.DeleteBucketLifecycleOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketLifecycleInput) (*request.Request, *s3.DeleteBucketLifecycleOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketLifecycleInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketLifecycleInput) *s3.DeleteBucketLifecycleOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketLifecycleOutput) } } return r0, r1 } // DeleteBucketLifecycleWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketLifecycleWithContext(_a0 context.Context, _a1 *s3.DeleteBucketLifecycleInput, _a2 ...request.Option) (*s3.DeleteBucketLifecycleOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketLifecycleWithContext") } var r0 *s3.DeleteBucketLifecycleOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketLifecycleInput, ...request.Option) (*s3.DeleteBucketLifecycleOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketLifecycleInput, ...request.Option) *s3.DeleteBucketLifecycleOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketLifecycleOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketLifecycleInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketMetricsConfiguration provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketMetricsConfiguration(_a0 *s3.DeleteBucketMetricsConfigurationInput) (*s3.DeleteBucketMetricsConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketMetricsConfiguration") } var r0 *s3.DeleteBucketMetricsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketMetricsConfigurationInput) (*s3.DeleteBucketMetricsConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketMetricsConfigurationInput) *s3.DeleteBucketMetricsConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketMetricsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketMetricsConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketMetricsConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketMetricsConfigurationRequest(_a0 *s3.DeleteBucketMetricsConfigurationInput) (*request.Request, *s3.DeleteBucketMetricsConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketMetricsConfigurationRequest") } var r0 *request.Request var r1 *s3.DeleteBucketMetricsConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketMetricsConfigurationInput) (*request.Request, *s3.DeleteBucketMetricsConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketMetricsConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketMetricsConfigurationInput) *s3.DeleteBucketMetricsConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketMetricsConfigurationOutput) } } return r0, r1 } // DeleteBucketMetricsConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketMetricsConfigurationWithContext(_a0 context.Context, _a1 *s3.DeleteBucketMetricsConfigurationInput, _a2 ...request.Option) (*s3.DeleteBucketMetricsConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketMetricsConfigurationWithContext") } var r0 *s3.DeleteBucketMetricsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketMetricsConfigurationInput, ...request.Option) (*s3.DeleteBucketMetricsConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketMetricsConfigurationInput, ...request.Option) *s3.DeleteBucketMetricsConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketMetricsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketMetricsConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketOwnershipControls provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketOwnershipControls(_a0 *s3.DeleteBucketOwnershipControlsInput) (*s3.DeleteBucketOwnershipControlsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketOwnershipControls") } var r0 *s3.DeleteBucketOwnershipControlsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketOwnershipControlsInput) (*s3.DeleteBucketOwnershipControlsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketOwnershipControlsInput) *s3.DeleteBucketOwnershipControlsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketOwnershipControlsOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketOwnershipControlsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketOwnershipControlsRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketOwnershipControlsRequest(_a0 *s3.DeleteBucketOwnershipControlsInput) (*request.Request, *s3.DeleteBucketOwnershipControlsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketOwnershipControlsRequest") } var r0 *request.Request var r1 *s3.DeleteBucketOwnershipControlsOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketOwnershipControlsInput) (*request.Request, *s3.DeleteBucketOwnershipControlsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketOwnershipControlsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketOwnershipControlsInput) *s3.DeleteBucketOwnershipControlsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketOwnershipControlsOutput) } } return r0, r1 } // DeleteBucketOwnershipControlsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketOwnershipControlsWithContext(_a0 context.Context, _a1 *s3.DeleteBucketOwnershipControlsInput, _a2 ...request.Option) (*s3.DeleteBucketOwnershipControlsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketOwnershipControlsWithContext") } var r0 *s3.DeleteBucketOwnershipControlsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketOwnershipControlsInput, ...request.Option) (*s3.DeleteBucketOwnershipControlsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketOwnershipControlsInput, ...request.Option) *s3.DeleteBucketOwnershipControlsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketOwnershipControlsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketOwnershipControlsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketPolicy provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketPolicy(_a0 *s3.DeleteBucketPolicyInput) (*s3.DeleteBucketPolicyOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketPolicy") } var r0 *s3.DeleteBucketPolicyOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketPolicyInput) (*s3.DeleteBucketPolicyOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketPolicyInput) *s3.DeleteBucketPolicyOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketPolicyOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketPolicyInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketPolicyRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketPolicyRequest(_a0 *s3.DeleteBucketPolicyInput) (*request.Request, *s3.DeleteBucketPolicyOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketPolicyRequest") } var r0 *request.Request var r1 *s3.DeleteBucketPolicyOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketPolicyInput) (*request.Request, *s3.DeleteBucketPolicyOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketPolicyInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketPolicyInput) *s3.DeleteBucketPolicyOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketPolicyOutput) } } return r0, r1 } // DeleteBucketPolicyWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketPolicyWithContext(_a0 context.Context, _a1 *s3.DeleteBucketPolicyInput, _a2 ...request.Option) (*s3.DeleteBucketPolicyOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketPolicyWithContext") } var r0 *s3.DeleteBucketPolicyOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketPolicyInput, ...request.Option) (*s3.DeleteBucketPolicyOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketPolicyInput, ...request.Option) *s3.DeleteBucketPolicyOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketPolicyOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketPolicyInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketReplication provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketReplication(_a0 *s3.DeleteBucketReplicationInput) (*s3.DeleteBucketReplicationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketReplication") } var r0 *s3.DeleteBucketReplicationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketReplicationInput) (*s3.DeleteBucketReplicationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketReplicationInput) *s3.DeleteBucketReplicationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketReplicationOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketReplicationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketReplicationRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketReplicationRequest(_a0 *s3.DeleteBucketReplicationInput) (*request.Request, *s3.DeleteBucketReplicationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketReplicationRequest") } var r0 *request.Request var r1 *s3.DeleteBucketReplicationOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketReplicationInput) (*request.Request, *s3.DeleteBucketReplicationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketReplicationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketReplicationInput) *s3.DeleteBucketReplicationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketReplicationOutput) } } return r0, r1 } // DeleteBucketReplicationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketReplicationWithContext(_a0 context.Context, _a1 *s3.DeleteBucketReplicationInput, _a2 ...request.Option) (*s3.DeleteBucketReplicationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketReplicationWithContext") } var r0 *s3.DeleteBucketReplicationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketReplicationInput, ...request.Option) (*s3.DeleteBucketReplicationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketReplicationInput, ...request.Option) *s3.DeleteBucketReplicationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketReplicationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketReplicationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketRequest(_a0 *s3.DeleteBucketInput) (*request.Request, *s3.DeleteBucketOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketRequest") } var r0 *request.Request var r1 *s3.DeleteBucketOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketInput) (*request.Request, *s3.DeleteBucketOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketInput) *s3.DeleteBucketOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketOutput) } } return r0, r1 } // DeleteBucketTagging provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketTagging(_a0 *s3.DeleteBucketTaggingInput) (*s3.DeleteBucketTaggingOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketTagging") } var r0 *s3.DeleteBucketTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketTaggingInput) (*s3.DeleteBucketTaggingOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketTaggingInput) *s3.DeleteBucketTaggingOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketTaggingOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketTaggingInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketTaggingRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketTaggingRequest(_a0 *s3.DeleteBucketTaggingInput) (*request.Request, *s3.DeleteBucketTaggingOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketTaggingRequest") } var r0 *request.Request var r1 *s3.DeleteBucketTaggingOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketTaggingInput) (*request.Request, *s3.DeleteBucketTaggingOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketTaggingInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketTaggingInput) *s3.DeleteBucketTaggingOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketTaggingOutput) } } return r0, r1 } // DeleteBucketTaggingWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketTaggingWithContext(_a0 context.Context, _a1 *s3.DeleteBucketTaggingInput, _a2 ...request.Option) (*s3.DeleteBucketTaggingOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketTaggingWithContext") } var r0 *s3.DeleteBucketTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketTaggingInput, ...request.Option) (*s3.DeleteBucketTaggingOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketTaggingInput, ...request.Option) *s3.DeleteBucketTaggingOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketTaggingOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketTaggingInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketWebsite provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketWebsite(_a0 *s3.DeleteBucketWebsiteInput) (*s3.DeleteBucketWebsiteOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketWebsite") } var r0 *s3.DeleteBucketWebsiteOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteBucketWebsiteInput) (*s3.DeleteBucketWebsiteOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketWebsiteInput) *s3.DeleteBucketWebsiteOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketWebsiteOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketWebsiteInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketWebsiteRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteBucketWebsiteRequest(_a0 *s3.DeleteBucketWebsiteInput) (*request.Request, *s3.DeleteBucketWebsiteOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteBucketWebsiteRequest") } var r0 *request.Request var r1 *s3.DeleteBucketWebsiteOutput if rf, ok := ret.Get(0).(func(*s3.DeleteBucketWebsiteInput) (*request.Request, *s3.DeleteBucketWebsiteOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteBucketWebsiteInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteBucketWebsiteInput) *s3.DeleteBucketWebsiteOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteBucketWebsiteOutput) } } return r0, r1 } // DeleteBucketWebsiteWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketWebsiteWithContext(_a0 context.Context, _a1 *s3.DeleteBucketWebsiteInput, _a2 ...request.Option) (*s3.DeleteBucketWebsiteOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketWebsiteWithContext") } var r0 *s3.DeleteBucketWebsiteOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketWebsiteInput, ...request.Option) (*s3.DeleteBucketWebsiteOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketWebsiteInput, ...request.Option) *s3.DeleteBucketWebsiteOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketWebsiteOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketWebsiteInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteBucketWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteBucketWithContext(_a0 context.Context, _a1 *s3.DeleteBucketInput, _a2 ...request.Option) (*s3.DeleteBucketOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteBucketWithContext") } var r0 *s3.DeleteBucketOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketInput, ...request.Option) (*s3.DeleteBucketOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteBucketInput, ...request.Option) *s3.DeleteBucketOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteBucketOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteBucketInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteObject provides a mock function with given fields: _a0 func (_m *S3API) DeleteObject(_a0 *s3.DeleteObjectInput) (*s3.DeleteObjectOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteObject") } var r0 *s3.DeleteObjectOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteObjectInput) (*s3.DeleteObjectOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteObjectInput) *s3.DeleteObjectOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteObjectOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteObjectInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteObjectRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteObjectRequest(_a0 *s3.DeleteObjectInput) (*request.Request, *s3.DeleteObjectOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteObjectRequest") } var r0 *request.Request var r1 *s3.DeleteObjectOutput if rf, ok := ret.Get(0).(func(*s3.DeleteObjectInput) (*request.Request, *s3.DeleteObjectOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteObjectInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteObjectInput) *s3.DeleteObjectOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteObjectOutput) } } return r0, r1 } // DeleteObjectTagging provides a mock function with given fields: _a0 func (_m *S3API) DeleteObjectTagging(_a0 *s3.DeleteObjectTaggingInput) (*s3.DeleteObjectTaggingOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteObjectTagging") } var r0 *s3.DeleteObjectTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteObjectTaggingInput) (*s3.DeleteObjectTaggingOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteObjectTaggingInput) *s3.DeleteObjectTaggingOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteObjectTaggingOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteObjectTaggingInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteObjectTaggingRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteObjectTaggingRequest(_a0 *s3.DeleteObjectTaggingInput) (*request.Request, *s3.DeleteObjectTaggingOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteObjectTaggingRequest") } var r0 *request.Request var r1 *s3.DeleteObjectTaggingOutput if rf, ok := ret.Get(0).(func(*s3.DeleteObjectTaggingInput) (*request.Request, *s3.DeleteObjectTaggingOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteObjectTaggingInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteObjectTaggingInput) *s3.DeleteObjectTaggingOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteObjectTaggingOutput) } } return r0, r1 } // DeleteObjectTaggingWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteObjectTaggingWithContext(_a0 context.Context, _a1 *s3.DeleteObjectTaggingInput, _a2 ...request.Option) (*s3.DeleteObjectTaggingOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteObjectTaggingWithContext") } var r0 *s3.DeleteObjectTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteObjectTaggingInput, ...request.Option) (*s3.DeleteObjectTaggingOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteObjectTaggingInput, ...request.Option) *s3.DeleteObjectTaggingOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteObjectTaggingOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteObjectTaggingInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteObjectWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteObjectWithContext(_a0 context.Context, _a1 *s3.DeleteObjectInput, _a2 ...request.Option) (*s3.DeleteObjectOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteObjectWithContext") } var r0 *s3.DeleteObjectOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteObjectInput, ...request.Option) (*s3.DeleteObjectOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteObjectInput, ...request.Option) *s3.DeleteObjectOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteObjectOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteObjectInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteObjects provides a mock function with given fields: _a0 func (_m *S3API) DeleteObjects(_a0 *s3.DeleteObjectsInput) (*s3.DeleteObjectsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteObjects") } var r0 *s3.DeleteObjectsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeleteObjectsInput) (*s3.DeleteObjectsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteObjectsInput) *s3.DeleteObjectsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteObjectsOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeleteObjectsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteObjectsRequest provides a mock function with given fields: _a0 func (_m *S3API) DeleteObjectsRequest(_a0 *s3.DeleteObjectsInput) (*request.Request, *s3.DeleteObjectsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeleteObjectsRequest") } var r0 *request.Request var r1 *s3.DeleteObjectsOutput if rf, ok := ret.Get(0).(func(*s3.DeleteObjectsInput) (*request.Request, *s3.DeleteObjectsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeleteObjectsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeleteObjectsInput) *s3.DeleteObjectsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeleteObjectsOutput) } } return r0, r1 } // DeleteObjectsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeleteObjectsWithContext(_a0 context.Context, _a1 *s3.DeleteObjectsInput, _a2 ...request.Option) (*s3.DeleteObjectsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeleteObjectsWithContext") } var r0 *s3.DeleteObjectsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteObjectsInput, ...request.Option) (*s3.DeleteObjectsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeleteObjectsInput, ...request.Option) *s3.DeleteObjectsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeleteObjectsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeleteObjectsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // DeletePublicAccessBlock provides a mock function with given fields: _a0 func (_m *S3API) DeletePublicAccessBlock(_a0 *s3.DeletePublicAccessBlockInput) (*s3.DeletePublicAccessBlockOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeletePublicAccessBlock") } var r0 *s3.DeletePublicAccessBlockOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.DeletePublicAccessBlockInput) (*s3.DeletePublicAccessBlockOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeletePublicAccessBlockInput) *s3.DeletePublicAccessBlockOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeletePublicAccessBlockOutput) } } if rf, ok := ret.Get(1).(func(*s3.DeletePublicAccessBlockInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // DeletePublicAccessBlockRequest provides a mock function with given fields: _a0 func (_m *S3API) DeletePublicAccessBlockRequest(_a0 *s3.DeletePublicAccessBlockInput) (*request.Request, *s3.DeletePublicAccessBlockOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for DeletePublicAccessBlockRequest") } var r0 *request.Request var r1 *s3.DeletePublicAccessBlockOutput if rf, ok := ret.Get(0).(func(*s3.DeletePublicAccessBlockInput) (*request.Request, *s3.DeletePublicAccessBlockOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.DeletePublicAccessBlockInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.DeletePublicAccessBlockInput) *s3.DeletePublicAccessBlockOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.DeletePublicAccessBlockOutput) } } return r0, r1 } // DeletePublicAccessBlockWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) DeletePublicAccessBlockWithContext(_a0 context.Context, _a1 *s3.DeletePublicAccessBlockInput, _a2 ...request.Option) (*s3.DeletePublicAccessBlockOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for DeletePublicAccessBlockWithContext") } var r0 *s3.DeletePublicAccessBlockOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.DeletePublicAccessBlockInput, ...request.Option) (*s3.DeletePublicAccessBlockOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.DeletePublicAccessBlockInput, ...request.Option) *s3.DeletePublicAccessBlockOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.DeletePublicAccessBlockOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.DeletePublicAccessBlockInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketAccelerateConfiguration provides a mock function with given fields: _a0 func (_m *S3API) GetBucketAccelerateConfiguration(_a0 *s3.GetBucketAccelerateConfigurationInput) (*s3.GetBucketAccelerateConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketAccelerateConfiguration") } var r0 *s3.GetBucketAccelerateConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketAccelerateConfigurationInput) (*s3.GetBucketAccelerateConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketAccelerateConfigurationInput) *s3.GetBucketAccelerateConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketAccelerateConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketAccelerateConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketAccelerateConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketAccelerateConfigurationRequest(_a0 *s3.GetBucketAccelerateConfigurationInput) (*request.Request, *s3.GetBucketAccelerateConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketAccelerateConfigurationRequest") } var r0 *request.Request var r1 *s3.GetBucketAccelerateConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketAccelerateConfigurationInput) (*request.Request, *s3.GetBucketAccelerateConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketAccelerateConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketAccelerateConfigurationInput) *s3.GetBucketAccelerateConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketAccelerateConfigurationOutput) } } return r0, r1 } // GetBucketAccelerateConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketAccelerateConfigurationWithContext(_a0 context.Context, _a1 *s3.GetBucketAccelerateConfigurationInput, _a2 ...request.Option) (*s3.GetBucketAccelerateConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketAccelerateConfigurationWithContext") } var r0 *s3.GetBucketAccelerateConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketAccelerateConfigurationInput, ...request.Option) (*s3.GetBucketAccelerateConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketAccelerateConfigurationInput, ...request.Option) *s3.GetBucketAccelerateConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketAccelerateConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketAccelerateConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketAcl provides a mock function with given fields: _a0 func (_m *S3API) GetBucketAcl(_a0 *s3.GetBucketAclInput) (*s3.GetBucketAclOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketAcl") } var r0 *s3.GetBucketAclOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketAclInput) (*s3.GetBucketAclOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketAclInput) *s3.GetBucketAclOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketAclOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketAclInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketAclRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketAclRequest(_a0 *s3.GetBucketAclInput) (*request.Request, *s3.GetBucketAclOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketAclRequest") } var r0 *request.Request var r1 *s3.GetBucketAclOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketAclInput) (*request.Request, *s3.GetBucketAclOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketAclInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketAclInput) *s3.GetBucketAclOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketAclOutput) } } return r0, r1 } // GetBucketAclWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketAclWithContext(_a0 context.Context, _a1 *s3.GetBucketAclInput, _a2 ...request.Option) (*s3.GetBucketAclOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketAclWithContext") } var r0 *s3.GetBucketAclOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketAclInput, ...request.Option) (*s3.GetBucketAclOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketAclInput, ...request.Option) *s3.GetBucketAclOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketAclOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketAclInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketAnalyticsConfiguration provides a mock function with given fields: _a0 func (_m *S3API) GetBucketAnalyticsConfiguration(_a0 *s3.GetBucketAnalyticsConfigurationInput) (*s3.GetBucketAnalyticsConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketAnalyticsConfiguration") } var r0 *s3.GetBucketAnalyticsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketAnalyticsConfigurationInput) (*s3.GetBucketAnalyticsConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketAnalyticsConfigurationInput) *s3.GetBucketAnalyticsConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketAnalyticsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketAnalyticsConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketAnalyticsConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketAnalyticsConfigurationRequest(_a0 *s3.GetBucketAnalyticsConfigurationInput) (*request.Request, *s3.GetBucketAnalyticsConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketAnalyticsConfigurationRequest") } var r0 *request.Request var r1 *s3.GetBucketAnalyticsConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketAnalyticsConfigurationInput) (*request.Request, *s3.GetBucketAnalyticsConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketAnalyticsConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketAnalyticsConfigurationInput) *s3.GetBucketAnalyticsConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketAnalyticsConfigurationOutput) } } return r0, r1 } // GetBucketAnalyticsConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketAnalyticsConfigurationWithContext(_a0 context.Context, _a1 *s3.GetBucketAnalyticsConfigurationInput, _a2 ...request.Option) (*s3.GetBucketAnalyticsConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketAnalyticsConfigurationWithContext") } var r0 *s3.GetBucketAnalyticsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketAnalyticsConfigurationInput, ...request.Option) (*s3.GetBucketAnalyticsConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketAnalyticsConfigurationInput, ...request.Option) *s3.GetBucketAnalyticsConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketAnalyticsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketAnalyticsConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketCors provides a mock function with given fields: _a0 func (_m *S3API) GetBucketCors(_a0 *s3.GetBucketCorsInput) (*s3.GetBucketCorsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketCors") } var r0 *s3.GetBucketCorsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketCorsInput) (*s3.GetBucketCorsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketCorsInput) *s3.GetBucketCorsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketCorsOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketCorsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketCorsRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketCorsRequest(_a0 *s3.GetBucketCorsInput) (*request.Request, *s3.GetBucketCorsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketCorsRequest") } var r0 *request.Request var r1 *s3.GetBucketCorsOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketCorsInput) (*request.Request, *s3.GetBucketCorsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketCorsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketCorsInput) *s3.GetBucketCorsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketCorsOutput) } } return r0, r1 } // GetBucketCorsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketCorsWithContext(_a0 context.Context, _a1 *s3.GetBucketCorsInput, _a2 ...request.Option) (*s3.GetBucketCorsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketCorsWithContext") } var r0 *s3.GetBucketCorsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketCorsInput, ...request.Option) (*s3.GetBucketCorsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketCorsInput, ...request.Option) *s3.GetBucketCorsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketCorsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketCorsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketEncryption provides a mock function with given fields: _a0 func (_m *S3API) GetBucketEncryption(_a0 *s3.GetBucketEncryptionInput) (*s3.GetBucketEncryptionOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketEncryption") } var r0 *s3.GetBucketEncryptionOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketEncryptionInput) (*s3.GetBucketEncryptionOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketEncryptionInput) *s3.GetBucketEncryptionOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketEncryptionOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketEncryptionInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketEncryptionRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketEncryptionRequest(_a0 *s3.GetBucketEncryptionInput) (*request.Request, *s3.GetBucketEncryptionOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketEncryptionRequest") } var r0 *request.Request var r1 *s3.GetBucketEncryptionOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketEncryptionInput) (*request.Request, *s3.GetBucketEncryptionOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketEncryptionInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketEncryptionInput) *s3.GetBucketEncryptionOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketEncryptionOutput) } } return r0, r1 } // GetBucketEncryptionWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketEncryptionWithContext(_a0 context.Context, _a1 *s3.GetBucketEncryptionInput, _a2 ...request.Option) (*s3.GetBucketEncryptionOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketEncryptionWithContext") } var r0 *s3.GetBucketEncryptionOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketEncryptionInput, ...request.Option) (*s3.GetBucketEncryptionOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketEncryptionInput, ...request.Option) *s3.GetBucketEncryptionOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketEncryptionOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketEncryptionInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketIntelligentTieringConfiguration provides a mock function with given fields: _a0 func (_m *S3API) GetBucketIntelligentTieringConfiguration(_a0 *s3.GetBucketIntelligentTieringConfigurationInput) (*s3.GetBucketIntelligentTieringConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketIntelligentTieringConfiguration") } var r0 *s3.GetBucketIntelligentTieringConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketIntelligentTieringConfigurationInput) (*s3.GetBucketIntelligentTieringConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketIntelligentTieringConfigurationInput) *s3.GetBucketIntelligentTieringConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketIntelligentTieringConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketIntelligentTieringConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketIntelligentTieringConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketIntelligentTieringConfigurationRequest(_a0 *s3.GetBucketIntelligentTieringConfigurationInput) (*request.Request, *s3.GetBucketIntelligentTieringConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketIntelligentTieringConfigurationRequest") } var r0 *request.Request var r1 *s3.GetBucketIntelligentTieringConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketIntelligentTieringConfigurationInput) (*request.Request, *s3.GetBucketIntelligentTieringConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketIntelligentTieringConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketIntelligentTieringConfigurationInput) *s3.GetBucketIntelligentTieringConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketIntelligentTieringConfigurationOutput) } } return r0, r1 } // GetBucketIntelligentTieringConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketIntelligentTieringConfigurationWithContext(_a0 context.Context, _a1 *s3.GetBucketIntelligentTieringConfigurationInput, _a2 ...request.Option) (*s3.GetBucketIntelligentTieringConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketIntelligentTieringConfigurationWithContext") } var r0 *s3.GetBucketIntelligentTieringConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketIntelligentTieringConfigurationInput, ...request.Option) (*s3.GetBucketIntelligentTieringConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketIntelligentTieringConfigurationInput, ...request.Option) *s3.GetBucketIntelligentTieringConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketIntelligentTieringConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketIntelligentTieringConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketInventoryConfiguration provides a mock function with given fields: _a0 func (_m *S3API) GetBucketInventoryConfiguration(_a0 *s3.GetBucketInventoryConfigurationInput) (*s3.GetBucketInventoryConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketInventoryConfiguration") } var r0 *s3.GetBucketInventoryConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketInventoryConfigurationInput) (*s3.GetBucketInventoryConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketInventoryConfigurationInput) *s3.GetBucketInventoryConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketInventoryConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketInventoryConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketInventoryConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketInventoryConfigurationRequest(_a0 *s3.GetBucketInventoryConfigurationInput) (*request.Request, *s3.GetBucketInventoryConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketInventoryConfigurationRequest") } var r0 *request.Request var r1 *s3.GetBucketInventoryConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketInventoryConfigurationInput) (*request.Request, *s3.GetBucketInventoryConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketInventoryConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketInventoryConfigurationInput) *s3.GetBucketInventoryConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketInventoryConfigurationOutput) } } return r0, r1 } // GetBucketInventoryConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketInventoryConfigurationWithContext(_a0 context.Context, _a1 *s3.GetBucketInventoryConfigurationInput, _a2 ...request.Option) (*s3.GetBucketInventoryConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketInventoryConfigurationWithContext") } var r0 *s3.GetBucketInventoryConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketInventoryConfigurationInput, ...request.Option) (*s3.GetBucketInventoryConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketInventoryConfigurationInput, ...request.Option) *s3.GetBucketInventoryConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketInventoryConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketInventoryConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketLifecycle provides a mock function with given fields: _a0 func (_m *S3API) GetBucketLifecycle(_a0 *s3.GetBucketLifecycleInput) (*s3.GetBucketLifecycleOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketLifecycle") } var r0 *s3.GetBucketLifecycleOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketLifecycleInput) (*s3.GetBucketLifecycleOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketLifecycleInput) *s3.GetBucketLifecycleOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketLifecycleOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketLifecycleInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketLifecycleConfiguration provides a mock function with given fields: _a0 func (_m *S3API) GetBucketLifecycleConfiguration(_a0 *s3.GetBucketLifecycleConfigurationInput) (*s3.GetBucketLifecycleConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketLifecycleConfiguration") } var r0 *s3.GetBucketLifecycleConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketLifecycleConfigurationInput) (*s3.GetBucketLifecycleConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketLifecycleConfigurationInput) *s3.GetBucketLifecycleConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketLifecycleConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketLifecycleConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketLifecycleConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketLifecycleConfigurationRequest(_a0 *s3.GetBucketLifecycleConfigurationInput) (*request.Request, *s3.GetBucketLifecycleConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketLifecycleConfigurationRequest") } var r0 *request.Request var r1 *s3.GetBucketLifecycleConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketLifecycleConfigurationInput) (*request.Request, *s3.GetBucketLifecycleConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketLifecycleConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketLifecycleConfigurationInput) *s3.GetBucketLifecycleConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketLifecycleConfigurationOutput) } } return r0, r1 } // GetBucketLifecycleConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketLifecycleConfigurationWithContext(_a0 context.Context, _a1 *s3.GetBucketLifecycleConfigurationInput, _a2 ...request.Option) (*s3.GetBucketLifecycleConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketLifecycleConfigurationWithContext") } var r0 *s3.GetBucketLifecycleConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketLifecycleConfigurationInput, ...request.Option) (*s3.GetBucketLifecycleConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketLifecycleConfigurationInput, ...request.Option) *s3.GetBucketLifecycleConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketLifecycleConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketLifecycleConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketLifecycleRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketLifecycleRequest(_a0 *s3.GetBucketLifecycleInput) (*request.Request, *s3.GetBucketLifecycleOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketLifecycleRequest") } var r0 *request.Request var r1 *s3.GetBucketLifecycleOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketLifecycleInput) (*request.Request, *s3.GetBucketLifecycleOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketLifecycleInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketLifecycleInput) *s3.GetBucketLifecycleOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketLifecycleOutput) } } return r0, r1 } // GetBucketLifecycleWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketLifecycleWithContext(_a0 context.Context, _a1 *s3.GetBucketLifecycleInput, _a2 ...request.Option) (*s3.GetBucketLifecycleOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketLifecycleWithContext") } var r0 *s3.GetBucketLifecycleOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketLifecycleInput, ...request.Option) (*s3.GetBucketLifecycleOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketLifecycleInput, ...request.Option) *s3.GetBucketLifecycleOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketLifecycleOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketLifecycleInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketLocation provides a mock function with given fields: _a0 func (_m *S3API) GetBucketLocation(_a0 *s3.GetBucketLocationInput) (*s3.GetBucketLocationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketLocation") } var r0 *s3.GetBucketLocationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketLocationInput) (*s3.GetBucketLocationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketLocationInput) *s3.GetBucketLocationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketLocationOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketLocationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketLocationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketLocationRequest(_a0 *s3.GetBucketLocationInput) (*request.Request, *s3.GetBucketLocationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketLocationRequest") } var r0 *request.Request var r1 *s3.GetBucketLocationOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketLocationInput) (*request.Request, *s3.GetBucketLocationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketLocationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketLocationInput) *s3.GetBucketLocationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketLocationOutput) } } return r0, r1 } // GetBucketLocationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketLocationWithContext(_a0 context.Context, _a1 *s3.GetBucketLocationInput, _a2 ...request.Option) (*s3.GetBucketLocationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketLocationWithContext") } var r0 *s3.GetBucketLocationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketLocationInput, ...request.Option) (*s3.GetBucketLocationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketLocationInput, ...request.Option) *s3.GetBucketLocationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketLocationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketLocationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketLogging provides a mock function with given fields: _a0 func (_m *S3API) GetBucketLogging(_a0 *s3.GetBucketLoggingInput) (*s3.GetBucketLoggingOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketLogging") } var r0 *s3.GetBucketLoggingOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketLoggingInput) (*s3.GetBucketLoggingOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketLoggingInput) *s3.GetBucketLoggingOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketLoggingOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketLoggingInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketLoggingRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketLoggingRequest(_a0 *s3.GetBucketLoggingInput) (*request.Request, *s3.GetBucketLoggingOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketLoggingRequest") } var r0 *request.Request var r1 *s3.GetBucketLoggingOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketLoggingInput) (*request.Request, *s3.GetBucketLoggingOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketLoggingInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketLoggingInput) *s3.GetBucketLoggingOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketLoggingOutput) } } return r0, r1 } // GetBucketLoggingWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketLoggingWithContext(_a0 context.Context, _a1 *s3.GetBucketLoggingInput, _a2 ...request.Option) (*s3.GetBucketLoggingOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketLoggingWithContext") } var r0 *s3.GetBucketLoggingOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketLoggingInput, ...request.Option) (*s3.GetBucketLoggingOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketLoggingInput, ...request.Option) *s3.GetBucketLoggingOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketLoggingOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketLoggingInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketMetricsConfiguration provides a mock function with given fields: _a0 func (_m *S3API) GetBucketMetricsConfiguration(_a0 *s3.GetBucketMetricsConfigurationInput) (*s3.GetBucketMetricsConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketMetricsConfiguration") } var r0 *s3.GetBucketMetricsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketMetricsConfigurationInput) (*s3.GetBucketMetricsConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketMetricsConfigurationInput) *s3.GetBucketMetricsConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketMetricsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketMetricsConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketMetricsConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketMetricsConfigurationRequest(_a0 *s3.GetBucketMetricsConfigurationInput) (*request.Request, *s3.GetBucketMetricsConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketMetricsConfigurationRequest") } var r0 *request.Request var r1 *s3.GetBucketMetricsConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketMetricsConfigurationInput) (*request.Request, *s3.GetBucketMetricsConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketMetricsConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketMetricsConfigurationInput) *s3.GetBucketMetricsConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketMetricsConfigurationOutput) } } return r0, r1 } // GetBucketMetricsConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketMetricsConfigurationWithContext(_a0 context.Context, _a1 *s3.GetBucketMetricsConfigurationInput, _a2 ...request.Option) (*s3.GetBucketMetricsConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketMetricsConfigurationWithContext") } var r0 *s3.GetBucketMetricsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketMetricsConfigurationInput, ...request.Option) (*s3.GetBucketMetricsConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketMetricsConfigurationInput, ...request.Option) *s3.GetBucketMetricsConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketMetricsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketMetricsConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketNotification provides a mock function with given fields: _a0 func (_m *S3API) GetBucketNotification(_a0 *s3.GetBucketNotificationConfigurationRequest) (*s3.NotificationConfigurationDeprecated, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketNotification") } var r0 *s3.NotificationConfigurationDeprecated var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketNotificationConfigurationRequest) (*s3.NotificationConfigurationDeprecated, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketNotificationConfigurationRequest) *s3.NotificationConfigurationDeprecated); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.NotificationConfigurationDeprecated) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketNotificationConfigurationRequest) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketNotificationConfiguration provides a mock function with given fields: _a0 func (_m *S3API) GetBucketNotificationConfiguration(_a0 *s3.GetBucketNotificationConfigurationRequest) (*s3.NotificationConfiguration, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketNotificationConfiguration") } var r0 *s3.NotificationConfiguration var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketNotificationConfigurationRequest) (*s3.NotificationConfiguration, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketNotificationConfigurationRequest) *s3.NotificationConfiguration); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.NotificationConfiguration) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketNotificationConfigurationRequest) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketNotificationConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketNotificationConfigurationRequest(_a0 *s3.GetBucketNotificationConfigurationRequest) (*request.Request, *s3.NotificationConfiguration) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketNotificationConfigurationRequest") } var r0 *request.Request var r1 *s3.NotificationConfiguration if rf, ok := ret.Get(0).(func(*s3.GetBucketNotificationConfigurationRequest) (*request.Request, *s3.NotificationConfiguration)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketNotificationConfigurationRequest) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketNotificationConfigurationRequest) *s3.NotificationConfiguration); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.NotificationConfiguration) } } return r0, r1 } // GetBucketNotificationConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketNotificationConfigurationWithContext(_a0 context.Context, _a1 *s3.GetBucketNotificationConfigurationRequest, _a2 ...request.Option) (*s3.NotificationConfiguration, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketNotificationConfigurationWithContext") } var r0 *s3.NotificationConfiguration var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketNotificationConfigurationRequest, ...request.Option) (*s3.NotificationConfiguration, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketNotificationConfigurationRequest, ...request.Option) *s3.NotificationConfiguration); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.NotificationConfiguration) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketNotificationConfigurationRequest, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketNotificationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketNotificationRequest(_a0 *s3.GetBucketNotificationConfigurationRequest) (*request.Request, *s3.NotificationConfigurationDeprecated) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketNotificationRequest") } var r0 *request.Request var r1 *s3.NotificationConfigurationDeprecated if rf, ok := ret.Get(0).(func(*s3.GetBucketNotificationConfigurationRequest) (*request.Request, *s3.NotificationConfigurationDeprecated)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketNotificationConfigurationRequest) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketNotificationConfigurationRequest) *s3.NotificationConfigurationDeprecated); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.NotificationConfigurationDeprecated) } } return r0, r1 } // GetBucketNotificationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketNotificationWithContext(_a0 context.Context, _a1 *s3.GetBucketNotificationConfigurationRequest, _a2 ...request.Option) (*s3.NotificationConfigurationDeprecated, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketNotificationWithContext") } var r0 *s3.NotificationConfigurationDeprecated var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketNotificationConfigurationRequest, ...request.Option) (*s3.NotificationConfigurationDeprecated, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketNotificationConfigurationRequest, ...request.Option) *s3.NotificationConfigurationDeprecated); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.NotificationConfigurationDeprecated) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketNotificationConfigurationRequest, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketOwnershipControls provides a mock function with given fields: _a0 func (_m *S3API) GetBucketOwnershipControls(_a0 *s3.GetBucketOwnershipControlsInput) (*s3.GetBucketOwnershipControlsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketOwnershipControls") } var r0 *s3.GetBucketOwnershipControlsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketOwnershipControlsInput) (*s3.GetBucketOwnershipControlsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketOwnershipControlsInput) *s3.GetBucketOwnershipControlsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketOwnershipControlsOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketOwnershipControlsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketOwnershipControlsRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketOwnershipControlsRequest(_a0 *s3.GetBucketOwnershipControlsInput) (*request.Request, *s3.GetBucketOwnershipControlsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketOwnershipControlsRequest") } var r0 *request.Request var r1 *s3.GetBucketOwnershipControlsOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketOwnershipControlsInput) (*request.Request, *s3.GetBucketOwnershipControlsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketOwnershipControlsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketOwnershipControlsInput) *s3.GetBucketOwnershipControlsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketOwnershipControlsOutput) } } return r0, r1 } // GetBucketOwnershipControlsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketOwnershipControlsWithContext(_a0 context.Context, _a1 *s3.GetBucketOwnershipControlsInput, _a2 ...request.Option) (*s3.GetBucketOwnershipControlsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketOwnershipControlsWithContext") } var r0 *s3.GetBucketOwnershipControlsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketOwnershipControlsInput, ...request.Option) (*s3.GetBucketOwnershipControlsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketOwnershipControlsInput, ...request.Option) *s3.GetBucketOwnershipControlsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketOwnershipControlsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketOwnershipControlsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketPolicy provides a mock function with given fields: _a0 func (_m *S3API) GetBucketPolicy(_a0 *s3.GetBucketPolicyInput) (*s3.GetBucketPolicyOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketPolicy") } var r0 *s3.GetBucketPolicyOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketPolicyInput) (*s3.GetBucketPolicyOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketPolicyInput) *s3.GetBucketPolicyOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketPolicyOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketPolicyInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketPolicyRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketPolicyRequest(_a0 *s3.GetBucketPolicyInput) (*request.Request, *s3.GetBucketPolicyOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketPolicyRequest") } var r0 *request.Request var r1 *s3.GetBucketPolicyOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketPolicyInput) (*request.Request, *s3.GetBucketPolicyOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketPolicyInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketPolicyInput) *s3.GetBucketPolicyOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketPolicyOutput) } } return r0, r1 } // GetBucketPolicyStatus provides a mock function with given fields: _a0 func (_m *S3API) GetBucketPolicyStatus(_a0 *s3.GetBucketPolicyStatusInput) (*s3.GetBucketPolicyStatusOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketPolicyStatus") } var r0 *s3.GetBucketPolicyStatusOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketPolicyStatusInput) (*s3.GetBucketPolicyStatusOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketPolicyStatusInput) *s3.GetBucketPolicyStatusOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketPolicyStatusOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketPolicyStatusInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketPolicyStatusRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketPolicyStatusRequest(_a0 *s3.GetBucketPolicyStatusInput) (*request.Request, *s3.GetBucketPolicyStatusOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketPolicyStatusRequest") } var r0 *request.Request var r1 *s3.GetBucketPolicyStatusOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketPolicyStatusInput) (*request.Request, *s3.GetBucketPolicyStatusOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketPolicyStatusInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketPolicyStatusInput) *s3.GetBucketPolicyStatusOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketPolicyStatusOutput) } } return r0, r1 } // GetBucketPolicyStatusWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketPolicyStatusWithContext(_a0 context.Context, _a1 *s3.GetBucketPolicyStatusInput, _a2 ...request.Option) (*s3.GetBucketPolicyStatusOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketPolicyStatusWithContext") } var r0 *s3.GetBucketPolicyStatusOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketPolicyStatusInput, ...request.Option) (*s3.GetBucketPolicyStatusOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketPolicyStatusInput, ...request.Option) *s3.GetBucketPolicyStatusOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketPolicyStatusOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketPolicyStatusInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketPolicyWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketPolicyWithContext(_a0 context.Context, _a1 *s3.GetBucketPolicyInput, _a2 ...request.Option) (*s3.GetBucketPolicyOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketPolicyWithContext") } var r0 *s3.GetBucketPolicyOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketPolicyInput, ...request.Option) (*s3.GetBucketPolicyOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketPolicyInput, ...request.Option) *s3.GetBucketPolicyOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketPolicyOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketPolicyInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketReplication provides a mock function with given fields: _a0 func (_m *S3API) GetBucketReplication(_a0 *s3.GetBucketReplicationInput) (*s3.GetBucketReplicationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketReplication") } var r0 *s3.GetBucketReplicationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketReplicationInput) (*s3.GetBucketReplicationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketReplicationInput) *s3.GetBucketReplicationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketReplicationOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketReplicationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketReplicationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketReplicationRequest(_a0 *s3.GetBucketReplicationInput) (*request.Request, *s3.GetBucketReplicationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketReplicationRequest") } var r0 *request.Request var r1 *s3.GetBucketReplicationOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketReplicationInput) (*request.Request, *s3.GetBucketReplicationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketReplicationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketReplicationInput) *s3.GetBucketReplicationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketReplicationOutput) } } return r0, r1 } // GetBucketReplicationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketReplicationWithContext(_a0 context.Context, _a1 *s3.GetBucketReplicationInput, _a2 ...request.Option) (*s3.GetBucketReplicationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketReplicationWithContext") } var r0 *s3.GetBucketReplicationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketReplicationInput, ...request.Option) (*s3.GetBucketReplicationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketReplicationInput, ...request.Option) *s3.GetBucketReplicationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketReplicationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketReplicationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketRequestPayment provides a mock function with given fields: _a0 func (_m *S3API) GetBucketRequestPayment(_a0 *s3.GetBucketRequestPaymentInput) (*s3.GetBucketRequestPaymentOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketRequestPayment") } var r0 *s3.GetBucketRequestPaymentOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketRequestPaymentInput) (*s3.GetBucketRequestPaymentOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketRequestPaymentInput) *s3.GetBucketRequestPaymentOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketRequestPaymentOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketRequestPaymentInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketRequestPaymentRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketRequestPaymentRequest(_a0 *s3.GetBucketRequestPaymentInput) (*request.Request, *s3.GetBucketRequestPaymentOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketRequestPaymentRequest") } var r0 *request.Request var r1 *s3.GetBucketRequestPaymentOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketRequestPaymentInput) (*request.Request, *s3.GetBucketRequestPaymentOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketRequestPaymentInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketRequestPaymentInput) *s3.GetBucketRequestPaymentOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketRequestPaymentOutput) } } return r0, r1 } // GetBucketRequestPaymentWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketRequestPaymentWithContext(_a0 context.Context, _a1 *s3.GetBucketRequestPaymentInput, _a2 ...request.Option) (*s3.GetBucketRequestPaymentOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketRequestPaymentWithContext") } var r0 *s3.GetBucketRequestPaymentOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketRequestPaymentInput, ...request.Option) (*s3.GetBucketRequestPaymentOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketRequestPaymentInput, ...request.Option) *s3.GetBucketRequestPaymentOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketRequestPaymentOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketRequestPaymentInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketTagging provides a mock function with given fields: _a0 func (_m *S3API) GetBucketTagging(_a0 *s3.GetBucketTaggingInput) (*s3.GetBucketTaggingOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketTagging") } var r0 *s3.GetBucketTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketTaggingInput) (*s3.GetBucketTaggingOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketTaggingInput) *s3.GetBucketTaggingOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketTaggingOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketTaggingInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketTaggingRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketTaggingRequest(_a0 *s3.GetBucketTaggingInput) (*request.Request, *s3.GetBucketTaggingOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketTaggingRequest") } var r0 *request.Request var r1 *s3.GetBucketTaggingOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketTaggingInput) (*request.Request, *s3.GetBucketTaggingOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketTaggingInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketTaggingInput) *s3.GetBucketTaggingOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketTaggingOutput) } } return r0, r1 } // GetBucketTaggingWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketTaggingWithContext(_a0 context.Context, _a1 *s3.GetBucketTaggingInput, _a2 ...request.Option) (*s3.GetBucketTaggingOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketTaggingWithContext") } var r0 *s3.GetBucketTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketTaggingInput, ...request.Option) (*s3.GetBucketTaggingOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketTaggingInput, ...request.Option) *s3.GetBucketTaggingOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketTaggingOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketTaggingInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketVersioning provides a mock function with given fields: _a0 func (_m *S3API) GetBucketVersioning(_a0 *s3.GetBucketVersioningInput) (*s3.GetBucketVersioningOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketVersioning") } var r0 *s3.GetBucketVersioningOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketVersioningInput) (*s3.GetBucketVersioningOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketVersioningInput) *s3.GetBucketVersioningOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketVersioningOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketVersioningInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketVersioningRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketVersioningRequest(_a0 *s3.GetBucketVersioningInput) (*request.Request, *s3.GetBucketVersioningOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketVersioningRequest") } var r0 *request.Request var r1 *s3.GetBucketVersioningOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketVersioningInput) (*request.Request, *s3.GetBucketVersioningOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketVersioningInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketVersioningInput) *s3.GetBucketVersioningOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketVersioningOutput) } } return r0, r1 } // GetBucketVersioningWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketVersioningWithContext(_a0 context.Context, _a1 *s3.GetBucketVersioningInput, _a2 ...request.Option) (*s3.GetBucketVersioningOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketVersioningWithContext") } var r0 *s3.GetBucketVersioningOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketVersioningInput, ...request.Option) (*s3.GetBucketVersioningOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketVersioningInput, ...request.Option) *s3.GetBucketVersioningOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketVersioningOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketVersioningInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketWebsite provides a mock function with given fields: _a0 func (_m *S3API) GetBucketWebsite(_a0 *s3.GetBucketWebsiteInput) (*s3.GetBucketWebsiteOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketWebsite") } var r0 *s3.GetBucketWebsiteOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetBucketWebsiteInput) (*s3.GetBucketWebsiteOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketWebsiteInput) *s3.GetBucketWebsiteOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketWebsiteOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketWebsiteInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetBucketWebsiteRequest provides a mock function with given fields: _a0 func (_m *S3API) GetBucketWebsiteRequest(_a0 *s3.GetBucketWebsiteInput) (*request.Request, *s3.GetBucketWebsiteOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetBucketWebsiteRequest") } var r0 *request.Request var r1 *s3.GetBucketWebsiteOutput if rf, ok := ret.Get(0).(func(*s3.GetBucketWebsiteInput) (*request.Request, *s3.GetBucketWebsiteOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetBucketWebsiteInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetBucketWebsiteInput) *s3.GetBucketWebsiteOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetBucketWebsiteOutput) } } return r0, r1 } // GetBucketWebsiteWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetBucketWebsiteWithContext(_a0 context.Context, _a1 *s3.GetBucketWebsiteInput, _a2 ...request.Option) (*s3.GetBucketWebsiteOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetBucketWebsiteWithContext") } var r0 *s3.GetBucketWebsiteOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketWebsiteInput, ...request.Option) (*s3.GetBucketWebsiteOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetBucketWebsiteInput, ...request.Option) *s3.GetBucketWebsiteOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetBucketWebsiteOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetBucketWebsiteInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetObject provides a mock function with given fields: _a0 func (_m *S3API) GetObject(_a0 *s3.GetObjectInput) (*s3.GetObjectOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObject") } var r0 *s3.GetObjectOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetObjectInput) (*s3.GetObjectOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectInput) *s3.GetObjectOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectAcl provides a mock function with given fields: _a0 func (_m *S3API) GetObjectAcl(_a0 *s3.GetObjectAclInput) (*s3.GetObjectAclOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectAcl") } var r0 *s3.GetObjectAclOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetObjectAclInput) (*s3.GetObjectAclOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectAclInput) *s3.GetObjectAclOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectAclOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectAclInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectAclRequest provides a mock function with given fields: _a0 func (_m *S3API) GetObjectAclRequest(_a0 *s3.GetObjectAclInput) (*request.Request, *s3.GetObjectAclOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectAclRequest") } var r0 *request.Request var r1 *s3.GetObjectAclOutput if rf, ok := ret.Get(0).(func(*s3.GetObjectAclInput) (*request.Request, *s3.GetObjectAclOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectAclInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectAclInput) *s3.GetObjectAclOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetObjectAclOutput) } } return r0, r1 } // GetObjectAclWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetObjectAclWithContext(_a0 context.Context, _a1 *s3.GetObjectAclInput, _a2 ...request.Option) (*s3.GetObjectAclOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetObjectAclWithContext") } var r0 *s3.GetObjectAclOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectAclInput, ...request.Option) (*s3.GetObjectAclOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectAclInput, ...request.Option) *s3.GetObjectAclOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectAclOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetObjectAclInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectAttributes provides a mock function with given fields: _a0 func (_m *S3API) GetObjectAttributes(_a0 *s3.GetObjectAttributesInput) (*s3.GetObjectAttributesOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectAttributes") } var r0 *s3.GetObjectAttributesOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetObjectAttributesInput) (*s3.GetObjectAttributesOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectAttributesInput) *s3.GetObjectAttributesOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectAttributesOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectAttributesInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectAttributesRequest provides a mock function with given fields: _a0 func (_m *S3API) GetObjectAttributesRequest(_a0 *s3.GetObjectAttributesInput) (*request.Request, *s3.GetObjectAttributesOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectAttributesRequest") } var r0 *request.Request var r1 *s3.GetObjectAttributesOutput if rf, ok := ret.Get(0).(func(*s3.GetObjectAttributesInput) (*request.Request, *s3.GetObjectAttributesOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectAttributesInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectAttributesInput) *s3.GetObjectAttributesOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetObjectAttributesOutput) } } return r0, r1 } // GetObjectAttributesWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetObjectAttributesWithContext(_a0 context.Context, _a1 *s3.GetObjectAttributesInput, _a2 ...request.Option) (*s3.GetObjectAttributesOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetObjectAttributesWithContext") } var r0 *s3.GetObjectAttributesOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectAttributesInput, ...request.Option) (*s3.GetObjectAttributesOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectAttributesInput, ...request.Option) *s3.GetObjectAttributesOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectAttributesOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetObjectAttributesInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectLegalHold provides a mock function with given fields: _a0 func (_m *S3API) GetObjectLegalHold(_a0 *s3.GetObjectLegalHoldInput) (*s3.GetObjectLegalHoldOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectLegalHold") } var r0 *s3.GetObjectLegalHoldOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetObjectLegalHoldInput) (*s3.GetObjectLegalHoldOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectLegalHoldInput) *s3.GetObjectLegalHoldOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectLegalHoldOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectLegalHoldInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectLegalHoldRequest provides a mock function with given fields: _a0 func (_m *S3API) GetObjectLegalHoldRequest(_a0 *s3.GetObjectLegalHoldInput) (*request.Request, *s3.GetObjectLegalHoldOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectLegalHoldRequest") } var r0 *request.Request var r1 *s3.GetObjectLegalHoldOutput if rf, ok := ret.Get(0).(func(*s3.GetObjectLegalHoldInput) (*request.Request, *s3.GetObjectLegalHoldOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectLegalHoldInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectLegalHoldInput) *s3.GetObjectLegalHoldOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetObjectLegalHoldOutput) } } return r0, r1 } // GetObjectLegalHoldWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetObjectLegalHoldWithContext(_a0 context.Context, _a1 *s3.GetObjectLegalHoldInput, _a2 ...request.Option) (*s3.GetObjectLegalHoldOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetObjectLegalHoldWithContext") } var r0 *s3.GetObjectLegalHoldOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectLegalHoldInput, ...request.Option) (*s3.GetObjectLegalHoldOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectLegalHoldInput, ...request.Option) *s3.GetObjectLegalHoldOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectLegalHoldOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetObjectLegalHoldInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectLockConfiguration provides a mock function with given fields: _a0 func (_m *S3API) GetObjectLockConfiguration(_a0 *s3.GetObjectLockConfigurationInput) (*s3.GetObjectLockConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectLockConfiguration") } var r0 *s3.GetObjectLockConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetObjectLockConfigurationInput) (*s3.GetObjectLockConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectLockConfigurationInput) *s3.GetObjectLockConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectLockConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectLockConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectLockConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) GetObjectLockConfigurationRequest(_a0 *s3.GetObjectLockConfigurationInput) (*request.Request, *s3.GetObjectLockConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectLockConfigurationRequest") } var r0 *request.Request var r1 *s3.GetObjectLockConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.GetObjectLockConfigurationInput) (*request.Request, *s3.GetObjectLockConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectLockConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectLockConfigurationInput) *s3.GetObjectLockConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetObjectLockConfigurationOutput) } } return r0, r1 } // GetObjectLockConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetObjectLockConfigurationWithContext(_a0 context.Context, _a1 *s3.GetObjectLockConfigurationInput, _a2 ...request.Option) (*s3.GetObjectLockConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetObjectLockConfigurationWithContext") } var r0 *s3.GetObjectLockConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectLockConfigurationInput, ...request.Option) (*s3.GetObjectLockConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectLockConfigurationInput, ...request.Option) *s3.GetObjectLockConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectLockConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetObjectLockConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectRequest provides a mock function with given fields: _a0 func (_m *S3API) GetObjectRequest(_a0 *s3.GetObjectInput) (*request.Request, *s3.GetObjectOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectRequest") } var r0 *request.Request var r1 *s3.GetObjectOutput if rf, ok := ret.Get(0).(func(*s3.GetObjectInput) (*request.Request, *s3.GetObjectOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectInput) *s3.GetObjectOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetObjectOutput) } } return r0, r1 } // GetObjectRetention provides a mock function with given fields: _a0 func (_m *S3API) GetObjectRetention(_a0 *s3.GetObjectRetentionInput) (*s3.GetObjectRetentionOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectRetention") } var r0 *s3.GetObjectRetentionOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetObjectRetentionInput) (*s3.GetObjectRetentionOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectRetentionInput) *s3.GetObjectRetentionOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectRetentionOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectRetentionInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectRetentionRequest provides a mock function with given fields: _a0 func (_m *S3API) GetObjectRetentionRequest(_a0 *s3.GetObjectRetentionInput) (*request.Request, *s3.GetObjectRetentionOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectRetentionRequest") } var r0 *request.Request var r1 *s3.GetObjectRetentionOutput if rf, ok := ret.Get(0).(func(*s3.GetObjectRetentionInput) (*request.Request, *s3.GetObjectRetentionOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectRetentionInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectRetentionInput) *s3.GetObjectRetentionOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetObjectRetentionOutput) } } return r0, r1 } // GetObjectRetentionWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetObjectRetentionWithContext(_a0 context.Context, _a1 *s3.GetObjectRetentionInput, _a2 ...request.Option) (*s3.GetObjectRetentionOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetObjectRetentionWithContext") } var r0 *s3.GetObjectRetentionOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectRetentionInput, ...request.Option) (*s3.GetObjectRetentionOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectRetentionInput, ...request.Option) *s3.GetObjectRetentionOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectRetentionOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetObjectRetentionInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectTagging provides a mock function with given fields: _a0 func (_m *S3API) GetObjectTagging(_a0 *s3.GetObjectTaggingInput) (*s3.GetObjectTaggingOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectTagging") } var r0 *s3.GetObjectTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetObjectTaggingInput) (*s3.GetObjectTaggingOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectTaggingInput) *s3.GetObjectTaggingOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectTaggingOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectTaggingInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectTaggingRequest provides a mock function with given fields: _a0 func (_m *S3API) GetObjectTaggingRequest(_a0 *s3.GetObjectTaggingInput) (*request.Request, *s3.GetObjectTaggingOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectTaggingRequest") } var r0 *request.Request var r1 *s3.GetObjectTaggingOutput if rf, ok := ret.Get(0).(func(*s3.GetObjectTaggingInput) (*request.Request, *s3.GetObjectTaggingOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectTaggingInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectTaggingInput) *s3.GetObjectTaggingOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetObjectTaggingOutput) } } return r0, r1 } // GetObjectTaggingWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetObjectTaggingWithContext(_a0 context.Context, _a1 *s3.GetObjectTaggingInput, _a2 ...request.Option) (*s3.GetObjectTaggingOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetObjectTaggingWithContext") } var r0 *s3.GetObjectTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectTaggingInput, ...request.Option) (*s3.GetObjectTaggingOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectTaggingInput, ...request.Option) *s3.GetObjectTaggingOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectTaggingOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetObjectTaggingInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectTorrent provides a mock function with given fields: _a0 func (_m *S3API) GetObjectTorrent(_a0 *s3.GetObjectTorrentInput) (*s3.GetObjectTorrentOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectTorrent") } var r0 *s3.GetObjectTorrentOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetObjectTorrentInput) (*s3.GetObjectTorrentOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectTorrentInput) *s3.GetObjectTorrentOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectTorrentOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectTorrentInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectTorrentRequest provides a mock function with given fields: _a0 func (_m *S3API) GetObjectTorrentRequest(_a0 *s3.GetObjectTorrentInput) (*request.Request, *s3.GetObjectTorrentOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetObjectTorrentRequest") } var r0 *request.Request var r1 *s3.GetObjectTorrentOutput if rf, ok := ret.Get(0).(func(*s3.GetObjectTorrentInput) (*request.Request, *s3.GetObjectTorrentOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetObjectTorrentInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetObjectTorrentInput) *s3.GetObjectTorrentOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetObjectTorrentOutput) } } return r0, r1 } // GetObjectTorrentWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetObjectTorrentWithContext(_a0 context.Context, _a1 *s3.GetObjectTorrentInput, _a2 ...request.Option) (*s3.GetObjectTorrentOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetObjectTorrentWithContext") } var r0 *s3.GetObjectTorrentOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectTorrentInput, ...request.Option) (*s3.GetObjectTorrentOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectTorrentInput, ...request.Option) *s3.GetObjectTorrentOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectTorrentOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetObjectTorrentInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetObjectWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetObjectWithContext(_a0 context.Context, _a1 *s3.GetObjectInput, _a2 ...request.Option) (*s3.GetObjectOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetObjectWithContext") } var r0 *s3.GetObjectOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectInput, ...request.Option) (*s3.GetObjectOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetObjectInput, ...request.Option) *s3.GetObjectOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetObjectOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetObjectInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // GetPublicAccessBlock provides a mock function with given fields: _a0 func (_m *S3API) GetPublicAccessBlock(_a0 *s3.GetPublicAccessBlockInput) (*s3.GetPublicAccessBlockOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetPublicAccessBlock") } var r0 *s3.GetPublicAccessBlockOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.GetPublicAccessBlockInput) (*s3.GetPublicAccessBlockOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetPublicAccessBlockInput) *s3.GetPublicAccessBlockOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetPublicAccessBlockOutput) } } if rf, ok := ret.Get(1).(func(*s3.GetPublicAccessBlockInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // GetPublicAccessBlockRequest provides a mock function with given fields: _a0 func (_m *S3API) GetPublicAccessBlockRequest(_a0 *s3.GetPublicAccessBlockInput) (*request.Request, *s3.GetPublicAccessBlockOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for GetPublicAccessBlockRequest") } var r0 *request.Request var r1 *s3.GetPublicAccessBlockOutput if rf, ok := ret.Get(0).(func(*s3.GetPublicAccessBlockInput) (*request.Request, *s3.GetPublicAccessBlockOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.GetPublicAccessBlockInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.GetPublicAccessBlockInput) *s3.GetPublicAccessBlockOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.GetPublicAccessBlockOutput) } } return r0, r1 } // GetPublicAccessBlockWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) GetPublicAccessBlockWithContext(_a0 context.Context, _a1 *s3.GetPublicAccessBlockInput, _a2 ...request.Option) (*s3.GetPublicAccessBlockOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetPublicAccessBlockWithContext") } var r0 *s3.GetPublicAccessBlockOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.GetPublicAccessBlockInput, ...request.Option) (*s3.GetPublicAccessBlockOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.GetPublicAccessBlockInput, ...request.Option) *s3.GetPublicAccessBlockOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.GetPublicAccessBlockOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.GetPublicAccessBlockInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // HeadBucket provides a mock function with given fields: _a0 func (_m *S3API) HeadBucket(_a0 *s3.HeadBucketInput) (*s3.HeadBucketOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for HeadBucket") } var r0 *s3.HeadBucketOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.HeadBucketInput) (*s3.HeadBucketOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.HeadBucketInput) *s3.HeadBucketOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.HeadBucketOutput) } } if rf, ok := ret.Get(1).(func(*s3.HeadBucketInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // HeadBucketRequest provides a mock function with given fields: _a0 func (_m *S3API) HeadBucketRequest(_a0 *s3.HeadBucketInput) (*request.Request, *s3.HeadBucketOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for HeadBucketRequest") } var r0 *request.Request var r1 *s3.HeadBucketOutput if rf, ok := ret.Get(0).(func(*s3.HeadBucketInput) (*request.Request, *s3.HeadBucketOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.HeadBucketInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.HeadBucketInput) *s3.HeadBucketOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.HeadBucketOutput) } } return r0, r1 } // HeadBucketWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) HeadBucketWithContext(_a0 context.Context, _a1 *s3.HeadBucketInput, _a2 ...request.Option) (*s3.HeadBucketOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for HeadBucketWithContext") } var r0 *s3.HeadBucketOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.HeadBucketInput, ...request.Option) (*s3.HeadBucketOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.HeadBucketInput, ...request.Option) *s3.HeadBucketOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.HeadBucketOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.HeadBucketInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // HeadObject provides a mock function with given fields: _a0 func (_m *S3API) HeadObject(_a0 *s3.HeadObjectInput) (*s3.HeadObjectOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for HeadObject") } var r0 *s3.HeadObjectOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.HeadObjectInput) (*s3.HeadObjectOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.HeadObjectInput) *s3.HeadObjectOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.HeadObjectOutput) } } if rf, ok := ret.Get(1).(func(*s3.HeadObjectInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // HeadObjectRequest provides a mock function with given fields: _a0 func (_m *S3API) HeadObjectRequest(_a0 *s3.HeadObjectInput) (*request.Request, *s3.HeadObjectOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for HeadObjectRequest") } var r0 *request.Request var r1 *s3.HeadObjectOutput if rf, ok := ret.Get(0).(func(*s3.HeadObjectInput) (*request.Request, *s3.HeadObjectOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.HeadObjectInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.HeadObjectInput) *s3.HeadObjectOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.HeadObjectOutput) } } return r0, r1 } // HeadObjectWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) HeadObjectWithContext(_a0 context.Context, _a1 *s3.HeadObjectInput, _a2 ...request.Option) (*s3.HeadObjectOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for HeadObjectWithContext") } var r0 *s3.HeadObjectOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.HeadObjectInput, ...request.Option) (*s3.HeadObjectOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.HeadObjectInput, ...request.Option) *s3.HeadObjectOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.HeadObjectOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.HeadObjectInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListBucketAnalyticsConfigurations provides a mock function with given fields: _a0 func (_m *S3API) ListBucketAnalyticsConfigurations(_a0 *s3.ListBucketAnalyticsConfigurationsInput) (*s3.ListBucketAnalyticsConfigurationsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListBucketAnalyticsConfigurations") } var r0 *s3.ListBucketAnalyticsConfigurationsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.ListBucketAnalyticsConfigurationsInput) (*s3.ListBucketAnalyticsConfigurationsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListBucketAnalyticsConfigurationsInput) *s3.ListBucketAnalyticsConfigurationsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListBucketAnalyticsConfigurationsOutput) } } if rf, ok := ret.Get(1).(func(*s3.ListBucketAnalyticsConfigurationsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListBucketAnalyticsConfigurationsRequest provides a mock function with given fields: _a0 func (_m *S3API) ListBucketAnalyticsConfigurationsRequest(_a0 *s3.ListBucketAnalyticsConfigurationsInput) (*request.Request, *s3.ListBucketAnalyticsConfigurationsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListBucketAnalyticsConfigurationsRequest") } var r0 *request.Request var r1 *s3.ListBucketAnalyticsConfigurationsOutput if rf, ok := ret.Get(0).(func(*s3.ListBucketAnalyticsConfigurationsInput) (*request.Request, *s3.ListBucketAnalyticsConfigurationsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListBucketAnalyticsConfigurationsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListBucketAnalyticsConfigurationsInput) *s3.ListBucketAnalyticsConfigurationsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListBucketAnalyticsConfigurationsOutput) } } return r0, r1 } // ListBucketAnalyticsConfigurationsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListBucketAnalyticsConfigurationsWithContext(_a0 context.Context, _a1 *s3.ListBucketAnalyticsConfigurationsInput, _a2 ...request.Option) (*s3.ListBucketAnalyticsConfigurationsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListBucketAnalyticsConfigurationsWithContext") } var r0 *s3.ListBucketAnalyticsConfigurationsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListBucketAnalyticsConfigurationsInput, ...request.Option) (*s3.ListBucketAnalyticsConfigurationsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListBucketAnalyticsConfigurationsInput, ...request.Option) *s3.ListBucketAnalyticsConfigurationsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListBucketAnalyticsConfigurationsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListBucketAnalyticsConfigurationsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListBucketIntelligentTieringConfigurations provides a mock function with given fields: _a0 func (_m *S3API) ListBucketIntelligentTieringConfigurations(_a0 *s3.ListBucketIntelligentTieringConfigurationsInput) (*s3.ListBucketIntelligentTieringConfigurationsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListBucketIntelligentTieringConfigurations") } var r0 *s3.ListBucketIntelligentTieringConfigurationsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.ListBucketIntelligentTieringConfigurationsInput) (*s3.ListBucketIntelligentTieringConfigurationsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListBucketIntelligentTieringConfigurationsInput) *s3.ListBucketIntelligentTieringConfigurationsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListBucketIntelligentTieringConfigurationsOutput) } } if rf, ok := ret.Get(1).(func(*s3.ListBucketIntelligentTieringConfigurationsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListBucketIntelligentTieringConfigurationsRequest provides a mock function with given fields: _a0 func (_m *S3API) ListBucketIntelligentTieringConfigurationsRequest(_a0 *s3.ListBucketIntelligentTieringConfigurationsInput) (*request.Request, *s3.ListBucketIntelligentTieringConfigurationsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListBucketIntelligentTieringConfigurationsRequest") } var r0 *request.Request var r1 *s3.ListBucketIntelligentTieringConfigurationsOutput if rf, ok := ret.Get(0).(func(*s3.ListBucketIntelligentTieringConfigurationsInput) (*request.Request, *s3.ListBucketIntelligentTieringConfigurationsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListBucketIntelligentTieringConfigurationsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListBucketIntelligentTieringConfigurationsInput) *s3.ListBucketIntelligentTieringConfigurationsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListBucketIntelligentTieringConfigurationsOutput) } } return r0, r1 } // ListBucketIntelligentTieringConfigurationsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListBucketIntelligentTieringConfigurationsWithContext(_a0 context.Context, _a1 *s3.ListBucketIntelligentTieringConfigurationsInput, _a2 ...request.Option) (*s3.ListBucketIntelligentTieringConfigurationsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListBucketIntelligentTieringConfigurationsWithContext") } var r0 *s3.ListBucketIntelligentTieringConfigurationsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListBucketIntelligentTieringConfigurationsInput, ...request.Option) (*s3.ListBucketIntelligentTieringConfigurationsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListBucketIntelligentTieringConfigurationsInput, ...request.Option) *s3.ListBucketIntelligentTieringConfigurationsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListBucketIntelligentTieringConfigurationsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListBucketIntelligentTieringConfigurationsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListBucketInventoryConfigurations provides a mock function with given fields: _a0 func (_m *S3API) ListBucketInventoryConfigurations(_a0 *s3.ListBucketInventoryConfigurationsInput) (*s3.ListBucketInventoryConfigurationsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListBucketInventoryConfigurations") } var r0 *s3.ListBucketInventoryConfigurationsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.ListBucketInventoryConfigurationsInput) (*s3.ListBucketInventoryConfigurationsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListBucketInventoryConfigurationsInput) *s3.ListBucketInventoryConfigurationsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListBucketInventoryConfigurationsOutput) } } if rf, ok := ret.Get(1).(func(*s3.ListBucketInventoryConfigurationsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListBucketInventoryConfigurationsRequest provides a mock function with given fields: _a0 func (_m *S3API) ListBucketInventoryConfigurationsRequest(_a0 *s3.ListBucketInventoryConfigurationsInput) (*request.Request, *s3.ListBucketInventoryConfigurationsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListBucketInventoryConfigurationsRequest") } var r0 *request.Request var r1 *s3.ListBucketInventoryConfigurationsOutput if rf, ok := ret.Get(0).(func(*s3.ListBucketInventoryConfigurationsInput) (*request.Request, *s3.ListBucketInventoryConfigurationsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListBucketInventoryConfigurationsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListBucketInventoryConfigurationsInput) *s3.ListBucketInventoryConfigurationsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListBucketInventoryConfigurationsOutput) } } return r0, r1 } // ListBucketInventoryConfigurationsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListBucketInventoryConfigurationsWithContext(_a0 context.Context, _a1 *s3.ListBucketInventoryConfigurationsInput, _a2 ...request.Option) (*s3.ListBucketInventoryConfigurationsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListBucketInventoryConfigurationsWithContext") } var r0 *s3.ListBucketInventoryConfigurationsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListBucketInventoryConfigurationsInput, ...request.Option) (*s3.ListBucketInventoryConfigurationsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListBucketInventoryConfigurationsInput, ...request.Option) *s3.ListBucketInventoryConfigurationsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListBucketInventoryConfigurationsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListBucketInventoryConfigurationsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListBucketMetricsConfigurations provides a mock function with given fields: _a0 func (_m *S3API) ListBucketMetricsConfigurations(_a0 *s3.ListBucketMetricsConfigurationsInput) (*s3.ListBucketMetricsConfigurationsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListBucketMetricsConfigurations") } var r0 *s3.ListBucketMetricsConfigurationsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.ListBucketMetricsConfigurationsInput) (*s3.ListBucketMetricsConfigurationsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListBucketMetricsConfigurationsInput) *s3.ListBucketMetricsConfigurationsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListBucketMetricsConfigurationsOutput) } } if rf, ok := ret.Get(1).(func(*s3.ListBucketMetricsConfigurationsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListBucketMetricsConfigurationsRequest provides a mock function with given fields: _a0 func (_m *S3API) ListBucketMetricsConfigurationsRequest(_a0 *s3.ListBucketMetricsConfigurationsInput) (*request.Request, *s3.ListBucketMetricsConfigurationsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListBucketMetricsConfigurationsRequest") } var r0 *request.Request var r1 *s3.ListBucketMetricsConfigurationsOutput if rf, ok := ret.Get(0).(func(*s3.ListBucketMetricsConfigurationsInput) (*request.Request, *s3.ListBucketMetricsConfigurationsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListBucketMetricsConfigurationsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListBucketMetricsConfigurationsInput) *s3.ListBucketMetricsConfigurationsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListBucketMetricsConfigurationsOutput) } } return r0, r1 } // ListBucketMetricsConfigurationsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListBucketMetricsConfigurationsWithContext(_a0 context.Context, _a1 *s3.ListBucketMetricsConfigurationsInput, _a2 ...request.Option) (*s3.ListBucketMetricsConfigurationsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListBucketMetricsConfigurationsWithContext") } var r0 *s3.ListBucketMetricsConfigurationsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListBucketMetricsConfigurationsInput, ...request.Option) (*s3.ListBucketMetricsConfigurationsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListBucketMetricsConfigurationsInput, ...request.Option) *s3.ListBucketMetricsConfigurationsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListBucketMetricsConfigurationsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListBucketMetricsConfigurationsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListBuckets provides a mock function with given fields: _a0 func (_m *S3API) ListBuckets(_a0 *s3.ListBucketsInput) (*s3.ListBucketsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListBuckets") } var r0 *s3.ListBucketsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.ListBucketsInput) (*s3.ListBucketsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListBucketsInput) *s3.ListBucketsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListBucketsOutput) } } if rf, ok := ret.Get(1).(func(*s3.ListBucketsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListBucketsRequest provides a mock function with given fields: _a0 func (_m *S3API) ListBucketsRequest(_a0 *s3.ListBucketsInput) (*request.Request, *s3.ListBucketsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListBucketsRequest") } var r0 *request.Request var r1 *s3.ListBucketsOutput if rf, ok := ret.Get(0).(func(*s3.ListBucketsInput) (*request.Request, *s3.ListBucketsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListBucketsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListBucketsInput) *s3.ListBucketsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListBucketsOutput) } } return r0, r1 } // ListBucketsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListBucketsWithContext(_a0 context.Context, _a1 *s3.ListBucketsInput, _a2 ...request.Option) (*s3.ListBucketsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListBucketsWithContext") } var r0 *s3.ListBucketsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListBucketsInput, ...request.Option) (*s3.ListBucketsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListBucketsInput, ...request.Option) *s3.ListBucketsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListBucketsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListBucketsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListDirectoryBuckets provides a mock function with given fields: _a0 func (_m *S3API) ListDirectoryBuckets(_a0 *s3.ListDirectoryBucketsInput) (*s3.ListDirectoryBucketsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListDirectoryBuckets") } var r0 *s3.ListDirectoryBucketsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.ListDirectoryBucketsInput) (*s3.ListDirectoryBucketsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListDirectoryBucketsInput) *s3.ListDirectoryBucketsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListDirectoryBucketsOutput) } } if rf, ok := ret.Get(1).(func(*s3.ListDirectoryBucketsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListDirectoryBucketsPages provides a mock function with given fields: _a0, _a1 func (_m *S3API) ListDirectoryBucketsPages(_a0 *s3.ListDirectoryBucketsInput, _a1 func(*s3.ListDirectoryBucketsOutput, bool) bool) error { ret := _m.Called(_a0, _a1) if len(ret) == 0 { panic("no return value specified for ListDirectoryBucketsPages") } var r0 error if rf, ok := ret.Get(0).(func(*s3.ListDirectoryBucketsInput, func(*s3.ListDirectoryBucketsOutput, bool) bool) error); ok { r0 = rf(_a0, _a1) } else { r0 = ret.Error(0) } return r0 } // ListDirectoryBucketsPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3 func (_m *S3API) ListDirectoryBucketsPagesWithContext(_a0 context.Context, _a1 *s3.ListDirectoryBucketsInput, _a2 func(*s3.ListDirectoryBucketsOutput, bool) bool, _a3 ...request.Option) error { _va := make([]interface{}, len(_a3)) for _i := range _a3 { _va[_i] = _a3[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1, _a2) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListDirectoryBucketsPagesWithContext") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListDirectoryBucketsInput, func(*s3.ListDirectoryBucketsOutput, bool) bool, ...request.Option) error); ok { r0 = rf(_a0, _a1, _a2, _a3...) } else { r0 = ret.Error(0) } return r0 } // ListDirectoryBucketsRequest provides a mock function with given fields: _a0 func (_m *S3API) ListDirectoryBucketsRequest(_a0 *s3.ListDirectoryBucketsInput) (*request.Request, *s3.ListDirectoryBucketsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListDirectoryBucketsRequest") } var r0 *request.Request var r1 *s3.ListDirectoryBucketsOutput if rf, ok := ret.Get(0).(func(*s3.ListDirectoryBucketsInput) (*request.Request, *s3.ListDirectoryBucketsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListDirectoryBucketsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListDirectoryBucketsInput) *s3.ListDirectoryBucketsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListDirectoryBucketsOutput) } } return r0, r1 } // ListDirectoryBucketsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListDirectoryBucketsWithContext(_a0 context.Context, _a1 *s3.ListDirectoryBucketsInput, _a2 ...request.Option) (*s3.ListDirectoryBucketsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListDirectoryBucketsWithContext") } var r0 *s3.ListDirectoryBucketsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListDirectoryBucketsInput, ...request.Option) (*s3.ListDirectoryBucketsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListDirectoryBucketsInput, ...request.Option) *s3.ListDirectoryBucketsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListDirectoryBucketsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListDirectoryBucketsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListMultipartUploads provides a mock function with given fields: _a0 func (_m *S3API) ListMultipartUploads(_a0 *s3.ListMultipartUploadsInput) (*s3.ListMultipartUploadsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListMultipartUploads") } var r0 *s3.ListMultipartUploadsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.ListMultipartUploadsInput) (*s3.ListMultipartUploadsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListMultipartUploadsInput) *s3.ListMultipartUploadsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListMultipartUploadsOutput) } } if rf, ok := ret.Get(1).(func(*s3.ListMultipartUploadsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListMultipartUploadsPages provides a mock function with given fields: _a0, _a1 func (_m *S3API) ListMultipartUploadsPages(_a0 *s3.ListMultipartUploadsInput, _a1 func(*s3.ListMultipartUploadsOutput, bool) bool) error { ret := _m.Called(_a0, _a1) if len(ret) == 0 { panic("no return value specified for ListMultipartUploadsPages") } var r0 error if rf, ok := ret.Get(0).(func(*s3.ListMultipartUploadsInput, func(*s3.ListMultipartUploadsOutput, bool) bool) error); ok { r0 = rf(_a0, _a1) } else { r0 = ret.Error(0) } return r0 } // ListMultipartUploadsPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3 func (_m *S3API) ListMultipartUploadsPagesWithContext(_a0 context.Context, _a1 *s3.ListMultipartUploadsInput, _a2 func(*s3.ListMultipartUploadsOutput, bool) bool, _a3 ...request.Option) error { _va := make([]interface{}, len(_a3)) for _i := range _a3 { _va[_i] = _a3[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1, _a2) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListMultipartUploadsPagesWithContext") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListMultipartUploadsInput, func(*s3.ListMultipartUploadsOutput, bool) bool, ...request.Option) error); ok { r0 = rf(_a0, _a1, _a2, _a3...) } else { r0 = ret.Error(0) } return r0 } // ListMultipartUploadsRequest provides a mock function with given fields: _a0 func (_m *S3API) ListMultipartUploadsRequest(_a0 *s3.ListMultipartUploadsInput) (*request.Request, *s3.ListMultipartUploadsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListMultipartUploadsRequest") } var r0 *request.Request var r1 *s3.ListMultipartUploadsOutput if rf, ok := ret.Get(0).(func(*s3.ListMultipartUploadsInput) (*request.Request, *s3.ListMultipartUploadsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListMultipartUploadsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListMultipartUploadsInput) *s3.ListMultipartUploadsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListMultipartUploadsOutput) } } return r0, r1 } // ListMultipartUploadsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListMultipartUploadsWithContext(_a0 context.Context, _a1 *s3.ListMultipartUploadsInput, _a2 ...request.Option) (*s3.ListMultipartUploadsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListMultipartUploadsWithContext") } var r0 *s3.ListMultipartUploadsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListMultipartUploadsInput, ...request.Option) (*s3.ListMultipartUploadsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListMultipartUploadsInput, ...request.Option) *s3.ListMultipartUploadsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListMultipartUploadsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListMultipartUploadsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListObjectVersions provides a mock function with given fields: _a0 func (_m *S3API) ListObjectVersions(_a0 *s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListObjectVersions") } var r0 *s3.ListObjectVersionsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListObjectVersionsInput) *s3.ListObjectVersionsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListObjectVersionsOutput) } } if rf, ok := ret.Get(1).(func(*s3.ListObjectVersionsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListObjectVersionsPages provides a mock function with given fields: _a0, _a1 func (_m *S3API) ListObjectVersionsPages(_a0 *s3.ListObjectVersionsInput, _a1 func(*s3.ListObjectVersionsOutput, bool) bool) error { ret := _m.Called(_a0, _a1) if len(ret) == 0 { panic("no return value specified for ListObjectVersionsPages") } var r0 error if rf, ok := ret.Get(0).(func(*s3.ListObjectVersionsInput, func(*s3.ListObjectVersionsOutput, bool) bool) error); ok { r0 = rf(_a0, _a1) } else { r0 = ret.Error(0) } return r0 } // ListObjectVersionsPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3 func (_m *S3API) ListObjectVersionsPagesWithContext(_a0 context.Context, _a1 *s3.ListObjectVersionsInput, _a2 func(*s3.ListObjectVersionsOutput, bool) bool, _a3 ...request.Option) error { _va := make([]interface{}, len(_a3)) for _i := range _a3 { _va[_i] = _a3[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1, _a2) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListObjectVersionsPagesWithContext") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListObjectVersionsInput, func(*s3.ListObjectVersionsOutput, bool) bool, ...request.Option) error); ok { r0 = rf(_a0, _a1, _a2, _a3...) } else { r0 = ret.Error(0) } return r0 } // ListObjectVersionsRequest provides a mock function with given fields: _a0 func (_m *S3API) ListObjectVersionsRequest(_a0 *s3.ListObjectVersionsInput) (*request.Request, *s3.ListObjectVersionsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListObjectVersionsRequest") } var r0 *request.Request var r1 *s3.ListObjectVersionsOutput if rf, ok := ret.Get(0).(func(*s3.ListObjectVersionsInput) (*request.Request, *s3.ListObjectVersionsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListObjectVersionsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListObjectVersionsInput) *s3.ListObjectVersionsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListObjectVersionsOutput) } } return r0, r1 } // ListObjectVersionsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListObjectVersionsWithContext(_a0 context.Context, _a1 *s3.ListObjectVersionsInput, _a2 ...request.Option) (*s3.ListObjectVersionsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListObjectVersionsWithContext") } var r0 *s3.ListObjectVersionsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListObjectVersionsInput, ...request.Option) (*s3.ListObjectVersionsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListObjectVersionsInput, ...request.Option) *s3.ListObjectVersionsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListObjectVersionsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListObjectVersionsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListObjects provides a mock function with given fields: _a0 func (_m *S3API) ListObjects(_a0 *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListObjects") } var r0 *s3.ListObjectsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.ListObjectsInput) (*s3.ListObjectsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListObjectsInput) *s3.ListObjectsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListObjectsOutput) } } if rf, ok := ret.Get(1).(func(*s3.ListObjectsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListObjectsPages provides a mock function with given fields: _a0, _a1 func (_m *S3API) ListObjectsPages(_a0 *s3.ListObjectsInput, _a1 func(*s3.ListObjectsOutput, bool) bool) error { ret := _m.Called(_a0, _a1) if len(ret) == 0 { panic("no return value specified for ListObjectsPages") } var r0 error if rf, ok := ret.Get(0).(func(*s3.ListObjectsInput, func(*s3.ListObjectsOutput, bool) bool) error); ok { r0 = rf(_a0, _a1) } else { r0 = ret.Error(0) } return r0 } // ListObjectsPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3 func (_m *S3API) ListObjectsPagesWithContext(_a0 context.Context, _a1 *s3.ListObjectsInput, _a2 func(*s3.ListObjectsOutput, bool) bool, _a3 ...request.Option) error { _va := make([]interface{}, len(_a3)) for _i := range _a3 { _va[_i] = _a3[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1, _a2) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListObjectsPagesWithContext") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListObjectsInput, func(*s3.ListObjectsOutput, bool) bool, ...request.Option) error); ok { r0 = rf(_a0, _a1, _a2, _a3...) } else { r0 = ret.Error(0) } return r0 } // ListObjectsRequest provides a mock function with given fields: _a0 func (_m *S3API) ListObjectsRequest(_a0 *s3.ListObjectsInput) (*request.Request, *s3.ListObjectsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListObjectsRequest") } var r0 *request.Request var r1 *s3.ListObjectsOutput if rf, ok := ret.Get(0).(func(*s3.ListObjectsInput) (*request.Request, *s3.ListObjectsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListObjectsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListObjectsInput) *s3.ListObjectsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListObjectsOutput) } } return r0, r1 } // ListObjectsV2 provides a mock function with given fields: _a0 func (_m *S3API) ListObjectsV2(_a0 *s3.ListObjectsV2Input) (*s3.ListObjectsV2Output, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListObjectsV2") } var r0 *s3.ListObjectsV2Output var r1 error if rf, ok := ret.Get(0).(func(*s3.ListObjectsV2Input) (*s3.ListObjectsV2Output, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListObjectsV2Input) *s3.ListObjectsV2Output); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListObjectsV2Output) } } if rf, ok := ret.Get(1).(func(*s3.ListObjectsV2Input) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListObjectsV2Pages provides a mock function with given fields: _a0, _a1 func (_m *S3API) ListObjectsV2Pages(_a0 *s3.ListObjectsV2Input, _a1 func(*s3.ListObjectsV2Output, bool) bool) error { ret := _m.Called(_a0, _a1) if len(ret) == 0 { panic("no return value specified for ListObjectsV2Pages") } var r0 error if rf, ok := ret.Get(0).(func(*s3.ListObjectsV2Input, func(*s3.ListObjectsV2Output, bool) bool) error); ok { r0 = rf(_a0, _a1) } else { r0 = ret.Error(0) } return r0 } // ListObjectsV2PagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3 func (_m *S3API) ListObjectsV2PagesWithContext(_a0 context.Context, _a1 *s3.ListObjectsV2Input, _a2 func(*s3.ListObjectsV2Output, bool) bool, _a3 ...request.Option) error { _va := make([]interface{}, len(_a3)) for _i := range _a3 { _va[_i] = _a3[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1, _a2) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListObjectsV2PagesWithContext") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListObjectsV2Input, func(*s3.ListObjectsV2Output, bool) bool, ...request.Option) error); ok { r0 = rf(_a0, _a1, _a2, _a3...) } else { r0 = ret.Error(0) } return r0 } // ListObjectsV2Request provides a mock function with given fields: _a0 func (_m *S3API) ListObjectsV2Request(_a0 *s3.ListObjectsV2Input) (*request.Request, *s3.ListObjectsV2Output) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListObjectsV2Request") } var r0 *request.Request var r1 *s3.ListObjectsV2Output if rf, ok := ret.Get(0).(func(*s3.ListObjectsV2Input) (*request.Request, *s3.ListObjectsV2Output)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListObjectsV2Input) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListObjectsV2Input) *s3.ListObjectsV2Output); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListObjectsV2Output) } } return r0, r1 } // ListObjectsV2WithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListObjectsV2WithContext(_a0 context.Context, _a1 *s3.ListObjectsV2Input, _a2 ...request.Option) (*s3.ListObjectsV2Output, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListObjectsV2WithContext") } var r0 *s3.ListObjectsV2Output var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListObjectsV2Input, ...request.Option) (*s3.ListObjectsV2Output, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListObjectsV2Input, ...request.Option) *s3.ListObjectsV2Output); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListObjectsV2Output) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListObjectsV2Input, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListObjectsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListObjectsWithContext(_a0 context.Context, _a1 *s3.ListObjectsInput, _a2 ...request.Option) (*s3.ListObjectsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListObjectsWithContext") } var r0 *s3.ListObjectsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListObjectsInput, ...request.Option) (*s3.ListObjectsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListObjectsInput, ...request.Option) *s3.ListObjectsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListObjectsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListObjectsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // ListParts provides a mock function with given fields: _a0 func (_m *S3API) ListParts(_a0 *s3.ListPartsInput) (*s3.ListPartsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListParts") } var r0 *s3.ListPartsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.ListPartsInput) (*s3.ListPartsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListPartsInput) *s3.ListPartsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListPartsOutput) } } if rf, ok := ret.Get(1).(func(*s3.ListPartsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // ListPartsPages provides a mock function with given fields: _a0, _a1 func (_m *S3API) ListPartsPages(_a0 *s3.ListPartsInput, _a1 func(*s3.ListPartsOutput, bool) bool) error { ret := _m.Called(_a0, _a1) if len(ret) == 0 { panic("no return value specified for ListPartsPages") } var r0 error if rf, ok := ret.Get(0).(func(*s3.ListPartsInput, func(*s3.ListPartsOutput, bool) bool) error); ok { r0 = rf(_a0, _a1) } else { r0 = ret.Error(0) } return r0 } // ListPartsPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3 func (_m *S3API) ListPartsPagesWithContext(_a0 context.Context, _a1 *s3.ListPartsInput, _a2 func(*s3.ListPartsOutput, bool) bool, _a3 ...request.Option) error { _va := make([]interface{}, len(_a3)) for _i := range _a3 { _va[_i] = _a3[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1, _a2) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListPartsPagesWithContext") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListPartsInput, func(*s3.ListPartsOutput, bool) bool, ...request.Option) error); ok { r0 = rf(_a0, _a1, _a2, _a3...) } else { r0 = ret.Error(0) } return r0 } // ListPartsRequest provides a mock function with given fields: _a0 func (_m *S3API) ListPartsRequest(_a0 *s3.ListPartsInput) (*request.Request, *s3.ListPartsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for ListPartsRequest") } var r0 *request.Request var r1 *s3.ListPartsOutput if rf, ok := ret.Get(0).(func(*s3.ListPartsInput) (*request.Request, *s3.ListPartsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.ListPartsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.ListPartsInput) *s3.ListPartsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.ListPartsOutput) } } return r0, r1 } // ListPartsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) ListPartsWithContext(_a0 context.Context, _a1 *s3.ListPartsInput, _a2 ...request.Option) (*s3.ListPartsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for ListPartsWithContext") } var r0 *s3.ListPartsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.ListPartsInput, ...request.Option) (*s3.ListPartsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.ListPartsInput, ...request.Option) *s3.ListPartsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.ListPartsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.ListPartsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketAccelerateConfiguration provides a mock function with given fields: _a0 func (_m *S3API) PutBucketAccelerateConfiguration(_a0 *s3.PutBucketAccelerateConfigurationInput) (*s3.PutBucketAccelerateConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketAccelerateConfiguration") } var r0 *s3.PutBucketAccelerateConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketAccelerateConfigurationInput) (*s3.PutBucketAccelerateConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketAccelerateConfigurationInput) *s3.PutBucketAccelerateConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketAccelerateConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketAccelerateConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketAccelerateConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketAccelerateConfigurationRequest(_a0 *s3.PutBucketAccelerateConfigurationInput) (*request.Request, *s3.PutBucketAccelerateConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketAccelerateConfigurationRequest") } var r0 *request.Request var r1 *s3.PutBucketAccelerateConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketAccelerateConfigurationInput) (*request.Request, *s3.PutBucketAccelerateConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketAccelerateConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketAccelerateConfigurationInput) *s3.PutBucketAccelerateConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketAccelerateConfigurationOutput) } } return r0, r1 } // PutBucketAccelerateConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketAccelerateConfigurationWithContext(_a0 context.Context, _a1 *s3.PutBucketAccelerateConfigurationInput, _a2 ...request.Option) (*s3.PutBucketAccelerateConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketAccelerateConfigurationWithContext") } var r0 *s3.PutBucketAccelerateConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketAccelerateConfigurationInput, ...request.Option) (*s3.PutBucketAccelerateConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketAccelerateConfigurationInput, ...request.Option) *s3.PutBucketAccelerateConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketAccelerateConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketAccelerateConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketAcl provides a mock function with given fields: _a0 func (_m *S3API) PutBucketAcl(_a0 *s3.PutBucketAclInput) (*s3.PutBucketAclOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketAcl") } var r0 *s3.PutBucketAclOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketAclInput) (*s3.PutBucketAclOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketAclInput) *s3.PutBucketAclOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketAclOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketAclInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketAclRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketAclRequest(_a0 *s3.PutBucketAclInput) (*request.Request, *s3.PutBucketAclOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketAclRequest") } var r0 *request.Request var r1 *s3.PutBucketAclOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketAclInput) (*request.Request, *s3.PutBucketAclOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketAclInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketAclInput) *s3.PutBucketAclOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketAclOutput) } } return r0, r1 } // PutBucketAclWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketAclWithContext(_a0 context.Context, _a1 *s3.PutBucketAclInput, _a2 ...request.Option) (*s3.PutBucketAclOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketAclWithContext") } var r0 *s3.PutBucketAclOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketAclInput, ...request.Option) (*s3.PutBucketAclOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketAclInput, ...request.Option) *s3.PutBucketAclOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketAclOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketAclInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketAnalyticsConfiguration provides a mock function with given fields: _a0 func (_m *S3API) PutBucketAnalyticsConfiguration(_a0 *s3.PutBucketAnalyticsConfigurationInput) (*s3.PutBucketAnalyticsConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketAnalyticsConfiguration") } var r0 *s3.PutBucketAnalyticsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketAnalyticsConfigurationInput) (*s3.PutBucketAnalyticsConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketAnalyticsConfigurationInput) *s3.PutBucketAnalyticsConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketAnalyticsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketAnalyticsConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketAnalyticsConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketAnalyticsConfigurationRequest(_a0 *s3.PutBucketAnalyticsConfigurationInput) (*request.Request, *s3.PutBucketAnalyticsConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketAnalyticsConfigurationRequest") } var r0 *request.Request var r1 *s3.PutBucketAnalyticsConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketAnalyticsConfigurationInput) (*request.Request, *s3.PutBucketAnalyticsConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketAnalyticsConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketAnalyticsConfigurationInput) *s3.PutBucketAnalyticsConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketAnalyticsConfigurationOutput) } } return r0, r1 } // PutBucketAnalyticsConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketAnalyticsConfigurationWithContext(_a0 context.Context, _a1 *s3.PutBucketAnalyticsConfigurationInput, _a2 ...request.Option) (*s3.PutBucketAnalyticsConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketAnalyticsConfigurationWithContext") } var r0 *s3.PutBucketAnalyticsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketAnalyticsConfigurationInput, ...request.Option) (*s3.PutBucketAnalyticsConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketAnalyticsConfigurationInput, ...request.Option) *s3.PutBucketAnalyticsConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketAnalyticsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketAnalyticsConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketCors provides a mock function with given fields: _a0 func (_m *S3API) PutBucketCors(_a0 *s3.PutBucketCorsInput) (*s3.PutBucketCorsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketCors") } var r0 *s3.PutBucketCorsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketCorsInput) (*s3.PutBucketCorsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketCorsInput) *s3.PutBucketCorsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketCorsOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketCorsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketCorsRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketCorsRequest(_a0 *s3.PutBucketCorsInput) (*request.Request, *s3.PutBucketCorsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketCorsRequest") } var r0 *request.Request var r1 *s3.PutBucketCorsOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketCorsInput) (*request.Request, *s3.PutBucketCorsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketCorsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketCorsInput) *s3.PutBucketCorsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketCorsOutput) } } return r0, r1 } // PutBucketCorsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketCorsWithContext(_a0 context.Context, _a1 *s3.PutBucketCorsInput, _a2 ...request.Option) (*s3.PutBucketCorsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketCorsWithContext") } var r0 *s3.PutBucketCorsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketCorsInput, ...request.Option) (*s3.PutBucketCorsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketCorsInput, ...request.Option) *s3.PutBucketCorsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketCorsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketCorsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketEncryption provides a mock function with given fields: _a0 func (_m *S3API) PutBucketEncryption(_a0 *s3.PutBucketEncryptionInput) (*s3.PutBucketEncryptionOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketEncryption") } var r0 *s3.PutBucketEncryptionOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketEncryptionInput) (*s3.PutBucketEncryptionOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketEncryptionInput) *s3.PutBucketEncryptionOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketEncryptionOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketEncryptionInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketEncryptionRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketEncryptionRequest(_a0 *s3.PutBucketEncryptionInput) (*request.Request, *s3.PutBucketEncryptionOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketEncryptionRequest") } var r0 *request.Request var r1 *s3.PutBucketEncryptionOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketEncryptionInput) (*request.Request, *s3.PutBucketEncryptionOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketEncryptionInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketEncryptionInput) *s3.PutBucketEncryptionOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketEncryptionOutput) } } return r0, r1 } // PutBucketEncryptionWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketEncryptionWithContext(_a0 context.Context, _a1 *s3.PutBucketEncryptionInput, _a2 ...request.Option) (*s3.PutBucketEncryptionOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketEncryptionWithContext") } var r0 *s3.PutBucketEncryptionOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketEncryptionInput, ...request.Option) (*s3.PutBucketEncryptionOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketEncryptionInput, ...request.Option) *s3.PutBucketEncryptionOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketEncryptionOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketEncryptionInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketIntelligentTieringConfiguration provides a mock function with given fields: _a0 func (_m *S3API) PutBucketIntelligentTieringConfiguration(_a0 *s3.PutBucketIntelligentTieringConfigurationInput) (*s3.PutBucketIntelligentTieringConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketIntelligentTieringConfiguration") } var r0 *s3.PutBucketIntelligentTieringConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketIntelligentTieringConfigurationInput) (*s3.PutBucketIntelligentTieringConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketIntelligentTieringConfigurationInput) *s3.PutBucketIntelligentTieringConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketIntelligentTieringConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketIntelligentTieringConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketIntelligentTieringConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketIntelligentTieringConfigurationRequest(_a0 *s3.PutBucketIntelligentTieringConfigurationInput) (*request.Request, *s3.PutBucketIntelligentTieringConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketIntelligentTieringConfigurationRequest") } var r0 *request.Request var r1 *s3.PutBucketIntelligentTieringConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketIntelligentTieringConfigurationInput) (*request.Request, *s3.PutBucketIntelligentTieringConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketIntelligentTieringConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketIntelligentTieringConfigurationInput) *s3.PutBucketIntelligentTieringConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketIntelligentTieringConfigurationOutput) } } return r0, r1 } // PutBucketIntelligentTieringConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketIntelligentTieringConfigurationWithContext(_a0 context.Context, _a1 *s3.PutBucketIntelligentTieringConfigurationInput, _a2 ...request.Option) (*s3.PutBucketIntelligentTieringConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketIntelligentTieringConfigurationWithContext") } var r0 *s3.PutBucketIntelligentTieringConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketIntelligentTieringConfigurationInput, ...request.Option) (*s3.PutBucketIntelligentTieringConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketIntelligentTieringConfigurationInput, ...request.Option) *s3.PutBucketIntelligentTieringConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketIntelligentTieringConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketIntelligentTieringConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketInventoryConfiguration provides a mock function with given fields: _a0 func (_m *S3API) PutBucketInventoryConfiguration(_a0 *s3.PutBucketInventoryConfigurationInput) (*s3.PutBucketInventoryConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketInventoryConfiguration") } var r0 *s3.PutBucketInventoryConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketInventoryConfigurationInput) (*s3.PutBucketInventoryConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketInventoryConfigurationInput) *s3.PutBucketInventoryConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketInventoryConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketInventoryConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketInventoryConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketInventoryConfigurationRequest(_a0 *s3.PutBucketInventoryConfigurationInput) (*request.Request, *s3.PutBucketInventoryConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketInventoryConfigurationRequest") } var r0 *request.Request var r1 *s3.PutBucketInventoryConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketInventoryConfigurationInput) (*request.Request, *s3.PutBucketInventoryConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketInventoryConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketInventoryConfigurationInput) *s3.PutBucketInventoryConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketInventoryConfigurationOutput) } } return r0, r1 } // PutBucketInventoryConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketInventoryConfigurationWithContext(_a0 context.Context, _a1 *s3.PutBucketInventoryConfigurationInput, _a2 ...request.Option) (*s3.PutBucketInventoryConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketInventoryConfigurationWithContext") } var r0 *s3.PutBucketInventoryConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketInventoryConfigurationInput, ...request.Option) (*s3.PutBucketInventoryConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketInventoryConfigurationInput, ...request.Option) *s3.PutBucketInventoryConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketInventoryConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketInventoryConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketLifecycle provides a mock function with given fields: _a0 func (_m *S3API) PutBucketLifecycle(_a0 *s3.PutBucketLifecycleInput) (*s3.PutBucketLifecycleOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketLifecycle") } var r0 *s3.PutBucketLifecycleOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketLifecycleInput) (*s3.PutBucketLifecycleOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketLifecycleInput) *s3.PutBucketLifecycleOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketLifecycleOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketLifecycleInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketLifecycleConfiguration provides a mock function with given fields: _a0 func (_m *S3API) PutBucketLifecycleConfiguration(_a0 *s3.PutBucketLifecycleConfigurationInput) (*s3.PutBucketLifecycleConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketLifecycleConfiguration") } var r0 *s3.PutBucketLifecycleConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketLifecycleConfigurationInput) (*s3.PutBucketLifecycleConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketLifecycleConfigurationInput) *s3.PutBucketLifecycleConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketLifecycleConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketLifecycleConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketLifecycleConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketLifecycleConfigurationRequest(_a0 *s3.PutBucketLifecycleConfigurationInput) (*request.Request, *s3.PutBucketLifecycleConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketLifecycleConfigurationRequest") } var r0 *request.Request var r1 *s3.PutBucketLifecycleConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketLifecycleConfigurationInput) (*request.Request, *s3.PutBucketLifecycleConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketLifecycleConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketLifecycleConfigurationInput) *s3.PutBucketLifecycleConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketLifecycleConfigurationOutput) } } return r0, r1 } // PutBucketLifecycleConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketLifecycleConfigurationWithContext(_a0 context.Context, _a1 *s3.PutBucketLifecycleConfigurationInput, _a2 ...request.Option) (*s3.PutBucketLifecycleConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketLifecycleConfigurationWithContext") } var r0 *s3.PutBucketLifecycleConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketLifecycleConfigurationInput, ...request.Option) (*s3.PutBucketLifecycleConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketLifecycleConfigurationInput, ...request.Option) *s3.PutBucketLifecycleConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketLifecycleConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketLifecycleConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketLifecycleRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketLifecycleRequest(_a0 *s3.PutBucketLifecycleInput) (*request.Request, *s3.PutBucketLifecycleOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketLifecycleRequest") } var r0 *request.Request var r1 *s3.PutBucketLifecycleOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketLifecycleInput) (*request.Request, *s3.PutBucketLifecycleOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketLifecycleInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketLifecycleInput) *s3.PutBucketLifecycleOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketLifecycleOutput) } } return r0, r1 } // PutBucketLifecycleWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketLifecycleWithContext(_a0 context.Context, _a1 *s3.PutBucketLifecycleInput, _a2 ...request.Option) (*s3.PutBucketLifecycleOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketLifecycleWithContext") } var r0 *s3.PutBucketLifecycleOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketLifecycleInput, ...request.Option) (*s3.PutBucketLifecycleOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketLifecycleInput, ...request.Option) *s3.PutBucketLifecycleOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketLifecycleOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketLifecycleInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketLogging provides a mock function with given fields: _a0 func (_m *S3API) PutBucketLogging(_a0 *s3.PutBucketLoggingInput) (*s3.PutBucketLoggingOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketLogging") } var r0 *s3.PutBucketLoggingOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketLoggingInput) (*s3.PutBucketLoggingOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketLoggingInput) *s3.PutBucketLoggingOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketLoggingOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketLoggingInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketLoggingRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketLoggingRequest(_a0 *s3.PutBucketLoggingInput) (*request.Request, *s3.PutBucketLoggingOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketLoggingRequest") } var r0 *request.Request var r1 *s3.PutBucketLoggingOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketLoggingInput) (*request.Request, *s3.PutBucketLoggingOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketLoggingInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketLoggingInput) *s3.PutBucketLoggingOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketLoggingOutput) } } return r0, r1 } // PutBucketLoggingWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketLoggingWithContext(_a0 context.Context, _a1 *s3.PutBucketLoggingInput, _a2 ...request.Option) (*s3.PutBucketLoggingOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketLoggingWithContext") } var r0 *s3.PutBucketLoggingOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketLoggingInput, ...request.Option) (*s3.PutBucketLoggingOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketLoggingInput, ...request.Option) *s3.PutBucketLoggingOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketLoggingOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketLoggingInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketMetricsConfiguration provides a mock function with given fields: _a0 func (_m *S3API) PutBucketMetricsConfiguration(_a0 *s3.PutBucketMetricsConfigurationInput) (*s3.PutBucketMetricsConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketMetricsConfiguration") } var r0 *s3.PutBucketMetricsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketMetricsConfigurationInput) (*s3.PutBucketMetricsConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketMetricsConfigurationInput) *s3.PutBucketMetricsConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketMetricsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketMetricsConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketMetricsConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketMetricsConfigurationRequest(_a0 *s3.PutBucketMetricsConfigurationInput) (*request.Request, *s3.PutBucketMetricsConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketMetricsConfigurationRequest") } var r0 *request.Request var r1 *s3.PutBucketMetricsConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketMetricsConfigurationInput) (*request.Request, *s3.PutBucketMetricsConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketMetricsConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketMetricsConfigurationInput) *s3.PutBucketMetricsConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketMetricsConfigurationOutput) } } return r0, r1 } // PutBucketMetricsConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketMetricsConfigurationWithContext(_a0 context.Context, _a1 *s3.PutBucketMetricsConfigurationInput, _a2 ...request.Option) (*s3.PutBucketMetricsConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketMetricsConfigurationWithContext") } var r0 *s3.PutBucketMetricsConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketMetricsConfigurationInput, ...request.Option) (*s3.PutBucketMetricsConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketMetricsConfigurationInput, ...request.Option) *s3.PutBucketMetricsConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketMetricsConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketMetricsConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketNotification provides a mock function with given fields: _a0 func (_m *S3API) PutBucketNotification(_a0 *s3.PutBucketNotificationInput) (*s3.PutBucketNotificationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketNotification") } var r0 *s3.PutBucketNotificationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketNotificationInput) (*s3.PutBucketNotificationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketNotificationInput) *s3.PutBucketNotificationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketNotificationOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketNotificationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketNotificationConfiguration provides a mock function with given fields: _a0 func (_m *S3API) PutBucketNotificationConfiguration(_a0 *s3.PutBucketNotificationConfigurationInput) (*s3.PutBucketNotificationConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketNotificationConfiguration") } var r0 *s3.PutBucketNotificationConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketNotificationConfigurationInput) (*s3.PutBucketNotificationConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketNotificationConfigurationInput) *s3.PutBucketNotificationConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketNotificationConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketNotificationConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketNotificationConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketNotificationConfigurationRequest(_a0 *s3.PutBucketNotificationConfigurationInput) (*request.Request, *s3.PutBucketNotificationConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketNotificationConfigurationRequest") } var r0 *request.Request var r1 *s3.PutBucketNotificationConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketNotificationConfigurationInput) (*request.Request, *s3.PutBucketNotificationConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketNotificationConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketNotificationConfigurationInput) *s3.PutBucketNotificationConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketNotificationConfigurationOutput) } } return r0, r1 } // PutBucketNotificationConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketNotificationConfigurationWithContext(_a0 context.Context, _a1 *s3.PutBucketNotificationConfigurationInput, _a2 ...request.Option) (*s3.PutBucketNotificationConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketNotificationConfigurationWithContext") } var r0 *s3.PutBucketNotificationConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketNotificationConfigurationInput, ...request.Option) (*s3.PutBucketNotificationConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketNotificationConfigurationInput, ...request.Option) *s3.PutBucketNotificationConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketNotificationConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketNotificationConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketNotificationRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketNotificationRequest(_a0 *s3.PutBucketNotificationInput) (*request.Request, *s3.PutBucketNotificationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketNotificationRequest") } var r0 *request.Request var r1 *s3.PutBucketNotificationOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketNotificationInput) (*request.Request, *s3.PutBucketNotificationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketNotificationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketNotificationInput) *s3.PutBucketNotificationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketNotificationOutput) } } return r0, r1 } // PutBucketNotificationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketNotificationWithContext(_a0 context.Context, _a1 *s3.PutBucketNotificationInput, _a2 ...request.Option) (*s3.PutBucketNotificationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketNotificationWithContext") } var r0 *s3.PutBucketNotificationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketNotificationInput, ...request.Option) (*s3.PutBucketNotificationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketNotificationInput, ...request.Option) *s3.PutBucketNotificationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketNotificationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketNotificationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketOwnershipControls provides a mock function with given fields: _a0 func (_m *S3API) PutBucketOwnershipControls(_a0 *s3.PutBucketOwnershipControlsInput) (*s3.PutBucketOwnershipControlsOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketOwnershipControls") } var r0 *s3.PutBucketOwnershipControlsOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketOwnershipControlsInput) (*s3.PutBucketOwnershipControlsOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketOwnershipControlsInput) *s3.PutBucketOwnershipControlsOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketOwnershipControlsOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketOwnershipControlsInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketOwnershipControlsRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketOwnershipControlsRequest(_a0 *s3.PutBucketOwnershipControlsInput) (*request.Request, *s3.PutBucketOwnershipControlsOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketOwnershipControlsRequest") } var r0 *request.Request var r1 *s3.PutBucketOwnershipControlsOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketOwnershipControlsInput) (*request.Request, *s3.PutBucketOwnershipControlsOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketOwnershipControlsInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketOwnershipControlsInput) *s3.PutBucketOwnershipControlsOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketOwnershipControlsOutput) } } return r0, r1 } // PutBucketOwnershipControlsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketOwnershipControlsWithContext(_a0 context.Context, _a1 *s3.PutBucketOwnershipControlsInput, _a2 ...request.Option) (*s3.PutBucketOwnershipControlsOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketOwnershipControlsWithContext") } var r0 *s3.PutBucketOwnershipControlsOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketOwnershipControlsInput, ...request.Option) (*s3.PutBucketOwnershipControlsOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketOwnershipControlsInput, ...request.Option) *s3.PutBucketOwnershipControlsOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketOwnershipControlsOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketOwnershipControlsInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketPolicy provides a mock function with given fields: _a0 func (_m *S3API) PutBucketPolicy(_a0 *s3.PutBucketPolicyInput) (*s3.PutBucketPolicyOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketPolicy") } var r0 *s3.PutBucketPolicyOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketPolicyInput) (*s3.PutBucketPolicyOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketPolicyInput) *s3.PutBucketPolicyOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketPolicyOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketPolicyInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketPolicyRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketPolicyRequest(_a0 *s3.PutBucketPolicyInput) (*request.Request, *s3.PutBucketPolicyOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketPolicyRequest") } var r0 *request.Request var r1 *s3.PutBucketPolicyOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketPolicyInput) (*request.Request, *s3.PutBucketPolicyOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketPolicyInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketPolicyInput) *s3.PutBucketPolicyOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketPolicyOutput) } } return r0, r1 } // PutBucketPolicyWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketPolicyWithContext(_a0 context.Context, _a1 *s3.PutBucketPolicyInput, _a2 ...request.Option) (*s3.PutBucketPolicyOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketPolicyWithContext") } var r0 *s3.PutBucketPolicyOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketPolicyInput, ...request.Option) (*s3.PutBucketPolicyOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketPolicyInput, ...request.Option) *s3.PutBucketPolicyOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketPolicyOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketPolicyInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketReplication provides a mock function with given fields: _a0 func (_m *S3API) PutBucketReplication(_a0 *s3.PutBucketReplicationInput) (*s3.PutBucketReplicationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketReplication") } var r0 *s3.PutBucketReplicationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketReplicationInput) (*s3.PutBucketReplicationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketReplicationInput) *s3.PutBucketReplicationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketReplicationOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketReplicationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketReplicationRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketReplicationRequest(_a0 *s3.PutBucketReplicationInput) (*request.Request, *s3.PutBucketReplicationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketReplicationRequest") } var r0 *request.Request var r1 *s3.PutBucketReplicationOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketReplicationInput) (*request.Request, *s3.PutBucketReplicationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketReplicationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketReplicationInput) *s3.PutBucketReplicationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketReplicationOutput) } } return r0, r1 } // PutBucketReplicationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketReplicationWithContext(_a0 context.Context, _a1 *s3.PutBucketReplicationInput, _a2 ...request.Option) (*s3.PutBucketReplicationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketReplicationWithContext") } var r0 *s3.PutBucketReplicationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketReplicationInput, ...request.Option) (*s3.PutBucketReplicationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketReplicationInput, ...request.Option) *s3.PutBucketReplicationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketReplicationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketReplicationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketRequestPayment provides a mock function with given fields: _a0 func (_m *S3API) PutBucketRequestPayment(_a0 *s3.PutBucketRequestPaymentInput) (*s3.PutBucketRequestPaymentOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketRequestPayment") } var r0 *s3.PutBucketRequestPaymentOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketRequestPaymentInput) (*s3.PutBucketRequestPaymentOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketRequestPaymentInput) *s3.PutBucketRequestPaymentOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketRequestPaymentOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketRequestPaymentInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketRequestPaymentRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketRequestPaymentRequest(_a0 *s3.PutBucketRequestPaymentInput) (*request.Request, *s3.PutBucketRequestPaymentOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketRequestPaymentRequest") } var r0 *request.Request var r1 *s3.PutBucketRequestPaymentOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketRequestPaymentInput) (*request.Request, *s3.PutBucketRequestPaymentOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketRequestPaymentInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketRequestPaymentInput) *s3.PutBucketRequestPaymentOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketRequestPaymentOutput) } } return r0, r1 } // PutBucketRequestPaymentWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketRequestPaymentWithContext(_a0 context.Context, _a1 *s3.PutBucketRequestPaymentInput, _a2 ...request.Option) (*s3.PutBucketRequestPaymentOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketRequestPaymentWithContext") } var r0 *s3.PutBucketRequestPaymentOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketRequestPaymentInput, ...request.Option) (*s3.PutBucketRequestPaymentOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketRequestPaymentInput, ...request.Option) *s3.PutBucketRequestPaymentOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketRequestPaymentOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketRequestPaymentInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketTagging provides a mock function with given fields: _a0 func (_m *S3API) PutBucketTagging(_a0 *s3.PutBucketTaggingInput) (*s3.PutBucketTaggingOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketTagging") } var r0 *s3.PutBucketTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketTaggingInput) (*s3.PutBucketTaggingOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketTaggingInput) *s3.PutBucketTaggingOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketTaggingOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketTaggingInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketTaggingRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketTaggingRequest(_a0 *s3.PutBucketTaggingInput) (*request.Request, *s3.PutBucketTaggingOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketTaggingRequest") } var r0 *request.Request var r1 *s3.PutBucketTaggingOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketTaggingInput) (*request.Request, *s3.PutBucketTaggingOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketTaggingInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketTaggingInput) *s3.PutBucketTaggingOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketTaggingOutput) } } return r0, r1 } // PutBucketTaggingWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketTaggingWithContext(_a0 context.Context, _a1 *s3.PutBucketTaggingInput, _a2 ...request.Option) (*s3.PutBucketTaggingOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketTaggingWithContext") } var r0 *s3.PutBucketTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketTaggingInput, ...request.Option) (*s3.PutBucketTaggingOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketTaggingInput, ...request.Option) *s3.PutBucketTaggingOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketTaggingOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketTaggingInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketVersioning provides a mock function with given fields: _a0 func (_m *S3API) PutBucketVersioning(_a0 *s3.PutBucketVersioningInput) (*s3.PutBucketVersioningOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketVersioning") } var r0 *s3.PutBucketVersioningOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketVersioningInput) (*s3.PutBucketVersioningOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketVersioningInput) *s3.PutBucketVersioningOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketVersioningOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketVersioningInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketVersioningRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketVersioningRequest(_a0 *s3.PutBucketVersioningInput) (*request.Request, *s3.PutBucketVersioningOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketVersioningRequest") } var r0 *request.Request var r1 *s3.PutBucketVersioningOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketVersioningInput) (*request.Request, *s3.PutBucketVersioningOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketVersioningInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketVersioningInput) *s3.PutBucketVersioningOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketVersioningOutput) } } return r0, r1 } // PutBucketVersioningWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketVersioningWithContext(_a0 context.Context, _a1 *s3.PutBucketVersioningInput, _a2 ...request.Option) (*s3.PutBucketVersioningOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketVersioningWithContext") } var r0 *s3.PutBucketVersioningOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketVersioningInput, ...request.Option) (*s3.PutBucketVersioningOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketVersioningInput, ...request.Option) *s3.PutBucketVersioningOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketVersioningOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketVersioningInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketWebsite provides a mock function with given fields: _a0 func (_m *S3API) PutBucketWebsite(_a0 *s3.PutBucketWebsiteInput) (*s3.PutBucketWebsiteOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketWebsite") } var r0 *s3.PutBucketWebsiteOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutBucketWebsiteInput) (*s3.PutBucketWebsiteOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketWebsiteInput) *s3.PutBucketWebsiteOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketWebsiteOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketWebsiteInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutBucketWebsiteRequest provides a mock function with given fields: _a0 func (_m *S3API) PutBucketWebsiteRequest(_a0 *s3.PutBucketWebsiteInput) (*request.Request, *s3.PutBucketWebsiteOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutBucketWebsiteRequest") } var r0 *request.Request var r1 *s3.PutBucketWebsiteOutput if rf, ok := ret.Get(0).(func(*s3.PutBucketWebsiteInput) (*request.Request, *s3.PutBucketWebsiteOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutBucketWebsiteInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutBucketWebsiteInput) *s3.PutBucketWebsiteOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutBucketWebsiteOutput) } } return r0, r1 } // PutBucketWebsiteWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutBucketWebsiteWithContext(_a0 context.Context, _a1 *s3.PutBucketWebsiteInput, _a2 ...request.Option) (*s3.PutBucketWebsiteOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutBucketWebsiteWithContext") } var r0 *s3.PutBucketWebsiteOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketWebsiteInput, ...request.Option) (*s3.PutBucketWebsiteOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutBucketWebsiteInput, ...request.Option) *s3.PutBucketWebsiteOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutBucketWebsiteOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutBucketWebsiteInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutObject provides a mock function with given fields: _a0 func (_m *S3API) PutObject(_a0 *s3.PutObjectInput) (*s3.PutObjectOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObject") } var r0 *s3.PutObjectOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutObjectInput) (*s3.PutObjectOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectInput) *s3.PutObjectOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectAcl provides a mock function with given fields: _a0 func (_m *S3API) PutObjectAcl(_a0 *s3.PutObjectAclInput) (*s3.PutObjectAclOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectAcl") } var r0 *s3.PutObjectAclOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutObjectAclInput) (*s3.PutObjectAclOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectAclInput) *s3.PutObjectAclOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectAclOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectAclInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectAclRequest provides a mock function with given fields: _a0 func (_m *S3API) PutObjectAclRequest(_a0 *s3.PutObjectAclInput) (*request.Request, *s3.PutObjectAclOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectAclRequest") } var r0 *request.Request var r1 *s3.PutObjectAclOutput if rf, ok := ret.Get(0).(func(*s3.PutObjectAclInput) (*request.Request, *s3.PutObjectAclOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectAclInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectAclInput) *s3.PutObjectAclOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutObjectAclOutput) } } return r0, r1 } // PutObjectAclWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutObjectAclWithContext(_a0 context.Context, _a1 *s3.PutObjectAclInput, _a2 ...request.Option) (*s3.PutObjectAclOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutObjectAclWithContext") } var r0 *s3.PutObjectAclOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectAclInput, ...request.Option) (*s3.PutObjectAclOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectAclInput, ...request.Option) *s3.PutObjectAclOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectAclOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutObjectAclInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectLegalHold provides a mock function with given fields: _a0 func (_m *S3API) PutObjectLegalHold(_a0 *s3.PutObjectLegalHoldInput) (*s3.PutObjectLegalHoldOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectLegalHold") } var r0 *s3.PutObjectLegalHoldOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutObjectLegalHoldInput) (*s3.PutObjectLegalHoldOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectLegalHoldInput) *s3.PutObjectLegalHoldOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectLegalHoldOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectLegalHoldInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectLegalHoldRequest provides a mock function with given fields: _a0 func (_m *S3API) PutObjectLegalHoldRequest(_a0 *s3.PutObjectLegalHoldInput) (*request.Request, *s3.PutObjectLegalHoldOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectLegalHoldRequest") } var r0 *request.Request var r1 *s3.PutObjectLegalHoldOutput if rf, ok := ret.Get(0).(func(*s3.PutObjectLegalHoldInput) (*request.Request, *s3.PutObjectLegalHoldOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectLegalHoldInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectLegalHoldInput) *s3.PutObjectLegalHoldOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutObjectLegalHoldOutput) } } return r0, r1 } // PutObjectLegalHoldWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutObjectLegalHoldWithContext(_a0 context.Context, _a1 *s3.PutObjectLegalHoldInput, _a2 ...request.Option) (*s3.PutObjectLegalHoldOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutObjectLegalHoldWithContext") } var r0 *s3.PutObjectLegalHoldOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectLegalHoldInput, ...request.Option) (*s3.PutObjectLegalHoldOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectLegalHoldInput, ...request.Option) *s3.PutObjectLegalHoldOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectLegalHoldOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutObjectLegalHoldInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectLockConfiguration provides a mock function with given fields: _a0 func (_m *S3API) PutObjectLockConfiguration(_a0 *s3.PutObjectLockConfigurationInput) (*s3.PutObjectLockConfigurationOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectLockConfiguration") } var r0 *s3.PutObjectLockConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutObjectLockConfigurationInput) (*s3.PutObjectLockConfigurationOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectLockConfigurationInput) *s3.PutObjectLockConfigurationOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectLockConfigurationOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectLockConfigurationInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectLockConfigurationRequest provides a mock function with given fields: _a0 func (_m *S3API) PutObjectLockConfigurationRequest(_a0 *s3.PutObjectLockConfigurationInput) (*request.Request, *s3.PutObjectLockConfigurationOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectLockConfigurationRequest") } var r0 *request.Request var r1 *s3.PutObjectLockConfigurationOutput if rf, ok := ret.Get(0).(func(*s3.PutObjectLockConfigurationInput) (*request.Request, *s3.PutObjectLockConfigurationOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectLockConfigurationInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectLockConfigurationInput) *s3.PutObjectLockConfigurationOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutObjectLockConfigurationOutput) } } return r0, r1 } // PutObjectLockConfigurationWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutObjectLockConfigurationWithContext(_a0 context.Context, _a1 *s3.PutObjectLockConfigurationInput, _a2 ...request.Option) (*s3.PutObjectLockConfigurationOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutObjectLockConfigurationWithContext") } var r0 *s3.PutObjectLockConfigurationOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectLockConfigurationInput, ...request.Option) (*s3.PutObjectLockConfigurationOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectLockConfigurationInput, ...request.Option) *s3.PutObjectLockConfigurationOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectLockConfigurationOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutObjectLockConfigurationInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectRequest provides a mock function with given fields: _a0 func (_m *S3API) PutObjectRequest(_a0 *s3.PutObjectInput) (*request.Request, *s3.PutObjectOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectRequest") } var r0 *request.Request var r1 *s3.PutObjectOutput if rf, ok := ret.Get(0).(func(*s3.PutObjectInput) (*request.Request, *s3.PutObjectOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectInput) *s3.PutObjectOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutObjectOutput) } } return r0, r1 } // PutObjectRetention provides a mock function with given fields: _a0 func (_m *S3API) PutObjectRetention(_a0 *s3.PutObjectRetentionInput) (*s3.PutObjectRetentionOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectRetention") } var r0 *s3.PutObjectRetentionOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutObjectRetentionInput) (*s3.PutObjectRetentionOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectRetentionInput) *s3.PutObjectRetentionOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectRetentionOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectRetentionInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectRetentionRequest provides a mock function with given fields: _a0 func (_m *S3API) PutObjectRetentionRequest(_a0 *s3.PutObjectRetentionInput) (*request.Request, *s3.PutObjectRetentionOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectRetentionRequest") } var r0 *request.Request var r1 *s3.PutObjectRetentionOutput if rf, ok := ret.Get(0).(func(*s3.PutObjectRetentionInput) (*request.Request, *s3.PutObjectRetentionOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectRetentionInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectRetentionInput) *s3.PutObjectRetentionOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutObjectRetentionOutput) } } return r0, r1 } // PutObjectRetentionWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutObjectRetentionWithContext(_a0 context.Context, _a1 *s3.PutObjectRetentionInput, _a2 ...request.Option) (*s3.PutObjectRetentionOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutObjectRetentionWithContext") } var r0 *s3.PutObjectRetentionOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectRetentionInput, ...request.Option) (*s3.PutObjectRetentionOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectRetentionInput, ...request.Option) *s3.PutObjectRetentionOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectRetentionOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutObjectRetentionInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectTagging provides a mock function with given fields: _a0 func (_m *S3API) PutObjectTagging(_a0 *s3.PutObjectTaggingInput) (*s3.PutObjectTaggingOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectTagging") } var r0 *s3.PutObjectTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutObjectTaggingInput) (*s3.PutObjectTaggingOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectTaggingInput) *s3.PutObjectTaggingOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectTaggingOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectTaggingInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectTaggingRequest provides a mock function with given fields: _a0 func (_m *S3API) PutObjectTaggingRequest(_a0 *s3.PutObjectTaggingInput) (*request.Request, *s3.PutObjectTaggingOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutObjectTaggingRequest") } var r0 *request.Request var r1 *s3.PutObjectTaggingOutput if rf, ok := ret.Get(0).(func(*s3.PutObjectTaggingInput) (*request.Request, *s3.PutObjectTaggingOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutObjectTaggingInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutObjectTaggingInput) *s3.PutObjectTaggingOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutObjectTaggingOutput) } } return r0, r1 } // PutObjectTaggingWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutObjectTaggingWithContext(_a0 context.Context, _a1 *s3.PutObjectTaggingInput, _a2 ...request.Option) (*s3.PutObjectTaggingOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutObjectTaggingWithContext") } var r0 *s3.PutObjectTaggingOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectTaggingInput, ...request.Option) (*s3.PutObjectTaggingOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectTaggingInput, ...request.Option) *s3.PutObjectTaggingOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectTaggingOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutObjectTaggingInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutObjectWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutObjectWithContext(_a0 context.Context, _a1 *s3.PutObjectInput, _a2 ...request.Option) (*s3.PutObjectOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutObjectWithContext") } var r0 *s3.PutObjectOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectInput, ...request.Option) (*s3.PutObjectOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutObjectInput, ...request.Option) *s3.PutObjectOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutObjectOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutObjectInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // PutPublicAccessBlock provides a mock function with given fields: _a0 func (_m *S3API) PutPublicAccessBlock(_a0 *s3.PutPublicAccessBlockInput) (*s3.PutPublicAccessBlockOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutPublicAccessBlock") } var r0 *s3.PutPublicAccessBlockOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.PutPublicAccessBlockInput) (*s3.PutPublicAccessBlockOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutPublicAccessBlockInput) *s3.PutPublicAccessBlockOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutPublicAccessBlockOutput) } } if rf, ok := ret.Get(1).(func(*s3.PutPublicAccessBlockInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // PutPublicAccessBlockRequest provides a mock function with given fields: _a0 func (_m *S3API) PutPublicAccessBlockRequest(_a0 *s3.PutPublicAccessBlockInput) (*request.Request, *s3.PutPublicAccessBlockOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for PutPublicAccessBlockRequest") } var r0 *request.Request var r1 *s3.PutPublicAccessBlockOutput if rf, ok := ret.Get(0).(func(*s3.PutPublicAccessBlockInput) (*request.Request, *s3.PutPublicAccessBlockOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.PutPublicAccessBlockInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.PutPublicAccessBlockInput) *s3.PutPublicAccessBlockOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.PutPublicAccessBlockOutput) } } return r0, r1 } // PutPublicAccessBlockWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) PutPublicAccessBlockWithContext(_a0 context.Context, _a1 *s3.PutPublicAccessBlockInput, _a2 ...request.Option) (*s3.PutPublicAccessBlockOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for PutPublicAccessBlockWithContext") } var r0 *s3.PutPublicAccessBlockOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.PutPublicAccessBlockInput, ...request.Option) (*s3.PutPublicAccessBlockOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.PutPublicAccessBlockInput, ...request.Option) *s3.PutPublicAccessBlockOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.PutPublicAccessBlockOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.PutPublicAccessBlockInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // RestoreObject provides a mock function with given fields: _a0 func (_m *S3API) RestoreObject(_a0 *s3.RestoreObjectInput) (*s3.RestoreObjectOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for RestoreObject") } var r0 *s3.RestoreObjectOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.RestoreObjectInput) (*s3.RestoreObjectOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.RestoreObjectInput) *s3.RestoreObjectOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.RestoreObjectOutput) } } if rf, ok := ret.Get(1).(func(*s3.RestoreObjectInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // RestoreObjectRequest provides a mock function with given fields: _a0 func (_m *S3API) RestoreObjectRequest(_a0 *s3.RestoreObjectInput) (*request.Request, *s3.RestoreObjectOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for RestoreObjectRequest") } var r0 *request.Request var r1 *s3.RestoreObjectOutput if rf, ok := ret.Get(0).(func(*s3.RestoreObjectInput) (*request.Request, *s3.RestoreObjectOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.RestoreObjectInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.RestoreObjectInput) *s3.RestoreObjectOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.RestoreObjectOutput) } } return r0, r1 } // RestoreObjectWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) RestoreObjectWithContext(_a0 context.Context, _a1 *s3.RestoreObjectInput, _a2 ...request.Option) (*s3.RestoreObjectOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for RestoreObjectWithContext") } var r0 *s3.RestoreObjectOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.RestoreObjectInput, ...request.Option) (*s3.RestoreObjectOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.RestoreObjectInput, ...request.Option) *s3.RestoreObjectOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.RestoreObjectOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.RestoreObjectInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // SelectObjectContent provides a mock function with given fields: _a0 func (_m *S3API) SelectObjectContent(_a0 *s3.SelectObjectContentInput) (*s3.SelectObjectContentOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for SelectObjectContent") } var r0 *s3.SelectObjectContentOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.SelectObjectContentInput) (*s3.SelectObjectContentOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.SelectObjectContentInput) *s3.SelectObjectContentOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.SelectObjectContentOutput) } } if rf, ok := ret.Get(1).(func(*s3.SelectObjectContentInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // SelectObjectContentRequest provides a mock function with given fields: _a0 func (_m *S3API) SelectObjectContentRequest(_a0 *s3.SelectObjectContentInput) (*request.Request, *s3.SelectObjectContentOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for SelectObjectContentRequest") } var r0 *request.Request var r1 *s3.SelectObjectContentOutput if rf, ok := ret.Get(0).(func(*s3.SelectObjectContentInput) (*request.Request, *s3.SelectObjectContentOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.SelectObjectContentInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.SelectObjectContentInput) *s3.SelectObjectContentOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.SelectObjectContentOutput) } } return r0, r1 } // SelectObjectContentWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) SelectObjectContentWithContext(_a0 context.Context, _a1 *s3.SelectObjectContentInput, _a2 ...request.Option) (*s3.SelectObjectContentOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for SelectObjectContentWithContext") } var r0 *s3.SelectObjectContentOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.SelectObjectContentInput, ...request.Option) (*s3.SelectObjectContentOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.SelectObjectContentInput, ...request.Option) *s3.SelectObjectContentOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.SelectObjectContentOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.SelectObjectContentInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // UploadPart provides a mock function with given fields: _a0 func (_m *S3API) UploadPart(_a0 *s3.UploadPartInput) (*s3.UploadPartOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for UploadPart") } var r0 *s3.UploadPartOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.UploadPartInput) (*s3.UploadPartOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.UploadPartInput) *s3.UploadPartOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.UploadPartOutput) } } if rf, ok := ret.Get(1).(func(*s3.UploadPartInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // UploadPartCopy provides a mock function with given fields: _a0 func (_m *S3API) UploadPartCopy(_a0 *s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for UploadPartCopy") } var r0 *s3.UploadPartCopyOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.UploadPartCopyInput) *s3.UploadPartCopyOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.UploadPartCopyOutput) } } if rf, ok := ret.Get(1).(func(*s3.UploadPartCopyInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // UploadPartCopyRequest provides a mock function with given fields: _a0 func (_m *S3API) UploadPartCopyRequest(_a0 *s3.UploadPartCopyInput) (*request.Request, *s3.UploadPartCopyOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for UploadPartCopyRequest") } var r0 *request.Request var r1 *s3.UploadPartCopyOutput if rf, ok := ret.Get(0).(func(*s3.UploadPartCopyInput) (*request.Request, *s3.UploadPartCopyOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.UploadPartCopyInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.UploadPartCopyInput) *s3.UploadPartCopyOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.UploadPartCopyOutput) } } return r0, r1 } // UploadPartCopyWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) UploadPartCopyWithContext(_a0 context.Context, _a1 *s3.UploadPartCopyInput, _a2 ...request.Option) (*s3.UploadPartCopyOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for UploadPartCopyWithContext") } var r0 *s3.UploadPartCopyOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.UploadPartCopyInput, ...request.Option) (*s3.UploadPartCopyOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.UploadPartCopyInput, ...request.Option) *s3.UploadPartCopyOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.UploadPartCopyOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.UploadPartCopyInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // UploadPartRequest provides a mock function with given fields: _a0 func (_m *S3API) UploadPartRequest(_a0 *s3.UploadPartInput) (*request.Request, *s3.UploadPartOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for UploadPartRequest") } var r0 *request.Request var r1 *s3.UploadPartOutput if rf, ok := ret.Get(0).(func(*s3.UploadPartInput) (*request.Request, *s3.UploadPartOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.UploadPartInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.UploadPartInput) *s3.UploadPartOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.UploadPartOutput) } } return r0, r1 } // UploadPartWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) UploadPartWithContext(_a0 context.Context, _a1 *s3.UploadPartInput, _a2 ...request.Option) (*s3.UploadPartOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for UploadPartWithContext") } var r0 *s3.UploadPartOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.UploadPartInput, ...request.Option) (*s3.UploadPartOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.UploadPartInput, ...request.Option) *s3.UploadPartOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.UploadPartOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.UploadPartInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // WaitUntilBucketExists provides a mock function with given fields: _a0 func (_m *S3API) WaitUntilBucketExists(_a0 *s3.HeadBucketInput) error { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for WaitUntilBucketExists") } var r0 error if rf, ok := ret.Get(0).(func(*s3.HeadBucketInput) error); ok { r0 = rf(_a0) } else { r0 = ret.Error(0) } return r0 } // WaitUntilBucketExistsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) WaitUntilBucketExistsWithContext(_a0 context.Context, _a1 *s3.HeadBucketInput, _a2 ...request.WaiterOption) error { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for WaitUntilBucketExistsWithContext") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s3.HeadBucketInput, ...request.WaiterOption) error); ok { r0 = rf(_a0, _a1, _a2...) } else { r0 = ret.Error(0) } return r0 } // WaitUntilBucketNotExists provides a mock function with given fields: _a0 func (_m *S3API) WaitUntilBucketNotExists(_a0 *s3.HeadBucketInput) error { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for WaitUntilBucketNotExists") } var r0 error if rf, ok := ret.Get(0).(func(*s3.HeadBucketInput) error); ok { r0 = rf(_a0) } else { r0 = ret.Error(0) } return r0 } // WaitUntilBucketNotExistsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) WaitUntilBucketNotExistsWithContext(_a0 context.Context, _a1 *s3.HeadBucketInput, _a2 ...request.WaiterOption) error { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for WaitUntilBucketNotExistsWithContext") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s3.HeadBucketInput, ...request.WaiterOption) error); ok { r0 = rf(_a0, _a1, _a2...) } else { r0 = ret.Error(0) } return r0 } // WaitUntilObjectExists provides a mock function with given fields: _a0 func (_m *S3API) WaitUntilObjectExists(_a0 *s3.HeadObjectInput) error { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for WaitUntilObjectExists") } var r0 error if rf, ok := ret.Get(0).(func(*s3.HeadObjectInput) error); ok { r0 = rf(_a0) } else { r0 = ret.Error(0) } return r0 } // WaitUntilObjectExistsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) WaitUntilObjectExistsWithContext(_a0 context.Context, _a1 *s3.HeadObjectInput, _a2 ...request.WaiterOption) error { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for WaitUntilObjectExistsWithContext") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s3.HeadObjectInput, ...request.WaiterOption) error); ok { r0 = rf(_a0, _a1, _a2...) } else { r0 = ret.Error(0) } return r0 } // WaitUntilObjectNotExists provides a mock function with given fields: _a0 func (_m *S3API) WaitUntilObjectNotExists(_a0 *s3.HeadObjectInput) error { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for WaitUntilObjectNotExists") } var r0 error if rf, ok := ret.Get(0).(func(*s3.HeadObjectInput) error); ok { r0 = rf(_a0) } else { r0 = ret.Error(0) } return r0 } // WaitUntilObjectNotExistsWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) WaitUntilObjectNotExistsWithContext(_a0 context.Context, _a1 *s3.HeadObjectInput, _a2 ...request.WaiterOption) error { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for WaitUntilObjectNotExistsWithContext") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *s3.HeadObjectInput, ...request.WaiterOption) error); ok { r0 = rf(_a0, _a1, _a2...) } else { r0 = ret.Error(0) } return r0 } // WriteGetObjectResponse provides a mock function with given fields: _a0 func (_m *S3API) WriteGetObjectResponse(_a0 *s3.WriteGetObjectResponseInput) (*s3.WriteGetObjectResponseOutput, error) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for WriteGetObjectResponse") } var r0 *s3.WriteGetObjectResponseOutput var r1 error if rf, ok := ret.Get(0).(func(*s3.WriteGetObjectResponseInput) (*s3.WriteGetObjectResponseOutput, error)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.WriteGetObjectResponseInput) *s3.WriteGetObjectResponseOutput); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.WriteGetObjectResponseOutput) } } if rf, ok := ret.Get(1).(func(*s3.WriteGetObjectResponseInput) error); ok { r1 = rf(_a0) } else { r1 = ret.Error(1) } return r0, r1 } // WriteGetObjectResponseRequest provides a mock function with given fields: _a0 func (_m *S3API) WriteGetObjectResponseRequest(_a0 *s3.WriteGetObjectResponseInput) (*request.Request, *s3.WriteGetObjectResponseOutput) { ret := _m.Called(_a0) if len(ret) == 0 { panic("no return value specified for WriteGetObjectResponseRequest") } var r0 *request.Request var r1 *s3.WriteGetObjectResponseOutput if rf, ok := ret.Get(0).(func(*s3.WriteGetObjectResponseInput) (*request.Request, *s3.WriteGetObjectResponseOutput)); ok { return rf(_a0) } if rf, ok := ret.Get(0).(func(*s3.WriteGetObjectResponseInput) *request.Request); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*request.Request) } } if rf, ok := ret.Get(1).(func(*s3.WriteGetObjectResponseInput) *s3.WriteGetObjectResponseOutput); ok { r1 = rf(_a0) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*s3.WriteGetObjectResponseOutput) } } return r0, r1 } // WriteGetObjectResponseWithContext provides a mock function with given fields: _a0, _a1, _a2 func (_m *S3API) WriteGetObjectResponseWithContext(_a0 context.Context, _a1 *s3.WriteGetObjectResponseInput, _a2 ...request.Option) (*s3.WriteGetObjectResponseOutput, error) { _va := make([]interface{}, len(_a2)) for _i := range _a2 { _va[_i] = _a2[_i] } var _ca []interface{} _ca = append(_ca, _a0, _a1) _ca = append(_ca, _va...) ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for WriteGetObjectResponseWithContext") } var r0 *s3.WriteGetObjectResponseOutput var r1 error if rf, ok := ret.Get(0).(func(context.Context, *s3.WriteGetObjectResponseInput, ...request.Option) (*s3.WriteGetObjectResponseOutput, error)); ok { return rf(_a0, _a1, _a2...) } if rf, ok := ret.Get(0).(func(context.Context, *s3.WriteGetObjectResponseInput, ...request.Option) *s3.WriteGetObjectResponseOutput); ok { r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*s3.WriteGetObjectResponseOutput) } } if rf, ok := ret.Get(1).(func(context.Context, *s3.WriteGetObjectResponseInput, ...request.Option) error); ok { r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } return r0, r1 } // NewS3API creates a new instance of S3API. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewS3API(t interface { mock.TestingT Cleanup(func()) }) *S3API { mock := &S3API{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) return mock } ================================================ FILE: common/archiver/s3store/queryParser.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source queryParser.go -destination queryParser_mock.go -mock_names Interface=MockQueryParser package s3store import ( "errors" "fmt" "strconv" "time" "github.com/xwb1989/sqlparser" "github.com/uber/cadence/common" ) type ( // QueryParser parses a limited SQL where clause into a struct QueryParser interface { Parse(query string) (*parsedQuery, error) } queryParser struct{} parsedQuery struct { workflowTypeName *string workflowID *string startTime *int64 closeTime *int64 searchPrecision *string } ) // All allowed fields for filtering const ( WorkflowTypeName = "WorkflowTypeName" WorkflowID = "WorkflowID" StartTime = "StartTime" CloseTime = "CloseTime" SearchPrecision = "SearchPrecision" ) // Precision specific values const ( PrecisionDay = "Day" PrecisionHour = "Hour" PrecisionMinute = "Minute" PrecisionSecond = "Second" ) const ( queryTemplate = "select * from dummy where %s" defaultDateTimeFormat = time.RFC3339 ) // NewQueryParser creates a new query parser for filestore func NewQueryParser() QueryParser { return &queryParser{} } func (p *queryParser) Parse(query string) (*parsedQuery, error) { stmt, err := sqlparser.Parse(fmt.Sprintf(queryTemplate, query)) if err != nil { return nil, err } whereExpr := stmt.(*sqlparser.Select).Where.Expr parsedQuery := &parsedQuery{} if err := p.convertWhereExpr(whereExpr, parsedQuery); err != nil { return nil, err } if parsedQuery.workflowID == nil && parsedQuery.workflowTypeName == nil { return nil, errors.New("WorkflowID or WorkflowTypeName is required in query") } if parsedQuery.workflowID != nil && parsedQuery.workflowTypeName != nil { return nil, errors.New("only one of WorkflowID or WorkflowTypeName can be specified in a query") } if parsedQuery.closeTime != nil && parsedQuery.startTime != nil { return nil, errors.New("only one of StartTime or CloseTime can be specified in a query") } if (parsedQuery.closeTime != nil || parsedQuery.startTime != nil) && parsedQuery.searchPrecision == nil { return nil, errors.New("SearchPrecision is required when searching for a StartTime or CloseTime") } if parsedQuery.closeTime == nil && parsedQuery.startTime == nil && parsedQuery.searchPrecision != nil { return nil, errors.New("SearchPrecision requires a StartTime or CloseTime") } return parsedQuery, nil } func (p *queryParser) convertWhereExpr(expr sqlparser.Expr, parsedQuery *parsedQuery) error { if expr == nil { return errors.New("where expression is nil") } switch expr := expr.(type) { case *sqlparser.ComparisonExpr: return p.convertComparisonExpr(expr, parsedQuery) case *sqlparser.AndExpr: return p.convertAndExpr(expr, parsedQuery) case *sqlparser.ParenExpr: return p.convertParenExpr(expr, parsedQuery) default: return errors.New("only comparison and \"and\" expression is supported") } } func (p *queryParser) convertParenExpr(parenExpr *sqlparser.ParenExpr, parsedQuery *parsedQuery) error { return p.convertWhereExpr(parenExpr.Expr, parsedQuery) } func (p *queryParser) convertAndExpr(andExpr *sqlparser.AndExpr, parsedQuery *parsedQuery) error { if err := p.convertWhereExpr(andExpr.Left, parsedQuery); err != nil { return err } return p.convertWhereExpr(andExpr.Right, parsedQuery) } func (p *queryParser) convertComparisonExpr(compExpr *sqlparser.ComparisonExpr, parsedQuery *parsedQuery) error { colName, ok := compExpr.Left.(*sqlparser.ColName) if !ok { return fmt.Errorf("invalid filter name: %s", sqlparser.String(compExpr.Left)) } colNameStr := sqlparser.String(colName) op := compExpr.Operator valExpr, ok := compExpr.Right.(*sqlparser.SQLVal) if !ok { return fmt.Errorf("invalid value: %s", sqlparser.String(compExpr.Right)) } valStr := sqlparser.String(valExpr) switch colNameStr { case WorkflowTypeName: val, err := extractStringValue(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Amazon S3", WorkflowTypeName) } if parsedQuery.workflowTypeName != nil { return fmt.Errorf("can not query %s multiple times", WorkflowTypeName) } parsedQuery.workflowTypeName = common.StringPtr(val) case WorkflowID: val, err := extractStringValue(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Amazon S3", WorkflowID) } if parsedQuery.workflowID != nil { return fmt.Errorf("can not query %s multiple times", WorkflowID) } parsedQuery.workflowID = common.StringPtr(val) case CloseTime: timestamp, err := convertToTimestamp(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Amazon S3", CloseTime) } parsedQuery.closeTime = ×tamp case StartTime: timestamp, err := convertToTimestamp(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Amazon S3", StartTime) } parsedQuery.startTime = ×tamp case SearchPrecision: val, err := extractStringValue(valStr) if err != nil { return err } if op != "=" { return fmt.Errorf("only operator = is supported for %s with Amazon S3", SearchPrecision) } if parsedQuery.searchPrecision != nil && *parsedQuery.searchPrecision != val { return fmt.Errorf("only one expression is allowed for %s", SearchPrecision) } switch val { case PrecisionDay: case PrecisionHour: case PrecisionMinute: case PrecisionSecond: default: return fmt.Errorf("invalid value for %s: %s", SearchPrecision, val) } parsedQuery.searchPrecision = common.StringPtr(val) default: return fmt.Errorf("unknown filter name: %s", colNameStr) } return nil } func convertToTimestamp(timeStr string) (int64, error) { timestamp, err := strconv.ParseInt(timeStr, 10, 64) if err == nil { return timestamp, nil } timestampStr, err := extractStringValue(timeStr) if err != nil { return 0, err } parsedTime, err := time.Parse(defaultDateTimeFormat, timestampStr) if err != nil { return 0, err } return parsedTime.UnixNano(), nil } func extractStringValue(s string) (string, error) { if len(s) >= 2 && s[0] == '\'' && s[len(s)-1] == '\'' { return s[1 : len(s)-1], nil } return "", fmt.Errorf("value %s is not a string value", s) } ================================================ FILE: common/archiver/s3store/queryParser_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: queryParser.go // // Generated by this command: // // mockgen -package s3store -source queryParser.go -destination queryParser_mock.go -mock_names Interface=MockQueryParser // // Package s3store is a generated GoMock package. package s3store import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockQueryParser is a mock of QueryParser interface. type MockQueryParser struct { ctrl *gomock.Controller recorder *MockQueryParserMockRecorder isgomock struct{} } // MockQueryParserMockRecorder is the mock recorder for MockQueryParser. type MockQueryParserMockRecorder struct { mock *MockQueryParser } // NewMockQueryParser creates a new mock instance. func NewMockQueryParser(ctrl *gomock.Controller) *MockQueryParser { mock := &MockQueryParser{ctrl: ctrl} mock.recorder = &MockQueryParserMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQueryParser) EXPECT() *MockQueryParserMockRecorder { return m.recorder } // Parse mocks base method. func (m *MockQueryParser) Parse(query string) (*parsedQuery, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Parse", query) ret0, _ := ret[0].(*parsedQuery) ret1, _ := ret[1].(error) return ret0, ret1 } // Parse indicates an expected call of Parse. func (mr *MockQueryParserMockRecorder) Parse(query any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parse", reflect.TypeOf((*MockQueryParser)(nil).Parse), query) } ================================================ FILE: common/archiver/s3store/queryParser_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package s3store import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" ) type queryParserSuite struct { *require.Assertions suite.Suite parser QueryParser } func TestQueryParserSuite(t *testing.T) { suite.Run(t, new(queryParserSuite)) } func (s *queryParserSuite) SetupTest() { s.Assertions = require.New(s.T()) s.parser = NewQueryParser() } func (s *queryParserSuite) TestParseWorkflowIDAndWorkflowTypeName() { testCases := []struct { query string expectErr bool parsedQuery *parsedQuery }{ { query: "WorkflowID = \"random workflowID\"", expectErr: false, parsedQuery: &parsedQuery{ workflowID: common.StringPtr("random workflowID"), }, }, { query: "WorkflowTypeName = \"random workflowTypeName\"", expectErr: false, parsedQuery: &parsedQuery{ workflowTypeName: common.StringPtr("random workflowTypeName"), }, }, { query: "WorkflowID = \"random workflowID\" and WorkflowTypeName = \"random workflowTypeName\"", expectErr: true, }, { query: "WorkflowID = \"random workflowID\" and WorkflowID = \"random workflowID\"", expectErr: true, }, { query: "RunID = \"random runID\"", expectErr: true, }, { query: "WorkflowID = 'random workflowID'", expectErr: false, parsedQuery: &parsedQuery{ workflowID: common.StringPtr("random workflowID"), }, }, { query: "(WorkflowID = \"random workflowID\")", expectErr: false, parsedQuery: &parsedQuery{ workflowID: common.StringPtr("random workflowID"), }, }, { query: "runID = random workflowID", expectErr: true, }, { query: "WorkflowID = \"random workflowID\" or WorkflowID = \"another workflowID\"", expectErr: true, }, { query: "WorkflowID = \"random workflowID\" or runID = \"random runID\"", expectErr: true, }, { query: "workflowid = \"random workflowID\"", expectErr: true, }, { query: "runID > \"random workflowID\"", expectErr: true, }, } for _, tc := range testCases { parsedQuery, err := s.parser.Parse(tc.query) if tc.expectErr { s.Error(err) continue } s.NoError(err) s.Equal(tc.parsedQuery.workflowID, parsedQuery.workflowID) s.Equal(tc.parsedQuery.workflowTypeName, parsedQuery.workflowTypeName) } } func (s *queryParserSuite) TestParsePrecision() { commonQueryPart := "WorkflowID = \"random workflowID\" AND " testCases := []struct { query string expectErr bool parsedQuery *parsedQuery }{ { query: commonQueryPart + "CloseTime = 1000 and SearchPrecision = 'Day'", expectErr: false, parsedQuery: &parsedQuery{ searchPrecision: common.StringPtr(PrecisionDay), }, }, { query: commonQueryPart + "CloseTime = 1000 and SearchPrecision = 'Hour'", expectErr: false, parsedQuery: &parsedQuery{ searchPrecision: common.StringPtr(PrecisionHour), }, }, { query: commonQueryPart + "CloseTime = 1000 and SearchPrecision = 'Minute'", expectErr: false, parsedQuery: &parsedQuery{ searchPrecision: common.StringPtr(PrecisionMinute), }, }, { query: commonQueryPart + "StartTime = 1000 and SearchPrecision = 'Second'", expectErr: false, parsedQuery: &parsedQuery{ searchPrecision: common.StringPtr(PrecisionSecond), }, }, { query: commonQueryPart + "SearchPrecision = 'Second'", expectErr: true, }, { query: commonQueryPart + "SearchPrecision = 'Invalid string'", expectErr: true, }, } for _, tc := range testCases { parsedQuery, err := s.parser.Parse(tc.query) if tc.expectErr { s.Error(err) continue } s.NoError(err) s.Equal(tc.parsedQuery.searchPrecision, parsedQuery.searchPrecision) } } func (s *queryParserSuite) TestParseCloseTime() { commonQueryPart := "WorkflowID = \"random workflowID\" AND SearchPrecision = 'Day' AND " testCases := []struct { query string expectErr bool parsedQuery *parsedQuery }{ { query: commonQueryPart + "CloseTime = 1000", expectErr: false, parsedQuery: &parsedQuery{ closeTime: common.Int64Ptr(1000), }, }, { query: commonQueryPart + "CloseTime = \"2019-01-01T11:11:11Z\"", expectErr: false, parsedQuery: &parsedQuery{ closeTime: common.Int64Ptr(1546341071000000000), }, }, { query: commonQueryPart + "closeTime = 2000", expectErr: true, }, { query: commonQueryPart + "CloseTime > \"2019-01-01 00:00:00\"", expectErr: true, }, } for _, tc := range testCases { parsedQuery, err := s.parser.Parse(tc.query) if tc.expectErr { s.Error(err) continue } s.NoError(err) s.Equal(tc.parsedQuery.closeTime, parsedQuery.closeTime) } } func (s *queryParserSuite) TestParseStartTime() { commonQueryPart := "WorkflowID = \"random workflowID\" AND SearchPrecision = 'Day' AND " testCases := []struct { query string expectErr bool parsedQuery *parsedQuery }{ { query: commonQueryPart + "StartTime = 1000", expectErr: false, parsedQuery: &parsedQuery{ startTime: common.Int64Ptr(1000), }, }, { query: commonQueryPart + "StartTime = \"2019-01-01T11:11:11Z\"", expectErr: false, parsedQuery: &parsedQuery{ startTime: common.Int64Ptr(1546341071000000000), }, }, { query: commonQueryPart + "startTime = 2000", expectErr: true, }, { query: commonQueryPart + "StartTime > \"2019-01-01 00:00:00\"", expectErr: true, }, } for _, tc := range testCases { parsedQuery, err := s.parser.Parse(tc.query) if tc.expectErr { s.Error(err) continue } s.NoError(err) s.Equal(tc.parsedQuery.closeTime, parsedQuery.closeTime) } } ================================================ FILE: common/archiver/s3store/util.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package s3store import ( "bytes" "context" "encoding/json" "fmt" "io/ioutil" "strings" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3iface" "go.uber.org/multierr" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/types" ) // encoding & decoding util func encode(v interface{}) ([]byte, error) { return json.Marshal(v) } func decodeHistoryBlob(data []byte) (*archiver.HistoryBlob, error) { historyBlob := &archiver.HistoryBlob{} err := json.Unmarshal(data, historyBlob) if err != nil { return nil, err } return historyBlob, nil } func decodeVisibilityRecord(data []byte) (*visibilityRecord, error) { record := &visibilityRecord{} err := json.Unmarshal(data, record) if err != nil { return nil, err } return record, nil } func serializeToken(token interface{}) ([]byte, error) { if token == nil { return nil, nil } return json.Marshal(token) } func deserializeGetHistoryToken(bytes []byte) (*getHistoryToken, error) { token := &getHistoryToken{} err := json.Unmarshal(bytes, token) return token, err } func deserializeQueryVisibilityToken(bytes []byte) *string { var ret = string(bytes) return &ret } func serializeQueryVisibilityToken(token string) []byte { return []byte(token) } // Only validates the scheme and buckets are passed func softValidateURI(URI archiver.URI) error { if URI.Scheme() != URIScheme { return archiver.ErrURISchemeMismatch } if len(URI.Hostname()) == 0 { return errNoBucketSpecified } return nil } func bucketExists(ctx context.Context, s3cli s3iface.S3API, URI archiver.URI) error { ctx, cancel := ensureContextTimeout(ctx) defer cancel() _, err := s3cli.HeadBucketWithContext(ctx, &s3.HeadBucketInput{ Bucket: aws.String(URI.Hostname()), }) if err == nil { return nil } if isNotFoundError(err) { return errBucketNotExists } return err } func keyExists(ctx context.Context, s3cli s3iface.S3API, URI archiver.URI, key string) (bool, error) { ctx, cancel := ensureContextTimeout(ctx) defer cancel() _, err := s3cli.HeadObjectWithContext(ctx, &s3.HeadObjectInput{ Bucket: aws.String(URI.Hostname()), Key: aws.String(key), }) if err != nil { if isNotFoundError(err) { return false, nil } return false, err } return true, nil } func isNotFoundError(err error) bool { aerr, ok := err.(awserr.Error) return ok && (aerr.Code() == "NotFound") } // Key construction func constructHistoryKey(path, domainID, workflowID, runID string, version int64, batchIdx int) string { prefix := constructHistoryKeyPrefixWithVersion(path, domainID, workflowID, runID, version) return fmt.Sprintf("%s%d", prefix, batchIdx) } func constructHistoryKeyPrefixWithVersion(path, domainID, workflowID, runID string, version int64) string { prefix := constructHistoryKeyPrefix(path, domainID, workflowID, runID) return fmt.Sprintf("%s/%v/", prefix, version) } func constructHistoryKeyPrefix(path, domainID, workflowID, runID string) string { return strings.TrimLeft(strings.Join([]string{path, domainID, "history", workflowID, runID}, "/"), "/") } func constructTimeBasedSearchKey(path, domainID, primaryIndexKey, primaryIndexValue, secondaryIndexKey string, timestamp int64, precision string) string { t := time.Unix(0, timestamp).In(time.UTC) var timeFormat = "" switch precision { case PrecisionSecond: timeFormat = ":05" fallthrough case PrecisionMinute: timeFormat = ":04" + timeFormat fallthrough case PrecisionHour: timeFormat = "15" + timeFormat fallthrough case PrecisionDay: timeFormat = "2006-01-02T" + timeFormat } return fmt.Sprintf("%s/%s", constructVisibilitySearchPrefix(path, domainID, primaryIndexKey, primaryIndexValue, secondaryIndexKey), t.Format(timeFormat)) } func constructTimestampIndex(path, domainID, primaryIndexKey, primaryIndexValue, secondaryIndexKey string, timestamp int64, runID string) string { t := time.Unix(0, timestamp).In(time.UTC) return fmt.Sprintf("%s/%s/%s", constructVisibilitySearchPrefix(path, domainID, primaryIndexKey, primaryIndexValue, secondaryIndexKey), t.Format(time.RFC3339), runID) } func constructVisibilitySearchPrefix(path, domainID, primaryIndexKey, primaryIndexValue, secondaryIndexType string) string { return strings.TrimLeft(strings.Join([]string{path, domainID, "visibility", primaryIndexKey, primaryIndexValue, secondaryIndexType}, "/"), "/") } func ensureContextTimeout(ctx context.Context) (context.Context, context.CancelFunc) { if _, ok := ctx.Deadline(); ok { return ctx, func() {} } return context.WithTimeout(ctx, defaultBlobstoreTimeout) } func upload(ctx context.Context, s3cli s3iface.S3API, URI archiver.URI, key string, data []byte) error { ctx, cancel := ensureContextTimeout(ctx) defer cancel() _, err := s3cli.PutObjectWithContext(ctx, &s3.PutObjectInput{ Bucket: aws.String(URI.Hostname()), Key: aws.String(key), Body: bytes.NewReader(data), }) if err != nil { if aerr, ok := err.(awserr.Error); ok { if aerr.Code() == s3.ErrCodeNoSuchBucket { return &types.BadRequestError{Message: errBucketNotExists.Error()} } } return err } return nil } func download(ctx context.Context, s3cli s3iface.S3API, URI archiver.URI, key string) ([]byte, error) { ctx, cancel := ensureContextTimeout(ctx) defer cancel() result, err := s3cli.GetObjectWithContext(ctx, &s3.GetObjectInput{ Bucket: aws.String(URI.Hostname()), Key: aws.String(key), }) if err != nil { if aerr, ok := err.(awserr.Error); ok { if aerr.Code() == s3.ErrCodeNoSuchBucket { return nil, &types.BadRequestError{Message: errBucketNotExists.Error()} } if aerr.Code() == s3.ErrCodeNoSuchKey { return nil, &types.EntityNotExistsError{Message: archiver.ErrHistoryNotExist.Error()} } } return nil, err } defer func() { if ierr := result.Body.Close(); ierr != nil { err = multierr.Append(err, ierr) } }() body, err := ioutil.ReadAll(result.Body) if err != nil { return nil, err } return body, nil } func contextExpired(ctx context.Context) bool { select { case <-ctx.Done(): return true default: return false } } func convertToExecutionInfo(record *visibilityRecord) *types.WorkflowExecutionInfo { return &types.WorkflowExecutionInfo{ Execution: &types.WorkflowExecution{ WorkflowID: record.WorkflowID, RunID: record.RunID, }, Type: &types.WorkflowType{ Name: record.WorkflowTypeName, }, StartTime: common.Int64Ptr(record.StartTimestamp), ExecutionTime: common.Int64Ptr(record.ExecutionTimestamp), CloseTime: common.Int64Ptr(record.CloseTimestamp), CloseStatus: record.CloseStatus.Ptr(), HistoryLength: record.HistoryLength, Memo: record.Memo, SearchAttributes: &types.SearchAttributes{ IndexedFields: archiver.ConvertSearchAttrToBytes(record.SearchAttributes), }, } } ================================================ FILE: common/archiver/s3store/visibilityArchiver.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package s3store import ( "context" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3iface" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type ( visibilityArchiver struct { container *archiver.VisibilityBootstrapContainer s3cli s3iface.S3API queryParser QueryParser } visibilityRecord archiver.ArchiveVisibilityRequest queryVisibilityRequest struct { domainID string pageSize int nextPageToken []byte parsedQuery *parsedQuery } indexToArchive struct { primaryIndex string primaryIndexValue string secondaryIndex string secondaryIndexTimestamp int64 } ) const ( errEncodeVisibilityRecord = "failed to encode visibility record" secondaryIndexKeyStartTimeout = "startTimeout" secondaryIndexKeyCloseTimeout = "closeTimeout" primaryIndexKeyWorkflowTypeName = "workflowTypeName" primaryIndexKeyWorkflowID = "workflowID" ) // NewVisibilityArchiver creates a new archiver.VisibilityArchiver based on s3 func NewVisibilityArchiver( container *archiver.VisibilityBootstrapContainer, config *config.S3Archiver, ) (archiver.VisibilityArchiver, error) { return newVisibilityArchiver(container, config) } func newVisibilityArchiver( container *archiver.VisibilityBootstrapContainer, config *config.S3Archiver) (*visibilityArchiver, error) { s3Config := &aws.Config{ Endpoint: config.Endpoint, Region: aws.String(config.Region), S3ForcePathStyle: aws.Bool(config.S3ForcePathStyle), } sess, err := session.NewSession(s3Config) if err != nil { return nil, err } return &visibilityArchiver{ container: container, s3cli: s3.New(sess), queryParser: NewQueryParser(), }, nil } func (v *visibilityArchiver) Archive( ctx context.Context, URI archiver.URI, request *archiver.ArchiveVisibilityRequest, opts ...archiver.ArchiveOption, ) (err error) { scope := v.container.MetricsClient.Scope(metrics.VisibilityArchiverScope, metrics.DomainTag(request.DomainName)) featureCatalog := archiver.GetFeatureCatalog(opts...) sw := scope.StartTimer(metrics.CadenceLatency) logger := archiver.TagLoggerWithArchiveVisibilityRequestAndURI(v.container.Logger, request, URI.String()) archiveFailReason := "" defer func() { sw.Stop() if err != nil { if isRetryableError(err) { scope.IncCounter(metrics.VisibilityArchiverArchiveTransientErrorCount) logger.Error(archiver.ArchiveTransientErrorMsg, tag.ArchivalArchiveFailReason(archiveFailReason), tag.Error(err)) } else { scope.IncCounter(metrics.VisibilityArchiverArchiveNonRetryableErrorCount) logger.Error(archiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason(archiveFailReason), tag.Error(err)) if featureCatalog.NonRetriableError != nil { err = featureCatalog.NonRetriableError() } } } }() if err := softValidateURI(URI); err != nil { archiveFailReason = archiver.ErrReasonInvalidURI return err } if err := archiver.ValidateVisibilityArchivalRequest(request); err != nil { archiveFailReason = archiver.ErrReasonInvalidArchiveRequest return err } encodedVisibilityRecord, err := encode(request) if err != nil { archiveFailReason = errEncodeVisibilityRecord return err } indexes := createIndexesToArchive(request) // Upload archive to all indexes for _, element := range indexes { key := constructTimestampIndex(URI.Path(), request.DomainID, element.primaryIndex, element.primaryIndexValue, element.secondaryIndex, element.secondaryIndexTimestamp, request.RunID) if err := upload(ctx, v.s3cli, URI, key, encodedVisibilityRecord); err != nil { archiveFailReason = errWriteKey return err } } scope.IncCounter(metrics.VisibilityArchiveSuccessCount) return nil } func createIndexesToArchive(request *archiver.ArchiveVisibilityRequest) []indexToArchive { return []indexToArchive{ {primaryIndexKeyWorkflowTypeName, request.WorkflowTypeName, secondaryIndexKeyCloseTimeout, request.CloseTimestamp}, {primaryIndexKeyWorkflowTypeName, request.WorkflowTypeName, secondaryIndexKeyStartTimeout, request.StartTimestamp}, {primaryIndexKeyWorkflowID, request.WorkflowID, secondaryIndexKeyCloseTimeout, request.CloseTimestamp}, {primaryIndexKeyWorkflowID, request.WorkflowID, secondaryIndexKeyStartTimeout, request.StartTimestamp}, } } func (v *visibilityArchiver) Query( ctx context.Context, URI archiver.URI, request *archiver.QueryVisibilityRequest, ) (*archiver.QueryVisibilityResponse, error) { if err := softValidateURI(URI); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidURI.Error()} } if err := archiver.ValidateQueryRequest(request); err != nil { return nil, &types.BadRequestError{Message: archiver.ErrInvalidQueryVisibilityRequest.Error()} } parsedQuery, err := v.queryParser.Parse(request.Query) if err != nil { return nil, &types.BadRequestError{Message: err.Error()} } return v.query(ctx, URI, &queryVisibilityRequest{ domainID: request.DomainID, pageSize: request.PageSize, nextPageToken: request.NextPageToken, parsedQuery: parsedQuery, }) } func (v *visibilityArchiver) query( ctx context.Context, URI archiver.URI, request *queryVisibilityRequest, ) (*archiver.QueryVisibilityResponse, error) { ctx, cancel := ensureContextTimeout(ctx) defer cancel() var token *string if request.nextPageToken != nil { token = deserializeQueryVisibilityToken(request.nextPageToken) } primaryIndex := primaryIndexKeyWorkflowTypeName primaryIndexValue := request.parsedQuery.workflowTypeName if request.parsedQuery.workflowID != nil { primaryIndex = primaryIndexKeyWorkflowID primaryIndexValue = request.parsedQuery.workflowID } var prefix = constructVisibilitySearchPrefix(URI.Path(), request.domainID, primaryIndex, *primaryIndexValue, secondaryIndexKeyCloseTimeout) + "/" if request.parsedQuery.closeTime != nil { prefix = constructTimeBasedSearchKey(URI.Path(), request.domainID, primaryIndex, *primaryIndexValue, secondaryIndexKeyCloseTimeout, *request.parsedQuery.closeTime, *request.parsedQuery.searchPrecision) } if request.parsedQuery.startTime != nil { prefix = constructTimeBasedSearchKey(URI.Path(), request.domainID, primaryIndex, *primaryIndexValue, secondaryIndexKeyStartTimeout, *request.parsedQuery.startTime, *request.parsedQuery.searchPrecision) } results, err := v.s3cli.ListObjectsV2WithContext(ctx, &s3.ListObjectsV2Input{ Bucket: aws.String(URI.Hostname()), Prefix: aws.String(prefix), MaxKeys: aws.Int64(int64(request.pageSize)), ContinuationToken: token, }) if err != nil { if isRetryableError(err) { return nil, &types.InternalServiceError{Message: err.Error()} } return nil, &types.BadRequestError{Message: err.Error()} } if len(results.Contents) == 0 { return &archiver.QueryVisibilityResponse{}, nil } response := &archiver.QueryVisibilityResponse{} if *results.IsTruncated { response.NextPageToken = serializeQueryVisibilityToken(*results.NextContinuationToken) } for _, item := range results.Contents { encodedRecord, err := download(ctx, v.s3cli, URI, *item.Key) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } record, err := decodeVisibilityRecord(encodedRecord) if err != nil { return nil, &types.InternalServiceError{Message: err.Error()} } response.Executions = append(response.Executions, convertToExecutionInfo(record)) } return response, nil } func (v *visibilityArchiver) ValidateURI(URI archiver.URI) error { err := softValidateURI(URI) if err != nil { return err } return bucketExists(context.TODO(), v.s3cli, URI) } ================================================ FILE: common/archiver/s3store/visibilityArchiver_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package s3store import ( "context" "encoding/json" "errors" "fmt" "testing" "time" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/s3" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/s3store/mocks" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type visibilityArchiverSuite struct { *require.Assertions suite.Suite s3cli *mocks.S3API container *archiver.VisibilityBootstrapContainer visibilityRecords []*visibilityRecord controller *gomock.Controller testArchivalURI archiver.URI } func TestVisibilityArchiverSuite(t *testing.T) { suite.Run(t, new(visibilityArchiverSuite)) } func (s *visibilityArchiverSuite) TestValidateURI() { testCases := []struct { URI string expectedErr error }{ { URI: "wrongscheme:///a/b/c", expectedErr: archiver.ErrURISchemeMismatch, }, { URI: "s3://", expectedErr: errNoBucketSpecified, }, { URI: "s3:///test", expectedErr: errNoBucketSpecified, }, { URI: "s3://bucket/a/b/c", expectedErr: errBucketNotExists, }, { URI: testBucketURI, expectedErr: nil, }, } s.s3cli.On("HeadBucketWithContext", mock.Anything, mock.MatchedBy(func(input *s3.HeadBucketInput) bool { return *input.Bucket != s.testArchivalURI.Hostname() })).Return(nil, awserr.New("NotFound", "", nil)) s.s3cli.On("HeadBucketWithContext", mock.Anything, mock.Anything).Return(&s3.HeadBucketOutput{}, nil) visibilityArchiver := s.newTestVisibilityArchiver() for _, tc := range testCases { URI, err := archiver.NewURI(tc.URI) s.NoError(err) s.Equal(tc.expectedErr, visibilityArchiver.ValidateURI(URI)) } } func (s *visibilityArchiverSuite) newTestVisibilityArchiver() *visibilityArchiver { archiver := &visibilityArchiver{ container: s.container, s3cli: s.s3cli, queryParser: NewQueryParser(), } return archiver } const ( testWorkflowTypeName = "test-workflow-type" ) func (s *visibilityArchiverSuite) SetupSuite() { var err error scope := tally.NewTestScope("test", nil) s.s3cli = &mocks.S3API{} setupFsEmulation(s.s3cli) s.testArchivalURI, err = archiver.NewURI(testBucketURI) s.Require().NoError(err) s.container = &archiver.VisibilityBootstrapContainer{ Logger: testlogger.New(s.T()), MetricsClient: metrics.NewClient(scope, metrics.History, metrics.HistogramMigration{}), } s.setupVisibilityDirectory() } func (s *visibilityArchiverSuite) TearDownSuite() { } func (s *visibilityArchiverSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) } func (s *visibilityArchiverSuite) TearDownTest() { s.controller.Finish() } func (s *visibilityArchiverSuite) TestArchive_Fail_InvalidURI() { visibilityArchiver := s.newTestVisibilityArchiver() URI, err := archiver.NewURI("wrongscheme://") s.NoError(err) request := &archiver.ArchiveVisibilityRequest{ DomainName: testDomainName, DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, StartTimestamp: time.Now().UnixNano(), ExecutionTimestamp: 0, // workflow without backoff CloseTimestamp: time.Now().UnixNano(), CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: int64(101), } err = visibilityArchiver.Archive(context.Background(), URI, request) s.Error(err) } func (s *visibilityArchiverSuite) TestArchive_Fail_InvalidRequest() { visibilityArchiver := s.newTestVisibilityArchiver() err := visibilityArchiver.Archive(context.Background(), s.testArchivalURI, &archiver.ArchiveVisibilityRequest{}) s.Error(err) } func (s *visibilityArchiverSuite) TestArchive_Fail_NonRetriableErrorOption() { visibilityArchiver := s.newTestVisibilityArchiver() nonRetriableErr := errors.New("some non-retryable error") err := visibilityArchiver.Archive( context.Background(), s.testArchivalURI, &archiver.ArchiveVisibilityRequest{ DomainID: testDomainID, }, archiver.GetNonRetriableErrorOption(nonRetriableErr), ) s.Equal(nonRetriableErr, err) } func (s *visibilityArchiverSuite) TestArchive_Success() { visibilityArchiver := s.newTestVisibilityArchiver() closeTimestamp := time.Now() request := &archiver.ArchiveVisibilityRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, StartTimestamp: closeTimestamp.Add(-time.Hour).UnixNano(), ExecutionTimestamp: 0, // workflow without backoff CloseTimestamp: closeTimestamp.UnixNano(), CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: int64(101), Memo: &types.Memo{ Fields: map[string][]byte{ "testFields": {1, 2, 3}, }, }, SearchAttributes: map[string]string{ "testAttribute": "456", }, } URI, err := archiver.NewURI(testBucketURI + "/test-archive-success") s.NoError(err) err = visibilityArchiver.Archive(context.Background(), URI, request) s.NoError(err) expectedKey := constructTimestampIndex(URI.Path(), testDomainID, primaryIndexKeyWorkflowID, testWorkflowID, secondaryIndexKeyCloseTimeout, closeTimestamp.UnixNano(), testRunID) data, err := download(context.Background(), visibilityArchiver.s3cli, URI, expectedKey) s.NoError(err, expectedKey) archivedRecord := &archiver.ArchiveVisibilityRequest{} err = json.Unmarshal(data, archivedRecord) s.NoError(err) s.Equal(request, archivedRecord) } func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidURI() { visibilityArchiver := s.newTestVisibilityArchiver() URI, err := archiver.NewURI("wrongscheme://") s.NoError(err) request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 1, } response, err := visibilityArchiver.Query(context.Background(), URI, request) s.Error(err) s.Nil(response) } func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidRequest() { visibilityArchiver := s.newTestVisibilityArchiver() response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, &archiver.QueryVisibilityRequest{}) s.Error(err) s.Nil(response) } func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidQuery() { visibilityArchiver := s.newTestVisibilityArchiver() mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(nil, errors.New("invalid query")) visibilityArchiver.queryParser = mockParser response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, &archiver.QueryVisibilityRequest{ DomainID: "some random domainID", PageSize: 10, Query: "some invalid query", }) s.Error(err) s.Nil(response) } func (s *visibilityArchiverSuite) TestQuery_Success_DirectoryNotExist() { visibilityArchiver := s.newTestVisibilityArchiver() mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ workflowID: common.StringPtr(testWorkflowID), closeTime: common.Int64Ptr(0), searchPrecision: common.StringPtr(PrecisionSecond), }, nil) visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, Query: "parsed by mockParser", PageSize: 1, } response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, request) s.NoError(err) s.NotNil(response) s.Empty(response.Executions) s.Empty(response.NextPageToken) } func (s *visibilityArchiverSuite) TestQuery_Success_NoNextPageToken() { visibilityArchiver := s.newTestVisibilityArchiver() mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ closeTime: common.Int64Ptr(int64(1 * time.Hour)), searchPrecision: common.StringPtr(PrecisionHour), workflowID: common.StringPtr(testWorkflowID), }, nil) visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 10, Query: "parsed by mockParser", } URI, err := archiver.NewURI(testBucketURI) s.NoError(err) response, err := visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.Nil(response.NextPageToken) s.Len(response.Executions, 2) s.Equal(convertToExecutionInfo(s.visibilityRecords[0]), response.Executions[0]) } func (s *visibilityArchiverSuite) TestQuery_Success_SmallPageSize() { visibilityArchiver := s.newTestVisibilityArchiver() mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ closeTime: common.Int64Ptr(0), searchPrecision: common.StringPtr(PrecisionDay), workflowID: common.StringPtr(testWorkflowID), }, nil).AnyTimes() visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 2, Query: "parsed by mockParser", } URI, err := archiver.NewURI(testBucketURI) s.NoError(err) response, err := visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.NotNil(response.NextPageToken) s.Len(response.Executions, 2) s.Equal(convertToExecutionInfo(s.visibilityRecords[0]), response.Executions[0]) s.Equal(convertToExecutionInfo(s.visibilityRecords[1]), response.Executions[1]) request.NextPageToken = response.NextPageToken response, err = visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.Nil(response.NextPageToken) s.Len(response.Executions, 1) s.Equal(convertToExecutionInfo(s.visibilityRecords[2]), response.Executions[0]) } type precisionTest struct { day int64 hour int64 minute int64 second int64 precision string } func (s *visibilityArchiverSuite) TestArchiveAndQueryPrecisions() { precisionTests := []*precisionTest{ { day: 1, hour: 0, minute: 0, second: 0, precision: PrecisionDay, }, { day: 1, hour: 1, minute: 0, second: 0, precision: PrecisionDay, }, { day: 2, hour: 1, minute: 0, second: 0, precision: PrecisionHour, }, { day: 2, hour: 1, minute: 30, second: 0, precision: PrecisionHour, }, { day: 3, hour: 2, minute: 1, second: 0, precision: PrecisionMinute, }, { day: 3, hour: 2, minute: 1, second: 30, precision: PrecisionMinute, }, { day: 4, hour: 3, minute: 2, second: 1, precision: PrecisionSecond, }, { day: 4, hour: 3, minute: 2, second: 1, precision: PrecisionSecond, }, { day: 4, hour: 3, minute: 2, second: 2, precision: PrecisionSecond, }, { day: 4, hour: 3, minute: 2, second: 2, precision: PrecisionSecond, }, } visibilityArchiver := s.newTestVisibilityArchiver() URI, err := archiver.NewURI(testBucketURI + "/archive-and-query-precision") s.NoError(err) for i, testData := range precisionTests { record := archiver.ArchiveVisibilityRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: fmt.Sprintf("%s-%d", testRunID, i), WorkflowTypeName: testWorkflowTypeName, StartTimestamp: testData.day*int64(time.Hour)*24 + testData.hour*int64(time.Hour) + testData.minute*int64(time.Minute) + testData.second*int64(time.Second), CloseTimestamp: (testData.day+30)*int64(time.Hour)*24 + testData.hour*int64(time.Hour) + testData.minute*int64(time.Minute) + testData.second*int64(time.Second), CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: 101, } err := visibilityArchiver.Archive(context.Background(), URI, &record) s.NoError(err) } request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 100, Query: "parsed by mockParser", } for i, testData := range precisionTests { mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ closeTime: common.Int64Ptr((testData.day+30)*int64(time.Hour)*24 + testData.hour*int64(time.Hour) + testData.minute*int64(time.Minute) + testData.second*int64(time.Second)), searchPrecision: common.StringPtr(testData.precision), workflowID: common.StringPtr(testWorkflowID), }, nil).AnyTimes() visibilityArchiver.queryParser = mockParser response, err := visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.Len(response.Executions, 2, "Iteration ", i) mockParser = NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ startTime: common.Int64Ptr((testData.day)*int64(time.Hour)*24 + testData.hour*int64(time.Hour) + testData.minute*int64(time.Minute) + testData.second*int64(time.Second)), searchPrecision: common.StringPtr(testData.precision), workflowID: common.StringPtr(testWorkflowID), }, nil).AnyTimes() visibilityArchiver.queryParser = mockParser response, err = visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.Len(response.Executions, 2, "Iteration ", i) mockParser = NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ closeTime: common.Int64Ptr((testData.day+30)*int64(time.Hour)*24 + testData.hour*int64(time.Hour) + testData.minute*int64(time.Minute) + testData.second*int64(time.Second)), searchPrecision: common.StringPtr(testData.precision), workflowTypeName: common.StringPtr(testWorkflowTypeName), }, nil).AnyTimes() visibilityArchiver.queryParser = mockParser response, err = visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.Len(response.Executions, 2, "Iteration ", i) mockParser = NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ startTime: common.Int64Ptr((testData.day)*int64(time.Hour)*24 + testData.hour*int64(time.Hour) + testData.minute*int64(time.Minute) + testData.second*int64(time.Second)), searchPrecision: common.StringPtr(testData.precision), workflowTypeName: common.StringPtr(testWorkflowTypeName), }, nil).AnyTimes() visibilityArchiver.queryParser = mockParser response, err = visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) s.Len(response.Executions, 2, "Iteration ", i) } } func (s *visibilityArchiverSuite) TestArchiveAndQuery() { visibilityArchiver := s.newTestVisibilityArchiver() URI, err := archiver.NewURI(testBucketURI + "/archive-and-query") s.NoError(err) for _, record := range s.visibilityRecords { err := visibilityArchiver.Archive(context.Background(), URI, (*archiver.ArchiveVisibilityRequest)(record)) s.NoError(err) } mockParser := NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ workflowID: common.StringPtr(testWorkflowID), }, nil).AnyTimes() visibilityArchiver.queryParser = mockParser request := &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 1, Query: "parsed by mockParser", } executions := []*types.WorkflowExecutionInfo{} var first = true for first || request.NextPageToken != nil { response, err := visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) executions = append(executions, response.Executions...) request.NextPageToken = response.NextPageToken first = false } s.Len(executions, 3) s.Equal(convertToExecutionInfo(s.visibilityRecords[0]), executions[0]) s.Equal(convertToExecutionInfo(s.visibilityRecords[1]), executions[1]) s.Equal(convertToExecutionInfo(s.visibilityRecords[2]), executions[2]) mockParser = NewMockQueryParser(s.controller) mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{ workflowTypeName: common.StringPtr(testWorkflowTypeName), }, nil).AnyTimes() visibilityArchiver.queryParser = mockParser request = &archiver.QueryVisibilityRequest{ DomainID: testDomainID, PageSize: 1, Query: "parsed by mockParser", } executions = []*types.WorkflowExecutionInfo{} first = true for first || request.NextPageToken != nil { response, err := visibilityArchiver.Query(context.Background(), URI, request) s.NoError(err) s.NotNil(response) executions = append(executions, response.Executions...) request.NextPageToken = response.NextPageToken first = false } s.Len(executions, 3) s.Equal(convertToExecutionInfo(s.visibilityRecords[0]), executions[0]) s.Equal(convertToExecutionInfo(s.visibilityRecords[1]), executions[1]) s.Equal(convertToExecutionInfo(s.visibilityRecords[2]), executions[2]) } func (s *visibilityArchiverSuite) setupVisibilityDirectory() { s.visibilityRecords = []*visibilityRecord{ { DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, StartTimestamp: 1, CloseTimestamp: int64(1 * time.Hour), CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: 101, }, { DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID + "1", WorkflowTypeName: testWorkflowTypeName, StartTimestamp: 1, CloseTimestamp: int64(1*time.Hour + 30*time.Minute), CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: 101, }, { DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID + "1", WorkflowTypeName: testWorkflowTypeName, StartTimestamp: 1, CloseTimestamp: int64(3 * time.Hour), CloseStatus: types.WorkflowExecutionCloseStatusFailed, HistoryLength: 101, }, } visibilityArchiver := s.newTestVisibilityArchiver() for _, record := range s.visibilityRecords { s.writeVisibilityRecordForQueryTest(visibilityArchiver, record) } } func (s *visibilityArchiverSuite) writeVisibilityRecordForQueryTest(visibilityArchiver *visibilityArchiver, record *visibilityRecord) { err := visibilityArchiver.Archive(context.Background(), s.testArchivalURI, (*archiver.ArchiveVisibilityRequest)(record)) s.Require().NoError(err) } ================================================ FILE: common/archiver/util.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) var ( errEmptyDomainID = errors.New("DomainID is empty") errEmptyDomainName = errors.New("DomainName is empty") errEmptyWorkflowID = errors.New("WorkflowID is empty") errEmptyRunID = errors.New("RunID is empty") errInvalidPageSize = errors.New("PageSize should be greater than 0") errEmptyWorkflowTypeName = errors.New("WorkflowTypeName is empty") errEmptyStartTime = errors.New("StartTimestamp is empty") errEmptyCloseTime = errors.New("CloseTimestamp is empty") errEmptyQuery = errors.New("Query string is empty") ) // TagLoggerWithArchiveHistoryRequestAndURI tags logger with fields in the archive history request and the URI func TagLoggerWithArchiveHistoryRequestAndURI(logger log.Logger, request *ArchiveHistoryRequest, URI string) log.Logger { return logger.WithTags( tag.ShardID(request.ShardID), tag.ArchivalRequestDomainID(request.DomainID), tag.ArchivalRequestDomainName(request.DomainName), tag.ArchivalRequestWorkflowID(request.WorkflowID), tag.ArchivalRequestRunID(request.RunID), tag.ArchivalRequestBranchToken(request.BranchToken), tag.ArchivalRequestNextEventID(request.NextEventID), tag.ArchivalRequestCloseFailoverVersion(request.CloseFailoverVersion), tag.ArchivalURI(URI), ) } // TagLoggerWithArchiveVisibilityRequestAndURI tags logger with fields in the archive visibility request and the URI func TagLoggerWithArchiveVisibilityRequestAndURI(logger log.Logger, request *ArchiveVisibilityRequest, URI string) log.Logger { return logger.WithTags( tag.ArchivalRequestDomainID(request.DomainID), tag.ArchivalRequestDomainName(request.DomainName), tag.ArchivalRequestWorkflowID(request.WorkflowID), tag.ArchivalRequestRunID(request.RunID), tag.ArchvialRequestWorkflowType(request.WorkflowTypeName), tag.ArchivalRequestCloseTimestamp(request.CloseTimestamp), tag.ArchivalRequestCloseStatus(request.CloseStatus.String()), tag.ArchivalURI(URI), ) } // ValidateHistoryArchiveRequest validates the archive history request func ValidateHistoryArchiveRequest(request *ArchiveHistoryRequest) error { if request.DomainID == "" { return errEmptyDomainID } if request.WorkflowID == "" { return errEmptyWorkflowID } if request.RunID == "" { return errEmptyRunID } if request.DomainName == "" { return errEmptyDomainName } return nil } // ValidateGetRequest validates the get archived history request func ValidateGetRequest(request *GetHistoryRequest) error { if request.DomainID == "" { return errEmptyDomainID } if request.WorkflowID == "" { return errEmptyWorkflowID } if request.RunID == "" { return errEmptyRunID } if request.PageSize == 0 { return errInvalidPageSize } return nil } // ValidateVisibilityArchivalRequest validates the archive visibility request func ValidateVisibilityArchivalRequest(request *ArchiveVisibilityRequest) error { if request.DomainID == "" { return errEmptyDomainID } if request.DomainName == "" { return errEmptyDomainName } if request.WorkflowID == "" { return errEmptyWorkflowID } if request.RunID == "" { return errEmptyRunID } if request.WorkflowTypeName == "" { return errEmptyWorkflowTypeName } if request.StartTimestamp == 0 { return errEmptyStartTime } if request.CloseTimestamp == 0 { return errEmptyCloseTime } return nil } // ValidateQueryRequest validates the query visibility request func ValidateQueryRequest(request *QueryVisibilityRequest) error { if request.DomainID == "" { return errEmptyDomainID } if request.PageSize == 0 { return errInvalidPageSize } if request.Query == "" { return errEmptyQuery } return nil } // ConvertSearchAttrToBytes converts search attribute value from string back to byte array func ConvertSearchAttrToBytes(searchAttrStr map[string]string) map[string][]byte { searchAttr := make(map[string][]byte) for k, v := range searchAttrStr { searchAttr[k] = []byte(v) } return searchAttr } func IsHistoryMutated(request *ArchiveHistoryRequest, historyBatches []*types.History, isLast bool, logger log.Logger) (mutated bool) { lastBatch := historyBatches[len(historyBatches)-1].Events lastEvent := lastBatch[len(lastBatch)-1] lastFailoverVersion := lastEvent.Version defer func() { if mutated { logger.Warn(ArchiveNonRetriableErrorMsg+":history is mutated when during archival", tag.ArchivalArchiveFailReason(ErrReasonHistoryMutated), tag.FailoverVersion(lastFailoverVersion), tag.TokenLastEventID(lastEvent.ID)) } }() if lastFailoverVersion > request.CloseFailoverVersion { return true } if !isLast { return false } lastEventID := lastEvent.ID return lastFailoverVersion != request.CloseFailoverVersion || lastEventID+1 != request.NextEventID } ================================================ FILE: common/archiver/util_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) type UtilSuite struct { *require.Assertions suite.Suite } func TestUtilSuite(t *testing.T) { suite.Run(t, new(UtilSuite)) } func (s *UtilSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *UtilSuite) TestHistoryMutated() { testCases := []struct { historyBatches []*types.History request *ArchiveHistoryRequest isLast bool isMutated bool }{ { historyBatches: []*types.History{ { Events: []*types.HistoryEvent{ { Version: 15, }, }, }, }, request: &ArchiveHistoryRequest{ CloseFailoverVersion: 3, }, isMutated: true, }, { historyBatches: []*types.History{ { Events: []*types.HistoryEvent{ { ID: 33, Version: 10, }, }, }, { Events: []*types.HistoryEvent{ { ID: 49, Version: 10, }, { ID: 50, Version: 10, }, }, }, }, request: &ArchiveHistoryRequest{ CloseFailoverVersion: 10, NextEventID: 34, }, isLast: true, isMutated: true, }, { historyBatches: []*types.History{ { Events: []*types.HistoryEvent{ { Version: 9, }, }, }, }, request: &ArchiveHistoryRequest{ CloseFailoverVersion: 10, }, isLast: true, isMutated: true, }, { historyBatches: []*types.History{ { Events: []*types.HistoryEvent{ { ID: 20, Version: 10, }, }, }, { Events: []*types.HistoryEvent{ { ID: 33, Version: 10, }, }, }, }, request: &ArchiveHistoryRequest{ CloseFailoverVersion: 10, NextEventID: 34, }, isLast: true, isMutated: false, }, } for _, tc := range testCases { s.Equal(tc.isMutated, IsHistoryMutated(tc.request, tc.historyBatches, tc.isLast, testlogger.New(s.T()))) } } ================================================ FILE: common/asyncworkflow/queue/consumer/default_consumer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package consumer import ( "context" "errors" "fmt" "sort" "sync" "time" "go.uber.org/yarpc" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/.gen/go/sqlblobs" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) const ( defaultShutdownTimeout = 5 * time.Second defaultStartWFTimeout = 3 * time.Second defaultConcurrency = 100 ) type DefaultConsumer struct { queueID string innerConsumer messaging.Consumer logger log.Logger scope metrics.Scope frontendClient frontend.Client ctx context.Context cancelFn context.CancelFunc wg sync.WaitGroup shutdownTimeout time.Duration startWFTimeout time.Duration msgDecoder codec.BinaryEncoder concurrency int } type Option func(*DefaultConsumer) func WithConcurrency(concurrency int) Option { return func(c *DefaultConsumer) { c.concurrency = concurrency } } func New( queueID string, innerConsumer messaging.Consumer, logger log.Logger, metricsClient metrics.Client, frontendClient frontend.Client, options ...Option, ) *DefaultConsumer { ctx, cancelFn := context.WithCancel(context.Background()) c := &DefaultConsumer{ queueID: queueID, innerConsumer: innerConsumer, logger: logger.WithTags(tag.AsyncWFQueueID(queueID)), scope: metricsClient.Scope(metrics.AsyncWorkflowConsumerScope), frontendClient: frontendClient, ctx: ctx, cancelFn: cancelFn, shutdownTimeout: defaultShutdownTimeout, startWFTimeout: defaultStartWFTimeout, msgDecoder: codec.NewThriftRWEncoder(), concurrency: defaultConcurrency, } for _, opt := range options { opt(c) } return c } func (c *DefaultConsumer) Start() error { if err := c.innerConsumer.Start(); err != nil { return err } for i := 0; i < c.concurrency; i++ { c.wg.Add(1) go c.runProcessLoop() c.logger.Info("Started process loop", tag.Counter(i)) } c.logger.Info("Started consumer", tag.Dynamic("concurrency", c.concurrency)) return nil } func (c *DefaultConsumer) Stop() { c.logger.Info("Stopping consumer") c.cancelFn() c.wg.Wait() if !common.AwaitWaitGroup(&c.wg, c.shutdownTimeout) { c.logger.Warn("Consumer timed out on shutdown", tag.Dynamic("timeout", c.shutdownTimeout)) return } c.innerConsumer.Stop() c.logger.Info("Stopped consumer") } func (c *DefaultConsumer) runProcessLoop() { defer c.wg.Done() for { select { case msg, ok := <-c.innerConsumer.Messages(): if !ok { c.logger.Info("Consumer channel closed") return } c.processMessage(msg) case <-c.ctx.Done(): c.logger.Info("Consumer context done so terminating loop") return } } } func (c *DefaultConsumer) processMessage(msg messaging.Message) { logger := c.logger.WithTags(tag.Dynamic("partition", msg.Partition()), tag.Dynamic("offset", msg.Offset())) logger.Debug("Received message") sw := c.scope.StartTimer(metrics.AsyncWorkflowProcessMsgLatency) defer sw.Stop() var request sqlblobs.AsyncRequestMessage if err := c.msgDecoder.Decode(msg.Value(), &request); err != nil { logger.Error("Failed to decode message", tag.Error(err)) c.scope.IncCounter(metrics.AsyncWorkflowFailureCorruptMsgCount) if err := msg.Nack(); err != nil { logger.Error("Failed to nack message", tag.Error(err)) } return } logTags, err := c.processRequest(logger, &request) if err != nil { logger.Error("Failed to process message", append(logTags, tag.Error(err))...) if nackErr := msg.Nack(); nackErr != nil { logger.Error("Failed to nack message", append(logTags, tag.Dynamic("original-error", err.Error()), tag.Error(nackErr))...) } return } logger = logger.WithTags(logTags...) if err := msg.Ack(); err != nil { logger.Error("Failed to ack message", tag.Error(err)) } logger.Info("Processed message successfully") } func (c *DefaultConsumer) processRequest(logger log.Logger, request *sqlblobs.AsyncRequestMessage) ([]tag.Tag, error) { requestType := request.GetType().String() scope := c.scope.Tagged(metrics.AsyncWFRequestTypeTag(requestType)) logTags := []tag.Tag{tag.AsyncWFRequestType(requestType)} switch request.GetType() { case sqlblobs.AsyncRequestTypeStartWorkflowExecutionAsyncRequest: startWFReq, err := c.decodeStartWorkflowRequest(request.GetPayload(), request.GetEncoding()) if err != nil { scope.IncCounter(metrics.AsyncWorkflowFailureCorruptMsgCount) return logTags, err } yarpcCallOpts := getYARPCOptions(request.GetHeader()) scope := scope.Tagged(metrics.DomainTag(startWFReq.GetDomain())) logTags = append(logTags, tag.WorkflowDomainName(startWFReq.GetDomain()), tag.WorkflowID(startWFReq.GetWorkflowID())) var resp *types.StartWorkflowExecutionResponse op := func(ctx1 context.Context) error { ctx, cancel := context.WithTimeout(ctx1, c.startWFTimeout) defer cancel() resp, err = c.frontendClient.StartWorkflowExecution(ctx, startWFReq, yarpcCallOpts...) var startedError *types.WorkflowExecutionAlreadyStartedError if errors.As(err, &startedError) { logger.Info("Received WorkflowExecutionAlreadyStartedError, treating it as a success", tag.WorkflowID(startWFReq.GetWorkflowID()), tag.WorkflowRunID(startedError.RunID)) return nil } return err } if err := callFrontendWithRetries(c.ctx, op); err != nil { scope.IncCounter(metrics.AsyncWorkflowFailureByFrontendCount) return logTags, fmt.Errorf("start workflow execution failed after all attempts: %w", err) } logTags = append(logTags, tag.WorkflowRunID(resp.GetRunID())) scope.IncCounter(metrics.AsyncWorkflowSuccessCount) case sqlblobs.AsyncRequestTypeSignalWithStartWorkflowExecutionAsyncRequest: startWFReq, err := c.decodeSignalWithStartWorkflowRequest(request.GetPayload(), request.GetEncoding()) if err != nil { c.scope.IncCounter(metrics.AsyncWorkflowFailureCorruptMsgCount) return logTags, err } yarpcCallOpts := getYARPCOptions(request.GetHeader()) scope := c.scope.Tagged(metrics.DomainTag(startWFReq.GetDomain())) logTags = append(logTags, tag.WorkflowDomainName(startWFReq.GetDomain()), tag.WorkflowID(startWFReq.GetWorkflowID())) var resp *types.StartWorkflowExecutionResponse op := func(ctx1 context.Context) error { ctx, cancel := context.WithTimeout(ctx1, c.startWFTimeout) defer cancel() resp, err = c.frontendClient.SignalWithStartWorkflowExecution(ctx, startWFReq, yarpcCallOpts...) var startedError *types.WorkflowExecutionAlreadyStartedError if errors.As(err, &startedError) { logger.Info("Received WorkflowExecutionAlreadyStartedError, treating it as a success", tag.WorkflowID(startWFReq.GetWorkflowID()), tag.WorkflowRunID(startedError.RunID)) return nil } return err } if err := callFrontendWithRetries(c.ctx, op); err != nil { scope.IncCounter(metrics.AsyncWorkflowFailureByFrontendCount) return logTags, fmt.Errorf("signal with start workflow execution failed after all attempts: %w", err) } scope.IncCounter(metrics.AsyncWorkflowSuccessCount) logTags = append(logTags, tag.WorkflowRunID(resp.GetRunID())) default: c.scope.IncCounter(metrics.AsyncWorkflowFailureCorruptMsgCount) return logTags, &UnsupportedRequestType{Type: request.GetType()} } return logTags, nil } func callFrontendWithRetries(ctx context.Context, op func(ctx context.Context) error) error { throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(common.CreateFrontendServiceRetryPolicy()), backoff.WithRetryableError(common.IsServiceTransientError), ) return throttleRetry.Do(ctx, op) } func getYARPCOptions(header *shared.Header) []yarpc.CallOption { if header == nil || header.GetFields() == nil { return nil } // sort the header fields to make the tests deterministic fields := header.GetFields() sortedKeys := make([]string, 0, len(fields)) for k := range fields { sortedKeys = append(sortedKeys, k) } sort.Strings(sortedKeys) var opts []yarpc.CallOption for _, k := range sortedKeys { opts = append(opts, yarpc.WithHeader(k, string(fields[k]))) } return opts } func (c *DefaultConsumer) decodeStartWorkflowRequest(payload []byte, encoding string) (*types.StartWorkflowExecutionRequest, error) { if encoding != string(constants.EncodingTypeThriftRW) { return nil, &UnsupportedEncoding{EncodingType: encoding} } var thriftObj shared.StartWorkflowExecutionAsyncRequest if err := c.msgDecoder.Decode(payload, &thriftObj); err != nil { return nil, err } startRequest := thrift.ToStartWorkflowExecutionAsyncRequest(&thriftObj) return startRequest.StartWorkflowExecutionRequest, nil } func (c *DefaultConsumer) decodeSignalWithStartWorkflowRequest(payload []byte, encoding string) (*types.SignalWithStartWorkflowExecutionRequest, error) { if encoding != string(constants.EncodingTypeThriftRW) { return nil, &UnsupportedEncoding{EncodingType: encoding} } var thriftObj shared.SignalWithStartWorkflowExecutionAsyncRequest if err := c.msgDecoder.Decode(payload, &thriftObj); err != nil { return nil, err } signalWithStartRequest := thrift.ToSignalWithStartWorkflowExecutionAsyncRequest(&thriftObj) return signalWithStartRequest.SignalWithStartWorkflowExecutionRequest, nil } ================================================ FILE: common/asyncworkflow/queue/consumer/default_consumer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package consumer import ( "errors" "testing" "github.com/google/go-cmp/cmp" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/.gen/go/sqlblobs" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) var ( testSignalWithStartAsyncReq = &types.SignalWithStartWorkflowExecutionAsyncRequest{ SignalWithStartWorkflowExecutionRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{Name: "test-workflow-type"}, Input: []byte("test-input"), SignalName: "test-signal-name", }, } testStartReq = &types.StartWorkflowExecutionAsyncRequest{ StartWorkflowExecutionRequest: &types.StartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{Name: "test-workflow-type"}, Input: []byte("test-input"), }, } ) type fakeMessageConsumer struct { // input failToStart bool ch chan messaging.Message // output stopped bool } func (f *fakeMessageConsumer) Start() error { if f.failToStart { return errors.New("failed to start") } return nil } func (f *fakeMessageConsumer) Stop() { f.stopped = true } func (f *fakeMessageConsumer) Messages() <-chan messaging.Message { return f.ch } type fakeMessage struct { // input val []byte wantAck bool // output acked bool nacked bool } func (m *fakeMessage) Value() []byte { return m.val } func (m *fakeMessage) Partition() int32 { return 0 } func (m *fakeMessage) Offset() int64 { return 0 } func (m *fakeMessage) Ack() error { m.acked = true return nil } func (m *fakeMessage) Nack() error { m.nacked = true return nil } func TestDefaultConsumer(t *testing.T) { tests := []struct { name string innerConsumerFailToStart bool closeChanBeforeStop bool frontendErr error expectStartRequest bool expectSignalWithStartRequest bool msgs []*fakeMessage }{ { name: "failed to start", innerConsumerFailToStart: true, }, { name: "invalid messages", msgs: []*fakeMessage{ {val: []byte("invalid payload"), wantAck: false}, {val: []byte("invalid payload 2"), wantAck: false}, }, }, { name: "unsupported request type", msgs: []*fakeMessage{ {val: mustGenerateUnsupportedRequestMsg(t), wantAck: false}, }, }, { name: "startworkflow request with invalid payload content", msgs: []*fakeMessage{ {val: mustGenerateStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeThriftRW, false), wantAck: false}, }, }, { name: "startworkflowfrontend error", frontendErr: &types.InternalServiceError{Message: "oh no"}, msgs: []*fakeMessage{ {val: mustGenerateStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeThriftRW, true), wantAck: false}, }, expectStartRequest: true, }, { name: "startworkflowfrontend WorkflowExecutionAlreadyStartedError", frontendErr: &types.WorkflowExecutionAlreadyStartedError{Message: "all good, already started"}, msgs: []*fakeMessage{ {val: mustGenerateStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeThriftRW, true), wantAck: true}, }, expectStartRequest: true, }, { name: "startworkflow unsupported encoding type. json encoding of requests are lossy due to PII masking so it shouldn't be used for async requests", msgs: []*fakeMessage{ {val: mustGenerateStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeJSON, true), wantAck: false}, }, }, { name: "startworkflow ok", msgs: []*fakeMessage{ {val: mustGenerateStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeThriftRW, true), wantAck: true}, }, expectStartRequest: true, }, { name: "startworkflow ok with chan closed before stopping", closeChanBeforeStop: true, msgs: []*fakeMessage{ {val: mustGenerateStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeThriftRW, true), wantAck: true}, }, expectStartRequest: true, }, // signal with start test cases { name: "signalwithstartworkflow request with invalid payload content", msgs: []*fakeMessage{ {val: mustGenerateSignalWithStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeThriftRW, false), wantAck: false}, }, }, { name: "signalwithstartworkflow frontend error", frontendErr: &types.InternalServiceError{Message: "oh no"}, msgs: []*fakeMessage{ {val: mustGenerateSignalWithStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeThriftRW, true), wantAck: false}, }, expectSignalWithStartRequest: true, }, { name: "signalwithstartworkflow WorkflowExecutionAlreadyStartedError error", frontendErr: &types.WorkflowExecutionAlreadyStartedError{Message: "All good"}, msgs: []*fakeMessage{ {val: mustGenerateSignalWithStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeThriftRW, true), wantAck: true}, }, expectSignalWithStartRequest: true, }, { name: "signalwithstartworkflow unsupported encoding type. json encoding of requests are lossy due to PII masking so it shouldn't be used for async requests", msgs: []*fakeMessage{ {val: mustGenerateSignalWithStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeJSON, true), wantAck: false}, }, }, { name: "signalwithstartworkflow ok", msgs: []*fakeMessage{ {val: mustGenerateSignalWithStartWorkflowExecutionRequestMsg(t, constants.EncodingTypeThriftRW, true), wantAck: true}, }, expectSignalWithStartRequest: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { fakeConsumer := &fakeMessageConsumer{ ch: make(chan messaging.Message), failToStart: tc.innerConsumerFailToStart, } mockFrontend := frontend.NewMockClient(gomock.NewController(t)) // we fake 2 headers and pass them manually to the mock because "..." extension doesn't work with mocked interface opts := getYARPCOptions(fakeHeaders()) if tc.expectStartRequest { mockFrontend.EXPECT(). StartWorkflowExecution(gomock.Any(), gomock.Any(), opts[0], opts[1]). DoAndReturn(func(ctx interface{}, req *types.StartWorkflowExecutionRequest, opts ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { if diff := cmp.Diff(testStartReq.StartWorkflowExecutionRequest, req); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } if tc.frontendErr != nil { return nil, tc.frontendErr } return &types.StartWorkflowExecutionResponse{RunID: "test-run-id"}, nil }).MinTimes(1) } if tc.expectSignalWithStartRequest { mockFrontend.EXPECT(). SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any(), opts[0], opts[1]). DoAndReturn(func(ctx interface{}, req *types.SignalWithStartWorkflowExecutionRequest, opts ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { if diff := cmp.Diff(testSignalWithStartAsyncReq.SignalWithStartWorkflowExecutionRequest, req); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } if tc.frontendErr != nil { return nil, tc.frontendErr } return &types.StartWorkflowExecutionResponse{RunID: "test-run-id"}, nil }).MinTimes(1) } c := New("queueid1", fakeConsumer, testlogger.New(t), metrics.NewNoopMetricsClient(), mockFrontend, WithConcurrency(2)) err := c.Start() if tc.innerConsumerFailToStart != (err != nil) { t.Errorf("Start() err: %v, wantErr: %v", err, tc.innerConsumerFailToStart) } if err != nil { return } for _, msg := range tc.msgs { fakeConsumer.ch <- msg } if tc.closeChanBeforeStop { close(fakeConsumer.ch) } c.Stop() if !fakeConsumer.stopped { t.Error("innerConsumer.Stop() not called") } for i, msg := range tc.msgs { if msg.wantAck && !msg.acked { t.Errorf("message %d not acked", i) } if !msg.wantAck && !msg.nacked { t.Errorf("message %d not nacked", i) } } }) } } func mustGenerateStartWorkflowExecutionRequestMsg(t *testing.T, encodingType constants.EncodingType, validPayload bool) []byte { encoder := codec.NewThriftRWEncoder() payload, err := encoder.Encode(thrift.FromStartWorkflowExecutionAsyncRequest(testStartReq)) if err != nil { t.Fatal(err) } if !validPayload { payload = []byte("invalid payload") } msg := &sqlblobs.AsyncRequestMessage{ Type: sqlblobs.AsyncRequestTypeStartWorkflowExecutionAsyncRequest.Ptr(), Header: fakeHeaders(), Encoding: common.StringPtr(string(encodingType)), Payload: payload, } res, err := codec.NewThriftRWEncoder().Encode(msg) if err != nil { t.Fatal(err) } return res } func mustGenerateSignalWithStartWorkflowExecutionRequestMsg(t *testing.T, encodingType constants.EncodingType, validPayload bool) []byte { encoder := codec.NewThriftRWEncoder() payload, err := encoder.Encode(thrift.FromSignalWithStartWorkflowExecutionAsyncRequest(testSignalWithStartAsyncReq)) if err != nil { t.Fatal(err) } if !validPayload { payload = []byte("invalid payload") } msg := &sqlblobs.AsyncRequestMessage{ Type: sqlblobs.AsyncRequestTypeSignalWithStartWorkflowExecutionAsyncRequest.Ptr(), Header: fakeHeaders(), Encoding: common.StringPtr(string(encodingType)), Payload: payload, } res, err := codec.NewThriftRWEncoder().Encode(msg) if err != nil { t.Fatal(err) } return res } func mustGenerateUnsupportedRequestMsg(t *testing.T) []byte { encoder := codec.NewThriftRWEncoder() payload, err := encoder.Encode(thrift.FromStartWorkflowExecutionAsyncRequest(testStartReq)) if err != nil { t.Fatal(err) } tp := sqlblobs.AsyncRequestType(-1) msg := &sqlblobs.AsyncRequestMessage{ Type: &tp, Header: &shared.Header{}, Encoding: common.StringPtr(string(constants.EncodingTypeJSON)), Payload: payload, } res, err := codec.NewThriftRWEncoder().Encode(msg) if err != nil { t.Fatal(err) } return res } func fakeHeaders() *shared.Header { return &shared.Header{ Fields: map[string][]byte{ "key1": []byte("val1"), "key2": []byte("val2"), }, } } ================================================ FILE: common/asyncworkflow/queue/consumer/errors.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package consumer import "github.com/uber/cadence/.gen/go/sqlblobs" type UnsupportedRequestType struct { Type sqlblobs.AsyncRequestType } func (e *UnsupportedRequestType) Error() string { return "unsupported request type: " + e.Type.String() } type UnsupportedEncoding struct { EncodingType string } func (e *UnsupportedEncoding) Error() string { return "unsupported encoding: " + e.EncodingType } ================================================ FILE: common/asyncworkflow/queue/interface.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/common/asyncworkflow/queue package queue import ( "github.com/uber/cadence/common/asyncworkflow/queue/provider" "github.com/uber/cadence/common/types" ) type ( // Provider is used to get a queue Provider interface { GetPredefinedQueue(string) (provider.Queue, error) GetQueue(string, *types.DataBlob) (provider.Queue, error) } ) ================================================ FILE: common/asyncworkflow/queue/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package queue -source interface.go -destination interface_mock.go -self_package github.com/uber/cadence/common/asyncworkflow/queue // // Package queue is a generated GoMock package. package queue import ( reflect "reflect" gomock "go.uber.org/mock/gomock" provider "github.com/uber/cadence/common/asyncworkflow/queue/provider" types "github.com/uber/cadence/common/types" ) // MockProvider is a mock of Provider interface. type MockProvider struct { ctrl *gomock.Controller recorder *MockProviderMockRecorder isgomock struct{} } // MockProviderMockRecorder is the mock recorder for MockProvider. type MockProviderMockRecorder struct { mock *MockProvider } // NewMockProvider creates a new mock instance. func NewMockProvider(ctrl *gomock.Controller) *MockProvider { mock := &MockProvider{ctrl: ctrl} mock.recorder = &MockProviderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProvider) EXPECT() *MockProviderMockRecorder { return m.recorder } // GetPredefinedQueue mocks base method. func (m *MockProvider) GetPredefinedQueue(arg0 string) (provider.Queue, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPredefinedQueue", arg0) ret0, _ := ret[0].(provider.Queue) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPredefinedQueue indicates an expected call of GetPredefinedQueue. func (mr *MockProviderMockRecorder) GetPredefinedQueue(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPredefinedQueue", reflect.TypeOf((*MockProvider)(nil).GetPredefinedQueue), arg0) } // GetQueue mocks base method. func (m *MockProvider) GetQueue(arg0 string, arg1 *types.DataBlob) (provider.Queue, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueue", arg0, arg1) ret0, _ := ret[0].(provider.Queue) ret1, _ := ret[1].(error) return ret0, ret1 } // GetQueue indicates an expected call of GetQueue. func (mr *MockProviderMockRecorder) GetQueue(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueue", reflect.TypeOf((*MockProvider)(nil).GetQueue), arg0, arg1) } ================================================ FILE: common/asyncworkflow/queue/kafka/config.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import ( "fmt" "strings" "github.com/IBM/sarama" "github.com/uber/cadence/common/authorization" "github.com/uber/cadence/common/config" ) type ( queueConfig struct { Connection connectionConfig `yaml:"connection"` Topic string `yaml:"topic"` } connectionConfig struct { Brokers []string `yaml:"brokers"` TLS config.TLS `yaml:"tls"` SASL config.SASL `yaml:"sasl"` } ) func (c *queueConfig) ID() string { return fmt.Sprintf("kafka::%s/%s", c.Topic, strings.Join(c.Connection.Brokers, ",")) } func newSaramaConfigWithAuth(tls *config.TLS, sasl *config.SASL) (*sarama.Config, error) { saramaConfig := sarama.NewConfig() // TLS support tlsConfig, err := tls.ToTLSConfig() if err != nil { return nil, fmt.Errorf("Error creating Kafka TLS config %w", err) } if tlsConfig != nil { saramaConfig.Net.TLS.Enable = true saramaConfig.Net.TLS.Config = tlsConfig } // SASL support if sasl.Enabled { saramaConfig.Net.SASL.Enable = true saramaConfig.Net.SASL.Handshake = true saramaConfig.Net.SASL.User = sasl.User saramaConfig.Net.SASL.Password = sasl.Password switch sasl.Algorithm { case "sha512": saramaConfig.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient { return &authorization.XDGSCRAMClient{HashGeneratorFcn: authorization.SHA512} } saramaConfig.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA512 case "sha256": saramaConfig.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient { return &authorization.XDGSCRAMClient{HashGeneratorFcn: authorization.SHA256} } saramaConfig.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA256 case "plain": saramaConfig.Net.SASL.Mechanism = sarama.SASLTypePlaintext default: return nil, fmt.Errorf("unknown SASL algorithm %v", sasl.Algorithm) } } return saramaConfig, nil } ================================================ FILE: common/asyncworkflow/queue/kafka/config_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import "testing" func TestQueueConfigID(t *testing.T) { tests := []struct { name string config queueConfig expected string }{ { name: "single broker", config: queueConfig{ Connection: connectionConfig{ Brokers: []string{"broker1:9092"}, }, Topic: "topic1", }, expected: "kafka::topic1/broker1:9092", }, { name: "multiple brokers", config: queueConfig{ Connection: connectionConfig{ Brokers: []string{"broker1:9092", "broker2:9092"}, }, Topic: "topic2", }, expected: "kafka::topic2/broker1:9092,broker2:9092", }, { name: "no brokers", config: queueConfig{ Connection: connectionConfig{ Brokers: []string{}, }, Topic: "topic3", }, expected: "kafka::topic3/", }, { name: "empty topic", config: queueConfig{ Connection: connectionConfig{ Brokers: []string{"broker1:9092"}, }, Topic: "", }, expected: "kafka::/broker1:9092", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.config.ID() if got != tt.expected { t.Errorf("queueConfig.ID() = %v, want %v", got, tt.expected) } }) } } ================================================ FILE: common/asyncworkflow/queue/kafka/decoder.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import ( "encoding/json" "fmt" "github.com/uber/cadence/common/asyncworkflow/queue/provider" "github.com/uber/cadence/common/types" ) type ( decoderImpl struct { blob *types.DataBlob } ) func newDecoder(blob *types.DataBlob) provider.Decoder { return &decoderImpl{ blob: blob, } } func (d *decoderImpl) Decode(out any) error { if d.blob.GetEncodingType() != types.EncodingTypeJSON { return fmt.Errorf("unsupported encoding type %v", d.blob.GetEncodingType()) } return json.Unmarshal(d.blob.Data, out) } ================================================ FILE: common/asyncworkflow/queue/kafka/decoder_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" ) func TestDecode(t *testing.T) { type testStruct struct { Name string `json:"name"` } tests := []struct { name string blob *types.DataBlob want *testStruct wantErr bool expectedErrMsg string }{ { name: "valid JSON encoding", blob: &types.DataBlob{ Data: []byte(`{"name":"test"}`), EncodingType: types.EncodingTypeJSON.Ptr(), }, want: &testStruct{Name: "test"}, wantErr: false, }, { name: "unsupported encoding type", blob: &types.DataBlob{ Data: []byte("aa"), EncodingType: types.EncodingTypeThriftRW.Ptr(), }, want: nil, wantErr: true, expectedErrMsg: "unsupported encoding type", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { decoder := newDecoder(tt.blob) var got testStruct err := decoder.Decode(&got) if tt.wantErr { assert.Error(t, err) assert.Contains(t, err.Error(), tt.expectedErrMsg) } else { assert.NoError(t, err) assert.Equal(t, tt.want, &got) } }) } } ================================================ FILE: common/asyncworkflow/queue/kafka/init.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import ( "fmt" "github.com/uber/cadence/common/asyncworkflow/queue/provider" ) func init() { must := func(err error) { if err != nil { panic(fmt.Errorf("failed to register default provider: %w", err)) } } must(provider.RegisterQueueProvider("kafka", newQueue)) must(provider.RegisterDecoder("kafka", newDecoder)) } ================================================ FILE: common/asyncworkflow/queue/kafka/queue.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import ( "fmt" "sort" "time" "github.com/IBM/sarama" "github.com/uber/cadence/common/asyncworkflow/queue/consumer" "github.com/uber/cadence/common/asyncworkflow/queue/provider" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/messaging/kafka" "github.com/uber/cadence/common/metrics" ) type ( queueImpl struct { config *queueConfig } ) func newQueue(decoder provider.Decoder) (provider.Queue, error) { var out queueConfig if err := decoder.Decode(&out); err != nil { return nil, fmt.Errorf("bad config: %w", err) } sort.Strings(out.Connection.Brokers) return &queueImpl{ config: &out, }, nil } func (q *queueImpl) ID() string { return q.config.ID() } func (q *queueImpl) CreateConsumer(p *provider.Params) (provider.Consumer, error) { consumerGroup := fmt.Sprintf("%s-asyncwf-consumer", q.config.Topic) dlqTopic := fmt.Sprintf("%s-dlq", q.config.Topic) dlqConfig, err := newSaramaConfigWithAuth(&q.config.Connection.TLS, &q.config.Connection.SASL) if err != nil { return nil, fmt.Errorf("failed to create kafka sarama config: %w", err) } dlqConfig.Producer.Return.Successes = true dlqProducer, err := newProducer(dlqTopic, q.config.Connection.Brokers, dlqConfig, p.MetricsClient, p.Logger) if err != nil { return nil, fmt.Errorf("failed to create kafka producer for dlq: %w", err) } consumerConfig, err := newSaramaConfigWithAuth(&q.config.Connection.TLS, &q.config.Connection.SASL) if err != nil { return nil, fmt.Errorf("failed to create kafka sarama config: %w", err) } consumerConfig.Consumer.Fetch.Default = 30 * 1024 * 1024 // 30MB. consumerConfig.Consumer.Return.Errors = true consumerConfig.Consumer.Offsets.AutoCommit.Enable = false // Use manual commit consumerConfig.Consumer.Offsets.Initial = sarama.OffsetOldest consumerConfig.Consumer.MaxProcessingTime = 250 * time.Millisecond kafkaConsumer, err := kafka.NewKafkaConsumer(dlqProducer, q.config.Connection.Brokers, q.config.Topic, consumerGroup, consumerConfig, p.MetricsClient, p.Logger) if err != nil { return nil, fmt.Errorf("failed to create kafka consumer: %w", err) } p.Logger.Info("Creating async wf consumer", tag.KafkaTopicName(q.config.Topic)) return consumer.New(q.ID(), kafkaConsumer, p.Logger, p.MetricsClient, p.FrontendClient), nil } func (q *queueImpl) CreateProducer(p *provider.Params) (messaging.Producer, error) { config, err := newSaramaConfigWithAuth(&q.config.Connection.TLS, &q.config.Connection.SASL) if err != nil { return nil, err } config.Producer.Return.Successes = true p.Logger.Info("Creating async wf producer", tag.KafkaTopicName(q.config.Topic)) return newProducer(q.config.Topic, q.config.Connection.Brokers, config, p.MetricsClient, p.Logger) } func newProducer(topic string, brokers []string, saramaConfig *sarama.Config, metricsClient metrics.Client, logger log.Logger) (messaging.Producer, error) { p, err := sarama.NewSyncProducer(brokers, saramaConfig) if err != nil { return nil, err } withMetricsOpt := messaging.WithMetricTags(metrics.TopicTag(topic)) return messaging.NewMetricProducer(kafka.NewKafkaProducer(topic, p, logger), metricsClient, withMetricsOpt), nil } ================================================ FILE: common/asyncworkflow/queue/kafka/queue_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import ( "errors" "testing" "github.com/IBM/sarama" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/asyncworkflow/queue/provider" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) type MockDecoder struct { DecodeFunc func(v any) error } func (m *MockDecoder) Decode(v any) error { return m.DecodeFunc(v) } func TestNewQueue(t *testing.T) { tests := []struct { name string decoder *MockDecoder want *queueImpl wantErr bool errString string }{ { name: "successful decoding", decoder: &MockDecoder{ DecodeFunc: func(v any) error { out := v.(*queueConfig) out.Connection.Brokers = []string{"broker2", "broker1"} return nil }, }, want: &queueImpl{ config: &queueConfig{ Connection: connectionConfig{Brokers: []string{"broker1", "broker2"}}, }, }, wantErr: false, }, { name: "decoding failure", decoder: &MockDecoder{ DecodeFunc: func(v any) error { return errors.New("decoding error") }, }, want: nil, wantErr: true, errString: "bad config: decoding error", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := newQueue(tt.decoder) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.want, got) } }) } } func TestCreateConsumer(t *testing.T) { testCases := []struct { name string config *queueConfig wantErr bool }{ { name: "Success case", config: &queueConfig{ Connection: connectionConfig{ Brokers: []string{"localhost:9092"}, TLS: config.TLS{ Enabled: false, }, SASL: config.SASL{ Enabled: false, }, }, Topic: "test-topic", }, }, { name: "Invalid SASL config", config: &queueConfig{ Connection: connectionConfig{ Brokers: []string{"localhost:9092"}, TLS: config.TLS{ Enabled: false, }, SASL: config.SASL{ Enabled: true, Algorithm: "test", }, }, Topic: "test-topic", }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockBroker := sarama.NewMockBroker(t, 0) defer mockBroker.Close() mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ "MetadataRequest": sarama.NewMockMetadataResponse(t). SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). SetLeader("test-topic", 0, mockBroker.BrokerID()). SetController(mockBroker.BrokerID()), }) tc.config.Connection.Brokers = []string{mockBroker.Addr()} q := &queueImpl{ config: tc.config, } p := &provider.Params{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), } consumer, err := q.CreateConsumer(p) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.NotNil(t, consumer) } }) } } func TestCreateProducer(t *testing.T) { testCases := []struct { name string config *queueConfig wantErr bool }{ { name: "Success case", config: &queueConfig{ Connection: connectionConfig{ Brokers: []string{"localhost:9092"}, TLS: config.TLS{ Enabled: false, }, SASL: config.SASL{ Enabled: false, }, }, Topic: "test-topic", }, }, { name: "Invalid SASL config", config: &queueConfig{ Connection: connectionConfig{ Brokers: []string{"localhost:9092"}, TLS: config.TLS{ Enabled: false, }, SASL: config.SASL{ Enabled: true, Algorithm: "test", }, }, Topic: "test-topic", }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockBroker := sarama.NewMockBroker(t, 0) defer mockBroker.Close() mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ "MetadataRequest": sarama.NewMockMetadataResponse(t). SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). SetLeader("test-topic", 0, mockBroker.BrokerID()). SetController(mockBroker.BrokerID()), }) tc.config.Connection.Brokers = []string{mockBroker.Addr()} q := &queueImpl{ config: tc.config, } p := &provider.Params{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), } consumer, err := q.CreateProducer(p) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.NotNil(t, consumer) } }) } } ================================================ FILE: common/asyncworkflow/queue/provider/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: provider.go // // Generated by this command: // // mockgen -package provider -source provider.go -destination interface_mock.go -self_package github.com/uber/cadence/common/asyncworkflow/queue/provider // // Package provider is a generated GoMock package. package provider import ( reflect "reflect" gomock "go.uber.org/mock/gomock" messaging "github.com/uber/cadence/common/messaging" ) // MockDecoder is a mock of Decoder interface. type MockDecoder struct { ctrl *gomock.Controller recorder *MockDecoderMockRecorder isgomock struct{} } // MockDecoderMockRecorder is the mock recorder for MockDecoder. type MockDecoderMockRecorder struct { mock *MockDecoder } // NewMockDecoder creates a new mock instance. func NewMockDecoder(ctrl *gomock.Controller) *MockDecoder { mock := &MockDecoder{ctrl: ctrl} mock.recorder = &MockDecoderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDecoder) EXPECT() *MockDecoderMockRecorder { return m.recorder } // Decode mocks base method. func (m *MockDecoder) Decode(arg0 any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Decode", arg0) ret0, _ := ret[0].(error) return ret0 } // Decode indicates an expected call of Decode. func (mr *MockDecoderMockRecorder) Decode(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decode", reflect.TypeOf((*MockDecoder)(nil).Decode), arg0) } // MockConsumer is a mock of Consumer interface. type MockConsumer struct { ctrl *gomock.Controller recorder *MockConsumerMockRecorder isgomock struct{} } // MockConsumerMockRecorder is the mock recorder for MockConsumer. type MockConsumerMockRecorder struct { mock *MockConsumer } // NewMockConsumer creates a new mock instance. func NewMockConsumer(ctrl *gomock.Controller) *MockConsumer { mock := &MockConsumer{ctrl: ctrl} mock.recorder = &MockConsumerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockConsumer) EXPECT() *MockConsumerMockRecorder { return m.recorder } // Start mocks base method. func (m *MockConsumer) Start() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start") ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockConsumerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockConsumer)(nil).Start)) } // Stop mocks base method. func (m *MockConsumer) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockConsumerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockConsumer)(nil).Stop)) } // MockQueue is a mock of Queue interface. type MockQueue struct { ctrl *gomock.Controller recorder *MockQueueMockRecorder isgomock struct{} } // MockQueueMockRecorder is the mock recorder for MockQueue. type MockQueueMockRecorder struct { mock *MockQueue } // NewMockQueue creates a new mock instance. func NewMockQueue(ctrl *gomock.Controller) *MockQueue { mock := &MockQueue{ctrl: ctrl} mock.recorder = &MockQueueMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQueue) EXPECT() *MockQueueMockRecorder { return m.recorder } // CreateConsumer mocks base method. func (m *MockQueue) CreateConsumer(arg0 *Params) (Consumer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateConsumer", arg0) ret0, _ := ret[0].(Consumer) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateConsumer indicates an expected call of CreateConsumer. func (mr *MockQueueMockRecorder) CreateConsumer(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateConsumer", reflect.TypeOf((*MockQueue)(nil).CreateConsumer), arg0) } // CreateProducer mocks base method. func (m *MockQueue) CreateProducer(arg0 *Params) (messaging.Producer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateProducer", arg0) ret0, _ := ret[0].(messaging.Producer) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateProducer indicates an expected call of CreateProducer. func (mr *MockQueueMockRecorder) CreateProducer(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateProducer", reflect.TypeOf((*MockQueue)(nil).CreateProducer), arg0) } // ID mocks base method. func (m *MockQueue) ID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ID") ret0, _ := ret[0].(string) return ret0 } // ID indicates an expected call of ID. func (mr *MockQueueMockRecorder) ID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockQueue)(nil).ID)) } ================================================ FILE: common/asyncworkflow/queue/provider/provider.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/common/asyncworkflow/queue/provider package provider import ( "fmt" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/syncmap" "github.com/uber/cadence/common/types" ) type ( Params struct { Logger log.Logger MetricsClient metrics.Client FrontendClient frontend.Client } Decoder interface { Decode(any) error } Consumer interface { Start() error Stop() } // Queue is an interface for async queue Queue interface { ID() string CreateConsumer(*Params) (Consumer, error) CreateProducer(*Params) (messaging.Producer, error) } QueueConstructor func(Decoder) (Queue, error) DecoderConstructor func(*types.DataBlob) Decoder ) var ( queueConstructors = syncmap.New[string, QueueConstructor]() decoderConstructors = syncmap.New[string, DecoderConstructor]() ) // RegisterQueueProvider registers a queue constructor for a given queue type func RegisterQueueProvider(queueType string, queueConstructor QueueConstructor) error { inserted := queueConstructors.Put(queueType, queueConstructor) if !inserted { return fmt.Errorf("queue type %v already registered", queueType) } return nil } // GetQueueProvider returns a queue constructor for a given queue type func GetQueueProvider(queueType string) (QueueConstructor, bool) { return queueConstructors.Get(queueType) } // RegisterDecoder registers a decoder constructor for a given queue type func RegisterDecoder(queueType string, decoderConstructor DecoderConstructor) error { inserted := decoderConstructors.Put(queueType, decoderConstructor) if !inserted { return fmt.Errorf("decoder type %v already registered", queueType) } return nil } // GetDecoder returns a decoder constructor for a given queue type func GetDecoder(queueType string) (DecoderConstructor, bool) { return decoderConstructors.Get(queueType) } ================================================ FILE: common/asyncworkflow/queue/provider/provider_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package provider import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" ) func TestQueueProvider(t *testing.T) { testCases := []struct { name string queueType string setup func() wantErr bool }{ { name: "Success case", queueType: "q1", wantErr: false, }, { name: "Duplicate type", queueType: "q2", setup: func() { RegisterQueueProvider("q2", func(Decoder) (Queue, error) { return nil, nil }) }, wantErr: true, }, } for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { _, ok := GetQueueProvider(tt.queueType) assert.False(t, ok) if tt.setup != nil { tt.setup() } err := RegisterQueueProvider(tt.queueType, func(Decoder) (Queue, error) { return nil, nil }) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } _, ok = GetQueueProvider(tt.queueType) assert.True(t, ok) }) } } func TestDecoder(t *testing.T) { testCases := []struct { name string queueType string setup func() wantErr bool }{ { name: "Success case", queueType: "q1", wantErr: false, }, { name: "Duplicate type", queueType: "q2", setup: func() { RegisterDecoder("q2", func(*types.DataBlob) Decoder { return nil }) }, wantErr: true, }, } for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { _, ok := GetDecoder(tt.queueType) assert.False(t, ok) if tt.setup != nil { tt.setup() } err := RegisterDecoder(tt.queueType, func(*types.DataBlob) Decoder { return nil }) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } _, ok = GetDecoder(tt.queueType) assert.True(t, ok) }) } } ================================================ FILE: common/asyncworkflow/queue/provider.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "fmt" "github.com/uber/cadence/common/asyncworkflow/queue/provider" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/types" ) type ( providerImpl struct { queues map[string]provider.Queue } ) // NewAsyncQueueProvider returns a new async queue provider func NewAsyncQueueProvider(cfg map[string]config.AsyncWorkflowQueueProvider) (Provider, error) { p := &providerImpl{ queues: make(map[string]provider.Queue), } for queueName, queueCfg := range cfg { queueConstructor, ok := provider.GetQueueProvider(queueCfg.Type) if !ok { return nil, fmt.Errorf("queue type %v not registered", queueCfg.Type) } queue, err := queueConstructor(queueCfg.Config) if err != nil { return nil, err } p.queues[queueName] = queue } return p, nil } func (p *providerImpl) GetPredefinedQueue(name string) (provider.Queue, error) { queue, ok := p.queues[name] if !ok { return nil, fmt.Errorf("queue %v not found", name) } return queue, nil } func (p *providerImpl) GetQueue(queueType string, queueConfig *types.DataBlob) (provider.Queue, error) { queueConfigDecoder, ok := provider.GetDecoder(queueType) if !ok { return nil, fmt.Errorf("queue type %v not registered", queueType) } decoder := queueConfigDecoder(queueConfig) queueConstructor, ok := provider.GetQueueProvider(queueType) if !ok { return nil, fmt.Errorf("queue type %v not registered", queueType) } return queueConstructor(decoder) } ================================================ FILE: common/asyncworkflow/queue/provider_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/asyncworkflow/queue/provider" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/types" ) func mockQueueConstructor(cfg provider.Decoder) (provider.Queue, error) { // Mock implementation return nil, nil } func mockDecoderConstructor(cfg *types.DataBlob) provider.Decoder { // Mock implementation return nil } func TestNewAsyncQueueProvider(t *testing.T) { // Mock the provider registration functions provider.RegisterQueueProvider("validType", mockQueueConstructor) provider.RegisterDecoder("validType", mockDecoderConstructor) tests := []struct { name string cfg map[string]config.AsyncWorkflowQueueProvider expectError bool errorContains string }{ { name: "Successful Initialization", cfg: map[string]config.AsyncWorkflowQueueProvider{ "testQueue": {Type: "validType", Config: &config.YamlNode{}}, }, expectError: false, }, { name: "Unregistered Queue Type", cfg: map[string]config.AsyncWorkflowQueueProvider{ "testQueue": {Type: "invalidType", Config: &config.YamlNode{}}, }, expectError: true, errorContains: "not registered", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := NewAsyncQueueProvider(tt.cfg) if tt.expectError { assert.Error(t, err) if tt.errorContains != "" { assert.Contains(t, err.Error(), tt.errorContains) } } else { assert.NoError(t, err) } }) } } func TestGetPredefinedQueue(t *testing.T) { p := &providerImpl{ queues: map[string]provider.Queue{ "testQueue": nil, }, } tests := []struct { name string queueName string expectError bool errorContains string }{ { name: "Successful Get", queueName: "testQueue", expectError: false, }, { name: "Queue Not Found", queueName: "invalidQueue", expectError: true, errorContains: "not found", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := p.GetPredefinedQueue(tt.queueName) if tt.expectError { assert.Error(t, err) if tt.errorContains != "" { assert.Contains(t, err.Error(), tt.errorContains) } } else { assert.NoError(t, err) } }) } } func TestGetQueue(t *testing.T) { // Mock the provider registration functions provider.RegisterQueueProvider("validType", mockQueueConstructor) provider.RegisterDecoder("validType", mockDecoderConstructor) p := &providerImpl{ queues: map[string]provider.Queue{}, } tests := []struct { name string queueType string queueConfig *types.DataBlob expectError bool errorContains string }{ { name: "Successful Get", queueType: "validType", queueConfig: &types.DataBlob{}, expectError: false, }, { name: "Unregistered Queue Type", queueType: "invalidType", queueConfig: &types.DataBlob{}, expectError: true, errorContains: "not registered", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := p.GetQueue(tt.queueType, tt.queueConfig) if tt.expectError { assert.Error(t, err) if tt.errorContains != "" { assert.Contains(t, err.Error(), tt.errorContains) } } else { assert.NoError(t, err) } }) } } ================================================ FILE: common/asyncworkflow/queueconfigapi/handler.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queueconfigapi import ( "context" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) type handlerImpl struct { logger log.Logger domainHandler domain.Handler } func New(logger log.Logger, dh domain.Handler) Handler { return &handlerImpl{ logger: logger, domainHandler: dh, } } func (h *handlerImpl) GetConfiguraton(ctx context.Context, req *types.GetDomainAsyncWorkflowConfiguratonRequest) (*types.GetDomainAsyncWorkflowConfiguratonResponse, error) { resp, err := h.domainHandler.DescribeDomain(ctx, &types.DescribeDomainRequest{ Name: &req.Domain, }) if err != nil { return nil, err } if resp == nil || resp.Configuration == nil || resp.Configuration.AsyncWorkflowConfig == nil { return &types.GetDomainAsyncWorkflowConfiguratonResponse{}, nil } return &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: resp.Configuration.AsyncWorkflowConfig, }, nil } func (h *handlerImpl) UpdateConfiguration(ctx context.Context, req *types.UpdateDomainAsyncWorkflowConfiguratonRequest) (*types.UpdateDomainAsyncWorkflowConfiguratonResponse, error) { if req == nil { return nil, &types.BadRequestError{Message: "Request is nil."} } err := h.domainHandler.UpdateAsyncWorkflowConfiguraton(ctx, *req) if err != nil { return nil, err } return &types.UpdateDomainAsyncWorkflowConfiguratonResponse{}, nil } ================================================ FILE: common/asyncworkflow/queueconfigapi/handler_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package queueconfigapi -source interface.go -destination handler_mock.go -self_package github.com/uber/cadence/common/asyncworkflow/queueconfigapi // // Package queueconfigapi is a generated GoMock package. package queueconfigapi import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockHandler is a mock of Handler interface. type MockHandler struct { ctrl *gomock.Controller recorder *MockHandlerMockRecorder isgomock struct{} } // MockHandlerMockRecorder is the mock recorder for MockHandler. type MockHandlerMockRecorder struct { mock *MockHandler } // NewMockHandler creates a new mock instance. func NewMockHandler(ctrl *gomock.Controller) *MockHandler { mock := &MockHandler{ctrl: ctrl} mock.recorder = &MockHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHandler) EXPECT() *MockHandlerMockRecorder { return m.recorder } // GetConfiguraton mocks base method. func (m *MockHandler) GetConfiguraton(arg0 context.Context, arg1 *types.GetDomainAsyncWorkflowConfiguratonRequest) (*types.GetDomainAsyncWorkflowConfiguratonResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetConfiguraton", arg0, arg1) ret0, _ := ret[0].(*types.GetDomainAsyncWorkflowConfiguratonResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetConfiguraton indicates an expected call of GetConfiguraton. func (mr *MockHandlerMockRecorder) GetConfiguraton(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfiguraton", reflect.TypeOf((*MockHandler)(nil).GetConfiguraton), arg0, arg1) } // UpdateConfiguration mocks base method. func (m *MockHandler) UpdateConfiguration(arg0 context.Context, arg1 *types.UpdateDomainAsyncWorkflowConfiguratonRequest) (*types.UpdateDomainAsyncWorkflowConfiguratonResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateConfiguration", arg0, arg1) ret0, _ := ret[0].(*types.UpdateDomainAsyncWorkflowConfiguratonResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateConfiguration indicates an expected call of UpdateConfiguration. func (mr *MockHandlerMockRecorder) UpdateConfiguration(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateConfiguration", reflect.TypeOf((*MockHandler)(nil).UpdateConfiguration), arg0, arg1) } ================================================ FILE: common/asyncworkflow/queueconfigapi/handler_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queueconfigapi import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) func TestGetConfiguraton(t *testing.T) { tests := map[string]struct { req *types.GetDomainAsyncWorkflowConfiguratonRequest domainHandlerMockFn func(*domain.MockHandler) wantResp *types.GetDomainAsyncWorkflowConfiguratonResponse wantErr bool }{ "Domain handler fails to DescribeDomain": { req: &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, domainHandlerMockFn: func(m *domain.MockHandler) { m.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(nil, errors.New("failed")).Times(1) }, wantErr: true, }, "Domain config is nil": { req: &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, domainHandlerMockFn: func(m *domain.MockHandler) { resp := &types.DescribeDomainResponse{ Configuration: nil, } m.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(resp, nil).Times(1) }, wantResp: &types.GetDomainAsyncWorkflowConfiguratonResponse{}, }, "Domain async wf config is nil": { req: &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, domainHandlerMockFn: func(m *domain.MockHandler) { resp := &types.DescribeDomainResponse{ Configuration: &types.DomainConfiguration{ AsyncWorkflowConfig: nil, }, } m.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(resp, nil).Times(1) }, wantResp: &types.GetDomainAsyncWorkflowConfiguratonResponse{}, }, "Success": { req: &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, domainHandlerMockFn: func(m *domain.MockHandler) { resp := &types.DescribeDomainResponse{ Configuration: &types.DomainConfiguration{ AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-queue", QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"topic":"test-topic","dlq_topic":"test-dlq-topic","consumer_group":"test-consumer-group","brokers":["test-broker"]}`), }, }, }, } m.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(resp, nil).Times(1) }, wantResp: &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-queue", QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"topic":"test-topic","dlq_topic":"test-dlq-topic","consumer_group":"test-consumer-group","brokers":["test-broker"]}`), }, }, }, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) domainHandlerMock := domain.NewMockHandler(ctrl) if tc.domainHandlerMockFn != nil { tc.domainHandlerMockFn(domainHandlerMock) } handler := New(testlogger.New(t), domainHandlerMock) resp, err := handler.GetConfiguraton(context.Background(), tc.req) if tc.wantErr != (err != nil) { t.Fatalf("Error mismatch. Got: %v, want?: %v", err, tc.wantErr) } assert.Equal(t, tc.wantResp, resp) }) } } func TestUpdateConfiguration(t *testing.T) { tests := map[string]struct { req *types.UpdateDomainAsyncWorkflowConfiguratonRequest domainHandlerMockFn func(*domain.MockHandler) wantResp *types.UpdateDomainAsyncWorkflowConfiguratonResponse wantErr bool }{ "Domain handler fails to UpdateAsyncWorkflowConfiguraton": { req: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, domainHandlerMockFn: func(m *domain.MockHandler) { m.EXPECT().UpdateAsyncWorkflowConfiguraton(gomock.Any(), gomock.Any()).Return(errors.New("failed")).Times(1) }, wantErr: true, }, "nil request": { req: nil, wantErr: true, }, "Success": { req: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, domainHandlerMockFn: func(m *domain.MockHandler) { m.EXPECT().UpdateAsyncWorkflowConfiguraton(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, wantResp: &types.UpdateDomainAsyncWorkflowConfiguratonResponse{}, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) domainHandlerMock := domain.NewMockHandler(ctrl) if tc.domainHandlerMockFn != nil { tc.domainHandlerMockFn(domainHandlerMock) } handler := New(testlogger.New(t), domainHandlerMock) resp, err := handler.UpdateConfiguration(context.Background(), tc.req) if tc.wantErr != (err != nil) { t.Fatalf("Error mismatch. Got: %v, want?: %v", err, tc.wantErr) } assert.Equal(t, tc.wantResp, resp) }) } } ================================================ FILE: common/asyncworkflow/queueconfigapi/interface.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queueconfigapi import ( "context" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination handler_mock.go -self_package github.com/uber/cadence/common/asyncworkflow/queueconfigapi type Handler interface { GetConfiguraton(context.Context, *types.GetDomainAsyncWorkflowConfiguratonRequest) (*types.GetDomainAsyncWorkflowConfiguratonResponse, error) UpdateConfiguration(context.Context, *types.UpdateDomainAsyncWorkflowConfiguratonRequest) (*types.UpdateDomainAsyncWorkflowConfiguratonResponse, error) } ================================================ FILE: common/authorization/README.md ================================================ ## Cadence has two authorizer options: 1. OAuthAuthorizer: validates JWTs issued by your Identity Provider and enforces permissions. 2. NoopAuthorizer: turns authorization off. In order to configure, add an authorization section to Cadence server config [example](https://github.com/cadence-workflow/cadence/blob/master/config/development_oauth.yaml). These fields map 1:1 to the Go structs in [common/config](https://github.com/cadence-workflow/cadence/blob/master/common/config/authorization.go). ### Option A for OAuth : Validate tokens via JWKS authorization: oauthAuthorizer: enable: true # Reject tokens with excessively long TTL (seconds). Optional but recommended. maxJwtTTL: 3600 # JWT verification config (algorithm + how to fetch public keys) jwtCredentials: algorithm: RS256 # supported: RS256 # publicKey is optional if you supply a JWKS URL (below) # publicKey: /etc/cadence/keys/idp-public.pem provider: jwksURL: "https://YOUR_IDP/.well-known/jwks.json" # Optional JSONPath-like claims locations used by Cadence: groupsAttributePath: "groups" adminAttributePath: "admin" ### Option B for OAuth : Validate tokens via a static public key authorization: oauthAuthorizer: enable: true maxJwtTTL: 3600 jwtCredentials: algorithm: RS256 publicKey: /etc/cadence/keys/idp-public.pem ### NoopAuthorizer: Turning authz off authorization: noopAuthorizer: enable: true ## Background The server constructs an authorization.Attributes object for each API call (actor, API name, domain, optional workflow/tasklist), evaluates the token, and returns an allow/deny Decision. JWTs are expected to contain Cadence-specific claims including groups and (optionally) an admin flag. ### Key structs & functions: ``` authorization.Authorizer interface authorization.Attributes authorization.Decision authorization.JWTClaims ``` When OAuth authZ is enabled, clients must present a valid JWT to the frontend service on every call (Cadence uses the provided token to authorize the API/Domain access). The exact header/wire placement is handled by Cadence’s server middleware and the client transport; the important bit is that the token must validate against your jwksURL/publicKey, include expected claims (groups/admin), and not exceed maxJwtTTL. ================================================ FILE: common/authorization/authority_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: authorizer.go // // Generated by this command: // // mockgen -package authorization -source authorizer.go -destination authority_mock.go -self_package github.com/uber/cadence/common/authorization // // Package authorization is a generated GoMock package. package authorization import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockAuthorizer is a mock of Authorizer interface. type MockAuthorizer struct { ctrl *gomock.Controller recorder *MockAuthorizerMockRecorder isgomock struct{} } // MockAuthorizerMockRecorder is the mock recorder for MockAuthorizer. type MockAuthorizerMockRecorder struct { mock *MockAuthorizer } // NewMockAuthorizer creates a new mock instance. func NewMockAuthorizer(ctrl *gomock.Controller) *MockAuthorizer { mock := &MockAuthorizer{ctrl: ctrl} mock.recorder = &MockAuthorizerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockAuthorizer) EXPECT() *MockAuthorizerMockRecorder { return m.recorder } // Authorize mocks base method. func (m *MockAuthorizer) Authorize(ctx context.Context, attributes *Attributes) (Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Authorize", ctx, attributes) ret0, _ := ret[0].(Result) ret1, _ := ret[1].(error) return ret0, ret1 } // Authorize indicates an expected call of Authorize. func (mr *MockAuthorizerMockRecorder) Authorize(ctx, attributes any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authorize", reflect.TypeOf((*MockAuthorizer)(nil).Authorize), ctx, attributes) } // MockFilteredRequestBody is a mock of FilteredRequestBody interface. type MockFilteredRequestBody struct { ctrl *gomock.Controller recorder *MockFilteredRequestBodyMockRecorder isgomock struct{} } // MockFilteredRequestBodyMockRecorder is the mock recorder for MockFilteredRequestBody. type MockFilteredRequestBodyMockRecorder struct { mock *MockFilteredRequestBody } // NewMockFilteredRequestBody creates a new mock instance. func NewMockFilteredRequestBody(ctrl *gomock.Controller) *MockFilteredRequestBody { mock := &MockFilteredRequestBody{ctrl: ctrl} mock.recorder = &MockFilteredRequestBodyMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFilteredRequestBody) EXPECT() *MockFilteredRequestBodyMockRecorder { return m.recorder } // SerializeForLogging mocks base method. func (m *MockFilteredRequestBody) SerializeForLogging() (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeForLogging") ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeForLogging indicates an expected call of SerializeForLogging. func (mr *MockFilteredRequestBodyMockRecorder) SerializeForLogging() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeForLogging", reflect.TypeOf((*MockFilteredRequestBody)(nil).SerializeForLogging)) } ================================================ FILE: common/authorization/authorizer.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination authority_mock.go -self_package github.com/uber/cadence/common/authorization package authorization import ( "context" "encoding/json" "fmt" "os" "reflect" "strings" clientworker "go.uber.org/cadence/worker" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) const ( // DecisionDeny means auth decision is deny DecisionDeny Decision = iota + 1 // DecisionAllow means auth decision is allow DecisionAllow ) const ( // PermissionRead means the user can write on the domain level APIs PermissionRead Permission = iota + 1 // PermissionWrite means the user can write on the domain level APIs PermissionWrite // PermissionAdmin means the user can read+write on the domain level APIs PermissionAdmin // PermissionProcess means the user can process via the task execution related APIs PermissionProcess ) type ( domainData map[string]string // Attributes is input for authority to make decision. // It can be extended in future if required auth on resources like WorkflowType and TaskList Attributes struct { Actor string APIName string DomainName string WorkflowType *types.WorkflowType TaskList *types.TaskList Permission Permission RequestBody FilteredRequestBody // request object except for data inputs (PII) } // Result is result from authority. Result struct { Decision Decision } // Decision is enum type for auth decision Decision int // Permission is enum type for auth permission Permission int ) func NewPermission(permission string) Permission { switch permission { case "read": return PermissionRead case "write": return PermissionWrite case "admin": return PermissionAdmin case "process": return PermissionProcess default: return -1 } } func (d domainData) Groups(groupType string) []string { res, ok := d[groupType] if !ok { return nil } return strings.Split(res, groupSeparator) } // Authorizer is an interface for authorization type Authorizer interface { Authorize(ctx context.Context, attributes *Attributes) (Result, error) } func GetAuthProviderClient(privateKey string) (clientworker.AuthorizationProvider, error) { pk, err := os.ReadFile(privateKey) if err != nil { return nil, fmt.Errorf("invalid private key path %s", privateKey) } return clientworker.NewAdminJwtAuthorizationProvider(pk), nil } // FilteredRequestBody request object except for data inputs (PII) type FilteredRequestBody interface { SerializeForLogging() (string, error) } type simpleRequestLogWrapper struct { request interface{} } func (f *simpleRequestLogWrapper) SerializeForLogging() (string, error) { // We have to check if the request is a typed nil. In the interface we have to handle typed nils. // The reflection check is slow but this function is doing json marshalling, so performance // shouldn't be an issue. if f.request == nil || reflect.ValueOf(f.request).IsNil() { return "", nil } res, err := json.Marshal(f.request) if err != nil { return "", err } return string(res), nil } func NewFilteredRequestBody(request interface{}) FilteredRequestBody { return &simpleRequestLogWrapper{request} } func validatePermission(claims *JWTClaims, attributes *Attributes, data domainData) error { if (attributes.Permission < PermissionRead) || (attributes.Permission > PermissionProcess) { return fmt.Errorf("permission %v is not supported", attributes.Permission) } allowedGroups := map[string]bool{} // groups that allowed by domain configuration(in domainData) // write groups are always checked for _, g := range data.Groups(constants.DomainDataKeyForWriteGroups) { allowedGroups[g] = true } if attributes.Permission == PermissionRead { for _, g := range data.Groups(constants.DomainDataKeyForReadGroups) { allowedGroups[g] = true } } if attributes.Permission == PermissionProcess { for _, g := range data.Groups(constants.DomainDataKeyForProcessGroups) { allowedGroups[g] = true } } for _, jwtGroup := range claims.GetGroups() { if _, ok := allowedGroups[jwtGroup]; ok { return nil } } return fmt.Errorf("token doesn't have the right permission, jwt groups: %v, allowed groups: %v", claims.GetGroups(), allowedGroups) } ================================================ FILE: common/authorization/authorizer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package authorization import ( "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) func Test_validatePermission(t *testing.T) { readRequestAttr := &Attributes{Permission: PermissionRead} writeRequestAttr := &Attributes{Permission: PermissionWrite} processRequestAttr := &Attributes{Permission: PermissionProcess} readWriteDomainData := domainData{ constants.DomainDataKeyForReadGroups: "read1", constants.DomainDataKeyForWriteGroups: "write1", } readDomainData := domainData{ constants.DomainDataKeyForReadGroups: "read1", } processDomainData := domainData{ constants.DomainDataKeyForProcessGroups: "process1", } emptyDomainData := domainData{} tests := []struct { name string claims *JWTClaims attributes *Attributes data map[string]string wantErr assert.ErrorAssertionFunc }{ { name: "no args should always fail", claims: &JWTClaims{}, attributes: &Attributes{}, data: emptyDomainData, wantErr: assert.Error, }, { name: "Empty claims will be denied even when domain data has no groups", claims: &JWTClaims{}, attributes: writeRequestAttr, data: emptyDomainData, wantErr: assert.Error, }, { name: "Empty claims will be denied when domain data has at least one group", claims: &JWTClaims{}, attributes: writeRequestAttr, data: readDomainData, wantErr: assert.Error, }, { name: "Read-only groups should not get access to write groups", claims: &JWTClaims{Groups: "read1"}, attributes: writeRequestAttr, data: readWriteDomainData, wantErr: assert.Error, }, { name: "Process-only groups should not get access to write groups", claims: &JWTClaims{Groups: "process1"}, attributes: writeRequestAttr, data: processDomainData, wantErr: assert.Error, }, { name: "Process-only groups should get access to process groups", claims: &JWTClaims{Groups: "process1"}, attributes: processRequestAttr, data: processDomainData, wantErr: assert.NoError, }, { name: "Write-only groups should get access to read groups", claims: &JWTClaims{Groups: "write1"}, attributes: readRequestAttr, data: readWriteDomainData, wantErr: assert.NoError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.wantErr(t, validatePermission(tt.claims, tt.attributes, tt.data)) }) } } func TestSignalWithStartWorkflowExecutionRequestSerializeForLogging(t *testing.T) { tests := map[string]struct { input interface{} expectedOutput string expectedErrorOutput error }{ "complete request without error": { input: createNewSignalWithStartWorkflowExecutionRequest(), expectedOutput: "{\"domain\":\"testDomain\",\"workflowId\":\"testWorkflowID\",\"workflowType\":{\"name\":\"testWorkflowType\"},\"taskList\":{\"name\":\"testTaskList\",\"kind\":\"STICKY\"},\"executionStartToCloseTimeoutSeconds\":1,\"taskStartToCloseTimeoutSeconds\":1,\"identity\":\"testIdentity\",\"requestId\":\"DF66E35D-A5B0-425D-8731-6AAC4A4B6368\",\"workflowIdReusePolicy\":\"AllowDuplicate\",\"signalName\":\"testRequest\",\"control\":\"dGVzdENvbnRyb2w=\",\"retryPolicy\":{\"initialIntervalInSeconds\":1,\"backoffCoefficient\":1,\"maximumIntervalInSeconds\":1,\"maximumAttempts\":1,\"nonRetriableErrorReasons\":[\"testArray\"],\"expirationIntervalInSeconds\":1},\"cronSchedule\":\"testSchedule\",\"header\":{},\"delayStartSeconds\":1,\"jitterStartSeconds\":1,\"firstRunAtTimestamp\":1}", }, "non marchalable struct should error": { input: make(chan struct{}), expectedErrorOutput: &json.UnsupportedTypeError{}, }, "empty request without error": { input: &types.SignalWithStartWorkflowExecutionRequest{}, expectedOutput: "{}", }, "typed nil request without error": { input: (*types.SignalWithStartWorkflowExecutionRequest)(nil), expectedOutput: "", }, "nil request without error": { input: nil, expectedOutput: "", }, } for name, test := range tests { t.Run(name, func(t *testing.T) { assert.NotPanics(t, func() { wrappedInput := NewFilteredRequestBody(test.input) output, err := wrappedInput.SerializeForLogging() assert.Equal(t, test.expectedOutput, output) if test.expectedErrorOutput != nil { assert.ErrorAs(t, err, &test.expectedErrorOutput) } else { assert.NoError(t, err) } assert.NotContains(t, output, "PII") }) }) } } func createNewSignalWithStartWorkflowExecutionRequest() *types.SignalWithStartWorkflowExecutionRequest { testTasklistKind := types.TaskListKind(1) testExecutionStartToCloseTimeoutSeconds := int32(1) testTaskStartToCloseTimeoutSeconds := int32(1) testWorkflowIDReusePolicy := types.WorkflowIDReusePolicy(1) testDelayStartSeconds := int32(1) testJitterStartSeconds := int32(1) testFirstRunAtTimestamp := int64(1) piiTestArray := []byte("testInputPII") piiTestMap := make(map[string][]byte) piiTestMap["PII"] = piiTestArray testReq := &types.SignalWithStartWorkflowExecutionRequest{ Domain: "testDomain", WorkflowID: "testWorkflowID", WorkflowType: &types.WorkflowType{Name: "testWorkflowType"}, TaskList: &types.TaskList{ Name: "testTaskList", Kind: &testTasklistKind, }, Input: piiTestArray, ExecutionStartToCloseTimeoutSeconds: &testExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: &testTaskStartToCloseTimeoutSeconds, Identity: "testIdentity", RequestID: "DF66E35D-A5B0-425D-8731-6AAC4A4B6368", WorkflowIDReusePolicy: &testWorkflowIDReusePolicy, SignalName: "testRequest", SignalInput: piiTestArray, Control: []byte("testControl"), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1, MaximumIntervalInSeconds: 1, MaximumAttempts: 1, NonRetriableErrorReasons: []string{"testArray"}, ExpirationIntervalInSeconds: 1, }, CronSchedule: "testSchedule", Memo: &types.Memo{Fields: piiTestMap}, SearchAttributes: &types.SearchAttributes{IndexedFields: piiTestMap}, Header: &types.Header{Fields: map[string][]byte{}}, DelayStartSeconds: &testDelayStartSeconds, JitterStartSeconds: &testJitterStartSeconds, FirstRunAtTimestamp: &testFirstRunAtTimestamp, } return testReq } ================================================ FILE: common/authorization/factory.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package authorization import ( "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" ) func NewAuthorizer(authorization config.Authorization, logger log.Logger, domainCache cache.DomainCache) (Authorizer, error) { switch true { case authorization.OAuthAuthorizer.Enable: return NewOAuthAuthorizer(authorization.OAuthAuthorizer, logger, domainCache) default: return NewNopAuthorizer() } } ================================================ FILE: common/authorization/factory_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package authorization import ( "testing" "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" ) type ( factorySuite struct { suite.Suite logger log.Logger } ) func TestFactorySuite(t *testing.T) { suite.Run(t, new(factorySuite)) } func (s *factorySuite) SetupTest() { s.logger = testlogger.New(s.Suite.T()) } func cfgNoop() config.Authorization { return config.Authorization{ OAuthAuthorizer: config.OAuthAuthorizer{ Enable: false, }, NoopAuthorizer: config.NoopAuthorizer{ Enable: true, }, } } func cfgOAuth() config.Authorization { return config.Authorization{ OAuthAuthorizer: config.OAuthAuthorizer{ Enable: true, JwtCredentials: &config.JwtCredentials{ Algorithm: jwt.SigningMethodRS256.Name, PublicKey: "../../config/credentials/keytest.pub", }, MaxJwtTTL: 12345, }, } } func (s *factorySuite) TestFactoryNoopAuthorizer() { cfgOAuthVar := cfgOAuth() publicKey, _ := common.LoadRSAPublicKey(cfgOAuthVar.OAuthAuthorizer.JwtCredentials.PublicKey) var tests = []struct { cfg config.Authorization expected Authorizer err error }{ {cfgNoop(), &nopAuthority{}, nil}, {cfgOAuthVar, &oauthAuthority{ config: cfgOAuthVar.OAuthAuthorizer, log: s.logger, publicKey: publicKey, parser: jwt.NewParser(jwt.WithValidMethods([]string{cfgOAuthVar.OAuthAuthorizer.JwtCredentials.Algorithm}), jwt.WithIssuedAt()), }, nil}, } for _, test := range tests { authorizer, err := NewAuthorizer(test.cfg, s.logger, nil) s.Equal(authorizer, test.expected) s.Equal(err, test.err) } } ================================================ FILE: common/authorization/nopAuthorizer.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package authorization import "context" type nopAuthority struct{} // NewNopAuthorizer creates a no-op authority func NewNopAuthorizer() (Authorizer, error) { return &nopAuthority{}, nil } func (a *nopAuthority) Authorize( ctx context.Context, attributes *Attributes, ) (Result, error) { return Result{Decision: DecisionAllow}, nil } ================================================ FILE: common/authorization/oauthAuthorizer.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package authorization import ( "context" "errors" "fmt" "strings" "time" "github.com/MicahParks/keyfunc/v2" "github.com/golang-jwt/jwt/v5" "github.com/jmespath/go-jmespath" "go.uber.org/yarpc" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) var _ jwt.Claims = (*JWTClaims)(nil) const ( groupSeparator = " " jwtInternalIssuer = "internal-jwt" ) type oauthAuthority struct { config config.OAuthAuthorizer domainCache cache.DomainCache log log.Logger parser *jwt.Parser publicKey interface{} jwks *keyfunc.JWKS } // JWTClaims is a Cadence specific claim with embeded Claims defined https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 type JWTClaims struct { jwt.RegisteredClaims Name string Groups string // separated by space Admin bool TTL int64 // TODO should be removed. ExpiresAt should be used } func (j JWTClaims) GetGroups() []string { return strings.Split(j.Groups, groupSeparator) } // NewOAuthAuthorizer creates an oauth Authorizer func NewOAuthAuthorizer( oauthConfig config.OAuthAuthorizer, log log.Logger, domainCache cache.DomainCache, ) (Authorizer, error) { var jwks *keyfunc.JWKS var key interface{} var err error if oauthConfig.JwtCredentials != nil { if oauthConfig.JwtCredentials.Algorithm != jwt.SigningMethodRS256.Name { return nil, fmt.Errorf("algorithm %q is not supported", oauthConfig.JwtCredentials.Algorithm) } if key, err = common.LoadRSAPublicKey(oauthConfig.JwtCredentials.PublicKey); err != nil { return nil, fmt.Errorf("loading RSA public key: %w", err) } } if oauthConfig.Provider != nil { if oauthConfig.Provider.JWKSURL == "" { return nil, fmt.Errorf("JWKSURL is not set") } // Create the JWKS from the resource at the given URL. if jwks, err = keyfunc.Get(oauthConfig.Provider.JWKSURL, keyfunc.Options{}); err != nil { return nil, fmt.Errorf("creating JWKS from resource: %s error: %w", oauthConfig.Provider.JWKSURL, err) } } return &oauthAuthority{ config: oauthConfig, domainCache: domainCache, log: log, parser: jwt.NewParser( jwt.WithValidMethods([]string{jwt.SigningMethodRS256.Name}), jwt.WithIssuedAt(), ), publicKey: key, jwks: jwks, }, nil } // Authorize defines the logic to verify get claims from token func (a *oauthAuthority) Authorize(ctx context.Context, attributes *Attributes) (Result, error) { call := yarpc.CallFromContext(ctx) token := call.Header(common.AuthorizationTokenHeaderName) if token == "" { a.log.Debug("request is not authorized", tag.Error(errors.New("token is not set in header"))) return Result{Decision: DecisionDeny}, nil } var claims JWTClaims parsedToken, err := a.parser.ParseWithClaims(token, &claims, a.keyFunc) if err != nil { a.log.Debug("request is not authorized", tag.Error(err)) return Result{Decision: DecisionDeny}, nil } if !isTokenInternal(parsedToken) { parsed, _, err := a.parser.ParseUnverified(token, jwt.MapClaims{}) if err != nil { a.log.Debug("request is not authorized", tag.Error(err)) return Result{Decision: DecisionDeny}, nil } if err := a.parseExternal(parsed.Claims.(jwt.MapClaims), &claims); err != nil { a.log.Debug("request is not authorized", tag.Error(err)) return Result{Decision: DecisionDeny}, nil } } if err := a.validateTTL(&claims); err != nil { a.log.Debug("request is not authorized", tag.Error(err)) return Result{Decision: DecisionDeny}, nil } if claims.Admin { return Result{Decision: DecisionAllow}, nil } domain, err := a.domainCache.GetDomain(attributes.DomainName) if err != nil { return Result{Decision: DecisionDeny}, err } if err := validatePermission(&claims, attributes, domain.GetInfo().Data); err != nil { a.log.Debug("request is not authorized", tag.Error(err)) return Result{Decision: DecisionDeny}, nil } return Result{Decision: DecisionAllow}, nil } // keyFunc returns correct key to check signature func (a *oauthAuthority) keyFunc(token *jwt.Token) (interface{}, error) { if isTokenInternal(token) && a.publicKey != nil { return a.publicKey, nil } // External provider with JWKS provided // https://datatracker.ietf.org/doc/html/rfc7517 if a.jwks != nil { return a.jwks.Keyfunc(token) } return nil, errors.New("no public key for verification") } func (a *oauthAuthority) validateTTL(claims *JWTClaims) error { // Fill ExpiresAt when TTL is passed if claims.TTL > 0 { claims.ExpiresAt = jwt.NewNumericDate(claims.IssuedAt.Time.Add(time.Second * time.Duration(claims.TTL))) } exp, err := claims.GetExpirationTime() if err != nil || exp == nil { return errors.New("ExpiresAt is not set") } timeLeft := exp.Unix() - time.Now().Unix() if timeLeft < 0 { return errors.New("token is expired") } if timeLeft > a.config.MaxJwtTTL { return fmt.Errorf("token TTL: %d is larger than MaxTTL allowed: %d", timeLeft, a.config.MaxJwtTTL) } return nil } func isTokenInternal(token *jwt.Token) bool { // external providers should set kid part always if _, ok := token.Header["kid"]; !ok { return true } issuer, err := token.Claims.GetIssuer() if err != nil { return false } return issuer == jwtInternalIssuer } func (a *oauthAuthority) parseExternal(rawClaims map[string]interface{}, claims *JWTClaims) error { if a.config.Provider.GroupsAttributePath != "" { userGroups, err := jmespath.Search(a.config.Provider.GroupsAttributePath, rawClaims) if err != nil { return fmt.Errorf("extracting JWT Groups claim: %w", err) } if _, ok := userGroups.(string); !ok { return errors.New("cannot convert groups to string") } claims.Groups = userGroups.(string) } if a.config.Provider.AdminAttributePath != "" { isAdmin, err := jmespath.Search(a.config.Provider.AdminAttributePath, rawClaims) if err != nil { return fmt.Errorf("extracting JWT Admin claim: %w", err) } if _, ok := isAdmin.(bool); !ok { return errors.New("cannot convert isAdmin to bool") } claims.Admin = isAdmin.(bool) } return nil } ================================================ FILE: common/authorization/oauthAuthorizer_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package authorization import ( "fmt" "strings" "testing" "time" "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "go.uber.org/yarpc/api/encoding" "go.uber.org/yarpc/api/transport" "golang.org/x/net/context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" ) type ( oauthSuite struct { suite.Suite logger *log.MockLogger cfg config.OAuthAuthorizer providerCfg config.OAuthAuthorizer att Attributes token string controller *gomock.Controller domainCache *cache.MockDomainCache ctx context.Context domainEntry *cache.DomainCacheEntry } ) func TestOAuthSuite(t *testing.T) { suite.Run(t, new(oauthSuite)) } func (s *oauthSuite) SetupTest() { s.logger = log.NewMockLogger(gomock.NewController(s.T())) s.cfg = config.OAuthAuthorizer{ Enable: true, JwtCredentials: &config.JwtCredentials{ Algorithm: jwt.SigningMethodRS256.Name, PublicKey: "../../config/credentials/keytest.pub", }, MaxJwtTTL: 300000001, } s.providerCfg = config.OAuthAuthorizer{ Enable: true, Provider: &config.OAuthProvider{ GroupsAttributePath: "tst_group", AdminAttributePath: "tst_admin", }, } // https://jwt.io/#debugger-io?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJTdWIiOiIxMjM0NTY3ODkwIiwiTmFtZSI6IkpvaG4gRG9lIiwiR3JvdXBzIjoiYSBiIGMiLCJBZG1pbiI6ZmFsc2UsIklhdCI6MTYyNzUzODcxMiwiVFRMIjozMDAwMDAwMDB9.bh4s8-l1bjG7-QFzuouPy9WPvkq3_9U2e815WFrN-M247NQROBii8ju_N21i6ixK0t-VZTgcJs2B4aN4w1uiCTCg6NyhdeeG8Xd8NcYw0Oq7fjSoFmOXzDzljY6oi9M1XXniNrDIMBLfKXx8tgseSBwOnWoT3vja3ioU6ReqD3Xsp-Wg_clDhb6vtA6pDtnaCVXJNStLSbgWyi-1Mxo9ar92zRDV5YsMaBdUjFUT2bW9QcFzMFAqpHin0QEIa6GPZezY-yn88k5S5cT6Yh7WA4C0Q6C3H1n3EOS05Phwpxt840w7zjh5XR0-rd8-kRX84pHMh0GwHfjV1K7jBQ2QnQ&publicKey=-----BEGIN%20PUBLIC%20KEY-----%0AMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAscukltHilaq%2Bo5gIVE4P%0AGwWl%2BesvJ2EaEpWw6ogr98Un11YJ4oKkwIkLw4iIo0tveCINA3cZmxaW1RejRWKE%0AqYFtQ1rYd6BsnFAHXWh2R3A1FtpG6ANUEGkE7OAJe2%2FL42E%2FImJ%2BGQxRvartInDM%0AyfiRfB7%2BL2n3wG%2BNi%2BhBNMtAaX4Wwbj2hup21Jjuo96TuhcGImBFBATGWaYR2wqe%0A%2F6by9wJexPHlY%2F1uDp3SnzF1dCLjp76SGCfyYqOGC%2FPxhQi7mDxeH9%2FtIC%2Blt%2FSz%0Awc1n8gZLtlRlZHinvYa8lhWXqVYw6WD8h4LTgALq9iY%2BbeD1PFQSY1GkQtt0RhRw%0AeQIDAQAB%0A-----END%20PUBLIC%20KEY----- s.token = `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJTdWIiOiIxMjM0NTY3ODkwIiwiTmFtZSI6IkpvaG4gRG9lIiwiR3JvdXBzIjoiYSBiIGMiLCJBZG1pbiI6ZmFsc2UsIklhdCI6MTYyNzUzODcxMiwiVFRMIjozMDAwMDAwMDB9.bh4s8-l1bjG7-QFzuouPy9WPvkq3_9U2e815WFrN-M247NQROBii8ju_N21i6ixK0t-VZTgcJs2B4aN4w1uiCTCg6NyhdeeG8Xd8NcYw0Oq7fjSoFmOXzDzljY6oi9M1XXniNrDIMBLfKXx8tgseSBwOnWoT3vja3ioU6ReqD3Xsp-Wg_clDhb6vtA6pDtnaCVXJNStLSbgWyi-1Mxo9ar92zRDV5YsMaBdUjFUT2bW9QcFzMFAqpHin0QEIa6GPZezY-yn88k5S5cT6Yh7WA4C0Q6C3H1n3EOS05Phwpxt840w7zjh5XR0-rd8-kRX84pHMh0GwHfjV1K7jBQ2QnQ` ctx := context.Background() ctx, call := encoding.NewInboundCall(ctx) err := call.ReadFromRequest(&transport.Request{ Headers: transport.NewHeaders().With(common.AuthorizationTokenHeaderName, s.token), }) s.NoError(err) s.att = Attributes{ Actor: "John Doe", APIName: "", DomainName: "test-domain", TaskList: nil, Permission: PermissionRead, } s.domainEntry = cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ ID: "test-domain-id", Name: "test-domain", Data: map[string]string{ constants.DomainDataKeyForReadGroups: "c", }, }, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, // not used ) s.controller = gomock.NewController(s.T()) s.domainCache = cache.NewMockDomainCache(s.controller) s.ctx = ctx } func (s *oauthSuite) TestCorrectPayload() { s.domainCache.EXPECT().GetDomain(s.att.DomainName).Return(s.domainEntry, nil).Times(1) authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.NoError(err) result, err := authorizer.Authorize(s.ctx, &s.att) s.NoError(err) s.Equal(result.Decision, DecisionAllow) } func (s *oauthSuite) TestItIsAdmin() { ctx := context.Background() ctx, call := encoding.NewInboundCall(ctx) // https://jwt.io/#debugger-io?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJTdWIiOiIxMjM0NTY3ODkwIiwiTmFtZSI6IkpvaG4gRG9lIiwiR3JvdXBzIjoiYSBiIGMiLCJBZG1pbiI6dHJ1ZSwiSWF0IjoxNjI3NTM4NzEyLCJUVEwiOjMwMDAwMDAwMH0.W_989GT8UWm-W7Hv0L2A3fND0Ly_CCuAdVMMoCs-l_GYxgxHP4_P5S9ejqh28AhUYllWNTRR_zM_hNakqnlufz09HP7mwlEKsxQrfoaycX20n8b7V-CktlysyVE2ZbCMt0Ef_MJF6bOOJ4JsayP6TQFXTP7QSUqNTpRYLZcBLlKHDZYm8uol_1EEs3kV5j3lP-WNcR18xBG0UIptakatm7aQEfPWOWnbRUpg9XVv3c4Bt8no4TW1z0XmFF9dD8vb2U-idPkPFstZwOZ0Ikn9nCt4W44kbeCC-i8uCe5SRiqNFWtvjnTBTVqXm27owT7ZbJwqvmMmhZ86Lz7eGtxgPQ&publicKey=-----BEGIN%20PUBLIC%20KEY-----%0AMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAscukltHilaq%2Bo5gIVE4P%0AGwWl%2BesvJ2EaEpWw6ogr98Un11YJ4oKkwIkLw4iIo0tveCINA3cZmxaW1RejRWKE%0AqYFtQ1rYd6BsnFAHXWh2R3A1FtpG6ANUEGkE7OAJe2%2FL42E%2FImJ%2BGQxRvartInDM%0AyfiRfB7%2BL2n3wG%2BNi%2BhBNMtAaX4Wwbj2hup21Jjuo96TuhcGImBFBATGWaYR2wqe%0A%2F6by9wJexPHlY%2F1uDp3SnzF1dCLjp76SGCfyYqOGC%2FPxhQi7mDxeH9%2FtIC%2Blt%2FSz%0Awc1n8gZLtlRlZHinvYa8lhWXqVYw6WD8h4LTgALq9iY%2BbeD1PFQSY1GkQtt0RhRw%0AeQIDAQAB%0A-----END%20PUBLIC%20KEY----- token := `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJTdWIiOiIxMjM0NTY3ODkwIiwiTmFtZSI6IkpvaG4gRG9lIiwiR3JvdXBzIjoiYSBiIGMiLCJBZG1pbiI6dHJ1ZSwiSWF0IjoxNjI3NTM4NzEyLCJUVEwiOjMwMDAwMDAwMH0.W_989GT8UWm-W7Hv0L2A3fND0Ly_CCuAdVMMoCs-l_GYxgxHP4_P5S9ejqh28AhUYllWNTRR_zM_hNakqnlufz09HP7mwlEKsxQrfoaycX20n8b7V-CktlysyVE2ZbCMt0Ef_MJF6bOOJ4JsayP6TQFXTP7QSUqNTpRYLZcBLlKHDZYm8uol_1EEs3kV5j3lP-WNcR18xBG0UIptakatm7aQEfPWOWnbRUpg9XVv3c4Bt8no4TW1z0XmFF9dD8vb2U-idPkPFstZwOZ0Ikn9nCt4W44kbeCC-i8uCe5SRiqNFWtvjnTBTVqXm27owT7ZbJwqvmMmhZ86Lz7eGtxgPQ` err := call.ReadFromRequest(&transport.Request{ Headers: transport.NewHeaders().With(common.AuthorizationTokenHeaderName, token), }) s.NoError(err) authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.NoError(err) result, err := authorizer.Authorize(ctx, &s.att) s.NoError(err) s.Equal(result.Decision, DecisionAllow) } func (s *oauthSuite) TestEmptyToken() { ctx := context.Background() ctx, call := encoding.NewInboundCall(ctx) err := call.ReadFromRequest(&transport.Request{ Headers: transport.NewHeaders().With(common.AuthorizationTokenHeaderName, ""), }) s.NoError(err) authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.NoError(err) s.logger.EXPECT().Debug("request is not authorized", gomock.Cond(func(t []tag.Tag) bool { return fmt.Sprintf("%v", t[0].Field().Interface) == "token is not set in header" })) result, _ := authorizer.Authorize(ctx, &s.att) s.Equal(result.Decision, DecisionDeny) } func (s *oauthSuite) TestGetDomainError() { s.domainCache.EXPECT().GetDomain(s.att.DomainName).Return(nil, fmt.Errorf("error")).Times(1) authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.NoError(err) result, err := authorizer.Authorize(s.ctx, &s.att) s.Equal(result.Decision, DecisionDeny) s.EqualError(err, "error") } func (s *oauthSuite) TestIncorrectPublicKey() { s.cfg.JwtCredentials.PublicKey = "incorrectPublicKey" authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.Equal(nil, authorizer) s.EqualError(err, "loading RSA public key: invalid public key path incorrectPublicKey") } func (s *oauthSuite) TestIncorrectAlgorithm() { s.cfg.JwtCredentials.Algorithm = "SHA256" authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.Equal(nil, authorizer) s.ErrorContains(err, "algorithm \"SHA256\" is not supported") } func (s *oauthSuite) TestMaxTTLLargerInToken() { s.cfg.MaxJwtTTL = 1 authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.NoError(err) s.logger.EXPECT().Debug("request is not authorized", gomock.Cond(func(t []tag.Tag) bool { return strings.HasPrefix(fmt.Sprintf("%v", t[0].Field().Interface), "token TTL:") })) result, _ := authorizer.Authorize(s.ctx, &s.att) s.Equal(result.Decision, DecisionDeny) } func (s *oauthSuite) TestIncorrectToken() { ctx := context.Background() ctx, call := encoding.NewInboundCall(ctx) err := call.ReadFromRequest(&transport.Request{ Headers: transport.NewHeaders().With(common.AuthorizationTokenHeaderName, "test"), }) s.NoError(err) authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.NoError(err) s.logger.EXPECT().Debug("request is not authorized", gomock.Cond(func(t []tag.Tag) bool { return fmt.Sprintf("%v", t[0].Field().Interface) == "token is malformed: token contains an invalid number of segments" })) result, _ := authorizer.Authorize(ctx, &s.att) s.Equal(result.Decision, DecisionDeny) } func (s *oauthSuite) TestIatExpiredToken() { ctx := context.Background() ctx, call := encoding.NewInboundCall(ctx) // https://jwt.io/#debugger-io?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJTdWIiOiIxMjM0NTY3ODkwIiwiTmFtZSI6IkpvaG4gRG9lIiwiR3JvdXBzIjoiYSBiIGMiLCJBZG1pbiI6ZmFsc2UsIklhdCI6MTYyNzUzODcxMiwiVFRMIjoxfQ.KLOkzV6sIBFCctbcbK98qT5v7ifL_H_6DAzkKsIE4124m5-LtVClA71o5ZtHuoZoiN2xwvGGnkOYg-LbrMajSjsixhGhgz0sAzAomufKACNX1eW9vB5onfTw2q26rpBz0vkIzBYFqUFor3BS30p0V_lnVQGYWRoIcDYspgTyDqMcJ_T77NVBlsyl6ISGiRdv_COcpMEqE_jse7ZKwuoNnQRQp97J3fapPXd6w6qB_PAPlZSXHikvIXG-_9o60RFcB8GDn1lvjZC1NUzGvM2CpVzS4r1_ViKjnjXMuWEPKOyNjQ6LBV9JkRx86N-6jy5V74OyXi-YkiSMplxAKY2G5g&publicKey=-----BEGIN%20PUBLIC%20KEY-----%0AMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAscukltHilaq%2Bo5gIVE4P%0AGwWl%2BesvJ2EaEpWw6ogr98Un11YJ4oKkwIkLw4iIo0tveCINA3cZmxaW1RejRWKE%0AqYFtQ1rYd6BsnFAHXWh2R3A1FtpG6ANUEGkE7OAJe2%2FL42E%2FImJ%2BGQxRvartInDM%0AyfiRfB7%2BL2n3wG%2BNi%2BhBNMtAaX4Wwbj2hup21Jjuo96TuhcGImBFBATGWaYR2wqe%0A%2F6by9wJexPHlY%2F1uDp3SnzF1dCLjp76SGCfyYqOGC%2FPxhQi7mDxeH9%2FtIC%2Blt%2FSz%0Awc1n8gZLtlRlZHinvYa8lhWXqVYw6WD8h4LTgALq9iY%2BbeD1PFQSY1GkQtt0RhRw%0AeQIDAQAB%0A-----END%20PUBLIC%20KEY----- token := `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJTdWIiOiIxMjM0NTY3ODkwIiwiTmFtZSI6IkpvaG4gRG9lIiwiR3JvdXBzIjoiYSBiIGMiLCJBZG1pbiI6ZmFsc2UsIklhdCI6MTYyNzUzODcxMiwiVFRMIjoxfQ.KLOkzV6sIBFCctbcbK98qT5v7ifL_H_6DAzkKsIE4124m5-LtVClA71o5ZtHuoZoiN2xwvGGnkOYg-LbrMajSjsixhGhgz0sAzAomufKACNX1eW9vB5onfTw2q26rpBz0vkIzBYFqUFor3BS30p0V_lnVQGYWRoIcDYspgTyDqMcJ_T77NVBlsyl6ISGiRdv_COcpMEqE_jse7ZKwuoNnQRQp97J3fapPXd6w6qB_PAPlZSXHikvIXG-_9o60RFcB8GDn1lvjZC1NUzGvM2CpVzS4r1_ViKjnjXMuWEPKOyNjQ6LBV9JkRx86N-6jy5V74OyXi-YkiSMplxAKY2G5g` err := call.ReadFromRequest(&transport.Request{ Headers: transport.NewHeaders().With(common.AuthorizationTokenHeaderName, token), }) s.NoError(err) authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.NoError(err) s.logger.EXPECT().Debug("request is not authorized", gomock.Cond(func(t []tag.Tag) bool { return fmt.Sprintf("%v", t[0].Field().Interface) == "token is expired" })) result, _ := authorizer.Authorize(ctx, &s.att) s.Equal(result.Decision, DecisionDeny) } func (s *oauthSuite) TestDifferentGroup() { s.domainEntry.GetInfo().Data[constants.DomainDataKeyForReadGroups] = "AdifferentGroup" s.domainCache.EXPECT().GetDomain(s.att.DomainName).Return(s.domainEntry, nil).Times(1) s.att.Permission = PermissionWrite authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.NoError(err) s.logger.EXPECT().Debug("request is not authorized", gomock.Cond(func(t []tag.Tag) bool { return fmt.Sprintf("%v", t[0].Field().Interface) == "token doesn't have the right permission, jwt groups: [a b c], allowed groups: map[]" })) result, _ := authorizer.Authorize(s.ctx, &s.att) s.Equal(result.Decision, DecisionDeny) } func (s *oauthSuite) TestExternalProviderWithoutJWKSWillFail() { authorizer, err := NewOAuthAuthorizer(s.providerCfg, s.logger, s.domainCache) s.Error(err) s.Equal(nil, authorizer) } func (s *oauthSuite) TestIncorrectPermission() { s.domainCache.EXPECT().GetDomain(s.att.DomainName).Return(s.domainEntry, nil).Times(1) s.att.Permission = Permission(15) authorizer, err := NewOAuthAuthorizer(s.cfg, s.logger, s.domainCache) s.NoError(err) s.logger.EXPECT().Debug("request is not authorized", gomock.Cond(func(t []tag.Tag) bool { return fmt.Sprintf("%v", t[0].Field().Interface) == "permission 15 is not supported" })) result, err := authorizer.Authorize(s.ctx, &s.att) s.NoError(err) s.Equal(result.Decision, DecisionDeny) } func Test_oauthAuthority_validateTTL(t *testing.T) { tests := []struct { name string claims *JWTClaims ttlConfig int64 wantErr assert.ErrorAssertionFunc }{ { name: "Empty claims will fail TTL validation", claims: &JWTClaims{}, wantErr: assert.Error, }, { name: "Claims with IAT and Claim TTL will pass", claims: &JWTClaims{ TTL: 300, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(time.Now()), }, }, wantErr: assert.NoError, ttlConfig: 500, }, { name: "Claims with IAT but without TTL or ExpiresAT will fail TTL validation", claims: &JWTClaims{ RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(time.Now().Add(-time.Minute)), }, }, ttlConfig: 1, wantErr: assert.Error, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { validator := &oauthAuthority{ config: config.OAuthAuthorizer{MaxJwtTTL: tt.ttlConfig}, } tt.wantErr(t, validator.validateTTL(tt.claims), fmt.Sprintf("validateTTL(%v)", tt.claims)) }) } } func TestIsTokenInternal(t *testing.T) { internalToken := &jwt.Token{ Header: map[string]interface{}{}, } internalTokenWithKid := &jwt.Token{ Header: map[string]interface{}{ "kid": jwtInternalIssuer, }, Claims: JWTClaims{ RegisteredClaims: jwt.RegisteredClaims{ Issuer: jwtInternalIssuer, }, }, } externalToken := &jwt.Token{ Header: map[string]interface{}{ "kid": "3lkj323jkj3", }, Claims: JWTClaims{ RegisteredClaims: jwt.RegisteredClaims{ Issuer: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_hNqHxsxaM", }, }, } tests := []struct { name string token *jwt.Token want bool }{ { name: "internal token w/o kid", token: internalToken, want: true, }, { name: "internal token with kid", token: internalTokenWithKid, want: true, }, { name: "external token with kid", token: externalToken, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equalf(t, tt.want, isTokenInternal(tt.token), "isTokenInternal(%v)", tt.token) }) } } func Test_oauthAuthority_parseExternal(t *testing.T) { claim := map[string]interface{}{"cognito:groups": []interface{}{"domain2", "domain1", "group1"}} tests := []struct { name string config config.OAuthAuthorizer mapToken map[string]interface{} claims *JWTClaims wantGroups string wantAdmin bool wantErr assert.ErrorAssertionFunc }{ { name: "empty config will not alter token", config: config.OAuthAuthorizer{ Provider: &config.OAuthProvider{ GroupsAttributePath: "", AdminAttributePath: "", }, }, wantErr: assert.NoError, }, { name: "groups incorrect path will result into an error", config: config.OAuthAuthorizer{ Provider: &config.OAuthProvider{ GroupsAttributePath: "/bad/path", AdminAttributePath: "", }, }, wantErr: assert.Error, }, { name: "admin incorrect path will result into an error", config: config.OAuthAuthorizer{ Provider: &config.OAuthProvider{ GroupsAttributePath: "", AdminAttributePath: "/bad/path", }, }, wantErr: assert.Error, }, { name: "correct groups path will fill claims", config: config.OAuthAuthorizer{ Provider: &config.OAuthProvider{ GroupsAttributePath: "\"cognito:groups\" | join(' ', @)", }, }, mapToken: claim, wantErr: assert.NoError, wantGroups: "domain2 domain1 group1", wantAdmin: false, }, { name: "correct admin path will fill claims", config: config.OAuthAuthorizer{ Provider: &config.OAuthProvider{ AdminAttributePath: "\"cognito:groups\" | contains(@, 'group1')", }, }, mapToken: claim, wantErr: assert.NoError, wantGroups: "", wantAdmin: true, }, { name: "non bool result for admin will result in error", config: config.OAuthAuthorizer{ Provider: &config.OAuthProvider{ AdminAttributePath: "\"cognito:groups\"", }, }, mapToken: claim, wantErr: assert.Error, wantGroups: "", wantAdmin: false, }, { name: "non string result for groups will result in error", config: config.OAuthAuthorizer{ Provider: &config.OAuthProvider{ GroupsAttributePath: "\"cognito:groups\" | contains(@, 'group1')", }, }, mapToken: claim, wantErr: assert.Error, wantGroups: "", wantAdmin: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { a := &oauthAuthority{ config: tt.config, } actualClaim := &JWTClaims{} err := a.parseExternal(tt.mapToken, actualClaim) tt.wantErr(t, err) assert.Equal(t, tt.wantGroups, actualClaim.Groups) assert.Equal(t, tt.wantAdmin, actualClaim.Admin) }) } } ================================================ FILE: common/authorization/scram_client.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package authorization import ( "crypto/sha256" "crypto/sha512" "hash" "github.com/xdg/scram" ) // NOTE: the code is copied from https://github.com/IBM/sarama/blob/master/examples/sasl_scram_client/scram_client.go // SHA256 algorithm var SHA256 scram.HashGeneratorFcn = func() hash.Hash { return sha256.New() } // SHA512 algorithm var SHA512 scram.HashGeneratorFcn = func() hash.Hash { return sha512.New() } // XDGSCRAMClient is the scram client type XDGSCRAMClient struct { *scram.Client *scram.ClientConversation scram.HashGeneratorFcn } // Begin creates new client func (x *XDGSCRAMClient) Begin(userName, password, authzID string) (err error) { x.Client, err = x.HashGeneratorFcn.NewClient(userName, password, authzID) if err != nil { return err } x.ClientConversation = x.Client.NewConversation() return nil } // Step takes a string provided from a server (or just an empty string for the // very first conversation step) and attempts to move the authentication // conversation forward. It returns a string to be sent to the server or an // error if the server message is invalid. Calling Step after a conversation // completes is also an error. func (x *XDGSCRAMClient) Step(challenge string) (response string, err error) { response, err = x.ClientConversation.Step(challenge) return } // Done stops the client func (x *XDGSCRAMClient) Done() bool { return x.ClientConversation.Done() } ================================================ FILE: common/authorization/test_result ================================================ ================================================ FILE: common/backoff/cron.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package backoff import ( "fmt" "math" "math/rand" "time" "github.com/robfig/cron/v3" "github.com/uber/cadence/common/types" ) // NoBackoff is used to represent backoff when no cron backoff is needed const NoBackoff = time.Duration(-1) // ValidateSchedule validates a cron schedule spec func ValidateSchedule(cronSchedule string) (cron.Schedule, error) { sched, err := cron.ParseStandard(cronSchedule) if err != nil { return nil, &types.BadRequestError{ Message: fmt.Sprintf("Invalid CronSchedule, failed to parse: %q, err: %v", cronSchedule, err), } } // schedule must parse and there must be a next-firing date (catches impossible dates like Feb 30) next := sched.Next(time.Now()) if next.IsZero() { return nil, &types.BadRequestError{ Message: fmt.Sprintf("Invalid CronSchedule, no next firing time found, maybe impossible date: %q", cronSchedule), } } return sched, nil } // GetBackoffForNextSchedule calculates the backoff time for the next run given // a cronSchedule, workflow start time and workflow close time func GetBackoffForNextSchedule( sched cron.Schedule, startTime time.Time, closeTime time.Time, jitterStartSeconds int32, cronOverlapPolicy types.CronOverlapPolicy, ) (time.Duration, error) { startUTCTime := startTime.In(time.UTC) closeUTCTime := closeTime.In(time.UTC) nextScheduleTime := sched.Next(startUTCTime) roundedInterval := time.Duration(0) if nextScheduleTime.IsZero() { // this should only occur for bad specs, e.g. impossible dates like Feb 30, // which should be prevented from being saved by the valid check. return NoBackoff, fmt.Errorf("invalid CronSchedule, no next firing time found") } if nextScheduleTime.Before(closeUTCTime) { // Cron overlap policy only applies if there were runs skipped switch cronOverlapPolicy { case types.CronOverlapPolicySkipped: for nextScheduleTime.Before(closeUTCTime) { nextScheduleTime = sched.Next(nextScheduleTime) if nextScheduleTime.IsZero() { // this should only occur for bad specs, e.g. impossible dates like Feb 30, // which should be prevented from being saved by the valid check. return NoBackoff, fmt.Errorf("invalid CronSchedule, no next firing time found") } } backoffInterval := nextScheduleTime.Sub(closeUTCTime) roundedInterval = time.Second * time.Duration(math.Ceil(backoffInterval.Seconds())) case types.CronOverlapPolicyBufferOne: // we want to start the next run as soon as possible, so we don't need to buffer roundedInterval = time.Duration(0) } } else { backoffInterval := nextScheduleTime.Sub(closeUTCTime) roundedInterval = time.Second * time.Duration(math.Ceil(backoffInterval.Seconds())) } var jitter time.Duration if jitterStartSeconds > 0 { jitter = time.Duration(rand.Int31n(jitterStartSeconds+1)) * time.Second } return roundedInterval + jitter, nil } // GetBackoffForNextScheduleInSeconds calculates the backoff time in seconds for the // next run given a cronSchedule and current time func GetBackoffForNextScheduleInSeconds( cronSchedule string, startTime time.Time, closeTime time.Time, jitterStartSeconds int32, overlapPolicy types.CronOverlapPolicy, ) (int32, error) { sched, err := ValidateSchedule(cronSchedule) if err != nil { return 0, err } backoffDuration, err := GetBackoffForNextSchedule(sched, startTime, closeTime, jitterStartSeconds, overlapPolicy) if err != nil { return 0, err } return int32(math.Ceil(backoffDuration.Seconds())), nil } ================================================ FILE: common/backoff/cron_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package backoff import ( "fmt" "math/rand" "strconv" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/types" ) func TestCron(t *testing.T) { var crontests = []struct { cron string startTime string endTime string result time.Duration }{ {"0 10 * * *", "2018-12-17T08:00:00-08:00", "", time.Hour * 18}, {"0 10 * * *", "2018-12-18T02:00:00-08:00", "", time.Hour * 24}, {"0 * * * *", "2018-12-17T08:08:00+00:00", "", time.Minute * 52}, {"0 * * * *", "2018-12-17T09:00:00+00:00", "", time.Hour}, {"* * * * *", "2018-12-17T08:08:18+00:00", "", time.Second * 42}, {"0 * * * *", "2018-12-17T09:00:00+00:00", "", time.Minute * 60}, {"0 10 * * *", "2018-12-17T08:00:00+00:00", "2018-12-20T00:00:00+00:00", time.Hour * 10}, {"0 10 * * *", "2018-12-17T08:00:00+00:00", "2018-12-17T09:00:00+00:00", time.Hour}, {"*/10 * * * *", "2018-12-17T00:04:00+00:00", "2018-12-17T01:02:00+00:00", time.Minute * 8}, {"invalid-cron-spec", "2018-12-17T00:04:00+00:00", "2018-12-17T01:02:00+00:00", NoBackoff}, {"@every 5h", "2018-12-17T08:00:00+00:00", "2018-12-17T09:00:00+00:00", time.Hour * 4}, {"@every 5h", "2018-12-17T08:00:00+00:00", "2018-12-18T00:00:00+00:00", time.Hour * 4}, {"0 3 * * 0-6", "2018-12-17T08:00:00-08:00", "", time.Hour * 11}, // At 16:05 East Coast (Day light saving on) {"CRON_TZ=America/New_York 5 16 * * *", "2021-03-14T00:00:00-04:00", "2021-03-14T15:05:00-04:00", time.Hour * 1}, // At 04:05 East Coast (Day light saving off) {"CRON_TZ=America/New_York 5 4 * * *", "2021-11-25T00:00:00-05:00", "2021-11-25T03:05:00-05:00", time.Hour * 1}, } for idx, tt := range crontests { t.Run(strconv.Itoa(idx), func(t *testing.T) { start, _ := time.Parse(time.RFC3339, tt.startTime) end := start if tt.endTime != "" { end, _ = time.Parse(time.RFC3339, tt.endTime) } sched, err := ValidateSchedule(tt.cron) if tt.result == NoBackoff { // no backoff == error, simplifies the test-struct a bit require.ErrorContains(t, err, "Invalid CronSchedule") } else { require.NoError(t, err) backoff, err := GetBackoffForNextSchedule(sched, start, end, 0, types.CronOverlapPolicySkipped) require.NoError(t, err) assert.Equal(t, tt.result, backoff, "The cron spec is %s and the expected result is %s", tt.cron, tt.result) } }) } } func TestCronWithJitterStart(t *testing.T) { var cronWithJitterStartTests = []struct { cron string startTime string jitterStartSeconds int32 expectedResultSeconds time.Duration expectedResultSeconds2 time.Duration }{ // Note that the cron scheduler we use (robfig) schedules differently depending on the syntax: // 1) * * * syntax : next run is scheduled on the first second of each minute, starting at next minute // 2) @every X syntax: next run is scheduled X seconds from the time passed in to Next() call. {"* * * * *", "2018-12-17T08:00:00+00:00", 10, time.Second * 60, time.Second * 60}, {"* * * * *", "2018-12-17T08:00:10+00:00", 30, time.Second * 50, time.Second * 60}, {"* * * * *", "2018-12-17T08:00:25+00:00", 15, time.Second * 35, time.Second * 60}, {"* * * * *", "2018-12-17T08:00:45+00:00", 0, time.Second * 15, time.Second * 60}, {"@every 60s", "2018-12-17T08:00:45+00:00", 0, time.Second * 60, time.Second * 60}, {"* * * * *", "2018-12-17T08:00:45+00:00", 45, time.Second * 15, time.Second * 60}, {"@every 60s", "2018-12-17T08:00:45+00:00", 45, time.Second * 60, time.Second * 60}, {"* * * * *", "2018-12-17T08:00:00+00:00", 70, time.Second * 60, time.Second * 60}, {"@every 20s", "2018-12-17T08:00:00+00:00", 15, time.Second * 20, time.Second * 20}, {"@every 10s", "2018-12-17T08:00:09+00:00", 0, time.Second * 10, time.Second * 10}, {"@every 20s", "2018-12-17T08:00:09+00:00", 15, time.Second * 20, time.Second * 20}, {"* * * * *", "0001-01-01T00:00:00+00:00", 0, time.Second * 60, time.Second * 60}, {"@every 20s", "0001-01-01T00:00:00+00:00", 0, time.Second * 20, time.Second * 20}, } rand.Seed(int64(time.Now().Nanosecond())) for idx, tt := range cronWithJitterStartTests { t.Run(strconv.Itoa(idx), func(t *testing.T) { exactCount := 0 start, _ := time.Parse(time.RFC3339, tt.startTime) end := start sched, err := ValidateSchedule(tt.cron) if tt.expectedResultSeconds != NoBackoff { assert.NoError(t, err) } backoff, err := GetBackoffForNextSchedule(sched, start, end, tt.jitterStartSeconds, types.CronOverlapPolicySkipped) require.NoError(t, err) fmt.Printf("Backoff time for test %d = %v\n", idx, backoff) delta := time.Duration(tt.jitterStartSeconds) * time.Second expectedResultTime := start.Add(tt.expectedResultSeconds) backoffTime := start.Add(backoff) assert.WithinDuration(t, expectedResultTime, backoffTime, delta, "The test specs are %v and the expected result in seconds is between %s and %s", tt, tt.expectedResultSeconds, tt.expectedResultSeconds+delta) if expectedResultTime == backoffTime { exactCount++ } // Also check next X cron times caseCount := 5 for i := 1; i < caseCount; i++ { startTime := expectedResultTime backoff, err := GetBackoffForNextSchedule(sched, startTime, startTime, tt.jitterStartSeconds, types.CronOverlapPolicySkipped) require.NoError(t, err) expectedResultTime := startTime.Add(tt.expectedResultSeconds2) backoffTime := startTime.Add(backoff) assert.WithinDuration(t, expectedResultTime, backoffTime, delta, "Iteration %d: The test specs are %v and the expected result in seconds is between %s and %s", i, tt, tt.expectedResultSeconds, tt.expectedResultSeconds+delta) if expectedResultTime == backoffTime { exactCount++ } } // If jitter is > 0, we want to detect whether jitter is being applied - BUT we don't want the test // to be flaky if the code randomly chooses a jitter of 0, so we try to have enough data points by // checking the next X cron times AND by choosing a jitter thats not super low. if tt.jitterStartSeconds > 0 && exactCount == caseCount { // Test to make sure a jitter test case sometimes doesn't get exact values t.Fatalf("FAILED to jitter properly? Test specs = %v\n", tt) } else if tt.jitterStartSeconds == 0 && exactCount != caseCount { // Test to make sure a non-jitter test case always gets exact values t.Fatalf("Jittered when we weren't supposed to? Test specs = %v\n", tt) } }) } } func TestCronWithoutBufferOneCronWorkflow(t *testing.T) { var bufferOneCronWorkflowTests = []struct { startTime string closeTime string expectedBackoff time.Duration description string }{ { startTime: "2018-12-17T10:00:00+00:00", closeTime: "2018-12-17T11:00:00+00:00", expectedBackoff: time.Hour * 23, description: "Next schedule is next day at 10:00", }, { startTime: "2018-12-17T10:00:00+00:00", closeTime: "2018-12-18T10:45:00+00:00", expectedBackoff: time.Hour*23 + time.Minute*15, description: "Skip that run and schedule at next possible time", }, } for idx, tt := range bufferOneCronWorkflowTests { t.Run(fmt.Sprintf("%d_%s", idx, tt.description), func(t *testing.T) { start, err := time.Parse(time.RFC3339, tt.startTime) require.NoError(t, err) close, err := time.Parse(time.RFC3339, tt.closeTime) require.NoError(t, err) sched, err := ValidateSchedule("0 10 * * *") require.NoError(t, err) backoff, err := GetBackoffForNextSchedule(sched, start, close, 0, types.CronOverlapPolicySkipped) require.NoError(t, err) assert.Equal(t, tt.expectedBackoff, backoff, "Test case %d failed: %s\nStart: %s\nClose: %s", idx, tt.description, tt.startTime, tt.closeTime) }) } } func TestCronWithBufferOneCronWorkflow(t *testing.T) { var bufferOneCronWorkflowTests = []struct { startTime string closeTime string expectedBackoff time.Duration description string }{ { startTime: "2018-12-17T10:00:00+00:00", closeTime: "2018-12-17T11:00:00+00:00", expectedBackoff: time.Hour * 23, description: "Next schedule is next day at 10:00", }, { startTime: "2018-12-17T10:00:00+00:00", closeTime: "2018-12-18T10:45:00+00:00", expectedBackoff: 0, description: "Start immediately after previous close time", }, { startTime: "2018-12-17T10:00:00+00:00", closeTime: "2018-12-17T10:30:00+00:00", expectedBackoff: time.Hour*23 + time.Minute*30, description: "Close time is after schedule time", }, { startTime: "2018-12-17T10:00:00+00:00", closeTime: "2018-12-30T10:45:00+00:00", expectedBackoff: 0, description: "Start immediately after previous close time even if previous one skipped multiple runs", }, } for idx, tt := range bufferOneCronWorkflowTests { t.Run(fmt.Sprintf("%d_%s", idx, tt.description), func(t *testing.T) { start, err := time.Parse(time.RFC3339, tt.startTime) require.NoError(t, err) close, err := time.Parse(time.RFC3339, tt.closeTime) require.NoError(t, err) sched, err := ValidateSchedule("0 10 * * *") require.NoError(t, err) backoff, err := GetBackoffForNextSchedule(sched, start, close, 0, types.CronOverlapPolicyBufferOne) require.NoError(t, err) assert.Equal(t, tt.expectedBackoff, backoff, "Test case %d failed: %s\nStart: %s\nClose: %s", idx, tt.description, tt.startTime, tt.closeTime) }) } } ================================================ FILE: common/backoff/jitter.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package backoff import ( "math/rand" "time" ) // JitDuration return random duration from (1-coefficient)*duration to (1+coefficient)*duration, inclusive, exclusive func JitDuration(duration time.Duration, coefficient float64) time.Duration { validateCoefficient(coefficient) return time.Duration(JitInt64(duration.Nanoseconds(), coefficient)) } // JitInt64 return random number from (1-coefficient)*input to (1+coefficient)*input, inclusive, exclusive func JitInt64(input int64, coefficient float64) int64 { validateCoefficient(coefficient) if coefficient == 0 || input == 0 { return input } base := int64(float64(input) * (1 - coefficient)) addon := rand.Int63n(2 * (input - base)) return base + addon } // JitFloat64 return random number from (1-coefficient)*input to (1+coefficient)*input, inclusive, exclusive func JitFloat64(input float64, coefficient float64) float64 { validateCoefficient(coefficient) if coefficient == 0 || input == 0 { return input } base := input * (1 - coefficient) addon := rand.Float64() * 2 * (input - base) return base + addon } func validateCoefficient(coefficient float64) { if coefficient < 0 || coefficient > 1 { panic("coefficient cannot be < 0 or > 1") } } ================================================ FILE: common/backoff/jitter_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package backoff import ( "log" "math/rand" "os" "testing" "time" "github.com/stretchr/testify/suite" ) type ( jitterSuite struct { suite.Suite } ) func TestJitterSuite(t *testing.T) { s := new(jitterSuite) suite.Run(t, s) } func (s *jitterSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } func (s *jitterSuite) TestJitInt64() { input := int64(1048576) coefficient := float64(0.25) lowerBound := int64(float64(input) * (1 - coefficient)) upperBound := int64(float64(input) * (1 + coefficient)) for i := 0; i < 1048576; i++ { result := JitInt64(input, coefficient) s.True(result >= lowerBound) s.True(result < upperBound) } } func (s *jitterSuite) TestJitInt64WithZeroCoefficient() { for i := 0; i < 1048576; i++ { input := rand.Int63() s.Equal(input, JitInt64(input, 0)) } } func (s *jitterSuite) TestJitInt64WithZeroInput() { s.Equal(int64(0), JitInt64(0, 0.5)) } func (s *jitterSuite) TestJitFloat64() { input := float64(1048576.1048576) coefficient := float64(0.16) lowerBound := float64(input) * (1 - coefficient) upperBound := float64(input) * (1 + coefficient) for i := 0; i < 1048576; i++ { result := JitFloat64(input, coefficient) s.True(result >= lowerBound) s.True(result < upperBound) } } func (s *jitterSuite) TestJitFloat64WithZeroCoefficient() { for i := 0; i < 1048576; i++ { input := rand.Float64() s.Equal(input, JitFloat64(input, 0)) } } func (s *jitterSuite) TestJitFloat64WithZeroInput() { s.Equal(float64(0), JitFloat64(0, 0.5)) } func (s *jitterSuite) TestJitDuration() { input := time.Duration(1099511627776) coefficient := float64(0.1) lowerBound := time.Duration(int64(float64(input.Nanoseconds()) * (1 - coefficient))) upperBound := time.Duration(int64(float64(input.Nanoseconds()) * (1 + coefficient))) for i := 0; i < 1048576; i++ { result := JitDuration(input, coefficient) s.True(result >= lowerBound) s.True(result < upperBound) } } func (s *jitterSuite) TestJitDurationWithZeroCoefficient() { for i := 0; i < 1048576; i++ { input := time.Duration(rand.Int63()) s.Equal(input, JitDuration(input, 0)) } } ================================================ FILE: common/backoff/retry.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package backoff import ( "context" "errors" "sync" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/types" ) type retryCountKeyType string const retryCountKey = retryCountKeyType("retryCount") var errCauseOperationTimeout = errors.New("operation timeout") type ( // Operation to retry Operation func(ctx context.Context) error // IsRetryable handler can be used to exclude certain errors during retry IsRetryable func(error) bool // ConcurrentRetrier is used for client-side throttling. It determines whether to // throttle outgoing traffic in case downstream backend server rejects // requests due to out-of-quota or server busy errors. ConcurrentRetrier struct { sync.Mutex retrier Retrier // Backoff retrier failureCount int64 // Number of consecutive failures seen } // ThrottleRetryOption sets the options of ThrottleRetry ThrottleRetryOption func(*ThrottleRetry) // ThrottleRetry is used to run operation with retry and also avoid throttling dependencies ThrottleRetry struct { policy RetryPolicy isRetryable IsRetryable throttlePolicy RetryPolicy isThrottle IsRetryable clock clock.TimeSource operationTimeout time.Duration expireContext bool } ) // Throttle Sleep if there were failures since the last success call. func (c *ConcurrentRetrier) Throttle() { c.throttleInternal() } func (c *ConcurrentRetrier) throttleInternal() time.Duration { next := done // Check if we have failure count. failureCount := c.failureCount if failureCount > 0 { defer c.Unlock() c.Lock() if c.failureCount > 0 { next = c.retrier.NextBackOff() } } if next != done { time.Sleep(next) } return next } // Succeeded marks client request succeeded. func (c *ConcurrentRetrier) Succeeded() { defer c.Unlock() c.Lock() c.failureCount = 0 c.retrier.Reset() } // Failed marks client request failed because backend is busy. func (c *ConcurrentRetrier) Failed() { defer c.Unlock() c.Lock() c.failureCount++ } // NewConcurrentRetrier returns an instance of concurrent backoff retrier. func NewConcurrentRetrier(retryPolicy RetryPolicy) *ConcurrentRetrier { retrier := NewRetrier(retryPolicy, clock.NewRealTimeSource()) return &ConcurrentRetrier{retrier: retrier} } // NewThrottleRetry returns a retry handler with given options func NewThrottleRetry(opts ...ThrottleRetryOption) *ThrottleRetry { retryPolicy := NewExponentialRetryPolicy(50 * time.Millisecond) retryPolicy.SetMaximumInterval(2 * time.Second) throttlePolicy := NewExponentialRetryPolicy(time.Second) throttlePolicy.SetMaximumInterval(10 * time.Second) throttlePolicy.SetExpirationInterval(NoInterval) tr := &ThrottleRetry{ policy: retryPolicy, isRetryable: func(_ error) bool { return false }, throttlePolicy: throttlePolicy, isThrottle: func(err error) bool { _, ok := err.(*types.ServiceBusyError) return ok }, clock: clock.NewRealTimeSource(), } for _, opt := range opts { opt(tr) } return tr } // WithRetryPolicy returns a setter setting the retry policy of ThrottleRetry func WithRetryPolicy(policy RetryPolicy) ThrottleRetryOption { return func(tr *ThrottleRetry) { tr.policy = policy } } // WithThrottlePolicy returns setter setting the retry policy when operation returns throttle error func WithThrottlePolicy(throttlePolicy RetryPolicy) ThrottleRetryOption { return func(tr *ThrottleRetry) { tr.throttlePolicy = throttlePolicy } } // WithRetryableError returns a setter setting the retryable error of ThrottleRetry func WithRetryableError(isRetryable IsRetryable) ThrottleRetryOption { return func(tr *ThrottleRetry) { tr.isRetryable = isRetryable } } // WithThrottleError returns a setter setting the throttle error of ThrottleRetry func WithThrottleError(isThrottle IsRetryable) ThrottleRetryOption { return func(tr *ThrottleRetry) { tr.isThrottle = isThrottle } } // WithOperationTimeout establishes a timeout for each attempt of the Operation, running each in a child context that // will be cancelled after the specified operationTimeout. // Failures caused by the operationTimeout are considered retryable so long as the Operation returns an error that // is/wraps a context.DeadlineExceeded. // By default, there is no deadline for each operation. func WithOperationTimeout(operationTimeout time.Duration) ThrottleRetryOption { return func(tr *ThrottleRetry) { tr.operationTimeout = operationTimeout } } // WithContextExpiration causes the ThrottleRetry to run operations within a child context with a deadline equivalent // to the RetryPolicy.Expiration. This ensures that across all attempts, the operations may not exceed the deadline // of the RetryPolicy. // By default, the RetryPolicy is only enforced by not starting additional attempts once it has expired. func WithContextExpiration() ThrottleRetryOption { return func(tr *ThrottleRetry) { tr.expireContext = true } } func WithClock(clock clock.TimeSource) ThrottleRetryOption { return func(tr *ThrottleRetry) { tr.clock = clock } } // Do function can be used to wrap any call with retry logic func (tr *ThrottleRetry) Do(ctx context.Context, op Operation) error { var prevErr error var err error var next time.Duration r := NewRetrier(tr.policy, tr.clock) t := NewRetrier(tr.throttlePolicy, tr.clock) // If enabled and the RetryPolicy has an expiration, enforce it via context timeout if expirationInterval := tr.policy.Expiration(); tr.expireContext && expirationInterval > 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, expirationInterval) defer cancel() } retryCount := 0 for { // record the previous error before an operation prevErr = err // Avoid shadowing err var attemptTimedOut bool attemptTimedOut, err = tr.attempt(ctx, retryCount, op) // operation completed successfully. No need to retry. if err == nil { return nil } retryCount++ // Check if the error is retryable // Attempts timing out is considered retryable if !attemptTimedOut && !tr.isRetryable(err) { // The returned error will be preferred to a previous one if one exists. That's because the // very last error is very likely a timeout error, and it's not useful for logging/troubleshooting if prevErr != nil { return prevErr } return err } if next = r.NextBackOff(); next == done { if prevErr != nil { return prevErr } return err } // check if the error is a throttle error if tr.isThrottle(err) { throttleBackOff := t.NextBackOff() if throttleBackOff != done && throttleBackOff > next { next = throttleBackOff } } select { case <-ctx.Done(): if prevErr != nil { return prevErr } return err case <-tr.clock.After(next): } } } func (tr *ThrottleRetry) attempt(ctx context.Context, retryCount int, op Operation) (bool, error) { // update context with retry count ctx = context.WithValue(ctx, retryCountKey, retryCount) // If configured with an operation timeout, set it on the context if tr.operationTimeout > 0 { // Avoid shadowing ctx var cancel context.CancelFunc ctx, cancel = context.WithTimeoutCause(ctx, tr.operationTimeout, errCauseOperationTimeout) defer cancel() } opErr := op(ctx) // Confirm that the context was cancelled by the above timeout // Validating the cause ensures that any other Context cancellation (incoming timeout, explicit cancel) doesn't // get treated as an attempt timeout. // Validating the returned error is/wraps a DeadlineExceeded adds confidence that it was caused by the context // timing out. if cause := context.Cause(ctx); errors.Is(cause, errCauseOperationTimeout) && errors.Is(opErr, context.DeadlineExceeded) { return true, opErr } return false, opErr } // IgnoreErrors can be used as IsRetryable handler for Retry function to exclude certain errors from the retry list func IgnoreErrors(errorsToExclude []error) func(error) bool { return func(err error) bool { for _, errorToExclude := range errorsToExclude { if err == errorToExclude { return false } } return true } } ================================================ FILE: common/backoff/retry_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package backoff import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type ( RetrySuite struct { *require.Assertions // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, not merely log an error suite.Suite } someError struct{} ) func TestRetrySuite(t *testing.T) { suite.Run(t, new(RetrySuite)) } func (s *RetrySuite) SetupTest() { s.Assertions = require.New(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil } func (s *RetrySuite) TestRetrySuccess() { i := 0 op := func(ctx context.Context) error { i++ if i == 5 { return nil } return &someError{} } policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetMaximumInterval(5 * time.Millisecond) policy.SetMaximumAttempts(10) throttleRetry := NewThrottleRetry( WithRetryPolicy(policy), WithRetryableError(func(_ error) bool { return true }), ) err := throttleRetry.Do(context.Background(), op) s.NoError(err) s.Equal(5, i) } func (s *RetrySuite) TestRetryFailed() { i := 0 op := func(ctx context.Context) error { i++ if i == 7 { return nil } return &someError{} } policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetMaximumInterval(5 * time.Millisecond) policy.SetMaximumAttempts(5) throttleRetry := NewThrottleRetry( WithRetryPolicy(policy), WithRetryableError(func(_ error) bool { return true }), ) err := throttleRetry.Do(context.Background(), op) s.Error(err) } func (s *RetrySuite) TestRetryFailedReturnPreviousError() { i := 0 the5thError := fmt.Errorf("this is the error of the 5th attempt(4th retry attempt)") op := func(ctx context.Context) error { i++ if i == 5 { return the5thError } if i == 7 { return nil } return &someError{} } policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetMaximumInterval(5 * time.Millisecond) // Note that this is retry attempts(maybe it should be renamed to SetMaximumRetryAttempts), // so the total attempts is 5+1=6 policy.SetMaximumAttempts(5) throttleRetry := NewThrottleRetry( WithRetryPolicy(policy), WithRetryableError(func(_ error) bool { return true }), ) err := throttleRetry.Do(context.Background(), op) s.Error(err) s.Equal(6, i) s.Equal(the5thError, err) } func (s *RetrySuite) TestIsRetryableSuccess() { i := 0 op := func(ctx context.Context) error { i++ if i == 5 { return nil } return &someError{} } isRetryable := func(err error) bool { if _, ok := err.(*someError); ok { return true } return false } policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetMaximumInterval(5 * time.Millisecond) policy.SetMaximumAttempts(10) throttleRetry := NewThrottleRetry( WithRetryPolicy(policy), WithRetryableError(isRetryable), ) err := throttleRetry.Do(context.Background(), op) s.NoError(err, "Retry count: %v", i) s.Equal(5, i) } func (s *RetrySuite) TestIsRetryableFailure() { i := 0 op := func(ctx context.Context) error { i++ if i == 5 { return nil } return &someError{} } policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetMaximumInterval(5 * time.Millisecond) policy.SetMaximumAttempts(10) throttleRetry := NewThrottleRetry( WithRetryPolicy(policy), WithRetryableError(IgnoreErrors([]error{&someError{}})), ) err := throttleRetry.Do(context.Background(), op) s.Error(err) s.Equal(1, i) } func (s *RetrySuite) TestRetryExpired() { i := 0 op := func(ctx context.Context) error { i++ time.Sleep(time.Second) return &someError{} } policy := NewExponentialRetryPolicy(10 * time.Millisecond) policy.SetExpirationInterval(NoInterval) throttleRetry := NewThrottleRetry( WithRetryPolicy(policy), WithRetryableError(func(_ error) bool { return true }), ) ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) defer cancel() err := throttleRetry.Do(ctx, op) s.Error(err) s.Equal(&someError{}, err) s.Equal(1, i) } func (s *RetrySuite) TestRetryExpiredReturnPreviousError() { i := 0 prevErr := fmt.Errorf("previousError") op := func(ctx context.Context) error { i++ if i == 1 { return prevErr } time.Sleep(time.Second) return &someError{} } policy := NewExponentialRetryPolicy(10 * time.Millisecond) policy.SetExpirationInterval(NoInterval) throttleRetry := NewThrottleRetry( WithRetryPolicy(policy), WithRetryableError(func(_ error) bool { return true }), ) ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() err := throttleRetry.Do(ctx, op) s.Error(err) s.Equal(prevErr, err) s.Equal(2, i) } func (s *RetrySuite) TestRetryThrottled() { i := 0 throttleErr := fmt.Errorf("throttled") op := func(ctx context.Context) error { i++ if i == 1 { return throttleErr } return nil } policy := NewExponentialRetryPolicy(time.Millisecond) policy.SetExpirationInterval(NoInterval) throttlePolicy := NewExponentialRetryPolicy(time.Second) throttleRetry := NewThrottleRetry( WithRetryPolicy(policy), WithRetryableError(func(_ error) bool { return true }), WithThrottlePolicy(throttlePolicy), WithThrottleError(func(e error) bool { return e == throttleErr }), ) ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() err := throttleRetry.Do(ctx, op) s.Error(err) s.Equal(throttleErr, err) s.Equal(1, i) // Because retry expires before next retry } func (s *RetrySuite) TestConcurrentRetrier() { policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetMaximumInterval(10 * time.Millisecond) policy.SetMaximumAttempts(4) // Basic checks retrier := NewConcurrentRetrier(policy) retrier.Failed() s.Equal(int64(1), retrier.failureCount) retrier.Succeeded() s.Equal(int64(0), retrier.failureCount) sleepDuration := retrier.throttleInternal() s.Equal(done, sleepDuration) // Multiple count check. retrier.Failed() retrier.Failed() s.Equal(int64(2), retrier.failureCount) // Verify valid sleep times. ch := make(chan time.Duration, 3) go func() { for i := 0; i < 3; i++ { ch <- retrier.throttleInternal() } }() for i := 0; i < 3; i++ { val := <-ch fmt.Printf("Duration: %d\n", val) s.True(val > 0) } retrier.Succeeded() s.Equal(int64(0), retrier.failureCount) // Verify we don't have any sleep times. go func() { for i := 0; i < 3; i++ { ch <- retrier.throttleInternal() } }() for i := 0; i < 3; i++ { val := <-ch fmt.Printf("Duration: %d\n", val) s.Equal(done, val) } } func (s *RetrySuite) TestRetryCountInContext() { retryCounts := make([]int, 0) op := func(ctx context.Context) error { retryCount := ctx.Value(retryCountKey).(int) retryCounts = append(retryCounts, retryCount) if retryCount == 2 { return nil } return &someError{} } policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetMaximumInterval(5 * time.Millisecond) policy.SetMaximumAttempts(10) throttleRetry := NewThrottleRetry( WithRetryPolicy(policy), WithRetryableError(func(_ error) bool { return true }), ) err := throttleRetry.Do(context.Background(), op) s.NoError(err) s.Equal([]int{0, 1, 2}, retryCounts) } func (s *RetrySuite) TestContextDeadline() { cases := []struct { name string policy func() RetryPolicy enforceDeadline bool opts []ThrottleRetryOption expectedMin time.Duration expectedMax time.Duration }{ { name: "no deadline", policy: func() RetryPolicy { policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetExpirationInterval(NoInterval) return policy }, enforceDeadline: true, expectedMin: 0, expectedMax: 0, }, { name: "don't enforce deadline", policy: func() RetryPolicy { policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetExpirationInterval(time.Hour) return policy }, enforceDeadline: false, expectedMin: 0, expectedMax: 0, }, { name: "expiration interval", policy: func() RetryPolicy { policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetExpirationInterval(time.Hour) return policy }, enforceDeadline: true, expectedMin: 50 * time.Minute, expectedMax: 70 * time.Minute, }, { name: "operation timeout", policy: func() RetryPolicy { policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetExpirationInterval(NoInterval) return policy }, enforceDeadline: true, opts: []ThrottleRetryOption{ WithOperationTimeout(time.Hour), }, expectedMin: 50 * time.Minute, expectedMax: 70 * time.Minute, }, { name: "expiration interval lower, take lower", policy: func() RetryPolicy { policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetExpirationInterval(time.Hour) return policy }, enforceDeadline: true, opts: []ThrottleRetryOption{ WithOperationTimeout(10 * time.Hour), }, expectedMin: 50 * time.Minute, expectedMax: 70 * time.Minute, }, { name: "operation timeout lower, take lower", policy: func() RetryPolicy { policy := NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetExpirationInterval(10 * time.Hour) return policy }, enforceDeadline: true, opts: []ThrottleRetryOption{ WithOperationTimeout(time.Hour), }, expectedMin: 50 * time.Minute, expectedMax: 70 * time.Minute, }, } for _, tc := range cases { s.Run(tc.name, func() { var opts []ThrottleRetryOption opts = append(opts, WithRetryPolicy(tc.policy())) if tc.enforceDeadline { opts = append(opts, WithContextExpiration()) } if len(tc.opts) > 0 { opts = append(opts, tc.opts...) } throttleRetry := NewThrottleRetry(opts...) var result time.Duration err := throttleRetry.Do(context.Background(), func(ctx context.Context) error { if deadline, ok := ctx.Deadline(); ok { result = deadline.Sub(time.Now()) } else { result = 0 } return nil }) s.NoError(err, "somehow failed") s.GreaterOrEqual(result, tc.expectedMin) s.LessOrEqual(result, tc.expectedMax) }) } } func (s *RetrySuite) TestOperationTimeoutIsRetryable() { attempts := 0 op := func(ctx context.Context) error { retryCount := ctx.Value(retryCountKey).(int) attempts = max(attempts, retryCount) // Return nil, indicating success if retryCount == 3 { return nil } // Return the DeadlineExceeded err select { case <-ctx.Done(): return ctx.Err() } } policy := NewExponentialRetryPolicy(time.Nanosecond) policy.SetMaximumInterval(time.Nanosecond) policy.SetMaximumAttempts(10) throttleRetry := NewThrottleRetry( WithRetryPolicy(policy), // Operation timeout is retryable anyway WithRetryableError(func(_ error) bool { return false }), // Set a timeout such that it will instantly be canceled every time WithOperationTimeout(time.Nanosecond), ) err := throttleRetry.Do(context.Background(), op) s.NoError(err, "somehow failed") s.Equal(3, attempts) } func (e *someError) Error() string { return "Some Error" } ================================================ FILE: common/backoff/retrypolicy.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package backoff import ( "math" "math/rand" "time" "github.com/uber/cadence/common/clock" ) const ( // NoInterval represents Maximim interval NoInterval = 0 done time.Duration = -1 noMaximumAttempts = 0 defaultBackoffCoefficient = 2.0 defaultMaximumInterval = 10 * time.Second defaultExpirationInterval = time.Minute defaultMaximumAttempts = noMaximumAttempts ) type ( // RetryPolicy is the API which needs to be implemented by various retry policy implementations RetryPolicy interface { ComputeNextDelay(elapsedTime time.Duration, numAttempts int) time.Duration // Expiration returns the maximum time allowed by the RetryPolicy. A value of NoInterval (0) indicates that the // RetryPolicy may take an unbounded amount of time. Expiration() time.Duration } // Retrier manages the state of retry operation Retrier interface { NextBackOff() time.Duration Reset() } // ExponentialRetryPolicy provides the implementation for retry policy using a coefficient to compute the next delay. // Formula used to compute the next delay is: initialInterval * math.Pow(backoffCoefficient, currentAttempt) ExponentialRetryPolicy struct { initialInterval time.Duration backoffCoefficient float64 maximumInterval time.Duration expirationInterval time.Duration maximumAttempts int } // MultiPhasesRetryPolicy implements a policy that first use one policy to get next delay, // and once expired use the next policy for the following retry. // It can achieve fast retries in first phase then slowly retires in second phase. // The supported retry policy is ExponentialRetryPolicy. // To have the correct next delay, set the maximumAttempts in the non-final policy. MultiPhasesRetryPolicy struct { policies []*ExponentialRetryPolicy } retrierImpl struct { policy RetryPolicy clock clock.TimeSource currentAttempt int startTime time.Time } ) // NewExponentialRetryPolicy returns an instance of ExponentialRetryPolicy using the provided initialInterval func NewExponentialRetryPolicy(initialInterval time.Duration) *ExponentialRetryPolicy { p := &ExponentialRetryPolicy{ initialInterval: initialInterval, backoffCoefficient: defaultBackoffCoefficient, maximumInterval: defaultMaximumInterval, expirationInterval: defaultExpirationInterval, maximumAttempts: defaultMaximumAttempts, } return p } // NewMultiPhasesRetryPolicy creates MultiPhasesRetryPolicy func NewMultiPhasesRetryPolicy(policies ...*ExponentialRetryPolicy) *MultiPhasesRetryPolicy { for i := 0; i < len(policies)-1; i++ { if policies[i].maximumAttempts == noMaximumAttempts { panic("Non final retry policy in MultiPhasesRetryPolicy need to set maximum attempts") } } return &MultiPhasesRetryPolicy{ policies: policies, } } // NewRetrier is used for creating a new instance of Retrier func NewRetrier(policy RetryPolicy, clock clock.TimeSource) Retrier { if policy == nil { panic("Retry policy cannot be nil.") } if clock == nil { panic("Retry clock cannot be nil.") } return &retrierImpl{ policy: policy, clock: clock, startTime: clock.Now(), currentAttempt: 0, } } // SetInitialInterval sets the initial interval used by ExponentialRetryPolicy for the very first retry // All later retries are computed using the following formula: // initialInterval * math.Pow(backoffCoefficient, currentAttempt) func (p *ExponentialRetryPolicy) SetInitialInterval(initialInterval time.Duration) { p.initialInterval = initialInterval } // SetBackoffCoefficient sets the coefficient used by ExponentialRetryPolicy to compute next delay for each retry // All retries are computed using the following formula: // initialInterval * math.Pow(backoffCoefficient, currentAttempt) func (p *ExponentialRetryPolicy) SetBackoffCoefficient(backoffCoefficient float64) { p.backoffCoefficient = backoffCoefficient } // SetMaximumInterval sets the maximum interval for each retry func (p *ExponentialRetryPolicy) SetMaximumInterval(maximumInterval time.Duration) { p.maximumInterval = maximumInterval } // SetExpirationInterval sets the absolute expiration interval for all retries func (p *ExponentialRetryPolicy) SetExpirationInterval(expirationInterval time.Duration) { p.expirationInterval = expirationInterval } // SetMaximumAttempts sets the maximum number of retry attempts func (p *ExponentialRetryPolicy) SetMaximumAttempts(maximumAttempts int) { p.maximumAttempts = maximumAttempts } // ComputeNextDelay returns the next delay interval. This is used by Retrier to delay calling the operation again func (p *ExponentialRetryPolicy) ComputeNextDelay(elapsedTime time.Duration, numAttempts int) time.Duration { // Check to see if we ran out of maximum number of attempts if p.maximumAttempts != noMaximumAttempts && numAttempts >= p.maximumAttempts { return done } // Stop retrying after expiration interval is elapsed if p.expirationInterval != NoInterval && elapsedTime > p.expirationInterval { return done } nextInterval := float64(p.initialInterval) * math.Pow(p.backoffCoefficient, float64(numAttempts)) // Disallow retries if initialInterval is negative or nextInterval overflows if nextInterval <= 0 { return done } if p.maximumInterval != NoInterval { nextInterval = math.Min(nextInterval, float64(p.maximumInterval)) } if p.expirationInterval != NoInterval { remainingTime := float64(math.Max(0, float64(p.expirationInterval-elapsedTime))) nextInterval = math.Min(remainingTime, nextInterval) } // Bail out if the next interval is smaller than initial retry interval nextDuration := time.Duration(nextInterval) if nextDuration < p.initialInterval { return done } // add jitter to avoid global synchronization jitterPortion := int(0.2 * nextInterval) // Prevent overflow if jitterPortion < 1 { jitterPortion = 1 } nextInterval = nextInterval*0.8 + float64(rand.Intn(jitterPortion)) return time.Duration(nextInterval) } func (p *ExponentialRetryPolicy) Expiration() time.Duration { return p.expirationInterval } // ComputeNextDelay returns the next delay interval. func (tp MultiPhasesRetryPolicy) ComputeNextDelay(elapsedTime time.Duration, numAttempts int) time.Duration { previousStageRetryCount := 0 for _, policy := range tp.policies { nextInterval := policy.ComputeNextDelay(elapsedTime, numAttempts-previousStageRetryCount) if nextInterval != done { return nextInterval } previousStageRetryCount += policy.maximumAttempts } return done } func (tp MultiPhasesRetryPolicy) Expiration() time.Duration { var sum time.Duration for _, policy := range tp.policies { exp := policy.Expiration() if exp == NoInterval { return NoInterval } sum += exp } return sum } // Reset will set the Retrier into initial state func (r *retrierImpl) Reset() { r.startTime = r.clock.Now() r.currentAttempt = 0 } // NextBackOff returns the next delay interval. This is used by Retry to delay calling the operation again func (r *retrierImpl) NextBackOff() time.Duration { nextInterval := r.policy.ComputeNextDelay(r.getElapsedTime(), r.currentAttempt) // Now increment the current attempt r.currentAttempt++ return nextInterval } func (r *retrierImpl) getElapsedTime() time.Duration { return r.clock.Now().Sub(r.startTime) } ================================================ FILE: common/backoff/retrypolicy_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package backoff import ( "math/rand" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/clock" ) type ( RetryPolicySuite struct { *require.Assertions // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, not merely log an error suite.Suite } ) func TestRetryPolicySuite(t *testing.T) { suite.Run(t, new(RetryPolicySuite)) } func (s *RetryPolicySuite) SetupTest() { rand.Seed(int64(time.Now().Nanosecond())) s.Assertions = require.New(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil } func (s *RetryPolicySuite) TestExponentialBackoff() { policy := createPolicy(time.Second) policy.SetMaximumInterval(10 * time.Second) expectedResult := []time.Duration{1, 2, 4, 8, 10} for i, d := range expectedResult { expectedResult[i] = d * time.Second } r, _ := createRetrier(policy) for _, expected := range expectedResult { min, max := getNextBackoffRange(expected) next := r.NextBackOff() s.True(next >= min, "NextBackoff too low") s.True(next < max, "NextBackoff too high") } } func (s *RetryPolicySuite) TestNumberOfAttempts() { policy := createPolicy(time.Second) policy.SetMaximumAttempts(5) r, _ := createRetrier(policy) var next time.Duration for i := 0; i < 6; i++ { next = r.NextBackOff() } s.Equal(done, next) } // Test to make sure relative maximum interval for each retry is honoured func (s *RetryPolicySuite) TestMaximumInterval() { policy := createPolicy(time.Second) policy.SetMaximumInterval(10 * time.Second) expectedResult := []time.Duration{1, 2, 4, 8, 10, 10, 10, 10, 10, 10} for i, d := range expectedResult { expectedResult[i] = d * time.Second } r, _ := createRetrier(policy) for _, expected := range expectedResult { min, max := getNextBackoffRange(expected) next := r.NextBackOff() s.True(next >= min, "NextBackoff too low") s.True(next < max, "NextBackoff too high") } } func (s *RetryPolicySuite) TestBackoffCoefficient() { policy := createPolicy(2 * time.Second) policy.SetBackoffCoefficient(1.0) r, _ := createRetrier(policy) min, max := getNextBackoffRange(2 * time.Second) for i := 0; i < 10; i++ { next := r.NextBackOff() s.True(next >= min, "NextBackoff too low") s.True(next < max, "NextBackoff too high") } } func (s *RetryPolicySuite) TestExpirationInterval() { policy := createPolicy(2 * time.Second) policy.SetExpirationInterval(5 * time.Minute) r, clock := createRetrier(policy) clock.Advance(6 * time.Minute) next := r.NextBackOff() s.Equal(done, next) } func (s *RetryPolicySuite) TestExpirationOverflow() { policy := createPolicy(2 * time.Second) policy.SetExpirationInterval(5 * time.Second) r, clock := createRetrier(policy) next := r.NextBackOff() min, max := getNextBackoffRange(2 * time.Second) s.True(next >= min, "NextBackoff too low") s.True(next < max, "NextBackoff too high") clock.Advance(2 * time.Second) next = r.NextBackOff() min, max = getNextBackoffRange(3 * time.Second) s.True(next >= min, "NextBackoff too low") s.True(next < max, "NextBackoff too high") } func (s *RetryPolicySuite) TestDefaultPublishRetryPolicy() { policy := NewExponentialRetryPolicy(50 * time.Millisecond) policy.SetExpirationInterval(time.Minute) policy.SetMaximumInterval(10 * time.Second) r, clock := createRetrier(policy) expectedResult := []time.Duration{ 50 * time.Millisecond, 100 * time.Millisecond, 200 * time.Millisecond, 400 * time.Millisecond, 800 * time.Millisecond, 1600 * time.Millisecond, 3200 * time.Millisecond, 6400 * time.Millisecond, 10000 * time.Millisecond, 10000 * time.Millisecond, 10000 * time.Millisecond, 10000 * time.Millisecond, 6000 * time.Millisecond, 1250 * time.Millisecond, done, } total := 0 for i, expected := range expectedResult { next := r.NextBackOff() total += int(next) if expected == done { s.Equal(done, next, "backoff not done yet!!!") } else { min, _ := getNextBackoffRange(expected) s.True(next >= min, "iteration %d: NextBackoff too low: actual: %v, expected: %v", i, next, expected) // s.True(next < max, "NextBackoff too high: actual: %v, expected: %v", next, expected) clock.Advance(expected) } } } func (s *RetryPolicySuite) TestNoMaxAttempts() { policy := createPolicy(50 * time.Millisecond) policy.SetExpirationInterval(time.Minute) policy.SetMaximumInterval(10 * time.Second) r, clock := createRetrier(policy) for i := 0; i < 100; i++ { next := r.NextBackOff() // print("Iter: ", i, ", Next Backoff: ", next.String(), "\n") s.True(next > 0 || next == done, "Unexpected value for next retry duration: %v", next) clock.Advance(next) } } func (s *RetryPolicySuite) TestUnbounded() { policy := createPolicy(50 * time.Millisecond) r, clock := createRetrier(policy) for i := 0; i < 100; i++ { next := r.NextBackOff() // print("Iter: ", i, ", Next Backoff: ", next.String(), "\n") s.True(next > 0 || next == done, "Unexpected value for next retry duration: %v", next) clock.Advance(next) } } func (s *RetryPolicySuite) TestMultiPhasesRetryPolicy() { firstPolicy := NewExponentialRetryPolicy(50 * time.Millisecond) firstPolicy.SetMaximumAttempts(3) secondPolicy := NewExponentialRetryPolicy(2 * time.Second) secondPolicy.SetMaximumInterval(128 * time.Second) secondPolicy.SetExpirationInterval(5 * time.Minute) policy := NewMultiPhasesRetryPolicy(firstPolicy, secondPolicy) r, clock := createRetrier(policy) expectedResult := []time.Duration{ 50 * time.Millisecond, 100 * time.Millisecond, 200 * time.Millisecond, 2 * time.Second, 4 * time.Second, 8 * time.Second, 16 * time.Second, 32 * time.Second, 64 * time.Second, 128 * time.Second, 45650 * time.Millisecond, done, } for _, expected := range expectedResult { next := r.NextBackOff() if expected == done { s.Equal(done, next, "backoff not done yet!!!") } else { min, max := getNextBackoffRange(expected) s.True(next >= min, "NextBackoff too low: actual: %v, expected: %v", next, expected) s.True(next < max, "NextBackoff too high: actual: %v, expected: %v", next, expected) clock.Advance(expected) } } } func createPolicy(initialInterval time.Duration) *ExponentialRetryPolicy { policy := NewExponentialRetryPolicy(initialInterval) policy.SetBackoffCoefficient(2) policy.SetMaximumInterval(NoInterval) policy.SetExpirationInterval(NoInterval) policy.SetMaximumAttempts(noMaximumAttempts) return policy } func createRetrier(policy RetryPolicy) (Retrier, clock.MockedTimeSource) { clock := clock.NewMockedTimeSourceAt(time.Time{}) return NewRetrier(policy, clock), clock } func getNextBackoffRange(duration time.Duration) (time.Duration, time.Duration) { rangeMin := time.Duration(0.8 * float64(duration)) return rangeMin, duration } ================================================ FILE: common/blobstore/client.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package blobstore import "context" //go:generate mockgen -package=$GOPACKAGE -destination=client_mock.go -self_package=github.com/uber/cadence/common/blobstore github.com/uber/cadence/common/blobstore Client type ( // Client defines the interface to a blobstore client. Client interface { Put(context.Context, *PutRequest) (*PutResponse, error) Get(context.Context, *GetRequest) (*GetResponse, error) Exists(context.Context, *ExistsRequest) (*ExistsResponse, error) Delete(context.Context, *DeleteRequest) (*DeleteResponse, error) IsRetryableError(error) bool } // PutRequest is the request to Put PutRequest struct { Key string Blob Blob } // PutResponse is the response from Put PutResponse struct{} // GetRequest is the request to Get GetRequest struct { Key string } // GetResponse is the response from Get GetResponse struct { Blob Blob } // ExistsRequest is the request to Exists ExistsRequest struct { Key string } // ExistsResponse is the response from Exists ExistsResponse struct { Exists bool } // DeleteRequest is the request to Delete DeleteRequest struct { Key string } // DeleteResponse is the response from Delete DeleteResponse struct{} // Blob defines a blob which can be stored and fetched from blobstore Blob struct { Tags map[string]string Body []byte } ) ================================================ FILE: common/blobstore/client_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/blobstore (interfaces: Client) // // Generated by this command: // // mockgen -package=blobstore -destination=client_mock.go -self_package=github.com/uber/cadence/common/blobstore github.com/uber/cadence/common/blobstore Client // // Package blobstore is a generated GoMock package. package blobstore import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // Delete mocks base method. func (m *MockClient) Delete(arg0 context.Context, arg1 *DeleteRequest) (*DeleteResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Delete", arg0, arg1) ret0, _ := ret[0].(*DeleteResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Delete indicates an expected call of Delete. func (mr *MockClientMockRecorder) Delete(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), arg0, arg1) } // Exists mocks base method. func (m *MockClient) Exists(arg0 context.Context, arg1 *ExistsRequest) (*ExistsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Exists", arg0, arg1) ret0, _ := ret[0].(*ExistsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Exists indicates an expected call of Exists. func (mr *MockClientMockRecorder) Exists(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exists", reflect.TypeOf((*MockClient)(nil).Exists), arg0, arg1) } // Get mocks base method. func (m *MockClient) Get(arg0 context.Context, arg1 *GetRequest) (*GetResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", arg0, arg1) ret0, _ := ret[0].(*GetResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Get indicates an expected call of Get. func (mr *MockClientMockRecorder) Get(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), arg0, arg1) } // IsRetryableError mocks base method. func (m *MockClient) IsRetryableError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsRetryableError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsRetryableError indicates an expected call of IsRetryableError. func (mr *MockClientMockRecorder) IsRetryableError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsRetryableError", reflect.TypeOf((*MockClient)(nil).IsRetryableError), arg0) } // Put mocks base method. func (m *MockClient) Put(arg0 context.Context, arg1 *PutRequest) (*PutResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Put", arg0, arg1) ret0, _ := ret[0].(*PutResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Put indicates an expected call of Put. func (mr *MockClientMockRecorder) Put(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockClient)(nil).Put), arg0, arg1) } ================================================ FILE: common/blobstore/filestore/client.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package filestore import ( "context" "encoding/json" "errors" "fmt" "os" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/util" ) type ( client struct { outputDirectory string } ) // NewFilestoreClient constructs a blobstore backed by local file system func NewFilestoreClient(cfg *config.FileBlobstore) (blobstore.Client, error) { if cfg == nil { return nil, errors.New("file blobstore config is nil") } if len(cfg.OutputDirectory) == 0 { return nil, errors.New("output directory not given for file blobstore") } outputDirectory := cfg.OutputDirectory exists, err := util.DirectoryExists(outputDirectory) if err != nil { return nil, err } if !exists { if err := util.MkdirAll(outputDirectory, os.FileMode(0766)); err != nil { return nil, err } } return &client{ outputDirectory: outputDirectory, }, nil } // Put stores a blob func (c *client) Put(_ context.Context, request *blobstore.PutRequest) (resp *blobstore.PutResponse, err error) { defer func() { if err != nil { os.Remove(c.bodyPath(request.Key)) os.Remove(c.tagsPath(request.Key)) } }() if err := util.WriteFile(c.bodyPath(request.Key), request.Blob.Body, os.FileMode(0666)); err != nil { return nil, err } tagsData, err := json.Marshal(request.Blob.Tags) if err != nil { return nil, err } if err := util.WriteFile(c.tagsPath(request.Key), tagsData, os.FileMode(0666)); err != nil { return nil, err } return &blobstore.PutResponse{}, nil } // Get fetches a blob func (c *client) Get(_ context.Context, request *blobstore.GetRequest) (*blobstore.GetResponse, error) { data, err := util.ReadFile(c.bodyPath(request.Key)) if err != nil { return nil, err } tagsData, err := util.ReadFile(c.tagsPath(request.Key)) if err != nil { return nil, err } tags := make(map[string]string) if err := json.Unmarshal(tagsData, &tags); err != nil { return nil, err } return &blobstore.GetResponse{ Blob: blobstore.Blob{ Body: data, Tags: tags, }, }, nil } // Exists determines if a blob exists func (c *client) Exists(_ context.Context, request *blobstore.ExistsRequest) (*blobstore.ExistsResponse, error) { exists, err := util.FileExists(c.bodyPath(request.Key)) if err != nil { return nil, err } return &blobstore.ExistsResponse{ Exists: exists, }, nil } // Delete deletes a blob func (c *client) Delete(_ context.Context, request *blobstore.DeleteRequest) (*blobstore.DeleteResponse, error) { if err := os.Remove(c.bodyPath(request.Key)); err != nil { return nil, err } if err := os.Remove(c.tagsPath(request.Key)); err != nil { return nil, err } return &blobstore.DeleteResponse{}, nil } // IsRetryableError returns true if the error is retryable false otherwise func (c *client) IsRetryableError(err error) bool { return false } func (c *client) bodyPath(key string) string { return fmt.Sprintf("%v/%v", c.outputDirectory, key) } func (c *client) tagsPath(key string) string { return fmt.Sprintf("%v/.%v.tags", c.outputDirectory, key) } ================================================ FILE: common/blobstore/filestore/client_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package filestore import ( "context" "os" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/util" ) type ClientSuite struct { *require.Assertions suite.Suite } func TestClientSuite(t *testing.T) { suite.Run(t, new(ClientSuite)) } func (s *ClientSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *ClientSuite) TestNewFilestoreClient_InvalidConfig() { _, err := NewFilestoreClient(nil) s.Error(err) _, err = NewFilestoreClient(&config.FileBlobstore{}) s.Error(err) } func (s *ClientSuite) TestNewFilestoreClient_DirectoryAlreadyExists() { name := s.T().TempDir() c, err := NewFilestoreClient(&config.FileBlobstore{OutputDirectory: name}) s.NoError(err) s.Equal(name, c.(*client).outputDirectory) } func (s *ClientSuite) TestNewFilestoreClient_DirectoryNotAlreadyExists() { name := s.T().TempDir() os.RemoveAll(name) exists, err := util.DirectoryExists(name) s.NoError(err) s.False(exists) c, err := NewFilestoreClient(&config.FileBlobstore{OutputDirectory: name}) s.NoError(err) s.Equal(name, c.(*client).outputDirectory) exists, err = util.DirectoryExists(name) s.NoError(err) s.True(exists) os.RemoveAll(name) } func (s *ClientSuite) TestCrudOperations() { name := s.T().TempDir() c, err := NewFilestoreClient(&config.FileBlobstore{OutputDirectory: name}) s.NoError(err) ctx := context.Background() // put three blobs in blobstore key1 := uuid.New() key2 := uuid.New() key3 := uuid.New() blob1 := blobstore.Blob{ Tags: nil, Body: []byte{1, 2, 3}, } blob2 := blobstore.Blob{ Tags: map[string]string{"key1": "value1"}, Body: nil, } blob3 := blobstore.Blob{ Tags: map[string]string{"key1": "value1", "key2": "value2"}, Body: []byte{1, 2, 3, 4, 5}, } _, err = c.Put(ctx, &blobstore.PutRequest{ Key: key1, Blob: blob1, }) s.NoError(err) _, err = c.Put(ctx, &blobstore.PutRequest{ Key: key2, Blob: blob2, }) s.NoError(err) _, err = c.Put(ctx, &blobstore.PutRequest{ Key: key3, Blob: blob3, }) s.NoError(err) // get the blobs back get1, err := c.Get(ctx, &blobstore.GetRequest{Key: key1}) s.NoError(err) s.Nil(get1.Blob.Tags) s.Equal([]byte{1, 2, 3}, get1.Blob.Body) get2, err := c.Get(ctx, &blobstore.GetRequest{Key: key2}) s.NoError(err) s.Equal(map[string]string{"key1": "value1"}, get2.Blob.Tags) s.Empty(get2.Blob.Body) get3, err := c.Get(ctx, &blobstore.GetRequest{Key: key3}) s.NoError(err) s.Equal(map[string]string{"key1": "value1", "key2": "value2"}, get3.Blob.Tags) s.Equal([]byte{1, 2, 3, 4, 5}, get3.Blob.Body) // confirm all the blobs exist exists1, err := c.Exists(ctx, &blobstore.ExistsRequest{Key: key1}) s.NoError(err) s.True(exists1.Exists) exists2, err := c.Exists(ctx, &blobstore.ExistsRequest{Key: key2}) s.NoError(err) s.True(exists2.Exists) exists3, err := c.Exists(ctx, &blobstore.ExistsRequest{Key: key3}) s.NoError(err) s.True(exists3.Exists) // delete a blob and confirm no longer can get and that no longer exists _, err = c.Delete(ctx, &blobstore.DeleteRequest{Key: key1}) s.NoError(err) exists1, err = c.Exists(ctx, &blobstore.ExistsRequest{Key: key1}) s.NoError(err) s.False(exists1.Exists) get1, err = c.Get(ctx, &blobstore.GetRequest{Key: key1}) s.Error(err) s.Nil(get1) } ================================================ FILE: common/blobstore/retryable_client.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package blobstore import ( "context" "github.com/uber/cadence/common/backoff" ) type ( retryableClient struct { client Client throttleRetry *backoff.ThrottleRetry } ) // NewRetryableClient constructs a blobstorre client which retries transient errors. func NewRetryableClient(client Client, policy backoff.RetryPolicy) Client { return &retryableClient{ client: client, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(client.IsRetryableError), ), } } func (c *retryableClient) Put(ctx context.Context, req *PutRequest) (*PutResponse, error) { var resp *PutResponse var err error op := func(ctx context.Context) error { resp, err = c.client.Put(ctx, req) return err } err = c.throttleRetry.Do(ctx, op) if err != nil { return nil, err } return resp, nil } func (c *retryableClient) Get(ctx context.Context, req *GetRequest) (*GetResponse, error) { var resp *GetResponse var err error op := func(ctx context.Context) error { resp, err = c.client.Get(ctx, req) return err } err = c.throttleRetry.Do(ctx, op) if err != nil { return nil, err } return resp, nil } func (c *retryableClient) Exists(ctx context.Context, req *ExistsRequest) (*ExistsResponse, error) { var resp *ExistsResponse var err error op := func(ctx context.Context) error { resp, err = c.client.Exists(ctx, req) return err } err = c.throttleRetry.Do(ctx, op) if err != nil { return nil, err } return resp, nil } func (c *retryableClient) Delete(ctx context.Context, req *DeleteRequest) (*DeleteResponse, error) { var resp *DeleteResponse var err error op := func(ctx context.Context) error { resp, err = c.client.Delete(ctx, req) return err } err = c.throttleRetry.Do(ctx, op) if err != nil { return nil, err } return resp, nil } func (c *retryableClient) IsRetryableError(err error) bool { return c.client.IsRetryableError(err) } ================================================ FILE: common/blobstore/retryable_client_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package blobstore import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/backoff" ) func runCRUDTest( t *testing.T, retryPolicy backoff.RetryPolicy, retryableError bool, req, resp any, expectFn func(*MockClient, any, any), callFn func(Client, context.Context, any) (any, error), assertFn func(*testing.T, any, any, any, error), ) { mockClient := NewMockClient(gomock.NewController(t)) throttleRetryOptions := []backoff.ThrottleRetryOption{ backoff.WithRetryPolicy(retryPolicy), } if retryableError { throttleRetryOptions = append(throttleRetryOptions, backoff.WithRetryableError(mockClient.IsRetryableError)) } client := &retryableClient{ client: mockClient, throttleRetry: backoff.NewThrottleRetry(throttleRetryOptions...), } expectFn(mockClient, req, resp) result, err := callFn(client, context.Background(), req) assertFn(t, req, resp, result, err) } func TestRetryableClient(t *testing.T) { tests := []struct { name string retryPolicy backoff.RetryPolicy retryableError bool req any resp any expectFn func(*MockClient, any, any) callFn func(Client, context.Context, any) (any, error) assertFn func(*testing.T, any, any, any, error) }{ { name: "Put", retryPolicy: backoff.NewExponentialRetryPolicy(0), retryableError: false, req: &PutRequest{}, resp: &PutResponse{}, expectFn: func(m *MockClient, req, resp any) { m.EXPECT().Put(gomock.Any(), req.(*PutRequest)).Return(resp.(*PutResponse), nil).Times(1) }, callFn: func(c Client, ctx context.Context, req any) (any, error) { return c.Put(ctx, req.(*PutRequest)) }, assertFn: func(t *testing.T, req any, resp any, result any, err error) { assert.NoError(t, err) assert.Equal(t, resp.(*PutResponse), result) }, }, { name: "Get", retryPolicy: backoff.NewExponentialRetryPolicy(0), retryableError: false, req: &GetRequest{}, resp: &GetResponse{}, expectFn: func(m *MockClient, req, resp any) { m.EXPECT().Get(gomock.Any(), req.(*GetRequest)).Return(resp.(*GetResponse), nil).Times(1) }, callFn: func(c Client, ctx context.Context, req any) (any, error) { return c.Get(ctx, req.(*GetRequest)) }, assertFn: func(t *testing.T, req any, resp any, result any, err error) { assert.NoError(t, err) assert.Equal(t, resp.(*GetResponse), result) }, }, { name: "Exists", retryPolicy: backoff.NewExponentialRetryPolicy(0), retryableError: false, req: &ExistsRequest{}, resp: &ExistsResponse{}, expectFn: func(m *MockClient, req, resp any) { m.EXPECT().Exists(gomock.Any(), req.(*ExistsRequest)).Return(resp.(*ExistsResponse), nil).Times(1) }, callFn: func(c Client, ctx context.Context, req any) (any, error) { return c.Exists(ctx, req.(*ExistsRequest)) }, assertFn: func(t *testing.T, req any, resp any, result any, err error) { assert.NoError(t, err) assert.Equal(t, resp.(*ExistsResponse), result) }, }, { name: "Delete", retryPolicy: backoff.NewExponentialRetryPolicy(0), retryableError: false, req: &DeleteRequest{}, resp: &DeleteResponse{}, expectFn: func(m *MockClient, req, resp any) { m.EXPECT().Delete(gomock.Any(), req.(*DeleteRequest)).Return(resp.(*DeleteResponse), nil).Times(1) }, callFn: func(c Client, ctx context.Context, req any) (any, error) { return c.Delete(ctx, req.(*DeleteRequest)) }, assertFn: func(t *testing.T, req any, resp any, result any, err error) { assert.NoError(t, err) assert.Equal(t, resp.(*DeleteResponse), result) }, }, { name: "RetryOnError", retryPolicy: backoff.NewExponentialRetryPolicy(1), retryableError: true, req: &PutRequest{}, resp: &PutResponse{}, expectFn: func(m *MockClient, req, resp any) { retryableError := errors.New("retryable error") m.EXPECT().Put(gomock.Any(), req.(*PutRequest)).Return(nil, retryableError).Times(1) m.EXPECT().IsRetryableError(retryableError).Return(true).Times(1) m.EXPECT().Put(gomock.Any(), req.(*PutRequest)).Return(resp, nil).Times(1) }, callFn: func(c Client, ctx context.Context, req any) (any, error) { return c.Put(ctx, req.(*PutRequest)) }, assertFn: func(t *testing.T, req any, resp any, result any, err error) { assert.NoError(t, err, "Expected no error on successful retry") assert.Equal(t, resp.(*PutResponse), result, "Expected the response to match") }, }, { name: "NotRetryOnError", retryPolicy: backoff.NewExponentialRetryPolicy(0), retryableError: true, req: &PutRequest{}, expectFn: func(m *MockClient, req, resp any) { nonRetryableError := errors.New("non-retryable error") m.EXPECT().Put(gomock.Any(), req.(*PutRequest)).Return(nil, nonRetryableError).Times(1) m.EXPECT().IsRetryableError(nonRetryableError).Return(false).Times(1) }, callFn: func(c Client, ctx context.Context, req any) (any, error) { return c.Put(ctx, req.(*PutRequest)) }, assertFn: func(t *testing.T, req any, resp any, result any, err error) { assert.Error(t, err) assert.Nil(t, result) }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { runCRUDTest( t, tc.retryPolicy, tc.retryableError, tc.req, tc.resp, tc.expectFn, tc.callFn, tc.assertFn, ) }) } } func TestRetryableClient_IsRetryableError(t *testing.T) { mockClient := NewMockClient(gomock.NewController(t)) client := &retryableClient{ client: mockClient, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(backoff.NewExponentialRetryPolicy(0)), backoff.WithRetryableError(mockClient.IsRetryableError), ), } retryableError := errors.New("retryable error") mockClient.EXPECT().IsRetryableError(retryableError).Return(true).Times(1) isRetryableError := client.IsRetryableError(retryableError) assert.True(t, isRetryableError, "Expected error to be retryable") } ================================================ FILE: common/cache/ack_cache.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cache import ( "container/heap" "errors" "math" "sync" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" ) var ( // ErrAckCacheFull is returned when the cache is at capacity and cannot accept new items ErrAckCacheFull = errors.New("ack cache is full") // ErrAlreadyAcked is returned when trying to add an item with sequence ID that was already acknowledged ErrAlreadyAcked = errors.New("sequence ID already acknowledged") ) // AckCacheItem represents an item that can be stored in an AckCache. // Items must have a sequence ID for ordering. type AckCacheItem interface { // GetSequenceID returns the sequence identifier for this item. // Items are ordered by sequence ID and acknowledged up to a sequence level. GetSequenceID() int64 } // AckCache is a bounded cache that stores items in sequence ID order. // Items are removed via acknowledgment - calling Ack(level) removes all items // with sequence ID <= level. When the cache reaches capacity, new items are // rejected rather than evicting existing items. // // This cache is useful for scenarios like: // - Buffering out-of-order messages that need acknowledgment // - Caching items that must be processed in sequence // - Storing items with backpressure (reject rather than evict) // // The cache is thread-safe for concurrent readers and writers. type AckCache[T AckCacheItem] interface { // Put stores an item in the cache with the specified size. Returns ErrAckCacheFull if the cache // is at capacity, or ErrAlreadyAcked if the item's sequence ID has already // been acknowledged. Silently ignores duplicate sequence IDs. Put(item T, size uint64) error // Get retrieves an item by its sequence ID. Returns the zero value of T // if the item is not found. Get(sequenceID int64) T // Ack acknowledges all items with sequence ID <= level, removing them // from the cache. Returns the total size of items that were removed and the count of items removed. // This is the primary mechanism for freeing cache space. Ack(level int64) (uint64, int) // Size returns the current total byte size of all items in the cache. Size() uint64 // Count returns the current number of items in the cache. Count() int } // BoundedAckCache is a heap-based implementation of AckCache that maintains // items in sequence ID order and rejects new items when capacity limits are reached. type BoundedAckCache[T AckCacheItem] struct { mu sync.Mutex maxCount dynamicproperties.IntPropertyFn maxSize dynamicproperties.IntPropertyFn // Heap maintains items in sequence ID order for efficient acknowledgment order sequenceHeap[T] // Map provides O(1) lookup by sequence ID cache map[int64]T // Track acknowledgment level and current usage lastAck int64 currSize uint64 logger log.Logger budgetManager Manager cacheID string } // NewBoundedAckCache creates a new bounded ack cache with the specified capacity limits. // // Parameters: // - maxCount: maximum number of items (dynamic property) // - maxSize: maximum total byte size (dynamic property) // - logger: optional logger for diagnostics (can be nil) // - budgetManager: optional budget manager for host-level capacity tracking (can be nil) // - cacheID: cache identifier for budget manager (required if budgetManager is provided) // // The cache will reject new items when either limit would be exceeded. // If a budget manager is provided, Put and Ack operations will automatically // reserve and release capacity through the budget manager. func NewBoundedAckCache[T AckCacheItem]( maxCount dynamicproperties.IntPropertyFn, maxSize dynamicproperties.IntPropertyFn, logger log.Logger, budgetManager Manager, cacheID string, ) AckCache[T] { initialCount := maxCount() return &BoundedAckCache[T]{ maxCount: maxCount, maxSize: maxSize, order: make(sequenceHeap[T], 0, initialCount), cache: make(map[int64]T, initialCount), logger: logger, budgetManager: budgetManager, cacheID: cacheID, } } // Put stores an item in the cache with the specified size. func (c *BoundedAckCache[T]) Put(item T, size uint64) error { c.mu.Lock() defer c.mu.Unlock() sequenceID := item.GetSequenceID() // Reject items that have already been acknowledged if c.lastAck >= sequenceID { return ErrAlreadyAcked } // Silently ignore duplicate sequence IDs if _, exists := c.cache[sequenceID]; exists { return nil } // Check capacity limits if (len(c.order) >= c.maxCount()) || (c.currSize+size >= uint64(c.maxSize())) || (c.currSize > math.MaxUint64-size) { return ErrAckCacheFull } if c.budgetManager != nil { return c.budgetManager.ReserveWithCallback(c.cacheID, size, 1, func() error { return c.putInternal(item, sequenceID, size) }) } return c.putInternal(item, sequenceID, size) } // Get retrieves an item by sequence ID. func (c *BoundedAckCache[T]) Get(sequenceID int64) T { c.mu.Lock() defer c.mu.Unlock() return c.cache[sequenceID] } // Ack acknowledges all items with sequence ID <= level and returns total freed size and count of items removed. func (c *BoundedAckCache[T]) Ack(level int64) (uint64, int) { c.mu.Lock() defer c.mu.Unlock() if c.budgetManager != nil { var freedSize uint64 var removedCount int64 err := c.budgetManager.ReleaseWithCallback(c.cacheID, func() (uint64, int64, error) { freedSize, removedCount = c.ackInternal(level) return freedSize, removedCount, nil }) if err != nil { return 0, 0 } return freedSize, int(removedCount) } freedSize, removedCount := c.ackInternal(level) return freedSize, int(removedCount) } // Size returns current total byte size. func (c *BoundedAckCache[T]) Size() uint64 { c.mu.Lock() defer c.mu.Unlock() return c.currSize } // Count returns current number of items. func (c *BoundedAckCache[T]) Count() int { c.mu.Lock() defer c.mu.Unlock() return len(c.order) } // putInternal adds an item to the cache. Caller must hold the lock. func (c *BoundedAckCache[T]) putInternal(item T, sequenceID int64, size uint64) error { c.cache[sequenceID] = item heap.Push(&c.order, heapItem[T]{sequenceID: sequenceID, size: size}) c.currSize += size return nil } // ackInternal removes all items with sequence ID <= level. Caller must hold the lock. func (c *BoundedAckCache[T]) ackInternal(level int64) (uint64, int64) { var freedSize uint64 var removedCount int64 for c.order.Len() > 0 && c.order.Peek().sequenceID <= level { item := heap.Pop(&c.order).(heapItem[T]) delete(c.cache, item.sequenceID) c.currSize -= item.size freedSize += item.size removedCount++ } c.lastAck = level return freedSize, removedCount } // heapItem represents an item in the sequence heap type heapItem[T AckCacheItem] struct { sequenceID int64 size uint64 } // sequenceHeap implements heap.Interface for maintaining items in sequence order type sequenceHeap[T AckCacheItem] []heapItem[T] func (h sequenceHeap[T]) Len() int { return len(h) } func (h sequenceHeap[T]) Less(i, j int) bool { return h[i].sequenceID < h[j].sequenceID } func (h sequenceHeap[T]) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h *sequenceHeap[T]) Push(x interface{}) { *h = append(*h, x.(heapItem[T])) } func (h *sequenceHeap[T]) Pop() interface{} { old := *h n := len(old) item := old[n-1] *h = old[0 : n-1] return item } func (h *sequenceHeap[T]) Peek() heapItem[T] { return (*h)[0] } ================================================ FILE: common/cache/ack_cache_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cache import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" ) // testItem implements AckCacheItem for testing type testItem struct { sequenceID int64 data string size uint64 } func (t *testItem) GetSequenceID() int64 { return t.sequenceID } func (t *testItem) ByteSize() uint64 { if t.size > 0 { return t.size } // Default size based on data length return uint64(len(t.data)) + 16 // 16 bytes overhead } func newTestItem(sequenceID int64, data string) *testItem { return &testItem{ sequenceID: sequenceID, data: data, } } func newTestItemWithSize(sequenceID int64, data string, size uint64) *testItem { return &testItem{ sequenceID: sequenceID, data: data, size: size, } } func TestBoundedAckCache_BasicOperations(t *testing.T) { cache := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(10), // max count dynamicproperties.GetIntPropertyFn(1000), // max size log.NewNoop(), nil, "", ) // Test empty cache assert.Equal(t, uint64(0), cache.Size()) assert.Equal(t, 0, cache.Count()) assert.Nil(t, cache.Get(100)) // Test adding items item1 := newTestItem(100, "item1") item2 := newTestItem(200, "item2") item3 := newTestItem(150, "item3") // out of order require.NoError(t, cache.Put(item1, item1.ByteSize())) assert.Equal(t, item1.ByteSize(), cache.Size()) assert.Equal(t, 1, cache.Count()) assert.Equal(t, item1, cache.Get(100)) require.NoError(t, cache.Put(item2, item2.ByteSize())) assert.Equal(t, item1.ByteSize()+item2.ByteSize(), cache.Size()) assert.Equal(t, 2, cache.Count()) assert.Equal(t, item2, cache.Get(200)) // Test out-of-order insertion require.NoError(t, cache.Put(item3, item3.ByteSize())) assert.Equal(t, item1.ByteSize()+item2.ByteSize()+item3.ByteSize(), cache.Size()) assert.Equal(t, 3, cache.Count()) assert.Equal(t, item3, cache.Get(150)) // Test duplicate insertion (should be silently ignored) require.NoError(t, cache.Put(item1, item1.ByteSize())) assert.Equal(t, 3, cache.Count()) // No change } func TestBoundedAckCache_Acknowledgment(t *testing.T) { cache := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(10), dynamicproperties.GetIntPropertyFn(1000), log.NewNoop(), nil, "", ) item1 := newTestItem(100, "item1") item2 := newTestItem(200, "item2") item3 := newTestItem(300, "item3") item4 := newTestItem(150, "item4") require.NoError(t, cache.Put(item1, item1.ByteSize())) require.NoError(t, cache.Put(item2, item2.ByteSize())) require.NoError(t, cache.Put(item3, item3.ByteSize())) require.NoError(t, cache.Put(item4, item4.ByteSize())) totalSize := item1.ByteSize() + item2.ByteSize() + item3.ByteSize() + item4.ByteSize() assert.Equal(t, totalSize, cache.Size()) assert.Equal(t, 4, cache.Count()) // Ack at level 0 - should not remove anything freedSize, removedCount := cache.Ack(0) assert.Equal(t, uint64(0), freedSize) assert.Equal(t, 0, removedCount) assert.Equal(t, 4, cache.Count()) assert.Equal(t, totalSize, cache.Size()) // Ack at level 100 - should remove item1 freedSize, removedCount = cache.Ack(100) assert.Equal(t, item1.ByteSize(), freedSize) assert.Equal(t, 1, removedCount) assert.Equal(t, 3, cache.Count()) assert.Equal(t, totalSize-item1.ByteSize(), cache.Size()) assert.Nil(t, cache.Get(100)) assert.Equal(t, item4, cache.Get(150)) assert.Equal(t, item2, cache.Get(200)) assert.Equal(t, item3, cache.Get(300)) // Ack at level 200 - should remove item4 and item2 freedSize, removedCount = cache.Ack(200) assert.Equal(t, item4.ByteSize()+item2.ByteSize(), freedSize) assert.Equal(t, 2, removedCount) assert.Equal(t, 1, cache.Count()) assert.Equal(t, item3.ByteSize(), cache.Size()) assert.Nil(t, cache.Get(100)) assert.Nil(t, cache.Get(150)) assert.Nil(t, cache.Get(200)) assert.Equal(t, item3, cache.Get(300)) // Ack at level 500 - should remove everything freedSize, removedCount = cache.Ack(500) assert.Equal(t, item3.ByteSize(), freedSize) assert.Equal(t, 1, removedCount) assert.Equal(t, 0, cache.Count()) assert.Equal(t, uint64(0), cache.Size()) assert.Nil(t, cache.Get(300)) } func TestBoundedAckCache_AlreadyAcked(t *testing.T) { cache := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(10), dynamicproperties.GetIntPropertyFn(1000), log.NewNoop(), nil, "", ) item1 := newTestItem(100, "item1") item2 := newTestItem(200, "item2") item3 := newTestItem(50, "item3") // lower than ack level require.NoError(t, cache.Put(item1, item1.ByteSize())) require.NoError(t, cache.Put(item2, item2.ByteSize())) // Ack at level 150 _, _ = cache.Ack(150) assert.Equal(t, 1, cache.Count()) assert.Nil(t, cache.Get(100)) assert.Equal(t, item2, cache.Get(200)) // Try to add item with sequence ID lower than ack level assert.Equal(t, ErrAlreadyAcked, cache.Put(item3, item3.ByteSize())) assert.Equal(t, 1, cache.Count()) // No change // Try to add item1 again (already acked) assert.Equal(t, ErrAlreadyAcked, cache.Put(item1, item1.ByteSize())) assert.Equal(t, 1, cache.Count()) // No change } func TestBoundedAckCache_CountLimit(t *testing.T) { cache := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(3), // max count = 3 dynamicproperties.GetIntPropertyFn(1000), // large size limit log.NewNoop(), nil, "", ) item1 := newTestItem(100, "item1") item2 := newTestItem(200, "item2") item3 := newTestItem(300, "item3") item4 := newTestItem(400, "item4") require.NoError(t, cache.Put(item1, item1.ByteSize())) require.NoError(t, cache.Put(item2, item2.ByteSize())) require.NoError(t, cache.Put(item3, item3.ByteSize())) assert.Equal(t, 3, cache.Count()) // Fourth item should be rejected assert.Equal(t, ErrAckCacheFull, cache.Put(item4, item4.ByteSize())) assert.Equal(t, 3, cache.Count()) assert.Nil(t, cache.Get(400)) // After acking, should be able to add more _, _ = cache.Ack(200) assert.Equal(t, 1, cache.Count()) require.NoError(t, cache.Put(item4, item4.ByteSize())) assert.Equal(t, 2, cache.Count()) assert.Equal(t, item4, cache.Get(400)) } func TestBoundedAckCache_SizeLimit(t *testing.T) { cache := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(10), // large count limit dynamicproperties.GetIntPropertyFn(100), // max size = 100 log.NewNoop(), nil, "", ) item1 := newTestItemWithSize(100, "item1", 40) item2 := newTestItemWithSize(200, "item2", 40) item3 := newTestItemWithSize(300, "item3", 30) // would exceed size limit require.NoError(t, cache.Put(item1, item1.ByteSize())) require.NoError(t, cache.Put(item2, item2.ByteSize())) assert.Equal(t, uint64(80), cache.Size()) assert.Equal(t, 2, cache.Count()) // Third item should be rejected due to size assert.Equal(t, ErrAckCacheFull, cache.Put(item3, item3.ByteSize())) assert.Equal(t, uint64(80), cache.Size()) assert.Equal(t, 2, cache.Count()) assert.Nil(t, cache.Get(300)) // After acking, should be able to add more _, _ = cache.Ack(150) assert.Equal(t, uint64(40), cache.Size()) assert.Equal(t, 1, cache.Count()) require.NoError(t, cache.Put(item3, item3.ByteSize())) assert.Equal(t, uint64(70), cache.Size()) assert.Equal(t, 2, cache.Count()) } func TestBoundedAckCache_DynamicLimits(t *testing.T) { maxCount := dynamicproperties.GetIntPropertyFn(2) maxSize := dynamicproperties.GetIntPropertyFn(100) cache := NewBoundedAckCache[*testItem](maxCount, maxSize, log.NewNoop(), nil, "") item1 := newTestItem(100, "item1") item2 := newTestItem(200, "item2") item3 := newTestItem(300, "item3") require.NoError(t, cache.Put(item1, item1.ByteSize())) require.NoError(t, cache.Put(item2, item2.ByteSize())) assert.Equal(t, ErrAckCacheFull, cache.Put(item3, item3.ByteSize())) // Increase count limit dynamically maxCount = dynamicproperties.GetIntPropertyFn(5) cache.(*BoundedAckCache[*testItem]).maxCount = maxCount // Now the third item should be accepted require.NoError(t, cache.Put(item3, item3.ByteSize())) assert.Equal(t, 3, cache.Count()) } func TestBoundedAckCache_ConcurrentOperations(t *testing.T) { cache := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(1000), dynamicproperties.GetIntPropertyFn(100000), log.NewNoop(), nil, "", ) // This test ensures no race conditions with concurrent access done := make(chan bool, 3) // Writer goroutine go func() { defer func() { done <- true }() for i := 0; i < 100; i++ { item := newTestItem(int64(i*10), "item") cache.Put(item, item.ByteSize()) } }() // Reader goroutine go func() { defer func() { done <- true }() for i := 0; i < 100; i++ { cache.Get(int64(i * 10)) cache.Size() cache.Count() } }() // Acker goroutine go func() { defer func() { done <- true }() for i := 0; i < 10; i++ { _, _ = cache.Ack(int64(i * 100)) } }() // Wait for all goroutines for i := 0; i < 3; i++ { <-done } // Final ack to clean up _, _ = cache.Ack(1000) assert.Equal(t, 0, cache.Count()) assert.Equal(t, uint64(0), cache.Size()) } func TestBoundedAckCache_WithBudgetManager(t *testing.T) { budgetMgr := NewBudgetManager( "test-budget", dynamicproperties.GetIntPropertyFn(1000), dynamicproperties.GetIntPropertyFn(100), AdmissionOptimistic, 0, nil, log.NewNoop(), nil, ) cache := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(10), dynamicproperties.GetIntPropertyFn(1000), log.NewNoop(), budgetMgr, "cache1", ) item1 := newTestItemWithSize(100, "item1", 100) item2 := newTestItemWithSize(200, "item2", 150) require.NoError(t, cache.Put(item1, item1.ByteSize())) assert.Equal(t, uint64(100), budgetMgr.UsedBytes()) assert.Equal(t, int64(1), budgetMgr.UsedCount()) require.NoError(t, cache.Put(item2, item2.ByteSize())) assert.Equal(t, uint64(250), budgetMgr.UsedBytes()) assert.Equal(t, int64(2), budgetMgr.UsedCount()) freedSize, freedCount := cache.Ack(100) assert.Equal(t, uint64(100), freedSize) assert.Equal(t, 1, freedCount) assert.Equal(t, uint64(150), budgetMgr.UsedBytes()) assert.Equal(t, int64(1), budgetMgr.UsedCount()) freedSize, freedCount = cache.Ack(200) assert.Equal(t, uint64(150), freedSize) assert.Equal(t, 1, freedCount) assert.Equal(t, uint64(0), budgetMgr.UsedBytes()) assert.Equal(t, int64(0), budgetMgr.UsedCount()) } func TestBoundedAckCache_WithBudgetManager_BudgetExceeded(t *testing.T) { budgetMgr := NewBudgetManager( "test-budget", dynamicproperties.GetIntPropertyFn(200), dynamicproperties.GetIntPropertyFn(10), AdmissionOptimistic, 0, nil, log.NewNoop(), nil, ) cache := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(10), dynamicproperties.GetIntPropertyFn(1000), log.NewNoop(), budgetMgr, "cache1", ) item1 := newTestItemWithSize(100, "item1", 150) item2 := newTestItemWithSize(200, "item2", 100) require.NoError(t, cache.Put(item1, item1.ByteSize())) assert.Equal(t, uint64(150), budgetMgr.UsedBytes()) err := cache.Put(item2, item2.ByteSize()) assert.Equal(t, ErrBytesBudgetExceeded, err) assert.Equal(t, uint64(150), budgetMgr.UsedBytes()) assert.Equal(t, int64(1), budgetMgr.UsedCount()) assert.Equal(t, 1, cache.Count()) assert.Nil(t, cache.Get(200)) } func TestBoundedAckCache_WithBudgetManager_MultipleCaches(t *testing.T) { budgetMgr := NewBudgetManager( "test-budget", dynamicproperties.GetIntPropertyFn(500), dynamicproperties.GetIntPropertyFn(100), AdmissionOptimistic, 0, nil, log.NewNoop(), nil, ) cache1 := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(10), dynamicproperties.GetIntPropertyFn(1000), log.NewNoop(), budgetMgr, "cache1", ) cache2 := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(10), dynamicproperties.GetIntPropertyFn(1000), log.NewNoop(), budgetMgr, "cache2", ) item1 := newTestItemWithSize(100, "item1", 200) item2 := newTestItemWithSize(200, "item2", 200) require.NoError(t, cache1.Put(item1, item1.ByteSize())) assert.Equal(t, uint64(200), budgetMgr.UsedBytes()) require.NoError(t, cache2.Put(item2, item2.ByteSize())) assert.Equal(t, uint64(400), budgetMgr.UsedBytes()) freedSize, freedCount := cache1.Ack(100) assert.Equal(t, uint64(200), freedSize) assert.Equal(t, 1, freedCount) assert.Equal(t, uint64(200), budgetMgr.UsedBytes()) freedSize, freedCount = cache2.Ack(200) assert.Equal(t, uint64(200), freedSize) assert.Equal(t, 1, freedCount) assert.Equal(t, uint64(0), budgetMgr.UsedBytes()) } func BenchmarkBoundedAckCache(b *testing.B) { cache := NewBoundedAckCache[*testItem]( dynamicproperties.GetIntPropertyFn(10000), dynamicproperties.GetIntPropertyFn(1000000), log.NewNoop(), nil, "", ) // Pre-populate cache for i := 0; i < 5000; i++ { item := newTestItem(int64(i*100), "benchmark_item") cache.Put(item, item.ByteSize()) } b.ResetTimer() for n := 0; n < b.N; n++ { sequenceID := int64(n * 100) cache.Get(sequenceID) item := newTestItem(int64(n*100+500000), "new_item") cache.Put(item, item.ByteSize()) if n%100 == 0 { _, _ = cache.Ack(sequenceID) } } } ================================================ FILE: common/cache/budget.go ================================================ package cache import ( "context" "errors" "math" "sync" "sync/atomic" "time" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) // Manager implements a generic host-scoped budget manager suitable for // both non-evicting caches (admission/backpressure) and evicting caches (e.g., LRU). // // Fair Share Logic: // The budget manager implements a two-tier soft cap system to fairly distribute capacity across // multiple active caches while preventing any single cache from monopolizing resources: // - Free space tier: (threshold * capacity) - shared by all caches on a first-come basis // - Fair share tier: ((1 - threshold) * capacity) / activeCaches - allocated per active cache // // An active cache is one that currently has non-zero usage (usedBytes > 0 or usedCount > 0). // This ensures that when multiple caches are active, each gets a guaranteed fair share of capacity, // preventing scenarios where one cache consumes all available space and starves others. // // There are two admission modes available: // - AdmissionOptimistic => add-then-undo on failure; may briefly overshoot limits // - AdmissionStrict => pre-check with CAS; never overshoots limits // // Note: Soft cap admission is always optimistic regardless of the configured mode. // // There are two modes available for reclaiming memory from the cache: // - ReserveOrReclaimSelfRelease => reclaim does its own Release() calls (per-item or per-batch). // - ReserveOrReclaimManagerRelease => reclaim returns totals; manager calls Release() once. // // Pick the reclaim mode that fits the cache's eviction style (self-release vs manager-release) // and admission mode based on whether temporary overshooting is acceptable (Optimistic vs Strict). // // How to use it: // // Manager is a means to track cache size across multiple caches at the Module or Host level. // It solves the problem of accounting memory for multiple caches working with a limited total amount of memory // in a centralized way, allowing each cache to operate independently while respecting global limits. // // The cache implementation should use the callback-based methods to ensure proper budget tracking // with automatic cleanup on errors. // // Example usage - Adding an item to the cache: // // func (c *myCache) Put(key, value interface{}) error { // itemSizeBytes := calculateSize(value) // return c.budgetMgr.ReserveWithCallback(c.cacheID, itemSizeBytes, 1, func() error { // return c.cache.Put(key, value) // }) // } // // Example usage - Removing an item from the cache: // // func (c *myCache) Delete(key interface{}) error { // return c.budgetMgr.ReleaseWithCallback(c.cacheID, func() (uint64, int64, error) { // bytes, count, err := c.cache.Delete(key) // return bytes, count, err // }) // } // // Example usage - Adding with automatic eviction (manager-release pattern): // // func (c *myCache) PutWithEviction(ctx context.Context, key, value interface{}) error { // itemSizeBytes := calculateSize(value) // return c.budgetMgr.ReserveOrReclaimManagerReleaseWithCallback( // ctx, c.cacheID, itemSizeBytes, 1, true, // func(needBytes uint64, needCount int64) (uint64, int64) { // return c.cache.EvictLRU(needBytes, needCount) // }, // func() error { // return c.cache.Put(key, value) // }, // ) // } // // Example usage - Adding with automatic eviction (self-release pattern): // // func (c *myCache) PutWithEviction(ctx context.Context, key, value interface{}) error { // itemSizeBytes := calculateSize(value) // return c.budgetMgr.ReserveOrReclaimSelfReleaseWithCallback( // ctx, c.cacheID, itemSizeBytes, 1, true, // func(needBytes uint64, needCount int64) { // // Evict items one-by-one until we've freed enough // var freedBytes, freedCount uint64, int64 // for freedBytes < needBytes || freedCount < needCount { // evictedBytes, evictedCount, err := c.cache.EvictOldest() // if err != nil { // break // } // c.budgetMgr.ReleaseForCache(c.cacheID, evictedBytes, evictedCount) // freedBytes += evictedBytes // freedCount += evictedCount // } // }, // func() error { // return c.cache.Put(key, value) // }, // ) // } type Manager interface { // Callback-based reserve methods (reserve -> callback -> release on error) // ReserveWithCallback reserves capacity, executes callback, releases on callback error. ReserveWithCallback(cacheID string, nBytes uint64, nCount int64, callback func() error) error // ReserveBytesWithCallback reserves bytes, executes callback, releases on callback error. ReserveBytesWithCallback(cacheID string, nBytes uint64, callback func() error) error // ReserveCountWithCallback reserves count, executes callback, releases on callback error. ReserveCountWithCallback(cacheID string, nCount int64, callback func() error) error // Callback-based release methods (callback -> release on success) // ReleaseWithCallback executes callback, releases capacity if callback succeeds. ReleaseWithCallback(cacheID string, callback func() (nBytes uint64, nCount int64, err error)) error // ReleaseBytesWithCallback executes callback, releases bytes if callback succeeds. ReleaseBytesWithCallback(cacheID string, callback func() (freedBytes uint64, err error)) error // ReleaseCountWithCallback executes callback, releases count if callback succeeds. ReleaseCountWithCallback(cacheID string, callback func() (freedCount int64, err error)) error // Callback-based reclaim methods // ReserveOrReclaimSelfReleaseWithCallback reserves/reclaims capacity, executes callback, releases on callback error. // todo: make sure to test before implementing caches that use this, for example LRU caches. Tests have only been done in unit tests ReserveOrReclaimSelfReleaseWithCallback(ctx context.Context, cacheID string, nBytes uint64, nCount int64, retriable bool, reclaim ReclaimSelfRelease, callback func() error) error // ReserveOrReclaimManagerReleaseWithCallback reserves/reclaims capacity, executes callback, releases on callback error. // todo: make sure to test before implementing caches that use this, for example LRU caches. Tests have only been done in unit tests ReserveOrReclaimManagerReleaseWithCallback(ctx context.Context, cacheID string, nBytes uint64, nCount int64, retriable bool, reclaim ReclaimManagerRelease, callback func() error) error // UsedBytes returns current used bytes. UsedBytes() uint64 // UsedCount returns current used count. UsedCount() int64 // CapacityBytes returns the current effective bytes capacity (math.MaxUint64 means "unlimited"). CapacityBytes() uint64 // CapacityCount returns the current effective count capacity (math.MaxInt64 means "unlimited"). CapacityCount() int64 // Stop stops the metrics ticker and releases resources. Stop() } type AdmissionMode uint8 const ( AdmissionOptimistic AdmissionMode = iota // add-then-undo; may overshoot briefly AdmissionStrict // CAS pre-check; never overshoots ) const ( budgetTypeBytes = "bytes" budgetTypeCount = "count" ) var ( ErrBytesBudgetExceeded = errors.New("bytes budget exceeded") ErrCountBudgetExceeded = errors.New("count budget exceeded") ErrBytesSoftCapExceeded = errors.New("bytes soft cap threshold exceeded") ErrCountSoftCapExceeded = errors.New("count soft cap threshold exceeded") ErrOverflow = errors.New("budget counter overflow") ErrInvalidValue = errors.New("invalid negative reserve value") ErrInsufficientUsageToReclaim = errors.New("insufficient usage to reclaim") ErrInvalidRequest = errors.New("invalid request: both additionalBytes and additionalCount are zero") ) type ReclaimSelfRelease func(needBytes uint64, needCount int64) // ReclaimManagerRelease is used when the manager should call Release once with totals. // IMPORTANT: Do NOT call mgr.Release(...) inside this callback, or you will double-release. type ReclaimManagerRelease func(needBytes uint64, needCount int64) (freedBytes uint64, freedCount int64) // CapEnforcementResult contains the result of capacity enforcement (both hard and soft cap) for a cache type CapEnforcementResult struct { FreeBytes uint64 // bytes allocated from free capacity (on success only) FairShareBytes uint64 // bytes allocated from fair share capacity (on success only) FreeCount int64 // count allocated from free capacity (on success only) FairShareCount int64 // count allocated from fair share capacity (on success only) AvailableBytes uint64 // total bytes available for this cache (min of soft cap and hard cap constraints) AvailableCount int64 // total count available for this cache (min of soft cap and hard cap constraints) } // Usage tracks usage for a specific cache type Usage struct { mu sync.Mutex // protects all fields below usedBytes uint64 // total bytes used by this cache usedCount int64 // total count used by this cache // Capacity type breakdown for fairness strategies fairShareCapacityBytes uint64 // bytes from (1-threshold) portion freeCapacityBytes uint64 // bytes from threshold portion fairShareCapacityCount int64 // count from (1-threshold) portion freeCapacityCount int64 // count from threshold portion } type manager struct { name string maxBytes dynamicproperties.IntPropertyFn maxCount dynamicproperties.IntPropertyFn reclaimBackoff time.Duration admission AdmissionMode scope metrics.Scope logger log.Logger // Rate-limited logger (1 RPS) enforcementSoftCapThreshold dynamicproperties.FloatPropertyFn usedBytes uint64 // atomic - total usage across all caches usedCount int64 // atomic - total usage across all caches // Per-cache usage tracking using sync.Map for lock-free operations cacheUsage sync.Map // map[string]*Usage // Optimistic tracking of active caches (usage > 0) activeCacheCount int64 // atomic // Metrics ticker metricsTicker *time.Ticker metricsStop chan struct{} } // NewBudgetManager creates a new Manager. // // Parameters: // - name: manager name for identification // - maxBytes: bytes capacity semantics: // < 0 => enforcement disabled (unlimited; still tracked) // == 0 => caching fully disabled (deny all reserves) // > 0 => enforcement enabled with that capacity // - maxCount: count capacity semantics (same as maxBytes) // - admission: AdmissionOptimistic (add-then-undo) or AdmissionStrict (CAS pre-check) // - reclaimBackoff: optional sleep between reclaim attempts (defaults to ~100µs if zero) // - scope: metrics scope for emitting metrics (can be nil) // - logger: logger for diagnostic messages (can be nil) // - softCapThreshold: optional percentage (0.0-1.0) defining how capacity is split between two tiers: // - Free space tier: (threshold * capacity) - shared by all caches // - Fair share tier: ((1 - threshold) * capacity) / activeCaches - allocated per cache // The fair share capacity is equal to the fair share capacity ((1 - threshold) * capacity) divided by the number // of active caches (caches with usage > 0). func NewBudgetManager( name string, maxBytes dynamicproperties.IntPropertyFn, maxCount dynamicproperties.IntPropertyFn, admission AdmissionMode, reclaimBackoff time.Duration, scope metrics.Scope, logger log.Logger, softCapThreshold dynamicproperties.FloatPropertyFn, // nil defaults to 1.0 (disabled) ) Manager { if scope == nil { scope = metrics.NoopScope } if maxBytes == nil { maxBytes = dynamicproperties.GetIntPropertyFn(-1) // unlimited if maxBytes is not provided } if maxCount == nil { maxCount = dynamicproperties.GetIntPropertyFn(-1) // unlimited if maxCount is not provided } if softCapThreshold == nil { softCapThreshold = dynamicproperties.GetFloatPropertyFn(1.0) // no threshold if softCapThreshold is not provided } if admission == 0 { admission = AdmissionOptimistic } // Create throttled logger (1 log per second) var throttledLogger log.Logger if logger != nil { throttledLogger = log.NewThrottledLogger(logger, dynamicproperties.GetIntPropertyFn(1)) } logger.Info("Budget manager started", tag.Name(name)) mgr := &manager{ name: name, maxBytes: maxBytes, maxCount: maxCount, reclaimBackoff: reclaimBackoff, admission: admission, scope: scope.Tagged(metrics.BudgetManagerNameTag(name)), logger: throttledLogger, enforcementSoftCapThreshold: softCapThreshold, metricsTicker: time.NewTicker(10 * time.Second), metricsStop: make(chan struct{}), } // Emit initial metrics mgr.updateMetrics() // Start metrics update goroutine go mgr.metricsLoop() return mgr } // Stop stops the metrics ticker and releases resources func (m *manager) Stop() { m.metricsTicker.Stop() close(m.metricsStop) } // metricsLoop periodically updates metrics every 10 seconds func (m *manager) metricsLoop() { for { select { case <-m.metricsTicker.C: m.updateMetrics() case <-m.metricsStop: return } } } // updateMetrics emits current state metrics func (m *manager) updateMetrics() { // Skip metrics if both capacity limits are disabled (set to 0) if m.maxBytes() == 0 && m.maxCount() == 0 { return } // Emit capacity metrics capacityBytes := m.CapacityBytes() // Only emit if not unlimited if capacityBytes != math.MaxUint64 { m.scope.UpdateGauge(metrics.BudgetManagerCapacityBytes, float64(capacityBytes)) } capacityCount := m.CapacityCount() // Only emit if not unlimited if capacityCount != math.MaxInt64 { m.scope.UpdateGauge(metrics.BudgetManagerCapacityCount, float64(capacityCount)) } // Emit usage metrics m.scope.UpdateGauge(metrics.BudgetManagerUsedBytes, float64(m.UsedBytes())) m.scope.UpdateGauge(metrics.BudgetManagerUsedCount, float64(m.UsedCount())) // Emit soft threshold m.scope.UpdateGauge(metrics.BudgetManagerSoftThreshold, m.enforcementSoftCapThreshold()) // Emit active cache count m.scope.UpdateGauge(metrics.BudgetManagerActiveCacheCount, float64(atomic.LoadInt64(&m.activeCacheCount))) } // emitHardCapExceeded logs when hard capacity limit is exceeded and increments counter func (m *manager) emitHardCapExceeded(cacheID, budgetType string, requested, available uint64) { // Skip metrics if both capacity limits are disabled (set to 0) if m.maxBytes() == 0 && m.maxCount() == 0 { return } if m.scope != nil { m.scope.IncCounter(metrics.BudgetManagerHardCapExceeded) } if m.logger != nil { m.logger.Debug("Hard capacity limit exceeded", tag.Name(m.name), tag.CacheID(cacheID), tag.Value(budgetType), tag.Counter(int(requested)), tag.Number(int64(available)), ) } } // emitSoftCapExceeded logs when soft cap is exceeded and increments counter func (m *manager) emitSoftCapExceeded(cacheID, budgetType string, requested, available uint64) { // Skip metrics if both capacity limits are disabled (set to 0) if m.maxBytes() == 0 && m.maxCount() == 0 { return } if m.scope != nil { m.scope.IncCounter(metrics.BudgetManagerSoftCapExceeded) } if m.logger != nil { m.logger.Debug("Soft capacity limit exceeded", tag.Name(m.name), tag.CacheID(cacheID), tag.Value(budgetType), tag.Counter(int(requested)), tag.Number(int64(available)), ) } } // emitCapacityExceeded emits appropriate metrics based on the error type func (m *manager) emitCapacityExceeded(cacheID string, err error, requestedBytes uint64, requestedCount int64, capResult CapEnforcementResult) { // Skip metrics if both capacity limits are disabled (set to 0) if m.maxBytes() == 0 && m.maxCount() == 0 { return } switch err { case ErrBytesBudgetExceeded: m.emitHardCapExceeded(cacheID, budgetTypeBytes, requestedBytes, capResult.AvailableBytes) case ErrCountBudgetExceeded: m.emitHardCapExceeded(cacheID, budgetTypeCount, uint64(requestedCount), uint64(capResult.AvailableCount)) case ErrBytesSoftCapExceeded: m.emitSoftCapExceeded(cacheID, budgetTypeBytes, requestedBytes, capResult.AvailableBytes) case ErrCountSoftCapExceeded: m.emitSoftCapExceeded(cacheID, budgetTypeCount, uint64(requestedCount), uint64(capResult.AvailableCount)) } } // Cache-aware Reserve methods implementing two-tier soft cap logic func (m *manager) ReserveForCache(cacheID string, nBytes uint64, nCount int64) error { // Check capacity constraints (both hard and soft cap) capResult, err := m.enforceCapForCache(cacheID, nBytes, nCount) if err != nil { m.emitCapacityExceeded(cacheID, err, nBytes, nCount, capResult) return err } cacheUsage := m.getCacheUsage(cacheID) cacheUsage.mu.Lock() defer cacheUsage.mu.Unlock() wasActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 if nBytes > 0 { if err := m.reserveBytes(nBytes); err != nil { return err } cacheUsage.usedBytes += nBytes cacheUsage.fairShareCapacityBytes += capResult.FairShareBytes cacheUsage.freeCapacityBytes += capResult.FreeBytes } if nCount > 0 { if err := m.reserveCount(nCount); err != nil { if nBytes > 0 { m.releaseBytes(nBytes) cacheUsage.usedBytes -= nBytes cacheUsage.fairShareCapacityBytes -= capResult.FairShareBytes cacheUsage.freeCapacityBytes -= capResult.FreeBytes } return err } cacheUsage.usedCount += nCount cacheUsage.fairShareCapacityCount += capResult.FairShareCount cacheUsage.freeCapacityCount += capResult.FreeCount } // Update active cache count while still holding the lock to prevent races isActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 if !wasActive && isActive { atomic.AddInt64(&m.activeCacheCount, 1) } return nil } func (m *manager) ReserveBytesForCache(cacheID string, nBytes uint64) error { // Check capacity constraints (both hard and soft cap) capResult, err := m.enforceCapForCache(cacheID, nBytes, 0) if err != nil { m.emitCapacityExceeded(cacheID, err, nBytes, 0, capResult) return err } cacheUsage := m.getCacheUsage(cacheID) cacheUsage.mu.Lock() defer cacheUsage.mu.Unlock() wasActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 if err := m.reserveBytes(nBytes); err != nil { return err } cacheUsage.usedBytes += nBytes cacheUsage.fairShareCapacityBytes += capResult.FairShareBytes cacheUsage.freeCapacityBytes += capResult.FreeBytes // Update active cache count while still holding the lock to prevent races isActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 if !wasActive && isActive { atomic.AddInt64(&m.activeCacheCount, 1) } return nil } func (m *manager) ReserveCountForCache(cacheID string, nCount int64) error { if nCount < 0 { if m.logger != nil { m.logger.Error("Invalid negative count value in ReserveCountForCache", tag.CacheID(cacheID), tag.Key("requested"), tag.Value(nCount), ) } return ErrInvalidValue } // Check capacity constraints (both hard and soft cap) capResult, err := m.enforceCapForCache(cacheID, 0, nCount) if err != nil { m.emitCapacityExceeded(cacheID, err, 0, nCount, capResult) return err } cacheUsage := m.getCacheUsage(cacheID) cacheUsage.mu.Lock() defer cacheUsage.mu.Unlock() wasActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 if err := m.reserveCount(nCount); err != nil { return err } cacheUsage.usedCount += nCount cacheUsage.fairShareCapacityCount += capResult.FairShareCount cacheUsage.freeCapacityCount += capResult.FreeCount // Update active cache count while still holding the lock to prevent races isActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 if !wasActive && isActive { atomic.AddInt64(&m.activeCacheCount, 1) } return nil } func (m *manager) reserveBytes(nBytes uint64) error { switch m.admission { case AdmissionStrict: return m.reserveBytesStrict(nBytes) default: return m.reserveBytesOptimistic(nBytes) } } func (m *manager) reserveCount(nCount int64) error { switch m.admission { case AdmissionStrict: return m.reserveCountStrict(nCount) default: return m.reserveCountOptimistic(nCount) } } func (m *manager) reserveBytesStrict(nBytes uint64) error { capB := m.CapacityBytes() for { old := atomic.LoadUint64(&m.usedBytes) if old > math.MaxUint64-nBytes { if m.logger != nil { m.logger.Error("Bytes budget overflow detected in strict mode", tag.Key("requested"), tag.Value(nBytes), tag.Key("current-used"), tag.Value(old), ) } return ErrOverflow } if old+nBytes > capB { m.emitHardCapExceeded("", budgetTypeBytes, old+nBytes, capB) return ErrBytesBudgetExceeded } if atomic.CompareAndSwapUint64(&m.usedBytes, old, old+nBytes) { return nil } // retry on contention } } func (m *manager) reserveBytesOptimistic(nBytes uint64) error { capB := m.CapacityBytes() newVal := atomic.AddUint64(&m.usedBytes, nBytes) if newVal < nBytes { // wrap-around m.releaseBytes(nBytes) if m.logger != nil { m.logger.Debug("Bytes budget overflow detected", tag.Key("requested"), tag.Value(nBytes), tag.Key("previous-used"), tag.Value(newVal-nBytes), ) } return ErrOverflow } if newVal <= capB { return nil } m.releaseBytes(nBytes) m.emitHardCapExceeded("", budgetTypeBytes, newVal, capB) return ErrBytesBudgetExceeded } func (m *manager) reserveCountStrict(nCount int64) error { capC := m.CapacityCount() for { old := atomic.LoadInt64(&m.usedCount) if old > math.MaxInt64-nCount { if m.logger != nil { m.logger.Error("Count budget overflow detected in strict mode", tag.Key("requested"), tag.Value(nCount), tag.Key("current-used"), tag.Value(old), ) } return ErrOverflow } if old+nCount > capC { m.emitHardCapExceeded("", budgetTypeCount, uint64(old+nCount), uint64(capC)) return ErrCountBudgetExceeded } if atomic.CompareAndSwapInt64(&m.usedCount, old, old+nCount) { return nil } // retry on contention } } func (m *manager) reserveCountOptimistic(nCount int64) error { capC := m.CapacityCount() newVal := atomic.AddInt64(&m.usedCount, nCount) if newVal < 0 { // wrap-around m.releaseCount(nCount) return ErrOverflow } if newVal <= capC { return nil } m.releaseCount(nCount) m.emitHardCapExceeded("", budgetTypeCount, uint64(newVal), uint64(capC)) return ErrCountBudgetExceeded } // reclaimNeeds holds the calculated reclaim requirements type reclaimNeeds struct { needBytes uint64 needCount int64 } // tryReserveOrCalculateReclaimNeeds attempts to reserve capacity immediately. // If capacity is not available, it calculates how much the cache needs to reclaim // and validates that the cache has enough usage to satisfy the reclaim requirement. // Returns: // - nil, nil if reservation succeeded immediately // - reclaimNeeds, nil if reclaim is needed and cache has sufficient usage // - nil, error if reservation failed and cannot be satisfied via reclaim func (m *manager) tryReserveOrCalculateReclaimNeeds(cacheID string, nBytes uint64, nCount int64) (*reclaimNeeds, error) { // Check capacity constraints (both soft and hard cap) capResult, err := m.enforceCapForCache(cacheID, nBytes, nCount) if err == nil { // Capacity available - try to reserve if err := m.ReserveForCache(cacheID, nBytes, nCount); err == nil { return nil, nil } // lost the race; caller should retry return nil, err } // Calculate reclaim amounts based on available capacity allowedBytes := capResult.AvailableBytes allowedCount := capResult.AvailableCount needB := nBytes - allowedBytes needC := nCount - allowedCount // Check if this cache can reclaim enough to satisfy the constraint cacheUsage := m.getCacheUsage(cacheID) cacheUsage.mu.Lock() defer cacheUsage.mu.Unlock() currentCacheBytes := cacheUsage.usedBytes currentCacheCount := cacheUsage.usedCount if (currentCacheBytes+allowedBytes < nBytes) || (currentCacheCount+allowedCount < nCount) { // Cache doesn't have enough to reclaim return nil, ErrInsufficientUsageToReclaim } return &reclaimNeeds{ needBytes: needB, needCount: needC, }, nil } func (m *manager) ReserveOrReclaimSelfRelease( ctx context.Context, cacheID string, nBytes uint64, nCount int64, retriable bool, reclaim ReclaimSelfRelease, ) error { if err := m.ReserveForCache(cacheID, nBytes, nCount); err == nil { return nil } else if !retriable { return err // single-shot; no reclaim } for { select { case <-ctx.Done(): return ctx.Err() default: } needs, err := m.tryReserveOrCalculateReclaimNeeds(cacheID, nBytes, nCount) if err != nil { return err } if needs == nil { // Reservation succeeded immediately return nil } if reclaim != nil && (needs.needBytes > 0 || needs.needCount > 0) { // The manager uses only atomic operations (no locks), so the cache // can safely take its own locks (e.g., for protecting internal data structures // during eviction) without risk of deadlock. The cache must call ReleaseForCache // separately after evicting items. reclaim(needs.needBytes, needs.needCount) // Fast path: try to consume immediately after reclaim before yielding. if err := m.ReserveForCache(cacheID, nBytes, nCount); err == nil { return nil } } m.yield() // tiny backoff to avoid busy spin } } func (m *manager) ReserveOrReclaimManagerRelease( ctx context.Context, cacheID string, nBytes uint64, nCount int64, retriable bool, reclaim ReclaimManagerRelease, ) error { if err := m.ReserveForCache(cacheID, nBytes, nCount); err == nil { return nil } else if !retriable { return err } for { select { case <-ctx.Done(): return ctx.Err() // propagate context cancellation/deadline default: } needs, err := m.tryReserveOrCalculateReclaimNeeds(cacheID, nBytes, nCount) if err != nil { return err } if needs == nil { // Reservation succeeded immediately return nil } if reclaim != nil && (needs.needBytes > 0 || needs.needCount > 0) { fb, fc := reclaim(needs.needBytes, needs.needCount) if fb > 0 || fc > 0 { m.ReleaseForCache(cacheID, fb, fc) // Fast-path: try to admit immediately after releasing if err := m.ReserveForCache(cacheID, nBytes, nCount); err == nil { return nil } } } m.yield() // small backoff to avoid busy spin } } func (m *manager) yield() { if m.reclaimBackoff > 0 { time.Sleep(m.reclaimBackoff) return } time.Sleep(100 * time.Microsecond) } // Cache-aware Release methods func (m *manager) ReleaseForCache(cacheID string, nBytes uint64, nCount int64) { cacheUsage := m.getCacheUsage(cacheID) cacheUsage.mu.Lock() defer cacheUsage.mu.Unlock() wasActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 if nBytes > 0 { m.releaseBytes(nBytes) // Update cache-specific usage tracking with over-release protection if nBytes > cacheUsage.usedBytes { if m.logger != nil { m.logger.Debug("Cache bytes over-release detected", tag.CacheID(cacheID), tag.Key("requested-release"), tag.Value(nBytes), tag.Key("current-used"), tag.Value(cacheUsage.usedBytes), ) } cacheUsage.usedBytes = 0 cacheUsage.fairShareCapacityBytes = 0 cacheUsage.freeCapacityBytes = 0 } else { cacheUsage.usedBytes -= nBytes // Update capacity type breakdown - subtract from fairShare first, then free remainingBytes := nBytes fairShareToSubtract := min(remainingBytes, cacheUsage.fairShareCapacityBytes) if fairShareToSubtract > 0 { cacheUsage.fairShareCapacityBytes -= fairShareToSubtract remainingBytes -= fairShareToSubtract } if remainingBytes > 0 { cacheUsage.freeCapacityBytes -= remainingBytes } } } if nCount > 0 { m.releaseCount(nCount) // Update cache-specific usage tracking with over-release protection if nCount > cacheUsage.usedCount { if m.logger != nil { m.logger.Debug("Cache count over-release detected", tag.CacheID(cacheID), tag.Key("requested-release"), tag.Value(nCount), tag.Key("current-used"), tag.Value(cacheUsage.usedCount), ) } cacheUsage.usedCount = 0 cacheUsage.fairShareCapacityCount = 0 cacheUsage.freeCapacityCount = 0 } else { cacheUsage.usedCount -= nCount // Update capacity type breakdown - subtract from fairShare first, then free remainingCount := nCount fairShareCountToSubtract := min(remainingCount, cacheUsage.fairShareCapacityCount) if fairShareCountToSubtract > 0 { cacheUsage.fairShareCapacityCount -= fairShareCountToSubtract remainingCount -= fairShareCountToSubtract } if remainingCount > 0 { cacheUsage.freeCapacityCount -= remainingCount } } } // Update active cache count while still holding the lock to prevent races isActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 if wasActive && !isActive { atomic.AddInt64(&m.activeCacheCount, -1) } } func (m *manager) ReleaseBytesForCache(cacheID string, nBytes uint64) { m.releaseBytes(nBytes) // Update cache-specific usage tracking cacheUsage := m.getCacheUsage(cacheID) cacheUsage.mu.Lock() defer cacheUsage.mu.Unlock() wasActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 // Update cache-specific usage tracking with over-release protection if nBytes > cacheUsage.usedBytes { if m.logger != nil { m.logger.Debug("Cache bytes over-release detected", tag.CacheID(cacheID), tag.Key("requested-release"), tag.Value(nBytes), tag.Key("current-used"), tag.Value(cacheUsage.usedBytes), ) } cacheUsage.usedBytes = 0 cacheUsage.fairShareCapacityBytes = 0 cacheUsage.freeCapacityBytes = 0 } else { cacheUsage.usedBytes -= nBytes // Update capacity type breakdown - subtract from fairShare first, then free remainingBytes := nBytes fairShareToSubtract := min(remainingBytes, cacheUsage.fairShareCapacityBytes) if fairShareToSubtract > 0 { cacheUsage.fairShareCapacityBytes -= fairShareToSubtract remainingBytes -= fairShareToSubtract } if remainingBytes > 0 { cacheUsage.freeCapacityBytes -= remainingBytes } } // Update active cache count while still holding the lock to prevent races isActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 if wasActive && !isActive { atomic.AddInt64(&m.activeCacheCount, -1) } } func (m *manager) ReleaseCountForCache(cacheID string, nCount int64) { m.releaseCount(nCount) // Update cache-specific usage tracking cacheUsage := m.getCacheUsage(cacheID) cacheUsage.mu.Lock() defer cacheUsage.mu.Unlock() wasActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 // Update cache-specific usage tracking with over-release protection if nCount > cacheUsage.usedCount { if m.logger != nil { m.logger.Debug("Cache count over-release detected", tag.CacheID(cacheID), tag.Key("requested-release"), tag.Value(nCount), tag.Key("current-used"), tag.Value(cacheUsage.usedCount), ) } cacheUsage.usedCount = 0 cacheUsage.fairShareCapacityCount = 0 cacheUsage.freeCapacityCount = 0 } else { cacheUsage.usedCount -= nCount // Update capacity type breakdown - subtract from fairShare first, then free remainingCount := nCount fairShareCountToSubtract := min(remainingCount, cacheUsage.fairShareCapacityCount) if fairShareCountToSubtract > 0 { cacheUsage.fairShareCapacityCount -= fairShareCountToSubtract remainingCount -= fairShareCountToSubtract } if remainingCount > 0 { cacheUsage.freeCapacityCount -= remainingCount } } // Update active cache count while still holding the lock to prevent races isActive := cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 if wasActive && !isActive { atomic.AddInt64(&m.activeCacheCount, -1) } } func (m *manager) releaseBytes(nBytes uint64) { for { old := atomic.LoadUint64(&m.usedBytes) var newVal uint64 if nBytes > old { // Over-release detected - clamp to 0 to prevent uint64 wraparound newVal = 0 if atomic.CompareAndSwapUint64(&m.usedBytes, old, newVal) { if m.logger != nil { m.logger.Debug("Bytes over-release detected", tag.Key("requested-release"), tag.Value(nBytes), tag.Key("current-used"), tag.Value(old), ) } return } } else { newVal = old - nBytes if atomic.CompareAndSwapUint64(&m.usedBytes, old, newVal) { return } } // retry on contention } } func (m *manager) releaseCount(nCount int64) { if nCount <= 0 { return } for { old := atomic.LoadInt64(&m.usedCount) var newVal int64 if nCount > old { // Over-release detected - clamp to 0 newVal = 0 if atomic.CompareAndSwapInt64(&m.usedCount, old, newVal) { if m.logger != nil { m.logger.Debug("Count over-release detected", tag.Key("requested-release"), tag.Value(nCount), tag.Key("current-used"), tag.Value(old), ) } return } } else { newVal = old - nCount if atomic.CompareAndSwapInt64(&m.usedCount, old, newVal) { return } } // retry on contention } } func (m *manager) ReserveWithCallback(cacheID string, nBytes uint64, nCount int64, callback func() error) error { if err := m.ReserveForCache(cacheID, nBytes, nCount); err != nil { return err } if err := callback(); err != nil { m.ReleaseForCache(cacheID, nBytes, nCount) return err } return nil } func (m *manager) ReserveBytesWithCallback(cacheID string, nBytes uint64, callback func() error) error { if err := m.ReserveBytesForCache(cacheID, nBytes); err != nil { return err } if err := callback(); err != nil { m.ReleaseBytesForCache(cacheID, nBytes) return err } return nil } func (m *manager) ReserveCountWithCallback(cacheID string, nCount int64, callback func() error) error { if err := m.ReserveCountForCache(cacheID, nCount); err != nil { return err } if err := callback(); err != nil { m.ReleaseCountForCache(cacheID, nCount) return err } return nil } func (m *manager) ReleaseWithCallback(cacheID string, callback func() (freedBytes uint64, freedCount int64, err error)) error { freedBytes, freedCount, err := callback() if err != nil { return err } m.ReleaseForCache(cacheID, freedBytes, freedCount) return nil } func (m *manager) ReleaseBytesWithCallback(cacheID string, callback func() (freedBytes uint64, err error)) error { freedBytes, err := callback() if err != nil { return err } m.ReleaseBytesForCache(cacheID, freedBytes) return nil } func (m *manager) ReleaseCountWithCallback(cacheID string, callback func() (freedCount int64, err error)) error { freedCount, err := callback() if err != nil { return err } m.ReleaseCountForCache(cacheID, freedCount) return nil } func (m *manager) ReserveOrReclaimSelfReleaseWithCallback(ctx context.Context, cacheID string, nBytes uint64, nCount int64, retriable bool, reclaim ReclaimSelfRelease, callback func() error) error { if err := m.ReserveOrReclaimSelfRelease(ctx, cacheID, nBytes, nCount, retriable, reclaim); err != nil { return err } if err := callback(); err != nil { m.ReleaseForCache(cacheID, nBytes, nCount) return err } return nil } func (m *manager) ReserveOrReclaimManagerReleaseWithCallback(ctx context.Context, cacheID string, nBytes uint64, nCount int64, retriable bool, reclaim ReclaimManagerRelease, callback func() error) error { if err := m.ReserveOrReclaimManagerRelease(ctx, cacheID, nBytes, nCount, retriable, reclaim); err != nil { return err } if err := callback(); err != nil { m.ReleaseForCache(cacheID, nBytes, nCount) return err } return nil } func (m *manager) UsedBytes() uint64 { return atomic.LoadUint64(&m.usedBytes) } func (m *manager) UsedCount() int64 { return atomic.LoadInt64(&m.usedCount) } func (m *manager) CapacityBytes() uint64 { v := m.maxBytes() if v < 0 { return math.MaxUint64 } return uint64(v) // includes 0 as a valid, enforced cap } func (m *manager) CapacityCount() int64 { v := m.maxCount() if v < 0 { return math.MaxInt64 } // clamp just in case if v > math.MaxInt64 { return math.MaxInt64 } return int64(v) // includes 0 as a valid, enforced cap } func (m *manager) AvailableBytes() uint64 { c := m.CapacityBytes() if c == 0 { return 0 } u := m.UsedBytes() if u >= c { return 0 } return c - u } func (m *manager) AvailableCount() int64 { c := m.CapacityCount() if c == 0 { return 0 } u := m.UsedCount() if u >= c { return 0 } return c - u } // getCacheUsage returns the CacheUsage for a given cacheID, creating one if it doesn't exist func (m *manager) getCacheUsage(cacheID string) *Usage { if value, ok := m.cacheUsage.Load(cacheID); ok { return value.(*Usage) } // Create new cache usage entry newUsage := &Usage{} actual, _ := m.cacheUsage.LoadOrStore(cacheID, newUsage) return actual.(*Usage) } // getActiveCacheCount returns the current count of active caches func (m *manager) getActiveCacheCount() int64 { return atomic.LoadInt64(&m.activeCacheCount) } // getCacheState safely reads the current state of a cache with defer unlock func (m *manager) getCacheState(cacheID string) (isActive bool, fairShareBytes uint64, fairShareCount int64) { cacheUsage := m.getCacheUsage(cacheID) cacheUsage.mu.Lock() defer cacheUsage.mu.Unlock() isActive = cacheUsage.usedBytes > 0 || cacheUsage.usedCount > 0 fairShareBytes = cacheUsage.fairShareCapacityBytes fairShareCount = cacheUsage.fairShareCapacityCount return } // enforceCapForCache implements both hard and soft cap logic func (m *manager) enforceCapForCache(cacheID string, additionalBytes uint64, additionalCount int64) (CapEnforcementResult, error) { // Check if caching is completely disabled (zero capacity) if m.CapacityBytes() == 0 || m.CapacityCount() == 0 { var err error if m.CapacityBytes() == 0 { err = ErrBytesBudgetExceeded } else { err = ErrCountBudgetExceeded } return CapEnforcementResult{ FreeBytes: 0, FairShareBytes: 0, FreeCount: 0, FairShareCount: 0, AvailableBytes: 0, AvailableCount: 0, }, err } // Reject requests with zero bytes AND zero count if additionalBytes == 0 && additionalCount == 0 { return CapEnforcementResult{ FreeBytes: 0, FairShareBytes: 0, FreeCount: 0, FairShareCount: 0, AvailableBytes: 0, AvailableCount: 0, }, ErrInvalidRequest } // Get hard cap constraints hardCapAvailableBytes := m.AvailableBytes() hardCapAvailableCount := m.AvailableCount() // Check soft cap constraints managerThreshold := m.enforcementSoftCapThreshold() if managerThreshold >= 0 && managerThreshold < 1 { // Soft caps enabled - calculate soft cap constraints thresholdBytes := uint64(float64(m.CapacityBytes()) * managerThreshold) thresholdCount := int64(float64(m.CapacityCount()) * managerThreshold) // Calculate available free space freeSpaceBytes := uint64(0) if thresholdBytes > m.UsedBytes() { freeSpaceBytes = thresholdBytes - m.UsedBytes() } freeSpaceCount := int64(0) if thresholdCount > m.UsedCount() { freeSpaceCount = thresholdCount - m.UsedCount() } // Calculate per-cache fair share limits activeCaches := m.getActiveCacheCount() // Check if this cache is currently inactive but will become active cacheIsActive, currentFairShareBytes, currentFairShareCount := m.getCacheState(cacheID) if !cacheIsActive { // Cache will become active, include it in the count activeCaches++ } else if activeCaches == 0 { // Cache is already active, but global counter hasn't been updated yet. // This happens because we update activeCacheCount under per-cache lock, // but read it without any lock. Ensure we count at least this active cache. activeCaches = 1 } fairShareCapacityBytes := uint64(float64(m.CapacityBytes()) * (1.0 - managerThreshold)) fairShareCapacityCount := int64(float64(m.CapacityCount()) * (1.0 - managerThreshold)) fairSharePerCacheBytes := fairShareCapacityBytes / uint64(activeCaches) fairSharePerCacheCount := fairShareCapacityCount / activeCaches // Calculate available fair share capacity for this cache fairShareAvailableBytes := uint64(0) if fairSharePerCacheBytes > currentFairShareBytes { fairShareAvailableBytes = fairSharePerCacheBytes - currentFairShareBytes } fairShareAvailableCount := int64(0) if fairSharePerCacheCount > currentFairShareCount { fairShareAvailableCount = fairSharePerCacheCount - currentFairShareCount } // Total soft cap available is free space + fair share for this cache softCapAvailableBytes := freeSpaceBytes + fairShareAvailableBytes softCapAvailableCount := freeSpaceCount + fairShareAvailableCount // Available capacity is minimum of soft cap and hard cap constraints availableBytes := min(softCapAvailableBytes, hardCapAvailableBytes) availableCount := min(softCapAvailableCount, hardCapAvailableCount) // Check if the request can be satisfied if additionalBytes <= availableBytes && additionalCount <= availableCount { // Success - calculate allocation breakdown freeBytesUsage := min(additionalBytes, freeSpaceBytes) fairShareBytesUsage := additionalBytes - freeBytesUsage freeCountUsage := min(additionalCount, freeSpaceCount) fairShareCountUsage := additionalCount - freeCountUsage return CapEnforcementResult{ FreeBytes: freeBytesUsage, FairShareBytes: fairShareBytesUsage, FreeCount: freeCountUsage, FairShareCount: fairShareCountUsage, AvailableBytes: availableBytes, AvailableCount: availableCount, }, nil } // Determine which constraint failed var err error if additionalBytes > hardCapAvailableBytes || additionalCount > hardCapAvailableCount { // Hard cap constraint failed if additionalBytes > hardCapAvailableBytes { err = ErrBytesBudgetExceeded } else { err = ErrCountBudgetExceeded } } else { // Soft cap constraint failed if additionalBytes > softCapAvailableBytes { err = ErrBytesSoftCapExceeded } else { err = ErrCountSoftCapExceeded } } return CapEnforcementResult{ FreeBytes: 0, // No allocation on failure FairShareBytes: 0, // No allocation on failure FreeCount: 0, // No allocation on failure FairShareCount: 0, // No allocation on failure AvailableBytes: availableBytes, AvailableCount: availableCount, }, err } // Soft caps disabled - only check hard cap availableBytes := hardCapAvailableBytes availableCount := hardCapAvailableCount if additionalBytes <= hardCapAvailableBytes && additionalCount <= hardCapAvailableCount { // Success - all allocation goes to fair share when soft caps disabled return CapEnforcementResult{ FreeBytes: 0, // No free space allocation when soft caps disabled FairShareBytes: additionalBytes, FreeCount: 0, // No free space allocation when soft caps disabled FairShareCount: additionalCount, AvailableBytes: availableBytes, AvailableCount: availableCount, }, nil } // Hard cap exceeded var err error if additionalBytes > hardCapAvailableBytes { err = ErrBytesBudgetExceeded } else { err = ErrCountBudgetExceeded } return CapEnforcementResult{ FreeBytes: 0, // No allocation on failure FairShareBytes: 0, // No allocation on failure FreeCount: 0, // No allocation on failure FairShareCount: 0, // No allocation on failure AvailableBytes: availableBytes, AvailableCount: availableCount, }, err } ================================================ FILE: common/cache/budget_test.go ================================================ package cache import ( "context" "errors" "fmt" "math" "sync" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" metricsmocks "github.com/uber/cadence/common/metrics/mocks" ) func TestBudgetManager_ReserveForCache(t *testing.T) { tests := []struct { name string capacityBytes int capacityCount int softCapThreshold float64 requestBytes uint64 requestCount int64 cacheID string expectedError error expectedUsedBytes uint64 expectedUsedCount int64 description string }{ { name: "successful reserve with sufficient capacity", capacityBytes: 1000, capacityCount: 100, softCapThreshold: 1.0, // disabled requestBytes: 100, requestCount: 10, cacheID: "cache1", expectedError: nil, expectedUsedBytes: 100, expectedUsedCount: 10, description: "Should successfully reserve when capacity is available", }, { name: "hard cap exceeded - bytes", capacityBytes: 100, capacityCount: 100, softCapThreshold: 1.0, // disabled requestBytes: 200, requestCount: 10, cacheID: "cache1", expectedError: ErrBytesBudgetExceeded, expectedUsedBytes: 0, expectedUsedCount: 0, description: "Should fail when requesting more bytes than hard cap", }, { name: "hard cap exceeded - count", capacityBytes: 1000, capacityCount: 10, softCapThreshold: 1.0, // disabled requestBytes: 100, requestCount: 20, cacheID: "cache1", expectedError: ErrCountBudgetExceeded, expectedUsedBytes: 0, expectedUsedCount: 0, description: "Should fail when requesting more count than hard cap", }, { name: "zero capacity - bytes", capacityBytes: 0, capacityCount: 100, softCapThreshold: 1.0, requestBytes: 10, requestCount: 1, cacheID: "cache1", expectedError: ErrBytesBudgetExceeded, expectedUsedBytes: 0, expectedUsedCount: 0, description: "Should fail when bytes capacity is zero", }, { name: "zero capacity - count", capacityBytes: 1000, capacityCount: 0, softCapThreshold: 1.0, requestBytes: 10, requestCount: 1, cacheID: "cache1", expectedError: ErrCountBudgetExceeded, expectedUsedBytes: 0, expectedUsedCount: 0, description: "Should fail when count capacity is zero", }, { name: "soft cap - free space allocation", capacityBytes: 1000, capacityCount: 100, softCapThreshold: 0.5, // 500 bytes free, 500 bytes fair share requestBytes: 100, requestCount: 10, cacheID: "cache1", expectedError: nil, expectedUsedBytes: 100, expectedUsedCount: 10, description: "Should successfully allocate from free space when under threshold", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(tt.capacityBytes), dynamicproperties.GetIntPropertyFn(tt.capacityCount), AdmissionOptimistic, 0, nil, testlogger.New(t), dynamicproperties.GetFloatPropertyFn(tt.softCapThreshold), ) err := mgr.ReserveWithCallback(tt.cacheID, tt.requestBytes, tt.requestCount, func() error { return nil }) if tt.expectedError != nil { assert.Equal(t, tt.expectedError, err, tt.description) } else { assert.NoError(t, err, tt.description) } assert.Equal(t, tt.expectedUsedBytes, mgr.UsedBytes(), "Used bytes mismatch") assert.Equal(t, tt.expectedUsedCount, mgr.UsedCount(), "Used count mismatch") }) } } // Test soft cap with multiple caches - fair share enforcement func TestBudgetManager_SoftCapFairShare(t *testing.T) { tests := []struct { name string capacityBytes int capacityCount int softCapThreshold float64 setup func(mgr Manager) requestBytes uint64 requestCount int64 cacheID string expectedError error description string }{ { name: "request within fair share - single active cache", capacityBytes: 1000, capacityCount: 100, softCapThreshold: 0.5, // 500 free, 500 fair share setup: func(mgr Manager) { // Fill up the free space (threshold) mgr.ReserveWithCallback("cache1", 500, 50, func() error { return nil }) // Now cache1 tries to allocate more, should use fair share }, requestBytes: 300, // Within fair share for cache1 (500/1 = 500 available) requestCount: 30, cacheID: "cache1", expectedError: nil, // Should succeed because fair share allows it (cache1 is only active cache) description: "Single active cache should get full fair share capacity", }, { name: "request within fair share - multiple caches fair share", capacityBytes: 1000, capacityCount: 100, softCapThreshold: 0.5, setup: func(mgr Manager) { // Fill up the free space mgr.ReserveWithCallback("cache1", 500, 50, func() error { return nil }) // Activate cache2 with some fair share usage mgr.ReserveWithCallback("cache2", 100, 10, func() error { return nil }) // Now 2 active caches, fair share = 500/2 = 250 per cache }, requestBytes: 120, // Should be within fair share for cache2 (250 - 100 existing = 150 available) requestCount: 10, // Should be within fair share for cache2 (25 - 10 existing = 15 available) cacheID: "cache2", expectedError: nil, description: "Should succeed because cache is within per-cache fair share with multiple active caches", }, { name: "soft cap exceeded - multiple caches fair share", capacityBytes: 1000, capacityCount: 100, softCapThreshold: 0.5, setup: func(mgr Manager) { // Fill up the free space mgr.ReserveWithCallback("cache1", 500, 50, func() error { return nil }) // Activate cache2 with some fair share usage mgr.ReserveWithCallback("cache2", 100, 10, func() error { return nil }) // Now 2 active caches, fair share = 500/2 = 250 per cache }, requestBytes: 200, // cache2 already has 100, requesting 200 more = 300 total > 250 fair share requestCount: 20, cacheID: "cache2", expectedError: ErrBytesSoftCapExceeded, description: "Should fail when exceeding per-cache fair share with multiple active caches", }, { name: "soft cap exceeded - count fair share violation", capacityBytes: 1000, capacityCount: 100, softCapThreshold: 0.5, setup: func(mgr Manager) { // Fill up the free space mgr.ReserveWithCallback("cache1", 500, 50, func() error { return nil }) // Activate cache2 mgr.ReserveWithCallback("cache2", 100, 10, func() error { return nil }) // Now 2 active caches, fair share count = 50/2 = 25 per cache }, requestBytes: 50, // bytes within limit requestCount: 20, // cache2 already has 10, requesting 20 more = 30 total > 25 fair share cacheID: "cache2", expectedError: ErrCountSoftCapExceeded, description: "Should fail when exceeding per-cache count fair share", }, { name: "soft cap exceeded - bytes fair share violation", capacityBytes: 1000, capacityCount: 100, softCapThreshold: 0.5, setup: func(mgr Manager) { // Fill up the free space mgr.ReserveWithCallback("cache1", 500, 50, func() error { return nil }) // Activate cache2 mgr.ReserveWithCallback("cache2", 100, 10, func() error { return nil }) // Now 2 active caches, fair share count = 50/2 = 25 per cache }, requestBytes: 200, // cache2 already has 100, requesting 200 more = 300 total > 250 fair share requestCount: 1, // count within limit cacheID: "cache2", expectedError: ErrBytesSoftCapExceeded, description: "Should fail when exceeding per-cache bytes fair share", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(tt.capacityBytes), dynamicproperties.GetIntPropertyFn(tt.capacityCount), AdmissionOptimistic, 0, nil, testlogger.New(t), dynamicproperties.GetFloatPropertyFn(tt.softCapThreshold), ) if tt.setup != nil { tt.setup(mgr) } err := mgr.ReserveWithCallback(tt.cacheID, tt.requestBytes, tt.requestCount, func() error { return nil }) if tt.expectedError != nil { assert.Equal(t, tt.expectedError, err, tt.description) } else { assert.NoError(t, err, tt.description) } }) } } // Test releasing capacity from fair share tracking func TestBudgetManager_ReleaseFairShareTracking(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(1000), // 1000 bytes capacity dynamicproperties.GetIntPropertyFn(100), // 100 count capacity AdmissionOptimistic, 0, nil, testlogger.New(t), dynamicproperties.GetFloatPropertyFn(0.5), // 500 free, 500 fair share ).(*manager) // Fill up the free space (500 bytes) err := mgr.ReserveWithCallback("cache1", 500, 50, func() error { return nil }) assert.NoError(t, err, "Should reserve free space successfully") // Now allocate from fair share (cache1 uses fair share portion) err = mgr.ReserveWithCallback("cache1", 200, 20, func() error { return nil }) assert.NoError(t, err, "Should reserve from fair share successfully") // Total usage: 700 bytes (500 free + 200 fair share) assert.Equal(t, uint64(700), mgr.UsedBytes(), "Total used should be 700") // Check fair share tracking for cache1 cacheUsage := mgr.getCacheUsage("cache1") fairShareBytes := cacheUsage.fairShareCapacityBytes assert.Equal(t, uint64(200), fairShareBytes, "Fair share bytes should be 200") // Release some capacity (100 bytes) - should deduct from fair share first mgr.ReleaseForCache("cache1", 100, 10) // Total usage should be 600 (not going to zero) assert.Equal(t, uint64(600), mgr.UsedBytes(), "Total used should be 600 after release") assert.Equal(t, int64(60), mgr.UsedCount(), "Total count should be 60 after release") // Fair share tracking should be reduced to 100 fairShareBytes = cacheUsage.fairShareCapacityBytes assert.Equal(t, uint64(100), fairShareBytes, "Fair share bytes should be reduced to 100") // Release more (150 bytes) - should deduct the remaining 100 from fair share, then 50 from free mgr.ReleaseForCache("cache1", 150, 15) // Total usage should be 450 assert.Equal(t, uint64(450), mgr.UsedBytes(), "Total used should be 450 after second release") assert.Equal(t, int64(45), mgr.UsedCount(), "Total count should be 45 after second release") // Fair share tracking should be 0 fairShareBytes = cacheUsage.fairShareCapacityBytes assert.Equal(t, uint64(0), fairShareBytes, "Fair share bytes should be 0 after releasing all fair share") } // Test individual Reserve/Release methods for bytes and count func TestBudgetManager_IndividualReserveRelease(t *testing.T) { tests := []struct { name string capacityBytes int capacityCount int operations func(mgr Manager) error expectedError error description string }{ { name: "ReserveBytesForCache success", capacityBytes: 1000, capacityCount: 100, operations: func(mgr Manager) error { return mgr.ReserveBytesWithCallback("cache1", 100, func() error { return nil }) }, expectedError: nil, description: "Should successfully reserve bytes only", }, { name: "ReserveBytesForCache exceeds capacity", capacityBytes: 100, capacityCount: 100, operations: func(mgr Manager) error { return mgr.ReserveBytesWithCallback("cache1", 200, func() error { return nil }) }, expectedError: ErrBytesBudgetExceeded, description: "Should fail when bytes exceed capacity", }, { name: "ReserveCountForCache success", capacityBytes: 1000, capacityCount: 100, operations: func(mgr Manager) error { return mgr.ReserveCountWithCallback("cache1", 10, func() error { return nil }) }, expectedError: nil, description: "Should successfully reserve count only", }, { name: "ReserveCountForCache exceeds capacity", capacityBytes: 1000, capacityCount: 10, operations: func(mgr Manager) error { return mgr.ReserveCountWithCallback("cache1", 20, func() error { return nil }) }, expectedError: ErrCountBudgetExceeded, description: "Should fail when count exceeds capacity", }, { name: "ReleaseBytesForCache", capacityBytes: 1000, capacityCount: 100, operations: func(mgr Manager) error { mgr.ReserveBytesWithCallback("cache1", 100, func() error { return nil }) mgr.ReleaseBytesWithCallback("cache1", func() (uint64, error) { return 50, nil }) if mgr.UsedBytes() != 50 { return assert.AnError } return nil }, expectedError: nil, description: "Should release bytes correctly", }, { name: "ReleaseCountForCache", capacityBytes: 1000, capacityCount: 100, operations: func(mgr Manager) error { mgr.ReserveCountWithCallback("cache1", 10, func() error { return nil }) mgr.ReleaseCountWithCallback("cache1", func() (int64, error) { return 5, nil }) if mgr.UsedCount() != 5 { return assert.AnError } return nil }, expectedError: nil, description: "Should release count correctly", }, { name: "ReserveCountForCache with negative value", capacityBytes: 1000, capacityCount: 100, operations: func(mgr Manager) error { return mgr.ReserveCountWithCallback("cache1", -10, func() error { return nil }) }, expectedError: ErrInvalidValue, description: "Should reject negative count values", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(tt.capacityBytes), dynamicproperties.GetIntPropertyFn(tt.capacityCount), AdmissionOptimistic, 0, nil, testlogger.New(t), nil, ) err := tt.operations(mgr) if tt.expectedError != nil { assert.Equal(t, tt.expectedError, err, tt.description) } else { assert.NoError(t, err, tt.description) } }) } } // Test Strict admission mode func TestBudgetManager_StrictAdmission(t *testing.T) { tests := []struct { name string capacityBytes int capacityCount int operations func(mgr Manager) error expectedError error description string }{ { name: "strict mode - reserve within capacity", capacityBytes: 1000, capacityCount: 100, operations: func(mgr Manager) error { return mgr.ReserveWithCallback("cache1", 100, 10, func() error { return nil }) }, expectedError: nil, description: "Should successfully reserve in strict mode when capacity available", }, { name: "strict mode - bytes exceed capacity", capacityBytes: 100, capacityCount: 100, operations: func(mgr Manager) error { return mgr.ReserveWithCallback("cache1", 200, 10, func() error { return nil }) }, expectedError: ErrBytesBudgetExceeded, description: "Should fail in strict mode when bytes exceed capacity", }, { name: "strict mode - count exceed capacity", capacityBytes: 1000, capacityCount: 10, operations: func(mgr Manager) error { return mgr.ReserveWithCallback("cache1", 100, 20, func() error { return nil }) }, expectedError: ErrCountBudgetExceeded, description: "Should fail in strict mode when count exceeds capacity", }, { name: "strict mode - prevents temporary overshoot", capacityBytes: 100, capacityCount: 100, operations: func(mgr Manager) error { // First reserve should succeed if err := mgr.ReserveWithCallback("cache1", 60, 60, func() error { return nil }); err != nil { return err } // Second reserve should fail (would exceed if allowed) return mgr.ReserveWithCallback("cache2", 50, 50, func() error { return nil }) }, expectedError: ErrBytesBudgetExceeded, description: "Strict mode should prevent any overshoot attempts", }, { name: "strict mode - negative count value", capacityBytes: 1000, capacityCount: 100, operations: func(mgr Manager) error { return mgr.ReserveCountWithCallback("cache1", -10, func() error { return nil }) }, expectedError: ErrInvalidValue, description: "Strict mode should reject negative count values", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(tt.capacityBytes), dynamicproperties.GetIntPropertyFn(tt.capacityCount), AdmissionStrict, // Use strict mode 0, nil, testlogger.New(t), nil, ) err := tt.operations(mgr) if tt.expectedError != nil { assert.Equal(t, tt.expectedError, err, tt.description) } else { assert.NoError(t, err, tt.description) } }) } } // Test rollback behavior when partial reservations fail func TestBudgetManager_Rollback(t *testing.T) { tests := []struct { name string capacityBytes int capacityCount int admissionMode AdmissionMode operations func(mgr Manager) (error, uint64, int64) expectedError error expectedBytes uint64 expectedCount int64 description string }{ { name: "rollback bytes when count fails", capacityBytes: 1000, capacityCount: 10, admissionMode: AdmissionOptimistic, operations: func(mgr Manager) (error, uint64, int64) { err := mgr.ReserveWithCallback("cache1", 100, 20, func() error { return nil }) return err, mgr.UsedBytes(), mgr.UsedCount() }, expectedError: ErrCountBudgetExceeded, expectedBytes: 0, expectedCount: 0, description: "Should rollback bytes when count reservation fails", }, { name: "no rollback needed when bytes fail first", capacityBytes: 100, capacityCount: 1000, admissionMode: AdmissionOptimistic, operations: func(mgr Manager) (error, uint64, int64) { err := mgr.ReserveWithCallback("cache1", 200, 10, func() error { return nil }) return err, mgr.UsedBytes(), mgr.UsedCount() }, expectedError: ErrBytesBudgetExceeded, expectedBytes: 0, expectedCount: 0, description: "No rollback needed when bytes fail before count is reserved", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(tt.capacityBytes), dynamicproperties.GetIntPropertyFn(tt.capacityCount), tt.admissionMode, 0, nil, testlogger.New(t), nil, ) err, usedBytes, usedCount := tt.operations(mgr) assert.Equal(t, tt.expectedError, err, tt.description) assert.Equal(t, tt.expectedBytes, usedBytes, "Used bytes should match expected after rollback") assert.Equal(t, tt.expectedCount, usedCount, "Used count should match expected") }) } } func TestBudgetManager_BytesOverflow(t *testing.T) { tests := []struct { name string capacityBytes uint64 capacityCount int64 admission AdmissionMode setupUsage uint64 reserveAmount uint64 expectedError error description string }{ { name: "strict mode - overflow detection", capacityBytes: math.MaxUint64, capacityCount: 100, admission: AdmissionStrict, setupUsage: math.MaxUint64 - 100, reserveAmount: 150, expectedError: ErrOverflow, description: "Strict mode should detect overflow when old + n > MaxUint64", }, { name: "optimistic mode - overflow detection via wraparound", capacityBytes: math.MaxUint64, capacityCount: 100, admission: AdmissionOptimistic, setupUsage: math.MaxUint64 - 50, reserveAmount: 75, expectedError: ErrOverflow, description: "Optimistic mode should detect wraparound when newVal < n", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(int(tt.capacityBytes)), dynamicproperties.GetIntPropertyFn(int(tt.capacityCount)), tt.admission, 0, nil, testlogger.New(t), nil, ) m := mgr.(*manager) atomic.StoreUint64(&m.usedBytes, tt.setupUsage) err := m.reserveBytes(tt.reserveAmount) assert.Equal(t, tt.expectedError, err, tt.description) }) } } func TestBudgetManager_ReserveOrReclaimSelfRelease(t *testing.T) { tests := []struct { name string capacityBytes int capacityCount int retriable bool requestBytes uint64 requestCount int64 setupUsage func(mgr Manager) reclaimFunc ReclaimSelfRelease expectedError error expectedBytes uint64 expectedCount int64 description string }{ { name: "immediate success - no reclaim needed", capacityBytes: 1000, capacityCount: 100, retriable: true, requestBytes: 100, requestCount: 10, setupUsage: nil, reclaimFunc: nil, expectedError: nil, expectedBytes: 100, expectedCount: 10, description: "Should succeed immediately when capacity is available", }, { name: "non-retriable failure", capacityBytes: 1000, capacityCount: 100, retriable: false, requestBytes: 500, requestCount: 50, setupUsage: func(mgr Manager) { mgr.ReserveWithCallback("other_cache", 600, 60, func() error { return nil }) }, reclaimFunc: nil, expectedError: ErrBytesBudgetExceeded, expectedBytes: 600, expectedCount: 60, description: "Should fail immediately when retriable is false", }, { name: "reclaim with self-release", capacityBytes: 1000, capacityCount: 100, retriable: true, requestBytes: 500, requestCount: 50, setupUsage: func(mgr Manager) { mgr.ReserveWithCallback("cache1", 600, 60, func() error { return nil }) }, reclaimFunc: func(needBytes uint64, needCount int64) { }, expectedError: nil, expectedBytes: 1000, expectedCount: 100, description: "Should succeed after reclaim callback (cache releases its own items)", }, { name: "context cancellation", capacityBytes: 1000, capacityCount: 100, retriable: true, requestBytes: 500, requestCount: 50, setupUsage: func(mgr Manager) { mgr.ReserveWithCallback("cache1", 1000, 100, func() error { return nil }) }, reclaimFunc: func(needBytes uint64, needCount int64) { }, expectedError: context.Canceled, expectedBytes: 1000, expectedCount: 100, description: "Should return context error when context is cancelled", }, { name: "insufficient cache budget for reclaim", capacityBytes: 1000, capacityCount: 100, retriable: true, requestBytes: 900, requestCount: 90, setupUsage: func(mgr Manager) { mgr.ReserveWithCallback("cache1", 100, 10, func() error { return nil }) mgr.ReserveWithCallback("other_cache", 200, 20, func() error { return nil }) }, reclaimFunc: func(needBytes uint64, needCount int64) { }, expectedError: ErrInsufficientUsageToReclaim, expectedBytes: 300, expectedCount: 30, description: "Should fail when cache doesn't have enough to reclaim", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(tt.capacityBytes), dynamicproperties.GetIntPropertyFn(tt.capacityCount), AdmissionOptimistic, 0, nil, testlogger.New(t), nil, ) if tt.setupUsage != nil { tt.setupUsage(mgr) } var reclaimCalled bool var reclaimFunc ReclaimSelfRelease if tt.reclaimFunc != nil { reclaimFunc = func(needBytes uint64, needCount int64) { reclaimCalled = true mgr.ReleaseWithCallback("cache1", func() (uint64, int64, error) { return needBytes, needCount, nil }) } } ctx := context.Background() if tt.expectedError == context.Canceled { var cancel context.CancelFunc ctx, cancel = context.WithCancel(ctx) cancel() } err := mgr.(*manager).ReserveOrReclaimSelfReleaseWithCallback(ctx, "cache1", tt.requestBytes, tt.requestCount, tt.retriable, reclaimFunc, func() error { return nil }) assert.Equal(t, tt.expectedError, err, tt.description) if tt.reclaimFunc != nil && tt.expectedError == nil { assert.True(t, reclaimCalled, "Reclaim function should be called") } assert.Equal(t, tt.expectedBytes, mgr.UsedBytes(), "Used bytes mismatch") assert.Equal(t, tt.expectedCount, mgr.UsedCount(), "Used count mismatch") }) } } func TestBudgetManager_ReserveOrReclaimManagerRelease(t *testing.T) { tests := []struct { name string capacityBytes int capacityCount int retriable bool requestBytes uint64 requestCount int64 setupUsage func(mgr Manager) reclaimFunc ReclaimManagerRelease expectedError error expectedBytes uint64 expectedCount int64 description string }{ { name: "immediate success - no reclaim needed", capacityBytes: 1000, capacityCount: 100, retriable: true, requestBytes: 100, requestCount: 10, setupUsage: nil, reclaimFunc: nil, expectedError: nil, expectedBytes: 100, expectedCount: 10, description: "Should succeed immediately when capacity is available", }, { name: "non-retriable failure", capacityBytes: 1000, capacityCount: 100, retriable: false, requestBytes: 500, requestCount: 50, setupUsage: func(mgr Manager) { mgr.ReserveWithCallback("other_cache", 600, 60, func() error { return nil }) }, reclaimFunc: nil, expectedError: ErrBytesBudgetExceeded, expectedBytes: 600, expectedCount: 60, description: "Should fail immediately when retriable is false", }, { name: "reclaim with manager release", capacityBytes: 1000, capacityCount: 100, retriable: true, requestBytes: 500, requestCount: 50, setupUsage: func(mgr Manager) { mgr.ReserveWithCallback("cache1", 600, 60, func() error { return nil }) }, reclaimFunc: func(needBytes uint64, needCount int64) (uint64, int64) { return needBytes, needCount }, expectedError: nil, expectedBytes: 1000, expectedCount: 100, description: "Should succeed after reclaim callback (manager releases items)", }, { name: "context cancellation", capacityBytes: 1000, capacityCount: 100, retriable: true, requestBytes: 500, requestCount: 50, setupUsage: func(mgr Manager) { mgr.ReserveWithCallback("cache1", 1000, 100, func() error { return nil }) }, reclaimFunc: func(needBytes uint64, needCount int64) (uint64, int64) { return needBytes, needCount }, expectedError: context.Canceled, expectedBytes: 1000, expectedCount: 100, description: "Should return context error when context is cancelled", }, { name: "insufficient cache budget for reclaim", capacityBytes: 1000, capacityCount: 100, retriable: true, requestBytes: 900, requestCount: 90, setupUsage: func(mgr Manager) { mgr.ReserveWithCallback("cache1", 100, 10, func() error { return nil }) mgr.ReserveWithCallback("other_cache", 200, 20, func() error { return nil }) }, reclaimFunc: func(needBytes uint64, needCount int64) (uint64, int64) { return needBytes, needCount }, expectedError: ErrInsufficientUsageToReclaim, expectedBytes: 300, expectedCount: 30, description: "Should fail when cache doesn't have enough to reclaim", }, { name: "reclaim returns zero", capacityBytes: 1000, capacityCount: 100, retriable: true, requestBytes: 500, requestCount: 50, setupUsage: func(mgr Manager) { mgr.ReserveWithCallback("cache1", 600, 60, func() error { return nil }) }, reclaimFunc: func(needBytes uint64, needCount int64) (uint64, int64) { return 0, 0 }, expectedError: ErrBytesBudgetExceeded, expectedBytes: 600, expectedCount: 60, description: "Should keep retrying when reclaim returns zero", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(tt.capacityBytes), dynamicproperties.GetIntPropertyFn(tt.capacityCount), AdmissionOptimistic, 0, nil, testlogger.New(t), nil, ) if tt.setupUsage != nil { tt.setupUsage(mgr) } var reclaimCalled bool var reclaimFunc ReclaimManagerRelease if tt.reclaimFunc != nil { reclaimFunc = func(needBytes uint64, needCount int64) (uint64, int64) { reclaimCalled = true return tt.reclaimFunc(needBytes, needCount) } } ctx := context.Background() if tt.expectedError == context.Canceled { var cancel context.CancelFunc ctx, cancel = context.WithCancel(ctx) cancel() } else if tt.name == "reclaim returns zero" { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, 10*time.Millisecond) defer cancel() } err := mgr.(*manager).ReserveOrReclaimManagerReleaseWithCallback(ctx, "cache1", tt.requestBytes, tt.requestCount, tt.retriable, reclaimFunc, func() error { return nil }) if tt.name == "reclaim returns zero" { assert.Error(t, err, tt.description) } else { assert.Equal(t, tt.expectedError, err, tt.description) } if tt.reclaimFunc != nil && tt.expectedError == nil { assert.True(t, reclaimCalled, "Reclaim function should be called") } assert.Equal(t, tt.expectedBytes, mgr.UsedBytes(), "Used bytes mismatch") assert.Equal(t, tt.expectedCount, mgr.UsedCount(), "Used count mismatch") }) } } func TestBudgetManager_CapacityCount(t *testing.T) { tests := []struct { name string maxCount int expectedCount int64 description string }{ { name: "positive capacity", maxCount: 100, expectedCount: 100, description: "Should return the configured count capacity", }, { name: "zero capacity", maxCount: 0, expectedCount: 0, description: "Should return 0 when capacity is 0", }, { name: "negative capacity (unlimited)", maxCount: -1, expectedCount: math.MaxInt64, description: "Should return MaxInt64 when capacity is negative (unlimited)", }, { name: "nil maxCount defaults to unlimited", maxCount: -2, expectedCount: math.MaxInt64, description: "Should return MaxInt64 when maxCount is nil", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var maxCountFn dynamicproperties.IntPropertyFn if tt.maxCount == -2 { maxCountFn = nil } else { maxCountFn = dynamicproperties.GetIntPropertyFn(tt.maxCount) } mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(1000), maxCountFn, AdmissionOptimistic, 0, nil, testlogger.New(t), nil, ) assert.Equal(t, tt.expectedCount, mgr.CapacityCount(), tt.description) }) } } func TestBudgetManager_AvailableBytes(t *testing.T) { tests := []struct { name string capacityBytes int usedBytes uint64 expectedAvail uint64 description string }{ { name: "full capacity available", capacityBytes: 1000, usedBytes: 0, expectedAvail: 1000, description: "Should return full capacity when nothing is used", }, { name: "partial capacity available", capacityBytes: 1000, usedBytes: 300, expectedAvail: 700, description: "Should return remaining capacity", }, { name: "no capacity available - fully used", capacityBytes: 1000, usedBytes: 1000, expectedAvail: 0, description: "Should return 0 when fully used", }, { name: "no capacity available - over capacity", capacityBytes: 1000, usedBytes: 1200, expectedAvail: 0, description: "Should return 0 when used exceeds capacity", }, { name: "zero capacity", capacityBytes: 0, usedBytes: 0, expectedAvail: 0, description: "Should return 0 when capacity is 0", }, { name: "nil maxBytes defaults to unlimited", capacityBytes: -2, usedBytes: 1000, expectedAvail: math.MaxUint64 - 1000, description: "Should return MaxUint64 - used when maxBytes is nil", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var maxBytesFn dynamicproperties.IntPropertyFn if tt.capacityBytes == -2 { maxBytesFn = nil } else { maxBytesFn = dynamicproperties.GetIntPropertyFn(tt.capacityBytes) } mgr := NewBudgetManager( "test", maxBytesFn, dynamicproperties.GetIntPropertyFn(100), AdmissionOptimistic, 0, nil, testlogger.New(t), nil, ) if tt.usedBytes > 0 { atomic.StoreUint64(&mgr.(*manager).usedBytes, tt.usedBytes) } assert.Equal(t, tt.expectedAvail, mgr.(*manager).AvailableBytes(), tt.description) }) } } func TestBudgetManager_AvailableCount(t *testing.T) { tests := []struct { name string capacityCount int usedCount int64 expectedAvail int64 description string }{ { name: "full capacity available", capacityCount: 100, usedCount: 0, expectedAvail: 100, description: "Should return full capacity when nothing is used", }, { name: "partial capacity available", capacityCount: 100, usedCount: 30, expectedAvail: 70, description: "Should return remaining capacity", }, { name: "no capacity available - fully used", capacityCount: 100, usedCount: 100, expectedAvail: 0, description: "Should return 0 when fully used", }, { name: "no capacity available - over capacity", capacityCount: 100, usedCount: 120, expectedAvail: 0, description: "Should return 0 when used exceeds capacity", }, { name: "zero capacity", capacityCount: 0, usedCount: 0, expectedAvail: 0, description: "Should return 0 when capacity is 0", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(1000), dynamicproperties.GetIntPropertyFn(tt.capacityCount), AdmissionOptimistic, 0, nil, testlogger.New(t), nil, ) if tt.usedCount > 0 { atomic.StoreInt64(&mgr.(*manager).usedCount, tt.usedCount) } assert.Equal(t, tt.expectedAvail, mgr.(*manager).AvailableCount(), tt.description) }) } } func TestBudgetManager_InternalReserveMethods(t *testing.T) { tests := []struct { name string capacityBytes int capacityCount int admissionMode AdmissionMode operation func(*manager) error expectedError error description string }{ { name: "reserveBytesStrict - capacity exceeded", capacityBytes: 100, capacityCount: 100, admissionMode: AdmissionStrict, operation: func(m *manager) error { return m.reserveBytesStrict(200) }, expectedError: ErrBytesBudgetExceeded, description: "Should fail when bytes exceed capacity in strict mode", }, { name: "reserveBytesStrict - success", capacityBytes: 100, capacityCount: 100, admissionMode: AdmissionStrict, operation: func(m *manager) error { return m.reserveBytesStrict(50) }, expectedError: nil, description: "Should succeed when bytes are within capacity in strict mode", }, { name: "reserveBytesStrict - overflow detection", capacityBytes: 1000, capacityCount: 100, admissionMode: AdmissionStrict, operation: func(m *manager) error { m.usedBytes = math.MaxUint64 - 100 return m.reserveBytesStrict(150) }, expectedError: ErrOverflow, description: "Should detect overflow in strict mode", }, { name: "reserveCountStrict - capacity exceeded", capacityBytes: 100, capacityCount: 10, admissionMode: AdmissionStrict, operation: func(m *manager) error { return m.reserveCountStrict(20) }, expectedError: ErrCountBudgetExceeded, description: "Should fail when count exceeds capacity in strict mode", }, { name: "reserveCountStrict - success", capacityBytes: 100, capacityCount: 10, admissionMode: AdmissionStrict, operation: func(m *manager) error { return m.reserveCountStrict(5) }, expectedError: nil, description: "Should succeed when count is within capacity in strict mode", }, { name: "reserveCountStrict - overflow detection", capacityBytes: 1000, capacityCount: 1000, admissionMode: AdmissionStrict, operation: func(m *manager) error { m.usedCount = math.MaxInt64 - 10 return m.reserveCountStrict(20) }, expectedError: ErrOverflow, description: "Should detect overflow in strict mode", }, { name: "reserveBytesOptimistic - capacity exceeded", capacityBytes: 100, capacityCount: 100, admissionMode: AdmissionOptimistic, operation: func(m *manager) error { return m.reserveBytesOptimistic(200) }, expectedError: ErrBytesBudgetExceeded, description: "Should fail when bytes exceed capacity in optimistic mode", }, { name: "reserveBytesOptimistic - success", capacityBytes: 100, capacityCount: 100, admissionMode: AdmissionOptimistic, operation: func(m *manager) error { return m.reserveBytesOptimistic(50) }, expectedError: nil, description: "Should succeed when bytes are within capacity in optimistic mode", }, { name: "reserveCountOptimistic - capacity exceeded", capacityBytes: 100, capacityCount: 10, admissionMode: AdmissionOptimistic, operation: func(m *manager) error { return m.reserveCountOptimistic(20) }, expectedError: ErrCountBudgetExceeded, description: "Should fail when count exceeds capacity in optimistic mode", }, { name: "reserveCountOptimistic - success", capacityBytes: 100, capacityCount: 10, admissionMode: AdmissionOptimistic, operation: func(m *manager) error { return m.reserveCountOptimistic(5) }, expectedError: nil, description: "Should succeed when count is within capacity in optimistic mode", }, { name: "reserveCountOptimistic - overflow wraparound", capacityBytes: 1000, capacityCount: 1000, admissionMode: AdmissionOptimistic, operation: func(m *manager) error { m.usedCount = math.MaxInt64 - 10 return m.reserveCountOptimistic(20) }, expectedError: ErrOverflow, description: "Should detect wraparound overflow in optimistic mode", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(tt.capacityBytes), dynamicproperties.GetIntPropertyFn(tt.capacityCount), tt.admissionMode, 0, nil, testlogger.New(t), nil, ) m := mgr.(*manager) err := tt.operation(m) if tt.expectedError != nil { assert.Equal(t, tt.expectedError, err, tt.description) } else { assert.NoError(t, err, tt.description) } }) } } func TestBudgetManager_CallbackMethods(t *testing.T) { tests := []struct { name string capacityBytes int capacityCount int operation func(mgr Manager) error expectedError error expectedBytes uint64 expectedCount int64 description string }{ { name: "ReserveWithCallback - success", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { called := false err := mgr.ReserveWithCallback("cache1", 100, 10, func() error { called = true return nil }) if !called { return errors.New("callback not called") } return err }, expectedError: nil, expectedBytes: 100, expectedCount: 10, description: "Should reserve and call callback on success", }, { name: "ReserveWithCallback - callback error releases", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { return mgr.ReserveWithCallback("cache1", 100, 10, func() error { return errors.New("callback failed") }) }, expectedError: errors.New("callback failed"), expectedBytes: 0, expectedCount: 0, description: "Should release on callback error", }, { name: "ReserveWithCallback - reserve failure", capacityBytes: 100, capacityCount: 100, operation: func(mgr Manager) error { return mgr.ReserveWithCallback("cache1", 200, 10, func() error { return errors.New("should not be called") }) }, expectedError: ErrBytesBudgetExceeded, expectedBytes: 0, expectedCount: 0, description: "Should not call callback when reserve fails", }, { name: "ReserveBytesWithCallback - success", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { called := false err := mgr.ReserveBytesWithCallback("cache1", 100, func() error { called = true return nil }) if !called { return errors.New("callback not called") } return err }, expectedError: nil, expectedBytes: 100, expectedCount: 0, description: "Should reserve bytes and call callback on success", }, { name: "ReserveBytesWithCallback - callback error releases", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { return mgr.ReserveBytesWithCallback("cache1", 100, func() error { return errors.New("callback failed") }) }, expectedError: errors.New("callback failed"), expectedBytes: 0, expectedCount: 0, description: "Should release bytes on callback error", }, { name: "ReserveCountWithCallback - success", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { called := false err := mgr.ReserveCountWithCallback("cache1", 10, func() error { called = true return nil }) if !called { return errors.New("callback not called") } return err }, expectedError: nil, expectedBytes: 0, expectedCount: 10, description: "Should reserve count and call callback on success", }, { name: "ReserveCountWithCallback - callback error releases", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { return mgr.ReserveCountWithCallback("cache1", 10, func() error { return errors.New("callback failed") }) }, expectedError: errors.New("callback failed"), expectedBytes: 0, expectedCount: 0, description: "Should release count on callback error", }, { name: "ReleaseWithCallback - success", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { mgr.ReserveWithCallback("cache1", 100, 10, func() error { return nil }) called := false err := mgr.ReleaseWithCallback("cache1", func() (uint64, int64, error) { called = true return 100, 10, nil }) if !called { return errors.New("callback not called") } return err }, expectedError: nil, expectedBytes: 0, expectedCount: 0, description: "Should call callback and release on success", }, { name: "ReleaseWithCallback - callback error does not release", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { mgr.ReserveWithCallback("cache1", 100, 10, func() error { return nil }) return mgr.ReleaseWithCallback("cache1", func() (uint64, int64, error) { return 0, 0, errors.New("callback failed") }) }, expectedError: errors.New("callback failed"), expectedBytes: 100, expectedCount: 10, description: "Should not release when callback fails", }, { name: "ReleaseBytesWithCallback - success", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { mgr.ReserveBytesWithCallback("cache1", 100, func() error { return nil }) called := false err := mgr.ReleaseBytesWithCallback("cache1", func() (uint64, error) { called = true return 100, nil }) if !called { return errors.New("callback not called") } return err }, expectedError: nil, expectedBytes: 0, expectedCount: 0, description: "Should call callback and release bytes on success", }, { name: "ReleaseBytesWithCallback - callback error does not release", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { mgr.ReserveBytesWithCallback("cache1", 100, func() error { return nil }) return mgr.ReleaseBytesWithCallback("cache1", func() (uint64, error) { return 0, errors.New("callback failed") }) }, expectedError: errors.New("callback failed"), expectedBytes: 100, expectedCount: 0, description: "Should not release bytes when callback fails", }, { name: "ReleaseCountWithCallback - success", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { mgr.ReserveCountWithCallback("cache1", 10, func() error { return nil }) called := false err := mgr.ReleaseCountWithCallback("cache1", func() (int64, error) { called = true return 10, nil }) if !called { return errors.New("callback not called") } return err }, expectedError: nil, expectedBytes: 0, expectedCount: 0, description: "Should call callback and release count on success", }, { name: "ReleaseCountWithCallback - callback error does not release", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { mgr.ReserveCountWithCallback("cache1", 10, func() error { return nil }) return mgr.ReleaseCountWithCallback("cache1", func() (int64, error) { return 0, errors.New("callback failed") }) }, expectedError: errors.New("callback failed"), expectedBytes: 0, expectedCount: 10, description: "Should not release count when callback fails", }, { name: "ReserveOrReclaimSelfReleaseWithCallback - success", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { mgr.ReserveWithCallback("cache1", 600, 60, func() error { return nil }) called := false callbackCalled := false err := mgr.(*manager).ReserveOrReclaimSelfReleaseWithCallback( context.Background(), "cache1", 500, 50, true, func(needBytes uint64, needCount int64) { called = true mgr.ReleaseWithCallback("cache1", func() (uint64, int64, error) { return needBytes, needCount, nil }) }, func() error { callbackCalled = true return nil }, ) if !called { return errors.New("reclaim not called") } if !callbackCalled { return errors.New("callback not called") } return err }, expectedError: nil, expectedBytes: 1000, expectedCount: 100, description: "Should reclaim, reserve and call callback on success", }, { name: "ReserveOrReclaimSelfReleaseWithCallback - callback error releases", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { mgr.ReserveWithCallback("cache1", 600, 60, func() error { return nil }) return mgr.(*manager).ReserveOrReclaimSelfReleaseWithCallback( context.Background(), "cache1", 500, 50, true, func(needBytes uint64, needCount int64) { mgr.ReleaseWithCallback("cache1", func() (uint64, int64, error) { return needBytes, needCount, nil }) }, func() error { return errors.New("callback failed") }, ) }, expectedError: errors.New("callback failed"), expectedBytes: 500, expectedCount: 50, description: "Should release on callback error", }, { name: "ReserveOrReclaimManagerReleaseWithCallback - success", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { mgr.ReserveWithCallback("cache1", 600, 60, func() error { return nil }) called := false callbackCalled := false err := mgr.(*manager).ReserveOrReclaimManagerReleaseWithCallback( context.Background(), "cache1", 500, 50, true, func(needBytes uint64, needCount int64) (uint64, int64) { called = true return needBytes, needCount }, func() error { callbackCalled = true return nil }, ) if !called { return errors.New("reclaim not called") } if !callbackCalled { return errors.New("callback not called") } return err }, expectedError: nil, expectedBytes: 1000, expectedCount: 100, description: "Should reclaim, reserve and call callback on success", }, { name: "ReserveOrReclaimManagerReleaseWithCallback - callback error releases", capacityBytes: 1000, capacityCount: 100, operation: func(mgr Manager) error { mgr.ReserveWithCallback("cache1", 600, 60, func() error { return nil }) return mgr.(*manager).ReserveOrReclaimManagerReleaseWithCallback( context.Background(), "cache1", 500, 50, true, func(needBytes uint64, needCount int64) (uint64, int64) { return needBytes, needCount }, func() error { return errors.New("callback failed") }, ) }, expectedError: errors.New("callback failed"), expectedBytes: 500, expectedCount: 50, description: "Should release on callback error", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(tt.capacityBytes), dynamicproperties.GetIntPropertyFn(tt.capacityCount), AdmissionOptimistic, 0, nil, testlogger.New(t), nil, ) err := tt.operation(mgr) if tt.expectedError != nil { assert.Error(t, err, tt.description) assert.Equal(t, tt.expectedError.Error(), err.Error(), tt.description) } else { assert.NoError(t, err, tt.description) } assert.Equal(t, tt.expectedBytes, mgr.UsedBytes(), "Used bytes mismatch") assert.Equal(t, tt.expectedCount, mgr.UsedCount(), "Used count mismatch") }) } } func TestBudgetManager_ConcurrentCorrectness(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(1000000), dynamicproperties.GetIntPropertyFn(100000), AdmissionOptimistic, 0, nil, testlogger.New(t), nil, ) const ( numGoroutines = 100 numOperations = 1000 reserveBytes = 100 reserveCount = 1 ) // Track total operations for verification var totalReservedBytes atomic.Uint64 var totalReservedCount atomic.Int64 var totalReleasedBytes atomic.Uint64 var totalReleasedCount atomic.Int64 // Run concurrent reserve/release operations // Each goroutine will reserve some and release some (but not all) var wg sync.WaitGroup wg.Add(numGoroutines) for g := 0; g < numGoroutines; g++ { go func(goroutineID int) { defer wg.Done() cacheID := fmt.Sprintf("cache%d", goroutineID%10) for i := 0; i < numOperations; i++ { // Reserve err := mgr.(*manager).ReserveForCache(cacheID, reserveBytes, reserveCount) if err == nil { totalReservedBytes.Add(reserveBytes) totalReservedCount.Add(reserveCount) // Release only half the time to maintain non-zero state if i%2 == 0 { mgr.(*manager).ReleaseForCache(cacheID, reserveBytes, reserveCount) totalReleasedBytes.Add(reserveBytes) totalReleasedCount.Add(reserveCount) } } } }(g) } wg.Wait() // Verify invariants without assuming zero state // 1. Manager's used bytes should equal total reserved - total released expectedBytes := totalReservedBytes.Load() - totalReleasedBytes.Load() expectedCount := totalReservedCount.Load() - totalReleasedCount.Load() assert.Equal(t, expectedBytes, mgr.UsedBytes(), "Used bytes should equal total reserved minus total released") assert.Equal(t, expectedCount, mgr.UsedCount(), "Used count should equal total reserved minus total released") // 2. Available capacity should equal total - used assert.Equal(t, uint64(1000000)-expectedBytes, mgr.(*manager).AvailableBytes(), "Available bytes should equal capacity minus used") assert.Equal(t, int64(100000)-expectedCount, mgr.(*manager).AvailableCount(), "Available count should equal capacity minus used") // 3. Active cache count should be > 0 since we have unreleased capacity if expectedBytes > 0 || expectedCount > 0 { assert.Greater(t, mgr.(*manager).getActiveCacheCount(), int64(0), "Should have active caches when capacity is in use") } // 4. Log final state for debugging t.Logf("Final state: reserved=%d bytes, released=%d bytes, used=%d bytes, active_caches=%d", totalReservedBytes.Load(), totalReleasedBytes.Load(), mgr.UsedBytes(), mgr.(*manager).getActiveCacheCount()) } func TestBudgetManager_ConcurrentSoftCapCorrectness(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(10000), // Smaller capacity dynamicproperties.GetIntPropertyFn(1000), AdmissionOptimistic, 0, nil, testlogger.New(t), dynamicproperties.GetFloatPropertyFn(0.5), // 50% soft cap = 5000 bytes free ) const ( numGoroutines = 50 numOperations = 100 reserveBytes = 200 reserveCount = 2 ) var totalReservedBytes atomic.Uint64 var totalReservedCount atomic.Int64 var totalReleasedBytes atomic.Uint64 var totalReleasedCount atomic.Int64 var totalSuccessful atomic.Int64 var totalFailed atomic.Int64 var wg sync.WaitGroup wg.Add(numGoroutines) // Try to reserve concurrently - soft cap should cause some failures // Release only half to maintain non-zero state for g := 0; g < numGoroutines; g++ { go func(goroutineID int) { defer wg.Done() cacheID := fmt.Sprintf("cache%d", goroutineID%10) for i := 0; i < numOperations; i++ { err := mgr.(*manager).ReserveForCache(cacheID, reserveBytes, reserveCount) if err == nil { totalSuccessful.Add(1) totalReservedBytes.Add(reserveBytes) totalReservedCount.Add(reserveCount) // Release only half the time to maintain non-zero state if i%2 == 0 { mgr.(*manager).ReleaseForCache(cacheID, reserveBytes, reserveCount) totalReleasedBytes.Add(reserveBytes) totalReleasedCount.Add(reserveCount) } } else { totalFailed.Add(1) } } }(g) } wg.Wait() // Verify invariants without assuming zero state expectedBytes := totalReservedBytes.Load() - totalReleasedBytes.Load() expectedCount := totalReservedCount.Load() - totalReleasedCount.Load() assert.Equal(t, expectedBytes, mgr.UsedBytes(), "Used bytes should equal total reserved minus total released") assert.Equal(t, expectedCount, mgr.UsedCount(), "Used count should equal total reserved minus total released") // Available capacity should equal total - used assert.Equal(t, uint64(10000)-expectedBytes, mgr.(*manager).AvailableBytes(), "Available bytes should equal capacity minus used") assert.Equal(t, int64(1000)-expectedCount, mgr.(*manager).AvailableCount(), "Available count should equal capacity minus used") // Active cache count should be > 0 since we have unreleased capacity if expectedBytes > 0 || expectedCount > 0 { assert.Greater(t, mgr.(*manager).getActiveCacheCount(), int64(0), "Should have active caches when capacity is in use") assert.NotZero(t, mgr.UsedBytes(), "Should have non-zero bytes in use") assert.NotZero(t, mgr.UsedCount(), "Should have non-zero count in use") } // Log statistics t.Logf("Final state: reserved=%d bytes, released=%d bytes, used=%d bytes, active_caches=%d", totalReservedBytes.Load(), totalReleasedBytes.Load(), mgr.UsedBytes(), mgr.(*manager).getActiveCacheCount()) t.Logf("Operations: successful=%d, failed=%d, total=%d", totalSuccessful.Load(), totalFailed.Load(), totalSuccessful.Load()+totalFailed.Load()) // At least some operations should have succeeded assert.Greater(t, totalSuccessful.Load(), int64(0), "Some operations should succeed") // Soft cap should have caused some failures assert.Greater(t, totalFailed.Load(), int64(0), "Soft cap should cause some failures") } func TestBudgetManager_ConcurrentActiveCacheCount(t *testing.T) { mgr := NewBudgetManager( "test", dynamicproperties.GetIntPropertyFn(1000000), dynamicproperties.GetIntPropertyFn(100000), AdmissionOptimistic, 0, nil, testlogger.New(t), dynamicproperties.GetFloatPropertyFn(0.5), ) const numCaches = 20 // Activate all caches for i := 0; i < numCaches; i++ { cacheID := fmt.Sprintf("cache%d", i) err := mgr.(*manager).ReserveForCache(cacheID, 100, 1) assert.NoError(t, err) } // Verify active cache count assert.Equal(t, int64(numCaches), mgr.(*manager).getActiveCacheCount(), "All caches should be active") // Deactivate all caches concurrently var wg sync.WaitGroup wg.Add(numCaches) for i := 0; i < numCaches; i++ { go func(cacheID string) { defer wg.Done() mgr.(*manager).ReleaseForCache(cacheID, 100, 1) }(fmt.Sprintf("cache%d", i)) } wg.Wait() // Verify all caches are inactive assert.Equal(t, int64(0), mgr.(*manager).getActiveCacheCount(), "All caches should be inactive after release") assert.Equal(t, uint64(0), mgr.UsedBytes(), "All bytes should be released") } func BenchmarkBudgetManager_ReserveRelease(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(1000000), dynamicproperties.GetIntPropertyFn(100000), AdmissionOptimistic, 0, nil, testlogger.New(b), nil, ) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { cacheID := "cache" + string(rune(i%10)) mgr.(*manager).ReserveForCache(cacheID, 100, 1) mgr.(*manager).ReleaseForCache(cacheID, 100, 1) i++ } }) } func BenchmarkBudgetManager_ReserveBytes(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(1000000), dynamicproperties.GetIntPropertyFn(100000), AdmissionOptimistic, 0, nil, testlogger.New(b), nil, ) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { cacheID := "cache" + string(rune(i%10)) mgr.(*manager).ReserveBytesForCache(cacheID, 100) mgr.(*manager).ReleaseBytesForCache(cacheID, 100) i++ } }) } func BenchmarkBudgetManager_ReserveCount(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(1000000), dynamicproperties.GetIntPropertyFn(100000), AdmissionOptimistic, 0, nil, testlogger.New(b), nil, ) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { cacheID := "cache" + string(rune(i%10)) mgr.(*manager).ReserveCountForCache(cacheID, 1) mgr.(*manager).ReleaseCountForCache(cacheID, 1) i++ } }) } func BenchmarkBudgetManager_ReserveWithCallback(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(1000000), dynamicproperties.GetIntPropertyFn(100000), AdmissionOptimistic, 0, nil, testlogger.New(b), nil, ) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { cacheID := "cache" + string(rune(i%10)) mgr.ReserveWithCallback(cacheID, 100, 1, func() error { return nil }) mgr.(*manager).ReleaseForCache(cacheID, 100, 1) i++ } }) } func BenchmarkBudgetManager_ReleaseWithCallback(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(1000000), dynamicproperties.GetIntPropertyFn(100000), AdmissionOptimistic, 0, nil, testlogger.New(b), nil, ) for i := 0; i < 1000; i++ { cacheID := "cache" + string(rune(i%10)) mgr.(*manager).ReserveForCache(cacheID, 100, 1) } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { cacheID := "cache" + string(rune(i%10)) mgr.ReleaseWithCallback(cacheID, func() (uint64, int64, error) { return 100, 1, nil }) mgr.(*manager).ReserveForCache(cacheID, 100, 1) i++ } }) } func BenchmarkBudgetManager_ReserveOrReclaimSelfRelease(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(10000), dynamicproperties.GetIntPropertyFn(1000), AdmissionOptimistic, 0, nil, testlogger.New(b), nil, ) mgr.(*manager).ReserveForCache("cache1", 9000, 900) b.ResetTimer() for i := 0; i < b.N; i++ { mgr.(*manager).ReserveOrReclaimSelfRelease( context.Background(), "cache1", 2000, 200, true, func(needBytes uint64, needCount int64) { mgr.(*manager).ReleaseForCache("cache1", needBytes, needCount) }, ) mgr.(*manager).ReleaseForCache("cache1", 2000, 200) } } func BenchmarkBudgetManager_ReserveOrReclaimManagerRelease(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(10000), dynamicproperties.GetIntPropertyFn(1000), AdmissionOptimistic, 0, nil, testlogger.New(b), nil, ) mgr.(*manager).ReserveForCache("cache1", 9000, 900) b.ResetTimer() for i := 0; i < b.N; i++ { mgr.(*manager).ReserveOrReclaimManagerRelease( context.Background(), "cache1", 2000, 200, true, func(needBytes uint64, needCount int64) (uint64, int64) { return needBytes, needCount }, ) mgr.(*manager).ReleaseForCache("cache1", 2000, 200) } } func BenchmarkBudgetManager_MultipleCaches(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(1000000), dynamicproperties.GetIntPropertyFn(100000), AdmissionOptimistic, 0, nil, testlogger.New(b), nil, ) numCaches := 100 b.ResetTimer() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { cacheID := "cache" + string(rune(i%numCaches)) mgr.(*manager).ReserveForCache(cacheID, 1000, 10) mgr.(*manager).ReleaseForCache(cacheID, 1000, 10) i++ } }) } func BenchmarkBudgetManager_StrictMode(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(1000000), dynamicproperties.GetIntPropertyFn(100000), AdmissionStrict, 0, nil, testlogger.New(b), nil, ) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { cacheID := "cache" + string(rune(i%10)) mgr.(*manager).ReserveForCache(cacheID, 100, 1) mgr.(*manager).ReleaseForCache(cacheID, 100, 1) i++ } }) } func BenchmarkBudgetManager_SoftCap(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(1000000), dynamicproperties.GetIntPropertyFn(100000), AdmissionOptimistic, 0, nil, testlogger.New(b), dynamicproperties.GetFloatPropertyFn(0.5), ) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { cacheID := fmt.Sprintf("cache%d", i%10) mgr.(*manager).ReserveForCache(cacheID, 100, 1) mgr.(*manager).ReleaseForCache(cacheID, 100, 1) i++ } }) } func BenchmarkBudgetManager_HighContention(b *testing.B) { mgr := NewBudgetManager( "benchmark", dynamicproperties.GetIntPropertyFn(100000), dynamicproperties.GetIntPropertyFn(10000), AdmissionOptimistic, 0, nil, testlogger.New(b), nil, ) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { mgr.(*manager).ReserveForCache("shared-cache", 100, 1) mgr.(*manager).ReleaseForCache("shared-cache", 100, 1) } }) } func TestBudgetManager_DisabledManager_NoMetricsEmitted(t *testing.T) { // Create a mock scope that will track all metric calls mockScope := &metricsmocks.Scope{} // Mock the Tagged call which returns itself mockScope.On("Tagged", mock.Anything).Return(mockScope) // Create manager with BOTH capacities disabled (set to 0) mgr := NewBudgetManager( "test-disabled-manager", dynamicproperties.GetIntPropertyFn(0), // maxBytes = 0 (disabled) dynamicproperties.GetIntPropertyFn(0), // maxCount = 0 (disabled) AdmissionOptimistic, 0, mockScope, testlogger.New(t), dynamicproperties.GetFloatPropertyFn(0.8), ) defer mgr.Stop() // Give time for any initial metrics to be emitted time.Sleep(100 * time.Millisecond) // Try to reserve capacity (should fail but shouldn't emit metrics) err := mgr.(*manager).ReserveForCache("cache1", 100, 1) assert.Error(t, err) // Try to release capacity (should not emit metrics) mgr.(*manager).ReleaseForCache("cache1", 100, 1) // Wait a bit to ensure no async metrics are emitted time.Sleep(100 * time.Millisecond) // Verify NO metrics were emitted (no calls to UpdateGauge or IncCounter) mockScope.AssertNotCalled(t, "UpdateGauge") mockScope.AssertNotCalled(t, "IncCounter") } ================================================ FILE: common/cache/cache.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/common/cache package cache import ( "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" ) // A Cache is a generalized interface to a cache. See cache.LRU for a specific // implementation (bounded cache with LRU eviction) type Cache interface { // Get retrieves an element based on a key, returning nil if the element // does not exist Get(key interface{}) interface{} // Put adds an element to the cache, returning the previous element Put(key interface{}, value interface{}) interface{} // PutIfNotExist puts a value associated with a given key if it does not exist PutIfNotExist(key interface{}, value interface{}) (interface{}, error) // Delete deletes an element in the cache Delete(key interface{}) // Release decrements the ref count of a pinned element. If the ref count // drops to 0, the element can be evicted from the cache. Release(key interface{}) // Iterator returns the iterator of the cache Iterator() Iterator // Size returns the number of entries currently stored in the Cache Size() int } // Options control the behavior of the cache type Options struct { // TTL controls the time-to-live for a given cache entry. Cache entries that // are older than the TTL will not be returned. TTL time.Duration // InitialCapacity controls the initial capacity of the cache InitialCapacity int // Pin prevents in-use objects from getting evicted. Pin bool // RemovedFunc is an optional function called when an element // is scheduled for deletion RemovedFunc RemovedFunc // MaxCount controls the max capacity of the cache // It is required option if MaxSize is not provided MaxCount int // MaxSize is an optional flag, but it has to be used along with a value that implements Sizeable() interface // to control the max size in bytes of the cache // It is required option if MaxCount is not provided MaxSize dynamicproperties.IntPropertyFn // ActivelyEvict will evict items that has expired TTL at every operation in the cache // This can be expensive if a lot of items expire at the same time // Should be used when it's important for memory that the expired items are evicted as soon as possible // If not set expired items will be evicted when one of these happens // - when the cache is full // - when the item is accessed ActivelyEvict bool // TimeSource is used to get the current time // It is optional and defaults to clock.NewRealTimeSource() TimeSource clock.TimeSource // IsSizeBased is an optional flag to indicate if the cache is size based // It's default is false, but if set to true, the cache will evict items based on item size instead of count // But the item HAS to be able to cast as a Sizeable interface otherwise the cache will fail IsSizeBased dynamicproperties.BoolPropertyFn // MetricsScope is used to emit metrics for internals of the cache MetricsScope metrics.Scope // Logger is used to emit logs for internals of the cache Logger log.Logger // Deprecated: GetCacheItemSizeFunc is a function called upon adding the item to update the cache size. // It returns 0 by default, assuming the cache is just count based // It is required option if MaxCount is not provided GetCacheItemSizeFunc GetCacheItemSizeFunc } // SimpleOptions provides options that can be used to configure SimpleCache type SimpleOptions struct { // InitialCapacity controls the initial capacity of the cache InitialCapacity int // RemovedFunc is an optional function called when an element // is scheduled for deletion RemovedFunc RemovedFunc } // RemovedFunc is a type for notifying applications when an item is // scheduled for removal from the Cache. If f is a function with the // appropriate signature and i is the interface{} scheduled for // deletion, Cache calls go f(i) type RemovedFunc func(interface{}) // Iterator represents the interface for cache iterators type Iterator interface { // Close closes the iterator // and releases any allocated resources Close() // HasNext return true if there is more items to be returned HasNext() bool // Next return the next item Next() Entry } // Entry represents a key-value entry within the map type Entry interface { // Key represents the key Key() interface{} // Value represents the value Value() interface{} // CreateTime represents the time when the entry is created CreateTime() time.Time } // GetCacheItemSizeFunc returns the cache item size in bytes type GetCacheItemSizeFunc func(interface{}) uint64 // DomainMetricsScopeCache represents a interface for mapping domainID and scopeIdx to metricsScope type DomainMetricsScopeCache interface { // Get retrieves metrics scope for a domainID and scopeIdx Get(domainID string, scopeIdx metrics.ScopeIdx) (metrics.Scope, bool) // Put adds metrics scope for a domainID and scopeIdx Put(domainID string, scopeIdx metrics.ScopeIdx, metricsScope metrics.Scope) common.Daemon } // Sizeable is a interface that implements ByteSize() function type Sizeable interface { // ByteSize returns an approximate size of the object in bytes ByteSize() uint64 } ================================================ FILE: common/cache/domainCache.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination domainCache_mock.go -self_package github.com/uber/cadence/common/cache package cache import ( "context" "fmt" "hash/fnv" "sort" "strconv" "strings" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // ReplicationPolicy is the domain's replication policy, // derived from domain's replication config type ReplicationPolicy int const ( // ReplicationPolicyOneCluster indicate that workflows does not need to be replicated // applicable to local domain & global domain with one cluster ReplicationPolicyOneCluster ReplicationPolicy = 0 // ReplicationPolicyMultiCluster indicate that workflows need to be replicated ReplicationPolicyMultiCluster ReplicationPolicy = 1 ) const ( domainCacheInitialSize = 10 * 1024 domainCacheMinRefreshInterval = 1 * time.Second // DomainCacheRefreshInterval domain cache refresh interval DomainCacheRefreshInterval = 10 * time.Second domainCacheRefreshPageSize = 200 domainCachePersistenceTimeout = 3 * time.Second domainCacheInitialized int32 = 0 domainCacheStarted int32 = 1 domainCacheStopped int32 = 2 ) type ( // PrepareCallbackFn is function to be called before CallbackFn is called, // it is guaranteed that PrepareCallbackFn and CallbackFn pair will be both called or non will be called PrepareCallbackFn func() // CallbackFn is function to be called when the domain cache entries are changed // it is guaranteed that CallbackFn pair will be both called or non will be called CallbackFn func(updatedDomains []*DomainCacheEntry) // CatchUpFn is a function to execute PrepareCallbackFn and/or CallbackFn during domain callback registration, // allowing the caller to catch up with the latest domain changes CatchUpFn func(domainCache DomainCache, prepareCallback PrepareCallbackFn, callback CallbackFn) // DomainCache is used the cache domain information and configuration to avoid making too many calls to cassandra. // This cache is mainly used by frontend for resolving domain names to domain uuids which are used throughout the // system. Each domain entry is kept in the cache for one hour but also has an expiry of 10 seconds. This results // in updating the domain entry every 10 seconds but in the case of a cassandra failure we can still keep on serving // requests using the stale entry from cache upto an hour DomainCache interface { common.Daemon RegisterDomainChangeCallback(id string, catchUpFn CatchUpFn, prepareCallback PrepareCallbackFn, callback CallbackFn) UnregisterDomainChangeCallback(id string) GetDomain(name string) (*DomainCacheEntry, error) GetDomainByID(id string) (*DomainCacheEntry, error) GetDomainID(name string) (string, error) GetDomainName(id string) (string, error) GetAllDomain() map[string]*DomainCacheEntry GetCacheSize() (sizeOfCacheByName int64, sizeOfCacheByID int64) } DefaultDomainCache struct { status int32 shutdownChan chan struct{} clusterGroup string clusterMetadata cluster.Metadata cacheNameToID *atomic.Value cacheByID *atomic.Value domainManager persistence.DomainManager timeSource clock.TimeSource scope metrics.Scope logger log.Logger // refresh lock is used to guarantee at most one // coroutine is doing domain refreshment refreshLock sync.Mutex lastRefreshTime time.Time // This is debug field to emit callback count lastCallbackEmitTime time.Time callbackLock sync.Mutex prepareCallbacks map[string]PrepareCallbackFn callbacks map[string]CallbackFn throttleRetry *backoff.ThrottleRetry // ctx and cancel are used to control the lifecycle of background operations ctx context.Context cancel context.CancelFunc } // DomainCacheEntries is DomainCacheEntry slice DomainCacheEntries []*DomainCacheEntry // DomainCacheEntry contains the info and config for a domain DomainCacheEntry struct { mu sync.RWMutex info *persistence.DomainInfo config *persistence.DomainConfig replicationConfig *persistence.DomainReplicationConfig configVersion int64 failoverVersion int64 isGlobalDomain bool failoverNotificationVersion int64 previousFailoverVersion int64 failoverEndTime *int64 notificationVersion int64 initialized bool // list of active clusters for active-active domains. initialized from replication config. activeClusters []string } ) type DomainCacheOption func(*DefaultDomainCache) func WithTimeSource(timeSource clock.TimeSource) DomainCacheOption { return func(cache *DefaultDomainCache) { if timeSource != nil { cache.timeSource = timeSource } } } // NewDomainCache creates a new instance of cache for holding onto domain information to reduce the load on persistence func NewDomainCache( domainManager persistence.DomainManager, metadata cluster.Metadata, metricsClient metrics.Client, logger log.Logger, opts ...DomainCacheOption, ) *DefaultDomainCache { throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(common.CreateDomainCacheRetryPolicy()), backoff.WithRetryableError(common.IsServiceTransientError), ) ctx, cancel := context.WithCancel(context.Background()) cache := &DefaultDomainCache{ status: domainCacheInitialized, shutdownChan: make(chan struct{}), clusterGroup: getClusterGroupIdentifier(metadata), clusterMetadata: metadata, cacheNameToID: &atomic.Value{}, cacheByID: &atomic.Value{}, domainManager: domainManager, timeSource: clock.NewRealTimeSource(), scope: metricsClient.Scope(metrics.DomainCacheScope), logger: logger, prepareCallbacks: make(map[string]PrepareCallbackFn), callbacks: make(map[string]CallbackFn), throttleRetry: throttleRetry, ctx: ctx, cancel: cancel, } cache.cacheNameToID.Store(newDomainCache()) cache.cacheByID.Store(newDomainCache()) for _, opt := range opts { opt(cache) } return cache } func getClusterGroupIdentifier(metadata cluster.Metadata) string { var clusters []string for cluster := range metadata.GetEnabledClusterInfo() { clusters = append(clusters, cluster) } sort.Strings(clusters) return strings.Join(clusters, "_") } func newDomainCache() Cache { return NewSimple(&SimpleOptions{ InitialCapacity: domainCacheInitialSize, }) } // NewGlobalDomainCacheEntryForTest returns an entry with test data func NewGlobalDomainCacheEntryForTest( info *persistence.DomainInfo, config *persistence.DomainConfig, repConfig *persistence.DomainReplicationConfig, failoverVersion int64, ) *DomainCacheEntry { return &DomainCacheEntry{ info: info, config: config, isGlobalDomain: true, replicationConfig: repConfig, failoverVersion: failoverVersion, } } // NewLocalDomainCacheEntryForTest returns an entry with test data func NewLocalDomainCacheEntryForTest( info *persistence.DomainInfo, config *persistence.DomainConfig, targetCluster string, ) *DomainCacheEntry { return &DomainCacheEntry{ info: info, config: config, isGlobalDomain: false, replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: targetCluster, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: targetCluster}}, }, failoverVersion: constants.EmptyVersion, } } // NewDomainCacheEntryForTest returns an entry with test data func NewDomainCacheEntryForTest( info *persistence.DomainInfo, config *persistence.DomainConfig, isGlobalDomain bool, repConfig *persistence.DomainReplicationConfig, failoverVersion int64, failoverEndtime *int64, failoverNotificationVersion int64, previousFailoverVersion int64, notificationVersion int64, ) *DomainCacheEntry { return &DomainCacheEntry{ info: info, config: config, isGlobalDomain: isGlobalDomain, replicationConfig: repConfig, failoverVersion: failoverVersion, failoverEndTime: failoverEndtime, failoverNotificationVersion: failoverNotificationVersion, previousFailoverVersion: previousFailoverVersion, notificationVersion: notificationVersion, activeClusters: getActiveClusters(repConfig), } } func (c *DefaultDomainCache) GetCacheSize() (sizeOfCacheByName int64, sizeOfCacheByID int64) { return int64(c.cacheByID.Load().(Cache).Size()), int64(c.cacheNameToID.Load().(Cache).Size()) } // Start starts the background refresh of domain func (c *DefaultDomainCache) Start() { if !atomic.CompareAndSwapInt32(&c.status, domainCacheInitialized, domainCacheStarted) { return } // initialize the cache by initial scan err := c.refreshDomains() if err != nil { c.logger.Fatal("Unable to initialize domain cache", tag.Error(err)) } go c.refreshLoop() } // Stop stops background refresh of domain func (c *DefaultDomainCache) Stop() { if !atomic.CompareAndSwapInt32(&c.status, domainCacheStarted, domainCacheStopped) { return } c.cancel() // Cancel the context first close(c.shutdownChan) } func (c *DefaultDomainCache) GetAllDomain() map[string]*DomainCacheEntry { result := make(map[string]*DomainCacheEntry) ite := c.cacheByID.Load().(Cache).Iterator() defer ite.Close() for ite.HasNext() { entry := ite.Next() id := entry.Key().(string) domainCacheEntry := entry.Value().(*DomainCacheEntry) domainCacheEntry.mu.RLock() dup := domainCacheEntry.duplicate() domainCacheEntry.mu.RUnlock() result[id] = dup } return result } // RegisterDomainChangeCallback set a domain change callback // WARN: the beforeCallback function will be triggered by domain cache when holding the domain cache lock, // make sure the callback function will not call domain cache again in case of dead lock // afterCallback will be invoked when NOT holding the domain cache lock. func (c *DefaultDomainCache) RegisterDomainChangeCallback( id string, catchUpFn CatchUpFn, prepareCallback PrepareCallbackFn, callback CallbackFn, ) { c.callbackLock.Lock() c.prepareCallbacks[id] = prepareCallback c.callbacks[id] = callback c.callbackLock.Unlock() catchUpFn(c, prepareCallback, callback) } // UnregisterDomainChangeCallback delete a domain failover callback func (c *DefaultDomainCache) UnregisterDomainChangeCallback( id string, ) { c.callbackLock.Lock() defer c.callbackLock.Unlock() delete(c.prepareCallbacks, id) delete(c.callbacks, id) } // GetDomain retrieves the information from the cache if it exists, otherwise retrieves the information from metadata // store and writes it to the cache with an expiry before returning back func (c *DefaultDomainCache) GetDomain( name string, ) (*DomainCacheEntry, error) { if name == "" { return nil, &types.BadRequestError{Message: "Domain name is empty"} } return c.getDomain(name) } // GetDomainByID retrieves the information from the cache if it exists, otherwise retrieves the information from metadata // store and writes it to the cache with an expiry before returning back func (c *DefaultDomainCache) GetDomainByID( id string, ) (*DomainCacheEntry, error) { if id == "" { return nil, &types.BadRequestError{Message: "DomainID is empty."} } return c.getDomainByID(id, true) } // GetDomainID retrieves domainID by using GetDomain func (c *DefaultDomainCache) GetDomainID( name string, ) (string, error) { entry, err := c.GetDomain(name) if err != nil { return "", err } return entry.info.ID, nil } // GetDomainName returns domain name given the domain id func (c *DefaultDomainCache) GetDomainName( id string, ) (string, error) { entry, err := c.getDomainByID(id, false) if err != nil { return "", err } return entry.info.Name, nil } func (c *DefaultDomainCache) refreshLoop() { timer := c.timeSource.NewTicker(DomainCacheRefreshInterval) defer timer.Stop() for { select { case <-c.shutdownChan: return case <-timer.Chan(): err := c.refreshDomains() if err != nil { c.logger.Error("Error refreshing domain cache", tag.Error(err)) continue } c.logger.Debug("Domain cache refreshed") } } } func (c *DefaultDomainCache) refreshDomains() error { c.refreshLock.Lock() defer c.refreshLock.Unlock() return c.throttleRetry.Do(c.ctx, c.refreshDomainsLocked) } // this function only refresh the domains in the v2 table // the domains in the v1 table will be refreshed if cache is stale func (c *DefaultDomainCache) refreshDomainsLocked(ctx context.Context) error { now := c.timeSource.Now() if now.Sub(c.lastRefreshTime) < domainCacheMinRefreshInterval { return nil } // first load the metadata record, then load domains // this can guarantee that domains in the cache are not updated more than metadata record ctx, cancel := context.WithTimeout(ctx, domainCachePersistenceTimeout) defer cancel() metadata, err := c.domainManager.GetMetadata(ctx) if err != nil { return err } var token []byte request := &persistence.ListDomainsRequest{PageSize: domainCacheRefreshPageSize} var domains DomainCacheEntries continuePage := true for continuePage { ctx, cancel := context.WithTimeout(ctx, domainCachePersistenceTimeout) request.NextPageToken = token response, err := c.domainManager.ListDomains(ctx, request) cancel() if err != nil { return err } token = response.NextPageToken for _, domain := range response.Domains { domains = append(domains, c.buildEntryFromRecord(domain)) } continuePage = len(token) != 0 } // we mush apply the domain change by order // since history shard have to update the shard info // with domain change version. sort.Sort(domains) var updatedEntries []*DomainCacheEntry // make a copy of the existing domain cache, so we can calculate diff and do compare and swap newCacheNameToID := newDomainCache() newCacheByID := newDomainCache() for _, domain := range c.GetAllDomain() { newCacheNameToID.Put(domain.info.Name, domain.info.ID) newCacheByID.Put(domain.info.ID, domain) } UpdateLoop: for _, domain := range domains { if domain.notificationVersion >= metadata.NotificationVersion { // this guarantee that domain change events before the // domainNotificationVersion is loaded into the cache. // the domain change events after the domainNotificationVersion // will be loaded into cache in the next refresh c.logger.Info("Domain notification is not less than than metadata notification version", tag.WorkflowDomainName(domain.GetInfo().Name)) break UpdateLoop } triggerCallback, nextEntry, err := c.updateIDToDomainCache(newCacheByID, domain.info.ID, domain) if err != nil { return err } c.scope.Tagged( metrics.DomainTag(nextEntry.info.Name), metrics.DomainTypeTag(nextEntry.isGlobalDomain), metrics.ClusterGroupTag(c.clusterGroup), metrics.ActiveClusterTag(nextEntry.replicationConfig.ActiveClusterName), metrics.IsActiveActiveDomainTag(nextEntry.replicationConfig.IsActiveActive()), ).UpdateGauge(metrics.ActiveClusterGauge, 1) c.updateNameToIDCache(newCacheNameToID, nextEntry.info.Name, nextEntry.info.ID) if triggerCallback { updatedEntries = append(updatedEntries, nextEntry) } } // NOTE: READ REF BEFORE MODIFICATION // ref: historyEngine.go registerDomainFailoverCallback function c.callbackLock.Lock() defer c.callbackLock.Unlock() c.triggerDomainChangePrepareCallbackLocked() c.cacheByID.Store(newCacheByID) c.cacheNameToID.Store(newCacheNameToID) c.triggerDomainChangeCallbackLocked(updatedEntries) // only update last refresh time when refresh succeeded c.lastRefreshTime = now if now.Sub(c.lastCallbackEmitTime) > 30*time.Minute { c.lastCallbackEmitTime = now c.scope.AddCounter(metrics.DomainCacheCallbacksCount, int64(len(c.callbacks))) c.scope.RecordHistogramDuration(metrics.DomainCacheUpdateLatency, c.timeSource.Now().Sub(now)) } return nil } func (c *DefaultDomainCache) checkDomainExists( name string, id string, ) error { ctx, cancel := context.WithTimeout(context.Background(), domainCachePersistenceTimeout) defer cancel() _, err := c.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{Name: name, ID: id}) return err } func (c *DefaultDomainCache) updateNameToIDCache( cacheNameToID Cache, name string, id string, ) { cacheNameToID.Put(name, id) } func (c *DefaultDomainCache) updateIDToDomainCache( cacheByID Cache, id string, record *DomainCacheEntry, ) (bool, *DomainCacheEntry, error) { elem, err := cacheByID.PutIfNotExist(id, &DomainCacheEntry{}) if err != nil { return false, nil, err } entry := elem.(*DomainCacheEntry) entry.mu.Lock() defer entry.mu.Unlock() // initialized will be true when the entry contains valid data triggerCallback := entry.initialized && record.notificationVersion > entry.notificationVersion entry.info = record.info entry.config = record.config entry.replicationConfig = record.replicationConfig entry.configVersion = record.configVersion entry.failoverVersion = record.failoverVersion entry.isGlobalDomain = record.isGlobalDomain entry.failoverNotificationVersion = record.failoverNotificationVersion entry.previousFailoverVersion = record.previousFailoverVersion entry.failoverEndTime = record.failoverEndTime entry.notificationVersion = record.notificationVersion entry.initialized = record.initialized entry.activeClusters = record.activeClusters return triggerCallback, entry.duplicate(), nil } // getDomain retrieves the information from the cache if it exists, otherwise retrieves the information from metadata // store and writes it to the cache with an expiry before returning back func (c *DefaultDomainCache) getDomain( name string, ) (*DomainCacheEntry, error) { id, cacheHit := c.cacheNameToID.Load().(Cache).Get(name).(string) if cacheHit { return c.getDomainByID(id, true) } if err := c.checkDomainExists(name, ""); err != nil { return nil, err } c.refreshLock.Lock() defer c.refreshLock.Unlock() id, cacheHit = c.cacheNameToID.Load().(Cache).Get(name).(string) if cacheHit { return c.getDomainByID(id, true) } if err := c.refreshDomainsLocked(context.Background()); err != nil { return nil, err } id, cacheHit = c.cacheNameToID.Load().(Cache).Get(name).(string) if cacheHit { return c.getDomainByID(id, true) } // impossible case return nil, &types.InternalServiceError{Message: "DefaultDomainCache encounter case where domain exists but cannot be loaded"} } // getDomainByID retrieves the information from the cache if it exists, otherwise retrieves the information from metadata // store and writes it to the cache with an expiry before returning back func (c *DefaultDomainCache) getDomainByID( id string, deepCopy bool, ) (*DomainCacheEntry, error) { var result *DomainCacheEntry defer func() { if result != nil { c.logger.Debugf("GetDomainByID returning domain %s, failoverVersion: %d", result.info.Name, result.failoverVersion) } }() entry, cacheHit := c.cacheByID.Load().(Cache).Get(id).(*DomainCacheEntry) if cacheHit { entry.mu.RLock() result = entry if deepCopy { result = entry.duplicate() } entry.mu.RUnlock() return result, nil } if err := c.checkDomainExists("", id); err != nil { return nil, err } c.refreshLock.Lock() defer c.refreshLock.Unlock() entry, cacheHit = c.cacheByID.Load().(Cache).Get(id).(*DomainCacheEntry) if cacheHit { entry.mu.RLock() result = entry if deepCopy { result = entry.duplicate() } entry.mu.RUnlock() return result, nil } if err := c.refreshDomainsLocked(context.Background()); err != nil { return nil, err } entry, cacheHit = c.cacheByID.Load().(Cache).Get(id).(*DomainCacheEntry) if cacheHit { entry.mu.RLock() result = entry if deepCopy { result = entry.duplicate() } entry.mu.RUnlock() return result, nil } // impossible case return nil, &types.InternalServiceError{Message: "DefaultDomainCache encounter case where domain exists but cannot be loaded"} } func (c *DefaultDomainCache) triggerDomainChangePrepareCallbackLocked() { sw := c.scope.StartTimer(metrics.DomainCachePrepareCallbacksLatency) defer sw.Stop() for _, prepareCallback := range c.prepareCallbacks { prepareCallback() } } func (c *DefaultDomainCache) triggerDomainChangeCallbackLocked(nextDomains []*DomainCacheEntry) { sw := c.scope.StartTimer(metrics.DomainCacheCallbacksLatency) defer sw.Stop() c.logger.Debug("Domain change callbacks are going to triggered", tag.Number(int64(len(nextDomains)))) for _, callback := range c.callbacks { callback(nextDomains) } c.logger.Debug("Domain change callbacks are completed", tag.Number(int64(len(nextDomains)))) } func (c *DefaultDomainCache) buildEntryFromRecord( record *persistence.GetDomainResponse, ) *DomainCacheEntry { // this is a shallow copy, but since the record is generated by persistence // and only accessible here, it would be fine return &DomainCacheEntry{ info: record.Info, config: record.Config, replicationConfig: record.ReplicationConfig, configVersion: record.ConfigVersion, failoverVersion: record.FailoverVersion, isGlobalDomain: record.IsGlobalDomain, failoverNotificationVersion: record.FailoverNotificationVersion, previousFailoverVersion: record.PreviousFailoverVersion, failoverEndTime: record.FailoverEndTime, notificationVersion: record.NotificationVersion, initialized: true, activeClusters: getActiveClusters(record.ReplicationConfig), } } func copyResetBinary(bins types.BadBinaries) types.BadBinaries { newbins := make(map[string]*types.BadBinaryInfo, len(bins.Binaries)) for k, v := range bins.Binaries { newbins[k] = v } return types.BadBinaries{ Binaries: newbins, } } func (entry *DomainCacheEntry) duplicate() *DomainCacheEntry { // this is a deep copy result := &DomainCacheEntry{} result.info = &persistence.DomainInfo{ ID: entry.info.ID, Name: entry.info.Name, Status: entry.info.Status, Description: entry.info.Description, OwnerEmail: entry.info.OwnerEmail, } result.info.Data = map[string]string{} for k, v := range entry.info.Data { result.info.Data[k] = v } result.config = &persistence.DomainConfig{ Retention: entry.config.Retention, EmitMetric: entry.config.EmitMetric, HistoryArchivalStatus: entry.config.HistoryArchivalStatus, HistoryArchivalURI: entry.config.HistoryArchivalURI, VisibilityArchivalStatus: entry.config.VisibilityArchivalStatus, VisibilityArchivalURI: entry.config.VisibilityArchivalURI, BadBinaries: copyResetBinary(entry.config.BadBinaries), AsyncWorkflowConfig: entry.config.AsyncWorkflowConfig.DeepCopy(), IsolationGroups: entry.config.IsolationGroups.DeepCopy(), } result.replicationConfig = &persistence.DomainReplicationConfig{ ActiveClusterName: entry.replicationConfig.ActiveClusterName, } for _, clusterCfg := range entry.replicationConfig.Clusters { c := *clusterCfg result.replicationConfig.Clusters = append(result.replicationConfig.Clusters, &c) } result.replicationConfig.ActiveClusters = entry.replicationConfig.ActiveClusters.DeepCopy() result.configVersion = entry.configVersion result.failoverVersion = entry.failoverVersion result.isGlobalDomain = entry.isGlobalDomain result.failoverNotificationVersion = entry.failoverNotificationVersion result.previousFailoverVersion = entry.previousFailoverVersion result.failoverEndTime = entry.failoverEndTime result.notificationVersion = entry.notificationVersion result.initialized = entry.initialized result.activeClusters = entry.activeClusters return result } // GetInfo return the domain info func (entry *DomainCacheEntry) GetInfo() *persistence.DomainInfo { return entry.info } // GetConfig return the domain config func (entry *DomainCacheEntry) GetConfig() *persistence.DomainConfig { return entry.config } // GetReplicationConfig return the domain replication config func (entry *DomainCacheEntry) GetReplicationConfig() *persistence.DomainReplicationConfig { return entry.replicationConfig } // GetConfigVersion return the domain config version func (entry *DomainCacheEntry) GetConfigVersion() int64 { return entry.configVersion } // GetFailoverVersion return the domain failover version func (entry *DomainCacheEntry) GetFailoverVersion() int64 { return entry.failoverVersion } // IsGlobalDomain return whether the domain is a global domain func (entry *DomainCacheEntry) IsGlobalDomain() bool { return entry.isGlobalDomain } // GetFailoverNotificationVersion return the global notification version of when failover happened func (entry *DomainCacheEntry) GetFailoverNotificationVersion() int64 { return entry.failoverNotificationVersion } // GetNotificationVersion return the global notification version of when domain changed func (entry *DomainCacheEntry) GetNotificationVersion() int64 { return entry.notificationVersion } // GetPreviousFailoverVersion return the last domain failover version func (entry *DomainCacheEntry) GetPreviousFailoverVersion() int64 { return entry.previousFailoverVersion } // GetFailoverEndTime return the failover end time func (entry *DomainCacheEntry) GetFailoverEndTime() *int64 { return entry.failoverEndTime } // GetActiveClusterInfoByClusterAttribute return the active cluster info for a given cluster attribute // if clusterAttribute is nil, return the domain-level active cluster info and true // if the clusterAttribute exists, return the active cluster info of the clusterAttribute and true // if the clusterAttribute is not found, return false func (entry *DomainCacheEntry) GetActiveClusterInfoByClusterAttribute(clusterAttribute *types.ClusterAttribute) (*types.ActiveClusterInfo, bool) { if clusterAttribute == nil { return &types.ActiveClusterInfo{ ActiveClusterName: entry.GetReplicationConfig().ActiveClusterName, FailoverVersion: entry.GetFailoverVersion(), }, true } if entry.replicationConfig.ActiveClusters == nil { return nil, false } if entry.replicationConfig.ActiveClusters.AttributeScopes == nil { return nil, false } scope, ok := entry.replicationConfig.ActiveClusters.AttributeScopes[clusterAttribute.Scope] if !ok { return nil, false } info, ok := scope.ClusterAttributes[clusterAttribute.Name] if !ok { return nil, false } return &types.ActiveClusterInfo{ ActiveClusterName: info.ActiveClusterName, FailoverVersion: info.FailoverVersion, }, true } // NewDomainNotActiveError return a domain not active error // currentCluster is the current cluster // activeCluster is the active cluster which is either domain's active cluster or it's inferred from workflow task version func (entry *DomainCacheEntry) NewDomainNotActiveError(currentCluster, activeCluster string) *types.DomainNotActiveError { if entry.GetReplicationConfig().IsActiveActive() { return &types.DomainNotActiveError{ Message: fmt.Sprintf( "Domain: %s is active in cluster(s): %v, while current cluster %s is a standby cluster. Operation active cluster: %s", entry.GetInfo().Name, entry.activeClusters, currentCluster, activeCluster, ), DomainName: entry.GetInfo().Name, CurrentCluster: currentCluster, ActiveCluster: activeCluster, ActiveClusters: entry.activeClusters, } } return &types.DomainNotActiveError{ Message: fmt.Sprintf( "Domain: %s is active in cluster: %s, while current cluster %s is a standby cluster.", entry.GetInfo().Name, activeCluster, currentCluster, ), DomainName: entry.GetInfo().Name, CurrentCluster: currentCluster, ActiveCluster: activeCluster, } } // IsActive return whether the domain is active in the current cluster, // - for local domain, it is always active // - for global domain, it is active if it is not pending active and the domain's active cluster is the current cluster or if the domain is active-active and the active cluster of one of the cluster attributes is the current cluster // TODO(active-active): for active-active domains, we should review this logic because now workflows can be active in different clusters based on the cluster attribute. // We should also revisit its usage in history service. func (entry *DomainCacheEntry) IsActiveIn(currentCluster string) bool { if !entry.IsGlobalDomain() { // domain is not a global domain, meaning domain is always "active" within each cluster return true } if entry.IsDomainPendingActive() { return false } activeCluster := entry.GetReplicationConfig().ActiveClusterName if currentCluster == activeCluster { return true } if activeClusters := entry.GetReplicationConfig().ActiveClusters; activeClusters != nil { for _, scope := range activeClusters.AttributeScopes { for _, cl := range scope.ClusterAttributes { if cl.ActiveClusterName == currentCluster { return true } } } } return false } // IsDomainPendingActive returns whether the domain is in pending active state func (entry *DomainCacheEntry) IsDomainPendingActive() bool { if !entry.isGlobalDomain { // domain is not a global domain, meaning domain can never be in pending active state return false } return entry.failoverEndTime != nil } // GetReplicationPolicy return the derived workflow replication policy func (entry *DomainCacheEntry) GetReplicationPolicy() ReplicationPolicy { // frontend guarantee that the clusters always contains the active domain, so if the # of clusters is 1 // then we do not need to send out any events for replication if entry.isGlobalDomain && len(entry.replicationConfig.Clusters) > 1 { return ReplicationPolicyMultiCluster } return ReplicationPolicyOneCluster } // HasReplicationCluster returns true if the domain has replication in the cluster func (entry *DomainCacheEntry) HasReplicationCluster(clusterName string) bool { for _, cluster := range entry.GetReplicationConfig().Clusters { if cluster.ClusterName == clusterName { return true } } return false } // Len return length func (t DomainCacheEntries) Len() int { return len(t) } // Swap implements sort.Interface. func (t DomainCacheEntries) Swap(i, j int) { t[i], t[j] = t[j], t[i] } // Less implements sort.Interface func (t DomainCacheEntries) Less(i, j int) bool { return t[i].notificationVersion < t[j].notificationVersion } // CreateDomainCacheEntry create a cache entry with domainName func CreateDomainCacheEntry( domainName string, ) *DomainCacheEntry { return &DomainCacheEntry{info: &persistence.DomainInfo{Name: domainName}} } // SampleRetentionKey is key to specify sample retention var SampleRetentionKey = "sample_retention_days" // SampleRateKey is key to specify sample rate var SampleRateKey = "sample_retention_rate" // GetRetentionDays returns retention in days for given workflow func (entry *DomainCacheEntry) GetRetentionDays( workflowID string, ) int32 { if entry.IsSampledForLongerRetention(workflowID) { if sampledRetentionValue, ok := entry.info.Data[SampleRetentionKey]; ok { sampledRetentionDays, err := strconv.Atoi(sampledRetentionValue) if err != nil || sampledRetentionDays < int(entry.config.Retention) { return entry.config.Retention } return int32(sampledRetentionDays) } } return entry.config.Retention } // IsSampledForLongerRetentionEnabled return whether sample for longer retention is enabled or not func (entry *DomainCacheEntry) IsSampledForLongerRetentionEnabled( workflowID string, ) bool { _, ok := entry.info.Data[SampleRateKey] return ok } // IsSampledForLongerRetention return should given workflow been sampled or not func (entry *DomainCacheEntry) IsSampledForLongerRetention( workflowID string, ) bool { if sampledRateValue, ok := entry.info.Data[SampleRateKey]; ok { sampledRate, err := strconv.ParseFloat(sampledRateValue, 64) if err != nil { return false } h := fnv.New32a() _, err = h.Write([]byte(workflowID)) if err != nil { return false } hash := h.Sum32() r := float64(hash%1000) / float64(1000) // use 1000 so we support one decimal rate like 1.5%. if r < sampledRate { // sampled return true } } return false } // IsDeprecatedOrDeleted This function checks the domain status to see if the domain has been deprecated or deleted. func (entry *DomainCacheEntry) IsDeprecatedOrDeleted() bool { if entry.info.Status == persistence.DomainStatusDeprecated || entry.info.Status == persistence.DomainStatusDeleted { return true } return false } func getActiveClusters(replicationConfig *persistence.DomainReplicationConfig) []string { if !replicationConfig.IsActiveActive() { return nil } // TODO(active-active): Replace with `GetAllClusters` once we remove regions activeClusters := make([]string, 0, len(replicationConfig.ActiveClusters.AttributeScopes)) for _, scope := range replicationConfig.ActiveClusters.AttributeScopes { for _, cl := range scope.ClusterAttributes { activeClusters = append(activeClusters, cl.ActiveClusterName) } } sort.Strings(activeClusters) return activeClusters } ================================================ FILE: common/cache/domainCacheNoOp.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cache type noOpDomainCache struct{} func (c *noOpDomainCache) GetCacheSize() (sizeOfCacheByName int64, sizeOfCacheByID int64) { return 0, 0 } func (c *noOpDomainCache) GetAllDomain() map[string]*DomainCacheEntry { return map[string]*DomainCacheEntry{} } func (c *noOpDomainCache) RegisterDomainChangeCallback( id string, catchUpFn CatchUpFn, prepareCallback PrepareCallbackFn, callback CallbackFn, ) { } func (c *noOpDomainCache) UnregisterDomainChangeCallback( id string, ) { } func (c *noOpDomainCache) GetDomain( name string, ) (*DomainCacheEntry, error) { return &DomainCacheEntry{}, nil } func (c *noOpDomainCache) GetDomainByID( id string, ) (*DomainCacheEntry, error) { return &DomainCacheEntry{}, nil } func (c *noOpDomainCache) GetDomainID( name string, ) (string, error) { return "", nil } func (c *noOpDomainCache) GetDomainName( id string, ) (string, error) { return "", nil } func (c *noOpDomainCache) getDomain( name string, ) (*DomainCacheEntry, error) { return &DomainCacheEntry{}, nil } func (c *noOpDomainCache) getDomainByID( id string, deepCopy bool, ) (*DomainCacheEntry, error) { return &DomainCacheEntry{}, nil } func NewNoOpDomainCache() DomainCache { return &noOpDomainCache{} } func (c *noOpDomainCache) Start() {} func (c *noOpDomainCache) Stop() {} ================================================ FILE: common/cache/domainCache_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: domainCache.go // // Generated by this command: // // mockgen -package cache -source domainCache.go -destination domainCache_mock.go -self_package github.com/uber/cadence/common/cache // // Package cache is a generated GoMock package. package cache import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockDomainCache is a mock of DomainCache interface. type MockDomainCache struct { ctrl *gomock.Controller recorder *MockDomainCacheMockRecorder isgomock struct{} } // MockDomainCacheMockRecorder is the mock recorder for MockDomainCache. type MockDomainCacheMockRecorder struct { mock *MockDomainCache } // NewMockDomainCache creates a new mock instance. func NewMockDomainCache(ctrl *gomock.Controller) *MockDomainCache { mock := &MockDomainCache{ctrl: ctrl} mock.recorder = &MockDomainCacheMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDomainCache) EXPECT() *MockDomainCacheMockRecorder { return m.recorder } // GetAllDomain mocks base method. func (m *MockDomainCache) GetAllDomain() map[string]*DomainCacheEntry { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllDomain") ret0, _ := ret[0].(map[string]*DomainCacheEntry) return ret0 } // GetAllDomain indicates an expected call of GetAllDomain. func (mr *MockDomainCacheMockRecorder) GetAllDomain() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllDomain", reflect.TypeOf((*MockDomainCache)(nil).GetAllDomain)) } // GetCacheSize mocks base method. func (m *MockDomainCache) GetCacheSize() (int64, int64) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCacheSize") ret0, _ := ret[0].(int64) ret1, _ := ret[1].(int64) return ret0, ret1 } // GetCacheSize indicates an expected call of GetCacheSize. func (mr *MockDomainCacheMockRecorder) GetCacheSize() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCacheSize", reflect.TypeOf((*MockDomainCache)(nil).GetCacheSize)) } // GetDomain mocks base method. func (m *MockDomainCache) GetDomain(name string) (*DomainCacheEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomain", name) ret0, _ := ret[0].(*DomainCacheEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomain indicates an expected call of GetDomain. func (mr *MockDomainCacheMockRecorder) GetDomain(name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomain", reflect.TypeOf((*MockDomainCache)(nil).GetDomain), name) } // GetDomainByID mocks base method. func (m *MockDomainCache) GetDomainByID(id string) (*DomainCacheEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainByID", id) ret0, _ := ret[0].(*DomainCacheEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainByID indicates an expected call of GetDomainByID. func (mr *MockDomainCacheMockRecorder) GetDomainByID(id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainByID", reflect.TypeOf((*MockDomainCache)(nil).GetDomainByID), id) } // GetDomainID mocks base method. func (m *MockDomainCache) GetDomainID(name string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainID", name) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainID indicates an expected call of GetDomainID. func (mr *MockDomainCacheMockRecorder) GetDomainID(name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainID", reflect.TypeOf((*MockDomainCache)(nil).GetDomainID), name) } // GetDomainName mocks base method. func (m *MockDomainCache) GetDomainName(id string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainName", id) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainName indicates an expected call of GetDomainName. func (mr *MockDomainCacheMockRecorder) GetDomainName(id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainName", reflect.TypeOf((*MockDomainCache)(nil).GetDomainName), id) } // RegisterDomainChangeCallback mocks base method. func (m *MockDomainCache) RegisterDomainChangeCallback(id string, catchUpFn CatchUpFn, prepareCallback PrepareCallbackFn, callback CallbackFn) { m.ctrl.T.Helper() m.ctrl.Call(m, "RegisterDomainChangeCallback", id, catchUpFn, prepareCallback, callback) } // RegisterDomainChangeCallback indicates an expected call of RegisterDomainChangeCallback. func (mr *MockDomainCacheMockRecorder) RegisterDomainChangeCallback(id, catchUpFn, prepareCallback, callback any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterDomainChangeCallback", reflect.TypeOf((*MockDomainCache)(nil).RegisterDomainChangeCallback), id, catchUpFn, prepareCallback, callback) } // Start mocks base method. func (m *MockDomainCache) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockDomainCacheMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockDomainCache)(nil).Start)) } // Stop mocks base method. func (m *MockDomainCache) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockDomainCacheMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockDomainCache)(nil).Stop)) } // UnregisterDomainChangeCallback mocks base method. func (m *MockDomainCache) UnregisterDomainChangeCallback(id string) { m.ctrl.T.Helper() m.ctrl.Call(m, "UnregisterDomainChangeCallback", id) } // UnregisterDomainChangeCallback indicates an expected call of UnregisterDomainChangeCallback. func (mr *MockDomainCacheMockRecorder) UnregisterDomainChangeCallback(id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterDomainChangeCallback", reflect.TypeOf((*MockDomainCache)(nil).UnregisterDomainChangeCallback), id) } ================================================ FILE: common/cache/domainCache_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cache import ( "context" "sync" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/testing/testdatagen" "github.com/uber/cadence/common/types" ) type ( domainCacheSuite struct { suite.Suite *require.Assertions metadataMgr *mocks.MetadataManager domainCache *DefaultDomainCache logger log.Logger } ) func TestDomainCacheSuite(t *testing.T) { s := new(domainCacheSuite) suite.Run(t, s) } func (s *domainCacheSuite) SetupSuite() { } func (s *domainCacheSuite) TearDownSuite() { } func (s *domainCacheSuite) SetupTest() { s.Assertions = require.New(s.T()) s.logger = testlogger.New(s.Suite.T()) s.metadataMgr = &mocks.MetadataManager{} metricsClient := metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) s.domainCache = NewDomainCache(s.metadataMgr, cluster.GetTestClusterMetadata(true), metricsClient, s.logger) s.domainCache.timeSource = clock.NewMockedTimeSource() } func (s *domainCacheSuite) TearDownTest() { s.domainCache.Stop() s.metadataMgr.AssertExpectations(s.T()) } func (s *domainCacheSuite) TestListDomain() { domainNotificationVersion := int64(0) domainRecord1 := &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: uuid.New(), Name: "some random domain name", Data: make(map[string]string)}, Config: &persistence.DomainConfig{ Retention: 1, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-async-wf-queue", }, IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": { Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": { Name: "zone-2", State: types.IsolationGroupStateHealthy, }, }, }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, FailoverNotificationVersion: 0, NotificationVersion: domainNotificationVersion, } entry1 := s.buildEntryFromRecord(domainRecord1) domainNotificationVersion++ domainRecord2 := &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: uuid.New(), Name: "another random domain name", Data: make(map[string]string)}, Config: &persistence.DomainConfig{ Retention: 2, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }}, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, FailoverNotificationVersion: 0, NotificationVersion: domainNotificationVersion, } entry2 := s.buildEntryFromRecord(domainRecord2) domainNotificationVersion++ domainRecord3 := &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: uuid.New(), Name: "yet another random domain name", Data: make(map[string]string)}, Config: &persistence.DomainConfig{ Retention: 3, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }}, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, FailoverNotificationVersion: 0, NotificationVersion: domainNotificationVersion, } // there is no domainNotificationVersion++ here // this is to test that if new domain change event happen during the pagination, // new change will not be loaded to domain cache pageToken := []byte("some random page token") s.metadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{NotificationVersion: domainNotificationVersion}, nil) s.metadataMgr.On("ListDomains", mock.Anything, &persistence.ListDomainsRequest{ PageSize: domainCacheRefreshPageSize, NextPageToken: nil, }).Return(&persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{domainRecord1}, NextPageToken: pageToken, }, nil).Once() s.metadataMgr.On("ListDomains", mock.Anything, &persistence.ListDomainsRequest{ PageSize: domainCacheRefreshPageSize, NextPageToken: pageToken, }).Return(&persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{domainRecord2, domainRecord3}, NextPageToken: nil, }, nil).Once() // load domains s.domainCache.Start() defer s.domainCache.Stop() entryByName1, err := s.domainCache.GetDomain(domainRecord1.Info.Name) s.Nil(err) s.Equal(entry1, entryByName1) entryByID1, err := s.domainCache.GetDomainByID(domainRecord1.Info.ID) s.Nil(err) s.Equal(entry1, entryByID1) entryByName2, err := s.domainCache.GetDomain(domainRecord2.Info.Name) s.Nil(err) s.Equal(entry2, entryByName2) entryByID2, err := s.domainCache.GetDomainByID(domainRecord2.Info.ID) s.Nil(err) s.Equal(entry2, entryByID2) allDomains := s.domainCache.GetAllDomain() s.Equal(map[string]*DomainCacheEntry{ entry1.GetInfo().ID: entry1, entry2.GetInfo().ID: entry2, }, allDomains) } func (s *domainCacheSuite) TestGetDomain_NonLoaded_GetByName() { domainNotificationVersion := int64(999999) // make this notification version really large for test s.metadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{NotificationVersion: domainNotificationVersion}, nil) domainRecord := &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: uuid.New(), Name: "some random domain name", Data: make(map[string]string)}, Config: &persistence.DomainConfig{ Retention: 1, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "abc": { Reason: "test reason", Operator: "test operator", CreatedTimeNano: common.Int64Ptr(123), }, }, }}, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, } entry := s.buildEntryFromRecord(domainRecord) s.metadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{Name: entry.info.Name}).Return(domainRecord, nil).Once() s.metadataMgr.On("ListDomains", mock.Anything, &persistence.ListDomainsRequest{ PageSize: domainCacheRefreshPageSize, NextPageToken: nil, }).Return(&persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{domainRecord}, NextPageToken: nil, }, nil).Once() entryByName, err := s.domainCache.GetDomain(domainRecord.Info.Name) s.Nil(err) s.Equal(entry, entryByName) entryByName, err = s.domainCache.GetDomain(domainRecord.Info.Name) s.Nil(err) s.Equal(entry, entryByName) } func (s *domainCacheSuite) TestGetDomain_NonLoaded_GetByID() { domainNotificationVersion := int64(999999) // make this notification version really large for test s.metadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{NotificationVersion: domainNotificationVersion}, nil) domainRecord := &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: uuid.New(), Name: "some random domain name", Data: make(map[string]string)}, Config: &persistence.DomainConfig{ Retention: 1, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }, }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, } entry := s.buildEntryFromRecord(domainRecord) s.metadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{ID: entry.info.ID}).Return(domainRecord, nil).Once() s.metadataMgr.On("ListDomains", mock.Anything, &persistence.ListDomainsRequest{ PageSize: domainCacheRefreshPageSize, NextPageToken: nil, }).Return(&persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{domainRecord}, NextPageToken: nil, }, nil).Once() entryByID, err := s.domainCache.GetDomainByID(domainRecord.Info.ID) s.Nil(err) s.Equal(entry, entryByID) entryByID, err = s.domainCache.GetDomainByID(domainRecord.Info.ID) s.Nil(err) s.Equal(entry, entryByID) } func Test_IsActiveIn(t *testing.T) { tests := []struct { msg string isGlobalDomain bool currentCluster string activeCluster string activeClusters *types.ActiveClusters failoverDeadline *int64 expectIsActive bool }{ { msg: "local domain", isGlobalDomain: false, expectIsActive: true, }, { msg: "global pending active domain", isGlobalDomain: true, failoverDeadline: common.Int64Ptr(time.Now().Unix()), expectIsActive: false, }, { msg: "global domain on active cluster", isGlobalDomain: true, currentCluster: "A", activeCluster: "A", expectIsActive: true, }, { msg: "global domain on passive cluster", isGlobalDomain: true, currentCluster: "A", activeCluster: "B", expectIsActive: false, }, { msg: "active-active domain on active cluster", isGlobalDomain: true, currentCluster: "A", activeClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region0": {ActiveClusterName: "A"}, "region1": {ActiveClusterName: "B"}, }, }, }, }, expectIsActive: true, }, { msg: "active-active domain on domain level active cluster", isGlobalDomain: true, currentCluster: "A", activeCluster: "A", activeClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region0": {ActiveClusterName: "B"}, "region1": {ActiveClusterName: "B"}, }, }, }, }, expectIsActive: true, }, { msg: "active-active domain on passive cluster", isGlobalDomain: true, currentCluster: "C", activeClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region0": {ActiveClusterName: "A"}, "region1": {ActiveClusterName: "B"}, }, }, }, }, expectIsActive: false, }, } for _, tt := range tests { t.Run(tt.msg, func(t *testing.T) { domain := NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "test-domain"}, nil, tt.isGlobalDomain, &persistence.DomainReplicationConfig{ ActiveClusterName: tt.activeCluster, ActiveClusters: tt.activeClusters, }, 0, tt.failoverDeadline, 0, 0, 0, ) isActive := domain.IsActiveIn(tt.currentCluster) assert.Equal(t, tt.expectIsActive, isActive) }) } } func (s *domainCacheSuite) TestRegisterCallback_CatchUp() { prepareCallbackInvoked := false callBackInvoked := false var entriesNotification []*DomainCacheEntry s.domainCache.RegisterDomainChangeCallback( "0", func(_ DomainCache, prepareCallback PrepareCallbackFn, callback CallbackFn) { prepareCallback() callback(entriesNotification) }, func() { prepareCallbackInvoked = true }, func(nextDomains []*DomainCacheEntry) { callBackInvoked = true }, ) s.True(prepareCallbackInvoked) s.True(callBackInvoked) } func (s *domainCacheSuite) TestUpdateCache_TriggerCallBack() { domainNotificationVersion := int64(0) domainRecord1Old := &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: uuid.New(), Name: "some random domain name", Data: make(map[string]string)}, Config: &persistence.DomainConfig{ Retention: 1, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }}, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, ConfigVersion: 10, FailoverVersion: 11, FailoverNotificationVersion: 0, NotificationVersion: domainNotificationVersion, } domainNotificationVersion++ domainRecord2Old := &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: uuid.New(), Name: "another random domain name", Data: make(map[string]string)}, Config: &persistence.DomainConfig{ Retention: 2, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }}, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, ConfigVersion: 20, FailoverVersion: 21, FailoverNotificationVersion: 0, NotificationVersion: domainNotificationVersion, } domainNotificationVersion++ s.metadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{NotificationVersion: domainNotificationVersion}, nil).Once() s.metadataMgr.On("ListDomains", mock.Anything, &persistence.ListDomainsRequest{ PageSize: domainCacheRefreshPageSize, NextPageToken: nil, }).Return(&persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{domainRecord1Old, domainRecord2Old}, NextPageToken: nil, }, nil).Once() // load domains s.Nil(s.domainCache.refreshDomains()) domainRecord2New := &persistence.GetDomainResponse{ Info: &*domainRecord2Old.Info, Config: &*domainRecord2Old.Config, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, // only this changed Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, ConfigVersion: domainRecord2Old.ConfigVersion, FailoverVersion: domainRecord2Old.FailoverVersion + 1, FailoverNotificationVersion: domainNotificationVersion, NotificationVersion: domainNotificationVersion, } entry2New := s.buildEntryFromRecord(domainRecord2New) domainNotificationVersion++ domainRecord1New := &persistence.GetDomainResponse{ // only the description changed Info: &persistence.DomainInfo{ID: domainRecord1Old.Info.ID, Name: domainRecord1Old.Info.Name, Description: "updated description", Data: make(map[string]string)}, Config: &*domainRecord2Old.Config, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, ConfigVersion: domainRecord1Old.ConfigVersion + 1, FailoverVersion: domainRecord1Old.FailoverVersion, FailoverNotificationVersion: domainRecord1Old.FailoverNotificationVersion, NotificationVersion: domainNotificationVersion, } entry1New := s.buildEntryFromRecord(domainRecord1New) domainNotificationVersion++ prepareCallbackInvoked := false var entriesNew []*DomainCacheEntry s.domainCache.RegisterDomainChangeCallback( "0", func(domainCache DomainCache, prepareCallback PrepareCallbackFn, callback CallbackFn) {}, func() { prepareCallbackInvoked = true }, func(nextDomains []*DomainCacheEntry) { entriesNew = nextDomains }, ) s.False(prepareCallbackInvoked) s.Empty(entriesNew) s.metadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{NotificationVersion: domainNotificationVersion}, nil).Once() s.metadataMgr.On("ListDomains", mock.Anything, &persistence.ListDomainsRequest{ PageSize: domainCacheRefreshPageSize, NextPageToken: nil, }).Return(&persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{domainRecord1New, domainRecord2New}, NextPageToken: nil, }, nil).Once() s.domainCache.timeSource.(clock.MockedTimeSource).Advance(domainCacheMinRefreshInterval) s.Nil(s.domainCache.refreshDomains()) // the order matters here: the record 2 got updated first, thus with a lower notification version // the record 1 got updated later, thus a higher notification version. // making sure notifying from lower to higher version helps the shard to keep track the // domain change events s.True(prepareCallbackInvoked) s.Equal([]*DomainCacheEntry{entry2New, entry1New}, entriesNew) } func (s *domainCacheSuite) TestGetTriggerListAndUpdateCache_ConcurrentAccess() { domainNotificationVersion := int64(999999) // make this notification version really large for test s.metadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{NotificationVersion: domainNotificationVersion}, nil) id := uuid.New() domainRecordOld := &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: id, Name: "some random domain name", Data: make(map[string]string)}, Config: &persistence.DomainConfig{ Retention: 1, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }}, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, ConfigVersion: 0, FailoverVersion: 0, } entryOld := s.buildEntryFromRecord(domainRecordOld) s.metadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{ID: id}).Return(domainRecordOld, nil).Maybe() s.metadataMgr.On("ListDomains", mock.Anything, &persistence.ListDomainsRequest{ PageSize: domainCacheRefreshPageSize, NextPageToken: nil, }).Return(&persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{domainRecordOld}, NextPageToken: nil, }, nil).Once() coroutineCountGet := 1000 waitGroup := &sync.WaitGroup{} startChan := make(chan struct{}) testGetFn := func() { <-startChan entryNew, err := s.domainCache.GetDomainByID(id) s.Nil(err) // make the config version the same so we can easily compare those entryNew.configVersion = 0 entryNew.failoverVersion = 0 s.Equal(entryOld, entryNew) waitGroup.Done() } for i := 0; i < coroutineCountGet; i++ { waitGroup.Add(1) go testGetFn() } close(startChan) waitGroup.Wait() } func (s *domainCacheSuite) TestGetCacheSize() { testCache := newDomainCache() testCache.Put("testDomainID", &DomainCacheEntry{ info: &persistence.DomainInfo{ID: "testDomainID", Name: "testDomain"}, }) s.domainCache.cacheByID.Store(testCache) s.domainCache.cacheNameToID.Store(testCache) byName, byID := s.domainCache.GetCacheSize() s.Equal(int64(1), byName) s.Equal(int64(1), byID) } func (s *domainCacheSuite) TestStart_Stop() { mockTimeSource := clock.NewMockedTimeSource() s.domainCache.timeSource = mockTimeSource s.domainCache.lastRefreshTime = mockTimeSource.Now() domainID := uuid.New() domainName := "some random domain name" s.Equal(domainCacheInitialized, s.domainCache.status) s.Equal(0, len(s.domainCache.GetAllDomain())) s.domainCache.Start() // testing noop s.domainCache.Start() mockTimeSource.BlockUntil(1) s.metadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{NotificationVersion: 4}, nil).Once() domainResponse := &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: domainID, Name: domainName, Data: make(map[string]string)}, Config: &persistence.DomainConfig{ BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }, }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, }, FailoverVersion: 124, NotificationVersion: 3, } listDomainsResponse := &persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{domainResponse}, } s.metadataMgr.On("ListDomains", mock.Anything, mock.Anything).Return(listDomainsResponse, nil).Once() mockTimeSource.Advance(DomainCacheRefreshInterval) s.Equal(domainCacheStarted, s.domainCache.status) // need to wait for the go routine to make progress and update the cache time.Sleep(200 * time.Millisecond) allDomains := s.domainCache.GetAllDomain() s.Equal(1, len(allDomains)) s.Equal(domainID, allDomains[domainID].GetInfo().ID) s.Equal(int64(124), allDomains[domainID].GetFailoverVersion()) s.domainCache.Stop() s.Equal(domainCacheStopped, s.domainCache.status) } func (s *domainCacheSuite) TestStart_Error() { mockLogger := log.NewMockLogger(gomock.NewController(s.T())) s.domainCache.logger = mockLogger s.Equal(domainCacheInitialized, s.domainCache.status) s.metadataMgr.On("GetMetadata", mock.Anything).Return(nil, assert.AnError).Once() mockLogger.EXPECT().Fatal("Unable to initialize domain cache", gomock.Any()).Times(1) s.domainCache.Start() } func (s *domainCacheSuite) TestUnregisterDomainChangeCallback() { s.domainCache.prepareCallbacks = map[string]PrepareCallbackFn{ "1": func() {}, } s.domainCache.callbacks = map[string]CallbackFn{ "1": func([]*DomainCacheEntry) {}, } s.domainCache.UnregisterDomainChangeCallback("1") s.Empty(s.domainCache.prepareCallbacks) s.Empty(s.domainCache.callbacks) } func (s *domainCacheSuite) TestGetDomain_Error() { entry, err := s.domainCache.GetDomain("") s.Nil(entry) s.ErrorContains(err, "Domain name is empty") } func (s *domainCacheSuite) TestGetDomainByID_Error() { entry, err := s.domainCache.GetDomainByID("") s.Nil(entry) s.ErrorContains(err, "DomainID is empty.") } func (s *domainCacheSuite) TestGetDomainID() { entry, err := s.domainCache.GetDomainID("") s.Empty(entry) s.ErrorContains(err, "Domain name is empty") domainName := "testDomain" domainID := "testDomainID" testCache := newDomainCache() domainEntry := &DomainCacheEntry{ info: &persistence.DomainInfo{ID: domainID, Name: domainName}, config: &persistence.DomainConfig{ BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }, }, replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, }, initialized: true, } testCache.Put(domainName, domainID) testCache.Put(domainID, domainEntry) s.domainCache.cacheByID.Store(testCache) s.domainCache.cacheNameToID.Store(testCache) entry, err = s.domainCache.GetDomainID(domainName) s.Nil(err) s.Equal(domainID, entry) } func (s *domainCacheSuite) TestGetDomainName() { domainName := "testDomain" domainID := "testDomainID" testCache := newDomainCache() domainEntry := &DomainCacheEntry{ info: &persistence.DomainInfo{ID: domainID, Name: domainName}, config: &persistence.DomainConfig{ BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }, }, replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, }, initialized: true, } testCache.Put(domainName, domainID) testCache.Put(domainID, domainEntry) s.domainCache.cacheByID.Store(testCache) s.domainCache.cacheNameToID.Store(testCache) entry, err := s.domainCache.GetDomainName(domainID) s.Nil(err) s.Equal(domainName, entry) } func (s *domainCacheSuite) TestGetDomainName_Error() { s.metadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{Name: "", ID: ""}).Return(nil, assert.AnError).Once() entry, err := s.domainCache.GetDomainName("") s.Empty(entry) s.ErrorIs(err, assert.AnError) } func (s *domainCacheSuite) Test_updateIDToDomainCache_Error() { domainCacheEntry := &DomainCacheEntry{ info: &persistence.DomainInfo{ID: "testDomainID", Name: "testDomain"}, } newCache := NewMockCache(gomock.NewController(s.T())) newCache.EXPECT().PutIfNotExist("testDomainID", &DomainCacheEntry{}).Return(false, assert.AnError).Times(1) triggerCallback, entry, err := s.domainCache.updateIDToDomainCache(newCache, "testDomainID", domainCacheEntry) s.False(triggerCallback) s.Nil(entry) s.ErrorIs(err, assert.AnError) } func (s *domainCacheSuite) Test_getDomain_Error_checkDomainExists() { domainName := "testDomain" s.metadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{Name: domainName, ID: ""}).Return(nil, assert.AnError).Once() entry, err := s.domainCache.getDomain(domainName) s.Nil(entry) s.ErrorIs(err, assert.AnError) } func (s *domainCacheSuite) Test_getDomain_Error_refreshDomainsLocked() { domainName := "testDomain" s.metadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{Name: domainName, ID: ""}).Return(nil, nil).Once() s.metadataMgr.On("GetMetadata", mock.Anything).Return(nil, assert.AnError).Once() entry, err := s.domainCache.getDomain(domainName) s.Nil(entry) s.ErrorIs(err, assert.AnError) } func (s *domainCacheSuite) Test_getDomain_cacheHitAfterRefreshLockLocked() { domainName := "testDomain" domainID := "testDomainID" testCache := newDomainCache() domainEntry := &DomainCacheEntry{ info: &persistence.DomainInfo{ID: domainID, Name: domainName, Data: map[string]string{"k1": "v1"}}, config: &persistence.DomainConfig{ BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }, }, replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, }, initialized: true, } testCache.Put(domainName, domainID) testCache.Put(domainID, domainEntry) wg := sync.WaitGroup{} wg.Add(1) go func() { defer s.domainCache.refreshLock.Unlock() // to test the cache hit after refresh lock is locked, need to ensure that the domain cache is added after the first cache hit check // force a lock to ensure that the code will block on the lock, wait for the first cache hit check, add the domain cache // and then release the lock s.domainCache.refreshLock.Lock() wg.Done() time.Sleep(200 * time.Millisecond) s.domainCache.cacheByID.Store(testCache) s.domainCache.cacheNameToID.Store(testCache) }() wg.Wait() s.metadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{Name: domainName, ID: ""}).Return(nil, nil).Once() entry, err := s.domainCache.getDomain(domainName) s.Nil(err) // because the code makes deep copies, it's not possible to compare all the pointers directly s.Equal(domainEntry.info.Name, entry.info.Name) s.Equal(domainEntry.info.ID, entry.info.ID) s.Equal(1, len(entry.info.Data)) s.Equal("v1", entry.info.Data["k1"]) } func (s *domainCacheSuite) Test_getDomainByID_refreshDomainsLockedError() { domainID := "testDomainID" s.metadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{Name: "", ID: domainID}).Return(nil, nil).Once() s.metadataMgr.On("GetMetadata", mock.Anything).Return(nil, assert.AnError).Once() entry, err := s.domainCache.getDomainByID(domainID, false) s.Nil(entry) s.ErrorIs(err, assert.AnError) } func (s *domainCacheSuite) Test_refreshLoop_domainCacheRefreshedError() { mockedTimeSource := clock.NewMockedTimeSource() s.domainCache.timeSource = mockedTimeSource s.metadataMgr.On("GetMetadata", mock.Anything).Return(nil, assert.AnError).Once() go func() { mockedTimeSource.BlockUntil(1) mockedTimeSource.Advance(DomainCacheRefreshInterval) s.domainCache.shutdownChan <- struct{}{} }() s.domainCache.refreshLoop() } func (s *domainCacheSuite) Test_refreshDomainsLocked_IntervalTooShort() { mockedTimeSource := clock.NewMockedTimeSource() s.domainCache.timeSource = mockedTimeSource s.domainCache.lastRefreshTime = mockedTimeSource.Now() ctx := context.Background() err := s.domainCache.refreshDomainsLocked(ctx) s.NoError(err) } func (s *domainCacheSuite) Test_refreshDomains_ListDomainsNonRetryableError() { s.metadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{NotificationVersion: 0}, nil).Once() s.metadataMgr.On("ListDomains", mock.Anything, mock.Anything).Return(nil, assert.AnError).Once() err := s.domainCache.refreshDomains() s.ErrorIs(err, assert.AnError) } func (s *domainCacheSuite) Test_refreshDomains_ListDomainsRetryableError() { retryableError := &types.ServiceBusyError{ Message: "Service is busy", } // We expect the metadataMgr to be called twice, once for the initial attempt and once for the retry s.metadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{NotificationVersion: 0}, nil).Times(2) // First time return retryable error s.metadataMgr.On("ListDomains", mock.Anything, mock.Anything).Return(nil, retryableError).Once() // Second time return non-retryable error s.metadataMgr.On("ListDomains", mock.Anything, mock.Anything).Return(nil, assert.AnError).Once() err := s.domainCache.refreshDomains() // We expect the error to be the first error s.ErrorIs(err, retryableError) } func (s *domainCacheSuite) TestDomainCacheEntry_Getters() { gen := testdatagen.New(s.T()) entry := DomainCacheEntry{} gen.Fuzz(&entry) s.Equal(entry.info, entry.GetInfo()) s.Equal(entry.config, entry.GetConfig()) s.Equal(entry.replicationConfig, entry.GetReplicationConfig()) s.Equal(entry.failoverNotificationVersion, entry.GetFailoverNotificationVersion()) s.Equal(entry.notificationVersion, entry.GetNotificationVersion()) s.Equal(entry.failoverVersion, entry.GetFailoverVersion()) s.Equal(entry.previousFailoverVersion, entry.GetPreviousFailoverVersion()) s.Equal(entry.failoverEndTime, entry.GetFailoverEndTime()) s.Equal(entry.isGlobalDomain, entry.IsGlobalDomain()) s.Equal(entry.configVersion, entry.GetConfigVersion()) } func (s *domainCacheSuite) TestDomainCacheEntry_IsDomainPendingActive() { // Local domain entry := CreateDomainCacheEntry("domainName") s.False(entry.IsDomainPendingActive()) // Global domain not pending active entry.isGlobalDomain = true s.False(entry.IsDomainPendingActive()) // Global domain pending active entry.failoverEndTime = common.Int64Ptr(time.Now().Unix() + 100) s.True(entry.IsDomainPendingActive()) } func (s *domainCacheSuite) TestDomainCacheEntry_GetReplicationPolicy() { entry := &DomainCacheEntry{ replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "active"}, {ClusterName: "standby"}, }, }, } s.Equal(ReplicationPolicyOneCluster, entry.GetReplicationPolicy()) entry.isGlobalDomain = true s.Equal(ReplicationPolicyMultiCluster, entry.GetReplicationPolicy()) } func (s *domainCacheSuite) TestDomainCacheEntry_HasReplicationCluster() { entry := &DomainCacheEntry{ replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "active"}, {ClusterName: "standby"}, }, }, } s.True(entry.HasReplicationCluster("active")) s.True(entry.HasReplicationCluster("standby")) s.False(entry.HasReplicationCluster("other")) } func (s *domainCacheSuite) TestDomainCacheEntry_IsDeprecatedOrDeleted() { entry := &DomainCacheEntry{ info: &persistence.DomainInfo{ Status: persistence.DomainStatusDeprecated, }, } s.True(entry.IsDeprecatedOrDeleted()) entry.info.Status = persistence.DomainStatusDeleted s.True(entry.IsDeprecatedOrDeleted()) entry.info.Status = persistence.DomainStatusRegistered s.False(entry.IsDeprecatedOrDeleted()) } func (s *domainCacheSuite) buildEntryFromRecord(record *persistence.GetDomainResponse) *DomainCacheEntry { newEntry := &DomainCacheEntry{} newEntry.info = &*record.Info newEntry.config = &*record.Config newEntry.replicationConfig = &persistence.DomainReplicationConfig{ ActiveClusterName: record.ReplicationConfig.ActiveClusterName, } for _, testCluster := range record.ReplicationConfig.Clusters { newEntry.replicationConfig.Clusters = append(newEntry.replicationConfig.Clusters, &*testCluster) } newEntry.configVersion = record.ConfigVersion newEntry.failoverVersion = record.FailoverVersion newEntry.isGlobalDomain = record.IsGlobalDomain newEntry.failoverNotificationVersion = record.FailoverNotificationVersion newEntry.notificationVersion = record.NotificationVersion newEntry.initialized = true return newEntry } func Test_GetRetentionDays(t *testing.T) { d := &DomainCacheEntry{ info: &persistence.DomainInfo{ Data: make(map[string]string), }, config: &persistence.DomainConfig{ Retention: 7, }, } d.info.Data[SampleRetentionKey] = "30" d.info.Data[SampleRateKey] = "0" wid := uuid.New() rd := d.GetRetentionDays(wid) require.Equal(t, int32(7), rd) d.info.Data[SampleRateKey] = "1" rd = d.GetRetentionDays(wid) require.Equal(t, int32(30), rd) d.info.Data[SampleRetentionKey] = "invalid-value" rd = d.GetRetentionDays(wid) require.Equal(t, int32(7), rd) // fallback to normal retention d.info.Data[SampleRetentionKey] = "30" d.info.Data[SampleRateKey] = "invalid-value" rd = d.GetRetentionDays(wid) require.Equal(t, int32(7), rd) // fallback to normal retention wid = "3aef42a8-db0a-4a3b-b8b7-9829d74b4ebf" d.info.Data[SampleRetentionKey] = "30" d.info.Data[SampleRateKey] = "0.8" rd = d.GetRetentionDays(wid) require.Equal(t, int32(7), rd) // fallback to normal retention d.info.Data[SampleRateKey] = "0.9" rd = d.GetRetentionDays(wid) require.Equal(t, int32(30), rd) } func Test_IsSampledForLongerRetentionEnabled(t *testing.T) { d := &DomainCacheEntry{ info: &persistence.DomainInfo{ Data: make(map[string]string), }, config: &persistence.DomainConfig{ Retention: 7, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }, }, } wid := uuid.New() require.False(t, d.IsSampledForLongerRetentionEnabled(wid)) d.info.Data[SampleRetentionKey] = "30" d.info.Data[SampleRateKey] = "0" require.True(t, d.IsSampledForLongerRetentionEnabled(wid)) } func Test_IsSampledForLongerRetention(t *testing.T) { d := &DomainCacheEntry{ info: &persistence.DomainInfo{ Data: make(map[string]string), }, config: &persistence.DomainConfig{ Retention: 7, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }, }, } wid := uuid.New() require.False(t, d.IsSampledForLongerRetention(wid)) d.info.Data[SampleRetentionKey] = "30" d.info.Data[SampleRateKey] = "0" require.False(t, d.IsSampledForLongerRetention(wid)) d.info.Data[SampleRateKey] = "1" require.True(t, d.IsSampledForLongerRetention(wid)) d.info.Data[SampleRateKey] = "invalid-value" require.False(t, d.IsSampledForLongerRetention(wid)) } func Test_WithTimeSource(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() metadataMgr := &mocks.MetadataManager{} timeSource := clock.NewRealTimeSource() domainCache := NewDomainCache(metadataMgr, cluster.GetTestClusterMetadata(true), metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), log.NewNoop(), WithTimeSource(timeSource)) assert.Equal(t, timeSource, domainCache.timeSource) } func Test_NewLocalDomainCacheEntryForTest(t *testing.T) { domain := NewLocalDomainCacheEntryForTest(&persistence.DomainInfo{Name: "test-domain"}, nil, "targetCluster") assert.False(t, domain.IsGlobalDomain()) } func Test_NewDomainNotActiveError(t *testing.T) { tests := []struct { msg string domain *DomainCacheEntry currentCluster string activeCluster string expectedErr *types.DomainNotActiveError }{ { msg: "local domain", domain: NewLocalDomainCacheEntryForTest(&persistence.DomainInfo{Name: "test-domain"}, nil, "targetCluster"), currentCluster: "currentCluster", activeCluster: "targetCluster", expectedErr: &types.DomainNotActiveError{ Message: "Domain: test-domain is active in cluster: targetCluster, while current cluster currentCluster is a standby cluster.", DomainName: "test-domain", CurrentCluster: "currentCluster", ActiveCluster: "targetCluster", }, }, { msg: "active-active domain", currentCluster: "cluster1", activeCluster: "cluster2", domain: NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "test-domain"}, nil, true, &persistence.DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": {ActiveClusterName: "cluster1"}, "region2": {ActiveClusterName: "cluster2"}, }, }, }, }, }, 0, nil, 0, 0, 0, ), expectedErr: &types.DomainNotActiveError{ Message: "Domain: test-domain is active in cluster(s): [cluster1 cluster2], while current cluster cluster1 is a standby cluster. Operation active cluster: cluster2", DomainName: "test-domain", CurrentCluster: "cluster1", ActiveClusters: []string{"cluster1", "cluster2"}, ActiveCluster: "cluster2", }, }, } for _, tt := range tests { t.Run(tt.msg, func(t *testing.T) { err := tt.domain.NewDomainNotActiveError(tt.currentCluster, tt.activeCluster) assert.Equal(t, tt.expectedErr, err) }) } } func Test_getActiveClusters(t *testing.T) { tests := []struct { msg string replicationConfig *persistence.DomainReplicationConfig expectedActiveClusters []string }{ { msg: "active-passive domain", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", }, expectedActiveClusters: nil, }, { msg: "active-active domain", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": {ActiveClusterName: "cluster1"}, "region2": {ActiveClusterName: "cluster2"}, }, }, }, }, }, expectedActiveClusters: []string{"cluster1", "cluster2"}, }, } for _, tt := range tests { t.Run(tt.msg, func(t *testing.T) { activeClusters := getActiveClusters(tt.replicationConfig) assert.Equal(t, tt.expectedActiveClusters, activeClusters) }) } } func Test_GetActiveClusterInfoByClusterAttribute(t *testing.T) { tests := []struct { name string entry *DomainCacheEntry clusterAttribute *types.ClusterAttribute expectedActiveCluster *types.ActiveClusterInfo expectedFound bool }{ { name: "nil cluster attribute - returns domain-level active cluster info", entry: &DomainCacheEntry{ replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "domain-active-cluster", }, failoverVersion: 100, }, clusterAttribute: nil, expectedActiveCluster: &types.ActiveClusterInfo{ ActiveClusterName: "domain-active-cluster", FailoverVersion: 100, }, expectedFound: true, }, { name: "nil active clusters - returns false", entry: &DomainCacheEntry{ replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "domain-active-cluster", ActiveClusters: nil, }, failoverVersion: 100, }, clusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, expectedActiveCluster: nil, expectedFound: false, }, { name: "nil attribute scopes - returns false", entry: &DomainCacheEntry{ replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "domain-active-cluster", ActiveClusters: &types.ActiveClusters{ AttributeScopes: nil, }, }, failoverVersion: 100, }, clusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, expectedActiveCluster: nil, expectedFound: false, }, { name: "scope not found - returns false", entry: &DomainCacheEntry{ replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "domain-active-cluster", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, }, }, }, }, failoverVersion: 100, }, clusterAttribute: &types.ClusterAttribute{ Scope: "region", // different scope Name: "us-west", }, expectedActiveCluster: nil, expectedFound: false, }, { name: "attribute not found in scope - returns false", entry: &DomainCacheEntry{ replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "domain-active-cluster", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": { ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, }, }, }, }, failoverVersion: 100, }, clusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", // different name }, expectedActiveCluster: nil, expectedFound: false, }, { name: "successful lookup - returns cluster attribute info", entry: &DomainCacheEntry{ replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "domain-active-cluster", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster-west", FailoverVersion: 300, }, "us-east": { ActiveClusterName: "cluster-east", FailoverVersion: 250, }, }, }, }, }, }, failoverVersion: 100, }, clusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, expectedActiveCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-west", FailoverVersion: 300, }, expectedFound: true, }, { name: "multiple scopes - successful lookup in specific scope", entry: &DomainCacheEntry{ replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "domain-active-cluster", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "region-cluster-west", FailoverVersion: 300, }, }, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "dc-cluster-west", FailoverVersion: 400, }, }, }, }, }, }, failoverVersion: 100, }, clusterAttribute: &types.ClusterAttribute{ Scope: "datacenter", Name: "us-west", }, expectedActiveCluster: &types.ActiveClusterInfo{ ActiveClusterName: "dc-cluster-west", FailoverVersion: 400, }, expectedFound: true, }, { name: "empty cluster attributes map in scope - returns false", entry: &DomainCacheEntry{ replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "domain-active-cluster", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, }, }, }, failoverVersion: 100, }, clusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, expectedActiveCluster: nil, expectedFound: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { actualActiveCluster, actualFound := tt.entry.GetActiveClusterInfoByClusterAttribute(tt.clusterAttribute) assert.Equal(t, tt.expectedFound, actualFound) assert.Equal(t, tt.expectedActiveCluster, actualActiveCluster) }) } } ================================================ FILE: common/cache/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: cache.go // // Generated by this command: // // mockgen -package cache -source cache.go -destination interface_mock.go -self_package github.com/uber/cadence/common/cache // // Package cache is a generated GoMock package. package cache import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" metrics "github.com/uber/cadence/common/metrics" ) // MockCache is a mock of Cache interface. type MockCache struct { ctrl *gomock.Controller recorder *MockCacheMockRecorder isgomock struct{} } // MockCacheMockRecorder is the mock recorder for MockCache. type MockCacheMockRecorder struct { mock *MockCache } // NewMockCache creates a new mock instance. func NewMockCache(ctrl *gomock.Controller) *MockCache { mock := &MockCache{ctrl: ctrl} mock.recorder = &MockCacheMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCache) EXPECT() *MockCacheMockRecorder { return m.recorder } // Delete mocks base method. func (m *MockCache) Delete(key any) { m.ctrl.T.Helper() m.ctrl.Call(m, "Delete", key) } // Delete indicates an expected call of Delete. func (mr *MockCacheMockRecorder) Delete(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCache)(nil).Delete), key) } // Get mocks base method. func (m *MockCache) Get(key any) any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", key) ret0, _ := ret[0].(any) return ret0 } // Get indicates an expected call of Get. func (mr *MockCacheMockRecorder) Get(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCache)(nil).Get), key) } // Iterator mocks base method. func (m *MockCache) Iterator() Iterator { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Iterator") ret0, _ := ret[0].(Iterator) return ret0 } // Iterator indicates an expected call of Iterator. func (mr *MockCacheMockRecorder) Iterator() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Iterator", reflect.TypeOf((*MockCache)(nil).Iterator)) } // Put mocks base method. func (m *MockCache) Put(key, value any) any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Put", key, value) ret0, _ := ret[0].(any) return ret0 } // Put indicates an expected call of Put. func (mr *MockCacheMockRecorder) Put(key, value any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockCache)(nil).Put), key, value) } // PutIfNotExist mocks base method. func (m *MockCache) PutIfNotExist(key, value any) (any, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PutIfNotExist", key, value) ret0, _ := ret[0].(any) ret1, _ := ret[1].(error) return ret0, ret1 } // PutIfNotExist indicates an expected call of PutIfNotExist. func (mr *MockCacheMockRecorder) PutIfNotExist(key, value any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutIfNotExist", reflect.TypeOf((*MockCache)(nil).PutIfNotExist), key, value) } // Release mocks base method. func (m *MockCache) Release(key any) { m.ctrl.T.Helper() m.ctrl.Call(m, "Release", key) } // Release indicates an expected call of Release. func (mr *MockCacheMockRecorder) Release(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Release", reflect.TypeOf((*MockCache)(nil).Release), key) } // Size mocks base method. func (m *MockCache) Size() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Size") ret0, _ := ret[0].(int) return ret0 } // Size indicates an expected call of Size. func (mr *MockCacheMockRecorder) Size() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Size", reflect.TypeOf((*MockCache)(nil).Size)) } // MockIterator is a mock of Iterator interface. type MockIterator struct { ctrl *gomock.Controller recorder *MockIteratorMockRecorder isgomock struct{} } // MockIteratorMockRecorder is the mock recorder for MockIterator. type MockIteratorMockRecorder struct { mock *MockIterator } // NewMockIterator creates a new mock instance. func NewMockIterator(ctrl *gomock.Controller) *MockIterator { mock := &MockIterator{ctrl: ctrl} mock.recorder = &MockIteratorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockIterator) EXPECT() *MockIteratorMockRecorder { return m.recorder } // Close mocks base method. func (m *MockIterator) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockIteratorMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockIterator)(nil).Close)) } // HasNext mocks base method. func (m *MockIterator) HasNext() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasNext") ret0, _ := ret[0].(bool) return ret0 } // HasNext indicates an expected call of HasNext. func (mr *MockIteratorMockRecorder) HasNext() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasNext", reflect.TypeOf((*MockIterator)(nil).HasNext)) } // Next mocks base method. func (m *MockIterator) Next() Entry { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Next") ret0, _ := ret[0].(Entry) return ret0 } // Next indicates an expected call of Next. func (mr *MockIteratorMockRecorder) Next() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Next", reflect.TypeOf((*MockIterator)(nil).Next)) } // MockEntry is a mock of Entry interface. type MockEntry struct { ctrl *gomock.Controller recorder *MockEntryMockRecorder isgomock struct{} } // MockEntryMockRecorder is the mock recorder for MockEntry. type MockEntryMockRecorder struct { mock *MockEntry } // NewMockEntry creates a new mock instance. func NewMockEntry(ctrl *gomock.Controller) *MockEntry { mock := &MockEntry{ctrl: ctrl} mock.recorder = &MockEntryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockEntry) EXPECT() *MockEntryMockRecorder { return m.recorder } // CreateTime mocks base method. func (m *MockEntry) CreateTime() time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateTime") ret0, _ := ret[0].(time.Time) return ret0 } // CreateTime indicates an expected call of CreateTime. func (mr *MockEntryMockRecorder) CreateTime() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTime", reflect.TypeOf((*MockEntry)(nil).CreateTime)) } // Key mocks base method. func (m *MockEntry) Key() any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Key") ret0, _ := ret[0].(any) return ret0 } // Key indicates an expected call of Key. func (mr *MockEntryMockRecorder) Key() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Key", reflect.TypeOf((*MockEntry)(nil).Key)) } // Value mocks base method. func (m *MockEntry) Value() any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Value") ret0, _ := ret[0].(any) return ret0 } // Value indicates an expected call of Value. func (mr *MockEntryMockRecorder) Value() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Value", reflect.TypeOf((*MockEntry)(nil).Value)) } // MockDomainMetricsScopeCache is a mock of DomainMetricsScopeCache interface. type MockDomainMetricsScopeCache struct { ctrl *gomock.Controller recorder *MockDomainMetricsScopeCacheMockRecorder isgomock struct{} } // MockDomainMetricsScopeCacheMockRecorder is the mock recorder for MockDomainMetricsScopeCache. type MockDomainMetricsScopeCacheMockRecorder struct { mock *MockDomainMetricsScopeCache } // NewMockDomainMetricsScopeCache creates a new mock instance. func NewMockDomainMetricsScopeCache(ctrl *gomock.Controller) *MockDomainMetricsScopeCache { mock := &MockDomainMetricsScopeCache{ctrl: ctrl} mock.recorder = &MockDomainMetricsScopeCacheMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDomainMetricsScopeCache) EXPECT() *MockDomainMetricsScopeCacheMockRecorder { return m.recorder } // Get mocks base method. func (m *MockDomainMetricsScopeCache) Get(domainID string, scopeIdx metrics.ScopeIdx) (metrics.Scope, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", domainID, scopeIdx) ret0, _ := ret[0].(metrics.Scope) ret1, _ := ret[1].(bool) return ret0, ret1 } // Get indicates an expected call of Get. func (mr *MockDomainMetricsScopeCacheMockRecorder) Get(domainID, scopeIdx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockDomainMetricsScopeCache)(nil).Get), domainID, scopeIdx) } // Put mocks base method. func (m *MockDomainMetricsScopeCache) Put(domainID string, scopeIdx metrics.ScopeIdx, metricsScope metrics.Scope) { m.ctrl.T.Helper() m.ctrl.Call(m, "Put", domainID, scopeIdx, metricsScope) } // Put indicates an expected call of Put. func (mr *MockDomainMetricsScopeCacheMockRecorder) Put(domainID, scopeIdx, metricsScope any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockDomainMetricsScopeCache)(nil).Put), domainID, scopeIdx, metricsScope) } // Start mocks base method. func (m *MockDomainMetricsScopeCache) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockDomainMetricsScopeCacheMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockDomainMetricsScopeCache)(nil).Start)) } // Stop mocks base method. func (m *MockDomainMetricsScopeCache) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockDomainMetricsScopeCacheMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockDomainMetricsScopeCache)(nil).Stop)) } // MockSizeable is a mock of Sizeable interface. type MockSizeable struct { ctrl *gomock.Controller recorder *MockSizeableMockRecorder isgomock struct{} } // MockSizeableMockRecorder is the mock recorder for MockSizeable. type MockSizeableMockRecorder struct { mock *MockSizeable } // NewMockSizeable creates a new mock instance. func NewMockSizeable(ctrl *gomock.Controller) *MockSizeable { mock := &MockSizeable{ctrl: ctrl} mock.recorder = &MockSizeableMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSizeable) EXPECT() *MockSizeableMockRecorder { return m.recorder } // ByteSize mocks base method. func (m *MockSizeable) ByteSize() uint64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByteSize") ret0, _ := ret[0].(uint64) return ret0 } // ByteSize indicates an expected call of ByteSize. func (mr *MockSizeableMockRecorder) ByteSize() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByteSize", reflect.TypeOf((*MockSizeable)(nil).ByteSize)) } ================================================ FILE: common/cache/lru.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cache import ( "container/list" "errors" "fmt" "sync" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) var ( // ErrCacheFull is returned if Put fails due to cache being filled with pinned elements ErrCacheFull = errors.New("Cache capacity is fully occupied with pinned elements") // ErrEntryTooBig is returned if the entry is too big to be cached ErrEntryTooBig = errors.New("Entry is too big to be cached") ) // upper limit to prevent infinite growing const cacheCountLimit = 1 << 25 // default size limit for size based cache if not defined (1 GB) const cacheDefaultSizeLimit = 1 << 30 // lru is a concurrent fixed size cache that evicts elements in lru order type ( lru struct { mut sync.Mutex byAccess *list.List byKey map[interface{}]*list.Element maxCount int ttl time.Duration pin bool rmFunc RemovedFunc sizeFunc GetCacheItemSizeFunc maxSize dynamicproperties.IntPropertyFn currSize uint64 sizeByKey map[interface{}]uint64 isSizeBased dynamicproperties.BoolPropertyFn activelyEvict bool // We use this instead of time.Now() in order to make testing easier timeSource clock.TimeSource logger log.Logger metricsScope metrics.Scope warnOnce sync.Once errorOnce sync.Once } iteratorImpl struct { lru *lru createTime time.Time nextItem *list.Element } entryImpl struct { key interface{} createTime time.Time value interface{} refCount int } ) // Close closes the iterator func (it *iteratorImpl) Close() { it.lru.mut.Unlock() } // HasNext return true if there is more items to be returned func (it *iteratorImpl) HasNext() bool { return it.nextItem != nil } // Next return the next item func (it *iteratorImpl) Next() Entry { if it.nextItem == nil { panic("LRU cache iterator Next called when there is no next item") } entry := it.nextItem.Value.(*entryImpl) it.nextItem = it.nextItem.Next() // make a copy of the entry so there will be no concurrent access to this entry entry = &entryImpl{ key: entry.key, value: entry.value, createTime: entry.createTime, } it.prepareNext() return entry } func (it *iteratorImpl) prepareNext() { for it.nextItem != nil { entry := it.nextItem.Value.(*entryImpl) if it.lru.isEntryExpired(entry, it.createTime) { nextItem := it.nextItem.Next() it.lru.deleteInternal(it.nextItem) it.nextItem = nextItem } else { return } } } // Iterator returns an iterator to the map. This map // does not use re-entrant locks, so access or modification // to the map during iteration can cause a dead lock. func (c *lru) Iterator() Iterator { c.mut.Lock() iterator := &iteratorImpl{ lru: c, createTime: c.timeSource.Now(), nextItem: c.byAccess.Front(), } iterator.prepareNext() return iterator } func (entry *entryImpl) Key() interface{} { return entry.key } func (entry *entryImpl) Value() interface{} { return entry.value } func (entry *entryImpl) CreateTime() time.Time { return entry.createTime } // New creates a new cache with the given options func New(opts *Options) Cache { if opts == nil || (opts.MaxCount <= 0 && opts.MaxSize() <= 0) { panic("Either MaxCount (count based) or " + "MaxSize must be provided for the LRU cache") } timeSource := opts.TimeSource if timeSource == nil { timeSource = clock.NewRealTimeSource() } cache := &lru{ byAccess: list.New(), byKey: make(map[interface{}]*list.Element, opts.InitialCapacity), ttl: opts.TTL, pin: opts.Pin, rmFunc: opts.RemovedFunc, activelyEvict: opts.ActivelyEvict, timeSource: timeSource, logger: opts.Logger, isSizeBased: opts.IsSizeBased, metricsScope: opts.MetricsScope, } if cache.logger == nil { cache.logger = log.NewNoop() } if cache.metricsScope == nil { cache.metricsScope = metrics.NoopScope } if opts.IsSizeBased == nil { cache.isSizeBased = dynamicproperties.GetBoolPropertyFn(false) } else { cache.isSizeBased = opts.IsSizeBased } cache.sizeFunc = opts.GetCacheItemSizeFunc cache.maxSize = opts.MaxSize if cache.maxSize == nil { // If maxSize is not defined for size-based cache, set default to cacheCountLimit cache.maxSize = dynamicproperties.GetIntPropertyFn(cacheDefaultSizeLimit) } cache.sizeByKey = make(map[interface{}]uint64, opts.InitialCapacity) cache.maxCount = opts.MaxCount cache.logger.Info("LRU cache initialized", tag.Value(map[string]interface{}{ "isSizeBased": cache.isSizeBased(), "maxCount": cache.maxCount, "maxSize": cache.maxSize(), }), ) return cache } // Get retrieves the value stored under the given key func (c *lru) Get(key interface{}) interface{} { c.mut.Lock() defer c.mut.Unlock() c.evictExpiredItems() element := c.byKey[key] if element == nil { c.metricsScope.IncCounter(metrics.BaseCacheMiss) return nil } entry := element.Value.(*entryImpl) if c.isEntryExpired(entry, c.timeSource.Now()) { // Entry has expired c.deleteInternal(element) c.metricsScope.IncCounter(metrics.BaseCacheMiss) return nil } if c.pin { entry.refCount++ } c.byAccess.MoveToFront(element) c.metricsScope.IncCounter(metrics.BaseCacheHit) return entry.value } // Put puts a new value associated with a given key, returning the existing value (if present) func (c *lru) Put(key interface{}, value interface{}) interface{} { if c.pin { panic("Cannot use Put API in Pin mode. Use Delete and PutIfNotExist if necessary") } val, _ := c.putInternal(key, value, true) return val } // PutIfNotExist puts a value associated with a given key if it does not exist func (c *lru) PutIfNotExist(key interface{}, value interface{}) (interface{}, error) { existing, err := c.putInternal(key, value, false) if err != nil { return nil, err } if existing == nil { // This is a new value return value, err } return existing, err } // Delete deletes a key, value pair associated with a key func (c *lru) Delete(key interface{}) { c.mut.Lock() defer c.mut.Unlock() c.evictExpiredItems() element := c.byKey[key] if element != nil { c.deleteInternal(element) } } // Release decrements the ref count of a pinned element. func (c *lru) Release(key interface{}) { c.mut.Lock() defer c.mut.Unlock() elt, ok := c.byKey[key] if !ok { return } entry := elt.Value.(*entryImpl) entry.refCount-- } // Size returns the number of entries currently in the lru, useful if cache is not full func (c *lru) Size() int { c.mut.Lock() defer c.mut.Unlock() c.evictExpiredItems() return len(c.byKey) } // evictExpiredItems evicts all items in the cache which are expired func (c *lru) evictExpiredItems() { if !c.activelyEvict { return // do nothing if activelyEvict is not set } now := c.timeSource.Now() for elt := c.byAccess.Back(); elt != nil; elt = c.byAccess.Back() { if !c.isEntryExpired(elt.Value.(*entryImpl), now) { // List is sorted by item age, so we can stop as soon as we found first non expired item. break } c.deleteInternal(elt) } } // Put puts a new value associated with a given key, returning the existing value (if present) // allowUpdate flag is used to control overwrite behavior if the value exists func (c *lru) putInternal(key interface{}, value interface{}, allowUpdate bool) (interface{}, error) { valueSize := uint64(1) sizeableValue, ok := value.(Sizeable) c.mut.Lock() defer c.mut.Unlock() if !ok { c.warnOnce.Do(func() { c.logger.Warn(fmt.Sprintf("Cache is strictly count-based because value %T does not implement sizable", value)) }) } else { valueSize = sizeableValue.ByteSize() } c.evictExpiredItems() element := c.byKey[key] if element != nil { entry := element.Value.(*entryImpl) if c.isEntryExpired(entry, c.timeSource.Now()) { // Entry has expired c.deleteInternal(element) } else { // replace the value existing := entry.value if allowUpdate { if c.isCacheFull() { c.metricsScope.IncCounter(metrics.BaseCacheFullCounter) } for c.isCacheFull() { // Find the oldest unpinned item to evict oldest := c.byAccess.Back() for oldest != nil { entry := oldest.Value.(*entryImpl) if entry.refCount == 0 { // Found an unpinned item, evict it c.deleteInternal(oldest) c.metricsScope.IncCounter(metrics.BaseCacheEvictCounter) break } oldest = oldest.Prev() } if oldest == nil { // All items are pinned, can't evict anything return existing, ErrCacheFull } } c.updateSizeOnDelete(key) c.updateSizeOnAdd(key, valueSize) entry.value = value if c.ttl != 0 { entry.createTime = c.timeSource.Now() } } c.byAccess.MoveToFront(element) if c.pin { entry.refCount++ } return existing, nil } } // add the value if possible entry := &entryImpl{ key: key, value: value, } if c.pin { entry.refCount++ } if c.ttl != 0 { entry.createTime = c.timeSource.Now() } // ensuring that the cache has at least one spot for the new entry // different logic between count and size approach if c.isSizeBased() { if valueSize > uint64(c.maxSize()) { // value is too big to be cached, we also don't want to evict everyone else // TODO: we should handle this logic in the caller return nil, ErrEntryTooBig } c.byKey[key] = c.byAccess.PushFront(entry) c.updateSizeOnAdd(key, valueSize) if c.isCacheFull() { c.metricsScope.IncCounter(metrics.BaseCacheFullCounter) } for c.isCacheFull() { // Find the oldest unpinned item to evict oldest := c.byAccess.Back() for oldest != nil { entry := oldest.Value.(*entryImpl) if entry.refCount == 0 { // Found an unpinned item, evict it c.deleteInternal(oldest) c.metricsScope.IncCounter(metrics.BaseCacheEvictCounter) break } oldest = oldest.Prev() } if oldest == nil { // All items are pinned, can't evict anything c.deleteInternal(c.byAccess.Front()) return nil, ErrCacheFull } } } else { c.byKey[key] = c.byAccess.PushFront(entry) c.updateSizeOnAdd(key, valueSize) if c.isCacheFull() { c.metricsScope.IncCounter(metrics.BaseCacheFullCounter) } for c.isCacheFull() { // Find the oldest unpinned item to evict oldest := c.byAccess.Back() for oldest != nil { entry := oldest.Value.(*entryImpl) if entry.refCount <= 0 { // Found an unpinned item, evict it c.deleteInternal(oldest) c.metricsScope.IncCounter(metrics.BaseCacheEvictCounter) break } oldest = oldest.Prev() } if oldest == nil { // All items are pinned, can't evict anything c.deleteInternal(c.byAccess.Front()) return nil, ErrCacheFull } } } return nil, nil } func (c *lru) deleteInternal(element *list.Element) { entry := c.byAccess.Remove(element).(*entryImpl) if c.rmFunc != nil { go c.rmFunc(entry.value) } delete(c.byKey, entry.key) c.updateSizeOnDelete(entry.key) } func (c *lru) isEntryExpired(entry *entryImpl, currentTime time.Time) bool { return entry.refCount == 0 && !entry.createTime.IsZero() && currentTime.After(entry.createTime.Add(c.ttl)) } func (c *lru) isCacheFull() bool { count := len(c.byKey) if c.isSizeBased() { if c.maxSize() == 0 { // we don't want to stop caching if maxSize is misconfigured to 0, we will use cacheDefaultSizeLimit instead // BUT we need to warn users for this config c.errorOnce.Do(func() { c.logger.Error(fmt.Sprintf("Cache size is misconfigured to 0 for value type %T, please fix config", c.byKey[0].Value.(*entryImpl).value)) }) return c.currSize > uint64(cacheDefaultSizeLimit) || count > cacheCountLimit } return c.currSize > uint64(c.maxSize()) || count > cacheCountLimit } return count > c.maxCount || count > cacheCountLimit } func (c *lru) updateSizeOnAdd(key interface{}, valueSize uint64) { c.sizeByKey[key] = valueSize // the int overflow should not happen here c.currSize += uint64(valueSize) c.emitSizeOnUpdate() } func (c *lru) updateSizeOnDelete(key interface{}) { c.currSize -= uint64(c.sizeByKey[key]) c.emitSizeOnUpdate() delete(c.sizeByKey, key) } func (c *lru) emitSizeOnUpdate() { c.metricsScope.UpdateGauge(metrics.BaseCacheByteSize, float64(c.currSize)) c.metricsScope.UpdateGauge(metrics.BaseCacheByteSizeLimitGauge, float64(c.maxSize())) c.metricsScope.UpdateGauge(metrics.BaseCacheCount, float64(len(c.byKey))) c.metricsScope.UpdateGauge(metrics.BaseCacheCountLimitGauge, float64(c.maxCount)) } ================================================ FILE: common/cache/lru_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cache import ( "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) type keyType struct { dummyString string dummyInt int } func TestLRU(t *testing.T) { cache := New(&Options{MaxCount: 5}) cache.Put("A", "Foo") assert.Equal(t, "Foo", cache.Get("A")) assert.Nil(t, cache.Get("B")) assert.Equal(t, 1, cache.Size()) cache.Put("B", "Bar") cache.Put("C", "Cid") cache.Put("D", "Delt") assert.Equal(t, 4, cache.Size()) assert.Equal(t, "Bar", cache.Get("B")) assert.Equal(t, "Cid", cache.Get("C")) assert.Equal(t, "Delt", cache.Get("D")) cache.Put("A", "Foo2") assert.Equal(t, "Foo2", cache.Get("A")) cache.Put("E", "Epsi") assert.Equal(t, "Epsi", cache.Get("E")) assert.Equal(t, "Foo2", cache.Get("A")) // Access C, D is now LRU cache.Get("C") cache.Put("F", "Felp") assert.Nil(t, cache.Get("B")) assert.Equal(t, 5, cache.Size()) cache.Delete("C") assert.Nil(t, cache.Get("C")) } func TestGenerics(t *testing.T) { key := keyType{ dummyString: "some random key", dummyInt: 59, } value := "some random value" cache := New(&Options{MaxCount: 5}) cache.Put(key, value) assert.Equal(t, value, cache.Get(key)) assert.Equal(t, value, cache.Get(keyType{ dummyString: "some random key", dummyInt: 59, })) assert.Nil(t, cache.Get(keyType{ dummyString: "some other random key", dummyInt: 56, })) } func TestLRUWithTTL(t *testing.T) { mockTimeSource := clock.NewMockedTimeSourceAt(time.UnixMilli(0)) cache := New(&Options{ MaxCount: 5, TTL: time.Millisecond * 100, TimeSource: mockTimeSource, }).(*lru) cache.Put("A", "foo") assert.Equal(t, "foo", cache.Get("A")) mockTimeSource.Advance(time.Millisecond * 300) assert.Nil(t, cache.Get("A")) assert.Equal(t, 0, cache.Size()) } func TestLRUCacheConcurrentAccess(t *testing.T) { cache := New(&Options{MaxCount: 5}) values := map[string]string{ "A": "foo", "B": "bar", "C": "zed", "D": "dank", "E": "ezpz", } for k, v := range values { cache.Put(k, v) } start := make(chan struct{}) var wg sync.WaitGroup for i := 0; i < 20; i++ { wg.Add(2) // concurrent get and put go func() { defer wg.Done() <-start for j := 0; j < 1000; j++ { cache.Get("A") cache.Put("A", "fooo") } }() // concurrent iteration go func() { defer wg.Done() <-start for j := 0; j < 50; j++ { var result []Entry it := cache.Iterator() for it.HasNext() { entry := it.Next() result = append(result, entry) //nolint:staticcheck } it.Close() } }() } close(start) wg.Wait() } func TestRemoveFunc(t *testing.T) { ch := make(chan bool) cache := New(&Options{ MaxCount: 5, RemovedFunc: func(i interface{}) { _, ok := i.(*testing.T) assert.True(t, ok) ch <- true }, }) cache.Put("testing", t) cache.Delete("testing") assert.Nil(t, cache.Get("testing")) timeout := time.NewTimer(time.Millisecond * 300) select { case b := <-ch: assert.True(t, b) case <-timeout.C: t.Error("RemovedFunc did not send true on channel ch") } } func TestRemovedFuncWithTTL(t *testing.T) { ch := make(chan bool) mockTimeSource := clock.NewMockedTimeSourceAt(time.UnixMilli(0)) cache := New(&Options{ MaxCount: 5, TTL: time.Millisecond * 50, RemovedFunc: func(i interface{}) { _, ok := i.(*testing.T) assert.True(t, ok) ch <- true }, TimeSource: mockTimeSource, }).(*lru) cache.Put("A", t) assert.Equal(t, t, cache.Get("A")) mockTimeSource.Advance(time.Millisecond * 100) assert.Nil(t, cache.Get("A")) select { case b := <-ch: assert.True(t, b) case <-mockTimeSource.After(100 * time.Millisecond): t.Error("RemovedFunc did not send true on channel ch") } } func TestRemovedFuncWithTTL_Pin(t *testing.T) { ch := make(chan bool) mockTimeSource := clock.NewMockedTimeSourceAt(time.UnixMilli(0)) cache := New(&Options{ MaxCount: 5, TTL: time.Millisecond * 50, Pin: true, RemovedFunc: func(i interface{}) { _, ok := i.(*testing.T) assert.True(t, ok) ch <- true }, TimeSource: mockTimeSource, }).(*lru) _, err := cache.PutIfNotExist("A", t) assert.NoError(t, err) assert.Equal(t, t, cache.Get("A")) mockTimeSource.Advance(time.Millisecond * 100) assert.Equal(t, t, cache.Get("A")) // release 3 time since put if not exist also increase the counter cache.Release("A") cache.Release("A") cache.Release("A") assert.Nil(t, cache.Get("A")) select { case b := <-ch: assert.True(t, b) case <-mockTimeSource.After(300 * time.Millisecond): t.Error("RemovedFunc did not send true on channel ch") } } func TestIterator(t *testing.T) { expected := map[string]string{ "A": "Alpha", "B": "Beta", "G": "Gamma", "D": "Delta", } cache := New(&Options{MaxCount: 5}) for k, v := range expected { cache.Put(k, v) } actual := map[string]string{} it := cache.Iterator() for it.HasNext() { entry := it.Next() actual[entry.Key().(string)] = entry.Value().(string) } it.Close() assert.Equal(t, expected, actual) it = cache.Iterator() for i := 0; i < len(expected); i++ { entry := it.Next() actual[entry.Key().(string)] = entry.Value().(string) } it.Close() assert.Equal(t, expected, actual) } // Move the struct definition and method outside the test function type sizeableValue struct { val string size uint64 } func (s sizeableValue) ByteSize() uint64 { return s.size } func TestLRU_SizeBased_SizeExceeded(t *testing.T) { cache := New(&Options{ IsSizeBased: dynamicproperties.GetBoolPropertyFn(true), MaxSize: dynamicproperties.GetIntPropertyFn(15), }) fooValue := sizeableValue{val: "Foo", size: 5} cache.Put("A", fooValue) assert.Equal(t, fooValue, cache.Get("A")) assert.Nil(t, cache.Get("B")) assert.Equal(t, 1, cache.Size()) barValue := sizeableValue{val: "Bar", size: 5} cidValue := sizeableValue{val: "Cid", size: 5} deltValue := sizeableValue{val: "Delt", size: 5} cache.Put("B", barValue) cache.Put("C", cidValue) cache.Put("D", deltValue) assert.Nil(t, cache.Get("A")) assert.Equal(t, 3, cache.Size()) assert.Equal(t, barValue, cache.Get("B")) assert.Equal(t, cidValue, cache.Get("C")) assert.Equal(t, deltValue, cache.Get("D")) foo2Value := sizeableValue{val: "Foo2", size: 5} cache.Put("A", foo2Value) assert.Equal(t, foo2Value, cache.Get("A")) assert.Nil(t, cache.Get("B")) assert.Equal(t, 3, cache.Size()) // Put large value to evict the rest in a loop epsiValue := sizeableValue{val: "Epsi", size: 15} cache.Put("E", epsiValue) assert.Nil(t, cache.Get("C")) assert.Equal(t, epsiValue, cache.Get("E")) assert.Nil(t, cache.Get("A")) assert.Equal(t, 1, cache.Size()) // Put large value greater than maxSize but should not evict anything mepsiValue := sizeableValue{val: "Mepsi", size: 25} cache.Put("M", mepsiValue) assert.Nil(t, cache.Get("M")) assert.Equal(t, 1, cache.Size()) } func TestLRU_SizeBased_CountExceeded(t *testing.T) { cache := New(&Options{ MaxCount: 5, IsSizeBased: dynamicproperties.GetBoolPropertyFn(true), MaxSize: dynamicproperties.GetIntPropertyFn(10000), }) fooValue := sizeableValue{val: "Foo", size: 5} cache.Put("A", fooValue) assert.Equal(t, fooValue, cache.Get("A")) assert.Nil(t, cache.Get("B")) assert.Equal(t, 1, cache.Size()) barValue := sizeableValue{val: "Bar", size: 5} cidValue := sizeableValue{val: "Cid", size: 5} deltValue := sizeableValue{val: "Delt", size: 5} cache.Put("B", barValue) cache.Put("C", cidValue) cache.Put("D", deltValue) assert.Equal(t, 4, cache.Size()) assert.Equal(t, barValue, cache.Get("B")) assert.Equal(t, cidValue, cache.Get("C")) assert.Equal(t, deltValue, cache.Get("D")) foo2Value := sizeableValue{val: "Foo2", size: 5} cache.Put("A", foo2Value) assert.Equal(t, foo2Value, cache.Get("A")) assert.Equal(t, 4, cache.Size()) epsiValue := sizeableValue{val: "Epsi", size: 5} cache.Put("E", epsiValue) assert.Equal(t, barValue, cache.Get("B")) assert.Equal(t, epsiValue, cache.Get("E")) assert.Equal(t, foo2Value, cache.Get("A")) assert.Equal(t, 5, cache.Size()) } func TestLRU_EvictWhileSwitchToSizeBased(t *testing.T) { sizeBased := true // Create a function literal that can be implicitly coerced to BoolPropertyFn cache := New(&Options{ MaxCount: 2, IsSizeBased: func(...dynamicproperties.FilterOption) bool { return sizeBased }, MaxSize: dynamicproperties.GetIntPropertyFn(10000), }) fooValue := sizeableValue{val: "Foo", size: 5} barValue := sizeableValue{val: "Bar", size: 5} cidValue := sizeableValue{val: "Cid", size: 5} deltValue := sizeableValue{val: "Delt", size: 5} cache.Put("A", fooValue) cache.Put("B", barValue) cache.Put("C", cidValue) cache.Put("D", deltValue) assert.Equal(t, 4, cache.Size()) assert.Equal(t, fooValue, cache.Get("A")) assert.Equal(t, barValue, cache.Get("B")) assert.Equal(t, cidValue, cache.Get("C")) assert.Equal(t, deltValue, cache.Get("D")) // Change the sizeBased flag to false sizeBased = false echoValue := sizeableValue{val: "Echo", size: 5} cache.Put("E", echoValue) assert.Equal(t, deltValue, cache.Get("D")) assert.Equal(t, echoValue, cache.Get("E")) assert.Equal(t, 2, cache.Size()) } func TestPanicMaxCountAndSizeNotProvided(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("The LRU was initialized without panic") } }() New(&Options{ TTL: time.Millisecond * 100, GetCacheItemSizeFunc: func(interface{}) uint64 { return 5 }, }) } func TestPanicMaxCountAndSizeFuncNotProvided(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("The LRU was initialized without panic") } }() New(&Options{ TTL: time.Millisecond * 100, MaxSize: dynamicproperties.GetIntPropertyFn(0), }) } func TestPanicOptionsIsNil(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("The LRU was initialized without panic") } }() New(nil) } func TestEvictItemsPastTimeToLive_ActivelyEvict(t *testing.T) { // Create the cache with a TTL of 75s mockTimeSource := clock.NewMockedTimeSourceAt(time.UnixMilli(0)) cache, ok := New(&Options{ MaxCount: 5, TTL: time.Second * 75, ActivelyEvict: true, TimeSource: mockTimeSource, }).(*lru) require.True(t, ok) _, err := cache.PutIfNotExist("A", 1) require.NoError(t, err) _, err = cache.PutIfNotExist("B", 2) require.NoError(t, err) // Nothing is expired after 50s mockTimeSource.Advance(time.Second * 50) assert.Equal(t, 2, cache.Size()) _, err = cache.PutIfNotExist("C", 3) require.NoError(t, err) _, err = cache.PutIfNotExist("D", 4) require.NoError(t, err) // No time has passed, so still nothing is expired assert.Equal(t, 4, cache.Size()) // Advance time to 100s, so A and B should be expired mockTimeSource.Advance(time.Second * 50) assert.Equal(t, 2, cache.Size()) // Advance time to 150s, so C and D should be expired as well mockTimeSource.Advance(time.Second * 50) assert.Equal(t, 0, cache.Size()) } func TestLRU_PutInternal_EvictUnpinnedInMiddle(t *testing.T) { cache, ok := New(&Options{ MaxCount: 5, Pin: true, }).(*lru) require.True(t, ok) _, err := cache.PutIfNotExist("A", "Alpha") require.NoError(t, err) _, err = cache.PutIfNotExist("B", "Beta") require.NoError(t, err) _, err = cache.PutIfNotExist("C", "Charlie") require.NoError(t, err) _, err = cache.PutIfNotExist("D", "Delta") require.NoError(t, err) _, err = cache.PutIfNotExist("E", "Echo") require.NoError(t, err) // Verify all items are present assert.Equal(t, "Alpha", cache.Get("A")) assert.Equal(t, "Beta", cache.Get("B")) assert.Equal(t, "Charlie", cache.Get("C")) assert.Equal(t, "Delta", cache.Get("D")) assert.Equal(t, "Echo", cache.Get("E")) // release C and D twice cache.Release("C") cache.Release("C") cache.Release("D") cache.Release("D") // Try to add a new item - should evict unpinned items C and D _, err = cache.PutIfNotExist("F", "Foxtrot") require.NoError(t, err) // Verify pinned items are still present assert.Equal(t, "Alpha", cache.Get("A")) assert.Equal(t, "Beta", cache.Get("B")) assert.Equal(t, "Echo", cache.Get("E")) assert.Equal(t, "Foxtrot", cache.Get("F")) // Verify only C was evicted assert.Nil(t, cache.Get("C")) assert.Equal(t, "Delta", cache.Get("D")) } func TestLRU_PutInternal_EvictUnpinnedFront(t *testing.T) { cache, ok := New(&Options{ MaxCount: 5, Pin: true, }).(*lru) require.True(t, ok) _, err := cache.PutIfNotExist("A", "Alpha") require.NoError(t, err) _, err = cache.PutIfNotExist("B", "Beta") require.NoError(t, err) _, err = cache.PutIfNotExist("C", "Charlie") require.NoError(t, err) _, err = cache.PutIfNotExist("D", "Delta") require.NoError(t, err) _, err = cache.PutIfNotExist("E", "Echo") require.NoError(t, err) // Verify all items are present assert.Equal(t, "Alpha", cache.Get("A")) assert.Equal(t, "Beta", cache.Get("B")) assert.Equal(t, "Delta", cache.Get("D")) assert.Equal(t, "Echo", cache.Get("E")) // move C to the front assert.Equal(t, "Charlie", cache.Get("C")) // release C twice cache.Release("C") cache.Release("C") // Try to add a new item - should evict unpinned item C _, err = cache.PutIfNotExist("F", "Foxtrot") require.NoError(t, err) // Verify pinned items are still present assert.Equal(t, "Foxtrot", cache.Get("F")) assert.Equal(t, "Alpha", cache.Get("A")) assert.Equal(t, "Beta", cache.Get("B")) assert.Equal(t, "Delta", cache.Get("D")) assert.Equal(t, "Echo", cache.Get("E")) // Verify only C was evicted assert.Nil(t, cache.Get("C")) } func TestLRU_PutInternal_EvictUnpinnedBack(t *testing.T) { cache, ok := New(&Options{ MaxCount: 5, Pin: true, }).(*lru) require.True(t, ok) _, err := cache.PutIfNotExist("A", "Alpha") require.NoError(t, err) _, err = cache.PutIfNotExist("B", "Beta") require.NoError(t, err) _, err = cache.PutIfNotExist("C", "Charlie") require.NoError(t, err) _, err = cache.PutIfNotExist("D", "Delta") require.NoError(t, err) _, err = cache.PutIfNotExist("E", "Echo") require.NoError(t, err) // Verify all items are present assert.Equal(t, "Alpha", cache.Get("A")) assert.Equal(t, "Beta", cache.Get("B")) assert.Equal(t, "Charlie", cache.Get("C")) assert.Equal(t, "Delta", cache.Get("D")) assert.Equal(t, "Echo", cache.Get("E")) // release A twice cache.Release("A") cache.Release("A") // Try to add a new item - should evict unpinned item A _, err = cache.PutIfNotExist("F", "Foxtrot") require.NoError(t, err) // Verify pinned items are still present assert.Equal(t, "Foxtrot", cache.Get("F")) assert.Equal(t, "Beta", cache.Get("B")) assert.Equal(t, "Charlie", cache.Get("C")) assert.Equal(t, "Delta", cache.Get("D")) assert.Equal(t, "Echo", cache.Get("E")) // Verify A was evicted assert.Nil(t, cache.Get("A")) } func TestLRU_PutInternal_AllPinned(t *testing.T) { cache, ok := New(&Options{ MaxCount: 5, Pin: true, }).(*lru) assert.True(t, ok) _, err := cache.PutIfNotExist("A", "Alpha") assert.NoError(t, err) _, err = cache.PutIfNotExist("B", "Beta") assert.NoError(t, err) _, err = cache.PutIfNotExist("C", "Charlie") assert.NoError(t, err) _, err = cache.PutIfNotExist("D", "Delta") assert.NoError(t, err) _, err = cache.PutIfNotExist("E", "Echo") assert.NoError(t, err) // Verify all items are present assert.Equal(t, "Alpha", cache.Get("A")) assert.Equal(t, "Beta", cache.Get("B")) assert.Equal(t, "Charlie", cache.Get("C")) assert.Equal(t, "Delta", cache.Get("D")) assert.Equal(t, "Echo", cache.Get("E")) // Try to add a new item - should not evict anything _, err = cache.PutIfNotExist("F", "Foxtrot") assert.Error(t, err) // Verify pinned items are still present assert.Equal(t, "Alpha", cache.Get("A")) assert.Equal(t, "Beta", cache.Get("B")) assert.Equal(t, "Charlie", cache.Get("C")) assert.Equal(t, "Delta", cache.Get("D")) assert.Equal(t, "Echo", cache.Get("E")) // Verify F was never added assert.Nil(t, cache.Get("F")) } func TestLRU_PutInternal_Unpinned(t *testing.T) { cache, ok := New(&Options{ MaxCount: 5, Pin: false, }).(*lru) assert.True(t, ok) _, err := cache.PutIfNotExist("A", "Alpha") assert.NoError(t, err) _, err = cache.PutIfNotExist("B", "Beta") assert.NoError(t, err) _, err = cache.PutIfNotExist("C", "Charlie") assert.NoError(t, err) _, err = cache.PutIfNotExist("D", "Delta") assert.NoError(t, err) _, err = cache.PutIfNotExist("E", "Echo") assert.NoError(t, err) // Verify all items are present assert.Equal(t, "Alpha", cache.Get("A")) assert.Equal(t, "Beta", cache.Get("B")) assert.Equal(t, "Charlie", cache.Get("C")) assert.Equal(t, "Delta", cache.Get("D")) assert.Equal(t, "Echo", cache.Get("E")) // Try to add a new item - should just evict A _, err = cache.PutIfNotExist("F", "Foxtrot") assert.NoError(t, err) assert.Equal(t, "Foxtrot", cache.Get("F")) assert.Equal(t, "Beta", cache.Get("B")) assert.Equal(t, "Charlie", cache.Get("C")) assert.Equal(t, "Delta", cache.Get("D")) assert.Equal(t, "Echo", cache.Get("E")) // Verify A was evicted assert.Nil(t, cache.Get("A")) } ================================================ FILE: common/cache/metricsScopeCache.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cache import ( "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/metrics" ) const flushBufferedMetricsScopeDuration = 10 * time.Second type ( metricsScopeMap map[string]map[metrics.ScopeIdx]metrics.Scope buffer struct { sync.RWMutex bufferMap metricsScopeMap } domainMetricsScopeCache struct { status int32 buffer *buffer cache atomic.Value closeCh chan struct{} flushDuration time.Duration timeSource clock.TimeSource } ) // NewDomainMetricsScopeCache constructs a new domainMetricsScopeCache func NewDomainMetricsScopeCache() DomainMetricsScopeCache { mc := &domainMetricsScopeCache{ buffer: &buffer{ bufferMap: make(metricsScopeMap), }, closeCh: make(chan struct{}), flushDuration: flushBufferedMetricsScopeDuration, timeSource: clock.NewRealTimeSource(), } mc.cache.Store(make(metricsScopeMap)) return mc } func (c *domainMetricsScopeCache) flushBufferedMetricsScope(flushDuration time.Duration) { ticker := c.timeSource.NewTicker(flushDuration) defer ticker.Stop() for { select { case <-ticker.Chan(): c.buffer.Lock() if len(c.buffer.bufferMap) > 0 { scopeMap := make(metricsScopeMap) data := c.cache.Load().(metricsScopeMap) // Copy everything over after atomic load for key, val := range data { scopeMap[key] = map[metrics.ScopeIdx]metrics.Scope{} for k, v := range val { scopeMap[key][k] = v } } // Copy from buffered array for key, val := range c.buffer.bufferMap { if _, ok := scopeMap[key]; !ok { scopeMap[key] = map[metrics.ScopeIdx]metrics.Scope{} } for k, v := range val { scopeMap[key][k] = v } } c.cache.Store(scopeMap) c.buffer.bufferMap = make(metricsScopeMap) } c.buffer.Unlock() case <-c.closeCh: return } } } // Get retrieves scope for domainID and scopeIdx func (c *domainMetricsScopeCache) Get(domainID string, scopeIdx metrics.ScopeIdx) (metrics.Scope, bool) { data := c.cache.Load().(metricsScopeMap) if data == nil { return nil, false } m, ok := data[domainID] if !ok { return nil, false } metricsScope, ok := m[scopeIdx] return metricsScope, ok } // Put puts map of domainID and scopeIdx to metricsScope func (c *domainMetricsScopeCache) Put(domainID string, scopeIdx metrics.ScopeIdx, scope metrics.Scope) { c.buffer.Lock() defer c.buffer.Unlock() if c.buffer.bufferMap[domainID] == nil { c.buffer.bufferMap[domainID] = map[metrics.ScopeIdx]metrics.Scope{} } c.buffer.bufferMap[domainID][scopeIdx] = scope } func (c *domainMetricsScopeCache) Start() { if !atomic.CompareAndSwapInt32(&c.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } go c.flushBufferedMetricsScope(c.flushDuration) } func (c *domainMetricsScopeCache) Stop() { if !atomic.CompareAndSwapInt32(&c.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(c.closeCh) } ================================================ FILE: common/cache/metricsScopeCache_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cache import ( "sync" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "github.com/uber/cadence/common/metrics" ) type domainMetricsCacheSuite struct { suite.Suite *require.Assertions metricsClient metrics.Client metricsCache DomainMetricsScopeCache } func TestDomainMetricsCacheSuite(t *testing.T) { s := new(domainMetricsCacheSuite) suite.Run(t, s) } func (s *domainMetricsCacheSuite) SetupTest() { s.Assertions = require.New(s.T()) s.metricsClient = metrics.NewClient(tally.NoopScope, metrics.Frontend, metrics.HistogramMigration{}) metricsCache := NewDomainMetricsScopeCache().(*domainMetricsScopeCache) metricsCache.flushDuration = 100 * time.Millisecond s.metricsCache = metricsCache s.metricsCache.Start() } func (s *domainMetricsCacheSuite) TearDownTest() { s.metricsCache.Stop() } func (s *domainMetricsCacheSuite) TestGetMetricsScope() { var found bool tests := []struct { scopeID metrics.ScopeIdx domainID string }{ {1, "A"}, {2, "B"}, {1, "C"}, } for _, t := range tests { mockMetricsScope := s.metricsClient.Scope(t.scopeID) s.metricsCache.Put(t.domainID, t.scopeID, mockMetricsScope) } time.Sleep(110 * time.Millisecond) metricsScope, found := s.metricsCache.Get("A", 1) testMetricsScope := s.metricsClient.Scope(1) s.Equal(testMetricsScope, metricsScope) s.Equal(found, true) _, found = s.metricsCache.Get("B", 2) s.Equal(found, true) metricsScope, found = s.metricsCache.Get("C", 1) testMetricsScope = s.metricsClient.Scope(3) s.NotEqual(testMetricsScope, metricsScope) s.Equal(found, true) metricsScope, found = s.metricsCache.Get("D", 3) testMetricsScope = s.metricsClient.Scope(3) s.NotEqual(testMetricsScope, metricsScope) s.Equal(found, false) } func (s *domainMetricsCacheSuite) TestGetMetricsScopeMultipleFlushLoop() { var found bool tests := []struct { scopeID metrics.ScopeIdx domainID string }{ {1, "A"}, {2, "B"}, {1, "C"}, {5, "D"}, {3, "E"}, } for i := 0; i < 3; i++ { t := tests[i] mockMetricsScope := s.metricsClient.Scope(t.scopeID) s.metricsCache.Put(t.domainID, t.scopeID, mockMetricsScope) } time.Sleep(110 * time.Millisecond) for i := 3; i < len(tests); i++ { t := tests[i] mockMetricsScope := s.metricsClient.Scope(t.scopeID) s.metricsCache.Put(t.domainID, t.scopeID, mockMetricsScope) } metricsScope, found := s.metricsCache.Get("A", 1) testMetricsScope := s.metricsClient.Scope(1) s.Equal(testMetricsScope, metricsScope) s.Equal(found, true) _, found = s.metricsCache.Get("B", 2) s.Equal(found, true) metricsScope, found = s.metricsCache.Get("C", 1) testMetricsScope = s.metricsClient.Scope(3) s.NotEqual(testMetricsScope, metricsScope) s.Equal(found, true) metricsScope, found = s.metricsCache.Get("D", 5) testMetricsScope = s.metricsClient.Scope(5) s.NotEqual(testMetricsScope, metricsScope) s.Equal(found, false) metricsScope, found = s.metricsCache.Get("E", 3) testMetricsScope = s.metricsClient.Scope(3) s.NotEqual(testMetricsScope, metricsScope) s.Equal(found, false) time.Sleep(200 * time.Millisecond) metricsScope, found = s.metricsCache.Get("D", 5) testMetricsScope = s.metricsClient.Scope(5) s.Equal(testMetricsScope, metricsScope) s.Equal(found, true) metricsScope, found = s.metricsCache.Get("E", 3) testMetricsScope = s.metricsClient.Scope(3) s.Equal(testMetricsScope, metricsScope) s.Equal(found, true) } func (s *domainMetricsCacheSuite) TestConcurrentMetricsScopeAccess() { ch := make(chan struct{}) var wg sync.WaitGroup var metricsScope, testMetricsScope metrics.Scope var found bool for i := 0; i < 1000; i++ { wg.Add(1) // concurrent get and put go func(scopeIdx metrics.ScopeIdx) { defer wg.Done() <-ch s.metricsCache.Get("test_domain", scopeIdx) s.metricsCache.Put("test_domain", scopeIdx, s.metricsClient.Scope(metrics.ScopeIdx(int(scopeIdx)%int(metrics.NumServices)))) }(metrics.ScopeIdx(i)) } close(ch) wg.Wait() time.Sleep(120 * time.Millisecond) for i := 0; i < 1000; i++ { metricsScope, found = s.metricsCache.Get("test_domain", metrics.ScopeIdx(i)) testMetricsScope = s.metricsClient.Scope(metrics.ScopeIdx(i % int(metrics.NumServices))) s.Equal(true, found) s.Equal(testMetricsScope, metricsScope) } } ================================================ FILE: common/cache/simple.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cache import ( "container/list" "sync" "time" ) var ( // DummyCreateTime is the create time used by all entries in the cache. DummyCreateTime = time.Time{} ) type ( simple struct { sync.RWMutex accessMap map[interface{}]*list.Element iterateList *list.List rmFunc RemovedFunc } simpleItr struct { simple *simple nextItem *list.Element } simpleEntry struct { key interface{} value interface{} } ) // Close closes the iterator func (it *simpleItr) Close() { it.simple.RUnlock() } // HasNext return true if there is more items to be returned func (it *simpleItr) HasNext() bool { return it.nextItem != nil } // Next returns the next item func (it *simpleItr) Next() Entry { if it.nextItem == nil { panic("Simple cache iterator Next called when there is no next item") } entry := it.nextItem.Value.(*simpleEntry) it.nextItem = it.nextItem.Next() // make a copy of the entry so there will be no concurrent access to this entry entry = &simpleEntry{ key: entry.key, value: entry.value, } return entry } func (e *simpleEntry) Key() interface{} { return e.key } func (e *simpleEntry) Value() interface{} { return e.value } // CreateTime is not implemented for simple cache entries func (e *simpleEntry) CreateTime() time.Time { return DummyCreateTime } // NewSimple creates a new simple cache with given options. // Simple cache will never evict entries and it will never reorder the elements. // Simple cache also does not have the concept of pinning that LRU cache has. // Internally simple cache uses a RWMutex instead of the exclusive Mutex that LRU cache uses. // The RWMutex makes simple cache readable by many threads without introducing lock contention. func NewSimple(opts *SimpleOptions) Cache { if opts == nil { opts = &SimpleOptions{} } return &simple{ iterateList: list.New(), accessMap: make(map[interface{}]*list.Element, opts.InitialCapacity), rmFunc: opts.RemovedFunc, } } // Get retrieves the value stored under the given key func (c *simple) Get(key interface{}) interface{} { c.RLock() defer c.RUnlock() element := c.accessMap[key] if element == nil { return nil } return element.Value.(*simpleEntry).Value() } // Put puts a new value associated with a given key, returning the existing value (if present). func (c *simple) Put(key interface{}, value interface{}) interface{} { c.Lock() defer c.Unlock() existing := c.putInternal(key, value, true) return existing } // PutIfNotExist puts a value associated with a given key if it does not exist func (c *simple) PutIfNotExist(key interface{}, value interface{}) (interface{}, error) { c.Lock() defer c.Unlock() existing := c.putInternal(key, value, false) if existing == nil { // This is a new value return value, nil } return existing, nil } // Delete deletes a key, value pair associated with a key func (c *simple) Delete(key interface{}) { c.Lock() defer c.Unlock() element := c.accessMap[key] if element == nil { return } entry := c.iterateList.Remove(element).(*simpleEntry) if c.rmFunc != nil { go c.rmFunc(entry.value) } delete(c.accessMap, entry.key) } // Release does nothing for simple cache func (c *simple) Release(_ interface{}) {} // Size returns the number of entries currently in the cache func (c *simple) Size() int { c.RLock() defer c.RUnlock() return len(c.accessMap) } func (c *simple) Iterator() Iterator { c.RLock() iterator := &simpleItr{ simple: c, nextItem: c.iterateList.Front(), } return iterator } func (c *simple) putInternal(key interface{}, value interface{}, allowUpdate bool) interface{} { elt := c.accessMap[key] if elt != nil { entry := elt.Value.(*simpleEntry) existing := entry.value if allowUpdate { entry.value = value } return existing } entry := &simpleEntry{ key: key, value: value, } c.accessMap[key] = c.iterateList.PushFront(entry) return nil } ================================================ FILE: common/cache/simple_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cache import ( "sync" "testing" "time" "github.com/stretchr/testify/assert" ) func TestSimple(t *testing.T) { cache := NewSimple(nil) cache.Put("A", "Foo") assert.Equal(t, "Foo", cache.Get("A")) assert.Nil(t, cache.Get("B")) assert.Equal(t, 1, cache.Size()) cache.Put("B", "Bar") cache.Put("C", "Cid") cache.Put("D", "Delt") assert.Equal(t, 4, cache.Size()) assert.Equal(t, "Bar", cache.Get("B")) assert.Equal(t, "Cid", cache.Get("C")) assert.Equal(t, "Delt", cache.Get("D")) cache.Put("A", "Foo2") assert.Equal(t, "Foo2", cache.Get("A")) cache.Put("E", "Epsi") assert.Equal(t, "Epsi", cache.Get("E")) assert.Equal(t, "Foo2", cache.Get("A")) cache.Delete("A") assert.Nil(t, cache.Get("A")) } func TestSimpleGenerics(t *testing.T) { key := keyType{ dummyString: "some random key", dummyInt: 59, } value := "some random value" cache := NewSimple(nil) cache.Put(key, value) assert.Equal(t, value, cache.Get(key)) assert.Equal(t, value, cache.Get(keyType{ dummyString: "some random key", dummyInt: 59, })) assert.Nil(t, cache.Get(keyType{ dummyString: "some other random key", dummyInt: 56, })) } func TestSimpleCacheConcurrentAccess(t *testing.T) { cache := NewSimple(nil) values := map[string]string{ "A": "foo", "B": "bar", "C": "zed", "D": "dank", "E": "ezpz", } for k, v := range values { cache.Put(k, v) } start := make(chan struct{}) var wg sync.WaitGroup for i := 0; i < 20; i++ { wg.Add(2) // concurrent get and put go func() { defer wg.Done() <-start for j := 0; j < 1000; j++ { cache.Get("A") cache.Put("A", "fooo") } }() // concurrent iteration go func() { defer wg.Done() <-start for j := 0; j < 50; j++ { var result []Entry it := cache.Iterator() for it.HasNext() { entry := it.Next() result = append(result, entry) //nolint:staticcheck } it.Close() } }() } close(start) wg.Wait() } func TestSimpleRemoveFunc(t *testing.T) { ch := make(chan bool) cache := NewSimple(&SimpleOptions{ RemovedFunc: func(i interface{}) { _, ok := i.(*testing.T) assert.True(t, ok) ch <- true }, }) cache.Put("testing", t) cache.Delete("testing") assert.Nil(t, cache.Get("testing")) timeout := time.NewTimer(time.Millisecond * 300) select { case b := <-ch: assert.True(t, b) case <-timeout.C: t.Error("RemovedFunc did not send true on channel ch") } } func TestSimpleIterator(t *testing.T) { expected := map[string]string{ "A": "Alpha", "B": "Beta", "G": "Gamma", "D": "Delta", } cache := NewSimple(nil) for k, v := range expected { cache.Put(k, v) } actual := map[string]string{} it := cache.Iterator() for it.HasNext() { entry := it.Next() actual[entry.Key().(string)] = entry.Value().(string) } it.Close() assert.Equal(t, expected, actual) it = cache.Iterator() for i := 0; i < len(expected); i++ { entry := it.Next() actual[entry.Key().(string)] = entry.Value().(string) } it.Close() assert.Equal(t, expected, actual) } ================================================ FILE: common/checksum/crc.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package checksum import ( "bytes" "encoding/binary" "fmt" "hash/crc32" "github.com/uber/cadence/common/codec" ) // GenerateCRC32 generates an IEEE crc32 checksum on the // serilized byte array of the given thrift object. The // serialization proto used will be of type thriftRW func GenerateCRC32( payload codec.ThriftObject, payloadVersion int, ) (Checksum, error) { encoder := codec.NewThriftRWEncoder() payloadBytes, err := encoder.Encode(payload) if err != nil { return Checksum{}, err } crc := crc32.ChecksumIEEE(payloadBytes) checksum := make([]byte, 4) binary.BigEndian.PutUint32(checksum, crc) return Checksum{ Value: checksum, Version: payloadVersion, Flavor: FlavorIEEECRC32OverThriftBinary, }, nil } // Verify verifies that the checksum generated from the // given thrift object matches the specified expected checksum // Return ErrMismatch when checksums mismatch func Verify( payload codec.ThriftObject, checksum Checksum, ) error { if !checksum.Flavor.IsValid() || checksum.Flavor != FlavorIEEECRC32OverThriftBinary { return fmt.Errorf("unknown checksum flavor %v", checksum.Flavor) } expected, err := GenerateCRC32(payload, checksum.Version) if err != nil { return err } if !bytes.Equal(expected.Value, checksum.Value) { return ErrMismatch } return nil } ================================================ FILE: common/checksum/crc_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package checksum import ( "sync" "sync/atomic" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" ) func TestCRC32OverThrift(t *testing.T) { // note: do not use a struct with map since // iteration order is not guaranteed in Go and // so, each call to thrift encode will result in // different set of serialized bytes obj := &shared.WorkflowExecutionInfo{ Execution: &shared.WorkflowExecution{ WorkflowId: common.StringPtr(uuid.New()), RunId: common.StringPtr(uuid.New()), }, StartTime: common.Int64Ptr(time.Now().UnixNano()), HistoryLength: common.Int64Ptr(550), } parallism := 10 loopCount := 100 successCount := int64(0) startC := make(chan struct{}) doneWG := sync.WaitGroup{} doneWG.Add(parallism) for i := 0; i < parallism; i++ { go func() { defer doneWG.Done() <-startC for count := 0; count < loopCount; count++ { csum, err := GenerateCRC32(obj, 1) if err != nil { return } if err := Verify(obj, csum); err != nil { return } atomic.AddInt64(&successCount, 1) } }() } close(startC) success := common.AwaitWaitGroup(&doneWG, time.Second) assert.True(t, success, "timed out waiting for goroutines to finish") assert.Equal(t, int64(parallism*loopCount), successCount) } ================================================ FILE: common/checksum/ctc_benchmark_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package checksum import ( "testing" "time" "github.com/pborman/uuid" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" ) func BenchmarkGenerateCRC32(b *testing.B) { obj := &shared.WorkflowExecutionInfo{ Execution: &shared.WorkflowExecution{ WorkflowId: common.StringPtr(uuid.New()), RunId: common.StringPtr(uuid.New()), }, StartTime: common.Int64Ptr(time.Now().UnixNano()), HistoryLength: common.Int64Ptr(550), } b.ResetTimer() for i := 0; i < b.N; i++ { checksum, _ := GenerateCRC32(obj, 1) _ = Verify(obj, checksum) } } ================================================ FILE: common/checksum/defs.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package checksum import "errors" type ( // Checksum represents a checksum value along // with associated metadata Checksum struct { // Version represents version of the payload from Version int // which this checksum was derived Flavor Flavor // Value is the checksum value Value []byte } // Flavor is an enum type that represents the type of checksum Flavor int ) const ( // FlavorUnknown represents an unknown/uninitialized checksum flavor FlavorUnknown Flavor = iota // FlavorIEEECRC32OverThriftBinary represents crc32 checksum generated over thriftRW serialized payload FlavorIEEECRC32OverThriftBinary maxFlavors ) // ErrMismatch indicates a checksum verification failure due to // a derived checksum not being equal to expected checksum var ErrMismatch = errors.New("checksum mismatch error") // IsValid returns true if the checksum flavor is valid func (f Flavor) IsValid() bool { return f > FlavorUnknown && f < maxFlavors } ================================================ FILE: common/client/versionChecker.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination versionChecker_mock.go -self_package github.com/uber/cadence/common/client github.com/uber/cadence/common/client VersionChecker package client import ( "context" "fmt" "github.com/gogo/protobuf/jsonpb" "github.com/hashicorp/go-version" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) const ( // GoSDK is the header value for common.ClientImplHeaderName indicating a go sdk client GoSDK = "uber-go" // JavaSDK is the header value for common.ClientImplHeaderName indicating a java sdk client JavaSDK = "uber-java" // CLI is the header value for common.ClientImplHeaderName indicating a cli client CLI = "cli" // SupportedGoSDKVersion indicates the highest go sdk version server will accept requests from SupportedGoSDKVersion = "1.7.0" // SupportedJavaSDKVersion indicates the highest java sdk version server will accept requests from SupportedJavaSDKVersion = "1.5.0" // SupportedCLIVersion indicates the highest cli version server will accept requests from SupportedCLIVersion = "1.7.0" // StickyQueryUnknownImplConstraints indicates the minimum client version of an unknown client type which supports StickyQuery StickyQueryUnknownImplConstraints = "1.0.0" // GoWorkerStickyQueryVersion indicates the minimum client version of go worker which supports StickyQuery GoWorkerStickyQueryVersion = "1.0.0" // JavaWorkerStickyQueryVersion indicates the minimum client version of the java worker which supports StickyQuery JavaWorkerStickyQueryVersion = "1.0.0" // GoWorkerConsistentQueryVersion indicates the minimum client version of the go worker which supports ConsistentQuery GoWorkerConsistentQueryVersion = "1.5.0" // JavaWorkerRawHistoryQueryVersion indicates the minimum client version of the java worker which supports RawHistoryQuery JavaWorkerRawHistoryQueryVersion = "1.3.0" // JavaWorkerConsistentQueryVersion indicates the minimum client version of the java worker which supports ConsistentQuery JavaWorkerConsistentQueryVersion = "1.5.0" // GoWorkerRawHistoryQueryVersion indicates the minimum client version of the go worker which supports RawHistoryQuery GoWorkerRawHistoryQueryVersion = "1.6.0" // CLIRawHistoryQueryVersion indicates the minimum CLI version of the go worker which supports RawHistoryQuery // Note: cli uses go client feature version CLIRawHistoryQueryVersion = "1.6.0" // Go Client version that supports WorkflowExecutionAlreadyCompleted Error CLIWorkflowAlreadyCompletedVersion = "1.7.0" // Go Client version that supports WorkflowExecutionAlreadyCompleted Error GoWorkerWorkflowAlreadyCompletedVersion = "1.7.0" // Java Client version that supports WorkflowExecutionAlreadyCompleted Error JavaWorkflowAlreadyCompletedVersion = "1.4.0" stickyQuery = "sticky-query" consistentQuery = "consistent-query" rawHistoryQuery = "send-raw-workflow-history" workflowAlreadyCompletedError = "workflow-already-completed" ) var ( // ErrUnknownFeature indicates that requested feature is not known by version checker ErrUnknownFeature = &types.BadRequestError{Message: "Unknown feature"} // DefaultCLIFeatureFlags is the default FeatureFlags used by Cadence CLI DefaultCLIFeatureFlags = apiv1.FeatureFlags{ WorkflowExecutionAlreadyCompletedErrorEnabled: true, } ) type ( // VersionChecker is used to check client/server compatibility and client's capabilities VersionChecker interface { ClientSupported(ctx context.Context, enableClientVersionCheck bool) error SupportsStickyQuery(clientImpl string, clientFeatureVersion string) error SupportsConsistentQuery(clientImpl string, clientFeatureVersion string) error SupportsRawHistoryQuery(clientImpl string, clientFeatureVersion string) error SupportsWorkflowAlreadyCompletedError(clientImpl string, clientFeatureVersion string, featureFlags apiv1.FeatureFlags) error } versionChecker struct { supportedFeatures map[string]map[string]version.Constraints supportedClients map[string]version.Constraints stickyQueryUnknownImplConstraints version.Constraints } ) // FeatureFlagsHeader returns the serialized version of the FeatureFlags func FeatureFlagsHeader(featureFlags apiv1.FeatureFlags) string { var marshaler jsonpb.Marshaler serialized, err := marshaler.MarshalToString(&featureFlags) if err != nil { // fail open and return empty feature flags return "" } return serialized } // GetFeatureFlagsFromHeader returns FeatureFlags from yarpc headers func GetFeatureFlagsFromHeader(call *yarpc.Call) apiv1.FeatureFlags { featureFlagsSerialized := call.Header(common.ClientFeatureFlagsHeaderName) var featureFlags apiv1.FeatureFlags err := jsonpb.UnmarshalString(featureFlagsSerialized, &featureFlags) if err != nil { // fail open and return empty feature flags return apiv1.FeatureFlags{} } return featureFlags } // NewVersionChecker constructs a new VersionChecker func NewVersionChecker() VersionChecker { supportedFeatures := map[string]map[string]version.Constraints{ GoSDK: { stickyQuery: mustNewConstraint(fmt.Sprintf(">=%v", GoWorkerStickyQueryVersion)), consistentQuery: mustNewConstraint(fmt.Sprintf(">=%v", GoWorkerConsistentQueryVersion)), rawHistoryQuery: mustNewConstraint(fmt.Sprintf(">=%v", GoWorkerRawHistoryQueryVersion)), workflowAlreadyCompletedError: mustNewConstraint(fmt.Sprintf(">=%v", GoWorkerWorkflowAlreadyCompletedVersion)), }, JavaSDK: { stickyQuery: mustNewConstraint(fmt.Sprintf(">=%v", JavaWorkerStickyQueryVersion)), consistentQuery: mustNewConstraint(fmt.Sprintf(">=%v", JavaWorkerConsistentQueryVersion)), rawHistoryQuery: mustNewConstraint(fmt.Sprintf(">=%v", JavaWorkerRawHistoryQueryVersion)), workflowAlreadyCompletedError: mustNewConstraint(fmt.Sprintf(">=%v", JavaWorkflowAlreadyCompletedVersion)), }, CLI: { rawHistoryQuery: mustNewConstraint(fmt.Sprintf(">=%v", CLIRawHistoryQueryVersion)), workflowAlreadyCompletedError: mustNewConstraint(fmt.Sprintf(">=%v", CLIWorkflowAlreadyCompletedVersion)), }, } supportedClients := map[string]version.Constraints{ GoSDK: mustNewConstraint(fmt.Sprintf("<=%v", SupportedGoSDKVersion)), JavaSDK: mustNewConstraint(fmt.Sprintf("<=%v", SupportedJavaSDKVersion)), CLI: mustNewConstraint(fmt.Sprintf("<=%v", SupportedCLIVersion)), } return &versionChecker{ supportedFeatures: supportedFeatures, supportedClients: supportedClients, stickyQueryUnknownImplConstraints: mustNewConstraint(fmt.Sprintf(">=%v", StickyQueryUnknownImplConstraints)), } } // ClientSupported returns an error if client is unsupported, nil otherwise. // In case client version lookup fails assume the client is supported. func (vc *versionChecker) ClientSupported(ctx context.Context, enableClientVersionCheck bool) error { if !enableClientVersionCheck { return nil } call := yarpc.CallFromContext(ctx) clientFeatureVersion := call.Header(common.FeatureVersionHeaderName) clientImpl := call.Header(common.ClientImplHeaderName) if clientFeatureVersion == "" { return nil } supportedVersions, ok := vc.supportedClients[clientImpl] if !ok { return nil } version, err := version.NewVersion(clientFeatureVersion) if err != nil || !supportedVersions.Check(version) { return &types.ClientVersionNotSupportedError{FeatureVersion: clientFeatureVersion, ClientImpl: clientImpl, SupportedVersions: supportedVersions.String()} } return nil } // SupportsStickyQuery returns error if sticky query is not supported otherwise nil. // In case client version lookup fails assume the client does not support feature. func (vc *versionChecker) SupportsStickyQuery(clientImpl string, clientFeatureVersion string) error { return vc.featureSupported(clientImpl, clientFeatureVersion, stickyQuery) } // SupportsConsistentQuery returns error if consistent query is not supported otherwise nil. // In case client version lookup fails assume the client does not support feature. func (vc *versionChecker) SupportsConsistentQuery(clientImpl string, clientFeatureVersion string) error { return vc.featureSupported(clientImpl, clientFeatureVersion, consistentQuery) } // SupportsRawHistoryQuery returns error if raw history query is not supported otherwise nil. // In case client version lookup fails assume the client does not support feature. func (vc *versionChecker) SupportsRawHistoryQuery(clientImpl string, clientFeatureVersion string) error { return vc.featureSupported(clientImpl, clientFeatureVersion, rawHistoryQuery) } // Returns error if workflowAlreadyCompletedError is not supported otherwise nil. // In case client version lookup fails assume the client does not support feature. // NOTE: Enabling this error will break customer code handling the workflow errors in their workflow func (vc *versionChecker) SupportsWorkflowAlreadyCompletedError(clientImpl string, clientFeatureVersion string, featureFlags apiv1.FeatureFlags) error { if featureFlags.WorkflowExecutionAlreadyCompletedErrorEnabled { return vc.featureSupported(clientImpl, clientFeatureVersion, workflowAlreadyCompletedError) } return &shared.FeatureNotEnabledError{FeatureFlag: "WorkflowExecutionAlreadyCompletedErrorEnabled"} } func (vc *versionChecker) featureSupported(clientImpl string, clientFeatureVersion string, feature string) error { // Some older clients may not provide clientImpl. // If this is the case special handling needs to be done to maintain backwards compatibility. // This can be removed after it is sure there are no existing clients which do not provide clientImpl in RPC headers. if clientImpl == "" { switch feature { case consistentQuery: case rawHistoryQuery, workflowAlreadyCompletedError: return &types.ClientVersionNotSupportedError{FeatureVersion: clientFeatureVersion} case stickyQuery: version, err := version.NewVersion(clientFeatureVersion) if err != nil { return &types.ClientVersionNotSupportedError{FeatureVersion: clientFeatureVersion} } if !vc.stickyQueryUnknownImplConstraints.Check(version) { return &types.ClientVersionNotSupportedError{FeatureVersion: clientFeatureVersion, SupportedVersions: vc.stickyQueryUnknownImplConstraints.String()} } return nil default: return ErrUnknownFeature } } if clientFeatureVersion == "" { return &types.ClientVersionNotSupportedError{ClientImpl: clientImpl, FeatureVersion: clientFeatureVersion} } implMap, ok := vc.supportedFeatures[clientImpl] if !ok { return &types.ClientVersionNotSupportedError{ClientImpl: clientImpl, FeatureVersion: clientFeatureVersion} } supportedVersions, ok := implMap[feature] if !ok { return &types.ClientVersionNotSupportedError{ClientImpl: clientImpl, FeatureVersion: clientFeatureVersion} } version, err := version.NewVersion(clientFeatureVersion) if err != nil { return &types.ClientVersionNotSupportedError{FeatureVersion: clientFeatureVersion, ClientImpl: clientImpl, SupportedVersions: supportedVersions.String()} } if !supportedVersions.Check(version) { return &types.ClientVersionNotSupportedError{ClientImpl: clientImpl, FeatureVersion: clientFeatureVersion, SupportedVersions: supportedVersions.String()} } return nil } func mustNewConstraint(v string) version.Constraints { constraint, err := version.NewConstraint(v) if err != nil { panic("invalid version constraint " + v) } return constraint } ================================================ FILE: common/client/versionChecker_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/client (interfaces: VersionChecker) // // Generated by this command: // // mockgen -package client -destination versionChecker_mock.go -self_package github.com/uber/cadence/common/client github.com/uber/cadence/common/client VersionChecker // // Package client is a generated GoMock package. package client import ( context "context" reflect "reflect" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" gomock "go.uber.org/mock/gomock" ) // MockVersionChecker is a mock of VersionChecker interface. type MockVersionChecker struct { ctrl *gomock.Controller recorder *MockVersionCheckerMockRecorder isgomock struct{} } // MockVersionCheckerMockRecorder is the mock recorder for MockVersionChecker. type MockVersionCheckerMockRecorder struct { mock *MockVersionChecker } // NewMockVersionChecker creates a new mock instance. func NewMockVersionChecker(ctrl *gomock.Controller) *MockVersionChecker { mock := &MockVersionChecker{ctrl: ctrl} mock.recorder = &MockVersionCheckerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockVersionChecker) EXPECT() *MockVersionCheckerMockRecorder { return m.recorder } // ClientSupported mocks base method. func (m *MockVersionChecker) ClientSupported(ctx context.Context, enableClientVersionCheck bool) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ClientSupported", ctx, enableClientVersionCheck) ret0, _ := ret[0].(error) return ret0 } // ClientSupported indicates an expected call of ClientSupported. func (mr *MockVersionCheckerMockRecorder) ClientSupported(ctx, enableClientVersionCheck any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientSupported", reflect.TypeOf((*MockVersionChecker)(nil).ClientSupported), ctx, enableClientVersionCheck) } // SupportsConsistentQuery mocks base method. func (m *MockVersionChecker) SupportsConsistentQuery(clientImpl, clientFeatureVersion string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SupportsConsistentQuery", clientImpl, clientFeatureVersion) ret0, _ := ret[0].(error) return ret0 } // SupportsConsistentQuery indicates an expected call of SupportsConsistentQuery. func (mr *MockVersionCheckerMockRecorder) SupportsConsistentQuery(clientImpl, clientFeatureVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportsConsistentQuery", reflect.TypeOf((*MockVersionChecker)(nil).SupportsConsistentQuery), clientImpl, clientFeatureVersion) } // SupportsRawHistoryQuery mocks base method. func (m *MockVersionChecker) SupportsRawHistoryQuery(clientImpl, clientFeatureVersion string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SupportsRawHistoryQuery", clientImpl, clientFeatureVersion) ret0, _ := ret[0].(error) return ret0 } // SupportsRawHistoryQuery indicates an expected call of SupportsRawHistoryQuery. func (mr *MockVersionCheckerMockRecorder) SupportsRawHistoryQuery(clientImpl, clientFeatureVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportsRawHistoryQuery", reflect.TypeOf((*MockVersionChecker)(nil).SupportsRawHistoryQuery), clientImpl, clientFeatureVersion) } // SupportsStickyQuery mocks base method. func (m *MockVersionChecker) SupportsStickyQuery(clientImpl, clientFeatureVersion string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SupportsStickyQuery", clientImpl, clientFeatureVersion) ret0, _ := ret[0].(error) return ret0 } // SupportsStickyQuery indicates an expected call of SupportsStickyQuery. func (mr *MockVersionCheckerMockRecorder) SupportsStickyQuery(clientImpl, clientFeatureVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportsStickyQuery", reflect.TypeOf((*MockVersionChecker)(nil).SupportsStickyQuery), clientImpl, clientFeatureVersion) } // SupportsWorkflowAlreadyCompletedError mocks base method. func (m *MockVersionChecker) SupportsWorkflowAlreadyCompletedError(clientImpl, clientFeatureVersion string, featureFlags apiv1.FeatureFlags) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SupportsWorkflowAlreadyCompletedError", clientImpl, clientFeatureVersion, featureFlags) ret0, _ := ret[0].(error) return ret0 } // SupportsWorkflowAlreadyCompletedError indicates an expected call of SupportsWorkflowAlreadyCompletedError. func (mr *MockVersionCheckerMockRecorder) SupportsWorkflowAlreadyCompletedError(clientImpl, clientFeatureVersion, featureFlags any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportsWorkflowAlreadyCompletedError", reflect.TypeOf((*MockVersionChecker)(nil).SupportsWorkflowAlreadyCompletedError), clientImpl, clientFeatureVersion, featureFlags) } ================================================ FILE: common/client/versionChecker_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package client import ( "context" "fmt" "strings" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc" "go.uber.org/yarpc/api/encoding" "go.uber.org/yarpc/api/transport" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) type ( VersionCheckerSuite struct { *require.Assertions suite.Suite } ) func TestVersionCheckerSuite(t *testing.T) { suite.Run(t, new(VersionCheckerSuite)) } func (s *VersionCheckerSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *VersionCheckerSuite) TestClientSupported() { testCases := []struct { callContext context.Context enableClientVersionCheck bool expectErr bool }{ { enableClientVersionCheck: false, expectErr: false, }, { callContext: context.Background(), enableClientVersionCheck: true, expectErr: false, }, { callContext: s.constructCallContext("unknown-client", "0.0.0"), enableClientVersionCheck: true, expectErr: false, }, { callContext: s.constructCallContext(GoSDK, "malformed-version"), enableClientVersionCheck: true, expectErr: true, }, { callContext: s.constructCallContext(GoSDK, s.getHigherVersion(SupportedGoSDKVersion)), enableClientVersionCheck: true, expectErr: true, }, { callContext: s.constructCallContext(JavaSDK, s.getHigherVersion(SupportedJavaSDKVersion)), enableClientVersionCheck: true, expectErr: true, }, { callContext: s.constructCallContext(CLI, s.getHigherVersion(SupportedCLIVersion)), enableClientVersionCheck: true, expectErr: true, }, { callContext: s.constructCallContext(GoSDK, SupportedGoSDKVersion), enableClientVersionCheck: true, expectErr: false, }, { callContext: s.constructCallContext(JavaSDK, SupportedJavaSDKVersion), enableClientVersionCheck: true, expectErr: false, }, { callContext: s.constructCallContext(CLI, SupportedCLIVersion), enableClientVersionCheck: true, expectErr: false, }, } for _, tc := range testCases { versionChecker := NewVersionChecker() err := versionChecker.ClientSupported(tc.callContext, tc.enableClientVersionCheck) if tc.expectErr { s.Error(err) s.IsType(&types.ClientVersionNotSupportedError{}, err) } else { s.NoError(err) } } } func (s *VersionCheckerSuite) TestSupportsStickyQuery() { testCases := []struct { clientImpl string clientFeatureVersion string expectErr bool }{ { clientImpl: "", expectErr: true, }, { clientImpl: "", clientFeatureVersion: "0.9.0", expectErr: true, }, { clientImpl: "", clientFeatureVersion: "1.0.0", expectErr: false, }, { clientImpl: GoSDK, expectErr: true, }, { clientImpl: "unknown", clientFeatureVersion: "0.0.0", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: "malformed-feature-version", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: GoWorkerStickyQueryVersion, expectErr: false, }, { clientImpl: JavaSDK, clientFeatureVersion: JavaWorkerStickyQueryVersion, expectErr: false, }, { clientImpl: GoSDK, clientFeatureVersion: "0.9.0", expectErr: true, }, { clientImpl: JavaSDK, clientFeatureVersion: "0.9.0", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: "2.0.0", expectErr: false, }, { clientImpl: JavaSDK, clientFeatureVersion: "2.0.0", expectErr: false, }, } for _, tc := range testCases { vc := NewVersionChecker() if tc.expectErr { err := vc.SupportsStickyQuery(tc.clientImpl, tc.clientFeatureVersion) s.Error(err) s.IsType(&types.ClientVersionNotSupportedError{}, err) } else { s.NoError(vc.SupportsStickyQuery(tc.clientImpl, tc.clientFeatureVersion)) } } } func (s *VersionCheckerSuite) TestSupportsConsistentQuery() { testCases := []struct { clientImpl string clientFeatureVersion string expectErr bool }{ { clientImpl: "", expectErr: true, }, { clientImpl: "", clientFeatureVersion: GoWorkerConsistentQueryVersion, expectErr: true, }, { clientImpl: GoSDK, expectErr: true, }, { clientImpl: "unknown", clientFeatureVersion: "0.0.0", expectErr: true, }, { clientImpl: JavaSDK, clientFeatureVersion: "1.4.0", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: "malformed-feature-version", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: GoWorkerConsistentQueryVersion, expectErr: false, }, { clientImpl: GoSDK, clientFeatureVersion: "1.4.0", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: "2.0.0", expectErr: false, }, } for _, tc := range testCases { vc := NewVersionChecker() if tc.expectErr { err := vc.SupportsConsistentQuery(tc.clientImpl, tc.clientFeatureVersion) s.Error(err) s.IsType(&types.ClientVersionNotSupportedError{}, err) } else { s.NoError(vc.SupportsConsistentQuery(tc.clientImpl, tc.clientFeatureVersion)) } } } func (s *VersionCheckerSuite) TestSupportsRawHistoryQuery() { testCases := []struct { clientImpl string clientFeatureVersion string expectErr bool }{ { clientImpl: "", expectErr: true, }, { clientImpl: "", clientFeatureVersion: GoWorkerRawHistoryQueryVersion, expectErr: true, }, { clientImpl: GoSDK, expectErr: true, }, { clientImpl: "unknown", clientFeatureVersion: "0.0.0", expectErr: true, }, { clientImpl: JavaSDK, clientFeatureVersion: "1.5.0", expectErr: false, }, { clientImpl: JavaSDK, clientFeatureVersion: "1.7.0", expectErr: false, }, { clientImpl: JavaSDK, clientFeatureVersion: "1.2.0", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: "malformed-feature-version", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: GoWorkerRawHistoryQueryVersion, expectErr: false, }, { clientImpl: GoSDK, clientFeatureVersion: "1.4.0", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: "2.0.0", expectErr: false, }, { clientImpl: CLI, clientFeatureVersion: "1.5.0", expectErr: true, }, { clientImpl: CLI, clientFeatureVersion: "1.7.0", expectErr: false, }, } for _, tc := range testCases { vc := NewVersionChecker() if tc.expectErr { err := vc.SupportsRawHistoryQuery(tc.clientImpl, tc.clientFeatureVersion) s.Error(err) s.IsType(&types.ClientVersionNotSupportedError{}, err) } else { s.NoError(vc.SupportsRawHistoryQuery(tc.clientImpl, tc.clientFeatureVersion)) } } } func (s *VersionCheckerSuite) TestSupportsWorkflowAlreadyCompletedError() { testCases := []struct { clientImpl string clientFeatureVersion string expectErr bool }{ { clientImpl: "", expectErr: true, }, { clientImpl: "", clientFeatureVersion: GoWorkerWorkflowAlreadyCompletedVersion, expectErr: true, }, { clientImpl: GoSDK, expectErr: true, }, { clientImpl: "unknown", clientFeatureVersion: "0.0.0", expectErr: true, }, { clientImpl: JavaSDK, clientFeatureVersion: "1.4.0", expectErr: false, }, { clientImpl: JavaSDK, clientFeatureVersion: "1.3.0", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: "malformed-feature-version", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: GoWorkerWorkflowAlreadyCompletedVersion, expectErr: false, }, { clientImpl: GoSDK, clientFeatureVersion: "1.4.0", expectErr: true, }, { clientImpl: GoSDK, clientFeatureVersion: "2.0.0", expectErr: false, }, { clientImpl: CLI, clientFeatureVersion: "1.5.0", expectErr: true, }, { clientImpl: CLI, clientFeatureVersion: "1.7.0", expectErr: false, }, } featureFlags := apiv1.FeatureFlags{ WorkflowExecutionAlreadyCompletedErrorEnabled: true, } for _, tc := range testCases { vc := NewVersionChecker() if tc.expectErr { err := vc.SupportsWorkflowAlreadyCompletedError(tc.clientImpl, tc.clientFeatureVersion, featureFlags) s.Error(err) s.IsType(&types.ClientVersionNotSupportedError{}, err) } else { s.NoError(vc.SupportsWorkflowAlreadyCompletedError(tc.clientImpl, tc.clientFeatureVersion, featureFlags)) } } } func (s *VersionCheckerSuite) getHigherVersion(version string) string { split := strings.Split(version, ".") s.Len(split, 3) return fmt.Sprintf("%v.%v.%v", split[0], split[1], split[2][0]-'0'+1) } func (s *VersionCheckerSuite) constructCallContext(clientImpl string, featureVersion string) context.Context { ctx := context.Background() ctx, call := encoding.NewInboundCall(ctx) err := call.ReadFromRequest(&transport.Request{ Headers: transport.NewHeaders().With(common.ClientImplHeaderName, clientImpl).With(common.FeatureVersionHeaderName, featureVersion), }) s.NoError(err) return ctx } func TestGetFeatureFlagsFromHeader(t *testing.T) { tests := []struct { name string featureFlagsValue string // use "" to omit header want apiv1.FeatureFlags }{ { name: "no header", featureFlagsValue: "", want: apiv1.FeatureFlags{}, }, { name: "valid JSON with WorkflowExecutionAlreadyCompletedErrorEnabled true", featureFlagsValue: `{"WorkflowExecutionAlreadyCompletedErrorEnabled":true}`, want: apiv1.FeatureFlags{ WorkflowExecutionAlreadyCompletedErrorEnabled: true, }, }, { name: "valid JSON with WorkflowExecutionAlreadyCompletedErrorEnabled false", featureFlagsValue: `{"WorkflowExecutionAlreadyCompletedErrorEnabled":false}`, want: apiv1.FeatureFlags{ WorkflowExecutionAlreadyCompletedErrorEnabled: false, }, }, { name: "invalid JSON returns empty", featureFlagsValue: `{invalid}`, want: apiv1.FeatureFlags{}, }, { name: "empty JSON object", featureFlagsValue: `{}`, want: apiv1.FeatureFlags{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() ctx, inboundCall := encoding.NewInboundCall(ctx) headers := transport.NewHeaders() if tt.featureFlagsValue != "" { headers = headers.With(common.ClientFeatureFlagsHeaderName, tt.featureFlagsValue) } err := inboundCall.ReadFromRequest(&transport.Request{Headers: headers}) require.NoError(t, err) call := yarpc.CallFromContext(ctx) got := GetFeatureFlagsFromHeader(call) require.Equal(t, tt.want, got) }) } } func TestGetFeatureFlagsFromHeader_RoundTrip(t *testing.T) { flags := apiv1.FeatureFlags{ WorkflowExecutionAlreadyCompletedErrorEnabled: true, } serialized := FeatureFlagsHeader(flags) require.NotEmpty(t, serialized) ctx := context.Background() ctx, inboundCall := encoding.NewInboundCall(ctx) err := inboundCall.ReadFromRequest(&transport.Request{ Headers: transport.NewHeaders().With(common.ClientFeatureFlagsHeaderName, serialized), }) require.NoError(t, err) call := yarpc.CallFromContext(ctx) got := GetFeatureFlagsFromHeader(call) require.Equal(t, flags, got) } ================================================ FILE: common/clock/clockfx/clockfx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clockfx import ( "go.uber.org/fx" "github.com/uber/cadence/common/clock" ) // Module provides real time source for fx application. var Module = fx.Module("clock", fx.Provide(clock.NewRealTimeSource)) ================================================ FILE: common/clock/event_timer_gate.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clock import ( "sync" "time" ) type ( // EventTimerGate is a TimerGate in which the time is retrieved from external events via SetCurrentTime method. EventTimerGate interface { TimerGate // SetCurrentTime set the current time, and additionally fire the fire chan // if new "current" time is after the fire time, return true if // "current" is actually updated SetCurrentTime(t time.Time) bool } eventTimerGateImpl struct { // the channel which will be used to proxy the fired timer fireChan chan time.Time // lock for timer and fire time sync.RWMutex // view of current time currentTime time.Time // time which timer will fire fireTime time.Time } ) // NewEventTimerGate create a new event timer gate instance func NewEventTimerGate(currentTime time.Time) EventTimerGate { timer := &eventTimerGateImpl{ currentTime: currentTime, fireTime: time.Time{}, fireChan: make(chan time.Time, 1), } return timer } func (t *eventTimerGateImpl) Chan() <-chan time.Time { return t.fireChan } func (t *eventTimerGateImpl) FireAfter(now time.Time) bool { t.RLock() defer t.RUnlock() active := t.currentTime.Before(t.fireTime) return active && t.fireTime.After(now) } func (t *eventTimerGateImpl) Update(nextTime time.Time) bool { t.Lock() defer t.Unlock() active := t.currentTime.Before(t.fireTime) if active { if t.fireTime.Before(nextTime) { // current time < next wake up time < next time return false } if t.currentTime.Before(nextTime) { // current time < next time <= next wake up time t.fireTime = nextTime return true } // next time <= current time < next wake up time t.fireTime = nextTime t.fire(t.currentTime) return true } // this means the timer, before stopped, has already fired / never active if !t.currentTime.Before(nextTime) { // next time is <= current time, need to fire immediately // whether to update next wake up time or not is irrelevent t.fire(t.currentTime) } else { // next time > current time t.fireTime = nextTime } return true } func (t *eventTimerGateImpl) Stop() { t.Lock() defer t.Unlock() t.fireTime = time.Time{} } func (t *eventTimerGateImpl) SetCurrentTime(currentTime time.Time) bool { t.Lock() defer t.Unlock() if !t.currentTime.Before(currentTime) { // new current time is <= current time return false } // NOTE: do not update the current time now if !t.currentTime.Before(t.fireTime) { // current time already >= next wakeup time // avoid duplicate fire t.currentTime = currentTime return true } t.currentTime = currentTime if !t.currentTime.Before(t.fireTime) { t.fire(t.currentTime) } return true } func (t *eventTimerGateImpl) fire(fireTime time.Time) { select { case t.fireChan <- fireTime: // timer successfully triggered default: // timer already triggered, pass } } ================================================ FILE: common/clock/event_timer_gate_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clock import ( "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type ( eventTimerGateSuite struct { suite.Suite *require.Assertions currentTime time.Time eventTimerGate EventTimerGate } ) func TestEventTimerGeteSuite(t *testing.T) { s := new(eventTimerGateSuite) suite.Run(t, s) } func (s *eventTimerGateSuite) SetupSuite() { } func (s *eventTimerGateSuite) TearDownSuite() { } func (s *eventTimerGateSuite) SetupTest() { s.Assertions = require.New(s.T()) s.currentTime = time.Now().Add(-10 * time.Minute) s.eventTimerGate = NewEventTimerGate(s.currentTime) } func (s *eventTimerGateSuite) TearDownTest() { } func (s *eventTimerGateSuite) TestTimerFire() { now := s.currentTime newTimer := now.Add(1 * time.Second) deadline := now.Add(2 * time.Second) s.eventTimerGate.Update(newTimer) select { case <-s.eventTimerGate.Chan(): s.Fail("timer should not fire when current time not updated") case <-time.NewTimer(deadline.Sub(now)).C: } s.eventTimerGate.SetCurrentTime(deadline) select { case <-s.eventTimerGate.Chan(): default: s.Fail("timer should fire") } } func (s *eventTimerGateSuite) TestTimerFireAfterUpdate_Active_Updated_BeforeNow() { now := s.currentTime newTimer := now.Add(5 * time.Second) updatedNewTimer := now.Add(-1 * time.Second) deadline := now.Add(3 * time.Second) s.eventTimerGate.Update(newTimer) select { case <-s.eventTimerGate.Chan(): s.Fail("timer should not fire when current time not updated") case <-time.NewTimer(deadline.Sub(now)).C: } s.True(s.eventTimerGate.Update(updatedNewTimer)) select { case <-s.eventTimerGate.Chan(): default: s.Fail("timer should fire") } } func (s *eventTimerGateSuite) TestTimerFireAfterUpdate_Active_Updated() { now := s.currentTime newTimer := now.Add(5 * time.Second) updatedNewTimer := now.Add(1 * time.Second) deadline := now.Add(3 * time.Second) s.eventTimerGate.Update(newTimer) s.True(s.eventTimerGate.Update(updatedNewTimer)) select { case <-s.eventTimerGate.Chan(): s.Fail("timer should not fire when current time not updated") case <-time.NewTimer(deadline.Sub(now)).C: } s.eventTimerGate.SetCurrentTime(updatedNewTimer) select { case <-s.eventTimerGate.Chan(): default: s.Fail("timer should fire") } } func (s *eventTimerGateSuite) TestTimerFireAfterUpdate_Active_NotUpdated() { now := s.currentTime newTimer := now.Add(1 * time.Second) updatedNewTimer := now.Add(3 * time.Second) deadline := now.Add(2 * time.Second) s.eventTimerGate.Update(newTimer) s.False(s.eventTimerGate.Update(updatedNewTimer)) select { case <-s.eventTimerGate.Chan(): s.Fail("timer should not fire when current time not updated") case <-time.NewTimer(deadline.Sub(now)).C: } s.eventTimerGate.SetCurrentTime(updatedNewTimer) select { case <-s.eventTimerGate.Chan(): default: s.Fail("timer should fire") } } func (s *eventTimerGateSuite) TestTimerFireAfterUpdate_NotActive_Updated() { now := s.currentTime newTimer := now.Add(-5 * time.Second) updatedNewTimer := now.Add(1 * time.Second) deadline := now.Add(2 * time.Second) s.eventTimerGate.Update(newTimer) // this is to drain existing signal <-s.eventTimerGate.Chan() // test setup up complete s.True(s.eventTimerGate.Update(updatedNewTimer)) select { case <-s.eventTimerGate.Chan(): s.Fail("timer should not fire when current time not updated") case <-time.NewTimer(deadline.Sub(now)).C: } s.eventTimerGate.SetCurrentTime(updatedNewTimer) select { case <-s.eventTimerGate.Chan(): default: s.Fail("timer should fire") } } func (s *eventTimerGateSuite) TestTimerFireAfterUpdate_NotActive_NotUpdated() { now := s.currentTime newTimer := now.Add(-5 * time.Second) updatedNewTimer := now.Add(-1 * time.Second) s.eventTimerGate.Update(newTimer) // this is to drain existing signal <-s.eventTimerGate.Chan() // test setup up complete s.True(s.eventTimerGate.Update(updatedNewTimer)) select { case <-s.eventTimerGate.Chan(): default: s.Fail("timer should fire when new timer is in the past") } } func (s *eventTimerGateSuite) TestTimerSetCurrentTime_NoUpdate() { now := s.currentTime newCurrentTime := now.Add(-1 * time.Second) s.False(s.eventTimerGate.SetCurrentTime(newCurrentTime)) select { case <-s.eventTimerGate.Chan(): s.Fail("timer should not fire") default: } } func (s *eventTimerGateSuite) TestTimerSetCurrentTime_Update_TimerAlreadyFired() { now := s.currentTime newTimer := now.Add(-1 * time.Second) newCurrentTime := now.Add(1 * time.Second) s.eventTimerGate.Update(newTimer) // this is to drain existing signal <-s.eventTimerGate.Chan() // test setup up complete s.True(s.eventTimerGate.SetCurrentTime(newCurrentTime)) select { case <-s.eventTimerGate.Chan(): s.Fail("timer should not fire") default: } } func (s *eventTimerGateSuite) TestTimerSetCurrentTime_Update_TimerNotFired() { now := s.currentTime newTimer := now.Add(2 * time.Second) newCurrentTime := now.Add(1 * time.Second) s.eventTimerGate.Update(newTimer) s.True(s.eventTimerGate.SetCurrentTime(newCurrentTime)) select { case <-s.eventTimerGate.Chan(): s.Fail("timer should not fire") default: } } func (s *eventTimerGateSuite) TestTimerSetCurrentTime_Update_TimerFired() { now := s.currentTime newTimer := now.Add(2 * time.Second) newCurrentTime := now.Add(2 * time.Second) s.eventTimerGate.Update(newTimer) s.True(s.eventTimerGate.SetCurrentTime(newCurrentTime)) select { case <-s.eventTimerGate.Chan(): default: s.Fail("timer should fire") } // should fire only once newCurrentTime = newCurrentTime.Add(1 * time.Second) s.True(s.eventTimerGate.SetCurrentTime(newCurrentTime)) select { case <-s.eventTimerGate.Chan(): s.Fail("timer should not fire") default: } } func (s *eventTimerGateSuite) TestTimerWillFire_Zero() { // this test is to validate initial notification will trigger a scan of timer s.eventTimerGate.Update(time.Time{}) s.False(s.eventTimerGate.FireAfter(time.Now())) } func (s *eventTimerGateSuite) TestTimerWillFire_Active() { now := s.currentTime newTimer := now.Add(2 * time.Second) timeBeforeNewTimer := now.Add(1 * time.Second) timeAfterNewimer := now.Add(3 * time.Second) s.eventTimerGate.Update(newTimer) s.True(s.eventTimerGate.FireAfter(timeBeforeNewTimer)) s.False(s.eventTimerGate.FireAfter(timeAfterNewimer)) } func (s *eventTimerGateSuite) TestTimerWillFire_NotActive() { now := s.currentTime newTimer := now.Add(-2 * time.Second) timeBeforeTimer := now.Add(-3 * time.Second) timeAfterTimer := now.Add(1 * time.Second) s.eventTimerGate.Update(newTimer) s.False(s.eventTimerGate.FireAfter(timeBeforeTimer)) s.False(s.eventTimerGate.FireAfter(timeAfterTimer)) } ================================================ FILE: common/clock/ratelimiter.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package=$GOPACKAGE -destination=ratelimiter_mock.go github.com/uber/cadence/common/clock Reservation package clock import ( "context" "fmt" "sync" "time" "golang.org/x/time/rate" ) type ( // Ratelimiter is a test-friendly version of [golang.org/x/time/rate.Limiter], // which can be backed by any TimeSource. The changes summarize as: // - Time is never allowed to rewind, and does not advance when canceling, // fixing core issues with [golang.org/x/time/rate.Limiter] and greatly // improving the chances of successfully canceling a reserved token in our // usage (from "almost literally never" to "likely 99%+"). // - Reservations are simplified and do not allow waiting, but are MUCH more // likely to return tokens when canceled or not allowed. // - All MethodAt APIs have been excluded as we do not use them, and they would // have to bypass the contained TimeSource, which seems error-prone. // - All `MethodN` APIs (specifically the ">1 event" parts) have been excluded // because we do not use them, but they seem fairly easy to add if needed later. Ratelimiter interface { // Allow returns true if an event can be performed now, i.e. there // are burst-able tokens available to use. // // To perform events at a steady rate, use Wait instead. Allow() bool // Burst returns the maximum burst size allowed by this Ratelimiter. // A Burst of zero returns false from Allow, unless its Limit is infinity. // // To see the number of burstable tokens available now, use Tokens. Burst() int // Limit returns the maximum overall rate of events that are allowed. Limit() rate.Limit // Reserve claims a single Allow() call that can be canceled if not used. // Check Reservation.Allow() to see if the event is allowed. // // This Reservation does not support waiting, and needs to have // Reservation.Used(true/false) called as soon as possible to have the // best chance of returning its unused token. Reserve() Reservation // SetBurst sets the Burst value SetBurst(newBurst int) // SetLimit sets the Limit value SetLimit(newLimit rate.Limit) SetLimitAndBurst(newLimit rate.Limit, newBurst int) // Tokens returns the number of immediately-allowable events when called. // Values >= 1 will lead to Allow returning true. // // These values are NOT able to guarantee that a later call will succeed // or fail, as other calls to Allow or Reserve may occur before you are // able to act on the returned value. // It is essentially only suitable for monitoring or test use, and calling // it may lead to failing to cancel reserved tokens (as it advances time). Tokens() float64 // Wait blocks until one token is available (and consumes it), or it returns // an error and does not consume any tokens if: // - the context is canceled // - a token will never become available (burst == 0) // - a token is not expected to become available in time // (short deadline and/or many pending reservations or waiters) // // If an error is returned, regardless of the reason, you may NOT proceed // with the limited event you were waiting for. Wait(ctx context.Context) error } // Reservation is a simplified and test-friendly version of [golang.org/x/time/rate.Reservation] // that only allows checking if it is successful or not, and (possibly) returning unused tokens. // // This Reservation MUST have Allow() checked and marked as Used(true/false) // as soon as possible, or an unused token is unlikely to be returned. // // Recommended use is either by using defer: // // func allow() (allowed bool) { // r := ratelimiter.Reserve() // // defer in a closure to read the most-recent value, not the value when deferred // defer func() { r.Used(allowed) }() // allowed = r.Allow() // if !allowed { // return false // also marks the reservation as not-used // } // if somethingElseAllowsIt() { // // allow the event, and mark the reservation as used so the token is consumed // return true // } // // cancel the reservation and possibly return the token // return false // } // // Or directly calling Used when appropriate: // // func allow() bool { // r := ratelimiter.Reserve() // allowed := r.Allow() // if !allowed { // r.Used(false) // mark the reservation as not-used // return false // } // if somethingElseAllowsIt() { // // allow the event, and mark the reservation as used // r.Used(true) // return true // } // r.Used(false) // mark the reservation as not-used, and restore the unused token // return false // } Reservation interface { // Allow returns true if a request should be allowed. // // This may be called at any time and it will return the same value each time, // but it becomes less correct as time passes, so you are expected to call it ~immediately. // // As soon as possible, also call Used(true/false) to consume / return the reserved token. // // This is equivalent to `OK() == true && DelayFrom(reservationTime) == 0` // with [rate.Reservation]. Allow() bool // Used marks this Reservation as either used or not-used, and it must be called // once on every Reservation: // - If true, the event is assumed to be allowed, and the Ratelimiter token // will remain consumed. // - If false, the Reservation will be rolled back, possibly restoring a Ratelimiter token. // This is equivalent to calling Cancel() on a [rate.Reservation], but it is more likely // to return the token. Used(wasUsed bool) } ratelimiter struct { // important design note: the lock MUST be held both while acquiring AND while handling // `timesource.Now()`. otherwise time will not be monotonically handled due to waiting // on mutexes, and the *rate.Limiter internal time may go backwards and misbehave. timesource TimeSource latestNow time.Time // updated on each call, never allowed to rewind limiter *rate.Limiter mut sync.Mutex } // a reservation that allows an immediate call, and can be canceled allowedReservation struct { res *rate.Reservation // reservedAt is used to cancel at the reservation time, restoring tokens // if (and only if) no other time-advancing calls have interleaved reservedAt time.Time // needs access to the parent-wrapped-ratelimiter to ensure time is not rewound limiter *ratelimiter } // a reservation that does NOT allow an immediate call. // the *rate.Reservation has already been canceled, so this type is trivial. deniedReservation struct{} ) var ( _ Ratelimiter = (*ratelimiter)(nil) _ Reservation = (*allowedReservation)(nil) _ Reservation = deniedReservation{} ) // err constants to keep allocations low var ( // ErrCannotWait is used as the common base for two internal reasons why Ratelimiter.Wait call cannot // succeed, without directly exposing the reason except as an explanatory string. // // Callers should only ever care about ErrCannotWait to be resistant to racing behavior. // Or perhaps ideally: not care about the reason at all beyond "not a ctx.Err()". ErrCannotWait = fmt.Errorf("ratelimiter.Wait() cannot be satisfied") errWaitLongerThanDeadline = fmt.Errorf("would wait longer than ctx deadline: %w", ErrCannotWait) errWaitZeroBurst = fmt.Errorf("zero burst will never allow: %w", ErrCannotWait) ) func NewRatelimiter(lim rate.Limit, burst int) Ratelimiter { return &ratelimiter{ timesource: NewRealTimeSource(), limiter: rate.NewLimiter(lim, burst), // intentionally zero, matches rate.Limiter and helps fill the bucket if it is changed before any use. latestNow: time.Time{}, } } func NewRateLimiterWithTimeSource(ts TimeSource, lim rate.Limit, burst int) Ratelimiter { return &ratelimiter{ timesource: ts, limiter: rate.NewLimiter(lim, burst), // intentionally zero, matches rate.Limiter and helps fill the bucket if it is changed before any use. latestNow: time.Time{}, } } // lockNow updates the current "now" time, and ensures it never rolls back. // This should be equivalent to setting `r.timesource.Now()`, outside of tests // that abuse mocked time and Advance with negative values. // // The lock MUST be held until all time-accepting methods are called on the // underlying rate.Limiter, as otherwise the internal "now" value may rewind // and cause undefined behavior. func (r *ratelimiter) lockNow() (now time.Time, unlock func()) { r.mut.Lock() newNow := r.timesource.Now() // caution: must be after acquiring the lock if newNow.After(r.latestNow) { // this should always be true because `time.Now()` is monotonic, and it is // always acquired inside the lock (so values are strictly ordered and // not arbitrarily delayed). // // that means e.g. lockReservedNow should not ever receive a value newer than // the Now we just acquired, so r.latestNow should never be newer either, so // this branch is always selected (unless time is equal). // // that said: it still shouldn't be allowed to rewind, so it's checked. r.latestNow = newNow } return r.latestNow, r.mut.Unlock } // lockReservedNow gets the correct "now" time to use, ensuring it never rolls back. // This is intended to be used by reservations, to allow them to return claimed tokens // after arbitrary waits, as long as no other calls have interleaved. // // The returned value MUST be used instead of the passed time: it may be the same or newer. // The passed value may rewind the rate.Limiter's internal time record, and cause undefined behavior. // // The lock MUST be held until all time-accepting methods are called on the // underlying rate.Limiter, as otherwise the internal "now" value may rewind // and cause undefined behavior. func (r *ratelimiter) lockReservedNow(tryNow time.Time) (now time.Time, unlock func()) { r.mut.Lock() if tryNow.After(r.latestNow) { // this should never occur! // // reservations and their reserved-time are gathered behind a lock, and time is monotonic. // that means that a reserved-time should ALWAYS be the same or older than any other time // the ratelimiter sees -> its time would have to go backwards. // // that said, if this DOES happen, it must still maintain the newest time. // this should only occur in tests that move time backwards, unless there's // an implementation bug in the ratelimiter or Go's time. r.latestNow = tryNow } return r.latestNow, r.mut.Unlock } func (r *ratelimiter) Allow() bool { now, unlock := r.lockNow() defer unlock() return r.limiter.AllowN(now, 1) } func (r *ratelimiter) Reserve() Reservation { now, unlock := r.lockNow() defer unlock() res := r.limiter.ReserveN(now, 1) if res.OK() && res.DelayFrom(now) == 0 { // usable token, return a cancel-able handler in case it's not used return &allowedReservation{ res: res, reservedAt: now, limiter: r, } } // unusable, immediately cancel it to get rid of the future-reservation // since we don't allow waiting for it. // // this always succeeds and returns the token because nothing is allowed to // interleave, unlike if it is done asynchronously, so it returns MANY more // tokens when contended. res.CancelAt(now) return deniedReservation{} } func (r *ratelimiter) Burst() int { // rate.Limiter.Burst does not currently advance time, // so this does not need to hold the ratelimiter lock. // if this changes, use lockNow. return r.limiter.Burst() } func (r *ratelimiter) Limit() rate.Limit { // rate.Limiter.Limit does not currently advance time, // so this does not need to hold the ratelimiter lock. // if this changes, use lockNow. return r.limiter.Limit() } func (r *ratelimiter) SetBurst(newBurst int) { r.mut.Lock() defer r.mut.Unlock() // setting burst/limit does not advance time, unlike the underlying limiter. // // this allows calling them in any order, and the next request // will fill the token bucket to match elapsed time. // // this prefers new burst/limit values over past values, // as they are assumed to be "better", and in particular ensures the first // time-advancing call fills with the full values (starting from 0 time, like // the underlying limiter does). r.limiter.SetBurstAt(r.latestNow, newBurst) } func (r *ratelimiter) SetLimit(newLimit rate.Limit) { r.mut.Lock() defer r.mut.Unlock() // setting burst/limit does not advance time, unlike the underlying limiter. // // this allows calling them in any order, and the next request // will fill the token bucket to match elapsed time. // // this prefers new burst/limit values over past values, // as they are assumed to be "better", and in particular ensures the first // time-advancing call fills with the full values (starting from 0 time, like // the underlying limiter does). r.limiter.SetLimitAt(r.latestNow, newLimit) } func (r *ratelimiter) SetLimitAndBurst(newLimit rate.Limit, newBurst int) { r.mut.Lock() defer r.mut.Unlock() // See SetLimit and SetBurst for more information r.limiter.SetLimitAt(r.latestNow, newLimit) r.limiter.SetBurstAt(r.latestNow, newBurst) } func (r *ratelimiter) Tokens() float64 { now, unlock := r.lockNow() defer unlock() return r.limiter.TokensAt(now) } func (r *ratelimiter) Wait(ctx context.Context) (err error) { if err := ctx.Err(); err != nil { // canceled contexts imply that the limited-work will not be done, // so do not allow any tokens to be consumed. // rate.Limiter also behaves this way in Wait. return err } now, unlock := r.lockNow() unlockOnce := doOnce(unlock) defer unlockOnce() // unlock if panic or returned early with no err res := r.limiter.ReserveN(now, 1) if !res.OK() { // !OK can only mean "impossible to wait" with `ReserveN`. // Since there is no deadline passed to the limiter (the reservation // contains the delay), and we only ever request 1 token, this can only // mean a burst of 0 (and non-infinite rate.Limit). res.CancelAt(now) // unused token, return it while locked return errWaitZeroBurst } delay := res.DelayFrom(now) if delay == 0 { return nil // available token, allow it } if deadline, ok := ctx.Deadline(); ok && now.Add(delay).After(deadline) { res.CancelAt(now) // unused token, return it while locked return errWaitLongerThanDeadline // don't wait for a known failure } unlockOnce() // unlock before waiting defer func() { if err != nil { // err return means "not allowed", so cancel the reservation. // // note that this makes a separate call to get the current time: // this is intentional, as time has likely passed while waiting. // // if the cancellation happened before the time-to-act (the delay), // the reservation will still be successfully rolled back. // (re)-acquire the latest now value. // // it should not be advanced to the "real" now, to improve the chance // that the token will be restored, but if there was any waiting // it's pretty likely that other calls occurred while unlocked and // the most-recently-observed time must be maintained. now, unlock := r.lockReservedNow(now) defer unlock() res.CancelAt(now) } }() // wait for cancellation or the waiter's turn. // re-acquiring Now() here is valid and may shorten the delay, but may add // more branches and isn't strictly necessary as sleeps aren't precise anyway. timer := r.timesource.NewTimer(delay) defer timer.Stop() select { case <-ctx.Done(): return ctx.Err() case <-timer.Chan(): return nil } } func (r *allowedReservation) Used(used bool) { if !used { now, unlock := r.limiter.lockReservedNow(r.reservedAt) // lock must be held while canceling, because it can affect the limiter's time defer unlock() // if no calls interleaved, this will be the same as the reservation time, // and the cancellation will be able to roll back an immediately-available call. // // otherwise, the ratelimiter's time has been advanced, and this will fail to // restore any tokens. r.res.CancelAt(now) } // if used, do nothing. // // this method largely exists so it can be mocked and asserted, // or customized by an implementation that tracks allowed/rejected metrics. } func (r *allowedReservation) Allow() bool { // this is equivalent to `r.res.OK() && r.res.DelayFrom(r.reservedAt) == 0`, // but this is known to be `true` when it is first reserved and `r.reservedAt` // does not ever change, so it can be skipped here. return true } func (r deniedReservation) Used(used bool) { // reservation already canceled, nothing to roll back } func (r deniedReservation) Allow() bool { return false } // a small sync.Once-alike for single-threaded use. // // Ratelimiter.Wait benchmarks perform measurably better with this vs sync.Once, // though not by a large margin. func doOnce(f func()) func() { called := false return func() { if called { return } called = true f() } } ================================================ FILE: common/clock/ratelimiter_bench_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clock import ( "context" "fmt" "testing" "time" "go.uber.org/atomic" "golang.org/x/time/rate" ) /* Benchmark data can be seen in the testdata folder, for relevant machines. Interpretation: - for essentially all operations, mostly regardless of sequential / parallel / how parallel, the added latency is ~50%, and even extremes are less than double. this seems entirely tolerable and safe to use. - "reserve and cancel" sequentially is actually *faster* with the wrapper, probably due to fewer time advancements. with parallel usage, real vs wrapper is basically a tie. - mock-time ratelimiter is faster than real-time in essentially all cases, as you would hope. though not by much, unless waiting was part of the test. - real *rate.Limiter instances have some pathological behavior that... might disqualify them from safe use tbh. essentially this summarizes as "time.Now() is not globally monotonic", and it can lead to significant over and under limiting. The real *rate.Limiter instances show these quirks during parallel tests, due to time rewinding and jumping forward repeatedly from the *rate.Limiter's point of view (as it has an internal last-Now value protected by a mutex): 1. `Allow()`-like calls (both literally `Allow()`, and `Reserve()` followed by sometimes `reservation.Cancel()`) can allow both significantly more or significantly fewer calls than they should. 2. `Wait()` can allow calls through at a faster rate than it should. At peak, a bit over 2x. (and probably slower, but sleeping is not guaranteed to wake up at a precise time so would still be correct behavior) Essentially, when one call sets the time to "now", and an out-of-sync call sets it to "now-1ms" (time is acquired outside the limiter's lock, and they may make progress out of order), a token may be restored when a time-consistent limiter would not do so. You can recreate this by hand by feeding a ratelimiter "now" and "now+1s" (>=2x the limit) randomly, and watching what it allows / what the value of Tokens() is as time passes. Doing this *literally* by hand reproduces it, e.g. pressing enter on a command-line loop that does this - high frequency is not necessary, just illogical "time travel" outside tight bounds. The first can be seen in m1_mac_go1.21.txt by comparing these tests: - BenchmarkLimiter/allow/parallel/real-4 (note the near-1µs time per allow, which serial calls match) - BenchmarkLimiter/allow/parallel/real-8 (time-per-allow is now 231ns, over 4x more allowed than it should) - BenchmarkLimiter/reserve-canceling-every-3/parallel/real-2 (1.5ms per allow, around 1,000x fewer allowed than it should) - BenchmarkLimiter/reserve-canceling-every-3/parallel/real-8 (450ns per allow, around 2x more than it should) "real with pinned time" behaves similarly, for similar reasons: canceling can time-travel. pinned time does make the behavior when called on a single goroutine roughly perfect though. And the second can be seen in m1_mac_go1.21.txt by comparing these tests: - BenchmarkLimiter/wait/parallel/1µs_rate/real (almost exactly 1µs per iteration) - BenchmarkLimiter/wait/parallel/1µs_rate/real-8 (~500ns, twice as fast as it should allow) I have not tried to verify Wait's cause by hand, but it certainly seems like time-thrashing explains it as well. This is also supported by the wrapper's time-locking *completely* eliminating this flaw, as all iterations take almost exactly 1µs as they should. --- All machines I've tried it on behave similarly, though whether one parallel test triggers misbehavior or not is fairly random. Higher parallelism triggers it more, and it seems *fairly* likely that at least one misbehaves in a full `-cpu 1,2,4,8,...[>=cores]` suite, and sometimes many more. */ func BenchmarkLimiter(b *testing.B) { const ( normalLimit = time.Microsecond // pretty arbitrary but has a good blend of allow and deny burst = 1000 // arbitrary, but more interesting than 1, and better matches how we use limiters (rate/s == burst) allowedPeriodFmt = "time per allow: %9s" ) type runType func(b *testing.B, each func(int) bool) // runs a callback in a sequential benchmark, and tracks allowed/denied metrics var runSerial runType = func(b *testing.B, each func(int) bool) { b.Helper() allowed, denied := 0, 0 for i := 0; i < b.N; i++ { if each(i) { allowed++ } else { denied++ } } as := fmt.Sprintf("allowed: %v,", allowed) ds := fmt.Sprintf("denied: %v,", denied) allowedPeriod := fmt.Sprintf(allowedPeriodFmt, "n/a") if allowed > 0 { allowedPeriod = fmt.Sprintf(allowedPeriodFmt, fmt.Sprint(b.Elapsed()/time.Duration(allowed))) } b.Logf("%-20s %-20s %s", as, ds, allowedPeriod) // allows controlling whitespace better } // runs a callback in a parallel benchmark, and tracks allowed/denied metrics var runParallel runType = func(b *testing.B, each func(int) bool) { b.Helper() var allowed, denied atomic.Int64 b.RunParallel(func(pb *testing.PB) { n := 0 for pb.Next() { if each(n) { allowed.Inc() } else { denied.Inc() } n++ } }) as := fmt.Sprintf("allowed: %v,", allowed.Load()) ds := fmt.Sprintf("denied: %v,", denied.Load()) allowedPeriod := fmt.Sprintf(allowedPeriodFmt, "n/a") if allowed.Load() > 0 { allowedPeriod = fmt.Sprintf(allowedPeriodFmt, fmt.Sprint(b.Elapsed()/time.Duration(allowed.Load()))) } b.Logf("%-20s %-20s %s", as, ds, allowedPeriod) // allows controlling whitespace better } both := map[string]runType{ "serial": runSerial, "parallel": runParallel, } // Allow is a significant amount of our ratelimiter usage, // so this should probably take top priority. b.Run("allow", func(b *testing.B) { for name, runner := range both { b.Run(name, func(b *testing.B) { b.Run("real", func(b *testing.B) { // very fast to jump back and forth, rather than slamming into "deny" rl := rate.NewLimiter(rate.Every(normalLimit), burst) runner(b, func(i int) bool { return rl.Allow() }) }) b.Run("wrapped", func(b *testing.B) { rl := NewRatelimiter(rate.Every(normalLimit), burst) runner(b, func(i int) bool { return rl.Allow() }) }) b.Run("mocked timesource", func(b *testing.B) { ts := NewMockedTimeSource() rl := NewRateLimiterWithTimeSource(ts, rate.Every(normalLimit), burst) runner(b, func(i int) bool { // adjusted by eye, to try to very roughly match the above values for the final runs. // anything not extremely higher or lower is probably fine. ts.Advance(normalLimit / 5) return rl.Allow() }) }) }) } }) // Reserve is used when tiering ratelimiters, which is only done in a few // primarily-user-facing limits that aren't super high perf need... // ... BUT Reserve is used in the wrapper's Wait, so this serves to separate // out its cost from the Wait benchmarks. cancelNth := 3 b.Run(fmt.Sprintf("reserve-canceling-every-%v", cancelNth), func(b *testing.B) { for name, runner := range both { b.Run(name, func(b *testing.B) { /* CAUTION: in parallel, these real ratelimiters run quickly, but note how frequently requests are allowed. Both more and less than intended, sometimes by multiple orders of magnitude. example: BenchmarkLimiter/reserve-canceling-every-3/parallel/real-2 ratelimiter_test.go:136: allowed: 752, denied: 7519799, time per allow: 1.558596ms 1,500x too few! ----------^ BenchmarkLimiter/reserve-canceling-every-3/parallel/real-8 ratelimiter_test.go:136: allowed: 2651622, denied: 1415746, time per allow: 449ns 2x too many! --------------^ this occurs because the ratelimiter gathers `time.Now()` before locking, which leads to *handling* time in a non-monotonic way depending on what goroutines acquire the lock / in which order. this is one of the reasons this wrapper was built. */ b.Run("real", func(b *testing.B) { rl := rate.NewLimiter(rate.Every(normalLimit), burst) runner(b, func(i int) (allowed bool) { r := rl.Reserve() allowed = r.OK() && r.Delay() == 0 canceled := i%cancelNth == 0 allowed = allowed && !canceled if !allowed { // all not-allowed calls must be canceled, as they will not be "used". // otherwise they will still be "used", and tokens will go negative, // possibly far into the negatives. r.Cancel() return } return }) }) b.Run("real pinned time", func(b *testing.B) { rl := rate.NewLimiter(rate.Every(normalLimit), burst) runner(b, func(i int) (allowed bool) { // expected to be faster as the limiter's time does not // advance as often, and "now" is only gathered once. // // but this is not correct with concurrent use, so it's // purely synthetic and serves as a lower bound only. now := time.Now() r := rl.ReserveN(now, 1) allowed = r.OK() && r.DelayFrom(now) == 0 canceled := i%cancelNth == 0 allowed = allowed && !canceled if !allowed { r.CancelAt(now) return } return }) }) // calls Reserve on the wrapped limiter and sometimes cancels, sequentially or in parallel runWrapped := func(b *testing.B, rl Ratelimiter, runner runType, advance func()) { b.Helper() runner(b, func(i int) (allowed bool) { // not b.Helper because no logs occur inside here, and it has runtime cost if advance != nil { advance() // for mock time } r := rl.Reserve() allowed = r.Allow() canceled := i%cancelNth == 0 allowed = allowed && !canceled r.Used(allowed) return }) } b.Run("wrapped", func(b *testing.B) { rl := NewRatelimiter(rate.Every(normalLimit), burst) runWrapped(b, rl, runner, nil) }) b.Run("mocked timesource", func(b *testing.B) { ts := NewMockedTimeSource() rl := NewRateLimiterWithTimeSource(ts, rate.Every(normalLimit), burst) runWrapped(b, rl, runner, func() { // any value should work, but smaller than normalLimit revealed // some issues in the past, where time-thrashing would lead to // extremely low allowed calls. // // this is now resolved with synchronous cancels when reservations // are not synchronously allowed, but it seems worth keeping to // reveal regressions. ts.Advance(normalLimit / 10) }) }) }) } }) // Wait is implemented quite differently between real and wrapped. // For the most part our use is in batches, where queueing is expected. // This makes benchmarks kinda synthetic. Queueing won't run any faster than the intended rate. // // Still, the allowed events per second should be similar (when limited). // // Unfortunately there are some rate.Limiter misbehaviors and a very high chance // of CPU latency impacting this test, so this is not asserted, only logged. b.Run("wait", func(b *testing.B) { for name, runner := range both { b.Run(name, func(b *testing.B) { durations := []time.Duration{ // try an insanely fast refresh, to see maximum throughput with "zero" waiting. // intentionally avoiding 1 nanosecond in case it gets special-cased, like 0 and math.MaxInt64. 2 * time.Nanosecond, // try a rate that should contend and wait heavily. // this SHOULD be how long the per-iteration time is (when not mocked)... but read comments below. normalLimit, } for _, dur := range durations { b.Run(fmt.Sprintf("%v rate", dur), func(b *testing.B) { limit := rate.Every(dur) // set a wait timeout long enough to allow ~all attempts. // // b.Run seems to target <1s, but very fast or very slow things can extend it, // and up to about 2s seems normal for these. // because of that, 5s occasionally timed out, but 10s seems fine. timeout := 20 * time.Second ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() b.Run("real", func(b *testing.B) { // sometimes misbehaves, allowing too many or too few requests through! rl := rate.NewLimiter(limit, burst) runner(b, func(i int) bool { return rl.Wait(ctx) == nil }) }) b.Run("wrapped", func(b *testing.B) { rl := NewRatelimiter(limit, burst) runner(b, func(i int) bool { return rl.Wait(ctx) == nil }) }) b.Run("mocked timesource", func(b *testing.B) { ts := NewMockedTimeSource() rl := NewRateLimiterWithTimeSource(ts, limit, burst) ctx := ×ourceContext{ ts: ts, deadline: time.Now().Add(timeout), } // make sure tests don't block super long even if they misbehave, // by canceling the context after enough real-world time has passed. defer time.AfterFunc(timeout, func() { ts.Advance(timeout) }).Stop() runner(b, func(i int) bool { // advance enough to restore a token so it does not block forever. // "mock-wait times out when it should" is asserted in the tests. ts.Advance(dur) return rl.Wait(ctx) == nil }) }) // should be true if the round took longer than timeout, // as the AfterFunc will fire -> advance time -> close context -> then the round finishes. if ctx.Err() != nil { b.Errorf("benchmark likely invalid, did not complete before context timeout (%v)", timeout) } }) } }) } }) } ================================================ FILE: common/clock/ratelimiter_comparison_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clock import ( "context" "fmt" "math/rand" "strings" "sync" "testing" "time" "github.com/stretchr/testify/assert" "golang.org/x/sync/errgroup" "golang.org/x/time/rate" ) const ( // amount of time between each conceptual "tick" of the test's clocks, both real and fake. // sadly even 10ms has some excessive latency a few % of the time, particularly during the Wait check. fuzzGranularity = 100 * time.Millisecond // keep running tests until this amount of time has passed, or a failure has occurred. // this could be a static count of tests to run, but a target duration // is a bit less fiddly. "many attempts" is the goal, not any specific number. // // alternatively, tests have a deadline, this could run until near that deadline. // but right now that's not fine-tuned per package, so it's quite long. fuzzDuration = 8 * time.Second // mostly stays under 10s, feels reasonable // debug-level output is very noisy on successful runs, so hide it by default. // using a custom seed will also set this to true. fuzzDebugLog = false fuzzCustomSeed = 0 // override with non-zero seed to run just one test with that seed ) func TestAgainstRealRatelimit(t *testing.T) { // make sure to skip this test when checking coverage, as coverage it generates is not stable. // t.Skip("skipped to check coverage") const ( // number of [fuzzGranularity] events to trigger per round, // and also the number of [fuzzGranularity] periods before a token is added. // // each test takes at least fuzzGranularity*events*rounds, so keep it kinda small. events = 10 // number of rounds of "N events" to try rounds = 2 // less than events to give Wait a better chance of waiting, // but anything up to (events*rounds)/2 will have some effect. maxBurst = events / 2 ) // The mock ratelimiter should behave the same as a real ratelimiter in all cases. // // So fuzz test it: throw random calls at non-overlapping times that are a bit // spaced out (to prevent noise due to busy CPUs) and make sure all impls agree. // // If a test fails, please verify by hand with the failing seed. // Enabling debug logs can help show what's happening and what *should* be happening, // though there is a chance it's just due to CPU contention. t.Run("fuzz", func(t *testing.T) { deadline := time.Now().Add(fuzzDuration) for testnum := 0; !t.Failed() && time.Now().Before(deadline); testnum++ { if testnum > 0 && fuzzCustomSeed != 0 { // already ran the seeded test, stop early. break } t.Run(fmt.Sprintf("attempt %v", testnum), func(t *testing.T) { t.Cleanup(func() { if t.Failed() { /* Is this test becoming too slow or too noisy? The easiest "likely to actually work" fix is probably going to require detecting excessive lag, and retrying instead of failing. That could miss some real flaws if they are racy by nature, but seems like it might be good enough. */ t.Logf("---- CAUTION ----") t.Logf("these tests are randomized by design, a random failure may be a real flaw!") t.Logf("please replay with the failing seed (set `fuzzCustomSeed`) and check detailed output to see what the behavior should be.") t.Logf("also try setting `fuzzDebugLog` to false to true to show Tokens() progression, this can help explain many issues.") t.Logf("") t.Logf("if you are intentionally making changes, be sure to run a few hundred rounds to make sure your changes are stable,") t.Logf("and in particular make sure an 'Advancing mock time ...' event occurs and times match, as that is a bit rare") t.Logf("---- CAUTION ----") } }) // parallel saves a fair bit of time but introduces a lot of CPU contention // and that leads to a moderate amount of flakiness. // // unfortunately not recommended here. // t.Parallel() seed := time.Now().UnixNano() if fuzzCustomSeed != 0 { seed = fuzzCustomSeed // override seed to test a specific scenario } rng := rand.New(rand.NewSource(seed)) t.Logf("rand seed: %v", seed) round := makeRandomSchedule(t, rng, rounds, events) burst := rng.Intn(maxBurst) // zero is not very interesting, but allowed // waits do not usually need to pause and it's tough to assert randomly. // any waited periods are still asserted to be similar though. // the expected-wait case is tested separately below. noMinWait := time.Duration(0) assertRatelimitersBehaveSimilarly(t, burst, noMinWait, round) }) } }) // also check edge cases that random tests only rarely or never hit: t.Run("edge cases", func(t *testing.T) { // Wait at the end of a round until a token recovers at the start of the next. t.Run("wait must wait similarly", func(t *testing.T) { schedule := [][]string{ {"a", "_", "_", "w"}, // consume the only token, then wait with 0.75 tokens. {"_", "_", "_", "_"}, // first slot must be _ to recover a token and unblock the wait. } logSchedule(t, schedule) minWait := fuzzGranularity / 2 // not waiting will fail this test burst := 1 assertRatelimitersBehaveSimilarly(t, burst, minWait, schedule) }) t.Run("canceled contexts do not consume tokens", func(t *testing.T) { actual := rate.NewLimiter(rate.Every(time.Second), 1) wrapped := NewRatelimiter(rate.Every(time.Second), 1) canceledCtx, cancel := context.WithCancel(context.Background()) cancel() actualErr := actual.Wait(canceledCtx) actualTokens := actual.Tokens() wrappedErr := wrapped.Wait(canceledCtx) wrappedTokens := wrapped.Tokens() assert.ErrorIs(t, actualErr, context.Canceled) assert.ErrorIs(t, wrappedErr, context.Canceled) assert.Equal(t, 1.0, actualTokens, "rate.Limiter should still have a token available") assert.Equal(t, 1.0, wrappedTokens, "wrapped should still have a token available") }) t.Run("cancel while waiting returns tokens", func(t *testing.T) { // expect to recover 0.1 token after sleep actual := rate.NewLimiter(rate.Every(10*fuzzGranularity), 1) wrapped := NewRatelimiter(rate.Every(10*fuzzGranularity), 1) actual.Allow() wrapped.Allow() t.Logf("tokens in real: %0.5f, actual: %0.5f", actual.Tokens(), wrapped.Tokens()) assert.InDeltaf(t, 0, actual.Tokens(), 0.1, "rate.Limiter should have near zero tokens") assert.InDeltaf(t, 0, wrapped.Tokens(), 0.1, "wrapped should have near zero tokens") ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func() { time.Sleep(fuzzGranularity) cancel() }() var actualDur, wrappedDur time.Duration var actualErr, wrappedErr error var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() start := time.Now() actualErr = actual.Wait(ctx) actualDur = time.Since(start).Round(time.Millisecond) }() go func() { defer wg.Done() start := time.Now() wrappedErr = wrapped.Wait(ctx) wrappedDur = time.Since(start).Round(time.Millisecond) }() wg.Wait() assert.ErrorIs(t, actualErr, context.Canceled) assert.ErrorIs(t, wrappedErr, context.Canceled) // in particular, this would go to -1 if the token was not successfully returned. // that can happen if canceling near the wait time elapsing, but that would take a full second in this test. t.Logf("tokens in real: %0.5f, actual: %0.5f", actual.Tokens(), wrapped.Tokens()) assert.InDeltaf(t, 0.1, actual.Tokens(), 0.1, "rate.Limiter should have returned token when canceled, and the 1/10th sleep should remain") assert.InDeltaf(t, 0.1, wrapped.Tokens(), 0.1, "wrapped should have returned token when canceled, and the 1/10th sleep should remain") t.Logf("waiting times for real: %v, actual: %v", actualDur, wrappedDur) assert.InDeltaf(t, actualDur, fuzzGranularity, float64(fuzzGranularity/2), "rate.Limiter should have waited until canceled") assert.InDeltaf(t, wrappedDur, fuzzGranularity, float64(fuzzGranularity/2), "wrapped should have waited until canceled") }) }) } /* Generate a random schedule for testing behavior. Schedules look like: [r _ a w _ _ _ _ _ w] [_ c _ _ _ _ w _ _ _] Which means that the first round will: - reserve() - do nothing - allow() - wait() (may extend to next event, so the next one is always "_") - ... By the end of that first array it'll reach the end of the `rate.Every` time, and may recover tokens during the next round's runtime. So the second round will: - do nothing, just refresh .1 while waiting ==> will have 1.0 tokens regenerated since the test began, as it was reserved earlier (if non-zero burst). (this could be immediately consumed by last round's final wait) - reserve but cancel, restoring the token (if any), and maybe refresh .1 token - do nothing, maybe refresh .1 more token... - ... and try to wait on the 7th event. Non-blank events do not overlap between rounds to avoid triggering a race between the old consumed tokens being refreshed and a new call consuming them, as these make for very flaky tests unless time is mocked. */ func makeRandomSchedule(t *testing.T, rng *rand.Rand, rounds, events int) [][]string { // generate some non-colliding "A == perform an Allow call" rounds. schedule := make([][]string, rounds) // using string so it prints nicely for i := 0; i < rounds; i++ { round := make([]string, events) for j := 0; j < events; j++ { round[j] = "_" // "do nothing" marker } schedule[i] = round } skipWait := false for i := 0; i < len(schedule[0]); i++ { if skipWait { skipWait = false continue } // pick one to set to true, or none (1 chance for none) set := rng.Intn(len(schedule) + 1) if set < len(schedule) { // 1/3rd of them are waits switch rng.Intn(4) { case 0: schedule[set][i] = "a" // allow case 1: schedule[set][i] = "r" // reserve case 2: schedule[set][i] = "c" // reserve but cancel case 3: schedule[set][i] = "w" // wait // waits can pause for an event and a half, so skip the next event to avoid overlapping skipWait = true default: panic("missing a case") } } } logSchedule(t, schedule) return schedule } func logSchedule(t *testing.T, schedule [][]string) { t.Log("round setup:") for _, round := range schedule { t.Logf("\t%v", round) } } func debugLogf(t *testing.T, format string, args ...interface{}) { // by default be silent, but log if checking a specific seed or force-enabled. if fuzzDebugLog || fuzzCustomSeed != 0 { t.Helper() t.Logf(format, args...) } } func assertRatelimitersBehaveSimilarly(t *testing.T, burst int, minWaitDuration time.Duration, schedule [][]string) { rounds, eventsPerRound := len(schedule), len(schedule[0]) limit := rate.Every(fuzzGranularity * time.Duration(eventsPerRound)) // refresh after each full round t.Logf("limit: %v, burst: %v", fuzzGranularity, burst) initialNow := time.Now().Round(time.Millisecond) // easier to read when logging/debugging when rounded ts := NewMockedTimeSourceAt(initialNow) compressedTS := NewMockedTimeSourceAt(initialNow) actual := rate.NewLimiter(limit, burst) wrapped := NewRatelimiter(limit, burst) mocked := NewRateLimiterWithTimeSource(ts, limit, burst) compressed := NewRateLimiterWithTimeSource(compressedTS, limit, burst) // record "to be executed" closures on the time-compressed ratelimiter too, // so it can be checked at a sped up rate. // nil values mean "nothing to do". compressedReplay := make([][]func(t *testing.T), rounds) for i := range compressedReplay { compressedReplay[i] = make([]func(t *testing.T), eventsPerRound) } ticker := time.NewTicker(fuzzGranularity) defer ticker.Stop() for round := range schedule { round := round // for closure for event := range schedule[round] { event := event // for closure <-ticker.C ts.Advance(fuzzGranularity) // may also be advanced inside wait debugLogf(t, "Tokens before round, real: %0.2f, wrapped: %0.2f, mocked: %0.2f", actual.Tokens(), wrapped.Tokens(), mocked.Tokens()) switch schedule[round][event] { case "a": // call Allow on everything a, w, m := actual.Allow(), wrapped.Allow(), mocked.Allow() assertAllowsAgree(t, "Allow", round, event, a, w, m, nil) compressedReplay[round][event] = func(t *testing.T) { c := compressed.Allow() assertAllowsAgree(t, "Allow", round, event, a, w, m, &c) } case "r": // call Reserve on everything, don't cancel any we acquired successfully now := time.Now() _a, _w, _m := actual.ReserveN(now, 1), wrapped.Reserve(), mocked.Reserve() a, w, m := _a.OK() && _a.DelayFrom(now) == 0, _w.Allow(), _m.Allow() // un-reserve any that were not successful, we don't leave them dangling. if !a { _a.CancelAt(now) } _w.Used(w) _m.Used(m) assertAllowsAgree(t, "Reserve", round, event, a, w, m, nil) compressedReplay[round][event] = func(t *testing.T) { _c := compressed.Reserve() c := _c.Allow() _c.Used(c) assertAllowsAgree(t, "Reserve", round, event, a, w, m, &c) } case "c": // call Reserve on everything, and cancel them all now := time.Now() // needed for the real ratelimiter to cancel successfully like the wrapper does _a, _w, _m := actual.ReserveN(now, 1), wrapped.Reserve(), mocked.Reserve() _a.CancelAt(now) _w.Used(false) _m.Used(false) t.Logf("round[%v][%v] Cancel: canceled", round, event) compressedReplay[round][event] = func(t *testing.T) { compressed.Reserve().Used(false) t.Logf("round[%v][%v] Cancel: canceled", round, event) } case "w": /* Try a brief Wait on everything. ctx should expire in the middle of the *next* event, so all three possibilities are exercised: - sometimes unblocks immediately because a token is available - sometimes waits because a token will become available soon - sometimes unblocks immediately because a token will not become available in time you can see waits logged like this, they're somewhat rare though. otherwise wait times are ~0s: regular: ratelimiter_test.go:402: Wait elapsed: 20ms, wrapped err: ratelimiter_test.go:639: Advancing mock time by 20ms ratelimiter_test.go:402: Wait elapsed: 20ms, mocked err: ratelimiter_test.go:402: Wait elapsed: 20ms, actual err: ratelimiter_test.go:655: round[0][9] Wait: actual: true, wrapped: true, mocked: true, ratelimiter_test.go:455: Wait time: actual: 20ms, wrapped: 20ms, mocked: 20ms, compressed: ratelimiter_test.go:672: Advancing mock time by 20ms ratelimiter_test.go:402: Wait elapsed: 20ms, compressed err: ratelimiter_test.go:684: round[0][9] Wait: actual: true, wrapped: true, mocked: true, compressed: true ratelimiter_test.go:455: Wait time: actual: 20ms, wrapped: 20ms, mocked: 20ms, compressed: 20ms */ timeout := fuzzGranularity + (fuzzGranularity / 2) started := time.Now() // intentionally gathered outside the goroutines, to reveal goroutine-starting lag ctx, cancel := context.WithTimeout(context.Background(), timeout) a, w, m := false, false, false var aLatency, wLatency, mLatency time.Duration var g errgroup.Group g.Go(func() error { _a := actual.Wait(ctx) aLatency = time.Since(started).Round(time.Millisecond) debugLogf(t, "Wait elapsed: %v, actual err: %v", aLatency, _a) a = _a == nil return nil }) g.Go(func() error { _w := wrapped.Wait(ctx) wLatency = time.Since(started).Round(time.Millisecond) debugLogf(t, "Wait elapsed: %v, wrapped err: %v", wLatency, _w) w = _w == nil return nil }) g.Go(func() error { // mocked time needs something to advance it, or it'll block until timeout... // but don't advance time if it returned immediately, the others won't wait either. done := make(chan struct{}) go func() { select { case <-time.After(fuzzGranularity): t.Logf("Advancing mock time by %v", fuzzGranularity) ts.Advance(fuzzGranularity) case <-done: // do nothing, it is not waiting } }() _m := mocked.Wait(ctx) close(done) mLatency = time.Since(started).Round(time.Millisecond) debugLogf(t, "Wait elapsed: %v, mocked err: %v", mLatency, _m) m = _m == nil return nil }) _ = g.Wait() assertAllowsAgree(t, "Wait", round, event, a, w, m, nil) assertLatenciesSimilar(t, minWaitDuration, timeout, aLatency, wLatency, mLatency, nil) compressedReplay[round][event] = func(t *testing.T) { // need a mocked-time context, or the real deadline will not match the mocked deadline ctx := ×ourceContext{ ts: compressedTS, deadline: compressedTS.Now().Add(fuzzGranularity + (fuzzGranularity / 2)), } done := make(chan struct{}) go func() { // wait until blocked inside Wait compressedTS.BlockUntil(1) // sleep briefly to encourage close(done) if racing, or blocked on something else time.Sleep(fuzzGranularity / 10) select { case <-done: // do nothing, Wait did not block correctly. // this goroutine may leak if no blockers occur. it's "fine". default: // blocked inside Wait, advance time so it unblocks t.Logf("Advancing mock time by %v", fuzzGranularity) compressedTS.Advance(fuzzGranularity) } }() compressedStarted := compressedTS.Now() _c := compressed.Wait(ctx) close(done) cLatency := time.Since(started).Round(time.Millisecond) cLatency = compressedTS.Since(compressedStarted) debugLogf(t, "Wait elapsed: %v, compressed err: %v", cLatency, _c) c := _c == nil assertAllowsAgree(t, "Wait", round, event, a, w, m, &c) assertLatenciesSimilar(t, minWaitDuration, timeout, aLatency, wLatency, mLatency, &cLatency) } cancel() case "_": // do nothing default: panic("unhandled case: " + schedule[round][event]) } debugLogf(t, "Tokens after round, real: %0.2f, wrapped: %0.2f, mocked: %0.2f", actual.Tokens(), wrapped.Tokens(), mocked.Tokens()) } } t.Run("compressed time", func(t *testing.T) { // and now replay the compressed ratelimiter and make sure it matches too, // as time-compressed must behave the same as real-time. // // this is primarily intended to detect cases where real-time is accidentally used, // as ~zero time actually passes, which is quite different from the above tests. // // it's not perfect, but it does eventually notice such bugs, as you can see by // changing literally any timesource.Now() calls into time.Now() and running // tests a few times. // depending on the change it might not notice in most tests, but eventually a // problematic combination is triggered and can be replayed with the logged seed. for round := range schedule { for event := range schedule[round] { compressedTS.Advance(fuzzGranularity) debugLogf(t, "Tokens before compressed round: %0.2f", compressed.Tokens()) replay := compressedReplay[round][event] if replay != nil { replay(t) } debugLogf(t, "Tokens after compressed round: %0.2f", compressed.Tokens()) } } }) } // broken out to consts to help keep padding the same, for easier visual scanning const ( similarBehaviorLogComponents = "%-11s %-14s %-15s %-14s" // "{what:} {actual} {wrapped} {mocked}" with padding similarBehaviorLogNormalRound = "round[%v][%v] " + similarBehaviorLogComponents ) func assertAllowsAgree(t *testing.T, what string, round, event int, actual, wrapped, mocked bool, compressed *bool) { t.Helper() sWhat := what + ":" sActual := fmt.Sprintf("actual: %v,", actual) sWrapped := fmt.Sprintf("wrapped: %v,", wrapped) sMocked := fmt.Sprintf("mocked: %v,", mocked) // format strings should share padding so output aligns and it's easy to read. // unfortunately only strings do this nicely.s if compressed != nil { sCompressed := "compressed: " + fmt.Sprint(*compressed) t.Logf(similarBehaviorLogNormalRound+" %s", round, event, sWhat, sActual, sWrapped, sMocked, sCompressed) assert.True(t, actual == wrapped && wrapped == mocked && mocked == *compressed, "ratelimiters disagree") } else { t.Logf(similarBehaviorLogNormalRound, round, event, sWhat, sActual, sWrapped, sMocked) assert.True(t, actual == wrapped && wrapped == mocked, "ratelimiters disagree") } } func assertLatenciesSimilar(t *testing.T, mustBeGreaterThan, mustBeLessThan, actual, wrapped, mocked time.Duration, compressed *time.Duration) { t.Helper() // none are currently expected to wait, but if they do, they must not wait the full timeout. // this also helps handle small cpu stutter, as some may wait 1ms or so. maxLatency := maxDur(actual, wrapped, mocked) minLatency := minDur(actual, wrapped, mocked) compressedString := "" if compressed != nil { maxLatency = maxDur(maxLatency, *compressed) minLatency = minDur(minLatency, *compressed) compressedString = fmt.Sprintf("compressed: %v", *compressed) } assert.GreaterOrEqualf(t, minLatency, mustBeGreaterThan, "minimum latency must be within bounds, else waits did not wait long enough") assert.LessOrEqualf(t, maxLatency, mustBeLessThan, "maximum latency must not exceed timeout, it implies wait did not unblock when it should") // log format tries to align with assertAllowsAgree indent := strings.Repeat(" ", len("round[1][1]")) sActual := fmt.Sprintf("actual: %v,", actual) sWrapped := fmt.Sprintf("wrapped: %v,", wrapped) sMocked := fmt.Sprintf("mocked: %v,", mocked) t.Logf("%s "+similarBehaviorLogComponents+" %s", indent, "Wait time:", sActual, sWrapped, sMocked, compressedString) // asserting is... tough. long-ish noise latencies are somewhat common, // sometimes even exceeding 10ms to schedule a goroutine. // // if this becomes too noisy, just switch to logging, and check by hand // when making changes. if minLatency < fuzzGranularity/2 { // "no wait" test, assert that they are all under this fuzzGranularity assertNoWait := func(what string, wait time.Duration) { t.Helper() assert.LessOrEqual(t, wait, fuzzGranularity/2, "%v ratelimiter waited too long", what) } assertNoWait("actual", actual) assertNoWait("wrapped", wrapped) assertNoWait("mocked", mocked) if compressed != nil { assertNoWait("compressed", *compressed) } } else { // "wait" test, assert that they all waited, and none waited significantly longer assertWaited := func(what string, wait time.Duration) { t.Helper() assert.True(t, wait > fuzzGranularity/2 && wait < fuzzGranularity+(fuzzGranularity/2), "%v waited an incorrect amount of time, should be %v < %v <%v", what, fuzzGranularity/2, wait, fuzzGranularity+(fuzzGranularity/2)) } assertWaited("actual", actual) assertWaited("wrapped", wrapped) assertWaited("mocked", mocked) if compressed != nil { assertWaited("compressed", *compressed) } } } func maxDur(d time.Duration, ds ...time.Duration) time.Duration { for _, tmp := range ds { if tmp > d { d = tmp } } return d } func minDur(d time.Duration, ds ...time.Duration) time.Duration { for _, tmp := range ds { if tmp < d { d = tmp } } return d } ================================================ FILE: common/clock/ratelimiter_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/clock (interfaces: Reservation) // // Generated by this command: // // mockgen -package=clock -destination=ratelimiter_mock.go github.com/uber/cadence/common/clock Reservation // // Package clock is a generated GoMock package. package clock import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockReservation is a mock of Reservation interface. type MockReservation struct { ctrl *gomock.Controller recorder *MockReservationMockRecorder isgomock struct{} } // MockReservationMockRecorder is the mock recorder for MockReservation. type MockReservationMockRecorder struct { mock *MockReservation } // NewMockReservation creates a new mock instance. func NewMockReservation(ctrl *gomock.Controller) *MockReservation { mock := &MockReservation{ctrl: ctrl} mock.recorder = &MockReservationMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockReservation) EXPECT() *MockReservationMockRecorder { return m.recorder } // Allow mocks base method. func (m *MockReservation) Allow() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Allow") ret0, _ := ret[0].(bool) return ret0 } // Allow indicates an expected call of Allow. func (mr *MockReservationMockRecorder) Allow() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Allow", reflect.TypeOf((*MockReservation)(nil).Allow)) } // Used mocks base method. func (m *MockReservation) Used(wasUsed bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "Used", wasUsed) } // Used indicates an expected call of Used. func (mr *MockReservationMockRecorder) Used(wasUsed any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Used", reflect.TypeOf((*MockReservation)(nil).Used), wasUsed) } ================================================ FILE: common/clock/ratelimiter_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clock import ( "context" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/atomic" "golang.org/x/sync/errgroup" "golang.org/x/time/rate" ) func TestRatelimiter(t *testing.T) { t.Parallel() for _, name := range []string{"real", "mocked"} { name := name t.Run(name, func(t *testing.T) { t.Parallel() ts := func() MockedTimeSource { return nil } if name == "mocked" { ts = NewMockedTimeSource } // needs to be a bit coarser than the comparison tests, because time // is less tightly controlled / mock time relies on a background loop. granularity := 100 * time.Millisecond assertRatelimiterBasicsWork(t, ts, granularity) }) } } // check relatively basic properties of the ratelimiter wrapper, // and make sure that it behaves the same with both real time (makeTimesource returns nil) // and with mocked time (simulated by advancing mock-time in a background goroutine). // // broken out because it's just very deeply indented otherwise. func assertRatelimiterBasicsWork(t *testing.T, makeTimesource func() MockedTimeSource, granularity time.Duration) { // all "events" take place at 2*granularity event := granularity * 2 makeLimiter := func(ts MockedTimeSource, limit rate.Limit, burst int) (rl Ratelimiter, sleep func(time.Duration)) { if ts == nil { return NewRatelimiter(limit, burst), time.Sleep } return NewRateLimiterWithTimeSource(ts, limit, burst), ts.Advance } now := func(ts MockedTimeSource) time.Time { if ts == nil { return time.Now() } return ts.Now() } // creates a context that will time out with the passed timesource, or real time if nil contextWithTimeout := func(ts TimeSource, dur time.Duration) (ctx context.Context, cancel context.CancelFunc) { if ts == nil { // could be a timesourceContext with a real timesource, // but I'm avoiding that until it's fleshed out fully and tested separately. return context.WithTimeout(context.Background(), dur) } ctx = ×ourceContext{ ts: ts, deadline: ts.Now().Add(dur), } return ctx, func() {} } // advances mock time in the background until stopped, at [granularity/10] per millisecond tick. // currently this leads to tests that are about 10x faster than real time, but "faster" is not // a goal in this context. // // "different" is however good for making sure we don't rely on real time somehow, // so try to keep it either faster or slower, whatever makes for a decently non-flaky suite. // // flawed ratelimiter behavior when using mock time should also cause the fuzz test to fail // eventually, so failures here are, hopefully, only due to flawed tests or excessive noise. advanceTime := func(ts MockedTimeSource) (cleanup func()) { if ts == nil { return func() {} // nothing to do } stop, done := make(chan struct{}), make(chan struct{}) go func() { defer close(done) // this is not a great way to do mock-time tests, but it does automatically // work in just about all cases, without needing customization. // // ideally, in other kinds of tests, you should wait for ts.BlockUntil(..) // or some other event, and then advance time semi-precisely, and fall back // to this strategy only when that isn't usable for some reason. // // one extremely nice quality of mocked time though, even when done like this: // time simply pauses while debugging, and nothing "artificially" times out // due to being blocked, regardless of how long you let things sit. ticker := time.NewTicker(time.Millisecond) defer ticker.Stop() for { select { case <-stop: return case <-ticker.C: ts.Advance(granularity / 10) } } }() return func() { close(stop) <-done } } t.Run("sets burst and limit", func(t *testing.T) { t.Parallel() // should set on construction rl, _ := makeLimiter(makeTimesource(), rate.Every(time.Second), 3) assert.EqualValues(t, 3, rl.Burst()) assert.Equal(t, rate.Every(time.Second), rl.Limit()) // and when calling the setters rl.SetBurst(5) rl.SetLimit(rate.Every(time.Millisecond)) assert.EqualValues(t, 5, rl.Burst()) assert.Equal(t, rate.Every(time.Millisecond), rl.Limit()) }) t.Run("limits requests to within available burst tokens", func(t *testing.T) { t.Parallel() rl, _ := makeLimiter(makeTimesource(), rate.Every(time.Second), 3) assert.EqualValues(t, 3, rl.Tokens(), "should have tokens to allow exactly 3 calls, and not exceed burst of 3") assert.True(t, rl.Allow()) assert.InDeltaf(t, 2, rl.Tokens(), 0.1, "Allow should consume a token") assert.True(t, rl.Allow()) assert.InDelta(t, 1, rl.Tokens(), 0.1) assert.True(t, rl.Allow()) assert.InDelta(t, 0, rl.Tokens(), 0.1) assert.False(t, rl.Allow(), "should not allow after burstable tokens are consumed") assert.InDelta(t, 0, rl.Tokens(), 0.1) assert.False(t, rl.Allow()) }) t.Run("recovers tokens as time passes", func(t *testing.T) { t.Parallel() rl, sleep := makeLimiter(makeTimesource(), rate.Every(event), 1) assert.True(t, rl.Allow()) assert.False(t, rl.Allow()) sleep(event) assert.True(t, rl.Allow(), "should have recovered one token") assert.False(t, rl.Allow()) }) t.Run("waits until tokens are available", func(t *testing.T) { t.Parallel() ts := makeTimesource() rl, _ := makeLimiter(ts, rate.Every(event), 1) started := now(ts) // must be collected before Allow, or sleep time could legitimately be lower than the rate assert.True(t, rl.Allow()) assert.False(t, rl.Allow()) stopTime := advanceTime(ts) ctx, cancel := contextWithTimeout(ts, event) defer cancel() err := rl.Wait(ctx) stopTime() assert.NoError(t, err, "Wait should have waited and then allowed the event") elapsed := now(ts).Sub(started) assert.Truef(t, elapsed >= event-granularity && elapsed < event+granularity, "elapsed time outside bounds, should be %v <= %v < %v", event-granularity, elapsed, event+granularity) assert.False(t, rl.Allow(), "Wait must consume a token") }) t.Run("reserve", func(t *testing.T) { t.Parallel() // simple stuff for name, tc := range map[string]struct { do func(t *testing.T, rl Ratelimiter, sleep func(time.Duration)) tokenChange float64 }{ "consumes a token": { do: func(t *testing.T, rl Ratelimiter, sleep func(time.Duration)) { r := rl.Reserve() assert.True(t, r.Allow()) r.Used(true) }, tokenChange: -1, }, "restores a token when canceling": { do: func(t *testing.T, rl Ratelimiter, sleep func(time.Duration)) { r := rl.Reserve() assert.True(t, r.Allow()) r.Used(false) }, tokenChange: 0, }, "does not restore if there are interleaving calls": { do: func(t *testing.T, rl Ratelimiter, sleep func(time.Duration)) { r := rl.Reserve() assert.True(t, r.Allow()) sleep(1) // advance time by any amount rl.Allow() r.Used(false) }, tokenChange: -2, // reservation did not restore back to -1 }, "does not restore if Tokens is called in before canceling": { do: func(t *testing.T, rl Ratelimiter, sleep func(time.Duration)) { // to make a possibly-surprising quirk explicit: // tokens advances the limiter's "now" value, which means // canceling an allowed token does not work. r := rl.Reserve() assert.True(t, r.Allow()) sleep(1) // advance time by any amount rl.Tokens() // advance time internally r.Used(false) }, tokenChange: -1, // -1 due to Tokens call }, "does not go negative": { do: func(t *testing.T, rl Ratelimiter, sleep func(time.Duration)) { rl.Allow() rl.Allow() assert.InDeltaf(t, 0, rl.Tokens(), 0.1, "should have consumed all tokens") r := rl.Reserve() assert.False(t, r.Allow()) r.Used(false) }, tokenChange: -2, // would be -3 if it went negative }, } { name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() ts := makeTimesource() rl, sleep := makeLimiter(ts, rate.Every(event), 2) before := rl.Tokens() stopTime := advanceTime(ts) tc.do(t, rl, sleep) stopTime() after := rl.Tokens() assert.InDeltaf(t, tc.tokenChange, after-before, 0.1, "tokens should have changed from %v to %v, but was %v", before, before+tc.tokenChange, after) }) } }) t.Run("wait", func(t *testing.T) { t.Parallel() wait := func(ts MockedTimeSource, periods float64, rl Ratelimiter) int64 { ctx, cancel := contextWithTimeout(ts, time.Duration(periods*float64(event))) defer cancel() if rl.Wait(ctx) == nil { return 1 } return 0 } parWait := func(ts MockedTimeSource, periods float64, count *atomic.Int64, rl Ratelimiter) { if wait(ts, periods, rl) == 1 { count.Inc() } } for name, tc := range map[string]struct { drainFirst bool do func(t *testing.T, rl Ratelimiter, ts MockedTimeSource) int64 latency float64 // num of [granularity]s that should elapse allowed int64 tokenChange float64 allowLowerTokens bool // concurrent waits cannot be guaranteed to return tokens, negatives are possible }{ "allows immediately with free tokens": { drainFirst: false, do: func(t *testing.T, rl Ratelimiter, ts MockedTimeSource) int64 { return wait(ts, 3, rl) // anything longer than 1 }, allowed: 1, tokenChange: -1, }, "fails immediately with too-short timeout": { drainFirst: true, do: func(t *testing.T, rl Ratelimiter, ts MockedTimeSource) int64 { return wait(ts, 0.1, rl) }, latency: 0, allowed: 0, }, "waits for one token": { drainFirst: true, do: func(t *testing.T, rl Ratelimiter, ts MockedTimeSource) int64 { return wait(ts, 2, rl) }, latency: 1, allowed: 1, }, "concurrently waits for 2 tokens": { drainFirst: true, do: func(t *testing.T, rl Ratelimiter, ts MockedTimeSource) int64 { count := atomic.Int64{} var g errgroup.Group for i := 0; i < 2; i++ { g.Go(func() error { parWait(ts, 2.5, &count, rl) // slightly longer than 2 return nil }) } g.Wait() return count.Load() }, latency: 2, allowed: 2, }, "one wins and one fails when concurrently waiting": { drainFirst: true, do: func(t *testing.T, rl Ratelimiter, ts MockedTimeSource) int64 { count := atomic.Int64{} var g errgroup.Group for i := 0; i < 2; i++ { g.Go(func() error { parWait(ts, 1.5, &count, rl) // slightly longer than 1 return nil }) } g.Wait() // only one can win return count.Load() }, latency: 1, allowed: 1, allowLowerTokens: true, // tokens are not guaranteed to be returned }, "immediately drains 2, waits for 2, fails 2": { drainFirst: false, do: func(t *testing.T, rl Ratelimiter, ts MockedTimeSource) int64 { count := atomic.Int64{} var g errgroup.Group for i := 0; i < 6; i++ { g.Go(func() error { parWait(ts, 2.5, &count, rl) // slightly longer than 2, so 2 waiters succeed return nil }) } g.Wait() return count.Load() }, latency: 2, allowed: 2, tokenChange: -2, allowLowerTokens: true, // tokens are not guaranteed to be returned }, "cancel while waiting": { drainFirst: true, do: func(t *testing.T, rl Ratelimiter, ts MockedTimeSource) int64 { var wg sync.WaitGroup count := atomic.Int64{} wg.Add(2) ctx, cancel := context.WithTimeout(context.Background(), event*3/2) go func() { defer wg.Done() // a bit too much of a mess to thread this through nicely if ts == nil { <-time.After(event / 2) // cancel half way through the wait cancel() } else { ts.AfterFunc(event/2, func() { cancel() }) } }() go func() { defer wg.Done() if rl.Wait(ctx) == nil { count.Inc() } }() wg.Wait() return count.Load() }, allowed: 0, latency: 0.5, // unfortunately allows 0 currently, but token change should ensure time passed tokenChange: 0.5, // only waited for half an event -> recovered half a token }, } { name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() ts := makeTimesource() rl, _ := makeLimiter(ts, rate.Every(granularity*2), 2) if tc.drainFirst { assert.True(t, rl.Allow()) assert.True(t, rl.Allow()) assert.False(t, rl.Allow()) } before := rl.Tokens() start := now(ts) stopTime := advanceTime(ts) tc.do(t, rl, ts) stopTime() after := rl.Tokens() elapsed := now(ts).Sub(start) target := time.Duration(tc.latency * float64(granularity*2)) if tc.allowLowerTokens { // some tests can reasonably fail to restore a used token, so they can go lower than would be ideal assert.True(t, after-before <= tc.tokenChange+0.5, "tokens should have changed from %0.2f to ~%0.2f (or lower), but was %0.2f -> %0.2f", before, before+tc.tokenChange, before, after) } else { // tokens should be fairly precise (cpu noise can make this fuzzy) assert.InDeltaf(t, tc.tokenChange, after-before, 0.5, "tokens should have changed from %0.2f to %0.2f (+/- 0.5 for cpu noise), but was %0.2f -> %0.2f", before, before+tc.tokenChange, before, after) } assert.True(t, elapsed > (target-granularity) && elapsed < (target+granularity), "should have taken %v < (actual) %v < %v", target-granularity, elapsed, target+granularity, ) }) } }) } func TestRatelimiterCoverage(t *testing.T) { t.Run("time paradox", func(t *testing.T) { // this test's behavior should not be possible, as any reservation with // a time in the future must have been reserved in the future, which // would have advanced the internal latestNow to match that future-time. rl := NewRatelimiter(rate.Every(time.Second), 1) impl := rl.(*ratelimiter) r := rl.Reserve() latestNow := impl.latestNow require.True(t, r.Allow()) rimpl := r.(*allowedReservation) // cheat, put the reservation into the future. rimpl.reservedAt = time.Now().Add(time.Second) rimpl.Used(false) assert.True(t, latestNow.Before(impl.latestNow), "ratelimiter-internal time should have advanced despite impossible sequence") }) t.Run("zero burst", func(t *testing.T) { // we do not currently make use of this, but it's allowed by the API. rl := NewRatelimiter(rate.Every(time.Second), 0) ctx, cancel := context.WithTimeout(context.Background(), time.Hour) // much longer than the rate defer cancel() started := time.Now() assert.Error(t, rl.Wait(ctx), "0 burst should never allow events, so it should fail immediately") elapsed := time.Since(started) assert.Less(t, elapsed, time.Second/10, "Wait should have returned almost immediately on impossible waits") }) t.Run("mock limiter constructor", func(t *testing.T) { // covered by fuzz testing, but this gets it to 100% without fuzz. _ = NewRateLimiterWithTimeSource(NewMockedTimeSource(), 1, 1) }) } // context which becomes Done() based on a TimeSource instead of real time type timesourceContext struct { // does not contain a parent context as we currently have no need, // but a "real" one would for forwarding Value and deadline lookups. ts TimeSource deadline time.Time mut sync.Mutex } func (t *timesourceContext) Deadline() (deadline time.Time, ok bool) { t.mut.Lock() defer t.mut.Unlock() return t.deadline, !t.deadline.IsZero() } func (t *timesourceContext) Done() <-chan struct{} { // not currently expected to be used, but it would look like this: t.mut.Lock() defer t.mut.Unlock() c := make(chan struct{}) delay := t.deadline.Sub(t.ts.Now()) t.ts.AfterFunc(delay, func() { // this stack may leak if time is not advanced past it in tests. close(c) }) return c } func (t *timesourceContext) Err() error { t.mut.Lock() defer t.mut.Unlock() if t.ts.Now().After(t.deadline) { return context.DeadlineExceeded } return nil } func (t *timesourceContext) Value(key any) any { panic("unimplemented") } var _ context.Context = (*timesourceContext)(nil) ================================================ FILE: common/clock/real_timer_benchmark_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clock import ( "testing" "time" ) func BenchmarkRealTimerGate(b *testing.B) { timer := NewTimerGate(NewRealTimeSource()) for i := 0; i < b.N; i++ { timer.Update(time.Now()) } } ================================================ FILE: common/clock/sustain.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clock import "time" // Sustain tracks whether a boolean value is consistently true over a dynamic duration. It does this by recording the earliest // time that it received a true value, and clearing that timestamp any time that a false value is encountered. // The timestamp is only initialized when a true datapoint is accepted. type Sustain struct { started time.Time source TimeSource duration func() time.Duration } func NewSustain(source TimeSource, duration func() time.Duration) Sustain { return Sustain{ source: source, duration: duration, } } // Check accepts a datapoint and returns true if the condition has been sustained. For example, if the duration was // 60s, and a true datapoint was accepted every second, it would return false until 60s had elapsed from the first datapoint // and then subsequently return true. func (s *Sustain) Check(value bool) bool { if value { now := s.source.Now() if s.started.IsZero() { s.started = now } if now.Sub(s.started) >= s.duration() { return true } } else { s.Reset() } return false } // CheckAndReset accepts a datapoint and returns true if the condition has been sustained. // If the condition has been sustained the timestamp is set to the current time so that it will be considered sustained // again until after the duration again elapses. // For example, if the duration was 60s, and a true datapoint was accepted every second, it would return true once every 60s and // otherwise return false func (s *Sustain) CheckAndReset(value bool) bool { if value { now := s.source.Now() if s.started.IsZero() { s.started = now } if now.Sub(s.started) >= s.duration() { s.started = now return true } } else { s.Reset() } return false } // Reset clears the datapoints that the Sustain has received. It is equivalent to providing a false datapoint func (s *Sustain) Reset() { s.started = time.Time{} } ================================================ FILE: common/clock/sustain_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clock import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) type check struct { seconds int value bool } func TestCheckAndReset(t *testing.T) { cases := []struct { name string duration time.Duration calls []check expected []bool }{ { name: "simple case", duration: time.Second * 10, calls: []check{ {0, true}, {10, true}, }, expected: []bool{ false, true, }, }, { name: "intermediate successes", duration: 10 * time.Second, calls: []check{ {0, true}, {2, true}, {2, true}, {2, true}, {2, true}, {2, true}, }, expected: []bool{ false, false, false, false, false, true, }, }, { name: "resets after success", duration: time.Second * 10, calls: []check{ {0, true}, {10, true}, {0, true}, }, expected: []bool{ false, true, false, }, }, { name: "resets after false", duration: time.Second * 10, calls: []check{ {0, true}, {1, false}, {1, true}, {9, true}, {1, true}, }, expected: []bool{ false, false, false, false, true, }, }, { name: "duration = 0", duration: 0, calls: []check{ {0, true}, }, expected: []bool{ true, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { clock := NewMockedTimeSource() sus := NewSustain(clock, func() time.Duration { return tc.duration }) require.Equal(t, len(tc.calls), len(tc.expected)) for i, c := range tc.calls { expected := tc.expected[i] clock.Advance(time.Duration(c.seconds) * time.Second) actual := sus.CheckAndReset(c.value) assert.Equal(t, expected, actual, "check %d", i) } }) } } func TestCheck(t *testing.T) { cases := []struct { name string duration time.Duration calls []check expected []bool }{ { name: "simple case", duration: time.Second * 10, calls: []check{ {0, true}, {10, true}, }, expected: []bool{ false, true, }, }, { name: "intermediate successes", duration: 10 * time.Second, calls: []check{ {0, true}, {2, true}, {2, true}, {2, true}, {2, true}, {2, true}, }, expected: []bool{ false, false, false, false, false, true, }, }, { name: "stays after success", duration: time.Second * 10, calls: []check{ {0, true}, {10, true}, {0, true}, }, expected: []bool{ false, true, true, }, }, { name: "resets after false", duration: time.Second * 10, calls: []check{ {0, true}, {1, false}, {1, true}, {9, true}, {1, true}, }, expected: []bool{ false, false, false, false, true, }, }, { name: "duration = 0", duration: 0, calls: []check{ {0, true}, }, expected: []bool{ true, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { clock := NewMockedTimeSource() sus := NewSustain(clock, func() time.Duration { return tc.duration }) require.Equal(t, len(tc.calls), len(tc.expected)) for i, c := range tc.calls { expected := tc.expected[i] clock.Advance(time.Duration(c.seconds) * time.Second) actual := sus.Check(c.value) assert.Equal(t, expected, actual, "check %d", i) } }) } } ================================================ FILE: common/clock/testdata/amd_linux_go1.22.txt ================================================ // On a giant cloud machine with 96 cores (shared machine, possibly noisy but I tried to pick a quiet period and reran until no obvious outliers): ❯ go version go version go1.22.1 linux/amd64 ❯ go test -bench . -test.run xxx -cpu 1,2,4,8,32,128 . goos: linux goarch: amd64 pkg: github.com/uber/cadence/common/clock cpu: AMD EPYC 7B13 BenchmarkLimiter/allow/serial/real 10606200 113.4 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 25.57µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 258ns ratelimiter_test.go:116: allowed: 2125, denied: 7875, time per allow: 531ns ratelimiter_test.go:116: allowed: 114089, denied: 885911, time per allow: 991ns ratelimiter_test.go:116: allowed: 1203727, denied: 9402473, time per allow: 999ns BenchmarkLimiter/allow/serial/real-2 10615315 113.9 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real-2 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 16.63µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 263ns ratelimiter_test.go:116: allowed: 2144, denied: 7856, time per allow: 536ns ratelimiter_test.go:116: allowed: 113999, denied: 886001, time per allow: 991ns ratelimiter_test.go:116: allowed: 1210150, denied: 9405165, time per allow: 999ns BenchmarkLimiter/allow/serial/real-4 10230478 113.4 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real-4 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 44.18µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 395ns ratelimiter_test.go:116: allowed: 2161, denied: 7839, time per allow: 542ns ratelimiter_test.go:116: allowed: 118221, denied: 881779, time per allow: 991ns ratelimiter_test.go:116: allowed: 1161201, denied: 9069277, time per allow: 999ns BenchmarkLimiter/allow/serial/real-8 10573476 113.5 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real-8 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 31.64µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 326ns ratelimiter_test.go:116: allowed: 2130, denied: 7870, time per allow: 534ns ratelimiter_test.go:116: allowed: 114434, denied: 885566, time per allow: 991ns ratelimiter_test.go:116: allowed: 1200860, denied: 9372616, time per allow: 999ns BenchmarkLimiter/allow/serial/real-32 10499926 113.6 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real-32 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 23.54µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 753ns ratelimiter_test.go:116: allowed: 2132, denied: 7868, time per allow: 540ns ratelimiter_test.go:116: allowed: 115211, denied: 884789, time per allow: 991ns ratelimiter_test.go:116: allowed: 1193395, denied: 9306531, time per allow: 999ns BenchmarkLimiter/allow/serial/real-128 10586818 113.4 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real-128 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 32.12µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 337ns ratelimiter_test.go:116: allowed: 2126, denied: 7874, time per allow: 535ns ratelimiter_test.go:116: allowed: 114293, denied: 885707, time per allow: 991ns ratelimiter_test.go:116: allowed: 1201247, denied: 9385571, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped 7489098 162.3 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 29.53µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 276ns ratelimiter_test.go:116: allowed: 2589, denied: 7411, time per allow: 616ns ratelimiter_test.go:116: allowed: 161196, denied: 838804, time per allow: 993ns ratelimiter_test.go:116: allowed: 1216236, denied: 6272862, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped-2 7691356 155.2 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped-2 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 36.03µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 634ns ratelimiter_test.go:116: allowed: 2569, denied: 7431, time per allow: 620ns ratelimiter_test.go:116: allowed: 156978, denied: 843022, time per allow: 993ns ratelimiter_test.go:116: allowed: 1194731, denied: 6496625, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped-4 7584391 157.7 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped-4 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 35.56µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 350ns ratelimiter_test.go:116: allowed: 2592, denied: 7408, time per allow: 623ns ratelimiter_test.go:116: allowed: 159164, denied: 840836, time per allow: 993ns ratelimiter_test.go:116: allowed: 1197000, denied: 6387391, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped-8 7750646 155.4 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped-8 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 35.15µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 393ns ratelimiter_test.go:116: allowed: 2696, denied: 7304, time per allow: 635ns ratelimiter_test.go:116: allowed: 155786, denied: 844214, time per allow: 993ns ratelimiter_test.go:116: allowed: 1205406, denied: 6545240, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped-32 7739062 154.5 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped-32 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 31.51µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 384ns ratelimiter_test.go:116: allowed: 2571, denied: 7429, time per allow: 614ns ratelimiter_test.go:116: allowed: 156018, denied: 843982, time per allow: 993ns ratelimiter_test.go:116: allowed: 1196945, denied: 6542117, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped-128 7452973 157.4 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped-128 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 34.23µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 368ns ratelimiter_test.go:116: allowed: 2880, denied: 7120, time per allow: 659ns ratelimiter_test.go:116: allowed: 161964, denied: 838036, time per allow: 993ns ratelimiter_test.go:116: allowed: 1173671, denied: 6279302, time per allow: 999ns BenchmarkLimiter/allow/serial/mocked_timesource 9300446 130.8 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 25.28µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 247ns ratelimiter_test.go:116: allowed: 2999, denied: 7001, time per allow: 399ns ratelimiter_test.go:116: allowed: 200999, denied: 799001, time per allow: 641ns ratelimiter_test.go:116: allowed: 1861089, denied: 7439357, time per allow: 653ns BenchmarkLimiter/allow/serial/mocked_timesource-2 9821623 120.8 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource-2 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 14.53µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 288ns ratelimiter_test.go:116: allowed: 2999, denied: 7001, time per allow: 411ns ratelimiter_test.go:116: allowed: 200999, denied: 799001, time per allow: 607ns ratelimiter_test.go:116: allowed: 1965324, denied: 7856299, time per allow: 603ns BenchmarkLimiter/allow/serial/mocked_timesource-4 9834667 122.7 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource-4 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 31.39µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 375ns ratelimiter_test.go:116: allowed: 2999, denied: 7001, time per allow: 439ns ratelimiter_test.go:116: allowed: 200999, denied: 799001, time per allow: 606ns ratelimiter_test.go:116: allowed: 1967933, denied: 7866734, time per allow: 613ns BenchmarkLimiter/allow/serial/mocked_timesource-8 8564794 128.5 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource-8 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 28.07µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 598ns ratelimiter_test.go:116: allowed: 2999, denied: 7001, time per allow: 483ns ratelimiter_test.go:116: allowed: 200999, denied: 799001, time per allow: 696ns ratelimiter_test.go:116: allowed: 1713958, denied: 6850836, time per allow: 642ns BenchmarkLimiter/allow/serial/mocked_timesource-32 9999607 121.6 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource-32 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 19.14µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 371ns ratelimiter_test.go:116: allowed: 2999, denied: 7001, time per allow: 538ns ratelimiter_test.go:116: allowed: 200999, denied: 799001, time per allow: 596ns ratelimiter_test.go:116: allowed: 2000921, denied: 7998686, time per allow: 607ns BenchmarkLimiter/allow/serial/mocked_timesource-128 9952394 121.5 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource-128 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 26.591µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 307ns ratelimiter_test.go:116: allowed: 2999, denied: 7001, time per allow: 451ns ratelimiter_test.go:116: allowed: 200999, denied: 799001, time per allow: 599ns ratelimiter_test.go:116: allowed: 1991478, denied: 7960916, time per allow: 607ns BenchmarkLimiter/allow/parallel/real 10205740 117.5 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 155.67µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 256ns ratelimiter_test.go:138: allowed: 2167, denied: 7833, time per allow: 541ns ratelimiter_test.go:138: allowed: 118534, denied: 881466, time per allow: 991ns ratelimiter_test.go:138: allowed: 1199932, denied: 9005808, time per allow: 999ns BenchmarkLimiter/allow/parallel/real-2 9410480 135.1 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real-2 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 57.2µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 421ns ratelimiter_test.go:138: allowed: 2461, denied: 7539, time per allow: 597ns ratelimiter_test.go:138: allowed: 155677, denied: 844323, time per allow: 992ns ratelimiter_test.go:138: allowed: 995318, denied: 6771672, time per allow: 995ns ratelimiter_test.go:138: allowed: 1276963, denied: 8133517, time per allow: 995ns BenchmarkLimiter/allow/parallel/real-4 11752964 124.5 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real-4 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 40.32µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 494ns ratelimiter_test.go:138: allowed: 3014, denied: 6986, time per allow: 534ns ratelimiter_test.go:138: allowed: 106056, denied: 893944, time per allow: 962ns ratelimiter_test.go:138: allowed: 1497530, denied: 10255434, time per allow: 977ns BenchmarkLimiter/allow/parallel/real-8 6841635 170.6 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real-8 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 65.389µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 462ns ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 223ns ratelimiter_test.go:138: allowed: 965185, denied: 34815, time per allow: 235ns ratelimiter_test.go:138: allowed: 3257342, denied: 2028365, time per allow: 284ns ratelimiter_test.go:138: allowed: 3769827, denied: 3071808, time per allow: 309ns BenchmarkLimiter/allow/parallel/real-32 3867985 311.2 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real-32 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 77.99µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 780ns ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 335ns ratelimiter_test.go:138: allowed: 999737, denied: 263, time per allow: 310ns ratelimiter_test.go:138: allowed: 3866778, denied: 1207, time per allow: 311ns BenchmarkLimiter/allow/parallel/real-128 3643759 322.4 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real-128 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 140.1µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.225µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 342ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 329ns ratelimiter_test.go:138: allowed: 3643553, denied: 206, time per allow: 322ns BenchmarkLimiter/allow/parallel/wrapped 7118395 166.9 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 138.249µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 455ns ratelimiter_test.go:138: allowed: 2588, denied: 7412, time per allow: 616ns ratelimiter_test.go:138: allowed: 169513, denied: 830487, time per allow: 994ns ratelimiter_test.go:138: allowed: 1189205, denied: 5929190, time per allow: 999ns BenchmarkLimiter/allow/parallel/wrapped-2 6918020 172.9 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped-2 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 49.24µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.206µs ratelimiter_test.go:138: allowed: 2713, denied: 7287, time per allow: 640ns ratelimiter_test.go:138: allowed: 174391, denied: 825609, time per allow: 994ns ratelimiter_test.go:138: allowed: 1197180, denied: 5720840, time per allow: 999ns BenchmarkLimiter/allow/parallel/wrapped-4 6418586 189.7 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped-4 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 35.67µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 870ns ratelimiter_test.go:138: allowed: 2960, denied: 7040, time per allow: 669ns ratelimiter_test.go:138: allowed: 187814, denied: 812186, time per allow: 994ns ratelimiter_test.go:138: allowed: 1218744, denied: 5199842, time per allow: 999ns BenchmarkLimiter/allow/parallel/wrapped-8 5855096 209.8 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped-8 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 61.67µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.039µs ratelimiter_test.go:138: allowed: 2878, denied: 7122, time per allow: 660ns ratelimiter_test.go:138: allowed: 205806, denied: 794194, time per allow: 995ns ratelimiter_test.go:138: allowed: 1229495, denied: 4625601, time per allow: 999ns BenchmarkLimiter/allow/parallel/wrapped-32 4693095 285.9 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped-32 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 46.33µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.325µs ratelimiter_test.go:138: allowed: 3945, denied: 6055, time per allow: 760ns ratelimiter_test.go:138: allowed: 256641, denied: 743359, time per allow: 996ns ratelimiter_test.go:138: allowed: 1342685, denied: 3350410, time per allow: 999ns BenchmarkLimiter/allow/parallel/wrapped-128 3859207 321.0 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped-128 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 118.79µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.673µs ratelimiter_test.go:138: allowed: 3188, denied: 6812, time per allow: 698ns ratelimiter_test.go:138: allowed: 311877, denied: 688123, time per allow: 996ns ratelimiter_test.go:138: allowed: 1239635, denied: 2619572, time per allow: 999ns BenchmarkLimiter/allow/parallel/mocked_timesource 8854556 134.4 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 160.4µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 332ns ratelimiter_test.go:138: allowed: 2999, denied: 7001, time per allow: 395ns ratelimiter_test.go:138: allowed: 200999, denied: 799001, time per allow: 674ns ratelimiter_test.go:138: allowed: 1771911, denied: 7082645, time per allow: 671ns BenchmarkLimiter/allow/parallel/mocked_timesource-2 8279631 151.5 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource-2 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 65.36µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 697ns ratelimiter_test.go:138: allowed: 2999, denied: 7001, time per allow: 618ns ratelimiter_test.go:138: allowed: 200999, denied: 799001, time per allow: 720ns ratelimiter_test.go:138: allowed: 1656926, denied: 6622705, time per allow: 757ns BenchmarkLimiter/allow/parallel/mocked_timesource-4 6189608 214.9 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource-4 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 52.91µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 882ns ratelimiter_test.go:138: allowed: 2999, denied: 7001, time per allow: 741ns ratelimiter_test.go:138: allowed: 200999, denied: 799001, time per allow: 964ns ratelimiter_test.go:138: allowed: 1238921, denied: 4950687, time per allow: 1.073µs BenchmarkLimiter/allow/parallel/mocked_timesource-8 3798624 333.2 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource-8 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 62.1µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 757ns ratelimiter_test.go:138: allowed: 2999, denied: 7001, time per allow: 1.229µs ratelimiter_test.go:138: allowed: 200999, denied: 799001, time per allow: 1.571µs ratelimiter_test.go:138: allowed: 760724, denied: 3037900, time per allow: 1.663µs BenchmarkLimiter/allow/parallel/mocked_timesource-32 3555322 384.3 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource-32 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 61.69µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 737ns ratelimiter_test.go:138: allowed: 2999, denied: 7001, time per allow: 1.268µs ratelimiter_test.go:138: allowed: 200999, denied: 799001, time per allow: 1.679µs ratelimiter_test.go:138: allowed: 712064, denied: 2843258, time per allow: 1.918µs BenchmarkLimiter/allow/parallel/mocked_timesource-128 3936543 305.4 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource-128 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 160.99µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 2.424µs ratelimiter_test.go:138: allowed: 2999, denied: 7001, time per allow: 1.717µs ratelimiter_test.go:138: allowed: 200999, denied: 799001, time per allow: 1.516µs ratelimiter_test.go:138: allowed: 788308, denied: 3148235, time per allow: 1.525µs BenchmarkLimiter/reserve-canceling-every-3/parallel/real 3808436 314.2 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 665ns ratelimiter_test.go:138: allowed: 2173, denied: 7827, time per allow: 1.364µs ratelimiter_test.go:138: allowed: 154369, denied: 845631, time per allow: 2.04µs ratelimiter_test.go:138: allowed: 576628, denied: 3231808, time per allow: 2.075µs BenchmarkLimiter/reserve-canceling-every-3/parallel/real-2 3794132 350.9 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real-2 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.116µs ratelimiter_test.go:138: allowed: 2542, denied: 7458, time per allow: 1.064µs ratelimiter_test.go:138: allowed: 241564, denied: 758436, time per allow: 1.309µs ratelimiter_test.go:138: allowed: 1008362, denied: 2785770, time per allow: 1.32µs BenchmarkLimiter/reserve-canceling-every-3/parallel/real-4 3830700 309.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real-4 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.112µs ratelimiter_test.go:138: allowed: 5345, denied: 4655, time per allow: 552ns ratelimiter_test.go:138: allowed: 532052, denied: 467948, time per allow: 588ns ratelimiter_test.go:138: allowed: 2016383, denied: 1814317, time per allow: 588ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real-8 3584832 343.9 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real-8 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 64, denied: 36, time per allow: 3.189µs ratelimiter_test.go:138: allowed: 6665, denied: 3335, time per allow: 533ns ratelimiter_test.go:138: allowed: 666662, denied: 333338, time per allow: 502ns ratelimiter_test.go:138: allowed: 2389749, denied: 1195083, time per allow: 515ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real-32 2424110 513.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real-32 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.58µs ratelimiter_test.go:138: allowed: 6666, denied: 3334, time per allow: 651ns ratelimiter_test.go:138: allowed: 666655, denied: 333345, time per allow: 742ns ratelimiter_test.go:138: allowed: 1616063, denied: 808047, time per allow: 770ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real-128 2093779 541.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real-128 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.696µs ratelimiter_test.go:138: allowed: 6623, denied: 3377, time per allow: 778ns ratelimiter_test.go:138: allowed: 666621, denied: 333379, time per allow: 859ns ratelimiter_test.go:138: allowed: 1395828, denied: 697951, time per allow: 812ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time 7565743 160.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 514ns ratelimiter_test.go:138: allowed: 2560, denied: 7440, time per allow: 612ns ratelimiter_test.go:138: allowed: 159529, denied: 840471, time per allow: 993ns ratelimiter_test.go:138: allowed: 1211482, denied: 6354261, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-2 5678437 200.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-2 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.277µs ratelimiter_test.go:138: allowed: 5500, denied: 4500, time per allow: 477ns ratelimiter_test.go:138: allowed: 329228, denied: 670772, time per allow: 641ns ratelimiter_test.go:138: allowed: 1771314, denied: 3907123, time per allow: 643ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-4 5894012 204.5 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-4 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 65, denied: 35, time per allow: 1.045µs ratelimiter_test.go:138: allowed: 6666, denied: 3334, time per allow: 341ns ratelimiter_test.go:138: allowed: 608898, denied: 391102, time per allow: 334ns ratelimiter_test.go:138: allowed: 3627237, denied: 2266775, time per allow: 332ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-8 4104926 245.3 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-8 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.087µs ratelimiter_test.go:138: allowed: 6666, denied: 3334, time per allow: 419ns ratelimiter_test.go:138: allowed: 666664, denied: 333336, time per allow: 438ns ratelimiter_test.go:138: allowed: 2736617, denied: 1368309, time per allow: 367ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-32 2771666 441.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-32 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 63, denied: 37, time per allow: 1.475µs ratelimiter_test.go:138: allowed: 6655, denied: 3345, time per allow: 693ns ratelimiter_test.go:138: allowed: 666666, denied: 333334, time per allow: 649ns ratelimiter_test.go:138: allowed: 1847767, denied: 923899, time per allow: 661ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-128 2853688 433.9 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-128 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 3.034µs ratelimiter_test.go:138: allowed: 6610, denied: 3390, time per allow: 618ns ratelimiter_test.go:138: allowed: 666626, denied: 333374, time per allow: 630ns ratelimiter_test.go:138: allowed: 1902376, denied: 951312, time per allow: 650ns BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped 4191378 292.4 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.19µs ratelimiter_test.go:138: allowed: 3852, denied: 6148, time per allow: 745ns ratelimiter_test.go:138: allowed: 287256, denied: 712744, time per allow: 996ns ratelimiter_test.go:138: allowed: 1226213, denied: 2965165, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-2 4106936 306.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-2 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.504µs ratelimiter_test.go:138: allowed: 4018, denied: 5982, time per allow: 758ns ratelimiter_test.go:138: allowed: 292848, denied: 707152, time per allow: 997ns ratelimiter_test.go:138: allowed: 1256480, denied: 2850456, time per allow: 1µs BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-4 3492135 331.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-4 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 65, denied: 35, time per allow: 1.614µs ratelimiter_test.go:138: allowed: 6049, denied: 3951, time per allow: 844ns ratelimiter_test.go:138: allowed: 342411, denied: 657589, time per allow: 1.003µs ratelimiter_test.go:138: allowed: 1150167, denied: 2341968, time per allow: 1.004µs BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-8 3619214 319.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-8 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 62, denied: 38, time per allow: 1.637µs ratelimiter_test.go:138: allowed: 4660, denied: 5340, time per allow: 826ns ratelimiter_test.go:138: allowed: 328449, denied: 671551, time per allow: 1.009µs ratelimiter_test.go:138: allowed: 1138671, denied: 2480543, time per allow: 1.013µs BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-32 3563878 352.9 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-32 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 58, denied: 42, time per allow: 2.592µs ratelimiter_test.go:138: allowed: 5067, denied: 4933, time per allow: 862ns ratelimiter_test.go:138: allowed: 332094, denied: 667906, time per allow: 1.013µs ratelimiter_test.go:138: allowed: 1229827, denied: 2334051, time per allow: 1.022µs BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-128 2696084 456.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-128 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 54, denied: 46, time per allow: 4.346µs ratelimiter_test.go:138: allowed: 4590, denied: 5410, time per allow: 855ns ratelimiter_test.go:138: allowed: 421401, denied: 578599, time per allow: 1.056µs ratelimiter_test.go:138: allowed: 1164520, denied: 1531564, time per allow: 1.055µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource 4711965 265.2 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.099µs ratelimiter_test.go:138: allowed: 1999, denied: 8001, time per allow: 1.256µs ratelimiter_test.go:138: allowed: 100999, denied: 899001, time per allow: 2.521µs ratelimiter_test.go:138: allowed: 472196, denied: 4239769, time per allow: 2.646µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-2 4082649 298.2 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-2 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.448µs ratelimiter_test.go:138: allowed: 1995, denied: 8005, time per allow: 1.502µs ratelimiter_test.go:138: allowed: 100853, denied: 899147, time per allow: 2.914µs ratelimiter_test.go:138: allowed: 408711, denied: 3673938, time per allow: 2.978µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-4 3961304 281.9 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-4 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.933µs ratelimiter_test.go:138: allowed: 1986, denied: 8014, time per allow: 2.219µs ratelimiter_test.go:138: allowed: 100626, denied: 899374, time per allow: 3.01µs ratelimiter_test.go:138: allowed: 395937, denied: 3565367, time per allow: 2.82µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-8 3600322 347.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-8 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 63, denied: 37, time per allow: 2.513µs ratelimiter_test.go:138: allowed: 1935, denied: 8065, time per allow: 2.113µs ratelimiter_test.go:138: allowed: 100133, denied: 899867, time per allow: 3.328µs ratelimiter_test.go:138: allowed: 358211, denied: 3242111, time per allow: 3.487µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-32 3664026 320.3 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-32 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 50, denied: 50, time per allow: 3.632µs ratelimiter_test.go:138: allowed: 1952, denied: 8048, time per allow: 2.115µs ratelimiter_test.go:138: allowed: 100075, denied: 899925, time per allow: 3.272µs ratelimiter_test.go:138: allowed: 364912, denied: 3299114, time per allow: 3.216µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-128 3662464 331.5 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-128 ratelimiter_test.go:138: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:138: allowed: 66, denied: 34, time per allow: 1.47µs ratelimiter_test.go:138: allowed: 1961, denied: 8039, time per allow: 1.737µs ratelimiter_test.go:138: allowed: 99630, denied: 900370, time per allow: 3.288µs ratelimiter_test.go:138: allowed: 362096, denied: 3300368, time per allow: 3.353µs BenchmarkLimiter/reserve-canceling-every-3/serial/real 3881887 310.9 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 549ns ratelimiter_test.go:116: allowed: 2175, denied: 7825, time per allow: 1.331µs ratelimiter_test.go:116: allowed: 149973, denied: 850027, time per allow: 2.061µs ratelimiter_test.go:116: allowed: 587105, denied: 3294782, time per allow: 2.055µs BenchmarkLimiter/reserve-canceling-every-3/serial/real-2 4098498 292.2 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real-2 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 551ns ratelimiter_test.go:116: allowed: 2114, denied: 7886, time per allow: 1.348µs ratelimiter_test.go:116: allowed: 136905, denied: 863095, time per allow: 2.138µs ratelimiter_test.go:116: allowed: 558606, denied: 3539892, time per allow: 2.144µs BenchmarkLimiter/reserve-canceling-every-3/serial/real-4 4114698 291.8 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real-4 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 932ns ratelimiter_test.go:116: allowed: 2148, denied: 7852, time per allow: 1.365µs ratelimiter_test.go:116: allowed: 135966, denied: 864034, time per allow: 2.144µs ratelimiter_test.go:116: allowed: 558237, denied: 3556461, time per allow: 2.151µs BenchmarkLimiter/reserve-canceling-every-3/serial/real-8 4095794 292.6 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real-8 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 548ns ratelimiter_test.go:116: allowed: 2228, denied: 7772, time per allow: 1.332µs ratelimiter_test.go:116: allowed: 138214, denied: 861786, time per allow: 2.119µs ratelimiter_test.go:116: allowed: 562747, denied: 3533047, time per allow: 2.129µs BenchmarkLimiter/reserve-canceling-every-3/serial/real-32 4068602 294.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real-32 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 673ns ratelimiter_test.go:116: allowed: 2095, denied: 7905, time per allow: 1.363µs ratelimiter_test.go:116: allowed: 140538, denied: 859462, time per allow: 2.098µs ratelimiter_test.go:116: allowed: 564976, denied: 3503626, time per allow: 2.122µs BenchmarkLimiter/reserve-canceling-every-3/serial/real-128 3990260 295.2 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real-128 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 1.239µs ratelimiter_test.go:116: allowed: 2184, denied: 7816, time per allow: 1.355µs ratelimiter_test.go:116: allowed: 144378, denied: 855622, time per allow: 2.082µs ratelimiter_test.go:116: allowed: 556167, denied: 3434093, time per allow: 2.117µs BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time 7891219 154.4 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 354ns ratelimiter_test.go:116: allowed: 2459, denied: 7541, time per allow: 594ns ratelimiter_test.go:116: allowed: 153042, denied: 846958, time per allow: 993ns ratelimiter_test.go:116: allowed: 1219275, denied: 6671944, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-2 7883937 153.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-2 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 470ns ratelimiter_test.go:116: allowed: 2565, denied: 7435, time per allow: 613ns ratelimiter_test.go:116: allowed: 153151, denied: 846849, time per allow: 993ns ratelimiter_test.go:116: allowed: 1212499, denied: 6671438, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-4 7756005 155.4 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-4 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 393ns ratelimiter_test.go:116: allowed: 2477, denied: 7523, time per allow: 599ns ratelimiter_test.go:116: allowed: 155664, denied: 844336, time per allow: 993ns ratelimiter_test.go:116: allowed: 1206085, denied: 6549920, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-8 7378713 158.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-8 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 408ns ratelimiter_test.go:116: allowed: 2484, denied: 7516, time per allow: 600ns ratelimiter_test.go:116: allowed: 163573, denied: 836427, time per allow: 994ns ratelimiter_test.go:116: allowed: 1166468, denied: 6212245, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-32 7624196 155.5 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-32 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 462ns ratelimiter_test.go:116: allowed: 2488, denied: 7512, time per allow: 601ns ratelimiter_test.go:116: allowed: 158338, denied: 841662, time per allow: 993ns ratelimiter_test.go:116: allowed: 1186770, denied: 6437426, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-128 7776684 153.1 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-128 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 444ns ratelimiter_test.go:116: allowed: 2488, denied: 7512, time per allow: 602ns ratelimiter_test.go:116: allowed: 155239, denied: 844761, time per allow: 993ns ratelimiter_test.go:116: allowed: 1191589, denied: 6585095, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped 4251330 286.8 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 560ns ratelimiter_test.go:116: allowed: 3599, denied: 6401, time per allow: 723ns ratelimiter_test.go:116: allowed: 283228, denied: 716772, time per allow: 996ns ratelimiter_test.go:116: allowed: 1220218, denied: 3031112, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-2 4503495 263.3 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-2 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 1.426µs ratelimiter_test.go:116: allowed: 4001, denied: 5999, time per allow: 755ns ratelimiter_test.go:116: allowed: 267416, denied: 732584, time per allow: 996ns ratelimiter_test.go:116: allowed: 1186958, denied: 3316537, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-4 4751306 261.2 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-4 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 1.246µs ratelimiter_test.go:116: allowed: 3583, denied: 6417, time per allow: 723ns ratelimiter_test.go:116: allowed: 253528, denied: 746472, time per allow: 996ns ratelimiter_test.go:116: allowed: 1242170, denied: 3509136, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-8 4372312 259.6 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-8 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 1.262µs ratelimiter_test.go:116: allowed: 3813, denied: 6187, time per allow: 740ns ratelimiter_test.go:116: allowed: 275414, denied: 724586, time per allow: 996ns ratelimiter_test.go:116: allowed: 1135920, denied: 3236392, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-32 4645706 256.3 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-32 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 838ns ratelimiter_test.go:116: allowed: 3748, denied: 6252, time per allow: 739ns ratelimiter_test.go:116: allowed: 259264, denied: 740736, time per allow: 996ns ratelimiter_test.go:116: allowed: 1191440, denied: 3454266, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-128 4710174 271.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-128 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 856ns ratelimiter_test.go:116: allowed: 3704, denied: 6296, time per allow: 733ns ratelimiter_test.go:116: allowed: 255734, denied: 744266, time per allow: 996ns ratelimiter_test.go:116: allowed: 1280804, denied: 3429370, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource 4923451 259.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 698ns ratelimiter_test.go:116: allowed: 1999, denied: 8001, time per allow: 1.203µs ratelimiter_test.go:116: allowed: 100999, denied: 899001, time per allow: 2.413µs ratelimiter_test.go:116: allowed: 493344, denied: 4430107, time per allow: 2.585µs BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-2 5527620 220.8 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-2 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 865ns ratelimiter_test.go:116: allowed: 1999, denied: 8001, time per allow: 1.321µs ratelimiter_test.go:116: allowed: 100999, denied: 899001, time per allow: 2.148µs ratelimiter_test.go:116: allowed: 553761, denied: 4973859, time per allow: 2.203µs BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-4 5319728 225.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-4 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 532ns ratelimiter_test.go:116: allowed: 1999, denied: 8001, time per allow: 1.162µs ratelimiter_test.go:116: allowed: 100999, denied: 899001, time per allow: 2.233µs ratelimiter_test.go:116: allowed: 532972, denied: 4786756, time per allow: 2.245µs BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-8 5014401 251.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-8 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 888ns ratelimiter_test.go:116: allowed: 1999, denied: 8001, time per allow: 1.217µs ratelimiter_test.go:116: allowed: 100999, denied: 899001, time per allow: 2.369µs ratelimiter_test.go:116: allowed: 502439, denied: 4511962, time per allow: 2.504µs BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-32 4355498 256.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-32 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 1.19µs ratelimiter_test.go:116: allowed: 1999, denied: 8001, time per allow: 1.388µs ratelimiter_test.go:116: allowed: 100999, denied: 899001, time per allow: 2.727µs ratelimiter_test.go:116: allowed: 436549, denied: 3918949, time per allow: 2.56µs BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-128 4664314 264.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-128 ratelimiter_test.go:116: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:116: allowed: 66, denied: 34, time per allow: 1.458µs ratelimiter_test.go:116: allowed: 1999, denied: 8001, time per allow: 1.817µs ratelimiter_test.go:116: allowed: 100999, denied: 899001, time per allow: 2.547µs ratelimiter_test.go:116: allowed: 467431, denied: 4196883, time per allow: 2.641µs BenchmarkLimiter/wait/serial/2ns_rate/real 1721722 735.7 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 33.8µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 831ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 654ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 696ns ratelimiter_test.go:116: allowed: 1721722, denied: 0, time per allow: 735ns BenchmarkLimiter/wait/serial/2ns_rate/real-2 1980352 650.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real-2 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 40.31µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.036µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 662ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 605ns ratelimiter_test.go:116: allowed: 1980352, denied: 0, time per allow: 650ns BenchmarkLimiter/wait/serial/2ns_rate/real-4 2035285 583.1 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real-4 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 25.06µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 942ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 643ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 589ns ratelimiter_test.go:116: allowed: 2035285, denied: 0, time per allow: 583ns BenchmarkLimiter/wait/serial/2ns_rate/real-8 1996000 629.1 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real-8 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 23.88µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.231µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 687ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 601ns ratelimiter_test.go:116: allowed: 1996000, denied: 0, time per allow: 629ns BenchmarkLimiter/wait/serial/2ns_rate/real-32 1934670 613.5 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real-32 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 24.9µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.185µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 666ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 620ns ratelimiter_test.go:116: allowed: 1934670, denied: 0, time per allow: 613ns BenchmarkLimiter/wait/serial/2ns_rate/real-128 1978052 613.3 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real-128 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 27.87µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.681µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 695ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 606ns ratelimiter_test.go:116: allowed: 1978052, denied: 0, time per allow: 613ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped 1572546 777.9 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 18.33µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 695ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 850ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 763ns ratelimiter_test.go:116: allowed: 1572546, denied: 0, time per allow: 777ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped-2 1827685 663.1 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped-2 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 46.22µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 890ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 692ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 656ns ratelimiter_test.go:116: allowed: 1827685, denied: 0, time per allow: 663ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped-4 1847516 634.6 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped-4 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 26.329µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 919ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 732ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 649ns ratelimiter_test.go:116: allowed: 1847516, denied: 0, time per allow: 634ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped-8 1892594 648.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped-8 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 24.38µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 853ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 730ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 633ns ratelimiter_test.go:116: allowed: 1892594, denied: 0, time per allow: 648ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped-32 1866420 643.1 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped-32 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 23.23µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.197µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 729ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 642ns ratelimiter_test.go:116: allowed: 1866420, denied: 0, time per allow: 643ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped-128 1860192 665.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped-128 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 23.49µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.444µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 738ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 645ns ratelimiter_test.go:116: allowed: 1860192, denied: 0, time per allow: 665ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource 3480636 338.0 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 20.771µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 621ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 296ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 344ns ratelimiter_test.go:116: allowed: 3480636, denied: 0, time per allow: 337ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-2 4025022 310.7 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-2 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 11.83µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.034µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 295ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 298ns ratelimiter_test.go:116: allowed: 4025022, denied: 0, time per allow: 310ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-4 3879685 311.0 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-4 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 30.59µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 798ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 329ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 309ns ratelimiter_test.go:116: allowed: 3879685, denied: 0, time per allow: 310ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-8 3954216 302.7 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-8 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 15.32µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 522ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 561ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 303ns ratelimiter_test.go:116: allowed: 3954216, denied: 0, time per allow: 302ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-32 3992751 311.0 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-32 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 15.98µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.062µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 298ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 300ns ratelimiter_test.go:116: allowed: 3992751, denied: 0, time per allow: 311ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-128 3994308 305.8 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-128 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 17.84µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 567ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 305ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 300ns ratelimiter_test.go:116: allowed: 3994308, denied: 0, time per allow: 305ns BenchmarkLimiter/wait/serial/1µs_rate/real 1000000 1030 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 36.19µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 749ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 913ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 1.03µs BenchmarkLimiter/wait/serial/1µs_rate/real-2 1000000 1008 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real-2 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 21.62µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.063µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 982ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 1.007µs BenchmarkLimiter/wait/serial/1µs_rate/real-4 1000000 1013 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real-4 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 23.92µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.27µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 912ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 1.012µs BenchmarkLimiter/wait/serial/1µs_rate/real-8 1000000 1010 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real-8 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 18.09µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.997µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 901ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 1.01µs BenchmarkLimiter/wait/serial/1µs_rate/real-32 1000000 1023 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real-32 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 18.8µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 2.092µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 917ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 1.022µs BenchmarkLimiter/wait/serial/1µs_rate/real-128 1000000 1012 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real-128 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 25.05µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 970ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 914ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 1.012µs BenchmarkLimiter/wait/serial/1µs_rate/wrapped 1153466 1029 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 29.46µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.157µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 1.557µs ratelimiter_test.go:116: allowed: 768654, denied: 0, time per allow: 1.04µs ratelimiter_test.go:116: allowed: 1153466, denied: 0, time per allow: 1.028µs BenchmarkLimiter/wait/serial/1µs_rate/wrapped-2 1000000 1010 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped-2 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 21.451µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 851ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 910ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 1.009µs BenchmarkLimiter/wait/serial/1µs_rate/wrapped-4 1000000 1008 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped-4 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 19.57µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.042µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 923ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 1.007µs BenchmarkLimiter/wait/serial/1µs_rate/wrapped-8 985587 1016 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped-8 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 20.13µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 2.425µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 1.214µs ratelimiter_test.go:116: allowed: 985587, denied: 0, time per allow: 1.016µs BenchmarkLimiter/wait/serial/1µs_rate/wrapped-32 1000000 1009 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped-32 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 37.11µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.453µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 913ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 1.009µs BenchmarkLimiter/wait/serial/1µs_rate/wrapped-128 1000000 1008 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped-128 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 21.431µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 1.652µs ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 938ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 1.008µs BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource 3296186 350.3 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 16.94µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 425ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 287ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 364ns ratelimiter_test.go:116: allowed: 3296186, denied: 0, time per allow: 350ns BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-2 3759115 305.0 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-2 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 23.92µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 825ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 415ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 319ns ratelimiter_test.go:116: allowed: 3759115, denied: 0, time per allow: 305ns BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-4 3970958 295.3 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-4 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 15.64µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 566ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 303ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 302ns ratelimiter_test.go:116: allowed: 3970958, denied: 0, time per allow: 295ns BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-8 4056230 301.3 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-8 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 14.15µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 632ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 310ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 295ns ratelimiter_test.go:116: allowed: 4056230, denied: 0, time per allow: 301ns BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-32 4013587 303.8 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-32 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 15.94µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 706ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 304ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 298ns ratelimiter_test.go:116: allowed: 4013587, denied: 0, time per allow: 303ns BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-128 3777873 300.7 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-128 ratelimiter_test.go:116: allowed: 1, denied: 0, time per allow: 16.33µs ratelimiter_test.go:116: allowed: 100, denied: 0, time per allow: 880ns ratelimiter_test.go:116: allowed: 10000, denied: 0, time per allow: 309ns ratelimiter_test.go:116: allowed: 1000000, denied: 0, time per allow: 317ns ratelimiter_test.go:116: allowed: 3777873, denied: 0, time per allow: 300ns BenchmarkLimiter/wait/parallel/2ns_rate/real 1606105 743.1 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 88.94µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 910ns ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 693ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 747ns ratelimiter_test.go:138: allowed: 1606105, denied: 0, time per allow: 743ns BenchmarkLimiter/wait/parallel/2ns_rate/real-2 2684506 594.9 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real-2 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 32.24µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 611ns ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 481ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 446ns ratelimiter_test.go:138: allowed: 2684506, denied: 0, time per allow: 594ns BenchmarkLimiter/wait/parallel/2ns_rate/real-4 2008572 607.9 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real-4 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 41.57µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.732µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 557ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 597ns ratelimiter_test.go:138: allowed: 2008572, denied: 0, time per allow: 607ns BenchmarkLimiter/wait/parallel/2ns_rate/real-8 2382729 570.9 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real-8 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 59.23µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 2.044µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 681ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 607ns ratelimiter_test.go:138: allowed: 1975958, denied: 0, time per allow: 503ns ratelimiter_test.go:138: allowed: 2382729, denied: 0, time per allow: 570ns BenchmarkLimiter/wait/parallel/2ns_rate/real-32 1412924 856.2 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real-32 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 135.94µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.709µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 800ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 849ns ratelimiter_test.go:138: allowed: 1412924, denied: 0, time per allow: 856ns BenchmarkLimiter/wait/parallel/2ns_rate/real-128 1227754 974.7 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real-128 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 86.34µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 2.269µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 975ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 977ns ratelimiter_test.go:138: allowed: 1227754, denied: 0, time per allow: 974ns BenchmarkLimiter/wait/parallel/2ns_rate/wrapped 1599991 780.7 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 101.84µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.018µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 1.044µs ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 749ns ratelimiter_test.go:138: allowed: 1599991, denied: 0, time per allow: 780ns BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-2 1609435 728.1 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-2 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 59.3µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 2.021µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 869ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 745ns ratelimiter_test.go:138: allowed: 1609435, denied: 0, time per allow: 728ns BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-4 1874695 693.3 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-4 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 36.2µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.42µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 688ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 640ns ratelimiter_test.go:138: allowed: 1874695, denied: 0, time per allow: 693ns BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-8 1358745 907.4 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-8 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 105.66µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.552µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 681ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 883ns ratelimiter_test.go:138: allowed: 1358745, denied: 0, time per allow: 907ns BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-32 1000000 1170 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-32 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 159.77µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 2.318µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 1.045µs ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 1.169µs BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-128 876781 1165 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-128 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 143.331µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 3.529µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 1.365µs ratelimiter_test.go:138: allowed: 876781, denied: 0, time per allow: 1.165µs BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource 2869887 360.1 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 123.22µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 553ns ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 318ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 418ns ratelimiter_test.go:138: allowed: 2869887, denied: 0, time per allow: 360ns BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-2 2966229 460.6 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-2 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 53.44µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 789ns ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 346ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 404ns ratelimiter_test.go:138: allowed: 2966229, denied: 0, time per allow: 460ns BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-4 2462452 560.8 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-4 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 68.69µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.374µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 599ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 487ns ratelimiter_test.go:138: allowed: 2462452, denied: 0, time per allow: 560ns BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-8 1838635 639.1 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-8 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 71.33µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 3.539µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 865ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 652ns ratelimiter_test.go:138: allowed: 1838635, denied: 0, time per allow: 639ns BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-32 1778676 689.3 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-32 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 82.09µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 2.509µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 795ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 674ns ratelimiter_test.go:138: allowed: 1778676, denied: 0, time per allow: 689ns BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-128 1928882 584.5 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-128 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 187.11µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 3.614µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 656ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 622ns ratelimiter_test.go:138: allowed: 1928882, denied: 0, time per allow: 584ns BenchmarkLimiter/wait/parallel/1µs_rate/real 1000000 1025 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 190.51µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 917ns ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 982ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 1.025µs BenchmarkLimiter/wait/parallel/1µs_rate/real-2 1314758 929.6 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real-2 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 56.4µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.13µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 774ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 912ns ratelimiter_test.go:138: allowed: 1314758, denied: 0, time per allow: 929ns BenchmarkLimiter/wait/parallel/1µs_rate/real-4 2196931 560.5 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real-4 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 39.84µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.296µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 524ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 546ns ratelimiter_test.go:138: allowed: 2196931, denied: 0, time per allow: 560ns BenchmarkLimiter/wait/parallel/1µs_rate/real-8 1964460 589.9 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real-8 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 60.08µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.316µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 562ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 610ns ratelimiter_test.go:138: allowed: 1964460, denied: 0, time per allow: 589ns BenchmarkLimiter/wait/parallel/1µs_rate/real-32 1600582 799.6 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real-32 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 61.42µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.34µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 770ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 749ns ratelimiter_test.go:138: allowed: 1600582, denied: 0, time per allow: 799ns BenchmarkLimiter/wait/parallel/1µs_rate/real-128 1240540 982.5 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real-128 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 171.79µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 2.062µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 865ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 967ns ratelimiter_test.go:138: allowed: 1240540, denied: 0, time per allow: 982ns BenchmarkLimiter/wait/parallel/1µs_rate/wrapped 1000000 1030 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 104.34µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 875ns ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 1µs ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 1.03µs BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-2 1000000 1006 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-2 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 55.1µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.365µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 960ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 1.006µs BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-4 1000000 1007 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-4 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 57.911µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.387µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 999ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 1.006µs BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-8 1000000 1009 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-8 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 44.7µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 2.105µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 929ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 1.008µs BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-32 1000000 1021 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-32 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 48.83µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 2.674µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 1.078µs ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 1.02µs BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-128 1000000 1081 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-128 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 115.78µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 3.145µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 1.023µs ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 1.08µs BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource 3340384 366.2 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 80.65µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 594ns ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 291ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 359ns ratelimiter_test.go:138: allowed: 3340384, denied: 0, time per allow: 366ns BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-2 3445034 368.1 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-2 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 43.04µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 892ns ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 460ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 348ns ratelimiter_test.go:138: allowed: 3445034, denied: 0, time per allow: 368ns BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-4 2553564 473.7 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-4 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 37.44µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.371µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 568ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 469ns ratelimiter_test.go:138: allowed: 2553564, denied: 0, time per allow: 473ns BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-8 1935856 607.9 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-8 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 68.55µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.884µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 717ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 619ns ratelimiter_test.go:138: allowed: 1935856, denied: 0, time per allow: 607ns BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-32 1874793 575.2 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-32 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 61.94µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 3.209µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 741ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 640ns ratelimiter_test.go:138: allowed: 1874793, denied: 0, time per allow: 575ns BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-128 1944561 599.0 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-128 ratelimiter_test.go:138: allowed: 1, denied: 0, time per allow: 179.579µs ratelimiter_test.go:138: allowed: 100, denied: 0, time per allow: 1.716µs ratelimiter_test.go:138: allowed: 10000, denied: 0, time per allow: 595ns ratelimiter_test.go:138: allowed: 1000000, denied: 0, time per allow: 617ns ratelimiter_test.go:138: allowed: 1944561, denied: 0, time per allow: 598ns PASS ok github.com/uber/cadence/common/clock 239.738s ================================================ FILE: common/clock/testdata/m1_mac_go1.21.txt ================================================ // On an M1 Pro Mac, 8 cores (4 high, 4 low): ❯ go version go version go1.21.9 darwin/arm64 ❯ go test -bench . -test.run xxx -cpu 1,2,4,8,32 . goos: darwin goarch: arm64 pkg: github.com/uber/cadence/common/clock BenchmarkLimiter/allow/serial/real 13040902 77.94 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 94.833µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 264ns ratelimiter_test.go:114: allowed: 2461, denied: 7539, time per allow: 602ns ratelimiter_test.go:114: allowed: 92982, denied: 907018, time per allow: 989ns ratelimiter_test.go:114: allowed: 1017345, denied: 12023557, time per allow: 999ns BenchmarkLimiter/allow/serial/real-2 15258253 78.99 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real-2 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 12.75µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 148ns ratelimiter_test.go:114: allowed: 1780, denied: 8220, time per allow: 441ns ratelimiter_test.go:114: allowed: 79617, denied: 920383, time per allow: 987ns ratelimiter_test.go:114: allowed: 1206105, denied: 14052148, time per allow: 999ns BenchmarkLimiter/allow/serial/real-4 15349381 81.67 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real-4 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 13.875µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 274ns ratelimiter_test.go:114: allowed: 1798, denied: 8202, time per allow: 446ns ratelimiter_test.go:114: allowed: 79149, denied: 920851, time per allow: 987ns ratelimiter_test.go:114: allowed: 1238332, denied: 14111049, time per allow: 1.012µs BenchmarkLimiter/allow/serial/real-8 15338174 77.89 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real-8 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 10.875µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 210ns ratelimiter_test.go:114: allowed: 1793, denied: 8207, time per allow: 445ns ratelimiter_test.go:114: allowed: 79210, denied: 920790, time per allow: 987ns ratelimiter_test.go:114: allowed: 1195634, denied: 14142540, time per allow: 999ns BenchmarkLimiter/allow/serial/real-32 15305487 77.85 ns/op --- BENCH: BenchmarkLimiter/allow/serial/real-32 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 16.334µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 361ns ratelimiter_test.go:114: allowed: 1780, denied: 8220, time per allow: 441ns ratelimiter_test.go:114: allowed: 79367, denied: 920633, time per allow: 987ns ratelimiter_test.go:114: allowed: 1192482, denied: 14113005, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped 10489392 116.3 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 16.625µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 232ns ratelimiter_test.go:114: allowed: 2192, denied: 7808, time per allow: 546ns ratelimiter_test.go:114: allowed: 115384, denied: 884616, time per allow: 991ns ratelimiter_test.go:114: allowed: 1220378, denied: 9269014, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped-2 10763562 112.3 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped-2 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 12.583µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 180ns ratelimiter_test.go:114: allowed: 2155, denied: 7845, time per allow: 537ns ratelimiter_test.go:114: allowed: 112471, denied: 887529, time per allow: 991ns ratelimiter_test.go:114: allowed: 1209814, denied: 9553748, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped-4 10674687 111.7 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped-4 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 8.834µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 195ns ratelimiter_test.go:114: allowed: 2152, denied: 7848, time per allow: 537ns ratelimiter_test.go:114: allowed: 113396, denied: 886604, time per allow: 991ns ratelimiter_test.go:114: allowed: 1193581, denied: 9481106, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped-8 10722634 111.9 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped-8 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 9.083µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 253ns ratelimiter_test.go:114: allowed: 2148, denied: 7852, time per allow: 535ns ratelimiter_test.go:114: allowed: 112895, denied: 887105, time per allow: 991ns ratelimiter_test.go:114: allowed: 1201251, denied: 9521383, time per allow: 999ns BenchmarkLimiter/allow/serial/wrapped-32 10658955 116.4 ns/op --- BENCH: BenchmarkLimiter/allow/serial/wrapped-32 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 39.166µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 260ns ratelimiter_test.go:114: allowed: 2158, denied: 7842, time per allow: 541ns ratelimiter_test.go:114: allowed: 113562, denied: 886438, time per allow: 991ns ratelimiter_test.go:114: allowed: 1231568, denied: 9427387, time per allow: 1.007µs BenchmarkLimiter/allow/serial/mocked_timesource 12198974 103.4 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 17µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 142ns ratelimiter_test.go:114: allowed: 2999, denied: 7001, time per allow: 329ns ratelimiter_test.go:114: allowed: 200999, denied: 799001, time per allow: 489ns ratelimiter_test.go:114: allowed: 2440794, denied: 9758180, time per allow: 516ns BenchmarkLimiter/allow/serial/mocked_timesource-2 12352620 96.20 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource-2 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 8.584µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 600ns ratelimiter_test.go:114: allowed: 2999, denied: 7001, time per allow: 320ns ratelimiter_test.go:114: allowed: 200999, denied: 799001, time per allow: 483ns ratelimiter_test.go:114: allowed: 2471523, denied: 9881097, time per allow: 480ns BenchmarkLimiter/allow/serial/mocked_timesource-4 12307213 96.72 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource-4 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 7.167µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 377ns ratelimiter_test.go:114: allowed: 2999, denied: 7001, time per allow: 323ns ratelimiter_test.go:114: allowed: 200999, denied: 799001, time per allow: 485ns ratelimiter_test.go:114: allowed: 2462442, denied: 9844771, time per allow: 483ns BenchmarkLimiter/allow/serial/mocked_timesource-8 12366156 96.46 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource-8 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 8.291µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 217ns ratelimiter_test.go:114: allowed: 2999, denied: 7001, time per allow: 331ns ratelimiter_test.go:114: allowed: 200999, denied: 799001, time per allow: 482ns ratelimiter_test.go:114: allowed: 2474231, denied: 9891925, time per allow: 482ns BenchmarkLimiter/allow/serial/mocked_timesource-32 12335895 96.76 ns/op --- BENCH: BenchmarkLimiter/allow/serial/mocked_timesource-32 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 18.792µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 324ns ratelimiter_test.go:114: allowed: 2999, denied: 7001, time per allow: 318ns ratelimiter_test.go:114: allowed: 200999, denied: 799001, time per allow: 483ns ratelimiter_test.go:114: allowed: 2468178, denied: 9867717, time per allow: 483ns BenchmarkLimiter/allow/parallel/real 14847120 80.67 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 54.083µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 164ns ratelimiter_test.go:136: allowed: 1809, denied: 8191, time per allow: 450ns ratelimiter_test.go:136: allowed: 81791, denied: 918209, time per allow: 987ns ratelimiter_test.go:136: allowed: 1198579, denied: 13648541, time per allow: 999ns BenchmarkLimiter/allow/parallel/real-2 11729421 91.31 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real-2 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 23.167µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 1.147µs ratelimiter_test.go:136: allowed: 1880, denied: 8120, time per allow: 476ns ratelimiter_test.go:136: allowed: 103270, denied: 896730, time per allow: 990ns ratelimiter_test.go:136: allowed: 1072367, denied: 10657054, time per allow: 998ns BenchmarkLimiter/allow/parallel/real-4 9522936 120.2 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real-4 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 21.334µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 604ns ratelimiter_test.go:136: allowed: 3052, denied: 6948, time per allow: 534ns ratelimiter_test.go:136: allowed: 127279, denied: 872721, time per allow: 989ns ratelimiter_test.go:136: allowed: 1148459, denied: 8374477, time per allow: 996ns BenchmarkLimiter/allow/parallel/real-8 6435804 170.1 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real-8 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 13.209µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 299ns ratelimiter_test.go:136: allowed: 3708, denied: 6292, time per allow: 450ns ratelimiter_test.go:136: allowed: 572085, denied: 427915, time per allow: 325ns ratelimiter_test.go:136: allowed: 3756916, denied: 2678888, time per allow: 291ns BenchmarkLimiter/allow/parallel/real-32 4673179 235.7 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/real-32 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 25.458µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 482ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 255ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 256ns ratelimiter_test.go:136: allowed: 3639089, denied: 1034090, time per allow: 302ns BenchmarkLimiter/allow/parallel/wrapped 10528147 116.5 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 31.833µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 206ns ratelimiter_test.go:136: allowed: 2121, denied: 7879, time per allow: 529ns ratelimiter_test.go:136: allowed: 114956, denied: 885044, time per allow: 991ns ratelimiter_test.go:136: allowed: 1227259, denied: 9300888, time per allow: 999ns BenchmarkLimiter/allow/parallel/wrapped-2 7672480 155.8 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped-2 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 18.958µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 219ns ratelimiter_test.go:136: allowed: 2663, denied: 7337, time per allow: 629ns ratelimiter_test.go:136: allowed: 157380, denied: 842620, time per allow: 993ns ratelimiter_test.go:136: allowed: 1196181, denied: 6476299, time per allow: 999ns BenchmarkLimiter/allow/parallel/wrapped-4 4856625 244.5 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped-4 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 22.083µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 298ns ratelimiter_test.go:136: allowed: 3294, denied: 6706, time per allow: 700ns ratelimiter_test.go:136: allowed: 248057, denied: 751943, time per allow: 996ns ratelimiter_test.go:136: allowed: 1188577, denied: 3668048, time per allow: 999ns BenchmarkLimiter/allow/parallel/wrapped-8 4135431 279.6 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped-8 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 15.541µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 347ns ratelimiter_test.go:136: allowed: 3936, denied: 6064, time per allow: 748ns ratelimiter_test.go:136: allowed: 291147, denied: 708853, time per allow: 996ns ratelimiter_test.go:136: allowed: 1157034, denied: 2978397, time per allow: 999ns BenchmarkLimiter/allow/parallel/wrapped-32 2850973 447.8 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/wrapped-32 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 22.959µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 384ns ratelimiter_test.go:136: allowed: 5235, denied: 4765, time per allow: 811ns ratelimiter_test.go:136: allowed: 421876, denied: 578124, time per allow: 997ns ratelimiter_test.go:136: allowed: 1277734, denied: 1573239, time per allow: 999ns BenchmarkLimiter/allow/parallel/mocked_timesource 11951947 104.4 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 39.042µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 179ns ratelimiter_test.go:136: allowed: 2999, denied: 7001, time per allow: 331ns ratelimiter_test.go:136: allowed: 200999, denied: 799001, time per allow: 499ns ratelimiter_test.go:136: allowed: 2391389, denied: 9560558, time per allow: 522ns BenchmarkLimiter/allow/parallel/mocked_timesource-2 7483614 158.0 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource-2 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 21.125µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 254ns ratelimiter_test.go:136: allowed: 2999, denied: 7001, time per allow: 559ns ratelimiter_test.go:136: allowed: 200999, denied: 799001, time per allow: 1.062µs ratelimiter_test.go:136: allowed: 1125014, denied: 4495057, time per allow: 801ns ratelimiter_test.go:136: allowed: 1497722, denied: 5985892, time per allow: 789ns BenchmarkLimiter/allow/parallel/mocked_timesource-4 6092762 194.1 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource-4 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 15.75µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 233ns ratelimiter_test.go:136: allowed: 2999, denied: 7001, time per allow: 627ns ratelimiter_test.go:136: allowed: 200999, denied: 799001, time per allow: 979ns ratelimiter_test.go:136: allowed: 1219552, denied: 4873210, time per allow: 969ns BenchmarkLimiter/allow/parallel/mocked_timesource-8 4215762 283.1 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource-8 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 21.375µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 292ns ratelimiter_test.go:136: allowed: 2999, denied: 7001, time per allow: 872ns ratelimiter_test.go:136: allowed: 200999, denied: 799001, time per allow: 1.416µs ratelimiter_test.go:136: allowed: 844152, denied: 3371610, time per allow: 1.413µs BenchmarkLimiter/allow/parallel/mocked_timesource-32 3159067 382.7 ns/op --- BENCH: BenchmarkLimiter/allow/parallel/mocked_timesource-32 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 22.625µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 227ns ratelimiter_test.go:136: allowed: 2999, denied: 7001, time per allow: 1.093µs ratelimiter_test.go:136: allowed: 200999, denied: 799001, time per allow: 1.889µs ratelimiter_test.go:136: allowed: 632813, denied: 2526254, time per allow: 1.91µs BenchmarkLimiter/reserve-canceling-every-3/serial/real 5593612 217.2 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 308ns ratelimiter_test.go:114: allowed: 1922, denied: 8078, time per allow: 1.046µs ratelimiter_test.go:114: allowed: 141910, denied: 858090, time per allow: 1.511µs ratelimiter_test.go:114: allowed: 795043, denied: 4798569, time per allow: 1.527µs BenchmarkLimiter/reserve-canceling-every-3/serial/real-2 5838519 208.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real-2 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 627ns ratelimiter_test.go:114: allowed: 1955, denied: 8045, time per allow: 1.077µs ratelimiter_test.go:114: allowed: 135241, denied: 864759, time per allow: 1.519µs ratelimiter_test.go:114: allowed: 798897, denied: 5039622, time per allow: 1.519µs BenchmarkLimiter/reserve-canceling-every-3/serial/real-4 5828708 206.3 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real-4 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 389ns ratelimiter_test.go:114: allowed: 2013, denied: 7987, time per allow: 1.002µs ratelimiter_test.go:114: allowed: 137317, denied: 862683, time per allow: 1.499µs ratelimiter_test.go:114: allowed: 798393, denied: 5030315, time per allow: 1.506µs BenchmarkLimiter/reserve-canceling-every-3/serial/real-8 5810233 206.4 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real-8 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 397ns ratelimiter_test.go:114: allowed: 1873, denied: 8127, time per allow: 1.087µs ratelimiter_test.go:114: allowed: 135457, denied: 864543, time per allow: 1.524µs ratelimiter_test.go:114: allowed: 798038, denied: 5012195, time per allow: 1.503µs BenchmarkLimiter/reserve-canceling-every-3/serial/real-32 5801004 206.5 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real-32 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 368ns ratelimiter_test.go:114: allowed: 2044, denied: 7956, time per allow: 1.01µs ratelimiter_test.go:114: allowed: 137067, denied: 862933, time per allow: 1.509µs ratelimiter_test.go:114: allowed: 797823, denied: 5003181, time per allow: 1.501µs BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time 9924230 121.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 252ns ratelimiter_test.go:114: allowed: 2163, denied: 7837, time per allow: 540ns ratelimiter_test.go:114: allowed: 121894, denied: 878106, time per allow: 991ns ratelimiter_test.go:114: allowed: 1201932, denied: 8722298, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-2 9902736 120.9 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-2 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 241ns ratelimiter_test.go:114: allowed: 2194, denied: 7806, time per allow: 549ns ratelimiter_test.go:114: allowed: 122146, denied: 877854, time per allow: 991ns ratelimiter_test.go:114: allowed: 1198147, denied: 8704589, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-4 9917348 120.9 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-4 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 315ns ratelimiter_test.go:114: allowed: 2226, denied: 7774, time per allow: 554ns ratelimiter_test.go:114: allowed: 121970, denied: 878030, time per allow: 991ns ratelimiter_test.go:114: allowed: 1200230, denied: 8717118, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-8 9918499 120.9 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-8 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 553ns ratelimiter_test.go:114: allowed: 2174, denied: 7826, time per allow: 543ns ratelimiter_test.go:114: allowed: 121959, denied: 878041, time per allow: 991ns ratelimiter_test.go:114: allowed: 1199866, denied: 8718633, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-32 9919794 121.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/real_pinned_time-32 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 460ns ratelimiter_test.go:114: allowed: 2166, denied: 7834, time per allow: 541ns ratelimiter_test.go:114: allowed: 121939, denied: 878061, time per allow: 991ns ratelimiter_test.go:114: allowed: 1201433, denied: 8718361, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped 6231513 194.6 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 448ns ratelimiter_test.go:114: allowed: 2823, denied: 7177, time per allow: 647ns ratelimiter_test.go:114: allowed: 193547, denied: 806453, time per allow: 994ns ratelimiter_test.go:114: allowed: 1213880, denied: 5017633, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-2 6742875 176.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-2 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 368ns ratelimiter_test.go:114: allowed: 2900, denied: 7100, time per allow: 657ns ratelimiter_test.go:114: allowed: 178948, denied: 821052, time per allow: 994ns ratelimiter_test.go:114: allowed: 1187742, denied: 5555133, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-4 6685393 177.5 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-4 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 436ns ratelimiter_test.go:114: allowed: 2833, denied: 7167, time per allow: 650ns ratelimiter_test.go:114: allowed: 180478, denied: 819522, time per allow: 994ns ratelimiter_test.go:114: allowed: 1187807, denied: 5497586, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-8 6632632 177.6 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-8 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 381ns ratelimiter_test.go:114: allowed: 2850, denied: 7150, time per allow: 651ns ratelimiter_test.go:114: allowed: 181903, denied: 818097, time per allow: 994ns ratelimiter_test.go:114: allowed: 1179238, denied: 5453394, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-32 6718569 178.1 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/wrapped-32 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 620ns ratelimiter_test.go:114: allowed: 2749, denied: 7251, time per allow: 637ns ratelimiter_test.go:114: allowed: 179568, denied: 820432, time per allow: 994ns ratelimiter_test.go:114: allowed: 1197318, denied: 5521251, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource 6727784 174.2 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 799ns ratelimiter_test.go:114: allowed: 1999, denied: 8001, time per allow: 856ns ratelimiter_test.go:114: allowed: 100999, denied: 899001, time per allow: 1.765µs ratelimiter_test.go:114: allowed: 673778, denied: 6054006, time per allow: 1.739µs BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-2 7773721 154.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-2 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 395ns ratelimiter_test.go:114: allowed: 1999, denied: 8001, time per allow: 825ns ratelimiter_test.go:114: allowed: 100999, denied: 899001, time per allow: 1.528µs ratelimiter_test.go:114: allowed: 778371, denied: 6995350, time per allow: 1.544µs BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-4 7683728 156.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-4 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 366ns ratelimiter_test.go:114: allowed: 1999, denied: 8001, time per allow: 811ns ratelimiter_test.go:114: allowed: 100999, denied: 899001, time per allow: 1.546µs ratelimiter_test.go:114: allowed: 769372, denied: 6914356, time per allow: 1.558µs BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-8 7686458 163.4 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-8 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 385ns ratelimiter_test.go:114: allowed: 1999, denied: 8001, time per allow: 839ns ratelimiter_test.go:114: allowed: 100999, denied: 899001, time per allow: 1.545µs ratelimiter_test.go:114: allowed: 769645, denied: 6916813, time per allow: 1.631µs BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-32 7580811 157.3 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/serial/mocked_timesource-32 ratelimiter_test.go:114: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:114: allowed: 66, denied: 34, time per allow: 387ns ratelimiter_test.go:114: allowed: 1999, denied: 8001, time per allow: 796ns ratelimiter_test.go:114: allowed: 100999, denied: 899001, time per allow: 1.567µs ratelimiter_test.go:114: allowed: 759080, denied: 6821731, time per allow: 1.57µs BenchmarkLimiter/reserve-canceling-every-3/parallel/real 5510388 218.3 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 462ns ratelimiter_test.go:136: allowed: 1908, denied: 8092, time per allow: 1.077µs ratelimiter_test.go:136: allowed: 134580, denied: 865420, time per allow: 1.618µs ratelimiter_test.go:136: allowed: 774155, denied: 4736233, time per allow: 1.554µs BenchmarkLimiter/reserve-canceling-every-3/parallel/real-2 7520551 155.9 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real-2 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 914ns ratelimiter_test.go:136: allowed: 823, denied: 9177, time per allow: 1.925µs ratelimiter_test.go:136: allowed: 767, denied: 999233, time per allow: 208.02µs ratelimiter_test.go:136: allowed: 752, denied: 7519799, time per allow: 1.558596ms BenchmarkLimiter/reserve-canceling-every-3/parallel/real-4 4660968 254.6 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real-4 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 707ns ratelimiter_test.go:136: allowed: 3525, denied: 6475, time per allow: 749ns ratelimiter_test.go:136: allowed: 202434, denied: 797566, time per allow: 1.271µs ratelimiter_test.go:136: allowed: 897507, denied: 3763461, time per allow: 1.322µs BenchmarkLimiter/reserve-canceling-every-3/parallel/real-8 4067368 293.3 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real-8 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 558ns ratelimiter_test.go:136: allowed: 6615, denied: 3385, time per allow: 409ns ratelimiter_test.go:136: allowed: 661672, denied: 338328, time per allow: 445ns ratelimiter_test.go:136: allowed: 2651622, denied: 1415746, time per allow: 449ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real-32 2719041 407.6 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real-32 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 791ns ratelimiter_test.go:136: allowed: 6666, denied: 3334, time per allow: 593ns ratelimiter_test.go:136: allowed: 661514, denied: 338486, time per allow: 667ns ratelimiter_test.go:136: allowed: 1812685, denied: 906356, time per allow: 611ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time 9683659 124.3 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 343ns ratelimiter_test.go:136: allowed: 2179, denied: 7821, time per allow: 542ns ratelimiter_test.go:136: allowed: 124893, denied: 875107, time per allow: 992ns ratelimiter_test.go:136: allowed: 1205125, denied: 8478534, time per allow: 999ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-2 8225566 146.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-2 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 309ns ratelimiter_test.go:136: allowed: 2680, denied: 7320, time per allow: 583ns ratelimiter_test.go:136: allowed: 143199, denied: 856801, time per allow: 1.018µs ratelimiter_test.go:136: allowed: 1169292, denied: 7056274, time per allow: 1.027µs BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-4 5578884 222.8 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-4 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 989ns ratelimiter_test.go:136: allowed: 5098, denied: 4902, time per allow: 420ns ratelimiter_test.go:136: allowed: 365764, denied: 634236, time per allow: 588ns ratelimiter_test.go:136: allowed: 2127002, denied: 3451882, time per allow: 584ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-8 5841618 207.8 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-8 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 65, denied: 35, time per allow: 430ns ratelimiter_test.go:136: allowed: 6664, denied: 3336, time per allow: 376ns ratelimiter_test.go:136: allowed: 633158, denied: 366842, time per allow: 324ns ratelimiter_test.go:136: allowed: 3671798, denied: 2169820, time per allow: 330ns BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-32 3719898 309.8 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/real_pinned_time-32 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 506ns ratelimiter_test.go:136: allowed: 6648, denied: 3352, time per allow: 346ns ratelimiter_test.go:136: allowed: 666666, denied: 333334, time per allow: 483ns ratelimiter_test.go:136: allowed: 2479932, denied: 1239966, time per allow: 464ns BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped 6123994 207.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 404ns ratelimiter_test.go:136: allowed: 2810, denied: 7190, time per allow: 646ns ratelimiter_test.go:136: allowed: 196910, denied: 803090, time per allow: 995ns ratelimiter_test.go:136: allowed: 1227907, denied: 4896087, time per allow: 1.032µs BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-2 5416012 228.0 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-2 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 1.486µs ratelimiter_test.go:136: allowed: 3350, denied: 6650, time per allow: 711ns ratelimiter_test.go:136: allowed: 221339, denied: 778661, time per allow: 1µs ratelimiter_test.go:136: allowed: 1230035, denied: 4185977, time per allow: 1.004µs BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-4 4629619 260.2 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-4 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 994ns ratelimiter_test.go:136: allowed: 3573, denied: 6427, time per allow: 778ns ratelimiter_test.go:136: allowed: 252008, denied: 747992, time per allow: 1.028µs ratelimiter_test.go:136: allowed: 1167444, denied: 3462175, time per allow: 1.031µs BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-8 4268707 283.6 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-8 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 561ns ratelimiter_test.go:136: allowed: 4390, denied: 5610, time per allow: 845ns ratelimiter_test.go:136: allowed: 287705, denied: 712295, time per allow: 1.219µs ratelimiter_test.go:136: allowed: 901533, denied: 2517796, time per allow: 1.066µs ratelimiter_test.go:136: allowed: 1133229, denied: 3135478, time per allow: 1.068µs BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-32 2863960 444.5 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/wrapped-32 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 755ns ratelimiter_test.go:136: allowed: 6306, denied: 3694, time per allow: 942ns ratelimiter_test.go:136: allowed: 390106, denied: 609894, time per allow: 1.074µs ratelimiter_test.go:136: allowed: 1178002, denied: 1685958, time per allow: 1.08µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource 6881562 176.7 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 395ns ratelimiter_test.go:136: allowed: 1999, denied: 8001, time per allow: 795ns ratelimiter_test.go:136: allowed: 100999, denied: 899001, time per allow: 1.726µs ratelimiter_test.go:136: allowed: 689156, denied: 6192406, time per allow: 1.764µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-2 5739153 209.1 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-2 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 1.123µs ratelimiter_test.go:136: allowed: 1986, denied: 8014, time per allow: 1.146µs ratelimiter_test.go:136: allowed: 100400, denied: 899600, time per allow: 2.082µs ratelimiter_test.go:136: allowed: 570975, denied: 5168178, time per allow: 2.101µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-4 4748774 252.8 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-4 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 511ns ratelimiter_test.go:136: allowed: 1961, denied: 8039, time per allow: 1.445µs ratelimiter_test.go:136: allowed: 97522, denied: 902478, time per allow: 2.591µs ratelimiter_test.go:136: allowed: 459408, denied: 4289366, time per allow: 2.613µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-8 4080715 290.5 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-8 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 474ns ratelimiter_test.go:136: allowed: 1929, denied: 8071, time per allow: 1.568µs ratelimiter_test.go:136: allowed: 93842, denied: 906158, time per allow: 3.133µs ratelimiter_test.go:136: allowed: 379398, denied: 3701317, time per allow: 3.124µs BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-32 3506800 351.2 ns/op --- BENCH: BenchmarkLimiter/reserve-canceling-every-3/parallel/mocked_timesource-32 ratelimiter_test.go:136: allowed: 0, denied: 1, time per allow: n/a ratelimiter_test.go:136: allowed: 66, denied: 34, time per allow: 931ns ratelimiter_test.go:136: allowed: 1925, denied: 8075, time per allow: 2.409µs ratelimiter_test.go:136: allowed: 90771, denied: 909229, time per allow: 3.769µs ratelimiter_test.go:136: allowed: 317064, denied: 3189736, time per allow: 3.884µs BenchmarkLimiter/wait/serial/2ns_rate/real 2271784 482.1 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 12.833µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 475ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 479ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 528ns ratelimiter_test.go:114: allowed: 2271784, denied: 0, time per allow: 482ns BenchmarkLimiter/wait/serial/2ns_rate/real-2 3010875 403.0 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real-2 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 14.583µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 524ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 434ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 398ns ratelimiter_test.go:114: allowed: 3010875, denied: 0, time per allow: 402ns BenchmarkLimiter/wait/serial/2ns_rate/real-4 3046982 388.7 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real-4 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 11.667µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 462ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 405ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 393ns ratelimiter_test.go:114: allowed: 3046982, denied: 0, time per allow: 388ns BenchmarkLimiter/wait/serial/2ns_rate/real-8 2938251 399.0 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real-8 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 13.459µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 491ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 408ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 408ns ratelimiter_test.go:114: allowed: 2938251, denied: 0, time per allow: 399ns BenchmarkLimiter/wait/serial/2ns_rate/real-32 2992760 394.0 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/real-32 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 11.083µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 493ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 415ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 400ns ratelimiter_test.go:114: allowed: 2992760, denied: 0, time per allow: 393ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped 2425740 506.7 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 21.791µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 653ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 397ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 494ns ratelimiter_test.go:114: allowed: 2425740, denied: 0, time per allow: 506ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped-2 2937199 407.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped-2 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 10.875µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 454ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 446ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 408ns ratelimiter_test.go:114: allowed: 2937199, denied: 0, time per allow: 407ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped-4 3006384 394.8 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped-4 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 9.167µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 506ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 424ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 399ns ratelimiter_test.go:114: allowed: 3006384, denied: 0, time per allow: 394ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped-8 2951150 426.7 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped-8 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 13.084µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 526ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 404ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 406ns ratelimiter_test.go:114: allowed: 2951150, denied: 0, time per allow: 426ns BenchmarkLimiter/wait/serial/2ns_rate/wrapped-32 2990496 403.0 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/wrapped-32 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 10.125µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 515ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 401ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 401ns ratelimiter_test.go:114: allowed: 2990496, denied: 0, time per allow: 402ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource 5608531 214.3 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 8.5µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 273ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 184ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 213ns ratelimiter_test.go:114: allowed: 5608531, denied: 0, time per allow: 214ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-2 6328546 189.3 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-2 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 10.709µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 582ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 193ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 189ns ratelimiter_test.go:114: allowed: 6328546, denied: 0, time per allow: 189ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-4 6209492 189.4 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-4 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 13.5µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 346ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 189ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 193ns ratelimiter_test.go:114: allowed: 6209492, denied: 0, time per allow: 189ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-8 6340005 196.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-8 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 22.041µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 339ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 202ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 189ns ratelimiter_test.go:114: allowed: 6340005, denied: 0, time per allow: 196ns BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-32 6319386 189.4 ns/op --- BENCH: BenchmarkLimiter/wait/serial/2ns_rate/mocked_timesource-32 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 13.75µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 311ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 189ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 189ns ratelimiter_test.go:114: allowed: 6319386, denied: 0, time per allow: 189ns BenchmarkLimiter/wait/serial/1µs_rate/real 1201171 999.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 17.084µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 502ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 900ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:114: allowed: 1201171, denied: 0, time per allow: 999ns BenchmarkLimiter/wait/serial/1µs_rate/real-2 1000000 1037 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real-2 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 8.542µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 581ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 901ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 1.037µs BenchmarkLimiter/wait/serial/1µs_rate/real-4 1201134 999.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real-4 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 8.083µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 555ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 901ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:114: allowed: 1201134, denied: 0, time per allow: 999ns BenchmarkLimiter/wait/serial/1µs_rate/real-8 1000000 1043 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real-8 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 11.709µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 505ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 900ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 1.042µs BenchmarkLimiter/wait/serial/1µs_rate/real-32 1201106 999.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/real-32 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 13.417µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 549ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 900ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:114: allowed: 1201106, denied: 0, time per allow: 999ns BenchmarkLimiter/wait/serial/1µs_rate/wrapped 1201178 999.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 10.75µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 865ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 900ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:114: allowed: 1201178, denied: 0, time per allow: 999ns BenchmarkLimiter/wait/serial/1µs_rate/wrapped-2 1000000 1049 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped-2 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 32.458µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 503ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 901ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 1.048µs BenchmarkLimiter/wait/serial/1µs_rate/wrapped-4 1201174 999.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped-4 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 8.084µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 557ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 900ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:114: allowed: 1201174, denied: 0, time per allow: 999ns BenchmarkLimiter/wait/serial/1µs_rate/wrapped-8 1201167 999.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped-8 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 9.458µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 714ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 901ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:114: allowed: 1201167, denied: 0, time per allow: 999ns BenchmarkLimiter/wait/serial/1µs_rate/wrapped-32 1201171 999.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/wrapped-32 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 8.125µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 513ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 900ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:114: allowed: 1201171, denied: 0, time per allow: 999ns BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource 5592026 236.8 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 9.041µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 271ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 191ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 214ns ratelimiter_test.go:114: allowed: 5592026, denied: 0, time per allow: 236ns BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-2 6447422 186.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-2 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 21.375µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 355ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 188ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 186ns ratelimiter_test.go:114: allowed: 6447422, denied: 0, time per allow: 186ns BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-4 6362046 186.8 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-4 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 11.75µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 374ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 193ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 188ns ratelimiter_test.go:114: allowed: 6362046, denied: 0, time per allow: 186ns BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-8 6338952 189.2 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-8 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 24.583µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 283ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 187ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 189ns ratelimiter_test.go:114: allowed: 6338952, denied: 0, time per allow: 189ns BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-32 6387135 187.9 ns/op --- BENCH: BenchmarkLimiter/wait/serial/1µs_rate/mocked_timesource-32 ratelimiter_test.go:114: allowed: 1, denied: 0, time per allow: 13µs ratelimiter_test.go:114: allowed: 100, denied: 0, time per allow: 350ns ratelimiter_test.go:114: allowed: 10000, denied: 0, time per allow: 192ns ratelimiter_test.go:114: allowed: 1000000, denied: 0, time per allow: 187ns ratelimiter_test.go:114: allowed: 6387135, denied: 0, time per allow: 187ns BenchmarkLimiter/wait/parallel/2ns_rate/real 2533497 495.8 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 63.791µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 505ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 506ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 473ns ratelimiter_test.go:136: allowed: 2533497, denied: 0, time per allow: 495ns BenchmarkLimiter/wait/parallel/2ns_rate/real-2 4293474 281.5 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real-2 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 19.958µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 608ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 291ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 279ns ratelimiter_test.go:136: allowed: 4293474, denied: 0, time per allow: 281ns BenchmarkLimiter/wait/parallel/2ns_rate/real-4 3730892 321.6 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real-4 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 20.541µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 635ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 342ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 321ns ratelimiter_test.go:136: allowed: 3730892, denied: 0, time per allow: 321ns BenchmarkLimiter/wait/parallel/2ns_rate/real-8 2580943 466.0 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real-8 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 19.667µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 551ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 451ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 464ns ratelimiter_test.go:136: allowed: 2580943, denied: 0, time per allow: 466ns BenchmarkLimiter/wait/parallel/2ns_rate/real-32 1901340 614.3 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/real-32 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 38.334µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 619ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 644ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 631ns ratelimiter_test.go:136: allowed: 1901340, denied: 0, time per allow: 614ns BenchmarkLimiter/wait/parallel/2ns_rate/wrapped 2373084 537.6 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 30.208µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 495ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 551ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 505ns ratelimiter_test.go:136: allowed: 2373084, denied: 0, time per allow: 537ns BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-2 3986494 302.0 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-2 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 21.083µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 455ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 320ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 301ns ratelimiter_test.go:136: allowed: 3986494, denied: 0, time per allow: 302ns BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-4 3463202 346.9 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-4 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 38.709µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 753ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 374ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 346ns ratelimiter_test.go:136: allowed: 3463202, denied: 0, time per allow: 346ns BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-8 1935219 651.2 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-8 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 17.083µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 461ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 644ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 620ns ratelimiter_test.go:136: allowed: 1935219, denied: 0, time per allow: 651ns BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-32 1308968 907.9 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/wrapped-32 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 25.458µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 562ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 955ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 916ns ratelimiter_test.go:136: allowed: 1308968, denied: 0, time per allow: 907ns BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource 5477860 221.1 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 35.083µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 313ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 196ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 219ns ratelimiter_test.go:136: allowed: 5477860, denied: 0, time per allow: 221ns BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-2 4676773 256.6 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-2 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 31.334µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 410ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 303ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 256ns ratelimiter_test.go:136: allowed: 4676773, denied: 0, time per allow: 256ns BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-4 4149928 285.0 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-4 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 22.875µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 375ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 337ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 289ns ratelimiter_test.go:136: allowed: 4149928, denied: 0, time per allow: 284ns BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-8 3410410 351.4 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-8 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 16.459µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 400ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 357ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 351ns ratelimiter_test.go:136: allowed: 3410410, denied: 0, time per allow: 351ns BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-32 2806215 442.2 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/2ns_rate/mocked_timesource-32 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 32.125µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 457ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 398ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 427ns ratelimiter_test.go:136: allowed: 2806215, denied: 0, time per allow: 442ns BenchmarkLimiter/wait/parallel/1µs_rate/real 1201167 999.2 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 43.708µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 543ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 901ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:136: allowed: 1201167, denied: 0, time per allow: 999ns BenchmarkLimiter/wait/parallel/1µs_rate/real-2 1215512 987.4 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real-2 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 14.625µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 479ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 883ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 987ns ratelimiter_test.go:136: allowed: 1215512, denied: 0, time per allow: 987ns BenchmarkLimiter/wait/parallel/1µs_rate/real-4 1280577 926.8 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real-4 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 14.875µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 432ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 846ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 937ns ratelimiter_test.go:136: allowed: 1280577, denied: 0, time per allow: 926ns BenchmarkLimiter/wait/parallel/1µs_rate/real-8 2569482 456.5 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real-8 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 24.917µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 395ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 428ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 467ns ratelimiter_test.go:136: allowed: 2569482, denied: 0, time per allow: 456ns BenchmarkLimiter/wait/parallel/1µs_rate/real-32 1904204 597.6 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/real-32 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 34.334µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 475ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 562ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 630ns ratelimiter_test.go:136: allowed: 1904204, denied: 0, time per allow: 597ns BenchmarkLimiter/wait/parallel/1µs_rate/wrapped 1201110 999.2 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 81.292µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 587ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 901ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:136: allowed: 1201110, denied: 0, time per allow: 999ns BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-2 1000000 1045 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-2 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 18.166µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 797ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 901ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 1.044µs BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-4 1201161 1007 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-4 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 16.666µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 702ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 901ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:136: allowed: 1201161, denied: 0, time per allow: 1.006µs BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-8 1201149 999.2 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-8 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 20.417µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 921ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 901ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 999ns ratelimiter_test.go:136: allowed: 1201149, denied: 0, time per allow: 999ns BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-32 1000000 1006 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/wrapped-32 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 25.416µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 570ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 971ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 1.006µs BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource 5436198 231.0 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 39.084µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 281ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 196ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 220ns ratelimiter_test.go:136: allowed: 5436198, denied: 0, time per allow: 231ns BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-2 4571574 262.2 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-2 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 14.334µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 554ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 257ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 262ns ratelimiter_test.go:136: allowed: 4571574, denied: 0, time per allow: 262ns BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-4 4076700 292.6 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-4 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 16.291µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 583ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 338ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 294ns ratelimiter_test.go:136: allowed: 4076700, denied: 0, time per allow: 292ns BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-8 2949456 356.2 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-8 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 19.5µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 347ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 313ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 406ns ratelimiter_test.go:136: allowed: 2949456, denied: 0, time per allow: 356ns BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-32 2807648 433.8 ns/op --- BENCH: BenchmarkLimiter/wait/parallel/1µs_rate/mocked_timesource-32 ratelimiter_test.go:136: allowed: 1, denied: 0, time per allow: 53.75µs ratelimiter_test.go:136: allowed: 100, denied: 0, time per allow: 431ns ratelimiter_test.go:136: allowed: 10000, denied: 0, time per allow: 539ns ratelimiter_test.go:136: allowed: 1000000, denied: 0, time per allow: 427ns ratelimiter_test.go:136: allowed: 2807648, denied: 0, time per allow: 433ns PASS ok github.com/uber/cadence/common/clock 199.030s ================================================ FILE: common/clock/time_source.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package clock import ( "context" "time" "github.com/jonboulle/clockwork" ) type ( // TimeSource provides an interface that packages can use instead of directly using // the [time] module, so that chronology-related behavior can be tested. TimeSource interface { After(d time.Duration) <-chan time.Time Sleep(d time.Duration) SleepWithContext(ctx context.Context, d time.Duration) error Now() time.Time Since(t time.Time) time.Duration NewTicker(d time.Duration) Ticker NewTimer(d time.Duration) Timer AfterFunc(d time.Duration, f func()) Timer ContextWithTimeout(context.Context, time.Duration) (context.Context, context.CancelFunc) ContextWithDeadline(context.Context, time.Time) (context.Context, context.CancelFunc) } // Ticker provides an interface which can be used instead of directly using // [time.Ticker]. The real-time ticker t provides ticks through t.C which // becomes t.Chan() to make this channel requirement definable in this // interface. Ticker interface { Chan() <-chan time.Time Reset(d time.Duration) Stop() } // Timer provides an interface which can be used instead of directly using // [time.Timer]. The real-time timer t provides events through t.C which becomes // t.Chan() to make this channel requirement definable in this interface. Timer interface { Chan() <-chan time.Time Reset(d time.Duration) bool Stop() bool } // clock serves real wall-clock time clock struct { clockwork.Clock } // fakeClock serves fake controlled time fakeClock struct { *clockwork.FakeClock } // MockedTimeSource provides an interface for a clock which can be manually advanced // through time. // // MockedTimeSource maintains a list of "waiters," which consists of all callers // waiting on the underlying clock (i.e. Tickers and Timers including callers of // Sleep or After). Users can call BlockUntil to block until the clock has an // expected number of waiters. MockedTimeSource interface { TimeSource // Advance advances the FakeClock to a new point in time, ensuring any existing // waiters are notified appropriately before returning. Advance(d time.Duration) // BlockUntil blocks until the FakeClock has at least the given number // of waiters running at the same time. // // Waiters are either time.Sleep, time.After[Func], time.Ticker, or time.Timer, // and they decrement the counter when they complete or are stopped. BlockUntil(waiters int) } ) // NewRealTimeSource returns a time source that servers // real wall clock time func NewRealTimeSource() TimeSource { return &clock{ Clock: clockwork.NewRealClock(), } } // NewMockedTimeSourceAt returns a time source that servers // fake controlled time. The initial time of the MockedTimeSource will be the given time. func NewMockedTimeSourceAt(t time.Time) MockedTimeSource { return &fakeClock{ FakeClock: clockwork.NewFakeClockAt(t), } } func (r *clock) ContextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { return context.WithTimeout(ctx, d) } func (r *clock) ContextWithDeadline(ctx context.Context, t time.Time) (context.Context, context.CancelFunc) { return context.WithDeadline(ctx, t) } func (r *clock) NewTicker(d time.Duration) Ticker { return r.Clock.NewTicker(d) } func (r *clock) NewTimer(d time.Duration) Timer { return r.Clock.NewTimer(d) } func (r *clock) AfterFunc(d time.Duration, f func()) Timer { return r.Clock.AfterFunc(d, f) } func (r *clock) SleepWithContext(ctx context.Context, duration time.Duration) error { select { case <-r.After(duration): return nil case <-ctx.Done(): return ctx.Err() } } // NewMockedTimeSource returns a time source that servers // fake controlled time func NewMockedTimeSource() MockedTimeSource { return &fakeClock{ FakeClock: clockwork.NewFakeClock(), } } func (c *fakeClock) NewTicker(d time.Duration) Ticker { return c.FakeClock.NewTicker(d) } func (c *fakeClock) NewTimer(d time.Duration) Timer { return c.FakeClock.NewTimer(d) } func (c *fakeClock) AfterFunc(d time.Duration, f func()) Timer { return c.FakeClock.AfterFunc(d, f) } func (c *fakeClock) ContextWithTimeout(ctx context.Context, duration time.Duration) (context.Context, context.CancelFunc) { return clockwork.WithTimeout(ctx, c.FakeClock, duration) } func (c *fakeClock) ContextWithDeadline(ctx context.Context, deadline time.Time) (context.Context, context.CancelFunc) { return clockwork.WithDeadline(ctx, c.FakeClock, deadline) } func (c *fakeClock) SleepWithContext(ctx context.Context, duration time.Duration) error { select { case <-c.After(duration): return nil case <-ctx.Done(): return ctx.Err() } } ================================================ FILE: common/clock/timer_gate.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package=$GOPACKAGE -destination=timer_gate_mock.go github.com/uber/cadence/common/clock TimerGate package clock import ( "sync" "time" ) type ( // TimerGate is a timer interface which fires a timer at a given timestamp. TimerGate interface { // Chan return the channel which will be fired when time is up Chan() <-chan time.Time // FireAfter check will the timer get fired after a certain time FireAfter(t time.Time) bool // Update update the timer gate, return true if update is a success // success means timer is idle or timer is set with a sooner time to fire Update(t time.Time) bool // Close shutdown the timer Stop() } timerGateImpl struct { sync.RWMutex timeSource TimeSource timer Timer fireTime time.Time } ) func NewTimerGate(timeSource TimeSource) TimerGate { t := &timerGateImpl{ timer: timeSource.NewTimer(0), timeSource: timeSource, } // the timer should be stopped when initialized if !t.timer.Stop() { // drain the existing signal if exist // TODO: the drain can be removed after go 1.23 <-t.timer.Chan() } return t } func (t *timerGateImpl) Chan() <-chan time.Time { return t.timer.Chan() } func (t *timerGateImpl) FireAfter(now time.Time) bool { t.RLock() defer t.RUnlock() return t.fireTime.After(now) } func (t *timerGateImpl) Stop() { t.Lock() defer t.Unlock() t.fireTime = time.Time{} t.timer.Stop() // TODO: the drain can be removed after go 1.23 select { case <-t.timer.Chan(): default: } } func (t *timerGateImpl) Update(fireTime time.Time) bool { t.Lock() defer t.Unlock() if t.timer.Stop() { if t.fireTime.Before(fireTime) { // this means the timer, before stopped, is active && next wake up time do not have to be updated t.timer.Reset(t.fireTime.Sub(t.timeSource.Now())) return false } } // TODO: the drain can be removed after go 1.23 select { case <-t.timer.Chan(): default: } // this means the timer, before stopped, is active && next wake up time has to be updated // or this means the timer, before stopped, is already fired / never active t.fireTime = fireTime t.timer.Reset(fireTime.Sub(t.timeSource.Now())) // Notifies caller that next notification is reset to fire at passed in 'next' visibility time return true } ================================================ FILE: common/clock/timer_gate_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/clock (interfaces: TimerGate) // // Generated by this command: // // mockgen -package=clock -destination=timer_gate_mock.go github.com/uber/cadence/common/clock TimerGate // // Package clock is a generated GoMock package. package clock import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" ) // MockTimerGate is a mock of TimerGate interface. type MockTimerGate struct { ctrl *gomock.Controller recorder *MockTimerGateMockRecorder isgomock struct{} } // MockTimerGateMockRecorder is the mock recorder for MockTimerGate. type MockTimerGateMockRecorder struct { mock *MockTimerGate } // NewMockTimerGate creates a new mock instance. func NewMockTimerGate(ctrl *gomock.Controller) *MockTimerGate { mock := &MockTimerGate{ctrl: ctrl} mock.recorder = &MockTimerGateMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTimerGate) EXPECT() *MockTimerGateMockRecorder { return m.recorder } // Chan mocks base method. func (m *MockTimerGate) Chan() <-chan time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Chan") ret0, _ := ret[0].(<-chan time.Time) return ret0 } // Chan indicates an expected call of Chan. func (mr *MockTimerGateMockRecorder) Chan() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Chan", reflect.TypeOf((*MockTimerGate)(nil).Chan)) } // FireAfter mocks base method. func (m *MockTimerGate) FireAfter(t time.Time) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FireAfter", t) ret0, _ := ret[0].(bool) return ret0 } // FireAfter indicates an expected call of FireAfter. func (mr *MockTimerGateMockRecorder) FireAfter(t any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FireAfter", reflect.TypeOf((*MockTimerGate)(nil).FireAfter), t) } // Stop mocks base method. func (m *MockTimerGate) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockTimerGateMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockTimerGate)(nil).Stop)) } // Update mocks base method. func (m *MockTimerGate) Update(t time.Time) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Update", t) ret0, _ := ret[0].(bool) return ret0 } // Update indicates an expected call of Update. func (mr *MockTimerGateMockRecorder) Update(t any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockTimerGate)(nil).Update), t) } ================================================ FILE: common/clock/timer_gate_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clock import ( "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type ( timerGateSuite struct { suite.Suite *require.Assertions timerGate TimerGate } ) func TestTimerGeteSuite(t *testing.T) { s := new(timerGateSuite) suite.Run(t, s) } func (s *timerGateSuite) SetupSuite() { } func (s *timerGateSuite) TearDownSuite() { } func (s *timerGateSuite) SetupTest() { s.Assertions = require.New(s.T()) s.timerGate = NewTimerGate(NewRealTimeSource()) } func (s *timerGateSuite) TearDownTest() { s.timerGate.Stop() } func (s *timerGateSuite) TestTimerFire() { now := time.Now() newTimer := now.Add(1 * time.Second) deadline := now.Add(2 * time.Second) s.True(s.timerGate.Update(newTimer)) select { case <-s.timerGate.Chan(): case <-time.NewTimer(deadline.Sub(now)).C: s.Fail("timer should fire before test deadline") } } func (s *timerGateSuite) TestTimerFireAfterUpdate_Active_Updated_BeforeNow() { now := time.Now() newTimer := now.Add(9 * time.Second) updatedNewTimer := now.Add(-1 * time.Second) deadline := now.Add(3 * time.Second) s.True(s.timerGate.Update(newTimer)) select { case <-s.timerGate.Chan(): s.Fail("timer should not fire when current time not updated") case <-time.NewTimer(deadline.Sub(now)).C: } s.True(s.timerGate.Update(updatedNewTimer)) select { case <-s.timerGate.Chan(): case <-time.NewTimer(deadline.Sub(now)).C: s.Fail("timer should fire before test deadline") } } func (s *timerGateSuite) TestTimerFireAfterUpdate_Active_Updated() { now := time.Now() newTimer := now.Add(5 * time.Second) updatedNewTimer := now.Add(1 * time.Second) deadline := now.Add(3 * time.Second) s.True(s.timerGate.Update(newTimer)) s.True(s.timerGate.Update(updatedNewTimer)) select { case <-s.timerGate.Chan(): case <-time.NewTimer(deadline.Sub(now)).C: s.Fail("timer should fire before test deadline") } } func (s *timerGateSuite) TestTimerFireAfterUpdate_Active_NotUpdated() { now := time.Now() newTimer := now.Add(1 * time.Second) updatedNewTimer := now.Add(3 * time.Second) deadline := now.Add(2 * time.Second) s.True(s.timerGate.Update(newTimer)) s.False(s.timerGate.Update(updatedNewTimer)) select { case <-s.timerGate.Chan(): case <-time.NewTimer(deadline.Sub(now)).C: s.Fail("timer should fire before test deadline") } } func (s *timerGateSuite) TestTimerFireAfterUpdate_NotActive_Updated() { now := time.Now() newTimer := now.Add(-5 * time.Second) updatedNewTimer := now.Add(1 * time.Second) deadline := now.Add(3 * time.Second) s.True(s.timerGate.Update(newTimer)) // this is to drain existing signal <-s.timerGate.Chan() // test setup up complete s.True(s.timerGate.Update(updatedNewTimer)) select { case <-s.timerGate.Chan(): case <-time.NewTimer(deadline.Sub(now)).C: s.Fail("timer should fire before test deadline") } } func (s *timerGateSuite) TestTimerWillFire_Zero() { // this test is to validate initial notification will trigger a scan of timer s.True(s.timerGate.Update(time.Time{})) s.False(s.timerGate.FireAfter(time.Now())) select { // this is to drain existing signal case <-s.timerGate.Chan(): case <-time.NewTimer(time.Second).C: s.Fail("timer should fire") } now := time.Now() newTimer := now.Add(1 * time.Second) deadline := now.Add(2 * time.Second) s.True(s.timerGate.Update(newTimer)) select { case <-s.timerGate.Chan(): case <-time.NewTimer(deadline.Sub(now)).C: s.Fail("timer should fire") } s.True(s.timerGate.Update(time.Time{})) select { // this is to drain existing signal case <-s.timerGate.Chan(): case <-time.NewTimer(time.Second).C: s.Fail("timer should fire") } now = time.Now() newTimer = now.Add(1 * time.Second) s.True(s.timerGate.Update(newTimer)) s.True(s.timerGate.Update(time.Time{})) select { // this is to drain existing signal case <-s.timerGate.Chan(): case <-time.NewTimer(time.Second).C: s.Fail("timer should fire") } } func (s *timerGateSuite) TestTimerFireAfterStopAndUpdate() { now := time.Now() newTimer := now.Add(1 * time.Second) deadline := now.Add(2 * time.Second) s.True(s.timerGate.Update(newTimer)) s.True(s.timerGate.FireAfter(newTimer.Add(-1 * time.Nanosecond))) s.timerGate.Stop() s.False(s.timerGate.FireAfter(newTimer.Add(-1 * time.Nanosecond))) select { case <-s.timerGate.Chan(): s.Fail("timer should be cancelled") case <-time.NewTimer(deadline.Sub(now)).C: } now = time.Now() newTimer = now.Add(1 * time.Second) deadline = now.Add(2 * time.Second) s.True(s.timerGate.Update(newTimer)) s.True(s.timerGate.FireAfter(newTimer.Add(-1 * time.Nanosecond))) select { case <-s.timerGate.Chan(): case <-time.NewTimer(deadline.Sub(now)).C: s.Fail("timer should fire") } } func (s *timerGateSuite) TestTimerWillFire() { now := time.Now() newTimer := now.Add(2 * time.Second) timeBeforeNewTimer := now.Add(1 * time.Second) timeAfterNewTimer := now.Add(3 * time.Second) s.timerGate.Update(newTimer) s.True(s.timerGate.FireAfter(timeBeforeNewTimer)) s.False(s.timerGate.FireAfter(timeAfterNewTimer)) s.timerGate.Stop() s.False(s.timerGate.FireAfter(timeBeforeNewTimer)) } ================================================ FILE: common/cluster/metadata.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cluster import ( "fmt" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type ( // Metadata provides information about clusters Metadata struct { log log.Logger metrics metrics.Scope // failoverVersionIncrement is the increment of each cluster's version when failover happen failoverVersionIncrement int64 // primaryClusterName is the name of the primary cluster, only the primary cluster can register / update domain // all clusters can do domain failover primaryClusterName string // currentClusterName is the name of the current cluster currentClusterName string // allClusters contains all cluster info allClusters map[string]config.ClusterInformation // enabledClusters contains enabled info enabledClusters map[string]config.ClusterInformation // remoteClusters contains enabled and remote info remoteClusters map[string]config.ClusterInformation // versionToClusterName contains all initial version -> corresponding cluster name versionToClusterName map[int64]string // allows for a new failover version migration useNewFailoverVersionOverride dynamicproperties.BoolPropertyFnWithDomainFilter } ) // NewMetadata create a new instance of Metadata func NewMetadata( clusterGroupMetadata config.ClusterGroupMetadata, useMinFailoverVersionOverrideConfig dynamicproperties.BoolPropertyFnWithDomainFilter, metricsClient metrics.Client, logger log.Logger, ) Metadata { versionToClusterName := make(map[int64]string) for clusterName, info := range clusterGroupMetadata.ClusterGroup { versionToClusterName[info.InitialFailoverVersion] = clusterName } // We never use disable clusters, filter them out on start enabledClusters := map[string]config.ClusterInformation{} for cluster, info := range clusterGroupMetadata.ClusterGroup { if info.Enabled { enabledClusters[cluster] = info } } // Precompute remote clusters, they are used in multiple places remoteClusters := map[string]config.ClusterInformation{} for cluster, info := range enabledClusters { if cluster != clusterGroupMetadata.CurrentClusterName { remoteClusters[cluster] = info } } m := Metadata{ log: logger, metrics: metricsClient.Scope(metrics.ClusterMetadataScope), failoverVersionIncrement: clusterGroupMetadata.FailoverVersionIncrement, primaryClusterName: clusterGroupMetadata.PrimaryClusterName, currentClusterName: clusterGroupMetadata.CurrentClusterName, allClusters: clusterGroupMetadata.ClusterGroup, enabledClusters: enabledClusters, remoteClusters: remoteClusters, versionToClusterName: versionToClusterName, useNewFailoverVersionOverride: useMinFailoverVersionOverrideConfig, } m.log.Info("cluster metadata created", tag.Dynamic("primary-cluster-name", m.primaryClusterName), tag.Dynamic("current-cluster-name", m.currentClusterName), tag.Dynamic("failover-version-increment", m.failoverVersionIncrement), ) return m } // GetNextFailoverVersion returns the next valid FailoverVersion for a domain func (m Metadata) GetNextFailoverVersion(targetClusterName string, currentFailoverVersion int64, domainName string) int64 { initialFailoverVersion := m.getInitialFailoverVersion(targetClusterName, domainName) failoverVersion := currentFailoverVersion/m.failoverVersionIncrement*m.failoverVersionIncrement + initialFailoverVersion if failoverVersion < currentFailoverVersion { return failoverVersion + m.failoverVersionIncrement } return failoverVersion } // IsVersionFromSameCluster return true if the new version is used for the same cluster func (m Metadata) IsVersionFromSameCluster(version1 int64, version2 int64) bool { v1Server, err := m.resolveServerName(version1) if err != nil { // preserving old behaviour however, this should never occur m.metrics.IncCounter(metrics.ClusterMetadataFailureToResolveCounter) m.log.Error("could not resolve an incoming version", tag.Dynamic("failover-version", version1)) return false } v2Server, err := m.resolveServerName(version2) if err != nil { m.log.Error("could not resolve an incoming version", tag.Dynamic("failover-version", version2)) return false } return v1Server == v2Server } func (m Metadata) IsPrimaryCluster() bool { return m.primaryClusterName == m.currentClusterName } // GetCurrentClusterName return the current cluster name func (m Metadata) GetCurrentClusterName() string { return m.currentClusterName } // GetAllClusterInfo return all cluster info func (m Metadata) GetAllClusterInfo() map[string]config.ClusterInformation { return m.allClusters } // GetEnabledClusterInfo return enabled cluster info func (m Metadata) GetEnabledClusterInfo() map[string]config.ClusterInformation { return m.enabledClusters } // GetRemoteClusterInfo return enabled AND remote cluster info func (m Metadata) GetRemoteClusterInfo() map[string]config.ClusterInformation { return m.remoteClusters } // ClusterNameForFailoverVersion return the corresponding cluster name for a given failover version func (m Metadata) ClusterNameForFailoverVersion(failoverVersion int64) (string, error) { if failoverVersion == constants.EmptyVersion { return m.currentClusterName, nil } server, err := m.resolveServerName(failoverVersion) if err != nil { m.metrics.IncCounter(metrics.ClusterMetadataResolvingFailoverVersionCounter) return "", fmt.Errorf("failed to resolve failover version to a cluster: %v", err) } return server, nil } // gets the initial failover version for a cluster / domain // along with some helpers for a migration - should it be necessary func (m Metadata) getInitialFailoverVersion(cluster string, domainName string) int64 { info, ok := m.allClusters[cluster] if !ok { panic(fmt.Sprintf( "Unknown cluster name: %v with given cluster initial failover version map: %v.", cluster, m.allClusters, )) } // if using the minFailover Version during a cluster config, then return this from config // (assuming it's safe to do so). This is not the normal state of things and intended only // for when migrating versions. usingNewFailoverVersion := m.useNewFailoverVersionOverride(domainName) if usingNewFailoverVersion && info.NewInitialFailoverVersion != nil { m.log.Debug("using new failover version for cluster", tag.ClusterName(cluster), tag.WorkflowDomainName(domainName)) m.metrics.IncCounter(metrics.ClusterMetadataGettingMinFailoverVersionCounter) return *info.NewInitialFailoverVersion } // default behaviour - return the initial failover version - a marker to // identify the cluster for all counters m.log.Debug("getting failover version for cluster", tag.ClusterName(cluster), tag.WorkflowDomainName(domainName)) m.metrics.IncCounter(metrics.ClusterMetadataGettingFailoverVersionCounter) return info.InitialFailoverVersion } // resolves the server name from a version number. Better to use this // than to check versionToClusterName directly, as this also falls back to catch // when there's a migration NewInitialFailoverVersion func (m Metadata) resolveServerName(originalVersion int64) (string, error) { version := originalVersion % m.failoverVersionIncrement // attempt a lookup first server, ok := m.versionToClusterName[version] if ok { return server, nil } // else fall back on checking for new failover versions for name, cluster := range m.allClusters { if cluster.NewInitialFailoverVersion != nil && *cluster.NewInitialFailoverVersion == version { return name, nil } } m.metrics.IncCounter(metrics.ClusterMetadataFailureToResolveCounter) return "", fmt.Errorf("could not resolve failover version: %d", originalVersion) } ================================================ FILE: common/cluster/metadata_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cluster import ( "fmt" "sort" "testing" "testing/quick" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) func TestMetadataBehaviour(t *testing.T) { const clusterName1 = "c1" const initialFailoverVersionC1 = 0 const clusterName2 = "c2" const initialFailoverVersionC2 = 2 const clusterName3 = "c3" const initialFailoverVersionC3 = 4 const failoverVersionIncrement = 100 tests := map[string]struct { failoverCluster string currentVersion int64 expectedOut int64 }{ "a new domain, created in c1 already - a failover to c1 where it already is should have no effect": { failoverCluster: clusterName1, currentVersion: 0, expectedOut: 0, }, "a new domain, created in c1 already - a failover to c2 should set the failover version to be based on c2": { failoverCluster: clusterName2, currentVersion: 0, expectedOut: 2, }, "a failover to c3 should set the failover version to be based on c3": { failoverCluster: clusterName3, currentVersion: 2, expectedOut: 4, }, "a subsequent failover back to c1 should increment the failover version by failoverVersionIncrement": { failoverCluster: clusterName1, currentVersion: 2, expectedOut: 100, }, "when the current failover version matches the target cluster it should not increment the failover version": { failoverCluster: clusterName1, currentVersion: 100, expectedOut: 100, }, "and a subsequent fail back over to c2": { failoverCluster: clusterName2, currentVersion: 100, expectedOut: 102, }, "and a subsequent fail back over to c1 should skip over c3": { failoverCluster: clusterName1, currentVersion: 102, expectedOut: 200, }, "Ensuring the behaviour of getNextFailoverVersion can handle negative numbers 1": { failoverCluster: clusterName1, currentVersion: -1, expectedOut: 0, }, "Ensuring the behaviour of getNextFailoverVersion can handle negative numbers 2": { failoverCluster: clusterName2, currentVersion: -1, expectedOut: 2, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { m := Metadata{ failoverVersionIncrement: failoverVersionIncrement, allClusters: map[string]config.ClusterInformation{ clusterName1: { InitialFailoverVersion: initialFailoverVersionC1, }, clusterName2: { InitialFailoverVersion: initialFailoverVersionC2, }, clusterName3: { InitialFailoverVersion: initialFailoverVersionC3, }, }, versionToClusterName: map[int64]string{ initialFailoverVersionC1: clusterName1, initialFailoverVersionC2: clusterName2, initialFailoverVersionC3: clusterName3, }, useNewFailoverVersionOverride: func(domain string) bool { return false }, metrics: metrics.NewNoopMetricsClient().Scope(0), log: testlogger.New(t), } assert.Equal(t, td.expectedOut, m.GetNextFailoverVersion(td.failoverCluster, td.currentVersion, "a domain"), name) }) } } func TestFailoverVersionLogicIsMonotonic(t *testing.T) { const clusterName1 = "c1" const initialFailoverVersionC1 = 0 var minFailoverVersionC1 = int64(1) const clusterName2 = "c2" const initialFailoverVersionC2 = 2 var minFailoverVersionC2 = int64(3) const failoverVersionIncrement = 100 const someDomainMigrating = "a domain migrating" const someDomainNotMigrating = "a domain not migrating" m := Metadata{ failoverVersionIncrement: failoverVersionIncrement, allClusters: map[string]config.ClusterInformation{ clusterName1: { InitialFailoverVersion: initialFailoverVersionC1, NewInitialFailoverVersion: &minFailoverVersionC1, }, clusterName2: { InitialFailoverVersion: initialFailoverVersionC2, NewInitialFailoverVersion: &minFailoverVersionC2, }, }, versionToClusterName: map[int64]string{ initialFailoverVersionC1: clusterName1, initialFailoverVersionC2: clusterName2, }, useNewFailoverVersionOverride: func(domain string) bool { return someDomainMigrating == domain }, metrics: metrics.NewNoopMetricsClient().Scope(0), log: testlogger.New(t), } current := int64(0) iterations := 0 for iterations < 4000 { next := m.GetNextFailoverVersion(clusterName2, current, someDomainNotMigrating) assert.Truef(t, next > current, "current: %d, next %d", current, next) current = next next = m.GetNextFailoverVersion(clusterName1, current, someDomainNotMigrating) assert.Truef(t, next > current, "current: %d, next %d", current, next) current = next next = m.GetNextFailoverVersion(clusterName1, current, someDomainMigrating) assert.Truef(t, next > current, "current: %d, next %d", current, next) current = next iterations++ } } func TestResolvingClusterVersion(t *testing.T) { const clusterName1 = "c1" const initialFailoverVersionC1 = 0 var newInitialFailoverVersionC1 = int64(1) const clusterName2 = "c2" const initialFailoverVersionC2 = 2 var newInitialFailoverVersionC2 = int64(3) const failoverVersionIncrement = 100 tests := map[string]struct { input int64 expectedOutput string expectedErr error }{ "first cluster, normal version": { input: initialFailoverVersionC1, expectedOutput: clusterName1, }, "first cluster, minFailover version": { input: newInitialFailoverVersionC1, expectedOutput: clusterName1, }, "second cluster, normal version": { input: initialFailoverVersionC2, expectedOutput: clusterName2, }, "second cluster, minFailover version": { input: newInitialFailoverVersionC2, expectedOutput: clusterName2, }, "first cluster, normal version - higher versions": { input: 200, expectedOutput: clusterName1, }, "first cluster, minFailover version - higher versions": { input: 101, expectedOutput: clusterName1, }, "second cluster, normal initialFailover version - higher versions": { input: 302, expectedOutput: clusterName2, }, "second cluster, minFailover version - higher versions": { input: 403, expectedOutput: clusterName2, }, "invalid input": { input: 599, expectedErr: fmt.Errorf("could not resolve failover version: 599"), }, } m := Metadata{ failoverVersionIncrement: failoverVersionIncrement, allClusters: map[string]config.ClusterInformation{ clusterName1: { InitialFailoverVersion: initialFailoverVersionC1, NewInitialFailoverVersion: &newInitialFailoverVersionC1, }, clusterName2: { InitialFailoverVersion: initialFailoverVersionC2, NewInitialFailoverVersion: &newInitialFailoverVersionC2, }, }, versionToClusterName: map[int64]string{ initialFailoverVersionC1: clusterName1, initialFailoverVersionC2: clusterName2, }, metrics: metrics.NewNoopMetricsClient().Scope(0), log: log.NewNoop(), } for name, td := range tests { t.Run(name, func(*testing.T) { out, err := m.resolveServerName(td.input) assert.Equal(t, td.expectedOutput, out) assert.Equal(t, td.expectedErr, err) }) } } func TestIsPartOfTheSameCluster(t *testing.T) { const clusterName1 = "c1" const initialFailoverVersionC1 = 0 const clusterName2 = "c2" const initialFailoverVersionC2 = 2 const failoverVersionIncrement = 100 tests := map[string]struct { v1 int64 v2 int64 useMinFailoverVersionAllowance func(string) bool expectedResult bool }{ "normal case v1 - is from the same cluster": { v1: 0, v2: 0, expectedResult: true, }, "normal case v2 - is from a different cluster": { v1: 0, v2: 2, expectedResult: false, }, "normal case v3 - is from the same cluster": { v1: 0, v2: 100, expectedResult: true, }, "normal case v4 - is from a different cluster": { v1: 0, v2: 102, expectedResult: false, }, } for name, td := range tests { t.Run(name, func(*testing.T) { m := Metadata{ failoverVersionIncrement: failoverVersionIncrement, allClusters: map[string]config.ClusterInformation{ clusterName1: { InitialFailoverVersion: initialFailoverVersionC1, }, clusterName2: { InitialFailoverVersion: initialFailoverVersionC2, }, }, versionToClusterName: map[int64]string{ initialFailoverVersionC1: clusterName1, initialFailoverVersionC2: clusterName2, }, log: log.NewNoop(), metrics: metrics.NewNoopMetricsClient().Scope(0), } assert.Equal(t, td.expectedResult, m.IsVersionFromSameCluster(td.v1, td.v2), name) }) } } func TestIsPartOfTheSameClusterAPIFixing(t *testing.T) { const clusterName1 = "c1" const initialFailoverVersionC1 = 0 const clusterName2 = "c2" const initialFailoverVersionC2 = 2 const failoverVersionIncrement = 100 tests := []struct { v0 int64 v1 int64 expected bool }{ {v0: 0, v1: 0, expected: true}, {v0: 0, v1: 1, expected: false}, {v0: 0, v1: 2, expected: false}, {v0: 0, v1: 3, expected: false}, {v0: 0, v1: 4, expected: false}, {v0: 0, v1: 5, expected: false}, {v0: 0, v1: 6, expected: false}, {v0: 0, v1: 7, expected: false}, {v0: 0, v1: 8, expected: false}, {v0: 0, v1: 9, expected: false}, {v0: 0, v1: 10, expected: false}, {v0: 0, v1: 11, expected: false}, {v0: 0, v1: 12, expected: false}, {v0: 0, v1: 13, expected: false}, {v0: 0, v1: 14, expected: false}, {v0: 0, v1: 15, expected: false}, {v0: 0, v1: 16, expected: false}, {v0: 0, v1: 17, expected: false}, {v0: 0, v1: 18, expected: false}, {v0: 0, v1: 19, expected: false}, {v0: 0, v1: 20, expected: false}, {v0: 0, v1: 21, expected: false}, {v0: 0, v1: 22, expected: false}, {v0: 0, v1: 23, expected: false}, {v0: 0, v1: 24, expected: false}, {v0: 0, v1: 25, expected: false}, {v0: 0, v1: 26, expected: false}, {v0: 0, v1: 27, expected: false}, {v0: 0, v1: 28, expected: false}, {v0: 0, v1: 29, expected: false}, {v0: 0, v1: 30, expected: false}, {v0: 0, v1: 31, expected: false}, {v0: 0, v1: 32, expected: false}, {v0: 0, v1: 33, expected: false}, {v0: 0, v1: 34, expected: false}, {v0: 0, v1: 35, expected: false}, {v0: 0, v1: 36, expected: false}, {v0: 0, v1: 37, expected: false}, {v0: 0, v1: 38, expected: false}, {v0: 0, v1: 39, expected: false}, {v0: 0, v1: 40, expected: false}, {v0: 0, v1: 41, expected: false}, {v0: 0, v1: 42, expected: false}, {v0: 0, v1: 43, expected: false}, {v0: 0, v1: 44, expected: false}, {v0: 0, v1: 45, expected: false}, {v0: 0, v1: 46, expected: false}, {v0: 0, v1: 47, expected: false}, {v0: 0, v1: 48, expected: false}, {v0: 0, v1: 49, expected: false}, {v0: 0, v1: 50, expected: false}, {v0: 0, v1: 51, expected: false}, {v0: 0, v1: 52, expected: false}, {v0: 0, v1: 53, expected: false}, {v0: 0, v1: 54, expected: false}, {v0: 0, v1: 55, expected: false}, {v0: 0, v1: 56, expected: false}, {v0: 0, v1: 57, expected: false}, {v0: 0, v1: 58, expected: false}, {v0: 0, v1: 59, expected: false}, {v0: 0, v1: 60, expected: false}, {v0: 0, v1: 61, expected: false}, {v0: 0, v1: 62, expected: false}, {v0: 0, v1: 63, expected: false}, {v0: 0, v1: 64, expected: false}, {v0: 0, v1: 65, expected: false}, {v0: 0, v1: 66, expected: false}, {v0: 0, v1: 67, expected: false}, {v0: 0, v1: 68, expected: false}, {v0: 0, v1: 69, expected: false}, {v0: 0, v1: 70, expected: false}, {v0: 0, v1: 71, expected: false}, {v0: 0, v1: 72, expected: false}, {v0: 0, v1: 73, expected: false}, {v0: 0, v1: 74, expected: false}, {v0: 0, v1: 75, expected: false}, {v0: 0, v1: 76, expected: false}, {v0: 0, v1: 77, expected: false}, {v0: 0, v1: 78, expected: false}, {v0: 0, v1: 79, expected: false}, {v0: 0, v1: 80, expected: false}, {v0: 0, v1: 81, expected: false}, {v0: 0, v1: 82, expected: false}, {v0: 0, v1: 83, expected: false}, {v0: 0, v1: 84, expected: false}, {v0: 0, v1: 85, expected: false}, {v0: 0, v1: 86, expected: false}, {v0: 0, v1: 87, expected: false}, {v0: 0, v1: 88, expected: false}, {v0: 0, v1: 89, expected: false}, {v0: 0, v1: 90, expected: false}, {v0: 0, v1: 91, expected: false}, {v0: 0, v1: 92, expected: false}, {v0: 0, v1: 93, expected: false}, {v0: 0, v1: 94, expected: false}, {v0: 0, v1: 95, expected: false}, {v0: 0, v1: 96, expected: false}, {v0: 0, v1: 97, expected: false}, {v0: 0, v1: 98, expected: false}, {v0: 0, v1: 99, expected: false}, {v0: 0, v1: 100, expected: true}, {v0: 0, v1: 101, expected: false}, {v0: 0, v1: 102, expected: false}, {v0: 0, v1: 103, expected: false}, {v0: 0, v1: 104, expected: false}, {v0: 0, v1: 105, expected: false}, {v0: 0, v1: 106, expected: false}, {v0: 0, v1: 107, expected: false}, {v0: 0, v1: 108, expected: false}, {v0: 0, v1: 109, expected: false}, {v0: 0, v1: 110, expected: false}, {v0: 0, v1: 111, expected: false}, {v0: 0, v1: 112, expected: false}, {v0: 0, v1: 113, expected: false}, {v0: 0, v1: 114, expected: false}, {v0: 0, v1: 115, expected: false}, {v0: 0, v1: 116, expected: false}, {v0: 0, v1: 117, expected: false}, {v0: 0, v1: 118, expected: false}, {v0: 0, v1: 119, expected: false}, {v0: 0, v1: 120, expected: false}, {v0: 0, v1: 121, expected: false}, {v0: 0, v1: 122, expected: false}, {v0: 0, v1: 123, expected: false}, {v0: 0, v1: 124, expected: false}, {v0: 0, v1: 125, expected: false}, {v0: 0, v1: 126, expected: false}, {v0: 0, v1: 127, expected: false}, {v0: 0, v1: 128, expected: false}, {v0: 0, v1: 129, expected: false}, {v0: 0, v1: 130, expected: false}, {v0: 0, v1: 131, expected: false}, {v0: 0, v1: 132, expected: false}, {v0: 0, v1: 133, expected: false}, {v0: 0, v1: 134, expected: false}, {v0: 0, v1: 135, expected: false}, {v0: 0, v1: 136, expected: false}, {v0: 0, v1: 137, expected: false}, {v0: 0, v1: 138, expected: false}, {v0: 0, v1: 139, expected: false}, {v0: 0, v1: 140, expected: false}, {v0: 0, v1: 141, expected: false}, {v0: 0, v1: 142, expected: false}, {v0: 0, v1: 143, expected: false}, {v0: 0, v1: 144, expected: false}, {v0: 0, v1: 145, expected: false}, {v0: 0, v1: 146, expected: false}, {v0: 0, v1: 147, expected: false}, {v0: 0, v1: 148, expected: false}, {v0: 0, v1: 149, expected: false}, {v0: 0, v1: 150, expected: false}, {v0: 0, v1: 151, expected: false}, {v0: 0, v1: 152, expected: false}, {v0: 0, v1: 153, expected: false}, {v0: 0, v1: 154, expected: false}, {v0: 0, v1: 155, expected: false}, {v0: 0, v1: 156, expected: false}, {v0: 0, v1: 157, expected: false}, {v0: 0, v1: 158, expected: false}, {v0: 0, v1: 159, expected: false}, {v0: 0, v1: 160, expected: false}, {v0: 0, v1: 161, expected: false}, {v0: 0, v1: 162, expected: false}, {v0: 0, v1: 163, expected: false}, {v0: 0, v1: 164, expected: false}, {v0: 0, v1: 165, expected: false}, {v0: 0, v1: 166, expected: false}, {v0: 0, v1: 167, expected: false}, {v0: 0, v1: 168, expected: false}, {v0: 0, v1: 169, expected: false}, {v0: 0, v1: 170, expected: false}, {v0: 0, v1: 171, expected: false}, {v0: 0, v1: 172, expected: false}, {v0: 0, v1: 173, expected: false}, {v0: 0, v1: 174, expected: false}, {v0: 0, v1: 175, expected: false}, {v0: 0, v1: 176, expected: false}, {v0: 0, v1: 177, expected: false}, {v0: 0, v1: 178, expected: false}, {v0: 0, v1: 179, expected: false}, {v0: 0, v1: 180, expected: false}, {v0: 0, v1: 181, expected: false}, {v0: 0, v1: 182, expected: false}, {v0: 0, v1: 183, expected: false}, {v0: 0, v1: 184, expected: false}, {v0: 0, v1: 185, expected: false}, {v0: 0, v1: 186, expected: false}, {v0: 0, v1: 187, expected: false}, {v0: 0, v1: 188, expected: false}, {v0: 0, v1: 189, expected: false}, {v0: 0, v1: 190, expected: false}, {v0: 0, v1: 191, expected: false}, {v0: 0, v1: 192, expected: false}, {v0: 0, v1: 193, expected: false}, {v0: 0, v1: 194, expected: false}, {v0: 0, v1: 195, expected: false}, {v0: 0, v1: 196, expected: false}, {v0: 0, v1: 197, expected: false}, {v0: 0, v1: 198, expected: false}, {v0: 0, v1: 199, expected: false}, {v0: 0, v1: 200, expected: true}, } m := Metadata{ failoverVersionIncrement: failoverVersionIncrement, allClusters: map[string]config.ClusterInformation{ clusterName1: { InitialFailoverVersion: initialFailoverVersionC1, }, clusterName2: { InitialFailoverVersion: initialFailoverVersionC2, }, }, versionToClusterName: map[int64]string{ initialFailoverVersionC1: clusterName1, initialFailoverVersionC2: clusterName2, }, useNewFailoverVersionOverride: func(domain string) bool { return false }, metrics: metrics.NewNoopMetricsClient().Scope(0), log: log.NewNoop(), } for i := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { assert.Equal(t, tests[i].expected, m.IsVersionFromSameCluster(tests[i].v0, tests[i].v1)) }) } } func TestClusterNameForFailoverVersion(t *testing.T) { const clusterName1 = "c1" const initialFailoverVersionC1 = 0 const clusterName2 = "c2" const initialFailoverVersionC2 = 2 const failoverVersionIncrement = 100 tests := []struct { v0 int64 v1 int64 expected bool }{ {v0: 0, v1: 0, expected: true}, {v0: 0, v1: 1, expected: false}, {v0: 0, v1: 2, expected: false}, {v0: 0, v1: 3, expected: false}, {v0: 0, v1: 4, expected: false}, {v0: 0, v1: 5, expected: false}, {v0: 0, v1: 6, expected: false}, {v0: 0, v1: 7, expected: false}, {v0: 0, v1: 8, expected: false}, {v0: 0, v1: 9, expected: false}, {v0: 0, v1: 10, expected: false}, {v0: 0, v1: 11, expected: false}, {v0: 0, v1: 12, expected: false}, {v0: 0, v1: 13, expected: false}, {v0: 0, v1: 14, expected: false}, {v0: 0, v1: 15, expected: false}, {v0: 0, v1: 16, expected: false}, {v0: 0, v1: 17, expected: false}, {v0: 0, v1: 18, expected: false}, {v0: 0, v1: 19, expected: false}, {v0: 0, v1: 20, expected: false}, {v0: 0, v1: 21, expected: false}, {v0: 0, v1: 22, expected: false}, {v0: 0, v1: 23, expected: false}, {v0: 0, v1: 24, expected: false}, {v0: 0, v1: 25, expected: false}, {v0: 0, v1: 26, expected: false}, {v0: 0, v1: 27, expected: false}, {v0: 0, v1: 28, expected: false}, {v0: 0, v1: 29, expected: false}, {v0: 0, v1: 30, expected: false}, {v0: 0, v1: 31, expected: false}, {v0: 0, v1: 32, expected: false}, {v0: 0, v1: 33, expected: false}, {v0: 0, v1: 34, expected: false}, {v0: 0, v1: 35, expected: false}, {v0: 0, v1: 36, expected: false}, {v0: 0, v1: 37, expected: false}, {v0: 0, v1: 38, expected: false}, {v0: 0, v1: 39, expected: false}, {v0: 0, v1: 40, expected: false}, {v0: 0, v1: 41, expected: false}, {v0: 0, v1: 42, expected: false}, {v0: 0, v1: 43, expected: false}, {v0: 0, v1: 44, expected: false}, {v0: 0, v1: 45, expected: false}, {v0: 0, v1: 46, expected: false}, {v0: 0, v1: 47, expected: false}, {v0: 0, v1: 48, expected: false}, {v0: 0, v1: 49, expected: false}, {v0: 0, v1: 50, expected: false}, {v0: 0, v1: 51, expected: false}, {v0: 0, v1: 52, expected: false}, {v0: 0, v1: 53, expected: false}, {v0: 0, v1: 54, expected: false}, {v0: 0, v1: 55, expected: false}, {v0: 0, v1: 56, expected: false}, {v0: 0, v1: 57, expected: false}, {v0: 0, v1: 58, expected: false}, {v0: 0, v1: 59, expected: false}, {v0: 0, v1: 60, expected: false}, {v0: 0, v1: 61, expected: false}, {v0: 0, v1: 62, expected: false}, {v0: 0, v1: 63, expected: false}, {v0: 0, v1: 64, expected: false}, {v0: 0, v1: 65, expected: false}, {v0: 0, v1: 66, expected: false}, {v0: 0, v1: 67, expected: false}, {v0: 0, v1: 68, expected: false}, {v0: 0, v1: 69, expected: false}, {v0: 0, v1: 70, expected: false}, {v0: 0, v1: 71, expected: false}, {v0: 0, v1: 72, expected: false}, {v0: 0, v1: 73, expected: false}, {v0: 0, v1: 74, expected: false}, {v0: 0, v1: 75, expected: false}, {v0: 0, v1: 76, expected: false}, {v0: 0, v1: 77, expected: false}, {v0: 0, v1: 78, expected: false}, {v0: 0, v1: 79, expected: false}, {v0: 0, v1: 80, expected: false}, {v0: 0, v1: 81, expected: false}, {v0: 0, v1: 82, expected: false}, {v0: 0, v1: 83, expected: false}, {v0: 0, v1: 84, expected: false}, {v0: 0, v1: 85, expected: false}, {v0: 0, v1: 86, expected: false}, {v0: 0, v1: 87, expected: false}, {v0: 0, v1: 88, expected: false}, {v0: 0, v1: 89, expected: false}, {v0: 0, v1: 90, expected: false}, {v0: 0, v1: 91, expected: false}, {v0: 0, v1: 92, expected: false}, {v0: 0, v1: 93, expected: false}, {v0: 0, v1: 94, expected: false}, {v0: 0, v1: 95, expected: false}, {v0: 0, v1: 96, expected: false}, {v0: 0, v1: 97, expected: false}, {v0: 0, v1: 98, expected: false}, {v0: 0, v1: 99, expected: false}, {v0: 0, v1: 100, expected: true}, {v0: 0, v1: 101, expected: false}, {v0: 0, v1: 102, expected: false}, {v0: 0, v1: 103, expected: false}, {v0: 0, v1: 104, expected: false}, {v0: 0, v1: 105, expected: false}, {v0: 0, v1: 106, expected: false}, {v0: 0, v1: 107, expected: false}, {v0: 0, v1: 108, expected: false}, {v0: 0, v1: 109, expected: false}, {v0: 0, v1: 110, expected: false}, {v0: 0, v1: 111, expected: false}, {v0: 0, v1: 112, expected: false}, {v0: 0, v1: 113, expected: false}, {v0: 0, v1: 114, expected: false}, {v0: 0, v1: 115, expected: false}, {v0: 0, v1: 116, expected: false}, {v0: 0, v1: 117, expected: false}, {v0: 0, v1: 118, expected: false}, {v0: 0, v1: 119, expected: false}, {v0: 0, v1: 120, expected: false}, {v0: 0, v1: 121, expected: false}, {v0: 0, v1: 122, expected: false}, {v0: 0, v1: 123, expected: false}, {v0: 0, v1: 124, expected: false}, {v0: 0, v1: 125, expected: false}, {v0: 0, v1: 126, expected: false}, {v0: 0, v1: 127, expected: false}, {v0: 0, v1: 128, expected: false}, {v0: 0, v1: 129, expected: false}, {v0: 0, v1: 130, expected: false}, {v0: 0, v1: 131, expected: false}, {v0: 0, v1: 132, expected: false}, {v0: 0, v1: 133, expected: false}, {v0: 0, v1: 134, expected: false}, {v0: 0, v1: 135, expected: false}, {v0: 0, v1: 136, expected: false}, {v0: 0, v1: 137, expected: false}, {v0: 0, v1: 138, expected: false}, {v0: 0, v1: 139, expected: false}, {v0: 0, v1: 140, expected: false}, {v0: 0, v1: 141, expected: false}, {v0: 0, v1: 142, expected: false}, {v0: 0, v1: 143, expected: false}, {v0: 0, v1: 144, expected: false}, {v0: 0, v1: 145, expected: false}, {v0: 0, v1: 146, expected: false}, {v0: 0, v1: 147, expected: false}, {v0: 0, v1: 148, expected: false}, {v0: 0, v1: 149, expected: false}, {v0: 0, v1: 150, expected: false}, {v0: 0, v1: 151, expected: false}, {v0: 0, v1: 152, expected: false}, {v0: 0, v1: 153, expected: false}, {v0: 0, v1: 154, expected: false}, {v0: 0, v1: 155, expected: false}, {v0: 0, v1: 156, expected: false}, {v0: 0, v1: 157, expected: false}, {v0: 0, v1: 158, expected: false}, {v0: 0, v1: 159, expected: false}, {v0: 0, v1: 160, expected: false}, {v0: 0, v1: 161, expected: false}, {v0: 0, v1: 162, expected: false}, {v0: 0, v1: 163, expected: false}, {v0: 0, v1: 164, expected: false}, {v0: 0, v1: 165, expected: false}, {v0: 0, v1: 166, expected: false}, {v0: 0, v1: 167, expected: false}, {v0: 0, v1: 168, expected: false}, {v0: 0, v1: 169, expected: false}, {v0: 0, v1: 170, expected: false}, {v0: 0, v1: 171, expected: false}, {v0: 0, v1: 172, expected: false}, {v0: 0, v1: 173, expected: false}, {v0: 0, v1: 174, expected: false}, {v0: 0, v1: 175, expected: false}, {v0: 0, v1: 176, expected: false}, {v0: 0, v1: 177, expected: false}, {v0: 0, v1: 178, expected: false}, {v0: 0, v1: 179, expected: false}, {v0: 0, v1: 180, expected: false}, {v0: 0, v1: 181, expected: false}, {v0: 0, v1: 182, expected: false}, {v0: 0, v1: 183, expected: false}, {v0: 0, v1: 184, expected: false}, {v0: 0, v1: 185, expected: false}, {v0: 0, v1: 186, expected: false}, {v0: 0, v1: 187, expected: false}, {v0: 0, v1: 188, expected: false}, {v0: 0, v1: 189, expected: false}, {v0: 0, v1: 190, expected: false}, {v0: 0, v1: 191, expected: false}, {v0: 0, v1: 192, expected: false}, {v0: 0, v1: 193, expected: false}, {v0: 0, v1: 194, expected: false}, {v0: 0, v1: 195, expected: false}, {v0: 0, v1: 196, expected: false}, {v0: 0, v1: 197, expected: false}, {v0: 0, v1: 198, expected: false}, {v0: 0, v1: 199, expected: false}, {v0: 0, v1: 200, expected: true}, } m := Metadata{ failoverVersionIncrement: failoverVersionIncrement, allClusters: map[string]config.ClusterInformation{ clusterName1: { InitialFailoverVersion: initialFailoverVersionC1, }, clusterName2: { InitialFailoverVersion: initialFailoverVersionC2, }, }, versionToClusterName: map[int64]string{ initialFailoverVersionC1: clusterName1, initialFailoverVersionC2: clusterName2, }, metrics: metrics.NewNoopMetricsClient().Scope(0), useNewFailoverVersionOverride: func(domain string) bool { return false }, log: log.NewNoop(), } for i := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { assert.Equal(t, tests[i].expected, m.IsVersionFromSameCluster(tests[i].v0, tests[i].v1)) }) } } func TestServerResolution(t *testing.T) { const clusterName1 = "c1" const initialFailoverVersionC1 = 0 const clusterName2 = "c2" const initialFailoverVersionC2 = 2 const failoverVersionIncrement = 100 const domainToMigrate = "some domain" allClusters := map[string]config.ClusterInformation{ clusterName1: { InitialFailoverVersion: initialFailoverVersionC1, }, clusterName2: { InitialFailoverVersion: initialFailoverVersionC2, }, } impl := Metadata{ failoverVersionIncrement: failoverVersionIncrement, allClusters: allClusters, versionToClusterName: map[int64]string{ initialFailoverVersionC1: clusterName1, initialFailoverVersionC2: clusterName2, }, metrics: metrics.NewNoopMetricsClient().Scope(0), useNewFailoverVersionOverride: func(domain string) bool { return domain == domainToMigrate }, log: log.NewNoop(), } err := quick.Check(func(currentFOVersion int64, migrateDomain bool) bool { var nextFailoverVersion int64 if migrateDomain { fo := impl.GetNextFailoverVersion(clusterName1, currentFOVersion, domainToMigrate) nextFailoverVersion = fo } else { fo := impl.GetNextFailoverVersion(clusterName1, currentFOVersion, "a different domain") nextFailoverVersion = fo } // do a round-trip clusterNameResolved, err := impl.ClusterNameForFailoverVersion(nextFailoverVersion) require.NoError(t, err) return clusterName1 == clusterNameResolved }, &quick.Config{}) assert.NoError(t, err) } // the point of this test is to assert that there's no clumsy errors, and // in an unmigrated state, the old implementation for getting versions and the new // one are equal func TestNoChangesInUnmigratedState(t *testing.T) { const clusterName1 = "c1" const initialFailoverVersionC1 = 0 const clusterName2 = "c2" const initialFailoverVersionC2 = 2 const failoverVersionIncrement = 100 allClusters := map[string]config.ClusterInformation{ clusterName1: { InitialFailoverVersion: initialFailoverVersionC1, }, clusterName2: { InitialFailoverVersion: initialFailoverVersionC2, }, } oldImpl := func(cluster string, currentFailoverVersion int64) int64 { info, ok := allClusters[cluster] if !ok { panic(fmt.Sprintf( "Unknown cluster name: %v with given cluster initial failover version map: %v.", cluster, allClusters, )) } failoverVersion := currentFailoverVersion/failoverVersionIncrement*failoverVersionIncrement + info.InitialFailoverVersion if failoverVersion < currentFailoverVersion { return failoverVersion + failoverVersionIncrement } return failoverVersion } newImpl := Metadata{ failoverVersionIncrement: failoverVersionIncrement, allClusters: allClusters, versionToClusterName: map[int64]string{ initialFailoverVersionC1: clusterName1, initialFailoverVersionC2: clusterName2, }, metrics: metrics.NewNoopMetricsClient().Scope(0), useNewFailoverVersionOverride: func(domain string) bool { return false }, log: log.NewNoop(), } err := quick.CheckEqual(func(currVersion int64) int64 { // partially apply the cluster-name, since the fuzzer trying to guess that is pointless return oldImpl(clusterName2, currVersion) }, func(currentVersion int64) int64 { return newImpl.GetNextFailoverVersion(clusterName2, currentVersion, "some domain") // all domains are turned off }, &quick.Config{}) assert.NoError(t, err) } // the point of this test is to assert that there's no clumsy errors, and // in an unmigrated state, the old implementation for getting versions and the new // one are equal func TestFailoverVersionResolution(t *testing.T) { const clusterName1 = "c1" const initialFailoverVersionC1 = 0 var newFailoverVersionC1 = int64(1) const clusterName2 = "c2" const initialFailoverVersionC2 = 2 const failoverVersionIncrement = 100 allClusters := map[string]config.ClusterInformation{ clusterName1: { InitialFailoverVersion: initialFailoverVersionC1, NewInitialFailoverVersion: &newFailoverVersionC1, }, clusterName2: { InitialFailoverVersion: initialFailoverVersionC2, }, } sut := Metadata{ failoverVersionIncrement: failoverVersionIncrement, allClusters: allClusters, versionToClusterName: map[int64]string{ initialFailoverVersionC1: clusterName1, initialFailoverVersionC2: clusterName2, }, metrics: metrics.NewNoopMetricsClient().Scope(0), useNewFailoverVersionOverride: func(domain string) bool { return false }, log: log.NewNoop(), } tests := map[string]struct { in int64 expectedOut string expectedErr error }{ "normal failover version": { in: initialFailoverVersionC1, expectedOut: clusterName1, }, "New failover version": { in: initialFailoverVersionC1, expectedOut: clusterName1, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { out, err := sut.ClusterNameForFailoverVersion(td.in) assert.NoError(t, err) assert.Equal(t, td.expectedOut, out) }) } } func TestGetters(t *testing.T) { m := NewMetadata( config.ClusterGroupMetadata{ ClusterGroup: map[string]config.ClusterInformation{ "cluster0": { InitialFailoverVersion: 1, Enabled: true, }, "cluster1": { InitialFailoverVersion: 3, Enabled: true, }, "cluster2": { InitialFailoverVersion: 5, Enabled: false, }, }, FailoverVersionIncrement: 100, CurrentClusterName: "cluster0", PrimaryClusterName: "cluster0", }, func(d string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), ) assert.True(t, m.IsPrimaryCluster()) assert.Equal(t, "cluster0", m.GetCurrentClusterName()) // do existence checks assert.Equal(t, []string{"cluster0", "cluster1", "cluster2"}, keysOfClusterInfoMap(m.GetAllClusterInfo())) assert.Equal(t, []string{"cluster0", "cluster1"}, keysOfClusterInfoMap(m.GetEnabledClusterInfo())) assert.Equal(t, []string{"cluster1"}, keysOfClusterInfoMap(m.GetRemoteClusterInfo())) } func keysOfClusterInfoMap(m map[string]config.ClusterInformation) []string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } sort.Strings(keys) return keys } ================================================ FILE: common/cluster/metadata_test_utils.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cluster import ( "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" commonMetrics "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/service" ) const ( // TestCurrentClusterInitialFailoverVersion is initial failover version for current cluster TestCurrentClusterInitialFailoverVersion = int64(0) // TestAlternativeClusterInitialFailoverVersion is initial failover version for alternative cluster TestAlternativeClusterInitialFailoverVersion = int64(1) // TestDisabledClusterInitialFailoverVersion is initial failover version for disabled cluster TestDisabledClusterInitialFailoverVersion = int64(2) // TestFailoverVersionIncrement is failover version increment used for test TestFailoverVersionIncrement = int64(10) // TestCurrentClusterName is current cluster used for test TestCurrentClusterName = "active" // TestAlternativeClusterName is alternative cluster used for test TestAlternativeClusterName = "standby" // TestDisabledClusterName is disabled cluster used for test TestDisabledClusterName = "disabled" // TestRegion1 is region1 used for test TestRegion1 = "region1" // TestRegion2 is region2 used for test TestRegion2 = "region2" // TestCurrentClusterFrontendAddress is the ip port address of current cluster TestCurrentClusterFrontendAddress = "127.0.0.1:7104" // TestAlternativeClusterFrontendAddress is the ip port address of alternative cluster TestAlternativeClusterFrontendAddress = "127.0.0.1:8104" // TestClusterXDCTransport is the RPC transport used for XDC traffic TestClusterXDCTransport = "grpc" ) var ( // TestAllClusterNames is the all cluster names used for test TestAllClusterNames = []string{TestCurrentClusterName, TestAlternativeClusterName} // TestAllClusterInfo is the same as above, just convenient for test mocking TestAllClusterInfo = map[string]config.ClusterInformation{ TestCurrentClusterName: { Enabled: true, InitialFailoverVersion: TestCurrentClusterInitialFailoverVersion, RPCName: service.Frontend, RPCAddress: TestCurrentClusterFrontendAddress, RPCTransport: TestClusterXDCTransport, }, TestAlternativeClusterName: { Enabled: true, InitialFailoverVersion: TestAlternativeClusterInitialFailoverVersion, RPCName: service.Frontend, RPCAddress: TestAlternativeClusterFrontendAddress, RPCTransport: TestClusterXDCTransport, }, TestDisabledClusterName: { Enabled: false, InitialFailoverVersion: TestDisabledClusterInitialFailoverVersion, }, } // TestSingleDCAllClusterNames is the all cluster names used for test TestSingleDCAllClusterNames = []string{TestCurrentClusterName} // TestSingleDCClusterInfo is the same as above, just convenient for test mocking TestSingleDCClusterInfo = map[string]config.ClusterInformation{ TestCurrentClusterName: { Enabled: true, InitialFailoverVersion: TestCurrentClusterInitialFailoverVersion, RPCName: service.Frontend, RPCAddress: TestCurrentClusterFrontendAddress, RPCTransport: TestClusterXDCTransport, }, } // TestActiveClusterMetadata is metadata for an active cluster TestActiveClusterMetadata = NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: TestFailoverVersionIncrement, PrimaryClusterName: TestCurrentClusterName, CurrentClusterName: TestCurrentClusterName, ClusterGroup: TestAllClusterInfo, }, func(d string) bool { return false }, commonMetrics.NewNoopMetricsClient(), log.NewNoop(), ) // TestPassiveClusterMetadata is metadata for a passive cluster TestPassiveClusterMetadata = NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: TestFailoverVersionIncrement, PrimaryClusterName: TestCurrentClusterName, CurrentClusterName: TestAlternativeClusterName, ClusterGroup: TestAllClusterInfo, }, func(d string) bool { return false }, commonMetrics.NewNoopMetricsClient(), log.NewNoop(), ) ) // GetTestClusterMetadata return an cluster metadata instance, which is initialized func GetTestClusterMetadata(isPrimaryCluster bool) Metadata { primaryClusterName := TestCurrentClusterName if !isPrimaryCluster { primaryClusterName = TestAlternativeClusterName } return NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: TestFailoverVersionIncrement, PrimaryClusterName: primaryClusterName, CurrentClusterName: TestCurrentClusterName, ClusterGroup: TestAllClusterInfo, }, func(d string) bool { return false }, commonMetrics.NewNoopMetricsClient(), log.NewNoop(), ) } ================================================ FILE: common/cluster/utils.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cluster import ( "github.com/uber/cadence/common/persistence" ) // GetOrUseDefaultActiveCluster return the current cluster name or use the input if valid func GetOrUseDefaultActiveCluster(currentClusterName string, activeClusterName string) string { if len(activeClusterName) == 0 { return currentClusterName } return activeClusterName } // GetOrUseDefaultClusters return the current cluster or use the input if valid func GetOrUseDefaultClusters(currentClusterName string, clusters []*persistence.ClusterReplicationConfig) []*persistence.ClusterReplicationConfig { if len(clusters) == 0 { return []*persistence.ClusterReplicationConfig{ &persistence.ClusterReplicationConfig{ ClusterName: currentClusterName, }, } } return clusters } ================================================ FILE: common/cluster/utils_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cluster import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/persistence" ) func TestGetOrUseDefaultActiveCluster(t *testing.T) { tests := []struct { name string currentCluster string activeCluster string expectedCluster string }{ { name: "empty active cluster", currentCluster: "cluster1", activeCluster: "", expectedCluster: "cluster1", }, { name: "non-empty active cluster", currentCluster: "cluster1", activeCluster: "cluster2", expectedCluster: "cluster2", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := GetOrUseDefaultActiveCluster(tt.currentCluster, tt.activeCluster) assert.Equal(t, tt.expectedCluster, got) }) } } func TestGetOrUseDefaultClusters(t *testing.T) { tests := []struct { name string currentCluster string clusters []*persistence.ClusterReplicationConfig expectedClusters []*persistence.ClusterReplicationConfig }{ { name: "empty clusters", currentCluster: "cluster1", clusters: []*persistence.ClusterReplicationConfig{}, expectedClusters: []*persistence.ClusterReplicationConfig{ &persistence.ClusterReplicationConfig{ ClusterName: "cluster1", }, }, }, { name: "non-empty clusters", currentCluster: "cluster1", clusters: []*persistence.ClusterReplicationConfig{ &persistence.ClusterReplicationConfig{ ClusterName: "cluster2", }, }, expectedClusters: []*persistence.ClusterReplicationConfig{ &persistence.ClusterReplicationConfig{ ClusterName: "cluster2", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := GetOrUseDefaultClusters(tt.currentCluster, tt.clusters) assert.Equal(t, tt.expectedClusters, got) }) } } ================================================ FILE: common/codec/gob/gob.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gob import ( "bytes" "encoding/gob" "errors" "fmt" "reflect" ) var errEmptyArgument = errors.New("length of input argument is 0") // Encoder is wrapper of gob encoder/decoder type Encoder struct{} // NewGobEncoder create new Encoder func NewGobEncoder() *Encoder { return &Encoder{} } // Encode one or more objects to binary func (gobEncoder *Encoder) Encode(value ...interface{}) ([]byte, error) { if len(value) == 0 { return nil, errEmptyArgument } var buf bytes.Buffer enc := gob.NewEncoder(&buf) for i, obj := range value { if err := enc.Encode(obj); err != nil { return nil, fmt.Errorf( "unable to encode argument: %d, %v, with gob error: %v", i, reflect.TypeOf(obj), err) } } return buf.Bytes(), nil } // Decode binary to one or more objects func (gobEncoder *Encoder) Decode(input []byte, valuePtr ...interface{}) error { if len(valuePtr) == 0 { return errEmptyArgument } dec := gob.NewDecoder(bytes.NewBuffer(input)) for i, obj := range valuePtr { if err := dec.Decode(obj); err != nil { return fmt.Errorf( "unable to decode argument: %d, %v, with gob error: %v", i, reflect.TypeOf(obj), err) } } return nil } ================================================ FILE: common/codec/gob/gob_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gob import ( "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" ) type testStruct struct { Domain string WorkflowID string RunID string StartTime int64 } func TestGobEncoder(t *testing.T) { encoder := NewGobEncoder() domain := "test-domain" wid := uuid.New() rid := uuid.New() startTime := time.Now().UnixNano() // test encode and decode 1 object msg := &testStruct{ Domain: domain, WorkflowID: wid, RunID: rid, StartTime: startTime, } payload, err := encoder.Encode(msg) require.NoError(t, err) var decoded *testStruct err = encoder.Decode(payload, &decoded) require.NoError(t, err) require.Equal(t, msg, decoded) // test encode and decode 2 objects msg2 := "test-string" payload, err = encoder.Encode(msg2, msg) require.NoError(t, err) var decoded2 string err = encoder.Decode(payload, &decoded2, &decoded) require.NoError(t, err) require.Equal(t, msg, decoded) require.Equal(t, msg2, decoded2) // test encode and decode 0 object _, err = encoder.Encode() require.Error(t, err) err = encoder.Decode(payload) require.Error(t, err) } ================================================ FILE: common/codec/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination interfaces_mock.go -self_package github.com/uber/cadence/common/codec github.com/uber/cadence/common/codec BinaryEncoder package codec import ( "go.uber.org/thriftrw/protocol/stream" "go.uber.org/thriftrw/wire" "github.com/uber/cadence/common/types" ) type ( // BinaryEncoder represent the encoder which can serialize or deserialize object BinaryEncoder interface { Encode(obj ThriftObject) ([]byte, error) Decode(payload []byte, val ThriftObject) error } // ThriftObject represents a thrift object ThriftObject interface { FromWire(w wire.Value) error ToWire() (wire.Value, error) Encode(stream.Writer) error Decode(stream.Reader) error } ) const ( // used by thriftrw binary codec preambleVersion0 byte = 0x59 ) var ( // MissingBinaryEncodingVersion indicate that the encoding version is missing MissingBinaryEncodingVersion = &types.BadRequestError{Message: "Missing binary encoding version."} // InvalidBinaryEncodingVersion indicate that the encoding version is incorrect InvalidBinaryEncodingVersion = &types.BadRequestError{Message: "Invalid binary encoding version."} // MsgPayloadNotThriftEncoded indicate message is not thrift encoded MsgPayloadNotThriftEncoded = &types.BadRequestError{Message: "Message payload is not thrift encoded."} ) ================================================ FILE: common/codec/interfaces_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/codec (interfaces: BinaryEncoder) // // Generated by this command: // // mockgen -package codec -destination interfaces_mock.go -self_package github.com/uber/cadence/common/codec github.com/uber/cadence/common/codec BinaryEncoder // // Package codec is a generated GoMock package. package codec import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockBinaryEncoder is a mock of BinaryEncoder interface. type MockBinaryEncoder struct { ctrl *gomock.Controller recorder *MockBinaryEncoderMockRecorder isgomock struct{} } // MockBinaryEncoderMockRecorder is the mock recorder for MockBinaryEncoder. type MockBinaryEncoderMockRecorder struct { mock *MockBinaryEncoder } // NewMockBinaryEncoder creates a new mock instance. func NewMockBinaryEncoder(ctrl *gomock.Controller) *MockBinaryEncoder { mock := &MockBinaryEncoder{ctrl: ctrl} mock.recorder = &MockBinaryEncoderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockBinaryEncoder) EXPECT() *MockBinaryEncoderMockRecorder { return m.recorder } // Decode mocks base method. func (m *MockBinaryEncoder) Decode(payload []byte, val ThriftObject) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Decode", payload, val) ret0, _ := ret[0].(error) return ret0 } // Decode indicates an expected call of Decode. func (mr *MockBinaryEncoderMockRecorder) Decode(payload, val any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decode", reflect.TypeOf((*MockBinaryEncoder)(nil).Decode), payload, val) } // Encode mocks base method. func (m *MockBinaryEncoder) Encode(obj ThriftObject) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Encode", obj) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // Encode indicates an expected call of Encode. func (mr *MockBinaryEncoderMockRecorder) Encode(obj any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Encode", reflect.TypeOf((*MockBinaryEncoder)(nil).Encode), obj) } ================================================ FILE: common/codec/version0_thriftrw.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package codec import ( "bytes" "go.uber.org/thriftrw/protocol/binary" ) type ( // ThriftRWEncoder is an implementation using thrift rw for binary encoding / decoding // NOTE: this encoder only works for thrift struct ThriftRWEncoder struct { } ) var _ BinaryEncoder = (*ThriftRWEncoder)(nil) // NewThriftRWEncoder generate a new ThriftRWEncoder func NewThriftRWEncoder() *ThriftRWEncoder { return &ThriftRWEncoder{} } // Encode encode the object func (t *ThriftRWEncoder) Encode(obj ThriftObject) ([]byte, error) { if obj == nil { return nil, MsgPayloadNotThriftEncoded } var writer bytes.Buffer // use the first byte to version the serialization err := writer.WriteByte(preambleVersion0) if err != nil { return nil, err } sw := binary.Default.Writer(&writer) defer sw.Close() if err := obj.Encode(sw); err != nil { return nil, err } return writer.Bytes(), nil } // Decode decode the object func (t *ThriftRWEncoder) Decode(b []byte, val ThriftObject) error { if len(b) < 1 { return MissingBinaryEncodingVersion } version := b[0] if version != preambleVersion0 { return InvalidBinaryEncodingVersion } reader := bytes.NewReader(b[1:]) sr := binary.Default.Reader(reader) return val.Decode(sr) } ================================================ FILE: common/codec/version0_thriftrw_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package codec import ( "fmt" "log" "os" "sync" "testing" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/suite" "go.uber.org/multierr" workflow "github.com/uber/cadence/.gen/go/shared" ) type ( thriftRWEncoderSuite struct { suite.Suite encoder *ThriftRWEncoder } ) var ( thriftObject = &workflow.HistoryEvent{ Version: int64Ptr(1234), EventId: int64Ptr(130), Timestamp: int64Ptr(112345132134), EventType: workflow.EventTypeRequestCancelExternalWorkflowExecutionInitiated.Ptr(), RequestCancelExternalWorkflowExecutionInitiatedEventAttributes: &workflow.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ Domain: stringPtr("some random target domain"), WorkflowExecution: &workflow.WorkflowExecution{ WorkflowId: stringPtr("some random target workflow ID"), RunId: stringPtr("some random target run ID"), }, ChildWorkflowOnly: boolPtr(true), Control: []byte("some random control"), }, } thriftEncodedBinary = []byte{ 89, 10, 0, 10, 0, 0, 0, 0, 0, 0, 0, 130, 10, 0, 20, 0, 0, 0, 26, 40, 74, 172, 102, 8, 0, 30, 0, 0, 0, 23, 10, 0, 35, 0, 0, 0, 0, 0, 0, 4, 210, 12, 1, 44, 11, 0, 20, 0, 0, 0, 25, 115, 111, 109, 101, 32, 114, 97, 110, 100, 111, 109, 32, 116, 97, 114, 103, 101, 116, 32, 100, 111, 109, 97, 105, 110, 12, 0, 30, 11, 0, 10, 0, 0, 0, 30, 115, 111, 109, 101, 32, 114, 97, 110, 100, 111, 109, 32, 116, 97, 114, 103, 101, 116, 32, 119, 111, 114, 107, 102, 108, 111, 119, 32, 73, 68, 11, 0, 20, 0, 0, 0, 25, 115, 111, 109, 101, 32, 114, 97, 110, 100, 111, 109, 32, 116, 97, 114, 103, 101, 116, 32, 114, 117, 110, 32, 73, 68, 0, 11, 0, 40, 0, 0, 0, 19, 115, 111, 109, 101, 32, 114, 97, 110, 100, 111, 109, 32, 99, 111, 110, 116, 114, 111, 108, 2, 0, 50, 1, 0, 0, } ) func TestThriftRWEncoderSuite(t *testing.T) { s := new(thriftRWEncoderSuite) suite.Run(t, s) } func (s *thriftRWEncoderSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } s.encoder = NewThriftRWEncoder() } func (s *thriftRWEncoderSuite) TestEncode() { binary, err := s.encoder.Encode(thriftObject) s.Nil(err) s.Equal(thriftEncodedBinary, binary) } func (s *thriftRWEncoderSuite) TestEncodeConcurrent() { var wg sync.WaitGroup count := 200 errs := make([]error, count) wg.Add(count) for i := 0; i < count; i++ { go func(idx int) { defer wg.Done() binary, err := s.encoder.Encode(thriftObject) if err != nil { errs[idx] = err return } if diff := cmp.Diff(thriftEncodedBinary, binary); diff != "" { errs[idx] = fmt.Errorf("Mismatch (-want +got):\n%s", diff) return } }(i) } wg.Wait() s.NoError(multierr.Combine(errs...)) } func (s *thriftRWEncoderSuite) TestDecode() { var val workflow.HistoryEvent err := s.encoder.Decode(thriftEncodedBinary, &val) s.Nil(err) val.Equals(thriftObject) } func (s *thriftRWEncoderSuite) TestDecode_MissingVersion() { binary := []byte{} var val workflow.HistoryEvent err := s.encoder.Decode(binary, &val) s.Equal(MissingBinaryEncodingVersion, err) } func (s *thriftRWEncoderSuite) TestDecode_InvalidVersion() { binary := []byte{} binary = append(binary, thriftEncodedBinary...) binary[0] = preambleVersion0 - 1 var val workflow.HistoryEvent err := s.encoder.Decode(binary, &val) s.Equal(InvalidBinaryEncodingVersion, err) } func int64Ptr(v int64) *int64 { return &v } func stringPtr(v string) *string { return &v } func boolPtr(v bool) *bool { return &v } ================================================ FILE: common/collection/channelPriorityQueue.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import "fmt" const numPriorities = 2 // channelPriorityQueue is a priority queue built using channels type channelPriorityQueue struct { channels []chan interface{} shutdownCh chan struct{} } // ChannelPriorityQueue is an interface for a priority queue type ChannelPriorityQueue interface { Add(priority int, item interface{}) bool Remove() (interface{}, bool) Close() } // NewChannelPriorityQueue returns a ChannelPriorityQueue func NewChannelPriorityQueue(queueSize int) ChannelPriorityQueue { channels := make([]chan interface{}, numPriorities) for i := range channels { channels[i] = make(chan interface{}, queueSize) } return &channelPriorityQueue{ channels: channels, shutdownCh: make(chan struct{}), } } // Add adds an item to a channel in the queue. This is blocking and waits for // the queue to get empty if it is full. Returns false if the queue is closed. func (c *channelPriorityQueue) Add(priority int, item interface{}) bool { if priority >= numPriorities { panic(fmt.Sprintf("trying to add item with invalid priority %v, queue only supports %v priorities", priority, numPriorities)) } select { case c.channels[priority] <- item: case <-c.shutdownCh: return false } return true } // Remove removes an item from the priority queue. This is blocking till an // element becomes available in the priority queue func (c *channelPriorityQueue) Remove() (interface{}, bool) { // pick from highest priority if exists select { case item, ok := <-c.channels[0]: return item, ok case <-c.shutdownCh: return nil, false default: } // blocking select from all priorities var item interface{} var ok bool select { case item, ok = <-c.channels[0]: case item, ok = <-c.channels[1]: case <-c.shutdownCh: } return item, ok } // Destroy - destroys the channel priority queue func (c *channelPriorityQueue) Close() { close(c.shutdownCh) } ================================================ FILE: common/collection/channelPriorityQueue_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "math/rand" "testing" "github.com/stretchr/testify/assert" ) func TestChannelPriorityQueue(t *testing.T) { queue := NewChannelPriorityQueue(1) shutdown := queue.Add(1, 20) assert.True(t, shutdown) shutdown = queue.Add(0, 10) assert.True(t, shutdown) item, ok := queue.Remove() assert.Equal(t, 10, item) assert.True(t, ok) item, ok = queue.Remove() assert.Equal(t, 20, item) assert.True(t, ok) queue.Close() // once we close the channel we should get shutdown at least once for { shutdown = queue.Add(1, 20) if !shutdown { break } } item, ok = queue.Remove() assert.Nil(t, item) assert.False(t, ok) } func BenchmarkChannelPriorityQueue(b *testing.B) { queue := NewChannelPriorityQueue(100) for i := 0; i < 10; i++ { go sendChannelQueue(queue) } for n := 0; n < b.N; n++ { queue.Remove() } } func sendChannelQueue(queue ChannelPriorityQueue) { for { priority := rand.Int() % numPriorities queue.Add(priority, struct{}{}) } } ================================================ FILE: common/collection/concurrent_priority_queue.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import "sync" type ( concurrentPriorityQueueImpl[T any] struct { sync.RWMutex priorityQueue Queue[T] } ) // NewConcurrentPriorityQueue create a new concurrent priority queue func NewConcurrentPriorityQueue[T any](compareLess func(this T, other T) bool) Queue[T] { return &concurrentPriorityQueueImpl[T]{ priorityQueue: NewPriorityQueue(compareLess), } } // Peek returns the top item of the priority queue func (pq *concurrentPriorityQueueImpl[T]) Peek() (T, error) { pq.RLock() defer pq.RUnlock() return pq.priorityQueue.Peek() } // Add push an item to priority queue func (pq *concurrentPriorityQueueImpl[T]) Add(item T) { pq.Lock() defer pq.Unlock() pq.priorityQueue.Add(item) } // Remove pop an item from priority queue func (pq *concurrentPriorityQueueImpl[T]) Remove() (T, error) { pq.Lock() defer pq.Unlock() return pq.priorityQueue.Remove() } // IsEmpty indicate if the priority queue is empty func (pq *concurrentPriorityQueueImpl[T]) IsEmpty() bool { pq.RLock() defer pq.RUnlock() return pq.priorityQueue.IsEmpty() } // Len return the size of the queue func (pq *concurrentPriorityQueueImpl[T]) Len() int { pq.RLock() defer pq.RUnlock() return pq.priorityQueue.Len() } ================================================ FILE: common/collection/concurrent_queue.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "errors" "sync" ) type ( concurrentQueueImpl[T any] struct { sync.RWMutex items []T } ) // NewConcurrentQueue creates a new concurrent queue func NewConcurrentQueue[T any]() Queue[T] { return &concurrentQueueImpl[T]{ items: make([]T, 0, 1000), } } func (q *concurrentQueueImpl[T]) Peek() (T, error) { q.RLock() defer q.RUnlock() var item T if q.isEmptyLocked() { return item, errors.New("queue is empty") } return q.items[0], nil } func (q *concurrentQueueImpl[T]) Add(item T) { q.Lock() defer q.Unlock() q.items = append(q.items, item) } func (q *concurrentQueueImpl[T]) Remove() (T, error) { q.Lock() defer q.Unlock() var item T if q.isEmptyLocked() { return item, errors.New("queue is empty") } item = q.items[0] q.items = q.items[1:] return item, nil } func (q *concurrentQueueImpl[T]) IsEmpty() bool { q.RLock() defer q.RUnlock() return q.isEmptyLocked() } func (q *concurrentQueueImpl[T]) Len() int { q.RLock() defer q.RUnlock() return len(q.items) } func (q *concurrentQueueImpl[T]) isEmptyLocked() bool { return len(q.items) == 0 } ================================================ FILE: common/collection/concurrent_queue_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "math/rand" "sync" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type ( concurrentQueueSuite struct { *require.Assertions suite.Suite concurrentQueue *concurrentQueueImpl[int] } ) func TestConcurrentQueueSuite(t *testing.T) { s := new(concurrentQueueSuite) suite.Run(t, s) } func (s *concurrentQueueSuite) SetupTest() { s.Assertions = require.New(s.T()) s.concurrentQueue = NewConcurrentQueue[int]().(*concurrentQueueImpl[int]) } func (s *concurrentQueueSuite) TearDownTest() { } func (s *concurrentQueueSuite) TestAddAndRemove() { s.Equal(0, s.concurrentQueue.Len()) s.True(s.concurrentQueue.IsEmpty()) _, err := s.concurrentQueue.Peek() s.Error(err) _, err = s.concurrentQueue.Remove() s.Error(err) numItems := 100 items := make([]int, 0, numItems) for i := 0; i != 100; i++ { num := rand.Int() items = append(items, num) s.concurrentQueue.Add(num) s.Equal(i+1, s.concurrentQueue.Len()) } s.False(s.concurrentQueue.IsEmpty()) num, err := s.concurrentQueue.Peek() s.NoError(err) s.Equal(items[0], num) for i := 0; i != 100; i++ { num, err := s.concurrentQueue.Remove() s.NoError(err) s.Equal(items[i], num) s.Equal(numItems-i-1, s.concurrentQueue.Len()) } s.True(s.concurrentQueue.IsEmpty()) _, err = s.concurrentQueue.Peek() s.Error(err) _, err = s.concurrentQueue.Remove() s.Error(err) } func (s *concurrentQueueSuite) TestMultipleProducer() { concurrency := 10 numItemsPerProducer := 10 var wg sync.WaitGroup for i := 0; i != concurrency; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j != numItemsPerProducer; j++ { s.concurrentQueue.Add(rand.Int()) } }() } wg.Wait() expectedLength := concurrency * numItemsPerProducer s.Equal(expectedLength, s.concurrentQueue.Len()) s.False(s.concurrentQueue.IsEmpty()) for i := 0; i != expectedLength; i++ { _, _ = s.concurrentQueue.Remove() } } func BenchmarkConcurrentQueue(b *testing.B) { queue := NewConcurrentQueue[testTask]() for i := 0; i < 100; i++ { go send(queue) } for n := 0; n < b.N; n++ { remove(queue) } } ================================================ FILE: common/collection/concurrent_tx_map.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "sync" "sync/atomic" ) const ( // nShards represents the number of shards // At any given point of time, there can only // be nShards number of concurrent writers to // the map at max nShards = 32 ) type ( // ShardedConcurrentTxMap is an implementation of // ConcurrentMap that internally uses multiple // sharded maps to increase parallelism ShardedConcurrentTxMap struct { shards [nShards]mapShard hashfn HashFunc size int32 initialCap int } // mapIteratorImpl represents an iterator type // for the concurrent map. mapIteratorImpl struct { stopCh chan struct{} dataCh chan *MapEntry } // mapShard represents a single instance // of thread safe map mapShard struct { sync.RWMutex items map[interface{}]interface{} } ) // NewShardedConcurrentTxMap returns an instance of ShardedConcurrentMap // // ShardedConcurrentMap is a thread safe map that maintains upto nShards // number of maps internally to allow nShards writers to be acive at the // same time. This map *does not* use re-entrant locks, so access to the // map during iterator can cause a dead lock. // // @param initialSz // // The initial size for the map // // @param hashfn // // The hash function to use for sharding func NewShardedConcurrentTxMap(initialCap int, hashfn HashFunc) ConcurrentTxMap { cmap := new(ShardedConcurrentTxMap) cmap.hashfn = hashfn cmap.initialCap = max(nShards, initialCap/nShards) return cmap } // Get returns the value corresponding to the key, if it exist func (cmap *ShardedConcurrentTxMap) Get(key interface{}) (interface{}, bool) { shard := cmap.getShard(key) var ok bool var value interface{} shard.RLock() if shard.items != nil { value, ok = shard.items[key] } shard.RUnlock() return value, ok } // Contains returns true if the key exist and false otherwise func (cmap *ShardedConcurrentTxMap) Contains(key interface{}) bool { _, ok := cmap.Get(key) return ok } // Put records the given key value mapping. Overwrites previous values func (cmap *ShardedConcurrentTxMap) Put(key interface{}, value interface{}) { shard := cmap.getShard(key) shard.Lock() cmap.lazyInitShard(shard) _, ok := shard.items[key] if !ok { atomic.AddInt32(&cmap.size, 1) } shard.items[key] = value shard.Unlock() } // PutIfNotExist records the mapping, if there is no mapping for this key already // Returns true if the mapping was recorded, false otherwise func (cmap *ShardedConcurrentTxMap) PutIfNotExist(key interface{}, value interface{}) bool { shard := cmap.getShard(key) var ok bool shard.Lock() cmap.lazyInitShard(shard) _, ok = shard.items[key] if !ok { shard.items[key] = value atomic.AddInt32(&cmap.size, 1) } shard.Unlock() return !ok } // Remove deletes the given key from the map func (cmap *ShardedConcurrentTxMap) Remove(key interface{}) { shard := cmap.getShard(key) shard.Lock() cmap.lazyInitShard(shard) _, ok := shard.items[key] if ok { delete(shard.items, key) atomic.AddInt32(&cmap.size, -1) } shard.Unlock() } // GetAndDo returns the value corresponding to the key, and apply fn to key value before return value // return (value, value exist or not, error when evaluation fn) func (cmap *ShardedConcurrentTxMap) GetAndDo(key interface{}, fn ActionFunc) (interface{}, bool, error) { shard := cmap.getShard(key) var value interface{} var ok bool var err error shard.Lock() if shard.items != nil { value, ok = shard.items[key] if ok { err = fn(key, value) } } shard.Unlock() return value, ok, err } // PutOrDo put the key value in the map, if key does not exists, otherwise, call fn with existing key and value // return (value, fn evaluated or not, error when evaluation fn) func (cmap *ShardedConcurrentTxMap) PutOrDo(key interface{}, value interface{}, fn ActionFunc) (interface{}, bool, error) { shard := cmap.getShard(key) var err error shard.Lock() cmap.lazyInitShard(shard) v, ok := shard.items[key] if !ok { shard.items[key] = value v = value atomic.AddInt32(&cmap.size, 1) } else { err = fn(key, v) } shard.Unlock() return v, ok, err } // RemoveIf deletes the given key from the map if fn return true func (cmap *ShardedConcurrentTxMap) RemoveIf(key interface{}, fn PredicateFunc) bool { shard := cmap.getShard(key) var removed bool shard.Lock() if shard.items != nil { value, ok := shard.items[key] if ok && fn(key, value) { removed = true delete(shard.items, key) atomic.AddInt32(&cmap.size, -1) } } shard.Unlock() return removed } func newMapIterator() *mapIteratorImpl { return &mapIteratorImpl{ dataCh: make(chan *MapEntry, 8), stopCh: make(chan struct{}), } } // Close closes the iterator func (it *mapIteratorImpl) Close() { close(it.stopCh) } // Entries returns a channel of map entries func (it *mapIteratorImpl) Entries() <-chan *MapEntry { return it.dataCh } // Iter returns an iterator to the map. This map // does not use re-entrant locks, so access or modification // to the map during iteration can cause a dead lock. func (cmap *ShardedConcurrentTxMap) Iter() MapIterator { iterator := newMapIterator() go func(iterator *mapIteratorImpl) { for i := 0; i < nShards; i++ { cmap.shards[i].RLock() for k, v := range cmap.shards[i].items { entry := &MapEntry{Key: k, Value: v} select { case iterator.dataCh <- entry: case <-iterator.stopCh: cmap.shards[i].RUnlock() close(iterator.dataCh) return } } cmap.shards[i].RUnlock() } close(iterator.dataCh) }(iterator) return iterator } // Len returns the number of items in the map func (cmap *ShardedConcurrentTxMap) Len() int { return int(atomic.LoadInt32(&cmap.size)) } func (cmap *ShardedConcurrentTxMap) getShard(key interface{}) *mapShard { shardIdx := cmap.hashfn(key) % nShards return &cmap.shards[shardIdx] } func (cmap *ShardedConcurrentTxMap) lazyInitShard(shard *mapShard) { if shard.items == nil { shard.items = make(map[interface{}]interface{}, cmap.initialCap) } } ================================================ FILE: common/collection/concurrent_tx_map_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "errors" "math/rand" "sync" "sync/atomic" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type ( ConcurrentTxMapSuite struct { *require.Assertions // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, not merely log an error suite.Suite } boolType bool intType int ) func TestConcurrentTxMapSuite(t *testing.T) { suite.Run(t, new(ConcurrentTxMapSuite)) } func (s *ConcurrentTxMapSuite) SetupTest() { s.Assertions = require.New(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil } func (s *ConcurrentTxMapSuite) TestLen() { testMap := NewShardedConcurrentTxMap(1, UUIDHashCode) key1 := "0001" testMap.Put(key1, boolType(true)) s.Equal(1, testMap.Len(), "Wrong concurrent map size") testMap.Put(key1, boolType(false)) s.Equal(1, testMap.Len(), "Wrong concurrent map size") key2 := "0002" testMap.Put(key2, boolType(false)) s.Equal(2, testMap.Len(), "Wrong concurrent map size") testMap.PutIfNotExist(key2, boolType(false)) s.Equal(2, testMap.Len(), "Wrong concurrent map size") testMap.Remove(key2) s.Equal(1, testMap.Len(), "Wrong concurrent map size") testMap.Remove(key2) s.Equal(1, testMap.Len(), "Wrong concurrent map size") } func (s *ConcurrentTxMapSuite) TestGetAndDo() { testMap := NewShardedConcurrentTxMap(1, UUIDHashCode) key := uuid.New() var value intType fnApplied := false interf, ok, err := testMap.GetAndDo(key, func(key interface{}, value interface{}) error { fnApplied = true return nil }) s.Nil(interf, "GetAndDo should return nil when key not found") s.Nil(err, "GetAndDo should return nil when function not applied") s.False(ok, "GetAndDo should return false when key not found") s.False(fnApplied, "GetAndDo should not apply function when key not exixts") value = intType(1) testMap.Put(key, &value) interf, ok, err = testMap.GetAndDo(key, func(key interface{}, value interface{}) error { fnApplied = true intValue := value.(*intType) *intValue++ return errors.New("some err") }) value1 := interf.(*intType) s.Equal(*(value1), intType(2)) s.NotNil(err, "GetAndDo should return non nil when function applied") s.True(ok, "GetAndDo should return true when key found") s.True(fnApplied, "GetAndDo should apply function when key exixts") } func (s *ConcurrentTxMapSuite) TestPutOrDo() { testMap := NewShardedConcurrentTxMap(1, UUIDHashCode) key := uuid.New() var value intType fnApplied := false value = intType(1) interf, ok, err := testMap.PutOrDo(key, &value, func(key interface{}, value interface{}) error { fnApplied = true return errors.New("some err") }) valueRetuern := interf.(*intType) s.Equal(value, *valueRetuern) s.Nil(err, "PutOrDo should return nil when function not applied") s.False(ok, "PutOrDo should return false when function not applied") s.False(fnApplied, "PutOrDo should not apply function when key not exixts") anotherValue := intType(111) interf, ok, err = testMap.PutOrDo(key, &anotherValue, func(key interface{}, value interface{}) error { fnApplied = true intValue := value.(*intType) *intValue++ return errors.New("some err") }) valueRetuern = interf.(*intType) s.Equal(value, *valueRetuern) s.NotNil(err, "PutOrDo should return non nil when function applied") s.True(ok, "PutOrDo should return true when function applied") s.True(fnApplied, "PutOrDo should apply function when key exixts") } func (s *ConcurrentTxMapSuite) TestRemoveIf() { testMap := NewShardedConcurrentTxMap(1, UUIDHashCode) key := uuid.New() value := intType(1) testMap.Put(key, &value) removed := testMap.RemoveIf(key, func(key interface{}, value interface{}) bool { intValue := value.(*intType) return *intValue == intType(2) }) s.Equal(1, testMap.Len(), "TestRemoveIf should only entry if condition is met") s.False(removed, "TestRemoveIf should return false if key is not deleted") removed = testMap.RemoveIf(key, func(key interface{}, value interface{}) bool { intValue := value.(*intType) return *intValue == intType(1) }) s.Equal(0, testMap.Len(), "TestRemoveIf should only entry if condition is met") s.True(removed, "TestRemoveIf should return true if key is deleted") } func (s *ConcurrentTxMapSuite) TestGetAfterPut() { countMap := make(map[string]int) testMap := NewShardedConcurrentTxMap(1, UUIDHashCode) for i := 0; i < 1024; i++ { key := uuid.New() countMap[key] = 0 testMap.Put(key, boolType(true)) } for k := range countMap { v, ok := testMap.Get(k) boolValue := v.(boolType) s.True(ok, "Get after put failed") s.True(bool(boolValue), "Wrong value returned from map") } s.Equal(len(countMap), testMap.Len(), "Size() returned wrong value") it := testMap.Iter() for entry := range it.Entries() { countMap[entry.Key.(string)]++ } it.Close() for _, v := range countMap { s.Equal(1, v, "Iterator test failed") } for k := range countMap { testMap.Remove(k) } s.Equal(0, testMap.Len(), "Map returned non-zero size after deleting all entries") } func (s *ConcurrentTxMapSuite) TestPutIfNotExist() { testMap := NewShardedConcurrentTxMap(1, UUIDHashCode) key := uuid.New() ok := testMap.PutIfNotExist(key, boolType(true)) s.True(ok, "PutIfNotExist failed to insert item") ok = testMap.PutIfNotExist(key, boolType(true)) s.False(ok, "PutIfNotExist invariant failed") } func (s *ConcurrentTxMapSuite) TestMapConcurrency() { nKeys := 1024 keys := make([]string, nKeys) for i := 0; i < nKeys; i++ { keys[i] = uuid.New() } var total int32 var startWG sync.WaitGroup var doneWG sync.WaitGroup testMap := NewShardedConcurrentTxMap(1024, UUIDHashCode) startWG.Add(1) for i := 0; i < 10; i++ { doneWG.Add(1) go func() { startWG.Wait() for n := 0; n < nKeys; n++ { val := intType(rand.Int()) if testMap.PutIfNotExist(keys[n], val) { atomic.AddInt32(&total, int32(val)) _, ok := testMap.Get(keys[n]) s.True(ok, "Concurrency Get test failed") } } doneWG.Done() }() } startWG.Done() doneWG.Wait() s.Equal(nKeys, testMap.Len(), "Wrong concurrent map size") var gotTotal int32 for i := 0; i < nKeys; i++ { v, ok := testMap.Get(keys[i]) s.True(ok, "Get failed to find previously inserted key") intVal := v.(intType) gotTotal += int32(intVal) } s.Equal(total, gotTotal, "Concurrent put test failed, wrong sum of values inserted") } ================================================ FILE: common/collection/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection type ( // Queue is the interface for queue Queue[T any] interface { // Peek returns the first item of the queue Peek() (T, error) // Add push an item to the queue Add(item T) // Remove pop an item from the queue Remove() (T, error) // IsEmpty indicate if the queue is empty IsEmpty() bool // Len return the size of the queue Len() int } // HashFunc represents a hash function for string HashFunc func(interface{}) uint32 // ActionFunc take a key and value, do calculation and return err ActionFunc func(key interface{}, value interface{}) error // PredicateFunc take a key and value, do calculation and return boolean PredicateFunc func(key interface{}, value interface{}) bool // ConcurrentTxMap is a generic interface for any implementation of a dictionary // or a key value lookup table that is thread safe, and providing functionality // to modify key / value pair inside within a transaction ConcurrentTxMap interface { // Get returns the value for the given key Get(key interface{}) (interface{}, bool) // Contains returns true if the key exist and false otherwise Contains(key interface{}) bool // Put records the mapping from given key to value Put(key interface{}, value interface{}) // PutIfNotExist records the key value mapping only // if the mapping does not already exist PutIfNotExist(key interface{}, value interface{}) bool // Remove deletes the key from the map Remove(key interface{}) // GetAndDo returns the value corresponding to the key, and apply fn to key value before return value // return (value, value exist or not, error when evaluation fn) GetAndDo(key interface{}, fn ActionFunc) (interface{}, bool, error) // PutOrDo put the key value in the map, if key does not exists, otherwise, call fn with existing key and value // return (value, fn evaluated or not, error when evaluation fn) PutOrDo(key interface{}, value interface{}, fn ActionFunc) (interface{}, bool, error) // RemoveIf deletes the given key from the map if fn return true // return whether the key is removed or not RemoveIf(key interface{}, fn PredicateFunc) bool // Iter returns an iterator to the map Iter() MapIterator // Len returns the number of items in the map Len() int } // MapIterator represents the interface for map iterators MapIterator interface { // Close closes the iterator // and releases any allocated resources Close() // Entries returns a channel of MapEntry // objects that can be used in a range loop Entries() <-chan *MapEntry } // MapEntry represents a key-value entry within the map MapEntry struct { // Key represents the key Key interface{} // Value represents the value Value interface{} } ) const ( // UUIDStringLength is the length of an UUID represented as a hex string UUIDStringLength = 36 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ) ================================================ FILE: common/collection/iterator.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection type ( // Iterator represents the interface for iterator Iterator interface { // HasNext return whether this iterator has next value HasNext() bool // Next returns the next item and error Next() (interface{}, error) } ) ================================================ FILE: common/collection/ordered_map.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "container/list" "sync" ) type ( // OrderedMap is an interface for a dictionary which during iteration // return all values in their insertion order OrderedMap interface { // Get returns the value for the given key Get(key interface{}) (interface{}, bool) // Contains returns true if the key exist and false otherwise Contains(key interface{}) bool // Put records the mapping from given key to value Put(key interface{}, value interface{}) // Remove deletes the key from the map Remove(key interface{}) // Iter returns an iterator to the map which iterates over map entries // in insertion order Iter() MapIterator // Len returns the number of items in the map Len() int } orderedMapValue struct { value interface{} listElement *list.Element } orderedMap struct { items map[interface{}]orderedMapValue list *list.List } concurrentOrderedMap struct { sync.RWMutex orderedMap *orderedMap } ) // NewOrderedMap creates a new OrderedMap // implementation has O(1) complexity for all methods func NewOrderedMap() OrderedMap { return &orderedMap{ items: make(map[interface{}]orderedMapValue), list: list.New(), } } func (m *orderedMap) Get(key interface{}) (interface{}, bool) { if mapValue, ok := m.items[key]; ok { return mapValue.value, true } return nil, false } func (m *orderedMap) Contains(key interface{}) bool { _, ok := m.items[key] return ok } func (m *orderedMap) Put(key interface{}, value interface{}) { // remove existing key if there's one m.Remove(key) listElement := m.list.PushBack(&MapEntry{ Key: key, Value: value, }) m.items[key] = orderedMapValue{ value: value, listElement: listElement, } } func (m *orderedMap) Remove(key interface{}) { mapValue, ok := m.items[key] if !ok { return } m.list.Remove(mapValue.listElement) delete(m.items, key) } func (m *orderedMap) Iter() MapIterator { iterator := newMapIterator() go orderedMapIteratorLoop(nil, iterator, m.list) return iterator } func (m *orderedMap) Len() int { return len(m.items) } // NewConcurrentOrderedMap creates a new thread safe OrderedMap func NewConcurrentOrderedMap() OrderedMap { return &concurrentOrderedMap{ orderedMap: NewOrderedMap().(*orderedMap), } } func (m *concurrentOrderedMap) Get(key interface{}) (interface{}, bool) { m.RLock() defer m.RUnlock() return m.orderedMap.Get(key) } func (m *concurrentOrderedMap) Contains(key interface{}) bool { m.RLock() defer m.RUnlock() return m.orderedMap.Contains(key) } func (m *concurrentOrderedMap) Put(key interface{}, value interface{}) { m.Lock() defer m.Unlock() m.orderedMap.Put(key, value) } func (m *concurrentOrderedMap) Remove(key interface{}) { m.Lock() defer m.Unlock() m.orderedMap.Remove(key) } // Iter returns an iterator to the map. // NOTE that any modification to the map during iteration // will lead to a dead lock. func (m *concurrentOrderedMap) Iter() MapIterator { iterator := newMapIterator() go orderedMapIteratorLoop(&m.RWMutex, iterator, m.orderedMap.list) return iterator } func (m *concurrentOrderedMap) Len() int { m.RLock() defer m.RUnlock() return m.orderedMap.Len() } func orderedMapIteratorLoop( mutex *sync.RWMutex, iterator *mapIteratorImpl, list *list.List, ) { if mutex != nil { mutex.RLock() defer mutex.RUnlock() } for element := list.Front(); element != nil; element = element.Next() { select { case iterator.dataCh <- element.Value.(*MapEntry): // no-op case <-iterator.stopCh: close(iterator.dataCh) return } } close(iterator.dataCh) } ================================================ FILE: common/collection/ordered_map_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type ( orderedMapSuite struct { *require.Assertions suite.Suite orderedMap OrderedMap } ) func TestOrderedMapSuite(t *testing.T) { s := new(orderedMapSuite) suite.Run(t, s) } func (s *orderedMapSuite) SetupTest() { s.Assertions = require.New(s.T()) s.orderedMap = NewOrderedMap() } func (s *orderedMapSuite) TestBasicOperation() { key := "0001" value1 := boolType(true) value2 := intType(1234) s.Equal(0, s.orderedMap.Len()) // remove a non-exist key s.orderedMap.Remove(key) // get a non-exist key v, ok := s.orderedMap.Get(key) s.False(ok) s.Nil(v) s.False(s.orderedMap.Contains(key)) s.orderedMap.Put(key, value1) v, ok = s.orderedMap.Get(key) s.True(ok) s.Equal(value1, v) s.True(s.orderedMap.Contains(key)) s.Equal(1, s.orderedMap.Len()) s.orderedMap.Put(key, value2) v, ok = s.orderedMap.Get(key) s.True(ok) s.Equal(value2, v) s.True(s.orderedMap.Contains(key)) s.Equal(1, s.orderedMap.Len()) s.orderedMap.Remove(key) s.False(s.orderedMap.Contains(key)) s.Equal(0, s.orderedMap.Len()) } func (s *orderedMapSuite) TestIterator() { numItems := 10 for i := 0; i != numItems; i++ { s.orderedMap.Put(i, intType(i)) } iter := s.orderedMap.Iter() numIterated := 0 for entry := range iter.Entries() { s.Equal(numIterated, entry.Key.(int)) s.Equal(intType(numIterated), entry.Value.(intType)) numIterated++ } iter.Close() // remove all even numbers for i := 0; i != numItems; i++ { if i%2 == 0 { s.orderedMap.Remove(i) } } // add more odd numbers at the end for i := numItems; i != 2*numItems; i++ { if i%2 == 1 { s.orderedMap.Put(i, intType(i)) } } iter = s.orderedMap.Iter() numIterated = 0 for entry := range iter.Entries() { s.Equal(2*numIterated+1, entry.Key.(int)) s.Equal(intType(2*numIterated+1), entry.Value.(intType)) numIterated++ } iter.Close() } ================================================ FILE: common/collection/pagingIterator.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection type ( // PaginationFn is the function which get a page of results PaginationFn func(paginationToken []byte) ([]interface{}, []byte, error) // PagingIteratorImpl is the implementation of PagingIterator PagingIteratorImpl struct { paginationFn PaginationFn pageToken []byte pageErr error pageItems []interface{} nextPageItemIndex int } ) // NewPagingIterator create a new paging iterator // TODO: this implementation should be removed in favor of pagination/iterator.go func NewPagingIterator(paginationFn PaginationFn) Iterator { iter := &PagingIteratorImpl{ paginationFn: paginationFn, pageToken: nil, pageErr: nil, pageItems: nil, nextPageItemIndex: 0, } iter.getNextPage() // this will initialize the paging iterator return iter } // HasNext return whether has next item or err func (iter *PagingIteratorImpl) HasNext() bool { // pagination encounters error if iter.pageErr != nil { return true } // still have local cached item to return if iter.nextPageItemIndex < len(iter.pageItems) { return true } if len(iter.pageToken) != 0 { iter.getNextPage() return iter.HasNext() } return false } // Next return next item or err func (iter *PagingIteratorImpl) Next() (interface{}, error) { if !iter.HasNext() { panic("HistoryEventIterator Next() called without checking HasNext()") } if iter.pageErr != nil { err := iter.pageErr iter.pageErr = nil return nil, err } // we have cached events if iter.nextPageItemIndex < len(iter.pageItems) { index := iter.nextPageItemIndex iter.nextPageItemIndex++ return iter.pageItems[index], nil } panic("HistoryEventIterator Next() should return either a history event or a err") } func (iter *PagingIteratorImpl) getNextPage() { items, token, err := iter.paginationFn(iter.pageToken) if err == nil { iter.pageItems = items iter.pageToken = token iter.pageErr = nil } else { iter.pageItems = nil iter.pageToken = nil iter.pageErr = err } iter.nextPageItemIndex = 0 } ================================================ FILE: common/collection/pagingIterator_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "errors" "log" "os" "testing" "github.com/stretchr/testify/suite" ) type ( pagingIteratorSuite struct { suite.Suite } ) func TestPagingIteratorSuite(t *testing.T) { s := new(pagingIteratorSuite) suite.Run(t, s) } func (s *pagingIteratorSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } func (s *pagingIteratorSuite) TearDownSuite() { } func (s *pagingIteratorSuite) SetupTest() { } func (s *pagingIteratorSuite) TearDownTest() { } func (s *pagingIteratorSuite) TestIteration_NoErr() { phase := 0 outputs := [][]interface{}{ []interface{}{1, 2, 3, 4, 5}, []interface{}{}, []interface{}{6}, []interface{}{}, } tokens := [][]byte{ []byte("some random token 1"), []byte("some random token 2"), []byte("some random token 3"), []byte(nil), } pagingFn := func(token []byte) ([]interface{}, []byte, error) { switch phase { case 0: s.Equal(0, len(token)) defer func() { phase++ }() return outputs[phase], tokens[phase], nil case 1: s.Equal(tokens[0], token) defer func() { phase++ }() return outputs[phase], tokens[phase], nil case 2: s.Equal(tokens[1], token) defer func() { phase++ }() return outputs[phase], tokens[phase], nil case 3: s.Equal(tokens[2], token) defer func() { phase++ }() return outputs[phase], tokens[phase], nil default: panic("should not reach here during test") } } result := []int{} ite := NewPagingIterator(pagingFn) for ite.HasNext() { item, err := ite.Next() s.Nil(err) num, ok := item.(int) s.True(ok) result = append(result, num) } s.Equal([]int{1, 2, 3, 4, 5, 6}, result) } func (s *pagingIteratorSuite) TestIteration_Err_Beginging() { phase := 0 ite := NewPagingIterator(func(token []byte) ([]interface{}, []byte, error) { switch phase { case 0: defer func() { phase++ }() return nil, nil, errors.New("some random error") default: panic("should not reach here during test") } }) s.True(ite.HasNext()) item, err := ite.Next() s.Nil(item) s.NotNil(err) s.False(ite.HasNext()) } func (s *pagingIteratorSuite) TestIteration_Err_NotBegining() { phase := 0 outputs := [][]interface{}{ []interface{}{1, 2, 3, 4, 5}, } tokens := [][]byte{ []byte("some random token 1"), } pagingFn := func(token []byte) ([]interface{}, []byte, error) { switch phase { case 0: s.Equal(0, len(token)) defer func() { phase++ }() return outputs[phase], tokens[phase], nil case 1: s.Equal(tokens[0], token) defer func() { phase++ }() return nil, nil, errors.New("some random error") default: panic("should not reach here during test") } } result := []int{} ite := NewPagingIterator(pagingFn) for ite.HasNext() { item, err := ite.Next() if err != nil { break } num, ok := item.(int) s.True(ok) result = append(result, num) } s.Equal([]int{1, 2, 3, 4, 5}, result) } ================================================ FILE: common/collection/priority_queue.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "container/heap" "errors" ) type ( priorityQueueImpl[T any] struct { compareLess func(this T, other T) bool items []T } ) // NewPriorityQueue create a new priority queue // with the provided list of items. // PriorityQueue will take ownership of the passed in items, // so caller should stop modifying it. // The complexity is O(n) where n is the number of items func NewPriorityQueue[T any]( compareLess func(this T, other T) bool, items ...T, ) Queue[T] { pq := &priorityQueueImpl[T]{ compareLess: compareLess, items: items, } if len(pq.items) > 0 { heap.Init(pq) } return pq } // Peek returns the top item of the priority queue func (pq *priorityQueueImpl[T]) Peek() (T, error) { var item T if pq.IsEmpty() { return item, errors.New("queue is empty") } return pq.items[0], nil } // Add push an item to priority queue func (pq *priorityQueueImpl[T]) Add(item T) { heap.Push(pq, item) } // Remove pop an item from priority queue func (pq *priorityQueueImpl[T]) Remove() (T, error) { var item T if pq.IsEmpty() { return item, errors.New("queue is empty") } return heap.Pop(pq).(T), nil } // IsEmpty indicate if the priority queue is empty func (pq *priorityQueueImpl[T]) IsEmpty() bool { return pq.Len() == 0 } // below are the functions used by heap.Interface and go internal heap implementation // Len implements sort.Interface func (pq *priorityQueueImpl[T]) Len() int { return len(pq.items) } // Less implements sort.Interface func (pq *priorityQueueImpl[T]) Less(i, j int) bool { return pq.compareLess(pq.items[i], pq.items[j]) } // Swap implements sort.Interface func (pq *priorityQueueImpl[T]) Swap(i, j int) { pq.items[i], pq.items[j] = pq.items[j], pq.items[i] } // Push push an item to priority queue, used by go internal heap implementation func (pq *priorityQueueImpl[T]) Push(item interface{}) { pq.items = append(pq.items, item.(T)) } // Pop pop an item from priority queue, used by go internal heap implementation func (pq *priorityQueueImpl[T]) Pop() interface{} { pqItem := pq.items[pq.Len()-1] pq.items = pq.items[0 : pq.Len()-1] return pqItem } ================================================ FILE: common/collection/priority_queue_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "math/rand" "runtime" "sort" "testing" "github.com/stretchr/testify/suite" ) type ( PriorityQueueSuite struct { suite.Suite pq Queue[testPriorityQueueItem] } testPriorityQueueItem struct { value int } ) func testPriorityQueueItemCompareLess(this testPriorityQueueItem, that testPriorityQueueItem) bool { return this.value < that.value } func TestPriorityQueueSuite(t *testing.T) { suite.Run(t, new(PriorityQueueSuite)) } func (s *PriorityQueueSuite) SetupTest() { s.pq = NewPriorityQueue(testPriorityQueueItemCompareLess) } func (s *PriorityQueueSuite) TestInsertAndPop() { s.pq.Add(testPriorityQueueItem{10}) s.pq.Add(testPriorityQueueItem{3}) s.pq.Add(testPriorityQueueItem{5}) s.pq.Add(testPriorityQueueItem{4}) s.pq.Add(testPriorityQueueItem{1}) s.pq.Add(testPriorityQueueItem{16}) s.pq.Add(testPriorityQueueItem{-10}) expected := []int{-10, 1, 3, 4, 5, 10, 16} result := []int{} for !s.pq.IsEmpty() { item, err := s.pq.Remove() s.NoError(err) result = append(result, item.value) } s.Equal(expected, result) s.pq.Add(testPriorityQueueItem{1000}) s.pq.Add(testPriorityQueueItem{1233}) s.pq.Remove() // remove 1000 s.pq.Add(testPriorityQueueItem{4}) s.pq.Add(testPriorityQueueItem{18}) s.pq.Add(testPriorityQueueItem{192}) s.pq.Add(testPriorityQueueItem{255}) s.pq.Remove() // remove 4 s.pq.Remove() // remove 18 s.pq.Add(testPriorityQueueItem{59}) s.pq.Add(testPriorityQueueItem{727}) expected = []int{59, 192, 255, 727, 1233} result = []int{} for !s.pq.IsEmpty() { item, err := s.pq.Remove() s.NoError(err) result = append(result, item.value) } s.Equal(expected, result) } func (s *PriorityQueueSuite) TestRandomNumber() { for round := 0; round < 1000; round++ { expected := []int{} result := []int{} for i := 0; i < 1000; i++ { num := rand.Int() s.pq.Add(testPriorityQueueItem{num}) expected = append(expected, num) } sort.Ints(expected) for !s.pq.IsEmpty() { item, err := s.pq.Remove() s.NoError(err) result = append(result, item.value) } s.Equal(expected, result) } } type testTask struct { id string priority int } func BenchmarkConcurrentPriorityQueue(b *testing.B) { queue := NewConcurrentPriorityQueue(func(this testTask, other testTask) bool { return this.priority < other.priority }) for i := 0; i < 100; i++ { go send(queue) } for n := 0; n < b.N; n++ { remove(queue) } } func remove(queue Queue[testTask]) { for queue.IsEmpty() { runtime.Gosched() } queue.Remove() } func send(queue Queue[testTask]) { for { t := testTask{ id: "abc", priority: rand.Int() % numPriorities, } queue.Add(t) } } ================================================ FILE: common/collection/util.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package collection import ( "encoding/binary" "encoding/hex" ) // UUIDHashCode is a hash function for hashing string uuid // if the uuid is malformed, then the hash function always // returns 0 as the hash value func UUIDHashCode(input interface{}) uint32 { key, ok := input.(string) if !ok { return 0 } if len(key) != UUIDStringLength { return 0 } // Use the first 4 bytes of the uuid as the hash b, err := hex.DecodeString(key[:8]) if err != nil { return 0 } return binary.BigEndian.Uint32(b) } ================================================ FILE: common/config/archival.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "errors" "github.com/uber/cadence/common/constants" ) // Validate validates the archival config func (a *Archival) Validate(domainDefaults *ArchivalDomainDefaults) error { if a.History.Status == constants.ArchivalEnabled { if domainDefaults.History.URI == "" || a.History.Provider == nil { return errors.New("invalid history archival config, must provide domainDefaults.History.URI and Provider") } } else { if a.History.EnableRead { return errors.New("invalid history archival config, cannot EnableRead when archival is disabled") } } if a.Visibility.Status == constants.ArchivalEnabled { if domainDefaults.Visibility.URI == "" || a.Visibility.Provider == nil { return errors.New("invalid visibility archival config, must provide domainDefaults.Visibility.URI and Provider") } } else { if a.Visibility.EnableRead { return errors.New("invalid visibility archival config, cannot EnableRead when archival is disabled") } } return nil } ================================================ FILE: common/config/archival_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "testing" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/constants" ) func defaultFilestoreConfig(t *testing.T) *YamlNode { node, err := ToYamlNode(&FilestoreArchiver{ FileMode: "044", }) require.NoError(t, err) return node } // History archival func TestValidEnabledHistoryArchivalConfig(t *testing.T) { archival := Archival{ History: HistoryArchival{ Status: constants.ArchivalEnabled, Provider: HistoryArchiverProvider{ FilestoreConfig: defaultFilestoreConfig(t), }, }, } err := archival.Validate(&ArchivalDomainDefaults{ History: HistoryArchivalDomainDefaults{ URI: "/var/tmp", }, }) require.NoError(t, err) } func TestInvalidHEnabledHistoryArchivalConfig(t *testing.T) { archival := Archival{ History: HistoryArchival{ Status: constants.ArchivalEnabled, }, } err := archival.Validate(&ArchivalDomainDefaults{}) require.Error(t, err) } func TestValidDisabledHistoryArchivalConfig(t *testing.T) { archival := Archival{ History: HistoryArchival{ Provider: HistoryArchiverProvider{ FilestoreConfig: defaultFilestoreConfig(t), }, }, } err := archival.Validate(&ArchivalDomainDefaults{}) require.NoError(t, err) } func TestInvalidDisabledHistoryArchivalConfig(t *testing.T) { archival := Archival{ History: HistoryArchival{ EnableRead: true, }, } err := archival.Validate(&ArchivalDomainDefaults{}) require.Error(t, err) } func TestValidEmptyHistoryArchivalConfig(t *testing.T) { archival := Archival{ History: HistoryArchival{}, } err := archival.Validate(&ArchivalDomainDefaults{}) require.NoError(t, err) } // Visibility archival func TestValidEnabledVisibilityArchivalConfig(t *testing.T) { archival := Archival{ Visibility: VisibilityArchival{ Status: constants.ArchivalEnabled, Provider: VisibilityArchiverProvider{ FilestoreConfig: defaultFilestoreConfig(t), }, }, } err := archival.Validate(&ArchivalDomainDefaults{ Visibility: VisibilityArchivalDomainDefaults{ URI: "/var/tmp", }, }) require.NoError(t, err) } func TestInvalidHEnabledVisibilityArchivalConfig(t *testing.T) { archival := Archival{ Visibility: VisibilityArchival{ Status: constants.ArchivalEnabled, }, } err := archival.Validate(&ArchivalDomainDefaults{}) require.Error(t, err) } func TestValidDisabledVisibilityArchivalConfig(t *testing.T) { archival := Archival{ Visibility: VisibilityArchival{ Provider: VisibilityArchiverProvider{ FilestoreConfig: defaultFilestoreConfig(t), }, }, } err := archival.Validate(&ArchivalDomainDefaults{}) require.NoError(t, err) } func TestInvalidDisabledVisibilityArchivalConfig(t *testing.T) { archival := Archival{ Visibility: VisibilityArchival{ EnableRead: true, }, } err := archival.Validate(&ArchivalDomainDefaults{}) require.Error(t, err) } func TestValidEmptyVisibilityArchivalConfig(t *testing.T) { archival := Archival{ Visibility: VisibilityArchival{}, } err := archival.Validate(&ArchivalDomainDefaults{}) require.NoError(t, err) } ================================================ FILE: common/config/authorization.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "errors" "fmt" "github.com/golang-jwt/jwt/v5" ) type ( Authorization struct { OAuthAuthorizer OAuthAuthorizer `yaml:"oauthAuthorizer"` NoopAuthorizer NoopAuthorizer `yaml:"noopAuthorizer"` } NoopAuthorizer struct { Enable bool `yaml:"enable"` } OAuthAuthorizer struct { Enable bool `yaml:"enable"` // Max of TTL in the claim MaxJwtTTL int64 `yaml:"maxJwtTTL"` // Credentials to verify/create the JWT using public/private keys JwtCredentials *JwtCredentials `yaml:"jwtCredentials"` // Provider Provider *OAuthProvider `yaml:"provider"` } JwtCredentials struct { // support: RS256 (RSA using SHA256) Algorithm string `yaml:"algorithm"` // Public Key Path for verifying JWT token passed in from external clients PublicKey string `yaml:"publicKey"` } // OAuthProvider is used to validate tokens provided by 3rd party Identity Provider service OAuthProvider struct { JWKSURL string `yaml:"jwksURL"` GroupsAttributePath string `yaml:"groupsAttributePath"` AdminAttributePath string `yaml:"adminAttributePath"` } ) // Validate validates the persistence config func (a *Authorization) Validate() error { if a.OAuthAuthorizer.Enable && a.NoopAuthorizer.Enable { return fmt.Errorf("[AuthorizationConfig] More than one authorizer is enabled") } if a.OAuthAuthorizer.Enable { if err := a.validateOAuth(); err != nil { return err } } return nil } func (a *Authorization) validateOAuth() error { oauthConfig := a.OAuthAuthorizer if oauthConfig.MaxJwtTTL <= 0 { return fmt.Errorf("[OAuthConfig] MaxTTL must be greater than 0") } if oauthConfig.JwtCredentials == nil && oauthConfig.Provider == nil { return errors.New("jwtCredentials or provider must be provided") } if oauthConfig.JwtCredentials != nil { if oauthConfig.JwtCredentials.PublicKey == "" { return fmt.Errorf("[OAuthConfig] PublicKey can't be empty") } if oauthConfig.JwtCredentials.Algorithm != jwt.SigningMethodRS256.Name { return fmt.Errorf("[OAuthConfig] The only supported Algorithm is RS256") } } if oauthConfig.Provider != nil { if oauthConfig.Provider.JWKSURL == "" { return fmt.Errorf("[OAuthConfig] JWKSURL is not set") } } return nil } ================================================ FILE: common/config/authorization_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "testing" "github.com/stretchr/testify/assert" ) func TestMultipleAuthEnabled(t *testing.T) { cfg := Authorization{ OAuthAuthorizer: OAuthAuthorizer{ Enable: true, }, NoopAuthorizer: NoopAuthorizer{ Enable: true, }, } err := cfg.Validate() assert.EqualError(t, err, "[AuthorizationConfig] More than one authorizer is enabled") } func TestTTLIsZero(t *testing.T) { cfg := Authorization{ OAuthAuthorizer: OAuthAuthorizer{ Enable: true, JwtCredentials: &JwtCredentials{}, MaxJwtTTL: 0, }, NoopAuthorizer: NoopAuthorizer{ Enable: false, }, } err := cfg.Validate() assert.EqualError(t, err, "[OAuthConfig] MaxTTL must be greater than 0") } func TestPublicKeyIsEmpty(t *testing.T) { cfg := Authorization{ OAuthAuthorizer: OAuthAuthorizer{ Enable: true, JwtCredentials: &JwtCredentials{ Algorithm: "", PublicKey: "", }, MaxJwtTTL: 1000000, }, NoopAuthorizer: NoopAuthorizer{ Enable: false, }, } err := cfg.Validate() assert.EqualError(t, err, "[OAuthConfig] PublicKey can't be empty") } func TestAlgorithmIsInvalid(t *testing.T) { cfg := Authorization{ OAuthAuthorizer: OAuthAuthorizer{ Enable: true, JwtCredentials: &JwtCredentials{ Algorithm: "SHA256", PublicKey: "public", }, MaxJwtTTL: 1000000, }, NoopAuthorizer: NoopAuthorizer{ Enable: false, }, } err := cfg.Validate() assert.EqualError(t, err, "[OAuthConfig] The only supported Algorithm is RS256") } func TestCorrectValidation(t *testing.T) { cfg := Authorization{ OAuthAuthorizer: OAuthAuthorizer{ Enable: true, JwtCredentials: &JwtCredentials{ Algorithm: "RS256", PublicKey: "public", }, MaxJwtTTL: 1000000, }, NoopAuthorizer: NoopAuthorizer{ Enable: false, }, } err := cfg.Validate() assert.NoError(t, err) } ================================================ FILE: common/config/cluster.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "errors" "fmt" "log" "go.uber.org/multierr" "go.uber.org/yarpc/transport/grpc" "go.uber.org/yarpc/transport/tchannel" ) type ( // ClusterGroupMetadata contains all the clusters participating in a replication group(aka XDC/GlobalDomain) ClusterGroupMetadata struct { // FailoverVersionIncrement is the increment of each cluster version when failover happens // It decides the maximum number clusters in this replication groups FailoverVersionIncrement int64 `yaml:"failoverVersionIncrement"` // PrimaryClusterName is the primary cluster name, only the primary cluster can register / update domain // all clusters can do domain failover PrimaryClusterName string `yaml:"primaryClusterName"` // Deprecated: please use PrimaryClusterName MasterClusterName string `yaml:"masterClusterName"` // CurrentClusterName is the name of the cluster of current deployment CurrentClusterName string `yaml:"currentClusterName"` // ClusterRedirectionPolicy contains the cluster redirection policy for global domains ClusterRedirectionPolicy *ClusterRedirectionPolicy `yaml:"clusterRedirectionPolicy"` // ClusterGroup contains information for each cluster within the replication group // Key is the clusterName ClusterGroup map[string]ClusterInformation `yaml:"clusterGroup"` // Deprecated: please use ClusterGroup ClusterInformation map[string]ClusterInformation `yaml:"clusterInformation"` } // ClusterInformation contains the information about each cluster participating in cross DC ClusterInformation struct { Enabled bool `yaml:"enabled"` // InitialFailoverVersion is the identifier of each cluster. 0 <= the value < failoverVersionIncrement InitialFailoverVersion int64 `yaml:"initialFailoverVersion"` // NewInitialFailoverVersion is a new failover version for an initialFailoverVersion migration // for when it's necessary to migrate between two values. // // this is a pointer to imply optionality, it's an optional field and its lack // is indicated by a nil pointer. Zero is a valid field NewInitialFailoverVersion *int64 `yaml:"newInitialFailoverVersion"` // RPCName indicate the remote service name RPCName string `yaml:"rpcName"` // Address indicate the remote service address(Host:Port). Host can be DNS name. // For currentCluster, it's usually the same as publicClient.hostPort RPCAddress string `yaml:"rpcAddress" validate:"nonzero"` // RPCTransport specifies transport to use for replication traffic. // Allowed values: tchannel|grpc // Default: tchannel RPCTransport string `yaml:"rpcTransport"` // AuthorizationProvider contains the information to authorize the cluster AuthorizationProvider AuthorizationProvider `yaml:"authorizationProvider"` // TLS configures client TLS/SSL authentication for connections to this cluster TLS TLS `yaml:"tls"` } AuthorizationProvider struct { // Enable indicates if the auth provider is enabled Enable bool `yaml:"enable"` // Type auth provider type Type string `yaml:"type"` // only supports OAuthAuthorization // PrivateKey is the private key path PrivateKey string `yaml:"privateKey"` } ) // Validate validates ClusterGroupMetadata func (m *ClusterGroupMetadata) Validate() error { if m == nil { return errors.New("ClusterGroupMetadata cannot be empty") } var errs error if len(m.PrimaryClusterName) == 0 { errs = multierr.Append(errs, errors.New("primary cluster name is empty")) } if len(m.CurrentClusterName) == 0 { errs = multierr.Append(errs, errors.New("current cluster name is empty")) } if m.FailoverVersionIncrement == 0 { errs = multierr.Append(errs, errors.New("version increment is 0")) } if len(m.ClusterGroup) == 0 { errs = multierr.Append(errs, errors.New("empty cluster group")) } if _, ok := m.ClusterGroup[m.PrimaryClusterName]; len(m.PrimaryClusterName) > 0 && !ok { errs = multierr.Append(errs, errors.New("primary cluster is not specified in the cluster group")) } if _, ok := m.ClusterGroup[m.CurrentClusterName]; len(m.CurrentClusterName) > 0 && !ok { errs = multierr.Append(errs, errors.New("current cluster is not specified in the cluster group")) } versionToClusterName := make(map[int64]string) for clusterName, info := range m.ClusterGroup { if len(clusterName) == 0 { errs = multierr.Append(errs, errors.New("cluster with empty name defined")) } versionToClusterName[info.InitialFailoverVersion] = clusterName if m.FailoverVersionIncrement <= info.InitialFailoverVersion || info.InitialFailoverVersion < 0 { errs = multierr.Append(errs, fmt.Errorf( "cluster %s: version increment %v is smaller than initial version: %v", clusterName, m.FailoverVersionIncrement, info.InitialFailoverVersion, )) } if info.Enabled && (len(info.RPCName) == 0 || len(info.RPCAddress) == 0) { errs = multierr.Append(errs, fmt.Errorf("cluster %v: rpc name / address is empty", clusterName)) } if info.RPCTransport != tchannel.TransportName && info.RPCTransport != grpc.TransportName { errs = multierr.Append(errs, fmt.Errorf("cluster %v: rpc transport must %v or %v", clusterName, tchannel.TransportName, grpc.TransportName)) } } if len(versionToClusterName) != len(m.ClusterGroup) { errs = multierr.Append(errs, errors.New("initial versions of the cluster group have duplicates")) } return errs } // FillDefaults populates default values for unspecified fields func (m *ClusterGroupMetadata) FillDefaults() { if m == nil { log.Fatal("ClusterGroupMetadata cannot be empty") } // TODO: remove this after 0.23 and mention a breaking change in config. if len(m.PrimaryClusterName) == 0 && len(m.MasterClusterName) != 0 { m.PrimaryClusterName = m.MasterClusterName log.Println("[WARN] masterClusterName config is deprecated. Please replace it with primaryClusterName.") } // TODO: remove this after 0.23 and mention a breaking change in config. if len(m.ClusterGroup) == 0 && len(m.ClusterInformation) != 0 { m.ClusterGroup = m.ClusterInformation log.Println("[WARN] clusterInformation config is deprecated. Please replace it with clusterGroup.") } for name, cluster := range m.ClusterGroup { if cluster.RPCName == "" { // filling RPCName with a default value if empty cluster.RPCName = "cadence-frontend" } if cluster.RPCTransport == "" { cluster.RPCTransport = tchannel.TransportName } m.ClusterGroup[name] = cluster } } ================================================ FILE: common/config/cluster_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "testing" "github.com/stretchr/testify/assert" ) func TestClusterGroupMetadataDefaults(t *testing.T) { config := ClusterGroupMetadata{ MasterClusterName: "active", ClusterInformation: map[string]ClusterInformation{ "active": {}, }, } config.FillDefaults() assert.Equal(t, "active", config.PrimaryClusterName) assert.Equal(t, "cadence-frontend", config.ClusterGroup["active"].RPCName) assert.Equal(t, "tchannel", config.ClusterGroup["active"].RPCTransport) } func TestClusterGroupMetadataValidate(t *testing.T) { modify := func(initial *ClusterGroupMetadata, modify func(metadata *ClusterGroupMetadata)) *ClusterGroupMetadata { modify(initial) return initial } tests := []struct { msg string config *ClusterGroupMetadata err string }{ { msg: "valid", config: validClusterGroupMetadata(), }, { msg: "empty", config: nil, err: "ClusterGroupMetadata cannot be empty", }, { msg: "primary cluster name is empty", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { m.PrimaryClusterName = "" }), err: "primary cluster name is empty", }, { msg: "current cluster name is empty", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { m.CurrentClusterName = "" }), err: "current cluster name is empty", }, { msg: "primary cluster is not specified in the cluster group", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { m.PrimaryClusterName = "non-existing" }), err: "primary cluster is not specified in the cluster group", }, { msg: "current cluster is not specified in the cluster group", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { m.CurrentClusterName = "non-existing" }), err: "current cluster is not specified in the cluster group", }, { msg: "version increment is 0", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { m.FailoverVersionIncrement = 0 }), err: "version increment is 0", }, { msg: "empty cluster group", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { m.ClusterGroup = nil }), err: "empty cluster group", }, { msg: "cluster with empty name defined", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { m.ClusterGroup[""] = ClusterInformation{} }), err: "cluster with empty name defined", }, { msg: "increment smaller than initial version", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { m.FailoverVersionIncrement = 1 }), err: "cluster standby: version increment 1 is smaller than initial version: 2", }, { msg: "empty rpc name", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { active := m.ClusterGroup["active"] active.RPCName = "" m.ClusterGroup["active"] = active }), err: "cluster active: rpc name / address is empty", }, { msg: "empty rpc address", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { active := m.ClusterGroup["active"] active.RPCAddress = "" m.ClusterGroup["active"] = active }), err: "cluster active: rpc name / address is empty", }, { msg: "invalid rpc transport", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { active := m.ClusterGroup["active"] active.RPCTransport = "invalid" m.ClusterGroup["active"] = active }), err: "cluster active: rpc transport must tchannel or grpc", }, { msg: "initial version duplicated", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { standby := m.ClusterGroup["standby"] standby.InitialFailoverVersion = 0 m.ClusterGroup["standby"] = standby }), err: "initial versions of the cluster group have duplicates", }, { msg: "multiple errors", config: modify(validClusterGroupMetadata(), func(m *ClusterGroupMetadata) { *m = ClusterGroupMetadata{} }), err: "primary cluster name is empty; current cluster name is empty; version increment is 0; empty cluster group", }, } for _, tt := range tests { t.Run(tt.msg, func(t *testing.T) { err := tt.config.Validate() if tt.err == "" { assert.NoError(t, err) } else { assert.Contains(t, err.Error(), tt.err) } }) } } func validClusterGroupMetadata() *ClusterGroupMetadata { return &ClusterGroupMetadata{ FailoverVersionIncrement: 10, PrimaryClusterName: "active", CurrentClusterName: "standby", ClusterGroup: map[string]ClusterInformation{ "active": { Enabled: true, InitialFailoverVersion: 0, RPCName: "cadence-frontend", RPCAddress: "localhost:7833", RPCTransport: "grpc", }, "standby": { Enabled: true, InitialFailoverVersion: 2, RPCName: "cadence-frontend", RPCAddress: "localhost:7833", RPCTransport: "grpc", }, }, } } ================================================ FILE: common/config/config.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "encoding/json" "fmt" "log" "regexp" "time" "github.com/uber-go/tally/m3" "github.com/uber-go/tally/prometheus" yarpctls "go.uber.org/yarpc/api/transport/tls" "gopkg.in/yaml.v2" // CAUTION: go.uber.org/config does not support yaml.v3 "github.com/uber/cadence/common/dynamicconfig" c "github.com/uber/cadence/common/dynamicconfig/configstore/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" ringpopprovider "github.com/uber/cadence/common/peerprovider/ringpopprovider/config" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" sdconfig "github.com/uber/cadence/service/sharddistributor/config" ) type ( // Config contains the configuration for a set of cadence services Config struct { // Ringpop is the ringpop related configuration Ringpop ringpopprovider.Config `yaml:"ringpop"` // Membership is used to configure peer provider plugin Membership Membership `yaml:"membership"` // Persistence contains the configuration for cadence datastores Persistence Persistence `yaml:"persistence"` // Log is the logging config Log Logger `yaml:"log"` // ClusterGroupMetadata is the config containing all valid clusters and active cluster ClusterGroupMetadata *ClusterGroupMetadata `yaml:"clusterGroupMetadata"` // Deprecated: please use ClusterGroupMetadata ClusterMetadata *ClusterGroupMetadata `yaml:"clusterMetadata"` // Deprecated: please use ClusterRedirectionPolicy under ClusterGroupMetadata DCRedirectionPolicy *ClusterRedirectionPolicy `yaml:"dcRedirectionPolicy"` // Services is a map of service name to service config items Services map[string]Service `yaml:"services"` // Kafka is the config for connecting to kafka Kafka KafkaConfig `yaml:"kafka"` // Archival is the config for archival Archival Archival `yaml:"archival"` // PublicClient is config for sys worker service connecting to cadence frontend PublicClient PublicClient `yaml:"publicClient"` // DynamicConfigClient is the config for setting up the file based dynamic config client // Filepath would be relative to the root directory when the path wasn't absolute. // Included for backwards compatibility, please transition to DynamicConfig // If both are specified, DynamicConig will be used. DynamicConfigClient dynamicconfig.FileBasedClientConfig `yaml:"dynamicConfigClient"` // DynamicConfig is the config for setting up all dynamic config clients // Allows for changes in client without needing code change DynamicConfig DynamicConfig `yaml:"dynamicconfig"` // DomainDefaults is the default config for every domain DomainDefaults DomainDefaults `yaml:"domainDefaults"` // Blobstore is the config for setting up blobstore Blobstore Blobstore `yaml:"blobstore"` // Authorization is the config for setting up authorization Authorization Authorization `yaml:"authorization"` // HeaderForwardingRules defines which inbound headers to include or exclude on outbound calls HeaderForwardingRules []HeaderRule `yaml:"headerForwardingRules"` // Note: This is not implemented yet. It's coming in the next release. // AsyncWorkflowQueues is the config for predefining async workflow queue(s) // To use Async APIs for a domain first specify the queue using Admin API. // Either refer to one of the predefined queues in this config or alternatively specify the queue details inline in the API call. AsyncWorkflowQueues map[string]AsyncWorkflowQueueProvider `yaml:"asyncWorkflowQueues"` // ShardDistributorClient is the config for shard distributor client // Shard distributor is used to distribute shards across multiple cadence service instances // Note: This is not recommended for use, it's still experimental ShardDistributorClient ShardDistributorClient `yaml:"shardDistributorClient"` // ShardDistribution is a config for the shard distributor leader election component that allows to run a single process per region and manage shard namespaces. ShardDistribution sdconfig.ShardDistribution `yaml:"shardDistribution"` // ShardDistributorMatchingConfig is the config for shard distributor executor client in matching service ShardDistributorMatchingConfig clientcommon.Config `yaml:"shard-distributor-matching"` // Histograms controls timer vs histogram metric emission while they are being migrated. // // Timers will eventually be dropped, and this config will be validation-only (e.g. to error if any explicitly request timers). Histograms metrics.HistogramMigration `yaml:"histograms"` } // Membership holds peer provider configuration. Membership struct { Provider PeerProvider `yaml:"provider"` } // PeerProvider is provider config. Contents depends on plugin in use PeerProvider map[string]*YamlNode HeaderRule struct { Add bool // if false, matching headers are removed if previously matched. Match *regexp.Regexp } DynamicConfig struct { Client string `yaml:"client"` ConfigStore c.ClientConfig `yaml:"configstore"` FileBased dynamicconfig.FileBasedClientConfig `yaml:"filebased"` } // Service contains the service specific config items Service struct { // TChannel is the tchannel configuration RPC RPC `yaml:"rpc"` // Metrics is the metrics subsystem configuration Metrics Metrics `yaml:"metrics"` // PProf is the PProf configuration PProf PProf `yaml:"pprof"` } // PProf contains the rpc config items PProf struct { // Port is the port on which the PProf will bind to Port int `yaml:"port"` // Host is the host on which the PProf will bind to, default to `localhost` Host string `yaml:"host"` } // RPC contains the rpc config items RPC struct { // Port is the port on which the Thrift TChannel will bind to Port uint16 `yaml:"port"` // GRPCPort is the port on which the grpc listener will bind to GRPCPort uint16 `yaml:"grpcPort"` // BindOnLocalHost is true if localhost is the bind address BindOnLocalHost bool `yaml:"bindOnLocalHost"` // BindOnIP can be used to bind service on specific ip (eg. `0.0.0.0`) - // check net.ParseIP for supported syntax, only IPv4 is supported, // mutually exclusive with `BindOnLocalHost` option BindOnIP string `yaml:"bindOnIP"` // DisableLogging disables all logging for rpc DisableLogging bool `yaml:"disableLogging"` // LogLevel is the desired log level LogLevel string `yaml:"logLevel"` // GRPCMaxMsgSize allows overriding default (4MB) message size for gRPC GRPCMaxMsgSize int `yaml:"grpcMaxMsgSize"` // TLS allows configuring optional TLS/SSL authentication on the server (only on gRPC port) TLS TLS `yaml:"tls"` // HTTP keeps configuration for exposed HTTP API HTTP *HTTP `yaml:"http"` } // HTTP API configuration HTTP struct { // Port for listening HTTP requests Port uint16 `yaml:"port"` // List of RPC procedures available to call using HTTP Procedures []string `yaml:"procedures"` // TLS allows configuring TLS/SSL for HTTP requests TLS TLS `yaml:"tls"` // Mode represents the TLS mode of the transport. // Available modes: disabled, permissive, enforced TLSMode yarpctls.Mode `yaml:"TLSMode"` } // Blobstore contains the config for blobstore Blobstore struct { Filestore *FileBlobstore `yaml:"filestore"` } // FileBlobstore contains the config for a file backed blobstore FileBlobstore struct { OutputDirectory string `yaml:"outputDirectory"` } // Persistence contains the configuration for data store / persistence layer Persistence struct { // DefaultStore is the name of the default data store to use DefaultStore string `yaml:"defaultStore" validate:"nonzero"` // VisibilityStore is the name of the datastore to be used for visibility records // Must provide one of VisibilityStore and AdvancedVisibilityStore VisibilityStore string `yaml:"visibilityStore"` // AdvancedVisibilityStore is the name of the datastore to be used for visibility records // Must provide one of VisibilityStore and AdvancedVisibilityStore AdvancedVisibilityStore string `yaml:"advancedVisibilityStore"` // HistoryMaxConns is the desired number of conns to history store. Value specified // here overrides the MaxConns config specified as part of datastore // Deprecated: This value is not used HistoryMaxConns int `yaml:"historyMaxConns"` // EnablePersistenceLatencyHistogramMetrics is to enable latency histogram metrics for persistence layer EnablePersistenceLatencyHistogramMetrics bool `yaml:"enablePersistenceLatencyHistogramMetrics"` // NumHistoryShards is the desired number of history shards. It's for computing the historyShardID from workflowID into [0, NumHistoryShards) // Therefore, the value cannot be changed once set. // TODO This config doesn't belong here, needs refactoring NumHistoryShards int `yaml:"numHistoryShards" validate:"nonzero"` // DataStores contains the configuration for all datastores DataStores map[string]DataStore `yaml:"datastores"` // TODO: move dynamic config out of static config // TransactionSizeLimit is the largest allowed transaction size TransactionSizeLimit dynamicproperties.IntPropertyFn `yaml:"-" json:"-"` // TODO: move dynamic config out of static config // ErrorInjectionRate is the the rate for injecting random error ErrorInjectionRate dynamicproperties.FloatPropertyFn `yaml:"-" json:"-"` } // DataStore is the configuration for a single datastore DataStore struct { // Cassandra contains the config for a cassandra datastore // Deprecated: please use NoSQL instead, the structure is backward-compatible Cassandra *Cassandra `yaml:"cassandra"` // SQL contains the config for a SQL based datastore SQL *SQL `yaml:"sql"` // NoSQL contains the config for a NoSQL based datastore NoSQL *NoSQL `yaml:"nosql"` // ShardedNoSQL contains the config for a collection of NoSQL datastores that are used as a single datastore ShardedNoSQL *ShardedNoSQL `yaml:"shardedNosql"` // ElasticSearch contains the config for a ElasticSearch datastore ElasticSearch *ElasticSearchConfig `yaml:"elasticsearch"` // Pinot contains the config for a Pinot datastore Pinot *PinotVisibilityConfig `yaml:"pinot"` } // Cassandra contains configuration to connect to Cassandra cluster // Deprecated: please use NoSQL instead, the structure is backward-compatible Cassandra = NoSQL // NoSQL contains configuration to connect to NoSQL Database cluster NoSQL struct { // PluginName is the name of NoSQL plugin, default is "cassandra". Supported values: cassandra PluginName string `yaml:"pluginName"` // Hosts is a csv of cassandra endpoints Hosts string `yaml:"hosts" validate:"nonzero"` // Port is the cassandra port used for connection by gocql client Port int `yaml:"port"` // User is the cassandra user used for authentication by gocql client User string `yaml:"user"` // Password is the cassandra password used for authentication by gocql client Password string `yaml:"password"` // AllowedAuthenticators informs the cassandra client to expect a custom authenticator AllowedAuthenticators []string `yaml:"allowedAuthenticators"` // Keyspace is the cassandra keyspace Keyspace string `yaml:"keyspace"` // Region is the region filter arg for cassandra Region string `yaml:"region"` // Datacenter is the data center filter arg for cassandra Datacenter string `yaml:"datacenter"` // MaxConns is the max number of connections to this datastore for a single keyspace MaxConns int `yaml:"maxConns"` // TLS configuration TLS *TLS `yaml:"tls"` // ProtoVersion ProtoVersion int `yaml:"protoVersion"` // ConnectTimeout defines duration for initial dial ConnectTimeout time.Duration `yaml:"connectTimeout"` // Timout is a connection timeout Timeout time.Duration `yaml:"timeout"` // Consistency defines default consistency level Consistency string `yaml:"consistency"` // SerialConsistency sets the consistency for the serial part of queries SerialConsistency string `yaml:"serialConsistency"` // ConnectAttributes is a set of key-value attributes as a supplement/extension to the above common fields // Use it ONLY when a configure is too specific to a particular NoSQL database that should not be in the common struct // Otherwise please add new fields to the struct for better documentation // If being used in any database, update this comment here to make it clear ConnectAttributes map[string]string `yaml:"connectAttributes"` // HostSelectionPolicy sets gocql policy for selecting host for a query // Available selections are: "tokenaware,roundrobin", "hostpool-epsilon-greedy", "roundrobin" HostSelectionPolicy string `yaml:"hostSelectionPolicy"` } // ShardedNoSQL contains configuration to connect to a set of NoSQL Database clusters in a sharded manner ShardedNoSQL struct { // DefaultShard is the DB shard where the non-sharded tables (ie. cluster metadata) are stored DefaultShard string `yaml:"defaultShard"` // ShardingPolicy is the configuration for the sharding strategy used ShardingPolicy ShardingPolicy `yaml:"shardingPolicy"` // Connections is the collection of NoSQL DB plugins that are used to connect to the shard Connections map[string]DBShardConnection `yaml:"connections"` } // ShardingPolicy contains configuration for physical DB sharding ShardingPolicy struct { // HistoryShardMapping defines the ranges of history shards stored by each DB shard. Ranges listed here *MUST* // be continuous and non-overlapping, such that the first range in the list starts from Shard 0, each following // range starts with + 1, and the last range ends with -1. HistoryShardMapping []HistoryShardRange `yaml:"historyShardMapping"` // TaskListHashing defines the parameters needed for shard ownership calculation based on hashing TaskListHashing TasklistHashing `yaml:"taskListHashing"` } // HistoryShardRange contains configuration for one NoSQL DB Shard HistoryShardRange struct { // Start defines the inclusive lower bound for the history shard range Start int `yaml:"start"` // End defines the exclusive upper bound for the history shard range End int `yaml:"end"` // Shard defines the shard that owns this range Shard string `yaml:"shard"` } TasklistHashing struct { // ShardOrder defines the order of shards to be used when hashing tasklists to shards ShardOrder []string `yaml:"shardOrder"` } // DBShardConnection contains configuration for one NoSQL DB Shard DBShardConnection struct { // NoSQLPlugin is the NoSQL plugin used for connecting to the DB shard NoSQLPlugin *NoSQL `yaml:"nosqlPlugin"` } // SQL is the configuration for connecting to a SQL backed datastore SQL struct { // User is the username to be used for the conn // If useMultipleDatabases, must be empty and provide it via multipleDatabasesConfig instead User string `yaml:"user"` // Password is the password corresponding to the user name // If useMultipleDatabases, must be empty and provide it via multipleDatabasesConfig instead Password string `yaml:"password"` // PluginName is the name of SQL plugin PluginName string `yaml:"pluginName" validate:"nonzero"` // DatabaseName is the name of SQL database to connect to // If useMultipleDatabases, must be empty and provide it via multipleDatabasesConfig instead // Required if not useMultipleDatabases DatabaseName string `yaml:"databaseName"` // ConnectAddr is the remote addr of the database // If useMultipleDatabases, must be empty and provide it via multipleDatabasesConfig instead // Required if not useMultipleDatabases ConnectAddr string `yaml:"connectAddr"` // ConnectProtocol is the protocol that goes with the ConnectAddr ex - tcp, unix ConnectProtocol string `yaml:"connectProtocol"` // ConnectAttributes is a set of key-value attributes to be sent as part of connect data_source_name url ConnectAttributes map[string]string `yaml:"connectAttributes"` // MaxConns the max number of connections to this datastore MaxConns int `yaml:"maxConns"` // MaxIdleConns is the max number of idle connections to this datastore MaxIdleConns int `yaml:"maxIdleConns"` // MaxConnLifetime is the maximum time a connection can be alive MaxConnLifetime time.Duration `yaml:"maxConnLifetime"` // NumShards is the number of DB shards in a sharded sql database. Default is 1 for single SQL database setup. // It's for computing a shardID value of [0,NumShards) to decide which shard of DB to query. // Relationship with NumHistoryShards, both values cannot be changed once set in the same cluster, // and the historyShardID value calculated from NumHistoryShards will be calculated using this NumShards to get a dbShardID NumShards int `yaml:"nShards"` // TLS is the configuration for TLS connections TLS *TLS `yaml:"tls"` // EncodingType is the configuration for the type of encoding used for sql blobs EncodingType string `yaml:"encodingType"` // DecodingTypes is the configuration for all the sql blob decoding types which need to be supported // DecodingTypes should not be removed unless there are no blobs in database with the encoding type DecodingTypes []string `yaml:"decodingTypes"` // UseMultipleDatabases enables using multiple databases as a sharding SQL database, default is false // When enabled, connection will be established using MultipleDatabasesConfig in favor of single values // of User, Password, DatabaseName, ConnectAddr. UseMultipleDatabases bool `yaml:"useMultipleDatabases"` // Required when UseMultipleDatabases is true // the length of the list should be exactly the same as NumShards MultipleDatabasesConfig []MultipleDatabasesConfigEntry `yaml:"multipleDatabasesConfig"` } // MultipleDatabasesConfigEntry is an entry for MultipleDatabasesConfig to connect to a single SQL database MultipleDatabasesConfigEntry struct { // User is the username to be used for the conn User string `yaml:"user"` // Password is the password corresponding to the user name Password string `yaml:"password"` // DatabaseName is the name of SQL database to connect to DatabaseName string `yaml:"databaseName" validate:"nonzero"` // ConnectAddr is the remote addr of the database ConnectAddr string `yaml:"connectAddr" validate:"nonzero"` } // CustomDatastoreConfig is the configuration for connecting to a custom datastore that is not supported by cadence core // TODO can we remove it? CustomDatastoreConfig struct { // Name of the custom datastore Name string `yaml:"name"` // Options is a set of key-value attributes that can be used by AbstractDatastoreFactory implementation Options map[string]string `yaml:"options"` } // Replicator describes the configuration of replicator // TODO can we remove it? Replicator struct{} // Logger contains the config items for logger Logger struct { // Stdout is true then the output needs to goto standard out // By default this is false and output will go to standard error Stdout bool `yaml:"stdout"` // Level is the desired log level Level string `yaml:"level"` // OutputFile is the path to the log output file // Stdout must be false, otherwise Stdout will take precedence OutputFile string `yaml:"outputFile"` // LevelKey is the desired log level, defaults to "level" LevelKey string `yaml:"levelKey"` // Encoding decides the format, supports "console" and "json". // "json" will print the log in JSON format(better for machine), while "console" will print in plain-text format(more human friendly) // Default is "json" Encoding string `yaml:"encoding"` } // ClusterRedirectionPolicy contains the frontend datacenter redirection policy // When using XDC (global domain) feature to failover a domain from one cluster to another one, client may call the passive cluster to start /signal workflows etc. // To have a seamless failover experience, cluster should use this forwarding option to forward those APIs to the active cluster. ClusterRedirectionPolicy struct { // Support "noop", "selected-apis-forwarding" and "all-domain-apis-forwarding", default (when empty) is "noop" // // 1) "noop" will not do any forwarding. // // 2) "all-domain-apis-forwarding" will forward all domain specific APIs(worker and non worker) if the current active domain is // the same as "allDomainApisForwardingTargetCluster"( or "allDomainApisForwardingTargetCluster" is empty), otherwise it fallbacks to "selected-apis-forwarding". // // 3) "selected-apis-forwarding" will forward all non-worker APIs including // 1. StartWorkflowExecution // 2. SignalWithStartWorkflowExecution // 3. SignalWorkflowExecution // 4. RequestCancelWorkflowExecution // 5. TerminateWorkflowExecution // 6. QueryWorkflow // 7. ResetWorkflow // // 4) "selected-apis-forwarding-v2" will forward all of "selected-apis-forwarding", and also activity responses // and heartbeats, but not other worker APIs. // // "selected-apis-forwarding(-v2)" and "all-domain-apis-forwarding" can work with EnableDomainNotActiveAutoForwarding dynamicconfig to select certain domains using the policy. // // Usage recommendation: when enabling XDC(global domain) feature, either "all-domain-apis-forwarding" or "selected-apis-forwarding(-v2)" should be used to ensure seamless domain failover(high availability) // Depending on the cost of cross cluster calls: // // 1) If the network communication overhead is high(e.g., clusters are in remote datacenters of different region), then should use "selected-apis-forwarding(-v2)". // But you must ensure a different set of workers with the same workflow & activity code are connected to each Cadence cluster. // // 2) If the network communication overhead is low (e.g. in the same datacenter, mostly for cluster migration usage), then you can use "all-domain-apis-forwarding". Then only one set of // workflow & activity worker connected of one of the Cadence cluster is enough as all domain APIs are forwarded. See more details in documentation of cluster migration section. // Usually "allDomainApisForwardingTargetCluster" should be empty(default value) except for very rare cases: you have more than two clusters and some are in a remote region but some are in local region. Policy string `yaml:"policy"` // A supplement for "all-domain-apis-forwarding" policy. It decides how the policy fallback to "selected-apis-forwarding" policy. // If this is not empty, and current domain is not active in the value of allDomainApisForwardingTargetCluster, then the policy will fallback to "selected-apis-forwarding" policy. // Default is empty, meaning that all requests will not fallback. AllDomainApisForwardingTargetCluster string `yaml:"allDomainApisForwardingTargetCluster"` // Not being used, but we have to keep it so that config loading is not broken ToDC string `yaml:"toDC"` } // Metrics contains the config items for metrics subsystem Metrics struct { // M3 is the configuration for m3 metrics reporter M3 *m3.Configuration `yaml:"m3"` // Statsd is the configuration for statsd reporter Statsd *Statsd `yaml:"statsd"` // Prometheus is the configuration for prometheus reporter // Some documentation below because the tally library is missing it: // In this configuration, default timerType is "histogram", alternatively "summary" is also supported. // In some cases, summary is better. Choose it wisely. // For histogram, default buckets are defined in https://github.com/uber/cadence/blob/master/common/metrics/tally/prometheus/buckets.go#L34 // For summary, default objectives are defined in https://github.com/uber-go/tally/blob/137973e539cd3589f904c23d0b3a28c579fd0ae4/prometheus/reporter.go#L70 // You can customize the buckets/objectives if the default is not good enough. Prometheus *prometheus.Configuration `yaml:"prometheus"` // Tags is the set of key-value pairs to be reported // as part of every metric Tags map[string]string `yaml:"tags"` // Prefix sets the prefix to all outgoing metrics Prefix string `yaml:"prefix"` // ReportingInterval is the interval of metrics reporter ReportingInterval time.Duration `yaml:"reportingInterval"` // defaults to 1s } // Statsd contains the config items for statsd metrics reporter Statsd struct { // The host and port of the statsd server HostPort string `yaml:"hostPort" validate:"nonzero"` // The prefix to use in reporting to statsd Prefix string `yaml:"prefix" validate:"nonzero"` // FlushInterval is the maximum interval for sending packets. // If it is not specified, it defaults to 1 second. FlushInterval time.Duration `yaml:"flushInterval"` // FlushBytes specifies the maximum udp packet size you wish to send. // If FlushBytes is unspecified, it defaults to 1432 bytes, which is // considered safe for local traffic. FlushBytes int `yaml:"flushBytes"` } // Archival contains the config for archival Archival struct { // History is the config for the history archival History HistoryArchival `yaml:"history"` // Visibility is the config for visibility archival Visibility VisibilityArchival `yaml:"visibility"` } // HistoryArchival contains the config for history archival HistoryArchival struct { // Status is the status of history archival either: enabled, disabled, or paused Status string `yaml:"status"` // EnableRead whether history can be read from archival EnableRead bool `yaml:"enableRead"` // Provider contains the config for all history archivers Provider HistoryArchiverProvider `yaml:"provider"` } // HistoryArchiverProvider contains the config for all history archivers. // // Because archivers support external plugins, so there is no fundamental structure expected, // but a top-level key per named store plugin is required, and will be used to select the // config for a plugin as it is initialized. // // Config keys and structures expected in the main default binary include: // - FilestoreConfig: [*FilestoreArchiver], used with provider scheme [github.com/uber/cadence/common/archiver/filestore.URIScheme] // - S3storeConfig: [*S3Archiver], used with provider scheme [github.com/uber/cadence/common/archiver/s3store.URIScheme] // - "gstorage" via [github.com/uber/cadence/common/archiver/gcloud.ConfigKey]: [github.com/uber/cadence/common/archiver/gcloud.Config], used with provider scheme "gs" [github.com/uber/cadence/common/archiver/gcloud.URIScheme] // // For handling hardcoded config, see ToYamlNode. HistoryArchiverProvider map[string]*YamlNode // VisibilityArchival contains the config for visibility archival VisibilityArchival struct { // Status is the status of visibility archival either: enabled, disabled, or paused Status string `yaml:"status"` // EnableRead whether visibility can be read from archival EnableRead bool `yaml:"enableRead"` // Provider contains the config for all visibility archivers Provider VisibilityArchiverProvider `yaml:"provider"` } // VisibilityArchiverProvider contains the config for all visibility archivers. // // Because archivers support external plugins, so there is no fundamental structure expected, // but a top-level key per named store plugin is required, and will be used to select the // config for a plugin as it is initialized. // // Config keys and structures expected in the main default binary include: // - FilestoreConfig: [*FilestoreArchiver], used with provider scheme [github.com/uber/cadence/common/archiver/filestore.URIScheme] // - S3storeConfig: [*S3Archiver], used with provider scheme [github.com/uber/cadence/common/archiver/s3store.URIScheme] // - "gstorage" via [github.com/uber/cadence/common/archiver/gcloud.ConfigKey]: [github.com/uber/cadence/common/archiver/gcloud.Config], used with provider scheme "gs" [github.com/uber/cadence/common/archiver/gcloud.URIScheme] // // For handling hardcoded config, see ToYamlNode. VisibilityArchiverProvider map[string]*YamlNode // FilestoreArchiver contain the config for filestore archiver FilestoreArchiver struct { FileMode string `yaml:"fileMode"` DirMode string `yaml:"dirMode"` } // S3Archiver contains the config for S3 archiver S3Archiver struct { Region string `yaml:"region"` Endpoint *string `yaml:"endpoint"` S3ForcePathStyle bool `yaml:"s3ForcePathStyle"` } // PublicClient is config for connecting to cadence frontend PublicClient struct { // HostPort is the host port to connect on. Host can be DNS name // Default to currentCluster's RPCAddress in ClusterInformation HostPort string `yaml:"hostPort"` // Transport is the tranport to use when communicating using the SDK client. // Defaults to: // - currentCluster's RPCTransport in ClusterInformation (if HostPort is not provided) // - grpc (if HostPort is provided) Transport string `yaml:"transport"` // interval to refresh DNS. Default to 10s RefreshInterval time.Duration `yaml:"RefreshInterval"` } // DomainDefaults is the default config for each domain DomainDefaults struct { // Archival is the default archival config for each domain Archival ArchivalDomainDefaults `yaml:"archival"` } // ArchivalDomainDefaults is the default archival config for each domain ArchivalDomainDefaults struct { // History is the domain default history archival config for each domain History HistoryArchivalDomainDefaults `yaml:"history"` // Visibility is the domain default visibility archival config for each domain Visibility VisibilityArchivalDomainDefaults `yaml:"visibility"` } // HistoryArchivalDomainDefaults is the default history archival config for each domain HistoryArchivalDomainDefaults struct { // Status is the domain default status of history archival: enabled or disabled Status string `yaml:"status"` // URI is the domain default URI for history archiver URI string `yaml:"URI"` } // VisibilityArchivalDomainDefaults is the default visibility archival config for each domain VisibilityArchivalDomainDefaults struct { // Status is the domain default status of visibility archival: enabled or disabled Status string `yaml:"status"` // URI is the domain default URI for visibility archiver URI string `yaml:"URI"` } // ShardDistributorClient contains the config items for shard distributor ShardDistributorClient struct { // The host and port of the shard distributor server HostPort string `yaml:"hostPort"` } // YamlNode is a lazy-unmarshaler, because *yaml.Node only exists in gopkg.in/yaml.v3, not v2, // and go.uber.org/config currently uses only v2. YamlNode struct { unmarshal func(out any) error } // AsyncWorkflowQueueProvider contains the config for an async workflow queue. // Type is the implementation type of the queue provider. // Config is the configuration for the queue provider. // Config types and structures expected in the main default binary include: // - type: "kafka", config: [*github.com/uber/cadence/common/asyncworkflow/queue/kafka.QueueConfig]]] AsyncWorkflowQueueProvider struct { Type string `yaml:"type"` Config *YamlNode `yaml:"config"` } ) const ( // NonShardedStoreName is the shard name used for singular (non-sharded) stores NonShardedStoreName = "NonShardedStore" FilestoreConfig = "filestore" S3storeConfig = "s3store" ) var _ yaml.Unmarshaler = (*YamlNode)(nil) func (y *YamlNode) UnmarshalYAML(unmarshal func(interface{}) error) error { y.unmarshal = unmarshal return nil } func (y *YamlNode) Decode(out any) error { if y == nil { return nil } return y.unmarshal(out) } // ToYamlNode is a bit of a hack to get a *yaml.Node for config-parsing compatibility purposes. // There is probably a better way to achieve this with yaml-loading compatibility, but this is at least fairly simple. func ToYamlNode(input any) (*YamlNode, error) { data, err := yaml.Marshal(input) if err != nil { // should be extremely unlikely, unless yaml marshaling is customized return nil, fmt.Errorf("could not serialize data to yaml: %w", err) } var out *YamlNode err = yaml.Unmarshal(data, &out) if err != nil { // should not be possible return nil, fmt.Errorf("could not deserialize to yaml node: %w", err) } return out, nil } func (n *NoSQL) ConvertToShardedNoSQLConfig() *ShardedNoSQL { connections := make(map[string]DBShardConnection) connections[NonShardedStoreName] = DBShardConnection{ NoSQLPlugin: n, } return &ShardedNoSQL{ DefaultShard: NonShardedStoreName, Connections: connections, } } // ValidateAndFillDefaults validates this config and fills default values if needed func (c *Config) ValidateAndFillDefaults() error { c.fillDefaults() return c.validate() } func (c *Config) validate() error { if err := c.Persistence.Validate(); err != nil { return err } if err := c.ClusterGroupMetadata.Validate(); err != nil { return err } if err := c.Archival.Validate(&c.DomainDefaults.Archival); err != nil { return err } return c.Authorization.Validate() } func (c *Config) fillDefaults() { c.Persistence.FillDefaults() // TODO: remove this at the point when we decided to make some breaking changes in config. if c.ClusterGroupMetadata == nil && c.ClusterMetadata != nil { c.ClusterGroupMetadata = c.ClusterMetadata log.Println("[WARN] clusterMetadata config is deprecated. Please replace it with clusterGroupMetadata.") } c.ClusterGroupMetadata.FillDefaults() // filling publicClient with current cluster's RPC address if empty if c.PublicClient.HostPort == "" && c.ClusterGroupMetadata != nil { name := c.ClusterGroupMetadata.CurrentClusterName currentCluster := c.ClusterGroupMetadata.ClusterGroup[name] c.PublicClient.HostPort = currentCluster.RPCAddress c.PublicClient.Transport = currentCluster.RPCTransport } if c.PublicClient.Transport == "" { c.PublicClient.Transport = "grpc" } if c.ClusterGroupMetadata.ClusterRedirectionPolicy == nil && c.DCRedirectionPolicy != nil { log.Println("[WARN] dcRedirectionPolicy config is deprecated. Please replace it with clusterRedirectionPolicy.") c.ClusterGroupMetadata.ClusterRedirectionPolicy = c.DCRedirectionPolicy } } // String converts the config object into a string func (c *Config) String() string { out, _ := json.MarshalIndent(c, "", " ") return string(out) } func (c *Config) GetServiceConfig(serviceName string) (Service, error) { shortName := service.ShortName(serviceName) serviceConfig, ok := c.Services[shortName] if !ok { return Service{}, fmt.Errorf("no config section for service: %s", shortName) } return serviceConfig, nil } ================================================ FILE: common/config/config_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "net/url" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" uconfig "go.uber.org/config" "gopkg.in/validator.v2" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/service" ) func TestToString(t *testing.T) { var cfg Config err := Load("", "../../config", "", &cfg) assert.NoError(t, err) assert.NotEmpty(t, cfg.String()) } func TestFillingDefaultSQLEncodingDecodingTypes(t *testing.T) { cfg := &Config{ Persistence: Persistence{ DataStores: map[string]DataStore{ "sql": { SQL: &SQL{}, }, }, }, ClusterGroupMetadata: &ClusterGroupMetadata{}, } cfg.fillDefaults() assert.Equal(t, string(constants.EncodingTypeThriftRW), cfg.Persistence.DataStores["sql"].SQL.EncodingType) assert.Equal(t, []string{string(constants.EncodingTypeThriftRW)}, cfg.Persistence.DataStores["sql"].SQL.DecodingTypes) } func getValidMultipleDatabasseConfig() *Config { metadata := validClusterGroupMetadata() cfg := &Config{ ClusterGroupMetadata: metadata, Persistence: Persistence{ DefaultStore: "default", AdvancedVisibilityStore: "esv7", DataStores: map[string]DataStore{ "default": { SQL: &SQL{ PluginName: "fake", ConnectProtocol: "tcp", NumShards: 2, UseMultipleDatabases: true, MultipleDatabasesConfig: []MultipleDatabasesConfigEntry{ { DatabaseName: "db1", ConnectAddr: "192.168.0.1:3306", }, { DatabaseName: "db2", ConnectAddr: "192.168.0.2:3306", }, }, }, }, "esv7": { ElasticSearch: &ElasticSearchConfig{ Version: "v7", URL: url.URL{Scheme: "http", Host: "127.0.0.1:9200", }, Indices: map[string]string{ "visibility": "cadence-visibility-dev", }, }, // no sql or nosql, should be populated from cassandra }, }, }, } return cfg } func TestValidMultipleDatabaseConfig(t *testing.T) { cfg := getValidMultipleDatabasseConfig() err := cfg.ValidateAndFillDefaults() require.NoError(t, err) } func TestInvalidMultipleDatabaseConfig_useBasicVisibility(t *testing.T) { cfg := getValidMultipleDatabasseConfig() cfg.Persistence.VisibilityStore = "basic" cfg.Persistence.DataStores["basic"] = DataStore{ SQL: &SQL{}, } err := cfg.ValidateAndFillDefaults() require.EqualError(t, err, "sql persistence config: multipleSQLDatabases can only be used with advanced visibility only") } func TestInvalidMultipleDatabaseConfig_wrongNumDBShards(t *testing.T) { cfg := getValidMultipleDatabasseConfig() sqlds := cfg.Persistence.DataStores["default"] sqlds.SQL.NumShards = 3 cfg.Persistence.DataStores["default"] = sqlds err := cfg.ValidateAndFillDefaults() require.EqualError(t, err, "sql persistence config: nShards must be greater than one and equal to the length of multipleDatabasesConfig") } func TestInvalidMultipleDatabaseConfig_nonEmptySQLUser(t *testing.T) { cfg := getValidMultipleDatabasseConfig() sqlds := cfg.Persistence.DataStores["default"] sqlds.SQL.User = "user" cfg.Persistence.DataStores["default"] = sqlds err := cfg.ValidateAndFillDefaults() require.EqualError(t, err, "sql persistence config: user can only be configured in multipleDatabasesConfig when UseMultipleDatabases is true") } func TestInvalidMultipleDatabaseConfig_nonEmptySQLPassword(t *testing.T) { cfg := getValidMultipleDatabasseConfig() sqlds := cfg.Persistence.DataStores["default"] sqlds.SQL.Password = "pw" cfg.Persistence.DataStores["default"] = sqlds err := cfg.ValidateAndFillDefaults() require.EqualError(t, err, "sql persistence config: password can only be configured in multipleDatabasesConfig when UseMultipleDatabases is true") } func TestInvalidMultipleDatabaseConfig_nonEmptySQLDatabaseName(t *testing.T) { cfg := getValidMultipleDatabasseConfig() sqlds := cfg.Persistence.DataStores["default"] sqlds.SQL.DatabaseName = "db" cfg.Persistence.DataStores["default"] = sqlds err := cfg.ValidateAndFillDefaults() require.EqualError(t, err, "sql persistence config: databaseName can only be configured in multipleDatabasesConfig when UseMultipleDatabases is true") } func TestInvalidMultipleDatabaseConfig_nonEmptySQLConnAddr(t *testing.T) { cfg := getValidMultipleDatabasseConfig() sqlds := cfg.Persistence.DataStores["default"] sqlds.SQL.ConnectAddr = "127.0.0.1:3306" cfg.Persistence.DataStores["default"] = sqlds err := cfg.ValidateAndFillDefaults() require.EqualError(t, err, "sql persistence config: connectAddr can only be configured in multipleDatabasesConfig when UseMultipleDatabases is true") } func TestConfigFallbacks(t *testing.T) { metadata := validClusterGroupMetadata() cfg := &Config{ Services: map[string]Service{ "frontend": { RPC: RPC{ Port: 7900, }, }, }, ClusterGroupMetadata: metadata, Persistence: Persistence{ DefaultStore: "default", VisibilityStore: "cass", DataStores: map[string]DataStore{ "default": { SQL: &SQL{ PluginName: "fake", ConnectProtocol: "tcp", ConnectAddr: "192.168.0.1:3306", DatabaseName: "db1", NumShards: 0, // default value, should be changed }, }, "cass": { Cassandra: &NoSQL{ Hosts: "127.0.0.1", }, // no sql or nosql, should be populated from cassandra }, }, }, } err := cfg.ValidateAndFillDefaults() require.NoError(t, err) // sanity check, must be valid or the later tests are potentially useless assert.NotEmpty(t, cfg.Persistence.DataStores["cass"].Cassandra, "cassandra config should remain after update") assert.NotEmpty(t, cfg.Persistence.DataStores["cass"].NoSQL, "nosql config should contain cassandra config / not be empty") assert.NotZero(t, cfg.Persistence.DataStores["default"].SQL.NumShards, "num shards should be nonzero") assert.Equal(t, "localhost:7833", cfg.PublicClient.HostPort) } func TestConfigErrorInAuthorizationConfig(t *testing.T) { cfg := &Config{ Authorization: Authorization{ OAuthAuthorizer: OAuthAuthorizer{ Enable: true, }, NoopAuthorizer: NoopAuthorizer{ Enable: true, }, }, ClusterGroupMetadata: &ClusterGroupMetadata{}, } err := cfg.ValidateAndFillDefaults() require.Error(t, err) } func TestGetServiceConfig(t *testing.T) { cfg := Config{} _, err := cfg.GetServiceConfig(service.Frontend) assert.EqualError(t, err, "no config section for service: frontend") cfg = Config{Services: map[string]Service{"frontend": {RPC: RPC{GRPCPort: 123}}}} svc, err := cfg.GetServiceConfig(service.Frontend) assert.NoError(t, err) assert.NotEmpty(t, svc) } func getValidShardedNoSQLConfig() *Config { metadata := validClusterGroupMetadata() cfg := &Config{ ClusterGroupMetadata: metadata, Persistence: Persistence{ NumHistoryShards: 2, DefaultStore: "default", AdvancedVisibilityStore: "visibility", DataStores: map[string]DataStore{ "default": { ShardedNoSQL: &ShardedNoSQL{ DefaultShard: "shard-1", ShardingPolicy: ShardingPolicy{ HistoryShardMapping: []HistoryShardRange{ HistoryShardRange{ Start: 0, End: 1, Shard: "shard-1", }, HistoryShardRange{ Start: 1, End: 2, Shard: "shard-2", }, }, TaskListHashing: TasklistHashing{ ShardOrder: []string{ "shard-1", "shard-2", }, }, }, Connections: map[string]DBShardConnection{ "shard-1": { NoSQLPlugin: &NoSQL{ PluginName: "cassandra", Hosts: "127.0.0.1", Keyspace: "unit-test", Port: 1234, }, }, "shard-2": { NoSQLPlugin: &NoSQL{ PluginName: "cassandra", Hosts: "127.0.0.1", Keyspace: "unit-test", Port: 5678, }, }, }, }, }, "visibility": { ElasticSearch: &ElasticSearchConfig{ Version: "v7", URL: url.URL{Scheme: "http", Host: "127.0.0.1:9200", }, Indices: map[string]string{ "visibility": "cadence-visibility-dev", }, }, }, }, }, } return cfg } func TestValidShardedNoSQLConfig(t *testing.T) { cfg := getValidShardedNoSQLConfig() err := cfg.ValidateAndFillDefaults() require.NoError(t, err) } func TestInvalidShardedNoSQLConfig_MultipleConfigTypes(t *testing.T) { cfg := getValidShardedNoSQLConfig() store := cfg.Persistence.DataStores["default"] store.NoSQL = &NoSQL{} cfg.Persistence.DataStores["default"] = store err := cfg.ValidateAndFillDefaults() require.ErrorContains(t, err, "must provide exactly one type of config, but provided 2") } func TestInvalidShardedNoSQLConfig_MissingDefaultShard(t *testing.T) { cfg := getValidShardedNoSQLConfig() store := cfg.Persistence.DataStores["default"] store.ShardedNoSQL.DefaultShard = "" err := cfg.ValidateAndFillDefaults() require.ErrorContains(t, err, "defaultShard can not be empty") } func TestInvalidShardedNoSQLConfig_UnknownDefaultShard(t *testing.T) { cfg := getValidShardedNoSQLConfig() store := cfg.Persistence.DataStores["default"] delete(store.ShardedNoSQL.Connections, store.ShardedNoSQL.DefaultShard) err := cfg.ValidateAndFillDefaults() require.ErrorContains(t, err, "defaultShard (shard-1) is not defined in connections list") } func TestInvalidShardedNoSQLConfig_HistoryShardingUnordered(t *testing.T) { cfg := getValidShardedNoSQLConfig() store := cfg.Persistence.DataStores["default"] store.ShardedNoSQL.ShardingPolicy.HistoryShardMapping[0].Start = 1 store.ShardedNoSQL.ShardingPolicy.HistoryShardMapping[0].End = 2 store.ShardedNoSQL.ShardingPolicy.HistoryShardMapping[1].Start = 0 store.ShardedNoSQL.ShardingPolicy.HistoryShardMapping[1].End = 1 err := cfg.ValidateAndFillDefaults() require.ErrorContains(t, err, "Non-continuous history shard range") } func TestInvalidShardedNoSQLConfig_HistoryShardingOverlapping(t *testing.T) { cfg := getValidShardedNoSQLConfig() store := cfg.Persistence.DataStores["default"] store.ShardedNoSQL.ShardingPolicy.HistoryShardMapping[0].End = 2 // 0-2 overlaps with 1-2 err := cfg.ValidateAndFillDefaults() require.ErrorContains(t, err, "Non-continuous history shard range") } func TestInvalidShardedNoSQLConfig_HistoryShardingMissingFirstShard(t *testing.T) { cfg := getValidShardedNoSQLConfig() store := cfg.Persistence.DataStores["default"] store.ShardedNoSQL.ShardingPolicy.HistoryShardMapping = store.ShardedNoSQL.ShardingPolicy.HistoryShardMapping[1:] err := cfg.ValidateAndFillDefaults() require.ErrorContains(t, err, "Non-continuous history shard range") } func TestInvalidShardedNoSQLConfig_HistoryShardingMissingLastShard(t *testing.T) { cfg := getValidShardedNoSQLConfig() cfg.Persistence.NumHistoryShards = 3 // config only specifies shards 0 and 1, so this is invalid err := cfg.ValidateAndFillDefaults() require.ErrorContains(t, err, "Last history shard found in the config is 1 while the max is 2") } func TestInvalidShardedNoSQLConfig_HistoryShardingRefersToUnknownConnection(t *testing.T) { cfg := getValidShardedNoSQLConfig() store := cfg.Persistence.DataStores["default"] store.ShardedNoSQL.ShardingPolicy.HistoryShardMapping[0].Shard = "unknown-shard-name" err := cfg.ValidateAndFillDefaults() require.ErrorContains(t, err, "Unknown history shard name") } func TestInvalidShardedNoSQLConfig_TasklistShardingRefersToUnknownConnection(t *testing.T) { cfg := getValidShardedNoSQLConfig() store := cfg.Persistence.DataStores["default"] store.ShardedNoSQL.ShardingPolicy.TaskListHashing.ShardOrder[1] = "unknown-shard-name" err := cfg.ValidateAndFillDefaults() require.ErrorContains(t, err, "Unknown tasklist shard name") } func TestHistogramMigrationConfig(t *testing.T) { t.Run("valid", func(t *testing.T) { orig := metrics.HistogramMigrationMetrics t.Cleanup(func() { metrics.HistogramMigrationMetrics = orig }) metrics.HistogramMigrationMetrics = map[string]struct{}{ "key1": {}, "key2": {}, "key3": {}, "key4": {}, } // intentionally avoiding the full config struct, as it has other required config yaml, err := uconfig.NewYAML(uconfig.RawSource(strings.NewReader(` default: histogram names: key1: true key2: false key3: `))) require.NoError(t, err) var cfg metrics.HistogramMigration err = yaml.Get(uconfig.Root).Populate(&cfg) require.NoError(t, err) err = validator.Validate(cfg) require.NoError(t, err) check := func(key string, timer, histogram bool) { assert.Equalf(t, timer, cfg.EmitTimer(key), "wrong value for EmitTimer(%q)", key) assert.Equalf(t, histogram, cfg.EmitHistogram(key), "wrong value for EmitHistogram(%q)", key) } check("key1", true, true) check("key2", false, false) check("key3", false, false) // the type's default mode == false. not truly intended behavior, but it's weird config so it's fine. check("key4", false, true) // configured default == histogram check("key5", true, true) // not migrating = always emitted if t.Failed() { t.Logf("config: %#v", cfg) } }) t.Run("invalid default", func(t *testing.T) { yaml, err := uconfig.NewYAML(uconfig.RawSource(strings.NewReader(` default: xyz `))) require.NoError(t, err) var cfg metrics.HistogramMigration err = yaml.Get(uconfig.Root).Populate(&cfg) assert.ErrorContains(t, err, `unsupported histogram migration mode "xyz", must be "timer", "histogram", or "both"`) }) t.Run("invalid key", func(t *testing.T) { orig := metrics.HistogramMigrationMetrics t.Cleanup(func() { metrics.HistogramMigrationMetrics = orig }) metrics.HistogramMigrationMetrics = map[string]struct{}{ "key1": {}, } yaml, err := uconfig.NewYAML(uconfig.RawSource(strings.NewReader(` names: key1: xyz `))) require.NoError(t, err) var cfg metrics.HistogramMigration err = yaml.Get(uconfig.Root).Populate(&cfg) assert.ErrorContains(t, err, "cannot unmarshal !!str `xyz` into bool") }) t.Run("nonexistent key", func(t *testing.T) { yaml, err := uconfig.NewYAML(uconfig.RawSource(strings.NewReader(` names: definitely_does_not_exist: true `))) require.NoError(t, err) var cfg metrics.HistogramMigration err = yaml.Get(uconfig.Root).Populate(&cfg) assert.ErrorContains(t, err, `unknown histogram-migration metric name "definitely_does_not_exist"`) }) } ================================================ FILE: common/config/elasticsearch.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "errors" "fmt" "net/url" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/uber/cadence/common/constants" ) var errAWSSigningCredential = fmt.Errorf("must provide exactly one type of credential, EnvironmentCredential or StaticCredential") // ElasticSearchConfig for connecting to ElasticSearch type ( ElasticSearchConfig struct { URL url.URL `yaml:"url"` //nolint:govet Indices map[string]string `yaml:"indices"` //nolint:govet // supporting v6 and v7. Default to v6 if empty. Version string `yaml:"version"` //nolint:govet // optional username to communicate with ElasticSearch Username string `yaml:"username"` //nolint:govet // optional password to communicate with ElasticSearch Password string `yaml:"password"` //nolint:govet // optional to disable sniff, according to issues on Github, // Sniff could cause issue like "no Elasticsearch node available" DisableSniff bool `yaml:"disableSniff"` // optional to disable health check DisableHealthCheck bool `yaml:"disableHealthCheck"` // optional to use AWS signing client // See more info https://github.com/olivere/elastic/wiki/Using-with-AWS-Elasticsearch-Service AWSSigning AWSSigning `yaml:"awsSigning"` // optional to use Signed Certificates over https TLS TLS `yaml:"tls"` // optional to add custom headers CustomHeaders map[string]string `yaml:"customHeaders,omitempty"` Migration VisibilityMigration `yaml:"migration"` // optional, will use default consumer name if not provided // default consumerName is topic + "-consumer" ConsumerName string `yaml:"consumerName"` } // AWSSigning contains config to enable signing, // Must provide either StaticCredential or EnvironmentCredential AWSSigning struct { Enable bool `yaml:"enable"` StaticCredential *AWSStaticCredential `yaml:"staticCredential"` EnvironmentCredential *AWSEnvironmentCredential `yaml:"environmentCredential"` } // AWSStaticCredential to create a static credentials value provider. // SessionToken is only required for temporary security credentials retrieved via STS, // otherwise an empty string can be passed for this parameter. // See more in https://github.com/aws/aws-sdk-go/blob/master/aws/credentials/static_provider.go#L21 AWSStaticCredential struct { AccessKey string `yaml:"accessKey"` Region string `yaml:"region"` SecretKey string `yaml:"secretKey"` SessionToken string `yaml:"sessionToken"` } // AWSEnvironmentCredential will make a new Session created from SDK defaults, config files, // environment, and user provided config files. // See more in https://github.com/aws/aws-sdk-go/blob/3974dd034387fbc7cf09c8cd2400787ce07f3285/aws/session/session.go#L147 AWSEnvironmentCredential struct { Region string `yaml:"region"` } VisibilityMigration struct { Enabled bool `yaml:"enabled"` } ) // GetVisibilityIndex return visibility index name func (cfg *ElasticSearchConfig) GetVisibilityIndex() string { return cfg.Indices[constants.VisibilityAppName] } // SetUsernamePassword set the username/password into URL // It is a bit tricky here because url.URL doesn't expose the username/password in the struct // because of the security concern. func (cfg *ElasticSearchConfig) SetUsernamePassword() { if cfg.Username != "" { cfg.URL.User = url.UserPassword(cfg.Username, cfg.Password) } } func (a AWSSigning) Validate() error { if a.EnvironmentCredential == nil && a.StaticCredential == nil { return errAWSSigningCredential } if a.EnvironmentCredential != nil && a.StaticCredential != nil { return errAWSSigningCredential } if a.EnvironmentCredential != nil && len(a.EnvironmentCredential.Region) == 0 { return errors.New("missing region in environmentCredential") } if a.StaticCredential != nil && len(a.StaticCredential.Region) == 0 { return errors.New("missing region in staticCredential") } return nil } func (a AWSSigning) GetCredentials() (*credentials.Credentials, *string, error) { if err := a.Validate(); err != nil { return nil, nil, err } // refer to https://github.com/olivere/elastic/blob/release-branch.v7/recipes/aws-connect-v4/main.go if a.EnvironmentCredential != nil { sess, err := session.NewSession(&aws.Config{Region: &a.EnvironmentCredential.Region}) if err != nil { return nil, nil, fmt.Errorf("creating aws session: %w", err) } return sess.Config.Credentials, sess.Config.Region, nil } awsCredentials := credentials.NewStaticCredentials( a.StaticCredential.AccessKey, a.StaticCredential.SecretKey, a.StaticCredential.SessionToken, ) return awsCredentials, &a.StaticCredential.Region, nil } // GetCustomHeader returns the header for the specified key func (cfg *ElasticSearchConfig) GetCustomHeader(headerKey string) string { if headerValue, ok := cfg.CustomHeaders[headerKey]; ok { return headerValue } return "" } ================================================ FILE: common/config/elasticsearch_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package config import ( "errors" "testing" "github.com/stretchr/testify/assert" ) func TestAWSSigning_ValidateEmpty(t *testing.T) { tests := []struct { msg string config AWSSigning err error }{ { msg: "Empty config should error", config: AWSSigning{ StaticCredential: nil, EnvironmentCredential: nil, }, err: errAWSSigningCredential, }, { msg: "error when both config sections are provided", config: AWSSigning{ Enable: false, StaticCredential: &AWSStaticCredential{}, EnvironmentCredential: &AWSEnvironmentCredential{}, }, err: errAWSSigningCredential, }, { msg: "StaticCredential must have region set", config: AWSSigning{ Enable: false, StaticCredential: &AWSStaticCredential{}, EnvironmentCredential: nil, }, err: errors.New("missing region in staticCredential"), }, { msg: "EnvironmentCredential must have region set", config: AWSSigning{ Enable: false, StaticCredential: nil, EnvironmentCredential: &AWSEnvironmentCredential{}, }, err: errors.New("missing region in environmentCredential"), }, { msg: "Valid StaticCredential config should have no error ", config: AWSSigning{ Enable: false, StaticCredential: &AWSStaticCredential{Region: "region1"}, EnvironmentCredential: nil, }, err: nil, }, { msg: "Valid EnvironmentCredential config should have no error", config: AWSSigning{ Enable: false, StaticCredential: nil, EnvironmentCredential: &AWSEnvironmentCredential{Region: "region1"}, }, err: nil, }, } for _, tc := range tests { assert.Equal(t, tc.err, tc.config.Validate(), tc.msg) } } func TestGetCustomHeader(t *testing.T) { tests := []struct { config ElasticSearchConfig header string expected string }{ { config: ElasticSearchConfig{ CustomHeaders: map[string]string{ "key1": "value1", }, }, header: "key1", expected: "value1", }, { config: ElasticSearchConfig{ CustomHeaders: map[string]string{ "key1": "value1", }, }, header: "key2", expected: "", }, } for _, tc := range tests { val := tc.config.GetCustomHeader(tc.header) assert.Equal(t, val, tc.expected) } } ================================================ FILE: common/config/fx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package config import ( "fmt" "os" "go.uber.org/fx" "github.com/uber/cadence/common/metrics" ) // Module returns a config.Provider that could be used byother components. var Module = fx.Module("configfx", fx.Provide(New), ) type Context struct { Environment string Zone string } // Params defines the dependencies of the configfx module. type Params struct { fx.In Service string `name:"service"` Context Context LookupEnv LookupEnvFunc `optional:"true"` ConfigDir string `name:"config-dir"` Lifecycle fx.Lifecycle `optional:"true"` // required for strict mode } // Result defines the objects that the configfx module provides. type Result struct { fx.Out Config Config ServiceConfig Service Histograms metrics.HistogramMigration } // LookupEnvFunc returns the value of the environment variable given by key. // It should behave the same as `os.LookupEnv`. If a function returns false, // an environment variable is looked up using `os.LookupEnv`. type LookupEnvFunc func(key string) (string, bool) // New exports functionality similar to Module, but allows the caller to wrap // or modify Result. Most users should use Module instead. func New(p Params) (Result, error) { lookupFun := os.LookupEnv if p.LookupEnv != nil { lookupFun = func(key string) (string, bool) { if result, ok := p.LookupEnv(key); ok { return result, true } return lookupFun(key) } } var cfg Config err := Load(p.Context.Environment, p.ConfigDir, p.Context.Zone, &cfg) if err != nil { return Result{}, fmt.Errorf("load config: %w", err) } cfg.fillDefaults() svcCfg, err := cfg.GetServiceConfig(p.Service) if err != nil { return Result{}, fmt.Errorf("get service config: %w", err) } p.Lifecycle.Append(fx.StartHook(cfg.validate)) return Result{ Config: cfg, ServiceConfig: svcCfg, Histograms: cfg.Histograms, }, nil } ================================================ FILE: common/config/kafkaConfig.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import "fmt" type ( // KafkaConfig describes the configuration needed to connect to all kafka clusters KafkaConfig struct { TLS TLS `yaml:"tls"` SASL SASL `yaml:"sasl"` Clusters map[string]ClusterConfig `yaml:"clusters"` Topics map[string]TopicConfig `yaml:"topics"` // Applications describes the applications that will use the Kafka topics Applications map[string]TopicList `yaml:"applications"` Version string `yaml:"version"` } // ClusterConfig describes the configuration for a single Kafka cluster ClusterConfig struct { Brokers []string `yaml:"brokers"` } // TopicConfig describes the mapping from topic to Kafka cluster TopicConfig struct { Cluster string `yaml:"cluster"` // Properties map describes whether the topic properties, such as whether it is secure Properties map[string]any `yaml:"properties,omitempty"` } // TopicList describes the topic names for each cluster TopicList struct { Topic string `yaml:"topic"` DLQTopic string `yaml:"dlq-topic"` } ) // Validate will validate config for kafka func (k *KafkaConfig) Validate(checkApp bool) { if len(k.Clusters) == 0 { panic("Empty Kafka Cluster Config") } if len(k.Topics) == 0 { panic("Empty Topics Config") } validateTopicsFn := func(topic string) { if topic == "" { panic("Empty Topic Name") } else if topicConfig, ok := k.Topics[topic]; !ok { panic(fmt.Sprintf("Missing Topic Config for Topic %v", topic)) } else if clusterConfig, ok := k.Clusters[topicConfig.Cluster]; !ok { panic(fmt.Sprintf("Missing Kafka Cluster Config for Cluster %v", topicConfig.Cluster)) } else if len(clusterConfig.Brokers) == 0 { panic(fmt.Sprintf("Missing Kafka Brokers Config for Cluster %v", topicConfig.Cluster)) } } if checkApp { if len(k.Applications) == 0 { panic("Empty Applications Config") } for _, topics := range k.Applications { validateTopicsFn(topics.Topic) validateTopicsFn(topics.DLQTopic) } } } // GetKafkaClusterForTopic gets cluster from topic func (k *KafkaConfig) GetKafkaClusterForTopic(topic string) string { return k.Topics[topic].Cluster } // GetBrokersForKafkaCluster gets broker from cluster func (k *KafkaConfig) GetBrokersForKafkaCluster(kafkaCluster string) []string { return k.Clusters[kafkaCluster].Brokers } // GetTopicsForApplication gets topic from application func (k *KafkaConfig) GetTopicsForApplication(app string) TopicList { return k.Applications[app] } // GetKafkaPropertiesForTopic gets properties from topic func (k *KafkaConfig) GetKafkaPropertyForTopic(topic string, property string) any { topicConfig, ok := k.Topics[topic] if !ok || topicConfig.Properties == nil { // No properties for the specified topic in the config return nil } // retrieve the property from the topic properties propertyValue, ok := topicConfig.Properties[property] if !ok { // Property not found return nil } return propertyValue } ================================================ FILE: common/config/loader.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "fmt" "log" "os" uconfig "go.uber.org/config" "gopkg.in/validator.v2" ) const ( // EnvKeyRoot the environment variable key for runtime root dir EnvKeyRoot = "CADENCE_ROOT" // EnvKeyConfigDir the environment variable key for config dir EnvKeyConfigDir = "CADENCE_CONFIG_DIR" // EnvKeyEnvironment is the environment variable key for environment EnvKeyEnvironment = "CADENCE_ENVIRONMENT" // EnvKeyAvailabilityZone is the environment variable key for AZ EnvKeyAvailabilityZone = "CADENCE_AVAILABILTY_ZONE" ) const ( baseFile = "base.yaml" envDevelopment = "development" defaultConfigDir = "config" ) // Load loads the configuration from a set of // yaml config files found in the config directory // // The loader first fetches the set of files matching // a pre-determined naming convention, then sorts // them by hierarchy order and after that, simply // loads the files one after another with the // key/values in the later files overriding the key/values // in the earlier files // // The hierarchy is as follows from lowest to highest // // base.yaml // env.yaml -- environment is one of the input params ex-development // env_az.yaml -- zone is another input param func Load(env string, configDir string, zone string, config interface{}) error { if len(env) == 0 { env = envDevelopment } if len(configDir) == 0 { configDir = defaultConfigDir } files, err := getConfigFiles(env, configDir, zone) if err != nil { return fmt.Errorf("unable to get config files: %w", err) } log.Printf("Loading configFiles=%v\n", files) var options []uconfig.YAMLOption for _, f := range files { options = append(options, uconfig.File(f)) } // expand env variables declared in .yaml files options = append(options, uconfig.Expand(os.LookupEnv)) yaml, err := uconfig.NewYAML(options...) if err != nil { return fmt.Errorf("unable to create yaml parser: %w", err) } err = yaml.Get(uconfig.Root).Populate(config) if err != nil { return fmt.Errorf("unable to populate config: %w", err) } err = validator.Validate(config) if err != nil { return fmt.Errorf("failed to validate config: %w", err) } return nil } // getConfigFiles returns the list of config files to // process in the hierarchy order func getConfigFiles(env string, configDir string, zone string) ([]string, error) { candidates := []string{ path(configDir, baseFile), path(configDir, file(env, "yaml")), } if len(zone) > 0 { f := file(concat(env, zone), "yaml") candidates = append(candidates, path(configDir, f)) } var result []string for _, c := range candidates { if _, err := os.Stat(c); err != nil { continue } result = append(result, c) } if len(result) == 0 { return nil, fmt.Errorf("no config files found within %v", configDir) } return result, nil } func concat(a, b string) string { return a + "_" + b } func file(name string, suffix string) string { return name + "." + suffix } func path(dir string, file string) string { return dir + "/" + file } ================================================ FILE: common/config/loader_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "io/ioutil" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type ( LoaderSuite struct { *require.Assertions suite.Suite } itemsConfig struct { Item1 string `yaml:"item1"` Item2 string `yaml:"item2"` } testConfig struct { Items itemsConfig `yaml:"items"` } ) func TestLoaderSuite(t *testing.T) { suite.Run(t, new(LoaderSuite)) } func (s *LoaderSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *LoaderSuite) TestBaseYaml() { dir := s.T().TempDir() s.createFile(dir, "base.yaml", ` items: item1: base1 item2: base2`) envs := []string{"", "prod"} zones := []string{"", "us-east-1a"} for _, env := range envs { for _, zone := range zones { var cfg testConfig err := Load(env, dir, zone, &cfg) s.Nil(err) s.Equal("base1", cfg.Items.Item1) s.Equal("base2", cfg.Items.Item2) } } } func (s *LoaderSuite) TestHierarchy() { dir := s.T().TempDir() s.createFile(dir, "base.yaml", ` items: item1: base1 item2: base2`) s.createFile(dir, "development.yaml", ` items: item1: development1`) s.createFile(dir, "prod.yaml", ` items: item1: prod1`) s.createFile(dir, "prod_dca.yaml", ` items: item1: prod_dca1`) testCases := []struct { env string zone string item1 string item2 string }{ {"", "", "development1", "base2"}, {"", "dca", "development1", "base2"}, {"", "pdx", "development1", "base2"}, {"development", "", "development1", "base2"}, {"development", "dca", "development1", "base2"}, {"development", "pdx", "development1", "base2"}, {"prod", "", "prod1", "base2"}, {"prod", "dca", "prod_dca1", "base2"}, {"prod", "pdx", "prod1", "base2"}, } for _, tc := range testCases { var cfg testConfig err := Load(tc.env, dir, tc.zone, &cfg) s.Nil(err) s.Equal(tc.item1, cfg.Items.Item1) s.Equal(tc.item2, cfg.Items.Item2) } } func (s *LoaderSuite) TestInvalidPath() { var cfg testConfig err := Load("prod", "", "", &cfg) s.NotNil(err) } func (s *LoaderSuite) createFile(dir string, file string, content string) { err := ioutil.WriteFile(path(dir, file), []byte(content), fileMode) s.Nil(err) } ================================================ FILE: common/config/log.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "fmt" "os" "strings" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) const fileMode = os.FileMode(0644) // NewZapLogger builds and returns a new // Zap logger for this logging configuration func (cfg *Logger) NewZapLogger() (*zap.Logger, error) { levelKey := cfg.LevelKey if levelKey == "" { levelKey = "level" } encodeConfig := zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: levelKey, NameKey: "logger", CallerKey: "", // we use our own caller, check common/log/logger.go MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: nil, } outputPath := "stderr" if len(cfg.OutputFile) > 0 { outputPath = cfg.OutputFile if cfg.Stdout { outputPath = "stdout" } } encoding := "json" if cfg.Encoding != "" { if cfg.Encoding == "json" || cfg.Encoding == "console" { encoding = cfg.Encoding } else { return nil, fmt.Errorf("invalid encoding for log, only supporting json or console") } } config := zap.Config{ Level: zap.NewAtomicLevelAt(parseZapLevel(cfg.Level)), Development: false, Sampling: nil, // consider exposing this to config for our external customer Encoding: encoding, EncoderConfig: encodeConfig, OutputPaths: []string{outputPath}, ErrorOutputPaths: []string{outputPath}, } return config.Build() } func parseZapLevel(level string) zapcore.Level { switch strings.ToLower(level) { case "debug": return zap.DebugLevel case "info": return zap.InfoLevel case "warn": return zap.WarnLevel case "error": return zap.ErrorLevel case "fatal": return zap.FatalLevel default: return zap.InfoLevel } } ================================================ FILE: common/config/log_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "os" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/zap" ) type LogSuite struct { *require.Assertions suite.Suite } func TestLogSuite(t *testing.T) { suite.Run(t, new(LogSuite)) } func (s *LogSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *LogSuite) TestParseLogLevel() { s.Equal(zap.DebugLevel, parseZapLevel("debug")) s.Equal(zap.InfoLevel, parseZapLevel("info")) s.Equal(zap.WarnLevel, parseZapLevel("warn")) s.Equal(zap.ErrorLevel, parseZapLevel("error")) s.Equal(zap.FatalLevel, parseZapLevel("fatal")) s.Equal(zap.InfoLevel, parseZapLevel("unknown")) } func (s *LogSuite) TestNewLogger() { dir := s.T().TempDir() config := &Logger{ Level: "info", OutputFile: dir + "/test.log", } log, _ := config.NewZapLogger() s.NotNil(log) _, err := os.Stat(dir + "/test.log") s.Nil(err) } ================================================ FILE: common/config/metrics.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "time" "github.com/cactus/go-statsd-client/statsd" prom "github.com/m3db/prometheus_client_golang/prometheus" "github.com/uber-go/tally" "github.com/uber-go/tally/prometheus" tallystatsdreporter "github.com/uber-go/tally/statsd" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" mprom "github.com/uber/cadence/common/metrics/tally/prometheus" statsdreporter "github.com/uber/cadence/common/metrics/tally/statsd" ) const ( _defaultReportingInterval = time.Second ) // tally sanitizer options that satisfy both Prometheus and M3 restrictions. // This will rename metrics at the tally emission level, so metrics name we // use maybe different from what gets emitted. In the current implementation // it will replace - and . with _ // We should still ensure that the base metrics are prometheus compatible, // but this is necessary as the same prom client initialization is used by // our system workflows. var ( safeCharacters = []rune{'_'} sanitizeOptions = tally.SanitizeOptions{ NameCharacters: tally.ValidCharacters{ Ranges: tally.AlphanumericRange, Characters: safeCharacters, }, KeyCharacters: tally.ValidCharacters{ Ranges: tally.AlphanumericRange, Characters: safeCharacters, }, ValueCharacters: tally.ValidCharacters{ Ranges: tally.AlphanumericRange, Characters: safeCharacters, }, ReplacementCharacter: tally.DefaultReplacementCharacter, } ) // NewScope builds a new tally scope for this metrics configuration // Only one reporter type is allowed func (c *Metrics) NewScope(logger log.Logger, service string) tally.Scope { if c.ReportingInterval <= 0 { c.ReportingInterval = _defaultReportingInterval } rootScope := tally.NoopScope if c.M3 != nil { rootScope = c.newM3Scope(logger) } if c.Statsd != nil { if rootScope != tally.NoopScope { logger.Fatal("error creating metric reporter: cannot have more than one types of metric configuration") } rootScope = c.newStatsdScope(logger) } if c.Prometheus != nil { if rootScope != tally.NoopScope { logger.Fatal("error creating metric reporter: cannot have more than one types of metric configuration") } rootScope = c.newPrometheusScope(logger) } rootScope = rootScope.Tagged(map[string]string{metrics.CadenceServiceTagName: service}) return rootScope } // newM3Scope returns a new m3 scope with // a default reporting interval of a second func (c *Metrics) newM3Scope(logger log.Logger) tally.Scope { reporter, err := c.M3.NewReporter() if err != nil { logger.Fatal("error creating m3 reporter", tag.Error(err)) } scopeOpts := tally.ScopeOptions{ Tags: c.Tags, CachedReporter: reporter, Prefix: c.Prefix, } scope, _ := tally.NewRootScope(scopeOpts, c.ReportingInterval) return scope } // newM3Scope returns a new statsd scope with // a default reporting interval of a second func (c *Metrics) newStatsdScope(logger log.Logger) tally.Scope { config := c.Statsd if len(config.HostPort) == 0 { return tally.NoopScope } statter, err := statsd.NewClientWithConfig(&statsd.ClientConfig{ Address: config.HostPort, Prefix: config.Prefix, UseBuffered: true, FlushInterval: config.FlushInterval, FlushBytes: config.FlushBytes, }) if err != nil { logger.Fatal("error creating statsd client", tag.Error(err)) } // NOTE: according to ( https://github.com/uber-go/tally )Tally's statsd implementation doesn't support tagging. // Therefore, we implement Tally interface to have a statsd reporter that can support tagging reporter := statsdreporter.NewReporter(statter, tallystatsdreporter.Options{}) scopeOpts := tally.ScopeOptions{ Tags: c.Tags, Reporter: reporter, Prefix: c.Prefix, } scope, _ := tally.NewRootScope(scopeOpts, c.ReportingInterval) return scope } // newPrometheusScope returns a new prometheus scope with // a default reporting interval of a second func (c *Metrics) newPrometheusScope(logger log.Logger) tally.Scope { if len(c.Prometheus.DefaultHistogramBuckets) == 0 { c.Prometheus.DefaultHistogramBuckets = mprom.DefaultHistogramBuckets() } reporter, err := c.Prometheus.NewReporter( prometheus.ConfigurationOptions{ Registry: prom.NewRegistry(), OnError: func(err error) { logger.Warn("error in prometheus reporter", tag.Error(err)) }, }, ) if err != nil { logger.Fatal("error creating prometheus reporter", tag.Error(err)) } scopeOpts := tally.ScopeOptions{ Tags: c.Tags, CachedReporter: reporter, Separator: prometheus.DefaultSeparator, SanitizeOptions: &sanitizeOptions, Prefix: c.Prefix, } scope, _ := tally.NewRootScope(scopeOpts, c.ReportingInterval) return scope } ================================================ FILE: common/config/metrics_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "github.com/uber-go/tally/m3" "github.com/uber-go/tally/prometheus" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) type MetricsSuite struct { *require.Assertions suite.Suite } func TestMetricsSuite(t *testing.T) { suite.Run(t, new(MetricsSuite)) } func (s *MetricsSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *MetricsSuite) TestStatsd() { statsd := &Statsd{ HostPort: "127.0.0.1:8125", Prefix: "testStatsd", } config := new(Metrics) config.Statsd = statsd scope := config.NewScope(testlogger.New(s.T()), "test") s.NotNil(scope) } func (s *MetricsSuite) TestM3() { m3 := &m3.Configuration{ HostPort: "127.0.0.1:8125", Service: "testM3", Env: "devel", } config := new(Metrics) config.M3 = m3 scope := config.NewScope(testlogger.New(s.T()), "test") s.NotNil(scope) } func (s *MetricsSuite) TestPrometheus() { prom := &prometheus.Configuration{ OnError: "panic", TimerType: "histogram", ListenAddress: "127.0.0.1:0", } config := new(Metrics) config.Prometheus = prom scope := config.NewScope(testlogger.New(s.T()), "test") s.NotNil(scope) } func (s *MetricsSuite) TestNoop() { config := &Metrics{} scope := config.NewScope(testlogger.New(s.T()), "test") s.Equal(tally.NoopScope.Tagged(map[string]string{metrics.CadenceServiceTagName: "test"}), scope) } ================================================ FILE: common/config/persistence.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "fmt" "github.com/uber/cadence/common/constants" ) const ( // StoreTypeSQL refers to sql based storage as persistence store StoreTypeSQL = "sql" // StoreTypeCassandra refers to cassandra as persistence store StoreTypeCassandra = "cassandra" ) // DefaultStoreType returns the storeType for the default persistence store func (c *Persistence) DefaultStoreType() string { if c.DataStores[c.DefaultStore].SQL != nil { return StoreTypeSQL } return StoreTypeCassandra } // FillDefaults populates default values for unspecified fields in persistence config func (c *Persistence) FillDefaults() { for k, store := range c.DataStores { if store.Cassandra != nil && store.NoSQL == nil { // for backward-compatibility store.NoSQL = store.Cassandra store.NoSQL.PluginName = "cassandra" } if store.SQL != nil { // filling default encodingType/decodingTypes for SQL persistence if store.SQL.EncodingType == "" { store.SQL.EncodingType = string(constants.EncodingTypeThriftRW) } if len(store.SQL.DecodingTypes) == 0 { store.SQL.DecodingTypes = []string{ string(constants.EncodingTypeThriftRW), } } if store.SQL.NumShards == 0 { store.SQL.NumShards = 1 } } // write changes back to DataStores, as ds is a value object c.DataStores[k] = store } } // Validate validates the persistence config func (c *Persistence) Validate() error { dbStoreKeys := []string{c.DefaultStore} useAdvancedVisibilityOnly := false if _, ok := c.DataStores[c.VisibilityStore]; ok { dbStoreKeys = append(dbStoreKeys, c.VisibilityStore) } else { if _, ok := c.DataStores[c.AdvancedVisibilityStore]; !ok { return fmt.Errorf("must provide one of VisibilityStore and AdvancedVisibilityStore") } useAdvancedVisibilityOnly = true } for _, st := range dbStoreKeys { ds, ok := c.DataStores[st] if !ok { return fmt.Errorf("persistence config: missing config for datastore %v", st) } if ds.Cassandra != nil && ds.NoSQL != nil && ds.Cassandra != ds.NoSQL { return fmt.Errorf("persistence config: datastore %v: only one of Cassandra or NoSQL can be specified", st) } configCount := 0 if ds.NoSQL != nil { configCount++ } if ds.ShardedNoSQL != nil { configCount++ } if ds.SQL != nil { configCount++ } if configCount != 1 { return fmt.Errorf("persistence config: datastore %v: must provide exactly one type of config, but provided %d", st, configCount) } if ds.SQL != nil { switch { case ds.SQL.UseMultipleDatabases: if !useAdvancedVisibilityOnly { return fmt.Errorf("sql persistence config: multipleSQLDatabases can only be used with advanced visibility only") } if ds.SQL.DatabaseName != "" { return fmt.Errorf("sql persistence config: databaseName can only be configured in multipleDatabasesConfig when UseMultipleDatabases is true") } if ds.SQL.ConnectAddr != "" { return fmt.Errorf("sql persistence config: connectAddr can only be configured in multipleDatabasesConfig when UseMultipleDatabases is true") } if ds.SQL.User != "" { return fmt.Errorf("sql persistence config: user can only be configured in multipleDatabasesConfig when UseMultipleDatabases is true") } if ds.SQL.Password != "" { return fmt.Errorf("sql persistence config: password can only be configured in multipleDatabasesConfig when UseMultipleDatabases is true") } if ds.SQL.NumShards <= 1 || len(ds.SQL.MultipleDatabasesConfig) != ds.SQL.NumShards { return fmt.Errorf("sql persistence config: nShards must be greater than one and equal to the length of multipleDatabasesConfig") } for _, entry := range ds.SQL.MultipleDatabasesConfig { if entry.DatabaseName == "" { return fmt.Errorf("sql multipleDatabasesConfig persistence config: databaseName can not be empty") } if entry.ConnectAddr == "" { return fmt.Errorf("sql multipleDatabasesConfig persistence config: connectAddr can not be empty") } } // SQLite plugin doesn't require ConnectAddr and DatabaseName case ds.SQL.PluginName != "sqlite": if ds.SQL.DatabaseName == "" { return fmt.Errorf("sql persistence config: databaseName can not be empty") } if ds.SQL.ConnectAddr == "" { return fmt.Errorf("sql persistence config: connectAddr can not be empty") } } } if ds.ShardedNoSQL != nil { cfg := ds.ShardedNoSQL connections := cfg.Connections // validate default shard if cfg.DefaultShard == "" { return fmt.Errorf("ShardedNosql config: defaultShard can not be empty") } if _, found := connections[cfg.DefaultShard]; !found { return fmt.Errorf( "ShardedNosql config: defaultShard (%v) is not defined in connections list", cfg.DefaultShard) } // validate history sharding historyShardMapping := cfg.ShardingPolicy.HistoryShardMapping currentShardID := 0 for _, shardRange := range historyShardMapping { if _, found := connections[shardRange.Shard]; !found { return fmt.Errorf("ShardedNosql config: Unknown history shard name: %v", shardRange.Shard) } if shardRange.Start != currentShardID { return fmt.Errorf("ShardedNosql config: Non-continuous history shard range %v (%v) found while expecting %v", shardRange.Start, shardRange.Shard, currentShardID, ) } currentShardID = shardRange.End } if currentShardID != c.NumHistoryShards { return fmt.Errorf("ShardedNosql config: Last history shard found in the config is %v while the max is %v", currentShardID-1, c.NumHistoryShards-1, ) } // validate tasklist sharding tasklistShards := cfg.ShardingPolicy.TaskListHashing.ShardOrder for _, shardName := range tasklistShards { if _, found := connections[shardName]; !found { return fmt.Errorf("ShardedNosql config: Unknown tasklist shard name: %v", shardName) } } } } return nil } // IsAdvancedVisibilityConfigExist returns whether user specified advancedVisibilityStore in config func (c *Persistence) IsAdvancedVisibilityConfigExist() bool { return len(c.AdvancedVisibilityStore) != 0 } ================================================ FILE: common/config/pinot.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config // PinotVisibilityConfig for connecting to Pinot type ( PinotVisibilityConfig struct { Cluster string `yaml:"cluster"` //nolint:govet Broker string `yaml:"broker"` //nolint:govet Table string `yaml:"table"` //nolint:govet ServiceName string `yaml:"serviceName"` //nolint:govet Migration VisibilityMigration `yaml:"migration"` //nolint:govet } ) ================================================ FILE: common/config/pprof.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "fmt" "net" "net/http" "sync/atomic" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" // DO NOT REMOVE THE LINE BELOW _ "net/http/pprof" ) type ( // PProfInitializerImpl initialize the pprof based on config PProfInitializerImpl struct { PProf *PProf Logger log.Logger } ) const ( pprofNotInitialized int32 = 0 pprofInitialized int32 = 1 ) // the pprof should only be initialized once per process // otherwise, the caller / worker will experience weird issue var pprofStatus = pprofNotInitialized // NewInitializer create a new instance of PProf Initializer func (cfg *PProf) NewInitializer(logger log.Logger) *PProfInitializerImpl { return &PProfInitializerImpl{ PProf: cfg, Logger: logger, } } // Start the pprof based on config func (initializer *PProfInitializerImpl) Start() error { port := initializer.PProf.Port if port == 0 { initializer.Logger.Info("PProf not started due to port not set") return nil } host := initializer.PProf.Host if host == "" { host = "localhost" } if atomic.CompareAndSwapInt32(&pprofStatus, pprofNotInitialized, pprofInitialized) { go func() { initializer.Logger.Info("PProf listen on ", tag.Dynamic("pprof-host", host), tag.Port(port)) err := http.ListenAndServe(net.JoinHostPort(host, fmt.Sprint(port)), nil) if err != nil { initializer.Logger.Error("listen and serve err", tag.Error(err)) } }() } return nil } ================================================ FILE: common/config/sasl.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config type ( // SASL describe SASL configuration (for Kafka) SASL struct { Enabled bool `yaml:"enabled"` // false as default User string `yaml:"user"` Password string `yaml:"password"` Algorithm string `yaml:"algorithm"` // plain, sha512 or sha256 } ) ================================================ FILE: common/config/tls.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "crypto/tls" "crypto/x509" "os" ) type ( // TLS describe TLS configuration TLS struct { Enabled bool `yaml:"enabled"` // For Postgres(https://www.postgresql.org/docs/9.1/libpq-ssl.html) and MySQL // default to require if Enable is true. // For MySQL: https://github.com/go-sql-driver/mysql , it also can be set in ConnectAttributes, default is tls-custom SSLMode string `yaml:"sslmode"` // CertPath and KeyPath are optional depending on server // config, but both fields must be omitted to avoid using a // client certificate CertFile string `yaml:"certFile"` KeyFile string `yaml:"keyFile"` CaFile string `yaml:"caFile"` // optional depending on server config CaFiles []string `yaml:"caFiles"` // If you want to verify the hostname and server cert (like a wildcard for cass cluster) then you should turn this on // This option is basically the inverse of InSecureSkipVerify // See InSecureSkipVerify in http://golang.org/pkg/crypto/tls/ for more info EnableHostVerification bool `yaml:"enableHostVerification"` // Set RequireClientAuth to true if mutual TLS is desired. // In this mode, client will need to present their certificate which is signed by CA // that is specified with server "caFile"/"caFiles" or stored in server host level CA store. RequireClientAuth bool `yaml:"requireClientAuth"` ServerName string `yaml:"serverName"` } ) // ToTLSConfig converts Cadence TLS config to crypto/tls.Config func (config TLS) ToTLSConfig() (*tls.Config, error) { if !config.Enabled { return nil, nil } // Setup base TLS config // EnableHostVerification is a secure flag vs insecureSkipVerify is insecure so inverse the value tlsConfig := &tls.Config{ InsecureSkipVerify: !config.EnableHostVerification, } // Setup server name if config.ServerName != "" { tlsConfig.ServerName = config.ServerName } // Load CA certs caFiles := config.CaFiles if config.CaFile != "" { caFiles = append(caFiles, config.CaFile) } if len(caFiles) > 0 { caCertPool := x509.NewCertPool() for _, caFile := range caFiles { caCert, err := os.ReadFile(caFile) if err != nil { return nil, err } caCertPool.AppendCertsFromPEM(caCert) } tlsConfig.RootCAs = caCertPool } // Enable mutual TLS if config.RequireClientAuth { tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert tlsConfig.ClientCAs = tlsConfig.RootCAs } // Load client cert if config.CertFile != "" && config.KeyFile != "" { cert, err := tls.LoadX509KeyPair(config.CertFile, config.KeyFile) if err != nil { return nil, err } tlsConfig.Certificates = []tls.Certificate{cert} } return tlsConfig, nil } ================================================ FILE: common/constants/constants.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package constants import ( "time" "github.com/uber/cadence/.gen/go/shadower" ) const ( // FirstEventID is the id of the first event in the history FirstEventID int64 = 1 // EmptyEventID is the id of the empty event EmptyEventID int64 = -23 // EmptyVersion is used as the default value for failover version when no value is provided EmptyVersion int64 = -24 // EndEventID is the id of the end event, here we use the int64 max EndEventID int64 = 1<<63 - 1 // BufferedEventID is the id of the buffered event BufferedEventID int64 = -123 // EmptyEventTaskID is uninitialized id of the task id within event EmptyEventTaskID int64 = -1234 // TransientEventID is the id of the transient event TransientEventID int64 = -124 // FirstBlobPageToken is the page token identifying the first blob for each history archival FirstBlobPageToken = 1 // LastBlobNextPageToken is the next page token on the last blob for each history archival LastBlobNextPageToken = -1 // InclusiveEndMessageID is the id of the end message, here we use the int64 max -1 because in some place we need to convert it to exclusive end message id InclusiveEndMessageID int64 = 1<<63 - 2 // EmptyMessageID is the default start message ID for replication level EmptyMessageID = -1 // InitialPreviousFailoverVersion is the initial previous failover version InitialPreviousFailoverVersion int64 = -1 ) const ( // EmptyUUID is the placeholder for UUID when it's empty EmptyUUID = "emptyUuid" ) // Data encoding types const ( EncodingTypeJSON EncodingType = "json" EncodingTypeThriftRW EncodingType = "thriftrw" EncodingTypeThriftRWSnappy EncodingType = "thriftrw_snappy" EncodingTypeGob EncodingType = "gob" EncodingTypeUnknown EncodingType = "unknow" EncodingTypeEmpty EncodingType = "" EncodingTypeProto EncodingType = "proto3" ) type ( // EncodingType is an enum that represents various data encoding types EncodingType string ) // MaxTaskTimeout is maximum task timeout allowed. 366 days in seconds const MaxTaskTimeout = 31622400 const ( // GetHistoryMaxPageSize is the max page size for get history GetHistoryMaxPageSize = 1000 // ReadDLQMessagesPageSize is the max page size for read DLQ messages ReadDLQMessagesPageSize = 1000 ) const ( // VisibilityAppName is used to find kafka topics and ES indexName for visibility VisibilityAppName = "visibility" PinotVisibilityAppName = "pinot-visibility" ) const ( // ESVisibilityStoreName is used to find es advanced visibility store ESVisibilityStoreName = "es-visibility" // PinotVisibilityStoreName is used to find pinot advanced visibility store PinotVisibilityStoreName = "pinot-visibility" // OSVisibilityStoreName is used to find opensearch advanced visibility store OSVisibilityStoreName = "os-visibility" ) // This was flagged by salus as potentially hardcoded credentials. This is a false positive by the scanner and should be // disregarded. // #nosec const ( // SystemGlobalDomainName is global domain name for cadence system workflows running globally SystemGlobalDomainName = "cadence-system-global" // SystemDomainID is domain id for all cadence system workflows SystemDomainID = "32049b68-7872-4094-8e63-d0dd59896a83" // SystemLocalDomainName is domain name for cadence system workflows running in local cluster SystemLocalDomainName = "cadence-system" // SystemDomainRetentionDays is retention config for all cadence system workflows SystemDomainRetentionDays = 7 // BatcherDomainID is domain id for batcher local domain BatcherDomainID = "3116607e-419b-4783-85fc-47726a4c3fe9" // BatcherLocalDomainName is domain name for batcher workflows running in local cluster // Batcher cannot use SystemLocalDomain because auth BatcherLocalDomainName = "cadence-batcher" // ShadowerDomainID is domain id for workflow shadower local domain ShadowerDomainID = "59c51119-1b41-4a28-986d-d6e377716f82" // ShadowerLocalDomainName ShadowerLocalDomainName = shadower.LocalDomainName ) const ( // MinLongPollTimeout is the minimum context timeout for long poll API, below which // the request won't be processed MinLongPollTimeout = time.Second * 2 // CriticalLongPollTimeout is a threshold for the context timeout passed into long poll API, // below which a warning will be logged CriticalLongPollTimeout = time.Second * 20 ) const ( // DefaultTransactionSizeLimit is the largest allowed transaction size to persistence DefaultTransactionSizeLimit = 14 * 1024 * 1024 ) const ( // DefaultIDLengthWarnLimit is the warning length for various ID types DefaultIDLengthWarnLimit = 128 // DefaultIDLengthErrorLimit is the maximum length allowed for various ID types DefaultIDLengthErrorLimit = 1000 ) const ( // ArchivalEnabled is the status for enabling archival ArchivalEnabled = "enabled" // ArchivalDisabled is the status for disabling archival ArchivalDisabled = "disabled" // ArchivalPaused is the status for pausing archival ArchivalPaused = "paused" ) // dynamic config AdvancedVisibility write/read mode const ( // AdvancedVisibilityModeOff means do not use advanced visibility store AdvancedVisibilityModeOff = "off" // AdvancedVisibilityModeES means ElasticSearch visibility mode VisibilityModeES = "es" // AdvancedVisibilityModePinot means Pinot visibility mode VisibilityModePinot = "pinot" // AdvancedVisibilityModeOS means OpenSearch visibility mode VisibilityModeOS = "os" // AdvancedVisibilityModeDB means db visibility mode VisibilityModeDB = "db" ) const ( ESPersistenceName = "elasticsearch" PinotPersistenceName = "pinot" DBPersistenceName = "db" ) const ( // DomainDataKeyForManagedFailover is key of DomainData for managed failover DomainDataKeyForManagedFailover = "IsManagedByCadence" // DomainDataKeyForPreferredCluster is the key of DomainData for domain rebalance DomainDataKeyForPreferredCluster = "PreferredCluster" // DomainDataKeyForFailoverHistory is the key of DomainData for failover history DomainDataKeyForFailoverHistory = "FailoverHistory" // DomainDataKeyForReadGroups stores which groups have read permission of the domain API DomainDataKeyForReadGroups = "READ_GROUPS" // DomainDataKeyForWriteGroups stores which groups have write permission of the domain API DomainDataKeyForWriteGroups = "WRITE_GROUPS" // DomainDataKeyForProcessGroups stores which groups have process permission of the domain API DomainDataKeyForProcessGroups = "PROCESS_GROUPS" ) type ( // TaskType is the enum for representing different task types TaskType int ) const ( // TaskTypeTransfer is the task type for transfer task // starting from 2 here to be consistent with the row type define for cassandra // TODO: we can remove +2 from the following definition // we don't have to make them consistent with cassandra definition // there's also no row type for sql or other nosql persistence implementation TaskTypeTransfer TaskType = iota + 2 // TaskTypeTimer is the task type for timer task TaskTypeTimer // TaskTypeReplication is the task type for replication task TaskTypeReplication // Deprecated: TaskTypeCrossCluster is the task type for cross cluster task // as of June 2024, this feature is no longer supported. Keeping the enum here // to avoid future reuse of the ID and/or confusion TaskTypeCrossCluster TaskType = 6 ) const ( // DefaultESAnalyzerPause controls if we want to dynamically pause the analyzer DefaultESAnalyzerPause = false // DefaultESAnalyzerTimeWindow controls how many days to go back for ElasticSearch Analyzer DefaultESAnalyzerTimeWindow = time.Hour * 24 * 30 // DefaultESAnalyzerMaxNumDomains controls how many domains to check DefaultESAnalyzerMaxNumDomains = 500 // DefaultESAnalyzerMaxNumWorkflowTypes controls how many workflow types per domain to check DefaultESAnalyzerMaxNumWorkflowTypes = 100 // DefaultESAnalyzerNumWorkflowsToRefresh controls how many workflows per workflow type should be refreshed DefaultESAnalyzerNumWorkflowsToRefresh = 100 // DefaultESAnalyzerBufferWaitTime controls min time required to consider a worklow stuck DefaultESAnalyzerBufferWaitTime = time.Minute * 30 // DefaultESAnalyzerMinNumWorkflowsForAvg controls how many workflows to have at least to rely on workflow run time avg per type DefaultESAnalyzerMinNumWorkflowsForAvg = 100 // DefaultESAnalyzerLimitToTypes controls if we want to limit ESAnalyzer only to some workflow types DefaultESAnalyzerLimitToTypes = "" // DefaultESAnalyzerEnableAvgDurationBasedChecks controls if we want to enable avg duration based refreshes DefaultESAnalyzerEnableAvgDurationBasedChecks = false // DefaultESAnalyzerLimitToDomains controls if we want to limit ESAnalyzer only to some domains DefaultESAnalyzerLimitToDomains = "" // DefaultESAnalyzerWorkflowDurationWarnThreshold defines warning threshold for a workflow duration DefaultESAnalyzerWorkflowDurationWarnThresholds = "" ) // StickyTaskConditionFailedErrorMsg error msg for sticky task ConditionFailedError const StickyTaskConditionFailedErrorMsg = "StickyTaskConditionFailedError" // MemoKeyForOperator is the memo key for operator const MemoKeyForOperator = "operator" // ReservedTaskListPrefix is the required naming prefix for any task list partition other than partition 0 const ReservedTaskListPrefix = "/__cadence_sys/" type ( // VisibilityOperation is an enum that represents visibility message types VisibilityOperation string ) // Enum for visibility message type const ( RecordStarted VisibilityOperation = "RecordStarted" RecordClosed VisibilityOperation = "RecordClosed" UpsertSearchAttributes VisibilityOperation = "UpsertSearchAttributes" ) const ( numBitsPerLevel = 3 ) const ( // HighPriorityClass is the priority class for high priority tasks HighPriorityClass = iota << numBitsPerLevel // DefaultPriorityClass is the priority class for default priority tasks DefaultPriorityClass // LowPriorityClass is the priority class for low priority tasks LowPriorityClass ) const ( // HighPrioritySubclass is the priority subclass for high priority tasks HighPrioritySubclass = iota // DefaultPrioritySubclass is the priority subclass for high priority tasks DefaultPrioritySubclass // LowPrioritySubclass is the priority subclass for high priority tasks LowPrioritySubclass ) const ( // DefaultHistoryMaxAutoResetPoints is the default maximum number for auto reset points DefaultHistoryMaxAutoResetPoints = 20 ) const ( // WorkflowIDRateLimitReason is the reason set in ServiceBusyError when workflow ID rate limit is exceeded WorkflowIDRateLimitReason = "external-workflow-id-rate-limit" ) type ( // FailoverType is the enum for representing different failover types FailoverType int ) const ( FailoverTypeForce FailoverType = iota + 1 FailoverTypeGrace ) func (v FailoverType) String() string { switch v { case FailoverTypeForce: return "Force" case FailoverTypeGrace: return "Grace" default: return "Unknown" } } const ( StringSizeOverheadBytes = 16 ) // GetTaskPriority returns priority given a task's priority class and subclass func GetTaskPriority( class int, subClass int, ) int { return class | subClass } // GRPCConnectionClosingError is the error message returned when a gRPC client connection is closing const GRPCConnectionClosingError = "grpc: the client connection is closing" ================================================ FILE: common/convert.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package common import ( "time" "go.uber.org/cadence/.gen/go/shared" s "github.com/uber/cadence/.gen/go/shared" ) // Returns a pointer to the given value. // todo (david.porter) Remove the remaining helpers here which are now obsolete func Ptr[T any](v T) *T { return &v } // IntPtr makes a copy and returns the pointer to an int. func IntPtr(v int) *int { return &v } // Int16Ptr makes a copy and returns the pointer to an int16. func Int16Ptr(v int16) *int16 { return &v } // Int32Ptr makes a copy and returns the pointer to an int32. func Int32Ptr(v int32) *int32 { return &v } // Int64Ptr makes a copy and returns the pointer to an int64. func Int64Ptr(v int64) *int64 { return &v } // Uint32Ptr makes a copy and returns the pointer to a uint32. func Uint32Ptr(v uint32) *uint32 { return &v } // Uint64Ptr makes a copy and returns the pointer to a uint64. func Uint64Ptr(v uint64) *uint64 { return &v } // Float64Ptr makes a copy and returns the pointer to an int64. func Float64Ptr(v float64) *float64 { return &v } // BoolPtr makes a copy and returns the pointer to a bool. func BoolPtr(v bool) *bool { return &v } // StringPtr makes a copy and returns the pointer to a string. func StringPtr(v string) *string { return &v } // TimeNowNanosPtr returns an int64 ptr to current time in unix nanos func TimeNowNanosPtr() *int64 { v := time.Now().UnixNano() return &v } // TimePtr makes a copy and returns the pointer to a time func TimePtr(v time.Time) *time.Time { return &v } // DurationPtr makes a copy and returns the pointer to a duration func DurationPtr(v time.Duration) *time.Duration { return &v } // TaskListPtr makes a copy and returns the pointer to a TaskList. func TaskListPtr(v s.TaskList) *s.TaskList { return &v } // ActivityTypePtr makes a copy and returns the pointer to a ActivityType. func ActivityTypePtr(v s.ActivityType) *s.ActivityType { return &v } // DecisionTypePtr makes a copy and returns the pointer to a DecisionType. func DecisionTypePtr(t s.DecisionType) *s.DecisionType { return &t } // ParentClosePolicyPtr makes a copy and returns the pointer to a DecisionType. func ParentClosePolicyPtr(t s.ParentClosePolicy) *s.ParentClosePolicy { return &t } // EventTypePtr makes a copy and returns the pointer to a EventType. func EventTypePtr(t s.EventType) *s.EventType { return &t } // WorkflowTypePtr makes a copy and returns the pointer to a WorkflowType. func WorkflowTypePtr(t s.WorkflowType) *s.WorkflowType { return &t } // TimeoutTypePtr makes a copy and returns the pointer to a TimeoutType. func TimeoutTypePtr(t s.TimeoutType) *s.TimeoutType { return &t } // TaskListKindPtr makes a copy and returns the pointer to a TaskListKind. func TaskListKindPtr(t s.TaskListKind) *s.TaskListKind { return &t } // TaskListTypePtr makes a copy and returns the pointer to a TaskListKind. func TaskListTypePtr(t s.TaskListType) *s.TaskListType { return &t } // DecisionTaskFailedCausePtr makes a copy and returns the pointer to a DecisionTaskFailedCause. func DecisionTaskFailedCausePtr(t s.DecisionTaskFailedCause) *s.DecisionTaskFailedCause { return &t } // CancelExternalWorkflowExecutionFailedCausePtr makes a copy and returns the pointer to a CancelExternalWorkflowExecutionFailedCause. func CancelExternalWorkflowExecutionFailedCausePtr(t s.CancelExternalWorkflowExecutionFailedCause) *s.CancelExternalWorkflowExecutionFailedCause { return &t } // SignalExternalWorkflowExecutionFailedCausePtr makes a copy and returns the pointer to a SignalExternalWorkflowExecutionFailedCause. func SignalExternalWorkflowExecutionFailedCausePtr(t s.SignalExternalWorkflowExecutionFailedCause) *s.SignalExternalWorkflowExecutionFailedCause { return &t } // ChildWorkflowExecutionFailedCausePtr makes a copy and returns the pointer to a ChildWorkflowExecutionFailedCause. func ChildWorkflowExecutionFailedCausePtr(t s.ChildWorkflowExecutionFailedCause) *s.ChildWorkflowExecutionFailedCause { return &t } // ArchivalStatusPtr makes a copy and returns the pointer to an ArchivalStatus. func ArchivalStatusPtr(t s.ArchivalStatus) *s.ArchivalStatus { return &t } // ClientArchivalStatusPtr makes a copy and returns the pointer to a client ArchivalStatus. func ClientArchivalStatusPtr(t shared.ArchivalStatus) *shared.ArchivalStatus { return &t } // QueryResultTypePtr makes a copy and returns the pointer to a QueryResultType func QueryResultTypePtr(t s.QueryResultType) *s.QueryResultType { return &t } // QueryRejectConditionPtr makes a copy and returns the pointer to a QueryRejectCondition func QueryRejectConditionPtr(t s.QueryRejectCondition) *s.QueryRejectCondition { return &t } // QueryConsistencyLevelPtr makes a copy and returns the pointer to a QueryConsistencyLevel func QueryConsistencyLevelPtr(t s.QueryConsistencyLevel) *s.QueryConsistencyLevel { return &t } // QueryTaskCompletedTypePtr makes a copy and returns the pointer to a QueryTaskCompletedType func QueryTaskCompletedTypePtr(t s.QueryTaskCompletedType) *s.QueryTaskCompletedType { return &t } // StringDefault returns value if string pointer is set otherwise default value of string func StringDefault(v *string) string { var defaultString string if v == nil { return defaultString } return *v } // Int32Default returns value if int32 pointer is set otherwise default value of int32 func Int32Default(v *int32) int32 { var defaultInt32 int32 if v == nil { return defaultInt32 } return *v } // Int64Default returns value if int64 pointer is set otherwise default value of int64 func Int64Default(v *int64) int64 { var defaultInt64 int64 if v == nil { return defaultInt64 } return *v } // BoolDefault returns value if bool pointer is set otherwise default value of bool func BoolDefault(v *bool) bool { var defaultBool bool if v == nil { return defaultBool } return *v } ================================================ FILE: common/convert_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package common import ( "testing" "github.com/stretchr/testify/assert" ) func TestPtr(t *testing.T) { aString := "a string" aStruct := struct{}{} anInterface := interface{}(nil) t.Run("a string should be converted to a pointer of a string", func(t *testing.T) { assert.Equal(t, &aString, Ptr(aString)) }) t.Run("a struct should be converted to a pointer of a string", func(t *testing.T) { assert.Equal(t, &aStruct, Ptr(aStruct)) }) t.Run("a interface should be converted to a pointer of a string", func(t *testing.T) { assert.Equal(t, &anInterface, Ptr(anInterface)) }) } ================================================ FILE: common/ctxutils/ctxutils.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ctxutils import ( "context" "sync" ) // WithPropagatedContextCancel returns a copy of parent which is cancelled whenever // it is cancelled itself (with the returned cancel func) or cancelCtx is cancelled // you need to call cancel func to avoid potential goroutine leak func WithPropagatedContextCancel(parent context.Context, cancelCtx context.Context) (context.Context, context.CancelFunc) { done := cancelCtx.Done() if done == nil { return parent, func() {} } childWithCancel, cancel := context.WithCancel(parent) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() select { case <-done: cancel() case <-childWithCancel.Done(): } }() return childWithCancel, func() { cancel() wg.Wait() } } ================================================ FILE: common/ctxutils/ctxutils_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ctxutils import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" ) func TestWithPropagatedContextCancel(t *testing.T) { defer goleak.VerifyNone(t) cancelCtx, cancelFn := context.WithCancel(context.Background()) ctx, stopper := WithPropagatedContextCancel(context.Background(), cancelCtx) defer stopper() cancelFn() // cancel should be propagated to "chained" ctx testTimeout := time.NewTimer(10 * time.Second) cancelledOriginalContext := false select { case <-ctx.Done(): cancelledOriginalContext = true case <-testTimeout.C: t.Error("test timed out") } require.True(t, cancelledOriginalContext) } func TestWithPropagatedContextCancelWorksWhenContextHasNoCancellation(t *testing.T) { assert.NotPanics(t, func() { WithPropagatedContextCancel(context.Background(), context.Background()) }) goleak.VerifyNone(t) } func TestWithPropagatedContextCancelWorksWhenParentContextIsCancelled(t *testing.T) { defer goleak.VerifyNone(t) cancelCtx1, cancelFn1 := context.WithCancel(context.Background()) cancelCtx2, _ := context.WithCancel(context.Background()) ctx, stopper := WithPropagatedContextCancel(cancelCtx1, cancelCtx2) defer stopper() // in this case there is no need to wait for the cancelCtx to cancel // since the parent context is closed before cancelFn1() testTimeout := time.NewTimer(10 * time.Second) select { case <-ctx.Done(): case <-testTimeout.C: t.Error("sanity check #1 - we expect ctx to be cancelled") } select { case <-cancelCtx1.Done(): case <-testTimeout.C: t.Error("sanity check #2 - we expect ctx to be cancelled") } } func TestWithPropagatedContextSanityCheckOriginalContextNeverAffected(t *testing.T) { defer goleak.VerifyNone(t) originalCtx, _ := context.WithCancel(context.Background()) cancelCtx, cancelFn := context.WithCancel(context.Background()) ctx, stopper := WithPropagatedContextCancel(originalCtx, cancelCtx) cancelFn() // cancel should be propagated to "chained" ctx stopper() testTimeout := time.NewTimer(10 * time.Second) // cancellation of ctx is expected select { case <-ctx.Done(): break case <-testTimeout.C: t.Error("test timed out") } // ... but cancellation of originalCtx is not // actually this is guaranteed by Go (contexts are immutable), but let's check anyway select { case <-originalCtx.Done(): t.Error("originalCtx should not be cancelled") default: } } ================================================ FILE: common/daemon.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package common const ( // used for background threads // DaemonStatusInitialized coroutine pool initialized DaemonStatusInitialized int32 = 0 // DaemonStatusStarted coroutine pool started DaemonStatusStarted int32 = 1 // DaemonStatusStopped coroutine pool stopped DaemonStatusStopped int32 = 2 ) type ( // Daemon is the base interfaces implemented by // background tasks within Cadence Daemon interface { Start() Stop() } ) ================================================ FILE: common/definition/indexedKeys.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package definition import "github.com/uber/cadence/common/types" // valid indexed fields on ES const ( DomainID = "DomainID" WorkflowID = "WorkflowID" RunID = "RunID" WorkflowType = "WorkflowType" StartTime = "StartTime" ExecutionTime = "ExecutionTime" CloseTime = "CloseTime" CloseStatus = "CloseStatus" HistoryLength = "HistoryLength" Encoding = "Encoding" KafkaKey = "KafkaKey" BinaryChecksums = "BinaryChecksums" TaskList = "TaskList" ClusterAttributeScope = "ClusterAttributeScope" ClusterAttributeName = "ClusterAttributeName" IsCron = "IsCron" NumClusters = "NumClusters" UpdateTime = "UpdateTime" CronSchedule = "CronSchedule" ExecutionStatus = "ExecutionStatus" ScheduledExecutionTime = "ScheduledExecutionTime" CustomDomain = "CustomDomain" // to support batch workflow Operator = "Operator" // to support batch workflow CustomStringField = "CustomStringField" CustomKeywordField = "CustomKeywordField" CustomIntField = "CustomIntField" CustomBoolField = "CustomBoolField" CustomDoubleField = "CustomDoubleField" CustomDatetimeField = "CustomDatetimeField" CadenceChangeVersion = "CadenceChangeVersion" ) const ( // Memo is valid non-indexed fields on ES Memo = "Memo" // Attr is prefix of custom search attributes Attr = "Attr" // HeaderFormat is the format of context headers in search attributes HeaderFormat = "Header_%s" ) // defaultIndexedKeys defines all searchable keys var defaultIndexedKeys = createDefaultIndexedKeys() func createDefaultIndexedKeys() map[string]interface{} { defaultIndexedKeys := map[string]interface{}{ CustomStringField: types.IndexedValueTypeString, CustomKeywordField: types.IndexedValueTypeKeyword, CustomIntField: types.IndexedValueTypeInt, CustomBoolField: types.IndexedValueTypeBool, CustomDoubleField: types.IndexedValueTypeDouble, CustomDatetimeField: types.IndexedValueTypeDatetime, CadenceChangeVersion: types.IndexedValueTypeKeyword, BinaryChecksums: types.IndexedValueTypeKeyword, CustomDomain: types.IndexedValueTypeString, Operator: types.IndexedValueTypeString, } for k, v := range systemIndexedKeys { defaultIndexedKeys[k] = v } return defaultIndexedKeys } // GetDefaultIndexedKeys return default valid indexed keys func GetDefaultIndexedKeys() map[string]interface{} { return defaultIndexedKeys } // systemIndexedKeys is Cadence created visibility keys var systemIndexedKeys = map[string]interface{}{ DomainID: types.IndexedValueTypeKeyword, WorkflowID: types.IndexedValueTypeKeyword, RunID: types.IndexedValueTypeKeyword, WorkflowType: types.IndexedValueTypeKeyword, StartTime: types.IndexedValueTypeInt, ExecutionTime: types.IndexedValueTypeInt, CloseTime: types.IndexedValueTypeInt, CloseStatus: types.IndexedValueTypeInt, HistoryLength: types.IndexedValueTypeInt, TaskList: types.IndexedValueTypeKeyword, IsCron: types.IndexedValueTypeBool, NumClusters: types.IndexedValueTypeInt, UpdateTime: types.IndexedValueTypeInt, CronSchedule: types.IndexedValueTypeKeyword, ExecutionStatus: types.IndexedValueTypeInt, ScheduledExecutionTime: types.IndexedValueTypeInt, ClusterAttributeScope: types.IndexedValueTypeKeyword, ClusterAttributeName: types.IndexedValueTypeKeyword, } // IsSystemIndexedKey return true is key is system added func IsSystemIndexedKey(key string) bool { _, ok := systemIndexedKeys[key] return ok } // IsSystemBoolKey return true is key is system added bool key func IsSystemBoolKey(key string) bool { return systemIndexedKeys[key] == types.IndexedValueTypeBool } ================================================ FILE: common/definition/indexedKeys_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package definition import ( "testing" "github.com/stretchr/testify/assert" ) func TestIsSystemBoolKey(t *testing.T) { tests := []struct { key string expected bool }{ {"IsCron", true}, {"StartTime", false}, } for _, test := range tests { actualResult := IsSystemBoolKey(test.key) assert.Equal(t, test.expected, actualResult) } } ================================================ FILE: common/definition/resourceDeduplication.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package definition import "fmt" const ( resourceIDTemplate = "%v::%v" eventReappliedIDTemplate = "%v::%v::%v" ) type ( // DeduplicationID uses to generate id for deduplication DeduplicationID interface { GetID() string } ) // Deduplication id type const ( eventReappliedID = iota ) // Deduplication resource struct type ( // EventReappliedID is the deduplication resource for reapply event EventReappliedID struct { id string } ) // NewEventReappliedID returns EventReappliedID resource func NewEventReappliedID( runID string, eventID int64, version int64, ) EventReappliedID { newID := fmt.Sprintf( eventReappliedIDTemplate, runID, eventID, version, ) return EventReappliedID{ id: newID, } } // GetID returns id of EventReappliedID func (e EventReappliedID) GetID() string { return e.id } // GenerateDeduplicationKey generates deduplication key func GenerateDeduplicationKey( resource DeduplicationID, ) string { switch resource.(type) { case EventReappliedID: return generateKey(eventReappliedID, resource.GetID()) default: panic("unsupported deduplication key") } } func generateKey(resourceType int32, id string) string { return fmt.Sprintf(resourceIDTemplate, resourceType, id) } ================================================ FILE: common/definition/resourceDeduplication_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package definition import ( "fmt" "testing" "github.com/stretchr/testify/suite" ) type ( resourceDeduplicationSuite struct { suite.Suite } ) func TestResourceDeduplicationSuite(t *testing.T) { s := new(resourceDeduplicationSuite) suite.Run(t, s) } func (s *resourceDeduplicationSuite) TestGenerateKey() { resourceType := int32(1) id := "id" key := generateKey(resourceType, id) s.Equal(fmt.Sprintf("%v::%v", resourceType, id), key) } func (s *resourceDeduplicationSuite) TestGenerateDeduplicationKey() { runID := "runID" eventID := int64(1) version := int64(2) resource := NewEventReappliedID(runID, eventID, version) key := GenerateDeduplicationKey(resource) s.Equal(fmt.Sprintf("%v::%v::%v::%v", eventReappliedID, runID, eventID, version), key) } func (s *resourceDeduplicationSuite) TestEventReappliedID() { runID := "runID" eventID := int64(1) version := int64(2) resource := NewEventReappliedID(runID, eventID, version) s.Equal(fmt.Sprintf("%v::%v::%v", runID, eventID, version), resource.GetID()) } ================================================ FILE: common/definition/workflowIdentifier.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package definition import ( "github.com/uber/cadence/common/constants" ) type ( // WorkflowIdentifier is the combinations which represent a workflow WorkflowIdentifier struct { DomainID string WorkflowID string RunID string } ) // Size calculates the size in bytes of the WorkflowIdentifier struct. func (wi *WorkflowIdentifier) ByteSize() uint64 { // Calculate the size of strings in bytes, we assume that all those fields are using ASCII which is 1 byte per char size := len(wi.DomainID) + len(wi.WorkflowID) + len(wi.RunID) // Each string internally holds a reference pointer and a length, which are 8 bytes each stringOverhead := 3 * constants.StringSizeOverheadBytes return uint64(size + stringOverhead) } // NewWorkflowIdentifier create a new WorkflowIdentifier func NewWorkflowIdentifier(domainID string, workflowID string, runID string) WorkflowIdentifier { return WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, } } ================================================ FILE: common/definition/workflowidentifier_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package definition import ( "testing" "github.com/stretchr/testify/assert" ) // TestWorkflowIdentifierSize verifies the Size method of WorkflowIdentifier. func TestWorkflowIdentifierSize(t *testing.T) { tests := []struct { name string wi WorkflowIdentifier expected uint64 }{ { name: "non-empty fields", wi: NewWorkflowIdentifier("domain", "workflow", "run"), expected: uint64(len("domain") + len("workflow") + len("run") + 3*16), }, { name: "empty fields", wi: NewWorkflowIdentifier("", "", ""), expected: uint64(3 * 16), }, { name: "short fields", wi: NewWorkflowIdentifier("a", "b", "c"), expected: uint64(3 + 3*16), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { size := test.wi.ByteSize() assert.Equal(t, test.expected, size) }) } } ================================================ FILE: common/domain/archivalConfigStateMachine.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import "github.com/uber/cadence/common/types" // domainArchivalConfigStateMachine is only used by domainHandler. // It is simply meant to simplify the logic around archival domain state changes. // Logically this class can be thought of as part of domainHandler. type ( // ArchivalState represents the state of archival config // the only invalid state is {URI="", status=enabled} // once URI is set it is immutable ArchivalState struct { Status types.ArchivalStatus URI string } // ArchivalEvent represents a change request to archival config state // the only restriction placed on events is that defaultURI is not empty // status can be nil, enabled, or disabled (nil indicates no update by user is being attempted) ArchivalEvent struct { defaultURI string URI string status *types.ArchivalStatus } ) // the following errors represents impossible code states that should never occur var ( errInvalidState = &types.BadRequestError{Message: "Encountered illegal state: archival is enabled but URI is not set (should be impossible)"} errInvalidEvent = &types.BadRequestError{Message: "Encountered illegal event: default URI is not set (should be impossible)"} errCannotHandleStateChange = &types.BadRequestError{Message: "Encountered current state and event that cannot be handled (should be impossible)"} errURIUpdate = &types.BadRequestError{Message: "Cannot update existing archival URI"} ) func neverEnabledState() *ArchivalState { return &ArchivalState{ URI: "", Status: types.ArchivalStatusDisabled, } } func (e *ArchivalEvent) validate() error { if len(e.defaultURI) == 0 { return errInvalidEvent } return nil } func (s *ArchivalState) validate() error { if s.Status == types.ArchivalStatusEnabled && len(s.URI) == 0 { return errInvalidState } return nil } func (s *ArchivalState) getNextState( e *ArchivalEvent, URIValidationFunc func(URI string) error, ) (nextState *ArchivalState, changed bool, err error) { defer func() { // ensure that any existing URI name was not mutated if nextState != nil && len(s.URI) != 0 && s.URI != nextState.URI { nextState = nil changed = false err = errCannotHandleStateChange return } // ensure that next state is valid if nextState != nil { if nextStateErr := nextState.validate(); nextStateErr != nil { nextState = nil changed = false err = nextStateErr return } } if nextState != nil && nextState.URI != "" { if validateURIErr := URIValidationFunc(nextState.URI); validateURIErr != nil { nextState = nil changed = false err = validateURIErr return } } }() if s == nil || e == nil { return nil, false, errCannotHandleStateChange } if err := s.validate(); err != nil { return nil, false, err } if err := e.validate(); err != nil { return nil, false, err } /** At this point state and event are both non-nil and valid. State can be any one of the following: {status=enabled, URI="foo"} {status=disabled, URI="foo"} {status=disabled, URI=""} Event can be any one of the following: {status=enabled, URI="foo", defaultURI="bar"} {status=enabled, URI="", defaultURI="bar"} {status=disabled, URI="foo", defaultURI="bar"} {status=disabled, URI="", defaultURI="bar"} {status=nil, URI="foo", defaultURI="bar"} {status=nil, URI="", defaultURI="bar"} */ stateURISet := len(s.URI) != 0 eventURISet := len(e.URI) != 0 // factor this case out to ensure that URI is immutable if stateURISet && eventURISet && s.URI != e.URI { return nil, false, errURIUpdate } // state 1 if s.Status == types.ArchivalStatusEnabled && stateURISet { if e.status != nil && *e.status == types.ArchivalStatusEnabled && eventURISet { return s, false, nil } if e.status != nil && *e.status == types.ArchivalStatusEnabled && !eventURISet { return s, false, nil } if e.status != nil && *e.status == types.ArchivalStatusDisabled && eventURISet { return &ArchivalState{ Status: types.ArchivalStatusDisabled, URI: s.URI, }, true, nil } if e.status != nil && *e.status == types.ArchivalStatusDisabled && !eventURISet { return &ArchivalState{ Status: types.ArchivalStatusDisabled, URI: s.URI, }, true, nil } if e.status == nil && eventURISet { return s, false, nil } if e.status == nil && !eventURISet { return s, false, nil } } // state 2 if s.Status == types.ArchivalStatusDisabled && stateURISet { if e.status != nil && *e.status == types.ArchivalStatusEnabled && eventURISet { return &ArchivalState{ URI: s.URI, Status: types.ArchivalStatusEnabled, }, true, nil } if e.status != nil && *e.status == types.ArchivalStatusEnabled && !eventURISet { return &ArchivalState{ Status: types.ArchivalStatusEnabled, URI: s.URI, }, true, nil } if e.status != nil && *e.status == types.ArchivalStatusDisabled && eventURISet { return s, false, nil } if e.status != nil && *e.status == types.ArchivalStatusDisabled && !eventURISet { return s, false, nil } if e.status == nil && eventURISet { return s, false, nil } if e.status == nil && !eventURISet { return s, false, nil } } // state 3 if s.Status == types.ArchivalStatusDisabled && !stateURISet { if e.status != nil && *e.status == types.ArchivalStatusEnabled && eventURISet { return &ArchivalState{ Status: types.ArchivalStatusEnabled, URI: e.URI, }, true, nil } if e.status != nil && *e.status == types.ArchivalStatusEnabled && !eventURISet { return &ArchivalState{ Status: types.ArchivalStatusEnabled, URI: e.defaultURI, }, true, nil } if e.status != nil && *e.status == types.ArchivalStatusDisabled && eventURISet { return &ArchivalState{ Status: types.ArchivalStatusDisabled, URI: e.URI, }, true, nil } if e.status != nil && *e.status == types.ArchivalStatusDisabled && !eventURISet { return s, false, nil } if e.status == nil && eventURISet { return &ArchivalState{ Status: types.ArchivalStatusDisabled, URI: e.URI, }, true, nil } if e.status == nil && !eventURISet { return s, false, nil } } return nil, false, errCannotHandleStateChange } ================================================ FILE: common/domain/archivalConfigStateMachine_test.go ================================================ // Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" ) func TestArchivalStateGetNextState(t *testing.T) { enabled := types.ArchivalStatusEnabled disabled := types.ArchivalStatusDisabled // Mock URI validation function that always succeeds successfulValidation := func(URI string) error { return nil } tests := []struct { name string currentState *ArchivalState event *ArchivalEvent validateURI func(string) error expectedState *ArchivalState expectedChanged bool expectedError error }{ { name: "Enable archival with valid URI, no change", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", URI: "http://existing-uri.com", status: &enabled, }, validateURI: successfulValidation, expectedState: &ArchivalState{Status: enabled, URI: "http://existing-uri.com"}, expectedChanged: false, expectedError: nil, }, { name: "Enable archival without setting a URI - triggers validation error", currentState: &ArchivalState{ Status: disabled, URI: "", }, event: &ArchivalEvent{ defaultURI: "", URI: "", status: &enabled, }, validateURI: successfulValidation, expectedState: nil, expectedChanged: false, expectedError: errInvalidEvent, // Expecting error due to enabling archival without a URI }, { name: "Archival disabled with URI, enabling with same URI", currentState: &ArchivalState{ Status: disabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", URI: "http://existing-uri.com", status: &enabled, }, validateURI: successfulValidation, expectedState: &ArchivalState{Status: enabled, URI: "http://existing-uri.com"}, expectedChanged: true, expectedError: nil, }, { name: "Archival disabled without URI, enabling with default URI", currentState: &ArchivalState{ Status: disabled, URI: "", }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", status: &enabled, }, validateURI: successfulValidation, expectedState: &ArchivalState{Status: enabled, URI: "http://default-uri.com"}, expectedChanged: true, expectedError: nil, }, { name: "Event with empty defaultURI", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ defaultURI: "", status: &enabled, }, validateURI: successfulValidation, expectedState: nil, expectedChanged: false, expectedError: errInvalidEvent, }, { name: "Immutable URI change attempt", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", URI: "http://new-uri.com", status: &enabled, }, validateURI: successfulValidation, expectedState: nil, expectedChanged: false, expectedError: errURIUpdate, }, { name: "Current state is nil", currentState: nil, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", status: &enabled, }, validateURI: successfulValidation, expectedState: nil, expectedChanged: false, expectedError: errCannotHandleStateChange, }, { name: "Event is nil", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: nil, validateURI: successfulValidation, expectedState: nil, expectedChanged: false, expectedError: errCannotHandleStateChange, }, { name: "State and event nil", currentState: nil, event: nil, validateURI: successfulValidation, expectedState: nil, expectedChanged: false, expectedError: errCannotHandleStateChange, }, { name: "Unrecognized ArchivalStatus in event", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", status: types.ArchivalStatus(999).Ptr(), // Assuming 999 is an unrecognized status }, validateURI: successfulValidation, expectedState: nil, expectedChanged: false, expectedError: errCannotHandleStateChange, }, { name: "Starting from an invalid state configuration", currentState: &ArchivalState{ Status: enabled, URI: "", // Invalid state: enabled but no URI }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", status: &enabled, }, validateURI: successfulValidation, expectedState: nil, expectedChanged: false, expectedError: errInvalidState, }, { name: "URI validation failure after passing immutability check", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ status: &enabled, URI: "http://existing-uri.com", defaultURI: "http://default-uri.com", }, validateURI: func(URI string) error { // Simulating a validation failure for any URI, focusing on triggering validation logic inside defer return &types.BadRequestError{Message: "URI validation failed due to invalid format"} }, expectedState: nil, // Expecting nil as the validation failure should prevent state transition expectedChanged: false, // Expecting false as no change should occur due to validation failure expectedError: &types.BadRequestError{Message: "URI validation failed due to invalid format"}, // Specific error from URI validation }, { name: "Trigger defer logic without URI change", currentState: &ArchivalState{ Status: types.ArchivalStatusEnabled, URI: "http://existing-uri.com", // Keeping URI same to avoid errURIUpdate. }, event: &ArchivalEvent{ status: &enabled, URI: "http://existing-uri.com", defaultURI: "http://default-uri.com", }, validateURI: func(URI string) error { return errCannotHandleStateChange }, expectedState: nil, // Adjusting expectation based on understanding of defer logic. expectedChanged: false, expectedError: errCannotHandleStateChange, // Expecting this validation failure. }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { nextState, changed, err := tt.currentState.getNextState(tt.event, tt.validateURI) assert.Equal(t, tt.expectedState, nextState) assert.Equal(t, tt.expectedChanged, changed) assert.Equal(t, tt.expectedError, err) }) } } func TestNeverEnabledState(t *testing.T) { expectedState := &ArchivalState{ URI: "", Status: types.ArchivalStatusDisabled, } actualState := neverEnabledState() assert.Equal(t, expectedState, actualState, "The returned state does not match the expected never enabled state") } func TestArchivalStateGetNextState_State1Scenarios(t *testing.T) { enabled := types.ArchivalStatusEnabled disabled := types.ArchivalStatusDisabled // Mock URI validation function that always succeeds successfulValidation := func(URI string) error { return nil } tests := []struct { name string currentState *ArchivalState event *ArchivalEvent validateURI func(string) error expectedState *ArchivalState expectedChanged bool expectedError error }{ { name: "Enabled status without event URI set - no change", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", status: &enabled, }, validateURI: successfulValidation, expectedState: &ArchivalState{Status: enabled, URI: "http://existing-uri.com"}, expectedChanged: false, expectedError: nil, }, { name: "Disabled status with event URI set - state change", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", URI: "http://existing-uri.com", status: &disabled, }, validateURI: successfulValidation, expectedState: &ArchivalState{Status: disabled, URI: "http://existing-uri.com"}, expectedChanged: true, expectedError: nil, }, { name: "Disabled status without event URI set - state change", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", status: &disabled, }, validateURI: successfulValidation, expectedState: &ArchivalState{Status: disabled, URI: "http://existing-uri.com"}, expectedChanged: true, expectedError: nil, }, { name: "No status with event URI set - no change", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", URI: "http://existing-uri.com", // URI set but no status change attempted. }, validateURI: successfulValidation, expectedState: &ArchivalState{Status: enabled, URI: "http://existing-uri.com"}, expectedChanged: false, expectedError: nil, }, { name: "No status without event URI set - no change", currentState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, event: &ArchivalEvent{ defaultURI: "http://default-uri.com", // No URI and no status change attempted. }, validateURI: successfulValidation, expectedState: &ArchivalState{Status: enabled, URI: "http://existing-uri.com"}, expectedChanged: false, expectedError: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { nextState, changed, err := tt.currentState.getNextState(tt.event, tt.validateURI) assert.Equal(t, tt.expectedState, nextState) assert.Equal(t, tt.expectedChanged, changed) assert.Equal(t, tt.expectedError, err) }) } } func TestArchivalStateGetNextState_State2Scenarios(t *testing.T) { disabled := types.ArchivalStatusDisabled enabled := types.ArchivalStatusEnabled successfulValidation := func(URI string) error { return nil } currentStateWithURI := &ArchivalState{ Status: disabled, URI: "http://existing-uri.com", } tests := []struct { name string event *ArchivalEvent validateURI func(string) error expectedState *ArchivalState expectedChanged bool expectedError error }{ { name: "Enable archival without event URI - state change to enabled with existing URI", event: &ArchivalEvent{ status: &enabled, defaultURI: "http://default-uri.com", }, validateURI: successfulValidation, expectedState: &ArchivalState{ Status: enabled, URI: "http://existing-uri.com", }, expectedChanged: true, expectedError: nil, }, { name: "Disable archival with event URI set - no change", event: &ArchivalEvent{ status: &disabled, URI: "http://existing-uri.com", defaultURI: "http://default-uri.com", }, validateURI: successfulValidation, expectedState: currentStateWithURI, expectedChanged: false, expectedError: nil, }, { name: "Disable archival without event URI - no change", event: &ArchivalEvent{ status: &disabled, defaultURI: "http://default-uri.com", }, validateURI: successfulValidation, expectedState: currentStateWithURI, expectedChanged: false, expectedError: nil, }, { name: "Event URI set without status change - no change", event: &ArchivalEvent{ URI: "http://existing-uri.com", defaultURI: "http://default-uri.com", }, validateURI: successfulValidation, expectedState: currentStateWithURI, expectedChanged: false, expectedError: nil, }, { name: "No event URI or status change - no change", event: &ArchivalEvent{ defaultURI: "http://default-uri.com", }, validateURI: successfulValidation, expectedState: currentStateWithURI, expectedChanged: false, expectedError: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { nextState, changed, err := currentStateWithURI.getNextState(tt.event, tt.validateURI) assert.Equal(t, tt.expectedState, nextState) assert.Equal(t, tt.expectedChanged, changed) assert.Equal(t, tt.expectedError, err) }) } } ================================================ FILE: common/domain/attrValidator.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "fmt" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // AttrValidatorImpl is domain attr validator AttrValidatorImpl struct { clusterMetadata cluster.Metadata minRetentionDays int32 } ) // newAttrValidator create a new domain attr validator func newAttrValidator( clusterMetadata cluster.Metadata, minRetentionDays int32, ) *AttrValidatorImpl { return &AttrValidatorImpl{ clusterMetadata: clusterMetadata, minRetentionDays: minRetentionDays, } } func (d *AttrValidatorImpl) validateLocalDomainUpdateRequest(updateRequest *types.UpdateDomainRequest) error { if updateRequest.ActiveClusterName != nil || updateRequest.ActiveClusters != nil || updateRequest.Clusters != nil { return errLocalDomainsCannotFailover } if updateRequest.FailoverTimeoutInSeconds != nil { return errLocalDomainsCannotFailover } return nil } func (d *AttrValidatorImpl) validateDomainConfig(config *persistence.DomainConfig) error { if config.Retention < int32(d.minRetentionDays) { return errInvalidRetentionPeriod } if config.HistoryArchivalStatus == types.ArchivalStatusEnabled && len(config.HistoryArchivalURI) == 0 { return errInvalidArchivalConfig } if config.VisibilityArchivalStatus == types.ArchivalStatusEnabled && len(config.VisibilityArchivalURI) == 0 { return errInvalidArchivalConfig } return nil } func (d *AttrValidatorImpl) validateDomainReplicationConfigForLocalDomain( replicationConfig *persistence.DomainReplicationConfig, ) error { activeCluster := replicationConfig.ActiveClusterName clusters := replicationConfig.Clusters if err := d.validateClusterName(activeCluster); err != nil { return err } for _, clusterConfig := range clusters { if err := d.validateClusterName(clusterConfig.ClusterName); err != nil { return err } } if activeCluster != d.clusterMetadata.GetCurrentClusterName() { return &types.BadRequestError{Message: "Invalid local domain active cluster"} } if len(clusters) != 1 || clusters[0].ClusterName != activeCluster { return &types.BadRequestError{Message: "Invalid local domain clusters"} } return nil } func (d *AttrValidatorImpl) validateDomainReplicationConfigForGlobalDomain( replicationConfig *persistence.DomainReplicationConfig, ) error { // TODO: https://github.com/uber/cadence/issues/4345 add checking for "pending active" as well // Right now we only have checking if clusters to remove are "current active cluster" in this method. // However, there could be edge cases that a cluster is in "pending active" state during graceful failover. // It's better to do this check so that people won't make mistake. // However, this is not critical -- even this happens, they can add the active cluster back activeCluster := replicationConfig.ActiveClusterName clusters := replicationConfig.Clusters activeClusters := replicationConfig.ActiveClusters if activeCluster == "" { return errActiveClusterNameRequired } for _, clusterConfig := range clusters { if err := d.validateClusterName(clusterConfig.ClusterName); err != nil { return err } } isInClusters := func(clusterName string) bool { for _, clusterConfig := range clusters { if clusterConfig.ClusterName == clusterName { return true } } return false } if err := d.validateClusterName(activeCluster); err != nil { return err } if !isInClusters(activeCluster) { return errActiveClusterNotInClusters } // For active-active domains, also validate that all clusters in AttributeScopes are valid if activeClusters != nil && activeClusters.AttributeScopes != nil { for _, scope := range activeClusters.AttributeScopes { for _, cluster := range scope.ClusterAttributes { if err := d.validateClusterName(cluster.ActiveClusterName); err != nil { return err } if !isInClusters(cluster.ActiveClusterName) { return errActiveClusterNotInClusters } } } } return nil } func (d *AttrValidatorImpl) validateDomainReplicationConfigClustersDoesNotRemove( clustersOld []*persistence.ClusterReplicationConfig, clustersNew []*persistence.ClusterReplicationConfig, ) error { clusterNamesOld := make(map[string]bool) for _, clusterConfig := range clustersOld { clusterNamesOld[clusterConfig.ClusterName] = true } clusterNamesNew := make(map[string]bool) for _, clusterConfig := range clustersNew { clusterNamesNew[clusterConfig.ClusterName] = true } if len(clusterNamesNew) < len(clusterNamesOld) { return errCannotRemoveClustersFromDomain } for clusterName := range clusterNamesOld { if _, ok := clusterNamesNew[clusterName]; !ok { return errCannotRemoveClustersFromDomain } } return nil } func (d *AttrValidatorImpl) validateClusterName( clusterName string, ) error { if _, ok := d.clusterMetadata.GetEnabledClusterInfo()[clusterName]; !ok { return &types.BadRequestError{Message: fmt.Sprintf( "Invalid cluster name: %v", clusterName, )} } return nil } func (d *AttrValidatorImpl) validateActiveActiveDomainReplicationConfig( activeClusters *types.ActiveClusters, ) error { if activeClusters == nil || activeClusters.AttributeScopes == nil { return nil } clusters := d.clusterMetadata.GetEnabledClusterInfo() for _, scopeData := range activeClusters.AttributeScopes { for _, activeCluster := range scopeData.ClusterAttributes { _, ok := clusters[activeCluster.ActiveClusterName] if !ok { return &types.BadRequestError{Message: fmt.Sprintf( "Invalid active cluster name: %v", activeCluster.ActiveClusterName, )} } if activeCluster.FailoverVersion < 0 { return &types.BadRequestError{Message: fmt.Sprintf( "invalid failover version: %d", activeCluster.FailoverVersion, )} } } } return nil } ================================================ FILE: common/domain/attrValidator_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( attrValidatorSuite struct { suite.Suite minRetentionDays int validator *AttrValidatorImpl } ) func TestAttrValidatorSuite(t *testing.T) { s := new(attrValidatorSuite) suite.Run(t, s) } func (s *attrValidatorSuite) SetupSuite() { } func (s *attrValidatorSuite) TearDownSuite() { } func (s *attrValidatorSuite) SetupTest() { s.minRetentionDays = 1 s.validator = newAttrValidator(cluster.TestActiveClusterMetadata, int32(s.minRetentionDays)) } func (s *attrValidatorSuite) TearDownTest() { } func (s *attrValidatorSuite) TestValidateDomainConfig() { testCases := []struct { testDesc string retention int32 historyArchival types.ArchivalStatus historyURI string visibilityArchival types.ArchivalStatus visibilityURI string expectedErr error }{ { testDesc: "valid retention and archival settings", retention: int32(s.minRetentionDays + 1), historyArchival: types.ArchivalStatusEnabled, historyURI: "testScheme://history", visibilityArchival: types.ArchivalStatusEnabled, visibilityURI: "testScheme://visibility", expectedErr: nil, }, { testDesc: "invalid retention period", retention: int32(s.minRetentionDays - 1), expectedErr: errInvalidRetentionPeriod, }, { testDesc: "enabled history archival without URI", retention: int32(s.minRetentionDays + 1), historyArchival: types.ArchivalStatusEnabled, expectedErr: errInvalidArchivalConfig, }, { testDesc: "enabled visibility archival without URI", retention: int32(s.minRetentionDays + 1), visibilityArchival: types.ArchivalStatusEnabled, expectedErr: errInvalidArchivalConfig, }, } for _, tc := range testCases { s.Run(tc.testDesc, func() { err := s.validator.validateDomainConfig(&persistence.DomainConfig{ Retention: tc.retention, HistoryArchivalStatus: tc.historyArchival, HistoryArchivalURI: tc.historyURI, VisibilityArchivalStatus: tc.visibilityArchival, VisibilityArchivalURI: tc.visibilityURI, }) if tc.expectedErr != nil { s.ErrorIs(err, tc.expectedErr) } else { s.NoError(err) } }) } } func (s *attrValidatorSuite) TestValidateConfigRetentionPeriod() { testCases := []struct { retentionPeriod int32 expectedErr error }{ { retentionPeriod: 10, expectedErr: nil, }, { retentionPeriod: 0, expectedErr: errInvalidRetentionPeriod, }, { retentionPeriod: -3, expectedErr: errInvalidRetentionPeriod, }, } for _, tc := range testCases { actualErr := s.validator.validateDomainConfig( &persistence.DomainConfig{Retention: tc.retentionPeriod}, ) s.Equal(tc.expectedErr, actualErr) } } func (s *attrValidatorSuite) TestClusterName() { err := s.validator.validateClusterName("some random foo bar") s.IsType(&types.BadRequestError{}, err) err = s.validator.validateClusterName(cluster.TestCurrentClusterName) s.NoError(err) err = s.validator.validateClusterName(cluster.TestAlternativeClusterName) s.NoError(err) } func (s *attrValidatorSuite) TestValidateDomainReplicationConfigForLocalDomain() { err := s.validator.validateDomainReplicationConfigForLocalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestAlternativeClusterName}, }, }, ) s.IsType(&types.BadRequestError{}, err) err = s.validator.validateDomainReplicationConfigForLocalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, ) s.IsType(&types.BadRequestError{}, err) err = s.validator.validateDomainReplicationConfigForLocalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, ) s.IsType(&types.BadRequestError{}, err) err = s.validator.validateDomainReplicationConfigForLocalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, }, }, ) s.NoError(err) } func (s *attrValidatorSuite) TestValidateDomainReplicationConfigForGlobalDomain() { err := s.validator.validateDomainReplicationConfigForGlobalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, }, }, ) s.NoError(err) err = s.validator.validateDomainReplicationConfigForGlobalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestAlternativeClusterName}, }, }, ) s.NoError(err) err = s.validator.validateDomainReplicationConfigForGlobalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, ) s.NoError(err) err = s.validator.validateDomainReplicationConfigForGlobalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, ) s.NoError(err) // When ActiveClusterName is not provided, and ActiveClusters are not provided, it should return an error err = s.validator.validateDomainReplicationConfigForGlobalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: "", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, }, }, ) s.Error(err) s.IsType(&types.BadRequestError{}, err) // When ActiveClusterName and ActiveClusters are provided, it should not return an error err = s.validator.validateDomainReplicationConfigForGlobalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: {ActiveClusterName: cluster.TestCurrentClusterName}, cluster.TestRegion2: {ActiveClusterName: cluster.TestAlternativeClusterName}, }, }, }, }, }, ) s.NoError(err) // When ActiveClusterName is not provided, and ActiveClusters are provided, it should return an error err = s.validator.validateDomainReplicationConfigForGlobalDomain( &persistence.DomainReplicationConfig{ ActiveClusterName: "", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: {ActiveClusterName: cluster.TestCurrentClusterName}, cluster.TestRegion2: {ActiveClusterName: cluster.TestAlternativeClusterName}, }, }, }, }, }, ) s.Error(err) s.IsType(&types.BadRequestError{}, err) } func (s *attrValidatorSuite) TestValidateDomainReplicationConfigClustersDoesNotRemove() { err := s.validator.validateDomainReplicationConfigClustersDoesNotRemove( []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ) s.NoError(err) err = s.validator.validateDomainReplicationConfigClustersDoesNotRemove( []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, }, []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ) s.NoError(err) err = s.validator.validateDomainReplicationConfigClustersDoesNotRemove( []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestAlternativeClusterName}, }, ) s.IsType(&types.BadRequestError{}, err) err = s.validator.validateDomainReplicationConfigClustersDoesNotRemove( []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, }, []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestAlternativeClusterName}, }, ) s.IsType(&types.BadRequestError{}, err) } func TestValidateActiveActiveDomainReplicationConfig(t *testing.T) { // Define test cluster names const ( clusterA = "cluster-a" clusterB = "cluster-b" ) // Create explicit cluster metadata for the test clusterMetadata := cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 10, PrimaryClusterName: clusterA, CurrentClusterName: clusterA, ClusterGroup: map[string]config.ClusterInformation{ clusterA: { Enabled: true, InitialFailoverVersion: 1, }, clusterB: { Enabled: true, InitialFailoverVersion: 2, }, }, }, func(d string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), ) validator := newAttrValidator(clusterMetadata, 1) testCases := []struct { name string activeClusters *types.ActiveClusters expectedErr bool errType interface{} }{ { name: "invalid cluster in AttributeScopes", activeClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "city": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "seattle": { ActiveClusterName: "invalid-cluster", FailoverVersion: 100, }, }, }, }, }, expectedErr: true, errType: &types.BadRequestError{}, }, { name: "empty ActiveClusters - all maps nil", activeClusters: &types.ActiveClusters{ AttributeScopes: nil, }, expectedErr: false, }, { name: "empty ActiveClusters - maps initialized but empty", activeClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{}, }, expectedErr: false, }, { name: "an invalid failover version should return an error", activeClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "city": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "seattle": { ActiveClusterName: clusterA, FailoverVersion: -1, }, }, }, }, }, expectedErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := validator.validateActiveActiveDomainReplicationConfig(tc.activeClusters) if tc.expectedErr { assert.Error(t, err) if tc.errType != nil { assert.IsType(t, tc.errType, err) } } else { assert.NoError(t, err) } }) } } ================================================ FILE: common/domain/audit/audit.go ================================================ package audit ================================================ FILE: common/domain/clusterattributes.go ================================================ package domain import ( "github.com/uber/cadence/common/types" ) func mergeActiveActiveScopes(existingDomain *types.ActiveClusters, incomingTask *types.ActiveClusters) (result *types.ActiveClusters, isChanged bool) { if existingDomain == nil && incomingTask == nil { return nil, false } // new active clusters are being added if existingDomain == nil && incomingTask != nil { return incomingTask, true } // existing data is not being mutated if incomingTask == nil && existingDomain != nil { return existingDomain, false } mergedActiveClusters := &types.ActiveClusters{ AttributeScopes: make(map[string]types.ClusterAttributeScope), } for scope, existingScopeData := range existingDomain.AttributeScopes { incomingScope, presentInIncoming := incomingTask.AttributeScopes[scope] if presentInIncoming { merged, isCh := mergeScope(existingScopeData, incomingScope) isChanged = isChanged || isCh mergedActiveClusters.AttributeScopes[scope] = merged } else { mergedActiveClusters.AttributeScopes[scope] = existingScopeData } } for newScope, newScopeData := range incomingTask.AttributeScopes { _, existsAlready := mergedActiveClusters.AttributeScopes[newScope] if !existsAlready { mergedActiveClusters.AttributeScopes[newScope] = newScopeData isChanged = true } } return mergedActiveClusters, isChanged } func mergeScope(existing types.ClusterAttributeScope, incoming types.ClusterAttributeScope) (merged types.ClusterAttributeScope, isChanged bool) { merged.ClusterAttributes = make(map[string]types.ActiveClusterInfo) for existingAttr := range existing.ClusterAttributes { incomingAtt, presentInNew := incoming.ClusterAttributes[existingAttr] if presentInNew { if incomingAtt.FailoverVersion > existing.ClusterAttributes[existingAttr].FailoverVersion { merged.ClusterAttributes[existingAttr] = incomingAtt isChanged = true } else { merged.ClusterAttributes[existingAttr] = existing.ClusterAttributes[existingAttr] } } else { merged.ClusterAttributes[existingAttr] = existing.ClusterAttributes[existingAttr] } } for newAttr, newAttrClusterInfo := range incoming.ClusterAttributes { _, existsInOld := existing.ClusterAttributes[newAttr] if !existsInOld { merged.ClusterAttributes[newAttr] = newAttrClusterInfo isChanged = true } } return merged, isChanged } ================================================ FILE: common/domain/clusterattributes_test.go ================================================ package domain import ( "context" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/mocks" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestDomainTask_ActiveActiveMerge(t *testing.T) { tests := map[string]struct { domainOperation types.DomainOperation isGlobalDomain bool activeClusters *types.ActiveClusters expectPublish bool expectedActiveClusters *types.ActiveClusters }{ "active-active domain with location and region scopes": { domainOperation: types.DomainOperationUpdate, isGlobalDomain: true, activeClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "Denver": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "Maarstricht": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "Sao Paulo": {ActiveClusterName: "clusterB", FailoverVersion: 1}, }, }, "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "us-west-1": {ActiveClusterName: "clusterB", FailoverVersion: 1}, }, }, }, }, expectPublish: true, expectedActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "Denver": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "Maarstricht": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "Sao Paulo": {ActiveClusterName: "clusterB", FailoverVersion: 1}, }, }, "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "us-west-1": {ActiveClusterName: "clusterB", FailoverVersion: 1}, }, }, }, }, }, "active-active domain create operation": { domainOperation: types.DomainOperationCreate, isGlobalDomain: true, activeClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "Denver": {ActiveClusterName: "clusterB", FailoverVersion: 1}, }, }, }, }, expectPublish: true, expectedActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "Denver": {ActiveClusterName: "clusterB", FailoverVersion: 1}, }, }, }, }, }, "non-global domain should not publish": { domainOperation: types.DomainOperationUpdate, isGlobalDomain: false, activeClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 0}, }, }, }, }, expectPublish: false, }, "nil active clusters": { domainOperation: types.DomainOperationUpdate, isGlobalDomain: true, activeClusters: nil, expectPublish: true, expectedActiveClusters: nil, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { kafkaProducer := &mocks.KafkaProducer{} domainReplicator := NewDomainReplicator( kafkaProducer, testlogger.New(t), ).(*domainReplicatorImpl) // Setup test data taskType := types.ReplicationTaskTypeDomain id := uuid.New() domainName := "test-active-active-domain" status := types.DomainStatusRegistered description := "test domain" ownerEmail := "test@example.com" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "test-history-uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "test-visibility-uri" clusterActive := "clusterA" clusterStandby := "clusterB" configVersion := int64(5) failoverVersion := int64(100) previousFailoverVersion := int64(50) clusters := []*p.ClusterReplicationConfig{ {ClusterName: clusterActive}, {ClusterName: clusterStandby}, } info := &p.DomainInfo{ ID: id, Name: domainName, Status: p.DomainStatusRegistered, Description: description, OwnerEmail: ownerEmail, Data: data, } config := &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{}, } replicationConfig := &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, ActiveClusters: tc.activeClusters, } if tc.expectPublish { kafkaProducer.On("Publish", mock.Anything, &types.ReplicationTask{ TaskType: &taskType, DomainTaskAttributes: &types.DomainTaskAttributes{ DomainOperation: &tc.domainOperation, ID: id, Info: &types.DomainInfo{ Name: domainName, Status: &status, Description: description, OwnerEmail: ownerEmail, Data: data, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterActive, Clusters: domainReplicator.convertClusterReplicationConfigToThrift(clusters), ActiveClusters: tc.expectedActiveClusters, }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, PreviousFailoverVersion: previousFailoverVersion, }, }).Return(nil).Once() } err := domainReplicator.HandleTransmissionTask( context.Background(), tc.domainOperation, info, config, replicationConfig, configVersion, failoverVersion, previousFailoverVersion, tc.isGlobalDomain, ) assert.NoError(t, err) kafkaProducer.AssertExpectations(t) }) } } func TestActiveActiveDomainUpdates_Merge(t *testing.T) { // worked example: // // Cluster A - Initial Failover Version: 0 - us-east-1 // Cluster B - Initial Failover Version: 1 - us-west-1 // Cluster C - Initial Failover Version: 2 - us-west-2 // Cluster D - Initial Failover Version: 3 - us-west-2 tests := map[string]struct { currentActiveClusters *types.ActiveClusters request *types.ActiveClusters expectedResult *types.ActiveClusters expectedIsChanged bool }{ "simple case - incoming location failover out of us-east-1 - failing over some cities to cluster C - the result should indicate the failover has occurred": { currentActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "Denver": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "Maarstricht": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Sao Paulo": {ActiveClusterName: "clusterD", FailoverVersion: 3}, }, }, "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "us-west-1": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "us-west-2": {ActiveClusterName: "clusterC", FailoverVersion: 2}, }, }, }, }, // incoming location failover out of us-east-1 - failing over some cities to cluster C request: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Denver": {ActiveClusterName: "clusterC", FailoverVersion: 2}, }, }, }, }, expectedIsChanged: true, expectedResult: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Denver": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Maarstricht": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Sao Paulo": {ActiveClusterName: "clusterD", FailoverVersion: 3}, }, }, "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "us-west-1": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "us-west-2": {ActiveClusterName: "clusterC", FailoverVersion: 2}, }, }, }, }, }, // this simulates a stale domain update being sent through the replication queue // in this case, the local data is more recent and should win-out "incoming location failover out of us-east-1 - failing over some cities to cluster C - the local data is more recent - the result should drop stale cluster-attributes from the replication message": { currentActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 100}, "Denver": {ActiveClusterName: "clusterB", FailoverVersion: 101}, "Maarstricht": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Sao Paulo": {ActiveClusterName: "clusterD", FailoverVersion: 3}, }, }, "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "us-west-1": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "us-west-2": {ActiveClusterName: "clusterC", FailoverVersion: 2}, }, }, }, }, // incoming location failover out of us-east-1 - failing over some cities to cluster C request: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Denver": {ActiveClusterName: "clusterC", FailoverVersion: 2}, }, }, }, }, expectedIsChanged: false, expectedResult: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 100}, "Denver": {ActiveClusterName: "clusterB", FailoverVersion: 101}, "Maarstricht": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Sao Paulo": {ActiveClusterName: "clusterD", FailoverVersion: 3}, }, }, "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "us-west-1": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "us-west-2": {ActiveClusterName: "clusterC", FailoverVersion: 2}, }, }, }, }, }, // This simulates the case when there's either been very fast changes or a network split // and at least two clusters have updated their local domain-data in the mean time and // at for the same amount of times. Ie it's pretty unlikely, though possible. // in this case, the arbitrary initial failover version is used as a deterministic tie-break "conflict resolution - both incoming and local data have been modified - employ tie-break - the result with the higher failover version should win": { currentActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 100}, "Denver": {ActiveClusterName: "clusterB", FailoverVersion: 101}, "Maarstricht": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Sao Paulo": {ActiveClusterName: "clusterD", FailoverVersion: 103}, }, }, }, }, // incoming location failover out of us-east-1 - failing all locations to cluster C request: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterC", FailoverVersion: 102}, "Denver": {ActiveClusterName: "clusterC", FailoverVersion: 102}, "Maarstricht": {ActiveClusterName: "clusterC", FailoverVersion: 102}, "Sao Paulo": {ActiveClusterName: "clusterC", FailoverVersion: 102}, }, }, }, }, expectedIsChanged: true, expectedResult: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ // Prague and Denver are failing over to cluster C - they should be updated to the new cluster // the somewhat arbitrary initial failover version is used as a tie-break // and clusterC is chosen "Prague": {ActiveClusterName: "clusterC", FailoverVersion: 102}, "Denver": {ActiveClusterName: "clusterC", FailoverVersion: 102}, "Maarstricht": {ActiveClusterName: "clusterC", FailoverVersion: 102}, // Sao Paulo is not failing over - it should remain at the same cluster "Sao Paulo": {ActiveClusterName: "clusterD", FailoverVersion: 103}, }, }, }, }, }, "simple case - no updates - no change": { currentActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "Denver": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "Maarstricht": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Sao Paulo": {ActiveClusterName: "clusterD", FailoverVersion: 3}, }, }, "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "us-west-1": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "us-west-2": {ActiveClusterName: "clusterC", FailoverVersion: 2}, }, }, }, }, request: &types.ActiveClusters{}, expectedResult: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "Prague": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "Denver": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "Maarstricht": {ActiveClusterName: "clusterC", FailoverVersion: 2}, "Sao Paulo": {ActiveClusterName: "clusterD", FailoverVersion: 3}, }, }, "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "clusterA", FailoverVersion: 0}, "us-west-1": {ActiveClusterName: "clusterB", FailoverVersion: 1}, "us-west-2": {ActiveClusterName: "clusterC", FailoverVersion: 2}, }, }, }, }, expectedIsChanged: false, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { result, isChanged := mergeActiveActiveScopes(td.currentActiveClusters, td.request) assert.Equal(t, td.expectedResult, result) assert.Equal(t, td.expectedIsChanged, isChanged) }) } } ================================================ FILE: common/domain/dlq_message_handler.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination dlq_message_handler_mock.go package domain import ( "context" "sync" "sync/atomic" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type ( // DLQMessageHandler is the interface handles domain DLQ messages DLQMessageHandler interface { common.Daemon Count(ctx context.Context, forceFetch bool) (int64, error) Read(ctx context.Context, lastMessageID int64, pageSize int, pageToken []byte) ([]*types.ReplicationTask, []byte, error) Purge(ctx context.Context, lastMessageID int64) error Merge(ctx context.Context, lastMessageID int64, pageSize int, pageToken []byte) ([]byte, error) } dlqMessageHandlerImpl struct { replicationHandler ReplicationTaskExecutor replicationQueue ReplicationQueue logger log.Logger metricsClient metrics.Client timeSrc clock.TimeSource done chan struct{} status int32 mu sync.Mutex lastCount int64 } ) // NewDLQMessageHandler returns a DLQTaskHandler instance func NewDLQMessageHandler( replicationHandler ReplicationTaskExecutor, replicationQueue ReplicationQueue, logger log.Logger, metricsClient metrics.Client, timeSource clock.TimeSource, ) DLQMessageHandler { return &dlqMessageHandlerImpl{ replicationHandler: replicationHandler, replicationQueue: replicationQueue, logger: logger, metricsClient: metricsClient, timeSrc: timeSource, done: make(chan struct{}), lastCount: -1, } } // Start starts the DLQ handler func (d *dlqMessageHandlerImpl) Start() { if !atomic.CompareAndSwapInt32(&d.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } go d.emitDLQSizeMetricsLoop() d.logger.Info("Domain DLQ handler started.") } // Stop stops the DLQ handler func (d *dlqMessageHandlerImpl) Stop() { if !atomic.CompareAndSwapInt32(&d.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } d.logger.Info("Domain DLQ handler shutting down.") close(d.done) } // Count counts domain replication DLQ messages func (d *dlqMessageHandlerImpl) Count(ctx context.Context, forceFetch bool) (int64, error) { if forceFetch || d.lastCount == -1 { if err := d.fetchAndEmitDLQSize(ctx); err != nil { return 0, err } } return d.lastCount, nil } // ReadMessages reads domain replication DLQ messages func (d *dlqMessageHandlerImpl) Read( ctx context.Context, lastMessageID int64, pageSize int, pageToken []byte, ) ([]*types.ReplicationTask, []byte, error) { ackLevel, err := d.replicationQueue.GetDLQAckLevel(ctx) if err != nil { return nil, nil, err } return d.replicationQueue.GetMessagesFromDLQ( ctx, ackLevel, lastMessageID, pageSize, pageToken, ) } // PurgeMessages purges domain replication DLQ messages func (d *dlqMessageHandlerImpl) Purge( ctx context.Context, lastMessageID int64, ) error { ackLevel, err := d.replicationQueue.GetDLQAckLevel(ctx) if err != nil { return err } if err := d.replicationQueue.RangeDeleteMessagesFromDLQ( ctx, ackLevel, lastMessageID, ); err != nil { return err } if err := d.replicationQueue.UpdateDLQAckLevel( ctx, lastMessageID, ); err != nil { d.logger.Error("Failed to update DLQ ack level after purging messages", tag.Error(err)) } return nil } // MergeMessages merges domain replication DLQ messages func (d *dlqMessageHandlerImpl) Merge( ctx context.Context, lastMessageID int64, pageSize int, pageToken []byte, ) ([]byte, error) { ackLevel, err := d.replicationQueue.GetDLQAckLevel(ctx) if err != nil { return nil, err } messages, token, err := d.replicationQueue.GetMessagesFromDLQ( ctx, ackLevel, lastMessageID, pageSize, pageToken, ) if err != nil { return nil, err } var ackedMessageID int64 for _, message := range messages { domainTask := message.GetDomainTaskAttributes() if domainTask == nil { return nil, &types.InternalServiceError{Message: "Encounter non domain replication task in domain replication queue."} } // TODO: if err := d.replicationHandler.Execute( domainTask, ); err != nil { return nil, err } ackedMessageID = message.SourceTaskID } if err := d.replicationQueue.RangeDeleteMessagesFromDLQ( ctx, ackLevel, ackedMessageID, ); err != nil { d.logger.Error("failed to delete merged tasks on merging domain DLQ message", tag.Error(err)) return nil, err } if err := d.replicationQueue.UpdateDLQAckLevel(ctx, ackedMessageID); err != nil { d.logger.Error("failed to update ack level on merging domain DLQ message", tag.Error(err)) } return token, nil } func (d *dlqMessageHandlerImpl) emitDLQSizeMetricsLoop() { ticker := d.timeSrc.NewTicker(queueSizeQueryInterval) defer ticker.Stop() for { select { case <-d.done: return case <-ticker.Chan(): err := d.fetchAndEmitDLQSize(context.Background()) if err != nil { d.logger.Warn("Failed to get DLQ size.", tag.Error(err)) } } } } func (d *dlqMessageHandlerImpl) fetchAndEmitDLQSize(ctx context.Context) error { size, err := d.replicationQueue.GetDLQSize(ctx) if err != nil { d.metricsClient.Scope(metrics.DomainReplicationQueueScope).IncCounter(metrics.DomainReplicationQueueSizeErrorCount) return err } d.metricsClient.Scope(metrics.DomainReplicationQueueScope).UpdateGauge(metrics.DomainReplicationQueueSizeGauge, float64(size)) d.mu.Lock() d.lastCount = size d.mu.Unlock() return nil } ================================================ FILE: common/domain/dlq_message_handler_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: dlq_message_handler.go // // Generated by this command: // // mockgen -package domain -source dlq_message_handler.go -destination dlq_message_handler_mock.go // // Package domain is a generated GoMock package. package domain import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockDLQMessageHandler is a mock of DLQMessageHandler interface. type MockDLQMessageHandler struct { ctrl *gomock.Controller recorder *MockDLQMessageHandlerMockRecorder isgomock struct{} } // MockDLQMessageHandlerMockRecorder is the mock recorder for MockDLQMessageHandler. type MockDLQMessageHandlerMockRecorder struct { mock *MockDLQMessageHandler } // NewMockDLQMessageHandler creates a new mock instance. func NewMockDLQMessageHandler(ctrl *gomock.Controller) *MockDLQMessageHandler { mock := &MockDLQMessageHandler{ctrl: ctrl} mock.recorder = &MockDLQMessageHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDLQMessageHandler) EXPECT() *MockDLQMessageHandlerMockRecorder { return m.recorder } // Count mocks base method. func (m *MockDLQMessageHandler) Count(ctx context.Context, forceFetch bool) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Count", ctx, forceFetch) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // Count indicates an expected call of Count. func (mr *MockDLQMessageHandlerMockRecorder) Count(ctx, forceFetch any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockDLQMessageHandler)(nil).Count), ctx, forceFetch) } // Merge mocks base method. func (m *MockDLQMessageHandler) Merge(ctx context.Context, lastMessageID int64, pageSize int, pageToken []byte) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Merge", ctx, lastMessageID, pageSize, pageToken) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // Merge indicates an expected call of Merge. func (mr *MockDLQMessageHandlerMockRecorder) Merge(ctx, lastMessageID, pageSize, pageToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Merge", reflect.TypeOf((*MockDLQMessageHandler)(nil).Merge), ctx, lastMessageID, pageSize, pageToken) } // Purge mocks base method. func (m *MockDLQMessageHandler) Purge(ctx context.Context, lastMessageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Purge", ctx, lastMessageID) ret0, _ := ret[0].(error) return ret0 } // Purge indicates an expected call of Purge. func (mr *MockDLQMessageHandlerMockRecorder) Purge(ctx, lastMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Purge", reflect.TypeOf((*MockDLQMessageHandler)(nil).Purge), ctx, lastMessageID) } // Read mocks base method. func (m *MockDLQMessageHandler) Read(ctx context.Context, lastMessageID int64, pageSize int, pageToken []byte) ([]*types.ReplicationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Read", ctx, lastMessageID, pageSize, pageToken) ret0, _ := ret[0].([]*types.ReplicationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // Read indicates an expected call of Read. func (mr *MockDLQMessageHandlerMockRecorder) Read(ctx, lastMessageID, pageSize, pageToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockDLQMessageHandler)(nil).Read), ctx, lastMessageID, pageSize, pageToken) } // Start mocks base method. func (m *MockDLQMessageHandler) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockDLQMessageHandlerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockDLQMessageHandler)(nil).Start)) } // Stop mocks base method. func (m *MockDLQMessageHandler) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockDLQMessageHandlerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockDLQMessageHandler)(nil).Stop)) } ================================================ FILE: common/domain/dlq_message_handler_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "context" "fmt" "sync/atomic" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type ( dlqMessageHandlerSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockReplicationTaskExecutor *MockReplicationTaskExecutor mockReplicationQueue *MockReplicationQueue dlqMessageHandler *dlqMessageHandlerImpl } ) func TestDLQMessageHandlerSuite(t *testing.T) { s := new(dlqMessageHandlerSuite) suite.Run(t, s) } func (s *dlqMessageHandlerSuite) SetupSuite() { } func (s *dlqMessageHandlerSuite) TearDownSuite() { } func (s *dlqMessageHandlerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockReplicationTaskExecutor = NewMockReplicationTaskExecutor(s.controller) s.mockReplicationQueue = NewMockReplicationQueue(s.controller) logger := testlogger.New(s.Suite.T()) s.dlqMessageHandler = NewDLQMessageHandler( s.mockReplicationTaskExecutor, s.mockReplicationQueue, logger, metrics.NewNoopMetricsClient(), clock.NewMockedTimeSource(), ).(*dlqMessageHandlerImpl) } func (s *dlqMessageHandlerSuite) TearDownTest() { } func (s *dlqMessageHandlerSuite) TestReadMessages() { ackLevel := int64(10) lastMessageID := int64(20) pageSize := 100 pageToken := []byte{} tasks := []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), SourceTaskID: 1, }, } s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(ackLevel, nil).Times(1) s.mockReplicationQueue.EXPECT().GetMessagesFromDLQ(gomock.Any(), ackLevel, lastMessageID, pageSize, pageToken). Return(tasks, nil, nil).Times(1) resp, token, err := s.dlqMessageHandler.Read(context.Background(), lastMessageID, pageSize, pageToken) s.NoError(err) s.Equal(tasks, resp) s.Nil(token) } func TestDLQMessageHandler_Start(t *testing.T) { tests := []struct { name string setupMocks func(m *MockReplicationQueue) initialStatus int32 expectedStatus int32 }{ { name: "Should start when initialized", setupMocks: func(m *MockReplicationQueue) { m.EXPECT().GetDLQSize(gomock.Any()).Return(int64(1), nil).AnyTimes() }, initialStatus: common.DaemonStatusInitialized, expectedStatus: common.DaemonStatusStarted, }, { name: "Should not start when already started", setupMocks: func(m *MockReplicationQueue) { // No calls expected since the handler should recognize it's already started }, initialStatus: common.DaemonStatusStarted, expectedStatus: common.DaemonStatusStarted, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := NewMockReplicationQueue(ctrl) mockReplicationTaskExecutor := NewMockReplicationTaskExecutor(ctrl) mockLogger := testlogger.New(t) mockedTime := clock.NewMockedTimeSource() tc.setupMocks(mockQueue) doneChan := make(chan struct{}) handler := &dlqMessageHandlerImpl{ replicationHandler: mockReplicationTaskExecutor, replicationQueue: mockQueue, metricsClient: metrics.NewNoopMetricsClient(), logger: mockLogger, done: doneChan, status: tc.initialStatus, timeSrc: mockedTime, } handler.Start() mockedTime.Advance(5 * time.Minute) defer handler.Stop() assert.Equal(t, tc.expectedStatus, atomic.LoadInt32(&handler.status)) }) } } func (s *dlqMessageHandlerSuite) TestStop() { tests := []struct { name string initialStatus int32 expectedStatus int32 shouldStop bool }{ { name: "Should stop when started", initialStatus: common.DaemonStatusStarted, expectedStatus: common.DaemonStatusStopped, shouldStop: true, }, { name: "Should not stop when not started", initialStatus: common.DaemonStatusInitialized, expectedStatus: common.DaemonStatusInitialized, shouldStop: false, }, } for _, test := range tests { s.Run(test.name, func() { atomic.StoreInt32(&s.dlqMessageHandler.status, test.initialStatus) s.dlqMessageHandler.Stop() s.Equal(test.expectedStatus, atomic.LoadInt32(&s.dlqMessageHandler.status)) }) } } func (s *dlqMessageHandlerSuite) TestCount() { tests := []struct { name string forceFetch bool lastCount int64 fetchSize int64 fetchError error expectedCount int64 expectedError error }{ { name: "Force fetch with error", forceFetch: true, lastCount: 10, fetchSize: 0, fetchError: fmt.Errorf("fetch error"), expectedCount: 0, expectedError: fmt.Errorf("fetch error"), }, { name: "Force fetch with success", forceFetch: true, lastCount: 10, fetchSize: 20, fetchError: nil, expectedCount: 20, expectedError: nil, }, { name: "No fetch needed", forceFetch: false, lastCount: 30, expectedCount: 30, expectedError: nil, }, } for _, test := range tests { s.Run(test.name, func() { s.mockReplicationQueue.EXPECT().GetDLQSize(gomock.Any()).Return(test.fetchSize, test.fetchError).MaxTimes(1) s.dlqMessageHandler.lastCount = test.lastCount count, err := s.dlqMessageHandler.Count(context.Background(), test.forceFetch) s.Equal(test.expectedCount, count) if test.expectedError != nil { s.Equal(test.expectedError, err) } else { s.NoError(err) } }) } } func (s *dlqMessageHandlerSuite) TestReadMessages_ThrowErrorOnGetDLQAckLevel() { lastMessageID := int64(20) pageSize := 100 pageToken := []byte{} tasks := []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), SourceTaskID: 1, }, } testError := fmt.Errorf("test") s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(int64(-1), testError).Times(1) s.mockReplicationQueue.EXPECT().GetMessagesFromDLQ(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(tasks, nil, nil).Times(0) _, _, err := s.dlqMessageHandler.Read(context.Background(), lastMessageID, pageSize, pageToken) s.Equal(testError, err) } func (s *dlqMessageHandlerSuite) TestReadMessages_ThrowErrorOnReadMessages() { ackLevel := int64(10) lastMessageID := int64(20) pageSize := 100 pageToken := []byte{} testError := fmt.Errorf("test") s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(ackLevel, nil).Times(1) s.mockReplicationQueue.EXPECT().GetMessagesFromDLQ(gomock.Any(), ackLevel, lastMessageID, pageSize, pageToken). Return(nil, nil, testError).Times(1) _, _, err := s.dlqMessageHandler.Read(context.Background(), lastMessageID, pageSize, pageToken) s.Equal(testError, err) } func (s *dlqMessageHandlerSuite) TestPurgeMessages() { ackLevel := int64(10) lastMessageID := int64(20) s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(ackLevel, nil).Times(1) s.mockReplicationQueue.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), ackLevel, lastMessageID).Return(nil).Times(1) s.mockReplicationQueue.EXPECT().UpdateDLQAckLevel(gomock.Any(), lastMessageID).Return(nil).Times(1) err := s.dlqMessageHandler.Purge(context.Background(), lastMessageID) s.NoError(err) } func (s *dlqMessageHandlerSuite) TestPurgeMessages_ThrowErrorOnGetDLQAckLevel() { lastMessageID := int64(20) testError := fmt.Errorf("test") s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(int64(-1), testError).Times(1) s.mockReplicationQueue.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(0) s.mockReplicationQueue.EXPECT().UpdateDLQAckLevel(gomock.Any(), gomock.Any()).Times(0) err := s.dlqMessageHandler.Purge(context.Background(), lastMessageID) s.Equal(testError, err) } func (s *dlqMessageHandlerSuite) TestPurgeMessages_ThrowErrorOnPurgeMessages() { ackLevel := int64(10) lastMessageID := int64(20) testError := fmt.Errorf("test") s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(ackLevel, nil).Times(1) s.mockReplicationQueue.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), ackLevel, lastMessageID).Return(testError).Times(1) s.mockReplicationQueue.EXPECT().UpdateDLQAckLevel(gomock.Any(), gomock.Any()).Times(0) err := s.dlqMessageHandler.Purge(context.Background(), lastMessageID) s.Equal(testError, err) } func (s *dlqMessageHandlerSuite) TestMergeMessages() { ackLevel := int64(10) lastMessageID := int64(20) pageSize := 100 pageToken := []byte{} messageID := int64(11) domainAttribute := &types.DomainTaskAttributes{ ID: uuid.New(), } tasks := []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), SourceTaskID: messageID, DomainTaskAttributes: domainAttribute, }, } s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(ackLevel, nil).Times(1) s.mockReplicationQueue.EXPECT().GetMessagesFromDLQ(gomock.Any(), ackLevel, lastMessageID, pageSize, pageToken). Return(tasks, nil, nil).Times(1) s.mockReplicationTaskExecutor.EXPECT().Execute(domainAttribute).Return(nil).Times(1) s.mockReplicationQueue.EXPECT().UpdateDLQAckLevel(gomock.Any(), messageID).Return(nil).Times(1) s.mockReplicationQueue.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), ackLevel, messageID).Return(nil).Times(1) token, err := s.dlqMessageHandler.Merge(context.Background(), lastMessageID, pageSize, pageToken) s.NoError(err) s.Nil(token) } func (s *dlqMessageHandlerSuite) TestMergeMessages_ThrowErrorOnGetDLQAckLevel() { lastMessageID := int64(20) pageSize := 100 pageToken := []byte{} messageID := int64(11) testError := fmt.Errorf("test") domainAttribute := &types.DomainTaskAttributes{ ID: uuid.New(), } tasks := []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), SourceTaskID: messageID, DomainTaskAttributes: domainAttribute, }, } s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(int64(-1), testError).Times(1) s.mockReplicationQueue.EXPECT().GetMessagesFromDLQ(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(tasks, nil, nil).Times(0) s.mockReplicationTaskExecutor.EXPECT().Execute(gomock.Any()).Times(0) s.mockReplicationQueue.EXPECT().DeleteMessageFromDLQ(gomock.Any(), gomock.Any()).Times(0) s.mockReplicationQueue.EXPECT().UpdateDLQAckLevel(gomock.Any(), gomock.Any()).Times(0) token, err := s.dlqMessageHandler.Merge(context.Background(), lastMessageID, pageSize, pageToken) s.Equal(testError, err) s.Nil(token) } func (s *dlqMessageHandlerSuite) TestMergeMessages_ThrowErrorOnGetDLQMessages() { ackLevel := int64(10) lastMessageID := int64(20) pageSize := 100 pageToken := []byte{} testError := fmt.Errorf("test") s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(ackLevel, nil).Times(1) s.mockReplicationQueue.EXPECT().GetMessagesFromDLQ(gomock.Any(), ackLevel, lastMessageID, pageSize, pageToken). Return(nil, nil, testError).Times(1) s.mockReplicationTaskExecutor.EXPECT().Execute(gomock.Any()).Times(0) s.mockReplicationQueue.EXPECT().DeleteMessageFromDLQ(gomock.Any(), gomock.Any()).Times(0) s.mockReplicationQueue.EXPECT().UpdateDLQAckLevel(gomock.Any(), gomock.Any()).Times(0) token, err := s.dlqMessageHandler.Merge(context.Background(), lastMessageID, pageSize, pageToken) s.Equal(testError, err) s.Nil(token) } func (s *dlqMessageHandlerSuite) TestMergeMessages_ThrowErrorOnHandleReceivingTask() { ackLevel := int64(10) lastMessageID := int64(20) pageSize := 100 pageToken := []byte{} messageID1 := int64(11) messageID2 := int64(12) testError := fmt.Errorf("test") domainAttribute1 := &types.DomainTaskAttributes{ ID: uuid.New(), } domainAttribute2 := &types.DomainTaskAttributes{ ID: uuid.New(), } tasks := []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), SourceTaskID: messageID1, DomainTaskAttributes: domainAttribute1, }, { TaskType: types.ReplicationTaskTypeDomain.Ptr(), SourceTaskID: messageID2, DomainTaskAttributes: domainAttribute2, }, } s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(ackLevel, nil).Times(1) s.mockReplicationQueue.EXPECT().GetMessagesFromDLQ(gomock.Any(), ackLevel, lastMessageID, pageSize, pageToken). Return(tasks, nil, nil).Times(1) s.mockReplicationTaskExecutor.EXPECT().Execute(domainAttribute1).Return(nil).Times(1) s.mockReplicationTaskExecutor.EXPECT().Execute(domainAttribute2).Return(testError).Times(1) s.mockReplicationQueue.EXPECT().DeleteMessageFromDLQ(gomock.Any(), messageID2).Times(0) token, err := s.dlqMessageHandler.Merge(context.Background(), lastMessageID, pageSize, pageToken) s.Equal(testError, err) s.Nil(token) } func (s *dlqMessageHandlerSuite) TestMergeMessages_ThrowErrorOnDeleteMessages() { ackLevel := int64(10) lastMessageID := int64(20) pageSize := 100 pageToken := []byte{} messageID1 := int64(11) messageID2 := int64(12) testError := fmt.Errorf("test") domainAttribute1 := &types.DomainTaskAttributes{ ID: uuid.New(), } domainAttribute2 := &types.DomainTaskAttributes{ ID: uuid.New(), } tasks := []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), SourceTaskID: messageID1, DomainTaskAttributes: domainAttribute1, }, { TaskType: types.ReplicationTaskTypeDomain.Ptr(), SourceTaskID: messageID2, DomainTaskAttributes: domainAttribute2, }, } s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(ackLevel, nil).Times(1) s.mockReplicationQueue.EXPECT().GetMessagesFromDLQ(gomock.Any(), ackLevel, lastMessageID, pageSize, pageToken). Return(tasks, nil, nil).Times(1) s.mockReplicationTaskExecutor.EXPECT().Execute(domainAttribute1).Return(nil).Times(1) s.mockReplicationTaskExecutor.EXPECT().Execute(domainAttribute2).Return(nil).Times(1) s.mockReplicationQueue.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), ackLevel, messageID2).Return(testError).Times(1) token, err := s.dlqMessageHandler.Merge(context.Background(), lastMessageID, pageSize, pageToken) s.Error(err) s.Nil(token) } func (s *dlqMessageHandlerSuite) TestMergeMessages_IgnoreErrorOnUpdateDLQAckLevel() { ackLevel := int64(10) lastMessageID := int64(20) pageSize := 100 pageToken := []byte{} messageID := int64(11) testError := fmt.Errorf("test") domainAttribute := &types.DomainTaskAttributes{ ID: uuid.New(), } tasks := []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), SourceTaskID: messageID, DomainTaskAttributes: domainAttribute, }, } s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(ackLevel, nil).Times(1) s.mockReplicationQueue.EXPECT().GetMessagesFromDLQ(gomock.Any(), ackLevel, lastMessageID, pageSize, pageToken). Return(tasks, nil, nil).Times(1) s.mockReplicationTaskExecutor.EXPECT().Execute(domainAttribute).Return(nil).Times(1) s.mockReplicationQueue.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), ackLevel, messageID).Return(nil).Times(1) s.mockReplicationQueue.EXPECT().UpdateDLQAckLevel(gomock.Any(), messageID).Return(testError).Times(1) token, err := s.dlqMessageHandler.Merge(context.Background(), lastMessageID, pageSize, pageToken) s.NoError(err) s.Nil(token) } func (s *dlqMessageHandlerSuite) TestMergeMessages_NonDomainTask() { ackLevel := int64(10) lastMessageID := int64(20) pageSize := 100 pageToken := []byte{} // Create a task that mimics a non-domain replication task by not setting DomainTaskAttributes tasks := []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), // Still set to domain but no attributes SourceTaskID: 1, }, } s.mockReplicationQueue.EXPECT().GetDLQAckLevel(gomock.Any()).Return(ackLevel, nil).Times(1) s.mockReplicationQueue.EXPECT().GetMessagesFromDLQ(gomock.Any(), ackLevel, lastMessageID, pageSize, pageToken). Return(tasks, nil, nil).Times(1) token, err := s.dlqMessageHandler.Merge(context.Background(), lastMessageID, pageSize, pageToken) s.NotNil(err) s.IsType(&types.InternalServiceError{}, err) s.Equal("Encounter non domain replication task in domain replication queue.", err.Error()) s.Nil(token) } ================================================ FILE: common/domain/errors.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import "github.com/uber/cadence/common/types" var ( // err indicating that this cluster is not the primary, so cannot do domain registration or update errNotPrimaryCluster = &types.BadRequestError{Message: "Cluster is not primary cluster, cannot do domain registration or domain update."} errCannotRemoveClustersFromDomain = &types.BadRequestError{Message: "Cannot remove existing replicated clusters from a domain."} errActiveClusterNotInClusters = &types.BadRequestError{Message: "Active cluster is not contained in all clusters."} errCannotDoDomainFailoverAndUpdate = &types.BadRequestError{Message: "Cannot set active cluster to current cluster when other parameters are set."} errCannotDoGracefulFailoverFromCluster = &types.BadRequestError{Message: "Cannot start the graceful failover from a to-be-passive cluster."} errGracefulFailoverInActiveCluster = &types.BadRequestError{Message: "Cannot start the graceful failover from an active cluster to an active cluster."} errOngoingGracefulFailover = &types.BadRequestError{Message: "Cannot start concurrent graceful failover."} errInvalidGracefulFailover = &types.BadRequestError{Message: "Cannot start graceful failover without updating active cluster or in local domain."} errInvalidFailoverNoChangeDetected = &types.BadRequestError{Message: "a failover was requested, but there was no change detected, the configuration was not updated"} errActiveClusterNameRequired = &types.BadRequestError{Message: "ActiveClusterName is required for all global domains."} errLocalDomainsCannotFailover = &types.BadRequestError{Message: "Local domains cannot perform failovers or change replication configuration"} errInvalidRetentionPeriod = &types.BadRequestError{Message: "A valid retention period is not set on request."} errInvalidArchivalConfig = &types.BadRequestError{Message: "Invalid to enable archival without specifying a uri."} ) ================================================ FILE: common/domain/failover_watcher.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination failover_watcher_mock.go package domain import ( "context" "fmt" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) const ( updateDomainRetryInitialInterval = 50 * time.Millisecond updateDomainRetryCoefficient = 2.0 updateDomainMaxRetry = 3 ) type ( // FailoverWatcher handles failover operation on domain entities FailoverWatcher interface { common.Daemon } failoverWatcherImpl struct { status int32 shutdownChan chan struct{} refreshInterval dynamicproperties.DurationPropertyFn refreshJitter dynamicproperties.FloatPropertyFn retryPolicy backoff.RetryPolicy domainManager persistence.DomainManager domainCache cache.DomainCache timeSource clock.TimeSource scope metrics.Scope logger log.Logger } ) var _ FailoverWatcher = (*failoverWatcherImpl)(nil) // NewFailoverWatcher initializes domain failover processor func NewFailoverWatcher( domainCache cache.DomainCache, domainManager persistence.DomainManager, timeSource clock.TimeSource, refreshInterval dynamicproperties.DurationPropertyFn, refreshJitter dynamicproperties.FloatPropertyFn, metricsClient metrics.Client, logger log.Logger, ) FailoverWatcher { retryPolicy := &backoff.ExponentialRetryPolicy{} retryPolicy.SetInitialInterval(updateDomainRetryInitialInterval) retryPolicy.SetBackoffCoefficient(updateDomainRetryCoefficient) retryPolicy.SetMaximumAttempts(updateDomainMaxRetry) return &failoverWatcherImpl{ status: common.DaemonStatusInitialized, shutdownChan: make(chan struct{}), refreshInterval: refreshInterval, refreshJitter: refreshJitter, retryPolicy: retryPolicy, domainCache: domainCache, domainManager: domainManager, timeSource: timeSource, scope: metricsClient.Scope(metrics.DomainFailoverScope), logger: logger, } } func (p *failoverWatcherImpl) Start() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } go p.refreshDomainLoop() p.logger.Info("Domain failover processor started.") } func (p *failoverWatcherImpl) Stop() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(p.shutdownChan) p.logger.Info("Domain failover processor stop.") } func (p *failoverWatcherImpl) refreshDomainLoop() { timer := p.timeSource.NewTimer(backoff.JitDuration( p.refreshInterval(), p.refreshJitter(), )) defer timer.Stop() for { select { case <-p.shutdownChan: return case <-timer.Chan(): domains := p.domainCache.GetAllDomain() for _, domain := range domains { p.handleFailoverTimeout(domain) select { case <-p.shutdownChan: p.logger.Debug("Stop refresh domain as the processing is stopping.") return default: } } timer.Reset(backoff.JitDuration( p.refreshInterval(), p.refreshJitter(), )) } } } func (p *failoverWatcherImpl) handleFailoverTimeout( domain *cache.DomainCacheEntry, ) { failoverEndTime := domain.GetFailoverEndTime() if domain.IsDomainPendingActive() && p.timeSource.Now().After(time.Unix(0, *failoverEndTime)) { domainID := domain.GetInfo().ID // force failover the domain without setting the failover timeout if err := CleanPendingActiveState( p.domainManager, domainID, domain.GetFailoverVersion(), p.retryPolicy, ); err != nil { p.logger.Error("Failed to update pending-active domain to active", tag.WorkflowDomainID(domainID), tag.Error(err)) return } p.scope.Tagged(metrics.DomainTag(domain.GetInfo().Name)).IncCounter(metrics.GracefulFailoverFailure) } } // CleanPendingActiveState removes the pending active state from the domain func CleanPendingActiveState( domainManager persistence.DomainManager, domainID string, failoverVersion int64, policy backoff.RetryPolicy, ) error { // must get the metadata (notificationVersion) first // this version can be regarded as the lock on the v2 domain table // and since we do not know which table will return the domain afterwards // this call has to be made metadata, err := domainManager.GetMetadata(context.Background()) if err != nil { return fmt.Errorf("getting metadata: %w", err) } getResponse, err := domainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{ID: domainID}) if err != nil { return fmt.Errorf("getting domain: %w", err) } localFailoverVersion := getResponse.FailoverVersion isGlobalDomain := getResponse.IsGlobalDomain gracefulFailoverEndTime := getResponse.FailoverEndTime if isGlobalDomain && gracefulFailoverEndTime != nil && failoverVersion == localFailoverVersion { // if the domain is still pending active and the failover versions are the same, clean the state updateReq := &persistence.UpdateDomainRequest{ Info: getResponse.Info, Config: getResponse.Config, ReplicationConfig: getResponse.ReplicationConfig, ConfigVersion: getResponse.ConfigVersion, FailoverVersion: localFailoverVersion, FailoverNotificationVersion: getResponse.FailoverNotificationVersion, FailoverEndTime: nil, NotificationVersion: metadata.NotificationVersion, } op := func(ctx context.Context) error { return domainManager.UpdateDomain(ctx, updateReq) } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(isUpdateDomainRetryable), ) if err := throttleRetry.Do(context.Background(), op); err != nil { return err } } return nil } func isUpdateDomainRetryable( err error, ) bool { return true } ================================================ FILE: common/domain/failover_watcher_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: failover_watcher.go // // Generated by this command: // // mockgen -package domain -source failover_watcher.go -destination failover_watcher_mock.go // // Package domain is a generated GoMock package. package domain import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockFailoverWatcher is a mock of FailoverWatcher interface. type MockFailoverWatcher struct { ctrl *gomock.Controller recorder *MockFailoverWatcherMockRecorder isgomock struct{} } // MockFailoverWatcherMockRecorder is the mock recorder for MockFailoverWatcher. type MockFailoverWatcherMockRecorder struct { mock *MockFailoverWatcher } // NewMockFailoverWatcher creates a new mock instance. func NewMockFailoverWatcher(ctrl *gomock.Controller) *MockFailoverWatcher { mock := &MockFailoverWatcher{ctrl: ctrl} mock.recorder = &MockFailoverWatcherMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFailoverWatcher) EXPECT() *MockFailoverWatcherMockRecorder { return m.recorder } // Start mocks base method. func (m *MockFailoverWatcher) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockFailoverWatcherMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockFailoverWatcher)(nil).Start)) } // Stop mocks base method. func (m *MockFailoverWatcher) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockFailoverWatcherMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockFailoverWatcher)(nil).Stop)) } ================================================ FILE: common/domain/failover_watcher_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "errors" "log" "os" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" ) type ( failoverWatcherSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockDomainCache *cache.MockDomainCache timeSource clock.TimeSource mockMetadataMgr *mocks.MetadataManager watcher *failoverWatcherImpl } ) func TestFailoverWatcherSuite(t *testing.T) { s := new(failoverWatcherSuite) suite.Run(t, s) } func (s *failoverWatcherSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } func (s *failoverWatcherSuite) TearDownSuite() { } func (s *failoverWatcherSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockDomainCache = cache.NewMockDomainCache(s.controller) s.timeSource = clock.NewMockedTimeSource() s.mockMetadataMgr = &mocks.MetadataManager{} s.mockMetadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: 1, }, nil) logger := testlogger.New(s.T()) scope := tally.NewTestScope("failover_test", nil) metricsClient := metrics.NewClient(scope, metrics.Frontend, metrics.HistogramMigration{}) s.watcher = NewFailoverWatcher( s.mockDomainCache, s.mockMetadataMgr, s.timeSource, dynamicproperties.GetDurationPropertyFn(10*time.Second), dynamicproperties.GetFloatPropertyFn(0.2), metricsClient, logger, ).(*failoverWatcherImpl) } func (s *failoverWatcherSuite) TearDownTest() { s.controller.Finish() s.watcher.Stop() } func (s *failoverWatcherSuite) TestCleanPendingActiveState() { domainName := uuid.New() info := &persistence.DomainInfo{ ID: domainName, Name: domainName, Status: persistence.DomainStatusRegistered, Description: "some random description", OwnerEmail: "some random email", Data: nil, } domainConfig := &persistence.DomainConfig{ Retention: 1, EmitMetric: true, } replicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "active"}, }, } s.mockMetadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{ ID: domainName, }).Return(&persistence.GetDomainResponse{ Info: info, Config: domainConfig, ReplicationConfig: replicationConfig, IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 1, FailoverNotificationVersion: 1, FailoverEndTime: nil, NotificationVersion: 1, }, nil).Times(1) // does not have failover end time err := CleanPendingActiveState(s.mockMetadataMgr, domainName, 1, s.watcher.retryPolicy) s.NoError(err) s.mockMetadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{ ID: domainName, }).Return(&persistence.GetDomainResponse{ Info: info, Config: domainConfig, ReplicationConfig: replicationConfig, IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 1, FailoverNotificationVersion: 1, FailoverEndTime: common.Int64Ptr(1), NotificationVersion: 1, }, nil).Times(1) // does not match failover versions err = CleanPendingActiveState(s.mockMetadataMgr, domainName, 5, s.watcher.retryPolicy) s.NoError(err) s.mockMetadataMgr.On("UpdateDomain", mock.Anything, &persistence.UpdateDomainRequest{ Info: info, Config: domainConfig, ReplicationConfig: replicationConfig, ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 2, FailoverEndTime: nil, NotificationVersion: 1, }).Return(nil).Times(1) s.mockMetadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{ ID: domainName, }).Return(&persistence.GetDomainResponse{ Info: info, Config: domainConfig, ReplicationConfig: replicationConfig, IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 2, FailoverEndTime: common.Int64Ptr(1), NotificationVersion: 1, }, nil).Times(1) err = CleanPendingActiveState(s.mockMetadataMgr, domainName, 2, s.watcher.retryPolicy) s.NoError(err) } func (s *failoverWatcherSuite) TestHandleFailoverTimeout() { domainName := uuid.New() info := &persistence.DomainInfo{ ID: domainName, Name: domainName, Status: persistence.DomainStatusRegistered, Description: "some random description", OwnerEmail: "some random email", Data: nil, } domainConfig := &persistence.DomainConfig{ Retention: 1, EmitMetric: true, } replicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "active"}, }, } endtime := common.Int64Ptr(s.timeSource.Now().UnixNano() - 1) s.mockMetadataMgr.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{ ID: domainName, }).Return(&persistence.GetDomainResponse{ Info: info, Config: domainConfig, ReplicationConfig: replicationConfig, IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 1, FailoverNotificationVersion: 1, FailoverEndTime: endtime, NotificationVersion: 1, }, nil).Times(1) s.mockMetadataMgr.On("UpdateDomain", mock.Anything, &persistence.UpdateDomainRequest{ Info: info, Config: domainConfig, ReplicationConfig: replicationConfig, ConfigVersion: 1, FailoverVersion: 1, FailoverNotificationVersion: 1, FailoverEndTime: nil, NotificationVersion: 1, }).Return(nil).Times(1) domainEntry := cache.NewDomainCacheEntryForTest( info, domainConfig, true, replicationConfig, 1, endtime, 0, 0, 0, ) s.watcher.handleFailoverTimeout(domainEntry) } func (s *failoverWatcherSuite) TestStart() { s.Assertions.Equal(common.DaemonStatusInitialized, s.watcher.status) s.watcher.Start() s.Assertions.Equal(common.DaemonStatusStarted, s.watcher.status) // Verify that calling Start again does not change the status s.watcher.Start() s.Assertions.Equal(common.DaemonStatusStarted, s.watcher.status) s.watcher.Stop() } func (s *failoverWatcherSuite) TestIsUpdateDomainRetryable() { testCases := []struct { name string inputErr error wantRetry bool }{ {"nil error", nil, true}, {"non-nil error", errors.New("some error"), true}, } for _, tc := range testCases { s.Run(tc.name, func() { retry := isUpdateDomainRetryable(tc.inputErr) s.Equal(tc.wantRetry, retry) }) } } func (s *failoverWatcherSuite) TestRefreshDomainLoop() { domainName := "testDomain" domainID := uuid.New() failoverEndTime := common.Int64Ptr(time.Now().Add(-time.Hour).UnixNano()) // 1 hour in the past mockTimeSource, _ := s.timeSource.(clock.MockedTimeSource) domainInfo := &persistence.DomainInfo{ID: domainID, Name: domainName} domainConfig := &persistence.DomainConfig{Retention: 1, EmitMetric: true} replicationConfig := &persistence.DomainReplicationConfig{ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "active"}}} domainEntry := cache.NewDomainCacheEntryForTest(domainInfo, domainConfig, true, replicationConfig, 1, failoverEndTime, 0, 0, 0) domainsMap := map[string]*cache.DomainCacheEntry{domainID: domainEntry} s.mockDomainCache.EXPECT().GetAllDomain().Return(domainsMap).AnyTimes() s.mockMetadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil).Maybe() s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.AnythingOfType("*persistence.GetDomainRequest")).Return(&persistence.GetDomainResponse{ Info: domainInfo, Config: domainConfig, ReplicationConfig: replicationConfig, IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 1, FailoverNotificationVersion: 1, FailoverEndTime: failoverEndTime, NotificationVersion: 1, }, nil).Once() s.mockMetadataMgr.On("UpdateDomain", mock.Anything, mock.Anything).Return(nil).Once() s.watcher.Start() // Delay to allow loop to start time.Sleep(1 * time.Second) mockTimeSource.Advance(12 * time.Second) // Now stop the watcher, which should trigger the shutdown case in refreshDomainLoop s.watcher.Stop() // Enough time for shutdown process to complete time.Sleep(1 * time.Second) s.mockMetadataMgr.AssertExpectations(s.T()) } ================================================ FILE: common/domain/handler.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination handler_mock.go package domain import ( "context" "encoding/json" "fmt" "regexp" "time" guuid "github.com/google/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" ) var ( errDomainUpdateTooFrequent = &types.ServiceBusyError{Message: "Domain update too frequent."} errInvalidDomainName = &types.BadRequestError{Message: "Domain name can only include alphanumeric and dash characters."} ) type ( // Handler is the domain operation handler Handler interface { DeleteDomain( ctx context.Context, deleteRequest *types.DeleteDomainRequest, ) error DeprecateDomain( ctx context.Context, deprecateRequest *types.DeprecateDomainRequest, ) error DescribeDomain( ctx context.Context, describeRequest *types.DescribeDomainRequest, ) (*types.DescribeDomainResponse, error) ListDomains( ctx context.Context, listRequest *types.ListDomainsRequest, ) (*types.ListDomainsResponse, error) RegisterDomain( ctx context.Context, registerRequest *types.RegisterDomainRequest, ) error UpdateDomain( ctx context.Context, updateRequest *types.UpdateDomainRequest, ) (*types.UpdateDomainResponse, error) FailoverDomain( ctx context.Context, failoverRequest *types.FailoverDomainRequest, ) (*types.FailoverDomainResponse, error) UpdateIsolationGroups( ctx context.Context, updateRequest types.UpdateDomainIsolationGroupsRequest, ) error UpdateAsyncWorkflowConfiguraton( ctx context.Context, updateRequest types.UpdateDomainAsyncWorkflowConfiguratonRequest, ) error } // handlerImpl is the domain operation handler implementation handlerImpl struct { domainManager persistence.DomainManager domainAuditManager persistence.DomainAuditManager clusterMetadata cluster.Metadata domainReplicator Replicator domainAttrValidator *AttrValidatorImpl archivalMetadata archiver.ArchivalMetadata archiverProvider provider.ArchiverProvider timeSource clock.TimeSource config Config logger log.Logger } // Config is the domain config for domain handler Config struct { MinRetentionDays dynamicproperties.IntPropertyFn MaxRetentionDays dynamicproperties.IntPropertyFn RequiredDomainDataKeys dynamicproperties.MapPropertyFn MaxBadBinaryCount dynamicproperties.IntPropertyFnWithDomainFilter FailoverCoolDown dynamicproperties.DurationPropertyFnWithDomainFilter FailoverHistoryMaxSize dynamicproperties.IntPropertyFnWithDomainFilter EnableDomainAuditLogging dynamicproperties.BoolPropertyFn } // FailoverEvent is the failover information to be stored for each failover event in domain data FailoverEvent struct { EventTime time.Time `json:"eventTime"` // active-passive domain failover FromCluster string `json:"fromCluster,omitempty"` ToCluster string `json:"toCluster,omitempty"` FailoverType string `json:"failoverType,omitempty"` Reason string `json:"reason,omitempty"` } // FailoverHistory is the history of failovers for a domain limited by the FailoverHistoryMaxSize config FailoverHistory struct { FailoverEvents []FailoverEvent } ) var _ Handler = (*handlerImpl)(nil) // NewHandler create a new domain handler func NewHandler( config Config, logger log.Logger, domainManager persistence.DomainManager, domainAuditManager persistence.DomainAuditManager, clusterMetadata cluster.Metadata, domainReplicator Replicator, archivalMetadata archiver.ArchivalMetadata, archiverProvider provider.ArchiverProvider, timeSource clock.TimeSource, ) Handler { return &handlerImpl{ logger: logger, domainManager: domainManager, domainAuditManager: domainAuditManager, clusterMetadata: clusterMetadata, domainReplicator: domainReplicator, domainAttrValidator: newAttrValidator(clusterMetadata, int32(config.MinRetentionDays())), archivalMetadata: archivalMetadata, archiverProvider: archiverProvider, timeSource: timeSource, config: config, } } // RegisterDomain register a new domain func (d *handlerImpl) RegisterDomain( ctx context.Context, registerRequest *types.RegisterDomainRequest, ) error { // cluster global domain enabled if !d.clusterMetadata.IsPrimaryCluster() && registerRequest.GetIsGlobalDomain() { return errNotPrimaryCluster } // first check if the name is already registered as the local domain _, err := d.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{Name: registerRequest.GetName()}) switch err.(type) { case nil: // domain already exists, cannot proceed return &types.DomainAlreadyExistsError{Message: "Domain already exists."} case *types.EntityNotExistsError: // domain does not exists, proceeds default: // other err return err } // input validation on domain name matchedRegex, err := regexp.MatchString("^[a-zA-Z0-9-]+$", registerRequest.GetName()) if err != nil { return err } if !matchedRegex { return errInvalidDomainName } activeClusterName := d.clusterMetadata.GetCurrentClusterName() // input validation on cluster names if registerRequest.ActiveClusterName != "" { activeClusterName = registerRequest.GetActiveClusterName() } clusters := []*persistence.ClusterReplicationConfig{} for _, clusterConfig := range registerRequest.Clusters { clusterName := clusterConfig.GetClusterName() clusters = append(clusters, &persistence.ClusterReplicationConfig{ClusterName: clusterName}) } clusters = cluster.GetOrUseDefaultClusters(activeClusterName, clusters) currentHistoryArchivalState := neverEnabledState() nextHistoryArchivalState := currentHistoryArchivalState clusterHistoryArchivalConfig := d.archivalMetadata.GetHistoryConfig() if clusterHistoryArchivalConfig.ClusterConfiguredForArchival() { archivalEvent, err := d.toArchivalRegisterEvent( registerRequest.HistoryArchivalStatus, registerRequest.GetHistoryArchivalURI(), clusterHistoryArchivalConfig.GetDomainDefaultStatus(), clusterHistoryArchivalConfig.GetDomainDefaultURI(), ) if err != nil { return err } nextHistoryArchivalState, _, err = currentHistoryArchivalState.getNextState(archivalEvent, d.validateHistoryArchivalURI) if err != nil { return err } } currentVisibilityArchivalState := neverEnabledState() nextVisibilityArchivalState := currentVisibilityArchivalState clusterVisibilityArchivalConfig := d.archivalMetadata.GetVisibilityConfig() if clusterVisibilityArchivalConfig.ClusterConfiguredForArchival() { archivalEvent, err := d.toArchivalRegisterEvent( registerRequest.VisibilityArchivalStatus, registerRequest.GetVisibilityArchivalURI(), clusterVisibilityArchivalConfig.GetDomainDefaultStatus(), clusterVisibilityArchivalConfig.GetDomainDefaultURI(), ) if err != nil { return err } nextVisibilityArchivalState, _, err = currentVisibilityArchivalState.getNextState(archivalEvent, d.validateVisibilityArchivalURI) if err != nil { return err } } eventID, err := guuid.NewV7() if err != nil { return err } info := &persistence.DomainInfo{ ID: eventID.String(), Name: registerRequest.GetName(), Status: persistence.DomainStatusRegistered, OwnerEmail: registerRequest.GetOwnerEmail(), Description: registerRequest.GetDescription(), Data: registerRequest.Data, } config := &persistence.DomainConfig{ Retention: registerRequest.GetWorkflowExecutionRetentionPeriodInDays(), EmitMetric: registerRequest.GetEmitMetric(), HistoryArchivalStatus: nextHistoryArchivalState.Status, HistoryArchivalURI: nextHistoryArchivalState.URI, VisibilityArchivalStatus: nextVisibilityArchivalState.Status, VisibilityArchivalURI: nextVisibilityArchivalState.URI, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, } activeClusters, err := d.activeClustersFromRegisterRequest(registerRequest) if err != nil { return err } replicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: activeClusterName, Clusters: clusters, ActiveClusters: activeClusters, } isGlobalDomain := registerRequest.GetIsGlobalDomain() if err := d.domainAttrValidator.validateDomainConfig(config); err != nil { return err } if isGlobalDomain { if err := d.domainAttrValidator.validateDomainReplicationConfigForGlobalDomain( replicationConfig, ); err != nil { return err } } else { if err := d.domainAttrValidator.validateDomainReplicationConfigForLocalDomain( replicationConfig, ); err != nil { return err } } failoverVersion := constants.EmptyVersion if registerRequest.GetIsGlobalDomain() { failoverVersion = d.clusterMetadata.GetNextFailoverVersion(activeClusterName, 0, registerRequest.Name) } domainRequest := &persistence.CreateDomainRequest{ Info: info, Config: config, ReplicationConfig: replicationConfig, IsGlobalDomain: isGlobalDomain, ConfigVersion: 0, FailoverVersion: failoverVersion, LastUpdatedTime: d.timeSource.Now().UnixNano(), } domainResponse, err := d.domainManager.CreateDomain(ctx, domainRequest) if err != nil { return err } if domainRequest.IsGlobalDomain { err = d.domainReplicator.HandleTransmissionTask( ctx, types.DomainOperationCreate, domainRequest.Info, domainRequest.Config, domainRequest.ReplicationConfig, domainRequest.ConfigVersion, domainRequest.FailoverVersion, constants.InitialPreviousFailoverVersion, domainRequest.IsGlobalDomain, ) if err != nil { return err } } d.logger.Info("Register domain succeeded", tag.WorkflowDomainName(registerRequest.GetName()), tag.WorkflowDomainID(domainResponse.ID), ) // Construct GetDomainResponse for audit log domainStateAfterCreate := &persistence.GetDomainResponse{ Info: domainRequest.Info, Config: domainRequest.Config, ReplicationConfig: domainRequest.ReplicationConfig, IsGlobalDomain: domainRequest.IsGlobalDomain, ConfigVersion: domainRequest.ConfigVersion, FailoverVersion: domainRequest.FailoverVersion, LastUpdatedTime: domainRequest.LastUpdatedTime, } err = d.updateDomainAuditLog(ctx, nil, domainStateAfterCreate, persistence.DomainAuditOperationTypeCreate, "domain created") if err != nil { return err } return nil } // ListDomains list all domains func (d *handlerImpl) ListDomains( ctx context.Context, listRequest *types.ListDomainsRequest, ) (*types.ListDomainsResponse, error) { pageSize := 100 if listRequest.GetPageSize() != 0 { pageSize = int(listRequest.GetPageSize()) } resp, err := d.domainManager.ListDomains(ctx, &persistence.ListDomainsRequest{ PageSize: pageSize, NextPageToken: listRequest.NextPageToken, }) if err != nil { return nil, err } domains := []*types.DescribeDomainResponse{} for _, domain := range resp.Domains { desc := &types.DescribeDomainResponse{ IsGlobalDomain: domain.IsGlobalDomain, FailoverVersion: domain.FailoverVersion, } desc.DomainInfo, desc.Configuration, desc.ReplicationConfiguration = d.createResponse(domain.Info, domain.Config, domain.ReplicationConfig) domains = append(domains, desc) } response := &types.ListDomainsResponse{ Domains: domains, NextPageToken: resp.NextPageToken, } return response, nil } // DescribeDomain describe the domain func (d *handlerImpl) DescribeDomain( ctx context.Context, describeRequest *types.DescribeDomainRequest, ) (*types.DescribeDomainResponse, error) { // TODO, we should migrate the non global domain to new table, see #773 req := &persistence.GetDomainRequest{ Name: describeRequest.GetName(), ID: describeRequest.GetUUID(), } resp, err := d.domainManager.GetDomain(ctx, req) if err != nil { return nil, err } response := &types.DescribeDomainResponse{ IsGlobalDomain: resp.IsGlobalDomain, FailoverVersion: resp.FailoverVersion, } if resp.FailoverEndTime != nil { response.FailoverInfo = &types.FailoverInfo{ FailoverVersion: resp.FailoverVersion, // This reflects that last domain update time. If there is a domain config update, this won't be accurate. FailoverStartTimestamp: resp.LastUpdatedTime, FailoverExpireTimestamp: *resp.FailoverEndTime, } } response.DomainInfo, response.Configuration, response.ReplicationConfiguration = d.createResponse(resp.Info, resp.Config, resp.ReplicationConfig) return response, nil } // UpdateDomain update the domain func (d *handlerImpl) UpdateDomain( ctx context.Context, updateRequest *types.UpdateDomainRequest, ) (*types.UpdateDomainResponse, error) { // must get the metadata (notificationVersion) first // this version can be regarded as the lock on the v2 domain table // and since we do not know which table will return the domain afterwards // this call has to be made metadata, err := d.domainManager.GetMetadata(ctx) if err != nil { return nil, err } notificationVersion := metadata.NotificationVersion currentDomainState, err := d.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}) if err != nil { return nil, err } isGlobalDomain := currentDomainState.IsGlobalDomain if !isGlobalDomain { return d.updateLocalDomain(ctx, updateRequest, currentDomainState, notificationVersion) } if updateRequest.IsAFailoverRequest() { return d.handleFailoverRequest(ctx, updateRequest, currentDomainState, notificationVersion) } return d.updateGlobalDomainConfiguration(ctx, updateRequest, currentDomainState, notificationVersion) } // All domain updates are throttled by the cool down time (incorrecty called 'failover' cool down). // The guard is an anti-flapping measure. func (d *handlerImpl) ensureUpdateOrFailoverCooldown(currentDomainState *persistence.GetDomainResponse) error { lastUpdatedTime := time.Unix(0, currentDomainState.LastUpdatedTime) now := d.timeSource.Now() if lastUpdatedTime.Add(d.config.FailoverCoolDown(currentDomainState.Info.Name)).After(now) { d.logger.Debugf("Domain was last updated at %v, failoverCoolDown: %v, current time: %v.", lastUpdatedTime, d.config.FailoverCoolDown(currentDomainState.Info.Name), now) return errDomainUpdateTooFrequent } return nil } // For global domains only, this is assumed to be invoked when // the incoming request is either specifying an active-cluster parameter to update // or active_clusters in the case of a AA domain func (d *handlerImpl) handleFailoverRequest(ctx context.Context, updateRequest *types.UpdateDomainRequest, currentState *persistence.GetDomainResponse, notificationVersion int64, ) (*types.UpdateDomainResponse, error) { // intendedDomainState will be modified // into the intended shape by the functions here intendedDomainState := currentState.DeepCopy() isGlobalDomain := currentState.IsGlobalDomain currentActiveCluster := currentState.ReplicationConfig.ActiveClusterName wasActiveActive := currentState.ReplicationConfig.IsActiveActive() now := d.timeSource.Now() // by default, we assume failovers are of type force failoverType := constants.FailoverTypeForce var activeClusterChanged bool var configurationChanged bool // will be set to the domain notification version after the update intendedDomainState.FailoverNotificationVersion = types.UndefinedFailoverVersion // not used except for graceful failover requests, but specifically set to -1 // so as to be explicitly undefined intendedDomainState.PreviousFailoverVersion = constants.InitialPreviousFailoverVersion // by default, we assume a force failover and that any preexisting graceful failover state is invalidated // if there's a duration of failover time to occur (such as in graceful failover) this will be re-set. // But if there's an existing graceful failover and a subsequent force // we want to ensure that it'll be ended immmediately. intendedDomainState.FailoverEndTime = nil // Update replication config replicationCfg, replicationConfigChanged, activeClusterChanged, err := d.updateReplicationConfig( currentState.Info.Name, intendedDomainState.ReplicationConfig, updateRequest, ) if err != nil { return nil, err } if !activeClusterChanged && !replicationConfigChanged { return nil, errInvalidFailoverNoChangeDetected } intendedDomainState.ReplicationConfig = replicationCfg err = d.ensureUpdateOrFailoverCooldown(currentState) if err != nil { return nil, err } // if the failover 'graceful' - as indicated as having a FailoverTimeoutInSeconds, // then we set some additional parameters for the graceful failover if updateRequest.FailoverTimeoutInSeconds != nil { gracefulFailoverEndTime, previousFailoverVersion, err := d.handleGracefulFailover( updateRequest, intendedDomainState.ReplicationConfig, currentActiveCluster, currentState.FailoverEndTime, currentState.FailoverVersion, activeClusterChanged, isGlobalDomain, ) if err != nil { return nil, err } failoverType = constants.FailoverTypeGrace intendedDomainState.FailoverEndTime = gracefulFailoverEndTime intendedDomainState.PreviousFailoverVersion = previousFailoverVersion } // replication config is a subset of config, configurationChanged = replicationConfigChanged if err = d.domainAttrValidator.validateDomainConfig(intendedDomainState.Config); err != nil { return nil, err } err = d.validateDomainReplicationConfigForFailover(intendedDomainState.ReplicationConfig, configurationChanged, activeClusterChanged) if err != nil { return nil, err } // increment the in the configuration fencing token to ensure that configurations // are applied in order if configurationChanged { intendedDomainState.ConfigVersion++ } intendedDomainState.FailoverVersion = d.clusterMetadata.GetNextFailoverVersion( intendedDomainState.ReplicationConfig.ActiveClusterName, currentState.FailoverVersion, updateRequest.Name, ) // this is an intended failover step, to capture the current cluster's domain configuration // (represented by the notification-version counter) and set it when running failover to allow // systems like graceful failover to dedup failover processes and callbacks intendedDomainState.FailoverNotificationVersion = notificationVersion isActiveActive := intendedDomainState.ReplicationConfig.IsActiveActive() if wasActiveActive || isActiveActive { // if the domain was ever active-active, we bump the failover-version at the domain level // to ensure that it gets incremented every single time, even though the failover may be only // at the cluster-attribute level (ie the FailoverVersion may just go up by the failover-increment // but otherwise be a noop - the actual active cluster may not have changed)). // // The reason for this is caution and simplicity at the time of writing: // It's harmless to bump and this simplifies thinking about failover events // through out the rest of the Cadence codebase. We can reliably assume that every failover // event that has meaningful changes only needs to subscribe to this fencing token. // to detect changes. // // Any parts of the system (domain-callbacks, domain cache etc) that watch for // failover events and may trigger some action or cache invalidation will be watching // the domain-level failver counter for changes. Therefore, by bumping it even for // cases where it isn't changing, we ensure all these other subprocesses will // take intendedDomainState.FailoverVersion = d.clusterMetadata.GetNextFailoverVersion( intendedDomainState.ReplicationConfig.ActiveClusterName, currentState.FailoverVersion+1, updateRequest.Name, ) } // the domain-data table is only updated for active-passive domain failovers // as a historical backwards compatibility measure. // Going forward, any history use-cases should rely on the FailoverHistory endpoint which // supports all failover types and more than a handful of entries. if !wasActiveActive && !isActiveActive { err = updateFailoverHistoryInDomainData(intendedDomainState.Info, d.config, NewFailoverEvent( now, failoverType, ¤tActiveCluster, updateRequest.ActiveClusterName, updateRequest.FailoverReason, )) if err != nil { d.logger.Warn("failed to update failover history", tag.Error(err)) } } updateReq := createUpdateRequest( intendedDomainState.Info, intendedDomainState.Config, intendedDomainState.ReplicationConfig, intendedDomainState.ConfigVersion, intendedDomainState.FailoverVersion, intendedDomainState.FailoverNotificationVersion, intendedDomainState.FailoverEndTime, intendedDomainState.PreviousFailoverVersion, now, notificationVersion, ) err = d.domainManager.UpdateDomain(ctx, &updateReq) if err != nil { return nil, err } if err = d.domainReplicator.HandleTransmissionTask( ctx, types.DomainOperationUpdate, intendedDomainState.Info, intendedDomainState.Config, intendedDomainState.ReplicationConfig, intendedDomainState.ConfigVersion, intendedDomainState.FailoverVersion, intendedDomainState.PreviousFailoverVersion, isGlobalDomain, ); err != nil { return nil, err } response := &types.UpdateDomainResponse{ IsGlobalDomain: isGlobalDomain, FailoverVersion: intendedDomainState.FailoverVersion, } response.DomainInfo, response.Configuration, response.ReplicationConfiguration = d.createResponse(intendedDomainState.Info, intendedDomainState.Config, intendedDomainState.ReplicationConfig) err = d.updateDomainAuditLog(ctx, currentState, intendedDomainState, persistence.DomainAuditOperationTypeFailover, "domain failover") if err != nil { return nil, err } d.logger.Info("Failover request succeeded", tag.WorkflowDomainName(intendedDomainState.Info.Name), tag.WorkflowDomainID(intendedDomainState.Info.ID), ) return response, nil } func (d *handlerImpl) updateDomainAuditLog(ctx context.Context, currentState *persistence.GetDomainResponse, intendedDomainState *persistence.GetDomainResponse, operationType persistence.DomainAuditOperationType, comment string, ) error { if d.domainAuditManager == nil { return nil } if !d.config.EnableDomainAuditLogging() { return nil } // Must be a UUID v7, since we need a time value as well eventID, err := guuid.NewV7() if err != nil { return err } // the creation time is used in the database as a partition but passed around // embedded in the eventUUID for ergonomics. This means that users wishing // to get an audit entry by ID do not need to know the creation time in advance // since it's embedded in the sorting-values of the UUID. creationTime := time.Unix(eventID.Time().UnixTime()) _, err = d.domainAuditManager.CreateDomainAuditLog(ctx, &persistence.CreateDomainAuditLogRequest{ DomainID: intendedDomainState.GetInfo().GetID(), EventID: eventID.String(), CreatedTime: creationTime, StateBefore: currentState, StateAfter: intendedDomainState, OperationType: operationType, Comment: comment, }) if err != nil { d.logger.Error("Failed to create domain audit log", tag.WorkflowDomainID(intendedDomainState.GetInfo().GetID()), tag.Error(err), ) // Log the error but don't fail the operation - audit logging is best effort // to avoid breaking critical domain operations } return nil } // updateGlobalDomainConfiguration handles the update of a global domain configuration // this excludes failover/active_cluster/active_clusters updates. They are grouped under // forms of failover func (d *handlerImpl) updateGlobalDomainConfiguration(ctx context.Context, updateRequest *types.UpdateDomainRequest, currentDomainState *persistence.GetDomainResponse, notificationVersion int64, ) (*types.UpdateDomainResponse, error) { // intendedDomainState will be modified // into the intended shape by the functions here intendedDomainState := currentDomainState.DeepCopy() configVersion := currentDomainState.ConfigVersion failoverVersion := currentDomainState.FailoverVersion isGlobalDomain := currentDomainState.IsGlobalDomain now := d.timeSource.Now() // whether history archival config changed historyArchivalConfigChanged := false // whether visibility archival config changed visibilityArchivalConfigChanged := false // whether active cluster is changed activeClusterChanged := false // whether anything other than active cluster is changed configurationChanged := false // Update history archival state historyArchivalConfigChanged, err := d.updateHistoryArchivalState(intendedDomainState.Config, updateRequest) if err != nil { return nil, err } // Update visibility archival state visibilityArchivalConfigChanged, err = d.updateVisibilityArchivalState(intendedDomainState.Config, updateRequest) if err != nil { return nil, err } // Update domain info info, domainInfoChanged := d.updateDomainInfo( updateRequest, intendedDomainState.Info, ) // Update domain config config, domainConfigChanged, err := d.updateDomainConfiguration( updateRequest.GetName(), intendedDomainState.Config, updateRequest, ) if err != nil { return nil, err } // Update domain bad binary config, deleteBinaryChanged, err := d.updateDeleteBadBinary( config, updateRequest.DeleteBadBinary, ) if err != nil { return nil, err } // Update replication config replicationConfig, replicationConfigChanged, activeClusterChanged, err := d.updateReplicationConfig( intendedDomainState.Info.Name, intendedDomainState.ReplicationConfig, updateRequest, ) if err != nil { return nil, err } configurationChanged = historyArchivalConfigChanged || visibilityArchivalConfigChanged || domainInfoChanged || domainConfigChanged || deleteBinaryChanged || replicationConfigChanged if err = d.domainAttrValidator.validateDomainConfig(config); err != nil { return nil, err } err = d.validateGlobalDomainReplicationConfigForUpdateDomain(replicationConfig, configurationChanged, activeClusterChanged) if err != nil { return nil, err } err = d.ensureUpdateOrFailoverCooldown(currentDomainState) if err != nil { return nil, err } if configurationChanged || activeClusterChanged { // set the versions if configurationChanged { configVersion++ } updateReq := createUpdateRequest( info, config, replicationConfig, configVersion, failoverVersion, currentDomainState.FailoverNotificationVersion, intendedDomainState.FailoverEndTime, intendedDomainState.PreviousFailoverVersion, now, notificationVersion, ) err = d.domainManager.UpdateDomain(ctx, &updateReq) if err != nil { return nil, err } } if err = d.domainReplicator.HandleTransmissionTask( ctx, types.DomainOperationUpdate, info, config, replicationConfig, configVersion, failoverVersion, intendedDomainState.PreviousFailoverVersion, isGlobalDomain, ); err != nil { return nil, err } response := &types.UpdateDomainResponse{ IsGlobalDomain: isGlobalDomain, FailoverVersion: failoverVersion, } response.DomainInfo, response.Configuration, response.ReplicationConfiguration = d.createResponse(info, config, replicationConfig) // Construct GetDomainResponse for audit log with the final updated values domainStateAfterUpdate := &persistence.GetDomainResponse{ Info: info, Config: config, ReplicationConfig: replicationConfig, IsGlobalDomain: isGlobalDomain, ConfigVersion: configVersion, FailoverVersion: failoverVersion, FailoverNotificationVersion: intendedDomainState.FailoverNotificationVersion, PreviousFailoverVersion: intendedDomainState.PreviousFailoverVersion, FailoverEndTime: intendedDomainState.FailoverEndTime, LastUpdatedTime: now.UnixNano(), NotificationVersion: notificationVersion, } err = d.updateDomainAuditLog(ctx, currentDomainState, domainStateAfterUpdate, persistence.DomainAuditOperationTypeUpdate, "domain updated") if err != nil { return nil, err } d.logger.Info("Update domain succeeded", tag.WorkflowDomainName(info.Name), tag.WorkflowDomainID(info.ID), ) return response, nil } func (d *handlerImpl) updateLocalDomain(ctx context.Context, updateRequest *types.UpdateDomainRequest, currentState *persistence.GetDomainResponse, notificationVersion int64, ) (*types.UpdateDomainResponse, error) { err := d.domainAttrValidator.validateLocalDomainUpdateRequest(updateRequest) if err != nil { return nil, err } // whether history archival config changed historyArchivalConfigChanged := false // whether visibility archival config changed visibilityArchivalConfigChanged := false // whether anything other than active cluster is changed configurationChanged := false intendedDomainState := currentState.DeepCopy() configVersion := currentState.ConfigVersion now := d.timeSource.Now() // Update history archival state historyArchivalConfigChanged, err = d.updateHistoryArchivalState(intendedDomainState.Config, updateRequest) if err != nil { return nil, err } // Update visibility archival state visibilityArchivalConfigChanged, err = d.updateVisibilityArchivalState(intendedDomainState.Config, updateRequest) if err != nil { return nil, err } // Update domain info info, domainInfoChanged := d.updateDomainInfo( updateRequest, intendedDomainState.Info, ) // Update domain config config, domainConfigChanged, err := d.updateDomainConfiguration( updateRequest.GetName(), intendedDomainState.Config, updateRequest, ) if err != nil { return nil, err } // Update domain bad binary config, deleteBinaryChanged, err := d.updateDeleteBadBinary( config, updateRequest.DeleteBadBinary, ) if err != nil { return nil, err } configurationChanged = historyArchivalConfigChanged || visibilityArchivalConfigChanged || domainInfoChanged || domainConfigChanged || deleteBinaryChanged if err = d.domainAttrValidator.validateDomainConfig(config); err != nil { return nil, err } if err = d.domainAttrValidator.validateDomainReplicationConfigForLocalDomain( intendedDomainState.ReplicationConfig, ); err != nil { return nil, err } if configurationChanged { // set the versions if configurationChanged { configVersion = intendedDomainState.ConfigVersion + 1 } updateReq := createUpdateRequest( info, config, intendedDomainState.ReplicationConfig, configVersion, intendedDomainState.FailoverVersion, intendedDomainState.FailoverNotificationVersion, intendedDomainState.FailoverEndTime, intendedDomainState.PreviousFailoverVersion, now, notificationVersion, ) err = d.domainManager.UpdateDomain(ctx, &updateReq) if err != nil { return nil, err } err = d.updateDomainAuditLog(ctx, currentState, intendedDomainState, persistence.DomainAuditOperationTypeUpdate, "domain updated") if err != nil { return nil, err } } response := &types.UpdateDomainResponse{ IsGlobalDomain: false, FailoverVersion: intendedDomainState.FailoverVersion, } response.DomainInfo, response.Configuration, response.ReplicationConfiguration = d.createResponse(info, config, intendedDomainState.ReplicationConfig) return response, nil } // FailoverDomain handles failover of the domain to a different cluster func (d *handlerImpl) FailoverDomain( ctx context.Context, failoverRequest *types.FailoverDomainRequest, ) (*types.FailoverDomainResponse, error) { metadata, err := d.domainManager.GetMetadata(ctx) if err != nil { return nil, err } notificationVersion := metadata.NotificationVersion currentDomainState, err := d.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{Name: failoverRequest.GetDomainName()}) if err != nil { return nil, err } err = d.validateDomainFailoverRequest(failoverRequest, currentDomainState) if err != nil { return nil, err } response, err := d.handleFailoverRequest( ctx, failoverRequest.ToUpdateDomainRequest(), currentDomainState, notificationVersion, ) if err != nil { return nil, err } return response.ToFailoverDomainResponse(), nil } // DeleteDomain deletes a domain func (d *handlerImpl) DeleteDomain( ctx context.Context, deleteRequest *types.DeleteDomainRequest, ) error { getResponse, err := d.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{Name: deleteRequest.GetName()}) if err != nil { return err } isGlobalDomain := getResponse.IsGlobalDomain if isGlobalDomain && !d.clusterMetadata.IsPrimaryCluster() { return errNotPrimaryCluster } deleteReq := &persistence.DeleteDomainByNameRequest{ Name: getResponse.Info.Name, } err = d.domainManager.DeleteDomainByName(ctx, deleteReq) if err != nil { return err } if isGlobalDomain { if err := d.domainReplicator.HandleTransmissionTask( ctx, types.DomainOperationDelete, getResponse.Info, getResponse.Config, getResponse.ReplicationConfig, getResponse.ConfigVersion, getResponse.FailoverVersion, getResponse.PreviousFailoverVersion, isGlobalDomain, ); err != nil { return fmt.Errorf("unable to delete a domain in replica cluster: %v", err) } } err = d.updateDomainAuditLog(ctx, getResponse, nil, persistence.DomainAuditOperationTypeDelete, "domain deleted") if err != nil { return err } d.logger.Info("Delete domain succeeded", tag.WorkflowDomainName(getResponse.Info.Name), tag.WorkflowDomainID(getResponse.Info.ID), ) return nil } // DeprecateDomain deprecates a domain func (d *handlerImpl) DeprecateDomain( ctx context.Context, deprecateRequest *types.DeprecateDomainRequest, ) error { // must get the metadata (notificationVersion) first // this version can be regarded as the lock on the v2 domain table // and since we do not know which table will return the domain afterwards // this call has to be made metadata, err := d.domainManager.GetMetadata(ctx) if err != nil { return err } notificationVersion := metadata.NotificationVersion getResponse, err := d.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{Name: deprecateRequest.GetName()}) if err != nil { return err } isGlobalDomain := getResponse.IsGlobalDomain if isGlobalDomain && !d.clusterMetadata.IsPrimaryCluster() { return errNotPrimaryCluster } getResponse.ConfigVersion = getResponse.ConfigVersion + 1 getResponse.Info.Status = persistence.DomainStatusDeprecated updateReq := createUpdateRequest( getResponse.Info, getResponse.Config, getResponse.ReplicationConfig, getResponse.ConfigVersion, getResponse.FailoverVersion, getResponse.FailoverNotificationVersion, getResponse.FailoverEndTime, getResponse.PreviousFailoverVersion, d.timeSource.Now(), notificationVersion, ) err = d.domainManager.UpdateDomain(ctx, &updateReq) if err != nil { return err } if isGlobalDomain { if err := d.domainReplicator.HandleTransmissionTask( ctx, types.DomainOperationUpdate, getResponse.Info, getResponse.Config, getResponse.ReplicationConfig, getResponse.ConfigVersion, getResponse.FailoverVersion, getResponse.PreviousFailoverVersion, isGlobalDomain, ); err != nil { return err } } d.logger.Info("DeprecateDomain domain succeeded", tag.WorkflowDomainName(getResponse.Info.Name), tag.WorkflowDomainID(getResponse.Info.ID), ) domainStateAfterDeprecate := &persistence.GetDomainResponse{ Info: getResponse.Info, } err = d.updateDomainAuditLog(ctx, getResponse, domainStateAfterDeprecate, persistence.DomainAuditOperationTypeDeprecate, "domain deprecated") if err != nil { return err } return nil } // UpdateIsolationGroups is used for draining and undraining of isolation-groups for a domain. // Like the isolation-group API, this controller expects Upsert semantics for // isolation-groups and does not modify any other domain information. // // Isolation-groups are regional in their configuration scope, so it's expected that this upsert // includes configuration for both clusters every time. // // The update is handled like other domain updates in that they expected to be replicated. So // unlike the global isolation-group API it shouldn't be necessary to call func (d *handlerImpl) UpdateIsolationGroups( ctx context.Context, updateRequest types.UpdateDomainIsolationGroupsRequest, ) error { // must get the metadata (notificationVersion) first // this version can be regarded as the lock on the v2 domain table // and since we do not know which table will return the domain afterwards // this call has to be made metadata, err := d.domainManager.GetMetadata(ctx) if err != nil { return err } notificationVersion := metadata.NotificationVersion if updateRequest.IsolationGroups == nil { return fmt.Errorf("invalid request, isolationGroup configuration must be set: Got: %v", updateRequest) } currentDomainConfig, err := d.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.Domain}) if err != nil { return err } if currentDomainConfig.Config == nil { return fmt.Errorf("unable to load config for domain as expected") } configVersion := currentDomainConfig.ConfigVersion lastUpdatedTime := time.Unix(0, currentDomainConfig.LastUpdatedTime) // Check the failover cool down time if lastUpdatedTime.Add(d.config.FailoverCoolDown(currentDomainConfig.Info.Name)).After(d.timeSource.Now()) { return errDomainUpdateTooFrequent } if !d.clusterMetadata.IsPrimaryCluster() && currentDomainConfig.IsGlobalDomain { return errNotPrimaryCluster } configVersion++ lastUpdatedTime = d.timeSource.Now() // Mutate the domain config to perform the isolation-group update currentDomainConfig.Config.IsolationGroups = updateRequest.IsolationGroups updateReq := createUpdateRequest( currentDomainConfig.Info, currentDomainConfig.Config, currentDomainConfig.ReplicationConfig, configVersion, currentDomainConfig.FailoverVersion, currentDomainConfig.FailoverNotificationVersion, currentDomainConfig.FailoverEndTime, currentDomainConfig.PreviousFailoverVersion, lastUpdatedTime, notificationVersion, ) err = d.domainManager.UpdateDomain(ctx, &updateReq) if err != nil { return err } if currentDomainConfig.IsGlobalDomain { // One might reasonably wonder what value there is in replication of isolation-group information - info which is // regional and therefore of no value to the other region? // Probably not a lot, in and of itself, however, the isolation-group information is stored // in the domain configuration fields in the domain tables. Access and updates to those records is // done through a replicated mechanism with explicit versioning and conflict resolution. // Therefore, in order to avoid making an already complex mechanisim much more difficult to understand, // the data is replicated in the same way so as to try and make things less confusing when both codepaths // are updating the table: // - versions like the confiugration version are updated in the same manner // - the last-updated timestamps are updated in the same manner if err := d.domainReplicator.HandleTransmissionTask( ctx, types.DomainOperationUpdate, currentDomainConfig.Info, currentDomainConfig.Config, currentDomainConfig.ReplicationConfig, configVersion, currentDomainConfig.FailoverVersion, currentDomainConfig.PreviousFailoverVersion, currentDomainConfig.IsGlobalDomain, ); err != nil { return err } } d.logger.Info("isolation group update succeeded", tag.WorkflowDomainName(currentDomainConfig.Info.Name), tag.WorkflowDomainID(currentDomainConfig.Info.ID), ) return nil } func (d *handlerImpl) UpdateAsyncWorkflowConfiguraton( ctx context.Context, updateRequest types.UpdateDomainAsyncWorkflowConfiguratonRequest, ) error { // must get the metadata (notificationVersion) first // this version can be regarded as the lock on the v2 domain table // and since we do not know which table will return the domain afterwards // this call has to be made metadata, err := d.domainManager.GetMetadata(ctx) if err != nil { return err } notificationVersion := metadata.NotificationVersion currentDomainConfig, err := d.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.Domain}) if err != nil { return err } if currentDomainConfig.Config == nil { return fmt.Errorf("unable to load config for domain as expected") } configVersion := currentDomainConfig.ConfigVersion lastUpdatedTime := time.Unix(0, currentDomainConfig.LastUpdatedTime) // Check the failover cool down time if lastUpdatedTime.Add(d.config.FailoverCoolDown(currentDomainConfig.Info.Name)).After(d.timeSource.Now()) { return errDomainUpdateTooFrequent } if !d.clusterMetadata.IsPrimaryCluster() && currentDomainConfig.IsGlobalDomain { return errNotPrimaryCluster } configVersion++ lastUpdatedTime = d.timeSource.Now() // Mutate the domain config to perform the async wf config update if updateRequest.Configuration == nil { // this is a delete request so empty all the fields currentDomainConfig.Config.AsyncWorkflowConfig = types.AsyncWorkflowConfiguration{} } else { currentDomainConfig.Config.AsyncWorkflowConfig = *updateRequest.Configuration } d.logger.Debug("async workflow queue config update", tag.Dynamic("config", currentDomainConfig)) updateReq := createUpdateRequest( currentDomainConfig.Info, currentDomainConfig.Config, currentDomainConfig.ReplicationConfig, configVersion, currentDomainConfig.FailoverVersion, currentDomainConfig.FailoverNotificationVersion, currentDomainConfig.FailoverEndTime, currentDomainConfig.PreviousFailoverVersion, lastUpdatedTime, notificationVersion, ) err = d.domainManager.UpdateDomain(ctx, &updateReq) if err != nil { return err } if currentDomainConfig.IsGlobalDomain { if err := d.domainReplicator.HandleTransmissionTask( ctx, types.DomainOperationUpdate, currentDomainConfig.Info, currentDomainConfig.Config, currentDomainConfig.ReplicationConfig, configVersion, currentDomainConfig.FailoverVersion, currentDomainConfig.PreviousFailoverVersion, currentDomainConfig.IsGlobalDomain, ); err != nil { return err } } err = d.updateDomainAuditLog(ctx, currentDomainConfig, currentDomainConfig, persistence.DomainAuditOperationTypeUpdate, "async workflow queue config update") if err != nil { return err } d.logger.Info("async workflow queue config update succeeded", tag.WorkflowDomainName(currentDomainConfig.Info.Name), tag.WorkflowDomainID(currentDomainConfig.Info.ID), ) return nil } func (d *handlerImpl) createResponse( info *persistence.DomainInfo, config *persistence.DomainConfig, replicationConfig *persistence.DomainReplicationConfig, ) (*types.DomainInfo, *types.DomainConfiguration, *types.DomainReplicationConfiguration) { infoResult := &types.DomainInfo{ Name: info.Name, Status: getDomainStatus(info), Description: info.Description, OwnerEmail: info.OwnerEmail, Data: info.Data, UUID: info.ID, } configResult := &types.DomainConfiguration{ EmitMetric: config.EmitMetric, WorkflowExecutionRetentionPeriodInDays: config.Retention, HistoryArchivalStatus: config.HistoryArchivalStatus.Ptr(), HistoryArchivalURI: config.HistoryArchivalURI, VisibilityArchivalStatus: config.VisibilityArchivalStatus.Ptr(), VisibilityArchivalURI: config.VisibilityArchivalURI, BadBinaries: &config.BadBinaries, IsolationGroups: &config.IsolationGroups, AsyncWorkflowConfig: &config.AsyncWorkflowConfig, } clusters := []*types.ClusterReplicationConfiguration{} for _, cluster := range replicationConfig.Clusters { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: cluster.ClusterName, }) } replicationConfigResult := &types.DomainReplicationConfiguration{ ActiveClusterName: replicationConfig.ActiveClusterName, Clusters: clusters, ActiveClusters: replicationConfig.ActiveClusters, } return infoResult, configResult, replicationConfigResult } func (d *handlerImpl) mergeBadBinaries( old map[string]*types.BadBinaryInfo, new map[string]*types.BadBinaryInfo, createTimeNano int64, ) types.BadBinaries { if old == nil { old = map[string]*types.BadBinaryInfo{} } for k, v := range new { v.CreatedTimeNano = common.Int64Ptr(createTimeNano) old[k] = v } return types.BadBinaries{ Binaries: old, } } func (d *handlerImpl) mergeDomainData( old map[string]string, new map[string]string, ) map[string]string { if old == nil { old = map[string]string{} } for k, v := range new { old[k] = v } return old } func (d *handlerImpl) toArchivalRegisterEvent( status *types.ArchivalStatus, URI string, defaultStatus types.ArchivalStatus, defaultURI string, ) (*ArchivalEvent, error) { event := &ArchivalEvent{ status: status, URI: URI, defaultURI: defaultURI, } if event.status == nil { event.status = defaultStatus.Ptr() } if err := event.validate(); err != nil { return nil, err } return event, nil } func (d *handlerImpl) toArchivalUpdateEvent( status *types.ArchivalStatus, URI string, defaultURI string, ) (*ArchivalEvent, error) { event := &ArchivalEvent{ status: status, URI: URI, defaultURI: defaultURI, } if err := event.validate(); err != nil { return nil, err } return event, nil } func (d *handlerImpl) validateHistoryArchivalURI(URIString string) error { URI, err := archiver.NewURI(URIString) if err != nil { return err } archiver, err := d.archiverProvider.GetHistoryArchiver(URI.Scheme(), service.Frontend) if err != nil { return err } return archiver.ValidateURI(URI) } func (d *handlerImpl) validateVisibilityArchivalURI(URIString string) error { URI, err := archiver.NewURI(URIString) if err != nil { return err } archiver, err := d.archiverProvider.GetVisibilityArchiver(URI.Scheme(), service.Frontend) if err != nil { return err } return archiver.ValidateURI(URI) } func (d *handlerImpl) getHistoryArchivalState( config *persistence.DomainConfig, updateRequest *types.UpdateDomainRequest, ) (*ArchivalState, bool, error) { currentHistoryArchivalState := &ArchivalState{ Status: config.HistoryArchivalStatus, URI: config.HistoryArchivalURI, } clusterHistoryArchivalConfig := d.archivalMetadata.GetHistoryConfig() if clusterHistoryArchivalConfig.ClusterConfiguredForArchival() { archivalEvent, err := d.toArchivalUpdateEvent( updateRequest.HistoryArchivalStatus, updateRequest.GetHistoryArchivalURI(), clusterHistoryArchivalConfig.GetDomainDefaultURI(), ) if err != nil { return currentHistoryArchivalState, false, err } return currentHistoryArchivalState.getNextState(archivalEvent, d.validateHistoryArchivalURI) } return currentHistoryArchivalState, false, nil } func (d *handlerImpl) updateHistoryArchivalState( config *persistence.DomainConfig, updateRequest *types.UpdateDomainRequest, ) (bool, error) { historyArchivalState, changed, err := d.getHistoryArchivalState(config, updateRequest) if err != nil { return false, err } if changed { config.HistoryArchivalStatus = historyArchivalState.Status config.HistoryArchivalURI = historyArchivalState.URI } return changed, nil } func (d *handlerImpl) getVisibilityArchivalState( config *persistence.DomainConfig, updateRequest *types.UpdateDomainRequest, ) (*ArchivalState, bool, error) { currentVisibilityArchivalState := &ArchivalState{ Status: config.VisibilityArchivalStatus, URI: config.VisibilityArchivalURI, } clusterVisibilityArchivalConfig := d.archivalMetadata.GetVisibilityConfig() if clusterVisibilityArchivalConfig.ClusterConfiguredForArchival() { archivalEvent, err := d.toArchivalUpdateEvent( updateRequest.VisibilityArchivalStatus, updateRequest.GetVisibilityArchivalURI(), clusterVisibilityArchivalConfig.GetDomainDefaultURI(), ) if err != nil { return currentVisibilityArchivalState, false, err } return currentVisibilityArchivalState.getNextState(archivalEvent, d.validateVisibilityArchivalURI) } return currentVisibilityArchivalState, false, nil } func (d *handlerImpl) updateVisibilityArchivalState( config *persistence.DomainConfig, updateRequest *types.UpdateDomainRequest, ) (bool, error) { visibilityArchivalState, changed, err := d.getVisibilityArchivalState( config, updateRequest, ) if err != nil { return false, err } if changed { config.VisibilityArchivalStatus = visibilityArchivalState.Status config.VisibilityArchivalURI = visibilityArchivalState.URI } return changed, nil } func (d *handlerImpl) updateDomainInfo( updateRequest *types.UpdateDomainRequest, currentDomainInfo *persistence.DomainInfo, ) (*persistence.DomainInfo, bool) { isDomainUpdated := false if updateRequest.Description != nil { isDomainUpdated = true currentDomainInfo.Description = *updateRequest.Description } if updateRequest.OwnerEmail != nil { isDomainUpdated = true currentDomainInfo.OwnerEmail = *updateRequest.OwnerEmail } if updateRequest.Data != nil { isDomainUpdated = true // only do merging currentDomainInfo.Data = d.mergeDomainData(currentDomainInfo.Data, updateRequest.Data) } return currentDomainInfo, isDomainUpdated } func (d *handlerImpl) updateDomainConfiguration( domainName string, config *persistence.DomainConfig, updateRequest *types.UpdateDomainRequest, ) (*persistence.DomainConfig, bool, error) { isConfigChanged := false if updateRequest.EmitMetric != nil { isConfigChanged = true config.EmitMetric = *updateRequest.EmitMetric } if updateRequest.WorkflowExecutionRetentionPeriodInDays != nil { isConfigChanged = true config.Retention = *updateRequest.WorkflowExecutionRetentionPeriodInDays } if updateRequest.BadBinaries != nil { maxLength := d.config.MaxBadBinaryCount(domainName) // only do merging config.BadBinaries = d.mergeBadBinaries(config.BadBinaries.Binaries, updateRequest.BadBinaries.Binaries, d.timeSource.Now().UnixNano()) if len(config.BadBinaries.Binaries) > maxLength { return config, isConfigChanged, &types.BadRequestError{ Message: fmt.Sprintf("Total resetBinaries cannot exceed the max limit: %v", maxLength), } } } return config, isConfigChanged, nil } func (d *handlerImpl) updateDeleteBadBinary( config *persistence.DomainConfig, deleteBadBinary *string, ) (*persistence.DomainConfig, bool, error) { if deleteBadBinary != nil { _, ok := config.BadBinaries.Binaries[*deleteBadBinary] if !ok { return config, false, &types.BadRequestError{ Message: fmt.Sprintf("Bad binary checksum %v doesn't exists.", *deleteBadBinary), } } delete(config.BadBinaries.Binaries, *deleteBadBinary) return config, true, nil } return config, false, nil } // updateReplicationConfig is the function which takes the input request and current state and edits and returns it // to the desired state by merging the request values with the current state. // replicationConfigChanged being turned on will trigger an increment in the configVersion. // activeClusterChanged indicates a failover is happening and a failover version is to be incremented func (d *handlerImpl) updateReplicationConfig( domainName string, config *persistence.DomainReplicationConfig, updateRequest *types.UpdateDomainRequest, ) ( mutatedCfg *persistence.DomainReplicationConfig, replicationConfigChanged bool, activeClusterChanged bool, err error, ) { if len(updateRequest.Clusters) != 0 { replicationConfigChanged = true clustersNew := []*persistence.ClusterReplicationConfig{} for _, clusterConfig := range updateRequest.Clusters { clustersNew = append(clustersNew, &persistence.ClusterReplicationConfig{ ClusterName: clusterConfig.GetClusterName(), }) } if err := d.domainAttrValidator.validateDomainReplicationConfigClustersDoesNotRemove( config.Clusters, clustersNew, ); err != nil { d.logger.Warn("removing replica clusters from domain replication group", tag.Error(err)) } config.Clusters = clustersNew } if updateRequest.ActiveClusterName != nil { activeClusterChanged = true config.ActiveClusterName = *updateRequest.ActiveClusterName } err = d.domainAttrValidator.validateActiveActiveDomainReplicationConfig(updateRequest.ActiveClusters) if err != nil { return nil, false, false, err } if updateRequest != nil && updateRequest.ActiveClusters != nil && updateRequest.ActiveClusters.AttributeScopes != nil { result, isCh := d.buildActiveActiveClusterScopesFromUpdateRequest(updateRequest, config, domainName) if isCh { if config.ActiveClusters == nil { config.ActiveClusters = &types.ActiveClusters{ AttributeScopes: result.AttributeScopes, } } config.ActiveClusters.AttributeScopes = result.AttributeScopes activeClusterChanged = true replicationConfigChanged = true } } return config, replicationConfigChanged, activeClusterChanged, nil } func (d *handlerImpl) handleGracefulFailover( updateRequest *types.UpdateDomainRequest, replicationConfig *persistence.DomainReplicationConfig, currentActiveCluster string, gracefulFailoverEndTime *int64, failoverVersion int64, activeClusterChanged bool, isGlobalDomain bool, ) (*int64, int64, error) { // must update active cluster on a global domain if !activeClusterChanged || !isGlobalDomain || replicationConfig.IsActiveActive() { return nil, 0, errInvalidGracefulFailover } // must start with the passive -> active cluster if replicationConfig.ActiveClusterName != d.clusterMetadata.GetCurrentClusterName() { return nil, 0, errCannotDoGracefulFailoverFromCluster } if replicationConfig.ActiveClusterName == currentActiveCluster { return nil, 0, errGracefulFailoverInActiveCluster } // cannot have concurrent failover if gracefulFailoverEndTime != nil { return nil, 0, errOngoingGracefulFailover } endTime := d.timeSource.Now().Add(time.Duration(updateRequest.GetFailoverTimeoutInSeconds()) * time.Second).UnixNano() previousFailoverVersion := failoverVersion return &endTime, previousFailoverVersion, nil } func (d *handlerImpl) validateGlobalDomainReplicationConfigForUpdateDomain( replicationConfig *persistence.DomainReplicationConfig, configurationChanged bool, activeClusterChanged bool, ) error { var err error if err = d.domainAttrValidator.validateDomainReplicationConfigForGlobalDomain( replicationConfig, ); err != nil { return err } if configurationChanged && activeClusterChanged && !replicationConfig.IsActiveActive() { return errCannotDoDomainFailoverAndUpdate } if !activeClusterChanged && !d.clusterMetadata.IsPrimaryCluster() { return errNotPrimaryCluster } return nil } // validateDomainFailoverRequest is the handler method for the FailoverDomain // handler. It can receive request to failover domains in a variety of combinations // including invalid ones. func (d *handlerImpl) validateDomainFailoverRequest( request *types.FailoverDomainRequest, currentDomainState *persistence.GetDomainResponse, ) error { if request == nil { return &types.BadRequestError{Message: "Request cannot be nil"} } if request.DomainName == "" { return &types.BadRequestError{Message: "DomainName cannot be empty"} } if !currentDomainState.IsGlobalDomain { return errLocalDomainsCannotFailover } if request.ActiveClusters == nil && request.DomainActiveClusterName == nil { return &types.BadRequestError{Message: "Domain's ActiveClusterName or ActiveClusters must be set to failover the domain"} } return nil } // validateDomainReplicationConfigForFailover is to check if the replication config // is valid and sane for failovers. It is only for global domains func (d *handlerImpl) validateDomainReplicationConfigForFailover( replicationConfig *persistence.DomainReplicationConfig, configurationChanged bool, activeClusterChanged bool, ) error { // todo (add any additional failover validation here) return d.validateGlobalDomainReplicationConfigForUpdateDomain(replicationConfig, configurationChanged, activeClusterChanged) } func (d *handlerImpl) activeClustersFromRegisterRequest(registerRequest *types.RegisterDomainRequest) (*types.ActiveClusters, error) { if !registerRequest.GetIsGlobalDomain() || registerRequest.ActiveClusters == nil { // local or active-passive domain return nil, nil } clusters := d.clusterMetadata.GetAllClusterInfo() activeClustersScopes := make(map[string]types.ClusterAttributeScope) // Handle AttributeScopes from the request if registerRequest.ActiveClusters != nil && registerRequest.ActiveClusters.AttributeScopes != nil { for scope, scopeData := range registerRequest.ActiveClusters.AttributeScopes { newScopeData := types.ClusterAttributeScope{ ClusterAttributes: make(map[string]types.ActiveClusterInfo), } for attribute, clusterInfo := range scopeData.ClusterAttributes { clusterMetadata, ok := clusters[clusterInfo.ActiveClusterName] if !ok { return nil, &types.BadRequestError{ Message: fmt.Sprintf("Cluster %v not found. Domain cannot be registered in this cluster for scope %q and attribute %q", clusterInfo.ActiveClusterName, scope, attribute), } } newScopeData.ClusterAttributes[attribute] = types.ActiveClusterInfo{ ActiveClusterName: clusterInfo.ActiveClusterName, FailoverVersion: clusterMetadata.InitialFailoverVersion, } } activeClustersScopes[scope] = newScopeData } } if len(activeClustersScopes) == 0 { return nil, nil } return &types.ActiveClusters{ AttributeScopes: activeClustersScopes, }, nil } func getDomainStatus(info *persistence.DomainInfo) *types.DomainStatus { switch info.Status { case persistence.DomainStatusRegistered: v := types.DomainStatusRegistered return &v case persistence.DomainStatusDeprecated: v := types.DomainStatusDeprecated return &v case persistence.DomainStatusDeleted: v := types.DomainStatusDeleted return &v } return nil } // Maps fields onto an updateDomain Request // it's really important that this explicitly calls out each field to ensure no fields get missed or dropped func createUpdateRequest( info *persistence.DomainInfo, config *persistence.DomainConfig, replicationConfig *persistence.DomainReplicationConfig, configVersion int64, failoverVersion int64, failoverNotificationVersion int64, failoverEndTime *int64, previousFailoverVersion int64, lastUpdatedTime time.Time, notificationVersion int64, ) persistence.UpdateDomainRequest { return persistence.UpdateDomainRequest{ Info: info, Config: config, ReplicationConfig: replicationConfig, ConfigVersion: configVersion, FailoverVersion: failoverVersion, FailoverNotificationVersion: failoverNotificationVersion, FailoverEndTime: failoverEndTime, PreviousFailoverVersion: previousFailoverVersion, LastUpdatedTime: lastUpdatedTime.UnixNano(), NotificationVersion: notificationVersion, } } func updateFailoverHistoryInDomainData( info *persistence.DomainInfo, config Config, failoverEvent FailoverEvent, ) error { data := info.Data if info.Data == nil { data = make(map[string]string) } var failoverHistory []FailoverEvent _ = json.Unmarshal([]byte(data[constants.DomainDataKeyForFailoverHistory]), &failoverHistory) failoverHistory = append([]FailoverEvent{failoverEvent}, failoverHistory...) // Truncate the history to the max size failoverHistoryJSON, err := json.Marshal(failoverHistory[:min(config.FailoverHistoryMaxSize(info.Name), len(failoverHistory))]) if err != nil { return err } data[constants.DomainDataKeyForFailoverHistory] = string(failoverHistoryJSON) info.Data = data return nil } func NewFailoverEvent( eventTime time.Time, failoverType constants.FailoverType, fromCluster *string, toCluster *string, reason *string, ) FailoverEvent { res := FailoverEvent{ EventTime: eventTime, FailoverType: failoverType.String(), } if fromCluster != nil { res.FromCluster = *fromCluster } if toCluster != nil { res.ToCluster = *toCluster } if reason != nil { res.Reason = *reason } return res } func (d *handlerImpl) buildActiveActiveClusterScopesFromUpdateRequest(updateRequest *types.UpdateDomainRequest, config *persistence.DomainReplicationConfig, domainName string) (out *types.ActiveClusters, isChanged bool) { var existing *types.ActiveClusters if config != nil && config.ActiveClusters != nil { existing = config.ActiveClusters } if updateRequest.ActiveClusters == nil || updateRequest.ActiveClusters.AttributeScopes == nil { return existing, false } // ensure a failover version is set for the incoming request for scope, scopeData := range updateRequest.ActiveClusters.AttributeScopes { for attribute, activeCluster := range scopeData.ClusterAttributes { currentFailoverVersion := types.UndefinedFailoverVersion if config != nil && config.ActiveClusters != nil { fo, err := config.ActiveClusters.GetFailoverVersionForAttribute(scope, attribute) if err == nil { currentFailoverVersion = fo } } nextFailoverVersion := d.clusterMetadata.GetNextFailoverVersion(activeCluster.ActiveClusterName, currentFailoverVersion, domainName) activeCluster.FailoverVersion = nextFailoverVersion scopeData.ClusterAttributes[attribute] = activeCluster } } // if there's no existing active cluster info, use what's in the request if existing == nil { return updateRequest.ActiveClusters, true } // if, on the other hand, there are existing active clusters, merge them with the incoming request result, isChanged := mergeActiveActiveScopes(config.ActiveClusters, updateRequest.ActiveClusters) if isChanged { return result, isChanged } return config.ActiveClusters, false } ================================================ FILE: common/domain/handler_MasterCluster_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "context" "encoding/json" "log" "os" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" dc "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" persistencetests "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/sql/sqlplugin/sqlite" "github.com/uber/cadence/common/types" ) type ( domainHandlerGlobalDomainEnabledPrimaryClusterSuite struct { *persistencetests.TestBase minRetentionDays int maxBadBinaryCount int failoverHistoryMaxSize int domainManager persistence.DomainManager mockProducer *mocks.KafkaProducer mockDomainReplicator Replicator archivalMetadata archiver.ArchivalMetadata mockArchiverProvider *provider.MockArchiverProvider mockDomainAuditManager persistence.DomainAuditManager handler *handlerImpl } ) func TestDomainHandlerGlobalDomainEnabledPrimaryClusterSuite(t *testing.T) { if testing.Verbose() { log.SetOutput(os.Stdout) } s := new(domainHandlerGlobalDomainEnabledPrimaryClusterSuite) s.setupTestBase(t) suite.Run(t, s) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) setupTestBase(t *testing.T) { sqliteTestBaseOptions := sqlite.GetTestClusterOption() sqliteTestBaseOptions.ClusterMetadata = cluster.GetTestClusterMetadata(true) s.TestBase = persistencetests.NewTestBaseWithSQL(t, sqliteTestBaseOptions) s.Setup() } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TearDownSuite() { s.TestBase.TearDownWorkflowStore() } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) SetupTest() { s.setupTestBase(s.T()) logger := s.Logger dcCollection := dc.NewCollection(dc.NewNopClient(), logger) s.minRetentionDays = 1 s.maxBadBinaryCount = 10 s.failoverHistoryMaxSize = 5 s.domainManager = s.TestBase.DomainManager s.mockProducer = &mocks.KafkaProducer{} s.mockDomainReplicator = NewDomainReplicator(s.mockProducer, logger) s.mockDomainAuditManager = persistence.NewMockDomainAuditManager(s.Controller) s.archivalMetadata = archiver.NewArchivalMetadata( dcCollection, "", false, "", false, &config.ArchivalDomainDefaults{}, ) s.mockArchiverProvider = &provider.MockArchiverProvider{} domainConfig := Config{ MinRetentionDays: dynamicproperties.GetIntPropertyFn(s.minRetentionDays), MaxBadBinaryCount: dynamicproperties.GetIntPropertyFilteredByDomain(s.maxBadBinaryCount), FailoverCoolDown: dynamicproperties.GetDurationPropertyFnFilteredByDomain(0 * time.Second), FailoverHistoryMaxSize: dynamicproperties.GetIntPropertyFilteredByDomain(s.failoverHistoryMaxSize), EnableDomainAuditLogging: dynamicproperties.GetBoolPropertyFn(false), } s.handler = NewHandler( domainConfig, logger, s.domainManager, s.mockDomainAuditManager, s.ClusterMetadata, s.mockDomainReplicator, s.archivalMetadata, s.mockArchiverProvider, clock.NewMockedTimeSource(), ).(*handlerImpl) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TearDownTest() { s.mockProducer.AssertExpectations(s.T()) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestRegisterGetDomain_LocalDomain_InvalidCluster() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true activeClusterName := cluster.TestAlternativeClusterName clusters := []*types.ClusterReplicationConfiguration{ { ClusterName: activeClusterName, }, } data := map[string]string{"some random key": "some random value"} isGlobalDomain := false err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: activeClusterName, Data: data, IsGlobalDomain: isGlobalDomain, }) s.IsType(&types.BadRequestError{}, err) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestRegisterGetDomain_LocalDomain_AllDefault() { domainName := s.getRandomDomainName() isGlobalDomain := false var clusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } retention := int32(1) err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, IsGlobalDomain: isGlobalDomain, WorkflowExecutionRetentionPeriodInDays: retention, }) s.Nil(err) resp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) s.NotEmpty(resp.DomainInfo.GetUUID()) resp.DomainInfo.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: "", OwnerEmail: "", Data: nil, UUID: "", }, resp.DomainInfo) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, resp.Configuration) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Clusters: clusters, }, resp.ReplicationConfiguration) s.Equal(constants.EmptyVersion, resp.GetFailoverVersion()) s.Equal(isGlobalDomain, resp.GetIsGlobalDomain()) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestRegisterGetDomain_LocalDomain_NoDefault() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true activeClusterName := cluster.TestCurrentClusterName clusters := []*types.ClusterReplicationConfiguration{ { ClusterName: activeClusterName, }, } data := map[string]string{"some random key": "some random value"} isGlobalDomain := false var expectedClusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { expectedClusters = append(expectedClusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: activeClusterName, Data: data, IsGlobalDomain: isGlobalDomain, }) s.Nil(err) resp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) s.NotEmpty(resp.DomainInfo.GetUUID()) resp.DomainInfo.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, resp.DomainInfo) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, resp.Configuration) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Clusters: expectedClusters, }, resp.ReplicationConfiguration) s.Equal(constants.EmptyVersion, resp.GetFailoverVersion()) s.Equal(isGlobalDomain, resp.GetIsGlobalDomain()) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestUpdateGetDomain_LocalDomain_NoAttrSet() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true data := map[string]string{"some random key": "some random value"} var clusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } isGlobalDomain := false err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Data: data, IsGlobalDomain: isGlobalDomain, }) s.Nil(err) fnTest := func(info *types.DomainInfo, config *types.DomainConfiguration, replicationConfig *types.DomainReplicationConfiguration, isGlobalDomain bool, failoverVersion int64) { s.NotEmpty(info.GetUUID()) info.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, info) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, config) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Clusters: clusters, }, replicationConfig) s.Equal(constants.EmptyVersion, failoverVersion) s.Equal(isGlobalDomain, isGlobalDomain) } updateResp, err := s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, }) s.Nil(err) fnTest( updateResp.DomainInfo, updateResp.Configuration, updateResp.ReplicationConfiguration, updateResp.GetIsGlobalDomain(), updateResp.GetFailoverVersion(), ) getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) fnTest( getResp.DomainInfo, getResp.Configuration, getResp.ReplicationConfiguration, getResp.GetIsGlobalDomain(), getResp.GetFailoverVersion(), ) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestUpdateGetDomain_LocalDomain_AllAttrSet() { domainName := s.getRandomDomainName() isGlobalDomain := false err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, IsGlobalDomain: isGlobalDomain, WorkflowExecutionRetentionPeriodInDays: 1, }) s.Nil(err) description := "some random description" email := "some random email" retention := int32(7) emitMetric := true data := map[string]string{"some random key": "some random value"} var clusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } fnTest := func(info *types.DomainInfo, config *types.DomainConfiguration, replicationConfig *types.DomainReplicationConfiguration, isGlobalDomain bool, failoverVersion int64) { s.NotEmpty(info.GetUUID()) info.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, info) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, config) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Clusters: clusters, }, replicationConfig) s.Equal(constants.EmptyVersion, failoverVersion) s.Equal(isGlobalDomain, isGlobalDomain) } updateResp, err := s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, Description: common.StringPtr(description), OwnerEmail: common.StringPtr(email), Data: data, WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(retention), EmitMetric: common.BoolPtr(emitMetric), HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: common.StringPtr(""), VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: common.StringPtr(""), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, }) s.Nil(err) fnTest( updateResp.DomainInfo, updateResp.Configuration, updateResp.ReplicationConfiguration, updateResp.GetIsGlobalDomain(), updateResp.GetFailoverVersion(), ) getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) fnTest( getResp.DomainInfo, getResp.Configuration, getResp.ReplicationConfiguration, getResp.GetIsGlobalDomain(), getResp.GetFailoverVersion(), ) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestDeprecateGetDomain_LocalDomain() { domainName := s.getRandomDomainName() domain := s.setupLocalDomain(domainName) err := s.handler.DeprecateDomain(context.Background(), &types.DeprecateDomainRequest{ Name: domainName, }) s.Nil(err) expectedResp := domain expectedResp.DomainInfo.Status = types.DomainStatusDeprecated.Ptr() getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) assertDomainEqual(s.Suite, getResp, expectedResp) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestRegisterGetDomain_GlobalDomain_AllDefault() { domainName := s.getRandomDomainName() isGlobalDomain := true var clusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Once() retention := int32(1) err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, IsGlobalDomain: isGlobalDomain, WorkflowExecutionRetentionPeriodInDays: retention, }) s.Nil(err) resp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) s.NotEmpty(resp.DomainInfo.GetUUID()) resp.DomainInfo.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: "", OwnerEmail: "", Data: nil, UUID: "", }, resp.DomainInfo) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, resp.Configuration) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Clusters: clusters, }, resp.ReplicationConfiguration) s.Equal(s.ClusterMetadata.GetNextFailoverVersion(s.ClusterMetadata.GetCurrentClusterName(), 0, "some-domaain"), resp.GetFailoverVersion()) s.Equal(isGlobalDomain, resp.GetIsGlobalDomain()) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestRegisterGetDomain_GlobalDomain_NoDefault() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true activeClusterName := "" clusters := []*types.ClusterReplicationConfiguration{} for clusterName := range s.ClusterMetadata.GetEnabledClusterInfo() { if clusterName != s.ClusterMetadata.GetCurrentClusterName() { activeClusterName = clusterName } clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: clusterName, }) } s.True(len(activeClusterName) > 0) s.True(len(clusters) > 1) data := map[string]string{"some random key": "some random value"} isGlobalDomain := true s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Once() err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: activeClusterName, Data: data, IsGlobalDomain: isGlobalDomain, }) s.Nil(err) resp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) s.NotEmpty(resp.DomainInfo.GetUUID()) resp.DomainInfo.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, resp.DomainInfo) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, resp.Configuration) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: activeClusterName, Clusters: clusters, }, resp.ReplicationConfiguration) s.Equal(s.ClusterMetadata.GetNextFailoverVersion(activeClusterName, 0, "some-domain"), resp.GetFailoverVersion()) s.Equal(isGlobalDomain, resp.GetIsGlobalDomain()) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestUpdateGetDomain_GlobalDomain_NoAttrSet() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true data := map[string]string{"some random key": "some random value"} activeClusterName := "" clusters := []*types.ClusterReplicationConfiguration{} for clusterName := range s.ClusterMetadata.GetEnabledClusterInfo() { if clusterName != s.ClusterMetadata.GetCurrentClusterName() { activeClusterName = clusterName } clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: clusterName, }) } s.True(len(activeClusterName) > 0) s.True(len(clusters) > 1) isGlobalDomain := true s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Twice() err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: activeClusterName, Data: data, IsGlobalDomain: isGlobalDomain, }) s.Nil(err) fnTest := func(info *types.DomainInfo, config *types.DomainConfiguration, replicationConfig *types.DomainReplicationConfiguration, isGlobalDomain bool, failoverVersion int64) { s.NotEmpty(info.GetUUID()) info.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, info) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, config) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: activeClusterName, Clusters: clusters, }, replicationConfig) s.Equal(s.ClusterMetadata.GetNextFailoverVersion(activeClusterName, 0, "some-domain"), failoverVersion) s.Equal(isGlobalDomain, isGlobalDomain) } updateResp, err := s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, }) s.Nil(err) fnTest( updateResp.DomainInfo, updateResp.Configuration, updateResp.ReplicationConfiguration, updateResp.GetIsGlobalDomain(), updateResp.GetFailoverVersion(), ) getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) fnTest( getResp.DomainInfo, getResp.Configuration, getResp.ReplicationConfiguration, getResp.GetIsGlobalDomain(), getResp.GetFailoverVersion(), ) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestUpdateGetDomain_GlobalDomain_AllAttrSet() { domainName := s.getRandomDomainName() activeClusterName := "" clusters := []*types.ClusterReplicationConfiguration{} for clusterName := range s.ClusterMetadata.GetEnabledClusterInfo() { if clusterName != s.ClusterMetadata.GetCurrentClusterName() { activeClusterName = clusterName } clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: clusterName, }) } s.True(len(activeClusterName) > 0) s.True(len(clusters) > 1) isGlobalDomain := true s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Twice() err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, IsGlobalDomain: isGlobalDomain, Clusters: clusters, ActiveClusterName: activeClusterName, WorkflowExecutionRetentionPeriodInDays: 1, }) s.Nil(err) description := "some random description" email := "some random email" retention := int32(7) emitMetric := true data := map[string]string{"some random key": "some random value"} fnTest := func(info *types.DomainInfo, config *types.DomainConfiguration, replicationConfig *types.DomainReplicationConfiguration, isGlobalDomain bool, failoverVersion int64) { s.NotEmpty(info.GetUUID()) info.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, info) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, config) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: activeClusterName, Clusters: clusters, }, replicationConfig) s.Equal(s.ClusterMetadata.GetNextFailoverVersion(activeClusterName, 0, "some-domain"), failoverVersion) s.Equal(isGlobalDomain, isGlobalDomain) } updateResp, err := s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, Description: common.StringPtr(description), OwnerEmail: common.StringPtr(email), Data: data, WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(retention), EmitMetric: common.BoolPtr(emitMetric), HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: common.StringPtr(""), VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: common.StringPtr(""), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, ActiveClusterName: nil, Clusters: clusters, }) s.Nil(err) fnTest( updateResp.DomainInfo, updateResp.Configuration, updateResp.ReplicationConfiguration, updateResp.GetIsGlobalDomain(), updateResp.GetFailoverVersion(), ) getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) fnTest( getResp.DomainInfo, getResp.Configuration, getResp.ReplicationConfiguration, getResp.GetIsGlobalDomain(), getResp.GetFailoverVersion(), ) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestUpdateGetDomain_GlobalDomain_Failover() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true data := map[string]string{"some random key": "some random value"} prevActiveClusterName := "" nextActiveClusterName := s.ClusterMetadata.GetCurrentClusterName() clusters := []*types.ClusterReplicationConfiguration{} for clusterName := range s.ClusterMetadata.GetEnabledClusterInfo() { if clusterName != nextActiveClusterName { prevActiveClusterName = clusterName } clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: clusterName, }) } s.True(len(prevActiveClusterName) > 0) s.True(len(clusters) > 1) isGlobalDomain := true s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Twice() err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: prevActiveClusterName, Data: data, IsGlobalDomain: isGlobalDomain, }) s.Nil(err) var failoverHistory []FailoverEvent failoverHistory = append(failoverHistory, FailoverEvent{ EventTime: s.handler.timeSource.Now(), FromCluster: prevActiveClusterName, ToCluster: nextActiveClusterName, FailoverType: constants.FailoverType(constants.FailoverTypeForce).String(), }) failoverHistoryJSON, _ := json.Marshal(failoverHistory) data[constants.DomainDataKeyForFailoverHistory] = string(failoverHistoryJSON) fnTest := func( info *types.DomainInfo, config *types.DomainConfiguration, replicationConfig *types.DomainReplicationConfiguration, isGlobalDomain bool, failoverVersion int64) { s.T().Helper() s.NotEmpty(info.GetUUID()) info.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, info) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, config) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: nextActiveClusterName, Clusters: clusters, }, replicationConfig) s.Equal(s.ClusterMetadata.GetNextFailoverVersion( nextActiveClusterName, s.ClusterMetadata.GetNextFailoverVersion(prevActiveClusterName, 0, "some-domain"), "some-domain"), failoverVersion) s.Equal(isGlobalDomain, isGlobalDomain) } updateResp, err := s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, ActiveClusterName: common.StringPtr(s.ClusterMetadata.GetCurrentClusterName()), }) s.Nil(err) fnTest( updateResp.DomainInfo, updateResp.Configuration, updateResp.ReplicationConfiguration, updateResp.GetIsGlobalDomain(), updateResp.GetFailoverVersion(), ) getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) fnTest( getResp.DomainInfo, getResp.Configuration, getResp.ReplicationConfiguration, getResp.GetIsGlobalDomain(), getResp.GetFailoverVersion(), ) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestUpdateDomain_CoolDown() { domainConfig := Config{ MinRetentionDays: dynamicproperties.GetIntPropertyFn(s.minRetentionDays), MaxBadBinaryCount: dynamicproperties.GetIntPropertyFilteredByDomain(s.maxBadBinaryCount), FailoverCoolDown: dynamicproperties.GetDurationPropertyFnFilteredByDomain(10000 * time.Second), EnableDomainAuditLogging: dynamicproperties.GetBoolPropertyFn(false), } s.handler = NewHandler( domainConfig, s.Logger, s.domainManager, s.mockDomainAuditManager, s.ClusterMetadata, s.mockDomainReplicator, s.archivalMetadata, s.mockArchiverProvider, clock.NewRealTimeSource(), ).(*handlerImpl) domainName := s.getRandomDomainName() isGlobalDomain := true var clusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Once() retention := int32(1) err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, IsGlobalDomain: isGlobalDomain, WorkflowExecutionRetentionPeriodInDays: retention, }) s.Nil(err) resp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) s.NotEmpty(resp.DomainInfo.GetUUID()) resp.DomainInfo.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: "", OwnerEmail: "", Data: nil, UUID: "", }, resp.DomainInfo) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, resp.Configuration) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Clusters: clusters, }, resp.ReplicationConfiguration) s.Equal(s.ClusterMetadata.GetNextFailoverVersion(s.ClusterMetadata.GetCurrentClusterName(), 0, "some-domain"), resp.GetFailoverVersion()) s.Equal(isGlobalDomain, resp.GetIsGlobalDomain()) _, err = s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, Description: common.StringPtr("test1"), }) s.Error(err) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) TestDeprecateGetDomain_GlobalDomain() { domainName := s.getRandomDomainName() domain := s.setupGlobalDomain(domainName) s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Once() err := s.handler.DeprecateDomain(context.Background(), &types.DeprecateDomainRequest{ Name: domainName, }) s.Nil(err) expectedResp := domain expectedResp.DomainInfo.Status = types.DomainStatusDeprecated.Ptr() getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) assertDomainEqual(s.Suite, getResp, expectedResp) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) getRandomDomainName() string { return "domain" + uuid.New() } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) setupLocalDomain(domainName string) *types.DescribeDomainResponse { return setupLocalDomain(s.Suite, s.handler, s.ClusterMetadata, domainName) } func (s *domainHandlerGlobalDomainEnabledPrimaryClusterSuite) setupGlobalDomain(domainName string) *types.DescribeDomainResponse { s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Once() return setupGlobalDomain(s.Suite, s.handler, s.ClusterMetadata, domainName) } func setupGlobalDomain(s *suite.Suite, handler *handlerImpl, clusterMetadata cluster.Metadata, domainName string) *types.DescribeDomainResponse { description := "some random description" email := "some random email" retention := int32(7) emitMetric := true data := map[string]string{"some random key": "some random value"} activeClusterName := "" clusters := []*types.ClusterReplicationConfiguration{} for clusterName := range clusterMetadata.GetEnabledClusterInfo() { if clusterName != clusterMetadata.GetCurrentClusterName() { activeClusterName = clusterName } clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: clusterName, }) } s.True(len(activeClusterName) > 0) s.True(len(clusters) > 1) isGlobalDomain := true err := handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: activeClusterName, Data: data, IsGlobalDomain: isGlobalDomain, }) s.Nil(err) getResp, err := handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) return getResp } func setupLocalDomain(s *suite.Suite, handler *handlerImpl, clusterMetadata cluster.Metadata, domainName string) *types.DescribeDomainResponse { description := "some random description" email := "some random email" retention := int32(7) emitMetric := true data := map[string]string{"some random key": "some random value"} var clusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(clusterMetadata.GetCurrentClusterName(), nil) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } err := handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: clusterMetadata.GetCurrentClusterName(), Data: data, }) s.Nil(err) getResp, err := handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) return getResp } func assertDomainEqual(s *suite.Suite, actual, expected *types.DescribeDomainResponse) { s.NotEmpty(actual.DomainInfo.GetUUID()) expected.DomainInfo.UUID = actual.DomainInfo.GetUUID() s.Equal(expected, actual) } ================================================ FILE: common/domain/handler_NotMasterCluster_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "context" "encoding/json" "log" "os" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" dc "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" persistencetests "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/sql/sqlplugin/sqlite" "github.com/uber/cadence/common/types" ) type ( domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite struct { *persistencetests.TestBase minRetentionDays int maxBadBinaryCount int failoverHistoryMaxSize int domainManager persistence.DomainManager mockProducer *mocks.KafkaProducer mockDomainReplicator Replicator archivalMetadata archiver.ArchivalMetadata mockArchiverProvider *provider.MockArchiverProvider mockDomainAuditManager persistence.DomainAuditManager handler *handlerImpl } ) func TestDomainHandlerGlobalDomainEnabledNotPrimaryClusterSuite(t *testing.T) { if testing.Verbose() { log.SetOutput(os.Stdout) } s := new(domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) s.setupTestBase(t) suite.Run(t, s) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) setupTestBase(t *testing.T) { sqliteTestBaseOptions := sqlite.GetTestClusterOption() sqliteTestBaseOptions.ClusterMetadata = cluster.GetTestClusterMetadata(false) s.TestBase = persistencetests.NewTestBaseWithSQL(t, sqliteTestBaseOptions) s.Setup() } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TearDownSuite() { s.TestBase.TearDownWorkflowStore() } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) SetupTest() { s.setupTestBase(s.T()) logger := s.Logger dcCollection := dc.NewCollection(dc.NewNopClient(), logger) s.minRetentionDays = 1 s.maxBadBinaryCount = 10 s.failoverHistoryMaxSize = 5 s.domainManager = s.TestBase.DomainManager s.mockProducer = &mocks.KafkaProducer{} s.mockDomainReplicator = NewDomainReplicator(s.mockProducer, logger) s.mockDomainAuditManager = persistence.NewMockDomainAuditManager(s.Controller) s.archivalMetadata = archiver.NewArchivalMetadata( dcCollection, "", false, "", false, &config.ArchivalDomainDefaults{}, ) s.mockArchiverProvider = &provider.MockArchiverProvider{} domainConfig := Config{ MinRetentionDays: dynamicproperties.GetIntPropertyFn(s.minRetentionDays), MaxBadBinaryCount: dynamicproperties.GetIntPropertyFilteredByDomain(s.maxBadBinaryCount), FailoverCoolDown: dynamicproperties.GetDurationPropertyFnFilteredByDomain(0 * time.Second), FailoverHistoryMaxSize: dynamicproperties.GetIntPropertyFilteredByDomain(s.failoverHistoryMaxSize), EnableDomainAuditLogging: dynamicproperties.GetBoolPropertyFn(false), } s.handler = NewHandler( domainConfig, logger, s.domainManager, s.mockDomainAuditManager, s.ClusterMetadata, s.mockDomainReplicator, s.archivalMetadata, s.mockArchiverProvider, clock.NewMockedTimeSource(), ).(*handlerImpl) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TearDownTest() { s.mockProducer.AssertExpectations(s.T()) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestRegisterGetDomain_LocalDomain_AllDefault() { domainName := s.getRandomDomainName() isGlobalDomain := false var clusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } retention := int32(1) err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, IsGlobalDomain: isGlobalDomain, WorkflowExecutionRetentionPeriodInDays: retention, }) s.Nil(err) resp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) s.NotEmpty(resp.DomainInfo.GetUUID()) resp.DomainInfo.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: "", OwnerEmail: "", Data: nil, UUID: "", }, resp.DomainInfo) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, resp.Configuration) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Clusters: clusters, }, resp.ReplicationConfiguration) s.Equal(constants.EmptyVersion, resp.GetFailoverVersion()) s.Equal(isGlobalDomain, resp.GetIsGlobalDomain()) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestRegisterGetDomain_LocalDomain_NoDefault() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true activeClusterName := cluster.TestCurrentClusterName clusters := []*types.ClusterReplicationConfiguration{ { ClusterName: activeClusterName, }, } data := map[string]string{"some random key": "some random value"} isGlobalDomain := false var expectedClusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { expectedClusters = append(expectedClusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: activeClusterName, Data: data, IsGlobalDomain: isGlobalDomain, }) s.Nil(err) resp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) s.NotEmpty(resp.DomainInfo.GetUUID()) resp.DomainInfo.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, resp.DomainInfo) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, resp.Configuration) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Clusters: expectedClusters, }, resp.ReplicationConfiguration) s.Equal(constants.EmptyVersion, resp.GetFailoverVersion()) s.Equal(isGlobalDomain, resp.GetIsGlobalDomain()) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestUpdateGetDomain_LocalDomain_NoAttrSet() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true data := map[string]string{"some random key": "some random value"} var clusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } isGlobalDomain := false err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Data: data, IsGlobalDomain: isGlobalDomain, }) s.Nil(err) fnTest := func(info *types.DomainInfo, config *types.DomainConfiguration, replicationConfig *types.DomainReplicationConfiguration, isGlobalDomain bool, failoverVersion int64) { s.NotEmpty(info.GetUUID()) info.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, info) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, config) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Clusters: clusters, }, replicationConfig) s.Equal(constants.EmptyVersion, failoverVersion) s.Equal(isGlobalDomain, isGlobalDomain) } updateResp, err := s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, }) s.Nil(err) fnTest( updateResp.DomainInfo, updateResp.Configuration, updateResp.ReplicationConfiguration, updateResp.GetIsGlobalDomain(), updateResp.GetFailoverVersion(), ) getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) fnTest( getResp.DomainInfo, getResp.Configuration, getResp.ReplicationConfiguration, getResp.GetIsGlobalDomain(), getResp.GetFailoverVersion(), ) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestUpdateGetDomain_LocalDomain_AllAttrSet() { domainName := s.getRandomDomainName() isGlobalDomain := false err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, IsGlobalDomain: isGlobalDomain, WorkflowExecutionRetentionPeriodInDays: 1, }) s.Nil(err) description := "some random description" email := "some random email" retention := int32(7) emitMetric := true data := map[string]string{"some random key": "some random value"} var clusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } fnTest := func(info *types.DomainInfo, config *types.DomainConfiguration, replicationConfig *types.DomainReplicationConfiguration, isGlobalDomain bool, failoverVersion int64) { s.NotEmpty(info.GetUUID()) info.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, info) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, config) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: s.ClusterMetadata.GetCurrentClusterName(), Clusters: clusters, }, replicationConfig) s.Equal(constants.EmptyVersion, failoverVersion) s.Equal(isGlobalDomain, isGlobalDomain) } updateResp, err := s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, Description: common.StringPtr(description), OwnerEmail: common.StringPtr(email), Data: data, WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(retention), EmitMetric: common.BoolPtr(emitMetric), HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: common.StringPtr(""), VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: common.StringPtr(""), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, }) s.Nil(err) fnTest( updateResp.DomainInfo, updateResp.Configuration, updateResp.ReplicationConfiguration, updateResp.GetIsGlobalDomain(), updateResp.GetFailoverVersion(), ) getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) fnTest( getResp.DomainInfo, getResp.Configuration, getResp.ReplicationConfiguration, getResp.GetIsGlobalDomain(), getResp.GetFailoverVersion(), ) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestDeprecateGetDomain_LocalDomain() { domainName := s.getRandomDomainName() domain := s.setupLocalDomain(domainName) err := s.handler.DeprecateDomain(context.Background(), &types.DeprecateDomainRequest{ Name: domainName, }) s.Nil(err) expectedResp := domain expectedResp.DomainInfo.Status = types.DomainStatusDeprecated.Ptr() getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) assertDomainEqual(s.Suite, getResp, expectedResp) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestRegisterGetDomain_GlobalDomain_AllDefault() { domainName := s.getRandomDomainName() isGlobalDomain := true var clusters []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } s.Equal(1, len(clusters)) err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, IsGlobalDomain: isGlobalDomain, }) s.IsType(&types.BadRequestError{}, err) resp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.IsType(&types.EntityNotExistsError{}, err) s.Nil(resp) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestRegisterGetDomain_GlobalDomain_NoDefault() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true activeClusterName := "" clusters := []*types.ClusterReplicationConfiguration{} for clusterName := range s.ClusterMetadata.GetEnabledClusterInfo() { if clusterName != s.ClusterMetadata.GetCurrentClusterName() { activeClusterName = clusterName } clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: clusterName, }) } s.True(len(activeClusterName) > 0) s.True(len(clusters) > 1) data := map[string]string{"some random key": "some random value"} isGlobalDomain := true err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: email, WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: common.BoolPtr(emitMetric), Clusters: clusters, ActiveClusterName: activeClusterName, Data: data, IsGlobalDomain: isGlobalDomain, }) s.IsType(&types.BadRequestError{}, err) resp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.IsType(&types.EntityNotExistsError{}, err) s.Nil(resp) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestUpdateGetDomain_GlobalDomain_NoAttrSet() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true activeClusterName := "" clusters := []*persistence.ClusterReplicationConfig{} for clusterName := range s.ClusterMetadata.GetEnabledClusterInfo() { if clusterName != s.ClusterMetadata.GetCurrentClusterName() { activeClusterName = clusterName } clusters = append(clusters, &persistence.ClusterReplicationConfig{ ClusterName: clusterName, }) } s.True(len(activeClusterName) > 0) s.True(len(clusters) > 1) data := map[string]string{"some random key": "some random value"} isGlobalDomain := true _, err := s.DomainManager.CreateDomain(context.Background(), &persistence.CreateDomainRequest{ Info: &persistence.DomainInfo{ ID: uuid.New(), Name: domainName, Status: persistence.DomainStatusRegistered, Description: description, OwnerEmail: email, Data: data, }, Config: &persistence.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: activeClusterName, Clusters: clusters, }, IsGlobalDomain: isGlobalDomain, ConfigVersion: 0, FailoverVersion: s.ClusterMetadata.GetNextFailoverVersion(activeClusterName, 0, "some-domain"), }) s.Nil(err) resp, err := s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, }) s.IsType(&types.BadRequestError{}, err) s.Nil(resp) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestUpdateGetDomain_GlobalDomain_AllAttrSet() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true activeClusterName := "" clusters := []*types.ClusterReplicationConfiguration{} clustersDB := []*persistence.ClusterReplicationConfig{} for clusterName := range s.ClusterMetadata.GetEnabledClusterInfo() { if clusterName != s.ClusterMetadata.GetCurrentClusterName() { activeClusterName = clusterName } clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: clusterName, }) clustersDB = append(clustersDB, &persistence.ClusterReplicationConfig{ ClusterName: clusterName, }) } s.True(len(activeClusterName) > 0) s.True(len(clusters) > 1) s.True(len(clustersDB) > 1) data := map[string]string{"some random key": "some random value"} isGlobalDomain := true _, err := s.DomainManager.CreateDomain(context.Background(), &persistence.CreateDomainRequest{ Info: &persistence.DomainInfo{ ID: uuid.New(), Name: domainName, Status: persistence.DomainStatusRegistered, Description: "", OwnerEmail: "", Data: map[string]string{}, }, Config: &persistence.DomainConfig{ Retention: 0, EmitMetric: false, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: activeClusterName, Clusters: clustersDB, }, IsGlobalDomain: isGlobalDomain, ConfigVersion: 0, FailoverVersion: s.ClusterMetadata.GetNextFailoverVersion(activeClusterName, 0, "some-domain"), }) s.Nil(err) updateResp, err := s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, Description: common.StringPtr(description), OwnerEmail: common.StringPtr(email), Data: data, WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(retention), EmitMetric: common.BoolPtr(emitMetric), HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: common.StringPtr(""), VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: common.StringPtr(""), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, ActiveClusterName: nil, Clusters: clusters, }) s.IsType(&types.BadRequestError{}, err) s.Nil(updateResp) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestUpdateGetDomain_GlobalDomain_Failover() { domainName := s.getRandomDomainName() description := "some random description" email := "some random email" retention := int32(7) emitMetric := true prevActiveClusterName := "" nextActiveClusterName := s.ClusterMetadata.GetCurrentClusterName() clusters := []*types.ClusterReplicationConfiguration{} clustersDB := []*persistence.ClusterReplicationConfig{} for clusterName := range s.ClusterMetadata.GetEnabledClusterInfo() { if clusterName != s.ClusterMetadata.GetCurrentClusterName() { prevActiveClusterName = clusterName } clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: clusterName, }) clustersDB = append(clustersDB, &persistence.ClusterReplicationConfig{ ClusterName: clusterName, }) } s.True(len(prevActiveClusterName) > 0) s.True(len(clusters) > 1) s.True(len(clustersDB) > 1) data := map[string]string{"some random key": "some random value"} isGlobalDomain := true _, err := s.DomainManager.CreateDomain(context.Background(), &persistence.CreateDomainRequest{ Info: &persistence.DomainInfo{ ID: uuid.New(), Name: domainName, Status: persistence.DomainStatusRegistered, Description: description, OwnerEmail: email, Data: data, }, Config: &persistence.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: prevActiveClusterName, Clusters: clustersDB, }, IsGlobalDomain: isGlobalDomain, ConfigVersion: 0, FailoverVersion: s.ClusterMetadata.GetNextFailoverVersion(prevActiveClusterName, 0, "some-domain"), }) s.Nil(err) var failoverHistory []FailoverEvent failoverHistory = append(failoverHistory, FailoverEvent{EventTime: s.handler.timeSource.Now(), FromCluster: prevActiveClusterName, ToCluster: nextActiveClusterName, FailoverType: constants.FailoverType(constants.FailoverTypeForce).String()}) failoverHistoryJSON, _ := json.Marshal(failoverHistory) data[constants.DomainDataKeyForFailoverHistory] = string(failoverHistoryJSON) fnTest := func(info *types.DomainInfo, config *types.DomainConfiguration, replicationConfig *types.DomainReplicationConfiguration, isGlobalDomain bool, failoverVersion int64) { s.NotEmpty(info.GetUUID()) info.UUID = "" s.Equal(&types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: description, OwnerEmail: email, Data: data, UUID: "", }, info) s.Equal(&types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, config) s.Equal(&types.DomainReplicationConfiguration{ ActiveClusterName: nextActiveClusterName, Clusters: clusters, }, replicationConfig) s.Equal(s.ClusterMetadata.GetNextFailoverVersion( nextActiveClusterName, s.ClusterMetadata.GetNextFailoverVersion(prevActiveClusterName, 0, "some-domain"), "some-domain"), failoverVersion) s.Equal(isGlobalDomain, isGlobalDomain) } s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Once() updateResp, err := s.handler.UpdateDomain(context.Background(), &types.UpdateDomainRequest{ Name: domainName, ActiveClusterName: common.StringPtr(s.ClusterMetadata.GetCurrentClusterName()), }) s.Nil(err) fnTest( updateResp.DomainInfo, updateResp.Configuration, updateResp.ReplicationConfiguration, updateResp.GetIsGlobalDomain(), updateResp.GetFailoverVersion(), ) getResp, err := s.handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) fnTest( getResp.DomainInfo, getResp.Configuration, getResp.ReplicationConfiguration, getResp.GetIsGlobalDomain(), getResp.GetFailoverVersion(), ) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) TestDeprecateGetDomain_GlobalDomain() { domainName := s.getRandomDomainName() s.setupGlobalDomainWithMetadataManager(domainName) err := s.handler.DeprecateDomain(context.Background(), &types.DeprecateDomainRequest{ Name: domainName, }) s.IsType(&types.BadRequestError{}, err) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) getRandomDomainName() string { return "domain" + uuid.New() } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) setupLocalDomain(domainName string) *types.DescribeDomainResponse { return setupLocalDomain(s.Suite, s.handler, s.ClusterMetadata, domainName) } func (s *domainHandlerGlobalDomainEnabledNotPrimaryClusterSuite) setupGlobalDomainWithMetadataManager(domainName string) *types.DescribeDomainResponse { return setupGlobalDomainWithMetadataManager(s.Suite, s.handler, s.ClusterMetadata, s.DomainManager, domainName) } func setupGlobalDomainWithMetadataManager(s *suite.Suite, handler *handlerImpl, clusterMetadata cluster.Metadata, domainManager persistence.DomainManager, domainName string) *types.DescribeDomainResponse { description := "some random description" email := "some random email" retention := int32(7) emitMetric := true activeClusterName := "" clusters := []*persistence.ClusterReplicationConfig{} for clusterName := range clusterMetadata.GetAllClusterInfo() { if clusterName != clusterMetadata.GetCurrentClusterName() { activeClusterName = clusterName } clusters = append(clusters, &persistence.ClusterReplicationConfig{ ClusterName: clusterName, }) } s.True(len(activeClusterName) > 0) s.True(len(clusters) > 1) data := map[string]string{"some random key": "some random value"} isGlobalDomain := true _, err := domainManager.CreateDomain(context.Background(), &persistence.CreateDomainRequest{ Info: &persistence.DomainInfo{ ID: uuid.New(), Name: domainName, Status: persistence.DomainStatusRegistered, Description: description, OwnerEmail: email, Data: data, }, Config: &persistence.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: activeClusterName, Clusters: clusters, }, IsGlobalDomain: isGlobalDomain, ConfigVersion: 0, FailoverVersion: clusterMetadata.GetNextFailoverVersion(activeClusterName, 0, "some-domain"), }) s.Nil(err) getResp, err := handler.DescribeDomain(context.Background(), &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) s.Nil(err) return getResp } ================================================ FILE: common/domain/handler_integration_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "context" "log" "os" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" dc "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" persistencetests "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/sql/sqlplugin/sqlite" "github.com/uber/cadence/common/types" ) type ( domainHandlerCommonSuite struct { *persistencetests.TestBase minRetentionDays int maxBadBinaryCount int failoverHistoryMaxSize int domainManager persistence.DomainManager mockProducer *mocks.KafkaProducer mockDomainReplicator Replicator archivalMetadata archiver.ArchivalMetadata mockArchiverProvider *provider.MockArchiverProvider mockDomainAuditManager persistence.DomainAuditManager handler *handlerImpl } ) var nowInt64 = time.Now().UnixNano() func TestDomainHandlerCommonSuite(t *testing.T) { if testing.Verbose() { log.SetOutput(os.Stdout) } s := new(domainHandlerCommonSuite) s.setupTestBase(t) suite.Run(t, s) } func (s *domainHandlerCommonSuite) setupTestBase(t *testing.T) { sqliteTestBaseOptions := sqlite.GetTestClusterOption() sqliteTestBaseOptions.ClusterMetadata = cluster.GetTestClusterMetadata(true) s.TestBase = persistencetests.NewTestBaseWithSQL(t, sqliteTestBaseOptions) s.Setup() } func (s *domainHandlerCommonSuite) TearDownSuite() { s.TearDownWorkflowStore() } func (s *domainHandlerCommonSuite) SetupTest() { s.setupTestBase(s.T()) logger := s.Logger dcCollection := dc.NewCollection(dc.NewNopClient(), logger) s.minRetentionDays = 1 s.maxBadBinaryCount = 10 s.failoverHistoryMaxSize = 5 s.domainManager = s.TestBase.DomainManager s.mockProducer = &mocks.KafkaProducer{} s.mockDomainReplicator = NewDomainReplicator(s.mockProducer, logger) s.mockDomainAuditManager = persistence.NewMockDomainAuditManager(s.Controller) s.archivalMetadata = archiver.NewArchivalMetadata( dcCollection, "", false, "", false, &config.ArchivalDomainDefaults{}, ) s.mockArchiverProvider = provider.NewMockArchiverProvider(s.Controller) domainConfig := Config{ MinRetentionDays: dynamicproperties.GetIntPropertyFn(s.minRetentionDays), MaxBadBinaryCount: dynamicproperties.GetIntPropertyFilteredByDomain(s.maxBadBinaryCount), FailoverCoolDown: dynamicproperties.GetDurationPropertyFnFilteredByDomain(0 * time.Second), FailoverHistoryMaxSize: dynamicproperties.GetIntPropertyFilteredByDomain(s.failoverHistoryMaxSize), EnableDomainAuditLogging: dynamicproperties.GetBoolPropertyFn(false), } s.handler = NewHandler( domainConfig, logger, s.domainManager, s.mockDomainAuditManager, s.ClusterMetadata, s.mockDomainReplicator, s.archivalMetadata, s.mockArchiverProvider, clock.NewRealTimeSource(), ).(*handlerImpl) } func (s *domainHandlerCommonSuite) TearDownTest() { s.mockProducer.AssertExpectations(s.T()) } func (s *domainHandlerCommonSuite) TestMergeDomainData_Overriding() { out := s.handler.mergeDomainData( map[string]string{ "k0": "v0", }, map[string]string{ "k0": "v2", }, ) assert.Equal(s.T(), map[string]string{ "k0": "v2", }, out) } func (s *domainHandlerCommonSuite) TestMergeDomainData_Adding() { out := s.handler.mergeDomainData( map[string]string{ "k0": "v0", }, map[string]string{ "k1": "v2", }, ) assert.Equal(s.T(), map[string]string{ "k0": "v0", "k1": "v2", }, out) } func (s *domainHandlerCommonSuite) TestMergeDomainData_Merging() { out := s.handler.mergeDomainData( map[string]string{ "k0": "v0", }, map[string]string{ "k0": "v1", "k1": "v2", }, ) assert.Equal(s.T(), map[string]string{ "k0": "v1", "k1": "v2", }, out) } func (s *domainHandlerCommonSuite) TestMergeDomainData_Nil() { out := s.handler.mergeDomainData( nil, map[string]string{ "k0": "v1", "k1": "v2", }, ) assert.Equal(s.T(), map[string]string{ "k0": "v1", "k1": "v2", }, out) } // test merging bad binaries func (s *domainHandlerCommonSuite) TestMergeBadBinaries_Overriding() { out := s.handler.mergeBadBinaries( map[string]*types.BadBinaryInfo{ "k0": {Reason: "reason0"}, }, map[string]*types.BadBinaryInfo{ "k0": {Reason: "reason2"}, }, nowInt64, ) assert.Equal(s.T(), types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "k0": {Reason: "reason2", CreatedTimeNano: common.Int64Ptr(nowInt64)}, }, }, out) } func (s *domainHandlerCommonSuite) TestMergeBadBinaries_Adding() { out := s.handler.mergeBadBinaries( map[string]*types.BadBinaryInfo{ "k0": {Reason: "reason0"}, }, map[string]*types.BadBinaryInfo{ "k1": {Reason: "reason2"}, }, nowInt64, ) expected := types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "k0": {Reason: "reason0"}, "k1": {Reason: "reason2", CreatedTimeNano: common.Int64Ptr(nowInt64)}, }, } assert.Equal(s.T(), expected, out) } func (s *domainHandlerCommonSuite) TestMergeBadBinaries_Merging() { out := s.handler.mergeBadBinaries( map[string]*types.BadBinaryInfo{ "k0": {Reason: "reason0"}, }, map[string]*types.BadBinaryInfo{ "k0": {Reason: "reason1"}, "k1": {Reason: "reason2"}, }, nowInt64, ) assert.Equal(s.T(), types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "k0": {Reason: "reason1", CreatedTimeNano: common.Int64Ptr(nowInt64)}, "k1": {Reason: "reason2", CreatedTimeNano: common.Int64Ptr(nowInt64)}, }, }, out) } func (s *domainHandlerCommonSuite) TestMergeBadBinaries_Nil() { out := s.handler.mergeBadBinaries( nil, map[string]*types.BadBinaryInfo{ "k0": {Reason: "reason1"}, "k1": {Reason: "reason2"}, }, nowInt64, ) assert.Equal(s.T(), types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "k0": {Reason: "reason1", CreatedTimeNano: common.Int64Ptr(nowInt64)}, "k1": {Reason: "reason2", CreatedTimeNano: common.Int64Ptr(nowInt64)}, }, }, out) } func (s *domainHandlerCommonSuite) TestListDomain() { domainName1 := s.getRandomDomainName() description1 := "some random description 1" email1 := "some random email 1" retention1 := int32(1) emitMetric1 := true data1 := map[string]string{"some random key 1": "some random value 1"} isGlobalDomain1 := false activeClusterName1 := s.ClusterMetadata.GetCurrentClusterName() var cluster1 []*types.ClusterReplicationConfiguration for _, replicationConfig := range cluster.GetOrUseDefaultClusters(s.ClusterMetadata.GetCurrentClusterName(), nil) { cluster1 = append(cluster1, &types.ClusterReplicationConfiguration{ ClusterName: replicationConfig.ClusterName, }) } err := s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName1, Description: description1, OwnerEmail: email1, WorkflowExecutionRetentionPeriodInDays: retention1, EmitMetric: common.BoolPtr(emitMetric1), Data: data1, IsGlobalDomain: isGlobalDomain1, }) s.Nil(err) domainName2 := s.getRandomDomainName() description2 := "some random description 2" email2 := "some random email 2" retention2 := int32(2) emitMetric2 := false data2 := map[string]string{"some random key 2": "some random value 2"} isGlobalDomain2 := true activeClusterName2 := "" var cluster2 []*types.ClusterReplicationConfiguration for clusterName := range s.ClusterMetadata.GetEnabledClusterInfo() { if clusterName != s.ClusterMetadata.GetCurrentClusterName() { activeClusterName2 = clusterName } cluster2 = append(cluster2, &types.ClusterReplicationConfiguration{ ClusterName: clusterName, }) } s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Once() err = s.handler.RegisterDomain(context.Background(), &types.RegisterDomainRequest{ Name: domainName2, Description: description2, OwnerEmail: email2, WorkflowExecutionRetentionPeriodInDays: retention2, EmitMetric: common.BoolPtr(emitMetric2), Clusters: cluster2, ActiveClusterName: activeClusterName2, Data: data2, IsGlobalDomain: isGlobalDomain2, }) s.Nil(err) domains := map[string]*types.DescribeDomainResponse{} pagesize := int32(1) var token []byte for doPaging := true; doPaging; doPaging = len(token) > 0 { resp, err := s.handler.ListDomains(context.Background(), &types.ListDomainsRequest{ PageSize: pagesize, NextPageToken: token, }) s.Nil(err) token = resp.NextPageToken s.True(len(resp.Domains) <= int(pagesize)) if len(resp.Domains) > 0 { s.NotEmpty(resp.Domains[0].DomainInfo.GetUUID()) resp.Domains[0].DomainInfo.UUID = "" domains[resp.Domains[0].DomainInfo.GetName()] = resp.Domains[0] } } delete(domains, constants.SystemLocalDomainName) s.Equal(map[string]*types.DescribeDomainResponse{ domainName1: { DomainInfo: &types.DomainInfo{ Name: domainName1, Status: types.DomainStatusRegistered.Ptr(), Description: description1, OwnerEmail: email1, Data: data1, UUID: "", }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention1, EmitMetric: emitMetric1, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: activeClusterName1, Clusters: cluster1, }, FailoverVersion: constants.EmptyVersion, IsGlobalDomain: isGlobalDomain1, }, domainName2: { DomainInfo: &types.DomainInfo{ Name: domainName2, Status: types.DomainStatusRegistered.Ptr(), Description: description2, OwnerEmail: email2, Data: data2, UUID: "", }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention2, EmitMetric: emitMetric2, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: activeClusterName2, Clusters: cluster2, }, FailoverVersion: s.ClusterMetadata.GetNextFailoverVersion(activeClusterName2, 0, "some-domain"), IsGlobalDomain: isGlobalDomain2, }, }, domains) } func (s *domainHandlerCommonSuite) TestRegisterDomain_InvalidRetentionPeriod() { registerRequest := &types.RegisterDomainRequest{ Name: "random-domain-name", Description: "random domain name", WorkflowExecutionRetentionPeriodInDays: int32(0), IsGlobalDomain: false, } err := s.handler.RegisterDomain(context.Background(), registerRequest) s.Equal(errInvalidRetentionPeriod, err) } func (s *domainHandlerCommonSuite) TestRegisterDomain_InvalidDomainName() { registerRequest := &types.RegisterDomainRequest{ Name: "random-domain name!^", Description: "a domain with bad naming", WorkflowExecutionRetentionPeriodInDays: int32(3), } err := s.handler.RegisterDomain(context.Background(), registerRequest) s.Equal(errInvalidDomainName, err) } func (s *domainHandlerCommonSuite) TestUpdateDomain_InvalidRetentionPeriod() { domain := "random-domain-name" registerRequest := &types.RegisterDomainRequest{ Name: domain, Description: domain, WorkflowExecutionRetentionPeriodInDays: int32(10), IsGlobalDomain: false, } err := s.handler.RegisterDomain(context.Background(), registerRequest) s.NoError(err) updateRequest := &types.UpdateDomainRequest{ Name: domain, WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(int32(-1)), } _, err = s.handler.UpdateDomain(context.Background(), updateRequest) s.Equal(errInvalidRetentionPeriod, err) } func (s *domainHandlerCommonSuite) TestUpdateDomain_GracefulFailover_Success() { s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Twice() domain := uuid.New() registerRequest := &types.RegisterDomainRequest{ Name: domain, Description: domain, WorkflowExecutionRetentionPeriodInDays: int32(10), IsGlobalDomain: true, ActiveClusterName: "standby", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: s.ClusterMetadata.GetCurrentClusterName()}, {ClusterName: "standby"}, }, } err := s.handler.RegisterDomain(context.Background(), registerRequest) s.NoError(err) resp1, _ := s.domainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{ Name: domain, }) s.Equal("standby", resp1.ReplicationConfig.ActiveClusterName) s.Equal(cluster.TestAlternativeClusterInitialFailoverVersion, resp1.FailoverVersion) updateRequest := &types.UpdateDomainRequest{ Name: domain, ActiveClusterName: common.StringPtr(s.ClusterMetadata.GetCurrentClusterName()), FailoverTimeoutInSeconds: common.Int32Ptr(100), } resp, err := s.handler.UpdateDomain(context.Background(), updateRequest) s.NoError(err) resp2, err := s.domainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{ ID: resp.GetDomainInfo().GetUUID(), }) s.NoError(err) s.NotNil(resp2.FailoverEndTime) s.Equal(cluster.TestFailoverVersionIncrement, resp2.FailoverVersion) s.Equal(cluster.TestAlternativeClusterInitialFailoverVersion, resp2.PreviousFailoverVersion) } func (s *domainHandlerCommonSuite) TestUpdateDomain_GracefulFailover_NotCurrentActiveCluster() { s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Once() domain := uuid.New() registerRequest := &types.RegisterDomainRequest{ Name: domain, Description: domain, WorkflowExecutionRetentionPeriodInDays: int32(10), IsGlobalDomain: true, ActiveClusterName: "active", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "active"}, {ClusterName: "standby"}, }, } err := s.handler.RegisterDomain(context.Background(), registerRequest) s.NoError(err) updateRequest := &types.UpdateDomainRequest{ Name: domain, ActiveClusterName: common.StringPtr("standby"), FailoverTimeoutInSeconds: common.Int32Ptr(100), } _, err = s.handler.UpdateDomain(context.Background(), updateRequest) s.Error(err) } func (s *domainHandlerCommonSuite) TestUpdateDomain_GracefulFailover_OngoingFailover() { s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Twice() domain := uuid.New() registerRequest := &types.RegisterDomainRequest{ Name: domain, Description: domain, WorkflowExecutionRetentionPeriodInDays: int32(10), IsGlobalDomain: true, ActiveClusterName: "standby", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: s.ClusterMetadata.GetCurrentClusterName()}, {ClusterName: "standby"}, }, } err := s.handler.RegisterDomain(context.Background(), registerRequest) s.NoError(err) updateRequest := &types.UpdateDomainRequest{ Name: domain, ActiveClusterName: common.StringPtr(s.ClusterMetadata.GetCurrentClusterName()), FailoverTimeoutInSeconds: common.Int32Ptr(100), } _, err = s.handler.UpdateDomain(context.Background(), updateRequest) s.NoError(err) _, err = s.handler.UpdateDomain(context.Background(), updateRequest) s.Error(err) } func (s *domainHandlerCommonSuite) TestUpdateDomain_GracefulFailover_NoUpdateActiveCluster() { s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Once() domain := uuid.New() registerRequest := &types.RegisterDomainRequest{ Name: domain, Description: domain, WorkflowExecutionRetentionPeriodInDays: int32(10), IsGlobalDomain: true, ActiveClusterName: "standby", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: s.ClusterMetadata.GetCurrentClusterName()}, {ClusterName: "standby"}, }, } err := s.handler.RegisterDomain(context.Background(), registerRequest) s.NoError(err) updateRequest := &types.UpdateDomainRequest{ Name: domain, OwnerEmail: common.StringPtr("test"), FailoverTimeoutInSeconds: common.Int32Ptr(100), } _, err = s.handler.UpdateDomain(context.Background(), updateRequest) s.Error(err) } func (s *domainHandlerCommonSuite) TestUpdateDomain_GracefulFailover_After_ForceFailover() { s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Times(3) domain := uuid.New() registerRequest := &types.RegisterDomainRequest{ Name: domain, Description: domain, WorkflowExecutionRetentionPeriodInDays: int32(10), IsGlobalDomain: true, ActiveClusterName: "standby", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: s.ClusterMetadata.GetCurrentClusterName()}, {ClusterName: "standby"}, }, } err := s.handler.RegisterDomain(context.Background(), registerRequest) s.NoError(err) // Start graceful failover updateRequest := &types.UpdateDomainRequest{ Name: domain, ActiveClusterName: common.StringPtr(s.ClusterMetadata.GetCurrentClusterName()), FailoverTimeoutInSeconds: common.Int32Ptr(100), } resp, err := s.handler.UpdateDomain(context.Background(), updateRequest) s.NoError(err) // Force failover updateRequest = &types.UpdateDomainRequest{ Name: domain, ActiveClusterName: common.StringPtr(s.ClusterMetadata.GetCurrentClusterName()), } _, err = s.handler.UpdateDomain(context.Background(), updateRequest) s.NoError(err) resp2, err := s.domainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{ ID: resp.GetDomainInfo().GetUUID(), }) s.NoError(err) s.Nil(resp2.FailoverEndTime) } func (s *domainHandlerCommonSuite) TestUpdateDomain_ForceFailover_SameActiveCluster() { s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Twice() domain := uuid.New() registerRequest := &types.RegisterDomainRequest{ Name: domain, Description: domain, WorkflowExecutionRetentionPeriodInDays: int32(10), IsGlobalDomain: true, ActiveClusterName: "standby", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: s.ClusterMetadata.GetCurrentClusterName()}, {ClusterName: "standby"}, }, } err := s.handler.RegisterDomain(context.Background(), registerRequest) s.NoError(err) // Start graceful failover updateRequest := &types.UpdateDomainRequest{ Name: domain, ActiveClusterName: common.StringPtr("standby"), } _, err = s.handler.UpdateDomain(context.Background(), updateRequest) s.NoError(err) } func (s *domainHandlerCommonSuite) getRandomDomainName() string { return "domain" + uuid.New() } func TestHandlerImpl_UpdateIsolationGroups(t *testing.T) { t0 := int64(1565914445 * time.Second) t1 := int64(1685914445 * time.Second) mockedTimeSourceAt := clock.NewMockedTimeSourceAt(time.Unix(0, t1)) info := persistence.DomainInfo{ ID: "10CF5859-C5CC-4CCC-888E-631F84D53F57", Name: "test", } domainConfig := persistence.DomainConfig{ Retention: 10, IsolationGroups: nil, } replicationConfig := persistence.DomainReplicationConfig{ ActiveClusterName: "cluster-1", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "cluster-1"}, }, } ig := types.IsolationGroupConfiguration{ "zone-1": {Name: "zone-1", State: types.IsolationGroupStateDrained}, } tests := map[string]struct { in types.UpdateDomainIsolationGroupsRequest managerAffordance func(m *persistence.MockDomainManager) expectedErr error }{ "successful update": { in: types.UpdateDomainIsolationGroupsRequest{ Domain: "test", IsolationGroups: ig, }, managerAffordance: func(m *persistence.MockDomainManager) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{ NotificationVersion: 3, }, nil) m.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: "test"}).Return(&persistence.GetDomainResponse{ Info: &info, Config: &domainConfig, ReplicationConfig: &replicationConfig, IsGlobalDomain: false, ConfigVersion: 1, FailoverVersion: 1, FailoverNotificationVersion: 1, PreviousFailoverVersion: 1, FailoverEndTime: &t0, LastUpdatedTime: t0, NotificationVersion: 1, }, nil) m.EXPECT().UpdateDomain(gomock.Any(), &persistence.UpdateDomainRequest{ Info: &info, Config: &persistence.DomainConfig{ Retention: 10, IsolationGroups: ig, }, ReplicationConfig: &replicationConfig, ConfigVersion: 2, FailoverVersion: 1, FailoverNotificationVersion: 1, PreviousFailoverVersion: 1, FailoverEndTime: &t0, LastUpdatedTime: t1, NotificationVersion: 3, }) }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) domainMgrMock := persistence.NewMockDomainManager(ctrl) td.managerAffordance(domainMgrMock) producer := messaging.NewNoopProducer() replicator := NewDomainReplicator(producer, testlogger.New(t)) handler := handlerImpl{ domainManager: domainMgrMock, clusterMetadata: cluster.Metadata{}, domainReplicator: replicator, domainAttrValidator: nil, archivalMetadata: nil, archiverProvider: nil, timeSource: mockedTimeSourceAt, config: Config{ MinRetentionDays: func(opts ...dynamicproperties.FilterOption) int { return 0 }, MaxBadBinaryCount: func(string) int { return 3 }, FailoverCoolDown: func(string) time.Duration { return time.Second }, }, logger: testlogger.New(t), } err := handler.UpdateIsolationGroups(context.TODO(), td.in) assert.Equal(t, td.expectedErr, err) }) } } func TestHandlerImpl_UpdateAsyncWorkflowConfiguraton(t *testing.T) { t0 := int64(1565914445 * time.Second) t1 := int64(1685914445 * time.Second) mockedTimeSourceAt := clock.NewMockedTimeSourceAt(time.Unix(0, t1)) info := persistence.DomainInfo{ ID: "10CF5859-C5CC-4CCC-888E-631F84D53F57", Name: "test", } domainConfig := persistence.DomainConfig{ Retention: 10, } replicationConfig := persistence.DomainReplicationConfig{ ActiveClusterName: "cluster-1", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "cluster-1"}, }, } asyncWFCfg := types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "queue-name", QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"key":"value"}`), }, } tests := map[string]struct { in types.UpdateDomainAsyncWorkflowConfiguratonRequest managerAffordance func(m *persistence.MockDomainManager) expectedErr error }{ "successful update": { in: types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test", Configuration: &asyncWFCfg, }, managerAffordance: func(m *persistence.MockDomainManager) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{ NotificationVersion: 3, }, nil).Times(1) m.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: "test"}).Return(&persistence.GetDomainResponse{ Info: &info, Config: &domainConfig, ReplicationConfig: &replicationConfig, IsGlobalDomain: false, ConfigVersion: 1, FailoverVersion: 1, FailoverNotificationVersion: 1, PreviousFailoverVersion: 1, FailoverEndTime: &t0, LastUpdatedTime: t0, NotificationVersion: 1, }, nil).Times(1) m.EXPECT().UpdateDomain(gomock.Any(), &persistence.UpdateDomainRequest{ Info: &info, Config: &persistence.DomainConfig{ Retention: 10, AsyncWorkflowConfig: asyncWFCfg, }, ReplicationConfig: &replicationConfig, ConfigVersion: 2, FailoverVersion: 1, FailoverNotificationVersion: 1, PreviousFailoverVersion: 1, FailoverEndTime: &t0, LastUpdatedTime: t1, NotificationVersion: 3, }).Times(1) }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) domainMgrMock := persistence.NewMockDomainManager(ctrl) td.managerAffordance(domainMgrMock) producer := messaging.NewNoopProducer() replicator := NewDomainReplicator(producer, testlogger.New(t)) handler := handlerImpl{ domainManager: domainMgrMock, clusterMetadata: cluster.Metadata{}, domainReplicator: replicator, timeSource: mockedTimeSourceAt, config: Config{ MinRetentionDays: func(opts ...dynamicproperties.FilterOption) int { return 0 }, MaxBadBinaryCount: func(string) int { return 3 }, FailoverCoolDown: func(string) time.Duration { return time.Second }, }, logger: testlogger.New(t), } err := handler.UpdateAsyncWorkflowConfiguraton(context.Background(), td.in) assert.Equal(t, td.expectedErr, err) }) } } ================================================ FILE: common/domain/handler_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: handler.go // // Generated by this command: // // mockgen -package domain -source handler.go -destination handler_mock.go // // Package domain is a generated GoMock package. package domain import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockHandler is a mock of Handler interface. type MockHandler struct { ctrl *gomock.Controller recorder *MockHandlerMockRecorder isgomock struct{} } // MockHandlerMockRecorder is the mock recorder for MockHandler. type MockHandlerMockRecorder struct { mock *MockHandler } // NewMockHandler creates a new mock instance. func NewMockHandler(ctrl *gomock.Controller) *MockHandler { mock := &MockHandler{ctrl: ctrl} mock.recorder = &MockHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHandler) EXPECT() *MockHandlerMockRecorder { return m.recorder } // DeleteDomain mocks base method. func (m *MockHandler) DeleteDomain(ctx context.Context, deleteRequest *types.DeleteDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteDomain", ctx, deleteRequest) ret0, _ := ret[0].(error) return ret0 } // DeleteDomain indicates an expected call of DeleteDomain. func (mr *MockHandlerMockRecorder) DeleteDomain(ctx, deleteRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDomain", reflect.TypeOf((*MockHandler)(nil).DeleteDomain), ctx, deleteRequest) } // DeprecateDomain mocks base method. func (m *MockHandler) DeprecateDomain(ctx context.Context, deprecateRequest *types.DeprecateDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeprecateDomain", ctx, deprecateRequest) ret0, _ := ret[0].(error) return ret0 } // DeprecateDomain indicates an expected call of DeprecateDomain. func (mr *MockHandlerMockRecorder) DeprecateDomain(ctx, deprecateRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeprecateDomain", reflect.TypeOf((*MockHandler)(nil).DeprecateDomain), ctx, deprecateRequest) } // DescribeDomain mocks base method. func (m *MockHandler) DescribeDomain(ctx context.Context, describeRequest *types.DescribeDomainRequest) (*types.DescribeDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeDomain", ctx, describeRequest) ret0, _ := ret[0].(*types.DescribeDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeDomain indicates an expected call of DescribeDomain. func (mr *MockHandlerMockRecorder) DescribeDomain(ctx, describeRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDomain", reflect.TypeOf((*MockHandler)(nil).DescribeDomain), ctx, describeRequest) } // FailoverDomain mocks base method. func (m *MockHandler) FailoverDomain(ctx context.Context, failoverRequest *types.FailoverDomainRequest) (*types.FailoverDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FailoverDomain", ctx, failoverRequest) ret0, _ := ret[0].(*types.FailoverDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // FailoverDomain indicates an expected call of FailoverDomain. func (mr *MockHandlerMockRecorder) FailoverDomain(ctx, failoverRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FailoverDomain", reflect.TypeOf((*MockHandler)(nil).FailoverDomain), ctx, failoverRequest) } // ListDomains mocks base method. func (m *MockHandler) ListDomains(ctx context.Context, listRequest *types.ListDomainsRequest) (*types.ListDomainsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListDomains", ctx, listRequest) ret0, _ := ret[0].(*types.ListDomainsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListDomains indicates an expected call of ListDomains. func (mr *MockHandlerMockRecorder) ListDomains(ctx, listRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDomains", reflect.TypeOf((*MockHandler)(nil).ListDomains), ctx, listRequest) } // RegisterDomain mocks base method. func (m *MockHandler) RegisterDomain(ctx context.Context, registerRequest *types.RegisterDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RegisterDomain", ctx, registerRequest) ret0, _ := ret[0].(error) return ret0 } // RegisterDomain indicates an expected call of RegisterDomain. func (mr *MockHandlerMockRecorder) RegisterDomain(ctx, registerRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterDomain", reflect.TypeOf((*MockHandler)(nil).RegisterDomain), ctx, registerRequest) } // UpdateAsyncWorkflowConfiguraton mocks base method. func (m *MockHandler) UpdateAsyncWorkflowConfiguraton(ctx context.Context, updateRequest types.UpdateDomainAsyncWorkflowConfiguratonRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAsyncWorkflowConfiguraton", ctx, updateRequest) ret0, _ := ret[0].(error) return ret0 } // UpdateAsyncWorkflowConfiguraton indicates an expected call of UpdateAsyncWorkflowConfiguraton. func (mr *MockHandlerMockRecorder) UpdateAsyncWorkflowConfiguraton(ctx, updateRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAsyncWorkflowConfiguraton", reflect.TypeOf((*MockHandler)(nil).UpdateAsyncWorkflowConfiguraton), ctx, updateRequest) } // UpdateDomain mocks base method. func (m *MockHandler) UpdateDomain(ctx context.Context, updateRequest *types.UpdateDomainRequest) (*types.UpdateDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomain", ctx, updateRequest) ret0, _ := ret[0].(*types.UpdateDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MockHandlerMockRecorder) UpdateDomain(ctx, updateRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MockHandler)(nil).UpdateDomain), ctx, updateRequest) } // UpdateIsolationGroups mocks base method. func (m *MockHandler) UpdateIsolationGroups(ctx context.Context, updateRequest types.UpdateDomainIsolationGroupsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateIsolationGroups", ctx, updateRequest) ret0, _ := ret[0].(error) return ret0 } // UpdateIsolationGroups indicates an expected call of UpdateIsolationGroups. func (mr *MockHandlerMockRecorder) UpdateIsolationGroups(ctx, updateRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIsolationGroups", reflect.TypeOf((*MockHandler)(nil).UpdateIsolationGroups), ctx, updateRequest) } ================================================ FILE: common/domain/handler_test.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "context" "encoding/json" "errors" "fmt" "net/url" "reflect" "strconv" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" ) // newTestHandler creates a new instance of the handler with mocked dependencies for testing. func newTestHandler(t *testing.T, ctrl *gomock.Controller, domainManager *persistence.MockDomainManager, primaryCluster bool, domainReplicator Replicator) Handler { mockDC := dynamicconfig.NewCollection(dynamicconfig.NewNopClient(), log.NewNoop()) domainDefaults := &config.ArchivalDomainDefaults{ History: config.HistoryArchivalDomainDefaults{ Status: "Disabled", URI: "https://history.example.com", }, Visibility: config.VisibilityArchivalDomainDefaults{ Status: "Disabled", URI: "https://visibility.example.com", }, } archivalMetadata := archiver.NewArchivalMetadata(mockDC, "Enabled", true, "Enabled", true, domainDefaults) testConfig := Config{ MinRetentionDays: dynamicproperties.GetIntPropertyFn(1), MaxRetentionDays: dynamicproperties.GetIntPropertyFn(5), RequiredDomainDataKeys: nil, MaxBadBinaryCount: func(string) int { return 3 }, FailoverCoolDown: func(string) time.Duration { return time.Second }, EnableDomainAuditLogging: dynamicproperties.GetBoolPropertyFn(true), } mockDomainAuditManager := persistence.NewMockDomainAuditManager(ctrl) mockDomainAuditManager.EXPECT().CreateDomainAuditLog(gomock.Any(), gomock.Any()).Return(&persistence.CreateDomainAuditLogResponse{EventID: "test-event-id"}, nil).AnyTimes() mockDomainAuditManager.EXPECT().Close().AnyTimes() return NewHandler( testConfig, log.NewNoop(), domainManager, mockDomainAuditManager, cluster.GetTestClusterMetadata(primaryCluster), domainReplicator, archivalMetadata, provider.NewArchiverProvider(nil, nil), clock.NewMockedTimeSource(), ) } func TestRegisterDomain(t *testing.T) { tests := []struct { name string request *types.RegisterDomainRequest isPrimaryCluster bool mockSetup func(*persistence.MockDomainManager, *MockReplicator, *types.RegisterDomainRequest) wantErr bool expectedErr error }{ { name: "success", isPrimaryCluster: true, request: &types.RegisterDomainRequest{ Name: "test-domain", WorkflowExecutionRetentionPeriodInDays: 3, }, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, replicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) mockDomainMgr.EXPECT().CreateDomain(gomock.Any(), gomock.Any()).Return(&persistence.CreateDomainResponse{ID: "test-domain-id"}, nil) }, wantErr: false, }, { name: "domain already exists", isPrimaryCluster: true, request: &types.RegisterDomainRequest{ Name: "existing-domain", WorkflowExecutionRetentionPeriodInDays: 3, }, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, replicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(&persistence.GetDomainResponse{}, nil) }, wantErr: true, }, { name: "global domain registration on non-primary cluster", isPrimaryCluster: false, request: &types.RegisterDomainRequest{ Name: "global-test-domain", IsGlobalDomain: true, }, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, replicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *persistence.GetDomainRequest) (*persistence.GetDomainResponse, error) { if req.Name == "global-test-domain" { return nil, &types.EntityNotExistsError{} } return nil, errors.New("unexpected domain name") }).AnyTimes() }, wantErr: true, expectedErr: errNotPrimaryCluster, }, { name: "unexpected error on domain lookup", request: &types.RegisterDomainRequest{ Name: "test-domain-with-lookup-error", WorkflowExecutionRetentionPeriodInDays: 3, }, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, replicator *MockReplicator, request *types.RegisterDomainRequest) { unexpectedErr := &types.InternalServiceError{Message: "Internal server error."} mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, unexpectedErr) }, wantErr: true, expectedErr: &types.InternalServiceError{}, }, { name: "domain name does not match regex", request: &types.RegisterDomainRequest{ Name: "invalid_domain!", WorkflowExecutionRetentionPeriodInDays: 3, }, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, replicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) }, wantErr: true, expectedErr: errInvalidDomainName, }, { name: "specify active cluster name", request: &types.RegisterDomainRequest{ Name: "test-domain-with-active-cluster", WorkflowExecutionRetentionPeriodInDays: 3, ActiveClusterName: "active", }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, replicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) mockDomainMgr.EXPECT().CreateDomain(gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, req *persistence.CreateDomainRequest) (*persistence.CreateDomainResponse, error) { return &persistence.CreateDomainResponse{ID: "test-domain-id"}, nil }) }, wantErr: false, }, { name: "specify clusters including an invalid one", request: &types.RegisterDomainRequest{ Name: "test-domain-with-clusters", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "valid-cluster-1"}, {ClusterName: "invalid-cluster"}, }, IsGlobalDomain: true, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, replicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) }, wantErr: true, expectedErr: &types.BadRequestError{}, }, { name: "invalid history archival configuration", request: &types.RegisterDomainRequest{ Name: "test-domain-invalid-archival-config", HistoryArchivalStatus: types.ArchivalStatusEnabled.Ptr(), HistoryArchivalURI: "invalid-uri", IsGlobalDomain: true, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, replicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) }, wantErr: true, expectedErr: &url.Error{}, }, { name: "error during domain creation", request: &types.RegisterDomainRequest{ Name: "domain-creation-error", WorkflowExecutionRetentionPeriodInDays: 2, IsGlobalDomain: false, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, replicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) mockDomainMgr.EXPECT().CreateDomain(gomock.Any(), gomock.Any()).Return(nil, errors.New("creation failed")) }, wantErr: true, }, { name: "global domain with replication task", request: &types.RegisterDomainRequest{ Name: "global-domain-with-replication", IsGlobalDomain: true, WorkflowExecutionRetentionPeriodInDays: 3, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, replicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) mockDomainMgr.EXPECT().CreateDomain(gomock.Any(), gomock.Any()).Return(&persistence.CreateDomainResponse{ID: "domain-id"}, nil) replicator.EXPECT().HandleTransmissionTask(gomock.Any(), types.DomainOperationCreate, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), commonconstants.InitialPreviousFailoverVersion, true).Return(nil) }, wantErr: false, }, { name: "global domain replication task failure", request: &types.RegisterDomainRequest{ Name: "global-domain-replication-failure", IsGlobalDomain: true, WorkflowExecutionRetentionPeriodInDays: 3, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, mockReplicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) mockDomainMgr.EXPECT().CreateDomain(gomock.Any(), gomock.Any()).Return(&persistence.CreateDomainResponse{ID: "domain-id"}, nil) mockReplicator.EXPECT().HandleTransmissionTask( gomock.Any(), types.DomainOperationCreate, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), commonconstants.InitialPreviousFailoverVersion, true, ).Return(errors.New("replication task failed")) }, wantErr: true, expectedErr: errors.New("replication task failed"), }, { name: "visibility archival URI triggers parsing/validation error and not able to reach next state", request: &types.RegisterDomainRequest{ Name: "domain-with-invalid-visibility-uri", VisibilityArchivalStatus: types.ArchivalStatusEnabled.Ptr(), VisibilityArchivalURI: "invalid-visibility-uri", IsGlobalDomain: true, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, mockReplicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) }, wantErr: true, expectedErr: &url.Error{}, }, { name: "local domain with invalid replication configuration: non-global domain", request: &types.RegisterDomainRequest{ Name: "local-invalid-replication", IsGlobalDomain: false, ActiveClusterName: "current-cluster", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "non-current-cluster"}, {ClusterName: "non-current-cluster2"}, }, WorkflowExecutionRetentionPeriodInDays: 3, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, mockReplicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) }, wantErr: true, expectedErr: &types.BadRequestError{Message: "Invalid local domain active cluster"}, }, { name: "local domain with invalid replication configuration: global domain", request: &types.RegisterDomainRequest{ Name: "global-invalid-replication", IsGlobalDomain: true, ActiveClusterName: "current-cluster", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "non-current-cluster2"}, {ClusterName: "non-current-cluster3"}, }, WorkflowExecutionRetentionPeriodInDays: 3, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, mockReplicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) }, wantErr: true, expectedErr: &types.BadRequestError{Message: "Invalid local domain active cluster"}, }, { name: "active-active domain with an invalid cluster in request", request: &types.RegisterDomainRequest{ Name: "active-active-domain", IsGlobalDomain: true, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: {ActiveClusterName: cluster.TestCurrentClusterName}, cluster.TestRegion2: {ActiveClusterName: "invalid-cluster"}, }, }, }, }, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, WorkflowExecutionRetentionPeriodInDays: 3, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, mockReplicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) }, wantErr: true, expectedErr: &types.BadRequestError{}, }, { name: "active-active domain with an active cluster in request that doesn't exist in domain's clusters list", request: &types.RegisterDomainRequest{ Name: "active-active-domain", IsGlobalDomain: true, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: {ActiveClusterName: cluster.TestCurrentClusterName}, cluster.TestRegion2: {ActiveClusterName: cluster.TestAlternativeClusterName}, }, }, }, }, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, }, WorkflowExecutionRetentionPeriodInDays: 3, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, mockReplicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) }, wantErr: true, expectedErr: &types.BadRequestError{}, }, { name: "active-active domain successfully registered with explicit ActiveClusterName", request: &types.RegisterDomainRequest{ Name: "active-active-domain", IsGlobalDomain: true, ActiveClusterName: cluster.TestCurrentClusterName, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: {ActiveClusterName: cluster.TestCurrentClusterName}, cluster.TestRegion2: {ActiveClusterName: cluster.TestAlternativeClusterName}, }, }, }, }, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, WorkflowExecutionRetentionPeriodInDays: 3, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, mockReplicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) mockDomainMgr.EXPECT().CreateDomain(gomock.Any(), gomock.Any()).Return(&persistence.CreateDomainResponse{ID: "test-domain-id"}, nil) mockReplicator.EXPECT().HandleTransmissionTask(gomock.Any(), types.DomainOperationCreate, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), commonconstants.InitialPreviousFailoverVersion, true).Return(nil) }, wantErr: false, }, { name: "active-active domain successfully registered without explicit ActiveClusterName (uses current cluster)", request: &types.RegisterDomainRequest{ Name: "active-active-domain", IsGlobalDomain: true, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: {ActiveClusterName: cluster.TestCurrentClusterName}, cluster.TestRegion2: {ActiveClusterName: cluster.TestAlternativeClusterName}, }, }, }, }, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, WorkflowExecutionRetentionPeriodInDays: 3, }, isPrimaryCluster: true, mockSetup: func(mockDomainMgr *persistence.MockDomainManager, mockReplicator *MockReplicator, request *types.RegisterDomainRequest) { mockDomainMgr.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: request.Name}).Return(nil, &types.EntityNotExistsError{}) mockDomainMgr.EXPECT().CreateDomain(gomock.Any(), gomock.Any()).Return(&persistence.CreateDomainResponse{ID: "test-domain-id"}, nil) mockReplicator.EXPECT().HandleTransmissionTask(gomock.Any(), types.DomainOperationCreate, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), commonconstants.InitialPreviousFailoverVersion, true).Return(nil) }, wantErr: false, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDomainMgr := persistence.NewMockDomainManager(controller) mockReplicator := NewMockReplicator(controller) handler := newTestHandler(t, controller, mockDomainMgr, tc.isPrimaryCluster, mockReplicator) tc.mockSetup(mockDomainMgr, mockReplicator, tc.request) err := handler.RegisterDomain(context.Background(), tc.request) if tc.wantErr { assert.Error(t, err) if tc.expectedErr != nil { assert.IsType(t, tc.expectedErr, err) } } else { assert.NoError(t, err) } }) } } func TestListDomains(t *testing.T) { var configIsolationGroup types.IsolationGroupConfiguration tests := []struct { name string request *types.ListDomainsRequest setupMocks func(mockDomainManager *persistence.MockDomainManager) expectedResp *types.ListDomainsResponse expectedError error }{ { name: "Success case - List domains", request: &types.ListDomainsRequest{ PageSize: 2, }, setupMocks: func(mockDomainManager *persistence.MockDomainManager) { resp := &persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{ { Info: &persistence.DomainInfo{ ID: "domainID1", Name: "domainName1", }, Config: &persistence.DomainConfig{}, ReplicationConfig: &persistence.DomainReplicationConfig{}, }, { Info: &persistence.DomainInfo{ ID: "domainID2", Name: "domainName2", }, Config: &persistence.DomainConfig{}, ReplicationConfig: &persistence.DomainReplicationConfig{}, }, }, NextPageToken: nil, } mockDomainManager.EXPECT().ListDomains(gomock.Any(), &persistence.ListDomainsRequest{ PageSize: 2, NextPageToken: nil, }).Return(resp, nil) }, expectedResp: &types.ListDomainsResponse{ Domains: []*types.DescribeDomainResponse{ { DomainInfo: &types.DomainInfo{ Name: "domainName1", UUID: "domainID1", Status: types.DomainStatusRegistered.Ptr(), Description: "", OwnerEmail: "", Data: nil, }, Configuration: &types.DomainConfiguration{ EmitMetric: false, BadBinaries: &types.BadBinaries{Binaries: nil}, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", IsolationGroups: &configIsolationGroup, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "", Clusters: []*types.ClusterReplicationConfiguration{}, }, }, { DomainInfo: &types.DomainInfo{ Name: "domainName2", UUID: "domainID2", Status: types.DomainStatusRegistered.Ptr(), Description: "", OwnerEmail: "", Data: nil, }, Configuration: &types.DomainConfiguration{ EmitMetric: false, BadBinaries: &types.BadBinaries{Binaries: nil}, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", IsolationGroups: &configIsolationGroup, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "", Clusters: []*types.ClusterReplicationConfiguration{}, }, }, }, NextPageToken: nil, }, expectedError: nil, }, { name: "Error case - Persistence error", request: &types.ListDomainsRequest{ PageSize: 10, }, setupMocks: func(mockDomainManager *persistence.MockDomainManager) { mockDomainManager.EXPECT().ListDomains(gomock.Any(), &persistence.ListDomainsRequest{ PageSize: 10, NextPageToken: nil, }).Return(nil, errors.New("persistence error")) }, expectedResp: nil, expectedError: errors.New("persistence error"), }, } for _, test := range tests { controller := gomock.NewController(t) mockDomainMgr := persistence.NewMockDomainManager(controller) mockReplicator := NewMockReplicator(controller) handler := newTestHandler(t, controller, mockDomainMgr, true, mockReplicator) t.Run(test.name, func(t *testing.T) { test.setupMocks(mockDomainMgr) resp, err := handler.ListDomains(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) assert.Contains(t, err.Error(), test.expectedError.Error()) } else { assert.NoError(t, err) assert.EqualValues(t, test.expectedResp, resp) } }) } } func TestHandler_DescribeDomain(t *testing.T) { controller := gomock.NewController(t) mockDomainManager := persistence.NewMockDomainManager(controller) mockReplicator := NewMockReplicator(controller) handler := newTestHandler(t, controller, mockDomainManager, true, mockReplicator) domainName := "test-domain" domainID := "test-domain-id" isGlobalDomain := true failoverVersion := int64(123) lastUpdatedTime := time.Now().UnixNano() failoverEndTime := lastUpdatedTime + int64(time.Hour) var configIsolationGroup types.IsolationGroupConfiguration domainInfo := &persistence.DomainInfo{ ID: domainID, Name: domainName, Status: persistence.DomainStatusRegistered, Description: "Test Domain", OwnerEmail: "test@uber.com", Data: map[string]string{"k1": "v1"}, } domainConfig := &persistence.DomainConfig{ Retention: 24, } domainReplicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "active"}, {ClusterName: "standby"}}, } mockDomainManager.EXPECT(). GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: domainName}). Return(&persistence.GetDomainResponse{ Info: domainInfo, Config: domainConfig, ReplicationConfig: domainReplicationConfig, IsGlobalDomain: isGlobalDomain, FailoverVersion: failoverVersion, FailoverEndTime: &failoverEndTime, LastUpdatedTime: lastUpdatedTime, }, nil) describeRequest := &types.DescribeDomainRequest{Name: &domainName} expectedResponse := &types.DescribeDomainResponse{ IsGlobalDomain: isGlobalDomain, FailoverVersion: failoverVersion, FailoverInfo: &types.FailoverInfo{ FailoverVersion: failoverVersion, FailoverStartTimestamp: lastUpdatedTime, FailoverExpireTimestamp: failoverEndTime, }, DomainInfo: &types.DomainInfo{ Name: domainName, Status: types.DomainStatusRegistered.Ptr(), Description: "Test Domain", OwnerEmail: "test@uber.com", Data: map[string]string{"k1": "v1"}, UUID: domainID, }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 24, EmitMetric: false, BadBinaries: &types.BadBinaries{Binaries: nil}, HistoryArchivalStatus: types.ArchivalStatusDisabled.Ptr(), HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "", IsolationGroups: &configIsolationGroup, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "active", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "active"}, {ClusterName: "standby"}, }, }, } resp, err := handler.DescribeDomain(context.Background(), describeRequest) assert.NoError(t, err) assert.EqualValues(t, expectedResponse, resp) mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(nil, types.EntityNotExistsError{}). Times(1) _, err = handler.DescribeDomain(context.Background(), describeRequest) assert.Error(t, err) assert.IsType(t, types.EntityNotExistsError{}, err) } func TestHandler_DeprecateDomain(t *testing.T) { tests := []struct { name string domainName string setupMocks func(m *persistence.MockDomainManager, r *MockReplicator) isGlobalDomain bool primaryCluster bool expectedErr error }{ { name: "success - deprecate local domain", domainName: "local-domain", setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { // Mock calls m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: "local-domain"}).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "local-domain", Status: persistence.DomainStatusRegistered}, Config: &persistence.DomainConfig{ Retention: 7, }, ReplicationConfig: &persistence.DomainReplicationConfig{}, IsGlobalDomain: false, }, nil) m.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(nil) }, isGlobalDomain: false, primaryCluster: true, expectedErr: nil, }, { name: "failure - domain not found", domainName: "non-existent-domain", setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: "non-existent-domain"}).Return(nil, errors.New("domain not found")) }, isGlobalDomain: false, primaryCluster: false, expectedErr: errors.New("domain not found"), }, { name: "failure - get metadata error", domainName: "test-domain", setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(nil, errors.New("metadata error")) }, expectedErr: errors.New("metadata error"), }, { name: "failure - not primary cluster for global domain", domainName: "global-domain", isGlobalDomain: true, primaryCluster: false, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: "global-domain"}).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "global-domain", Status: persistence.DomainStatusRegistered}, Config: &persistence.DomainConfig{ Retention: 7, }, ReplicationConfig: &persistence.DomainReplicationConfig{}, IsGlobalDomain: true, }, nil) }, expectedErr: errNotPrimaryCluster, }, { name: "failure - update domain error", domainName: "test-domain", primaryCluster: true, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "test-domain", Status: persistence.DomainStatusRegistered}, Config: &persistence.DomainConfig{ Retention: 7, }, ReplicationConfig: &persistence.DomainReplicationConfig{}, FailoverVersion: 123, IsGlobalDomain: true, ConfigVersion: 1, }, nil) m.EXPECT().UpdateDomain(gomock.Any(), gomock.AssignableToTypeOf(&persistence.UpdateDomainRequest{})).DoAndReturn( func(_ context.Context, req *persistence.UpdateDomainRequest) error { if req.Info.Name != "test-domain" || req.ConfigVersion != 2 || req.Info.Status != persistence.DomainStatusDeprecated { return errors.New("unexpected UpdateDomainRequest") } return errors.New("update domain error") }, ) }, expectedErr: errors.New("update domain error"), }, { name: "failure - replicator error for global domain", domainName: "global-domain", isGlobalDomain: true, primaryCluster: true, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Status: persistence.DomainStatusDeprecated}, IsGlobalDomain: true, ConfigVersion: 1}, nil) m.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(nil) r.EXPECT().HandleTransmissionTask(gomock.Any(), types.DomainOperationUpdate, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), true).Return(errors.New("replicator error")) }, expectedErr: errors.New("replicator error"), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDomainManager := persistence.NewMockDomainManager(controller) mockReplicator := NewMockReplicator(controller) handler := newTestHandler(t, controller, mockDomainManager, tc.primaryCluster, mockReplicator) tc.setupMocks(mockDomainManager, mockReplicator) err := handler.DeprecateDomain(context.Background(), &types.DeprecateDomainRequest{Name: tc.domainName}) if tc.expectedErr != nil { assert.Error(t, err) assert.EqualError(t, err, tc.expectedErr.Error()) } else { assert.NoError(t, err) } }) } } func TestHandler_UpdateIsolationGroups(t *testing.T) { tests := []struct { name string domain string isolationGroups types.IsolationGroupConfiguration isPrimaryCluster bool setupMocks func(m *persistence.MockDomainManager, r *MockReplicator) expectedErr error }{ { name: "Successful Update for Local Domain", domain: "local-domain", isolationGroups: types.IsolationGroupConfiguration{ "group1": {Name: "group1", State: types.IsolationGroupStateHealthy}, }, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), gomock.Eq(&persistence.GetDomainRequest{Name: "local-domain"})).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ Name: "local-domain", ID: "domainID", }, Config: &persistence.DomainConfig{ Retention: 7, EmitMetric: true, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{"group1": {Name: "group1", State: types.IsolationGroupStateHealthy}}, HistoryArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "https://test.history", VisibilityArchivalURI: "https://test.visibility", AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: false}, }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "active"}, {ClusterName: "standby"}}, }, ConfigVersion: 1, FailoverVersion: 123, IsGlobalDomain: false, LastUpdatedTime: time.Date(2024, 1, 1, 1, 1, 1, 1, time.UTC).UnixNano(), }, nil) m.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(nil) }, expectedErr: nil, }, { name: "Successful Update for Global Domain", domain: "global-domain", isolationGroups: types.IsolationGroupConfiguration{ "group1": {State: types.IsolationGroupStateDrained}, }, isPrimaryCluster: true, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "global-domain"}, Config: &persistence.DomainConfig{ Retention: 30, }, ReplicationConfig: &persistence.DomainReplicationConfig{}, ConfigVersion: int64(1), IsGlobalDomain: true, LastUpdatedTime: time.Date(2024, 1, 1, 1, 1, 1, 1, time.UTC).UnixNano(), }, nil) m.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(nil) r.EXPECT().HandleTransmissionTask(gomock.Any(), types.DomainOperationUpdate, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), true).Return(nil) }, expectedErr: nil, }, { name: "Failure Due to Invalid Request (nil Isolation Groups)", domain: "domain-with-nil-isolation-groups", setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) }, expectedErr: fmt.Errorf("invalid request, isolationGroup configuration must be set: Got: {domain-with-nil-isolation-groups map[]}"), }, { name: "Failure Due to Domain Not Found", domain: "nonexistent-domain", isolationGroups: types.IsolationGroupConfiguration{ "group1": {State: types.IsolationGroupStateHealthy}, }, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(nil, errors.New("domain not found")) }, expectedErr: errors.New("domain not found"), }, { name: "Failure Due to UpdateDomain Error", domain: "domain-with-update-error", isolationGroups: types.IsolationGroupConfiguration{ "group1": {State: types.IsolationGroupStateHealthy}, }, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "domain-with-update-error"}, Config: &persistence.DomainConfig{}, ReplicationConfig: &persistence.DomainReplicationConfig{}, IsGlobalDomain: false, }, nil) m.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(errors.New("update error")) }, expectedErr: errors.New("update error"), }, { name: "Failure Due to Replication Error (for Global Domains)", domain: "global-domain-with-replication-error", isolationGroups: types.IsolationGroupConfiguration{ "group1": {State: types.IsolationGroupStateHealthy}, }, isPrimaryCluster: true, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "global-domain-with-replication-error"}, Config: &persistence.DomainConfig{}, ReplicationConfig: &persistence.DomainReplicationConfig{}, IsGlobalDomain: true, }, nil) m.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(nil) r.EXPECT().HandleTransmissionTask(gomock.Any(), types.DomainOperationUpdate, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), true).Return(errors.New("replication error")) }, expectedErr: errors.New("replication error"), }, { name: "Failure due to GetMetadata error", domain: "test-domain", setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(nil, errors.New("metadata error")) }, expectedErr: errors.New("metadata error"), }, { name: "Failure due to nil domain config", domain: "test-domain", isolationGroups: types.IsolationGroupConfiguration{}, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "test-domain", ID: "domainID"}, Config: nil, ReplicationConfig: &persistence.DomainReplicationConfig{}, IsGlobalDomain: false, }, nil) }, expectedErr: fmt.Errorf("unable to load config for domain as expected"), }, { name: "Failure due to failover cool-down", domain: "test-domain", isolationGroups: types.IsolationGroupConfiguration{ "group1": {Name: "group1", State: types.IsolationGroupStateHealthy}, }, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) lastUpdatedWithinCoolDownPeriod := time.Now().UnixNano() - (500 * int64(time.Millisecond)) m.EXPECT().GetDomain(gomock.Any(), gomock.Eq(&persistence.GetDomainRequest{Name: "test-domain"})).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "test-domain", ID: "domainID"}, Config: &persistence.DomainConfig{ Retention: 7, EmitMetric: true, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, HistoryArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "https://test.history", VisibilityArchivalURI: "https://test.visibility", IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: false}, }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "active"}, {ClusterName: "standby"}}, }, ConfigVersion: 1, FailoverVersion: 123, IsGlobalDomain: false, LastUpdatedTime: lastUpdatedWithinCoolDownPeriod, }, nil) }, expectedErr: errDomainUpdateTooFrequent, }, { name: "Global domain update from non-primary cluster", domain: "global-domain", isolationGroups: types.IsolationGroupConfiguration{ "group1": {Name: "group1", State: types.IsolationGroupStateHealthy}, }, setupMocks: func(m *persistence.MockDomainManager, r *MockReplicator) { m.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) m.EXPECT().GetDomain(gomock.Any(), gomock.Eq(&persistence.GetDomainRequest{Name: "global-domain"})).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "global-domain", ID: "domainID"}, Config: &persistence.DomainConfig{ Retention: 7, }, ReplicationConfig: &persistence.DomainReplicationConfig{}, IsGlobalDomain: true, }, nil) }, expectedErr: errNotPrimaryCluster, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDomainManager := persistence.NewMockDomainManager(controller) mockReplicator := NewMockReplicator(controller) handler := newTestHandler(t, controller, mockDomainManager, tc.isPrimaryCluster, mockReplicator) tc.setupMocks(mockDomainManager, mockReplicator) err := handler.UpdateIsolationGroups(context.Background(), types.UpdateDomainIsolationGroupsRequest{ Domain: tc.domain, IsolationGroups: tc.isolationGroups, }) if tc.expectedErr != nil { assert.Error(t, err) assert.Equal(t, tc.expectedErr, err) } else { assert.NoError(t, err) } }) } } func TestHandler_UpdateAsyncWorkflowConfiguraton(t *testing.T) { testDomain := "testDomain" createDomainConfig := func(asyncEnabled bool) *persistence.DomainConfig { return &persistence.DomainConfig{ Retention: 10, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "https://history.example.com", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "https://visibility.example.com", BadBinaries: types.BadBinaries{}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: asyncEnabled}, } } tests := []struct { name string setupMocks func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) isPrimaryCluster bool request *types.UpdateDomainAsyncWorkflowConfiguratonRequest expectedErr error }{ { name: "success", setupMocks: func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) { mockDomainManager.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) mockDomainManager.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: testDomain}).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: "domainID", Name: testDomain}, Config: createDomainConfig(false), ConfigVersion: 1, FailoverVersion: 1, LastUpdatedTime: time.Now().Add(-10 * time.Minute).UnixNano(), }, nil) mockDomainManager.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, req *persistence.UpdateDomainRequest) error { expectedReq := &persistence.UpdateDomainRequest{ Info: &persistence.DomainInfo{ID: "domainID", Name: testDomain}, Config: createDomainConfig(true), ConfigVersion: 2, FailoverVersion: 1, FailoverNotificationVersion: 0, LastUpdatedTime: req.LastUpdatedTime, NotificationVersion: 1, } if !reflect.DeepEqual(req, expectedReq) { return fmt.Errorf("unexpected UpdateDomainRequest: got %+v, want %+v", req, expectedReq) } return nil }, ) }, request: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: testDomain, Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, }, }, expectedErr: nil, }, { name: "fail to get metadata", setupMocks: func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) { mockDomainManager.EXPECT().GetMetadata(gomock.Any()).Return(nil, fmt.Errorf("metadata error")) }, request: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: testDomain, }, expectedErr: fmt.Errorf("metadata error"), }, { name: "fail to get domain", setupMocks: func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) { mockDomainManager.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) mockDomainManager.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("get domain error")) }, request: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: testDomain, }, expectedErr: fmt.Errorf("get domain error"), }, { name: "fail due to nil domain config", setupMocks: func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) { mockDomainManager.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) mockDomainManager.EXPECT().GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: testDomain}).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: "domainID", Name: testDomain}, Config: nil, }, nil) }, request: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: testDomain, Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, }, }, expectedErr: fmt.Errorf("unable to load config for domain as expected"), }, { name: "fail due to update too frequent", setupMocks: func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) { mockDomainManager.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) lastUpdatedWithinCoolDownPeriod := time.Now().UnixNano() - (500 * int64(time.Millisecond)) mockDomainManager.EXPECT().GetDomain(gomock.Any(), gomock.Eq(&persistence.GetDomainRequest{Name: "test-domain"})).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "test-domain", ID: "domainID"}, Config: createDomainConfig(false), ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "active"}, {ClusterName: "standby"}}, }, ConfigVersion: 1, FailoverVersion: 123, IsGlobalDomain: false, LastUpdatedTime: lastUpdatedWithinCoolDownPeriod, }, nil) }, request: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, }, }, expectedErr: errDomainUpdateTooFrequent, }, { name: "fail due to non-primary cluster update for global domain", isPrimaryCluster: false, setupMocks: func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) { mockDomainManager.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) mockDomainManager.EXPECT().GetDomain(gomock.Any(), gomock.Eq(&persistence.GetDomainRequest{Name: "global-test-domain"})).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "global-test-domain", ID: "domainID"}, Config: createDomainConfig(false), ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "active"}, {ClusterName: "standby"}}, }, ConfigVersion: 1, FailoverVersion: 123, IsGlobalDomain: true, LastUpdatedTime: time.Now().Add(-24 * time.Hour).UnixNano(), }, nil) }, request: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "global-test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, }, }, expectedErr: errNotPrimaryCluster, }, { name: "configuration delete request", setupMocks: func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) { mockDomainManager.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) mockDomainManager.EXPECT().GetDomain(gomock.Any(), gomock.Eq(&persistence.GetDomainRequest{Name: "test-domain"})).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "test-domain", ID: "domainID"}, Config: createDomainConfig(true), ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "active"}, {ClusterName: "standby"}}, }, ConfigVersion: 1, FailoverVersion: 123, IsGlobalDomain: false, LastUpdatedTime: time.Now().Add(-24 * time.Hour).UnixNano(), }, nil) mockDomainManager.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, req *persistence.UpdateDomainRequest) error { if req.Config.AsyncWorkflowConfig.Enabled != false { return fmt.Errorf("unexpected UpdateDomainRequest for delete configuration") } return nil }, ) }, request: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: nil, }, expectedErr: nil, }, { name: "fail on domain manager UpdateDomain call", setupMocks: func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) { mockDomainManager.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) mockDomainManager.EXPECT().GetDomain(gomock.Any(), gomock.Eq(&persistence.GetDomainRequest{Name: "test-domain"})).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "test-domain", ID: "domainID"}, Config: createDomainConfig(false), ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "active"}, {ClusterName: "standby"}}, }, ConfigVersion: 1, FailoverVersion: 123, IsGlobalDomain: false, LastUpdatedTime: time.Now().Add(-24 * time.Hour).UnixNano(), }, nil) mockDomainManager.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(fmt.Errorf("update domain error")) }, request: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, }, }, expectedErr: fmt.Errorf("update domain error"), }, { name: "global domain update triggers replication", isPrimaryCluster: true, setupMocks: func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) { mockDomainManager.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) mockDomainManager.EXPECT().GetDomain(gomock.Any(), gomock.Eq(&persistence.GetDomainRequest{Name: "global-test-domain"})).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "global-test-domain", ID: "domainID"}, Config: createDomainConfig(false), ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "active"}, {ClusterName: "standby"}}, }, ConfigVersion: 1, FailoverVersion: 123, IsGlobalDomain: true, LastUpdatedTime: time.Now().Add(-24 * time.Hour).UnixNano(), }, nil) mockDomainManager.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, req *persistence.UpdateDomainRequest) error { if req.Config.AsyncWorkflowConfig.Enabled != true { return fmt.Errorf("unexpected UpdateDomainRequest for global domain update") } return nil }, ) mockReplicator.EXPECT().HandleTransmissionTask( gomock.Any(), types.DomainOperationUpdate, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), true, ).Return(nil) }, request: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "global-test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, }, }, expectedErr: nil, }, { name: "global domain update replication error", isPrimaryCluster: true, setupMocks: func(mockDomainManager *persistence.MockDomainManager, mockReplicator *MockReplicator) { mockDomainManager.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil) mockDomainManager.EXPECT().GetDomain(gomock.Any(), gomock.Eq(&persistence.GetDomainRequest{Name: "global-test-domain"})).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{Name: "global-test-domain", ID: "domainID"}, Config: createDomainConfig(false), ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "active"}, {ClusterName: "standby"}}, }, ConfigVersion: 1, FailoverVersion: 123, IsGlobalDomain: true, LastUpdatedTime: time.Now().Add(-24 * time.Hour).UnixNano(), }, nil) mockDomainManager.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, req *persistence.UpdateDomainRequest) error { // Validate the UpdateDomainRequest for global domain update if req.Config.AsyncWorkflowConfig.Enabled != true { return fmt.Errorf("unexpected UpdateDomainRequest for global domain update") } return nil }, ) mockReplicator.EXPECT().HandleTransmissionTask( gomock.Any(), types.DomainOperationUpdate, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), true, ).Return(fmt.Errorf("replication error")) }, request: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "global-test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, }, }, expectedErr: fmt.Errorf("replication error"), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDomainManager := persistence.NewMockDomainManager(ctrl) mockReplicator := NewMockReplicator(ctrl) handler := newTestHandler(t, ctrl, mockDomainManager, test.isPrimaryCluster, mockReplicator) test.setupMocks(mockDomainManager, mockReplicator) err := handler.UpdateAsyncWorkflowConfiguraton(context.Background(), *test.request) if test.expectedErr != nil { assert.EqualError(t, err, test.expectedErr.Error()) } else { assert.NoError(t, err) } }) } } func TestHandler_UpdateDomain(t *testing.T) { ctx := context.Background() maxLength := 1 testCases := []struct { name string setupMock func( domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator, ) request *types.UpdateDomainRequest response func(timeSource clock.MockedTimeSource) *types.UpdateDomainResponse err error }{ { name: "Success case - global domain force failover", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator) { domainResponse := &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, } domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(domainResponse, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) timeSource.Advance(time.Hour) failoverData, _ := json.Marshal([]FailoverEvent{{ EventTime: timeSource.Now(), FromCluster: cluster.TestCurrentClusterName, ToCluster: cluster.TestAlternativeClusterName, FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeForce).String(), }}) expectedUpdateRequest := &persistence.UpdateDomainRequest{ Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, Description: domainResponse.Info.Description, OwnerEmail: domainResponse.Info.OwnerEmail, Data: map[string]string{ "FailoverHistory": string(failoverData), }, }, Config: domainResponse.Config, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, PreviousFailoverVersion: commonconstants.InitialPreviousFailoverVersion, ConfigVersion: domainResponse.ConfigVersion, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, LastUpdatedTime: timeSource.Now().UnixNano(), } domainManager.EXPECT().UpdateDomain(ctx, expectedUpdateRequest).Return(nil).Times(1) domainReplicator.EXPECT(). HandleTransmissionTask( ctx, types.DomainOperationUpdate, expectedUpdateRequest.Info, domainResponse.Config, expectedUpdateRequest.ReplicationConfig, domainResponse.ConfigVersion, cluster.TestAlternativeClusterInitialFailoverVersion, commonconstants.InitialPreviousFailoverVersion, domainResponse.IsGlobalDomain, ).Return(nil).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, ActiveClusterName: common.Ptr(cluster.TestAlternativeClusterName), }, response: func(timeSource clock.MockedTimeSource) *types.UpdateDomainResponse { data, _ := json.Marshal([]FailoverEvent{{EventTime: timeSource.Now(), FromCluster: cluster.TestCurrentClusterName, ToCluster: cluster.TestAlternativeClusterName, FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeForce).String()}}) return &types.UpdateDomainResponse{ IsGlobalDomain: true, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion/cluster.TestFailoverVersionIncrement*cluster.TestFailoverVersionIncrement + cluster.TestAlternativeClusterInitialFailoverVersion, DomainInfo: &types.DomainInfo{ Name: constants.TestDomainName, UUID: constants.TestDomainID, Data: map[string]string{commonconstants.DomainDataKeyForFailoverHistory: string(data)}, Status: common.Ptr(types.DomainStatusRegistered), }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 1, EmitMetric: true, HistoryArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), VisibilityArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, } }, }, { name: "Success case - global domain grace failover", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator) { domainResponse := &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), FailoverVersion: 1, } domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(domainResponse, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) timeSource.Advance(time.Hour) data, _ := json.Marshal([]FailoverEvent{{EventTime: timeSource.Now(), FromCluster: cluster.TestAlternativeClusterName, ToCluster: cluster.TestCurrentClusterName, FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeGrace).String()}}) expectedUpdateRequest := &persistence.UpdateDomainRequest{ Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, Description: domainResponse.Info.Description, OwnerEmail: domainResponse.Info.OwnerEmail, Data: map[string]string{ commonconstants.DomainDataKeyForFailoverHistory: string(data), }, }, Config: domainResponse.Config, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, PreviousFailoverVersion: 1, ConfigVersion: domainResponse.ConfigVersion, FailoverVersion: 10, FailoverEndTime: common.Ptr(timeSource.Now().Add(time.Duration(10) * time.Second).UnixNano()), LastUpdatedTime: timeSource.Now().UnixNano(), } domainManager.EXPECT().UpdateDomain(ctx, expectedUpdateRequest).Return(nil).Times(1) domainReplicator.EXPECT(). HandleTransmissionTask( ctx, types.DomainOperationUpdate, expectedUpdateRequest.Info, domainResponse.Config, expectedUpdateRequest.ReplicationConfig, domainResponse.ConfigVersion, int64(10), int64(1), domainResponse.IsGlobalDomain, ).Return(nil).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, ActiveClusterName: common.Ptr(cluster.TestCurrentClusterName), FailoverTimeoutInSeconds: common.Int32Ptr(10), }, response: func(timeSource clock.MockedTimeSource) *types.UpdateDomainResponse { data, _ := json.Marshal([]FailoverEvent{{EventTime: timeSource.Now(), FromCluster: cluster.TestAlternativeClusterName, ToCluster: cluster.TestCurrentClusterName, FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeGrace).String()}}) return &types.UpdateDomainResponse{ IsGlobalDomain: true, FailoverVersion: cluster.TestFailoverVersionIncrement, DomainInfo: &types.DomainInfo{ Name: constants.TestDomainName, UUID: constants.TestDomainID, Data: map[string]string{commonconstants.DomainDataKeyForFailoverHistory: string(data)}, Status: common.Ptr(types.DomainStatusRegistered), }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 1, EmitMetric: true, HistoryArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), VisibilityArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, } }, }, { name: "Success case - global domain config change", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator) { domainResponse := &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, } domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(domainResponse, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) timeSource.Advance(time.Hour) expectedUpdateRequest := &persistence.UpdateDomainRequest{ Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, Description: domainResponse.Info.Description, OwnerEmail: domainResponse.Info.OwnerEmail, Data: nil, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: false, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfig: domainResponse.ReplicationConfig, PreviousFailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, ConfigVersion: domainResponse.ConfigVersion + 1, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, LastUpdatedTime: timeSource.Now().UnixNano(), } domainManager.EXPECT().UpdateDomain(ctx, expectedUpdateRequest).Return(nil).Times(1) domainReplicator.EXPECT(). HandleTransmissionTask( ctx, types.DomainOperationUpdate, expectedUpdateRequest.Info, expectedUpdateRequest.Config, domainResponse.ReplicationConfig, domainResponse.ConfigVersion+1, cluster.TestCurrentClusterInitialFailoverVersion, cluster.TestCurrentClusterInitialFailoverVersion, domainResponse.IsGlobalDomain, ).Return(nil).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, EmitMetric: common.Ptr(false), }, response: func(_ clock.MockedTimeSource) *types.UpdateDomainResponse { return &types.UpdateDomainResponse{ IsGlobalDomain: true, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, DomainInfo: &types.DomainInfo{ Name: constants.TestDomainName, UUID: constants.TestDomainID, Status: common.Ptr(types.DomainStatusRegistered), }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 1, EmitMetric: false, HistoryArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), VisibilityArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, } }, }, { name: "Success case - active-active domain active clusters change", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator) { domainResponse := &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, }, }, }, }, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, } domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(domainResponse, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) timeSource.Advance(time.Hour) var data map[string]string expectedUpdateRequest := &persistence.UpdateDomainRequest{ Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, Description: domainResponse.Info.Description, OwnerEmail: domainResponse.Info.OwnerEmail, Data: data, }, Config: domainResponse.Config, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestCurrentClusterName, // previously it was alternative cluster with failover version 1. // failover version should be the next failover version of the new cluster FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion + cluster.TestFailoverVersionIncrement, }, }, }, }, }, }, PreviousFailoverVersion: -1, // this is not applicable to active-active domain ConfigVersion: domainResponse.ConfigVersion + 1, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion + cluster.TestFailoverVersionIncrement, // this is incremented to indicate there was a change in replication config LastUpdatedTime: timeSource.Now().UnixNano(), } domainManager.EXPECT().UpdateDomain(ctx, expectedUpdateRequest).Return(nil).Times(1) domainReplicator.EXPECT(). HandleTransmissionTask( ctx, types.DomainOperationUpdate, expectedUpdateRequest.Info, domainResponse.Config, expectedUpdateRequest.ReplicationConfig, domainResponse.ConfigVersion+1, cluster.TestCurrentClusterInitialFailoverVersion+cluster.TestFailoverVersionIncrement, int64(-1), // previous failover version is not applicable to active-active domain domainResponse.IsGlobalDomain, ).Return(nil).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { // failover region2 to region1 ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: 123123123123, // this number will be ignored and replaced with appropriate failover version }, }, }, }, }, }, response: func(timeSource clock.MockedTimeSource) *types.UpdateDomainResponse { return &types.UpdateDomainResponse{ IsGlobalDomain: true, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion + cluster.TestFailoverVersionIncrement, DomainInfo: &types.DomainInfo{ Name: constants.TestDomainName, UUID: constants.TestDomainID, Status: common.Ptr(types.DomainStatusRegistered), }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 1, EmitMetric: true, HistoryArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), VisibilityArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion + cluster.TestFailoverVersionIncrement, }, }, }, }, }, }, } }, }, { name: "Success case - active-passive to active-active migration", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator) { domainResponse := &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusterName: cluster.TestCurrentClusterName, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), // once migrated to active clusters, this version should be carried over to new active clusters config FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion + 2*cluster.TestFailoverVersionIncrement, } domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(domainResponse, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) timeSource.Advance(time.Hour) var data map[string]string expectedUpdateRequest := &persistence.UpdateDomainRequest{ Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, Description: domainResponse.Info.Description, OwnerEmail: domainResponse.Info.OwnerEmail, Data: data, }, Config: domainResponse.Config, ReplicationConfig: &persistence.DomainReplicationConfig{ Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusterName: cluster.TestCurrentClusterName, // should be left as is ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, }, }, }, }, }, PreviousFailoverVersion: -1, // this is not applicable to active-active domain ConfigVersion: domainResponse.ConfigVersion + 1, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion + 3*cluster.TestFailoverVersionIncrement, // this is incremented to indicate there was a change in replication config LastUpdatedTime: timeSource.Now().UnixNano(), } domainManager.EXPECT().UpdateDomain(ctx, expectedUpdateRequest).Return(nil).Times(1) domainReplicator.EXPECT(). HandleTransmissionTask( ctx, types.DomainOperationUpdate, expectedUpdateRequest.Info, domainResponse.Config, expectedUpdateRequest.ReplicationConfig, domainResponse.ConfigVersion+1, cluster.TestCurrentClusterInitialFailoverVersion+3*cluster.TestFailoverVersionIncrement, int64(-1), // previous failover version is not applicable to active-active domain domainResponse.IsGlobalDomain, ).Return(nil).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, }, }, }, }, }, response: func(timeSource clock.MockedTimeSource) *types.UpdateDomainResponse { return &types.UpdateDomainResponse{ IsGlobalDomain: true, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion + 3*cluster.TestFailoverVersionIncrement, DomainInfo: &types.DomainInfo{ Name: constants.TestDomainName, UUID: constants.TestDomainID, Status: common.Ptr(types.DomainStatusRegistered), }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 1, EmitMetric: true, HistoryArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), VisibilityArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusterName: cluster.TestCurrentClusterName, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, }, }, }, }, }, } }, }, { name: "Error case - local domain force failover - shoudl not be able to failover a local domain", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, _ *MockReplicator) { domainResponse := &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}}, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, }, IsGlobalDomain: false, LastUpdatedTime: timeSource.Now().UnixNano(), FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, } domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(domainResponse, nil).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, ActiveClusterName: common.Ptr(cluster.TestCurrentClusterName), }, response: func(_ clock.MockedTimeSource) *types.UpdateDomainResponse { return &types.UpdateDomainResponse{ IsGlobalDomain: false, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion/cluster.TestFailoverVersionIncrement*cluster.TestFailoverVersionIncrement + cluster.TestCurrentClusterInitialFailoverVersion, DomainInfo: &types.DomainInfo{ Name: constants.TestDomainName, UUID: constants.TestDomainID, Status: common.Ptr(types.DomainStatusRegistered), }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 1, EmitMetric: true, HistoryArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), VisibilityArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, }, }, } }, err: errLocalDomainsCannotFailover, }, { name: "Error case - GetMetadata error", setupMock: func(domainManager *persistence.MockDomainManager, _ *types.UpdateDomainRequest, _ *archiver.MockArchivalMetadata, _ clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(nil, errors.New("get-metadata-error")).Times(1) }, err: errors.New("get-metadata-error"), }, { name: "Error case - GetDomain error", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, _ *archiver.MockArchivalMetadata, _ clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}).Return(nil, errors.New("get-domain-error")).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, }, err: errors.New("get-domain-error"), }, { name: "Error case - updateHistoryArchivalState error", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, _ clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(&persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{}, Config: &persistence.DomainConfig{}, }, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalEnabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalEnabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalEnabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, }, err: errInvalidEvent, }, { name: "Error case - updateVisibilityArchivalState error", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, _ clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(&persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{}, Config: &persistence.DomainConfig{}, }, nil).Times(1) archivalConfigHistory := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalConfigVisibility := archiver.NewArchivalConfig( commonconstants.ArchivalEnabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalEnabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalEnabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfigHistory).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfigVisibility).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, }, err: errInvalidEvent, }, { name: "Error case - updateDomainConfiguration error", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, _ clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(&persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{}, Config: &persistence.DomainConfig{}, LastUpdatedTime: time.Now().UnixNano(), }, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{ "bad-binary": { Reason: "test-reason", }, "bad-binary-2": { Reason: "test-reason-2", }, }}, }, err: &types.BadRequestError{ Message: fmt.Sprintf("Total resetBinaries cannot exceed the max limit: %v", maxLength), }, }, { name: "Error case - updateDeleteBadBinary error", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, _ clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(&persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{}, Config: &persistence.DomainConfig{}, }, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, DeleteBadBinary: common.Ptr("bad-binary"), }, err: &types.BadRequestError{ Message: fmt.Sprintf("Bad binary checksum %v doesn't exists.", "bad-binary"), }, }, { name: "Error case - handleGracefulFailover error in the case of a global domain - it should return an error to the user", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, _ clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ Name: constants.TestDomainName, }, ReplicationConfig: &persistence.DomainReplicationConfig{}, Config: &persistence.DomainConfig{}, IsGlobalDomain: true, }, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, FailoverTimeoutInSeconds: common.Int32Ptr(1), }, err: errInvalidFailoverNoChangeDetected, }, { name: "Error case - validateDomainConfig error", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, _ clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(&persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{}, Config: &persistence.DomainConfig{}, IsGlobalDomain: true, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, }, }, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, }, err: errInvalidRetentionPeriod, }, { name: "Error case - validateDomainReplicationConfigForUpdateDomain error", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, _ clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(&persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "bad-cluster", }, Config: &persistence.DomainConfig{ Retention: 1, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, }, IsGlobalDomain: true, }, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, }, err: &types.BadRequestError{Message: fmt.Sprintf( "Invalid cluster name: %v", "bad-cluster", )}, }, { name: "Error case - updateChangesForUpdateDomain error", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(&persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, Config: &persistence.DomainConfig{ Retention: 1, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), }, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) timeSource.Advance(-time.Hour) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, ActiveClusterName: common.Ptr(cluster.TestAlternativeClusterName), }, err: errDomainUpdateTooFrequent, }, { name: "Error case - domainManager.UpdateDomain error", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, _ *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(&persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, Config: &persistence.DomainConfig{ Retention: 1, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), }, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) timeSource.Advance(time.Hour) domainManager.EXPECT().UpdateDomain(ctx, gomock.Any()).Return(errors.New("update-domain-error")).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, ActiveClusterName: common.Ptr(cluster.TestAlternativeClusterName), }, err: errors.New("update-domain-error"), }, { name: "Success case - active-active domain failover with explicit ActiveClusterName uses correct cluster for failover version", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator) { domainResponse := &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, }, }, }, }, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, } domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(domainResponse, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) timeSource.Advance(time.Hour) var data map[string]string expectedUpdateRequest := &persistence.UpdateDomainRequest{ Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, Description: domainResponse.Info.Description, OwnerEmail: domainResponse.Info.OwnerEmail, Data: data, }, Config: domainResponse.Config, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, }, }, }, }, }, PreviousFailoverVersion: -1, ConfigVersion: domainResponse.ConfigVersion, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, LastUpdatedTime: timeSource.Now().UnixNano(), } domainManager.EXPECT().UpdateDomain(ctx, expectedUpdateRequest).Return(nil).Times(1) domainReplicator.EXPECT(). HandleTransmissionTask( ctx, types.DomainOperationUpdate, expectedUpdateRequest.Info, domainResponse.Config, expectedUpdateRequest.ReplicationConfig, domainResponse.ConfigVersion, cluster.TestAlternativeClusterInitialFailoverVersion, int64(-1), domainResponse.IsGlobalDomain, ).Return(nil).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, ActiveClusterName: common.Ptr(cluster.TestAlternativeClusterName), }, response: func(timeSource clock.MockedTimeSource) *types.UpdateDomainResponse { return &types.UpdateDomainResponse{ IsGlobalDomain: true, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, DomainInfo: &types.DomainInfo{ Name: constants.TestDomainName, UUID: constants.TestDomainID, Status: common.Ptr(types.DomainStatusRegistered), }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 1, EmitMetric: true, HistoryArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), VisibilityArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, }, }, }, }, }, } }, }, { name: "Error case - HandleTransmissionTask error", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.UpdateDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetName()}). Return(&persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, Config: &persistence.DomainConfig{ Retention: 1, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), }, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") archivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) archivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) timeSource.Advance(time.Hour) domainManager.EXPECT().UpdateDomain(ctx, gomock.Any()).Return(nil).Times(1) domainReplicator.EXPECT(). HandleTransmissionTask(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(errors.New("handle-transmission-task-error")).Times(1) }, request: &types.UpdateDomainRequest{ Name: constants.TestDomainName, ActiveClusterName: common.Ptr(cluster.TestAlternativeClusterName), }, err: errors.New("handle-transmission-task-error"), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDomainManager := persistence.NewMockDomainManager(ctrl) mockReplicator := NewMockReplicator(ctrl) mockArchivalMetadata := &archiver.MockArchivalMetadata{} mockDomainAuditManager := persistence.NewMockDomainAuditManager(ctrl) testConfig := Config{ MinRetentionDays: dynamicproperties.GetIntPropertyFn(1), MaxRetentionDays: dynamicproperties.GetIntPropertyFn(5), RequiredDomainDataKeys: nil, MaxBadBinaryCount: dynamicproperties.GetIntPropertyFilteredByDomain(maxLength), FailoverCoolDown: func(string) time.Duration { return time.Second }, FailoverHistoryMaxSize: dynamicproperties.GetIntPropertyFilteredByDomain(5), EnableDomainAuditLogging: dynamicproperties.GetBoolPropertyFn(true), } clusterMetadata := cluster.GetTestClusterMetadata(true) mockTimeSource := clock.NewMockedTimeSourceAt(time.Unix(1761769472, 0)) handler := handlerImpl{ domainManager: mockDomainManager, clusterMetadata: clusterMetadata, domainReplicator: mockReplicator, domainAttrValidator: newAttrValidator(clusterMetadata, int32(testConfig.MinRetentionDays())), archivalMetadata: mockArchivalMetadata, archiverProvider: provider.NewArchiverProvider(nil, nil), timeSource: mockTimeSource, config: testConfig, logger: log.NewNoop(), domainAuditManager: mockDomainAuditManager, } // For all tests in this suite, audit log succeeds mockDomainAuditManager.EXPECT(). CreateDomainAuditLog(gomock.Any(), gomock.Any()). Return(&persistence.CreateDomainAuditLogResponse{EventID: "test-event-id"}, nil). AnyTimes() tc.setupMock(mockDomainManager, tc.request, mockArchivalMetadata, mockTimeSource, mockReplicator) response, err := handler.UpdateDomain(ctx, tc.request) if tc.err != nil { assert.Error(t, err) assert.Equal(t, tc.err.Error(), err.Error()) } else { assert.NoError(t, err) assert.NotNil(t, response) assert.Equal(t, tc.response(mockTimeSource), response) } }) } } func TestHandler_UpdateDomain_AuditLogFailureDoesNotPropagate(t *testing.T) { // This test verifies that audit log write failures do not prevent domain updates from succeeding. // Audit logging is best-effort only and should not block critical domain operations. ctx := context.Background() ctrl := gomock.NewController(t) defer ctrl.Finish() mockDomainManager := persistence.NewMockDomainManager(ctrl) mockReplicator := NewMockReplicator(ctrl) mockArchivalMetadata := &archiver.MockArchivalMetadata{} mockDomainAuditManager := persistence.NewMockDomainAuditManager(ctrl) testConfig := Config{ MinRetentionDays: dynamicproperties.GetIntPropertyFn(1), MaxRetentionDays: dynamicproperties.GetIntPropertyFn(5), RequiredDomainDataKeys: nil, MaxBadBinaryCount: dynamicproperties.GetIntPropertyFilteredByDomain(1), FailoverCoolDown: func(string) time.Duration { return time.Second }, FailoverHistoryMaxSize: dynamicproperties.GetIntPropertyFilteredByDomain(5), EnableDomainAuditLogging: dynamicproperties.GetBoolPropertyFn(true), } clusterMetadata := cluster.GetTestClusterMetadata(true) mockTimeSource := clock.NewMockedTimeSourceAt(time.Unix(1761769472, 0)) handler := handlerImpl{ domainManager: mockDomainManager, clusterMetadata: clusterMetadata, domainReplicator: mockReplicator, domainAttrValidator: newAttrValidator(clusterMetadata, int32(testConfig.MinRetentionDays())), archivalMetadata: mockArchivalMetadata, archiverProvider: provider.NewArchiverProvider(nil, nil), timeSource: mockTimeSource, config: testConfig, logger: log.NewNoop(), domainAuditManager: mockDomainAuditManager, } domainResponse := &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, Description: "original-description", }, IsGlobalDomain: true, LastUpdatedTime: mockTimeSource.Now().UnixNano(), FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, } // Set up mocks mockDomainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) mockDomainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: constants.TestDomainName}). Return(domainResponse, nil).Times(1) archivalConfig := archiver.NewArchivalConfig( commonconstants.ArchivalDisabled, dynamicproperties.GetStringPropertyFn(commonconstants.ArchivalDisabled), false, dynamicproperties.GetBoolPropertyFn(false), commonconstants.ArchivalDisabled, "") mockArchivalMetadata.On("GetHistoryConfig").Return(archivalConfig).Times(1) mockArchivalMetadata.On("GetVisibilityConfig").Return(archivalConfig).Times(1) mockTimeSource.Advance(time.Hour) mockDomainManager.EXPECT().UpdateDomain(ctx, gomock.Any()).Return(nil).Times(1) mockReplicator.EXPECT(). HandleTransmissionTask( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(nil).Times(1) // Critical assertion: Set up audit log to FAIL mockDomainAuditManager.EXPECT(). CreateDomainAuditLog(gomock.Any(), gomock.Any()). Return(nil, errors.New("audit log database unavailable")). Times(1) // Execute the update updateRequest := &types.UpdateDomainRequest{ Name: constants.TestDomainName, Description: common.Ptr("updated-description"), } response, err := handler.UpdateDomain(ctx, updateRequest) // Assert: Domain update should succeed despite audit log failure assert.NoError(t, err, "UpdateDomain should succeed even when audit log write fails") assert.NotNil(t, response, "Response should not be nil") // Verify the response contains the updated information assert.Equal(t, true, response.IsGlobalDomain) assert.Equal(t, cluster.TestCurrentClusterInitialFailoverVersion, response.FailoverVersion) assert.Equal(t, constants.TestDomainName, response.DomainInfo.Name) assert.Equal(t, constants.TestDomainID, response.DomainInfo.UUID) assert.Equal(t, "updated-description", response.DomainInfo.Description) assert.Equal(t, types.DomainStatusRegistered, *response.DomainInfo.Status) } func TestUpdateDomainInfo(t *testing.T) { testCases := []struct { name string request *types.UpdateDomainRequest changed bool updatedDomainInfo *persistence.DomainInfo }{ { name: "Success case - new domain info", request: &types.UpdateDomainRequest{ Description: common.Ptr("new-description"), OwnerEmail: common.Ptr("new-email"), Data: map[string]string{"new-key": "new-value"}, }, changed: true, updatedDomainInfo: &persistence.DomainInfo{ ID: constants.TestDomainID, Name: constants.TestDomainName, Status: persistence.DomainStatusRegistered, Description: "new-description", OwnerEmail: "new-email", Data: map[string]string{"key": "value", "new-key": "new-value"}, }, }, { name: "Success case - no new domain info in request", request: &types.UpdateDomainRequest{}, updatedDomainInfo: &persistence.DomainInfo{ ID: constants.TestDomainID, Name: constants.TestDomainName, Status: persistence.DomainStatusRegistered, Description: "some-description", OwnerEmail: "some-email", Data: map[string]string{"key": "value"}, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) domainInfo := &persistence.DomainInfo{ ID: constants.TestDomainID, Name: constants.TestDomainName, Status: persistence.DomainStatusRegistered, Description: "some-description", OwnerEmail: "some-email", Data: map[string]string{"key": "value"}, } mockDomainMgr := persistence.NewMockDomainManager(controller) mockReplicator := NewMockReplicator(controller) handler := newTestHandler(t, controller, mockDomainMgr, true, mockReplicator) updatedDomainInfo, changed := (*handlerImpl).updateDomainInfo(handler.(*handlerImpl), tc.request, domainInfo) assert.Equal(t, tc.changed, changed) assert.Equal(t, tc.updatedDomainInfo, updatedDomainInfo) }) } } func TestUpdateDomainConfiguration(t *testing.T) { testCases := []struct { name string request *types.UpdateDomainRequest changed bool updatedDomainConfig func(now int64) *persistence.DomainConfig err error }{ { name: "Success case - new domain config", request: &types.UpdateDomainRequest{ EmitMetric: common.Ptr(false), WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(3), BadBinaries: &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "bad-binary": { Reason: "test-reason", }, }, }, }, changed: true, updatedDomainConfig: func(now int64) *persistence.DomainConfig { return &persistence.DomainConfig{ Retention: 3, EmitMetric: false, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "bad-binary": { Reason: "test-reason", CreatedTimeNano: common.Ptr(now), }, }, }, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, } }, }, { name: "Success case - new domain config - only new bad binaries", request: &types.UpdateDomainRequest{ BadBinaries: &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "bad-binary": { Reason: "test-reason", }, }, }, }, changed: false, updatedDomainConfig: func(now int64) *persistence.DomainConfig { return &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "bad-binary": { Reason: "test-reason", CreatedTimeNano: common.Ptr(now), }, }, }, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, } }, }, { name: "Error case - new domain config has bad binaries greater than max length", request: &types.UpdateDomainRequest{ EmitMetric: common.Ptr(false), WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(3), BadBinaries: &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "bad-binary": { Reason: "test-reason", }, "bad-binary-2": { Reason: "test-reason-2", }, "bad-binary-3": { Reason: "test-reason-3", }, "bad-binary-4": { Reason: "test-reason-4", }, }, }, }, err: &types.BadRequestError{ Message: fmt.Sprintf("Total resetBinaries cannot exceed the max limit: %v", 3), }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDomainMgr := persistence.NewMockDomainManager(controller) mockReplicator := NewMockReplicator(controller) handler := newTestHandler(t, controller, mockDomainMgr, true, mockReplicator) cfg := &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, } now := handler.(*handlerImpl).timeSource.Now().UnixNano() updatedDomainConfig, changed, err := (*handlerImpl).updateDomainConfiguration(handler.(*handlerImpl), constants.TestDomainName, cfg, tc.request) if tc.err != nil { assert.Error(t, err) assert.Equal(t, tc.err, err) } else { assert.NoError(t, err) assert.Equal(t, tc.changed, changed) assert.Equal(t, tc.updatedDomainConfig(now), updatedDomainConfig) } }) } } func TestUpdateDeleteBadBinary(t *testing.T) { now := time.Now().UnixNano() testCases := []struct { name string deleteBadBinary *string changed bool updatedDomainConfig *persistence.DomainConfig err error }{ { name: "Success case - deleteBadBinary not nil", deleteBadBinary: common.Ptr("bad-binary"), changed: true, updatedDomainConfig: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, }, { name: "Success case - deleteBadBinary nil", updatedDomainConfig: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{ "bad-binary": { Reason: "test-reason", CreatedTimeNano: &now, }, }}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, }, { name: "Error case - deleteBadBinary not in config.BadBinaries.Binaries", deleteBadBinary: common.Ptr("bad-binary-2"), err: &types.BadRequestError{Message: "Bad binary checksum bad-binary-2 doesn't exists."}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDomainMgr := persistence.NewMockDomainManager(controller) mockReplicator := NewMockReplicator(controller) handler := newTestHandler(t, controller, mockDomainMgr, true, mockReplicator) cfg := &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{ "bad-binary": { Reason: "test-reason", CreatedTimeNano: &now, }, }}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, } updatedDomainConfig, changed, err := (*handlerImpl).updateDeleteBadBinary(handler.(*handlerImpl), cfg, tc.deleteBadBinary) if tc.err != nil { assert.Error(t, err) assert.Equal(t, tc.err, err) } else { assert.NoError(t, err) assert.Equal(t, tc.changed, changed) assert.Equal(t, tc.updatedDomainConfig, updatedDomainConfig) } }) } } func TestUpdateReplicationConfig(t *testing.T) { cfg := func() *persistence.DomainReplicationConfig { return &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, } } activeActiveCfg := func() *persistence.DomainReplicationConfig { return &persistence.DomainReplicationConfig{ Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, }, }, }, }, } } testCases := []struct { name string request *types.UpdateDomainRequest currentReplicationConfig *persistence.DomainReplicationConfig updatedReplicationConfig *persistence.DomainReplicationConfig clusterUpdated bool activeClusterUpdated bool }{ { name: "Success case - no change", request: &types.UpdateDomainRequest{}, currentReplicationConfig: cfg(), updatedReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, }, { name: "Success case - cluster and activeCluster updated", request: &types.UpdateDomainRequest{ ActiveClusterName: common.Ptr(cluster.TestAlternativeClusterName), Clusters: []*types.ClusterReplicationConfiguration{{ClusterName: cluster.TestDisabledClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, {ClusterName: cluster.TestCurrentClusterName}}, }, currentReplicationConfig: cfg(), updatedReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestDisabledClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, {ClusterName: cluster.TestCurrentClusterName}}, }, clusterUpdated: true, activeClusterUpdated: true, }, { name: "Success case - cluster and activeCluster updated with warning", request: &types.UpdateDomainRequest{ ActiveClusterName: common.Ptr(cluster.TestAlternativeClusterName), Clusters: []*types.ClusterReplicationConfiguration{{ClusterName: cluster.TestAlternativeClusterName}}, }, currentReplicationConfig: cfg(), updatedReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestAlternativeClusterName}}, }, clusterUpdated: true, activeClusterUpdated: true, }, { name: "active-active domain - update cluster of region2", request: &types.UpdateDomainRequest{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion2: { // changing this from alternative cluster to current cluster ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: 99999, // this will be ignored }, }, }, }, }, }, currentReplicationConfig: activeActiveCfg(), updatedReplicationConfig: &persistence.DomainReplicationConfig{ Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion + cluster.TestFailoverVersionIncrement, }, }, }, }, }, }, clusterUpdated: true, activeClusterUpdated: true, }, { name: "active-active domain - add a new cluster in a new region", request: &types.UpdateDomainRequest{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region3": { ActiveClusterName: cluster.TestCurrentClusterName, }, }, }, }, }, }, currentReplicationConfig: activeActiveCfg(), updatedReplicationConfig: &persistence.DomainReplicationConfig{ Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ cluster.TestRegion1: { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, cluster.TestRegion2: { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, "region3": { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, }, }, }, }, }, clusterUpdated: true, activeClusterUpdated: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDomainMgr := persistence.NewMockDomainManager(controller) mockReplicator := NewMockReplicator(controller) handler := newTestHandler(t, controller, mockDomainMgr, true, mockReplicator).(*handlerImpl) updatedReplicationConfig, clusterUpdated, activeClusterUpdated, err := handler.updateReplicationConfig( constants.TestDomainName, tc.currentReplicationConfig, tc.request, ) assert.NoError(t, err) assert.Equal(t, tc.clusterUpdated, clusterUpdated, "cluster-updated field was %v when it was expected to be %v", clusterUpdated, tc.clusterUpdated) assert.Equal(t, tc.activeClusterUpdated, activeClusterUpdated, "active-cluster-updated field was %v when it was expected to be %v", activeClusterUpdated, tc.activeClusterUpdated) assert.Equal(t, tc.updatedReplicationConfig, updatedReplicationConfig, "replication-config-updated was flagged as %v when it was expected to be %v", updatedReplicationConfig, tc.updatedReplicationConfig) }) } } func TestHandleGracefulFailover(t *testing.T) { failoverTimeoutInSeconds := int32(1) failoverVersion := int64(3) testCases := []struct { name string replicationConfig *persistence.DomainReplicationConfig currentActiveCluster string gracefulFailoverEndTime *int64 activeClusterChange bool isGlobalDomain bool updatedGracefulFailoverEndTime func(now time.Time) *int64 err error }{ { name: "Success case", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, currentActiveCluster: cluster.TestAlternativeClusterName, activeClusterChange: true, isGlobalDomain: true, updatedGracefulFailoverEndTime: func(now time.Time) *int64 { return common.Ptr(now.Add(time.Duration(failoverTimeoutInSeconds) * time.Second).UnixNano()) }, }, { name: "Error case - activeClusterChange is false", activeClusterChange: false, isGlobalDomain: true, err: errInvalidGracefulFailover, }, { name: "Error case - isGlobalDomain is false", activeClusterChange: true, isGlobalDomain: false, err: errInvalidGracefulFailover, }, { name: "Error case - replication ActiveClusterName is different from clusterMetadata currentClusterName", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, activeClusterChange: true, isGlobalDomain: true, err: errCannotDoGracefulFailoverFromCluster, }, { name: "Error case - replication ActiveClusterName is the same as target currentActiveCluster", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, currentActiveCluster: cluster.TestCurrentClusterName, activeClusterChange: true, isGlobalDomain: true, err: errGracefulFailoverInActiveCluster, }, { name: "Error case - ongoing failover, cannot have concurrent failover", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, currentActiveCluster: cluster.TestAlternativeClusterName, activeClusterChange: true, isGlobalDomain: true, gracefulFailoverEndTime: common.Int64Ptr(time.Now().UnixNano()), err: errOngoingGracefulFailover, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDomainMgr := persistence.NewMockDomainManager(controller) mockReplicator := NewMockReplicator(controller) handler := newTestHandler(t, controller, mockDomainMgr, true, mockReplicator) now := handler.(*handlerImpl).timeSource.Now() request := &types.UpdateDomainRequest{ FailoverTimeoutInSeconds: common.Int32Ptr(failoverTimeoutInSeconds), } gracefulFailoverEndTime, previousFailoverVersion, err := (*handlerImpl).handleGracefulFailover( handler.(*handlerImpl), request, tc.replicationConfig, tc.currentActiveCluster, tc.gracefulFailoverEndTime, failoverVersion, tc.activeClusterChange, tc.isGlobalDomain, ) if tc.err != nil { assert.Error(t, err) assert.Equal(t, tc.err, err) } else { assert.NoError(t, err) assert.Equal(t, tc.updatedGracefulFailoverEndTime(now), gracefulFailoverEndTime) assert.Equal(t, failoverVersion, previousFailoverVersion) } }) } } func TestUpdateFailoverHistory(t *testing.T) { now := time.Now() fromCluster := "fromCluster" toCluster := "toCluster" failoverType := commonconstants.FailoverType(commonconstants.FailoverTypeForce) failoverHistoryMaxSize := 5 testCases := []struct { name string domainInfo func() *persistence.DomainInfo newFailoverEvent FailoverEvent response func() string err error }{ { name: "Success case - DomainInfo data is nil", domainInfo: func() *persistence.DomainInfo { return &persistence.DomainInfo{} }, newFailoverEvent: FailoverEvent{ EventTime: now, FromCluster: fromCluster, ToCluster: toCluster, FailoverType: failoverType.String(), }, response: func() string { failoverHistory := []FailoverEvent{{EventTime: now, FromCluster: fromCluster, ToCluster: toCluster, FailoverType: failoverType.String()}} jsonResp, _ := json.Marshal(failoverHistory) return string(jsonResp) }, }, { name: "Success case - FailoverHistory is nil", domainInfo: func() *persistence.DomainInfo { return &persistence.DomainInfo{Data: map[string]string{}} }, newFailoverEvent: FailoverEvent{ EventTime: now, FromCluster: fromCluster, ToCluster: toCluster, FailoverType: failoverType.String(), }, response: func() string { failoverHistory := []FailoverEvent{{EventTime: now, FromCluster: fromCluster, ToCluster: toCluster, FailoverType: failoverType.String()}} jsonResp, _ := json.Marshal(failoverHistory) return string(jsonResp) }, }, { name: "Success case - FailoverHistory is not nil", domainInfo: func() *persistence.DomainInfo { eventTime := time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC) failoverHistory := []FailoverEvent{{EventTime: eventTime, FromCluster: "fromCluster1", ToCluster: "toCluster1", FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeGrace).String()}} failoverHistoryJSON, _ := json.Marshal(failoverHistory) return &persistence.DomainInfo{Data: map[string]string{commonconstants.DomainDataKeyForFailoverHistory: string(failoverHistoryJSON)}} }, newFailoverEvent: FailoverEvent{ EventTime: now, FromCluster: fromCluster, ToCluster: toCluster, FailoverType: failoverType.String(), }, response: func() string { eventTime := time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC) failoverHistory := []FailoverEvent{{EventTime: eventTime, FromCluster: "fromCluster1", ToCluster: "toCluster1", FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeGrace).String()}} failoverHistory = append([]FailoverEvent{{EventTime: now, FromCluster: fromCluster, ToCluster: toCluster, FailoverType: failoverType.String()}}, failoverHistory...) jsonResp, _ := json.Marshal(failoverHistory) return string(jsonResp) }, }, { name: "Success case - active-passive to active-active", domainInfo: func() *persistence.DomainInfo { eventTime := time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC) failoverHistory := []FailoverEvent{{EventTime: eventTime, FromCluster: "fromCluster1", ToCluster: "toCluster1", FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeGrace).String()}} failoverHistoryJSON, _ := json.Marshal(failoverHistory) return &persistence.DomainInfo{Data: map[string]string{commonconstants.DomainDataKeyForFailoverHistory: string(failoverHistoryJSON)}} }, newFailoverEvent: FailoverEvent{ EventTime: now, FromCluster: fromCluster, ToCluster: "", FailoverType: failoverType.String(), }, response: func() string { eventTime := time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC) failoverHistory := []FailoverEvent{{EventTime: eventTime, FromCluster: "fromCluster1", ToCluster: "toCluster1", FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeGrace).String()}} failoverHistory = append([]FailoverEvent{ { EventTime: now, FromCluster: fromCluster, ToCluster: "", FailoverType: failoverType.String(), }}, failoverHistory...) jsonResp, _ := json.Marshal(failoverHistory) return string(jsonResp) }, }, { name: "Success case - FailoverHistory is at max size", domainInfo: func() *persistence.DomainInfo { var failoverHistory []FailoverEvent for i := 0; i < failoverHistoryMaxSize; i++ { eventTime := time.Date(2021, 1, i, 1, 1, 1, 1, time.UTC) failoverHistory = append(failoverHistory, FailoverEvent{EventTime: eventTime, FromCluster: "fromCluster" + strconv.Itoa(i), ToCluster: "toCluster" + strconv.Itoa(i), FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeGrace).String()}) } failoverHistoryJSON, _ := json.Marshal(failoverHistory) return &persistence.DomainInfo{Data: map[string]string{commonconstants.DomainDataKeyForFailoverHistory: string(failoverHistoryJSON)}} }, newFailoverEvent: FailoverEvent{ EventTime: now, FromCluster: fromCluster, ToCluster: toCluster, FailoverType: failoverType.String(), }, response: func() string { var failoverHistory []FailoverEvent for i := 0; i < 5; i++ { eventTime := time.Date(2021, 1, i, 1, 1, 1, 1, time.UTC) failoverHistory = append(failoverHistory, FailoverEvent{EventTime: eventTime, FromCluster: "fromCluster" + strconv.Itoa(i), ToCluster: "toCluster" + strconv.Itoa(i), FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeGrace).String()}) } failoverHistory = append([]FailoverEvent{{EventTime: now, FromCluster: fromCluster, ToCluster: toCluster, FailoverType: failoverType.String()}}, failoverHistory[:(5-1)]...) jsonResp, _ := json.Marshal(failoverHistory) return string(jsonResp) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { cfg := Config{ FailoverHistoryMaxSize: func(domain string) int { return failoverHistoryMaxSize }, } domainInfo := tc.domainInfo() err := updateFailoverHistoryInDomainData(domainInfo, cfg, tc.newFailoverEvent) if tc.err != nil { assert.Equal(t, tc.err, err) } else { assert.NoError(t, err) assert.NotNil(t, domainInfo.Data) assert.NotNil(t, domainInfo.Data[commonconstants.DomainDataKeyForFailoverHistory]) assert.Equal(t, tc.response(), domainInfo.Data[commonconstants.DomainDataKeyForFailoverHistory]) } }) } } func TestHandler_FailoverDomain(t *testing.T) { ctx := context.Background() maxLength := 1 clusterA := "cluster-a" clusterAInitialFailoverVersion := int64(1) clusterB := "cluster-b" clusterBInitialFailoverVersion := int64(2) testCases := []struct { name string setupMock func( domainManager *persistence.MockDomainManager, updateRequest *types.FailoverDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator, ) request *types.FailoverDomainRequest response func(timeSource clock.MockedTimeSource) *types.FailoverDomainResponse err error }{ { name: "Success case - active/passive domain - global domain force failover - failing over from cluster A to cluster B", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.FailoverDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator) { domainResponse := &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: clusterA, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: clusterA}, {ClusterName: clusterB}}, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), FailoverVersion: clusterAInitialFailoverVersion, } domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{ NotificationVersion: 15, }, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetDomainName()}). Return(domainResponse, nil).Times(1) timeSource.Advance(time.Hour) failoverHistoryJSON, _ := json.Marshal([]FailoverEvent{ {EventTime: timeSource.Now(), FromCluster: clusterA, ToCluster: clusterB, FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeForce).String()}, }) updateExpectation := &persistence.UpdateDomainRequest{ Info: &persistence.DomainInfo{ ID: constants.TestDomainID, Name: constants.TestDomainName, Status: persistence.DomainStatusRegistered, Data: map[string]string{ commonconstants.DomainDataKeyForFailoverHistory: string(failoverHistoryJSON), }, }, Config: domainResponse.Config, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: clusterB, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: clusterA}, {ClusterName: clusterB}}, }, PreviousFailoverVersion: commonconstants.InitialPreviousFailoverVersion, ConfigVersion: domainResponse.ConfigVersion, FailoverVersion: 2, LastUpdatedTime: timeSource.Now().UnixNano(), FailoverNotificationVersion: 15, NotificationVersion: 15, } domainManager.EXPECT().UpdateDomain(ctx, updateExpectation).Return(nil).Times(1) domainReplicator.EXPECT(). HandleTransmissionTask( ctx, types.DomainOperationUpdate, &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, Data: map[string]string{ commonconstants.DomainDataKeyForFailoverHistory: string(failoverHistoryJSON), }, }, domainResponse.Config, &persistence.DomainReplicationConfig{ ActiveClusterName: clusterB, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: clusterA}, {ClusterName: clusterB}}, }, domainResponse.ConfigVersion, clusterBInitialFailoverVersion, commonconstants.InitialPreviousFailoverVersion, true, ).Return(nil).Times(1) }, request: &types.FailoverDomainRequest{ DomainName: constants.TestDomainName, DomainActiveClusterName: common.Ptr(clusterB), }, response: func(timeSource clock.MockedTimeSource) *types.FailoverDomainResponse { data, _ := json.Marshal([]FailoverEvent{ {EventTime: timeSource.Now(), FromCluster: clusterA, ToCluster: clusterB, FailoverType: commonconstants.FailoverType(commonconstants.FailoverTypeForce).String()}, }) return &types.FailoverDomainResponse{ IsGlobalDomain: true, FailoverVersion: clusterBInitialFailoverVersion, DomainInfo: &types.DomainInfo{ Name: constants.TestDomainName, UUID: constants.TestDomainID, Data: map[string]string{commonconstants.DomainDataKeyForFailoverHistory: string(data)}, Status: common.Ptr(types.DomainStatusRegistered), }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 1, EmitMetric: true, HistoryArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), VisibilityArchivalStatus: common.Ptr(types.ArchivalStatusDisabled), BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterB, Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: clusterA}, {ClusterName: clusterB}, }, }, } }, }, { name: "Error case - domain not found", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.FailoverDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator) { domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetDomainName()}). Return(nil, &types.EntityNotExistsError{Message: "Domain not found"}).Times(1) }, request: &types.FailoverDomainRequest{ DomainName: constants.TestDomainName, DomainActiveClusterName: common.Ptr(cluster.TestAlternativeClusterName), }, err: &types.EntityNotExistsError{Message: "Domain not found"}, }, { name: "Error case - update too frequent", setupMock: func(domainManager *persistence.MockDomainManager, updateRequest *types.FailoverDomainRequest, archivalMetadata *archiver.MockArchivalMetadata, timeSource clock.MockedTimeSource, domainReplicator *MockReplicator) { domainResponse := &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, Info: &persistence.DomainInfo{ Name: constants.TestDomainName, ID: constants.TestDomainID, Status: persistence.DomainStatusRegistered, }, IsGlobalDomain: true, LastUpdatedTime: timeSource.Now().UnixNano(), // Set to current time to trigger cool down FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, } domainManager.EXPECT().GetMetadata(ctx).Return(&persistence.GetMetadataResponse{}, nil).Times(1) domainManager.EXPECT().GetDomain(ctx, &persistence.GetDomainRequest{Name: updateRequest.GetDomainName()}). Return(domainResponse, nil).Times(1) }, request: &types.FailoverDomainRequest{ DomainName: constants.TestDomainName, DomainActiveClusterName: common.Ptr(cluster.TestCurrentClusterName), }, err: errDomainUpdateTooFrequent, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDomainManager := persistence.NewMockDomainManager(ctrl) mockReplicator := NewMockReplicator(ctrl) mockArchivalMetadata := &archiver.MockArchivalMetadata{} testConfig := Config{ MinRetentionDays: dynamicproperties.GetIntPropertyFn(1), MaxRetentionDays: dynamicproperties.GetIntPropertyFn(5), RequiredDomainDataKeys: nil, MaxBadBinaryCount: dynamicproperties.GetIntPropertyFilteredByDomain(maxLength), FailoverCoolDown: func(string) time.Duration { return time.Second }, FailoverHistoryMaxSize: dynamicproperties.GetIntPropertyFilteredByDomain(5), } clusterMetadata := cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 100, PrimaryClusterName: clusterA, CurrentClusterName: clusterA, ClusterGroup: map[string]config.ClusterInformation{ clusterA: { Enabled: true, InitialFailoverVersion: clusterAInitialFailoverVersion, }, clusterB: { Enabled: true, InitialFailoverVersion: clusterBInitialFailoverVersion, }, }, }, func(d string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), ) mockTimeSource := clock.NewMockedTimeSourceAt(time.Unix(1730419200, 0)) handler := handlerImpl{ domainManager: mockDomainManager, clusterMetadata: clusterMetadata, domainReplicator: mockReplicator, domainAttrValidator: newAttrValidator(clusterMetadata, int32(testConfig.MinRetentionDays())), archivalMetadata: mockArchivalMetadata, archiverProvider: provider.NewArchiverProvider(nil, nil), timeSource: mockTimeSource, config: testConfig, logger: log.NewNoop(), } tc.setupMock(mockDomainManager, tc.request, mockArchivalMetadata, mockTimeSource, mockReplicator) response, err := handler.FailoverDomain(ctx, tc.request) if tc.err != nil { assert.Error(t, err) assert.Equal(t, tc.err.Error(), err.Error()) } else { assert.NoError(t, err) assert.NotNil(t, response) assert.Equal(t, tc.response(mockTimeSource), response) } }) } } func TestBuildActiveActiveClustersFromUpdateRequest(t *testing.T) { testsCases := map[string]struct { updateRequest *types.UpdateDomainRequest config *persistence.DomainReplicationConfig domainName string handler *handlerImpl expectedActiveClusters *types.ActiveClusters expectedIsChanged bool }{ "Success case - ActiveClusters - failover event - where there's existing cluster attributes and we expect them to be incremented": { updateRequest: &types.UpdateDomainRequest{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "nyc": { ActiveClusterName: "clusterC", // this is expected to be a failvoer to cluster C from the existing A }, }, }, }, }, }, config: &persistence.DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "nyc": { ActiveClusterName: "clusterA", FailoverVersion: 100, }, "morocco": { ActiveClusterName: "clusterB", FailoverVersion: 1, }, "tokyo": { ActiveClusterName: "clusterC", FailoverVersion: 2, }, }, }, }, }, }, expectedActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "nyc": { ActiveClusterName: "clusterC", FailoverVersion: 102, }, "morocco": { ActiveClusterName: "clusterB", FailoverVersion: 1, }, "tokyo": { ActiveClusterName: "clusterC", FailoverVersion: 2, }, }, }, }, }, expectedIsChanged: true, }, "Success case - ActiveClusters - where there is the introduction of cluster attributes for the first time - we should see that these results are reflected": { updateRequest: &types.UpdateDomainRequest{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "nyc": { ActiveClusterName: "clusterA", // failover version can be absent }, "morocco": { ActiveClusterName: "clusterB", // failover version can be absent }, "tokyo": { ActiveClusterName: "clusterC", // failover version can be absent }, }, }, }, }, }, expectedActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "nyc": { ActiveClusterName: "clusterA", FailoverVersion: 0, }, "morocco": { ActiveClusterName: "clusterB", FailoverVersion: 1, }, "tokyo": { ActiveClusterName: "clusterC", FailoverVersion: 2, }, }, }, }, }, expectedIsChanged: true, }, "Success case - ActiveClusters - where there existing cluster attributes. These should be merged": { updateRequest: &types.UpdateDomainRequest{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "nyc": { ActiveClusterName: "clusterA", // failover version can be absent }, }, }, }, }, }, config: &persistence.DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "tokyo": { ActiveClusterName: "clusterC", FailoverVersion: 2, }, "morocco": { ActiveClusterName: "clusterB", FailoverVersion: 1, }, }, }, }, }, }, expectedActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "nyc": { ActiveClusterName: "clusterA", FailoverVersion: 0, }, "tokyo": { ActiveClusterName: "clusterC", FailoverVersion: 2, }, "morocco": { ActiveClusterName: "clusterB", FailoverVersion: 1, }, }, }, }, }, expectedIsChanged: true, }, "Success case - AttributeScopes is nil": { updateRequest: &types.UpdateDomainRequest{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: nil, }, }, expectedActiveClusters: nil, expectedIsChanged: false, }, } for name, tc := range testsCases { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDomainManager := persistence.NewMockDomainManager(ctrl) metadata := cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 100, ClusterGroup: map[string]config.ClusterInformation{ "clusterA": { InitialFailoverVersion: 0, }, "clusterB": { InitialFailoverVersion: 1, }, "clusterC": { InitialFailoverVersion: 2, }, "clusterD": { InitialFailoverVersion: 3, }, }, }, func(d string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), ) mockTimeSource := clock.NewMockedTimeSource() handler := handlerImpl{ domainManager: mockDomainManager, clusterMetadata: metadata, archiverProvider: provider.NewArchiverProvider(nil, nil), timeSource: mockTimeSource, logger: log.NewNoop(), } activeClusters, isChanged := handler.buildActiveActiveClusterScopesFromUpdateRequest(tc.updateRequest, tc.config, tc.domainName) assert.Equal(t, tc.expectedActiveClusters, activeClusters) assert.Equal(t, tc.expectedIsChanged, isChanged) }) } } func TestActiveClustersFromRegisterRequest(t *testing.T) { tests := []struct { name string request *types.RegisterDomainRequest expectedResult *types.ActiveClusters expectedErr error clusterMetadata func() cluster.Metadata }{ { name: "local domain returns nil", request: &types.RegisterDomainRequest{ Name: "test-domain", IsGlobalDomain: false, }, expectedResult: nil, expectedErr: nil, clusterMetadata: func() cluster.Metadata { return cluster.GetTestClusterMetadata(true) }, }, { name: "global domain with no active cluster data returns nil", request: &types.RegisterDomainRequest{ Name: "test-domain", IsGlobalDomain: true, }, expectedResult: nil, expectedErr: nil, clusterMetadata: func() cluster.Metadata { return cluster.GetTestClusterMetadata(true) }, }, { name: "new AttributeScopes with valid clusters", request: &types.RegisterDomainRequest{ Name: "test-domain", IsGlobalDomain: true, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: cluster.TestCurrentClusterName, }, "dc2": { ActiveClusterName: cluster.TestAlternativeClusterName, }, }, }, }, }, }, expectedResult: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, "dc2": { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, }, }, }, }, expectedErr: nil, clusterMetadata: func() cluster.Metadata { return cluster.GetTestClusterMetadata(true) }, }, { name: "new AttributeScopes with invalid cluster", request: &types.RegisterDomainRequest{ Name: "test-domain", IsGlobalDomain: true, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "invalid-cluster", }, }, }, }, }, }, expectedResult: nil, expectedErr: &types.BadRequestError{}, clusterMetadata: func() cluster.Metadata { return cluster.GetTestClusterMetadata(true) }, }, { name: "multiple scopes with multiple attributes", request: &types.RegisterDomainRequest{ Name: "test-domain", IsGlobalDomain: true, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: cluster.TestCurrentClusterName, }, "us-east": { ActiveClusterName: cluster.TestAlternativeClusterName, }, }, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: cluster.TestCurrentClusterName, }, }, }, }, }, }, expectedResult: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, "us-east": { ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: cluster.TestAlternativeClusterInitialFailoverVersion, }, }, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: cluster.TestCurrentClusterInitialFailoverVersion, }, }, }, }, }, expectedErr: nil, clusterMetadata: func() cluster.Metadata { return cluster.GetTestClusterMetadata(true) }, }, { name: "empty ActiveClusters with non-nil AttributeScopes", request: &types.RegisterDomainRequest{ Name: "test-domain", IsGlobalDomain: true, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{}, }, }, expectedResult: nil, expectedErr: nil, clusterMetadata: func() cluster.Metadata { return cluster.GetTestClusterMetadata(true) }, }, { name: "custom cluster metadata with different failover versions", request: &types.RegisterDomainRequest{ Name: "test-domain", IsGlobalDomain: true, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": {ActiveClusterName: "clusterA"}, "region2": {ActiveClusterName: "clusterB"}, }, }, }, }, }, expectedResult: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: "clusterA", FailoverVersion: 10, }, "region2": { ActiveClusterName: "clusterB", FailoverVersion: 20, }, }, }, }, }, expectedErr: nil, clusterMetadata: func() cluster.Metadata { return cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 100, PrimaryClusterName: "clusterA", CurrentClusterName: "clusterA", ClusterGroup: map[string]config.ClusterInformation{ "clusterA": { Enabled: true, InitialFailoverVersion: 10, }, "clusterB": { Enabled: true, InitialFailoverVersion: 20, }, }, }, func(d string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), ) }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { handler := &handlerImpl{ clusterMetadata: tc.clusterMetadata(), logger: log.NewNoop(), } result, err := handler.activeClustersFromRegisterRequest(tc.request) if tc.expectedErr != nil { assert.Error(t, err) assert.IsType(t, tc.expectedErr, err) if badReqErr, ok := tc.expectedErr.(*types.BadRequestError); ok { resultErr, ok := err.(*types.BadRequestError) assert.True(t, ok) if badReqErr.Message != "" { assert.Equal(t, badReqErr.Message, resultErr.Message) } } } else { assert.NoError(t, err) assert.Equal(t, tc.expectedResult, result) } }) } } func TestValidateDomainReplicationConfigForFailover(t *testing.T) { tests := []struct { name string replicationConfig *persistence.DomainReplicationConfig isGlobalDomain bool configurationChanged bool activeClusterChanged bool isPrimaryCluster bool expectedErr error }{ { name: "global domain with valid config on primary cluster - no changes", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, isGlobalDomain: true, configurationChanged: false, activeClusterChanged: false, isPrimaryCluster: true, expectedErr: nil, }, { name: "global domain config change only on primary cluster", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, isGlobalDomain: true, configurationChanged: true, activeClusterChanged: false, isPrimaryCluster: true, expectedErr: nil, }, { name: "global domain active cluster change only on primary cluster", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, isGlobalDomain: true, configurationChanged: false, activeClusterChanged: true, isPrimaryCluster: true, expectedErr: nil, }, { name: "global domain config change on non-primary cluster should fail", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, isGlobalDomain: true, configurationChanged: true, activeClusterChanged: false, isPrimaryCluster: false, expectedErr: errNotPrimaryCluster, }, { name: "global active-passive domain cannot change both config and active cluster", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, isGlobalDomain: true, configurationChanged: true, activeClusterChanged: true, isPrimaryCluster: true, expectedErr: errCannotDoDomainFailoverAndUpdate, }, { name: "global active-active domain can change both config and active cluster", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": {ActiveClusterName: cluster.TestCurrentClusterName}, "region2": {ActiveClusterName: cluster.TestAlternativeClusterName}, }, }, }, }, }, isGlobalDomain: true, configurationChanged: true, activeClusterChanged: true, isPrimaryCluster: true, expectedErr: nil, }, { name: "global active-active domain with AttributeScopes can change both", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": {ActiveClusterName: cluster.TestCurrentClusterName}, "dc2": {ActiveClusterName: cluster.TestAlternativeClusterName}, }, }, }, }, }, isGlobalDomain: true, configurationChanged: true, activeClusterChanged: true, isPrimaryCluster: true, expectedErr: nil, }, { name: "global domain with invalid cluster name", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "invalid-cluster", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "invalid-cluster"}, }, }, isGlobalDomain: true, configurationChanged: false, activeClusterChanged: false, isPrimaryCluster: true, expectedErr: &types.BadRequestError{}, }, { name: "global domain with no clusters should fail", replicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{}, }, isGlobalDomain: true, configurationChanged: false, activeClusterChanged: false, isPrimaryCluster: true, expectedErr: &types.BadRequestError{}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { clusterMetadata := cluster.GetTestClusterMetadata(tc.isPrimaryCluster) handler := &handlerImpl{ clusterMetadata: clusterMetadata, domainAttrValidator: newAttrValidator(clusterMetadata, 1), logger: log.NewNoop(), } err := handler.validateDomainReplicationConfigForFailover( tc.replicationConfig, tc.configurationChanged, tc.activeClusterChanged, ) if tc.expectedErr != nil { assert.Error(t, err) assert.IsType(t, tc.expectedErr, err) if tc.expectedErr == errNotPrimaryCluster { assert.Equal(t, errNotPrimaryCluster, err) } else if tc.expectedErr == errCannotDoDomainFailoverAndUpdate { assert.Equal(t, errCannotDoDomainFailoverAndUpdate, err) } } else { assert.NoError(t, err) } }) } } ================================================ FILE: common/domain/replicationTaskExecutor.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination replicationTaskHandler_mock.go package domain import ( "context" "errors" "fmt" "time" guuid "github.com/google/uuid" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) var ( // ErrEmptyDomainReplicationTask is the error to indicate empty replication task ErrEmptyDomainReplicationTask = &types.BadRequestError{Message: "empty domain replication task"} // ErrInvalidDomainOperation is the error to indicate empty domain operation attribute ErrInvalidDomainOperation = &types.BadRequestError{Message: "invalid domain operation attribute"} // ErrInvalidDomainID is the error to indicate empty rID attribute ErrInvalidDomainID = &types.BadRequestError{Message: "invalid domain ID attribute"} // ErrInvalidDomainInfo is the error to indicate empty info attribute ErrInvalidDomainInfo = &types.BadRequestError{Message: "invalid domain info attribute"} // ErrInvalidDomainConfig is the error to indicate empty config attribute ErrInvalidDomainConfig = &types.BadRequestError{Message: "invalid domain config attribute"} // ErrInvalidDomainReplicationConfig is the error to indicate empty replication config attribute ErrInvalidDomainReplicationConfig = &types.BadRequestError{Message: "invalid domain replication config attribute"} // ErrInvalidDomainStatus is the error to indicate invalid domain status ErrInvalidDomainStatus = &types.BadRequestError{Message: "invalid domain status attribute"} // ErrNameUUIDCollision is the error to indicate domain name / UUID collision ErrNameUUIDCollision = &types.BadRequestError{Message: "domain replication encounter name / UUID collision"} ) const ( defaultDomainRepliationTaskContextTimeout = 5 * time.Second ) // NOTE: the counterpart of domain replication transmission logic is in service/fropntend package type ( // ReplicationTaskExecutor is the interface which is to execute domain replication task ReplicationTaskExecutor interface { Execute(task *types.DomainTaskAttributes) error } domainReplicationTaskExecutorImpl struct { domainManager persistence.DomainManager timeSource clock.TimeSource logger log.Logger domainAuditManager persistence.DomainAuditManager enableDomainAuditLogging dynamicproperties.BoolPropertyFn } ) // NewReplicationTaskExecutor create a new instance of domain replicator func NewReplicationTaskExecutor( domainManager persistence.DomainManager, domainAuditManager persistence.DomainAuditManager, timeSource clock.TimeSource, logger log.Logger, enableDomainAuditLogging dynamicproperties.BoolPropertyFn, ) ReplicationTaskExecutor { return &domainReplicationTaskExecutorImpl{ domainManager: domainManager, timeSource: timeSource, logger: logger, domainAuditManager: domainAuditManager, enableDomainAuditLogging: enableDomainAuditLogging, } } // Execute handles receiving of the domain replication task func (h *domainReplicationTaskExecutorImpl) Execute(task *types.DomainTaskAttributes) error { ctx, cancel := context.WithTimeout(context.Background(), defaultDomainRepliationTaskContextTimeout) defer cancel() if err := h.validateDomainReplicationTask(task); err != nil { return err } switch task.GetDomainOperation() { case types.DomainOperationCreate: return h.handleDomainCreationReplicationTask(ctx, task) case types.DomainOperationUpdate: return h.handleDomainUpdateReplicationTask(ctx, task) case types.DomainOperationDelete: return h.handleDomainDeleteReplicationTask(ctx, task) default: return ErrInvalidDomainOperation } } // handleDomainCreationReplicationTask handles the domain creation replication task func (h *domainReplicationTaskExecutorImpl) handleDomainCreationReplicationTask(ctx context.Context, task *types.DomainTaskAttributes) error { // task already validated status, err := h.convertDomainStatusFromThrift(task.Info.Status) if err != nil { return err } request := &persistence.CreateDomainRequest{ Info: &persistence.DomainInfo{ ID: task.GetID(), Name: task.Info.GetName(), Status: status, Description: task.Info.GetDescription(), OwnerEmail: task.Info.GetOwnerEmail(), Data: task.Info.Data, }, Config: &persistence.DomainConfig{ Retention: task.Config.GetWorkflowExecutionRetentionPeriodInDays(), EmitMetric: task.Config.GetEmitMetric(), HistoryArchivalStatus: task.Config.GetHistoryArchivalStatus(), HistoryArchivalURI: task.Config.GetHistoryArchivalURI(), VisibilityArchivalStatus: task.Config.GetVisibilityArchivalStatus(), VisibilityArchivalURI: task.Config.GetVisibilityArchivalURI(), }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: task.ReplicationConfig.GetActiveClusterName(), Clusters: h.convertClusterReplicationConfigFromThrift(task.ReplicationConfig.Clusters), ActiveClusters: task.ReplicationConfig.GetActiveClusters(), }, IsGlobalDomain: true, // local domain will not be replicated ConfigVersion: task.GetConfigVersion(), FailoverVersion: task.GetFailoverVersion(), LastUpdatedTime: h.timeSource.Now().UnixNano(), } _, err = h.domainManager.CreateDomain(ctx, request) if err != nil { // SQL and Cassandra handle domain UUID collision differently // here, whenever seeing a error replicating a domain // do a check if there is a name / UUID collision recordExists := true resp, getErr := h.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{ Name: task.Info.GetName(), }) switch getErr.(type) { case nil: if resp.Info.ID != task.GetID() { return ErrNameUUIDCollision } case *types.EntityNotExistsError: // no check is necessary recordExists = false default: // return the original err return err } resp, getErr = h.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{ ID: task.GetID(), }) switch getErr.(type) { case nil: if resp.Info.Name != task.Info.GetName() { return ErrNameUUIDCollision } case *types.EntityNotExistsError: // no check is necessary recordExists = false default: // return the original err return err } if recordExists { // name -> id & id -> name check pass, this is duplication request return nil } return err } resp, getErr := h.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{ ID: task.GetID(), }) if getErr != nil { return fmt.Errorf("failed to get domain while trying to create domain audit log: %w", getErr) } h.createDomainAuditLog( ctx, task, persistence.DomainAuditOperationTypeCreate, nil, resp, ) return nil } // handleDomainUpdateReplicationTask handles the domain update replication task func (h *domainReplicationTaskExecutorImpl) handleDomainUpdateReplicationTask(ctx context.Context, task *types.DomainTaskAttributes) error { // task already validated status, err := h.convertDomainStatusFromThrift(task.Info.Status) if err != nil { return err } // first we need to get the current notification version since we need to it for conditional update metadata, err := h.domainManager.GetMetadata(ctx) if err != nil { h.logger.Error("Error getting metadata while handling replication task", tag.Error(err)) return err } notificationVersion := metadata.NotificationVersion // plus, we need to check whether the config version is <= the config version set in the input // plus, we need to check whether the failover version is <= the failover version set in the input originalDomainState, err := h.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{ ID: task.GetID(), }) intendedDomainState := originalDomainState.DeepCopy() if err != nil { if _, ok := err.(*types.EntityNotExistsError); ok { // this can happen if the create domain replication task is to processed. // e.g. new cluster which does not have anything return h.handleDomainCreationReplicationTask(ctx, task) } h.logger.Error("Domain update failed, error in fetching domain", tag.Error(err)) return err } recordUpdated := false request := &persistence.UpdateDomainRequest{ Info: intendedDomainState.Info, Config: intendedDomainState.Config, ReplicationConfig: intendedDomainState.ReplicationConfig, ConfigVersion: intendedDomainState.ConfigVersion, FailoverVersion: intendedDomainState.FailoverVersion, FailoverNotificationVersion: intendedDomainState.FailoverNotificationVersion, PreviousFailoverVersion: intendedDomainState.PreviousFailoverVersion, NotificationVersion: notificationVersion, LastUpdatedTime: h.timeSource.Now().UnixNano(), } if intendedDomainState.ConfigVersion < task.GetConfigVersion() { recordUpdated = true request.Info = &persistence.DomainInfo{ ID: task.GetID(), Name: task.Info.GetName(), Status: status, Description: task.Info.GetDescription(), OwnerEmail: task.Info.GetOwnerEmail(), Data: task.Info.Data, } request.Config = &persistence.DomainConfig{ Retention: task.Config.GetWorkflowExecutionRetentionPeriodInDays(), EmitMetric: task.Config.GetEmitMetric(), HistoryArchivalStatus: task.Config.GetHistoryArchivalStatus(), HistoryArchivalURI: task.Config.GetHistoryArchivalURI(), VisibilityArchivalStatus: task.Config.GetVisibilityArchivalStatus(), VisibilityArchivalURI: task.Config.GetVisibilityArchivalURI(), IsolationGroups: task.Config.GetIsolationGroupsConfiguration(), AsyncWorkflowConfig: task.Config.GetAsyncWorkflowConfiguration(), } if task.Config.GetBadBinaries() != nil { request.Config.BadBinaries = *task.Config.GetBadBinaries() } request.ReplicationConfig.Clusters = h.convertClusterReplicationConfigFromThrift(task.ReplicationConfig.Clusters) request.ConfigVersion = task.GetConfigVersion() } if originalDomainState.FailoverVersion < task.GetFailoverVersion() { recordUpdated = true request.ReplicationConfig.ActiveClusterName = task.ReplicationConfig.GetActiveClusterName() request.ReplicationConfig.ActiveClusters = task.ReplicationConfig.GetActiveClusters() request.FailoverVersion = task.GetFailoverVersion() request.FailoverNotificationVersion = notificationVersion request.PreviousFailoverVersion = task.GetPreviousFailoverVersion() } else if !originalDomainState.ReplicationConfig.IsActiveActive() { h.logger.Warn("the existing failover version was more recent, indicating that the domain replication message was out of date and is consequently being dropped", tag.WorkflowDomainName(originalDomainState.Info.Name), tag.FailoverVersion(originalDomainState.FailoverVersion), tag.FailoverVersion(task.GetFailoverVersion())) } if intendedDomainState.ReplicationConfig.IsActiveActive() || task.ReplicationConfig.IsActiveActive() { mergedActiveClusters, aaChanged := mergeActiveActiveScopes(intendedDomainState.ReplicationConfig.ActiveClusters, task.ReplicationConfig.ActiveClusters) if aaChanged { recordUpdated = true request.ReplicationConfig.ActiveClusters = mergedActiveClusters } } if !recordUpdated { h.logger.Warn("no record updated while handling domain update replication task", tag.WorkflowDomainName(task.Info.GetName()), tag.WorkflowDomainID(task.GetID())) return nil } err = h.domainManager.UpdateDomain(ctx, request) if err != nil { h.logger.Error("failed to update domain while handling domain update replication task", tag.Error(err), tag.WorkflowDomainName(task.Info.GetName()), tag.WorkflowDomainID(task.GetID())) return err } afterUpdate, getErr := h.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{ ID: task.GetID(), }) if getErr != nil { return fmt.Errorf("failed to get domain while trying to create domain audit log for update: %w", getErr) } // relying on the fact that for both failovers and the failover of cluster-attibutes // within the domain, in both instances the failover version will be incremented, indicating // this is failover type update. if intendedDomainState.FailoverVersion < afterUpdate.FailoverVersion { h.createDomainAuditLog( ctx, task, persistence.DomainAuditOperationTypeFailover, originalDomainState, afterUpdate, ) return nil } h.createDomainAuditLog( ctx, task, persistence.DomainAuditOperationTypeUpdate, originalDomainState, afterUpdate, ) return nil } // handleDomainDeleteReplicationTask handles the domain delete replication task func (h *domainReplicationTaskExecutorImpl) handleDomainDeleteReplicationTask(ctx context.Context, task *types.DomainTaskAttributes) error { request := &persistence.DeleteDomainByNameRequest{ Name: task.Info.GetName(), } // ignoring error since this might be already deleted getDomainResp, _ := h.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{ Name: task.Info.GetName(), }) err := h.domainManager.DeleteDomainByName(ctx, request) if err != nil { _, err := h.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{ Name: task.Info.GetName(), }) var entityNotExistsError *types.EntityNotExistsError if errors.As(err, &entityNotExistsError) { return nil } return err } h.createDomainAuditLog( ctx, task, persistence.DomainAuditOperationTypeDelete, getDomainResp, nil, ) return nil } func (h *domainReplicationTaskExecutorImpl) validateDomainReplicationTask(task *types.DomainTaskAttributes) error { if task == nil { return ErrEmptyDomainReplicationTask } if task.DomainOperation == nil { return ErrInvalidDomainOperation } else if task.ID == "" { return ErrInvalidDomainID } else if task.Info == nil { return ErrInvalidDomainInfo } else if task.Config == nil { return ErrInvalidDomainConfig } else if task.ReplicationConfig == nil { return ErrInvalidDomainReplicationConfig } return nil } func (h *domainReplicationTaskExecutorImpl) convertClusterReplicationConfigFromThrift( input []*types.ClusterReplicationConfiguration) []*persistence.ClusterReplicationConfig { output := []*persistence.ClusterReplicationConfig{} for _, cluster := range input { clusterName := cluster.GetClusterName() output = append(output, &persistence.ClusterReplicationConfig{ClusterName: clusterName}) } return output } func (h *domainReplicationTaskExecutorImpl) convertDomainStatusFromThrift(input *types.DomainStatus) (int, error) { if input == nil { return 0, ErrInvalidDomainStatus } switch *input { case types.DomainStatusRegistered: return persistence.DomainStatusRegistered, nil case types.DomainStatusDeprecated: return persistence.DomainStatusDeprecated, nil default: return 0, ErrInvalidDomainStatus } } func (h *domainReplicationTaskExecutorImpl) createDomainAuditLog( ctx context.Context, task *types.DomainTaskAttributes, operationType persistence.DomainAuditOperationType, stateBefore *persistence.GetDomainResponse, stateAfter *persistence.GetDomainResponse, ) { if !h.enableDomainAuditLogging() { return } eventID, err := guuid.NewV7() if err != nil { h.logger.Error("failed to generate event ID while creating domain audit log", tag.Error(err)) return } creationTime := time.Unix(eventID.Time().UnixTime()) _, err = h.domainAuditManager.CreateDomainAuditLog(ctx, &persistence.CreateDomainAuditLogRequest{ EventID: eventID.String(), DomainID: task.GetID(), OperationType: operationType, CreatedTime: creationTime, StateBefore: stateBefore, StateAfter: stateAfter, Identity: "replication task executor", Comment: fmt.Sprintf("replicated domain operation %s for domain %s", operationType.String(), task.Info.GetName()), }) if err != nil { h.logger.Error("failed to create domain audit log while creating domain audit log", tag.Error(err), tag.WorkflowDomainName(task.Info.GetName()), tag.WorkflowDomainID(task.GetID())) } } ================================================ FILE: common/domain/replicationTaskExecutor_integration_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "context" "log" "os" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" persistencetests "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/sql/sqlplugin/sqlite" "github.com/uber/cadence/common/types" ) type ( domainReplicationTaskExecutorSuite struct { *persistencetests.TestBase domainReplicator *domainReplicationTaskExecutorImpl } ) func TestDomainReplicationTaskExecutorSuite(t *testing.T) { if testing.Verbose() { log.SetOutput(os.Stdout) } s := new(domainReplicationTaskExecutorSuite) s.setupTestBase(t) suite.Run(t, s) } func (s *domainReplicationTaskExecutorSuite) setupTestBase(t *testing.T) { sqliteTestBaseOptions := sqlite.GetTestClusterOption() s.TestBase = persistencetests.NewTestBaseWithSQL(t, sqliteTestBaseOptions) s.Setup() } func (s *domainReplicationTaskExecutorSuite) SetupTest() { s.setupTestBase(s.T()) domainAuditManager, err := s.ExecutionMgrFactory.NewDomainAuditManager() if err != nil { s.T().Fatalf("Failed to create domain audit manager: %v", err) } // Disable audit logging for integration tests as SQLite doesn't fully support the audit manager enableAuditLogging := func(...dynamicproperties.FilterOption) bool { return false } s.domainReplicator = NewReplicationTaskExecutor( s.DomainManager, domainAuditManager, clock.NewRealTimeSource(), s.Logger, enableAuditLogging, ).(*domainReplicationTaskExecutorImpl) } func (s *domainReplicationTaskExecutorSuite) TearDownTest() { s.TearDownWorkflowStore() } func (s *domainReplicationTaskExecutorSuite) TestExecute_RegisterDomainTask_NameUUIDCollision() { operation := types.DomainOperationCreate id := uuid.New() name := "some random domain test name" status := types.DomainStatusRegistered description := "some random test description" ownerEmail := "some random test owner" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "some random history archival uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "some random visibility archival uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(0) failoverVersion := int64(59) clusters := []*types.ClusterReplicationConfiguration{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } task := &types.DomainTaskAttributes{ DomainOperation: &operation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &status, Description: description, OwnerEmail: ownerEmail, Data: data, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterActive, Clusters: clusters, }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, } err := s.domainReplicator.Execute(task) s.Nil(err) task.ID = uuid.New() task.Info.Name = name err = s.domainReplicator.Execute(task) s.NotNil(err) s.IsType(&types.BadRequestError{}, err) task.ID = id task.Info.Name = "other random domain test name" err = s.domainReplicator.Execute(task) s.NotNil(err) s.IsType(&types.BadRequestError{}, err) } func (s *domainReplicationTaskExecutorSuite) TestExecute_RegisterDomainTask() { operation := types.DomainOperationCreate id := uuid.New() name := "some random domain test name" status := types.DomainStatusRegistered description := "some random test description" ownerEmail := "some random test owner" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "some random history archival uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "some random visibility archival uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(0) failoverVersion := int64(59) clusters := []*types.ClusterReplicationConfiguration{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } task := &types.DomainTaskAttributes{ DomainOperation: &operation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &status, Description: description, OwnerEmail: ownerEmail, Data: data, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterActive, Clusters: clusters, }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, } metadata, err := s.DomainManager.GetMetadata(context.Background()) s.Nil(err) notificationVersion := metadata.NotificationVersion err = s.domainReplicator.Execute(task) s.Nil(err) resp, err := s.DomainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{ID: id}) s.Nil(err) s.NotNil(resp) s.Equal(id, resp.Info.ID) s.Equal(name, resp.Info.Name) s.Equal(persistence.DomainStatusRegistered, resp.Info.Status) s.Equal(description, resp.Info.Description) s.Equal(ownerEmail, resp.Info.OwnerEmail) s.Equal(data, resp.Info.Data) s.Equal(retention, resp.Config.Retention) s.Equal(emitMetric, resp.Config.EmitMetric) s.Equal(historyArchivalStatus, resp.Config.HistoryArchivalStatus) s.Equal(historyArchivalURI, resp.Config.HistoryArchivalURI) s.Equal(visibilityArchivalStatus, resp.Config.VisibilityArchivalStatus) s.Equal(visibilityArchivalURI, resp.Config.VisibilityArchivalURI) s.Equal(clusterActive, resp.ReplicationConfig.ActiveClusterName) s.Equal(s.domainReplicator.convertClusterReplicationConfigFromThrift(clusters), resp.ReplicationConfig.Clusters) s.Equal(configVersion, resp.ConfigVersion) s.Equal(failoverVersion, resp.FailoverVersion) s.Equal(int64(0), resp.FailoverNotificationVersion) s.Equal(notificationVersion, resp.NotificationVersion) // handle duplicated task err = s.domainReplicator.Execute(task) s.Nil(err) } func (s *domainReplicationTaskExecutorSuite) TestExecute_UpdateDomainTask_DomainNotExist() { operation := types.DomainOperationUpdate id := uuid.New() name := "some random domain test name" status := types.DomainStatusRegistered description := "some random test description" ownerEmail := "some random test owner" retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "some random history archival uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "some random visibility archival uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(12) failoverVersion := int64(59) domainData := map[string]string{"k1": "v1", "k2": "v2"} clusters := []*types.ClusterReplicationConfiguration{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } updateTask := &types.DomainTaskAttributes{ DomainOperation: &operation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &status, Description: description, OwnerEmail: ownerEmail, Data: domainData, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterActive, Clusters: clusters, }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, } metadata, err := s.DomainManager.GetMetadata(context.Background()) s.Nil(err) notificationVersion := metadata.NotificationVersion err = s.domainReplicator.Execute(updateTask) s.Nil(err) resp, err := s.DomainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{Name: name}) s.Nil(err) s.NotNil(resp) s.Equal(id, resp.Info.ID) s.Equal(name, resp.Info.Name) s.Equal(persistence.DomainStatusRegistered, resp.Info.Status) s.Equal(description, resp.Info.Description) s.Equal(ownerEmail, resp.Info.OwnerEmail) s.Equal(domainData, resp.Info.Data) s.Equal(retention, resp.Config.Retention) s.Equal(emitMetric, resp.Config.EmitMetric) s.Equal(historyArchivalStatus, resp.Config.HistoryArchivalStatus) s.Equal(historyArchivalURI, resp.Config.HistoryArchivalURI) s.Equal(visibilityArchivalStatus, resp.Config.VisibilityArchivalStatus) s.Equal(visibilityArchivalURI, resp.Config.VisibilityArchivalURI) s.Equal(clusterActive, resp.ReplicationConfig.ActiveClusterName) s.Equal(s.domainReplicator.convertClusterReplicationConfigFromThrift(clusters), resp.ReplicationConfig.Clusters) s.Equal(configVersion, resp.ConfigVersion) s.Equal(failoverVersion, resp.FailoverVersion) s.Equal(int64(0), resp.FailoverNotificationVersion) s.Equal(notificationVersion, resp.NotificationVersion) } func (s *domainReplicationTaskExecutorSuite) TestExecute_UpdateDomainTask_UpdateConfig_UpdateActiveCluster() { operation := types.DomainOperationCreate id := uuid.New() name := "some random domain test name" status := types.DomainStatusRegistered description := "some random test description" ownerEmail := "some random test owner" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "some random history archival uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "some random visibility archival uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(0) failoverVersion := int64(59) clusters := []*types.ClusterReplicationConfiguration{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } createTask := &types.DomainTaskAttributes{ DomainOperation: &operation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &status, Description: description, OwnerEmail: ownerEmail, Data: data, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterActive, Clusters: clusters, }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, } err := s.domainReplicator.Execute(createTask) s.Nil(err) // success update case updateOperation := types.DomainOperationUpdate updateStatus := types.DomainStatusDeprecated updateDescription := "other random domain test description" updateOwnerEmail := "other random domain test owner" updatedData := map[string]string{"k": "v1"} updateRetention := int32(122) updateEmitMetric := true updateHistoryArchivalStatus := types.ArchivalStatusDisabled updateHistoryArchivalURI := "some updated history archival uri" updateVisibilityArchivalStatus := types.ArchivalStatusDisabled updateVisibilityArchivalURI := "some updated visibility archival uri" updateClusterActive := "other random active cluster name" updateClusterStandby := "other random standby cluster name" updateConfigVersion := configVersion + 1 updateFailoverVersion := failoverVersion + 1 updateClusters := []*types.ClusterReplicationConfiguration{ { ClusterName: updateClusterActive, }, { ClusterName: updateClusterStandby, }, } updateTask := &types.DomainTaskAttributes{ DomainOperation: &updateOperation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &updateStatus, Description: updateDescription, OwnerEmail: updateOwnerEmail, Data: updatedData, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: updateRetention, EmitMetric: updateEmitMetric, HistoryArchivalStatus: updateHistoryArchivalStatus.Ptr(), HistoryArchivalURI: updateHistoryArchivalURI, VisibilityArchivalStatus: updateVisibilityArchivalStatus.Ptr(), VisibilityArchivalURI: updateVisibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: updateClusterActive, Clusters: updateClusters, }, ConfigVersion: updateConfigVersion, FailoverVersion: updateFailoverVersion, } metadata, err := s.DomainManager.GetMetadata(context.Background()) s.Nil(err) notificationVersion := metadata.NotificationVersion err = s.domainReplicator.Execute(updateTask) s.Nil(err) resp, err := s.DomainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{Name: name}) s.Nil(err) s.NotNil(resp) s.Equal(id, resp.Info.ID) s.Equal(name, resp.Info.Name) s.Equal(persistence.DomainStatusDeprecated, resp.Info.Status) s.Equal(updateDescription, resp.Info.Description) s.Equal(updateOwnerEmail, resp.Info.OwnerEmail) s.Equal(updatedData, resp.Info.Data) s.Equal(updateRetention, resp.Config.Retention) s.Equal(updateEmitMetric, resp.Config.EmitMetric) s.Equal(updateHistoryArchivalStatus, resp.Config.HistoryArchivalStatus) s.Equal(updateHistoryArchivalURI, resp.Config.HistoryArchivalURI) s.Equal(updateVisibilityArchivalStatus, resp.Config.VisibilityArchivalStatus) s.Equal(updateVisibilityArchivalURI, resp.Config.VisibilityArchivalURI) s.Equal(updateClusterActive, resp.ReplicationConfig.ActiveClusterName) s.Equal(s.domainReplicator.convertClusterReplicationConfigFromThrift(updateClusters), resp.ReplicationConfig.Clusters) s.Equal(updateConfigVersion, resp.ConfigVersion) s.Equal(updateFailoverVersion, resp.FailoverVersion) s.Equal(notificationVersion, resp.FailoverNotificationVersion) s.Equal(notificationVersion, resp.NotificationVersion) } func (s *domainReplicationTaskExecutorSuite) TestExecute_UpdateDomainTask_UpdateConfig_NoUpdateActiveCluster() { operation := types.DomainOperationCreate id := uuid.New() name := "some random domain test name" status := types.DomainStatusRegistered description := "some random test description" ownerEmail := "some random test owner" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusDisabled historyArchivalURI := "" visibilityArchivalStatus := types.ArchivalStatusDisabled visibilityArchivalURI := "" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(0) failoverVersion := int64(59) previousFailoverVersion := int64(55) clusters := []*types.ClusterReplicationConfiguration{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } createTask := &types.DomainTaskAttributes{ DomainOperation: &operation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &status, Description: description, OwnerEmail: ownerEmail, Data: data, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterActive, Clusters: clusters, }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, } err := s.domainReplicator.Execute(createTask) s.Nil(err) // success update case updateOperation := types.DomainOperationUpdate updateStatus := types.DomainStatusDeprecated updateDescription := "other random domain test description" updateOwnerEmail := "other random domain test owner" updateData := map[string]string{"k": "v2"} updateRetention := int32(122) updateEmitMetric := true updateHistoryArchivalStatus := types.ArchivalStatusEnabled updateHistoryArchivalURI := "some updated history archival uri" updateVisibilityArchivalStatus := types.ArchivalStatusEnabled updateVisibilityArchivalURI := "some updated visibility archival uri" updateClusterActive := "other random active cluster name" updateClusterStandby := "other random standby cluster name" updateConfigVersion := configVersion + 1 updateFailoverVersion := failoverVersion - 1 updateClusters := []*types.ClusterReplicationConfiguration{ { ClusterName: updateClusterActive, }, { ClusterName: updateClusterStandby, }, } updateTask := &types.DomainTaskAttributes{ DomainOperation: &updateOperation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &updateStatus, Description: updateDescription, OwnerEmail: updateOwnerEmail, Data: updateData, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: updateRetention, EmitMetric: updateEmitMetric, HistoryArchivalStatus: updateHistoryArchivalStatus.Ptr(), HistoryArchivalURI: updateHistoryArchivalURI, VisibilityArchivalStatus: updateVisibilityArchivalStatus.Ptr(), VisibilityArchivalURI: updateVisibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: updateClusterActive, Clusters: updateClusters, }, ConfigVersion: updateConfigVersion, FailoverVersion: updateFailoverVersion, PreviousFailoverVersion: previousFailoverVersion, } metadata, err := s.DomainManager.GetMetadata(context.Background()) s.Nil(err) notificationVersion := metadata.NotificationVersion err = s.domainReplicator.Execute(updateTask) s.Nil(err) resp, err := s.DomainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{Name: name}) s.Nil(err) s.NotNil(resp) s.Equal(id, resp.Info.ID) s.Equal(name, resp.Info.Name) s.Equal(persistence.DomainStatusDeprecated, resp.Info.Status) s.Equal(updateDescription, resp.Info.Description) s.Equal(updateOwnerEmail, resp.Info.OwnerEmail) s.Equal(updateData, resp.Info.Data) s.Equal(updateRetention, resp.Config.Retention) s.Equal(updateEmitMetric, resp.Config.EmitMetric) s.Equal(updateHistoryArchivalStatus, resp.Config.HistoryArchivalStatus) s.Equal(updateHistoryArchivalURI, resp.Config.HistoryArchivalURI) s.Equal(updateVisibilityArchivalStatus, resp.Config.VisibilityArchivalStatus) s.Equal(updateVisibilityArchivalURI, resp.Config.VisibilityArchivalURI) s.Equal(clusterActive, resp.ReplicationConfig.ActiveClusterName) s.Equal(s.domainReplicator.convertClusterReplicationConfigFromThrift(updateClusters), resp.ReplicationConfig.Clusters) s.Equal(updateConfigVersion, resp.ConfigVersion) s.Equal(failoverVersion, resp.FailoverVersion) s.Equal(constants.InitialPreviousFailoverVersion, resp.PreviousFailoverVersion) s.Equal(int64(0), resp.FailoverNotificationVersion) s.Equal(notificationVersion, resp.NotificationVersion) } func (s *domainReplicationTaskExecutorSuite) TestExecute_UpdateDomainTask_NoUpdateConfig_UpdateActiveCluster() { operation := types.DomainOperationCreate id := uuid.New() name := "some random domain test name" status := types.DomainStatusRegistered description := "some random test description" ownerEmail := "some random test owner" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "some random history archival uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "some random visibility archival uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(0) failoverVersion := int64(59) previousFailoverVersion := int64(55) clusters := []*types.ClusterReplicationConfiguration{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } createTask := &types.DomainTaskAttributes{ DomainOperation: &operation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &status, Description: description, OwnerEmail: ownerEmail, Data: data, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterActive, Clusters: clusters, }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, PreviousFailoverVersion: previousFailoverVersion, } err := s.domainReplicator.Execute(createTask) s.Nil(err) resp1, err := s.DomainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{Name: name}) s.Nil(err) s.NotNil(resp1) s.Equal(id, resp1.Info.ID) s.Equal(name, resp1.Info.Name) s.Equal(persistence.DomainStatusRegistered, resp1.Info.Status) s.Equal(description, resp1.Info.Description) s.Equal(ownerEmail, resp1.Info.OwnerEmail) s.Equal(data, resp1.Info.Data) s.Equal(retention, resp1.Config.Retention) s.Equal(emitMetric, resp1.Config.EmitMetric) s.Equal(historyArchivalStatus, resp1.Config.HistoryArchivalStatus) s.Equal(historyArchivalURI, resp1.Config.HistoryArchivalURI) s.Equal(visibilityArchivalStatus, resp1.Config.VisibilityArchivalStatus) s.Equal(visibilityArchivalURI, resp1.Config.VisibilityArchivalURI) s.Equal(clusterActive, resp1.ReplicationConfig.ActiveClusterName) s.Equal(s.domainReplicator.convertClusterReplicationConfigFromThrift(clusters), resp1.ReplicationConfig.Clusters) s.Equal(configVersion, resp1.ConfigVersion) s.Equal(failoverVersion, resp1.FailoverVersion) s.Equal(constants.InitialPreviousFailoverVersion, resp1.PreviousFailoverVersion) // success update case updateOperation := types.DomainOperationUpdate updateStatus := types.DomainStatusDeprecated updateDescription := "other random domain test description" updateOwnerEmail := "other random domain test owner" updatedData := map[string]string{"k": "v2"} updateRetention := int32(122) updateEmitMetric := true updateClusterActive := "other random active cluster name" updateClusterStandby := "other random standby cluster name" updateConfigVersion := configVersion - 1 updateFailoverVersion := failoverVersion + 1 updateClusters := []*types.ClusterReplicationConfiguration{ { ClusterName: updateClusterActive, }, { ClusterName: updateClusterStandby, }, } updateTask := &types.DomainTaskAttributes{ DomainOperation: &updateOperation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &updateStatus, Description: updateDescription, OwnerEmail: updateOwnerEmail, Data: updatedData, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: updateRetention, EmitMetric: updateEmitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: updateClusterActive, Clusters: updateClusters, }, ConfigVersion: updateConfigVersion, FailoverVersion: updateFailoverVersion, PreviousFailoverVersion: failoverVersion, } metadata, err := s.DomainManager.GetMetadata(context.Background()) s.Nil(err) notificationVersion := metadata.NotificationVersion err = s.domainReplicator.Execute(updateTask) s.Nil(err) resp, err := s.DomainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{Name: name}) s.Nil(err) s.NotNil(resp) s.Equal(id, resp.Info.ID) s.Equal(name, resp.Info.Name) s.Equal(persistence.DomainStatusRegistered, resp.Info.Status) s.Equal(description, resp.Info.Description) s.Equal(ownerEmail, resp.Info.OwnerEmail) s.Equal(data, resp.Info.Data) s.Equal(retention, resp.Config.Retention) s.Equal(emitMetric, resp.Config.EmitMetric) s.Equal(historyArchivalStatus, resp.Config.HistoryArchivalStatus) s.Equal(historyArchivalURI, resp.Config.HistoryArchivalURI) s.Equal(visibilityArchivalStatus, resp.Config.VisibilityArchivalStatus) s.Equal(visibilityArchivalURI, resp.Config.VisibilityArchivalURI) s.Equal(updateClusterActive, resp.ReplicationConfig.ActiveClusterName) s.Equal(s.domainReplicator.convertClusterReplicationConfigFromThrift(clusters), resp.ReplicationConfig.Clusters) s.Equal(configVersion, resp.ConfigVersion) s.Equal(updateFailoverVersion, resp.FailoverVersion) s.Equal(notificationVersion, resp.FailoverNotificationVersion) s.Equal(notificationVersion, resp.NotificationVersion) s.Equal(failoverVersion, resp.PreviousFailoverVersion) } func (s *domainReplicationTaskExecutorSuite) TestExecute_UpdateDomainTask_NoUpdateConfig_NoUpdateActiveCluster() { operation := types.DomainOperationCreate id := uuid.New() name := "some random domain test name" status := types.DomainStatusRegistered description := "some random test description" ownerEmail := "some random test owner" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "some random history archival uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "some random visibility archival uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(0) failoverVersion := int64(59) previousFailoverVersion := int64(55) clusters := []*types.ClusterReplicationConfiguration{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } createTask := &types.DomainTaskAttributes{ DomainOperation: &operation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &status, Description: description, OwnerEmail: ownerEmail, Data: data, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterActive, Clusters: clusters, }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, } metadata, err := s.DomainManager.GetMetadata(context.Background()) s.Nil(err) notificationVersion := metadata.NotificationVersion err = s.domainReplicator.Execute(createTask) s.Nil(err) // success update case updateOperation := types.DomainOperationUpdate updateStatus := types.DomainStatusDeprecated updateDescription := "other random domain test description" updateOwnerEmail := "other random domain test owner" updatedData := map[string]string{"k": "v2"} updateRetention := int32(122) updateEmitMetric := true updateClusterActive := "other random active cluster name" updateClusterStandby := "other random standby cluster name" updateConfigVersion := configVersion - 1 updateFailoverVersion := failoverVersion - 1 updateClusters := []*types.ClusterReplicationConfiguration{ { ClusterName: updateClusterActive, }, { ClusterName: updateClusterStandby, }, } updateTask := &types.DomainTaskAttributes{ DomainOperation: &updateOperation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &updateStatus, Description: updateDescription, OwnerEmail: updateOwnerEmail, Data: updatedData, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: updateRetention, EmitMetric: updateEmitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: updateClusterActive, Clusters: updateClusters, }, ConfigVersion: updateConfigVersion, FailoverVersion: updateFailoverVersion, PreviousFailoverVersion: previousFailoverVersion, } err = s.domainReplicator.Execute(updateTask) s.Nil(err) resp, err := s.DomainManager.GetDomain(context.Background(), &persistence.GetDomainRequest{Name: name}) s.Nil(err) s.NotNil(resp) s.Equal(id, resp.Info.ID) s.Equal(name, resp.Info.Name) s.Equal(persistence.DomainStatusRegistered, resp.Info.Status) s.Equal(description, resp.Info.Description) s.Equal(ownerEmail, resp.Info.OwnerEmail) s.Equal(data, resp.Info.Data) s.Equal(retention, resp.Config.Retention) s.Equal(emitMetric, resp.Config.EmitMetric) s.Equal(historyArchivalStatus, resp.Config.HistoryArchivalStatus) s.Equal(historyArchivalURI, resp.Config.HistoryArchivalURI) s.Equal(visibilityArchivalStatus, resp.Config.VisibilityArchivalStatus) s.Equal(visibilityArchivalURI, resp.Config.VisibilityArchivalURI) s.Equal(clusterActive, resp.ReplicationConfig.ActiveClusterName) s.Equal(s.domainReplicator.convertClusterReplicationConfigFromThrift(clusters), resp.ReplicationConfig.Clusters) s.Equal(configVersion, resp.ConfigVersion) s.Equal(failoverVersion, resp.FailoverVersion) s.Equal(constants.InitialPreviousFailoverVersion, resp.PreviousFailoverVersion) s.Equal(int64(0), resp.FailoverNotificationVersion) s.Equal(notificationVersion, resp.NotificationVersion) } ================================================ FILE: common/domain/replicationTaskExecutor_test.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "context" "errors" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestDomainReplicationTaskExecutor_Execute(t *testing.T) { tests := []struct { name string setupMock func(mockDomainManager persistence.MockDomainManager, mockAuditManager persistence.MockDomainAuditManager) task *types.DomainTaskAttributes wantErr bool errType interface{} }{ { name: "Validate Domain Task - Empty Task", setupMock: func(mockDomainManager persistence.MockDomainManager, mockAuditManager persistence.MockDomainAuditManager) { // No setup required as the task itself is nil, triggering the validation error }, task: nil, wantErr: true, errType: &types.BadRequestError{}, }, { name: "Handle Create Domain Task - Valid", setupMock: func(mockDomainManager persistence.MockDomainManager, mockAuditManager persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(&persistence.CreateDomainResponse{ID: "validDomainID"}, nil). Times(1) // Expect GetDomain for audit log mockDomainManager.EXPECT(). GetDomain(gomock.Any(), &persistence.GetDomainRequest{ID: "validDomainID"}). Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ ID: "validDomainID", Name: "validDomain", }, }, nil). Times(1) // Expect audit log creation mockAuditManager.EXPECT(). CreateDomainAuditLog(gomock.Any(), gomock.Any()). Return(&persistence.CreateDomainAuditLogResponse{}, nil). Times(1) }, task: &types.DomainTaskAttributes{ DomainOperation: types.DomainOperationCreate.Ptr(), ID: "validDomainID", Info: &types.DomainInfo{ Name: "validDomain", Status: types.DomainStatusRegistered.Ptr(), Description: "A valid domain", OwnerEmail: "owner@example.com", Data: map[string]string{"k1": "v1"}, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 7, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled.Ptr(), HistoryArchivalURI: "test://history", VisibilityArchivalStatus: types.ArchivalStatusEnabled.Ptr(), VisibilityArchivalURI: "test://visibility", }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: "activeClusterName", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "activeClusterName"}, {ClusterName: "standbyClusterName"}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: "activeClusterName", }, "region2": { ActiveClusterName: "standbyClusterName", }, }, }, }, }, }, ConfigVersion: 1, FailoverVersion: 1, }, wantErr: false, }, { name: "Handle Create Domain Task - Name UUID Collision", setupMock: func(mockDomainManager persistence.MockDomainManager, mockAuditManager persistence.MockDomainAuditManager) { // call to GetDomain simulates a name collision by returning a different domain ID mockDomainManager.EXPECT(). GetDomain(gomock.Any(), &persistence.GetDomainRequest{Name: "collisionDomain"}). Return(&persistence.GetDomainResponse{Info: &persistence.DomainInfo{ID: uuid.New()}}, nil). Times(1) // Expect CreateDomain to be called, which should result in a collision error mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(nil, ErrNameUUIDCollision). Times(1) }, task: &types.DomainTaskAttributes{ DomainOperation: types.DomainOperationCreate.Ptr(), ID: uuid.New(), Info: &types.DomainInfo{ Name: "collisionDomain", Status: types.DomainStatusRegistered.Ptr(), Description: "A domain with UUID collision", OwnerEmail: "owner@example.com", Data: map[string]string{"k1": "v1"}, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 7, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled.Ptr(), HistoryArchivalURI: "test://history", VisibilityArchivalStatus: types.ArchivalStatusEnabled.Ptr(), VisibilityArchivalURI: "test://visibility", }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: "activeClusterName", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "activeClusterName"}, {ClusterName: "standbyClusterName"}, }, }, ConfigVersion: 1, FailoverVersion: 1, }, wantErr: true, errType: &types.BadRequestError{}, }, { name: "Handle Update Domain Task - Valid Update", setupMock: func(mockDomainManager persistence.MockDomainManager, mockAuditManager persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). GetMetadata(gomock.Any()). Return(&persistence.GetMetadataResponse{NotificationVersion: 123}, nil). Times(1) // Mock GetDomain to simulate domain fetch before update (using ID) mockDomainManager.EXPECT(). GetDomain(gomock.Any(), &persistence.GetDomainRequest{ID: "existingDomainID"}). Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: "existingDomainID", Name: "existingDomainName"}, Config: &persistence.DomainConfig{}, ReplicationConfig: &persistence.DomainReplicationConfig{}, ConfigVersion: 1, FailoverVersion: 50, }, nil).Times(1) // Mock UpdateDomain to simulate a successful domain update mockDomainManager.EXPECT(). UpdateDomain(gomock.Any(), gomock.Any()). Return(nil).Times(1) // Mock GetDomain after update for audit log mockDomainManager.EXPECT(). GetDomain(gomock.Any(), &persistence.GetDomainRequest{ID: "existingDomainID"}). Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: "existingDomainID", Name: "existingDomainName"}, Config: &persistence.DomainConfig{}, ReplicationConfig: &persistence.DomainReplicationConfig{}, ConfigVersion: 2, FailoverVersion: 100, }, nil).Times(1) // Expect audit log creation mockAuditManager.EXPECT(). CreateDomainAuditLog(gomock.Any(), gomock.Any()). Return(&persistence.CreateDomainAuditLogResponse{}, nil). Times(1) }, task: &types.DomainTaskAttributes{ DomainOperation: types.DomainOperationUpdate.Ptr(), ID: "existingDomainID", Info: &types.DomainInfo{ Name: "existingDomainName", Status: types.DomainStatusRegistered.Ptr(), Description: "Updated description", OwnerEmail: "updatedOwner@example.com", Data: map[string]string{"updatedKey": "updatedValue"}, }, Config: &types.DomainConfiguration{}, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: "activeClusterName", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "activeClusterName"}, {ClusterName: "standbyClusterName"}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: "activeClusterName", }, }, }, }, }, }, ConfigVersion: 2, FailoverVersion: 100, }, wantErr: false, }, { name: "Handle Invalid Domain Operation", setupMock: func(mockDomainManager persistence.MockDomainManager, mockAuditManager persistence.MockDomainAuditManager) { // No mock setup is required as the operation should not proceed to any database calls }, task: &types.DomainTaskAttributes{ // Set up a task without a valid DomainOperation or with an unrecognized operation DomainOperation: nil, // Assuming this would not match any specific case ID: "invalidOperationDomainID", Info: &types.DomainInfo{ Name: "invalidOperationDomain", Status: types.DomainStatusRegistered.Ptr(), Description: "Domain with invalid operation", OwnerEmail: "owner@example.com", Data: map[string]string{"k1": "v1"}, }, Config: &types.DomainConfiguration{}, ReplicationConfig: &types.DomainReplicationConfiguration{}, }, wantErr: true, errType: &types.BadRequestError{}, }, { name: "Handle Unsupported Domain Operation", setupMock: func(mockDomainManager persistence.MockDomainManager, mockAuditManager persistence.MockDomainAuditManager) { // No mock setup is needed as the operation should immediately return an error }, task: &types.DomainTaskAttributes{ DomainOperation: types.DomainOperation(999).Ptr(), // Assuming 999 is not a valid operation ID: "someDomainID", Info: &types.DomainInfo{ Name: "someDomain", Status: types.DomainStatusRegistered.Ptr(), Description: "A domain with an unsupported operation", OwnerEmail: "email@example.com", Data: map[string]string{"key": "value"}, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 7, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled.Ptr(), HistoryArchivalURI: "uri://history", VisibilityArchivalStatus: types.ArchivalStatusEnabled.Ptr(), VisibilityArchivalURI: "uri://visibility", }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: "activeCluster", Clusters: []*types.ClusterReplicationConfiguration{{ClusterName: "activeCluster"}}, }, }, wantErr: true, errType: &types.BadRequestError{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDomainManager := persistence.NewMockDomainManager(ctrl) mockDomainAuditManager := persistence.NewMockDomainAuditManager(ctrl) mockTimeSource := clock.NewRealTimeSource() mockLogger := log.NewNoop() enableAuditLogging := func(...dynamicproperties.FilterOption) bool { return true } executor := NewReplicationTaskExecutor(mockDomainManager, mockDomainAuditManager, mockTimeSource, mockLogger, enableAuditLogging).(*domainReplicationTaskExecutorImpl) tt.setupMock(*mockDomainManager, *mockDomainAuditManager) err := executor.Execute(tt.task) if tt.wantErr { require.Error(t, err) assert.IsType(t, tt.errType, err) } else { assert.NoError(t, err) } }) } } func domainCreationTask() *types.DomainTaskAttributes { return &types.DomainTaskAttributes{ DomainOperation: types.DomainOperationCreate.Ptr(), ID: "testDomainID", Info: &types.DomainInfo{ Name: "testDomain", Status: types.DomainStatusRegistered.Ptr(), Description: "This is a test domain", OwnerEmail: "owner@test.com", Data: map[string]string{"key1": "value1"}, // Arbitrary domain metadata }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 10, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled.Ptr(), HistoryArchivalURI: "test://history/archival", VisibilityArchivalStatus: types.ArchivalStatusEnabled.Ptr(), VisibilityArchivalURI: "test://visibility/archival", }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: "activeClusterName", Clusters: []*types.ClusterReplicationConfiguration{ { ClusterName: "activeClusterName", }, { ClusterName: "standbyClusterName", }, }, }, ConfigVersion: 1, FailoverVersion: 1, PreviousFailoverVersion: 0, } } func TestHandleDomainCreationReplicationTask(t *testing.T) { tests := []struct { name string task *types.DomainTaskAttributes setup func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) wantError bool }{ { name: "Successful Domain Creation", task: domainCreationTask(), setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(&persistence.CreateDomainResponse{ID: "testDomainID"}, nil) // Expect GetDomain for audit log mockDomainManager.EXPECT(). GetDomain(gomock.Any(), &persistence.GetDomainRequest{ID: "testDomainID"}). Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ ID: "testDomainID", Name: "testDomain", }, }, nil) // Expect audit log creation mockAuditManager.EXPECT(). CreateDomainAuditLog(gomock.Any(), gomock.Any()). Return(&persistence.CreateDomainAuditLogResponse{}, nil) }, wantError: false, }, { name: "Generic Error During Domain Creation", task: domainCreationTask(), setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(nil, types.InternalServiceError{Message: "an internal error"}). Times(1) // Since CreateDomain failed, handleDomainCreationReplicationTask check for domain existence by name and ID mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(nil, &types.EntityNotExistsError{}). // Simulate that no domain exists with the given name/ID AnyTimes() }, wantError: true, }, { name: "Handle Name/UUID Collision - EntityNotExistsError", task: domainCreationTask(), setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(nil, ErrNameUUIDCollision).Times(1) mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()).Return(nil, &types.EntityNotExistsError{}).AnyTimes() }, wantError: true, }, { name: "Immediate Error Return from CreateDomain", setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(nil, types.InternalServiceError{Message: "internal error"}). Times(1) mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(nil, ErrInvalidDomainStatus). AnyTimes() }, task: domainCreationTask(), wantError: true, }, { name: "Domain Creation with Nil Status", task: &types.DomainTaskAttributes{ DomainOperation: types.DomainOperationCreate.Ptr(), ID: "testDomainID", Info: &types.DomainInfo{ Name: "testDomain", // Status is intentionally left as nil to trigger the error }, }, setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { // No need to set up a mock for CreateDomain as the call should not reach this point }, wantError: true, }, { name: "Domain Creation with Unrecognized Status", task: &types.DomainTaskAttributes{ DomainOperation: types.DomainOperationCreate.Ptr(), ID: "testDomainID", Info: &types.DomainInfo{ Name: "testDomain", Status: types.DomainStatus(999).Ptr(), // Assuming 999 is an unrecognized status }, }, setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { // As before, no need for mock setup for CreateDomain }, wantError: true, }, { name: "Unexpected Error Type from GetDomain Leads to Default Error Handling", task: domainCreationTask(), setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(nil, ErrInvalidDomainStatus).Times(1) mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(nil, errors.New("unexpected error")).Times(1) }, wantError: true, }, { name: "Successful GetDomain with Name/UUID Mismatch", task: domainCreationTask(), setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(nil, ErrNameUUIDCollision).AnyTimes() mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: "testDomainID", Name: "mismatchName"}, }, nil).AnyTimes() }, wantError: true, }, { name: "Handle Domain Creation with Unhandled Error", task: domainCreationTask(), setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(nil, &types.EntityNotExistsError{}). AnyTimes() mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(nil, errors.New("unhandled error")). Times(1) }, wantError: true, }, { name: "Handle Domain Creation - Unexpected Error from GetDomain", task: domainCreationTask(), setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(nil, errors.New("test error")).Times(1) mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(nil, &types.EntityNotExistsError{}).Times(1) mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(nil, errors.New("unexpected error")).Times(1) }, wantError: true, }, { name: "Duplicate Domain Creation With Same ID and Name", task: domainCreationTask(), setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(nil, ErrNameUUIDCollision).Times(1) // Setup GetDomain to return matching ID and Name, indicating no actual conflict // This setup ensures that recordExists becomes true mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: "testDomainID", Name: "testDomain"}, }, nil).Times(2) }, wantError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDomainManager := persistence.NewMockDomainManager(ctrl) mockDomainAuditManager := persistence.NewMockDomainAuditManager(ctrl) mockLogger := testlogger.New(t) mockTimeSource := clock.NewMockedTimeSourceAt(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)) // Fixed time enableAuditLogging := func(...dynamicproperties.FilterOption) bool { return true } executor := &domainReplicationTaskExecutorImpl{ domainManager: mockDomainManager, domainAuditManager: mockDomainAuditManager, logger: mockLogger, timeSource: mockTimeSource, enableDomainAuditLogging: enableAuditLogging, } tt.setup(mockDomainManager, mockDomainAuditManager) err := executor.handleDomainCreationReplicationTask(context.Background(), tt.task) if tt.wantError { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestHandleDomainUpdateReplicationTask(t *testing.T) { tests := []struct { name string task *types.DomainTaskAttributes wantErr bool setup func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) }{ { name: "Convert Status Error", task: &types.DomainTaskAttributes{ Info: &types.DomainInfo{ Status: func() *types.DomainStatus { var ds types.DomainStatus = 999; return &ds }(), // Invalid status to trigger conversion error }, }, wantErr: true, setup: func(dm *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) {}, }, { name: "Error Fetching Metadata", task: domainCreationTask(), wantErr: true, setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). GetMetadata(gomock.Any()). Return(nil, errors.New("Error getting metadata while handling replication task")). AnyTimes() }, }, { name: "GetDomain Returns General Error", task: &types.DomainTaskAttributes{ Info: &types.DomainInfo{ Name: "someDomain", }, }, wantErr: true, setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(nil, errors.New("general error")).AnyTimes() mockDomainManager.EXPECT(). GetMetadata(gomock.Any()). Return(&persistence.GetMetadataResponse{}, nil).AnyTimes() }, }, { name: "GetDomain Returns EntityNotExistsError - Triggers Domain Creation", task: &types.DomainTaskAttributes{ ID: "nonexistentDomainID", Info: &types.DomainInfo{ Name: "nonexistentDomain", }, }, wantErr: true, setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). GetDomain(gomock.Any(), &persistence.GetDomainRequest{ ID: "nonexistentDomainID", }). Return(nil, &types.EntityNotExistsError{}).AnyTimes() mockDomainManager.EXPECT(). GetMetadata(gomock.Any()). Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil). AnyTimes() mockDomainManager.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()). Return(&persistence.CreateDomainResponse{ ID: "nonexistentDomainID", }, nil). AnyTimes() }, }, { name: "Record Not Updated then return nil", task: &types.DomainTaskAttributes{ Info: &types.DomainInfo{ Name: "testDomain", Status: types.DomainStatusRegistered.Ptr(), }, Config: &types.DomainConfiguration{ BadBinaries: &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "checksum1": { Reason: "reasontest", Operator: "operatortest", CreatedTimeNano: func() *int64 { var ct int64 = 12345; return &ct }(), }, }, }, }, }, wantErr: false, setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). GetMetadata(gomock.Any()). Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil).AnyTimes() mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ID: "testDomainID", Name: "testDomain"}, Config: &persistence.DomainConfig{}, ReplicationConfig: &persistence.DomainReplicationConfig{}, }, nil). AnyTimes() mockDomainManager.EXPECT(). UpdateDomain(gomock.Any(), gomock.Any()). Return(nil). AnyTimes() }, }, { name: "Update Domain with BadBinaries Set", task: &types.DomainTaskAttributes{ Info: &types.DomainInfo{ Name: "existingDomainName", }, Config: &types.DomainConfiguration{ BadBinaries: &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }, }, }, wantErr: true, setup: func(mockDomainManager *persistence.MockDomainManager, mockAuditManager *persistence.MockDomainAuditManager) { mockDomainManager.EXPECT(). GetMetadata(gomock.Any()). Return(&persistence.GetMetadataResponse{NotificationVersion: 1}, nil). AnyTimes() mockDomainManager.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(&persistence.GetDomainResponse{}, nil). AnyTimes() mockDomainManager.EXPECT(). UpdateDomain(gomock.Any(), gomock.Any()). Return(nil). AnyTimes() }, }, } assert := assert.New(t) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockDomainManager := persistence.NewMockDomainManager(mockCtrl) mockDomainAuditManager := persistence.NewMockDomainAuditManager(mockCtrl) mockLogger := testlogger.New(t) mockTimeSource := clock.NewMockedTimeSourceAt(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)) // Fixed time enableAuditLogging := func(...dynamicproperties.FilterOption) bool { return true } executor := &domainReplicationTaskExecutorImpl{ domainManager: mockDomainManager, domainAuditManager: mockDomainAuditManager, logger: mockLogger, timeSource: mockTimeSource, enableDomainAuditLogging: enableAuditLogging, } tt.setup(mockDomainManager, mockDomainAuditManager) err := executor.handleDomainUpdateReplicationTask(context.Background(), tt.task) if tt.wantErr { assert.Error(err, "Expected an error for test case: %s", tt.name) } else { assert.NoError(err, "Expected no error for test case: %s", tt.name) } }) } } ================================================ FILE: common/domain/replicationTaskHandler_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: replicationTaskExecutor.go // // Generated by this command: // // mockgen -package domain -source replicationTaskExecutor.go -destination replicationTaskHandler_mock.go // // Package domain is a generated GoMock package. package domain import ( reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockReplicationTaskExecutor is a mock of ReplicationTaskExecutor interface. type MockReplicationTaskExecutor struct { ctrl *gomock.Controller recorder *MockReplicationTaskExecutorMockRecorder isgomock struct{} } // MockReplicationTaskExecutorMockRecorder is the mock recorder for MockReplicationTaskExecutor. type MockReplicationTaskExecutorMockRecorder struct { mock *MockReplicationTaskExecutor } // NewMockReplicationTaskExecutor creates a new mock instance. func NewMockReplicationTaskExecutor(ctrl *gomock.Controller) *MockReplicationTaskExecutor { mock := &MockReplicationTaskExecutor{ctrl: ctrl} mock.recorder = &MockReplicationTaskExecutorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockReplicationTaskExecutor) EXPECT() *MockReplicationTaskExecutorMockRecorder { return m.recorder } // Execute mocks base method. func (m *MockReplicationTaskExecutor) Execute(task *types.DomainTaskAttributes) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Execute", task) ret0, _ := ret[0].(error) return ret0 } // Execute indicates an expected call of Execute. func (mr *MockReplicationTaskExecutorMockRecorder) Execute(task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockReplicationTaskExecutor)(nil).Execute), task) } ================================================ FILE: common/domain/replication_queue.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination replication_queue_mock.go -self_package github.com/uber/common/persistence package domain import ( "context" "errors" "fmt" "math" "sync/atomic" "time" "github.com/uber/cadence/.gen/go/replicator" "github.com/uber/cadence/common" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) const ( purgeInterval = 5 * time.Minute queueSizeQueryInterval = 5 * time.Minute localDomainReplicationCluster = "domainReplication" ) var _ ReplicationQueue = (*replicationQueueImpl)(nil) // NewReplicationQueue creates a new ReplicationQueue instance func NewReplicationQueue( queue persistence.QueueManager, clusterName string, metricsClient metrics.Client, logger log.Logger, ) ReplicationQueue { return &replicationQueueImpl{ queue: queue, clusterName: clusterName, metricsClient: metricsClient, logger: logger, encoder: codec.NewThriftRWEncoder(), done: make(chan bool), status: common.DaemonStatusInitialized, } } type ( replicationQueueImpl struct { queue persistence.QueueManager clusterName string metricsClient metrics.Client logger log.Logger encoder codec.BinaryEncoder done chan bool status int32 } // ReplicationQueue is used to publish and list domain replication tasks ReplicationQueue interface { common.Daemon Publish(ctx context.Context, message interface{}) error PublishToDLQ(ctx context.Context, message interface{}) error GetReplicationMessages(ctx context.Context, lastMessageID int64, maxCount int) ([]*types.ReplicationTask, int64, error) UpdateAckLevel(ctx context.Context, lastProcessedMessageID int64, clusterName string) error GetAckLevels(ctx context.Context) (map[string]int64, error) GetMessagesFromDLQ(ctx context.Context, firstMessageID int64, lastMessageID int64, pageSize int, pageToken []byte) ([]*types.ReplicationTask, []byte, error) UpdateDLQAckLevel(ctx context.Context, lastProcessedMessageID int64) error GetDLQAckLevel(ctx context.Context) (int64, error) RangeDeleteMessagesFromDLQ(ctx context.Context, firstMessageID int64, lastMessageID int64) error DeleteMessageFromDLQ(ctx context.Context, messageID int64) error GetDLQSize(ctx context.Context) (int64, error) } ) func (q *replicationQueueImpl) Start() { if !atomic.CompareAndSwapInt32(&q.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } go q.purgeProcessor() } func (q *replicationQueueImpl) Stop() { if !atomic.CompareAndSwapInt32(&q.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(q.done) } func (q *replicationQueueImpl) Publish( ctx context.Context, message interface{}, ) error { task, ok := message.(*types.ReplicationTask) if !ok { return errors.New("wrong message type") } bytes, err := q.encoder.Encode(thrift.FromReplicationTask(task)) if err != nil { return fmt.Errorf("failed to encode message: %v", err) } return q.queue.EnqueueMessage(ctx, &persistence.EnqueueMessageRequest{ MessagePayload: bytes, }) } func (q *replicationQueueImpl) PublishToDLQ( ctx context.Context, message interface{}, ) error { task, ok := message.(*types.ReplicationTask) if !ok { return errors.New("wrong message type") } bytes, err := q.encoder.Encode(thrift.FromReplicationTask(task)) if err != nil { return fmt.Errorf("failed to encode message: %v", err) } return q.queue.EnqueueMessageToDLQ(ctx, &persistence.EnqueueMessageToDLQRequest{ MessagePayload: bytes, }) } func (q *replicationQueueImpl) GetReplicationMessages( ctx context.Context, lastMessageID int64, maxCount int, ) ([]*types.ReplicationTask, int64, error) { resp, err := q.queue.ReadMessages(ctx, &persistence.ReadMessagesRequest{ LastMessageID: lastMessageID, MaxCount: maxCount, }) if err != nil { return nil, lastMessageID, err } var replicationTasks []*types.ReplicationTask for _, message := range resp.Messages { var replicationTask replicator.ReplicationTask err := q.encoder.Decode(message.Payload, &replicationTask) if err != nil { return nil, lastMessageID, fmt.Errorf("failed to decode task: %v", err) } lastMessageID = message.ID replicationTasks = append(replicationTasks, thrift.ToReplicationTask(&replicationTask)) } q.logger.Debugf("Returning %d domain replication tasks. lastMessageID: %d", len(replicationTasks), lastMessageID) return replicationTasks, lastMessageID, nil } func (q *replicationQueueImpl) UpdateAckLevel( ctx context.Context, lastProcessedMessageID int64, clusterName string, ) error { if err := q.queue.UpdateAckLevel(ctx, &persistence.UpdateAckLevelRequest{ MessageID: lastProcessedMessageID, ClusterName: clusterName, }); err != nil { return fmt.Errorf("failed to update ack level: %v", err) } return nil } func (q *replicationQueueImpl) GetAckLevels( ctx context.Context, ) (map[string]int64, error) { resp, err := q.queue.GetAckLevels(ctx, &persistence.GetAckLevelsRequest{}) if err != nil { return nil, err } return resp.AckLevels, nil } func (q *replicationQueueImpl) GetMessagesFromDLQ( ctx context.Context, firstMessageID int64, lastMessageID int64, pageSize int, pageToken []byte, ) ([]*types.ReplicationTask, []byte, error) { resp, err := q.queue.ReadMessagesFromDLQ(ctx, &persistence.ReadMessagesFromDLQRequest{ FirstMessageID: firstMessageID, LastMessageID: lastMessageID, PageSize: pageSize, PageToken: pageToken, }) if err != nil { return nil, nil, err } var replicationTasks []*types.ReplicationTask for _, message := range resp.Messages { var replicationTask replicator.ReplicationTask err := q.encoder.Decode(message.Payload, &replicationTask) if err != nil { return nil, nil, fmt.Errorf("failed to decode dlq task: %v", err) } // Overwrite to local cluster message id replicationTask.SourceTaskId = common.Int64Ptr(int64(message.ID)) replicationTasks = append(replicationTasks, thrift.ToReplicationTask(&replicationTask)) } return replicationTasks, resp.NextPageToken, nil } func (q *replicationQueueImpl) UpdateDLQAckLevel( ctx context.Context, lastProcessedMessageID int64, ) error { return q.queue.UpdateDLQAckLevel(ctx, &persistence.UpdateDLQAckLevelRequest{ MessageID: lastProcessedMessageID, ClusterName: localDomainReplicationCluster, }) } func (q *replicationQueueImpl) GetDLQAckLevel( ctx context.Context, ) (int64, error) { dlqMetadataResp, err := q.queue.GetDLQAckLevels(ctx, &persistence.GetDLQAckLevelsRequest{}) if err != nil { return constants.EmptyMessageID, err } ackLevel, ok := dlqMetadataResp.AckLevels[localDomainReplicationCluster] if !ok { return constants.EmptyMessageID, nil } return ackLevel, nil } func (q *replicationQueueImpl) RangeDeleteMessagesFromDLQ( ctx context.Context, firstMessageID int64, lastMessageID int64, ) error { return q.queue.RangeDeleteMessagesFromDLQ(ctx, &persistence.RangeDeleteMessagesFromDLQRequest{ FirstMessageID: firstMessageID, LastMessageID: lastMessageID, }) } func (q *replicationQueueImpl) DeleteMessageFromDLQ( ctx context.Context, messageID int64, ) error { return q.queue.DeleteMessageFromDLQ(ctx, &persistence.DeleteMessageFromDLQRequest{ MessageID: messageID, }) } func (q *replicationQueueImpl) GetDLQSize(ctx context.Context) (int64, error) { resp, err := q.queue.GetDLQSize(ctx, &persistence.GetDLQSizeRequest{}) if err != nil { return 0, err } return resp.Size, nil } func (q *replicationQueueImpl) purgeAckedMessages() error { ackLevelByCluster, err := q.GetAckLevels(context.Background()) if err != nil { return fmt.Errorf("failed to purge messages: %v", err) } if len(ackLevelByCluster) == 0 { return nil } minAckLevel := int64(math.MaxInt64) for _, ackLevel := range ackLevelByCluster { if ackLevel < minAckLevel { minAckLevel = ackLevel } } if minAckLevel != int64(math.MaxInt64) { if err = q.queue.DeleteMessagesBefore(context.Background(), &persistence.DeleteMessagesBeforeRequest{ MessageID: minAckLevel, }); err != nil { return fmt.Errorf("failed to purge messages: %v", err) } } return nil } func (q *replicationQueueImpl) purgeProcessor() { ticker := time.NewTicker(purgeInterval) defer ticker.Stop() for { select { case <-q.done: return case <-ticker.C: if err := q.purgeAckedMessages(); err != nil { q.logger.Warn("Failed to purge acked domain replication messages.", tag.Error(err)) } } } } ================================================ FILE: common/domain/replication_queue_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: replication_queue.go // // Generated by this command: // // mockgen -package domain -source replication_queue.go -destination replication_queue_mock.go -self_package github.com/uber/common/persistence // // Package domain is a generated GoMock package. package domain import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockReplicationQueue is a mock of ReplicationQueue interface. type MockReplicationQueue struct { ctrl *gomock.Controller recorder *MockReplicationQueueMockRecorder isgomock struct{} } // MockReplicationQueueMockRecorder is the mock recorder for MockReplicationQueue. type MockReplicationQueueMockRecorder struct { mock *MockReplicationQueue } // NewMockReplicationQueue creates a new mock instance. func NewMockReplicationQueue(ctrl *gomock.Controller) *MockReplicationQueue { mock := &MockReplicationQueue{ctrl: ctrl} mock.recorder = &MockReplicationQueueMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockReplicationQueue) EXPECT() *MockReplicationQueueMockRecorder { return m.recorder } // DeleteMessageFromDLQ mocks base method. func (m *MockReplicationQueue) DeleteMessageFromDLQ(ctx context.Context, messageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessageFromDLQ", ctx, messageID) ret0, _ := ret[0].(error) return ret0 } // DeleteMessageFromDLQ indicates an expected call of DeleteMessageFromDLQ. func (mr *MockReplicationQueueMockRecorder) DeleteMessageFromDLQ(ctx, messageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessageFromDLQ", reflect.TypeOf((*MockReplicationQueue)(nil).DeleteMessageFromDLQ), ctx, messageID) } // GetAckLevels mocks base method. func (m *MockReplicationQueue) GetAckLevels(ctx context.Context) (map[string]int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAckLevels", ctx) ret0, _ := ret[0].(map[string]int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAckLevels indicates an expected call of GetAckLevels. func (mr *MockReplicationQueueMockRecorder) GetAckLevels(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAckLevels", reflect.TypeOf((*MockReplicationQueue)(nil).GetAckLevels), ctx) } // GetDLQAckLevel mocks base method. func (m *MockReplicationQueue) GetDLQAckLevel(ctx context.Context) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDLQAckLevel", ctx) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDLQAckLevel indicates an expected call of GetDLQAckLevel. func (mr *MockReplicationQueueMockRecorder) GetDLQAckLevel(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDLQAckLevel", reflect.TypeOf((*MockReplicationQueue)(nil).GetDLQAckLevel), ctx) } // GetDLQSize mocks base method. func (m *MockReplicationQueue) GetDLQSize(ctx context.Context) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDLQSize", ctx) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDLQSize indicates an expected call of GetDLQSize. func (mr *MockReplicationQueueMockRecorder) GetDLQSize(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDLQSize", reflect.TypeOf((*MockReplicationQueue)(nil).GetDLQSize), ctx) } // GetMessagesFromDLQ mocks base method. func (m *MockReplicationQueue) GetMessagesFromDLQ(ctx context.Context, firstMessageID, lastMessageID int64, pageSize int, pageToken []byte) ([]*types.ReplicationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMessagesFromDLQ", ctx, firstMessageID, lastMessageID, pageSize, pageToken) ret0, _ := ret[0].([]*types.ReplicationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // GetMessagesFromDLQ indicates an expected call of GetMessagesFromDLQ. func (mr *MockReplicationQueueMockRecorder) GetMessagesFromDLQ(ctx, firstMessageID, lastMessageID, pageSize, pageToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessagesFromDLQ", reflect.TypeOf((*MockReplicationQueue)(nil).GetMessagesFromDLQ), ctx, firstMessageID, lastMessageID, pageSize, pageToken) } // GetReplicationMessages mocks base method. func (m *MockReplicationQueue) GetReplicationMessages(ctx context.Context, lastMessageID int64, maxCount int) ([]*types.ReplicationTask, int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReplicationMessages", ctx, lastMessageID, maxCount) ret0, _ := ret[0].([]*types.ReplicationTask) ret1, _ := ret[1].(int64) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // GetReplicationMessages indicates an expected call of GetReplicationMessages. func (mr *MockReplicationQueueMockRecorder) GetReplicationMessages(ctx, lastMessageID, maxCount any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationMessages", reflect.TypeOf((*MockReplicationQueue)(nil).GetReplicationMessages), ctx, lastMessageID, maxCount) } // Publish mocks base method. func (m *MockReplicationQueue) Publish(ctx context.Context, message any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Publish", ctx, message) ret0, _ := ret[0].(error) return ret0 } // Publish indicates an expected call of Publish. func (mr *MockReplicationQueueMockRecorder) Publish(ctx, message any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Publish", reflect.TypeOf((*MockReplicationQueue)(nil).Publish), ctx, message) } // PublishToDLQ mocks base method. func (m *MockReplicationQueue) PublishToDLQ(ctx context.Context, message any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PublishToDLQ", ctx, message) ret0, _ := ret[0].(error) return ret0 } // PublishToDLQ indicates an expected call of PublishToDLQ. func (mr *MockReplicationQueueMockRecorder) PublishToDLQ(ctx, message any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishToDLQ", reflect.TypeOf((*MockReplicationQueue)(nil).PublishToDLQ), ctx, message) } // RangeDeleteMessagesFromDLQ mocks base method. func (m *MockReplicationQueue) RangeDeleteMessagesFromDLQ(ctx context.Context, firstMessageID, lastMessageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteMessagesFromDLQ", ctx, firstMessageID, lastMessageID) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteMessagesFromDLQ indicates an expected call of RangeDeleteMessagesFromDLQ. func (mr *MockReplicationQueueMockRecorder) RangeDeleteMessagesFromDLQ(ctx, firstMessageID, lastMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteMessagesFromDLQ", reflect.TypeOf((*MockReplicationQueue)(nil).RangeDeleteMessagesFromDLQ), ctx, firstMessageID, lastMessageID) } // Start mocks base method. func (m *MockReplicationQueue) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockReplicationQueueMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockReplicationQueue)(nil).Start)) } // Stop mocks base method. func (m *MockReplicationQueue) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockReplicationQueueMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockReplicationQueue)(nil).Stop)) } // UpdateAckLevel mocks base method. func (m *MockReplicationQueue) UpdateAckLevel(ctx context.Context, lastProcessedMessageID int64, clusterName string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAckLevel", ctx, lastProcessedMessageID, clusterName) ret0, _ := ret[0].(error) return ret0 } // UpdateAckLevel indicates an expected call of UpdateAckLevel. func (mr *MockReplicationQueueMockRecorder) UpdateAckLevel(ctx, lastProcessedMessageID, clusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAckLevel", reflect.TypeOf((*MockReplicationQueue)(nil).UpdateAckLevel), ctx, lastProcessedMessageID, clusterName) } // UpdateDLQAckLevel mocks base method. func (m *MockReplicationQueue) UpdateDLQAckLevel(ctx context.Context, lastProcessedMessageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDLQAckLevel", ctx, lastProcessedMessageID) ret0, _ := ret[0].(error) return ret0 } // UpdateDLQAckLevel indicates an expected call of UpdateDLQAckLevel. func (mr *MockReplicationQueueMockRecorder) UpdateDLQAckLevel(ctx, lastProcessedMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDLQAckLevel", reflect.TypeOf((*MockReplicationQueue)(nil).UpdateDLQAckLevel), ctx, lastProcessedMessageID) } ================================================ FILE: common/domain/replication_queue_test.go ================================================ // Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "bytes" "context" "encoding/binary" "errors" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const ( preambleVersion0 byte = 0x59 ) func TestReplicationQueueImpl_Start(t *testing.T) { tests := []struct { name string initialStatus int32 expectedStatus int32 shouldStart bool }{ { name: "Should start when initialized", initialStatus: common.DaemonStatusInitialized, expectedStatus: common.DaemonStatusStarted, shouldStart: true, }, { name: "Should not start when already started", initialStatus: common.DaemonStatusStarted, expectedStatus: common.DaemonStatusStarted, shouldStart: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)).(*replicationQueueImpl) atomic.StoreInt32(&rq.status, tt.initialStatus) rq.Start() defer rq.Stop() assert.Equal(t, tt.expectedStatus, atomic.LoadInt32(&rq.status)) if tt.shouldStart { select { case <-rq.done: t.Error("purgeProcessor should not have stopped") case <-time.After(time.Millisecond): // expected, as the purgeProcessor should still be running } } }) } } func TestReplicationQueueImpl_Stop(t *testing.T) { tests := []struct { name string initialStatus int32 expectedStatus int32 shouldStop bool }{ { name: "Should stop when started", initialStatus: common.DaemonStatusStarted, expectedStatus: common.DaemonStatusStopped, shouldStop: true, }, { name: "Should not stop when not started", initialStatus: common.DaemonStatusInitialized, expectedStatus: common.DaemonStatusInitialized, shouldStop: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)).(*replicationQueueImpl) atomic.StoreInt32(&rq.status, tt.initialStatus) rq.Stop() assert.Equal(t, tt.expectedStatus, atomic.LoadInt32(&rq.status)) if tt.shouldStop { select { case <-rq.done: // expected channel closed default: t.Error("done channel should be closed") } } }) } } func TestReplicationQueueImpl_Publish(t *testing.T) { tests := []struct { name string task *types.ReplicationTask wantErr bool setupMock func(q *persistence.MockQueueManager) }{ { name: "successful publish", task: &types.ReplicationTask{}, wantErr: false, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().EnqueueMessage(gomock.Any(), gomock.Any()).Return(nil) }, }, { name: "publish fails", task: &types.ReplicationTask{}, wantErr: true, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().EnqueueMessage(gomock.Any(), gomock.Any()).Return(errors.New("enqueue error")) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)) tt.setupMock(mockQueue) err := rq.Publish(context.Background(), tt.task) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestReplicationQueueImpl_PublishToDLQ(t *testing.T) { tests := []struct { name string task *types.ReplicationTask wantErr bool setupMock func(q *persistence.MockQueueManager) }{ { name: "successful publish to DLQ", task: &types.ReplicationTask{}, wantErr: false, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().EnqueueMessageToDLQ(gomock.Any(), gomock.Any()).Return(nil) }, }, { name: "publish to DLQ fails", task: &types.ReplicationTask{}, wantErr: true, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().EnqueueMessageToDLQ(gomock.Any(), gomock.Any()).Return(errors.New("enqueue to DLQ error")) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)) tt.setupMock(mockQueue) err := rq.PublishToDLQ(context.Background(), tt.task) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestGetReplicationMessages(t *testing.T) { tests := []struct { name string setupMocks func(mockQueueManager *persistence.MockQueueManager) expectedTasks int expectedLastID int64 expectError bool }{ { name: "handles empty message list", setupMocks: func(mockQueueManager *persistence.MockQueueManager) { mockQueueManager.EXPECT(). ReadMessages(gomock.Any(), gomock.Any()). Return(&persistence.ReadMessagesResponse{Messages: nil}, nil) }, expectedTasks: 0, expectedLastID: 0, expectError: false, }, { name: "decodes single message correctly", setupMocks: func(mockQueueManager *persistence.MockQueueManager) { // Setup mock to return one encoded message encodedMessage, _ := mockEncodeReplicationTask(123) mockQueueManager.EXPECT(). ReadMessages(gomock.Any(), gomock.Any()). Return(&persistence.ReadMessagesResponse{Messages: []*persistence.QueueMessage{{ID: 1, Payload: encodedMessage}}}, nil) }, expectedTasks: 1, expectedLastID: 1, expectError: false, }, { name: "decodes multiple messages correctly", setupMocks: func(mockQueueManager *persistence.MockQueueManager) { // Setup mock to return multiple encoded messages encodedMessage1, _ := mockEncodeReplicationTask(123) encodedMessage2, _ := mockEncodeReplicationTask(456) mockQueueManager.EXPECT(). ReadMessages(gomock.Any(), gomock.Any()). Return(&persistence.ReadMessagesResponse{Messages: []*persistence.QueueMessage{ {ID: 1, Payload: encodedMessage1}, {ID: 2, Payload: encodedMessage2}, }}, nil) }, expectedTasks: 2, expectedLastID: 2, expectError: false, }, { name: "read messages fails", expectedLastID: 100, expectError: true, setupMocks: func(mockQueueManager *persistence.MockQueueManager) { mockQueueManager.EXPECT().ReadMessages(gomock.Any(), gomock.Any()).Return(nil, errors.New("read error")) }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueueManager := persistence.NewMockQueueManager(ctrl) tc.setupMocks(mockQueueManager) replicationQueue := NewReplicationQueue(mockQueueManager, "testCluster", nil, testlogger.New(t)) tasks, lastID, err := replicationQueue.GetReplicationMessages(context.Background(), 0, 10) if tc.expectError { require.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tc.expectedTasks, len(tasks)) assert.Equal(t, tc.expectedLastID, lastID) } }) } } func TestUpdateAckLevel(t *testing.T) { tests := []struct { name string lastID int64 cluster string wantErr bool setupMock func(q *persistence.MockQueueManager) }{ { name: "successful ack level update", lastID: 100, cluster: "testCluster", wantErr: false, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().UpdateAckLevel(gomock.Any(), gomock.Eq(&persistence.UpdateAckLevelRequest{ MessageID: int64(100), ClusterName: "testCluster", })).Return(nil) }, }, { name: "ack level update fails", lastID: 100, cluster: "testCluster", wantErr: true, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().UpdateAckLevel(gomock.Any(), gomock.Eq(&persistence.UpdateAckLevelRequest{ MessageID: int64(100), ClusterName: "testCluster", })).Return(errors.New("update error")) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)) tt.setupMock(mockQueue) err := rq.UpdateAckLevel(context.Background(), tt.lastID, tt.cluster) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestReplicationQueueImpl_GetAckLevels(t *testing.T) { tests := []struct { name string want map[string]int64 wantErr bool setupMock func(q *persistence.MockQueueManager) }{ { name: "successful ack levels retrieval", want: map[string]int64{"testCluster": 100}, wantErr: false, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().GetAckLevels(gomock.Any(), gomock.Any()). Return(&persistence.GetAckLevelsResponse{AckLevels: map[string]int64{"testCluster": 100}}, nil) }, }, { name: "ack levels retrieval fails", wantErr: true, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().GetAckLevels(gomock.Any(), gomock.Any()).Return(nil, errors.New("retrieval error")) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)) tt.setupMock(mockQueue) got, err := rq.GetAckLevels(context.Background()) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.want, got) } }) } } func mockEncodeReplicationTask(sourceTaskID int64) ([]byte, error) { var buf bytes.Buffer buf.WriteByte(preambleVersion0) binary.Write(&buf, binary.BigEndian, sourceTaskID) return buf.Bytes(), nil } func TestGetMessagesFromDLQ(t *testing.T) { tests := []struct { name string firstID int64 lastID int64 pageSize int pageToken []byte taskID int64 wantErr bool }{ { name: "successful message retrieval", firstID: 100, lastID: 200, pageSize: 10, pageToken: []byte("token"), taskID: 12345, wantErr: false, }, { name: "read messages fails", firstID: 100, lastID: 200, pageSize: 10, pageToken: []byte("token"), wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)) if !tt.wantErr { encodedData, _ := mockEncodeReplicationTask(tt.taskID) messages := []*persistence.QueueMessage{ {ID: 1, Payload: encodedData}, } mockQueue.EXPECT().ReadMessagesFromDLQ(gomock.Any(), gomock.Any()). Return(&persistence.ReadMessagesFromDLQResponse{Messages: messages, NextPageToken: []byte("nextToken")}, nil) } else { mockQueue.EXPECT().ReadMessagesFromDLQ(gomock.Any(), gomock.Any()).Return(nil, errors.New("read error")) } replicationTasks, token, err := rq.GetMessagesFromDLQ(context.Background(), tt.firstID, tt.lastID, tt.pageSize, tt.pageToken) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Len(t, replicationTasks, 1, "Expected one replication task to be returned") assert.Equal(t, []byte("nextToken"), token, "Expected token to match 'nextToken'") } }) } } func TestUpdateDLQAckLevel(t *testing.T) { tests := []struct { name string lastID int64 wantErr bool setupMock func(q *persistence.MockQueueManager) }{ { name: "successful DLQ ack level update", lastID: 100, wantErr: false, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().UpdateDLQAckLevel(gomock.Any(), gomock.Eq(&persistence.UpdateDLQAckLevelRequest{ MessageID: int64(100), ClusterName: "domainReplication", })).Return(nil) }, }, { name: "DLQ ack level update fails", lastID: 100, wantErr: true, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().UpdateDLQAckLevel(gomock.Any(), gomock.Eq(&persistence.UpdateDLQAckLevelRequest{ MessageID: int64(100), ClusterName: "domainReplication", })).Return(errors.New("update DLQ ack level error")) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)) tt.setupMock(mockQueue) err := rq.UpdateDLQAckLevel(context.Background(), tt.lastID) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestGetDLQAckLevel(t *testing.T) { tests := []struct { name string want int64 wantErr bool setupMock func(q *persistence.MockQueueManager) }{ { name: "successful DLQ ack level retrieval", want: 100, wantErr: false, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().GetDLQAckLevels(gomock.Any(), gomock.Any()). Return(&persistence.GetDLQAckLevelsResponse{AckLevels: map[string]int64{"domainReplication": 100}}, nil) }, }, { name: "DLQ ack level retrieval fails", wantErr: true, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().GetDLQAckLevels(gomock.Any(), gomock.Any()).Return(nil, errors.New("get DLQ ack level error")) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)) tt.setupMock(mockQueue) got, err := rq.GetDLQAckLevel(context.Background()) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.want, got) } }) } } func TestRangeDeleteMessagesFromDLQ(t *testing.T) { tests := []struct { name string firstID int64 lastID int64 wantErr bool setupMock func(q *persistence.MockQueueManager) }{ { name: "successful range delete from DLQ", firstID: 10, lastID: 20, wantErr: false, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), gomock.Eq(&persistence.RangeDeleteMessagesFromDLQRequest{ FirstMessageID: int64(10), LastMessageID: int64(20), })).Return(nil) }, }, { name: "range delete from DLQ fails", firstID: 10, lastID: 20, wantErr: true, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), gomock.Eq(&persistence.RangeDeleteMessagesFromDLQRequest{ FirstMessageID: int64(10), LastMessageID: int64(20), })).Return(errors.New("range delete error")) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)) tt.setupMock(mockQueue) err := rq.RangeDeleteMessagesFromDLQ(context.Background(), tt.firstID, tt.lastID) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestDeleteMessageFromDLQ(t *testing.T) { tests := []struct { name string messageID int64 wantErr bool setupMock func(q *persistence.MockQueueManager) }{ { name: "successful delete from DLQ", messageID: 15, wantErr: false, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().DeleteMessageFromDLQ(gomock.Any(), gomock.Eq(&persistence.DeleteMessageFromDLQRequest{ MessageID: int64(15), })).Return(nil) }, }, { name: "delete from DLQ fails", messageID: 15, wantErr: true, setupMock: func(q *persistence.MockQueueManager) { q.EXPECT().DeleteMessageFromDLQ(gomock.Any(), gomock.Eq(&persistence.DeleteMessageFromDLQRequest{ MessageID: int64(15), })).Return(errors.New("delete error")) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueue := persistence.NewMockQueueManager(ctrl) rq := NewReplicationQueue(mockQueue, "testCluster", nil, testlogger.New(t)) tt.setupMock(mockQueue) err := rq.DeleteMessageFromDLQ(context.Background(), tt.messageID) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestGetDLQSize(t *testing.T) { tests := []struct { name string wantSize int64 wantErr bool setupMock func(m *persistence.MockQueueManager) }{ { name: "returns correct size for non-empty DLQ", wantSize: 10, wantErr: false, setupMock: func(m *persistence.MockQueueManager) { m.EXPECT().GetDLQSize(gomock.Any(), gomock.Any()).Return(&persistence.GetDLQSizeResponse{Size: int64(10)}, nil) }, }, { name: "returns zero for empty DLQ", wantSize: 0, wantErr: false, setupMock: func(m *persistence.MockQueueManager) { m.EXPECT().GetDLQSize(gomock.Any(), gomock.Any()).Return(&persistence.GetDLQSizeResponse{Size: int64(0)}, nil) }, }, { name: "propagates error from underlying queue", wantErr: true, setupMock: func(m *persistence.MockQueueManager) { m.EXPECT().GetDLQSize(gomock.Any(), gomock.Any()).Return(nil, errors.New("database error")) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueueManager := persistence.NewMockQueueManager(ctrl) tt.setupMock(mockQueueManager) q := &replicationQueueImpl{queue: mockQueueManager, logger: testlogger.New(t)} size, err := q.GetDLQSize(context.Background()) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.wantSize, size) } }) } } func TestPurgeAckedMessages(t *testing.T) { tests := []struct { name string wantErr bool setupMock func(m *persistence.MockQueueManager) }{ { name: "successfully purges messages", wantErr: false, setupMock: func(m *persistence.MockQueueManager) { m.EXPECT().GetAckLevels(gomock.Any(), gomock.Any()). Return(&persistence.GetAckLevelsResponse{AckLevels: map[string]int64{"clusterA": 5}}, nil) m.EXPECT().DeleteMessagesBefore(gomock.Any(), gomock.Eq(&persistence.DeleteMessagesBeforeRequest{ MessageID: int64(5), })).Return(nil) }, }, { name: "does nothing when no ack levels", wantErr: false, setupMock: func(m *persistence.MockQueueManager) { m.EXPECT().GetAckLevels(gomock.Any(), gomock.Any()). Return(&persistence.GetAckLevelsResponse{AckLevels: map[string]int64{}}, nil) }, }, { name: "error on GetAckLevels", wantErr: true, setupMock: func(m *persistence.MockQueueManager) { m.EXPECT().GetAckLevels(gomock.Any(), gomock.Any()).Return(nil, errors.New("database error")) }, }, { name: "error on DeleteMessagesBefore", wantErr: true, setupMock: func(m *persistence.MockQueueManager) { m.EXPECT().GetAckLevels(gomock.Any(), gomock.Any()). Return(&persistence.GetAckLevelsResponse{AckLevels: map[string]int64{"clusterA": 5}}, nil) m.EXPECT().DeleteMessagesBefore(gomock.Any(), gomock.Eq(&persistence.DeleteMessagesBeforeRequest{ MessageID: int64(5), })).Return(errors.New("delete error")) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueueManager := persistence.NewMockQueueManager(ctrl) tt.setupMock(mockQueueManager) q := &replicationQueueImpl{queue: mockQueueManager, logger: testlogger.New(t)} err := q.purgeAckedMessages() if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } ================================================ FILE: common/domain/transmissionTaskHandler.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination transmissionTaskHandler_mock.go package domain import ( "context" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // NOTE: the counterpart of domain replication receiving logic is in service/worker package type ( // Replicator is the interface which can replicate the domain Replicator interface { HandleTransmissionTask( ctx context.Context, domainOperation types.DomainOperation, info *persistence.DomainInfo, config *persistence.DomainConfig, replicationConfig *persistence.DomainReplicationConfig, configVersion int64, failoverVersion int64, previousFailoverVersion int64, isGlobalDomainEnabled bool, ) error } domainReplicatorImpl struct { replicationMessageSink messaging.Producer logger log.Logger } ) // NewDomainReplicator create a new instance of domain replicator func NewDomainReplicator(replicationMessageSink messaging.Producer, logger log.Logger) Replicator { return &domainReplicatorImpl{ replicationMessageSink: replicationMessageSink, logger: logger, } } // HandleTransmissionTask handle transmission of the domain replication task func (domainReplicator *domainReplicatorImpl) HandleTransmissionTask( ctx context.Context, domainOperation types.DomainOperation, info *persistence.DomainInfo, config *persistence.DomainConfig, replicationConfig *persistence.DomainReplicationConfig, configVersion int64, failoverVersion int64, previousFailoverVersion int64, isGlobalDomainEnabled bool, ) error { if !isGlobalDomainEnabled { domainReplicator.logger.Warn("Should not replicate non global domain", tag.WorkflowDomainID(info.ID)) return nil } status, err := domainReplicator.convertDomainStatusToThrift(info.Status) if err != nil { return err } taskType := types.ReplicationTaskTypeDomain task := &types.DomainTaskAttributes{ DomainOperation: &domainOperation, ID: info.ID, Info: &types.DomainInfo{ Name: info.Name, Status: status, Description: info.Description, OwnerEmail: info.OwnerEmail, Data: info.Data, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: config.Retention, EmitMetric: config.EmitMetric, HistoryArchivalStatus: config.HistoryArchivalStatus.Ptr(), HistoryArchivalURI: config.HistoryArchivalURI, VisibilityArchivalStatus: config.VisibilityArchivalStatus.Ptr(), VisibilityArchivalURI: config.VisibilityArchivalURI, BadBinaries: &config.BadBinaries, IsolationGroups: &config.IsolationGroups, AsyncWorkflowConfig: &config.AsyncWorkflowConfig, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: replicationConfig.ActiveClusterName, Clusters: domainReplicator.convertClusterReplicationConfigToThrift(replicationConfig.Clusters), ActiveClusters: replicationConfig.ActiveClusters, }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, PreviousFailoverVersion: previousFailoverVersion, } return domainReplicator.replicationMessageSink.Publish( ctx, &types.ReplicationTask{ TaskType: &taskType, DomainTaskAttributes: task, }) } func (domainReplicator *domainReplicatorImpl) convertClusterReplicationConfigToThrift( input []*persistence.ClusterReplicationConfig, ) []*types.ClusterReplicationConfiguration { output := []*types.ClusterReplicationConfiguration{} for _, cluster := range input { output = append(output, &types.ClusterReplicationConfiguration{ClusterName: cluster.ClusterName}) } return output } func (domainReplicator *domainReplicatorImpl) convertDomainStatusToThrift(input int) (*types.DomainStatus, error) { switch input { case persistence.DomainStatusRegistered: output := types.DomainStatusRegistered return &output, nil case persistence.DomainStatusDeprecated: output := types.DomainStatusDeprecated return &output, nil default: return nil, ErrInvalidDomainStatus } } ================================================ FILE: common/domain/transmissionTaskHandler_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: transmissionTaskHandler.go // // Generated by this command: // // mockgen -package domain -source transmissionTaskHandler.go -destination transmissionTaskHandler_mock.go // // Package domain is a generated GoMock package. package domain import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" types "github.com/uber/cadence/common/types" ) // MockReplicator is a mock of Replicator interface. type MockReplicator struct { ctrl *gomock.Controller recorder *MockReplicatorMockRecorder isgomock struct{} } // MockReplicatorMockRecorder is the mock recorder for MockReplicator. type MockReplicatorMockRecorder struct { mock *MockReplicator } // NewMockReplicator creates a new mock instance. func NewMockReplicator(ctrl *gomock.Controller) *MockReplicator { mock := &MockReplicator{ctrl: ctrl} mock.recorder = &MockReplicatorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockReplicator) EXPECT() *MockReplicatorMockRecorder { return m.recorder } // HandleTransmissionTask mocks base method. func (m *MockReplicator) HandleTransmissionTask(ctx context.Context, domainOperation types.DomainOperation, info *persistence.DomainInfo, config *persistence.DomainConfig, replicationConfig *persistence.DomainReplicationConfig, configVersion, failoverVersion, previousFailoverVersion int64, isGlobalDomainEnabled bool) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleTransmissionTask", ctx, domainOperation, info, config, replicationConfig, configVersion, failoverVersion, previousFailoverVersion, isGlobalDomainEnabled) ret0, _ := ret[0].(error) return ret0 } // HandleTransmissionTask indicates an expected call of HandleTransmissionTask. func (mr *MockReplicatorMockRecorder) HandleTransmissionTask(ctx, domainOperation, info, config, replicationConfig, configVersion, failoverVersion, previousFailoverVersion, isGlobalDomainEnabled any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleTransmissionTask", reflect.TypeOf((*MockReplicator)(nil).HandleTransmissionTask), ctx, domainOperation, info, config, replicationConfig, configVersion, failoverVersion, previousFailoverVersion, isGlobalDomainEnabled) } ================================================ FILE: common/domain/transmissionTaskHandler_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package domain import ( "context" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/mocks" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( transmissionTaskSuite struct { suite.Suite domainReplicator *domainReplicatorImpl kafkaProducer *mocks.KafkaProducer } ) func TestTransmissionTaskSuite(t *testing.T) { s := new(transmissionTaskSuite) suite.Run(t, s) } func (s *transmissionTaskSuite) SetupSuite() { } func (s *transmissionTaskSuite) TearDownSuite() { } func (s *transmissionTaskSuite) SetupTest() { s.kafkaProducer = &mocks.KafkaProducer{} s.domainReplicator = NewDomainReplicator( s.kafkaProducer, testlogger.New(s.Suite.T()), ).(*domainReplicatorImpl) } func (s *transmissionTaskSuite) TearDownTest() { s.kafkaProducer.AssertExpectations(s.T()) } func (s *transmissionTaskSuite) TestHandleTransmissionTask_RegisterDomainTask_IsGlobalDomain() { taskType := types.ReplicationTaskTypeDomain id := uuid.New() name := "some random domain test name" status := types.DomainStatusRegistered description := "some random test description" ownerEmail := "some random test owner" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "some random history archival uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "some random visibility archival uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(0) failoverVersion := int64(59) previousFailoverVersion := int64(55) clusters := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } domainOperation := types.DomainOperationCreate info := &p.DomainInfo{ ID: id, Name: name, Status: p.DomainStatusRegistered, Description: description, OwnerEmail: ownerEmail, Data: data, } config := &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{}, } replicationConfig := &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, } isGlobalDomain := true s.kafkaProducer.On("Publish", mock.Anything, &types.ReplicationTask{ TaskType: &taskType, DomainTaskAttributes: &types.DomainTaskAttributes{ DomainOperation: &domainOperation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &status, Description: description, OwnerEmail: ownerEmail, Data: data, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{}, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterActive, Clusters: s.domainReplicator.convertClusterReplicationConfigToThrift(clusters), }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, PreviousFailoverVersion: previousFailoverVersion, }, }).Return(nil).Once() err := s.domainReplicator.HandleTransmissionTask( context.Background(), domainOperation, info, config, replicationConfig, configVersion, failoverVersion, previousFailoverVersion, isGlobalDomain, ) s.Nil(err) } func (s *transmissionTaskSuite) TestHandleTransmissionTask_RegisterDomainTask_NotGlobalDomain() { id := uuid.New() name := "some random domain test name" description := "some random test description" ownerEmail := "some random test owner" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "some random history archival uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "some random visibility archival uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(0) failoverVersion := int64(59) previousFailoverVersion := int64(55) clusters := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } domainOperation := types.DomainOperationCreate info := &p.DomainInfo{ ID: id, Name: name, Status: p.DomainStatusRegistered, Description: description, OwnerEmail: ownerEmail, Data: data, } config := &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: types.BadBinaries{}, } replicationConfig := &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, } isGlobalDomain := false err := s.domainReplicator.HandleTransmissionTask( context.Background(), domainOperation, info, config, replicationConfig, configVersion, failoverVersion, previousFailoverVersion, isGlobalDomain, ) s.Nil(err) } func (s *transmissionTaskSuite) TestHandleTransmissionTask_UpdateDomainTask_IsGlobalDomain() { taskType := types.ReplicationTaskTypeDomain id := uuid.New() name := "some random domain test name" status := types.DomainStatusDeprecated description := "some random test description" ownerEmail := "some random test owner" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "some random history archival uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "some random visibility archival uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(0) failoverVersion := int64(59) previousFailoverVersion := int64(55) clusters := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } domainOperation := types.DomainOperationUpdate info := &p.DomainInfo{ ID: id, Name: name, Status: p.DomainStatusDeprecated, Description: description, OwnerEmail: ownerEmail, Data: data, } config := &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": {Name: "zone-1", State: types.IsolationGroupStateDrained}, }, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "queue1", QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"cluster": "queue1"}`), }, }, } replicationConfig := &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, } isGlobalDomain := true s.kafkaProducer.On("Publish", mock.Anything, &types.ReplicationTask{ TaskType: &taskType, DomainTaskAttributes: &types.DomainTaskAttributes{ DomainOperation: &domainOperation, ID: id, Info: &types.DomainInfo{ Name: name, Status: &status, Description: description, OwnerEmail: ownerEmail, Data: data, }, Config: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus.Ptr(), HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus.Ptr(), VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: &types.IsolationGroupConfiguration{ "zone-1": {Name: "zone-1", State: types.IsolationGroupStateDrained}, }, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "queue1", QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"cluster": "queue1"}`), }, }, }, ReplicationConfig: &types.DomainReplicationConfiguration{ ActiveClusterName: clusterActive, Clusters: s.domainReplicator.convertClusterReplicationConfigToThrift(clusters), }, ConfigVersion: configVersion, FailoverVersion: failoverVersion, PreviousFailoverVersion: previousFailoverVersion, }, }).Return(nil).Once() err := s.domainReplicator.HandleTransmissionTask( context.Background(), domainOperation, info, config, replicationConfig, configVersion, failoverVersion, previousFailoverVersion, isGlobalDomain, ) s.Nil(err) } func (s *transmissionTaskSuite) TestHandleTransmissionTask_UpdateDomainTask_NotGlobalDomain() { id := uuid.New() name := "some random domain test name" description := "some random test description" ownerEmail := "some random test owner" data := map[string]string{"k": "v"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "some random history archival uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "some random visibility archival uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(0) failoverVersion := int64(59) previousFailoverVersion := int64(55) clusters := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } domainOperation := types.DomainOperationUpdate info := &p.DomainInfo{ ID: id, Name: name, Status: p.DomainStatusDeprecated, Description: description, OwnerEmail: ownerEmail, Data: data, } config := &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, } replicationConfig := &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, } isGlobalDomain := false err := s.domainReplicator.HandleTransmissionTask( context.Background(), domainOperation, info, config, replicationConfig, configVersion, failoverVersion, previousFailoverVersion, isGlobalDomain, ) s.Nil(err) } ================================================ FILE: common/dynamicconfig/clientInterface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination clientInterface_mock.go -self_package github.com/uber/cadence/common/dynamicconfig package dynamicconfig import ( "time" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) const ( ConfigStoreClient = "configstore" FileBasedClient = "filebased" InMemoryClient = "memory" NopClient = "nop" ) // Client allows fetching values from a dynamic configuration system NOTE: This does not have async // options right now. In the interest of keeping it minimal, we can add when requirement arises. type Client interface { GetValue(name dynamicproperties.Key) (interface{}, error) GetValueWithFilters(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) (interface{}, error) GetIntValue(name dynamicproperties.IntKey, filters map[dynamicproperties.Filter]interface{}) (int, error) GetFloatValue(name dynamicproperties.FloatKey, filters map[dynamicproperties.Filter]interface{}) (float64, error) GetBoolValue(name dynamicproperties.BoolKey, filters map[dynamicproperties.Filter]interface{}) (bool, error) GetStringValue(name dynamicproperties.StringKey, filters map[dynamicproperties.Filter]interface{}) (string, error) GetMapValue(name dynamicproperties.MapKey, filters map[dynamicproperties.Filter]interface{}) (map[string]interface{}, error) GetDurationValue(name dynamicproperties.DurationKey, filters map[dynamicproperties.Filter]interface{}) (time.Duration, error) GetListValue(name dynamicproperties.ListKey, filters map[dynamicproperties.Filter]interface{}) ([]interface{}, error) // UpdateValue takes value as map and updates by overriding. It doesn't support update with filters. UpdateValue(name dynamicproperties.Key, value interface{}) error RestoreValue(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) error ListValue(name dynamicproperties.Key) ([]*types.DynamicConfigEntry, error) } var NotFoundError = &types.EntityNotExistsError{ Message: "unable to find key", } ================================================ FILE: common/dynamicconfig/clientInterface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: clientInterface.go // // Generated by this command: // // mockgen -package dynamicconfig -source clientInterface.go -destination clientInterface_mock.go -self_package github.com/uber/cadence/common/dynamicconfig // // Package dynamicconfig is a generated GoMock package. package dynamicconfig import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" dynamicproperties "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" types "github.com/uber/cadence/common/types" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // GetBoolValue mocks base method. func (m *MockClient) GetBoolValue(name dynamicproperties.BoolKey, filters map[dynamicproperties.Filter]any) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBoolValue", name, filters) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // GetBoolValue indicates an expected call of GetBoolValue. func (mr *MockClientMockRecorder) GetBoolValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBoolValue", reflect.TypeOf((*MockClient)(nil).GetBoolValue), name, filters) } // GetDurationValue mocks base method. func (m *MockClient) GetDurationValue(name dynamicproperties.DurationKey, filters map[dynamicproperties.Filter]any) (time.Duration, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDurationValue", name, filters) ret0, _ := ret[0].(time.Duration) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDurationValue indicates an expected call of GetDurationValue. func (mr *MockClientMockRecorder) GetDurationValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDurationValue", reflect.TypeOf((*MockClient)(nil).GetDurationValue), name, filters) } // GetFloatValue mocks base method. func (m *MockClient) GetFloatValue(name dynamicproperties.FloatKey, filters map[dynamicproperties.Filter]any) (float64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFloatValue", name, filters) ret0, _ := ret[0].(float64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFloatValue indicates an expected call of GetFloatValue. func (mr *MockClientMockRecorder) GetFloatValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFloatValue", reflect.TypeOf((*MockClient)(nil).GetFloatValue), name, filters) } // GetIntValue mocks base method. func (m *MockClient) GetIntValue(name dynamicproperties.IntKey, filters map[dynamicproperties.Filter]any) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetIntValue", name, filters) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // GetIntValue indicates an expected call of GetIntValue. func (mr *MockClientMockRecorder) GetIntValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIntValue", reflect.TypeOf((*MockClient)(nil).GetIntValue), name, filters) } // GetListValue mocks base method. func (m *MockClient) GetListValue(name dynamicproperties.ListKey, filters map[dynamicproperties.Filter]any) ([]any, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetListValue", name, filters) ret0, _ := ret[0].([]any) ret1, _ := ret[1].(error) return ret0, ret1 } // GetListValue indicates an expected call of GetListValue. func (mr *MockClientMockRecorder) GetListValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListValue", reflect.TypeOf((*MockClient)(nil).GetListValue), name, filters) } // GetMapValue mocks base method. func (m *MockClient) GetMapValue(name dynamicproperties.MapKey, filters map[dynamicproperties.Filter]any) (map[string]any, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMapValue", name, filters) ret0, _ := ret[0].(map[string]any) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMapValue indicates an expected call of GetMapValue. func (mr *MockClientMockRecorder) GetMapValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMapValue", reflect.TypeOf((*MockClient)(nil).GetMapValue), name, filters) } // GetStringValue mocks base method. func (m *MockClient) GetStringValue(name dynamicproperties.StringKey, filters map[dynamicproperties.Filter]any) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetStringValue", name, filters) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStringValue indicates an expected call of GetStringValue. func (mr *MockClientMockRecorder) GetStringValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStringValue", reflect.TypeOf((*MockClient)(nil).GetStringValue), name, filters) } // GetValue mocks base method. func (m *MockClient) GetValue(name dynamicproperties.Key) (any, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValue", name) ret0, _ := ret[0].(any) ret1, _ := ret[1].(error) return ret0, ret1 } // GetValue indicates an expected call of GetValue. func (mr *MockClientMockRecorder) GetValue(name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValue", reflect.TypeOf((*MockClient)(nil).GetValue), name) } // GetValueWithFilters mocks base method. func (m *MockClient) GetValueWithFilters(name dynamicproperties.Key, filters map[dynamicproperties.Filter]any) (any, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValueWithFilters", name, filters) ret0, _ := ret[0].(any) ret1, _ := ret[1].(error) return ret0, ret1 } // GetValueWithFilters indicates an expected call of GetValueWithFilters. func (mr *MockClientMockRecorder) GetValueWithFilters(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValueWithFilters", reflect.TypeOf((*MockClient)(nil).GetValueWithFilters), name, filters) } // ListValue mocks base method. func (m *MockClient) ListValue(name dynamicproperties.Key) ([]*types.DynamicConfigEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListValue", name) ret0, _ := ret[0].([]*types.DynamicConfigEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // ListValue indicates an expected call of ListValue. func (mr *MockClientMockRecorder) ListValue(name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListValue", reflect.TypeOf((*MockClient)(nil).ListValue), name) } // RestoreValue mocks base method. func (m *MockClient) RestoreValue(name dynamicproperties.Key, filters map[dynamicproperties.Filter]any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RestoreValue", name, filters) ret0, _ := ret[0].(error) return ret0 } // RestoreValue indicates an expected call of RestoreValue. func (mr *MockClientMockRecorder) RestoreValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestoreValue", reflect.TypeOf((*MockClient)(nil).RestoreValue), name, filters) } // UpdateValue mocks base method. func (m *MockClient) UpdateValue(name dynamicproperties.Key, value any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateValue", name, value) ret0, _ := ret[0].(error) return ret0 } // UpdateValue indicates an expected call of UpdateValue. func (mr *MockClientMockRecorder) UpdateValue(name, value any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateValue", reflect.TypeOf((*MockClient)(nil).UpdateValue), name, value) } ================================================ FILE: common/dynamicconfig/config/testConfig.yaml ================================================ admin.HeaderForwardingRules: - value: - Add: true Match: (?i)key constraints: {} frontend.validSearchAttributes: - value: DomainID: 1 constraints: {} testGetBoolPropertyKey: - value: false constraints: {} - value: true constraints: domainName: global-samples-domain - value: true constraints: domainName: samples-domain testGetDurationPropertyKey: - value: 1m constraints: {} - value: wrong duration string constraints: domainName: samples-domain taskListName: longIdleTimeTasklist - value: 2 constraints: domainName: samples-domain testGetFloat64PropertyKey: - value: 12 constraints: {} - value: wrong type constraints: domainName: samples-domain testGetIntPropertyKey: - value: 1000 constraints: {} - value: 1000.1 constraints: domainName: global-samples-domain testGetMapPropertyKey: - value: key1: "1" key2: 1 key3: - false - key4: true key5: 2.1 constraints: {} - value: "1" constraints: taskListName: random tasklist testGetStringPropertyKey: - value: some random string constraints: {} - value: constrained-string constraints: taskListName: random tasklist ================================================ FILE: common/dynamicconfig/config.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamicconfig import ( "fmt" "strings" "sync/atomic" "time" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) const ( errCountLogThreshold = 1000 ) // NewCollection creates a new collection func NewCollection( client Client, logger log.Logger, filterOptions ...dynamicproperties.FilterOption, ) *Collection { return &Collection{ client: client, logger: logger, errCount: -1, filterOptions: filterOptions, } } // Collection wraps dynamic config client with a closure so that across the code, the config values // can be directly accessed by calling the function without propagating the client everywhere in // code type Collection struct { client Client logger log.Logger errCount int64 filterOptions []dynamicproperties.FilterOption } func (c *Collection) logError( key dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}, err error, ) { errCount := atomic.AddInt64(&c.errCount, 1) if errCount%errCountLogThreshold == 0 { // log only every 'x' errors to reduce mem allocs and to avoid log noise filteredKey := getFilteredKeyAsString(key, filters) if _, ok := err.(*types.EntityNotExistsError); ok { c.logger.Debug("dynamic config not set, use default value", tag.Key(filteredKey)) } else { c.logger.Warn("Failed to fetch key from dynamic config", tag.Key(filteredKey), tag.Error(err)) } } } // GetProperty gets a interface property and returns defaultValue if property is not found func (c *Collection) GetProperty(key dynamicproperties.Key) dynamicproperties.PropertyFn { return func() interface{} { val, err := c.client.GetValue(key) if err != nil { c.logError(key, nil, err) return key.DefaultValue() } return val } } // GetIntProperty gets property and asserts that it's an integer func (c *Collection) GetIntProperty(key dynamicproperties.IntKey) dynamicproperties.IntPropertyFn { return func(opts ...dynamicproperties.FilterOption) int { filters := c.toFilterMap(opts...) val, err := c.client.GetIntValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultInt() } return val } } // GetIntPropertyFilteredByDomain gets property with domain filter and asserts that it's an integer func (c *Collection) GetIntPropertyFilteredByDomain(key dynamicproperties.IntKey) dynamicproperties.IntPropertyFnWithDomainFilter { return func(domain string) int { filters := c.toFilterMap(dynamicproperties.DomainFilter(domain)) val, err := c.client.GetIntValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultInt() } return val } } // GetIntPropertyFilteredByWorkflowType gets property with workflow type filter and asserts that it's an integer func (c *Collection) GetIntPropertyFilteredByWorkflowType(key dynamicproperties.IntKey) dynamicproperties.IntPropertyFnWithWorkflowTypeFilter { return func(domainName string, workflowType string) int { filters := c.toFilterMap( dynamicproperties.DomainFilter(domainName), dynamicproperties.WorkflowTypeFilter(workflowType), ) val, err := c.client.GetIntValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultInt() } return val } } // GetDurationPropertyFilteredByWorkflowType gets property with workflow type filter and asserts that it's a duration func (c *Collection) GetDurationPropertyFilteredByWorkflowType(key dynamicproperties.DurationKey) dynamicproperties.DurationPropertyFnWithWorkflowTypeFilter { return func(domainName string, workflowType string) time.Duration { filters := c.toFilterMap( dynamicproperties.DomainFilter(domainName), dynamicproperties.WorkflowTypeFilter(workflowType), ) val, err := c.client.GetDurationValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultDuration() } return val } } // GetIntPropertyFilteredByTaskListInfo gets property with taskListInfo as filters and asserts that it's an integer func (c *Collection) GetIntPropertyFilteredByTaskListInfo(key dynamicproperties.IntKey) dynamicproperties.IntPropertyFnWithTaskListInfoFilters { return func(domain string, taskList string, taskType int) int { filters := c.toFilterMap( dynamicproperties.DomainFilter(domain), dynamicproperties.TaskListFilter(taskList), dynamicproperties.TaskTypeFilter(taskType), ) val, err := c.client.GetIntValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultInt() } return val } } // GetIntPropertyFilteredByShardID gets property with shardID as filter and asserts that it's an integer func (c *Collection) GetIntPropertyFilteredByShardID(key dynamicproperties.IntKey) dynamicproperties.IntPropertyFnWithShardIDFilter { return func(shardID int) int { filters := c.toFilterMap(dynamicproperties.ShardIDFilter(shardID)) val, err := c.client.GetIntValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultInt() } return val } } // GetBoolPropertyFilteredByShardID gets property with shardID as filter and asserts that it's a bool func (c *Collection) GetBoolPropertyFilteredByShardID(key dynamicproperties.BoolKey) dynamicproperties.BoolPropertyFnWithShardIDFilter { return func(shardID int) bool { filters := c.toFilterMap(dynamicproperties.ShardIDFilter(shardID)) val, err := c.client.GetBoolValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultBool() } return val } } // GetFloat64Property gets property and asserts that it's a float64 func (c *Collection) GetFloat64Property(key dynamicproperties.FloatKey) dynamicproperties.FloatPropertyFn { return func(opts ...dynamicproperties.FilterOption) float64 { filters := c.toFilterMap(opts...) val, err := c.client.GetFloatValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultFloat() } return val } } // GetFloat64PropertyFilteredByShardID gets property with shardID filter and asserts that it's a float64 func (c *Collection) GetFloat64PropertyFilteredByShardID(key dynamicproperties.FloatKey) dynamicproperties.FloatPropertyFnWithShardIDFilter { return func(shardID int) float64 { filters := c.toFilterMap(dynamicproperties.ShardIDFilter(shardID)) val, err := c.client.GetFloatValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultFloat() } return val } } // GetFloatPropertyFilteredByTaskListInfo gets property with taskListInfo as filters and asserts that it's a float64 func (c *Collection) GetFloat64PropertyFilteredByTaskListInfo(key dynamicproperties.FloatKey) dynamicproperties.FloatPropertyFnWithTaskListInfoFilters { return func(domain string, taskList string, taskType int) float64 { filters := c.toFilterMap( dynamicproperties.DomainFilter(domain), dynamicproperties.TaskListFilter(taskList), dynamicproperties.TaskTypeFilter(taskType), ) val, err := c.client.GetFloatValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultFloat() } return val } } // GetFloat64PropertyFilteredByNamespace gets property with domain filter and asserts that it's a float64 func (c *Collection) GetFloat64PropertyFilteredByNamespace(key dynamicproperties.FloatKey) dynamicproperties.Float64PropertyFnWithNamespaceFilters { return func(namespace string) float64 { filters := c.toFilterMap(dynamicproperties.NamespaceFilter(namespace)) val, err := c.client.GetFloatValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultFloat() } return val } } // GetDurationProperty gets property and asserts that it's a duration func (c *Collection) GetDurationProperty(key dynamicproperties.DurationKey) dynamicproperties.DurationPropertyFn { return func(opts ...dynamicproperties.FilterOption) time.Duration { filters := c.toFilterMap(opts...) val, err := c.client.GetDurationValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultDuration() } return val } } // GetDurationPropertyFilteredByDomain gets property with domain filter and asserts that it's a duration func (c *Collection) GetDurationPropertyFilteredByDomain(key dynamicproperties.DurationKey) dynamicproperties.DurationPropertyFnWithDomainFilter { return func(domain string) time.Duration { filters := c.toFilterMap(dynamicproperties.DomainFilter(domain)) val, err := c.client.GetDurationValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultDuration() } return val } } // GetDurationPropertyFilteredByDomainID gets property with domainID filter and asserts that it's a duration func (c *Collection) GetDurationPropertyFilteredByDomainID(key dynamicproperties.DurationKey) dynamicproperties.DurationPropertyFnWithDomainIDFilter { return func(domainID string) time.Duration { filters := c.toFilterMap(dynamicproperties.DomainIDFilter(domainID)) val, err := c.client.GetDurationValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultDuration() } return val } } // GetDurationPropertyFilteredByTaskListInfo gets property with taskListInfo as filters and asserts that it's a duration func (c *Collection) GetDurationPropertyFilteredByTaskListInfo(key dynamicproperties.DurationKey) dynamicproperties.DurationPropertyFnWithTaskListInfoFilters { return func(domain string, taskList string, taskType int) time.Duration { filters := c.toFilterMap( dynamicproperties.DomainFilter(domain), dynamicproperties.TaskListFilter(taskList), dynamicproperties.TaskTypeFilter(taskType), ) val, err := c.client.GetDurationValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultDuration() } return val } } // GetDurationPropertyFilteredByShardID gets property with shardID id as filter and asserts that it's a duration func (c *Collection) GetDurationPropertyFilteredByShardID(key dynamicproperties.DurationKey) dynamicproperties.DurationPropertyFnWithShardIDFilter { return func(shardID int) time.Duration { filters := c.toFilterMap(dynamicproperties.ShardIDFilter(shardID)) val, err := c.client.GetDurationValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultDuration() } return val } } // GetBoolProperty gets property and asserts that it's an bool func (c *Collection) GetBoolProperty(key dynamicproperties.BoolKey) dynamicproperties.BoolPropertyFn { return func(opts ...dynamicproperties.FilterOption) bool { filters := c.toFilterMap(opts...) val, err := c.client.GetBoolValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultBool() } return val } } // GetStringProperty gets property and asserts that it's an string func (c *Collection) GetStringProperty(key dynamicproperties.StringKey) dynamicproperties.StringPropertyFn { return func(opts ...dynamicproperties.FilterOption) string { filters := c.toFilterMap(opts...) val, err := c.client.GetStringValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultString() } return val } } // GetMapProperty gets property and asserts that it's a map func (c *Collection) GetMapProperty(key dynamicproperties.MapKey) dynamicproperties.MapPropertyFn { return func(opts ...dynamicproperties.FilterOption) map[string]interface{} { filters := c.toFilterMap(opts...) val, err := c.client.GetMapValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultMap() } return val } } // GetMapPropertyFilteredByDomain gets property with domain filter and asserts that it's a map func (c *Collection) GetMapPropertyFilteredByDomain(key dynamicproperties.MapKey) dynamicproperties.MapPropertyFnWithDomainFilter { return func(domain string) map[string]interface{} { filters := c.toFilterMap(dynamicproperties.DomainFilter(domain)) val, err := c.client.GetMapValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultMap() } return val } } // GetStringPropertyFilteredByDomain gets property with domain filter and asserts that it's a string func (c *Collection) GetStringPropertyFilteredByDomain(key dynamicproperties.StringKey) dynamicproperties.StringPropertyFnWithDomainFilter { return func(domain string) string { filters := c.toFilterMap(dynamicproperties.DomainFilter(domain)) val, err := c.client.GetStringValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultString() } return val } } func (c *Collection) GetStringPropertyFilteredByTaskListInfo(key dynamicproperties.StringKey) dynamicproperties.StringPropertyFnWithTaskListInfoFilters { return func(domain string, taskList string, taskType int) string { filters := c.toFilterMap( dynamicproperties.DomainFilter(domain), dynamicproperties.TaskListFilter(taskList), dynamicproperties.TaskTypeFilter(taskType), ) val, err := c.client.GetStringValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultString() } return val } } // GetStringPropertyFilteredByNamespace gets property with domain filter and asserts that it's a string func (c *Collection) GetStringPropertyFilteredByNamespace(key dynamicproperties.StringKey) dynamicproperties.StringPropertyFnWithNamespaceFilters { return func(namespace string) string { filters := c.toFilterMap(dynamicproperties.NamespaceFilter(namespace)) val, err := c.client.GetStringValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultString() } return val } } // GetBoolPropertyFilteredByDomain gets property with domain filter and asserts that it's a bool func (c *Collection) GetBoolPropertyFilteredByDomain(key dynamicproperties.BoolKey) dynamicproperties.BoolPropertyFnWithDomainFilter { return func(domain string) bool { filters := c.toFilterMap(dynamicproperties.DomainFilter(domain)) val, err := c.client.GetBoolValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultBool() } return val } } // GetBoolPropertyFilteredByDomainID gets property with domainID filter and asserts that it's a bool func (c *Collection) GetBoolPropertyFilteredByDomainID(key dynamicproperties.BoolKey) dynamicproperties.BoolPropertyFnWithDomainIDFilter { return func(domainID string) bool { filters := c.toFilterMap(dynamicproperties.DomainIDFilter(domainID)) val, err := c.client.GetBoolValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultBool() } return val } } // GetBoolPropertyFilteredByDomainIDAndWorkflowID gets property with domainID and workflowID filters and asserts that it's a bool func (c *Collection) GetBoolPropertyFilteredByDomainIDAndWorkflowID(key dynamicproperties.BoolKey) dynamicproperties.BoolPropertyFnWithDomainIDAndWorkflowIDFilter { return func(domainID string, workflowID string) bool { filters := c.toFilterMap(dynamicproperties.DomainIDFilter(domainID), dynamicproperties.WorkflowIDFilter(workflowID)) val, err := c.client.GetBoolValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultBool() } return val } } // GetBoolPropertyFilteredByTaskListInfo gets property with taskListInfo as filters and asserts that it's an bool func (c *Collection) GetBoolPropertyFilteredByTaskListInfo(key dynamicproperties.BoolKey) dynamicproperties.BoolPropertyFnWithTaskListInfoFilters { return func(domain string, taskList string, taskType int) bool { filters := c.toFilterMap( dynamicproperties.DomainFilter(domain), dynamicproperties.TaskListFilter(taskList), dynamicproperties.TaskTypeFilter(taskType), ) val, err := c.client.GetBoolValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultBool() } return val } } func (c *Collection) GetListProperty(key dynamicproperties.ListKey) dynamicproperties.ListPropertyFn { return func(opts ...dynamicproperties.FilterOption) []interface{} { filters := c.toFilterMap(opts...) val, err := c.client.GetListValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultList() } return val } } func (c *Collection) GetStringPropertyFilteredByRatelimitKey(key dynamicproperties.StringKey) dynamicproperties.StringPropertyWithRatelimitKeyFilter { return func(ratelimitKey string) string { filters := c.toFilterMap(dynamicproperties.RatelimitKeyFilter(ratelimitKey)) val, err := c.client.GetStringValue( key, filters, ) if err != nil { c.logError(key, filters, err) return key.DefaultString() } return val } } func (c *Collection) toFilterMap(opts ...dynamicproperties.FilterOption) map[dynamicproperties.Filter]interface{} { l := len(opts) m := make(map[dynamicproperties.Filter]interface{}, l) for _, opt := range opts { opt(m) } for _, opt := range c.filterOptions { opt(m) } return m } func getFilteredKeyAsString( key dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}, ) string { var sb strings.Builder sb.WriteString(key.String()) for filter := dynamicproperties.UnknownFilter + 1; filter < dynamicproperties.LastFilterTypeForTest; filter++ { if value, ok := filters[filter]; ok { sb.WriteString(fmt.Sprintf(",%v:%v", filter.String(), value)) } } return sb.String() } ================================================ FILE: common/dynamicconfig/config_benchmark_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamicconfig import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" ) func BenchmarkGetIntProperty(b *testing.B) { client := NewInMemoryClient() cln := NewCollection(client, log.NewNoop()) key := dynamicproperties.MatchingMaxTaskBatchSize for i := 0; i < b.N; i++ { size := cln.GetIntProperty(key) assert.Equal(b, 100, size()) } } ================================================ FILE: common/dynamicconfig/config_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamicconfig import ( "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" ) type configSuite struct { suite.Suite client *inMemoryClient cln *Collection } func TestConfigSuite(t *testing.T) { s := new(configSuite) suite.Run(t, s) } func (s *configSuite) SetupTest() { s.client = NewInMemoryClient().(*inMemoryClient) logger := log.NewNoop() s.cln = NewCollection(s.client, logger) } func (s *configSuite) TestGetProperty() { key := dynamicproperties.TestGetStringPropertyKey value := s.cln.GetProperty(key) s.Equal(key.DefaultValue(), value()) s.client.SetValue(key, "b") s.Equal("b", value()) } func (s *configSuite) TestGetStringProperty() { key := dynamicproperties.TestGetStringPropertyKey value := s.cln.GetStringProperty(key) s.Equal(key.DefaultValue(), value()) s.client.SetValue(key, "b") s.Equal("b", value()) } func (s *configSuite) TestGetIntProperty() { key := dynamicproperties.TestGetIntPropertyKey value := s.cln.GetIntProperty(key) s.Equal(key.DefaultInt(), value()) s.client.SetValue(key, 50) s.Equal(50, value()) } func (s *configSuite) TestGetIntPropertyFilteredByDomain() { key := dynamicproperties.TestGetIntPropertyFilteredByDomainKey domain := "testDomain" value := s.cln.GetIntPropertyFilteredByDomain(key) s.Equal(key.DefaultInt(), value(domain)) s.client.SetValue(key, 50) s.Equal(50, value(domain)) } func (s *configSuite) TestGetIntPropertyFilteredByWorkflowType() { key := dynamicproperties.TestGetIntPropertyFilteredByWorkflowTypeKey domain := "testDomain" workflowType := "testWorkflowType" value := s.cln.GetIntPropertyFilteredByWorkflowType(key) s.Equal(key.DefaultInt(), value(domain, workflowType)) s.client.SetValue(key, 50) s.Equal(50, value(domain, workflowType)) } func (s *configSuite) TestGetIntPropertyFilteredByShardID() { key := dynamicproperties.TestGetIntPropertyFilteredByShardIDKey shardID := 1 value := s.cln.GetIntPropertyFilteredByShardID(key) s.Equal(key.DefaultInt(), value(shardID)) s.client.SetValue(key, 10) s.Equal(10, value(shardID)) } func (s *configSuite) TestGetStringPropertyFnWithDomainFilter() { key := dynamicproperties.DefaultEventEncoding domain := "testDomain" value := s.cln.GetStringPropertyFilteredByDomain(key) s.Equal(key.DefaultString(), value(domain)) s.client.SetValue(key, "efg") s.Equal("efg", value(domain)) } func (s *configSuite) TestGetStringPropertyFnByTaskListInfo() { key := dynamicproperties.TasklistLoadBalancerStrategy domain := "testDomain" taskList := "testTaskList" taskType := 0 value := s.cln.GetStringPropertyFilteredByTaskListInfo(key) s.Equal(key.DefaultString(), value(domain, taskList, taskType)) s.client.SetValue(key, "round-robin") s.Equal("round-robin", value(domain, taskList, taskType)) } func (s *configSuite) TestGetStringPropertyFnWithNamespaceFilter() { key := dynamicproperties.ShardDistributorMigrationMode namespace := "testService" value := s.cln.GetStringPropertyFilteredByNamespace(key) s.Equal(key.DefaultString(), value(namespace)) s.client.SetValue(key, "efg") s.Equal("efg", value(namespace)) } func (s *configSuite) TestGetStringPropertyFilteredByRatelimitKey() { key := dynamicproperties.FrontendGlobalRatelimiterMode ratelimitKey := "user:testDomain" value := s.cln.GetStringPropertyFilteredByRatelimitKey(key) s.Equal(key.DefaultString(), value(ratelimitKey)) s.client.SetValue(key, "fake-mode") s.Equal("fake-mode", value(ratelimitKey)) } func (s *configSuite) TestGetIntPropertyFilteredByTaskListInfo() { key := dynamicproperties.TestGetIntPropertyFilteredByTaskListInfoKey domain := "testDomain" taskList := "testTaskList" taskType := 0 value := s.cln.GetIntPropertyFilteredByTaskListInfo(key) s.Equal(key.DefaultInt(), value(domain, taskList, taskType)) s.client.SetValue(key, 50) s.Equal(50, value(domain, taskList, taskType)) } func (s *configSuite) TestGetFloat64Property() { key := dynamicproperties.TestGetFloat64PropertyKey value := s.cln.GetFloat64Property(key) s.Equal(key.DefaultFloat(), value()) s.client.SetValue(key, 0.01) s.Equal(0.01, value()) } func (s *configSuite) TestGetFloat64PropertyFilteredByShardID() { key := dynamicproperties.TestGetFloat64PropertyFilteredByShardIDKey shardID := 1 value := s.cln.GetFloat64PropertyFilteredByShardID(key) s.Equal(key.DefaultFloat(), value(shardID)) s.client.SetValue(key, 0.01) s.Equal(0.01, value(shardID)) } func (s *configSuite) TestGetBoolProperty() { key := dynamicproperties.TestGetBoolPropertyKey value := s.cln.GetBoolProperty(key) s.Equal(key.DefaultBool(), value()) s.client.SetValue(key, false) s.Equal(false, value()) } func (s *configSuite) TestGetBoolPropertyFilteredByDomainID() { key := dynamicproperties.TestGetBoolPropertyFilteredByDomainIDKey domainID := "testDomainID" value := s.cln.GetBoolPropertyFilteredByDomainID(key) s.Equal(key.DefaultBool(), value(domainID)) s.client.SetValue(key, false) s.Equal(false, value(domainID)) } func (s *configSuite) TestGetBoolPropertyFilteredByDomain() { key := dynamicproperties.TestGetBoolPropertyFilteredByDomainKey domain := "testDomain" value := s.cln.GetBoolPropertyFilteredByDomain(key) s.Equal(key.DefaultBool(), value(domain)) s.client.SetValue(key, true) s.Equal(true, value(domain)) } func (s *configSuite) TestGetBoolPropertyFilteredByDomainIDAndWorkflowID() { key := dynamicproperties.TestGetBoolPropertyFilteredByDomainIDAndWorkflowIDKey domainID := "testDomainID" workflowID := "testWorkflowID" value := s.cln.GetBoolPropertyFilteredByDomainIDAndWorkflowID(key) s.Equal(key.DefaultBool(), value(domainID, workflowID)) s.client.SetValue(key, true) s.Equal(true, value(domainID, workflowID)) } func (s *configSuite) TestGetBoolPropertyFilteredByTaskListInfo() { key := dynamicproperties.TestGetBoolPropertyFilteredByTaskListInfoKey domain := "testDomain" taskList := "testTaskList" taskType := 0 value := s.cln.GetBoolPropertyFilteredByTaskListInfo(key) s.Equal(key.DefaultBool(), value(domain, taskList, taskType)) s.client.SetValue(key, true) s.Equal(true, value(domain, taskList, taskType)) } func (s *configSuite) TestGetBoolPropertyFilteredByShardID() { key := dynamicproperties.TestGetBoolPropertyFilteredByShardIDKey shardID := 1 value := s.cln.GetBoolPropertyFilteredByShardID(key) s.Equal(key.DefaultBool(), value(shardID)) s.client.SetValue(key, true) s.Equal(true, value(shardID)) } func (s *configSuite) TestGetDurationProperty() { key := dynamicproperties.TestGetDurationPropertyKey value := s.cln.GetDurationProperty(key) s.Equal(key.DefaultDuration(), value()) s.client.SetValue(key, time.Minute) s.Equal(time.Minute, value()) } func (s *configSuite) TestGetDurationPropertyFilteredByDomain() { key := dynamicproperties.TestGetDurationPropertyFilteredByDomainKey domain := "testDomain" value := s.cln.GetDurationPropertyFilteredByDomain(key) s.Equal(key.DefaultDuration(), value(domain)) s.client.SetValue(key, time.Minute) s.Equal(time.Minute, value(domain)) } func (s *configSuite) TestGetDurationPropertyFilteredByDomainID() { key := dynamicproperties.TestGetDurationPropertyFilteredByDomainIDKey domain := "testDomainID" value := s.cln.GetDurationPropertyFilteredByDomainID(key) s.Equal(key.DefaultDuration(), value(domain)) s.client.SetValue(key, time.Minute) s.Equal(time.Minute, value(domain)) } func (s *configSuite) TestGetDurationPropertyFilteredByShardID() { key := dynamicproperties.TestGetDurationPropertyFilteredByShardID shardID := 1 value := s.cln.GetDurationPropertyFilteredByShardID(key) s.Equal(key.DefaultDuration(), value(shardID)) s.client.SetValue(key, time.Minute) s.Equal(time.Minute, value(shardID)) } func (s *configSuite) TestGetDurationPropertyFilteredByTaskListInfo() { key := dynamicproperties.TestGetDurationPropertyFilteredByTaskListInfoKey domain := "testDomain" taskList := "testTaskList" taskType := 0 value := s.cln.GetDurationPropertyFilteredByTaskListInfo(key) s.Equal(key.DefaultDuration(), value(domain, taskList, taskType)) s.client.SetValue(key, time.Minute) s.Equal(time.Minute, value(domain, taskList, taskType)) } func (s *configSuite) TestGetDurationPropertyFilteredByWorkflowType() { key := dynamicproperties.TestGetDurationPropertyFilteredByWorkflowTypeKey domain := "testDomain" workflowType := "testWorkflowType" value := s.cln.GetDurationPropertyFilteredByWorkflowType(key) s.Equal(key.DefaultDuration(), value(domain, workflowType)) s.client.SetValue(key, time.Minute) s.Equal(time.Minute, value(domain, workflowType)) } func (s *configSuite) TestGetMapProperty() { key := dynamicproperties.TestGetMapPropertyKey val := map[string]interface{}{ "testKey": 123, } value := s.cln.GetMapProperty(key) s.Equal(key.DefaultMap(), value()) val["testKey"] = "321" s.client.SetValue(key, val) s.Equal(val, value()) s.Equal("321", value()["testKey"]) } func (s *configSuite) TestGetMapPropertyFilteredByDomain() { key := dynamicproperties.TestGetMapPropertyKey domain := "testDomain" val := map[string]interface{}{ "testKey": 123, } value := s.cln.GetMapPropertyFilteredByDomain(key) s.Equal(key.DefaultMap(), value(domain)) val["testKey"] = "321" s.client.SetValue(key, val) s.Equal(val, value(domain)) s.Equal("321", value(domain)["testKey"]) } func (s *configSuite) TestGetListProperty() { key := dynamicproperties.TestGetListPropertyKey arr := []interface{}{} value := s.cln.GetListProperty(key) s.Equal(key.DefaultList(), value()) arr = append(arr, 1) s.client.SetValue(key, arr) s.Equal(1, len(value())) s.Equal(1, value()[0]) } func (s *configSuite) TestUpdateConfig() { key := dynamicproperties.TestGetBoolPropertyKey value := s.cln.GetBoolProperty(key) err := s.client.UpdateValue(key, false) s.NoError(err) s.Equal(false, value()) err = s.client.UpdateValue(key, true) s.NoError(err) s.Equal(true, value()) } func TestDynamicConfigKeyIsMapped(t *testing.T) { for i := dynamicproperties.UnknownIntKey + 1; i < dynamicproperties.LastIntKey; i++ { key, ok := dynamicproperties.IntKeys[i] require.True(t, ok, "missing IntKey: %d", i) require.NotEmpty(t, key, "empty IntKey: %d", i) } for i := dynamicproperties.UnknownBoolKey + 1; i < dynamicproperties.LastBoolKey; i++ { key, ok := dynamicproperties.BoolKeys[i] require.True(t, ok, "missing BoolKey: %d", i) require.NotEmpty(t, key, "empty BoolKey: %d", i) } for i := dynamicproperties.UnknownFloatKey + 1; i < dynamicproperties.LastFloatKey; i++ { key, ok := dynamicproperties.FloatKeys[i] require.True(t, ok, "missing FloatKey: %d", i) require.NotEmpty(t, key, "empty FloatKey: %d", i) } for i := dynamicproperties.UnknownStringKey + 1; i < dynamicproperties.LastStringKey; i++ { key, ok := dynamicproperties.StringKeys[i] require.True(t, ok, "missing StringKey: %d", i) require.NotEmpty(t, key, "empty StringKey: %d", i) } for i := dynamicproperties.UnknownDurationKey + 1; i < dynamicproperties.LastDurationKey; i++ { key, ok := dynamicproperties.DurationKeys[i] require.True(t, ok, "missing DurationKey: %d", i) require.NotEmpty(t, key, "empty DurationKey: %d", i) } for i := dynamicproperties.UnknownMapKey + 1; i < dynamicproperties.LastMapKey; i++ { key, ok := dynamicproperties.MapKeys[i] require.True(t, ok, "missing MapKey: %d", i) require.NotEmpty(t, key, "empty MapKey: %d", i) } } ================================================ FILE: common/dynamicconfig/configstore/config/config.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import "time" // This package is necessary to avoid import cycle as config_store_client imports common/config // while common/config imports this ClientConfig definition // ClientConfig is the config for the config store based dynamic config client. // It specifies how often the cached config should be updated by checking underlying database. type ClientConfig struct { PollInterval time.Duration `yaml:"pollInterval"` UpdateRetryAttempts int `yaml:"updateRetryAttempts"` FetchTimeout time.Duration `yaml:"FetchTimeout"` UpdateTimeout time.Duration `yaml:"UpdateTimeout"` } ================================================ FILE: common/dynamicconfig/configstore/config_store_client.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package configstore import ( "context" "encoding/json" "errors" "fmt" "math" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" dc "github.com/uber/cadence/common/dynamicconfig" csc "github.com/uber/cadence/common/dynamicconfig/configstore/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql" "github.com/uber/cadence/common/persistence/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination configstore_mock.go -self_package github.com/uber/cadence/common/dynamicconfig/configstore var _ dc.Client = (*configStoreClient)(nil) // Client is a stateful config store type Client interface { common.Daemon dc.Client } const ( configStoreMinPollInterval = time.Second * 2 ) var defaultConfigValues = &csc.ClientConfig{ PollInterval: time.Second * 10, UpdateRetryAttempts: 1, FetchTimeout: 2, UpdateTimeout: 2, } type configStoreClient struct { status int32 configStoreType persistence.ConfigType values atomic.Value lastUpdatedTime time.Time config *csc.ClientConfig configStoreManager persistence.ConfigStoreManager doneCh chan struct{} logger log.Logger } type cacheEntry struct { cacheVersion int64 schemaVersion int64 dcEntries map[string]*types.DynamicConfigEntry } // NewConfigStoreClient creates a config store client func NewConfigStoreClient( clientCfg *csc.ClientConfig, persistenceCfg *config.Persistence, logger log.Logger, metricsClient metrics.Client, configType persistence.ConfigType, ) (Client, error) { if persistenceCfg == nil { return nil, errors.New("persistence cfg is nil") } ds, ok := persistenceCfg.DataStores[persistenceCfg.DefaultStore] if !ok { return nil, errors.New("default persistence config missing") } if err := validateClientConfig(clientCfg); err != nil { logger.Warn("invalid ClientConfig values, using default values") clientCfg = defaultConfigValues } client, err := newConfigStoreClient(clientCfg, &ds, logger, metricsClient, configType) if err != nil { return nil, err } err = client.startUpdate() if err != nil { return nil, err } return client, nil } func newConfigStoreClient( clientCfg *csc.ClientConfig, ds *config.DataStore, logger log.Logger, metricsClient metrics.Client, configType persistence.ConfigType, ) (*configStoreClient, error) { var store persistence.ConfigStore var err error switch { case ds.ShardedNoSQL != nil: store, err = nosql.NewNoSQLConfigStore(*ds.ShardedNoSQL, logger, metricsClient, nil) case ds.NoSQL != nil: store, err = nosql.NewNoSQLConfigStore(*ds.NoSQL.ConvertToShardedNoSQLConfig(), logger, metricsClient, nil) case ds.SQL != nil: var db sqlplugin.DB db, err = sql.NewSQLDB(ds.SQL) if err != nil { return nil, err } store, err = sql.NewSQLConfigStore(db, logger, nil) default: return nil, errors.New("both NoSQL and SQL store are not provided") } if err != nil { return nil, err } doneCh := make(chan struct{}) client := &configStoreClient{ status: common.DaemonStatusStarted, config: clientCfg, doneCh: doneCh, configStoreManager: persistence.NewConfigStoreManagerImpl(store, logger), logger: logger, configStoreType: configType, } return client, nil } func (csc *configStoreClient) startUpdate() error { if err := csc.update(); err != nil { return err } go func() { ticker := time.NewTicker(csc.config.PollInterval) for { select { case <-ticker.C: err := csc.update() if err != nil { csc.logger.Error("Failed to update dynamic config", tag.Error(err)) } case <-csc.doneCh: ticker.Stop() return } } }() return nil } func (csc *configStoreClient) GetValue(name dynamicproperties.Key) (interface{}, error) { return csc.getValueWithFilters(name, nil, name.DefaultValue()) } func (csc *configStoreClient) GetValueWithFilters(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) (interface{}, error) { return csc.getValueWithFilters(name, filters, name.DefaultValue()) } func (csc *configStoreClient) GetIntValue(name dynamicproperties.IntKey, filters map[dynamicproperties.Filter]interface{}) (int, error) { defaultValue := name.DefaultInt() val, err := csc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } floatVal, ok := val.(float64) if !ok { return defaultValue, errors.New("value type is not int") } if floatVal != math.Trunc(floatVal) { return defaultValue, errors.New("value type is not int") } return int(floatVal), nil } func (csc *configStoreClient) GetFloatValue(name dynamicproperties.FloatKey, filters map[dynamicproperties.Filter]interface{}) (float64, error) { defaultValue := name.DefaultFloat() val, err := csc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if floatVal, ok := val.(float64); ok { return floatVal, nil } return defaultValue, errors.New("value type is not float64") } func (csc *configStoreClient) GetBoolValue(name dynamicproperties.BoolKey, filters map[dynamicproperties.Filter]interface{}) (bool, error) { defaultValue := name.DefaultBool() val, err := csc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if boolVal, ok := val.(bool); ok { return boolVal, nil } return defaultValue, errors.New("value type is not bool") } func (csc *configStoreClient) GetStringValue(name dynamicproperties.StringKey, filters map[dynamicproperties.Filter]interface{}) (string, error) { defaultValue := name.DefaultString() val, err := csc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if stringVal, ok := val.(string); ok { return stringVal, nil } return defaultValue, errors.New("value type is not string") } // Note that all number types (ex: ints) will be returned as float64. // It is the caller's responsibility to convert based on their context for value type. func (csc *configStoreClient) GetMapValue(name dynamicproperties.MapKey, filters map[dynamicproperties.Filter]interface{}) (map[string]interface{}, error) { defaultValue := name.DefaultMap() val, err := csc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if mapVal, ok := val.(map[string]interface{}); ok { return mapVal, nil } return defaultValue, errors.New("value type is not map") } func (csc *configStoreClient) GetDurationValue(name dynamicproperties.DurationKey, filters map[dynamicproperties.Filter]interface{}) (time.Duration, error) { defaultValue := name.DefaultDuration() val, err := csc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } var durVal time.Duration switch v := val.(type) { case string: durVal, err = time.ParseDuration(v) if err != nil { return defaultValue, errors.New("value string encoding cannot be parsed into duration") } case time.Duration: durVal = v default: return defaultValue, errors.New("value type is not duration") } return durVal, nil } func (csc *configStoreClient) GetListValue(name dynamicproperties.ListKey, filters map[dynamicproperties.Filter]interface{}) ([]interface{}, error) { defaultValue := name.DefaultList() val, err := csc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if listVal, ok := val.([]interface{}); ok { return listVal, nil } return defaultValue, errors.New("value type is not list") } func (csc *configStoreClient) UpdateValue(name dynamicproperties.Key, value interface{}) error { dcValues, ok := value.([]*types.DynamicConfigValue) if !ok && value != nil { return errors.New("invalid value") } return csc.updateValue(name, dcValues, csc.config.UpdateRetryAttempts) } func (csc *configStoreClient) RestoreValue(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) error { // if empty filter provided, update fallback value. // if u want to remove entire entry, just do update value with empty loaded := csc.values.Load() if loaded == nil { return dc.NotFoundError } currentCached := loaded.(cacheEntry) if currentCached.dcEntries == nil { return dc.NotFoundError } val, ok := currentCached.dcEntries[name.String()] if !ok { return dc.NotFoundError } newValues := make([]*types.DynamicConfigValue, 0, len(val.Values)) if filters == nil { for _, dcValue := range val.Values { if dcValue.Filters != nil || len(dcValue.Filters) != 0 { newValues = append(newValues, dcValue.Copy()) } } } else { for _, dcValue := range val.Values { if !matchFilters(dcValue, filters) || dcValue.Filters == nil || len(dcValue.Filters) == 0 { newValues = append(newValues, dcValue.Copy()) } } } return csc.updateValue(name, newValues, csc.config.UpdateRetryAttempts) } func (csc *configStoreClient) ListValue(name dynamicproperties.Key) ([]*types.DynamicConfigEntry, error) { var resList []*types.DynamicConfigEntry loaded := csc.values.Load() if loaded == nil { return nil, nil } currentCached := loaded.(cacheEntry) if currentCached.dcEntries == nil { return nil, nil } listAll := false if name == nil { // if key is not specified, return all entries listAll = true } else if _, ok := currentCached.dcEntries[name.String()]; !ok { // if key is not known, return all entries listAll = true } if listAll { // if key is not known/specified, return all entries resList = make([]*types.DynamicConfigEntry, 0, len(currentCached.dcEntries)) for _, entry := range currentCached.dcEntries { resList = append(resList, entry.Copy()) } } else { // if key is known, return just that specific entry resList = make([]*types.DynamicConfigEntry, 0, 1) resList = append(resList, currentCached.dcEntries[name.String()]) } return resList, nil } func (csc *configStoreClient) Stop() { if !atomic.CompareAndSwapInt32(&csc.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(csc.doneCh) csc.configStoreManager.Close() } func (csc *configStoreClient) Start() { err := csc.startUpdate() if err != nil { csc.logger.Fatal("could not start config store", tag.Error(err)) } if !atomic.CompareAndSwapInt32(&csc.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } } func (csc *configStoreClient) updateValue(name dynamicproperties.Key, dcValues []*types.DynamicConfigValue, retryAttempts int) error { // since values are not unique, no way to know if you are trying to update a specific value // or if you want to add another of the same value with different filters. // UpdateValue will replace everything associated with dc key. for _, dcValue := range dcValues { if err := validateKeyDataBlobPair(name, dcValue.Value); err != nil { return err } } loaded := csc.values.Load() var currentCached cacheEntry if loaded == nil { currentCached = cacheEntry{ cacheVersion: 0, schemaVersion: 0, dcEntries: map[string]*types.DynamicConfigEntry{}, } } else { currentCached = loaded.(cacheEntry) } keyName := name.String() var newEntries []*types.DynamicConfigEntry existingEntry, entryExists := currentCached.dcEntries[keyName] if len(dcValues) == 0 { newEntries = make([]*types.DynamicConfigEntry, 0, len(currentCached.dcEntries)) for _, entry := range currentCached.dcEntries { if entryExists && entry == existingEntry { continue } else { newEntries = append(newEntries, entry.Copy()) } } } else { if entryExists { newEntries = make([]*types.DynamicConfigEntry, 0, len(currentCached.dcEntries)) } else { newEntries = make([]*types.DynamicConfigEntry, 0, len(currentCached.dcEntries)+1) newEntries = append(newEntries, &types.DynamicConfigEntry{ Name: keyName, Values: dcValues, }) } for _, entry := range currentCached.dcEntries { if entryExists && entry.Name == keyName { newEntries = append(newEntries, &types.DynamicConfigEntry{ Name: keyName, Values: dcValues, }) } else { newEntries = append(newEntries, entry.Copy()) } } } newSnapshot := &persistence.DynamicConfigSnapshot{ Version: currentCached.cacheVersion + 1, Values: &types.DynamicConfigBlob{ SchemaVersion: currentCached.schemaVersion, Entries: newEntries, }, } ctx, cancel := context.WithTimeout(context.Background(), csc.config.UpdateTimeout) defer cancel() err := csc.configStoreManager.UpdateDynamicConfig( ctx, &persistence.UpdateDynamicConfigRequest{ Snapshot: newSnapshot, }, csc.configStoreType, ) select { case <-ctx.Done(): // potentially we can retry on timeout return errors.New("timeout error on update") default: if err != nil { if _, ok := err.(*persistence.ConditionFailedError); ok && retryAttempts > 0 { // fetch new config and retry err := csc.update() if err != nil { return err } return csc.updateValue(name, dcValues, retryAttempts-1) } if retryAttempts == 0 { return errors.New("ran out of retry attempts on update") } return err } return nil } } func (csc *configStoreClient) update() error { ctx, cancel := context.WithTimeout(context.Background(), csc.config.FetchTimeout) defer cancel() res, err := csc.configStoreManager.FetchDynamicConfig(ctx, csc.configStoreType) select { case <-ctx.Done(): return errors.New("timeout error on fetch") default: if err != nil { return fmt.Errorf("failed to fetch dynamic config snapshot %v", err) } if res != nil && res.Snapshot != nil { defer func() { csc.lastUpdatedTime = time.Now() }() return csc.storeValues(res.Snapshot) } } return nil } func (csc *configStoreClient) storeValues(snapshot *persistence.DynamicConfigSnapshot) error { // Converting the list of dynamic config entries into a map for better lookup performance var dcEntryMap map[string]*types.DynamicConfigEntry if snapshot.Values.Entries == nil { dcEntryMap = nil } else { dcEntryMap = make(map[string]*types.DynamicConfigEntry) for _, entry := range snapshot.Values.Entries { dcEntryMap[entry.Name] = entry } } csc.values.Store(cacheEntry{ cacheVersion: snapshot.Version, schemaVersion: snapshot.Values.SchemaVersion, dcEntries: dcEntryMap, }) csc.logger.Debug("Updated dynamic config") return nil } func (csc *configStoreClient) getValueWithFilters(key dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}, defaultValue interface{}) (interface{}, error) { keyName := key.String() loaded := csc.values.Load() if loaded == nil { return defaultValue, nil } cached := loaded.(cacheEntry) found := false if entry, ok := cached.dcEntries[keyName]; ok && entry != nil { for _, dcValue := range entry.Values { if len(dcValue.Filters) == 0 { parsedVal, err := convertFromDataBlob(dcValue.Value) if err == nil { defaultValue = parsedVal found = true } continue } if matchFilters(dcValue, filters) { return convertFromDataBlob(dcValue.Value) } } } if found { return defaultValue, nil } return defaultValue, dc.NotFoundError } func matchFilters(dcValue *types.DynamicConfigValue, filters map[dynamicproperties.Filter]interface{}) bool { if len(dcValue.Filters) > len(filters) { return false } for _, valueFilter := range dcValue.Filters { filterKey := dynamicproperties.ParseFilter(valueFilter.Name) if filters[filterKey] == nil { return false } requestValue, err := convertFromDataBlob(valueFilter.Value) if err != nil || filters[filterKey] != requestValue { return false } } return true } func validateClientConfig(config *csc.ClientConfig) error { if config == nil { return errors.New("no config found for config store based dynamic config client") } if config.PollInterval < configStoreMinPollInterval { return fmt.Errorf("poll interval should be at least %v", configStoreMinPollInterval) } if config.UpdateRetryAttempts < 0 { return errors.New("UpdateRetryAttempts must be non-negative") } if config.FetchTimeout <= 0 { return errors.New("FetchTimeout must be positive") } if config.UpdateTimeout <= 0 { return errors.New("UpdateTimeout must be positive") } return nil } func convertFromDataBlob(blob *types.DataBlob) (interface{}, error) { switch *blob.EncodingType { case types.EncodingTypeJSON: var v interface{} err := json.Unmarshal(blob.Data, &v) return v, err default: return nil, errors.New("unsupported blob encoding") } } func validateKeyDataBlobPair(key dynamicproperties.Key, blob *types.DataBlob) error { value, err := convertFromDataBlob(blob) if err != nil { return err } err = fmt.Errorf("key value pair mismatch, key type: %T, value type: %T", key, value) switch key.(type) { case dynamicproperties.IntKey: if _, ok := value.(int); !ok { floatVal, ok := value.(float64) if !ok { // int can be decoded as float64 return err } if floatVal != math.Trunc(floatVal) { return errors.New("value type is not int") } } case dynamicproperties.BoolKey: if _, ok := value.(bool); !ok { return err } case dynamicproperties.FloatKey: if _, ok := value.(float64); !ok { return err } case dynamicproperties.StringKey: if _, ok := value.(string); !ok { return err } case dynamicproperties.DurationKey: if _, ok := value.(time.Duration); !ok { durationStr, ok := value.(string) if !ok { return err } if _, err = time.ParseDuration(durationStr); err != nil { return errors.New("value string encoding cannot be parsed into duration") } } case dynamicproperties.MapKey: if _, ok := value.(map[string]interface{}); !ok { return err } case dynamicproperties.ListKey: if _, ok := value.([]interface{}); !ok { return err } default: return fmt.Errorf("unknown key type: %T", key) } return nil } ================================================ FILE: common/dynamicconfig/configstore/config_store_client_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package configstore import ( "context" "encoding/json" "errors" "fmt" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" c "github.com/uber/cadence/common/dynamicconfig/configstore/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) const ( retryAttempts = 2 ) type configStoreClientSuite struct { suite.Suite *require.Assertions client *configStoreClient mockManager *p.MockConfigStoreManager mockController *gomock.Controller doneCh chan struct{} } var snapshot1 *p.DynamicConfigSnapshot func TestConfigStoreClientSuite(t *testing.T) { s := new(configStoreClientSuite) suite.Run(t, s) } func (s *configStoreClientSuite) SetupSuite() { s.doneCh = make(chan struct{}) s.mockController = gomock.NewController(s.T()) mockPlugin := nosqlplugin.NewMockPlugin(s.mockController) mockPlugin.EXPECT(). CreateDB(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, nil).AnyTimes() nosql.RegisterPlugin("cassandra", mockPlugin) } func (s *configStoreClientSuite) TearDownSuite() { close(s.doneCh) } func (s *configStoreClientSuite) SetupTest() { s.Assertions = require.New(s.T()) snapshot1 = &p.DynamicConfigSnapshot{ Version: 1, Values: &types.DynamicConfigBlob{ SchemaVersion: 1, Entries: []*types.DynamicConfigEntry{ { Name: dynamicproperties.TestGetBoolPropertyKey.String(), Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(false), }, Filters: nil, }, { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(true), }, Filters: []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("global-samples-domain"), }, }, }, }, { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(true), }, Filters: []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("samples-domain"), }, }, }, }, }, }, { Name: dynamicproperties.TestGetIntPropertyKey.String(), Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(1000), }, Filters: nil, }, { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(1000.1), }, Filters: []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("global-samples-domain"), }, }, }, }, }, }, { Name: dynamicproperties.TestGetFloat64PropertyKey.String(), Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(12), }, Filters: nil, }, { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("wrong type"), }, Filters: []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("samples-domain"), }, }, }, }, }, }, { Name: dynamicproperties.TestGetStringPropertyKey.String(), Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("some random string"), }, Filters: nil, }, { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("constrained-string"), }, Filters: []*types.DynamicConfigFilter{ { Name: "taskListName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("random tasklist"), }, }, }, }, }, }, { Name: dynamicproperties.TestGetMapPropertyKey.String(), Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(map[string]interface{}{ "key1": "1", "key2": 1, "key3": []interface{}{ false, map[string]interface{}{ "key4": true, "key5": 2.1, }, }, }), }, Filters: nil, }, { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("1"), }, Filters: []*types.DynamicConfigFilter{ { Name: "taskListName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("random tasklist"), }, }, }, }, }, }, { Name: dynamicproperties.TestGetDurationPropertyKey.String(), Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("1m"), }, Filters: nil, }, { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("wrong duration string"), }, Filters: []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("samples-domain"), }, }, { Name: "taskListName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("longIdleTimeTaskList"), }, }, }, }, { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(2), }, Filters: []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("samples-domain"), }, }, }, }, }, }, }, }, } connections := make(map[string]config.DBShardConnection) connections[config.NonShardedStoreName] = config.DBShardConnection{ NoSQLPlugin: &config.NoSQL{ PluginName: "cassandra", }, } var err error s.client, err = newConfigStoreClient( &c.ClientConfig{ PollInterval: time.Second * 2, UpdateRetryAttempts: retryAttempts, FetchTimeout: time.Second * 1, UpdateTimeout: time.Second * 1, }, &config.DataStore{ ShardedNoSQL: &config.ShardedNoSQL{ DefaultShard: config.NonShardedStoreName, Connections: connections, }, }, log.NewNoop(), metrics.NewNoopMetricsClient(), p.DynamicConfig) s.Require().NoError(err) s.mockManager = p.NewMockConfigStoreManager(s.mockController) s.client.configStoreManager = s.mockManager } func defaultTestSetup(s *configStoreClientSuite) { s.mockManager.EXPECT(). FetchDynamicConfig(gomock.Any(), p.DynamicConfig). Return(&p.FetchDynamicConfigResponse{ Snapshot: snapshot1, }, nil). AnyTimes() err := s.client.startUpdate() s.NoError(err) } func (s *configStoreClientSuite) TestGetValue() { defaultTestSetup(s) v, err := s.client.GetValue(dynamicproperties.TestGetBoolPropertyKey) s.NoError(err) s.Equal(false, v) } func (s *configStoreClientSuite) TestGetValue_NonExistKey() { defaultTestSetup(s) v, err := s.client.GetValue(dynamicproperties.MaxRetentionDays) s.Error(err) s.Equal(dynamicproperties.MaxRetentionDays.DefaultInt(), v) v, err = s.client.GetValue(dynamicproperties.EnableVisibilitySampling) s.Error(err) s.Equal(dynamicproperties.EnableVisibilitySampling.DefaultBool(), v) v, err = s.client.GetValue(dynamicproperties.FrontendErrorInjectionRate) s.Error(err) s.Equal(dynamicproperties.FrontendErrorInjectionRate.DefaultFloat(), v) v, err = s.client.GetValue(dynamicproperties.WriteVisibilityStoreName) s.Error(err) s.Equal(dynamicproperties.WriteVisibilityStoreName.DefaultString(), v) v, err = s.client.GetValue(dynamicproperties.FrontendShutdownDrainDuration) s.Error(err) s.Equal(dynamicproperties.FrontendShutdownDrainDuration.DefaultDuration(), v) v, err = s.client.GetValue(dynamicproperties.RequiredDomainDataKeys) s.Error(err) s.Equal(dynamicproperties.RequiredDomainDataKeys.DefaultMap(), v) } func (s *configStoreClientSuite) TestGetValueWithFilters() { defaultTestSetup(s) filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "global-samples-domain", } v, err := s.client.GetValueWithFilters(dynamicproperties.TestGetBoolPropertyKey, filters) s.NoError(err) s.Equal(true, v) filters = map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "non-exist-domain", } v, err = s.client.GetValueWithFilters(dynamicproperties.TestGetBoolPropertyKey, filters) s.NoError(err) s.Equal(false, v) filters = map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", dynamicproperties.TaskListName: "non-exist-tasklist", } v, err = s.client.GetValueWithFilters(dynamicproperties.TestGetBoolPropertyKey, filters) s.NoError(err) s.Equal(true, v) } func (s *configStoreClientSuite) TestGetValueWithFilters_UnknownFilter() { defaultTestSetup(s) filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "global-samples-domain1", dynamicproperties.UnknownFilter: "unknown-filter1", } v, err := s.client.GetValueWithFilters(dynamicproperties.TestGetBoolPropertyKey, filters) s.NoError(err) s.Equal(false, v) } func (s *configStoreClientSuite) TestGetIntValue() { defaultTestSetup(s) v, err := s.client.GetIntValue(dynamicproperties.TestGetIntPropertyKey, nil) s.NoError(err) s.Equal(1000, v) } func (s *configStoreClientSuite) TestGetIntValue_FilterNotMatch() { defaultTestSetup(s) filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", } v, err := s.client.GetIntValue(dynamicproperties.TestGetIntPropertyKey, filters) s.NoError(err) s.Equal(1000, v) } func (s *configStoreClientSuite) TestGetIntValue_WrongType() { defaultTestSetup(s) filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "global-samples-domain", } v, err := s.client.GetIntValue(dynamicproperties.TestGetIntPropertyKey, filters) s.Error(err) s.Equal(dynamicproperties.TestGetIntPropertyKey.DefaultInt(), v) } func (s *configStoreClientSuite) TestGetFloatValue() { defaultTestSetup(s) v, err := s.client.GetFloatValue(dynamicproperties.TestGetFloat64PropertyKey, nil) s.NoError(err) s.Equal(12.0, v) } func (s *configStoreClientSuite) TestGetFloatValue_WrongType() { defaultTestSetup(s) filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", } v, err := s.client.GetFloatValue(dynamicproperties.TestGetFloat64PropertyKey, filters) s.Error(err) s.Equal(dynamicproperties.TestGetFloat64PropertyKey.DefaultFloat(), v) } func (s *configStoreClientSuite) TestGetBoolValue() { defaultTestSetup(s) v, err := s.client.GetBoolValue(dynamicproperties.TestGetBoolPropertyKey, nil) s.NoError(err) s.Equal(false, v) } func (s *configStoreClientSuite) TestGetStringValue() { defaultTestSetup(s) filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.TaskListName: "random tasklist", } v, err := s.client.GetStringValue(dynamicproperties.TestGetStringPropertyKey, filters) s.NoError(err) s.Equal("constrained-string", v) } func (s *configStoreClientSuite) TestGetMapValue() { defaultTestSetup(s) v, err := s.client.GetMapValue(dynamicproperties.TestGetMapPropertyKey, nil) s.NoError(err) expectedVal := map[string]interface{}{ "key1": "1", "key2": float64(1), "key3": []interface{}{ false, map[string]interface{}{ "key4": true, "key5": 2.1, }, }, } s.Equal(expectedVal, v) } func (s *configStoreClientSuite) TestGetMapValue_WrongType() { defaultTestSetup(s) filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.TaskListName: "random tasklist", } v, err := s.client.GetMapValue(dynamicproperties.TestGetMapPropertyKey, filters) s.Error(err) s.Equal(dynamicproperties.TestGetMapPropertyKey.DefaultMap(), v) } func (s *configStoreClientSuite) TestGetDurationValue() { defaultTestSetup(s) v, err := s.client.GetDurationValue(dynamicproperties.TestGetDurationPropertyKey, nil) s.NoError(err) s.Equal(time.Minute, v) } func (s *configStoreClientSuite) TestGetDurationValue_NotStringRepresentation() { defaultTestSetup(s) filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", } v, err := s.client.GetDurationValue(dynamicproperties.TestGetDurationPropertyKey, filters) s.Error(err) s.Equal(dynamicproperties.TestGetDurationPropertyKey.DefaultDuration(), v) } func (s *configStoreClientSuite) TestGetDurationValue_ParseFailed() { defaultTestSetup(s) filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", dynamicproperties.TaskListName: "longIdleTimeTaskList", } v, err := s.client.GetDurationValue(dynamicproperties.TestGetDurationPropertyKey, filters) s.Error(err) s.Equal(dynamicproperties.TestGetDurationPropertyKey.DefaultDuration(), v) } func (s *configStoreClientSuite) TestValidateConfig_InvalidConfig() { err := validateClientConfig( &c.ClientConfig{ PollInterval: time.Second * 1, UpdateRetryAttempts: 0, FetchTimeout: time.Second * 3, UpdateTimeout: time.Second * 4, }, ) s.Error(err) err = validateClientConfig( &c.ClientConfig{ PollInterval: time.Second * 2, UpdateRetryAttempts: -1, FetchTimeout: time.Second * 2, UpdateTimeout: time.Second * 2, }, ) s.Error(err) err = validateClientConfig( &c.ClientConfig{ PollInterval: time.Second * 2, UpdateRetryAttempts: 0, FetchTimeout: time.Second * 0, UpdateTimeout: time.Second * 0, }, ) s.Error(err) err = validateClientConfig( &c.ClientConfig{ PollInterval: time.Second * 2, UpdateRetryAttempts: 1, FetchTimeout: time.Second * 1, UpdateTimeout: time.Second * 0, }, ) s.Error(err) } func (s *configStoreClientSuite) TestMatchFilters() { testCases := []struct { v *types.DynamicConfigValue filters map[dynamicproperties.Filter]interface{} matched bool }{ { v: &types.DynamicConfigValue{ Value: nil, Filters: nil, }, filters: map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "some random domain", }, matched: true, }, { v: &types.DynamicConfigValue{ Value: nil, Filters: []*types.DynamicConfigFilter{ { Name: "some key", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("some value"), }, }, }, }, filters: map[dynamicproperties.Filter]interface{}{}, matched: false, }, { v: &types.DynamicConfigValue{ Value: nil, Filters: []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("samples-domain"), }, }, }, }, filters: map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "some random domain", }, matched: false, }, { v: &types.DynamicConfigValue{ Value: nil, Filters: []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("samples-domain"), }, }, { Name: "taskListName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("sample-task-list"), }, }, }, }, filters: map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", dynamicproperties.TaskListName: "sample-task-list", }, matched: true, }, { v: &types.DynamicConfigValue{ Value: nil, Filters: []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("samples-domain"), }, }, { Name: "some-other-filter", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("sample-task-list"), }, }, }, }, filters: map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", dynamicproperties.TaskListName: "sample-task-list", }, matched: false, }, { v: &types.DynamicConfigValue{ Value: nil, Filters: []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("samples-domain"), }, }, }, }, filters: map[dynamicproperties.Filter]interface{}{ dynamicproperties.TaskListName: "sample-task-list", }, matched: false, }, } for index, tc := range testCases { matched := matchFilters(tc.v, tc.filters) s.Equal(tc.matched, matched, fmt.Sprintf("Test case %v failed", index)) } } func (s *configStoreClientSuite) TestUpdateValue_NilOverwrite() { defaultTestSetup(s) s.mockManager.EXPECT(). UpdateDynamicConfig(gomock.Any(), gomock.Any(), p.DynamicConfig). DoAndReturn(func(_ context.Context, request *p.UpdateDynamicConfigRequest, cfgType p.ConfigType) error { if request.Snapshot.Values.Entries[0].Name != dynamicproperties.TestGetBoolPropertyKey.String() { return nil } return errors.New("entry not removed") }).AnyTimes() err := s.client.UpdateValue(dynamicproperties.TestGetBoolPropertyKey, nil) s.NoError(err) } func (s *configStoreClientSuite) TestUpdateValue_NoRetrySuccess() { defaultTestSetup(s) s.mockManager.EXPECT(). UpdateDynamicConfig(gomock.Any(), EqSnapshotVersion(2), p.DynamicConfig). Return(nil).MaxTimes(1) values := []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(true), }, Filters: nil, }, } err := s.client.UpdateValue(dynamicproperties.TestGetBoolPropertyKey, values) s.NoError(err) snapshot2 := snapshot1 snapshot2.Values.Entries[0].Values = values s.mockManager.EXPECT(). FetchDynamicConfig(gomock.Any(), p.DynamicConfig). Return(&p.FetchDynamicConfigResponse{ Snapshot: snapshot2, }, nil).MaxTimes(1) err = s.client.update() s.NoError(err) v, err := s.client.GetValue(dynamicproperties.TestGetBoolPropertyKey) s.NoError(err) s.Equal(true, v) } func (s *configStoreClientSuite) TestUpdateValue_SuccessNewKey() { values := []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(true), }, Filters: nil, }, } s.mockManager.EXPECT(). FetchDynamicConfig(gomock.Any(), p.DynamicConfig). Return(&p.FetchDynamicConfigResponse{ Snapshot: &p.DynamicConfigSnapshot{ Version: 1, Values: &types.DynamicConfigBlob{ SchemaVersion: 1, Entries: nil, }, }, }, nil). AnyTimes() s.mockManager.EXPECT(). UpdateDynamicConfig(gomock.Any(), gomock.Any(), p.DynamicConfig). DoAndReturn(func(_ context.Context, request *p.UpdateDynamicConfigRequest, cfgType p.ConfigType) error { s.Equal(1, len(request.Snapshot.Values.Entries)) s.Equal(request.Snapshot.Values.Entries[0].Values, values) return nil }).AnyTimes() s.client.update() err := s.client.UpdateValue(dynamicproperties.TestGetBoolPropertyKey, values) s.NoError(err) } func (s *configStoreClientSuite) TestUpdateValue_RetrySuccess() { s.mockManager.EXPECT(). UpdateDynamicConfig(gomock.Any(), EqSnapshotVersion(2), p.DynamicConfig). Return(&p.ConditionFailedError{}).AnyTimes() s.mockManager.EXPECT(). UpdateDynamicConfig(gomock.Any(), EqSnapshotVersion(3), p.DynamicConfig). Return(nil).AnyTimes() snapshot1.Version = 2 s.mockManager.EXPECT(). FetchDynamicConfig(gomock.Any(), p.DynamicConfig). Return(&p.FetchDynamicConfigResponse{ Snapshot: snapshot1, }, nil).AnyTimes() s.client.update() err := s.client.UpdateValue(dynamicproperties.TestGetBoolPropertyKey, []*types.DynamicConfigValue{}) s.NoError(err) } func (s *configStoreClientSuite) TestUpdateValue_RetryFailure() { defaultTestSetup(s) s.mockManager.EXPECT(). UpdateDynamicConfig(gomock.Any(), gomock.Any(), p.DynamicConfig). Return(&p.ConditionFailedError{}).MaxTimes(retryAttempts + 1) err := s.client.UpdateValue(dynamicproperties.TestGetFloat64PropertyKey, []*types.DynamicConfigValue{}) s.Error(err) } func (s *configStoreClientSuite) TestUpdateValue_Timeout() { defaultTestSetup(s) s.mockManager.EXPECT(). UpdateDynamicConfig(gomock.Any(), gomock.Any(), p.DynamicConfig). DoAndReturn(func(_ context.Context, _ *p.UpdateDynamicConfigRequest, cfgType p.ConfigType) error { time.Sleep(2 * time.Second) return nil }).AnyTimes() err := s.client.UpdateValue(dynamicproperties.TestGetDurationPropertyKey, []*types.DynamicConfigValue{}) s.Error(err) } func (s *configStoreClientSuite) TestRestoreValue_NoFilter() { defaultTestSetup(s) s.mockManager.EXPECT(). UpdateDynamicConfig(gomock.Any(), gomock.Any(), p.DynamicConfig). DoAndReturn(func(_ context.Context, request *p.UpdateDynamicConfigRequest, cfgType p.ConfigType) error { for _, entry := range request.Snapshot.Values.Entries { if entry.Name == dynamicproperties.TestGetBoolPropertyKey.String() { for _, value := range entry.Values { s.Equal(value.Value.Data, jsonMarshalHelper(true)) if value.Filters == nil { return errors.New("fallback value not restored") } } } } return nil }).AnyTimes() err := s.client.RestoreValue(dynamicproperties.TestGetBoolPropertyKey, nil) s.NoError(err) } func (s *configStoreClientSuite) TestRestoreValue_FilterNoMatch() { defaultTestSetup(s) s.mockManager.EXPECT(). UpdateDynamicConfig(gomock.Any(), gomock.Any(), p.DynamicConfig). DoAndReturn(func(_ context.Context, request *p.UpdateDynamicConfigRequest, cfgType p.ConfigType) error { for _, resEntry := range request.Snapshot.Values.Entries { for _, oriEntry := range snapshot1.Values.Entries { if oriEntry.Name == resEntry.Name { s.Equal(resEntry.Values, oriEntry.Values) } } } return nil }).AnyTimes() noMatchFilter := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "unknown-domain", } err := s.client.RestoreValue(dynamicproperties.TestGetBoolPropertyKey, noMatchFilter) s.NoError(err) } func (s *configStoreClientSuite) TestRestoreValue_FilterMatch() { defaultTestSetup(s) s.mockManager.EXPECT(). UpdateDynamicConfig(gomock.Any(), gomock.Any(), p.DynamicConfig). DoAndReturn(func(_ context.Context, request *p.UpdateDynamicConfigRequest, cfgType p.ConfigType) error { for _, resEntry := range request.Snapshot.Values.Entries { if resEntry.Name == dynamicproperties.TestGetBoolPropertyKey.String() { s.Equal(2, len(resEntry.Values)) } } return nil }).AnyTimes() filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", } err := s.client.RestoreValue(dynamicproperties.TestGetBoolPropertyKey, filters) s.NoError(err) } func (s *configStoreClientSuite) TestListValues() { defaultTestSetup(s) val, err := s.client.ListValue(nil) s.NoError(err) for _, resEntry := range val { for _, oriEntry := range snapshot1.Values.Entries { if oriEntry.Name == resEntry.Name { s.Equal(resEntry.Values, oriEntry.Values) } } } } func (s *configStoreClientSuite) TestListValues_EmptyCache() { s.mockManager.EXPECT(). FetchDynamicConfig(gomock.Any(), p.DynamicConfig). Return(&p.FetchDynamicConfigResponse{ Snapshot: &p.DynamicConfigSnapshot{ Version: 1, Values: &types.DynamicConfigBlob{ SchemaVersion: 1, Entries: nil, }, }, }, nil). MaxTimes(1) s.client.update() val, err := s.client.ListValue(nil) s.NoError(err) s.Nil(val) } func (s *configStoreClientSuite) TestValidateKeyDataBlobPair() { tests := []struct { name string key dynamicproperties.Key blob *types.DataBlob wantErr bool }{ { name: "valid int key", key: dynamicproperties.TestGetIntPropertyKey, blob: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(10), }, wantErr: false, }, { name: "invalid int key - wrong type", key: dynamicproperties.TestGetIntPropertyKey, blob: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(true), }, wantErr: true, }, { name: "valid bool key", key: dynamicproperties.TestGetBoolPropertyKey, blob: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper(true), }, wantErr: false, }, { name: "invalid bool key - wrong type", key: dynamicproperties.TestGetBoolPropertyKey, blob: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonMarshalHelper("true"), }, wantErr: true, }, } for _, tc := range tests { s.Run(tc.name, func() { err := validateKeyDataBlobPair(tc.key, tc.blob) if tc.wantErr { s.Require().Error(err, "Expected an error for case: %s", tc.name) } else { s.Require().NoError(err, "Expected no error for case: %s", tc.name) } }) } } func (s *configStoreClientSuite) TestNewConfigStoreClient_NilPersistenceConfig() { _, err := NewConfigStoreClient(&c.ClientConfig{}, nil, log.NewNoop(), metrics.NewNoopMetricsClient(), p.DynamicConfig) s.Require().Error(err, "should fail when persistence config is nil") s.Require().EqualError(err, "persistence cfg is nil") } func (s *configStoreClientSuite) TestNewConfigStoreClient_MissingDefaultPersistenceConfig() { persistenceCfg := &config.Persistence{ DataStores: map[string]config.DataStore{}, } _, err := NewConfigStoreClient(&c.ClientConfig{}, persistenceCfg, log.NewNoop(), metrics.NewNoopMetricsClient(), p.DynamicConfig) s.Require().Error(err, "should fail when default persistence config is missing") s.Require().EqualError(err, "default persistence config missing") } func (s *configStoreClientSuite) TestNewConfigStoreClient_InvalidClientConfig() { persistenceCfg := &config.Persistence{ DataStores: map[string]config.DataStore{ "default": {}, }, DefaultStore: "default", } clientCfg := &c.ClientConfig{ PollInterval: time.Millisecond, } logger := log.NewNoop() _, err := NewConfigStoreClient(clientCfg, persistenceCfg, logger, metrics.NewNoopMetricsClient(), p.DynamicConfig) s.Require().Error(err, "should fail when client config is invalid") } func jsonMarshalHelper(v interface{}) []byte { data, _ := json.Marshal(v) return data } type eqSnapshotVersionMatcher struct { version int64 } func (e eqSnapshotVersionMatcher) Matches(x interface{}) bool { arg, ok := x.(*p.UpdateDynamicConfigRequest) if !ok { return false } return e.version == arg.Snapshot.Version } func (e eqSnapshotVersionMatcher) String() string { return fmt.Sprintf("Version match %d.\n", e.version) } func EqSnapshotVersion(version int64) gomock.Matcher { return eqSnapshotVersionMatcher{version} } ================================================ FILE: common/dynamicconfig/configstore/configstore_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: config_store_client.go // // Generated by this command: // // mockgen -package configstore -source config_store_client.go -destination configstore_mock.go -self_package github.com/uber/cadence/common/dynamicconfig/configstore // // Package configstore is a generated GoMock package. package configstore import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" dynamicproperties "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" types "github.com/uber/cadence/common/types" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // GetBoolValue mocks base method. func (m *MockClient) GetBoolValue(name dynamicproperties.BoolKey, filters map[dynamicproperties.Filter]any) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBoolValue", name, filters) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // GetBoolValue indicates an expected call of GetBoolValue. func (mr *MockClientMockRecorder) GetBoolValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBoolValue", reflect.TypeOf((*MockClient)(nil).GetBoolValue), name, filters) } // GetDurationValue mocks base method. func (m *MockClient) GetDurationValue(name dynamicproperties.DurationKey, filters map[dynamicproperties.Filter]any) (time.Duration, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDurationValue", name, filters) ret0, _ := ret[0].(time.Duration) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDurationValue indicates an expected call of GetDurationValue. func (mr *MockClientMockRecorder) GetDurationValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDurationValue", reflect.TypeOf((*MockClient)(nil).GetDurationValue), name, filters) } // GetFloatValue mocks base method. func (m *MockClient) GetFloatValue(name dynamicproperties.FloatKey, filters map[dynamicproperties.Filter]any) (float64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFloatValue", name, filters) ret0, _ := ret[0].(float64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFloatValue indicates an expected call of GetFloatValue. func (mr *MockClientMockRecorder) GetFloatValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFloatValue", reflect.TypeOf((*MockClient)(nil).GetFloatValue), name, filters) } // GetIntValue mocks base method. func (m *MockClient) GetIntValue(name dynamicproperties.IntKey, filters map[dynamicproperties.Filter]any) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetIntValue", name, filters) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // GetIntValue indicates an expected call of GetIntValue. func (mr *MockClientMockRecorder) GetIntValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIntValue", reflect.TypeOf((*MockClient)(nil).GetIntValue), name, filters) } // GetListValue mocks base method. func (m *MockClient) GetListValue(name dynamicproperties.ListKey, filters map[dynamicproperties.Filter]any) ([]any, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetListValue", name, filters) ret0, _ := ret[0].([]any) ret1, _ := ret[1].(error) return ret0, ret1 } // GetListValue indicates an expected call of GetListValue. func (mr *MockClientMockRecorder) GetListValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListValue", reflect.TypeOf((*MockClient)(nil).GetListValue), name, filters) } // GetMapValue mocks base method. func (m *MockClient) GetMapValue(name dynamicproperties.MapKey, filters map[dynamicproperties.Filter]any) (map[string]any, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMapValue", name, filters) ret0, _ := ret[0].(map[string]any) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMapValue indicates an expected call of GetMapValue. func (mr *MockClientMockRecorder) GetMapValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMapValue", reflect.TypeOf((*MockClient)(nil).GetMapValue), name, filters) } // GetStringValue mocks base method. func (m *MockClient) GetStringValue(name dynamicproperties.StringKey, filters map[dynamicproperties.Filter]any) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetStringValue", name, filters) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStringValue indicates an expected call of GetStringValue. func (mr *MockClientMockRecorder) GetStringValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStringValue", reflect.TypeOf((*MockClient)(nil).GetStringValue), name, filters) } // GetValue mocks base method. func (m *MockClient) GetValue(name dynamicproperties.Key) (any, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValue", name) ret0, _ := ret[0].(any) ret1, _ := ret[1].(error) return ret0, ret1 } // GetValue indicates an expected call of GetValue. func (mr *MockClientMockRecorder) GetValue(name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValue", reflect.TypeOf((*MockClient)(nil).GetValue), name) } // GetValueWithFilters mocks base method. func (m *MockClient) GetValueWithFilters(name dynamicproperties.Key, filters map[dynamicproperties.Filter]any) (any, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValueWithFilters", name, filters) ret0, _ := ret[0].(any) ret1, _ := ret[1].(error) return ret0, ret1 } // GetValueWithFilters indicates an expected call of GetValueWithFilters. func (mr *MockClientMockRecorder) GetValueWithFilters(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValueWithFilters", reflect.TypeOf((*MockClient)(nil).GetValueWithFilters), name, filters) } // ListValue mocks base method. func (m *MockClient) ListValue(name dynamicproperties.Key) ([]*types.DynamicConfigEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListValue", name) ret0, _ := ret[0].([]*types.DynamicConfigEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // ListValue indicates an expected call of ListValue. func (mr *MockClientMockRecorder) ListValue(name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListValue", reflect.TypeOf((*MockClient)(nil).ListValue), name) } // RestoreValue mocks base method. func (m *MockClient) RestoreValue(name dynamicproperties.Key, filters map[dynamicproperties.Filter]any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RestoreValue", name, filters) ret0, _ := ret[0].(error) return ret0 } // RestoreValue indicates an expected call of RestoreValue. func (mr *MockClientMockRecorder) RestoreValue(name, filters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestoreValue", reflect.TypeOf((*MockClient)(nil).RestoreValue), name, filters) } // Start mocks base method. func (m *MockClient) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockClientMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockClient)(nil).Start)) } // Stop mocks base method. func (m *MockClient) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockClientMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockClient)(nil).Stop)) } // UpdateValue mocks base method. func (m *MockClient) UpdateValue(name dynamicproperties.Key, value any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateValue", name, value) ret0, _ := ret[0].(error) return ret0 } // UpdateValue indicates an expected call of UpdateValue. func (mr *MockClientMockRecorder) UpdateValue(name, value any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateValue", reflect.TypeOf((*MockClient)(nil).UpdateValue), name, value) } ================================================ FILE: common/dynamicconfig/dynamicconfigfx/fx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package dynamicconfigfx import ( "path/filepath" "go.uber.org/fx" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/configstore" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) // Module provides fx options for dynamic config initialization var Module = fx.Options(fx.Provide(New)) // Params required to build a new dynamic config. type Params struct { fx.In Cfg config.Config Logger log.Logger MetricsClient metrics.Client RootDir string `name:"root-dir"` Lifecycle fx.Lifecycle } type Result struct { fx.Out Client dynamicconfig.Client Collection *dynamicconfig.Collection } // New creates dynamicconfig.Client from the configuration func New(p Params) Result { stopped := make(chan struct{}) if p.Cfg.DynamicConfig.Client == "" { p.Cfg.DynamicConfigClient.Filepath = constructPathIfNeed(p.RootDir, p.Cfg.DynamicConfigClient.Filepath) } else { p.Cfg.DynamicConfig.FileBased.Filepath = constructPathIfNeed(p.RootDir, p.Cfg.DynamicConfig.FileBased.Filepath) } p.Lifecycle.Append(fx.StopHook(func() { close(stopped) })) var res dynamicconfig.Client var err error if p.Cfg.DynamicConfig.Client == "" { p.Logger.Warn("falling back to legacy file based dynamicClientConfig") res, err = dynamicconfig.NewFileBasedClient(&p.Cfg.DynamicConfigClient, p.Logger, stopped) } else { switch p.Cfg.DynamicConfig.Client { case dynamicconfig.ConfigStoreClient: p.Logger.Info("initialising ConfigStore dynamic config client") res, err = configstore.NewConfigStoreClient( &p.Cfg.DynamicConfig.ConfigStore, &p.Cfg.Persistence, p.Logger, p.MetricsClient, persistence.DynamicConfig, ) case dynamicconfig.FileBasedClient: p.Logger.Info("initialising File Based dynamic config client") res, err = dynamicconfig.NewFileBasedClient(&p.Cfg.DynamicConfig.FileBased, p.Logger, stopped) } } if res == nil { p.Logger.Info("initialising NOP dynamic config client") res = dynamicconfig.NewNopClient() } else if err != nil { p.Logger.Error("creating dynamic config client failed, using no-op config client instead", tag.Error(err)) res = dynamicconfig.NewNopClient() } clusterGroupMetadata := p.Cfg.ClusterGroupMetadata dc := dynamicconfig.NewCollection( res, p.Logger, dynamicproperties.ClusterNameFilter(clusterGroupMetadata.CurrentClusterName), ) return Result{ Client: res, Collection: dc, } } // constructPathIfNeed would append the dir as the root dir // when the file wasn't absolute path. func constructPathIfNeed(dir string, file string) string { if !filepath.IsAbs(file) { return dir + "/" + file } return file } ================================================ FILE: common/dynamicconfig/dynamicconfigfx/fx_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package dynamicconfigfx import ( "testing" "go.uber.org/fx" "go.uber.org/fx/fxtest" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) func TestModule(t *testing.T) { app := fxtest.New(t, testlogger.Module(t), fx.Provide( func() config.Config { return config.Config{ ClusterGroupMetadata: &config.ClusterGroupMetadata{}, } }, func() fxRoot { return fxRoot{ RootDir: "../../../", } }, metrics.NewNoopMetricsClient, ), Module, fx.Invoke(func(c dynamicconfig.Client) {}), ) app.RequireStart().RequireStop() } type fxRoot struct { fx.Out RootDir string `name:"root-dir"` } ================================================ FILE: common/dynamicconfig/dynamicproperties/config_mock.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamicproperties import ( "time" ) // These mock functions are for tests to use config properties that are dynamic // GetIntPropertyFn returns value as IntPropertyFn func GetIntPropertyFn(value int) func(opts ...FilterOption) int { return func(...FilterOption) int { return value } } // GetIntPropertyFilteredByDomain returns values as IntPropertyFnWithDomainFilters func GetIntPropertyFilteredByDomain(value int) func(domain string) int { return func(domain string) int { return value } } // GetIntPropertyFilteredByTaskListInfo returns value as IntPropertyFnWithTaskListInfoFilters func GetIntPropertyFilteredByTaskListInfo(value int) func(domain string, taskList string, taskType int) int { return func(domain string, taskList string, taskType int) int { return value } } // GetIntPropertyFilteredByShardID returns values as IntPropertyFnWithShardIDFilter func GetIntPropertyFilteredByShardID(value int) func(shardID int) int { return func(shardID int) int { return value } } // GetIntPropertyFilteredByWorkflowType returns values as IntPropertyFnWithWorkflowTypeFilters func GetIntPropertyFilteredByWorkflowType(value int) func(domainName string, workflowType string) int { return func(domainName string, workflowType string) int { return value } } // GetDurationPropertyFilteredByWorkflowType returns values as IntPropertyFnWithWorkflowTypeFilters func GetDurationPropertyFilteredByWorkflowType(value time.Duration) func(domainName string, workflowType string) time.Duration { return func(domainName string, workflowType string) time.Duration { return value } } // GetFloatPropertyFn returns value as FloatPropertyFn func GetFloatPropertyFn(value float64) func(opts ...FilterOption) float64 { return func(...FilterOption) float64 { return value } } // GetBoolPropertyFn returns value as BoolPropertyFn func GetBoolPropertyFn(value bool) func(opts ...FilterOption) bool { return func(...FilterOption) bool { return value } } // GetBoolPropertyFnFilteredByDomain returns value as BoolPropertyFnWithDomainFilters func GetBoolPropertyFnFilteredByDomain(value bool) func(domain string) bool { return func(domain string) bool { return value } } // GetBoolPropertyFnFilteredByDomainID returns value as BoolPropertyFnWithDomainIDFilters func GetBoolPropertyFnFilteredByDomainID(value bool) func(domainID string) bool { return func(domainID string) bool { return value } } // GetBoolPropertyFilteredByTaskListInfo returns value as BoolPropertyFnWithTaskListInfoFilters func GetBoolPropertyFilteredByTaskListInfo(value bool) func(domain string, taskList string, taskType int) bool { return func(domain string, taskList string, taskType int) bool { return value } } // GetDurationPropertyFnFilteredByDomain returns value as DurationPropertyFnFilteredByDomain func GetDurationPropertyFnFilteredByDomain(value time.Duration) func(domain string) time.Duration { return func(domain string) time.Duration { return value } } // GetDurationPropertyFn returns value as DurationPropertyFn func GetDurationPropertyFn(value time.Duration) func(opts ...FilterOption) time.Duration { return func(...FilterOption) time.Duration { return value } } // GetDurationPropertyFnFilteredByTaskListInfo returns value as DurationPropertyFnWithTaskListInfoFilters func GetDurationPropertyFnFilteredByTaskListInfo(value time.Duration) func(domain string, taskList string, taskType int) time.Duration { return func(domain string, taskList string, taskType int) time.Duration { return value } } // GetDurationPropertyFnFilteredByShardID returns value as DurationPropertyFnWithShardIDFilter func GetDurationPropertyFnFilteredByShardID(value time.Duration) func(shardID int) time.Duration { return func(shardID int) time.Duration { return value } } // GetStringPropertyFn returns value as StringPropertyFn func GetStringPropertyFn(value string) func(opts ...FilterOption) string { return func(...FilterOption) string { return value } } // GetStringPropertyFnFilteredByDomain returns value as StringPropertyFnWithDomainFilters func GetStringPropertyFnFilteredByDomain(value string) func(domain string) string { return func(domain string) string { return value } } // GetMapPropertyFn returns value as MapPropertyFn func GetMapPropertyFn(value map[string]interface{}) func(opts ...FilterOption) map[string]interface{} { return func(...FilterOption) map[string]interface{} { return value } } ================================================ FILE: common/dynamicconfig/dynamicproperties/constants.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamicproperties import ( "fmt" "math" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" ) type ( // DynamicInt defines the properties for a dynamic config with int value type DynamicInt struct { KeyName string Description string DefaultValue int Filters []Filter } DynamicBool struct { KeyName string Description string DefaultValue bool Filters []Filter } DynamicFloat struct { KeyName string Description string DefaultValue float64 Filters []Filter } DynamicString struct { KeyName string Description string DefaultValue string Filters []Filter } DynamicDuration struct { KeyName string Description string DefaultValue time.Duration Filters []Filter } DynamicMap struct { KeyName string Description string DefaultValue map[string]interface{} Filters []Filter } DynamicList struct { KeyName string Description string DefaultValue []interface{} Filters []Filter } IntKey int BoolKey int FloatKey int StringKey int DurationKey int MapKey int ListKey int Key interface { String() string Description() string DefaultValue() interface{} // Filters is used to identify what filters a DynamicConfig key may have. // For example, CLI tool uses this to figure out all domain specific configurations for migration validation. Filters() []Filter } ) // ListAllProductionKeys returns all key used in production func ListAllProductionKeys() []Key { result := make([]Key, 0, len(IntKeys)+len(BoolKeys)+len(FloatKeys)+len(StringKeys)+len(DurationKeys)+len(MapKeys)) for i := TestGetIntPropertyFilteredByTaskListInfoKey + 1; i < LastIntKey; i++ { result = append(result, i) } for i := TestGetBoolPropertyFilteredByTaskListInfoKey + 1; i < LastBoolKey; i++ { result = append(result, i) } for i := TestGetFloat64PropertyKey + 1; i < LastFloatKey; i++ { result = append(result, i) } for i := TestGetStringPropertyKey + 1; i < LastStringKey; i++ { result = append(result, i) } for i := TestGetDurationPropertyFilteredByTaskListInfoKey + 1; i < LastDurationKey; i++ { result = append(result, i) } for i := TestGetMapPropertyKey + 1; i < LastMapKey; i++ { result = append(result, i) } for i := TestGetListPropertyKey + 1; i < LastListKey; i++ { result = append(result, i) } return result } func GetKeyFromKeyName(keyName string) (Key, error) { keyVal, ok := _keyNames[keyName] if !ok { return nil, fmt.Errorf("invalid dynamic config key name: %s", keyName) } return keyVal, nil } // GetAllKeys returns a copy of all configuration keys with all details func GetAllKeys() map[string]Key { result := make(map[string]Key, len(_keyNames)) for k, v := range _keyNames { result[k] = v } return result } func ValidateKeyValuePair(key Key, value interface{}) error { err := fmt.Errorf("key value pair mismatch, key type: %T, value type: %T", key, value) switch key.(type) { case IntKey: if _, ok := value.(int); !ok { return err } case BoolKey: if _, ok := value.(bool); !ok { return err } case FloatKey: if _, ok := value.(float64); !ok { return err } case StringKey: if _, ok := value.(string); !ok { return err } case DurationKey: if _, ok := value.(time.Duration); !ok { return err } case MapKey: if _, ok := value.(map[string]interface{}); !ok { return err } case ListKey: if _, ok := value.([]interface{}); !ok { return err } default: return fmt.Errorf("unknown key type: %T", key) } return nil } func (k IntKey) String() string { return IntKeys[k].KeyName } func (k IntKey) Description() string { return IntKeys[k].Description } func (k IntKey) DefaultValue() interface{} { return IntKeys[k].DefaultValue } func (k IntKey) DefaultInt() int { return IntKeys[k].DefaultValue } func (k IntKey) Filters() []Filter { return IntKeys[k].Filters } func (k BoolKey) String() string { return BoolKeys[k].KeyName } func (k BoolKey) Description() string { return BoolKeys[k].Description } func (k BoolKey) DefaultValue() interface{} { return BoolKeys[k].DefaultValue } func (k BoolKey) DefaultBool() bool { return BoolKeys[k].DefaultValue } func (k BoolKey) Filters() []Filter { return BoolKeys[k].Filters } func (k FloatKey) String() string { return FloatKeys[k].KeyName } func (k FloatKey) Description() string { return FloatKeys[k].Description } func (k FloatKey) DefaultValue() interface{} { return FloatKeys[k].DefaultValue } func (k FloatKey) DefaultFloat() float64 { return FloatKeys[k].DefaultValue } func (k FloatKey) Filters() []Filter { return FloatKeys[k].Filters } func (k StringKey) String() string { return StringKeys[k].KeyName } func (k StringKey) Description() string { return StringKeys[k].Description } func (k StringKey) DefaultValue() interface{} { return StringKeys[k].DefaultValue } func (k StringKey) DefaultString() string { return StringKeys[k].DefaultValue } func (k StringKey) Filters() []Filter { return StringKeys[k].Filters } func (k DurationKey) String() string { return DurationKeys[k].KeyName } func (k DurationKey) Description() string { return DurationKeys[k].Description } func (k DurationKey) DefaultValue() interface{} { return DurationKeys[k].DefaultValue } func (k DurationKey) DefaultDuration() time.Duration { return DurationKeys[k].DefaultValue } func (k DurationKey) Filters() []Filter { return DurationKeys[k].Filters } func (k MapKey) String() string { return MapKeys[k].KeyName } func (k MapKey) Description() string { return MapKeys[k].Description } func (k MapKey) DefaultValue() interface{} { return MapKeys[k].DefaultValue } func (k MapKey) DefaultMap() map[string]interface{} { return MapKeys[k].DefaultValue } func (k MapKey) Filters() []Filter { return MapKeys[k].Filters } func (k ListKey) String() string { return ListKeys[k].KeyName } func (k ListKey) Description() string { return ListKeys[k].Description } func (k ListKey) DefaultValue() interface{} { return ListKeys[k].DefaultValue } func (k ListKey) DefaultList() []interface{} { return ListKeys[k].DefaultValue } func (k ListKey) Filters() []Filter { return ListKeys[k].Filters } // UnlimitedRPS represents an integer to use for "unlimited" RPS values. // // Since our ratelimiters do int/float conversions, and zero or negative values // result in not allowing any requests, math.MaxInt is unsafe: // // int(float64(math.MaxInt)) // -9223372036854775808 // // Much higher values are possible, but we can't handle 2 billion RPS, this is good enough. const UnlimitedRPS = math.MaxInt32 /*** * !!!Important!!! * For developer: Make sure to add/maintain the comment in the right format: usage, keyName, and default value * So that our go-docs can have the full [documentation](https://pkg.go.dev/github.com/uber/cadence@v0.19.1/common/service/dynamicconfig#Key). ***/ const ( UnknownIntKey IntKey = iota // key for tests TestGetIntPropertyKey TestGetIntPropertyFilteredByDomainKey TestGetIntPropertyFilteredByWorkflowTypeKey TestGetIntPropertyFilteredByTaskListInfoKey TestGetIntPropertyFilteredByShardIDKey // key for common & admin // TransactionSizeLimit is the maximum allowed size in bytes for a single persistence transaction when appending history events // KeyName: system.transactionSizeLimit // Value type: Int // Default value: 14680064 (14*1024*1024, ~14MB) // Allowed filters: N/A TransactionSizeLimit // MaxRetentionDays is the maximum allowed retention period in days for workflow history after workflow close for all domains // KeyName: system.maxRetentionDays // Value type: Int // Default value: 30 // Allowed filters: N/A MaxRetentionDays // MinRetentionDays is the minimum allowed retention period in days for workflow history after workflow close for all domains // KeyName: system.minRetentionDays // Value type: Int // Default value: 1 // Allowed filters: N/A MinRetentionDays // MaxDecisionStartToCloseSeconds is the maximum allowed value for decision start to close timeout in seconds // KeyName: system.maxDecisionStartToCloseSeconds // Value type: Int // Default value: 240 // Allowed filters: N/A MaxDecisionStartToCloseSeconds // BlobSizeLimitError is the per event blob size limit // KeyName: limit.blobSize.error // Value type: Int // Default value: 2 * 1024 * 1024 // Allowed filters: N/A BlobSizeLimitError // BlobSizeLimitWarn is the per event blob size limit for warning // KeyName: limit.blobSize.warn // Value type: Int // Default value: 262144 (256*1024) // Allowed filters: DomainName BlobSizeLimitWarn // HistorySizeLimitError is the per workflow execution history size limit // KeyName: limit.historySize.error // Value type: Int // Default value: 209715200 (200*1024*1024) // Allowed filters: DomainName HistorySizeLimitError // HistorySizeLimitWarn is the per workflow execution history size limit for warning // KeyName: limit.historySize.warn // Value type: Int // Default value: 52428800 (50*1024*1024) // Allowed filters: DomainName HistorySizeLimitWarn // HistoryCountLimitError is the per workflow execution history event count limit // KeyName: limit.historyCount.error // Value type: Int // Default value: 204800 (200*1024) // Allowed filters: DomainName HistoryCountLimitError // HistoryCountLimitWarn is the per workflow execution history event count limit for warning // KeyName: limit.historyCount.warn // Value type: Int // Default value: 51200 (50*1024) // Allowed filters: DomainName HistoryCountLimitWarn // PendingActivitiesCountLimitError is the limit of how many pending activities a workflow can have at a point in time // KeyName: limit.pendingActivityCount.error // Value type: Int // Default value: 1024 // Allowed filters: N/A PendingActivitiesCountLimitError // PendingActivitiesCountLimitWarn is the limit of how many activities a workflow can have before a warning is logged // KeyName: limit.pendingActivityCount.warn // Value type: Int // Default value: 512 // Allowed filters: N/A PendingActivitiesCountLimitWarn // DomainNameMaxLength is the length limit for domain name // KeyName: limit.domainNameLength // Value type: Int // Default value: common.DefaultIDLengthErrorLimit (1000) // Allowed filters: DomainName DomainNameMaxLength // IdentityMaxLength is the length limit for identity // KeyName: limit.identityLength // Value type: Int // Default value: 1000 ( see common.DefaultIDLengthErrorLimit) // Allowed filters: DomainName IdentityMaxLength // WorkflowIDMaxLength is the length limit for workflowID // KeyName: limit.workflowIDLength // Value type: Int // Default value: 1000 (see common.DefaultIDLengthErrorLimit) // Allowed filters: DomainName WorkflowIDMaxLength // SignalNameMaxLength is the length limit for signal name // KeyName: limit.signalNameLength // Value type: Int // Default value: 1000 (see common.DefaultIDLengthErrorLimit) // Allowed filters: DomainName SignalNameMaxLength // WorkflowTypeMaxLength is the length limit for workflow type // KeyName: limit.workflowTypeLength // Value type: Int // Default value: 1000 (see common.DefaultIDLengthErrorLimit) // Allowed filters: DomainName WorkflowTypeMaxLength // RequestIDMaxLength is the length limit for requestID // KeyName: limit.requestIDLength // Value type: Int // Default value: 1000 (see common.DefaultIDLengthErrorLimit) // Allowed filters: DomainName RequestIDMaxLength // TaskListNameMaxLength is the length limit for task list name // KeyName: limit.taskListNameLength // Value type: Int // Default value: 1000 (see common.DefaultIDLengthErrorLimit) // Allowed filters: DomainName TaskListNameMaxLength // ActivityIDMaxLength is the length limit for activityID // KeyName: limit.activityIDLength // Value type: Int // Default value: 1000 (see common.DefaultIDLengthErrorLimit) // Allowed filters: DomainName ActivityIDMaxLength // ActivityTypeMaxLength is the length limit for activity type // KeyName: limit.activityTypeLength // Value type: Int // Default value: 1000 (see common.DefaultIDLengthErrorLimit) // Allowed filters: DomainName ActivityTypeMaxLength // MarkerNameMaxLength is the length limit for marker name // KeyName: limit.markerNameLength // Value type: Int // Default value: 1000 (see common.DefaultIDLengthErrorLimit) // Allowed filters: DomainName MarkerNameMaxLength // TimerIDMaxLength is the length limit for timerID // KeyName: limit.timerIDLength // Value type: Int // Default value: 1000 (see common.DefaultIDLengthErrorLimit) // Allowed filters: DomainName TimerIDMaxLength // MaxIDLengthWarnLimit is the warn length limit for various IDs, including: Domain, TaskList, WorkflowID, ActivityID, TimerID, WorkflowType, ActivityType, SignalName, MarkerName, ErrorReason/FailureReason/CancelCause, Identity, RequestID // KeyName: limit.maxIDWarnLength // Value type: Int // Default value: 128 (see common.DefaultIDLengthWarnLimit) // Allowed filters: N/A MaxIDLengthWarnLimit // key for frontend // FrontendPersistenceMaxQPS is the max qps frontend host can query DB // KeyName: frontend.persistenceMaxQPS // Value type: Int // Default value: 2000 // Allowed filters: N/A FrontendPersistenceMaxQPS // FrontendPersistenceGlobalMaxQPS is the max qps frontend cluster can query DB // KeyName: frontend.persistenceGlobalMaxQPS // Value type: Int // Default value: 0 // Allowed filters: N/A FrontendPersistenceGlobalMaxQPS // FrontendVisibilityMaxPageSize is default max size for ListWorkflowExecutions in one page // KeyName: frontend.visibilityMaxPageSize // Value type: Int // Default value: 1000 // Allowed filters: DomainName FrontendVisibilityMaxPageSize // FrontendVisibilityListMaxQPS is max qps frontend can list open/close workflows // KeyName: frontend.visibilityListMaxQPS // Value type: Int // Default value: 10 // Allowed filters: DomainName // deprecated: never used for ratelimiting, only sampling-based failure injection, and only on database-based visibility FrontendVisibilityListMaxQPS // FrontendESVisibilityListMaxQPS is max qps frontend can list open/close workflows from ElasticSearch // KeyName: frontend.esVisibilityListMaxQPS // Value type: Int // Default value: 30 // Allowed filters: DomainName // deprecated: never read from, all ES reads and writes erroneously use PersistenceMaxQPS FrontendESVisibilityListMaxQPS // FrontendESIndexMaxResultWindow is ElasticSearch index setting max_result_window // KeyName: frontend.esIndexMaxResultWindow // Value type: Int // Default value: 10000 // Allowed filters: N/A FrontendESIndexMaxResultWindow // FrontendHistoryMaxPageSize is default max size for GetWorkflowExecutionHistory in one page // KeyName: frontend.historyMaxPageSize // Value type: Int // Default value: 1000 (see common.GetHistoryMaxPageSize) // Allowed filters: DomainName FrontendHistoryMaxPageSize // FrontendUserRPS is used to limit "user" requests (StartWorkflow, Signal, etc) // per frontend instance (across all domains, or for non-domain-related requests), // and is mostly intended to protect against excessive single-host load. // // KeyName: frontend.rps // Value type: Int // Default value: 1200 // Allowed filters: N/A FrontendUserRPS // FrontendWorkerRPS is used to limit "worker" requests (PollFor...Task, RespondTask..., etc) // per frontend instance (across all domains, or for non-domain-related requests), // and is mostly intended to protect against excessive single-host load. // // KeyName: frontend.workerrps // Value type: Int // Default value: UnlimitedRPS // Allowed filters: N/A FrontendWorkerRPS // FrontendVisibilityRPS is used to limit "visibility" requests (ListWorkflow* and similar) // per frontend instance (across all domains, or for non-domain-related requests), // and is mostly intended to protect against excessive single-host load. // // KeyName: frontend.visibilityrps // Value type: Int // Default value: UnlimitedRPS // Allowed filters: N/A FrontendVisibilityRPS // FrontendAsyncRPS is used to limit "async" requests (StartWorkflowAsync, etc for many "user" APIs) // per frontend instance (across all domains, or for non-domain-related requests), // and is mostly intended to protect against excessive single-host load. // // KeyName: frontend.asyncrps // Value type: Int // Default value: 10000 // Allowed filters: N/A FrontendAsyncRPS // FrontendMaxDomainUserRPSPerInstance is used to limit "user" requests (StartWorkflow, Signal, etc) // per domain per frontend instance, and is mostly intended to protect against excessive single-host load. // // This limit applies along-side FrontendGlobalDomainUserRPS: both must be allowed to allow a request. // // KeyName: frontend.domainrps // Value type: Int // Default value: 1200 // Allowed filters: DomainName FrontendMaxDomainUserRPSPerInstance // FrontendMaxDomainWorkerRPSPerInstance is used to limit "worker" requests (PollFor...Task, RespondTask..., etc) // per domain per frontend instance, and is mostly intended to protect against excessive single-host load. // // This limit applies along-side FrontendGlobalDomainWorkerRPS: both must be allowed to allow a request. // // KeyName: frontend.domainworkerrps // Value type: Int // Default value: UnlimitedRPS // Allowed filters: DomainName FrontendMaxDomainWorkerRPSPerInstance // FrontendMaxDomainVisibilityRPSPerInstance is used to limit "visibility" requests (ListWorkflow* and similar) // per domain per frontend instance, and is mostly intended to protect against excessive single-host load. // // This limit applies along-side FrontendGlobalDomainVisibilityRPS: both must be allowed to allow a request. // // KeyName: frontend.domainvisibilityrps // Value type: Int // Default value: UnlimitedRPS // Allowed filters: DomainName FrontendMaxDomainVisibilityRPSPerInstance // FrontendMaxDomainAsyncRPSPerInstance is used to limit "async" requests (StartWorkflowAsync, etc for many "user" APIs) // per frontend instance, and is mostly intended to protect against excessive single-host load. // // This limit applies along-side FrontendGlobalDomainAsyncRPS: both must be allowed to allow a request. // // KeyName: frontend.domainasyncrps // Value type: Int // Default value: 10000 // Allowed filters: DomainName FrontendMaxDomainAsyncRPSPerInstance // FrontendGlobalDomainUserRPS is used to limit "user" requests (StartWorkflow, Signal, etc) // per domain to a target RPS that is shared across the entire cluster. // // Currently, there are two ways this is achieved, which can be selected by FrontendGlobalRatelimiterMode // with a "user:" key prefix: // 1. "local", where the configured RPS is split evenly across all frontend hosts in the cluster. // This works well if your load is roughly evenly distributed. // 2. "global", where frontend hosts share load information with each other, to adjust to imbalanced load. // This works well if your load is very imbalanced, e.g. one domain tends to contact a subset of frontend hosts much more than others. // // KeyName: frontend.globalDomainrps // Value type: Int // Default value: UnlimitedRPS (0 triggers a fallback to per-instance-RPS, generally avoid) // Allowed filters: DomainName FrontendGlobalDomainUserRPS // FrontendGlobalDomainWorkerRPS is used to limit "worker" requests (PollFor...Task, RespondTask..., etc) // per domain to a target RPS that is shared across the entire cluster. // // Currently, there are two ways this is achieved, which can be selected by FrontendGlobalRatelimiterMode // with a "worker:" key prefix: // 1. "local", where the configured RPS is split evenly across all frontend hosts in the cluster. // This works well if your load is roughly evenly distributed. // 2. "global", where frontend hosts share load information with each other, to adjust to imbalanced load. // This works well if your load is very imbalanced, e.g. one domain tends to contact a subset of frontend hosts much more than others. // // KeyName: frontend.globalDomainWorkerrps // Value type: Int // Default value: UnlimitedRPS (0 triggers a fallback to per-instance-RPS, generally avoid) // Allowed filters: DomainName FrontendGlobalDomainWorkerRPS // FrontendGlobalDomainVisibilityRPS is used to limit "visibility" requests (ListWorkflow* and similar) // per domain to a target RPS that is shared across the entire cluster. // // Currently, there are two ways this is achieved, which can be selected by FrontendGlobalRatelimiterMode // with a "visibility:" key prefix: // 1. "local", where the configured RPS is split evenly across all frontend hosts in the cluster. // This works well if your load is roughly evenly distributed. // 2. "global", where frontend hosts share load information with each other, to adjust to imbalanced load. // This works well if your load is very imbalanced, e.g. one domain tends to contact a subset of frontend hosts much more than others. // // KeyName: frontend.globalDomainVisibilityrps // Value type: Int // Default value: UnlimitedRPS (0 triggers a fallback to per-instance-RPS, generally avoid) // Allowed filters: DomainName FrontendGlobalDomainVisibilityRPS // FrontendGlobalDomainAsyncRPS is used to limit "async" requests (StartWorkflowAsync, etc for many "user" APIs) // per domain to a target RPS that is shared across the entire cluster. // // Currently, there are two ways this is achieved, which can be selected by FrontendGlobalRatelimiterMode // with a "async:" key prefix: // 1. "local", where the configured RPS is split evenly across all frontend hosts in the cluster. // This works well if your load is roughly evenly distributed. // 2. "global", where frontend hosts share load information with each other, to adjust to imbalanced load. // This works well if your load is very imbalanced, e.g. one domain tends to contact a subset of frontend hosts much more than others. // // KeyName: frontend.globalDomainAsyncrps // Value type: Int // Default value: 100000 // Allowed filters: DomainName FrontendGlobalDomainAsyncRPS // FrontendDecisionResultCountLimit is max number of decisions per RespondDecisionTaskCompleted request // KeyName: frontend.decisionResultCountLimit // Value type: Int // Default value: 0 // Allowed filters: DomainName FrontendDecisionResultCountLimit // FrontendThrottledLogRPS is the rate limit on number of log messages emitted per second for throttled logger // KeyName: frontend.throttledLogRPS // Value type: Int // Default value: 20 // Allowed filters: N/A FrontendThrottledLogRPS // FrontendMaxBadBinaries is the max number of bad binaries in domain config // KeyName: frontend.maxBadBinaries // Value type: Int // Default value: 10 (see domain.MaxBadBinaries) // Allowed filters: DomainName FrontendMaxBadBinaries // SearchAttributesNumberOfKeysLimit is the limit of number of keys // KeyName: frontend.searchAttributesNumberOfKeysLimit // Value type: Int // Default value: 100 // Allowed filters: DomainName SearchAttributesNumberOfKeysLimit // SearchAttributesSizeOfValueLimit is the size limit of each value // KeyName: frontend.searchAttributesSizeOfValueLimit // Value type: Int // Default value: 2048 (2*1024) // Allowed filters: DomainName SearchAttributesSizeOfValueLimit // SearchAttributesTotalSizeLimit is the size limit of the whole map // KeyName: frontend.searchAttributesTotalSizeLimit // Value type: Int // Default value: 40960 (40*1024) // Allowed filters: DomainName SearchAttributesTotalSizeLimit // VisibilityArchivalQueryMaxPageSize is the maximum page size for a visibility archival query // KeyName: frontend.visibilityArchivalQueryMaxPageSize // Value type: Int // Default value: 10000 // Allowed filters: N/A VisibilityArchivalQueryMaxPageSize // FrontendFailoverHistoryMaxSize is the maximum size for the number of failover event records in a domain failover history // KeyName: frontend.failoverHistoryMaxSize // Value type: Int // Default value: 5 // Allowed filters: DomainName FrontendFailoverHistoryMaxSize // key for matching // MatchingUserRPS is request rate per second for each matching host // KeyName: matching.rps // Value type: Int // Default value: 1200 // Allowed filters: N/A MatchingUserRPS // MatchingWorkerRPS is background-processing request rate per second for each matching host // KeyName: matching.workerrps // Value type: Int // Default value: UnlimitedRPS // Allowed filters: N/A MatchingWorkerRPS // MatchingDomainUserRPS is request rate per domain per second for each matching host // KeyName: matching.domainrps // Value type: Int // Default value: 0 // Allowed filters: N/A MatchingDomainUserRPS // MatchingDomainWorkerRPS is background-processing request rate per domain per second for each matching host // KeyName: matching.domainworkerrps // Value type: Int // Default value: UnlimitedRPS // Allowed filters: N/A MatchingDomainWorkerRPS // MatchingPersistenceMaxQPS is the max qps matching host can query DB // KeyName: matching.persistenceMaxQPS // Value type: Int // Default value: 3000 // Allowed filters: N/A MatchingPersistenceMaxQPS // MatchingPersistenceGlobalMaxQPS is the max qps matching cluster can query DB // KeyName: matching.persistenceGlobalMaxQPS // Value type: Int // Default value: 0 // Allowed filters: N/A MatchingPersistenceGlobalMaxQPS // MatchingMinTaskThrottlingBurstSize is the minimum burst size for task list throttling // KeyName: matching.minTaskThrottlingBurstSize // Value type: Int // Default value: 1 // Allowed filters: DomainName,TasklistName,TaskType MatchingMinTaskThrottlingBurstSize // MatchingGetTasksBatchSize is the maximum batch size to fetch from the task buffer // KeyName: matching.getTasksBatchSize // Value type: Int // Default value: 1000 // Allowed filters: DomainName,TasklistName,TaskType MatchingGetTasksBatchSize // MatchingOutstandingTaskAppendsThreshold is the threshold for outstanding task appends // KeyName: matching.outstandingTaskAppendsThreshold // Value type: Int // Default value: 250 // Allowed filters: DomainName,TasklistName,TaskType MatchingOutstandingTaskAppendsThreshold // MatchingMaxTaskBatchSize is max batch size for task writer // KeyName: matching.maxTaskBatchSize // Value type: Int // Default value: 100 // Allowed filters: DomainName,TasklistName,TaskType MatchingMaxTaskBatchSize // MatchingMaxTaskDeleteBatchSize is the max batch size for range deletion of tasks // KeyName: matching.maxTaskDeleteBatchSize // Value type: Int // Default value: 100 // Allowed filters: DomainName,TasklistName,TaskType MatchingMaxTaskDeleteBatchSize // MatchingThrottledLogRPS is the rate limit on number of log messages emitted per second for throttled logger // KeyName: matching.throttledLogRPS // Value type: Int // Default value: 20 // Allowed filters: N/A MatchingThrottledLogRPS // MatchingNumTasklistWritePartitions is the number of write partitions for a task list // KeyName: matching.numTasklistWritePartitions // Value type: Int // Default value: 1 // Allowed filters: DomainName,TasklistName,TaskType MatchingNumTasklistWritePartitions // MatchingNumTasklistReadPartitions is the number of read partitions for a task list // KeyName: matching.numTasklistReadPartitions // Value type: Int // Default value: 1 // Allowed filters: DomainName,TasklistName,TaskType MatchingNumTasklistReadPartitions // MatchingForwarderMaxOutstandingPolls is the max number of inflight polls from the forwarder // KeyName: matching.forwarderMaxOutstandingPolls // Value type: Int // Default value: 1 // Allowed filters: DomainName,TasklistName,TaskType MatchingForwarderMaxOutstandingPolls // MatchingForwarderMaxOutstandingTasks is the max number of inflight addTask/queryTask from the forwarder // KeyName: matching.forwarderMaxOutstandingTasks // Value type: Int // Default value: 1 // Allowed filters: DomainName,TasklistName,TaskType MatchingForwarderMaxOutstandingTasks // MatchingForwarderMaxRatePerSecond is the max rate at which add/query can be forwarded // KeyName: matching.forwarderMaxRatePerSecond // Value type: Int // Default value: 10 // Allowed filters: DomainName,TasklistName,TaskType MatchingForwarderMaxRatePerSecond // MatchingForwarderMaxChildrenPerNode is the max number of children per node in the task list partition tree // KeyName: matching.forwarderMaxChildrenPerNode // Value type: Int // Default value: 20 // Allowed filters: DomainName,TasklistName,TaskType MatchingForwarderMaxChildrenPerNode // MatchingReadRangeSize is the read range size for the task reader // KeyName: matching.readRangeSize // Value type: Int // Default value: 50000 // Allowed filters: N/A MatchingReadRangeSize // MatchingPartitionUpscaleRPS is the threshold of adding tasks RPS per partition to trigger upscale // KeyName: matching.partitionUpscaleRPS // Value type: Int // Default value: 200 // Allowed filters: DomainName,TasklistName,TaskType MatchingPartitionUpscaleRPS // MatchingIsolationGroupsPerPartition is the target number of isolation groups to assign to each partition // KeyName: matching.isolationGroupsPerPartition // Value type: Int // Default value: 2 // Allowed filters: DomainName,TasklistName,TaskType MatchingIsolationGroupsPerPartition // MatchingPercentageOnboardedToShardManager is the percentage of task lists that will be onboarded to the shard manager. // KeyName: matching.percentageOnboardedToShardManager // Value type: Int // Default value: 0 // Allowed filters: N/A MatchingPercentageOnboardedToShardManager // key for history // HistoryRPS is request rate per second for each history host // KeyName: history.rps // Value type: Int // Default value: 3000 // Allowed filters: N/A HistoryRPS // HistoryPersistenceMaxQPS is the max qps history host can query DB // KeyName: history.persistenceMaxQPS // Value type: Int // Default value: 9000 // Allowed filters: N/A HistoryPersistenceMaxQPS // HistoryPersistenceGlobalMaxQPS is the max qps history cluster can query DB // KeyName: history.persistenceGlobalMaxQPS // Value type: Int // Default value: 0 // Allowed filters: N/A HistoryPersistenceGlobalMaxQPS // HistoryVisibilityOpenMaxQPS is max qps one history host can write visibility open_executions // KeyName: history.historyVisibilityOpenMaxQPS // Value type: Int // Default value: 300 // Allowed filters: DomainName HistoryVisibilityOpenMaxQPS // HistoryVisibilityClosedMaxQPS is max qps one history host can write visibility closed_executions // KeyName: history.historyVisibilityClosedMaxQPS // Value type: Int // Default value: 300 // Allowed filters: DomainName HistoryVisibilityClosedMaxQPS // HistoryCacheInitialSize is initial size of history cache // KeyName: history.cacheInitialSize // Value type: Int // Default value: 128 // Allowed filters: N/A HistoryCacheInitialSize // HistoryCacheMaxSize is max size of history cache // KeyName: history.cacheMaxSize // Value type: Int // Default value: 512 // Allowed filters: N/A HistoryCacheMaxSize // ExecutionCacheMaxByteSize is the max byte size of history cache // KeyName: history.executionCacheMaxByteSize // Value type: Int // Default value: 0 // Allowed filters: N/A ExecutionCacheMaxByteSize // EventsCacheInitialCount is initial count of events cache // KeyName: history.eventsCacheInitialSize // Value type: Int // Default value: 128 // Allowed filters: N/A EventsCacheInitialCount // EventsCacheMaxCount is max count of events cache // KeyName: history.eventsCacheMaxSize // Value type: Int // Default value: 512 // Allowed filters: N/A EventsCacheMaxCount // EventsCacheMaxSize is max size of events cache in bytes // KeyName: history.eventsCacheMaxSizeInBytes // Value type: Int // Default value: 0 // Allowed filters: N/A EventsCacheMaxSize // EventsCacheGlobalInitialCount is initial count of global events cache // KeyName: history.eventsCacheGlobalInitialSize // Value type: Int // Default value: 4096 // Allowed filters: N/A EventsCacheGlobalInitialCount // EventsCacheGlobalMaxCount is max count of global events cache // KeyName: history.eventsCacheGlobalMaxSize // Value type: Int // Default value: 131072 // Allowed filters: N/A EventsCacheGlobalMaxCount // AcquireShardConcurrency is number of goroutines that can be used to acquire shards in the shard controller. // KeyName: history.acquireShardConcurrency // Value type: Int // Default value: 1 // Allowed filters: N/A AcquireShardConcurrency // TaskProcessRPS is the task processing rate per second for each domain // KeyName: history.taskProcessRPS // Value type: Int // Default value: 1000 // Allowed filters: DomainName TaskProcessRPS // TaskSchedulerType is the task scheduler type for priority task processor // KeyName: history.taskSchedulerType // Value type: Int enum(1 for SchedulerTypeFIFO, 2 for SchedulerTypeWRR(weighted round robin scheduler implementation)) // Default value: 2 (task.SchedulerTypeWRR) // Allowed filters: N/A TaskSchedulerType // TaskSchedulerWorkerCount is the number of workers per host in task scheduler // KeyName: history.taskSchedulerWorkerCount // Value type: Int // Default value: 200 // Allowed filters: N/A TaskSchedulerWorkerCount // TaskSchedulerQueueSize is the size of task channel for host level task scheduler // KeyName: history.taskSchedulerQueueSize // Value type: Int // Default value: 10000 // Allowed filters: N/A TaskSchedulerQueueSize // TaskSchedulerDispatcherCount is the number of task dispatcher in task scheduler (only applies to host level task scheduler) // KeyName: history.taskSchedulerDispatcherCount // Value type: Int // Default value: 1 // Allowed filters: N/A TaskSchedulerDispatcherCount // TaskSchedulerGlobalDomainRPS is the task scheduling domain rate limit per second for the whole Cadence cluster // KeyName: history.taskSchedulerGlobalDomainRPS // Value type: Int // Default value: 1000 // Allowed filters: DomainName TaskSchedulerGlobalDomainRPS // TaskCriticalRetryCount is the critical retry count for background tasks // when task attempt exceeds this threshold: // - task attempt metrics and additional error logs will be emitted // - task priority will be lowered // KeyName: history.taskCriticalRetryCount // Value type: Int // Default value: 50 // Allowed filters: N/A TaskCriticalRetryCount // QueueProcessorSplitMaxLevel is the max processing queue level // KeyName: history.queueProcessorSplitMaxLevel // Value type: Int // Default value: 2 // 3 levels, start from 0 // Allowed filters: N/A QueueProcessorSplitMaxLevel // QueueMaxPendingTaskCount is the max number of pending tasks in the queue // KeyName: history.queueMaxPendingTaskCount // Value type: Int // Default value: 10000 // Allowed filters: N/A QueueMaxPendingTaskCount // QueueCriticalPendingTaskCount is the critical pending task count for the queue, which is supposed to be less than QueueMaxPendingTaskCount // KeyName: history.queueCriticalPendingTaskCount // Value type: Int // Default value: 9000 // Allowed filters: N/A QueueCriticalPendingTaskCount // TimerTaskBatchSize is batch size for timer processor to process tasks // KeyName: history.timerTaskBatchSize // Value type: Int // Default value: 100 // Allowed filters: N/A TimerTaskBatchSize // TimerTaskDeleteBatchSize is batch size for timer processor to delete timer tasks // KeyName: history.timerTaskDeleteBatchSize // Value type: Int // Default value: 4000 // Allowed filters: N/A TimerTaskDeleteBatchSize // TimerProcessorGetFailureRetryCount is retry count for timer processor get failure operation // KeyName: history.timerProcessorGetFailureRetryCount // Value type: Int // Default value: 5 // Allowed filters: N/A TimerProcessorGetFailureRetryCount // TimerProcessorCompleteTimerFailureRetryCount is retry count for timer processor complete timer operation // KeyName: history.timerProcessorCompleteTimerFailureRetryCount // Value type: Int // Default value: 10 // Allowed filters: N/A TimerProcessorCompleteTimerFailureRetryCount // TimerProcessorFailoverMaxPollRPS is max poll rate per second for timer processor // KeyName: history.timerProcessorFailoverMaxPollRPS // Value type: Int // Default value: 1 // Allowed filters: N/A TimerProcessorFailoverMaxPollRPS // TimerProcessorMaxPollRPS is max poll rate per second for timer processor // KeyName: history.timerProcessorMaxPollRPS // Value type: Int // Default value: 20 // Allowed filters: N/A TimerProcessorMaxPollRPS // TimerProcessorMaxRedispatchQueueSize is the threshold of the number of tasks in the redispatch queue for timer processor // KeyName: history.timerProcessorMaxRedispatchQueueSize // Value type: Int // Default value: 10000 // Allowed filters: N/A TimerProcessorMaxRedispatchQueueSize // TimerProcessorHistoryArchivalSizeLimit is the max history size for inline archival // KeyName: history.timerProcessorHistoryArchivalSizeLimit // Value type: Int // Default value: 500*1024 // Allowed filters: N/A TimerProcessorHistoryArchivalSizeLimit // TransferTaskBatchSize is batch size for transferQueueProcessor // KeyName: history.transferTaskBatchSize // Value type: Int // Default value: 100 // Allowed filters: N/A TransferTaskBatchSize // TransferTaskDeleteBatchSize is batch size for transferQueueProcessor to delete transfer tasks // KeyName: history.transferTaskDeleteBatchSize // Value type: Int // Default value: 4000 // Allowed filters: N/A TransferTaskDeleteBatchSize // TransferProcessorFailoverMaxPollRPS is max poll rate per second for transferQueueProcessor // KeyName: history.transferProcessorFailoverMaxPollRPS // Value type: Int // Default value: 1 // Allowed filters: N/A TransferProcessorFailoverMaxPollRPS // TransferProcessorMaxPollRPS is max poll rate per second for transferQueueProcessor // KeyName: history.transferProcessorMaxPollRPS // Value type: Int // Default value: 20 // Allowed filters: N/A TransferProcessorMaxPollRPS // TransferProcessorCompleteTransferFailureRetryCount is times of retry for failure // KeyName: history.transferProcessorCompleteTransferFailureRetryCount // Value type: Int // Default value: 10 // Allowed filters: N/A TransferProcessorCompleteTransferFailureRetryCount // TransferProcessorMaxRedispatchQueueSize is the threshold of the number of tasks in the redispatch queue for transferQueueProcessor // KeyName: history.transferProcessorMaxRedispatchQueueSize // Value type: Int // Default value: 10000 // Allowed filters: N/A TransferProcessorMaxRedispatchQueueSize // ReplicatorTaskBatchSize is batch size for ReplicatorProcessor // KeyName: history.replicatorTaskBatchSize // Value type: Int // Default value: 25 // Allowed filters: N/A ReplicatorTaskBatchSize // ReplicatorMaxTaskBatchSize is the maximum batch size for ReplicatorProcessor // KeyName: history.replicatorMaxTaskBatchSize // Value type: Int // Default value: 1000 // Allowed filters: ShardID, ClusterName ReplicatorMaxTaskBatchSize // ReplicatorMinTaskBatchSize is the minimum batch size for ReplicatorProcessor // KeyName: history.replicatorMinTaskBatchSize // Value type: Int // Default value: 1 // Allowed filters: ShardID, ClusterName ReplicatorMinTaskBatchSize // ReplicatorTaskBatchStepCount is how many batch size values between ReplicatorMinTaskBatchSize and ReplicatorMaxTaskBatchSize are used // by ReplicatorProcessor to adjust the batch size of the replication tasks. Less value will make the adjustment more aggressive. // cannot be less than 3, otherwise only ReplicatorMinTaskBatchSize and ReplicatorMaxTaskBatchSize will be used. // KeyName: history.replicatorTaskBatchStepCount // Value type: Int // Default value: 10 // Allowed filters: ShardID, ClusterName ReplicatorTaskBatchStepCount // ReplicatorTaskDeleteBatchSize is batch size for ReplicatorProcessor to delete replication tasks // KeyName: history.replicatorTaskDeleteBatchSize // Value type: Int // Default value: 4000 // Allowed filters: N/A ReplicatorTaskDeleteBatchSize // HistoryNodeDeleteBatchSize is batch size for deleting history nodes // KeyName: history.historyNodeDeleteBatchSize // Value type: Int // Default value: 1000 // Allowed filters: N/A HistoryNodeDeleteBatchSize // ReplicatorReadTaskMaxRetryCount is the number of read replication task retry time // KeyName: history.replicatorReadTaskMaxRetryCount // Value type: Int // Default value: 3 // Allowed filters: N/A ReplicatorReadTaskMaxRetryCount // ReplicatorCacheCapacity is the capacity of replication cache in number of tasks // KeyName: history.replicatorCacheCapacity // Value type: Int // Default value: 0 // Allowed filters: N/A ReplicatorCacheCapacity // ReplicatorCacheMaxSize is the max size of the replication cache in bytes // KeyName: history.replicatorCacheMaxSize // Value type: Int // Default value: 0 // Allowed filters: N/A ReplicatorCacheMaxSize // ReplicationBudgetManagerMaxSizeBytes is the max size of the replication budget manager cache in bytes // KeyName: history.replicationBudgetManagerMaxSizeBytes // Value type: Int // Default value: 0 // Allowed filters: N/A ReplicationBudgetManagerMaxSizeBytes // ReplicationBudgetManagerMaxSizeCount is the max count of the replication budget manager cache // KeyName: history.replicationBudgetManagerMaxSizeCount // Value type: Int // Default value: 0 // Allowed filters: N/A ReplicationBudgetManagerMaxSizeCount // MaximumBufferedEventsBatch is max number of buffer event in mutable state // KeyName: history.maximumBufferedEventsBatch // Value type: Int // Default value: 100 // Allowed filters: N/A MaximumBufferedEventsBatch // MaximumSignalsPerExecution is max number of signals supported by single execution // KeyName: history.maximumSignalsPerExecution // Value type: Int // Default value: 10000 // Allowed filters: DomainName MaximumSignalsPerExecution // NumArchiveSystemWorkflows is key for number of archive system workflows running in total // KeyName: history.numArchiveSystemWorkflows // Value type: Int // Default value: 1000 // Allowed filters: N/A NumArchiveSystemWorkflows // ArchiveRequestRPS is the rate limit on the number of archive request per second // KeyName: history.archiveRequestRPS // Value type: Int // Default value: 300 // should be much smaller than frontend RPS // Allowed filters: N/A ArchiveRequestRPS // ArchiveInlineHistoryRPS is the (per instance) rate limit on the number of inline history archival attempts per second // KeyName: history.archiveInlineHistoryRPS // Value type: Int // Default value: 1000 // Allowed filters: N/A ArchiveInlineHistoryRPS // ArchiveInlineHistoryGlobalRPS is the global rate limit on the number of inline history archival attempts per second // KeyName: history.archiveInlineHistoryGlobalRPS // Value type: Int // Default value: 10000 // Allowed filters: N/A ArchiveInlineHistoryGlobalRPS // ArchiveInlineVisibilityRPS is the (per instance) rate limit on the number of inline visibility archival attempts per second // KeyName: history.archiveInlineVisibilityRPS // Value type: Int // Default value: 1000 // Allowed filters: N/A ArchiveInlineVisibilityRPS // ArchiveInlineVisibilityGlobalRPS is the global rate limit on the number of inline visibility archival attempts per second // KeyName: history.archiveInlineVisibilityGlobalRPS // Value type: Int // Default value: 10000 // Allowed filters: N/A ArchiveInlineVisibilityGlobalRPS // HistoryMaxAutoResetPoints is the key for max number of auto reset points stored in mutableState // KeyName: history.historyMaxAutoResetPoints // Value type: Int // Default value: DefaultHistoryMaxAutoResetPoints // Allowed filters: DomainName HistoryMaxAutoResetPoints // ParentClosePolicyThreshold decides that parent close policy will be processed by sys workers(if enabled) if the number of children is greater than or equal to this threshold // KeyName: history.parentClosePolicyThreshold // Value type: Int // Default value: 10 // Allowed filters: DomainName ParentClosePolicyThreshold // ParentClosePolicyBatchSize is the batch size of parent close policy processed by sys workers // KeyName: history.parentClosePolicyBatchSize // Value type: Int // Default value: 200 // Allowed filters: DomainName ParentClosePolicyBatchSize // NumParentClosePolicySystemWorkflows is key for number of parentClosePolicy system workflows running in total // KeyName: history.numParentClosePolicySystemWorkflows // Value type: Int // Default value: 10 // Allowed filters: N/A NumParentClosePolicySystemWorkflows // HistoryThrottledLogRPS is the rate limit on number of log messages emitted per second for throttled logger // KeyName: history.throttledLogRPS // Value type: Int // Default value: 4 // Allowed filters: N/A HistoryThrottledLogRPS // DecisionRetryCriticalAttempts is decision attempt threshold for logging and emiting metrics // KeyName: history.decisionRetryCriticalAttempts // Value type: Int // Default value: 10 // Allowed filters: N/A DecisionRetryCriticalAttempts // DecisionRetryMaxAttempts is the max limit for decision retry attempts. 0 indicates infinite number of attempts. // KeyName: history.decisionRetryMaxAttempts // Value type: Int // Default value: 1000 // Allowed filters: DomainName DecisionRetryMaxAttempts // NormalDecisionScheduleToStartMaxAttempts is the maximum decision attempt for creating a scheduleToStart timeout // timer for normal (non-sticky) decision // KeyName: history.normalDecisionScheduleToStartMaxAttempts // Value type: Int // Default value: 0 // Allowed filters: DomainName NormalDecisionScheduleToStartMaxAttempts // MaxBufferedQueryCount indicates the maximum number of queries which can be buffered at a given time for a single workflow // KeyName: history.MaxBufferedQueryCount // Value type: Int // Default value: 1 // Allowed filters: N/A MaxBufferedQueryCount // MutableStateChecksumGenProbability is the probability [0-100] that checksum will be generated for mutable state // KeyName: history.mutableStateChecksumGenProbability // Value type: Int // Default value: 0 // Allowed filters: DomainName MutableStateChecksumGenProbability // MutableStateChecksumVerifyProbability is the probability [0-100] that checksum will be verified for mutable state // KeyName: history.mutableStateChecksumVerifyProbability // Value type: Int // Default value: 0 // Allowed filters: DomainName MutableStateChecksumVerifyProbability // TaskSchedulerMigrationRatio is the ratio of task that is migrated to new scheduler // KeyName: history.taskSchedulerMigrationRatio // Value type: Int // Default value: 0 // Allowed filters: N/A TaskSchedulerMigrationRatio // MaxActivityCountDispatchByDomain max # of activity tasks to dispatch to matching before creating transfer tasks. This is an performance optimization to skip activity scheduling efforts. // KeyName: history.activityDispatchForSyncMatchCountByDomain // Value type: Int // Default value: 0 // Allowed filters: DomainName MaxActivityCountDispatchByDomain // key for history replication // ReplicationTaskFetcherParallelism determines how many go routines we spin up for fetching tasks // KeyName: history.ReplicationTaskFetcherParallelism // Value type: Int // Default value: 1 // Allowed filters: N/A ReplicationTaskFetcherParallelism // ReplicationTaskProcessorErrorRetryMaxAttempts is the max retry attempts for applying replication tasks // KeyName: history.ReplicationTaskProcessorErrorRetryMaxAttempts // Value type: Int // Default value: 10 // Allowed filters: ShardID ReplicationTaskProcessorErrorRetryMaxAttempts // WorkflowIDExternalRPS is the rate limit per workflowID for external calls // KeyName: history.workflowIDExternalRPS // Value type: Int // Default value: UnlimitedRPS // Allowed filters: DomainName WorkflowIDExternalRPS // WorkflowIDInternalRPS is the rate limit per workflowID for internal calls // KeyName: history.workflowIDInternalRPS // Value type: Int // Default value: UnlimitedRPS // Allowed filters: DomainName WorkflowIDInternalRPS // key for worker // WorkerPersistenceMaxQPS is the max qps worker host can query DB // KeyName: worker.persistenceMaxQPS // Value type: Int // Default value: 500 // Allowed filters: N/A WorkerPersistenceMaxQPS // WorkerPersistenceGlobalMaxQPS is the max qps worker cluster can query DB // KeyName: worker.persistenceGlobalMaxQPS // Value type: Int // Default value: 0 // Allowed filters: N/A WorkerPersistenceGlobalMaxQPS // WorkerIndexerConcurrency is the max concurrent messages to be processed at any given time // KeyName: worker.indexerConcurrency // Value type: Int // Default value: 1000 // Allowed filters: N/A WorkerIndexerConcurrency // WorkerESProcessorNumOfWorkers is num of workers for esProcessor // KeyName: worker.ESProcessorNumOfWorkers // Value type: Int // Default value: 1 // Allowed filters: N/A WorkerESProcessorNumOfWorkers // WorkerESProcessorBulkActions is max number of requests in bulk for esProcessor // KeyName: worker.ESProcessorBulkActions // Value type: Int // Default value: 1000 // Allowed filters: N/A WorkerESProcessorBulkActions // WorkerESProcessorBulkSize is max total size of bulk in bytes for esProcessor // KeyName: worker.ESProcessorBulkSize // Value type: Int // Default value: 2<<24 // 16MB // Allowed filters: N/A WorkerESProcessorBulkSize // WorkerArchiverConcurrency is controls the number of coroutines handling archival work per archival workflow // KeyName: worker.ArchiverConcurrency // Value type: Int // Default value: 50 // Allowed filters: N/A WorkerArchiverConcurrency // WorkerArchivalsPerIteration is controls the number of archivals handled in each iteration of archival workflow // KeyName: worker.ArchivalsPerIteration // Value type: Int // Default value: 1000 // Allowed filters: N/A WorkerArchivalsPerIteration // WorkerThrottledLogRPS is the rate limit on number of log messages emitted per second for throttled logger // KeyName: worker.throttledLogRPS // Value type: Int // Default value: 20 // Allowed filters: N/A WorkerThrottledLogRPS // ScannerPersistenceMaxQPS is the maximum rate of persistence calls from worker.Scanner // KeyName: worker.scannerPersistenceMaxQPS // Value type: Int // Default value: 5 // Allowed filters: N/A ScannerPersistenceMaxQPS // ScannerGetOrphanTasksPageSize is the maximum number of orphans to delete in one batch // KeyName: worker.scannerGetOrphanTasksPageSize // Value type: Int // Default value: 1000 // Allowed filters: N/A ScannerGetOrphanTasksPageSize // ScannerBatchSizeForTasklistHandler is for: 1. max number of tasks to query per call(get tasks for tasklist) in the scavenger handler. 2. The scavenger then uses the return to decide if a tasklist can be deleted. It's better to keep it a relatively high number to let it be more efficient. // KeyName: worker.scannerBatchSizeForTasklistHandler // Value type: Int // Default value: 1000 // Allowed filters: N/A ScannerBatchSizeForTasklistHandler // ScannerMaxTasksProcessedPerTasklistJob is the number of tasks to process for a tasklist in each workflow run // KeyName: worker.scannerMaxTasksProcessedPerTasklistJob // Value type: Int // Default value: 256 // Allowed filters: N/A ScannerMaxTasksProcessedPerTasklistJob // ConcreteExecutionsScannerConcurrency indicates the concurrency of concrete execution scanner // KeyName: worker.executionsScannerConcurrency // Value type: Int // Default value: 25 // Allowed filters: N/A ConcreteExecutionsScannerConcurrency // ConcreteExecutionsScannerBlobstoreFlushThreshold indicates the flush threshold of blobstore in concrete execution scanner // KeyName: worker.executionsScannerBlobstoreFlushThreshold // Value type: Int // Default value: 100 // Allowed filters: N/A ConcreteExecutionsScannerBlobstoreFlushThreshold // ConcreteExecutionsScannerActivityBatchSize indicates the batch size of scanner activities // KeyName: worker.executionsScannerActivityBatchSize // Value type: Int // Default value: 25 // Allowed filters: N/A ConcreteExecutionsScannerActivityBatchSize // ConcreteExecutionsScannerPersistencePageSize indicates the page size of execution persistence fetches in concrete execution scanner // KeyName: worker.executionsScannerPersistencePageSize // Value type: Int // Default value: 1000 // Allowed filters: N/A ConcreteExecutionsScannerPersistencePageSize // CurrentExecutionsScannerConcurrency indicates the concurrency of current executions scanner // KeyName: worker.currentExecutionsConcurrency // Value type: Int // Default value: 25 // Allowed filters: N/A CurrentExecutionsScannerConcurrency // CurrentExecutionsScannerBlobstoreFlushThreshold indicates the flush threshold of blobstore in current executions scanner // KeyName: worker.currentExecutionsBlobstoreFlushThreshold // Value type: Int // Default value: 100 // Allowed filters: N/A CurrentExecutionsScannerBlobstoreFlushThreshold // CurrentExecutionsScannerActivityBatchSize indicates the batch size of scanner activities // KeyName: worker.currentExecutionsActivityBatchSize // Value type: Int // Default value: 25 // Allowed filters: N/A CurrentExecutionsScannerActivityBatchSize // CurrentExecutionsScannerPersistencePageSize indicates the page size of execution persistence fetches in current executions scanner // KeyName: worker.currentExecutionsPersistencePageSize // Value type: INt // Default value: 1000 // Allowed filters: N/A CurrentExecutionsScannerPersistencePageSize // TimersScannerConcurrency is the concurrency of timers scanner // KeyName: worker.timersScannerConcurrency // Value type: Int // Default value: 5 // Allowed filters: N/A TimersScannerConcurrency // TimersScannerPersistencePageSize is the page size of timers persistence fetches in timers scanner // KeyName: worker.timersScannerPersistencePageSize // Value type: Int // Default value: 1000 // Allowed filters: N/A TimersScannerPersistencePageSize // TimersScannerBlobstoreFlushThreshold is threshold to flush blob store // KeyName: worker.timersScannerBlobstoreFlushThreshold // Value type: Int // Default value: 100 // Allowed filters: N/A TimersScannerBlobstoreFlushThreshold // TimersScannerActivityBatchSize is TimersScannerActivityBatchSize // KeyName: worker.timersScannerActivityBatchSize // Value type: Int // Default value: 25 // Allowed filters: N/A TimersScannerActivityBatchSize // TimersScannerPeriodStart is interval start for fetching scheduled timers // KeyName: worker.timersScannerPeriodStart // Value type: Int // Default value: 24 // Allowed filters: N/A TimersScannerPeriodStart // TimersScannerPeriodEnd is interval end for fetching scheduled timers // KeyName: worker.timersScannerPeriodEnd // Value type: Int // Default value: 3 // Allowed filters: N/A TimersScannerPeriodEnd // ESAnalyzerMaxNumDomains defines how many domains to check // KeyName: worker.ESAnalyzerMaxNumDomains // Value type: int // Default value: 500 ESAnalyzerMaxNumDomains // ESAnalyzerMaxNumWorkflowTypes defines how many workflow types to check per domain // KeyName: worker.ESAnalyzerMaxNumWorkflowTypes // Value type: int // Default value: 100 ESAnalyzerMaxNumWorkflowTypes // ESAnalyzerNumWorkflowsToRefresh controls how many workflows per workflow type should be refreshed per workflow type // KeyName: worker.ESAnalyzerNumWorkflowsToRefresh // Value type: Int // Default value: 100 ESAnalyzerNumWorkflowsToRefresh // ESAnalyzerMinNumWorkflowsForAvg controls how many workflows to have at least to rely on workflow run time avg per type // KeyName: worker.ESAnalyzerMinNumWorkflowsForAvg // Value type: Int // Default value: 100 ESAnalyzerMinNumWorkflowsForAvg // VisibilityArchivalQueryMaxRangeInDays is the maximum number of days for a visibility archival query // KeyName: frontend.visibilityArchivalQueryMaxRangeInDays // Value type: Int // Default value: 60 // Allowed filters: N/A // TODO: https://github.com/uber/cadence/issues/3861 // Note: not currently used in open-source VisibilityArchivalQueryMaxRangeInDays // VisibilityArchivalQueryMaxQPS is the timeout for a visibility archival query // KeyName: frontend.visibilityArchivalQueryMaxQPS // Value type: Int // Default value: 1 // Allowed filters: N/A // TODO: https://github.com/uber/cadence/issues/3861 // Note: not currently used in open-source VisibilityArchivalQueryMaxQPS // WorkflowDeletionJitterRange defines the duration in minutes for workflow close tasks jittering // KeyName: system.workflowDeletionJitterRange // Value type: Int // Default value: 60 WorkflowDeletionJitterRange // SampleLoggingRate defines the rate we want sampled logs to be logged at // KeyName: system.sampleLoggingRate // Value type: Int // Default value: 100 SampleLoggingRate // LargeShardHistorySizeMetricThreshold defines the threshold for what constitutes a large history storage size to alert on // KeyName: system.largeShardHistorySizeMetricThreshold // Value type: Int // Default value: 10485760 (10mb) LargeShardHistorySizeMetricThreshold // LargeShardHistoryEventMetricThreshold defines the threshold for what constitutes a large history event size to alert on // KeyName: system.largeShardHistoryEventMetricThreshold // Value type: Int // Default value: 50 * 1024 LargeShardHistoryEventMetricThreshold // LargeShardHistoryBlobMetricThreshold defines the threshold for what constitutes a large history blob size to alert on // KeyName: system.largeShardHistoryBlobMetricThreshold // Value type: Int // Default value: 262144 (1/4mb) LargeShardHistoryBlobMetricThreshold // IsolationGroupStateUpdateRetryAttempts // KeyName: system.isolationGroupStateUpdateRetryAttempts // Value type: int // Default value: 2 IsolationGroupStateUpdateRetryAttempts // DeleteHistoryEventContextTimeout in seconds // KeyName: system.deleteHistoryEventContextTimeout // Value type: Int // Default value: 30 DeleteHistoryEventContextTimeout // QueueMaxVirtualQueueCount is the max number of virtual queues // KeyName: history.queueMaxVirtualQueueCount // Value type: Int // Default value: 2 // Allowed filters: N/A QueueMaxVirtualQueueCount // ShardDistributorMaxEtcdTxnOps is the maximum number of operations per etcd transaction. // etcd enforces a server-side limit (--max-txn-ops, default 128). // This value must not exceed the etcd cluster's configured limit. // KeyName: shardDistributor.maxEtcdTxnOps // Value type: Int // Default value: 128 // Allowed filters: N/A ShardDistributorMaxEtcdTxnOps // LastIntKey must be the last one in this const group LastIntKey ) const ( UnknownBoolKey BoolKey = iota // key for tests TestGetBoolPropertyKey TestGetBoolPropertyFilteredByDomainIDKey TestGetBoolPropertyFilteredByTaskListInfoKey TestGetBoolPropertyFilteredByDomainKey TestGetBoolPropertyFilteredByDomainIDAndWorkflowIDKey TestGetBoolPropertyFilteredByShardIDKey // key for common & admin // EnableVisibilitySampling is key for enable visibility sampling for basic(DB based) visibility // KeyName: system.enableVisibilitySampling // Value type: Bool // Default value: false // Allowed filters: N/A EnableVisibilitySampling // EnableReadFromClosedExecutionV2 is key for enable read from cadence_visibility.closed_executions_v2 // KeyName: system.enableReadFromClosedExecutionV2 // Value type: Bool // Default value: false // Allowed filters: N/A EnableReadFromClosedExecutionV2 // EnableLogCustomerQueryParameter is key for enable log customer query parameters // KeyName: system.enableLogCustomerQueryParameter // Value type: Bool // Default value: false // Allowed filters: DomainName EnableLogCustomerQueryParameter // EmitShardDiffLog is whether emit the shard diff log // KeyName: history.emitShardDiffLog // Value type: Bool // Default value: false // Allowed filters: N/A EmitShardDiffLog // DisableListVisibilityByFilter is config to disable list open/close workflow using filter // KeyName: frontend.disableListVisibilityByFilter // Value type: Bool // Default value: false // Allowed filters: DomainName DisableListVisibilityByFilter // EnableReadFromHistoryArchival is key for enabling reading history from archival store // KeyName: system.enableReadFromHistoryArchival // Value type: Bool // Default value: true // Allowed filters: N/A EnableReadFromHistoryArchival // EnableReadFromVisibilityArchival is key for enabling reading visibility from archival store to override the value from static config. // KeyName: system.enableReadFromVisibilityArchival // Value type: Bool // Default value: true // Allowed filters: N/A EnableReadFromVisibilityArchival // EnableDomainNotActiveAutoForwarding decides requests form which domain will be forwarded to active cluster if domain is not active in current cluster. // Only when "selected-api-forwarding" or "all-domain-apis-forwarding" is the policy in ClusterRedirectionPolicy(in static config). // If the policy is "noop"(default) this flag is not doing anything. // KeyName: system.enableDomainNotActiveAutoForwarding // Value type: Bool // Default value: true (meaning all domains are allowed to use the policy specified in static config) // Allowed filters: DomainName EnableDomainNotActiveAutoForwarding // EnableGracefulFailover is whether enabling graceful failover // KeyName: system.enableGracefulFailover // Value type: Bool // Default value: true // Allowed filters: N/A EnableGracefulFailover // DisallowQuery is the key to disallow query for a domain // KeyName: system.disallowQuery // Value type: Bool // Default value: false // Allowed filters: DomainName DisallowQuery // EnableDebugMode is for enabling debugging components, logs and metrics // KeyName: system.enableDebugMode // Value type: Bool // Default value: false // Allowed filters: N/A EnableDebugMode // EnableGRPCOutbound is the key for enabling outbound GRPC traffic // KeyName: system.enableGRPCOutbound // Value type: Bool // Default value: true // Allowed filters: N/A EnableGRPCOutbound // EnableSQLAsyncTransaction is the key for enabling async transaction // KeyName: system.enableSQLAsyncTransaction // Value type: Bool // Default value: false // Allowed filters: N/A EnableSQLAsyncTransaction // EnableConnectionRetainingDirectChooser is the key for enabling connection retaining direct yarpc chooser // KeyName: system.enableConnectionRetainingDirectChooser // Value type: Bool // Default value: false // Allowed filters: N/A EnableConnectionRetainingDirectChooser // EnableDomainAuditLogging enables audit logging for a domain to the domain audit log table // KeyName: system.enableDomainAuditLogging // Value type: Bool // Default value: false // Allowed filters: N/A EnableDomainAuditLogging // key for frontend // EnableClientVersionCheck is enables client version check for frontend // KeyName: frontend.enableClientVersionCheck // Value type: Bool // Default value: false // Allowed filters: N/A EnableClientVersionCheck // SendRawWorkflowHistory is whether to enable raw history retrieving // KeyName: frontend.sendRawWorkflowHistory // Value type: Bool // Default value: false // Allowed filters: DomainName SendRawWorkflowHistory // FrontendEmitSignalNameMetricsTag enables emitting signal name tag in metrics in frontend client // KeyName: frontend.emitSignalNameMetricsTag // Value type: Bool // Default value: false // Allowed filters: DomainName FrontendEmitSignalNameMetricsTag // EnableQueryAttributeValidation enables validation of queries' search attributes against the dynamic config whitelist // Keyname: frontend.enableQueryAttributeValidation // Value type: Bool // Default value: true // Allowed filters: N/A EnableQueryAttributeValidation // key for matching // MatchingEnableSyncMatch is to enable sync match // KeyName: matching.enableSyncMatch // Value type: Bool // Default value: true // Allowed filters: DomainName,TasklistName,TaskType MatchingEnableSyncMatch // MatchingEnableTaskInfoLogByDomainID is enables info level logs for decision/activity task based on the request domainID // KeyName: matching.enableTaskInfoLogByDomainID // Value type: Bool // Default value: false // Allowed filters: DomainID MatchingEnableTaskInfoLogByDomainID // MatchingEnableTasklistGuardAgainstOwnershipShardLoss // enables guards to prevent tasklists from processing if there is any detection that the host // no longer is active or owns the shard // KeyName: matching.enableTasklistGuardAgainstOwnershipLoss // Value type: Bool // Default value: false // Allowed filters: N/A MatchingEnableTasklistGuardAgainstOwnershipShardLoss // MatchingEnableStandbyTaskCompletion is to enable completion of tasks in the domain's passive side // KeyName: matching.enableStandbyTaskCompletion // Value type: Bool // Default value: true // Allowed filters: DomainName,TasklistName,TaskType MatchingEnableStandbyTaskCompletion // MatchingEnableGetNumberOfPartitionsFromCache is to enable getting number of partitions from cache instead of dynamic config // KeyName: matching.enableGetNumberOfPartitionsFromCache // Value type: Bool // Default value: false // Allowed filters: DomainName,TasklistName,TaskType MatchingEnableGetNumberOfPartitionsFromCache // MatchingEnableAdaptiveScaler is to enable adaptive task list scaling // KeyName: matching.enableAdaptiveScaler // Value type: Bool // Default value: false // Allowed filters: DomainName,TasklistName,TaskType MatchingEnableAdaptiveScaler // MatchingEnablePartitionEmptyCheck enables using TaskListStatus.empty to check if a partition is empty // KeyName: matching.enablePartitionEmptyCheck // Value type: Bool // Default value: false // Allowed filters: DomainName,TasklistName,TaskType MatchingEnablePartitionEmptyCheck // MatchingEnableReturnAllTaskListKinds returns TaskLists of all kinds when GetTaskListsByDomain is called. Useful in testing Cadence // KeyName: matching.matchingReturnAllTaskListKinds // Value type: Bool // Default value: false // Allowed filters: N/A MatchingEnableReturnAllTaskListKinds // key for history // EventsCacheGlobalEnable is enables global cache over all history shards // KeyName: history.eventsCacheGlobalEnable // Value type: Bool // Default value: false // Allowed filters: N/A EventsCacheGlobalEnable // QueueProcessorEnableSplit indicates whether processing queue split policy should be enabled // KeyName: history.queueProcessorEnableSplit // Value type: Bool // Default value: false // Allowed filters: N/A QueueProcessorEnableSplit // QueueProcessorEnableRandomSplitByDomainID indicates whether random queue split policy should be enabled for a domain // KeyName: history.queueProcessorEnableRandomSplitByDomainID // Value type: Bool // Default value: false // Allowed filters: DomainID QueueProcessorEnableRandomSplitByDomainID // QueueProcessorEnablePendingTaskSplitByDomainID indicates whether pending task split policy should be enabled // KeyName: history.queueProcessorEnablePendingTaskSplitByDomainID // Value type: Bool // Default value: false // Allowed filters: DomainID QueueProcessorEnablePendingTaskSplitByDomainID // QueueProcessorEnableStuckTaskSplitByDomainID indicates whether stuck task split policy should be enabled // KeyName: history.queueProcessorEnableStuckTaskSplitByDomainID // Value type: Bool // Default value: false // Allowed filters: DomainID QueueProcessorEnableStuckTaskSplitByDomainID // QueueProcessorEnablePersistQueueStates indicates whether processing queue states should be persisted // KeyName: history.queueProcessorEnablePersistQueueStates // Value type: Bool // Default value: true // Allowed filters: N/A QueueProcessorEnablePersistQueueStates // QueueProcessorEnableLoadQueueStates indicates whether processing queue states should be loaded // KeyName: history.queueProcessorEnableLoadQueueStates // Value type: Bool // Default value: true // Allowed filters: N/A QueueProcessorEnableLoadQueueStates // QueueProcessorEnableGracefulSyncShutdown indicates whether processing queue should be shutdown gracefully & synchronously // KeyName: history.queueProcessorEnableGracefulSyncShutdown // Value type: Bool // Default value: false // Allowed filters: N/A QueueProcessorEnableGracefulSyncShutdown // TransferProcessorEnableValidator is whether validator should be enabled for transferQueueProcessor // KeyName: history.transferProcessorEnableValidator // Value type: Bool // Default value: false // Allowed filters: N/A TransferProcessorEnableValidator // TaskSchedulerEnableRateLimiter indicates whether the task scheduler rate limiter is enabled // KeyName: history.taskSchedulerEnableRateLimiter // Value type: Bool // Default value: false // Allowed filters: N/A TaskSchedulerEnableRateLimiter // TaskSchedulerEnableRateLimiterShadowMode indicates whether the task scheduler rate limiter is in shadow mode // KeyName: history.taskSchedulerEnableRateLimiterShadowMode // Value type: Bool // Default value: true // Allowed filters: DomainName TaskSchedulerEnableRateLimiterShadowMode // TaskSchedulerEnableMigration indicates whether the task scheduler migration is enabled // KeyName: history.taskSchedulerEnableMigration // Value type: Bool // Default value: false // Allowed filters: N/A TaskSchedulerEnableMigration // EnableAdminProtection is whether to enable admin checking // KeyName: history.enableAdminProtection // Value type: Bool // Default value: false // Allowed filters: N/A EnableAdminProtection // EnableParentClosePolicy is whether to ParentClosePolicy // KeyName: history.enableParentClosePolicy // Value type: Bool // Default value: true // Allowed filters: DomainName EnableParentClosePolicy // EnableDropStuckTaskByDomainID is whether stuck timer/transfer task should be dropped for a domain // KeyName: history.DropStuckTaskByDomain // Value type: Bool // Default value: false // Allowed filters: DomainID EnableDropStuckTaskByDomainID // EnableConsistentQuery indicates if consistent query is enabled for the cluster // KeyName: history.EnableConsistentQuery // Value type: Bool // Default value: true // Allowed filters: N/A EnableConsistentQuery // EnableConsistentQueryByDomain indicates if consistent query is enabled for a domain // KeyName: history.EnableConsistentQueryByDomain // Value type: Bool // Default value: false // Allowed filters: DomainName EnableConsistentQueryByDomain // EnableContextHeaderInVisibility is key for enable context header in visibility // KeyName: history.enableContextHeaderInVisibility // Value type: Bool // Default value: false // Allowed filters: DomainName EnableContextHeaderInVisibility // EnableCrossClusterOperationsForDomain indicates if cross cluster operations can be scheduled for a domain // KeyName: history.enableCrossClusterOperations // Value type: Bool // Default value: false // Allowed filters: DomainName EnableCrossClusterOperationsForDomain // EnableHistoryCorruptionCheck enables additional sanity check for corrupted history. This allows early catches of DB corruptions but potiantally increased latency. // KeyName: history.enableHistoryCorruptionCheck // Value type: Bool // Default value: false // Allowed filters: DomainName EnableHistoryCorruptionCheck // EnableActivityLocalDispatchByDomain is allows worker to dispatch activity tasks through local tunnel after decisions are made. This is an performance optimization to skip activity scheduling efforts // KeyName: history.enableActivityLocalDispatchByDomain // Value type: Bool // Default value: true // Allowed filters: DomainName EnableActivityLocalDispatchByDomain // HistoryEnableTaskInfoLogByDomainID is enables info level logs for decision/activity task based on the request domainID // KeyName: history.enableTaskInfoLogByDomainID // Value type: Bool // Default value: false // Allowed filters: DomainID HistoryEnableTaskInfoLogByDomainID // EnableReplicationTaskGeneration is the flag to control replication generation // KeyName: history.enableReplicationTaskGeneration // Value type: Bool // Default value: true // Allowed filters: DomainID, WorkflowID EnableReplicationTaskGeneration // UseNewInitialFailoverVersion is a switch to issue a failover version based on the minFailoverVersion // rather than the default initialFailoverVersion. USed as a per-domain migration switch // KeyName: history.useNewInitialFailoverVersion // Value type: Bool // Default value: false // Allowed filters: N/A UseNewInitialFailoverVersion // EnableRecordWorkflowExecutionUninitialized enables record workflow execution uninitialized state in ElasticSearch // KeyName: history.EnableRecordWorkflowExecutionUninitialized // Value type: Bool // Default value: true // Allowed filters: DomainName EnableRecordWorkflowExecutionUninitialized // EnableCleanupOrphanedHistoryBranchOnWorkflowCreation enables cleanup of orphaned history branches when CreateWorkflowExecution fails // KeyName: history.enableCleanupOrphanedHistoryBranchOnWorkflowCreation // Value type: Bool // Default value: false // Allowed filters: N/A EnableCleanupOrphanedHistoryBranchOnWorkflowCreation // AllowArchivingIncompleteHistory will continue on when seeing some error like history mutated(usually caused by database consistency issues) // KeyName: worker.AllowArchivingIncompleteHistory // Value type: Bool // Default value: false // Allowed filters: N/A AllowArchivingIncompleteHistory // EnableCleaningOrphanTaskInTasklistScavenger indicates if enabling the scanner to clean up orphan tasks // Only implemented for single SQL database. TODO https://github.com/uber/cadence/issues/4064 for supporting multiple/sharded SQL database and NoSQL // KeyName: worker.enableCleaningOrphanTaskInTasklistScavenger // Value type: Bool // Default value: false // Allowed filters: N/A EnableCleaningOrphanTaskInTasklistScavenger // TaskListScannerEnabled indicates if task list scanner should be started as part of worker.Scanner // KeyName: worker.taskListScannerEnabled // Value type: Bool // Default value: true // Allowed filters: N/A TaskListScannerEnabled // HistoryScannerEnabled indicates if history scanner should be started as part of worker.Scanner // KeyName: worker.historyScannerEnabled // Value type: Bool // Default value: false // Allowed filters: N/A HistoryScannerEnabled // ConcreteExecutionsScannerEnabled indicates if executions scanner should be started as part of worker.Scanner // KeyName: worker.executionsScannerEnabled // Value type: Bool // Default value: false // Allowed filters: N/A ConcreteExecutionsScannerEnabled // ConcreteExecutionsScannerInvariantCollectionMutableState indicates if mutable state invariant checks should be run // KeyName: worker.executionsScannerInvariantCollectionMutableState // Value type: Bool // Default value: true // Allowed filters: N/A ConcreteExecutionsScannerInvariantCollectionMutableState // ConcreteExecutionsScannerInvariantCollectionHistory indicates if history invariant checks should be run // KeyName: worker.executionsScannerInvariantCollectionHistory // Value type: Bool // Default value: true // Allowed filters: N/A ConcreteExecutionsScannerInvariantCollectionHistory // ConcreteExecutionsFixerInvariantCollectionStale indicates if the stale workflow invariant should be run // KeyName: worker.executionsFixerInvariantCollectionStale // Value type: Bool // Default value: false // Allowed filters: N/A ConcreteExecutionsFixerInvariantCollectionStale // ConcreteExecutionsFixerInvariantCollectionMutableState indicates if mutable state invariant checks should be run // KeyName: worker.executionsFixerInvariantCollectionMutableState // Value type: Bool // Default value: true // Allowed filters: N/A ConcreteExecutionsFixerInvariantCollectionMutableState // ConcreteExecutionsFixerInvariantCollectionHistory indicates if history invariant checks should be run // KeyName: worker.executionsFixerInvariantCollectionHistory // Value type: Bool // Default value: true // Allowed filters: N/A ConcreteExecutionsFixerInvariantCollectionHistory // ConcreteExecutionsScannerInvariantCollectionStale indicates if the stale workflow invariant should be run // KeyName: worker.executionsScannerInvariantCollectionStale // Value type: Bool // Default value: false // Allowed filters: N/A ConcreteExecutionsScannerInvariantCollectionStale // CurrentExecutionsScannerEnabled indicates if current executions scanner should be started as part of worker.Scanner // KeyName: worker.currentExecutionsScannerEnabled // Value type: Bool // Default value: false // Allowed filters: N/A CurrentExecutionsScannerEnabled // CurrentExecutionsScannerInvariantCollectionHistory indicates if history invariant checks should be run // KeyName: worker.currentExecutionsScannerInvariantCollectionHistory // Value type: Bool // Default value: true // Allowed filters: N/A CurrentExecutionsScannerInvariantCollectionHistory // CurrentExecutionsScannerInvariantCollectionMutableState indicates if mutable state invariant checks should be run // KeyName: worker.currentExecutionsInvariantCollectionMutableState // Value type: Bool // Default value: true // Allowed filters: N/A CurrentExecutionsScannerInvariantCollectionMutableState // EnableBatcher is decides whether start batcher in our worker // KeyName: worker.enableBatcher // Value type: Bool // Default value: true // Allowed filters: N/A EnableBatcher // EnableScheduler decides whether to start the scheduler worker for cron-based scheduling // KeyName: worker.enableScheduler // Value type: Bool // Default value: false // Allowed filters: N/A EnableScheduler // EnableParentClosePolicyWorker decides whether or not enable system workers for processing parent close policy task // KeyName: system.enableParentClosePolicyWorker // Value type: Bool // Default value: true // Allowed filters: N/A EnableParentClosePolicyWorker // EnableESAnalyzer decides whether to enable system workers for processing ElasticSearch Analyzer // KeyName: system.enableESAnalyzer // Value type: Bool // Default value: false // Allowed filters: N/A EnableESAnalyzer // EnableAsyncWorkflowConsumption decides whether to enable system workers for processing async workflows // KeyName: worker.enableAsyncWorkflowConsumption // Value type: Bool // Default value: true // Allowed filters: N/A EnableAsyncWorkflowConsumption // EnableStickyQuery indicates if sticky query should be enabled per domain // KeyName: system.enableStickyQuery // Value type: Bool // Default value: true // Allowed filters: DomainName EnableStickyQuery // EnableFailoverManager indicates if failover manager is enabled // KeyName: system.enableFailoverManager // Value type: Bool // Default value: true // Allowed filters: N/A EnableFailoverManager // ConcreteExecutionFixerDomainAllow is which domains are allowed to be fixed by concrete fixer workflow // KeyName: worker.concreteExecutionFixerDomainAllow // Value type: Bool // Default value: false // Allowed filters: DomainName ConcreteExecutionFixerDomainAllow // CurrentExecutionFixerDomainAllow is which domains are allowed to be fixed by current fixer workflow // KeyName: worker.currentExecutionFixerDomainAllow // Value type: Bool // Default value: false // Allowed filters: DomainName CurrentExecutionFixerDomainAllow // TimersScannerEnabled is if timers scanner should be started as part of worker.Scanner // KeyName: worker.timersScannerEnabled // Value type: Bool // Default value: false // Allowed filters: N/A TimersScannerEnabled // TimersFixerEnabled is if timers fixer should be started as part of worker.Scanner // KeyName: worker.timersFixerEnabled // Value type: Bool // Default value: false // Allowed filters: N/A TimersFixerEnabled // TimersFixerDomainAllow is which domains are allowed to be fixed by timer fixer workflow // KeyName: worker.timersFixerDomainAllow // Value type: Bool // Default value: false // Allowed filters: DomainName TimersFixerDomainAllow // ConcreteExecutionFixerEnabled is if concrete execution fixer workflow is enabled // KeyName: worker.concreteExecutionFixerEnabled // Value type: Bool // Default value: false // Allowed filters: N/A ConcreteExecutionFixerEnabled // CurrentExecutionFixerEnabled is if current execution fixer workflow is enabled // KeyName: worker.currentExecutionFixerEnabled // Value type: Bool // Default value: false // Allowed filters: N/A CurrentExecutionFixerEnabled // EnableAuthorization is the key to enable authorization for a domain, only for extension binary: // KeyName: N/A // Default value: N/A // TODO: https://github.com/uber/cadence/issues/3861 // Note: not currently used in open-source EnableAuthorization // EnableServiceAuthorization is the key to enable authorization for a service, only for extension binary: // KeyName: N/A // Default value: N/A // TODO: https://github.com/uber/cadence/issues/3861 // Note: not currently used in open-source EnableServiceAuthorization // EnableServiceAuthorizationLogOnly is the key to enable authorization logging for a service, only for extension binary: // KeyName: N/A // Default value: N/A // TODO: https://github.com/uber/cadence/issues/3861 // Note: not currently used in open-source EnableServiceAuthorizationLogOnly // ESAnalyzerPause defines if we want to dynamically pause the analyzer workflow // KeyName: worker.ESAnalyzerPause // Value type: bool // Default value: false ESAnalyzerPause // EnableArchivalCompression indicates whether blobs are compressed before they are archived // KeyName: N/A // Default value: N/A // TODO: https://github.com/uber/cadence/issues/3861 // Note: not currently used in open-source EnableArchivalCompression // ESAnalyzerEnableAvgDurationBasedChecks controls if we want to enable avg duration based task refreshes // KeyName: worker.ESAnalyzerEnableAvgDurationBasedChecks // Value type: Bool // Default value: false ESAnalyzerEnableAvgDurationBasedChecks // Lockdown defines if we want to allow failovers of domains to this cluster // KeyName: system.Lockdown // Value type: bool // Default value: false Lockdown // EnablePendingActivityValidation is feature flag if pending activity count validation is enabled // KeyName: limit.pendingActivityCount.enabled // Value type: bool // Default value: false EnablePendingActivityValidation // EnableCassandraAllConsistencyLevelDelete uses all consistency level for Cassandra delete operations // KeyName: system.enableCassandraAllConsistencyLevelDelete // Value type: Bool // Default value: false // Allowed filters: N/A EnableCassandraAllConsistencyLevelDelete // EnableTasklistIsolation Is a feature to enable subdivision of workflows by units called 'isolation-groups' // and to control their movement and blast radius. This has some nontrivial operational overheads in management // and a good understanding of poller distribution, so probably not worth enabling unless it's well understood. // KeyName: system.enableTasklistIsolation // Value type: bool // Default value: false EnableTasklistIsolation // EnablePartitionIsolationGroupAssignment enables assigning isolation groups to individual TaskList partitions // KeyName: matching.enablePartitionIsolationGroupAssignment // Value type: bool // Default value: false // Allowed filters: DomainName,TasklistName,TaskType EnablePartitionIsolationGroupAssignment // EnableShardIDMetrics turns on or off shardId metrics // KeyName: system.enableShardIDMetrics // Value type: Bool // Default value: true EnableShardIDMetrics // EnableTimerDebugLogByDomainID enables log for debugging timer task issue by domain // KeyName: history.enableTimerDebugLogByDomainID // Value type: Bool // Default value: false // Allowed filters: DomainID EnableTimerDebugLogByDomainID // EnableRetryForChecksumFailure enables retry if mutable state checksum verification fails // KeyName: history.enableMutableStateChecksumFailureRetry // Value type: Bool // Default value: false // Allowed filters: DomainName EnableRetryForChecksumFailure // EnableStrongIdempotency enables strong idempotency for APIs // KeyName: history.enableStrongIdempotency // Value type: Bool // Default value: false // Allowed filters: DomainName EnableStrongIdempotency // EnableStrongIdempotencySanityCheck enables sanity check for strong idempotency // KeyName: history.enableStrongIdempotencySanityCheck // Value type: Bool // Default value: false // Allowed filters: DomainName EnableStrongIdempotencySanityCheck // MatchingEnableClientAutoConfig enables auto config for domain and tasklist on client (worker) side // KeyName: matching.enableClientAutoConfig // Value type: Bool // Default value: false // Allowed filters: DomainName,TasklistName,TaskType MatchingEnableClientAutoConfig // EnableNoSQLHistoryTaskDualWriteMode is to enable dual write of history events // KeyName: history.enableNoSQLHistoryTaskDualWrite // Value type: Bool // Default value: false // Allowed filters: N/A EnableNoSQLHistoryTaskDualWriteMode // ReadNoSQLHistoryTaskFromDataBlob is to read history tasks from data blob // KeyName: history.readNoSQLHistoryTaskFromDataBlob // Value type: Bool // Default value: false // Allowed filters: N/A ReadNoSQLHistoryTaskFromDataBlob // ReadNoSQLShardFromDataBlob is to read shards from data blob // KeyName: history.readNoSQLShardFromDataBlob // Value type: Bool // Default value: true // Allowed filters: N/A ReadNoSQLShardFromDataBlob // EnableSizeBasedHistoryExecutionCache is the feature flag to enable size based cache for execution cache // KeyName: history.enableSizeBasedHistoryExecutionCache // Value type: Bool // Default value: false EnableSizeBasedHistoryExecutionCache // EnableSizeBasedHistoryEventCache is the feature flag to enable size based cache for event cache // KeyName: history.enableSizeBasedHistoryEventCache // Value type: Bool // Default value: false EnableSizeBasedHistoryEventCache // DisableTransferFailoverQueue is to disable transfer failover queue // KeyName: history.disableTransferFailoverQueue // Value type: Bool // Default value: false // Allowed filters: N/A DisableTransferFailoverQueue // DisableTimerFailoverQueue is to disable timer failover queue // KeyName: history.disableTimerFailoverQueue // Value type: Bool // Default value: false // Allowed filters: N/A DisableTimerFailoverQueue // EnableTransferQueueV2 is to enable transfer queue v2 // KeyName: history.enableTransferQueueV2 // Value type: Bool // Default value: false // Allowed filters: ShardID EnableTransferQueueV2 // EnableTimerQueueV2 is to enable timer queue v2 // KeyName: history.enableTimerQueueV2 // Value type: Bool // Default value: false // Allowed filters: ShardID EnableTimerQueueV2 // EnableTransferQueueV2PendingTaskCountAlert is to enable transfer queue v2 pending task count alert // KeyName: history.enableTransferQueueV2PendingTaskCountAlert // Value type: Bool // Default value: false // Allowed filters: ShardID EnableTransferQueueV2PendingTaskCountAlert // EnableTimerQueueV2PendingTaskCountAlert is to enable timer queue v2 pending task count alert // KeyName: history.enableTimerQueueV2PendingTaskCountAlert // Value type: Bool // Default value: false // Allowed filters: ShardID EnableTimerQueueV2PendingTaskCountAlert // EnableActiveClusterSelectionPolicyInStartWorkflow is to enable active cluster selection policy in start workflow requests for a domain // KeyName: frontend.enableActiveClusterSelectionPolicyInStartWorkflow // Value type: Bool // Default value: false // Allowed filters: DomainName EnableActiveClusterSelectionPolicyInStartWorkflow // EnforceDecisionTaskAttempts is the key for enforcing decision retry attempts limit in case of timeouts. // KeyName: history.enforceDecisionTaskAttempts // Value type: Bool // Default value: false // Allowed filters: DomainName EnforceDecisionTaskAttempts // MatchingExcludeShortLivedTaskListsFromShardManager excludes short-lived task lists (e.g. bits task lists and sticky task lists) // from using the shard manager to handle these shards. These short-lived task lists are assigned using hash_ring. // KeyName: matching.excludeShortLivedTaskListsFromShardManager // Value type: Bool // Default value: true // Allowed filters: N/A MatchingExcludeShortLivedTaskListsFromShardManager // EnableHierarchicalWeightedRoundRobinTaskScheduler is to enable hierarchical weighted round robin task scheduler // KeyName: history.enableHierarchicalWeightedRoundRobinTaskScheduler // Value type: Bool // Default value: false // Allowed filters: N/A EnableHierarchicalWeightedRoundRobinTaskScheduler // EnableTaskListAwareTaskSchedulerByDomain is to enable task list aware task scheduler by domain // KeyName: history.enableTaskListAwareTaskSchedulerByDomain // Value type: Bool // Default value: false // Allowed filters: DomainName EnableTaskListAwareTaskSchedulerByDomain // LastBoolKey must be the last one in this const group LastBoolKey ) const ( UnknownFloatKey FloatKey = iota // key for tests TestGetFloat64PropertyKey TestGetFloat64PropertyFilteredByShardIDKey // key for common & admin // PersistenceErrorInjectionRate is rate for injecting random error in persistence // KeyName: system.persistenceErrorInjectionRate // Value type: Float64 // Default value: 0 // Allowed filters: N/A PersistenceErrorInjectionRate // AdminErrorInjectionRate is the rate for injecting random error in admin client // KeyName: admin.errorInjectionRate // Value type: Float64 // Default value: 0 // Allowed filters: N/A AdminErrorInjectionRate // key for frontend // DomainFailoverRefreshTimerJitterCoefficient is the jitter for domain failover refresh timer jitter // KeyName: frontend.domainFailoverRefreshTimerJitterCoefficient // Value type: Float64 // Default value: 0.1 // Allowed filters: N/A DomainFailoverRefreshTimerJitterCoefficient // FrontendErrorInjectionRate is rate for injecting random error in frontend client // KeyName: frontend.errorInjectionRate // Value type: Float64 // Default value: 0 // Allowed filters: N/A FrontendErrorInjectionRate // key for matching // MatchingErrorInjectionRate is rate for injecting random error in matching client // KeyName: matching.errorInjectionRate // Value type: Float64 // Default value: 0 // Allowed filters: N/A MatchingErrorInjectionRate // key for history // QueueProcessorRandomSplitProbability is the probability for a domain to be split to a new processing queue // KeyName: history.queueProcessorRandomSplitProbability // Value type: Float64 // Default value: 0.01 // Allowed filters: N/A QueueProcessorRandomSplitProbability // QueueProcessorPollBackoffIntervalJitterCoefficient is backoff interval jitter coefficient // KeyName: history.queueProcessorPollBackoffIntervalJitterCoefficient // Value type: Float64 // Default value: 0.15 // Allowed filters: N/A QueueProcessorPollBackoffIntervalJitterCoefficient // TimerProcessorUpdateAckIntervalJitterCoefficient is the update interval jitter coefficient // KeyName: history.timerProcessorUpdateAckIntervalJitterCoefficient // Value type: Float64 // Default value: 0.15 // Allowed filters: N/A TimerProcessorUpdateAckIntervalJitterCoefficient // TimerProcessorMaxPollIntervalJitterCoefficient is the max poll interval jitter coefficient // KeyName: history.timerProcessorMaxPollIntervalJitterCoefficient // Value type: Float64 // Default value: 0.15 // Allowed filters: N/A TimerProcessorMaxPollIntervalJitterCoefficient // TimerProcessorSplitQueueIntervalJitterCoefficient is the split processing queue interval jitter coefficient // KeyName: history.timerProcessorSplitQueueIntervalJitterCoefficient // Value type: Float64 // Default value: 0.15 // Allowed filters: N/A TimerProcessorSplitQueueIntervalJitterCoefficient // TransferProcessorMaxPollIntervalJitterCoefficient is the max poll interval jitter coefficient // KeyName: history.transferProcessorMaxPollIntervalJitterCoefficient // Value type: Float64 // Default value: 0.15 // Allowed filters: N/A TransferProcessorMaxPollIntervalJitterCoefficient // TransferProcessorSplitQueueIntervalJitterCoefficient is the split processing queue interval jitter coefficient // KeyName: history.transferProcessorSplitQueueIntervalJitterCoefficient // Value type: Float64 // Default value: 0.15 // Allowed filters: N/A TransferProcessorSplitQueueIntervalJitterCoefficient // TransferProcessorUpdateAckIntervalJitterCoefficient is the update interval jitter coefficient // KeyName: history.transferProcessorUpdateAckIntervalJitterCoefficient // Value type: Float64 // Default value: 0.15 // Allowed filters: N/A TransferProcessorUpdateAckIntervalJitterCoefficient // ReplicationTaskProcessorCleanupJitterCoefficient is the jitter for cleanup timer // KeyName: history.ReplicationTaskProcessorCleanupJitterCoefficient // Value type: Float64 // Default value: 0.15 // Allowed filters: ShardID ReplicationTaskProcessorCleanupJitterCoefficient // ReplicationTaskProcessorStartWaitJitterCoefficient is the jitter for batch start wait timer // KeyName: history.ReplicationTaskProcessorStartWaitJitterCoefficient // Value type: Float64 // Default value: 0.9 // Allowed filters: ShardID ReplicationTaskProcessorStartWaitJitterCoefficient // ReplicationTaskProcessorHostQPS is the qps of task processing rate limiter on host level // KeyName: history.ReplicationTaskProcessorHostQPS // Value type: Float64 // Default value: 1500 // Allowed filters: N/A ReplicationTaskProcessorHostQPS // ReplicationTaskProcessorShardQPS is the qps of task processing rate limiter on shard level // KeyName: history.ReplicationTaskProcessorShardQPS // Value type: Float64 // Default value: 5 // Allowed filters: N/A ReplicationTaskProcessorShardQPS // ReplicationTaskGenerationQPS is the wait time between each replication task generation qps // KeyName: history.ReplicationTaskGenerationQPS // Value type: Float64 // Default value: 100 // Allowed filters: N/A ReplicationTaskGenerationQPS // MutableStateChecksumInvalidateBefore is the epoch timestamp before which all checksums are to be discarded // KeyName: history.mutableStateChecksumInvalidateBefore // Value type: Float64 // Default value: 0 // Allowed filters: N/A MutableStateChecksumInvalidateBefore // NotifyFailoverMarkerTimerJitterCoefficient is the jitter for failover marker notifier timer // KeyName: history.NotifyFailoverMarkerTimerJitterCoefficient // Value type: Float64 // Default value: 0.15 // Allowed filters: N/A NotifyFailoverMarkerTimerJitterCoefficient // HistoryErrorInjectionRate is rate for injecting random error in history client // KeyName: history.errorInjectionRate // Value type: Float64 // Default value: 0 // Allowed filters: N/A HistoryErrorInjectionRate // ReplicationBudgetManagerSoftCapThreshold is the soft cap threshold for the replication budget manager cache (0.0 to 1.0) // KeyName: history.replicationBudgetManagerSoftCapThreshold // Value type: Float64 // Default value: 1.0 // Allowed filters: N/A ReplicationBudgetManagerSoftCapThreshold // ReplicationTaskFetcherTimerJitterCoefficient is the jitter for fetcher timer // KeyName: history.ReplicationTaskFetcherTimerJitterCoefficient // Value type: Float64 // Default value: 0.15 // Allowed filters: N/A ReplicationTaskFetcherTimerJitterCoefficient // WorkerDeterministicConstructionCheckProbability controls the probability of running a deterministic construction check for any given archival // KeyName: N/A // Default value: N/A // TODO: https://github.com/uber/cadence/issues/3861 // Note: not currently used in open-source WorkerDeterministicConstructionCheckProbability // WorkerBlobIntegrityCheckProbability controls the probability of running an integrity check for any given archival // KeyName: N/A // Default value: N/A // TODO: https://github.com/uber/cadence/issues/3861 // Note: not currently used in open-source WorkerBlobIntegrityCheckProbability // HistoryGlobalRatelimiterNewDataWeight defines how much weight to give each host's newest data, per update. Must be between 0 and 1, higher values match new values more closely after a single update. // KeyName: history.globalRatelimiterNewDataWeight // Value type: Float64 // Default value: 0.5 HistoryGlobalRatelimiterNewDataWeight MatchingPartitionDownscaleFactor // MatchingOverrideTaskListRPS is the RPS override for a specific TaskList. // When set to a non-zero value, this overrides the RPS value that pollers specify. // KeyName: matching.overrideTaskListRps // Value type: Float64 // Default value: 0 // Allowed filters: DomainName, TaskListName, TaskType MatchingOverrideTaskListRPS // Key for shard distributor // ShardDistributorErrorInjectionRate is rate for injecting random error in shard distributor client // KeyName: sharddistributor.errorInjectionRate // Value type: Float64 // Default value: 0 // Allowed filters: N/A ShardDistributorErrorInjectionRate // ShardDistributorExecutorErrorInjectionRate is rate for injecting random error in shard distributor executor client // KeyName: sharddistributorexecutor.errorInjectionRate // Value type: Float64 // Default value: 0 // Allowed filters: N/A ShardDistributorExecutorErrorInjectionRate // ShardDistributorLoadBalancingNaiveMaxDeviation is max deviation between the coldest and hottest executors // in naive load balancing mode // // KeyName: shardDistributor.loadBalancingNaive.maxDeviation // Value type: Float64 // Default value: 2.0 // Allowed filters: namespace ShardDistributorLoadBalancingNaiveMaxDeviation // LastFloatKey must be the last one in this const group LastFloatKey ) const ( UnknownStringKey StringKey = iota // key for tests TestGetStringPropertyKey // key for common & admin // WriteVisibilityStoreName is key for how to write to advanced visibility. Usually using with ReadVisibilityStoreName // KeyName: system.writeVisibilityStoreName // Value type: String enum: string ["es","os","pinot","db"] // Default value: "es" // Allowed filters: N/A WriteVisibilityStoreName // ReadVisibilityStoreName is key to identify which store to read visibility data from // KeyName: system.readVisibilityStoreName // Value type: String enum: "db" or "es" or "pinot" or "os" // Default value: "es" // Allowed filters: DomainName ReadVisibilityStoreName // HistoryArchivalStatus is key for the status of history archival to override the value from static config. // KeyName: system.historyArchivalStatus // Value type: string enum: "enabled" or "disabled" // Default value: "enabled" // Allowed filters: N/A HistoryArchivalStatus // VisibilityArchivalStatus is key for the status of visibility archival to override the value from static config. // KeyName: system.visibilityArchivalStatus // Value type: string enum: "enabled" or "disabled" // Default value: "enabled" // Allowed filters: N/A VisibilityArchivalStatus // DefaultEventEncoding is the encoding type for history events // KeyName: history.defaultEventEncoding // Value type: String // Default value: string(constants.EncodingTypeThriftRW) // Allowed filters: DomainName DefaultEventEncoding // AdminOperationToken is the token to pass admin checking // KeyName: history.adminOperationToken // Value type: String // Default value: common.DefaultAdminOperationToken // Allowed filters: N/A AdminOperationToken // ESAnalyzerLimitToTypes controls if we want to limit ESAnalyzer only to some workflow types // KeyName: worker.ESAnalyzerLimitToTypes // Value type: String // Default value: "" => means no limitation ESAnalyzerLimitToTypes // ESAnalyzerLimitToDomains controls if we want to limit ESAnalyzer only to some domains // KeyName: worker.ESAnalyzerLimitToDomains // Value type: String // Default value: "" => means no limitation ESAnalyzerLimitToDomains // ESAnalyzerWorkflowDurationWarnThresholds defines the warning execution thresholds for workflow types // KeyName: worker.ESAnalyzerWorkflowDurationWarnThresholds // Value type: string [{"DomainName":"", "WorkflowType":"", "Threshold":"", "Refresh":, "MaxNumWorkflows":}] // Default value: "" ESAnalyzerWorkflowDurationWarnThresholds // ESAnalyzerWorkflowVersionMetricDomains defines the domains we want to emit wf version metrics on // KeyName: worker.ESAnalyzerWorkflowVersionMetricDomains // Value type: string ["test-domain","test-domain2"] // Default value: "" ESAnalyzerWorkflowVersionMetricDomains // ESAnalyzerWorkflowTypeMetricDomains defines the domains we want to emit wf type metrics on // KeyName: worker.ESAnalyzerWorkflowTypeMetricDomains // Value type: string ["test-domain","test-domain2"] // Default value: "" ESAnalyzerWorkflowTypeMetricDomains // FrontendGlobalRatelimiterMode controls what keys use global vs fallback behavior, // and whether shadowing is enabled. This is only available for frontend usage for now. // // - "disabled" stops usage-tracking and all Update requests, in an attempt to be as close to "do not use at all" as possible. // - "local" uses the new limiters with call tracking and metrics, but forces local-only behavior and does not submit usage data to aggregators. // - "global" uses the new global-load-balanced logic (though it may decide to use a local-fallback internally, and this is not prevented) // - "x-shadow-y" means "use x, and shadow all calls to y but ignore the result". // this calls both, tracks and emits both metrics, and can be used to "warm" either limiter's in-memory state before switching. // // These values can be seen as constants of github.com/uber/cadence/common/quotas/global/collection.keyMode // // KeyName: frontend.globalRatelimiterMode // Value type: string enum: "disabled", "local", "global", "local-shadow-global", or "global-shadow-local" // Default value: "disabled" // Allowed filters: RatelimitKey (on global key, e.g. prefixed by collection name) FrontendGlobalRatelimiterMode // EnableAuthorizationV2 is the key to enable authorization v2 for a domain, only for extension binary: // KeyName: system.enableAuthorizationV2 // Value type: string ["disabled","shadow","enabled"] // Default value: "disabled" // TODO: https://github.com/uber/cadence/issues/3861 // Note: not currently used in open-source EnableAuthorizationV2 TasklistLoadBalancerStrategy // EnableAdminAuthorization is the key to enable authorization for admin operations, only for extension binary: // KeyName: system.enableAdminAuthorization // Value type: string ["disabled","shadow","enabled"] // Default value: "disabled" // TODO: https://github.com/uber/cadence/issues/3861 // Note: not currently used in open-source EnableAdminAuthorization // MatchingShardDistributionMode is the mode of shard distribution for matching, we currently have four modes, we _highly_ // recommend using hash_ring while the shard distributor is still in development. // // - "hash_ring" means that the shards are distributed using a consistent hash ring, in particular using the ringpop library // - "shard_distributor" means that the shards are distributed using the _highly experimental_ shard distributor service // - "hash_ring-shadow-shard_distributor" means that the shards are distrubuted using the hash ring, but shadowed by the shard distributor // - "shard_distributor-shadow-hash_ring" means that the shards are distrubuted using the shard distributor, but shadowed by the hash ring // // KeyName: matching.shardDistributionMode // Value type: string enum: "hash_ring" or "shard_distributor" // Default value: "hash_ring" MatchingShardDistributionMode // SerializationEncoding is the encoding type for blobs // KeyName: history.serializationEncoding // Value type: String // Default value: "thriftrw" SerializationEncoding // ShardDistributorMigrationMode is the mode the at represent the state of the migration to rely on shard distributor for the sharding mechanism // // "invalid" invalid mode for the migration, not expected to be used // "local_pass" the executor library is integrated but no external call to the SD happening // "local_pass_shadow" heartbeat calls to the SD to update the sharding state in SD // "distributed_pass" the local sharding mechanism is sent to SD, returned by SD and applied in the onboarded service // "onboarded" the sharding logic in SD is used // // KeyName: shardDistributor.migrationMode // Value type: String // Default value: onboarded // Allowed filters: namespace ShardDistributorMigrationMode // ShardDistributorLoadBalancingMode is the load balancing mode for the shard distributor // Depending on the mode, the shard distributor will use different ways to distribute the shards // // * "naive" - mode assigns shards to the least loaded hosts without considering the existing shard distribution // * "greedy" - mode balances the load across all hosts while minimizing shard movements and uses shard statistics to make better decisions // // KeyName: shardDistributor.loadBalancingMode // Value type: String // Default value: "naive" // Allowed filters: namespace ShardDistributorLoadBalancingMode // LastStringKey must be the last one in this const group LastStringKey ) const ( UnknownDurationKey DurationKey = iota // key for tests TestGetDurationPropertyKey TestGetDurationPropertyFilteredByDomainKey TestGetDurationPropertyFilteredByTaskListInfoKey TestGetDurationPropertyFilteredByWorkflowTypeKey TestGetDurationPropertyFilteredByDomainIDKey TestGetDurationPropertyFilteredByShardID // FrontendShutdownDrainDuration is the duration of traffic drain during shutdown // KeyName: frontend.shutdownDrainDuration // Value type: Duration // Default value: 0 // Allowed filters: N/A FrontendShutdownDrainDuration // FrontendWarmupDuration is the duration before a frontend host reports its status as healthy // KeyName: frontend.warmupDuration // Value type: Duration // Default value: 30s // Allowed filters: N/A FrontendWarmupDuration // FrontendFailoverCoolDown is duration between two domain failvoers // KeyName: frontend.failoverCoolDown // Value type: Duration // Default value: 1m (one minute, see domain.FailoverCoolDown) // Allowed filters: DomainName FrontendFailoverCoolDown // DomainFailoverRefreshInterval is the domain failover refresh timer // KeyName: frontend.domainFailoverRefreshInterval // Value type: Duration // Default value: 10s (10*time.Second) // Allowed filters: N/A DomainFailoverRefreshInterval // GlobalRatelimiterUpdateInterval controls how frequently ratelimiter usage information is submitted to aggregators. // This value is shared between limiting and aggregating hosts (frontend and history). // KeyName: frontend.globalRatelimiterUpdateInterval // Value type: Duration // Default value: 3 seconds GlobalRatelimiterUpdateInterval // FrontendMaxWorkerPollDelay is the maximum duration a worker poll request (PollForActivityTask, PollForDecisionTask) // will wait for a rate limit token before being rejected. This setting doesn't completely control how // long a request can take - the request will complete after the minimum of the configured request timeout // KeyName: frontend.maxWorkerPollDelay // Value type: Duration // Default value: 0 // Allowed filters: DomainName FrontendMaxWorkerPollDelay // MatchingLongPollExpirationInterval is the long poll expiration interval in the matching service // KeyName: matching.longPollExpirationInterval // Value type: Duration // Default value: time.Minute // Allowed filters: DomainName,TasklistName,TaskType MatchingLongPollExpirationInterval // MatchingUpdateAckInterval is the interval for update ack // KeyName: matching.updateAckInterval // Value type: Duration // Default value: 1m (1*time.Minute) // Allowed filters: DomainName,TasklistName,TaskType MatchingUpdateAckInterval // MatchingIdleTasklistCheckInterval is the IdleTasklistCheckInterval // KeyName: matching.idleTasklistCheckInterval // Value type: Duration // Default value: 5m (5*time.Minute) // Allowed filters: DomainName,TasklistName,TaskType MatchingIdleTasklistCheckInterval // MaxTasklistIdleTime is the max time tasklist being idle // KeyName: matching.maxTasklistIdleTime // Value type: Duration // Default value: 5m (5*time.Minute) // Allowed filters: DomainName,TasklistName,TaskType MaxTasklistIdleTime // MatchingShutdownDrainDuration is the duration of traffic drain during shutdown // KeyName: matching.shutdownDrainDuration // Value type: Duration // Default value: 0 // Allowed filters: N/A MatchingShutdownDrainDuration // MatchingActivityTaskSyncMatchWaitTime is the amount of time activity task will wait to be sync matched // KeyName: matching.activityTaskSyncMatchWaitTime // Value type: Duration // Default value: 100ms // Allowed filters: DomainName MatchingActivityTaskSyncMatchWaitTime // MatchingPartitionUpscaleSustainedDuration is the sustained period to wait before upscaling the number of partitions // KeyName: matching.partitionUpscaleSustainedDuration // Value type: Duration // Default value: 1m // Allowed filters: DomainName,TasklistName,TaskType MatchingPartitionUpscaleSustainedDuration // MatchingPartitionDownscaleSustainedDuration is the sustained period to wait before downscaling the number of partitions // KeyName: matching.partitionDownscaleSustainedDuration // Value type: Duration // Default value: 2m // Allowed filters: DomainName,TasklistName,TaskType MatchingPartitionDownscaleSustainedDuration // MatchingAdaptiveScalerUpdateInterval is the internal for adaptive scaler to update // KeyName: matching.adaptiveScalerUpdateInterval // Value type: Duration // Default value: 15s // Allowed filters: DomainName,TasklistName,TaskType MatchingAdaptiveScalerUpdateInterval // MatchingQPSTrackerInterval is the interval for qps tracker's loop. Changes are not reflected until service restart // KeyName: matching.qpsTrackerInterval // Value type: Duration // Default value: 10s // Allowed filters: DomainName,TasklistName,TaskType MatchingQPSTrackerInterval // MatchingIsolationGroupUpscaleSustainedDuration is the sustained period to wait before upscaling the number of partitions an isolation group is assigned to // KeyName: matching.isolationGroupUpscaleSustainedDuration // Value type: Duration // Default value: 1m // Allowed filters: DomainName,TasklistName,TaskType MatchingIsolationGroupUpscaleSustainedDuration // MatchingIsolationGroupDownscaleSustainedDuration is the sustained period to wait before downscaling the number of partitions an isolation group is assigned to // KeyName: matching.isolationGroupDownscaleSustainedDuration // Value type: Duration // Default value: 2m // Allowed filters: DomainName,TasklistName,TaskType MatchingIsolationGroupDownscaleSustainedDuration // MatchingIsolationGroupHasPollersSustainedDuration is the sustained period to wait before considering an isolation group as an active and assigning partitions to it // KeyName: matching.isolationGroupHasPollersSustainedDuration // Value type: Duration // Default value: 1m // Allowed filters: DomainName,TasklistName,TaskType MatchingIsolationGroupHasPollersSustainedDuration // MatchingIsolationGroupNoPollersSustainedDuration is the sustained period to wait before considering an isolation group as inactive and unassigning all partitions from it // KeyName: matching.isolationGroupNoPollersSustainedDuration // Value type: Duration // Default value: 1m // Allowed filters: DomainName,TasklistName,TaskType MatchingIsolationGroupNoPollersSustainedDuration // HistoryLongPollExpirationInterval is the long poll expiration interval in the history service // KeyName: history.longPollExpirationInterval // Value type: Duration // Default value: 20s( time.Second*20) // Allowed filters: DomainName HistoryLongPollExpirationInterval // HistoryCacheTTL is TTL of history cache // KeyName: history.cacheTTL // Value type: Duration // Default value: 1h (time.Hour) // Allowed filters: N/A HistoryCacheTTL // HistoryShutdownDrainDuration is the duration of traffic drain during shutdown // KeyName: history.shutdownDrainDuration // Value type: Duration // Default value: 0 // Allowed filters: N/A HistoryShutdownDrainDuration // EventsCacheTTL is TTL of events cache // KeyName: history.eventsCacheTTL // Value type: Duration // Default value: 1h (time.Hour) // Allowed filters: N/A EventsCacheTTL // AcquireShardInterval is interval that timer used to acquire shard // KeyName: history.acquireShardInterval // Value type: Duration // Default value: 1m (time.Minute) // Allowed filters: N/A AcquireShardInterval // StandbyClusterDelay is the artificial delay added to standby cluster's view of active cluster's time // KeyName: history.standbyClusterDelay // Value type: Duration // Default value: 5m (5*time.Minute) // Allowed filters: N/A StandbyClusterDelay // StandbyTaskMissingEventsResendDelay is the amount of time standby cluster's will wait (if events are missing)before calling remote for missing events // KeyName: history.standbyTaskMissingEventsResendDelay // Value type: Duration // Default value: 15m (15*time.Minute) // Allowed filters: N/A StandbyTaskMissingEventsResendDelay // StandbyTaskMissingEventsDiscardDelay is the amount of time standby cluster's will wait (if events are missing)before discarding the task // KeyName: history.standbyTaskMissingEventsDiscardDelay // Value type: Duration // Default value: 25m (25*time.Minute) // Allowed filters: N/A StandbyTaskMissingEventsDiscardDelay // ActiveTaskRedispatchInterval is the active task redispatch interval // KeyName: history.activeTaskRedispatchInterval // Value type: Duration // Default value: 5s (5*time.Second) // Allowed filters: N/A ActiveTaskRedispatchInterval // StandbyTaskRedispatchInterval is the standby task redispatch interval // KeyName: history.standbyTaskRedispatchInterval // Value type: Duration // Default value: 30s (30*time.Second) // Allowed filters: N/A StandbyTaskRedispatchInterval // StandbyTaskReReplicationContextTimeout is the context timeout for standby task re-replication // KeyName: history.standbyTaskReReplicationContextTimeout // Value type: Duration // Default value: 3m (3*time.Minute) // Allowed filters: DomainID StandbyTaskReReplicationContextTimeout // ResurrectionCheckMinDelay is the minimal timer processing delay before scanning history to see // if there's a resurrected timer/activity // KeyName: history.resurrectionCheckMinDelay // Value type: Duration // Default value: 24h (24*time.Hour) // Allowed filters: DomainName ResurrectionCheckMinDelay // QueueProcessorSplitLookAheadDurationByDomainID is the look ahead duration when spliting a domain to a new processing queue // KeyName: history.queueProcessorSplitLookAheadDurationByDomainID // Value type: Duration // Default value: 20m (20*time.Minute) // Allowed filters: DomainID QueueProcessorSplitLookAheadDurationByDomainID // QueueProcessorPollBackoffInterval is the backoff duration when queue processor is throttled // KeyName: history.queueProcessorPollBackoffInterval // Value type: Duration // Default value: 5s (5*time.Second) // Allowed filters: N/A QueueProcessorPollBackoffInterval // VirtualSliceForceAppendInterval is the duration forcing a new virtual slice to be appended to the root virtual queue instead of being merged. It has 2 benefits: First, virtual slices won't grow infinitely, task loading for that slice can complete and its scope can be shrinked. Second, when we need to unload a virtual slice to free memory, we won't unload too many tasks. // KeyName: history.virtualSliceForceAppendInterval // Value type: Duration // Default value: 5m // Allowed filters: N/A VirtualSliceForceAppendInterval // TimerProcessorUpdateAckInterval is update interval for timer processor // KeyName: history.timerProcessorUpdateAckInterval // Value type: Duration // Default value: 30s (30*time.Second) // Allowed filters: N/A TimerProcessorUpdateAckInterval // TimerProcessorCompleteTimerInterval is complete timer interval for timer processor // KeyName: history.timerProcessorCompleteTimerInterval // Value type: Duration // Default value: 60s (60*time.Second) // Allowed filters: N/A TimerProcessorCompleteTimerInterval // TimerProcessorFailoverMaxStartJitterInterval is the max jitter interval for starting timer // failover queue processing. The actual jitter interval used will be a random duration between // 0 and the max interval so that timer failover queue across different shards won't start at // the same time // KeyName: history.timerProcessorFailoverMaxStartJitterInterval // Value type: Duration // Default value: 0s (0*time.Second) // Allowed filters: N/A TimerProcessorFailoverMaxStartJitterInterval // TimerProcessorMaxPollInterval is max poll interval for timer processor // KeyName: history.timerProcessorMaxPollInterval // Value type: Duration // Default value: 5m (5*time.Minute) // Allowed filters: N/A TimerProcessorMaxPollInterval // TimerProcessorSplitQueueInterval is the split processing queue interval for timer processor // KeyName: history.timerProcessorSplitQueueInterval // Value type: Duration // Default value: 1m (1*time.Minute) // Allowed filters: N/A TimerProcessorSplitQueueInterval // TimerProcessorArchivalTimeLimit is the upper time limit for inline history archival // KeyName: history.timerProcessorArchivalTimeLimit // Value type: Duration // Default value: 2s (2*time.Second) // Allowed filters: N/A TimerProcessorArchivalTimeLimit // TimerProcessorMaxTimeShift is the max shift timer processor can have // KeyName: history.timerProcessorMaxTimeShift // Value type: Duration // Default value: 1s (1*time.Second) // Allowed filters: N/A TimerProcessorMaxTimeShift // TransferProcessorFailoverMaxStartJitterInterval is the max jitter interval for starting transfer // failover queue processing. The actual jitter interval used will be a random duration between // 0 and the max interval so that timer failover queue across different shards won't start at // the same time // KeyName: history.transferProcessorFailoverMaxStartJitterInterval // Value type: Duration // Default value: 0s (0*time.Second) // Allowed filters: N/A TransferProcessorFailoverMaxStartJitterInterval // TransferProcessorMaxPollInterval is max poll interval for transferQueueProcessor // KeyName: history.transferProcessorMaxPollInterval // Value type: Duration // Default value: 1m (1*time.Minute) // Allowed filters: N/A TransferProcessorMaxPollInterval // TransferProcessorSplitQueueInterval is the split processing queue interval for transferQueueProcessor // KeyName: history.transferProcessorSplitQueueInterval // Value type: Duration // Default value: 1m (1*time.Minute) // Allowed filters: N/A TransferProcessorSplitQueueInterval // TransferProcessorUpdateAckInterval is update interval for transferQueueProcessor // KeyName: history.transferProcessorUpdateAckInterval // Value type: Duration // Default value: 30s (30*time.Second) // Allowed filters: N/A TransferProcessorUpdateAckInterval // TransferProcessorCompleteTransferInterval is complete timer interval for transferQueueProcessor // KeyName: history.transferProcessorCompleteTransferInterval // Value type: Duration // Default value: 60s (60*time.Second) // Allowed filters: N/A TransferProcessorCompleteTransferInterval // TransferProcessorValidationInterval is interval for performing transfer queue validation // KeyName: history.transferProcessorValidationInterval // Value type: Duration // Default value: 30s (30*time.Second) // Allowed filters: N/A TransferProcessorValidationInterval // TransferProcessorVisibilityArchivalTimeLimit is the upper time limit for archiving visibility records // KeyName: history.transferProcessorVisibilityArchivalTimeLimit // Value type: Duration // Default value: 400ms (400*time.Millisecond) // Allowed filters: N/A TransferProcessorVisibilityArchivalTimeLimit // ReplicatorUpperLatency indicates the max allowed replication latency between clusters // KeyName: history.replicatorUpperLatency // Value type: Duration // Default value: 40s (40 * time.Second) // Allowed filters: N/A ReplicatorUpperLatency // ShardUpdateMinInterval is the minimal time interval which the shard info can be updated // KeyName: history.shardUpdateMinInterval // Value type: Duration // Default value: 5m (5*time.Minute) // Allowed filters: N/A ShardUpdateMinInterval // ShardSyncMinInterval is the minimal time interval which the shard info should be sync to remote // KeyName: history.shardSyncMinInterval // Value type: Duration // Default value: 5m (5*time.Minute) // Allowed filters: N/A ShardSyncMinInterval // StickyTTL is to expire a sticky tasklist if no update more than this duration // KeyName: history.stickyTTL // Value type: Duration // Default value: time.Hour*24*365 // Allowed filters: DomainName StickyTTL // DecisionHeartbeatTimeout is for decision heartbeat // KeyName: history.decisionHeartbeatTimeout // Value type: Duration // Default value: 30m (time.Minute*30) // Allowed filters: DomainName DecisionHeartbeatTimeout // NormalDecisionScheduleToStartTimeout is scheduleToStart timeout duration for normal (non-sticky) decision task // KeyName: history.normalDecisionScheduleToStartTimeout // Value type: Duration // Default value: time.Minute*5 // Allowed filters: DomainName NormalDecisionScheduleToStartTimeout // NotifyFailoverMarkerInterval is determines the frequency to notify failover marker // KeyName: history.NotifyFailoverMarkerInterval // Value type: Duration // Default value: 5s (5*time.Second) // Allowed filters: N/A NotifyFailoverMarkerInterval // ActivityMaxScheduleToStartTimeoutForRetry is maximum value allowed when overwritting the schedule to start timeout for activities with retry policy // KeyName: history.activityMaxScheduleToStartTimeoutForRetry // Value type: Duration // Default value: 30m (30*time.Minute) // Allowed filters: DomainName ActivityMaxScheduleToStartTimeoutForRetry // ReplicationTaskFetcherAggregationInterval determines how frequently the fetch requests are sent // KeyName: history.ReplicationTaskFetcherAggregationInterval // Value type: Duration // Default value: 2s (2 * time.Second) // Allowed filters: N/A ReplicationTaskFetcherAggregationInterval // ReplicationTaskFetcherErrorRetryWait is the wait time when fetcher encounters error // KeyName: history.ReplicationTaskFetcherErrorRetryWait // Value type: Duration // Default value: time.Second // Allowed filters: N/A ReplicationTaskFetcherErrorRetryWait // ReplicationTaskFetcherServiceBusyWait is the wait time when fetcher encounters service busy error // KeyName: history.ReplicationTaskFetcherServiceBusyWait // Value type: Duration // Default value: 60s (60 * time.Second) // Allowed filters: N/A ReplicationTaskFetcherServiceBusyWait // ReplicationTaskProcessorErrorRetryWait is the initial retry wait when we see errors in applying replication tasks // KeyName: history.ReplicationTaskProcessorErrorRetryWait // Value type: Duration // Default value: 50ms (50*time.Millisecond) // Allowed filters: ShardID ReplicationTaskProcessorErrorRetryWait // ReplicationTaskProcessorErrorSecondRetryWait is the initial retry wait for the second phase retry // KeyName: history.ReplicationTaskProcessorErrorSecondRetryWait // Value type: Duration // Default value: 5s (5* time.Second) // Allowed filters: ShardID ReplicationTaskProcessorErrorSecondRetryWait // ReplicationTaskProcessorErrorSecondRetryMaxWait is the max wait time for the second phase retry // KeyName: history.ReplicationTaskProcessorErrorSecondRetryMaxWait // Value type: Duration // Default value: 30s (30 * time.Second) // Allowed filters: ShardID ReplicationTaskProcessorErrorSecondRetryMaxWait // ReplicationTaskProcessorErrorSecondRetryExpiration is the expiration duration for the second phase retry // KeyName: history.ReplicationTaskProcessorErrorSecondRetryExpiration // Value type: Duration // Default value: 5m (5* time.Minute) // Allowed filters: ShardID ReplicationTaskProcessorErrorSecondRetryExpiration // ReplicationTaskProcessorNoTaskInitialWait is the wait time when not ask is returned // KeyName: history.ReplicationTaskProcessorNoTaskInitialWait // Value type: Duration // Default value: 2s (2* time.Second) // Allowed filters: ShardID ReplicationTaskProcessorNoTaskInitialWait // ReplicationTaskProcessorCleanupInterval determines how frequently the cleanup replication queue // KeyName: history.ReplicationTaskProcessorCleanupInterval // Value type: Duration // Default value: 1m (1* time.Minute) // Allowed filters: ShardID ReplicationTaskProcessorCleanupInterval // ReplicationTaskProcessorStartWait is the wait time before each task processing batch // KeyName: history.ReplicationTaskProcessorStartWait // Value type: Duration // Default value: 0 // Allowed filters: ShardID ReplicationTaskProcessorStartWait // ReplicationTaskProcessorLatencyLogThreshold is the threshold of whether history will log history replication latency // KeyName: history.ReplicationTaskProcessorLatencyLogThreshold // Value type: Duration // Default value: 0 // Allowed filters: N/A ReplicationTaskProcessorLatencyLogThreshold // WorkerESProcessorFlushInterval is flush interval for esProcessor // KeyName: worker.ESProcessorFlushInterval // Value type: Duration // Default value: 1s (1*time.Second) // Allowed filters: N/A WorkerESProcessorFlushInterval // WorkerTimeLimitPerArchivalIteration is controls the time limit of each iteration of archival workflow // KeyName: worker.TimeLimitPerArchivalIteration // Value type: Duration // Default value: archiver.MaxArchivalIterationTimeout() // Allowed filters: N/A WorkerTimeLimitPerArchivalIteration // WorkerReplicationTaskMaxRetryDuration is the max retry duration for any task // KeyName: worker.replicationTaskMaxRetryDuration // Value type: Duration // Default value: 10m (time.Minute*10) // Allowed filters: N/A WorkerReplicationTaskMaxRetryDuration // ESAnalyzerTimeWindow defines the time window ElasticSearch Analyzer will consider while taking workflow averages // KeyName: worker.ESAnalyzerTimeWindow // Value type: Duration // Default value: 30 days ESAnalyzerTimeWindow // ESAnalyzerBufferWaitTime controls min time required to consider a worklow stuck // KeyName: worker.ESAnalyzerBufferWaitTime // Value type: Duration // Default value: 30 minutes ESAnalyzerBufferWaitTime // IsolationGroupStateRefreshInterval // KeyName: system.isolationGroupStateRefreshInterval // Value type: Duration // Default value: 30 seconds IsolationGroupStateRefreshInterval // IsolationGroupStateFetchTimeout is the dynamic config DB fetch timeout value // KeyName: system.isolationGroupStateFetchTimeout // Value type: Duration // Default value: 30 seconds IsolationGroupStateFetchTimeout // IsolationGroupStateUpdateTimeout is the dynamic config DB update timeout value // KeyName: system.isolationGroupStateUpdateTimeout // Value type: Duration // Default value: 30 seconds IsolationGroupStateUpdateTimeout // AsyncTaskDispatchTimeout is the timeout of dispatching tasks for async match // KeyName: matching.asyncTaskDispatchTimeout // Value type: Duration // Default value: 3 seconds // Allowed filters: domainName, taskListName, taskListType AsyncTaskDispatchTimeout // AppendTaskTimeout is the timeout of appending tasks to persistence. // KeyName: matching.appendTaskTimeout // Value type: Duration // Default value: 5 seconds // Allowed filters: domainName, taskListName, taskListType AppendTaskTimeout // HistoryGlobalRatelimiterDecayAfter defines how long to wait for an update before considering a host's data "possibly gone", causing its weight to gradually decline. // KeyName: history.globalRatelimiterDecayAfter // Value type: Duration // Default value: 6 seconds HistoryGlobalRatelimiterDecayAfter // HistoryGlobalRatelimiterGCAfter defines how long to wait until a host's data is considered entirely useless, e.g. host has likely disappeared, its weight is very low, and the data can be deleted. // KeyName: history.globalRatelimiterGCAfter // Value type: Duration // Default value: 30 seconds HistoryGlobalRatelimiterGCAfter // LocalPollWaitTime is the wait time for a poller to wait before considering request forwarding // KeyName: matching.localPollWaitTime // Value type: Duration // Default value: 10ms // Allowed filters: domainName, taskListName, taskListType LocalPollWaitTime // LocalTaskWaitTime is the wait time for a task to wait before considering task forwarding // KeyName: matching.localTaskWaitTime // Value type: Duration // Default value: 10ms // Allowed filters: domainName, taskListName, taskListType LocalTaskWaitTime // TaskIsolationDuration is the time period for which we attempt to respect tasklist isolation before allowing any poller to process the task // KeyName: matching.taskIsolationDuration // Value type: Duration // Default value: 200ms // Allowed filters: domainName, taskListName, taskListType TaskIsolationDuration // TaskIsolationPollerWindow is the time period for which pollers are remembered when deciding whether to skip tasklist isolation due to unpolled isolation groups. // KeyName: matching.taskIsolationPollerWindow // Value type: Duration // Default value: 2s // Allowed filters: domainName, taskListName, taskListType TaskIsolationPollerWindow // DomainAuditLogTTL is the TTL for domain audit log entries // KeyName: system.domainAuditLogTTL // Value type: Duration // Default value: 365 days (1 year) // Allowed filters: DomainID DomainAuditLogTTL // LastDurationKey must be the last one in this const group LastDurationKey ) const ( UnknownMapKey MapKey = iota // key for tests TestGetMapPropertyKey // key for common & admin // RequiredDomainDataKeys is the key for the list of data keys required in domain registration // KeyName: system.requiredDomainDataKeys // Value type: Map // Default value: nil // Allowed filters: N/A RequiredDomainDataKeys // key for frontend // ValidSearchAttributes is legal indexed keys that can be used in list APIs. When overriding, ensure to include the existing default attributes of the current release // KeyName: frontend.validSearchAttributes // Value type: Map // Default value: the default attributes of this release version, see definition.GetDefaultIndexedKeys() // Allowed filters: N/A ValidSearchAttributes // key for history // TaskSchedulerRoundRobinWeights is the priority weight for weighted round robin task scheduler // KeyName: history.taskSchedulerRoundRobinWeight // Value type: Map // Default value: please see common.ConvertIntMapToDynamicConfigMapProperty(DefaultTaskPriorityWeight) in code base // Allowed filters: N/A TaskSchedulerRoundRobinWeights // TaskSchedulerDomainRoundRobinWeights is the priority round robin weights for domains // KeyName: history.taskSchedulerDomainRoundRobinWeight // Value type: Map // Default value: see common.ConvertIntMapToDynamicConfigMapProperty(DefaultTaskSchedulerRoundRobinWeights) in code base // Allowed filters: DomainName TaskSchedulerDomainRoundRobinWeights // QueueProcessorPendingTaskSplitThreshold is the threshold for the number of pending tasks per domain // KeyName: history.queueProcessorPendingTaskSplitThreshold // Value type: Map // Default value: see common.ConvertIntMapToDynamicConfigMapProperty(DefaultPendingTaskSplitThreshold) in code base // Allowed filters: N/A QueueProcessorPendingTaskSplitThreshold // QueueProcessorStuckTaskSplitThreshold is the threshold for the number of attempts of a task // KeyName: history.queueProcessorStuckTaskSplitThreshold // Value type: Map // Default value: see common.ConvertIntMapToDynamicConfigMapProperty(DefaultStuckTaskSplitThreshold) in code base // Allowed filters: N/A QueueProcessorStuckTaskSplitThreshold // PinotOptimizedQueryColumns is the list of search attributes that can be used in pinot optimized query // KeyName: frontend.pinotOptimizedQueryColumns // Value type: Map // Default value: empty map // Allowed filters: N/A PinotOptimizedQueryColumns // SearchAttributesHiddenValueKeys is the list of search attributes that values should be hidden // KeyName: frontend.searchAttributesHiddenValueKeys // Value type: Map // Default value: empty map // Allowed filters: N/A SearchAttributesHiddenValueKeys // LastMapKey must be the last one in this const group LastMapKey ) const ( UnknownListKey ListKey = iota TestGetListPropertyKey // AllIsolationGroups is the list of all possible isolation groups in a service // KeyName: system.allIsolationGroups // Value type: []string // Default value: N/A // Allowed filters: N/A AllIsolationGroups // RateLimiterBypassCallerTypes defines which caller types bypass rate limiters (both frontend and persistence) // KeyName: system.rateLimiterBypassCallerTypes // Value type: []string // Default value: empty list // Allowed filters: N/A RateLimiterBypassCallerTypes // HeaderForwardingRules defines which headers are forwarded from inbound calls to outbound. // This value is only loaded at startup. // // Regexes and header names are used as-is, you are strongly encouraged to use `(?i)` to make your regex case-insensitive. // // KeyName: admin.HeaderForwardingRules // Value type: []rpc.HeaderRule or an []interface{} containing `map[string]interface{}{"Add":bool,"Match":string}` values. // Default value: forward all headers. (this is a problematic value, and it will be changing as we reduce to a list of known values) HeaderForwardingRules LastListKey ) // DefaultIsolationGroupConfigStoreManagerGlobalMapping is the dynamic config value for isolation groups // Note: This is not typically used for normal dynamic config (type 0), but instead // it's used only for IsolationGroup config (type 1). // KeyName: system.defaultIsolationGroupConfigStoreManagerGlobalMapping const DefaultIsolationGroupConfigStoreManagerGlobalMapping ListKey = -1 // This is a hack to put it in a different list due to it being a different config type var IntKeys = map[IntKey]DynamicInt{ TestGetIntPropertyKey: { KeyName: "testGetIntPropertyKey", Description: "", DefaultValue: 0, }, TestGetIntPropertyFilteredByDomainKey: { KeyName: "testGetIntPropertyFilteredByDomainKey", Description: "", DefaultValue: 0, }, TestGetIntPropertyFilteredByTaskListInfoKey: { KeyName: "testGetIntPropertyFilteredByTaskListInfoKey", Description: "", DefaultValue: 0, }, TestGetIntPropertyFilteredByWorkflowTypeKey: { KeyName: "testGetIntPropertyFilteredByWorkflowTypeKey", Description: "", DefaultValue: 0, }, TestGetIntPropertyFilteredByShardIDKey: { KeyName: "testGetIntPropertyFilteredByShardIDKey", Description: "", DefaultValue: 0, Filters: nil, }, TransactionSizeLimit: { KeyName: "system.transactionSizeLimit", Description: "TransactionSizeLimit is the largest allowed transaction size to persistence", DefaultValue: 14680064, }, MaxRetentionDays: { KeyName: "system.maxRetentionDays", Description: "MaxRetentionDays is the maximum allowed retention days for domain", DefaultValue: 30, }, MinRetentionDays: { KeyName: "system.minRetentionDays", Description: "MinRetentionDays is the minimal allowed retention days for domain", DefaultValue: 1, }, MaxDecisionStartToCloseSeconds: { KeyName: "system.maxDecisionStartToCloseSeconds", Description: "MaxDecisionStartToCloseSeconds is the maximum allowed value for decision start to close timeout in seconds", DefaultValue: 240, }, BlobSizeLimitError: { KeyName: "limit.blobSize.error", Description: "BlobSizeLimitError is the per event blob size limit", DefaultValue: 2 * 1024 * 1024, }, BlobSizeLimitWarn: { KeyName: "limit.blobSize.warn", Filters: []Filter{DomainName}, Description: "BlobSizeLimitWarn is the per event blob size limit for warning", DefaultValue: 256 * 1024, }, HistorySizeLimitError: { KeyName: "limit.historySize.error", Filters: []Filter{DomainName}, Description: "HistorySizeLimitError is the per workflow execution history size limit", DefaultValue: 200 * 1024 * 1024, }, HistorySizeLimitWarn: { KeyName: "limit.historySize.warn", Filters: []Filter{DomainName}, Description: "HistorySizeLimitWarn is the per workflow execution history size limit for warning", DefaultValue: 50 * 1024 * 1024, }, HistoryCountLimitError: { KeyName: "limit.historyCount.error", Filters: []Filter{DomainName}, Description: "HistoryCountLimitError is the per workflow execution history event count limit", DefaultValue: 200 * 1024, }, HistoryCountLimitWarn: { KeyName: "limit.historyCount.warn", Filters: []Filter{DomainName}, Description: "HistoryCountLimitWarn is the per workflow execution history event count limit for warning", DefaultValue: 50 * 1024, }, PendingActivitiesCountLimitError: { KeyName: "limit.pendingActivityCount.error", Description: "PendingActivitiesCountLimitError is the limit of how many pending activities a workflow can have at a point in time", DefaultValue: 1024, }, PendingActivitiesCountLimitWarn: { KeyName: "limit.pendingActivityCount.warn", Description: "PendingActivitiesCountLimitWarn is the limit of how many activities a workflow can have before a warning is logged", DefaultValue: 512, }, DomainNameMaxLength: { KeyName: "limit.domainNameLength", Filters: []Filter{DomainName}, Description: "DomainNameMaxLength is the length limit for domain name", DefaultValue: 1000, }, IdentityMaxLength: { KeyName: "limit.identityLength", Filters: []Filter{DomainName}, Description: "IdentityMaxLength is the length limit for identity", DefaultValue: 1000, }, WorkflowIDMaxLength: { KeyName: "limit.workflowIDLength", Filters: []Filter{DomainName}, Description: "WorkflowIDMaxLength is the length limit for workflowID", DefaultValue: 1000, }, SignalNameMaxLength: { KeyName: "limit.signalNameLength", Filters: []Filter{DomainName}, Description: "SignalNameMaxLength is the length limit for signal name", DefaultValue: 1000, }, WorkflowTypeMaxLength: { KeyName: "limit.workflowTypeLength", Filters: []Filter{DomainName}, Description: "WorkflowTypeMaxLength is the length limit for workflow type", DefaultValue: 1000, }, RequestIDMaxLength: { KeyName: "limit.requestIDLength", Filters: []Filter{DomainName}, Description: "RequestIDMaxLength is the length limit for requestID", DefaultValue: 1000, }, TaskListNameMaxLength: { KeyName: "limit.taskListNameLength", Filters: []Filter{DomainName}, Description: "TaskListNameMaxLength is the length limit for task list name", DefaultValue: 1000, }, ActivityIDMaxLength: { KeyName: "limit.activityIDLength", Filters: []Filter{DomainName}, Description: "ActivityIDMaxLength is the length limit for activityID", DefaultValue: 1000, }, ActivityTypeMaxLength: { KeyName: "limit.activityTypeLength", Filters: []Filter{DomainName}, Description: "ActivityTypeMaxLength is the length limit for activity type", DefaultValue: 1000, }, MarkerNameMaxLength: { KeyName: "limit.markerNameLength", Filters: []Filter{DomainName}, Description: "MarkerNameMaxLength is the length limit for marker name", DefaultValue: 1000, }, TimerIDMaxLength: { KeyName: "limit.timerIDLength", Filters: []Filter{DomainName}, Description: "TimerIDMaxLength is the length limit for timerID", DefaultValue: 1000, }, MaxIDLengthWarnLimit: { KeyName: "limit.maxIDWarnLength", Description: "MaxIDLengthWarnLimit is the warn length limit for various IDs, including: Domain, TaskList, WorkflowID, ActivityID, TimerID, WorkflowType, ActivityType, SignalName, MarkerName, ErrorReason/FailureReason/CancelCause, Identity, RequestID", DefaultValue: 128, }, FrontendPersistenceMaxQPS: { KeyName: "frontend.persistenceMaxQPS", Description: "FrontendPersistenceMaxQPS is the max qps frontend host can query DB", DefaultValue: 2000, }, FrontendPersistenceGlobalMaxQPS: { KeyName: "frontend.persistenceGlobalMaxQPS", Description: "FrontendPersistenceGlobalMaxQPS is the max qps frontend cluster can query DB", DefaultValue: 0, }, FrontendVisibilityMaxPageSize: { KeyName: "frontend.visibilityMaxPageSize", Filters: []Filter{DomainName}, Description: "FrontendVisibilityMaxPageSize is default max size for ListWorkflowExecutions in one page", DefaultValue: 1000, }, FrontendVisibilityListMaxQPS: { KeyName: "frontend.visibilityListMaxQPS", Description: "deprecated: never used for ratelimiting, only sampling-based failure injection, and only on database-based visibility.\n" + "FrontendVisibilityListMaxQPS is max qps frontend can list open/close workflows", DefaultValue: 10, }, FrontendESVisibilityListMaxQPS: { KeyName: "frontend.esVisibilityListMaxQPS", Description: "deprecated: never read from, all ES reads and writes erroneously use PersistenceMaxQPS.\n" + "FrontendESVisibilityListMaxQPS is max qps frontend can list open/close workflows from ElasticSearch", DefaultValue: 30, }, FrontendESIndexMaxResultWindow: { KeyName: "frontend.esIndexMaxResultWindow", Description: "FrontendESIndexMaxResultWindow is ElasticSearch index setting max_result_window", DefaultValue: 10000, }, FrontendHistoryMaxPageSize: { KeyName: "frontend.historyMaxPageSize", Filters: []Filter{DomainName}, Description: "FrontendHistoryMaxPageSize is default max size for GetWorkflowExecutionHistory in one page", DefaultValue: 1000, }, FrontendUserRPS: { KeyName: "frontend.rps", Description: "FrontendUserRPS is workflow rate limit per second", DefaultValue: 1200, }, FrontendWorkerRPS: { KeyName: "frontend.workerrps", Description: "FrontendWorkerRPS is background-processing workflow rate limit per second", DefaultValue: UnlimitedRPS, }, FrontendVisibilityRPS: { KeyName: "frontend.visibilityrps", Description: "FrontendVisibilityRPS is the global workflow List*WorkflowExecutions request rate limit per second", DefaultValue: UnlimitedRPS, }, FrontendAsyncRPS: { KeyName: "frontend.asyncrps", Description: "FrontendAsyncRPS is the async workflow request rate limit per second", DefaultValue: 10000, }, FrontendMaxDomainUserRPSPerInstance: { KeyName: "frontend.domainrps", Filters: []Filter{DomainName}, Description: "FrontendMaxDomainUserRPSPerInstance is workflow domain rate limit per second", DefaultValue: 1200, }, FrontendMaxDomainWorkerRPSPerInstance: { KeyName: "frontend.domainworkerrps", Filters: []Filter{DomainName}, Description: "FrontendMaxDomainWorkerRPSPerInstance is background-processing workflow domain rate limit per second", DefaultValue: UnlimitedRPS, }, FrontendMaxDomainVisibilityRPSPerInstance: { KeyName: "frontend.domainvisibilityrps", Filters: []Filter{DomainName}, Description: "FrontendMaxDomainVisibilityRPSPerInstance is the per-instance List*WorkflowExecutions request rate limit per second", DefaultValue: UnlimitedRPS, }, FrontendMaxDomainAsyncRPSPerInstance: { KeyName: "frontend.domainasyncrps", Filters: []Filter{DomainName}, Description: "FrontendMaxDomainAsyncRPSPerInstance is the per-instance async workflow request rate limit per second", DefaultValue: 10000, }, FrontendGlobalDomainUserRPS: { KeyName: "frontend.globalDomainrps", Filters: []Filter{DomainName}, Description: "FrontendGlobalDomainUserRPS is workflow domain rate limit per second for the whole Cadence cluster", DefaultValue: UnlimitedRPS, }, FrontendGlobalDomainWorkerRPS: { KeyName: "frontend.globalDomainWorkerrps", Filters: []Filter{DomainName}, Description: "FrontendGlobalDomainWorkerRPS is background-processing workflow domain rate limit per second for the whole Cadence cluster", DefaultValue: UnlimitedRPS, }, FrontendGlobalDomainVisibilityRPS: { KeyName: "frontend.globalDomainVisibilityrps", Filters: []Filter{DomainName}, Description: "FrontendGlobalDomainVisibilityRPS is the per-domain List*WorkflowExecutions request rate limit per second", DefaultValue: UnlimitedRPS, }, FrontendGlobalDomainAsyncRPS: { KeyName: "frontend.globalDomainAsyncrps", Filters: []Filter{DomainName}, Description: "FrontendGlobalDomainAsyncRPS is the per-domain async workflow request rate limit per second", DefaultValue: 100000, }, FrontendDecisionResultCountLimit: { KeyName: "frontend.decisionResultCountLimit", Filters: []Filter{DomainName}, Description: "FrontendDecisionResultCountLimit is max number of decisions per RespondDecisionTaskCompleted request", DefaultValue: 0, }, FrontendThrottledLogRPS: { KeyName: "frontend.throttledLogRPS", Description: "FrontendThrottledLogRPS is the rate limit on number of log messages emitted per second for throttled logger", DefaultValue: 20, }, FrontendMaxBadBinaries: { KeyName: "frontend.maxBadBinaries", Filters: []Filter{DomainName}, Description: "FrontendMaxBadBinaries is the max number of bad binaries in domain config", DefaultValue: 10, }, SearchAttributesNumberOfKeysLimit: { KeyName: "frontend.searchAttributesNumberOfKeysLimit", Filters: []Filter{DomainName}, Description: "SearchAttributesNumberOfKeysLimit is the limit of number of keys", DefaultValue: 100, }, SearchAttributesSizeOfValueLimit: { KeyName: "frontend.searchAttributesSizeOfValueLimit", Filters: []Filter{DomainName}, Description: "SearchAttributesSizeOfValueLimit is the size limit of each value", DefaultValue: 2048, }, SearchAttributesTotalSizeLimit: { KeyName: "frontend.searchAttributesTotalSizeLimit", Filters: []Filter{DomainName}, Description: "SearchAttributesTotalSizeLimit is the size limit of the whole map", DefaultValue: 40 * 1024, }, VisibilityArchivalQueryMaxPageSize: { KeyName: "frontend.visibilityArchivalQueryMaxPageSize", Description: "VisibilityArchivalQueryMaxPageSize is the maximum page size for a visibility archival query", DefaultValue: 10000, }, FrontendFailoverHistoryMaxSize: { KeyName: "frontend.failoverHistoryMaxSize", Filters: []Filter{DomainName}, Description: "FrontendFailoverHistoryMaxSize is the maximum size for the number of failover event records in a domain failover history", DefaultValue: 5, }, MatchingUserRPS: { KeyName: "matching.rps", Description: "MatchingUserRPS is request rate per second for each matching host", DefaultValue: 1200, }, MatchingWorkerRPS: { KeyName: "matching.workerrps", Description: "MatchingWorkerRPS is background-processing request rate per second for each matching host", DefaultValue: UnlimitedRPS, }, MatchingDomainUserRPS: { KeyName: "matching.domainrps", Description: "MatchingDomainUserRPS is request rate per domain per second for each matching host", DefaultValue: 0, }, MatchingDomainWorkerRPS: { KeyName: "matching.domainworkerrps", Description: "MatchingDomainWorkerRPS is background-processing request rate per domain per second for each matching host", DefaultValue: UnlimitedRPS, }, MatchingPersistenceMaxQPS: { KeyName: "matching.persistenceMaxQPS", Description: "MatchingPersistenceMaxQPS is the max qps matching host can query DB", DefaultValue: 3000, }, MatchingPersistenceGlobalMaxQPS: { KeyName: "matching.persistenceGlobalMaxQPS", Description: "MatchingPersistenceGlobalMaxQPS is the max qps matching cluster can query DB", DefaultValue: 0, }, MatchingMinTaskThrottlingBurstSize: { KeyName: "matching.minTaskThrottlingBurstSize", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingMinTaskThrottlingBurstSize is the minimum burst size for task list throttling", DefaultValue: 1, }, MatchingGetTasksBatchSize: { KeyName: "matching.getTasksBatchSize", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingGetTasksBatchSize is the maximum batch size to fetch from the task buffer", DefaultValue: 1000, }, MatchingOutstandingTaskAppendsThreshold: { KeyName: "matching.outstandingTaskAppendsThreshold", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingOutstandingTaskAppendsThreshold is the threshold for outstanding task appends", DefaultValue: 250, }, MatchingMaxTaskBatchSize: { KeyName: "matching.maxTaskBatchSize", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingMaxTaskBatchSize is max batch size for task writer", DefaultValue: 100, }, MatchingMaxTaskDeleteBatchSize: { KeyName: "matching.maxTaskDeleteBatchSize", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingMaxTaskDeleteBatchSize is the max batch size for range deletion of tasks", DefaultValue: 100, }, MatchingThrottledLogRPS: { KeyName: "matching.throttledLogRPS", Description: "MatchingThrottledLogRPS is the rate limit on number of log messages emitted per second for throttled logger", DefaultValue: 20, }, MatchingNumTasklistWritePartitions: { KeyName: "matching.numTasklistWritePartitions", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingNumTasklistWritePartitions is the number of write partitions for a task list", DefaultValue: 1, }, MatchingNumTasklistReadPartitions: { KeyName: "matching.numTasklistReadPartitions", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingNumTasklistReadPartitions is the number of read partitions for a task list", DefaultValue: 1, }, MatchingForwarderMaxOutstandingPolls: { KeyName: "matching.forwarderMaxOutstandingPolls", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingForwarderMaxOutstandingPolls is the max number of inflight polls from the forwarder", DefaultValue: 1, }, MatchingForwarderMaxOutstandingTasks: { KeyName: "matching.forwarderMaxOutstandingTasks", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingForwarderMaxOutstandingTasks is the max number of inflight addTask/queryTask from the forwarder", DefaultValue: 1, }, MatchingForwarderMaxRatePerSecond: { KeyName: "matching.forwarderMaxRatePerSecond", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingForwarderMaxRatePerSecond is the max rate at which add/query can be forwarded", DefaultValue: 10, }, MatchingForwarderMaxChildrenPerNode: { KeyName: "matching.forwarderMaxChildrenPerNode", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingForwarderMaxChildrenPerNode is the max number of children per node in the task list partition tree", DefaultValue: 20, }, MatchingReadRangeSize: { KeyName: "matching.readRangeSize", Description: "MatchingReadRangeSize is the read range size for the task reader", DefaultValue: 50000, }, MatchingPartitionUpscaleRPS: { KeyName: "matching.partitionUpscaleRPS", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingPartitionUpscaleRPS is the threshold of adding tasks RPS per partition to trigger upscale", DefaultValue: 200, }, MatchingIsolationGroupsPerPartition: { KeyName: "matching.isolationGroupsPerPartition", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingIsolationGroupsPerPartition is the target number of isolation groups to assign to each partition", DefaultValue: 2, }, MatchingPercentageOnboardedToShardManager: { KeyName: "matching.percentageOnboardedToShardManager", Description: "MatchingPercentageOnboardedToShardManager is the percentage of task lists that will be onboarded to the shard manager", DefaultValue: 0, }, HistoryRPS: { KeyName: "history.rps", Description: "HistoryRPS is request rate per second for each history host", DefaultValue: 3000, }, HistoryPersistenceMaxQPS: { KeyName: "history.persistenceMaxQPS", Description: "HistoryPersistenceMaxQPS is the max qps history host can query DB", DefaultValue: 9000, }, HistoryPersistenceGlobalMaxQPS: { KeyName: "history.persistenceGlobalMaxQPS", Description: "HistoryPersistenceGlobalMaxQPS is the max qps history cluster can query DB", DefaultValue: 0, }, HistoryVisibilityOpenMaxQPS: { KeyName: "history.historyVisibilityOpenMaxQPS", Filters: []Filter{DomainName}, Description: "HistoryVisibilityOpenMaxQPS is max qps one history host can write visibility open_executions", DefaultValue: 300, }, HistoryVisibilityClosedMaxQPS: { KeyName: "history.historyVisibilityClosedMaxQPS", Filters: []Filter{DomainName}, Description: "HistoryVisibilityClosedMaxQPS is max qps one history host can write visibility closed_executions", DefaultValue: 300, }, HistoryCacheInitialSize: { KeyName: "history.cacheInitialSize", Description: "HistoryCacheInitialSize is initial size of history cache", DefaultValue: 128, }, HistoryCacheMaxSize: { KeyName: "history.cacheMaxSize", Description: "HistoryCacheMaxSize is max size of history cache", DefaultValue: 512, }, ExecutionCacheMaxByteSize: { KeyName: "history.executionCacheMaxByteSize", Description: "ExecutionCacheMaxByteSize is max size of execution cache in bytes", DefaultValue: 0, }, EventsCacheInitialCount: { KeyName: "history.eventsCacheInitialSize", Description: "EventsCacheInitialCount is initial count of events cache", DefaultValue: 128, }, EventsCacheMaxCount: { KeyName: "history.eventsCacheMaxSize", Description: "EventsCacheMaxCount is max count of events cache", DefaultValue: 512, }, EventsCacheMaxSize: { KeyName: "history.eventsCacheMaxSizeInBytes", Description: "EventsCacheMaxSize is max size of events cache in bytes", DefaultValue: 0, }, EventsCacheGlobalInitialCount: { KeyName: "history.eventsCacheGlobalInitialSize", Description: "EventsCacheGlobalInitialCount is initial count of global events cache", DefaultValue: 4096, }, EventsCacheGlobalMaxCount: { KeyName: "history.eventsCacheGlobalMaxSize", Description: "EventsCacheGlobalMaxCount is max count of global events cache", DefaultValue: 131072, }, AcquireShardConcurrency: { KeyName: "history.acquireShardConcurrency", Description: "AcquireShardConcurrency is number of goroutines that can be used to acquire shards in the shard controller.", DefaultValue: 1, }, TaskProcessRPS: { KeyName: "history.taskProcessRPS", Filters: []Filter{DomainName}, Description: "TaskProcessRPS is the task processing rate per second for each domain", DefaultValue: 1000, }, TaskSchedulerType: { KeyName: "history.taskSchedulerType", Description: "TaskSchedulerType is the task scheduler type for priority task processor", DefaultValue: 2, // int(task.SchedulerTypeWRR), }, TaskSchedulerWorkerCount: { KeyName: "history.taskSchedulerWorkerCount", Description: "TaskSchedulerWorkerCount is the number of workers per host in task scheduler", DefaultValue: 200, }, TaskSchedulerQueueSize: { KeyName: "history.taskSchedulerQueueSize", Description: "TaskSchedulerQueueSize is the size of task channel for host level task scheduler", DefaultValue: 10000, }, TaskSchedulerDispatcherCount: { KeyName: "history.taskSchedulerDispatcherCount", Description: "TaskSchedulerDispatcherCount is the number of task dispatcher in task scheduler (only applies to host level task scheduler)", DefaultValue: 1, }, TaskSchedulerGlobalDomainRPS: { KeyName: "history.taskSchedulerGlobalDomainRPS", Filters: []Filter{DomainName}, Description: "TaskSchedulerGlobalDomainRPS is the task scheduling domain rate limit per second for the whole Cadence cluster", DefaultValue: 1000, }, TaskCriticalRetryCount: { KeyName: "history.taskCriticalRetryCount", Description: "TaskCriticalRetryCount is the critical retry count for background tasks, when task attempt exceeds this threshold:- task attempt metrics and additional error logs will be emitted- task priority will be lowered", DefaultValue: 50, }, QueueProcessorSplitMaxLevel: { KeyName: "history.queueProcessorSplitMaxLevel", Description: "QueueProcessorSplitMaxLevel is the max processing queue level", DefaultValue: 2, // 3 levels, start from 0 }, QueueMaxPendingTaskCount: { KeyName: "history.queueMaxPendingTaskCount", Description: "QueueMaxPendingTaskCount is the max number of pending tasks in the queue", DefaultValue: 10000, }, QueueCriticalPendingTaskCount: { KeyName: "history.queueCriticalPendingTaskCount", Description: "QueueCriticalPendingTaskCount is the critical pending task count for the queue, which is supposed to be less than QueueMaxPendingTaskCount", DefaultValue: 9000, }, TimerTaskBatchSize: { KeyName: "history.timerTaskBatchSize", Description: "TimerTaskBatchSize is batch size for timer processor to process tasks", DefaultValue: 100, }, TimerTaskDeleteBatchSize: { KeyName: "history.timerTaskDeleteBatchSize", Description: "TimerTaskDeleteBatchSize is batch size for timer processor to delete timer tasks", DefaultValue: 4000, }, TimerProcessorGetFailureRetryCount: { KeyName: "history.timerProcessorGetFailureRetryCount", Description: "TimerProcessorGetFailureRetryCount is retry count for timer processor get failure operation", DefaultValue: 5, }, TimerProcessorCompleteTimerFailureRetryCount: { KeyName: "history.timerProcessorCompleteTimerFailureRetryCount", Description: "TimerProcessorCompleteTimerFailureRetryCount is retry count for timer processor complete timer operation", DefaultValue: 10, }, TimerProcessorFailoverMaxPollRPS: { KeyName: "history.timerProcessorFailoverMaxPollRPS", Description: "TimerProcessorFailoverMaxPollRPS is max poll rate per second for timer processor", DefaultValue: 1, }, TimerProcessorMaxPollRPS: { KeyName: "history.timerProcessorMaxPollRPS", Description: "TimerProcessorMaxPollRPS is max poll rate per second for timer processor", DefaultValue: 20, }, TimerProcessorMaxRedispatchQueueSize: { KeyName: "history.timerProcessorMaxRedispatchQueueSize", Description: "TimerProcessorMaxRedispatchQueueSize is the threshold of the number of tasks in the redispatch queue for timer processor", DefaultValue: 10000, }, TimerProcessorHistoryArchivalSizeLimit: { KeyName: "history.timerProcessorHistoryArchivalSizeLimit", Description: "TimerProcessorHistoryArchivalSizeLimit is the max history size for inline archival", DefaultValue: 500 * 1024, }, TransferTaskBatchSize: { KeyName: "history.transferTaskBatchSize", Description: "TransferTaskBatchSize is batch size for transferQueueProcessor", DefaultValue: 100, }, TransferTaskDeleteBatchSize: { KeyName: "history.transferTaskDeleteBatchSize", Description: "TransferTaskDeleteBatchSize is batch size for transferQueueProcessor to delete transfer tasks", DefaultValue: 4000, }, TransferProcessorFailoverMaxPollRPS: { KeyName: "history.transferProcessorFailoverMaxPollRPS", Description: "TransferProcessorFailoverMaxPollRPS is max poll rate per second for transferQueueProcessor", DefaultValue: 1, }, TransferProcessorMaxPollRPS: { KeyName: "history.transferProcessorMaxPollRPS", Description: "TransferProcessorMaxPollRPS is max poll rate per second for transferQueueProcessor", DefaultValue: 20, }, TransferProcessorCompleteTransferFailureRetryCount: { KeyName: "history.transferProcessorCompleteTransferFailureRetryCount", Description: "TransferProcessorCompleteTransferFailureRetryCount is times of retry for failure", DefaultValue: 10, }, TransferProcessorMaxRedispatchQueueSize: { KeyName: "history.transferProcessorMaxRedispatchQueueSize", Description: "TransferProcessorMaxRedispatchQueueSize is the threshold of the number of tasks in the redispatch queue for transferQueueProcessor", DefaultValue: 10000, }, ReplicatorTaskBatchSize: { KeyName: "history.replicatorTaskBatchSize", Description: "ReplicatorTaskBatchSize is batch size for ReplicatorProcessor", DefaultValue: 25, }, ReplicatorMinTaskBatchSize: { KeyName: "history.replicatorMinTaskBatchSize", Description: "ReplicatorMinTaskBatchSize is minimum batch size for ReplicatorProcessor", Filters: []Filter{ShardID, ClusterName}, DefaultValue: 1, }, ReplicatorMaxTaskBatchSize: { KeyName: "history.replicatorMaxTaskBatchSize", Description: "ReplicatorMaxTaskBatchSize is maximum size for ReplicatorProcessor", Filters: []Filter{ShardID, ClusterName}, DefaultValue: 1000, }, ReplicatorTaskBatchStepCount: { KeyName: "history.replicatorTaskBatchStepCount", Description: "ReplicatorTaskBatchStepCount is how many batch size values between ReplicatorMinTaskBatchSize and ReplicatorMaxTaskBatchSize are used by ReplicatorProcessor to adjust the batch size of the replication tasks. Less value will make the adjustment more aggressive. It cannot be less than 3, otherwise only ReplicatorMinTaskBatchSize and ReplicatorMaxTaskBatchSize will be used.", Filters: []Filter{ShardID, ClusterName}, DefaultValue: 10, }, ReplicatorTaskDeleteBatchSize: { KeyName: "history.replicatorTaskDeleteBatchSize", Description: "ReplicatorTaskDeleteBatchSize is batch size for ReplicatorProcessor to delete replication tasks", DefaultValue: 4000, }, HistoryNodeDeleteBatchSize: { KeyName: "history.historyNodeDeleteBatchSize", Description: "HistoryNodeDeleteBatchSize is batch size for deleting history nodes", DefaultValue: 1000, }, ReplicatorReadTaskMaxRetryCount: { KeyName: "history.replicatorReadTaskMaxRetryCount", Description: "ReplicatorReadTaskMaxRetryCount is the number of read replication task retry time", DefaultValue: 3, }, ReplicatorCacheCapacity: { KeyName: "history.replicatorCacheCapacity", Description: "ReplicatorCacheCapacity is the capacity of replication cache in number of tasks", DefaultValue: 0, }, ReplicatorCacheMaxSize: { KeyName: "history.replicatorCacheMaxSize", Description: "ReplicatorCacheMaxSize is the max size of the replication cache in bytes", DefaultValue: 0, }, ReplicationBudgetManagerMaxSizeBytes: { KeyName: "history.replicationBudgetManagerMaxSizeBytes", Description: "ReplicationBudgetManagerMaxSizeBytes is the max size of the replication budget manager cache in bytes", DefaultValue: 0, }, ReplicationBudgetManagerMaxSizeCount: { KeyName: "history.replicationBudgetManagerMaxSizeCount", Description: "ReplicationBudgetManagerMaxSizeCount is the max count of the replication budget manager cache", DefaultValue: 0, }, MaximumBufferedEventsBatch: { KeyName: "history.maximumBufferedEventsBatch", Description: "MaximumBufferedEventsBatch is max number of buffer event in mutable state", DefaultValue: 100, }, MaximumSignalsPerExecution: { KeyName: "history.maximumSignalsPerExecution", Filters: []Filter{DomainName}, Description: "MaximumSignalsPerExecution is max number of signals supported by single execution", DefaultValue: 10000, // 10K signals should big enough given workflow execution has 200K history lengh limit. It needs to be non-zero to protect continueAsNew from infinit loop }, NumArchiveSystemWorkflows: { KeyName: "history.numArchiveSystemWorkflows", Description: "NumArchiveSystemWorkflows is key for number of archive system workflows running in total", DefaultValue: 1000, }, ArchiveRequestRPS: { KeyName: "history.archiveRequestRPS", Description: "ArchiveRequestRPS is the rate limit on the number of archive request per second", DefaultValue: 300, // should be much smaller than frontend RPS }, ArchiveInlineHistoryRPS: { KeyName: "history.archiveInlineHistoryRPS", Description: "ArchiveInlineHistoryRPS is the (per instance) rate limit on the number of inline history archival attempts per second", DefaultValue: 1000, }, ArchiveInlineHistoryGlobalRPS: { KeyName: "history.archiveInlineHistoryGlobalRPS", Description: "ArchiveInlineHistoryGlobalRPS is the global rate limit on the number of inline history archival attempts per second", DefaultValue: 10000, }, ArchiveInlineVisibilityRPS: { KeyName: "history.archiveInlineVisibilityRPS", Description: "ArchiveInlineVisibilityRPS is the (per instance) rate limit on the number of inline visibility archival attempts per second", DefaultValue: 1000, }, ArchiveInlineVisibilityGlobalRPS: { KeyName: "history.archiveInlineVisibilityGlobalRPS", Description: "ArchiveInlineVisibilityGlobalRPS is the global rate limit on the number of inline visibility archival attempts per second", DefaultValue: 10000, }, HistoryMaxAutoResetPoints: { KeyName: "history.historyMaxAutoResetPoints", Filters: []Filter{DomainName}, Description: "HistoryMaxAutoResetPoints is the key for max number of auto reset points stored in mutableState", DefaultValue: 20, }, ParentClosePolicyThreshold: { KeyName: "history.parentClosePolicyThreshold", Filters: []Filter{DomainName}, Description: "ParentClosePolicyThreshold decides that parent close policy will be processed by sys workers(if enabled) if the number of children is greater than or equal to this threshold", DefaultValue: 10, }, ParentClosePolicyBatchSize: { KeyName: "history.parentClosePolicyBatchSize", Filters: []Filter{DomainName}, Description: "ParentClosePolicyBatchSize is the batch size of parent close policy processed by sys workers", DefaultValue: 200, }, NumParentClosePolicySystemWorkflows: { KeyName: "history.numParentClosePolicySystemWorkflows", Description: "NumParentClosePolicySystemWorkflows is key for number of parentClosePolicy system workflows running in total", DefaultValue: 10, }, HistoryThrottledLogRPS: { KeyName: "history.throttledLogRPS", Description: "HistoryThrottledLogRPS is the rate limit on number of log messages emitted per second for throttled logger", DefaultValue: 4, }, DecisionRetryCriticalAttempts: { KeyName: "history.decisionRetryCriticalAttempts", Description: "DecisionRetryCriticalAttempts is decision attempt threshold for logging and emiting metrics", DefaultValue: 10, }, DecisionRetryMaxAttempts: { KeyName: "history.decisionRetryMaxAttempts", Filters: []Filter{DomainName}, Description: "DecisionRetryMaxAttempts is the max limit for decision retry attempts. 0 indicates infinite number of attempts.", DefaultValue: 1000, }, NormalDecisionScheduleToStartMaxAttempts: { KeyName: "history.normalDecisionScheduleToStartMaxAttempts", Filters: []Filter{DomainName}, Description: "NormalDecisionScheduleToStartMaxAttempts is the maximum decision attempt for creating a scheduleToStart timeout timer for normal (non-sticky) decision", DefaultValue: 0, }, MaxBufferedQueryCount: { KeyName: "history.MaxBufferedQueryCount", Description: "MaxBufferedQueryCount indicates the maximum number of queries which can be buffered at a given time for a single workflow", DefaultValue: 1, }, MutableStateChecksumGenProbability: { KeyName: "history.mutableStateChecksumGenProbability", Filters: []Filter{DomainName}, Description: "MutableStateChecksumGenProbability is the probability [0-100] that checksum will be generated for mutable state", DefaultValue: 0, }, MutableStateChecksumVerifyProbability: { KeyName: "history.mutableStateChecksumVerifyProbability", Filters: []Filter{DomainName}, Description: "MutableStateChecksumVerifyProbability is the probability [0-100] that checksum will be verified for mutable state", DefaultValue: 0, }, TaskSchedulerMigrationRatio: { KeyName: "history.taskSchedulerMigrationRatio", Description: "DEPRECATED: TaskSchedulerMigrationRatio is the ratio of task that is migrated to new scheduler", DefaultValue: 0, }, MaxActivityCountDispatchByDomain: { KeyName: "history.maxActivityCountDispatchByDomain", Description: "MaxActivityCountDispatchByDomain max # of activity tasks to dispatch to matching before creating transfer tasks. This is an performance optimization to skip activity scheduling efforts.", DefaultValue: 0, }, ReplicationTaskFetcherParallelism: { KeyName: "history.ReplicationTaskFetcherParallelism", Description: "ReplicationTaskFetcherParallelism determines how many go routines we spin up for fetching tasks", DefaultValue: 1, }, ReplicationTaskProcessorErrorRetryMaxAttempts: { KeyName: "history.ReplicationTaskProcessorErrorRetryMaxAttempts", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorErrorRetryMaxAttempts is the max retry attempts for applying replication tasks", DefaultValue: 10, }, WorkflowIDExternalRPS: { KeyName: "history.workflowIDExternalRPS", Filters: []Filter{DomainName}, Description: "WorkflowIDExternalRPS is the rate limit per workflowID for external calls", DefaultValue: UnlimitedRPS, }, WorkflowIDInternalRPS: { KeyName: "history.workflowIDInternalRPS", Filters: []Filter{DomainName}, Description: "WorkflowIDInternalRPS is the rate limit per workflowID for internal calls", DefaultValue: UnlimitedRPS, }, WorkerPersistenceMaxQPS: { KeyName: "worker.persistenceMaxQPS", Description: "WorkerPersistenceMaxQPS is the max qps worker host can query DB", DefaultValue: 500, }, WorkerPersistenceGlobalMaxQPS: { KeyName: "worker.persistenceGlobalMaxQPS", Description: "WorkerPersistenceGlobalMaxQPS is the max qps worker cluster can query DB", DefaultValue: 0, }, WorkerIndexerConcurrency: { KeyName: "worker.indexerConcurrency", Description: "WorkerIndexerConcurrency is the max concurrent messages to be processed at any given time", DefaultValue: 1000, }, WorkerESProcessorNumOfWorkers: { KeyName: "worker.ESProcessorNumOfWorkers", Description: "WorkerESProcessorNumOfWorkers is num of workers for esProcessor", DefaultValue: 1, }, WorkerESProcessorBulkActions: { KeyName: "worker.ESProcessorBulkActions", Description: "WorkerESProcessorBulkActions is max number of requests in bulk for esProcessor", DefaultValue: 1000, }, WorkerESProcessorBulkSize: { KeyName: "worker.ESProcessorBulkSize", Description: "WorkerESProcessorBulkSize is max total size of bulk in bytes for esProcessor", DefaultValue: 2 << 24, // 16MB }, WorkerArchiverConcurrency: { KeyName: "worker.ArchiverConcurrency", Description: "WorkerArchiverConcurrency is controls the number of coroutines handling archival work per archival workflow", DefaultValue: 50, }, WorkerArchivalsPerIteration: { KeyName: "worker.ArchivalsPerIteration", Description: "WorkerArchivalsPerIteration is controls the number of archivals handled in each iteration of archival workflow", DefaultValue: 1000, }, WorkerThrottledLogRPS: { KeyName: "worker.throttledLogRPS", Description: "WorkerThrottledLogRPS is the rate limit on number of log messages emitted per second for throttled logger", DefaultValue: 20, }, ScannerPersistenceMaxQPS: { KeyName: "worker.scannerPersistenceMaxQPS", Description: "ScannerPersistenceMaxQPS is the maximum rate of persistence calls from worker.Scanner", DefaultValue: 5, }, ScannerGetOrphanTasksPageSize: { KeyName: "worker.scannerGetOrphanTasksPageSize", Description: "ScannerGetOrphanTasksPageSize is the maximum number of orphans to delete in one batch", DefaultValue: 1000, }, ScannerBatchSizeForTasklistHandler: { KeyName: "worker.scannerBatchSizeForTasklistHandler", Description: "ScannerBatchSizeForTasklistHandler is for: 1. max number of tasks to query per call(get tasks for tasklist) in the scavenger handler. 2. The scavenger then uses the return to decide if a tasklist can be deleted. It's better to keep it a relatively high number to let it be more efficient.", DefaultValue: 1000, }, ScannerMaxTasksProcessedPerTasklistJob: { KeyName: "worker.scannerMaxTasksProcessedPerTasklistJob", Description: "ScannerMaxTasksProcessedPerTasklistJob is the number of tasks to process for a tasklist in each workflow run", DefaultValue: 256, }, ConcreteExecutionsScannerConcurrency: { KeyName: "worker.executionsScannerConcurrency", Description: "ConcreteExecutionsScannerConcurrency indicates the concurrency of concrete execution scanner", DefaultValue: 25, }, ConcreteExecutionsScannerBlobstoreFlushThreshold: { KeyName: "worker.executionsScannerBlobstoreFlushThreshold", Description: "ConcreteExecutionsScannerBlobstoreFlushThreshold indicates the flush threshold of blobstore in concrete execution scanner", DefaultValue: 100, }, ConcreteExecutionsScannerActivityBatchSize: { KeyName: "worker.executionsScannerActivityBatchSize", Description: "ConcreteExecutionsScannerActivityBatchSize indicates the batch size of scanner activities", DefaultValue: 25, }, ConcreteExecutionsScannerPersistencePageSize: { KeyName: "worker.executionsScannerPersistencePageSize", Description: "ConcreteExecutionsScannerPersistencePageSize indicates the page size of execution persistence fetches in concrete execution scanner", DefaultValue: 1000, }, CurrentExecutionsScannerConcurrency: { KeyName: "worker.currentExecutionsConcurrency", Description: "CurrentExecutionsScannerConcurrency indicates the concurrency of current executions scanner", DefaultValue: 25, }, CurrentExecutionsScannerBlobstoreFlushThreshold: { KeyName: "worker.currentExecutionsBlobstoreFlushThreshold", Description: "CurrentExecutionsScannerBlobstoreFlushThreshold indicates the flush threshold of blobstore in current executions scanner", DefaultValue: 100, }, CurrentExecutionsScannerActivityBatchSize: { KeyName: "worker.currentExecutionsActivityBatchSize", Description: "CurrentExecutionsScannerActivityBatchSize indicates the batch size of scanner activities", DefaultValue: 25, }, CurrentExecutionsScannerPersistencePageSize: { KeyName: "worker.currentExecutionsPersistencePageSize", Description: "CurrentExecutionsScannerPersistencePageSize indicates the page size of execution persistence fetches in current executions scanner", DefaultValue: 1000, }, TimersScannerConcurrency: { KeyName: "worker.timersScannerConcurrency", Description: "TimersScannerConcurrency is the concurrency of timers scanner", DefaultValue: 5, }, TimersScannerPersistencePageSize: { KeyName: "worker.timersScannerPersistencePageSize", Description: "TimersScannerPersistencePageSize is the page size of timers persistence fetches in timers scanner", DefaultValue: 1000, }, TimersScannerBlobstoreFlushThreshold: { KeyName: "worker.timersScannerBlobstoreFlushThreshold", Description: "TimersScannerBlobstoreFlushThreshold is threshold to flush blob store", DefaultValue: 100, }, TimersScannerActivityBatchSize: { KeyName: "worker.timersScannerActivityBatchSize", Description: "TimersScannerActivityBatchSize is TimersScannerActivityBatchSize", DefaultValue: 25, }, TimersScannerPeriodStart: { KeyName: "worker.timersScannerPeriodStart", Description: "TimersScannerPeriodStart is interval start for fetching scheduled timers", DefaultValue: 24, }, TimersScannerPeriodEnd: { KeyName: "worker.timersScannerPeriodEnd", Description: "TimersScannerPeriodEnd is interval end for fetching scheduled timers", DefaultValue: 3, }, ESAnalyzerMaxNumDomains: { KeyName: "worker.ESAnalyzerMaxNumDomains", Description: "ESAnalyzerMaxNumDomains defines how many domains to check", DefaultValue: 500, }, ESAnalyzerMaxNumWorkflowTypes: { KeyName: "worker.ESAnalyzerMaxNumWorkflowTypes", Description: "ESAnalyzerMaxNumWorkflowTypes defines how many workflow types to check per domain", DefaultValue: 100, }, ESAnalyzerNumWorkflowsToRefresh: { KeyName: "worker.ESAnalyzerNumWorkflowsToRefresh", Description: "ESAnalyzerNumWorkflowsToRefresh controls how many workflows per workflow type should be refreshed per workflow type", DefaultValue: 100, }, ESAnalyzerMinNumWorkflowsForAvg: { KeyName: "worker.ESAnalyzerMinNumWorkflowsForAvg", Description: "ESAnalyzerMinNumWorkflowsForAvg controls how many workflows to have at least to rely on workflow run time avg per type", DefaultValue: 100, }, VisibilityArchivalQueryMaxRangeInDays: { KeyName: "frontend.visibilityArchivalQueryMaxRangeInDays", Description: "VisibilityArchivalQueryMaxRangeInDays is the maximum number of days for a visibility archival query", DefaultValue: 60, }, VisibilityArchivalQueryMaxQPS: { KeyName: "frontend.visibilityArchivalQueryMaxQPS", Description: "VisibilityArchivalQueryMaxQPS is the timeout for a visibility archival query", DefaultValue: 1, }, WorkflowDeletionJitterRange: { KeyName: "system.workflowDeletionJitterRange", Description: "WorkflowDeletionJitterRange defines the duration in minutes for workflow close tasks jittering", DefaultValue: 60, }, SampleLoggingRate: { KeyName: "system.sampleLoggingRate", Description: "The rate for which sampled logs are logged at. 100 means 1/100 is logged", DefaultValue: 100, }, LargeShardHistorySizeMetricThreshold: { KeyName: "system.largeShardHistorySizeMetricThreshold", Description: "defines the threshold for what consititutes a large history size to alert on, default is 10mb", DefaultValue: 10485760, }, LargeShardHistoryEventMetricThreshold: { KeyName: "system.largeShardHistoryEventMetricThreshold", Description: "defines the threshold for what consititutes a large history event length to alert on, default is 50k", DefaultValue: 50 * 1024, }, LargeShardHistoryBlobMetricThreshold: { KeyName: "system.largeShardHistoryBlobMetricThreshold", Description: "defines the threshold for what consititutes a large history blob write to alert on, default is 1/4mb", DefaultValue: 262144, }, IsolationGroupStateUpdateRetryAttempts: { KeyName: "system.isolationGroupStateUpdateRetryAttempts", Description: "The number of attempts to push Isolation group configuration to the config store", DefaultValue: 2, }, DeleteHistoryEventContextTimeout: { KeyName: "system.deleteHistoryEventContextTimeout", Description: "This is the number of seconds allowed for a deleteHistoryEvent task to the database", DefaultValue: 30, }, QueueMaxVirtualQueueCount: { KeyName: "history.queueMaxVirtualQueueCount", Description: "QueueMaxVirtualQueueCount is the max number of virtual queues", DefaultValue: 2, }, ShardDistributorMaxEtcdTxnOps: { KeyName: "shardDistributor.maxEtcdTxnOps", Description: "ShardDistributorMaxEtcdTxnOps is the maximum number of operations per etcd transaction, must not exceed the etcd cluster's configured --max-txn-ops limit", DefaultValue: 128, }, } var BoolKeys = map[BoolKey]DynamicBool{ TestGetBoolPropertyKey: { KeyName: "testGetBoolPropertyKey", Description: "", DefaultValue: false, }, TestGetBoolPropertyFilteredByDomainIDKey: { KeyName: "testGetBoolPropertyFilteredByDomainIDKey", Description: "", DefaultValue: false, }, TestGetBoolPropertyFilteredByTaskListInfoKey: { KeyName: "testGetBoolPropertyFilteredByTaskListInfoKey", Description: "", DefaultValue: false, }, TestGetBoolPropertyFilteredByDomainKey: { KeyName: "testGetBoolPropertyFilteredByDomainKey", Description: "", DefaultValue: false, Filters: nil, }, TestGetBoolPropertyFilteredByDomainIDAndWorkflowIDKey: { KeyName: "testGetBoolPropertyFilteredByDomainIDandWorkflowIDKey", Description: "", DefaultValue: false, Filters: nil, }, TestGetBoolPropertyFilteredByShardIDKey: { KeyName: "testGetBoolPropertyFilteredByShardIDKey", Description: "", DefaultValue: false, Filters: []Filter{ShardID}, }, EnableVisibilitySampling: { KeyName: "system.enableVisibilitySampling", Description: "EnableVisibilitySampling is key for enable visibility sampling for basic(DB based) visibility", DefaultValue: false, // ... Filters: nil, }, EnableReadFromClosedExecutionV2: { KeyName: "system.enableReadFromClosedExecutionV2", Description: "EnableReadFromClosedExecutionV2 is key for enable read from cadence_visibility.closed_executions_v2", DefaultValue: false, }, EnableLogCustomerQueryParameter: { KeyName: "system.enableLogCustomerQueryParameter", Filters: []Filter{DomainName}, Description: "EnableLogCustomerQueryParameter is key for enable log customer query parameters", DefaultValue: false, }, EmitShardDiffLog: { KeyName: "history.emitShardDiffLog", Description: "EmitShardDiffLog is whether emit the shard diff log", DefaultValue: false, }, EnableRecordWorkflowExecutionUninitialized: { KeyName: "history.enableRecordWorkflowExecutionUninitialized", Description: "EnableRecordWorkflowExecutionUninitialized enables record workflow execution uninitialized state in ElasticSearch", DefaultValue: false, }, EnableCleanupOrphanedHistoryBranchOnWorkflowCreation: { KeyName: "history.enableCleanupOrphanedHistoryBranchOnWorkflowCreation", Description: "EnableCleanupOrphanedHistoryBranchOnWorkflowCreation enables cleanup of orphaned history branches when CreateWorkflowExecution fails", DefaultValue: true, }, DisableListVisibilityByFilter: { KeyName: "frontend.disableListVisibilityByFilter", Filters: []Filter{DomainName}, Description: "DisableListVisibilityByFilter is config to disable list open/close workflow using filter", DefaultValue: false, }, EnableReadFromHistoryArchival: { KeyName: "system.enableReadFromHistoryArchival", Description: "EnableReadFromHistoryArchival is key for enabling reading history from archival store", DefaultValue: true, }, EnableReadFromVisibilityArchival: { KeyName: "system.enableReadFromVisibilityArchival", Description: "EnableReadFromVisibilityArchival is key for enabling reading visibility from archival store to override the value from static config.", DefaultValue: true, }, EnableDomainNotActiveAutoForwarding: { KeyName: "system.enableDomainNotActiveAutoForwarding", Filters: []Filter{DomainName}, Description: "EnableDomainNotActiveAutoForwarding decides requests form which domain will be forwarded to active cluster if domain is not active in current cluster. Only when selected-api-forwarding or all-domain-apis-forwarding is the policy in ClusterRedirectionPolicy(in static config). If the policy is noop(default) this flag is not doing anything.", DefaultValue: true, }, EnableGracefulFailover: { KeyName: "system.enableGracefulFailover", Description: "EnableGracefulFailover is whether enabling graceful failover", DefaultValue: true, }, EnableDomainAuditLogging: { KeyName: "system.enableDomainAuditLogging", Description: "EnableDomainAuditLogging enables audit logging for a domain to the domain audit log table", DefaultValue: false, }, DisallowQuery: { KeyName: "system.disallowQuery", Filters: []Filter{DomainName}, Description: "DisallowQuery is the key to disallow query for a domain", DefaultValue: false, }, EnableDebugMode: { KeyName: "system.enableDebugMode", Description: "EnableDebugMode is for enabling debugging components, logs and metrics", DefaultValue: false, }, EnableGRPCOutbound: { KeyName: "system.enableGRPCOutbound", Description: "EnableGRPCOutbound is the key for enabling outbound GRPC traffic", DefaultValue: true, }, EnableSQLAsyncTransaction: { KeyName: "system.enableSQLAsyncTransaction", Description: "EnableSQLAsyncTransaction is the key for enabling async transaction", DefaultValue: false, }, EnableConnectionRetainingDirectChooser: { KeyName: "system.enableConnectionRetainingDirectChooser", Description: "EnableConnectionRetainingDirectChooser is the key for enabling connection retaining direct chooser", DefaultValue: false, }, EnableClientVersionCheck: { KeyName: "frontend.enableClientVersionCheck", Description: "EnableClientVersionCheck is enables client version check for frontend", DefaultValue: false, }, SendRawWorkflowHistory: { KeyName: "frontend.sendRawWorkflowHistory", Filters: []Filter{DomainName}, Description: "SendRawWorkflowHistory is whether to enable raw history retrieving", DefaultValue: false, }, FrontendEmitSignalNameMetricsTag: { KeyName: "frontend.emitSignalNameMetricsTag", Filters: []Filter{DomainName}, Description: "FrontendEmitSignalNameMetricsTag enables emitting signal name tag in metrics in frontend client", DefaultValue: false, }, EnableQueryAttributeValidation: { KeyName: "frontend.enableQueryAttributeValidation", Description: "EnableQueryAttributeValidation enables validation of queries' search attributes against the dynamic config whitelist", DefaultValue: true, }, MatchingEnableSyncMatch: { KeyName: "matching.enableSyncMatch", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingEnableSyncMatch is to enable sync match", DefaultValue: true, }, MatchingEnableTaskInfoLogByDomainID: { KeyName: "matching.enableTaskInfoLogByDomainID", Filters: []Filter{DomainID}, Description: "MatchingEnableTaskInfoLogByDomainID is enables info level logs for decision/activity task based on the request domainID", DefaultValue: false, }, MatchingEnableTasklistGuardAgainstOwnershipShardLoss: { KeyName: "matching.enableTasklistGuardAgainstOwnershipLoss", Description: "allows guards to ensure that tasklists don't continue processing if there's signal that they've lost ownership", DefaultValue: false, }, MatchingEnableGetNumberOfPartitionsFromCache: { KeyName: "matching.enableGetNumberOfPartitionsFromCache", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingEnableGetNumberOfPartitionsFromCache is to enable getting number of partitions from cache instead of dynamic config", DefaultValue: false, }, MatchingEnableStandbyTaskCompletion: { KeyName: "matching.enableStandbyTaskCompletion", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingEnableStandbyTaskCompletion is to enable completion of tasks in the domain's passive side", DefaultValue: true, }, MatchingEnableAdaptiveScaler: { KeyName: "matching.enableAdaptiveScaler", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingEnableAdaptiveScaler is to enable adaptive task list scaling", DefaultValue: false, }, MatchingEnablePartitionEmptyCheck: { KeyName: "matching.enablePartitionEmptyCheck", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingEnablePartitionEmptyCheck enables using TaskListStatus.empty to check if a partition is empty", DefaultValue: false, }, MatchingEnableReturnAllTaskListKinds: { KeyName: "matching.matchingReturnAllTaskListKinds", Description: "Returns TaskLists of all kinds when GetTaskListsByDomain is called. Useful in testing Cadence", DefaultValue: false, }, EventsCacheGlobalEnable: { KeyName: "history.eventsCacheGlobalEnable", Description: "EventsCacheGlobalEnable is enables global cache over all history shards", DefaultValue: false, }, QueueProcessorEnableSplit: { KeyName: "history.queueProcessorEnableSplit", Description: "QueueProcessorEnableSplit indicates whether processing queue split policy should be enabled", DefaultValue: false, }, QueueProcessorEnableRandomSplitByDomainID: { KeyName: "history.queueProcessorEnableRandomSplitByDomainID", Filters: []Filter{DomainID}, Description: "QueueProcessorEnableRandomSplitByDomainID indicates whether random queue split policy should be enabled for a domain", DefaultValue: false, }, QueueProcessorEnablePendingTaskSplitByDomainID: { KeyName: "history.queueProcessorEnablePendingTaskSplitByDomainID", Filters: []Filter{DomainID}, Description: "ueueProcessorEnablePendingTaskSplitByDomainID indicates whether pending task split policy should be enabled", DefaultValue: false, }, QueueProcessorEnableStuckTaskSplitByDomainID: { KeyName: "history.queueProcessorEnableStuckTaskSplitByDomainID", Filters: []Filter{DomainID}, Description: "QueueProcessorEnableStuckTaskSplitByDomainID indicates whether stuck task split policy should be enabled", DefaultValue: false, }, QueueProcessorEnablePersistQueueStates: { KeyName: "history.queueProcessorEnablePersistQueueStates", Description: "QueueProcessorEnablePersistQueueStates indicates whether processing queue states should be persisted", DefaultValue: true, }, QueueProcessorEnableLoadQueueStates: { KeyName: "history.queueProcessorEnableLoadQueueStates", Description: "QueueProcessorEnableLoadQueueStates indicates whether processing queue states should be loaded", DefaultValue: true, }, QueueProcessorEnableGracefulSyncShutdown: { KeyName: "history.queueProcessorEnableGracefulSyncShutdown", Description: "QueueProcessorEnableGracefulSyncShutdown indicates whether processing queue should be shutdown gracefully & synchronously", DefaultValue: false, }, TransferProcessorEnableValidator: { KeyName: "history.transferProcessorEnableValidator", Description: "TransferProcessorEnableValidator is whether validator should be enabled for transferQueueProcessor", DefaultValue: false, }, TaskSchedulerEnableRateLimiter: { KeyName: "history.taskSchedulerEnableRateLimiter", Description: "TaskSchedulerEnableRateLimiter indicates whether the task scheduler rate limiter is enabled", DefaultValue: false, }, TaskSchedulerEnableRateLimiterShadowMode: { KeyName: "history.taskSchedulerEnableRateLimiterShadowMode", Filters: []Filter{DomainName}, Description: "TaskSchedulerEnableRateLimiterShadowMode indicates whether the task scheduler rate limiter is in shadow mode", DefaultValue: true, }, TaskSchedulerEnableMigration: { KeyName: "history.taskSchedulerEnableMigration", Description: "DEPRECATED: TaskSchedulerEnableMigration indicates whether the task scheduler migration is enabled", DefaultValue: false, }, EnableAdminProtection: { KeyName: "history.enableAdminProtection", Description: "EnableAdminProtection is whether to enable admin checking", DefaultValue: false, }, EnableParentClosePolicy: { KeyName: "history.enableParentClosePolicy", Filters: []Filter{DomainName}, Description: "EnableParentClosePolicy is whether to ParentClosePolicy", DefaultValue: true, }, EnableDropStuckTaskByDomainID: { KeyName: "history.DropStuckTaskByDomain", Filters: []Filter{DomainID}, Description: "EnableDropStuckTaskByDomainID is whether stuck timer/transfer task should be dropped for a domain", DefaultValue: false, }, EnableConsistentQuery: { KeyName: "history.EnableConsistentQuery", Description: "EnableConsistentQuery indicates if consistent query is enabled for the cluster", DefaultValue: true, }, EnableConsistentQueryByDomain: { KeyName: "history.EnableConsistentQueryByDomain", Filters: []Filter{DomainName}, Description: "EnableConsistentQueryByDomain indicates if consistent query is enabled for a domain", DefaultValue: false, }, EnableContextHeaderInVisibility: { KeyName: "history.enableContextHeaderInVisibility", Filters: []Filter{DomainName}, Description: "EnableContextHeaderInVisibility is key for enable context header in visibility", DefaultValue: false, }, EnableCrossClusterOperationsForDomain: { KeyName: "history.enableCrossClusterOperations", Filters: []Filter{DomainName}, Description: "EnableCrossClusterOperationsForDomain indicates if cross cluster operations can be scheduled for a domain", DefaultValue: false, }, EnableHistoryCorruptionCheck: { KeyName: "history.enableHistoryCorruptionCheck", Filters: []Filter{DomainName}, Description: "EnableHistoryCorruptionCheck enables additional sanity check for corrupted history. This allows early catches of DB corruptions but potiantally increased latency.", DefaultValue: false, }, EnableActivityLocalDispatchByDomain: { KeyName: "history.enableActivityLocalDispatchByDomain", Filters: []Filter{DomainName}, Description: "EnableActivityLocalDispatchByDomain is allows worker to dispatch activity tasks through local tunnel after decisions are made. This is an performance optimization to skip activity scheduling efforts", DefaultValue: true, }, HistoryEnableTaskInfoLogByDomainID: { KeyName: "history.enableTaskInfoLogByDomainID", Filters: []Filter{DomainID}, Description: "HistoryEnableTaskInfoLogByDomainID is enables info level logs for decision/activity task based on the request domainID", DefaultValue: false, }, EnableReplicationTaskGeneration: { KeyName: "history.enableReplicationTaskGeneration", Filters: []Filter{DomainID, WorkflowID}, Description: "EnableReplicationTaskGeneration is the flag to control replication generation", DefaultValue: true, }, UseNewInitialFailoverVersion: { KeyName: "history.useNewInitialFailoverVersion", Description: "use the minInitialFailover version", DefaultValue: false, }, AllowArchivingIncompleteHistory: { KeyName: "worker.AllowArchivingIncompleteHistory", Description: "AllowArchivingIncompleteHistory will continue on when seeing some error like history mutated(usually caused by database consistency issues)", DefaultValue: false, }, EnableCleaningOrphanTaskInTasklistScavenger: { KeyName: "worker.enableCleaningOrphanTaskInTasklistScavenger", Description: "EnableCleaningOrphanTaskInTasklistScavenger indicates if enabling the scanner to clean up orphan tasks", DefaultValue: false, }, TaskListScannerEnabled: { KeyName: "worker.taskListScannerEnabled", Description: "TaskListScannerEnabled indicates if task list scanner should be started as part of worker.Scanner", DefaultValue: true, }, HistoryScannerEnabled: { KeyName: "worker.historyScannerEnabled", Description: "HistoryScannerEnabled indicates if history scanner should be started as part of worker.Scanner", DefaultValue: false, }, ConcreteExecutionsScannerEnabled: { KeyName: "worker.executionsScannerEnabled", Description: "ConcreteExecutionsScannerEnabled indicates if executions scanner should be started as part of worker.Scanner", DefaultValue: false, }, ConcreteExecutionsScannerInvariantCollectionMutableState: { KeyName: "worker.executionsScannerInvariantCollectionMutableState", Description: "ConcreteExecutionsScannerInvariantCollectionMutableState indicates if mutable state invariant checks should be run", DefaultValue: true, }, ConcreteExecutionsScannerInvariantCollectionHistory: { KeyName: "worker.executionsScannerInvariantCollectionHistory", Description: "ConcreteExecutionsScannerInvariantCollectionHistory indicates if history invariant checks should be run", DefaultValue: true, }, ConcreteExecutionsScannerInvariantCollectionStale: { KeyName: "worker.executionsScannerInvariantCollectionStale", Description: "ConcreteExecutionsScannerInvariantCollectionStale indicates if the stale-workflow invariant should be run", DefaultValue: false, // may be enabled after further verification, but for now it's a bit too risky to enable by default }, ConcreteExecutionsFixerInvariantCollectionMutableState: { KeyName: "worker.executionsFixerInvariantCollectionMutableState", Description: "ConcreteExecutionsFixerInvariantCollectionMutableState indicates if mutable state invariant checks should be run", DefaultValue: true, }, ConcreteExecutionsFixerInvariantCollectionHistory: { KeyName: "worker.executionsFixerInvariantCollectionHistory", Description: "ConcreteExecutionsFixerInvariantCollectionHistory indicates if history invariant checks should be run", DefaultValue: true, }, ConcreteExecutionsFixerInvariantCollectionStale: { KeyName: "worker.executionsFixerInvariantCollectionStale", Description: "ConcreteExecutionsFixerInvariantCollectionStale indicates if the stale-workflow invariant should be run", DefaultValue: false, // may be enabled after further verification, but for now it's a bit too risky to enable by default }, CurrentExecutionsScannerEnabled: { KeyName: "worker.currentExecutionsScannerEnabled", Description: "CurrentExecutionsScannerEnabled indicates if current executions scanner should be started as part of worker.Scanner", DefaultValue: false, }, CurrentExecutionsScannerInvariantCollectionHistory: { KeyName: "worker.currentExecutionsScannerInvariantCollectionHistory", Description: "CurrentExecutionsScannerInvariantCollectionHistory indicates if history invariant checks should be run", DefaultValue: true, }, CurrentExecutionsScannerInvariantCollectionMutableState: { KeyName: "worker.currentExecutionsInvariantCollectionMutableState", Description: "CurrentExecutionsScannerInvariantCollectionMutableState indicates if mutable state invariant checks should be run", DefaultValue: true, }, EnableBatcher: { KeyName: "worker.enableBatcher", Description: "EnableBatcher is decides whether start batcher in our worker", DefaultValue: true, }, EnableScheduler: { KeyName: "worker.enableScheduler", Description: "EnableScheduler decides whether to start the scheduler worker for cron-based scheduling", DefaultValue: false, }, EnableParentClosePolicyWorker: { KeyName: "system.enableParentClosePolicyWorker", Description: "EnableParentClosePolicyWorker decides whether or not enable system workers for processing parent close policy task", DefaultValue: true, }, EnableESAnalyzer: { KeyName: "system.enableESAnalyzer", Description: "EnableESAnalyzer decides whether to enable system workers for processing ElasticSearch Analyzer", DefaultValue: false, }, EnableAsyncWorkflowConsumption: { KeyName: "worker.enableAsyncWorkflowConsumption", Description: "EnableAsyncWorkflowConsumption decides whether to enable async workflows", DefaultValue: true, }, EnableStickyQuery: { KeyName: "system.enableStickyQuery", Filters: []Filter{DomainName}, Description: "EnableStickyQuery indicates if sticky query should be enabled per domain", DefaultValue: true, }, EnableFailoverManager: { KeyName: "system.enableFailoverManager", Description: "EnableFailoverManager indicates if failover manager is enabled", DefaultValue: true, }, ConcreteExecutionFixerDomainAllow: { KeyName: "worker.concreteExecutionFixerDomainAllow", Filters: []Filter{DomainName}, Description: "ConcreteExecutionFixerDomainAllow is which domains are allowed to be fixed by concrete fixer workflow", DefaultValue: false, }, CurrentExecutionFixerDomainAllow: { KeyName: "worker.currentExecutionFixerDomainAllow", Filters: []Filter{DomainName}, Description: "CurrentExecutionFixerDomainAllow is which domains are allowed to be fixed by current fixer workflow", DefaultValue: false, }, TimersScannerEnabled: { KeyName: "worker.timersScannerEnabled", Description: "TimersScannerEnabled is if timers scanner should be started as part of worker.Scanner", DefaultValue: false, }, TimersFixerEnabled: { KeyName: "worker.timersFixerEnabled", Description: "TimersFixerEnabled is if timers fixer should be started as part of worker.Scanner", DefaultValue: false, }, TimersFixerDomainAllow: { KeyName: "worker.timersFixerDomainAllow", Filters: []Filter{DomainName}, Description: "TimersFixerDomainAllow is which domains are allowed to be fixed by timer fixer workflow", DefaultValue: false, }, ConcreteExecutionFixerEnabled: { KeyName: "worker.concreteExecutionFixerEnabled", Description: "ConcreteExecutionFixerEnabled is if concrete execution fixer workflow is enabled", DefaultValue: false, }, CurrentExecutionFixerEnabled: { KeyName: "worker.currentExecutionFixerEnabled", Description: "CurrentExecutionFixerEnabled is if current execution fixer workflow is enabled", DefaultValue: false, }, EnableAuthorization: { KeyName: "system.enableAuthorization", Description: "EnableAuthorization is the key to enable authorization for a domain, only for extension binary:", DefaultValue: false, }, EnableTasklistIsolation: { KeyName: "system.enableTasklistIsolation", Description: "EnableTasklistIsolation is a feature to enable isolation-groups for a domain. Should not be enabled without a deep understanding of this feature", DefaultValue: false, }, EnableServiceAuthorization: { KeyName: "system.enableServiceAuthorization", Description: "EnableServiceAuthorization is the key to enable authorization for a service, only for extension binary:", DefaultValue: false, }, EnableServiceAuthorizationLogOnly: { KeyName: "system.enableServiceAuthorizationLogOnly", Description: "EnableServiceAuthorizationLogOnly is the key to enable authorization logging for a service, only for extension binary:", DefaultValue: false, }, ESAnalyzerPause: { KeyName: "worker.ESAnalyzerPause", Description: "ESAnalyzerPause defines if we want to dynamically pause the analyzer workflow", DefaultValue: false, }, EnableArchivalCompression: { KeyName: "worker.EnableArchivalCompression", Description: "EnableArchivalCompression indicates whether blobs are compressed before they are archived", DefaultValue: false, }, ESAnalyzerEnableAvgDurationBasedChecks: { KeyName: "worker.ESAnalyzerEnableAvgDurationBasedChecks", Description: "ESAnalyzerEnableAvgDurationBasedChecks controls if we want to enable avg duration based task refreshes", DefaultValue: false, }, Lockdown: { KeyName: "system.Lockdown", Description: "Lockdown defines if we want to allow failovers of domains to this cluster", DefaultValue: false, }, EnablePendingActivityValidation: { KeyName: "limit.pendingActivityCount.enabled", Description: "Enables pending activity count limiting/validation", DefaultValue: false, }, EnableCassandraAllConsistencyLevelDelete: { KeyName: "system.enableCassandraAllConsistencyLevelDelete", Description: "Uses all consistency level for Cassandra delete operations", DefaultValue: false, }, EnableShardIDMetrics: { KeyName: "system.enableShardIDMetrics", Description: "Enable shardId metrics in persistence client", DefaultValue: true, }, EnableTimerDebugLogByDomainID: { KeyName: "history.enableTimerDebugLogByDomainID", Filters: []Filter{DomainID}, Description: "Enable log for debugging timer task issue by domain", DefaultValue: false, }, EnableRetryForChecksumFailure: { KeyName: "history.enableMutableStateChecksumFailureRetry", Filters: []Filter{DomainName}, Description: "EnableRetryForChecksumFailure enables retry if mutable state checksum verification fails", DefaultValue: false, }, EnableStrongIdempotency: { KeyName: "history.enableStrongIdempotency", Filters: []Filter{DomainName}, Description: "EnableStrongIdempotency enables strong idempotency for APIs", DefaultValue: false, }, EnableStrongIdempotencySanityCheck: { KeyName: "history.enableStrongIdempotencySanityCheck", Filters: []Filter{DomainName}, Description: "EnableStrongIdempotencySanityCheck enables sanity check for strong idempotency", DefaultValue: false, }, MatchingEnableClientAutoConfig: { KeyName: "matching.enableClientAutoConfig", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingEnableClientAutoConfig is to enable auto config on worker side", DefaultValue: false, }, EnablePartitionIsolationGroupAssignment: { KeyName: "matching.enablePartitionIsolationGroupAssignment", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "EnablePartitionIsolationGroupAssignment enables assigning isolation groups to individual TaskList partitions", DefaultValue: false, }, EnableNoSQLHistoryTaskDualWriteMode: { KeyName: "history.enableNoSQLHistoryTaskDualWrite", Description: "EnableHistoryTaskDualWrite is to enable dual write of history events", DefaultValue: false, }, ReadNoSQLHistoryTaskFromDataBlob: { KeyName: "history.readNoSQLHistoryTaskFromDataBlob", Description: "ReadNoSQLHistoryTaskFromDataBlob is to read history tasks from data blob", DefaultValue: false, }, ReadNoSQLShardFromDataBlob: { KeyName: "history.readNoSQLShardFromDataBlob", Description: "ReadNoSQLShardFromDataBlob is to read shards from data blob", DefaultValue: true, }, EnableSizeBasedHistoryExecutionCache: { KeyName: "history.enableSizeBasedHistoryExecutionCache", Description: "EnableSizeBasedHistoryExecutionCache is to enable size based history execution cache", DefaultValue: false, }, EnableSizeBasedHistoryEventCache: { KeyName: "history.enableSizeBasedHistoryEventCache", Description: "EnableSizeBasedHistoryEventCache is to enable size based history event cache", DefaultValue: false, }, DisableTransferFailoverQueue: { KeyName: "history.disableTransferFailoverQueue", Description: "DisableTransferFailoverQueue is to disable transfer failover queue", DefaultValue: false, }, DisableTimerFailoverQueue: { KeyName: "history.disableTimerFailoverQueue", Description: "DisableTimerFailoverQueue is to disable timer failover queue", DefaultValue: false, }, EnableTransferQueueV2: { KeyName: "history.enableTransferQueueV2", Description: "EnableTransferQueueV2 is to enable transfer queue v2", Filters: []Filter{ShardID}, DefaultValue: false, }, EnableTimerQueueV2: { KeyName: "history.enableTimerQueueV2", Description: "EnableTimerQueueV2 is to enable timer queue v2", Filters: []Filter{ShardID}, DefaultValue: false, }, EnableTransferQueueV2PendingTaskCountAlert: { KeyName: "history.enableTransferQueueV2PendingTaskCountAlert", Description: "EnableTransferQueueV2PendingTaskCountAlert is to enable transfer queue v2 pending task count alert", Filters: []Filter{ShardID}, DefaultValue: false, }, EnableTimerQueueV2PendingTaskCountAlert: { KeyName: "history.enableTimerQueueV2PendingTaskCountAlert", Description: "EnableTimerQueueV2PendingTaskCountAlert is to enable timer queue v2 pending task count alert", Filters: []Filter{ShardID}, DefaultValue: false, }, EnableActiveClusterSelectionPolicyInStartWorkflow: { KeyName: "frontend.enableActiveClusterSelectionPolicyInStartWorkflow", Description: "EnableActiveClusterSelectionPolicyInStartWorkflow is to enable active cluster selection policy in start workflow requests for a domain", DefaultValue: false, Filters: []Filter{DomainName}, }, EnforceDecisionTaskAttempts: { KeyName: "history.enforceDecisionTaskAttempts", Filters: []Filter{DomainName}, Description: "EnforceDecisionTaskAttempts is the key for enforcing decision retry attempts limit in case of timeouts", DefaultValue: false, }, MatchingExcludeShortLivedTaskListsFromShardManager: { KeyName: "matching.excludeShortLivedTaskListsFromShardManager", Description: "MatchingExcludeShortLivedTaskListsFromShardManager excludes short-lived task lists (e.g. bits task lists and sticky task lists) from the shard manager", DefaultValue: true, }, EnableHierarchicalWeightedRoundRobinTaskScheduler: { KeyName: "history.enableHierarchicalWeightedRoundRobinTaskScheduler", Description: "EnableHierarchicalWeightedRoundRobinTaskScheduler is to enable hierarchical weighted round robin task scheduler", DefaultValue: false, }, EnableTaskListAwareTaskSchedulerByDomain: { KeyName: "history.enableTaskListAwareTaskSchedulerByDomain", Description: "EnableTaskListAwareTaskSchedulerByDomain is to enable task list aware task scheduler by domain", Filters: []Filter{DomainName}, DefaultValue: false, }, } var FloatKeys = map[FloatKey]DynamicFloat{ TestGetFloat64PropertyKey: { KeyName: "testGetFloat64PropertyKey", Description: "", DefaultValue: 0, }, TestGetFloat64PropertyFilteredByShardIDKey: { KeyName: "testGetFloat64PropertyFilteredByShardIDKey", Description: "", DefaultValue: 0, Filters: nil, }, PersistenceErrorInjectionRate: { KeyName: "system.persistenceErrorInjectionRate", Description: "PersistenceErrorInjectionRate is rate for injecting random error in persistence", DefaultValue: 0, }, AdminErrorInjectionRate: { KeyName: "admin.errorInjectionRate", Description: "dminErrorInjectionRate is the rate for injecting random error in admin client", DefaultValue: 0, }, DomainFailoverRefreshTimerJitterCoefficient: { KeyName: "frontend.domainFailoverRefreshTimerJitterCoefficient", Description: "DomainFailoverRefreshTimerJitterCoefficient is the jitter for domain failover refresh timer jitter", DefaultValue: 0.1, }, FrontendErrorInjectionRate: { KeyName: "frontend.errorInjectionRate", Description: "FrontendErrorInjectionRate is rate for injecting random error in frontend client", DefaultValue: 0, }, MatchingErrorInjectionRate: { KeyName: "matching.errorInjectionRate", Description: "MatchingErrorInjectionRate is rate for injecting random error in matching client", DefaultValue: 0, }, QueueProcessorRandomSplitProbability: { KeyName: "history.queueProcessorRandomSplitProbability", Description: "QueueProcessorRandomSplitProbability is the probability for a domain to be split to a new processing queue", DefaultValue: 0.01, }, QueueProcessorPollBackoffIntervalJitterCoefficient: { KeyName: "history.queueProcessorPollBackoffIntervalJitterCoefficient", Description: "QueueProcessorPollBackoffIntervalJitterCoefficient is backoff interval jitter coefficient", DefaultValue: 0.15, }, TimerProcessorUpdateAckIntervalJitterCoefficient: { KeyName: "history.timerProcessorUpdateAckIntervalJitterCoefficient", Description: "TimerProcessorUpdateAckIntervalJitterCoefficient is the update interval jitter coefficient", DefaultValue: 0.15, }, TimerProcessorMaxPollIntervalJitterCoefficient: { KeyName: "history.timerProcessorMaxPollIntervalJitterCoefficient", Description: "TimerProcessorMaxPollIntervalJitterCoefficient is the max poll interval jitter coefficient", DefaultValue: 0.15, }, TimerProcessorSplitQueueIntervalJitterCoefficient: { KeyName: "history.timerProcessorSplitQueueIntervalJitterCoefficient", Description: "TimerProcessorSplitQueueIntervalJitterCoefficient is the split processing queue interval jitter coefficient", DefaultValue: 0.15, }, TransferProcessorMaxPollIntervalJitterCoefficient: { KeyName: "history.transferProcessorMaxPollIntervalJitterCoefficient", Description: "TransferProcessorMaxPollIntervalJitterCoefficient is the max poll interval jitter coefficient", DefaultValue: 0.15, }, TransferProcessorSplitQueueIntervalJitterCoefficient: { KeyName: "history.transferProcessorSplitQueueIntervalJitterCoefficient", Description: "TransferProcessorSplitQueueIntervalJitterCoefficient is the split processing queue interval jitter coefficient", DefaultValue: 0.15, }, TransferProcessorUpdateAckIntervalJitterCoefficient: { KeyName: "history.transferProcessorUpdateAckIntervalJitterCoefficient", Description: "TransferProcessorUpdateAckIntervalJitterCoefficient is the update interval jitter coefficient", DefaultValue: 0.15, }, ReplicationTaskProcessorCleanupJitterCoefficient: { KeyName: "history.ReplicationTaskProcessorCleanupJitterCoefficient", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorCleanupJitterCoefficient is the jitter for cleanup timer", DefaultValue: 0.15, }, ReplicationTaskProcessorStartWaitJitterCoefficient: { KeyName: "history.ReplicationTaskProcessorStartWaitJitterCoefficient", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorStartWaitJitterCoefficient is the jitter for batch start wait timer", DefaultValue: 0.9, }, ReplicationTaskProcessorHostQPS: { KeyName: "history.ReplicationTaskProcessorHostQPS", Description: "ReplicationTaskProcessorHostQPS is the qps of task processing rate limiter on host level", DefaultValue: 1500, }, ReplicationTaskProcessorShardQPS: { KeyName: "history.ReplicationTaskProcessorShardQPS", Description: "ReplicationTaskProcessorShardQPS is the qps of task processing rate limiter on shard level", DefaultValue: 5, }, ReplicationTaskGenerationQPS: { KeyName: "history.ReplicationTaskGenerationQPS", Description: "ReplicationTaskGenerationQPS is the wait time between each replication task generation qps", DefaultValue: 100, }, MutableStateChecksumInvalidateBefore: { KeyName: "history.mutableStateChecksumInvalidateBefore", Description: "MutableStateChecksumInvalidateBefore is the epoch timestamp before which all checksums are to be discarded", DefaultValue: 0, }, NotifyFailoverMarkerTimerJitterCoefficient: { KeyName: "history.NotifyFailoverMarkerTimerJitterCoefficient", Description: "NotifyFailoverMarkerTimerJitterCoefficient is the jitter for failover marker notifier timer", DefaultValue: 0.15, }, HistoryErrorInjectionRate: { KeyName: "history.errorInjectionRate", Description: "HistoryErrorInjectionRate is rate for injecting random error in history client", DefaultValue: 0, }, ReplicationBudgetManagerSoftCapThreshold: { KeyName: "history.replicationBudgetManagerSoftCapThreshold", Description: "ReplicationBudgetManagerSoftCapThreshold is the soft cap threshold for the replication budget manager cache (0.0 to 1.0)", DefaultValue: 1.0, }, ReplicationTaskFetcherTimerJitterCoefficient: { KeyName: "history.ReplicationTaskFetcherTimerJitterCoefficient", Description: "ReplicationTaskFetcherTimerJitterCoefficient is the jitter for fetcher timer", DefaultValue: 0.15, }, WorkerDeterministicConstructionCheckProbability: { KeyName: "worker.DeterministicConstructionCheckProbability", Description: "WorkerDeterministicConstructionCheckProbability controls the probability of running a deterministic construction check for any given archival", DefaultValue: 0.002, }, WorkerBlobIntegrityCheckProbability: { KeyName: "worker.BlobIntegrityCheckProbability", Description: "WorkerBlobIntegrityCheckProbability controls the probability of running an integrity check for any given archival", DefaultValue: 0.002, }, HistoryGlobalRatelimiterNewDataWeight: { KeyName: "history.globalRatelimiterNewDataWeight", Description: "HistoryGlobalRatelimiterNewDataWeight defines how much weight to give each host's newest data, per update. Must be between 0 and 1, higher values match new values more closely after a single update", DefaultValue: 0.5, }, MatchingPartitionDownscaleFactor: { KeyName: "matching.partitionDownscaleFactor", Description: "MatchingPartitionDownscaleFactor introduces hysteresis to prevent oscillation by setting a lower QPS threshold for downscaling, ensuring partitions are only removed when the load decreases significantly below the capacity of fewer partitions.", Filters: []Filter{DomainName, TaskListName, TaskType}, DefaultValue: 0.75, }, MatchingOverrideTaskListRPS: { KeyName: "matching.overrideTaskListRps", Description: "MatchingOverrideTaskListRPS is the RPS override for a specific TaskList. When set to a non-zero value, this overrides the RPS value that pollers specify. By default (0), the pollers' specified RPS is respected.", Filters: []Filter{DomainName, TaskListName, TaskType}, DefaultValue: 0, }, ShardDistributorErrorInjectionRate: { KeyName: "sharddistributor.errorInjectionRate", Description: "ShardDistributorInjectionRate is rate for injecting random error in shard distributor client", DefaultValue: 0, }, ShardDistributorExecutorErrorInjectionRate: { KeyName: "sharddistributorexecutor.errorInjectionRate", Description: "ShardDistributorExecutorInjectionRate is rate for injecting random error in shard distributor executor client", DefaultValue: 0, }, ShardDistributorLoadBalancingNaiveMaxDeviation: { KeyName: "shardDistributor.loadBalancingNaive.maxDeviation", Description: "ShardDistributorLoadBalancingNaiveMaxDeviation is max deviation between the coldest and hottest executors in naive load balancing mode", DefaultValue: 2.0, Filters: []Filter{Namespace}, }, } var StringKeys = map[StringKey]DynamicString{ TestGetStringPropertyKey: { KeyName: "testGetStringPropertyKey", Description: "", DefaultValue: "", }, WriteVisibilityStoreName: { KeyName: "system.writeVisibilityStoreName", Description: "WriteVisibilityStoreName is key for how to write to advanced visibility. The default option is es, which can be used for seamless migration from db visibility to advanced visibility, usually using with ReadVisibilityStoreName", DefaultValue: "es", }, HistoryArchivalStatus: { KeyName: "system.historyArchivalStatus", Description: "HistoryArchivalStatus is key for the status of history archival to override the value from static config.", DefaultValue: "enabled", }, VisibilityArchivalStatus: { KeyName: "system.visibilityArchivalStatus", Description: "VisibilityArchivalStatus is key for the status of visibility archival to override the value from static config.", DefaultValue: "enabled", }, DefaultEventEncoding: { KeyName: "history.defaultEventEncoding", Filters: []Filter{DomainName}, Description: "DefaultEventEncoding is the encoding type for history events", DefaultValue: string(constants.EncodingTypeThriftRW), }, AdminOperationToken: { KeyName: "history.adminOperationToken", Description: "AdminOperationToken is the token to pass admin checking", DefaultValue: "CadenceTeamONLY", }, ESAnalyzerLimitToTypes: { KeyName: "worker.ESAnalyzerLimitToTypes", Description: "ESAnalyzerLimitToTypes controls if we want to limit ESAnalyzer only to some workflow types", DefaultValue: "", }, ESAnalyzerLimitToDomains: { KeyName: "worker.ESAnalyzerLimitToDomains", Description: "ESAnalyzerLimitToDomains controls if we want to limit ESAnalyzer only to some domains", DefaultValue: "", }, ESAnalyzerWorkflowDurationWarnThresholds: { KeyName: "worker.ESAnalyzerWorkflowDurationWarnThresholds", Description: "ESAnalyzerWorkflowDurationWarnThresholds defines the warning execution thresholds for workflow types", DefaultValue: "", }, ESAnalyzerWorkflowVersionMetricDomains: { KeyName: "worker.ESAnalyzerWorkflowVersionMetricDomains", Description: "ESAnalyzerWorkflowDurationWarnThresholds defines the domains we want to emit wf version metrics on", DefaultValue: "", }, ESAnalyzerWorkflowTypeMetricDomains: { KeyName: "worker.ESAnalyzerWorkflowTypeMetricDomains", Description: "ESAnalyzerWorkflowDurationWarnThresholds defines the domains we want to emit wf version metrics on", DefaultValue: "", }, FrontendGlobalRatelimiterMode: { KeyName: "frontend.globalRatelimiterMode", Description: "FrontendGlobalRatelimiterMode defines which mode a global key should be in, per key, to make gradual changes to ratelimiter algorithms", DefaultValue: "disabled", Filters: []Filter{RatelimitKey}, }, EnableAuthorizationV2: { KeyName: "system.enableAuthorizationV2", Description: "EnableAuthorizationV2 is the key to enable authorization v2 for a domain, only for extension binary:", DefaultValue: "disabled", // available options: "disabled","shadow","enabled" }, TasklistLoadBalancerStrategy: { KeyName: "system.tasklistLoadBalancerStrategy", Description: "TasklistLoadBalancerStrategy is the key for tasklist load balancer strategy", DefaultValue: "weighted", // available options: "random, round-robin, weighted" Filters: []Filter{DomainName, TaskListName, TaskType}, }, EnableAdminAuthorization: { KeyName: "system.enableAdminAuthorization", Description: "EnableAdminAuthorization is the key to enable authorization for admin operations, only for extension of authorizer implementation", DefaultValue: "disabled", // available options: "disabled","shadow","enabled" }, ReadVisibilityStoreName: { KeyName: "system.readVisibilityStoreName", Description: "ReadVisibilityStoreName is key to identify which store to read visibility data from", DefaultValue: "es", Filters: []Filter{DomainName}, }, MatchingShardDistributionMode: { KeyName: "matching.shardDistributionMode", Description: "MatchingShardDistributionMode defines which shard distribution mode should be used", DefaultValue: "hash_ring", }, SerializationEncoding: { KeyName: "history.serializationEncoding", Description: "SerializationEncoding is the encoding type for blobs", DefaultValue: string(constants.EncodingTypeThriftRW), }, ShardDistributorMigrationMode: { KeyName: "shardDistributor.migrationMode", Description: "ShardDistributorMigrationMode is the mode the at represent the state of the migration to rely on shard distributor for the sharding mechanism", DefaultValue: "onboarded", Filters: []Filter{Namespace}, }, ShardDistributorLoadBalancingMode: { KeyName: "shardDistributor.loadBalancingMode", Description: "ShardDistributorLoadBalancingMode is the load balancing mode for the shard distributor. Depending on the mode, the shard distributor will use different ways to distribute the shards", DefaultValue: "naive", }, } var DurationKeys = map[DurationKey]DynamicDuration{ TestGetDurationPropertyKey: { KeyName: "testGetDurationPropertyKey", Description: "", DefaultValue: 0, }, TestGetDurationPropertyFilteredByDomainKey: { KeyName: "testGetDurationPropertyFilteredByDomainKey", Description: "", DefaultValue: 0, }, TestGetDurationPropertyFilteredByTaskListInfoKey: { KeyName: "testGetDurationPropertyFilteredByTaskListInfoKey", Description: "", DefaultValue: 0, }, TestGetDurationPropertyFilteredByWorkflowTypeKey: { KeyName: "testGetDurationPropertyFilteredByWorkflowTypeKey", Description: "", DefaultValue: 0, Filters: nil, }, TestGetDurationPropertyFilteredByDomainIDKey: { KeyName: "testGetDurationPropertyFilteredByDomainIDKey", Description: "", DefaultValue: 0, Filters: nil, }, TestGetDurationPropertyFilteredByShardID: { KeyName: "testGetDurationPropertyFilteredByShardID", Description: "", DefaultValue: 0, Filters: nil, }, FrontendShutdownDrainDuration: { KeyName: "frontend.shutdownDrainDuration", Description: "FrontendShutdownDrainDuration is the duration of traffic drain during shutdown", DefaultValue: 0, }, FrontendWarmupDuration: { KeyName: "frontend.warmupDuration", Description: "FrontendWarmupDuration is the duration before a frontend host reports its status as healthy", DefaultValue: 30 * time.Second, }, FrontendFailoverCoolDown: { KeyName: "frontend.failoverCoolDown", Filters: []Filter{DomainName}, Description: "FrontendFailoverCoolDown is duration between two domain failvoers", DefaultValue: time.Minute, }, DomainFailoverRefreshInterval: { KeyName: "frontend.domainFailoverRefreshInterval", Description: "DomainFailoverRefreshInterval is the domain failover refresh timer", DefaultValue: time.Second * 10, }, GlobalRatelimiterUpdateInterval: { KeyName: "frontend.globalRatelimiterUpdateInterval", Description: "GlobalRatelimiterUpdateInterval defines how often each global ratelimiter collection submits load information, and the expected update rate in aggregators (used to determine when hosts are lost)", DefaultValue: 3 * time.Second, }, FrontendMaxWorkerPollDelay: { KeyName: "frontend.maxWorkerPollDelay", Filters: []Filter{DomainName}, Description: "FrontendMaxWorkerPollDelay is the maximum duration a worker poll request will wait for a rate limit token before being rejected", DefaultValue: 0, }, MatchingLongPollExpirationInterval: { KeyName: "matching.longPollExpirationInterval", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingLongPollExpirationInterval is the long poll expiration interval in the matching service", DefaultValue: time.Minute, }, MatchingUpdateAckInterval: { KeyName: "matching.updateAckInterval", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingUpdateAckInterval is the interval for update ack", DefaultValue: time.Minute, }, MatchingIdleTasklistCheckInterval: { KeyName: "matching.idleTasklistCheckInterval", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingIdleTasklistCheckInterval is the IdleTasklistCheckInterval", DefaultValue: time.Minute * 5, }, MaxTasklistIdleTime: { KeyName: "matching.maxTasklistIdleTime", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MaxTasklistIdleTime is the max time tasklist being idle", DefaultValue: time.Minute * 5, }, MatchingShutdownDrainDuration: { KeyName: "matching.shutdownDrainDuration", Description: "MatchingShutdownDrainDuration is the duration of traffic drain during shutdown", DefaultValue: 0, }, MatchingActivityTaskSyncMatchWaitTime: { KeyName: "matching.activityTaskSyncMatchWaitTime", Filters: []Filter{DomainName}, Description: "MatchingActivityTaskSyncMatchWaitTime is the amount of time activity task will wait to be sync matched", DefaultValue: time.Millisecond * 50, }, MatchingPartitionUpscaleSustainedDuration: { KeyName: "matching.partitionUpscaleSustainedDuration", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingPartitionUpscaleSustainedDuration is the sustained period to wait before upscaling the number of partitions", DefaultValue: time.Minute, }, MatchingPartitionDownscaleSustainedDuration: { KeyName: "matching.partitionDownscaleSustainedDuration", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingPartitionDownscaleSustainedDuration is the sustained period to wait before downscaling the number of partitions", DefaultValue: 2 * time.Minute, }, MatchingIsolationGroupUpscaleSustainedDuration: { KeyName: "matching.isolationGroupUpscaleSustainedDuration", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingIsolationGroupUpscaleSustainedDuration is the sustained period to wait before upscaling the number of partitions an isolation group is assigned to", DefaultValue: time.Minute, }, MatchingIsolationGroupDownscaleSustainedDuration: { KeyName: "matching.isolationGroupDownscaleSustainedDuration", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingIsolationGroupDownscaleSustainedDuration is the sustained period to wait before downscaling the number of partitions an isolation group is assigned to", DefaultValue: 2 * time.Minute, }, MatchingIsolationGroupHasPollersSustainedDuration: { KeyName: "matching.isolationGroupHasPollersSustainedDuration", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingIsolationGroupHasPollersSustainedDuration is the sustained period to wait before considering an isolation group as an active and assigning partitions to it", DefaultValue: time.Minute, }, MatchingIsolationGroupNoPollersSustainedDuration: { KeyName: "matching.isolationGroupNoPollersSustainedDuration", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingIsolationGroupNoPollersSustainedDuration is the sustained period to wait before considering an isolation group as inactive and unassigning all partitions from it", DefaultValue: time.Minute, }, MatchingAdaptiveScalerUpdateInterval: { KeyName: "matching.adaptiveScalerUpdateInterval", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingAdaptiveScalerUpdateInterval is the internal for adaptive scaler to update", DefaultValue: time.Second * 15, }, MatchingQPSTrackerInterval: { KeyName: "matching.qpsTrackerInterval", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingQPSTrackerInterval is the interval for qps tracker's loop. Changes are not reflected until service restart", DefaultValue: time.Second * 10, }, HistoryLongPollExpirationInterval: { KeyName: "history.longPollExpirationInterval", Filters: []Filter{DomainName}, Description: "HistoryLongPollExpirationInterval is the long poll expiration interval in the history service", DefaultValue: time.Second * 20, // history client: client/history/client.go set the client timeout 20s }, HistoryCacheTTL: { KeyName: "history.cacheTTL", Description: "HistoryCacheTTL is TTL of history cache", DefaultValue: time.Hour, }, HistoryShutdownDrainDuration: { KeyName: "history.shutdownDrainDuration", Description: "HistoryShutdownDrainDuration is the duration of traffic drain during shutdown", DefaultValue: 0, }, EventsCacheTTL: { KeyName: "history.eventsCacheTTL", Description: "EventsCacheTTL is TTL of events cache", DefaultValue: time.Hour, }, AcquireShardInterval: { KeyName: "history.acquireShardInterval", Description: "AcquireShardInterval is interval that timer used to acquire shard", DefaultValue: time.Minute, }, StandbyClusterDelay: { KeyName: "history.standbyClusterDelay", Description: "StandbyClusterDelay is the artificial delay added to standby cluster's view of active cluster's time", DefaultValue: time.Minute * 5, }, StandbyTaskMissingEventsResendDelay: { KeyName: "history.standbyTaskMissingEventsResendDelay", Description: "StandbyTaskMissingEventsResendDelay is the amount of time standby cluster's will wait (if events are missing)before calling remote for missing events", DefaultValue: time.Minute * 15, }, StandbyTaskMissingEventsDiscardDelay: { KeyName: "history.standbyTaskMissingEventsDiscardDelay", Description: "StandbyTaskMissingEventsDiscardDelay is the amount of time standby cluster's will wait (if events are missing)before discarding the task", DefaultValue: time.Minute * 25, }, ActiveTaskRedispatchInterval: { KeyName: "history.activeTaskRedispatchInterval", Description: "ActiveTaskRedispatchInterval is the active task redispatch interval", DefaultValue: time.Second * 5, }, StandbyTaskRedispatchInterval: { KeyName: "history.standbyTaskRedispatchInterval", Description: "StandbyTaskRedispatchInterval is the standby task redispatch interval", DefaultValue: time.Second * 30, }, StandbyTaskReReplicationContextTimeout: { KeyName: "history.standbyTaskReReplicationContextTimeout", Filters: []Filter{DomainID}, Description: "StandbyTaskReReplicationContextTimeout is the context timeout for standby task re-replication", DefaultValue: time.Minute * 3, }, ResurrectionCheckMinDelay: { KeyName: "history.resurrectionCheckMinDelay", Filters: []Filter{DomainName}, Description: "ResurrectionCheckMinDelay is the minimal timer processing delay before scanning history to see if there's a resurrected timer/activity", DefaultValue: time.Hour * 24, }, QueueProcessorSplitLookAheadDurationByDomainID: { KeyName: "history.queueProcessorSplitLookAheadDurationByDomainID", Filters: []Filter{DomainID}, Description: "QueueProcessorSplitLookAheadDurationByDomainID is the look ahead duration when spliting a domain to a new processing queue", DefaultValue: time.Minute * 20, }, QueueProcessorPollBackoffInterval: { KeyName: "history.queueProcessorPollBackoffInterval", Description: "QueueProcessorPollBackoffInterval is the backoff duration when queue processor is throttled", DefaultValue: time.Second * 5, }, VirtualSliceForceAppendInterval: { KeyName: "history.virtualSliceForceAppendInterval", Description: "VirtualSliceForceAppendInterval is the duration forcing a new virtual slice to be appended to the root virtual queue instead of being merged. It has 2 benefits: First, virtual slices won't grow infinitely, task loading for that slice can complete and its scope can be shrinked. Second, when we need to unload a virtual slice to free memory, we won't unload too many tasks.", DefaultValue: time.Minute * 5, }, TimerProcessorUpdateAckInterval: { KeyName: "history.timerProcessorUpdateAckInterval", Description: "TimerProcessorUpdateAckInterval is update interval for timer processor", DefaultValue: time.Second * 30, }, TimerProcessorCompleteTimerInterval: { KeyName: "history.timerProcessorCompleteTimerInterval", Description: "TimerProcessorCompleteTimerInterval is complete timer interval for timer processor", DefaultValue: time.Minute, }, TimerProcessorFailoverMaxStartJitterInterval: { KeyName: "history.timerProcessorFailoverMaxStartJitterInterval", Description: "TimerProcessorFailoverMaxStartJitterInterval is the max jitter interval for starting timer failover queue processing. The actual jitter interval used will be a random duration between 0 and the max interval so that timer failover queue across different shards won't start at the same time", DefaultValue: 0, }, TimerProcessorMaxPollInterval: { KeyName: "history.timerProcessorMaxPollInterval", Description: "TimerProcessorMaxPollInterval is max poll interval for timer processor", DefaultValue: time.Minute * 5, }, TimerProcessorSplitQueueInterval: { KeyName: "history.timerProcessorSplitQueueInterval", Description: "TimerProcessorSplitQueueInterval is the split processing queue interval for timer processor", DefaultValue: time.Minute, }, TimerProcessorArchivalTimeLimit: { KeyName: "history.timerProcessorArchivalTimeLimit", Description: "TimerProcessorArchivalTimeLimit is the upper time limit for inline history archival", DefaultValue: time.Second * 2, }, TimerProcessorMaxTimeShift: { KeyName: "history.timerProcessorMaxTimeShift", Description: "TimerProcessorMaxTimeShift is the max shift timer processor can have", DefaultValue: time.Second, }, TransferProcessorFailoverMaxStartJitterInterval: { KeyName: "history.transferProcessorFailoverMaxStartJitterInterval", Description: "TransferProcessorFailoverMaxStartJitterInterval is the max jitter interval for starting transfer failover queue processing. The actual jitter interval used will be a random duration between 0 and the max interval so that timer failover queue across different shards won't start at the same time", DefaultValue: 0, }, TransferProcessorMaxPollInterval: { KeyName: "history.transferProcessorMaxPollInterval", Description: "TransferProcessorMaxPollInterval is max poll interval for transferQueueProcessor", DefaultValue: time.Minute, }, TransferProcessorSplitQueueInterval: { KeyName: "history.transferProcessorSplitQueueInterval", Description: "TransferProcessorSplitQueueInterval is the split processing queue interval for transferQueueProcessor", DefaultValue: time.Minute, }, TransferProcessorUpdateAckInterval: { KeyName: "history.transferProcessorUpdateAckInterval", Description: "TransferProcessorUpdateAckInterval is update interval for transferQueueProcessor", DefaultValue: time.Second * 30, }, TransferProcessorCompleteTransferInterval: { KeyName: "history.transferProcessorCompleteTransferInterval", Description: "TransferProcessorCompleteTransferInterval is complete timer interval for transferQueueProcessor", DefaultValue: time.Minute, }, TransferProcessorValidationInterval: { KeyName: "history.transferProcessorValidationInterval", Description: "TransferProcessorValidationInterval is interval for performing transfer queue validation", DefaultValue: time.Second * 30, }, TransferProcessorVisibilityArchivalTimeLimit: { KeyName: "history.transferProcessorVisibilityArchivalTimeLimit", Description: "TransferProcessorVisibilityArchivalTimeLimit is the upper time limit for archiving visibility records", DefaultValue: time.Millisecond * 400, }, ReplicatorUpperLatency: { KeyName: "history.replicatorUpperLatency", Description: "ReplicatorUpperLatency indicates the max allowed replication latency between clusters", DefaultValue: time.Second * 40, }, ShardUpdateMinInterval: { KeyName: "history.shardUpdateMinInterval", Description: "ShardUpdateMinInterval is the minimal time interval which the shard info can be updated", DefaultValue: time.Minute * 5, }, ShardSyncMinInterval: { KeyName: "history.shardSyncMinInterval", Description: "ShardSyncMinInterval is the minimal time interval which the shard info should be sync to remote", DefaultValue: time.Minute * 5, }, StickyTTL: { KeyName: "history.stickyTTL", Filters: []Filter{DomainName}, Description: "StickyTTL is to expire a sticky tasklist if no update more than this duration", DefaultValue: time.Hour * 24 * 365, }, DecisionHeartbeatTimeout: { KeyName: "history.decisionHeartbeatTimeout", Filters: []Filter{DomainName}, Description: "DecisionHeartbeatTimeout is for decision heartbeat", DefaultValue: time.Minute * 30, // about 30m }, NormalDecisionScheduleToStartTimeout: { KeyName: "history.normalDecisionScheduleToStartTimeout", Filters: []Filter{DomainName}, Description: "NormalDecisionScheduleToStartTimeout is scheduleToStart timeout duration for normal (non-sticky) decision task", DefaultValue: time.Minute * 5, }, NotifyFailoverMarkerInterval: { KeyName: "history.NotifyFailoverMarkerInterval", Description: "NotifyFailoverMarkerInterval is determines the frequency to notify failover marker", DefaultValue: time.Second * 5, }, ActivityMaxScheduleToStartTimeoutForRetry: { KeyName: "history.activityMaxScheduleToStartTimeoutForRetry", Filters: []Filter{DomainName}, Description: "ActivityMaxScheduleToStartTimeoutForRetry is maximum value allowed when overwritting the schedule to start timeout for activities with retry policy", DefaultValue: time.Minute * 30, }, ReplicationTaskFetcherAggregationInterval: { KeyName: "history.ReplicationTaskFetcherAggregationInterval", Description: "ReplicationTaskFetcherAggregationInterval determines how frequently the fetch requests are sent", DefaultValue: time.Second * 2, }, ReplicationTaskFetcherErrorRetryWait: { KeyName: "history.ReplicationTaskFetcherErrorRetryWait", Description: "ReplicationTaskFetcherErrorRetryWait is the wait time when fetcher encounters error", DefaultValue: time.Second, }, ReplicationTaskFetcherServiceBusyWait: { KeyName: "history.ReplicationTaskFetcherServiceBusyWait", Description: "ReplicationTaskFetcherServiceBusyWait is the wait time when fetcher encounters service busy error", DefaultValue: time.Minute, }, ReplicationTaskProcessorErrorRetryWait: { KeyName: "history.ReplicationTaskProcessorErrorRetryWait", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorErrorRetryWait is the initial retry wait when we see errors in applying replication tasks", DefaultValue: time.Millisecond * 50, }, ReplicationTaskProcessorErrorSecondRetryWait: { KeyName: "history.ReplicationTaskProcessorErrorSecondRetryWait", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorErrorSecondRetryWait is the initial retry wait for the second phase retry", DefaultValue: time.Second * 5, }, ReplicationTaskProcessorErrorSecondRetryMaxWait: { KeyName: "history.ReplicationTaskProcessorErrorSecondRetryMaxWait", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorErrorSecondRetryMaxWait is the max wait time for the second phase retry", DefaultValue: time.Second * 30, }, ReplicationTaskProcessorErrorSecondRetryExpiration: { KeyName: "history.ReplicationTaskProcessorErrorSecondRetryExpiration", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorErrorSecondRetryExpiration is the expiration duration for the second phase retry", DefaultValue: time.Minute * 5, }, ReplicationTaskProcessorNoTaskInitialWait: { KeyName: "history.ReplicationTaskProcessorNoTaskInitialWait", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorNoTaskInitialWait is the wait time when not ask is returned", DefaultValue: time.Second * 2, }, ReplicationTaskProcessorCleanupInterval: { KeyName: "history.ReplicationTaskProcessorCleanupInterval", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorCleanupInterval determines how frequently the cleanup replication queue", DefaultValue: time.Minute, }, ReplicationTaskProcessorStartWait: { KeyName: "history.ReplicationTaskProcessorStartWait", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorStartWait is the wait time before each task processing batch", DefaultValue: 0, }, ReplicationTaskProcessorLatencyLogThreshold: { KeyName: "history.ReplicationTaskProcessorLatencyLogThreshold", Description: "ReplicationTaskProcessorLatencyLogThreshold is is the threshold of whether history will log history replication latency.", DefaultValue: 0, }, WorkerESProcessorFlushInterval: { KeyName: "worker.ESProcessorFlushInterval", Description: "WorkerESProcessorFlushInterval is flush interval for esProcessor", DefaultValue: time.Second, }, WorkerTimeLimitPerArchivalIteration: { KeyName: "worker.TimeLimitPerArchivalIteration", Description: "WorkerTimeLimitPerArchivalIteration is controls the time limit of each iteration of archival workflow", DefaultValue: time.Hour * 24 * 15, }, WorkerReplicationTaskMaxRetryDuration: { KeyName: "worker.replicationTaskMaxRetryDuration", Description: "WorkerReplicationTaskMaxRetryDuration is the max retry duration for any task", DefaultValue: time.Minute * 10, }, ESAnalyzerTimeWindow: { KeyName: "worker.ESAnalyzerTimeWindow", Description: "ESAnalyzerTimeWindow defines the time window ElasticSearch Analyzer will consider while taking workflow averages", DefaultValue: time.Hour * 24 * 30, }, IsolationGroupStateRefreshInterval: { KeyName: "system.isolationGroupStateRefreshInterval", Description: "the frequency by which the IsolationGroupState handler will poll configuration", DefaultValue: time.Second * 30, }, IsolationGroupStateFetchTimeout: { KeyName: "system.IsolationGroupStateFetchTimeout", Description: "IsolationGroupStateFetchTimeout is the dynamic config DB fetch timeout value", DefaultValue: time.Second * 30, }, IsolationGroupStateUpdateTimeout: { KeyName: "system.IsolationGroupStateUpdateTimeout", Description: "IsolationGroupStateFetchTimeout is the dynamic config DB update timeout value", DefaultValue: time.Second * 30, }, ESAnalyzerBufferWaitTime: { KeyName: "worker.ESAnalyzerBufferWaitTime", Description: "ESAnalyzerBufferWaitTime controls min time required to consider a worklow stuck", DefaultValue: time.Minute * 30, }, AsyncTaskDispatchTimeout: { KeyName: "matching.asyncTaskDispatchTimeout", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "AsyncTaskDispatchTimeout is the timeout of dispatching tasks for async match", DefaultValue: time.Second * 3, }, AppendTaskTimeout: { KeyName: "matching.appendTaskTimeout", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "AppendTaskTimeout is the timeout of appending tasks to persistence.", DefaultValue: time.Second * 5, }, HistoryGlobalRatelimiterDecayAfter: { KeyName: "history.globalRatelimiterDecayAfter", Description: "HistoryGlobalRatelimiterDecayAfter defines how long to wait for an update before considering a host's data \"possibly gone\", causing its weight to gradually decline.", DefaultValue: 6 * time.Second, }, HistoryGlobalRatelimiterGCAfter: { KeyName: "history.globalRatelimiterGCAfter", Description: "HistoryGlobalRatelimiterGCAfter defines how long to wait until a host's data is considered entirely useless, e.g. host has likely disappeared, its weight is very low, and the data can be deleted.", DefaultValue: 30 * time.Second, }, LocalPollWaitTime: { KeyName: "matching.localPollWaitTime", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "LocalPollWaitTime is the time a poller waits before considering request forwarding.", DefaultValue: time.Millisecond * 10, }, LocalTaskWaitTime: { KeyName: "matching.localTaskWaitTime", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "LocalTaskWaitTime is the time a task waits for a poller to arrive before considering task forwarding", DefaultValue: time.Millisecond * 10, }, TaskIsolationDuration: { KeyName: "matching.taskIsolationDuration", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "TaskIsolationDuration is the time period for which we attempt to respect tasklist isolation before allowing any poller to process the task", DefaultValue: time.Millisecond * 200, }, TaskIsolationPollerWindow: { KeyName: "matching.taskIsolationPollerWindow", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "TaskIsolationDuration is the time period for which we attempt to respect tasklist isolation before allowing any poller to process the task", DefaultValue: time.Second * 2, }, DomainAuditLogTTL: { KeyName: "system.domainAuditLogTTL", Filters: []Filter{DomainID}, Description: "DomainAuditLogTTL is the TTL for domain audit log entries", DefaultValue: time.Hour * 24 * 365, // 1 year }, } var MapKeys = map[MapKey]DynamicMap{ TestGetMapPropertyKey: { KeyName: "testGetMapPropertyKey", Description: "", DefaultValue: nil, }, RequiredDomainDataKeys: { KeyName: "system.requiredDomainDataKeys", Description: "RequiredDomainDataKeys is the key for the list of data keys required in domain registration", DefaultValue: nil, }, ValidSearchAttributes: { KeyName: "frontend.validSearchAttributes", Description: "ValidSearchAttributes is legal indexed keys that can be used in list APIs. When overriding, ensure to include the existing default attributes of the current release", DefaultValue: definition.GetDefaultIndexedKeys(), }, TaskSchedulerRoundRobinWeights: { KeyName: "history.taskSchedulerRoundRobinWeight", Description: "TaskSchedulerRoundRobinWeights is the priority weight for weighted round robin task scheduler", DefaultValue: ConvertIntMapToDynamicConfigMapProperty(DefaultTaskSchedulerRoundRobinWeights), }, TaskSchedulerDomainRoundRobinWeights: { KeyName: "history.taskSchedulerDomainRoundRobinWeight", Description: "TaskSchedulerDomainRoundRobinWeights is the priority round robin weights for domains", Filters: []Filter{DomainName}, DefaultValue: ConvertIntMapToDynamicConfigMapProperty(DefaultTaskSchedulerRoundRobinWeights), }, QueueProcessorPendingTaskSplitThreshold: { KeyName: "history.queueProcessorPendingTaskSplitThreshold", Description: "QueueProcessorPendingTaskSplitThreshold is the threshold for the number of pending tasks per domain", DefaultValue: ConvertIntMapToDynamicConfigMapProperty(map[int]int{0: 1000, 1: 10000}), }, QueueProcessorStuckTaskSplitThreshold: { KeyName: "history.queueProcessorStuckTaskSplitThreshold", Description: "QueueProcessorStuckTaskSplitThreshold is the threshold for the number of attempts of a task", DefaultValue: ConvertIntMapToDynamicConfigMapProperty(map[int]int{0: 100, 1: 10000}), }, PinotOptimizedQueryColumns: { KeyName: "frontend.pinotOptimizedQueryColumns", Description: "PinotOptimizedQueryColumns is the list of search attributes that can be used in pinot optimized query", DefaultValue: map[string]interface{}{}, }, SearchAttributesHiddenValueKeys: { KeyName: "frontend.searchAttributesHiddenValueKeys", Description: "SearchAttributesHiddenValueKeys is the list of search attributes that values should be hidden", DefaultValue: map[string]interface{}{}, }, } var ListKeys = map[ListKey]DynamicList{ AllIsolationGroups: { KeyName: "system.allIsolationGroups", Description: "A list of all the isolation groups in a system", }, RateLimiterBypassCallerTypes: { KeyName: "system.rateLimiterBypassCallerTypes", Description: "List of caller types that bypass rate limiters (both frontend and persistence)", DefaultValue: []interface{}{}, }, DefaultIsolationGroupConfigStoreManagerGlobalMapping: { KeyName: "system.defaultIsolationGroupConfigStoreManagerGlobalMapping", Description: "A configuration store for global isolation groups - used in isolation-group config only, not normal dynamic config." + "Not intended for use in normal dynamic config", }, HeaderForwardingRules: { KeyName: "admin.HeaderForwardingRules", // make a new scope for global? Description: "Only loaded at startup. " + "A list of rpc.HeaderRule values that define which headers to include or exclude for all requests, applied in order. " + "Regexes and header names are used as-is, you are strongly encouraged to use `(?i)` to make your regex case-insensitive.", DefaultValue: []interface{}{ // historical behavior: include literally everything. // this alone is quite problematic, and is strongly recommended against. map[string]interface{}{ // config imports dynamicconfig, sadly "Add": true, "Match": "", }, }, }, } var _keyNames map[string]Key func init() { panicIfKeyInvalid := func(name string, key Key) { if name == "" { panic(fmt.Sprintf("empty keyName: %T, %v", key, key)) } if _, ok := _keyNames[name]; ok { panic(fmt.Sprintf("duplicate keyName: %v", name)) } } _keyNames = make(map[string]Key) for k, v := range IntKeys { panicIfKeyInvalid(v.KeyName, k) _keyNames[v.KeyName] = k } for k, v := range BoolKeys { panicIfKeyInvalid(v.KeyName, k) _keyNames[v.KeyName] = k } for k, v := range FloatKeys { panicIfKeyInvalid(v.KeyName, k) _keyNames[v.KeyName] = k } for k, v := range StringKeys { panicIfKeyInvalid(v.KeyName, k) _keyNames[v.KeyName] = k } for k, v := range DurationKeys { panicIfKeyInvalid(v.KeyName, k) _keyNames[v.KeyName] = k } for k, v := range MapKeys { panicIfKeyInvalid(v.KeyName, k) _keyNames[v.KeyName] = k } for k, v := range ListKeys { panicIfKeyInvalid(v.KeyName, k) _keyNames[v.KeyName] = k } } var ( DefaultTaskSchedulerRoundRobinWeights = map[int]int{ constants.GetTaskPriority(constants.HighPriorityClass, constants.DefaultPrioritySubclass): 500, constants.GetTaskPriority(constants.DefaultPriorityClass, constants.DefaultPrioritySubclass): 20, constants.GetTaskPriority(constants.LowPriorityClass, constants.DefaultPrioritySubclass): 5, } ) ================================================ FILE: common/dynamicconfig/dynamicproperties/constants_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package dynamicproperties import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/constants" ) type constantSuite struct { suite.Suite } func TestConstantSuite(t *testing.T) { suite.Run(t, new(constantSuite)) } func (s *constantSuite) TestListAllProductionKeys() { // check if we given enough capacity testResult := ListAllProductionKeys() s.GreaterOrEqual(len(IntKeys)+len(BoolKeys)+len(FloatKeys)+len(StringKeys)+len(DurationKeys)+len(MapKeys), len(testResult)) s.Equal(TestGetIntPropertyFilteredByTaskListInfoKey+1, testResult[0]) } func (s *constantSuite) TestGetKeyFromKeyName() { okKeyName := "system.transactionSizeLimit" okResult, err := GetKeyFromKeyName(okKeyName) s.NoError(err) s.Equal(TransactionSizeLimit, okResult) notOkKeyName := "system.transactionSizeLimit1" notOkResult, err := GetKeyFromKeyName(notOkKeyName) s.Error(err) s.Nil(notOkResult) } func (s *constantSuite) TestGetAllKeys() { testResult := GetAllKeys() s.Equal(len(IntKeys)+len(BoolKeys)+len(FloatKeys)+len(StringKeys)+len(DurationKeys)+len(MapKeys)+len(ListKeys), len(testResult)) s.Equal(_keyNames["testGetIntPropertyKey"], testResult["testGetIntPropertyKey"]) s.NotEqual(_keyNames["testGetIntPropertyKey"], testResult["testGetIntPropertyFilteredByTaskListInfoKey"]) } type NewKey int func (k NewKey) String() string { return "NewKey" } func (k NewKey) Description() string { return "NewKey is a new key" } func (k NewKey) DefaultValue() interface{} { return 0 } func (k NewKey) Filters() []Filter { return nil } func (s *constantSuite) TestValidateKeyValuePair() { newKeyError := ValidateKeyValuePair(NewKey(0), 0) s.Error(newKeyError) intKeyError := ValidateKeyValuePair(TestGetIntPropertyKey, "0") s.Error(intKeyError) boolKeyError := ValidateKeyValuePair(TestGetBoolPropertyKey, 0) s.Error(boolKeyError) floatKeyError := ValidateKeyValuePair(TestGetFloat64PropertyKey, 0) s.Error(floatKeyError) stringKeyError := ValidateKeyValuePair(TestGetStringPropertyKey, 0) s.Error(stringKeyError) durationKeyError := ValidateKeyValuePair(TestGetDurationPropertyKey, 0) s.Error(durationKeyError) mapKeyError := ValidateKeyValuePair(TestGetMapPropertyKey, 0) s.Error(mapKeyError) listKeyError := ValidateKeyValuePair(TestGetListPropertyKey, 0) s.Error(listKeyError) } func (s *constantSuite) TestIntKey() { testIntKeys := map[string]struct { key IntKey expectedString string expectedDefaultValue int expectedDescription string expectedFilters []Filter }{ "TestGetIntPropertyKey": { key: TestGetIntPropertyKey, expectedString: "testGetIntPropertyKey", expectedDescription: "", expectedDefaultValue: 0, expectedFilters: nil, }, "TransactionSizeLimit": { key: TransactionSizeLimit, expectedString: "system.transactionSizeLimit", expectedDescription: "TransactionSizeLimit is the largest allowed transaction size to persistence", expectedDefaultValue: 14680064, }, "BlobSizeLimitWarn": { key: BlobSizeLimitWarn, expectedString: "limit.blobSize.warn", expectedDescription: "BlobSizeLimitWarn is the per event blob size limit for warning", expectedDefaultValue: 256 * 1024, expectedFilters: []Filter{DomainName}, }, } for _, value := range testIntKeys { s.Equal(value.expectedString, value.key.String()) s.Equal(value.expectedDefaultValue, value.key.DefaultValue()) s.Equal(value.expectedDescription, value.key.Description()) s.Equal(value.expectedFilters, value.key.Filters()) s.Equal(value.expectedDefaultValue, value.key.DefaultInt()) } } func (s *constantSuite) TestBoolKey() { testBoolKeys := map[string]struct { key BoolKey expectedString string expectedDefaultValue bool expectedDescription string expectedFilters []Filter }{ "TestGetBoolPropertyKey": { key: TestGetBoolPropertyKey, expectedString: "testGetBoolPropertyKey", expectedDescription: "", expectedDefaultValue: false, expectedFilters: nil, }, "FrontendEmitSignalNameMetricsTag": { key: FrontendEmitSignalNameMetricsTag, expectedString: "frontend.emitSignalNameMetricsTag", expectedDescription: "FrontendEmitSignalNameMetricsTag enables emitting signal name tag in metrics in frontend client", expectedDefaultValue: false, expectedFilters: []Filter{DomainName}, }, } for _, value := range testBoolKeys { s.Equal(value.expectedString, value.key.String()) s.Equal(value.expectedDefaultValue, value.key.DefaultValue()) s.Equal(value.expectedDescription, value.key.Description()) s.Equal(value.expectedFilters, value.key.Filters()) s.Equal(value.expectedDefaultValue, value.key.DefaultBool()) } } func (s *constantSuite) TestFloatKey() { testFloatKeys := map[string]struct { key FloatKey KeyName string Filters []Filter Description string DefaultValue float64 }{ "TestGetFloat64PropertyKey": { key: TestGetFloat64PropertyKey, KeyName: "testGetFloat64PropertyKey", Description: "", DefaultValue: 0, }, "DomainFailoverRefreshTimerJitterCoefficient": { key: DomainFailoverRefreshTimerJitterCoefficient, KeyName: "frontend.domainFailoverRefreshTimerJitterCoefficient", Description: "DomainFailoverRefreshTimerJitterCoefficient is the jitter for domain failover refresh timer jitter", DefaultValue: 0.1, }, "ReplicationTaskProcessorStartWaitJitterCoefficient": { key: ReplicationTaskProcessorStartWaitJitterCoefficient, KeyName: "history.ReplicationTaskProcessorStartWaitJitterCoefficient", Filters: []Filter{ShardID}, Description: "ReplicationTaskProcessorStartWaitJitterCoefficient is the jitter for batch start wait timer", DefaultValue: 0.9, }, } for _, value := range testFloatKeys { s.Equal(value.KeyName, value.key.String()) s.Equal(value.DefaultValue, value.key.DefaultValue()) s.Equal(value.Description, value.key.Description()) s.Equal(value.Filters, value.key.Filters()) s.Equal(value.DefaultValue, value.key.DefaultFloat()) } } func (s *constantSuite) TestStringKey() { testStringKeys := map[string]struct { Key StringKey KeyName string Filters []Filter Description string DefaultValue string }{ "TestGetStringPropertyKey": { Key: TestGetStringPropertyKey, KeyName: "testGetStringPropertyKey", Description: "", DefaultValue: "", }, "HistoryArchivalStatus": { Key: HistoryArchivalStatus, KeyName: "system.historyArchivalStatus", Description: "HistoryArchivalStatus is key for the status of history archival to override the value from static config.", DefaultValue: "enabled", }, "DefaultEventEncoding": { Key: DefaultEventEncoding, KeyName: "history.defaultEventEncoding", Filters: []Filter{DomainName}, Description: "DefaultEventEncoding is the encoding type for history events", DefaultValue: string(constants.EncodingTypeThriftRW), }, "ReadVisibilityStoreName": { Key: ReadVisibilityStoreName, KeyName: "system.readVisibilityStoreName", Filters: []Filter{DomainName}, Description: "ReadVisibilityStoreName is key to identify which store to read visibility data from", DefaultValue: "es", }, "ShardDistributorMigrationMode": { Key: ShardDistributorMigrationMode, KeyName: "shardDistributor.migrationMode", Filters: []Filter{Namespace}, Description: "ShardDistributorMigrationMode is the mode the at represent the state of the migration to rely on shard distributor for the sharding mechanism", DefaultValue: "onboarded", }, } for _, value := range testStringKeys { s.Equal(value.KeyName, value.Key.String()) s.Equal(value.DefaultValue, value.Key.DefaultValue()) s.Equal(value.Description, value.Key.Description()) s.Equal(value.Filters, value.Key.Filters()) s.Equal(value.DefaultValue, value.Key.DefaultString()) } } func (s *constantSuite) TestDurationKey() { testDurationKeys := map[string]struct { Key DurationKey KeyName string Filters []Filter Description string DefaultValue time.Duration }{ "TestGetDurationPropertyKey": { Key: TestGetDurationPropertyKey, KeyName: "testGetDurationPropertyKey", Description: "", DefaultValue: 0, }, "FrontendFailoverCoolDown": { Key: FrontendFailoverCoolDown, KeyName: "frontend.failoverCoolDown", Filters: []Filter{DomainName}, Description: "FrontendFailoverCoolDown is duration between two domain failvoers", DefaultValue: time.Minute, }, "MatchingIdleTasklistCheckInterval": { Key: MatchingIdleTasklistCheckInterval, KeyName: "matching.idleTasklistCheckInterval", Filters: []Filter{DomainName, TaskListName, TaskType}, Description: "MatchingIdleTasklistCheckInterval is the IdleTasklistCheckInterval", DefaultValue: time.Minute * 5, }, } for _, value := range testDurationKeys { s.Equal(value.KeyName, value.Key.String()) s.Equal(value.DefaultValue, value.Key.DefaultValue()) s.Equal(value.Description, value.Key.Description()) s.Equal(value.Filters, value.Key.Filters()) s.Equal(value.DefaultValue, value.Key.DefaultDuration()) } } func (s *constantSuite) TestMapKey() { testMapKeys := map[string]struct { Key MapKey KeyName string Filters []Filter Description string DefaultValue map[string]interface{} }{ "TestGetMapPropertyKey": { Key: TestGetMapPropertyKey, KeyName: "testGetMapPropertyKey", Description: "", DefaultValue: nil, }, "TaskSchedulerRoundRobinWeights": { Key: TaskSchedulerRoundRobinWeights, KeyName: "history.taskSchedulerRoundRobinWeight", Description: "TaskSchedulerRoundRobinWeights is the priority weight for weighted round robin task scheduler", DefaultValue: ConvertIntMapToDynamicConfigMapProperty(map[int]int{ constants.GetTaskPriority(constants.HighPriorityClass, constants.DefaultPrioritySubclass): 500, constants.GetTaskPriority(constants.DefaultPriorityClass, constants.DefaultPrioritySubclass): 20, constants.GetTaskPriority(constants.LowPriorityClass, constants.DefaultPrioritySubclass): 5, }), }, "QueueProcessorStuckTaskSplitThreshold": { Key: QueueProcessorStuckTaskSplitThreshold, KeyName: "history.queueProcessorStuckTaskSplitThreshold", Description: "QueueProcessorStuckTaskSplitThreshold is the threshold for the number of attempts of a task", DefaultValue: ConvertIntMapToDynamicConfigMapProperty(map[int]int{0: 100, 1: 10000}), }, } for _, value := range testMapKeys { s.Equal(value.KeyName, value.Key.String()) s.Equal(value.DefaultValue, value.Key.DefaultValue()) s.Equal(value.Description, value.Key.Description()) s.Equal(value.Filters, value.Key.Filters()) s.Equal(value.DefaultValue, value.Key.DefaultMap()) } } func (s *constantSuite) TestListKey() { testListKeys := map[string]struct { Key ListKey KeyName string Filters []Filter Description string DefaultValue []interface{} }{ "DefaultIsolationGroupConfigStoreManagerGlobalMapping": { Key: DefaultIsolationGroupConfigStoreManagerGlobalMapping, KeyName: "system.defaultIsolationGroupConfigStoreManagerGlobalMapping", Description: "A configuration store for global isolation groups - used in isolation-group config only, not normal dynamic config." + "Not intended for use in normal dynamic config", }, "HeaderForwardingRules": { Key: HeaderForwardingRules, KeyName: "admin.HeaderForwardingRules", Description: "Only loaded at startup. " + "A list of rpc.HeaderRule values that define which headers to include or exclude for all requests, applied in order. " + "Regexes and header names are used as-is, you are strongly encouraged to use `(?i)` to make your regex case-insensitive.", DefaultValue: []interface{}{ map[string]interface{}{ "Add": true, "Match": "", }, }, }, } for _, value := range testListKeys { s.Equal(value.KeyName, value.Key.String()) s.Equal(value.DefaultValue, value.Key.DefaultValue()) s.Equal(value.Description, value.Key.Description()) s.Equal(value.Filters, value.Key.Filters()) s.Equal(value.DefaultValue, value.Key.DefaultList()) } } func TestDynamicConfigFilterTypeIsMapped(t *testing.T) { require.Equal(t, int(LastFilterTypeForTest), len(filters)) for i := UnknownFilter; i < LastFilterTypeForTest; i++ { require.NotEmpty(t, filters[i]) } } func TestDynamicConfigFilterTypeIsParseable(t *testing.T) { allFilters := map[Filter]int{} for idx, filterString := range filters { // TestDynamicConfigFilterTypeIsMapped ensures this is a complete list // all filter-strings must parse to unique filters parsed := ParseFilter(filterString) prev, ok := allFilters[parsed] assert.False(t, ok, "%q is already mapped to the same filter type as %q", filterString, filters[prev]) allFilters[parsed] = idx // otherwise, only "unknown" should map to "unknown". // ParseFilter should probably be re-implemented to simply use a map that is shared with the definitions // so values cannot get out of sync, but for now this is just asserting what is currently built. if idx == 0 { assert.Equalf(t, UnknownFilter, ParseFilter(filterString), "first filter string should have parsed as unknown: %v", filterString) // unknown filter string is likely safe to change and then should be updated here, but otherwise this ensures the logic isn't entirely position-dependent. require.Equalf(t, "unknownFilter", filterString, "expected first filter to be 'unknownFilter', but it was %v", filterString) } else { assert.NotEqualf(t, UnknownFilter, ParseFilter(filterString), "failed to parse filter: %s, make sure it is in ParseFilter's switch statement", filterString) } } } func TestDynamicConfigFilterStringsCorrectly(t *testing.T) { for _, filterString := range filters { // filter-string-parsing and the resulting filter's String() must match parsed := ParseFilter(filterString) assert.Equal(t, filterString, parsed.String(), "filters need to String() correctly as some impls rely on it") } // should not be possible normally, but improper casting could trigger it badFilter := Filter(len(filters)) assert.Equal(t, UnknownFilter.String(), badFilter.String(), "filters with indexes outside the list of known strings should String() to the unknown filter type") } ================================================ FILE: common/dynamicconfig/dynamicproperties/definitions.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package dynamicproperties import ( "fmt" "strconv" "strings" "time" ) // PropertyFn is a wrapper to get property from dynamic config type PropertyFn func() interface{} // IntPropertyFn is a wrapper to get int property from dynamic config type IntPropertyFn func(opts ...FilterOption) int // IntPropertyFnWithDomainFilter is a wrapper to get int property from dynamic config with domain as filter type IntPropertyFnWithDomainFilter func(domain string) int // IntPropertyFnWithTaskListInfoFilters is a wrapper to get int property from dynamic config with three filters: domain, taskList, taskType type IntPropertyFnWithTaskListInfoFilters func(domain string, taskList string, taskType int) int // IntPropertyFnWithShardIDFilter is a wrapper to get int property from dynamic config with shardID as filter type IntPropertyFnWithShardIDFilter func(shardID int) int // FloatPropertyFn is a wrapper to get float property from dynamic config type FloatPropertyFn func(opts ...FilterOption) float64 // FloatPropertyFnWithShardIDFilter is a wrapper to get float property from dynamic config with shardID as filter type FloatPropertyFnWithShardIDFilter func(shardID int) float64 // FloatPropertyFnWithTaskListInfoFilters is a wrapper to get duration property from dynamic config with three filters: domain, taskList, taskType type FloatPropertyFnWithTaskListInfoFilters func(domain string, taskList string, taskType int) float64 // Float64PropertyFnWithNamespaceFilters is a wrapper to get string property from dynamic config with namespace as filter type Float64PropertyFnWithNamespaceFilters func(namespace string) float64 // DurationPropertyFn is a wrapper to get duration property from dynamic config type DurationPropertyFn func(opts ...FilterOption) time.Duration // DurationPropertyFnWithDomainFilter is a wrapper to get duration property from dynamic config with domain as filter type DurationPropertyFnWithDomainFilter func(domain string) time.Duration // DurationPropertyFnWithDomainIDFilter is a wrapper to get duration property from dynamic config with domainID as filter type DurationPropertyFnWithDomainIDFilter func(domainID string) time.Duration // DurationPropertyFnWithTaskListInfoFilters is a wrapper to get duration property from dynamic config with three filters: domain, taskList, taskType type DurationPropertyFnWithTaskListInfoFilters func(domain string, taskList string, taskType int) time.Duration // DurationPropertyFnWithShardIDFilter is a wrapper to get duration property from dynamic config with shardID as filter type DurationPropertyFnWithShardIDFilter func(shardID int) time.Duration // BoolPropertyFn is a wrapper to get bool property from dynamic config type BoolPropertyFn func(opts ...FilterOption) bool // StringPropertyFn is a wrapper to get string property from dynamic config type StringPropertyFn func(opts ...FilterOption) string // MapPropertyFn is a wrapper to get map property from dynamic config type MapPropertyFn func(opts ...FilterOption) map[string]interface{} // MapPropertyFnWithDomainFilter is a wrapper to get map property from dynamic config with domainName as filter type MapPropertyFnWithDomainFilter func(domain string) map[string]interface{} // StringPropertyFnWithDomainFilter is a wrapper to get string property from dynamic config type StringPropertyFnWithDomainFilter func(domain string) string // StringPropertyFnWithTaskListInfoFilters is a wrapper to get string property from dynamic config with domainID as filter type StringPropertyFnWithTaskListInfoFilters func(domain string, taskList string, taskType int) string // StringPropertyFnWithNamespaceFilters is a wrapper to get string property from dynamic config with namespace as filter type StringPropertyFnWithNamespaceFilters func(namespace string) string // BoolPropertyFnWithDomainFilter is a wrapper to get bool property from dynamic config with domain as filter type BoolPropertyFnWithDomainFilter func(domain string) bool // BoolPropertyFnWithDomainIDFilter is a wrapper to get bool property from dynamic config with domainID as filter type BoolPropertyFnWithDomainIDFilter func(domainID string) bool // BoolPropertyFnWithDomainIDAndWorkflowIDFilter is a wrapper to get bool property from dynamic config with domainID and workflowID as filter type BoolPropertyFnWithDomainIDAndWorkflowIDFilter func(domainID string, workflowID string) bool // BoolPropertyFnWithTaskListInfoFilters is a wrapper to get bool property from dynamic config with three filters: domain, taskList, taskType type BoolPropertyFnWithTaskListInfoFilters func(domain string, taskList string, taskType int) bool // BoolPropertyFnWithShardIDFilter is a wrapper to get bool property from dynamic config with shardID as filter type BoolPropertyFnWithShardIDFilter func(shardID int) bool // IntPropertyFnWithWorkflowTypeFilter is a wrapper to get int property from dynamic config with domain as filter type IntPropertyFnWithWorkflowTypeFilter func(domainName string, workflowType string) int // DurationPropertyFnWithWorkflowTypeFilter is a wrapper to get duration property from dynamic config with domain as filter type DurationPropertyFnWithWorkflowTypeFilter func(domainName string, workflowType string) time.Duration // ListPropertyFn is a wrapper to get a list property from dynamic config type ListPropertyFn func(opts ...FilterOption) []interface{} // StringPropertyWithRatelimitKeyFilter is a wrapper to get strings (currently global ratelimiter modes) per global ratelimit key type StringPropertyWithRatelimitKeyFilter func(globalRatelimitKey string) string // StringPropertyWithNamespaceFilter is a wrapper to get strings per namespace type StringPropertyWithNamespaceFilter func(namespace string) string func (f IntPropertyFn) AsFloat64(opts ...FilterOption) func() float64 { return func() float64 { return float64(f(opts...)) } } func (f IntPropertyFnWithDomainFilter) AsFloat64(domain string) func() float64 { return func() float64 { return float64(f(domain)) } } func (f FloatPropertyFn) AsFloat64(opts ...FilterOption) func() float64 { return func() float64 { return float64(f(opts...)) } } // ConvertIntMapToDynamicConfigMapProperty converts a map whose key value type are both int to // a map value that is compatible with dynamic config's map property func ConvertIntMapToDynamicConfigMapProperty( intMap map[int]int, ) map[string]interface{} { dcValue := make(map[string]interface{}) for key, value := range intMap { dcValue[strconv.Itoa(key)] = value } return dcValue } // ConvertDynamicConfigMapPropertyToIntMap convert a map property from dynamic config to a map // whose type for both key and value are int func ConvertDynamicConfigMapPropertyToIntMap(dcValue map[string]interface{}) (map[int]int, error) { intMap := make(map[int]int) for key, value := range dcValue { intKey, err := strconv.Atoi(strings.TrimSpace(key)) if err != nil { return nil, fmt.Errorf("failed to convert key %v, error: %v", key, err) } var intValue int switch value := value.(type) { case float64: intValue = int(value) case int: intValue = value case int32: intValue = int(value) case int64: intValue = int(value) default: return nil, fmt.Errorf("unknown value %v with type %T", value, value) } intMap[intKey] = intValue } return intMap, nil } ================================================ FILE: common/dynamicconfig/dynamicproperties/filter.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamicproperties import ( "encoding/json" "fmt" "github.com/uber/cadence/common/types" ) // Filter represents a filter on the dynamic config key type Filter int func (f Filter) String() string { if f > UnknownFilter && int(f) < len(filters) { return filters[f] } return filters[UnknownFilter] } func ParseFilter(filterName string) Filter { switch filterName { case "domainName": return DomainName case "domainID": return DomainID case "taskListName": return TaskListName case "taskType": return TaskType case "shardID": return ShardID case "clusterName": return ClusterName case "workflowID": return WorkflowID case "workflowType": return WorkflowType case "ratelimitKey": return RatelimitKey case "namespace": return Namespace default: return UnknownFilter } } var filters = []string{ "unknownFilter", "domainName", "domainID", "taskListName", "taskType", "shardID", "clusterName", "workflowID", "workflowType", "ratelimitKey", "namespace", } const ( UnknownFilter Filter = iota // DomainName is the domain name DomainName // DomainID is the domain id DomainID // TaskListName is the tasklist name TaskListName // TaskType is the task type (0:Decision, 1:Activity) TaskType // ShardID is the shard id ShardID // ClusterName is the cluster name in a multi-region setup ClusterName // WorkflowID is the workflow id WorkflowID // WorkflowType is the workflow type name WorkflowType // RatelimitKey is the global ratelimit key (not a local key name) RatelimitKey // Namespace is the entity of independent shard distribution mechanism Namespace // LastFilterTypeForTest must be the last one in this const group for testing purpose LastFilterTypeForTest ) // FilterOption is used to provide filters for dynamic config keys type FilterOption func(filterMap map[Filter]interface{}) // TaskListFilter filters by task list name func TaskListFilter(name string) FilterOption { return func(filterMap map[Filter]interface{}) { filterMap[TaskListName] = name } } // DomainFilter filters by domain name func DomainFilter(name string) FilterOption { return func(filterMap map[Filter]interface{}) { filterMap[DomainName] = name } } // DomainIDFilter filters by domain id func DomainIDFilter(domainID string) FilterOption { return func(filterMap map[Filter]interface{}) { filterMap[DomainID] = domainID } } // TaskTypeFilter filters by task type func TaskTypeFilter(taskType int) FilterOption { return func(filterMap map[Filter]interface{}) { filterMap[TaskType] = taskType } } // ShardIDFilter filters by shard id func ShardIDFilter(shardID int) FilterOption { return func(filterMap map[Filter]interface{}) { filterMap[ShardID] = shardID } } // ClusterNameFilter filters by cluster name func ClusterNameFilter(clusterName string) FilterOption { return func(filterMap map[Filter]interface{}) { filterMap[ClusterName] = clusterName } } // WorkflowIDFilter filters by workflowID func WorkflowIDFilter(workflowID string) FilterOption { return func(filterMap map[Filter]interface{}) { filterMap[WorkflowID] = workflowID } } // WorkflowType filters by workflow type name func WorkflowTypeFilter(name string) FilterOption { return func(filterMap map[Filter]interface{}) { filterMap[WorkflowType] = name } } // RatelimitKeyFilter filters on global ratelimiter keys (via the global name, not local names) func RatelimitKeyFilter(key string) FilterOption { return func(filterMap map[Filter]interface{}) { filterMap[RatelimitKey] = key } } // NamespaceFilter filters by namespace func NamespaceFilter(namespace string) FilterOption { return func(filterMap map[Filter]interface{}) { filterMap[Namespace] = namespace } } // ToGetDynamicConfigFilterRequest generates a GetDynamicConfigRequest object // by converting filters to DynamicConfigFilter objects and setting values func ToGetDynamicConfigFilterRequest(configName string, filters []FilterOption) *types.GetDynamicConfigRequest { filterMap := make(map[Filter]interface{}, len(filters)) for _, opt := range filters { opt(filterMap) } var dcFilters []*types.DynamicConfigFilter for f, entity := range filterMap { filter := &types.DynamicConfigFilter{ Name: f.String(), } data, err := json.Marshal(entity) if err != nil { fmt.Errorf("could not marshall entity: %s", err) } encodingType := types.EncodingTypeJSON filter.Value = &types.DataBlob{ EncodingType: &encodingType, Data: data, } dcFilters = append(dcFilters, filter) } request := &types.GetDynamicConfigRequest{ ConfigName: configName, Filters: dcFilters, } return request } ================================================ FILE: common/dynamicconfig/dynamicproperties/utils_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package dynamicproperties import ( "strconv" "testing" "github.com/stretchr/testify/require" ) func TestConvertDynamicConfigMapPropertyToIntMap(t *testing.T) { dcValue := make(map[string]interface{}) for idx, value := range []interface{}{int(0), int32(1), int64(2), float64(3.0)} { dcValue[strconv.Itoa(idx)] = value } intMap, err := ConvertDynamicConfigMapPropertyToIntMap(dcValue) require.NoError(t, err) require.Len(t, intMap, 4) for i := 0; i != 4; i++ { require.Equal(t, i, intMap[i]) } } ================================================ FILE: common/dynamicconfig/file_based_client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamicconfig import ( "errors" "fmt" "io/ioutil" "os" "sync/atomic" "time" "gopkg.in/yaml.v2" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) var _ Client = (*fileBasedClient)(nil) const ( minPollInterval = time.Second * 5 fileMode = 0644 // used for update config file ) type constrainedValue struct { Value interface{} Constraints map[string]interface{} } // FileBasedClientConfig is the config for the file based dynamic config client. // It specifies where the config file is stored and how often the config should be // updated by checking the config file again. type FileBasedClientConfig struct { Filepath string `yaml:"filepath"` PollInterval time.Duration `yaml:"pollInterval"` } type fileBasedClient struct { values atomic.Value lastUpdatedTime time.Time config *FileBasedClientConfig doneCh chan struct{} logger log.Logger } // NewFileBasedClient creates a file based client. func NewFileBasedClient(config *FileBasedClientConfig, logger log.Logger, doneCh chan struct{}) (Client, error) { if err := validateConfig(config); err != nil { return nil, err } client := &fileBasedClient{ config: config, doneCh: doneCh, logger: logger, } if err := client.update(); err != nil { return nil, err } go func() { ticker := time.NewTicker(client.config.PollInterval) for { select { case <-ticker.C: err := client.update() if err != nil { client.logger.Error("Failed to update dynamic config", tag.Error(err)) } case <-client.doneCh: ticker.Stop() return } } }() return client, nil } func (fc *fileBasedClient) GetValue(name dynamicproperties.Key) (interface{}, error) { return fc.getValueWithFilters(name, nil, name.DefaultValue()) } func (fc *fileBasedClient) GetValueWithFilters(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) (interface{}, error) { return fc.getValueWithFilters(name, filters, name.DefaultValue()) } func (fc *fileBasedClient) GetIntValue(name dynamicproperties.IntKey, filters map[dynamicproperties.Filter]interface{}) (int, error) { defaultValue := name.DefaultInt() val, err := fc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if intVal, ok := val.(int); ok { return intVal, nil } return defaultValue, fmt.Errorf("value type is not int but is: %T", val) } func (fc *fileBasedClient) GetFloatValue(name dynamicproperties.FloatKey, filters map[dynamicproperties.Filter]interface{}) (float64, error) { defaultValue := name.DefaultFloat() val, err := fc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if floatVal, ok := val.(float64); ok { return floatVal, nil } else if intVal, ok := val.(int); ok { return float64(intVal), nil } return defaultValue, fmt.Errorf("value type is not float64 but is: %T", val) } func (fc *fileBasedClient) GetBoolValue(name dynamicproperties.BoolKey, filters map[dynamicproperties.Filter]interface{}) (bool, error) { defaultValue := name.DefaultBool() val, err := fc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if boolVal, ok := val.(bool); ok { return boolVal, nil } return defaultValue, fmt.Errorf("value type is not bool but is: %T", val) } func (fc *fileBasedClient) GetStringValue(name dynamicproperties.StringKey, filters map[dynamicproperties.Filter]interface{}) (string, error) { defaultValue := name.DefaultString() val, err := fc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if stringVal, ok := val.(string); ok { return stringVal, nil } return defaultValue, fmt.Errorf("value type is not string but is: %T", val) } func (fc *fileBasedClient) GetMapValue(name dynamicproperties.MapKey, filters map[dynamicproperties.Filter]interface{}) (map[string]interface{}, error) { defaultValue := name.DefaultMap() val, err := fc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if mapVal, ok := val.(map[string]interface{}); ok { return mapVal, nil } return defaultValue, fmt.Errorf("value type is not map but is: %T", val) } func (fc *fileBasedClient) GetDurationValue(name dynamicproperties.DurationKey, filters map[dynamicproperties.Filter]interface{}) (time.Duration, error) { defaultValue := name.DefaultDuration() val, err := fc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } durationString, ok := val.(string) if !ok { return defaultValue, fmt.Errorf("value type is not string but is: %T", val) } durationVal, err := time.ParseDuration(durationString) if err != nil { return defaultValue, fmt.Errorf("failed to parse duration: %v", err) } return durationVal, nil } func (fc *fileBasedClient) GetListValue(name dynamicproperties.ListKey, filters map[dynamicproperties.Filter]interface{}) ([]interface{}, error) { defaultValue := name.DefaultList() val, err := fc.getValueWithFilters(name, filters, defaultValue) if err != nil { return defaultValue, err } if listVal, ok := val.([]interface{}); ok { return listVal, nil } return defaultValue, fmt.Errorf("value type is not list but is: %T", val) } func (fc *fileBasedClient) UpdateValue(name dynamicproperties.Key, value interface{}) error { if err := dynamicproperties.ValidateKeyValuePair(name, value); err != nil { return err } keyName := name.String() currentValues := make(map[string][]*constrainedValue) confContent, err := ioutil.ReadFile(fc.config.Filepath) if err != nil { return fmt.Errorf("failed to read dynamic config file %v: %v", fc.config.Filepath, err) } if err = yaml.Unmarshal(confContent, currentValues); err != nil { return fmt.Errorf("failed to decode dynamic config %v", err) } cVal := &constrainedValue{ Value: value, } currentValues[keyName] = []*constrainedValue{cVal} newBytes, _ := yaml.Marshal(currentValues) err = ioutil.WriteFile(fc.config.Filepath, newBytes, fileMode) if err != nil { return fmt.Errorf("failed to write config file, err: %v", err) } return fc.storeValues(currentValues) } func (fc *fileBasedClient) RestoreValue(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) error { return errors.New("not supported for file based client") } func (fc *fileBasedClient) ListValue(name dynamicproperties.Key) ([]*types.DynamicConfigEntry, error) { return nil, errors.New("not supported for file based client") } func (fc *fileBasedClient) update() error { defer func() { fc.lastUpdatedTime = time.Now() }() newValues := make(map[string][]*constrainedValue) info, err := os.Stat(fc.config.Filepath) if err != nil { return fmt.Errorf("failed to get status of dynamic config file: %v", err) } if !info.ModTime().After(fc.lastUpdatedTime) { return nil } confContent, err := ioutil.ReadFile(fc.config.Filepath) if err != nil { return fmt.Errorf("failed to read dynamic config file %v: %v", fc.config.Filepath, err) } if err = yaml.Unmarshal(confContent, newValues); err != nil { return fmt.Errorf("failed to decode dynamic config %v", err) } return fc.storeValues(newValues) } func (fc *fileBasedClient) storeValues(newValues map[string][]*constrainedValue) error { // yaml will unmarshal map into map[interface{}]interface{} instead of map[string]interface{} // manually convert key type to string for all values here // We don't need to convert constraints as their type can't be map. If user does use a map as filter // value, it won't match anyway. for _, s := range newValues { for _, cv := range s { var err error cv.Value, err = convertKeyTypeToString(cv.Value) if err != nil { return err } } } fc.values.Store(newValues) fc.logger.Info("Updated dynamic config") return nil } func (fc *fileBasedClient) getValueWithFilters(key dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}, defaultValue interface{}) (interface{}, error) { keyName := key.String() values := fc.values.Load().(map[string][]*constrainedValue) found := false for _, constrainedValue := range values[keyName] { if len(constrainedValue.Constraints) == 0 { // special handling for default value (value without any constraints) defaultValue = constrainedValue.Value found = true continue } if match(constrainedValue, filters) { return constrainedValue.Value, nil } } if !found { return defaultValue, NotFoundError } return defaultValue, nil } // match will return true if the constraints matches the filters or any subsets func match(v *constrainedValue, filters map[dynamicproperties.Filter]interface{}) bool { if len(v.Constraints) > len(filters) { return false } for constrain, constrainedValue := range v.Constraints { constrainKey := dynamicproperties.ParseFilter(constrain) if filters[constrainKey] == nil || filters[constrainKey] != constrainedValue { return false } } return true } func convertKeyTypeToString(v interface{}) (interface{}, error) { switch v := v.(type) { case map[interface{}]interface{}: return convertKeyTypeToStringMap(v) case []interface{}: return convertKeyTypeToStringSlice(v) default: return v, nil } } func convertKeyTypeToStringMap(m map[interface{}]interface{}) (map[string]interface{}, error) { stringKeyMap := make(map[string]interface{}) for key, value := range m { stringKey, ok := key.(string) if !ok { return nil, fmt.Errorf("type of key %v is not string", key) } convertedValue, err := convertKeyTypeToString(value) if err != nil { return nil, err } stringKeyMap[stringKey] = convertedValue } return stringKeyMap, nil } func convertKeyTypeToStringSlice(s []interface{}) ([]interface{}, error) { stringKeySlice := make([]interface{}, len(s)) for idx, value := range s { convertedValue, err := convertKeyTypeToString(value) if err != nil { return nil, err } stringKeySlice[idx] = convertedValue } return stringKeySlice, nil } func validateConfig(config *FileBasedClientConfig) error { if config == nil { return errors.New("no config found for file based dynamic config client") } if _, err := os.Stat(config.Filepath); err != nil { return fmt.Errorf("error checking dynamic config file at path %s, error: %v", config.Filepath, err) } // check if poll interval needs to be adjusted if config.PollInterval < minPollInterval { config.PollInterval = minPollInterval } return nil } ================================================ FILE: common/dynamicconfig/file_based_client_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamicconfig import ( "fmt" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" ) type fileBasedClientSuite struct { suite.Suite *require.Assertions client Client doneCh chan struct{} } func TestFileBasedClientSuite(t *testing.T) { s := new(fileBasedClientSuite) suite.Run(t, s) } func (s *fileBasedClientSuite) SetupSuite() { var err error s.doneCh = make(chan struct{}) s.client, err = NewFileBasedClient(&FileBasedClientConfig{ Filepath: "config/testConfig.yaml", PollInterval: time.Second * 5, }, log.NewNoop(), s.doneCh) s.Require().NoError(err) } func (s *fileBasedClientSuite) TearDownSuite() { close(s.doneCh) } func (s *fileBasedClientSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *fileBasedClientSuite) TestGetValue() { v, err := s.client.GetValue(dynamicproperties.TestGetBoolPropertyKey) s.NoError(err) s.Equal(false, v) } func (s *fileBasedClientSuite) TestGetValue_NonExistKey() { v, err := s.client.GetValue(dynamicproperties.EnableVisibilitySampling) s.Error(err) s.Equal(dynamicproperties.EnableVisibilitySampling.DefaultBool(), v) } func (s *fileBasedClientSuite) TestGetValueWithFilters() { filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "global-samples-domain", } v, err := s.client.GetValueWithFilters(dynamicproperties.TestGetBoolPropertyKey, filters) s.NoError(err) s.Equal(true, v) filters = map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "non-exist-domain", } v, err = s.client.GetValueWithFilters(dynamicproperties.TestGetBoolPropertyKey, filters) s.NoError(err) s.Equal(false, v) filters = map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", dynamicproperties.TaskListName: "non-exist-tasklist", } v, err = s.client.GetValueWithFilters(dynamicproperties.TestGetBoolPropertyKey, filters) s.NoError(err) s.Equal(true, v) } func (s *fileBasedClientSuite) TestGetValueWithFilters_UnknownFilter() { filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "global-samples-domain1", dynamicproperties.UnknownFilter: "unknown-filter1", } v, err := s.client.GetValueWithFilters(dynamicproperties.TestGetBoolPropertyKey, filters) s.NoError(err) s.Equal(false, v) } func (s *fileBasedClientSuite) TestGetIntValue() { v, err := s.client.GetIntValue(dynamicproperties.TestGetIntPropertyKey, nil) s.NoError(err) s.Equal(1000, v) } func (s *fileBasedClientSuite) TestGetIntValue_FilterNotMatch() { filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", } v, err := s.client.GetIntValue(dynamicproperties.TestGetIntPropertyKey, filters) s.NoError(err) s.Equal(1000, v) } func (s *fileBasedClientSuite) TestGetIntValue_WrongType() { filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "global-samples-domain", } v, err := s.client.GetIntValue(dynamicproperties.TestGetIntPropertyKey, filters) s.Error(err) s.Equal(dynamicproperties.TestGetIntPropertyKey.DefaultInt(), v) } func (s *fileBasedClientSuite) TestGetFloatValue() { v, err := s.client.GetFloatValue(dynamicproperties.TestGetFloat64PropertyKey, nil) s.NoError(err) s.Equal(12.0, v) } func (s *fileBasedClientSuite) TestGetFloatValue_WrongType() { filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", } v, err := s.client.GetFloatValue(dynamicproperties.TestGetFloat64PropertyKey, filters) s.Error(err) s.Equal(dynamicproperties.TestGetFloat64PropertyKey.DefaultFloat(), v) } func (s *fileBasedClientSuite) TestGetBoolValue() { v, err := s.client.GetBoolValue(dynamicproperties.TestGetBoolPropertyKey, nil) s.NoError(err) s.Equal(false, v) } func (s *fileBasedClientSuite) TestGetStringValue() { filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.TaskListName: "random tasklist", } v, err := s.client.GetStringValue(dynamicproperties.TestGetStringPropertyKey, filters) s.NoError(err) s.Equal("constrained-string", v) } func (s *fileBasedClientSuite) TestGetMapValue() { v, err := s.client.GetMapValue(dynamicproperties.TestGetMapPropertyKey, nil) s.NoError(err) expectedVal := map[string]interface{}{ "key1": "1", "key2": 1, "key3": []interface{}{ false, map[string]interface{}{ "key4": true, "key5": 2.1, }, }, } s.Equal(expectedVal, v) } func (s *fileBasedClientSuite) TestGetMapValue_WrongType() { filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.TaskListName: "random tasklist", } v, err := s.client.GetMapValue(dynamicproperties.TestGetMapPropertyKey, filters) s.Error(err) s.Equal(dynamicproperties.TestGetMapPropertyKey.DefaultMap(), v) } func (s *fileBasedClientSuite) TestGetDurationValue() { v, err := s.client.GetDurationValue(dynamicproperties.TestGetDurationPropertyKey, nil) s.NoError(err) s.Equal(time.Minute, v) } func (s *fileBasedClientSuite) TestGetDurationValue_NotStringRepresentation() { filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", } v, err := s.client.GetDurationValue(dynamicproperties.TestGetDurationPropertyKey, filters) s.Error(err) s.Equal(dynamicproperties.TestGetDurationPropertyKey.DefaultDuration(), v) } func (s *fileBasedClientSuite) TestGetDurationValue_ParseFailed() { filters := map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", dynamicproperties.TaskListName: "longIdleTimeTasklist", } v, err := s.client.GetDurationValue(dynamicproperties.TestGetDurationPropertyKey, filters) s.Error(err) s.Equal(dynamicproperties.TestGetDurationPropertyKey.DefaultDuration(), v) } func (s *fileBasedClientSuite) TestValidateConfig_ConfigNotExist() { _, err := NewFileBasedClient(nil, nil, nil) s.Error(err) } func (s *fileBasedClientSuite) TestValidateConfig_FileNotExist() { _, err := NewFileBasedClient(&FileBasedClientConfig{ Filepath: "file/not/exist.yaml", PollInterval: time.Second * 10, }, nil, nil) s.Error(err) } func (s *fileBasedClientSuite) TestValidateConfig_ShortPollInterval() { cfg := &FileBasedClientConfig{ Filepath: "config/testConfig.yaml", PollInterval: time.Second, } _, err := NewFileBasedClient(cfg, log.NewNoop(), nil) s.NoError(err) s.Equal(minPollInterval, cfg.PollInterval, "fallback to default poll interval") } func (s *fileBasedClientSuite) TestMatch() { testCases := []struct { v *constrainedValue filters map[dynamicproperties.Filter]interface{} matched bool }{ { v: &constrainedValue{ Constraints: map[string]interface{}{}, }, filters: map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "some random domain", }, matched: true, }, { v: &constrainedValue{ Constraints: map[string]interface{}{"some key": "some value"}, }, filters: map[dynamicproperties.Filter]interface{}{}, matched: false, }, { v: &constrainedValue{ Constraints: map[string]interface{}{"domainName": "samples-domain"}, }, filters: map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "some random domain", }, matched: false, }, { v: &constrainedValue{ Constraints: map[string]interface{}{ "domainName": "samples-domain", "taskListName": "sample-task-list", }, }, filters: map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", dynamicproperties.TaskListName: "sample-task-list", }, matched: true, }, { v: &constrainedValue{ Constraints: map[string]interface{}{ "domainName": "samples-domain", "some-other-filter": "sample-task-list", }, }, filters: map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples-domain", dynamicproperties.TaskListName: "sample-task-list", }, matched: false, }, { v: &constrainedValue{ Constraints: map[string]interface{}{ "domainName": "samples-domain", }, }, filters: map[dynamicproperties.Filter]interface{}{ dynamicproperties.TaskListName: "sample-task-list", }, matched: false, }, } for index, tc := range testCases { matched := match(tc.v, tc.filters) s.Equal(tc.matched, matched, fmt.Sprintf("Test case %v failved", index)) } } func (s *fileBasedClientSuite) TestUpdateConfig() { client := s.client.(*fileBasedClient) key := dynamicproperties.ValidSearchAttributes // pre-check existing config current, err := client.GetMapValue(key, nil) s.NoError(err) currentDomainVal, ok := current["DomainID"] s.True(ok) s.Equal(1, currentDomainVal) _, ok = current["WorkflowID"] s.False(ok) // update config v := map[string]interface{}{ "WorkflowID": 1, "DomainID": 2, } err = client.UpdateValue(key, v) s.NoError(err) // verify update result current, err = client.GetMapValue(key, nil) s.NoError(err) currentDomainVal, ok = current["DomainID"] s.True(ok) s.Equal(2, currentDomainVal) currentWorkflowIDVal, ok := current["WorkflowID"] s.True(ok) s.Equal(1, currentWorkflowIDVal) // revert test file back v = map[string]interface{}{ "DomainID": 1, } err = client.UpdateValue(key, v) s.NoError(err) } ================================================ FILE: common/dynamicconfig/inMemoryClient.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package dynamicconfig import ( "errors" "sync" "time" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) type inMemoryClient struct { sync.RWMutex globalValues map[dynamicproperties.Key]interface{} } // NewInMemoryClient creates a new in memory dynamic config client for testing purpose func NewInMemoryClient() Client { return &inMemoryClient{ globalValues: make(map[dynamicproperties.Key]interface{}), } } func (mc *inMemoryClient) SetValue(key dynamicproperties.Key, value interface{}) { mc.Lock() defer mc.Unlock() mc.globalValues[key] = value } func (mc *inMemoryClient) GetValue(key dynamicproperties.Key) (interface{}, error) { mc.RLock() defer mc.RUnlock() if val, ok := mc.globalValues[key]; ok { return val, nil } return key.DefaultValue(), NotFoundError } func (mc *inMemoryClient) GetValueWithFilters(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) (interface{}, error) { mc.RLock() defer mc.RUnlock() return mc.GetValue(name) } func (mc *inMemoryClient) GetIntValue(name dynamicproperties.IntKey, filters map[dynamicproperties.Filter]interface{}) (int, error) { mc.RLock() defer mc.RUnlock() if val, ok := mc.globalValues[name]; ok { return val.(int), nil } return name.DefaultInt(), NotFoundError } func (mc *inMemoryClient) GetFloatValue(name dynamicproperties.FloatKey, filters map[dynamicproperties.Filter]interface{}) (float64, error) { mc.RLock() defer mc.RUnlock() if val, ok := mc.globalValues[name]; ok { return val.(float64), nil } return name.DefaultFloat(), NotFoundError } func (mc *inMemoryClient) GetBoolValue(name dynamicproperties.BoolKey, filters map[dynamicproperties.Filter]interface{}) (bool, error) { mc.RLock() defer mc.RUnlock() if val, ok := mc.globalValues[name]; ok { return val.(bool), nil } return name.DefaultBool(), NotFoundError } func (mc *inMemoryClient) GetStringValue(name dynamicproperties.StringKey, filters map[dynamicproperties.Filter]interface{}) (string, error) { mc.RLock() defer mc.RUnlock() if val, ok := mc.globalValues[name]; ok { return val.(string), nil } return name.DefaultString(), NotFoundError } func (mc *inMemoryClient) GetMapValue(name dynamicproperties.MapKey, filters map[dynamicproperties.Filter]interface{}) (map[string]interface{}, error) { mc.RLock() defer mc.RUnlock() if val, ok := mc.globalValues[name]; ok { return val.(map[string]interface{}), nil } return name.DefaultMap(), NotFoundError } func (mc *inMemoryClient) GetDurationValue(name dynamicproperties.DurationKey, filters map[dynamicproperties.Filter]interface{}) (time.Duration, error) { mc.RLock() defer mc.RUnlock() if val, ok := mc.globalValues[name]; ok { return val.(time.Duration), nil } return name.DefaultDuration(), NotFoundError } func (mc *inMemoryClient) GetListValue(name dynamicproperties.ListKey, filters map[dynamicproperties.Filter]interface{}) ([]interface{}, error) { mc.RLock() defer mc.RUnlock() if val, ok := mc.globalValues[name]; ok { return val.([]interface{}), nil } return name.DefaultList(), NotFoundError } func (mc *inMemoryClient) UpdateValue(key dynamicproperties.Key, value interface{}) error { if err := dynamicproperties.ValidateKeyValuePair(key, value); err != nil { return err } mc.SetValue(key, value) return nil } func (mc *inMemoryClient) RestoreValue(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) error { return errors.New("not supported for in-memory client") } func (mc *inMemoryClient) ListValue(name dynamicproperties.Key) ([]*types.DynamicConfigEntry, error) { return nil, errors.New("not supported for in-memory client") } ================================================ FILE: common/dynamicconfig/nopClient.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamicconfig import ( "errors" "time" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) // nopClient is a dummy implements of dynamicconfig Client interface, all operations will always return default values. type nopClient struct{} func (mc *nopClient) GetValue(name dynamicproperties.Key) (interface{}, error) { return nil, NotFoundError } func (mc *nopClient) GetValueWithFilters(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) (interface{}, error) { return nil, NotFoundError } func (mc *nopClient) GetIntValue(name dynamicproperties.IntKey, filters map[dynamicproperties.Filter]interface{}) (int, error) { return name.DefaultInt(), NotFoundError } func (mc *nopClient) GetFloatValue(name dynamicproperties.FloatKey, filters map[dynamicproperties.Filter]interface{}) (float64, error) { return name.DefaultFloat(), NotFoundError } func (mc *nopClient) GetBoolValue(name dynamicproperties.BoolKey, filters map[dynamicproperties.Filter]interface{}) (bool, error) { if filters[dynamicproperties.DomainName] == "TestRawHistoryDomain" { return true, NotFoundError } return name.DefaultBool(), NotFoundError } func (mc *nopClient) GetStringValue(name dynamicproperties.StringKey, filters map[dynamicproperties.Filter]interface{}) (string, error) { return name.DefaultString(), NotFoundError } func (mc *nopClient) GetMapValue(name dynamicproperties.MapKey, filters map[dynamicproperties.Filter]interface{}) (map[string]interface{}, error) { return name.DefaultMap(), NotFoundError } func (mc *nopClient) GetDurationValue(name dynamicproperties.DurationKey, filters map[dynamicproperties.Filter]interface{}) (time.Duration, error) { return name.DefaultDuration(), NotFoundError } func (mc *nopClient) GetListValue(name dynamicproperties.ListKey, filters map[dynamicproperties.Filter]interface{}) ([]interface{}, error) { return name.DefaultList(), NotFoundError } func (mc *nopClient) UpdateValue(name dynamicproperties.Key, value interface{}) error { return errors.New("not supported for nop client") } func (mc *nopClient) RestoreValue(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) error { return errors.New("not supported for nop client") } func (mc *nopClient) ListValue(name dynamicproperties.Key) ([]*types.DynamicConfigEntry, error) { return nil, errors.New("not supported for nop client") } // NewNopClient creates a nop client func NewNopClient() Client { return &nopClient{} } // NewNopCollection creates a new nop collection func NewNopCollection() *Collection { return NewCollection(&nopClient{}, log.NewNoop()) } ================================================ FILE: common/dynamicconfig/quotas/dynamicratelimiterfactory.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package quotas import ( "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/quotas" ) // NewSimpleDynamicRateLimiterFactory creates a new LimiterFactory which creates // a new DynamicRateLimiter for each domain, the RPS for the DynamicRateLimiter is given by the dynamic config func NewSimpleDynamicRateLimiterFactory(rps dynamicproperties.IntPropertyFnWithDomainFilter) quotas.LimiterFactory { return dynamicRateLimiterFactory{ rps: rps, } } type dynamicRateLimiterFactory struct { rps dynamicproperties.IntPropertyFnWithDomainFilter } // GetLimiter returns a new Limiter for the given domain func (f dynamicRateLimiterFactory) GetLimiter(domain string) quotas.Limiter { return quotas.NewDynamicRateLimiter(func() float64 { return float64(f.rps(domain)) }) } ================================================ FILE: common/dynamicconfig/quotas/dynamicratelimiterfactory_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package quotas import ( "testing" "github.com/stretchr/testify/assert" "golang.org/x/time/rate" ) func TestNewSimpleDynamicRateLimiterFactory(t *testing.T) { const _testDomain = "test-domain" factory := NewSimpleDynamicRateLimiterFactory(func(domain string) int { assert.Equal(t, _testDomain, domain) return 100 }) limiter := factory.GetLimiter(_testDomain) assert.Equal(t, rate.Limit(100), limiter.Limit()) } ================================================ FILE: common/dynamicconfig/quotas/fallbackdynamicratelimiterfactory.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package quotas import ( "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/quotas" ) // NewFallbackDynamicRateLimiterFactory is used to create a Limiter for a given domain // the created Limiter will use the primary dynamic config if it is set // otherwise it will use the secondary dynamic config func NewFallbackDynamicRateLimiterFactory( primary dynamicproperties.IntPropertyFnWithDomainFilter, secondary dynamicproperties.IntPropertyFn, ) quotas.LimiterFactory { return fallbackDynamicRateLimiterFactory{ primary: primary, secondary: secondary, } } type fallbackDynamicRateLimiterFactory struct { primary dynamicproperties.IntPropertyFnWithDomainFilter // secondary is used when primary is not set secondary dynamicproperties.IntPropertyFn } // GetLimiter returns a new Limiter for the given domain func (f fallbackDynamicRateLimiterFactory) GetLimiter(domain string) quotas.Limiter { return quotas.NewDynamicRateLimiter(func() float64 { if primaryLimit := f.primary(domain); primaryLimit > 0 { return float64(primaryLimit) } return float64(f.secondary()) }) } ================================================ FILE: common/dynamicconfig/quotas/fallbackdynamicratelimiterfactory_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package quotas import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) func TestNewFallbackDynamicRateLimiterFactory(t *testing.T) { factory := NewFallbackDynamicRateLimiterFactory( func(string) int { return 2 }, func(opts ...dynamicproperties.FilterOption) int { return 100 }, ) limiter := factory.GetLimiter("TestDomainName") // The limiter should accept 2 requests per second assert.Equal(t, true, limiter.Allow()) assert.Equal(t, true, limiter.Allow()) assert.Equal(t, false, limiter.Allow()) } func TestNewFallbackDynamicRateLimiterFactoryFallback(t *testing.T) { factory := NewFallbackDynamicRateLimiterFactory( func(string) int { return 0 }, func(opts ...dynamicproperties.FilterOption) int { return 2 }, ) limiter := factory.GetLimiter("TestDomainName") // The limiter should accept 2 requests per second assert.Equal(t, true, limiter.Allow()) assert.Equal(t, true, limiter.Allow()) assert.Equal(t, false, limiter.Allow()) } ================================================ FILE: common/elasticsearch/bulk/backoff.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package bulk import ( "math" "math/rand" "time" ) // exponentialBackoff implements the simple exponential backoff described by // Douglas Thain at http://dthain.blogspot.de/2009/02/exponential-backoff-in-distributed.html. // TODO https://github.com/uber/cadence/issues/3676 type exponentialBackoff struct { initialTimeout float64 // initial timeout (in msec) factor float64 // exponential factor (e.g. 2) maximumTimeout float64 // maximum timeout (in msec) } var _ GenericBackoff = (*exponentialBackoff)(nil) // NewExponentialBackoff returns a exponentialBackoff backoff policy. // Use initialTimeout to set the first/minimal interval // and maxTimeout to set the maximum wait interval. func NewExponentialBackoff(initialTimeout, maxTimeout time.Duration) GenericBackoff { return &exponentialBackoff{ initialTimeout: float64(int64(initialTimeout / time.Millisecond)), factor: 2.0, maximumTimeout: float64(int64(maxTimeout / time.Millisecond)), } } // Next implements BackoffFunc for exponentialBackoff. func (b *exponentialBackoff) Next(retry int) (time.Duration, bool) { r := 1.0 + rand.Float64() // random number in [1..2] m := math.Min(r*b.initialTimeout*math.Pow(b.factor, float64(retry)), b.maximumTimeout) if m >= b.maximumTimeout { return 0, false } d := time.Duration(int64(m)) * time.Millisecond return d, true } ================================================ FILE: common/elasticsearch/bulk/bulk.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package bulk import ( "context" "time" ) const UnknownStatusCode = -1 type GenericBulkableRequestType int const ( BulkableIndexRequest GenericBulkableRequestType = iota BulkableDeleteRequest BulkableCreateRequest ) type ( // GenericBulkProcessor is a bulk processor GenericBulkProcessor interface { Start(ctx context.Context) error Stop() error Close() error Add(request *GenericBulkableAddRequest) Flush() error } // BulkProcessorParameters holds all required and optional parameters for executing bulk service BulkProcessorParameters struct { Name string NumOfWorkers int BulkActions int BulkSize int FlushInterval time.Duration Backoff GenericBackoff BeforeFunc GenericBulkBeforeFunc AfterFunc GenericBulkAfterFunc } // GenericBackoff allows callers to implement their own Backoff strategy. GenericBackoff interface { // Next implements a BackoffFunc. Next(retry int) (time.Duration, bool) } // GenericBulkBeforeFunc defines the signature of callbacks that are executed // before a commit to Elasticsearch. GenericBulkBeforeFunc func(executionId int64, requests []GenericBulkableRequest) // GenericBulkAfterFunc defines the signature of callbacks that are executed // after a commit to Elasticsearch. The err parameter signals an error. GenericBulkAfterFunc func(executionId int64, requests []GenericBulkableRequest, response *GenericBulkResponse, err *GenericError) // GenericBulkableRequest is a generic interface to bulkable requests. GenericBulkableRequest interface { String() string Source() ([]string, error) } // GenericBulkableAddRequest a struct to hold a bulk request GenericBulkableAddRequest struct { Index string Type string ID string VersionType string Version int64 // request types can be index, delete or create RequestType GenericBulkableRequestType // should be nil if IsDelete is true Doc interface{} } // GenericBulkResponse is generic struct of bulk response GenericBulkResponse struct { Took int `json:"took,omitempty"` Errors bool `json:"errors,omitempty"` Items []map[string]*GenericBulkResponseItem `json:"items,omitempty"` } // GenericError encapsulates error status and details returned from Elasticsearch. GenericError struct { Status int `json:"status"` Details error `json:"error,omitempty"` } // GenericBulkResponseItem is the result of a single bulk request. GenericBulkResponseItem struct { Index string `json:"_index,omitempty"` Type string `json:"_type,omitempty"` ID string `json:"_id,omitempty"` Version int64 `json:"_version,omitempty"` Result string `json:"result,omitempty"` Status int `json:"status,omitempty"` // the error details Error interface{} } ) ================================================ FILE: common/elasticsearch/bulk/bulk_delete_request.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package bulk import ( "encoding/json" "fmt" "strings" ) var _ GenericBulkableRequest = (*BulkDeleteRequest)(nil) // BulkDeleteRequest is a request to remove a document from Elasticsearch. // // See https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-bulk.html // for details. type BulkDeleteRequest struct { index string typ string id string routing string version int64 versionType string // default is "internal" source []string } type bulkDeleteRequestCommand map[string]bulkDeleteRequestCommandOp type bulkDeleteRequestCommandOp struct { Index string `json:"_index,omitempty"` ID string `json:"_id,omitempty"` Version int64 `json:"version,omitempty"` VersionType string `json:"version_type,omitempty"` } // NewBulkDeleteRequest returns a new BulkDeleteRequest. func NewBulkDeleteRequest() *BulkDeleteRequest { return &BulkDeleteRequest{} } // Index specifies the Elasticsearch index to use for this delete request. // If unspecified, the index set on the BulkService will be used. func (r *BulkDeleteRequest) Index(index string) *BulkDeleteRequest { r.index = index r.source = nil return r } // Type specifies the Elasticsearch type to use for this delete request. // If unspecified, the type set on the BulkService will be used. func (r *BulkDeleteRequest) Type(typ string) *BulkDeleteRequest { r.typ = typ r.source = nil return r } // ID specifies the identifier of the document to delete. func (r *BulkDeleteRequest) ID(id string) *BulkDeleteRequest { r.id = id r.source = nil return r } // Version indicates the version to be deleted as part of an optimistic // concurrency model. func (r *BulkDeleteRequest) Version(version int64) *BulkDeleteRequest { r.version = version r.source = nil return r } // VersionType can be "internal" (default), "external", "external_gte", // or "external_gt". func (r *BulkDeleteRequest) VersionType(versionType string) *BulkDeleteRequest { r.versionType = versionType r.source = nil return r } // String returns the on-wire representation of the delete request, // concatenated as a single string. func (r *BulkDeleteRequest) String() string { lines, err := r.Source() if err != nil { return fmt.Sprintf("error: %v", err) } return strings.Join(lines, "\n") } // Source returns the on-wire representation of the delete request, // split into an action-and-meta-data line and an (optional) source line. // See https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-bulk.html // for details. func (r *BulkDeleteRequest) Source() ([]string, error) { if r.source != nil { return r.source, nil } command := bulkDeleteRequestCommand{ "delete": bulkDeleteRequestCommandOp{ Index: r.index, ID: r.id, Version: r.version, VersionType: r.versionType, }, } var err error var body []byte body, err = json.Marshal(command) if err != nil { return nil, err } lines := []string{string(body)} r.source = lines return lines, nil } ================================================ FILE: common/elasticsearch/bulk/bulk_index_request.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package bulk import ( "encoding/json" "fmt" "strings" ) var _ GenericBulkableRequest = (*BulkIndexRequest)(nil) type BulkIndexRequest struct { index string typ string id string opType string version *int64 versionType string // default is "internal" doc interface{} source []string } type bulkIndexRequestCommand map[string]bulkIndexRequestCommandOp type bulkIndexRequestCommandOp struct { Index string `json:"_index,omitempty"` ID string `json:"_id,omitempty"` Version *int64 `json:"version,omitempty"` VersionType string `json:"version_type,omitempty"` } // NewBulkIndexRequest returns a new BulkIndexRequest. // The operation type is "index" by default. func NewBulkIndexRequest() *BulkIndexRequest { return &BulkIndexRequest{ opType: "index", } } // Index specifies the Elasticsearch index to use for this index request. // If unspecified, the index set on the BulkService will be used. func (r *BulkIndexRequest) Index(index string) *BulkIndexRequest { r.index = index r.source = nil return r } // Type specifies the Elasticsearch type to use for this index request. // If unspecified, the type set on the BulkService will be used. func (r *BulkIndexRequest) Type(typ string) *BulkIndexRequest { r.typ = typ r.source = nil return r } // ID specifies the identifier of the document to index. func (r *BulkIndexRequest) ID(id string) *BulkIndexRequest { r.id = id r.source = nil return r } // OpType specifies if this request should follow create-only or upsert // behavior. This follows the OpType of the standard document index API. // See https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-index_.html#operation-type // for details. func (r *BulkIndexRequest) OpType(opType string) *BulkIndexRequest { r.opType = opType r.source = nil return r } // Version indicates the version of the document as part of an optimistic // concurrency model. func (r *BulkIndexRequest) Version(version int64) *BulkIndexRequest { r.version = &version r.source = nil return r } // VersionType specifies how versions are created. It can be e.g. internal, // external, external_gte, or force. // // See https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-index_.html#index-versioning // for details. func (r *BulkIndexRequest) VersionType(versionType string) *BulkIndexRequest { r.versionType = versionType r.source = nil return r } // Doc specifies the document to index. func (r *BulkIndexRequest) Doc(doc interface{}) *BulkIndexRequest { r.doc = doc r.source = nil return r } // String returns the on-wire representation of the index request, // concatenated as a single string. func (r *BulkIndexRequest) String() string { lines, err := r.Source() if err != nil { return fmt.Sprintf("error: %v", err) } return strings.Join(lines, "\n") } // Source returns the on-wire representation of the index request, // split into an action-and-meta-data line and an (optional) source line. // See https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-bulk.html // for details. func (r *BulkIndexRequest) Source() ([]string, error) { // { "index" : { "_index" : "test", "_type" : "type1", "_id" : "1" } } // { "field1" : "value1" } if r.source != nil { return r.source, nil } lines := make([]string, 2) indexCommand := bulkIndexRequestCommandOp{ Index: r.index, ID: r.id, Version: r.version, VersionType: r.versionType, } command := bulkIndexRequestCommand{ r.opType: indexCommand, } var err error var body []byte body, err = json.Marshal(command) if err != nil { return nil, err } lines[0] = string(body) // "field1" ... if r.doc != nil { body, err := json.Marshal(r.doc) if err != nil { return nil, err } lines[1] = string(body) } else { lines[1] = "{}" } r.source = lines return lines, nil } ================================================ FILE: common/elasticsearch/bulk/bulk_update_request.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package bulk import ( "encoding/json" "fmt" "strings" ) var _ GenericBulkableRequest = (*BulkUpdateRequest)(nil) // BulkUpdateRequest is a request to update a document in Elasticsearch. // // See https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-bulk.html // for details. type BulkUpdateRequest struct { index string id string version int64 versionType string // default is "internal" doc interface{} source []string } type bulkUpdateRequestCommand map[string]bulkUpdateRequestCommandOp type bulkUpdateRequestCommandOp struct { Index string `json:"_index,omitempty"` ID string `json:"_id,omitempty"` Version int64 `json:"version,omitempty"` VersionType string `json:"version_type,omitempty"` } type bulkUpdateRequestCommandData struct { Doc interface{} `json:"doc,omitempty"` Upsert interface{} `json:"upsert,omitempty"` Source *bool `json:"_source,omitempty"` } // NewBulkUpdateRequest returns a new BulkUpdateRequest. func NewBulkUpdateRequest() *BulkUpdateRequest { return &BulkUpdateRequest{} } // Index specifies the Elasticsearch index to use for this update request. // If unspecified, the index set on the BulkService will be used. func (r *BulkUpdateRequest) Index(index string) *BulkUpdateRequest { r.index = index r.source = nil return r } // ID specifies the identifier of the document to update. func (r *BulkUpdateRequest) ID(id string) *BulkUpdateRequest { r.id = id r.source = nil return r } // Version indicates the version of the document as part of an optimistic // concurrency model. func (r *BulkUpdateRequest) Version(version int64) *BulkUpdateRequest { r.version = version r.source = nil return r } // VersionType can be "internal" (default), "external", "external_gte", // or "external_gt". func (r *BulkUpdateRequest) VersionType(versionType string) *BulkUpdateRequest { r.versionType = versionType r.source = nil return r } // Doc specifies the updated document. func (r *BulkUpdateRequest) Doc(doc interface{}) *BulkUpdateRequest { r.doc = doc r.source = nil return r } // String returns the on-wire representation of the update request, // concatenated as a single string. func (r *BulkUpdateRequest) String() string { lines, err := r.Source() if err != nil { return fmt.Sprintf("error: %v", err) } return strings.Join(lines, "\n") } // Source returns the on-wire representation of the update request, // split into an action-and-meta-data line and an (optional) source line. // See https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-bulk.html // for details. func (r *BulkUpdateRequest) Source() ([]string, error) { // { "update" : { "_index" : "test", "_type" : "type1", "_id" : "1", ... } } // { "doc" : { "field1" : "value1", ... } } // or // { "update" : { "_index" : "test", "_type" : "type1", "_id" : "1", ... } } // { "script" : { ... } } if r.source != nil { return r.source, nil } lines := make([]string, 2) // "update" ... updateCommand := bulkUpdateRequestCommandOp{ Index: r.index, ID: r.id, Version: r.version, VersionType: r.versionType, } command := bulkUpdateRequestCommand{ "update": updateCommand, } var err error var body []byte body, err = json.Marshal(command) if err != nil { return nil, err } lines[0] = string(body) // 2nd line: {"doc" : { ... }} or {"script": {...}} data := bulkUpdateRequestCommandData{ Doc: r.doc, } // encoding/json body, err = json.Marshal(data) if err != nil { return nil, err } lines[1] = string(body) r.source = lines return lines, nil } ================================================ FILE: common/elasticsearch/bulk/mocks/GenericBulkProcessor.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Code generated by mockery v0.0.0-dev. DO NOT EDIT. package mocks import ( context "context" mock "github.com/stretchr/testify/mock" bulk "github.com/uber/cadence/common/elasticsearch/bulk" ) // GenericBulkProcessor is an autogenerated mock type for the GenericBulkProcessor type type GenericBulkProcessor struct { mock.Mock } // Add provides a mock function with given fields: request func (_m *GenericBulkProcessor) Add(request *bulk.GenericBulkableAddRequest) { _m.Called(request) } // Close provides a mock function with given fields: func (_m *GenericBulkProcessor) Close() error { ret := _m.Called() var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() } else { r0 = ret.Error(0) } return r0 } // Flush provides a mock function with given fields: func (_m *GenericBulkProcessor) Flush() error { ret := _m.Called() var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() } else { r0 = ret.Error(0) } return r0 } // Start provides a mock function with given fields: ctx func (_m *GenericBulkProcessor) Start(ctx context.Context) error { ret := _m.Called(ctx) var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) } else { r0 = ret.Error(0) } return r0 } // Stop provides a mock function with given fields: func (_m *GenericBulkProcessor) Stop() error { ret := _m.Called() var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() } else { r0 = ret.Error(0) } return r0 } ================================================ FILE: common/elasticsearch/bulk/mocks/GenericBulkableRequest.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import mock "github.com/stretchr/testify/mock" // GenericBulkableRequest is an autogenerated mock type for the GenericBulkableRequest type type GenericBulkableRequest struct { mock.Mock } // Source provides a mock function with given fields: func (_m *GenericBulkableRequest) Source() ([]string, error) { ret := _m.Called() var r0 []string if rf, ok := ret.Get(0).(func() []string); ok { r0 = rf() } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]string) } } var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { r1 = ret.Error(1) } return r0, r1 } // String provides a mock function with given fields: func (_m *GenericBulkableRequest) String() string { ret := _m.Called() var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } return r0 } ================================================ FILE: common/elasticsearch/client/client.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package client import ( "context" "encoding/json" "github.com/uber/cadence/common/elasticsearch/bulk" ) // Client is a generic ES client implementation. // This interface allows to use different Elasticsearch and OpenSearch versions // without exposing implementation details and structs type Client interface { // ClearScroll clears the search context and results for a scrolling search. ClearScroll(ctx context.Context, scrollID string) error // Count returns number of document matches by given query Count(ctx context.Context, index, body string) (int64, error) // CreateIndex creates index with given name CreateIndex(ctx context.Context, index string) error // IsNotFoundError checks if error is a "not found" IsNotFoundError(err error) bool // PutMapping updates Client with new field mapping PutMapping(ctx context.Context, index, body string) error // RunBulkProcessor starts bulk indexing processor // @TODO consider to extract Bulk Processor as a separate entity RunBulkProcessor(ctx context.Context, p *bulk.BulkProcessorParameters) (bulk.GenericBulkProcessor, error) // Scroll retrieves the next batch of results for a scrolling search. Scroll(ctx context.Context, index, body, scrollID string) (*Response, error) // Search returns Elasticsearch hit bytes and additional metadata Search(ctx context.Context, index, body string) (*Response, error) } // Response is used to pass data retrieved from Elasticsearch/OpenSearch to upper layer type Response struct { TookInMillis int64 TotalHits int64 Hits *SearchHits Aggregations map[string]json.RawMessage Sort []interface{} ScrollID string } // SearchHits specifies the list of search hits. type SearchHits struct { TotalHits *TotalHits // total number of hits found Hits []*SearchHit // the actual hits returned } // TotalHits specifies total number of hits and its relation type TotalHits struct { Value int64 // value of the total hit count } // SearchHit is a single hit. type SearchHit struct { Index string // index name ID string // external or internal Sort []interface{} // sort information Source json.RawMessage // stored document source } ================================================ FILE: common/elasticsearch/client/os2/client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package os2 import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "net/http" "strings" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/opensearch-project/opensearch-go/v4" osapi "github.com/opensearch-project/opensearch-go/v4/opensearchapi" "github.com/opensearch-project/opensearch-go/v4/opensearchtransport" requestsigner "github.com/opensearch-project/opensearch-go/v4/signer/aws" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/elasticsearch/client" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) type ( // OS2 implements Client OS2 struct { client *osapi.Client logger log.Logger decoder *NumberDecoder } errorDetails struct { Type string `json:"type"` Reason string `json:"reason"` Index string `json:"index,omitempty"` } // response holds data retrieved from OpenSearch response struct { TookInMillis int64 `json:"took,omitempty"` Hits *searchHits Aggregations map[string]json.RawMessage `json:"aggregations,omitempty"` Sort []interface{} `json:"sort,omitempty"` // sort information ScrollID string `json:"_scroll_id,omitempty"` } // searchHits specifies the list of search hits. searchHits struct { TotalHits *totalHits `json:"total,omitempty"` // total number of hits found Hits []*searchHit `json:"hits,omitempty"` // the actual hits returned } // totalHits specifies total number of hits and its relation totalHits struct { Value int64 `json:"value"` // value of the total hit count } // searchHit is a single hit. searchHit struct { Index string `json:"_index,omitempty"` // index name ID string `json:"_id,omitempty"` // external or internal Sort []any `json:"sort,omitempty"` // sort information Source json.RawMessage `json:"_source,omitempty"` // stored document source } convertLogger struct { logger log.Logger } ) var _ opensearchtransport.Logger = (*convertLogger)(nil) func (c convertLogger) LogRoundTrip(request *http.Request, h *http.Response, err error, t time.Time, duration time.Duration) error { // req and resp bodies must not be touched because we have not enabled them, and doing so might affect the request if err != nil { // possible future enhancement: bulk failures are MUCH more relevant than query failures like timeouts. // this can probably be figured out by checking the request URL, if the volume proves too high. c.logger.Error( "opensearch request failed", tag.Error(err), tag.Dynamic("request_uri", request.URL.String()), tag.Dynamic("request_method", request.Method), tag.Dynamic("response_code", h.StatusCode), tag.Duration(duration), ) } return nil } func (c convertLogger) RequestBodyEnabled() bool { return false } func (c convertLogger) ResponseBodyEnabled() bool { return false } // NewClient returns a new implementation of GenericClient func NewClient( connectConfig *config.ElasticSearchConfig, logger log.Logger, tlsClient *http.Client, ) (*OS2, error) { osconfig := osapi.Config{ Client: opensearch.Config{ Addresses: []string{connectConfig.URL.String()}, MaxRetries: 5, RetryBackoff: func(i int) time.Duration { return time.Duration(i) * 100 * time.Millisecond }, Logger: &convertLogger{logger: logger}, }, } if len(connectConfig.CustomHeaders) > 0 { osconfig.Client.Header = http.Header{} for key, value := range connectConfig.CustomHeaders { osconfig.Client.Header.Set(key, value) } } // DiscoverNodesOnStart is false by default. Turn it on only when disable sniff is set to False in ES config if !connectConfig.DisableSniff { osconfig.Client.DiscoverNodesOnStart = true } if connectConfig.AWSSigning.Enable { credentials, region, err := connectConfig.AWSSigning.GetCredentials() if err != nil { return nil, fmt.Errorf("getting aws credentials: %w", err) } sessionOptions := session.Options{ Config: aws.Config{ Region: region, Credentials: credentials, }, } signer, err := requestsigner.NewSigner(sessionOptions) if err != nil { return nil, fmt.Errorf("creating aws signer: %w", err) } osconfig.Client.Signer = signer } if tlsClient != nil { osconfig.Client.Transport = tlsClient.Transport logger.Info("Using TLS client") } osClient, err := osapi.NewClient(osconfig) if err != nil { return nil, fmt.Errorf("creating OpenSearch client: %w", err) } // initial health check ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() resp, err := osClient.Ping(ctx, nil /*PingReq*/) if err != nil { return nil, fmt.Errorf("OpenSearch client unable to ping: %w", err) } if resp.IsError() { return nil, fmt.Errorf("OpenSearch client received error on ping: %s", resp) } return &OS2{ client: osClient, logger: logger, decoder: &NumberDecoder{}, }, nil } func (c *OS2) IsNotFoundError(err error) bool { var clientErr *opensearch.StructError if errors.As(err, &clientErr) { return clientErr.Status == http.StatusNotFound } return false } func (c *OS2) PutMapping(ctx context.Context, index, body string) error { req := osapi.MappingPutReq{ Indices: []string{index}, Body: strings.NewReader(body), } _, err := c.client.Indices.Mapping.Put(ctx, req) if err != nil { return fmt.Errorf("OpenSearch PutMapping: %w", err) } return nil } func (c *OS2) CreateIndex(ctx context.Context, index string) error { req := osapi.IndicesCreateReq{ Index: index, } _, err := c.client.Indices.Create(ctx, req) if err != nil { return err } return nil } func (c *OS2) Count(ctx context.Context, index, query string) (int64, error) { req := &osapi.IndicesCountReq{ Indices: []string{index}, Body: strings.NewReader(query), } resp, err := c.client.Indices.Count(ctx, req) if err != nil { return 0, fmt.Errorf("OpenSearch Count: %w", err) } return int64(resp.Count), nil } func (c *OS2) ClearScroll(ctx context.Context, scrollID string) error { _, err := c.client.Scroll.Delete(ctx, osapi.ScrollDeleteReq{ ScrollIDs: []string{scrollID}, }) if err != nil { return fmt.Errorf("OpenSearch ClearScroll: %w", err) } return nil } func (c *OS2) Scroll(ctx context.Context, index, body, scrollID string) (*client.Response, error) { var scrollResp *osapi.ScrollGetResp var searchResp *osapi.SearchResp var osResponse response var respBody io.ReadCloser var searchErr error // handle scroll id get call if len(scrollID) != 0 { scrollResp, searchErr = c.client.Scroll.Get(ctx, osapi.ScrollGetReq{ ScrollID: scrollID, Params: osapi.ScrollGetParams{ Scroll: time.Minute, // do not set scroll ID here as it will be added to the params and scroll ID can be excessively long }, }) if searchErr != nil { return nil, fmt.Errorf("opensearch scroll search error: %w", searchErr) } if scrollResp.Inspect().Response == nil { return nil, fmt.Errorf("OpenSearch scroll search response nil") } respBody = scrollResp.Inspect().Response.Body } else { // when scrollID is not passed, it is normal search request searchResp, searchErr = c.client.Search(ctx, &osapi.SearchReq{ Indices: []string{index}, Body: strings.NewReader(body), Params: osapi.SearchParams{ Scroll: time.Minute, }, }) if searchErr != nil { return nil, fmt.Errorf("opensearch scroll search error: %w", searchErr) } if searchResp.Inspect().Response == nil { return nil, fmt.Errorf("OpenSearch scroll search response nil") } respBody = searchResp.Inspect().Response.Body } bodyBytes, err := io.ReadAll(respBody) if err != nil { return nil, fmt.Errorf("failed to read scroll search response body: %w", err) } if err := c.decoder.Decode(bytes.NewReader(bodyBytes), &osResponse); err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("decoding OpenSearch scroll response to Response: %w", err) } var totalHits int64 var hits []*client.SearchHit // no more hits if osResponse.Hits == nil || len(osResponse.Hits.Hits) == 0 { return &client.Response{ ScrollID: osResponse.ScrollID, TookInMillis: osResponse.TookInMillis, Hits: &client.SearchHits{Hits: hits}, }, io.EOF } for _, h := range osResponse.Hits.Hits { hits = append(hits, &client.SearchHit{Source: h.Source}) } if osResponse.Hits.TotalHits != nil { totalHits = osResponse.Hits.TotalHits.Value } return &client.Response{ TookInMillis: osResponse.TookInMillis, TotalHits: totalHits, Hits: &client.SearchHits{Hits: hits}, Aggregations: osResponse.Aggregations, ScrollID: osResponse.ScrollID, }, nil } func (c *OS2) Search(ctx context.Context, index, body string) (*client.Response, error) { resp, err := c.client.Search(ctx, &osapi.SearchReq{ Indices: []string{index}, Body: strings.NewReader(body), }) if err != nil { return nil, fmt.Errorf("OpenSearch Search error: %w", err) } if resp.Inspect().Response == nil { return nil, fmt.Errorf("OpenSearch search response nil") } var osResponse response bodyBytes, err := io.ReadAll(resp.Inspect().Response.Body) if err != nil { return nil, fmt.Errorf("failed to read OpenSearch scroll search response body: %w", err) } if err := c.decoder.Decode(bytes.NewReader(bodyBytes), &osResponse); err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("decoding Opensearch result to Response: %w", err) } var hits []*client.SearchHit var sort []interface{} var totalHits int64 if osResponse.Hits != nil && osResponse.Hits.TotalHits != nil { totalHits = osResponse.Hits.TotalHits.Value for _, h := range osResponse.Hits.Hits { sort = h.Sort hits = append(hits, &client.SearchHit{Source: h.Source}) } } return &client.Response{ TookInMillis: osResponse.TookInMillis, TotalHits: totalHits, Hits: &client.SearchHits{Hits: hits}, Aggregations: osResponse.Aggregations, Sort: sort, }, nil } ================================================ FILE: common/elasticsearch/client/os2/client_bulk.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package os2 import ( "bytes" "context" "encoding/json" "io" "github.com/opensearch-project/opensearch-go/v4/opensearchapi" "github.com/opensearch-project/opensearch-go/v4/opensearchutil" "github.com/uber/cadence/common/elasticsearch/bulk" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) // metricsRequest is used for a callback/metrics needs var metricsRequest = []bulk.GenericBulkableRequest{&bulk.BulkIndexRequest{}} type bulkProcessor struct { processor opensearchutil.BulkIndexer before bulk.GenericBulkBeforeFunc after bulk.GenericBulkAfterFunc logger log.Logger } func (v *bulkProcessor) Start(ctx context.Context) error { return nil } func (v *bulkProcessor) Stop() error { return v.Close() } func (v *bulkProcessor) Close() error { return v.processor.Close(context.Background()) } func (v *bulkProcessor) Add(request *bulk.GenericBulkableAddRequest) { req := opensearchutil.BulkIndexerItem{ Index: request.Index, DocumentID: request.ID, } var callBackRequest bulk.GenericBulkableRequest switch request.RequestType { case bulk.BulkableDeleteRequest: req.Action = "delete" callBackRequest = bulk.NewBulkDeleteRequest(). ID(request.ID). Index(request.Index) case bulk.BulkableIndexRequest: body, err := json.Marshal(request.Doc) if err != nil { v.logger.Error("marshal bulk index request doc", tag.Error(err)) return } req.Action = "index" req.Version = &request.Version req.VersionType = &request.VersionType req.Body = bytes.NewReader(body) callBackRequest = bulk.NewBulkIndexRequest(). ID(request.ID). Index(request.Index). Version(request.Version). VersionType(request.VersionType).Doc(request.Doc) case bulk.BulkableCreateRequest: body, err := json.Marshal(request.Doc) if err != nil { v.logger.Error("marshal bulk create request doc", tag.Error(err)) return } versionType := "internal" req.Action = "create" req.VersionType = &versionType req.Body = bytes.NewReader(body) callBackRequest = bulk.NewBulkIndexRequest().ID(request.ID). Index(request.Index). Version(request.Version). VersionType(versionType).Doc(request.Doc) } req.OnFailure = func(ctx context.Context, item opensearchutil.BulkIndexerItem, res opensearchapi.BulkRespItem, err error) { v.processCallback(0, callBackRequest, req, res, false, err) } req.OnSuccess = func(ctx context.Context, item opensearchutil.BulkIndexerItem, res opensearchapi.BulkRespItem) { v.processCallback(0, callBackRequest, req, res, true, nil) } if err := v.processor.Add(context.Background(), req); err != nil { v.logger.Error("adding bulk request to OpenSearch", tag.Error(err)) } } // processCallback processes both success and failure scenarios. func (v *bulkProcessor) processCallback(index int64, callBackRequest bulk.GenericBulkableRequest, req opensearchutil.BulkIndexerItem, res opensearchapi.BulkRespItem, onSuccess bool, err error) { v.before(0, metricsRequest) gr := []bulk.GenericBulkableRequest{callBackRequest} it := bulk.GenericBulkResponseItem{ Index: res.Index, ID: res.ID, Version: int64(res.Version), Status: res.Status, Error: res.Error, } if onSuccess { gbr := bulk.GenericBulkResponse{ Took: 0, Errors: false, Items: []map[string]*bulk.GenericBulkResponseItem{ {req.Action: &it}, }, } v.after(0, gr, &gbr, nil /*No errors here*/) } else { gbr := bulk.GenericBulkResponse{ Took: 0, Errors: res.Error != nil && (res.Error.Type != "" || res.Status > 201), Items: []map[string]*bulk.GenericBulkResponseItem{ {req.Action: &it}, }, } ge := bulk.GenericError{ Status: res.Status, Details: err, } v.after(0, gr, &gbr, &ge) } } func (v *bulkProcessor) Flush() error { // Opensearch client doesn't provide flush method return nil } func (c *OS2) RunBulkProcessor(_ context.Context, parameters *bulk.BulkProcessorParameters) (bulk.GenericBulkProcessor, error) { processor, err := opensearchutil.NewBulkIndexer(opensearchutil.BulkIndexerConfig{ Client: c.client, FlushInterval: parameters.FlushInterval, NumWorkers: parameters.NumOfWorkers, }) if err != nil { return nil, err } return &bulkProcessor{ processor: processor, before: parameters.BeforeFunc, after: parameters.AfterFunc, logger: c.logger, }, nil } // NumberDecoder uses json.NewDecoder, with UseNumber() enabled, from // the Go standard library to decode JSON data. type NumberDecoder struct{} func (u *NumberDecoder) UnmarshalFromReader(reader io.Reader, response *opensearchapi.BulkRespItem) error { dec := json.NewDecoder(reader) dec.UseNumber() return dec.Decode(response) } func (u *NumberDecoder) Decode(reader io.Reader, v interface{}) error { dec := json.NewDecoder(reader) dec.UseNumber() return dec.Decode(v) } ================================================ FILE: common/elasticsearch/client/os2/client_bulk_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package os2 import ( "bytes" "context" "encoding/json" "errors" "io" "testing" "github.com/opensearch-project/opensearch-go/v4/opensearchapi" "github.com/opensearch-project/opensearch-go/v4/opensearchutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/uber/cadence/common/elasticsearch/bulk" "github.com/uber/cadence/common/log/testlogger" ) func TestStart(t *testing.T) { ctx := context.Background() processor := &bulkProcessor{} if err := processor.Start(ctx); err != nil { t.Errorf("Start() error = %v, wantErr %v", err, nil) } } func TestStopAndClose(t *testing.T) { osClient, testServer := getSecureMockOS2Client(t, nil, false) defer testServer.Close() params := bulk.BulkProcessorParameters{ FlushInterval: 1, NumOfWorkers: 1, } processor, err := osClient.RunBulkProcessor(context.Background(), ¶ms) assert.NoError(t, err) if err := processor.Stop(); err != nil { t.Errorf("Stop() error = %v, wantErr %v", err, nil) } } func TestFlush(t *testing.T) { processor := &bulkProcessor{} if err := processor.Flush(); err != nil { t.Errorf("Flush() error = %v, wantErr %v", err, nil) } } func TestAddDeleteRequest(t *testing.T) { testcases := []struct { name string input bulk.GenericBulkableAddRequest expectErr bool }{ { name: "delete request", input: bulk.GenericBulkableAddRequest{ RequestType: bulk.BulkableDeleteRequest, Index: "test-index", ID: "test-id", Version: 1, VersionType: "external", }, expectErr: false, }, { name: "index request success", input: bulk.GenericBulkableAddRequest{ RequestType: bulk.BulkableIndexRequest, Index: "test-index", ID: "test-id", Doc: map[string]interface{}{"field": "value"}, }, expectErr: false, }, { name: "create request success", input: bulk.GenericBulkableAddRequest{ RequestType: bulk.BulkableCreateRequest, Index: "test-index", ID: "test-id", Doc: map[string]interface{}{"field": "value"}, }, expectErr: false, }, { name: "create request failure", input: bulk.GenericBulkableAddRequest{ RequestType: bulk.BulkableCreateRequest, Index: "test-index", ID: "test-id", Doc: func() {}, }, expectErr: false, }, { name: "index request failure", input: bulk.GenericBulkableAddRequest{ RequestType: bulk.BulkableIndexRequest, Index: "test-index", ID: "test-id", Doc: func() {}, }, expectErr: false, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { osClient, testServer := getSecureMockOS2Client(t, nil, false) defer testServer.Close() params := bulk.BulkProcessorParameters{ FlushInterval: 1, NumOfWorkers: 1, } processor, err := osClient.RunBulkProcessor(context.Background(), ¶ms) assert.NoError(t, err) processor.Add(&tc.input) processor.Stop() }) } } func TestProcessCallback(t *testing.T) { osClient, testServer := getSecureMockOS2Client(t, nil, false) defer testServer.Close() processor, _ := opensearchutil.NewBulkIndexer(opensearchutil.BulkIndexerConfig{ Client: osClient.client, FlushInterval: 1, NumWorkers: 1, }) bulkProcessor := &bulkProcessor{ processor: processor, before: beforeFunc, after: func(int64, []bulk.GenericBulkableRequest, *bulk.GenericBulkResponse, *bulk.GenericError) {}, } callBackRequest := bulk.NewBulkIndexRequest().ID("test-id").Index("test-index").Doc(map[string]interface{}{"field": "value"}) req := opensearchutil.BulkIndexerItem{Index: "test-index", DocumentID: "test-id"} res := opensearchapi.BulkRespItem{Index: "test-index", ID: "test-id", Status: 200} tests := []struct { name string onSuccess bool errorPassed error expectError bool }{ {"Success Callback", true, nil, false}, {"Failure Callback", false, errors.New("test error"), true}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { bulkProcessor.processCallback(0, callBackRequest, req, res, test.onSuccess, test.errorPassed) }) } } func beforeFunc(executionID int64, requests []bulk.GenericBulkableRequest) {} func TestRunBulkProcessor(t *testing.T) { osClient, testServer := getSecureMockOS2Client(t, nil, false) defer testServer.Close() testcases := []struct { name string input bulk.BulkProcessorParameters expectErr bool }{ { name: "normal", input: bulk.BulkProcessorParameters{ FlushInterval: 1, NumOfWorkers: 1, }, expectErr: false, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { processor, err := osClient.RunBulkProcessor(context.Background(), &tc.input) if tc.expectErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.NotNil(t, processor) } }) } } func TestUnmarshalFromReader(t *testing.T) { // test unmarshalFromReader testcases := []struct { name string input string expectErr bool }{ { name: "normal", input: `{"status":200}`, expectErr: false, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { decoder := &NumberDecoder{} reader := createReaderFromString(tc.input) var response opensearchapi.BulkRespItem err := decoder.UnmarshalFromReader(reader, &response) if tc.expectErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestDecode(t *testing.T) { // test decode testcases := []struct { name string input string expectErr bool }{ { name: "normal", input: `{"status":200}`, expectErr: false, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { decoder := &NumberDecoder{} reader := createReaderFromString(tc.input) var response opensearchapi.BulkRespItem err := decoder.Decode(reader, &response) if tc.expectErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } // Function to create a sample reader from a string func createReaderFromString(s string) io.Reader { return bytes.NewBufferString(s) } // MockProcessor is a mock of the processor that will capture the requests. type MockProcessor struct { mock.Mock } func (m *MockProcessor) Add(ctx context.Context, req opensearchutil.BulkIndexerItem) error { args := m.Called(ctx, req) return args.Error(0) } func (m *MockProcessor) Close(ctx context.Context) error { args := m.Called(ctx) return args.Error(0) } func (m *MockProcessor) Stats() opensearchutil.BulkIndexerStats { args := m.Called() return args.Get(0).(opensearchutil.BulkIndexerStats) } func TestBulkProcessorPayload(t *testing.T) { mockProcessor := new(MockProcessor) logger := testlogger.New(t) v := &bulkProcessor{ processor: mockProcessor, logger: logger, } request := &bulk.GenericBulkableAddRequest{ Index: "test-index", ID: "123", Version: 1, VersionType: "external", RequestType: bulk.BulkableIndexRequest, Doc: map[string]interface{}{ "field1": "value1", }, } expectedBody, err := json.Marshal(request.Doc) assert.NoError(t, err) expectedReq := opensearchutil.BulkIndexerItem{ Index: "test-index", DocumentID: "123", Action: "index", Version: &request.Version, VersionType: &request.VersionType, Body: bytes.NewReader(expectedBody), } mockProcessor.On("Add", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { actualReq := args.Get(1).(opensearchutil.BulkIndexerItem) assert.Equal(t, expectedReq.Index, actualReq.Index) assert.Equal(t, expectedReq.DocumentID, actualReq.DocumentID) assert.Equal(t, expectedReq.Action, actualReq.Action) assert.Equal(t, expectedReq.Version, actualReq.Version) assert.Equal(t, expectedReq.VersionType, actualReq.VersionType) var expectedDoc, actualDoc map[string]interface{} assert.NoError(t, json.NewDecoder(expectedReq.Body).Decode(&expectedDoc)) assert.NoError(t, json.NewDecoder(actualReq.Body).Decode(&actualDoc)) assert.Equal(t, expectedDoc, actualDoc) }).Return(nil) v.Add(request) mockProcessor.AssertExpectations(t) } ================================================ FILE: common/elasticsearch/client/os2/client_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package os2 import ( "context" "crypto/tls" "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "net/url" "testing" "github.com/opensearch-project/opensearch-go/v4" osapi "github.com/opensearch-project/opensearch-go/v4/opensearchapi" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" ) type MockTransport struct{} func (m *MockTransport) Perform(req *http.Request) (*http.Response, error) { // Simulate a network or connection error return nil, fmt.Errorf("forced connection error") } func TestNewClient(t *testing.T) { logger := testlogger.New(t) testServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "status": "green" }`)) } else { w.WriteHeader(http.StatusNotFound) } })) defer testServer.Close() url, err := url.Parse(testServer.URL) if err != nil { t.Fatalf("Failed to parse bad URL: %v", err) } badURL, err := url.Parse("http://nonexistent.elasticsearch.server:9200") if err != nil { t.Fatalf("Failed to parse bad URL: %v", err) } tests := []struct { name string config *config.ElasticSearchConfig handlerFunc http.HandlerFunc expectedErr bool }{ { name: "without aws signing config", config: &config.ElasticSearchConfig{ URL: *url, DisableSniff: false, CustomHeaders: map[string]string{ "key": "value", }, }, expectedErr: false, }, { name: "with wrong aws signing config", config: &config.ElasticSearchConfig{ URL: *badURL, DisableSniff: false, AWSSigning: config.AWSSigning{ Enable: true, }, }, expectedErr: true, //will fail to ping os sever }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { client, err := NewClient(tt.config, logger, testServer.Client()) if !tt.expectedErr { assert.NoError(t, err) assert.NotNil(t, client) } else { assert.Error(t, err) } }) } } func TestCreateIndex(t *testing.T) { tests := []struct { name string handler http.HandlerFunc expectErr bool secure bool }{ { name: "normal case", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == "PUT" && r.URL.Path == "/test-index" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"acknowledged": true, "shards_acknowledged": true, "index": "test-index"}`)) } else { w.WriteHeader(http.StatusNotFound) } }), expectErr: false, secure: true, }, { name: "error case", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) }), expectErr: true, secure: true, }, { name: "not valid config", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) }), expectErr: true, secure: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os2Client, testServer := getSecureMockOS2Client(t, tt.handler, tt.secure) defer testServer.Close() err := os2Client.CreateIndex(context.Background(), "test-index") if tt.expectErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func getSecureMockOS2Client(t *testing.T, handler http.HandlerFunc, secure bool) (*OS2, *httptest.Server) { testServer := httptest.NewTLSServer(handler) osConfig := osapi.Config{ Client: opensearch.Config{ Addresses: []string{testServer.URL}, }, } if secure { osConfig.Client.Transport = &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, } } client, err := osapi.NewClient(osConfig) if err != nil { t.Fatalf("Failed to create open search client: %v", err) } mockClient := &OS2{ client: client, logger: testlogger.New(t), decoder: &NumberDecoder{}, } assert.NoError(t, err) return mockClient, testServer } func TestPutMapping(t *testing.T) { testCases := []struct { name string handler http.HandlerFunc index string body string expectedErr bool }{ { name: "Successful PutMapping", handler: func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"acknowledged": true}`)) }, index: "testIndex", body: `{"properties": {"field": {"type": "text"}}}`, expectedErr: false, }, { name: "Failed PutMapping", handler: func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) }, index: "nonExistentIndex", body: `{"properties": {"field": {"type": "text"}}}`, expectedErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { os2Client, testServer := getSecureMockOS2Client(t, tc.handler, true) defer testServer.Close() err := os2Client.PutMapping(context.Background(), tc.index, tc.body) if tc.expectedErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestPutMappingError(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } os2Client, testServer := getSecureMockOS2Client(t, http.HandlerFunc(handler), true) defer testServer.Close() os2Client.client.Client.Transport = &MockTransport{} err := os2Client.PutMapping(context.Background(), "testIndex", `{"properties": {"field": {"type": "text"}}}`) assert.Error(t, err) } func TestIsNotFoundError(t *testing.T) { testCases := []struct { name string handler http.HandlerFunc expected bool }{ { name: "Other error", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "Bad Request", http.StatusBadRequest) }), expected: false, }, { name: "NotFound error", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) json.NewEncoder(w).Encode(map[string]interface{}{ "error": map[string]interface{}{ "type": "index_not_found_exception", }, "status": 404, }) }), expected: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { os2Client, testServer := getSecureMockOS2Client(t, tc.handler, true) defer testServer.Close() err := os2Client.CreateIndex(context.Background(), "testIndex") res := os2Client.IsNotFoundError(err) assert.Equal(t, tc.expected, res) }) } } func TestCount(t *testing.T) { testCases := []struct { name string handler http.HandlerFunc index string query string expectedCount int64 expectError bool }{ { name: "Successful Count", handler: func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintln(w, `{"count": 42}`) }, index: "testIndex", query: "{}", expectedCount: 42, expectError: false, }, { name: "OpenSearch Error", handler: func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(w, `{"error": "Internal Server Error"}`) }, index: "testIndex", query: "{}", expectError: true, }, { name: "Decoding Error", handler: func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintln(w, `{"count": "should be an int64"}`) }, index: "testIndex", query: "{}", expectError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { os2Client, testServer := getSecureMockOS2Client(t, tc.handler, true) defer testServer.Close() count, err := os2Client.Count(context.Background(), tc.index, tc.query) if tc.expectError { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedCount, count) } }) } } func TestScroll(t *testing.T) { testCases := []struct { name string scrollID string handler http.HandlerFunc expectError bool expectedScrollID string // Add more fields as needed for assertions }{ { name: "Initial Search Request", scrollID: "", handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, `{"_scroll_id": "scrollID123", "took": 10, "hits": {"total": {"value": 2}, "hits": [{"_source": {"field1": "value1"}}]}}`) }, expectError: false, expectedScrollID: "scrollID123", }, { name: "Subsequent Scroll Request", scrollID: "existingScrollID", handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, `{"_scroll_id": "scrollID456", "took": 5, "hits": {"total": {"value": 1}, "hits": [{"_source": {"field2": "value2"}}]}}`) }, expectError: false, expectedScrollID: "scrollID456", }, { name: "Error Response", scrollID: "", handler: func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(w, `{"error": "Internal Server Error"}`) }, expectError: true, }, { name: "No More Hits", scrollID: "someScrollID", handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, `{"_scroll_id": "scrollIDNoHits", "took": 5, "hits": {"hits": []}}`) }, expectError: false, expectedScrollID: "scrollIDNoHits", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { os2Client, testServer := getSecureMockOS2Client(t, tc.handler, true) defer testServer.Close() resp, err := os2Client.Scroll(context.Background(), "testIndex", "{}", tc.scrollID) if tc.expectError { assert.Error(t, err) } else if tc.name == "No More Hits" { assert.Equal(t, io.EOF, err, "Expected io.EOF error for no more hits") } else { assert.NoError(t, err) assert.NotNil(t, resp) assert.Equal(t, tc.expectedScrollID, resp.ScrollID) } }) } } func TestClearScroll(t *testing.T) { testCases := []struct { name string scrollID string handler http.HandlerFunc expectedError bool }{ { name: "Successful Scroll Clear", scrollID: "testScrollID", handler: func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"succeeded": true}`)) }, expectedError: false, }, { name: "OpenSearch Server Error", scrollID: "testScrollID", handler: func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(w, `{"error": {"root_cause": [{"type": "internal_server_error","reason": "Internal server error"}],"type": "internal_server_error","reason": "Internal server error"}}`) }, expectedError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { os2Client, testServer := getSecureMockOS2Client(t, tc.handler, true) defer testServer.Close() err := os2Client.ClearScroll(context.Background(), tc.scrollID) if tc.expectedError { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestSearch(t *testing.T) { testCases := []struct { name string index string body string handler http.HandlerFunc expectedError bool expectedHits int }{ { name: "Successful Search", index: "testIndex", body: `{"query": {"match_all": {}}}`, handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, `{"took": 10, "hits": {"total": {"value": 2}, "hits": [{"_source": {"field": "value"}, "sort": [1750950124525781262, "test sort val"]}, {"_source": {"field": "another value"}, "sort": [1750950124525781269, "test sort val 2"]}]}}`) }, expectedError: false, expectedHits: 2, }, { name: "OpenSearch Error", index: "testIndex", body: `{"query": {"match_all": {}}}`, handler: func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) fmt.Fprintln(w, `{"error": "Bad request"}`) }, expectedError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { os2Client, testServer := getSecureMockOS2Client(t, tc.handler, true) defer testServer.Close() resp, err := os2Client.Search(context.Background(), tc.index, tc.body) if tc.expectedError { assert.Error(t, err) assert.Nil(t, resp) } else { assert.NoError(t, err) assert.NotNil(t, resp) assert.Len(t, resp.Hits.Hits, tc.expectedHits) assert.Equal(t, resp.Sort[0], json.Number("1750950124525781269")) } }) } } ================================================ FILE: common/elasticsearch/client/v6/client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package v6 import ( "context" "encoding/json" "fmt" "net/http" "time" "github.com/olivere/elastic" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/elasticsearch/client" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) type ( // ElasticV6 implements Client ElasticV6 struct { client *elastic.Client logger log.Logger } convertlogger func(msg string, tags ...tag.Tag) ) var _ elastic.Logger = (convertlogger)(nil) func (c convertlogger) Printf(format string, v ...interface{}) { c(fmt.Sprintf(format, v...)) } func (c *ElasticV6) IsNotFoundError(err error) bool { return elastic.IsNotFound(err) } // NewV6Client returns a new implementation of GenericClient func NewV6Client( connectConfig *config.ElasticSearchConfig, logger log.Logger, tlsClient *http.Client, awsSigningClient *http.Client, ) (*ElasticV6, error) { clientOptFuncs := []elastic.ClientOptionFunc{ elastic.SetURL(connectConfig.URL.String()), elastic.SetRetrier(elastic.NewBackoffRetrier(elastic.NewExponentialBackoff(128*time.Millisecond, 513*time.Millisecond))), elastic.SetDecoder(&elastic.NumberDecoder{}), // critical to ensure decode of int64 won't lose precise) // elastic.SetInfoLog(convertlogger(logger.Info)), // logs start/stop of components, and every successful request's code and timing. probably too noisy. elastic.SetErrorLog(convertlogger(logger.Error)), // logs errors when they occur, sounds likely not noisy } if connectConfig.DisableSniff { clientOptFuncs = append(clientOptFuncs, elastic.SetSniff(false)) } if connectConfig.DisableHealthCheck { clientOptFuncs = append(clientOptFuncs, elastic.SetHealthcheck(false)) } if awsSigningClient != nil { clientOptFuncs = append(clientOptFuncs, elastic.SetHttpClient(awsSigningClient)) } if tlsClient != nil { clientOptFuncs = append(clientOptFuncs, elastic.SetHttpClient(tlsClient)) } client, err := elastic.NewClient(clientOptFuncs...) if err != nil { return nil, err } return &ElasticV6{ client: client, logger: logger, }, nil } func (c *ElasticV6) PutMapping(ctx context.Context, index, body string) error { _, err := c.client.PutMapping().Index(index).Type("_doc").BodyString(body).Do(ctx) return err } func (c *ElasticV6) CreateIndex(ctx context.Context, index string) error { _, err := c.client.CreateIndex(index).Do(ctx) return err } func (c *ElasticV6) Count(ctx context.Context, index, query string) (int64, error) { return c.client.Count(index).BodyString(query).Do(ctx) } func (c *ElasticV6) ClearScroll(ctx context.Context, scrollID string) error { return elastic.NewScrollService(c.client).ScrollId(scrollID).Clear(ctx) } func (c *ElasticV6) Scroll(ctx context.Context, index, body, scrollID string) (*client.Response, error) { scrollService := elastic.NewScrollService(c.client) var esResult *elastic.SearchResult var err error // we are not returning error immediately here, as result + error combination is possible if len(scrollID) == 0 { esResult, err = scrollService.Index(index).Body(body).Do(ctx) } else { esResult, err = scrollService.ScrollId(scrollID).Do(ctx) } if esResult == nil { return nil, err } var hits []*client.SearchHit if esResult.Hits != nil { for _, h := range esResult.Hits.Hits { if h.Source != nil { hits = append(hits, &client.SearchHit{Source: *h.Source}) } } } result := &client.Response{ TookInMillis: esResult.TookInMillis, TotalHits: esResult.TotalHits(), Hits: &client.SearchHits{Hits: hits}, ScrollID: esResult.ScrollId, } if len(esResult.Aggregations) > 0 { result.Aggregations = make(map[string]json.RawMessage, len(esResult.Aggregations)) for key, agg := range esResult.Aggregations { if agg != nil { result.Aggregations[key] = *agg } } } return result, err } func (c *ElasticV6) Search(ctx context.Context, index, body string) (*client.Response, error) { esResult, err := c.client.Search(index).Source(body).Do(ctx) if err != nil { return nil, err } if esResult.Error != nil { return nil, types.InternalServiceError{ Message: fmt.Sprintf("ElasticSearch Error: %#v", esResult.Error), } } else if esResult.TimedOut { return nil, types.InternalServiceError{ Message: fmt.Sprintf("ElasticSearch Error: Request timed out: %v ms", esResult.TookInMillis), } } var sort []interface{} var hits []*client.SearchHit if esResult != nil && esResult.Hits != nil { for _, h := range esResult.Hits.Hits { if h.Source != nil { hits = append(hits, &client.SearchHit{Source: *h.Source}) } sort = h.Sort } } result := &client.Response{ TookInMillis: esResult.TookInMillis, TotalHits: esResult.TotalHits(), Hits: &client.SearchHits{Hits: hits}, Sort: sort, } if len(esResult.Aggregations) > 0 { result.Aggregations = make(map[string]json.RawMessage, len(esResult.Aggregations)) for key, agg := range esResult.Aggregations { if agg != nil { result.Aggregations[key] = *agg } } } return result, nil } ================================================ FILE: common/elasticsearch/client/v6/client_bulk.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package v6 import ( "context" "time" "github.com/olivere/elastic" "github.com/uber/cadence/common/elasticsearch/bulk" "github.com/uber/cadence/common/log" ) // bulkProcessorParametersV6 holds all required and optional parameters for executing bulk service type bulkProcessorParametersV6 struct { Name string NumOfWorkers int BulkActions int BulkSize int FlushInterval time.Duration Backoff elastic.Backoff BeforeFunc elastic.BulkBeforeFunc AfterFunc elastic.BulkAfterFunc } type v6BulkProcessor struct { processor *elastic.BulkProcessor logger log.Logger } func (v *v6BulkProcessor) Start(ctx context.Context) error { return v.processor.Start(ctx) } func (v *v6BulkProcessor) Stop() error { return v.processor.Stop() } func (v *v6BulkProcessor) Close() error { return v.processor.Close() } func (v *v6BulkProcessor) Add(request *bulk.GenericBulkableAddRequest) { var req elastic.BulkableRequest switch request.RequestType { case bulk.BulkableDeleteRequest: req = elastic.NewBulkDeleteRequest(). Index(request.Index). Type(request.Type). Id(request.ID) case bulk.BulkableIndexRequest: req = elastic.NewBulkIndexRequest(). Index(request.Index). Type(request.Type). Id(request.ID). VersionType(request.VersionType). Version(request.Version). Doc(request.Doc) case bulk.BulkableCreateRequest: // for bulk create request still calls the bulk index method // with providing operation type req = elastic.NewBulkIndexRequest(). OpType("create"). Index(request.Index). Type(request.Type). Id(request.ID). VersionType("internal"). Doc(request.Doc) } v.processor.Add(req) } func (v *v6BulkProcessor) Flush() error { return v.processor.Flush() } func (c *ElasticV6) runBulkProcessor(ctx context.Context, p *bulkProcessorParametersV6) (*v6BulkProcessor, error) { processor, err := c.client.BulkProcessor(). Name(p.Name). Workers(p.NumOfWorkers). BulkActions(p.BulkActions). BulkSize(p.BulkSize). FlushInterval(p.FlushInterval). Backoff(p.Backoff). Before(p.BeforeFunc). After(p.AfterFunc). Do(ctx) if err != nil { return nil, err } return &v6BulkProcessor{ processor: processor, }, nil } func (c *ElasticV6) RunBulkProcessor(ctx context.Context, parameters *bulk.BulkProcessorParameters) (bulk.GenericBulkProcessor, error) { beforeFunc := func(executionId int64, requests []elastic.BulkableRequest) { parameters.BeforeFunc(executionId, fromV6ToGenericBulkableRequests(requests)) } afterFunc := func(executionId int64, requests []elastic.BulkableRequest, response *elastic.BulkResponse, err error) { parameters.AfterFunc( executionId, fromV6ToGenericBulkableRequests(requests), fromV6ToGenericBulkResponse(response), convertV6ErrorToGenericError(err)) } return c.runBulkProcessor(ctx, &bulkProcessorParametersV6{ Name: parameters.Name, NumOfWorkers: parameters.NumOfWorkers, BulkActions: parameters.BulkActions, BulkSize: parameters.BulkSize, FlushInterval: parameters.FlushInterval, Backoff: parameters.Backoff, BeforeFunc: beforeFunc, AfterFunc: afterFunc, }) } func convertV6ErrorToGenericError(err error) *bulk.GenericError { if err == nil { return nil } status := bulk.UnknownStatusCode switch e := err.(type) { case *elastic.Error: status = e.Status } return &bulk.GenericError{ Status: status, Details: err, } } func fromV6ToGenericBulkResponse(response *elastic.BulkResponse) *bulk.GenericBulkResponse { if response == nil { return &bulk.GenericBulkResponse{} } return &bulk.GenericBulkResponse{ Took: response.Took, Errors: response.Errors, Items: fromV6ToGenericBulkResponseItemMaps(response.Items), } } func fromV6ToGenericBulkResponseItemMaps(items []map[string]*elastic.BulkResponseItem) []map[string]*bulk.GenericBulkResponseItem { var gitems []map[string]*bulk.GenericBulkResponseItem for _, it := range items { gitems = append(gitems, fromV6ToGenericBulkResponseItemMap(it)) } return gitems } func fromV6ToGenericBulkResponseItemMap(m map[string]*elastic.BulkResponseItem) map[string]*bulk.GenericBulkResponseItem { if m == nil { return nil } gm := make(map[string]*bulk.GenericBulkResponseItem, len(m)) for k, v := range m { gm[k] = fromV6ToGenericBulkResponseItem(v) } return gm } func fromV6ToGenericBulkResponseItem(v *elastic.BulkResponseItem) *bulk.GenericBulkResponseItem { return &bulk.GenericBulkResponseItem{ Index: v.Index, Type: v.Type, ID: v.Id, Version: v.Version, Result: v.Result, Status: v.Status, } } func fromV6ToGenericBulkableRequests(requests []elastic.BulkableRequest) []bulk.GenericBulkableRequest { var v6Reqs []bulk.GenericBulkableRequest for _, req := range requests { v6Reqs = append(v6Reqs, req) } return v6Reqs } ================================================ FILE: common/elasticsearch/client/v6/client_bulk_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package v6 import ( "context" "errors" "testing" "time" "github.com/olivere/elastic" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/elasticsearch/bulk" "github.com/uber/cadence/common/log/testlogger" ) func TestConvertV6ErrorToGenericError(t *testing.T) { tests := []struct { name string err error expected *bulk.GenericError }{ { name: "nil error", err: nil, expected: nil, }, { name: "non-elasticsearch error", err: errors.New("generic error"), expected: &bulk.GenericError{ Status: bulk.UnknownStatusCode, Details: errors.New("generic error"), }, }, { name: "elasticsearch error", err: &elastic.Error{ Status: 404, Details: &elastic.ErrorDetails{ Type: "index_not_found_exception", Reason: "no such index", }, }, expected: &bulk.GenericError{ Status: 404, Details: &elastic.Error{ Status: 404, Details: &elastic.ErrorDetails{ Type: "index_not_found_exception", Reason: "no such index", }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := convertV6ErrorToGenericError(tt.err) if tt.expected == nil { assert.Nil(t, result) } else { assert.NotNil(t, result) assert.Equal(t, tt.expected.Status, result.Status) if expectedDetails, ok := tt.expected.Details.(*elastic.Error); ok { resultDetails, _ := result.Details.(*elastic.Error) assert.NotNil(t, resultDetails) assert.Equal(t, expectedDetails.Status, resultDetails.Status) assert.Equal(t, expectedDetails.Details.Type, resultDetails.Details.Type) assert.Equal(t, expectedDetails.Details.Reason, resultDetails.Details.Reason) } else { assert.Equal(t, tt.expected.Details.Error(), result.Details.Error()) } } }) } } func TestFromV6toGenericBulkResponse(t *testing.T) { tests := []struct { name string response *elastic.BulkResponse expectNil bool expectedTook int expectedErrors bool }{ { name: "nil response", response: nil, expectNil: false, expectedTook: 0, expectedErrors: false, }, { name: "non-nil response", response: &elastic.BulkResponse{ Took: 100, Errors: true, Items: []map[string]*elastic.BulkResponseItem{ { "index": &elastic.BulkResponseItem{ Status: 200, Error: nil, }, }, }, }, expectNil: false, expectedTook: 100, expectedErrors: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := fromV6ToGenericBulkResponse(tt.response) if tt.expectNil { assert.Nil(t, result) } else { assert.NotNil(t, result) assert.Equal(t, tt.expectedTook, result.Took) assert.Equal(t, tt.expectedErrors, result.Errors) } }) } } func TestFromV6ToGenericBulkResponseItemMaps(t *testing.T) { tests := []struct { name string v6items []map[string]*elastic.BulkResponseItem expected []map[string]*bulk.GenericBulkResponseItem }{ { name: "normal case", v6items: []map[string]*elastic.BulkResponseItem{ { "index": &elastic.BulkResponseItem{ Status: 200, }, }, { "update": &elastic.BulkResponseItem{ Status: 404, Error: &elastic.ErrorDetails{ Type: "index_not_found_exception", Reason: "no such index", }, }, }, }, expected: []map[string]*bulk.GenericBulkResponseItem{ { "index": { Status: 200, }, }, { "update": { Status: 404, }, }, }, }, { name: "nil case", v6items: nil, expected: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { genericItems := fromV6ToGenericBulkResponseItemMaps(tt.v6items) assert.Len(t, genericItems, len(tt.expected), "The lengths of actual and expected slices should match.") assert.Equal(t, tt.expected, genericItems) }) } } func TestFromV6ToGenericBulkResponseItemMap(t *testing.T) { tests := []struct { name string v6item map[string]*elastic.BulkResponseItem expected map[string]*bulk.GenericBulkResponseItem }{ { name: "nil case", v6item: nil, expected: nil, }, { name: "normal case", v6item: map[string]*elastic.BulkResponseItem{ "index": { Index: "test_index", Type: "test_type", Id: "1", Version: 1, Result: "created", Status: 201, }, }, expected: map[string]*bulk.GenericBulkResponseItem{ "index": { Index: "test_index", Type: "test_type", ID: "1", Version: 1, Result: "created", Status: 201, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { genericItems := fromV6ToGenericBulkResponseItemMap(tt.v6item) assert.Equal(t, tt.expected, genericItems) }) } } func TestFromV6ToGenericBulkResponseItem(t *testing.T) { elasticItem := &elastic.BulkResponseItem{ Index: "test_index", Type: "test_type", Id: "1", Version: 1, Result: "created", Status: 201, } expectedGenericItem := &bulk.GenericBulkResponseItem{ Index: "test_index", Type: "test_type", ID: "1", Version: 1, Result: "created", Status: 201, } result := fromV6ToGenericBulkResponseItem(elasticItem) assert.Equal(t, expectedGenericItem, result) } func TestFromV6ToGenericBulkableRequests(t *testing.T) { mockRequests := []elastic.BulkableRequest{ elastic.NewBulkIndexRequest().Index("index1").Type("_doc").Id("1").Doc(map[string]interface{}{"field": "value1"}), elastic.NewBulkIndexRequest().Index("index2").Type("_doc").Id("2").Doc(map[string]interface{}{"field": "value2"}), } genericRequests := fromV6ToGenericBulkableRequests(mockRequests) assert.Len(t, genericRequests, len(mockRequests)) } func getV6BulkProcessor(t *testing.T) *v6BulkProcessor { return &v6BulkProcessor{ processor: &elastic.BulkProcessor{}, logger: testlogger.New(t), } } func TestProcessorFunc(t *testing.T) { processor := getV6BulkProcessor(t) err := processor.Start(context.Background()) assert.NoError(t, err) err = processor.Flush() assert.NoError(t, err) err = processor.Stop() assert.NoError(t, err) err = processor.Close() assert.NoError(t, err) } func TestProcessorAdd(t *testing.T) { tests := []struct { name string request *bulk.GenericBulkableAddRequest expectErr bool }{ { name: "bulk add nil case", request: nil, expectErr: true, }, { name: "bulk add normal case", request: &bulk.GenericBulkableAddRequest{ Index: "test-index", RequestType: bulk.BulkableIndexRequest, ID: "test-id", VersionType: "internal", Type: "test-type", Version: int64(1), Doc: "", }, expectErr: false, }, { name: "bulk create normal case", request: &bulk.GenericBulkableAddRequest{ Index: "test-index", RequestType: bulk.BulkableCreateRequest, ID: "test-id", VersionType: "internal", Type: "test-type", Version: int64(1), Doc: "", }, expectErr: false, }, { name: "bulk add normal case", request: &bulk.GenericBulkableAddRequest{ Index: "test-index", RequestType: bulk.BulkableDeleteRequest, ID: "test-id", VersionType: "internal", Type: "test-type", Version: int64(1), Doc: "", }, expectErr: false, }, } elasticV6, testServer := getMockClient(t, nil) defer testServer.Close() svc := elasticV6.client.BulkProcessor(). Name("FlushInterval-1"). Workers(2). BulkActions(-1). BulkSize(-1) p, err := svc.Do(context.Background()) if err != nil { t.Fatal(err) } processor := v6BulkProcessor{ processor: p, logger: testlogger.New(t), } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.expectErr { assert.Panics(t, func() { processor.Add(tt.request) }, "The method should panic") } else { assert.NotPanics(t, func() { processor.Add(tt.request) }, "The method should not panic") } }) } } func TestRunBulkProcessor(t *testing.T) { tests := []struct { name string params *bulk.BulkProcessorParameters }{ { name: "successful initialization", params: &bulk.BulkProcessorParameters{ Name: "test-processor", NumOfWorkers: 5, BulkActions: 1000, BulkSize: 2 * 1024 * 1024, // 2MB FlushInterval: 30 * time.Second, Backoff: elastic.NewExponentialBackoff(10*time.Millisecond, 8*time.Second), BeforeFunc: func(executionId int64, requests []bulk.GenericBulkableRequest) { }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { elasticV6, testServer := getMockClient(t, nil) defer testServer.Close() result, err := elasticV6.RunBulkProcessor(context.Background(), tt.params) assert.NoError(t, err) assert.NotNil(t, result) }) } } ================================================ FILE: common/elasticsearch/client/v6/client_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package v6 import ( "context" "encoding/json" "io" "net/http" "net/http/httptest" "net/url" "testing" "github.com/olivere/elastic" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" ) func TestNewV6Client(t *testing.T) { logger := testlogger.New(t) testServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) defer testServer.Close() url, err := url.Parse(testServer.URL) if err != nil { t.Fatalf("Failed to parse bad URL: %v", err) } connectionConfig := &config.ElasticSearchConfig{ URL: *url, DisableSniff: true, DisableHealthCheck: true, } sharedClient := testServer.Client() client, err := NewV6Client(connectionConfig, logger, sharedClient, sharedClient) assert.NoError(t, err) assert.NotNil(t, client) // failed case due to an unreachable Elasticsearch server badURL, err := url.Parse("http://nonexistent.elasticsearch.server:9200") if err != nil { t.Fatalf("Failed to parse bad URL: %v", err) } connectionConfig.DisableHealthCheck = false connectionConfig.URL = *badURL _, err = NewV6Client(connectionConfig, logger, nil, nil) assert.Error(t, err) } func TestCreateIndex(t *testing.T) { var handlerCalled bool handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handlerCalled = true if r.URL.Path == "/testIndex" && r.Method == "PUT" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"acknowledged": true}`)) } else { w.WriteHeader(http.StatusNotFound) } }) elasticV6, testServer := getMockClient(t, handler) defer testServer.Close() err := elasticV6.CreateIndex(context.Background(), "testIndex") assert.True(t, handlerCalled, "Expected handler to be called") assert.NoError(t, err) } func TestPutMapping(t *testing.T) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_mapping/_doc" && r.Method == "PUT" { body, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("Failed to read request body: %v", err) } defer r.Body.Close() var receivedMapping map[string]interface{} if err := json.Unmarshal(body, &receivedMapping); err != nil { t.Fatalf("Failed to unmarshal request body: %v", err) } // Define expected mapping structurally expectedMapping := map[string]interface{}{ "properties": map[string]interface{}{ "title": map[string]interface{}{ "type": "text", }, "publish_date": map[string]interface{}{ "type": "date", }, }, } // Compare structurally if !assert.Equal(t, expectedMapping, receivedMapping) { w.WriteHeader(http.StatusBadRequest) return } w.WriteHeader(http.StatusOK) w.Write([]byte(`{"acknowledged": true}`)) } else { w.WriteHeader(http.StatusNotFound) } }) elasticV6, testServer := getMockClient(t, handler) defer testServer.Close() err := elasticV6.PutMapping(context.Background(), "testIndex", `{ "properties": { "title": { "type": "text" }, "publish_date": { "type": "date" } } }`) assert.NoError(t, err) } func TestCount(t *testing.T) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_count" && r.Method == "POST" { body, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("Failed to read request body: %v", err) } defer r.Body.Close() expectedQuery := `{"query":{"match":{"WorkflowID":"test-workflow-id"}}}` if string(body) != expectedQuery { t.Fatalf("Expected query %s, got %s", expectedQuery, body) } w.WriteHeader(http.StatusOK) w.Write([]byte(`{"count": 42}`)) } else { w.WriteHeader(http.StatusNotFound) } }) elasticV6, testServer := getMockClient(t, handler) defer testServer.Close() count, err := elasticV6.Count(context.Background(), "testIndex", `{"query":{"match":{"WorkflowID":"test-workflow-id"}}}`) assert.NoError(t, err) assert.Equal(t, int64(42), count) } func TestSearch(t *testing.T) { testCases := []struct { name string query string expected map[string]interface{} expectErr bool expectAgg bool index string handler http.HandlerFunc }{ { name: "normal case", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "testIndex", expected: map[string]interface{}{ "WorkflowID": "test-workflow-id", }, handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "took": 5, "timed_out": false, "hits": { "total": 1, "hits": [{ "_source": { "WorkflowID": "test-workflow-id" }, "sort": [1] }] } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), expectErr: false, }, { name: "elasticsearch error", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "testIndex", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "error": { "root_cause": [ { "type": "index_not_found_exception", "reason": "no such index", "resource.type": "index_or_alias", "resource.id": "testIndex", "index_uuid": "_na_", "index": "testIndex" } ], "type": "index_not_found_exception", "reason": "no such index", "resource.type": "index_or_alias", "resource.id": "testIndex", "index_uuid": "_na_", "index": "testIndex" }, "status": 404 }`)) } else { w.WriteHeader(http.StatusNotFound) } }), expectErr: true, }, { name: "elasticsearch timeout", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "testIndex", expectErr: true, handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) // Assuming Elasticsearch returns HTTP 200 for timeouts with an indication in the body w.Write([]byte(`{ "took": 30, "timed_out": true, "hits": { "total": 0, "hits": [] } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), }, { name: "elasticsearch aggregations", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "testIndex", expected: map[string]interface{}{ "WorkflowID": "test-workflow-id", }, expectErr: false, expectAgg: true, handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "took": 5, "timed_out": false, "hits": { "total": 1, "hits": [{ "_source": { "WorkflowID": "test-workflow-id" } }] }, "aggregations": { "sample_agg": { "value": 42 } } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), }, { name: "elasticsearch non exist index", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "test_failure", expectErr: true, handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) } else { w.WriteHeader(http.StatusNotFound) } }), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { elasticV6, testServer := getMockClient(t, tc.handler) defer testServer.Close() resp, err := elasticV6.Search(context.Background(), tc.index, tc.query) if !tc.expectErr { assert.NoError(t, err) assert.NotNil(t, resp) // Verify the response details assert.Equal(t, int64(5), resp.TookInMillis) assert.Equal(t, int64(1), resp.TotalHits) assert.NotNil(t, resp.Hits) assert.Len(t, resp.Hits.Hits, 1) var actual map[string]interface{} if err := json.Unmarshal([]byte(string(resp.Hits.Hits[0].Source)), &actual); err != nil { t.Fatalf("Failed to unmarshal actual JSON: %v", err) } assert.Equal(t, tc.expected, actual) if tc.expectAgg { // Verify the response includes the expected aggregations assert.NotNil(t, resp.Aggregations, "Aggregations should not be nil") assert.Contains(t, resp.Aggregations, "sample_agg", "Aggregations should contain 'sample_agg'") // Additional assertions can be made to verify the contents of the aggregation sampleAgg := resp.Aggregations["sample_agg"] var aggResult map[string]interface{} err = json.Unmarshal(sampleAgg, &aggResult) assert.NoError(t, err) assert.Equal(t, float64(42), aggResult["value"], "Aggregation 'sample_agg' should have a value of 42") } } else { assert.Error(t, err) } }) } } func TestScroll(t *testing.T) { testCases := []struct { name string query string expected map[string]interface{} expectErr bool index string handler http.HandlerFunc scrollID string }{ { name: "normal case", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, expected: map[string]interface{}{ "WorkflowID": "test-workflow-id", }, index: "testIndex", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "took": 5, "timed_out": false, "hits": { "total": 1, "hits": [{ "_source": { "WorkflowID": "test-workflow-id" } }] }, "aggregations": { "sample_agg": { "value": 42 } } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), expectErr: false, }, { name: "error case", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "testIndex", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) }), expectErr: true, scrollID: "1", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { elasticV6, testServer := getMockClient(t, tc.handler) defer testServer.Close() resp, err := elasticV6.Scroll(context.Background(), tc.index, tc.query, tc.scrollID) if !tc.expectErr { assert.NoError(t, err) var actualSource map[string]interface{} err := json.Unmarshal(resp.Hits.Hits[0].Source, &actualSource) assert.NoError(t, err) assert.Equal(t, tc.expected, actualSource) } else { assert.Error(t, err) assert.Nil(t, resp) } }) } } func TestClearScroll(t *testing.T) { var handlerCalled bool handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handlerCalled = true if r.Method == "DELETE" && r.URL.Path == "/_search/scroll" { // Simulate a successful clear scroll response w.WriteHeader(http.StatusOK) response := `{ "succeeded": true, "num_freed": 1 }` w.Write([]byte(response)) } else { w.WriteHeader(http.StatusNotFound) } }) elasticV6, testServer := getMockClient(t, handler) defer testServer.Close() err := elasticV6.ClearScroll(context.Background(), "scrollID") assert.True(t, handlerCalled, "Expected handler to be called") assert.NoError(t, err) } func TestIsNotFoundError(t *testing.T) { testCases := []struct { name string handler http.HandlerFunc expected bool }{ { name: "NotFound error", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) }), expected: true, }, { name: "Other error", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "Bad Request", http.StatusBadRequest) }), expected: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { elasticV6, testServer := getMockClient(t, tc.handler) defer testServer.Close() err := elasticV6.CreateIndex(context.Background(), "testIndex") res := elasticV6.IsNotFoundError(err) assert.Equal(t, tc.expected, res) }) } } func getMockClient(t *testing.T, handler http.HandlerFunc) (ElasticV6, *httptest.Server) { testServer := httptest.NewTLSServer(handler) mockClient, err := elastic.NewClient( elastic.SetURL(testServer.URL), elastic.SetSniff(false), elastic.SetHealthcheck(false), elastic.SetHttpClient(testServer.Client())) assert.NoError(t, err) return ElasticV6{ client: mockClient, }, testServer } ================================================ FILE: common/elasticsearch/client/v7/client.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package v7 import ( "context" "fmt" "net/http" "time" "github.com/olivere/elastic/v7" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/elasticsearch/client" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) type ( // ElasticV7 implements ES7 ElasticV7 struct { client *elastic.Client logger log.Logger } convertlogger func(msg string, tags ...tag.Tag) ) var _ elastic.Logger = (convertlogger)(nil) func (c convertlogger) Printf(format string, v ...interface{}) { c(fmt.Sprintf(format, v...)) } // NewV7Client returns a new implementation of GenericClient func NewV7Client( connectConfig *config.ElasticSearchConfig, logger log.Logger, tlsClient *http.Client, awsSigningClient *http.Client, ) (*ElasticV7, error) { clientOptFuncs := []elastic.ClientOptionFunc{ elastic.SetURL(connectConfig.URL.String()), elastic.SetRetrier(elastic.NewBackoffRetrier(elastic.NewExponentialBackoff(128*time.Millisecond, 513*time.Millisecond))), elastic.SetDecoder(&elastic.NumberDecoder{}), // critical to ensure decode of int64 won't lose precise // elastic.SetInfoLog(convertlogger(logger.Info)), // logs start/stop of components, and every successful request's code and timing. probably too noisy. elastic.SetErrorLog(convertlogger(logger.Error)), // logs errors when they occur, sounds likely not noisy } if connectConfig.DisableSniff { clientOptFuncs = append(clientOptFuncs, elastic.SetSniff(false)) } if connectConfig.DisableHealthCheck { clientOptFuncs = append(clientOptFuncs, elastic.SetHealthcheck(false)) } if awsSigningClient != nil { clientOptFuncs = append(clientOptFuncs, elastic.SetHttpClient(awsSigningClient)) } if tlsClient != nil { clientOptFuncs = append(clientOptFuncs, elastic.SetHttpClient(tlsClient)) } client, err := elastic.NewClient(clientOptFuncs...) if err != nil { return nil, err } return &ElasticV7{ client: client, logger: logger, }, nil } func (c *ElasticV7) IsNotFoundError(err error) bool { return elastic.IsNotFound(err) } func (c *ElasticV7) PutMapping(ctx context.Context, index, body string) error { _, err := c.client.PutMapping().Index(index).BodyString(body).Do(ctx) return err } func (c *ElasticV7) CreateIndex(ctx context.Context, index string) error { _, err := c.client.CreateIndex(index).Do(ctx) return err } func (c *ElasticV7) Count(ctx context.Context, index, query string) (int64, error) { return c.client.Count(index).BodyString(query).Do(ctx) } func (c *ElasticV7) ClearScroll(ctx context.Context, scrollID string) error { return elastic.NewScrollService(c.client).ScrollId(scrollID).Clear(ctx) } func (c *ElasticV7) Search(ctx context.Context, index string, body string) (*client.Response, error) { esResult, err := c.client.Search(index).Source(body).Do(ctx) if err != nil { return nil, err } if esResult.Error != nil { return nil, types.InternalServiceError{ Message: fmt.Sprintf("ElasticSearch Error: %#v", esResult.Error), } } else if esResult.TimedOut { return nil, types.InternalServiceError{ Message: fmt.Sprintf("ElasticSearch Error: Request timed out: %v ms", esResult.TookInMillis), } } var sort []interface{} var hits []*client.SearchHit if esResult != nil && esResult.Hits != nil { for _, h := range esResult.Hits.Hits { hits = append(hits, &client.SearchHit{Source: h.Source}) sort = h.Sort } } return &client.Response{ TookInMillis: esResult.TookInMillis, TotalHits: esResult.TotalHits(), Hits: &client.SearchHits{Hits: hits}, Aggregations: esResult.Aggregations, Sort: sort, }, nil } func (c *ElasticV7) Scroll(ctx context.Context, index, body, scrollID string) (*client.Response, error) { scrollService := elastic.NewScrollService(c.client) var esResult *elastic.SearchResult var err error // we are not returning error immediately here, as result + error combination is possible if len(scrollID) == 0 { esResult, err = scrollService.Index(index).Body(body).Do(ctx) } else { esResult, err = scrollService.ScrollId(scrollID).Do(ctx) } if esResult == nil { return nil, err } var hits []*client.SearchHit if esResult.Hits != nil { for _, h := range esResult.Hits.Hits { hits = append(hits, &client.SearchHit{Source: h.Source}) } } return &client.Response{ TookInMillis: esResult.TookInMillis, TotalHits: esResult.TotalHits(), Hits: &client.SearchHits{Hits: hits}, Aggregations: esResult.Aggregations, ScrollID: esResult.ScrollId, }, err } ================================================ FILE: common/elasticsearch/client/v7/client_bulk.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package v7 import ( "context" "time" "github.com/olivere/elastic/v7" "github.com/uber/cadence/common/elasticsearch/bulk" ) type // bulkProcessorParametersV7 holds all required and optional parameters for executing bulk service bulkProcessorParametersV7 struct { Name string NumOfWorkers int BulkActions int BulkSize int FlushInterval time.Duration Backoff elastic.Backoff BeforeFunc elastic.BulkBeforeFunc AfterFunc elastic.BulkAfterFunc } type v7BulkProcessor struct { processor *elastic.BulkProcessor } func (v *v7BulkProcessor) Start(ctx context.Context) error { return v.processor.Start(ctx) } func (v *v7BulkProcessor) Stop() error { return v.processor.Stop() } func (v *v7BulkProcessor) Close() error { return v.processor.Close() } func (v *v7BulkProcessor) Add(request *bulk.GenericBulkableAddRequest) { var req elastic.BulkableRequest switch request.RequestType { case bulk.BulkableDeleteRequest: req = elastic.NewBulkDeleteRequest(). Index(request.Index). Id(request.ID) case bulk.BulkableIndexRequest: req = elastic.NewBulkIndexRequest(). Index(request.Index). Id(request.ID). VersionType(request.VersionType). Version(request.Version). Doc(request.Doc) case bulk.BulkableCreateRequest: // for bulk create request still calls the bulk index method // with providing operation type req = elastic.NewBulkIndexRequest(). OpType("create"). Index(request.Index). Id(request.ID). VersionType("internal"). Doc(request.Doc) } v.processor.Add(req) } func (v *v7BulkProcessor) Flush() error { return v.processor.Flush() } func (c *ElasticV7) runBulkProcessor(ctx context.Context, p *bulkProcessorParametersV7) (*v7BulkProcessor, error) { processor, err := c.client.BulkProcessor(). Name(p.Name). Workers(p.NumOfWorkers). BulkActions(p.BulkActions). BulkSize(p.BulkSize). FlushInterval(p.FlushInterval). Backoff(p.Backoff). Before(p.BeforeFunc). After(p.AfterFunc). Do(ctx) if err != nil { return nil, err } return &v7BulkProcessor{ processor: processor, }, nil } func fromV7ToGenericBulkResponse(response *elastic.BulkResponse) *bulk.GenericBulkResponse { if response == nil { return &bulk.GenericBulkResponse{} } return &bulk.GenericBulkResponse{ Took: response.Took, Errors: response.Errors, Items: fromV7ToGenericBulkResponseItemMaps(response.Items), } } func fromV7ToGenericBulkResponseItemMaps(items []map[string]*elastic.BulkResponseItem) []map[string]*bulk.GenericBulkResponseItem { var gitems []map[string]*bulk.GenericBulkResponseItem for _, it := range items { gitems = append(gitems, fromV7ToGenericBulkResponseItemMap(it)) } return gitems } func fromV7ToGenericBulkResponseItemMap(m map[string]*elastic.BulkResponseItem) map[string]*bulk.GenericBulkResponseItem { if m == nil { return nil } gm := make(map[string]*bulk.GenericBulkResponseItem, len(m)) for k, v := range m { gm[k] = fromV7ToGenericBulkResponseItem(v) } return gm } func fromV7ToGenericBulkResponseItem(v *elastic.BulkResponseItem) *bulk.GenericBulkResponseItem { return &bulk.GenericBulkResponseItem{ Index: v.Index, Type: v.Type, ID: v.Id, Version: v.Version, Result: v.Result, Status: v.Status, } } func fromV7ToGenericBulkableRequests(requests []elastic.BulkableRequest) []bulk.GenericBulkableRequest { var v7Reqs []bulk.GenericBulkableRequest for _, req := range requests { v7Reqs = append(v7Reqs, req) } return v7Reqs } func (c *ElasticV7) RunBulkProcessor(ctx context.Context, parameters *bulk.BulkProcessorParameters) (bulk.GenericBulkProcessor, error) { beforeFunc := func(executionId int64, requests []elastic.BulkableRequest) { parameters.BeforeFunc(executionId, fromV7ToGenericBulkableRequests(requests)) } afterFunc := func(executionId int64, requests []elastic.BulkableRequest, response *elastic.BulkResponse, err error) { parameters.AfterFunc( executionId, fromV7ToGenericBulkableRequests(requests), fromV7ToGenericBulkResponse(response), convertV7ErrorToGenericError(err)) } return c.runBulkProcessor(ctx, &bulkProcessorParametersV7{ Name: parameters.Name, NumOfWorkers: parameters.NumOfWorkers, BulkActions: parameters.BulkActions, BulkSize: parameters.BulkSize, FlushInterval: parameters.FlushInterval, Backoff: parameters.Backoff, BeforeFunc: beforeFunc, AfterFunc: afterFunc, }) } func convertV7ErrorToGenericError(err error) *bulk.GenericError { if err == nil { return nil } status := bulk.UnknownStatusCode switch e := err.(type) { case *elastic.Error: status = e.Status } return &bulk.GenericError{ Status: status, Details: err, } } ================================================ FILE: common/elasticsearch/client/v7/client_bulk_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package v7 import ( "context" "errors" "testing" "time" elastic "github.com/olivere/elastic/v7" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/elasticsearch/bulk" ) func TestFromV7toGenericBulkResponse(t *testing.T) { tests := []struct { name string response *elastic.BulkResponse expectNil bool expectedTook int expectedErrors bool }{ { name: "nil response", response: nil, expectNil: false, expectedTook: 0, expectedErrors: false, }, { name: "non-nil response", response: &elastic.BulkResponse{ Took: 100, Errors: true, Items: []map[string]*elastic.BulkResponseItem{ { "index": &elastic.BulkResponseItem{ Status: 200, Error: nil, }, }, }, }, expectNil: false, expectedTook: 100, expectedErrors: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := fromV7ToGenericBulkResponse(tt.response) if tt.expectNil { assert.Nil(t, result) } else { assert.NotNil(t, result) assert.Equal(t, tt.expectedTook, result.Took) assert.Equal(t, tt.expectedErrors, result.Errors) } }) } } func TestFromV7ToGenericBulkResponseItemMaps(t *testing.T) { tests := []struct { name string v7items []map[string]*elastic.BulkResponseItem expected []map[string]*bulk.GenericBulkResponseItem }{ { name: "normal case", v7items: []map[string]*elastic.BulkResponseItem{ { "index": &elastic.BulkResponseItem{ Status: 200, }, }, { "update": &elastic.BulkResponseItem{ Status: 404, Error: &elastic.ErrorDetails{ Type: "index_not_found_exception", Reason: "no such index", }, }, }, }, expected: []map[string]*bulk.GenericBulkResponseItem{ { "index": { Status: 200, }, }, { "update": { Status: 404, }, }, }, }, { name: "nil case", v7items: nil, expected: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { genericItems := fromV7ToGenericBulkResponseItemMaps(tt.v7items) assert.Len(t, genericItems, len(tt.expected), "The lengths of actual and expected slices should match.") assert.Equal(t, tt.expected, genericItems) }) } } func TestFromV7ToGenericBulkResponseItemMap(t *testing.T) { tests := []struct { name string v7item map[string]*elastic.BulkResponseItem expected map[string]*bulk.GenericBulkResponseItem }{ { name: "nil case", v7item: nil, expected: nil, }, { name: "normal case", v7item: map[string]*elastic.BulkResponseItem{ "index": { Index: "test_index", Type: "test_type", Id: "1", Version: 1, Result: "created", Status: 201, }, }, expected: map[string]*bulk.GenericBulkResponseItem{ "index": { Index: "test_index", Type: "test_type", ID: "1", Version: 1, Result: "created", Status: 201, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { genericItems := fromV7ToGenericBulkResponseItemMap(tt.v7item) assert.Equal(t, tt.expected, genericItems) }) } } func TestFromV7ToGenericBulkResponseItem(t *testing.T) { elasticItem := &elastic.BulkResponseItem{ Index: "test_index", Type: "test_type", Id: "1", Version: 1, Result: "created", Status: 201, } expectedGenericItem := &bulk.GenericBulkResponseItem{ Index: "test_index", Type: "test_type", ID: "1", Version: 1, Result: "created", Status: 201, } result := fromV7ToGenericBulkResponseItem(elasticItem) assert.Equal(t, expectedGenericItem, result) } func TestFromV7ToGenericBulkableRequests(t *testing.T) { mockRequests := []elastic.BulkableRequest{ elastic.NewBulkIndexRequest().Index("index1").Type("_doc").Id("1").Doc(map[string]interface{}{"field": "value1"}), elastic.NewBulkIndexRequest().Index("index2").Type("_doc").Id("2").Doc(map[string]interface{}{"field": "value2"}), } genericRequests := fromV7ToGenericBulkableRequests(mockRequests) assert.Len(t, genericRequests, len(mockRequests)) } func getV7BulkProcessor() *v7BulkProcessor { return &v7BulkProcessor{ processor: &elastic.BulkProcessor{}, } } func TestProcessorFunc(t *testing.T) { processor := getV7BulkProcessor() err := processor.Start(context.Background()) assert.NoError(t, err) err = processor.Flush() assert.NoError(t, err) err = processor.Stop() assert.NoError(t, err) err = processor.Close() assert.NoError(t, err) } func TestProcessorAdd(t *testing.T) { tests := []struct { name string request *bulk.GenericBulkableAddRequest expectErr bool }{ { name: "bulk add nil case", request: nil, expectErr: true, }, { name: "bulk add normal case", request: &bulk.GenericBulkableAddRequest{ Index: "test-index", RequestType: bulk.BulkableIndexRequest, ID: "test-id", VersionType: "internal", Type: "test-type", Version: int64(1), Doc: "", }, expectErr: false, }, { name: "bulk create normal case", request: &bulk.GenericBulkableAddRequest{ Index: "test-index", RequestType: bulk.BulkableCreateRequest, ID: "test-id", VersionType: "internal", Type: "test-type", Version: int64(1), Doc: "", }, expectErr: false, }, { name: "bulk add normal case", request: &bulk.GenericBulkableAddRequest{ Index: "test-index", RequestType: bulk.BulkableDeleteRequest, ID: "test-id", VersionType: "internal", Type: "test-type", Version: int64(1), Doc: "", }, expectErr: false, }, } elasticV7, testServer := getMockClient(t, nil) defer testServer.Close() svc := elasticV7.client.BulkProcessor(). Name("FlushInterval-1"). Workers(2). BulkActions(-1). BulkSize(-1) p, err := svc.Do(context.Background()) if err != nil { t.Fatal(err) } processor := v7BulkProcessor{ processor: p, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.expectErr { assert.Panics(t, func() { processor.Add(tt.request) }, "The method should panic") } else { assert.NotPanics(t, func() { processor.Add(tt.request) }, "The method should not panic") } }) } } func TestRunBulkProcessor(t *testing.T) { tests := []struct { name string params *bulk.BulkProcessorParameters }{ { name: "successful initialization", params: &bulk.BulkProcessorParameters{ Name: "test-processor", NumOfWorkers: 5, BulkActions: 1000, BulkSize: 2 * 1024 * 1024, // 2MB FlushInterval: 30 * time.Second, Backoff: elastic.NewExponentialBackoff(10*time.Millisecond, 8*time.Second), BeforeFunc: func(executionId int64, requests []bulk.GenericBulkableRequest) { }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { elasticV7, testServer := getMockClient(t, nil) defer testServer.Close() result, err := elasticV7.RunBulkProcessor(context.Background(), tt.params) assert.NoError(t, err) assert.NotNil(t, result) }) } } func TestConvertV7ErrorToGenericError(t *testing.T) { tests := []struct { name string err error expected *bulk.GenericError }{ { name: "nil error", err: nil, expected: nil, }, { name: "non-elasticsearch error", err: errors.New("generic error"), expected: &bulk.GenericError{ Status: bulk.UnknownStatusCode, Details: errors.New("generic error"), }, }, { name: "elasticsearch error", err: &elastic.Error{ Status: 404, Details: &elastic.ErrorDetails{ Type: "index_not_found_exception", Reason: "no such index", }, }, expected: &bulk.GenericError{ Status: 404, Details: &elastic.Error{ Status: 404, Details: &elastic.ErrorDetails{ Type: "index_not_found_exception", Reason: "no such index", }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := convertV7ErrorToGenericError(tt.err) if tt.expected == nil { assert.Nil(t, result) } else { assert.NotNil(t, result) assert.Equal(t, tt.expected.Status, result.Status) if expectedDetails, ok := tt.expected.Details.(*elastic.Error); ok { resultDetails, _ := result.Details.(*elastic.Error) assert.NotNil(t, resultDetails) assert.Equal(t, expectedDetails.Status, resultDetails.Status) assert.Equal(t, expectedDetails.Details.Type, resultDetails.Details.Type) assert.Equal(t, expectedDetails.Details.Reason, resultDetails.Details.Reason) } else { assert.Equal(t, tt.expected.Details.Error(), result.Details.Error()) } } }) } } ================================================ FILE: common/elasticsearch/client/v7/client_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package v7 import ( "context" "encoding/json" "io" "net/http" "net/http/httptest" "net/url" "testing" elastic "github.com/olivere/elastic/v7" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" ) func TestNewV7Client(t *testing.T) { logger := testlogger.New(t) testServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) defer testServer.Close() url, err := url.Parse(testServer.URL) if err != nil { t.Fatalf("Failed to parse bad URL: %v", err) } connectionConfig := &config.ElasticSearchConfig{ URL: *url, DisableSniff: true, DisableHealthCheck: true, } sharedClient := testServer.Client() client, err := NewV7Client(connectionConfig, logger, sharedClient, sharedClient) assert.NoError(t, err) assert.NotNil(t, client) // failed case due to an unreachable Elasticsearch server badURL, err := url.Parse("http://nonexistent.elasticsearch.server:9200") if err != nil { t.Fatalf("Failed to parse bad URL: %v", err) } connectionConfig.DisableHealthCheck = false connectionConfig.URL = *badURL _, err = NewV7Client(connectionConfig, logger, nil, nil) assert.Error(t, err) } func TestCreateIndex(t *testing.T) { var handlerCalled bool handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handlerCalled = true if r.URL.Path == "/testIndex" && r.Method == "PUT" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"acknowledged": true}`)) } else { w.WriteHeader(http.StatusNotFound) } }) elasticV7, testServer := getMockClient(t, handler) defer testServer.Close() err := elasticV7.CreateIndex(context.Background(), "testIndex") assert.True(t, handlerCalled, "Expected handler to be called") assert.NoError(t, err) } func TestPutMapping(t *testing.T) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_mapping" && r.Method == "PUT" { body, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("Failed to read request body: %v", err) } defer r.Body.Close() var receivedMapping map[string]interface{} if err := json.Unmarshal(body, &receivedMapping); err != nil { t.Fatalf("Failed to unmarshal request body: %v", err) } // Define expected mapping structurally expectedMapping := map[string]interface{}{ "properties": map[string]interface{}{ "title": map[string]interface{}{ "type": "text", }, "publish_date": map[string]interface{}{ "type": "date", }, }, } // Compare structurally if !assert.Equal(t, expectedMapping, receivedMapping) { w.WriteHeader(http.StatusBadRequest) return } w.WriteHeader(http.StatusOK) w.Write([]byte(`{"acknowledged": true}`)) } else { w.WriteHeader(http.StatusNotFound) } }) elasticV7, testServer := getMockClient(t, handler) defer testServer.Close() err := elasticV7.PutMapping(context.Background(), "testIndex", `{ "properties": { "title": { "type": "text" }, "publish_date": { "type": "date" } } }`) assert.NoError(t, err) } func TestCount(t *testing.T) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_count" && r.Method == "POST" { body, err := io.ReadAll(r.Body) if err != nil { t.Fatalf("Failed to read request body: %v", err) } defer r.Body.Close() expectedQuery := `{"query":{"match":{"WorkflowID":"test-workflow-id"}}}` if string(body) != expectedQuery { t.Fatalf("Expected query %s, got %s", expectedQuery, body) } w.WriteHeader(http.StatusOK) w.Write([]byte(`{"count": 42}`)) } else { w.WriteHeader(http.StatusNotFound) } }) elasticV7, testServer := getMockClient(t, handler) defer testServer.Close() count, err := elasticV7.Count(context.Background(), "testIndex", `{"query":{"match":{"WorkflowID":"test-workflow-id"}}}`) assert.NoError(t, err) assert.Equal(t, int64(42), count) } func TestSearch(t *testing.T) { testCases := []struct { name string query string expected map[string]interface{} expectErr bool expectAgg bool index string handler http.HandlerFunc }{ { name: "normal case", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "testIndex", expected: map[string]interface{}{ "WorkflowID": "test-workflow-id", }, handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "took": 5, "timed_out": false, "hits": { "total": 1, "hits": [{ "_source": { "WorkflowID": "test-workflow-id" }, "sort": [1] }] } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), expectErr: false, }, { name: "elasticsearch error", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "testIndex", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "error": { "root_cause": [ { "type": "index_not_found_exception", "reason": "no such index", "resource.type": "index_or_alias", "resource.id": "testIndex", "index_uuid": "_na_", "index": "testIndex" } ], "type": "index_not_found_exception", "reason": "no such index", "resource.type": "index_or_alias", "resource.id": "testIndex", "index_uuid": "_na_", "index": "testIndex" }, "status": 404 }`)) } else { w.WriteHeader(http.StatusNotFound) } }), expectErr: true, }, { name: "elasticsearch timeout", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "testIndex", expectErr: true, handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) // Assuming Elasticsearch returns HTTP 200 for timeouts with an indication in the body w.Write([]byte(`{ "took": 30, "timed_out": true, "hits": { "total": 0, "hits": [] } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), }, { name: "elasticsearch aggregations", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "testIndex", expected: map[string]interface{}{ "WorkflowID": "test-workflow-id", }, expectErr: false, expectAgg: true, handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "took": 5, "timed_out": false, "hits": { "total": 1, "hits": [{ "_source": { "WorkflowID": "test-workflow-id" } }] }, "aggregations": { "sample_agg": { "value": 42 } } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), }, { name: "elasticsearch non exist index", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "test_failure", expectErr: true, handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) } else { w.WriteHeader(http.StatusNotFound) } }), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { elasticV7, testServer := getMockClient(t, tc.handler) defer testServer.Close() resp, err := elasticV7.Search(context.Background(), tc.index, tc.query) if !tc.expectErr { assert.NoError(t, err) assert.NotNil(t, resp) // Verify the response details assert.Equal(t, int64(5), resp.TookInMillis) assert.Equal(t, int64(1), resp.TotalHits) assert.NotNil(t, resp.Hits) assert.Len(t, resp.Hits.Hits, 1) var actual map[string]interface{} if err := json.Unmarshal([]byte(string(resp.Hits.Hits[0].Source)), &actual); err != nil { t.Fatalf("Failed to unmarshal actual JSON: %v", err) } assert.Equal(t, tc.expected, actual) if tc.expectAgg { // Verify the response includes the expected aggregations assert.NotNil(t, resp.Aggregations, "Aggregations should not be nil") assert.Contains(t, resp.Aggregations, "sample_agg", "Aggregations should contain 'sample_agg'") // Additional assertions can be made to verify the contents of the aggregation sampleAgg := resp.Aggregations["sample_agg"] var aggResult map[string]interface{} err = json.Unmarshal(sampleAgg, &aggResult) assert.NoError(t, err) assert.Equal(t, float64(42), aggResult["value"], "Aggregation 'sample_agg' should have a value of 42") } } else { assert.Error(t, err) } }) } } func TestScroll(t *testing.T) { testCases := []struct { name string query string expected map[string]interface{} expectErr bool index string handler http.HandlerFunc scrollID string }{ { name: "normal case", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, expected: map[string]interface{}{ "WorkflowID": "test-workflow-id", }, index: "testIndex", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/testIndex/_search" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "took": 5, "timed_out": false, "hits": { "total": 1, "hits": [{ "_source": { "WorkflowID": "test-workflow-id" } }] }, "aggregations": { "sample_agg": { "value": 42 } } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), expectErr: false, }, { name: "error case", query: `{"query":{"bool":{"must":{"match":{"WorkflowID":"test-workflow-id"}}}}}`, index: "testIndex", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) }), expectErr: true, scrollID: "1", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { elasticV7, testServer := getMockClient(t, tc.handler) defer testServer.Close() resp, err := elasticV7.Scroll(context.Background(), tc.index, tc.query, tc.scrollID) if !tc.expectErr { assert.NoError(t, err) var actualSource map[string]interface{} err := json.Unmarshal(resp.Hits.Hits[0].Source, &actualSource) assert.NoError(t, err) assert.Equal(t, tc.expected, actualSource) } else { assert.Error(t, err) assert.Nil(t, resp) } }) } } func TestClearScroll(t *testing.T) { var handlerCalled bool handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handlerCalled = true if r.Method == "DELETE" && r.URL.Path == "/_search/scroll" { // Simulate a successful clear scroll response w.WriteHeader(http.StatusOK) response := `{ "succeeded": true, "num_freed": 1 }` w.Write([]byte(response)) } else { w.WriteHeader(http.StatusNotFound) } }) elasticV7, testServer := getMockClient(t, handler) defer testServer.Close() err := elasticV7.ClearScroll(context.Background(), "scrollID") assert.True(t, handlerCalled, "Expected handler to be called") assert.NoError(t, err) } func TestIsNotFoundError(t *testing.T) { testCases := []struct { name string handler http.HandlerFunc expected bool }{ { name: "NotFound error", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) }), expected: true, }, { name: "Other error", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "Bad Request", http.StatusBadRequest) }), expected: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { elasticV7, testServer := getMockClient(t, tc.handler) defer testServer.Close() err := elasticV7.CreateIndex(context.Background(), "testIndex") res := elasticV7.IsNotFoundError(err) assert.Equal(t, tc.expected, res) }) } } func getMockClient(t *testing.T, handler http.HandlerFunc) (ElasticV7, *httptest.Server) { testServer := httptest.NewTLSServer(handler) mockClient, err := elastic.NewClient( elastic.SetURL(testServer.URL), elastic.SetSniff(false), elastic.SetHealthcheck(false), elastic.SetHttpClient(testServer.Client())) assert.NoError(t, err) return ElasticV7{ client: mockClient, }, testServer } ================================================ FILE: common/elasticsearch/client.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package elasticsearch import ( "context" "encoding/json" "fmt" "io" "math" "strconv" "strings" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/elasticsearch/bulk" "github.com/uber/cadence/common/elasticsearch/client" "github.com/uber/cadence/common/elasticsearch/query" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) var _ GenericClient = (*ESClient)(nil) type ESClient struct { Client client.Client Logger log.Logger } func (c *ESClient) RunBulkProcessor(ctx context.Context, p *bulk.BulkProcessorParameters) (bulk.GenericBulkProcessor, error) { return c.Client.RunBulkProcessor(ctx, p) } func (c *ESClient) CreateIndex(ctx context.Context, index string) error { return c.Client.CreateIndex(ctx, index) } func (c *ESClient) IsNotFoundError(err error) bool { return c.Client.IsNotFoundError(err) } func (c *ESClient) Search(ctx context.Context, request *SearchRequest) (*SearchResponse, error) { token, err := GetNextPageToken(request.ListRequest.NextPageToken) if err != nil { return nil, err } searchResult, err := c.getSearchResult( ctx, request.Index, request.ListRequest, request.MatchQuery, request.IsOpen, token, ) if err != nil { return nil, err } return c.getListWorkflowExecutionsResponse(searchResult, token, request.ListRequest.PageSize, request.MaxResultWindow, request.Filter) } func (c *ESClient) SearchByQuery(ctx context.Context, request *SearchByQueryRequest) (*SearchResponse, error) { token, err := GetNextPageToken(request.NextPageToken) if err != nil { return nil, err } searchResult, err := c.Client.Search(ctx, request.Index, request.Query) if err != nil { return nil, err } return c.getListWorkflowExecutionsResponse(searchResult, token, request.PageSize, request.MaxResultWindow, request.Filter) } func (c *ESClient) SearchRaw(ctx context.Context, index, query string) (*RawResponse, error) { response, err := c.Client.Search(ctx, index, query) if err != nil { return nil, err } result := RawResponse{ TookInMillis: response.TookInMillis, Hits: SearchHits{ TotalHits: response.TotalHits, Hits: c.esHitsToExecutions(response.Hits, nil /* no filter */), }, Aggregations: response.Aggregations, } return &result, nil } func (c *ESClient) esHitsToExecutions(eshits *client.SearchHits, filter IsRecordValidFilter) []*p.InternalVisibilityWorkflowExecutionInfo { var hits = make([]*p.InternalVisibilityWorkflowExecutionInfo, 0) if eshits != nil && len(eshits.Hits) > 0 { for _, hit := range eshits.Hits { workflowExecutionInfo := c.convertSearchResultToVisibilityRecord(hit) if filter == nil || filter(workflowExecutionInfo) { hits = append(hits, workflowExecutionInfo) } } } return hits } func (c *ESClient) ScanByQuery(ctx context.Context, request *ScanByQueryRequest) (*SearchResponse, error) { var err error token, err := GetNextPageToken(request.NextPageToken) if err != nil { return nil, err } searchResult, err := c.Client.Scroll(ctx, request.Index, request.Query, token.ScrollID) isLastPage := false if err == io.EOF { // no more result isLastPage = true if err := c.Client.ClearScroll(ctx, searchResult.ScrollID); err != nil { c.Logger.Warn("scroll clear failed", tag.Error(err)) } } else if err != nil && isNodeUnavailableError(err) { // scroll ID is no longer valid and points to a node that is unavailable. Query with fresh scroll c.Logger.Warn("scroll node not available for id, retrying with fresh scroll", tag.Dynamic("scrollID", token.ScrollID), tag.Error(err)) // fresh scroll res, scrollErr := c.Client.Scroll(ctx, request.Index, request.Query, "") if scrollErr != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ScanByQuery failed after fresh scroll. Error: %v", err), } } // set fresh scroll result to search result searchResult = res } else if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ScanByQuery failed. Error: %v", err), } } response := &p.InternalListWorkflowExecutionsResponse{} if searchResult == nil { return response, nil } response.Executions = c.esHitsToExecutions(searchResult.Hits, nil /* no filter */) if len(searchResult.Hits.Hits) == request.PageSize && !isLastPage { nextPageToken, err := SerializePageToken(&ElasticVisibilityPageToken{ScrollID: searchResult.ScrollID}) if err != nil { return nil, err } response.NextPageToken = make([]byte, len(nextPageToken)) copy(response.NextPageToken, nextPageToken) } return response, nil } func (c *ESClient) SearchForOneClosedExecution(ctx context.Context, index string, request *SearchForOneClosedExecutionRequest) (*SearchForOneClosedExecutionResponse, error) { matchDomainQuery := query.NewMatchQuery(DomainID, request.DomainUUID) existClosedStatusQuery := query.NewExistsQuery(CloseStatus) matchWorkflowIDQuery := query.NewMatchQuery(WorkflowID, request.Execution.GetWorkflowID()) boolQuery := query.NewBoolQuery().Must(matchDomainQuery).Must(existClosedStatusQuery).Must(matchWorkflowIDQuery) rid := request.Execution.GetRunID() if rid != "" { matchRunIDQuery := query.NewMatchQuery(RunID, rid) boolQuery = boolQuery.Must(matchRunIDQuery) } qb := query.NewBuilder() body, err := qb.Query(boolQuery).String() if err != nil { return nil, fmt.Errorf("getting body from query builder: %w", err) } searchResult, err := c.Client.Search(ctx, index, body) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("SearchForOneClosedExecution failed. Error: %v", err), } } response := &p.InternalGetClosedWorkflowExecutionResponse{} actualHits := searchResult.Hits.Hits if len(actualHits) == 0 { return response, nil } response.Execution = c.convertSearchResultToVisibilityRecord(actualHits[0]) return response, nil } func (c *ESClient) CountByQuery(ctx context.Context, index, query string) (int64, error) { return c.Client.Count(ctx, index, query) } func (c *ESClient) PutMapping(ctx context.Context, index, root, key, valueType string) error { mapping := buildPutMappingBody(root, key, valueType) body, err := json.Marshal(mapping) if err != nil { return err } return c.Client.PutMapping(ctx, index, string(body)) } func (c *ESClient) getListWorkflowExecutionsResponse( searchHits *client.Response, token *ElasticVisibilityPageToken, pageSize int, maxResultWindow int, isRecordValid func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool, ) (*p.InternalListWorkflowExecutionsResponse, error) { response := &p.InternalListWorkflowExecutionsResponse{} numOfActualHits := len(searchHits.Hits.Hits) response.Executions = make([]*p.InternalVisibilityWorkflowExecutionInfo, 0) for i := 0; i < numOfActualHits; i++ { workflowExecutionInfo := c.convertSearchResultToVisibilityRecord(searchHits.Hits.Hits[i]) if isRecordValid == nil || isRecordValid(workflowExecutionInfo) { // for old APIs like ListOpenWorkflowExecutions, we added 1 ms to range query to overcome ES limitation // (see getSearchResult function), but manually dropped records beyond request range here. response.Executions = append(response.Executions, workflowExecutionInfo) } } if numOfActualHits == pageSize { // this means the response is not the last page var nextPageToken []byte var err error // ES Search API support pagination using From and PageSize, but has limit that From+PageSize cannot exceed a threshold // to retrieve deeper pages, use ES SearchAfter if searchHits.TotalHits <= int64(maxResultWindow-pageSize) { // use ES Search From+Size nextPageToken, err = SerializePageToken(&ElasticVisibilityPageToken{From: token.From + numOfActualHits}) } else { // use ES Search After var sortVal interface{} sortVal = searchHits.Sort[0] tieBreaker := searchHits.Sort[1].(string) nextPageToken, err = SerializePageToken(&ElasticVisibilityPageToken{SortValue: sortVal, TieBreaker: tieBreaker}) } if err != nil { return nil, err } response.NextPageToken = make([]byte, len(nextPageToken)) copy(response.NextPageToken, nextPageToken) } return response, nil } func (c *ESClient) convertSearchResultToVisibilityRecord(hit *client.SearchHit) *p.InternalVisibilityWorkflowExecutionInfo { var source *VisibilityRecord err := json.Unmarshal(hit.Source, &source) if err != nil { // log and skip error return nil } record := &p.InternalVisibilityWorkflowExecutionInfo{ DomainID: source.DomainID, WorkflowType: source.WorkflowType, WorkflowID: source.WorkflowID, RunID: source.RunID, TypeName: source.WorkflowType, StartTime: time.Unix(0, source.StartTime), ExecutionTime: time.Unix(0, source.ExecutionTime), Memo: p.NewDataBlob(source.Memo, constants.EncodingType(source.Encoding)), TaskList: source.TaskList, IsCron: source.IsCron, NumClusters: source.NumClusters, ClusterAttributeScope: source.ClusterAttributeScope, ClusterAttributeName: source.ClusterAttributeName, SearchAttributes: source.Attr, CronSchedule: source.CronSchedule, ExecutionStatus: types.WorkflowExecutionStatus(source.ExecutionStatus), ScheduledExecutionTime: time.Unix(0, source.ScheduledExecutionTime), } if source.UpdateTime != 0 { record.UpdateTime = time.Unix(0, source.UpdateTime) } if source.CloseTime != 0 { record.CloseTime = time.Unix(0, source.CloseTime) record.Status = thrift.ToWorkflowExecutionCloseStatus(&source.CloseStatus) record.HistoryLength = source.HistoryLength } return record } func (c *ESClient) getSearchResult( ctx context.Context, index string, request *p.InternalListWorkflowExecutionsRequest, matchQuery *query.MatchQuery, isOpen bool, token *ElasticVisibilityPageToken, ) (*client.Response, error) { // always match domain id boolQuery := query.NewBoolQuery().Must(query.NewMatchQuery(DomainID, request.DomainUUID)) if matchQuery != nil { boolQuery = boolQuery.Must(matchQuery) } // ElasticSearch v6 is unable to precisely compare time, have to manually add resolution 1ms to time range. // Also has to use string instead of int64 to avoid data conversion issue, // 9223372036854775807 to 9223372036854776000 (long overflow) if request.LatestTime.UnixNano() > math.MaxInt64-oneMicroSecondInNano { // prevent latestTime overflow request.LatestTime = time.Unix(0, math.MaxInt64-oneMicroSecondInNano) } if request.EarliestTime.UnixNano() < math.MinInt64+oneMicroSecondInNano { // prevent earliestTime overflow request.EarliestTime = time.Unix(0, math.MinInt64+oneMicroSecondInNano) } var rangeQueryField string existClosedStatusQuery := query.NewExistsQuery(CloseStatus) if isOpen { rangeQueryField = StartTime boolQuery = boolQuery.MustNot(existClosedStatusQuery) } else { boolQuery = boolQuery.Must(existClosedStatusQuery) rangeQueryField = CloseTime } earliestTimeStr := strconv.FormatInt(request.EarliestTime.UnixNano()-oneMicroSecondInNano, 10) latestTimeStr := strconv.FormatInt(request.LatestTime.UnixNano()+oneMicroSecondInNano, 10) rangeQuery := query.NewRangeQuery(rangeQueryField).Gte(earliestTimeStr).Lte(latestTimeStr) boolQuery = boolQuery.Filter(rangeQuery) qb := query.NewBuilder() qb.Query(boolQuery).From(token.From).Size(request.PageSize) qb.Sortby( query.NewFieldSort(rangeQueryField).Desc(), query.NewFieldSort(RunID).Desc(), ) if ShouldSearchAfter(token) { qb.SearchAfter(token.SortValue, token.TieBreaker) } body, err := qb.String() if err != nil { return nil, fmt.Errorf("getting body from query builder: %w", err) } return c.Client.Search(ctx, index, body) } func buildPutMappingBody(root, key, valueType string) map[string]interface{} { body := make(map[string]interface{}) if len(root) != 0 { body["properties"] = map[string]interface{}{ root: map[string]interface{}{ "properties": map[string]interface{}{ key: map[string]interface{}{ "type": valueType, }, }, }, } } else { body["properties"] = map[string]interface{}{ key: map[string]interface{}{ "type": valueType, }, } } return body } // Helper to check if error is node unavailable error from OpenSearch func isNodeUnavailableError(err error) bool { if err == nil { return false } errMsg := err.Error() return strings.Contains(errMsg, "node") && strings.Contains(errMsg, "not available") } ================================================ FILE: common/elasticsearch/common.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package elasticsearch import ( "net/http" "time" "github.com/uber/cadence/common/config" ) const ( // TODO https://github.com/uber/cadence/issues/3686 oneMicroSecondInNano = int64(time.Microsecond / time.Nanosecond) esDocIDDelimiter = "~" esDocType = "_doc" esDocIDSizeLimit = 512 ) // Build Http Client with TLS func buildTLSHTTPClient(config config.TLS) (*http.Client, error) { tlsConfig, err := config.ToTLSConfig() if err != nil { return nil, err } // Setup HTTPS client transport := &http.Transport{TLSClientConfig: tlsConfig} tlsClient := &http.Client{Transport: transport} return tlsClient, nil } func GetESDocIDSizeLimit() int { return esDocIDSizeLimit } func GetESDocType() string { return esDocType } func GetESDocDelimiter() string { return esDocIDDelimiter } func GenerateDocID(wid, rid string) string { return wid + esDocIDDelimiter + rid } ================================================ FILE: common/elasticsearch/defs.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package elasticsearch import "github.com/uber/cadence/.gen/go/indexer" // All legal fields allowed in elastic search index const ( DomainID = "DomainID" WorkflowID = "WorkflowID" RunID = "RunID" WorkflowType = "WorkflowType" StartTime = "StartTime" ExecutionTime = "ExecutionTime" CloseTime = "CloseTime" CloseStatus = "CloseStatus" HistoryLength = "HistoryLength" Memo = "Memo" Encoding = "Encoding" TaskList = "TaskList" IsCron = "IsCron" NumClusters = "NumClusters" ClusterAttributeScope = "ClusterAttributeScope" ClusterAttributeName = "ClusterAttributeName" VisibilityOperation = "VisibilityOperation" UpdateTime = "UpdateTime" ShardID = "ShardID" CronSchedule = "CronSchedule" ExecutionStatus = "ExecutionStatus" ScheduledExecutionTime = "ScheduledExecutionTime" ) // Supported field types var ( FieldTypeString = indexer.FieldTypeString FieldTypeInt = indexer.FieldTypeInt FieldTypeBool = indexer.FieldTypeBool FieldTypeBinary = indexer.FieldTypeBinary ) ================================================ FILE: common/elasticsearch/esql/README.md ================================================ # ESQL: Translate SQL to Elasticsearch DSL Use SQL to query Elasticsearch. ES V6 compatible. ## Supported features - [x] =, !=, <, >, <=, >=, <>, () - [x] comparison with arithmetics (e.g. colA + 1 < colB * 2) - [x] arithmetic operators: +, -, *, /, %, >>, <<, () - [x] AND, OR, NOT - [x] LIKE, IN, REGEX, IS NULL, BETWEEN - [x] LIMIT, SIZE, DISTINCT - [x] COUNT, COUNT(DISTINCT) - [x] AVG, MAX, MIN, SUM - [x] GROUP BY, ORDER BY - [x] HAVING - [x] query key value macro (see usage) - [x] pagination (search after) - [ ] pagination for aggregation - [ ] functions - [ ] JOIN - [ ] nested queries ## Usage Please refer to code and comments in `esql.go`. `esql.go` contains all the apis that an outside user needs. ### Basic Usage ~~~~go sql := `SELECT COUNT(*), MAX(colA) FROM myTable WHERE colB < 10 GROUP BY colC HAVING COUNT(*) > 20` e := NewESql() dsl, _, err := e.ConvertPretty(sql) // convert sql to dsl if err == nil { fmt.Println(dsl) } ~~~~ ### Custom Query Macro ESQL support API `ProcessQueryKey` to register custom policy for colName replacement. It accepts 2 functions, the first function determines whether a colName is to be replaced, the second specifies how to do the replacement. Use case: user has custom field `field`, but to resolve confict, server stores the field as `Custom.field`. `ProcessQueryKey` API can automatically do the conversion. ESQL support API `ProcessQueryValue` to register custom policy for value processing. It accepts 2 functions, the first function determines whether a value of a colName is to be processed, the second specifies how to do the processing. Use case: user want to query time in readable format, but server stores time as an integer (unix nano). `ProcessQueryValue` API can automatically do the conversion. Below shows an example. ~~~~go sql := "SELECT colA FROM myTable WHERE colB < 10 AND dateTime = '2015-01-01T02:59:59Z'" // custom policy that change colName like "col.." to "myCol.." func myKeyFilter(colName string) bool { return strings.HasPrefix(colName, "col") } func myKeyProcess(colName string) (string, error) { return "myCol"+colName[3:], nil } // custom policy that convert formatted time string to unix nano func myValueFilter(colName string) bool { return strings.Contains(colName, "Time") || strings.Contains(colName, "time") } func myValueProcess(timeStr string) (string, error) { // convert formatted time string to unix nano integer parsedTime, _ := time.Parse(defaultDateTimeFormat, timeStr) return fmt.Sprintf("%v", parsedTime.UnixNano()), nil } // with the 2 policies , converted dsl is equivalent to // "SELECT myColA FROM myTable WHERE myColB < 10 AND dateTime = '1561678568048000000' // in which the time is in unix nano format e := NewESql() e.ProcessQueryKey(myKeyFilter, myKeyProcess) // set up macro for key e.ProcessQueryValue(myValueFilter, myValueProcess) // set up macro for value dsl, _, err := e.ConvertPretty(sql) // convert sql to dsl if err == nil { fmt.Println(dsl) } ~~~~ ### Pagination ESQL support 2 kinds of pagination: FROM keyword and ES search_after. - FROM keyword: the same as SQL syntax. Be careful, **ES only support a page smaller than 10k**, if your offset is large than 10k, search_after is necessary. - search_after: Once you know the paging tokens, just feed them to `Convert` or `ConvertPretty` API in order. Below shows an example. ~~~~go // first page sql_page1 := "SELECT * FROM myTable ORDER BY colA, colB LIMIT 10" e := NewESql() dsl_page1, sortFields, err := e.ConvertPretty(sql_page1) // second page // 1. Use FROM to retrieve the 2nd page sql_page2_FROM := "SELECT * FROM myTable ORDER BY colA, colB LIMIT 10 FROM 10" dsl_page2_FROM, sortFields, err := e.ConvertPretty(sql_page2_FROM) // 2. Use search_after to retrieve the 2nd page // we can use sortFields and the query result from page 1 to get the page tokens sql_page2_search_after := sql_page1 page_token_colA := "123" page_token_colB := "bbc" dsl_page2_search_after, sortFields, err := e.ConvertPretty(sql_page2_search_after, page_colA, page_colB) ~~~~ For Cadence usage, refer to [this link](cadenceDevReadme.md). ## ES V2.x vs ES V6.5 |Item|ES V2.x|ES v6.5| |:-:|:-:|:-:| |missing check|{"missing": {"field": "xxx"}}|{"must_not": {"exist": {"field": "xxx"}}}| |group by multiple columns|nested "aggs" field|"composite" flattened grouping| ## Attentions - If you want to apply aggregation on some fields, they should not be in type `text` in ES - `COUNT(colName)` will include documents w/ null values in that column in ES SQL API, while in esql we exclude null valued documents - ES SQL API and esql do not support `SELECT DISTINCT`, a workaround is to query something like `SELECT * FROM table GROUP BY colName` - ES SQL API does not support `ORDER BY aggregation`, esql support it by applying bucket_sort - ES SQL API does not support `HAVING aggregation` that not show up in `SELECT`, esql support it - To use regex query, the column should be `keyword` type, otherwise the regex is applied to all the terms produced by tokenizer from the original text rather than the original text itself - Comparison with arithmetics can be potentially slow since it uses scripting query and thus is not able to take advantage of reverse index. For binary operators, please refer to [this link](https://www.elastic.co/guide/en/elasticsearch/painless/6.5/painless-operators.html) on the precedence. We don't support all of them. - Comparison with arithmetics does not support date type ## Acknowledgement This project is originated from [elasticsql](https://github.com/cch123/elasticsql). Table below shows the improvement. |Item|detail| |:-:|:-:| |comparison|support comparison with arithmetics of different columns| |keyword IS|support standard SQL keywords IS NULL, IS NOT NULL for missing check| |keyword NOT|support NOT, convert NOT recursively since elasticsearch's must_not is not the same as boolean operator NOT in sql| |keyword LIKE|using "wildcard" tag, support SQL wildcard '%' and '_'| |keyword REGEX|using "regexp" tag, support standard regular expression syntax| |keyword GROUP BY|using "composite" tag to flatten multiple grouping| |keyword ORDER BY|using "bucket_sort" to support order by aggregation functions| |keyword HAVING|using "bucket_selector" and painless scripting language to support HAVING| |aggregations|allow introducing aggregation functions from all HAVING, SELECT, ORDER BY| |column name filtering|allow user pass an white list, when the sql query tries to select column out side white list, refuse the converting| |column name replacing|allow user pass an function as initializing parameter, the matched column name will be replaced upon the policy| |query value replacing|allow user pass an function as initializing parameter, query value will be processed by such function if the column name matched in filter function| |pagination|also return the sorting fields for future search after usage| |optimization|using "filter" tag rather than "must" tag to avoid scoring analysis and save time| |optimization|no redundant {"bool": {"filter": xxx}} wrapped|all queries wrapped by {"bool": {"filter": xxx}}| |optimization|does not return document contents in aggregation query| |optimization|only return fields user specifies after SELECT| ================================================ FILE: common/elasticsearch/esql/aggregation.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esql import ( "fmt" "strings" "github.com/xwb1989/sqlparser" ) func (e *ESql) convertAgg(sel sqlparser.Select) (dsl string, err error) { if len(sel.GroupBy) == 0 && sel.Having != nil { err = fmt.Errorf(`esql: HAVING used without GROUP BY`) return "", err } colNameSetGroupBy := make(map[string]int) var dslGroupBy string if len(sel.GroupBy) != 0 { dslGroupBy, colNameSetGroupBy, err = e.convertGroupByExpr(sel.GroupBy) if err != nil { return "", err } } aggFuncExprSlice, colNameSlice, aggNameSlice, err := e.extractSelectedExpr(sel.SelectExprs) if err != nil { return "", err } // verify don't select col name out side agg group name if err = e.checkSelGroupByCompatibility(colNameSlice, colNameSetGroupBy, aggNameSlice); err != nil { return "", err } // explanations for getAggSelect, getAggOrderBy, getAggHaving: // user can introduce aggregation functions from SELECT, ORDER BY and HAVING, for each different // aggregation functions, we need to add a tag for it in "aggs" field, which let ES to do the calculation // each aggregation's query body is in the form of ": {"": {"field": ""}} // // is generated by us, the convention in esql is tag = _ to prevent dup tag name // can be sum, max, min, count, avg // is the field that agg apply to // // however, for each source, there can be dups, we don't want to introduce duplicate tags // aggTagSet, aggTagOrderBySet, aggTagHavingSet are used to resolve dups, each of them is a map[string]int // which maps the tag string to an offset integer which indicates the position of this tag in // the corresponding aggxxxSlice // // aggNamexxxSlice stores agg functions names, aggTargetxxxSlice stores colNames, aggTagxxxSlice stores the tags // they are used to generate final json query // handle selected aggregation functions aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet, err := e.getAggSelect(aggFuncExprSlice) if err != nil { return "", err } // handle order by aggregation functions aggNameOrderBySlice, aggTargetOrderBySlice, aggTagOrderBySlice, aggDirOrderBySlice, aggTagOrderBySet, err := e.getAggOrderBy(sel.OrderBy) if err != nil { return "", err } // handle having aggregation functions script, aggNameHavingSlice, aggTargetHavingSlice, aggTagHavingSlice, aggTagHavingSet, err := e.getAggHaving(sel.Having) if err != nil { return "", err } // add necessary aggregations originated from order by and having for tag, i := range aggTagOrderBySet { if _, exist := aggTagSet[tag]; !exist { aggTagSet[tag] = len(aggTagSet) aggNameSlice = append(aggNameSlice, aggNameOrderBySlice[i]) aggTargetSlice = append(aggTargetSlice, aggTargetOrderBySlice[i]) aggTagSlice = append(aggTagSlice, aggTagOrderBySlice[i]) } } for tag, i := range aggTagHavingSet { if _, exist := aggTagSet[tag]; !exist { aggTagSet[tag] = len(aggTagSet) aggNameSlice = append(aggNameSlice, aggNameHavingSlice[i]) aggTargetSlice = append(aggTargetSlice, aggTargetHavingSlice[i]) aggTagSlice = append(aggTagSlice, aggTagHavingSlice[i]) } } // generate inside aggs field var dslAgg string if len(aggTagSlice) > 0 { var dslAggSlice []string for i, tag := range aggTagSlice { if tag != "_count" { dslAgg := fmt.Sprintf(`"%v": {"%v": {"field": "%v"}}`, tag, aggNameSlice[i], aggTargetSlice[i]) dslAggSlice = append(dslAggSlice, dslAgg) } } if len(aggTagOrderBySlice) > 0 { var dslOrderSlice []string for i, tag := range aggTagOrderBySlice { dslOrder := fmt.Sprintf(`{"%v": {"order": "%v"}}`, tag, aggDirOrderBySlice[i]) dslOrderSlice = append(dslOrderSlice, dslOrder) } dslAggOrder := strings.Join(dslOrderSlice, ",") dslAggOrder = fmt.Sprintf(`"bucket_sort": {"bucket_sort": {"sort": [%v], "size": %v}}`, dslAggOrder, e.bucketNumber) dslAggSlice = append(dslAggSlice, dslAggOrder) } if script != "" { var bucketPathSlice []string for tag := range aggTagHavingSet { bucketPathSlice = append(bucketPathSlice, fmt.Sprintf(`"%v": "%v"`, tag, tag)) } bucketPathStr := strings.Join(bucketPathSlice, ",") bucketFilterStr := fmt.Sprintf(`"having": {"bucket_selector": {"buckets_path": {%v}, "script": "%v"}}`, bucketPathStr, script) dslAggSlice = append(dslAggSlice, bucketFilterStr) } dslAgg = "{" + strings.Join(dslAggSlice, ",") + "}" } // generate final dsl for aggs field // here "groupby" is just a tag and can be any unreserved word if len(dslGroupBy) == 0 && len(aggTagSlice) == 0 { dsl = "" } else if len(aggTagSlice) == 0 { dsl = fmt.Sprintf(`{"groupby": {%v}}`, dslGroupBy) } else if len(dslGroupBy) == 0 { dsl = dslAgg } else { dsl = fmt.Sprintf(`{"groupby": {%v, "aggs": %v}}`, dslGroupBy, dslAgg) } return dsl, nil } func (e *ESql) checkSelGroupByCompatibility(colNameSlice []string, colNameGroupBy map[string]int, aggNameSlice []string) error { for _, aggName := range aggNameSlice { colNameGroupBy[aggName] = 1 } if len(colNameGroupBy) == 0 { return nil } for _, colNameStr := range colNameSlice { if _, exist := colNameGroupBy[colNameStr]; !exist { err := fmt.Errorf(`esql: select column %v that not in group by`, colNameStr) return err } } return nil } func (e *ESql) getAggOrderBy(orderBy sqlparser.OrderBy) ([]string, []string, []string, []string, map[string]int, error) { var aggNameSlice, aggTargetSlice, aggDirSlice, aggTagSlice []string aggTagDirSet := make(map[string]string) // tag -> asc / desc aggTagSet := make(map[string]int) // tag -> offset, for compatiblity checking aggCnt := 0 for _, orderExpr := range orderBy { switch orderExpr.Expr.(type) { case *sqlparser.FuncExpr: aggCnt++ funcExpr := orderExpr.Expr.(*sqlparser.FuncExpr) aggNameStr := strings.ToLower(funcExpr.Name.String()) // ? should we convert funcExpr.Exprs to colname? aggTargetStr := sqlparser.String(funcExpr.Exprs) aggTargetStr = strings.Trim(aggTargetStr, "`") aggTargetStr, err := e.keyProcess(aggTargetStr) if err != nil { return nil, nil, nil, nil, nil, err } var aggTagStr string switch aggNameStr { case "count": // no need to handle count(*) since the size of bucket is always returned if aggTargetStr == "*" { aggTagStr = "_count" } else if funcExpr.Distinct { aggTagStr = aggNameStr + "_distinct_" + aggTargetStr aggNameStr = "cardinality" } else { aggTagStr = aggNameStr + "_" + aggTargetStr aggNameStr = "value_count" } case "avg", "sum", "min", "max": if funcExpr.Distinct { err := fmt.Errorf(`esql: aggregation function %v w/ DISTINCT not supported`, aggNameStr) return nil, nil, nil, nil, nil, err } aggTagStr = aggNameStr + "_" + aggTargetStr default: err := fmt.Errorf(`esql: aggregation function %v not supported`, aggNameStr) return nil, nil, nil, nil, nil, err } if dir, exist := aggTagDirSet[aggTagStr]; exist { if dir != orderExpr.Direction { err := fmt.Errorf(`esql: order by aggregation direction conflict`) return nil, nil, nil, nil, nil, err } continue } aggTagStr = strings.Replace(aggTagStr, ".", "_", -1) aggTagDirSet[aggTagStr] = orderExpr.Direction aggTagSet[aggTagStr] = len(aggTagSet) aggNameSlice = append(aggNameSlice, aggNameStr) aggTargetSlice = append(aggTargetSlice, aggTargetStr) aggTagSlice = append(aggTagSlice, aggTagStr) aggDirSlice = append(aggDirSlice, orderExpr.Direction) default: } } if aggCnt > 0 && aggCnt < len(orderBy) { err := fmt.Errorf(`esql: mix order by agg functions and column names`) return nil, nil, nil, nil, nil, err } return aggNameSlice, aggTargetSlice, aggTagSlice, aggDirSlice, aggTagSet, nil } func (e *ESql) getAggSelect(exprs []*sqlparser.FuncExpr) ([]string, []string, []string, map[string]int, error) { var aggNameSlice, aggTargetSlice, aggTagSlice []string aggTagSet := make(map[string]int) // tag -> offset, for compatibility checking for _, funcExpr := range exprs { aggNameStr := strings.ToLower(funcExpr.Name.String()) aggTargetStr := sqlparser.String(funcExpr.Exprs) aggTargetStr = strings.Trim(aggTargetStr, "`") aggTargetStr, err := e.keyProcess(aggTargetStr) if err != nil { return nil, nil, nil, nil, err } var aggTagStr string switch aggNameStr { case "count": // no need to handle count(*) since the size of bucket is always returned if aggTargetStr == "*" { continue } if funcExpr.Distinct { aggTagStr = aggNameStr + "_distinct_" + aggTargetStr aggNameStr = "cardinality" } else { aggTagStr = aggNameStr + "_" + aggTargetStr // * ES SQL translate API just ignore non DISTINCT COUNT since the count of a bucket is always // * returned. However, we don't want count null value of a certain field, as a result we count // * documents w/ non-null value of the target field by "value_count" keyword aggNameStr = "value_count" } case "avg", "sum", "min", "max": if funcExpr.Distinct { err := fmt.Errorf(`esql: aggregation function %v w/ DISTINCT not supported`, aggNameStr) return nil, nil, nil, nil, err } aggTagStr = aggNameStr + "_" + aggTargetStr default: err := fmt.Errorf(`esql: aggregation function %v not supported`, aggNameStr) return nil, nil, nil, nil, err } if _, exist := aggTagSet[aggTagStr]; exist { continue } aggTagStr = strings.Replace(aggTagStr, ".", "_", -1) aggTagSet[aggTagStr] = len(aggTagSet) aggNameSlice = append(aggNameSlice, aggNameStr) aggTargetSlice = append(aggTargetSlice, aggTargetStr) aggTagSlice = append(aggTagSlice, aggTagStr) } return aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet, nil } func (e *ESql) extractSelectedExpr(expr sqlparser.SelectExprs) ([]*sqlparser.FuncExpr, []string, []string, error) { var aggFuncExprSlice []*sqlparser.FuncExpr var colNameSlice, aggNameSlice []string for _, selectExpr := range expr { // from sqlparser's definition, we need to first convert the selectExpr to AliasedExpr // and then check whether AliasedExpr is a FuncExpr or just ColName switch selectExpr.(type) { case *sqlparser.AliasedExpr: aliasedExpr := selectExpr.(*sqlparser.AliasedExpr) switch aliasedExpr.Expr.(type) { case *sqlparser.FuncExpr: funcExpr := aliasedExpr.Expr.(*sqlparser.FuncExpr) aggFuncExprSlice = append(aggFuncExprSlice, funcExpr) aggNameSlice = append(aggNameSlice, sqlparser.String(funcExpr.Exprs)) case *sqlparser.ColName: lhs := aliasedExpr.Expr.(*sqlparser.ColName) lhsStr, err := e.convertColName(lhs) if err != nil { return nil, nil, nil, err } colNameSlice = append(colNameSlice, lhsStr) default: err := fmt.Errorf(`esql: %T not supported in select body`, aliasedExpr) return nil, nil, nil, err } default: } } return aggFuncExprSlice, colNameSlice, aggNameSlice, nil } func (e *ESql) convertGroupByExpr(expr sqlparser.GroupBy) (dsl string, colNameSet map[string]int, err error) { var groupByStrSlice []string colNameSet = make(map[string]int) for _, groupByExpr := range expr { switch groupByItem := groupByExpr.(type) { case *sqlparser.ColName: colNameStr, err := e.convertColName(groupByItem) if err != nil { return "", nil, err } if _, exist := colNameSet[colNameStr]; !exist { colNameSet[colNameStr] = 1 groupByStr := fmt.Sprintf(`{"group_%v": {"terms": {"field": "%v", "missing_bucket": true}}}`, colNameStr, colNameStr) groupByStrSlice = append(groupByStrSlice, groupByStr) } default: err = fmt.Errorf(`esql: GROUP BY %T not supported`, groupByExpr) return "", nil, err } } dsl = strings.Join(groupByStrSlice, ",") dsl = fmt.Sprintf(`"composite": {"size": %v, "sources": [%v]}`, e.bucketNumber, dsl) return dsl, colNameSet, nil } ================================================ FILE: common/elasticsearch/esql/cadenceDevReadme.md ================================================ # ESQL Cadence Usage ## Motivation Currently [Cadence](https://github.com/cadence-workflow/cadence) is using [elasticsql](https://github.com/cch123/elasticsql) to translate sql query. However it only support up to ES V2.x while Cadence is using ES V6.x. Beyond that, Cadence has some specific requirements that not supported by elasticsql yet. Current Cadence query request processing steps are listed below: - generate SQL from query - use elasticsql to translate SQL to DSL - ES V6.x does not support "missing" field, convert "missing" to "bool","must_not","exist" for ExecutionTime query if any - complete "range" field for ExecutionTime query by adding {"gt": 0} - add domain query - key whitelist filtering - delete some useless field like "from", "size" - modify sorting field (add workflow id as sorting tie breaker) - setup search after for pagination ESQL aims at dealing all these addtional processing steps and providing an api to generate DSL in one step for visibility usage in Cadence. ## Usage ESQL has convert functions specific for cadence usage. Please refer to `cadencesql.go`. Below shows an example. Attention: to use cadence version api, `SetCadence()` must be called at initialzation. ~~~~go sql := "SELECT colA FROM myTable WHERE colB < 10 AND dateTime = '2015-01-01T02:59:59Z'" domainID := "CadenceSampleDomain" // custom policy that change colName like "col.." to "myCol.." func myKeyFilter(colName string) bool { return strings.HasPrefix(colName, "col") } func myKeyProcess(colName string) (string, error) { return "myCol"+colName[3:], nil } // custom policy that convert formatted time string to unix nano func myValueFilter(colName string) bool { return strings.Contains(colName, "Time") || strings.Contains(colName, "time") } func myValueProcess(timeStr string) (string, error) { // convert formatted time string to unix nano integer parsedTime, _ := time.Parse(defaultDateTimeFormat, timeStr) return fmt.Sprintf("%v", parsedTime.UnixNano()), nil } // with the 2 policies , converted dsl is equivalent to // "SELECT myColA FROM myTable WHERE myColB < 10 AND dateTime = '1561678568048000000' // in which the time is in unix nano format e := NewESql() e.SetCadence() e.ProcessQueryKey(myKeyFilter, myKeyProcess) // set up filtering policy e.ProcessQueryValue(myValueFilter, myValueProcess) // set up process policy dsl, _, err := e.ConvertPrettyCadence(sql, domainID) // convert sql to dsl if err == nil { fmt.Println(dsl) } ~~~~ ## Testing To setup local testing environment: - start cassandra service locally. Please refer to [Cadence](https://github.com/cadence-workflow/cadence) readme. - start zookeeper and kafka service locally. Here is a [referecne](https://kafka.apache.org/quickstart). - start elasticsearch and kibana service locally. - start a cadence worker by `./bin/helloworld -m worker` under cadence directory. - start cadence service locally. Please refer to [Cadence](https://github.com/cadence-workflow/cadence) readme. ================================================ FILE: common/elasticsearch/esql/cadenceSpecial.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esql import ( "fmt" "strings" "github.com/xwb1989/sqlparser" ) func (e *ESql) addCadenceSort(orderBySlice []string, sortFields []string) ([]string, []string, error) { switch len(orderBySlice) { case 0: // if unsorted, use default sorting cadenceOrderStartTime := fmt.Sprintf(`{"%v": "%v"}`, StartTime, StartTimeOrder) orderBySlice = append(orderBySlice, cadenceOrderStartTime) sortFields = append(sortFields, StartTime) case 1: // user should not use tieBreaker to sort if sortFields[0] == TieBreaker { err := fmt.Errorf("esql: Cadence does not allow user sort by RunID") return nil, nil, err } default: err := fmt.Errorf("esql: Cadence only allow 1 custom sort field") return nil, nil, err } // add tie breaker cadenceOrderTieBreaker := fmt.Sprintf(`{"%v": "%v"}`, TieBreaker, TieBreakerOrder) orderBySlice = append(orderBySlice, cadenceOrderTieBreaker) sortFields = append(sortFields, TieBreaker) return orderBySlice, sortFields, nil } func (e *ESql) addCadenceDomainTimeQuery(sel sqlparser.Select, domainID string, dslMap map[string]interface{}) { var domainIDQuery string if domainID != "" { domainIDQuery = fmt.Sprintf(`{"term": {"%v": "%v"}}`, DomainID, domainID) } if sel.Where == nil { if domainID != "" { dslMap["query"] = domainIDQuery } } else { if domainID != "" { domainIDQuery = domainIDQuery + "," } if strings.Contains(fmt.Sprintf("%v", dslMap["query"]), ExecutionTime) { executionTimeBound := fmt.Sprintf(`{"range": {"%v": {"gte": "0"}}}`, ExecutionTime) dslMap["query"] = fmt.Sprintf(`{"bool": {"filter": [%v %v, %v]}}`, domainIDQuery, executionTimeBound, dslMap["query"]) } else { dslMap["query"] = fmt.Sprintf(`{"bool": {"filter": [%v %v]}}`, domainIDQuery, dslMap["query"]) } } } ================================================ FILE: common/elasticsearch/esql/cadencesql.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esql import ( "bytes" "encoding/json" "fmt" "github.com/xwb1989/sqlparser" ) // SetCadence ... specify whether do special handling for cadence visibility // should not be called if there is potential race condition // should not be called by non-cadence user func (e *ESql) SetCadence(cadenceArg bool) { e.cadence = cadenceArg } // ConvertPrettyCadence ... // convert sql to es dsl, for cadence usage func (e *ESql) ConvertPrettyCadence(sql string, domainID string, pagination ...interface{}) (dsl string, sortFields []string, err error) { dsl, sortFields, err = e.ConvertCadence(sql, domainID, pagination...) if err != nil { return "", nil, err } var prettifiedDSLBytes bytes.Buffer err = json.Indent(&prettifiedDSLBytes, []byte(dsl), "", " ") if err != nil { return "", nil, err } return prettifiedDSLBytes.String(), sortFields, err } // ConvertCadence ... // convert sql to es dsl, for cadence usage func (e *ESql) ConvertCadence(sql string, domainID string, pagination ...interface{}) (dsl string, sortFields []string, err error) { if !e.cadence { err = fmt.Errorf(`esql: cadence option not turned on`) return "", nil, err } stmt, err := sqlparser.Parse(sql) if err != nil { return "", nil, err } // sql valid, start to handle switch stmt := stmt.(type) { case *sqlparser.Select: dsl, sortFields, err = e.convertSelect(*(stmt), domainID, pagination...) default: err = fmt.Errorf(`esql: Queries other than select not supported`) } if err != nil { return "", nil, err } return dsl, sortFields, nil } ================================================ FILE: common/elasticsearch/esql/esql.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esql import ( "bytes" "encoding/json" "fmt" "github.com/xwb1989/sqlparser" ) // ProcessFunc ... // esql use ProcessFunc to process query key value macro type ProcessFunc func(string) (string, error) // FilterFunc ... // esql use FilterFunc to determine whether a target is to be processed by ProcessFunc // only those FilterFunc(colName) == true will be processed type FilterFunc func(string) bool // ESql ... // ESql is used to hold necessary information that required in parsing type ESql struct { filterKey FilterFunc // select the column we want to process key macro filterValue FilterFunc // select the column we want to process value macro processKey ProcessFunc // if selected by filterKey, change the query name processValue ProcessFunc // if selected by filterValue, change the query value cadence bool pageSize int bucketNumber int } // NewESql ... return a new default ESql func NewESql() *ESql { return &ESql{ pageSize: DefaultPageSize, bucketNumber: DefaultBucketNumber, cadence: false, processKey: nil, processValue: nil, filterKey: nil, filterValue: nil, } } // ProcessQueryKey ... set up user specified column name processing policy // should not be called if there is potential race condition func (e *ESql) ProcessQueryKey(filterArg FilterFunc, processArg ProcessFunc) { e.filterKey = filterArg e.processKey = processArg } // ProcessQueryValue ... set up user specified column value processing policy // should not be called if there is potential race condition func (e *ESql) ProcessQueryValue(filterArg FilterFunc, processArg ProcessFunc) { e.filterValue = filterArg e.processValue = processArg } // SetPageSize ... set the number of documents returned in a non-aggregation query // should not be called if there is potential race condition func (e *ESql) SetPageSize(pageSizeArg int) { e.pageSize = pageSizeArg } // SetBucketNum ... set the number of bucket returned in an aggregation query // should not be called if there is potential race condition func (e *ESql) SetBucketNum(bucketNumArg int) { e.bucketNumber = bucketNumArg } // ConvertPretty ... // Transform sql to elasticsearch dsl, and prettify the output json // // usage: // - dsl, sortField, err := e.ConvertPretty(sql, pageParam1, pageParam2, ...) // // arguments: // - sql: the sql query needs conversion in string format // - pagination: variadic arguments that indicates es search_after for pagination // // return values: // - dsl: the elasticsearch dsl json style string // - sortField: string array that contains all column names used for sorting. useful for pagination. // - err: contains err information func (e *ESql) ConvertPretty(sql string, pagination ...interface{}) (dsl string, sortField []string, err error) { dsl, sortField, err = e.Convert(sql, pagination...) if err != nil { return "", nil, err } var prettifiedDSLBytes bytes.Buffer err = json.Indent(&prettifiedDSLBytes, []byte(dsl), "", " ") if err != nil { return "", nil, err } return prettifiedDSLBytes.String(), sortField, err } // Convert ... // Transform sql to elasticsearch dsl string // // usage: // - dsl, sortField, err := e.Convert(sql, pageParam1, pageParam2, ...) // // arguments: // - sql: the sql query needs conversion in string format // - pagination: variadic arguments that indicates es search_after // // return values: // - dsl: the elasticsearch dsl json style string // - sortField: string array that contains all column names used for sorting. useful for pagination. // - err: contains err information func (e *ESql) Convert(sql string, pagination ...interface{}) (dsl string, sortField []string, err error) { stmt, err := sqlparser.Parse(sql) if err != nil { return "", nil, err } // sql valid, start to handle switch stmt := stmt.(type) { case *sqlparser.Select: dsl, sortField, err = e.convertSelect(*(stmt), "", pagination...) default: err = fmt.Errorf(`esql: Queries other than select not supported`) } if err != nil { return "", nil, err } return dsl, sortField, nil } ================================================ FILE: common/elasticsearch/esql/esql_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or `SELl // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esql import ( "encoding/json" "fmt" "reflect" "testing" ) var sqls = []string{ `SELECT * FROM test1`, `SELECT * FROM test1 ORDER BY colE, colD DESC LIMIT 10 OFFSET 4`, `SELECT * FROM test1 WHERE colB = 'ab'`, `SELECT * FROM test1 WHERE colB = colBB`, `SELECT * FROM test1 WHERE colB = 'ab' AND colB = colBB`, `SELECT * FROM test1 WHERE colB = 'ab' AND colB != colBB`, `SELECT * FROM test1 WHERE colD = 10`, `SELECT * FROM test1 WHERE ( NOT colD = 10)`, `SELECT * FROM test1 WHERE ( NOT (colD = 10))`, `SELECT * FROM test1 WHERE colD != 10 ORDER BY colD ASC LIMIT 14`, `SELECT * FROM test1 WHERE ( NOT colD != 10)`, `SELECT * FROM test1 WHERE colD + 1 > 9`, `SELECT * FROM test1 WHERE colB = colBB AND 2 * colD > 8+2`, `SELECT * FROM test1 WHERE colB + 'c' = colA`, `SELECT * FROM test1 WHERE colB + "c" = colA`, "SELECT * FROM test1 WHERE `colB` + 'c' = colA", `SELECT * FROM test1 WHERE ((colD != 10))`, `SELECT * FROM test1 WHERE colB = 'ab' AND ExecutionTime = 2016 AND colB = 'ab'`, `SELECT * FROM test1 WHERE colB = 'ab' OR colD = 10`, `SELECT * FROM test1 WHERE colD != 10 AND colB = 'bc' OR colB = 'ab'`, `SELECT * FROM test1 WHERE colD != 10 AND NOT colB = 'bc' OR NOT (colB = 'ab' AND NOT colE < 10)`, `SELECT * FROM test1 WHERE colD != 10 AND (colB = 'bc' OR colB = 'ab')`, `SELECT * FROM test1 WHERE (colD != 10 AND colB = 'bc') OR colB = 'ab'`, `SELECT * FROM test1 WHERE ((colD != 10) AND (colB = 'bc' OR colB = 'ab'))`, `SELECT * FROM test1 WHERE NOT colD != 10 ORDER BY colE DESC, colD DESC`, `SELECT * FROM test1 WHERE NOT NOT colD != 10`, `SELECT * FROM test1 WHERE NOT NOT NOT colD != 10 ORDER BY colE, colD ASC`, `SELECT * FROM test1 WHERE NOT colD != 10 AND colB = 'bc' OR colB = 'ab'`, `SELECT * FROM test1 WHERE colD != 10 AND NOT (colB = 'bc' OR colB = 'ab')`, `SELECT * FROM test1 WHERE (colD != 10 AND colB = 'bc') OR NOT colB = 'ab'`, `SELECT * FROM test1 WHERE NOT ((colD != 10) AND (NOT colB = 'bc' OR colB = 'ab'))`, `SELECT * FROM test1 WHERE colE > 3 AND colD <= 15`, `SELECT * FROM test1 WHERE colE < 5 OR colD >= 17 ORDER BY colE DESC, colD ASC`, `SELECT * FROM test1 WHERE NOT (colE >= 5 AND colD < 17)`, `SELECT * FROM test1 WHERE NOT colE >= 5 AND colD < 17`, `SELECT * FROM test1 WHERE colE <= 9 OR colD >= 6`, `SELECT * FROM test1 WHERE NOT (colE > 9 AND colD < 6)`, `SELECT * FROM test1 WHERE colE > 0 OR colD <= 21.000`, `SELECT colC FROM test1 WHERE ExecutionTime IS NULL ORDER BY colD`, `SELECT * FROM test1 WHERE colB IS NOT NULL ORDER BY colE`, `SELECT * FROM test1 WHERE ExecutionTime IS NULL AND colD IS NOT NULL`, `SELECT * FROM test1 WHERE NOT ExecutionTime IS NULL OR colD IS NOT NULL`, `SELECT * FROM test1 WHERE colB = 'ab' AND (ExecutionTime IS NULL OR colD IS NOT NULL)`, `SELECT * FROM test1 WHERE NOT ExecutionTime IS NULL`, `SELECT * FROM test1 WHERE NOT ExecutionTime IS NOT NULL`, `SELECT ExecutionTime FROM test1 WHERE NOT colE BETWEEN 4 AND 15`, `SELECT * FROM test1 WHERE colE BETWEEN 3 AND 12`, `SELECT * FROM test1 WHERE NOT colE BETWEEN 3 AND 15 AND colD < 9 OR NOT colB != 'aa'`, `SELECT colA FROM test1 WHERE colB IN ('aa', 'ab', 'bb')`, `SELECT ExecutionTime FROM test1 WHERE colB NOT IN ('ab', 'bb') AND ExecutionTime IS NOT NULL`, `SELECT * FROM test1 WHERE colB NOT IN ('ab', 'bb') AND NOT (colE > 8 OR NOT colD <> 10)`, `SELECT colB FROM test1 GROUP BY colB ORDER BY colB`, `SELECT colB, colA FROM test1 GROUP BY colB, colB, colA, colA`, `SELECT colB FROM test1 WHERE colE > 6 and ExecutionTime IS NOT NULL GROUP BY colB`, `SELECT * FROM test1 WHERE colC REGEXP '[ab]{3} a{2}[ab] b+'`, `SELECT * FROM test1 WHERE colB LIKE '_a_' OR colB LIKE 'b%'`, `SELECT colB, colA FROM test1 GROUP BY colB, colA`, `SELECT COUNT(DISTINCT colB) FROM test1`, `SELECT COUNT(DISTINCT colB), COUNT(colB) FROM test1`, `SELECT COUNT(DISTINCT colB), COUNT(colB), COUNT(DISTINCT colB), COUNT(colB) FROM test1`, `SELECT COUNT(colA), COUNT(colB) FROM test1`, `SELECT COUNT(*), COUNT(colB) FROM test1`, `SELECT COUNT(DISTINCT colB), AVG(colE) FROM test1`, `SELECT COUNT(*) FROM test1`, `SELECT COUNT(colB) FROM test1`, `SELECT MIN(colE) FROM test1`, `SELECT colA AS a FROM test1`, "SELECT `colA` FROM `test1` WHERE `colB` != 'ab'", "SELECT `colA` FROM `test1` WHERE `colB` != \"ab\"", "SELECT `colA` FROM `test1` WHERE `colE` BETWEEN 2 AND 10", `SELECT COUNT(colB), AVG(colD), MAX(colE) FROM test1`, `SELECT AVG(colE) FROM test1 GROUP BY colB`, `SELECT AVG(colE), COUNT(*), colB FROM test1 GROUP BY colB, colA`, `SELECT AVG(colE), COUNT(ExecutionTime) FROM test1 GROUP BY colB, colA`, `SELECT AVG(colE), COUNT(colA) FROM test1 GROUP BY colB, colA`, `SELECT COUNT(DISTINCT colE) FROM test1 GROUP BY colB, colA`, `SELECT MAX(colD), AVG(colE), COUNT(colA) FROM test1 GROUP BY colB, colA`, `SELECT MAX(colD), AVG(colE), COUNT(colA) FROM test1 WHERE ExecutionTime IS NOT NULL GROUP BY colB, colA`, `SELECT MAX(colD), AVG(colE), MIN(colD) FROM test1 WHERE ExecutionTime IS NOT NULL AND NOT colD < 2 GROUP BY colB, colA`, `SELECT MAX(colD), AVG(colE), MIN(colD) FROM test1 WHERE colE > 1 GROUP BY colB, colA`, `SELECT AVG(colE) FROM test1 GROUP BY colB ORDER BY AVG(colE)`, `SELECT AVG(colE) FROM test1 GROUP BY colB ORDER BY AVG(colE) DESC`, `SELECT AVG(colE) FROM test1 GROUP BY colB ORDER BY AVG(colE) ASC`, `SELECT AVG(colE) FROM test1 GROUP BY colB ORDER BY COUNT(*) ASC`, `SELECT AVG(colE) FROM test1 GROUP BY colB ORDER BY COUNT(colA) ASC`, `SELECT AVG(colE) FROM test1 GROUP BY colB ORDER BY COUNT(colA) ASC, COUNT(colA) ASC`, `SELECT AVG(colE) FROM test1 GROUP BY colB ORDER BY COUNT(DISTINCT colA) DESC`, `SELECT AVG(colE) FROM test1 GROUP BY colB ORDER BY COUNT(DISTINCT colA) DESC, COUNT(colA), COUNT(*) DESC`, `SELECT AVG(colE), COUNT(colE), COUNT(DISTINCT colE) FROM test1 GROUP BY colB ORDER BY COUNT(colE), COUNT(DISTINCT colE) ASC`, `SELECT COUNT(DISTINCT colA) FROM test1 GROUP BY colB ORDER BY COUNT(colA), COUNT(*), COUNT(DISTINCT colA)`, `SELECT AVG(colE) FROM test1 GROUP BY colB HAVING MAX(colD) > 4`, `SELECT AVG(colE) FROM test1 GROUP BY colB HAVING COUNT(colD) > 4`, `SELECT AVG(colE) FROM test1 GROUP BY colB HAVING COUNT(*) > 4`, `SELECT AVG(colE) FROM test1 GROUP BY colB HAVING COUNT(DISTINCT colA) > 2`, `SELECT AVG(colE) FROM test1 GROUP BY colB HAVING MAX(colD) > MIN(colE)`, `SELECT AVG(colE) FROM test1 GROUP BY colB HAVING COUNT(DISTINCT colA) > MIN(colE)`, `SELECT AVG(colE) FROM test1 GROUP BY colB HAVING MAX(colD) > COUNT(colD)`, `SELECT AVG(colE) FROM test1 WHERE colD > 2 GROUP BY colB HAVING MAX(colD) > COUNT(colD) OR MAX(colD) < AVG(colE) AND COUNT(colD) = COUNT(colD) OR COUNT(colD) <> MAX(colD)`, `SELECT COUNT(DISTINCT colB) FROM test1 GROUP BY colB`, `SELECT AVG(colE) FROM test1 GROUP BY colB ORDER BY MAX(colD)`, `SELECT * FROM test1 GROUP BY colB HAVING COUNT(*) > COUNT(colA)`, `SELECT * FROM test1 GROUP BY colB HAVING NOT (COUNT(*) > COUNT(colA))`, `SELECT * FROM test1 GROUP BY colB HAVING COUNT(*) BETWEEN 0 AND 50`, } var dsls = []string{ `{"size": 1000}`, `{"size": 10,"from": 4,"sort": [{"colE": "asc"},{"colD": "desc"}]}`, `{"query": {"term": {"colB": "ab"}},"size": 1000}`, `{"query": {"bool": {"filter": {"script": {"script": {"source": "doc['colB'].value == doc['colBB'].value"}}}}},"size": 1000}`, `{"query": {"bool": {"filter": [{"term": {"colB": "ab"}},{"bool": {"filter": {"script": {"script": {"source": "doc['colB'].value == doc['colBB'].value"}}}}}]}},"size": 1000}`, `{"query": {"bool": {"filter": [{"term": {"colB": "ab"}},{"bool": {"filter": {"script": {"script": {"source": "doc['colB'].value !== doc['colBB'].value"}}}}}]}},"size": 1000}`, `{"query": {"term": {"colD": "10"}},"size": 1000}`, `{"query": {"bool": {"must_not": {"term": {"colD": "10"}}}},"size": 1000}`, `{"query": {"bool": {"must_not": {"term": {"colD": "10"}}}},"size": 1000}`, `{"size": 14,"sort": [{"colD": "asc"}],"query": {"bool": {"must_not": {"term": {"colD": "10"}}}}}`, `{"query": {"term": {"colD": "10"}},"size": 1000}`, `{"query": {"bool": {"filter": {"script": {"script": {"source": "doc['colD'].value + 1 > 9"}}}}},"size": 1000}`, `{"query": {"bool": {"filter": [{"bool": {"filter": {"script": {"script": {"source": "doc['colB'].value == doc['colBB'].value"}}}}},{"bool": {"filter": {"script": {"script": {"source": "2 * doc['colD'].value > 8 + 2"}}}}}]}},"size": 1000}`, `{"query": {"bool": {"filter": {"script": {"script": {"source": "doc['colB'].value + 'c' == doc['colA'].value"}}}}},"size": 1000}`, `{"size": 1000,"query": {"bool": {"filter": {"script": {"script": {"source": "doc['colB'].value + 'c' == doc['colA'].value"}}}}}}`, `{"query": {"bool": {"filter": {"script": {"script": {"source": "doc['colB'].value + 'c' == doc['colA'].value"}}}}},"size": 1000}`, `{"query": {"bool": {"must_not": {"term": {"colD": "10"}}}},"size": 1000}`, `{"query": {"bool": {"filter": [{"term": {"colB": "ab"}},{"term": {"ExecutionTime": "2016"}},{"term": {"colB": "ab"}}]}},"size": 1000}`, `{"query": {"bool": {"should": [{"term": {"colB": "ab"}},{"term": {"colD": "10"}}]}},"size": 1000}`, `{"query": {"bool": {"should": [{"bool": {"filter": [{"bool": {"must_not": {"term": {"colD": "10"}}}},{"term": {"colB": "bc"}}]}},{"term": {"colB": "ab"}}]}},"size": 1000}`, `{"query": {"bool": {"should": [{"bool": {"filter": [{"bool": {"must_not": {"term": {"colD": "10"}}}},{"bool": {"must_not": {"term": {"colB": "bc"}}}}]}},{"bool": {"must_not": {"term": {"colB": "ab"}}}},{"range": {"colE": {"lt": "10"}}}]}},"size": 1000}`, `{"query": {"bool": {"filter": [{"bool": {"must_not": {"term": {"colD": "10"}}}},{"bool": {"should": [{"term": {"colB": "bc"}},{"term": {"colB": "ab"}}]}}]}},"size": 1000}`, `{"query": {"bool": {"should": [{"bool": {"filter": [{"bool": {"must_not": {"term": {"colD": "10"}}}},{"term": {"colB": "bc"}}]}},{"term": {"colB": "ab"}}]}},"size": 1000}`, `{"query": {"bool": {"filter": [{"bool": {"must_not": {"term": {"colD": "10"}}}},{"bool": {"should": [{"term": {"colB": "bc"}},{"term": {"colB": "ab"}}]}}]}},"size": 1000}`, `{"query": {"term": {"colD": "10"}},"size": 1000,"sort": [{"colE": "desc"},{"colD": "desc"}]}`, `{"query": {"bool": {"must_not": {"term": {"colD": "10"}}}},"size": 1000}`, `{"query": {"term": {"colD": "10"}},"size": 1000,"sort": [{"colE": "asc"},{"colD": "asc"}]}`, `{"query": {"bool": {"should": [{"bool": {"filter": [{"term": {"colD": "10"}},{"term": {"colB": "bc"}}]}},{"term": {"colB": "ab"}}]}},"size": 1000}`, `{"query": {"bool": {"filter": [{"bool": {"must_not": {"term": {"colD": "10"}}}},{"bool": {"must_not": {"term": {"colB": "bc"}}}},{"bool": {"must_not": {"term": {"colB": "ab"}}}}]}},"size": 1000}`, `{"query": {"bool": {"should": [{"bool": {"filter": [{"bool": {"must_not": {"term": {"colD": "10"}}}},{"term": {"colB": "bc"}}]}},{"bool": {"must_not": {"term": {"colB": "ab"}}}}]}},"size": 1000}`, `{"query": {"bool": {"should": [{"term": {"colD": "10"}},{"bool": {"filter": [{"term": {"colB": "bc"}},{"bool": {"must_not": {"term": {"colB": "ab"}}}}]}}]}},"size": 1000}`, `{"query": {"bool": {"filter": [{"range": {"colE": {"gt": "3"}}},{"range": {"colD": {"lte": "15"}}}]}},"size": 1000}`, `{"query": {"bool": {"should": [{"range": {"colE": {"lt": "5"}}},{"range": {"colD": {"gte": "17"}}}]}},"size": 1000,"sort": [{"colE": "desc"},{"colD": "asc"}]}`, `{"query": {"bool": {"should": [{"range": {"colE": {"lt": "5"}}},{"range": {"colD": {"gte": "17"}}}]}},"size": 1000}`, `{"query": {"bool": {"filter": [{"range": {"colE": {"lt": "5"}}},{"range": {"colD": {"lt": "17"}}}]}},"size": 1000}`, `{"query": {"bool": {"should": [{"range": {"colE": {"lte": "9"}}},{"range": {"colD": {"gte": "6"}}}]}},"size": 1000}`, `{"query": {"bool": {"should": [{"range": {"colE": {"lte": "9"}}},{"range": {"colD": {"gte": "6"}}}]}},"size": 1000}`, `{"query": {"bool": {"should": [{"range": {"colE": {"gt": "0"}}},{"range": {"colD": {"lte": "21.000"}}}]}},"size": 1000}`, `{"size": 1000,"sort": [{"colD": "asc"}],"query": {"bool": {"must_not": {"exists": {"field": "ExecutionTime"}}}},"_source": {"includes": ["colC"]}}`, `{"query": {"exists": {"field": "colB"}},"size": 1000,"sort": [{"colE": "asc"}]}`, `{"query": {"bool": {"filter": [{"bool": {"must_not": {"exists": {"field": "ExecutionTime"}}}},{"exists": {"field": "colD"}}]}},"size": 1000}`, `{"size": 1000,"query": {"bool": {"should": [{"exists": {"field": "ExecutionTime"}},{"exists": {"field": "colD"}}]}}}`, `{"query": {"bool": {"filter": [{"term": {"colB": "ab"}},{"bool": {"should": [{"bool": {"must_not": {"exists": {"field": "ExecutionTime"}}}},{"exists": {"field": "colD"}}]}}]}},"size": 1000}`, `{"query": {"exists": {"field": "ExecutionTime"}},"size": 1000}`, `{"size": 1000,"query": {"bool": {"must_not": {"exists": {"field": "ExecutionTime"}}}}}`, `{"query": {"bool": {"must_not": [{"range": {"colE": {"gte": "4", "lte": "15"}}}]}},"_source": {"includes": ["ExecutionTime"]},"size": 1000}`, `{"query": {"range": {"colE": {"gte": "3", "lte": "12"}}},"size": 1000}`, `{"query": {"bool": {"should": [{"bool": {"filter": [{"bool": {"must_not": [{"range": {"colE": {"gte": "3", "lte": "15"}}}]}},{"range": {"colD": {"lt": "9"}}}]}},{"term": {"colB": "aa"}}]}},"size": 1000}`, `{"query": {"terms": {"colB": ["aa", "ab", "bb"]}},"_source": {"includes": ["colA"]},"size": 1000}`, `{"query": {"bool": {"filter": [{"bool": {"must_not": {"terms": {"colB": ["ab", "bb"]}}}},{"exists": {"field": "ExecutionTime"}}]}},"_source": {"includes": ["ExecutionTime"]},"size": 1000}`, `{"query": {"bool": {"filter": [{"bool": {"must_not": {"terms": {"colB": ["ab", "bb"]}}}},{"range": {"colE": {"lte": "8"}}},{"bool": {"must_not": {"term": {"colD": "10"}}}}]}},"size": 1000}`, `{"_source": {"includes": ["colB"]},"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}},{"group_colA": {"terms": {"field": "colA", "missing_bucket": true}}}]}}},"size": 0,"_source": {"includes": ["colB", "colA"]}}`, `{"query": {"bool": {"filter": [{"range": {"colE": {"gt": "6"}}},{"exists": {"field": "ExecutionTime"}}]}},"_source": {"includes": ["colB"]},"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}}},"size": 0}`, `{"query": {"regexp": {"colC": "[ab]{3} a{2}[ab] b+"}},"size": 1000}`, `{"size": 1000,"query": {"bool": {"should": [{"wildcard": {"colB": {"wildcard": "?a?"}}},{"wildcard": {"colB": {"wildcard": "b*"}}}]}}}`, `{"_source": {"includes": ["colB", "colA"]},"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}},{"group_colA": {"terms": {"field": "colA", "missing_bucket": true}}}]}}},"size": 0}`, `{"size": 0,"aggs": {"count_distinct_colB": {"cardinality": {"field": "colB"}}}}`, `{"size": 0,"aggs": {"count_distinct_colB": {"cardinality": {"field": "colB"}},"count_colB": {"value_count": {"field": "colB"}}}}`, `{"aggs": {"count_distinct_colB": {"cardinality": {"field": "colB"}},"count_colB": {"value_count": {"field": "colB"}}},"size": 0}`, `{"size": 0,"aggs": {"count_colA": {"value_count": {"field": "colA"}},"count_colB": {"value_count": {"field": "colB"}}}}`, `{"aggs": {"count_colB": {"value_count": {"field": "colB"}}},"size": 0}`, `{"size": 0,"aggs": {"count_distinct_colB": {"cardinality": {"field": "colB"}},"avg_colE": {"avg": {"field": "colE"}}}}`, `{"size": 0}`, `{"aggs": {"count_colB": {"value_count": {"field": "colB"}}},"size": 0}`, `{"aggs": {"min_colE": {"min": {"field": "colE"}}},"size": 0}`, `{"_source": {"includes": ["colA"]},"size": 1000}`, `{"_source": {"includes": ["colA"]},"size": 1000,"query": {"bool": {"must_not": {"term": {"colB": "ab"}}}}}`, `{"query": {"bool": {"must_not": {"term": {"colB": "ab"}}}},"_source": {"includes": ["colA"]},"size": 1000}`, `{"_source": {"includes": ["colA"]},"size": 1000,"query": {"range": {"colE": {"gte": "2", "lte": "10"}}}}`, `{"aggs": {"count_colB": {"value_count": {"field": "colB"}},"avg_colD": {"avg": {"field": "colD"}},"max_colE": {"max": {"field": "colE"}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}}}}},"size": 0}`, `{"_source": {"includes": ["colB"]},"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}},{"group_colA": {"terms": {"field": "colA", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}},{"group_colA": {"terms": {"field": "colA", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"count_ExecutionTime": {"value_count": {"field": "ExecutionTime"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}},{"group_colA": {"terms": {"field": "colA", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"count_colA": {"value_count": {"field": "colA"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}},{"group_colA": {"terms": {"field": "colA", "missing_bucket": true}}}]}, "aggs": {"count_distinct_colE": {"cardinality": {"field": "colE"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}},{"group_colA": {"terms": {"field": "colA", "missing_bucket": true}}}]}, "aggs": {"max_colD": {"max": {"field": "colD"}},"avg_colE": {"avg": {"field": "colE"}},"count_colA": {"value_count": {"field": "colA"}}}}},"size": 0}`, `{"query": {"exists": {"field": "ExecutionTime"}},"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}},{"group_colA": {"terms": {"field": "colA", "missing_bucket": true}}}]}, "aggs": {"max_colD": {"max": {"field": "colD"}},"avg_colE": {"avg": {"field": "colE"}},"count_colA": {"value_count": {"field": "colA"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}},{"group_colA": {"terms": {"field": "colA", "missing_bucket": true}}}]}, "aggs": {"max_colD": {"max": {"field": "colD"}},"avg_colE": {"avg": {"field": "colE"}},"min_colD": {"min": {"field": "colD"}}}}},"size": 0,"query": {"bool": {"filter": [{"exists": {"field": "ExecutionTime"}},{"range": {"colD": {"gte": "2"}}}]}}}`, `{"query": {"range": {"colE": {"gt": "1"}}},"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}},{"group_colA": {"terms": {"field": "colA", "missing_bucket": true}}}]}, "aggs": {"max_colD": {"max": {"field": "colD"}},"avg_colE": {"avg": {"field": "colE"}},"min_colD": {"min": {"field": "colD"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"bucket_sort": {"bucket_sort": {"sort": [{"avg_colE": {"order": "asc"}}], "size": 1000}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"bucket_sort": {"bucket_sort": {"sort": [{"avg_colE": {"order": "desc"}}], "size": 1000}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"bucket_sort": {"bucket_sort": {"sort": [{"avg_colE": {"order": "asc"}}], "size": 1000}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"bucket_sort": {"bucket_sort": {"sort": [{"_count": {"order": "asc"}}], "size": 1000}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"count_colA": {"value_count": {"field": "colA"}},"bucket_sort": {"bucket_sort": {"sort": [{"count_colA": {"order": "asc"}}], "size": 1000}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"count_colA": {"value_count": {"field": "colA"}},"bucket_sort": {"bucket_sort": {"sort": [{"count_colA": {"order": "asc"}}], "size": 1000}}}}},"size": 0}`, `{"size": 0,"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"count_distinct_colA": {"cardinality": {"field": "colA"}},"bucket_sort": {"bucket_sort": {"sort": [{"count_distinct_colA": {"order": "desc"}}], "size": 1000}}}}}}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"count_distinct_colA": {"cardinality": {"field": "colA"}},"count_colA": {"value_count": {"field": "colA"}},"bucket_sort": {"bucket_sort": {"sort": [{"count_distinct_colA": {"order": "desc"}},{"count_colA": {"order": "asc"}},{"_count": {"order": "desc"}}], "size": 1000}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"count_colE": {"value_count": {"field": "colE"}},"count_distinct_colE": {"cardinality": {"field": "colE"}},"bucket_sort": {"bucket_sort": {"sort": [{"count_colE": {"order": "asc"}},{"count_distinct_colE": {"order": "asc"}}], "size": 1000}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"count_distinct_colA": {"cardinality": {"field": "colA"}},"count_colA": {"value_count": {"field": "colA"}},"bucket_sort": {"bucket_sort": {"sort": [{"count_colA": {"order": "asc"}},{"_count": {"order": "asc"}},{"count_distinct_colA": {"order": "asc"}}], "size": 1000}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"max_colD": {"max": {"field": "colD"}},"having": {"bucket_selector": {"buckets_path": {"max_colD": "max_colD"}, "script": "params.max_colD > 4"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"count_colD": {"value_count": {"field": "colD"}},"having": {"bucket_selector": {"buckets_path": {"count_colD": "count_colD"}, "script": "params.count_colD > 4"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"having": {"bucket_selector": {"buckets_path": {"_count": "_count"}, "script": "params._count > 4"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"count_distinct_colA": {"cardinality": {"field": "colA"}},"having": {"bucket_selector": {"buckets_path": {"count_distinct_colA": "count_distinct_colA"}, "script": "params.count_distinct_colA > 2"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"max_colD": {"max": {"field": "colD"}},"min_colE": {"min": {"field": "colE"}},"having": {"bucket_selector": {"buckets_path": {"max_colD": "max_colD","min_colE": "min_colE"}, "script": "params.max_colD > params.min_colE"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"count_distinct_colA": {"cardinality": {"field": "colA"}},"min_colE": {"min": {"field": "colE"}},"having": {"bucket_selector": {"buckets_path": {"count_distinct_colA": "count_distinct_colA","min_colE": "min_colE"}, "script": "params.count_distinct_colA > params.min_colE"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"max_colD": {"max": {"field": "colD"}},"count_colD": {"value_count": {"field": "colD"}},"having": {"bucket_selector": {"buckets_path": {"count_colD": "count_colD","max_colD": "max_colD"}, "script": "params.max_colD > params.count_colD"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"max_colD": {"max": {"field": "colD"}},"count_colD": {"value_count": {"field": "colD"}},"having": {"bucket_selector": {"buckets_path": {"avg_colE": "avg_colE","max_colD": "max_colD","count_colD": "count_colD"}, "script": "params.max_colD > params.count_colD || params.max_colD < params.avg_colE && params.count_colD == params.count_colD || params.count_colD !== params.max_colD"}}}}},"size": 0,"query": {"range": {"colD": {"gt": "2"}}}}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"count_distinct_colB": {"cardinality": {"field": "colB"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"avg_colE": {"avg": {"field": "colE"}},"max_colD": {"max": {"field": "colD"}},"bucket_sort": {"bucket_sort": {"sort": [{"max_colD": {"order": "asc"}}], "size": 1000}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"count_colA": {"value_count": {"field": "colA"}},"having": {"bucket_selector": {"buckets_path": {"_count": "_count","count_colA": "count_colA"}, "script": "params._count > params.count_colA"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"count_colA": {"value_count": {"field": "colA"}},"having": {"bucket_selector": {"buckets_path": {"_count": "_count","count_colA": "count_colA"}, "script": "!(params._count > params.count_colA)"}}}}},"size": 0}`, `{"aggs": {"groupby": {"composite": {"size": 1000, "sources": [{"group_colB": {"terms": {"field": "colB", "missing_bucket": true}}}]}, "aggs": {"having": {"bucket_selector": {"buckets_path": {"_count": "_count"}, "script": "(params._count >= 0 && params._count <= 50)"}}}}},"size": 0}`, } func TestUnit(t *testing.T) { e := NewESql() for i, sql := range sqls { fmt.Printf("test %dth query ...\n", i+1) dsl, _, err := e.Convert(sql) if err != nil { t.Errorf("%vth query fails: %v", i+1, err) return } var dslMap, dslMapRef map[string]interface{} err = json.Unmarshal([]byte(dsl), &dslMap) if err != nil { t.Errorf("%vth query fails", i+1) return } err = json.Unmarshal([]byte(dsls[i]), &dslMapRef) if err != nil { t.Errorf("%vth query reference fails", i+1) return } if !reflect.DeepEqual(dslMap, dslMapRef) { t.Errorf("%vth query does not match", i+1) return } } } ================================================ FILE: common/elasticsearch/esql/globals.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esql import "github.com/xwb1989/sqlparser" // used for invert operator when NOT is specified var oppositeOperator = map[string]string{ "=": "!=", "!=": "=", "<": ">=", "<=": ">", ">": "<=", ">=": "<", "<>": "=", "in": "not in", "like": "not like", "regexp": "not regexp", "not in": "in", "not like": "like", "not regexp": "regexp", sqlparser.IsNullStr: sqlparser.IsNotNullStr, sqlparser.IsNotNullStr: sqlparser.IsNullStr, sqlparser.BetweenStr: sqlparser.NotBetweenStr, sqlparser.NotBetweenStr: sqlparser.BetweenStr, } // used for convert SQL operator to painless operator in HAVING expression var op2PainlessOp = map[string]string{ "=": "==", "!=": "!==", "<": "<", "<=": "<=", ">": ">", ">=": ">=", "<>": "!==", } var opBinaryExpr = map[string]string{ "|": "|", "&": "&", "^": "^", "+": "+", "-": "-", "*": "*", "/": "/", "%": "%", ">>": ">>", "<<": "<<", } // default sizes and identifiers used in cadence visibility const ( DefaultPageSize = 1000 DefaultBucketNumber = 1000 ESDefaultMaxPageSize = 10000 TieBreaker = "RunID" RunID = "RunID" StartTime = "StartTime" DomainID = "DomainID" WorkflowID = "WorkflowID" ExecutionTime = "ExecutionTime" TieBreakerOrder = "desc" StartTimeOrder = "desc" ) ================================================ FILE: common/elasticsearch/esql/having.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esql import ( "fmt" "strings" "github.com/xwb1989/sqlparser" ) func (e *ESql) getAggHaving(having *sqlparser.Where) (string, []string, []string, []string, map[string]int, error) { var aggNameSlice, aggTargetSlice, aggTagSlice []string aggTagSet := make(map[string]int) var script string var err error if having != nil { script, err = e.convertHavingExpr(having.Expr, &aggNameSlice, &aggTargetSlice, &aggTagSlice, aggTagSet) if err != nil { return "", nil, nil, nil, nil, err } } return script, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet, nil } func (e *ESql) convertHavingExpr(expr sqlparser.Expr, aggNameSlice *[]string, aggTargetSlice *[]string, aggTagSlice *[]string, aggTagSet map[string]int) (string, error) { switch expr.(type) { case *sqlparser.ComparisonExpr: return e.convertHavingComparisionExpr(expr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) case *sqlparser.AndExpr: return e.convertHavingAndExpr(expr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) case *sqlparser.OrExpr: return e.convertHavingOrExpr(expr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) case *sqlparser.NotExpr: return e.convertHavingNotExpr(expr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) case *sqlparser.ParenExpr: return e.convertHavingParenExpr(expr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) case *sqlparser.RangeCond: return e.convertHavingBetweenExpr(expr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) default: err := fmt.Errorf(`esql: %T expression in HAVING no supported`, expr) return "", err } } func (e *ESql) convertHavingBetweenExpr(expr sqlparser.Expr, aggNameSlice *[]string, aggTargetSlice *[]string, aggTagSlice *[]string, aggTagSet map[string]int) (string, error) { rangeCond := expr.(*sqlparser.RangeCond) lhs := rangeCond.Left from, to := rangeCond.From, rangeCond.To var expr1 sqlparser.Expr = &sqlparser.ComparisonExpr{Left: lhs, Right: from, Operator: ">="} var expr2 sqlparser.Expr = &sqlparser.ComparisonExpr{Left: lhs, Right: to, Operator: "<="} var expr3 sqlparser.Expr = &sqlparser.AndExpr{Left: expr1, Right: expr2} script, err := e.convertHavingAndExpr(expr3, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) if err != nil { return "", err } // here parenthesis is to deal with the case when an not(!) operator out side // if no parenthesis, NOT xxx BETWEEN a and b -> !xxx > a && xxx < b script = fmt.Sprintf(`(%v)`, script) return script, nil } func (e *ESql) convertHavingAndExpr(expr sqlparser.Expr, aggNameSlice *[]string, aggTargetSlice *[]string, aggTagSlice *[]string, aggTagSet map[string]int) (string, error) { andExpr := expr.(*sqlparser.AndExpr) leftExpr := andExpr.Left rightExpr := andExpr.Right scriptLeft, err := e.convertHavingExpr(leftExpr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) if err != nil { return "", err } scriptRight, err := e.convertHavingExpr(rightExpr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) if err != nil { return "", err } return fmt.Sprintf(`%v && %v`, scriptLeft, scriptRight), nil } func (e *ESql) convertHavingOrExpr(expr sqlparser.Expr, aggNameSlice *[]string, aggTargetSlice *[]string, aggTagSlice *[]string, aggTagSet map[string]int) (string, error) { orExpr := expr.(*sqlparser.OrExpr) leftExpr := orExpr.Left rightExpr := orExpr.Right scriptLeft, err := e.convertHavingExpr(leftExpr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) if err != nil { return "", err } scriptRight, err := e.convertHavingExpr(rightExpr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) if err != nil { return "", err } return fmt.Sprintf(`%v || %v`, scriptLeft, scriptRight), nil } func (e *ESql) convertHavingParenExpr(expr sqlparser.Expr, aggNameSlice *[]string, aggTargetSlice *[]string, aggTagSlice *[]string, aggTagSet map[string]int) (string, error) { parenExpr := expr.(*sqlparser.ParenExpr) script, err := e.convertHavingExpr(parenExpr.Expr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) if err != nil { return "", err } return fmt.Sprintf(`(%v)`, script), nil } func (e *ESql) convertHavingNotExpr(expr sqlparser.Expr, aggNameSlice *[]string, aggTargetSlice *[]string, aggTagSlice *[]string, aggTagSet map[string]int) (string, error) { notExpr := expr.(*sqlparser.NotExpr) script, err := e.convertHavingExpr(notExpr.Expr, aggNameSlice, aggTargetSlice, aggTagSlice, aggTagSet) if err != nil { return "", err } return fmt.Sprintf(`!%v`, script), nil } func (e *ESql) convertHavingComparisionExpr(expr sqlparser.Expr, aggNameSlice *[]string, aggTargetSlice *[]string, aggTagSlice *[]string, aggTagSet map[string]int) (string, error) { comparisonExpr := expr.(*sqlparser.ComparisonExpr) var funcExprs []*sqlparser.FuncExpr if _, exist := op2PainlessOp[comparisonExpr.Operator]; !exist { err := fmt.Errorf(`esql: %s operator not supported in having comparison clause`, comparisonExpr.Operator) return "", err } // convert SQL operator format to equivalent painless operator op := op2PainlessOp[comparisonExpr.Operator] // lhs leftFuncExpr, ok := comparisonExpr.Left.(*sqlparser.FuncExpr) if !ok { err := fmt.Errorf("esql: found %v in HAVING which is not aggregation function", sqlparser.String(comparisonExpr.Left)) return "", err } funcExprs = append(funcExprs, leftFuncExpr) // rhs, can be a value or an aggregation function var rhsStr, script string switch comparisonExpr.Right.(type) { case *sqlparser.SQLVal: rhsStr = sqlparser.String(comparisonExpr.Right) rhsStr = strings.Trim(rhsStr, `'`) case *sqlparser.FuncExpr: rightFuncExpr := comparisonExpr.Right.(*sqlparser.FuncExpr) funcExprs = append(funcExprs, rightFuncExpr) default: err := fmt.Errorf("esql: %T in HAVING rhs not supported", comparisonExpr.Right) return "", err } for _, funcExpr := range funcExprs { aggNameStr := strings.ToLower(funcExpr.Name.String()) aggTargetStr := sqlparser.String(funcExpr.Exprs) aggTargetStr = strings.Trim(aggTargetStr, "`") aggTargetStr, err := e.keyProcess(aggTargetStr) if err != nil { return "", err } var aggTagStr string switch aggNameStr { case "count": if aggTargetStr == "*" { aggTagStr = "_count" } else if funcExpr.Distinct { aggTagStr = aggNameStr + "_distinct_" + aggTargetStr aggNameStr = "cardinality" } else { aggTagStr = aggNameStr + "_" + aggTargetStr aggNameStr = "value_count" } case "avg", "sum", "min", "max": if funcExpr.Distinct { err := fmt.Errorf(`esql: HAVING: aggregation function %v w/ DISTINCT not supported`, aggNameStr) return "", err } aggTagStr = aggNameStr + "_" + aggTargetStr default: err := fmt.Errorf(`esql: HAVING: aggregation function %v not supported`, aggNameStr) return "", err } aggTagStr = strings.Replace(aggTagStr, ".", "_", -1) aggTagSet[aggTagStr] = len(*aggNameSlice) *aggNameSlice = append(*aggNameSlice, aggNameStr) *aggTargetSlice = append(*aggTargetSlice, aggTargetStr) *aggTagSlice = append(*aggTagSlice, aggTagStr) } n := len(*aggTagSlice) if rhsStr == "" { script = fmt.Sprintf(`params.%v %v params.%v`, (*aggTagSlice)[n-2], op, (*aggTagSlice)[n-1]) } else { script = fmt.Sprintf(`params.%v %v %v`, (*aggTagSlice)[n-1], op, rhsStr) } return script, nil } ================================================ FILE: common/elasticsearch/esql/scriptQuery.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esql import ( "fmt" "github.com/xwb1989/sqlparser" ) func (e *ESql) convertToScript(expr sqlparser.Expr) (script string, err error) { switch expr := expr.(type) { case *sqlparser.ColName: script, err = e.convertColName(expr) script = fmt.Sprintf(`doc['%v'].value`, script) case *sqlparser.SQLVal: script, err = e.convertValExpr(expr, true) case *sqlparser.BinaryExpr: script, err = e.convertBinaryExpr(expr) case *sqlparser.ParenExpr: script, err = e.convertToScript(expr.Expr) script = fmt.Sprintf(`(%v)`, script) default: err = fmt.Errorf("esql: invalid expression type for scripting") } if err != nil { return "", err } return script, nil } func (e *ESql) convertBinaryExpr(expr sqlparser.Expr) (string, error) { var script, lhsStr, rhsStr string var err error binExpr, ok := expr.(*sqlparser.BinaryExpr) if !ok { err = fmt.Errorf("esql: invalid binary expression") return "", err } lhsExpr, rhsExpr := binExpr.Left, binExpr.Right op, ok := opBinaryExpr[binExpr.Operator] if !ok { err = fmt.Errorf("esql: not supported binary expression operator") return "", err } lhsStr, err = e.convertToScript(lhsExpr) if err != nil { return "", err } rhsStr, err = e.convertToScript(rhsExpr) if err != nil { return "", err } script = fmt.Sprintf(`%v %v %v`, lhsStr, op, rhsStr) return script, nil } ================================================ FILE: common/elasticsearch/esql/select.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esql import ( "fmt" "strings" "github.com/xwb1989/sqlparser" ) func (e *ESql) convertSelect(sel sqlparser.Select, domainID string, pagination ...interface{}) (dsl string, sortField []string, err error) { if sel.Distinct != "" { err := fmt.Errorf(`esql: SELECT DISTINCT not supported. use GROUP BY instead`) return "", nil, err } var rootParent sqlparser.Expr // a map that contains the main components of a query dslMap := make(map[string]interface{}) // handle WHERE keyword if sel.Where != nil { dslQuery, err := e.convertWhereExpr(sel.Where.Expr, rootParent) if err != nil { return "", nil, err } dslMap["query"] = dslQuery } // cadence special handling: add domain ID query and time query bounds if e.cadence { e.addCadenceDomainTimeQuery(sel, domainID, dslMap) } // handle FROM keyword, currently only support 1 target table if len(sel.From) != 1 { if len(sel.From) == 0 { err = fmt.Errorf("esql: invalid from expressino: no from expression specified") } else { err = fmt.Errorf("esql: join not supported") } return "", nil, err } // handle SELECT keyword _, selectedColNameSlice, aggNameSlice, err := e.extractSelectedExpr(sel.SelectExprs) if err != nil { return "", nil, err } if len(selectedColNameSlice) > 0 { colNames := `"` + strings.Join(selectedColNameSlice, `", "`) + `"` dslMap["_source"] = fmt.Sprintf(`{"includes": [%v]}`, colNames) } // handle all aggregations, including GROUP BY, SELECT , ORDER BY , HAVING dslAgg, err := e.convertAgg(sel) if err != nil { return "", nil, err } if dslAgg != "" || len(aggNameSlice) > 0 { if dslAgg != "" { dslMap["aggs"] = dslAgg } // do not return document contents if this is an aggregation query dslMap["size"] = 0 } else { // handle LIMIT and OFFSET keyword, these 2 keywords only works in non-aggregation query dslMap["size"] = e.pageSize if sel.Limit != nil { if sel.Limit.Offset != nil { dslMap["from"] = sqlparser.String(sel.Limit.Offset) } dslMap["size"] = sqlparser.String(sel.Limit.Rowcount) } // handle pagination var searchAfterSlice []string for _, v := range pagination { switch v.(type) { case int: searchAfterSlice = append(searchAfterSlice, fmt.Sprintf(`%v`, v)) default: searchAfterSlice = append(searchAfterSlice, fmt.Sprintf(`"%v"`, v)) } } if len(searchAfterSlice) > 0 { searchAfterStr := strings.Join(searchAfterSlice, ",") dslMap["search_after"] = fmt.Sprintf(`[%v]`, searchAfterStr) } } // handle ORDER BY // if it is an aggregate query, no point to order if _, exist := dslMap["aggs"]; !exist && len(aggNameSlice) == 0 { var orderBySlice []string for _, orderExpr := range sel.OrderBy { var colNameStr string if colName, ok := orderExpr.Expr.(*sqlparser.ColName); ok { colNameStr, err = e.convertColName(colName) if err != nil { return "", nil, err } } else { err := fmt.Errorf(`esql: mix order by aggregations and column names`) return "", nil, err } colNameStr = strings.Trim(colNameStr, "`") orderByStr := fmt.Sprintf(`{"%v": "%v"}`, colNameStr, orderExpr.Direction) orderBySlice = append(orderBySlice, orderByStr) sortField = append(sortField, colNameStr) } // cadence special handling: add runID as sorting tie breaker if e.cadence { orderBySlice, sortField, err = e.addCadenceSort(orderBySlice, sortField) if err != nil { return "", nil, err } } if len(orderBySlice) > 0 { dslMap["sort"] = fmt.Sprintf("[%v]", strings.Join(orderBySlice, ",")) } } // generate the final json query var dslQuerySlice []string for tag, content := range dslMap { dslQuerySlice = append(dslQuerySlice, fmt.Sprintf(`"%v": %v`, tag, content)) } dsl = "{" + strings.Join(dslQuerySlice, ",") + "}" return dsl, sortField, nil } func (e *ESql) convertWhereExpr(expr sqlparser.Expr, parent sqlparser.Expr) (string, error) { var err error if expr == nil { err = fmt.Errorf("esql: invalid where expression, where expression should not be nil") return "", err } switch expr.(type) { case *sqlparser.ComparisonExpr: return e.convertComparisionExpr(expr, parent, false) case *sqlparser.AndExpr: return e.convertAndExpr(expr, parent) case *sqlparser.OrExpr: return e.convertOrExpr(expr, parent) case *sqlparser.ParenExpr: return e.convertParenExpr(expr, parent) case *sqlparser.NotExpr: return e.convertNotExpr(expr, parent) case *sqlparser.RangeCond: return e.convertBetweenExpr(expr, parent, true, true, false) case *sqlparser.IsExpr: return e.convertIsExpr(expr, parent, false) default: err = fmt.Errorf(`esql: %T expression not supported in WHERE clause`, expr) return "", err } } func (e *ESql) convertBetweenExpr(expr sqlparser.Expr, parent sqlparser.Expr, fromInclusive bool, toInclusive bool, not bool) (string, error) { rangeCond := expr.(*sqlparser.RangeCond) lhs, ok := rangeCond.Left.(*sqlparser.ColName) if !ok { err := fmt.Errorf("esql: invalid range column name") return "", err } lhsStr, err := e.convertColName(lhs) if err != nil { return "", err } fromStr := strings.Trim(sqlparser.String(rangeCond.From), `'`) toStr := strings.Trim(sqlparser.String(rangeCond.To), `'`) op := rangeCond.Operator if not { op = oppositeOperator[op] } gt := "gte" lt := "lte" if !fromInclusive { gt = "gt" } if !toInclusive { lt = "lt" } dsl := fmt.Sprintf(`{"range": {"%v": {"%v": "%v", "%v": "%v"}}}`, lhsStr, gt, fromStr, lt, toStr) if op == sqlparser.NotBetweenStr { dsl = fmt.Sprintf(`{"bool": {"must_not": [%v]}}`, dsl) } return dsl, nil } func (e *ESql) convertParenExpr(expr sqlparser.Expr, parent sqlparser.Expr) (string, error) { exprInside := expr.(*sqlparser.ParenExpr).Expr return e.convertWhereExpr(exprInside, expr) } // * dsl must_not is not an equivalent to sql NOT, should convert the inside expression accordingly func (e *ESql) convertNotExpr(expr sqlparser.Expr, parent sqlparser.Expr) (string, error) { notExpr := expr.(*sqlparser.NotExpr) exprInside := notExpr.Expr switch exprInside := (exprInside).(type) { case *sqlparser.NotExpr: expr1 := exprInside expr2 := expr1.Expr return e.convertWhereExpr(expr2, parent) case *sqlparser.AndExpr: expr1 := exprInside var exprLeft sqlparser.Expr = &sqlparser.NotExpr{Expr: expr1.Left} var exprRight sqlparser.Expr = &sqlparser.NotExpr{Expr: expr1.Right} var expr2 sqlparser.Expr = &sqlparser.OrExpr{Left: exprLeft, Right: exprRight} return e.convertOrExpr(expr2, parent) case *sqlparser.OrExpr: expr1 := exprInside var exprLeft sqlparser.Expr = &sqlparser.NotExpr{Expr: expr1.Left} var exprRight sqlparser.Expr = &sqlparser.NotExpr{Expr: expr1.Right} var expr2 sqlparser.Expr = &sqlparser.AndExpr{Left: exprLeft, Right: exprRight} return e.convertAndExpr(expr2, parent) case *sqlparser.ParenExpr: expr1 := exprInside exprBody := expr1.Expr var expr2 sqlparser.Expr = &sqlparser.NotExpr{Expr: exprBody} return e.convertNotExpr(expr2, parent) case *sqlparser.ComparisonExpr: return e.convertComparisionExpr(exprInside, parent, true) case *sqlparser.IsExpr: return e.convertIsExpr(exprInside, parent, true) case *sqlparser.RangeCond: return e.convertBetweenExpr(exprInside, parent, true, true, true) default: err := fmt.Errorf("esql: %T expression not supported", exprInside) return "", err } } func (e *ESql) convertAndExpr(expr sqlparser.Expr, parent sqlparser.Expr) (string, error) { andExpr := expr.(*sqlparser.AndExpr) lhsExpr := andExpr.Left rhsExpr := andExpr.Right lhsStr, err := e.convertWhereExpr(lhsExpr, expr) if err != nil { return "", err } rhsStr, err := e.convertWhereExpr(rhsExpr, expr) if err != nil { return "", err } var dsl string if lhsStr == "" || rhsStr == "" { dsl = lhsStr + rhsStr } else { dsl = lhsStr + `,` + rhsStr } // merge chained AND expression if _, ok := parent.(*sqlparser.AndExpr); ok { return dsl, nil } return fmt.Sprintf(`{"bool": {"filter": [%v]}}`, dsl), nil } func (e *ESql) convertOrExpr(expr sqlparser.Expr, parent sqlparser.Expr) (string, error) { orExpr := expr.(*sqlparser.OrExpr) lhsExpr := orExpr.Left rhsExpr := orExpr.Right lhsStr, err := e.convertWhereExpr(lhsExpr, expr) if err != nil { return "", err } rhsStr, err := e.convertWhereExpr(rhsExpr, expr) if err != nil { return "", err } var dsl string if lhsStr == "" || rhsStr == "" { dsl = lhsStr + rhsStr } else { dsl = lhsStr + `,` + rhsStr } // merge chained OR expression if _, ok := parent.(*sqlparser.OrExpr); ok { return dsl, nil } return fmt.Sprintf(`{"bool": {"should": [%v]}}`, dsl), nil } func (e *ESql) convertIsExpr(expr sqlparser.Expr, parent sqlparser.Expr, not bool) (string, error) { isExpr := expr.(*sqlparser.IsExpr) lhs, ok := isExpr.Expr.(*sqlparser.ColName) if !ok { return "", fmt.Errorf("esql: is expression only support colname missing check") } lhsStr, err := e.convertColName(lhs) if err != nil { return "", err } dsl := "" op := isExpr.Operator if not { if _, exist := oppositeOperator[op]; !exist { err := fmt.Errorf("esql: is expression only support is null and is not null") return "", err } op = oppositeOperator[op] } switch op { case sqlparser.IsNullStr: dsl = fmt.Sprintf(`{"bool": {"must_not": {"exists": {"field": "%v"}}}}`, lhsStr) case sqlparser.IsNotNullStr: dsl = fmt.Sprintf(`{"exists": {"field": "%v"}}`, lhsStr) default: return "", fmt.Errorf("esql: is expression only support is null and is not null") } return dsl, nil } func (e *ESql) convertComparisionExpr(expr sqlparser.Expr, parent sqlparser.Expr, not bool) (string, error) { // extract lhs, and check lhs is a colName var err error scriptQuery := false comparisonExpr := expr.(*sqlparser.ComparisonExpr) lhsExpr, rhsExpr := comparisonExpr.Left, comparisonExpr.Right var lhsStr, rhsStr, dsl string // get operator op := comparisonExpr.Operator if not { if _, exist := oppositeOperator[op]; !exist { err := fmt.Errorf(`esql: %s operator not supported in comparison clause`, comparisonExpr.Operator) return "", err } op = oppositeOperator[op] } if _, ok := lhsExpr.(*sqlparser.ColName); !ok { scriptQuery = true } switch rhsExpr.(type) { case *sqlparser.SQLVal, sqlparser.ValTuple: rhsStr, err = e.convertValExpr(rhsExpr, false) if err != nil { return "", err } rhsStr, err = e.valueProcess(lhsStr, rhsStr) if err != nil { return "", err } default: scriptQuery = true } // use painless scripting query here if scriptQuery { lhsStr, err = e.convertToScript(lhsExpr) if err != nil { return "", err } rhsStr, err = e.convertToScript(rhsExpr) if err != nil { return "", err } op, ok := op2PainlessOp[op] if !ok { err = fmt.Errorf("esql: not supported painless operator") return "", err } script := fmt.Sprintf(`%v %v %v`, lhsStr, op, rhsStr) dsl = fmt.Sprintf(`{"bool": {"filter": {"script": {"script": {"source": "%v"}}}}}`, script) return dsl, nil } lhs := lhsExpr.(*sqlparser.ColName) lhsStr, err = e.convertColName(lhs) if err != nil { return "", err } // generate dsl according to operator switch op { case "=": dsl = fmt.Sprintf(`{"term": {"%v": "%v"}}`, lhsStr, rhsStr) case "<": dsl = fmt.Sprintf(`{"range": {"%v": {"lt": "%v"}}}`, lhsStr, rhsStr) case "<=": dsl = fmt.Sprintf(`{"range": {"%v": {"lte": "%v"}}}`, lhsStr, rhsStr) case ">": dsl = fmt.Sprintf(`{"range": {"%v": {"gt": "%v"}}}`, lhsStr, rhsStr) case ">=": dsl = fmt.Sprintf(`{"range": {"%v": {"gte": "%v"}}}`, lhsStr, rhsStr) case "<>", "!=": dsl = fmt.Sprintf(`{"bool": {"must_not": {"term": {"%v": "%v"}}}}`, lhsStr, rhsStr) case "in": rhsStr = strings.Replace(rhsStr, `'`, `"`, -1) rhsStr = strings.Trim(rhsStr, "(") rhsStr = strings.Trim(rhsStr, ")") dsl = fmt.Sprintf(`{"terms": {"%v": [%v]}}`, lhsStr, rhsStr) case "not in": rhsStr = strings.Replace(rhsStr, `'`, `"`, -1) rhsStr = strings.Trim(rhsStr, "(") rhsStr = strings.Trim(rhsStr, ")") dsl = fmt.Sprintf(`{"bool": {"must_not": {"terms": {"%v": [%v]}}}}`, lhsStr, rhsStr) case "like": rhsStr = strings.Replace(rhsStr, `_`, `?`, -1) rhsStr = strings.Replace(rhsStr, `%`, `*`, -1) dsl = fmt.Sprintf(`{"wildcard": {"%v": {"wildcard": "%v"}}}`, lhsStr, rhsStr) case "not like": rhsStr = strings.Replace(rhsStr, `_`, `?`, -1) rhsStr = strings.Replace(rhsStr, `%`, `*`, -1) dsl = fmt.Sprintf(`{"bool": {"must_not": {"wildcard": {"%v": {"wildcard": "%v"}}}}}`, lhsStr, rhsStr) case "regexp": dsl = fmt.Sprintf(`{"regexp": {"%v": "%v"}}`, lhsStr, rhsStr) case "not regexp": dsl = fmt.Sprintf(`{"bool": {"must_not": {"regexp": {"%v": "%v"}}}}`, lhsStr, rhsStr) default: err := fmt.Errorf(`esql: %s operator not supported in comparison clause`, comparisonExpr.Operator) return "", err } return dsl, nil } func (e *ESql) convertValExpr(expr sqlparser.Expr, script bool) (dsl string, err error) { switch expr.(type) { case *sqlparser.SQLVal: dsl = sqlparser.String(expr) if !script { dsl = strings.Trim(dsl, `'`) } // ValTuple is not a pointer from sqlparser case sqlparser.ValTuple: dsl = sqlparser.String(expr) default: err = fmt.Errorf("esql: not supported rhs expression %T", expr) return "", err } return dsl, nil } func (e *ESql) convertColName(colName *sqlparser.ColName) (string, error) { // here we garuantee colName is of type *ColName colNameStr := sqlparser.String(colName) replacedColNameStr, err := e.keyProcess(colNameStr) if err != nil { return "", err } replacedColNameStr = strings.Replace(replacedColNameStr, "`", "", -1) return replacedColNameStr, nil } func (e *ESql) keyProcess(target string) (string, error) { if e.filterKey != nil && e.filterKey(target) && e.processKey != nil { target, err := e.processKey(target) if err != nil { return "", err } return target, nil } return target, nil } func (e *ESql) valueProcess(colName string, value string) (string, error) { if e.filterValue != nil && e.filterValue(colName) && e.processValue != nil { value, err := e.processValue(value) if err != nil { return "", err } return value, nil } return value, nil } ================================================ FILE: common/elasticsearch/interfaces.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package elasticsearch import ( "context" "encoding/json" "fmt" "net/http" esaws "github.com/olivere/elastic/aws/v4" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/elasticsearch/bulk" esc "github.com/uber/cadence/common/elasticsearch/client" "github.com/uber/cadence/common/elasticsearch/client/os2" v6 "github.com/uber/cadence/common/elasticsearch/client/v6" v7 "github.com/uber/cadence/common/elasticsearch/client/v7" "github.com/uber/cadence/common/elasticsearch/query" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" p "github.com/uber/cadence/common/persistence" ) // NewGenericClient create a ES client func NewGenericClient( connectConfig *config.ElasticSearchConfig, logger log.Logger, ) (*ESClient, error) { if connectConfig.Version == "" { connectConfig.Version = "v6" } var tlsClient *http.Client var signingAWSClient *http.Client if connectConfig.AWSSigning.Enable { creds, region, err := connectConfig.AWSSigning.GetCredentials() if err != nil { return nil, fmt.Errorf("getting AWS credentials: %w", err) } signingAWSClient = esaws.NewV4SigningClient(creds, *region) } if connectConfig.TLS.Enabled { var err error tlsClient, err = buildTLSHTTPClient(connectConfig.TLS) if err != nil { return nil, err } } var esClient esc.Client var err error // this logger is only used for client-internal purposes, e.g. request errors, which are generally lack detailed context. // it is currently (unfortunately) shared between both queries and bulk-updates. clientLogger := logger.WithTags(tag.ComponentESVisibilityClient) switch connectConfig.Version { case "v6": esClient, err = v6.NewV6Client(connectConfig, clientLogger, tlsClient, signingAWSClient) case "v7": esClient, err = v7.NewV7Client(connectConfig, clientLogger, tlsClient, signingAWSClient) case "os2": esClient, err = os2.NewClient(connectConfig, clientLogger, tlsClient) default: return nil, fmt.Errorf("not supported ElasticSearch version: %v", connectConfig.Version) } if err != nil { return nil, err } return &ESClient{ Client: esClient, Logger: logger, }, nil } type ( // GenericClient is a generic interface for all versions of ElasticSearch clients GenericClient interface { // Search API is only for supporting various List[Open/Closed]WorkflowExecutions(ByXyz). // Use SearchByQuery or ScanByQuery for generic purpose searching. Search(ctx context.Context, request *SearchRequest) (*SearchResponse, error) // SearchByQuery is the generic purpose searching SearchByQuery(ctx context.Context, request *SearchByQueryRequest) (*SearchResponse, error) // SearchRaw is for searching with raw json. Returns RawResult object which is subset of ESv6 and ESv7 response SearchRaw(ctx context.Context, index, query string) (*RawResponse, error) // ScanByQuery is also generic purpose searching, but implemented with ScrollService of ElasticSearch, // which is more performant for pagination, but comes with some limitation of in-parallel requests. ScanByQuery(ctx context.Context, request *ScanByQueryRequest) (*SearchResponse, error) // TODO remove it in https://github.com/uber/cadence/issues/3682 SearchForOneClosedExecution(ctx context.Context, index string, request *SearchForOneClosedExecutionRequest) (*SearchForOneClosedExecutionResponse, error) // CountByQuery is for returning the count of workflow executions that match the query CountByQuery(ctx context.Context, index, query string) (int64, error) // RunBulkProcessor returns a processor for adding/removing docs into ElasticSearch index RunBulkProcessor(ctx context.Context, p *bulk.BulkProcessorParameters) (bulk.GenericBulkProcessor, error) // PutMapping adds new field type to the index PutMapping(ctx context.Context, index, root, key, valueType string) error // CreateIndex creates a new index CreateIndex(ctx context.Context, index string) error IsNotFoundError(err error) bool } // SearchRequest is request for Search SearchRequest struct { Index string ListRequest *p.InternalListWorkflowExecutionsRequest IsOpen bool Filter IsRecordValidFilter MatchQuery *query.MatchQuery MaxResultWindow int } // SearchByQueryRequest is request for SearchByQuery SearchByQueryRequest struct { Index string Query string NextPageToken []byte PageSize int Filter IsRecordValidFilter MaxResultWindow int } // ScanByQueryRequest is request for SearchByQuery ScanByQueryRequest struct { Index string Query string NextPageToken []byte PageSize int } // SearchResponse is a response to Search, SearchByQuery and ScanByQuery SearchResponse = p.InternalListWorkflowExecutionsResponse // SearchForOneClosedExecutionRequest is request for SearchForOneClosedExecution SearchForOneClosedExecutionRequest = p.InternalGetClosedWorkflowExecutionRequest // SearchForOneClosedExecutionResponse is response for SearchForOneClosedExecution SearchForOneClosedExecutionResponse = p.InternalGetClosedWorkflowExecutionResponse // VisibilityRecord is a struct of doc for deserialization VisibilityRecord struct { WorkflowID string RunID string WorkflowType string DomainID string StartTime int64 ExecutionTime int64 CloseTime int64 CloseStatus workflow.WorkflowExecutionCloseStatus HistoryLength int64 Memo []byte Encoding string TaskList string IsCron bool NumClusters int16 ClusterAttributeScope string ClusterAttributeName string UpdateTime int64 Attr map[string]interface{} CronSchedule string ExecutionStatus int32 ScheduledExecutionTime int64 } SearchHits struct { TotalHits int64 Hits []*p.InternalVisibilityWorkflowExecutionInfo } RawResponse struct { TookInMillis int64 Hits SearchHits Aggregations map[string]json.RawMessage } // IsRecordValidFilter is a function to filter visibility records IsRecordValidFilter func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool ) ================================================ FILE: common/elasticsearch/mocks/GenericClient.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import ( context "context" mock "github.com/stretchr/testify/mock" elasticsearch "github.com/uber/cadence/common/elasticsearch" bulk "github.com/uber/cadence/common/elasticsearch/bulk" persistence "github.com/uber/cadence/common/persistence" ) // GenericClient is an autogenerated mock type for the GenericClient type type GenericClient struct { mock.Mock } // CountByQuery provides a mock function with given fields: ctx, index, query func (_m *GenericClient) CountByQuery(ctx context.Context, index string, query string) (int64, error) { ret := _m.Called(ctx, index, query) var r0 int64 if rf, ok := ret.Get(0).(func(context.Context, string, string) int64); ok { r0 = rf(ctx, index, query) } else { r0 = ret.Get(0).(int64) } var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { r1 = rf(ctx, index, query) } else { r1 = ret.Error(1) } return r0, r1 } // CreateIndex provides a mock function with given fields: ctx, index func (_m *GenericClient) CreateIndex(ctx context.Context, index string) error { ret := _m.Called(ctx, index) var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, index) } else { r0 = ret.Error(0) } return r0 } // IsNotFoundError provides a mock function with given fields: err func (_m *GenericClient) IsNotFoundError(err error) bool { ret := _m.Called(err) var r0 bool if rf, ok := ret.Get(0).(func(error) bool); ok { r0 = rf(err) } else { r0 = ret.Get(0).(bool) } return r0 } // PutMapping provides a mock function with given fields: ctx, index, root, key, valueType func (_m *GenericClient) PutMapping(ctx context.Context, index string, root string, key string, valueType string) error { ret := _m.Called(ctx, index, root, key, valueType) var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) error); ok { r0 = rf(ctx, index, root, key, valueType) } else { r0 = ret.Error(0) } return r0 } // RunBulkProcessor provides a mock function with given fields: ctx, p func (_m *GenericClient) RunBulkProcessor(ctx context.Context, p *bulk.BulkProcessorParameters) (bulk.GenericBulkProcessor, error) { ret := _m.Called(ctx, p) var r0 bulk.GenericBulkProcessor if rf, ok := ret.Get(0).(func(context.Context, *bulk.BulkProcessorParameters) bulk.GenericBulkProcessor); ok { r0 = rf(ctx, p) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(bulk.GenericBulkProcessor) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *bulk.BulkProcessorParameters) error); ok { r1 = rf(ctx, p) } else { r1 = ret.Error(1) } return r0, r1 } // ScanByQuery provides a mock function with given fields: ctx, request func (_m *GenericClient) ScanByQuery(ctx context.Context, request *elasticsearch.ScanByQueryRequest) (*persistence.InternalListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.InternalListWorkflowExecutionsResponse if rf, ok := ret.Get(0).(func(context.Context, *elasticsearch.ScanByQueryRequest) *persistence.InternalListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.InternalListWorkflowExecutionsResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *elasticsearch.ScanByQueryRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // Search provides a mock function with given fields: ctx, request func (_m *GenericClient) Search(ctx context.Context, request *elasticsearch.SearchRequest) (*persistence.InternalListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.InternalListWorkflowExecutionsResponse if rf, ok := ret.Get(0).(func(context.Context, *elasticsearch.SearchRequest) *persistence.InternalListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.InternalListWorkflowExecutionsResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *elasticsearch.SearchRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } func (_m *GenericClient) SearchRaw(ctx context.Context, index string, query string) (*elasticsearch.RawResponse, error) { ret := _m.Called(ctx, index, query) var r0 *elasticsearch.RawResponse if rf, ok := ret.Get(0).(func(context.Context, string, string) *elasticsearch.RawResponse); ok { r0 = rf(ctx, index, query) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*elasticsearch.RawResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { r1 = rf(ctx, index, query) } else { r1 = ret.Error(1) } return r0, r1 } // SearchByQuery provides a mock function with given fields: ctx, request func (_m *GenericClient) SearchByQuery(ctx context.Context, request *elasticsearch.SearchByQueryRequest) (*persistence.InternalListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.InternalListWorkflowExecutionsResponse if rf, ok := ret.Get(0).(func(context.Context, *elasticsearch.SearchByQueryRequest) *persistence.InternalListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.InternalListWorkflowExecutionsResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *elasticsearch.SearchByQueryRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // SearchForOneClosedExecution provides a mock function with given fields: ctx, index, request func (_m *GenericClient) SearchForOneClosedExecution(ctx context.Context, index string, request *persistence.InternalGetClosedWorkflowExecutionRequest) (*persistence.InternalGetClosedWorkflowExecutionResponse, error) { ret := _m.Called(ctx, index, request) var r0 *persistence.InternalGetClosedWorkflowExecutionResponse if rf, ok := ret.Get(0).(func(context.Context, string, *persistence.InternalGetClosedWorkflowExecutionRequest) *persistence.InternalGetClosedWorkflowExecutionResponse); ok { r0 = rf(ctx, index, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.InternalGetClosedWorkflowExecutionResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, *persistence.InternalGetClosedWorkflowExecutionRequest) error); ok { r1 = rf(ctx, index, request) } else { r1 = ret.Error(1) } return r0, r1 } ================================================ FILE: common/elasticsearch/page_token.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package elasticsearch import ( "bytes" "encoding/json" "fmt" "github.com/uber/cadence/common/types" ) type ( // ElasticVisibilityPageToken holds the paging token for ElasticSearch ElasticVisibilityPageToken struct { // for ES API From+Size From int // for ES API searchAfter SortValue interface{} TieBreaker string // runID // for ES scroll API ScrollID string } ) // DeserializePageToken return the structural token func DeserializePageToken(data []byte) (*ElasticVisibilityPageToken, error) { var token ElasticVisibilityPageToken dec := json.NewDecoder(bytes.NewReader(data)) dec.UseNumber() err := dec.Decode(&token) if err != nil { return nil, &types.BadRequestError{ Message: fmt.Sprintf("unable to deserialize page token. err: %v", err), } } return &token, nil } // SerializePageToken return the token blob func SerializePageToken(token *ElasticVisibilityPageToken) ([]byte, error) { data, err := json.Marshal(token) if err != nil { return nil, &types.BadRequestError{ Message: fmt.Sprintf("unable to serialize page token. err: %v", err), } } return data, nil } // GetNextPageToken returns the structural token with nil handling func GetNextPageToken(token []byte) (*ElasticVisibilityPageToken, error) { var result *ElasticVisibilityPageToken var err error if len(token) > 0 { result, err = DeserializePageToken(token) if err != nil { return nil, err } } else { result = &ElasticVisibilityPageToken{} } return result, nil } // ShouldSearchAfter decides if should search after func ShouldSearchAfter(token *ElasticVisibilityPageToken) bool { return token.TieBreaker != "" } ================================================ FILE: common/elasticsearch/query/bool_query.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query type Query interface { // Source returns the JSON-serializable query request. Source() (interface{}, error) } type BoolQuery struct { mustClauses []Query mustNotClauses []Query filterClauses []Query } // Creates a new bool query. func NewBoolQuery() *BoolQuery { return &BoolQuery{ mustClauses: make([]Query, 0), mustNotClauses: make([]Query, 0), filterClauses: make([]Query, 0), } } func (q *BoolQuery) Must(queries ...Query) *BoolQuery { q.mustClauses = append(q.mustClauses, queries...) return q } func (q *BoolQuery) MustNot(queries ...Query) *BoolQuery { q.mustNotClauses = append(q.mustNotClauses, queries...) return q } func (q *BoolQuery) Filter(filters ...Query) *BoolQuery { q.filterClauses = append(q.filterClauses, filters...) return q } func (q *BoolQuery) Source() (interface{}, error) { query := make(map[string]interface{}) boolClause := make(map[string]interface{}) query["bool"] = boolClause // must if len(q.mustClauses) == 1 { src, err := q.mustClauses[0].Source() if err != nil { return nil, err } boolClause["must"] = src } else if len(q.mustClauses) > 1 { var clauses []interface{} for _, subQuery := range q.mustClauses { src, err := subQuery.Source() if err != nil { return nil, err } clauses = append(clauses, src) } boolClause["must"] = clauses } // must_not if len(q.mustNotClauses) == 1 { src, err := q.mustNotClauses[0].Source() if err != nil { return nil, err } boolClause["must_not"] = src } else if len(q.mustNotClauses) > 1 { var clauses []interface{} for _, subQuery := range q.mustNotClauses { src, err := subQuery.Source() if err != nil { return nil, err } clauses = append(clauses, src) } boolClause["must_not"] = clauses } // filter if len(q.filterClauses) == 1 { src, err := q.filterClauses[0].Source() if err != nil { return nil, err } boolClause["filter"] = src } else if len(q.filterClauses) > 1 { var clauses []interface{} for _, subQuery := range q.filterClauses { src, err := subQuery.Source() if err != nil { return nil, err } clauses = append(clauses, src) } boolClause["filter"] = clauses } return query, nil } ================================================ FILE: common/elasticsearch/query/bool_query_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query import ( "encoding/json" "testing" ) func TestBoolQuery(t *testing.T) { q := NewBoolQuery() q = q.MustNot(NewRangeQuery("age").From(10).To(20)) src, err := q.Source() if err != nil { t.Fatal(err) } data, err := json.Marshal(src) if err != nil { t.Fatalf("marshaling to JSON failed: %v", err) } got := string(data) expected := `{"bool":{"must_not":{"range":{"age":{"from":10,"include_lower":true,"include_upper":true,"to":20}}}}}` if got != expected { t.Errorf("expected\n%s\n,got:\n%s", expected, got) } } ================================================ FILE: common/elasticsearch/query/builder.go ================================================ package query import "encoding/json" // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. type Builder struct { query Query // query from int // from size int // size sorters []Sorter // sort searchAfterSortValues []interface{} // search_after } func NewBuilder() *Builder { return &Builder{ from: -1, size: -1, } } func (b *Builder) Query(query Query) *Builder { b.query = query return b } func (b *Builder) From(from int) *Builder { b.from = from return b } func (b *Builder) Sortby(sorters ...Sorter) *Builder { b.sorters = sorters return b } func (b *Builder) Size(size int) *Builder { b.size = size return b } func (b *Builder) SearchAfter(v ...interface{}) *Builder { b.searchAfterSortValues = v return b } // Source returns the serializable JSON for the source builder. func (b *Builder) Source() (interface{}, error) { source := make(map[string]interface{}) if b.from != -1 { source["from"] = b.from } if b.size != -1 { source["size"] = b.size } if b.query != nil { src, err := b.query.Source() if err != nil { return nil, err } source["query"] = src } if len(b.sorters) > 0 { var sortarr []interface{} for _, sorter := range b.sorters { src, err := sorter.Source() if err != nil { return nil, err } sortarr = append(sortarr, src) } source["sort"] = sortarr } if len(b.searchAfterSortValues) > 0 { source["search_after"] = b.searchAfterSortValues } return source, nil } func (b *Builder) String() (string, error) { source, err := b.Source() if err != nil { return "", err } marshaled, err := json.Marshal(source) if err != nil { return "", err } return string(marshaled), nil } ================================================ FILE: common/elasticsearch/query/builder_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query import ( "encoding/json" "testing" "github.com/olivere/elastic/v7" "github.com/stretchr/testify/assert" ) func TestQueryBuilder(t *testing.T) { qb := NewBuilder() qb.Query(NewExistsQuery("user")) qb.Size(10) qb.From(100) qb.Sortby(NewFieldSort("StartDate")) src, err := qb.Source() if err != nil { t.Fatal(err) } data, err := json.Marshal(src) if err != nil { t.Fatalf("marshaling to JSON failed: %v", err) } got := string(data) expected := `{"from":100,"query":{"exists":{"field":"user"}},"size":10,"sort":[{"StartDate":{"order":"asc"}}]}` if got != expected { t.Errorf("expected\n%s\n,got:\n%s", expected, got) } } func TestBuilderAgainsESv7(t *testing.T) { qb := NewBuilder() qb.Query(NewExistsQuery("user")) qb.Size(10) qb.Sortby(NewFieldSort("runid").Desc()) qb.Query(NewBoolQuery().Must(NewMatchQuery("domainID", "uuid"))).SearchAfter("sortval", "tiebraker") qbs, err := qb.Source() assert.NoError(t, err) searchSource := elastic.NewSearchSource(). Query(elastic.NewExistsQuery("user")). Size(10). SortBy(elastic.NewFieldSort("runid").Desc()). Query(elastic.NewBoolQuery().Must(elastic.NewMatchQuery("domainID", "uuid"))).SearchAfter("sortval", "tiebraker") sss, err := searchSource.Source() assert.NoError(t, err) assert.Equal(t, sss, qbs, "ESv7 and local QueryBuilder should produce the same query") } ================================================ FILE: common/elasticsearch/query/exists_query.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query // ExistsQuery is a query that only matches on documents that the field // has a value in them. // // For more details, see: // https://www.elastic.co/guide/en/elasticsearch/reference/6.8/query-dsl-exists-query.html type ExistsQuery struct { name string queryName string } // NewExistsQuery creates and initializes a new exists query. func NewExistsQuery(name string) *ExistsQuery { return &ExistsQuery{ name: name, } } // QueryName sets the query name for the filter that can be used // when searching for matched queries per hit. func (q *ExistsQuery) QueryName(queryName string) *ExistsQuery { q.queryName = queryName return q } // Source returns the JSON serializable content for this query. func (q *ExistsQuery) Source() (interface{}, error) { // { // "exists" : { // "field" : "user" // } // } query := make(map[string]interface{}) params := make(map[string]interface{}) query["exists"] = params params["field"] = q.name if q.queryName != "" { params["_name"] = q.queryName } return query, nil } ================================================ FILE: common/elasticsearch/query/exists_query_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query import ( "encoding/json" "testing" ) func TestExistsQuery(t *testing.T) { q := NewExistsQuery("user") src, err := q.Source() if err != nil { t.Fatal(err) } data, err := json.Marshal(src) if err != nil { t.Fatalf("marshaling to JSON failed: %v", err) } got := string(data) expected := `{"exists":{"field":"user"}}` if got != expected { t.Errorf("expected\n%s\n,got:\n%s", expected, got) } } ================================================ FILE: common/elasticsearch/query/match_query.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query // MatchQuery is a family of queries that accepts text/numerics/dates, // analyzes them, and constructs a query. // // To create a new MatchQuery, use NewMatchQuery. To create specific types // of queries, e.g. a match_phrase query, use NewMatchPhrQuery(...).Type("phrase"), // or use one of the shortcuts e.g. NewMatchPhraseQuery(...). // // For more details, see // https://www.elastic.co/guide/en/elasticsearch/reference/7.0/query-dsl-match-query.html type MatchQuery struct { name string text interface{} operator string // or / and analyzer string boost *float64 fuzziness string prefixLength *int maxExpansions *int minimumShouldMatch string fuzzyRewrite string lenient *bool fuzzyTranspositions *bool zeroTermsQuery string cutoffFrequency *float64 queryName string } // NewMatchQuery creates and initializes a new MatchQuery. func NewMatchQuery(name string, text interface{}) *MatchQuery { return &MatchQuery{name: name, text: text} } // Operator sets the operator to use when using a boolean query. // Can be "AND" or "OR" (default). func (q *MatchQuery) Operator(operator string) *MatchQuery { q.operator = operator return q } // Analyzer explicitly sets the analyzer to use. It defaults to use explicit // mapping config for the field, or, if not set, the default search analyzer. func (q *MatchQuery) Analyzer(analyzer string) *MatchQuery { q.analyzer = analyzer return q } // Fuzziness sets the fuzziness when evaluated to a fuzzy query type. // Defaults to "AUTO". func (q *MatchQuery) Fuzziness(fuzziness string) *MatchQuery { q.fuzziness = fuzziness return q } // PrefixLength sets the length of a length of common (non-fuzzy) // prefix for fuzzy match queries. It must be non-negative. func (q *MatchQuery) PrefixLength(prefixLength int) *MatchQuery { q.prefixLength = &prefixLength return q } // MaxExpansions is used with fuzzy or prefix type queries. It specifies // the number of term expansions to use. It defaults to unbounded so that // its recommended to set it to a reasonable value for faster execution. func (q *MatchQuery) MaxExpansions(maxExpansions int) *MatchQuery { q.maxExpansions = &maxExpansions return q } // CutoffFrequency can be a value in [0..1] (or an absolute number >=1). // It represents the maximum treshold of a terms document frequency to be // considered a low frequency term. func (q *MatchQuery) CutoffFrequency(cutoff float64) *MatchQuery { q.cutoffFrequency = &cutoff return q } // MinimumShouldMatch sets the optional minimumShouldMatch value to // apply to the query. func (q *MatchQuery) MinimumShouldMatch(minimumShouldMatch string) *MatchQuery { q.minimumShouldMatch = minimumShouldMatch return q } // FuzzyRewrite sets the fuzzy_rewrite parameter controlling how the // fuzzy query will get rewritten. func (q *MatchQuery) FuzzyRewrite(fuzzyRewrite string) *MatchQuery { q.fuzzyRewrite = fuzzyRewrite return q } // FuzzyTranspositions sets whether transpositions are supported in // fuzzy queries. // // The default metric used by fuzzy queries to determine a match is // the Damerau-Levenshtein distance formula which supports transpositions. // Setting transposition to false will // * switch to classic Levenshtein distance. // * If not set, Damerau-Levenshtein distance metric will be used. func (q *MatchQuery) FuzzyTranspositions(fuzzyTranspositions bool) *MatchQuery { q.fuzzyTranspositions = &fuzzyTranspositions return q } // Lenient specifies whether format based failures will be ignored. func (q *MatchQuery) Lenient(lenient bool) *MatchQuery { q.lenient = &lenient return q } // ZeroTermsQuery can be "all" or "none". func (q *MatchQuery) ZeroTermsQuery(zeroTermsQuery string) *MatchQuery { q.zeroTermsQuery = zeroTermsQuery return q } // Boost sets the boost to apply to this query. func (q *MatchQuery) Boost(boost float64) *MatchQuery { q.boost = &boost return q } // QueryName sets the query name for the filter that can be used when // searching for matched filters per hit. func (q *MatchQuery) QueryName(queryName string) *MatchQuery { q.queryName = queryName return q } // Source returns JSON for the function score query. func (q *MatchQuery) Source() (interface{}, error) { // {"match":{"name":{"query":"value","type":"boolean/phrase"}}} source := make(map[string]interface{}) match := make(map[string]interface{}) source["match"] = match query := make(map[string]interface{}) match[q.name] = query query["query"] = q.text if q.operator != "" { query["operator"] = q.operator } if q.analyzer != "" { query["analyzer"] = q.analyzer } if q.fuzziness != "" { query["fuzziness"] = q.fuzziness } if q.prefixLength != nil { query["prefix_length"] = *q.prefixLength } if q.maxExpansions != nil { query["max_expansions"] = *q.maxExpansions } if q.minimumShouldMatch != "" { query["minimum_should_match"] = q.minimumShouldMatch } if q.fuzzyRewrite != "" { query["fuzzy_rewrite"] = q.fuzzyRewrite } if q.lenient != nil { query["lenient"] = *q.lenient } if q.fuzzyTranspositions != nil { query["fuzzy_transpositions"] = *q.fuzzyTranspositions } if q.zeroTermsQuery != "" { query["zero_terms_query"] = q.zeroTermsQuery } if q.cutoffFrequency != nil { query["cutoff_frequency"] = *q.cutoffFrequency } if q.boost != nil { query["boost"] = *q.boost } if q.queryName != "" { query["_name"] = q.queryName } return source, nil } ================================================ FILE: common/elasticsearch/query/match_query_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query import ( "encoding/json" "testing" ) func TestMatchQuery(t *testing.T) { q := NewMatchQuery("message", "this is a test") src, err := q.Source() if err != nil { t.Fatal(err) } data, err := json.Marshal(src) if err != nil { t.Fatalf("marshaling to JSON failed: %v", err) } got := string(data) expected := `{"match":{"message":{"query":"this is a test"}}}` if got != expected { t.Errorf("expected\n%s\n,got:\n%s", expected, got) } } func TestMatchQueryWithOptions(t *testing.T) { q := NewMatchQuery("message", "this is a test").Analyzer("whitespace").Operator("or").Boost(2.5) src, err := q.Source() if err != nil { t.Fatal(err) } data, err := json.Marshal(src) if err != nil { t.Fatalf("marshaling to JSON failed: %v", err) } got := string(data) expected := `{"match":{"message":{"analyzer":"whitespace","boost":2.5,"operator":"or","query":"this is a test"}}}` if got != expected { t.Errorf("expected\n%s\n,got:\n%s", expected, got) } } ================================================ FILE: common/elasticsearch/query/range_query.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query // RangeQuery matches documents with fields that have terms within a certain range. // // For details, see // https://www.elastic.co/guide/en/elasticsearch/reference/7.0/query-dsl-range-query.html type RangeQuery struct { name string from interface{} to interface{} includeLower bool includeUpper bool } // NewRangeQuery creates and initializes a new RangeQuery. func NewRangeQuery(name string) *RangeQuery { return &RangeQuery{name: name, includeLower: true, includeUpper: true} } // From indicates the from part of the RangeQuery. // Use nil to indicate an unbounded from part. func (q *RangeQuery) From(from interface{}) *RangeQuery { q.from = from return q } // Gt indicates a greater-than value for the from part. // Use nil to indicate an unbounded from part. func (q *RangeQuery) Gt(from interface{}) *RangeQuery { q.from = from q.includeLower = false return q } // Gte indicates a greater-than-or-equal value for the from part. // Use nil to indicate an unbounded from part. func (q *RangeQuery) Gte(from interface{}) *RangeQuery { q.from = from q.includeLower = true return q } // To indicates the to part of the RangeQuery. // Use nil to indicate an unbounded to part. func (q *RangeQuery) To(to interface{}) *RangeQuery { q.to = to return q } // Lt indicates a less-than value for the to part. // Use nil to indicate an unbounded to part. func (q *RangeQuery) Lt(to interface{}) *RangeQuery { q.to = to q.includeUpper = false return q } // Lte indicates a less-than-or-equal value for the to part. // Use nil to indicate an unbounded to part. func (q *RangeQuery) Lte(to interface{}) *RangeQuery { q.to = to q.includeUpper = true return q } // IncludeLower indicates whether the lower bound should be included or not. // Defaults to true. func (q *RangeQuery) IncludeLower(includeLower bool) *RangeQuery { q.includeLower = includeLower return q } // IncludeUpper indicates whether the upper bound should be included or not. // Defaults to true. func (q *RangeQuery) IncludeUpper(includeUpper bool) *RangeQuery { q.includeUpper = includeUpper return q } // Source returns JSON for the query. func (q *RangeQuery) Source() (interface{}, error) { source := make(map[string]interface{}) rangeQ := make(map[string]interface{}) source["range"] = rangeQ params := make(map[string]interface{}) rangeQ[q.name] = params params["from"] = q.from params["to"] = q.to params["include_lower"] = q.includeLower params["include_upper"] = q.includeUpper return source, nil } ================================================ FILE: common/elasticsearch/query/range_query_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query import ( "encoding/json" "testing" ) func TestRangeQuery(t *testing.T) { q := NewRangeQuery("postDate").From("2010-03-01").To("2010-04-01") src, err := q.Source() if err != nil { t.Fatal(err) } data, err := json.Marshal(src) if err != nil { t.Fatalf("marshaling to JSON failed: %v", err) } got := string(data) expected := `{"range":{"postDate":{"from":"2010-03-01","include_lower":true,"include_upper":true,"to":"2010-04-01"}}}` if got != expected { t.Errorf("expected\n%s\n,got:\n%s", expected, got) } } ================================================ FILE: common/elasticsearch/query/sort.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query type Sorter interface { Source() (interface{}, error) } // FieldSort sorts by a given field. type FieldSort struct { Sorter fieldName string ascending bool } // NewFieldSort creates a new FieldSort. func NewFieldSort(fieldName string) *FieldSort { return &FieldSort{ fieldName: fieldName, ascending: true, } } // FieldName specifies the name of the field to be used for sorting. func (s *FieldSort) FieldName(fieldName string) *FieldSort { s.fieldName = fieldName return s } // Order defines whether sorting ascending (default) or descending. func (s *FieldSort) Order(ascending bool) *FieldSort { s.ascending = ascending return s } // Asc sets ascending sort order. func (s *FieldSort) Asc() *FieldSort { s.ascending = true return s } // Desc sets descending sort order. func (s *FieldSort) Desc() *FieldSort { s.ascending = false return s } // Source returns the JSON-serializable data. func (s *FieldSort) Source() (interface{}, error) { source := make(map[string]interface{}) x := make(map[string]interface{}) source[s.fieldName] = x if s.ascending { x["order"] = "asc" } else { x["order"] = "desc" } return source, nil } ================================================ FILE: common/elasticsearch/query/sort_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query import ( "encoding/json" "testing" ) func TestFieldSort(t *testing.T) { builder := NewFieldSort("grade") src, err := builder.Source() if err != nil { t.Fatal(err) } data, err := json.Marshal(src) if err != nil { t.Fatalf("marshaling to JSON failed: %v", err) } got := string(data) expected := `{"grade":{"order":"asc"}}` if got != expected { t.Errorf("expected\n%s\n,got:\n%s", expected, got) } } ================================================ FILE: common/elasticsearch/validator/queryValidator.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to qvom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, qvETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package validator import ( "errors" "fmt" "strings" "github.com/xwb1989/sqlparser" "github.com/uber/cadence/common" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) // VisibilityQueryValidator for sql query validation type VisibilityQueryValidator struct { validSearchAttributes dynamicproperties.MapPropertyFn enableQueryAttributeValidation dynamicproperties.BoolPropertyFn } // NewQueryValidator create VisibilityQueryValidator func NewQueryValidator( validSearchAttributes dynamicproperties.MapPropertyFn, enableQueryAttributeValidation dynamicproperties.BoolPropertyFn) *VisibilityQueryValidator { return &VisibilityQueryValidator{ validSearchAttributes: validSearchAttributes, enableQueryAttributeValidation: enableQueryAttributeValidation, } } // ValidateQuery validates that search attributes in the query are legal. // Adds attr prefix for customized fields and returns modified query. func (qv *VisibilityQueryValidator) ValidateQuery(whereClause string) (string, error) { if len(whereClause) != 0 { // Build a placeholder query that allows us to easily parse the contents of the where clause. // IMPORTANT: This query is never executed, it is just used to parse and validate whereClause var placeholderQuery string whereClause := strings.TrimSpace(whereClause) // #nosec if common.IsJustOrderByClause(whereClause) { // just order by placeholderQuery = fmt.Sprintf("SELECT * FROM dummy %s", whereClause) } else { placeholderQuery = fmt.Sprintf("SELECT * FROM dummy WHERE %s", whereClause) } stmt, err := sqlparser.Parse(placeholderQuery) if err != nil { return "", &types.BadRequestError{Message: "Invalid query."} } sel, ok := stmt.(*sqlparser.Select) if !ok { return "", &types.BadRequestError{Message: "Invalid select query."} } buf := sqlparser.NewTrackedBuffer(nil) // validate where expr if sel.Where != nil { err = qv.validateWhereExpr(sel.Where.Expr) if err != nil { return "", &types.BadRequestError{Message: err.Error()} } sel.Where.Expr.Format(buf) } // validate order by err = qv.validateOrderByExpr(sel.OrderBy) if err != nil { return "", &types.BadRequestError{Message: err.Error()} } sel.OrderBy.Format(buf) return buf.String(), nil } return whereClause, nil } func (qv *VisibilityQueryValidator) validateWhereExpr(expr sqlparser.Expr) error { if expr == nil { return nil } switch expr := expr.(type) { case *sqlparser.AndExpr, *sqlparser.OrExpr: return qv.validateAndOrExpr(expr) case *sqlparser.ComparisonExpr: return qv.validateComparisonExpr(expr) case *sqlparser.RangeCond: return qv.validateRangeExpr(expr) case *sqlparser.ParenExpr: return qv.validateWhereExpr(expr.Expr) default: return errors.New("invalid where clause") } } func (qv *VisibilityQueryValidator) validateAndOrExpr(expr sqlparser.Expr) error { var leftExpr sqlparser.Expr var rightExpr sqlparser.Expr switch expr := expr.(type) { case *sqlparser.AndExpr: leftExpr = expr.Left rightExpr = expr.Right case *sqlparser.OrExpr: leftExpr = expr.Left rightExpr = expr.Right } if err := qv.validateWhereExpr(leftExpr); err != nil { return err } return qv.validateWhereExpr(rightExpr) } func (qv *VisibilityQueryValidator) validateComparisonExpr(expr sqlparser.Expr) error { comparisonExpr := expr.(*sqlparser.ComparisonExpr) colName, ok := comparisonExpr.Left.(*sqlparser.ColName) if !ok { return errors.New("invalid comparison expression") } colNameStr := colName.Name.String() if !qv.isValidSearchAttributes(colNameStr) { return fmt.Errorf("invalid search attribute %q", colNameStr) } if !definition.IsSystemIndexedKey(colNameStr) { // add search attribute prefix comparisonExpr.Left = &sqlparser.ColName{ Metadata: colName.Metadata, Name: sqlparser.NewColIdent(definition.Attr + "." + colNameStr), Qualifier: colName.Qualifier, } } return nil } func (qv *VisibilityQueryValidator) validateRangeExpr(expr sqlparser.Expr) error { rangeCond := expr.(*sqlparser.RangeCond) colName, ok := rangeCond.Left.(*sqlparser.ColName) if !ok { return errors.New("invalid range expression") } colNameStr := colName.Name.String() if !qv.isValidSearchAttributes(colNameStr) { return fmt.Errorf("invalid search attribute %q", colNameStr) } if !definition.IsSystemIndexedKey(colNameStr) { // add search attribute prefix rangeCond.Left = &sqlparser.ColName{ Metadata: colName.Metadata, Name: sqlparser.NewColIdent(definition.Attr + "." + colNameStr), Qualifier: colName.Qualifier, } } return nil } func (qv *VisibilityQueryValidator) validateOrderByExpr(orderBy sqlparser.OrderBy) error { for _, orderByExpr := range orderBy { colName, ok := orderByExpr.Expr.(*sqlparser.ColName) if !ok { return errors.New("invalid order by expression") } colNameStr := colName.Name.String() if qv.isValidSearchAttributes(colNameStr) { if !definition.IsSystemIndexedKey(colNameStr) { // add search attribute prefix orderByExpr.Expr = &sqlparser.ColName{ Metadata: colName.Metadata, Name: sqlparser.NewColIdent(definition.Attr + "." + colNameStr), Qualifier: colName.Qualifier, } } } else { return errors.New("invalid order by attribute") } } return nil } // isValidSearchAttributes return true if key is registered func (qv *VisibilityQueryValidator) isValidSearchAttributes(key string) bool { if qv.enableQueryAttributeValidation() { validAttr := qv.validSearchAttributes() _, isValidKey := validAttr[key] return isValidKey } return true } ================================================ FILE: common/elasticsearch/validator/queryValidator_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to qvom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, qvETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package validator import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) func TestValidateQuery(t *testing.T) { tests := []struct { msg string query string validated string err string dcValid map[string]interface{} }{ { msg: "empty query", query: "", validated: "", }, { msg: "simple query", query: "WorkflowID = 'wid'", validated: "WorkflowID = 'wid'", }, { msg: "custom field", query: "CustomStringField = 'custom'", validated: "`Attr.CustomStringField` = 'custom'", }, { msg: "complex query", query: "WorkflowID = 'wid' and ((CustomStringField = 'custom') or CustomIntField between 1 and 10)", validated: "WorkflowID = 'wid' and ((`Attr.CustomStringField` = 'custom') or `Attr.CustomIntField` between 1 and 10)", }, { msg: "invalid SQL", query: "Invalid SQL", err: "Invalid query.", }, { msg: "invalid where expression", query: "InvalidWhereExpr", err: "invalid where clause", }, { msg: "invalid comparison", query: "WorkflowID = 'wid' and 1 < 2", err: "invalid comparison expression", }, { msg: "invalid range", query: "1 between 1 and 2 or WorkflowID = 'wid'", err: "invalid range expression", }, { msg: "invalid search attribute in comparison", query: "Invalid = 'a' and 1 < 2", err: "invalid search attribute \"Invalid\"", }, { msg: "invalid search attribute in range", query: "Invalid between 1 and 2 or WorkflowID = 'wid'", err: "invalid search attribute \"Invalid\"", }, { msg: "only order by", query: "order by CloseTime desc", validated: " order by CloseTime desc", }, { msg: "only order by search attribute", query: "order by CustomIntField desc", validated: " order by `Attr.CustomIntField` desc", }, { msg: "condition + order by", query: "WorkflowID = 'wid' order by CloseTime desc", validated: "WorkflowID = 'wid' order by CloseTime desc", }, { msg: "invalid order by attribute", query: "order by InvalidField desc", err: "invalid order by attribute", }, { msg: "invalid order by attribute expr", query: "order by 123", err: "invalid order by expression", }, { msg: "security SQL injection - with another statement", query: "WorkflowID = 'wid'; SELECT * FROM important_table;", err: "Invalid query.", }, { msg: "security SQL injection - with always true expression", query: "WorkflowID = 'wid' and (RunID = 'rid' or 1 = 1)", err: "invalid comparison expression", }, { msg: "security SQL injection - with union", query: "WorkflowID = 'wid' union select * from dummy", err: "Invalid select query.", }, { msg: "valid custom search attribute", query: "CustomStringField = 'value'", validated: "`Attr.CustomStringField` = 'value'", }, { msg: "custom search attribute can contain underscore", query: "Custom_Field = 'value'", validated: "`Attr.Custom_Field` = 'value'", dcValid: map[string]interface{}{ "Custom_Field": types.IndexedValueTypeString, }, }, { msg: "custom search attribute can contain number", query: "Custom_0 = 'value'", validated: "`Attr.Custom_0` = 'value'", dcValid: map[string]interface{}{ "Custom_0": types.IndexedValueTypeString, }, }, { msg: "customg search attribute cannot contain dot", query: "Custom.Field = 'value'", err: "invalid search attribute \"Field\"", dcValid: map[string]interface{}{ "Custom.Field": types.IndexedValueTypeString, }, }, { msg: "custom search attribute cannot contain dash", query: "Custom-Field = 'value'", err: "invalid comparison expression", dcValid: map[string]interface{}{ "Custom-Field": types.IndexedValueTypeString, }, }, { msg: "custom string search attribute support not like", query: "CustomStringField not like 'value'", validated: "`Attr.CustomStringField` not like 'value'", dcValid: map[string]interface{}{ "CustomStringField": types.IndexedValueTypeString, }, }, } for _, tt := range tests { t.Run(tt.msg, func(t *testing.T) { validSearchAttr := func(opts ...dynamicproperties.FilterOption) map[string]interface{} { valid := definition.GetDefaultIndexedKeys() for k, v := range tt.dcValid { valid[k] = v } return valid } validateSearchAttr := dynamicproperties.GetBoolPropertyFn(true) qv := NewQueryValidator(validSearchAttr, validateSearchAttr) validated, err := qv.ValidateQuery(tt.query) if err != nil { assert.Equal(t, tt.err, err.Error()) } else { assert.Equal(t, tt.validated, validated) } }) } } ================================================ FILE: common/elasticsearch/validator/searchAttrValidator.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to qvom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, qvETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package validator import ( "fmt" "github.com/uber/cadence/common" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) // SearchAttributesValidator is used to validate search attributes type SearchAttributesValidator struct { logger log.Logger enableQueryAttributeValidation dynamicproperties.BoolPropertyFn validSearchAttributes dynamicproperties.MapPropertyFn searchAttributesNumberOfKeysLimit dynamicproperties.IntPropertyFnWithDomainFilter searchAttributesSizeOfValueLimit dynamicproperties.IntPropertyFnWithDomainFilter searchAttributesTotalSizeLimit dynamicproperties.IntPropertyFnWithDomainFilter } // NewSearchAttributesValidator create SearchAttributesValidator func NewSearchAttributesValidator( logger log.Logger, enableQueryAttributeValidation dynamicproperties.BoolPropertyFn, validSearchAttributes dynamicproperties.MapPropertyFn, searchAttributesNumberOfKeysLimit dynamicproperties.IntPropertyFnWithDomainFilter, searchAttributesSizeOfValueLimit dynamicproperties.IntPropertyFnWithDomainFilter, searchAttributesTotalSizeLimit dynamicproperties.IntPropertyFnWithDomainFilter, ) *SearchAttributesValidator { return &SearchAttributesValidator{ logger: logger, enableQueryAttributeValidation: enableQueryAttributeValidation, validSearchAttributes: validSearchAttributes, searchAttributesNumberOfKeysLimit: searchAttributesNumberOfKeysLimit, searchAttributesSizeOfValueLimit: searchAttributesSizeOfValueLimit, searchAttributesTotalSizeLimit: searchAttributesTotalSizeLimit, } } // ValidateSearchAttributes validate search attributes are valid for writing and not exceed limits func (sv *SearchAttributesValidator) ValidateSearchAttributes(input *types.SearchAttributes, domain string) error { if input == nil { return nil } // verify: number of keys <= limit fields := input.GetIndexedFields() lengthOfFields := len(fields) if lengthOfFields > sv.searchAttributesNumberOfKeysLimit(domain) { sv.logger.WithTags(tag.Number(int64(lengthOfFields)), tag.WorkflowDomainName(domain)). Error("number of keys in search attributes exceed limit") return &types.BadRequestError{Message: fmt.Sprintf("number of keys %d exceed limit", lengthOfFields)} } totalSize := 0 validateAttrFn := sv.enableQueryAttributeValidation validateAttr := true if validateAttrFn != nil { validateAttr = validateAttrFn() } validAttr := sv.validSearchAttributes() for key, val := range fields { if validateAttr { // verify: key is whitelisted if !sv.isValidSearchAttributesKey(validAttr, key) { sv.logger.WithTags(tag.ESKey(key), tag.WorkflowDomainName(domain)). Error("invalid search attribute key") return &types.BadRequestError{Message: fmt.Sprintf("%s is not a valid search attribute key", key)} } // verify: value has the correct type if !sv.isValidSearchAttributesValue(validAttr, key, val) { sv.logger.WithTags(tag.ESKey(key), tag.ESValue(val), tag.WorkflowDomainName(domain)). Error("invalid search attribute value") return &types.BadRequestError{Message: fmt.Sprintf("%s is not a valid search attribute value for key %s", val, key)} } } // verify: key is not system reserved if definition.IsSystemIndexedKey(key) { sv.logger.WithTags(tag.ESKey(key), tag.WorkflowDomainName(domain)). Error("illegal update of system reserved attribute") return &types.BadRequestError{Message: fmt.Sprintf("%s is read-only Cadence reservered attribute", key)} } // verify: size of single value <= limit if len(val) > sv.searchAttributesSizeOfValueLimit(domain) { sv.logger.WithTags(tag.ESKey(key), tag.Number(int64(len(val))), tag.WorkflowDomainName(domain)). Error("value size of search attribute exceed limit") return &types.BadRequestError{Message: fmt.Sprintf("size limit exceed for key %s", key)} } totalSize += len(key) + len(val) } // verify: total size <= limit if totalSize > sv.searchAttributesTotalSizeLimit(domain) { sv.logger.WithTags(tag.Number(int64(totalSize)), tag.WorkflowDomainName(domain)). Error("total size of search attributes exceed limit") return &types.BadRequestError{Message: fmt.Sprintf("total size %d exceed limit", totalSize)} } return nil } // isValidSearchAttributesKey return true if key is registered func (sv *SearchAttributesValidator) isValidSearchAttributesKey( validAttr map[string]interface{}, key string, ) bool { _, isValidKey := validAttr[key] return isValidKey } // isValidSearchAttributesValue return true if value has the correct representation for the attribute key func (sv *SearchAttributesValidator) isValidSearchAttributesValue( validAttr map[string]interface{}, key string, value []byte, ) bool { valueType := common.ConvertIndexedValueTypeToInternalType(validAttr[key], sv.logger) _, err := common.DeserializeSearchAttributeValue(value, valueType) return err == nil } ================================================ FILE: common/elasticsearch/validator/searchAttrValidator_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to qvom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, qvETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package validator import ( "testing" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) type searchAttributesValidatorSuite struct { suite.Suite } func TestSearchAttributesValidatorSuite(t *testing.T) { s := new(searchAttributesValidatorSuite) suite.Run(t, s) } func (s *searchAttributesValidatorSuite) TestValidateSearchAttributes() { numOfKeysLimit := 2 sizeOfValueLimit := 5 sizeOfTotalLimit := 20 validator := NewSearchAttributesValidator(log.NewNoop(), dynamicproperties.GetBoolPropertyFn(true), dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), dynamicproperties.GetIntPropertyFilteredByDomain(numOfKeysLimit), dynamicproperties.GetIntPropertyFilteredByDomain(sizeOfValueLimit), dynamicproperties.GetIntPropertyFilteredByDomain(sizeOfTotalLimit)) domain := "domain" var attr *types.SearchAttributes err := validator.ValidateSearchAttributes(attr, domain) s.Nil(err) fields := map[string][]byte{ "CustomIntField": []byte(`1`), } attr = &types.SearchAttributes{ IndexedFields: fields, } err = validator.ValidateSearchAttributes(attr, domain) s.Nil(err) fields = map[string][]byte{ "CustomIntField": []byte(`1`), "CustomKeywordField": []byte(`"keyword"`), "CustomBoolField": []byte(`true`), } attr.IndexedFields = fields err = validator.ValidateSearchAttributes(attr, domain) s.Equal("number of keys 3 exceed limit", err.Error()) fields = map[string][]byte{ "InvalidKey": []byte(`"1"`), } attr.IndexedFields = fields err = validator.ValidateSearchAttributes(attr, domain) s.Equal(`InvalidKey is not a valid search attribute key`, err.Error()) fields = map[string][]byte{ "CustomStringField": []byte(`"1"`), "CustomBoolField": []byte(`123`), } attr.IndexedFields = fields err = validator.ValidateSearchAttributes(attr, domain) s.Equal(`123 is not a valid search attribute value for key CustomBoolField`, err.Error()) fields = map[string][]byte{ "CustomIntField": []byte(`[1,2]`), } attr.IndexedFields = fields err = validator.ValidateSearchAttributes(attr, domain) s.NoError(err) fields = map[string][]byte{ "StartTime": []byte(`1`), } attr.IndexedFields = fields err = validator.ValidateSearchAttributes(attr, domain) s.Equal(`StartTime is read-only Cadence reservered attribute`, err.Error()) fields = map[string][]byte{ "CustomKeywordField": []byte(`"123456"`), } attr.IndexedFields = fields err = validator.ValidateSearchAttributes(attr, domain) s.Equal(`size limit exceed for key CustomKeywordField`, err.Error()) fields = map[string][]byte{ "CustomKeywordField": []byte(`"123"`), "CustomStringField": []byte(`"12"`), } attr.IndexedFields = fields err = validator.ValidateSearchAttributes(attr, domain) s.Equal(`total size 44 exceed limit`, err.Error()) } ================================================ FILE: common/errors/fake_errors.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package errors import ( "context" "errors" "math/rand" "github.com/uber/cadence/common/types" ) var ( // ErrFakeServiceBusy is a fake service busy error. ErrFakeServiceBusy = &types.ServiceBusyError{Message: "Fake Service Busy Error."} // ErrFakeInternalService is a fake internal service error. ErrFakeInternalService = &types.InternalServiceError{Message: "Fake Internal Service Error."} // ErrFakeTimeout is a fake timeout error. ErrFakeTimeout = context.DeadlineExceeded // ErrFakeUnhandled is a fake unhandled error. ErrFakeUnhandled = errors.New("fake unhandled error") ) var ( fakeErrors = []error{ ErrFakeServiceBusy, ErrFakeInternalService, ErrFakeTimeout, ErrFakeUnhandled, } ) // ShouldForwardCall determines if the call should be forward to the underlying // client given the fake error generated func ShouldForwardCall( err error, ) bool { if err == nil { return true } if err == ErrFakeTimeout || err == ErrFakeUnhandled { // forward the call with 50% chance return rand.Intn(2) == 0 } return false } // GenerateFakeError generates a random fake error func GenerateFakeError( errorRate float64, ) error { if rand.Float64() < errorRate { return fakeErrors[rand.Intn(len(fakeErrors))] } return nil } ================================================ FILE: common/errors/internal_failure_error.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package errors type ( // InternalFailureError represents unexpected case happening or a code bug InternalFailureError struct { Msg string } ) // NewInternalFailureError return internal failure error func NewInternalFailureError(msg string) *InternalFailureError { return &InternalFailureError{Msg: msg} } func (e *InternalFailureError) Error() string { return e.Msg } ================================================ FILE: common/errors/peer_hostname_error.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package errors import ( "errors" "fmt" ) // PeerHostnameError wraps an error with peer hostname information type PeerHostnameError struct { PeerHostname string WrappedError error } // Error implements the error interface func (e *PeerHostnameError) Error() string { return fmt.Sprintf("peer hostname: %s, error: %v", e.PeerHostname, e.WrappedError) } // Unwrap implements the error unwrapping interface func (e *PeerHostnameError) Unwrap() error { return e.WrappedError } // NewPeerHostnameError creates a new PeerHostnameError func NewPeerHostnameError(err error, peer string) error { if err == nil { return nil } if peer == "" { return err } return &PeerHostnameError{ PeerHostname: peer, WrappedError: err, } } // ExtractPeerHostname extracts the peer hostname from a wrapped error // Returns the hostname and the original unwrapped error func ExtractPeerHostname(err error) (string, error) { if err == nil { return "", nil } var peerErr *PeerHostnameError current := err if errors.As(current, &peerErr) { return peerErr.PeerHostname, peerErr.WrappedError } return "", err } ================================================ FILE: common/errors/peer_hostname_error_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package errors import ( "errors" "testing" "github.com/stretchr/testify/assert" ) func TestNewPeerHostnameError(t *testing.T) { tests := []struct { name string err error peer string wantErr bool wantPeer string }{ { name: "nil error returns nil", err: nil, peer: "host1", wantErr: false, wantPeer: "", }, { name: "empty peer returns original error", err: errors.New("original error"), peer: "", wantErr: true, wantPeer: "", }, { name: "wraps error with peer", err: errors.New("original error"), peer: "host1", wantErr: true, wantPeer: "host1", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := NewPeerHostnameError(tt.err, tt.peer) if !tt.wantErr { assert.Nil(t, got) return } assert.NotNil(t, got) if tt.wantPeer == "" { assert.Equal(t, tt.err, got) } else { var peerErr *PeerHostnameError assert.True(t, errors.As(got, &peerErr)) assert.Equal(t, tt.wantPeer, peerErr.PeerHostname) assert.Equal(t, tt.err, peerErr.WrappedError) } }) } } func TestExtractPeerHostname(t *testing.T) { tests := []struct { name string err error wantPeer string wantErr error }{ { name: "nil error", err: nil, wantPeer: "", wantErr: nil, }, { name: "non-peer error", err: errors.New("some error"), wantPeer: "", wantErr: errors.New("some error"), }, { name: "peer error", err: NewPeerHostnameError(errors.New("original error"), "host1"), wantPeer: "host1", wantErr: errors.New("original error"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { peer, err := ExtractPeerHostname(tt.err) assert.Equal(t, tt.wantPeer, peer) assert.Equal(t, tt.wantErr, err) }) } } ================================================ FILE: common/errors/tasklist_not_owned_by_host_error.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package errors import "fmt" var _ error = &TaskListNotOwnedByHostError{} type TaskListNotOwnedByHostError struct { OwnedByIdentity string MyIdentity string TasklistName string } func (m *TaskListNotOwnedByHostError) Error() string { return fmt.Sprintf("task list is not owned by this host: OwnedBy: %s, Me: %s, Tasklist: %s", m.OwnedByIdentity, m.MyIdentity, m.TasklistName) } func NewTaskListNotOwnedByHostError(ownedByIdentity string, myIdentity string, tasklistName string) *TaskListNotOwnedByHostError { return &TaskListNotOwnedByHostError{ OwnedByIdentity: ownedByIdentity, MyIdentity: myIdentity, TasklistName: tasklistName, } } ================================================ FILE: common/future/future.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package future import ( "context" "errors" "fmt" "reflect" "sync/atomic" ) type ( // Future represents a value that will be available in the future Future interface { Get(ctx context.Context, valuePtr interface{}) error IsReady() bool } // Settable is for setting value and error for the corresponding Future // The Set method can only be called once. Later calls will result in a // panic since the value and error has already been set. Settable interface { Set(interface{}, error) } futureImpl struct { value interface{} err error readyCh chan struct{} status int32 } ) const ( valueNotSet = iota valueSet ) // NewFuture creates a new Future and the Settable for setting its value func NewFuture() (Future, Settable) { future := &futureImpl{ readyCh: make(chan struct{}), status: valueNotSet, } return future, future } func (f *futureImpl) Get( ctx context.Context, valuePtr interface{}, ) error { if err := ctx.Err(); err != nil { // if the given context is invalid, // guarantee to return an error return err } select { case <-f.readyCh: return f.populateValue(valuePtr) case <-ctx.Done(): return ctx.Err() } } func (f *futureImpl) IsReady() bool { select { case <-f.readyCh: return true default: return false } } func (f *futureImpl) Set( value interface{}, err error, ) { if !atomic.CompareAndSwapInt32(&f.status, valueNotSet, valueSet) { panic("future has already been set") } f.value = value f.err = err close(f.readyCh) } func (f *futureImpl) populateValue( valuePtr interface{}, ) (err error) { defer func() { if p := recover(); p != nil { err = fmt.Errorf("failed to populate valuePtr: %v", p) } }() if f.err != nil || f.value == nil || valuePtr == nil { return f.err } rf := reflect.ValueOf(valuePtr) if rf.Type().Kind() != reflect.Ptr { return errors.New("valuePtr parameter is not a pointer") } fv := reflect.ValueOf(f.value) if fv.IsValid() { rf.Elem().Set(fv) } return nil } ================================================ FILE: common/future/future_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package future import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type ( futureSuite struct { suite.Suite *require.Assertions } testType struct { intField int stringField string timeField time.Time } ) func TestFutureSuite(t *testing.T) { s := new(futureSuite) suite.Run(t, s) } func (s *futureSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *futureSuite) TestFutureSetGet() { var intVal int var listVal []string var mapVal map[string]struct{} var testTypeVal testType testCases := []struct { futureValue interface{} futureErr error valuePtr interface{} expectErr bool }{ { futureValue: testType{ intField: 101, stringField: "a", timeField: time.Now(), }, valuePtr: &testTypeVal, }, { futureValue: int(101), valuePtr: &intVal, }, { futureValue: []string{"a", "b", "c"}, valuePtr: &listVal, }, { futureValue: nil, valuePtr: &listVal, }, { futureValue: map[string]struct{}{"a": {}, "b": {}}, valuePtr: &mapVal, }, { futureValue: map[string]struct{}{"a": {}, "b": {}}, valuePtr: &intVal, expectErr: true, }, { futureValue: int(101), valuePtr: 101, expectErr: true, }, { futureValue: nil, futureErr: errors.New("some random value"), valuePtr: &intVal, expectErr: true, }, { futureValue: time.Now(), valuePtr: nil, }, } for _, tc := range testCases { future, settable := NewFuture() settable.Set(tc.futureValue, tc.futureErr) err := future.Get(context.Background(), tc.valuePtr) if tc.expectErr { if tc.futureErr != nil { s.Equal(tc.futureErr, err) } s.Error(err) continue } s.NoError(err) if tc.valuePtr != nil { switch tc.futureValue.(type) { case int: s.Equal(tc.futureValue, intVal) case []string: s.Equal(tc.futureValue, listVal) case map[string]struct{}: s.Equal(tc.futureValue, mapVal) case testType: s.Equal(tc.futureValue, testTypeVal) } } } } func (s *futureSuite) TestFutureGet_ContextErr() { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100) defer cancel() future, settable := NewFuture() err := future.Get(ctx, nil) s.Error(err) settable.Set("some random value", nil) err = future.Get(ctx, nil) s.Error(err) } func (s *futureSuite) TestFutureIsReady() { future, settable := NewFuture() s.False(future.IsReady()) settable.Set(nil, errors.New("some random error")) s.True(future.IsReady()) } func (s *futureSuite) TestFutureDoubleSet() { _, settable := NewFuture() settable.Set("some random value", nil) s.Panics(func() { settable.Set(nil, errors.New("some random error")) }) } ================================================ FILE: common/headers.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package common import "github.com/uber/cadence/common/types" const ( // LibraryVersionHeaderName refers to the name of the // tchannel / http header that contains the client // library version LibraryVersionHeaderName = "cadence-client-library-version" // FeatureVersionHeaderName refers to the name of the // tchannel / http header that contains the client // feature version // the feature version sent from client represents the // feature set of the cadence client library support. // This can be used for client capibility check, on // Cadence server, for backward compatibility FeatureVersionHeaderName = "cadence-client-feature-version" // Header name to pass the feature flags from customer to client or server ClientFeatureFlagsHeaderName = "cadence-client-feature-flags" // ClientImplHeaderName refers to the name of the // header that contains the client implementation ClientImplHeaderName = "cadence-client-name" // AuthorizationTokenHeaderName refers to the jwt token in the request AuthorizationTokenHeaderName = "cadence-authorization" // AutoforwardingClusterHeaderName refers to the name of the header that contains the source cluster of the auto-forwarding request AutoforwardingClusterHeaderName = "cadence-forwarding-cluster" // PartitionConfigHeaderName refers to the name of the header that contains the json encoded partition config of the request PartitionConfigHeaderName = "cadence-workflow-partition-config" // IsolationGroupHeaderName refers to the name of the header that contains the isolation group of the client IsolationGroupHeaderName = "cadence-worker-isolation-group" // ClientIsolationGroupHeaderName refers to the name of the header that contains the isolation group which the client request is from ClientIsolationGroupHeaderName = "cadence-client-isolation-group" // CallerTypeHeaderName refers to the name of the header that contains the caller type (CLI, UI, SDK, internal, etc.) CallerTypeHeaderName = types.CallerTypeHeaderName ) ================================================ FILE: common/isolationgroup/context.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroup import "context" const ( GroupKey = "isolation-group" OriginalGroupKey = "original-isolation-group" WorkflowIDKey = "wf-id" ) type configKey struct{} type isolationGroupKey struct{} // ConfigFromContext retrieves infomation about the partition config of the context // which is used for tasklist isolation func ConfigFromContext(ctx context.Context) map[string]string { val, ok := ctx.Value(configKey{}).(map[string]string) if !ok { return nil } return val } // ContextWithConfig stores the partition config of tasklist isolation into the given context func ContextWithConfig(ctx context.Context, partitionConfig map[string]string) context.Context { return context.WithValue(ctx, configKey{}, partitionConfig) } // IsolationGroupFromContext retrieves the isolation group from the given context, // which is used to identify which isolation group the poller is from func IsolationGroupFromContext(ctx context.Context) string { val, ok := ctx.Value(isolationGroupKey{}).(string) if !ok { return "" } return val } // ContextWithIsolationGroup stores the isolation group into the given context func ContextWithIsolationGroup(ctx context.Context, isolationGroup string) context.Context { return context.WithValue(ctx, isolationGroupKey{}, isolationGroup) } ================================================ FILE: common/isolationgroup/context_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroup import ( "context" "testing" "github.com/stretchr/testify/assert" ) func TestContext(t *testing.T) { partitionConfig := map[string]string{"test": "abc"} assert.Equal(t, partitionConfig, ConfigFromContext(ContextWithConfig(context.Background(), partitionConfig))) isolationGroup := "zone1" assert.Equal(t, isolationGroup, IsolationGroupFromContext(ContextWithIsolationGroup(context.Background(), isolationGroup))) } ================================================ FILE: common/isolationgroup/defaultisolationgroupstate/state.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package defaultisolationgroupstate import ( "context" "fmt" "sync/atomic" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/isolationgroup/isolationgroupapi" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type defaultIsolationGroupStateHandler struct { status int32 done chan struct{} log log.Logger domainCache cache.DomainCache globalIsolationGroupDrains dynamicconfig.Client config defaultConfig metricsClient metrics.Client } // NewDefaultIsolationGroupStateWatcherWithConfigStoreClient Is a constructor which allows passing in the dynamic config client func NewDefaultIsolationGroupStateWatcherWithConfigStoreClient( logger log.Logger, dc *dynamicconfig.Collection, domainCache cache.DomainCache, cfgStoreClient dynamicconfig.Client, // can be nil, which means global drain is unsupported metricsClient metrics.Client, getIsolationGroups func() []string, ) (isolationgroup.State, error) { stopChan := make(chan struct{}) config := defaultConfig{ IsolationGroupEnabled: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableTasklistIsolation), AllIsolationGroups: getIsolationGroups, } return &defaultIsolationGroupStateHandler{ done: stopChan, domainCache: domainCache, globalIsolationGroupDrains: cfgStoreClient, status: common.DaemonStatusInitialized, log: logger, config: config, metricsClient: metricsClient, }, nil } func (z *defaultIsolationGroupStateHandler) IsDrained(ctx context.Context, domain string, isolationGroup string) (bool, error) { state, err := z.get(ctx, domain) if err != nil { return false, fmt.Errorf("could not determine if drained: %w", err) } return isDrained(isolationGroup, state.Global, state.Domain), nil } func (z *defaultIsolationGroupStateHandler) IsDrainedByDomainID(ctx context.Context, domainID string, isolationGroup string) (bool, error) { domain, err := z.domainCache.GetDomainByID(domainID) if err != nil { return false, fmt.Errorf("could not determine if drained: %w", err) } return z.IsDrained(ctx, domain.GetInfo().Name, isolationGroup) } // Start the state handler func (z *defaultIsolationGroupStateHandler) Start() { if !atomic.CompareAndSwapInt32(&z.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } } func (z *defaultIsolationGroupStateHandler) Stop() { if z == nil { return } if !atomic.CompareAndSwapInt32(&z.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(z.done) } // Get the statue of a isolationGroup, with respect to both domain and global drains. Domain-specific drains override global config // will return nil, nil when it is not enabled func (z *defaultIsolationGroupStateHandler) get(ctx context.Context, domain string) (*isolationGroups, error) { if !z.config.IsolationGroupEnabled(domain) { return &isolationGroups{}, nil } domainData, err := z.domainCache.GetDomain(domain) if err != nil || domainData == nil { return nil, fmt.Errorf("could not resolve domain in isolationGroup handler: %w", err) } domainCfg := domainData.GetConfig() var domainState types.IsolationGroupConfiguration if domainCfg != nil && domainCfg.IsolationGroups != nil { domainState = domainCfg.IsolationGroups } ig := &isolationGroups{ Domain: domainState, } if z.globalIsolationGroupDrains != nil { globalCfg, err := z.globalIsolationGroupDrains.GetListValue(dynamicproperties.DefaultIsolationGroupConfigStoreManagerGlobalMapping, nil) if err != nil { return nil, fmt.Errorf("could not resolve global drains in %w", err) } globalState, err := isolationgroupapi.MapDynamicConfigResponse(globalCfg) if err != nil { return nil, fmt.Errorf("could not resolve global drains in isolationGroup handler: %w", err) } ig.Global = globalState } return ig, nil } func isDrained(isolationGroup string, global types.IsolationGroupConfiguration, domain types.IsolationGroupConfiguration) bool { globalCfg, hasGlobalConfig := global[isolationGroup] domainCfg, hasDomainConfig := domain[isolationGroup] if hasGlobalConfig { if globalCfg.State == types.IsolationGroupStateDrained { return true } } if hasDomainConfig { if domainCfg.State == types.IsolationGroupStateDrained { return true } } return false } ================================================ FILE: common/isolationgroup/defaultisolationgroupstate/state_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package defaultisolationgroupstate import ( "context" "encoding/json" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/isolationgroup/isolationgroupapi" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestIsDrainedHandler(t *testing.T) { validInput := types.IsolationGroupConfiguration{ "zone-1": types.IsolationGroupPartition{ Name: "zone-1", State: types.IsolationGroupStateHealthy, }, "zone-2": types.IsolationGroupPartition{ Name: "zone-2", State: types.IsolationGroupStateDrained, }, } validCfg, _ := isolationgroupapi.MapUpdateGlobalIsolationGroupsRequest(validInput) validCfgData := validCfg[0].Value.GetData() dynamicConfigResponse := []interface{}{} json.Unmarshal(validCfgData, &dynamicConfigResponse) tests := map[string]struct { requestIsolationgroup string dcAffordance func(client *dynamicconfig.MockClient) domainAffordance func(mock *cache.MockDomainCache) cfg defaultConfig expected bool expectedErr error }{ "normal case - no drains present - no configuration specifying a drain - feature is enabled": { requestIsolationgroup: "zone-3", // no config specified for this cfg: defaultConfig{ IsolationGroupEnabled: func(string) bool { return true }, AllIsolationGroups: func() []string { return []string{"zone-1", "zone-2", "zone-3"} }, }, dcAffordance: func(client *dynamicconfig.MockClient) { client.EXPECT().GetListValue( dynamicproperties.DefaultIsolationGroupConfigStoreManagerGlobalMapping, gomock.Any(), // covering the mapping in the mapper unit-test instead ).Return(dynamicConfigResponse, nil) }, domainAffordance: func(mock *cache.MockDomainCache) { domainResponse := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ID: "domain-id", Name: "domain"}, &persistence.DomainConfig{}, true, nil, 0, nil, 0, 0, 0) mock.EXPECT().GetDomainByID("domain-id").Return(domainResponse, nil) mock.EXPECT().GetDomain("domain").Return(domainResponse, nil) }, expected: false, }, "normal case - drains present - feature is enabled": { requestIsolationgroup: "zone-2", // no config specified for this cfg: defaultConfig{ IsolationGroupEnabled: func(string) bool { return true }, AllIsolationGroups: func() []string { return []string{"zone-1", "zone-2", "zone-3"} }, }, dcAffordance: func(client *dynamicconfig.MockClient) { client.EXPECT().GetListValue( dynamicproperties.DefaultIsolationGroupConfigStoreManagerGlobalMapping, gomock.Any(), // covering the mapping in the mapper unit-test instead ).Return(dynamicConfigResponse, nil) }, domainAffordance: func(mock *cache.MockDomainCache) { domainResponse := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ID: "domain-id", Name: "domain"}, &persistence.DomainConfig{}, true, nil, 0, nil, 0, 0, 0) mock.EXPECT().GetDomainByID("domain-id").Return(domainResponse, nil) mock.EXPECT().GetDomain("domain").Return(domainResponse, nil) }, expected: true, }, "normal case - feature is disabled": { requestIsolationgroup: "zone-2", // no config specified for this cfg: defaultConfig{ IsolationGroupEnabled: func(string) bool { return false }, AllIsolationGroups: func() []string { return []string{"zone-1", "zone-2", "zone-3"} }, }, dcAffordance: func(client *dynamicconfig.MockClient) {}, domainAffordance: func(mock *cache.MockDomainCache) { domainResponse := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ID: "domain-id", Name: "domain"}, &persistence.DomainConfig{}, true, nil, 0, nil, 0, 0, 0) mock.EXPECT().GetDomainByID("domain-id").Return(domainResponse, nil) }, expected: false, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { mockCtl := gomock.NewController(t) dcMock := dynamicconfig.NewMockClient(mockCtl) domaincacheMock := cache.NewMockDomainCache(mockCtl) td.dcAffordance(dcMock) td.domainAffordance(domaincacheMock) handler := defaultIsolationGroupStateHandler{ log: testlogger.New(t), globalIsolationGroupDrains: dcMock, domainCache: domaincacheMock, config: td.cfg, } res, err := handler.IsDrainedByDomainID(context.Background(), "domain-id", td.requestIsolationgroup) assert.Equal(t, td.expected, res) assert.Equal(t, td.expectedErr, err) }) } } func TestIsDrained(t *testing.T) { igA := "isolationGroupA" igB := "isolationGroupB" isolationGroupsAllHealthy := types.IsolationGroupConfiguration{ igA: { Name: igA, State: types.IsolationGroupStateHealthy, }, igB: { Name: igB, State: types.IsolationGroupStateHealthy, }, } isolationGroupsOneDrain := types.IsolationGroupConfiguration{ igA: { Name: igA, State: types.IsolationGroupStateDrained, }, } tests := map[string]struct { globalIGCfg types.IsolationGroupConfiguration domainIGCfg types.IsolationGroupConfiguration isolationGroup string expected bool }{ "default behaviour - no drains - isolationGroup is specified": { globalIGCfg: isolationGroupsAllHealthy, domainIGCfg: isolationGroupsAllHealthy, isolationGroup: igA, expected: false, }, "default behaviour - no drains - isolationGroup is not specified": { globalIGCfg: isolationGroupsAllHealthy, domainIGCfg: isolationGroupsAllHealthy, isolationGroup: "some-not-specified-drain", expected: false, }, "default behaviour - globalDrain": { globalIGCfg: isolationGroupsOneDrain, domainIGCfg: isolationGroupsAllHealthy, isolationGroup: igA, expected: true, }, "default behaviour - domainDrain": { globalIGCfg: isolationGroupsAllHealthy, domainIGCfg: isolationGroupsOneDrain, isolationGroup: igA, expected: true, }, "default behaviour - both ": { globalIGCfg: isolationGroupsOneDrain, domainIGCfg: isolationGroupsOneDrain, isolationGroup: igA, expected: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, isDrained(td.isolationGroup, td.globalIGCfg, td.domainIGCfg)) }) } } func TestIsolationGroupStateMapping(t *testing.T) { z1 := types.IsolationGroupPartition{ Name: "zone-1", State: types.IsolationGroupStateHealthy, } z2 := types.IsolationGroupPartition{ Name: "zone-2", State: types.IsolationGroupStateDrained, } // JSON serialization inside the dynamic config library makes a mess of things because // it doesn't have any type information. So mimicing this type information loss to ensure // any field name types or similar serialization quirks are picked up by the test zMarshalled, _ := json.Marshal([]types.IsolationGroupPartition{z1, z2}) var rawIsolationGroupDataMarshalled []interface{} json.Unmarshal(zMarshalled, &rawIsolationGroupDataMarshalled) tests := map[string]struct { in []interface{} expected types.IsolationGroupConfiguration expectedErr error }{ "valid mapping": { in: rawIsolationGroupDataMarshalled, expected: map[string]types.IsolationGroupPartition{ "zone-1": { Name: "zone-1", State: types.IsolationGroupStateHealthy, }, "zone-2": { Name: "zone-2", State: types.IsolationGroupStateDrained, }, }, }, "empty mapping": { in: nil, expected: nil, }, "invalid mapping 1": { in: []interface{}{"invalid"}, expectedErr: errors.New("failed parse a dynamic config entry, map[], (got invalid)"), }, "invalid mapping 2": { in: []interface{}{`{"Name": "some zone", "State": "not the right type"}`}, expectedErr: errors.New(`failed parse a dynamic config entry, map[], (got {"Name": "some zone", "State": "not the right type"})`), }, } for name, td := range tests { t.Run(name, func(t *testing.T) { res, err := isolationgroupapi.MapDynamicConfigResponse(td.in) assert.Equal(t, td.expected, res) assert.Equal(t, td.expectedErr, err) }) } } func TestUpdateRequest(t *testing.T) { tests := map[string]struct { in types.IsolationGroupConfiguration expected []*types.DynamicConfigValue expectedErr error }{ "valid mapping": { in: types.IsolationGroupConfiguration{ "zone-1": { Name: "zone-1", State: types.IsolationGroupStateHealthy, }, "zone-2": { Name: "zone-2", State: types.IsolationGroupStateDrained, }, }, expected: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`[{"Name":"zone-1","State":1},{"Name":"zone-2","State":2}]`), }, Filters: nil, }, }, }, "empty mapping": { in: types.IsolationGroupConfiguration{}, expected: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`[]`), }, Filters: nil, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { res, err := isolationgroupapi.MapUpdateGlobalIsolationGroupsRequest(td.in) assert.Equal(t, td.expected, res) assert.Equal(t, td.expectedErr, err) }) } } func TestNewDefaultIsolationGroupStateWatcherWithConfigStoreClient(t *testing.T) { dc := dynamicconfig.NewNopCollection() domainCache := cache.NewNoOpDomainCache() client := metrics.NewNoopMetricsClient() ig := func() []string { return nil } NewDefaultIsolationGroupStateWatcherWithConfigStoreClient( log.NewNoop(), dc, domainCache, nil, client, ig, ) } func TestIsolationGroupShutdown(t *testing.T) { var v defaultIsolationGroupStateHandler assert.NotPanics(t, func() { v.Stop() }) } ================================================ FILE: common/isolationgroup/defaultisolationgroupstate/types.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package defaultisolationgroupstate import ( "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) // IsolationGroups is an internal convenience return type of a collection of IsolationGroup configurations type isolationGroups struct { Global types.IsolationGroupConfiguration Domain types.IsolationGroupConfiguration } // defaultConfig values for the partitioning library for segmenting portions of workflows into isolation-groups - a resiliency // concept meant to help move workflows around and away from failure zones. type defaultConfig struct { // IsolationGroupEnabled is a domain-based configuration value for whether this feature is enabled at all IsolationGroupEnabled dynamicproperties.BoolPropertyFnWithDomainFilter // AllIsolationGroups is all the possible isolation-groups available for a region AllIsolationGroups func() []string } ================================================ FILE: common/isolationgroup/interface.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroup import ( "context" "github.com/uber/cadence/common" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination isolation_group_mock.go -self_package github.com/uber/cadence/common/isolationgroup // State is a heavily cached in-memory library for returning the state of what zones are healthy or // drained presently. It may return an inclusive (allow-list based) or an exclusive (deny-list based) set of IsolationGroups // depending on the implementation. type State interface { common.Daemon // IsDrained answers the question - "is this particular isolationGroup drained?". Used by startWorkflow calls // and similar sync frontend calls to make routing decisions IsDrained(ctx context.Context, Domain string, IsolationGroup string) (bool, error) IsDrainedByDomainID(ctx context.Context, DomainID string, IsolationGroup string) (bool, error) } ================================================ FILE: common/isolationgroup/isolation_group_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package isolationgroup -source interface.go -destination isolation_group_mock.go -self_package github.com/uber/cadence/common/isolationgroup // // Package isolationgroup is a generated GoMock package. package isolationgroup import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockState is a mock of State interface. type MockState struct { ctrl *gomock.Controller recorder *MockStateMockRecorder isgomock struct{} } // MockStateMockRecorder is the mock recorder for MockState. type MockStateMockRecorder struct { mock *MockState } // NewMockState creates a new mock instance. func NewMockState(ctrl *gomock.Controller) *MockState { mock := &MockState{ctrl: ctrl} mock.recorder = &MockStateMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockState) EXPECT() *MockStateMockRecorder { return m.recorder } // IsDrained mocks base method. func (m *MockState) IsDrained(ctx context.Context, Domain, IsolationGroup string) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsDrained", ctx, Domain, IsolationGroup) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsDrained indicates an expected call of IsDrained. func (mr *MockStateMockRecorder) IsDrained(ctx, Domain, IsolationGroup any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDrained", reflect.TypeOf((*MockState)(nil).IsDrained), ctx, Domain, IsolationGroup) } // IsDrainedByDomainID mocks base method. func (m *MockState) IsDrainedByDomainID(ctx context.Context, DomainID, IsolationGroup string) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsDrainedByDomainID", ctx, DomainID, IsolationGroup) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsDrainedByDomainID indicates an expected call of IsDrainedByDomainID. func (mr *MockStateMockRecorder) IsDrainedByDomainID(ctx, DomainID, IsolationGroup any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDrainedByDomainID", reflect.TypeOf((*MockState)(nil).IsDrainedByDomainID), ctx, DomainID, IsolationGroup) } // Start mocks base method. func (m *MockState) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockStateMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockState)(nil).Start)) } // Stop mocks base method. func (m *MockState) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockStateMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockState)(nil).Stop)) } ================================================ FILE: common/isolationgroup/isolationgroupapi/domain-api-handlers.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroupapi import ( "context" "github.com/uber/cadence/common/types" ) func (z *handlerImpl) GetDomainState(ctx context.Context, request types.GetDomainIsolationGroupsRequest) (*types.GetDomainIsolationGroupsResponse, error) { res, err := z.domainHandler.DescribeDomain(ctx, &types.DescribeDomainRequest{ Name: &request.Domain, }) if err != nil { return nil, err } if res == nil || res.Configuration == nil || res.Configuration.IsolationGroups == nil { return &types.GetDomainIsolationGroupsResponse{}, nil } return &types.GetDomainIsolationGroupsResponse{ IsolationGroups: *res.Configuration.IsolationGroups, }, nil } // UpdateDomainState is the read operation for updating a domain's isolation-groups // todo (david.porter) delete this handler and use domain-handler directly func (z *handlerImpl) UpdateDomainState(ctx context.Context, request types.UpdateDomainIsolationGroupsRequest) error { err := z.domainHandler.UpdateIsolationGroups(ctx, request) return err } ================================================ FILE: common/isolationgroup/isolationgroupapi/domain-api-handlers_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroupapi import ( "context" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. func TestUpdateDomainState(t *testing.T) { tests := map[string]struct { in types.UpdateDomainIsolationGroupsRequest domainHandlerAffordance func(h *domain.MockHandler) expectedErr error }{ "updating normal value": { in: types.UpdateDomainIsolationGroupsRequest{ Domain: "domain", IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": {Name: "zone-1", State: types.IsolationGroupStateHealthy}, "zone-2": {Name: "zone-2", State: types.IsolationGroupStateDrained}, }}, domainHandlerAffordance: func(h *domain.MockHandler) { h.EXPECT().UpdateIsolationGroups(gomock.Any(), types.UpdateDomainIsolationGroupsRequest{ Domain: "domain", IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": {Name: "zone-1", State: types.IsolationGroupStateHealthy}, "zone-2": {Name: "zone-2", State: types.IsolationGroupStateDrained}, }, }).Return(nil) }, }, "empty value - ie removing isolation groups": { in: types.UpdateDomainIsolationGroupsRequest{ Domain: "domain", IsolationGroups: types.IsolationGroupConfiguration{}, }, domainHandlerAffordance: func(h *domain.MockHandler) { h.EXPECT().UpdateIsolationGroups(gomock.Any(), types.UpdateDomainIsolationGroupsRequest{ Domain: "domain", IsolationGroups: types.IsolationGroupConfiguration{}, }).Return(nil) }, }, "error state - nil value - should not happen": { in: types.UpdateDomainIsolationGroupsRequest{ Domain: "domain", IsolationGroups: nil, }, domainHandlerAffordance: func(h *domain.MockHandler) { var ig types.IsolationGroupConfiguration h.EXPECT().UpdateIsolationGroups(gomock.Any(), types.UpdateDomainIsolationGroupsRequest{ Domain: "domain", IsolationGroups: ig, // nil }).Return(nil) }, }, "error returned": { in: types.UpdateDomainIsolationGroupsRequest{ Domain: "domain", IsolationGroups: nil, }, domainHandlerAffordance: func(h *domain.MockHandler) { var ig types.IsolationGroupConfiguration h.EXPECT().UpdateIsolationGroups(gomock.Any(), types.UpdateDomainIsolationGroupsRequest{ Domain: "domain", IsolationGroups: ig, }).Return(assert.AnError) }, expectedErr: assert.AnError, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) dcMock := dynamicconfig.NewMockClient(gomock.NewController(t)) domainHandlerMock := domain.NewMockHandler(ctrl) td.domainHandlerAffordance(domainHandlerMock) handler := handlerImpl{ log: testlogger.New(t), domainHandler: domainHandlerMock, globalIsolationGroupDrains: dcMock, } err := handler.UpdateDomainState(context.Background(), td.in) assert.Equal(t, td.expectedErr, err) }) } } func TestGetDomainState(t *testing.T) { validCfg := types.IsolationGroupConfiguration{ "zone-1": types.IsolationGroupPartition{ Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": types.IsolationGroupPartition{ Name: "zone-2", State: types.IsolationGroupStateHealthy, }, } domainName := "domain" tests := map[string]struct { in types.GetDomainIsolationGroupsRequest expected *types.GetDomainIsolationGroupsResponse domainHandlerAffordance func(h *domain.MockHandler) expectedErr error }{ "normal value being returned - valid config present": { in: types.GetDomainIsolationGroupsRequest{ Domain: domainName, }, domainHandlerAffordance: func(h *domain.MockHandler) { h.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &domainName, }).Return(&types.DescribeDomainResponse{ Configuration: &types.DomainConfiguration{ IsolationGroups: &validCfg, }, }, nil) }, expected: &types.GetDomainIsolationGroupsResponse{IsolationGroups: validCfg}, }, "normal value being returned - no config present - should just return empty": { in: types.GetDomainIsolationGroupsRequest{ Domain: domainName, }, domainHandlerAffordance: func(h *domain.MockHandler) { h.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &domainName, }).Return(&types.DescribeDomainResponse{ Configuration: &types.DomainConfiguration{}, }, nil) }, expected: &types.GetDomainIsolationGroupsResponse{}, }, "normal value being returned - no config present - should just return nil - 2": { in: types.GetDomainIsolationGroupsRequest{ Domain: domainName, }, domainHandlerAffordance: func(h *domain.MockHandler) { h.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &domainName, }).Return(&types.DescribeDomainResponse{ Configuration: &types.DomainConfiguration{ IsolationGroups: &types.IsolationGroupConfiguration{}, }, }, nil) }, expected: &types.GetDomainIsolationGroupsResponse{ IsolationGroups: types.IsolationGroupConfiguration{}, }, }, "an error was returned": { in: types.GetDomainIsolationGroupsRequest{ Domain: domainName, }, domainHandlerAffordance: func(h *domain.MockHandler) { h.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &domainName, }).Return(nil, assert.AnError) }, expectedErr: assert.AnError, }, "invalid input - nil input": { in: types.GetDomainIsolationGroupsRequest{}, domainHandlerAffordance: func(h *domain.MockHandler) { n := "" h.EXPECT(). DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &n, }). Return(nil, nil) }, expected: &types.GetDomainIsolationGroupsResponse{}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) domainHandlerMock := domain.NewMockHandler(ctrl) td.domainHandlerAffordance(domainHandlerMock) handler := handlerImpl{ log: testlogger.New(t), domainHandler: domainHandlerMock, } res, err := handler.GetDomainState(context.Background(), td.in) assert.Equal(t, td.expected, res) if td.expectedErr != nil { assert.Equal(t, td.expectedErr.Error(), err.Error()) } }) } } ================================================ FILE: common/isolationgroup/isolationgroupapi/global-api-handlers.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroupapi import ( "context" "errors" "fmt" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) func (z *handlerImpl) UpdateGlobalState(ctx context.Context, in types.UpdateGlobalIsolationGroupsRequest) error { if z.globalIsolationGroupDrains == nil { return &types.BadRequestError{"global isolation group drain is not supported in this cluster"} } mappedInput, err := MapUpdateGlobalIsolationGroupsRequest(in.IsolationGroups) if err != nil { return err } return z.globalIsolationGroupDrains.UpdateValue( dynamicproperties.DefaultIsolationGroupConfigStoreManagerGlobalMapping, mappedInput, ) } func (z *handlerImpl) GetGlobalState(ctx context.Context) (*types.GetGlobalIsolationGroupsResponse, error) { if z.globalIsolationGroupDrains == nil { return nil, &types.BadRequestError{"global isolation group drain is not supported in this cluster"} } res, err := z.globalIsolationGroupDrains.GetListValue(dynamicproperties.DefaultIsolationGroupConfigStoreManagerGlobalMapping, nil) if err != nil { var e types.EntityNotExistsError if errors.As(err, &e) { return &types.GetGlobalIsolationGroupsResponse{}, nil } return nil, fmt.Errorf("failed to get global isolation groups from datastore: %w", err) } resp, err := MapDynamicConfigResponse(res) if err != nil { return nil, fmt.Errorf("failed to get global isolation groups from datastore: %w", err) } return &types.GetGlobalIsolationGroupsResponse{IsolationGroups: resp}, nil } ================================================ FILE: common/isolationgroup/isolationgroupapi/global-api-handlers_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroupapi import ( "context" "encoding/json" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) func TestUpdateGlobalState(t *testing.T) { tests := map[string]struct { in types.UpdateGlobalIsolationGroupsRequest dcAffordance func(client *dynamicconfig.MockClient) expectedErr error }{ "updating normal value": { in: types.UpdateGlobalIsolationGroupsRequest{IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": {Name: "zone-1", State: types.IsolationGroupStateHealthy}, "zone-2": {Name: "zone-2", State: types.IsolationGroupStateDrained}, }}, dcAffordance: func(client *dynamicconfig.MockClient) { client.EXPECT().UpdateValue( dynamicproperties.DefaultIsolationGroupConfigStoreManagerGlobalMapping, gomock.Any(), // covering the mapping in the mapper unit-test instead ) }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { dcMock := dynamicconfig.NewMockClient(gomock.NewController(t)) td.dcAffordance(dcMock) handler := handlerImpl{ log: testlogger.New(t), globalIsolationGroupDrains: dcMock, } err := handler.UpdateGlobalState(context.Background(), td.in) assert.Equal(t, td.expectedErr, err) }) } } func TestGetGlobalState(t *testing.T) { validInput := types.IsolationGroupConfiguration{ "zone-1": types.IsolationGroupPartition{ Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": types.IsolationGroupPartition{ Name: "zone-2", State: types.IsolationGroupStateHealthy, }, } validCfg, _ := MapUpdateGlobalIsolationGroupsRequest(validInput) validCfgData := validCfg[0].Value.GetData() dynamicConfigResponse := []interface{}{} json.Unmarshal(validCfgData, &dynamicConfigResponse) tests := map[string]struct { in types.GetGlobalIsolationGroupsRequest dcAffordance func(client *dynamicconfig.MockClient) expected *types.GetGlobalIsolationGroupsResponse expectedErr error }{ "updating normal value": { in: types.GetGlobalIsolationGroupsRequest{}, dcAffordance: func(client *dynamicconfig.MockClient) { client.EXPECT().GetListValue( dynamicproperties.DefaultIsolationGroupConfigStoreManagerGlobalMapping, gomock.Any(), ).Return(dynamicConfigResponse, nil) }, expected: &types.GetGlobalIsolationGroupsResponse{IsolationGroups: validInput}, }, "an error was returned": { in: types.GetGlobalIsolationGroupsRequest{}, dcAffordance: func(client *dynamicconfig.MockClient) { client.EXPECT().GetListValue( dynamicproperties.DefaultIsolationGroupConfigStoreManagerGlobalMapping, gomock.Any(), ).Return(nil, errors.New("an error")) }, expectedErr: errors.New("failed to get global isolation groups from datastore: an error"), }, } for name, td := range tests { t.Run(name, func(t *testing.T) { dcMock := dynamicconfig.NewMockClient(gomock.NewController(t)) td.dcAffordance(dcMock) handler := handlerImpl{ log: testlogger.New(t), globalIsolationGroupDrains: dcMock, } res, err := handler.GetGlobalState(context.Background()) assert.Equal(t, td.expected, res) if td.expectedErr != nil { // only compare strings because full wrapping makes it fiddly otherwise assert.Equal(t, td.expectedErr.Error(), err.Error()) } }) } } ================================================ FILE: common/isolationgroup/isolationgroupapi/handler.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroupapi import ( "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log" ) type handlerImpl struct { log log.Logger globalIsolationGroupDrains dynamicconfig.Client domainHandler domain.Handler } func New(log log.Logger, igConfigStore dynamicconfig.Client, dh domain.Handler) Handler { return &handlerImpl{ log: log, globalIsolationGroupDrains: igConfigStore, domainHandler: dh, } } ================================================ FILE: common/isolationgroup/isolationgroupapi/interface.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroupapi import ( "context" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination isolation_handler_mock.go -self_package github.com/uber/cadence/common/isolationgroup/isolationgrouphandler type Handler interface { // GetGlobalState returns the current configuration of isolation-groups which apply to all domains GetGlobalState(ctx context.Context) (*types.GetGlobalIsolationGroupsResponse, error) // UpdateGlobalState updates isolation-groups which apply to all domains UpdateGlobalState(ctx context.Context, state types.UpdateGlobalIsolationGroupsRequest) error // GetDomainState is the read operation for getting the current state of a domain's isolation-groups GetDomainState(ctx context.Context, request types.GetDomainIsolationGroupsRequest) (*types.GetDomainIsolationGroupsResponse, error) // UpdateDomainState is the read operation for updating a domain's isolation-groups UpdateDomainState(ctx context.Context, state types.UpdateDomainIsolationGroupsRequest) error } ================================================ FILE: common/isolationgroup/isolationgroupapi/isolation_handler_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package isolationgroupapi -source interface.go -destination isolation_handler_mock.go -self_package github.com/uber/cadence/common/isolationgroup/isolationgrouphandler // // Package isolationgroupapi is a generated GoMock package. package isolationgroupapi import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockHandler is a mock of Handler interface. type MockHandler struct { ctrl *gomock.Controller recorder *MockHandlerMockRecorder isgomock struct{} } // MockHandlerMockRecorder is the mock recorder for MockHandler. type MockHandlerMockRecorder struct { mock *MockHandler } // NewMockHandler creates a new mock instance. func NewMockHandler(ctrl *gomock.Controller) *MockHandler { mock := &MockHandler{ctrl: ctrl} mock.recorder = &MockHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHandler) EXPECT() *MockHandlerMockRecorder { return m.recorder } // GetDomainState mocks base method. func (m *MockHandler) GetDomainState(ctx context.Context, request types.GetDomainIsolationGroupsRequest) (*types.GetDomainIsolationGroupsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainState", ctx, request) ret0, _ := ret[0].(*types.GetDomainIsolationGroupsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainState indicates an expected call of GetDomainState. func (mr *MockHandlerMockRecorder) GetDomainState(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainState", reflect.TypeOf((*MockHandler)(nil).GetDomainState), ctx, request) } // GetGlobalState mocks base method. func (m *MockHandler) GetGlobalState(ctx context.Context) (*types.GetGlobalIsolationGroupsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetGlobalState", ctx) ret0, _ := ret[0].(*types.GetGlobalIsolationGroupsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetGlobalState indicates an expected call of GetGlobalState. func (mr *MockHandlerMockRecorder) GetGlobalState(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGlobalState", reflect.TypeOf((*MockHandler)(nil).GetGlobalState), ctx) } // UpdateDomainState mocks base method. func (m *MockHandler) UpdateDomainState(ctx context.Context, state types.UpdateDomainIsolationGroupsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomainState", ctx, state) ret0, _ := ret[0].(error) return ret0 } // UpdateDomainState indicates an expected call of UpdateDomainState. func (mr *MockHandlerMockRecorder) UpdateDomainState(ctx, state any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomainState", reflect.TypeOf((*MockHandler)(nil).UpdateDomainState), ctx, state) } // UpdateGlobalState mocks base method. func (m *MockHandler) UpdateGlobalState(ctx context.Context, state types.UpdateGlobalIsolationGroupsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateGlobalState", ctx, state) ret0, _ := ret[0].(error) return ret0 } // UpdateGlobalState indicates an expected call of UpdateGlobalState. func (mr *MockHandlerMockRecorder) UpdateGlobalState(ctx, state any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGlobalState", reflect.TypeOf((*MockHandler)(nil).UpdateGlobalState), ctx, state) } ================================================ FILE: common/isolationgroup/isolationgroupapi/mappers.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroupapi import ( "encoding/json" "fmt" "github.com/uber/cadence/common/types" ) func MapDynamicConfigResponse(in []interface{}) (out types.IsolationGroupConfiguration, err error) { if in == nil { return nil, nil } out = make(types.IsolationGroupConfiguration, len(in)) for _, v := range in { v1, ok := v.(map[string]interface{}) if !ok { return nil, fmt.Errorf("failed parse a dynamic config entry, %v, (got %v)", v1, v) } n, okName := v1["Name"] s, okState := v1["State"] if !okState || !okName { return nil, fmt.Errorf("failed parse a dynamic config entry, %v, (got %v)", v1, v) } nS, okStr := n.(string) sI, okI := s.(float64) if !okStr || !okI { return nil, fmt.Errorf("failed parse a dynamic config entry, %v, (got %v)", v1, v) } out[nS] = types.IsolationGroupPartition{ Name: nS, State: types.IsolationGroupState(sI), } } return out, nil } func MapUpdateGlobalIsolationGroupsRequest(in types.IsolationGroupConfiguration) ([]*types.DynamicConfigValue, error) { jsonData, err := json.Marshal(in.ToPartitionList()) if err != nil { return nil, fmt.Errorf("failed to marshal input for dynamic config: %w", err) } out := []*types.DynamicConfigValue{ &types.DynamicConfigValue{ Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: jsonData, }, }, } return out, nil } func MapAllIsolationGroupsResponse(in []interface{}) ([]string, error) { var allIsolationGroups []string for k := range in { v, ok := in[k].(string) if !ok { return nil, fmt.Errorf("failed to get all-isolation-groups response from dynamic config: got %v (%T)", in[k], in[k]) } allIsolationGroups = append(allIsolationGroups, v) } return allIsolationGroups, nil } ================================================ FILE: common/isolationgroup/isolationgroupapi/mappers_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package isolationgroupapi import ( "errors" "testing" "github.com/stretchr/testify/assert" ) func TestMapAllIsolationGroupStates(t *testing.T) { tests := map[string]struct { in []interface{} expected []string expectedErr error }{ "valid mapping": { in: []interface{}{"zone-1", "zone-2", "zone-3"}, expected: []string{"zone-1", "zone-2", "zone-3"}, }, "invalid mapping": { in: []interface{}{1, 2, 3}, expectedErr: errors.New("failed to get all-isolation-groups response from dynamic config: got 1 (int)"), }, } for name, td := range tests { t.Run(name, func(t *testing.T) { res, err := MapAllIsolationGroupsResponse(td.in) assert.Equal(t, td.expected, res) assert.Equal(t, td.expectedErr, err) }) } } ================================================ FILE: common/json_task_token_serializer.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package common import "encoding/json" type ( jsonTaskTokenSerializer struct{} ) // NewJSONTaskTokenSerializer creates a new instance of TaskTokenSerializer func NewJSONTaskTokenSerializer() TaskTokenSerializer { return &jsonTaskTokenSerializer{} } func (j *jsonTaskTokenSerializer) Serialize(token *TaskToken) ([]byte, error) { data, err := json.Marshal(token) return data, err } func (j *jsonTaskTokenSerializer) Deserialize(data []byte) (*TaskToken, error) { var token TaskToken err := json.Unmarshal(data, &token) return &token, err } func (j *jsonTaskTokenSerializer) SerializeQueryTaskToken(token *QueryTaskToken) ([]byte, error) { data, err := json.Marshal(token) return data, err } func (j *jsonTaskTokenSerializer) DeserializeQueryTaskToken(data []byte) (*QueryTaskToken, error) { var token QueryTaskToken err := json.Unmarshal(data, &token) return &token, err } ================================================ FILE: common/json_task_token_serializer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package common import ( "testing" "github.com/stretchr/testify/require" ) func TestJsonTaskTokenSerializer_Token_RoundTrip(t *testing.T) { token := TaskToken{ DomainID: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: "test-workflow-type", RunID: "test-run-id", ScheduleID: 1, ScheduleAttempt: 1, ActivityID: "test-activity-id", ActivityType: "test-activity-type", } serializer := NewJSONTaskTokenSerializer() serialized, err := serializer.Serialize(&token) require.NoError(t, err) deserializedToken, err := serializer.Deserialize(serialized) require.NoError(t, err) require.Equal(t, token, *deserializedToken) } func TestNewJSONTaskTokenSerializer_QueryToken_Roundtrip(t *testing.T) { token := QueryTaskToken{ DomainID: "test-domain", WorkflowID: "test-workflow-id", RunID: "test-run-id", TaskList: "test-task-list", TaskID: "test-task-id", } serializer := NewJSONTaskTokenSerializer() serialized, err := serializer.SerializeQueryTaskToken(&token) require.NoError(t, err) deserializedToken, err := serializer.DeserializeQueryTaskToken(serialized) require.NoError(t, err) require.Equal(t, token, *deserializedToken) } ================================================ FILE: common/locks/lock.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package locks import ( "context" "sync" ) type ( // Mutex accepts a context in its Lock method. // It blocks the goroutine until either the lock is acquired or the context // is closed. Mutex interface { Lock(context.Context) error Unlock() } ) type impl struct { ch chan struct{} } func NewMutex() Mutex { ch := make(chan struct{}, 1) ch <- struct{}{} return &impl{ ch: ch, } } func (m *impl) Lock(ctx context.Context) error { select { case <-m.ch: return nil case <-ctx.Done(): return ctx.Err() } } func (m *impl) Unlock() { select { case m.ch <- struct{}{}: default: // reaching this branch means the mutex was not locked. // this will intentionally crash the process, and print stack traces. // // it's not really possible to do this by hand (`fatal` is private), // and other common options like `log.Fatal` don't print stacks / don't // print all stacks / have loads of minor inconsistencies. // // regardless, what we want is to mimic mutexes when wrongly unlocked, // so just use the mutex implementation to guarantee it's the same. var m sync.Mutex m.Unlock() } } ================================================ FILE: common/locks/lock_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package locks import ( "context" "os" "sync" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" ) func TestBasicLocking(t *testing.T) { lock := NewMutex() err1 := lock.Lock(context.Background()) require.Nil(t, err1) lock.Unlock() } func TestExpiredContext(t *testing.T) { lock := NewMutex() err1 := lock.Lock(context.Background()) require.Nil(t, err1) ctx, cancel := context.WithTimeout(context.Background(), time.Minute) cancel() err2 := lock.Lock(ctx) require.NotNil(t, err2) require.Equal(t, err2, ctx.Err()) lock.Unlock() err3 := lock.Lock(context.Background()) require.Nil(t, err3) lock.Unlock() } func BenchmarkLock(b *testing.B) { l := NewMutex() ctx := context.Background() for n := 0; n < b.N; n++ { _ = l.Lock(ctx) l.Unlock() } } func BenchmarkStdlibLock(b *testing.B) { var m sync.Mutex for n := 0; n < b.N; n++ { m.Lock() m.Unlock() } } func BenchmarkParallelLock(b *testing.B) { l := NewMutex() ctx := context.Background() b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = l.Lock(ctx) l.Unlock() } }) } func BenchmarkParallelStdlibLock(b *testing.B) { var m sync.Mutex b.RunParallel(func(pb *testing.PB) { for pb.Next() { m.Lock() m.Unlock() } }) } func TestShouldBeFatal(t *testing.T) { t.Run("stdlib", func(t *testing.T) { fatalTest(t) var m sync.Mutex m.Unlock() }) t.Run("custom", func(t *testing.T) { fatalTest(t) NewMutex().Unlock() }) } func fatalTest(t *testing.T) { t.Helper() if os.Getenv("RUN_FATAL_TESTS") != "true" { t.Skipf( "This test intentionally crashes the process, and cannot be tested normally.\n"+ "Check output manually when running this test on its own:\n"+ "\tRUN_FATAL_TESTS=true go test -test.run %q %v", t.Name(), "github.com/uber/cadence/common/locks", ) } else { t.Cleanup(func() { t.Fatal("process should have crashed, and this should be unreachable") }) } } func TestConcurrently(t *testing.T) { defer goleak.VerifyNone(t) const ( tryDuration = time.Millisecond // long enough to ensure at least one acquire tries = 1000 ) // attempt to cancel all attempts within a reasonable amount of time ctx, cancel := context.WithTimeout(context.Background(), tryDuration*tries*10) defer cancel() success, err := int32(0), int32(0) m := NewMutex() var wg sync.WaitGroup for i := 0; i < tries; i++ { wg.Add(1) go func() { defer wg.Done() ctx, cancel := context.WithTimeout(ctx, tryDuration) defer cancel() if m.Lock(ctx) == nil { atomic.AddInt32(&success, 1) time.Sleep(tryDuration / 10) // sleep for a fraction of the time, to guarantee some timeouts m.Unlock() } else { atomic.AddInt32(&err, 1) } }() } wg.Wait() t.Logf("success: %v, err: %v", success, err) assert.Greater(t, success, int32(1), "should have successfully acquired more than once, as once could be a fluke") assert.NotZero(t, err, "should have timed out at least once") } ================================================ FILE: common/log/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package log import "github.com/uber/cadence/common/log/tag" //go:generate mockgen -package=$GOPACKAGE -destination=logger_mock.go -self_package=github.com/uber/cadence/common/log github.com/uber/cadence/common/log Logger // Logger is our abstraction for logging // Usage examples: // // import "github.com/uber/cadence/common/log/tag" // 1) logger = logger.WithTags( // tag.WorkflowNextEventID( 123), // tag.WorkflowActionWorkflowStarted, // tag.WorkflowDomainID("test-domain-id")) // logger.Info("hello world") // 2) logger.Info("hello world", // tag.WorkflowNextEventID( 123), // tag.WorkflowActionWorkflowStarted, // tag.WorkflowDomainID("test-domain-id")) // ) // Note: msg should be static, it is not recommended to use fmt.Sprintf() for msg. // Anything dynamic should be tagged. type Logger interface { Debugf(msg string, args ...any) Debug(msg string, tags ...tag.Tag) Info(msg string, tags ...tag.Tag) Warn(msg string, tags ...tag.Tag) Error(msg string, tags ...tag.Tag) Fatal(msg string, tags ...tag.Tag) WithTags(tags ...tag.Tag) Logger SampleInfo(msg string, sampleRate int, tags ...tag.Tag) DebugOn() bool // Helper returns a logger that will skip one more level in stack trace. This is helpful for layered architecture, when you want to point to a business logic error, instead of pointing to the wrapped generated level. Helper() Logger } type noop struct{} // NewNoop return a noop logger func NewNoop() Logger { return &noop{} } func (n *noop) Debugf(msg string, args ...any) {} func (n *noop) Debug(msg string, tags ...tag.Tag) {} func (n *noop) Info(msg string, tags ...tag.Tag) {} func (n *noop) Warn(msg string, tags ...tag.Tag) {} func (n *noop) Error(msg string, tags ...tag.Tag) {} func (n *noop) Fatal(msg string, tags ...tag.Tag) {} func (n *noop) SampleInfo(msg string, sampleRate int, tags ...tag.Tag) {} func (n *noop) WithTags(tags ...tag.Tag) Logger { return n } func (n *noop) DebugOn() bool { return true } func (n *noop) Helper() Logger { return &noop{} } ================================================ FILE: common/log/logfx/fx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package logfx import ( "go.uber.org/fx" "go.uber.org/zap" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" ) // Module provides fx options to initialize logger from configuration. var Module = fx.Options( fx.Provide(zapBuilder), ModuleWithoutZap, ) // ModuleWithoutZap provides fx options to initialize logger from existing zap logger, which might be provided outside. // This is useful for monorepos or any application that uses centralize log configuration like Uber monorepo. var ModuleWithoutZap = fx.Options( fx.Provide(log.NewLogger)) type zapBuilderParams struct { fx.In Cfg config.Config } func zapBuilder(p zapBuilderParams) (*zap.Logger, error) { logger, err := p.Cfg.Log.NewZapLogger() if err != nil { return nil, err } return logger, nil } ================================================ FILE: common/log/logfx/fx_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package logfx import ( "testing" "go.uber.org/fx" "go.uber.org/fx/fxtest" "go.uber.org/zap" "go.uber.org/zap/zaptest" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" ) func TestLogFx(t *testing.T) { app := fxtest.New(t, fx.Provide(func() config.Config { return config.Config{} }), Module, fx.Invoke(func(logger log.Logger) { logger.Info("hello world") }), ) app.RequireStart().RequireStop() } func TestLogFxWithExternalZapLogger(t *testing.T) { app := fxtest.New(t, fx.Provide(func() *zap.Logger { return zaptest.NewLogger(t) }), ModuleWithoutZap, fx.Invoke(func(logger log.Logger) { logger.Info("hello world") })) app.RequireStart().RequireStop() } ================================================ FILE: common/log/logger.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package log import ( "fmt" "math/rand" "path/filepath" "runtime" "go.uber.org/zap" "go.uber.org/zap/zapcore" "github.com/uber/cadence/common/log/tag" ) type loggerImpl struct { zapLogger *zap.Logger skip int sampleLocalFn func(int) bool } const ( skipForDefaultLogger = 3 // we put a default message when it is empty so that the log can be searchable/filterable defaultMsgForEmpty = "none" ) var defaultSampleFn = func(i int) bool { return rand.Intn(i) == 0 } // NewLogger returns a new logger func NewLogger(zapLogger *zap.Logger, opts ...Option) Logger { impl := &loggerImpl{ // Skip wrapped logger from the stack trace so that zap logs will point to the caller. zapLogger: zapLogger.WithOptions(zap.AddCallerSkip(1)), skip: skipForDefaultLogger, sampleLocalFn: defaultSampleFn, } for _, opt := range opts { opt(impl) } return impl } func caller(skip int) string { _, path, lineno, ok := runtime.Caller(skip) if !ok { return "" } return fmt.Sprintf("%v:%v", filepath.Base(path), lineno) } func (lg *loggerImpl) buildFieldsWithCallat(tags []tag.Tag) []zap.Field { fs := lg.buildFields(tags) fs = append(fs, zap.String(tag.LoggingCallAtKey, caller(lg.skip))) return fs } func (lg *loggerImpl) buildFields(tags []tag.Tag) []zap.Field { fs := make([]zap.Field, 0, len(tags)) for _, t := range tags { f := t.Field() if f.Key == "" { // ignore empty field(which can be constructed manually) continue } fs = append(fs, f) if obj, ok := f.Interface.(zapcore.ObjectMarshaler); ok && f.Type == zapcore.ErrorType { fs = append(fs, zap.Object(f.Key+"-details", obj)) } } return fs } func setDefaultMsg(msg string) string { if msg == "" { return defaultMsgForEmpty } return msg } func (lg *loggerImpl) Debugf(msg string, args ...any) { if !lg.DebugOn() { return } fields := lg.buildFieldsWithCallat(nil) lg.zapLogger.Debug(setDefaultMsg(fmt.Sprintf(msg, args...)), fields...) } func (lg *loggerImpl) Debug(msg string, tags ...tag.Tag) { if !lg.DebugOn() { return } fields := lg.buildFieldsWithCallat(tags) lg.zapLogger.Debug(msg, fields...) } func (lg *loggerImpl) Info(msg string, tags ...tag.Tag) { msg = setDefaultMsg(msg) fields := lg.buildFieldsWithCallat(tags) lg.zapLogger.Info(msg, fields...) } func (lg *loggerImpl) Warn(msg string, tags ...tag.Tag) { msg = setDefaultMsg(msg) fields := lg.buildFieldsWithCallat(tags) lg.zapLogger.Warn(msg, fields...) } func (lg *loggerImpl) Error(msg string, tags ...tag.Tag) { msg = setDefaultMsg(msg) fields := lg.buildFieldsWithCallat(tags) lg.zapLogger.Error(msg, fields...) } func (lg *loggerImpl) Fatal(msg string, tags ...tag.Tag) { msg = setDefaultMsg(msg) fields := lg.buildFieldsWithCallat(tags) lg.zapLogger.Fatal(msg, fields...) } func (lg *loggerImpl) WithTags(tags ...tag.Tag) Logger { fields := lg.buildFields(tags) zapLogger := lg.zapLogger.With(fields...) return &loggerImpl{ zapLogger: zapLogger, skip: lg.skip, sampleLocalFn: lg.sampleLocalFn, } } // DebugOn checks if debug level is on. func (lg *loggerImpl) DebugOn() bool { return lg.zapLogger.Check(zap.DebugLevel, "test") != nil } func (lg *loggerImpl) SampleInfo(msg string, sampleRate int, tags ...tag.Tag) { if lg.sampleLocalFn(sampleRate) { msg = setDefaultMsg(msg) fields := lg.buildFieldsWithCallat(tags) lg.zapLogger.Info(msg, fields...) } } func (lg *loggerImpl) Helper() Logger { return &loggerImpl{ // AddCallerSkip increments the value so we don't need to track previous level. zapLogger: lg.zapLogger.WithOptions(zap.AddCallerSkip(1)), skip: lg.skip, sampleLocalFn: lg.sampleLocalFn, } } ================================================ FILE: common/log/logger_bench_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package log import ( "testing" "go.uber.org/zap" "go.uber.org/zap/zapcore" "github.com/uber/cadence/common/log/tag" ) /** longer@~/gocode/src/github.com/uber/cadence/common/log:(log)$ go test -v -bench=. | egrep "(Bench)|(ns/op)" BenchmarkZapLoggerWithFields-8 {"level":"info","ts":1555094254.794006,"caller":"log/logger_bench_test.go:21","msg":"msg to print log, 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890","wf-schedule-id":0,"cluster-name":"this is a very long value: 1234567890 1234567890 1234567890 1234567890","wf-domain-name":"test-domain-name"} 200000 8609 ns/op BenchmarkLoggerWithFields-8 {"level":"info","ts":1555094256.608516,"msg":"msg to print log, 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890","wf-schedule-id":0,"cluster-name":"this is a very long value: 1234567890 1234567890 1234567890 1234567890","wf-domain-name":"test-domain-name","logging-call-at":"logger_bench_test.go:36"} 200000 8773 ns/op BenchmarkZapLoggerWithoutFields-8 {"level":"info","ts":1555094258.4521542,"caller":"log/logger_bench_test.go:49","msg":"msg to print log, 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890","wf-schedule-id":0,"cluster-name":"this is a very long value: 1234567890 1234567890 1234567890 1234567890","wf-domain-name":"test-domain-name"} 200000 8535 ns/op BenchmarkLoggerWithoutFields-8 {"level":"info","ts":1555094260.2499342,"msg":"msg to print log, 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890","wf-domain-name":"test-domain-name","wf-schedule-id":0,"cluster-name":"this is a very long value: 1234567890 1234567890 1234567890 1234567890","logging-call-at":"logger_bench_test.go:64"} 200000 8998 ns/op */ func BenchmarkZapLoggerWithFields(b *testing.B) { zapLogger, err := buildZapLoggerForZap() if err != nil { b.Fail() } for i := 0; i < b.N; i++ { lg := zapLogger.With(zap.Int64("wf-schedule-id", int64(i)), zap.String("cluster-name", "this is a very long value: 1234567890 1234567890 1234567890 1234567890")) lg.Info("msg to print log, 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890", zap.String("wf-domain-name", "test-domain-name")) } } func BenchmarkLoggerWithFields(b *testing.B) { zapLogger, err := buildZapLoggerForDefault() if err != nil { b.Fail() } logger := NewLogger(zapLogger) for i := 0; i < b.N; i++ { lg := logger.WithTags(tag.WorkflowScheduleID(int64(i)), tag.ClusterName("this is a very long value: 1234567890 1234567890 1234567890 1234567890")) lg.Info("msg to print log, 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890", tag.WorkflowDomainName("test-domain-name")) } } func BenchmarkZapLoggerWithoutFields(b *testing.B) { zapLogger, err := buildZapLoggerForZap() if err != nil { b.Fail() } for i := 0; i < b.N; i++ { zapLogger.Info("msg to print log, 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890", zap.Int64("wf-schedule-id", int64(i)), zap.String("cluster-name", "this is a very long value: 1234567890 1234567890 1234567890 1234567890"), zap.String("wf-domain-name", "test-domain-name")) } } func BenchmarkLoggerWithoutFields(b *testing.B) { zapLogger, err := buildZapLoggerForDefault() if err != nil { b.Fail() } logger := NewLogger(zapLogger) for i := 0; i < b.N; i++ { logger.Info("msg to print log, 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890", tag.WorkflowDomainName("test-domain-name"), tag.WorkflowScheduleID(int64(i)), tag.ClusterName("this is a very long value: 1234567890 1234567890 1234567890 1234567890")) } } func buildZapLoggerForZap() (*zap.Logger, error) { encConfig := zap.NewProductionEncoderConfig() cfg := zap.Config{ Level: zap.NewAtomicLevelAt(zap.InfoLevel), Development: false, Sampling: nil, Encoding: "json", EncoderConfig: encConfig, OutputPaths: []string{"stderr"}, ErrorOutputPaths: []string{"stderr"}, } return cfg.Build() } // only difference here is we disable caller since we already have another one func buildZapLoggerForDefault() (*zap.Logger, error) { encConfig := zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", NameKey: "logger", CallerKey: "", // disable caller here since we already have another one MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.EpochTimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: nil, } cfg := zap.Config{ Level: zap.NewAtomicLevelAt(zap.InfoLevel), Development: false, Sampling: nil, Encoding: "json", EncoderConfig: encConfig, OutputPaths: []string{"stderr"}, ErrorOutputPaths: []string{"stderr"}, } return cfg.Build() } ================================================ FILE: common/log/logger_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/log (interfaces: Logger) // // Generated by this command: // // mockgen -package=log -destination=logger_mock.go -self_package=github.com/uber/cadence/common/log github.com/uber/cadence/common/log Logger // // Package log is a generated GoMock package. package log import ( reflect "reflect" gomock "go.uber.org/mock/gomock" tag "github.com/uber/cadence/common/log/tag" ) // MockLogger is a mock of Logger interface. type MockLogger struct { ctrl *gomock.Controller recorder *MockLoggerMockRecorder isgomock struct{} } // MockLoggerMockRecorder is the mock recorder for MockLogger. type MockLoggerMockRecorder struct { mock *MockLogger } // NewMockLogger creates a new mock instance. func NewMockLogger(ctrl *gomock.Controller) *MockLogger { mock := &MockLogger{ctrl: ctrl} mock.recorder = &MockLoggerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockLogger) EXPECT() *MockLoggerMockRecorder { return m.recorder } // Debug mocks base method. func (m *MockLogger) Debug(msg string, tags ...tag.Tag) { m.ctrl.T.Helper() varargs := []any{msg} for _, a := range tags { varargs = append(varargs, a) } m.ctrl.Call(m, "Debug", varargs...) } // Debug indicates an expected call of Debug. func (mr *MockLoggerMockRecorder) Debug(msg any, tags ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{msg}, tags...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), varargs...) } // DebugOn mocks base method. func (m *MockLogger) DebugOn() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DebugOn") ret0, _ := ret[0].(bool) return ret0 } // DebugOn indicates an expected call of DebugOn. func (mr *MockLoggerMockRecorder) DebugOn() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DebugOn", reflect.TypeOf((*MockLogger)(nil).DebugOn)) } // Debugf mocks base method. func (m *MockLogger) Debugf(msg string, args ...any) { m.ctrl.T.Helper() varargs := []any{msg} for _, a := range args { varargs = append(varargs, a) } m.ctrl.Call(m, "Debugf", varargs...) } // Debugf indicates an expected call of Debugf. func (mr *MockLoggerMockRecorder) Debugf(msg any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{msg}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...) } // Error mocks base method. func (m *MockLogger) Error(msg string, tags ...tag.Tag) { m.ctrl.T.Helper() varargs := []any{msg} for _, a := range tags { varargs = append(varargs, a) } m.ctrl.Call(m, "Error", varargs...) } // Error indicates an expected call of Error. func (mr *MockLoggerMockRecorder) Error(msg any, tags ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{msg}, tags...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), varargs...) } // Fatal mocks base method. func (m *MockLogger) Fatal(msg string, tags ...tag.Tag) { m.ctrl.T.Helper() varargs := []any{msg} for _, a := range tags { varargs = append(varargs, a) } m.ctrl.Call(m, "Fatal", varargs...) } // Fatal indicates an expected call of Fatal. func (mr *MockLoggerMockRecorder) Fatal(msg any, tags ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{msg}, tags...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fatal", reflect.TypeOf((*MockLogger)(nil).Fatal), varargs...) } // Helper mocks base method. func (m *MockLogger) Helper() Logger { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Helper") ret0, _ := ret[0].(Logger) return ret0 } // Helper indicates an expected call of Helper. func (mr *MockLoggerMockRecorder) Helper() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Helper", reflect.TypeOf((*MockLogger)(nil).Helper)) } // Info mocks base method. func (m *MockLogger) Info(msg string, tags ...tag.Tag) { m.ctrl.T.Helper() varargs := []any{msg} for _, a := range tags { varargs = append(varargs, a) } m.ctrl.Call(m, "Info", varargs...) } // Info indicates an expected call of Info. func (mr *MockLoggerMockRecorder) Info(msg any, tags ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{msg}, tags...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), varargs...) } // SampleInfo mocks base method. func (m *MockLogger) SampleInfo(msg string, sampleRate int, tags ...tag.Tag) { m.ctrl.T.Helper() varargs := []any{msg, sampleRate} for _, a := range tags { varargs = append(varargs, a) } m.ctrl.Call(m, "SampleInfo", varargs...) } // SampleInfo indicates an expected call of SampleInfo. func (mr *MockLoggerMockRecorder) SampleInfo(msg, sampleRate any, tags ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{msg, sampleRate}, tags...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SampleInfo", reflect.TypeOf((*MockLogger)(nil).SampleInfo), varargs...) } // Warn mocks base method. func (m *MockLogger) Warn(msg string, tags ...tag.Tag) { m.ctrl.T.Helper() varargs := []any{msg} for _, a := range tags { varargs = append(varargs, a) } m.ctrl.Call(m, "Warn", varargs...) } // Warn indicates an expected call of Warn. func (mr *MockLoggerMockRecorder) Warn(msg any, tags ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{msg}, tags...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), varargs...) } // WithTags mocks base method. func (m *MockLogger) WithTags(tags ...tag.Tag) Logger { m.ctrl.T.Helper() varargs := []any{} for _, a := range tags { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "WithTags", varargs...) ret0, _ := ret[0].(Logger) return ret0 } // WithTags indicates an expected call of WithTags. func (mr *MockLoggerMockRecorder) WithTags(tags ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithTags", reflect.TypeOf((*MockLogger)(nil).WithTags), tags...) } ================================================ FILE: common/log/logger_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package log import ( "encoding/json" "fmt" "runtime" "strconv" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zapcore" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" ) const anyNum = "[0-9]+" func TestLoggers(t *testing.T) { for _, tc := range []struct { name string loggerFactory func(zapLogger *zap.Logger) Logger }{ { name: "normal", loggerFactory: func(zapLogger *zap.Logger) Logger { return NewLogger(zapLogger) }, }, { name: "throttled", loggerFactory: func(zapLogger *zap.Logger) Logger { return NewThrottledLogger(NewLogger(zapLogger), dynamicproperties.GetIntPropertyFn(1)) }, }, // Unfortunately, replay logger is impossible to test because it requires a workflow context, which is not exposed by go client. } { t.Run(tc.name, func(t *testing.T) { for _, scenario := range []struct { name string fn func(logger Logger) expected string }{ { name: "debug", fn: func(logger Logger) { logger.Debug("test debug") }, expected: `{"level":"debug","msg":"test debug","logging-call-at":"logger_test.go:%s"}`, }, { name: "info", fn: func(logger Logger) { logger.Info("test info") }, expected: `{"level":"info","msg":"test info","logging-call-at":"logger_test.go:%s"}`, }, { name: "warn", fn: func(logger Logger) { logger.Warn("test warn") }, expected: `{"level":"warn","msg":"test warn","logging-call-at":"logger_test.go:%s"}`, }, { name: "error", fn: func(logger Logger) { logger.Error("test error") }, expected: `{"level":"error","msg":"test error","logging-call-at":"logger_test.go:%s"}`, }, { name: "debugf", fn: func(logger Logger) { logger.Debugf("test %v", "debug") }, expected: `{"level":"debug","msg":"test debug","logging-call-at":"logger_test.go:%s"}`, }, { name: "with tags", fn: func(logger Logger) { logger.WithTags(tag.Error(fmt.Errorf("test error"))).Info("test info", tag.WorkflowActionWorkflowStarted) }, expected: `{"level":"info","msg":"test info","error":"test error","wf-action":"add-workflow-started-event","logging-call-at":"logger_test.go:%s"}`, }, { name: "empty message", fn: func(logger Logger) { logger.Info("", tag.WorkflowActionWorkflowStarted) }, expected: `{"level":"info","msg":"` + defaultMsgForEmpty + `","wf-action":"add-workflow-started-event","logging-call-at":"logger_test.go:%s"}`, }, { name: "error with details", fn: func(logger Logger) { logger.Error("oh no", tag.Error(&testError{"workflow123"})) }, expected: `{"level":"error","msg":"oh no","error":"test error","error-details":{"workflow-id":"workflow123"},"logging-call-at":"logger_test.go:%s"}`, }, } { t.Run(scenario.name, func(t *testing.T) { buf := &strings.Builder{} zapLogger := zap.New(zapcore.NewCore(zapcore.NewJSONEncoder(zapcore.EncoderConfig{ MessageKey: "msg", LevelKey: "level", NameKey: "logger", EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, }), zapcore.AddSync(buf), zap.DebugLevel)) logger := tc.loggerFactory(zapLogger) scenario.fn(logger) out := strings.TrimRight(buf.String(), "\n") expected := fmt.Sprintf(scenario.expected, anyNum) assert.Regexp(t, expected, out) }) } }) } } func TestLoggerCallAt(t *testing.T) { buf := &strings.Builder{} zapLogger := zap.New(zapcore.NewCore(zapcore.NewJSONEncoder(zapcore.EncoderConfig{ MessageKey: "msg", LevelKey: "level", NameKey: "logger", EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, }), zapcore.AddSync(buf), zap.DebugLevel)) logger := NewLogger(zapLogger) preCaller := caller(1) logger.WithTags(tag.Error(fmt.Errorf("test error"))).Info("test info", tag.WorkflowActionWorkflowStarted) out := strings.TrimRight(buf.String(), "\n") sps := strings.Split(preCaller, ":") par, err := strconv.Atoi(sps[1]) assert.Nil(t, err) lineNum := fmt.Sprintf("%v", par+1) assert.Equal(t, `{"level":"info","msg":"test info","error":"test error","wf-action":"add-workflow-started-event","logging-call-at":"logger_test.go:`+lineNum+`"}`, out) } func TestLogger_Sampled(t *testing.T) { buf := &strings.Builder{} zapLogger := zap.New(zapcore.NewCore(zapcore.NewJSONEncoder(zapcore.EncoderConfig{ MessageKey: "msg", LevelKey: "level", NameKey: "logger", EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, }), zapcore.AddSync(buf), zap.DebugLevel)) logger := NewLogger(zapLogger, WithSampleFunc(func(i int) bool { return true })) // No Assertion here, since default logger uses random function. // But we check that it is logger.SampleInfo("test info", 1, tag.WorkflowActionWorkflowStarted) out := strings.TrimRight(buf.String(), "\n") assert.Regexp(t, `{"level":"info","msg":"test info","wf-action":"add-workflow-started-event","logging-call-at":"logger_test.go:`+anyNum+`"}`, out) } func TestDebugOn(t *testing.T) { buf := &strings.Builder{} l := zap.NewAtomicLevel() zapLogger := zap.New(zapcore.NewCore(zapcore.NewJSONEncoder(zapcore.EncoderConfig{ MessageKey: "msg", LevelKey: "level", NameKey: "logger", EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, }), zapcore.AddSync(buf), l)) logger := NewLogger(zapLogger) // Set level to debug and check if debugOn is true l.SetLevel(zap.DebugLevel) assert.True(t, logger.DebugOn()) // Set level to info and check if debugOn is false l.SetLevel(zap.InfoLevel) assert.False(t, logger.DebugOn()) // Set level to debug again and check if debugOn is true l.SetLevel(zap.DebugLevel) assert.True(t, logger.DebugOn()) } func TestHelper(t *testing.T) { // Test without Helper() first t.Run("WithoutHelper", func(t *testing.T) { buf := &strings.Builder{} zapLogger := createTestZapLogger(buf) logger := NewLogger(zapLogger) expectedLine := helperFuncWithoutHelper(logger) out := strings.TrimRight(buf.String(), "\n") var logEntry map[string]interface{} if err := json.Unmarshal([]byte(out), &logEntry); err != nil { t.Fatalf("Failed to parse log output as JSON: %v", err) } logCaller, ok := logEntry["caller"].(string) if !ok { t.Fatal("Log entry does not contain caller information") } require.Contains(t, logCaller, "logger_test.go") // Get the line from the caller string parts := strings.Split(logCaller, ":") if len(parts) != 2 { t.Fatalf("Unexpected caller format: %s", logCaller) } // Extract line number lineStr := parts[1] lineNum, err := strconv.Atoi(lineStr) if err != nil { t.Fatalf("Failed to parse line number from %s: %v", logCaller, err) } // With Helper(), the caller should be near the line where helper was called // Allow a small range of lines to avoid brittle tests if lineNum < expectedLine { t.Errorf("Caller line number (%d) is not near the calling line (%d)", lineNum, expectedLine) } }) // Test with Helper() t.Run("WithHelper", func(t *testing.T) { buf := &strings.Builder{} zapLogger := createTestZapLogger(buf) logger := NewLogger(zapLogger) // Line number where the helper is called from the test callerLine := line() // Get current line number helperFuncWithHelper(logger) out := strings.TrimRight(buf.String(), "\n") var logEntry map[string]interface{} if err := json.Unmarshal([]byte(out), &logEntry); err != nil { t.Fatalf("Failed to parse log output as JSON: %v", err) } logCaller, ok := logEntry["caller"].(string) if !ok { t.Fatal("Log entry does not contain caller information") } require.Contains(t, logCaller, "logger_test.go") // Get the line from the caller string parts := strings.Split(logCaller, ":") if len(parts) != 2 { t.Fatalf("Unexpected caller format: %s", logCaller) } // Extract line number lineStr := parts[1] lineNum, err := strconv.Atoi(lineStr) if err != nil { t.Fatalf("Failed to parse line number from %s: %v", logCaller, err) } // With Helper(), the caller should be near the line where helper was called // Allow a small range of lines to avoid brittle tests if lineNum < callerLine+1 { t.Errorf("Caller line number (%d) is not near the calling line (%d)", lineNum, callerLine) } }) } type testError struct { WorkflowID string } func (e testError) Error() string { return "test error" } func (e testError) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("workflow-id", e.WorkflowID) return nil } // Helper function to create the zap logger func createTestZapLogger(buf *strings.Builder) *zap.Logger { l := zap.NewAtomicLevel() return zap.New(zapcore.NewCore(zapcore.NewJSONEncoder(zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", NameKey: "logger", CallerKey: "caller", MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, }), zapcore.AddSync(buf), l), zap.AddStacktrace(zapcore.ErrorLevel), zap.AddCaller()) } func helperFuncWithoutHelper(l Logger) (lineNum int) { lineNum = line() + 1 l.Error("test info", tag.WorkflowActionWorkflowStarted) return lineNum } func helperFuncWithHelper(l Logger) { l.Helper().Error("test info", tag.WorkflowActionWorkflowStarted) } // Helper function to get current line number func line() int { _, _, line, _ := runtime.Caller(1) return line } ================================================ FILE: common/log/options.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package log // Option is used to set options for the logger. type Option func(impl *loggerImpl) // WithSampleFunc sets the sampling function for the logger. func WithSampleFunc(fn func(int) bool) Option { return func(impl *loggerImpl) { impl.sampleLocalFn = fn } } ================================================ FILE: common/log/panic.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package log import ( "fmt" "runtime/debug" "github.com/uber/cadence/common/log/tag" ) // CapturePanic is used to capture panic, it will log the panic and also return the error through pointer. // If the panic value is not error then a default error is returned // We have to use pointer is because in golang: "recover return nil if was not called directly by a deferred function." // And we have to set the returned error otherwise our handler will return nil as error which is incorrect // errPanic MUST be the result from calling recover, which MUST be done in a single level deep // deferred function. The usual way of calling this is: // - defer func() { log.CapturePanic(recover(), logger, &err) }() func CapturePanic(errPanic interface{}, logger Logger, retError *error) { if errPanic != nil { err, ok := errPanic.(error) if !ok { err = fmt.Errorf("panic object is not error: %#v", errPanic) } st := string(debug.Stack()) // This function is called in deferred block and is all over the place. // We want the log to point to the line of panic, not this line, or stack of the defer function. logger.Helper().Helper().Error("Panic is captured", tag.SysStackTrace(st), tag.Error(err)) if retError != nil { *retError = err } } } ================================================ FILE: common/log/replay.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package log import ( "fmt" "math/rand" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/log/tag" ) type replayLogger struct { logger Logger ctx workflow.Context enableLogInReplay bool } const skipForReplayLogger = skipForDefaultLogger + 1 // NewReplayLogger creates a logger which is aware of cadence's replay mode func NewReplayLogger(logger Logger, ctx workflow.Context, enableLogInReplay bool) Logger { lg, ok := logger.(*loggerImpl) if ok { logger = &loggerImpl{ zapLogger: lg.zapLogger, skip: skipForReplayLogger, sampleLocalFn: lg.sampleLocalFn, } } else { logger.Warn("ReplayLogger may not emit callat tag correctly because the logger passed in is not loggerImpl") } return &replayLogger{ logger: logger, ctx: ctx, enableLogInReplay: enableLogInReplay, } } func (r *replayLogger) Debugf(msg string, args ...any) { if workflow.IsReplaying(r.ctx) && !r.enableLogInReplay { return } r.logger.Debugf(fmt.Sprintf(msg, args...)) } func (r *replayLogger) Debug(msg string, tags ...tag.Tag) { if workflow.IsReplaying(r.ctx) && !r.enableLogInReplay { return } r.logger.Debug(msg, tags...) } func (r *replayLogger) Info(msg string, tags ...tag.Tag) { if workflow.IsReplaying(r.ctx) && !r.enableLogInReplay { return } r.logger.Info(msg, tags...) } func (r *replayLogger) Warn(msg string, tags ...tag.Tag) { if workflow.IsReplaying(r.ctx) && !r.enableLogInReplay { return } r.logger.Warn(msg, tags...) } func (r *replayLogger) Error(msg string, tags ...tag.Tag) { if workflow.IsReplaying(r.ctx) && !r.enableLogInReplay { return } r.logger.Error(msg, tags...) } func (r *replayLogger) Fatal(msg string, tags ...tag.Tag) { if workflow.IsReplaying(r.ctx) && !r.enableLogInReplay { return } r.logger.Fatal(msg, tags...) } func (r *replayLogger) SampleInfo(msg string, sampleRate int, tags ...tag.Tag) { if rand.Intn(sampleRate) == 0 { if workflow.IsReplaying(r.ctx) && !r.enableLogInReplay { return } r.logger.Info(msg, tags...) } } func (r *replayLogger) DebugOn() bool { return r.logger.DebugOn() } func (r *replayLogger) WithTags(tags ...tag.Tag) Logger { return &replayLogger{ logger: r.logger.WithTags(tags...), ctx: r.ctx, enableLogInReplay: r.enableLogInReplay, } } func (r *replayLogger) Helper() Logger { return &replayLogger{ logger: r.logger.Helper(), ctx: r.ctx, enableLogInReplay: r.enableLogInReplay, } } ================================================ FILE: common/log/tag/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tag import ( "fmt" "time" "go.uber.org/zap" ) // Tag is the interface for logging system type Tag struct { // keep this field private field zap.Field } // Field returns a zap field func (t *Tag) Field() zap.Field { return t.field } func newStringTag(key string, value string) Tag { return Tag{ field: zap.String(key, value), } } func newStringsTag(key string, value []string) Tag { return Tag{ field: zap.Strings(key, value), } } func newInt64(key string, value int64) Tag { return Tag{ field: zap.Int64(key, value), } } func newInt(key string, value int) Tag { return Tag{ field: zap.Int(key, value), } } func newInt32(key string, value int32) Tag { return Tag{ field: zap.Int32(key, value), } } func newBoolTag(key string, value bool) Tag { return Tag{ field: zap.Bool(key, value), } } func newErrorTag(key string, value error) Tag { // NOTE zap already chosen "error" as key return Tag{ field: zap.Error(value), } } func newDurationTag(key string, value time.Duration) Tag { return Tag{ field: zap.Duration(key, value), } } func newTimeTag(key string, value time.Time) Tag { return Tag{ field: zap.Time(key, value), } } func newObjectTag(key string, value interface{}) Tag { return Tag{ field: zap.String(key, fmt.Sprintf("%v", value)), } } func newPredefinedStringTag(key string, value string) Tag { return Tag{ field: zap.String(key, value), } } func newPredefinedDynamicTag(key string, value interface{}) Tag { return Tag{ field: zap.Any(key, value), } } func newFloat64Tag(key string, value float64) Tag { return Tag{ field: zap.Float64(key, value), } } ================================================ FILE: common/log/tag/tags.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tag import ( "fmt" "time" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/types" ) // All logging tags are defined in this file. // To help finding available tags, we recommend that all tags to be categorized and placed in the corresponding section. // We currently have those categories: // 0. Common tags that can't be categorized(or belong to more than one) // 1. Workflow: these tags are information that are useful to our customer, like workflow-id/run-id/task-list/... // 2. System : these tags are internal information which usually cannot be understood by our customers, // ///////////////// Common tags defined here /////////////////// // Error returns tag for Error func Error(err error) Tag { return newErrorTag("error", err) } // ClusterName returns tag for ClusterName func ClusterName(clusterName string) Tag { return newStringTag("cluster-name", clusterName) } // Region returns tag for Region func Region(region string) Tag { return newStringTag("region", region) } // ActiveClusterName returns tag for ActiveClusterName. func ActiveClusterName(activeClusterName string) Tag { return newStringTag("active-cluster-name", activeClusterName) } // ActiveClusterChangeType returns tag for ActiveClusterChangeType. func ActiveClusterChangeType(changeType string) Tag { return newStringTag("active-active-change-type", changeType) } // Timestamp returns tag for Timestamp func Timestamp(timestamp time.Time) Tag { return newTimeTag("timestamp", timestamp) } func EarliestTime(time int64) Tag { return newInt64("earliest-time", time) } func LatestTime(time int64) Tag { return newInt64("latest-time", time) } // ///////////////// Workflow tags defined here: ( wf is short for workflow) /////////////////// // WorkflowAction returns tag for WorkflowAction func workflowAction(action string) Tag { return newPredefinedStringTag("wf-action", action) } // WorkflowListFilterType returns tag for WorkflowListFilterType func workflowListFilterType(listFilterType string) Tag { return newPredefinedStringTag("wf-list-filter-type", listFilterType) } // general // ClientImpl returns tag for ClientImpl func ClientImpl(clientImpl string) Tag { return newStringTag("client-impl", clientImpl) } // ClientFeatureVersion returns tag for ClientFeatureVersion func ClientFeatureVersion(clientFeatureVersion string) Tag { return newStringTag("client-feature-version", clientFeatureVersion) } // WorkflowError returns tag for WorkflowError func WorkflowError(error error) Tag { return newErrorTag("wf-error", error) } // WorkflowTimeoutType returns tag for WorkflowTimeoutType func WorkflowTimeoutType(timeoutType int64) Tag { return newInt64("wf-timeout-type", timeoutType) } // ActivityTimeoutType returns tag for ActivityTimeoutType func ActivityTimeoutType(timerType shared.TimeoutType) Tag { return newStringTag("activity-timer-type", timerType.String()) } // WorkflowPollContextTimeout returns tag for WorkflowPollContextTimeout func WorkflowPollContextTimeout(pollContextTimeout time.Duration) Tag { return newDurationTag("wf-poll-context-timeout", pollContextTimeout) } // WorkflowHandlerName returns tag for WorkflowHandlerName func WorkflowHandlerName(handlerName string) Tag { return newStringTag("wf-handler-name", handlerName) } // WorkflowID returns tag for WorkflowID func WorkflowID(workflowID string) Tag { return newStringTag("wf-id", workflowID) } // TargetWorkflowID returns tag for target WorkflowID func TargetWorkflowID(workflowID string) Tag { return newStringTag("target-wf-id", workflowID) } // WorkflowType returns tag for WorkflowType func WorkflowType(wfType string) Tag { return newStringTag("wf-type", wfType) } // WorkflowSignalName returns tag for WorkflowSignalName func WorkflowSignalName(signalName string) Tag { return newStringTag("wf-signal-name", signalName) } // WorkflowRequestID returns tag for WorkflowRequestID func WorkflowRequestID(requestID string) Tag { return newStringTag("wf-request-id", requestID) } // WorkflowState returns tag for WorkflowState func WorkflowState(s int) Tag { return newInt("wf-state", s) } // WorkflowRunID returns tag for WorkflowRunID func WorkflowRunID(runID string) Tag { return newStringTag("wf-run-id", runID) } // TargetWorkflowRunID returns tag for target WorkflowRunID func TargetWorkflowRunID(runID string) Tag { return newStringTag("target-wf-run-id", runID) } // WorkflowResetBaseRunID returns tag for WorkflowResetBaseRunID func WorkflowResetBaseRunID(runID string) Tag { return newStringTag("wf-reset-base-run-id", runID) } // WorkflowResetNewRunID returns tag for WorkflowResetNewRunID func WorkflowResetNewRunID(runID string) Tag { return newStringTag("wf-reset-new-run-id", runID) } // WorkflowBinaryChecksum returns tag for WorkflowBinaryChecksum func WorkflowBinaryChecksum(cs string) Tag { return newStringTag("wf-binary-checksum", cs) } // WorkflowActivityID returns tag for WorkflowActivityID func WorkflowActivityID(id string) Tag { return newStringTag("wf-activity-id", id) } // WorkflowTimerID returns tag for WorkflowTimerID func WorkflowTimerID(id string) Tag { return newStringTag("wf-timer-id", id) } // WorkflowBeginningRunID returns tag for WorkflowBeginningRunID func WorkflowBeginningRunID(beginningRunID string) Tag { return newStringTag("wf-beginning-run-id", beginningRunID) } // WorkflowEndingRunID returns tag for WorkflowEndingRunID func WorkflowEndingRunID(endingRunID string) Tag { return newStringTag("wf-ending-run-id", endingRunID) } // WorkflowDecisionTimeoutSeconds returns tag for WorkflowDecisionTimeoutSeconds func WorkflowDecisionTimeoutSeconds(s int32) Tag { return newInt32("wf-decision-timeout", s) } // QueryID returns tag for QueryID func QueryID(queryID string) Tag { return newStringTag("query-id", queryID) } // BlobSizeViolationOperation returns tag for BlobSizeViolationOperation func BlobSizeViolationOperation(operation string) Tag { return newStringTag("blob-size-violation-operation", operation) } // WorkflowCronSchedule returns a tag to report a workflow's cron schedule func WorkflowCronSchedule(schedule string) Tag { return newStringTag("wf-cron-schedule", schedule) } // WorkflowCloseStatus returns a tag to report a workflow's close status func WorkflowCloseStatus(status int) Tag { return newInt("close-status", status) } // IsWorkflowOpen returns a tag to report a workflow is open or not func IsWorkflowOpen(isOpen bool) Tag { return newBoolTag("is-workflow-open", isOpen) } // WorkflowTerminationReason returns a tag to report a workflow's termination reason func WorkflowTerminationReason(reason string) Tag { return newStringTag("wf-termination-reason", reason) } func Duration(duration time.Duration) Tag { return newDurationTag("duration", duration) } // domain related // WorkflowDomainID returns tag for WorkflowDomainID func WorkflowDomainID(domainID string) Tag { return newStringTag("wf-domain-id", domainID) } // TargetWorkflowDomainID returns tag for target WorkflowDomainID func TargetWorkflowDomainID(domainID string) Tag { return newStringTag("target-wf-domain-id", domainID) } // WorkflowDomainName returns tag for WorkflowDomainName func WorkflowDomainName(domainName string) Tag { return newStringTag("wf-domain-name", domainName) } // WorkflowDomainIDs returns tag for WorkflowDomainIDs func WorkflowDomainIDs(domainIDs interface{}) Tag { return newObjectTag("wf-domain-ids", domainIDs) } // OperationName returns tag for OperationName func OperationName(operationName string) Tag { return newStringTag("operation-name", operationName) } // history event ID related // WorkflowEventID returns tag for WorkflowEventID func WorkflowEventID(eventID int64) Tag { return newInt64("wf-history-event-id", eventID) } // WorkflowScheduleID returns tag for WorkflowScheduleID func WorkflowScheduleID(scheduleID int64) Tag { return newInt64("wf-schedule-id", scheduleID) } // WorkflowStartedID returns tag for WorkflowStartedID func WorkflowStartedID(id int64) Tag { return newInt64("wf-started-id", id) } // WorkflowInitiatedID returns tag for WorkflowInitiatedID func WorkflowInitiatedID(id int64) Tag { return newInt64("wf-initiated-id", id) } // WorkflowFirstEventID returns tag for WorkflowFirstEventID func WorkflowFirstEventID(firstEventID int64) Tag { return newInt64("wf-first-event-id", firstEventID) } // WorkflowNextEventID returns tag for WorkflowNextEventID func WorkflowNextEventID(nextEventID int64) Tag { return newInt64("wf-next-event-id", nextEventID) } // WorkflowResetNextEventID returns tag for WorkflowResetNextEventID func WorkflowResetNextEventID(resetNextEventID int64) Tag { return newInt64("wf-reset-next-event-id", resetNextEventID) } func WorkflowExternalEntityType(externalEntityType string) Tag { return newStringTag("wf-external-entity-type", externalEntityType) } func WorkflowExternalEntityKey(externalEntityKey string) Tag { return newStringTag("wf-external-entity-key", externalEntityKey) } // history tree // WorkflowTreeID returns tag for WorkflowTreeID func WorkflowTreeID(treeID string) Tag { return newStringTag("wf-tree-id", treeID) } // WorkflowBranchID returns tag for WorkflowBranchID func WorkflowBranchID(branchID string) Tag { return newStringTag("wf-branch-id", branchID) } // workflow task // WorkflowDecisionType returns tag for WorkflowDecisionType func WorkflowDecisionType(decisionType int64) Tag { return newInt64("wf-decision-type", decisionType) } // WorkflowQueryType returns tag for WorkflowQueryType func WorkflowQueryType(qt string) Tag { return newStringTag("wf-query-type", qt) } // WorkflowDecisionFailCause returns tag for WorkflowDecisionFailCause func WorkflowDecisionFailCause(decisionFailCause int64) Tag { return newInt64("wf-decision-fail-cause", decisionFailCause) } // WorkflowTaskListType returns tag for WorkflowTaskListType func WorkflowTaskListType(taskListType int) Tag { return newInt("wf-task-list-type", taskListType) } // WorkflowTaskListKind returns tag for WorkflowTaskListKind func WorkflowTaskListKind(taskListKind int32) Tag { return newInt32("wf-task-list-kind", taskListKind) } // WorkflowTaskListName returns tag for WorkflowTaskListName func WorkflowTaskListName(taskListName string) Tag { return newStringTag("wf-task-list-name", taskListName) } // size limit // WorkflowSize returns tag for WorkflowSize func WorkflowSize(workflowSize int64) Tag { return newInt64("wf-size", workflowSize) } // WorkflowSignalCount returns tag for SignalCount func WorkflowSignalCount(signalCount int32) Tag { return newInt32("wf-signal-count", signalCount) } // WorkflowHistorySize returns tag for HistorySize func WorkflowHistorySize(historySize int) Tag { return newInt("wf-history-size", historySize) } // WorkflowHistorySizeBytes returns tag for HistorySizeBytes func WorkflowHistorySizeBytes(historySizeBytes int) Tag { return newInt("wf-history-size-bytes", historySizeBytes) } // WorkflowEventCount returns tag for EventCount func WorkflowEventCount(eventCount int) Tag { return newInt("wf-event-count", eventCount) } func WorkflowEventType(eventType string) Tag { return newStringTag("wf-event-type", eventType) } // ///////////////// System tags defined here: /////////////////// // Tags with pre-define values // component returns tag for component func component(component string) Tag { return newPredefinedStringTag("component", component) } // lifecycle returns tag for lifecycle func lifecycle(lifecycle string) Tag { return newPredefinedStringTag("lifecycle", lifecycle) } // storeOperation returns tag for storeOperation func storeOperation(storeOperation string) Tag { return newPredefinedStringTag("store-operation", storeOperation) } // clientOperation returns tag for clientOperation func clientOperation(clientOperation string) Tag { return newPredefinedStringTag("client-operation", clientOperation) } // operationResult returns tag for operationResult func operationResult(operationResult string) Tag { return newPredefinedStringTag("operation-result", operationResult) } // errorType returns tag for errorType func errorType(errorType string) Tag { return newPredefinedStringTag("error-type", errorType) } // shardupdate returns tag for shardupdate func shardupdate(shardupdate string) Tag { return newPredefinedStringTag("shard-update", shardupdate) } // idType returns tag for idType func idType(idType string) Tag { return newPredefinedStringTag("id-type", idType) } // queueType returns tag for queueType func queueType(queueType string) Tag { return newPredefinedStringTag("queue-type", queueType) } // general // Service returns tag for Service func Service(sv string) Tag { return newStringTag("service", sv) } // DestService returns tag for destination service func DestService(sv string) Tag { return newStringTag("dest-service", sv) } // Addresses returns tag for Addresses func Addresses(ads []string) Tag { return newObjectTag("addresses", ads) } // Subscriber returns tag for Subscriber func Subscriber(subscriber string) Tag { return newStringTag("subscriber", subscriber) } // Address return tag for Address func Address(ad string) Tag { return newStringTag("address", ad) } // Env return tag for runtime environment func Env(env string) Tag { return newStringTag("env", env) } // Key returns tag for Key func Key(k string) Tag { return newStringTag("key", k) } // Name returns tag for Name func Name(k string) Tag { return newStringTag("name", k) } // Mode returns tag for Mode func Mode(mode string) Tag { return newStringTag("mode", mode) } // Value returns tag for Value func Value(v interface{}) Tag { return newObjectTag("value", v) } // Reason returns tag for Reason func Reason(reason string) Tag { return newStringTag("reason", reason) } // ValueType returns tag for ValueType func ValueType(v interface{}) Tag { return newStringTag("value-type", fmt.Sprintf("%T", v)) } // DefaultValue returns tag for DefaultValue func DefaultValue(v interface{}) Tag { return newObjectTag("default-value", v) } // Port returns tag for Port func Port(p int) Tag { return newInt("port", p) } // CursorTimestamp returns tag for CursorTimestamp func CursorTimestamp(timestamp time.Time) Tag { return newTimeTag("cursor-timestamp", timestamp) } // MetricScope returns tag for MetricScope func MetricScope(metricScope int) Tag { return newInt("metric-scope", metricScope) } // StoreType returns tag for StoreType func StoreType(storeType string) Tag { return newPredefinedStringTag("store-type", storeType) } // StoreError returns tag for StoreError func StoreError(storeErr error) Tag { return newErrorTag("store-error", storeErr) } // StoreShard returns tag for StoreShard func StoreShard(storeShard string) Tag { return newPredefinedStringTag("store-shard", storeShard) } // ClientError returns tag for ClientError func ClientError(clientErr error) Tag { return newErrorTag("client-error", clientErr) } // DetailInfo returns tag for DetailInfo func DetailInfo(i string) Tag { return newStringTag("detail-info", i) } // Counter returns tag for Counter func Counter(c int) Tag { return newInt("counter", c) } // Number returns tag for Number func Number(n int64) Tag { return newInt64("number", n) } // NextNumber returns tag for NextNumber func NextNumber(n int64) Tag { return newInt64("next-number", n) } // Bool returns tag for Bool func Bool(b bool) Tag { return newBoolTag("bool", b) } /* Tags for logging manual access */ // RequestCaller returns tag for caller (the name of the service making this request) func RequestCaller(callerName string) Tag { return newStringTag("request-caller", callerName) } // ActorType returns type of the actor (service or user) making this request func ActorType(actorType string) Tag { return newStringTag("actor-type", actorType) } // ActorID returns tag for the actor ID func ActorID(actorID string) Tag { return newStringTag("actor-id", actorID) } // ActorEmail returns tag for the actor's email address func ActorEmail(actorEmail string) Tag { return newStringTag("actor-email", actorEmail) } func IsShadowModeEnabled(isShadow bool) Tag { return newBoolTag("is-shadow-mode-enabled", isShadow) } // HandlerCall returns tag for the API name of a request func HandlerCall(handlerCall string) Tag { return newStringTag("handler-call", handlerCall) } // RequestBody returns the tag for the API request body func RequestBody(requestBody string) Tag { return newStringTag("request-body", requestBody) } // RequestType return tag for the type of request (internal, external) func RequestType(requestType string) Tag { return newStringTag("request-type", requestType) } // history engine shard // ShardID returns tag for ShardID func ShardID(shardID int) Tag { return newInt("shard-id", shardID) } // ShardTime returns tag for ShardTime func ShardTime(shardTime interface{}) Tag { return newObjectTag("shard-time", shardTime) } // ShardReplicationAck returns tag for ShardReplicationAck func ShardReplicationAck(shardReplicationAck int64) Tag { return newInt64("shard-replication-ack", shardReplicationAck) } // ShardReplicationToken returns information about a particular replication request func ShardReplicationToken(token interface{}) Tag { return newObjectTag("shard-replication-token", token) } // PreviousShardRangeID returns tag for PreviousShardRangeID func PreviousShardRangeID(id int64) Tag { return newInt64("previous-shard-range-id", id) } // ShardRangeID returns tag for ShardRangeID func ShardRangeID(id int64) Tag { return newInt64("shard-range-id", id) } // ReadLevel returns tag for ReadLevel func ReadLevel(lv int64) Tag { return newInt64("read-level", lv) } // MinLevel returns tag for MinLevel func MinLevel(lv int64) Tag { return newInt64("min-level", lv) } // MaxLevel returns tag for MaxLevel func MaxLevel(lv int64) Tag { return newInt64("max-level", lv) } // ShardTransferAcks returns tag for ShardTransferAcks func ShardTransferAcks(shardTransferAcks interface{}) Tag { return newObjectTag("shard-transfer-acks", shardTransferAcks) } // ShardTimerAcks returns tag for ShardTimerAcks func ShardTimerAcks(shardTimerAcks interface{}) Tag { return newObjectTag("shard-timer-acks", shardTimerAcks) } // ShardCrossClusterAcks returns tag for ShardCrossClusterAcks func ShardCrossClusterAcks(shardCrossClusterAcks interface{}) Tag { return newObjectTag("shard-cross-cluster-acks", shardCrossClusterAcks) } // task queue processor // QueueLevel returns tag for QueueLevel func QueueLevel(level int) Tag { return newInt("queue-level", level) } // PreviousQueueLevel returns tag for PreviousQueueLevel func PreviousQueueLevel(level int) Tag { return newInt("previous-queue-level", level) } // QueueSplitPolicyType returns tag for QueueSplitPolicyType func QueueSplitPolicyType(policyType int) Tag { return newInt("split-policy-type", policyType) } // TaskID returns tag for TaskID func TaskID(taskID int64) Tag { return newInt64("queue-task-id", taskID) } // TaskType returns tag for TaskType for queue processor func TaskType(taskType int) Tag { return newInt("queue-task-type", taskType) } // TaskVisibilityTimestamp returns tag for task visibilityTimestamp func TaskVisibilityTimestamp(timestamp int64) Tag { return newInt64("queue-task-visibility-timestamp", timestamp) } // NumberProcessed returns tag for NumberProcessed func NumberProcessed(n int) Tag { return newInt("number-processed", n) } // NumberDeleted returns tag for NumberDeleted func NumberDeleted(n int) Tag { return newInt("number-deleted", n) } // TimerTaskStatus returns tag for TimerTaskStatus func TimerTaskStatus(timerTaskStatus int32) Tag { return newInt32("timer-task-status", timerTaskStatus) } // retry // Attempt returns tag for Attempt func Attempt(attempt int32) Tag { return newInt32("attempt", attempt) } // AttemptCount returns tag for AttemptCount func AttemptCount(attemptCount int) Tag { return newInt("attempt-count", attemptCount) } // AttemptStart returns tag for AttemptStart func AttemptStart(attemptStart time.Time) Tag { return newTimeTag("attempt-start", attemptStart) } // AttemptEnd returns tag for AttemptEnd func AttemptEnd(attemptEnd time.Time) Tag { return newTimeTag("attempt-end", attemptEnd) } // ScheduleAttempt returns tag for ScheduleAttempt func ScheduleAttempt(scheduleAttempt int64) Tag { return newInt64("schedule-attempt", scheduleAttempt) } // ElasticSearch // ESRequest returns tag for ESRequest func ESRequest(ESRequest string) Tag { return newStringTag("es-request", ESRequest) } // ESResponseStatus returns tag for ESResponse status func ESResponseStatus(status int) Tag { return newInt("es-response-status", status) } // ESResponseError returns tag for ESResponse error func ESResponseError(msg string) Tag { return newStringTag("es-response-error", msg) } // ESKey returns tag for ESKey func ESKey(ESKey string) Tag { return newStringTag("es-mapping-key", ESKey) } // ESValue returns tag for ESValue func ESValue(ESValue []byte) Tag { // convert value to string type so that the value logged is human readable return newStringTag("es-mapping-value", string(ESValue)) } // ESConfig returns tag for ESConfig func ESConfig(c interface{}) Tag { return newObjectTag("es-config", c) } // ESField returns tag for ESField func ESField(ESField string) Tag { return newStringTag("es-field", ESField) } // ESDocID returns tag for ESDocID func ESDocID(id string) Tag { return newStringTag("es-doc-id", id) } // ESAggregationID returns tag for ESDocID func ESAggregationID(id string) Tag { return newStringTag("es-agg-id", id) } // LoggingCallAtKey is reserved tag const LoggingCallAtKey = "logging-call-at" // SysStackTrace returns tag for SysStackTrace func SysStackTrace(stackTrace string) Tag { return newStringTag("sys-stack-trace", stackTrace) } // Kafka related // KafkaTopicName returns tag for TopicName func KafkaTopicName(topicName string) Tag { return newStringTag("kafka-topic-name", topicName) } // KafkaConsumerName returns tag for ConsumerName func KafkaConsumerName(consumerName string) Tag { return newStringTag("kafka-consumer-name", consumerName) } // KafkaPartition returns tag for Partition func KafkaPartition(partition int32) Tag { return newInt32("kafka-partition", partition) } // KafkaPartitionKey returns tag for PartitionKey func KafkaPartitionKey(partitionKey interface{}) Tag { return newObjectTag("kafka-partition-key", partitionKey) } // KafkaOffset returns tag for Offset func KafkaOffset(offset int64) Tag { return newInt64("kafka-offset", offset) } // TokenLastEventID returns tag for TokenLastEventID func TokenLastEventID(id int64) Tag { return newInt64("token-last-event-id", id) } // ///////////////// XDC tags defined here: xdc- /////////////////// // SourceCluster returns tag for SourceCluster func SourceCluster(sourceCluster string) Tag { return newStringTag("xdc-source-cluster", sourceCluster) } func RemoteCluster(remoteCluster string) Tag { return newStringTag("xdc-remote-cluster", remoteCluster) } // PrevActiveCluster returns tag for PrevActiveCluster func PrevActiveCluster(prevActiveCluster string) Tag { return newStringTag("xdc-prev-active-cluster", prevActiveCluster) } // FailoverMsg returns tag for FailoverMsg func FailoverMsg(failoverMsg string) Tag { return newStringTag("xdc-failover-msg", failoverMsg) } // FailoverVersion returns tag for Version func FailoverVersion(version int64) Tag { return newInt64("xdc-failover-version", version) } // CurrentVersion returns tag for CurrentVersion func CurrentVersion(currentVersion int64) Tag { return newInt64("xdc-current-version", currentVersion) } // IncomingVersion returns tag for IncomingVersion func IncomingVersion(incomingVersion int64) Tag { return newInt64("xdc-incoming-version", incomingVersion) } // ReplicationInfo returns tag for ReplicationInfo func ReplicationInfo(replicationInfo interface{}) Tag { return newObjectTag("xdc-replication-info", replicationInfo) } // FirstEventVersion returns tag for FirstEventVersion func FirstEventVersion(version int64) Tag { return newInt64("xdc-first-event-version", version) } // LastEventVersion returns tag for LastEventVersion func LastEventVersion(version int64) Tag { return newInt64("xdc-last-event-version", version) } // TokenLastEventVersion returns tag for TokenLastEventVersion func TokenLastEventVersion(version int64) Tag { return newInt64("xdc-token-last-event-version", version) } // ResponseSize returns tag for ResponseSize func ResponseSize(size int) Tag { return newInt("response-size", size) } // ResponseTotalSize returns tag for ResponseTotalSize func ResponseTotalSize(size int) Tag { return newInt("response-total-size", size) } // ResponseMaxSize returns tag for ResponseMaxSize func ResponseMaxSize(size int) Tag { return newInt("response-max-size", size) } // ReplicationMessagesTotalSize returns tag for ReplicationMessagesTotalSize // Should be used to indicate the final size of types.ReplicationMessages func ReplicationMessagesTotalSize(size int) Tag { return newInt("replication-messages-total-size", size) } // ReplicationMessagesMaxSize returns tag for ReplicationMessagesMaxSize // Should be used to indicate maximum allowed size of types.ReplicationMessages func ReplicationMessagesMaxSize(size int) Tag { return newInt("replication-messages-max-size", size) } // ReplicationTaskID returns tag for ReplicationTaskID // Should be used to indicate id of a types.ReplicationTask func ReplicationTaskID(id int64) Tag { return newInt64("replication-task-id", id) } // ReplicationTaskCreationTime returns tag for ReplicationTaskCreationTime // Should be used to indicate CreationTime of a types.ReplicationTask func ReplicationTaskCreationTime(creationTime *int64) Tag { if creationTime == nil { return newStringTag("replication-task-creation-time", "nil") } return newInt64("replication-task-creation-time", *creationTime) } // ReplicationTaskBatchSize returns tag for task batch size // Should be used to indicate used batch size for replication task processing func ReplicationTaskBatchSize(batchSize int) Tag { return newInt("replication-task-batch-size", batchSize) } // ///////////////// Archival tags defined here: archival- /////////////////// // archival request tags // ArchivalCallerServiceName returns tag for the service name calling archival client func ArchivalCallerServiceName(callerServiceName string) Tag { return newStringTag("archival-caller-service-name", callerServiceName) } // ArchivalArchiveAttemptedInline returns tag for whether archival is attempted inline before signal is sent. func ArchivalArchiveAttemptedInline(archiveInline bool) Tag { return newBoolTag("archival-archive-attempted-inline", archiveInline) } // ArchivalRequestDomainID returns tag for RequestDomainID func ArchivalRequestDomainID(requestDomainID string) Tag { return newStringTag("archival-request-domain-id", requestDomainID) } // ArchivalRequestDomainName returns tag for RequestDomainName func ArchivalRequestDomainName(requestDomainName string) Tag { return newStringTag("archival-request-domain-name", requestDomainName) } // ArchivalRequestWorkflowID returns tag for RequestWorkflowID func ArchivalRequestWorkflowID(requestWorkflowID string) Tag { return newStringTag("archival-request-workflow-id", requestWorkflowID) } // ArchvialRequestWorkflowType returns tag for RequestWorkflowType func ArchvialRequestWorkflowType(requestWorkflowType string) Tag { return newStringTag("archival-request-workflow-type", requestWorkflowType) } // ArchivalRequestRunID returns tag for RequestRunID func ArchivalRequestRunID(requestRunID string) Tag { return newStringTag("archival-request-run-id", requestRunID) } // ArchivalRequestBranchToken returns tag for RequestBranchToken func ArchivalRequestBranchToken(requestBranchToken []byte) Tag { return newObjectTag("archival-request-branch-token", requestBranchToken) } // ArchivalRequestNextEventID returns tag for RequestNextEventID func ArchivalRequestNextEventID(requestNextEventID int64) Tag { return newInt64("archival-request-next-event-id", requestNextEventID) } // ArchivalRequestCloseFailoverVersion returns tag for RequestCloseFailoverVersion func ArchivalRequestCloseFailoverVersion(requestCloseFailoverVersion int64) Tag { return newInt64("archival-request-close-failover-version", requestCloseFailoverVersion) } // ArchivalRequestCloseTimestamp returns tag for RequestCloseTimestamp func ArchivalRequestCloseTimestamp(requestCloseTimeStamp int64) Tag { return newInt64("archival-request-close-timestamp", requestCloseTimeStamp) } // ArchivalRequestCloseStatus returns tag for RequestCloseStatus func ArchivalRequestCloseStatus(requestCloseStatus string) Tag { return newStringTag("archival-request-close-status", requestCloseStatus) } // ArchivalURI returns tag for Archival URI func ArchivalURI(URI string) Tag { return newStringTag("archival-URI", URI) } // ArchivalArchiveFailReason returns tag for ArchivalArchiveFailReason func ArchivalArchiveFailReason(archiveFailReason string) Tag { return newStringTag("archival-archive-fail-reason", archiveFailReason) } // ArchivalDeleteHistoryFailReason returns tag for ArchivalDeleteHistoryFailReason func ArchivalDeleteHistoryFailReason(deleteHistoryFailReason string) Tag { return newStringTag("archival-delete-history-fail-reason", deleteHistoryFailReason) } // ArchivalVisibilityQuery returns tag for the query for getting archived visibility record func ArchivalVisibilityQuery(query string) Tag { return newStringTag("archival-visibility-query", query) } // The following logger tags are only used by internal archiver implemention. // TODO: move them to internal repo once cadence plugin model is in place. // ArchivalBlobKey returns tag for BlobKey func ArchivalBlobKey(blobKey string) Tag { return newStringTag("archival-blob-key", blobKey) } // ArchivalDeterministicConstructionCheckFailReason returns tag for ArchivalDeterministicConstructionCheckFailReason func ArchivalDeterministicConstructionCheckFailReason(deterministicConstructionCheckFailReason string) Tag { return newStringTag("archival-deterministic-construction-check-fail-reason", deterministicConstructionCheckFailReason) } // ArchivalNonDeterministicBlobKey returns tag for randomly generated NonDeterministicBlobKey func ArchivalNonDeterministicBlobKey(nondeterministicBlobKey string) Tag { return newStringTag("archival-non-deterministic-blob-key", nondeterministicBlobKey) } // ArchivalBlobIntegrityCheckFailReason returns tag for ArchivalBlobIntegrityCheckFailReason func ArchivalBlobIntegrityCheckFailReason(blobIntegrityCheckFailReason string) Tag { return newStringTag("archival-blob-integrity-check-fail-reason", blobIntegrityCheckFailReason) } // ArchivalBlobstoreContextTimeout returns tag for ArchivalBlobstoreContextTimeout func ArchivalBlobstoreContextTimeout(blobstoreContextTimeout time.Duration) Tag { return newDurationTag("archival-blobstore-context-timeout", blobstoreContextTimeout) } // VisibilityQuery returns tag for the query for getting visibility records func VisibilityQuery(query string) Tag { return newStringTag("visibility-query", query) } // MembershipChangeEvent is a predefined tag for when logging hashring change events, // expected to be of type membership.ChangeEvent func MembershipChangeEvent(event interface{}) Tag { return newPredefinedDynamicTag("membership-change-event", event) } // Dynamic Uses reflection based logging for arbitrary values // for not very performant logging func Dynamic(key string, v interface{}) Tag { return newPredefinedDynamicTag(key, v) } func IsolationGroup(group string) Tag { return newStringTag("isolation-group", group) } func TaskLatency(duration time.Duration) Tag { return newDurationTag("task-latency", duration) } func IsolationDuration(duration time.Duration) Tag { return newDurationTag("isolation-duration", duration) } func PartitionConfig(p map[string]string) Tag { return newObjectTag("partition-config", p) } func PollerGroups(pollers []string) Tag { return newObjectTag("poller-isolation-groups", pollers) } func FallbackIsolationGroup(group string) Tag { return newStringTag("fallback-isolation-group", group) } func PollerGroupsConfiguration(pollers types.IsolationGroupConfiguration) Tag { return newObjectTag("poller-isolation-groups", pollers.ToPartitionList()) } func WorkflowIDCacheSize(size int) Tag { return newInt("workflow-id-cache-size", size) } func AsyncWFQueueID(queueID string) Tag { return newStringTag("async-wf-queue-id", queueID) } func AsyncWFRequestType(requestType string) Tag { return newStringTag("async-wf-request-type", requestType) } func GlobalRatelimiterKey(globalKey string) Tag { return newStringTag("global-ratelimit-key", globalKey) } func GlobalRatelimiterKeyMode(mode string) Tag { return newStringTag("global-ratelimit-key-mode", mode) } func GlobalRatelimiterIdleCount(count int) Tag { return newInt("global-ratelimit-key-idle-count", count) } func GlobalRatelimiterCollectionName(name string) Tag { return newStringTag("global-ratelimit-collection", name) } func GlobalRatelimiterPeer(peer string) Tag { return newStringTag("global-ratelimit-peer", peer) } func CurrentQPS(qps float64) Tag { return newFloat64Tag("current-qps", qps) } func NumReadPartitions(n int) Tag { return newInt("num-read-partitions", n) } func NumWritePartitions(n int) Tag { return newInt("num-write-partitions", n) } func ReadChanged(b bool) Tag { return newBoolTag("read-changed", b) } func WriteChanged(b bool) Tag { return newBoolTag("write-changed", b) } func IsolationChanged(b bool) Tag { return newBoolTag("isolation-changed", b) } func CurrentNumReadPartitions(n int) Tag { return newInt("current-num-read-partitions", n) } func CurrentNumWritePartitions(n int) Tag { return newInt("current-num-write-partitions", n) } func PartitionUpscaleThreshold(qps float64) Tag { return newFloat64Tag("partition-upscale-threshold", qps) } func PartitionDownscaleThreshold(qps float64) Tag { return newFloat64Tag("partition-downscale-threshold", qps) } func PartitionDownscaleFactor(qps float64) Tag { return newFloat64Tag("partition-downscale-factor", qps) } func MatchingTaskID(id int64) Tag { return newInt64("matching-task-id", id) } func MatchingTaskScheduleID(id int64) Tag { return newInt64("matching-task-schedule-id", id) } func DecisionTaskState(state int32) Tag { return newInt32("decision-task-state", state) } func ActivityTaskState(state int32) Tag { return newInt32("activity-task-state", state) } func ShardNamespace(name string) Tag { return newStringTag("shard-namespace", name) } func ShardExecutor(ID string) Tag { return newStringTag("shard-executor", ID) } func ShardExecutors(executorIDs []string) Tag { return newStringsTag("shard-executors", executorIDs) } func ShardKey(shardKey string) Tag { return newStringTag("shard-key", shardKey) } func ShardStatus(status string) Tag { return newStringTag("shard-status", status) } func ShardLoad(load string) Tag { return newStringTag("shard-load", load) } func ElectionDelay(t time.Duration) Tag { return newDurationTag("election-delay", t) } func WorkflowContextLockLatency(duration time.Duration) Tag { return newDurationTag("workflow-context-lock-latency", duration) } // DynamicConfigLinearIteratorSpec is a predefined tag to log dynamic config linear iterator spec func DynamicConfigLinearIteratorSpec(spec interface{}) Tag { return newObjectTag("dynamic-config-linear-iterator-spec", spec) } func HashRingResult(addr string) Tag { return newStringTag("hashring-result", addr) } func ShardDistributorResult(addr string) Tag { return newStringTag("shard-distributor-result", addr) } // PeerHostname returns a tag for peer hostname func PeerHostname(hostname string) Tag { return newStringTag("peer-hostname", hostname) } // PendingTaskCount returns a tag for pending task count func PendingTaskCount(count int) Tag { return newInt("pending-task-count", count) } // MaxTaskCount returns a tag for max task count func MaxTaskCount(count int) Tag { return newInt("max-task-count", count) } // VirtualQueueID returns a tag for virtual queue id func VirtualQueueID(id int64) Tag { return newInt64("virtual-queue-id", id) } func AlertType(alertType int) Tag { return newInt("alert-type", alertType) } // CacheID returns a tag for cache identifier func CacheID(cacheID string) Tag { return newStringTag("cache-id", cacheID) } func DomainAuditOperationType(operationType fmt.Stringer) Tag { return newStringTag("domain-audit-operation-type", operationType.String()) } ================================================ FILE: common/log/tag/values.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tag // Pre-defined values for TagWorkflowAction var ( // workflow start / finish WorkflowActionWorkflowStarted = workflowAction("add-workflow-started-event") WorkflowActionWorkflowCanceled = workflowAction("add-workflow-canceled-event") WorkflowActionWorkflowCompleted = workflowAction("add-workflow-completed--event") WorkflowActionWorkflowFailed = workflowAction("add-workflow-failed-event") WorkflowActionWorkflowTimeout = workflowAction("add-workflow-timeout-event") WorkflowActionWorkflowTerminated = workflowAction("add-workflow-terminated-event") WorkflowActionWorkflowContinueAsNew = workflowAction("add-workflow-continue-as-new-event") // workflow cancellation / sign WorkflowActionWorkflowCancelRequested = workflowAction("add-workflow-cancel-requested-event") WorkflowActionWorkflowSignaled = workflowAction("add-workflow-signaled-event") WorkflowActionWorkflowRecordMarker = workflowAction("add-workflow-marker-record-event") WorkflowActionUpsertWorkflowSearchAttributes = workflowAction("add-workflow-upsert-search-attributes-event") // decision WorkflowActionDecisionTaskScheduled = workflowAction("add-decisiontask-scheduled-event") WorkflowActionDecisionTaskStarted = workflowAction("add-decisiontask-started-event") WorkflowActionDecisionTaskCompleted = workflowAction("add-decisiontask-completed-event") WorkflowActionDecisionTaskTimedOut = workflowAction("add-decisiontask-timedout-event") WorkflowActionDecisionTaskFailed = workflowAction("add-decisiontask-failed-event") // in memory decision WorkflowActionInMemoryDecisionTaskScheduled = workflowAction("add-in-memory-decisiontask-scheduled") WorkflowActionInMemoryDecisionTaskStarted = workflowAction("add-in-memory-decisiontask-started") // activity WorkflowActionActivityTaskScheduled = workflowAction("add-activitytask-scheduled-event") WorkflowActionActivityTaskStarted = workflowAction("add-activitytask-started-event") WorkflowActionActivityTaskCompleted = workflowAction("add-activitytask-completed-event") WorkflowActionActivityTaskFailed = workflowAction("add-activitytask-failed-event") WorkflowActionActivityTaskTimedOut = workflowAction("add-activitytask-timed-event") WorkflowActionActivityTaskCanceled = workflowAction("add-activitytask-canceled-event") WorkflowActionActivityTaskCancelRequested = workflowAction("add-activitytask-cancel-requested-event") WorkflowActionActivityTaskCancelFailed = workflowAction("add-activitytask-cancel-failed-event") WorkflowActionActivityTaskRetry = workflowAction("add-activitytask-retry-event") // timer WorkflowActionTimerStarted = workflowAction("add-timer-started-event") WorkflowActionTimerFired = workflowAction("add-timer-fired-event") WorkflowActionTimerCanceled = workflowAction("add-timer-canceled-event") WorkflowActionTimerCancelFailed = workflowAction("add-timer-cancel-failed-event") // child workflow start / finish WorkflowActionChildWorkflowInitiated = workflowAction("add-childworkflow-initiated-event") WorkflowActionChildWorkflowStarted = workflowAction("add-childworkflow-started-event") WorkflowActionChildWorkflowInitiationFailed = workflowAction("add-childworkflow-initiation-failed-event") WorkflowActionChildWorkflowCanceled = workflowAction("add-childworkflow-canceled-event") WorkflowActionChildWorkflowCompleted = workflowAction("add-childworkflow-completed-event") WorkflowActionChildWorkflowFailed = workflowAction("add-childworkflow-failed-event") WorkflowActionChildWorkflowTerminated = workflowAction("add-childworkflow-terminated-event") WorkflowActionChildWorkflowTimedOut = workflowAction("add-childworkflow-timedout-event") // external workflow cancellation WorkflowActionExternalWorkflowCancelInitiated = workflowAction("add-externalworkflow-cancel-initiated-event") WorkflowActionExternalWorkflowCancelRequested = workflowAction("add-externalworkflow-cancel-requested-event") WorkflowActionExternalWorkflowCancelFailed = workflowAction("add-externalworkflow-cancel-failed-event") // external workflow signal WorkflowActionExternalWorkflowSignalInitiated = workflowAction("add-externalworkflow-signal-initiated-event") WorkflowActionExternalWorkflowSignalRequested = workflowAction("add-externalworkflow-signal-requested-event") WorkflowActionExternalWorkflowSignalFailed = workflowAction("add-externalworkflow-signal-failed-event") WorkflowActionUnknown = workflowAction("add-unknown-event") ) // Pre-defined values for TagWorkflowListFilterType var ( WorkflowListWorkflowFilterByID = workflowListFilterType("WID") WorkflowListWorkflowFilterByType = workflowListFilterType("WType") WorkflowListWorkflowFilterByStatus = workflowListFilterType("status") ) // Pre-defined values for TagSysComponent var ( ComponentTaskList = component("tasklist") ComponentHistoryEngine = component("history-engine") ComponentHistoryCache = component("history-cache") ComponentDecisionHandler = component("decision-handler") ComponentEventsCache = component("events-cache") ComponentTransferQueue = component("transfer-queue-processor") ComponentTransferQueueV2 = component("transfer-queue-processor-v2") ComponentTimerQueue = component("timer-queue-processor") ComponentTimerQueueV2 = component("timer-queue-processor-v2") ComponentTimerBuilder = component("timer-builder") ComponentReplicatorQueue = component("replicator-queue-processor") ComponentShardController = component("shard-controller") ComponentShard = component("shard") ComponentShardItem = component("shard-item") ComponentShardEngine = component("shard-engine") ComponentMatchingEngine = component("matching-engine") ComponentReplicator = component("replicator") ComponentReplicationTaskProcessor = component("replication-task-processor") ComponentReplicationAckManager = component("replication-ack-manager") ComponentReplicationCacheManager = component("replication-cache-manager") ComponentReplicationDynamicTaskBatchSizer = component("replication-dynamic-task-batch-sizer") ComponentHistoryReplicator = component("history-replicator") ComponentHistoryResender = component("history-resender") ComponentIndexer = component("indexer") ComponentIndexerProcessor = component("indexer-processor") ComponentIndexerESProcessor = component("indexer-es-processor") ComponentESVisibilityClient = component("visibility-client") // used for client-internal logs, e.g. request errors ComponentESVisibilityManager = component("es-visibility-manager") ComponentArchiver = component("archiver") ComponentBatcher = component("batcher") ComponentScheduler = component("scheduler") ComponentWorker = component("worker") ComponentServiceResolver = component("service-resolver") ComponentFailoverCoordinator = component("failover-coordinator") ComponentFailoverMarkerNotifier = component("failover-marker-notifier") ComponentCrossClusterQueueProcessor = component("cross-cluster-queue-processor") ComponentCrossClusterTaskProcessor = component("cross-cluster-task-processor") ComponentCrossClusterTaskFetcher = component("cross-cluster-task-fetcher") ComponentShardScanner = component("shardscanner-scanner") ComponentShardFixer = component("shardscanner-fixer") ComponentPinotVisibilityManager = component("pinot-visibility-manager") ComponentAsyncWFConsumptionManager = component("async-wf-consumption-manager") ComponentGlobalRatelimiter = component("global-ratelimiter") ComponentMapQ = component("mapq") ComponentMapQTree = component("mapq-tree") ComponentMapQTreeNode = component("mapq-tree-node") ComponentRPCFactory = component("rpc-factory") ComponentTaskListAdaptiveScaler = component("task-list-adaptive-scaler") ComponentActiveClusterManager = component("active-cluster-manager") ComponentMembershipResolver = component("membership-resolver") ComponentHashring = component("hashring") ComponentNamespaceManager = component("shard-namespace-manager") ComponentLeaderElection = component("shard-leader-election") ComponentLeaderProcessor = component("shard-leader-processor") ) // Predefined values for QueueTypes var ( QueueTypeActive = queueType("active") QueueTypeStandby = queueType("standby") ) // Pre-defined values for TagSysLifecycle var ( LifeCycleStarting = lifecycle("Starting") LifeCycleStarted = lifecycle("Started") LifeCycleStopping = lifecycle("Stopping") LifeCycleStopped = lifecycle("Stopped") LifeCycleStopTimedout = lifecycle("StopTimedout") LifeCycleStartFailed = lifecycle("StartFailed") LifeCycleStopFailed = lifecycle("StopFailed") LifeCycleProcessingFailed = lifecycle("ProcessingFailed") ) // Pre-defined values for SysErrorType var ( ErrorTypeInvalidHistoryAction = errorType("InvalidHistoryAction") ErrorTypeInvalidQueryTask = errorType("InvalidQueryTask") ErrorTypeQueryTaskFailed = errorType("QueryTaskFailed") ErrorTypePersistentStoreError = errorType("PersistentStoreError") ErrorTypeHistorySerializationError = errorType("HistorySerializationError") ErrorTypeHistoryDeserializationError = errorType("HistoryDeserializationError") ErrorTypeDuplicateTask = errorType("DuplicateTask") ErrorTypeMultipleCompletionDecisions = errorType("MultipleCompletionDecisions") ErrorTypeDuplicateTransferTask = errorType("DuplicateTransferTask") ErrorTypeDecisionFailed = errorType("DecisionFailed") ErrorTypeInvalidMutableStateAction = errorType("InvalidMutableStateAction") ErrorTypeInvalidMemDecisionTaskAction = errorType("InvalidMemDecisionTaskAction") ) // Pre-defined values for SysShardUpdate var ( // Shard context events ValueShardRangeUpdated = shardupdate("ShardRangeUpdated") ValueShardAllocateTimerBeforeRead = shardupdate("ShardAllocateTimerBeforeRead") ValueRingMembershipChangedEvent = shardupdate("RingMembershipChangedEvent") ) // Pre-defined values for OperationResult var ( OperationFailed = operationResult("OperationFailed") OperationStuck = operationResult("OperationStuck") OperationCritical = operationResult("OperationCritical") ) // Pre-defined values for TagSysStoreOperation var ( StoreOperationCreateShard = storeOperation("create-shard") StoreOperationGetShard = storeOperation("get-shard") StoreOperationUpdateShard = storeOperation("update-shard") StoreOperationCreateWorkflowExecution = storeOperation("create-wf-execution") StoreOperationGetWorkflowExecution = storeOperation("get-wf-execution") StoreOperationUpdateWorkflowExecution = storeOperation("update-wf-execution") StoreOperationConflictResolveWorkflowExecution = storeOperation("conflict-resolve-wf-execution") StoreOperationResetWorkflowExecution = storeOperation("reset-wf-execution") StoreOperationDeleteWorkflowExecution = storeOperation("delete-wf-execution") StoreOperationDeleteCurrentWorkflowExecution = storeOperation("delete-current-wf-execution") StoreOperationGetCurrentExecution = storeOperation("get-current-execution") StoreOperationListCurrentExecution = storeOperation("list-current-execution") StoreOperationIsWorkflowExecutionExists = storeOperation("is-wf-execution-exists") StoreOperationListConcreteExecution = storeOperation("list-concrete-execution") StoreOperationGetTransferTasks = storeOperation("get-transfer-tasks") StoreOperationGetCrossClusterTasks = storeOperation("get-cross-cluster-tasks") StoreOperationGetReplicationTasks = storeOperation("get-replication-tasks") StoreOperationCompleteTransferTask = storeOperation("complete-transfer-task") StoreOperationCompleteCrossClusterTask = storeOperation("complete-cross-cluster-task") StoreOperationCompleteReplicationTask = storeOperation("complete-replication-task") StoreOperationPutReplicationTaskToDLQ = storeOperation("put-replication-task-to-dlq") StoreOperationGetReplicationTasksFromDLQ = storeOperation("get-replication-tasks-from-dlq") StoreOperationGetReplicationDLQSize = storeOperation("get-replication-dlq-size") StoreOperationDeleteReplicationTaskFromDLQ = storeOperation("delete-replication-task-from-dlq") StoreOperationRangeDeleteReplicationTaskFromDLQ = storeOperation("range-delete-replication-task-from-dlq") StoreOperationCreateFailoverMarkerTasks = storeOperation("createFailoverMarkerTasks") StoreOperationGetTimerIndexTasks = storeOperation("get-timer-index-tasks") StoreOperationCompleteTimerTask = storeOperation("complete-timer-task") StoreOperationGetHistoryTasks = storeOperation("get-history-tasks") StoreOperationCompleteHistoryTask = storeOperation("complete-history-task") StoreOperationRangeCompleteHistoryTask = storeOperation("range-complete-history-task") StoreOperationGetActiveClusterSelectionPolicy = storeOperation("get-active-cluster-selection-policy") StoreOperationDeleteActiveClusterSelectionPolicy = storeOperation("delete-active-cluster-selection-policy") StoreOperationCreateTasks = storeOperation("create-tasks") StoreOperationGetTasks = storeOperation("get-tasks") StoreOperationGetOrphanTasks = storeOperation("get-orphan-tasks") StoreOperationCompleteTask = storeOperation("complete-task") StoreOperationCompleteTasksLessThan = storeOperation("complete-tasks-less-than") StoreOperationLeaseTaskList = storeOperation("lease-task-list") StoreOperationGetTaskList = storeOperation("get-task-list") StoreOperationUpdateTaskList = storeOperation("update-task-list") StoreOperationListTaskList = storeOperation("list-task-list") StoreOperationDeleteTaskList = storeOperation("delete-task-list") StoreOperationGetTaskListSize = storeOperation("get-task-list-size") StoreOperationStopTaskList = storeOperation("stop-task-list") StoreOperationCreateDomain = storeOperation("create-domain") StoreOperationGetDomain = storeOperation("get-domain") StoreOperationUpdateDomain = storeOperation("update-domain") StoreOperationDeleteDomain = storeOperation("delete-domain") StoreOperationDeleteDomainByName = storeOperation("delete-domain-by-name") StoreOperationListDomains = storeOperation("list-domains") StoreOperationGetMetadata = storeOperation("get-metadata") StoreOperationRecordWorkflowExecutionStarted = storeOperation("record-wf-execution-started") StoreOperationRecordWorkflowExecutionClosed = storeOperation("record-wf-execution-closed") StoreOperationUpsertWorkflowExecution = storeOperation("upsert-wf-execution") StoreOperationListOpenWorkflowExecutions = storeOperation("list-open-wf-executions") StoreOperationListClosedWorkflowExecutions = storeOperation("list-closed-wf-executions") StoreOperationListOpenWorkflowExecutionsByType = storeOperation("list-open-wf-executions-by-type") StoreOperationListClosedWorkflowExecutionsByType = storeOperation("list-closed-wf-executions-by-type") StoreOperationListOpenWorkflowExecutionsByWorkflowID = storeOperation("list-open-wf-executions-by-wfID") StoreOperationListClosedWorkflowExecutionsByWorkflowID = storeOperation("list-closed-wf-executions-by-wfID") StoreOperationListClosedWorkflowExecutionsByStatus = storeOperation("list-closed-wf-executions-by-status") StoreOperationGetClosedWorkflowExecution = storeOperation("get-closed-wf-execution") StoreOperationVisibilityDeleteWorkflowExecution = storeOperation("vis-delete-wf-execution") StoreOperationListWorkflowExecutions = storeOperation("list-wf-executions") StoreOperationScanWorkflowExecutions = storeOperation("scan-wf-executions") StoreOperationCountWorkflowExecutions = storeOperation("count-wf-executions") StoreOperationDeleteUninitializedWorkflowExecution = storeOperation("delete-uninitialized-wf-execution") StoreOperationRecordWorkflowExecutionUninitialized = storeOperation("record-wf-execution-uninitialized") StoreOperationAppendHistoryNodes = storeOperation("append-history-nodes") StoreOperationReadHistoryBranch = storeOperation("read-history-branch") StoreOperationReadHistoryBranchByBatch = storeOperation("read-history-branch-by-batch") StoreOperationReadRawHistoryBranch = storeOperation("read-raw-history-branch") StoreOperationForkHistoryBranch = storeOperation("fork-history-branch") StoreOperationDeleteHistoryBranch = storeOperation("delete-history-branch") StoreOperationGetHistoryTree = storeOperation("get-history-tree") StoreOperationGetAllHistoryTreeBranches = storeOperation("get-all-history-tree-branches") StoreOperationEnqueueMessage = storeOperation("enqueue-message") StoreOperationReadMessages = storeOperation("read-messages") StoreOperationUpdateAckLevel = storeOperation("update-ack-level") StoreOperationGetAckLevels = storeOperation("get-ack-levels") StoreOperationDeleteMessagesBefore = storeOperation("delete-messages-before") StoreOperationEnqueueMessageToDLQ = storeOperation("enqueue-message-to-dlq") StoreOperationReadMessagesFromDLQ = storeOperation("read-messages-from-dlq") StoreOperationRangeDeleteMessagesFromDLQ = storeOperation("range-delete-messages-from-dlq") StoreOperationUpdateDLQAckLevel = storeOperation("update-dlq-ack-level") StoreOperationGetDLQAckLevels = storeOperation("get-dlq-ack-levels") StoreOperationGetDLQSize = storeOperation("get-dlq-size") StoreOperationDeleteMessageFromDLQ = storeOperation("delete-message-from-dlq") StoreOperationFetchDynamicConfig = storeOperation("fetch-dynamic-config") StoreOperationUpdateDynamicConfig = storeOperation("update-dynamic-config") ) // Pre-defined values for TagSysClientOperation var ( AdminClientOperationAddSearchAttribute = clientOperation("admin-add-search-attribute") AdminClientOperationDescribeHistoryHost = clientOperation("admin-describe-history-host") AdminClientOperationDescribeShardDistribution = clientOperation("admin-shard-list") AdminClientOperationRemoveTask = clientOperation("admin-remove-task") AdminClientOperationCloseShard = clientOperation("admin-close-shard") AdminClientOperationResetQueue = clientOperation("admin-reset-queue") AdminClientOperationDescribeQueue = clientOperation("admin-describe-queue") AdminClientOperationDescribeWorkflowExecution = clientOperation("admin-describe-wf-execution") AdminClientOperationGetWorkflowExecutionRawHistoryV2 = clientOperation("admin-get-wf-execution-raw-history-v2") AdminClientOperationDescribeCluster = clientOperation("admin-describe-cluster") AdminClientOperationGetReplicationMessages = clientOperation("admin-get-replication-messsages") AdminClientOperationGetDomainReplicationMessages = clientOperation("admin-get-domain-replication-messsages") AdminClientOperationGetDLQReplicationMessages = clientOperation("admin-get-dlq-replication-messsages") AdminClientOperationReapplyEvents = clientOperation("admin-reapply-events") AdminClientOperationCountDLQMessages = clientOperation("admin-count-dlq-messsages") AdminClientOperationDeleteWorkflow = clientOperation("admin-delete-workflow") AdminClientOperationReadDLQMessages = clientOperation("admin-read-dlq-messsages") AdminClientOperationPurgeDLQMessages = clientOperation("admin-purge-dlq-messsages") AdminClientOperationMergeDLQMessages = clientOperation("admin-merge-dlq-messsages") AdminClientOperationRefreshWorkflowTasks = clientOperation("admin-refresh-wf-tasks") AdminClientOperationResendReplicationTasks = clientOperation("admin-resend-replication-tasks") AdminClientOperationGetCrossClusterTasks = clientOperation("admin-get-cross-cluster-tasks") AdminClientOperationRespondCrossClusterTasksCompleted = clientOperation("admin-respond-cross-cluster-tasks-completed") AdminClientOperationGetDynamicConfig = clientOperation("admin-get-dynamic-config") AdminClientOperationUpdateDynamicConfig = clientOperation("admin-update-dynamic-config") AdminClientOperationRestoreDynamicConfig = clientOperation("admin-restore-dynamic-config") AdminClientOperationListDynamicConfig = clientOperation("admin-list-dynamic-config") AdminClientOperationMaintainCorruptWorkflow = clientOperation("admin-maintain-corrupt-workflow") AdminClientOperationUpdateGlobalIsolationGroups = clientOperation("admin-update-global-isolation-groups") AdminClientOperationGetGlobalIsolationGroups = clientOperation("admin-get-global-isolation-groups") AdminClientOperationUpdateDomainIsolationGroups = clientOperation("admin-update-domain-isolation-groups") AdminClientOperationGetDomainIsolationGroups = clientOperation("admin-get-domain-isolation-groups") AdminClientOperationGetDomainAsyncWorkflowConfiguraton = clientOperation("admin-get-domain-async-workflow-configuration") AdminClientOperationUpdateDomainAsyncWorkflowConfiguraton = clientOperation("admin-update-domain-async-workflow-configuration") AdminDeleteWorkflow = clientOperation("admin-delete-workflow") MaintainCorruptWorkflow = clientOperation("maintain-corrupt-workflow") AdminClientOperationUpdateTaskListPartitionConfig = clientOperation("admin-update-task-list-partition-config") FrontendClientOperationDeleteDomain = clientOperation("frontend-delete-domain") FrontendClientOperationDeprecateDomain = clientOperation("frontend-deprecate-domain") FrontendClientOperationDescribeDomain = clientOperation("frontend-describe-domain") FrontendClientOperationDescribeTaskList = clientOperation("frontend-describe-task-list") FrontendClientOperationDescribeWorkflowExecution = clientOperation("frontend-describe-wf-execution") FrontendClientOperationDiagnoseWorkflowExecution = clientOperation("frontend-diagnose-wf-execution") FrontendClientOperationGetWorkflowExecutionHistory = clientOperation("frontend-get-wf-execution-history") FrontendClientOperationListArchivedWorkflowExecutions = clientOperation("frontend-list-archived-wf-executions") FrontendClientOperationListClosedWorkflowExecutions = clientOperation("frontend-list-closed-wf-executions") FrontendClientOperationListDomains = clientOperation("frontend-list-domains") FrontendClientOperationListOpenWorkflowExecutions = clientOperation("frontend-list-open-wf-executions") FrontendClientOperationListWorkflowExecutions = clientOperation("frontend-list-wf-executions") FrontendClientOperationScanWorkflowExecutions = clientOperation("frontend-scan-wf-executions") FrontendClientOperationCountWorkflowExecutions = clientOperation("frontend-count-wf-executions") FrontendClientOperationGetSearchAttributes = clientOperation("frontend-get-search-attributes") FrontendClientOperationPollForActivityTask = clientOperation("frontend-poll-for-activity-task") FrontendClientOperationPollForDecisionTask = clientOperation("frontend-poll-for-decision-task") FrontendClientOperationQueryWorkflow = clientOperation("frontend-query-workflow") FrontendClientOperationRecordActivityTaskHeartbeat = clientOperation("frontend-record-activity-heartbeat") FrontendClientOperationRecordActivityTaskHeartbeatByID = clientOperation("frontend-record-activity-heartbeat-by-id") FrontendClientOperationRegisterDomain = clientOperation("frontend-register-domain") FrontendClientOperationRequestCancelWorkflowExecution = clientOperation("frontend-request-cancel-wf-execution") FrontendClientOperationResetStickyTaskList = clientOperation("frontend-reset-sticky-task-list") FrontendClientOperationResetWorkflowExecution = clientOperation("frontend-reset-wf-execution") FrontendClientOperationRefreshWorkflowTasks = clientOperation("frontend-refresh-wf-tasks") FrontendClientOperationRespondActivityTaskCanceled = clientOperation("frontend-respond-activity-task-canceled") FrontendClientOperationRespondActivityTaskCanceledByID = clientOperation("frontend-respond-activity-task-canceled-by-id") FrontendClientOperationRespondActivityTaskCompleted = clientOperation("frontend-respond-activity-task-completed") FrontendClientOperationRespondActivityTaskCompletedByID = clientOperation("frontend-respond-activity-task-completed-by-id") FrontendClientOperationRespondActivityTaskFailed = clientOperation("frontend-respond-activity-task-failed") FrontendClientOperationRespondActivityTaskFailedByID = clientOperation("frontend-respond-activity-task-failed-by-id") FrontendClientOperationRespondDecisionTaskCompleted = clientOperation("frontend-respond-decision-task-completed") FrontendClientOperationRespondDecisionTaskFailed = clientOperation("frontend-respond-decision-task-failed") FrontendClientOperationRespondQueryTaskCompleted = clientOperation("frontend-respond-query-task-completed") FrontendClientOperationRestartWorkflowExecution = clientOperation("frontend-restart-wf-execution") FrontendClientOperationSignalWithStartWorkflowExecution = clientOperation("frontend-signal-with-start-wf-execution") FrontendClientOperationSignalWithStartWorkflowExecutionAsync = clientOperation("frontend-signal-with-start-wf-execution-async") FrontendClientOperationSignalWorkflowExecution = clientOperation("frontend-signal-wf-execution") FrontendClientOperationStartWorkflowExecution = clientOperation("frontend-start-wf-execution") FrontendClientOperationStartWorkflowExecutionAsync = clientOperation("frontend-start-wf-execution-async") FrontendClientOperationTerminateWorkflowExecution = clientOperation("frontend-terminate-wf-execution") FrontendClientOperationUpdateDomain = clientOperation("frontend-update-domain") FrontendClientOperationFailoverDomain = clientOperation("frontend-failover-domain") FrontendClientOperationListFailoverHistory = clientOperation("frontend-list-failover-history") FrontendClientOperationGetClusterInfo = clientOperation("frontend-get-cluster-info") FrontendClientOperationListTaskListPartitions = clientOperation("frontend-list-task-list-partitions") FrontendClientOperationGetTaskListsByDomain = clientOperation("frontend-get-task-list-for-domain") HistoryClientOperationStartWorkflowExecution = clientOperation("history-start-wf-execution") HistoryClientOperationDescribeHistoryHost = clientOperation("history-describe-history-host") HistoryClientOperationCloseShard = clientOperation("history-close-shard") HistoryClientOperationResetQueue = clientOperation("history-reset-queue") HistoryClientOperationDescribeQueue = clientOperation("history-describe-queue") HistoryClientOperationRemoveTask = clientOperation("history-remove-task") HistoryClientOperationDescribeMutableState = clientOperation("history-describe-mutable-state") HistoryClientOperationGetMutableState = clientOperation("history-get-mutable-state") HistoryClientOperationPollMutableState = clientOperation("history-poll-mutable-state") HistoryClientOperationResetStickyTaskList = clientOperation("history-reset-task-list") HistoryClientOperationDescribeWorkflowExecution = clientOperation("history-describe-wf-execution") HistoryClientOperationRecordDecisionTaskStarted = clientOperation("history-record-decision-task-started") HistoryClientOperationRecordActivityTaskStarted = clientOperation("history-record-activity-task-started") HistoryClientOperationRecordDecisionTaskCompleted = clientOperation("history-record-decision-task-completed") HistoryClientOperationRecordDecisionTaskFailed = clientOperation("history-record-decision-task-failed") HistoryClientOperationRecordActivityTaskCompleted = clientOperation("history-record-activity-task-completed") HistoryClientOperationRecordActivityTaskFailed = clientOperation("history-record-activity-task-failed") HistoryClientOperationRecordActivityTaskCanceled = clientOperation("history-record-activity-task-canceled") HistoryClientOperationRecordActivityTaskHeartbeat = clientOperation("history-record-activity-task-heartbeat") HistoryClientOperationRequestCancelWorkflowExecution = clientOperation("history-request-cancel-wf-execution") HistoryClientOperationSignalWorkflowExecution = clientOperation("history-signal-wf-execution") HistoryClientOperationSignalWithStartWorkflowExecution = clientOperation("history-signal-with-start-wf-execution") HistoryClientOperationRemoveSignalMutableState = clientOperation("history-remove-signal-mutable-state") HistoryClientOperationTerminateWorkflowExecution = clientOperation("history-terminate-wf-execution") HistoryClientOperationResetWorkflowExecution = clientOperation("history-reset-wf-execution") HistoryClientOperationScheduleDecisionTask = clientOperation("history-schedule-decision-task") HistoryClientOperationRecordChildExecutionCompleted = clientOperation("history-record-child-execution-completed") HistoryClientOperationReplicateEventsV2 = clientOperation("history-replicate-events-v2") HistoryClientOperationSyncShardStatus = clientOperation("history-sync-shard-status") HistoryClientOperationSyncActivity = clientOperation("history-sync-activity") HistoryClientOperationGetReplicationMessages = clientOperation("history-get-replication-messages") HistoryClientOperationGetDLQReplicationMessages = clientOperation("history-get-dlq-replication-messages") HistoryClientOperationQueryWorkflow = clientOperation("history-query-wf") HistoryClientOperationReapplyEvents = clientOperation("history-reapply-events") HistoryClientOperationCountDLQMessages = clientOperation("history-count-dlq-messages") HistoryClientOperationReadDLQMessages = clientOperation("history-read-dlq-messages") HistoryClientOperationPurgeDLQMessages = clientOperation("history-purge-dlq-messages") HistoryClientOperationMergeDLQMessages = clientOperation("history-merge-dlq-messages") HistoryClientOperationRefreshWorkflowTasks = clientOperation("history-refresh-wf-tasks") HistoryClientOperationNotifyFailoverMarkers = clientOperation("history-notify-failover-markers") HistoryClientOperationGetCrossClusterTasks = clientOperation("history-get-cross-cluster-tasks") HistoryClientOperationRespondCrossClusterTasksCompleted = clientOperation("history-respond-cross-cluster-tasks-completed") HistoryClientOperationGetFailoverInfo = clientOperation("history-get-failover-info") HistoryClientOperationRespondActivityTaskCanceled = clientOperation("history-respond-activity-task-canceled") HistoryClientOperationRespondActivityTaskCompleted = clientOperation("history-respond-activity-task-completed") HistoryClientOperationRespondActivityTaskFailed = clientOperation("history-respond-activity-task-failed") HistoryClientOperationRespondDecisionTaskCompleted = clientOperation("history-respond-decision-task-completed") HistoryClientOperationRespondDecisionTaskFailed = clientOperation("history-respond-decision-task-failed") HistoryClientOperationRatelimitUpdate = clientOperation("history-ratelimit-update") MatchingClientOperationAddActivityTask = clientOperation("matching-add-activity-task") MatchingClientOperationAddDecisionTask = clientOperation("matching-add-decision-task") MatchingClientOperationPollForActivityTask = clientOperation("matching-poll-for-activity-task") MatchingClientOperationPollForDecisionTask = clientOperation("matching-poll-for-decision-task") MatchingClientOperationQueryWorkflow = clientOperation("matching-query-wf") MatchingClientOperationQueryTaskCompleted = clientOperation("matching-query-task-completed") MatchingClientOperationCancelOutstandingPoll = clientOperation("matching-cancel-outstanding-poll") MatchingClientOperationDescribeTaskList = clientOperation("matching-describe-task-list") MatchingClientOperationListTaskListPartitions = clientOperation("matching-list-task-list-partitions") MatchingClientOperationGetTaskListsByDomain = clientOperation("matching-get-task-list-for-domain") MatchingClientOperationRespondQueryTaskCompleted = clientOperation("matching-respond-query-task-completed") MatchingClientOperationUpdateTaskListPartitionConfig = clientOperation("matching-update-task-list-partition-config") MatchingClientOperationRefreshTaskListPartitionConfig = clientOperation("matching-refresh-task-list-partition-config") ShardDistributorClientOperationGetShardOwner = clientOperation("shard-distributor-get-shard-owner") ShardDistributorClientOperationWatchNamespaceState = clientOperation("shard-distributor-watch-namespace-state") ShardDistributorExecutorClientOperationHeartbeat = clientOperation("shard-distributor-executor-heartbeat") ) // Pre-defined values for TagIDType var ( IDTypeDomainName = idType("domainName") IDTypeIdentity = idType("identity") IDTypeWorkflowID = idType("workflowID") IDTypeSignalName = idType("signalName") IDTypeWorkflowType = idType("workflowType") IDTypeRequestID = idType("requestID") IDTypeTaskListName = idType("taskListName") IDTypeActivityID = idType("activityID") IDTypeActivityType = idType("activityType") IDTypeMarkerName = idType("markerName") IDTypeTimerID = idType("timerID") ) ================================================ FILE: common/log/testlogger/fx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testlogger import ( "testing" "go.uber.org/fx" ) // Module allows to push testlogger for tests. func Module(t *testing.T) fx.Option { return fx.Options( fx.Provide(func() TestingT { return TestingT(t) }), fx.Provide(New), ) } ================================================ FILE: common/log/testlogger/fx_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testlogger import ( "testing" "go.uber.org/fx" "go.uber.org/fx/fxtest" "github.com/uber/cadence/common/log" ) func TestModule(t *testing.T) { app := fxtest.New(t, Module(t), fx.Invoke(func(logger log.Logger) {})) app.RequireStart().RequireStop() } ================================================ FILE: common/log/testlogger/testlogger.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testlogger import ( "fmt" "slices" "strings" "github.com/stretchr/testify/require" "go.uber.org/atomic" "go.uber.org/zap" "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest/observer" "github.com/uber/cadence/common/log" ) type TestingT interface { zaptest.TestingT Cleanup(func()) // not currently part of zaptest.TestingT } // New is a helper to create new development logger in unit test func New(t TestingT) log.Logger { // test logger that emits all logs (none dropped / sample func never returns false) return log.NewLogger(NewZap(t), log.WithSampleFunc(func(int) bool { return true })) } // NewZap makes a new test-oriented logger that prevents bad-lifecycle logs from failing tests. func NewZap(t TestingT) *zap.Logger { /* HORRIBLE HACK due to async shutdown, both in our code and in libraries (e.g. gocql): normally, logs produced after a test finishes will *intentionally* fail the test and/or cause data to race on the test's internal `t.done` field. that's a good thing, it reveals possibly-dangerously-flawed lifecycle management. unfortunately some of our code and some libraries do not have good lifecycle management, and this cannot easily be patched from the outside. so this logger cheats: after a test completes, it logs to stderr rather than TestingT. EVERY ONE of these logs is bad and we should not produce them, but it's causing many otherwise-useful tests to be flaky, and that's a larger interruption than is useful. */ logAfterComplete, err := zap.NewDevelopment() require.NoError(t, err, "could not build a fallback zap logger") replaced := &fallbackTestCore{ t: t, fallback: logAfterComplete.Core(), testing: zaptest.NewLogger(t).Core(), completed: &atomic.Bool{}, } t.Cleanup(replaced.UseFallback) // switch to fallback before ending the test return zap.New(replaced) } // NewObserved makes a new test logger that both logs to `t` and collects logged // events for asserting in tests. func NewObserved(t TestingT) (log.Logger, *observer.ObservedLogs) { obsCore, obs := observer.New(zapcore.DebugLevel) z := NewZap(t) z = z.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core { return zapcore.NewTee(core, obsCore) })) l := log.NewLogger(z, log.WithSampleFunc(func(int) bool { return true })) return l, obs } type fallbackTestCore struct { t TestingT fallback zapcore.Core testing zapcore.Core completed *atomic.Bool } var _ zapcore.Core = (*fallbackTestCore)(nil) func (f *fallbackTestCore) UseFallback() { f.completed.Store(true) } func (f *fallbackTestCore) Enabled(level zapcore.Level) bool { if f.completed.Load() { return f.fallback.Enabled(level) } return f.testing.Enabled(level) } func (f *fallbackTestCore) With(fields []zapcore.Field) zapcore.Core { // need to copy and defer, else the returned core will be used at an // arbitrarily later point in time, possibly after the test has completed. return &fallbackTestCore{ t: f.t, fallback: f.fallback.With(fields), testing: f.testing.With(fields), completed: f.completed, } } func (f *fallbackTestCore) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry { // see other Check impls, all look similar. // this defers the "where to log" decision to Write, as `f` is the core that will write. if f.fallback.Enabled(entry.Level) { return checked.AddCore(entry, f) } return checked // do not add any cores } func (f *fallbackTestCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { if f.completed.Load() { entry.Message = fmt.Sprintf("COULD FAIL TEST %q, logged too late: %v", f.t.Name(), entry.Message) hasStack := slices.ContainsFunc(fields, func(field zapcore.Field) bool { // no specific stack-trace type, so just look for probable fields. return strings.Contains(strings.ToLower(field.Key), "stack") }) if !hasStack { fields = append(fields, zap.Stack("log_stack")) } return f.fallback.Write(entry, fields) } return f.testing.Write(entry, fields) } func (f *fallbackTestCore) Sync() error { if f.completed.Load() { return f.fallback.Sync() } return f.testing.Sync() } var _ zapcore.Core = (*fallbackTestCore)(nil) ================================================ FILE: common/log/testlogger/testlogger_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testlogger import ( "fmt" "os" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) var ( done = make(chan struct{}) logged = make(chan struct{}) ) func TestMain(m *testing.M) { code := m.Run() // ensure synchronization between t.done and t.logf, else this test is extremely flaky. // for details see: https://github.com/golang/go/issues/67701 close(done) select { case <-logged: os.Exit(code) case <-time.After(time.Second): // should be MUCH faster _, _ = fmt.Fprintln(os.Stderr, "timed out waiting for test to log") os.Exit(1) } } // Unfortunately a moderate hack, to work around our faulty lifecycle management, // and some libraries with issues as well. // Ideally this test WOULD fail, but that's much harder to assert "safely". func TestLoggerShouldNotFailIfLoggedLate(t *testing.T) { origLogger := New(t) // if With does not defer core selection, this will fail the test // by sending the logs to t.Logf withLogger := origLogger.WithTags(tag.ActorID("testing")) // literally any tag origLogger.Info("before is fine, orig") withLogger.Info("before is fine, with") go func() { <-done origLogger.Info("too late, orig") withLogger.Info("too late, with") close(logged) }() } func TestSubtestShouldNotFail(t *testing.T) { // when complete, a subtest's too-late logs just get pushed to the parent, // and do not fail any tests. they only fail when no running parent exists. // // if Go changes this behavior, this test could fail, otherwise AFAICT it // should be stable. assertDoesNotFail := func(name string, setup, log func(t *testing.T)) { // need to wrap in something that will out-live the "real" test, // to ensure there is a running parent test to push logs toward. t.Run(name, func(t *testing.T) { // same setup as TestMain but contained within this sub-test var ( done = make(chan struct{}) logged = make(chan struct{}) ) t.Run("inner", func(t *testing.T) { setup(t) go func() { <-done // despite being too late, the parent test is still running // so this does not fail the test. log(t) close(logged) }() time.AfterFunc(10*time.Millisecond, func() { close(done) }) }) <-logged }) } assertDoesNotFail("real", func(t *testing.T) { // no setup needed }, func(t *testing.T) { t.Logf("too late but allowed") }) var l log.Logger assertDoesNotFail("wrapped", func(t *testing.T) { l = New(t) }, func(t *testing.T) { l.Info("too late but allowed") }) } func TestObserver(t *testing.T) { l, obs := NewObserved(t) l.Info("a log") l.Info("some unrelated log") assert.Len(t, obs.FilterMessage("a log").All(), 1, "did not find a log that was logged") } ================================================ FILE: common/log/throttle.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package log import ( "math/rand" "sync/atomic" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/quotas" ) type throttledLogger struct { rps int32 limiter quotas.Limiter log Logger } const skipForThrottleLogger = 6 // NewThrottledLogger returns an implementation of logger that throttles the // log messages being emitted. The underlying implementation uses a token bucket // ratelimiter and stops emitting logs once the bucket runs out of tokens // // Fatal/Panic logs are always emitted without any throttling func NewThrottledLogger(logger Logger, rps dynamicproperties.IntPropertyFn) Logger { var log Logger lg, ok := logger.(*loggerImpl) if ok { log = &loggerImpl{ zapLogger: lg.zapLogger, skip: skipForThrottleLogger, sampleLocalFn: lg.sampleLocalFn, } } else { logger.Warn("ReplayLogger may not emit callat tag correctly because the logger passed in is not loggerImpl") log = logger } rate := rps() limiter := quotas.NewDynamicRateLimiter(func() float64 { return float64(rps()) }) tl := &throttledLogger{ limiter: limiter, rps: int32(rate), log: log, } return tl } func (tl *throttledLogger) Debugf(msg string, args ...any) { tl.rateLimit(func() { tl.log.Debugf(msg, args...) }) } func (tl *throttledLogger) Debug(msg string, tags ...tag.Tag) { tl.rateLimit(func() { tl.log.Debug(msg, tags...) }) } func (tl *throttledLogger) Info(msg string, tags ...tag.Tag) { tl.rateLimit(func() { tl.log.Info(msg, tags...) }) } func (tl *throttledLogger) Warn(msg string, tags ...tag.Tag) { tl.rateLimit(func() { tl.log.Warn(msg, tags...) }) } func (tl *throttledLogger) Error(msg string, tags ...tag.Tag) { tl.rateLimit(func() { tl.log.Error(msg, tags...) }) } func (tl *throttledLogger) Fatal(msg string, tags ...tag.Tag) { tl.rateLimit(func() { tl.log.Fatal(msg, tags...) }) } func (tl *throttledLogger) SampleInfo(msg string, sampleRate int, tags ...tag.Tag) { if rand.Intn(sampleRate) == 0 { tl.rateLimit(func() { tl.log.Info(msg, tags...) }) } } func (tl *throttledLogger) DebugOn() bool { return tl.log.DebugOn() } // Return a logger with the specified key-value pairs set, to be included in a subsequent normal logging call func (tl *throttledLogger) WithTags(tags ...tag.Tag) Logger { result := &throttledLogger{ rps: atomic.LoadInt32(&tl.rps), limiter: tl.limiter, log: tl.log.WithTags(tags...), } return result } func (tl *throttledLogger) rateLimit(f func()) { if ok := tl.limiter.Allow(); ok { f() } } func (tl *throttledLogger) Helper() Logger { result := &throttledLogger{ rps: atomic.LoadInt32(&tl.rps), limiter: tl.limiter, log: tl.log.Helper(), } return result } ================================================ FILE: common/mapq/README.md ================================================ # MAPQ: Multi-tenant, Auto-partitioned, Persistent Queue NOTE: This component is WIP. ## Overview MAPQ is a new queue framework (introduced in June 2024), aiming to unify Cadence's internal task/request queues. The existing implementations for these applications are cumbersome and maintenance-heavy, with significant overlap and limited extensibility. MAPQ will address the challenges of scalability, throughput, consistency, and ordering guarantees required by these diverse needs. Challenges and Motivation - History Task Queues: These queues are poorly understood and difficult to maintain, owing to the departure of their original developer and the non-maintainable state of the code. The design struggles with burst loads from timer/child workflow cases, requiring introduction of more granular task types and automated partitioning that the current system cannot support without extensive refactoring. - Matching Task Lists: These are basic FIFO queues with some advanced features like sticky task lists, zonal isolation groups and partitioning. The most pressing issue is auto partitioning to reduce operational overhead. - Async Request Queues: Initially integrated with Kafka topics as the request queue. Initial testing faced challenges like complex provisioning, inability to dynamically create topics/register consumers, poor visibility into the requests in the queue and difficult to tweak alerts. Async APIs are already designed with pluggable queue implementation already so swapping Kafka with something else will not be tricky. ### Goals MAPQ will provide a solution tailored to meet the following goals: - Multi-Tenancy: Guarantees fair access to resources for each tenant based on predefined quotas. - Auto-Partitioning: Dynamically adjusts partitions based on specified fine-grained policies, supporting both random and deterministic message distribution across physically or virtually partitioned queues. - Burst-Protection: Detects incoming message spikes and mitigates by utilizing dynamic auto-partitioning. - Skew-Protection: Detects incoming message skews for given partition keys and mitigates by utilizing dynamic auto-partitioning. - Advanced Partitioning Policies: Executes on a tree-like partitioning policy to support various levels of partition key hierarchies and strategies. - Persistent: Ensures message durability via pluggable persistent layer. - Delivery Guarantees: Guarantees at least once delivery. ### Problems with Existing Queues in Cadence History Queues: - Lack of granular partitioning and inextensibility of history queues make it difficult to address following pathological scenarios: - Task prioritization: Background tasks like workflow deletion timer tasks share the same queue and consume from the same “processing budget” as other high priority tasks such as user created timers. This is because all timer tasks for a given shard are managed by a single queue. - Multi tenancy: Tasks of the same type (e.g. all timers) are managed by a single queue and a noisy domain can drastically regress the experience of other domains. It is not possible to write tasks of a specific domain(s) to a separate queue and adjust read/write qps. Current queue granularity ends at task type (timer or transfer). - Burst cases: Bursts of timers or child workflows are known issues that Cadence has no answers to. These bursts usually cause timeouts and may also impact processing of other domains’ tasks. ## High Level Design MAPQ uses a tree data structure where nodes route incoming messages to child nodes. Nodes can be splitted/merged based on given policies. All leaf nodes are at the same level. Leaf nodes are the actual “queues” where messages are written to/read from via a provided persistent layer plugin. The routing key per level, partitioning/departitioning strategy, RPS limits and other options are provided to MAPQ during initialization as a tree-like policy. It contains per level defaults and per-node (identified via path from root) overrides. Once initialized the tree will have a minimal number of nodes provided in the policy but it respects policies for not-yet-existing nodes. Since MAPQ supports auto-partitioning there will be new nodes added/removed and it accepts providing policies for such nodes. For example, you might want to partition by domain only for bursty domains and allocate them specific RPS. #### Tree structure with policies ![MAPQ partitioned queue tree](../../docs/images/mapq_partitioned_queue_tree_example.png) #### Initialization and Object Hierarcy ![MAPQ initialization](../../docs/images/mapq_initialization.png) #### Enqueue Flow ![MAPQ enqueue flow](../../docs/images/mapq_enqueue_flow.png) #### Dispatch Flow ![MAPQ enqueue flow](../../docs/images/mapq_dispatch_flow.png) ================================================ FILE: common/mapq/client_impl.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package mapq import ( "context" "errors" "fmt" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mapq/tree" "github.com/uber/cadence/common/mapq/types" "github.com/uber/cadence/common/metrics" ) type clientImpl struct { logger log.Logger scope metrics.Scope persister types.Persister consumerFactory types.ConsumerFactory tree *tree.QueueTree partitions []string policies []types.NodePolicy } func (c *clientImpl) Start(ctx context.Context) error { c.logger.Info("Starting MAPQ client") err := c.tree.Start(ctx) if err != nil { return err } c.logger.Info("Started MAPQ client") return nil } func (c *clientImpl) Stop(ctx context.Context) error { c.logger.Info("Stopping MAPQ client") // Stop the tree which will stop the dispatchers if err := c.tree.Stop(ctx); err != nil { return fmt.Errorf("failed to stop tree: %w", err) } // stop the consumer factory which will stop the consumers err := c.consumerFactory.Stop(ctx) if err != nil { return fmt.Errorf("failed to stop consumer factory: %w", err) } c.logger.Info("Stopped MAPQ client") return nil } func (c *clientImpl) Enqueue(ctx context.Context, items []types.Item) ([]types.ItemToPersist, error) { return c.tree.Enqueue(ctx, items) } func (c *clientImpl) Ack(context.Context, types.Item) error { return errors.New("not implemented") } func (c *clientImpl) Nack(context.Context, types.Item) error { return errors.New("not implemented") } ================================================ FILE: common/mapq/client_impl_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package mapq import ( "context" "testing" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/mapq/types" "github.com/uber/cadence/common/metrics" ) func TestNew(t *testing.T) { ctrl := gomock.NewController(t) tests := []struct { name string opts []Options wantErr bool }{ { name: "success", opts: []Options{ WithPersister(types.NewMockPersister(ctrl)), WithConsumerFactory(types.NewMockConsumerFactory(ctrl)), }, }, { name: "no persister", wantErr: true, opts: []Options{ WithConsumerFactory(types.NewMockConsumerFactory(ctrl)), }, }, { name: "no consumer factoru", wantErr: true, opts: []Options{ WithPersister(types.NewMockPersister(ctrl)), }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { logger := testlogger.New(t) scope := metrics.NoopScope cl, err := New(logger, scope, tc.opts...) if (err != nil) != tc.wantErr { t.Errorf("New() error: %v, wantErr: %v", err, tc.wantErr) } if err != nil { return } _, ok := cl.(*clientImpl) if !ok { t.Errorf("New() = %T, want *clientImpl", cl) } }) } } func TestStartStop(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) consumerFactory := types.NewMockConsumerFactory(ctrl) consumer := types.NewMockConsumer(ctrl) consumerFactory.EXPECT().Stop(gomock.Any()).Return(nil).Times(1) consumerFactory.EXPECT().New(gomock.Any()).Return(consumer, nil).Times(1) opts := []Options{ WithPersister(types.NewMockPersister(ctrl)), WithConsumerFactory(consumerFactory), } logger := testlogger.New(t) scope := metrics.NoopScope cl, err := New(logger, scope, opts...) if err != nil { t.Fatalf("New() error: %v", err) } cl.Start(context.Background()) defer cl.Stop(context.Background()) } func TestAck(t *testing.T) { ctrl := gomock.NewController(t) consumerFactory := types.NewMockConsumerFactory(ctrl) consumer := types.NewMockConsumer(ctrl) consumerFactory.EXPECT().Stop(gomock.Any()).Return(nil).Times(1) consumerFactory.EXPECT().New(gomock.Any()).Return(consumer, nil).Times(1) opts := []Options{ WithPersister(types.NewMockPersister(ctrl)), WithConsumerFactory(consumerFactory), } logger := testlogger.New(t) scope := metrics.NoopScope cl, err := New(logger, scope, opts...) if err != nil { t.Fatalf("New() error: %v", err) } cl.Start(context.Background()) defer cl.Stop(context.Background()) err = cl.Ack(context.Background(), nil) if err == nil || err.Error() != "not implemented" { t.Errorf("Ack() error: %q, want %q", err, "not implemented") } } func TestNack(t *testing.T) { ctrl := gomock.NewController(t) consumerFactory := types.NewMockConsumerFactory(ctrl) consumer := types.NewMockConsumer(ctrl) consumerFactory.EXPECT().Stop(gomock.Any()).Return(nil).Times(1) consumerFactory.EXPECT().New(gomock.Any()).Return(consumer, nil).Times(1) opts := []Options{ WithPersister(types.NewMockPersister(ctrl)), WithConsumerFactory(consumerFactory), } logger := testlogger.New(t) scope := metrics.NoopScope cl, err := New(logger, scope, opts...) if err != nil { t.Fatalf("New() error: %v", err) } cl.Start(context.Background()) defer cl.Stop(context.Background()) err = cl.Nack(context.Background(), nil) if err == nil || err.Error() != "not implemented" { t.Errorf("Ack() error: %q, want %q", err, "not implemented") } } ================================================ FILE: common/mapq/dispatcher/dispatcher.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package dispatcher import ( "context" "fmt" "sync" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/mapq/types" ) type Dispatcher struct { consumer types.Consumer ctx context.Context cancelCtx context.CancelFunc wg sync.WaitGroup } func New(c types.Consumer) *Dispatcher { ctx, cancelCtx := context.WithCancel(context.Background()) return &Dispatcher{ consumer: c, ctx: ctx, cancelCtx: cancelCtx, } } func (d *Dispatcher) Start(ctx context.Context) error { d.wg.Add(1) go d.run() return nil } func (d *Dispatcher) Stop(ctx context.Context) error { d.cancelCtx() timeout := 10 * time.Second if dl, ok := ctx.Deadline(); ok { timeout = time.Until(dl) } if !common.AwaitWaitGroup(&d.wg, timeout) { return fmt.Errorf("failed to stop dispatcher in %v", timeout) } return nil } func (d *Dispatcher) run() { defer d.wg.Done() // TODO: implement } ================================================ FILE: common/mapq/dispatcher/dispatcher_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package dispatcher import ( "context" "testing" "time" "go.uber.org/goleak" ) func TestStartStop(t *testing.T) { defer goleak.VerifyNone(t) d := New(nil) err := d.Start(context.Background()) if err != nil { t.Fatalf("Start() failed: %v", err) } ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) defer cancel() err = d.Stop(ctx) if err != nil { t.Fatalf("Stop() failed: %v", err) } } ================================================ FILE: common/mapq/example_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package mapq import ( "context" "fmt" "testing" "time" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/mapq/types" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) func TestExample(t *testing.T) { persister := &InMemoryPersister{} logger := testlogger.New(t) scope := metrics.NoopScope cl, err := New( logger, scope, WithConsumerFactory(&NoOpConsumerFactory{}), WithPersister(persister), WithPartitions([]string{"type", "sub-type", "domain"}), WithPolicies([]types.NodePolicy{ // level 0: default policy for root (splitted by type) { Path: "*", SplitPolicy: &types.SplitPolicy{ PredefinedSplits: []any{"timer", "transfer"}, }, }, // level 1: default policy (splitted by sub-type) { Path: "*/.", SplitPolicy: &types.SplitPolicy{ PredefinedSplits: []any{}, }, }, // level 1: timer node { Path: "*/timer", SplitPolicy: &types.SplitPolicy{ PredefinedSplits: []any{ persistence.TaskTypeDeleteHistoryEvent, }, }, }, // level 1: transfer node { Path: "*/transfer", SplitPolicy: &types.SplitPolicy{ PredefinedSplits: []any{ persistence.TransferTaskTypeStartChildExecution, }, }, }, // level 2: nodes per pairs // - default 1000 RPS for per sub-type node // - split by domain. predefined split for d3 domain { Path: "*/./.", SplitPolicy: &types.SplitPolicy{ PredefinedSplits: []any{"d3"}, }, }, // override for timer delete history event: // - only allow 50 RPS // - disable split policy { Path: "*/timer/4", DispatchPolicy: &types.DispatchPolicy{DispatchRPS: 50}, SplitPolicy: &types.SplitPolicy{Disabled: true}, }, // override for start child execution // - only allow 10 RPS // - disable split policy { Path: "*/transfer/4", DispatchPolicy: &types.DispatchPolicy{DispatchRPS: 10}, SplitPolicy: &types.SplitPolicy{Disabled: true}, }, // level 3: default policy for all nodes at this level. nodes per pairs // - only allow 100 rps // - disable split policy { Path: "*/././.", DispatchPolicy: &types.DispatchPolicy{DispatchRPS: 100}, SplitPolicy: &types.SplitPolicy{Disabled: true}, }, // level 3: override policy for catch-all nodes at this level (all domains that don't have a specific node) // this policy will override the 100 RPS policy defined above to give more RPS to catch-all nodes { Path: "*/././*", DispatchPolicy: &types.DispatchPolicy{DispatchRPS: 1000}, SplitPolicy: &types.SplitPolicy{Disabled: true}, }, }), ) if err != nil { panic(err) } ctx := context.Background() if err := cl.Start(ctx); err != nil { panic(err) } defer cl.Stop(ctx) _, err = cl.Enqueue(context.Background(), []types.Item{ newTimerItem("d1", time.Now(), persistence.TaskTypeDecisionTimeout), newTimerItem("d1", time.Now(), persistence.TaskTypeActivityTimeout), newTimerItem("d1", time.Now(), persistence.TaskTypeUserTimer), newTimerItem("d3", time.Now(), persistence.TaskTypeUserTimer), newTimerItem("d3", time.Now(), persistence.TaskTypeUserTimer), }) if err != nil { panic(err) } if len(persister.items) != 5 { panic(fmt.Errorf("expected 5 items in persister, got %v", len(persister.items))) } _, err = cl.Enqueue(context.Background(), []types.Item{ newTransferItem("d2", 1, persistence.TransferTaskTypeDecisionTask), newTransferItem("d2", 2, persistence.TransferTaskTypeActivityTask), newTransferItem("d2", 3, persistence.TransferTaskTypeStartChildExecution), newTransferItem("d2", 4, persistence.TransferTaskTypeStartChildExecution), newTransferItem("d2", 5, persistence.TransferTaskTypeStartChildExecution), }) if err != nil { panic(err) } if len(persister.items) != 10 { panic(fmt.Errorf("expected 10 items in persister, got %v", len(persister.items))) } } var _ types.ConsumerFactory = (*NoOpConsumerFactory)(nil) type NoOpConsumerFactory struct{} func (f *NoOpConsumerFactory) New(types.ItemPartitions) (types.Consumer, error) { return &NoOpConsumer{}, nil } func (f *NoOpConsumerFactory) Stop(context.Context) error { return nil } type NoOpConsumer struct{} func (c *NoOpConsumer) Start(context.Context) error { return nil } func (c *NoOpConsumer) Stop(context.Context) error { return nil } func (c *NoOpConsumer) Process(ctx context.Context, item types.Item) error { fmt.Printf("processing item: %v\n", item) return nil } type InMemoryPersister struct { items []types.ItemToPersist offsets *types.Offsets } func (p *InMemoryPersister) Persist(ctx context.Context, items []types.ItemToPersist) error { fmt.Printf("persisting %v items\n", len(items)) for _, item := range items { partitionsKV := map[string]any{} actualKV := map[string]any{} for _, k := range item.GetPartitionKeys() { partitionsKV[k] = item.GetPartitionValue(k) actualKV[k] = item.GetAttribute(k) } fmt.Printf("item attributes: %v, partitions: %v\n", actualKV, partitionsKV) } p.items = append(p.items, items...) return nil } func (p *InMemoryPersister) GetOffsets(context.Context) (*types.Offsets, error) { return p.offsets, nil } func (p *InMemoryPersister) CommitOffsets(ctx context.Context, offsets *types.Offsets) error { fmt.Printf("committing offsets: %v\n", offsets) p.offsets = offsets return nil } // Fetch(ctx context.Context, partitions ItemPartitions, pageInfo PageInfo) ([]Item, error) func (p *InMemoryPersister) Fetch(ctx context.Context, partitions types.ItemPartitions, pageInfo types.PageInfo) ([]types.Item, error) { return nil, nil } func newTimerItem(domain string, t time.Time, timerType int) types.Item { switch timerType { case persistence.TaskTypeDecisionTimeout: case persistence.TaskTypeActivityTimeout: case persistence.TaskTypeUserTimer: case persistence.TaskTypeWorkflowTimeout: case persistence.TaskTypeDeleteHistoryEvent: case persistence.TaskTypeActivityRetryTimer: case persistence.TaskTypeWorkflowBackoffTimer: default: panic(fmt.Errorf("unknown timer type: %v", timerType)) } return &timerItem{ t: t, timerType: timerType, domain: domain, } } type timerItem struct { t time.Time timerType int domain string } func (t *timerItem) String() string { return fmt.Sprintf("timerItem{timerType: %v, time: %v}", t.timerType, t.t) } func (t *timerItem) Offset() int64 { return t.t.UnixNano() } func (t *timerItem) GetAttribute(key string) any { switch key { case "type": return "timer" case "sub-type": return t.timerType case "domain": return t.domain default: panic(fmt.Errorf("unknown key: %v", key)) } } func newTransferItem(domain string, taskID int64, transferType int) types.Item { switch transferType { case persistence.TransferTaskTypeActivityTask: case persistence.TransferTaskTypeDecisionTask: case persistence.TransferTaskTypeCloseExecution: case persistence.TransferTaskTypeCancelExecution: case persistence.TransferTaskTypeSignalExecution: case persistence.TransferTaskTypeStartChildExecution: case persistence.TransferTaskTypeRecordWorkflowStarted: case persistence.TransferTaskTypeResetWorkflow: case persistence.TransferTaskTypeUpsertWorkflowSearchAttributes: case persistence.TransferTaskTypeRecordWorkflowClosed: case persistence.TransferTaskTypeRecordChildExecutionCompleted: case persistence.TransferTaskTypeApplyParentClosePolicy: default: panic(fmt.Errorf("unknown transfer type: %v", transferType)) } return &transferItem{ domain: domain, taskID: taskID, transferType: transferType, } } type transferItem struct { taskID int64 transferType int domain string } func (t *transferItem) String() string { return fmt.Sprintf("transferItem{transferType: %v, taskID: %v}", t.transferType, t.taskID) } func (t *transferItem) Offset() int64 { return t.taskID } func (t *transferItem) GetAttribute(key string) any { switch key { case "type": return "transfer" case "sub-type": return t.transferType case "domain": return t.domain default: panic(fmt.Errorf("unknown key: %v", key)) } } ================================================ FILE: common/mapq/mapq.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package mapq import ( "fmt" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/mapq/tree" "github.com/uber/cadence/common/mapq/types" "github.com/uber/cadence/common/metrics" ) type Options func(*clientImpl) func WithPersister(p types.Persister) Options { return func(c *clientImpl) { c.persister = p } } func WithConsumerFactory(cf types.ConsumerFactory) Options { return func(c *clientImpl) { c.consumerFactory = cf } } // WithPartitions sets the partition keys for each level. // MAPQ creates a tree with depth = len(partitions) func WithPartitions(partitions []string) Options { return func(c *clientImpl) { c.partitions = partitions } } // WithPolicies sets the policies for the MAPQ instance. // Policies can be defined for nodes at a specific level or nodes with specific path. // // Path conventions: // - "*" -> represents the root node at level 0 // - "*/." matches with all nodes at level 1 // - "*/*" represents the catch-all node at level 1 // - "*/xyz" represents a specific node at level 1 whose partition value is xyz // - "*/./." matches with all nodes at level 2 // - "*/xyz/." matches with all nodes at level 2 whose parent is xyz node // - "*/xyz/*" represents the catch-all node at level 2 whose parent is xyz node // - "*/xyz/abc" represents a specific node at level 2 whose level 2 attribute value is abc and parent is xyz node func WithPolicies(policies []types.NodePolicy) Options { return func(c *clientImpl) { c.policies = policies } } func New(logger log.Logger, scope metrics.Scope, opts ...Options) (types.Client, error) { c := &clientImpl{ logger: logger.WithTags(tag.ComponentMapQ), scope: scope, } for _, opt := range opts { opt(c) } if c.persister == nil { return nil, fmt.Errorf("persister is required. Use WithPersister option to set it") } if c.consumerFactory == nil { return nil, fmt.Errorf("consumer factory is required. Use WithConsumerFactory option to set it") } tree, err := tree.New(logger, scope, c.partitions, c.policies, c.persister, c.consumerFactory) if err != nil { return nil, err } c.tree = tree c.logger.Info("MAPQ client created", tag.Dynamic("partitions", c.partitions), tag.Dynamic("policies", c.policies), tag.Dynamic("tree", c.tree.String()), ) return c, nil } ================================================ FILE: common/mapq/tree/queue_tree.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tree import ( "context" "fmt" "strings" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/mapq/types" "github.com/uber/cadence/common/metrics" ) // QueueTree is a tree structure that represents the queue structure for MAPQ type QueueTree struct { originalLogger log.Logger logger log.Logger scope metrics.Scope partitions []string policyCol types.NodePolicyCollection persister types.Persister consumerFactory types.ConsumerFactory root *QueueTreeNode } func New( logger log.Logger, scope metrics.Scope, partitions []string, policies []types.NodePolicy, persister types.Persister, consumerFactory types.ConsumerFactory, ) (*QueueTree, error) { t := &QueueTree{ originalLogger: logger, logger: logger.WithTags(tag.ComponentMapQTree), scope: scope, partitions: partitions, policyCol: types.NewNodePolicyCollection(policies), persister: persister, consumerFactory: consumerFactory, } return t, t.init() } // Start the dispatchers for all leaf nodes func (t *QueueTree) Start(ctx context.Context) error { t.logger.Info("Starting MAPQ tree", tag.Dynamic("tree", t.String())) err := t.root.Start(ctx, t.consumerFactory, nil, map[string]any{}) if err != nil { return fmt.Errorf("failed to start root node: %w", err) } t.logger.Info("Started MAPQ tree") return nil } // Stop the dispatchers for all leaf nodes func (t *QueueTree) Stop(ctx context.Context) error { t.logger.Info("Stopping MAPQ tree", tag.Dynamic("tree", t.String())) err := t.root.Stop(ctx) if err != nil { return fmt.Errorf("failed to stop nodes: %w", err) } t.logger.Info("Stopped MAPQ tree") return nil } func (t *QueueTree) String() string { var sb strings.Builder var nodes []*QueueTreeNode nodes = append(nodes, t.root) for len(nodes) > 0 { node := nodes[0] nodes = nodes[1:] sb.WriteString(node.String()) sb.WriteString("\n") for _, child := range node.Children { nodes = append(nodes, child) } } return sb.String() } func (t *QueueTree) Enqueue(ctx context.Context, items []types.Item) ([]types.ItemToPersist, error) { if t.root == nil { return nil, fmt.Errorf("root node is nil") } var itemsToPersist []types.ItemToPersist for _, item := range items { itemToPersist, err := t.root.Enqueue(ctx, item, nil, map[string]any{}) if err != nil { return nil, err } itemsToPersist = append(itemsToPersist, itemToPersist) } return itemsToPersist, t.persister.Persist(ctx, itemsToPersist) } func (t *QueueTree) init() error { t.root = &QueueTreeNode{ Path: "*", // Root node Children: map[any]*QueueTreeNode{}, } if err := t.root.Init(t.originalLogger, t.scope, t.policyCol, t.partitions); err != nil { return fmt.Errorf("failed to initialize node: %w", err) } // Create tree nodes with catch-all nodes at all levels and predefined splits. // There will be len(partitions) levels in the tree. err := t.constructInitialNodes(t.root) if err != nil { return fmt.Errorf("failed to construct initial tree: %w", err) } return nil } func (t *QueueTree) constructInitialNodes(n *QueueTreeNode) error { nodeLevel := nodeLevel(n.Path) if nodeLevel == len(t.partitions) { // reached the leaf level return nil } if n.Children["*"] != nil { // catch-all node already exists return nil } _, err := n.addChild("*", t.policyCol, t.partitions) if err != nil { return err } for _, child := range n.Children { if err := t.constructInitialNodes(child); err != nil { return err } } return nil } func nodeLevel(path string) int { return len(strings.Split(path, "/")) - 1 } ================================================ FILE: common/mapq/tree/queue_tree_node.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tree import ( "context" "fmt" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/mapq/dispatcher" "github.com/uber/cadence/common/mapq/types" "github.com/uber/cadence/common/metrics" ) // QueueTreeNode represents a node in the queue tree type QueueTreeNode struct { // originalLogger is the logger passed in during creation. No node specific tags are added to this logger and it should be passed to child nodes originalLogger log.Logger // logger is the logger for this node. It has the node specific tags added to it logger log.Logger scope metrics.Scope // The path to the node Path string // The partition key used by this node to decide which child to enqueue the item. // Partition key of a node is the attribute key of child node. PartitionKey string // The attribute key used to create this node by parent AttributeKey string // The attribute value used to create this node by parent AttributeVal any // The policy for this node. It's merged policy from all policies that match this node NodePolicy types.NodePolicy // Children by attribute key // "*" is a special key that represents the default/fallback child queue // If there's no children then the node is considered leaf node Children map[any]*QueueTreeNode // The dispatcher for this node. Only leaf nodes have dispatcher Dispatcher *dispatcher.Dispatcher } func (n *QueueTreeNode) Start( ctx context.Context, consumerFactory types.ConsumerFactory, partitions []string, partitionMap map[string]any, ) error { n.logger.Info("Starting node", tag.Dynamic("node", n.String())) // If there are no children then this is a leaf node if len(n.Children) == 0 { n.logger.Info("Creating consumer and starting a new dispatcher for leaf node") c, err := consumerFactory.New(types.NewItemPartitions(partitions, partitionMap)) if err != nil { return err } d := dispatcher.New(c) if err := d.Start(ctx); err != nil { return err } n.Dispatcher = d return nil } for _, child := range n.Children { partitionMap[n.PartitionKey] = child.AttributeVal err := child.Start(ctx, consumerFactory, partitions, partitionMap) if err != nil { return fmt.Errorf("failed to start child %s: %w", child.Path, err) } } n.logger.Info("Started node") return nil } func (n *QueueTreeNode) Stop(ctx context.Context) error { n.logger.Info("Stopping node") if n.Dispatcher != nil { // leaf node return n.Dispatcher.Stop(ctx) } for _, child := range n.Children { if err := child.Stop(ctx); err != nil { return fmt.Errorf("failed to stop child %s: %w", child.Path, err) } } n.logger.Info("Stopped node") return nil } func (n *QueueTreeNode) Enqueue( ctx context.Context, item types.Item, partitions []string, partitionMap map[string]any, ) (types.ItemToPersist, error) { // If there are no children then this is a leaf node if len(n.Children) == 0 { return types.NewItemToPersist(item, types.NewItemPartitions(partitions, partitionMap)), nil } // Add the attribute value to queueNodePathParts partitionVal := item.GetAttribute(n.PartitionKey) partitions = append(partitions, n.PartitionKey) partitionMap[n.PartitionKey] = partitionVal child, ok := n.Children[partitionVal] if !ok { // TODO: thread safety missing child, ok = n.Children["*"] partitionMap[n.PartitionKey] = "*" if !ok { // catch-all nodes are created during initalization so this should never happen return nil, fmt.Errorf("no child found for attribute %v in node %v", partitionVal, n.Path) } } return child.Enqueue(ctx, item, partitions, partitionMap) } func (n *QueueTreeNode) String() string { return fmt.Sprintf("QueueTreeNode{Path: %q, AttributeKey: %v, AttributeVal: %v, NodePolicy: %s, Num Children: %d}", n.Path, n.AttributeKey, n.AttributeVal, n.NodePolicy, len(n.Children)) } func (n *QueueTreeNode) Init(logger log.Logger, scope metrics.Scope, policyCol types.NodePolicyCollection, partitions []string) error { n.originalLogger = logger n.logger = logger.WithTags(tag.ComponentMapQTreeNode, tag.Dynamic("path", n.Path)) n.scope = scope // Get the merged policy for this node policy, err := policyCol.GetMergedPolicyForNode(n.Path) if err != nil { return err } n.NodePolicy = policy // Set partition key of the node nodeLevel := nodeLevel(n.Path) if nodeLevel < len(partitions) { n.PartitionKey = partitions[nodeLevel] } // Create predefined children nodes return n.addPredefinedSplits(policyCol, partitions) } func (n *QueueTreeNode) addChild(attrVal any, policyCol types.NodePolicyCollection, partitions []string) (*QueueTreeNode, error) { path := fmt.Sprintf("%s/%v", n.Path, attrVal) ch := &QueueTreeNode{ Path: path, AttributeKey: n.PartitionKey, AttributeVal: attrVal, Children: map[any]*QueueTreeNode{}, } if err := ch.Init(n.originalLogger, n.scope, policyCol, partitions); err != nil { return nil, err } n.Children[attrVal] = ch return ch, nil } func (n *QueueTreeNode) addPredefinedSplits(policyCol types.NodePolicyCollection, partitions []string) error { if n.NodePolicy.SplitPolicy == nil || len(n.NodePolicy.SplitPolicy.PredefinedSplits) == 0 { return nil } if nodeLevel(n.Path) >= len(partitions) { return fmt.Errorf("predefined split is defined for a leaf level node %s", n.Path) } for _, split := range n.NodePolicy.SplitPolicy.PredefinedSplits { _, err := n.addChild(split, policyCol, partitions) if err != nil { return err } } return nil } ================================================ FILE: common/mapq/tree/queue_tree_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tree import ( "context" "testing" "github.com/google/go-cmp/cmp" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/mapq/types" "github.com/uber/cadence/common/metrics" ) func TestStartStop(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) consumerFactory := types.NewMockConsumerFactory(ctrl) consumer := types.NewMockConsumer(ctrl) // 7 consumers are created in the test tree for each leaf node defined by getTestPolicies() // - */timer/deletehistory/* // - */timer/*/domain1 // - */timer/*/* // - */transfer/*/domain1 // - */transfer/*/* // - */*/*/domain1 // - */*/*/* consumerFactory.EXPECT().New(gomock.Any()).Return(consumer, nil).Times(7) tree, err := New( testlogger.New(t), metrics.NoopScope, []string{"type", "sub-type", "domain"}, getTestPolicies(), types.NewMockPersister(ctrl), consumerFactory, ) if err != nil { t.Fatalf("failed to create queue tree: %v", err) } if err := tree.Start(context.Background()); err != nil { t.Fatalf("failed to start queue tree: %v", err) } if err := tree.Stop(context.Background()); err != nil { t.Fatalf("failed to stop queue tree: %v", err) } } func TestEnqueue(t *testing.T) { tests := []struct { name string policies []types.NodePolicy leafNodeCount int items []types.Item persistErr error wantItemsToPersist []types.ItemToPersist wantErr bool }{ { name: "success", policies: getTestPolicies(), leafNodeCount: 7, items: []types.Item{ mockItem(t, map[string]any{"type": "timer", "sub-type": "deletehistory", "domain": "domain1"}), mockItem(t, map[string]any{"type": "timer", "sub-type": "deletehistory", "domain": "domain2"}), mockItem(t, map[string]any{"type": "timer", "sub-type": "deletehistory", "domain": "domain3"}), mockItem(t, map[string]any{"type": "timer", "sub-type": "activitytimeout", "domain": "domain1"}), mockItem(t, map[string]any{"type": "timer", "sub-type": "activitytimeout", "domain": "domain2"}), mockItem(t, map[string]any{"type": "timer", "sub-type": "activitytimeout", "domain": "domain3"}), mockItem(t, map[string]any{"type": "transfer", "sub-type": "activity", "domain": "domain1"}), mockItem(t, map[string]any{"type": "transfer", "sub-type": "activity", "domain": "domain2"}), mockItem(t, map[string]any{"type": "transfer", "sub-type": "activity", "domain": "domain3"}), }, wantItemsToPersist: func() []types.ItemToPersist { result := make([]types.ItemToPersist, 9) partitions := []string{"type", "sub-type", "domain"} // deletehistory timers should go to the leaf node "*/timer/deletehistory/*" result[0] = types.NewItemToPersist( mockItem(t, map[string]any{"type": "timer", "sub-type": "deletehistory", "domain": "domain1"}), types.NewItemPartitions(partitions, map[string]any{"type": "timer", "sub-type": "deletehistory", "domain": "*"}), ) result[1] = types.NewItemToPersist( mockItem(t, map[string]any{"type": "timer", "sub-type": "deletehistory", "domain": "domain2"}), types.NewItemPartitions(partitions, map[string]any{"type": "timer", "sub-type": "deletehistory", "domain": "*"}), ) result[2] = types.NewItemToPersist( mockItem(t, map[string]any{"type": "timer", "sub-type": "deletehistory", "domain": "domain3"}), types.NewItemPartitions(partitions, map[string]any{"type": "timer", "sub-type": "deletehistory", "domain": "*"}), ) // activitytimeout timers either goes to domain1 specific leaf node ("*/timer/*/domain1") or the catch all leaf node "*/timer/*/*" result[3] = types.NewItemToPersist( mockItem(t, map[string]any{"type": "timer", "sub-type": "activitytimeout", "domain": "domain1"}), types.NewItemPartitions(partitions, map[string]any{"type": "timer", "sub-type": "*", "domain": "domain1"}), ) result[4] = types.NewItemToPersist( mockItem(t, map[string]any{"type": "timer", "sub-type": "activitytimeout", "domain": "domain2"}), types.NewItemPartitions(partitions, map[string]any{"type": "timer", "sub-type": "*", "domain": "*"}), ) result[5] = types.NewItemToPersist( mockItem(t, map[string]any{"type": "timer", "sub-type": "activitytimeout", "domain": "domain3"}), types.NewItemPartitions(partitions, map[string]any{"type": "timer", "sub-type": "*", "domain": "*"}), ) // transfer tasks either goes to domain1 specific leaf node ("*/transfer/*/domain1") or the catch all leaf node "*/transfer/*/*" result[6] = types.NewItemToPersist( mockItem(t, map[string]any{"type": "transfer", "sub-type": "activity", "domain": "domain1"}), types.NewItemPartitions(partitions, map[string]any{"type": "transfer", "sub-type": "*", "domain": "domain1"}), ) result[7] = types.NewItemToPersist( mockItem(t, map[string]any{"type": "transfer", "sub-type": "activity", "domain": "domain2"}), types.NewItemPartitions(partitions, map[string]any{"type": "transfer", "sub-type": "*", "domain": "*"}), ) result[8] = types.NewItemToPersist( mockItem(t, map[string]any{"type": "transfer", "sub-type": "activity", "domain": "domain3"}), types.NewItemPartitions(partitions, map[string]any{"type": "transfer", "sub-type": "*", "domain": "*"}), ) return result }(), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) consumerFactory := types.NewMockConsumerFactory(ctrl) consumer := types.NewMockConsumer(ctrl) // consumers will be creeted for each leaf node consumerFactory.EXPECT().New(gomock.Any()).Return(consumer, nil).Times(tc.leafNodeCount) var gotItemsToPersistByPersister []types.ItemToPersist persister := types.NewMockPersister(ctrl) persister.EXPECT().Persist(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, itemsToPersist []types.ItemToPersist) error { gotItemsToPersistByPersister = itemsToPersist return tc.persistErr }) tree, err := New( testlogger.New(t), metrics.NoopScope, []string{"type", "sub-type", "domain"}, getTestPolicies(), persister, consumerFactory, ) if err != nil { t.Fatalf("failed to create queue tree: %v", err) } if err := tree.Start(context.Background()); err != nil { t.Fatalf("failed to start queue tree: %v", err) } defer tree.Stop(context.Background()) returnedItemsToPersist, err := tree.Enqueue(context.Background(), tc.items) if (err != nil) != tc.wantErr { t.Errorf("Enqueue() error: %v, wantErr: %v", err, tc.wantErr) } if err != nil { return } if got, want := len(gotItemsToPersistByPersister), len(tc.wantItemsToPersist); got != want { t.Fatalf("Items to persist count mismatch: gotItemsToPersistByPersister=%v, want=%v", got, want) } if got, want := len(returnedItemsToPersist), len(tc.wantItemsToPersist); got != want { t.Fatalf("Items to persist count mismatch: returnedItemsToPersist=%v, want=%v", got, want) } for i := range gotItemsToPersistByPersister { if diff := cmp.Diff(tc.wantItemsToPersist[i].String(), gotItemsToPersistByPersister[i].String()); diff != "" { t.Errorf("%d - gotItemsToPersistByPersister mismatch (-want +got):\n%s", i, diff) } if diff := cmp.Diff(tc.wantItemsToPersist[i].String(), returnedItemsToPersist[i].String()); diff != "" { t.Errorf("%d - returnedItemsToPersist mismatch (-want +got):\n%s", i, diff) } } }) } } func mockItem(t *testing.T, attributes map[string]any) types.Item { item := types.NewMockItem(gomock.NewController(t)) item.EXPECT().GetAttribute(gomock.Any()).DoAndReturn(func(key string) any { return attributes[key] }).AnyTimes() item.EXPECT().String().Return("mockitem").AnyTimes() return item } func getTestPolicies() []types.NodePolicy { return []types.NodePolicy{ { Path: "*", // level 0 SplitPolicy: &types.SplitPolicy{ PredefinedSplits: []any{"timer", "transfer"}, }, DispatchPolicy: &types.DispatchPolicy{DispatchRPS: 100}, }, { Path: "*/.", // level 1 default policy SplitPolicy: &types.SplitPolicy{}, DispatchPolicy: &types.DispatchPolicy{DispatchRPS: 50}, }, { Path: "*/timer", // level 1 timer node SplitPolicy: &types.SplitPolicy{ PredefinedSplits: []any{"deletehistory"}, }, }, { Path: "*/./.", // level 2 default policy SplitPolicy: &types.SplitPolicy{ PredefinedSplits: []any{"domain1"}, }, }, { Path: "*/timer/deletehistory", // level 2 deletehistory timer node policy SplitPolicy: &types.SplitPolicy{ Disabled: true, }, DispatchPolicy: &types.DispatchPolicy{DispatchRPS: 5}, }, { Path: "*/././*", // level 3 default catch-all node policy SplitPolicy: &types.SplitPolicy{ Disabled: true, }, DispatchPolicy: &types.DispatchPolicy{DispatchRPS: 1000}, }, { Path: "*/././domain1", // level 3 domain node policy SplitPolicy: &types.SplitPolicy{ Disabled: true, }, DispatchPolicy: &types.DispatchPolicy{DispatchRPS: 42}, }, } } ================================================ FILE: common/mapq/types/client.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "context" ) type Client interface { // Enqueue adds an item to the queue Enqueue(context.Context, []Item) ([]ItemToPersist, error) // Ack marks the item as processed. // Out of order acks are supported. // Acking an item that has not been dequeued will have no effect. // Queue's committed offset is updated to the last ack'ed item's offset periodically until there's a gap (un-acked item). // In other words, all items up to the committed offset are ack'ed. // Ack'ed item might still be returned from Dequeue if its offset is higher than last committed offset before process restarts. Ack(context.Context, Item) error // Nack negatively acknowledges an item in the queue // Nack'ing an already ack'ed item will have no effect. Nack(context.Context, Item) error // Start the client. It will // - fetch the last committed offsets from the persister, // - start corresponding consumers // - dispatch items starting from those offsets. Start(context.Context) error // Stop the client. It will // - stop all consumers // - persist the last committed offsets Stop(context.Context) error } ================================================ FILE: common/mapq/types/consumer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import "context" //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination consumer_mock.go -package types github.com/uber/cadence/common/mapq/types ConsumerFactory,Consumer type ConsumerFactory interface { // New creates a new consumer with the given partitions or returns an existing consumer // to process the given partitions // Consumer lifecycle is managed by the factory so the returned consumer must be started. New(ItemPartitions) (Consumer, error) // Stop stops all consumers created by this factory Stop(context.Context) error } type Consumer interface { Start(context.Context) error Stop(context.Context) error Process(context.Context, Item) error } ================================================ FILE: common/mapq/types/consumer_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: consumer.go // // Generated by this command: // // mockgen -package types -source consumer.go -destination consumer_mock.go -package types github.com/uber/cadence/common/mapq/types ConsumerFactory,Consumer // // Package types is a generated GoMock package. package types import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockConsumerFactory is a mock of ConsumerFactory interface. type MockConsumerFactory struct { ctrl *gomock.Controller recorder *MockConsumerFactoryMockRecorder isgomock struct{} } // MockConsumerFactoryMockRecorder is the mock recorder for MockConsumerFactory. type MockConsumerFactoryMockRecorder struct { mock *MockConsumerFactory } // NewMockConsumerFactory creates a new mock instance. func NewMockConsumerFactory(ctrl *gomock.Controller) *MockConsumerFactory { mock := &MockConsumerFactory{ctrl: ctrl} mock.recorder = &MockConsumerFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockConsumerFactory) EXPECT() *MockConsumerFactoryMockRecorder { return m.recorder } // New mocks base method. func (m *MockConsumerFactory) New(arg0 ItemPartitions) (Consumer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "New", arg0) ret0, _ := ret[0].(Consumer) ret1, _ := ret[1].(error) return ret0, ret1 } // New indicates an expected call of New. func (mr *MockConsumerFactoryMockRecorder) New(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "New", reflect.TypeOf((*MockConsumerFactory)(nil).New), arg0) } // Stop mocks base method. func (m *MockConsumerFactory) Stop(arg0 context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Stop", arg0) ret0, _ := ret[0].(error) return ret0 } // Stop indicates an expected call of Stop. func (mr *MockConsumerFactoryMockRecorder) Stop(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockConsumerFactory)(nil).Stop), arg0) } // MockConsumer is a mock of Consumer interface. type MockConsumer struct { ctrl *gomock.Controller recorder *MockConsumerMockRecorder isgomock struct{} } // MockConsumerMockRecorder is the mock recorder for MockConsumer. type MockConsumerMockRecorder struct { mock *MockConsumer } // NewMockConsumer creates a new mock instance. func NewMockConsumer(ctrl *gomock.Controller) *MockConsumer { mock := &MockConsumer{ctrl: ctrl} mock.recorder = &MockConsumerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockConsumer) EXPECT() *MockConsumerMockRecorder { return m.recorder } // Process mocks base method. func (m *MockConsumer) Process(arg0 context.Context, arg1 Item) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Process", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // Process indicates an expected call of Process. func (mr *MockConsumerMockRecorder) Process(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Process", reflect.TypeOf((*MockConsumer)(nil).Process), arg0, arg1) } // Start mocks base method. func (m *MockConsumer) Start(arg0 context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start", arg0) ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockConsumerMockRecorder) Start(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockConsumer)(nil).Start), arg0) } // Stop mocks base method. func (m *MockConsumer) Stop(arg0 context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Stop", arg0) ret0, _ := ret[0].(error) return ret0 } // Stop indicates an expected call of Stop. func (mr *MockConsumerMockRecorder) Stop(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockConsumer)(nil).Stop), arg0) } ================================================ FILE: common/mapq/types/item.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination item_mock.go -package types github.com/uber/cadence/common/mapq/types Item import "fmt" type Item interface { // GetAttribute returns the value of the attribute key. // Value can be any type. It's up to the client to interpret the value. // Attribute keys used as partition keys will be converted to string because they are used as node // identifiers in the queue tree. GetAttribute(key string) any // Offset returns the offset of the item in the queue. e.g. monotonically increasing sequence number or a timestamp Offset() int64 // String returns a human friendly representation of the item for logging purposes String() string } type ItemToPersist interface { Item ItemPartitions } func NewItemToPersist(item Item, itemPartitions ItemPartitions) ItemToPersist { return &defaultItemToPersist{ item: item, itemPartitions: itemPartitions, } } type ItemPartitions interface { // GetPartitionKeys returns the partition keys ordered by their level in the tree GetPartitionKeys() []string // GetPartitionValue returns the partition value to determine the target queue // e.g. // Below example demonstrates that item is in a catch-all queue for sub-type // Item.GetAttribute("sub-type") returns "timer" // ItemPartitions.GetPartitionValue("sub-type") returns "*" // GetPartitionValue(key string) any // String returns a human friendly representation of the item for logging purposes String() string } func NewItemPartitions(partitionKeys []string, partitionMap map[string]any) ItemPartitions { return &defaultItemPartitions{ partitionKeys: partitionKeys, partitionMap: partitionMap, } } type defaultItemPartitions struct { partitionKeys []string partitionMap map[string]any } func (i *defaultItemPartitions) GetPartitionKeys() []string { return i.partitionKeys } func (i *defaultItemPartitions) GetPartitionValue(key string) any { return i.partitionMap[key] } func (i *defaultItemPartitions) String() string { return fmt.Sprintf("ItemPartitions{partitionKeys:%v, partitionMap:%v}", i.partitionKeys, i.partitionMap) } type defaultItemToPersist struct { item Item itemPartitions ItemPartitions } func (i *defaultItemToPersist) String() string { return fmt.Sprintf("ItemToPersist{item:%v, itemPartitions:%v}", i.item, i.itemPartitions) } func (i *defaultItemToPersist) Offset() int64 { return i.item.Offset() } func (i *defaultItemToPersist) GetAttribute(key string) any { return i.item.GetAttribute(key) } func (i *defaultItemToPersist) GetPartitionKeys() []string { return i.itemPartitions.GetPartitionKeys() } func (i *defaultItemToPersist) GetPartitionValue(key string) any { return i.itemPartitions.GetPartitionValue(key) } ================================================ FILE: common/mapq/types/item_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: item.go // // Generated by this command: // // mockgen -package types -source item.go -destination item_mock.go -package types github.com/uber/cadence/common/mapq/types Item // // Package types is a generated GoMock package. package types import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockItem is a mock of Item interface. type MockItem struct { ctrl *gomock.Controller recorder *MockItemMockRecorder isgomock struct{} } // MockItemMockRecorder is the mock recorder for MockItem. type MockItemMockRecorder struct { mock *MockItem } // NewMockItem creates a new mock instance. func NewMockItem(ctrl *gomock.Controller) *MockItem { mock := &MockItem{ctrl: ctrl} mock.recorder = &MockItemMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockItem) EXPECT() *MockItemMockRecorder { return m.recorder } // GetAttribute mocks base method. func (m *MockItem) GetAttribute(key string) any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAttribute", key) ret0, _ := ret[0].(any) return ret0 } // GetAttribute indicates an expected call of GetAttribute. func (mr *MockItemMockRecorder) GetAttribute(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttribute", reflect.TypeOf((*MockItem)(nil).GetAttribute), key) } // Offset mocks base method. func (m *MockItem) Offset() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Offset") ret0, _ := ret[0].(int64) return ret0 } // Offset indicates an expected call of Offset. func (mr *MockItemMockRecorder) Offset() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Offset", reflect.TypeOf((*MockItem)(nil).Offset)) } // String mocks base method. func (m *MockItem) String() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "String") ret0, _ := ret[0].(string) return ret0 } // String indicates an expected call of String. func (mr *MockItemMockRecorder) String() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockItem)(nil).String)) } // MockItemToPersist is a mock of ItemToPersist interface. type MockItemToPersist struct { ctrl *gomock.Controller recorder *MockItemToPersistMockRecorder isgomock struct{} } // MockItemToPersistMockRecorder is the mock recorder for MockItemToPersist. type MockItemToPersistMockRecorder struct { mock *MockItemToPersist } // NewMockItemToPersist creates a new mock instance. func NewMockItemToPersist(ctrl *gomock.Controller) *MockItemToPersist { mock := &MockItemToPersist{ctrl: ctrl} mock.recorder = &MockItemToPersistMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockItemToPersist) EXPECT() *MockItemToPersistMockRecorder { return m.recorder } // GetAttribute mocks base method. func (m *MockItemToPersist) GetAttribute(key string) any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAttribute", key) ret0, _ := ret[0].(any) return ret0 } // GetAttribute indicates an expected call of GetAttribute. func (mr *MockItemToPersistMockRecorder) GetAttribute(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttribute", reflect.TypeOf((*MockItemToPersist)(nil).GetAttribute), key) } // GetPartitionKeys mocks base method. func (m *MockItemToPersist) GetPartitionKeys() []string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPartitionKeys") ret0, _ := ret[0].([]string) return ret0 } // GetPartitionKeys indicates an expected call of GetPartitionKeys. func (mr *MockItemToPersistMockRecorder) GetPartitionKeys() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartitionKeys", reflect.TypeOf((*MockItemToPersist)(nil).GetPartitionKeys)) } // GetPartitionValue mocks base method. func (m *MockItemToPersist) GetPartitionValue(key string) any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPartitionValue", key) ret0, _ := ret[0].(any) return ret0 } // GetPartitionValue indicates an expected call of GetPartitionValue. func (mr *MockItemToPersistMockRecorder) GetPartitionValue(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartitionValue", reflect.TypeOf((*MockItemToPersist)(nil).GetPartitionValue), key) } // Offset mocks base method. func (m *MockItemToPersist) Offset() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Offset") ret0, _ := ret[0].(int64) return ret0 } // Offset indicates an expected call of Offset. func (mr *MockItemToPersistMockRecorder) Offset() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Offset", reflect.TypeOf((*MockItemToPersist)(nil).Offset)) } // String mocks base method. func (m *MockItemToPersist) String() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "String") ret0, _ := ret[0].(string) return ret0 } // String indicates an expected call of String. func (mr *MockItemToPersistMockRecorder) String() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockItemToPersist)(nil).String)) } // MockItemPartitions is a mock of ItemPartitions interface. type MockItemPartitions struct { ctrl *gomock.Controller recorder *MockItemPartitionsMockRecorder isgomock struct{} } // MockItemPartitionsMockRecorder is the mock recorder for MockItemPartitions. type MockItemPartitionsMockRecorder struct { mock *MockItemPartitions } // NewMockItemPartitions creates a new mock instance. func NewMockItemPartitions(ctrl *gomock.Controller) *MockItemPartitions { mock := &MockItemPartitions{ctrl: ctrl} mock.recorder = &MockItemPartitionsMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockItemPartitions) EXPECT() *MockItemPartitionsMockRecorder { return m.recorder } // GetPartitionKeys mocks base method. func (m *MockItemPartitions) GetPartitionKeys() []string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPartitionKeys") ret0, _ := ret[0].([]string) return ret0 } // GetPartitionKeys indicates an expected call of GetPartitionKeys. func (mr *MockItemPartitionsMockRecorder) GetPartitionKeys() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartitionKeys", reflect.TypeOf((*MockItemPartitions)(nil).GetPartitionKeys)) } // GetPartitionValue mocks base method. func (m *MockItemPartitions) GetPartitionValue(key string) any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPartitionValue", key) ret0, _ := ret[0].(any) return ret0 } // GetPartitionValue indicates an expected call of GetPartitionValue. func (mr *MockItemPartitionsMockRecorder) GetPartitionValue(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartitionValue", reflect.TypeOf((*MockItemPartitions)(nil).GetPartitionValue), key) } // String mocks base method. func (m *MockItemPartitions) String() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "String") ret0, _ := ret[0].(string) return ret0 } // String indicates an expected call of String. func (mr *MockItemPartitionsMockRecorder) String() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockItemPartitions)(nil).String)) } ================================================ FILE: common/mapq/types/item_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "strings" "testing" "github.com/google/go-cmp/cmp" "go.uber.org/mock/gomock" ) func TestNewItemToPersist(t *testing.T) { ctrl := gomock.NewController(t) item := NewMockItem(ctrl) itemStr := "###item###" item.EXPECT().String().Return(itemStr).Times(1) item.EXPECT().GetAttribute("attr1").Return("value1").Times(1) item.EXPECT().GetAttribute("attr2").Return("value2").Times(1) partitions := []string{"attr1", "attr2"} itemPartitions := NewItemPartitions( partitions, map[string]any{ "attr1": "*", "attr2": "value2", }, ) itemToPersist := NewItemToPersist(item, itemPartitions) if itemToPersist == nil { t.Fatal("itemToPersist is nil") } if got := itemToPersist.GetAttribute("attr1"); got != "value1" { t.Errorf("itemToPersist.GetAttribute(attr1) = %v, want %v", got, "value1") } if got := itemToPersist.GetAttribute("attr2"); got != "value2" { t.Errorf("itemToPersist.GetAttribute(attr2) = %v, want %v", got, "value2") } gotPartitions := itemToPersist.GetPartitionKeys() if diff := cmp.Diff(partitions, gotPartitions); diff != "" { t.Fatalf("Partition keys mismatch (-want +got):\n%s", diff) } if got := itemToPersist.GetPartitionValue("attr1"); got != "*" { t.Errorf("itemToPersist.GetPartitionValue(attr1) = %v, want %v", got, "*") } if got := itemToPersist.GetPartitionValue("attr2"); got != "value2" { t.Errorf("itemToPersist.GetPartitionValue(attr2) = %v, want %v", got, "value2") } itemToPersistStr := itemToPersist.String() itemPartitionsStr := itemPartitions.String() if !strings.Contains(itemToPersistStr, itemPartitionsStr) { t.Errorf("itemToPersist.String() = %v, want to contain %v", itemToPersistStr, itemPartitionsStr) } if !strings.Contains(itemToPersistStr, itemStr) { t.Errorf("itemToPersist.String() = %v, want to contain %v", itemToPersistStr, itemStr) } } ================================================ FILE: common/mapq/types/offsets.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types // Offsets encapsulates the whole queue tree state including the offsets of each leaf node type Offsets struct { // TODO: complete } ================================================ FILE: common/mapq/types/persister.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import "context" //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination persister_mock.go -package types github.com/uber/cadence/common/mapq/types Persister type Persister interface { Persist(ctx context.Context, items []ItemToPersist) error GetOffsets(ctx context.Context) (*Offsets, error) CommitOffsets(ctx context.Context, offsets *Offsets) error Fetch(ctx context.Context, partitions ItemPartitions, pageInfo PageInfo) ([]Item, error) } type PageInfo struct { // TODO: define ack levels, page size, etc. } ================================================ FILE: common/mapq/types/persister_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: persister.go // // Generated by this command: // // mockgen -package types -source persister.go -destination persister_mock.go -package types github.com/uber/cadence/common/mapq/types Persister // // Package types is a generated GoMock package. package types import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockPersister is a mock of Persister interface. type MockPersister struct { ctrl *gomock.Controller recorder *MockPersisterMockRecorder isgomock struct{} } // MockPersisterMockRecorder is the mock recorder for MockPersister. type MockPersisterMockRecorder struct { mock *MockPersister } // NewMockPersister creates a new mock instance. func NewMockPersister(ctrl *gomock.Controller) *MockPersister { mock := &MockPersister{ctrl: ctrl} mock.recorder = &MockPersisterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPersister) EXPECT() *MockPersisterMockRecorder { return m.recorder } // CommitOffsets mocks base method. func (m *MockPersister) CommitOffsets(ctx context.Context, offsets *Offsets) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CommitOffsets", ctx, offsets) ret0, _ := ret[0].(error) return ret0 } // CommitOffsets indicates an expected call of CommitOffsets. func (mr *MockPersisterMockRecorder) CommitOffsets(ctx, offsets any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitOffsets", reflect.TypeOf((*MockPersister)(nil).CommitOffsets), ctx, offsets) } // Fetch mocks base method. func (m *MockPersister) Fetch(ctx context.Context, partitions ItemPartitions, pageInfo PageInfo) ([]Item, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Fetch", ctx, partitions, pageInfo) ret0, _ := ret[0].([]Item) ret1, _ := ret[1].(error) return ret0, ret1 } // Fetch indicates an expected call of Fetch. func (mr *MockPersisterMockRecorder) Fetch(ctx, partitions, pageInfo any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fetch", reflect.TypeOf((*MockPersister)(nil).Fetch), ctx, partitions, pageInfo) } // GetOffsets mocks base method. func (m *MockPersister) GetOffsets(ctx context.Context) (*Offsets, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOffsets", ctx) ret0, _ := ret[0].(*Offsets) ret1, _ := ret[1].(error) return ret0, ret1 } // GetOffsets indicates an expected call of GetOffsets. func (mr *MockPersisterMockRecorder) GetOffsets(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOffsets", reflect.TypeOf((*MockPersister)(nil).GetOffsets), ctx) } // Persist mocks base method. func (m *MockPersister) Persist(ctx context.Context, items []ItemToPersist) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Persist", ctx, items) ret0, _ := ret[0].(error) return ret0 } // Persist indicates an expected call of Persist. func (mr *MockPersisterMockRecorder) Persist(ctx, items any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Persist", reflect.TypeOf((*MockPersister)(nil).Persist), ctx, items) } ================================================ FILE: common/mapq/types/policy.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "encoding/json" "fmt" ) type DispatchPolicy struct { // DispatchRPS is the rate limit for items dequeued from the node to be pushed to processors. // All nodes inherit the DispatchRPS from the parent node as is (not distributed to children). // If parent has 100 rps limit, then all curent and to-be-created children will have 100 rps limit. DispatchRPS int64 `json:"dispatchRPS,omitempty"` // Concurrency is the maximum number of items to be processed concurrently. Concurrency int `json:"concurrency,omitempty"` // TODO: define retry policy } func (dp DispatchPolicy) String() string { return fmt.Sprintf("DispatchPolicy{DispatchRPS:%d, Concurrency:%d}", dp.DispatchRPS, dp.Concurrency) } type SplitPolicy struct { // Disabled is used to disable the split policy for the node. Disabled bool `json:"disabled,omitempty"` // PredefinedSplits is a list of predefined splits for the attribute key // Child nodes for these attributes will be created during initialization PredefinedSplits []any `json:"predefinedSplits,omitempty"` } func (sp SplitPolicy) String() string { return fmt.Sprintf("SplitPolicy{Disabled:%v, PredefinedSplits:%v}", sp.Disabled, sp.PredefinedSplits) } type NodePolicy struct { // The path to the node // Root node has empty path "". // "/" is used as path separator. // "*" means the policy applies to the special catch-all node // "." means the policy applies to all nodes in the specified level except the catch-all node Path string `json:"path,omitempty"` SplitPolicy *SplitPolicy `json:"splitPolicy,omitempty"` // DispatchPolicy is enforced at the leaf node level. DispatchPolicy *DispatchPolicy `json:"dispatchPolicy,omitempty"` } // Merge merges two NodePolicy objects by marshalling/unmarshalling them. // Any field in the other policy will override the field in the current policy. func (np NodePolicy) Merge(other NodePolicy) (NodePolicy, error) { marshalled1, err := json.Marshal(np) if err != nil { return NodePolicy{}, err } var m1 map[string]any err = json.Unmarshal(marshalled1, &m1) if err != nil { return NodePolicy{}, err } marshalled2, err := json.Marshal(other) if err != nil { return NodePolicy{}, err } var m2 map[string]any err = json.Unmarshal(marshalled2, &m2) if err != nil { return NodePolicy{}, err } for k, v2 := range m2 { m1[k] = v2 } mergedMarshalled, err := json.Marshal(m1) if err != nil { return NodePolicy{}, err } var merged NodePolicy err = json.Unmarshal(mergedMarshalled, &merged) if err != nil { return NodePolicy{}, err } return merged, nil } func (np NodePolicy) String() string { return fmt.Sprintf("NodePolicy{Path:%v, DispatchPolicy:%s, SplitPolicy:%s}", np.Path, np.DispatchPolicy, np.SplitPolicy) } ================================================ FILE: common/mapq/types/policy_collection.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "fmt" "sort" "strings" ) type NodePolicyCollection struct { // policies is sorted by level and generic to special policies []NodePolicy } func NewNodePolicyCollection(policies []NodePolicy) NodePolicyCollection { trimPaths(policies) sortPolicies(policies) return NodePolicyCollection{ policies: policies, } } func (npc NodePolicyCollection) GetMergedPolicyForNode(path string) (NodePolicy, error) { result := NodePolicy{} for _, policy := range npc.policies { pathParts := strings.Split(path, "/") plcPathParts := strings.Split(policy.Path, "/") length := len(pathParts) if len(plcPathParts) <= length { length = len(plcPathParts) } else { // if policy path is longer than the node path then it can't be a match continue } match := true for i := 0; i < length; i++ { pathPart := pathParts[i] plcPathPart := plcPathParts[i] if pathPart == "." || plcPathPart == "." || pathPart == plcPathPart { continue } match = false break } if !match { continue } var err error result, err = result.Merge(policy) if err != nil { return result, fmt.Errorf("failed to merge policies: %w", err) } } // set the path in the result result.Path = path return result, nil } func (npc NodePolicyCollection) GetPolicies() []NodePolicy { // return a copy of the slice so the order is not messed up policies := make([]NodePolicy, len(npc.policies)) copy(policies, npc.policies) return policies } func trimPaths(policies []NodePolicy) { for i := range policies { policies[i].Path = strings.TrimRight(policies[i].Path, "/") } } func sortPolicies(policies []NodePolicy) { // sort policies by level and generic to special sort.Slice(policies, func(i, j int) bool { parts1 := strings.Split(policies[i].Path, "/") parts2 := strings.Split(policies[j].Path, "/") l1 := len(parts1) l2 := len(parts2) if l1 != l2 { return l1 < l2 } pathPartPrecedence := map[any]int{ ".": 0, "*": 1, } for partIdx := 0; partIdx < l1; partIdx++ { p1, ok := pathPartPrecedence[parts1[partIdx]] if !ok { p1 = 2 } p2, ok := pathPartPrecedence[parts2[partIdx]] if !ok { p2 = 2 } if p1 != p2 { return p1 < p2 } } // If parts have same precedence then just return the order return i < j }) } ================================================ FILE: common/mapq/types/policy_collection_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "math/rand" "testing" "time" "github.com/google/go-cmp/cmp" ) const ( fuzzCustomSeed = 0 ) func TestGetPolicies(t *testing.T) { policies := getTestPolicies() wantOrderedPaths := make([]string, len(policies)) for i, p := range policies { wantOrderedPaths[i] = p.Path } shufflePolicies(policies) npc := NewNodePolicyCollection(policies) gotPolicies := npc.GetPolicies() if len(gotPolicies) != len(policies) { t.Fatalf("Policies count mismatch, got %v, want %v", len(gotPolicies), len(policies)) } gotPaths := make([]string, len(policies)) for i, p := range gotPolicies { t.Logf("%d - %s", i, p) gotPaths[i] = p.Path } if diff := cmp.Diff(wantOrderedPaths, gotPaths); diff != "" { t.Fatalf("Policies not sorted as expected (-want +got):\n%s", diff) } } func TestGetMergedPolicyforNode(t *testing.T) { npc := NewNodePolicyCollection(getTestPolicies()) tests := []struct { name string path string want NodePolicy }{ { name: "root node", path: "*", want: NodePolicy{ Path: "*", SplitPolicy: &SplitPolicy{ PredefinedSplits: []any{"timer", "transfer"}, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 100}, }, }, { name: "level 1 catch-all node", path: "*/*", want: NodePolicy{ Path: "*/*", SplitPolicy: &SplitPolicy{}, DispatchPolicy: &DispatchPolicy{DispatchRPS: 50}, }, }, { name: "level 1 timer node", path: "*/timer", want: NodePolicy{ Path: "*/timer", SplitPolicy: &SplitPolicy{ PredefinedSplits: []any{"deletehistory"}, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 50}, }, }, { name: "level 2 catch all node", path: "*/./*", want: NodePolicy{ Path: "*/./*", SplitPolicy: &SplitPolicy{ PredefinedSplits: []any{"domain1"}, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 50}, }, }, { name: "level 2 deletehistory timer node", path: "*/timer/deletehistory", want: NodePolicy{ Path: "*/timer/deletehistory", SplitPolicy: &SplitPolicy{ Disabled: true, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 5}, }, }, { name: "level 3 catch-all node", path: "*/*/*/*", want: NodePolicy{ Path: "*/*/*/*", SplitPolicy: &SplitPolicy{ Disabled: true, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 1000}, }, }, { name: "level 3 domain1 node for activitytimeout", path: "*/timer/activitytimeout/domain1", want: NodePolicy{ Path: "*/timer/activitytimeout/domain1", SplitPolicy: &SplitPolicy{ Disabled: true, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 42}, }, }, { name: "level 3 domain1 node for childwfcompleted", path: "*/transfer/childwfcompleted/domain1", want: NodePolicy{ Path: "*/transfer/childwfcompleted/domain1", SplitPolicy: &SplitPolicy{ Disabled: true, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 42}, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got, err := npc.GetMergedPolicyForNode(tc.path) if err != nil { t.Fatalf("failed to get merged policy for node %v: %v", tc.path, err) } if diff := cmp.Diff(tc.want, got); diff != "" { t.Fatalf("Policy mismatch (-want +got):\n%s", diff) } }) } } // getTestPolicies returns a set of test policies for testing // It intentionally returns the policies in a sorted order to test the sorting logic func getTestPolicies() []NodePolicy { return []NodePolicy{ { Path: "*", // level 0 SplitPolicy: &SplitPolicy{ PredefinedSplits: []any{"timer", "transfer"}, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 100}, }, { Path: "*/.", // level 1 default policy SplitPolicy: &SplitPolicy{}, DispatchPolicy: &DispatchPolicy{DispatchRPS: 50}, }, { Path: "*/timer", // level 1 timer node SplitPolicy: &SplitPolicy{ PredefinedSplits: []any{"deletehistory"}, }, }, { Path: "*/./.", // level 2 default policy SplitPolicy: &SplitPolicy{ PredefinedSplits: []any{"domain1"}, }, }, { Path: "*/timer/deletehistory", // level 2 deletehistory timer node policy SplitPolicy: &SplitPolicy{ Disabled: true, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 5}, }, { Path: "*/././*", // level 3 default catch-all node policy SplitPolicy: &SplitPolicy{ Disabled: true, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 1000}, }, { Path: "*/././domain1", // level 3 domain node policy SplitPolicy: &SplitPolicy{ Disabled: true, }, DispatchPolicy: &DispatchPolicy{DispatchRPS: 42}, }, } } func shufflePolicies(policies []NodePolicy) { seed := time.Now().UnixNano() if fuzzCustomSeed != 0 { seed = fuzzCustomSeed // override seed to test a specific scenario } rng := rand.New(rand.NewSource(seed)) rng.Shuffle(len(policies), func(i, j int) { policies[i], policies[j] = policies[j], policies[i] }) } ================================================ FILE: common/membership/hashring.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package membership import ( "fmt" "slices" "sort" "strings" "sync" "sync/atomic" "time" "github.com/dgryski/go-farm" "github.com/uber/ringpop-go/hashring" "github.com/uber/ringpop-go/membership" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination peerprovider_mock.go github.com/uber/cadence/common/membership PeerProvider // ErrInsufficientHosts is thrown when there are not enough hosts to serve the request var ErrInsufficientHosts = &types.InternalServiceError{Message: "Not enough hosts to serve the request"} const ( minRefreshInternal = time.Second defaultRefreshInterval = 2 * time.Second replicaPoints = 100 ) // PeerProvider is used to retrieve membership information from provider type PeerProvider interface { common.Daemon GetMembers(service string) ([]HostInfo, error) WhoAmI() (HostInfo, error) SelfEvict() error Subscribe(name string, handler func(ChangedEvent)) error } type Ring struct { status int32 service string peerProvider PeerProvider refreshChan chan struct{} shutdownCh chan struct{} shutdownWG sync.WaitGroup timeSource clock.TimeSource scope metrics.Scope logger log.Logger value atomic.Value // this stores the current hashring members struct { sync.RWMutex refreshed time.Time keys map[string]HostInfo // for mapping ip:port to HostInfo } subscribers struct { sync.RWMutex keys map[string]chan<- *ChangedEvent } } func NewHashring( service string, provider PeerProvider, timeSource clock.TimeSource, logger log.Logger, scope metrics.Scope, ) *Ring { r := &Ring{ status: common.DaemonStatusInitialized, service: service, peerProvider: provider, shutdownCh: make(chan struct{}), refreshChan: make(chan struct{}, 1), timeSource: timeSource, logger: logger.WithTags(tag.ComponentHashring), scope: scope, } r.members.keys = make(map[string]HostInfo) r.subscribers.keys = make(map[string]chan<- *ChangedEvent) r.value.Store(emptyHashring()) return r } func emptyHashring() *hashring.HashRing { return hashring.New(farm.Fingerprint32, replicaPoints) } // Start starts the hashring func (r *Ring) Start() { if !atomic.CompareAndSwapInt32( &r.status, common.DaemonStatusInitialized, common.DaemonStatusStarted, ) { return } r.logger.Info("Starting hashring", tag.ComponentHashring) defer r.logger.Info("Started hashring", tag.ComponentHashring) if err := r.peerProvider.Subscribe(r.service, r.handleUpdates); err != nil { r.logger.Fatal("subscribing to peer provider", tag.Error(err)) } if err := r.Refresh(); err != nil { r.logger.Fatal("failed to start service resolver", tag.Error(err)) } r.shutdownWG.Add(1) go r.refreshRingWorker() } // Stop stops the resolver func (r *Ring) Stop() { if !atomic.CompareAndSwapInt32( &r.status, common.DaemonStatusStarted, common.DaemonStatusStopped, ) { return } r.logger.Info("Stopping hashring", tag.ComponentHashring) defer r.logger.Info("Stopped hashring", tag.ComponentHashring) r.peerProvider.Stop() r.value.Store(emptyHashring()) r.subscribers.Lock() r.subscribers.keys = make(map[string]chan<- *ChangedEvent) r.subscribers.Unlock() close(r.shutdownCh) if success := common.AwaitWaitGroup(&r.shutdownWG, time.Minute); !success { r.logger.Warn("service resolver timed out on shutdown.") } } // LookupRaw finds the host address in the ring responsible for serving the given key // without converting it to HostInfo func (r *Ring) LookupRaw(key string) (string, error) { addr, found := r.ring().Lookup(key) if !found { r.signalSelf() return "", ErrInsufficientHosts } return addr, nil } // Lookup finds the host in the ring responsible for serving the given key func (r *Ring) Lookup(key string) (HostInfo, error) { addr, err := r.LookupRaw(key) if err != nil { return HostInfo{}, err } return r.AddressToHost(addr) } func (r *Ring) AddressToHost(addr string) (HostInfo, error) { r.members.RLock() defer r.members.RUnlock() host, ok := r.members.keys[addr] if !ok { return HostInfo{}, fmt.Errorf("host not found in member keys, host: %q", addr) } return host, nil } // Subscribe registers callback watcher. Services can use this to be informed about membership changes func (r *Ring) Subscribe(watcher string, notifyChannel chan<- *ChangedEvent) error { r.subscribers.Lock() defer r.subscribers.Unlock() _, ok := r.subscribers.keys[watcher] if ok { return fmt.Errorf("watcher %q is already subscribed", watcher) } r.subscribers.keys[watcher] = notifyChannel return nil } func (r *Ring) handleUpdates(event ChangedEvent) { r.signalSelf() } func (r *Ring) signalSelf() { var event struct{} select { case r.refreshChan <- event: default: // channel already has an event, don't block } } func (r *Ring) notifySubscribers(msg ChangedEvent) { r.subscribers.Lock() defer r.subscribers.Unlock() for name, ch := range r.subscribers.keys { select { case ch <- &msg: default: r.logger.Warn("subscriber notification failed", tag.Name(name)) } } } // Unsubscribe removes subscriber func (r *Ring) Unsubscribe(name string) error { r.subscribers.Lock() defer r.subscribers.Unlock() delete(r.subscribers.keys, name) return nil } // MemberCount returns number of hosts in a ring func (r *Ring) MemberCount() int { return r.ring().ServerCount() } func (r *Ring) Members() []HostInfo { servers := r.ring().Servers() var hosts = make([]HostInfo, 0, len(servers)) r.members.RLock() defer r.members.RUnlock() for _, s := range servers { host, ok := r.members.keys[s] if !ok { r.logger.Warn("host not found in hashring keys", tag.Address(s)) continue } hosts = append(hosts, host) } return hosts } func (r *Ring) Refresh() error { if r.members.refreshed.After(r.timeSource.Now().Add(-minRefreshInternal)) { // refreshed too frequently r.logger.Debug("refresh skipped, refreshed too frequently") return nil } members, err := r.peerProvider.GetMembers(r.service) if err != nil { return fmt.Errorf("getting members from peer provider: %w", err) } newMembersMap := r.makeMembersMap(members) diff := r.diffMembers(newMembersMap) if diff.Empty() { return nil } ring := emptyHashring() ring.AddMembers(castToMembers(members)...) r.value.Store(ring) // sort members for deterministic order in the logs sort.Slice(members, func(i, j int) bool { return members[i].addr < members[j].addr }) r.logger.Info("refreshed ring members", tag.Value(members), tag.Counter(len(members)), tag.Service(r.service)) r.updateMembersMap(newMembersMap) r.emitHashIdentifier() r.notifySubscribers(diff) r.logger.Info("notified subscribers", tag.Service(r.service)) return nil } func (r *Ring) updateMembersMap(newMembers map[string]HostInfo) { r.members.Lock() defer r.members.Unlock() r.members.keys = newMembers r.members.refreshed = r.timeSource.Now() } func (r *Ring) refreshRingWorker() { defer r.shutdownWG.Done() refreshTicker := r.timeSource.NewTicker(defaultRefreshInterval) defer refreshTicker.Stop() for { select { case <-r.shutdownCh: return case <-r.refreshChan: // local signal or signal from provider if err := r.Refresh(); err != nil { r.logger.Error("failed to refresh ring", tag.Error(err)) } case <-refreshTicker.Chan(): // periodically force refreshing membership r.signalSelf() } } } func (r *Ring) ring() *hashring.HashRing { return r.value.Load().(*hashring.HashRing) } func (r *Ring) emitHashIdentifier() float64 { members := r.Members() self, err := r.peerProvider.WhoAmI() if err != nil { r.logger.Error("Observed a problem looking up self from the membership provider while emitting hash identifier metrics", tag.Error(err)) self = HostInfo{ identity: "unknown", } } sort.Slice(members, func(i int, j int) bool { return members[i].addr > members[j].addr }) var sb strings.Builder for i := range members { sb.WriteString(members[i].addr) sb.WriteString("\n") } hashedView := farm.Hash32([]byte(sb.String())) // Trimming the metric because collisions are unlikely and I didn't want to use the full Float64 // in-case it overflowed something. The number itself is meaningless, so additional precision // doesn't really give any advantage, besides reducing the risk of collision trimmedForMetric := float64(hashedView % 1000) r.logger.Debug("Hashring view", tag.Dynamic("hashring-view", sb.String()), tag.Dynamic("trimmed-hash-id", trimmedForMetric), tag.Service(r.service), tag.Dynamic("self-addr", self.addr), tag.Dynamic("self-identity", self.identity), tag.Dynamic("self-ip", self.ip), ) r.scope.Tagged( metrics.ServiceTag(r.service), metrics.HostTag(self.identity), ).UpdateGauge(metrics.HashringViewIdentifier, trimmedForMetric) return trimmedForMetric } func (r *Ring) makeMembersMap(members []HostInfo) map[string]HostInfo { membersMap := make(map[string]HostInfo, len(members)) for _, m := range members { membersMap[m.GetAddress()] = m } return membersMap } func (r *Ring) diffMembers(newMembers map[string]HostInfo) ChangedEvent { r.members.RLock() defer r.members.RUnlock() var combinedChange ChangedEvent // find newly added hosts for addr := range newMembers { if _, found := r.members.keys[addr]; !found { combinedChange.HostsAdded = append(combinedChange.HostsAdded, addr) } } // find removed hosts for addr := range r.members.keys { if _, found := newMembers[addr]; !found { combinedChange.HostsRemoved = append(combinedChange.HostsRemoved, addr) } } // order since it will most probably used in logs slices.Sort(combinedChange.HostsAdded) slices.Sort(combinedChange.HostsUpdated) slices.Sort(combinedChange.HostsRemoved) return combinedChange } func castToMembers[T membership.Member](members []T) []membership.Member { result := make([]membership.Member, 0, len(members)) for _, h := range members { result = append(result, h) } return result } ================================================ FILE: common/membership/hashring_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package membership import ( "errors" "fmt" "math/rand" "runtime" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/mock/gomock" "go.uber.org/zap/zaptest/observer" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) var letters = []rune("abcdefghijklmnopqrstuvwxyz") const maxTestDuration = 5 * time.Second func randSeq(n int) string { b := make([]rune, n) for i := range b { b[i] = letters[rand.Intn(len(letters))] } return string(b) } func randomHostInfo(n int) []HostInfo { res := make([]HostInfo, 0, n) for i := 0; i < n; i++ { res = append(res, NewDetailedHostInfo(randSeq(5), randSeq(12), PortMap{randSeq(3): 666})) } return res } func TestDiffMemberMakesCorrectDiff(t *testing.T) { tests := []struct { name string curr []HostInfo new []HostInfo expectedChange ChangedEvent }{ { name: "empty and one added", curr: []HostInfo{}, new: []HostInfo{NewHostInfo("a")}, expectedChange: ChangedEvent{HostsAdded: []string{"a"}}, }, { name: "non-empty and added", curr: []HostInfo{NewHostInfo("a")}, new: []HostInfo{NewHostInfo("a"), NewHostInfo("b")}, expectedChange: ChangedEvent{HostsAdded: []string{"b"}}, }, { name: "empty and nothing has changed", curr: []HostInfo{}, new: []HostInfo{}, expectedChange: ChangedEvent{}, }, { name: "multiple hosts, but no change", curr: []HostInfo{NewHostInfo("a"), NewHostInfo("b"), NewHostInfo("c")}, new: []HostInfo{NewHostInfo("c"), NewHostInfo("b"), NewHostInfo("a")}, expectedChange: ChangedEvent{}, }, { name: "multiple hosts, add/delete", curr: []HostInfo{NewHostInfo("a"), NewHostInfo("b"), NewHostInfo("c")}, new: []HostInfo{NewHostInfo("b"), NewHostInfo("e"), NewHostInfo("f")}, expectedChange: ChangedEvent{HostsRemoved: []string{"a", "c"}, HostsAdded: []string{"e", "f"}}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Ring{} currMembers := r.makeMembersMap(tt.curr) r.members.keys = currMembers combinedChange := r.diffMembers(r.makeMembersMap(tt.new)) assert.Equal(t, tt.expectedChange, combinedChange) }) } } type hashringTestData struct { t *testing.T mockPeerProvider *MockPeerProvider mockTimeSource clock.MockedTimeSource hashRing *Ring observedLogs *observer.ObservedLogs } func newHashringTestData(t *testing.T) *hashringTestData { var td hashringTestData ctrl := gomock.NewController(t) td.t = t td.mockPeerProvider = NewMockPeerProvider(ctrl) td.mockTimeSource = clock.NewMockedTimeSourceAt(time.Now()) logger, observedLogs := testlogger.NewObserved(t) td.observedLogs = observedLogs td.hashRing = NewHashring( "test-service", td.mockPeerProvider, td.mockTimeSource, logger, metrics.NoopScope, ) return &td } // starts hashring' background work and verifies all the goroutines closed at the end func (td *hashringTestData) startHashRing() { td.mockPeerProvider.EXPECT().Stop() td.t.Cleanup(func() { td.hashRing.Stop() goleak.VerifyNone(td.t) }) td.hashRing.Start() } func (td *hashringTestData) bypassRefreshRatelimiter() { td.hashRing.members.refreshed = time.Now().AddDate(0, 0, -1) } func TestFailedLookupWillAskProvider(t *testing.T) { td := newHashringTestData(t) var wg sync.WaitGroup wg.Add(2) td.mockPeerProvider.EXPECT().Subscribe(gomock.Any(), gomock.Any()).Times(1) td.mockPeerProvider.EXPECT().GetMembers("test-service"). Do(func(string) { // we expect first call on hashring creation // the second call should be initiated by failed Lookup wg.Done() }).Times(2) td.startHashRing() _, err := td.hashRing.Lookup("a") assert.Error(t, err) require.True(t, common.AwaitWaitGroup(&wg, maxTestDuration), "Failed Lookup should lead to refresh") } func TestFailingToSubscribeIsFatal(t *testing.T) { defer goleak.VerifyNone(t) td := newHashringTestData(t) // we need to intercept logger calls, use mock mockLogger := log.NewMockLogger(gomock.NewController(t)) td.hashRing.logger = mockLogger mockLogger.EXPECT().Fatal(gomock.Any(), gomock.Any()).Do( func(msg string, args ...any) { // we need to stop goroutine like log.Fatal() does with an entire program runtime.Goexit() }, ).Times(1) mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).Times(2) td.mockPeerProvider.EXPECT(). Subscribe(gomock.Any(), gomock.Any()). Return(errors.New("can't subscribe")) // because we use runtime.Goexit() we need to call .Start in a separate goroutine var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() td.hashRing.Start() }() require.True(t, common.AwaitWaitGroup(&wg, maxTestDuration), "must be finished - failed to subscribe") } func TestHandleUpdatesNeverBlocks(t *testing.T) { td := newHashringTestData(t) var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { td.hashRing.handleUpdates(ChangedEvent{}) wg.Done() }() } require.True(t, common.AwaitWaitGroup(&wg, maxTestDuration), "handleUpdates should never block") } func TestHandlerSchedulesUpdates(t *testing.T) { td := newHashringTestData(t) var wg sync.WaitGroup td.mockPeerProvider.EXPECT().Subscribe(gomock.Any(), gomock.Any()).Times(1) td.mockPeerProvider.EXPECT().GetMembers("test-service").DoAndReturn(func(service string) ([]HostInfo, error) { wg.Done() fmt.Println("GetMembers called") return randomHostInfo(5), nil }).Times(2) td.mockPeerProvider.EXPECT().WhoAmI().AnyTimes() wg.Add(1) // we expect 1st GetMembers to be called during hashring start td.startHashRing() require.True(t, common.AwaitWaitGroup(&wg, maxTestDuration), "GetMembers must be called") wg.Add(1) // another call to GetMembers should happen because of handleUpdate td.bypassRefreshRatelimiter() td.hashRing.handleUpdates(ChangedEvent{}) require.True(t, common.AwaitWaitGroup(&wg, maxTestDuration), "GetMembers must be called again") } func TestFailedRefreshLogsError(t *testing.T) { td := newHashringTestData(t) var wg sync.WaitGroup td.mockPeerProvider.EXPECT().Subscribe(gomock.Any(), gomock.Any()).Times(1) td.mockPeerProvider.EXPECT().GetMembers("test-service").DoAndReturn(func(service string) ([]HostInfo, error) { wg.Done() return randomHostInfo(5), nil }).Times(1) td.mockPeerProvider.EXPECT().WhoAmI().AnyTimes() wg.Add(1) // we expect 1st GetMembers to be called during hashring start td.startHashRing() require.True(t, common.AwaitWaitGroup(&wg, maxTestDuration), "GetMembers must be called") td.mockPeerProvider.EXPECT().GetMembers("test-service").DoAndReturn(func(service string) ([]HostInfo, error) { wg.Done() return nil, errors.New("GetMembers failed") }).Times(1) wg.Add(1) // another call to GetMembers should happen because of handleUpdate td.bypassRefreshRatelimiter() td.hashRing.handleUpdates(ChangedEvent{}) require.True(t, common.AwaitWaitGroup(&wg, maxTestDuration), "GetMembers must be called again (and fail)") td.hashRing.Stop() assert.Equal(t, 1, td.observedLogs.FilterMessageSnippet("failed to refresh ring").Len()) } func TestRefreshUpdatesRingOnlyWhenRingHasChanged(t *testing.T) { td := newHashringTestData(t) td.mockPeerProvider.EXPECT().Subscribe(gomock.Any(), gomock.Any()).Times(1) td.mockPeerProvider.EXPECT().GetMembers("test-service").Times(1).Return(randomHostInfo(3), nil) td.mockPeerProvider.EXPECT().WhoAmI().AnyTimes() // Start will also call .Refresh() td.startHashRing() updatedAt := td.hashRing.members.refreshed assert.NoError(t, td.hashRing.Refresh()) assert.Equal(t, updatedAt, td.hashRing.members.refreshed) } func TestRefreshWillNotifySubscribers(t *testing.T) { td := newHashringTestData(t) var hostsToReturn []HostInfo td.mockPeerProvider.EXPECT().Subscribe(gomock.Any(), gomock.Any()).Times(1) td.mockPeerProvider.EXPECT().GetMembers("test-service").DoAndReturn(func(service string) ([]HostInfo, error) { hostsToReturn = randomHostInfo(5) return hostsToReturn, nil }).Times(2) td.mockPeerProvider.EXPECT().WhoAmI().AnyTimes() td.startHashRing() var changeCh = make(chan *ChangedEvent, 2) // Check if multiple subscribers will get notified assert.NoError(t, td.hashRing.Subscribe("subscriber1", changeCh)) assert.NoError(t, td.hashRing.Subscribe("subscriber2", changeCh)) wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() changedEvent := <-changeCh changedEvent2 := <-changeCh assert.NotEmpty(t, changedEvent, "changed event should never be empty") assert.NotEmpty(t, changedEvent2, "changed event should never be empty") }() td.bypassRefreshRatelimiter() td.hashRing.signalSelf() // wait until both subscribers will get notification require.True(t, common.AwaitWaitGroup(&wg, maxTestDuration)) // Test if internal members are updated assert.ElementsMatch(t, td.hashRing.Members(), hostsToReturn, "members should contain just-added nodes") } func TestSubscribersAreNotifiedPeriodically(t *testing.T) { td := newHashringTestData(t) var hostsToReturn []HostInfo td.mockPeerProvider.EXPECT().Subscribe(gomock.Any(), gomock.Any()).Times(1) td.mockPeerProvider.EXPECT().GetMembers("test-service").DoAndReturn(func(service string) ([]HostInfo, error) { // we have to change members since subscribers are only notified on change hostsToReturn = randomHostInfo(5) return hostsToReturn, nil }).Times(2) td.mockPeerProvider.EXPECT().WhoAmI().AnyTimes() td.startHashRing() var changeCh = make(chan *ChangedEvent, 1) assert.NoError(t, td.hashRing.Subscribe("subscriber1", changeCh)) wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() event := <-changeCh assert.NotEmpty(t, event, "changed event should never be empty") }() td.mockTimeSource.BlockUntil(1) // we should wait until ticker(defaultRefreshInterval) is created td.mockTimeSource.Advance(defaultRefreshInterval) // and only then to advance time require.True(t, common.AwaitWaitGroup(&wg, maxTestDuration)) // wait until subscriber will get notification // Test if internal members are updated assert.ElementsMatch(t, td.hashRing.Members(), hostsToReturn, "members should contain just-added nodes") } func TestSubscribeIgnoresDuplicates(t *testing.T) { var changeCh = make(chan *ChangedEvent) td := newHashringTestData(t) assert.NoError(t, td.hashRing.Subscribe("test-service", changeCh)) assert.Error(t, td.hashRing.Subscribe("test-service", changeCh)) assert.Equal(t, 1, len(td.hashRing.subscribers.keys)) } func TestUnsubcribeIgnoresDeletionOnEmpty(t *testing.T) { td := newHashringTestData(t) assert.Equal(t, 0, len(td.hashRing.subscribers.keys)) assert.NoError(t, td.hashRing.Unsubscribe("test-service")) assert.NoError(t, td.hashRing.Unsubscribe("test-service")) assert.NoError(t, td.hashRing.Unsubscribe("test-service")) } func TestUnsubcribeDeletes(t *testing.T) { td := newHashringTestData(t) var changeCh = make(chan *ChangedEvent) assert.Equal(t, 0, len(td.hashRing.subscribers.keys)) assert.NoError(t, td.hashRing.Subscribe("testservice1", changeCh)) assert.Equal(t, 1, len(td.hashRing.subscribers.keys)) assert.NoError(t, td.hashRing.Unsubscribe("test-service")) assert.Equal(t, 1, len(td.hashRing.subscribers.keys)) assert.NoError(t, td.hashRing.Unsubscribe("testservice1")) assert.Equal(t, 0, len(td.hashRing.subscribers.keys)) } func TestMemberCountReturnsNumber(t *testing.T) { td := newHashringTestData(t) assert.Equal(t, 0, td.hashRing.MemberCount()) ring := emptyHashring() for _, addr := range []string{"127", "128"} { host := NewHostInfo(addr) ring.AddMembers(host) } td.hashRing.value.Store(ring) assert.Equal(t, 2, td.hashRing.MemberCount()) } func TestErrorIsPropagatedWhenProviderFails(t *testing.T) { td := newHashringTestData(t) td.mockPeerProvider.EXPECT().GetMembers(gomock.Any()).Return(nil, errors.New("provider failure")) assert.ErrorContains(t, td.hashRing.Refresh(), "provider failure") } func TestStopWillStopProvider(t *testing.T) { td := newHashringTestData(t) td.mockPeerProvider.EXPECT().Stop().Times(1) td.hashRing.status = common.DaemonStatusStarted td.hashRing.Stop() } func TestLookupAndRefreshRaceCondition(t *testing.T) { td := newHashringTestData(t) var wg sync.WaitGroup td.mockPeerProvider.EXPECT().Subscribe(gomock.Any(), gomock.Any()).Times(1) td.mockPeerProvider.EXPECT().GetMembers("test-service").AnyTimes().DoAndReturn(func(service string) ([]HostInfo, error) { return randomHostInfo(5), nil }) td.mockPeerProvider.EXPECT().WhoAmI().AnyTimes() td.startHashRing() wg.Add(2) go func() { for i := 0; i < 50; i++ { _, _ = td.hashRing.Lookup("a") } wg.Done() }() go func() { for i := 0; i < 50; i++ { td.bypassRefreshRatelimiter() assert.NoError(t, td.hashRing.Refresh()) } wg.Done() }() wg.Wait() } func TestEmitHashringView(t *testing.T) { tests := map[string]struct { hosts []HostInfo lookuperr error selfInfo HostInfo selfErr error expectedResult float64 }{ "example one - sorted set 1 - the output should be some random hashed value": { hosts: []HostInfo{ {addr: "10.0.0.1:1234", ip: "10.0.0.1", identity: "host1", portMap: nil}, {addr: "10.0.0.2:1234", ip: "10.0.0.2", identity: "host2", portMap: nil}, {addr: "10.0.0.3:1234", ip: "10.0.0.3", identity: "host3", portMap: nil}, }, selfInfo: HostInfo{identity: "host123"}, expectedResult: 835.0, // the number here is meaningless }, "example one - unsorted set 1 - the order of the hosts should not matter": { hosts: []HostInfo{ {addr: "10.0.0.1:1234", ip: "10.0.0.1", identity: "host1", portMap: nil}, {addr: "10.0.0.3:1234", ip: "10.0.0.3", identity: "host3", portMap: nil}, {addr: "10.0.0.2:1234", ip: "10.0.0.2", identity: "host2", portMap: nil}, }, selfInfo: HostInfo{identity: "host123"}, expectedResult: 835.0, // the test here is that it's the same as test 1 }, "example 2 - empty set": { hosts: []HostInfo{}, selfInfo: HostInfo{identity: "host123"}, expectedResult: 242.0, // meaningless hash value }, "example 3 - nil set": { hosts: nil, selfInfo: HostInfo{identity: "host123"}, expectedResult: 242.0, // meaningless hash value }, } for testName, testInput := range tests { t.Run(testName, func(t *testing.T) { td := newHashringTestData(t) td.mockPeerProvider.EXPECT().GetMembers("test-service").DoAndReturn(func(service string) ([]HostInfo, error) { return testInput.hosts, testInput.lookuperr }) td.mockPeerProvider.EXPECT().WhoAmI().DoAndReturn(func() (HostInfo, error) { return testInput.selfInfo, testInput.selfErr }).AnyTimes() require.NoError(t, td.hashRing.Refresh()) assert.Equal(t, testInput.expectedResult, td.hashRing.emitHashIdentifier()) }) } } ================================================ FILE: common/membership/hostinfo.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package membership import ( "fmt" "net" "strconv" "strings" ) const ( PortTchannel = "tchannel" PortGRPC = "grpc" ) // PortMap is a map of port names to port numbers. type PortMap map[string]uint16 // HostInfo is a type that contains the info about a cadence host type HostInfo struct { addr string // ip:port returned by peer provider ip string // @todo should we set this to net.IP ? identity string portMap PortMap // ports host is listening to } // NewHostInfo creates a new HostInfo instance func NewHostInfo(addr string) HostInfo { ip, _, _ := net.SplitHostPort(addr) return HostInfo{ addr: addr, ip: ip, } } // String formats a PortMap into a string of name:port pairs func (m PortMap) String() string { res := make([]string, 0, len(m)) for name, port := range m { res = append(res, fmt.Sprintf("%s:%d", name, port)) } return strings.Join(res, ", ") } // NewDetailedHostInfo creates a new HostInfo instance with identity and portmap information func NewDetailedHostInfo(addr string, identity string, portMap PortMap) HostInfo { ip, _, _ := net.SplitHostPort(addr) return HostInfo{ addr: addr, ip: ip, identity: identity, portMap: portMap, } } // GetAddress returns the ip:port address func (hi HostInfo) GetAddress() string { return hi.addr } // GetNamedAddress returns the ip:port address func (hi HostInfo) GetNamedAddress(port string) (string, error) { if port, set := hi.portMap[port]; set { return net.JoinHostPort(hi.ip, strconv.Itoa(int(port))), nil } return "", fmt.Errorf("port %q is not set for %+v", port, hi) } // Belongs tells if ip:port is assigned to this member func (hi HostInfo) Belongs(address string) (bool, error) { if hi.addr == address { return true, nil } ip, port, err := net.SplitHostPort(address) if err != nil { return false, err } if ip != hi.ip { return false, nil } for _, number := range hi.portMap { if port == strconv.Itoa(int(number)) { return true, nil } } return false, nil } // Identity implements ringpop's Membership interface func (hi HostInfo) Identity() string { // if identity is not set, return address if hi.identity == "" { return hi.addr } return hi.identity } // Label is a noop function to conform to ringpop hashring member interface func (hi HostInfo) Label(key string) (value string, has bool) { return "", false } // SetLabel is a noop function to conform to ringpop hashring member interface func (hi HostInfo) SetLabel(key string, value string) { } // String will return a human-readable host details func (hi HostInfo) String() string { return fmt.Sprintf("addr: %s, identity: %s, portMap: %s", hi.addr, hi.identity, hi.portMap) } ================================================ FILE: common/membership/hostinfo_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package membership import ( "testing" "github.com/stretchr/testify/assert" ) func TestBelongs(t *testing.T) { host := HostInfo{} belongs, err := host.Belongs("127.1") assert.False(t, belongs, "invalid host info data should result to false") assert.Error(t, err) host2 := NewDetailedHostInfo("127.0.0.1:123", "dummy", PortMap{}) belongs, err = host2.Belongs("127.0.0.1:123") assert.True(t, belongs, "match on address, port map might be empty") assert.NoError(t, err) host3 := NewDetailedHostInfo("127.0.0.1:1234", "dummy", PortMap{PortGRPC: 3333}) belongs, err = host3.Belongs("127.0.0.1:3333") assert.True(t, belongs, "portmap should be checked") assert.NoError(t, err) host4 := NewDetailedHostInfo("127.0.0.1:1234", "dummy", PortMap{PortGRPC: 3333}) belongs, err = host4.Belongs("127.0.0.2:3333") assert.False(t, belongs, "different IP, will result in false") assert.NoError(t, err) host5 := NewDetailedHostInfo("127.0.0.1:1234", "dummy", PortMap{PortGRPC: 3333}) belongs, err = host5.Belongs("127.0.0.1:3334") assert.False(t, belongs, "portmap has no such port, should return empty without an error") assert.NoError(t, err) } ================================================ FILE: common/membership/membershipfx/membershipfx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package membershipfx import ( "fmt" "go.uber.org/fx" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/common/service" ) // Module provides membership components for fx app. var Module = fx.Module("membership", fx.Provide(buildMembership)) type buildMembershipParams struct { fx.In Clock clock.TimeSource RPCFactory rpc.Factory PeerProvider membership.PeerProvider Logger log.Logger MetricsClient metrics.Client Lifecycle fx.Lifecycle } type buildMembershipResult struct { fx.Out Rings map[string]membership.SingleProvider Resolver membership.Resolver } func buildMembership(params buildMembershipParams) (buildMembershipResult, error) { rings := make(map[string]membership.SingleProvider) for _, s := range service.ListWithRing { rings[s] = membership.NewHashring(s, params.PeerProvider, params.Clock, params.Logger, params.MetricsClient.Scope(metrics.HashringScope)) } resolver, err := membership.NewResolver( params.PeerProvider, params.MetricsClient, params.Logger, rings, ) if err != nil { return buildMembershipResult{}, fmt.Errorf("create resolver: %w", err) } params.Lifecycle.Append(fx.StartStopHook(startResolver(resolver, params.RPCFactory), resolver.Stop)) params.Lifecycle.Append(fx.StopHook(params.RPCFactory.Stop)) return buildMembershipResult{ Rings: rings, Resolver: resolver, }, nil } func startResolver(resolver membership.Resolver, rpcFactory rpc.Factory) func() error { return func() error { err := rpcFactory.Start(resolver) if err != nil { return fmt.Errorf("start rpc factory: %w", err) } resolver.Start() return nil } } ================================================ FILE: common/membership/membershipfx/memebershipfx_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package membershipfx import ( "testing" "go.uber.org/fx" "go.uber.org/fx/fxtest" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/common/service" ) func TestFxStartStop(t *testing.T) { app := fxtest.New(t, fx.Provide(func() appParams { ctrl := gomock.NewController(t) provider := membership.NewMockPeerProvider(ctrl) provider.EXPECT().Start() provider.EXPECT().Stop() for _, s := range service.ListWithRing { provider.EXPECT().Subscribe(s, gomock.Any()).Return(nil) provider.EXPECT().GetMembers(s).Return([]membership.HostInfo{}, nil) // this is also called by every ring, but could be called multiple times. provider.EXPECT().Stop() } factory := rpc.NewMockFactory(ctrl) factory.EXPECT().Start(gomock.Any()).Return(nil) factory.EXPECT().Stop().Return(nil) return appParams{ Clock: clock.NewMockedTimeSource(), PeerProvider: provider, Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), RPCFactory: factory, } }), Module, fx.Invoke(func(resolver membership.Resolver) {})) app.RequireStart().RequireStop() } type appParams struct { fx.Out Clock clock.TimeSource PeerProvider membership.PeerProvider Logger log.Logger MetricsClient metrics.Client RPCFactory rpc.Factory } ================================================ FILE: common/membership/peerprovider_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: hashring.go // // Generated by this command: // // mockgen -package membership -source hashring.go -destination peerprovider_mock.go github.com/uber/cadence/common/membership PeerProvider // // Package membership is a generated GoMock package. package membership import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockPeerProvider is a mock of PeerProvider interface. type MockPeerProvider struct { ctrl *gomock.Controller recorder *MockPeerProviderMockRecorder isgomock struct{} } // MockPeerProviderMockRecorder is the mock recorder for MockPeerProvider. type MockPeerProviderMockRecorder struct { mock *MockPeerProvider } // NewMockPeerProvider creates a new mock instance. func NewMockPeerProvider(ctrl *gomock.Controller) *MockPeerProvider { mock := &MockPeerProvider{ctrl: ctrl} mock.recorder = &MockPeerProviderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPeerProvider) EXPECT() *MockPeerProviderMockRecorder { return m.recorder } // GetMembers mocks base method. func (m *MockPeerProvider) GetMembers(service string) ([]HostInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMembers", service) ret0, _ := ret[0].([]HostInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMembers indicates an expected call of GetMembers. func (mr *MockPeerProviderMockRecorder) GetMembers(service any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMembers", reflect.TypeOf((*MockPeerProvider)(nil).GetMembers), service) } // SelfEvict mocks base method. func (m *MockPeerProvider) SelfEvict() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelfEvict") ret0, _ := ret[0].(error) return ret0 } // SelfEvict indicates an expected call of SelfEvict. func (mr *MockPeerProviderMockRecorder) SelfEvict() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelfEvict", reflect.TypeOf((*MockPeerProvider)(nil).SelfEvict)) } // Start mocks base method. func (m *MockPeerProvider) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockPeerProviderMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockPeerProvider)(nil).Start)) } // Stop mocks base method. func (m *MockPeerProvider) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockPeerProviderMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockPeerProvider)(nil).Stop)) } // Subscribe mocks base method. func (m *MockPeerProvider) Subscribe(name string, handler func(ChangedEvent)) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Subscribe", name, handler) ret0, _ := ret[0].(error) return ret0 } // Subscribe indicates an expected call of Subscribe. func (mr *MockPeerProviderMockRecorder) Subscribe(name, handler any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockPeerProvider)(nil).Subscribe), name, handler) } // WhoAmI mocks base method. func (m *MockPeerProvider) WhoAmI() (HostInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WhoAmI") ret0, _ := ret[0].(HostInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // WhoAmI indicates an expected call of WhoAmI. func (mr *MockPeerProviderMockRecorder) WhoAmI() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WhoAmI", reflect.TypeOf((*MockPeerProvider)(nil).WhoAmI)) } ================================================ FILE: common/membership/resolver.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination resolver_mock.go -self_package github.com/uber/cadence/common/membership // Package membership provides service discovery and membership information mechanism package membership import ( "fmt" "sync" "sync/atomic" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type ( // ChangedEvent describes a change in a membership ring ChangedEvent struct { HostsAdded []string HostsUpdated []string HostsRemoved []string } // Resolver provides membership information for all cadence services. Resolver interface { common.Daemon // WhoAmI returns self host details. // To be consistent with peer provider, it is advised to use peer provider // to return this information WhoAmI() (HostInfo, error) // EvictSelf evicts this member from the membership ring. After this method is // called, other members should discover that this node is no longer part of the // ring. // This primitive is useful to carry out graceful host shutdown during deployments. EvictSelf() error // Lookup will return host which is an owner for provided key. Lookup(service, key string) (HostInfo, error) // Subscribe adds a subscriber which will get detailed change data on the given // channel, whenever membership changes. Subscribe(service, name string, notifyChannel chan<- *ChangedEvent) error // Unsubscribe removes a subscriber for this service. Unsubscribe(service, name string) error // MemberCount returns host count in a service specific hashring MemberCount(service string) (int, error) // Members returns all host addresses in a service specific hashring Members(service string) ([]HostInfo, error) // LookupByAddress returns Host which owns IP:port tuple LookupByAddress(service, address string) (HostInfo, error) } ) // MultiringResolver uses ring-per-service for membership information type MultiringResolver struct { metrics metrics.Client logger log.Logger status int32 provider PeerProvider mu sync.Mutex rings map[string]SingleProvider } var _ Resolver = (*MultiringResolver)(nil) // NewResolver builds hashrings for all services func NewResolver( provider PeerProvider, metricsClient metrics.Client, logger log.Logger, rings map[string]SingleProvider, ) (Resolver, error) { rpo := &MultiringResolver{ status: common.DaemonStatusInitialized, provider: provider, rings: rings, metrics: metricsClient, logger: logger.WithTags(tag.ComponentMembershipResolver), mu: sync.Mutex{}, } return rpo, nil } // Start starts provider and all rings func (rpo *MultiringResolver) Start() { if !atomic.CompareAndSwapInt32( &rpo.status, common.DaemonStatusInitialized, common.DaemonStatusStarted, ) { return } rpo.logger.Info("Starting membership resolver", tag.ComponentMembershipResolver) defer rpo.logger.Info("Started membership resolver", tag.ComponentMembershipResolver) rpo.provider.Start() rpo.mu.Lock() defer rpo.mu.Unlock() for _, ring := range rpo.rings { ring.Start() } } // Stop stops all rings and membership provider func (rpo *MultiringResolver) Stop() { if !atomic.CompareAndSwapInt32( &rpo.status, common.DaemonStatusStarted, common.DaemonStatusStopped, ) { return } rpo.logger.Info("Stopping membership resolver", tag.ComponentMembershipResolver) defer rpo.logger.Info("Stopped membership resolver", tag.ComponentMembershipResolver) rpo.mu.Lock() defer rpo.mu.Unlock() for _, ring := range rpo.rings { ring.Stop() } rpo.provider.Stop() } // WhoAmI asks to provide current instance address func (rpo *MultiringResolver) WhoAmI() (HostInfo, error) { return rpo.provider.WhoAmI() } // EvictSelf is used to remove this host from membership ring func (rpo *MultiringResolver) EvictSelf() error { return rpo.provider.SelfEvict() } func (rpo *MultiringResolver) getRing(service string) (SingleProvider, error) { rpo.mu.Lock() defer rpo.mu.Unlock() ring, found := rpo.rings[service] if !found { return nil, fmt.Errorf("service %q is not tracked by Resolver", service) } return ring, nil } func (rpo *MultiringResolver) Lookup(service string, key string) (HostInfo, error) { ring, err := rpo.getRing(service) if err != nil { return HostInfo{}, err } return ring.Lookup(key) } func (rpo *MultiringResolver) Subscribe(service string, name string, notifyChannel chan<- *ChangedEvent) error { ring, err := rpo.getRing(service) if err != nil { return err } return ring.Subscribe(name, notifyChannel) } func (rpo *MultiringResolver) Unsubscribe(service string, name string) error { ring, err := rpo.getRing(service) if err != nil { return err } return ring.Unsubscribe(name) } func (rpo *MultiringResolver) Members(service string) ([]HostInfo, error) { ring, err := rpo.getRing(service) if err != nil { return nil, err } return ring.Members(), nil } func (rpo *MultiringResolver) LookupByAddress(service, address string) (HostInfo, error) { members, err := rpo.Members(service) if err != nil { return HostInfo{}, err } for _, m := range members { if belongs, err := m.Belongs(address); err == nil && belongs { return m, nil } } rpo.metrics.Scope(metrics.ResolverHostNotFoundScope).IncCounter(metrics.RingResolverError) return HostInfo{}, fmt.Errorf("host not found in service %s: %s", service, address) } func (rpo *MultiringResolver) MemberCount(service string) (int, error) { ring, err := rpo.getRing(service) if err != nil { return 0, err } return ring.MemberCount(), nil } func (ce *ChangedEvent) Empty() bool { return len(ce.HostsAdded) == 0 && len(ce.HostsUpdated) == 0 && len(ce.HostsRemoved) == 0 } ================================================ FILE: common/membership/resolver_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: resolver.go // // Generated by this command: // // mockgen -package membership -source resolver.go -destination resolver_mock.go -self_package github.com/uber/cadence/common/membership // // Package membership is a generated GoMock package. package membership import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockResolver is a mock of Resolver interface. type MockResolver struct { ctrl *gomock.Controller recorder *MockResolverMockRecorder isgomock struct{} } // MockResolverMockRecorder is the mock recorder for MockResolver. type MockResolverMockRecorder struct { mock *MockResolver } // NewMockResolver creates a new mock instance. func NewMockResolver(ctrl *gomock.Controller) *MockResolver { mock := &MockResolver{ctrl: ctrl} mock.recorder = &MockResolverMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockResolver) EXPECT() *MockResolverMockRecorder { return m.recorder } // EvictSelf mocks base method. func (m *MockResolver) EvictSelf() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EvictSelf") ret0, _ := ret[0].(error) return ret0 } // EvictSelf indicates an expected call of EvictSelf. func (mr *MockResolverMockRecorder) EvictSelf() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EvictSelf", reflect.TypeOf((*MockResolver)(nil).EvictSelf)) } // Lookup mocks base method. func (m *MockResolver) Lookup(service, key string) (HostInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Lookup", service, key) ret0, _ := ret[0].(HostInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // Lookup indicates an expected call of Lookup. func (mr *MockResolverMockRecorder) Lookup(service, key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lookup", reflect.TypeOf((*MockResolver)(nil).Lookup), service, key) } // LookupByAddress mocks base method. func (m *MockResolver) LookupByAddress(service, address string) (HostInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LookupByAddress", service, address) ret0, _ := ret[0].(HostInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // LookupByAddress indicates an expected call of LookupByAddress. func (mr *MockResolverMockRecorder) LookupByAddress(service, address any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupByAddress", reflect.TypeOf((*MockResolver)(nil).LookupByAddress), service, address) } // MemberCount mocks base method. func (m *MockResolver) MemberCount(service string) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MemberCount", service) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // MemberCount indicates an expected call of MemberCount. func (mr *MockResolverMockRecorder) MemberCount(service any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MemberCount", reflect.TypeOf((*MockResolver)(nil).MemberCount), service) } // Members mocks base method. func (m *MockResolver) Members(service string) ([]HostInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Members", service) ret0, _ := ret[0].([]HostInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // Members indicates an expected call of Members. func (mr *MockResolverMockRecorder) Members(service any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Members", reflect.TypeOf((*MockResolver)(nil).Members), service) } // Start mocks base method. func (m *MockResolver) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockResolverMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockResolver)(nil).Start)) } // Stop mocks base method. func (m *MockResolver) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockResolverMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockResolver)(nil).Stop)) } // Subscribe mocks base method. func (m *MockResolver) Subscribe(service, name string, notifyChannel chan<- *ChangedEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Subscribe", service, name, notifyChannel) ret0, _ := ret[0].(error) return ret0 } // Subscribe indicates an expected call of Subscribe. func (mr *MockResolverMockRecorder) Subscribe(service, name, notifyChannel any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockResolver)(nil).Subscribe), service, name, notifyChannel) } // Unsubscribe mocks base method. func (m *MockResolver) Unsubscribe(service, name string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Unsubscribe", service, name) ret0, _ := ret[0].(error) return ret0 } // Unsubscribe indicates an expected call of Unsubscribe. func (mr *MockResolverMockRecorder) Unsubscribe(service, name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockResolver)(nil).Unsubscribe), service, name) } // WhoAmI mocks base method. func (m *MockResolver) WhoAmI() (HostInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WhoAmI") ret0, _ := ret[0].(HostInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // WhoAmI indicates an expected call of WhoAmI. func (mr *MockResolverMockRecorder) WhoAmI() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WhoAmI", reflect.TypeOf((*MockResolver)(nil).WhoAmI)) } ================================================ FILE: common/membership/resolver_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package membership import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" ) var testServices = []string{"test-worker", "test-services"} func TestSubscribeIsCalledOnPeerProvider(t *testing.T) { r, pp := newTestResolver(t) _, err := r.getRing("test-worker") assert.NoError(t, err) // After membership is started, we expect start, subscribe and GetMembers on PeerProvider pp.EXPECT().Start().Times(1) pp.EXPECT().Subscribe(gomock.Any(), gomock.Any()).Times(len(testServices)) pp.EXPECT().GetMembers(gomock.Any()).Times(len(testServices)) r.Start() } func TestNewCreatesAllRings(t *testing.T) { a, _ := newTestResolver(t) assert.Equal(t, len(testServices), len(a.rings)) } func TestMethodsAreRoutedToARing(t *testing.T) { var changeCh = make(chan *ChangedEvent) a, pp := newTestResolver(t) // add members to this ring hosts := []HostInfo{} for _, addr := range []string{"127", "128"} { hosts = append(hosts, NewHostInfo(addr)) } pp.EXPECT().GetMembers("test-worker").Return(hosts, nil).Times(1) pp.EXPECT().WhoAmI().AnyTimes() r, err := a.getRing("test-worker") r.Refresh() assert.NoError(t, err) hi, err := r.Lookup("key") assert.NoError(t, err) assert.Equal(t, "127", hi.GetAddress()) // the same ring will be picked here _, err = a.Lookup("WRONG-RING-NAME", "key") assert.Error(t, err) members, err := a.Members("test-worker") assert.NoError(t, err) assert.Equal(t, 2, len(members)) nomembers, err := a.Members("WRONG-RING-NAME") assert.Error(t, err) assert.Equal(t, 0, len(nomembers)) memcount, err := a.MemberCount("test-worker") assert.NoError(t, err) assert.Equal(t, 2, memcount) nomemcount, err := a.MemberCount("WRONG-RING-NAME") assert.Error(t, err) assert.Equal(t, 0, nomemcount) serr := a.Subscribe("test-worker", "sub1", changeCh) assert.NoError(t, serr) serr = a.Subscribe("WRONG-RING-NAME", "sub1", changeCh) assert.Error(t, serr) serr = a.Unsubscribe("test-worker", "sub1") assert.NoError(t, serr) serr = a.Unsubscribe("WRONG-RING-NAME", "sub1") assert.Error(t, serr) } func TestNonExistingRingReturnsError(t *testing.T) { a, _ := newTestResolver(t) _, err := a.getRing("non-existing") assert.Error(t, err) } func TestCallsAreForwardedToProvider(t *testing.T) { a, mockedPeer := newTestResolver(t) mockedPeer.EXPECT().WhoAmI().Times(1) mockedPeer.EXPECT().SelfEvict().Times(1) mockedPeer.EXPECT().Stop().Times(1) a.status = common.DaemonStatusStarted a.WhoAmI() a.EvictSelf() a.Stop() } func newTestResolver(t *testing.T) (*MultiringResolver, *MockPeerProvider) { ctrl := gomock.NewController(t) pp := NewMockPeerProvider(ctrl) rings := make(map[string]SingleProvider, len(testServices)) for _, testService := range testServices { ring := NewHashring(testService, pp, clock.NewMockedTimeSource(), log.NewNoop(), metrics.NewNoopMetricsClient().Scope(metrics.HashringScope)) rings[testService] = ring } resolver, err := NewResolver( pp, metrics.NewNoopMetricsClient(), log.NewNoop(), rings, ) require.NoError(t, err) return resolver.(*MultiringResolver), pp } ================================================ FILE: common/membership/sharddistributorresolver.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package membership import ( "context" "fmt" "net" "strconv" "time" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/service/sharddistributor/client/spectatorclient" ) type ModeKey string const ( // ModeKeyHashRing represents the hash ring shard distribution mode ModeKeyHashRing ModeKey = "hash_ring" // ModeKeyShardDistributor represents the shard distributor mode ModeKeyShardDistributor ModeKey = "shard_distributor" // ModeKeyHashRingShadowShardDistributor represents hash ring mode with shard distributor shadow ModeKeyHashRingShadowShardDistributor ModeKey = "hash_ring-shadow-shard_distributor" // ModeKeyShardDistributorShadowHashRing represents shard distributor mode with hash ring shadow ModeKeyShardDistributorShadowHashRing ModeKey = "shard_distributor-shadow-hash_ring" ) type shardDistributorResolver struct { shardDistributionMode dynamicproperties.StringPropertyFn excludeShortLivedTaskLists dynamicproperties.BoolPropertyFn percentageOnboarded dynamicproperties.IntPropertyFn spectator spectatorclient.Spectator ring SingleProvider logger log.Logger } func (s shardDistributorResolver) AddressToHost(owner string) (HostInfo, error) { return s.ring.AddressToHost(owner) } func NewShardDistributorResolver( spectator spectatorclient.Spectator, shardDistributionMode dynamicproperties.StringPropertyFn, excludeShortLivedTaskLists dynamicproperties.BoolPropertyFn, percentageOnboarded dynamicproperties.IntPropertyFn, ring SingleProvider, logger log.Logger, ) SingleProvider { return &shardDistributorResolver{ spectator: spectator, shardDistributionMode: shardDistributionMode, excludeShortLivedTaskLists: excludeShortLivedTaskLists, percentageOnboarded: percentageOnboarded, ring: ring, logger: logger, } } func (s shardDistributorResolver) Start() { // We do not need to start anything in the shard distributor, so just start the ring s.ring.Start() } func (s shardDistributorResolver) Stop() { // We do not need to stop anything in the shard distributor, so just stop the ring s.ring.Stop() } func (s shardDistributorResolver) Lookup(key string) (HostInfo, error) { if s.shardDistributionMode() != "hash_ring" && s.spectator == nil { // This will avoid panics when the shard distributor is not configured s.logger.Warn("No shard distributor client, defaulting to hash ring", tag.Value(s.shardDistributionMode())) return s.ring.Lookup(key) } excludeTaskList := TaskListExcludedFromShardDistributor(key, uint64(s.percentageOnboarded()), s.excludeShortLivedTaskLists()) if excludeTaskList { return s.ring.Lookup(key) } switch ModeKey(s.shardDistributionMode()) { case ModeKeyHashRing: return s.ring.Lookup(key) case ModeKeyShardDistributor: return s.lookUpInShardDistributor(key) case ModeKeyHashRingShadowShardDistributor: hashRingResult, err := s.ring.Lookup(key) if err != nil { return HostInfo{}, err } // Asynchronously lookup in shard distributor to avoid blocking the main thread go func() { shardDistributorResult, err := s.lookUpInShardDistributor(key) if err != nil { s.logger.Warn("Failed to lookup in shard distributor shadow", tag.Error(err)) } else { logDifferencesInHostInfo(s.logger, hashRingResult, shardDistributorResult) } }() return hashRingResult, nil case ModeKeyShardDistributorShadowHashRing: shardDistributorResult, err := s.lookUpInShardDistributor(key) if err != nil { return HostInfo{}, err } // Asynchronously lookup in hash ring to avoid blocking the main thread go func() { hashRingResult, err := s.ring.Lookup(key) if err != nil { s.logger.Warn("Failed to lookup in hash ring shadow", tag.Error(err)) } else { logDifferencesInHostInfo(s.logger, hashRingResult, shardDistributorResult) } }() return shardDistributorResult, nil } // Default to hash ring s.logger.Warn("Unknown shard distribution mode, defaulting to hash ring", tag.Value(s.shardDistributionMode())) return s.ring.Lookup(key) } func (s shardDistributorResolver) Subscribe(name string, channel chan<- *ChangedEvent) error { // Shard distributor does not support subscription yet, so use the ring return s.ring.Subscribe(name, channel) } func (s shardDistributorResolver) Unsubscribe(name string) error { // Shard distributor does not support subscription yet, so use the ring return s.ring.Unsubscribe(name) } func (s shardDistributorResolver) Members() []HostInfo { // Shard distributor does not member tracking yet, so use the ring return s.ring.Members() } func (s shardDistributorResolver) MemberCount() int { // Shard distributor does not member tracking yet, so use the ring return s.ring.MemberCount() } func (s shardDistributorResolver) Refresh() error { // Shard distributor does not need refresh, so propagate to the ring return s.ring.Refresh() } // TODO: cache the hostinfos, creating them on every request is relatively expensive (we need to do string parsing etc.) func (s shardDistributorResolver) lookUpInShardDistributor(key string) (HostInfo, error) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() owner, err := s.spectator.GetShardOwner(ctx, key) if err != nil { return HostInfo{}, err } portMap := make(PortMap) for _, portType := range []string{PortTchannel, PortGRPC} { portString, ok := owner.Metadata[portType] if !ok { s.logger.Warn(fmt.Sprintf("Port %s not found in metadata", portType), tag.Value(owner)) continue } port, err := strconv.ParseUint(portString, 10, 16) if err != nil { s.logger.Warn(fmt.Sprintf("Failed to convert %s port to int", portType), tag.Error(err), tag.Value(owner)) continue } // This is safe because the conversion above ensures the port is within the uint16 range portMap[portType] = uint16(port) } hostaddress := owner.Metadata["hostIP"] if hostaddress == "" { return HostInfo{}, fmt.Errorf("hostIP not found in metadata") } address := net.JoinHostPort(hostaddress, strconv.Itoa(int(portMap[PortTchannel]))) hostInfo := NewDetailedHostInfo(address, owner.ExecutorID, portMap) return hostInfo, nil } func logDifferencesInHostInfo(logger log.Logger, hashRingResult, shardDistributorResult HostInfo) { for _, portName := range []string{PortTchannel, PortGRPC} { hashRingAddr, err := hashRingResult.GetNamedAddress(portName) if err != nil { logger.Warn(fmt.Sprintf("Failed to get hash ring %s address", portName), tag.Error(err)) continue } shardDistributorAddr, err := shardDistributorResult.GetNamedAddress(portName) if err != nil { logger.Warn(fmt.Sprintf("Failed to get shard distributor %s address", portName), tag.Error(err)) continue } if hashRingAddr != shardDistributorAddr { logger.Warn(fmt.Sprintf("Shadow lookup mismatch %s addresses", portName), tag.HashRingResult(hashRingAddr), tag.ShardDistributorResult(shardDistributorAddr)) } } } ================================================ FILE: common/membership/sharddistributorresolver_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package membership import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/service/sharddistributor/client/spectatorclient" ) func TestShardDistributorResolver_Lookup_modeHashRing(t *testing.T) { resolver, ring, _ := newShardDistributorResolver(t) resolver.shardDistributionMode = func(...dynamicproperties.FilterOption) string { return string(ModeKeyHashRing) } ring.EXPECT().Lookup("test-key").Return(HostInfo{addr: "test-addr"}, nil) host, err := resolver.Lookup("test-key") assert.NoError(t, err) assert.Equal(t, "test-addr", host.addr) } func TestShardDistributorResolver_Lookup_modeShardDistributor(t *testing.T) { resolver, _, shardDistributorMock := newShardDistributorResolver(t) resolver.shardDistributionMode = func(...dynamicproperties.FilterOption) string { return string(ModeKeyShardDistributor) } shardDistributorMock.EXPECT().GetShardOwner(gomock.Any(), "test-key"). Return(&spectatorclient.ShardOwner{ ExecutorID: "test-owner", Metadata: map[string]string{ "hostIP": "127.0.0.1", "tchannel": "7933", "grpc": "7833", }, }, nil) host, err := resolver.Lookup("test-key") assert.NoError(t, err) assert.Equal(t, "127.0.0.1:7933", host.addr) } func TestShardDistributorResolver_Lookup_modeHashRingShadowShardDistributor(t *testing.T) { resolver, ring, shardDistributorMock := newShardDistributorResolver(t) resolver.shardDistributionMode = func(...dynamicproperties.FilterOption) string { return string(ModeKeyHashRingShadowShardDistributor) } cases := []struct { name string hashRingAddr string hashRingError error shardDistributorHostIP string shardDistributorError error expectedLog string }{ { name: "hash ring and shard distributor agree", hashRingAddr: "127.0.0.1:7933", shardDistributorHostIP: "127.0.0.1", }, { name: "hash ring and shard distributor disagree", hashRingAddr: "127.0.0.1:7933", shardDistributorHostIP: "127.0.0.2", expectedLog: "Shadow lookup mismatch", }, { name: "shard distributor error", hashRingAddr: "127.0.0.1:7933", shardDistributorError: assert.AnError, expectedLog: "Failed to lookup in shard distributor shadow", }, { name: "hash ring error", hashRingError: assert.AnError, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { logger, logs := testlogger.NewObserved(t) resolver.logger = logger ring.EXPECT().Lookup("test-key").Return(NewDetailedHostInfo( tc.hashRingAddr, "test-owner", PortMap{PortTchannel: 7933, PortGRPC: 7833}, ), tc.hashRingError) // If the hash ring lookup fails, we should just bail out and not call the shard distributor if tc.hashRingError == nil { shardDistributorMock.EXPECT().GetShardOwner(gomock.Any(), "test-key"). Return(&spectatorclient.ShardOwner{ ExecutorID: "test-owner", Metadata: map[string]string{ "hostIP": tc.shardDistributorHostIP, "tchannel": "7933", "grpc": "7833", }, }, tc.shardDistributorError) } host, err := resolver.Lookup("test-key") assert.Equal(t, err, tc.hashRingError) if tc.hashRingError == nil { assert.Equal(t, "127.0.0.1:7933", host.addr) } // Wait a bit for async shadow lookup to complete time.Sleep(50 * time.Millisecond) if tc.expectedLog != "" { if tc.expectedLog == "Shadow lookup mismatch" { // logDifferencesInHostInfo logs separately for tchannel and grpc ports assert.Equal(t, 2, logs.Len()) } else { // Error cases only log once assert.Equal(t, 1, logs.Len()) assert.Equal(t, 1, logs.FilterMessage(tc.expectedLog).Len()) } } else { assert.Equal(t, 0, logs.Len()) } }) } } func TestShardDistributorResolver_Lookup_modeShardDistributorShadowHashRing(t *testing.T) { resolver, ring, shardDistributorMock := newShardDistributorResolver(t) resolver.shardDistributionMode = func(...dynamicproperties.FilterOption) string { return string(ModeKeyShardDistributorShadowHashRing) } cases := []struct { name string shardDistributorHostIP string shardDistributorError error hashRingAddr string hashRingError error expectedLog string }{ { name: "shard distributor and hash ring agree", shardDistributorHostIP: "127.0.0.1", hashRingAddr: "127.0.0.1:7933", }, { name: "shard distributor and hash ring disagree", shardDistributorHostIP: "127.0.0.1", hashRingAddr: "127.0.0.2:7933", expectedLog: "Shadow lookup mismatch", }, { name: "hash ring error", shardDistributorHostIP: "127.0.0.1", hashRingError: assert.AnError, expectedLog: "Failed to lookup in hash ring shadow", }, { name: "shard distributor error", shardDistributorError: assert.AnError, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { logger, logs := testlogger.NewObserved(t) resolver.logger = logger shardDistributorMock.EXPECT().GetShardOwner(gomock.Any(), "test-key"). Return(&spectatorclient.ShardOwner{ ExecutorID: "test-owner", Metadata: map[string]string{ "hostIP": tc.shardDistributorHostIP, "tchannel": "7933", "grpc": "7833", }, }, tc.shardDistributorError) // If the hash ring lookup fails, we should just bail out and not call the shard distributor if tc.shardDistributorError == nil { ring.EXPECT().Lookup("test-key").Return(NewDetailedHostInfo( tc.hashRingAddr, "test-owner", PortMap{PortTchannel: 7933, PortGRPC: 7833}, ), tc.hashRingError) } host, err := resolver.Lookup("test-key") assert.Equal(t, err, tc.shardDistributorError) if tc.shardDistributorError == nil { assert.Equal(t, "127.0.0.1:7933", host.addr) } // Wait a bit for async shadow lookup to complete time.Sleep(50 * time.Millisecond) if tc.expectedLog != "" { if tc.expectedLog == "Shadow lookup mismatch" { // logDifferencesInHostInfo logs separately for tchannel and grpc ports assert.Equal(t, 2, logs.Len()) } else { // Error cases only log once assert.Equal(t, 1, logs.Len()) assert.Equal(t, 1, logs.FilterMessage(tc.expectedLog).Len()) } } else { assert.Equal(t, 0, logs.Len()) } }) } } func TestShardDistributorResolver_Lookup_UnknownMode(t *testing.T) { resolver, ring, _ := newShardDistributorResolver(t) resolver.shardDistributionMode = func(...dynamicproperties.FilterOption) string { return "unknown" } ring.EXPECT().Lookup("test-key").Return(HostInfo{addr: "test-addr"}, nil) host, err := resolver.Lookup("test-key") assert.NoError(t, err) assert.Equal(t, "test-addr", host.addr) } /* Test all the simple proxies */ func TestShardDistributorResolver_Start(t *testing.T) { resolver, ring, _ := newShardDistributorResolver(t) ring.EXPECT().Start().Times(1) resolver.Start() } func TestShardDistributorResolver_Stop(t *testing.T) { resolver, ring, _ := newShardDistributorResolver(t) ring.EXPECT().Stop().Times(1) resolver.Stop() } func TestShardDistributorResolver_Subscribe(t *testing.T) { resolver, ring, _ := newShardDistributorResolver(t) ring.EXPECT().Subscribe("test-name", gomock.Any()).Times(1) resolver.Subscribe("test-name", nil) } func TestShardDistributorResolver_UnSubscribe(t *testing.T) { resolver, ring, _ := newShardDistributorResolver(t) ring.EXPECT().Unsubscribe("test-name").Times(1) resolver.Unsubscribe("test-name") } func TestShardDistributorResolver_Members(t *testing.T) { resolver, ring, _ := newShardDistributorResolver(t) ring.EXPECT().Members().Return(nil) resolver.Members() } func TestShardDistributorResolver_MemberCount(t *testing.T) { resolver, ring, _ := newShardDistributorResolver(t) ring.EXPECT().MemberCount().Return(0) resolver.MemberCount() } func TestShardDistributorResolver_Refresh(t *testing.T) { resolver, ring, _ := newShardDistributorResolver(t) ring.EXPECT().Refresh().Times(1) resolver.Refresh() } func TestShardDistributorResolver_AddressToHost(t *testing.T) { resolver, ring, _ := newShardDistributorResolver(t) ring.EXPECT().AddressToHost("test").Return(HostInfo{}, nil) resolver.AddressToHost("test") } func TestShardDistributorResolver_Lookup_ExcludeShortLivedTaskLists(t *testing.T) { cases := []struct { name string excludeShortLivedTaskLists bool taskListName string mode ModeKey expectHashRing bool }{ { name: "exclude enabled with UUID tasklist uses hash ring", excludeShortLivedTaskLists: true, taskListName: "tasklist-550e8400-e29b-41d4-a716-446655440000", mode: ModeKeyShardDistributor, expectHashRing: true, }, { name: "exclude enabled without UUID tasklist uses shard distributor", excludeShortLivedTaskLists: true, taskListName: "my-regular-tasklist", mode: ModeKeyShardDistributor, expectHashRing: false, }, { name: "exclude disabled with UUID tasklist uses shard distributor", excludeShortLivedTaskLists: false, taskListName: "tasklist-550e8400-e29b-41d4-a716-446655440000", mode: ModeKeyShardDistributor, expectHashRing: false, }, { name: "exclude enabled with UUID tasklist in hash ring mode still uses hash ring", excludeShortLivedTaskLists: true, taskListName: "tasklist-550e8400-e29b-41d4-a716-446655440000", mode: ModeKeyHashRing, expectHashRing: true, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) spectator := spectatorclient.NewMockSpectator(ctrl) ring := NewMockSingleProvider(ctrl) logger := log.NewNoop() resolver := NewShardDistributorResolver( spectator, dynamicproperties.GetStringPropertyFn(string(tc.mode)), dynamicproperties.GetBoolPropertyFn(tc.excludeShortLivedTaskLists), dynamicproperties.GetIntPropertyFn(100), ring, logger, ).(*shardDistributorResolver) if tc.expectHashRing { ring.EXPECT().Lookup(tc.taskListName).Return(HostInfo{addr: "hash-ring-addr"}, nil) } else { spectator.EXPECT().GetShardOwner(gomock.Any(), tc.taskListName). Return(&spectatorclient.ShardOwner{ ExecutorID: "test-owner", Metadata: map[string]string{ "hostIP": "127.0.0.1", "tchannel": "7933", "grpc": "7833", }, }, nil) } host, err := resolver.Lookup(tc.taskListName) assert.NoError(t, err) if tc.expectHashRing { assert.Equal(t, "hash-ring-addr", host.addr) } else { assert.Equal(t, "127.0.0.1:7933", host.addr) } }) } } func newShardDistributorResolver(t *testing.T) (*shardDistributorResolver, *MockSingleProvider, *spectatorclient.MockSpectator) { ctrl := gomock.NewController(t) spectator := spectatorclient.NewMockSpectator(ctrl) shardDistributionMode := dynamicproperties.GetStringPropertyFn("") excludeShortLivedTaskLists := dynamicproperties.GetBoolPropertyFn(false) percentageOnboarded := dynamicproperties.GetIntPropertyFn(100) ring := NewMockSingleProvider(ctrl) logger := log.NewNoop() resolver := NewShardDistributorResolver(spectator, shardDistributionMode, excludeShortLivedTaskLists, percentageOnboarded, ring, logger).(*shardDistributorResolver) return resolver, ring, spectator } ================================================ FILE: common/membership/singleprovider.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package membership import "github.com/uber/cadence/common" //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination=singleprovider_mock.go SingleProvider type SingleProvider interface { common.Daemon Lookup(key string) (HostInfo, error) Subscribe(name string, channel chan<- *ChangedEvent) error AddressToHost(owner string) (HostInfo, error) Unsubscribe(name string) error Members() []HostInfo MemberCount() int Refresh() error } ================================================ FILE: common/membership/singleprovider_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: singleprovider.go // // Generated by this command: // // mockgen -package membership -source singleprovider.go -destination=singleprovider_mock.go SingleProvider // // Package membership is a generated GoMock package. package membership import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockSingleProvider is a mock of SingleProvider interface. type MockSingleProvider struct { ctrl *gomock.Controller recorder *MockSingleProviderMockRecorder isgomock struct{} } // MockSingleProviderMockRecorder is the mock recorder for MockSingleProvider. type MockSingleProviderMockRecorder struct { mock *MockSingleProvider } // NewMockSingleProvider creates a new mock instance. func NewMockSingleProvider(ctrl *gomock.Controller) *MockSingleProvider { mock := &MockSingleProvider{ctrl: ctrl} mock.recorder = &MockSingleProviderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSingleProvider) EXPECT() *MockSingleProviderMockRecorder { return m.recorder } // AddressToHost mocks base method. func (m *MockSingleProvider) AddressToHost(owner string) (HostInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddressToHost", owner) ret0, _ := ret[0].(HostInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // AddressToHost indicates an expected call of AddressToHost. func (mr *MockSingleProviderMockRecorder) AddressToHost(owner any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddressToHost", reflect.TypeOf((*MockSingleProvider)(nil).AddressToHost), owner) } // Lookup mocks base method. func (m *MockSingleProvider) Lookup(key string) (HostInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Lookup", key) ret0, _ := ret[0].(HostInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // Lookup indicates an expected call of Lookup. func (mr *MockSingleProviderMockRecorder) Lookup(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lookup", reflect.TypeOf((*MockSingleProvider)(nil).Lookup), key) } // MemberCount mocks base method. func (m *MockSingleProvider) MemberCount() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MemberCount") ret0, _ := ret[0].(int) return ret0 } // MemberCount indicates an expected call of MemberCount. func (mr *MockSingleProviderMockRecorder) MemberCount() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MemberCount", reflect.TypeOf((*MockSingleProvider)(nil).MemberCount)) } // Members mocks base method. func (m *MockSingleProvider) Members() []HostInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Members") ret0, _ := ret[0].([]HostInfo) return ret0 } // Members indicates an expected call of Members. func (mr *MockSingleProviderMockRecorder) Members() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Members", reflect.TypeOf((*MockSingleProvider)(nil).Members)) } // Refresh mocks base method. func (m *MockSingleProvider) Refresh() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Refresh") ret0, _ := ret[0].(error) return ret0 } // Refresh indicates an expected call of Refresh. func (mr *MockSingleProviderMockRecorder) Refresh() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Refresh", reflect.TypeOf((*MockSingleProvider)(nil).Refresh)) } // Start mocks base method. func (m *MockSingleProvider) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockSingleProviderMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockSingleProvider)(nil).Start)) } // Stop mocks base method. func (m *MockSingleProvider) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockSingleProviderMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockSingleProvider)(nil).Stop)) } // Subscribe mocks base method. func (m *MockSingleProvider) Subscribe(name string, channel chan<- *ChangedEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Subscribe", name, channel) ret0, _ := ret[0].(error) return ret0 } // Subscribe indicates an expected call of Subscribe. func (mr *MockSingleProviderMockRecorder) Subscribe(name, channel any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockSingleProvider)(nil).Subscribe), name, channel) } // Unsubscribe mocks base method. func (m *MockSingleProvider) Unsubscribe(name string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Unsubscribe", name) ret0, _ := ret[0].(error) return ret0 } // Unsubscribe indicates an expected call of Unsubscribe. func (mr *MockSingleProviderMockRecorder) Unsubscribe(name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockSingleProvider)(nil).Unsubscribe), name) } ================================================ FILE: common/membership/tasklist_differentiator.go ================================================ package membership import ( "regexp" "github.com/dgryski/go-farm" ) const uuidRegex = `[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}` var uuidRegexp = regexp.MustCompile(uuidRegex) func TaskListExcludedFromShardDistributor(taskListName string, percentageOnboarded uint64, excludeShortLivedTaskLists bool) bool { // This regex checks if the task list name has a UUID, if it does we // consider it a short lived tasklist that will not be managed by the shard distributor. excludeShortLivedTaskLists = excludeShortLivedTaskLists && uuidRegexp.MatchString(taskListName) return excludeShortLivedTaskLists || !belowPercentage(taskListName, percentageOnboarded) } func belowPercentage(taskListName string, percentageOnboarded uint64) bool { hash := farm.Fingerprint64([]byte(taskListName)) return isbelowPercentage(hash, percentageOnboarded) } func isbelowPercentage(hash uint64, percentageOnboarded uint64) bool { return hash%100 < percentageOnboarded } ================================================ FILE: common/membership/tasklist_differentiator_test.go ================================================ package membership import ( "testing" "github.com/stretchr/testify/assert" ) func TestTaskListExcludedFromShardDistributor(t *testing.T) { tests := []struct { name string taskListName string want bool percentageOnboarded uint64 excludeShortLivedTaskLists bool }{ { name: "task list with UUID - exclude enabled", taskListName: "tasklist-550e8400-e29b-41d4-a716-446655440000", want: true, percentageOnboarded: 100, excludeShortLivedTaskLists: true, }, { name: "task list with UUID - exclude disabled", taskListName: "tasklist-550e8400-e29b-41d4-a716-446655440000", want: false, percentageOnboarded: 100, excludeShortLivedTaskLists: false, }, { name: "task list with uppercase UUID - exclude enabled", taskListName: "tasklist-550E8400-E29B-41D4-A716-446655440000", want: true, percentageOnboarded: 100, excludeShortLivedTaskLists: true, }, { name: "task list name is UUID only - exclude enabled", taskListName: "550e8400-e29b-41d4-a716-446655440000", want: true, percentageOnboarded: 100, excludeShortLivedTaskLists: true, }, { name: "task list without UUID", taskListName: "my-task-list", want: false, percentageOnboarded: 100, excludeShortLivedTaskLists: true, }, { name: "empty task list name", taskListName: "", want: false, percentageOnboarded: 100, excludeShortLivedTaskLists: true, }, { name: "task list with partial UUID-like string", taskListName: "tasklist-550e8400-e29b", want: false, percentageOnboarded: 100, excludeShortLivedTaskLists: true, }, { name: "task list with UUID prefix", taskListName: "550e8400-e29b-41d4-a716-446655440000-suffix", want: true, percentageOnboarded: 100, excludeShortLivedTaskLists: true, }, { name: "task list below percentage", taskListName: "my-task-list", // farm.Fingerprint64([]byte("my-task-list"))%100 = 58 want: false, percentageOnboarded: 60, excludeShortLivedTaskLists: true, }, { name: "task list above percentage", taskListName: "my-task-list", // farm.Fingerprint64([]byte("my-task-list"))%100 = 58 want: true, percentageOnboarded: 50, excludeShortLivedTaskLists: true, }, { name: "task list below percentage - but UUID - exclude enabled", taskListName: "my-task-list-550e8400-e29b-41d4-a716-446655440003", // farm.Fingerprint64([]byte("my-task-list-550e8400-e29b-41d4-a716-446655440003"))%100 = 69 want: true, percentageOnboarded: 70, excludeShortLivedTaskLists: true, }, { name: "task list below percentage - UUID - exclude disabled", taskListName: "my-task-list-550e8400-e29b-41d4-a716-446655440003", // farm.Fingerprint64([]byte("my-task-list-550e8400-e29b-41d4-a716-446655440003"))%100 = 69 want: false, percentageOnboarded: 70, excludeShortLivedTaskLists: false, }, { name: "task list above percentage - but UUID", taskListName: "my-task-list-550e8400-e29b-41d4-a716-446655440003", // farm.Fingerprint64([]byte("my-task-list-550e8400-e29b-41d4-a716-446655440003"))%100 = 69 want: true, percentageOnboarded: 60, excludeShortLivedTaskLists: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := TaskListExcludedFromShardDistributor(tt.taskListName, tt.percentageOnboarded, tt.excludeShortLivedTaskLists) assert.Equal(t, tt.want, got) }) } } func TestIsbelowPercentage(t *testing.T) { tests := []struct { name string hash uint64 percentage uint64 want bool }{ { name: "hash and percentage are 0", hash: 0, percentage: 0, want: false, }, { name: "hash is 0 and percentage is 1", hash: 0, percentage: 1, want: true, }, { name: "hash is 100 and percentage is 1 (we wrap)", hash: 100, percentage: 1, want: true, }, { name: "hash is same as percentage", hash: 33, percentage: 33, want: false, }, { name: "hash is big", hash: 10000000000033, percentage: 34, want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := isbelowPercentage(tt.hash, tt.percentage) assert.Equal(t, tt.want, got) }) } } ================================================ FILE: common/messaging/ackManager.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package messaging import ( "fmt" "sync" "go.uber.org/atomic" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) type ackManager struct { sync.RWMutex outstandingMessages map[int64]bool // key->itemID, value->(true for acked/completed, false->for non acked) readLevel int64 // Maximum itemID inserted into outstandingMessages ackLevel int64 // Maximum itemID below which all messages are acked backlogCounter atomic.Int64 logIncontinuousErr bool // emit error for itemID being incontinuous when consuming for potential bugs logger log.Logger } // NewAckManager returns a AckManager without monitoring the itemIDs continousness. // For example, our internal matching task queue doesn't guarantee it. func NewAckManager(logger log.Logger) AckManager { return newAckManager(false, logger) } // NewContinuousAckManager returns a ContinuousAckManager // it will emit error logs for itemIDs being incontinuous // This is useful for some message queue system that guarantees continuousness // that we want to monitor it's behaving correctly func NewContinuousAckManager(logger log.Logger) AckManager { return newAckManager(true, logger) } func newAckManager(logIncontinuousErr bool, logger log.Logger) AckManager { return &ackManager{ logger: logger, outstandingMessages: make(map[int64]bool), readLevel: -1, ackLevel: -1, logIncontinuousErr: logIncontinuousErr, } } // Registers message as in-flight and moves read level to it. Messages can be added in increasing order of messageID only. // NOTE that ackManager assumes adding messages is in order func (m *ackManager) ReadItem(itemID int64) error { m.Lock() defer m.Unlock() m.backlogCounter.Inc() if m.readLevel >= itemID { return fmt.Errorf("next item ID is less than or equal to current read level. itemID %d, readLevel %d", itemID, m.readLevel) } if _, ok := m.outstandingMessages[itemID]; ok { return fmt.Errorf("already present in outstanding items but hasn't added itemID:%d", itemID) } m.readLevel = itemID if m.ackLevel == -1 { // because of ordering, the first itemID is the minimum to ack m.ackLevel = itemID - 1 m.logger.Info("this is the very first itemID being read in this ackManager", tag.TaskID(itemID), ) } m.outstandingMessages[itemID] = false // true is for acked return nil } func (m *ackManager) AckItem(itemID int64) (ackLevel int64) { m.Lock() defer m.Unlock() if completed, ok := m.outstandingMessages[itemID]; ok && !completed { m.outstandingMessages[itemID] = true m.backlogCounter.Dec() } else { m.logger.Warn("Duplicated completion for item", tag.TaskID(itemID)) } // Update ackLevel for current := m.ackLevel + 1; current <= m.readLevel; current++ { if acked, ok := m.outstandingMessages[current]; ok { if acked { m.ackLevel = current delete(m.outstandingMessages, current) } else { return m.ackLevel } } else { if m.logIncontinuousErr { m.logger.Error("potential bug, an item is probably skipped when adding", tag.TaskID(current)) } } } return m.ackLevel } func (m *ackManager) GetReadLevel() int64 { m.RLock() defer m.RUnlock() return m.readLevel } func (m *ackManager) SetReadLevel(readLevel int64) { m.Lock() defer m.Unlock() m.readLevel = readLevel } func (m *ackManager) GetAckLevel() int64 { m.RLock() defer m.RUnlock() return m.ackLevel } func (m *ackManager) SetAckLevel(ackLevel int64) { m.Lock() defer m.Unlock() if ackLevel > m.ackLevel { m.ackLevel = ackLevel } if ackLevel > m.readLevel { m.readLevel = ackLevel } } func (m *ackManager) GetBacklogCount() int64 { return m.backlogCounter.Load() } ================================================ FILE: common/messaging/ackManager_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package messaging import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/log/testlogger" ) func TestAckManager(t *testing.T) { logger := testlogger.New(t) m := NewAckManager(logger) m.SetAckLevel(100) assert.EqualValues(t, 100, m.GetAckLevel()) assert.EqualValues(t, 100, m.GetReadLevel()) const t1 = 200 const t2 = 220 const t3 = 320 const t4 = 340 const t5 = 360 err := m.ReadItem(t1) assert.Nil(t, err) assert.EqualValues(t, 100, m.GetAckLevel()) assert.EqualValues(t, t1, m.GetReadLevel()) err = m.ReadItem(t2) assert.Nil(t, err) assert.EqualValues(t, 100, m.GetAckLevel()) assert.EqualValues(t, t2, m.GetReadLevel()) m.AckItem(t2) assert.EqualValues(t, 100, m.GetAckLevel()) assert.EqualValues(t, t2, m.GetReadLevel()) m.AckItem(t1) assert.EqualValues(t, t2, m.GetAckLevel()) assert.EqualValues(t, t2, m.GetReadLevel()) m.SetAckLevel(300) assert.EqualValues(t, 300, m.GetAckLevel()) assert.EqualValues(t, 300, m.GetReadLevel()) err = m.ReadItem(t3) assert.Nil(t, err) assert.EqualValues(t, 300, m.GetAckLevel()) assert.EqualValues(t, t3, m.GetReadLevel()) err = m.ReadItem(t4) assert.Nil(t, err) assert.EqualValues(t, 300, m.GetAckLevel()) assert.EqualValues(t, t4, m.GetReadLevel()) m.AckItem(t3) assert.EqualValues(t, t3, m.GetAckLevel()) assert.EqualValues(t, t4, m.GetReadLevel()) m.AckItem(t4) assert.EqualValues(t, t4, m.GetAckLevel()) assert.EqualValues(t, t4, m.GetReadLevel()) m.SetReadLevel(t5) assert.EqualValues(t, t5, m.GetReadLevel()) } ================================================ FILE: common/messaging/client_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package messaging -source interface.go -destination client_mock.go // // Package messaging is a generated GoMock package. package messaging import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // NewConsumer mocks base method. func (m *MockClient) NewConsumer(appName, consumerName string) (Consumer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewConsumer", appName, consumerName) ret0, _ := ret[0].(Consumer) ret1, _ := ret[1].(error) return ret0, ret1 } // NewConsumer indicates an expected call of NewConsumer. func (mr *MockClientMockRecorder) NewConsumer(appName, consumerName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewConsumer", reflect.TypeOf((*MockClient)(nil).NewConsumer), appName, consumerName) } // NewProducer mocks base method. func (m *MockClient) NewProducer(appName string) (Producer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewProducer", appName) ret0, _ := ret[0].(Producer) ret1, _ := ret[1].(error) return ret0, ret1 } // NewProducer indicates an expected call of NewProducer. func (mr *MockClientMockRecorder) NewProducer(appName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewProducer", reflect.TypeOf((*MockClient)(nil).NewProducer), appName) } // MockConsumer is a mock of Consumer interface. type MockConsumer struct { ctrl *gomock.Controller recorder *MockConsumerMockRecorder isgomock struct{} } // MockConsumerMockRecorder is the mock recorder for MockConsumer. type MockConsumerMockRecorder struct { mock *MockConsumer } // NewMockConsumer creates a new mock instance. func NewMockConsumer(ctrl *gomock.Controller) *MockConsumer { mock := &MockConsumer{ctrl: ctrl} mock.recorder = &MockConsumerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockConsumer) EXPECT() *MockConsumerMockRecorder { return m.recorder } // Messages mocks base method. func (m *MockConsumer) Messages() <-chan Message { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Messages") ret0, _ := ret[0].(<-chan Message) return ret0 } // Messages indicates an expected call of Messages. func (mr *MockConsumerMockRecorder) Messages() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Messages", reflect.TypeOf((*MockConsumer)(nil).Messages)) } // Start mocks base method. func (m *MockConsumer) Start() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start") ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockConsumerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockConsumer)(nil).Start)) } // Stop mocks base method. func (m *MockConsumer) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockConsumerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockConsumer)(nil).Stop)) } // MockMessage is a mock of Message interface. type MockMessage struct { ctrl *gomock.Controller recorder *MockMessageMockRecorder isgomock struct{} } // MockMessageMockRecorder is the mock recorder for MockMessage. type MockMessageMockRecorder struct { mock *MockMessage } // NewMockMessage creates a new mock instance. func NewMockMessage(ctrl *gomock.Controller) *MockMessage { mock := &MockMessage{ctrl: ctrl} mock.recorder = &MockMessageMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMessage) EXPECT() *MockMessageMockRecorder { return m.recorder } // Ack mocks base method. func (m *MockMessage) Ack() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Ack") ret0, _ := ret[0].(error) return ret0 } // Ack indicates an expected call of Ack. func (mr *MockMessageMockRecorder) Ack() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ack", reflect.TypeOf((*MockMessage)(nil).Ack)) } // Nack mocks base method. func (m *MockMessage) Nack() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Nack") ret0, _ := ret[0].(error) return ret0 } // Nack indicates an expected call of Nack. func (mr *MockMessageMockRecorder) Nack() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Nack", reflect.TypeOf((*MockMessage)(nil).Nack)) } // Offset mocks base method. func (m *MockMessage) Offset() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Offset") ret0, _ := ret[0].(int64) return ret0 } // Offset indicates an expected call of Offset. func (mr *MockMessageMockRecorder) Offset() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Offset", reflect.TypeOf((*MockMessage)(nil).Offset)) } // Partition mocks base method. func (m *MockMessage) Partition() int32 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Partition") ret0, _ := ret[0].(int32) return ret0 } // Partition indicates an expected call of Partition. func (mr *MockMessageMockRecorder) Partition() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Partition", reflect.TypeOf((*MockMessage)(nil).Partition)) } // Value mocks base method. func (m *MockMessage) Value() []byte { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Value") ret0, _ := ret[0].([]byte) return ret0 } // Value indicates an expected call of Value. func (mr *MockMessageMockRecorder) Value() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Value", reflect.TypeOf((*MockMessage)(nil).Value)) } // MockProducer is a mock of Producer interface. type MockProducer struct { ctrl *gomock.Controller recorder *MockProducerMockRecorder isgomock struct{} } // MockProducerMockRecorder is the mock recorder for MockProducer. type MockProducerMockRecorder struct { mock *MockProducer } // NewMockProducer creates a new mock instance. func NewMockProducer(ctrl *gomock.Controller) *MockProducer { mock := &MockProducer{ctrl: ctrl} mock.recorder = &MockProducerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProducer) EXPECT() *MockProducerMockRecorder { return m.recorder } // Publish mocks base method. func (m *MockProducer) Publish(ctx context.Context, message any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Publish", ctx, message) ret0, _ := ret[0].(error) return ret0 } // Publish indicates an expected call of Publish. func (mr *MockProducerMockRecorder) Publish(ctx, message any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Publish", reflect.TypeOf((*MockProducer)(nil).Publish), ctx, message) } // MockCloseableProducer is a mock of CloseableProducer interface. type MockCloseableProducer struct { ctrl *gomock.Controller recorder *MockCloseableProducerMockRecorder isgomock struct{} } // MockCloseableProducerMockRecorder is the mock recorder for MockCloseableProducer. type MockCloseableProducerMockRecorder struct { mock *MockCloseableProducer } // NewMockCloseableProducer creates a new mock instance. func NewMockCloseableProducer(ctrl *gomock.Controller) *MockCloseableProducer { mock := &MockCloseableProducer{ctrl: ctrl} mock.recorder = &MockCloseableProducerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCloseableProducer) EXPECT() *MockCloseableProducerMockRecorder { return m.recorder } // Close mocks base method. func (m *MockCloseableProducer) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockCloseableProducerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockCloseableProducer)(nil).Close)) } // Publish mocks base method. func (m *MockCloseableProducer) Publish(ctx context.Context, message any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Publish", ctx, message) ret0, _ := ret[0].(error) return ret0 } // Publish indicates an expected call of Publish. func (mr *MockCloseableProducerMockRecorder) Publish(ctx, message any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Publish", reflect.TypeOf((*MockCloseableProducer)(nil).Publish), ctx, message) } // MockAckManager is a mock of AckManager interface. type MockAckManager struct { ctrl *gomock.Controller recorder *MockAckManagerMockRecorder isgomock struct{} } // MockAckManagerMockRecorder is the mock recorder for MockAckManager. type MockAckManagerMockRecorder struct { mock *MockAckManager } // NewMockAckManager creates a new mock instance. func NewMockAckManager(ctrl *gomock.Controller) *MockAckManager { mock := &MockAckManager{ctrl: ctrl} mock.recorder = &MockAckManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockAckManager) EXPECT() *MockAckManagerMockRecorder { return m.recorder } // AckItem mocks base method. func (m *MockAckManager) AckItem(id int64) int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AckItem", id) ret0, _ := ret[0].(int64) return ret0 } // AckItem indicates an expected call of AckItem. func (mr *MockAckManagerMockRecorder) AckItem(id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AckItem", reflect.TypeOf((*MockAckManager)(nil).AckItem), id) } // GetAckLevel mocks base method. func (m *MockAckManager) GetAckLevel() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAckLevel") ret0, _ := ret[0].(int64) return ret0 } // GetAckLevel indicates an expected call of GetAckLevel. func (mr *MockAckManagerMockRecorder) GetAckLevel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAckLevel", reflect.TypeOf((*MockAckManager)(nil).GetAckLevel)) } // GetBacklogCount mocks base method. func (m *MockAckManager) GetBacklogCount() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBacklogCount") ret0, _ := ret[0].(int64) return ret0 } // GetBacklogCount indicates an expected call of GetBacklogCount. func (mr *MockAckManagerMockRecorder) GetBacklogCount() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBacklogCount", reflect.TypeOf((*MockAckManager)(nil).GetBacklogCount)) } // GetReadLevel mocks base method. func (m *MockAckManager) GetReadLevel() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReadLevel") ret0, _ := ret[0].(int64) return ret0 } // GetReadLevel indicates an expected call of GetReadLevel. func (mr *MockAckManagerMockRecorder) GetReadLevel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReadLevel", reflect.TypeOf((*MockAckManager)(nil).GetReadLevel)) } // ReadItem mocks base method. func (m *MockAckManager) ReadItem(id int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadItem", id) ret0, _ := ret[0].(error) return ret0 } // ReadItem indicates an expected call of ReadItem. func (mr *MockAckManagerMockRecorder) ReadItem(id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadItem", reflect.TypeOf((*MockAckManager)(nil).ReadItem), id) } // SetAckLevel mocks base method. func (m *MockAckManager) SetAckLevel(ackLevel int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetAckLevel", ackLevel) } // SetAckLevel indicates an expected call of SetAckLevel. func (mr *MockAckManagerMockRecorder) SetAckLevel(ackLevel any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAckLevel", reflect.TypeOf((*MockAckManager)(nil).SetAckLevel), ackLevel) } // SetReadLevel mocks base method. func (m *MockAckManager) SetReadLevel(readLevel int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetReadLevel", readLevel) } // SetReadLevel indicates an expected call of SetReadLevel. func (mr *MockAckManagerMockRecorder) SetReadLevel(readLevel any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadLevel", reflect.TypeOf((*MockAckManager)(nil).SetReadLevel), readLevel) } ================================================ FILE: common/messaging/errors.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package messaging import "errors" var ( // ErrMessageSizeLimit indicate that message is rejected by server due to size limitation ErrMessageSizeLimit = errors.New("message was too large, server rejected it to avoid allocation error") ) ================================================ FILE: common/messaging/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination client_mock.go package messaging import "context" type ( // Client is the interface used to abstract out interaction with messaging system for replication Client interface { NewConsumer(appName, consumerName string) (Consumer, error) NewProducer(appName string) (Producer, error) } // Consumer is the unified interface for both internal and external kafka clients Consumer interface { // Start starts the consumer Start() error // Stop stops the consumer Stop() // Messages return the message channel for this consumer Messages() <-chan Message } // Message is the unified interface for a Kafka message Message interface { // Value is a mutable reference to the message's value Value() []byte // Partition is the ID of the partition from which the message was read. Partition() int32 // Offset is the message's offset. Offset() int64 // Ack marks the message as successfully processed. Ack() error // Nack marks the message processing as failed and the message will be retried or sent to DLQ. Nack() error } // Producer is the interface used to send replication tasks to other clusters through replicator Producer interface { Publish(ctx context.Context, message interface{}) error } // CloseableProducer is a Producer that can be closed CloseableProducer interface { Producer Close() error } // AckManager convert out of order acks into ackLevel movement. AckManager interface { // Read an item into backlog for processing for ack ReadItem(id int64) error // Get current max ID from read items GetReadLevel() int64 // Set current max ID from read items SetReadLevel(readLevel int64) // Mark an item as done processing, and remove from backlog AckItem(id int64) (ackLevel int64) // Get current max level that can safely ack GetAckLevel() int64 // Set current max level that can safely ack SetAckLevel(ackLevel int64) // GetBacklogCount return the of items that are waiting for ack GetBacklogCount() int64 } ) ================================================ FILE: common/messaging/kafka/client_impl.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package kafka import ( "fmt" "strings" "time" "github.com/IBM/sarama" "github.com/uber-go/tally" "github.com/uber/cadence/common/authorization" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" ) type ( // This is a default implementation of Client interface which makes use of uber-go/kafka-client as consumer clientImpl struct { config *config.KafkaConfig metricsClient metrics.Client logger log.Logger } ) var _ messaging.Client = (*clientImpl)(nil) // NewKafkaClient is used to create an instance of KafkaClient func NewKafkaClient( kc *config.KafkaConfig, metricsClient metrics.Client, logger log.Logger, _ tally.Scope, checkApp bool, ) messaging.Client { kc.Validate(checkApp) // mapping from cluster name to list of broker ip addresses brokers := map[string][]string{} for cluster, cfg := range kc.Clusters { brokers[cluster] = cfg.Brokers for i := range brokers[cluster] { if !strings.Contains(cfg.Brokers[i], ":") { cfg.Brokers[i] += ":9092" } } } // mapping from topic name to cluster that has that topic topicClusterAssignment := map[string][]string{} for topic, cfg := range kc.Topics { topicClusterAssignment[topic] = []string{cfg.Cluster} } return &clientImpl{ config: kc, metricsClient: metricsClient, logger: logger, } } // NewConsumer is used to create a Kafka consumer func (c *clientImpl) NewConsumer(app, consumerName string) (messaging.Consumer, error) { topics := c.config.GetTopicsForApplication(app) // All defaut values are copied from uber/kafka-clientImpl bo keep the same behavior kafkaVersion := c.config.Version if kafkaVersion == "" { kafkaVersion = "0.10.2.0" } version, err := sarama.ParseKafkaVersion(kafkaVersion) if err != nil { return nil, err } saramaConfig := sarama.NewConfig() saramaConfig.Version = version saramaConfig.Consumer.Fetch.Default = 30 * 1024 * 1024 // 30MB. saramaConfig.Consumer.Return.Errors = true saramaConfig.Consumer.Offsets.CommitInterval = time.Second saramaConfig.Consumer.Offsets.Initial = sarama.OffsetOldest saramaConfig.Consumer.MaxProcessingTime = 250 * time.Millisecond err = c.initAuth(saramaConfig) if err != nil { return nil, err } dlqProducer, err := c.newProducerByTopic(topics.DLQTopic) if err != nil { return nil, err } clusterName := c.config.GetKafkaClusterForTopic(topics.Topic) brokers := c.config.GetBrokersForKafkaCluster(clusterName) return NewKafkaConsumer(dlqProducer, brokers, topics.Topic, consumerName, saramaConfig, c.metricsClient, c.logger) } // NewProducer is used to create a Kafka producer func (c *clientImpl) NewProducer(app string) (messaging.Producer, error) { topics := c.config.GetTopicsForApplication(app) return c.newProducerByTopic(topics.Topic) } func (c *clientImpl) newProducerByTopic(topic string) (messaging.Producer, error) { kafkaClusterName := c.config.GetKafkaClusterForTopic(topic) brokers := c.config.GetBrokersForKafkaCluster(kafkaClusterName) config := sarama.NewConfig() config.Producer.Return.Successes = true err := c.initAuth(config) if err != nil { return nil, err } producer, err := sarama.NewSyncProducer(brokers, config) if err != nil { return nil, err } if c.metricsClient != nil { c.logger.Info("Create producer with metricsClient") withMetricsOpt := messaging.WithMetricTags(metrics.TopicTag(topic)) return messaging.NewMetricProducer(NewKafkaProducer(topic, producer, c.logger), c.metricsClient, withMetricsOpt), nil } return NewKafkaProducer(topic, producer, c.logger), nil } func (c *clientImpl) initAuth(saramaConfig *sarama.Config) error { tlsConfig, err := c.config.TLS.ToTLSConfig() if err != nil { panic(fmt.Sprintf("Error creating Kafka TLS config %v", err)) } // TLS support saramaConfig.Net.TLS.Enable = tlsConfig != nil saramaConfig.Net.TLS.Config = tlsConfig // SASL support saramaConfig.Net.SASL.Enable = c.config.SASL.Enabled saramaConfig.Net.SASL.User = c.config.SASL.User saramaConfig.Net.SASL.Password = c.config.SASL.Password saramaConfig.Net.SASL.Handshake = true if c.config.SASL.Enabled { if c.config.SASL.Algorithm == "sha512" { saramaConfig.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient { return &authorization.XDGSCRAMClient{HashGeneratorFcn: authorization.SHA512} } saramaConfig.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA512 } else if c.config.SASL.Algorithm == "sha256" { saramaConfig.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient { return &authorization.XDGSCRAMClient{HashGeneratorFcn: authorization.SHA256} } saramaConfig.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA256 } else if c.config.SASL.Algorithm == "plain" { saramaConfig.Net.SASL.Mechanism = sarama.SASLTypePlaintext } else { return fmt.Errorf("invalid SHA algorithm %s: can be either sha256 or sha512", c.config.SASL.Algorithm) } } return nil } ================================================ FILE: common/messaging/kafka/client_impl_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) func TestNewKafkaClient(t *testing.T) { metricsClient := metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) logger := testlogger.New(t) testCases := []struct { name string config *config.KafkaConfig checkApp bool expectedErr string }{ { name: "Missing clusters", config: &config.KafkaConfig{ Clusters: map[string]config.ClusterConfig{}, }, checkApp: true, expectedErr: "Empty Kafka Cluster Config", }, { name: "Missing topics", config: &config.KafkaConfig{ Clusters: map[string]config.ClusterConfig{ "testCluster": { Brokers: []string{"testBrokers"}, }, }, Topics: map[string]config.TopicConfig{}, }, checkApp: true, expectedErr: "Empty Topics Config", }, { name: "Missing Applications", config: &config.KafkaConfig{ Clusters: map[string]config.ClusterConfig{ "test-cluster": { Brokers: []string{"test-brokers"}, }, }, Topics: map[string]config.TopicConfig{ "test-topic": { Cluster: "test-cluster", }, }, Applications: map[string]config.TopicList{}, }, checkApp: true, expectedErr: "Empty Applications Config", }, { name: "Missing topics config", config: &config.KafkaConfig{ Clusters: map[string]config.ClusterConfig{ "test-cluster": { Brokers: []string{"test-brokers"}, }, }, Topics: map[string]config.TopicConfig{ "test-topic": { Cluster: "test-cluster", }, }, Applications: map[string]config.TopicList{ "test-app": { Topic: "test-topic", DLQTopic: "test-topic-dlq", }, }, }, checkApp: true, expectedErr: "Missing Topic Config for Topic test-topic-dlq", }, { name: "Normal Case", config: &config.KafkaConfig{ Clusters: map[string]config.ClusterConfig{ "test-cluster": { Brokers: []string{"test-brokers"}, }, }, Topics: map[string]config.TopicConfig{ "test-topic": { Cluster: "test-cluster", }, "test-topic-dlq": { Cluster: "test-cluster", }, }, Applications: map[string]config.TopicList{ "test-app": { Topic: "test-topic", DLQTopic: "test-topic-dlq", }, }, }, checkApp: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { defer func() { if r := recover(); r != nil { assert.Equal(t, tc.expectedErr, r) } }() kafkaClient := NewKafkaClient(tc.config, metricsClient, logger, nil, tc.checkApp) // Type assert to *clientImpl to access struct fields client, ok := kafkaClient.(*clientImpl) assert.True(t, ok, "Expected kafkaClient to be of type *clientImpl") assert.Equal(t, tc.config, client.config) }) } } ================================================ FILE: common/messaging/kafka/consumer_impl.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package kafka import ( "sync" "time" "github.com/IBM/sarama" "golang.org/x/net/context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" ) const rcvBufferSize = 2 * 1024 const dlqPublishTimeout = time.Minute type ( // a wrapper of sarama consumer group for our consumer interface consumerImpl struct { topic string consumerHandler *consumerHandlerImpl consumerGroup sarama.ConsumerGroup msgChan <-chan messaging.Message wg sync.WaitGroup cancelFunc context.CancelFunc logger log.Logger } // consumerHandlerImpl represents a Sarama consumer group consumer // It's for passing into sarama consumer group API consumerHandlerImpl struct { sync.RWMutex dlqProducer messaging.Producer topic string currentSession sarama.ConsumerGroupSession msgChan chan<- messaging.Message manager *partitionAckManager metricsClient metrics.Client logger log.Logger throttleRetry *backoff.ThrottleRetry } messageImpl struct { saramaMsg *sarama.ConsumerMessage session sarama.ConsumerGroupSession handler *consumerHandlerImpl logger log.Logger } ) var _ messaging.Message = (*messageImpl)(nil) var _ messaging.Consumer = (*consumerImpl)(nil) func NewKafkaConsumer( dlqProducer messaging.Producer, brokers []string, topic string, consumerName string, saramaConfig *sarama.Config, metricsClient metrics.Client, logger log.Logger, ) (messaging.Consumer, error) { consumerGroup, err := sarama.NewConsumerGroup(brokers, consumerName, saramaConfig) if err != nil { return nil, err } msgChan := make(chan messaging.Message, rcvBufferSize) consumerHandler := newConsumerHandlerImpl(dlqProducer, topic, msgChan, metricsClient, logger) return &consumerImpl{ topic: topic, consumerHandler: consumerHandler, consumerGroup: consumerGroup, msgChan: msgChan, logger: logger, }, nil } func (c *consumerImpl) Start() error { ctx, cancel := context.WithCancel(context.Background()) c.cancelFunc = cancel c.wg.Add(1) // consumer loop go func() { defer c.wg.Done() for { // `Consume` should be called inside an infinite loop, when a // server-side rebalance happens, the consumer session will need to be // recreated to get the new claims if err := c.consumerGroup.Consume(ctx, []string{c.topic}, c.consumerHandler); err != nil { c.logger.Error("Error from consumer: %v", tag.Error(err)) } // check if context was cancelled, signaling that the consumer should stop if ctx.Err() != nil { c.logger.Info("context was cancel, stopping consumer loop") return } } }() return nil } // Stop stops the consumer func (c *consumerImpl) Stop() { c.logger.Info("Stopping consumer") c.cancelFunc() c.logger.Info("Waiting consumer goroutines to complete") c.wg.Wait() c.logger.Info("Stopping consumer handler and group") c.consumerHandler.stop() c.consumerGroup.Close() c.logger.Info("Stopped consumer") } // Messages return the message channel for this consumer func (c *consumerImpl) Messages() <-chan messaging.Message { return c.msgChan } func newConsumerHandlerImpl( dlqProducer messaging.Producer, topic string, msgChan chan<- messaging.Message, metricsClient metrics.Client, logger log.Logger, ) *consumerHandlerImpl { return &consumerHandlerImpl{ dlqProducer: dlqProducer, topic: topic, msgChan: msgChan, metricsClient: metricsClient, logger: logger, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(common.CreateDlqPublishRetryPolicy()), backoff.WithRetryableError(func(_ error) bool { return true }), ), } } // Setup is run at the beginning of a new session, before ConsumeClaim func (h *consumerHandlerImpl) Setup(session sarama.ConsumerGroupSession) error { h.Lock() defer h.Unlock() h.currentSession = session h.manager = newPartitionAckManager(h.metricsClient, h.logger) h.logger.Info("start consumer group session", tag.Name(string(session.GenerationID()))) h.metricsClient.IncCounter(metrics.MessagingClientConsumerScope, metrics.KafkaConsumerSessionStart) return nil } func (h *consumerHandlerImpl) getCurrentSession() sarama.ConsumerGroupSession { h.RLock() defer h.RUnlock() return h.currentSession } func (h *consumerHandlerImpl) completeMessage(message *messageImpl, isAck bool) error { h.RLock() defer h.RUnlock() if !isAck { op := func(ctx context.Context) error { // NOTE: current KafkaProducer is not taking use the this context, because saramaProducer doesn't support it // https://github.com/IBM/sarama/issues/1849 ctx, cancel := context.WithTimeout(ctx, dlqPublishTimeout) err := h.dlqProducer.Publish(ctx, message.saramaMsg) cancel() return err } err := h.throttleRetry.Do(context.Background(), op) if err != nil { h.metricsClient.IncCounter(metrics.MessagingClientConsumerScope, metrics.KafkaConsumerMessageNackDlqErr) h.logger.Error("Fail to publish message to DLQ when nacking message, please take action!!", tag.KafkaPartition(message.Partition()), tag.KafkaOffset(message.Offset())) } else { h.logger.Warn("nack message and publish to DLQ", tag.KafkaPartition(message.Partition()), tag.KafkaOffset(message.Offset())) } } ackLevel, err := h.manager.CompleteMessage(message.Partition(), message.Offset(), isAck) if err != nil { h.logger.Error("Failed to complete an message that hasn't been added to the partition", tag.KafkaPartition(message.Partition()), tag.KafkaOffset(message.Offset())) return err } h.currentSession.MarkOffset(h.topic, message.Partition(), ackLevel+1, "") return nil } // Cleanup is run at the end of a session, once all ConsumeClaim goroutines have exited func (h *consumerHandlerImpl) Cleanup(sarama.ConsumerGroupSession) error { return nil } // ConsumeClaim must start a consumer loop of ConsumerGroupClaim's Messages(). func (h *consumerHandlerImpl) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error { h.RLock() defer h.RUnlock() // NOTE: Do not move the code below to a goroutine. // The `ConsumeClaim` itself is called within a goroutine: for message := range claim.Messages() { h.manager.AddMessage(message.Partition, message.Offset) h.msgChan <- &messageImpl{ saramaMsg: message, session: session, handler: h, logger: h.logger, } } return nil } func (h *consumerHandlerImpl) stop() { close(h.msgChan) } func (m *messageImpl) Value() []byte { return m.saramaMsg.Value } func (m *messageImpl) Partition() int32 { return m.saramaMsg.Partition } func (m *messageImpl) Offset() int64 { return m.saramaMsg.Offset } func (m *messageImpl) Ack() error { if m.isFromPreviousSession() { return nil } return m.handler.completeMessage(m, true) } func (m *messageImpl) Nack() error { if m.isFromPreviousSession() { return nil } return m.handler.completeMessage(m, false) } func (m *messageImpl) isFromPreviousSession() bool { if m.session.GenerationID() != m.handler.getCurrentSession().GenerationID() { m.logger.Warn("Skip message that is from a previous session", tag.KafkaPartition(m.saramaMsg.Partition), tag.KafkaOffset(m.saramaMsg.Offset), tag.Name(string(m.session.GenerationID())), tag.Value(m.handler.getCurrentSession().GenerationID()), ) return true } return false } ================================================ FILE: common/messaging/kafka/consumer_impl_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import ( "context" "testing" "github.com/IBM/sarama" "github.com/IBM/sarama/mocks" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" ) func TestNewConsumer(t *testing.T) { mockProducer := mocks.NewSyncProducer(t, nil) group := "tests" mockBroker := initMockBroker(t, group) defer mockBroker.Close() brokerAddr := []string{mockBroker.Addr()} kafkaConfig := &config.KafkaConfig{ Clusters: map[string]config.ClusterConfig{ "test-cluster": { Brokers: brokerAddr, }, }, Topics: map[string]config.TopicConfig{ "test-topic": { Cluster: "test-cluster", }, "test-topic-dlq": { Cluster: "test-cluster", }, }, Applications: map[string]config.TopicList{ "test-app": { Topic: "test-topic", DLQTopic: "test-topic-dlq", }, }, } topic := "test-topic" consumerName := "test-consumer" metricsClient := metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) logger := testlogger.New(t) kafkaProducer := NewKafkaProducer(topic, mockProducer, logger) clusterName := kafkaConfig.GetKafkaClusterForTopic(topic) brokers := kafkaConfig.GetBrokersForKafkaCluster(clusterName) consumer, err := NewKafkaConsumer(kafkaProducer, brokers, topic, consumerName, nil, metricsClient, logger) assert.NoError(t, err, "An error was not expected but got %v", err) assert.NotNil(t, consumer, "Expected consumer but got nil") err = consumer.Start() assert.NoError(t, err) consumer.Stop() } func TestNewConsumerHandlerImpl(t *testing.T) { topic := "test-topic" metricsClient := metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) logger := testlogger.New(t) mockProducer := mocks.NewSyncProducer(t, nil) kafkaProducer := NewKafkaProducer(topic, mockProducer, logger) msgChan := make(chan messaging.Message, 1) consumerHandler := newConsumerHandlerImpl(kafkaProducer, topic, msgChan, metricsClient, logger) assert.NotNil(t, consumerHandler) assert.Equal(t, kafkaProducer, consumerHandler.dlqProducer) assert.Equal(t, topic, consumerHandler.topic) // Close the channel at the end of the test close(msgChan) } // test multiple methods related to messageImpl since setup is repeated func TestMessageImpl(t *testing.T) { topic := "test-topic" metricsClient := metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) logger := testlogger.New(t) mockProducer := mocks.NewSyncProducer(t, nil) kafkaProducer := NewKafkaProducer(topic, mockProducer, logger) msgChan := make(chan messaging.Message, 1) consumerHandler := newConsumerHandlerImpl(kafkaProducer, topic, msgChan, metricsClient, logger) consumerGroupSession := NewMockConsumerGroupSession(int32(1)) partition := int32(100) offset := int64(0) msgImpl := &messageImpl{ saramaMsg: &sarama.ConsumerMessage{ Topic: topic, Partition: partition, Offset: offset, }, session: consumerGroupSession, handler: consumerHandler, logger: logger, } // Ack message that is from a previous session err := msgImpl.handler.Setup(NewMockConsumerGroupSession(int32(2))) assert.NoError(t, err) err = msgImpl.Ack() assert.NoError(t, err) // normal case err = msgImpl.handler.Setup(NewMockConsumerGroupSession(int32(1))) assert.NoError(t, err) msgImpl.handler.manager.AddMessage(partition, offset) err = msgImpl.Ack() assert.NoError(t, err) // Nack message that is from a previous session err = msgImpl.handler.Setup(NewMockConsumerGroupSession(int32(2))) assert.NoError(t, err) err = msgImpl.Nack() assert.NoError(t, err) // normal case err = msgImpl.handler.Setup(NewMockConsumerGroupSession(int32(1))) assert.NoError(t, err) mockProducer.ExpectSendMessageAndSucceed() msgImpl.handler.manager.AddMessage(partition, offset) err = msgImpl.Nack() assert.NoError(t, err) close(msgChan) } // MockConsumerGroupSession implements sarama.ConsumerGroupSession for testing purposes. type MockConsumerGroupSession struct { claims map[string][]int32 memberID string generationID int32 offsets map[string]map[int32]int64 commitCalled bool context context.Context } func NewMockConsumerGroupSession(generationID int32) *MockConsumerGroupSession { return &MockConsumerGroupSession{ claims: map[string][]int32{}, memberID: "test-member", generationID: generationID, offsets: map[string]map[int32]int64{}, commitCalled: false, } } func (m *MockConsumerGroupSession) Claims() map[string][]int32 { return m.claims } func (m *MockConsumerGroupSession) MemberID() string { return m.memberID } func (m *MockConsumerGroupSession) GenerationID() int32 { return m.generationID } func (m *MockConsumerGroupSession) MarkOffset(topic string, partition int32, offset int64, metadata string) { // not needed for testing } func (m *MockConsumerGroupSession) Commit() { // not needed for testing } func (m *MockConsumerGroupSession) ResetOffset(topic string, partition int32, offset int64, metadata string) { // not needed for testing } func (m *MockConsumerGroupSession) MarkMessage(msg *sarama.ConsumerMessage, metadata string) { // not needed for testing } func (m *MockConsumerGroupSession) Context() context.Context { // Return a context, you can use context.Background() or a custom context if needed return m.context } func initMockBroker(t *testing.T, group string) *sarama.MockBroker { topics := []string{"test-topic"} mockBroker := sarama.NewMockBroker(t, 0) mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ "MetadataRequest": sarama.NewMockMetadataResponse(t). SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). SetLeader(topics[0], 0, mockBroker.BrokerID()). SetController(mockBroker.BrokerID()), "FindCoordinatorRequest": sarama.NewMockFindCoordinatorResponse(t). SetCoordinator(sarama.CoordinatorGroup, group, mockBroker), }) return mockBroker } ================================================ FILE: common/messaging/kafka/partition_ack_manager.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package kafka import ( "errors" "sync" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" ) // Used to convert out of order acks into ackLevel movement, // assuming reading messages is in order and continuous(no skipping) type partitionAckManager struct { sync.RWMutex ackMgrs map[int32]messaging.AckManager // map from partition to its ackManager scopes map[int32]metrics.Scope // map from partition to its Scope metricsClient metrics.Client logger log.Logger } func newPartitionAckManager(metricsClient metrics.Client, logger log.Logger) *partitionAckManager { return &partitionAckManager{ ackMgrs: make(map[int32]messaging.AckManager), scopes: make(map[int32]metrics.Scope), metricsClient: metricsClient, logger: logger, } } // AddMessage mark a messageID as read and waiting for completion func (pam *partitionAckManager) AddMessage(partitionID int32, messageID int64) { var err error pam.RLock() if am, ok := pam.ackMgrs[partitionID]; ok { err = am.ReadItem(messageID) pam.scopes[partitionID].IncCounter(metrics.KafkaConsumerMessageIn) pam.RUnlock() } else { pam.RUnlock() pam.Lock() partitionLogger := pam.logger.WithTags(tag.KafkaPartition(partitionID)) am := messaging.NewContinuousAckManager(partitionLogger) pam.ackMgrs[partitionID] = am scope := pam.metricsClient.Scope(metrics.MessagingClientConsumerScope, metrics.KafkaPartitionTag(partitionID)) pam.scopes[partitionID] = scope scope.IncCounter(metrics.KafkaConsumerMessageIn) err = am.ReadItem(messageID) pam.Unlock() } if err != nil { pam.logger.Warn("potential bug when adding message to ackManager", tag.Error(err), tag.KafkaPartition(partitionID), tag.TaskID(messageID)) } } // CompleteMessage complete the message from ack/nack kafka message func (pam *partitionAckManager) CompleteMessage(partitionID int32, messageID int64, isAck bool) (ackLevel int64, err error) { pam.RLock() defer pam.RUnlock() if am, ok := pam.ackMgrs[partitionID]; ok { ackLevel = am.AckItem(messageID) if isAck { pam.scopes[partitionID].IncCounter(metrics.KafkaConsumerMessageAck) } else { pam.scopes[partitionID].IncCounter(metrics.KafkaConsumerMessageNack) } } else { return -1, errors.New("Failed to complete an message that hasn't been added to the partition") } return ackLevel, nil } ================================================ FILE: common/messaging/kafka/partition_ack_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) func TestAddMessage(t *testing.T) { metricsClient := metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) logger := testlogger.New(t) // Mocked pam := newPartitionAckManager(metricsClient, logger) partitionID := int32(1) messageID := int64(100) // Test adding a message pam.AddMessage(partitionID, messageID) // Verify the message is added to the ack manager pam.RLock() // Read lock since we are only reading data _, ok := pam.ackMgrs[partitionID] pam.RUnlock() assert.True(t, ok, "AckManager for partition %v was not created", partitionID) } func TestCompleteMessage(t *testing.T) { // Setup metricsClient := metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) logger := testlogger.New(t) pam := newPartitionAckManager(metricsClient, logger) partitionID := int32(1) messageID := int64(100) testCases := []struct { name string partitionID int32 messageID int64 isAck bool expected int64 hasErr bool }{ { name: "Acknowledge the message", partitionID: partitionID, messageID: messageID, isAck: true, expected: int64(100), hasErr: false, }, { name: "Not acknowledge the message", partitionID: partitionID, messageID: messageID, isAck: false, expected: int64(100), hasErr: false, }, { name: "Not exist partition", partitionID: partitionID + 1, messageID: messageID, isAck: true, expected: int64(-1), hasErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Add a message first to simulate a real-world scenario, this will create ackMgr for partition pam.AddMessage(partitionID, messageID) ackLevel, err := pam.CompleteMessage(tc.partitionID, tc.messageID, tc.isAck) assert.True(t, ackLevel == tc.expected, "Test case %s failed: expected ackLevel %d, got %d", tc.name, tc.expected, ackLevel) if tc.hasErr { assert.Error(t, err, "Expected an error but none was found") } else { assert.NoError(t, err, "An error was not expected but got %v", err) } }) } } ================================================ FILE: common/messaging/kafka/producer_impl.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package kafka import ( "context" "errors" "github.com/IBM/sarama" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/.gen/go/sqlblobs" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" ) type ( producerImpl struct { topic string producer sarama.SyncProducer msgEncoder codec.BinaryEncoder logger log.Logger } ) var _ messaging.Producer = (*producerImpl)(nil) // NewKafkaProducer is used to create the Kafka based producer implementation func NewKafkaProducer(topic string, producer sarama.SyncProducer, logger log.Logger) messaging.Producer { return &producerImpl{ topic: topic, producer: producer, msgEncoder: codec.NewThriftRWEncoder(), logger: logger.WithTags(tag.KafkaTopicName(topic)), } } // Publish is used to send messages to other clusters through Kafka topic // TODO implement context when https://github.com/IBM/sarama/issues/1849 is supported func (p *producerImpl) Publish(_ context.Context, msg interface{}) error { message, err := p.getProducerMessage(msg) if err != nil { return err } partition, offset, err := p.producer.SendMessage(message) if err != nil { p.logger.Warn("Failed to publish message to kafka", tag.KafkaPartition(partition), tag.KafkaPartitionKey(message.Key), tag.KafkaOffset(offset), tag.Error(err)) return p.convertErr(err) } return nil } // Close is used to close Kafka publisher func (p *producerImpl) Close() error { return p.convertErr(p.producer.Close()) } func (p *producerImpl) serializeThrift(input codec.ThriftObject) ([]byte, error) { payload, err := p.msgEncoder.Encode(input) if err != nil { p.logger.Error("Failed to serialize thrift object", tag.Error(err)) return nil, err } return payload, nil } func (p *producerImpl) getProducerMessage(message interface{}) (*sarama.ProducerMessage, error) { switch message := message.(type) { case *indexer.Message: payload, err := p.serializeThrift(message) if err != nil { return nil, err } msg := &sarama.ProducerMessage{ Topic: p.topic, Key: sarama.StringEncoder(message.GetWorkflowID()), Value: sarama.ByteEncoder(payload), } return msg, nil case *sarama.ConsumerMessage: msg := &sarama.ProducerMessage{ Topic: p.topic, Key: sarama.ByteEncoder(message.Key), Value: sarama.ByteEncoder(message.Value), } return msg, nil case *indexer.PinotMessage: msg := &sarama.ProducerMessage{ Topic: p.topic, Key: sarama.StringEncoder(message.GetWorkflowID()), Value: sarama.ByteEncoder(message.GetPayload()), } return msg, nil case *sqlblobs.AsyncRequestMessage: payload, err := p.serializeThrift(message) if err != nil { return nil, err } msg := &sarama.ProducerMessage{ Topic: p.topic, Key: sarama.StringEncoder(message.GetPartitionKey()), Value: sarama.ByteEncoder(payload), } return msg, nil default: return nil, errors.New("unknown producer message type") } } func (p *producerImpl) convertErr(err error) error { switch err { case sarama.ErrMessageSizeTooLarge: return messaging.ErrMessageSizeLimit default: return err } } ================================================ FILE: common/messaging/kafka/producer_impl_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package kafka import ( "context" "testing" "github.com/IBM/sarama/mocks" "github.com/stretchr/testify/assert" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/testlogger" ) func TestNewKafkaProducer(t *testing.T) { // Create a mock sarama.SyncProducer mockProducer := mocks.NewSyncProducer(t, nil) logger := testlogger.New(t) topic := "test-topic" kafkaProducer := NewKafkaProducer(topic, mockProducer, logger) // Type assert to *producerImpl to access struct fields producedImpl, ok := kafkaProducer.(*producerImpl) assert.True(t, ok, "Expected kafkaProducer to be of type *producerImpl") // Assert that all fields are correctly set assert.Equal(t, topic, producedImpl.topic) assert.Equal(t, mockProducer, producedImpl.producer) assert.NotNil(t, producedImpl.logger) assert.NotNil(t, producedImpl.msgEncoder) } func TestPublish(t *testing.T) { msgType := indexer.MessageTypeIndex testCases := []struct { name string message interface{} hasErr bool }{ { name: "Publish message succeeded", message: &indexer.Message{ MessageType: &msgType, DomainID: common.StringPtr("test-domain-id"), WorkflowID: common.StringPtr("test-workflow-id"), RunID: common.StringPtr("test-workflow-run-id"), }, hasErr: false, }, { name: "Unrecognized message type", message: "This is not a recognized message type", hasErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Create a mock sarama.SyncProducer mockProducer := mocks.NewSyncProducer(t, nil) logger := testlogger.New(t) topic := "test-topic" ctx := context.Background() kafkaProducer := NewKafkaProducer(topic, mockProducer, logger) // Only expect SendMessage to be called if no error is expected if !tc.hasErr { mockProducer.ExpectSendMessageAndSucceed() } err := kafkaProducer.Publish(ctx, tc.message) if !tc.hasErr { assert.NoError(t, err, "An error was not expected but got %v", err) } else { assert.Error(t, err, "Expected an error but none was found") } }) } } ================================================ FILE: common/messaging/metrics_producer.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package messaging import ( "context" "github.com/uber/cadence/common/metrics" ) type MetricsProducer struct { producer Producer scope metrics.Scope tags []metrics.Tag } type MetricProducerOptions func(*MetricsProducer) func WithMetricTags(tags ...metrics.Tag) MetricProducerOptions { return func(p *MetricsProducer) { p.tags = tags } } // NewMetricProducer creates a new instance of producer that emits metrics func NewMetricProducer( producer Producer, metricsClient metrics.Client, opts ...MetricProducerOptions, ) Producer { p := &MetricsProducer{ producer: producer, } for _, opt := range opts { opt(p) } p.scope = metricsClient.Scope(metrics.MessagingClientPublishScope, p.tags...) return p } func (p *MetricsProducer) Publish(ctx context.Context, msg interface{}) error { p.scope.IncCounter(metrics.CadenceClientRequests) sw := p.scope.StartTimer(metrics.CadenceClientLatency) err := p.producer.Publish(ctx, msg) sw.Stop() if err != nil { p.scope.IncCounter(metrics.CadenceClientFailures) } return err } func (p *MetricsProducer) Close() error { if closeableProducer, ok := p.producer.(CloseableProducer); ok { return closeableProducer.Close() } return nil } ================================================ FILE: common/messaging/metrics_producer_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package messaging import ( "context" "errors" "testing" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/metrics/mocks" ) func TestPublish(t *testing.T) { tests := []struct { desc string tags []metrics.Tag producerFails bool metricsClientMockFn func() *mocks.Client }{ { desc: "success", producerFails: false, tags: []metrics.Tag{ metrics.TopicTag("test-topic-1"), }, metricsClientMockFn: func() *mocks.Client { metricsClient := &mocks.Client{} metricsScope := &mocks.Scope{} metricsClient. On("Scope", metrics.MessagingClientPublishScope, metrics.TopicTag("test-topic-1")). Return(metricsScope). Once() metricsScope.On("IncCounter", metrics.CadenceClientRequests).Once() sw := metrics.NoopScope.StartTimer(-1) metricsScope.On("StartTimer", metrics.CadenceClientLatency).Return(sw).Once() return metricsClient }, }, { desc: "failure", producerFails: true, tags: []metrics.Tag{ metrics.TopicTag("test-topic-2"), }, metricsClientMockFn: func() *mocks.Client { metricsClient := &mocks.Client{} metricsScope := &mocks.Scope{} metricsClient. On("Scope", metrics.MessagingClientPublishScope, metrics.TopicTag("test-topic-2")). Return(metricsScope). Once() metricsScope.On("IncCounter", metrics.CadenceClientRequests).Once() metricsScope.On("IncCounter", metrics.CadenceClientFailures).Once() sw := metrics.NoopScope.StartTimer(-1) metricsScope.On("StartTimer", metrics.CadenceClientLatency).Return(sw).Once() return metricsClient }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { // setup ctrl := gomock.NewController(t) mockProducer := NewMockProducer(ctrl) msg := "custom-message" if tc.producerFails { mockProducer.EXPECT().Publish(gomock.Any(), msg).Return(errors.New("publish failed")).Times(1) } else { mockProducer.EXPECT().Publish(gomock.Any(), msg).Return(nil).Times(1) } metricsClient := tc.metricsClientMockFn() // create producer and call publish p := NewMetricProducer(mockProducer, metricsClient, WithMetricTags(tc.tags...)) err := p.Publish(context.Background(), msg) // validations if tc.producerFails != (err != nil) { t.Errorf("expected producer to fail: %v, got: %v", tc.producerFails, err) } if err != nil { return } metricsClient.AssertExpectations(t) }) } } ================================================ FILE: common/messaging/mocks/message_mock.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mocks import mock "github.com/stretchr/testify/mock" // Message is an autogenerated mock type for the Message type type Message struct { mock.Mock } // Ack provides a mock function with given fields: func (_m *Message) Ack() error { ret := _m.Called() var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() } else { r0 = ret.Error(0) } return r0 } // Nack provides a mock function with given fields: func (_m *Message) Nack() error { ret := _m.Called() var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() } else { r0 = ret.Error(0) } return r0 } // Offset provides a mock function with given fields: func (_m *Message) Offset() int64 { ret := _m.Called() var r0 int64 if rf, ok := ret.Get(0).(func() int64); ok { r0 = rf() } else { r0 = ret.Get(0).(int64) } return r0 } // Partition provides a mock function with given fields: func (_m *Message) Partition() int32 { ret := _m.Called() var r0 int32 if rf, ok := ret.Get(0).(func() int32); ok { r0 = rf() } else { r0 = ret.Get(0).(int32) } return r0 } // Value provides a mock function with given fields: func (_m *Message) Value() []byte { ret := _m.Called() var r0 []byte if rf, ok := ret.Get(0).(func() []byte); ok { r0 = rf() } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]byte) } } return r0 } ================================================ FILE: common/messaging/noop_producer.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package messaging import "context" type noopProducer struct{} // NewNoopProducer returns a no-op message producer func NewNoopProducer() Producer { return &noopProducer{} } func (p noopProducer) Publish( _ context.Context, _ interface{}, ) error { return nil } ================================================ FILE: common/metrics/client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import ( "time" "github.com/uber-go/tally" ) // ClientImpl is used for reporting metrics by various Cadence services type ClientImpl struct { // parentReporter is the parent scope for the metrics parentScope tally.Scope childScopes map[ScopeIdx]tally.Scope metricDefs map[MetricIdx]metricDefinition serviceIdx ServiceIdx config HistogramMigration } // NewClient creates and returns a new instance of // Client implementation // reporter holds the common tags for the service // serviceIdx indicates the service type in (InputhostIndex, ... StorageIndex) func NewClient(scope tally.Scope, serviceIdx ServiceIdx, config HistogramMigration) Client { totalScopes := len(ScopeDefs[Common]) + len(ScopeDefs[serviceIdx]) metricsClient := &ClientImpl{ parentScope: scope, childScopes: make(map[ScopeIdx]tally.Scope, totalScopes), metricDefs: getMetricDefs(serviceIdx), serviceIdx: serviceIdx, config: config, } for idx, def := range ScopeDefs[Common] { scopeTags := map[string]string{ OperationTagName: def.operation, } mergeMapToRight(def.tags, scopeTags) metricsClient.childScopes[idx] = scope.Tagged(scopeTags) } for idx, def := range ScopeDefs[serviceIdx] { scopeTags := map[string]string{ OperationTagName: def.operation, } mergeMapToRight(def.tags, scopeTags) metricsClient.childScopes[idx] = scope.Tagged(scopeTags) } return metricsClient } // IncCounter increments one for a counter and emits // to metrics backend func (m *ClientImpl) IncCounter(scope ScopeIdx, counterIdx MetricIdx) { name := string(m.metricDefs[counterIdx].metricName) m.childScopes[scope].Counter(name).Inc(1) } // AddCounter adds delta to the counter and // emits to the metrics backend func (m *ClientImpl) AddCounter(scope ScopeIdx, counterIdx MetricIdx, delta int64) { name := string(m.metricDefs[counterIdx].metricName) m.childScopes[scope].Counter(name).Inc(delta) } // StartTimer starts a timer for the given // metric name func (m *ClientImpl) StartTimer(scope ScopeIdx, timerIdx MetricIdx) tally.Stopwatch { name := string(m.metricDefs[timerIdx].metricName) if m.config.EmitTimer(name) { return m.childScopes[scope].Timer(name).Start() } return NoopStopwatch } // RecordTimer record and emit a timer for the given // metric name func (m *ClientImpl) RecordTimer(scope ScopeIdx, timerIdx MetricIdx, d time.Duration) { name := string(m.metricDefs[timerIdx].metricName) if m.config.EmitTimer(name) { m.childScopes[scope].Timer(name).Record(d) } } // RecordHistogramDuration record and emit a duration func (m *ClientImpl) RecordHistogramDuration(scope ScopeIdx, timerIdx MetricIdx, d time.Duration) { name := string(m.metricDefs[timerIdx].metricName) if m.config.EmitHistogram(name) { m.childScopes[scope].Histogram(name, m.getBuckets(timerIdx)).RecordDuration(d) } } // UpdateGauge reports Gauge type metric func (m *ClientImpl) UpdateGauge(scopeIdx ScopeIdx, gaugeIdx MetricIdx, value float64) { name := string(m.metricDefs[gaugeIdx].metricName) m.childScopes[scopeIdx].Gauge(name).Update(value) } // Scope return a new internal metrics scope that can be used to add additional // information to the metrics emitted func (m *ClientImpl) Scope(scope ScopeIdx, tags ...Tag) Scope { sc := m.childScopes[scope] return newMetricsScope(sc, sc, m.metricDefs, false, m.config).Tagged(tags...) } func (m *ClientImpl) getBuckets(id MetricIdx) tally.Buckets { if m.metricDefs[id].buckets != nil { return m.metricDefs[id].buckets } return tally.DefaultBuckets } func getMetricDefs(serviceIdx ServiceIdx) map[MetricIdx]metricDefinition { defs := make(map[MetricIdx]metricDefinition) for idx, def := range MetricDefs[Common] { defs[idx] = def } for idx, def := range MetricDefs[serviceIdx] { defs[idx] = def } return defs } func mergeMapToRight(src map[string]string, dest map[string]string) { for k, v := range src { dest[k] = v } } ================================================ FILE: common/metrics/config.go ================================================ package metrics import "fmt" type HistogramMigration struct { Default HistogramMigrationMode `yaml:"default"` // Names maps "metric name" -> "should it be emitted". // // If a name/key does not exist, the default mode will be checked to determine // if a timer or histogram should be emitted. // // This is only checked for timers and histograms that are in HistogramMigrationMetrics. Names map[string]bool `yaml:"names"` } func (h *HistogramMigration) UnmarshalYAML(read func(any) error) error { type tmpType HistogramMigration // without the custom unmarshaler var tmp tmpType if err := read(&tmp); err != nil { return err } for k := range tmp.Names { if _, ok := HistogramMigrationMetrics[k]; !ok { return fmt.Errorf( "unknown histogram-migration metric name %q. "+ "if this is a valid name, add it to common/metrics.HistogramMigrationMetrics before starting the service", k, ) } } *h = HistogramMigration(tmp) return nil } // HistogramMigrationMetrics contains all metric names being migrated, to prevent affecting // non-migration-related timers and histograms, and to catch metric name config // mistakes early on. // // It is public to allow Cadence operators to add to the collection before // loading config, in case they have any custom migrations to perform. // This is likely best done in an `init` func, to ensure it happens early enough // and does not race with config reading. var HistogramMigrationMetrics = map[string]struct{}{ "task_attempt": {}, "task_attempt_counts": {}, "task_attempt_per_domain": {}, "task_attempt_per_domain_counts": {}, "task_latency": {}, "task_latency_ns": {}, "task_latency_per_domain": {}, "task_latency_per_domain_ns": {}, "task_latency_processing": {}, "task_latency_processing_ns": {}, "task_latency_queue": {}, "task_latency_queue_ns": {}, "task_latency_processing_per_domain": {}, "task_latency_processing_per_domain_ns": {}, "task_latency_queue_per_domain": {}, "task_latency_queue_per_domain_ns": {}, "replication_tasks_lag": {}, "replication_tasks_lag_counts": {}, "replication_tasks_applied_latency": {}, "replication_tasks_applied_latency_ns": {}, "cache_latency": {}, "cache_latency_ns": {}, "cache_size": {}, "cache_size_counts": {}, "replication_task_latency": {}, "replication_task_latency_ns": {}, "replication_tasks_returned": {}, "replication_tasks_returned_counts": {}, "replication_tasks_returned_diff": {}, "replication_tasks_returned_diff_counts": {}, "replication_tasks_fetched": {}, "replication_tasks_fetched_counts": {}, "replication_tasks_lag_raw": {}, "replication_tasks_lag_raw_counts": {}, } func (h HistogramMigration) EmitTimer(name string) bool { if _, ok := HistogramMigrationMetrics[name]; !ok { return true } emit, ok := h.Names[name] if ok { return emit } return h.Default.EmitTimer() } func (h HistogramMigration) EmitHistogram(name string) bool { if _, ok := HistogramMigrationMetrics[name]; !ok { return true } emit, ok := h.Names[name] if ok { return emit } return h.Default.EmitHistogram() } // HistogramMigrationMode is a pseudo-enum to provide unmarshalling config and helper methods. // It should only be created by YAML unmarshaling, or by getting it from the HistogramMigration map. // Zero values from the map are valid, they are just the default mode (NOT the configured default). // // By default / when not specified / when an empty string, it currently means "timer". // This will likely change when most or all timers have histograms available, and will // eventually be fully deprecated and removed. type HistogramMigrationMode string func (h *HistogramMigrationMode) UnmarshalYAML(read func(any) error) error { var value string if err := read(&value); err != nil { return fmt.Errorf("cannot read histogram migration mode as a string: %w", err) } switch value { case "timer", "histogram", "both": *h = HistogramMigrationMode(value) default: return fmt.Errorf(`unsupported histogram migration mode %q, must be "timer", "histogram", or "both"`, value) } return nil } func (h HistogramMigrationMode) EmitTimer() bool { switch h { case "timer", "both", "": // default == not specified == both return true default: return false } } func (h HistogramMigrationMode) EmitHistogram() bool { switch h { case "histogram", "both": // default == not specified == both return true default: return false } } ================================================ FILE: common/metrics/config_test.go ================================================ package metrics import ( "testing" "golang.org/x/exp/maps" ) func TestHistogramMigrationMetricsExist(t *testing.T) { dup := maps.Clone(HistogramMigrationMetrics) for _, serviceMetrics := range MetricDefs { for _, def := range serviceMetrics { delete(dup, def.metricName.String()) } } if len(dup) != 0 { t.Error("HistogramMigrationMetrics contains metric names which do not exist:", dup) } } ================================================ FILE: common/metrics/context.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import "context" type contextTag string const contextTagsKey = contextTag("metrics.Tags") func TagContext(ctx context.Context, ctxTags ...Tag) context.Context { tags, ok := ctx.Value(contextTagsKey).([]Tag) if !ok { tags = []Tag{} } tags = append(tags, ctxTags...) return context.WithValue(ctx, contextTagsKey, tags) } func GetContextTags(ctx context.Context) []Tag { tags, ok := ctx.Value(contextTagsKey).([]Tag) if !ok { tags = []Tag{} } return tags } ================================================ FILE: common/metrics/context_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import ( "context" "testing" "github.com/stretchr/testify/assert" ) func TestContextTags(t *testing.T) { ctx := context.Background() assert.Empty(t, GetContextTags(ctx)) tag1 := DomainTag("domain") ctx = TagContext(ctx, tag1) assert.Equal(t, []Tag{tag1}, GetContextTags(ctx)) tag2 := TransportTag("grpc") ctx = TagContext(ctx, tag2) assert.Equal(t, []Tag{tag1, tag2}, GetContextTags(ctx)) ctx1 := context.Background() ctx1 = TagContext(ctx1, tag1, tag2) assert.Contains(t, GetContextTags(ctx1), tag1) assert.Contains(t, GetContextTags(ctx1), tag2) } ================================================ FILE: common/metrics/defs.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import ( "math" "time" "github.com/uber-go/tally" ) // types used/defined by the package type ( // MetricName is the name of the metric MetricName string // MetricType is the type of the metric MetricType int // metricDefinition contains the definition for a metric metricDefinition struct { metricType MetricType // metric type metricName MetricName // metric name metricRollupName MetricName // optional. if non-empty, this name must be used for rolled-up version of this metric buckets tally.Buckets // buckets if we are emitting histograms exponentialBuckets histogrammy[SubsettableHistogram] intExponentialBuckets histogrammy[IntSubsettableHistogram] } // scopeDefinition holds the tag definitions for a scope scopeDefinition struct { operation string // 'operation' tag for scope tags map[string]string // additional tags for scope } // ServiceIdx is an index that uniquely identifies the service ServiceIdx int // ScopeIdx is an index that uniquely identifies an operation, which is required to form a new metrics scope ScopeIdx int // MetricIdx is an index that uniquely identifies the metric definition MetricIdx int ) func (s scopeDefinition) GetOperationString() string { return s.operation } // MetricTypes which are supported const ( Counter MetricType = iota Timer Gauge Histogram ) // Service names for all services that emit metrics. const ( Common ServiceIdx = iota Frontend History Matching Worker ShardDistributor NumServices ) // This package should hold all the metrics and tags for cadence // Note that to better support Prometheus, metric name and tag name // should match the regex [a-zA-Z_][a-zA-Z0-9_]*, tag value can be any Unicode characters. // See more https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels // Common tags for all services const ( OperationTagName = "operation" CadenceRoleTagName = "cadence_role" CadenceServiceTagName = "cadence_service" StatsTypeTagName = "stats_type" CacheTypeTagName = "cache_type" ) // Common tag values const ( HistoryClientRoleTagValue = "history_client" MatchingClientRoleTagValue = "matching_client" FrontendClientRoleTagValue = "frontend_client" AdminClientRoleTagValue = "admin_client" DCRedirectionRoleTagValue = "dc_redirection" BlobstoreRoleTagValue = "blobstore" SizeStatsTypeTagValue = "size" CountStatsTypeTagValue = "count" MutableStateCacheTypeTagValue = "mutablestate" EventsCacheTypeTagValue = "events" ) // Common service base metrics const ( RestartCount = "restarts" NumGoRoutinesGauge = "num_goroutines" GoMaxProcsGauge = "gomaxprocs" MemoryAllocatedGauge = "memory_allocated" MemoryHeapGauge = "memory_heap" MemoryHeapIdleGauge = "memory_heapidle" MemoryHeapInuseGauge = "memory_heapinuse" MemoryStackGauge = "memory_stack" NumGCCounter = "memory_num_gc" GcPauseMsTimer = "memory_gc_pause_ms" ) // ServiceMetrics are types for common service base metrics var ServiceMetrics = map[MetricName]MetricType{ RestartCount: Counter, } // GoRuntimeMetrics represent the runtime stats from go runtime var GoRuntimeMetrics = map[MetricName]MetricType{ NumGoRoutinesGauge: Gauge, GoMaxProcsGauge: Gauge, MemoryAllocatedGauge: Gauge, MemoryHeapGauge: Gauge, MemoryHeapIdleGauge: Gauge, MemoryHeapInuseGauge: Gauge, MemoryStackGauge: Gauge, NumGCCounter: Counter, GcPauseMsTimer: Timer, } // Scopes enum const ( // -- Common Operation scopes -- // PersistenceCreateShardScope tracks CreateShard calls made by service to persistence layer PersistenceCreateShardScope ScopeIdx = iota // PersistenceGetShardScope tracks GetShard calls made by service to persistence layer PersistenceGetShardScope // PersistenceUpdateShardScope tracks UpdateShard calls made by service to persistence layer PersistenceUpdateShardScope // PersistenceCreateWorkflowExecutionScope tracks CreateWorkflowExecution calls made by service to persistence layer PersistenceCreateWorkflowExecutionScope // PersistenceGetWorkflowExecutionScope tracks GetWorkflowExecution calls made by service to persistence layer PersistenceGetWorkflowExecutionScope // PersistenceUpdateWorkflowExecutionScope tracks UpdateWorkflowExecution calls made by service to persistence layer PersistenceUpdateWorkflowExecutionScope // PersistenceConflictResolveWorkflowExecutionScope tracks ConflictResolveWorkflowExecution calls made by service to persistence layer PersistenceConflictResolveWorkflowExecutionScope // PersistenceResetWorkflowExecutionScope tracks ResetWorkflowExecution calls made by service to persistence layer PersistenceResetWorkflowExecutionScope // PersistenceDeleteWorkflowExecutionScope tracks DeleteWorkflowExecution calls made by service to persistence layer PersistenceDeleteWorkflowExecutionScope // PersistenceDeleteCurrentWorkflowExecutionScope tracks DeleteCurrentWorkflowExecution calls made by service to persistence layer PersistenceDeleteCurrentWorkflowExecutionScope // PersistenceGetCurrentExecutionScope tracks GetCurrentExecution calls made by service to persistence layer PersistenceGetCurrentExecutionScope // PersistenceIsWorkflowExecutionExistsScope tracks IsWorkflowExecutionExists calls made by service to persistence layer PersistenceIsWorkflowExecutionExistsScope // PersistenceListCurrentExecutionsScope tracks ListCurrentExecutions calls made by service to persistence layer PersistenceListCurrentExecutionsScope // PersistenceListConcreteExecutionsScope tracks ListConcreteExecutions calls made by service to persistence layer PersistenceListConcreteExecutionsScope // PersistenceGetTransferTasksScope tracks GetTransferTasks calls made by service to persistence layer PersistenceGetTransferTasksScope // PersistenceCompleteTransferTaskScope tracks CompleteTransferTasks calls made by service to persistence layer PersistenceCompleteTransferTaskScope // PersistenceGetCrossClusterTasksScope tracks GetCrossClusterTasks calls made by service to persistence layer PersistenceGetCrossClusterTasksScope // PersistenceCompleteCrossClusterTaskScope tracks CompleteCrossClusterTasks calls made by service to persistence layer PersistenceCompleteCrossClusterTaskScope // PersistenceGetReplicationTasksScope tracks GetReplicationTasks calls made by service to persistence layer PersistenceGetReplicationTasksScope // PersistenceCompleteReplicationTaskScope tracks CompleteReplicationTasks calls made by service to persistence layer PersistenceCompleteReplicationTaskScope // PersistencePutReplicationTaskToDLQScope tracks PersistencePutReplicationTaskToDLQScope calls made by service to persistence layer PersistencePutReplicationTaskToDLQScope // PersistenceGetReplicationTasksFromDLQScope tracks PersistenceGetReplicationTasksFromDLQScope calls made by service to persistence layer PersistenceGetReplicationTasksFromDLQScope // PersistenceGetReplicationDLQSizeScope tracks PersistenceGetReplicationDLQSizeScope calls made by service to persistence layer PersistenceGetReplicationDLQSizeScope // PersistenceDeleteReplicationTaskFromDLQScope tracks PersistenceDeleteReplicationTaskFromDLQScope calls made by service to persistence layer PersistenceDeleteReplicationTaskFromDLQScope // PersistenceRangeDeleteReplicationTaskFromDLQScope tracks PersistenceRangeDeleteReplicationTaskFromDLQScope calls made by service to persistence layer PersistenceRangeDeleteReplicationTaskFromDLQScope // PersistenceCreateFailoverMarkerTasksScope tracks CreateFailoverMarkerTasks calls made by service to persistence layer PersistenceCreateFailoverMarkerTasksScope // PersistenceGetTimerIndexTasksScope tracks GetTimerIndexTasks calls made by service to persistence layer PersistenceGetTimerIndexTasksScope // PersistenceCompleteTimerTaskScope tracks CompleteTimerTasks calls made by service to persistence layer PersistenceCompleteTimerTaskScope // PersistenceGetHistoryTasksScope tracks GetHistoryTasks calls made by service to persistence layer PersistenceGetHistoryTasksScope // PersistenceCompleteHistoryTaskScope tracks CompleteHistoryTask calls made by service to persistence layer PersistenceCompleteHistoryTaskScope // PersistenceRangeCompleteHistoryTaskScope tracks RangeCompleteHistoryTask calls made by service to persistence layer PersistenceRangeCompleteHistoryTaskScope // PersistenceCreateTasksScope tracks CreateTask calls made by service to persistence layer PersistenceCreateTasksScope // PersistenceGetTasksScope tracks GetTasks calls made by service to persistence layer PersistenceGetTasksScope // PersistenceCompleteTaskScope tracks CompleteTask calls made by service to persistence layer PersistenceCompleteTaskScope // PersistenceCompleteTasksLessThanScope is the metric scope for persistence.TaskManager.PersistenceCompleteTasksLessThan API PersistenceCompleteTasksLessThanScope // PersistenceGetOrphanTasksScope is the metric scope for persistence.TaskManager.GetOrphanTasks API PersistenceGetOrphanTasksScope // PersistenceLeaseTaskListScope tracks LeaseTaskList calls made by service to persistence layer PersistenceLeaseTaskListScope // PersistenceGetTaskListScope tracks GetTaskList calls made by service to persistence layer PersistenceGetTaskListScope // PersistenceUpdateTaskListScope tracks PersistenceUpdateTaskListScope calls made by service to persistence layer PersistenceUpdateTaskListScope // PersistenceListTaskListScope is the metric scope for persistence.TaskManager.ListTaskList API PersistenceListTaskListScope // PersistenceDeleteTaskListScope is the metric scope for persistence.TaskManager.DeleteTaskList API PersistenceDeleteTaskListScope // PersistenceGetTaskListSizeScope is the metric scope for persistence.TaskManager.GetTaskListSize API PersistenceGetTaskListSizeScope // PersistenceAppendHistoryEventsScope tracks AppendHistoryEvents calls made by service to persistence layer PersistenceAppendHistoryEventsScope // PersistenceGetWorkflowExecutionHistoryScope tracks GetWorkflowExecutionHistory calls made by service to persistence layer PersistenceGetWorkflowExecutionHistoryScope // PersistenceDeleteWorkflowExecutionHistoryScope tracks DeleteWorkflowExecutionHistory calls made by service to persistence layer PersistenceDeleteWorkflowExecutionHistoryScope // PersistenceCreateDomainScope tracks CreateDomain calls made by service to persistence layer PersistenceCreateDomainScope // PersistenceGetDomainScope tracks GetDomain calls made by service to persistence layer PersistenceGetDomainScope // PersistenceUpdateDomainScope tracks UpdateDomain calls made by service to persistence layer PersistenceUpdateDomainScope // PersistenceDeleteDomainScope tracks DeleteDomain calls made by service to persistence layer PersistenceDeleteDomainScope // PersistenceDeleteDomainByNameScope tracks DeleteDomainByName calls made by service to persistence layer PersistenceDeleteDomainByNameScope // PersistenceListDomainsScope tracks DeleteDomainByName calls made by service to persistence layer PersistenceListDomainsScope // PersistenceGetMetadataScope tracks DeleteDomainByName calls made by service to persistence layer PersistenceGetMetadataScope // PersistenceRecordWorkflowExecutionStartedScope tracks RecordWorkflowExecutionStarted calls made by service to persistence layer PersistenceRecordWorkflowExecutionStartedScope // PersistenceRecordWorkflowExecutionClosedScope tracks RecordWorkflowExecutionClosed calls made by service to persistence layer PersistenceRecordWorkflowExecutionClosedScope // PersistenceRecordWorkflowExecutionUninitializedScope tracks RecordWorkflowExecutionUninitialized calls made by service to persistence layer PersistenceRecordWorkflowExecutionUninitializedScope // PersistenceUpsertWorkflowExecutionScope tracks UpsertWorkflowExecution calls made by service to persistence layer PersistenceUpsertWorkflowExecutionScope // PersistenceListOpenWorkflowExecutionsScope tracks ListOpenWorkflowExecutions calls made by service to persistence layer PersistenceListOpenWorkflowExecutionsScope // PersistenceListClosedWorkflowExecutionsScope tracks ListClosedWorkflowExecutions calls made by service to persistence layer PersistenceListClosedWorkflowExecutionsScope // PersistenceListOpenWorkflowExecutionsByTypeScope tracks ListOpenWorkflowExecutionsByType calls made by service to persistence layer PersistenceListOpenWorkflowExecutionsByTypeScope // PersistenceListClosedWorkflowExecutionsByTypeScope tracks ListClosedWorkflowExecutionsByType calls made by service to persistence layer PersistenceListClosedWorkflowExecutionsByTypeScope // PersistenceListOpenWorkflowExecutionsByWorkflowIDScope tracks ListOpenWorkflowExecutionsByWorkflowID calls made by service to persistence layer PersistenceListOpenWorkflowExecutionsByWorkflowIDScope // PersistenceListClosedWorkflowExecutionsByWorkflowIDScope tracks ListClosedWorkflowExecutionsByWorkflowID calls made by service to persistence layer PersistenceListClosedWorkflowExecutionsByWorkflowIDScope // PersistenceListClosedWorkflowExecutionsByStatusScope tracks ListClosedWorkflowExecutionsByStatus calls made by service to persistence layer PersistenceListClosedWorkflowExecutionsByStatusScope // PersistenceGetClosedWorkflowExecutionScope tracks GetClosedWorkflowExecution calls made by service to persistence layer PersistenceGetClosedWorkflowExecutionScope // PersistenceVisibilityDeleteWorkflowExecutionScope is the metrics scope for persistence.VisibilityManager.DeleteWorkflowExecution PersistenceVisibilityDeleteWorkflowExecutionScope // PersistenceDeleteUninitializedWorkflowExecutionScope tracks DeleteUninitializedWorkflowExecution calls made by service to persistence layer PersistenceDeleteUninitializedWorkflowExecutionScope // PersistenceListWorkflowExecutionsScope tracks ListWorkflowExecutions calls made by service to persistence layer PersistenceListWorkflowExecutionsScope // PersistenceScanWorkflowExecutionsScope tracks ScanWorkflowExecutions calls made by service to persistence layer PersistenceScanWorkflowExecutionsScope // PersistenceCountWorkflowExecutionsScope tracks CountWorkflowExecutions calls made by service to persistence layer PersistenceCountWorkflowExecutionsScope // PersistenceEnqueueMessageScope tracks Enqueue calls made by service to persistence layer PersistenceEnqueueMessageScope // PersistenceEnqueueMessageToDLQScope tracks Enqueue DLQ calls made by service to persistence layer PersistenceEnqueueMessageToDLQScope // PersistenceReadMessagesScope tracks ReadMessages calls made by service to persistence layer PersistenceReadMessagesScope // PersistenceReadMessagesFromDLQScope tracks ReadMessagesFromDLQ calls made by service to persistence layer PersistenceReadMessagesFromDLQScope // PersistenceDeleteMessagesBeforeScope tracks DeleteMessages calls made by service to persistence layer PersistenceDeleteMessagesBeforeScope // PersistenceDeleteMessageFromDLQScope tracks DeleteMessageFromDLQ calls made by service to persistence layer PersistenceDeleteMessageFromDLQScope // PersistenceRangeDeleteMessagesFromDLQScope tracks RangeDeleteMessagesFromDLQ calls made by service to persistence layer PersistenceRangeDeleteMessagesFromDLQScope // PersistenceUpdateAckLevelScope tracks UpdateAckLevel calls made by service to persistence layer PersistenceUpdateAckLevelScope // PersistenceGetAckLevelsScope tracks GetAckLevel calls made by service to persistence layer PersistenceGetAckLevelsScope // PersistenceUpdateDLQAckLevelScope tracks UpdateDLQAckLevel calls made by service to persistence layer PersistenceUpdateDLQAckLevelScope // PersistenceGetDLQAckLevelsScope tracks GetDLQAckLevel calls made by service to persistence layer PersistenceGetDLQAckLevelsScope // PersistenceGetDLQSizeScope tracks GetDLQSize calls made by service to persistence layer PersistenceGetDLQSizeScope // PersistenceFetchDynamicConfigScope tracks FetchDynamicConfig calls made by service to persistence layer PersistenceFetchDynamicConfigScope // PersistenceUpdateDynamicConfigScope tracks UpdateDynamicConfig calls made by service to persistence layer PersistenceUpdateDynamicConfigScope // PersistenceShardRequestCountScope tracks number of persistence calls made to each shard PersistenceShardRequestCountScope // PersistenceGetActiveClusterSelectionPolicyScope tracks GetActiveClusterSelectionPolicy calls made by service to persistence layer PersistenceGetActiveClusterSelectionPolicyScope // PersistenceDeleteActiveClusterSelectionPolicyScope tracks DeleteActiveClusterSelectionPolicy calls made by service to persistence layer PersistenceDeleteActiveClusterSelectionPolicyScope // ResolverHostNotFoundScope is a simple low level error indicating a lookup failed in the membership resolver ResolverHostNotFoundScope // HashringScope is a metrics scope for emitting events for the service hashrhing HashringScope // HistoryClientStartWorkflowExecutionScope tracks RPC calls to history service HistoryClientStartWorkflowExecutionScope // HistoryClientDescribeHistoryHostScope tracks RPC calls to history service HistoryClientDescribeHistoryHostScope // HistoryClientRemoveTaskScope tracks RPC calls to history service HistoryClientRemoveTaskScope // HistoryClientCloseShardScope tracks RPC calls to history service HistoryClientCloseShardScope // HistoryClientResetQueueScope tracks RPC calls to history service HistoryClientResetQueueScope // HistoryClientDescribeQueueScope tracks RPC calls to history service HistoryClientDescribeQueueScope // HistoryClientRecordActivityTaskHeartbeatScope tracks RPC calls to history service HistoryClientRecordActivityTaskHeartbeatScope // HistoryClientRespondDecisionTaskCompletedScope tracks RPC calls to history service HistoryClientRespondDecisionTaskCompletedScope // HistoryClientRespondDecisionTaskFailedScope tracks RPC calls to history service HistoryClientRespondDecisionTaskFailedScope // HistoryClientRespondActivityTaskCompletedScope tracks RPC calls to history service HistoryClientRespondActivityTaskCompletedScope // HistoryClientRespondActivityTaskFailedScope tracks RPC calls to history service HistoryClientRespondActivityTaskFailedScope // HistoryClientRespondActivityTaskCanceledScope tracks RPC calls to history service HistoryClientRespondActivityTaskCanceledScope // HistoryClientDescribeMutableStateScope tracks RPC calls to history service HistoryClientDescribeMutableStateScope // HistoryClientGetMutableStateScope tracks RPC calls to history service HistoryClientGetMutableStateScope // HistoryClientPollMutableStateScope tracks RPC calls to history service HistoryClientPollMutableStateScope // HistoryClientResetStickyTaskListScope tracks RPC calls to history service HistoryClientResetStickyTaskListScope // HistoryClientDescribeWorkflowExecutionScope tracks RPC calls to history service HistoryClientDescribeWorkflowExecutionScope // HistoryClientRecordDecisionTaskStartedScope tracks RPC calls to history service HistoryClientRecordDecisionTaskStartedScope // HistoryClientRecordActivityTaskStartedScope tracks RPC calls to history service HistoryClientRecordActivityTaskStartedScope // HistoryClientRequestCancelWorkflowExecutionScope tracks RPC calls to history service HistoryClientRequestCancelWorkflowExecutionScope // HistoryClientSignalWorkflowExecutionScope tracks RPC calls to history service HistoryClientSignalWorkflowExecutionScope // HistoryClientSignalWithStartWorkflowExecutionScope tracks RPC calls to history service HistoryClientSignalWithStartWorkflowExecutionScope // HistoryClientRemoveSignalMutableStateScope tracks RPC calls to history service HistoryClientRemoveSignalMutableStateScope // HistoryClientTerminateWorkflowExecutionScope tracks RPC calls to history service HistoryClientTerminateWorkflowExecutionScope // HistoryClientResetWorkflowExecutionScope tracks RPC calls to history service HistoryClientResetWorkflowExecutionScope // HistoryClientScheduleDecisionTaskScope tracks RPC calls to history service HistoryClientScheduleDecisionTaskScope // HistoryClientRecordChildExecutionCompletedScope tracks RPC calls to history service HistoryClientRecordChildExecutionCompletedScope // HistoryClientSyncShardStatusScope tracks RPC calls to history service HistoryClientReplicateEventsV2Scope // HistoryClientReplicateRawEventsV2Scope tracks RPC calls to history service HistoryClientSyncShardStatusScope // HistoryClientSyncActivityScope tracks RPC calls to history service HistoryClientSyncActivityScope // HistoryClientGetReplicationTasksScope tracks RPC calls to history service HistoryClientGetReplicationTasksScope // HistoryClientGetDLQReplicationTasksScope tracks RPC calls to history service HistoryClientGetDLQReplicationTasksScope // HistoryClientQueryWorkflowScope tracks RPC calls to history service HistoryClientQueryWorkflowScope // HistoryClientReapplyEventsScope tracks RPC calls to history service HistoryClientReapplyEventsScope // HistoryClientCountDLQMessagesScope tracks RPC calls to history service HistoryClientCountDLQMessagesScope // HistoryClientReadDLQMessagesScope tracks RPC calls to history service HistoryClientReadDLQMessagesScope // HistoryClientPurgeDLQMessagesScope tracks RPC calls to history service HistoryClientPurgeDLQMessagesScope // HistoryClientMergeDLQMessagesScope tracks RPC calls to history service HistoryClientMergeDLQMessagesScope // HistoryClientRefreshWorkflowTasksScope tracks RPC calls to history service HistoryClientRefreshWorkflowTasksScope // HistoryClientNotifyFailoverMarkersScope tracks RPC calls to history service HistoryClientNotifyFailoverMarkersScope // HistoryClientGetCrossClusterTasksScope tracks RPC calls to history service HistoryClientGetCrossClusterTasksScope // HistoryClientRespondCrossClusterTasksCompletedScope tracks RPC calls to history service HistoryClientRespondCrossClusterTasksCompletedScope // HistoryClientGetFailoverInfoScope tracks RPC calls to history service HistoryClientGetFailoverInfoScope // HistoryClientGetDLQReplicationMessagesScope tracks RPC calls to history service HistoryClientGetDLQReplicationMessagesScope // HistoryClientGetReplicationMessagesScope tracks RPC calls to history service HistoryClientGetReplicationMessagesScope // HistoryClientWfIDCacheScope tracks workflow ID cache metrics HistoryClientWfIDCacheScope // HistoryClientRatelimitUpdateScope tracks global ratelimiter related calls to history service HistoryClientRatelimitUpdateScope // MatchingClientPollForDecisionTaskScope tracks RPC calls to matching service MatchingClientPollForDecisionTaskScope // MatchingClientPollForActivityTaskScope tracks RPC calls to matching service MatchingClientPollForActivityTaskScope // MatchingClientAddActivityTaskScope tracks RPC calls to matching service MatchingClientAddActivityTaskScope // MatchingClientAddDecisionTaskScope tracks RPC calls to matching service MatchingClientAddDecisionTaskScope // MatchingClientQueryWorkflowScope tracks RPC calls to matching service MatchingClientQueryWorkflowScope // MatchingClientRespondQueryTaskCompletedScope tracks RPC calls to matching service MatchingClientRespondQueryTaskCompletedScope // MatchingClientCancelOutstandingPollScope tracks RPC calls to matching service MatchingClientCancelOutstandingPollScope // MatchingClientDescribeTaskListScope tracks RPC calls to matching service MatchingClientDescribeTaskListScope // MatchingClientListTaskListPartitionsScope tracks RPC calls to matching service MatchingClientListTaskListPartitionsScope // MatchingClientGetTaskListsByDomainScope tracks RPC calls to matching service MatchingClientGetTaskListsByDomainScope // MatchingClientUpdateTaskListPartitionConfigScope tracks RPC calls to matching service MatchingClientUpdateTaskListPartitionConfigScope // MatchingClientRefreshTaskListPartitionConfigScope tracks RPC calls to matching service MatchingClientRefreshTaskListPartitionConfigScope // FrontendClientDeleteDomainScope tracks RPC calls to frontend service FrontendClientDeleteDomainScope // FrontendClientDeprecateDomainScope tracks RPC calls to frontend service FrontendClientDeprecateDomainScope // FrontendClientDescribeDomainScope tracks RPC calls to frontend service FrontendClientDescribeDomainScope // FrontendClientDescribeTaskListScope tracks RPC calls to frontend service FrontendClientDescribeTaskListScope // FrontendClientDescribeWorkflowExecutionScope tracks RPC calls to frontend service FrontendClientDescribeWorkflowExecutionScope // FrontendClientDiagnoseWorkflowExecutionScope tracks RPC calls to frontend service FrontendClientDiagnoseWorkflowExecutionScope // FrontendClientGetWorkflowExecutionHistoryScope tracks RPC calls to frontend service FrontendClientGetWorkflowExecutionHistoryScope // FrontendClientGetWorkflowExecutionRawHistoryScope tracks RPC calls to frontend service FrontendClientGetWorkflowExecutionRawHistoryScope // FrontendClientPollForWorkflowExecutionRawHistoryScope tracks RPC calls to frontend service FrontendClientPollForWorkflowExecutionRawHistoryScope // FrontendClientListArchivedWorkflowExecutionsScope tracks RPC calls to frontend service FrontendClientListArchivedWorkflowExecutionsScope // FrontendClientListClosedWorkflowExecutionsScope tracks RPC calls to frontend service FrontendClientListClosedWorkflowExecutionsScope // FrontendClientListDomainsScope tracks RPC calls to frontend service FrontendClientListDomainsScope // FrontendClientListOpenWorkflowExecutionsScope tracks RPC calls to frontend service FrontendClientListOpenWorkflowExecutionsScope // FrontendClientPollForActivityTaskScope tracks RPC calls to frontend service FrontendClientPollForActivityTaskScope // FrontendClientPollForDecisionTaskScope tracks RPC calls to frontend service FrontendClientPollForDecisionTaskScope // FrontendClientQueryWorkflowScope tracks RPC calls to frontend service FrontendClientQueryWorkflowScope // FrontendClientRecordActivityTaskHeartbeatScope tracks RPC calls to frontend service FrontendClientRecordActivityTaskHeartbeatScope // FrontendClientRecordActivityTaskHeartbeatByIDScope tracks RPC calls to frontend service FrontendClientRecordActivityTaskHeartbeatByIDScope // FrontendClientRegisterDomainScope tracks RPC calls to frontend service FrontendClientRegisterDomainScope // FrontendClientRequestCancelWorkflowExecutionScope tracks RPC calls to frontend service FrontendClientRequestCancelWorkflowExecutionScope // FrontendClientResetStickyTaskListScope tracks RPC calls to frontend service FrontendClientResetStickyTaskListScope // FrontendClientRefreshWorkflowTasksScope tracks RPC calls to frontend service FrontendClientRefreshWorkflowTasksScope // FrontendClientResetWorkflowExecutionScope tracks RPC calls to frontend service FrontendClientResetWorkflowExecutionScope // FrontendClientRespondActivityTaskCanceledScope tracks RPC calls to frontend service FrontendClientRespondActivityTaskCanceledScope // FrontendClientRespondActivityTaskCanceledByIDScope tracks RPC calls to frontend service FrontendClientRespondActivityTaskCanceledByIDScope // FrontendClientRespondActivityTaskCompletedScope tracks RPC calls to frontend service FrontendClientRespondActivityTaskCompletedScope // FrontendClientRespondActivityTaskCompletedByIDScope tracks RPC calls to frontend service FrontendClientRespondActivityTaskCompletedByIDScope // FrontendClientRespondActivityTaskFailedScope tracks RPC calls to frontend service FrontendClientRespondActivityTaskFailedScope // FrontendClientRespondActivityTaskFailedByIDScope tracks RPC calls to frontend service FrontendClientRespondActivityTaskFailedByIDScope // FrontendClientRespondDecisionTaskCompletedScope tracks RPC calls to frontend service FrontendClientRespondDecisionTaskCompletedScope // FrontendClientRespondDecisionTaskFailedScope tracks RPC calls to frontend service FrontendClientRespondDecisionTaskFailedScope // FrontendClientRespondQueryTaskCompletedScope tracks RPC calls to frontend service FrontendClientRespondQueryTaskCompletedScope // FrontendClientSignalWithStartWorkflowExecutionScope tracks RPC calls to frontend service FrontendClientSignalWithStartWorkflowExecutionScope // FrontendClientSignalWorkflowExecutionAsyncScope tracks RPC calls to frontend service FrontendClientSignalWithStartWorkflowExecutionAsyncScope // FrontendClientSignalWorkflowExecutionScope tracks RPC calls to frontend service FrontendClientSignalWorkflowExecutionScope // FrontendClientStartWorkflowExecutionScope tracks RPC calls to frontend service FrontendClientStartWorkflowExecutionScope // FrontendClientStartWorkflowExecutionAsyncScope tracks RPC calls to frontend service FrontendClientStartWorkflowExecutionAsyncScope // FrontendClientRestartWorkflowExecutionScope tracks RPC calls to frontend service FrontendClientRestartWorkflowExecutionScope // FrontendClientTerminateWorkflowExecutionScope tracks RPC calls to frontend service FrontendClientTerminateWorkflowExecutionScope // FrontendClientUpdateDomainScope tracks RPC calls to frontend service FrontendClientUpdateDomainScope // FrontendClientFailoverDomainScope tracks RPC calls to frontend service FrontendClientFailoverDomainScope // FrontendClientListFailoverHistoryScope tracks RPC calls to frontend service FrontendClientListFailoverHistoryScope // FrontendClientListWorkflowExecutionsScope tracks RPC calls to frontend service FrontendClientListWorkflowExecutionsScope // FrontendClientScanWorkflowExecutionsScope tracks RPC calls to frontend service FrontendClientScanWorkflowExecutionsScope // FrontendClientCountWorkflowExecutionsScope tracks RPC calls to frontend service FrontendClientCountWorkflowExecutionsScope // FrontendClientGetSearchAttributesScope tracks RPC calls to frontend service FrontendClientGetSearchAttributesScope // FrontendClientGetReplicationTasksScope tracks RPC calls to frontend service FrontendClientGetReplicationTasksScope // FrontendClientGetDomainReplicationTasksScope tracks RPC calls to frontend service FrontendClientGetDomainReplicationTasksScope // FrontendClientGetDLQReplicationTasksScope tracks RPC calls to frontend service FrontendClientGetDLQReplicationTasksScope // FrontendClientReapplyEventsScope tracks RPC calls to frontend service FrontendClientReapplyEventsScope // FrontendClientGetClusterInfoScope tracks RPC calls to frontend FrontendClientGetClusterInfoScope // FrontendClientListTaskListPartitionsScope tracks RPC calls to frontend service FrontendClientListTaskListPartitionsScope // FrontendClientGetTaskListsByDomainScope tracks RPC calls to frontend service FrontendClientGetTaskListsByDomainScope // AdminClientAddSearchAttributeScope tracks RPC calls to admin service AdminClientAddSearchAttributeScope // AdminClientCloseShardScope tracks RPC calls to admin service AdminClientCloseShardScope // AdminClientRemoveTaskScope tracks RPC calls to admin service AdminClientRemoveTaskScope // AdminClientResetQueueScope tracks RPC calls to admin service AdminClientResetQueueScope // AdminClientDescribeQueueScope tracks RPC calls to admin service AdminClientDescribeQueueScope // AdminClientDescribeHistoryHostScope tracks RPC calls to admin service AdminClientDescribeHistoryHostScope // AdminClientDescribeShardDistributionScope tracks RPC calls to admin service AdminClientDescribeShardDistributionScope // AdminClientDescribeWorkflowExecutionScope tracks RPC calls to admin service AdminClientDescribeWorkflowExecutionScope // AdminClientGetWorkflowExecutionRawHistoryV2Scope tracks RPC calls to admin service AdminClientGetWorkflowExecutionRawHistoryV2Scope // AdminClientDescribeClusterScope tracks RPC calls to admin service AdminClientDescribeClusterScope // AdminClientCountDLQMessagesScope tracks RPC calls to admin service AdminClientCountDLQMessagesScope // AdminClientReadDLQMessagesScope tracks RPC calls to admin service AdminClientReadDLQMessagesScope // AdminClientPurgeDLQMessagesScope tracks RPC calls to admin service AdminClientPurgeDLQMessagesScope // AdminClientMergeDLQMessagesScope tracks RPC calls to admin service AdminClientMergeDLQMessagesScope // AdminClientRefreshWorkflowTasksScope tracks RPC calls to admin service AdminClientRefreshWorkflowTasksScope // AdminClientResendReplicationTasksScope tracks RPC calls to admin service AdminClientResendReplicationTasksScope // AdminClientGetCrossClusterTasksScope tracks RPC calls to Admin service AdminClientGetCrossClusterTasksScope // AdminClientRespondCrossClusterTasksCompletedScope tracks RPC calls to Admin service AdminClientRespondCrossClusterTasksCompletedScope // AdminClientGetDynamicConfigScope tracks RPC calls to admin service AdminClientGetDynamicConfigScope // AdminClientUpdateDynamicConfigScope tracks RPC calls to admin service AdminClientUpdateDynamicConfigScope // AdminClientRestoreDynamicConfigScope tracks RPC calls to admin service AdminClientRestoreDynamicConfigScope // AdminClientListDynamicConfigScope tracks RPC calls to admin service AdminClientListDynamicConfigScope // AdminClientGetGlobalIsolationGroupsScope is a request to get all the global isolation-groups AdminClientGetGlobalIsolationGroupsScope // AdminClientUpdateGlobalIsolationGroupsScope is a request to update the global isolation-groups AdminClientUpdateGlobalIsolationGroupsScope // AdminClientGetDomainIsolationGroupsScope is a request to get the domains' isolation groups AdminClientGetDomainIsolationGroupsScope // AdminClientUpdateDomainIsolationGroupsScope is a request to update the domains isolation-groups AdminClientUpdateDomainIsolationGroupsScope // AdminClientDeleteWorkflowScope is the metric scope for admin.DeleteWorkflow AdminClientDeleteWorkflowScope // AdminClientMaintainCorruptWorkflowScope is the metric scope for admin.MaintainCorruptWorkflow AdminClientMaintainCorruptWorkflowScope // AdminClientGetReplicationTasksScope is the metric scope for admin.GetReplicationTasks AdminClientGetReplicationTasksScope // AdminClientReapplyEventsScope is the metric scope for admin.ReapplyEvents AdminClientReapplyEventsScope // AdminClientGetDLQReplicationMessagesScope is the metric scope for admin.GetDLQReplicationMessages AdminClientGetDLQReplicationMessagesScope // AdminClientGetDomainReplicationMessagesScope is the metric scope for admin.GetDomainReplicationMessages AdminClientGetDomainReplicationMessagesScope // AdminClientGetReplicationMessagesScope is the metric scope for admin.GetReplicationMessages AdminClientGetReplicationMessagesScope // AdminClientGetWorkflowExecutionRawHistoryScope is the metric scope for admin.GetDomainAsyncWorkflow AdminClientGetDomainAsyncWorkflowConfiguratonScope // AdminClientGetWorkflowExecutionRawHistoryScope is the metric scope for admin.UpdateDomainAsyncWorkflowConfiguration AdminClientUpdateDomainAsyncWorkflowConfiguratonScope // AdminClientUpdateTaskListPartitionConfigScope is the metrics scope for admin.UpdateTaskListPartitionConfig AdminClientUpdateTaskListPartitionConfigScope // DCRedirectionDeleteDomainScope tracks RPC calls for dc redirection DCRedirectionDeleteDomainScope // DCRedirectionDeleteDomainScope tracks RPC calls for dc redirection DCRedirectionDeprecateDomainScope // DCRedirectionFailoverDomainScope tracks RPC calls for dc redirection DCRedirectionFailoverDomainScope // DCRedirectionListFailoverHistoryScope tracks RPC calls for dc redirection DCRedirectionListFailoverHistoryScope // DCRedirectionDescribeDomainScope tracks RPC calls for dc redirection DCRedirectionDescribeDomainScope // DCRedirectionDescribeTaskListScope tracks RPC calls for dc redirection DCRedirectionDescribeTaskListScope // DCRedirectionDescribeWorkflowExecutionScope tracks RPC calls for dc redirection DCRedirectionDescribeWorkflowExecutionScope // DCRedirectionDiagnoseWorkflowExecutionScope tracks RPC calls for dc redirection DCRedirectionDiagnoseWorkflowExecutionScope // DCRedirectionGetWorkflowExecutionHistoryScope tracks RPC calls for dc redirection DCRedirectionGetWorkflowExecutionHistoryScope // DCRedirectionGetWorkflowExecutionRawHistoryScope tracks RPC calls for dc redirection DCRedirectionGetWorkflowExecutionRawHistoryScope // DCRedirectionPollForWorklfowExecutionRawHistoryScope tracks RPC calls for dc redirection DCRedirectionPollForWorklfowExecutionRawHistoryScope // DCRedirectionListArchivedWorkflowExecutionsScope tracks RPC calls for dc redirection DCRedirectionListArchivedWorkflowExecutionsScope // DCRedirectionListClosedWorkflowExecutionsScope tracks RPC calls for dc redirection DCRedirectionListClosedWorkflowExecutionsScope // DCRedirectionListDomainsScope tracks RPC calls for dc redirection DCRedirectionListDomainsScope // DCRedirectionListOpenWorkflowExecutionsScope tracks RPC calls for dc redirection DCRedirectionListOpenWorkflowExecutionsScope // DCRedirectionListWorkflowExecutionsScope tracks RPC calls for dc redirection DCRedirectionListWorkflowExecutionsScope // DCRedirectionScanWorkflowExecutionsScope tracks RPC calls for dc redirection DCRedirectionScanWorkflowExecutionsScope // DCRedirectionCountWorkflowExecutionsScope tracks RPC calls for dc redirection DCRedirectionCountWorkflowExecutionsScope // DCRedirectionGetSearchAttributesScope tracks RPC calls for dc redirection DCRedirectionGetSearchAttributesScope // DCRedirectionPollForActivityTaskScope tracks RPC calls for dc redirection DCRedirectionPollForActivityTaskScope // DCRedirectionPollForDecisionTaskScope tracks RPC calls for dc redirection DCRedirectionPollForDecisionTaskScope // DCRedirectionQueryWorkflowScope tracks RPC calls for dc redirection DCRedirectionQueryWorkflowScope // DCRedirectionRecordActivityTaskHeartbeatScope tracks RPC calls for dc redirection DCRedirectionRecordActivityTaskHeartbeatScope // DCRedirectionRecordActivityTaskHeartbeatByIDScope tracks RPC calls for dc redirection DCRedirectionRecordActivityTaskHeartbeatByIDScope // DCRedirectionRegisterDomainScope tracks RPC calls for dc redirection DCRedirectionRegisterDomainScope // DCRedirectionRequestCancelWorkflowExecutionScope tracks RPC calls for dc redirection DCRedirectionRequestCancelWorkflowExecutionScope // DCRedirectionResetStickyTaskListScope tracks RPC calls for dc redirection DCRedirectionResetStickyTaskListScope // DCRedirectionResetWorkflowExecutionScope tracks RPC calls for dc redirection DCRedirectionResetWorkflowExecutionScope // DCRedirectionRespondActivityTaskCanceledScope tracks RPC calls for dc redirection DCRedirectionRespondActivityTaskCanceledScope // DCRedirectionRespondActivityTaskCanceledByIDScope tracks RPC calls for dc redirection DCRedirectionRespondActivityTaskCanceledByIDScope // DCRedirectionRespondActivityTaskCompletedScope tracks RPC calls for dc redirection DCRedirectionRespondActivityTaskCompletedScope // DCRedirectionRespondActivityTaskCompletedByIDScope tracks RPC calls for dc redirection DCRedirectionRespondActivityTaskCompletedByIDScope // DCRedirectionRespondActivityTaskFailedScope tracks RPC calls for dc redirection DCRedirectionRespondActivityTaskFailedScope // DCRedirectionRespondActivityTaskFailedByIDScope tracks RPC calls for dc redirection DCRedirectionRespondActivityTaskFailedByIDScope // DCRedirectionRespondDecisionTaskCompletedScope tracks RPC calls for dc redirection DCRedirectionRespondDecisionTaskCompletedScope // DCRedirectionRespondDecisionTaskFailedScope tracks RPC calls for dc redirection DCRedirectionRespondDecisionTaskFailedScope // DCRedirectionRespondQueryTaskCompletedScope tracks RPC calls for dc redirection DCRedirectionRespondQueryTaskCompletedScope // DCRedirectionSignalWithStartWorkflowExecutionScope tracks RPC calls for dc redirection DCRedirectionSignalWithStartWorkflowExecutionScope // DCRedirectionSignalWithStartWorkflowExecutionAsyncScope tracks RPC calls for dc redirection DCRedirectionSignalWithStartWorkflowExecutionAsyncScope // DCRedirectionSignalWorkflowExecutionScope tracks RPC calls for dc redirection DCRedirectionSignalWorkflowExecutionScope // DCRedirectionStartWorkflowExecutionScope tracks RPC calls for dc redirection DCRedirectionStartWorkflowExecutionScope // DCRedirectionStartWorkflowExecutionAsyncScope tracks RPC calls for dc redirection DCRedirectionStartWorkflowExecutionAsyncScope // DCRedirectionTerminateWorkflowExecutionScope tracks RPC calls for dc redirection DCRedirectionTerminateWorkflowExecutionScope // DCRedirectionUpdateDomainScope tracks RPC calls for dc redirection DCRedirectionUpdateDomainScope // DCRedirectionListTaskListPartitionsScope tracks RPC calls for dc redirection DCRedirectionListTaskListPartitionsScope // DCRedirectionGetTaskListsByDomainScope tracks RPC calls for dc redirection DCRedirectionGetTaskListsByDomainScope // DCRedirectionRefreshWorkflowTasksScope tracks RPC calls for dc redirection DCRedirectionRefreshWorkflowTasksScope // DCRedirectionRestartWorkflowExecutionScope tracks RPC calls for dc redirection DCRedirectionRestartWorkflowExecutionScope // DCRedirectionForwardingPolicyScope tracks cluster redirection decisions DCRedirectionForwardingPolicyScope // MessagingPublishScope tracks Publish calls made by service to messaging layer MessagingClientPublishScope // MessagingPublishBatchScope tracks Publish calls made by service to messaging layer MessagingClientPublishBatchScope // MessagingClientConsumerScope tracks the consumer activities MessagingClientConsumerScope // DomainCacheScope tracks domain cache callbacks DomainCacheScope // HistoryRereplicationByTransferTaskScope tracks history replication calls made by transfer task HistoryRereplicationByTransferTaskScope // HistoryRereplicationByTimerTaskScope tracks history replication calls made by timer task HistoryRereplicationByTimerTaskScope // HistoryRereplicationByHistoryReplicationScope tracks history replication calls made by history replication HistoryRereplicationByHistoryReplicationScope // HistoryRereplicationByHistoryMetadataReplicationScope tracks history replication calls made by history replication HistoryRereplicationByHistoryMetadataReplicationScope // HistoryRereplicationByActivityReplicationScope tracks history replication calls made by activity replication HistoryRereplicationByActivityReplicationScope // PersistenceAppendHistoryNodesScope tracks AppendHistoryNodes calls made by service to persistence layer PersistenceAppendHistoryNodesScope // PersistenceReadHistoryBranchScope tracks ReadHistoryBranch calls made by service to persistence layer PersistenceReadHistoryBranchScope // PersistenceReadHistoryBranchByBatchScope tracks ReadHistoryBranch calls made by service to persistence layer PersistenceReadHistoryBranchByBatchScope // PersistenceReadRawHistoryBranchScope tracks ReadHistoryBranch calls made by service to persistence layer PersistenceReadRawHistoryBranchScope // PersistenceForkHistoryBranchScope tracks ForkHistoryBranch calls made by service to persistence layer PersistenceForkHistoryBranchScope // PersistenceDeleteHistoryBranchScope tracks DeleteHistoryBranch calls made by service to persistence layer PersistenceDeleteHistoryBranchScope // PersistenceCompleteForkBranchScope tracks CompleteForkBranch calls made by service to persistence layer PersistenceCompleteForkBranchScope // PersistenceGetHistoryTreeScope tracks GetHistoryTree calls made by service to persistence layer PersistenceGetHistoryTreeScope // PersistenceGetAllHistoryTreeBranchesScope tracks GetHistoryTree calls made by service to persistence layer PersistenceGetAllHistoryTreeBranchesScope // ClusterMetadataArchivalConfigScope tracks ArchivalConfig calls to ClusterMetadata ClusterMetadataArchivalConfigScope // ElasticsearchRecordWorkflowExecutionStartedScope tracks RecordWorkflowExecutionStarted calls made by service to persistence layer ElasticsearchRecordWorkflowExecutionStartedScope // ElasticsearchRecordWorkflowExecutionClosedScope tracks RecordWorkflowExecutionClosed calls made by service to persistence layer ElasticsearchRecordWorkflowExecutionClosedScope // ElasticsearchRecordWorkflowExecutionUninitializedScope tracks RecordWorkflowExecutionUninitialized calls made by service to persistence layer ElasticsearchRecordWorkflowExecutionUninitializedScope // ElasticsearchUpsertWorkflowExecutionScope tracks UpsertWorkflowExecution calls made by service to persistence layer ElasticsearchUpsertWorkflowExecutionScope // ElasticsearchListOpenWorkflowExecutionsScope tracks ListOpenWorkflowExecutions calls made by service to persistence layer ElasticsearchListOpenWorkflowExecutionsScope // ElasticsearchListClosedWorkflowExecutionsScope tracks ListClosedWorkflowExecutions calls made by service to persistence layer ElasticsearchListClosedWorkflowExecutionsScope // ElasticsearchListOpenWorkflowExecutionsByTypeScope tracks ListOpenWorkflowExecutionsByType calls made by service to persistence layer ElasticsearchListOpenWorkflowExecutionsByTypeScope // ElasticsearchListClosedWorkflowExecutionsByTypeScope tracks ListClosedWorkflowExecutionsByType calls made by service to persistence layer ElasticsearchListClosedWorkflowExecutionsByTypeScope // ElasticsearchListOpenWorkflowExecutionsByWorkflowIDScope tracks ListOpenWorkflowExecutionsByWorkflowID calls made by service to persistence layer ElasticsearchListOpenWorkflowExecutionsByWorkflowIDScope // ElasticsearchListClosedWorkflowExecutionsByWorkflowIDScope tracks ListClosedWorkflowExecutionsByWorkflowID calls made by service to persistence layer ElasticsearchListClosedWorkflowExecutionsByWorkflowIDScope // ElasticsearchListClosedWorkflowExecutionsByStatusScope tracks ListClosedWorkflowExecutionsByStatus calls made by service to persistence layer ElasticsearchListClosedWorkflowExecutionsByStatusScope // ElasticsearchGetClosedWorkflowExecutionScope tracks GetClosedWorkflowExecution calls made by service to persistence layer ElasticsearchGetClosedWorkflowExecutionScope // ElasticsearchListWorkflowExecutionsScope tracks ListWorkflowExecutions calls made by service to persistence layer ElasticsearchListWorkflowExecutionsScope // ElasticsearchScanWorkflowExecutionsScope tracks ScanWorkflowExecutions calls made by service to persistence layer ElasticsearchScanWorkflowExecutionsScope // ElasticsearchCountWorkflowExecutionsScope tracks CountWorkflowExecutions calls made by service to persistence layer ElasticsearchCountWorkflowExecutionsScope // ElasticsearchDeleteWorkflowExecutionsScope tracks DeleteWorkflowExecution calls made by service to persistence layer ElasticsearchDeleteWorkflowExecutionsScope // ElasticsearchDeleteUninitializedWorkflowExecutionsScope tracks DeleteUninitializedWorkflowExecution calls made by service to persistence layer ElasticsearchDeleteUninitializedWorkflowExecutionsScope // PinotRecordWorkflowExecutionStartedScope tracks RecordWorkflowExecutionStarted calls made by service to persistence layer PinotRecordWorkflowExecutionStartedScope // PinotRecordWorkflowExecutionClosedScope tracks RecordWorkflowExecutionClosed calls made by service to persistence layer PinotRecordWorkflowExecutionClosedScope // PinotRecordWorkflowExecutionUninitializedScope tracks RecordWorkflowExecutionUninitialized calls made by service to persistence layer PinotRecordWorkflowExecutionUninitializedScope // PinotUpsertWorkflowExecutionScope tracks UpsertWorkflowExecution calls made by service to persistence layer PinotUpsertWorkflowExecutionScope // PinotListOpenWorkflowExecutionsScope tracks ListOpenWorkflowExecutions calls made by service to persistence layer PinotListOpenWorkflowExecutionsScope // PinotListClosedWorkflowExecutionsScope tracks ListClosedWorkflowExecutions calls made by service to persistence layer PinotListClosedWorkflowExecutionsScope // PinotListOpenWorkflowExecutionsByTypeScope tracks ListOpenWorkflowExecutionsByType calls made by service to persistence layer PinotListOpenWorkflowExecutionsByTypeScope // PinotListClosedWorkflowExecutionsByTypeScope tracks ListClosedWorkflowExecutionsByType calls made by service to persistence layer PinotListClosedWorkflowExecutionsByTypeScope // PinotListOpenWorkflowExecutionsByWorkflowIDScope tracks ListOpenWorkflowExecutionsByWorkflowID calls made by service to persistence layer PinotListOpenWorkflowExecutionsByWorkflowIDScope // PinotListClosedWorkflowExecutionsByWorkflowIDScope tracks ListClosedWorkflowExecutionsByWorkflowID calls made by service to persistence layer PinotListClosedWorkflowExecutionsByWorkflowIDScope // PinotListClosedWorkflowExecutionsByStatusScope tracks ListClosedWorkflowExecutionsByStatus calls made by service to persistence layer PinotListClosedWorkflowExecutionsByStatusScope // PinotGetClosedWorkflowExecutionScope tracks GetClosedWorkflowExecution calls made by service to persistence layer PinotGetClosedWorkflowExecutionScope // PinotListWorkflowExecutionsScope tracks ListWorkflowExecutions calls made by service to persistence layer PinotListWorkflowExecutionsScope // PinotScanWorkflowExecutionsScope tracks ScanWorkflowExecutions calls made by service to persistence layer PinotScanWorkflowExecutionsScope // PinotCountWorkflowExecutionsScope tracks CountWorkflowExecutions calls made by service to persistence layer PinotCountWorkflowExecutionsScope // PinotDeleteWorkflowExecutionsScope tracks DeleteWorkflowExecution calls made by service to persistence layer PinotDeleteWorkflowExecutionsScope // PinotDeleteUninitializedWorkflowExecutionsScope tracks DeleteUninitializedWorkflowExecution calls made by service to persistence layer PinotDeleteUninitializedWorkflowExecutionsScope // SequentialTaskProcessingScope is used by sequential task processing logic SequentialTaskProcessingScope // ParallelTaskProcessingScope is used by parallel task processing logic ParallelTaskProcessingScope // TaskSchedulerScope is used by task scheduler logic TaskSchedulerScope // TaskSchedulerRateLimiterScope is used by task scheduler rate limiter logic TaskSchedulerRateLimiterScope // HistoryEngineScope is used by history engine for areas that aren't covered by other, more specific scopes HistoryEngineScope // HistoryArchiverScope is used by history archivers HistoryArchiverScope // VisibilityArchiverScope is used by visibility archivers VisibilityArchiverScope // The following metrics are only used by internal archiver implemention. // TODO: move them to internal repo once cadence plugin model is in place. // BlobstoreClientUploadScope tracks Upload calls to blobstore BlobstoreClientUploadScope // BlobstoreClientDownloadScope tracks Download calls to blobstore BlobstoreClientDownloadScope // BlobstoreClientGetMetadataScope tracks GetMetadata calls to blobstore BlobstoreClientGetMetadataScope // BlobstoreClientExistsScope tracks Exists calls to blobstore BlobstoreClientExistsScope // BlobstoreClientDeleteScope tracks Delete calls to blobstore BlobstoreClientDeleteScope // BlobstoreClientDirectoryExistsScope tracks DirectoryExists calls to blobstore BlobstoreClientDirectoryExistsScope // DomainFailoverScope is used in domain failover processor DomainFailoverScope // DomainReplicationQueueScope is used in domainreplication queue DomainReplicationQueueScope // ClusterMetadataScope is used for the cluster metadata ClusterMetadataScope // GetAvailableIsolationGroupsScope is the metric for the default partitioner's getIsolationGroups operation GetAvailableIsolationGroupsScope // TaskValidatorScope is the metric for the taskvalidator's workflow check operation. TaskValidatorScope // GlobalRatelimiter is the metrics scope for limiting-side common/quotas/global behavior GlobalRatelimiter // GlobalRatelimiterAggregator is the metrics scope for aggregator-side common/quotas/global behavior GlobalRatelimiterAggregator // P2PRPCPeerChooserScope is the metrics scope for P2P RPC peer chooser P2PRPCPeerChooserScope // PartitionConfigProviderScope is the metrics scope for Partition Config Provider PartitionConfigProviderScope // ShardDistributorClientGetShardOwnerScope tracks GetShardOwner calls made by service to shard distributor ShardDistributorClientGetShardOwnerScope // ShardDistributorClientWatchNamespaceStateScope tracks WatchNamespaceState calls made by service to shard distributor ShardDistributorClientWatchNamespaceStateScope // ShardDistributorExecutorClientHeartbeatScope tracks Heartbeat calls made by executor to shard distributor ShardDistributorExecutorClientHeartbeatScope // LoadBalancerScope is the metrics scope for Round Robin load balancer LoadBalancerScope // ActiveClusterManager is the scope used by active cluster manager ActiveClusterManager // ActiveClusterManagerWorkflowCacheScope is the scope used by active cluster manager's workflow cache ActiveClusterManagerWorkflowCacheScope NumCommonScopes ) // -- Operation scopes for Admin service -- const ( // AdminDescribeHistoryHostScope is the metric scope for admin.AdminDescribeHistoryHostScope AdminDescribeHistoryHostScope = iota + NumCommonScopes // AdminDescribeClusterScope is the metric scope for admin.AdminDescribeClusterScope AdminDescribeClusterScope // AdminAddSearchAttributeScope is the metric scope for admin.AdminAddSearchAttributeScope AdminAddSearchAttributeScope // AdminDescribeWorkflowExecutionScope is the metric scope for admin.AdminDescribeWorkflowExecutionScope AdminDescribeWorkflowExecutionScope // AdminGetWorkflowExecutionRawHistoryScope is the metric scope for admin.GetWorkflowExecutionRawHistoryScope AdminGetWorkflowExecutionRawHistoryScope // AdminGetWorkflowExecutionRawHistoryV2Scope is the metric scope for admin.GetWorkflowExecutionRawHistoryScope AdminGetWorkflowExecutionRawHistoryV2Scope // AdminGetReplicationMessagesScope is the metric scope for admin.GetReplicationMessages AdminGetReplicationMessagesScope // AdminGetDomainReplicationMessagesScope is the metric scope for admin.GetDomainReplicationMessages AdminGetDomainReplicationMessagesScope // AdminGetDLQReplicationMessagesScope is the metric scope for admin.GetDLQReplicationMessages AdminGetDLQReplicationMessagesScope // AdminReapplyEventsScope is the metric scope for admin.ReapplyEvents AdminReapplyEventsScope // AdminRefreshWorkflowTasksScope is the metric scope for admin.RefreshWorkflowTasks AdminRefreshWorkflowTasksScope // AdminResendReplicationTasksScope is the metric scope for admin.ResendReplicationTasks AdminResendReplicationTasksScope // AdminRemoveTaskScope is the metric scope for admin.AdminRemoveTaskScope AdminRemoveTaskScope // AdminCloseShardScope is the metric scope for admin.AdminCloseShardScope AdminCloseShardScope // AdminResetQueueScope is the metric scope for admin.AdminResetQueueScope AdminResetQueueScope // AdminDescribeQueueScope is the metrics scope for admin.AdminDescribeQueueScope AdminDescribeQueueScope // AdminCountDLQMessagesScope is the metric scope for admin.AdminCountDLQMessagesScope AdminCountDLQMessagesScope // AdminReadDLQMessagesScope is the metric scope for admin.AdminReadDLQMessagesScope AdminReadDLQMessagesScope // AdminPurgeDLQMessagesScope is the metric scope for admin.AdminPurgeDLQMessagesScope AdminPurgeDLQMessagesScope // AdminMergeDLQMessagesScope is the metric scope for admin.AdminMergeDLQMessagesScope AdminMergeDLQMessagesScope // AdminDescribeShardDistributionScope is the metric scope for admin.DescribeShardDistribution AdminDescribeShardDistributionScope // AdminGetCrossClusterTasksScope is the metric scope for admin.GetCrossClusterTasks AdminGetCrossClusterTasksScope // AdminRespondCrossClusterTasksCompletedScope is the metric scope for admin.AdminRespondCrossClusterTasksCompleted AdminRespondCrossClusterTasksCompletedScope // AdminGetDynamicConfigScope is the metric scope for admin.GetDynamicConfig AdminGetDynamicConfigScope // AdminUpdateDynamicConfigScope is the metric scope for admin.UpdateDynamicConfig AdminUpdateDynamicConfigScope // AdminRestoreDynamicConfigScope is the metric scope for admin.RestoreDynamicConfig AdminRestoreDynamicConfigScope // AdminListDynamicConfigScope is the metric scope for admin.ListDynamicConfig AdminListDynamicConfigScope // AdminDeleteWorkflowScope is the metric scope for admin.DeleteWorkflow AdminDeleteWorkflowScope // GetGlobalIsolationGroups is the scope for getting global isolation groups GetGlobalIsolationGroups // UpdateGlobalIsolationGroups is the scope for getting global isolation groups UpdateGlobalIsolationGroups // GetDomainIsolationGroups is the scope for getting domain isolation groups GetDomainIsolationGroups // UpdateDomainIsolationGroups is the scope for getting domain isolation groups UpdateDomainIsolationGroups // GetDomainAsyncWorkflowConfiguraton is the scope for getting domain async workflow configuration GetDomainAsyncWorkflowConfiguraton // UpdateDomainAsyncWorkflowConfiguraton is the scope for updating domain async workflow configuration UpdateDomainAsyncWorkflowConfiguraton // UpdateTaskListPartitionConfig is the scope for update task list partition config UpdateTaskListPartitionConfig NumAdminScopes ) // -- Operation scopes for Frontend service -- const ( // FrontendRestartWorkflowExecutionScope is the metric for frontend.RestartWorkflowExecution FrontendRestartWorkflowExecutionScope = iota + NumAdminScopes // FrontendStartWorkflowExecutionScope is the metric scope for frontend.StartWorkflowExecution FrontendStartWorkflowExecutionScope // FrontendStartWorkflowExecutionAsyncScope is the metric scope for frontend.StartWorkflowExecutionAsync FrontendStartWorkflowExecutionAsyncScope // PollForDecisionTaskScope is the metric scope for frontend.PollForDecisionTask FrontendPollForDecisionTaskScope // FrontendPollForActivityTaskScope is the metric scope for frontend.PollForActivityTask FrontendPollForActivityTaskScope // FrontendRecordActivityTaskHeartbeatScope is the metric scope for frontend.RecordActivityTaskHeartbeat FrontendRecordActivityTaskHeartbeatScope // FrontendRecordActivityTaskHeartbeatByIDScope is the metric scope for frontend.RespondDecisionTaskCompleted FrontendRecordActivityTaskHeartbeatByIDScope // FrontendRespondDecisionTaskCompletedScope is the metric scope for frontend.RespondDecisionTaskCompleted FrontendRespondDecisionTaskCompletedScope // FrontendRespondDecisionTaskFailedScope is the metric scope for frontend.RespondDecisionTaskFailed FrontendRespondDecisionTaskFailedScope // FrontendRespondQueryTaskCompletedScope is the metric scope for frontend.RespondQueryTaskCompleted FrontendRespondQueryTaskCompletedScope // FrontendRespondActivityTaskCompletedScope is the metric scope for frontend.RespondActivityTaskCompleted FrontendRespondActivityTaskCompletedScope // FrontendRespondActivityTaskFailedScope is the metric scope for frontend.RespondActivityTaskFailed FrontendRespondActivityTaskFailedScope // FrontendRespondActivityTaskCanceledScope is the metric scope for frontend.RespondActivityTaskCanceled FrontendRespondActivityTaskCanceledScope // FrontendRespondActivityTaskCompletedScope is the metric scope for frontend.RespondActivityTaskCompletedByID FrontendRespondActivityTaskCompletedByIDScope // FrontendRespondActivityTaskFailedScope is the metric scope for frontend.RespondActivityTaskFailedByID FrontendRespondActivityTaskFailedByIDScope // FrontendRespondActivityTaskCanceledScope is the metric scope for frontend.RespondActivityTaskCanceledByID FrontendRespondActivityTaskCanceledByIDScope // FrontendGetWorkflowExecutionHistoryScope is the metric scope for frontend.GetWorkflowExecutionHistory FrontendGetWorkflowExecutionHistoryScope // FrontendGetWorkflowExecutionRawHistoryScope is the metric scope for frontend.GetWorkflowExecutionRawHistory FrontendGetWorkflowExecutionRawHistoryScope // FrontendPollForWorklfowExecutionRawHistoryScope is the metric scope for frontend.GetWorkflowExecutionRawHistory FrontendPollForWorklfowExecutionRawHistoryScope // FrontendSignalWorkflowExecutionScope is the metric scope for frontend.SignalWorkflowExecution FrontendSignalWorkflowExecutionScope // FrontendSignalWithStartWorkflowExecutionScope is the metric scope for frontend.SignalWithStartWorkflowExecution FrontendSignalWithStartWorkflowExecutionScope // FrontendSignalWithStartWorkflowExecutionAsyncScope is the metric scope for frontend.SignalWithStartWorkflowExecutionAsync FrontendSignalWithStartWorkflowExecutionAsyncScope // FrontendTerminateWorkflowExecutionScope is the metric scope for frontend.TerminateWorkflowExecution FrontendTerminateWorkflowExecutionScope // FrontendRequestCancelWorkflowExecutionScope is the metric scope for frontend.RequestCancelWorkflowExecution FrontendRequestCancelWorkflowExecutionScope // FrontendListArchivedWorkflowExecutionsScope is the metric scope for frontend.ListArchivedWorkflowExecutions FrontendListArchivedWorkflowExecutionsScope // FrontendListOpenWorkflowExecutionsScope is the metric scope for frontend.ListOpenWorkflowExecutions FrontendListOpenWorkflowExecutionsScope // FrontendListClosedWorkflowExecutionsScope is the metric scope for frontend.ListClosedWorkflowExecutions FrontendListClosedWorkflowExecutionsScope // FrontendListWorkflowExecutionsScope is the metric scope for frontend.ListWorkflowExecutions FrontendListWorkflowExecutionsScope // FrontendScanWorkflowExecutionsScope is the metric scope for frontend.ListWorkflowExecutions FrontendScanWorkflowExecutionsScope // FrontendCountWorkflowExecutionsScope is the metric scope for frontend.CountWorkflowExecutions FrontendCountWorkflowExecutionsScope // FrontendRegisterDomainScope is the metric scope for frontend.RegisterDomain FrontendRegisterDomainScope // FrontendDescribeDomainScope is the metric scope for frontend.DescribeDomain FrontendDescribeDomainScope // FrontendUpdateDomainScope is the metric scope for frontend.DescribeDomain FrontendUpdateDomainScope // FrontendDeleteDomainScope is the metric scope for frontend.DeleteDomain FrontendDeleteDomainScope // FrontendDeprecateDomainScope is the metric scope for frontend.DeprecateDomain FrontendDeprecateDomainScope // FrontendFailoverDomainScope is the metric scope for frontend.FailoverDomain FrontendFailoverDomainScope // FrontendListFailoverHistoryScope is the metric scope for frontend.ListFailoverHistory FrontendListFailoverHistoryScope // FrontendQueryWorkflowScope is the metric scope for frontend.QueryWorkflow FrontendQueryWorkflowScope // FrontendDescribeWorkflowExecutionScope is the metric scope for frontend.DescribeWorkflowExecution FrontendDescribeWorkflowExecutionScope // FrontendDiagnoseWorkflowExecutionScope is the metric scope for frontend.DescribeWorkflowExecution FrontendDiagnoseWorkflowExecutionScope // FrontendDescribeWorkflowExecutionStatusScope is a custom metric for more // rich details about workflow description calls, including workflow open/closed status FrontendDescribeWorkflowExecutionStatusScope // FrontendDescribeTaskListScope is the metric scope for frontend.DescribeTaskList FrontendDescribeTaskListScope // FrontendResetStickyTaskListScope is the metric scope for frontend.ResetStickyTaskList FrontendListTaskListPartitionsScope // FrontendGetTaskListsByDomainScope is the metric scope for frontend.ResetStickyTaskList FrontendGetTaskListsByDomainScope // FrontendRefreshWorkflowTasksScope is the metric scope for frontend.RefreshWorkflowTasks FrontendRefreshWorkflowTasksScope // FrontendResetStickyTaskListScope is the metric scope for frontend.ResetStickyTaskList FrontendResetStickyTaskListScope // FrontendListDomainsScope is the metric scope for frontend.ListDomain FrontendListDomainsScope // FrontendResetWorkflowExecutionScope is the metric scope for frontend.ResetWorkflowExecution FrontendResetWorkflowExecutionScope // FrontendGetSearchAttributesScope is the metric scope for frontend.GetSearchAttributes FrontendGetSearchAttributesScope // FrontendGetClusterInfoScope is the metric scope for frontend.GetClusterInfo FrontendGetClusterInfoScope NumFrontendScopes ) // -- Operation scopes for History service -- const ( // HistoryStartWorkflowExecutionScope tracks StartWorkflowExecution API calls received by service HistoryStartWorkflowExecutionScope = iota + NumFrontendScopes // HistoryRecordActivityTaskHeartbeatScope tracks RecordActivityTaskHeartbeat API calls received by service HistoryRecordActivityTaskHeartbeatScope // HistoryRespondDecisionTaskCompletedScope tracks RespondDecisionTaskCompleted API calls received by service HistoryRespondDecisionTaskCompletedScope // HistoryRespondDecisionTaskFailedScope tracks RespondDecisionTaskFailed API calls received by service HistoryRespondDecisionTaskFailedScope // HistoryRespondActivityTaskCompletedScope tracks RespondActivityTaskCompleted API calls received by service HistoryRespondActivityTaskCompletedScope // HistoryRespondActivityTaskFailedScope tracks RespondActivityTaskFailed API calls received by service HistoryRespondActivityTaskFailedScope // HistoryRespondActivityTaskCanceledScope tracks RespondActivityTaskCanceled API calls received by service HistoryRespondActivityTaskCanceledScope // HistoryResetQueueScope tracks ResetQueue API calls received by service HistoryResetQueueScope // HistoryDescribeQueueScope tracks DescribeQueue API calls received by service HistoryDescribeQueueScope // HistoryDescribeMutabelStateScope tracks DescribeMutableState API calls received by service HistoryDescribeMutabelStateScope // HistoryGetMutableStateScope tracks GetMutableState API calls received by service HistoryGetMutableStateScope // HistoryPollMutableStateScope tracks PollMutableState API calls received by service HistoryPollMutableStateScope // HistoryResetStickyTaskListScope tracks ResetStickyTaskList API calls received by service HistoryResetStickyTaskListScope // HistoryDescribeWorkflowExecutionScope tracks DescribeWorkflowExecution API calls received by service HistoryDescribeWorkflowExecutionScope // HistoryRecordDecisionTaskStartedScope tracks RecordDecisionTaskStarted API calls received by service HistoryRecordDecisionTaskStartedScope // HistoryRecordActivityTaskStartedScope tracks RecordActivityTaskStarted API calls received by service HistoryRecordActivityTaskStartedScope // HistorySignalWorkflowExecutionScope tracks SignalWorkflowExecution API calls received by service HistorySignalWorkflowExecutionScope // HistorySignalWithStartWorkflowExecutionScope tracks SignalWithStartWorkflowExecution API calls received by service HistorySignalWithStartWorkflowExecutionScope // HistoryRemoveSignalMutableStateScope tracks RemoveSignalMutableState API calls received by service HistoryRemoveSignalMutableStateScope // HistoryTerminateWorkflowExecutionScope tracks TerminateWorkflowExecution API calls received by service HistoryTerminateWorkflowExecutionScope // HistoryScheduleDecisionTaskScope tracks ScheduleDecisionTask API calls received by service HistoryScheduleDecisionTaskScope // HistoryRecordChildExecutionCompletedScope tracks CompleteChildExecution API calls received by service HistoryRecordChildExecutionCompletedScope // HistoryRequestCancelWorkflowExecutionScope tracks RequestCancelWorkflowExecution API calls received by service HistoryRequestCancelWorkflowExecutionScope // HistoryReplicateEventsScope tracks ReplicateEvents API calls received by service HistoryReplicateEventsScope // HistoryReplicateRawEventsScope tracks ReplicateEvents API calls received by service HistoryReplicateRawEventsScope // HistoryReplicateEventsV2Scope tracks ReplicateEvents API calls received by service HistoryReplicateEventsV2Scope // HistorySyncShardStatusScope tracks HistorySyncShardStatus API calls received by service HistorySyncShardStatusScope // HistorySyncActivityScope tracks HistoryActivity API calls received by service HistorySyncActivityScope // HistoryDescribeMutableStateScope tracks HistoryActivity API calls received by service HistoryDescribeMutableStateScope // GetReplicationMessages tracks GetReplicationMessages API calls received by service HistoryGetReplicationMessagesScope // HistoryGetDLQReplicationMessagesScope tracks GetReplicationMessages API calls received by service HistoryGetDLQReplicationMessagesScope // HistoryCountDLQMessagesScope tracks CountDLQMessages API calls received by service HistoryCountDLQMessagesScope // HistoryReadDLQMessagesScope tracks ReadDLQMessages API calls received by service HistoryReadDLQMessagesScope // HistoryPurgeDLQMessagesScope tracks PurgeDLQMessages API calls received by service HistoryPurgeDLQMessagesScope // HistoryMergeDLQMessagesScope tracks MergeDLQMessages API calls received by service HistoryMergeDLQMessagesScope // HistoryShardControllerScope is the scope used by shard controller HistoryShardControllerScope // HistoryReapplyEventsScope tracks ReapplyEvents API calls received by service HistoryReapplyEventsScope // HistoryRefreshWorkflowTasksScope tracks RefreshWorkflowTasks API calls received by service HistoryRefreshWorkflowTasksScope // HistoryNotifyFailoverMarkersScope is the scope used by notify failover marker API HistoryNotifyFailoverMarkersScope // HistoryGetCrossClusterTasksScope tracks GetCrossClusterTasks API calls received by service HistoryGetCrossClusterTasksScope // HistoryRespondCrossClusterTasksCompletedScope tracks RespondCrossClusterTasksCompleted API calls received by service HistoryRespondCrossClusterTasksCompletedScope // HistoryGetFailoverInfoScope tracks HistoryGetFailoverInfo API calls received by service HistoryGetFailoverInfoScope // HistoryRatelimitUpdateScope tracks RatelimitUpdate API calls received by the history service HistoryRatelimitUpdateScope // TaskPriorityAssignerScope is the scope used by all metric emitted by task priority assigner TaskPriorityAssignerScope // TransferQueueProcessorScope is the scope used by all metric emitted by transfer queue processor TransferQueueProcessorScope // TransferQueueProcessorV2Scope is the scope used by all metric emitted by transfer queue processor TransferQueueProcessorV2Scope // TransferActiveQueueProcessorScope is the scope used by all metric emitted by transfer queue processor TransferActiveQueueProcessorScope // TransferStandbyQueueProcessorScope is the scope used by all metric emitted by transfer queue processor TransferStandbyQueueProcessorScope // TransferActiveTaskActivityScope is the scope used for activity task processing by transfer queue processor TransferActiveTaskActivityScope // TransferActiveTaskDecisionScope is the scope used for decision task processing by transfer queue processor TransferActiveTaskDecisionScope // TransferActiveTaskCloseExecutionScope is the scope used for close execution task processing by transfer queue processor TransferActiveTaskCloseExecutionScope // TransferActiveTaskCancelExecutionScope is the scope used for cancel execution task processing by transfer queue processor TransferActiveTaskCancelExecutionScope // TransferActiveTaskSignalExecutionScope is the scope used for signal execution task processing by transfer queue processor TransferActiveTaskSignalExecutionScope // TransferActiveTaskStartChildExecutionScope is the scope used for start child execution task processing by transfer queue processor TransferActiveTaskStartChildExecutionScope // TransferActiveTaskRecordWorkflowStartedScope is the scope used for record workflow started task processing by transfer queue processor TransferActiveTaskRecordWorkflowStartedScope // TransferActiveTaskResetWorkflowScope is the scope used for record workflow started task processing by transfer queue processor TransferActiveTaskResetWorkflowScope // TransferActiveTaskUpsertWorkflowSearchAttributesScope is the scope used for upsert search attributes processing by transfer queue processor TransferActiveTaskUpsertWorkflowSearchAttributesScope // TransferActiveTaskRecordWorkflowClosedScope is the scope used for record workflow closed task processing by transfer queue processor TransferActiveTaskRecordWorkflowClosedScope // TransferActiveTaskRecordChildExecutionCompletedScope is the scope used for record child execution completed task processing by transfer queue processor TransferActiveTaskRecordChildExecutionCompletedScope // TransferActiveTaskApplyParentClosePolicyScope is the scope used for apply parent close policy task processing by transfer queue processor TransferActiveTaskApplyParentClosePolicyScope // TransferStandbyTaskResetWorkflowScope is the scope used for record workflow started task processing by transfer queue processor TransferStandbyTaskResetWorkflowScope // TransferStandbyTaskActivityScope is the scope used for activity task processing by transfer queue processor TransferStandbyTaskActivityScope // TransferStandbyTaskDecisionScope is the scope used for decision task processing by transfer queue processor TransferStandbyTaskDecisionScope // TransferStandbyTaskCloseExecutionScope is the scope used for close execution task processing by transfer queue processor TransferStandbyTaskCloseExecutionScope // TransferStandbyTaskCancelExecutionScope is the scope used for cancel execution task processing by transfer queue processor TransferStandbyTaskCancelExecutionScope // TransferStandbyTaskSignalExecutionScope is the scope used for signal execution task processing by transfer queue processor TransferStandbyTaskSignalExecutionScope // TransferStandbyTaskStartChildExecutionScope is the scope used for start child execution task processing by transfer queue processor TransferStandbyTaskStartChildExecutionScope // TransferStandbyTaskRecordWorkflowStartedScope is the scope used for record workflow started task processing by transfer queue processor TransferStandbyTaskRecordWorkflowStartedScope // TransferStandbyTaskUpsertWorkflowSearchAttributesScope is the scope used for upsert search attributes processing by transfer queue processor TransferStandbyTaskUpsertWorkflowSearchAttributesScope // TransferActiveTaskRecordWorkflowClosedScope is the scope used for record workflow closed task processing by transfer queue processor TransferStandbyTaskRecordWorkflowClosedScope // TransferActiveTaskRecordChildExecutionCompletedScope is the scope used for record child execution completed task processing by transfer queue processor TransferStandbyTaskRecordChildExecutionCompletedScope // TransferActiveTaskApplyParentClosePolicyScope is the scope used for apply parent close policy task processing by transfer queue processor TransferStandbyTaskApplyParentClosePolicyScope // TimerQueueProcessorScope is the scope used by all metric emitted by timer queue processor TimerQueueProcessorScope // TimerQueueProcessorV2Scope is the scope used by all metric emitted by timer queue processor TimerQueueProcessorV2Scope // TimerActiveQueueProcessorScope is the scope used by all metric emitted by timer queue processor TimerActiveQueueProcessorScope // TimerQueueProcessorScope is the scope used by all metric emitted by timer queue processor TimerStandbyQueueProcessorScope // TimerActiveTaskActivityTimeoutScope is the scope used by metric emitted by timer queue processor for processing activity timeouts TimerActiveTaskActivityTimeoutScope // TimerActiveTaskDecisionTimeoutScope is the scope used by metric emitted by timer queue processor for processing decision timeouts TimerActiveTaskDecisionTimeoutScope // TimerActiveTaskUserTimerScope is the scope used by metric emitted by timer queue processor for processing user timers TimerActiveTaskUserTimerScope // TimerActiveTaskWorkflowTimeoutScope is the scope used by metric emitted by timer queue processor for processing workflow timeouts. TimerActiveTaskWorkflowTimeoutScope // TimerActiveTaskActivityRetryTimerScope is the scope used by metric emitted by timer queue processor for processing retry task. TimerActiveTaskActivityRetryTimerScope // TimerActiveTaskWorkflowBackoffTimerScope is the scope used by metric emitted by timer queue processor for processing retry task. TimerActiveTaskWorkflowBackoffTimerScope // TimerActiveTaskDeleteHistoryEventScope is the scope used by metric emitted by timer queue processor for processing history event cleanup TimerActiveTaskDeleteHistoryEventScope // TimerStandbyTaskActivityTimeoutScope is the scope used by metric emitted by timer queue processor for processing activity timeouts TimerStandbyTaskActivityTimeoutScope // TimerStandbyTaskDecisionTimeoutScope is the scope used by metric emitted by timer queue processor for processing decision timeouts TimerStandbyTaskDecisionTimeoutScope // TimerStandbyTaskUserTimerScope is the scope used by metric emitted by timer queue processor for processing user timers TimerStandbyTaskUserTimerScope // TimerStandbyTaskWorkflowTimeoutScope is the scope used by metric emitted by timer queue processor for processing workflow timeouts. TimerStandbyTaskWorkflowTimeoutScope // TimerStandbyTaskActivityRetryTimerScope is the scope used by metric emitted by timer queue processor for processing retry task. TimerStandbyTaskActivityRetryTimerScope // TimerStandbyTaskDeleteHistoryEventScope is the scope used by metric emitted by timer queue processor for processing history event cleanup TimerStandbyTaskDeleteHistoryEventScope // TimerStandbyTaskWorkflowBackoffTimerScope is the scope used by metric emitted by timer queue processor for processing retry task. TimerStandbyTaskWorkflowBackoffTimerScope // CrossClusterQueueProcessorScope is the scope used by all metric emitted by cross cluster queue processor in the source cluster CrossClusterQueueProcessorScope // CrossClusterTaskProcessorScope is the scope used by all metric emitted by cross cluster task processor in the target cluster CrossClusterTaskProcessorScope // CrossClusterTaskFetcherScope is the scope used by all metrics emitted by cross cluster task fetcher in the target cluster CrossClusterTaskFetcherScope // CrossClusterSourceTaskStartChildExecutionScope is the scope used by metric emitted by cross cluster queue processor for processing start child workflow task. CrossClusterSourceTaskStartChildExecutionScope // CrossClusterSourceTaskCancelExecutionScope is the scope used by metric emitted by cross cluster queue processor for processing cancel workflow task. CrossClusterSourceTaskCancelExecutionScope // CrossClusterSourceTaskSignalExecutionScope is the scope used by metric emitted by cross cluster queue processor for processing signal workflow task. CrossClusterSourceTaskSignalExecutionScope // CrossClusterSourceTaskRecordChildWorkflowExecutionCompleteScope is the scope used by metric emitted by cross cluster queue processor for recording child workflow completion task. CrossClusterSourceTaskRecordChildWorkflowExecutionCompleteScope // CrossClusterSourceTaskApplyParentClosePolicyScope is the scope used by metric emitted by cross cluster queue processor for processing applying parent close policy CrossClusterSourceTaskApplyParentClosePolicyScope // CrossClusterTargetTaskStartChildExecutionScope is the scope used by metric emitted by cross cluster queue processor for processing start child workflow task. CrossClusterTargetTaskStartChildExecutionScope // CrossClusterTargetTaskCancelExecutionScope is the scope used by metric emitted by cross cluster queue processor for processing cancel workflow task. CrossClusterTargetTaskCancelExecutionScope // CrossClusterTargetTaskSignalExecutionScope is the scope used by metric emitted by cross cluster queue processor for processing signal workflow task. CrossClusterTargetTaskSignalExecutionScope // CrossClusterTargetTaskRecordChildWorkflowExecutionCompleteScope is the scope used by metric emitted by cross cluster queue processor for recording child workflow completion task. CrossClusterTargetTaskRecordChildWorkflowExecutionCompleteScope // CrossClusterTargetTaskApplyParentClosePolicyScope is the scope used by metric emitted by cross cluster queue processor for processing applying parent close policy CrossClusterTargetTaskApplyParentClosePolicyScope // HistoryEventNotificationScope is the scope used by shard history event notification HistoryEventNotificationScope // ReplicatorQueueProcessorScope is the scope used by all metric emitted by replicator queue processor ReplicatorQueueProcessorScope // ReplicatorCacheManagerScope is the scope used by all metric emitted by replicator cache manager ReplicatorCacheManagerScope // ReplicatorTaskHistoryScope is the scope used for history task processing by replicator queue processor ReplicatorTaskHistoryScope // ReplicatorTaskSyncActivityScope is the scope used for sync activity by replicator queue processor ReplicatorTaskSyncActivityScope // ReplicateHistoryEventsScope is the scope used by historyReplicator API for applying events ReplicateHistoryEventsScope // ReplicationMetricEmitterScope is the scope used by all metrics emitted by replication metric emitter ReplicationMetricEmitterScope // ShardInfoScope is the scope used when updating shard info ShardInfoScope // WorkflowContextScope is the scope used by WorkflowContext component WorkflowContextScope // HistoryCacheGetAndCreateScope is the scope used by history cache HistoryCacheGetAndCreateScope // HistoryCacheGetOrCreateScope is the scope used by history cache HistoryCacheGetOrCreateScope // HistoryCacheGetOrCreateCurrentScope is the scope used by history cache HistoryCacheGetOrCreateCurrentScope // HistoryCacheGetCurrentExecutionScope is the scope used by history cache for getting current execution HistoryCacheGetCurrentExecutionScope // EventsCacheGetEventScope is the scope used by events cache EventsCacheGetEventScope // EventsCachePutEventScope is the scope used by events cache EventsCachePutEventScope // EventsCacheGetFromStoreScope is the scope used by events cache EventsCacheGetFromStoreScope // ExecutionSizeStatsScope is the scope used for emiting workflow execution size related stats ExecutionSizeStatsScope // ExecutionCountStatsScope is the scope used for emiting workflow execution count related stats ExecutionCountStatsScope // SessionSizeStatsScope is the scope used for emiting session update size related stats SessionSizeStatsScope // SessionCountStatsScope is the scope used for emiting session update count related stats SessionCountStatsScope // HistoryResetWorkflowExecutionScope tracks ResetWorkflowExecution API calls received by service HistoryResetWorkflowExecutionScope // HistoryQueryWorkflowScope tracks QueryWorkflow API calls received by service HistoryQueryWorkflowScope // HistoryProcessDeleteHistoryEventScope tracks ProcessDeleteHistoryEvent processing calls HistoryProcessDeleteHistoryEventScope // WorkflowCompletionStatsScope tracks workflow completion updates WorkflowCompletionStatsScope // ArchiverClientScope is scope used by all metrics emitted by archiver.Client ArchiverClientScope // ReplicationTaskFetcherScope is scope used by all metrics emitted by ReplicationTaskFetcher ReplicationTaskFetcherScope // ReplicationTaskCleanupScope is scope used by all metrics emitted by ReplicationTaskProcessor cleanup ReplicationTaskCleanupScope // ReplicationDLQStatsScope is scope used by all metrics emitted related to replication DLQ ReplicationDLQStatsScope // FailoverMarkerScope is scope used by all metrics emitted related to failover marker FailoverMarkerScope // HistoryReplicationV2TaskScope is the scope used by history task replication processing HistoryReplicationV2TaskScope // SyncActivityTaskScope is the scope used by sync activity information processing SyncActivityTaskScope // LargeExecutionSizeShardScope is the scope to track large history size for hotshard detection LargeExecutionSizeShardScope // LargeExecutionCountShardScope is the scope to track large history count for hotshard detection LargeExecutionCountShardScope // LargeExecutionBlobShardScope is the scope to track large blobs for hotshard detection LargeExecutionBlobShardScope // HistoryExecutionCacheScope is the scope used by history execution cache HistoryExecutionCacheScope // HistoryWorkflowCacheScope is the scope used by history workflow cache HistoryWorkflowCacheScope // HistoryFlushBufferedEventsScope is the scope used by history when flushing buffered events HistoryFlushBufferedEventsScope // HistoryTaskSchedulerMigrationScope is the scope used by history task scheduler migration HistoryTaskSchedulerMigrationScope NumHistoryScopes ) // -- Operation scopes for Matching service -- const ( // PollForDecisionTaskScope tracks PollForDecisionTask API calls received by service MatchingPollForDecisionTaskScope = iota + NumHistoryScopes // PollForActivityTaskScope tracks PollForActivityTask API calls received by service MatchingPollForActivityTaskScope // MatchingAddActivityTaskScope tracks AddActivityTask API calls received by service MatchingAddActivityTaskScope // MatchingAddDecisionTaskScope tracks AddDecisionTask API calls received by service MatchingAddDecisionTaskScope // MatchingAddTaskScope tracks both AddActivityTask and AddDevisionTask API calls received by service MatchingAddTaskScope // MatchingTaskListMgrScope is the metrics scope for matching.TaskListManager component MatchingTaskListMgrScope // MatchingAdaptiveScalerScope is hte metrics scope for matching's Adaptive Scaler component MatchingAdaptiveScalerScope // MatchingQueryWorkflowScope tracks AddDecisionTask API calls received by service MatchingQueryWorkflowScope // MatchingRespondQueryTaskCompletedScope tracks AddDecisionTask API calls received by service MatchingRespondQueryTaskCompletedScope // MatchingCancelOutstandingPollScope tracks CancelOutstandingPoll API calls received by service MatchingCancelOutstandingPollScope // MatchingDescribeTaskListScope tracks DescribeTaskList API calls received by service MatchingDescribeTaskListScope // MatchingListTaskListPartitionsScope tracks ListTaskListPartitions API calls received by service MatchingListTaskListPartitionsScope // MatchingGetTaskListsByDomainScope tracks GetTaskListsByDomain API calls received by service MatchingGetTaskListsByDomainScope // MatchingUpdateTaskListPartitionConfigScope tracks UpdateTaskListPartitionConfig API calls received by service MatchingUpdateTaskListPartitionConfigScope // MatchingRefreshTaskListPartitionConfigScope tracks RefreshTaskListPartitionConfig API calls received by service MatchingRefreshTaskListPartitionConfigScope NumMatchingScopes ) // -- Operation scopes for Worker service -- const ( // ReplicationScope is the scope used by all metric emitted by replicator ReplicatorScope = iota + NumMatchingScopes // DomainReplicationTaskScope is the scope used by domain task replication processing DomainReplicationTaskScope // ESProcessorScope is scope used by all metric emitted by esProcessor ESProcessorScope // IndexProcessorScope is scope used by all metric emitted by index processor IndexProcessorScope // ArchiverDeleteHistoryActivityScope is scope used by all metrics emitted by archiver.DeleteHistoryActivity ArchiverDeleteHistoryActivityScope // ArchiverUploadHistoryActivityScope is scope used by all metrics emitted by archiver.UploadHistoryActivity ArchiverUploadHistoryActivityScope // ArchiverArchiveVisibilityActivityScope is scope used by all metrics emitted by archiver.ArchiveVisibilityActivity ArchiverArchiveVisibilityActivityScope // ArchiverScope is scope used by all metrics emitted by archiver.Archiver ArchiverScope // ArchiverPumpScope is scope used by all metrics emitted by archiver.Pump ArchiverPumpScope // ArchiverArchivalWorkflowScope is scope used by all metrics emitted by archiver.ArchivalWorkflow ArchiverArchivalWorkflowScope // TaskListScavengerScope is scope used by all metrics emitted by worker.tasklist.Scavenger module TaskListScavengerScope // ExecutionsScannerScope is scope used by all metrics emitted by worker.executions.Scanner module ExecutionsScannerScope // ExecutionsFixerScope is the scope used by all metrics emitted by worker.executions.Fixer module ExecutionsFixerScope // BatcherScope is scope used by all metrics emitted by worker.Batcher module BatcherScope // HistoryScavengerScope is scope used by all metrics emitted by worker.history.Scavenger module HistoryScavengerScope // ParentClosePolicyProcessorScope is scope used by all metrics emitted by worker.ParentClosePolicyProcessor ParentClosePolicyProcessorScope // ShardScannerScope is scope used by all metrics emitted by worker.shardscanner module ShardScannerScope // CheckDataCorruptionWorkflowScope is scope used by the data corruption workflow CheckDataCorruptionWorkflowScope // ESAnalyzerScope is scope used by ElasticSearch Analyzer (esanalyzer) workflow ESAnalyzerScope // AsyncWorkflowConsumerScope is scope used by async workflow consumer AsyncWorkflowConsumerScope // DiagnosticsWorkflowScope is scope used by diagnostics workflow DiagnosticsWorkflowScope NumWorkerScopes ) // -- Operation scopes for ShardDistributor service -- const ( // ShardDistributorGetShardOwnerScope tracks GetShardOwner API calls received by service ShardDistributorGetShardOwnerScope = iota + NumWorkerScopes ShardDistributorWatchNamespaceStateScope ShardDistributorHeartbeatScope ShardDistributorAssignLoopScope ShardDistributorStoreGetShardOwnerScope ShardDistributorStoreAssignShardScope ShardDistributorStoreAssignShardsScope ShardDistributorStoreDeleteExecutorsScope ShardDistributorStoreGetShardStatsScope ShardDistributorStoreDeleteShardStatsScope ShardDistributorStoreGetHeartbeatScope ShardDistributorStoreGetExecutorScope ShardDistributorStoreGetStateScope ShardDistributorStoreRecordHeartbeatScope ShardDistributorStoreSubscribeToExecutorStatusChangesScope ShardDistributorStoreSubscribeToAssignmentChangesScope ShardDistributorStoreDeleteAssignedStatesScope // The scope for the shard distributor executor ShardDistributorExecutorScope // ShardDistributorWatchScope tracks etcd watch stream processing ShardDistributorWatchScope NumShardDistributorScopes ) // ScopeDefs record the scopes for all services var ScopeDefs = map[ServiceIdx]map[ScopeIdx]scopeDefinition{ // common scope Names Common: { PersistenceCreateShardScope: {operation: "CreateShard"}, PersistenceGetShardScope: {operation: "GetShard"}, PersistenceUpdateShardScope: {operation: "UpdateShard"}, PersistenceCreateWorkflowExecutionScope: {operation: "CreateWorkflowExecution"}, PersistenceGetWorkflowExecutionScope: {operation: "GetWorkflowExecution"}, PersistenceUpdateWorkflowExecutionScope: {operation: "UpdateWorkflowExecution"}, PersistenceConflictResolveWorkflowExecutionScope: {operation: "ConflictResolveWorkflowExecution"}, PersistenceResetWorkflowExecutionScope: {operation: "ResetWorkflowExecution"}, PersistenceDeleteWorkflowExecutionScope: {operation: "DeleteWorkflowExecution"}, PersistenceDeleteCurrentWorkflowExecutionScope: {operation: "DeleteCurrentWorkflowExecution"}, PersistenceGetCurrentExecutionScope: {operation: "GetCurrentExecution"}, PersistenceIsWorkflowExecutionExistsScope: {operation: "IsWorkflowExecutionExists"}, PersistenceListCurrentExecutionsScope: {operation: "ListCurrentExecutions"}, PersistenceListConcreteExecutionsScope: {operation: "ListConcreteExecutions"}, PersistenceGetTransferTasksScope: {operation: "GetTransferTasks"}, PersistenceCompleteTransferTaskScope: {operation: "CompleteTransferTask"}, PersistenceGetCrossClusterTasksScope: {operation: "GetCrossClusterTasks"}, PersistenceCompleteCrossClusterTaskScope: {operation: "GetCrossClusterTasks"}, PersistenceGetReplicationTasksScope: {operation: "GetReplicationTasks"}, PersistenceCompleteReplicationTaskScope: {operation: "CompleteReplicationTask"}, PersistencePutReplicationTaskToDLQScope: {operation: "PutReplicationTaskToDLQ"}, PersistenceGetReplicationTasksFromDLQScope: {operation: "GetReplicationTasksFromDLQ"}, PersistenceGetReplicationDLQSizeScope: {operation: "GetReplicationDLQSize"}, PersistenceDeleteReplicationTaskFromDLQScope: {operation: "DeleteReplicationTaskFromDLQ"}, PersistenceRangeDeleteReplicationTaskFromDLQScope: {operation: "RangeDeleteReplicationTaskFromDLQ"}, PersistenceCreateFailoverMarkerTasksScope: {operation: "CreateFailoverMarkerTasks"}, PersistenceGetTimerIndexTasksScope: {operation: "GetTimerIndexTasks"}, PersistenceCompleteTimerTaskScope: {operation: "CompleteTimerTask"}, PersistenceGetHistoryTasksScope: {operation: "GetHistoryTasks"}, PersistenceCompleteHistoryTaskScope: {operation: "CompleteHistoryTask"}, PersistenceRangeCompleteHistoryTaskScope: {operation: "RangeCompleteHistoryTask"}, PersistenceCreateTasksScope: {operation: "CreateTask"}, PersistenceGetTasksScope: {operation: "GetTasks"}, PersistenceCompleteTaskScope: {operation: "CompleteTask"}, PersistenceCompleteTasksLessThanScope: {operation: "CompleteTasksLessThan"}, PersistenceGetOrphanTasksScope: {operation: "GetOrphanTasks"}, PersistenceLeaseTaskListScope: {operation: "LeaseTaskList"}, PersistenceGetTaskListScope: {operation: "GetTaskList"}, PersistenceUpdateTaskListScope: {operation: "UpdateTaskList"}, PersistenceListTaskListScope: {operation: "ListTaskList"}, PersistenceDeleteTaskListScope: {operation: "DeleteTaskList"}, PersistenceGetTaskListSizeScope: {operation: "GetTaskListSize"}, PersistenceAppendHistoryEventsScope: {operation: "AppendHistoryEvents"}, PersistenceGetWorkflowExecutionHistoryScope: {operation: "GetWorkflowExecutionHistory"}, PersistenceDeleteWorkflowExecutionHistoryScope: {operation: "DeleteWorkflowExecutionHistory"}, PersistenceCreateDomainScope: {operation: "CreateDomain"}, PersistenceGetDomainScope: {operation: "GetDomain"}, PersistenceUpdateDomainScope: {operation: "UpdateDomain"}, PersistenceDeleteDomainScope: {operation: "DeleteDomain"}, PersistenceDeleteDomainByNameScope: {operation: "DeleteDomainByName"}, PersistenceListDomainsScope: {operation: "ListDomain"}, PersistenceGetMetadataScope: {operation: "GetMetadata"}, PersistenceRecordWorkflowExecutionStartedScope: {operation: "RecordWorkflowExecutionStarted"}, PersistenceRecordWorkflowExecutionClosedScope: {operation: "RecordWorkflowExecutionClosed"}, PersistenceRecordWorkflowExecutionUninitializedScope: {operation: "RecordWorkflowExecutionUninitialized"}, PersistenceUpsertWorkflowExecutionScope: {operation: "UpsertWorkflowExecution"}, PersistenceListOpenWorkflowExecutionsScope: {operation: "ListOpenWorkflowExecutions"}, PersistenceListClosedWorkflowExecutionsScope: {operation: "ListClosedWorkflowExecutions"}, PersistenceListOpenWorkflowExecutionsByTypeScope: {operation: "ListOpenWorkflowExecutionsByType"}, PersistenceListClosedWorkflowExecutionsByTypeScope: {operation: "ListClosedWorkflowExecutionsByType"}, PersistenceListOpenWorkflowExecutionsByWorkflowIDScope: {operation: "ListOpenWorkflowExecutionsByWorkflowID"}, PersistenceListClosedWorkflowExecutionsByWorkflowIDScope: {operation: "ListClosedWorkflowExecutionsByWorkflowID"}, PersistenceListClosedWorkflowExecutionsByStatusScope: {operation: "ListClosedWorkflowExecutionsByStatus"}, PersistenceGetClosedWorkflowExecutionScope: {operation: "GetClosedWorkflowExecution"}, PersistenceVisibilityDeleteWorkflowExecutionScope: {operation: "VisibilityDeleteWorkflowExecution"}, PersistenceDeleteUninitializedWorkflowExecutionScope: {operation: "VisibilityDeleteUninitializedWorkflowExecution"}, PersistenceListWorkflowExecutionsScope: {operation: "ListWorkflowExecutions"}, PersistenceScanWorkflowExecutionsScope: {operation: "ScanWorkflowExecutions"}, PersistenceCountWorkflowExecutionsScope: {operation: "CountWorkflowExecutions"}, PersistenceAppendHistoryNodesScope: {operation: "AppendHistoryNodes"}, PersistenceReadHistoryBranchScope: {operation: "ReadHistoryBranch"}, PersistenceReadHistoryBranchByBatchScope: {operation: "ReadHistoryBranch"}, PersistenceReadRawHistoryBranchScope: {operation: "ReadHistoryBranch"}, PersistenceForkHistoryBranchScope: {operation: "ForkHistoryBranch"}, PersistenceDeleteHistoryBranchScope: {operation: "DeleteHistoryBranch"}, PersistenceCompleteForkBranchScope: {operation: "CompleteForkBranch"}, PersistenceGetHistoryTreeScope: {operation: "GetHistoryTree"}, PersistenceGetAllHistoryTreeBranchesScope: {operation: "GetAllHistoryTreeBranches"}, PersistenceEnqueueMessageScope: {operation: "EnqueueMessage"}, PersistenceEnqueueMessageToDLQScope: {operation: "EnqueueMessageToDLQ"}, PersistenceReadMessagesScope: {operation: "ReadQueueMessages"}, PersistenceReadMessagesFromDLQScope: {operation: "ReadQueueMessagesFromDLQ"}, PersistenceDeleteMessagesBeforeScope: {operation: "DeleteQueueMessages"}, PersistenceDeleteMessageFromDLQScope: {operation: "DeleteQueueMessageFromDLQ"}, PersistenceRangeDeleteMessagesFromDLQScope: {operation: "RangeDeleteMessagesFromDLQ"}, PersistenceUpdateAckLevelScope: {operation: "UpdateAckLevel"}, PersistenceGetAckLevelsScope: {operation: "GetAckLevel"}, PersistenceUpdateDLQAckLevelScope: {operation: "UpdateDLQAckLevel"}, PersistenceGetDLQAckLevelsScope: {operation: "GetDLQAckLevel"}, PersistenceGetDLQSizeScope: {operation: "GetDLQSize"}, PersistenceFetchDynamicConfigScope: {operation: "FetchDynamicConfig"}, PersistenceUpdateDynamicConfigScope: {operation: "UpdateDynamicConfig"}, PersistenceShardRequestCountScope: {operation: "ShardIdPersistenceRequest"}, PersistenceGetActiveClusterSelectionPolicyScope: {operation: "GetActiveClusterSelectionPolicy"}, PersistenceDeleteActiveClusterSelectionPolicyScope: {operation: "DeleteActiveClusterSelectionPolicy"}, ResolverHostNotFoundScope: {operation: "ResolverHostNotFound"}, ClusterMetadataArchivalConfigScope: {operation: "ArchivalConfig"}, HistoryClientStartWorkflowExecutionScope: {operation: "HistoryClientStartWorkflowExecution", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientDescribeHistoryHostScope: {operation: "HistoryClientDescribeHistoryHost", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRemoveTaskScope: {operation: "HistoryClientRemoveTask", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientCloseShardScope: {operation: "HistoryClientCloseShard", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientResetQueueScope: {operation: "HistoryClientResetQueue", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientDescribeQueueScope: {operation: "HistoryClientDescribeQueue", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRecordActivityTaskHeartbeatScope: {operation: "HistoryClientRecordActivityTaskHeartbeat", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRespondDecisionTaskCompletedScope: {operation: "HistoryClientRespondDecisionTaskCompleted", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRespondDecisionTaskFailedScope: {operation: "HistoryClientRespondDecisionTaskFailed", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRespondActivityTaskCompletedScope: {operation: "HistoryClientRespondActivityTaskCompleted", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRespondActivityTaskFailedScope: {operation: "HistoryClientRespondActivityTaskFailed", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRespondActivityTaskCanceledScope: {operation: "HistoryClientRespondActivityTaskCanceled", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientDescribeMutableStateScope: {operation: "HistoryClientDescribeMutableState", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientGetMutableStateScope: {operation: "HistoryClientGetMutableState", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientPollMutableStateScope: {operation: "HistoryClientPollMutableState", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientResetStickyTaskListScope: {operation: "HistoryClientResetStickyTaskList", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientDescribeWorkflowExecutionScope: {operation: "HistoryClientDescribeWorkflowExecution", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRecordDecisionTaskStartedScope: {operation: "HistoryClientRecordDecisionTaskStarted", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRecordActivityTaskStartedScope: {operation: "HistoryClientRecordActivityTaskStarted", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRequestCancelWorkflowExecutionScope: {operation: "HistoryClientRequestCancelWorkflowExecution", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientSignalWorkflowExecutionScope: {operation: "HistoryClientSignalWorkflowExecution", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientSignalWithStartWorkflowExecutionScope: {operation: "HistoryClientSignalWithStartWorkflowExecution", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRemoveSignalMutableStateScope: {operation: "HistoryClientRemoveSignalMutableState", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientTerminateWorkflowExecutionScope: {operation: "HistoryClientTerminateWorkflowExecution", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientResetWorkflowExecutionScope: {operation: "HistoryClientResetWorkflowExecution", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientScheduleDecisionTaskScope: {operation: "HistoryClientScheduleDecisionTask", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRecordChildExecutionCompletedScope: {operation: "HistoryClientRecordChildExecutionCompleted", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientReplicateEventsV2Scope: {operation: "HistoryClientReplicateEventsV2", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientSyncShardStatusScope: {operation: "HistoryClientSyncShardStatus", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientSyncActivityScope: {operation: "HistoryClientSyncActivity", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientGetReplicationTasksScope: {operation: "HistoryClientGetReplicationTasks", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientGetDLQReplicationTasksScope: {operation: "HistoryClientGetDLQReplicationTasks", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientQueryWorkflowScope: {operation: "HistoryClientQueryWorkflow", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientReapplyEventsScope: {operation: "HistoryClientReapplyEvents", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientCountDLQMessagesScope: {operation: "HistoryClientCountDLQMessages", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientReadDLQMessagesScope: {operation: "HistoryClientReadDLQMessages", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientPurgeDLQMessagesScope: {operation: "HistoryClientPurgeDLQMessages", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientMergeDLQMessagesScope: {operation: "HistoryClientMergeDLQMessages", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRefreshWorkflowTasksScope: {operation: "HistoryClientRefreshWorkflowTasks", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientNotifyFailoverMarkersScope: {operation: "HistoryClientNotifyFailoverMarkers", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientGetCrossClusterTasksScope: {operation: "HistoryClientGetCrossClusterTasks", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRespondCrossClusterTasksCompletedScope: {operation: "HistoryClientRespondCrossClusterTasksCompleted", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientGetFailoverInfoScope: {operation: "HistoryClientGetFailoverInfo", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientGetDLQReplicationMessagesScope: {operation: "HistoryClientGetDLQReplicationMessages", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientGetReplicationMessagesScope: {operation: "HistoryClientGetReplicationMessages", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientWfIDCacheScope: {operation: "HistoryClientWfIDCache", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, HistoryClientRatelimitUpdateScope: {operation: "HistoryClientRatelimitUpdate", tags: map[string]string{CadenceRoleTagName: HistoryClientRoleTagValue}}, MatchingClientPollForDecisionTaskScope: {operation: "MatchingClientPollForDecisionTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientPollForActivityTaskScope: {operation: "MatchingClientPollForActivityTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientAddActivityTaskScope: {operation: "MatchingClientAddActivityTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientAddDecisionTaskScope: {operation: "MatchingClientAddDecisionTask", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientQueryWorkflowScope: {operation: "MatchingClientQueryWorkflow", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientRespondQueryTaskCompletedScope: {operation: "MatchingClientRespondQueryTaskCompleted", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientCancelOutstandingPollScope: {operation: "MatchingClientCancelOutstandingPoll", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientDescribeTaskListScope: {operation: "MatchingClientDescribeTaskList", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientListTaskListPartitionsScope: {operation: "MatchingClientListTaskListPartitions", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientGetTaskListsByDomainScope: {operation: "MatchingClientGetTaskListsByDomain", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientUpdateTaskListPartitionConfigScope: {operation: "MatchingClientUpdateTaskListPartitionConfig", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, MatchingClientRefreshTaskListPartitionConfigScope: {operation: "MatchingClientRefreshTaskListPartitionConfig", tags: map[string]string{CadenceRoleTagName: MatchingClientRoleTagValue}}, FrontendClientDeleteDomainScope: {operation: "FrontendClientDeleteDomain", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientDeprecateDomainScope: {operation: "FrontendClientDeprecateDomain", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientDescribeDomainScope: {operation: "FrontendClientDescribeDomain", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientDescribeTaskListScope: {operation: "FrontendClientDescribeTaskList", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientDescribeWorkflowExecutionScope: {operation: "FrontendClientDescribeWorkflowExecution", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientDiagnoseWorkflowExecutionScope: {operation: "FrontendClientDiagnoseWorkflowExecution", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientGetWorkflowExecutionHistoryScope: {operation: "FrontendClientGetWorkflowExecutionHistory", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientGetWorkflowExecutionRawHistoryScope: {operation: "FrontendClientGetWorkflowExecutionRawHistory", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientPollForWorkflowExecutionRawHistoryScope: {operation: "FrontendClientPollForWorkflowExecutionRawHistory", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientListArchivedWorkflowExecutionsScope: {operation: "FrontendClientListArchivedWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientListClosedWorkflowExecutionsScope: {operation: "FrontendClientListClosedWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientListDomainsScope: {operation: "FrontendClientListDomains", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientListOpenWorkflowExecutionsScope: {operation: "FrontendClientListOpenWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientPollForActivityTaskScope: {operation: "FrontendClientPollForActivityTask", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientPollForDecisionTaskScope: {operation: "FrontendClientPollForDecisionTask", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientQueryWorkflowScope: {operation: "FrontendClientQueryWorkflow", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRecordActivityTaskHeartbeatScope: {operation: "FrontendClientRecordActivityTaskHeartbeat", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRecordActivityTaskHeartbeatByIDScope: {operation: "FrontendClientRecordActivityTaskHeartbeatByID", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRegisterDomainScope: {operation: "FrontendClientRegisterDomain", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRequestCancelWorkflowExecutionScope: {operation: "FrontendClientRequestCancelWorkflowExecution", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientResetStickyTaskListScope: {operation: "FrontendClientResetStickyTaskList", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRefreshWorkflowTasksScope: {operation: "FrontendClientRefreshWorkflowTasks", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientResetWorkflowExecutionScope: {operation: "FrontendClientResetWorkflowExecution", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRespondActivityTaskCanceledScope: {operation: "FrontendClientRespondActivityTaskCanceled", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRespondActivityTaskCanceledByIDScope: {operation: "FrontendClientRespondActivityTaskCanceledByID", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRespondActivityTaskCompletedScope: {operation: "FrontendClientRespondActivityTaskCompleted", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRespondActivityTaskCompletedByIDScope: {operation: "FrontendClientRespondActivityTaskCompletedByID", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRespondActivityTaskFailedScope: {operation: "FrontendClientRespondActivityTaskFailed", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRespondActivityTaskFailedByIDScope: {operation: "FrontendClientRespondActivityTaskFailedByID", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRespondDecisionTaskCompletedScope: {operation: "FrontendClientRespondDecisionTaskCompleted", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRespondDecisionTaskFailedScope: {operation: "FrontendClientRespondDecisionTaskFailed", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRespondQueryTaskCompletedScope: {operation: "FrontendClientRespondQueryTaskCompleted", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientSignalWithStartWorkflowExecutionScope: {operation: "FrontendClientSignalWithStartWorkflowExecution", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientSignalWithStartWorkflowExecutionAsyncScope: {operation: "FrontendClientSignalWithStartWorkflowExecutionAsync", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientSignalWorkflowExecutionScope: {operation: "FrontendClientSignalWorkflowExecution", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientStartWorkflowExecutionScope: {operation: "FrontendClientStartWorkflowExecution", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientStartWorkflowExecutionAsyncScope: {operation: "FrontendClientStartWorkflowExecutionAsync", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientTerminateWorkflowExecutionScope: {operation: "FrontendClientTerminateWorkflowExecution", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientUpdateDomainScope: {operation: "FrontendClientUpdateDomain", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientFailoverDomainScope: {operation: "FrontendClientFailoverDomain", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientListFailoverHistoryScope: {operation: "FrontendClientListFailoverHistory", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientListWorkflowExecutionsScope: {operation: "FrontendClientListWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientScanWorkflowExecutionsScope: {operation: "FrontendClientScanWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientCountWorkflowExecutionsScope: {operation: "FrontendClientCountWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientGetSearchAttributesScope: {operation: "FrontendClientGetSearchAttributes", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientGetReplicationTasksScope: {operation: "FrontendClientGetReplicationTasks", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientGetDomainReplicationTasksScope: {operation: "FrontendClientGetDomainReplicationTasks", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientGetDLQReplicationTasksScope: {operation: "FrontendClientGetDLQReplicationTasks", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientReapplyEventsScope: {operation: "FrontendClientReapplyEvents", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientGetClusterInfoScope: {operation: "FrontendClientGetClusterInfo", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientListTaskListPartitionsScope: {operation: "FrontendClientListTaskListPartitions", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientGetTaskListsByDomainScope: {operation: "FrontendClientGetTaskListsByDomain", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, FrontendClientRestartWorkflowExecutionScope: {operation: "FrontendClientRestartWorkflowExecution", tags: map[string]string{CadenceRoleTagName: FrontendClientRoleTagValue}}, AdminClientGetReplicationTasksScope: {operation: "AdminClientGetReplicationTasks", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientAddSearchAttributeScope: {operation: "AdminClientAddSearchAttribute", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientDescribeHistoryHostScope: {operation: "AdminClientDescribeHistoryHost", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientDescribeShardDistributionScope: {operation: "AdminClientDescribeShardDistribution", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientDescribeWorkflowExecutionScope: {operation: "AdminClientDescribeWorkflowExecution", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientGetWorkflowExecutionRawHistoryV2Scope: {operation: "AdminClientGetWorkflowExecutionRawHistoryV2", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientDescribeClusterScope: {operation: "AdminClientDescribeCluster", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientRefreshWorkflowTasksScope: {operation: "AdminClientRefreshWorkflowTasks", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientResendReplicationTasksScope: {operation: "AdminClientResendReplicationTasks", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientCloseShardScope: {operation: "AdminClientCloseShard", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientRemoveTaskScope: {operation: "AdminClientRemoveTask", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientResetQueueScope: {operation: "AdminClientResetQueue", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientDescribeQueueScope: {operation: "AdminClientDescribeQueue", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientCountDLQMessagesScope: {operation: "AdminClientCountDLQMessages", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientReadDLQMessagesScope: {operation: "AdminClientReadDLQMessages", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientPurgeDLQMessagesScope: {operation: "AdminClientPurgeDLQMessages", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientMergeDLQMessagesScope: {operation: "AdminClientMergeDLQMessages", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientGetCrossClusterTasksScope: {operation: "AdminClientGetCrossClusterTasks", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientRespondCrossClusterTasksCompletedScope: {operation: "AdminClientRespondCrossClusterTasksCompleted", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientGetDynamicConfigScope: {operation: "AdminClientGetDynamicConfig", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientUpdateDynamicConfigScope: {operation: "AdminClientUpdateDynamicConfig", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientRestoreDynamicConfigScope: {operation: "AdminClientRestoreDynamicConfig", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientListDynamicConfigScope: {operation: "AdminClientListDynamicConfigScope", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientGetGlobalIsolationGroupsScope: {operation: "AdminClientGetGlobalIsolationGroups", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientUpdateGlobalIsolationGroupsScope: {operation: "AdminClientUpdateGlobalIsolationGroups", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientGetDomainIsolationGroupsScope: {operation: "AdminClientGetDomainIsolationGroups", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientUpdateDomainIsolationGroupsScope: {operation: "AdminClientUpdateDomainIsolationGroups", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientDeleteWorkflowScope: {operation: "AdminClientDeleteWorkflow", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientMaintainCorruptWorkflowScope: {operation: "AdminClientMaintainCorruptWorkflow", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientReapplyEventsScope: {operation: "AdminClientReapplyEvents", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientGetDLQReplicationMessagesScope: {operation: "AdminClientGetDLQReplicationMessages", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientGetDomainReplicationMessagesScope: {operation: "AdminClientGetDomainReplicationMessages", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientGetReplicationMessagesScope: {operation: "AdminClientGetReplicationMessages", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientGetDomainAsyncWorkflowConfiguratonScope: {operation: "AdminClientGetDomainAsyncWorkflowConfiguraton", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientUpdateDomainAsyncWorkflowConfiguratonScope: {operation: "AdminClientUpdateDomainAsyncWorkflowConfiguraton", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, AdminClientUpdateTaskListPartitionConfigScope: {operation: "AdminClientUpdateTaskListPartitionConfig", tags: map[string]string{CadenceRoleTagName: AdminClientRoleTagValue}}, DCRedirectionDeleteDomainScope: {operation: "DCRedirectionDeleteDomain", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionDeprecateDomainScope: {operation: "DCRedirectionDeprecateDomain", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionFailoverDomainScope: {operation: "DCRedirectionFailoverDomain", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionListFailoverHistoryScope: {operation: "DCRedirectionListFailoverHistory", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionDescribeDomainScope: {operation: "DCRedirectionDescribeDomain", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionDescribeTaskListScope: {operation: "DCRedirectionDescribeTaskList", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionDescribeWorkflowExecutionScope: {operation: "DCRedirectionDescribeWorkflowExecution", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionDiagnoseWorkflowExecutionScope: {operation: "DCRedirectionDiagnoseWorkflowExecution", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionGetWorkflowExecutionHistoryScope: {operation: "DCRedirectionGetWorkflowExecutionHistory", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionGetWorkflowExecutionRawHistoryScope: {operation: "DCRedirectionGetWorkflowExecutionRawHistoryScope", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionPollForWorklfowExecutionRawHistoryScope: {operation: "DCRedirectionPollForWorklfowExecutionRawHistory", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionListArchivedWorkflowExecutionsScope: {operation: "DCRedirectionListArchivedWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionListClosedWorkflowExecutionsScope: {operation: "DCRedirectionListClosedWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionListDomainsScope: {operation: "DCRedirectionListDomains", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionListOpenWorkflowExecutionsScope: {operation: "DCRedirectionListOpenWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionListWorkflowExecutionsScope: {operation: "DCRedirectionListWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionScanWorkflowExecutionsScope: {operation: "DCRedirectionScanWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionCountWorkflowExecutionsScope: {operation: "DCRedirectionCountWorkflowExecutions", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionGetSearchAttributesScope: {operation: "DCRedirectionGetSearchAttributes", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionPollForActivityTaskScope: {operation: "DCRedirectionPollForActivityTask", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionPollForDecisionTaskScope: {operation: "DCRedirectionPollForDecisionTask", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionQueryWorkflowScope: {operation: "DCRedirectionQueryWorkflow", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRecordActivityTaskHeartbeatScope: {operation: "DCRedirectionRecordActivityTaskHeartbeat", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRecordActivityTaskHeartbeatByIDScope: {operation: "DCRedirectionRecordActivityTaskHeartbeatByID", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRegisterDomainScope: {operation: "DCRedirectionRegisterDomain", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRequestCancelWorkflowExecutionScope: {operation: "DCRedirectionRequestCancelWorkflowExecution", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionResetStickyTaskListScope: {operation: "DCRedirectionResetStickyTaskList", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionResetWorkflowExecutionScope: {operation: "DCRedirectionResetWorkflowExecution", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRespondActivityTaskCanceledScope: {operation: "DCRedirectionRespondActivityTaskCanceled", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRespondActivityTaskCanceledByIDScope: {operation: "DCRedirectionRespondActivityTaskCanceledByID", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRespondActivityTaskCompletedScope: {operation: "DCRedirectionRespondActivityTaskCompleted", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRespondActivityTaskCompletedByIDScope: {operation: "DCRedirectionRespondActivityTaskCompletedByID", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRespondActivityTaskFailedScope: {operation: "DCRedirectionRespondActivityTaskFailed", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRespondActivityTaskFailedByIDScope: {operation: "DCRedirectionRespondActivityTaskFailedByID", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRespondDecisionTaskCompletedScope: {operation: "DCRedirectionRespondDecisionTaskCompleted", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRespondDecisionTaskFailedScope: {operation: "DCRedirectionRespondDecisionTaskFailed", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRespondQueryTaskCompletedScope: {operation: "DCRedirectionRespondQueryTaskCompleted", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionSignalWithStartWorkflowExecutionScope: {operation: "DCRedirectionSignalWithStartWorkflowExecution", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionSignalWithStartWorkflowExecutionAsyncScope: {operation: "DCRedirectionSignalWithStartWorkflowExecutionAsync", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionSignalWorkflowExecutionScope: {operation: "DCRedirectionSignalWorkflowExecution", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionStartWorkflowExecutionScope: {operation: "DCRedirectionStartWorkflowExecution", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionStartWorkflowExecutionAsyncScope: {operation: "DCRedirectionStartWorkflowExecutionAsync", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionTerminateWorkflowExecutionScope: {operation: "DCRedirectionTerminateWorkflowExecution", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionUpdateDomainScope: {operation: "DCRedirectionUpdateDomain", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionListTaskListPartitionsScope: {operation: "DCRedirectionListTaskListPartitions", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionGetTaskListsByDomainScope: {operation: "DCRedirectionGetTaskListsByDomain", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRefreshWorkflowTasksScope: {operation: "DCRedirectionRefreshWorkflowTasks", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionRestartWorkflowExecutionScope: {operation: "DCRedirectionRestartWorkflowExecution", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, DCRedirectionForwardingPolicyScope: {operation: "DCRedirectionForwardingPolicy", tags: map[string]string{CadenceRoleTagName: DCRedirectionRoleTagValue}}, MessagingClientPublishScope: {operation: "MessagingClientPublish"}, MessagingClientPublishBatchScope: {operation: "MessagingClientPublishBatch"}, MessagingClientConsumerScope: {operation: "MessagingClientConsumerScope"}, DomainCacheScope: {operation: "DomainCache"}, HistoryRereplicationByTransferTaskScope: {operation: "HistoryRereplicationByTransferTask"}, HistoryRereplicationByTimerTaskScope: {operation: "HistoryRereplicationByTimerTask"}, HistoryRereplicationByHistoryReplicationScope: {operation: "HistoryRereplicationByHistoryReplication"}, HistoryRereplicationByHistoryMetadataReplicationScope: {operation: "HistoryRereplicationByHistoryMetadataReplication"}, HistoryRereplicationByActivityReplicationScope: {operation: "HistoryRereplicationByActivityReplication"}, ElasticsearchRecordWorkflowExecutionStartedScope: {operation: "RecordWorkflowExecutionStarted"}, ElasticsearchRecordWorkflowExecutionClosedScope: {operation: "RecordWorkflowExecutionClosed"}, ElasticsearchRecordWorkflowExecutionUninitializedScope: {operation: "RecordWorkflowExecutionUninitialized"}, ElasticsearchUpsertWorkflowExecutionScope: {operation: "UpsertWorkflowExecution"}, ElasticsearchListOpenWorkflowExecutionsScope: {operation: "ListOpenWorkflowExecutions"}, ElasticsearchListClosedWorkflowExecutionsScope: {operation: "ListClosedWorkflowExecutions"}, ElasticsearchListOpenWorkflowExecutionsByTypeScope: {operation: "ListOpenWorkflowExecutionsByType"}, ElasticsearchListClosedWorkflowExecutionsByTypeScope: {operation: "ListClosedWorkflowExecutionsByType"}, ElasticsearchListOpenWorkflowExecutionsByWorkflowIDScope: {operation: "ListOpenWorkflowExecutionsByWorkflowID"}, ElasticsearchListClosedWorkflowExecutionsByWorkflowIDScope: {operation: "ListClosedWorkflowExecutionsByWorkflowID"}, ElasticsearchListClosedWorkflowExecutionsByStatusScope: {operation: "ListClosedWorkflowExecutionsByStatus"}, ElasticsearchGetClosedWorkflowExecutionScope: {operation: "GetClosedWorkflowExecution"}, ElasticsearchListWorkflowExecutionsScope: {operation: "ListWorkflowExecutions"}, ElasticsearchScanWorkflowExecutionsScope: {operation: "ScanWorkflowExecutions"}, ElasticsearchCountWorkflowExecutionsScope: {operation: "CountWorkflowExecutions"}, ElasticsearchDeleteWorkflowExecutionsScope: {operation: "DeleteWorkflowExecution"}, ElasticsearchDeleteUninitializedWorkflowExecutionsScope: {operation: "DeleteUninitializedWorkflowExecution"}, PinotRecordWorkflowExecutionStartedScope: {operation: "RecordWorkflowExecutionStarted"}, PinotRecordWorkflowExecutionClosedScope: {operation: "RecordWorkflowExecutionClosed"}, PinotRecordWorkflowExecutionUninitializedScope: {operation: "RecordWorkflowExecutionUninitialized"}, PinotUpsertWorkflowExecutionScope: {operation: "UpsertWorkflowExecution"}, PinotListOpenWorkflowExecutionsScope: {operation: "ListOpenWorkflowExecutions"}, PinotListClosedWorkflowExecutionsScope: {operation: "ListClosedWorkflowExecutions"}, PinotListOpenWorkflowExecutionsByTypeScope: {operation: "ListOpenWorkflowExecutionsByType"}, PinotListClosedWorkflowExecutionsByTypeScope: {operation: "ListClosedWorkflowExecutionsByType"}, PinotListOpenWorkflowExecutionsByWorkflowIDScope: {operation: "ListOpenWorkflowExecutionsByWorkflowID"}, PinotListClosedWorkflowExecutionsByWorkflowIDScope: {operation: "ListClosedWorkflowExecutionsByWorkflowID"}, PinotListClosedWorkflowExecutionsByStatusScope: {operation: "ListClosedWorkflowExecutionsByStatus"}, PinotGetClosedWorkflowExecutionScope: {operation: "GetClosedWorkflowExecution"}, PinotListWorkflowExecutionsScope: {operation: "ListWorkflowExecutions"}, PinotScanWorkflowExecutionsScope: {operation: "ScanWorkflowExecutions"}, PinotCountWorkflowExecutionsScope: {operation: "CountWorkflowExecutions"}, PinotDeleteWorkflowExecutionsScope: {operation: "DeleteWorkflowExecution"}, PinotDeleteUninitializedWorkflowExecutionsScope: {operation: "DeleteUninitializedWorkflowExecution"}, SequentialTaskProcessingScope: {operation: "SequentialTaskProcessing"}, ParallelTaskProcessingScope: {operation: "ParallelTaskProcessing"}, TaskSchedulerScope: {operation: "TaskScheduler"}, TaskSchedulerRateLimiterScope: {operation: "TaskSchedulerRateLimiter"}, HistoryEngineScope: {operation: "HistoryEngine"}, HistoryArchiverScope: {operation: "HistoryArchiver"}, VisibilityArchiverScope: {operation: "VisibilityArchiver"}, BlobstoreClientUploadScope: {operation: "BlobstoreClientUpload", tags: map[string]string{CadenceRoleTagName: BlobstoreRoleTagValue}}, BlobstoreClientDownloadScope: {operation: "BlobstoreClientDownload", tags: map[string]string{CadenceRoleTagName: BlobstoreRoleTagValue}}, BlobstoreClientGetMetadataScope: {operation: "BlobstoreClientGetMetadata", tags: map[string]string{CadenceRoleTagName: BlobstoreRoleTagValue}}, BlobstoreClientExistsScope: {operation: "BlobstoreClientExists", tags: map[string]string{CadenceRoleTagName: BlobstoreRoleTagValue}}, BlobstoreClientDeleteScope: {operation: "BlobstoreClientDelete", tags: map[string]string{CadenceRoleTagName: BlobstoreRoleTagValue}}, BlobstoreClientDirectoryExistsScope: {operation: "BlobstoreClientDirectoryExists", tags: map[string]string{CadenceRoleTagName: BlobstoreRoleTagValue}}, GetAvailableIsolationGroupsScope: {operation: "GetAvailableIsolationGroups"}, DomainFailoverScope: {operation: "DomainFailover"}, TaskValidatorScope: {operation: "TaskValidation"}, DomainReplicationQueueScope: {operation: "DomainReplicationQueue"}, ClusterMetadataScope: {operation: "ClusterMetadata"}, HashringScope: {operation: "Hashring"}, // currently used by both frontend and history, but may grow to other limiting-host-services. GlobalRatelimiter: {operation: "GlobalRatelimiter"}, GlobalRatelimiterAggregator: {operation: "GlobalRatelimiterAggregator"}, P2PRPCPeerChooserScope: {operation: "P2PRPCPeerChooser"}, PartitionConfigProviderScope: {operation: "PartitionConfigProvider"}, ShardDistributorClientGetShardOwnerScope: {operation: "ShardDistributorClientGetShardOwner"}, ShardDistributorClientWatchNamespaceStateScope: {operation: "ShardDistributorClientWatchNamespaceState"}, ShardDistributorExecutorClientHeartbeatScope: {operation: "ShardDistributorExecutorHeartbeat"}, LoadBalancerScope: {operation: "RRLoadBalancer"}, ActiveClusterManager: {operation: "ActiveClusterManager"}, ActiveClusterManagerWorkflowCacheScope: {operation: "ActiveClusterManagerWorkflowCache"}, }, // Frontend Scope Names Frontend: { // Admin API scope co-locates with frontend AdminRemoveTaskScope: {operation: "AdminRemoveTask"}, AdminCloseShardScope: {operation: "AdminCloseShard"}, AdminResetQueueScope: {operation: "AdminResetQueue"}, AdminDescribeQueueScope: {operation: "AdminDescribeQueue"}, AdminCountDLQMessagesScope: {operation: "AdminCountDLQMessages"}, AdminReadDLQMessagesScope: {operation: "AdminReadDLQMessages"}, AdminPurgeDLQMessagesScope: {operation: "AdminPurgeDLQMessages"}, AdminMergeDLQMessagesScope: {operation: "AdminMergeDLQMessages"}, AdminDescribeHistoryHostScope: {operation: "DescribeHistoryHost"}, AdminDescribeShardDistributionScope: {operation: "AdminShardList"}, AdminDescribeClusterScope: {operation: "DescribeCluster"}, AdminAddSearchAttributeScope: {operation: "AddSearchAttribute"}, AdminDescribeWorkflowExecutionScope: {operation: "DescribeWorkflowExecution"}, AdminGetWorkflowExecutionRawHistoryScope: {operation: "GetWorkflowExecutionRawHistory"}, AdminGetWorkflowExecutionRawHistoryV2Scope: {operation: "GetWorkflowExecutionRawHistoryV2"}, AdminGetReplicationMessagesScope: {operation: "GetReplicationMessages"}, AdminGetDomainReplicationMessagesScope: {operation: "GetDomainReplicationMessages"}, AdminGetDLQReplicationMessagesScope: {operation: "AdminGetDLQReplicationMessages"}, AdminReapplyEventsScope: {operation: "ReapplyEvents"}, AdminRefreshWorkflowTasksScope: {operation: "RefreshWorkflowTasks"}, AdminResendReplicationTasksScope: {operation: "ResendReplicationTasks"}, AdminGetCrossClusterTasksScope: {operation: "AdminGetCrossClusterTasks"}, AdminRespondCrossClusterTasksCompletedScope: {operation: "AdminRespondCrossClusterTasksCompleted"}, AdminGetDynamicConfigScope: {operation: "AdminGetDynamicConfig"}, AdminUpdateDynamicConfigScope: {operation: "AdminUpdateDynamicConfig"}, AdminRestoreDynamicConfigScope: {operation: "AdminRestoreDynamicConfig"}, AdminListDynamicConfigScope: {operation: "AdminListDynamicConfig"}, AdminDeleteWorkflowScope: {operation: "AdminDeleteWorkflow"}, GetGlobalIsolationGroups: {operation: "GetGlobalIsolationGroups"}, UpdateGlobalIsolationGroups: {operation: "UpdateGlobalIsolationGroups"}, GetDomainIsolationGroups: {operation: "GetDomainIsolationGroups"}, UpdateDomainIsolationGroups: {operation: "UpdateDomainIsolationGroups"}, GetDomainAsyncWorkflowConfiguraton: {operation: "GetDomainAsyncWorkflowConfiguraton"}, UpdateDomainAsyncWorkflowConfiguraton: {operation: "UpdateDomainAsyncWorkflowConfiguraton"}, UpdateTaskListPartitionConfig: {operation: "UpdateTaskListPartitionConfig"}, FrontendRestartWorkflowExecutionScope: {operation: "RestartWorkflowExecution"}, FrontendStartWorkflowExecutionScope: {operation: "StartWorkflowExecution"}, FrontendStartWorkflowExecutionAsyncScope: {operation: "StartWorkflowExecutionAsync"}, FrontendPollForDecisionTaskScope: {operation: "PollForDecisionTask"}, FrontendPollForActivityTaskScope: {operation: "PollForActivityTask"}, FrontendRecordActivityTaskHeartbeatScope: {operation: "RecordActivityTaskHeartbeat"}, FrontendRecordActivityTaskHeartbeatByIDScope: {operation: "RecordActivityTaskHeartbeatByID"}, FrontendRespondDecisionTaskCompletedScope: {operation: "RespondDecisionTaskCompleted"}, FrontendRespondDecisionTaskFailedScope: {operation: "RespondDecisionTaskFailed"}, FrontendRespondQueryTaskCompletedScope: {operation: "RespondQueryTaskCompleted"}, FrontendRespondActivityTaskCompletedScope: {operation: "RespondActivityTaskCompleted"}, FrontendRespondActivityTaskFailedScope: {operation: "RespondActivityTaskFailed"}, FrontendRespondActivityTaskCanceledScope: {operation: "RespondActivityTaskCanceled"}, FrontendRespondActivityTaskCompletedByIDScope: {operation: "RespondActivityTaskCompletedByID"}, FrontendRespondActivityTaskFailedByIDScope: {operation: "RespondActivityTaskFailedByID"}, FrontendRespondActivityTaskCanceledByIDScope: {operation: "RespondActivityTaskCanceledByID"}, FrontendGetWorkflowExecutionHistoryScope: {operation: "GetWorkflowExecutionHistory"}, FrontendGetWorkflowExecutionRawHistoryScope: {operation: "GetWorkflowExecutionRawHistory"}, FrontendPollForWorklfowExecutionRawHistoryScope: {operation: "PollForWorklfowExecutionRawHistory"}, FrontendSignalWorkflowExecutionScope: {operation: "SignalWorkflowExecution"}, FrontendSignalWithStartWorkflowExecutionScope: {operation: "SignalWithStartWorkflowExecution"}, FrontendSignalWithStartWorkflowExecutionAsyncScope: {operation: "SignalWithStartWorkflowExecutionAsync"}, FrontendTerminateWorkflowExecutionScope: {operation: "TerminateWorkflowExecution"}, FrontendResetWorkflowExecutionScope: {operation: "ResetWorkflowExecution"}, FrontendRequestCancelWorkflowExecutionScope: {operation: "RequestCancelWorkflowExecution"}, FrontendListArchivedWorkflowExecutionsScope: {operation: "ListArchivedWorkflowExecutions"}, FrontendListOpenWorkflowExecutionsScope: {operation: "ListOpenWorkflowExecutions"}, FrontendListClosedWorkflowExecutionsScope: {operation: "ListClosedWorkflowExecutions"}, FrontendListWorkflowExecutionsScope: {operation: "ListWorkflowExecutions"}, FrontendScanWorkflowExecutionsScope: {operation: "ScanWorkflowExecutions"}, FrontendCountWorkflowExecutionsScope: {operation: "CountWorkflowExecutions"}, FrontendRegisterDomainScope: {operation: "RegisterDomain"}, FrontendDescribeDomainScope: {operation: "DescribeDomain"}, FrontendListDomainsScope: {operation: "ListDomain"}, FrontendUpdateDomainScope: {operation: "UpdateDomain"}, FrontendDeleteDomainScope: {operation: "DeleteDomain"}, FrontendDeprecateDomainScope: {operation: "DeprecateDomain"}, FrontendFailoverDomainScope: {operation: "FailoverDomain"}, FrontendListFailoverHistoryScope: {operation: "ListFailoverHistory"}, FrontendQueryWorkflowScope: {operation: "QueryWorkflow"}, FrontendDescribeWorkflowExecutionScope: {operation: "DescribeWorkflowExecution"}, FrontendDiagnoseWorkflowExecutionScope: {operation: "DiagnoseWorkflowExecution"}, FrontendDescribeWorkflowExecutionStatusScope: {operation: "DescribeWorkflowExecutionStatus"}, FrontendListTaskListPartitionsScope: {operation: "FrontendListTaskListPartitions"}, FrontendGetTaskListsByDomainScope: {operation: "FrontendGetTaskListsByDomain"}, FrontendRefreshWorkflowTasksScope: {operation: "FrontendRefreshWorkflowTasks"}, FrontendDescribeTaskListScope: {operation: "DescribeTaskList"}, FrontendResetStickyTaskListScope: {operation: "ResetStickyTaskList"}, FrontendGetSearchAttributesScope: {operation: "GetSearchAttributes"}, FrontendGetClusterInfoScope: {operation: "GetClusterInfo"}, }, // History Scope Names History: { HistoryStartWorkflowExecutionScope: {operation: "StartWorkflowExecution"}, HistoryRecordActivityTaskHeartbeatScope: {operation: "RecordActivityTaskHeartbeat"}, HistoryRespondDecisionTaskCompletedScope: {operation: "RespondDecisionTaskCompleted"}, HistoryRespondDecisionTaskFailedScope: {operation: "RespondDecisionTaskFailed"}, HistoryRespondActivityTaskCompletedScope: {operation: "RespondActivityTaskCompleted"}, HistoryRespondActivityTaskFailedScope: {operation: "RespondActivityTaskFailed"}, HistoryRespondActivityTaskCanceledScope: {operation: "RespondActivityTaskCanceled"}, HistoryResetQueueScope: {operation: "ResetQueue"}, HistoryDescribeQueueScope: {operation: "DescribeQueue"}, HistoryDescribeMutabelStateScope: {operation: "DescribeMutableState"}, HistoryGetMutableStateScope: {operation: "GetMutableState"}, HistoryPollMutableStateScope: {operation: "PollMutableState"}, HistoryResetStickyTaskListScope: {operation: "ResetStickyTaskListScope"}, HistoryDescribeWorkflowExecutionScope: {operation: "DescribeWorkflowExecution"}, HistoryRecordDecisionTaskStartedScope: {operation: "RecordDecisionTaskStarted"}, HistoryRecordActivityTaskStartedScope: {operation: "RecordActivityTaskStarted"}, HistorySignalWorkflowExecutionScope: {operation: "SignalWorkflowExecution"}, HistorySignalWithStartWorkflowExecutionScope: {operation: "SignalWithStartWorkflowExecution"}, HistoryRemoveSignalMutableStateScope: {operation: "RemoveSignalMutableState"}, HistoryTerminateWorkflowExecutionScope: {operation: "TerminateWorkflowExecution"}, HistoryResetWorkflowExecutionScope: {operation: "ResetWorkflowExecution"}, HistoryQueryWorkflowScope: {operation: "QueryWorkflow"}, HistoryProcessDeleteHistoryEventScope: {operation: "ProcessDeleteHistoryEvent"}, HistoryScheduleDecisionTaskScope: {operation: "ScheduleDecisionTask"}, HistoryRecordChildExecutionCompletedScope: {operation: "RecordChildExecutionCompleted"}, HistoryRequestCancelWorkflowExecutionScope: {operation: "RequestCancelWorkflowExecution"}, HistoryReplicateEventsScope: {operation: "ReplicateEvents"}, HistoryReplicateRawEventsScope: {operation: "ReplicateRawEvents"}, HistoryReplicateEventsV2Scope: {operation: "ReplicateEventsV2"}, HistorySyncShardStatusScope: {operation: "SyncShardStatus"}, HistorySyncActivityScope: {operation: "SyncActivity"}, HistoryDescribeMutableStateScope: {operation: "DescribeMutableState"}, HistoryGetReplicationMessagesScope: {operation: "GetReplicationMessages"}, HistoryGetDLQReplicationMessagesScope: {operation: "GetDLQReplicationMessages"}, HistoryCountDLQMessagesScope: {operation: "CountDLQMessages"}, HistoryReadDLQMessagesScope: {operation: "ReadDLQMessages"}, HistoryPurgeDLQMessagesScope: {operation: "PurgeDLQMessages"}, HistoryMergeDLQMessagesScope: {operation: "MergeDLQMessages"}, HistoryShardControllerScope: {operation: "ShardController"}, HistoryReapplyEventsScope: {operation: "EventReapplication"}, HistoryRefreshWorkflowTasksScope: {operation: "RefreshWorkflowTasks"}, HistoryNotifyFailoverMarkersScope: {operation: "NotifyFailoverMarkers"}, HistoryGetCrossClusterTasksScope: {operation: "GetCrossClusterTasks"}, HistoryRespondCrossClusterTasksCompletedScope: {operation: "RespondCrossClusterTasksCompleted"}, HistoryGetFailoverInfoScope: {operation: "GetFailoverInfo"}, HistoryRatelimitUpdateScope: {operation: "RatelimitUpdate"}, TaskPriorityAssignerScope: {operation: "TaskPriorityAssigner"}, TransferQueueProcessorScope: {operation: "TransferQueueProcessor"}, TransferQueueProcessorV2Scope: {operation: "TransferQueueProcessorV2"}, TransferActiveQueueProcessorScope: {operation: "TransferActiveQueueProcessor"}, TransferStandbyQueueProcessorScope: {operation: "TransferStandbyQueueProcessor"}, TransferActiveTaskActivityScope: {operation: "TransferActiveTaskActivity"}, TransferActiveTaskDecisionScope: {operation: "TransferActiveTaskDecision"}, TransferActiveTaskCloseExecutionScope: {operation: "TransferActiveTaskCloseExecution"}, TransferActiveTaskCancelExecutionScope: {operation: "TransferActiveTaskCancelExecution"}, TransferActiveTaskSignalExecutionScope: {operation: "TransferActiveTaskSignalExecution"}, TransferActiveTaskStartChildExecutionScope: {operation: "TransferActiveTaskStartChildExecution"}, TransferActiveTaskRecordWorkflowStartedScope: {operation: "TransferActiveTaskRecordWorkflowStarted"}, TransferActiveTaskResetWorkflowScope: {operation: "TransferActiveTaskResetWorkflow"}, TransferActiveTaskUpsertWorkflowSearchAttributesScope: {operation: "TransferActiveTaskUpsertWorkflowSearchAttributes"}, TransferActiveTaskRecordWorkflowClosedScope: {operation: "TransferActiveTaskRecordWorkflowClosed"}, TransferActiveTaskRecordChildExecutionCompletedScope: {operation: "TransferActiveTaskRecordChildExecutionCompleted"}, TransferActiveTaskApplyParentClosePolicyScope: {operation: "TransferActiveTaskApplyParentClosePolicy"}, TransferStandbyTaskActivityScope: {operation: "TransferStandbyTaskActivity"}, TransferStandbyTaskDecisionScope: {operation: "TransferStandbyTaskDecision"}, TransferStandbyTaskCloseExecutionScope: {operation: "TransferStandbyTaskCloseExecution"}, TransferStandbyTaskCancelExecutionScope: {operation: "TransferStandbyTaskCancelExecution"}, TransferStandbyTaskSignalExecutionScope: {operation: "TransferStandbyTaskSignalExecution"}, TransferStandbyTaskStartChildExecutionScope: {operation: "TransferStandbyTaskStartChildExecution"}, TransferStandbyTaskRecordWorkflowStartedScope: {operation: "TransferStandbyTaskRecordWorkflowStarted"}, TransferStandbyTaskResetWorkflowScope: {operation: "TransferStandbyTaskResetWorkflow"}, TransferStandbyTaskUpsertWorkflowSearchAttributesScope: {operation: "TransferStandbyTaskUpsertWorkflowSearchAttributes"}, TransferStandbyTaskRecordWorkflowClosedScope: {operation: "TransferStandbyTaskRecordWorkflowClosed"}, TransferStandbyTaskRecordChildExecutionCompletedScope: {operation: "TransferStandbyTaskRecordChildExecutionCompleted"}, TransferStandbyTaskApplyParentClosePolicyScope: {operation: "TransferStandbyTaskApplyParentClosePolicy"}, TimerQueueProcessorScope: {operation: "TimerQueueProcessor"}, TimerQueueProcessorV2Scope: {operation: "TimerQueueProcessorV2"}, TimerActiveQueueProcessorScope: {operation: "TimerActiveQueueProcessor"}, TimerStandbyQueueProcessorScope: {operation: "TimerStandbyQueueProcessor"}, TimerActiveTaskActivityTimeoutScope: {operation: "TimerActiveTaskActivityTimeout"}, TimerActiveTaskDecisionTimeoutScope: {operation: "TimerActiveTaskDecisionTimeout"}, TimerActiveTaskUserTimerScope: {operation: "TimerActiveTaskUserTimer"}, TimerActiveTaskWorkflowTimeoutScope: {operation: "TimerActiveTaskWorkflowTimeout"}, TimerActiveTaskActivityRetryTimerScope: {operation: "TimerActiveTaskActivityRetryTimer"}, TimerActiveTaskWorkflowBackoffTimerScope: {operation: "TimerActiveTaskWorkflowBackoffTimer"}, TimerActiveTaskDeleteHistoryEventScope: {operation: "TimerActiveTaskDeleteHistoryEvent"}, TimerStandbyTaskActivityTimeoutScope: {operation: "TimerStandbyTaskActivityTimeout"}, TimerStandbyTaskDecisionTimeoutScope: {operation: "TimerStandbyTaskDecisionTimeout"}, TimerStandbyTaskUserTimerScope: {operation: "TimerStandbyTaskUserTimer"}, TimerStandbyTaskWorkflowTimeoutScope: {operation: "TimerStandbyTaskWorkflowTimeout"}, TimerStandbyTaskActivityRetryTimerScope: {operation: "TimerStandbyTaskActivityRetryTimer"}, TimerStandbyTaskWorkflowBackoffTimerScope: {operation: "TimerStandbyTaskWorkflowBackoffTimer"}, TimerStandbyTaskDeleteHistoryEventScope: {operation: "TimerStandbyTaskDeleteHistoryEvent"}, CrossClusterQueueProcessorScope: {operation: "CrossClusterQueueProcessor"}, CrossClusterTaskProcessorScope: {operation: "CrossClusterTaskProcessor"}, CrossClusterTaskFetcherScope: {operation: "CrossClusterTaskFetcher"}, CrossClusterSourceTaskStartChildExecutionScope: {operation: "CrossClusterSourceTaskStartChildExecution"}, CrossClusterSourceTaskCancelExecutionScope: {operation: "CrossClusterSourceTaskCancelExecution"}, CrossClusterSourceTaskSignalExecutionScope: {operation: "CrossClusterSourceTaskSignalExecution"}, CrossClusterSourceTaskRecordChildWorkflowExecutionCompleteScope: {operation: "CrossClusterSourceTaskTypeRecordChildWorkflowExecutionComplete"}, CrossClusterSourceTaskApplyParentClosePolicyScope: {operation: "CrossClusterSourceTaskTypeApplyParentClosePolicy"}, CrossClusterTargetTaskStartChildExecutionScope: {operation: "CrossClusterTargetTaskStartChildExecution"}, CrossClusterTargetTaskCancelExecutionScope: {operation: "CrossClusterTargetTaskCancelExecution"}, CrossClusterTargetTaskSignalExecutionScope: {operation: "CrossClusterTargetTaskSignalExecution"}, CrossClusterTargetTaskRecordChildWorkflowExecutionCompleteScope: {operation: "CrossClusterTargetTaskTypeRecordChildWorkflowExecutionComplete"}, CrossClusterTargetTaskApplyParentClosePolicyScope: {operation: "CrossClusterTargetTaskTypeApplyParentClosePolicy"}, HistoryEventNotificationScope: {operation: "HistoryEventNotification"}, ReplicatorQueueProcessorScope: {operation: "ReplicatorQueueProcessor"}, ReplicatorCacheManagerScope: {operation: "ReplicatorCacheManager"}, ReplicatorTaskHistoryScope: {operation: "ReplicatorTaskHistory"}, ReplicatorTaskSyncActivityScope: {operation: "ReplicatorTaskSyncActivity"}, ReplicateHistoryEventsScope: {operation: "ReplicateHistoryEvents"}, ReplicationMetricEmitterScope: {operation: "ReplicationMetricEmitter"}, ShardInfoScope: {operation: "ShardInfo"}, WorkflowContextScope: {operation: "WorkflowContext"}, HistoryCacheGetAndCreateScope: {operation: "HistoryCacheGetAndCreate", tags: map[string]string{CacheTypeTagName: MutableStateCacheTypeTagValue}}, HistoryCacheGetOrCreateScope: {operation: "HistoryCacheGetOrCreate", tags: map[string]string{CacheTypeTagName: MutableStateCacheTypeTagValue}}, HistoryCacheGetOrCreateCurrentScope: {operation: "HistoryCacheGetOrCreateCurrent", tags: map[string]string{CacheTypeTagName: MutableStateCacheTypeTagValue}}, HistoryCacheGetCurrentExecutionScope: {operation: "HistoryCacheGetCurrentExecution", tags: map[string]string{CacheTypeTagName: MutableStateCacheTypeTagValue}}, EventsCacheGetEventScope: {operation: "EventsCacheGetEvent", tags: map[string]string{CacheTypeTagName: EventsCacheTypeTagValue}}, EventsCachePutEventScope: {operation: "EventsCachePutEvent", tags: map[string]string{CacheTypeTagName: EventsCacheTypeTagValue}}, EventsCacheGetFromStoreScope: {operation: "EventsCacheGetFromStore", tags: map[string]string{CacheTypeTagName: EventsCacheTypeTagValue}}, ExecutionSizeStatsScope: {operation: "ExecutionStats", tags: map[string]string{StatsTypeTagName: SizeStatsTypeTagValue}}, ExecutionCountStatsScope: {operation: "ExecutionStats", tags: map[string]string{StatsTypeTagName: CountStatsTypeTagValue}}, SessionSizeStatsScope: {operation: "SessionStats", tags: map[string]string{StatsTypeTagName: SizeStatsTypeTagValue}}, SessionCountStatsScope: {operation: "SessionStats", tags: map[string]string{StatsTypeTagName: CountStatsTypeTagValue}}, WorkflowCompletionStatsScope: {operation: "CompletionStats", tags: map[string]string{StatsTypeTagName: CountStatsTypeTagValue}}, ArchiverClientScope: {operation: "ArchiverClient"}, ReplicationTaskFetcherScope: {operation: "ReplicationTaskFetcher"}, ReplicationTaskCleanupScope: {operation: "ReplicationTaskCleanup"}, ReplicationDLQStatsScope: {operation: "ReplicationDLQStats"}, FailoverMarkerScope: {operation: "FailoverMarker"}, HistoryReplicationV2TaskScope: {operation: "HistoryReplicationV2Task"}, SyncActivityTaskScope: {operation: "SyncActivityTask"}, LargeExecutionSizeShardScope: {operation: "LargeExecutionSizeShard"}, LargeExecutionCountShardScope: {operation: "LargeExecutionCountShard"}, LargeExecutionBlobShardScope: {operation: "LargeExecutionBlobShard"}, HistoryExecutionCacheScope: {operation: "HistoryExecutionCache"}, HistoryWorkflowCacheScope: {operation: "HistoryWorkflowCache"}, HistoryFlushBufferedEventsScope: {operation: "HistoryFlushBufferedEvents"}, HistoryTaskSchedulerMigrationScope: {operation: "HistoryTaskSchedulerMigration"}, }, // Matching Scope Names Matching: { MatchingPollForDecisionTaskScope: {operation: "PollForDecisionTask"}, MatchingPollForActivityTaskScope: {operation: "PollForActivityTask"}, MatchingAddActivityTaskScope: {operation: "AddActivityTask"}, MatchingAddDecisionTaskScope: {operation: "AddDecisionTask"}, MatchingAddTaskScope: {operation: "AddTask"}, MatchingTaskListMgrScope: {operation: "TaskListMgr"}, MatchingAdaptiveScalerScope: {operation: "adaptivescaler"}, MatchingQueryWorkflowScope: {operation: "QueryWorkflow"}, MatchingRespondQueryTaskCompletedScope: {operation: "RespondQueryTaskCompleted"}, MatchingCancelOutstandingPollScope: {operation: "CancelOutstandingPoll"}, MatchingDescribeTaskListScope: {operation: "DescribeTaskList"}, MatchingListTaskListPartitionsScope: {operation: "ListTaskListPartitions"}, MatchingGetTaskListsByDomainScope: {operation: "GetTaskListsByDomain"}, MatchingUpdateTaskListPartitionConfigScope: {operation: "UpdateTaskListPartitionConfig"}, MatchingRefreshTaskListPartitionConfigScope: {operation: "RefreshTaskListPartitionConfig"}, }, // Worker Scope Names Worker: { ReplicatorScope: {operation: "Replicator"}, DomainReplicationTaskScope: {operation: "DomainReplicationTask"}, ESProcessorScope: {operation: "ESProcessor"}, IndexProcessorScope: {operation: "IndexProcessor"}, ArchiverDeleteHistoryActivityScope: {operation: "ArchiverDeleteHistoryActivity"}, ArchiverUploadHistoryActivityScope: {operation: "ArchiverUploadHistoryActivity"}, ArchiverArchiveVisibilityActivityScope: {operation: "ArchiverArchiveVisibilityActivity"}, ArchiverScope: {operation: "Archiver"}, ArchiverPumpScope: {operation: "ArchiverPump"}, ArchiverArchivalWorkflowScope: {operation: "ArchiverArchivalWorkflow"}, TaskListScavengerScope: {operation: "tasklistscavenger"}, ExecutionsScannerScope: {operation: "ExecutionsScanner"}, ShardScannerScope: {operation: "ShardScanner"}, CheckDataCorruptionWorkflowScope: {operation: "CheckDataCorruptionWorkflow"}, ExecutionsFixerScope: {operation: "ExecutionsFixer"}, HistoryScavengerScope: {operation: "historyscavenger"}, BatcherScope: {operation: "batcher"}, ParentClosePolicyProcessorScope: {operation: "ParentClosePolicyProcessor"}, ESAnalyzerScope: {operation: "ESAnalyzer"}, AsyncWorkflowConsumerScope: {operation: "AsyncWorkflowConsumer"}, DiagnosticsWorkflowScope: {operation: "DiagnosticsWorkflow"}, }, ShardDistributor: { ShardDistributorGetShardOwnerScope: {operation: "GetShardOwner"}, ShardDistributorWatchNamespaceStateScope: {operation: "WatchNamespaceState"}, ShardDistributorHeartbeatScope: {operation: "ExecutorHeartbeat"}, ShardDistributorAssignLoopScope: {operation: "ShardAssignLoop"}, ShardDistributorExecutorScope: {operation: "Executor"}, ShardDistributorStoreGetShardOwnerScope: {operation: "StoreGetShardOwner"}, ShardDistributorStoreAssignShardScope: {operation: "StoreAssignShard"}, ShardDistributorStoreAssignShardsScope: {operation: "StoreAssignShards"}, ShardDistributorStoreDeleteExecutorsScope: {operation: "StoreDeleteExecutors"}, ShardDistributorStoreGetShardStatsScope: {operation: "StoreGetShardStats"}, ShardDistributorStoreDeleteShardStatsScope: {operation: "StoreDeleteShardStats"}, ShardDistributorStoreGetHeartbeatScope: {operation: "StoreGetHeartbeat"}, ShardDistributorStoreGetExecutorScope: {operation: "StoreGetExecutor"}, ShardDistributorStoreGetStateScope: {operation: "StoreGetState"}, ShardDistributorStoreRecordHeartbeatScope: {operation: "StoreRecordHeartbeat"}, ShardDistributorStoreSubscribeToExecutorStatusChangesScope: {operation: "StoreSubscribeToExecutorStatusChanges"}, ShardDistributorStoreSubscribeToAssignmentChangesScope: {operation: "StoreSubscribeToAssignmentChanges"}, ShardDistributorStoreDeleteAssignedStatesScope: {operation: "StoreDeleteAssignedStates"}, ShardDistributorWatchScope: {operation: "Watch"}, }, } // Common Metrics enum const ( CadenceRequests MetricIdx = iota CadenceFailures CadenceLatency CadenceErrBadRequestCounter CadenceErrDomainNotActiveCounter CadenceErrServiceBusyCounter CadenceErrEntityNotExistsCounter CadenceErrWorkflowExecutionAlreadyCompletedCounter CadenceErrExecutionAlreadyStartedCounter CadenceErrDomainAlreadyExistsCounter CadenceErrCancellationAlreadyRequestedCounter CadenceErrQueryFailedCounter CadenceErrLimitExceededCounter CadenceErrContextTimeoutCounter CadenceErrGRPCConnectionClosingCounter CadenceErrRetryTaskCounter CadenceErrBadBinaryCounter CadenceErrClientVersionNotSupportedCounter CadenceErrIncompleteHistoryCounter CadenceErrNonDeterministicCounter CadenceErrUnauthorizedCounter CadenceErrAuthorizeFailedCounter CadenceRequestsWithoutCallerType CadenceErrRemoteSyncMatchFailedCounter CadenceErrDomainNameExceededWarnLimit CadenceErrIdentityExceededWarnLimit CadenceErrWorkflowIDExceededWarnLimit CadenceErrSignalNameExceededWarnLimit CadenceErrWorkflowTypeExceededWarnLimit CadenceErrRequestIDExceededWarnLimit CadenceErrTaskListNameExceededWarnLimit CadenceErrActivityIDExceededWarnLimit CadenceErrActivityTypeExceededWarnLimit CadenceErrMarkerNameExceededWarnLimit CadenceErrTimerIDExceededWarnLimit PersistenceRequests PersistenceFailures PersistenceLatency PersistenceLatencyHistogram PersistenceErrShardExistsCounter PersistenceErrShardOwnershipLostCounter PersistenceErrConditionFailedCounter PersistenceErrCurrentWorkflowConditionFailedCounter PersistenceErrTimeoutCounter PersistenceErrBusyCounter PersistenceErrEntityNotExistsCounter PersistenceErrExecutionAlreadyStartedCounter PersistenceErrDomainAlreadyExistsCounter PersistenceErrBadRequestCounter PersistenceErrDuplicateRequestCounter PersistenceErrDBUnavailableCounter PersistenceSampledCounter PersistenceEmptyResponseCounter PersistenceResponseRowSize PersistenceResponsePayloadSize PersistenceRequestsPerDomain PersistenceRequestsPerShard PersistenceFailuresPerDomain PersistenceLatencyPerDomain PersistenceLatencyPerShard PersistenceErrShardExistsCounterPerDomain PersistenceErrShardOwnershipLostCounterPerDomain PersistenceErrConditionFailedCounterPerDomain PersistenceErrCurrentWorkflowConditionFailedCounterPerDomain PersistenceErrTimeoutCounterPerDomain PersistenceErrBusyCounterPerDomain PersistenceErrEntityNotExistsCounterPerDomain PersistenceErrExecutionAlreadyStartedCounterPerDomain PersistenceErrDomainAlreadyExistsCounterPerDomain PersistenceErrBadRequestCounterPerDomain PersistenceErrDuplicateRequestCounterPerDomain PersistenceErrDBUnavailableCounterPerDomain PersistenceSampledCounterPerDomain PersistenceEmptyResponseCounterPerDomain NoSQLShardStoreReadFromOriginalColumnCounter NoSQLShardStoreReadFromDataBlobCounter CadenceClientRequests CadenceClientFailures CadenceClientLatency CadenceTasklistRequests CadenceDcRedirectionClientRequests CadenceDcRedirectionClientFailures CadenceDcRedirectionClientLatency CadenceAuthorizationLatency DomainCachePrepareCallbacksLatency DomainCacheCallbacksLatency DomainCacheCallbacksCount HistorySize HistoryCount EventBlobSize EventBlobSizeExceedLimit DecisionResultCount ArchivalConfigFailures ActiveClusterGauge ElasticsearchRequests ElasticsearchFailures ElasticsearchLatency ElasticsearchErrBadRequestCounter ElasticsearchErrBusyCounter ElasticsearchRequestsPerDomain ElasticsearchFailuresPerDomain ElasticsearchLatencyPerDomain ElasticsearchErrBadRequestCounterPerDomain ElasticsearchErrBusyCounterPerDomain PinotRequests PinotFailures PinotLatency PinotErrBadRequestCounter PinotErrBusyCounter PinotRequestsPerDomain PinotFailuresPerDomain PinotLatencyPerDomain PinotErrBadRequestCounterPerDomain PinotErrBusyCounterPerDomain SequentialTaskSubmitRequest SequentialTaskSubmitRequestTaskQueueExist SequentialTaskSubmitRequestTaskQueueMissing SequentialTaskSubmitLatency SequentialTaskQueueSize SequentialTaskQueueProcessingLatency SequentialTaskTaskProcessingLatency ParallelTaskSubmitRequest ParallelTaskSubmitLatency ParallelTaskTaskProcessingLatency PriorityTaskSubmitRequest PriorityTaskSubmitLatency KafkaConsumerMessageIn KafkaConsumerMessageAck KafkaConsumerMessageNack KafkaConsumerMessageNackDlqErr KafkaConsumerSessionStart DescribeWorkflowStatusCount DescribeWorkflowStatusError GracefulFailoverLatency GracefulFailoverFailure HistoryArchiverArchiveNonRetryableErrorCount HistoryArchiverArchiveTransientErrorCount HistoryArchiverArchiveSuccessCount HistoryArchiverHistoryMutatedCount HistoryArchiverTotalUploadSize HistoryArchiverHistorySize HistoryArchiverDuplicateArchivalsCount // The following metrics are only used by internal history archiver implemention. // TODO: move them to internal repo once cadence plugin model is in place. HistoryArchiverBlobExistsCount HistoryArchiverBlobSize HistoryArchiverRunningDeterministicConstructionCheckCount HistoryArchiverDeterministicConstructionCheckFailedCount HistoryArchiverRunningBlobIntegrityCheckCount HistoryArchiverBlobIntegrityCheckFailedCount VisibilityArchiverArchiveNonRetryableErrorCount VisibilityArchiverArchiveTransientErrorCount VisibilityArchiveSuccessCount MatchingClientForwardedCounter MatchingClientInvalidTaskListName // common metrics that are emitted per task list CadenceRequestsPerTaskList CadenceRequestsPerTaskListWithoutRollup CadenceFailuresPerTaskList CadenceLatencyPerTaskList CadenceErrBadRequestPerTaskListCounter CadenceErrDomainNotActivePerTaskListCounter CadenceErrServiceBusyPerTaskListCounter CadenceErrEntityNotExistsPerTaskListCounter CadenceErrExecutionAlreadyStartedPerTaskListCounter CadenceErrDomainAlreadyExistsPerTaskListCounter CadenceErrCancellationAlreadyRequestedPerTaskListCounter CadenceErrQueryFailedPerTaskListCounter CadenceErrLimitExceededPerTaskListCounter CadenceErrContextTimeoutPerTaskListCounter CadenceErrRetryTaskPerTaskListCounter CadenceErrBadBinaryPerTaskListCounter CadenceErrClientVersionNotSupportedPerTaskListCounter CadenceErrIncompleteHistoryPerTaskListCounter CadenceErrNonDeterministicPerTaskListCounter CadenceErrUnauthorizedPerTaskListCounter CadenceErrAuthorizeFailedPerTaskListCounter CadenceErrRemoteSyncMatchFailedPerTaskListCounter CadenceErrStickyWorkerUnavailablePerTaskListCounter CadenceErrReadOnlyPartitionPerTaskListCounter CadenceErrTaskListNotOwnedByHostPerTaskListCounter CadenceShardSuccessGauge CadenceShardFailureGauge DomainReplicationQueueSizeGauge DomainReplicationQueueSizeErrorCount DomainCacheUpdateLatency ParentClosePolicyProcessorSuccess ParentClosePolicyProcessorFailures ValidatedWorkflowCount HashringViewIdentifier AsyncRequestPayloadSize // limiter-side metrics GlobalRatelimiterStartupUsageHistogram GlobalRatelimiterFailingUsageHistogram GlobalRatelimiterGlobalUsageHistogram GlobalRatelimiterUpdateLatency // time spent performing all Update requests, per batch attempt. ideally well below update interval. GlobalRatelimiterAllowedRequestsCount // per key/type usage GlobalRatelimiterRejectedRequestsCount // per key/type usage GlobalRatelimiterQuota // per-global-key quota information, emitted when a key is in us // aggregator-side metrics GlobalRatelimiterInitialized GlobalRatelimiterReinitialized GlobalRatelimiterUpdated GlobalRatelimiterDecayed GlobalRatelimiterLimitsQueried GlobalRatelimiterHostLimitsQueried GlobalRatelimiterRemovedLimits GlobalRatelimiterRemovedHostLimits // p2p rpc metrics P2PPeersCount P2PPeerAdded P2PPeerRemoved // task list partition config metrics TaskListPartitionConfigVersionGauge TaskListPartitionConfigNumReadGauge TaskListPartitionConfigNumWriteGauge // base cache metrics BaseCacheByteSize BaseCacheByteSizeLimitGauge BaseCacheHit BaseCacheMiss BaseCacheCount BaseCacheCountLimitGauge BaseCacheFullCounter BaseCacheEvictCounter // active cluster manager metrics ActiveClusterManagerLookupRequestCount ActiveClusterManagerLookupSuccessCount ActiveClusterManagerLookupFailureCount ActiveClusterManagerLookupLatency // cluster forwarding policy metrics ClusterForwardingPolicyRequests RingResolverError // WorkflowExecutionHistoryAccess tracks the access to the workflow history WorkflowExecutionHistoryAccess // Budget manager metrics BudgetManagerCapacityBytes BudgetManagerCapacityCount BudgetManagerUsedBytes BudgetManagerUsedCount BudgetManagerSoftThreshold BudgetManagerActiveCacheCount BudgetManagerHardCapExceeded BudgetManagerSoftCapExceeded WeightedChannelPoolSizeGauge NumCommonMetrics // Needs to be last on this list for iota numbering ) // History Metrics enum const ( TaskRequests = iota + NumCommonMetrics TaskLatency ExponentialTaskLatency TaskFailures TaskDiscarded TaskAttemptTimer ExponentialTaskAttemptCounts TaskStandbyRetryCounter TaskNotActiveCounter TaskLimitExceededCounter TaskBatchCompleteCounter TaskBatchCompleteFailure TaskProcessingLatency ExponentialTaskProcessingLatency TaskQueueLatency ExponentialTaskQueueLatency ScheduleToStartHistoryQueueLatencyPerTaskList TaskRequestsOldScheduler TaskRequestsNewScheduler PendingTaskGauge ReschedulerTaskCountGauge NewHistoryTaskCounter TaskRequestsPerDomain TaskLatencyPerDomain ExponentialTaskLatencyPerDomain TaskFailuresPerDomain TaskWorkflowBusyPerDomain TaskDiscardedPerDomain TaskUnsupportedPerDomain TaskAttemptTimerPerDomain ExponentialTaskAttemptCountsPerDomain TaskStandbyRetryCounterPerDomain TaskListNotOwnedByHostCounterPerDomain TaskPendingActiveCounterPerDomain TaskNotActiveCounterPerDomain TaskTargetNotActiveCounterPerDomain TaskLimitExceededCounterPerDomain TaskProcessingLatencyPerDomain ExponentialTaskProcessingLatencyPerDomain TaskQueueLatencyPerDomain ExponentialTaskQueueLatencyPerDomain TaskScheduleLatencyPerDomain TaskEnqueueToFetchLatency TransferTaskMissingEventCounterPerDomain ReplicationTasksAppliedPerDomain WorkflowTerminateCounterPerDomain TaskSchedulerAllowedCounterPerDomain TaskSchedulerThrottledCounterPerDomain TaskRedispatchQueuePendingTasksTimer TransferTaskThrottledCounter TimerTaskThrottledCounter CrossClusterTaskThrottledCounter TransferTaskMissingEventCounter ProcessingQueueNumTimer ProcessingQueueMaxLevelTimer ProcessingQueuePendingTaskSplitCounter ProcessingQueueStuckTaskSplitCounter ProcessingQueueSelectedDomainSplitCounter ProcessingQueueRandomSplitCounter ProcessingQueueThrottledCounter CorruptedHistoryTaskCounter QueueValidatorLostTaskCounter QueueValidatorDropTaskCounter QueueValidatorInvalidLoadCounter QueueValidatorValidationCounter QueueValidatorValidationFailure CrossClusterFetchLatency CrossClusterFetchRequests CrossClusterFetchFailures CrossClusterFetchServiceBusyFailures CrossClusterTaskRespondLatency CrossClusterTaskRespondRequests CrossClusterTaskRespondFailures CrossClusterTaskFetchedTimer CrossClusterTaskPendingTimer ClusterMetadataFailureToResolveCounter ClusterMetadataGettingMinFailoverVersionCounter ClusterMetadataGettingFailoverVersionCounter ClusterMetadataResolvingFailoverVersionCounter ClusterMetadataResolvingMinFailoverVersionCounter ActivityE2ELatency ActivityLostCounter AckLevelUpdateCounter AckLevelUpdateFailedCounter DecisionTypeScheduleActivityCounter DecisionTypeScheduleActivityDispatchSucceedCounter DecisionTypeScheduleActivityDispatchCounter DecisionTypeCompleteWorkflowCounter DecisionTypeFailWorkflowCounter DecisionTypeCancelWorkflowCounter DecisionTypeStartTimerCounter DecisionTypeCancelActivityCounter DecisionTypeCancelTimerCounter DecisionTypeRecordMarkerCounter DecisionTypeCancelExternalWorkflowCounter DecisionTypeChildWorkflowCounter DecisionTypeContinueAsNewCounter DecisionTypeSignalExternalWorkflowCounter DecisionTypeUpsertWorkflowSearchAttributesCounter EmptyCompletionDecisionsCounter MultipleCompletionDecisionsCounter FailedDecisionsCounter DecisionAttemptTimer DecisionRetriesExceededCounter StaleMutableStateCounter DataInconsistentCounter DuplicateActivityTaskEventCounter TimerResurrectionCounter TimerProcessingDeletionTimerNoopDueToMutableStateNotLoading TimerProcessingDeletionTimerNoopDueToWFRunning ActivityResurrectionCounter AutoResetPointsLimitExceededCounter AutoResetPointCorruptionCounter ConcurrencyUpdateFailureCounter CadenceErrEventAlreadyStartedCounter CadenceErrShardOwnershipLostCounter HeartbeatTimeoutCounter ScheduleToStartTimeoutCounter StartToCloseTimeoutCounter ScheduleToCloseTimeoutCounter NewTimerCounter NewTimerNotifyCounter AcquireShardsCounter AcquireShardsLatency ShardClosedCounter ShardItemCreatedCounter ShardItemRemovedCounter ShardItemAcquisitionLatency ShardInfoReplicationPendingTasksTimer ShardInfoTransferActivePendingTasksTimer ShardInfoTransferStandbyPendingTasksTimer ShardInfoTimerActivePendingTasksTimer ShardInfoTimerStandbyPendingTasksTimer ShardInfoCrossClusterPendingTasksTimer ShardInfoReplicationLagTimer ShardInfoTransferLagTimer ShardInfoTimerLagTimer ShardInfoCrossClusterLagTimer ShardInfoTransferDiffTimer ShardInfoTimerDiffTimer ShardInfoTransferFailoverInProgressTimer ShardInfoTimerFailoverInProgressTimer ShardInfoTransferFailoverLatencyTimer ShardInfoTimerFailoverLatencyTimer SyncShardFromRemoteCounter SyncShardFromRemoteFailure MembershipChangedCounter NumShardsGauge GetEngineForShardErrorCounter GetEngineForShardLatency RemoveEngineForShardLatency CompleteDecisionWithStickyEnabledCounter CompleteDecisionWithStickyDisabledCounter DecisionHeartbeatTimeoutCounter HistoryEventNotificationQueueingLatency HistoryEventNotificationFanoutLatency HistoryEventNotificationInFlightMessageGauge HistoryEventNotificationFailDeliveryCount EmptyReplicationEventsCounter DuplicateReplicationEventsCounter StaleReplicationEventsCounter ReplicationEventsSizeTimer BufferReplicationTaskTimer UnbufferReplicationTaskTimer HistoryConflictsCounter CompleteTaskFailedCounter CacheSize CacheSizeHistogram CacheRequests CacheFailures CacheLatency ExponentialCacheLatency CacheHitCounter CacheMissCounter CacheFullCounter AcquireLockFailedCounter WorkflowContextCleared WorkflowContextLockLatency MutableStateSize ExecutionInfoSize ActivityInfoSize TimerInfoSize ChildInfoSize SignalInfoSize BufferedEventsSize ActivityInfoCount TimerInfoCount ChildInfoCount SignalInfoCount RequestCancelInfoCount BufferedEventsCount TransferTasksCount TimerTasksCount CrossClusterTasksCount ReplicationTasksCount DeleteActivityInfoCount DeleteTimerInfoCount DeleteChildInfoCount DeleteSignalInfoCount DeleteRequestCancelInfoCount WorkflowRetryBackoffTimerCount WorkflowCronBackoffTimerCount WorkflowCleanupDeleteCount WorkflowCleanupArchiveCount WorkflowCleanupNopCount WorkflowCleanupDeleteHistoryInlineCount WorkflowSuccessCount WorkflowCancelCount WorkflowFailedCount WorkflowTimeoutCount WorkflowTerminateCount WorkflowContinuedAsNew WorkflowCompletedUnknownType // WorkflowCreationFailedCleanupHaltedTimeoutCount is where the attempt to cleanup after wf start failure was halted due to a timeout making it uncertain if it's safe WorkflowCreationFailedCleanupHaltedTimeoutCount // WorkflowCreationFailedCleanupUnknownCount is where the attempt to cleanup after wf start failure was halted due to not having enough certainty WorkflowCreationFailedCleanupUnknownCount // WorkflowCreationFailedCleanupSuccessCount is where the attempt to cleanup after wf start failure was successful WorkflowCreationFailedCleanupSuccessCount // WorkflowCreationFailedCleanupFailureCount is where the attempt to cleanup after wf start failure also resulted in failure WorkflowCreationFailedCleanupFailureCount ArchiverClientSendSignalCount ArchiverClientSendSignalFailureCount ArchiverClientHistoryRequestCount ArchiverClientHistoryInlineArchiveAttemptCount ArchiverClientHistoryInlineArchiveFailureCount ArchiverClientHistoryInlineArchiveThrottledCount ArchiverClientVisibilityRequestCount ArchiverClientVisibilityInlineArchiveAttemptCount ArchiverClientVisibilityInlineArchiveFailureCount ArchiverClientVisibilityInlineArchiveThrottledCount ArchiverClientSendSignalCountPerDomain ArchiverClientSendSignalFailureCountPerDomain ArchiverClientHistoryRequestCountPerDomain ArchiverClientHistoryInlineArchiveAttemptCountPerDomain ArchiverClientHistoryInlineArchiveFailureCountPerDomain ArchiverClientHistoryInlineArchiveThrottledCountPerDomain ArchiverClientVisibilityRequestCountPerDomain ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain ArchiverClientVisibilityInlineArchiveFailureCountPerDomain ArchiverClientVisibilityInlineArchiveThrottledCountPerDomain LastRetrievedMessageID LastProcessedMessageID ReplicationLatency ReplicationTasksApplied ReplicationTasksFailed ReplicationTasksLag ReplicationTasksLagHistogram ReplicationTasksLagRaw ReplicationTasksLagRawHistogram ReplicationTasksDelay ReplicationTasksFetched ReplicationTasksFetchedHistogram ReplicationTasksReturned ReplicationTasksReturnedHistogram ReplicationTasksReturnedDiff ReplicationTasksReturnedDiffHistogram ReplicationTasksAppliedLatency ReplicationTasksAppliedLatencyHistogram ReplicationTasksBatchSize ReplicationDynamicTaskBatchSizerDecision ReplicationDLQFailed ReplicationDLQMaxLevelGauge ReplicationDLQAckLevelGauge ReplicationDLQProbeFailed ReplicationDLQSize ReplicationDLQValidationFailed ReplicationMessageTooLargePerShard GetReplicationMessagesForShardLatency GetDLQReplicationMessagesLatency EventReapplySkippedCount DirectQueryDispatchLatency DirectQueryDispatchStickyLatency DirectQueryDispatchNonStickyLatency DirectQueryDispatchStickySuccessCount DirectQueryDispatchNonStickySuccessCount DirectQueryDispatchClearStickinessLatency DirectQueryDispatchClearStickinessSuccessCount DirectQueryDispatchTimeoutBeforeNonStickyCount DecisionTaskQueryLatency ConsistentQueryPerShard ConsistentQueryTimeoutCount QueryBeforeFirstDecisionCount QueryBufferExceededCount QueryRegistryInvalidStateCount WorkerNotSupportsConsistentQueryCount DecisionStartToCloseTimeoutOverrideCount ReplicationTaskCleanupCount ReplicationTaskCleanupFailure ReplicationTaskLatency ExponentialReplicationTaskLatency ExponentialReplicationTaskFetchLatency ReplicationTasksFetchedSize MutableStateChecksumMismatch MutableStateChecksumInvalidated FailoverMarkerCount FailoverMarkerReplicationLatency FailoverMarkerInsertFailure FailoverMarkerNotificationFailure FailoverMarkerUpdateShardFailure FailoverMarkerCallbackCount HistoryFailoverCallbackCount WorkflowVersionCount WorkflowTypeCount WorkflowStartedCount LargeHistoryBlobCount LargeHistoryEventCount LargeHistorySizeCount UpdateWorkflowExecutionCount WorkflowIDCacheSizeGauge WorkflowIDCacheRequestsExternalRatelimitedCounter WorkflowIDCacheRequestsExternalMaxRequestsPerSecondsTimer WorkflowIDCacheRequestsInternalMaxRequestsPerSecondsTimer WorkflowIDCacheRequestsInternalRatelimitedCounter VirtualQueueCountGauge VirtualQueuePausedGauge VirtualQueueRunningGauge TaskRequestsPerTaskList ExponentialTaskLatencyPerTaskList ExponentialTaskProcessingLatencyPerTaskList ExponentialTaskQueueLatencyPerTaskList ExponentialTaskScheduleLatencyPerTaskList NumHistoryMetrics ) // Matching metrics enum const ( PollSuccessPerTaskListCounter = iota + NumHistoryMetrics PollTimeoutPerTaskListCounter PollSuccessWithSyncPerTaskListCounter LeaseRequestPerTaskListCounter LeaseFailurePerTaskListCounter ConditionFailedErrorPerTaskListCounter RespondQueryTaskFailedPerTaskListCounter SyncThrottlePerTaskListCounter BufferThrottlePerTaskListCounter BufferUnknownTaskDispatchError BufferIsolationGroupRedirectCounter BufferIsolationGroupRedirectFailureCounter BufferIsolationGroupMisconfiguredCounter SyncMatchLatencyPerTaskList AsyncMatchLatencyPerTaskList AsyncMatchDispatchLatencyPerTaskList AsyncMatchDispatchTimeoutCounterPerTaskList ExpiredTasksPerTaskListCounter ForwardedPerTaskListCounter ForwardTaskCallsPerTaskList ForwardTaskErrorsPerTaskList SyncMatchForwardTaskThrottleErrorPerTasklist AsyncMatchForwardTaskThrottleErrorPerTasklist ForwardTaskLatencyPerTaskList ForwardQueryCallsPerTaskList ForwardQueryErrorsPerTaskList ForwardQueryLatencyPerTaskList ForwardPollCallsPerTaskList ForwardPollErrorsPerTaskList ForwardPollLatencyPerTaskList LocalToLocalMatchPerTaskListCounter LocalToRemoteMatchPerTaskListCounter RemoteToLocalMatchPerTaskListCounter RemoteToRemoteMatchPerTaskListCounter IsolationTaskMatchPerTaskListCounter IsolationSuccessPerTaskListCounter PollerPerTaskListCounter PollerInvalidIsolationGroupCounter TaskListPartitionUpdateFailedCounter TaskListManagersGauge TaskLagPerTaskListGauge TaskBacklogPerTaskListGauge TaskCountPerTaskListGauge RateLimitPerTaskListGauge SyncMatchLocalPollLatencyPerTaskList SyncMatchForwardPollLatencyPerTaskList AsyncMatchLocalPollCounterPerTaskList AsyncMatchLocalPollAttemptPerTaskList AsyncMatchLocalPollLatencyPerTaskList AsyncMatchForwardPollCounterPerTaskList AsyncMatchForwardPollAttemptPerTaskList AsyncMatchForwardPollLatencyPerTaskList AsyncMatchLocalPollAfterForwardFailedCounterPerTaskList AsyncMatchLocalPollAfterForwardFailedAttemptPerTaskList AsyncMatchLocalPollAfterForwardFailedLatencyPerTaskList PollLocalMatchLatencyPerTaskList PollForwardMatchLatencyPerTaskList PollLocalMatchAfterForwardFailedLatencyPerTaskList PollDecisionTaskAlreadyStartedCounterPerTaskList PollActivityTaskAlreadyStartedCounterPerTaskList TaskListReadWritePartitionMismatchGauge TaskListPollerPartitionMismatchGauge EstimatedAddTaskQPSGauge TaskListPartitionUpscaleThresholdGauge TaskListPartitionDownscaleThresholdGauge StandbyClusterTasksCompletedCounterPerTaskList StandbyClusterTasksNotStartedCounterPerTaskList StandbyClusterTasksCompletionFailurePerTaskList TaskIsolationLeakPerTaskList PartitionUpscale PartitionDownscale IsolationRebalance IsolationGroupPartitionsGauge IsolationGroupStartedPolling IsolationGroupStoppedPolling IsolationGroupUpscale IsolationGroupDownscale PartitionDrained NumMatchingMetrics ) // Worker metrics enum const ( ReplicatorMessages = iota + NumMatchingMetrics ReplicatorFailures ReplicatorMessagesDropped ReplicatorLatency ReplicatorDLQFailures ESProcessorRequests ESProcessorRetries ESProcessorFailures ESProcessorCorruptedData ESProcessorProcessMsgLatency IndexProcessorCorruptedData IndexProcessorProcessMsgLatency ArchiverNonRetryableErrorCount ArchiverStartedCount ArchiverStoppedCount ArchiverCoroutineStartedCount ArchiverCoroutineStoppedCount ArchiverHandleHistoryRequestLatency ArchiverHandleVisibilityRequestLatency ArchiverUploadWithRetriesLatency ArchiverDeleteWithRetriesLatency ArchiverUploadFailedAllRetriesCount ArchiverUploadSuccessCount ArchiverDeleteFailedAllRetriesCount ArchiverDeleteSuccessCount ArchiverHandleVisibilityFailedAllRetiresCount ArchiverHandleVisibilitySuccessCount ArchiverBacklogSizeGauge ArchiverPumpTimeoutCount ArchiverPumpSignalThresholdCount ArchiverPumpTimeoutWithoutSignalsCount ArchiverPumpSignalChannelClosedCount ArchiverWorkflowStartedCount ArchiverNumPumpedRequestsCount ArchiverNumHandledRequestsCount ArchiverPumpedNotEqualHandledCount ArchiverHandleAllRequestsLatency ArchiverWorkflowStoppingCount TaskProcessedCount TaskDeletedCount TaskListProcessedCount TaskListDeletedCount TaskListOutstandingCount ExecutionsOutstandingCount StartedCount StoppedCount ExecutorTasksDeferredCount ExecutorTasksDroppedCount BatcherProcessorSuccess BatcherProcessorFailures HistoryScavengerSuccessCount HistoryScavengerErrorCount HistoryScavengerSkipCount DomainReplicationEnqueueDLQCount ScannerExecutionsGauge ScannerCorruptedGauge ScannerCheckFailedGauge ScannerCorruptionByTypeGauge ScannerCorruptedOpenExecutionGauge ScannerShardSizeMaxGauge ScannerShardSizeMedianGauge ScannerShardSizeMinGauge ScannerShardSizeNinetyGauge ScannerShardSizeSeventyFiveGauge ScannerShardSizeTwentyFiveGauge ScannerShardSizeTenGauge ShardScannerScan ShardScannerFix DataCorruptionWorkflowCount DataCorruptionWorkflowFailure DataCorruptionWorkflowSuccessCount DataCorruptionWorkflowSkipCount ESAnalyzerNumStuckWorkflowsDiscovered ESAnalyzerNumStuckWorkflowsRefreshed ESAnalyzerNumStuckWorkflowsFailedToRefresh ESAnalyzerNumLongRunningWorkflows AsyncWorkflowConsumerCount AsyncWorkflowProcessMsgLatency AsyncWorkflowFailureCorruptMsgCount AsyncWorkflowFailureByFrontendCount AsyncWorkflowSuccessCount DiagnosticsWorkflowStartedCount DiagnosticsWorkflowSuccess DiagnosticsWorkflowExecutionLatency NumWorkerMetrics ) // ShardDistributor metrics enum const ( ShardDistributorRequests = iota + NumWorkerMetrics ShardDistributorFailures ShardDistributorLatency ShardDistributorErrContextTimeoutCounter ShardDistributorErrNamespaceNotFound ShardDistributorErrShardNotFound ShardDistributorAssignLoopNumRebalancedShards ShardDistributorAssignLoopShardRebalanceLatency ShardDistributorAssignLoopAttempts ShardDistributorAssignLoopSuccess ShardDistributorAssignLoopFail ShardDistributorActiveShards ShardDistributorTotalExecutors ShardDistributorOldestExecutorHeartbeatLag ShardDistributorStoreExecutorNotFound ShardDistributorStoreFailuresPerNamespace ShardDistributorStoreRequestsPerNamespace ShardDistributorStoreLatencyHistogramPerNamespace // ShardDistributorShardAssignmentDistributionLatency measures the time taken between assignment of a shard // and the time it is fully distributed to executors ShardDistributorShardAssignmentDistributionLatency // ShardDistributorShardHandoverLatency measures the time taken to hand over a shard from one executor to another ShardDistributorShardHandoverLatency // ShardDistributorWatchProcessingLatency measures how long it takes to process a single WatchResponse ShardDistributorWatchProcessingLatency // ShardDistributorWatchEventsReceived counts the total number of watch events received ShardDistributorWatchEventsReceived NumShardDistributorMetrics ) // MetricDefs record the metrics for all services var MetricDefs = map[ServiceIdx]map[MetricIdx]metricDefinition{ Common: { CadenceRequests: {metricName: "cadence_requests", metricType: Counter}, CadenceFailures: {metricName: "cadence_errors", metricType: Counter}, CadenceLatency: {metricName: "cadence_latency", metricType: Timer}, CadenceErrBadRequestCounter: {metricName: "cadence_errors_bad_request", metricType: Counter}, CadenceErrDomainNotActiveCounter: {metricName: "cadence_errors_domain_not_active", metricType: Counter}, CadenceErrServiceBusyCounter: {metricName: "cadence_errors_service_busy", metricType: Counter}, CadenceErrEntityNotExistsCounter: {metricName: "cadence_errors_entity_not_exists", metricType: Counter}, CadenceErrWorkflowExecutionAlreadyCompletedCounter: {metricName: "cadence_errors_workflow_execution_already_completed", metricType: Counter}, CadenceErrExecutionAlreadyStartedCounter: {metricName: "cadence_errors_execution_already_started", metricType: Counter}, CadenceErrDomainAlreadyExistsCounter: {metricName: "cadence_errors_domain_already_exists", metricType: Counter}, CadenceErrCancellationAlreadyRequestedCounter: {metricName: "cadence_errors_cancellation_already_requested", metricType: Counter}, CadenceErrQueryFailedCounter: {metricName: "cadence_errors_query_failed", metricType: Counter}, CadenceErrLimitExceededCounter: {metricName: "cadence_errors_limit_exceeded", metricType: Counter}, CadenceErrContextTimeoutCounter: {metricName: "cadence_errors_context_timeout", metricType: Counter}, CadenceErrGRPCConnectionClosingCounter: {metricName: "cadence_errors_grpc_connection_closing", metricType: Counter}, CadenceErrRetryTaskCounter: {metricName: "cadence_errors_retry_task", metricType: Counter}, CadenceErrBadBinaryCounter: {metricName: "cadence_errors_bad_binary", metricType: Counter}, CadenceErrClientVersionNotSupportedCounter: {metricName: "cadence_errors_client_version_not_supported", metricType: Counter}, CadenceErrIncompleteHistoryCounter: {metricName: "cadence_errors_incomplete_history", metricType: Counter}, CadenceErrNonDeterministicCounter: {metricName: "cadence_errors_nondeterministic", metricType: Counter}, CadenceErrUnauthorizedCounter: {metricName: "cadence_errors_unauthorized", metricType: Counter}, CadenceErrAuthorizeFailedCounter: {metricName: "cadence_errors_authorize_failed", metricType: Counter}, CadenceRequestsWithoutCallerType: {metricName: "cadence_requests_without_caller_type", metricType: Counter}, CadenceErrRemoteSyncMatchFailedCounter: {metricName: "cadence_errors_remote_syncmatch_failed", metricType: Counter}, CadenceErrDomainNameExceededWarnLimit: {metricName: "cadence_errors_domain_name_exceeded_warn_limit", metricType: Counter}, CadenceErrIdentityExceededWarnLimit: {metricName: "cadence_errors_identity_exceeded_warn_limit", metricType: Counter}, CadenceErrWorkflowIDExceededWarnLimit: {metricName: "cadence_errors_workflow_id_exceeded_warn_limit", metricType: Counter}, CadenceErrSignalNameExceededWarnLimit: {metricName: "cadence_errors_signal_name_exceeded_warn_limit", metricType: Counter}, CadenceErrWorkflowTypeExceededWarnLimit: {metricName: "cadence_errors_workflow_type_exceeded_warn_limit", metricType: Counter}, CadenceErrRequestIDExceededWarnLimit: {metricName: "cadence_errors_request_id_exceeded_warn_limit", metricType: Counter}, CadenceErrTaskListNameExceededWarnLimit: {metricName: "cadence_errors_task_list_name_exceeded_warn_limit", metricType: Counter}, CadenceErrActivityIDExceededWarnLimit: {metricName: "cadence_errors_activity_id_exceeded_warn_limit", metricType: Counter}, CadenceErrActivityTypeExceededWarnLimit: {metricName: "cadence_errors_activity_type_exceeded_warn_limit", metricType: Counter}, CadenceErrMarkerNameExceededWarnLimit: {metricName: "cadence_errors_marker_name_exceeded_warn_limit", metricType: Counter}, CadenceErrTimerIDExceededWarnLimit: {metricName: "cadence_errors_timer_id_exceeded_warn_limit", metricType: Counter}, PersistenceRequests: {metricName: "persistence_requests", metricType: Counter}, PersistenceFailures: {metricName: "persistence_errors", metricType: Counter}, PersistenceLatency: {metricName: "persistence_latency", metricType: Timer}, PersistenceLatencyHistogram: {metricName: "persistence_latency_histogram", metricType: Histogram, buckets: PersistenceLatencyBuckets}, PersistenceErrShardExistsCounter: {metricName: "persistence_errors_shard_exists", metricType: Counter}, PersistenceErrShardOwnershipLostCounter: {metricName: "persistence_errors_shard_ownership_lost", metricType: Counter}, PersistenceErrConditionFailedCounter: {metricName: "persistence_errors_condition_failed", metricType: Counter}, PersistenceErrCurrentWorkflowConditionFailedCounter: {metricName: "persistence_errors_current_workflow_condition_failed", metricType: Counter}, PersistenceErrTimeoutCounter: {metricName: "persistence_errors_timeout", metricType: Counter}, PersistenceErrBusyCounter: {metricName: "persistence_errors_busy", metricType: Counter}, PersistenceErrEntityNotExistsCounter: {metricName: "persistence_errors_entity_not_exists", metricType: Counter}, PersistenceErrExecutionAlreadyStartedCounter: {metricName: "persistence_errors_execution_already_started", metricType: Counter}, PersistenceErrDomainAlreadyExistsCounter: {metricName: "persistence_errors_domain_already_exists", metricType: Counter}, PersistenceErrBadRequestCounter: {metricName: "persistence_errors_bad_request", metricType: Counter}, PersistenceErrDuplicateRequestCounter: {metricName: "persistence_errors_duplicate_request", metricType: Counter}, PersistenceErrDBUnavailableCounter: {metricName: "persistence_errors_db_unavailable", metricType: Counter}, PersistenceSampledCounter: {metricName: "persistence_sampled", metricType: Counter}, PersistenceEmptyResponseCounter: {metricName: "persistence_empty_response", metricType: Counter}, PersistenceResponseRowSize: {metricName: "persistence_response_row_size", metricType: Histogram, buckets: ResponseRowSizeBuckets}, PersistenceResponsePayloadSize: {metricName: "persistence_response_payload_size", metricType: Histogram, buckets: ResponsePayloadSizeBuckets}, PersistenceRequestsPerDomain: {metricName: "persistence_requests_per_domain", metricRollupName: "persistence_requests", metricType: Counter}, PersistenceRequestsPerShard: {metricName: "persistence_requests_per_shard", metricType: Counter}, PersistenceFailuresPerDomain: {metricName: "persistence_errors_per_domain", metricRollupName: "persistence_errors", metricType: Counter}, PersistenceLatencyPerDomain: {metricName: "persistence_latency_per_domain", metricRollupName: "persistence_latency", metricType: Timer}, PersistenceLatencyPerShard: {metricName: "persistence_latency_per_shard", metricType: Timer}, PersistenceErrShardExistsCounterPerDomain: {metricName: "persistence_errors_shard_exists_per_domain", metricRollupName: "persistence_errors_shard_exists", metricType: Counter}, PersistenceErrShardOwnershipLostCounterPerDomain: {metricName: "persistence_errors_shard_ownership_lost_per_domain", metricRollupName: "persistence_errors_shard_ownership_lost", metricType: Counter}, PersistenceErrConditionFailedCounterPerDomain: {metricName: "persistence_errors_condition_failed_per_domain", metricRollupName: "persistence_errors_condition_failed", metricType: Counter}, PersistenceErrCurrentWorkflowConditionFailedCounterPerDomain: {metricName: "persistence_errors_current_workflow_condition_failed_per_domain", metricRollupName: "persistence_errors_current_workflow_condition_failed", metricType: Counter}, PersistenceErrTimeoutCounterPerDomain: {metricName: "persistence_errors_timeout_per_domain", metricRollupName: "persistence_errors_timeout", metricType: Counter}, PersistenceErrBusyCounterPerDomain: {metricName: "persistence_errors_busy_per_domain", metricRollupName: "persistence_errors_busy", metricType: Counter}, PersistenceErrEntityNotExistsCounterPerDomain: {metricName: "persistence_errors_entity_not_exists_per_domain", metricRollupName: "persistence_errors_entity_not_exists", metricType: Counter}, PersistenceErrExecutionAlreadyStartedCounterPerDomain: {metricName: "persistence_errors_execution_already_started_per_domain", metricRollupName: "persistence_errors_execution_already_started", metricType: Counter}, PersistenceErrDomainAlreadyExistsCounterPerDomain: {metricName: "persistence_errors_domain_already_exists_per_domain", metricRollupName: "persistence_errors_domain_already_exists", metricType: Counter}, PersistenceErrBadRequestCounterPerDomain: {metricName: "persistence_errors_bad_request_per_domain", metricRollupName: "persistence_errors_bad_request", metricType: Counter}, PersistenceErrDuplicateRequestCounterPerDomain: {metricName: "persistence_errors_duplicate_request_per_domain", metricRollupName: "persistence_errors_duplicate_request", metricType: Counter}, PersistenceErrDBUnavailableCounterPerDomain: {metricName: "persistence_errors_db_unavailable_per_domain", metricRollupName: "persistence_errors_db_unavailable", metricType: Counter}, PersistenceSampledCounterPerDomain: {metricName: "persistence_sampled_per_domain", metricRollupName: "persistence_sampled", metricType: Counter}, PersistenceEmptyResponseCounterPerDomain: {metricName: "persistence_empty_response_per_domain", metricRollupName: "persistence_empty_response", metricType: Counter}, NoSQLShardStoreReadFromOriginalColumnCounter: {metricName: "nosql_shard_store_read_from_original_column", metricType: Counter}, NoSQLShardStoreReadFromDataBlobCounter: {metricName: "nosql_shard_store_read_from_data_blob", metricType: Counter}, CadenceClientRequests: {metricName: "cadence_client_requests", metricType: Counter}, CadenceClientFailures: {metricName: "cadence_client_errors", metricType: Counter}, CadenceClientLatency: {metricName: "cadence_client_latency", metricType: Timer}, CadenceTasklistRequests: {metricName: "cadence_tasklist_request", metricType: Counter}, CadenceDcRedirectionClientRequests: {metricName: "cadence_client_requests_redirection", metricType: Counter}, CadenceDcRedirectionClientFailures: {metricName: "cadence_client_errors_redirection", metricType: Counter}, CadenceDcRedirectionClientLatency: {metricName: "cadence_client_latency_redirection", metricType: Timer}, CadenceAuthorizationLatency: {metricName: "cadence_authorization_latency", metricType: Timer}, DomainCachePrepareCallbacksLatency: {metricName: "domain_cache_prepare_callbacks_latency", metricType: Timer}, DomainCacheCallbacksLatency: {metricName: "domain_cache_callbacks_latency", metricType: Timer}, DomainCacheCallbacksCount: {metricName: "domain_cache_callbacks_count", metricType: Counter}, HistorySize: {metricName: "history_size", metricType: Timer}, HistoryCount: {metricName: "history_count", metricType: Timer}, EventBlobSizeExceedLimit: {metricName: "blob_size_exceed_limit", metricType: Counter}, EventBlobSize: {metricName: "event_blob_size", metricType: Timer}, DecisionResultCount: {metricName: "decision_result_count", metricType: Timer}, ArchivalConfigFailures: {metricName: "archivalconfig_failures", metricType: Counter}, ActiveClusterGauge: {metricName: "active_cluster", metricType: Gauge}, ElasticsearchRequests: {metricName: "elasticsearch_requests", metricType: Counter}, ElasticsearchFailures: {metricName: "elasticsearch_errors", metricType: Counter}, ElasticsearchLatency: {metricName: "elasticsearch_latency", metricType: Timer}, ElasticsearchErrBadRequestCounter: {metricName: "elasticsearch_errors_bad_request", metricType: Counter}, ElasticsearchErrBusyCounter: {metricName: "elasticsearch_errors_busy", metricType: Counter}, ElasticsearchRequestsPerDomain: {metricName: "elasticsearch_requests_per_domain", metricRollupName: "elasticsearch_requests", metricType: Counter}, ElasticsearchFailuresPerDomain: {metricName: "elasticsearch_errors_per_domain", metricRollupName: "elasticsearch_errors", metricType: Counter}, ElasticsearchLatencyPerDomain: {metricName: "elasticsearch_latency_per_domain", metricRollupName: "elasticsearch_latency", metricType: Timer}, ElasticsearchErrBadRequestCounterPerDomain: {metricName: "elasticsearch_errors_bad_request_per_domain", metricRollupName: "elasticsearch_errors_bad_request", metricType: Counter}, ElasticsearchErrBusyCounterPerDomain: {metricName: "elasticsearch_errors_busy_per_domain", metricRollupName: "elasticsearch_errors_busy", metricType: Counter}, PinotRequests: {metricName: "pinot_requests", metricType: Counter}, PinotFailures: {metricName: "pinot_errors", metricType: Counter}, PinotLatency: {metricName: "pinot_latency", metricType: Timer}, PinotErrBadRequestCounter: {metricName: "pinot_errors_bad_request", metricType: Counter}, PinotErrBusyCounter: {metricName: "pinot_errors_busy", metricType: Counter}, PinotRequestsPerDomain: {metricName: "pinot_requests_per_domain", metricRollupName: "pinot_requests", metricType: Counter}, PinotFailuresPerDomain: {metricName: "pinot_errors_per_domain", metricRollupName: "pinot_errors", metricType: Counter}, PinotLatencyPerDomain: {metricName: "pinot_latency_per_domain", metricRollupName: "pinot_latency", metricType: Timer}, PinotErrBadRequestCounterPerDomain: {metricName: "pinot_errors_bad_request_per_domain", metricRollupName: "pinot_errors_bad_request", metricType: Counter}, PinotErrBusyCounterPerDomain: {metricName: "pinot_errors_busy_per_domain", metricRollupName: "pinot_errors_busy", metricType: Counter}, SequentialTaskSubmitRequest: {metricName: "sequentialtask_submit_request", metricType: Counter}, SequentialTaskSubmitRequestTaskQueueExist: {metricName: "sequentialtask_submit_request_taskqueue_exist", metricType: Counter}, SequentialTaskSubmitRequestTaskQueueMissing: {metricName: "sequentialtask_submit_request_taskqueue_missing", metricType: Counter}, SequentialTaskSubmitLatency: {metricName: "sequentialtask_submit_latency", metricType: Timer}, SequentialTaskQueueSize: {metricName: "sequentialtask_queue_size", metricType: Timer}, SequentialTaskQueueProcessingLatency: {metricName: "sequentialtask_queue_processing_latency", metricType: Timer}, SequentialTaskTaskProcessingLatency: {metricName: "sequentialtask_task_processing_latency", metricType: Timer}, ParallelTaskSubmitRequest: {metricName: "paralleltask_submit_request", metricType: Counter}, ParallelTaskSubmitLatency: {metricName: "paralleltask_submit_latency", metricType: Timer}, ParallelTaskTaskProcessingLatency: {metricName: "paralleltask_task_processing_latency", metricType: Timer}, PriorityTaskSubmitRequest: {metricName: "prioritytask_submit_request", metricType: Counter}, PriorityTaskSubmitLatency: {metricName: "prioritytask_submit_latency", metricType: Timer}, KafkaConsumerMessageIn: {metricName: "kafka_consumer_message_in", metricType: Counter}, KafkaConsumerMessageAck: {metricName: "kafka_consumer_message_ack", metricType: Counter}, KafkaConsumerMessageNack: {metricName: "kafka_consumer_message_nack", metricType: Counter}, KafkaConsumerMessageNackDlqErr: {metricName: "kafka_consumer_message_nack_dlq_err", metricType: Counter}, KafkaConsumerSessionStart: {metricName: "kafka_consumer_session_start", metricType: Counter}, GracefulFailoverLatency: {metricName: "graceful_failover_latency", metricType: Timer}, GracefulFailoverFailure: {metricName: "graceful_failover_failures", metricType: Counter}, HistoryArchiverArchiveNonRetryableErrorCount: {metricName: "history_archiver_archive_non_retryable_error", metricType: Counter}, HistoryArchiverArchiveTransientErrorCount: {metricName: "history_archiver_archive_transient_error", metricType: Counter}, HistoryArchiverArchiveSuccessCount: {metricName: "history_archiver_archive_success", metricType: Counter}, HistoryArchiverHistoryMutatedCount: {metricName: "history_archiver_history_mutated", metricType: Counter}, HistoryArchiverTotalUploadSize: {metricName: "history_archiver_total_upload_size", metricType: Timer}, HistoryArchiverHistorySize: {metricName: "history_archiver_history_size", metricType: Timer}, HistoryArchiverDuplicateArchivalsCount: {metricName: "history_archiver_duplicate_archivals", metricType: Counter}, HistoryArchiverBlobExistsCount: {metricName: "history_archiver_blob_exists", metricType: Counter}, HistoryArchiverBlobSize: {metricName: "history_archiver_blob_size", metricType: Timer}, HistoryArchiverRunningDeterministicConstructionCheckCount: {metricName: "history_archiver_running_deterministic_construction_check", metricType: Counter}, HistoryArchiverDeterministicConstructionCheckFailedCount: {metricName: "history_archiver_deterministic_construction_check_failed", metricType: Counter}, HistoryArchiverRunningBlobIntegrityCheckCount: {metricName: "history_archiver_running_blob_integrity_check", metricType: Counter}, HistoryArchiverBlobIntegrityCheckFailedCount: {metricName: "history_archiver_blob_integrity_check_failed", metricType: Counter}, VisibilityArchiverArchiveNonRetryableErrorCount: {metricName: "visibility_archiver_archive_non_retryable_error", metricType: Counter}, VisibilityArchiverArchiveTransientErrorCount: {metricName: "visibility_archiver_archive_transient_error", metricType: Counter}, VisibilityArchiveSuccessCount: {metricName: "visibility_archiver_archive_success", metricType: Counter}, MatchingClientForwardedCounter: {metricName: "forwarded", metricType: Counter}, MatchingClientInvalidTaskListName: {metricName: "invalid_task_list_name", metricType: Counter}, // per task list common metrics CadenceRequestsPerTaskList: { metricName: "cadence_requests_per_tl", metricRollupName: "cadence_requests", metricType: Counter, }, CadenceRequestsPerTaskListWithoutRollup: { metricName: "cadence_requests_per_tl", metricType: Counter, }, CadenceFailuresPerTaskList: { metricName: "cadence_errors_per_tl", metricRollupName: "cadence_errors", metricType: Counter, }, CadenceLatencyPerTaskList: { metricName: "cadence_latency_per_tl", metricRollupName: "cadence_latency", metricType: Timer, }, CadenceErrBadRequestPerTaskListCounter: { metricName: "cadence_errors_bad_request_per_tl", metricRollupName: "cadence_errors_bad_request", metricType: Counter, }, CadenceErrDomainNotActivePerTaskListCounter: { metricName: "cadence_errors_domain_not_active_per_tl", metricRollupName: "cadence_errors_domain_not_active", metricType: Counter, }, CadenceErrServiceBusyPerTaskListCounter: { metricName: "cadence_errors_service_busy_per_tl", metricRollupName: "cadence_errors_service_busy", metricType: Counter, }, CadenceErrEntityNotExistsPerTaskListCounter: { metricName: "cadence_errors_entity_not_exists_per_tl", metricRollupName: "cadence_errors_entity_not_exists", metricType: Counter, }, CadenceErrExecutionAlreadyStartedPerTaskListCounter: { metricName: "cadence_errors_execution_already_started_per_tl", metricRollupName: "cadence_errors_execution_already_started", metricType: Counter, }, CadenceErrDomainAlreadyExistsPerTaskListCounter: { metricName: "cadence_errors_domain_already_exists_per_tl", metricRollupName: "cadence_errors_domain_already_exists", metricType: Counter, }, CadenceErrCancellationAlreadyRequestedPerTaskListCounter: { metricName: "cadence_errors_cancellation_already_requested_per_tl", metricRollupName: "cadence_errors_cancellation_already_requested", metricType: Counter, }, CadenceErrQueryFailedPerTaskListCounter: { metricName: "cadence_errors_query_failed_per_tl", metricRollupName: "cadence_errors_query_failed", metricType: Counter, }, CadenceErrLimitExceededPerTaskListCounter: { metricName: "cadence_errors_limit_exceeded_per_tl", metricRollupName: "cadence_errors_limit_exceeded", metricType: Counter, }, CadenceErrContextTimeoutPerTaskListCounter: { metricName: "cadence_errors_context_timeout_per_tl", metricRollupName: "cadence_errors_context_timeout", metricType: Counter, }, CadenceErrRetryTaskPerTaskListCounter: { metricName: "cadence_errors_retry_task_per_tl", metricRollupName: "cadence_errors_retry_task", metricType: Counter, }, CadenceErrBadBinaryPerTaskListCounter: { metricName: "cadence_errors_bad_binary_per_tl", metricRollupName: "cadence_errors_bad_binary", metricType: Counter, }, CadenceErrClientVersionNotSupportedPerTaskListCounter: { metricName: "cadence_errors_client_version_not_supported_per_tl", metricRollupName: "cadence_errors_client_version_not_supported", metricType: Counter, }, CadenceErrIncompleteHistoryPerTaskListCounter: { metricName: "cadence_errors_incomplete_history_per_tl", metricRollupName: "cadence_errors_incomplete_history", metricType: Counter, }, CadenceErrNonDeterministicPerTaskListCounter: { metricName: "cadence_errors_nondeterministic_per_tl", metricRollupName: "cadence_errors_nondeterministic", metricType: Counter, }, CadenceErrUnauthorizedPerTaskListCounter: { metricName: "cadence_errors_unauthorized_per_tl", metricRollupName: "cadence_errors_unauthorized", metricType: Counter, }, CadenceErrAuthorizeFailedPerTaskListCounter: { metricName: "cadence_errors_authorize_failed_per_tl", metricRollupName: "cadence_errors_authorize_failed", metricType: Counter, }, CadenceErrRemoteSyncMatchFailedPerTaskListCounter: { metricName: "cadence_errors_remote_syncmatch_failed_per_tl", metricRollupName: "cadence_errors_remote_syncmatch_failed", metricType: Counter, }, CadenceErrStickyWorkerUnavailablePerTaskListCounter: { metricName: "cadence_errors_sticky_worker_unavailable_per_tl", metricRollupName: "cadence_errors_sticky_worker_unavailable_per_tl", metricType: Counter, }, CadenceErrReadOnlyPartitionPerTaskListCounter: { metricName: "cadence_errors_read_only_partition_per_tl", metricRollupName: "cadence_errors_read_only_partition", metricType: Counter, }, CadenceErrTaskListNotOwnedByHostPerTaskListCounter: { metricName: "cadence_errors_task_list_not_owned_by_host_per_tl", metricRollupName: "cadence_errors_task_list_not_owned_by_host", metricType: Counter, }, CadenceShardSuccessGauge: {metricName: "cadence_shard_success", metricType: Gauge}, CadenceShardFailureGauge: {metricName: "cadence_shard_failure", metricType: Gauge}, DomainReplicationQueueSizeGauge: {metricName: "domain_replication_queue_size", metricType: Gauge}, DomainReplicationQueueSizeErrorCount: {metricName: "domain_replication_queue_failed", metricType: Counter}, DomainCacheUpdateLatency: {metricName: "domain_cache_update_latency", metricType: Histogram, buckets: DomainCacheUpdateBuckets}, ParentClosePolicyProcessorSuccess: {metricName: "parent_close_policy_processor_requests", metricType: Counter}, ParentClosePolicyProcessorFailures: {metricName: "parent_close_policy_processor_errors", metricType: Counter}, ValidatedWorkflowCount: {metricName: "task_validator_count", metricType: Counter}, HashringViewIdentifier: {metricName: "hashring_view_identifier", metricType: Counter}, DescribeWorkflowStatusError: {metricName: "describe_wf_error", metricType: Counter}, DescribeWorkflowStatusCount: {metricName: "describe_wf_status", metricType: Counter}, AsyncRequestPayloadSize: {metricName: "async_request_payload_size_per_domain", metricRollupName: "async_request_payload_size", metricType: Timer}, GlobalRatelimiterStartupUsageHistogram: {metricName: "global_ratelimiter_startup_usage_histogram", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterFailingUsageHistogram: {metricName: "global_ratelimiter_failing_usage_histogram", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterGlobalUsageHistogram: {metricName: "global_ratelimiter_global_usage_histogram", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterUpdateLatency: {metricName: "global_ratelimiter_update_latency", metricType: Timer}, GlobalRatelimiterAllowedRequestsCount: {metricName: "global_ratelimiter_allowed_requests", metricType: Counter}, GlobalRatelimiterRejectedRequestsCount: {metricName: "global_ratelimiter_rejected_requests", metricType: Counter}, GlobalRatelimiterQuota: {metricName: "global_ratelimiter_quota", metricType: Gauge}, GlobalRatelimiterInitialized: {metricName: "global_ratelimiter_initialized", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterReinitialized: {metricName: "global_ratelimiter_reinitialized", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterUpdated: {metricName: "global_ratelimiter_updated", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterDecayed: {metricName: "global_ratelimiter_decayed", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterLimitsQueried: {metricName: "global_ratelimiter_limits_queried", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterHostLimitsQueried: {metricName: "global_ratelimiter_host_limits_queried", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterRemovedLimits: {metricName: "global_ratelimiter_removed_limits", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, GlobalRatelimiterRemovedHostLimits: {metricName: "global_ratelimiter_removed_host_limits", metricType: Histogram, buckets: GlobalRatelimiterUsageHistogram}, P2PPeersCount: {metricName: "peers_count", metricType: Gauge}, P2PPeerAdded: {metricName: "peer_added", metricType: Counter}, P2PPeerRemoved: {metricName: "peer_removed", metricType: Counter}, TaskListPartitionConfigVersionGauge: {metricName: "task_list_partition_config_version", metricType: Gauge}, TaskListPartitionConfigNumReadGauge: {metricName: "task_list_partition_config_num_read", metricType: Gauge}, TaskListPartitionConfigNumWriteGauge: {metricName: "task_list_partition_config_num_write", metricType: Gauge}, BaseCacheByteSize: {metricName: "cache_byte_size", metricType: Gauge}, BaseCacheByteSizeLimitGauge: {metricName: "cache_byte_size_limit", metricType: Gauge}, BaseCacheHit: {metricName: "cache_hit", metricType: Counter}, BaseCacheMiss: {metricName: "cache_miss", metricType: Counter}, BaseCacheCount: {metricName: "cache_count", metricType: Counter}, BaseCacheCountLimitGauge: {metricName: "cache_count_limit", metricType: Gauge}, BaseCacheFullCounter: {metricName: "cache_full", metricType: Counter}, BaseCacheEvictCounter: {metricName: "cache_evict", metricType: Counter}, ActiveClusterManagerLookupRequestCount: {metricName: "active_cluster_manager_lookup_request_count", metricType: Counter}, ActiveClusterManagerLookupSuccessCount: {metricName: "active_cluster_manager_lookup_success_count", metricType: Counter}, ActiveClusterManagerLookupFailureCount: {metricName: "active_cluster_manager_lookup_failure_count", metricType: Counter}, ActiveClusterManagerLookupLatency: {metricName: "active_cluster_manager_lookup_latency", metricType: Histogram, buckets: ExponentialDurationBuckets}, ClusterForwardingPolicyRequests: {metricName: "cluster_forwarding_policy_requests", metricType: Counter}, RingResolverError: {metricName: "ring_resolver_error", metricType: Counter}, WorkflowExecutionHistoryAccess: {metricName: "workflow_execution_history_access", metricType: Gauge}, // Budget manager metrics BudgetManagerCapacityBytes: {metricName: "budget_manager_capacity_bytes", metricType: Gauge}, BudgetManagerCapacityCount: {metricName: "budget_manager_capacity_count", metricType: Gauge}, BudgetManagerUsedBytes: {metricName: "budget_manager_used_bytes", metricType: Gauge}, BudgetManagerUsedCount: {metricName: "budget_manager_used_count", metricType: Gauge}, BudgetManagerSoftThreshold: {metricName: "budget_manager_soft_threshold", metricType: Gauge}, BudgetManagerActiveCacheCount: {metricName: "budget_manager_active_cache_count", metricType: Gauge}, BudgetManagerHardCapExceeded: {metricName: "budget_manager_hard_cap_exceeded", metricType: Counter}, BudgetManagerSoftCapExceeded: {metricName: "budget_manager_soft_cap_exceeded", metricType: Counter}, WeightedChannelPoolSizeGauge: {metricName: "weighted_channel_pool_size", metricType: Gauge}, }, History: { TaskRequests: {metricName: "task_requests", metricType: Counter}, TaskLatency: {metricName: "task_latency", metricType: Timer}, ExponentialTaskLatency: {metricName: "task_latency_ns", metricType: Histogram, exponentialBuckets: Low1ms100s}, TaskAttemptTimer: {metricName: "task_attempt", metricType: Timer}, ExponentialTaskAttemptCounts: {metricName: "task_attempt_counts", metricType: Histogram, intExponentialBuckets: Mid1To16k}, TaskFailures: {metricName: "task_errors", metricType: Counter}, TaskDiscarded: {metricName: "task_errors_discarded", metricType: Counter}, TaskStandbyRetryCounter: {metricName: "task_errors_standby_retry_counter", metricType: Counter}, TaskNotActiveCounter: {metricName: "task_errors_not_active_counter", metricType: Counter}, TaskLimitExceededCounter: {metricName: "task_errors_limit_exceeded_counter", metricType: Counter}, TaskProcessingLatency: {metricName: "task_latency_processing", metricType: Timer}, ExponentialTaskProcessingLatency: {metricName: "task_latency_processing_ns", metricType: Histogram, exponentialBuckets: Low1ms100s}, TaskQueueLatency: {metricName: "task_latency_queue", metricType: Timer}, ExponentialTaskQueueLatency: {metricName: "task_latency_queue_ns", metricType: Histogram, exponentialBuckets: Mid1ms24h}, ScheduleToStartHistoryQueueLatencyPerTaskList: {metricName: "schedule_to_start_history_queue_latency_per_tl", metricType: Timer}, TaskRequestsOldScheduler: {metricName: "task_requests_old_scheduler", metricType: Counter}, TaskRequestsNewScheduler: {metricName: "task_requests_new_scheduler", metricType: Counter}, PendingTaskGauge: {metricName: "pending_task_gauge", metricType: Gauge}, ReschedulerTaskCountGauge: {metricName: "rescheduler_task_count", metricType: Gauge}, NewHistoryTaskCounter: {metricName: "new_history_task_counter", metricType: Counter}, // per domain task metrics TaskRequestsPerDomain: {metricName: "task_requests_per_domain", metricRollupName: "task_requests", metricType: Counter}, TaskLatencyPerDomain: {metricName: "task_latency_per_domain", metricRollupName: "task_latency", metricType: Timer}, ExponentialTaskLatencyPerDomain: {metricName: "task_latency_per_domain_ns", metricType: Histogram, exponentialBuckets: Mid1ms24h}, TaskAttemptTimerPerDomain: {metricName: "task_attempt_per_domain", metricRollupName: "task_attempt", metricType: Timer}, ExponentialTaskAttemptCountsPerDomain: {metricName: "task_attempt_per_domain_counts", metricType: Histogram, intExponentialBuckets: Mid1To16k}, TaskFailuresPerDomain: {metricName: "task_errors_per_domain", metricRollupName: "task_errors", metricType: Counter}, TaskWorkflowBusyPerDomain: {metricName: "task_errors_workflow_busy_per_domain", metricRollupName: "task_errors_workflow_busy", metricType: Counter}, TaskDiscardedPerDomain: {metricName: "task_errors_discarded_per_domain", metricRollupName: "task_errors_discarded", metricType: Counter}, TaskUnsupportedPerDomain: {metricName: "task_errors_unsupported_per_domain", metricRollupName: "task_errors_discarded", metricType: Counter}, TaskStandbyRetryCounterPerDomain: {metricName: "task_errors_standby_retry_counter_per_domain", metricRollupName: "task_errors_standby_retry_counter", metricType: Counter}, TaskListNotOwnedByHostCounterPerDomain: {metricName: "task_errors_task_list_not_owned_by_host_counter_per_domain", metricRollupName: "task_errors_task_list_not_owned_by_host_counter", metricType: Counter}, TaskPendingActiveCounterPerDomain: {metricName: "task_errors_pending_active_counter_per_domain", metricRollupName: "task_errors_pending_active_counter", metricType: Counter}, TaskNotActiveCounterPerDomain: {metricName: "task_errors_not_active_counter_per_domain", metricRollupName: "task_errors_not_active_counter", metricType: Counter}, TaskTargetNotActiveCounterPerDomain: {metricName: "task_errors_target_not_active_counter_per_domain", metricRollupName: "task_errors_target_not_active_counter", metricType: Counter}, TaskLimitExceededCounterPerDomain: {metricName: "task_errors_limit_exceeded_counter_per_domain", metricRollupName: "task_errors_limit_exceeded_counter", metricType: Counter}, TaskProcessingLatencyPerDomain: {metricName: "task_latency_processing_per_domain", metricRollupName: "task_latency_processing", metricType: Timer}, ExponentialTaskProcessingLatencyPerDomain: {metricName: "task_latency_processing_per_domain_ns", metricType: Histogram, exponentialBuckets: Low1ms100s}, TaskQueueLatencyPerDomain: {metricName: "task_latency_queue_per_domain", metricRollupName: "task_latency_queue", metricType: Timer}, ExponentialTaskQueueLatencyPerDomain: {metricName: "task_latency_queue_per_domain_ns", metricType: Histogram, exponentialBuckets: Mid1ms24h}, TaskScheduleLatencyPerDomain: {metricName: "task_latency_schedule_per_domain", metricRollupName: "task_latency_schedule", metricType: Histogram, buckets: HistoryTaskLatencyBuckets}, TaskEnqueueToFetchLatency: {metricName: "task_latency_enqueue_to_fetch", metricType: Histogram, buckets: HistoryTaskLatencyBuckets}, TransferTaskMissingEventCounterPerDomain: {metricName: "transfer_task_missing_event_counter_per_domain", metricRollupName: "transfer_task_missing_event_counter", metricType: Counter}, ReplicationTasksAppliedPerDomain: {metricName: "replication_tasks_applied_per_domain", metricType: Counter}, WorkflowTerminateCounterPerDomain: {metricName: "workflow_terminate_counter_per_domain", metricRollupName: "workflow_terminate_counter", metricType: Counter}, TaskSchedulerAllowedCounterPerDomain: {metricName: "task_scheduler_allowed_counter_per_domain", metricRollupName: "task_scheduler_allowed_counter", metricType: Counter}, TaskSchedulerThrottledCounterPerDomain: {metricName: "task_scheduler_throttled_counter_per_domain", metricRollupName: "task_scheduler_throttled_counter", metricType: Counter}, // per task list task metrics TaskRequestsPerTaskList: {metricName: "task_requests_per_task_list", metricType: Counter}, ExponentialTaskLatencyPerTaskList: {metricName: "task_latency_per_task_list_ns", metricType: Histogram, exponentialBuckets: High1ms24h}, ExponentialTaskProcessingLatencyPerTaskList: {metricName: "task_latency_processing_per_task_list_ns", metricType: Histogram, exponentialBuckets: High1ms24h}, ExponentialTaskQueueLatencyPerTaskList: {metricName: "task_latency_queue_per_task_list_ns", metricType: Histogram, exponentialBuckets: High1ms24h}, ExponentialTaskScheduleLatencyPerTaskList: {metricName: "task_latency_schedule_per_task_list_ns", metricType: Histogram, exponentialBuckets: High1ms24h}, TaskBatchCompleteCounter: {metricName: "task_batch_complete_counter", metricType: Counter}, TaskBatchCompleteFailure: {metricName: "task_batch_complete_error", metricType: Counter}, TaskRedispatchQueuePendingTasksTimer: {metricName: "task_redispatch_queue_pending_tasks", metricType: Timer}, TransferTaskThrottledCounter: {metricName: "transfer_task_throttled_counter", metricType: Counter}, TimerTaskThrottledCounter: {metricName: "timer_task_throttled_counter", metricType: Counter}, CrossClusterTaskThrottledCounter: {metricName: "cross_cluster_task_throttled_counter", metricType: Counter}, TransferTaskMissingEventCounter: {metricName: "transfer_task_missing_event_counter", metricType: Counter}, ProcessingQueueNumTimer: {metricName: "processing_queue_num", metricType: Timer}, ProcessingQueueMaxLevelTimer: {metricName: "processing_queue_max_level", metricType: Timer}, ProcessingQueuePendingTaskSplitCounter: {metricName: "processing_queue_pending_task_split_counter", metricType: Counter}, ProcessingQueueStuckTaskSplitCounter: {metricName: "processing_queue_stuck_task_split_counter", metricType: Counter}, ProcessingQueueSelectedDomainSplitCounter: {metricName: "processing_queue_selected_domain_split_counter", metricType: Counter}, ProcessingQueueRandomSplitCounter: {metricName: "processing_queue_random_split_counter", metricType: Counter}, ProcessingQueueThrottledCounter: {metricName: "processing_queue_throttled_counter", metricType: Counter}, CorruptedHistoryTaskCounter: {metricName: "corrupted_history_task_counter", metricType: Counter}, QueueValidatorLostTaskCounter: {metricName: "queue_validator_lost_task_counter", metricType: Counter}, QueueValidatorDropTaskCounter: {metricName: "queue_validator_drop_task_counter", metricType: Counter}, QueueValidatorInvalidLoadCounter: {metricName: "queue_validator_invalid_load_counter", metricType: Counter}, QueueValidatorValidationCounter: {metricName: "queue_validator_validation_counter", metricType: Counter}, QueueValidatorValidationFailure: {metricName: "queue_validator_validation_error", metricType: Counter}, CrossClusterFetchLatency: {metricName: "cross_cluster_fetch_latency", metricType: Timer}, CrossClusterFetchRequests: {metricName: "cross_cluster_fetch_requests", metricType: Counter}, CrossClusterFetchFailures: {metricName: "cross_cluster_fetch_errors", metricType: Counter}, CrossClusterFetchServiceBusyFailures: {metricName: "cross_cluster_fetch_errors_service_busy", metricType: Counter}, CrossClusterTaskRespondLatency: {metricName: "cross_cluster_respond_latency", metricType: Timer}, CrossClusterTaskRespondRequests: {metricName: "cross_cluster_respond_requests", metricType: Counter}, CrossClusterTaskRespondFailures: {metricName: "cross_cluster_fetch_errors", metricType: Counter}, CrossClusterTaskFetchedTimer: {metricName: "cross_cluster_task_fetched", metricType: Timer}, CrossClusterTaskPendingTimer: {metricName: "cross_cluster_task_pending", metricType: Timer}, ClusterMetadataFailureToResolveCounter: {metricName: "failed_to_resolve_failover_version", metricType: Counter}, ClusterMetadataGettingMinFailoverVersionCounter: {metricName: "getting_min_failover_version_counter", metricType: Counter}, ClusterMetadataGettingFailoverVersionCounter: {metricName: "getting_failover_version_counter", metricType: Counter}, ClusterMetadataResolvingFailoverVersionCounter: {metricName: "resolving_failover_version_counter", metricType: Counter}, ClusterMetadataResolvingMinFailoverVersionCounter: {metricName: "resolving_min_failover_version_counter", metricType: Counter}, ActivityE2ELatency: {metricName: "activity_end_to_end_latency", metricType: Timer}, ActivityLostCounter: {metricName: "activity_lost", metricType: Counter}, AckLevelUpdateCounter: {metricName: "ack_level_update", metricType: Counter}, AckLevelUpdateFailedCounter: {metricName: "ack_level_update_failed", metricType: Counter}, DecisionTypeScheduleActivityCounter: {metricName: "schedule_activity_decision", metricType: Counter}, DecisionTypeScheduleActivityDispatchSucceedCounter: {metricName: "schedule_activity_decision_sync_match_succeed", metricType: Counter}, DecisionTypeScheduleActivityDispatchCounter: {metricName: "schedule_activity_decision_try_sync_match", metricType: Counter}, DecisionTypeCompleteWorkflowCounter: {metricName: "complete_workflow_decision", metricType: Counter}, DecisionTypeFailWorkflowCounter: {metricName: "fail_workflow_decision", metricType: Counter}, DecisionTypeCancelWorkflowCounter: {metricName: "cancel_workflow_decision", metricType: Counter}, DecisionTypeStartTimerCounter: {metricName: "start_timer_decision", metricType: Counter}, DecisionTypeCancelActivityCounter: {metricName: "cancel_activity_decision", metricType: Counter}, DecisionTypeCancelTimerCounter: {metricName: "cancel_timer_decision", metricType: Counter}, DecisionTypeRecordMarkerCounter: {metricName: "record_marker_decision", metricType: Counter}, DecisionTypeCancelExternalWorkflowCounter: {metricName: "cancel_external_workflow_decision", metricType: Counter}, DecisionTypeContinueAsNewCounter: {metricName: "continue_as_new_decision", metricType: Counter}, DecisionTypeSignalExternalWorkflowCounter: {metricName: "signal_external_workflow_decision", metricType: Counter}, DecisionTypeUpsertWorkflowSearchAttributesCounter: {metricName: "upsert_workflow_search_attributes_decision", metricType: Counter}, DecisionTypeChildWorkflowCounter: {metricName: "child_workflow_decision", metricType: Counter}, EmptyCompletionDecisionsCounter: {metricName: "empty_completion_decisions", metricType: Counter}, MultipleCompletionDecisionsCounter: {metricName: "multiple_completion_decisions", metricType: Counter}, FailedDecisionsCounter: {metricName: "failed_decisions", metricType: Counter}, DecisionAttemptTimer: {metricName: "decision_attempt", metricType: Timer}, DecisionRetriesExceededCounter: {metricName: "decision_retries_exceeded", metricType: Counter}, StaleMutableStateCounter: {metricName: "stale_mutable_state", metricType: Counter}, DataInconsistentCounter: {metricName: "data_inconsistent", metricType: Counter}, DuplicateActivityTaskEventCounter: {metricName: "duplicate_activity_task_event", metricType: Counter}, TimerResurrectionCounter: {metricName: "timer_resurrection", metricType: Counter}, TimerProcessingDeletionTimerNoopDueToMutableStateNotLoading: {metricName: "timer_processing_skipping_deletion_due_to_missing_mutable_state", metricType: Counter}, TimerProcessingDeletionTimerNoopDueToWFRunning: {metricName: "timer_processing_skipping_deletion_due_to_running", metricType: Counter}, ActivityResurrectionCounter: {metricName: "activity_resurrection", metricType: Counter}, AutoResetPointsLimitExceededCounter: {metricName: "auto_reset_points_exceed_limit", metricType: Counter}, AutoResetPointCorruptionCounter: {metricName: "auto_reset_point_corruption", metricType: Counter}, ConcurrencyUpdateFailureCounter: {metricName: "concurrency_update_failure", metricType: Counter}, CadenceErrShardOwnershipLostCounter: {metricName: "cadence_errors_shard_ownership_lost", metricType: Counter}, CadenceErrEventAlreadyStartedCounter: {metricName: "cadence_errors_event_already_started", metricType: Counter}, HeartbeatTimeoutCounter: {metricName: "heartbeat_timeout", metricType: Counter}, ScheduleToStartTimeoutCounter: {metricName: "schedule_to_start_timeout", metricType: Counter}, StartToCloseTimeoutCounter: {metricName: "start_to_close_timeout", metricType: Counter}, ScheduleToCloseTimeoutCounter: {metricName: "schedule_to_close_timeout", metricType: Counter}, NewTimerCounter: {metricName: "new_timer", metricType: Counter}, NewTimerNotifyCounter: {metricName: "new_timer_notifications", metricType: Counter}, AcquireShardsCounter: {metricName: "acquire_shards_count", metricType: Counter}, AcquireShardsLatency: {metricName: "acquire_shards_latency", metricType: Timer}, ShardClosedCounter: {metricName: "shard_closed_count", metricType: Counter}, ShardItemCreatedCounter: {metricName: "sharditem_created_count", metricType: Counter}, ShardItemRemovedCounter: {metricName: "sharditem_removed_count", metricType: Counter}, ShardItemAcquisitionLatency: {metricName: "sharditem_acquisition_latency", metricType: Timer}, ShardInfoReplicationPendingTasksTimer: {metricName: "shardinfo_replication_pending_task", metricType: Timer}, ShardInfoTransferActivePendingTasksTimer: {metricName: "shardinfo_transfer_active_pending_task", metricType: Timer}, ShardInfoTransferStandbyPendingTasksTimer: {metricName: "shardinfo_transfer_standby_pending_task", metricType: Timer}, ShardInfoTimerActivePendingTasksTimer: {metricName: "shardinfo_timer_active_pending_task", metricType: Timer}, ShardInfoTimerStandbyPendingTasksTimer: {metricName: "shardinfo_timer_standby_pending_task", metricType: Timer}, ShardInfoCrossClusterPendingTasksTimer: {metricName: "shardinfo_cross_cluster_pending_task", metricType: Timer}, ShardInfoReplicationLagTimer: {metricName: "shardinfo_replication_lag", metricType: Timer}, ShardInfoTransferLagTimer: {metricName: "shardinfo_transfer_lag", metricType: Timer}, ShardInfoTimerLagTimer: {metricName: "shardinfo_timer_lag", metricType: Timer}, ShardInfoCrossClusterLagTimer: {metricName: "shardinfo_cross_cluster_lag", metricType: Timer}, ShardInfoTransferDiffTimer: {metricName: "shardinfo_transfer_diff", metricType: Timer}, ShardInfoTimerDiffTimer: {metricName: "shardinfo_timer_diff", metricType: Timer}, ShardInfoTransferFailoverInProgressTimer: {metricName: "shardinfo_transfer_failover_in_progress", metricType: Timer}, ShardInfoTimerFailoverInProgressTimer: {metricName: "shardinfo_timer_failover_in_progress", metricType: Timer}, ShardInfoTransferFailoverLatencyTimer: {metricName: "shardinfo_transfer_failover_latency", metricType: Timer}, ShardInfoTimerFailoverLatencyTimer: {metricName: "shardinfo_timer_failover_latency", metricType: Timer}, SyncShardFromRemoteCounter: {metricName: "syncshard_remote_count", metricType: Counter}, SyncShardFromRemoteFailure: {metricName: "syncshard_remote_failed", metricType: Counter}, MembershipChangedCounter: {metricName: "membership_changed_count", metricType: Counter}, NumShardsGauge: {metricName: "numshards_gauge", metricType: Gauge}, GetEngineForShardErrorCounter: {metricName: "get_engine_for_shard_errors", metricType: Counter}, GetEngineForShardLatency: {metricName: "get_engine_for_shard_latency", metricType: Timer}, RemoveEngineForShardLatency: {metricName: "remove_engine_for_shard_latency", metricType: Timer}, CompleteDecisionWithStickyEnabledCounter: {metricName: "complete_decision_sticky_enabled_count", metricType: Counter}, CompleteDecisionWithStickyDisabledCounter: {metricName: "complete_decision_sticky_disabled_count", metricType: Counter}, DecisionHeartbeatTimeoutCounter: {metricName: "decision_heartbeat_timeout_count", metricType: Counter}, HistoryEventNotificationQueueingLatency: {metricName: "history_event_notification_queueing_latency", metricType: Timer}, HistoryEventNotificationFanoutLatency: {metricName: "history_event_notification_fanout_latency", metricType: Timer}, HistoryEventNotificationInFlightMessageGauge: {metricName: "history_event_notification_inflight_message_gauge", metricType: Gauge}, HistoryEventNotificationFailDeliveryCount: {metricName: "history_event_notification_fail_delivery_count", metricType: Counter}, EmptyReplicationEventsCounter: {metricName: "empty_replication_events", metricType: Counter}, DuplicateReplicationEventsCounter: {metricName: "duplicate_replication_events", metricType: Counter}, StaleReplicationEventsCounter: {metricName: "stale_replication_events", metricType: Counter}, ReplicationEventsSizeTimer: {metricName: "replication_events_size", metricType: Timer}, BufferReplicationTaskTimer: {metricName: "buffer_replication_tasks", metricType: Timer}, UnbufferReplicationTaskTimer: {metricName: "unbuffer_replication_tasks", metricType: Timer}, HistoryConflictsCounter: {metricName: "history_conflicts", metricType: Counter}, CompleteTaskFailedCounter: {metricName: "complete_task_fail_count", metricType: Counter}, CacheSize: {metricName: "cache_size", metricType: Timer}, CacheSizeHistogram: {metricName: "cache_size_counts", metricType: Histogram, buckets: TaskCountBuckets}, CacheRequests: {metricName: "cache_requests", metricType: Counter}, CacheFailures: {metricName: "cache_errors", metricType: Counter}, CacheLatency: {metricName: "cache_latency", metricType: Timer}, ExponentialCacheLatency: {metricName: "cache_latency_ns", metricType: Histogram, exponentialBuckets: Low1ms100s}, CacheHitCounter: {metricName: "cache_hit", metricType: Counter}, CacheMissCounter: {metricName: "cache_miss", metricType: Counter}, CacheFullCounter: {metricName: "cache_full", metricType: Counter}, AcquireLockFailedCounter: {metricName: "acquire_lock_failed", metricType: Counter}, WorkflowContextCleared: {metricName: "workflow_context_cleared", metricType: Counter}, WorkflowContextLockLatency: {metricName: "workflow_context_lock_latency", metricType: Timer}, MutableStateSize: {metricName: "mutable_state_size", metricType: Timer}, ExecutionInfoSize: {metricName: "execution_info_size", metricType: Timer}, ActivityInfoSize: {metricName: "activity_info_size", metricType: Timer}, TimerInfoSize: {metricName: "timer_info_size", metricType: Timer}, ChildInfoSize: {metricName: "child_info_size", metricType: Timer}, SignalInfoSize: {metricName: "signal_info_size", metricType: Timer}, BufferedEventsSize: {metricName: "buffered_events_size", metricType: Timer}, ActivityInfoCount: {metricName: "activity_info_count", metricType: Timer}, TimerInfoCount: {metricName: "timer_info_count", metricType: Timer}, ChildInfoCount: {metricName: "child_info_count", metricType: Timer}, SignalInfoCount: {metricName: "signal_info_count", metricType: Timer}, RequestCancelInfoCount: {metricName: "request_cancel_info_count", metricType: Timer}, BufferedEventsCount: {metricName: "buffered_events_count", metricType: Timer}, DeleteActivityInfoCount: {metricName: "delete_activity_info", metricType: Timer}, DeleteTimerInfoCount: {metricName: "delete_timer_info", metricType: Timer}, DeleteChildInfoCount: {metricName: "delete_child_info", metricType: Timer}, DeleteSignalInfoCount: {metricName: "delete_signal_info", metricType: Timer}, DeleteRequestCancelInfoCount: {metricName: "delete_request_cancel_info", metricType: Timer}, WorkflowRetryBackoffTimerCount: {metricName: "workflow_retry_backoff_timer", metricType: Counter}, WorkflowCronBackoffTimerCount: {metricName: "workflow_cron_backoff_timer", metricType: Counter}, WorkflowCleanupDeleteCount: {metricName: "workflow_cleanup_delete", metricType: Counter}, WorkflowCleanupArchiveCount: {metricName: "workflow_cleanup_archive", metricType: Counter}, WorkflowCleanupNopCount: {metricName: "workflow_cleanup_nop", metricType: Counter}, WorkflowCleanupDeleteHistoryInlineCount: {metricName: "workflow_cleanup_delete_history_inline", metricType: Counter}, WorkflowSuccessCount: {metricName: "workflow_success", metricType: Counter}, WorkflowCancelCount: {metricName: "workflow_cancel", metricType: Counter}, WorkflowFailedCount: {metricName: "workflow_failed", metricType: Counter}, WorkflowTimeoutCount: {metricName: "workflow_timeout", metricType: Counter}, WorkflowTerminateCount: {metricName: "workflow_terminate", metricType: Counter}, WorkflowContinuedAsNew: {metricName: "workflow_continued_as_new", metricType: Counter}, WorkflowCompletedUnknownType: {metricName: "workflow_completed_unknown_type", metricType: Counter}, WorkflowCreationFailedCleanupHaltedTimeoutCount: {metricName: "workflow_creation_failed_cleanup_halted_timeout_count", metricType: Counter}, // where an attempt to cleanup after wf start failure was halted due to a timeout making it uncertain if it's safe WorkflowCreationFailedCleanupUnknownCount: {metricName: "workflow_creation_failed_cleanup_unknown_count", metricType: Counter}, // where an attempt to cleanup after wf start failure was halted due to not having enough certainty WorkflowCreationFailedCleanupSuccessCount: {metricName: "workflow_creation_failed_cleanup_success_count", metricType: Counter}, // where an attempt to cleanup after wf start failure failure was successful WorkflowCreationFailedCleanupFailureCount: {metricName: "workflow_creation_failed_cleanup_failure_count", metricType: Counter}, // where an attempt to cleanup in failure also resulted in failure ArchiverClientSendSignalCount: {metricName: "archiver_client_sent_signal", metricType: Counter}, ArchiverClientSendSignalFailureCount: {metricName: "archiver_client_send_signal_error", metricType: Counter}, ArchiverClientHistoryRequestCount: {metricName: "archiver_client_history_request", metricType: Counter}, ArchiverClientHistoryInlineArchiveAttemptCount: {metricName: "archiver_client_history_inline_archive_attempt", metricType: Counter}, ArchiverClientHistoryInlineArchiveFailureCount: {metricName: "archiver_client_history_inline_archive_failure", metricType: Counter}, ArchiverClientHistoryInlineArchiveThrottledCount: {metricName: "archiver_client_history_inline_archive_throttled", metricType: Counter}, ArchiverClientVisibilityRequestCount: {metricName: "archiver_client_visibility_request", metricType: Counter}, ArchiverClientVisibilityInlineArchiveAttemptCount: {metricName: "archiver_client_visibility_inline_archive_attempt", metricType: Counter}, ArchiverClientVisibilityInlineArchiveFailureCount: {metricName: "archiver_client_visibility_inline_archive_failure", metricType: Counter}, ArchiverClientVisibilityInlineArchiveThrottledCount: {metricName: "archiver_client_visibility_inline_archive_throttled", metricType: Counter}, ArchiverClientSendSignalCountPerDomain: {metricName: "archiver_client_sent_signal_per_domain", metricRollupName: "archiver_client_sent_signal", metricType: Counter}, ArchiverClientSendSignalFailureCountPerDomain: {metricName: "archiver_client_send_signal_error_per_domain", metricRollupName: "archiver_client_send_signal_error", metricType: Counter}, ArchiverClientHistoryRequestCountPerDomain: {metricName: "archiver_client_history_request_per_domain", metricRollupName: "archiver_client_history_request", metricType: Counter}, ArchiverClientHistoryInlineArchiveAttemptCountPerDomain: {metricName: "archiver_client_history_inline_archive_attempt_per_domain", metricRollupName: "archiver_client_history_inline_archive_attempt", metricType: Counter}, ArchiverClientHistoryInlineArchiveFailureCountPerDomain: {metricName: "archiver_client_history_inline_archive_failure_per_domain", metricRollupName: "archiver_client_history_inline_archive_failure", metricType: Counter}, ArchiverClientHistoryInlineArchiveThrottledCountPerDomain: {metricName: "archiver_client_history_inline_archive_throttled_per_domain", metricRollupName: "archiver_client_history_inline_archive_throttled", metricType: Counter}, ArchiverClientVisibilityRequestCountPerDomain: {metricName: "archiver_client_visibility_request_per_domain", metricRollupName: "archiver_client_visibility_request", metricType: Counter}, ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain: {metricName: "archiver_client_visibility_inline_archive_attempt_per_domain", metricRollupName: "archiver_client_visibility_inline_archive_attempt", metricType: Counter}, ArchiverClientVisibilityInlineArchiveFailureCountPerDomain: {metricName: "archiver_client_visibility_inline_archive_failure_per_domain", metricRollupName: "archiver_client_visibility_inline_archive_failure", metricType: Counter}, ArchiverClientVisibilityInlineArchiveThrottledCountPerDomain: {metricName: "archiver_client_visibility_inline_archive_throttled_per_domain", metricRollupName: "archiver_client_visibility_inline_archive_throttled", metricType: Counter}, LastRetrievedMessageID: {metricName: "last_retrieved_message_id", metricType: Gauge}, LastProcessedMessageID: {metricName: "last_processed_message_id", metricType: Gauge}, ReplicationLatency: {metricName: "replication_latency", metricType: Gauge}, ReplicationTasksApplied: {metricName: "replication_tasks_applied", metricType: Counter}, ReplicationTasksFailed: {metricName: "replication_tasks_failed", metricType: Counter}, ReplicationTasksLag: {metricName: "replication_tasks_lag", metricType: Timer}, ReplicationTasksLagHistogram: {metricName: "replication_tasks_lag_counts", metricType: Histogram, buckets: TaskCountBuckets}, ReplicationTasksLagRaw: {metricName: "replication_tasks_lag_raw", metricType: Timer}, ReplicationTasksLagRawHistogram: {metricName: "replication_tasks_lag_raw_counts", metricType: Histogram, buckets: TaskCountBuckets}, ReplicationTasksDelay: {metricName: "replication_tasks_delay", metricType: Histogram, buckets: ReplicationTaskDelayBucket}, ReplicationTasksFetched: {metricName: "replication_tasks_fetched", metricType: Timer}, ReplicationTasksFetchedHistogram: {metricName: "replication_tasks_fetched_counts", metricType: Histogram, buckets: ResponseRowSizeBuckets}, ReplicationTasksReturned: {metricName: "replication_tasks_returned", metricType: Timer}, ReplicationTasksReturnedHistogram: {metricName: "replication_tasks_returned_counts", metricType: Histogram, buckets: ResponseRowSizeBuckets}, ReplicationTasksReturnedDiff: {metricName: "replication_tasks_returned_diff", metricType: Timer}, ReplicationTasksReturnedDiffHistogram: {metricName: "replication_tasks_returned_diff_counts", metricType: Histogram, buckets: ResponseRowSizeBuckets}, ReplicationTasksAppliedLatency: {metricName: "replication_tasks_applied_latency", metricType: Timer}, ReplicationTasksAppliedLatencyHistogram: {metricName: "replication_tasks_applied_latency_ns", metricType: Histogram, exponentialBuckets: Low1ms100s}, ReplicationTasksBatchSize: {metricName: "replication_tasks_batch_size", metricType: Gauge}, ReplicationDynamicTaskBatchSizerDecision: {metricName: "replication_dynamic_task_batch_sizer_decision", metricType: Counter}, ReplicationDLQFailed: {metricName: "replication_dlq_enqueue_failed", metricType: Counter}, ReplicationDLQMaxLevelGauge: {metricName: "replication_dlq_max_level", metricType: Gauge}, ReplicationDLQAckLevelGauge: {metricName: "replication_dlq_ack_level", metricType: Gauge}, ReplicationDLQProbeFailed: {metricName: "replication_dlq_probe_failed", metricType: Counter}, ReplicationDLQSize: {metricName: "replication_dlq_size", metricType: Gauge}, ReplicationDLQValidationFailed: {metricName: "replication_dlq_validation_failed", metricType: Counter}, ReplicationMessageTooLargePerShard: {metricName: "replication_message_too_large_per_shard", metricType: Counter}, GetReplicationMessagesForShardLatency: {metricName: "get_replication_messages_for_shard", metricType: Timer}, GetDLQReplicationMessagesLatency: {metricName: "get_dlq_replication_messages", metricType: Timer}, EventReapplySkippedCount: {metricName: "event_reapply_skipped_count", metricType: Counter}, DirectQueryDispatchLatency: {metricName: "direct_query_dispatch_latency", metricType: Timer}, DirectQueryDispatchStickyLatency: {metricName: "direct_query_dispatch_sticky_latency", metricType: Timer}, DirectQueryDispatchNonStickyLatency: {metricName: "direct_query_dispatch_non_sticky_latency", metricType: Timer}, DirectQueryDispatchStickySuccessCount: {metricName: "direct_query_dispatch_sticky_success", metricType: Counter}, DirectQueryDispatchNonStickySuccessCount: {metricName: "direct_query_dispatch_non_sticky_success", metricType: Counter}, DirectQueryDispatchClearStickinessLatency: {metricName: "direct_query_dispatch_clear_stickiness_latency", metricType: Timer}, DirectQueryDispatchClearStickinessSuccessCount: {metricName: "direct_query_dispatch_clear_stickiness_success", metricType: Counter}, DirectQueryDispatchTimeoutBeforeNonStickyCount: {metricName: "direct_query_dispatch_timeout_before_non_sticky", metricType: Counter}, DecisionTaskQueryLatency: {metricName: "decision_task_query_latency", metricType: Timer}, ConsistentQueryPerShard: {metricName: "consistent_query_per_shard", metricType: Counter}, ConsistentQueryTimeoutCount: {metricName: "consistent_query_timeout", metricType: Counter}, QueryBeforeFirstDecisionCount: {metricName: "query_before_first_decision", metricType: Counter}, QueryBufferExceededCount: {metricName: "query_buffer_exceeded", metricType: Counter}, QueryRegistryInvalidStateCount: {metricName: "query_registry_invalid_state", metricType: Counter}, WorkerNotSupportsConsistentQueryCount: {metricName: "worker_not_supports_consistent_query", metricType: Counter}, DecisionStartToCloseTimeoutOverrideCount: {metricName: "decision_start_to_close_timeout_overrides", metricType: Counter}, ReplicationTaskCleanupCount: {metricName: "replication_task_cleanup_count", metricType: Counter}, ReplicationTaskCleanupFailure: {metricName: "replication_task_cleanup_failed", metricType: Counter}, ReplicationTaskLatency: {metricName: "replication_task_latency", metricType: Timer}, ExponentialReplicationTaskLatency: {metricName: "replication_task_latency_ns", metricType: Histogram, exponentialBuckets: Mid1ms24h}, ExponentialReplicationTaskFetchLatency: {metricName: "replication_task_fetch_latency_ns", metricType: Histogram, exponentialBuckets: Mid1ms24h}, ReplicationTasksFetchedSize: {metricName: "replication_tasks_fetched_size", metricType: Histogram, buckets: ResponseRowSizeBuckets}, MutableStateChecksumMismatch: {metricName: "mutable_state_checksum_mismatch", metricType: Counter}, MutableStateChecksumInvalidated: {metricName: "mutable_state_checksum_invalidated", metricType: Counter}, FailoverMarkerCount: {metricName: "failover_marker_count", metricType: Counter}, FailoverMarkerReplicationLatency: {metricName: "failover_marker_replication_latency", metricType: Timer}, FailoverMarkerInsertFailure: {metricName: "failover_marker_insert_failures", metricType: Counter}, FailoverMarkerNotificationFailure: {metricName: "failover_marker_notification_failures", metricType: Counter}, FailoverMarkerUpdateShardFailure: {metricName: "failover_marker_update_shard_failures", metricType: Counter}, FailoverMarkerCallbackCount: {metricName: "failover_marker_callback_count", metricType: Counter}, HistoryFailoverCallbackCount: {metricName: "failover_callback_handler_count", metricType: Counter}, TransferTasksCount: {metricName: "transfer_tasks_count", metricType: Timer}, TimerTasksCount: {metricName: "timer_tasks_count", metricType: Timer}, CrossClusterTasksCount: {metricName: "cross_cluster_tasks_count", metricType: Timer}, ReplicationTasksCount: {metricName: "replication_tasks_count", metricType: Timer}, WorkflowVersionCount: {metricName: "workflow_version_count", metricType: Gauge}, WorkflowTypeCount: {metricName: "workflow_type_count", metricType: Gauge}, WorkflowStartedCount: {metricName: "workflow_started_count", metricType: Counter}, LargeHistoryBlobCount: {metricName: "large_history_blob_count", metricType: Counter}, LargeHistoryEventCount: {metricName: "large_history_event_count", metricType: Counter}, LargeHistorySizeCount: {metricName: "large_history_size_count", metricType: Counter}, UpdateWorkflowExecutionCount: {metricName: "update_workflow_execution_count", metricType: Counter}, WorkflowIDCacheSizeGauge: {metricName: "workflow_id_cache_size", metricType: Gauge}, WorkflowIDCacheRequestsExternalRatelimitedCounter: {metricName: "workflow_id_external_requests_ratelimited", metricType: Counter}, WorkflowIDCacheRequestsExternalMaxRequestsPerSecondsTimer: {metricName: "workflow_id_external_requests_max_requests_per_seconds", metricType: Timer}, WorkflowIDCacheRequestsInternalMaxRequestsPerSecondsTimer: {metricName: "workflow_id_internal_requests_max_requests_per_seconds", metricType: Timer}, WorkflowIDCacheRequestsInternalRatelimitedCounter: {metricName: "workflow_id_internal_requests_ratelimited", metricType: Counter}, VirtualQueueCountGauge: {metricName: "virtual_queue_count", metricType: Gauge}, VirtualQueuePausedGauge: {metricName: "virtual_queue_paused", metricType: Gauge}, VirtualQueueRunningGauge: {metricName: "virtual_queue_running", metricType: Gauge}, }, Matching: { PollSuccessPerTaskListCounter: {metricName: "poll_success_per_tl", metricRollupName: "poll_success"}, PollTimeoutPerTaskListCounter: {metricName: "poll_timeouts_per_tl", metricRollupName: "poll_timeouts"}, PollSuccessWithSyncPerTaskListCounter: {metricName: "poll_success_sync_per_tl", metricRollupName: "poll_success_sync"}, LeaseRequestPerTaskListCounter: {metricName: "lease_requests_per_tl", metricRollupName: "lease_requests"}, LeaseFailurePerTaskListCounter: {metricName: "lease_failures_per_tl", metricRollupName: "lease_failures"}, ConditionFailedErrorPerTaskListCounter: {metricName: "condition_failed_errors_per_tl", metricRollupName: "condition_failed_errors"}, RespondQueryTaskFailedPerTaskListCounter: {metricName: "respond_query_failed_per_tl", metricRollupName: "respond_query_failed"}, SyncThrottlePerTaskListCounter: {metricName: "sync_throttle_count_per_tl", metricRollupName: "sync_throttle_count"}, BufferThrottlePerTaskListCounter: {metricName: "buffer_throttle_count_per_tl", metricRollupName: "buffer_throttle_count"}, BufferUnknownTaskDispatchError: {metricName: "buffer_unknown_task_dispatch_error_per_tl", metricRollupName: "buffer_unknown_task_dispatch_error"}, BufferIsolationGroupRedirectCounter: {metricName: "buffer_isolation_group_redirected_per_tl", metricRollupName: "buffer_isolation_group_redirected"}, BufferIsolationGroupRedirectFailureCounter: {metricName: "buffer_isolation_group_redirect_failure_per_tl", metricRollupName: "buffer_isolation_group_redirect_failure"}, BufferIsolationGroupMisconfiguredCounter: {metricName: "buffer_isolation_group_misconfigured_failure_per_tl", metricRollupName: "buffer_isolation_group_misconfigured_failure"}, ExpiredTasksPerTaskListCounter: {metricName: "tasks_expired_per_tl", metricRollupName: "tasks_expired"}, ForwardedPerTaskListCounter: {metricName: "forwarded_per_tl", metricRollupName: "forwarded"}, ForwardTaskCallsPerTaskList: {metricName: "forward_task_calls_per_tl", metricRollupName: "forward_task_calls"}, ForwardTaskErrorsPerTaskList: {metricName: "forward_task_errors_per_tl", metricRollupName: "forward_task_errors"}, SyncMatchForwardTaskThrottleErrorPerTasklist: {metricName: "sync_forward_task_throttle_errors_per_tl", metricRollupName: "sync_forward_task_throttle_errors"}, AsyncMatchForwardTaskThrottleErrorPerTasklist: {metricName: "async_forward_task_throttle_errors_per_tl", metricRollupName: "async_forward_task_throttle_errors"}, ForwardQueryCallsPerTaskList: {metricName: "forward_query_calls_per_tl", metricRollupName: "forward_query_calls"}, ForwardQueryErrorsPerTaskList: {metricName: "forward_query_errors_per_tl", metricRollupName: "forward_query_errors"}, ForwardPollCallsPerTaskList: {metricName: "forward_poll_calls_per_tl", metricRollupName: "forward_poll_calls"}, ForwardPollErrorsPerTaskList: {metricName: "forward_poll_errors_per_tl", metricRollupName: "forward_poll_errors"}, SyncMatchLatencyPerTaskList: {metricName: "syncmatch_latency_per_tl", metricRollupName: "syncmatch_latency", metricType: Timer}, AsyncMatchLatencyPerTaskList: {metricName: "asyncmatch_latency_per_tl", metricRollupName: "asyncmatch_latency", metricType: Timer}, AsyncMatchDispatchLatencyPerTaskList: {metricName: "asyncmatch_dispatch_latency_per_tl", metricRollupName: "asyncmatch_dispatch_latency", metricType: Timer}, AsyncMatchDispatchTimeoutCounterPerTaskList: {metricName: "asyncmatch_dispatch_timeouts_per_tl", metricRollupName: "asyncmatch_dispatch_timeouts"}, ForwardTaskLatencyPerTaskList: {metricName: "forward_task_latency_per_tl", metricRollupName: "forward_task_latency"}, ForwardQueryLatencyPerTaskList: {metricName: "forward_query_latency_per_tl", metricRollupName: "forward_query_latency"}, ForwardPollLatencyPerTaskList: {metricName: "forward_poll_latency_per_tl", metricRollupName: "forward_poll_latency"}, LocalToLocalMatchPerTaskListCounter: {metricName: "local_to_local_matches_per_tl", metricRollupName: "local_to_local_matches"}, LocalToRemoteMatchPerTaskListCounter: {metricName: "local_to_remote_matches_per_tl", metricRollupName: "local_to_remote_matches"}, RemoteToLocalMatchPerTaskListCounter: {metricName: "remote_to_local_matches_per_tl", metricRollupName: "remote_to_local_matches"}, RemoteToRemoteMatchPerTaskListCounter: {metricName: "remote_to_remote_matches_per_tl", metricRollupName: "remote_to_remote_matches"}, IsolationTaskMatchPerTaskListCounter: {metricName: "isolation_task_matches_per_tl", metricType: Counter}, IsolationSuccessPerTaskListCounter: {metricName: "isolation_success_per_tl", metricRollupName: "isolation_success"}, PollerPerTaskListCounter: {metricName: "poller_count_per_tl", metricRollupName: "poller_count"}, PollerInvalidIsolationGroupCounter: {metricName: "poller_invalid_isolation_group_per_tl", metricType: Counter}, TaskListPartitionUpdateFailedCounter: {metricName: "tasklist_partition_update_failed_per_tl", metricType: Counter}, TaskListManagersGauge: {metricName: "tasklist_managers", metricType: Gauge}, TaskLagPerTaskListGauge: {metricName: "task_lag_per_tl", metricType: Gauge}, TaskBacklogPerTaskListGauge: {metricName: "task_backlog_per_tl", metricType: Gauge}, TaskCountPerTaskListGauge: {metricName: "task_count_per_tl", metricType: Gauge}, RateLimitPerTaskListGauge: {metricName: "rate_limit_per_tl", metricType: Gauge}, SyncMatchLocalPollLatencyPerTaskList: {metricName: "syncmatch_local_poll_latency_per_tl", metricRollupName: "syncmatch_local_poll_latency"}, SyncMatchForwardPollLatencyPerTaskList: {metricName: "syncmatch_forward_poll_latency_per_tl", metricRollupName: "syncmatch_forward_poll_latency"}, AsyncMatchLocalPollCounterPerTaskList: {metricName: "asyncmatch_local_poll_per_tl", metricRollupName: "asyncmatch_local_poll"}, AsyncMatchLocalPollAttemptPerTaskList: {metricName: "asyncmatch_local_poll_attempt_per_tl", metricRollupName: "asyncmatch_local_poll_attempt", metricType: Timer}, AsyncMatchLocalPollLatencyPerTaskList: {metricName: "asyncmatch_local_poll_latency_per_tl", metricRollupName: "asyncmatch_local_poll_latency"}, AsyncMatchForwardPollCounterPerTaskList: {metricName: "asyncmatch_forward_poll_per_tl", metricRollupName: "asyncmatch_forward_poll"}, AsyncMatchForwardPollAttemptPerTaskList: {metricName: "asyncmatch_forward_poll_attempt_per_tl", metricRollupName: "asyncmatch_forward_poll_attempt", metricType: Timer}, AsyncMatchForwardPollLatencyPerTaskList: {metricName: "asyncmatch_forward_poll_latency_per_tl", metricRollupName: "asyncmatch_forward_poll_latency"}, AsyncMatchLocalPollAfterForwardFailedCounterPerTaskList: {metricName: "asyncmatch_local_poll_after_forward_failed_per_tl", metricRollupName: "asyncmatch_local_poll_after_forward_failed"}, AsyncMatchLocalPollAfterForwardFailedAttemptPerTaskList: {metricName: "asyncmatch_local_poll_after_forward_failed_attempt_per_tl", metricRollupName: "asyncmatch_local_poll_after_forward_failed_attempt", metricType: Timer}, AsyncMatchLocalPollAfterForwardFailedLatencyPerTaskList: {metricName: "asyncmatch_local_poll_after_forward_failed_latency_per_tl", metricRollupName: "asyncmatch_local_poll_after_forward_failed_latency"}, PollLocalMatchLatencyPerTaskList: {metricName: "poll_local_match_latency_per_tl", metricRollupName: "poll_local_match_latency", metricType: Timer}, PollForwardMatchLatencyPerTaskList: {metricName: "poll_forward_match_latency_per_tl", metricRollupName: "poll_forward_match_latency", metricType: Timer}, PollLocalMatchAfterForwardFailedLatencyPerTaskList: {metricName: "poll_local_match_after_forward_failed_latency_per_tl", metricRollupName: "poll_local_match_after_forward_failed_latency", metricType: Timer}, PollDecisionTaskAlreadyStartedCounterPerTaskList: {metricName: "poll_decision_task_already_started_per_tl", metricType: Counter}, PollActivityTaskAlreadyStartedCounterPerTaskList: {metricName: "poll_activity_task_already_started_per_tl", metricType: Counter}, TaskListReadWritePartitionMismatchGauge: {metricName: "tasklist_read_write_partition_mismatch", metricType: Gauge}, TaskListPollerPartitionMismatchGauge: {metricName: "tasklist_poller_partition_mismatch", metricType: Gauge}, EstimatedAddTaskQPSGauge: {metricName: "estimated_add_task_qps_per_tl", metricType: Gauge}, TaskListPartitionUpscaleThresholdGauge: {metricName: "tasklist_partition_upscale_threshold", metricType: Gauge}, TaskListPartitionDownscaleThresholdGauge: {metricName: "tasklist_partition_downscale_threshold", metricType: Gauge}, StandbyClusterTasksCompletedCounterPerTaskList: {metricName: "standby_cluster_tasks_completed_per_tl", metricType: Counter}, StandbyClusterTasksNotStartedCounterPerTaskList: {metricName: "standby_cluster_tasks_not_started_per_tl", metricType: Counter}, StandbyClusterTasksCompletionFailurePerTaskList: {metricName: "standby_cluster_tasks_completion_failure_per_tl", metricType: Counter}, TaskIsolationLeakPerTaskList: {metricName: "task_isolation_leak_per_tl", metricRollupName: "task_isolation_leak"}, PartitionUpscale: {metricName: "partition_upscale_per_tl", metricRollupName: "partition_upscale"}, PartitionDownscale: {metricName: "partition_downscale_per_tl", metricRollupName: "partition_downscale"}, PartitionDrained: {metricName: "partition_drained_per_tl", metricRollupName: "partition_drained"}, IsolationRebalance: {metricName: "isolation_rebalance_per_tl", metricRollupName: "isolation_rebalance"}, IsolationGroupStartedPolling: {metricName: "ig_started_polling_per_tl", metricRollupName: "ig_started_polling"}, IsolationGroupStoppedPolling: {metricName: "ig_stopped_polling_per_tl", metricRollupName: "ig_stopped_polling"}, IsolationGroupUpscale: {metricName: "ig_upscale_per_tl", metricRollupName: "ig_upscale"}, IsolationGroupDownscale: {metricName: "ig_downscale_per_tl", metricRollupName: "ig_downscale"}, IsolationGroupPartitionsGauge: {metricName: "ig_partitions_per_tl", metricType: Gauge}, }, Worker: { ReplicatorMessages: {metricName: "replicator_messages"}, ReplicatorFailures: {metricName: "replicator_errors"}, ReplicatorMessagesDropped: {metricName: "replicator_messages_dropped"}, ReplicatorLatency: {metricName: "replicator_latency"}, ReplicatorDLQFailures: {metricName: "replicator_dlq_enqueue_fails", metricType: Counter}, ESProcessorRequests: {metricName: "es_processor_requests"}, ESProcessorRetries: {metricName: "es_processor_retries"}, ESProcessorFailures: {metricName: "es_processor_errors"}, ESProcessorCorruptedData: {metricName: "es_processor_corrupted_data"}, ESProcessorProcessMsgLatency: {metricName: "es_processor_process_msg_latency", metricType: Timer}, IndexProcessorCorruptedData: {metricName: "index_processor_corrupted_data"}, IndexProcessorProcessMsgLatency: {metricName: "index_processor_process_msg_latency", metricType: Timer}, ArchiverNonRetryableErrorCount: {metricName: "archiver_non_retryable_error"}, ArchiverStartedCount: {metricName: "archiver_started"}, ArchiverStoppedCount: {metricName: "archiver_stopped"}, ArchiverCoroutineStartedCount: {metricName: "archiver_coroutine_started"}, ArchiverCoroutineStoppedCount: {metricName: "archiver_coroutine_stopped"}, ArchiverHandleHistoryRequestLatency: {metricName: "archiver_handle_history_request_latency"}, ArchiverHandleVisibilityRequestLatency: {metricName: "archiver_handle_visibility_request_latency"}, ArchiverUploadWithRetriesLatency: {metricName: "archiver_upload_with_retries_latency"}, ArchiverDeleteWithRetriesLatency: {metricName: "archiver_delete_with_retries_latency"}, ArchiverUploadFailedAllRetriesCount: {metricName: "archiver_upload_failed_all_retries"}, ArchiverUploadSuccessCount: {metricName: "archiver_upload_success"}, ArchiverDeleteFailedAllRetriesCount: {metricName: "archiver_delete_failed_all_retries"}, ArchiverDeleteSuccessCount: {metricName: "archiver_delete_success"}, ArchiverHandleVisibilityFailedAllRetiresCount: {metricName: "archiver_handle_visibility_failed_all_retries"}, ArchiverHandleVisibilitySuccessCount: {metricName: "archiver_handle_visibility_success"}, ArchiverBacklogSizeGauge: {metricName: "archiver_backlog_size"}, ArchiverPumpTimeoutCount: {metricName: "archiver_pump_timeout"}, ArchiverPumpSignalThresholdCount: {metricName: "archiver_pump_signal_threshold"}, ArchiverPumpTimeoutWithoutSignalsCount: {metricName: "archiver_pump_timeout_without_signals"}, ArchiverPumpSignalChannelClosedCount: {metricName: "archiver_pump_signal_channel_closed"}, ArchiverWorkflowStartedCount: {metricName: "archiver_workflow_started"}, ArchiverNumPumpedRequestsCount: {metricName: "archiver_num_pumped_requests"}, ArchiverNumHandledRequestsCount: {metricName: "archiver_num_handled_requests"}, ArchiverPumpedNotEqualHandledCount: {metricName: "archiver_pumped_not_equal_handled"}, ArchiverHandleAllRequestsLatency: {metricName: "archiver_handle_all_requests_latency"}, ArchiverWorkflowStoppingCount: {metricName: "archiver_workflow_stopping"}, TaskProcessedCount: {metricName: "task_processed", metricType: Gauge}, TaskDeletedCount: {metricName: "task_deleted", metricType: Gauge}, TaskListProcessedCount: {metricName: "tasklist_processed", metricType: Gauge}, TaskListDeletedCount: {metricName: "tasklist_deleted", metricType: Gauge}, TaskListOutstandingCount: {metricName: "tasklist_outstanding", metricType: Gauge}, ExecutionsOutstandingCount: {metricName: "executions_outstanding", metricType: Gauge}, StartedCount: {metricName: "started", metricType: Counter}, StoppedCount: {metricName: "stopped", metricType: Counter}, ExecutorTasksDeferredCount: {metricName: "executor_deferred", metricType: Counter}, ExecutorTasksDroppedCount: {metricName: "executor_dropped", metricType: Counter}, BatcherProcessorSuccess: {metricName: "batcher_processor_requests", metricType: Counter}, BatcherProcessorFailures: {metricName: "batcher_processor_errors", metricType: Counter}, HistoryScavengerSuccessCount: {metricName: "scavenger_success", metricType: Counter}, HistoryScavengerErrorCount: {metricName: "scavenger_errors", metricType: Counter}, HistoryScavengerSkipCount: {metricName: "scavenger_skips", metricType: Counter}, DomainReplicationEnqueueDLQCount: {metricName: "domain_replication_dlq_enqueue_requests", metricType: Counter}, ScannerExecutionsGauge: {metricName: "scanner_executions", metricType: Gauge}, ScannerCorruptedGauge: {metricName: "scanner_corrupted", metricType: Gauge}, ScannerCheckFailedGauge: {metricName: "scanner_check_failed", metricType: Gauge}, ScannerCorruptionByTypeGauge: {metricName: "scanner_corruption_by_type", metricType: Gauge}, ScannerCorruptedOpenExecutionGauge: {metricName: "scanner_corrupted_open_execution", metricType: Gauge}, ScannerShardSizeMaxGauge: {metricName: "scanner_shard_size_max", metricType: Gauge}, ScannerShardSizeMedianGauge: {metricName: "scanner_shard_size_median", metricType: Gauge}, ScannerShardSizeMinGauge: {metricName: "scanner_shard_size_min", metricType: Gauge}, ScannerShardSizeNinetyGauge: {metricName: "scanner_shard_size_ninety", metricType: Gauge}, ScannerShardSizeSeventyFiveGauge: {metricName: "scanner_shard_size_seventy_five", metricType: Gauge}, ScannerShardSizeTwentyFiveGauge: {metricName: "scanner_shard_size_twenty_five", metricType: Gauge}, ScannerShardSizeTenGauge: {metricName: "scanner_shard_size_ten", metricType: Gauge}, ShardScannerScan: {metricName: "shardscanner_scan", metricType: Counter}, ShardScannerFix: {metricName: "shardscanner_fix", metricType: Counter}, DataCorruptionWorkflowFailure: {metricName: "data_corruption_workflow_failure", metricType: Counter}, DataCorruptionWorkflowSuccessCount: {metricName: "data_corruption_workflow_success", metricType: Counter}, DataCorruptionWorkflowCount: {metricName: "data_corruption_workflow_count", metricType: Counter}, DataCorruptionWorkflowSkipCount: {metricName: "data_corruption_workflow_skips", metricType: Counter}, ESAnalyzerNumStuckWorkflowsDiscovered: {metricName: "es_analyzer_num_stuck_workflows_discovered", metricType: Counter}, ESAnalyzerNumStuckWorkflowsRefreshed: {metricName: "es_analyzer_num_stuck_workflows_refreshed", metricType: Counter}, ESAnalyzerNumStuckWorkflowsFailedToRefresh: {metricName: "es_analyzer_num_stuck_workflows_failed_to_refresh", metricType: Counter}, ESAnalyzerNumLongRunningWorkflows: {metricName: "es_analyzer_num_long_running_workflows", metricType: Counter}, AsyncWorkflowConsumerCount: {metricName: "async_workflow_consumer_count", metricType: Gauge}, AsyncWorkflowProcessMsgLatency: {metricName: "async_workflow_process_msg_latency", metricType: Timer}, AsyncWorkflowFailureCorruptMsgCount: {metricName: "async_workflow_failure_corrupt_msg", metricType: Counter}, AsyncWorkflowFailureByFrontendCount: {metricName: "async_workflow_failure_by_frontend", metricType: Counter}, AsyncWorkflowSuccessCount: {metricName: "async_workflow_success", metricType: Counter}, DiagnosticsWorkflowStartedCount: {metricName: "diagnostics_workflow_count", metricType: Counter}, DiagnosticsWorkflowSuccess: {metricName: "diagnostics_workflow_success", metricType: Counter}, DiagnosticsWorkflowExecutionLatency: {metricName: "diagnostics_workflow_execution_latency", metricType: Timer}, }, ShardDistributor: { ShardDistributorRequests: {metricName: "shard_distributor_requests", metricType: Counter}, ShardDistributorErrContextTimeoutCounter: {metricName: "shard_distributor_err_context_timeout", metricType: Counter}, ShardDistributorFailures: {metricName: "shard_distributor_failures", metricType: Counter}, ShardDistributorLatency: {metricName: "shard_distributor_latency", metricType: Timer}, ShardDistributorErrNamespaceNotFound: {metricName: "shard_distributor_err_namespace_not_found", metricType: Counter}, ShardDistributorErrShardNotFound: {metricName: "shard_distributor_err_shard_not_found", metricType: Counter}, ShardDistributorAssignLoopShardRebalanceLatency: {metricName: "shard_distrubutor_shard_assign_latency", metricType: Histogram}, ShardDistributorAssignLoopNumRebalancedShards: {metricName: "shard_distributor_shard_assign_reassigned_shards", metricType: Gauge}, ShardDistributorAssignLoopAttempts: {metricName: "shard_distrubutor_shard_assign_attempt", metricType: Counter}, ShardDistributorAssignLoopSuccess: {metricName: "shard_distrubutor_shard_assign_success", metricType: Counter}, ShardDistributorAssignLoopFail: {metricName: "shard_distrubutor_shard_assign_fail", metricType: Counter}, ShardDistributorActiveShards: {metricName: "shard_distributor_active_shards", metricType: Gauge}, ShardDistributorTotalExecutors: {metricName: "shard_distributor_total_executors", metricType: Gauge}, ShardDistributorOldestExecutorHeartbeatLag: {metricName: "shard_distributor_oldest_executor_heartbeat_lag", metricType: Gauge}, ShardDistributorStoreExecutorNotFound: {metricName: "shard_distributor_store_executor_not_found", metricType: Counter}, ShardDistributorStoreFailuresPerNamespace: {metricName: "shard_distributor_store_failures_per_namespace", metricType: Counter}, ShardDistributorStoreRequestsPerNamespace: {metricName: "shard_distributor_store_requests_per_namespace", metricType: Counter}, ShardDistributorStoreLatencyHistogramPerNamespace: {metricName: "shard_distributor_store_latency_histogram_per_namespace", metricType: Histogram, buckets: ShardDistributorExecutorStoreLatencyBuckets}, ShardDistributorShardAssignmentDistributionLatency: {metricName: "shard_distributor_shard_assignment_distribution_latency", metricType: Histogram, buckets: ShardDistributorShardAssignmentLatencyBuckets}, ShardDistributorShardHandoverLatency: {metricName: "shard_distributor_shard_handover_latency", metricType: Histogram, buckets: ShardDistributorShardAssignmentLatencyBuckets}, ShardDistributorWatchProcessingLatency: {metricName: "shard_distributor_watch_processing_latency", metricType: Histogram, buckets: Default1ms100s.buckets()}, ShardDistributorWatchEventsReceived: {metricName: "shard_distributor_watch_events_received", metricType: Counter}, }, } var ( // PersistenceLatencyBuckets contains duration buckets for measuring persistence latency PersistenceLatencyBuckets = tally.DurationBuckets([]time.Duration{ 1 * time.Millisecond, 2 * time.Millisecond, 3 * time.Millisecond, 4 * time.Millisecond, 5 * time.Millisecond, 6 * time.Millisecond, 7 * time.Millisecond, 8 * time.Millisecond, 9 * time.Millisecond, 10 * time.Millisecond, 12 * time.Millisecond, 15 * time.Millisecond, 17 * time.Millisecond, 20 * time.Millisecond, 25 * time.Millisecond, 30 * time.Millisecond, 35 * time.Millisecond, 40 * time.Millisecond, 50 * time.Millisecond, 60 * time.Millisecond, 70 * time.Millisecond, 80 * time.Millisecond, 90 * time.Millisecond, 100 * time.Millisecond, 120 * time.Millisecond, 150 * time.Millisecond, 170 * time.Millisecond, 200 * time.Millisecond, 250 * time.Millisecond, 300 * time.Millisecond, 400 * time.Millisecond, 500 * time.Millisecond, 600 * time.Millisecond, 700 * time.Millisecond, 800 * time.Millisecond, 900 * time.Millisecond, 1 * time.Second, 2 * time.Second, 3 * time.Second, 4 * time.Second, 5 * time.Second, 6 * time.Second, 7 * time.Second, 8 * time.Second, 9 * time.Second, 10 * time.Second, 12 * time.Second, 15 * time.Second, 20 * time.Second, 25 * time.Second, 30 * time.Second, 35 * time.Second, 40 * time.Second, 50 * time.Second, 60 * time.Second, }) ShardDistributorExecutorStoreLatencyBuckets = tally.DurationBuckets([]time.Duration{ 0, 5 * time.Millisecond, 10 * time.Millisecond, 25 * time.Millisecond, 50 * time.Millisecond, 75 * time.Millisecond, 100 * time.Millisecond, 120 * time.Millisecond, 150 * time.Millisecond, 170 * time.Millisecond, 200 * time.Millisecond, 250 * time.Millisecond, 300 * time.Millisecond, 400 * time.Millisecond, 500 * time.Millisecond, 600 * time.Millisecond, 700 * time.Millisecond, 800 * time.Millisecond, 900 * time.Millisecond, 1 * time.Second, 2 * time.Second, 3 * time.Second, 4 * time.Second, 5 * time.Second, 6 * time.Second, 7 * time.Second, 8 * time.Second, 9 * time.Second, 10 * time.Second, 12 * time.Second, 15 * time.Second, 20 * time.Second, 25 * time.Second, 30 * time.Second, 35 * time.Second, 40 * time.Second, 50 * time.Second, 60 * time.Second, }) ShardDistributorShardAssignmentLatencyBuckets = tally.DurationBuckets([]time.Duration{ // ShardDistributorShardHandoverLatency for GracefulHandoverType should be within 0s and 1s 0, 50 * time.Millisecond, 100 * time.Millisecond, 200 * time.Millisecond, 300 * time.Millisecond, 400 * time.Millisecond, 500 * time.Millisecond, 600 * time.Millisecond, 700 * time.Millisecond, 800 * time.Millisecond, 900 * time.Millisecond, // ShardDistributorShardHandoverLatency for EmergencyHandoverType should be within 0s and 10s 1 * time.Second, 2 * time.Second, 3 * time.Second, 4 * time.Second, 5 * time.Second, 6 * time.Second, 7 * time.Second, 8 * time.Second, 9 * time.Second, 10 * time.Second, 12 * time.Second, 15 * time.Second, 20 * time.Second, 30 * time.Second, 45 * time.Second, 1 * time.Minute, 2 * time.Minute, 5 * time.Minute, 10 * time.Minute, }) // ReplicationTaskDelayBucket contains buckets for replication task delay ReplicationTaskDelayBucket = tally.DurationBuckets([]time.Duration{ 0 * time.Second, // zero value is needed for the first bucket 1 * time.Second, 10 * time.Second, 1 * time.Minute, 5 * time.Minute, 10 * time.Minute, 30 * time.Minute, 1 * time.Hour, 2 * time.Hour, 6 * time.Hour, 12 * time.Hour, 24 * time.Hour, 36 * time.Hour, 48 * time.Hour, 72 * time.Hour, 96 * time.Hour, 120 * time.Hour, 144 * time.Hour, 168 * time.Hour, // one week }) HistoryTaskLatencyBuckets = tally.DurationBuckets([]time.Duration{ 1 * time.Millisecond, 5 * time.Millisecond, 10 * time.Millisecond, 20 * time.Millisecond, 50 * time.Millisecond, 100 * time.Millisecond, 200 * time.Millisecond, 250 * time.Millisecond, 500 * time.Millisecond, 1 * time.Second, 2 * time.Second, 5 * time.Second, 10 * time.Second, 30 * time.Second, 1 * time.Minute, 5 * time.Minute, 10 * time.Minute, 1 * time.Hour, }) ) // GlobalRatelimiterUsageHistogram contains buckets for tracking how many ratelimiters are // in which various states (startup, healthy, failing, as well as aggregator-side quantities, deleted, etc). // // this is intended for coarse scale checking, not alerting, so the buckets // should be considered unstable and can be changed whenever desired. var GlobalRatelimiterUsageHistogram = append( tally.ValueBuckets{0}, // need an explicit 0 or zero is reported as 1 tally.MustMakeExponentialValueBuckets(1, 2, 17)..., // 1..65536 ) // ResponseRowSizeBuckets contains buckets for tracking how many rows are returned per persistence operation var ResponseRowSizeBuckets = append( tally.ValueBuckets{0}, // need an explicit 0 or zero is reported as 1 tally.MustMakeExponentialValueBuckets(1, 2, 17)..., // 1..65536 ) // TaskCountBuckets contains buckets for tracking task counts that can reach into the millions, // such as replication lag measured as task ID distance (2^20 = 1,048,576). var TaskCountBuckets = append( tally.ValueBuckets{0}, // need an explicit 0 or zero is reported as 1 tally.MustMakeExponentialValueBuckets(1, 2, 21)..., // 1..1048576 ) // DomainCacheUpdateBuckets contain metric results for domain update operations var DomainCacheUpdateBuckets = append( tally.ValueBuckets{0}, // need an explicit 0 or zero is reported as 1 tally.MustMakeExponentialValueBuckets(1, 2, 17)..., // 1..65536 ) // ResponsePayloadSizeBuckets contains buckets for tracking the size of the payload returned per persistence operation var ResponsePayloadSizeBuckets = append( tally.ValueBuckets{0}, // need an explicit 0 or zero is reported as 1 tally.MustMakeExponentialValueBuckets(1024, 2, 20)..., // 1kB..1GB ) // ExponentialDurationBuckets is a set of exponential duration buckets var ExponentialDurationBuckets = func() tally.DurationBuckets { // generate 79 buckets, starting from 1ms, with a factor of 2^0.25 buckets, err := tally.ExponentialDurationBuckets(1*time.Millisecond, math.Pow(2, 0.25), 79) if err != nil { panic(err) } // add a 0 bucket to the beginning buckets = append([]time.Duration{0}, buckets...) return buckets }() // ErrorClass is an enum to help with classifying SLA vs. non-SLA errors (SLA = "service level agreement") type ErrorClass uint8 const ( // NoError indicates that there is no error (error should be nil) NoError = ErrorClass(iota) // UserError indicates that this is NOT an SLA-reportable error UserError // InternalError indicates that this is an SLA-reportable error InternalError ) // Empty returns true if the metricName is an empty string func (mn MetricName) Empty() bool { return mn == "" } // String returns string representation of this metric name func (mn MetricName) String() string { return string(mn) } ================================================ FILE: common/metrics/defs_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import ( "fmt" "math" "regexp" "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var IsMetric = regexp.MustCompile(`^[a-z][a-z_]*$`).MatchString func TestScopeDefsMapped(t *testing.T) { for i := PersistenceCreateShardScope; i < NumCommonScopes; i++ { key, ok := ScopeDefs[Common][i] require.True(t, ok) require.NotEmpty(t, key) for tag := range key.tags { assert.True(t, IsMetric(tag), "metric tags should conform to regex") } } for i := AdminDescribeHistoryHostScope; i < NumAdminScopes; i++ { key, ok := ScopeDefs[Frontend][i] require.True(t, ok) require.NotEmpty(t, key) for tag := range key.tags { assert.True(t, IsMetric(tag), "metric tags should conform to regex") } } for i := FrontendStartWorkflowExecutionScope; i < NumFrontendScopes; i++ { key, ok := ScopeDefs[Frontend][i] require.True(t, ok) require.NotEmpty(t, key) for tag := range key.tags { assert.True(t, IsMetric(tag), "metric tags should conform to regex") } } for i := HistoryStartWorkflowExecutionScope; i < NumHistoryScopes; i++ { key, ok := ScopeDefs[History][i] require.True(t, ok) require.NotEmpty(t, key) for tag := range key.tags { assert.True(t, IsMetric(tag), "metric tags should conform to regex") } } for i := MatchingPollForDecisionTaskScope; i < NumMatchingScopes; i++ { key, ok := ScopeDefs[Matching][i] require.True(t, ok) require.NotEmpty(t, key) for tag := range key.tags { assert.True(t, IsMetric(tag), "metric tags should conform to regex") } } for i := ReplicatorScope; i < NumWorkerScopes; i++ { key, ok := ScopeDefs[Worker][i] require.True(t, ok) require.NotEmpty(t, key) for tag := range key.tags { assert.True(t, IsMetric(tag), "metric tags should conform to regex") } } for i := ShardDistributorGetShardOwnerScope; i < NumShardDistributorScopes; i++ { key, ok := ScopeDefs[ShardDistributor][i] require.True(t, ok) require.NotEmpty(t, key) for tag := range key.tags { assert.True(t, IsMetric(tag), "metric tags should conform to regex") } } } func TestMetricDefsMapped(t *testing.T) { for i := CadenceRequests; i < NumCommonMetrics; i++ { key, ok := MetricDefs[Common][i] require.True(t, ok) require.NotEmpty(t, key) } for i := TaskRequests; i < NumHistoryMetrics; i++ { key, ok := MetricDefs[History][i] require.True(t, ok) require.NotEmpty(t, key) } for i := PollSuccessPerTaskListCounter; i < NumMatchingMetrics; i++ { key, ok := MetricDefs[Matching][i] require.True(t, ok) require.NotEmpty(t, key) } for i := ReplicatorMessages; i < NumWorkerMetrics; i++ { key, ok := MetricDefs[Worker][i] require.True(t, ok) require.NotEmpty(t, key) } for i := ShardDistributorRequests; i < NumShardDistributorMetrics; i++ { key, ok := MetricDefs[ShardDistributor][i] require.True(t, ok) require.NotEmpty(t, key) } } func TestMetricDefs(t *testing.T) { for service, metrics := range MetricDefs { for _, metricDef := range metrics { matched := IsMetric(string(metricDef.metricName)) assert.True(t, matched, fmt.Sprintf("Service: %v, metric_name: %v", service, metricDef.metricName)) } } } // "index -> operation" must be unique so they can be looked up without needing to know the service ID. // // Duplicate indexes with the same operation name are technically fine, but there doesn't seem to be any benefit in allowing it, // and it trivially ensures that all values have only one operation name. func TestOperationIndexesAreUnique(t *testing.T) { seen := make(map[ScopeIdx]bool) for serviceIdx, serviceOps := range ScopeDefs { for idx := range serviceOps { if seen[idx] { t.Error("duplicate operation index:", idx, "with name:", serviceOps[idx].operation, "in service:", serviceIdx) } seen[idx] = true // to serve as documentation: operations are NOT unique due to dups across services. // this is probably fine, they're just used as metric tag values. // but not being able to prevent dups means it's possible we have unintentional name collisions. // // name := serviceOps[idx].operation // if seenNames[name] { // t.Error("duplicate operation string:", name, "at different indexes") // } // seenNames[name] = true } } } func TestMetricsAreUnique(t *testing.T) { // Duplicate indexes is arguably fine, but there doesn't seem to be any benefit in allowing it. t.Run("indexes", func(t *testing.T) { seen := make(map[MetricIdx]string) for _, serviceMetrics := range MetricDefs { for idx, met := range serviceMetrics { if prev, ok := seen[idx]; ok { t.Errorf("duplicate metric index %d, with names %q and %q", idx, met.metricName.String(), prev) } seen[idx] = met.metricName.String() } } }) // Duplicate names carry a high risk of causing different-tag collisions in Prometheus, and should not be allowed. t.Run("names", func(t *testing.T) { seen := make(map[string]bool) for _, serviceMetrics := range MetricDefs { for _, met := range serviceMetrics { name := met.metricName.String() if seen[name] { switch name { case "cache_full", "cache_miss", "cache_hit", "cadence_requests_per_tl", "cross_cluster_fetch_errors": continue // known dup. worth changing as some cause problems. } t.Errorf("duplicate metric name %q", name) } seen[name] = true } } }) } func TestHistogramSuffixes(t *testing.T) { for _, serviceMetrics := range MetricDefs { for _, def := range serviceMetrics { if def.intExponentialBuckets != nil { if !strings.HasSuffix(def.metricName.String(), "_counts") { t.Errorf("int-exponential-histogram metric %q should have suffix \"_counts\"", def.metricName.String()) } } if def.exponentialBuckets != nil { if !strings.HasSuffix(def.metricName.String(), "_ns") { t.Errorf("exponential-histogram metric %q should have suffix \"_ns\"", def.metricName.String()) } } } } } func TestExponentialDurationBuckets(t *testing.T) { factor := math.Pow(2, 0.25) assert.Equal(t, 80, len(ExponentialDurationBuckets)) assert.Equal(t, 0*time.Millisecond, ExponentialDurationBuckets[0], "bucket[0] mismatch") assert.Equal(t, 1*time.Millisecond, ExponentialDurationBuckets[1], "bucket[1] mismatch") assert.InDelta(t, bucketVal(1*time.Millisecond, factor, 2), ExponentialDurationBuckets[2], float64(time.Millisecond), "bucket[2] mismatch") assert.InDelta(t, bucketVal(1*time.Millisecond, factor, 79), ExponentialDurationBuckets[79], 0.1*float64(time.Second), "bucket[79] mismatch") } func bucketVal(start time.Duration, factor float64, bucket int) time.Duration { if bucket == 0 { return 0 } return time.Duration(math.Pow(factor, float64(bucket-1)) * float64(start)) } ================================================ FILE: common/metrics/histograms.go ================================================ package metrics import ( "fmt" "math" "slices" "strconv" "time" "github.com/uber-go/tally" ) // Nearly all histograms should use pre-defined buckets here, to encourage consistency. // // When creating a new one, make sure to read makeSubsettableHistogram for context, and choose // values that communicate your *intent*, not the actual final value. Add a test to show the // resulting buckets, so reviewers can see just how much / how little you chose. // // Similarly, name them more by their intent than their exact ranges, because "it has just barely // enough buckets" is almost always not *actually* enough when things misbehave. // Choosing by intent also helps make adjusting these values safer if needed (e.g. subsetting or // reducing the range), because we can be sure all intents match. var ( // Default1ms100s is our "default" set of buckets, targeting at least 1ms through 100s, // and is "rounded up" slightly to reach 80 buckets (100s needs 68 buckets), // plus multi-minute exceptions are common enough to support for the small additional cost // (this goes up to ~15m). // // That makes this *relatively* costly, but hopefully good enough for most metrics without // needing any careful thought. If this proves incorrect, it will be down-scaled without // checking existing uses, so make sure to use something else if you require a certain level // of precision. // // If you need sub-millisecond precision, or substantially longer durations than about 1 minute, // consider using a different histogram. Default1ms100s = makeSubsettableHistogram(2, time.Millisecond, 100*time.Second, 80) // Low1ms100s is a half-resolution version of Default1ms100s, intended for use any time the default // is expected to be more costly than we feel comfortable with. Or for automatic down-scaling // at runtime via dynamic config, if that's ever built. // // This uses only 40 buckets, so it's likely good enough for most purposes, e.g. database operations // that might have high cardinality but should not exceed about a minute even in exceptional cases. // Reducing this to "1ms to 10s" seems reasonable for some cases, but that still needs ~32 buckets, // which isn't a big savings and reduces our ability to notice abnormally slow events. Low1ms100s = Default1ms100s.subsetTo(1) // High1ms24h covers things like activity latency, where ranges are very large but // "longer than 1 day" is not particularly worth being precise about. // // This uses a lot of buckets, so it must be used with relatively-low cardinality elsewhere, // and/or consider dual emitting (Mid1ms24h or lower) so overviews can be queried efficiently. High1ms24h = makeSubsettableHistogram(2, time.Millisecond, 24*time.Hour, 112) // Mid1ms24h is a one-scale-lower version of High1ms24h, // for use when we know it's too detailed to be worth emitting. // // This uses 56 buckets, half of High1ms24h's 112 Mid1ms24h = High1ms24h.subsetTo(1) // Mid1To16k is a histogram for small counters, like "how many replication tasks did we receive". // // This targets single-digits through ~10k with some buffer, and ends well below 1M. // // Note: we intentionally start at 8 (not 1). Starting at 1 causes duplicate bucket boundaries // due to float->duration truncation, which Prometheus rejects. Mid1To16k = IntSubsettableHistogram(makeSubsettableHistogram(2, 8, 16384, 64)) ) // SubsettableHistogram is a duration-based histogram that can be subset to a lower scale // in a standardized, predictable way. It is intentionally compatible with Prometheus and OTEL's // "exponential histograms": https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram // // These histograms MUST always have a "_ns" suffix in their name to avoid confusion with timers. type SubsettableHistogram struct { tallyBuckets tally.DurationBuckets scale int } // IntSubsettableHistogram is a non-duration-based integer-distribution histogram, otherwise identical // to SubsettableHistogram but built as a separate type so you cannot pass the wrong one. // // These histograms MUST always have a "_counts" suffix in their name to avoid confusion with timers. // (more suffixes will likely be allowed as needed) type IntSubsettableHistogram SubsettableHistogram // currently we have no apparent need for float-histograms, // as all our value ranges go from >=1 to many thousands, where // decimal precision is pointless and mostly just looks bad. // // if we ever need 0..1 precision in histograms, we can add them then. // type FloatSubsettableHistogram struct { // tally.ValueBuckets // scale int // } // subsetTo takes an existing histogram and reduces its detail level. // // this can be replaced by simply creating a new histogram with the same args // as the original but half the length, but it's added because: 1) it works too, // and 2) to document how the process works, because we'll likely be doing this // at query time to reduce the level of detail in small / over-crowded graphs. func (s SubsettableHistogram) subsetTo(newScale int) SubsettableHistogram { if newScale >= s.scale { panic(fmt.Sprintf("scale %v is not less than the current scale %v", newScale, s.scale)) } if newScale < 0 { panic(fmt.Sprintf("negative scales (%v == greater than *2 per step) are possible, but do not have tests yet", newScale)) } dup := SubsettableHistogram{ tallyBuckets: slices.Clone(s.tallyBuckets), scale: s.scale, } // compress every other bucket per -1 scale for dup.scale > newScale { if (len(dup.tallyBuckets)-2)%2 != 0 { // -2 for 0 and last-2^N panic(fmt.Sprintf( "cannot subset from scale %v to %v, %v-buckets is not divisible by 2", dup.scale, dup.scale-1, len(dup.tallyBuckets)-2)) } if len(dup.tallyBuckets) <= 3 { // at 3 buckets, there's just 0, start, end. // // this is well past the point of being useful, // and it means we might try to slice `[1:0]` or similar. panic(fmt.Sprintf( "not enough buckets to subset from scale %d to %d, only have %d: %v", dup.scale, dup.scale-1, len(dup.tallyBuckets), dup.tallyBuckets)) } // the first and last buckets will be kept, the rest will lose half per scale bucketsToCompress := dup.tallyBuckets[1 : len(dup.tallyBuckets)-2] // trim half := make(tally.DurationBuckets, 0, (len(bucketsToCompress)/2)+2) half = append(half, dup.tallyBuckets[0]) // keep the zero value intact for i := 0; i < len(bucketsToCompress); i += 2 { half = append(half, bucketsToCompress[i]) // keep the first, third, etc } half = append(half, dup.tallyBuckets[len(dup.tallyBuckets)-1]) // keep the last 2^N intact dup.tallyBuckets = half dup.scale-- } return dup } func (i IntSubsettableHistogram) subsetTo(newScale int) IntSubsettableHistogram { return IntSubsettableHistogram(SubsettableHistogram(i).subsetTo(newScale)) } func (s SubsettableHistogram) tags() map[string]string { return map[string]string{ // use the duration string func, as "1ms" duration suffix is clearer than "1000000" for nanoseconds. // this also helps differentiate "tracks time" from "may be a general value distribution", // both visually when querying by hand and for any future automation (if needed). "histogram_start": s.start().String(), "histogram_end": s.end().String(), "histogram_scale": strconv.Itoa(s.scale), } } func (i IntSubsettableHistogram) tags() map[string]string { return map[string]string{ "histogram_start": strconv.Itoa(int(i.start())), "histogram_end": strconv.Itoa(int(i.end())), "histogram_scale": strconv.Itoa(i.scale), } } // makeSubsettableHistogram is a replacement for tally.MustMakeExponentialDurationBuckets, // tailored to make "construct a range" or "ensure N buckets" simpler for OTEL-compatible exponential histograms // (i.e. https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram). // // It ensures values are as precise as possible (preventing display-space-wasteful numbers like 3.99996ms), // that all histograms start with a 0 value (else erroneous negative values are impossible to notice), // and avoids some bugs with low values (tally.MustMakeExponentialDurationBuckets misbehaves for small numbers, // causing all buckets to == the minimum value). // // # To use // // Choose scale/start/end values that match your *intent*, i.e. the range you want to capture, and then choose // a length that EXCEEDS that by at least 2x, ideally 4x-10x, and ends at a highly-divisible-by-2 value. // // Length should ideally be a highly-divisible-by-2 value, to keep subsetting "clean" as long as possible, // i.e. the ranges of values in each bucket stays the same rather than having the second-to-last one cover // a smaller range. // // This length will be padded internally to add both a zero value and a next-power-of-2 value, to help keep // negative values identifiable, and keep the upper limit unchanged across all scales (as it cannot cleanly // subset, because it holds an infinite range of values). // // The exact length / exceeding / etc does not matter (just write a test so it's documented and can be reviewed), // it just serves to document your intent and to make sure that we have some head-room so we can understand how // wrongly we guessed at our needs if it's exceeded during an incident of some kind. We've failed at this // multiple times in the past, it's worth paying a bit extra to be able to diagnose problems. // // For all histograms produced, add a test to print the concrete values, so they can be quickly checked when reading. func makeSubsettableHistogram(scale int, start, end time.Duration, length int) SubsettableHistogram { if scale < 0 || scale > 3 { // anything outside this range is currently not expected and probably a mistake, // but any value is technically sound. panic(fmt.Sprintf("scale must be between 0 (grows by *2) and 3 (grows by *2^1/8), got scale: %v", scale)) } if start <= 0 { panic(fmt.Sprintf("start must be greater than 0 or it will not grow exponentially, got %v", start)) } if start >= end { panic(fmt.Sprintf("start must be less than end (%v < %v)", start, end)) } if length < 12 || length > 160 { // 160 is probably higher than we should consider, but going further is currently risking much too high costs. // if this changes, e.g. due to metrics optimizations, just adjust this validation. // // 12 is pretty arbitrary, I just don't expect us to ever make a histogram that small, so it's probably // from accidentally passing scale or something to the wrong argument. panic(fmt.Sprintf("length must be between 12 < %d <=160", length)) } // make sure the number of buckets completes a "full" row, // to which we will add one more to reach the next start*2^N value. // // for a more visual example of what this means, see the logged strings in tests: // each "row" of values must be the same width, and it must have a single value in the last row. // // adding a couple buckets to ensure this is met costs very little, and ensures // subsetting combines values more consistently (not crossing rows) for longer. powerOfTwoWidth := int(math.Pow(2, float64(scale))) // num of buckets needed to double a value missing := length % powerOfTwoWidth if missing != 0 { panic(fmt.Sprintf(`number of buckets must end at a power of 2 of the starting value. `+ `got %d, probably raise to %d`, length, length+missing, )) } var buckets tally.DurationBuckets for i := 0; i < length; i++ { buckets = append(buckets, nextBucket(start, len(buckets), scale)) } // the loop above has "filled" a full row, we need one more to reach the next power of 2. buckets = append(buckets, nextBucket(start, len(buckets), scale)) if last(buckets) < end*2 { panic(fmt.Sprintf("not enough buckets (%d) to exceed the end target (%v) by at least 2x. "+ "you are STRONGLY encouraged to include ~2x-10x more range than your intended end value, "+ "preferably 4x+, because we almost always underestimate the ranges needed during incidents", length, last(buckets)), ) } return SubsettableHistogram{ tallyBuckets: append( // always include a zero value at the beginning, so negative values are noticeable ("-inf to 0" bucket) tally.DurationBuckets{0}, buckets..., ), scale: scale, } } // last-item-in-slice helper to eliminate some magic `-1`s func last[T any, X ~[]T](s X) T { return s[len(s)-1] } func nextBucket(start time.Duration, num int, scale int) time.Duration { // calculating it from `start` each time reduces floating point error, ensuring "clean" multiples // at every power of 2 (and possibly others), instead of e.g. "1ms ... 1.9999994ms" which occurs // if you try to build from the previous value each time. return time.Duration( float64(start) * math.Pow(2, float64(num)/math.Pow(2, float64(scale)))) } func (s SubsettableHistogram) histScale() int { return s.scale } func (s SubsettableHistogram) width() int { return int(math.Pow(2, float64(s.scale))) } func (s SubsettableHistogram) len() int { return len(s.tallyBuckets) } func (s SubsettableHistogram) start() time.Duration { return s.tallyBuckets[1] } func (s SubsettableHistogram) end() time.Duration { return s.tallyBuckets[len(s.tallyBuckets)-1] } func (s SubsettableHistogram) buckets() tally.DurationBuckets { return s.tallyBuckets } func (s SubsettableHistogram) print(to func(string, ...any)) { to("%v\n", s.tallyBuckets[0:1]) // zero value on its own row for rowStart := 1; rowStart < s.len(); rowStart += s.width() { to("%v\n", s.tallyBuckets[rowStart:min(rowStart+s.width(), len(s.tallyBuckets))]) } } func (i IntSubsettableHistogram) histScale() int { return i.scale } func (i IntSubsettableHistogram) width() int { return int(math.Pow(2, float64(i.scale))) } func (i IntSubsettableHistogram) len() int { return len(i.tallyBuckets) } func (i IntSubsettableHistogram) start() time.Duration { return i.tallyBuckets[1] } func (i IntSubsettableHistogram) end() time.Duration { return i.tallyBuckets[len(i.tallyBuckets)-1] } func (i IntSubsettableHistogram) buckets() tally.DurationBuckets { return i.tallyBuckets } func (i IntSubsettableHistogram) print(to func(string, ...any)) { // fairly unreadable as duration-strings, so convert to int by hand to("[%d]\n", int(i.tallyBuckets[0])) // zero value on its own row for rowStart := 1; rowStart < i.len(); rowStart += i.width() { ints := make([]int, 0, i.width()) for _, d := range i.tallyBuckets[rowStart:min(rowStart+i.width(), len(i.tallyBuckets))] { ints = append(ints, int(d)) } to("%v\n", ints) } } var _ histogrammy[SubsettableHistogram] = SubsettableHistogram{} var _ histogrammy[IntSubsettableHistogram] = IntSubsettableHistogram{} // internal utility/test methods, but could be exposed if there's a use for it type histogrammy[T any] interface { histScale() int // exponential scale value. 0..3 inclusive. width() int // number of values per power of 2 == how wide to print each row. 1, 2, 4, or 8. len() int // number of buckets start() time.Duration // first non-zero bucket end() time.Duration // last bucket buckets() tally.DurationBuckets // access to all buckets subsetTo(newScale int) T // generic so specific types can be returned tags() map[string]string // start, end, and scale tags that need to be implicitly added print(to func(string, ...any)) // test-oriented printer } ================================================ FILE: common/metrics/histograms_test.go ================================================ package metrics import ( "fmt" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestHistogramValues(t *testing.T) { t.Run("default_1ms_to_100s", func(t *testing.T) { checkHistogram(t, Default1ms100s, ` [0s] [1ms 1.189207ms 1.414213ms 1.681792ms] [2ms 2.378414ms 2.828427ms 3.363585ms] [4ms 4.756828ms 5.656854ms 6.727171ms] [8ms 9.513656ms 11.313708ms 13.454342ms] [16ms 19.027313ms 22.627416ms 26.908685ms] [32ms 38.054627ms 45.254833ms 53.81737ms] [64ms 76.109255ms 90.509667ms 107.634741ms] [128ms 152.21851ms 181.019335ms 215.269482ms] [256ms 304.437021ms 362.038671ms 430.538964ms] [512ms 608.874042ms 724.077343ms 861.077929ms] [1.024s 1.217748085s 1.448154687s 1.722155858s] [2.048s 2.435496171s 2.896309375s 3.444311716s] [4.096s 4.870992343s 5.792618751s 6.888623433s] [8.192s 9.741984686s 11.585237502s 13.777246867s] [16.384s 19.483969372s 23.170475005s 27.554493735s] [32.768s 38.967938744s 46.340950011s 55.10898747s] [1m5.536s 1m17.935877488s 1m32.681900023s 1m50.21797494s] [2m11.072s 2m35.871754977s 3m5.363800047s 3m40.43594988s] [4m22.144s 5m11.743509955s 6m10.727600094s 7m20.87189976s] [8m44.288s 10m23.48701991s 12m21.455200189s 14m41.743799521s] [17m28.576s] `) }) t.Run("low_1ms_to_100s", func(t *testing.T) { checkHistogram(t, Low1ms100s, ` [0s] [1ms 1.414213ms] [2ms 2.828427ms] [4ms 5.656854ms] [8ms 11.313708ms] [16ms 22.627416ms] [32ms 45.254833ms] [64ms 90.509667ms] [128ms 181.019335ms] [256ms 362.038671ms] [512ms 724.077343ms] [1.024s 1.448154687s] [2.048s 2.896309375s] [4.096s 5.792618751s] [8.192s 11.585237502s] [16.384s 23.170475005s] [32.768s 46.340950011s] [1m5.536s 1m32.681900023s] [2m11.072s 3m5.363800047s] [4m22.144s 6m10.727600094s] [8m44.288s 12m21.455200189s] [17m28.576s] `) }) t.Run("high_1ms_to_24h", func(t *testing.T) { checkHistogram(t, High1ms24h, ` [0s] [1ms 1.189207ms 1.414213ms 1.681792ms] [2ms 2.378414ms 2.828427ms 3.363585ms] [4ms 4.756828ms 5.656854ms 6.727171ms] [8ms 9.513656ms 11.313708ms 13.454342ms] [16ms 19.027313ms 22.627416ms 26.908685ms] [32ms 38.054627ms 45.254833ms 53.81737ms] [64ms 76.109255ms 90.509667ms 107.634741ms] [128ms 152.21851ms 181.019335ms 215.269482ms] [256ms 304.437021ms 362.038671ms 430.538964ms] [512ms 608.874042ms 724.077343ms 861.077929ms] [1.024s 1.217748085s 1.448154687s 1.722155858s] [2.048s 2.435496171s 2.896309375s 3.444311716s] [4.096s 4.870992343s 5.792618751s 6.888623433s] [8.192s 9.741984686s 11.585237502s 13.777246867s] [16.384s 19.483969372s 23.170475005s 27.554493735s] [32.768s 38.967938744s 46.340950011s 55.10898747s] [1m5.536s 1m17.935877488s 1m32.681900023s 1m50.21797494s] [2m11.072s 2m35.871754977s 3m5.363800047s 3m40.43594988s] [4m22.144s 5m11.743509955s 6m10.727600094s 7m20.87189976s] [8m44.288s 10m23.48701991s 12m21.455200189s 14m41.743799521s] [17m28.576s 20m46.974039821s 24m42.910400378s 29m23.487599042s] [34m57.152s 41m33.948079642s 49m25.820800757s 58m46.975198084s] [1h9m54.304s 1h23m7.896159284s 1h38m51.641601515s 1h57m33.950396168s] [2h19m48.608s 2h46m15.792318568s 3h17m43.283203031s 3h55m7.900792337s] [4h39m37.216s 5h32m31.584637137s 6h35m26.566406062s 7h50m15.801584674s] [9h19m14.432s 11h5m3.169274274s 13h10m53.132812125s 15h40m31.603169349s] [18h38m28.864s 22h10m6.338548549s 26h21m46.265624251s 31h21m3.206338698s] [37h16m57.728s 44h20m12.677097099s 52h43m32.531248503s 62h42m6.412677396s] [74h33m55.456s] `) }) t.Run("mid_1ms_24h", func(t *testing.T) { checkHistogram(t, Mid1ms24h, ` [0s] [1ms 1.414213ms] [2ms 2.828427ms] [4ms 5.656854ms] [8ms 11.313708ms] [16ms 22.627416ms] [32ms 45.254833ms] [64ms 90.509667ms] [128ms 181.019335ms] [256ms 362.038671ms] [512ms 724.077343ms] [1.024s 1.448154687s] [2.048s 2.896309375s] [4.096s 5.792618751s] [8.192s 11.585237502s] [16.384s 23.170475005s] [32.768s 46.340950011s] [1m5.536s 1m32.681900023s] [2m11.072s 3m5.363800047s] [4m22.144s 6m10.727600094s] [8m44.288s 12m21.455200189s] [17m28.576s 24m42.910400378s] [34m57.152s 49m25.820800757s] [1h9m54.304s 1h38m51.641601515s] [2h19m48.608s 3h17m43.283203031s] [4h39m37.216s 6h35m26.566406062s] [9h19m14.432s 13h10m53.132812125s] [18h38m28.864s 26h21m46.265624251s] [37h16m57.728s 52h43m32.531248503s] [74h33m55.456s] `) }) t.Run("mid_to_16k_ints", func(t *testing.T) { checkHistogram(t, Mid1To16k, ` [0] [8 9 11 13] [16 19 22 26] [32 38 45 53] [64 76 90 107] [128 152 181 215] [256 304 362 430] [512 608 724 861] [1024 1217 1448 1722] [2048 2435 2896 3444] [4096 4870 5792 6888] [8192 9741 11585 13777] [16384 19483 23170 27554] [32768 38967 46340 55108] [65536 77935 92681 110217] [131072 155871 185363 220435] [262144 311743 370727 440871] [524288] `) }) } // most histograms should pass this check, but fuzzy comparison is fine if needed for extreme cases. func checkHistogram[T any](t *testing.T, h histogrammy[T], expected string) { var buf strings.Builder h.print(func(s string, a ...any) { str := fmt.Sprintf(s, a...) t.Logf(str) buf.WriteString(str) }) if strings.TrimSpace(expected) != strings.TrimSpace(buf.String()) { t.Error("histogram definition changed, update the test if this is intended") } buckets := h.buckets() assert.EqualValues(t, 0, buckets[0], "first bucket should always be zero") for i := 1; i < len(buckets); i += h.width() { if i > 1 { // ensure good float math. // // this is *intentionally* doing exact comparisons, as floating point math with // human-friendly integers have precise power-of-2 multiples for a very long time. // // note that the equivalent tally buckets, e.g.: // tally.MustMakeExponentialDurationBuckets(time.Millisecond, math.Pow(2, 1.0/4.0)) // fails this test, and the logs produced show ugly e.g. 31.999942ms values. // tally also produces incorrect results if you start at e.g. 1, // as it just produces endless `1` values. assert.Equalf(t, buckets[i-h.width()]*2, buckets[i], "current row's value (%v) is not a power of 2 greater than previous (%v), skewed / bad math?", buckets[i-h.width()], buckets[i]) } } } ================================================ FILE: common/metrics/interfaces.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import ( "time" "github.com/uber-go/tally" ) type ( // Client is the interface used to report metrics tally. Client interface { // IncCounter increments a counter metric IncCounter(scope ScopeIdx, counter MetricIdx) // AddCounter adds delta to the counter metric AddCounter(scope ScopeIdx, counter MetricIdx, delta int64) // StartTimer starts a timer for the given // metric name. Time will be recorded when stopwatch is stopped. StartTimer(scope ScopeIdx, timer MetricIdx) tally.Stopwatch // RecordTimer starts a timer for the given // metric name RecordTimer(scope ScopeIdx, timer MetricIdx, d time.Duration) // RecordHistogramDuration records a histogram duration value for the given // metric name RecordHistogramDuration(scope ScopeIdx, timer MetricIdx, d time.Duration) // UpdateGauge reports Gauge type absolute value metric UpdateGauge(scope ScopeIdx, gauge MetricIdx, value float64) // Scope return an internal scope that can be used to add additional // information to metrics Scope(scope ScopeIdx, tags ...Tag) Scope } // Scope is an interface for metrics Scope interface { // IncCounter increments a counter metric IncCounter(counter MetricIdx) // AddCounter adds delta to the counter metric AddCounter(counter MetricIdx, delta int64) // StartTimer starts a timer for the given metric name. // Time will be recorded when stopwatch is stopped. StartTimer(timer MetricIdx) Stopwatch // RecordTimer starts a timer for the given metric name RecordTimer(timer MetricIdx, d time.Duration) // RecordHistogramDuration records a histogram duration value for the given // metric name RecordHistogramDuration(timer MetricIdx, d time.Duration) // RecordHistogramValue records a histogram value for the given metric name RecordHistogramValue(timer MetricIdx, value float64) // ExponentialHistogram records a subsettable exponential histogram value for the given metric name ExponentialHistogram(hist MetricIdx, d time.Duration) // IntExponentialHistogram records a subsettable exponential histogram value for the given metric name IntExponentialHistogram(hist MetricIdx, value int) // UpdateGauge reports Gauge type absolute value metric UpdateGauge(gauge MetricIdx, value float64) // Tagged return an internal scope that can be used to add additional // information to metrics Tagged(tags ...Tag) Scope } ) var sanitizer = tally.NewSanitizer(tally.SanitizeOptions{ NameCharacters: tally.ValidCharacters{tally.AlphanumericRange, tally.UnderscoreCharacters}, KeyCharacters: tally.ValidCharacters{tally.AlphanumericRange, tally.UnderscoreCharacters}, ValueCharacters: tally.ValidCharacters{tally.AlphanumericRange, tally.UnderscoreCharacters}, ReplacementCharacter: '_', }) ================================================ FILE: common/metrics/metricsfx/metricsfx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metricsfx import ( "github.com/uber-go/tally" "go.uber.org/fx" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/service" ) // Module provides metrics client for fx application. var Module = fx.Module("metricsfx", fx.Provide(buildClient)) // ModuleForExternalScope provides metrics client for fx application when tally.Scope is created outside. var ModuleForExternalScope = fx.Module("metricsfx", fx.Provide(func(params serviceIdxParams) metrics.ServiceIdx { return service.GetMetricsServiceIdx(params.ServiceFullName, params.Logger) }), fx.Provide(buildClientFromTally)) type clientParams struct { fx.In Logger log.Logger ServiceFullName string `name:"service-full-name"` SvcCfg config.Service HistogramCfg metrics.HistogramMigration } type clientResult struct { fx.Out Scope tally.Scope Client metrics.Client } func buildClient(params clientParams) clientResult { scope := params.SvcCfg.Metrics.NewScope(params.Logger, params.ServiceFullName) return clientResult{ Scope: scope, Client: buildClientFromTally(scope, service.GetMetricsServiceIdx(params.ServiceFullName, params.Logger), params.HistogramCfg), } } type serviceIdxParams struct { fx.In Logger log.Logger ServiceFullName string `name:"service-full-name"` } func buildClientFromTally(scope tally.Scope, serviceID metrics.ServiceIdx, hm metrics.HistogramMigration) metrics.Client { return metrics.NewClient(scope, serviceID, hm) } ================================================ FILE: common/metrics/metricsfx/metricsfx_test.go ================================================ package metricsfx import ( "testing" "github.com/uber-go/tally" "go.uber.org/fx" "go.uber.org/fx/fxtest" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/service" ) func TestModule(t *testing.T) { fxApp := fxtest.New(t, testlogger.Module(t), fx.Provide(fx.Annotated{ Target: func() string { return service.Frontend }, Name: "service-full-name"}, func() config.Service { return config.Service{} }), fx.Provide(func() metrics.HistogramMigration { return metrics.HistogramMigration{} }), Module, fx.Invoke(func(mc metrics.Client) {})) fxApp.RequireStart().RequireStop() } func TestModuleWithExternalScope(t *testing.T) { fxApp := fxtest.New(t, testlogger.Module(t), fx.Provide(func() tally.Scope { return tally.NoopScope }, fx.Annotated{ Target: func() string { return service.Frontend }, Name: "service-full-name"}), fx.Provide(func() metrics.HistogramMigration { return metrics.HistogramMigration{} }), ModuleForExternalScope, fx.Invoke(func(mc metrics.Client) {})) fxApp.RequireStart().RequireStop() } ================================================ FILE: common/metrics/mocks/Client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery 2.7.4. DO NOT EDIT. package mocks import ( time "time" mock "github.com/stretchr/testify/mock" tally "github.com/uber-go/tally" metrics "github.com/uber/cadence/common/metrics" ) // Client is an autogenerated mock type for the Client type type Client struct { mock.Mock } // AddCounter provides a mock function with given fields: scope, counter, delta func (_m *Client) AddCounter(scope metrics.ScopeIdx, counter metrics.MetricIdx, delta int64) { _m.Called(scope, counter, delta) } // IncCounter provides a mock function with given fields: scope, counter func (_m *Client) IncCounter(scope metrics.ScopeIdx, counter metrics.MetricIdx) { _m.Called(scope, counter) } // RecordHistogramDuration provides a mock function with given fields: scope, timer, d func (_m *Client) RecordHistogramDuration(scope metrics.ScopeIdx, timer metrics.MetricIdx, d time.Duration) { _m.Called(scope, timer, d) } // RecordTimer provides a mock function with given fields: scope, timer, d func (_m *Client) RecordTimer(scope metrics.ScopeIdx, timer metrics.MetricIdx, d time.Duration) { _m.Called(scope, timer, d) } // Scope provides a mock function with given fields: scope, tags func (_m *Client) Scope(scope metrics.ScopeIdx, tags ...metrics.Tag) metrics.Scope { _va := make([]interface{}, len(tags)) for _i := range tags { _va[_i] = tags[_i] } var _ca []interface{} _ca = append(_ca, scope) _ca = append(_ca, _va...) ret := _m.Called(_ca...) var r0 metrics.Scope if rf, ok := ret.Get(0).(func(metrics.ScopeIdx, ...metrics.Tag) metrics.Scope); ok { r0 = rf(scope, tags...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(metrics.Scope) } } return r0 } // StartTimer provides a mock function with given fields: scope, timer func (_m *Client) StartTimer(scope metrics.ScopeIdx, timer metrics.MetricIdx) tally.Stopwatch { ret := _m.Called(scope, timer) var r0 tally.Stopwatch if rf, ok := ret.Get(0).(func(metrics.ScopeIdx, metrics.MetricIdx) tally.Stopwatch); ok { r0 = rf(scope, timer) } else { r0 = ret.Get(0).(tally.Stopwatch) } return r0 } // UpdateGauge provides a mock function with given fields: scope, gauge, value func (_m *Client) UpdateGauge(scope metrics.ScopeIdx, gauge metrics.MetricIdx, value float64) { _m.Called(scope, gauge, value) } ================================================ FILE: common/metrics/mocks/Scope.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Code generated by mockery v1.0.0. DO NOT EDIT. package mocks import ( time "time" mock "github.com/stretchr/testify/mock" metrics "github.com/uber/cadence/common/metrics" ) // Scope is an autogenerated mock type for the Scope type type Scope struct { mock.Mock } // AddCounter provides a mock function with given fields: counter, delta func (_m *Scope) AddCounter(counter metrics.MetricIdx, delta int64) { _m.Called(counter, delta) } // IncCounter provides a mock function with given fields: counter func (_m *Scope) IncCounter(counter metrics.MetricIdx) { _m.Called(counter) } // RecordHistogramDuration provides a mock function with given fields: timer, d func (_m *Scope) RecordHistogramDuration(timer metrics.MetricIdx, d time.Duration) { _m.Called(timer, d) } // RecordHistogramValue provides a mock function with given fields: timer, value func (_m *Scope) RecordHistogramValue(timer metrics.MetricIdx, value float64) { _m.Called(timer, value) } func (_m *Scope) ExponentialHistogram(hist metrics.MetricIdx, d time.Duration) { _m.Called(hist, d) } func (_m *Scope) IntExponentialHistogram(hist metrics.MetricIdx, value int) { _m.Called(hist, value) } // RecordTimer provides a mock function with given fields: timer, d func (_m *Scope) RecordTimer(timer metrics.MetricIdx, d time.Duration) { _m.Called(timer, d) } // StartTimer provides a mock function with given fields: timer func (_m *Scope) StartTimer(timer metrics.MetricIdx) metrics.Stopwatch { ret := _m.Called(timer) var r0 metrics.Stopwatch if rf, ok := ret.Get(0).(func(idx metrics.MetricIdx) metrics.Stopwatch); ok { r0 = rf(timer) } else { r0 = ret.Get(0).(metrics.Stopwatch) } return r0 } // Tagged provides a mock function with given fields: tags func (_m *Scope) Tagged(tags ...metrics.Tag) metrics.Scope { _va := make([]interface{}, len(tags)) for _i := range tags { _va[_i] = tags[_i] } var _ca []interface{} _ca = append(_ca, _va...) ret := _m.Called(_ca...) var r0 metrics.Scope if rf, ok := ret.Get(0).(func(...metrics.Tag) metrics.Scope); ok { r0 = rf(tags...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(metrics.Scope) } } return r0 } // UpdateGauge provides a mock function with given fields: gauge, value func (_m *Scope) UpdateGauge(gauge metrics.MetricIdx, value float64) { _m.Called(gauge, value) } ================================================ FILE: common/metrics/nop.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import ( "time" "github.com/uber-go/tally" ) var ( NoopClient Client = &noopClientImpl{} NoopScope Scope = &noopScopeImpl{} NoopStopwatch = tally.NewStopwatch(time.Now(), &nopStopwatchRecorder{}) ) type nopStopwatchRecorder struct{} // RecordStopwatch is a nop impl for replay mode func (n *nopStopwatchRecorder) RecordStopwatch(stopwatchStart time.Time) {} // NopStopwatch return a fake tally stop watch func NopStopwatch() tally.Stopwatch { return tally.NewStopwatch(time.Now(), &nopStopwatchRecorder{}) } type noopClientImpl struct{} func (n noopClientImpl) IncCounter(scope ScopeIdx, counter MetricIdx) {} func (n noopClientImpl) AddCounter(scope ScopeIdx, counter MetricIdx, delta int64) {} func (n noopClientImpl) StartTimer(scope ScopeIdx, timer MetricIdx) tally.Stopwatch { return NoopStopwatch } func (n noopClientImpl) RecordTimer(scope ScopeIdx, timer MetricIdx, d time.Duration) {} func (n *noopClientImpl) RecordHistogramDuration(scope ScopeIdx, timer MetricIdx, d time.Duration) {} func (n noopClientImpl) UpdateGauge(scope ScopeIdx, gauge MetricIdx, value float64) {} func (n noopClientImpl) Scope(scope ScopeIdx, tags ...Tag) Scope { return NoopScope } // NewNoopMetricsClient initialize new no-op metrics client func NewNoopMetricsClient() Client { return &noopClientImpl{} } type noopScopeImpl struct{} func (n *noopScopeImpl) IncCounter(counter MetricIdx) {} func (n *noopScopeImpl) AddCounter(counter MetricIdx, delta int64) {} func (n *noopScopeImpl) StartTimer(timer MetricIdx) Stopwatch { return NewTestStopwatch() } func (n *noopScopeImpl) RecordTimer(timer MetricIdx, d time.Duration) {} func (n *noopScopeImpl) RecordHistogramDuration(timer MetricIdx, d time.Duration) {} func (n *noopScopeImpl) RecordHistogramValue(timer MetricIdx, value float64) {} func (n *noopScopeImpl) ExponentialHistogram(hist MetricIdx, d time.Duration) {} func (n *noopScopeImpl) IntExponentialHistogram(hist MetricIdx, value int) {} func (n *noopScopeImpl) UpdateGauge(gauge MetricIdx, value float64) {} func (n *noopScopeImpl) Tagged(tags ...Tag) Scope { return n } ================================================ FILE: common/metrics/runtime.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import ( "runtime" "strconv" "sync/atomic" "time" "github.com/uber-go/tally" "github.com/uber/cadence/common/log" ) var ( // Revision is the VCS revision associated with this build. Overridden using ldflags // at compile time. Example: // $ go build -ldflags "-X github.com/uber/cadence/common/metrics.Revision=abcdef" ... // see get-ldflags.sh for GIT_REVISION Revision = "unknown" // Branch is the VCS branch associated with this build. Branch = "unknown" // ReleaseVersion is the version associated with this build. ReleaseVersion = "unknown" // BuildDate is the date this build was created. BuildDate = "unknown" // BuildTimeUnix is the seconds since epoch representing the date this build was created. BuildTimeUnix = "0" // goVersion is the current runtime version. goVersion = runtime.Version() // cadenceVersion is the current version of cadence cadenceVersion = VersionString ) const ( // buildInfoMetricName is the emitted build information metric's name. buildInfoMetricName = "build_information" // buildAgeMetricName is the emitted build age metric's name. buildAgeMetricName = "build_age" ) // RuntimeMetricsReporter A struct containing the state of the RuntimeMetricsReporter. type RuntimeMetricsReporter struct { scope tally.Scope buildInfoScope tally.Scope reportInterval time.Duration started int32 quit chan struct{} logger log.Logger lastNumGC uint32 buildTime time.Time } // NewRuntimeMetricsReporter Creates a new RuntimeMetricsReporter. func NewRuntimeMetricsReporter( scope tally.Scope, reportInterval time.Duration, logger log.Logger, instanceID string, ) *RuntimeMetricsReporter { const ( base = 10 bitSize = 64 ) if len(instanceID) > 0 { scope = scope.Tagged(map[string]string{instance: instanceID}) } var memstats runtime.MemStats runtime.ReadMemStats(&memstats) rReporter := &RuntimeMetricsReporter{ scope: scope, reportInterval: reportInterval, logger: logger, lastNumGC: memstats.NumGC, quit: make(chan struct{}), } rReporter.buildInfoScope = scope.Tagged( map[string]string{ revisionTag: Revision, branchTag: Branch, buildDateTag: BuildDate, buildVersionTag: ReleaseVersion, goVersionTag: goVersion, cadenceVersionTag: cadenceVersion, }, ) sec, err := strconv.ParseInt(BuildTimeUnix, base, bitSize) if err != nil || sec < 0 { sec = 0 } rReporter.buildTime = time.Unix(sec, 0) return rReporter } // report Sends runtime metrics to the local metrics collector. func (r *RuntimeMetricsReporter) report() { var memStats runtime.MemStats runtime.ReadMemStats(&memStats) r.scope.Gauge(NumGoRoutinesGauge).Update(float64(runtime.NumGoroutine())) r.scope.Gauge(GoMaxProcsGauge).Update(float64(runtime.GOMAXPROCS(0))) r.scope.Gauge(MemoryAllocatedGauge).Update(float64(memStats.Alloc)) r.scope.Gauge(MemoryHeapGauge).Update(float64(memStats.HeapAlloc)) r.scope.Gauge(MemoryHeapIdleGauge).Update(float64(memStats.HeapIdle)) r.scope.Gauge(MemoryHeapInuseGauge).Update(float64(memStats.HeapInuse)) r.scope.Gauge(MemoryStackGauge).Update(float64(memStats.StackInuse)) // memStats.NumGC is a perpetually incrementing counter (unless it wraps at 2^32) num := memStats.NumGC lastNum := atomic.SwapUint32(&r.lastNumGC, num) // reset for the next iteration if delta := num - lastNum; delta > 0 { r.scope.Counter(NumGCCounter).Inc(int64(delta)) if delta > 255 { // too many GCs happened, the timestamps buffer got wrapped around. Report only the last 256 lastNum = num - 256 } for i := lastNum; i != num; i++ { pause := memStats.PauseNs[i%256] r.scope.Timer(GcPauseMsTimer).Record(time.Duration(pause)) } } // report build info buildInfoGauge := r.buildInfoScope.Gauge(buildInfoMetricName) buildAgeGauge := r.buildInfoScope.Gauge(buildAgeMetricName) buildInfoGauge.Update(1.0) buildAgeGauge.Update(float64(time.Since(r.buildTime))) } // Start Starts the reporter thread that periodically emits metrics. func (r *RuntimeMetricsReporter) Start() { if !atomic.CompareAndSwapInt32(&r.started, 0, 1) { return } go func() { ticker := time.NewTicker(r.reportInterval) for { select { case <-ticker.C: r.report() case <-r.quit: ticker.Stop() return } } }() r.logger.Info("RuntimeMetricsReporter started") } // Stop Stops reporting of runtime metrics. The reporter cannot be started again after it's been stopped. func (r *RuntimeMetricsReporter) Stop() { close(r.quit) r.logger.Info("RuntimeMetricsReporter stopped") } ================================================ FILE: common/metrics/scope.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import ( "time" "github.com/uber-go/tally" ) type metricsScope struct { scope tally.Scope rootScope tally.Scope defs map[MetricIdx]metricDefinition isDomainTagged bool config HistogramMigration } func newMetricsScope( rootScope tally.Scope, scope tally.Scope, defs map[MetricIdx]metricDefinition, isDomain bool, config HistogramMigration, ) Scope { return &metricsScope{ scope: scope, rootScope: rootScope, defs: defs, isDomainTagged: isDomain, config: config, } } func (m *metricsScope) IncCounter(id MetricIdx) { m.AddCounter(id, 1) } func (m *metricsScope) AddCounter(id MetricIdx, delta int64) { def := m.defs[id] m.scope.Counter(def.metricName.String()).Inc(delta) if !def.metricRollupName.Empty() { m.rootScope.Counter(def.metricRollupName.String()).Inc(delta) } } func (m *metricsScope) UpdateGauge(id MetricIdx, value float64) { def := m.defs[id] m.scope.Gauge(def.metricName.String()).Update(value) if !def.metricRollupName.Empty() { m.scope.Gauge(def.metricRollupName.String()).Update(value) } } func (m *metricsScope) StartTimer(id MetricIdx) Stopwatch { def := m.defs[id] timer := m.scope.Timer(def.metricName.String()) switch { case !def.metricRollupName.Empty(): if m.config.EmitTimer(def.metricRollupName.String()) { return NewStopwatch(timer, m.rootScope.Timer(def.metricRollupName.String())) } case m.isDomainTagged: if m.config.EmitTimer(def.metricName.String()) { timerAll := m.scope.Tagged(map[string]string{domain: allValue}).Timer(def.metricName.String()) return NewStopwatch(timer, timerAll) } default: if m.config.EmitTimer(def.metricName.String()) { return NewStopwatch(timer) } } return Stopwatch{} // noop } func (m *metricsScope) RecordTimer(id MetricIdx, d time.Duration) { def := m.defs[id] if m.config.EmitTimer(def.metricName.String()) { m.scope.Timer(def.metricName.String()).Record(d) } switch { case !def.metricRollupName.Empty(): if m.config.EmitTimer(def.metricRollupName.String()) { m.rootScope.Timer(def.metricRollupName.String()).Record(d) } case m.isDomainTagged: // N.B. - Dual emit here so that we can see aggregate timer stats across all // domains along with the individual domains stats if m.config.EmitTimer(def.metricName.String()) { m.scope.Tagged(map[string]string{domain: allValue}).Timer(def.metricName.String()).Record(d) } } } func (m *metricsScope) RecordHistogramDuration(id MetricIdx, value time.Duration) { def := m.defs[id] if m.config.EmitHistogram(def.metricName.String()) { m.scope.Histogram(def.metricName.String(), m.getBuckets(id)).RecordDuration(value) } if !def.metricRollupName.Empty() { if m.config.EmitHistogram(def.metricRollupName.String()) { m.rootScope.Histogram(def.metricRollupName.String(), m.getBuckets(id)).RecordDuration(value) } } } func (m *metricsScope) RecordHistogramValue(id MetricIdx, value float64) { def := m.defs[id] if m.config.EmitHistogram(def.metricName.String()) { m.scope.Histogram(def.metricName.String(), m.getBuckets(id)).RecordValue(value) } if !def.metricRollupName.Empty() { if m.config.EmitHistogram(def.metricRollupName.String()) { m.rootScope.Histogram(def.metricRollupName.String(), m.getBuckets(id)).RecordValue(value) } } } func (m *metricsScope) ExponentialHistogram(id MetricIdx, value time.Duration) { def := m.defs[id] if m.config.EmitHistogram(def.metricName.String()) { m.scope.Tagged(def.exponentialBuckets.tags()).Histogram(def.metricName.String(), def.exponentialBuckets.buckets()).RecordDuration(value) } } func (m *metricsScope) IntExponentialHistogram(id MetricIdx, value int) { def := m.defs[id] if m.config.EmitHistogram(def.metricName.String()) { m.scope.Tagged(def.intExponentialBuckets.tags()).Histogram(def.metricName.String(), def.intExponentialBuckets.buckets()).RecordDuration(time.Duration(value)) } } func (m *metricsScope) Tagged(tags ...Tag) Scope { domainTagged := m.isDomainTagged tagMap := make(map[string]string, len(tags)) for _, tag := range tags { if isDomainTagged(tag) { domainTagged = true } tagMap[tag.Key()] = tag.Value() } return newMetricsScope(m.rootScope, m.scope.Tagged(tagMap), m.defs, domainTagged, m.config) } func (m *metricsScope) getBuckets(id MetricIdx) tally.Buckets { if m.defs[id].buckets != nil { return m.defs[id].buckets } return tally.DefaultBuckets } func isDomainTagged(tag Tag) bool { return tag.Key() == domain && tag.Value() != allValue } ================================================ FILE: common/metrics/scope_test.go ================================================ package metrics import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" ) func TestHistogramMode(t *testing.T) { ts := tally.NewTestScope("", nil) findName := func(m MetricIdx) string { def, ok := MetricDefs[Common][m] if ok { return def.metricName.String() } def, ok = MetricDefs[History][m] if ok { return def.metricName.String() } t.Fatalf("MetricDef not found in common or history: %v", m) return "unknown" } orig := HistogramMigrationMetrics t.Cleanup(func() { HistogramMigrationMetrics = orig }) HistogramMigrationMetrics = map[string]struct{}{ findName(CadenceLatency): {}, findName(ExponentialReplicationTaskLatency): {}, findName(PersistenceLatencyPerShard): {}, findName(ExponentialTaskProcessingLatency): {}, findName(PersistenceLatency): {}, findName(PersistenceLatencyHistogram): {}, findName(TaskAttemptTimer): {}, findName(ExponentialTaskAttemptCounts): {}, findName(TaskQueueLatency): {}, findName(ExponentialTaskQueueLatency): {}, findName(TaskLatencyPerDomain): {}, findName(ExponentialTaskLatencyPerDomain): {}, findName(TaskAttemptTimerPerDomain): {}, findName(ExponentialTaskAttemptCountsPerDomain): {}, findName(TaskProcessingLatencyPerDomain): {}, findName(ExponentialTaskProcessingLatencyPerDomain): {}, findName(TaskQueueLatencyPerDomain): {}, findName(ExponentialTaskQueueLatencyPerDomain): {}, } c := NewClient(ts, History, HistogramMigration{ // Default: ..., left at default value Names: map[string]bool{ findName(CadenceLatency): true, // timer type findName(ExponentialReplicationTaskLatency): false, // histogram type findName(PersistenceLatencyPerShard): false, // timer type findName(ExponentialTaskProcessingLatency): true, // histogram type }, }) scope := c.Scope(HistoryDescribeQueueScope) // scope doesn't matter for this test scope.RecordTimer(CadenceLatency, time.Second) scope.ExponentialHistogram(ExponentialReplicationTaskLatency, 2*time.Second) scope.RecordTimer(PersistenceLatencyPerShard, 3*time.Second) scope.ExponentialHistogram(ExponentialTaskProcessingLatency, 4*time.Second) // unspecified -> default config scope.RecordTimer(PersistenceLatency, 5*time.Second) scope.RecordHistogramDuration(PersistenceLatencyHistogram, 6*time.Second) // not migrating -> always emit scope.RecordTimer(CadenceDcRedirectionClientLatency, 7*time.Second) scope.RecordHistogramDuration(GlobalRatelimiterStartupUsageHistogram, 8*time.Second) s := ts.Snapshot() findMetric := func(idx MetricIdx) (timer, histogram bool) { name := findName(idx) for _, v := range s.Timers() { if v.Name() == name { t.Logf("found timer: %v = %v", v.Name(), v.Values()) timer = true break } } for _, v := range s.Histograms() { if v.Name() == findName(idx) { nzDur := make(map[time.Duration]int64, 1) for k, val := range v.Durations() { if val != 0 { nzDur[k] = val } } nzVal := make(map[float64]int64, 1) for k, val := range v.Values() { if val != 0 { nzVal[k] = val } } t.Logf("found histogram: %v = %v (values: %v)", v.Name(), nzDur, nzVal) histogram = true break } } return } assertFound := func(idx MetricIdx, timer, histogram bool) { name := findName(idx) foundTimer, foundHistogram := findMetric(idx) assert.Equalf(t, foundTimer, timer, "wrong timer behavior for %v", name) assert.Equalf(t, foundHistogram, histogram, "wrong histogram behavior for %v", name) } // only the timer assertFound(CadenceLatency, true, false) assertFound(ExponentialReplicationTaskLatency, false, false) // only the histogram assertFound(PersistenceLatencyPerShard, false, false) assertFound(ExponentialTaskProcessingLatency, false, true) // timers only (via default) assertFound(PersistenceLatency, true, false) assertFound(PersistenceLatencyHistogram, false, false) // not migrating, the correct type should be emitted assertFound(CadenceDcRedirectionClientLatency, true, false) assertFound(GlobalRatelimiterStartupUsageHistogram, false, true) // when fixing: check logs! you should see metrics with values for: 1, 4, 7, 8 } ================================================ FILE: common/metrics/stopwatch.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import ( "time" "github.com/uber-go/tally" ) // Stopwatch is a helper for simpler tracking of elapsed time, use the // Stop() method to report time elapsed since its created back to the // timer or histogram. type Stopwatch struct { start time.Time timers []tally.Timer } // NewStopwatch creates a new immutable stopwatch for recording the start // time to a stopwatch reporter. func NewStopwatch(timers ...tally.Timer) Stopwatch { return Stopwatch{time.Now(), timers} } // NewTestStopwatch returns a new test stopwatch func NewTestStopwatch() Stopwatch { return Stopwatch{time.Now(), []tally.Timer{}} } // Stop reports time elapsed since the stopwatch start to the recorder. func (sw Stopwatch) Stop() { d := time.Since(sw.start) for _, timer := range sw.timers { timer.Record(d) } } ================================================ FILE: common/metrics/tags.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package metrics import ( "regexp" "strconv" ) const ( revisionTag = "revision" branchTag = "branch" buildDateTag = "build_date" buildVersionTag = "build_version" goVersionTag = "go_version" cadenceVersionTag = "cadence_version" instance = "instance" domain = "domain" domainType = "domain_type" clusterGroup = "cluster_group" sourceCluster = "source_cluster" targetCluster = "target_cluster" activeCluster = "active_cluster" isActiveActiveDomain = "is_active_active_domain" taskList = "tasklist" taskListType = "tasklistType" taskListRootPartition = "tasklist_root_partition" workflowType = "workflowType" activityType = "activityType" decisionType = "decisionType" invariantType = "invariantType" shardScannerScanResult = "shardscanner_scan_result" shardScannerFixResult = "shardscanner_fix_result" kafkaPartition = "kafkaPartition" transport = "transport" caller = "caller" service = "service" destService = "dest_service" signalName = "signalName" workflowVersion = "workflow_version" shardID = "shard_id" matchingHost = "matching_host" host = "host" pollerIsolationGroup = "poller_isolation_group" asyncWFRequestType = "async_wf_request_type" workflowTerminationReason = "workflow_termination_reason" workflowCloseStatus = "workflow_close_status" isolationEnabled = "isolation_enabled" isolationGroup = "isolation_group" leakCause = "leak_cause" topic = "topic" mode = "mode" isRetry = "is_retry" queryConsistencyLevel = "query_consistency_level" budgetManagerName = "budget_manager_name" // limiter-side tags globalRatelimitKey = "global_ratelimit_key" globalRatelimitType = "global_ratelimit_type" globalRatelimitIsPrimary = "is_primary" globalRatelimitCollectionName = "global_ratelimit_collection" allValue = "all" unknownValue = "_unknown_" ) var ( safeAlphaNumericStringRE = regexp.MustCompile(`[^a-zA-Z0-9]`) ) // Tag is an interface to define metrics tags type ( Tag interface { Key() string Value() string } simpleMetric struct { key string value string } ) func (s simpleMetric) Key() string { return s.key } func (s simpleMetric) Value() string { return s.value } func metricWithUnknown(key, value string) Tag { if len(value) == 0 { value = unknownValue } return simpleMetric{key: key, value: value} } func ShardIDTag(shardIDVal int) Tag { return metricWithUnknown(shardID, strconv.Itoa(shardIDVal)) } // DomainTag returns a new domain tag. For timers, this also ensures that we // dual emit the metric with the all tag. If a blank domain is provided then // this converts that to an unknown domain. func DomainTag(value string) Tag { return metricWithUnknown(domain, value) } // DomainTypeTag returns a tag for domain type. // This allows differentiating between global/local domains. func DomainTypeTag(isGlobal bool) Tag { var value string if isGlobal { value = "global" } else { value = "local" } return simpleMetric{key: domainType, value: value} } // DomainUnknownTag returns a new domain:unknown tag-value func DomainUnknownTag() Tag { return DomainTag("") } // ClusterGroupTag return a new cluster group tag func ClusterGroupTag(value string) Tag { return simpleMetric{key: clusterGroup, value: value} } // InstanceTag returns a new instance tag func InstanceTag(value string) Tag { return simpleMetric{key: instance, value: value} } // SourceClusterTag returns a new source cluster tag. func SourceClusterTag(value string) Tag { return metricWithUnknown(sourceCluster, value) } // TargetClusterTag returns a new target cluster tag. func TargetClusterTag(value string) Tag { return metricWithUnknown(targetCluster, value) } // ActiveClusterTag returns a new active cluster type tag. func ActiveClusterTag(value string) Tag { return metricWithUnknown(activeCluster, value) } // IsActiveActiveDomainTag returns a new is active active domain tag. func IsActiveActiveDomainTag(value bool) Tag { return simpleMetric{key: isActiveActiveDomain, value: strconv.FormatBool(value)} } // TaskListTag returns a new task list tag. func TaskListTag(value string) Tag { if len(value) == 0 { value = unknownValue } return simpleMetric{key: taskList, value: sanitizer.Value(value)} } // TaskListUnknownTag returns a new tasklist:unknown tag-value func TaskListUnknownTag() Tag { return simpleMetric{key: taskList, value: unknownValue} } // TaskListTypeTag returns a new task list type tag. func TaskListTypeTag(value string) Tag { return metricWithUnknown(taskListType, value) } // TaskListRootPartition returns a new task list root partition tag. func TaskListRootPartitionTag(value string) Tag { if len(value) == 0 { value = unknownValue } return simpleMetric{key: taskListRootPartition, value: sanitizer.Value(value)} } // WorkflowTypeTag returns a new workflow type tag. func WorkflowTypeTag(value string) Tag { return metricWithUnknown(workflowType, value) } // ActivityTypeTag returns a new activity type tag. func ActivityTypeTag(value string) Tag { return metricWithUnknown(activityType, value) } // DecisionTypeTag returns a new decision type tag. func DecisionTypeTag(value string) Tag { return metricWithUnknown(decisionType, value) } // ShardScannerScanResult returns a new shardscanner scan result type tag. func ShardScannerScanResult(value string) Tag { return metricWithUnknown(shardScannerScanResult, value) } // ShardScannerFixResult returns a new shardscanner fix result type tag. func ShardScannerFixResult(value string) Tag { return metricWithUnknown(shardScannerFixResult, value) } // InvariantTypeTag returns a new invariant type tag. func InvariantTypeTag(value string) Tag { return metricWithUnknown(invariantType, value) } // KafkaPartitionTag returns a new KafkaPartition type tag. func KafkaPartitionTag(value int32) Tag { return simpleMetric{key: kafkaPartition, value: strconv.Itoa(int(value))} } // TransportTag returns a new RPC Transport type tag. func TransportTag(value string) Tag { return simpleMetric{key: transport, value: value} } // CallerTag returns a new RPC Caller type tag. func CallerTag(value string) Tag { return simpleMetric{key: caller, value: value} } // ServiceTag returns a new service tag. func ServiceTag(value string) Tag { return simpleMetric{key: service, value: value} } // DestServiceTag returns a new destination service tag. func DestServiceTag(value string) Tag { return simpleMetric{key: destService, value: value} } // Hosttag emits the host identifier func HostTag(value string) Tag { return metricWithUnknown(host, value) } // SignalNameTag returns a new SignalName tag func SignalNameTag(value string) Tag { return metricWithUnknown(signalName, value) } // SignalNameAllTag returns a new SignalName tag with all value func SignalNameAllTag() Tag { return metricWithUnknown(signalName, allValue) } // WorkflowVersionTag returns a new WorkflowVersion tag func WorkflowVersionTag(value string) Tag { return metricWithUnknown(workflowVersion, value) } func MatchingHostTag(value string) Tag { return metricWithUnknown(matchingHost, value) } // PollerIsolationGroupTag returns a new PollerIsolationGroup tag func PollerIsolationGroupTag(value string) Tag { return metricWithUnknown(pollerIsolationGroup, value) } // AsyncWFRequestTypeTag returns a new AsyncWFRequestTypeTag tag func AsyncWFRequestTypeTag(value string) Tag { return metricWithUnknown(asyncWFRequestType, value) } // GlobalRatelimiterKeyTag reports the local ratelimit key being used, e.g. "domain-x". // This will likely be ambiguous if it is not combined with the collection name, // but keeping this untouched helps keep the values template-friendly and correlate-able // in metrics dashboards and queries. func GlobalRatelimiterKeyTag(value string) Tag { return simpleMetric{key: globalRatelimitKey, value: value} } // GlobalRatelimiterTypeTag reports the "limit usage type" being reported, e.g. global vs local func GlobalRatelimiterTypeTag(value string) Tag { return simpleMetric{key: globalRatelimitType, value: value} } func GlobalRatelimiterIsPrimary(isPrimary bool) Tag { return simpleMetric{key: globalRatelimitIsPrimary, value: strconv.FormatBool(isPrimary)} } // GlobalRatelimiterCollectionName is a namespacing tag to uniquely identify metrics // coming from the different ratelimiter collections (user, worker, visibility, async). func GlobalRatelimiterCollectionName(value string) Tag { return simpleMetric{key: globalRatelimitCollectionName, value: value} } // WorkflowTerminationReasonTag reports the reason for workflow termination func WorkflowTerminationReasonTag(value string) Tag { value = safeAlphaNumericStringRE.ReplaceAllString(value, "_") return simpleMetric{key: workflowTerminationReason, value: value} } // WorkflowCloseStatusTag is a stringified workflow status func WorkflowCloseStatusTag(value string) Tag { value = safeAlphaNumericStringRE.ReplaceAllString(value, "_") return simpleMetric{key: workflowCloseStatus, value: value} } func IsolationGroupTag(group string) Tag { return simpleMetric{key: isolationGroup, value: sanitizer.Value(group)} } func IsolationLeakCause(cause string) Tag { return simpleMetric{key: leakCause, value: sanitizer.Value(cause)} } // IsolationEnabledTag returns whether isolation is enabled func IsolationEnabledTag(enabled bool) Tag { return simpleMetric{key: isolationEnabled, value: strconv.FormatBool(enabled)} } func TopicTag(value string) Tag { return metricWithUnknown(topic, value) } func ModeTag(value string) Tag { return metricWithUnknown(mode, value) } // IsRetryTag returns a new is_retry tag. func IsRetryTag(retry bool) Tag { return simpleMetric{key: isRetry, value: strconv.FormatBool(retry)} } func NamespaceTag(namespace string) Tag { return metricWithUnknown("namespace", namespace) } func NamespaceTypeTag(namespaceType string) Tag { return metricWithUnknown("namespace_type", namespaceType) } func HandoverTypeTag(handoverType string) Tag { return metricWithUnknown("handover_type", handoverType) } func ExecutorStatusTag(status string) Tag { return metricWithUnknown("executor_status", status) } func ShardDistributorWatchTypeTag(watchType string) Tag { return metricWithUnknown("watch_type", watchType) } func TaskCategoryTag(category string) Tag { return metricWithUnknown("task_category", category) } // ReasonTag returns a new reason tag func ReasonTag(reason string) Tag { return metricWithUnknown("reason", reason) } // DecisionTag returns a new decision tag func DecisionTag(decision string) Tag { return metricWithUnknown("decision", decision) } // ActiveClusterLookupFnTag returns a new active cluster lookup function tag. func ActiveClusterLookupFnTag(fn string) Tag { return metricWithUnknown("fn", fn) } // ActiveClusterSelectionStrategyTag returns a new active cluster selection strategy tag. func ActiveClusterSelectionStrategyTag(strategy string) Tag { return metricWithUnknown("strategy", strategy) } // QueryConsistencyLevelTag returns a new query consistency level tag. func QueryConsistencyLevelTag(level string) Tag { return metricWithUnknown(queryConsistencyLevel, level) } // BudgetManagerNameTag returns a new budget manager name tag. func BudgetManagerNameTag(name string) Tag { return metricWithUnknown(budgetManagerName, name) } ================================================ FILE: common/metrics/tally/prometheus/buckets.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package prometheus import ( "time" "github.com/uber-go/tally/prometheus" ) // DefaultHistogramBuckets is the default histogram buckets used when // creating a new Histogram in the prometheus registry. // Cadence uses Histogram to report both latency and non-latency metrics(like history_count/history_size). // The buckets need to set correct boundary for both // See more in https://github.com/uber/cadence/issues/4006 func DefaultHistogramBuckets() []prometheus.HistogramObjective { // For latency metrics, this is a nanoSecond. // For non-latency metrics, this is ONE. // divided by Second because of https://github.com/uber-go/tally/blob/04828d51c63b1d09b46824d7c0d7904b5eb1b3b6/prometheus/reporter.go#L183 oneOrNanoSecond := float64(time.Nanosecond) / float64(time.Second) // convenient units microSecOr1K := oneOrNanoSecond * 1000 milliSecOr1M := oneOrNanoSecond * 1000000 return []prometheus.HistogramObjective{ {Upper: 5 * oneOrNanoSecond}, {Upper: 10 * oneOrNanoSecond}, {Upper: 50 * oneOrNanoSecond}, {Upper: 100 * oneOrNanoSecond}, {Upper: microSecOr1K}, {Upper: 10 * microSecOr1K}, {Upper: 50 * microSecOr1K}, {Upper: 200 * microSecOr1K}, // Below are exactly the same as https://github.com/uber-go/tally/blob/04828d51c63b1d09b46824d7c0d7904b5eb1b3b6/prometheus/reporter.go#L51 // to ensure backward compatibility {Upper: milliSecOr1M}, {Upper: 2 * milliSecOr1M}, {Upper: 5 * milliSecOr1M}, {Upper: 10 * milliSecOr1M}, {Upper: 20 * milliSecOr1M}, {Upper: 50 * milliSecOr1M}, {Upper: 100 * milliSecOr1M}, {Upper: 200 * milliSecOr1M}, {Upper: 500 * milliSecOr1M}, {Upper: 1000 * milliSecOr1M}, {Upper: 2000 * milliSecOr1M}, {Upper: 5000 * milliSecOr1M}, {Upper: 10000 * milliSecOr1M}, } } ================================================ FILE: common/metrics/tally/statsd/reporter.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package statsd import ( "bytes" "sort" "time" "github.com/cactus/go-statsd-client/statsd" "github.com/uber-go/tally" tallystatsdreporter "github.com/uber-go/tally/statsd" ) type cadenceTallyStatsdReporter struct { // Wrapper on top of "github.com/uber-go/tally/statsd" tallystatsd tally.StatsReporter } func (r *cadenceTallyStatsdReporter) metricNameWithTags(originalName string, tags map[string]string) string { var keys []string for k := range tags { keys = append(keys, k) } sort.Strings(keys) var buffer bytes.Buffer buffer.WriteString(originalName) for _, tk := range keys { // adding "." as delimiter so that it will show as different parts in Graphite/Grafana buffer.WriteString("." + tk + "." + tags[tk]) } return buffer.String() } // NewReporter is a wrapper on top of "github.com/uber-go/tally/statsd" // The purpose is to support tagging // The implementation is to append tags as metric name suffixes func NewReporter(statsd statsd.Statter, opts tallystatsdreporter.Options) tally.StatsReporter { return &cadenceTallyStatsdReporter{ tallystatsd: tallystatsdreporter.NewReporter(statsd, opts), } } func (r *cadenceTallyStatsdReporter) ReportCounter(name string, tags map[string]string, value int64) { newName := r.metricNameWithTags(name, tags) r.tallystatsd.ReportCounter(newName, map[string]string{}, value) } func (r *cadenceTallyStatsdReporter) ReportGauge(name string, tags map[string]string, value float64) { newName := r.metricNameWithTags(name, tags) r.tallystatsd.ReportGauge(newName, map[string]string{}, value) } func (r *cadenceTallyStatsdReporter) ReportTimer(name string, tags map[string]string, interval time.Duration) { newName := r.metricNameWithTags(name, tags) r.tallystatsd.ReportTimer(newName, map[string]string{}, interval) } func (r *cadenceTallyStatsdReporter) ReportHistogramValueSamples( name string, tags map[string]string, buckets tally.Buckets, bucketLowerBound, bucketUpperBound float64, samples int64, ) { newName := r.metricNameWithTags(name, tags) r.tallystatsd.ReportHistogramValueSamples(newName, map[string]string{}, buckets, bucketLowerBound, bucketUpperBound, samples) } func (r *cadenceTallyStatsdReporter) ReportHistogramDurationSamples( name string, tags map[string]string, buckets tally.Buckets, bucketLowerBound, bucketUpperBound time.Duration, samples int64, ) { newName := r.metricNameWithTags(name, tags) r.tallystatsd.ReportHistogramDurationSamples(newName, map[string]string{}, buckets, bucketLowerBound, bucketUpperBound, samples) } func (r *cadenceTallyStatsdReporter) Capabilities() tally.Capabilities { return r.tallystatsd.Capabilities() } func (r *cadenceTallyStatsdReporter) Flush() { r.tallystatsd.Flush() } ================================================ FILE: common/metrics/tally/statsd/reporter_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package statsd import ( "testing" "github.com/stretchr/testify/assert" ) func TestMetricNameWithTags(t *testing.T) { r := cadenceTallyStatsdReporter{ tallystatsd: nil, } tags := map[string]string{ "tag1": "123", "tag2": "456", "tag3": "789", } name := "test-metric-name1" assert.Equal(t, "test-metric-name1.tag1.123.tag2.456.tag3.789", r.metricNameWithTags(name, tags)) } func TestMetricNameWithTagsStability(t *testing.T) { r := cadenceTallyStatsdReporter{ tallystatsd: nil, } tags := map[string]string{ "tag1": "123", "tag2": "456", "tag3": "789", } name := "test-metric-name2" // test the order is stable for i := 1; i <= 16; i++ { assert.Equal(t, r.metricNameWithTags(name, tags), r.metricNameWithTags(name, tags)) } } ================================================ FILE: common/metrics/version.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metrics // VersionString the current release version const VersionString = "1.2.5" ================================================ FILE: common/mocks/ExecutionManager.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Code generated by mockery v2.32.0. DO NOT EDIT. package mocks import ( context "context" mock "github.com/stretchr/testify/mock" persistence "github.com/uber/cadence/common/persistence" types "github.com/uber/cadence/common/types" ) // ExecutionManager is an autogenerated mock type for the ExecutionManager type type ExecutionManager struct { mock.Mock } // Close provides a mock function with given fields: func (_m *ExecutionManager) Close() { _m.Called() } // CompleteHistoryTask provides a mock function with given fields: ctx, request func (_m *ExecutionManager) CompleteHistoryTask(ctx context.Context, request *persistence.CompleteHistoryTaskRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.CompleteHistoryTaskRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // ConflictResolveWorkflowExecution provides a mock function with given fields: ctx, request func (_m *ExecutionManager) ConflictResolveWorkflowExecution(ctx context.Context, request *persistence.ConflictResolveWorkflowExecutionRequest) (*persistence.ConflictResolveWorkflowExecutionResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.ConflictResolveWorkflowExecutionResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ConflictResolveWorkflowExecutionRequest) (*persistence.ConflictResolveWorkflowExecutionResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ConflictResolveWorkflowExecutionRequest) *persistence.ConflictResolveWorkflowExecutionResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ConflictResolveWorkflowExecutionResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ConflictResolveWorkflowExecutionRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // CreateFailoverMarkerTasks provides a mock function with given fields: ctx, request func (_m *ExecutionManager) CreateFailoverMarkerTasks(ctx context.Context, request *persistence.CreateFailoverMarkersRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.CreateFailoverMarkersRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // CreateWorkflowExecution provides a mock function with given fields: ctx, request func (_m *ExecutionManager) CreateWorkflowExecution(ctx context.Context, request *persistence.CreateWorkflowExecutionRequest) (*persistence.CreateWorkflowExecutionResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.CreateWorkflowExecutionResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.CreateWorkflowExecutionRequest) (*persistence.CreateWorkflowExecutionResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.CreateWorkflowExecutionRequest) *persistence.CreateWorkflowExecutionResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.CreateWorkflowExecutionResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.CreateWorkflowExecutionRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteCurrentWorkflowExecution provides a mock function with given fields: ctx, request func (_m *ExecutionManager) DeleteCurrentWorkflowExecution(ctx context.Context, request *persistence.DeleteCurrentWorkflowExecutionRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.DeleteCurrentWorkflowExecutionRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // DeleteReplicationTaskFromDLQ provides a mock function with given fields: ctx, request func (_m *ExecutionManager) DeleteReplicationTaskFromDLQ(ctx context.Context, request *persistence.DeleteReplicationTaskFromDLQRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.DeleteReplicationTaskFromDLQRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // DeleteWorkflowExecution provides a mock function with given fields: ctx, request func (_m *ExecutionManager) DeleteWorkflowExecution(ctx context.Context, request *persistence.DeleteWorkflowExecutionRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.DeleteWorkflowExecutionRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // GetCurrentExecution provides a mock function with given fields: ctx, request func (_m *ExecutionManager) GetCurrentExecution(ctx context.Context, request *persistence.GetCurrentExecutionRequest) (*persistence.GetCurrentExecutionResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetCurrentExecutionResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetCurrentExecutionRequest) (*persistence.GetCurrentExecutionResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetCurrentExecutionRequest) *persistence.GetCurrentExecutionResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetCurrentExecutionResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetCurrentExecutionRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetHistoryTasks provides a mock function with given fields: ctx, request func (_m *ExecutionManager) GetHistoryTasks(ctx context.Context, request *persistence.GetHistoryTasksRequest) (*persistence.GetHistoryTasksResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetHistoryTasksResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetHistoryTasksRequest) (*persistence.GetHistoryTasksResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetHistoryTasksRequest) *persistence.GetHistoryTasksResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetHistoryTasksResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetHistoryTasksRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetName provides a mock function with given fields: func (_m *ExecutionManager) GetName() string { ret := _m.Called() var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } return r0 } // GetReplicationDLQSize provides a mock function with given fields: ctx, request func (_m *ExecutionManager) GetReplicationDLQSize(ctx context.Context, request *persistence.GetReplicationDLQSizeRequest) (*persistence.GetReplicationDLQSizeResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetReplicationDLQSizeResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetReplicationDLQSizeRequest) (*persistence.GetReplicationDLQSizeResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetReplicationDLQSizeRequest) *persistence.GetReplicationDLQSizeResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetReplicationDLQSizeResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetReplicationDLQSizeRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetReplicationTasksFromDLQ provides a mock function with given fields: ctx, request func (_m *ExecutionManager) GetReplicationTasksFromDLQ(ctx context.Context, request *persistence.GetReplicationTasksFromDLQRequest) (*persistence.GetHistoryTasksResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetHistoryTasksResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetReplicationTasksFromDLQRequest) (*persistence.GetHistoryTasksResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetReplicationTasksFromDLQRequest) *persistence.GetHistoryTasksResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetHistoryTasksResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetReplicationTasksFromDLQRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetShardID provides a mock function with given fields: func (_m *ExecutionManager) GetShardID() int { ret := _m.Called() var r0 int if rf, ok := ret.Get(0).(func() int); ok { r0 = rf() } else { r0 = ret.Get(0).(int) } return r0 } // GetWorkflowExecution provides a mock function with given fields: ctx, request func (_m *ExecutionManager) GetWorkflowExecution(ctx context.Context, request *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetWorkflowExecutionResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetWorkflowExecutionRequest) *persistence.GetWorkflowExecutionResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetWorkflowExecutionResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetWorkflowExecutionRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // IsWorkflowExecutionExists provides a mock function with given fields: ctx, request func (_m *ExecutionManager) IsWorkflowExecutionExists(ctx context.Context, request *persistence.IsWorkflowExecutionExistsRequest) (*persistence.IsWorkflowExecutionExistsResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.IsWorkflowExecutionExistsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.IsWorkflowExecutionExistsRequest) (*persistence.IsWorkflowExecutionExistsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.IsWorkflowExecutionExistsRequest) *persistence.IsWorkflowExecutionExistsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.IsWorkflowExecutionExistsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.IsWorkflowExecutionExistsRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ListConcreteExecutions provides a mock function with given fields: ctx, request func (_m *ExecutionManager) ListConcreteExecutions(ctx context.Context, request *persistence.ListConcreteExecutionsRequest) (*persistence.ListConcreteExecutionsResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.ListConcreteExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListConcreteExecutionsRequest) (*persistence.ListConcreteExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListConcreteExecutionsRequest) *persistence.ListConcreteExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListConcreteExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListConcreteExecutionsRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ListCurrentExecutions provides a mock function with given fields: ctx, request func (_m *ExecutionManager) ListCurrentExecutions(ctx context.Context, request *persistence.ListCurrentExecutionsRequest) (*persistence.ListCurrentExecutionsResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.ListCurrentExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListCurrentExecutionsRequest) (*persistence.ListCurrentExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListCurrentExecutionsRequest) *persistence.ListCurrentExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListCurrentExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListCurrentExecutionsRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // PutReplicationTaskToDLQ provides a mock function with given fields: ctx, request func (_m *ExecutionManager) PutReplicationTaskToDLQ(ctx context.Context, request *persistence.PutReplicationTaskToDLQRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.PutReplicationTaskToDLQRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // RangeCompleteHistoryTask provides a mock function with given fields: ctx, request func (_m *ExecutionManager) RangeCompleteHistoryTask(ctx context.Context, request *persistence.RangeCompleteHistoryTaskRequest) (*persistence.RangeCompleteHistoryTaskResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.RangeCompleteHistoryTaskResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.RangeCompleteHistoryTaskRequest) (*persistence.RangeCompleteHistoryTaskResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.RangeCompleteHistoryTaskRequest) *persistence.RangeCompleteHistoryTaskResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.RangeCompleteHistoryTaskResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.RangeCompleteHistoryTaskRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // RangeDeleteReplicationTaskFromDLQ provides a mock function with given fields: ctx, request func (_m *ExecutionManager) RangeDeleteReplicationTaskFromDLQ(ctx context.Context, request *persistence.RangeDeleteReplicationTaskFromDLQRequest) (*persistence.RangeDeleteReplicationTaskFromDLQResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.RangeDeleteReplicationTaskFromDLQResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.RangeDeleteReplicationTaskFromDLQRequest) (*persistence.RangeDeleteReplicationTaskFromDLQResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.RangeDeleteReplicationTaskFromDLQRequest) *persistence.RangeDeleteReplicationTaskFromDLQResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.RangeDeleteReplicationTaskFromDLQResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.RangeDeleteReplicationTaskFromDLQRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // UpdateWorkflowExecution provides a mock function with given fields: ctx, request func (_m *ExecutionManager) UpdateWorkflowExecution(ctx context.Context, request *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.UpdateWorkflowExecutionResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.UpdateWorkflowExecutionRequest) *persistence.UpdateWorkflowExecutionResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.UpdateWorkflowExecutionResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.UpdateWorkflowExecutionRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetActiveClusterSelectionPolicy provides a mock function with given fields: ctx, domainID, wfID, rID func (_m *ExecutionManager) GetActiveClusterSelectionPolicy(ctx context.Context, domainID, wfID, rID string) (*types.ActiveClusterSelectionPolicy, error) { ret := _m.Called(ctx, domainID, wfID, rID) var r0 *types.ActiveClusterSelectionPolicy var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (*types.ActiveClusterSelectionPolicy, error)); ok { return rf(ctx, domainID, wfID, rID) } if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *types.ActiveClusterSelectionPolicy); ok { r0 = rf(ctx, domainID, wfID, rID) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*types.ActiveClusterSelectionPolicy) } } if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { r1 = rf(ctx, domainID, wfID, rID) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteActiveClusterSelectionPolicy provides a mock function with given fields: ctx, domainID, wfID, rID func (_m *ExecutionManager) DeleteActiveClusterSelectionPolicy(ctx context.Context, domainID, wfID, rID string) error { ret := _m.Called(ctx, domainID, wfID, rID) var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok { r0 = rf(ctx, domainID, wfID, rID) } else { r0 = ret.Error(0) } return r0 } // NewExecutionManager creates a new instance of ExecutionManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewExecutionManager(t interface { mock.TestingT Cleanup(func()) }) *ExecutionManager { mock := &ExecutionManager{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) return mock } ================================================ FILE: common/mocks/ExecutionManagerFactory.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mocks import ( "github.com/stretchr/testify/mock" "github.com/uber/cadence/common/persistence" ) // ExecutionManagerFactory is an autogenerated mock type for the ExecutionManagerFactory type type ExecutionManagerFactory struct { mock.Mock } // NewExecutionManager provides a mock function with given fields: shardID func (_m *ExecutionManagerFactory) NewExecutionManager(shardID int) (persistence.ExecutionManager, error) { ret := _m.Called(shardID) var r0 persistence.ExecutionManager if rf, ok := ret.Get(0).(func(int) persistence.ExecutionManager); ok { r0 = rf(shardID) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(persistence.ExecutionManager) } } var r1 error if rf, ok := ret.Get(1).(func(int) error); ok { r1 = rf(shardID) } else { r1 = ret.Error(1) } return r0, r1 } // Close is mock implementation for Close of ExecutionManagerFactory func (_m *ExecutionManagerFactory) Close() { _m.Called() } ================================================ FILE: common/mocks/HistoryV2Manager.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Code generated by mockery v2.2.1. DO NOT EDIT. package mocks import ( context "context" mock "github.com/stretchr/testify/mock" persistence "github.com/uber/cadence/common/persistence" ) // HistoryV2Manager is an autogenerated mock type for the HistoryV2Manager type // TODO: rename to HistoryManager type HistoryV2Manager struct { mock.Mock } // AppendHistoryNodes provides a mock function with given fields: ctx, request func (_m *HistoryV2Manager) AppendHistoryNodes(ctx context.Context, request *persistence.AppendHistoryNodesRequest) (*persistence.AppendHistoryNodesResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.AppendHistoryNodesResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.AppendHistoryNodesRequest) *persistence.AppendHistoryNodesResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.AppendHistoryNodesResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.AppendHistoryNodesRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // Close provides a mock function with given fields: func (_m *HistoryV2Manager) Close() { _m.Called() } // DeleteHistoryBranch provides a mock function with given fields: ctx, request func (_m *HistoryV2Manager) DeleteHistoryBranch(ctx context.Context, request *persistence.DeleteHistoryBranchRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.DeleteHistoryBranchRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // ForkHistoryBranch provides a mock function with given fields: ctx, request func (_m *HistoryV2Manager) ForkHistoryBranch(ctx context.Context, request *persistence.ForkHistoryBranchRequest) (*persistence.ForkHistoryBranchResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.ForkHistoryBranchResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.ForkHistoryBranchRequest) *persistence.ForkHistoryBranchResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ForkHistoryBranchResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.ForkHistoryBranchRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetAllHistoryTreeBranches provides a mock function with given fields: ctx, request func (_m *HistoryV2Manager) GetAllHistoryTreeBranches(ctx context.Context, request *persistence.GetAllHistoryTreeBranchesRequest) (*persistence.GetAllHistoryTreeBranchesResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetAllHistoryTreeBranchesResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetAllHistoryTreeBranchesRequest) *persistence.GetAllHistoryTreeBranchesResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetAllHistoryTreeBranchesResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetAllHistoryTreeBranchesRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetHistoryTree provides a mock function with given fields: ctx, request func (_m *HistoryV2Manager) GetHistoryTree(ctx context.Context, request *persistence.GetHistoryTreeRequest) (*persistence.GetHistoryTreeResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetHistoryTreeResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetHistoryTreeRequest) *persistence.GetHistoryTreeResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetHistoryTreeResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetHistoryTreeRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetName provides a mock function with given fields: func (_m *HistoryV2Manager) GetName() string { ret := _m.Called() var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } return r0 } // ReadHistoryBranch provides a mock function with given fields: ctx, request func (_m *HistoryV2Manager) ReadHistoryBranch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (*persistence.ReadHistoryBranchResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.ReadHistoryBranchResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.ReadHistoryBranchRequest) *persistence.ReadHistoryBranchResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ReadHistoryBranchResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.ReadHistoryBranchRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ReadHistoryBranchByBatch provides a mock function with given fields: ctx, request func (_m *HistoryV2Manager) ReadHistoryBranchByBatch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (*persistence.ReadHistoryBranchByBatchResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.ReadHistoryBranchByBatchResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.ReadHistoryBranchRequest) *persistence.ReadHistoryBranchByBatchResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ReadHistoryBranchByBatchResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.ReadHistoryBranchRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ReadRawHistoryBranch provides a mock function with given fields: ctx, request func (_m *HistoryV2Manager) ReadRawHistoryBranch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (*persistence.ReadRawHistoryBranchResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.ReadRawHistoryBranchResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.ReadHistoryBranchRequest) *persistence.ReadRawHistoryBranchResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ReadRawHistoryBranchResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.ReadHistoryBranchRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } ================================================ FILE: common/mocks/KafkaProducer.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mocks import ( context "context" "github.com/stretchr/testify/mock" "github.com/uber/cadence/common/messaging" ) // KafkaProducer is an autogenerated mock type for the KafkaProducer type type KafkaProducer struct { mock.Mock } // Close provides a mock function with given fields: func (_m *KafkaProducer) Close() error { ret := _m.Called() var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() } else { r0 = ret.Error(0) } return r0 } // Publish provides a mock function with given fields: ctx, message func (_m *KafkaProducer) Publish(ctx context.Context, message interface{}) error { ret := _m.Called(ctx, message) var r0 error if rf, ok := ret.Get(0).(func(context.Context, interface{}) error); ok { r0 = rf(ctx, message) } else { r0 = ret.Error(0) } return r0 } var _ messaging.Producer = (*KafkaProducer)(nil) ================================================ FILE: common/mocks/MessagingClient.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mocks import "github.com/uber/cadence/common/messaging" type ( // MessagingClient is the mock implementation for Service interface MessagingClient struct { consumerMock messaging.Consumer publisherMock messaging.Producer } ) var _ messaging.Client = (*MessagingClient)(nil) // NewMockMessagingClient generate a dummy implementation of messaging client func NewMockMessagingClient(publisher messaging.Producer, consumer messaging.Consumer) messaging.Client { return &MessagingClient{ publisherMock: publisher, consumerMock: consumer, } } // NewConsumer generates a dummy implementation of kafka consumer func (c *MessagingClient) NewConsumer(appName, consumerName string) (messaging.Consumer, error) { return c.consumerMock, nil } // NewProducer generates a dummy implementation of kafka producer func (c *MessagingClient) NewProducer(appName string) (messaging.Producer, error) { return c.publisherMock, nil } ================================================ FILE: common/mocks/MetadataManager.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Code generated by mockery v2.2.1. DO NOT EDIT. package mocks import ( context "context" mock "github.com/stretchr/testify/mock" persistence "github.com/uber/cadence/common/persistence" ) // MetadataManager is an autogenerated mock type for the MetadataManager type type MetadataManager struct { mock.Mock } // Close provides a mock function with given fields: func (_m *MetadataManager) Close() { _m.Called() } // CreateDomain provides a mock function with given fields: ctx, request func (_m *MetadataManager) CreateDomain(ctx context.Context, request *persistence.CreateDomainRequest) (*persistence.CreateDomainResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.CreateDomainResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.CreateDomainRequest) *persistence.CreateDomainResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.CreateDomainResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.CreateDomainRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteDomain provides a mock function with given fields: ctx, request func (_m *MetadataManager) DeleteDomain(ctx context.Context, request *persistence.DeleteDomainRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.DeleteDomainRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // DeleteDomainByName provides a mock function with given fields: ctx, request func (_m *MetadataManager) DeleteDomainByName(ctx context.Context, request *persistence.DeleteDomainByNameRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.DeleteDomainByNameRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // GetDomain provides a mock function with given fields: ctx, request func (_m *MetadataManager) GetDomain(ctx context.Context, request *persistence.GetDomainRequest) (*persistence.GetDomainResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetDomainResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetDomainRequest) *persistence.GetDomainResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetDomainResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetDomainRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetMetadata provides a mock function with given fields: ctx func (_m *MetadataManager) GetMetadata(ctx context.Context) (*persistence.GetMetadataResponse, error) { ret := _m.Called(ctx) var r0 *persistence.GetMetadataResponse if rf, ok := ret.Get(0).(func(context.Context) *persistence.GetMetadataResponse); ok { r0 = rf(ctx) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetMetadataResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { r1 = ret.Error(1) } return r0, r1 } // GetName provides a mock function with given fields: func (_m *MetadataManager) GetName() string { ret := _m.Called() var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } return r0 } // ListDomains provides a mock function with given fields: ctx, request func (_m *MetadataManager) ListDomains(ctx context.Context, request *persistence.ListDomainsRequest) (*persistence.ListDomainsResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.ListDomainsResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListDomainsRequest) *persistence.ListDomainsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListDomainsResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListDomainsRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // UpdateDomain provides a mock function with given fields: ctx, request func (_m *MetadataManager) UpdateDomain(ctx context.Context, request *persistence.UpdateDomainRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.UpdateDomainRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } ================================================ FILE: common/mocks/ShardManager.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Code generated by mockery v2.2.1. DO NOT EDIT. package mocks import ( context "context" mock "github.com/stretchr/testify/mock" persistence "github.com/uber/cadence/common/persistence" ) // ShardManager is an autogenerated mock type for the ShardManager type type ShardManager struct { mock.Mock } // Close provides a mock function with given fields: func (_m *ShardManager) Close() { _m.Called() } // CreateShard provides a mock function with given fields: ctx, request func (_m *ShardManager) CreateShard(ctx context.Context, request *persistence.CreateShardRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.CreateShardRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // GetName provides a mock function with given fields: func (_m *ShardManager) GetName() string { ret := _m.Called() var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } return r0 } // GetShard provides a mock function with given fields: ctx, request func (_m *ShardManager) GetShard(ctx context.Context, request *persistence.GetShardRequest) (*persistence.GetShardResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetShardResponse if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetShardRequest) *persistence.GetShardResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetShardResponse) } } var r1 error if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetShardRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // UpdateShard provides a mock function with given fields: ctx, request func (_m *ShardManager) UpdateShard(ctx context.Context, request *persistence.UpdateShardRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.UpdateShardRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } ================================================ FILE: common/mocks/TaskManager.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Code generated by mockery v2.32.0. DO NOT EDIT. package mocks import ( context "context" mock "github.com/stretchr/testify/mock" persistence "github.com/uber/cadence/common/persistence" ) // TaskManager is an autogenerated mock type for the TaskManager type type TaskManager struct { mock.Mock } // Close provides a mock function with given fields: func (_m *TaskManager) Close() { _m.Called() } // CompleteTask provides a mock function with given fields: ctx, request func (_m *TaskManager) CompleteTask(ctx context.Context, request *persistence.CompleteTaskRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.CompleteTaskRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // CompleteTasksLessThan provides a mock function with given fields: ctx, request func (_m *TaskManager) CompleteTasksLessThan(ctx context.Context, request *persistence.CompleteTasksLessThanRequest) (*persistence.CompleteTasksLessThanResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.CompleteTasksLessThanResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.CompleteTasksLessThanRequest) (*persistence.CompleteTasksLessThanResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.CompleteTasksLessThanRequest) *persistence.CompleteTasksLessThanResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.CompleteTasksLessThanResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.CompleteTasksLessThanRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // CreateTasks provides a mock function with given fields: ctx, request func (_m *TaskManager) CreateTasks(ctx context.Context, request *persistence.CreateTasksRequest) (*persistence.CreateTasksResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.CreateTasksResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.CreateTasksRequest) (*persistence.CreateTasksResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.CreateTasksRequest) *persistence.CreateTasksResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.CreateTasksResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.CreateTasksRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteTaskList provides a mock function with given fields: ctx, request func (_m *TaskManager) DeleteTaskList(ctx context.Context, request *persistence.DeleteTaskListRequest) error { ret := _m.Called(ctx, request) var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.DeleteTaskListRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // GetName provides a mock function with given fields: func (_m *TaskManager) GetName() string { ret := _m.Called() var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } return r0 } // GetOrphanTasks provides a mock function with given fields: ctx, request func (_m *TaskManager) GetOrphanTasks(ctx context.Context, request *persistence.GetOrphanTasksRequest) (*persistence.GetOrphanTasksResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetOrphanTasksResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetOrphanTasksRequest) (*persistence.GetOrphanTasksResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetOrphanTasksRequest) *persistence.GetOrphanTasksResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetOrphanTasksResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetOrphanTasksRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetTaskList provides a mock function with given fields: ctx, request func (_m *TaskManager) GetTaskList(ctx context.Context, request *persistence.GetTaskListRequest) (*persistence.GetTaskListResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetTaskListResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetTaskListRequest) (*persistence.GetTaskListResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetTaskListRequest) *persistence.GetTaskListResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetTaskListResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetTaskListRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetTaskListSize provides a mock function with given fields: ctx, request func (_m *TaskManager) GetTaskListSize(ctx context.Context, request *persistence.GetTaskListSizeRequest) (*persistence.GetTaskListSizeResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetTaskListSizeResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetTaskListSizeRequest) (*persistence.GetTaskListSizeResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetTaskListSizeRequest) *persistence.GetTaskListSizeResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetTaskListSizeResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetTaskListSizeRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetTasks provides a mock function with given fields: ctx, request func (_m *TaskManager) GetTasks(ctx context.Context, request *persistence.GetTasksRequest) (*persistence.GetTasksResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.GetTasksResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetTasksRequest) (*persistence.GetTasksResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetTasksRequest) *persistence.GetTasksResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetTasksResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetTasksRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // LeaseTaskList provides a mock function with given fields: ctx, request func (_m *TaskManager) LeaseTaskList(ctx context.Context, request *persistence.LeaseTaskListRequest) (*persistence.LeaseTaskListResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.LeaseTaskListResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.LeaseTaskListRequest) (*persistence.LeaseTaskListResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.LeaseTaskListRequest) *persistence.LeaseTaskListResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.LeaseTaskListResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.LeaseTaskListRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ListTaskList provides a mock function with given fields: ctx, request func (_m *TaskManager) ListTaskList(ctx context.Context, request *persistence.ListTaskListRequest) (*persistence.ListTaskListResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.ListTaskListResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListTaskListRequest) (*persistence.ListTaskListResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListTaskListRequest) *persistence.ListTaskListResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListTaskListResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListTaskListRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // UpdateTaskList provides a mock function with given fields: ctx, request func (_m *TaskManager) UpdateTaskList(ctx context.Context, request *persistence.UpdateTaskListRequest) (*persistence.UpdateTaskListResponse, error) { ret := _m.Called(ctx, request) var r0 *persistence.UpdateTaskListResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.UpdateTaskListRequest) (*persistence.UpdateTaskListResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.UpdateTaskListRequest) *persistence.UpdateTaskListResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.UpdateTaskListResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.UpdateTaskListRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // NewTaskManager creates a new instance of TaskManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewTaskManager(t interface { mock.TestingT Cleanup(func()) }) *TaskManager { mock := &TaskManager{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) return mock } ================================================ FILE: common/mocks/VisibilityManager.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Code generated by mockery v2.43.1. DO NOT EDIT. package mocks import ( context "context" mock "github.com/stretchr/testify/mock" persistence "github.com/uber/cadence/common/persistence" ) // VisibilityManager is an autogenerated mock type for the VisibilityManager type type VisibilityManager struct { mock.Mock } // Close provides a mock function with given fields: func (_m *VisibilityManager) Close() { _m.Called() } // CountWorkflowExecutions provides a mock function with given fields: ctx, request func (_m *VisibilityManager) CountWorkflowExecutions(ctx context.Context, request *persistence.CountWorkflowExecutionsRequest) (*persistence.CountWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for CountWorkflowExecutions") } var r0 *persistence.CountWorkflowExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.CountWorkflowExecutionsRequest) (*persistence.CountWorkflowExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.CountWorkflowExecutionsRequest) *persistence.CountWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.CountWorkflowExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.CountWorkflowExecutionsRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // DeleteUninitializedWorkflowExecution provides a mock function with given fields: ctx, request func (_m *VisibilityManager) DeleteUninitializedWorkflowExecution(ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest) error { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for DeleteUninitializedWorkflowExecution") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.VisibilityDeleteWorkflowExecutionRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // DeleteWorkflowExecution provides a mock function with given fields: ctx, request func (_m *VisibilityManager) DeleteWorkflowExecution(ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest) error { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for DeleteWorkflowExecution") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.VisibilityDeleteWorkflowExecutionRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // GetClosedWorkflowExecution provides a mock function with given fields: ctx, request func (_m *VisibilityManager) GetClosedWorkflowExecution(ctx context.Context, request *persistence.GetClosedWorkflowExecutionRequest) (*persistence.GetClosedWorkflowExecutionResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for GetClosedWorkflowExecution") } var r0 *persistence.GetClosedWorkflowExecutionResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetClosedWorkflowExecutionRequest) (*persistence.GetClosedWorkflowExecutionResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.GetClosedWorkflowExecutionRequest) *persistence.GetClosedWorkflowExecutionResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.GetClosedWorkflowExecutionResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.GetClosedWorkflowExecutionRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // GetName provides a mock function with given fields: func (_m *VisibilityManager) GetName() string { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for GetName") } var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } return r0 } // ListClosedWorkflowExecutions provides a mock function with given fields: ctx, request func (_m *VisibilityManager) ListClosedWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsRequest) (*persistence.ListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for ListClosedWorkflowExecutions") } var r0 *persistence.ListWorkflowExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsRequest) (*persistence.ListWorkflowExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsRequest) *persistence.ListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListWorkflowExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListWorkflowExecutionsRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ListClosedWorkflowExecutionsByStatus provides a mock function with given fields: ctx, request func (_m *VisibilityManager) ListClosedWorkflowExecutionsByStatus(ctx context.Context, request *persistence.ListClosedWorkflowExecutionsByStatusRequest) (*persistence.ListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for ListClosedWorkflowExecutionsByStatus") } var r0 *persistence.ListWorkflowExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListClosedWorkflowExecutionsByStatusRequest) (*persistence.ListWorkflowExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListClosedWorkflowExecutionsByStatusRequest) *persistence.ListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListWorkflowExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListClosedWorkflowExecutionsByStatusRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ListClosedWorkflowExecutionsByType provides a mock function with given fields: ctx, request func (_m *VisibilityManager) ListClosedWorkflowExecutionsByType(ctx context.Context, request *persistence.ListWorkflowExecutionsByTypeRequest) (*persistence.ListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for ListClosedWorkflowExecutionsByType") } var r0 *persistence.ListWorkflowExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByTypeRequest) (*persistence.ListWorkflowExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByTypeRequest) *persistence.ListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListWorkflowExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListWorkflowExecutionsByTypeRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ListClosedWorkflowExecutionsByWorkflowID provides a mock function with given fields: ctx, request func (_m *VisibilityManager) ListClosedWorkflowExecutionsByWorkflowID(ctx context.Context, request *persistence.ListWorkflowExecutionsByWorkflowIDRequest) (*persistence.ListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for ListClosedWorkflowExecutionsByWorkflowID") } var r0 *persistence.ListWorkflowExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByWorkflowIDRequest) (*persistence.ListWorkflowExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByWorkflowIDRequest) *persistence.ListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListWorkflowExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListWorkflowExecutionsByWorkflowIDRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ListOpenWorkflowExecutions provides a mock function with given fields: ctx, request func (_m *VisibilityManager) ListOpenWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsRequest) (*persistence.ListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for ListOpenWorkflowExecutions") } var r0 *persistence.ListWorkflowExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsRequest) (*persistence.ListWorkflowExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsRequest) *persistence.ListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListWorkflowExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListWorkflowExecutionsRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ListOpenWorkflowExecutionsByType provides a mock function with given fields: ctx, request func (_m *VisibilityManager) ListOpenWorkflowExecutionsByType(ctx context.Context, request *persistence.ListWorkflowExecutionsByTypeRequest) (*persistence.ListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for ListOpenWorkflowExecutionsByType") } var r0 *persistence.ListWorkflowExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByTypeRequest) (*persistence.ListWorkflowExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByTypeRequest) *persistence.ListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListWorkflowExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListWorkflowExecutionsByTypeRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ListOpenWorkflowExecutionsByWorkflowID provides a mock function with given fields: ctx, request func (_m *VisibilityManager) ListOpenWorkflowExecutionsByWorkflowID(ctx context.Context, request *persistence.ListWorkflowExecutionsByWorkflowIDRequest) (*persistence.ListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for ListOpenWorkflowExecutionsByWorkflowID") } var r0 *persistence.ListWorkflowExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByWorkflowIDRequest) (*persistence.ListWorkflowExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByWorkflowIDRequest) *persistence.ListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListWorkflowExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListWorkflowExecutionsByWorkflowIDRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // ListWorkflowExecutions provides a mock function with given fields: ctx, request func (_m *VisibilityManager) ListWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsByQueryRequest) (*persistence.ListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for ListWorkflowExecutions") } var r0 *persistence.ListWorkflowExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByQueryRequest) (*persistence.ListWorkflowExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByQueryRequest) *persistence.ListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListWorkflowExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListWorkflowExecutionsByQueryRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // RecordWorkflowExecutionClosed provides a mock function with given fields: ctx, request func (_m *VisibilityManager) RecordWorkflowExecutionClosed(ctx context.Context, request *persistence.RecordWorkflowExecutionClosedRequest) error { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for RecordWorkflowExecutionClosed") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.RecordWorkflowExecutionClosedRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // RecordWorkflowExecutionStarted provides a mock function with given fields: ctx, request func (_m *VisibilityManager) RecordWorkflowExecutionStarted(ctx context.Context, request *persistence.RecordWorkflowExecutionStartedRequest) error { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for RecordWorkflowExecutionStarted") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.RecordWorkflowExecutionStartedRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // RecordWorkflowExecutionUninitialized provides a mock function with given fields: ctx, request func (_m *VisibilityManager) RecordWorkflowExecutionUninitialized(ctx context.Context, request *persistence.RecordWorkflowExecutionUninitializedRequest) error { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for RecordWorkflowExecutionUninitialized") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.RecordWorkflowExecutionUninitializedRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // ScanWorkflowExecutions provides a mock function with given fields: ctx, request func (_m *VisibilityManager) ScanWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsByQueryRequest) (*persistence.ListWorkflowExecutionsResponse, error) { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for ScanWorkflowExecutions") } var r0 *persistence.ListWorkflowExecutionsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByQueryRequest) (*persistence.ListWorkflowExecutionsResponse, error)); ok { return rf(ctx, request) } if rf, ok := ret.Get(0).(func(context.Context, *persistence.ListWorkflowExecutionsByQueryRequest) *persistence.ListWorkflowExecutionsResponse); ok { r0 = rf(ctx, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*persistence.ListWorkflowExecutionsResponse) } } if rf, ok := ret.Get(1).(func(context.Context, *persistence.ListWorkflowExecutionsByQueryRequest) error); ok { r1 = rf(ctx, request) } else { r1 = ret.Error(1) } return r0, r1 } // UpsertWorkflowExecution provides a mock function with given fields: ctx, request func (_m *VisibilityManager) UpsertWorkflowExecution(ctx context.Context, request *persistence.UpsertWorkflowExecutionRequest) error { ret := _m.Called(ctx, request) if len(ret) == 0 { panic("no return value specified for UpsertWorkflowExecution") } var r0 error if rf, ok := ret.Get(0).(func(context.Context, *persistence.UpsertWorkflowExecutionRequest) error); ok { r0 = rf(ctx, request) } else { r0 = ret.Error(0) } return r0 } // NewVisibilityManager creates a new instance of VisibilityManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewVisibilityManager(t interface { mock.TestingT Cleanup(func()) }) *VisibilityManager { mock := &VisibilityManager{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) return mock } ================================================ FILE: common/ndc/history_resender.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination history_resender_mock.go package ndc import ( "context" "errors" "fmt" "time" "github.com/uber/cadence/client" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/types" ) var ( // ErrSkipTask is the error to skip task due to absence of the workflow in the source cluster ErrSkipTask = errors.New("the source workflow does not exist") ) const ( resendContextTimeout = 30 * time.Second defaultPageSize = int32(100) ) type ( // nDCHistoryReplicationFn provides the functionality to deliver replication raw history request to history // the provided func should be thread safe nDCHistoryReplicationFn func(ctx context.Context, request *types.ReplicateEventsV2Request) error // HistoryResender is the interface for resending history events to remote HistoryResender interface { // SendSingleWorkflowHistory sends multiple run IDs's history events to remote SendSingleWorkflowHistory( remoteCluster string, domainID string, workflowID string, runID string, startEventID *int64, startEventVersion *int64, endEventID *int64, endEventVersion *int64, ) error } // HistoryResenderImpl is the implementation of NDCHistoryResender HistoryResenderImpl struct { domainCache cache.DomainCache clientBean client.Bean historyReplicationFn nDCHistoryReplicationFn rereplicationTimeout dynamicproperties.DurationPropertyFnWithDomainIDFilter currentExecutionCheck invariant.Invariant logger log.Logger } historyBatch struct { versionHistory *types.VersionHistory rawEventBatch *types.DataBlob } ) // NewHistoryResender create a new NDCHistoryResenderImpl func NewHistoryResender( domainCache cache.DomainCache, clientBean client.Bean, historyReplicationFn nDCHistoryReplicationFn, rereplicationTimeout dynamicproperties.DurationPropertyFnWithDomainIDFilter, currentExecutionCheck invariant.Invariant, logger log.Logger, ) *HistoryResenderImpl { return &HistoryResenderImpl{ domainCache: domainCache, clientBean: clientBean, historyReplicationFn: historyReplicationFn, rereplicationTimeout: rereplicationTimeout, currentExecutionCheck: currentExecutionCheck, logger: logger.WithTags(tag.ComponentHistoryResender), } } // SendSingleWorkflowHistory sends one run IDs's history events to remote func (n *HistoryResenderImpl) SendSingleWorkflowHistory( remoteCluster string, domainID string, workflowID string, runID string, startEventID *int64, startEventVersion *int64, endEventID *int64, endEventVersion *int64, ) error { domainName, err := n.domainCache.GetDomainName(domainID) if err != nil { return fmt.Errorf("getting domain: %w", err) } ctx := context.Background() var cancel context.CancelFunc if n.rereplicationTimeout != nil { resendContextTimeout := n.rereplicationTimeout(domainID) if resendContextTimeout > 0 { ctx, cancel = context.WithTimeout(ctx, resendContextTimeout) defer cancel() } } historyIterator := collection.NewPagingIterator(n.getPaginationFn( ctx, remoteCluster, domainName, workflowID, runID, startEventID, startEventVersion, endEventID, endEventVersion)) for historyIterator.HasNext() { result, err := historyIterator.Next() if err != nil { return fmt.Errorf("history iterator: %w", err) } historyBatch := result.(*historyBatch) replicationRequest := n.createReplicationRawRequest( domainID, workflowID, runID, historyBatch.rawEventBatch, historyBatch.versionHistory.GetItems()) err = n.sendReplicationRawRequest(ctx, replicationRequest) switch err.(type) { case nil: // continue to process the events break case *types.EntityNotExistsError: // Case 1: the workflow pass the retention period // Case 2: the workflow is corrupted if skipTask := n.fixCurrentExecution(ctx, domainID, workflowID, runID); skipTask { return ErrSkipTask } return err default: return fmt.Errorf("sending replication request: %w", err) } } return nil } func (n *HistoryResenderImpl) getPaginationFn( ctx context.Context, remoteCluster string, domainName string, workflowID string, runID string, startEventID *int64, startEventVersion *int64, endEventID *int64, endEventVersion *int64, ) collection.PaginationFn { return func(paginationToken []byte) ([]interface{}, []byte, error) { response, err := n.getHistory( ctx, remoteCluster, domainName, workflowID, runID, startEventID, startEventVersion, endEventID, endEventVersion, paginationToken, defaultPageSize, ) if err != nil { return nil, nil, err } var paginateItems []interface{} versionHistory := response.GetVersionHistory() for _, history := range response.GetHistoryBatches() { batch := &historyBatch{ versionHistory: versionHistory, rawEventBatch: history, } paginateItems = append(paginateItems, batch) } return paginateItems, response.NextPageToken, nil } } func (n *HistoryResenderImpl) createReplicationRawRequest( domainID string, workflowID string, runID string, historyBlob *types.DataBlob, versionHistoryItems []*types.VersionHistoryItem, ) *types.ReplicateEventsV2Request { request := &types.ReplicateEventsV2Request{ DomainUUID: domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Events: historyBlob, VersionHistoryItems: versionHistoryItems, } return request } func (n *HistoryResenderImpl) sendReplicationRawRequest( ctx context.Context, request *types.ReplicateEventsV2Request, ) error { ctx, cancel := context.WithTimeout(ctx, resendContextTimeout) defer cancel() return n.historyReplicationFn(ctx, request) } func (n *HistoryResenderImpl) getHistory( ctx context.Context, remoteCluster string, domainName string, workflowID string, runID string, startEventID *int64, startEventVersion *int64, endEventID *int64, endEventVersion *int64, token []byte, pageSize int32, ) (*types.GetWorkflowExecutionRawHistoryV2Response, error) { ctx, cancel := context.WithTimeout(ctx, resendContextTimeout) defer cancel() adminClient, err := n.clientBean.GetRemoteAdminClient(remoteCluster) if err != nil { return nil, err } response, err := adminClient.GetWorkflowExecutionRawHistoryV2(ctx, &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: domainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, StartEventID: startEventID, StartEventVersion: startEventVersion, EndEventID: endEventID, EndEventVersion: endEventVersion, MaximumPageSize: pageSize, NextPageToken: token, }) if err != nil { return nil, fmt.Errorf("getting history: %w", err) } return response, nil } func (n *HistoryResenderImpl) fixCurrentExecution( ctx context.Context, domainID string, workflowID string, runID string, ) bool { if n.currentExecutionCheck == nil { return false } execution := &entity.CurrentExecution{ Execution: entity.Execution{ DomainID: domainID, WorkflowID: workflowID, State: persistence.WorkflowStateRunning, }, } res := n.currentExecutionCheck.Check(ctx, execution) switch res.CheckResultType { case invariant.CheckResultTypeCorrupted: n.logger.Error( "Encounter corrupted workflow", tag.WorkflowDomainID(domainID), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), ) n.currentExecutionCheck.Fix(ctx, execution) return false case invariant.CheckResultTypeFailed: return false default: return true } } ================================================ FILE: common/ndc/history_resender_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: history_resender.go // // Generated by this command: // // mockgen -package ndc -source history_resender.go -destination history_resender_mock.go // // Package ndc is a generated GoMock package. package ndc import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockHistoryResender is a mock of HistoryResender interface. type MockHistoryResender struct { ctrl *gomock.Controller recorder *MockHistoryResenderMockRecorder isgomock struct{} } // MockHistoryResenderMockRecorder is the mock recorder for MockHistoryResender. type MockHistoryResenderMockRecorder struct { mock *MockHistoryResender } // NewMockHistoryResender creates a new mock instance. func NewMockHistoryResender(ctrl *gomock.Controller) *MockHistoryResender { mock := &MockHistoryResender{ctrl: ctrl} mock.recorder = &MockHistoryResenderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHistoryResender) EXPECT() *MockHistoryResenderMockRecorder { return m.recorder } // SendSingleWorkflowHistory mocks base method. func (m *MockHistoryResender) SendSingleWorkflowHistory(remoteCluster, domainID, workflowID, runID string, startEventID, startEventVersion, endEventID, endEventVersion *int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendSingleWorkflowHistory", remoteCluster, domainID, workflowID, runID, startEventID, startEventVersion, endEventID, endEventVersion) ret0, _ := ret[0].(error) return ret0 } // SendSingleWorkflowHistory indicates an expected call of SendSingleWorkflowHistory. func (mr *MockHistoryResenderMockRecorder) SendSingleWorkflowHistory(remoteCluster, domainID, workflowID, runID, startEventID, startEventVersion, endEventID, endEventVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendSingleWorkflowHistory", reflect.TypeOf((*MockHistoryResender)(nil).SendSingleWorkflowHistory), remoteCluster, domainID, workflowID, runID, startEventID, startEventVersion, endEventID, endEventVersion) } ================================================ FILE: common/ndc/history_resender_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/client" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/types" ) type ( historyResenderSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockDomainCache *cache.MockDomainCache mockClientBean *client.MockBean mockAdminClient *admin.MockClient mockHistoryClient *history.MockClient domainID string domainName string serializer persistence.PayloadSerializer logger log.Logger rereplicator *HistoryResenderImpl } ) func TestHistoryResenderSuite(t *testing.T) { s := new(historyResenderSuite) suite.Run(t, s) } func (s *historyResenderSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockAdminClient = admin.NewMockClient(s.controller) s.mockHistoryClient = history.NewMockClient(s.controller) s.mockDomainCache = cache.NewMockDomainCache(s.controller) s.mockClientBean = client.NewMockBean(s.controller) s.logger = testlogger.New(s.Suite.T()) s.domainID = uuid.New() s.domainName = "some random domain name" domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: s.domainID, Name: s.domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, ) s.mockDomainCache.EXPECT().GetDomainName(s.domainID).Return(s.domainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(s.domainName).Return(domainEntry, nil).AnyTimes() s.serializer = persistence.NewPayloadSerializer() s.rereplicator = NewHistoryResender( s.mockDomainCache, s.mockClientBean, func(ctx context.Context, request *types.ReplicateEventsV2Request) error { return s.mockHistoryClient.ReplicateEventsV2(ctx, request) }, nil, nil, s.logger, ) } func (s *historyResenderSuite) TearDownTest() { s.controller.Finish() } func (s *historyResenderSuite) TestSendSingleWorkflowHistory() { remoteCluster := "remote-cluster" workflowID := "some random workflow ID" runID := uuid.New() startEventID := int64(123) startEventVersion := int64(100) token := []byte{1} pageSize := defaultPageSize eventBatch := []*types.HistoryEvent{ { ID: 2, Version: 123, Timestamp: common.Int64Ptr(time.Now().UnixNano()), EventType: types.EventTypeDecisionTaskScheduled.Ptr(), }, { ID: 3, Version: 123, Timestamp: common.Int64Ptr(time.Now().UnixNano()), EventType: types.EventTypeDecisionTaskStarted.Ptr(), }, } blob := s.serializeEvents(eventBatch) versionHistoryItems := []*types.VersionHistoryItem{ { EventID: 1, Version: 1, }, } s.mockClientBean.EXPECT().GetRemoteAdminClient(remoteCluster).Return(s.mockAdminClient, nil).Times(2) s.mockAdminClient.EXPECT().GetWorkflowExecutionRawHistoryV2( gomock.Any(), &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, StartEventID: common.Int64Ptr(startEventID), StartEventVersion: common.Int64Ptr(startEventVersion), MaximumPageSize: pageSize, NextPageToken: nil, }).Return(&types.GetWorkflowExecutionRawHistoryV2Response{ HistoryBatches: []*types.DataBlob{blob}, NextPageToken: token, VersionHistory: &types.VersionHistory{ Items: versionHistoryItems, }, }, nil).Times(1) s.mockAdminClient.EXPECT().GetWorkflowExecutionRawHistoryV2( gomock.Any(), &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, StartEventID: common.Int64Ptr(startEventID), StartEventVersion: common.Int64Ptr(startEventVersion), MaximumPageSize: pageSize, NextPageToken: token, }).Return(&types.GetWorkflowExecutionRawHistoryV2Response{ HistoryBatches: []*types.DataBlob{blob}, NextPageToken: nil, VersionHistory: &types.VersionHistory{ Items: versionHistoryItems, }, }, nil).Times(1) s.mockHistoryClient.EXPECT().ReplicateEventsV2( gomock.Any(), &types.ReplicateEventsV2Request{ DomainUUID: s.domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, VersionHistoryItems: versionHistoryItems, Events: blob, }).Return(nil).Times(2) err := s.rereplicator.SendSingleWorkflowHistory( remoteCluster, s.domainID, workflowID, runID, common.Int64Ptr(startEventID), common.Int64Ptr(startEventVersion), nil, nil, ) s.Nil(err) } func (s *historyResenderSuite) TestCreateReplicateRawEventsRequest() { workflowID := "some random workflow ID" runID := uuid.New() blob := &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("some random history blob"), } versionHistoryItems := []*types.VersionHistoryItem{ { EventID: 1, Version: 1, }, } s.Equal(&types.ReplicateEventsV2Request{ DomainUUID: s.domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, VersionHistoryItems: versionHistoryItems, Events: blob, }, s.rereplicator.createReplicationRawRequest( s.domainID, workflowID, runID, blob, versionHistoryItems)) } func (s *historyResenderSuite) TestSendReplicationRawRequest() { workflowID := "some random workflow ID" runID := uuid.New() item := &types.VersionHistoryItem{ EventID: 1, Version: 1, } request := &types.ReplicateEventsV2Request{ DomainUUID: s.domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Events: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("some random history blob"), }, VersionHistoryItems: []*types.VersionHistoryItem{item}, } s.mockHistoryClient.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(nil).Times(1) err := s.rereplicator.sendReplicationRawRequest(context.Background(), request) s.Nil(err) } func (s *historyResenderSuite) TestSendReplicationRawRequest_Err() { workflowID := "some random workflow ID" runID := uuid.New() item := &types.VersionHistoryItem{ EventID: 1, Version: 1, } request := &types.ReplicateEventsV2Request{ DomainUUID: s.domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Events: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("some random history blob"), }, VersionHistoryItems: []*types.VersionHistoryItem{item}, } retryErr := &types.RetryTaskV2Error{ DomainID: s.domainID, WorkflowID: workflowID, RunID: runID, } s.mockHistoryClient.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(retryErr).Times(1) err := s.rereplicator.sendReplicationRawRequest(context.Background(), request) s.Equal(retryErr, err) } func (s *historyResenderSuite) TestGetHistory() { remoteCluster := "remote-cluster" workflowID := "some random workflow ID" runID := uuid.New() startEventID := int64(123) endEventID := int64(345) version := int64(20) nextTokenIn := []byte("some random next token in") nextTokenOut := []byte("some random next token out") pageSize := int32(59) blob := []byte("some random events blob") encodingTypeThriftRW := types.EncodingTypeThriftRW response := &types.GetWorkflowExecutionRawHistoryV2Response{ HistoryBatches: []*types.DataBlob{&types.DataBlob{ EncodingType: &encodingTypeThriftRW, Data: blob, }}, NextPageToken: nextTokenOut, } s.mockClientBean.EXPECT().GetRemoteAdminClient(remoteCluster).Return(s.mockAdminClient, nil).Times(1) s.mockAdminClient.EXPECT().GetWorkflowExecutionRawHistoryV2(gomock.Any(), &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, StartEventID: common.Int64Ptr(startEventID), StartEventVersion: common.Int64Ptr(version), EndEventID: common.Int64Ptr(endEventID), EndEventVersion: common.Int64Ptr(version), MaximumPageSize: pageSize, NextPageToken: nextTokenIn, }).Return(response, nil).Times(1) out, err := s.rereplicator.getHistory( context.Background(), remoteCluster, s.domainName, workflowID, runID, &startEventID, &version, &endEventID, &version, nextTokenIn, pageSize) s.Nil(err) s.Equal(response, out) } func (s *historyResenderSuite) TestCurrentExecutionCheck() { domainID := uuid.New() workflowID1 := uuid.New() workflowID2 := uuid.New() runID := uuid.New() invariantMock := invariant.NewMockInvariant(s.controller) s.rereplicator = NewHistoryResender( s.mockDomainCache, s.mockClientBean, func(ctx context.Context, request *types.ReplicateEventsV2Request) error { return s.mockHistoryClient.ReplicateEventsV2(ctx, request) }, nil, invariantMock, s.logger, ) execution1 := &entity.CurrentExecution{ Execution: entity.Execution{ DomainID: domainID, WorkflowID: workflowID1, State: persistence.WorkflowStateRunning, }, } execution2 := &entity.CurrentExecution{ Execution: entity.Execution{ DomainID: domainID, WorkflowID: workflowID2, State: persistence.WorkflowStateRunning, }, } invariantMock.EXPECT().Check(gomock.Any(), execution1).Return(invariant.CheckResult{ CheckResultType: invariant.CheckResultTypeCorrupted, }).Times(1) invariantMock.EXPECT().Check(gomock.Any(), execution2).Return(invariant.CheckResult{ CheckResultType: invariant.CheckResultTypeHealthy, }).Times(1) invariantMock.EXPECT().Fix(gomock.Any(), gomock.Any()).Return(invariant.FixResult{}).Times(1) skipTask := s.rereplicator.fixCurrentExecution(context.Background(), domainID, workflowID1, runID) s.False(skipTask) skipTask = s.rereplicator.fixCurrentExecution(context.Background(), domainID, workflowID2, runID) s.True(skipTask) } func (s *historyResenderSuite) serializeEvents(events []*types.HistoryEvent) *types.DataBlob { blob, err := s.serializer.SerializeBatchEvents(events, constants.EncodingTypeThriftRW) s.Nil(err) return &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: blob.Data, } } ================================================ FILE: common/pagination/interface.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination mocks.go -self_package github.com/uber/cadence/common/pagination package pagination import ( "context" "errors" ) // ErrIteratorFinished indicates that Next was called on a finished iterator var ErrIteratorFinished = errors.New("iterator has reached end") type ( // Page contains a PageToken which identifies the current page, // a PageToken which identifies the next page and a list of Entity. Page struct { NextToken PageToken CurrentToken PageToken Entities []Entity } // Entity is a generic type which can be operated on by Iterator and Writer Entity interface{} // PageToken identifies a page PageToken interface{} ) type ( // WriteFn writes given Page to underlying sink. // The Pages's NextToken will always be nil, its the responsibility of WriteFn to // construct and return the next PageToken, or return an error on failure. WriteFn func(Page) (PageToken, error) // ShouldFlushFn returns true if given page should be flushed false otherwise. ShouldFlushFn func(Page) bool // FetchFn fetches Page from PageToken. // Once a page with nil NextToken is returned no more pages will be fetched. FetchFn func(context.Context, PageToken) (Page, error) ) type ( // Iterator is used to get entities from a collection of pages. // When HasNext returns true it is guaranteed that Next will not return an error. // Once iterator returns an error it will never make progress again and will always return that same error. // Iterator is not thread safe and does not make defensive in or out copies. Iterator interface { Next() (Entity, error) HasNext() bool } // Writer is used to buffer and write entities to underlying store. Writer interface { Add(Entity) error Flush() error FlushIfNotEmpty() error FlushedPages() []PageToken FirstFlushedPage() PageToken LastFlushedPage() PageToken } ) ================================================ FILE: common/pagination/iterator.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pagination import "context" type ( iterator struct { ctx context.Context page Page entityIndex int nextEntity Entity nextError error fetchFn FetchFn } ) // NewIterator constructs a new Iterator func NewIterator( ctx context.Context, startingPageToken PageToken, fetchFn FetchFn, ) Iterator { itr := &iterator{ ctx: ctx, page: Page{ Entities: nil, CurrentToken: nil, NextToken: startingPageToken, }, entityIndex: 0, fetchFn: fetchFn, } itr.advance(true) return itr } // Next returns the next Entity or error. // Returning nil, nil is valid if that is what the provided fetch function provided. func (i *iterator) Next() (Entity, error) { entity := i.nextEntity err := i.nextError i.advance(false) return entity, err } // HasNext returns true if there is a next element. There is considered to be a next element // As long as a fatal error has not occurred and the iterator has not reached the end. func (i *iterator) HasNext() bool { return i.nextError == nil } func (i *iterator) advance(firstPage bool) { if !i.HasNext() && !firstPage { return } if i.entityIndex < len(i.page.Entities) { i.consume() } else { if err := i.advanceToNonEmptyPage(firstPage); err != nil { i.terminate(err) } else { i.consume() } } } func (i *iterator) advanceToNonEmptyPage(firstPage bool) error { if i.page.NextToken == nil && !firstPage { return ErrIteratorFinished } nextPage, err := i.fetchFn(i.ctx, i.page.NextToken) if err != nil { return err } i.page = nextPage if len(i.page.Entities) != 0 { i.entityIndex = 0 return nil } return i.advanceToNonEmptyPage(false) } func (i *iterator) consume() { i.nextEntity = i.page.Entities[i.entityIndex] i.nextError = nil i.entityIndex++ } func (i *iterator) terminate(err error) { i.nextEntity = nil i.nextError = err } ================================================ FILE: common/pagination/iterator_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pagination import ( "context" "errors" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) var fetchMap = map[PageToken][]Entity{ 0: nil, 1: {}, 2: {"one", "two", "three"}, 3: {"four", "five", "six", "seven"}, 4: {"eight"}, } type IteratorSuite struct { *require.Assertions suite.Suite } func TestIteratorSuite(t *testing.T) { suite.Run(t, new(IteratorSuite)) } func (s *IteratorSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *IteratorSuite) TestInitializedToEmpty() { fetchFn := func(_ context.Context, token PageToken) (Page, error) { if token.(int) == 2 { return Page{ CurrentToken: token, NextToken: nil, Entities: nil, }, nil } return Page{ CurrentToken: token, NextToken: token.(int) + 1, Entities: fetchMap[token], }, nil } itr := NewIterator(context.Background(), 0, fetchFn) s.False(itr.HasNext()) _, err := itr.Next() s.Equal(ErrIteratorFinished, err) } func (s *IteratorSuite) TestNonEmptyNoErrors() { fetchFn := func(_ context.Context, token PageToken) (Page, error) { var nextPageToken interface{} = token.(int) + 1 if nextPageToken.(int) == 5 { nextPageToken = nil } return Page{ CurrentToken: token, NextToken: nextPageToken, Entities: fetchMap[token], }, nil } itr := NewIterator(context.Background(), 0, fetchFn) expectedResults := []string{"one", "two", "three", "four", "five", "six", "seven", "eight"} i := 0 for itr.HasNext() { curr, err := itr.Next() s.NoError(err) s.Equal(expectedResults[i], curr.(string)) i++ } s.False(itr.HasNext()) _, err := itr.Next() s.Equal(ErrIteratorFinished, err) } func (s *IteratorSuite) TestNonEmptyWithErrors() { fetchFn := func(_ context.Context, token PageToken) (Page, error) { if token.(int) == 4 { return Page{}, errors.New("got error") } return Page{ CurrentToken: token, NextToken: token.(int) + 1, Entities: fetchMap[token], }, nil } itr := NewIterator(context.Background(), 0, fetchFn) expectedResults := []string{"one", "two", "three", "four", "five", "six", "seven"} i := 0 for itr.HasNext() { curr, err := itr.Next() s.NoError(err) s.Equal(expectedResults[i], curr.(string)) i++ } s.False(itr.HasNext()) curr, err := itr.Next() s.Nil(curr) s.Equal("got error", err.Error()) } ================================================ FILE: common/pagination/mocks.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package pagination -source interface.go -destination mocks.go -self_package github.com/uber/cadence/common/pagination // // Package pagination is a generated GoMock package. package pagination import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockEntity is a mock of Entity interface. type MockEntity struct { ctrl *gomock.Controller recorder *MockEntityMockRecorder isgomock struct{} } // MockEntityMockRecorder is the mock recorder for MockEntity. type MockEntityMockRecorder struct { mock *MockEntity } // NewMockEntity creates a new mock instance. func NewMockEntity(ctrl *gomock.Controller) *MockEntity { mock := &MockEntity{ctrl: ctrl} mock.recorder = &MockEntityMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockEntity) EXPECT() *MockEntityMockRecorder { return m.recorder } // MockPageToken is a mock of PageToken interface. type MockPageToken struct { ctrl *gomock.Controller recorder *MockPageTokenMockRecorder isgomock struct{} } // MockPageTokenMockRecorder is the mock recorder for MockPageToken. type MockPageTokenMockRecorder struct { mock *MockPageToken } // NewMockPageToken creates a new mock instance. func NewMockPageToken(ctrl *gomock.Controller) *MockPageToken { mock := &MockPageToken{ctrl: ctrl} mock.recorder = &MockPageTokenMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPageToken) EXPECT() *MockPageTokenMockRecorder { return m.recorder } // MockIterator is a mock of Iterator interface. type MockIterator struct { ctrl *gomock.Controller recorder *MockIteratorMockRecorder isgomock struct{} } // MockIteratorMockRecorder is the mock recorder for MockIterator. type MockIteratorMockRecorder struct { mock *MockIterator } // NewMockIterator creates a new mock instance. func NewMockIterator(ctrl *gomock.Controller) *MockIterator { mock := &MockIterator{ctrl: ctrl} mock.recorder = &MockIteratorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockIterator) EXPECT() *MockIteratorMockRecorder { return m.recorder } // HasNext mocks base method. func (m *MockIterator) HasNext() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasNext") ret0, _ := ret[0].(bool) return ret0 } // HasNext indicates an expected call of HasNext. func (mr *MockIteratorMockRecorder) HasNext() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasNext", reflect.TypeOf((*MockIterator)(nil).HasNext)) } // Next mocks base method. func (m *MockIterator) Next() (Entity, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Next") ret0, _ := ret[0].(Entity) ret1, _ := ret[1].(error) return ret0, ret1 } // Next indicates an expected call of Next. func (mr *MockIteratorMockRecorder) Next() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Next", reflect.TypeOf((*MockIterator)(nil).Next)) } // MockWriter is a mock of Writer interface. type MockWriter struct { ctrl *gomock.Controller recorder *MockWriterMockRecorder isgomock struct{} } // MockWriterMockRecorder is the mock recorder for MockWriter. type MockWriterMockRecorder struct { mock *MockWriter } // NewMockWriter creates a new mock instance. func NewMockWriter(ctrl *gomock.Controller) *MockWriter { mock := &MockWriter{ctrl: ctrl} mock.recorder = &MockWriterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWriter) EXPECT() *MockWriterMockRecorder { return m.recorder } // Add mocks base method. func (m *MockWriter) Add(arg0 Entity) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Add", arg0) ret0, _ := ret[0].(error) return ret0 } // Add indicates an expected call of Add. func (mr *MockWriterMockRecorder) Add(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockWriter)(nil).Add), arg0) } // FirstFlushedPage mocks base method. func (m *MockWriter) FirstFlushedPage() PageToken { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FirstFlushedPage") ret0, _ := ret[0].(PageToken) return ret0 } // FirstFlushedPage indicates an expected call of FirstFlushedPage. func (mr *MockWriterMockRecorder) FirstFlushedPage() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FirstFlushedPage", reflect.TypeOf((*MockWriter)(nil).FirstFlushedPage)) } // Flush mocks base method. func (m *MockWriter) Flush() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Flush") ret0, _ := ret[0].(error) return ret0 } // Flush indicates an expected call of Flush. func (mr *MockWriterMockRecorder) Flush() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Flush", reflect.TypeOf((*MockWriter)(nil).Flush)) } // FlushIfNotEmpty mocks base method. func (m *MockWriter) FlushIfNotEmpty() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FlushIfNotEmpty") ret0, _ := ret[0].(error) return ret0 } // FlushIfNotEmpty indicates an expected call of FlushIfNotEmpty. func (mr *MockWriterMockRecorder) FlushIfNotEmpty() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushIfNotEmpty", reflect.TypeOf((*MockWriter)(nil).FlushIfNotEmpty)) } // FlushedPages mocks base method. func (m *MockWriter) FlushedPages() []PageToken { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FlushedPages") ret0, _ := ret[0].([]PageToken) return ret0 } // FlushedPages indicates an expected call of FlushedPages. func (mr *MockWriterMockRecorder) FlushedPages() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushedPages", reflect.TypeOf((*MockWriter)(nil).FlushedPages)) } // LastFlushedPage mocks base method. func (m *MockWriter) LastFlushedPage() PageToken { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LastFlushedPage") ret0, _ := ret[0].(PageToken) return ret0 } // LastFlushedPage indicates an expected call of LastFlushedPage. func (mr *MockWriterMockRecorder) LastFlushedPage() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastFlushedPage", reflect.TypeOf((*MockWriter)(nil).LastFlushedPage)) } ================================================ FILE: common/pagination/writer.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pagination type ( writer struct { writeFn WriteFn shouldFlushFn ShouldFlushFn flushedPages []PageToken page Page } ) // NewWriter constructs a new Writer func NewWriter( writeFn WriteFn, shouldFlushFn ShouldFlushFn, startingPage PageToken, ) Writer { return &writer{ writeFn: writeFn, shouldFlushFn: shouldFlushFn, flushedPages: nil, page: Page{ Entities: nil, CurrentToken: startingPage, }, } } // Add adds entity to buffer and flushes if provided shouldFlushFn indicates the page should be flushed. func (w *writer) Add(e Entity) error { w.page.Entities = append(w.page.Entities, e) if !w.shouldFlushFn(w.page) { return nil } return w.Flush() } // Flush flushes the buffer. func (w *writer) Flush() error { nextPageToken, err := w.writeFn(w.page) if err != nil { return err } w.flushedPages = append(w.flushedPages, w.page.CurrentToken) w.page = Page{ Entities: nil, CurrentToken: nextPageToken, } return nil } // FlushIfNotEmpty flushes the buffer if and only if it is not empty func (w *writer) FlushIfNotEmpty() error { if len(w.page.Entities) == 0 { return nil } return w.Flush() } // FlushedPages returns all pages which have been successfully flushed. func (w *writer) FlushedPages() []PageToken { return w.flushedPages } // FirstFlushedPage returns the first page that was flushed or nil if no pages have been flushed. func (w *writer) FirstFlushedPage() PageToken { if len(w.flushedPages) == 0 { return nil } return w.flushedPages[0] } // LastFlushedPage returns the last page that was flushed or nil if no pages have been flushed func (w *writer) LastFlushedPage() PageToken { if len(w.flushedPages) == 0 { return nil } return w.flushedPages[len(w.flushedPages)-1] } ================================================ FILE: common/pagination/writerIterator_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pagination import ( "bytes" "context" "encoding/json" "fmt" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type TestEntity struct { Counter int } var separator = []byte("\r\n") type WriterIteratorSuite struct { *require.Assertions suite.Suite } func TestWriterIteratorSuite(t *testing.T) { suite.Run(t, new(WriterIteratorSuite)) } func (s *WriterIteratorSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *WriterIteratorSuite) TestWriterIterator() { store := make(map[string][]byte) shouldFlushFn := func(page Page) bool { return len(page.Entities) == 10 } pageNum := 0 writeFn := func(page Page) (PageToken, error) { buffer := &bytes.Buffer{} for _, e := range page.Entities { data, err := json.Marshal(e) if err != nil { return nil, err } buffer.Write(data) buffer.Write(separator) } store[page.CurrentToken.(string)] = buffer.Bytes() pageNum++ return fmt.Sprintf("key_%v", pageNum), nil } writer := NewWriter(writeFn, shouldFlushFn, fmt.Sprintf("key_%v", pageNum)) for i := 0; i < 100; i++ { te := TestEntity{ Counter: i, } s.NoError(writer.Add(te)) } flushedKeys := writer.FlushedPages() s.Len(flushedKeys, 10) for i := 0; i < 10; i++ { expectedKey := fmt.Sprintf("key_%v", i) s.Equal(expectedKey, flushedKeys[i].(string)) } fetchFn := func(_ context.Context, token PageToken) (Page, error) { key := flushedKeys[token.(int)] data := store[key.(string)] dataBlobs := bytes.Split(data, separator) var entities []Entity for _, db := range dataBlobs { if len(db) == 0 { continue } var entity TestEntity if err := json.Unmarshal(db, &entity); err != nil { return Page{}, err } entities = append(entities, entity) } var nextPageToken interface{} = token.(int) + 1 if nextPageToken == len(flushedKeys) { nextPageToken = nil } return Page{ CurrentToken: token, NextToken: nextPageToken, Entities: entities, }, nil } itr := NewIterator(context.Background(), 0, fetchFn) itrCount := 0 for itr.HasNext() { val, err := itr.Next() s.NoError(err) te := val.(TestEntity) s.Equal(itrCount, te.Counter) itrCount++ } s.Equal(100, itrCount) } ================================================ FILE: common/pagination/writer_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pagination import ( "errors" "fmt" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type WriterSuite struct { *require.Assertions suite.Suite } func TestWriterSuite(t *testing.T) { suite.Run(t, new(WriterSuite)) } func (s *WriterSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *WriterSuite) TestAddNoFlush() { writeFn := func(_ Page) (PageToken, error) { return nil, nil } shouldFlushFn := func(_ Page) bool { return false } writer := NewWriter(writeFn, shouldFlushFn, nil) s.NoError(writer.Add(1)) s.Empty(writer.FlushedPages()) s.Nil(writer.FirstFlushedPage()) s.Nil(writer.LastFlushedPage()) } func (s *WriterSuite) TestAddAndFlush() { writeFn := func(_ Page) (PageToken, error) { return nil, nil } shouldFlushFn := func(_ Page) bool { return true } writer := NewWriter(writeFn, shouldFlushFn, "test_token") s.NoError(writer.Add(1)) flushedPages := writer.FlushedPages() s.Len(flushedPages, 1) s.Equal("test_token", flushedPages[0].(string)) s.Equal("test_token", writer.FirstFlushedPage().(string)) s.Equal("test_token", writer.LastFlushedPage().(string)) } func (s *WriterSuite) TestFlushErrorOnWrite() { writeFn := func(_ Page) (PageToken, error) { return nil, errors.New("got error") } shouldFlushFn := func(_ Page) bool { return true } writer := NewWriter(writeFn, shouldFlushFn, nil) s.Error(writer.Flush()) } func (s *WriterSuite) TestFlushNoError() { writeFn := func(_ Page) (PageToken, error) { return nil, nil } shouldFlushFn := func(_ Page) bool { return true } writer := NewWriter(writeFn, shouldFlushFn, "test_token") s.Len(writer.FlushedPages(), 0) s.NoError(writer.Flush()) s.Len(writer.FlushedPages(), 1) s.Equal("test_token", writer.FlushedPages()[0].(string)) s.Equal("test_token", writer.FirstFlushedPage().(string)) s.Equal("test_token", writer.LastFlushedPage().(string)) s.NoError(writer.FlushIfNotEmpty()) s.Len(writer.FlushedPages(), 1) s.Equal("test_token", writer.FlushedPages()[0].(string)) s.Equal("test_token", writer.FirstFlushedPage().(string)) s.Equal("test_token", writer.LastFlushedPage().(string)) } func (s *WriterSuite) TestMultiPageWrite() { pageNum := 0 writeFnCalls := 0 writeFn := func(_ Page) (PageToken, error) { writeFnCalls++ pageNum++ return fmt.Sprintf("page_%v", pageNum), nil } shouldFlushCalls := 0 shouldFlushFn := func(p Page) bool { shouldFlushCalls++ return len(p.Entities) == 5 } writer := NewWriter(writeFn, shouldFlushFn, "page_0") for i := 1; i <= 100; i++ { s.NoError(writer.Add(i)) } var expectedPages []string for i := 0; i < 20; i++ { expectedPages = append(expectedPages, fmt.Sprintf("page_%v", i)) } actualPages := writer.FlushedPages() s.Len(actualPages, 20) for i, expected := range expectedPages { s.Equal(expected, actualPages[i].(string)) } s.Equal(writeFnCalls, 20) s.Equal(shouldFlushCalls, 100) s.Equal("page_0", writer.FirstFlushedPage().(string)) s.Equal("page_19", writer.LastFlushedPage().(string)) } ================================================ FILE: common/peerprovider/plugin.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package peerprovider import ( "fmt" "go.uber.org/yarpc/transport/tchannel" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/syncmap" ) const key = "peerprovider" // Container is passed to peer provider plugin type Container struct { Service string // Channel is required by ringpop Channel tchannel.Channel Logger log.Logger Portmap membership.PortMap } type constructorFn func(cfg *config.YamlNode, container Container) (membership.PeerProvider, error) var plugins = syncmap.New[string, plugin]() type plugin struct { fn constructorFn configKey string } type Provider struct { config config.PeerProvider container Container } func New(config config.PeerProvider, container Container) *Provider { return &Provider{ config: config, container: container, } } func Register(configKey string, constructor constructorFn) error { inserted := plugins.Put(key, plugin{ fn: constructor, configKey: configKey, }) // only one plugin is allowed to be registered if !inserted { registeredPlugin, _ := plugins.Get(key) return fmt.Errorf("cannot register %q provider, %q is already registered", configKey, registeredPlugin.configKey) } return nil } func (p *Provider) Provider() (membership.PeerProvider, error) { registeredPlugin, found := plugins.Get(key) if !found { return nil, fmt.Errorf("no configured peer providers found") } for configKey, cfg := range p.config { if configKey == registeredPlugin.configKey { return registeredPlugin.fn(cfg, p.container) } } return nil, fmt.Errorf("no configuration for %q peer provider found", registeredPlugin.configKey) } ================================================ FILE: common/peerprovider/plugin_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package peerprovider import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/syncmap" ) func TestProviderRetrunsErrorWhenNoProviderRegistered(t *testing.T) { // Reset plugins plugins = syncmap.New[string, plugin]() a := Provider{ config: nil, container: Container{}, } p, err := a.Provider() assert.Nil(t, p) assert.EqualError(t, err, "no configured peer providers found") } func TestProviderRetrunsErrorWhenPluginAlreadyRegistered(t *testing.T) { // Reset plugins plugins = syncmap.New[string, plugin]() err := Register("provider1", func(cfg *config.YamlNode, container Container) (membership.PeerProvider, error) { return nil, nil }) assert.NoError(t, err) err = Register("provider2", func(cfg *config.YamlNode, container Container) (membership.PeerProvider, error) { return nil, nil }) assert.Error(t, err) } func TestConfigIsPickedUp(t *testing.T) { // Reset plugins plugins = syncmap.New[string, plugin]() peerProviderConfig := map[string]*config.YamlNode{} peerProviderConfig["provider1"] = &config.YamlNode{} pp := New(peerProviderConfig, Container{}) err := Register("provider1", func(cfg *config.YamlNode, container Container) (membership.PeerProvider, error) { return nil, nil }) assert.NoError(t, err) _, err = pp.Provider() assert.NoError(t, err) } func TestErrorWhenConfigIsNotProvided(t *testing.T) { // Reset plugins plugins = syncmap.New[string, plugin]() pp := New(config.PeerProvider{}, Container{}) err := Register("provider1", func(cfg *config.YamlNode, container Container) (membership.PeerProvider, error) { return nil, nil }) p, err := pp.Provider() assert.Nil(t, p) assert.EqualError(t, err, "no configuration for \"provider1\" peer provider found") } ================================================ FILE: common/peerprovider/ringpopprovider/config/config.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "fmt" "strings" "time" "github.com/uber/ringpop-go/discovery" ) // BootstrapMode is an enum type for ringpop bootstrap mode type BootstrapMode int const ( // BootstrapModeNone represents a bootstrap mode set to nothing or invalid BootstrapModeNone BootstrapMode = iota // BootstrapModeFile represents a file-based bootstrap mode BootstrapModeFile // BootstrapModeHosts represents a list of hosts passed in the configuration BootstrapModeHosts // BootstrapModeCustom represents a custom bootstrap mode BootstrapModeCustom // BootstrapModeDNS represents a list of hosts passed in the configuration // to be resolved, and the resulting addresses are used for bootstrap BootstrapModeDNS // BootstrapModeDNSSRV represents a list of DNS hosts passed in the configuration // to resolve secondary addresses that DNS SRV record would return resulting in // a host list that will contain multiple dynamic addresses and their unique ports BootstrapModeDNSSRV ) const ( defaultMaxJoinDuration = 10 * time.Second ) // Config contains the ringpop config items type Config struct { // Name to be used in ringpop advertisement Name string `yaml:"name" validate:"nonzero"` // BroadcastAddress is communicated with peers to connect to this container/host. // This is useful when running cadence in K8s and the containers need to listen on 0.0.0.0 but advertise their pod IP to peers. // If not set, the listen address will be used as broadcast address. BroadcastAddress string `yaml:"broadcastAddress"` // BootstrapMode is a enum that defines the ringpop bootstrap method, currently supports: hosts, files, custom, dns, and dns-srv BootstrapMode BootstrapMode `yaml:"bootstrapMode"` // BootstrapHosts is a list of seed hosts to be used for ringpop bootstrap BootstrapHosts []string `yaml:"bootstrapHosts"` // BootstrapFile is the file path to be used for ringpop bootstrap BootstrapFile string `yaml:"bootstrapFile"` // MaxJoinDuration is the max wait time to join the ring MaxJoinDuration time.Duration `yaml:"maxJoinDuration"` // Custom discovery provider, cannot be specified through yaml DiscoveryProvider discovery.DiscoverProvider `yaml:"-"` } func (rpConfig *Config) Validate() error { if len(rpConfig.Name) == 0 { return fmt.Errorf("ringpop config missing `name` param") } if rpConfig.MaxJoinDuration == 0 { rpConfig.MaxJoinDuration = defaultMaxJoinDuration } return validateBootstrapMode(rpConfig) } // UnmarshalYAML is called by the yaml package to convert // the config YAML into a BootstrapMode. func (m *BootstrapMode) UnmarshalYAML( unmarshal func(interface{}) error, ) error { var s string if err := unmarshal(&s); err != nil { return err } var err error *m, err = parseBootstrapMode(s) return err } // parseBootstrapMode reads a string value and returns a bootstrap mode. func parseBootstrapMode( mode string, ) (BootstrapMode, error) { switch strings.ToLower(mode) { case "hosts": return BootstrapModeHosts, nil case "file": return BootstrapModeFile, nil case "custom": return BootstrapModeCustom, nil case "dns": return BootstrapModeDNS, nil case "dns-srv": return BootstrapModeDNSSRV, nil } return BootstrapModeNone, fmt.Errorf("invalid ringpop bootstrap mode %q", mode) } func validateBootstrapMode( rpConfig *Config, ) error { switch rpConfig.BootstrapMode { case BootstrapModeFile: if len(rpConfig.BootstrapFile) == 0 { return fmt.Errorf("ringpop config missing bootstrap file param") } case BootstrapModeHosts, BootstrapModeDNS, BootstrapModeDNSSRV: if len(rpConfig.BootstrapHosts) == 0 { return fmt.Errorf("ringpop config missing boostrap hosts param") } case BootstrapModeCustom: if rpConfig.DiscoveryProvider == nil { return fmt.Errorf("ringpop bootstrapMode is set to custom but discoveryProvider is nil") } default: return fmt.Errorf("ringpop config with unknown boostrap mode %q", rpConfig.BootstrapMode) } return nil } ================================================ FILE: common/peerprovider/ringpopprovider/config/config_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/ringpop-go/discovery/statichosts" "gopkg.in/yaml.v2" ) func TestHostsMode(t *testing.T) { var cfg Config err := yaml.Unmarshal([]byte(getHostsConfig()), &cfg) assert.Nil(t, err) assert.Equal(t, "test", cfg.Name) assert.Equal(t, BootstrapModeHosts, cfg.BootstrapMode) assert.Equal(t, []string{"127.0.0.1:1111"}, cfg.BootstrapHosts) assert.Equal(t, time.Second*30, cfg.MaxJoinDuration) err = cfg.Validate() assert.Nil(t, err) } func TestFileMode(t *testing.T) { var cfg Config err := yaml.Unmarshal([]byte(getJSONConfig()), &cfg) assert.Nil(t, err) assert.Equal(t, "test", cfg.Name) assert.Equal(t, BootstrapModeFile, cfg.BootstrapMode) assert.Equal(t, "/tmp/file.json", cfg.BootstrapFile) assert.Equal(t, time.Second*30, cfg.MaxJoinDuration) err = cfg.Validate() assert.Nil(t, err) } func TestCustomMode(t *testing.T) { var cfg Config err := yaml.Unmarshal([]byte(getCustomConfig()), &cfg) assert.Nil(t, err) assert.Equal(t, "test", cfg.Name) assert.Equal(t, BootstrapModeCustom, cfg.BootstrapMode) assert.NotNil(t, cfg.Validate()) cfg.DiscoveryProvider = statichosts.New("127.0.0.1") assert.Nil(t, cfg.Validate()) } func TestInvalidConfig(t *testing.T) { var cfg Config assert.NotNil(t, cfg.Validate()) cfg.Name = "test" assert.NotNil(t, cfg.Validate()) cfg.BootstrapMode = BootstrapModeNone assert.NotNil(t, cfg.Validate()) _, err := parseBootstrapMode("unknown") assert.NotNil(t, err) } func getJSONConfig() string { return `name: "test" bootstrapMode: "file" bootstrapFile: "/tmp/file.json" maxJoinDuration: 30s` } func getHostsConfig() string { return `name: "test" bootstrapMode: "hosts" bootstrapHosts: ["127.0.0.1:1111"] maxJoinDuration: 30s` } func getCustomConfig() string { return `name: "test" bootstrapMode: "custom" maxJoinDuration: 30s` } ================================================ FILE: common/peerprovider/ringpopprovider/factory.go ================================================ package ringpopprovider import ( "context" "errors" "fmt" "net" "strings" "github.com/uber/ringpop-go/discovery" "github.com/uber/ringpop-go/discovery/jsonfile" "github.com/uber/ringpop-go/discovery/statichosts" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/peerprovider/ringpopprovider/config" ) type dnsHostResolver interface { LookupHost(ctx context.Context, host string) (addrs []string, err error) LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error) } type dnsProvider struct { UnresolvedHosts []string Resolver dnsHostResolver Logger log.Logger } func newDNSProvider( hosts []string, resolver dnsHostResolver, logger log.Logger, ) *dnsProvider { set := map[string]struct{}{} for _, hostport := range hosts { set[hostport] = struct{}{} } var keys []string for key := range set { keys = append(keys, key) } return &dnsProvider{ UnresolvedHosts: keys, Resolver: resolver, Logger: logger, } } func (provider *dnsProvider) Hosts() ([]string, error) { var results []string resolvedHosts := map[string][]string{} for _, hostPort := range provider.UnresolvedHosts { host, port, err := net.SplitHostPort(hostPort) if err != nil { provider.Logger.Warn("could not split host and port", tag.Address(hostPort), tag.Error(err)) continue } resolved, exists := resolvedHosts[host] if !exists { resolved, err = provider.Resolver.LookupHost(context.Background(), host) if err != nil { provider.Logger.Warn("could not resolve host", tag.Address(host), tag.Error(err)) continue } resolvedHosts[host] = resolved } for _, r := range resolved { results = append(results, net.JoinHostPort(r, port)) } } if len(results) == 0 { return nil, errors.New("no hosts found, and bootstrap requires at least one") } return results, nil } type dnsSRVProvider struct { UnresolvedHosts []string Resolver dnsHostResolver Logger log.Logger } func newDNSSRVProvider( hosts []string, resolver dnsHostResolver, logger log.Logger, ) *dnsSRVProvider { set := map[string]struct{}{} for _, hostport := range hosts { set[hostport] = struct{}{} } var keys []string for key := range set { keys = append(keys, key) } return &dnsSRVProvider{ UnresolvedHosts: keys, Resolver: resolver, Logger: logger, } } func (provider *dnsSRVProvider) Hosts() ([]string, error) { var results []string resolvedHosts := map[string][]string{} for _, host := range provider.UnresolvedHosts { hostParts := strings.Split(host, ".") if len(hostParts) <= 2 { return nil, fmt.Errorf("could not seperate host from domain %q", host) } serviceName := hostParts[0] domain := strings.Join(hostParts[1:], ".") resolved, exists := resolvedHosts[serviceName] if !exists { _, srvs, err := provider.Resolver.LookupSRV(context.Background(), serviceName, "tcp", domain) if err != nil { return nil, fmt.Errorf("could not resolve host: %s.%s", serviceName, domain) } var targets []string for _, s := range srvs { addrs, err := provider.Resolver.LookupHost(context.Background(), s.Target) if err != nil { provider.Logger.Warn("could not resolve srv dns host", tag.Address(s.Target), tag.Error(err)) continue } for _, a := range addrs { targets = append(targets, net.JoinHostPort(a, fmt.Sprintf("%d", s.Port))) } } resolvedHosts[serviceName] = targets resolved = targets } results = append(results, resolved...) } if len(results) == 0 { return nil, errors.New("no hosts found, and bootstrap requires at least one") } return results, nil } func newDiscoveryProvider( cfg *config.Config, logger log.Logger, ) (discovery.DiscoverProvider, error) { if cfg.DiscoveryProvider != nil { // custom discovery provider takes first precedence return cfg.DiscoveryProvider, nil } switch cfg.BootstrapMode { case config.BootstrapModeHosts: return statichosts.New(cfg.BootstrapHosts...), nil case config.BootstrapModeFile: return jsonfile.New(cfg.BootstrapFile), nil case config.BootstrapModeDNS: return newDNSProvider(cfg.BootstrapHosts, net.DefaultResolver, logger), nil case config.BootstrapModeDNSSRV: return newDNSSRVProvider(cfg.BootstrapHosts, net.DefaultResolver, logger), nil } return nil, fmt.Errorf("unknown bootstrap mode %q", cfg.BootstrapMode) } ================================================ FILE: common/peerprovider/ringpopprovider/factory_test.go ================================================ package ringpopprovider import ( "context" "fmt" "net" "testing" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v2" "github.com/uber/cadence/common/log/testlogger" ringpopproviderconfig "github.com/uber/cadence/common/peerprovider/ringpopprovider/config" ) type mockResolver struct { Hosts map[string][]string SRV map[string][]net.SRV } func (resolver *mockResolver) LookupHost(ctx context.Context, host string) ([]string, error) { addrs, ok := resolver.Hosts[host] if !ok { return nil, fmt.Errorf("Host was not resolved: %s", host) } return addrs, nil } func (resolver *mockResolver) LookupSRV(ctx context.Context, service string, proto string, name string) (string, []*net.SRV, error) { var records []*net.SRV srvs, ok := resolver.SRV[service] if !ok { return "", nil, fmt.Errorf("Host was not resolved: %s", service) } for _, record := range srvs { srvRecord := record records = append(records, &srvRecord) } return "test", records, nil } func TestDNSMode(t *testing.T) { var cfg ringpopproviderconfig.Config err := yaml.Unmarshal([]byte(getDNSConfig()), &cfg) assert.Nil(t, err) assert.Equal(t, "test", cfg.Name) assert.Equal(t, ringpopproviderconfig.BootstrapModeDNS, cfg.BootstrapMode) assert.Equal(t, "10.66.1.71", cfg.BroadcastAddress) assert.Nil(t, cfg.Validate()) logger := testlogger.New(t) assert.ElementsMatch( t, []string{ "example.net:1111", "example.net:1112", "unknown-duplicate.example.net:1111", "unknown-duplicate.example.net:1111", "badhostport", }, cfg.BootstrapHosts, ) provider := newDNSProvider( cfg.BootstrapHosts, &mockResolver{ Hosts: map[string][]string{"example.net": []string{"10.0.0.0", "10.0.0.1"}}, }, logger, ) cfg.DiscoveryProvider = provider assert.ElementsMatch( t, []string{ "example.net:1111", "example.net:1112", "unknown-duplicate.example.net:1111", "badhostport", }, provider.UnresolvedHosts, "duplicate entries should be removed", ) hostports, err := cfg.DiscoveryProvider.Hosts() assert.Nil(t, err) assert.ElementsMatch( t, []string{ "10.0.0.0:1111", "10.0.0.1:1111", "10.0.0.0:1112", "10.0.0.1:1112", }, hostports, ) cfg.DiscoveryProvider = newDNSProvider( cfg.BootstrapHosts, &mockResolver{Hosts: map[string][]string{}}, logger, ) hostports, err = cfg.DiscoveryProvider.Hosts() assert.Nil(t, hostports) assert.NotNil(t, err, "error should be returned when no hosts") } func TestDNSSRVMode(t *testing.T) { var cfg ringpopproviderconfig.Config err := yaml.Unmarshal([]byte(getDNSSRVConfig()), &cfg) assert.Nil(t, err) assert.Equal(t, "test", cfg.Name) assert.Equal(t, ringpopproviderconfig.BootstrapModeDNSSRV, cfg.BootstrapMode) assert.Nil(t, cfg.Validate()) logger := testlogger.New(t) assert.ElementsMatch( t, []string{ "service-a.example.net", "service-b.example.net", "unknown-duplicate.example.net", "unknown-duplicate.example.net", "badhostport", }, cfg.BootstrapHosts, ) provider := newDNSSRVProvider( cfg.BootstrapHosts, &mockResolver{ SRV: map[string][]net.SRV{ "service-a": []net.SRV{{Target: "az1-service-a.addr.example.net", Port: 7755}, {Target: "az2-service-a.addr.example.net", Port: 7566}}, "service-b": []net.SRV{{Target: "az1-service-b.addr.example.net", Port: 7788}, {Target: "az2-service-b.addr.example.net", Port: 7896}}, }, Hosts: map[string][]string{ "az1-service-a.addr.example.net": []string{"10.0.0.1"}, "az2-service-a.addr.example.net": []string{"10.0.2.0", "10.0.2.3"}, "az1-service-b.addr.example.net": []string{"10.0.3.0", "10.0.3.3"}, "az2-service-b.addr.example.net": []string{"10.0.3.1"}, }, }, logger, ) cfg.DiscoveryProvider = provider assert.ElementsMatch( t, []string{ "service-a.example.net", "service-b.example.net", "unknown-duplicate.example.net", "badhostport", }, provider.UnresolvedHosts, "duplicate entries should be removed", ) // Expect unknown-duplicate.example.net to not resolve _, err = cfg.DiscoveryProvider.Hosts() assert.NotNil(t, err) // Remove known bad hosts from Unresolved list provider.UnresolvedHosts = []string{ "service-a.example.net", "service-b.example.net", "badhostport", } // Expect badhostport to not seperate service name _, err = cfg.DiscoveryProvider.Hosts() assert.NotNil(t, err) // Remove known bad hosts from Unresolved list provider.UnresolvedHosts = []string{ "service-a.example.net", "service-b.example.net", } hostports, err := cfg.DiscoveryProvider.Hosts() assert.Nil(t, err) assert.ElementsMatch( t, []string{ "10.0.0.1:7755", "10.0.2.0:7566", "10.0.2.3:7566", "10.0.3.0:7788", "10.0.3.3:7788", "10.0.3.1:7896", }, hostports, "duplicate entries should be removed", ) cfg.DiscoveryProvider = newDNSProvider( cfg.BootstrapHosts, &mockResolver{Hosts: map[string][]string{}}, logger, ) hostports, err = cfg.DiscoveryProvider.Hosts() assert.Nil(t, hostports) assert.NotNil(t, err, "error should be returned when no hosts") } func getDNSConfig() string { return `name: "test" bootstrapMode: "dns" broadcastAddress: "10.66.1.71" bootstrapHosts: - example.net:1111 - example.net:1112 - unknown-duplicate.example.net:1111 - unknown-duplicate.example.net:1111 - badhostport maxJoinDuration: 30s` } func getDNSSRVConfig() string { return `name: "test" bootstrapMode: "dns-srv" bootstrapHosts: - service-a.example.net - service-b.example.net - unknown-duplicate.example.net - unknown-duplicate.example.net - badhostport maxJoinDuration: 30s` } ================================================ FILE: common/peerprovider/ringpopprovider/provider.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ringpopprovider import ( "fmt" "net" "strconv" "sync" "sync/atomic" "github.com/uber/ringpop-go" "github.com/uber/ringpop-go/events" rpmembership "github.com/uber/ringpop-go/membership" "github.com/uber/ringpop-go/swim" tcg "github.com/uber/tchannel-go" "go.uber.org/yarpc/transport/tchannel" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" ringpopproviderconfig "github.com/uber/cadence/common/peerprovider/ringpopprovider/config" ) type ( // Provider use ringpop to announce membership changes Provider struct { status int32 service string ringpop *ringpop.Ringpop bootParams *swim.BootstrapOptions logger log.Logger portmap membership.PortMap mu sync.RWMutex subscribers map[string]func(membership.ChangedEvent) } ) const ( // roleKey label is set by every single service as soon as it bootstraps its // ringpop instance. The data for this key is the service name roleKey = "serviceName" ) var _ membership.PeerProvider = (*Provider)(nil) func New( service string, config *ringpopproviderconfig.Config, channel tchannel.Channel, portMap membership.PortMap, logger log.Logger, ) (*Provider, error) { if err := config.Validate(); err != nil { return nil, err } discoveryProvider, err := newDiscoveryProvider(config, logger) if err != nil { return nil, fmt.Errorf("ringpop discovery provider: %w", err) } bootstrapOpts := &swim.BootstrapOptions{ MaxJoinDuration: config.MaxJoinDuration, DiscoverProvider: discoveryProvider, } ch := channel.(*tcg.Channel) opts := []ringpop.Option{ringpop.Channel(ch)} if config.BroadcastAddress != "" { broadcastIP := net.ParseIP(config.BroadcastAddress) if broadcastIP == nil { return nil, fmt.Errorf("failed parsing broadcast address %q, err: %w", config.BroadcastAddress, err) } logger.Info("Initializing ringpop with custom broadcast address", tag.Address(broadcastIP.String())) opts = append(opts, ringpop.AddressResolverFunc(broadcastAddrResolver(ch, broadcastIP))) } rp, err := ringpop.New(config.Name, opts...) if err != nil { return nil, fmt.Errorf("ringpop instance creation: %w", err) } return NewRingpopProvider(service, rp, portMap, bootstrapOpts, logger), nil } // NewRingpopProvider sets up ringpop based peer provider func NewRingpopProvider( service string, rp *ringpop.Ringpop, portMap membership.PortMap, bootstrapOpts *swim.BootstrapOptions, logger log.Logger, ) *Provider { return &Provider{ service: service, status: common.DaemonStatusInitialized, bootParams: bootstrapOpts, logger: logger, portmap: portMap, ringpop: rp, subscribers: map[string]func(membership.ChangedEvent){}, } } // Start starts ringpop func (r *Provider) Start() { if !atomic.CompareAndSwapInt32( &r.status, common.DaemonStatusInitialized, common.DaemonStatusStarted, ) { return } _, err := r.ringpop.Bootstrap(r.bootParams) if err != nil { r.logger.Fatal("unable to bootstrap ringpop", tag.Error(err)) } // Get updates from ringpop ring r.ringpop.AddListener(r) labels, err := r.ringpop.Labels() if err != nil { r.logger.Fatal("unable to get ring pop labels", tag.Error(err)) } // set port labels for name, port := range r.portmap { if err = labels.Set(name, strconv.Itoa(int(port))); err != nil { r.logger.Fatal("unable to set port label", tag.Error(err)) } } if err = labels.Set(roleKey, r.service); err != nil { r.logger.Fatal("unable to set ringpop role label", tag.Error(err)) } } // HandleEvent handles updates from ringpop func (r *Provider) HandleEvent(event events.Event) { // We only care about RingChangedEvent e, ok := event.(events.RingChangedEvent) if !ok { return } change := membership.ChangedEvent{ HostsAdded: e.ServersAdded, HostsUpdated: e.ServersUpdated, HostsRemoved: e.ServersRemoved, } r.logger.Info("Received a ringpop ring changed event", tag.MembershipChangeEvent(change)) r.notifySubscribers(change) } func (r *Provider) notifySubscribers(event membership.ChangedEvent) { r.mu.RLock() defer r.mu.RUnlock() for _, handler := range r.subscribers { handler(event) } } func (r *Provider) SelfEvict() error { return r.ringpop.SelfEvict() } // GetMembers returns all hosts with a specified role value func (r *Provider) GetMembers(service string) ([]membership.HostInfo, error) { var res []membership.HostInfo // filter member by service name, add port info to Hostinfo if they are present memberData := func(member swim.Member) bool { if v, ok := member.Label(roleKey); !ok || v != service { return false } // replicating swim member isReachable() method here as we are skipping other predicates if member.Status != swim.Alive && member.Status != swim.Suspect { return false } portMap := make(membership.PortMap) if v, ok := member.Label(membership.PortTchannel); ok { port, err := labelToPort(v) if err != nil { r.logger.Warn("tchannel port cannot be converted", tag.Error(err), tag.Value(v)) } else { portMap[membership.PortTchannel] = port } } else { // for backwards compatibility: get tchannel port from member address _, port, err := net.SplitHostPort(member.Address) if err != nil { r.logger.Warn("getting ringpop member port from address", tag.Value(member.Address), tag.Error(err)) } else { tchannelPort, err := labelToPort(port) if err != nil { r.logger.Warn("tchannel port cannot be converted", tag.Error(err), tag.Value(port)) } else { portMap[membership.PortTchannel] = tchannelPort } } } if v, ok := member.Label(membership.PortGRPC); ok { port, err := labelToPort(v) if err != nil { r.logger.Warn("grpc port cannot be converted", tag.Error(err), tag.Value(v)) } else { portMap[membership.PortGRPC] = port } } res = append(res, membership.NewDetailedHostInfo(member.GetAddress(), member.Identity(), portMap)) return true } _, err := r.ringpop.GetReachableMembers(memberData) if err != nil { return nil, fmt.Errorf("ringpop get members: %w", err) } return res, nil } // WhoAmI returns address of this instance func (r *Provider) WhoAmI() (membership.HostInfo, error) { address, err := r.ringpop.WhoAmI() if err != nil { return membership.HostInfo{}, fmt.Errorf("ringpop doesn't know Who Am I: %w", err) } labels, err := r.ringpop.Labels() if err != nil { return membership.HostInfo{}, fmt.Errorf("getting ringpop labels: %w", err) } hostIdentity := address // this is needed to in a situation when Cadence is trying to identify the owner for a key // make sure we are comparing identities, but not host:port pairs rpIdentity, set := labels.Get(rpmembership.IdentityLabelKey) if set { hostIdentity = rpIdentity } return membership.NewDetailedHostInfo(address, hostIdentity, r.portmap), nil } // Stop stops ringpop func (r *Provider) Stop() { if !atomic.CompareAndSwapInt32( &r.status, common.DaemonStatusStarted, common.DaemonStatusStopped, ) { return } r.ringpop.Destroy() } // Subscribe allows to be subscribed for ring changes func (r *Provider) Subscribe(name string, handler func(membership.ChangedEvent)) error { r.mu.Lock() defer r.mu.Unlock() _, ok := r.subscribers[name] if ok { return fmt.Errorf("%q already subscribed to ringpop provider", name) } r.subscribers[name] = handler return nil } func labelToPort(label string) (uint16, error) { port, err := strconv.ParseUint(label, 10, 16) if err != nil { return 0, err } return uint16(port), nil } func broadcastAddrResolver(ch *tcg.Channel, broadcastIP net.IP) func() (string, error) { return func() (string, error) { peerInfo := ch.PeerInfo() if peerInfo.IsEphemeralHostPort() { // not listening yet return "", ringpop.ErrEphemeralAddress } _, port, err := net.SplitHostPort(peerInfo.HostPort) if err != nil { return "", fmt.Errorf("failed splitting tchannel's hostport %q, err: %w", peerInfo.HostPort, err) } // return broadcast_ip:listen_port return net.JoinHostPort(broadcastIP.String(), port), nil } } ================================================ FILE: common/peerprovider/ringpopprovider/provider_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ringpopprovider import ( "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/ringpop-go/events" "github.com/uber/tchannel-go" "go.uber.org/goleak" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" ringpopproviderconfig "github.com/uber/cadence/common/peerprovider/ringpopprovider/config" ) const testServiceName = "test-service" type srvAndCh struct { service string ch *tchannel.Channel provider *Provider } func TestRingpopProvider(t *testing.T) { t.Cleanup(func() { goleak.VerifyNone(t, // ignore the goroutines leaked by ringpop library goleak.IgnoreTopFunction("github.com/rcrowley/go-metrics.(*meterArbiter).tick"), goleak.IgnoreTopFunction("github.com/uber/ringpop-go.(*Ringpop).startTimers.func1"), goleak.IgnoreTopFunction("github.com/uber/ringpop-go.(*Ringpop).startTimers.func2")) }) logger := testlogger.New(t) serviceName := "matching" matchingChs, cleanupMatchingChs, err := createAndListenChannels(serviceName, 3) t.Cleanup(cleanupMatchingChs) if err != nil { t.Fatalf("Failed to create and listen on channels: %v", err) } irrelevantChs, cleanupIrrelevantChs, err := createAndListenChannels("random-svc", 1) t.Cleanup(cleanupIrrelevantChs) if err != nil { t.Fatalf("Failed to create and listen on channels: %v", err) } allServicesAndChs := append(matchingChs, irrelevantChs...) cfg := ringpopproviderconfig.Config{ BootstrapMode: ringpopproviderconfig.BootstrapModeHosts, Name: "ring", BootstrapHosts: toHosts(t, allServicesAndChs), MaxJoinDuration: 10 * time.Second, } t.Logf("Config: %+v", cfg) // start ringpop provider for each channel var wg sync.WaitGroup for i, svc := range allServicesAndChs { svc := svc cfg := cfg if i == 0 { // set broadcast address for the first provider to test that path cfg.BroadcastAddress = "127.0.0.1" } p, err := New(svc.service, &cfg, svc.ch, membership.PortMap{}, logger) if err != nil { t.Fatalf("Failed to create ringpop provider: %v", err) } svc.provider = p wg.Add(1) go func() { defer wg.Done() p.Start() }() t.Cleanup(p.Stop) } t.Logf("Waiting for %d ringpop providers to start", len(matchingChs)+len(irrelevantChs)) wg.Wait() sleep := 5 * time.Second t.Logf("Sleeping for %d seconds for ring to update", int(sleep.Seconds())) time.Sleep(sleep) provider := matchingChs[0].provider hostInfo, err := provider.WhoAmI() if err != nil { t.Fatalf("Failed to get who am I: %v", err) } t.Logf("Who am I: %+v", hostInfo.GetAddress()) members, err := provider.GetMembers(serviceName) if err != nil { t.Fatalf("Failed to get members: %v", err) } if len(members) != 3 { t.Fatalf("Expected 3 members, got %v", len(members)) } // Evict one of the providers and make sure it's removed from the ring matchingChs[1].provider.SelfEvict() t.Logf("A peer is evicted. Sleeping for %d seconds for ring to update", int(sleep.Seconds())) time.Sleep(sleep) members, err = provider.GetMembers(serviceName) if err != nil { t.Fatalf("Failed to get members: %v", err) } if len(members) != 2 { t.Fatalf("Expected 2 members, got %v", len(members)) } } func TestSubscribeAndNotify(t *testing.T) { provider := NewRingpopProvider(testServiceName, nil, nil, nil, testlogger.New(t)) ringpopEvent := events.RingChangedEvent{ ServersAdded: []string{"aa", "bb", "cc"}, ServersUpdated: []string{"dd"}, ServersRemoved: []string{"ee", "ff"}, } expectedEvent := membership.ChangedEvent{ HostsAdded: ringpopEvent.ServersAdded, HostsUpdated: ringpopEvent.ServersUpdated, HostsRemoved: ringpopEvent.ServersRemoved, } var calls1, calls2 int require.NoError(t, provider.Subscribe("subscriber1", func(ev membership.ChangedEvent) { calls1++ assert.Equal(t, ev, expectedEvent) }, )) require.NoError(t, provider.Subscribe("subscriber2", func(ev membership.ChangedEvent) { calls2++ assert.Equal(t, ev, expectedEvent) }, )) require.Error(t, provider.Subscribe( "subscriber2", func(membership.ChangedEvent) { t.Error("Should never be called") }, ), "Subscribe doesn't allow duplicate names", ) provider.HandleEvent(ringpopEvent) assert.Equal(t, 1, calls1, "every subscriber must have been called once") assert.Equal(t, 1, calls2, "every subscriber must have been called once") } func createAndListenChannels(serviceName string, n int) ([]*srvAndCh, func(), error) { var res []*srvAndCh cleanupFn := func(srvs []*srvAndCh) func() { return func() { for _, s := range srvs { s.ch.Close() } } } for i := 0; i < n; i++ { ch, err := tchannel.NewChannel(serviceName, nil) if err != nil { return nil, cleanupFn(res), err } if err := ch.ListenAndServe("localhost:0"); err != nil { return nil, cleanupFn(res), err } res = append(res, &srvAndCh{service: serviceName, ch: ch}) } return res, cleanupFn(res), nil } func toHosts(t *testing.T, allServicesAndChs []*srvAndCh) []string { var hosts []string for _, svc := range allServicesAndChs { if svc.ch.PeerInfo().IsEphemeralHostPort() { t.Fatalf("Channel %v is not listening on a port", svc.ch) } hosts = append(hosts, svc.ch.PeerInfo().HostPort) } return hosts } func TestLabelToPort(t *testing.T) { tests := []struct { label string want uint16 wantErr bool }{ { label: "0", want: 0, }, { label: "1234", want: 1234, }, { label: "-1", wantErr: true, }, { label: "32768", want: 32768, }, { label: "65535", want: 65535, }, { label: "65536", // greater than max uint16 (2^16-1) wantErr: true, }, } for _, tc := range tests { got, err := labelToPort(tc.label) if got != tc.want || (err != nil) != tc.wantErr { t.Errorf("labelToPort(%v) = %v, %v; want %v, %v", tc.label, got, err, tc.want, tc.wantErr) } } } ================================================ FILE: common/peerprovider/ringpopprovider/ringpopfx/ringpopfx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ringpopfx import ( "go.uber.org/fx" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/peerprovider/ringpopprovider" "github.com/uber/cadence/common/rpc" ) // Module provides a peer resolver based on ringpop for fx app. var Module = fx.Module("ringpop", fx.Provide(buildRingpopProvider)) type Params struct { fx.In ServiceFullName string `name:"service-full-name"` Config config.Config ServiceConfig config.Service Logger log.Logger RPCFactory rpc.Factory Lifecycle fx.Lifecycle } func buildRingpopProvider(params Params) (membership.PeerProvider, error) { provider, err := ringpopprovider.New(params.ServiceFullName, ¶ms.Config.Ringpop, params.RPCFactory.GetTChannel(), membership.PortMap{ membership.PortGRPC: params.ServiceConfig.RPC.GRPCPort, membership.PortTchannel: params.ServiceConfig.RPC.Port, }, params.Logger) if err != nil { return nil, err } return provider, nil } ================================================ FILE: common/peerprovider/ringpopprovider/ringpopfx/ringpopfx_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ringpopfx import ( "testing" "github.com/stretchr/testify/require" "github.com/uber/tchannel-go" "go.uber.org/fx" "go.uber.org/fx/fxtest" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" ringpopproviderconfig "github.com/uber/cadence/common/peerprovider/ringpopprovider/config" "github.com/uber/cadence/common/rpc" ) func TestFxApp(t *testing.T) { app := fxtest.New(t, fx.Provide( func() testSetupParams { ctrl := gomock.NewController(t) factory := rpc.NewMockFactory(ctrl) tch, err := tchannel.NewChannel("test-ringpop", nil) require.NoError(t, err) factory.EXPECT().GetTChannel().Return(tch) return testSetupParams{ Service: "test", Logger: testlogger.New(t), RPCFactory: factory, Config: config.Config{ Ringpop: ringpopproviderconfig.Config{ Name: "test-ringpop", BootstrapMode: ringpopproviderconfig.BootstrapModeHosts, BootstrapHosts: []string{"127.0.0.1:7933", "127.0.0.1:7934", "127.0.0.1:7935"}, }, }, } }), Module, fx.Invoke(func(provider membership.PeerProvider) {}), ) app.RequireStart().RequireStop() } type testSetupParams struct { fx.Out Service string `name:"service-full-name"` Config config.Config ServiceConfig config.Service Logger log.Logger RPCFactory rpc.Factory } ================================================ FILE: common/persistence/client/bean.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination bean_mock.go package client import ( "sync" "github.com/uber/cadence/common/config" es "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/common/service" ) type ( // Bean in an collection of persistence manager Bean interface { Close() GetDomainManager() persistence.DomainManager SetDomainManager(persistence.DomainManager) GetDomainAuditManager() persistence.DomainAuditManager SetDomainAuditManager(persistence.DomainAuditManager) GetTaskManager() persistence.TaskManager SetTaskManager(persistence.TaskManager) GetVisibilityManager() persistence.VisibilityManager SetVisibilityManager(persistence.VisibilityManager) GetDomainReplicationQueueManager() persistence.QueueManager SetDomainReplicationQueueManager(persistence.QueueManager) GetShardManager() persistence.ShardManager SetShardManager(persistence.ShardManager) GetHistoryManager() persistence.HistoryManager SetHistoryManager(persistence.HistoryManager) GetExecutionManager(int) (persistence.ExecutionManager, error) SetExecutionManager(int, persistence.ExecutionManager) GetConfigStoreManager() persistence.ConfigStoreManager SetConfigStoreManager(persistence.ConfigStoreManager) } // BeanImpl stores persistence managers BeanImpl struct { domainManager persistence.DomainManager domainAuditManager persistence.DomainAuditManager taskManager persistence.TaskManager visibilityManager persistence.VisibilityManager domainReplicationQueueManager persistence.QueueManager shardManager persistence.ShardManager historyManager persistence.HistoryManager configStoreManager persistence.ConfigStoreManager executionManagerFactory persistence.ExecutionManagerFactory sync.RWMutex shardIDToExecutionManager map[int]persistence.ExecutionManager } // Params contains dependencies for persistence Params struct { PersistenceConfig config.Persistence MetricsClient metrics.Client MessagingClient messaging.Client ESClient es.GenericClient ESConfig *config.ElasticSearchConfig PinotConfig *config.PinotVisibilityConfig PinotClient pinot.GenericClient OSClient es.GenericClient OSConfig *config.ElasticSearchConfig } ) // NewBeanFromFactory crate a new store bean using factory func NewBeanFromFactory( factory Factory, params *Params, serviceConfig *service.Config, ) (Bean, error) { metadataMgr, err := factory.NewDomainManager() if err != nil { return nil, err } domainAuditMgr, err := factory.NewDomainAuditManager() if err != nil { return nil, err } taskMgr, err := factory.NewTaskManager() if err != nil { return nil, err } visibilityMgr, err := factory.NewVisibilityManager(params, serviceConfig) if err != nil { return nil, err } domainReplicationQueue, err := factory.NewDomainReplicationQueueManager() if err != nil { return nil, err } shardMgr, err := factory.NewShardManager() if err != nil { return nil, err } historyMgr, err := factory.NewHistoryManager() if err != nil { return nil, err } configStoreMgr, err := factory.NewConfigStoreManager() if err != nil { return nil, err } return NewBean( metadataMgr, domainAuditMgr, taskMgr, visibilityMgr, domainReplicationQueue, shardMgr, historyMgr, configStoreMgr, factory, ), nil } // NewBean create a new store bean func NewBean( domainManager persistence.DomainManager, domainAuditManager persistence.DomainAuditManager, taskManager persistence.TaskManager, visibilityManager persistence.VisibilityManager, domainReplicationQueueManager persistence.QueueManager, shardManager persistence.ShardManager, historyManager persistence.HistoryManager, configStoreManager persistence.ConfigStoreManager, executionManagerFactory persistence.ExecutionManagerFactory, ) *BeanImpl { return &BeanImpl{ domainManager: domainManager, domainAuditManager: domainAuditManager, taskManager: taskManager, visibilityManager: visibilityManager, domainReplicationQueueManager: domainReplicationQueueManager, shardManager: shardManager, historyManager: historyManager, configStoreManager: configStoreManager, executionManagerFactory: executionManagerFactory, shardIDToExecutionManager: make(map[int]persistence.ExecutionManager), } } // GetDomainManager get DomainManager func (s *BeanImpl) GetDomainManager() persistence.DomainManager { s.RLock() defer s.RUnlock() return s.domainManager } // SetMetadataManager set DomainManager func (s *BeanImpl) SetDomainManager( domainManager persistence.DomainManager, ) { s.Lock() defer s.Unlock() s.domainManager = domainManager } // GetDomainAuditManager get DomainAuditManager func (s *BeanImpl) GetDomainAuditManager() persistence.DomainAuditManager { s.RLock() defer s.RUnlock() return s.domainAuditManager } // SetDomainAuditManager set DomainAuditManager func (s *BeanImpl) SetDomainAuditManager( domainAuditManager persistence.DomainAuditManager, ) { s.Lock() defer s.Unlock() s.domainAuditManager = domainAuditManager } // GetTaskManager get TaskManager func (s *BeanImpl) GetTaskManager() persistence.TaskManager { s.RLock() defer s.RUnlock() return s.taskManager } // SetTaskManager set TaskManager func (s *BeanImpl) SetTaskManager( taskManager persistence.TaskManager, ) { s.Lock() defer s.Unlock() s.taskManager = taskManager } // GetVisibilityManager get VisibilityManager func (s *BeanImpl) GetVisibilityManager() persistence.VisibilityManager { s.RLock() defer s.RUnlock() return s.visibilityManager } // SetVisibilityManager set VisibilityManager func (s *BeanImpl) SetVisibilityManager( visibilityManager persistence.VisibilityManager, ) { s.Lock() defer s.Unlock() s.visibilityManager = visibilityManager } // GetDomainReplicationQueueManager gets domain replication QueueManager func (s *BeanImpl) GetDomainReplicationQueueManager() persistence.QueueManager { s.RLock() defer s.RUnlock() return s.domainReplicationQueueManager } // SetDomainReplicationQueueManager sets domain replication QueueManager func (s *BeanImpl) SetDomainReplicationQueueManager( domainReplicationQueueManager persistence.QueueManager, ) { s.Lock() defer s.Unlock() s.domainReplicationQueueManager = domainReplicationQueueManager } // GetShardManager get ShardManager func (s *BeanImpl) GetShardManager() persistence.ShardManager { s.RLock() defer s.RUnlock() return s.shardManager } // SetShardManager set ShardManager func (s *BeanImpl) SetShardManager( shardManager persistence.ShardManager, ) { s.Lock() defer s.Unlock() s.shardManager = shardManager } // GetHistoryManager get HistoryManager func (s *BeanImpl) GetHistoryManager() persistence.HistoryManager { s.RLock() defer s.RUnlock() return s.historyManager } // SetHistoryManager set HistoryManager func (s *BeanImpl) SetHistoryManager( historyManager persistence.HistoryManager, ) { s.Lock() defer s.Unlock() s.historyManager = historyManager } // GetExecutionManager get ExecutionManager func (s *BeanImpl) GetExecutionManager( shardID int, ) (persistence.ExecutionManager, error) { s.RLock() executionManager, ok := s.shardIDToExecutionManager[shardID] if ok { s.RUnlock() return executionManager, nil } s.RUnlock() s.Lock() defer s.Unlock() executionManager, ok = s.shardIDToExecutionManager[shardID] if ok { return executionManager, nil } executionManager, err := s.executionManagerFactory.NewExecutionManager(shardID) if err != nil { return nil, err } s.shardIDToExecutionManager[shardID] = executionManager return executionManager, nil } // SetExecutionManager set ExecutionManager func (s *BeanImpl) SetExecutionManager( shardID int, executionManager persistence.ExecutionManager, ) { s.Lock() defer s.Unlock() s.shardIDToExecutionManager[shardID] = executionManager } // GetConfigStoreManager gets ConfigStoreManager func (s *BeanImpl) GetConfigStoreManager() persistence.ConfigStoreManager { s.RLock() defer s.RUnlock() return s.configStoreManager } // GetConfigStoreManager gets ConfigStoreManager func (s *BeanImpl) SetConfigStoreManager( configStoreManager persistence.ConfigStoreManager, ) { s.Lock() defer s.Unlock() s.configStoreManager = configStoreManager } // Close cleanup connections func (s *BeanImpl) Close() { s.Lock() defer s.Unlock() s.domainManager.Close() if s.domainAuditManager != nil { s.domainAuditManager.Close() } s.taskManager.Close() if s.visibilityManager != nil { // visibilityManager can be nil s.visibilityManager.Close() } s.domainReplicationQueueManager.Close() s.shardManager.Close() s.historyManager.Close() s.executionManagerFactory.Close() s.configStoreManager.Close() for _, executionMgr := range s.shardIDToExecutionManager { executionMgr.Close() } } ================================================ FILE: common/persistence/client/bean_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: bean.go // // Generated by this command: // // mockgen -package client -source bean.go -destination bean_mock.go // // Package client is a generated GoMock package. package client import ( reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" ) // MockBean is a mock of Bean interface. type MockBean struct { ctrl *gomock.Controller recorder *MockBeanMockRecorder isgomock struct{} } // MockBeanMockRecorder is the mock recorder for MockBean. type MockBeanMockRecorder struct { mock *MockBean } // NewMockBean creates a new mock instance. func NewMockBean(ctrl *gomock.Controller) *MockBean { mock := &MockBean{ctrl: ctrl} mock.recorder = &MockBeanMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockBean) EXPECT() *MockBeanMockRecorder { return m.recorder } // Close mocks base method. func (m *MockBean) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockBeanMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockBean)(nil).Close)) } // GetConfigStoreManager mocks base method. func (m *MockBean) GetConfigStoreManager() persistence.ConfigStoreManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetConfigStoreManager") ret0, _ := ret[0].(persistence.ConfigStoreManager) return ret0 } // GetConfigStoreManager indicates an expected call of GetConfigStoreManager. func (mr *MockBeanMockRecorder) GetConfigStoreManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfigStoreManager", reflect.TypeOf((*MockBean)(nil).GetConfigStoreManager)) } // GetDomainAuditManager mocks base method. func (m *MockBean) GetDomainAuditManager() persistence.DomainAuditManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainAuditManager") ret0, _ := ret[0].(persistence.DomainAuditManager) return ret0 } // GetDomainAuditManager indicates an expected call of GetDomainAuditManager. func (mr *MockBeanMockRecorder) GetDomainAuditManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainAuditManager", reflect.TypeOf((*MockBean)(nil).GetDomainAuditManager)) } // GetDomainManager mocks base method. func (m *MockBean) GetDomainManager() persistence.DomainManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainManager") ret0, _ := ret[0].(persistence.DomainManager) return ret0 } // GetDomainManager indicates an expected call of GetDomainManager. func (mr *MockBeanMockRecorder) GetDomainManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainManager", reflect.TypeOf((*MockBean)(nil).GetDomainManager)) } // GetDomainReplicationQueueManager mocks base method. func (m *MockBean) GetDomainReplicationQueueManager() persistence.QueueManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainReplicationQueueManager") ret0, _ := ret[0].(persistence.QueueManager) return ret0 } // GetDomainReplicationQueueManager indicates an expected call of GetDomainReplicationQueueManager. func (mr *MockBeanMockRecorder) GetDomainReplicationQueueManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainReplicationQueueManager", reflect.TypeOf((*MockBean)(nil).GetDomainReplicationQueueManager)) } // GetExecutionManager mocks base method. func (m *MockBean) GetExecutionManager(arg0 int) (persistence.ExecutionManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetExecutionManager", arg0) ret0, _ := ret[0].(persistence.ExecutionManager) ret1, _ := ret[1].(error) return ret0, ret1 } // GetExecutionManager indicates an expected call of GetExecutionManager. func (mr *MockBeanMockRecorder) GetExecutionManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExecutionManager", reflect.TypeOf((*MockBean)(nil).GetExecutionManager), arg0) } // GetHistoryManager mocks base method. func (m *MockBean) GetHistoryManager() persistence.HistoryManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryManager") ret0, _ := ret[0].(persistence.HistoryManager) return ret0 } // GetHistoryManager indicates an expected call of GetHistoryManager. func (mr *MockBeanMockRecorder) GetHistoryManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryManager", reflect.TypeOf((*MockBean)(nil).GetHistoryManager)) } // GetShardManager mocks base method. func (m *MockBean) GetShardManager() persistence.ShardManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardManager") ret0, _ := ret[0].(persistence.ShardManager) return ret0 } // GetShardManager indicates an expected call of GetShardManager. func (mr *MockBeanMockRecorder) GetShardManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardManager", reflect.TypeOf((*MockBean)(nil).GetShardManager)) } // GetTaskManager mocks base method. func (m *MockBean) GetTaskManager() persistence.TaskManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskManager") ret0, _ := ret[0].(persistence.TaskManager) return ret0 } // GetTaskManager indicates an expected call of GetTaskManager. func (mr *MockBeanMockRecorder) GetTaskManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskManager", reflect.TypeOf((*MockBean)(nil).GetTaskManager)) } // GetVisibilityManager mocks base method. func (m *MockBean) GetVisibilityManager() persistence.VisibilityManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVisibilityManager") ret0, _ := ret[0].(persistence.VisibilityManager) return ret0 } // GetVisibilityManager indicates an expected call of GetVisibilityManager. func (mr *MockBeanMockRecorder) GetVisibilityManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVisibilityManager", reflect.TypeOf((*MockBean)(nil).GetVisibilityManager)) } // SetConfigStoreManager mocks base method. func (m *MockBean) SetConfigStoreManager(arg0 persistence.ConfigStoreManager) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetConfigStoreManager", arg0) } // SetConfigStoreManager indicates an expected call of SetConfigStoreManager. func (mr *MockBeanMockRecorder) SetConfigStoreManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetConfigStoreManager", reflect.TypeOf((*MockBean)(nil).SetConfigStoreManager), arg0) } // SetDomainAuditManager mocks base method. func (m *MockBean) SetDomainAuditManager(arg0 persistence.DomainAuditManager) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetDomainAuditManager", arg0) } // SetDomainAuditManager indicates an expected call of SetDomainAuditManager. func (mr *MockBeanMockRecorder) SetDomainAuditManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDomainAuditManager", reflect.TypeOf((*MockBean)(nil).SetDomainAuditManager), arg0) } // SetDomainManager mocks base method. func (m *MockBean) SetDomainManager(arg0 persistence.DomainManager) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetDomainManager", arg0) } // SetDomainManager indicates an expected call of SetDomainManager. func (mr *MockBeanMockRecorder) SetDomainManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDomainManager", reflect.TypeOf((*MockBean)(nil).SetDomainManager), arg0) } // SetDomainReplicationQueueManager mocks base method. func (m *MockBean) SetDomainReplicationQueueManager(arg0 persistence.QueueManager) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetDomainReplicationQueueManager", arg0) } // SetDomainReplicationQueueManager indicates an expected call of SetDomainReplicationQueueManager. func (mr *MockBeanMockRecorder) SetDomainReplicationQueueManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDomainReplicationQueueManager", reflect.TypeOf((*MockBean)(nil).SetDomainReplicationQueueManager), arg0) } // SetExecutionManager mocks base method. func (m *MockBean) SetExecutionManager(arg0 int, arg1 persistence.ExecutionManager) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetExecutionManager", arg0, arg1) } // SetExecutionManager indicates an expected call of SetExecutionManager. func (mr *MockBeanMockRecorder) SetExecutionManager(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetExecutionManager", reflect.TypeOf((*MockBean)(nil).SetExecutionManager), arg0, arg1) } // SetHistoryManager mocks base method. func (m *MockBean) SetHistoryManager(arg0 persistence.HistoryManager) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetHistoryManager", arg0) } // SetHistoryManager indicates an expected call of SetHistoryManager. func (mr *MockBeanMockRecorder) SetHistoryManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHistoryManager", reflect.TypeOf((*MockBean)(nil).SetHistoryManager), arg0) } // SetShardManager mocks base method. func (m *MockBean) SetShardManager(arg0 persistence.ShardManager) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetShardManager", arg0) } // SetShardManager indicates an expected call of SetShardManager. func (mr *MockBeanMockRecorder) SetShardManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetShardManager", reflect.TypeOf((*MockBean)(nil).SetShardManager), arg0) } // SetTaskManager mocks base method. func (m *MockBean) SetTaskManager(arg0 persistence.TaskManager) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetTaskManager", arg0) } // SetTaskManager indicates an expected call of SetTaskManager. func (mr *MockBeanMockRecorder) SetTaskManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTaskManager", reflect.TypeOf((*MockBean)(nil).SetTaskManager), arg0) } // SetVisibilityManager mocks base method. func (m *MockBean) SetVisibilityManager(arg0 persistence.VisibilityManager) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetVisibilityManager", arg0) } // SetVisibilityManager indicates an expected call of SetVisibilityManager. func (mr *MockBeanMockRecorder) SetVisibilityManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVisibilityManager", reflect.TypeOf((*MockBean)(nil).SetVisibilityManager), arg0) } ================================================ FILE: common/persistence/client/bean_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package client import ( "fmt" "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "golang.org/x/sync/errgroup" "github.com/uber/cadence/common/persistence" ) type beanmocks struct { mockCtrl *gomock.Controller domainManager *persistence.MockDomainManager domainAuditManager *persistence.MockDomainAuditManager taskManager *persistence.MockTaskManager visibilityManager *persistence.MockVisibilityManager replicationManager *persistence.MockQueueManager shardManager *persistence.MockShardManager historyManager *persistence.MockHistoryManager configManager *persistence.MockConfigStoreManager } func beanSetup(t *testing.T) (f *MockFactory, m beanmocks, defaultMocks func()) { ctrl := gomock.NewController(t) m = beanmocks{ mockCtrl: ctrl, domainManager: persistence.NewMockDomainManager(ctrl), domainAuditManager: persistence.NewMockDomainAuditManager(ctrl), taskManager: persistence.NewMockTaskManager(ctrl), visibilityManager: persistence.NewMockVisibilityManager(ctrl), replicationManager: persistence.NewMockQueueManager(ctrl), shardManager: persistence.NewMockShardManager(ctrl), historyManager: persistence.NewMockHistoryManager(ctrl), configManager: persistence.NewMockConfigStoreManager(ctrl), } f = NewMockFactory(ctrl) defaultMocks = func() { // allow any of them to be called once or never, individual tests will set earlier mocks as needed f.EXPECT().NewDomainManager().Return(m.domainManager, nil).MaxTimes(1) f.EXPECT().NewDomainAuditManager().Return(m.domainAuditManager, nil).MaxTimes(1) f.EXPECT().NewTaskManager().Return(m.taskManager, nil).MaxTimes(1) f.EXPECT().NewVisibilityManager(gomock.Any(), gomock.Any()).Return(m.visibilityManager, nil).MaxTimes(1) f.EXPECT().NewDomainReplicationQueueManager().Return(m.replicationManager, nil).MaxTimes(1) f.EXPECT().NewShardManager().Return(m.shardManager, nil).MaxTimes(1) f.EXPECT().NewHistoryManager().Return(m.historyManager, nil).MaxTimes(1) f.EXPECT().NewConfigStoreManager().Return(m.configManager, nil).MaxTimes(1) } return f, m, defaultMocks } func TestBeanCoverage(t *testing.T) { // not particularly valuable but needed to hit min-% goals t.Run("NewBeanFromFactory", func(t *testing.T) { t.Parallel() // coverage for the New func, very straightforward err-branches. tests := map[string]struct { mockSetup func(t *testing.T, f *MockFactory) err string }{ "success": { mockSetup: func(t *testing.T, f *MockFactory) { // intentionally empty - setup already assume success }, err: "", }, "domain manager error": { mockSetup: func(t *testing.T, f *MockFactory) { f.EXPECT().NewDomainManager().Return(nil, fmt.Errorf("no domain manager")) }, err: "no domain manager", }, "task manager error": { mockSetup: func(t *testing.T, f *MockFactory) { f.EXPECT().NewTaskManager().Return(nil, fmt.Errorf("no task manager")) }, err: "no task manager", }, "visibility manager error": { mockSetup: func(t *testing.T, f *MockFactory) { f.EXPECT().NewVisibilityManager(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("no visibility manager")) }, err: "no visibility manager", }, "domain replication queue manager error": { mockSetup: func(t *testing.T, f *MockFactory) { f.EXPECT().NewDomainReplicationQueueManager().Return(nil, fmt.Errorf("no domain replication queue manager")) }, err: "no domain replication queue manager", }, "shard manager error": { mockSetup: func(t *testing.T, f *MockFactory) { f.EXPECT().NewShardManager().Return(nil, fmt.Errorf("no shard manager")) }, err: "no shard manager", }, "history manager error": { mockSetup: func(t *testing.T, f *MockFactory) { f.EXPECT().NewHistoryManager().Return(nil, fmt.Errorf("no history manager")) }, err: "no history manager", }, "config manager error": { mockSetup: func(t *testing.T, f *MockFactory) { f.EXPECT().NewConfigStoreManager().Return(nil, fmt.Errorf("no config manager")) }, err: "no config manager", }, } for name, test := range tests { name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() f, _, defaultMocks := beanSetup(t) test.mockSetup(t, f) defaultMocks() impl, err := NewBeanFromFactory(f, nil, nil) if test.err != "" { assert.ErrorContains(t, err, test.err) } else { assert.NoError(t, err) assert.NotNil(t, impl) } }) } }) t.Run("Simple getters", func(t *testing.T) { t.Parallel() f, m, defaultMocks := beanSetup(t) defaultMocks() // build all mock objects impl, err := NewBeanFromFactory(f, nil, nil) require.NoError(t, err) // these need to be concurrency-safe, so run them concurrently var g errgroup.Group g.Go(errgroupAssertEqual(t, m.domainManager, impl.GetDomainManager)) g.Go(errgroupAssertEqual(t, m.domainAuditManager, impl.GetDomainAuditManager)) g.Go(errgroupAssertEqual(t, m.taskManager, impl.GetTaskManager)) g.Go(errgroupAssertEqual(t, m.visibilityManager, impl.GetVisibilityManager)) g.Go(errgroupAssertEqual(t, m.replicationManager, impl.GetDomainReplicationQueueManager)) g.Go(errgroupAssertEqual(t, m.shardManager, impl.GetShardManager)) g.Go(errgroupAssertEqual(t, m.historyManager, impl.GetHistoryManager)) g.Go(errgroupAssertEqual(t, m.configManager, impl.GetConfigStoreManager)) require.NoError(t, g.Wait()) // execution managers are per shard, checked separately }) t.Run("Simple setters", func(t *testing.T) { t.Parallel() f, _, defaultMocks := beanSetup(t) _, m2, _ := beanSetup(t) // make a second set of mock objects, so they can be compared defaultMocks() // allow constructors to be called impl, err := NewBeanFromFactory(f, nil, nil) require.NoError(t, err) // these need to be concurrency-safe, so run them concurrently var g errgroup.Group g.Go(errgroupAssertSets(t, m2.domainManager, impl.SetDomainManager, impl.GetDomainManager)) g.Go(errgroupAssertSets(t, m2.domainAuditManager, impl.SetDomainAuditManager, impl.GetDomainAuditManager)) g.Go(errgroupAssertSets(t, m2.taskManager, impl.SetTaskManager, impl.GetTaskManager)) g.Go(errgroupAssertSets(t, m2.visibilityManager, impl.SetVisibilityManager, impl.GetVisibilityManager)) g.Go(errgroupAssertSets(t, m2.replicationManager, impl.SetDomainReplicationQueueManager, impl.GetDomainReplicationQueueManager)) g.Go(errgroupAssertSets(t, m2.shardManager, impl.SetShardManager, impl.GetShardManager)) g.Go(errgroupAssertSets(t, m2.historyManager, impl.SetHistoryManager, impl.GetHistoryManager)) g.Go(errgroupAssertSets(t, m2.configManager, impl.SetConfigStoreManager, impl.GetConfigStoreManager)) require.NoError(t, g.Wait()) // execution managers are per shard, checked separately }) t.Run("Execution manager getter", func(t *testing.T) { t.Parallel() f, m, defaultMocks := beanSetup(t) // execution managers are per shard, make sure they're set up correctly ex1, ex2 := persistence.NewMockExecutionManager(m.mockCtrl), persistence.NewMockExecutionManager(m.mockCtrl) f.EXPECT().NewExecutionManager(1).Return(ex1, nil).Times(1) f.EXPECT().NewExecutionManager(2).Return(ex2, nil).Times(1) // build all other mock objects so New works. // these will not duplicate ^ above instances, so if they are used they'll fail assert.Equal checks. defaultMocks() impl, err := NewBeanFromFactory(f, nil, nil) require.NoError(t, err) // must be concurrency safe, so run them concurrently var g errgroup.Group g.Go(errgroupAssertExecutionManagerEqual(t, 1, ex1, impl)) g.Go(errgroupAssertExecutionManagerEqual(t, 2, ex2, impl)) // make sure re-getting doesn't make a duplicate / a different instance. // the `.Times(1)` also ensures this does not make an extra construction. g.Go(errgroupAssertExecutionManagerEqual(t, 1, ex1, impl)) g.Go(errgroupAssertExecutionManagerEqual(t, 2, ex2, impl)) require.NoError(t, g.Wait()) }) t.Run("Execution manager setter", func(t *testing.T) { t.Parallel() f, m, defaultMocks := beanSetup(t) // seed with some pre-existing data so we can say "not same as before". f.EXPECT().NewExecutionManager(1).Return(persistence.NewMockExecutionManager(m.mockCtrl), nil).Times(1) f.EXPECT().NewExecutionManager(2).Return(persistence.NewMockExecutionManager(m.mockCtrl), nil).Times(1) defaultMocks() impl, err := NewBeanFromFactory(f, nil, nil) require.NoError(t, err) _, err = impl.GetExecutionManager(1) require.NoError(t, err, "setup sanity check failed") _, err = impl.GetExecutionManager(2) require.NoError(t, err, "setup sanity check failed") // make the execution managers that will be set ex1, ex2 := persistence.NewMockExecutionManager(m.mockCtrl), persistence.NewMockExecutionManager(m.mockCtrl) // must be concurrency safe, so run them concurrently var g errgroup.Group g.Go(errgroupAssertSetsExecutionManager(t, 1, ex1, impl)) g.Go(errgroupAssertSetsExecutionManager(t, 2, ex2, impl)) require.NoError(t, g.Wait()) }) t.Run("Lifecycle", func(t *testing.T) { t.Parallel() f, m, defaultMocks := beanSetup(t) // make some execution managers so they can be closed ex1, ex2 := persistence.NewMockExecutionManager(m.mockCtrl), persistence.NewMockExecutionManager(m.mockCtrl) f.EXPECT().NewExecutionManager(1).Return(ex1, nil).Times(1) f.EXPECT().NewExecutionManager(2).Return(ex2, nil).Times(1) // expect everything to close m.domainManager.EXPECT().Close().Return().Times(1) m.domainAuditManager.EXPECT().Close().Return().Times(1) m.taskManager.EXPECT().Close().Return().Times(1) m.visibilityManager.EXPECT().Close().Return().Times(1) m.replicationManager.EXPECT().Close().Return().Times(1) m.shardManager.EXPECT().Close().Return().Times(1) m.historyManager.EXPECT().Close().Return().Times(1) m.configManager.EXPECT().Close().Return().Times(1) ex1.EXPECT().Close().Return().Times(1) ex2.EXPECT().Close().Return().Times(1) // which includes the execution-manager-factory itself f.EXPECT().Close().Return().Times(1) defaultMocks() // for New impl, err := NewBeanFromFactory(f, nil, nil) require.NoError(t, err) // seed the execution managers (could call Set instead) v, err := impl.GetExecutionManager(1) require.NoError(t, err) require.NotNil(t, v) v, err = impl.GetExecutionManager(2) require.NoError(t, err) require.NotNil(t, v) // ensure everything is closed impl.Close() }) } // generics complains that the interface and mock-impl types don't match, // so this is done via reflection. testify ensures the values and types are the same. // // `actualFn` needs to be a `func() T` which returns the expected value/type. func errgroupAssertEqual(t *testing.T, expected, getFn any) func() error { t.Helper() return func() error { t.Helper() assertMocksEqual(t, expected, reflect.ValueOf(getFn).Call(nil)[0].Interface()) return nil } } // similar to above, not generics-friendly. // setFn must be `func(T)` and getFn must be `func() T`, or the calls will panic. func errgroupAssertSets(t *testing.T, expected, setFn, getFn any) func() error { t.Helper() return func() error { t.Helper() // sanity check first if !assertMocksNotEqual(t, expected, reflect.ValueOf(getFn).Call(nil)[0].Interface()) { return nil } // perform the SetX call. // panics if incorrect. reflect.ValueOf(setFn).Call([]reflect.Value{reflect.ValueOf(expected)}) // make sure the getter gets the set value assertMocksEqual(t, expected, reflect.ValueOf(getFn).Call(nil)[0].Interface()) return nil } } func errgroupAssertExecutionManagerEqual(t *testing.T, arg int, expected persistence.ExecutionManager, impl Bean) func() error { t.Helper() return func() error { t.Helper() val, err := impl.GetExecutionManager(arg) assert.NoError(t, err) // cannot use require in other goroutines assertMocksEqual(t, expected, val) return err } } func errgroupAssertSetsExecutionManager(t *testing.T, arg int, expected persistence.ExecutionManager, impl Bean) func() error { t.Helper() return func() error { t.Helper() // sanity check first val, err := impl.GetExecutionManager(arg) if !assert.NoError(t, err, "could not do initial Get to check values") { return nil } if !assertMocksNotEqual(t, expected, val) { return nil } impl.SetExecutionManager(arg, expected) val, err = impl.GetExecutionManager(arg) assert.NoError(t, err) // cannot use require in other goroutines assertMocksEqual(t, expected, val) return err } } // mock-equality helper because `assert.Equal` considers all mocks of the same type to be equal, // as they (generally) use the same mock controller and recorder, and it ignores different pointers // due to using reflect.DeepEqual. func assertMocksEqual(t *testing.T, expected, actual any) bool { t.Helper() if actual != expected { t.Errorf("does not contain the expected reference, got: %T(%p) expected: %T(%p)", actual, actual, expected, expected) return false } return true } // mock-not-equality helper because `assert.NotEqual` considers all mocks of the same type to be equal, // as they (generally) use the same mock controller and recorder, and it ignores different pointers // due to using reflect.DeepEqual. func assertMocksNotEqual(t *testing.T, expected, actual any) bool { t.Helper() if actual == expected { t.Errorf("actual and expected values (pointers) are identical") return false } return true } ================================================ FILE: common/persistence/client/factory.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination factory_mock.go package client import ( "sync" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" es "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/elasticsearch" "github.com/uber/cadence/common/persistence/nosql" pinotVisibility "github.com/uber/cadence/common/persistence/pinot" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql" "github.com/uber/cadence/common/persistence/wrappers/errorinjectors" "github.com/uber/cadence/common/persistence/wrappers/metered" "github.com/uber/cadence/common/persistence/wrappers/ratelimited" "github.com/uber/cadence/common/persistence/wrappers/sampled" pnt "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/service" ) type ( // Factory defines the interface for any implementation that can vend // persistence layer objects backed by a datastore. The actual datastore // is implementation detail hidden behind this interface Factory interface { // Close the factory Close() // NewTaskManager returns a new task manager NewTaskManager() (p.TaskManager, error) // NewShardManager returns a new shard manager NewShardManager() (p.ShardManager, error) // NewHistoryManager returns a new history manager NewHistoryManager() (p.HistoryManager, error) // NewDomainManager returns a new metadata manager NewDomainManager() (p.DomainManager, error) // NewDomainAuditManager returns a new domain audit manager NewDomainAuditManager() (p.DomainAuditManager, error) // NewExecutionManager returns a new execution manager for a given shardID NewExecutionManager(shardID int) (p.ExecutionManager, error) // NewVisibilityManager returns a new visibility manager NewVisibilityManager(params *Params, serviceConfig *service.Config) (p.VisibilityManager, error) // NewDomainReplicationQueueManager returns a new queue for domain replication NewDomainReplicationQueueManager() (p.QueueManager, error) // NewConfigStoreManager returns a new config store manager NewConfigStoreManager() (p.ConfigStoreManager, error) } // DataStoreFactory is a low level interface to be implemented by a datastore // Examples of datastores are cassandra, mysql etc DataStoreFactory interface { // Close closes the factory Close() // NewTaskStore returns a new task store NewTaskStore() (p.TaskStore, error) // NewShardStore returns a new shard store NewShardStore() (p.ShardStore, error) // NewHistoryStore returns a new history store NewHistoryStore() (p.HistoryStore, error) // NewDomainStore returns a new metadata store NewDomainStore() (p.DomainStore, error) // NewDomainAuditStore returns a new domain audit store NewDomainAuditStore() (p.DomainAuditStore, error) // NewExecutionStore returns an execution store for given shardID NewExecutionStore(shardID int) (p.ExecutionStore, error) // NewVisibilityStore returns a new visibility store, // TODO We temporarily using sortByCloseTime to determine whether or not ListClosedWorkflowExecutions should // be ordering by CloseTime. This will be removed when implementing https://github.com/uber/cadence/issues/3621 NewVisibilityStore(sortByCloseTime bool) (p.VisibilityStore, error) NewQueue(queueType p.QueueType) (p.QueueStore, error) // NewConfigStore returns a new config store NewConfigStore() (p.ConfigStore, error) } // Datastore represents a datastore Datastore struct { factory DataStoreFactory ratelimit quotas.Limiter } factoryImpl struct { sync.RWMutex config *config.Persistence metricsClient metrics.Client logger log.Logger datastores map[storeType]Datastore clusterName string dc *p.DynamicConfiguration } storeType int ) const ( storeTypeHistory storeType = iota + 1 storeTypeTask storeTypeShard storeTypeMetadata storeTypeExecution storeTypeVisibility storeTypeQueue storeTypeConfigStore ) var storeTypes = []storeType{ storeTypeHistory, storeTypeTask, storeTypeShard, storeTypeMetadata, storeTypeExecution, storeTypeVisibility, storeTypeQueue, storeTypeConfigStore, } // NewFactory returns an implementation of factory that vends persistence objects based on // specified configuration. This factory takes as input a config.Persistence object // which specifies the datastore to be used for a given type of object. This config // also contains config for individual datastores themselves. // // The objects returned by this factory enforce ratelimit and maxconns according to // given configuration. In addition, all objects will emit metrics automatically func NewFactory( cfg *config.Persistence, persistenceMaxQPS quotas.RPSFunc, clusterName string, metricsClient metrics.Client, logger log.Logger, dc *p.DynamicConfiguration, ) Factory { factory := &factoryImpl{ config: cfg, metricsClient: metricsClient, logger: logger, clusterName: clusterName, dc: dc, } limiters := buildRatelimiters(cfg, persistenceMaxQPS) factory.init(clusterName, limiters) return factory } // NewTaskManager returns a new task manager func (f *factoryImpl) NewTaskManager() (p.TaskManager, error) { ds := f.datastores[storeTypeTask] store, err := ds.factory.NewTaskStore() if err != nil { return nil, err } result := p.NewTaskManager(store) if errorRate := f.config.ErrorInjectionRate(); errorRate != 0 { result = errorinjectors.NewTaskManager(result, errorRate, f.logger, time.Now()) } if ds.ratelimit != nil { result = ratelimited.NewTaskManager(result, ds.ratelimit, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) } if f.metricsClient != nil { result = metered.NewTaskManager(result, f.metricsClient, f.logger, f.config) } return result, nil } // NewShardManager returns a new shard manager func (f *factoryImpl) NewShardManager() (p.ShardManager, error) { ds := f.datastores[storeTypeShard] store, err := ds.factory.NewShardStore() if err != nil { return nil, err } result := p.NewShardManager(store, f.dc) if errorRate := f.config.ErrorInjectionRate(); errorRate != 0 { result = errorinjectors.NewShardManager(result, errorRate, f.logger, time.Now()) } if ds.ratelimit != nil { result = ratelimited.NewShardManager(result, ds.ratelimit, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) } if f.metricsClient != nil { result = metered.NewShardManager(result, f.metricsClient, f.logger, f.config) } return result, nil } // NewHistoryManager returns a new history manager func (f *factoryImpl) NewHistoryManager() (p.HistoryManager, error) { ds := f.datastores[storeTypeHistory] store, err := ds.factory.NewHistoryStore() if err != nil { return nil, err } result := p.NewHistoryV2ManagerImpl(store, f.logger, p.NewPayloadSerializer(), codec.NewThriftRWEncoder(), f.config.TransactionSizeLimit) if errorRate := f.config.ErrorInjectionRate(); errorRate != 0 { result = errorinjectors.NewHistoryManager(result, errorRate, f.logger, time.Now()) } if ds.ratelimit != nil { result = ratelimited.NewHistoryManager(result, ds.ratelimit, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) } if f.metricsClient != nil { result = metered.NewHistoryManager(result, f.metricsClient, f.logger, f.config) } return result, nil } // NewDomainManager returns a new metadata manager func (f *factoryImpl) NewDomainManager() (p.DomainManager, error) { var err error var store p.DomainStore ds := f.datastores[storeTypeMetadata] store, err = ds.factory.NewDomainStore() if err != nil { return nil, err } result := p.NewDomainManagerImpl(store, f.logger, p.NewPayloadSerializer(), f.dc) if errorRate := f.config.ErrorInjectionRate(); errorRate != 0 { result = errorinjectors.NewDomainManager(result, errorRate, f.logger, time.Now()) } if ds.ratelimit != nil { result = ratelimited.NewDomainManager(result, ds.ratelimit, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) } if f.metricsClient != nil { result = metered.NewDomainManager(result, f.metricsClient, f.logger, f.config) } return result, nil } // NewDomainAuditManager returns a new domain audit manager func (f *factoryImpl) NewDomainAuditManager() (p.DomainAuditManager, error) { var err error var store p.DomainAuditStore ds := f.datastores[storeTypeMetadata] store, err = ds.factory.NewDomainAuditStore() if err != nil { return nil, err } if store == nil { return nil, nil } result := p.NewDomainAuditManagerImpl(store, f.logger, p.NewPayloadSerializer(), f.dc) return result, nil } // NewExecutionManager returns a new execution manager for a given shardID func (f *factoryImpl) NewExecutionManager(shardID int) (p.ExecutionManager, error) { ds := f.datastores[storeTypeExecution] store, err := ds.factory.NewExecutionStore(shardID) if err != nil { return nil, err } result := p.NewExecutionManagerImpl(store, f.logger, p.NewPayloadSerializer(), f.dc) if errorRate := f.config.ErrorInjectionRate(); errorRate != 0 { result = errorinjectors.NewExecutionManager(result, errorRate, f.logger, time.Now()) } if ds.ratelimit != nil { result = ratelimited.NewExecutionManager(result, ds.ratelimit, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) } if f.metricsClient != nil { result = metered.NewExecutionManager(result, f.metricsClient, f.logger, f.config, f.dc.PersistenceSampleLoggingRate, f.dc.EnableShardIDMetrics) } return result, nil } // NewVisibilityManager returns a new visibility manager func (f *factoryImpl) NewVisibilityManager( params *Params, resourceConfig *service.Config, ) (p.VisibilityManager, error) { if resourceConfig.ReadVisibilityStoreName == nil && resourceConfig.WriteVisibilityStoreName == nil { // No need to create visibility manager as no read/write needed return nil, nil } var visibilityFromDB, visibilityFromES, visibilityFromPinot, visibilityFromOS p.VisibilityManager var err error if params.PersistenceConfig.VisibilityStore != "" { visibilityFromDB, err = f.newDBVisibilityManager(resourceConfig) if err != nil { return nil, err } } switch params.PersistenceConfig.AdvancedVisibilityStore { case constants.PinotVisibilityStoreName: visibilityFromPinot, err = setupPinotVisibilityManager(params, resourceConfig, f.logger, f.dc, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) if err != nil { f.logger.Fatal("Creating Pinot advanced visibility manager failed", tag.Error(err)) } visibilityMgrs := map[string]p.VisibilityManager{ constants.VisibilityModeDB: visibilityFromDB, constants.VisibilityModePinot: visibilityFromPinot, } if params.PinotConfig.Migration.Enabled { visibilityFromES, err = setupESVisibilityManager(params, resourceConfig, f.logger, f.dc, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) if err != nil { f.logger.Fatal("Creating ES advanced visibility manager failed", tag.Error(err)) } visibilityMgrs[constants.VisibilityModeES] = visibilityFromES } return p.NewVisibilityHybridManager( visibilityMgrs, resourceConfig.ReadVisibilityStoreName, resourceConfig.WriteVisibilityStoreName, resourceConfig.EnableLogCustomerQueryParameter, constants.PinotPersistenceName, f.logger, ), nil case constants.OSVisibilityStoreName: visibilityFromOS, err = setupOSVisibilityManager(params, resourceConfig, f.logger, f.dc, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) if err != nil { f.logger.Fatal("Creating OS advanced visibility manager failed", tag.Error(err)) } visibilityMgrs := map[string]p.VisibilityManager{ constants.VisibilityModeDB: visibilityFromDB, constants.VisibilityModeOS: visibilityFromOS, } if params.OSConfig.Migration.Enabled { visibilityFromES, err = setupESVisibilityManager(params, resourceConfig, f.logger, f.dc, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) if err != nil { f.logger.Fatal("Creating ES advanced visibility manager failed", tag.Error(err)) } visibilityMgrs[constants.VisibilityModeES] = visibilityFromES } return p.NewVisibilityHybridManager( visibilityMgrs, resourceConfig.ReadVisibilityStoreName, resourceConfig.WriteVisibilityStoreName, resourceConfig.EnableLogCustomerQueryParameter, constants.ESPersistenceName, f.logger, ), nil case constants.ESVisibilityStoreName: visibilityFromES, err = setupESVisibilityManager(params, resourceConfig, f.logger, f.dc, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) if err != nil { f.logger.Fatal("Creating advanced visibility manager failed", tag.Error(err)) } visibilityMgrs := map[string]p.VisibilityManager{ constants.VisibilityModeDB: visibilityFromDB, constants.VisibilityModeES: visibilityFromES, } return p.NewVisibilityHybridManager( visibilityMgrs, resourceConfig.ReadVisibilityStoreName, resourceConfig.WriteVisibilityStoreName, resourceConfig.EnableLogCustomerQueryParameter, constants.ESPersistenceName, f.logger, ), nil default: visibilityMgrs := map[string]p.VisibilityManager{ constants.VisibilityModeDB: visibilityFromDB, } if visibilityFromDB != nil { return p.NewVisibilityHybridManager( visibilityMgrs, resourceConfig.ReadVisibilityStoreName, resourceConfig.WriteVisibilityStoreName, resourceConfig.EnableLogCustomerQueryParameter, visibilityFromDB.GetName(), // db has multiple different stores f.logger, ), nil } return nil, nil // no visibility manager available for write } } // NewESVisibilityManager create a visibility manager for ElasticSearch // In history, it only needs kafka producer for writing data; // In frontend, it only needs ES client and related config for reading data func newPinotVisibilityManager( pinotClient pnt.GenericClient, visibilityConfig *service.Config, producer messaging.Producer, metricsClient metrics.Client, log log.Logger, dc *p.DynamicConfiguration, callerBypass quotas.CallerBypass, ) p.VisibilityManager { visibilityFromPinotStore := pinotVisibility.NewPinotVisibilityStore(pinotClient, visibilityConfig, producer, log) visibilityFromPinot := p.NewVisibilityManagerImpl(visibilityFromPinotStore, log, dc) // wrap with rate limiter if visibilityConfig.PersistenceMaxQPS != nil && visibilityConfig.PersistenceMaxQPS() != 0 { pinotRateLimiter := quotas.NewDynamicRateLimiter(visibilityConfig.PersistenceMaxQPS.AsFloat64()) visibilityFromPinot = ratelimited.NewVisibilityManager(visibilityFromPinot, pinotRateLimiter, callerBypass) } if metricsClient != nil { // wrap with metrics visibilityFromPinot = pinotVisibility.NewPinotVisibilityMetricsClient(visibilityFromPinot, metricsClient, log) } return visibilityFromPinot } // NewESVisibilityManager create a visibility manager for ElasticSearch // In history, it only needs kafka producer for writing data; // In frontend, it only needs ES client and related config for reading data func newESVisibilityManager( indexName string, esClient es.GenericClient, visibilityConfig *service.Config, producer messaging.Producer, metricsClient metrics.Client, log log.Logger, dc *p.DynamicConfiguration, callerBypass quotas.CallerBypass, ) p.VisibilityManager { visibilityFromESStore := elasticsearch.NewElasticSearchVisibilityStore(esClient, indexName, producer, visibilityConfig, log) visibilityFromES := p.NewVisibilityManagerImpl(visibilityFromESStore, log, dc) // wrap with rate limiter if visibilityConfig.PersistenceMaxQPS != nil && visibilityConfig.PersistenceMaxQPS() != 0 { esRateLimiter := quotas.NewDynamicRateLimiter(visibilityConfig.PersistenceMaxQPS.AsFloat64()) visibilityFromES = ratelimited.NewVisibilityManager(visibilityFromES, esRateLimiter, callerBypass) } if metricsClient != nil { // wrap with metrics visibilityFromES = elasticsearch.NewVisibilityMetricsClient(visibilityFromES, metricsClient, log) } return visibilityFromES } func (f *factoryImpl) newDBVisibilityManager( visibilityConfig *service.Config, ) (p.VisibilityManager, error) { enableReadFromClosedExecutionV2 := false if visibilityConfig.EnableReadDBVisibilityFromClosedExecutionV2 != nil { enableReadFromClosedExecutionV2 = visibilityConfig.EnableReadDBVisibilityFromClosedExecutionV2() } else { f.logger.Warn("missing visibility and EnableReadFromClosedExecutionV2 config", tag.Value(visibilityConfig)) } ds := f.datastores[storeTypeVisibility] store, err := ds.factory.NewVisibilityStore(enableReadFromClosedExecutionV2) if err != nil { return nil, err } result := p.NewVisibilityManagerImpl(store, f.logger, f.dc) if errorRate := f.config.ErrorInjectionRate(); errorRate != 0 { result = errorinjectors.NewVisibilityManager(result, errorRate, f.logger, time.Now()) } if ds.ratelimit != nil { result = ratelimited.NewVisibilityManager(result, ds.ratelimit, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) } if visibilityConfig.EnableDBVisibilitySampling != nil && visibilityConfig.EnableDBVisibilitySampling() { result = sampled.NewVisibilityManager(result, sampled.Params{ Config: &sampled.Config{ VisibilityClosedMaxQPS: visibilityConfig.WriteDBVisibilityClosedMaxQPS, VisibilityListMaxQPS: visibilityConfig.DBVisibilityListMaxQPS, VisibilityOpenMaxQPS: visibilityConfig.WriteDBVisibilityOpenMaxQPS, }, MetricClient: f.metricsClient, Logger: f.logger, TimeSource: clock.NewRealTimeSource(), RateLimiterFactoryFunc: sampled.NewDomainToBucketMap, }) } if f.metricsClient != nil { result = metered.NewVisibilityManager(result, f.metricsClient, f.logger, f.config) } return result, nil } func (f *factoryImpl) NewDomainReplicationQueueManager() (p.QueueManager, error) { ds := f.datastores[storeTypeQueue] store, err := ds.factory.NewQueue(p.DomainReplicationQueueType) if err != nil { return nil, err } result := p.NewQueueManager(store) if errorRate := f.config.ErrorInjectionRate(); errorRate != 0 { result = errorinjectors.NewQueueManager(result, errorRate, f.logger, time.Now()) } if ds.ratelimit != nil { result = ratelimited.NewQueueManager(result, ds.ratelimit, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) } if f.metricsClient != nil { result = metered.NewQueueManager(result, f.metricsClient, f.logger, f.config) } return result, nil } func (f *factoryImpl) NewConfigStoreManager() (p.ConfigStoreManager, error) { ds := f.datastores[storeTypeConfigStore] store, err := ds.factory.NewConfigStore() if err != nil { return nil, err } result := p.NewConfigStoreManagerImpl(store, f.logger) if errorRate := f.config.ErrorInjectionRate(); errorRate != 0 { result = errorinjectors.NewConfigStoreManager(result, errorRate, f.logger, time.Now()) } if ds.ratelimit != nil { result = ratelimited.NewConfigStoreManager(result, ds.ratelimit, quotas.NewCallerBypass(f.dc.RateLimiterBypassCallerTypes)) } if f.metricsClient != nil { result = metered.NewConfigStoreManager(result, f.metricsClient, f.logger, f.config) } return result, nil } // Close closes this factory func (f *factoryImpl) Close() { ds := f.datastores[storeTypeExecution] ds.factory.Close() } func (f *factoryImpl) init(clusterName string, limiters map[string]quotas.Limiter) { f.datastores = make(map[storeType]Datastore, len(storeTypes)) defaultCfg := f.config.DataStores[f.config.DefaultStore] if defaultCfg.Cassandra != nil { f.logger.Warn("Cassandra config is deprecated, please use NoSQL with pluginName of cassandra.") } defaultDataStore := Datastore{ratelimit: limiters[f.config.DefaultStore]} switch { case defaultCfg.NoSQL != nil: parser := f.getParser() taskSerializer := serialization.NewTaskSerializer(parser) shardedNoSQLConfig := defaultCfg.NoSQL.ConvertToShardedNoSQLConfig() defaultDataStore.factory = nosql.NewFactory(*shardedNoSQLConfig, clusterName, f.logger, f.metricsClient, taskSerializer, parser, f.dc) case defaultCfg.ShardedNoSQL != nil: parser := f.getParser() taskSerializer := serialization.NewTaskSerializer(parser) defaultDataStore.factory = nosql.NewFactory(*defaultCfg.ShardedNoSQL, clusterName, f.logger, f.metricsClient, taskSerializer, parser, f.dc) case defaultCfg.SQL != nil: if defaultCfg.SQL.EncodingType == "" { defaultCfg.SQL.EncodingType = string(constants.EncodingTypeThriftRW) } if len(defaultCfg.SQL.DecodingTypes) == 0 { defaultCfg.SQL.DecodingTypes = []string{ string(constants.EncodingTypeThriftRW), } } defaultDataStore.factory = sql.NewFactory(*defaultCfg.SQL, clusterName, f.logger, f.getParser(), f.dc) default: f.logger.Fatal("invalid config: one of nosql or sql params must be specified for defaultDataStore") } for _, st := range storeTypes { if st != storeTypeVisibility { f.datastores[st] = defaultDataStore } } visibilityCfg, ok := f.config.DataStores[f.config.VisibilityStore] if !ok { f.logger.Info("no visibilityStore is configured, will use advancedVisibilityStore") // NOTE: f.datastores[storeTypeVisibility] will be nil return } if visibilityCfg.Cassandra != nil { f.logger.Warn("Cassandra config is deprecated, please use NoSQL with pluginName of cassandra.") } visibilityDataStore := Datastore{ratelimit: limiters[f.config.VisibilityStore]} switch { case visibilityCfg.NoSQL != nil: parser := f.getParser() taskSerializer := serialization.NewTaskSerializer(parser) shardedNoSQLConfig := visibilityCfg.NoSQL.ConvertToShardedNoSQLConfig() visibilityDataStore.factory = nosql.NewFactory(*shardedNoSQLConfig, clusterName, f.logger, f.metricsClient, taskSerializer, parser, f.dc) case visibilityCfg.SQL != nil: visibilityDataStore.factory = sql.NewFactory(*visibilityCfg.SQL, clusterName, f.logger, f.getParser(), f.dc) default: f.logger.Fatal("invalid config: one of nosql or sql params must be specified for visibilityStore") } f.datastores[storeTypeVisibility] = visibilityDataStore } func (f *factoryImpl) getParser() serialization.Parser { parser, err := serialization.NewParser(f.dc) if err != nil { f.logger.Fatal("failed to construct parser", tag.Error(err)) } return parser } func buildRatelimiters(cfg *config.Persistence, maxQPS quotas.RPSFunc) map[string]quotas.Limiter { result := make(map[string]quotas.Limiter, len(cfg.DataStores)) for dsName := range cfg.DataStores { if maxQPS != nil && maxQPS() > 0 { result[dsName] = quotas.NewDynamicRateLimiter(maxQPS) } } return result } func setupPinotVisibilityManager(params *Params, resourceConfig *service.Config, logger log.Logger, dc *p.DynamicConfiguration, callerBypass quotas.CallerBypass) (p.VisibilityManager, error) { visibilityProducer, err := params.MessagingClient.NewProducer(constants.PinotVisibilityAppName) if err != nil { return nil, err } return newPinotVisibilityManager(params.PinotClient, resourceConfig, visibilityProducer, params.MetricsClient, logger, dc, callerBypass), nil } func setupESVisibilityManager(params *Params, resourceConfig *service.Config, logger log.Logger, dc *p.DynamicConfiguration, callerBypass quotas.CallerBypass) (p.VisibilityManager, error) { visibilityIndexName := params.ESConfig.Indices[constants.VisibilityAppName] visibilityProducer, err := params.MessagingClient.NewProducer(constants.VisibilityAppName) if err != nil { return nil, err } return newESVisibilityManager(visibilityIndexName, params.ESClient, resourceConfig, visibilityProducer, params.MetricsClient, logger, dc, callerBypass), nil } func setupOSVisibilityManager(params *Params, resourceConfig *service.Config, logger log.Logger, dc *p.DynamicConfiguration, callerBypass quotas.CallerBypass) (p.VisibilityManager, error) { visibilityIndexName := params.OSConfig.Indices[constants.VisibilityAppName] visibilityProducer, err := params.MessagingClient.NewProducer(constants.VisibilityAppName) if err != nil { return nil, err } return newESVisibilityManager(visibilityIndexName, params.OSClient, resourceConfig, visibilityProducer, params.MetricsClient, logger, dc, callerBypass), nil } ================================================ FILE: common/persistence/client/factory_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: factory.go // // Generated by this command: // // mockgen -package client -source factory.go -destination factory_mock.go // // Package client is a generated GoMock package. package client import ( reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" service "github.com/uber/cadence/common/service" ) // MockFactory is a mock of Factory interface. type MockFactory struct { ctrl *gomock.Controller recorder *MockFactoryMockRecorder isgomock struct{} } // MockFactoryMockRecorder is the mock recorder for MockFactory. type MockFactoryMockRecorder struct { mock *MockFactory } // NewMockFactory creates a new mock instance. func NewMockFactory(ctrl *gomock.Controller) *MockFactory { mock := &MockFactory{ctrl: ctrl} mock.recorder = &MockFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFactory) EXPECT() *MockFactoryMockRecorder { return m.recorder } // Close mocks base method. func (m *MockFactory) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockFactoryMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockFactory)(nil).Close)) } // NewConfigStoreManager mocks base method. func (m *MockFactory) NewConfigStoreManager() (persistence.ConfigStoreManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewConfigStoreManager") ret0, _ := ret[0].(persistence.ConfigStoreManager) ret1, _ := ret[1].(error) return ret0, ret1 } // NewConfigStoreManager indicates an expected call of NewConfigStoreManager. func (mr *MockFactoryMockRecorder) NewConfigStoreManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewConfigStoreManager", reflect.TypeOf((*MockFactory)(nil).NewConfigStoreManager)) } // NewDomainAuditManager mocks base method. func (m *MockFactory) NewDomainAuditManager() (persistence.DomainAuditManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewDomainAuditManager") ret0, _ := ret[0].(persistence.DomainAuditManager) ret1, _ := ret[1].(error) return ret0, ret1 } // NewDomainAuditManager indicates an expected call of NewDomainAuditManager. func (mr *MockFactoryMockRecorder) NewDomainAuditManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDomainAuditManager", reflect.TypeOf((*MockFactory)(nil).NewDomainAuditManager)) } // NewDomainManager mocks base method. func (m *MockFactory) NewDomainManager() (persistence.DomainManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewDomainManager") ret0, _ := ret[0].(persistence.DomainManager) ret1, _ := ret[1].(error) return ret0, ret1 } // NewDomainManager indicates an expected call of NewDomainManager. func (mr *MockFactoryMockRecorder) NewDomainManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDomainManager", reflect.TypeOf((*MockFactory)(nil).NewDomainManager)) } // NewDomainReplicationQueueManager mocks base method. func (m *MockFactory) NewDomainReplicationQueueManager() (persistence.QueueManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewDomainReplicationQueueManager") ret0, _ := ret[0].(persistence.QueueManager) ret1, _ := ret[1].(error) return ret0, ret1 } // NewDomainReplicationQueueManager indicates an expected call of NewDomainReplicationQueueManager. func (mr *MockFactoryMockRecorder) NewDomainReplicationQueueManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDomainReplicationQueueManager", reflect.TypeOf((*MockFactory)(nil).NewDomainReplicationQueueManager)) } // NewExecutionManager mocks base method. func (m *MockFactory) NewExecutionManager(shardID int) (persistence.ExecutionManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewExecutionManager", shardID) ret0, _ := ret[0].(persistence.ExecutionManager) ret1, _ := ret[1].(error) return ret0, ret1 } // NewExecutionManager indicates an expected call of NewExecutionManager. func (mr *MockFactoryMockRecorder) NewExecutionManager(shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewExecutionManager", reflect.TypeOf((*MockFactory)(nil).NewExecutionManager), shardID) } // NewHistoryManager mocks base method. func (m *MockFactory) NewHistoryManager() (persistence.HistoryManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewHistoryManager") ret0, _ := ret[0].(persistence.HistoryManager) ret1, _ := ret[1].(error) return ret0, ret1 } // NewHistoryManager indicates an expected call of NewHistoryManager. func (mr *MockFactoryMockRecorder) NewHistoryManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewHistoryManager", reflect.TypeOf((*MockFactory)(nil).NewHistoryManager)) } // NewShardManager mocks base method. func (m *MockFactory) NewShardManager() (persistence.ShardManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewShardManager") ret0, _ := ret[0].(persistence.ShardManager) ret1, _ := ret[1].(error) return ret0, ret1 } // NewShardManager indicates an expected call of NewShardManager. func (mr *MockFactoryMockRecorder) NewShardManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewShardManager", reflect.TypeOf((*MockFactory)(nil).NewShardManager)) } // NewTaskManager mocks base method. func (m *MockFactory) NewTaskManager() (persistence.TaskManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewTaskManager") ret0, _ := ret[0].(persistence.TaskManager) ret1, _ := ret[1].(error) return ret0, ret1 } // NewTaskManager indicates an expected call of NewTaskManager. func (mr *MockFactoryMockRecorder) NewTaskManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewTaskManager", reflect.TypeOf((*MockFactory)(nil).NewTaskManager)) } // NewVisibilityManager mocks base method. func (m *MockFactory) NewVisibilityManager(params *Params, serviceConfig *service.Config) (persistence.VisibilityManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewVisibilityManager", params, serviceConfig) ret0, _ := ret[0].(persistence.VisibilityManager) ret1, _ := ret[1].(error) return ret0, ret1 } // NewVisibilityManager indicates an expected call of NewVisibilityManager. func (mr *MockFactoryMockRecorder) NewVisibilityManager(params, serviceConfig any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewVisibilityManager", reflect.TypeOf((*MockFactory)(nil).NewVisibilityManager), params, serviceConfig) } // MockDataStoreFactory is a mock of DataStoreFactory interface. type MockDataStoreFactory struct { ctrl *gomock.Controller recorder *MockDataStoreFactoryMockRecorder isgomock struct{} } // MockDataStoreFactoryMockRecorder is the mock recorder for MockDataStoreFactory. type MockDataStoreFactoryMockRecorder struct { mock *MockDataStoreFactory } // NewMockDataStoreFactory creates a new mock instance. func NewMockDataStoreFactory(ctrl *gomock.Controller) *MockDataStoreFactory { mock := &MockDataStoreFactory{ctrl: ctrl} mock.recorder = &MockDataStoreFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDataStoreFactory) EXPECT() *MockDataStoreFactoryMockRecorder { return m.recorder } // Close mocks base method. func (m *MockDataStoreFactory) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockDataStoreFactoryMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDataStoreFactory)(nil).Close)) } // NewConfigStore mocks base method. func (m *MockDataStoreFactory) NewConfigStore() (persistence.ConfigStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewConfigStore") ret0, _ := ret[0].(persistence.ConfigStore) ret1, _ := ret[1].(error) return ret0, ret1 } // NewConfigStore indicates an expected call of NewConfigStore. func (mr *MockDataStoreFactoryMockRecorder) NewConfigStore() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewConfigStore", reflect.TypeOf((*MockDataStoreFactory)(nil).NewConfigStore)) } // NewDomainAuditStore mocks base method. func (m *MockDataStoreFactory) NewDomainAuditStore() (persistence.DomainAuditStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewDomainAuditStore") ret0, _ := ret[0].(persistence.DomainAuditStore) ret1, _ := ret[1].(error) return ret0, ret1 } // NewDomainAuditStore indicates an expected call of NewDomainAuditStore. func (mr *MockDataStoreFactoryMockRecorder) NewDomainAuditStore() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDomainAuditStore", reflect.TypeOf((*MockDataStoreFactory)(nil).NewDomainAuditStore)) } // NewDomainStore mocks base method. func (m *MockDataStoreFactory) NewDomainStore() (persistence.DomainStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewDomainStore") ret0, _ := ret[0].(persistence.DomainStore) ret1, _ := ret[1].(error) return ret0, ret1 } // NewDomainStore indicates an expected call of NewDomainStore. func (mr *MockDataStoreFactoryMockRecorder) NewDomainStore() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDomainStore", reflect.TypeOf((*MockDataStoreFactory)(nil).NewDomainStore)) } // NewExecutionStore mocks base method. func (m *MockDataStoreFactory) NewExecutionStore(shardID int) (persistence.ExecutionStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewExecutionStore", shardID) ret0, _ := ret[0].(persistence.ExecutionStore) ret1, _ := ret[1].(error) return ret0, ret1 } // NewExecutionStore indicates an expected call of NewExecutionStore. func (mr *MockDataStoreFactoryMockRecorder) NewExecutionStore(shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewExecutionStore", reflect.TypeOf((*MockDataStoreFactory)(nil).NewExecutionStore), shardID) } // NewHistoryStore mocks base method. func (m *MockDataStoreFactory) NewHistoryStore() (persistence.HistoryStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewHistoryStore") ret0, _ := ret[0].(persistence.HistoryStore) ret1, _ := ret[1].(error) return ret0, ret1 } // NewHistoryStore indicates an expected call of NewHistoryStore. func (mr *MockDataStoreFactoryMockRecorder) NewHistoryStore() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewHistoryStore", reflect.TypeOf((*MockDataStoreFactory)(nil).NewHistoryStore)) } // NewQueue mocks base method. func (m *MockDataStoreFactory) NewQueue(queueType persistence.QueueType) (persistence.QueueStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewQueue", queueType) ret0, _ := ret[0].(persistence.QueueStore) ret1, _ := ret[1].(error) return ret0, ret1 } // NewQueue indicates an expected call of NewQueue. func (mr *MockDataStoreFactoryMockRecorder) NewQueue(queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewQueue", reflect.TypeOf((*MockDataStoreFactory)(nil).NewQueue), queueType) } // NewShardStore mocks base method. func (m *MockDataStoreFactory) NewShardStore() (persistence.ShardStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewShardStore") ret0, _ := ret[0].(persistence.ShardStore) ret1, _ := ret[1].(error) return ret0, ret1 } // NewShardStore indicates an expected call of NewShardStore. func (mr *MockDataStoreFactoryMockRecorder) NewShardStore() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewShardStore", reflect.TypeOf((*MockDataStoreFactory)(nil).NewShardStore)) } // NewTaskStore mocks base method. func (m *MockDataStoreFactory) NewTaskStore() (persistence.TaskStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewTaskStore") ret0, _ := ret[0].(persistence.TaskStore) ret1, _ := ret[1].(error) return ret0, ret1 } // NewTaskStore indicates an expected call of NewTaskStore. func (mr *MockDataStoreFactoryMockRecorder) NewTaskStore() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewTaskStore", reflect.TypeOf((*MockDataStoreFactory)(nil).NewTaskStore)) } // NewVisibilityStore mocks base method. func (m *MockDataStoreFactory) NewVisibilityStore(sortByCloseTime bool) (persistence.VisibilityStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewVisibilityStore", sortByCloseTime) ret0, _ := ret[0].(persistence.VisibilityStore) ret1, _ := ret[1].(error) return ret0, ret1 } // NewVisibilityStore indicates an expected call of NewVisibilityStore. func (mr *MockDataStoreFactoryMockRecorder) NewVisibilityStore(sortByCloseTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewVisibilityStore", reflect.TypeOf((*MockDataStoreFactory)(nil).NewVisibilityStore), sortByCloseTime) } ================================================ FILE: common/persistence/client/factory_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package client import ( "math/rand" "testing" "github.com/IBM/sarama/mocks" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/messaging/kafka" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" ) /* New...Manager methods are intentionally NOT tested here. They require importing a datastore plugin, which imports the persistence tests, which imports this package, leading to a cycle. They also generally try to connect to the target datastore *immediately* upon construction, which means these would need to be integration tests... ...which we already have, in the persistence/persistence-tests package. */ func TestNew(t *testing.T) { // largely a sanity test for makeFactory, but it does ensure it constructs and closes, // though this does not actually achieve very much. fact := makeFactory(t) fact.Close() } // check ensures the func returns a value and no error. // most manager-constructors can use this, but not all. func check[T interface{}](t *testing.T, fn func() (T, error)) { val, err := fn() assert.NoError(t, err, "manager-constructor method should not error") assert.NotNil(t, val, "manager-constructor method should return a non-nil value") } func TestFactoryMethods(t *testing.T) { t.Run("NewTaskManager", func(t *testing.T) { fact := makeFactory(t) ds := mockDatastore(t, fact, storeTypeTask) ds.EXPECT().NewTaskStore().Return(nil, nil).MinTimes(1) check(t, fact.NewTaskManager) }) t.Run("NewShardManager", func(t *testing.T) { fact := makeFactory(t) ds := mockDatastore(t, fact, storeTypeShard) ds.EXPECT().NewShardStore().Return(nil, nil).MinTimes(1) check(t, fact.NewShardManager) }) t.Run("NewHistoryManager", func(t *testing.T) { fact := makeFactory(t) ds := mockDatastore(t, fact, storeTypeHistory) ds.EXPECT().NewHistoryStore().Return(nil, nil).MinTimes(1) check(t, fact.NewHistoryManager) }) t.Run("NewDomainManager", func(t *testing.T) { fact := makeFactory(t) ds := mockDatastore(t, fact, storeTypeMetadata) ds.EXPECT().NewDomainStore().Return(nil, nil).MinTimes(1) check(t, fact.NewDomainManager) }) t.Run("NewExecutionManager", func(t *testing.T) { // cannot control the persistence.NewExecutionManagerImpl call, so we must prevent calls to it. // currently the only call is the metrics wrapper calling GetShardID(), so just don't use the metrics wrapper. fact := makeFactoryWithMetrics(t, false) ds := mockDatastore(t, fact, storeTypeExecution) shard := rand.Int() ds.EXPECT().NewExecutionStore(shard).Return(nil, nil).MinTimes(1) em, err := fact.NewExecutionManager(shard) assert.NoError(t, err) assert.NotNil(t, em) }) t.Run("NewVisibilityManager can be nil", func(t *testing.T) { fact := makeFactory(t) // no datastores are mocked as there are no calls at all expected vm, err := fact.NewVisibilityManager( nil, // params are unused &service.Config{ // if both of these are nil, a nil response is correct, // because no "manager" is needed: // ES cannot be dynamically enabled, so no dual-writing / etc is // needed, so the baseline database store is sufficient. ReadVisibilityStoreName: nil, WriteVisibilityStoreName: nil, }) assert.NoError(t, err) assert.Nil(t, vm, "nil response is expected if advanced visibility cannot be enabled dynamically") }) t.Run("NewDomainReplicationQueueManager", func(t *testing.T) { fact := makeFactory(t) ds := mockDatastore(t, fact, storeTypeQueue) ds.EXPECT().NewQueue(persistence.DomainReplicationQueueType).Return(nil, nil).MinTimes(1) check(t, fact.NewDomainReplicationQueueManager) }) t.Run("NewConfigStoreManager", func(t *testing.T) { fact := makeFactory(t) ds := mockDatastore(t, fact, storeTypeConfigStore) ds.EXPECT().NewConfigStore().Return(nil, nil).MinTimes(1) check(t, fact.NewConfigStoreManager) }) t.Run("NewVisibilityManager_TripleVisibilityManager_Pinot", func(t *testing.T) { fact := makeFactory(t) ds := mockDatastore(t, fact, storeTypeVisibility) logger := testlogger.New(t) mc := messaging.NewMockClient(gomock.NewController(t)) mc.EXPECT().NewProducer(gomock.Any()).Return(kafka.NewKafkaProducer("test-topic", mocks.NewSyncProducer(t, nil), logger), nil).MinTimes(1) readFromClosed := true testAttributes := map[string]interface{}{ "CustomAttribute": "test", // Define your custom attributes and types } ds.EXPECT().NewVisibilityStore(readFromClosed).Return(nil, nil).MinTimes(1) _, err := fact.NewVisibilityManager(&Params{ PersistenceConfig: config.Persistence{ // a configured VisibilityStore uses the db store, which is mockable, // unlike basically every other store. AdvancedVisibilityStore: "pinot-visibility", VisibilityStore: "fake", DataStores: map[string]config.DataStore{ "pinot-visibility": { Pinot: &config.PinotVisibilityConfig{ // fields are unused but must be non-nil Cluster: "cluster", Broker: "broker", }, // fields are unused but must be non-nil }, }, }, MessagingClient: mc, PinotConfig: &config.PinotVisibilityConfig{ Migration: config.VisibilityMigration{ Enabled: true, }, }, ESConfig: &config.ElasticSearchConfig{ Indices: map[string]string{ "visibility": "test-index", }, }, }, &service.Config{ // must be non-nil to create a "manager", else nil return from NewVisibilityManager is expected ReadVisibilityStoreName: func(domain string) string { return "es" // any value is fine as there are no read calls }, // non-nil avoids a warning log EnableReadDBVisibilityFromClosedExecutionV2: func(opts ...dynamicproperties.FilterOption) bool { return readFromClosed // any value is fine as there are no read calls }, ValidSearchAttributes: func(opts ...dynamicproperties.FilterOption) map[string]interface{} { return testAttributes }, }) assert.NoError(t, err) }) t.Run("NewVisibilityManager_DualVisibilityManager_ES", func(t *testing.T) { fact := makeFactory(t) ds := mockDatastore(t, fact, storeTypeVisibility) logger := testlogger.New(t) mc := messaging.NewMockClient(gomock.NewController(t)) mc.EXPECT().NewProducer(gomock.Any()).Return(kafka.NewKafkaProducer("test-topic", mocks.NewSyncProducer(t, nil), logger), nil).MinTimes(1) readFromClosed := true testAttributes := map[string]interface{}{ "CustomAttribute": "test", // Define your custom attributes and types } ds.EXPECT().NewVisibilityStore(readFromClosed).Return(nil, nil).MinTimes(1) _, err := fact.NewVisibilityManager(&Params{ PersistenceConfig: config.Persistence{ // a configured VisibilityStore uses the db store, which is mockable, // unlike basically every other store. AdvancedVisibilityStore: "es-visibility", VisibilityStore: "fake", DataStores: map[string]config.DataStore{ "es-visibility": { ElasticSearch: &config.ElasticSearchConfig{ // fields are unused but must be non-nil Indices: map[string]string{ "visibility": "test-index", }, }, // fields are unused but must be non-nil }, }, }, MessagingClient: mc, ESConfig: &config.ElasticSearchConfig{ Indices: map[string]string{ "visibility": "test-index", }, }, }, &service.Config{ // must be non-nil to create a "manager", else nil return from NewVisibilityManager is expected ReadVisibilityStoreName: func(domain string) string { return "es" // any value is fine as there are no read calls }, // non-nil avoids a warning log EnableReadDBVisibilityFromClosedExecutionV2: func(opts ...dynamicproperties.FilterOption) bool { return readFromClosed // any value is fine as there are no read calls }, ValidSearchAttributes: func(opts ...dynamicproperties.FilterOption) map[string]interface{} { return testAttributes }, }) assert.NoError(t, err) }) } func makeFactory(t *testing.T) Factory { return makeFactoryWithMetrics(t, true) } func makeFactoryWithMetrics(t *testing.T, withMetrics bool) Factory { qpsFn := func() float64 { return 1000 } // anything non-zero exercises the ratelimit config paths logger := testlogger.New(t) var met metrics.Client if withMetrics { met = metrics.NewClient(tally.NewTestScope("", nil), service.GetMetricsServiceIdx(service.Frontend, logger), metrics.HistogramMigration{}) } ctrl := gomock.NewController(t) dc := dynamicconfig.NewCollection(dynamicconfig.NewMockClient(ctrl), logger) pdc := persistence.NewDynamicConfiguration(dc) cfg := &config.Persistence{ DefaultStore: "fake", VisibilityStore: "fake", AdvancedVisibilityStore: "fake", NumHistoryShards: 1024, DataStores: map[string]config.DataStore{ "fake": { NoSQL: &config.NoSQL{}, // fields are unused but must be non-nil ElasticSearch: &config.ElasticSearchConfig{}, // fields are unused but must be non-nil Pinot: &config.PinotVisibilityConfig{}, // fields are unused but must be non-nil }, }, TransactionSizeLimit: nil, ErrorInjectionRate: func(opts ...dynamicproperties.FilterOption) float64 { return 0.5 // half errors, unused in these tests beyond "nonzero" so it wraps with the error injector }, } return NewFactory(cfg, qpsFn, "test cluster", met, logger, pdc) } func mockDatastore(t *testing.T, fact Factory, store storeType) *MockDataStoreFactory { ctrl := gomock.NewController(t) impl := fact.(*factoryImpl) // I would prefer to mock everything so calls to the wrong store could have better errors, // but there doesn't seem to be a way to "name" these or say "any call fails with X message", // nor can you check what calls occurred after the fact. // // so just mock the only thing expected, so you get a nil panic stack trace when it fails // somewhere in a persistence database connection or something. it's sometimes easier to debug. mock := NewMockDataStoreFactory(ctrl) ds := impl.datastores[store] ds.factory = mock impl.datastores[store] = ds // write back the value type return mock } func TestVisibilityManagers(t *testing.T) { tests := []struct { name string advanced string datastores map[string]config.DataStore esConfig *config.ElasticSearchConfig osConfig *config.ElasticSearchConfig pinotConfig *config.PinotVisibilityConfig }{ { name: "TripleVisibilityManager_OpenSearch", advanced: "os-visibility", datastores: map[string]config.DataStore{ "os-visibility": { ElasticSearch: &config.ElasticSearchConfig{ // fields are unused but must be non-nil Indices: map[string]string{ "visibility": "test-index", }, }, // fields are unused but must be non-nil }, }, osConfig: &config.ElasticSearchConfig{ Indices: map[string]string{ "visibility": "test-index", }, Migration: config.VisibilityMigration{ Enabled: true, }, }, esConfig: &config.ElasticSearchConfig{ Indices: map[string]string{ "visibility": "test-index", }, }, }, { name: "NewVisibilityManager_TripleVisibilityManager_Pinot", advanced: "pinot-visibility", datastores: map[string]config.DataStore{ "pinot-visibility": { Pinot: &config.PinotVisibilityConfig{ Cluster: "cluster", Broker: "broker", }, }, }, pinotConfig: &config.PinotVisibilityConfig{ Migration: config.VisibilityMigration{ Enabled: true, }, }, esConfig: &config.ElasticSearchConfig{ Indices: map[string]string{ "visibility": "test-index", }, }, }, { name: "NewVisibilityManager_DualVisibilityManager_Pinot", advanced: "pinot-visibility", datastores: map[string]config.DataStore{ "pinot-visibility": { Pinot: &config.PinotVisibilityConfig{ Cluster: "cluster", Broker: "broker", }, }, }, pinotConfig: &config.PinotVisibilityConfig{ Migration: config.VisibilityMigration{ Enabled: false, }, }, }, { name: "NewVisibilityManager_DualVisibilityManager_ES", advanced: "es-visibility", datastores: map[string]config.DataStore{ "es-visibility": { ElasticSearch: &config.ElasticSearchConfig{ Indices: map[string]string{ "visibility": "test-index", }, }, }, }, esConfig: &config.ElasticSearchConfig{ Indices: map[string]string{ "visibility": "test-index", }, }, }, { name: "NewVisibilityManager_DualVisibilityManager_OS", advanced: "os-visibility", datastores: map[string]config.DataStore{ "os-visibility": { ElasticSearch: &config.ElasticSearchConfig{ Indices: map[string]string{ "visibility": "test-index", }, }, }, }, osConfig: &config.ElasticSearchConfig{ Indices: map[string]string{ "visibility": "test-index", }, Migration: config.VisibilityMigration{ Enabled: false, }, }, }, } for _, test := range tests { fact := makeFactory(t) ds := mockDatastore(t, fact, storeTypeVisibility) ds.EXPECT().NewVisibilityStore(true).Return(nil, nil).MinTimes(1) mc := messaging.NewMockClient(gomock.NewController(t)) mc.EXPECT().NewProducer(gomock.Any()).Return(kafka.NewKafkaProducer("test-topic", mocks.NewSyncProducer(t, nil), testlogger.New(t)), nil).MinTimes(1) _, err := fact.NewVisibilityManager(&Params{ PersistenceConfig: config.Persistence{ AdvancedVisibilityStore: test.advanced, VisibilityStore: "fake", DataStores: test.datastores, }, MessagingClient: mc, PinotConfig: test.pinotConfig, ESConfig: test.esConfig, OSConfig: test.osConfig, }, &service.Config{ // must be non-nil to create a "manager", else nil return from NewVisibilityManager is expected ReadVisibilityStoreName: func(domain string) string { return "es" // any value is fine as there are no read calls }, // non-nil avoids a warning log EnableReadDBVisibilityFromClosedExecutionV2: func(opts ...dynamicproperties.FilterOption) bool { return true // any value is fine as there are no read calls }, ValidSearchAttributes: func(opts ...dynamicproperties.FilterOption) map[string]interface{} { return map[string]interface{}{ "CustomAttribute": "test", } }, }) assert.NoError(t, err) } } ================================================ FILE: common/persistence/config.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) type ( // DynamicConfiguration represents dynamic configuration for persistence layer DynamicConfiguration struct { EnableSQLAsyncTransaction dynamicproperties.BoolPropertyFn EnableCassandraAllConsistencyLevelDelete dynamicproperties.BoolPropertyFn PersistenceSampleLoggingRate dynamicproperties.IntPropertyFn EnableShardIDMetrics dynamicproperties.BoolPropertyFn EnableHistoryTaskDualWriteMode dynamicproperties.BoolPropertyFn ReadNoSQLHistoryTaskFromDataBlob dynamicproperties.BoolPropertyFn ReadNoSQLShardFromDataBlob dynamicproperties.BoolPropertyFn SerializationEncoding dynamicproperties.StringPropertyFn DomainAuditLogTTL dynamicproperties.DurationPropertyFnWithDomainIDFilter HistoryNodeDeleteBatchSize dynamicproperties.IntPropertyFn RateLimiterBypassCallerTypes dynamicproperties.ListPropertyFn } ) // NewDynamicConfiguration returns new config with default values func NewDynamicConfiguration(dc *dynamicconfig.Collection) *DynamicConfiguration { return &DynamicConfiguration{ EnableSQLAsyncTransaction: dc.GetBoolProperty(dynamicproperties.EnableSQLAsyncTransaction), EnableCassandraAllConsistencyLevelDelete: dc.GetBoolProperty(dynamicproperties.EnableCassandraAllConsistencyLevelDelete), PersistenceSampleLoggingRate: dc.GetIntProperty(dynamicproperties.SampleLoggingRate), EnableShardIDMetrics: dc.GetBoolProperty(dynamicproperties.EnableShardIDMetrics), EnableHistoryTaskDualWriteMode: dc.GetBoolProperty(dynamicproperties.EnableNoSQLHistoryTaskDualWriteMode), ReadNoSQLHistoryTaskFromDataBlob: dc.GetBoolProperty(dynamicproperties.ReadNoSQLHistoryTaskFromDataBlob), ReadNoSQLShardFromDataBlob: dc.GetBoolProperty(dynamicproperties.ReadNoSQLShardFromDataBlob), SerializationEncoding: dc.GetStringProperty(dynamicproperties.SerializationEncoding), DomainAuditLogTTL: dc.GetDurationPropertyFilteredByDomainID(dynamicproperties.DomainAuditLogTTL), HistoryNodeDeleteBatchSize: dc.GetIntProperty(dynamicproperties.HistoryNodeDeleteBatchSize), RateLimiterBypassCallerTypes: dc.GetListProperty(dynamicproperties.RateLimiterBypassCallerTypes), } } ================================================ FILE: common/persistence/config_store_manager.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "context" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" ) type ( // configStoreManagerImpl implements ConfigStoreManager based on ConfigStore and PayloadSerializer configStoreManagerImpl struct { serializer PayloadSerializer persistence ConfigStore logger log.Logger timeSrc clock.TimeSource } ) var _ ConfigStoreManager = (*configStoreManagerImpl)(nil) // NewConfigStoreManagerImpl returns new ConfigStoreManager func NewConfigStoreManagerImpl(persistence ConfigStore, logger log.Logger) ConfigStoreManager { return &configStoreManagerImpl{ serializer: NewPayloadSerializer(), persistence: persistence, logger: logger, timeSrc: clock.NewRealTimeSource(), } } func (m *configStoreManagerImpl) Close() { m.persistence.Close() } func (m *configStoreManagerImpl) FetchDynamicConfig(ctx context.Context, cfgType ConfigType) (*FetchDynamicConfigResponse, error) { values, err := m.persistence.FetchConfig(ctx, cfgType) if err != nil || values == nil { return nil, err } config, err := m.serializer.DeserializeDynamicConfigBlob(values.Values) if err != nil { return nil, err } return &FetchDynamicConfigResponse{Snapshot: &DynamicConfigSnapshot{ Version: values.Version, Values: config, }}, nil } func (m *configStoreManagerImpl) UpdateDynamicConfig(ctx context.Context, request *UpdateDynamicConfigRequest, cfgType ConfigType) error { blob, err := m.serializer.SerializeDynamicConfigBlob(request.Snapshot.Values, constants.EncodingTypeThriftRW) if err != nil { return err } entry := &InternalConfigStoreEntry{ RowType: int(cfgType), Version: request.Snapshot.Version, Timestamp: m.timeSrc.Now(), Values: blob, } return m.persistence.UpdateConfig(ctx, entry) } ================================================ FILE: common/persistence/config_store_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) func setUpMocksForConfigStoreManager(t *testing.T) (*configStoreManagerImpl, *MockConfigStore, *MockPayloadSerializer) { ctrl := gomock.NewController(t) mockStore := NewMockConfigStore(ctrl) mockSerializer := NewMockPayloadSerializer(ctrl) logger := log.NewNoop() return &configStoreManagerImpl{ serializer: mockSerializer, persistence: mockStore, logger: logger, timeSrc: clock.NewRealTimeSource(), }, mockStore, mockSerializer } func TestNewConfigStoreManagerImpl(t *testing.T) { ctrl := gomock.NewController(t) mockStore := NewMockConfigStore(ctrl) logger := log.NewNoop() m := NewConfigStoreManagerImpl(mockStore, logger) assert.NotNil(t, m) cm, ok := m.(*configStoreManagerImpl) assert.True(t, ok) assert.Equal(t, mockStore, cm.persistence) assert.Equal(t, logger, cm.logger) assert.NotNil(t, cm.serializer) } func TestFetchDynamicConfig(t *testing.T) { encodingType := constants.EncodingTypeThriftRW testCases := []struct { name string setupMock func(mockStore *MockConfigStore, mockSerializer *MockPayloadSerializer) cfgType ConfigType expectError bool expectedError string expectedResponse *FetchDynamicConfigResponse }{ { name: "success", setupMock: func(mockStore *MockConfigStore, mockSerializer *MockPayloadSerializer) { // Mocking persistence DataBlob mockStore.EXPECT().FetchConfig(gomock.Any(), DynamicConfig).Return(&InternalConfigStoreEntry{ Version: 1, Timestamp: time.Now(), Values: &DataBlob{Encoding: encodingType, Data: []byte("serialized-values")}, }, nil).Times(1) // Mocking deserialization of persistence.DataBlob into types.DynamicConfigBlob mockSerializer.EXPECT(). DeserializeDynamicConfigBlob(&DataBlob{Encoding: encodingType, Data: []byte("serialized-values")}). Return(&types.DynamicConfigBlob{ SchemaVersion: 1, Entries: []*types.DynamicConfigEntry{ { Name: "TestEntry", Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("config-value"), }, Filters: []*types.DynamicConfigFilter{ { Name: "Filter1", Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("filter-value"), }, }, }, }, }, }, }, }, nil).Times(1) }, cfgType: DynamicConfig, // Updated to use DynamicConfig expectError: false, expectedResponse: &FetchDynamicConfigResponse{ Snapshot: &DynamicConfigSnapshot{ Version: 1, Values: &types.DynamicConfigBlob{ SchemaVersion: 1, Entries: []*types.DynamicConfigEntry{ { Name: "TestEntry", Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("config-value"), }, Filters: []*types.DynamicConfigFilter{ { Name: "Filter1", Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("filter-value"), }, }, }, }, }, }, }, }, }, }, }, { name: "fetch config error", setupMock: func(mockStore *MockConfigStore, mockSerializer *MockPayloadSerializer) { mockStore.EXPECT().FetchConfig(gomock.Any(), gomock.Any()).Return(nil, errors.New("fetch error")).Times(1) }, cfgType: DynamicConfig, // Updated to use DynamicConfig expectError: true, expectedError: "fetch error", }, { name: "deserialization error", setupMock: func(mockStore *MockConfigStore, mockSerializer *MockPayloadSerializer) { mockStore.EXPECT().FetchConfig(gomock.Any(), gomock.Any()).Return(&InternalConfigStoreEntry{ Version: 1, Timestamp: time.Now(), Values: &DataBlob{Encoding: encodingType, Data: []byte("serialized-values")}, }, nil).Times(1) mockSerializer.EXPECT(). DeserializeDynamicConfigBlob(&DataBlob{Encoding: encodingType, Data: []byte("serialized-values")}). Return(nil, errors.New("deserialization error")).Times(1) }, cfgType: DynamicConfig, // Updated to use DynamicConfig expectError: true, expectedError: "deserialization error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { configStoreManager, mockStore, mockSerializer := setUpMocksForConfigStoreManager(t) tc.setupMock(mockStore, mockSerializer) resp, err := configStoreManager.FetchDynamicConfig(context.Background(), tc.cfgType) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedResponse, resp) } }) } } func TestUpdateDynamicConfig(t *testing.T) { encodingType := constants.EncodingTypeThriftRW testCases := []struct { name string setupMock func(mockStore *MockConfigStore, mockSerializer *MockPayloadSerializer) cfgType ConfigType request *UpdateDynamicConfigRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockStore *MockConfigStore, mockSerializer *MockPayloadSerializer) { // Mock serialization of types.DynamicConfigBlob to persistence.DataBlob mockSerializer.EXPECT(). SerializeDynamicConfigBlob(&types.DynamicConfigBlob{ SchemaVersion: 1, Entries: []*types.DynamicConfigEntry{ { Name: "TestEntry", Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("config-value"), }, Filters: []*types.DynamicConfigFilter{ { Name: "Filter1", Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("filter-value"), }, }, }, }, }, }, }, }, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: encodingType, Data: []byte("serialized-values")}, nil).Times(1) mockStore.EXPECT().UpdateConfig(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, cfgType: DynamicConfig, // Updated to use DynamicConfig request: &UpdateDynamicConfigRequest{ Snapshot: &DynamicConfigSnapshot{ Version: 1, Values: &types.DynamicConfigBlob{ SchemaVersion: 1, Entries: []*types.DynamicConfigEntry{ { Name: "TestEntry", Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("config-value"), }, Filters: []*types.DynamicConfigFilter{ { Name: "Filter1", Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("filter-value"), }, }, }, }, }, }, }, }, }, }, expectError: false, }, { name: "serialization error", setupMock: func(mockStore *MockConfigStore, mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). SerializeDynamicConfigBlob(&types.DynamicConfigBlob{ SchemaVersion: 1, Entries: []*types.DynamicConfigEntry{ { Name: "TestEntry", Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("config-value"), }, Filters: []*types.DynamicConfigFilter{ { Name: "Filter1", Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("filter-value"), }, }, }, }, }, }, }, }, constants.EncodingTypeThriftRW). Return(nil, errors.New("serialization error")).Times(1) }, cfgType: DynamicConfig, // Updated to use DynamicConfig request: &UpdateDynamicConfigRequest{ Snapshot: &DynamicConfigSnapshot{ Version: 1, Values: &types.DynamicConfigBlob{ SchemaVersion: 1, Entries: []*types.DynamicConfigEntry{ { Name: "TestEntry", Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("config-value"), }, Filters: []*types.DynamicConfigFilter{ { Name: "Filter1", Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("filter-value"), }, }, }, }, }, }, }, }, }, }, expectError: true, expectedError: "serialization error", }, { name: "update config error", setupMock: func(mockStore *MockConfigStore, mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). SerializeDynamicConfigBlob(&types.DynamicConfigBlob{ SchemaVersion: 1, Entries: []*types.DynamicConfigEntry{ { Name: "TestEntry", Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("config-value"), }, Filters: []*types.DynamicConfigFilter{ { Name: "Filter1", Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("filter-value"), }, }, }, }, }, }, }, }, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: encodingType, Data: []byte("serialized-values")}, nil).Times(1) mockStore.EXPECT().UpdateConfig(gomock.Any(), gomock.Any()). Return(errors.New("update error")).Times(1) }, cfgType: DynamicConfig, // Updated to use DynamicConfig request: &UpdateDynamicConfigRequest{ Snapshot: &DynamicConfigSnapshot{ Version: 1, Values: &types.DynamicConfigBlob{ SchemaVersion: 1, Entries: []*types.DynamicConfigEntry{ { Name: "TestEntry", Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("config-value"), }, Filters: []*types.DynamicConfigFilter{ { Name: "Filter1", Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("filter-value"), }, }, }, }, }, }, }, }, }, }, expectError: true, expectedError: "update error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { configStoreManager, mockStore, mockSerializer := setUpMocksForConfigStoreManager(t) tc.setupMock(mockStore, mockSerializer) err := configStoreManager.UpdateDynamicConfig(context.Background(), tc.request, tc.cfgType) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestCloseConfigStoreManager(t *testing.T) { t.Run("close persistence", func(t *testing.T) { configStoreManager, mockStore, _ := setUpMocksForConfigStoreManager(t) // Expect Close method to be called once mockStore.EXPECT().Close().Times(1) // Call the Close method configStoreManager.Close() }) } ================================================ FILE: common/persistence/data_manager_interfaces.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Generate rate limiter wrappers. //go:generate mockgen -package $GOPACKAGE -destination data_manager_interfaces_mock.go github.com/uber/cadence/common/persistence Task,ShardManager,ExecutionManager,ExecutionManagerFactory,TaskManager,HistoryManager,DomainManager,DomainAuditManager,QueueManager,ConfigStoreManager //go:generate gowrap gen -g -p . -i ConfigStoreManager -t ./wrappers/templates/ratelimited.tmpl -o wrappers/ratelimited/configstore_generated.go //go:generate gowrap gen -g -p . -i DomainManager -t ./wrappers/templates/ratelimited.tmpl -o wrappers/ratelimited/domain_generated.go //go:generate gowrap gen -g -p . -i HistoryManager -t ./wrappers/templates/ratelimited.tmpl -o wrappers/ratelimited/history_generated.go //go:generate gowrap gen -g -p . -i ExecutionManager -t ./wrappers/templates/ratelimited.tmpl -o wrappers/ratelimited/execution_generated.go //go:generate gowrap gen -g -p . -i QueueManager -t ./wrappers/templates/ratelimited.tmpl -o wrappers/ratelimited/queue_generated.go //go:generate gowrap gen -g -p . -i TaskManager -t ./wrappers/templates/ratelimited.tmpl -o wrappers/ratelimited/task_generated.go //go:generate gowrap gen -g -p . -i ShardManager -t ./wrappers/templates/ratelimited.tmpl -o wrappers/ratelimited/shard_generated.go // Generate error injector wrappers. //go:generate gowrap gen -g -p . -i ConfigStoreManager -t ./wrappers/templates/errorinjector.tmpl -o wrappers/errorinjectors/configstore_generated.go //go:generate gowrap gen -g -p . -i ShardManager -t ./wrappers/templates/errorinjector.tmpl -o wrappers/errorinjectors/shard_generated.go //go:generate gowrap gen -g -p . -i ExecutionManager -t ./wrappers/templates/errorinjector.tmpl -o wrappers/errorinjectors/execution_generated.go //go:generate gowrap gen -g -p . -i TaskManager -t ./wrappers/templates/errorinjector.tmpl -o wrappers/errorinjectors/task_generated.go //go:generate gowrap gen -g -p . -i HistoryManager -t ./wrappers/templates/errorinjector.tmpl -o wrappers/errorinjectors/history_generated.go //go:generate gowrap gen -g -p . -i DomainManager -t ./wrappers/templates/errorinjector.tmpl -o wrappers/errorinjectors/domain_generated.go //go:generate gowrap gen -g -p . -i QueueManager -t ./wrappers/templates/errorinjector.tmpl -o wrappers/errorinjectors/queue_generated.go // Generate metered wrappers. //go:generate gowrap gen -g -p . -i ConfigStoreManager -t ./wrappers/templates/metered.tmpl -o wrappers/metered/configstore_generated.go //go:generate gowrap gen -g -p . -i ShardManager -t ./wrappers/templates/metered.tmpl -o wrappers/metered/shard_generated.go //go:generate gowrap gen -g -p . -i TaskManager -t ./wrappers/templates/metered.tmpl -o wrappers/metered/task_generated.go //go:generate gowrap gen -g -p . -i HistoryManager -t ./wrappers/templates/metered.tmpl -o wrappers/metered/history_generated.go //go:generate gowrap gen -g -p . -i DomainManager -t ./wrappers/templates/metered.tmpl -o wrappers/metered/domain_generated.go //go:generate gowrap gen -g -p . -i QueueManager -t ./wrappers/templates/metered.tmpl -o wrappers/metered/queue_generated.go // execution metered wrapper is special //go:generate gowrap gen -g -p . -i ExecutionManager -t ./wrappers/templates/metered_execution.tmpl -o wrappers/metered/execution_generated.go package persistence import ( "context" "errors" "fmt" "strings" "time" "github.com/pborman/uuid" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) const DBTimestampMinPrecision = time.Millisecond // Domain status const ( DomainStatusRegistered = iota DomainStatusDeprecated DomainStatusDeleted ) const ( // EventStoreVersion is already deprecated, this is used for forward // compatibility (so that rollback is possible). // TODO we can remove it after fixing all the query templates and when // we decide the compatibility is no longer needed. EventStoreVersion = 2 ) // CreateWorkflowMode workflow creation mode type CreateWorkflowMode int // QueueType is an enum that represents various queue types in persistence type QueueType int // DomainReplicationQueueType queue types used in queue table // Use positive numbers for queue type // Negative numbers are reserved for DLQ const ( DomainReplicationQueueType QueueType = iota + 1 ) // Create Workflow Execution Mode const ( // CreateWorkflowModeBrandNew Fail if current record exists // Only applicable for CreateWorkflowExecution CreateWorkflowModeBrandNew CreateWorkflowMode = iota // CreateWorkflowModeWorkflowIDReuse Update current record only if workflow is closed // Only applicable for CreateWorkflowExecution CreateWorkflowModeWorkflowIDReuse // CreateWorkflowModeContinueAsNew Update current record only if workflow is open // Only applicable for UpdateWorkflowExecution CreateWorkflowModeContinueAsNew // CreateWorkflowModeZombie Do not update current record since workflow to // applicable for CreateWorkflowExecution, UpdateWorkflowExecution CreateWorkflowModeZombie ) // UpdateWorkflowMode update mode type UpdateWorkflowMode int // Update Workflow Execution Mode const ( // UpdateWorkflowModeUpdateCurrent Update workflow, including current record // NOTE: update on current record is a condition update UpdateWorkflowModeUpdateCurrent UpdateWorkflowMode = iota // UpdateWorkflowModeBypassCurrent Update workflow, without current record // NOTE: current record CANNOT point to the workflow to be updated UpdateWorkflowModeBypassCurrent // UpdateWorkflowModeIgnoreCurrent Update workflow, ignoring current record // NOTE: current record may or may not point to the workflow // this mode should only be used for (re-)generating workflow tasks // and there's no other changes to the workflow UpdateWorkflowModeIgnoreCurrent ) // ConflictResolveWorkflowMode conflict resolve mode type ConflictResolveWorkflowMode int // Conflict Resolve Workflow Mode const ( // ConflictResolveWorkflowModeUpdateCurrent Conflict resolve workflow, including current record // NOTE: update on current record is a condition update ConflictResolveWorkflowModeUpdateCurrent ConflictResolveWorkflowMode = iota // ConflictResolveWorkflowModeBypassCurrent Conflict resolve workflow, without current record // NOTE: current record CANNOT point to the workflow to be updated ConflictResolveWorkflowModeBypassCurrent ) // Workflow execution states const ( WorkflowStateCreated = iota WorkflowStateRunning WorkflowStateCompleted WorkflowStateZombie WorkflowStateVoid WorkflowStateCorrupted ) // Workflow execution close status const ( WorkflowCloseStatusNone = iota WorkflowCloseStatusCompleted WorkflowCloseStatusFailed WorkflowCloseStatusCanceled WorkflowCloseStatusTerminated WorkflowCloseStatusContinuedAsNew WorkflowCloseStatusTimedOut ) // Types of task lists const ( TaskListTypeDecision = iota TaskListTypeActivity ) // Kinds of task lists const ( TaskListKindNormal = iota TaskListKindSticky TaskListKindEphemeral ) // HistoryTaskCategory represents various categories of history tasks type HistoryTaskCategory struct { categoryType int categoryID int categoryName string } func (c HistoryTaskCategory) Type() int { return c.categoryType } func (c HistoryTaskCategory) ID() int { return c.categoryID } func (c HistoryTaskCategory) Name() string { return c.categoryName } const ( HistoryTaskCategoryTypeImmediate = iota + 1 HistoryTaskCategoryTypeScheduled ) const ( HistoryTaskCategoryIDTransfer = 1 HistoryTaskCategoryIDTimer = 2 HistoryTaskCategoryIDReplication = 3 ) var ( HistoryTaskCategoryTransfer = HistoryTaskCategory{ categoryType: HistoryTaskCategoryTypeImmediate, categoryID: HistoryTaskCategoryIDTransfer, categoryName: "transfer", } HistoryTaskCategoryTimer = HistoryTaskCategory{ categoryType: HistoryTaskCategoryTypeScheduled, categoryID: HistoryTaskCategoryIDTimer, categoryName: "timer", } HistoryTaskCategoryReplication = HistoryTaskCategory{ categoryType: HistoryTaskCategoryTypeImmediate, categoryID: HistoryTaskCategoryIDReplication, categoryName: "replication", } ) // Transfer task types const ( TransferTaskTypeDecisionTask = iota TransferTaskTypeActivityTask TransferTaskTypeCloseExecution TransferTaskTypeCancelExecution TransferTaskTypeStartChildExecution TransferTaskTypeSignalExecution TransferTaskTypeRecordWorkflowStarted TransferTaskTypeResetWorkflow TransferTaskTypeUpsertWorkflowSearchAttributes TransferTaskTypeRecordWorkflowClosed TransferTaskTypeRecordChildExecutionCompleted TransferTaskTypeApplyParentClosePolicy // Deprecated: this is related to cross-cluster tasks ) // Types of replication tasks const ( ReplicationTaskTypeHistory = iota ReplicationTaskTypeSyncActivity ReplicationTaskTypeFailoverMarker ) // Types of timers const ( TaskTypeDecisionTimeout = iota TaskTypeActivityTimeout TaskTypeUserTimer TaskTypeWorkflowTimeout TaskTypeDeleteHistoryEvent TaskTypeActivityRetryTimer TaskTypeWorkflowBackoffTimer ) // WorkflowRequestType is the type of workflow request type WorkflowRequestType int // Types of workflow requests const ( WorkflowRequestTypeStart WorkflowRequestType = iota WorkflowRequestTypeSignal WorkflowRequestTypeCancel WorkflowRequestTypeReset ) const ( DomainAuditOperationTypeInvalid DomainAuditOperationType = iota DomainAuditOperationTypeCreate DomainAuditOperationTypeUpdate DomainAuditOperationTypeDeprecate DomainAuditOperationTypeDelete DomainAuditOperationTypeFailover ) func (d DomainAuditOperationType) String() string { switch d { case DomainAuditOperationTypeCreate: return "Create" case DomainAuditOperationTypeUpdate: return "Update" case DomainAuditOperationTypeFailover: return "Failover" case DomainAuditOperationTypeDeprecate: return "Deprecate" case DomainAuditOperationTypeDelete: return "Delete" default: return "Invalid" } } // CreateWorkflowRequestMode is the mode of create workflow request type CreateWorkflowRequestMode int // Modes of create workflow request const ( // CreateWorkflowRequestModeNew Fail if data with the same domain_id, workflow_id, request_id exists // It is used for transactions started by external API requests // to allow us detecting duplicate requests CreateWorkflowRequestModeNew CreateWorkflowRequestMode = iota // CreateWorkflowRequestModeReplicated Upsert the data without checking duplication // It is used for transactions started by replication stack to achieve // eventual consistency CreateWorkflowRequestModeReplicated ) // UnknownNumRowsAffected is returned when the number of rows that an API affected cannot be determined const UnknownNumRowsAffected = -1 // Types of workflow backoff timeout const ( WorkflowBackoffTimeoutTypeRetry = iota WorkflowBackoffTimeoutTypeCron ) const ( // InitialFailoverNotificationVersion is the initial failover version for a domain InitialFailoverNotificationVersion int64 = 0 // TransferTaskTransferTargetWorkflowID is the dummy workflow ID for transfer tasks of types // that do not have a target workflow TransferTaskTransferTargetWorkflowID = "20000000-0000-f000-f000-000000000001" // TransferTaskTransferTargetRunID is the dummy run ID for transfer tasks of types // that do not have a target workflow TransferTaskTransferTargetRunID = "30000000-0000-f000-f000-000000000002" // indicate invalid workflow state transition invalidStateTransitionMsg = "unable to change workflow state from %v to %v, close status %v" ) const numItemsInGarbageInfo = 3 type ConfigType int const ( DynamicConfig ConfigType = iota GlobalIsolationGroupConfig ) type ( // ShardInfo describes a shard ShardInfo struct { ShardID int `json:"shard_id"` Owner string `json:"owner"` RangeID int64 `json:"range_id"` StolenSinceRenew int `json:"stolen_since_renew"` UpdatedAt time.Time `json:"updated_at"` ReplicationAckLevel int64 `json:"replication_ack_level"` ReplicationDLQAckLevel map[string]int64 `json:"replication_dlq_ack_level"` TransferAckLevel int64 `json:"transfer_ack_level"` TimerAckLevel time.Time `json:"timer_ack_level"` ClusterTransferAckLevel map[string]int64 `json:"cluster_transfer_ack_level"` ClusterTimerAckLevel map[string]time.Time `json:"cluster_timer_ack_level"` TransferProcessingQueueStates *types.ProcessingQueueStates `json:"transfer_processing_queue_states"` TimerProcessingQueueStates *types.ProcessingQueueStates `json:"timer_processing_queue_states"` ClusterReplicationLevel map[string]int64 `json:"cluster_replication_level"` DomainNotificationVersion int64 `json:"domain_notification_version"` PendingFailoverMarkers []*types.FailoverMarkerAttributes `json:"pending_failover_markers"` QueueStates map[int32]*types.QueueState `json:"queue_states"` } // WorkflowExecutionInfo describes a workflow execution WorkflowExecutionInfo struct { DomainID string WorkflowID string RunID string FirstExecutionRunID string ParentDomainID string ParentWorkflowID string ParentRunID string InitiatedID int64 CompletionEventBatchID int64 CompletionEvent *types.HistoryEvent TaskList string TaskListKind types.TaskListKind WorkflowTypeName string WorkflowTimeout int32 DecisionStartToCloseTimeout int32 ExecutionContext []byte State int CloseStatus int LastFirstEventID int64 LastEventTaskID int64 NextEventID int64 LastProcessedEvent int64 StartTimestamp time.Time LastUpdatedTimestamp time.Time CreateRequestID string SignalCount int32 DecisionVersion int64 DecisionScheduleID int64 DecisionStartedID int64 DecisionRequestID string DecisionTimeout int32 DecisionAttempt int64 DecisionStartedTimestamp int64 DecisionScheduledTimestamp int64 DecisionOriginalScheduledTimestamp int64 CancelRequested bool CancelRequestID string StickyTaskList string StickyScheduleToStartTimeout int32 ClientLibraryVersion string ClientFeatureVersion string ClientImpl string AutoResetPoints *types.ResetPoints Memo map[string][]byte SearchAttributes map[string][]byte PartitionConfig map[string]string ExecutionStatus types.WorkflowExecutionStatus ScheduledExecutionTimestamp int64 // unit is unix nano, used to record the actual execution timestamp if it's a cron workflow // for retry Attempt int32 HasRetryPolicy bool InitialInterval int32 BackoffCoefficient float64 MaximumInterval int32 ExpirationTime time.Time MaximumAttempts int32 NonRetriableErrors []string BranchToken []byte // Cron IsCron bool CronOverlapPolicy types.CronOverlapPolicy ExpirationSeconds int32 // TODO: is this field useful? CronSchedule string ActiveClusterSelectionPolicy *types.ActiveClusterSelectionPolicy } // ExecutionStats is the statistics about workflow execution ExecutionStats struct { HistorySize int64 } // ReplicationState represents mutable state information for global domains. // This information is used by replication protocol when applying events from remote clusters // TODO: remove this struct after all 2DC workflows complete ReplicationState struct { CurrentVersion int64 StartVersion int64 LastWriteVersion int64 LastWriteEventID int64 LastReplicationInfo map[string]*ReplicationInfo } // CurrentWorkflowExecution describes a current execution record CurrentWorkflowExecution struct { DomainID string WorkflowID string RunID string State int CurrentRunID string } // FailoverLevel contains corresponding start / end level FailoverLevel struct { StartTime time.Time MinLevel HistoryTaskKey CurrentLevel HistoryTaskKey MaxLevel HistoryTaskKey DomainIDs map[string]struct{} } // TransferTaskInfo describes a transfer task TransferTaskInfo struct { DomainID string WorkflowID string RunID string VisibilityTimestamp time.Time TaskID int64 TargetDomainID string TargetDomainIDs map[string]struct{} // used for ApplyParentPolicy request TargetWorkflowID string TargetRunID string TargetChildWorkflowOnly bool TaskList string TaskType int ScheduleID int64 Version int64 RecordVisibility bool OriginalTaskList string OriginalTaskListKind types.TaskListKind } // CrossClusterTaskInfo describes a cross-cluster task // Cross cluster tasks are exactly like transfer tasks so // instead of creating another struct and duplicating the same // logic everywhere. We reuse TransferTaskInfo // This is a deprecated feature as of May 24 CrossClusterTaskInfo = TransferTaskInfo // ReplicationTaskInfo describes the replication task created for replication of history events ReplicationTaskInfo struct { DomainID string WorkflowID string RunID string TaskID int64 TaskType int FirstEventID int64 NextEventID int64 Version int64 ScheduledID int64 BranchToken []byte NewRunBranchToken []byte CreationTime int64 } // TimerTaskInfo describes a timer task. TimerTaskInfo struct { DomainID string WorkflowID string RunID string VisibilityTimestamp time.Time TaskID int64 TaskType int TimeoutType int EventID int64 ScheduleAttempt int64 Version int64 TaskList string } // TaskListInfo describes a state of a task list implementation. TaskListInfo struct { DomainID string Name string TaskType int RangeID int64 AckLevel int64 Kind int Expiry time.Time LastUpdated time.Time AdaptivePartitionConfig *TaskListPartitionConfig } TaskListPartition struct { IsolationGroups []string } // TaskListPartitionConfig represents the configuration for task list partitions. TaskListPartitionConfig struct { Version int64 ReadPartitions map[int]*TaskListPartition WritePartitions map[int]*TaskListPartition } // TaskInfo describes either activity or decision task TaskInfo struct { DomainID string WorkflowID string RunID string TaskID int64 ScheduleID int64 ScheduleToStartTimeoutSeconds int32 Expiry time.Time CreatedTime time.Time PartitionConfig map[string]string } // TaskKey gives primary key info for a specific task TaskKey struct { DomainID string TaskListName string TaskType int TaskID int64 } // ReplicationInfo represents the information stored for last replication event details per cluster ReplicationInfo struct { Version int64 LastEventID int64 } // VersionHistoryItem contains the event id and the associated version VersionHistoryItem struct { EventID int64 Version int64 } // VersionHistory provides operations on version history VersionHistory struct { BranchToken []byte Items []*VersionHistoryItem } // VersionHistories contains a set of VersionHistory VersionHistories struct { CurrentVersionHistoryIndex int Histories []*VersionHistory } // WorkflowMutableState indicates workflow related state WorkflowMutableState struct { ActivityInfos map[int64]*ActivityInfo TimerInfos map[string]*TimerInfo ChildExecutionInfos map[int64]*ChildExecutionInfo RequestCancelInfos map[int64]*RequestCancelInfo SignalInfos map[int64]*SignalInfo SignalRequestedIDs map[string]struct{} ExecutionInfo *WorkflowExecutionInfo ExecutionStats *ExecutionStats BufferedEvents []*types.HistoryEvent VersionHistories *VersionHistories ReplicationState *ReplicationState // TODO: remove this after all 2DC workflows complete Checksum checksum.Checksum } // ActivityInfo details. ActivityInfo struct { Version int64 ScheduleID int64 ScheduledEventBatchID int64 ScheduledEvent *types.HistoryEvent ScheduledTime time.Time StartedID int64 StartedEvent *types.HistoryEvent StartedTime time.Time DomainID string ActivityID string RequestID string Details []byte ScheduleToStartTimeout int32 ScheduleToCloseTimeout int32 StartToCloseTimeout int32 HeartbeatTimeout int32 CancelRequested bool CancelRequestID int64 LastHeartBeatUpdatedTime time.Time TimerTaskStatus int32 // For retry Attempt int32 StartedIdentity string TaskList string TaskListKind types.TaskListKind HasRetryPolicy bool InitialInterval int32 BackoffCoefficient float64 MaximumInterval int32 ExpirationTime time.Time MaximumAttempts int32 NonRetriableErrors []string LastFailureReason string LastWorkerIdentity string LastFailureDetails []byte // Not written to database - This is used only for deduping heartbeat timer creation LastHeartbeatTimeoutVisibilityInSeconds int64 } // TimerInfo details - metadata about user timer info. TimerInfo struct { Version int64 TimerID string StartedID int64 ExpiryTime time.Time TaskStatus int64 } // ChildExecutionInfo has details for pending child executions. ChildExecutionInfo struct { Version int64 InitiatedID int64 InitiatedEventBatchID int64 InitiatedEvent *types.HistoryEvent StartedID int64 StartedWorkflowID string StartedRunID string StartedEvent *types.HistoryEvent CreateRequestID string DomainID string DomainNameDEPRECATED string // deprecated: please use DomainID field instead WorkflowTypeName string ParentClosePolicy types.ParentClosePolicy } // RequestCancelInfo has details for pending external workflow cancellations RequestCancelInfo struct { Version int64 InitiatedEventBatchID int64 InitiatedID int64 CancelRequestID string } // SignalInfo has details for pending external workflow signal SignalInfo struct { Version int64 InitiatedEventBatchID int64 InitiatedID int64 SignalRequestID string SignalName string Input []byte Control []byte } // CreateShardRequest is used to create a shard in executions table CreateShardRequest struct { ShardInfo *ShardInfo } // GetShardRequest is used to get shard information GetShardRequest struct { ShardID int } // GetShardResponse is the response to GetShard GetShardResponse struct { ShardInfo *ShardInfo } // UpdateShardRequest is used to update shard information UpdateShardRequest struct { ShardInfo *ShardInfo PreviousRangeID int64 } // CreateWorkflowExecutionRequest is used to write a new workflow execution CreateWorkflowExecutionRequest struct { RangeID int64 Mode CreateWorkflowMode PreviousRunID string PreviousLastWriteVersion int64 NewWorkflowSnapshot WorkflowSnapshot WorkflowRequestMode CreateWorkflowRequestMode DomainName string } // CreateWorkflowExecutionResponse is the response to CreateWorkflowExecutionRequest CreateWorkflowExecutionResponse struct { MutableStateUpdateSessionStats *MutableStateUpdateSessionStats } // GetWorkflowExecutionRequest is used to retrieve the info of a workflow execution GetWorkflowExecutionRequest struct { DomainID string Execution types.WorkflowExecution DomainName string RangeID int64 } // GetWorkflowExecutionResponse is the response to GetWorkflowExecutionRequest GetWorkflowExecutionResponse struct { State *WorkflowMutableState MutableStateStats *MutableStateStats } // GetCurrentExecutionRequest is used to retrieve the current RunId for an execution GetCurrentExecutionRequest struct { DomainID string WorkflowID string DomainName string } // ListCurrentExecutionsRequest is request to ListCurrentExecutions ListCurrentExecutionsRequest struct { PageSize int PageToken []byte } // ListCurrentExecutionsResponse is the response to ListCurrentExecutionsRequest ListCurrentExecutionsResponse struct { Executions []*CurrentWorkflowExecution PageToken []byte } // IsWorkflowExecutionExistsRequest is used to check if the concrete execution exists IsWorkflowExecutionExistsRequest struct { DomainID string DomainName string WorkflowID string RunID string } // ListConcreteExecutionsRequest is request to ListConcreteExecutions ListConcreteExecutionsRequest struct { PageSize int PageToken []byte } // ListConcreteExecutionsResponse is response to ListConcreteExecutions ListConcreteExecutionsResponse struct { Executions []*ListConcreteExecutionsEntity PageToken []byte } // ListConcreteExecutionsEntity is a single entity in ListConcreteExecutionsResponse ListConcreteExecutionsEntity struct { ExecutionInfo *WorkflowExecutionInfo VersionHistories *VersionHistories } // GetCurrentExecutionResponse is the response to GetCurrentExecution GetCurrentExecutionResponse struct { StartRequestID string RunID string State int CloseStatus int LastWriteVersion int64 } // IsWorkflowExecutionExistsResponse is the response to IsWorkflowExecutionExists IsWorkflowExecutionExistsResponse struct { Exists bool } // UpdateWorkflowExecutionRequest is used to update a workflow execution UpdateWorkflowExecutionRequest struct { RangeID int64 Mode UpdateWorkflowMode UpdateWorkflowMutation WorkflowMutation NewWorkflowSnapshot *WorkflowSnapshot WorkflowRequestMode CreateWorkflowRequestMode Encoding constants.EncodingType // optional binary encoding type DomainName string } // ConflictResolveWorkflowExecutionRequest is used to reset workflow execution state for a single run ConflictResolveWorkflowExecutionRequest struct { RangeID int64 Mode ConflictResolveWorkflowMode // workflow to be reset ResetWorkflowSnapshot WorkflowSnapshot // maybe new workflow NewWorkflowSnapshot *WorkflowSnapshot // current workflow CurrentWorkflowMutation *WorkflowMutation WorkflowRequestMode CreateWorkflowRequestMode Encoding constants.EncodingType // optional binary encoding type DomainName string } // WorkflowEvents is used as generic workflow history events transaction container WorkflowEvents struct { DomainID string WorkflowID string RunID string BranchToken []byte Events []*types.HistoryEvent } // WorkflowRequest is used as requestID and it's corresponding failover version container WorkflowRequest struct { RequestID string Version int64 RequestType WorkflowRequestType } // WorkflowMutation is used as generic workflow execution state mutation WorkflowMutation struct { ExecutionInfo *WorkflowExecutionInfo ExecutionStats *ExecutionStats VersionHistories *VersionHistories UpsertActivityInfos []*ActivityInfo DeleteActivityInfos []int64 UpsertTimerInfos []*TimerInfo DeleteTimerInfos []string UpsertChildExecutionInfos []*ChildExecutionInfo DeleteChildExecutionInfos []int64 UpsertRequestCancelInfos []*RequestCancelInfo DeleteRequestCancelInfos []int64 UpsertSignalInfos []*SignalInfo DeleteSignalInfos []int64 UpsertSignalRequestedIDs []string DeleteSignalRequestedIDs []string NewBufferedEvents []*types.HistoryEvent ClearBufferedEvents bool TasksByCategory map[HistoryTaskCategory][]Task WorkflowRequests []*WorkflowRequest Condition int64 Checksum checksum.Checksum } // WorkflowSnapshot is used as generic workflow execution state snapshot WorkflowSnapshot struct { ExecutionInfo *WorkflowExecutionInfo ExecutionStats *ExecutionStats VersionHistories *VersionHistories ActivityInfos []*ActivityInfo TimerInfos []*TimerInfo ChildExecutionInfos []*ChildExecutionInfo RequestCancelInfos []*RequestCancelInfo SignalInfos []*SignalInfo SignalRequestedIDs []string TasksByCategory map[HistoryTaskCategory][]Task WorkflowRequests []*WorkflowRequest Condition int64 Checksum checksum.Checksum } // DeleteWorkflowExecutionRequest is used to delete a workflow execution DeleteWorkflowExecutionRequest struct { DomainID string WorkflowID string RunID string DomainName string } // DeleteCurrentWorkflowExecutionRequest is used to delete the current workflow execution DeleteCurrentWorkflowExecutionRequest struct { DomainID string WorkflowID string RunID string DomainName string } // PutReplicationTaskToDLQRequest is used to put a replication task to dlq PutReplicationTaskToDLQRequest struct { SourceClusterName string TaskInfo *ReplicationTaskInfo DomainName string } // GetReplicationTasksFromDLQRequest is used to get replication tasks from dlq GetReplicationTasksFromDLQRequest struct { SourceClusterName string ReadLevel int64 MaxReadLevel int64 BatchSize int NextPageToken []byte } // GetReplicationDLQSizeRequest is used to get one replication task from dlq GetReplicationDLQSizeRequest struct { SourceClusterName string } // DeleteReplicationTaskFromDLQRequest is used to delete replication task from DLQ DeleteReplicationTaskFromDLQRequest struct { SourceClusterName string TaskID int64 } // RangeDeleteReplicationTaskFromDLQRequest is used to delete replication tasks from DLQ RangeDeleteReplicationTaskFromDLQRequest struct { SourceClusterName string InclusiveBeginTaskID int64 ExclusiveEndTaskID int64 PageSize int } // RangeDeleteReplicationTaskFromDLQResponse is the response of RangeDeleteReplicationTaskFromDLQ RangeDeleteReplicationTaskFromDLQResponse struct { TasksCompleted int } // GetReplicationDLQSizeResponse is the response for GetReplicationDLQSize GetReplicationDLQSizeResponse struct { Size int64 } // GetHistoryTasksRequest is used to get history tasks GetHistoryTasksRequest struct { TaskCategory HistoryTaskCategory InclusiveMinTaskKey HistoryTaskKey ExclusiveMaxTaskKey HistoryTaskKey PageSize int NextPageToken []byte } // GetHistoryTasksResponse is the response for GetHistoryTasks GetHistoryTasksResponse struct { Tasks []Task NextPageToken []byte } // CompleteHistoryTaskRequest is used to complete a history task CompleteHistoryTaskRequest struct { TaskCategory HistoryTaskCategory TaskKey HistoryTaskKey } // RangeCompleteHistoryTaskRequest is used to complete a range of history tasks RangeCompleteHistoryTaskRequest struct { TaskCategory HistoryTaskCategory InclusiveMinTaskKey HistoryTaskKey ExclusiveMaxTaskKey HistoryTaskKey PageSize int } RangeCompleteHistoryTaskResponse struct { TasksCompleted int } // LeaseTaskListRequest is used to request lease of a task list LeaseTaskListRequest struct { DomainID string DomainName string TaskList string TaskType int TaskListKind int RangeID int64 CurrentTimeStamp time.Time } // LeaseTaskListResponse is response to LeaseTaskListRequest LeaseTaskListResponse struct { TaskListInfo *TaskListInfo } GetTaskListRequest struct { DomainID string DomainName string TaskList string TaskType int } GetTaskListResponse struct { TaskListInfo *TaskListInfo } // UpdateTaskListRequest is used to update task list implementation information UpdateTaskListRequest struct { TaskListInfo *TaskListInfo DomainName string CurrentTimeStamp time.Time } // UpdateTaskListResponse is the response to UpdateTaskList UpdateTaskListResponse struct { } // ListTaskListRequest contains the request params needed to invoke ListTaskList API ListTaskListRequest struct { PageSize int PageToken []byte } // ListTaskListResponse is the response from ListTaskList API ListTaskListResponse struct { Items []TaskListInfo NextPageToken []byte } // DeleteTaskListRequest contains the request params needed to invoke DeleteTaskList API DeleteTaskListRequest struct { DomainID string DomainName string TaskListName string TaskListType int RangeID int64 } GetTaskListSizeRequest struct { DomainID string DomainName string TaskListName string TaskListType int AckLevel int64 } GetTaskListSizeResponse struct { Size int64 } // CreateTasksRequest is used to create a new task for a workflow execution CreateTasksRequest struct { TaskListInfo *TaskListInfo Tasks []*CreateTaskInfo DomainName string CurrentTimeStamp time.Time } // CreateTaskInfo describes a task to be created in CreateTasksRequest CreateTaskInfo struct { Data *TaskInfo TaskID int64 } // CreateTasksResponse is the response to CreateTasksRequest CreateTasksResponse struct { } // GetTasksRequest is used to retrieve tasks of a task list GetTasksRequest struct { DomainID string TaskList string TaskType int ReadLevel int64 // range exclusive MaxReadLevel *int64 // optional: range inclusive when specified BatchSize int DomainName string } // GetTasksResponse is the response to GetTasksRequests GetTasksResponse struct { Tasks []*TaskInfo } // CompleteTaskRequest is used to complete a task CompleteTaskRequest struct { TaskList *TaskListInfo TaskID int64 DomainName string } // CompleteTasksLessThanRequest contains the request params needed to invoke CompleteTasksLessThan API CompleteTasksLessThanRequest struct { DomainID string TaskListName string TaskType int TaskID int64 // Tasks less than or equal to this ID will be completed Limit int // Limit on the max number of tasks that can be completed. Required param DomainName string } // CompleteTasksLessThanResponse is the response of CompleteTasksLessThan CompleteTasksLessThanResponse struct { TasksCompleted int } // GetOrphanTasksRequest contains the request params need to invoke the GetOrphanTasks API GetOrphanTasksRequest struct { Limit int } // GetOrphanTasksResponse is the response to GetOrphanTasksRequests GetOrphanTasksResponse struct { Tasks []*TaskKey } // DomainInfo describes the domain entity DomainInfo struct { ID string Name string Status int Description string OwnerEmail string Data map[string]string } // DomainConfig describes the domain configuration DomainConfig struct { // NOTE: this retention is in days, not in seconds Retention int32 EmitMetric bool HistoryArchivalStatus types.ArchivalStatus HistoryArchivalURI string VisibilityArchivalStatus types.ArchivalStatus VisibilityArchivalURI string BadBinaries types.BadBinaries IsolationGroups types.IsolationGroupConfiguration AsyncWorkflowConfig types.AsyncWorkflowConfiguration } // DomainReplicationConfig describes the cross DC domain replication configuration DomainReplicationConfig struct { Clusters []*ClusterReplicationConfig // ActiveClusterName is the name of the cluster that the domain is active in. // Required for all global domains (both active-passive and active-active). // For active-passive domains, this is the single active cluster. // For active-active domains this is the default cluster whenever a ClusterAttribute is not provided ActiveClusterName string // ActiveClusters is only applicable for active-active domains. // When this is set, the domain is considered active-active and workflows are routed // based on their ClusterAttributes. ActiveClusters *types.ActiveClusters } // ClusterReplicationConfig describes the cross DC cluster replication configuration ClusterReplicationConfig struct { ClusterName string // Note: if adding new properties of non-primitive types, remember to update GetCopy() } // CreateDomainRequest is used to create the domain CreateDomainRequest struct { Info *DomainInfo Config *DomainConfig ReplicationConfig *DomainReplicationConfig IsGlobalDomain bool ConfigVersion int64 FailoverVersion int64 LastUpdatedTime int64 CurrentTimeStamp time.Time } // CreateDomainResponse is the response for CreateDomain CreateDomainResponse struct { ID string } // GetDomainRequest is used to read domain GetDomainRequest struct { ID string Name string } // GetDomainResponse is the response for GetDomain GetDomainResponse struct { Info *DomainInfo Config *DomainConfig ReplicationConfig *DomainReplicationConfig IsGlobalDomain bool ConfigVersion int64 FailoverVersion int64 FailoverNotificationVersion int64 PreviousFailoverVersion int64 FailoverEndTime *int64 LastUpdatedTime int64 NotificationVersion int64 } // UpdateDomainRequest is used to update domain UpdateDomainRequest struct { Info *DomainInfo Config *DomainConfig ReplicationConfig *DomainReplicationConfig ConfigVersion int64 FailoverVersion int64 FailoverNotificationVersion int64 PreviousFailoverVersion int64 FailoverEndTime *int64 LastUpdatedTime int64 NotificationVersion int64 } // DeleteDomainRequest is used to delete domain entry from domains table DeleteDomainRequest struct { ID string } // DeleteDomainByNameRequest is used to delete domain entry from domains_by_name table DeleteDomainByNameRequest struct { Name string } // ListDomainsRequest is used to list domains ListDomainsRequest struct { PageSize int NextPageToken []byte } // ListDomainsResponse is the response for GetDomain ListDomainsResponse struct { Domains []*GetDomainResponse NextPageToken []byte } // GetMetadataResponse is the response for GetMetadata GetMetadataResponse struct { NotificationVersion int64 } // CreateDomainAuditLogRequest is used to create a domain audit log entry CreateDomainAuditLogRequest struct { DomainID string EventID string // must be a UUID v7 StateBefore *GetDomainResponse StateAfter *GetDomainResponse OperationType DomainAuditOperationType CreatedTime time.Time Identity string IdentityType string Comment string } // CreateDomainAuditLogResponse is the response for CreateDomainAuditLog CreateDomainAuditLogResponse struct { EventID string } // GetDomainAuditLogsRequest is used to get domain audit logs GetDomainAuditLogsRequest struct { DomainID string OperationType DomainAuditOperationType MinCreatedTime *time.Time MaxCreatedTime *time.Time PageSize int NextPageToken []byte } // GetDomainAuditLogsResponse is the response for GetDomainAuditLogs GetDomainAuditLogsResponse struct { AuditLogs []*DomainAuditLog NextPageToken []byte } // DomainAuditLog represents a single domain audit log entry DomainAuditLog struct { EventID string DomainID string StateBefore *GetDomainResponse StateAfter *GetDomainResponse OperationType DomainAuditOperationType CreatedTime time.Time LastUpdatedTime time.Time Identity string IdentityType string Comment string } // MutableStateStats is the size stats for MutableState MutableStateStats struct { // Total size of mutable state MutableStateSize int // Breakdown of size into more granular stats ExecutionInfoSize int ActivityInfoSize int TimerInfoSize int ChildInfoSize int SignalInfoSize int BufferedEventsSize int // Item count for various information captured within mutable state ActivityInfoCount int TimerInfoCount int ChildInfoCount int SignalInfoCount int RequestCancelInfoCount int BufferedEventsCount int } // MutableStateUpdateSessionStats is size stats for mutableState updating session MutableStateUpdateSessionStats struct { MutableStateSize int // Total size of mutable state update // Breakdown of mutable state size update for more granular stats ExecutionInfoSize int ActivityInfoSize int TimerInfoSize int ChildInfoSize int SignalInfoSize int BufferedEventsSize int // Item counts in this session update ActivityInfoCount int TimerInfoCount int ChildInfoCount int SignalInfoCount int RequestCancelInfoCount int // Deleted item counts in this session update DeleteActivityInfoCount int DeleteTimerInfoCount int DeleteChildInfoCount int DeleteSignalInfoCount int DeleteRequestCancelInfoCount int TaskCountByCategory map[HistoryTaskCategory]int } // UpdateWorkflowExecutionResponse is response for UpdateWorkflowExecutionRequest UpdateWorkflowExecutionResponse struct { MutableStateUpdateSessionStats *MutableStateUpdateSessionStats } // ConflictResolveWorkflowExecutionResponse is response for ConflictResolveWorkflowExecutionRequest ConflictResolveWorkflowExecutionResponse struct { MutableStateUpdateSessionStats *MutableStateUpdateSessionStats } // AppendHistoryNodesRequest is used to append a batch of history nodes AppendHistoryNodesRequest struct { // true if this is the first append request to the branch IsNewBranch bool // the info for clean up data in background Info string // The branch to be appended BranchToken []byte // The batch of events to be appended. The first eventID will become the nodeID of this batch Events []*types.HistoryEvent // requested TransactionID for this write operation. For the same eventID, the node with larger TransactionID always wins TransactionID int64 // optional binary encoding type Encoding constants.EncodingType // The shard to get history node data ShardID *int // DomainName to get metrics created with the domain DomainName string } // AppendHistoryNodesResponse is a response to AppendHistoryNodesRequest AppendHistoryNodesResponse struct { // The data blob that was persisted to database DataBlob DataBlob } // ReadHistoryBranchRequest is used to read a history branch ReadHistoryBranchRequest struct { // The branch to be read BranchToken []byte // Get the history nodes from MinEventID. Inclusive. MinEventID int64 // Get the history nodes upto MaxEventID. Exclusive. MaxEventID int64 // Maximum number of batches of events per page. Not that number of events in a batch >=1, it is not number of events per page. // However for a single page, it is also possible that the returned events is less than PageSize (event zero events) due to stale events. PageSize int // Token to continue reading next page of history append transactions. Pass in empty slice for first page NextPageToken []byte // The shard to get history branch data ShardID *int DomainName string } // ReadHistoryBranchResponse is the response to ReadHistoryBranchRequest ReadHistoryBranchResponse struct { // History events HistoryEvents []*types.HistoryEvent // Token to read next page if there are more events beyond page size. // Use this to set NextPageToken on ReadHistoryBranchRequest to read the next page. // Empty means we have reached the last page, not need to continue NextPageToken []byte // Size of history read from store Size int // the first_event_id of last loaded batch LastFirstEventID int64 } // ReadHistoryBranchByBatchResponse is the response to ReadHistoryBranchRequest ReadHistoryBranchByBatchResponse struct { // History events by batch History []*types.History // Token to read next page if there are more events beyond page size. // Use this to set NextPageToken on ReadHistoryBranchRequest to read the next page. // Empty means we have reached the last page, not need to continue NextPageToken []byte // Size of history read from store Size int // the first_event_id of last loaded batch LastFirstEventID int64 } // ReadRawHistoryBranchResponse is the response to ReadHistoryBranchRequest ReadRawHistoryBranchResponse struct { // HistoryEventBlobs history event blobs HistoryEventBlobs []*DataBlob // Token to read next page if there are more events beyond page size. // Use this to set NextPageToken on ReadHistoryBranchRequest to read the next page. // Empty means we have reached the last page, not need to continue NextPageToken []byte // Size of history read from store Size int } // ForkHistoryBranchRequest is used to fork a history branch ForkHistoryBranchRequest struct { // The base branch to fork from ForkBranchToken []byte // The nodeID to fork from, the new branch will start from ( inclusive ), the base branch will stop at(exclusive) // Application must provide a void forking nodeID, it must be a valid nodeID in that branch. A valid nodeID is the firstEventID of a valid batch of events. // And ForkNodeID > 1 because forking from 1 doesn't make any sense. ForkNodeID int64 // the info for clean up data in background Info string // The shard to get history branch data ShardID *int // DomainName to create metrics for Domain Cost Attribution DomainName string } // ForkHistoryBranchResponse is the response to ForkHistoryBranchRequest ForkHistoryBranchResponse struct { // branchToken to represent the new branch NewBranchToken []byte } // CompleteForkBranchRequest is used to complete forking CompleteForkBranchRequest struct { // the new branch returned from ForkHistoryBranchRequest BranchToken []byte // true means the fork is success, will update the flag, otherwise will delete the new branch Success bool // The shard to update history branch data ShardID *int } // DeleteHistoryBranchRequest is used to remove a history branch DeleteHistoryBranchRequest struct { // branch to be deleted BranchToken []byte // The shard to delete history branch data ShardID *int // DomainName to generate metrics for Domain Cost Attribution DomainName string } // GetHistoryTreeRequest is used to retrieve branch info of a history tree GetHistoryTreeRequest struct { // A UUID of a tree TreeID string // Get data from this shard ShardID *int // optional: can provide treeID via branchToken if treeID is empty BranchToken []byte // DomainName to create metrics DomainName string } // HistoryBranchDetail contains detailed information of a branch HistoryBranchDetail struct { TreeID string BranchID string ForkTime time.Time Info string } // GetHistoryTreeResponse is a response to GetHistoryTreeRequest GetHistoryTreeResponse struct { // all branches of a tree Branches []*workflow.HistoryBranch } // GetAllHistoryTreeBranchesRequest is a request of GetAllHistoryTreeBranches GetAllHistoryTreeBranchesRequest struct { // pagination token NextPageToken []byte // maximum number of branches returned per page PageSize int } // GetAllHistoryTreeBranchesResponse is a response to GetAllHistoryTreeBranches GetAllHistoryTreeBranchesResponse struct { // pagination token NextPageToken []byte // all branches of all trees Branches []HistoryBranchDetail } // CreateFailoverMarkersRequest is request to create failover markers CreateFailoverMarkersRequest struct { RangeID int64 Markers []*FailoverMarkerTask CurrentTimeStamp time.Time } // FetchDynamicConfigResponse is a response to FetchDynamicConfigResponse FetchDynamicConfigResponse struct { Snapshot *DynamicConfigSnapshot } // UpdateDynamicConfigRequest is a request to update dynamic config with snapshot UpdateDynamicConfigRequest struct { Snapshot *DynamicConfigSnapshot } DynamicConfigSnapshot struct { Version int64 Values *types.DynamicConfigBlob } // Closeable is an interface for any entity that supports a close operation to release resources Closeable interface { Close() } // ShardManager is used to manage all shards ShardManager interface { Closeable GetName() string CreateShard(ctx context.Context, request *CreateShardRequest) error GetShard(ctx context.Context, request *GetShardRequest) (*GetShardResponse, error) UpdateShard(ctx context.Context, request *UpdateShardRequest) error } // ExecutionManager is used to manage workflow executions ExecutionManager interface { Closeable GetName() string GetShardID() int CreateWorkflowExecution(ctx context.Context, request *CreateWorkflowExecutionRequest) (*CreateWorkflowExecutionResponse, error) GetWorkflowExecution(ctx context.Context, request *GetWorkflowExecutionRequest) (*GetWorkflowExecutionResponse, error) UpdateWorkflowExecution(ctx context.Context, request *UpdateWorkflowExecutionRequest) (*UpdateWorkflowExecutionResponse, error) ConflictResolveWorkflowExecution(ctx context.Context, request *ConflictResolveWorkflowExecutionRequest) (*ConflictResolveWorkflowExecutionResponse, error) DeleteWorkflowExecution(ctx context.Context, request *DeleteWorkflowExecutionRequest) error DeleteCurrentWorkflowExecution(ctx context.Context, request *DeleteCurrentWorkflowExecutionRequest) error GetCurrentExecution(ctx context.Context, request *GetCurrentExecutionRequest) (*GetCurrentExecutionResponse, error) IsWorkflowExecutionExists(ctx context.Context, request *IsWorkflowExecutionExistsRequest) (*IsWorkflowExecutionExistsResponse, error) // Replication task related methods PutReplicationTaskToDLQ(ctx context.Context, request *PutReplicationTaskToDLQRequest) error GetReplicationTasksFromDLQ(ctx context.Context, request *GetReplicationTasksFromDLQRequest) (*GetHistoryTasksResponse, error) GetReplicationDLQSize(ctx context.Context, request *GetReplicationDLQSizeRequest) (*GetReplicationDLQSizeResponse, error) DeleteReplicationTaskFromDLQ(ctx context.Context, request *DeleteReplicationTaskFromDLQRequest) error RangeDeleteReplicationTaskFromDLQ(ctx context.Context, request *RangeDeleteReplicationTaskFromDLQRequest) (*RangeDeleteReplicationTaskFromDLQResponse, error) CreateFailoverMarkerTasks(ctx context.Context, request *CreateFailoverMarkersRequest) error GetHistoryTasks(ctx context.Context, request *GetHistoryTasksRequest) (*GetHistoryTasksResponse, error) CompleteHistoryTask(ctx context.Context, request *CompleteHistoryTaskRequest) error RangeCompleteHistoryTask(ctx context.Context, request *RangeCompleteHistoryTaskRequest) (*RangeCompleteHistoryTaskResponse, error) // Scan operations ListConcreteExecutions(ctx context.Context, request *ListConcreteExecutionsRequest) (*ListConcreteExecutionsResponse, error) ListCurrentExecutions(ctx context.Context, request *ListCurrentExecutionsRequest) (*ListCurrentExecutionsResponse, error) GetActiveClusterSelectionPolicy(ctx context.Context, domainID, wfID, rID string) (*types.ActiveClusterSelectionPolicy, error) DeleteActiveClusterSelectionPolicy(ctx context.Context, domainID, workflowID, runID string) error } // ExecutionManagerFactory creates an instance of ExecutionManager for a given shard ExecutionManagerFactory interface { Closeable NewExecutionManager(shardID int) (ExecutionManager, error) } // TaskManager is used to manage tasks TaskManager interface { Closeable GetName() string LeaseTaskList(ctx context.Context, request *LeaseTaskListRequest) (*LeaseTaskListResponse, error) UpdateTaskList(ctx context.Context, request *UpdateTaskListRequest) (*UpdateTaskListResponse, error) GetTaskList(ctx context.Context, request *GetTaskListRequest) (*GetTaskListResponse, error) ListTaskList(ctx context.Context, request *ListTaskListRequest) (*ListTaskListResponse, error) DeleteTaskList(ctx context.Context, request *DeleteTaskListRequest) error GetTaskListSize(ctx context.Context, request *GetTaskListSizeRequest) (*GetTaskListSizeResponse, error) CreateTasks(ctx context.Context, request *CreateTasksRequest) (*CreateTasksResponse, error) GetTasks(ctx context.Context, request *GetTasksRequest) (*GetTasksResponse, error) CompleteTask(ctx context.Context, request *CompleteTaskRequest) error CompleteTasksLessThan(ctx context.Context, request *CompleteTasksLessThanRequest) (*CompleteTasksLessThanResponse, error) GetOrphanTasks(ctx context.Context, request *GetOrphanTasksRequest) (*GetOrphanTasksResponse, error) } // HistoryManager is used to manager workflow history events HistoryManager interface { Closeable GetName() string // The below are history V2 APIs // V2 regards history events growing as a tree, decoupled from workflow concepts // For Cadence, treeID is new runID, except for fork(reset), treeID will be the runID that it forks from. // AppendHistoryNodes add(or override) a batch of nodes to a history branch AppendHistoryNodes(ctx context.Context, request *AppendHistoryNodesRequest) (*AppendHistoryNodesResponse, error) // ReadHistoryBranch returns history node data for a branch ReadHistoryBranch(ctx context.Context, request *ReadHistoryBranchRequest) (*ReadHistoryBranchResponse, error) // ReadHistoryBranchByBatch returns history node data for a branch ByBatch ReadHistoryBranchByBatch(ctx context.Context, request *ReadHistoryBranchRequest) (*ReadHistoryBranchByBatchResponse, error) // ReadRawHistoryBranch returns history node raw data for a branch ByBatch // NOTE: this API should only be used by 3+DC ReadRawHistoryBranch(ctx context.Context, request *ReadHistoryBranchRequest) (*ReadRawHistoryBranchResponse, error) // ForkHistoryBranch forks a new branch from an old branch ForkHistoryBranch(ctx context.Context, request *ForkHistoryBranchRequest) (*ForkHistoryBranchResponse, error) // DeleteHistoryBranch removes a branch // If this is the last branch to delete, it will also remove the root node DeleteHistoryBranch(ctx context.Context, request *DeleteHistoryBranchRequest) error // GetHistoryTree returns all branch information of a tree GetHistoryTree(ctx context.Context, request *GetHistoryTreeRequest) (*GetHistoryTreeResponse, error) // GetAllHistoryTreeBranches returns all branches of all trees GetAllHistoryTreeBranches(ctx context.Context, request *GetAllHistoryTreeBranchesRequest) (*GetAllHistoryTreeBranchesResponse, error) } // DomainManager is used to manage metadata CRUD for domain entities DomainManager interface { Closeable GetName() string CreateDomain(ctx context.Context, request *CreateDomainRequest) (*CreateDomainResponse, error) GetDomain(ctx context.Context, request *GetDomainRequest) (*GetDomainResponse, error) UpdateDomain(ctx context.Context, request *UpdateDomainRequest) error DeleteDomain(ctx context.Context, request *DeleteDomainRequest) error DeleteDomainByName(ctx context.Context, request *DeleteDomainByNameRequest) error ListDomains(ctx context.Context, request *ListDomainsRequest) (*ListDomainsResponse, error) GetMetadata(ctx context.Context) (*GetMetadataResponse, error) } // DomainAuditManager is used to manage domain audit logs DomainAuditManager interface { Closeable GetName() string CreateDomainAuditLog(ctx context.Context, request *CreateDomainAuditLogRequest) (*CreateDomainAuditLogResponse, error) GetDomainAuditLogs(ctx context.Context, request *GetDomainAuditLogsRequest) (*GetDomainAuditLogsResponse, error) } EnqueueMessageRequest struct { MessagePayload []byte } ReadMessagesRequest struct { LastMessageID int64 MaxCount int } ReadMessagesResponse struct { Messages QueueMessageList } DeleteMessagesBeforeRequest struct { MessageID int64 } UpdateAckLevelRequest struct { MessageID int64 ClusterName string } GetAckLevelsRequest struct{} GetAckLevelsResponse struct { AckLevels map[string]int64 } EnqueueMessageToDLQRequest struct { MessagePayload []byte } ReadMessagesFromDLQRequest struct { FirstMessageID int64 LastMessageID int64 PageSize int PageToken []byte } ReadMessagesFromDLQResponse struct { Messages []*QueueMessage NextPageToken []byte } DeleteMessageFromDLQRequest struct { MessageID int64 } RangeDeleteMessagesFromDLQRequest struct { FirstMessageID int64 LastMessageID int64 } UpdateDLQAckLevelRequest struct { MessageID int64 ClusterName string } GetDLQAckLevelsRequest struct{} GetDLQAckLevelsResponse struct { AckLevels map[string]int64 } GetDLQSizeRequest struct{} GetDLQSizeResponse struct { Size int64 } // QueueManager is used to manage queue store QueueManager interface { Closeable EnqueueMessage(ctx context.Context, request *EnqueueMessageRequest) error ReadMessages(ctx context.Context, request *ReadMessagesRequest) (*ReadMessagesResponse, error) DeleteMessagesBefore(ctx context.Context, request *DeleteMessagesBeforeRequest) error UpdateAckLevel(ctx context.Context, request *UpdateAckLevelRequest) error GetAckLevels(ctx context.Context, request *GetAckLevelsRequest) (*GetAckLevelsResponse, error) EnqueueMessageToDLQ(ctx context.Context, request *EnqueueMessageToDLQRequest) error ReadMessagesFromDLQ(ctx context.Context, request *ReadMessagesFromDLQRequest) (*ReadMessagesFromDLQResponse, error) DeleteMessageFromDLQ(ctx context.Context, request *DeleteMessageFromDLQRequest) error RangeDeleteMessagesFromDLQ(ctx context.Context, request *RangeDeleteMessagesFromDLQRequest) error UpdateDLQAckLevel(ctx context.Context, request *UpdateDLQAckLevelRequest) error GetDLQAckLevels(ctx context.Context, request *GetDLQAckLevelsRequest) (*GetDLQAckLevelsResponse, error) GetDLQSize(ctx context.Context, request *GetDLQSizeRequest) (*GetDLQSizeResponse, error) } // QueueMessage is the message that stores in the queue QueueMessage struct { ID int64 `json:"message_id"` QueueType QueueType `json:"queue_type"` Payload []byte `json:"message_payload"` } QueueMessageList []*QueueMessage ConfigStoreManager interface { Closeable FetchDynamicConfig(ctx context.Context, cfgType ConfigType) (*FetchDynamicConfigResponse, error) UpdateDynamicConfig(ctx context.Context, request *UpdateDynamicConfigRequest, cfgType ConfigType) error // can add functions for config types other than dynamic config } ) // IsTimeoutError check whether error is TimeoutError func IsTimeoutError(err error) bool { if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { return true } var timeoutError *TimeoutError ok := errors.As(err, &timeoutError) return ok } // GetTaskID returns the task ID for transfer task func (t *TransferTaskInfo) GetTaskID() int64 { return t.TaskID } // GetVersion returns the task version for transfer task func (t *TransferTaskInfo) GetVersion() int64 { return t.Version } // GetTaskType returns the task type for transfer task func (t *TransferTaskInfo) GetTaskType() int { return t.TaskType } // GetVisibilityTimestamp returns the task type for transfer task func (t *TransferTaskInfo) GetVisibilityTimestamp() time.Time { return t.VisibilityTimestamp } // GetWorkflowID returns the workflow ID for transfer task func (t *TransferTaskInfo) GetWorkflowID() string { return t.WorkflowID } // GetRunID returns the run ID for transfer task func (t *TransferTaskInfo) GetRunID() string { return t.RunID } // GetTargetDomainIDs returns the targetDomainIDs for applyParentPolicy func (t *TransferTaskInfo) GetTargetDomainIDs() map[string]struct{} { return t.TargetDomainIDs } // GetDomainID returns the domain ID for transfer task func (t *TransferTaskInfo) GetDomainID() string { return t.DomainID } // GetTaskList returns the task list for transfer task func (t *TransferTaskInfo) GetTaskList() string { return t.TaskList } // GetOriginalTaskList returns the original task list for transfer task func (t *TransferTaskInfo) GetOriginalTaskList() string { return t.OriginalTaskList } // GetOriginalTaskListKind returns the original task list kind for transfer task func (t *TransferTaskInfo) GetOriginalTaskListKind() types.TaskListKind { return t.OriginalTaskListKind } // String returns a string representation for transfer task func (t *TransferTaskInfo) String() string { return fmt.Sprintf("%#v", t) } func (t *TransferTaskInfo) ToTask() (Task, error) { workflowIdentifier := WorkflowIdentifier{ DomainID: t.DomainID, WorkflowID: t.WorkflowID, RunID: t.RunID, } taskData := TaskData{ Version: t.Version, TaskID: t.TaskID, VisibilityTimestamp: t.VisibilityTimestamp, } switch t.TaskType { case TransferTaskTypeActivityTask: return &ActivityTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: t.TargetDomainID, TaskList: t.TaskList, ScheduleID: t.ScheduleID, }, nil case TransferTaskTypeDecisionTask: return &DecisionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: t.TargetDomainID, TaskList: t.TaskList, ScheduleID: t.ScheduleID, OriginalTaskList: t.OriginalTaskList, OriginalTaskListKind: t.OriginalTaskListKind, }, nil case TransferTaskTypeCloseExecution: return &CloseExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: t.TaskList, }, nil case TransferTaskTypeRecordWorkflowStarted: return &RecordWorkflowStartedTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: t.TaskList, }, nil case TransferTaskTypeResetWorkflow: return &ResetWorkflowTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: t.TaskList, }, nil case TransferTaskTypeRecordWorkflowClosed: return &RecordWorkflowClosedTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: t.TaskList, }, nil case TransferTaskTypeRecordChildExecutionCompleted: targetRunID := t.TargetRunID if t.TargetRunID == TransferTaskTransferTargetRunID { targetRunID = "" } return &RecordChildExecutionCompletedTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: t.TargetDomainID, TargetWorkflowID: t.TargetWorkflowID, TargetRunID: targetRunID, TaskList: t.TaskList, }, nil case TransferTaskTypeUpsertWorkflowSearchAttributes: return &UpsertWorkflowSearchAttributesTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: t.TaskList, }, nil case TransferTaskTypeStartChildExecution: return &StartChildExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: t.TargetDomainID, TargetWorkflowID: t.TargetWorkflowID, InitiatedID: t.ScheduleID, TaskList: t.TaskList, }, nil case TransferTaskTypeCancelExecution: targetRunID := t.TargetRunID if t.TargetRunID == TransferTaskTransferTargetRunID { targetRunID = "" } return &CancelExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: t.TargetDomainID, TargetWorkflowID: t.TargetWorkflowID, TargetRunID: targetRunID, InitiatedID: t.ScheduleID, TargetChildWorkflowOnly: t.TargetChildWorkflowOnly, TaskList: t.TaskList, }, nil case TransferTaskTypeSignalExecution: targetRunID := t.TargetRunID if t.TargetRunID == TransferTaskTransferTargetRunID { targetRunID = "" } return &SignalExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: t.TargetDomainID, TargetWorkflowID: t.TargetWorkflowID, TargetRunID: targetRunID, InitiatedID: t.ScheduleID, TargetChildWorkflowOnly: t.TargetChildWorkflowOnly, TaskList: t.TaskList, }, nil default: return nil, fmt.Errorf("unknown task type: %d", t.TaskType) } } func (r *GetDomainResponse) GetFailoverVersion() int64 { if r == nil { return types.UndefinedFailoverVersion } return r.FailoverVersion } // GetTaskID returns the task ID for replication task func (t *ReplicationTaskInfo) GetTaskID() int64 { return t.TaskID } // GetVersion returns the task version for replication task func (t *ReplicationTaskInfo) GetVersion() int64 { return t.Version } // GetTaskType returns the task type for replication task func (t *ReplicationTaskInfo) GetTaskType() int { return t.TaskType } // GetVisibilityTimestamp returns the task type for replication task func (t *ReplicationTaskInfo) GetVisibilityTimestamp() time.Time { return time.Time{} } // GetWorkflowID returns the workflow ID for replication task func (t *ReplicationTaskInfo) GetWorkflowID() string { return t.WorkflowID } // GetRunID returns the run ID for replication task func (t *ReplicationTaskInfo) GetRunID() string { return t.RunID } // GetDomainID returns the domain ID for replication task func (t *ReplicationTaskInfo) GetDomainID() string { return t.DomainID } // GetTaskID returns the task ID for timer task func (t *TimerTaskInfo) GetTaskID() int64 { return t.TaskID } // GetVersion returns the task version for timer task func (t *TimerTaskInfo) GetVersion() int64 { return t.Version } // GetTaskType returns the task type for timer task func (t *TimerTaskInfo) GetTaskType() int { return t.TaskType } // GetVisibilityTimestamp returns the task type for timer task func (t *TimerTaskInfo) GetVisibilityTimestamp() time.Time { return t.VisibilityTimestamp } // GetWorkflowID returns the workflow ID for timer task func (t *TimerTaskInfo) GetWorkflowID() string { return t.WorkflowID } // GetRunID returns the run ID for timer task func (t *TimerTaskInfo) GetRunID() string { return t.RunID } // GetDomainID returns the domain ID for timer task func (t *TimerTaskInfo) GetDomainID() string { return t.DomainID } // GetTaskList returns the task list for timer task func (t *TimerTaskInfo) GetTaskList() string { return t.TaskList } // String returns a string representation for timer task func (t *TimerTaskInfo) String() string { return fmt.Sprintf( "{DomainID: %v, WorkflowID: %v, RunID: %v, VisibilityTimestamp: %v, TaskID: %v, TaskType: %v, TimeoutType: %v, EventID: %v, ScheduleAttempt: %v, Version: %v.}", t.DomainID, t.WorkflowID, t.RunID, t.VisibilityTimestamp, t.TaskID, t.TaskType, t.TimeoutType, t.EventID, t.ScheduleAttempt, t.Version, ) } func (t *TimerTaskInfo) ToTask() (Task, error) { workflowIdentifier := WorkflowIdentifier{ DomainID: t.DomainID, WorkflowID: t.WorkflowID, RunID: t.RunID, } taskData := TaskData{ Version: t.Version, TaskID: t.TaskID, VisibilityTimestamp: t.VisibilityTimestamp, } switch t.TaskType { case TaskTypeDecisionTimeout: return &DecisionTimeoutTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, EventID: t.EventID, ScheduleAttempt: t.ScheduleAttempt, TimeoutType: t.TimeoutType, TaskList: t.TaskList, }, nil case TaskTypeActivityTimeout: return &ActivityTimeoutTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TimeoutType: t.TimeoutType, EventID: t.EventID, Attempt: t.ScheduleAttempt, TaskList: t.TaskList, }, nil case TaskTypeDeleteHistoryEvent: return &DeleteHistoryEventTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: t.TaskList, }, nil case TaskTypeWorkflowTimeout: return &WorkflowTimeoutTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: t.TaskList, }, nil case TaskTypeUserTimer: return &UserTimerTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, EventID: t.EventID, TaskList: t.TaskList, }, nil case TaskTypeActivityRetryTimer: return &ActivityRetryTimerTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, EventID: t.EventID, Attempt: t.ScheduleAttempt, TaskList: t.TaskList, }, nil case TaskTypeWorkflowBackoffTimer: return &WorkflowBackoffTimerTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TimeoutType: t.TimeoutType, TaskList: t.TaskList, }, nil default: return nil, fmt.Errorf("unknown task type: %d", t.TaskType) } } func (c *DomainReplicationConfig) GetActiveClusters() *types.ActiveClusters { if c != nil && c.ActiveClusters != nil { return c.ActiveClusters } return nil } func (c *DomainReplicationConfig) GetActiveClusterName() string { if c == nil { return "" } return c.ActiveClusterName } func (c *DomainReplicationConfig) GetClusterAttributeScopes() map[string]types.ClusterAttributeScope { if c == nil || c.ActiveClusters == nil { return nil } return c.ActiveClusters.AttributeScopes } // GetID returns the ID from DomainInfo func (d *DomainInfo) GetID() string { if d == nil { return "" } return d.ID } // ToNilSafeCopy // TODO: it seems that we just need a nil safe shardInfo, deep copy is not necessary func (s *ShardInfo) ToNilSafeCopy() *ShardInfo { if s == nil { s = &ShardInfo{} } shardInfo := s.copy() if shardInfo.ClusterTransferAckLevel == nil { shardInfo.ClusterTransferAckLevel = make(map[string]int64) } if shardInfo.ClusterTimerAckLevel == nil { shardInfo.ClusterTimerAckLevel = make(map[string]time.Time) } if shardInfo.ClusterReplicationLevel == nil { shardInfo.ClusterReplicationLevel = make(map[string]int64) } if shardInfo.ReplicationDLQAckLevel == nil { shardInfo.ReplicationDLQAckLevel = make(map[string]int64) } if shardInfo.TransferProcessingQueueStates == nil { shardInfo.TransferProcessingQueueStates = &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), } } if shardInfo.TimerProcessingQueueStates == nil { shardInfo.TimerProcessingQueueStates = &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), } } if shardInfo.QueueStates == nil { shardInfo.QueueStates = make(map[int32]*types.QueueState) } return shardInfo } // copy returns a deep copy of shardInfo func (s *ShardInfo) copy() *ShardInfo { // TODO: do we really need to deep copy those fields? var clusterTransferAckLevel map[string]int64 if s.ClusterTransferAckLevel != nil { clusterTransferAckLevel = make(map[string]int64) for k, v := range s.ClusterTransferAckLevel { clusterTransferAckLevel[k] = v } } var clusterTimerAckLevel map[string]time.Time if s.ClusterTimerAckLevel != nil { clusterTimerAckLevel = make(map[string]time.Time) for k, v := range s.ClusterTimerAckLevel { clusterTimerAckLevel[k] = v } } var clusterReplicationLevel map[string]int64 if s.ClusterReplicationLevel != nil { clusterReplicationLevel = make(map[string]int64) for k, v := range s.ClusterReplicationLevel { clusterReplicationLevel[k] = v } } var replicationDLQAckLevel map[string]int64 if s.ReplicationDLQAckLevel != nil { replicationDLQAckLevel = make(map[string]int64) for k, v := range s.ReplicationDLQAckLevel { replicationDLQAckLevel[k] = v } } var queueStates map[int32]*types.QueueState if s.QueueStates != nil { queueStates = make(map[int32]*types.QueueState) for k, v := range s.QueueStates { queueStates[k] = v.Copy() } } return &ShardInfo{ ShardID: s.ShardID, Owner: s.Owner, RangeID: s.RangeID, StolenSinceRenew: s.StolenSinceRenew, ReplicationAckLevel: s.ReplicationAckLevel, TransferAckLevel: s.TransferAckLevel, TimerAckLevel: s.TimerAckLevel, ClusterTransferAckLevel: clusterTransferAckLevel, ClusterTimerAckLevel: clusterTimerAckLevel, TransferProcessingQueueStates: s.TransferProcessingQueueStates, TimerProcessingQueueStates: s.TimerProcessingQueueStates, DomainNotificationVersion: s.DomainNotificationVersion, ClusterReplicationLevel: clusterReplicationLevel, ReplicationDLQAckLevel: replicationDLQAckLevel, PendingFailoverMarkers: s.PendingFailoverMarkers, UpdatedAt: s.UpdatedAt, QueueStates: queueStates, } } // SerializeClusterConfigs makes an array of *ClusterReplicationConfig serializable // by flattening them into map[string]interface{} func SerializeClusterConfigs(replicationConfigs []*ClusterReplicationConfig) []map[string]interface{} { var serializedReplicationConfigs []map[string]interface{} for index := range replicationConfigs { serializedReplicationConfigs = append(serializedReplicationConfigs, replicationConfigs[index].serialize()) } return serializedReplicationConfigs } // DeserializeClusterConfigs creates an array of ClusterReplicationConfigs from an array of map representations func DeserializeClusterConfigs(replicationConfigs []map[string]interface{}) []*ClusterReplicationConfig { deserializedReplicationConfigs := []*ClusterReplicationConfig{} for index := range replicationConfigs { deserializedReplicationConfig := &ClusterReplicationConfig{} deserializedReplicationConfig.deserialize(replicationConfigs[index]) deserializedReplicationConfigs = append(deserializedReplicationConfigs, deserializedReplicationConfig) } return deserializedReplicationConfigs } func (config *ClusterReplicationConfig) serialize() map[string]interface{} { output := make(map[string]interface{}) output["cluster_name"] = config.ClusterName return output } func (config *ClusterReplicationConfig) deserialize(input map[string]interface{}) { config.ClusterName = input["cluster_name"].(string) } // GetCopy return a copy of ClusterReplicationConfig func (config *ClusterReplicationConfig) GetCopy() *ClusterReplicationConfig { res := *config return &res } func (r *GetDomainResponse) GetReplicationConfig() *DomainReplicationConfig { if r == nil || r.ReplicationConfig == nil { return nil } return r.ReplicationConfig } // DeepCopy returns a deep copy of GetDomainResponse // todo (david.porter) delete this manual deepcopying since it's annoying to maintain and // use codegen for generating them instead func (r *GetDomainResponse) DeepCopy() *GetDomainResponse { if r == nil { return nil } result := &GetDomainResponse{ IsGlobalDomain: r.IsGlobalDomain, ConfigVersion: r.ConfigVersion, FailoverVersion: r.FailoverVersion, FailoverNotificationVersion: r.FailoverNotificationVersion, PreviousFailoverVersion: r.PreviousFailoverVersion, LastUpdatedTime: r.LastUpdatedTime, NotificationVersion: r.NotificationVersion, } // Deep copy FailoverEndTime if r.FailoverEndTime != nil { failoverEndTime := *r.FailoverEndTime result.FailoverEndTime = &failoverEndTime } // Deep copy DomainInfo if r.Info != nil { result.Info = &DomainInfo{ ID: r.Info.ID, Name: r.Info.Name, Status: r.Info.Status, Description: r.Info.Description, OwnerEmail: r.Info.OwnerEmail, } if r.Info.Data != nil { result.Info.Data = make(map[string]string, len(r.Info.Data)) for k, v := range r.Info.Data { result.Info.Data[k] = v } } } // Deep copy DomainConfig if r.Config != nil { result.Config = &DomainConfig{ Retention: r.Config.Retention, EmitMetric: r.Config.EmitMetric, HistoryArchivalStatus: r.Config.HistoryArchivalStatus, HistoryArchivalURI: r.Config.HistoryArchivalURI, VisibilityArchivalStatus: r.Config.VisibilityArchivalStatus, VisibilityArchivalURI: r.Config.VisibilityArchivalURI, } // Deep copy BadBinaries result.Config.BadBinaries = r.Config.BadBinaries.DeepCopy() // Deep copy IsolationGroups result.Config.IsolationGroups = r.Config.IsolationGroups.DeepCopy() // Deep copy AsyncWorkflowConfig result.Config.AsyncWorkflowConfig = r.Config.AsyncWorkflowConfig.DeepCopy() } // Deep copy DomainReplicationConfig if r.ReplicationConfig != nil { result.ReplicationConfig = &DomainReplicationConfig{ ActiveClusterName: r.ReplicationConfig.ActiveClusterName, } // Deep copy Clusters if r.ReplicationConfig.Clusters != nil { result.ReplicationConfig.Clusters = make([]*ClusterReplicationConfig, len(r.ReplicationConfig.Clusters)) for i, cluster := range r.ReplicationConfig.Clusters { if cluster != nil { result.ReplicationConfig.Clusters[i] = cluster.GetCopy() } } } // Deep copy ActiveClusters if r.ReplicationConfig.ActiveClusters != nil { result.ReplicationConfig.ActiveClusters = r.ReplicationConfig.ActiveClusters.DeepCopy() } } return result } // GetInfo returns the DomainInfo from GetDomainResponse func (r *GetDomainResponse) GetInfo() *DomainInfo { if r == nil { return nil } return r.Info } // DBTimestampToUnixNano converts Milliseconds timestamp to UnixNano func DBTimestampToUnixNano(milliseconds int64) int64 { return milliseconds * 1000 * 1000 // Milliseconds are 10⁻³, nanoseconds are 10⁻⁹, (-3) - (-9) = 6, so multiply by 10⁶ } // UnixNanoToDBTimestamp converts UnixNano to Milliseconds timestamp func UnixNanoToDBTimestamp(timestamp int64) int64 { return timestamp / (1000 * 1000) // Milliseconds are 10⁻³, nanoseconds are 10⁻⁹, (-9) - (-3) = -6, so divide by 10⁶ } var internalThriftEncoder = codec.NewThriftRWEncoder() // NewHistoryBranchToken return a new branch token func NewHistoryBranchToken(treeID string) ([]byte, error) { branchID := uuid.New() bi := &workflow.HistoryBranch{ TreeID: &treeID, BranchID: &branchID, Ancestors: []*workflow.HistoryBranchRange{}, } token, err := internalThriftEncoder.Encode(bi) if err != nil { return nil, err } return token, nil } // NewHistoryBranchTokenByBranchID return a new branch token with treeID/branchID func NewHistoryBranchTokenByBranchID(treeID, branchID string) ([]byte, error) { bi := &workflow.HistoryBranch{ TreeID: &treeID, BranchID: &branchID, Ancestors: []*workflow.HistoryBranchRange{}, } token, err := internalThriftEncoder.Encode(bi) if err != nil { return nil, err } return token, nil } // NewHistoryBranchTokenFromAnother make up a branchToken func NewHistoryBranchTokenFromAnother(branchID string, anotherToken []byte) ([]byte, error) { var branch workflow.HistoryBranch err := internalThriftEncoder.Decode(anotherToken, &branch) if err != nil { return nil, err } bi := &workflow.HistoryBranch{ TreeID: branch.TreeID, BranchID: &branchID, Ancestors: []*workflow.HistoryBranchRange{}, } token, err := internalThriftEncoder.Encode(bi) if err != nil { return nil, err } return token, nil } // BuildHistoryGarbageCleanupInfo combine the workflow identity information into a string func BuildHistoryGarbageCleanupInfo(domainID, workflowID, runID string) string { return fmt.Sprintf("%v:%v:%v", domainID, workflowID, runID) } // SplitHistoryGarbageCleanupInfo returns workflow identity information func SplitHistoryGarbageCleanupInfo(info string) (domainID, workflowID, runID string, err error) { ss := strings.Split(info, ":") // workflowID can contain ":" so len(ss) can be greater than 3 if len(ss) < numItemsInGarbageInfo { return "", "", "", fmt.Errorf("not able to split info for %s", info) } domainID = ss[0] runID = ss[len(ss)-1] workflowEnd := len(info) - len(runID) - 1 workflowID = info[len(domainID)+1 : workflowEnd] return } // NewGetReplicationTasksFromDLQRequest creates a new GetReplicationTasksFromDLQRequest func NewGetReplicationTasksFromDLQRequest( sourceClusterName string, readLevel int64, maxReadLevel int64, batchSize int, nextPageToken []byte, ) *GetReplicationTasksFromDLQRequest { return &GetReplicationTasksFromDLQRequest{ SourceClusterName: sourceClusterName, ReadLevel: readLevel, MaxReadLevel: maxReadLevel, BatchSize: batchSize, NextPageToken: nextPageToken, } } // IsTransientError checks if the error is a transient persistence error func IsTransientError(err error) bool { var internalServiceError *types.InternalServiceError var serviceBusyError *types.ServiceBusyError var timeoutError *TimeoutError switch { case errors.As(err, &internalServiceError), errors.As(err, &serviceBusyError), errors.As(err, &timeoutError): return true } return false } // IsBackgroundTransientError checks if the error is a transient error on background jobs func IsBackgroundTransientError(err error) bool { var internalServiceError *types.InternalServiceError var timeoutError *TimeoutError switch { case errors.As(err, &internalServiceError), errors.As(err, &timeoutError): return true } return false } // HasMoreRowsToDelete checks if there is more data need to be deleted func HasMoreRowsToDelete(rowsDeleted, batchSize int) bool { if rowsDeleted < batchSize || // all target tasks are deleted rowsDeleted == UnknownNumRowsAffected || // underlying database does not support rows affected, so pageSize is not honored and all target tasks are deleted rowsDeleted > batchSize || batchSize <= 0 { // if batchSize is <= 0 the attempt is for the request to be unbounded and remove all rows from min to max return false } return true } func TaskListKindHasTTL(taskListKind int) bool { switch taskListKind { case TaskListKindEphemeral: return true case TaskListKindSticky: return true case TaskListKindNormal: return false default: return false } } func (e *WorkflowExecutionInfo) CopyMemo() map[string][]byte { if e.Memo == nil { return nil } memo := make(map[string][]byte) for k, v := range e.Memo { val := make([]byte, len(v)) copy(val, v) memo[k] = val } return memo } func (e *WorkflowExecutionInfo) CopySearchAttributes() map[string][]byte { if e.SearchAttributes == nil { return nil } searchAttr := make(map[string][]byte) for k, v := range e.SearchAttributes { val := make([]byte, len(v)) copy(val, v) searchAttr[k] = val } return searchAttr } func (e *WorkflowExecutionInfo) CopyPartitionConfig() map[string]string { if e.PartitionConfig == nil { return nil } partitionConfig := make(map[string]string) for k, v := range e.PartitionConfig { partitionConfig[k] = v } return partitionConfig } func (p *TaskListPartitionConfig) ToInternalType() *types.TaskListPartitionConfig { if p == nil { return nil } var readPartitions map[int]*types.TaskListPartition if p.ReadPartitions != nil { readPartitions = make(map[int]*types.TaskListPartition, len(p.ReadPartitions)) for id, par := range p.ReadPartitions { readPartitions[id] = par.ToInternalType() } } var writePartitions map[int]*types.TaskListPartition if p.WritePartitions != nil { writePartitions = make(map[int]*types.TaskListPartition, len(p.WritePartitions)) for id, par := range p.WritePartitions { writePartitions[id] = par.ToInternalType() } } return &types.TaskListPartitionConfig{ Version: p.Version, ReadPartitions: readPartitions, WritePartitions: writePartitions, } } func (p *TaskListPartition) ToInternalType() *types.TaskListPartition { if p == nil { return nil } return &types.TaskListPartition{IsolationGroups: p.IsolationGroups} } // IsActiveActive returns true if the domain has active-active configuration. // Returns true if ClusterAttributes (or legacy RegionToClusters) have been configured for this domain. // TODO(active-active): Update unit tests of all components that use this function to cover active-active case func (c *DomainReplicationConfig) IsActiveActive() bool { if c == nil || c.ActiveClusters == nil { return false } // Check to see if a ClusterAttribute has been configured for this domain. if len(c.ActiveClusters.AttributeScopes) > 0 { for _, scope := range c.ActiveClusters.AttributeScopes { if len(scope.ClusterAttributes) > 0 { return true } } } return false } func IsWorkflowRunning(state int) bool { return state == WorkflowStateRunning || state == WorkflowStateCreated } ================================================ FILE: common/persistence/data_manager_interfaces_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/persistence (interfaces: Task,ShardManager,ExecutionManager,ExecutionManagerFactory,TaskManager,HistoryManager,DomainManager,DomainAuditManager,QueueManager,ConfigStoreManager) // // Generated by this command: // // mockgen -package persistence -destination data_manager_interfaces_mock.go github.com/uber/cadence/common/persistence Task,ShardManager,ExecutionManager,ExecutionManagerFactory,TaskManager,HistoryManager,DomainManager,DomainAuditManager,QueueManager,ConfigStoreManager // // Package persistence is a generated GoMock package. package persistence import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockTask is a mock of Task interface. type MockTask struct { ctrl *gomock.Controller recorder *MockTaskMockRecorder isgomock struct{} } // MockTaskMockRecorder is the mock recorder for MockTask. type MockTaskMockRecorder struct { mock *MockTask } // NewMockTask creates a new mock instance. func NewMockTask(ctrl *gomock.Controller) *MockTask { mock := &MockTask{ctrl: ctrl} mock.recorder = &MockTaskMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTask) EXPECT() *MockTaskMockRecorder { return m.recorder } // ByteSize mocks base method. func (m *MockTask) ByteSize() uint64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByteSize") ret0, _ := ret[0].(uint64) return ret0 } // ByteSize indicates an expected call of ByteSize. func (mr *MockTaskMockRecorder) ByteSize() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByteSize", reflect.TypeOf((*MockTask)(nil).ByteSize)) } // GetDomainID mocks base method. func (m *MockTask) GetDomainID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainID") ret0, _ := ret[0].(string) return ret0 } // GetDomainID indicates an expected call of GetDomainID. func (mr *MockTaskMockRecorder) GetDomainID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainID", reflect.TypeOf((*MockTask)(nil).GetDomainID)) } // GetOriginalTaskList mocks base method. func (m *MockTask) GetOriginalTaskList() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOriginalTaskList") ret0, _ := ret[0].(string) return ret0 } // GetOriginalTaskList indicates an expected call of GetOriginalTaskList. func (mr *MockTaskMockRecorder) GetOriginalTaskList() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOriginalTaskList", reflect.TypeOf((*MockTask)(nil).GetOriginalTaskList)) } // GetOriginalTaskListKind mocks base method. func (m *MockTask) GetOriginalTaskListKind() types.TaskListKind { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOriginalTaskListKind") ret0, _ := ret[0].(types.TaskListKind) return ret0 } // GetOriginalTaskListKind indicates an expected call of GetOriginalTaskListKind. func (mr *MockTaskMockRecorder) GetOriginalTaskListKind() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOriginalTaskListKind", reflect.TypeOf((*MockTask)(nil).GetOriginalTaskListKind)) } // GetRunID mocks base method. func (m *MockTask) GetRunID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRunID") ret0, _ := ret[0].(string) return ret0 } // GetRunID indicates an expected call of GetRunID. func (mr *MockTaskMockRecorder) GetRunID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRunID", reflect.TypeOf((*MockTask)(nil).GetRunID)) } // GetTaskCategory mocks base method. func (m *MockTask) GetTaskCategory() HistoryTaskCategory { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskCategory") ret0, _ := ret[0].(HistoryTaskCategory) return ret0 } // GetTaskCategory indicates an expected call of GetTaskCategory. func (mr *MockTaskMockRecorder) GetTaskCategory() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskCategory", reflect.TypeOf((*MockTask)(nil).GetTaskCategory)) } // GetTaskID mocks base method. func (m *MockTask) GetTaskID() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskID") ret0, _ := ret[0].(int64) return ret0 } // GetTaskID indicates an expected call of GetTaskID. func (mr *MockTaskMockRecorder) GetTaskID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskID", reflect.TypeOf((*MockTask)(nil).GetTaskID)) } // GetTaskKey mocks base method. func (m *MockTask) GetTaskKey() HistoryTaskKey { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskKey") ret0, _ := ret[0].(HistoryTaskKey) return ret0 } // GetTaskKey indicates an expected call of GetTaskKey. func (mr *MockTaskMockRecorder) GetTaskKey() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskKey", reflect.TypeOf((*MockTask)(nil).GetTaskKey)) } // GetTaskList mocks base method. func (m *MockTask) GetTaskList() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskList") ret0, _ := ret[0].(string) return ret0 } // GetTaskList indicates an expected call of GetTaskList. func (mr *MockTaskMockRecorder) GetTaskList() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskList", reflect.TypeOf((*MockTask)(nil).GetTaskList)) } // GetTaskType mocks base method. func (m *MockTask) GetTaskType() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskType") ret0, _ := ret[0].(int) return ret0 } // GetTaskType indicates an expected call of GetTaskType. func (mr *MockTaskMockRecorder) GetTaskType() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskType", reflect.TypeOf((*MockTask)(nil).GetTaskType)) } // GetVersion mocks base method. func (m *MockTask) GetVersion() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVersion") ret0, _ := ret[0].(int64) return ret0 } // GetVersion indicates an expected call of GetVersion. func (mr *MockTaskMockRecorder) GetVersion() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVersion", reflect.TypeOf((*MockTask)(nil).GetVersion)) } // GetVisibilityTimestamp mocks base method. func (m *MockTask) GetVisibilityTimestamp() time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVisibilityTimestamp") ret0, _ := ret[0].(time.Time) return ret0 } // GetVisibilityTimestamp indicates an expected call of GetVisibilityTimestamp. func (mr *MockTaskMockRecorder) GetVisibilityTimestamp() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVisibilityTimestamp", reflect.TypeOf((*MockTask)(nil).GetVisibilityTimestamp)) } // GetWorkflowID mocks base method. func (m *MockTask) GetWorkflowID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowID") ret0, _ := ret[0].(string) return ret0 } // GetWorkflowID indicates an expected call of GetWorkflowID. func (mr *MockTaskMockRecorder) GetWorkflowID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowID", reflect.TypeOf((*MockTask)(nil).GetWorkflowID)) } // SetTaskID mocks base method. func (m *MockTask) SetTaskID(id int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetTaskID", id) } // SetTaskID indicates an expected call of SetTaskID. func (mr *MockTaskMockRecorder) SetTaskID(id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTaskID", reflect.TypeOf((*MockTask)(nil).SetTaskID), id) } // SetVersion mocks base method. func (m *MockTask) SetVersion(version int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetVersion", version) } // SetVersion indicates an expected call of SetVersion. func (mr *MockTaskMockRecorder) SetVersion(version any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVersion", reflect.TypeOf((*MockTask)(nil).SetVersion), version) } // SetVisibilityTimestamp mocks base method. func (m *MockTask) SetVisibilityTimestamp(timestamp time.Time) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetVisibilityTimestamp", timestamp) } // SetVisibilityTimestamp indicates an expected call of SetVisibilityTimestamp. func (mr *MockTaskMockRecorder) SetVisibilityTimestamp(timestamp any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVisibilityTimestamp", reflect.TypeOf((*MockTask)(nil).SetVisibilityTimestamp), timestamp) } // ToInternalReplicationTaskInfo mocks base method. func (m *MockTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ToInternalReplicationTaskInfo") ret0, _ := ret[0].(*types.ReplicationTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ToInternalReplicationTaskInfo indicates an expected call of ToInternalReplicationTaskInfo. func (mr *MockTaskMockRecorder) ToInternalReplicationTaskInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToInternalReplicationTaskInfo", reflect.TypeOf((*MockTask)(nil).ToInternalReplicationTaskInfo)) } // ToTimerTaskInfo mocks base method. func (m *MockTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ToTimerTaskInfo") ret0, _ := ret[0].(*TimerTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ToTimerTaskInfo indicates an expected call of ToTimerTaskInfo. func (mr *MockTaskMockRecorder) ToTimerTaskInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToTimerTaskInfo", reflect.TypeOf((*MockTask)(nil).ToTimerTaskInfo)) } // ToTransferTaskInfo mocks base method. func (m *MockTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ToTransferTaskInfo") ret0, _ := ret[0].(*TransferTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ToTransferTaskInfo indicates an expected call of ToTransferTaskInfo. func (mr *MockTaskMockRecorder) ToTransferTaskInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToTransferTaskInfo", reflect.TypeOf((*MockTask)(nil).ToTransferTaskInfo)) } // MockShardManager is a mock of ShardManager interface. type MockShardManager struct { ctrl *gomock.Controller recorder *MockShardManagerMockRecorder isgomock struct{} } // MockShardManagerMockRecorder is the mock recorder for MockShardManager. type MockShardManagerMockRecorder struct { mock *MockShardManager } // NewMockShardManager creates a new mock instance. func NewMockShardManager(ctrl *gomock.Controller) *MockShardManager { mock := &MockShardManager{ctrl: ctrl} mock.recorder = &MockShardManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShardManager) EXPECT() *MockShardManagerMockRecorder { return m.recorder } // Close mocks base method. func (m *MockShardManager) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockShardManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockShardManager)(nil).Close)) } // CreateShard mocks base method. func (m *MockShardManager) CreateShard(ctx context.Context, request *CreateShardRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateShard", ctx, request) ret0, _ := ret[0].(error) return ret0 } // CreateShard indicates an expected call of CreateShard. func (mr *MockShardManagerMockRecorder) CreateShard(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateShard", reflect.TypeOf((*MockShardManager)(nil).CreateShard), ctx, request) } // GetName mocks base method. func (m *MockShardManager) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockShardManagerMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockShardManager)(nil).GetName)) } // GetShard mocks base method. func (m *MockShardManager) GetShard(ctx context.Context, request *GetShardRequest) (*GetShardResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShard", ctx, request) ret0, _ := ret[0].(*GetShardResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetShard indicates an expected call of GetShard. func (mr *MockShardManagerMockRecorder) GetShard(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShard", reflect.TypeOf((*MockShardManager)(nil).GetShard), ctx, request) } // UpdateShard mocks base method. func (m *MockShardManager) UpdateShard(ctx context.Context, request *UpdateShardRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateShard", ctx, request) ret0, _ := ret[0].(error) return ret0 } // UpdateShard indicates an expected call of UpdateShard. func (mr *MockShardManagerMockRecorder) UpdateShard(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateShard", reflect.TypeOf((*MockShardManager)(nil).UpdateShard), ctx, request) } // MockExecutionManager is a mock of ExecutionManager interface. type MockExecutionManager struct { ctrl *gomock.Controller recorder *MockExecutionManagerMockRecorder isgomock struct{} } // MockExecutionManagerMockRecorder is the mock recorder for MockExecutionManager. type MockExecutionManagerMockRecorder struct { mock *MockExecutionManager } // NewMockExecutionManager creates a new mock instance. func NewMockExecutionManager(ctrl *gomock.Controller) *MockExecutionManager { mock := &MockExecutionManager{ctrl: ctrl} mock.recorder = &MockExecutionManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockExecutionManager) EXPECT() *MockExecutionManagerMockRecorder { return m.recorder } // Close mocks base method. func (m *MockExecutionManager) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockExecutionManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockExecutionManager)(nil).Close)) } // CompleteHistoryTask mocks base method. func (m *MockExecutionManager) CompleteHistoryTask(ctx context.Context, request *CompleteHistoryTaskRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CompleteHistoryTask", ctx, request) ret0, _ := ret[0].(error) return ret0 } // CompleteHistoryTask indicates an expected call of CompleteHistoryTask. func (mr *MockExecutionManagerMockRecorder) CompleteHistoryTask(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompleteHistoryTask", reflect.TypeOf((*MockExecutionManager)(nil).CompleteHistoryTask), ctx, request) } // ConflictResolveWorkflowExecution mocks base method. func (m *MockExecutionManager) ConflictResolveWorkflowExecution(ctx context.Context, request *ConflictResolveWorkflowExecutionRequest) (*ConflictResolveWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConflictResolveWorkflowExecution", ctx, request) ret0, _ := ret[0].(*ConflictResolveWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ConflictResolveWorkflowExecution indicates an expected call of ConflictResolveWorkflowExecution. func (mr *MockExecutionManagerMockRecorder) ConflictResolveWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConflictResolveWorkflowExecution", reflect.TypeOf((*MockExecutionManager)(nil).ConflictResolveWorkflowExecution), ctx, request) } // CreateFailoverMarkerTasks mocks base method. func (m *MockExecutionManager) CreateFailoverMarkerTasks(ctx context.Context, request *CreateFailoverMarkersRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateFailoverMarkerTasks", ctx, request) ret0, _ := ret[0].(error) return ret0 } // CreateFailoverMarkerTasks indicates an expected call of CreateFailoverMarkerTasks. func (mr *MockExecutionManagerMockRecorder) CreateFailoverMarkerTasks(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFailoverMarkerTasks", reflect.TypeOf((*MockExecutionManager)(nil).CreateFailoverMarkerTasks), ctx, request) } // CreateWorkflowExecution mocks base method. func (m *MockExecutionManager) CreateWorkflowExecution(ctx context.Context, request *CreateWorkflowExecutionRequest) (*CreateWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateWorkflowExecution", ctx, request) ret0, _ := ret[0].(*CreateWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateWorkflowExecution indicates an expected call of CreateWorkflowExecution. func (mr *MockExecutionManagerMockRecorder) CreateWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateWorkflowExecution", reflect.TypeOf((*MockExecutionManager)(nil).CreateWorkflowExecution), ctx, request) } // DeleteActiveClusterSelectionPolicy mocks base method. func (m *MockExecutionManager) DeleteActiveClusterSelectionPolicy(ctx context.Context, domainID, workflowID, runID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteActiveClusterSelectionPolicy", ctx, domainID, workflowID, runID) ret0, _ := ret[0].(error) return ret0 } // DeleteActiveClusterSelectionPolicy indicates an expected call of DeleteActiveClusterSelectionPolicy. func (mr *MockExecutionManagerMockRecorder) DeleteActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteActiveClusterSelectionPolicy", reflect.TypeOf((*MockExecutionManager)(nil).DeleteActiveClusterSelectionPolicy), ctx, domainID, workflowID, runID) } // DeleteCurrentWorkflowExecution mocks base method. func (m *MockExecutionManager) DeleteCurrentWorkflowExecution(ctx context.Context, request *DeleteCurrentWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteCurrentWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteCurrentWorkflowExecution indicates an expected call of DeleteCurrentWorkflowExecution. func (mr *MockExecutionManagerMockRecorder) DeleteCurrentWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentWorkflowExecution", reflect.TypeOf((*MockExecutionManager)(nil).DeleteCurrentWorkflowExecution), ctx, request) } // DeleteReplicationTaskFromDLQ mocks base method. func (m *MockExecutionManager) DeleteReplicationTaskFromDLQ(ctx context.Context, request *DeleteReplicationTaskFromDLQRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteReplicationTaskFromDLQ", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteReplicationTaskFromDLQ indicates an expected call of DeleteReplicationTaskFromDLQ. func (mr *MockExecutionManagerMockRecorder) DeleteReplicationTaskFromDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationTaskFromDLQ", reflect.TypeOf((*MockExecutionManager)(nil).DeleteReplicationTaskFromDLQ), ctx, request) } // DeleteWorkflowExecution mocks base method. func (m *MockExecutionManager) DeleteWorkflowExecution(ctx context.Context, request *DeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteWorkflowExecution indicates an expected call of DeleteWorkflowExecution. func (mr *MockExecutionManagerMockRecorder) DeleteWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWorkflowExecution", reflect.TypeOf((*MockExecutionManager)(nil).DeleteWorkflowExecution), ctx, request) } // GetActiveClusterSelectionPolicy mocks base method. func (m *MockExecutionManager) GetActiveClusterSelectionPolicy(ctx context.Context, domainID, wfID, rID string) (*types.ActiveClusterSelectionPolicy, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveClusterSelectionPolicy", ctx, domainID, wfID, rID) ret0, _ := ret[0].(*types.ActiveClusterSelectionPolicy) ret1, _ := ret[1].(error) return ret0, ret1 } // GetActiveClusterSelectionPolicy indicates an expected call of GetActiveClusterSelectionPolicy. func (mr *MockExecutionManagerMockRecorder) GetActiveClusterSelectionPolicy(ctx, domainID, wfID, rID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterSelectionPolicy", reflect.TypeOf((*MockExecutionManager)(nil).GetActiveClusterSelectionPolicy), ctx, domainID, wfID, rID) } // GetCurrentExecution mocks base method. func (m *MockExecutionManager) GetCurrentExecution(ctx context.Context, request *GetCurrentExecutionRequest) (*GetCurrentExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCurrentExecution", ctx, request) ret0, _ := ret[0].(*GetCurrentExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentExecution indicates an expected call of GetCurrentExecution. func (mr *MockExecutionManagerMockRecorder) GetCurrentExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentExecution", reflect.TypeOf((*MockExecutionManager)(nil).GetCurrentExecution), ctx, request) } // GetHistoryTasks mocks base method. func (m *MockExecutionManager) GetHistoryTasks(ctx context.Context, request *GetHistoryTasksRequest) (*GetHistoryTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryTasks", ctx, request) ret0, _ := ret[0].(*GetHistoryTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHistoryTasks indicates an expected call of GetHistoryTasks. func (mr *MockExecutionManagerMockRecorder) GetHistoryTasks(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryTasks", reflect.TypeOf((*MockExecutionManager)(nil).GetHistoryTasks), ctx, request) } // GetName mocks base method. func (m *MockExecutionManager) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockExecutionManagerMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockExecutionManager)(nil).GetName)) } // GetReplicationDLQSize mocks base method. func (m *MockExecutionManager) GetReplicationDLQSize(ctx context.Context, request *GetReplicationDLQSizeRequest) (*GetReplicationDLQSizeResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReplicationDLQSize", ctx, request) ret0, _ := ret[0].(*GetReplicationDLQSizeResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetReplicationDLQSize indicates an expected call of GetReplicationDLQSize. func (mr *MockExecutionManagerMockRecorder) GetReplicationDLQSize(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationDLQSize", reflect.TypeOf((*MockExecutionManager)(nil).GetReplicationDLQSize), ctx, request) } // GetReplicationTasksFromDLQ mocks base method. func (m *MockExecutionManager) GetReplicationTasksFromDLQ(ctx context.Context, request *GetReplicationTasksFromDLQRequest) (*GetHistoryTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReplicationTasksFromDLQ", ctx, request) ret0, _ := ret[0].(*GetHistoryTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetReplicationTasksFromDLQ indicates an expected call of GetReplicationTasksFromDLQ. func (mr *MockExecutionManagerMockRecorder) GetReplicationTasksFromDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationTasksFromDLQ", reflect.TypeOf((*MockExecutionManager)(nil).GetReplicationTasksFromDLQ), ctx, request) } // GetShardID mocks base method. func (m *MockExecutionManager) GetShardID() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardID") ret0, _ := ret[0].(int) return ret0 } // GetShardID indicates an expected call of GetShardID. func (mr *MockExecutionManagerMockRecorder) GetShardID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardID", reflect.TypeOf((*MockExecutionManager)(nil).GetShardID)) } // GetWorkflowExecution mocks base method. func (m *MockExecutionManager) GetWorkflowExecution(ctx context.Context, request *GetWorkflowExecutionRequest) (*GetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowExecution", ctx, request) ret0, _ := ret[0].(*GetWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetWorkflowExecution indicates an expected call of GetWorkflowExecution. func (mr *MockExecutionManagerMockRecorder) GetWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowExecution", reflect.TypeOf((*MockExecutionManager)(nil).GetWorkflowExecution), ctx, request) } // IsWorkflowExecutionExists mocks base method. func (m *MockExecutionManager) IsWorkflowExecutionExists(ctx context.Context, request *IsWorkflowExecutionExistsRequest) (*IsWorkflowExecutionExistsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsWorkflowExecutionExists", ctx, request) ret0, _ := ret[0].(*IsWorkflowExecutionExistsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // IsWorkflowExecutionExists indicates an expected call of IsWorkflowExecutionExists. func (mr *MockExecutionManagerMockRecorder) IsWorkflowExecutionExists(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWorkflowExecutionExists", reflect.TypeOf((*MockExecutionManager)(nil).IsWorkflowExecutionExists), ctx, request) } // ListConcreteExecutions mocks base method. func (m *MockExecutionManager) ListConcreteExecutions(ctx context.Context, request *ListConcreteExecutionsRequest) (*ListConcreteExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListConcreteExecutions", ctx, request) ret0, _ := ret[0].(*ListConcreteExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListConcreteExecutions indicates an expected call of ListConcreteExecutions. func (mr *MockExecutionManagerMockRecorder) ListConcreteExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListConcreteExecutions", reflect.TypeOf((*MockExecutionManager)(nil).ListConcreteExecutions), ctx, request) } // ListCurrentExecutions mocks base method. func (m *MockExecutionManager) ListCurrentExecutions(ctx context.Context, request *ListCurrentExecutionsRequest) (*ListCurrentExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListCurrentExecutions", ctx, request) ret0, _ := ret[0].(*ListCurrentExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListCurrentExecutions indicates an expected call of ListCurrentExecutions. func (mr *MockExecutionManagerMockRecorder) ListCurrentExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCurrentExecutions", reflect.TypeOf((*MockExecutionManager)(nil).ListCurrentExecutions), ctx, request) } // PutReplicationTaskToDLQ mocks base method. func (m *MockExecutionManager) PutReplicationTaskToDLQ(ctx context.Context, request *PutReplicationTaskToDLQRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PutReplicationTaskToDLQ", ctx, request) ret0, _ := ret[0].(error) return ret0 } // PutReplicationTaskToDLQ indicates an expected call of PutReplicationTaskToDLQ. func (mr *MockExecutionManagerMockRecorder) PutReplicationTaskToDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutReplicationTaskToDLQ", reflect.TypeOf((*MockExecutionManager)(nil).PutReplicationTaskToDLQ), ctx, request) } // RangeCompleteHistoryTask mocks base method. func (m *MockExecutionManager) RangeCompleteHistoryTask(ctx context.Context, request *RangeCompleteHistoryTaskRequest) (*RangeCompleteHistoryTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeCompleteHistoryTask", ctx, request) ret0, _ := ret[0].(*RangeCompleteHistoryTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeCompleteHistoryTask indicates an expected call of RangeCompleteHistoryTask. func (mr *MockExecutionManagerMockRecorder) RangeCompleteHistoryTask(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeCompleteHistoryTask", reflect.TypeOf((*MockExecutionManager)(nil).RangeCompleteHistoryTask), ctx, request) } // RangeDeleteReplicationTaskFromDLQ mocks base method. func (m *MockExecutionManager) RangeDeleteReplicationTaskFromDLQ(ctx context.Context, request *RangeDeleteReplicationTaskFromDLQRequest) (*RangeDeleteReplicationTaskFromDLQResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteReplicationTaskFromDLQ", ctx, request) ret0, _ := ret[0].(*RangeDeleteReplicationTaskFromDLQResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteReplicationTaskFromDLQ indicates an expected call of RangeDeleteReplicationTaskFromDLQ. func (mr *MockExecutionManagerMockRecorder) RangeDeleteReplicationTaskFromDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteReplicationTaskFromDLQ", reflect.TypeOf((*MockExecutionManager)(nil).RangeDeleteReplicationTaskFromDLQ), ctx, request) } // UpdateWorkflowExecution mocks base method. func (m *MockExecutionManager) UpdateWorkflowExecution(ctx context.Context, request *UpdateWorkflowExecutionRequest) (*UpdateWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecution", ctx, request) ret0, _ := ret[0].(*UpdateWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateWorkflowExecution indicates an expected call of UpdateWorkflowExecution. func (mr *MockExecutionManagerMockRecorder) UpdateWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecution", reflect.TypeOf((*MockExecutionManager)(nil).UpdateWorkflowExecution), ctx, request) } // MockExecutionManagerFactory is a mock of ExecutionManagerFactory interface. type MockExecutionManagerFactory struct { ctrl *gomock.Controller recorder *MockExecutionManagerFactoryMockRecorder isgomock struct{} } // MockExecutionManagerFactoryMockRecorder is the mock recorder for MockExecutionManagerFactory. type MockExecutionManagerFactoryMockRecorder struct { mock *MockExecutionManagerFactory } // NewMockExecutionManagerFactory creates a new mock instance. func NewMockExecutionManagerFactory(ctrl *gomock.Controller) *MockExecutionManagerFactory { mock := &MockExecutionManagerFactory{ctrl: ctrl} mock.recorder = &MockExecutionManagerFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockExecutionManagerFactory) EXPECT() *MockExecutionManagerFactoryMockRecorder { return m.recorder } // Close mocks base method. func (m *MockExecutionManagerFactory) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockExecutionManagerFactoryMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockExecutionManagerFactory)(nil).Close)) } // NewExecutionManager mocks base method. func (m *MockExecutionManagerFactory) NewExecutionManager(shardID int) (ExecutionManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewExecutionManager", shardID) ret0, _ := ret[0].(ExecutionManager) ret1, _ := ret[1].(error) return ret0, ret1 } // NewExecutionManager indicates an expected call of NewExecutionManager. func (mr *MockExecutionManagerFactoryMockRecorder) NewExecutionManager(shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewExecutionManager", reflect.TypeOf((*MockExecutionManagerFactory)(nil).NewExecutionManager), shardID) } // MockTaskManager is a mock of TaskManager interface. type MockTaskManager struct { ctrl *gomock.Controller recorder *MockTaskManagerMockRecorder isgomock struct{} } // MockTaskManagerMockRecorder is the mock recorder for MockTaskManager. type MockTaskManagerMockRecorder struct { mock *MockTaskManager } // NewMockTaskManager creates a new mock instance. func NewMockTaskManager(ctrl *gomock.Controller) *MockTaskManager { mock := &MockTaskManager{ctrl: ctrl} mock.recorder = &MockTaskManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskManager) EXPECT() *MockTaskManagerMockRecorder { return m.recorder } // Close mocks base method. func (m *MockTaskManager) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockTaskManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockTaskManager)(nil).Close)) } // CompleteTask mocks base method. func (m *MockTaskManager) CompleteTask(ctx context.Context, request *CompleteTaskRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CompleteTask", ctx, request) ret0, _ := ret[0].(error) return ret0 } // CompleteTask indicates an expected call of CompleteTask. func (mr *MockTaskManagerMockRecorder) CompleteTask(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompleteTask", reflect.TypeOf((*MockTaskManager)(nil).CompleteTask), ctx, request) } // CompleteTasksLessThan mocks base method. func (m *MockTaskManager) CompleteTasksLessThan(ctx context.Context, request *CompleteTasksLessThanRequest) (*CompleteTasksLessThanResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CompleteTasksLessThan", ctx, request) ret0, _ := ret[0].(*CompleteTasksLessThanResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CompleteTasksLessThan indicates an expected call of CompleteTasksLessThan. func (mr *MockTaskManagerMockRecorder) CompleteTasksLessThan(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompleteTasksLessThan", reflect.TypeOf((*MockTaskManager)(nil).CompleteTasksLessThan), ctx, request) } // CreateTasks mocks base method. func (m *MockTaskManager) CreateTasks(ctx context.Context, request *CreateTasksRequest) (*CreateTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateTasks", ctx, request) ret0, _ := ret[0].(*CreateTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateTasks indicates an expected call of CreateTasks. func (mr *MockTaskManagerMockRecorder) CreateTasks(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTasks", reflect.TypeOf((*MockTaskManager)(nil).CreateTasks), ctx, request) } // DeleteTaskList mocks base method. func (m *MockTaskManager) DeleteTaskList(ctx context.Context, request *DeleteTaskListRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTaskList", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteTaskList indicates an expected call of DeleteTaskList. func (mr *MockTaskManagerMockRecorder) DeleteTaskList(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTaskList", reflect.TypeOf((*MockTaskManager)(nil).DeleteTaskList), ctx, request) } // GetName mocks base method. func (m *MockTaskManager) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockTaskManagerMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockTaskManager)(nil).GetName)) } // GetOrphanTasks mocks base method. func (m *MockTaskManager) GetOrphanTasks(ctx context.Context, request *GetOrphanTasksRequest) (*GetOrphanTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrphanTasks", ctx, request) ret0, _ := ret[0].(*GetOrphanTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetOrphanTasks indicates an expected call of GetOrphanTasks. func (mr *MockTaskManagerMockRecorder) GetOrphanTasks(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrphanTasks", reflect.TypeOf((*MockTaskManager)(nil).GetOrphanTasks), ctx, request) } // GetTaskList mocks base method. func (m *MockTaskManager) GetTaskList(ctx context.Context, request *GetTaskListRequest) (*GetTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskList", ctx, request) ret0, _ := ret[0].(*GetTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTaskList indicates an expected call of GetTaskList. func (mr *MockTaskManagerMockRecorder) GetTaskList(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskList", reflect.TypeOf((*MockTaskManager)(nil).GetTaskList), ctx, request) } // GetTaskListSize mocks base method. func (m *MockTaskManager) GetTaskListSize(ctx context.Context, request *GetTaskListSizeRequest) (*GetTaskListSizeResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskListSize", ctx, request) ret0, _ := ret[0].(*GetTaskListSizeResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTaskListSize indicates an expected call of GetTaskListSize. func (mr *MockTaskManagerMockRecorder) GetTaskListSize(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskListSize", reflect.TypeOf((*MockTaskManager)(nil).GetTaskListSize), ctx, request) } // GetTasks mocks base method. func (m *MockTaskManager) GetTasks(ctx context.Context, request *GetTasksRequest) (*GetTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasks", ctx, request) ret0, _ := ret[0].(*GetTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTasks indicates an expected call of GetTasks. func (mr *MockTaskManagerMockRecorder) GetTasks(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasks", reflect.TypeOf((*MockTaskManager)(nil).GetTasks), ctx, request) } // LeaseTaskList mocks base method. func (m *MockTaskManager) LeaseTaskList(ctx context.Context, request *LeaseTaskListRequest) (*LeaseTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LeaseTaskList", ctx, request) ret0, _ := ret[0].(*LeaseTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // LeaseTaskList indicates an expected call of LeaseTaskList. func (mr *MockTaskManagerMockRecorder) LeaseTaskList(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LeaseTaskList", reflect.TypeOf((*MockTaskManager)(nil).LeaseTaskList), ctx, request) } // ListTaskList mocks base method. func (m *MockTaskManager) ListTaskList(ctx context.Context, request *ListTaskListRequest) (*ListTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTaskList", ctx, request) ret0, _ := ret[0].(*ListTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTaskList indicates an expected call of ListTaskList. func (mr *MockTaskManagerMockRecorder) ListTaskList(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTaskList", reflect.TypeOf((*MockTaskManager)(nil).ListTaskList), ctx, request) } // UpdateTaskList mocks base method. func (m *MockTaskManager) UpdateTaskList(ctx context.Context, request *UpdateTaskListRequest) (*UpdateTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskList", ctx, request) ret0, _ := ret[0].(*UpdateTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskList indicates an expected call of UpdateTaskList. func (mr *MockTaskManagerMockRecorder) UpdateTaskList(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskList", reflect.TypeOf((*MockTaskManager)(nil).UpdateTaskList), ctx, request) } // MockHistoryManager is a mock of HistoryManager interface. type MockHistoryManager struct { ctrl *gomock.Controller recorder *MockHistoryManagerMockRecorder isgomock struct{} } // MockHistoryManagerMockRecorder is the mock recorder for MockHistoryManager. type MockHistoryManagerMockRecorder struct { mock *MockHistoryManager } // NewMockHistoryManager creates a new mock instance. func NewMockHistoryManager(ctrl *gomock.Controller) *MockHistoryManager { mock := &MockHistoryManager{ctrl: ctrl} mock.recorder = &MockHistoryManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHistoryManager) EXPECT() *MockHistoryManagerMockRecorder { return m.recorder } // AppendHistoryNodes mocks base method. func (m *MockHistoryManager) AppendHistoryNodes(ctx context.Context, request *AppendHistoryNodesRequest) (*AppendHistoryNodesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AppendHistoryNodes", ctx, request) ret0, _ := ret[0].(*AppendHistoryNodesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AppendHistoryNodes indicates an expected call of AppendHistoryNodes. func (mr *MockHistoryManagerMockRecorder) AppendHistoryNodes(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendHistoryNodes", reflect.TypeOf((*MockHistoryManager)(nil).AppendHistoryNodes), ctx, request) } // Close mocks base method. func (m *MockHistoryManager) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockHistoryManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockHistoryManager)(nil).Close)) } // DeleteHistoryBranch mocks base method. func (m *MockHistoryManager) DeleteHistoryBranch(ctx context.Context, request *DeleteHistoryBranchRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteHistoryBranch", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteHistoryBranch indicates an expected call of DeleteHistoryBranch. func (mr *MockHistoryManagerMockRecorder) DeleteHistoryBranch(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteHistoryBranch", reflect.TypeOf((*MockHistoryManager)(nil).DeleteHistoryBranch), ctx, request) } // ForkHistoryBranch mocks base method. func (m *MockHistoryManager) ForkHistoryBranch(ctx context.Context, request *ForkHistoryBranchRequest) (*ForkHistoryBranchResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ForkHistoryBranch", ctx, request) ret0, _ := ret[0].(*ForkHistoryBranchResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ForkHistoryBranch indicates an expected call of ForkHistoryBranch. func (mr *MockHistoryManagerMockRecorder) ForkHistoryBranch(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForkHistoryBranch", reflect.TypeOf((*MockHistoryManager)(nil).ForkHistoryBranch), ctx, request) } // GetAllHistoryTreeBranches mocks base method. func (m *MockHistoryManager) GetAllHistoryTreeBranches(ctx context.Context, request *GetAllHistoryTreeBranchesRequest) (*GetAllHistoryTreeBranchesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllHistoryTreeBranches", ctx, request) ret0, _ := ret[0].(*GetAllHistoryTreeBranchesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAllHistoryTreeBranches indicates an expected call of GetAllHistoryTreeBranches. func (mr *MockHistoryManagerMockRecorder) GetAllHistoryTreeBranches(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllHistoryTreeBranches", reflect.TypeOf((*MockHistoryManager)(nil).GetAllHistoryTreeBranches), ctx, request) } // GetHistoryTree mocks base method. func (m *MockHistoryManager) GetHistoryTree(ctx context.Context, request *GetHistoryTreeRequest) (*GetHistoryTreeResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryTree", ctx, request) ret0, _ := ret[0].(*GetHistoryTreeResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHistoryTree indicates an expected call of GetHistoryTree. func (mr *MockHistoryManagerMockRecorder) GetHistoryTree(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryTree", reflect.TypeOf((*MockHistoryManager)(nil).GetHistoryTree), ctx, request) } // GetName mocks base method. func (m *MockHistoryManager) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockHistoryManagerMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockHistoryManager)(nil).GetName)) } // ReadHistoryBranch mocks base method. func (m *MockHistoryManager) ReadHistoryBranch(ctx context.Context, request *ReadHistoryBranchRequest) (*ReadHistoryBranchResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadHistoryBranch", ctx, request) ret0, _ := ret[0].(*ReadHistoryBranchResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadHistoryBranch indicates an expected call of ReadHistoryBranch. func (mr *MockHistoryManagerMockRecorder) ReadHistoryBranch(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadHistoryBranch", reflect.TypeOf((*MockHistoryManager)(nil).ReadHistoryBranch), ctx, request) } // ReadHistoryBranchByBatch mocks base method. func (m *MockHistoryManager) ReadHistoryBranchByBatch(ctx context.Context, request *ReadHistoryBranchRequest) (*ReadHistoryBranchByBatchResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadHistoryBranchByBatch", ctx, request) ret0, _ := ret[0].(*ReadHistoryBranchByBatchResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadHistoryBranchByBatch indicates an expected call of ReadHistoryBranchByBatch. func (mr *MockHistoryManagerMockRecorder) ReadHistoryBranchByBatch(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadHistoryBranchByBatch", reflect.TypeOf((*MockHistoryManager)(nil).ReadHistoryBranchByBatch), ctx, request) } // ReadRawHistoryBranch mocks base method. func (m *MockHistoryManager) ReadRawHistoryBranch(ctx context.Context, request *ReadHistoryBranchRequest) (*ReadRawHistoryBranchResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadRawHistoryBranch", ctx, request) ret0, _ := ret[0].(*ReadRawHistoryBranchResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadRawHistoryBranch indicates an expected call of ReadRawHistoryBranch. func (mr *MockHistoryManagerMockRecorder) ReadRawHistoryBranch(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadRawHistoryBranch", reflect.TypeOf((*MockHistoryManager)(nil).ReadRawHistoryBranch), ctx, request) } // MockDomainManager is a mock of DomainManager interface. type MockDomainManager struct { ctrl *gomock.Controller recorder *MockDomainManagerMockRecorder isgomock struct{} } // MockDomainManagerMockRecorder is the mock recorder for MockDomainManager. type MockDomainManagerMockRecorder struct { mock *MockDomainManager } // NewMockDomainManager creates a new mock instance. func NewMockDomainManager(ctrl *gomock.Controller) *MockDomainManager { mock := &MockDomainManager{ctrl: ctrl} mock.recorder = &MockDomainManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDomainManager) EXPECT() *MockDomainManagerMockRecorder { return m.recorder } // Close mocks base method. func (m *MockDomainManager) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockDomainManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDomainManager)(nil).Close)) } // CreateDomain mocks base method. func (m *MockDomainManager) CreateDomain(ctx context.Context, request *CreateDomainRequest) (*CreateDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateDomain", ctx, request) ret0, _ := ret[0].(*CreateDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateDomain indicates an expected call of CreateDomain. func (mr *MockDomainManagerMockRecorder) CreateDomain(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDomain", reflect.TypeOf((*MockDomainManager)(nil).CreateDomain), ctx, request) } // DeleteDomain mocks base method. func (m *MockDomainManager) DeleteDomain(ctx context.Context, request *DeleteDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteDomain", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteDomain indicates an expected call of DeleteDomain. func (mr *MockDomainManagerMockRecorder) DeleteDomain(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDomain", reflect.TypeOf((*MockDomainManager)(nil).DeleteDomain), ctx, request) } // DeleteDomainByName mocks base method. func (m *MockDomainManager) DeleteDomainByName(ctx context.Context, request *DeleteDomainByNameRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteDomainByName", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteDomainByName indicates an expected call of DeleteDomainByName. func (mr *MockDomainManagerMockRecorder) DeleteDomainByName(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDomainByName", reflect.TypeOf((*MockDomainManager)(nil).DeleteDomainByName), ctx, request) } // GetDomain mocks base method. func (m *MockDomainManager) GetDomain(ctx context.Context, request *GetDomainRequest) (*GetDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomain", ctx, request) ret0, _ := ret[0].(*GetDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomain indicates an expected call of GetDomain. func (mr *MockDomainManagerMockRecorder) GetDomain(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomain", reflect.TypeOf((*MockDomainManager)(nil).GetDomain), ctx, request) } // GetMetadata mocks base method. func (m *MockDomainManager) GetMetadata(ctx context.Context) (*GetMetadataResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMetadata", ctx) ret0, _ := ret[0].(*GetMetadataResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMetadata indicates an expected call of GetMetadata. func (mr *MockDomainManagerMockRecorder) GetMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetadata", reflect.TypeOf((*MockDomainManager)(nil).GetMetadata), ctx) } // GetName mocks base method. func (m *MockDomainManager) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockDomainManagerMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockDomainManager)(nil).GetName)) } // ListDomains mocks base method. func (m *MockDomainManager) ListDomains(ctx context.Context, request *ListDomainsRequest) (*ListDomainsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListDomains", ctx, request) ret0, _ := ret[0].(*ListDomainsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListDomains indicates an expected call of ListDomains. func (mr *MockDomainManagerMockRecorder) ListDomains(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDomains", reflect.TypeOf((*MockDomainManager)(nil).ListDomains), ctx, request) } // UpdateDomain mocks base method. func (m *MockDomainManager) UpdateDomain(ctx context.Context, request *UpdateDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomain", ctx, request) ret0, _ := ret[0].(error) return ret0 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MockDomainManagerMockRecorder) UpdateDomain(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MockDomainManager)(nil).UpdateDomain), ctx, request) } // MockDomainAuditManager is a mock of DomainAuditManager interface. type MockDomainAuditManager struct { ctrl *gomock.Controller recorder *MockDomainAuditManagerMockRecorder isgomock struct{} } // MockDomainAuditManagerMockRecorder is the mock recorder for MockDomainAuditManager. type MockDomainAuditManagerMockRecorder struct { mock *MockDomainAuditManager } // NewMockDomainAuditManager creates a new mock instance. func NewMockDomainAuditManager(ctrl *gomock.Controller) *MockDomainAuditManager { mock := &MockDomainAuditManager{ctrl: ctrl} mock.recorder = &MockDomainAuditManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDomainAuditManager) EXPECT() *MockDomainAuditManagerMockRecorder { return m.recorder } // Close mocks base method. func (m *MockDomainAuditManager) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockDomainAuditManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDomainAuditManager)(nil).Close)) } // CreateDomainAuditLog mocks base method. func (m *MockDomainAuditManager) CreateDomainAuditLog(ctx context.Context, request *CreateDomainAuditLogRequest) (*CreateDomainAuditLogResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateDomainAuditLog", ctx, request) ret0, _ := ret[0].(*CreateDomainAuditLogResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateDomainAuditLog indicates an expected call of CreateDomainAuditLog. func (mr *MockDomainAuditManagerMockRecorder) CreateDomainAuditLog(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDomainAuditLog", reflect.TypeOf((*MockDomainAuditManager)(nil).CreateDomainAuditLog), ctx, request) } // GetDomainAuditLogs mocks base method. func (m *MockDomainAuditManager) GetDomainAuditLogs(ctx context.Context, request *GetDomainAuditLogsRequest) (*GetDomainAuditLogsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainAuditLogs", ctx, request) ret0, _ := ret[0].(*GetDomainAuditLogsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainAuditLogs indicates an expected call of GetDomainAuditLogs. func (mr *MockDomainAuditManagerMockRecorder) GetDomainAuditLogs(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainAuditLogs", reflect.TypeOf((*MockDomainAuditManager)(nil).GetDomainAuditLogs), ctx, request) } // GetName mocks base method. func (m *MockDomainAuditManager) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockDomainAuditManagerMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockDomainAuditManager)(nil).GetName)) } // MockQueueManager is a mock of QueueManager interface. type MockQueueManager struct { ctrl *gomock.Controller recorder *MockQueueManagerMockRecorder isgomock struct{} } // MockQueueManagerMockRecorder is the mock recorder for MockQueueManager. type MockQueueManagerMockRecorder struct { mock *MockQueueManager } // NewMockQueueManager creates a new mock instance. func NewMockQueueManager(ctrl *gomock.Controller) *MockQueueManager { mock := &MockQueueManager{ctrl: ctrl} mock.recorder = &MockQueueManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQueueManager) EXPECT() *MockQueueManagerMockRecorder { return m.recorder } // Close mocks base method. func (m *MockQueueManager) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockQueueManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockQueueManager)(nil).Close)) } // DeleteMessageFromDLQ mocks base method. func (m *MockQueueManager) DeleteMessageFromDLQ(ctx context.Context, request *DeleteMessageFromDLQRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessageFromDLQ", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteMessageFromDLQ indicates an expected call of DeleteMessageFromDLQ. func (mr *MockQueueManagerMockRecorder) DeleteMessageFromDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessageFromDLQ", reflect.TypeOf((*MockQueueManager)(nil).DeleteMessageFromDLQ), ctx, request) } // DeleteMessagesBefore mocks base method. func (m *MockQueueManager) DeleteMessagesBefore(ctx context.Context, request *DeleteMessagesBeforeRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessagesBefore", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteMessagesBefore indicates an expected call of DeleteMessagesBefore. func (mr *MockQueueManagerMockRecorder) DeleteMessagesBefore(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessagesBefore", reflect.TypeOf((*MockQueueManager)(nil).DeleteMessagesBefore), ctx, request) } // EnqueueMessage mocks base method. func (m *MockQueueManager) EnqueueMessage(ctx context.Context, request *EnqueueMessageRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EnqueueMessage", ctx, request) ret0, _ := ret[0].(error) return ret0 } // EnqueueMessage indicates an expected call of EnqueueMessage. func (mr *MockQueueManagerMockRecorder) EnqueueMessage(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnqueueMessage", reflect.TypeOf((*MockQueueManager)(nil).EnqueueMessage), ctx, request) } // EnqueueMessageToDLQ mocks base method. func (m *MockQueueManager) EnqueueMessageToDLQ(ctx context.Context, request *EnqueueMessageToDLQRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EnqueueMessageToDLQ", ctx, request) ret0, _ := ret[0].(error) return ret0 } // EnqueueMessageToDLQ indicates an expected call of EnqueueMessageToDLQ. func (mr *MockQueueManagerMockRecorder) EnqueueMessageToDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnqueueMessageToDLQ", reflect.TypeOf((*MockQueueManager)(nil).EnqueueMessageToDLQ), ctx, request) } // GetAckLevels mocks base method. func (m *MockQueueManager) GetAckLevels(ctx context.Context, request *GetAckLevelsRequest) (*GetAckLevelsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAckLevels", ctx, request) ret0, _ := ret[0].(*GetAckLevelsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAckLevels indicates an expected call of GetAckLevels. func (mr *MockQueueManagerMockRecorder) GetAckLevels(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAckLevels", reflect.TypeOf((*MockQueueManager)(nil).GetAckLevels), ctx, request) } // GetDLQAckLevels mocks base method. func (m *MockQueueManager) GetDLQAckLevels(ctx context.Context, request *GetDLQAckLevelsRequest) (*GetDLQAckLevelsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDLQAckLevels", ctx, request) ret0, _ := ret[0].(*GetDLQAckLevelsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDLQAckLevels indicates an expected call of GetDLQAckLevels. func (mr *MockQueueManagerMockRecorder) GetDLQAckLevels(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDLQAckLevels", reflect.TypeOf((*MockQueueManager)(nil).GetDLQAckLevels), ctx, request) } // GetDLQSize mocks base method. func (m *MockQueueManager) GetDLQSize(ctx context.Context, request *GetDLQSizeRequest) (*GetDLQSizeResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDLQSize", ctx, request) ret0, _ := ret[0].(*GetDLQSizeResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDLQSize indicates an expected call of GetDLQSize. func (mr *MockQueueManagerMockRecorder) GetDLQSize(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDLQSize", reflect.TypeOf((*MockQueueManager)(nil).GetDLQSize), ctx, request) } // RangeDeleteMessagesFromDLQ mocks base method. func (m *MockQueueManager) RangeDeleteMessagesFromDLQ(ctx context.Context, request *RangeDeleteMessagesFromDLQRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteMessagesFromDLQ", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteMessagesFromDLQ indicates an expected call of RangeDeleteMessagesFromDLQ. func (mr *MockQueueManagerMockRecorder) RangeDeleteMessagesFromDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteMessagesFromDLQ", reflect.TypeOf((*MockQueueManager)(nil).RangeDeleteMessagesFromDLQ), ctx, request) } // ReadMessages mocks base method. func (m *MockQueueManager) ReadMessages(ctx context.Context, request *ReadMessagesRequest) (*ReadMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadMessages", ctx, request) ret0, _ := ret[0].(*ReadMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadMessages indicates an expected call of ReadMessages. func (mr *MockQueueManagerMockRecorder) ReadMessages(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadMessages", reflect.TypeOf((*MockQueueManager)(nil).ReadMessages), ctx, request) } // ReadMessagesFromDLQ mocks base method. func (m *MockQueueManager) ReadMessagesFromDLQ(ctx context.Context, request *ReadMessagesFromDLQRequest) (*ReadMessagesFromDLQResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadMessagesFromDLQ", ctx, request) ret0, _ := ret[0].(*ReadMessagesFromDLQResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadMessagesFromDLQ indicates an expected call of ReadMessagesFromDLQ. func (mr *MockQueueManagerMockRecorder) ReadMessagesFromDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadMessagesFromDLQ", reflect.TypeOf((*MockQueueManager)(nil).ReadMessagesFromDLQ), ctx, request) } // UpdateAckLevel mocks base method. func (m *MockQueueManager) UpdateAckLevel(ctx context.Context, request *UpdateAckLevelRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAckLevel", ctx, request) ret0, _ := ret[0].(error) return ret0 } // UpdateAckLevel indicates an expected call of UpdateAckLevel. func (mr *MockQueueManagerMockRecorder) UpdateAckLevel(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAckLevel", reflect.TypeOf((*MockQueueManager)(nil).UpdateAckLevel), ctx, request) } // UpdateDLQAckLevel mocks base method. func (m *MockQueueManager) UpdateDLQAckLevel(ctx context.Context, request *UpdateDLQAckLevelRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDLQAckLevel", ctx, request) ret0, _ := ret[0].(error) return ret0 } // UpdateDLQAckLevel indicates an expected call of UpdateDLQAckLevel. func (mr *MockQueueManagerMockRecorder) UpdateDLQAckLevel(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDLQAckLevel", reflect.TypeOf((*MockQueueManager)(nil).UpdateDLQAckLevel), ctx, request) } // MockConfigStoreManager is a mock of ConfigStoreManager interface. type MockConfigStoreManager struct { ctrl *gomock.Controller recorder *MockConfigStoreManagerMockRecorder isgomock struct{} } // MockConfigStoreManagerMockRecorder is the mock recorder for MockConfigStoreManager. type MockConfigStoreManagerMockRecorder struct { mock *MockConfigStoreManager } // NewMockConfigStoreManager creates a new mock instance. func NewMockConfigStoreManager(ctrl *gomock.Controller) *MockConfigStoreManager { mock := &MockConfigStoreManager{ctrl: ctrl} mock.recorder = &MockConfigStoreManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockConfigStoreManager) EXPECT() *MockConfigStoreManagerMockRecorder { return m.recorder } // Close mocks base method. func (m *MockConfigStoreManager) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockConfigStoreManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockConfigStoreManager)(nil).Close)) } // FetchDynamicConfig mocks base method. func (m *MockConfigStoreManager) FetchDynamicConfig(ctx context.Context, cfgType ConfigType) (*FetchDynamicConfigResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FetchDynamicConfig", ctx, cfgType) ret0, _ := ret[0].(*FetchDynamicConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // FetchDynamicConfig indicates an expected call of FetchDynamicConfig. func (mr *MockConfigStoreManagerMockRecorder) FetchDynamicConfig(ctx, cfgType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchDynamicConfig", reflect.TypeOf((*MockConfigStoreManager)(nil).FetchDynamicConfig), ctx, cfgType) } // UpdateDynamicConfig mocks base method. func (m *MockConfigStoreManager) UpdateDynamicConfig(ctx context.Context, request *UpdateDynamicConfigRequest, cfgType ConfigType) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDynamicConfig", ctx, request, cfgType) ret0, _ := ret[0].(error) return ret0 } // UpdateDynamicConfig indicates an expected call of UpdateDynamicConfig. func (mr *MockConfigStoreManagerMockRecorder) UpdateDynamicConfig(ctx, request, cfgType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDynamicConfig", reflect.TypeOf((*MockConfigStoreManager)(nil).UpdateDynamicConfig), ctx, request, cfgType) } ================================================ FILE: common/persistence/data_manager_interfaces_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "bytes" "context" "errors" "fmt" "testing" "time" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/testing/testdatagen" "github.com/uber/cadence/common/types" ) func TestClusterReplicationConfigGetCopy(t *testing.T) { config := &ClusterReplicationConfig{ ClusterName: "test", } assert.Equal(t, config, config.GetCopy()) // deep equal assert.Equal(t, true, config != config.GetCopy()) } func TestGetDomainResponseDeepCopy(t *testing.T) { tests := []struct { name string input *GetDomainResponse validate func(t *testing.T, original, copied *GetDomainResponse) }{ { name: "nil response", input: nil, validate: func(t *testing.T, original, copied *GetDomainResponse) { assert.Nil(t, copied) }, }, { name: "empty response", input: &GetDomainResponse{}, validate: func(t *testing.T, original, copied *GetDomainResponse) { assert.NotNil(t, copied) assert.Equal(t, original, copied) assert.True(t, original != copied, "should be different pointers") }, }, { name: "full populated response", input: &GetDomainResponse{ Info: &DomainInfo{ ID: "full-id", Name: "full-name", Status: 1, Description: "full-description", OwnerEmail: "full@example.com", Data: map[string]string{"k": "v"}, }, Config: &DomainConfig{ Retention: 30, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "test://history", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "test://visibility", BadBinaries: types.BadBinaries{}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*ClusterReplicationConfig{{ClusterName: "c1"}}, }, IsGlobalDomain: true, ConfigVersion: 100, FailoverVersion: 200, FailoverNotificationVersion: 300, PreviousFailoverVersion: 400, FailoverEndTime: func() *int64 { i := int64(500); return &i }(), LastUpdatedTime: 600, NotificationVersion: 700, }, validate: func(t *testing.T, original, copied *GetDomainResponse) { assert.NotNil(t, copied) assert.Equal(t, original, copied) // Verify all nested pointers are different assert.True(t, original != copied) assert.True(t, original.Info != copied.Info) assert.True(t, original.Config != copied.Config) assert.True(t, original.ReplicationConfig != copied.ReplicationConfig) assert.True(t, original.FailoverEndTime != copied.FailoverEndTime) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { copied := tt.input.DeepCopy() tt.validate(t, tt.input, copied) }) } } func TestGetDomainResponseDeepCopy_FuzzGenerated(t *testing.T) { fuzzer := testdatagen.New(t) // Run multiple iterations to test with different data for i := 0; i < 100; i++ { t.Run(fmt.Sprintf("iteration_%d", i), func(t *testing.T) { original := &GetDomainResponse{} fuzzer.Fuzz(original) // Create a deep copy copied := original.DeepCopy() // Verify the copy is not nil require.NotNil(t, copied, "DeepCopy should not return nil for non-nil input") // Verify the copied struct is equal to the original assert.Equal(t, original, copied, "DeepCopy should produce an equal struct") // Verify they are different instances (different memory addresses) assert.True(t, original != copied, "DeepCopy should create a new instance") }) } } func TestIsTransientError(t *testing.T) { transientErrors := []error{ &types.ServiceBusyError{}, &types.InternalServiceError{}, &TimeoutError{}, } for _, err := range transientErrors { require.True(t, IsTransientError(err)) } nonRetryableErrors := []error{ &types.EntityNotExistsError{}, &types.DomainAlreadyExistsError{}, &WorkflowExecutionAlreadyStartedError{}, errors.New("some unknown error"), } for _, err := range nonRetryableErrors { require.False(t, IsTransientError(err)) } } func TestIsTimeoutError(t *testing.T) { tests := []struct { name string err error expected bool }{ { name: "nil error", err: nil, expected: false, }, { name: "regular error", err: fmt.Errorf("not timeout error"), expected: false, }, { name: "context.DeadlineExceeded", err: context.DeadlineExceeded, expected: true, }, { name: "context.Canceled", err: context.Canceled, expected: true, }, { name: "wrapped context.DeadlineExceeded", err: fmt.Errorf("operation failed: %w", context.DeadlineExceeded), expected: true, }, { name: "wrapped context.Canceled", err: fmt.Errorf("operation failed: %w", context.Canceled), expected: true, }, { name: "doubly wrapped context.DeadlineExceeded", err: fmt.Errorf("outer: %w", fmt.Errorf("inner: %w", context.DeadlineExceeded)), expected: true, }, { name: "doubly wrapped context.Canceled", err: fmt.Errorf("outer: %w", fmt.Errorf("inner: %w", context.Canceled)), expected: true, }, { name: "TimeoutError", err: &TimeoutError{Msg: "timeout"}, expected: true, }, { name: "wrapped TimeoutError", err: fmt.Errorf("operation failed: %w", &TimeoutError{Msg: "timeout"}), expected: true, }, { name: "doubly wrapped TimeoutError", err: fmt.Errorf("outer: %w", fmt.Errorf("inner: %w", &TimeoutError{Msg: "timeout"})), expected: true, }, { name: "empty TimeoutError", err: &TimeoutError{}, expected: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { result := IsTimeoutError(tc.err) assert.Equal(t, tc.expected, result) }) } } func TestIsBackgroundTransientError(t *testing.T) { transientErrors := map[error]bool{ &types.ServiceBusyError{}: false, &types.InternalServiceError{}: true, &TimeoutError{}: true, } for error, result := range transientErrors { assert.Equal(t, result, IsBackgroundTransientError(error)) } } func TestNewHistoryBranch(t *testing.T) { newTreeID := "newTreeID" generalToken, err := NewHistoryBranchToken(newTreeID) assert.NoError(t, err) assert.NotEmpty(t, generalToken) newBranchID := "newBranchID" branchToken, err := NewHistoryBranchTokenByBranchID(newTreeID, newBranchID) assert.NoError(t, err) assert.NotEmpty(t, branchToken) anotherBranchToken, err := NewHistoryBranchTokenFromAnother(newBranchID, generalToken) assert.NoError(t, err) assert.NotEmpty(t, anotherBranchToken) } func TestHistoryGarbageCleanupInfo(t *testing.T) { testDomainID := "testDomainID" testWorkflowID := "testWorkflowID" testRunID := "testRunID" actualInfo := BuildHistoryGarbageCleanupInfo(testDomainID, testWorkflowID, testRunID) splitDomainID, splitWorkflowID, splitRunID, splitError := SplitHistoryGarbageCleanupInfo(actualInfo) assert.Equal(t, testDomainID, splitDomainID) assert.Equal(t, testWorkflowID, splitWorkflowID) assert.Equal(t, testRunID, splitRunID) assert.NoError(t, splitError) _, _, _, splitError = SplitHistoryGarbageCleanupInfo("invalidInfo") assert.Error(t, splitError) } func TestNewGetReplicationTasksFromDLQRequest(t *testing.T) { sourceCluster := "testSourceCluster" readLevel := int64(1) maxReadLevel := int64(2) batchSize := 10 tasksRequest := NewGetReplicationTasksFromDLQRequest(sourceCluster, readLevel, maxReadLevel, batchSize, nil) assert.Equal(t, sourceCluster, tasksRequest.SourceClusterName) } func TestHasMoreRowsToDelete(t *testing.T) { assert.True(t, HasMoreRowsToDelete(10, 10)) assert.False(t, HasMoreRowsToDelete(11, 10)) assert.False(t, HasMoreRowsToDelete(9, 10)) assert.False(t, HasMoreRowsToDelete(-1, 10)) assert.False(t, HasMoreRowsToDelete(100, 0)) assert.False(t, HasMoreRowsToDelete(0, 0)) assert.False(t, HasMoreRowsToDelete(50, -1)) } func TestTransferTaskInfo(t *testing.T) { timeNow := time.Now() task := &TransferTaskInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", TargetDomainIDs: map[string]struct{}{"test-target-domain-id": {}}, TaskType: TransferTaskTypeActivityTask, TaskID: 1, ScheduleID: 1, Version: 1, VisibilityTimestamp: timeNow, } expectedString := fmt.Sprintf("%#v", task) assert.Equal(t, "test-domain-id", task.GetDomainID()) assert.Equal(t, "test-workflow-id", task.GetWorkflowID()) assert.Equal(t, "test-run-id", task.GetRunID()) assert.Equal(t, TransferTaskTypeActivityTask, task.GetTaskType()) assert.Equal(t, int64(1), task.GetTaskID()) assert.Equal(t, int64(1), task.GetVersion()) assert.Equal(t, timeNow, task.GetVisibilityTimestamp()) assert.Equal(t, map[string]struct{}{"test-target-domain-id": {}}, task.GetTargetDomainIDs()) assert.Equal(t, expectedString, task.String()) } func TestReplicationTaskInfo(t *testing.T) { task := &ReplicationTaskInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", TaskType: ReplicationTaskTypeHistory, TaskID: 1, Version: 1, FirstEventID: 1, NextEventID: 2, ScheduledID: 3, } assert.Equal(t, "test-domain-id", task.GetDomainID()) assert.Equal(t, "test-workflow-id", task.GetWorkflowID()) assert.Equal(t, "test-run-id", task.GetRunID()) assert.Equal(t, ReplicationTaskTypeHistory, task.GetTaskType()) assert.Equal(t, int64(1), task.GetTaskID()) assert.Equal(t, int64(1), task.GetVersion()) assert.Equal(t, time.Time{}, task.GetVisibilityTimestamp()) } func TestTimeTaskInfo(t *testing.T) { timeNow := time.Now() task := &TimerTaskInfo{ VisibilityTimestamp: timeNow, TaskType: 4, TaskID: 3, Version: 2, RunID: "test-run-id", DomainID: "test-domain-id", WorkflowID: "test-workflow-id", } expectedString := fmt.Sprintf( "{DomainID: %v, WorkflowID: %v, RunID: %v, VisibilityTimestamp: %v, TaskID: %v, TaskType: %v, TimeoutType: %v, EventID: %v, ScheduleAttempt: %v, Version: %v.}", task.DomainID, task.WorkflowID, task.RunID, task.VisibilityTimestamp, task.TaskID, task.TaskType, task.TimeoutType, task.EventID, task.ScheduleAttempt, task.Version, ) assert.Equal(t, 4, task.GetTaskType()) assert.Equal(t, int64(3), task.GetTaskID()) assert.Equal(t, int64(2), task.GetVersion()) assert.Equal(t, timeNow, task.GetVisibilityTimestamp()) assert.Equal(t, "test-run-id", task.GetRunID()) assert.Equal(t, "test-domain-id", task.GetDomainID()) assert.Equal(t, "test-workflow-id", task.GetWorkflowID()) assert.Equal(t, expectedString, task.String()) } func TestShardInfoCopy(t *testing.T) { f := fuzz.New().NilChance(0.1) info := &ShardInfo{} for i := 0; i < 1000; i++ { f.Fuzz(info) infoCopy := info.copy() assert.Equal(t, info, infoCopy) } } func TestShardInfoNilSafeCopy(t *testing.T) { var info *ShardInfo infoCopy := info.ToNilSafeCopy() assert.NotNil(t, infoCopy) assert.NotNil(t, infoCopy.ClusterTransferAckLevel) assert.NotNil(t, infoCopy.ClusterTimerAckLevel) assert.NotNil(t, infoCopy.ClusterReplicationLevel) assert.NotNil(t, infoCopy.QueueStates) assert.NotNil(t, infoCopy.TransferProcessingQueueStates) assert.NotNil(t, infoCopy.TransferProcessingQueueStates.StatesByCluster) assert.NotNil(t, infoCopy.TimerProcessingQueueStates) assert.NotNil(t, infoCopy.TimerProcessingQueueStates.StatesByCluster) assert.NotNil(t, infoCopy.ReplicationDLQAckLevel) } func TestSerializeAndDeserializeClusterConfigs(t *testing.T) { configs := []*ClusterReplicationConfig{ { ClusterName: "test-cluster1", }, { ClusterName: "test-cluster2", }, } serializedResult := SerializeClusterConfigs(configs) deserializedResult := DeserializeClusterConfigs(serializedResult) assert.Equal(t, configs, deserializedResult) } func TestTimeStampConvertion(t *testing.T) { timeNow := time.Now() milisSecond := UnixNanoToDBTimestamp(timeNow.UnixNano()) unixNanoTime := DBTimestampToUnixNano(milisSecond) assert.Equal(t, timeNow.UnixNano()/(1000*1000), unixNanoTime/(1000*1000)) // unixNano to milisSecond will result in info loss } func TestCopyMemo(t *testing.T) { tests := []struct { name string inputMemo map[string][]byte expectedOutput map[string][]byte }{ { name: "TC1: Memo is nil", inputMemo: nil, expectedOutput: nil, }, { name: "TC2: Memo is empty", inputMemo: map[string][]byte{}, expectedOutput: map[string][]byte{}, }, { name: "TC3: Memo contains multiple entries", inputMemo: map[string][]byte{ "key1": []byte("val1"), "key2": []byte("val2"), }, expectedOutput: map[string][]byte{ "key1": []byte("val1"), "key2": []byte("val2"), }, }, { name: "TC4: Memo contains empty byte slices", inputMemo: map[string][]byte{ "key1": []byte(""), "key2": []byte{}, }, expectedOutput: map[string][]byte{ "key1": []byte(""), "key2": []byte{}, }, }, { name: "TC5: Memo contains duplicate byte slices", inputMemo: map[string][]byte{ "key1": []byte("dup"), "key2": []byte("dup"), }, expectedOutput: map[string][]byte{ "key1": []byte("dup"), "key2": []byte("dup"), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { execInfo := &WorkflowExecutionInfo{ Memo: tt.inputMemo, } copyMemo := execInfo.CopyMemo() // Check if both are nil if tt.expectedOutput == nil { if copyMemo != nil { t.Errorf("Expected nil, got %v", copyMemo) } return } // Check if lengths match if len(copyMemo) != len(tt.expectedOutput) { t.Errorf("Expected map length %d, got %d", len(tt.expectedOutput), len(copyMemo)) } // Check each key-value pair for key, expectedVal := range tt.expectedOutput { copiedVal, exists := copyMemo[key] if !exists { t.Errorf("Expected key %s not found in copy", key) continue } if !bytes.Equal(copiedVal, expectedVal) { t.Errorf("For key %s, expected value %v, got %v", key, expectedVal, copiedVal) } // Ensure that the byte slices are different underlying arrays (deep copy) if len(copiedVal) > 0 && &copiedVal[0] == &expectedVal[0] { t.Errorf("For key %s, byte slices reference the same underlying array", key) } } }) } } func TestCopySearchAttributes(t *testing.T) { tests := []struct { name string inputSearchAttrs map[string][]byte expectedOutputAttrs map[string][]byte }{ { name: "TC1: SearchAttributes is nil", inputSearchAttrs: nil, expectedOutputAttrs: nil, }, { name: "TC2: SearchAttributes is empty", inputSearchAttrs: map[string][]byte{}, expectedOutputAttrs: map[string][]byte{}, }, { name: "TC3: SearchAttributes contains multiple entries", inputSearchAttrs: map[string][]byte{ "attr1": []byte("value1"), "attr2": []byte("value2"), }, expectedOutputAttrs: map[string][]byte{ "attr1": []byte("value1"), "attr2": []byte("value2"), }, }, { name: "TC4: SearchAttributes contains empty byte slices", inputSearchAttrs: map[string][]byte{ "attr1": []byte(""), "attr2": []byte{}, }, expectedOutputAttrs: map[string][]byte{ "attr1": []byte(""), "attr2": []byte{}, }, }, { name: "TC5: SearchAttributes contains duplicate byte slices", inputSearchAttrs: map[string][]byte{ "attr1": []byte("dup"), "attr2": []byte("dup"), }, expectedOutputAttrs: map[string][]byte{ "attr1": []byte("dup"), "attr2": []byte("dup"), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { execInfo := &WorkflowExecutionInfo{ SearchAttributes: tt.inputSearchAttrs, } copyAttrs := execInfo.CopySearchAttributes() // Check if both are nil if tt.expectedOutputAttrs == nil { if copyAttrs != nil { t.Errorf("Expected nil, got %v", copyAttrs) } return } // Check if lengths match if len(copyAttrs) != len(tt.expectedOutputAttrs) { t.Errorf("Expected map length %d, got %d", len(tt.expectedOutputAttrs), len(copyAttrs)) } // Check each key-value pair for key, expectedVal := range tt.expectedOutputAttrs { copiedVal, exists := copyAttrs[key] if !exists { t.Errorf("Expected key %s not found in copy", key) continue } if !bytes.Equal(copiedVal, expectedVal) { t.Errorf("For key %s, expected value %v, got %v", key, expectedVal, copiedVal) } // Ensure that the byte slices are different underlying arrays (deep copy) if len(copiedVal) > 0 && &copiedVal[0] == &expectedVal[0] { t.Errorf("For key %s, byte slices reference the same underlying array", key) } } }) } } func TestCopyPartitionConfig(t *testing.T) { tests := []struct { name string inputPartitionConfig map[string]string expectedOutputConfig map[string]string }{ { name: "TC1: PartitionConfig is nil", inputPartitionConfig: nil, expectedOutputConfig: nil, }, { name: "TC2: PartitionConfig is empty", inputPartitionConfig: map[string]string{}, expectedOutputConfig: map[string]string{}, }, { name: "TC3: PartitionConfig contains multiple entries", inputPartitionConfig: map[string]string{ "partition1": "config1", "partition2": "config2", }, expectedOutputConfig: map[string]string{ "partition1": "config1", "partition2": "config2", }, }, { name: "TC4: PartitionConfig contains empty strings", inputPartitionConfig: map[string]string{ "partition1": "", "partition2": "", }, expectedOutputConfig: map[string]string{ "partition1": "", "partition2": "", }, }, { name: "TC5: PartitionConfig contains duplicate values", inputPartitionConfig: map[string]string{ "partition1": "dup", "partition2": "dup", }, expectedOutputConfig: map[string]string{ "partition1": "dup", "partition2": "dup", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { execInfo := &WorkflowExecutionInfo{ PartitionConfig: tt.inputPartitionConfig, } copyConfig := execInfo.CopyPartitionConfig() // Check if both are nil if tt.expectedOutputConfig == nil { if copyConfig != nil { t.Errorf("Expected nil, got %v", copyConfig) } return } // Check if lengths match if len(copyConfig) != len(tt.expectedOutputConfig) { t.Errorf("Expected map length %d, got %d", len(tt.expectedOutputConfig), len(copyConfig)) } // Check each key-value pair for key, expectedVal := range tt.expectedOutputConfig { copiedVal, exists := copyConfig[key] if !exists { t.Errorf("Expected key %s not found in copy", key) continue } if copiedVal != expectedVal { t.Errorf("For key %s, expected value %s, got %s", key, expectedVal, copiedVal) } } // Since strings are immutable in Go, no need to check underlying references }) } } func TestTaskListPartitionConfigToInternalType(t *testing.T) { testCases := []struct { name string input *TaskListPartitionConfig expect *types.TaskListPartitionConfig }{ { name: "nil case", input: nil, expect: nil, }, { name: "empty case", input: &TaskListPartitionConfig{}, expect: &types.TaskListPartitionConfig{}, }, { name: "normal case", input: &TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*TaskListPartition{ 0: { IsolationGroups: []string{"foo"}, }, 1: {}, }, WritePartitions: map[int]*TaskListPartition{ 0: { IsolationGroups: []string{"foo"}, }, 1: {}, 2: {}, }, }, expect: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: { IsolationGroups: []string{"foo"}, }, 1: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: { IsolationGroups: []string{"foo"}, }, 1: {}, 2: {}, }, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expect, tc.input.ToInternalType()) }) } } func TestVersionHistoryCopy(t *testing.T) { a := VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: []*VersionHistory{ { BranchToken: []byte("token"), Items: []*VersionHistoryItem{ { EventID: 1, Version: 2, }, }, }, { BranchToken: []byte("token"), Items: []*VersionHistoryItem{ { EventID: 1, Version: 2, }, }, }, }, } assert.Equal(t, &a, a.Duplicate()) } func TestDomainReplicationConfig_IsActiveActive(t *testing.T) { tests := []struct { name string config *DomainReplicationConfig want bool }{ { name: "nil DomainReplicationConfig should return false", config: nil, want: false, }, { name: "nil ActiveClusters should return false", config: &DomainReplicationConfig{ActiveClusters: nil}, want: false, }, { name: "empty ActiveClusters should return false", config: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{}, }, want: false, }, { name: "only AttributeScopes populated should return true", config: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster2", FailoverVersion: 200, }, }, }, }, }, }, want: true, }, { name: "both formats populated should return true", config: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 100, }, "us-west-1": { ActiveClusterName: "cluster2", FailoverVersion: 200, }, }, }, }, }, }, want: true, }, { name: "empty AttributeScopes map should return false", config: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{}, }, }, want: false, }, { name: "AttributeScopes with empty scope should return false", config: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, }, }, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.config.IsActiveActive() if got != tt.want { t.Errorf("IsActiveActive() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: common/persistence/data_store_interfaces.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "context" "fmt" "time" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -destination data_store_interfaces_mock.go -self_package github.com/uber/cadence/common/persistence github.com/uber/cadence/common/persistence ExecutionStore,ShardStore,DomainStore,TaskStore,HistoryStore,ConfigStore,DomainAuditStore //go:generate mockgen -package $GOPACKAGE -destination visibility_store_mock.go -self_package github.com/uber/cadence/common/persistence github.com/uber/cadence/common/persistence VisibilityStore type ( // //////////////////////////////////////////////////////////////////// // Persistence interface is a lower layer of dataInterface. // The intention is to let different persistence implementation(SQL,Cassandra/etc) share some common logic // Right now the only common part is serialization/deserialization, and only ExecutionManager/HistoryManager need it. // TaskManager are the same. // //////////////////////////////////////////////////////////////////// // ShardStore is the lower level of ShardManager ShardStore interface { Closeable GetName() string CreateShard(ctx context.Context, request *InternalCreateShardRequest) error GetShard(ctx context.Context, request *InternalGetShardRequest) (*InternalGetShardResponse, error) UpdateShard(ctx context.Context, request *InternalUpdateShardRequest) error } // TaskStore is a lower level of TaskManager TaskStore interface { Closeable GetName() string LeaseTaskList(ctx context.Context, request *LeaseTaskListRequest) (*LeaseTaskListResponse, error) GetTaskList(ctx context.Context, request *GetTaskListRequest) (*GetTaskListResponse, error) UpdateTaskList(ctx context.Context, request *UpdateTaskListRequest) (*UpdateTaskListResponse, error) ListTaskList(ctx context.Context, request *ListTaskListRequest) (*ListTaskListResponse, error) DeleteTaskList(ctx context.Context, request *DeleteTaskListRequest) error GetTaskListSize(ctx context.Context, request *GetTaskListSizeRequest) (*GetTaskListSizeResponse, error) CreateTasks(ctx context.Context, request *CreateTasksRequest) (*CreateTasksResponse, error) GetTasks(ctx context.Context, request *GetTasksRequest) (*GetTasksResponse, error) CompleteTask(ctx context.Context, request *CompleteTaskRequest) error // CompleteTasksLessThan completes tasks less than or equal to the given task id // This API takes a limit parameter which specifies the count of maxRows that // can be deleted. This parameter may be ignored by the underlying storage, but // its mandatory to specify it. On success this method returns the number of rows // actually deleted. If the underlying storage doesn't support "limit", all rows // less than or equal to taskID will be deleted. // On success, this method returns: // - number of rows actually deleted, if limit is honored // - UnknownNumRowsDeleted, when all rows below value are deleted CompleteTasksLessThan(ctx context.Context, request *CompleteTasksLessThanRequest) (*CompleteTasksLessThanResponse, error) // GetOrphanTasks returns tasks that exist as records in the database but are part of task lists which // _do not_ exist in the database. They are therefore unreachable and no longer represent valid items // that can be legitimately acted upon. GetOrphanTasks(ctx context.Context, request *GetOrphanTasksRequest) (*GetOrphanTasksResponse, error) } // DomainStore is a lower level of DomainManager DomainStore interface { Closeable GetName() string CreateDomain(ctx context.Context, request *InternalCreateDomainRequest) (*CreateDomainResponse, error) GetDomain(ctx context.Context, request *GetDomainRequest) (*InternalGetDomainResponse, error) UpdateDomain(ctx context.Context, request *InternalUpdateDomainRequest) error DeleteDomain(ctx context.Context, request *DeleteDomainRequest) error DeleteDomainByName(ctx context.Context, request *DeleteDomainByNameRequest) error ListDomains(ctx context.Context, request *ListDomainsRequest) (*InternalListDomainsResponse, error) GetMetadata(ctx context.Context) (*GetMetadataResponse, error) } // DomainAuditStore is a lower level of DomainAuditManager DomainAuditStore interface { Closeable GetName() string CreateDomainAuditLog(ctx context.Context, request *InternalCreateDomainAuditLogRequest) (*CreateDomainAuditLogResponse, error) GetDomainAuditLogs(ctx context.Context, request *GetDomainAuditLogsRequest) (*InternalGetDomainAuditLogsResponse, error) } // ExecutionStore is used to manage workflow executions for Persistence layer ExecutionStore interface { Closeable GetName() string GetShardID() int // The below three APIs are related to serialization/deserialization GetWorkflowExecution(ctx context.Context, request *InternalGetWorkflowExecutionRequest) (*InternalGetWorkflowExecutionResponse, error) UpdateWorkflowExecution(ctx context.Context, request *InternalUpdateWorkflowExecutionRequest) error ConflictResolveWorkflowExecution(ctx context.Context, request *InternalConflictResolveWorkflowExecutionRequest) error CreateWorkflowExecution(ctx context.Context, request *InternalCreateWorkflowExecutionRequest) (*CreateWorkflowExecutionResponse, error) DeleteWorkflowExecution(ctx context.Context, request *DeleteWorkflowExecutionRequest) error DeleteCurrentWorkflowExecution(ctx context.Context, request *DeleteCurrentWorkflowExecutionRequest) error GetCurrentExecution(ctx context.Context, request *GetCurrentExecutionRequest) (*GetCurrentExecutionResponse, error) IsWorkflowExecutionExists(ctx context.Context, request *IsWorkflowExecutionExistsRequest) (*IsWorkflowExecutionExistsResponse, error) // Replication task related methods PutReplicationTaskToDLQ(ctx context.Context, request *InternalPutReplicationTaskToDLQRequest) error GetReplicationTasksFromDLQ(ctx context.Context, request *GetReplicationTasksFromDLQRequest) (*GetHistoryTasksResponse, error) GetReplicationDLQSize(ctx context.Context, request *GetReplicationDLQSizeRequest) (*GetReplicationDLQSizeResponse, error) DeleteReplicationTaskFromDLQ(ctx context.Context, request *DeleteReplicationTaskFromDLQRequest) error RangeDeleteReplicationTaskFromDLQ(ctx context.Context, request *RangeDeleteReplicationTaskFromDLQRequest) (*RangeDeleteReplicationTaskFromDLQResponse, error) CreateFailoverMarkerTasks(ctx context.Context, request *CreateFailoverMarkersRequest) error // History task related methods GetHistoryTasks(ctx context.Context, request *GetHistoryTasksRequest) (*GetHistoryTasksResponse, error) CompleteHistoryTask(ctx context.Context, request *CompleteHistoryTaskRequest) error RangeCompleteHistoryTask(ctx context.Context, request *RangeCompleteHistoryTaskRequest) (*RangeCompleteHistoryTaskResponse, error) // Scan related methods ListConcreteExecutions(ctx context.Context, request *ListConcreteExecutionsRequest) (*InternalListConcreteExecutionsResponse, error) ListCurrentExecutions(ctx context.Context, request *ListCurrentExecutionsRequest) (*ListCurrentExecutionsResponse, error) // Active cluster selection policy related methods GetActiveClusterSelectionPolicy(ctx context.Context, domainID, wfID, rID string) (*DataBlob, error) DeleteActiveClusterSelectionPolicy(ctx context.Context, domainID, wfID, rID string) error } // HistoryStore is to manager workflow history events HistoryStore interface { Closeable GetName() string // The below are history V2 APIs // V2 regards history events growing as a tree, decoupled from workflow concepts // AppendHistoryNodes add(or override) a node to a history branch AppendHistoryNodes(ctx context.Context, request *InternalAppendHistoryNodesRequest) error // ReadHistoryBranch returns history node data for a branch ReadHistoryBranch(ctx context.Context, request *InternalReadHistoryBranchRequest) (*InternalReadHistoryBranchResponse, error) // ForkHistoryBranch forks a new branch from a old branch ForkHistoryBranch(ctx context.Context, request *InternalForkHistoryBranchRequest) (*InternalForkHistoryBranchResponse, error) // DeleteHistoryBranch removes a branch DeleteHistoryBranch(ctx context.Context, request *InternalDeleteHistoryBranchRequest) error // GetHistoryTree returns all branch information of a tree GetHistoryTree(ctx context.Context, request *InternalGetHistoryTreeRequest) (*InternalGetHistoryTreeResponse, error) // GetAllHistoryTreeBranches returns all branches of all trees GetAllHistoryTreeBranches(ctx context.Context, request *GetAllHistoryTreeBranchesRequest) (*GetAllHistoryTreeBranchesResponse, error) } // VisibilityStore is the store interface for visibility VisibilityStore interface { Closeable GetName() string RecordWorkflowExecutionStarted(ctx context.Context, request *InternalRecordWorkflowExecutionStartedRequest) error RecordWorkflowExecutionClosed(ctx context.Context, request *InternalRecordWorkflowExecutionClosedRequest) error RecordWorkflowExecutionUninitialized(ctx context.Context, request *InternalRecordWorkflowExecutionUninitializedRequest) error UpsertWorkflowExecution(ctx context.Context, request *InternalUpsertWorkflowExecutionRequest) error ListOpenWorkflowExecutions(ctx context.Context, request *InternalListWorkflowExecutionsRequest) (*InternalListWorkflowExecutionsResponse, error) ListClosedWorkflowExecutions(ctx context.Context, request *InternalListWorkflowExecutionsRequest) (*InternalListWorkflowExecutionsResponse, error) ListOpenWorkflowExecutionsByType(ctx context.Context, request *InternalListWorkflowExecutionsByTypeRequest) (*InternalListWorkflowExecutionsResponse, error) ListClosedWorkflowExecutionsByType(ctx context.Context, request *InternalListWorkflowExecutionsByTypeRequest) (*InternalListWorkflowExecutionsResponse, error) ListOpenWorkflowExecutionsByWorkflowID(ctx context.Context, request *InternalListWorkflowExecutionsByWorkflowIDRequest) (*InternalListWorkflowExecutionsResponse, error) ListClosedWorkflowExecutionsByWorkflowID(ctx context.Context, request *InternalListWorkflowExecutionsByWorkflowIDRequest) (*InternalListWorkflowExecutionsResponse, error) ListClosedWorkflowExecutionsByStatus(ctx context.Context, request *InternalListClosedWorkflowExecutionsByStatusRequest) (*InternalListWorkflowExecutionsResponse, error) GetClosedWorkflowExecution(ctx context.Context, request *InternalGetClosedWorkflowExecutionRequest) (*InternalGetClosedWorkflowExecutionResponse, error) DeleteWorkflowExecution(ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest) error ListWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*InternalListWorkflowExecutionsResponse, error) ScanWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*InternalListWorkflowExecutionsResponse, error) CountWorkflowExecutions(ctx context.Context, request *CountWorkflowExecutionsRequest) (*CountWorkflowExecutionsResponse, error) DeleteUninitializedWorkflowExecution(ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest) error } ConfigStore interface { Closeable FetchConfig(ctx context.Context, configType ConfigType) (*InternalConfigStoreEntry, error) UpdateConfig(ctx context.Context, value *InternalConfigStoreEntry) error } InternalConfigStoreEntry struct { RowType int Version int64 Timestamp time.Time Values *DataBlob } InternalEnqueueMessageRequest struct { MessagePayload []byte CurrentTimeStamp time.Time } InternalReadMessagesRequest struct { LastMessageID int64 MaxCount int } InternalReadMessagesResponse struct { Messages []*InternalQueueMessage } InternalDeleteMessagesBeforeRequest struct { MessageID int64 } InternalUpdateAckLevelRequest struct { MessageID int64 ClusterName string CurrentTimeStamp time.Time } InternalGetAckLevelsRequest struct{} InternalGetAckLevelsResponse struct { AckLevels map[string]int64 } InternalEnqueueMessageToDLQRequest struct { MessagePayload []byte CurrentTimeStamp time.Time } InternalReadMessagesFromDLQRequest struct { FirstMessageID int64 LastMessageID int64 PageSize int PageToken []byte } InternalReadMessagesFromDLQResponse struct { Messages []*InternalQueueMessage NextPageToken []byte } InternalDeleteMessageFromDLQRequest struct { MessageID int64 } InternalRangeDeleteMessagesFromDLQRequest struct { FirstMessageID int64 LastMessageID int64 } InternalUpdateDLQAckLevelRequest struct { MessageID int64 ClusterName string CurrentTimeStamp time.Time } InternalGetDLQAckLevelsRequest struct{} InternalGetDLQAckLevelsResponse struct { AckLevels map[string]int64 } InternalGetDLQSizeRequest struct{} InternalGetDLQSizeResponse struct { Size int64 } // QueueStore is a store to enqueue and get messages QueueStore interface { Closeable EnqueueMessage(ctx context.Context, request *InternalEnqueueMessageRequest) error ReadMessages(ctx context.Context, request *InternalReadMessagesRequest) (*InternalReadMessagesResponse, error) DeleteMessagesBefore(ctx context.Context, request *InternalDeleteMessagesBeforeRequest) error UpdateAckLevel(ctx context.Context, request *InternalUpdateAckLevelRequest) error GetAckLevels(ctx context.Context, request *InternalGetAckLevelsRequest) (*InternalGetAckLevelsResponse, error) EnqueueMessageToDLQ(ctx context.Context, request *InternalEnqueueMessageToDLQRequest) error ReadMessagesFromDLQ(ctx context.Context, request *InternalReadMessagesFromDLQRequest) (*InternalReadMessagesFromDLQResponse, error) DeleteMessageFromDLQ(ctx context.Context, request *InternalDeleteMessageFromDLQRequest) error RangeDeleteMessagesFromDLQ(ctx context.Context, request *InternalRangeDeleteMessagesFromDLQRequest) error UpdateDLQAckLevel(ctx context.Context, request *InternalUpdateDLQAckLevelRequest) error GetDLQAckLevels(ctx context.Context, request *InternalGetDLQAckLevelsRequest) (*InternalGetDLQAckLevelsResponse, error) GetDLQSize(ctx context.Context, request *InternalGetDLQSizeRequest) (*InternalGetDLQSizeResponse, error) } // InternalQueueMessage is the message that stores in the queue InternalQueueMessage struct { ID int64 `json:"message_id"` QueueType QueueType `json:"queue_type"` Payload []byte `json:"message_payload"` } // DataBlob represents a blob for any binary data. // It contains raw data, and metadata(right now only encoding) in other field // Note that it should be only used for Persistence layer, below dataInterface and application(historyEngine/etc) DataBlob struct { Encoding constants.EncodingType Data []byte } // InternalCreateWorkflowExecutionRequest is used to write a new workflow execution InternalCreateWorkflowExecutionRequest struct { RangeID int64 Mode CreateWorkflowMode PreviousRunID string PreviousLastWriteVersion int64 NewWorkflowSnapshot InternalWorkflowSnapshot WorkflowRequestMode CreateWorkflowRequestMode CurrentTimeStamp time.Time } // InternalPutReplicationTaskToDLQRequest is used to put a replication task to dlq InternalPutReplicationTaskToDLQRequest struct { SourceClusterName string TaskInfo *InternalReplicationTaskInfo } // InternalReplicationTaskInfo describes the replication task created for replication of history events InternalReplicationTaskInfo struct { DomainID string WorkflowID string RunID string TaskID int64 TaskType int FirstEventID int64 NextEventID int64 Version int64 ScheduledID int64 BranchToken []byte NewRunBranchToken []byte CreationTime time.Time CurrentTimeStamp time.Time } // InternalWorkflowExecutionInfo describes a workflow execution for Persistence Interface InternalWorkflowExecutionInfo struct { DomainID string WorkflowID string RunID string FirstExecutionRunID string ParentDomainID string ParentWorkflowID string ParentRunID string InitiatedID int64 CompletionEventBatchID int64 CompletionEvent *DataBlob TaskList string TaskListKind types.TaskListKind WorkflowTypeName string WorkflowTimeout time.Duration DecisionStartToCloseTimeout time.Duration ExecutionContext []byte State int CloseStatus int LastFirstEventID int64 LastEventTaskID int64 NextEventID int64 LastProcessedEvent int64 StartTimestamp time.Time LastUpdatedTimestamp time.Time CreateRequestID string SignalCount int32 DecisionVersion int64 DecisionScheduleID int64 DecisionStartedID int64 DecisionRequestID string DecisionTimeout time.Duration DecisionAttempt int64 DecisionStartedTimestamp time.Time DecisionScheduledTimestamp time.Time DecisionOriginalScheduledTimestamp time.Time CancelRequested bool CancelRequestID string StickyTaskList string StickyScheduleToStartTimeout time.Duration ClientLibraryVersion string ClientFeatureVersion string ClientImpl string AutoResetPoints *DataBlob // for retry Attempt int32 HasRetryPolicy bool InitialInterval time.Duration BackoffCoefficient float64 MaximumInterval time.Duration ExpirationTime time.Time MaximumAttempts int32 NonRetriableErrors []string BranchToken []byte CronSchedule string CronOverlapPolicy types.CronOverlapPolicy ExpirationInterval time.Duration Memo map[string][]byte SearchAttributes map[string][]byte PartitionConfig map[string]string ActiveClusterSelectionPolicy *DataBlob // attributes which are not related to mutable state at all HistorySize int64 IsCron bool } // InternalWorkflowMutableState indicates workflow related state for Persistence Interface InternalWorkflowMutableState struct { ExecutionInfo *InternalWorkflowExecutionInfo VersionHistories *DataBlob ReplicationState *ReplicationState // TODO: remove this after all 2DC workflows complete ActivityInfos map[int64]*InternalActivityInfo TimerInfos map[string]*TimerInfo ChildExecutionInfos map[int64]*InternalChildExecutionInfo RequestCancelInfos map[int64]*RequestCancelInfo SignalInfos map[int64]*SignalInfo SignalRequestedIDs map[string]struct{} BufferedEvents []*DataBlob // Checksum field is used by Cassandra storage // ChecksumData is used by All SQL storage Checksum checksum.Checksum ChecksumData *DataBlob } // InternalActivityInfo details for Persistence Interface InternalActivityInfo struct { Version int64 ScheduleID int64 ScheduledEventBatchID int64 ScheduledEvent *DataBlob ScheduledTime time.Time StartedID int64 StartedEvent *DataBlob StartedTime time.Time ActivityID string RequestID string Details []byte ScheduleToStartTimeout time.Duration ScheduleToCloseTimeout time.Duration StartToCloseTimeout time.Duration HeartbeatTimeout time.Duration CancelRequested bool CancelRequestID int64 LastHeartBeatUpdatedTime time.Time TimerTaskStatus int32 // For retry Attempt int32 DomainID string StartedIdentity string TaskList string TaskListKind types.TaskListKind HasRetryPolicy bool InitialInterval time.Duration BackoffCoefficient float64 MaximumInterval time.Duration ExpirationTime time.Time MaximumAttempts int32 NonRetriableErrors []string LastFailureReason string LastWorkerIdentity string LastFailureDetails []byte // Not written to database - This is used only for deduping heartbeat timer creation LastHeartbeatTimeoutVisibilityInSeconds int64 } // InternalChildExecutionInfo has details for pending child executions for Persistence Interface InternalChildExecutionInfo struct { Version int64 InitiatedID int64 InitiatedEventBatchID int64 InitiatedEvent *DataBlob StartedID int64 StartedWorkflowID string StartedRunID string StartedEvent *DataBlob CreateRequestID string DomainID string DomainNameDEPRECATED string // deprecated: use DomainID field WorkflowTypeName string ParentClosePolicy types.ParentClosePolicy } // InternalUpdateWorkflowExecutionRequest is used to update a workflow execution for Persistence Interface InternalUpdateWorkflowExecutionRequest struct { RangeID int64 Mode UpdateWorkflowMode UpdateWorkflowMutation InternalWorkflowMutation NewWorkflowSnapshot *InternalWorkflowSnapshot WorkflowRequestMode CreateWorkflowRequestMode CurrentTimeStamp time.Time } // InternalConflictResolveWorkflowExecutionRequest is used to reset workflow execution state for Persistence Interface InternalConflictResolveWorkflowExecutionRequest struct { RangeID int64 Mode ConflictResolveWorkflowMode // workflow to be resetted ResetWorkflowSnapshot InternalWorkflowSnapshot // maybe new workflow NewWorkflowSnapshot *InternalWorkflowSnapshot // current workflow CurrentWorkflowMutation *InternalWorkflowMutation WorkflowRequestMode CreateWorkflowRequestMode CurrentTimeStamp time.Time } // InternalWorkflowMutation is used as generic workflow execution state mutation for Persistence Interface InternalWorkflowMutation struct { ExecutionInfo *InternalWorkflowExecutionInfo VersionHistories *DataBlob StartVersion int64 LastWriteVersion int64 UpsertActivityInfos []*InternalActivityInfo DeleteActivityInfos []int64 UpsertTimerInfos []*TimerInfo DeleteTimerInfos []string UpsertChildExecutionInfos []*InternalChildExecutionInfo DeleteChildExecutionInfos []int64 UpsertRequestCancelInfos []*RequestCancelInfo DeleteRequestCancelInfos []int64 UpsertSignalInfos []*SignalInfo DeleteSignalInfos []int64 UpsertSignalRequestedIDs []string DeleteSignalRequestedIDs []string NewBufferedEvents *DataBlob ClearBufferedEvents bool TasksByCategory map[HistoryTaskCategory][]Task WorkflowRequests []*WorkflowRequest Condition int64 Checksum checksum.Checksum ChecksumData *DataBlob } // InternalWorkflowSnapshot is used as generic workflow execution state snapshot for Persistence Interface InternalWorkflowSnapshot struct { ExecutionInfo *InternalWorkflowExecutionInfo VersionHistories *DataBlob StartVersion int64 LastWriteVersion int64 ActivityInfos []*InternalActivityInfo TimerInfos []*TimerInfo ChildExecutionInfos []*InternalChildExecutionInfo RequestCancelInfos []*RequestCancelInfo SignalInfos []*SignalInfo SignalRequestedIDs []string TasksByCategory map[HistoryTaskCategory][]Task WorkflowRequests []*WorkflowRequest Condition int64 Checksum checksum.Checksum ChecksumData *DataBlob } // InternalAppendHistoryEventsRequest is used to append new events to workflow execution history for Persistence Interface InternalAppendHistoryEventsRequest struct { DomainID string Execution workflow.WorkflowExecution FirstEventID int64 EventBatchVersion int64 RangeID int64 TransactionID int64 Events *DataBlob Overwrite bool } // InternalAppendHistoryNodesRequest is used to append a batch of history nodes InternalAppendHistoryNodesRequest struct { // True if it is the first append request to the branch IsNewBranch bool // The info for clean up data in background Info string // The branch to be appended BranchInfo types.HistoryBranch // The first eventID becomes the nodeID to be appended NodeID int64 // The events to be appended Events *DataBlob // Requested TransactionID for conditional update TransactionID int64 // Used in sharded data stores to identify which shard to use ShardID int CurrentTimeStamp time.Time } // InternalGetWorkflowExecutionRequest is used to retrieve the info of a workflow execution InternalGetWorkflowExecutionRequest struct { DomainID string Execution types.WorkflowExecution RangeID int64 } // InternalGetWorkflowExecutionResponse is the response to GetWorkflowExecution for Persistence Interface InternalGetWorkflowExecutionResponse struct { State *InternalWorkflowMutableState } // InternalListConcreteExecutionsResponse is the response to ListConcreteExecutions for Persistence Interface InternalListConcreteExecutionsResponse struct { Executions []*InternalListConcreteExecutionsEntity NextPageToken []byte } // InternalListConcreteExecutionsEntity is a single entity in InternalListConcreteExecutionsResponse InternalListConcreteExecutionsEntity struct { ExecutionInfo *InternalWorkflowExecutionInfo VersionHistories *DataBlob } // InternalForkHistoryBranchRequest is used to fork a history branch InternalForkHistoryBranchRequest struct { // The base branch to fork from ForkBranchInfo types.HistoryBranch // The nodeID to fork from, the new branch will start from ( inclusive ), the base branch will stop at(exclusive) ForkNodeID int64 // branchID of the new branch NewBranchID string // the info for clean up data in background Info string // Used in sharded data stores to identify which shard to use ShardID int CurrentTimeStamp time.Time } // InternalForkHistoryBranchResponse is the response to ForkHistoryBranchRequest InternalForkHistoryBranchResponse struct { // branchInfo to represent the new branch NewBranchInfo types.HistoryBranch } // InternalDeleteHistoryBranchRequest is used to remove a history branch InternalDeleteHistoryBranchRequest struct { // branch to be deleted BranchInfo types.HistoryBranch // Used in sharded data stores to identify which shard to use ShardID int } // InternalReadHistoryBranchRequest is used to read a history branch InternalReadHistoryBranchRequest struct { // The tree of branch range to be read TreeID string // The branch range to be read BranchID string // Get the history nodes from MinNodeID. Inclusive. MinNodeID int64 // Get the history nodes upto MaxNodeID. Exclusive. MaxNodeID int64 // passing thru for pagination PageSize int // Pagination token NextPageToken []byte // LastNodeID is the last known node ID attached to a history node LastNodeID int64 // LastTransactionID is the last known transaction ID attached to a history node LastTransactionID int64 // Used in sharded data stores to identify which shard to use ShardID int } // InternalCompleteForkBranchRequest is used to update some tree/branch meta data for forking InternalCompleteForkBranchRequest struct { // branch to be updated BranchInfo workflow.HistoryBranch // whether fork is successful Success bool // Used in sharded data stores to identify which shard to use ShardID int } // InternalReadHistoryBranchResponse is the response to ReadHistoryBranchRequest InternalReadHistoryBranchResponse struct { // History events History []*DataBlob // Pagination token NextPageToken []byte // LastNodeID is the last known node ID attached to a history node LastNodeID int64 // LastTransactionID is the last known transaction ID attached to a history node LastTransactionID int64 } // InternalGetHistoryTreeRequest is used to get history tree InternalGetHistoryTreeRequest struct { // A UUID of a tree TreeID string // Get data from this shard ShardID *int // optional: can provide treeID via branchToken if treeID is empty BranchToken []byte } // InternalGetHistoryTreeResponse is the response to GetHistoryTree InternalGetHistoryTreeResponse struct { // all branches of a tree Branches []*types.HistoryBranch } // InternalVisibilityWorkflowExecutionInfo is visibility info for internal response InternalVisibilityWorkflowExecutionInfo struct { DomainID string WorkflowType string WorkflowID string RunID string TypeName string StartTime time.Time ExecutionTime time.Time CloseTime time.Time Status *types.WorkflowExecutionCloseStatus HistoryLength int64 Memo *DataBlob TaskList string IsCron bool NumClusters int16 ClusterAttributeScope string ClusterAttributeName string UpdateTime time.Time SearchAttributes map[string]interface{} ShardID int16 CronSchedule string ExecutionStatus types.WorkflowExecutionStatus ScheduledExecutionTime time.Time } // InternalListWorkflowExecutionsResponse is response from ListWorkflowExecutions InternalListWorkflowExecutionsResponse struct { Executions []*InternalVisibilityWorkflowExecutionInfo // Token to read next page if there are more workflow executions beyond page size. // Use this to set NextPageToken on ListWorkflowExecutionsRequest to read the next page. NextPageToken []byte } // InternalGetClosedWorkflowExecutionRequest is used retrieve the record for a specific execution InternalGetClosedWorkflowExecutionRequest struct { DomainUUID string Domain string // domain name is not persisted, but used as config filter key Execution types.WorkflowExecution } // InternalListClosedWorkflowExecutionsByStatusRequest is used to list executions that have specific close status InternalListClosedWorkflowExecutionsByStatusRequest struct { InternalListWorkflowExecutionsRequest Status types.WorkflowExecutionCloseStatus } // InternalListWorkflowExecutionsByWorkflowIDRequest is used to list executions that have specific WorkflowID in a domain InternalListWorkflowExecutionsByWorkflowIDRequest struct { InternalListWorkflowExecutionsRequest WorkflowID string } // InternalListWorkflowExecutionsByTypeRequest is used to list executions of a specific type in a domain InternalListWorkflowExecutionsByTypeRequest struct { InternalListWorkflowExecutionsRequest WorkflowTypeName string } // InternalGetClosedWorkflowExecutionResponse is response from GetWorkflowExecution InternalGetClosedWorkflowExecutionResponse struct { Execution *InternalVisibilityWorkflowExecutionInfo } // InternalRecordWorkflowExecutionStartedRequest request to RecordWorkflowExecutionStarted InternalRecordWorkflowExecutionStartedRequest struct { DomainUUID string WorkflowID string RunID string WorkflowTypeName string StartTimestamp time.Time ExecutionTimestamp time.Time WorkflowTimeout time.Duration TaskID int64 Memo *DataBlob TaskList string IsCron bool CronSchedule string NumClusters int16 ClusterAttributeScope string ClusterAttributeName string UpdateTimestamp time.Time SearchAttributes map[string][]byte ShardID int16 ExecutionStatus types.WorkflowExecutionStatus ScheduledExecutionTime time.Time } // InternalRecordWorkflowExecutionClosedRequest is request to RecordWorkflowExecutionClosed InternalRecordWorkflowExecutionClosedRequest struct { DomainUUID string WorkflowID string RunID string WorkflowTypeName string StartTimestamp time.Time ExecutionTimestamp time.Time TaskID int64 Memo *DataBlob TaskList string SearchAttributes map[string][]byte CloseTimestamp time.Time Status types.WorkflowExecutionCloseStatus HistoryLength int64 RetentionPeriod time.Duration IsCron bool CronSchedule string NumClusters int16 ClusterAttributeScope string ClusterAttributeName string UpdateTimestamp time.Time ShardID int16 ExecutionStatus types.WorkflowExecutionStatus ScheduledExecutionTime time.Time } // InternalRecordWorkflowExecutionUninitializedRequest is used to add a record of a newly uninitialized execution InternalRecordWorkflowExecutionUninitializedRequest struct { DomainUUID string WorkflowID string RunID string WorkflowTypeName string UpdateTimestamp time.Time ShardID int64 } // InternalUpsertWorkflowExecutionRequest is request to UpsertWorkflowExecution InternalUpsertWorkflowExecutionRequest struct { DomainUUID string WorkflowID string RunID string WorkflowTypeName string StartTimestamp time.Time ExecutionTimestamp time.Time WorkflowTimeout time.Duration TaskID int64 Memo *DataBlob TaskList string IsCron bool CronSchedule string NumClusters int16 ClusterAttributeScope string ClusterAttributeName string UpdateTimestamp time.Time SearchAttributes map[string][]byte ShardID int64 ExecutionStatus types.WorkflowExecutionStatus ScheduledExecutionTimestamp int64 } // InternalListWorkflowExecutionsRequest is used to list executions in a domain InternalListWorkflowExecutionsRequest struct { DomainUUID string Domain string // domain name is not persisted, but used as config filter key // The earliest end of the time range EarliestTime time.Time // The latest end of the time range LatestTime time.Time // Maximum number of workflow executions per page PageSize int // Token to continue reading next page of workflow executions. // Pass in empty slice for first page. NextPageToken []byte } // InternalDomainConfig describes the domain configuration InternalDomainConfig struct { Retention time.Duration EmitMetric bool // deprecated ArchivalBucket string // deprecated ArchivalStatus types.ArchivalStatus // deprecated HistoryArchivalStatus types.ArchivalStatus HistoryArchivalURI string VisibilityArchivalStatus types.ArchivalStatus VisibilityArchivalURI string BadBinaries *DataBlob IsolationGroups *DataBlob AsyncWorkflowsConfig *DataBlob } InternalDomainReplicationConfig struct { Clusters []*ClusterReplicationConfig ActiveClusterName string ActiveClustersConfig *DataBlob } // InternalCreateDomainRequest is used to create the domain InternalCreateDomainRequest struct { Info *DomainInfo Config *InternalDomainConfig ReplicationConfig *InternalDomainReplicationConfig IsGlobalDomain bool ConfigVersion int64 FailoverVersion int64 LastUpdatedTime time.Time CurrentTimeStamp time.Time } // InternalGetDomainResponse is the response for GetDomain InternalGetDomainResponse struct { Info *DomainInfo Config *InternalDomainConfig ReplicationConfig *InternalDomainReplicationConfig IsGlobalDomain bool ConfigVersion int64 FailoverVersion int64 FailoverNotificationVersion int64 PreviousFailoverVersion int64 FailoverEndTime *time.Time LastUpdatedTime time.Time NotificationVersion int64 } // InternalUpdateDomainRequest is used to update domain InternalUpdateDomainRequest struct { Info *DomainInfo Config *InternalDomainConfig ReplicationConfig *InternalDomainReplicationConfig ConfigVersion int64 FailoverVersion int64 FailoverNotificationVersion int64 PreviousFailoverVersion int64 FailoverEndTime *time.Time LastUpdatedTime time.Time NotificationVersion int64 } // InternalListDomainsResponse is the response for GetDomain InternalListDomainsResponse struct { Domains []*InternalGetDomainResponse NextPageToken []byte } DomainAuditOperationType int // InternalCreateDomainAuditLogRequest is used to create a domain audit log entry InternalCreateDomainAuditLogRequest struct { DomainID string EventID string StateBefore *DataBlob StateAfter *DataBlob OperationType DomainAuditOperationType CreatedTime time.Time LastUpdatedTime time.Time Identity string IdentityType string Comment string TTLSeconds int64 // TTL for the audit log entry in seconds } // InternalGetDomainAuditLogsResponse is the response for GetDomainAuditLogs InternalGetDomainAuditLogsResponse struct { AuditLogs []*InternalDomainAuditLog NextPageToken []byte } // InternalDomainAuditLog represents a single internal domain audit log entry InternalDomainAuditLog struct { EventID string DomainID string StateBefore *DataBlob StateAfter *DataBlob OperationType DomainAuditOperationType CreatedTime time.Time LastUpdatedTime time.Time Identity string IdentityType string Comment string } // InternalShardInfo describes a shard InternalShardInfo struct { ShardID int `json:"shard_id"` Owner string `json:"owner"` RangeID int64 `json:"range_id"` StolenSinceRenew int `json:"stolen_since_renew"` UpdatedAt time.Time `json:"updated_at"` ReplicationAckLevel int64 `json:"replication_ack_level"` ReplicationDLQAckLevel map[string]int64 `json:"replication_dlq_ack_level"` TransferAckLevel int64 `json:"transfer_ack_level"` TimerAckLevel time.Time `json:"timer_ack_level"` ClusterTransferAckLevel map[string]int64 `json:"cluster_transfer_ack_level"` ClusterTimerAckLevel map[string]time.Time `json:"cluster_timer_ack_level"` TransferProcessingQueueStates *DataBlob `json:"transfer_processing_queue_states"` TimerProcessingQueueStates *DataBlob `json:"timer_processing_queue_states"` ClusterReplicationLevel map[string]int64 `json:"cluster_replication_level"` DomainNotificationVersion int64 `json:"domain_notification_version"` PendingFailoverMarkers *DataBlob `json:"pending_failover_markers"` QueueStates map[int32]*types.QueueState `json:"queue_states"` CurrentTimestamp time.Time } // InternalCreateShardRequest is request to CreateShard InternalCreateShardRequest struct { ShardInfo *InternalShardInfo CurrentTimeStamp time.Time } // InternalGetShardRequest is used to get shard information InternalGetShardRequest struct { ShardID int } // InternalUpdateShardRequest is used to update shard information InternalUpdateShardRequest struct { ShardInfo *InternalShardInfo PreviousRangeID int64 CurrentTimeStamp time.Time } // InternalGetShardResponse is the response to GetShard InternalGetShardResponse struct { ShardInfo *InternalShardInfo } ) func (tr *InternalGetHistoryTreeResponse) ByBranchID() map[string]*types.HistoryBranch { out := make(map[string]*types.HistoryBranch, len(tr.Branches)) for _, branch := range tr.Branches { out[branch.BranchID] = branch } return out } // NewDataBlob returns a new DataBlob func NewDataBlob(data []byte, encodingType constants.EncodingType) *DataBlob { if len(data) == 0 { return nil } if encodingType != constants.EncodingTypeThriftRW && data[0] == 'Y' { // original reason for this is not written down, but maybe for handling data prior to an encoding type? panic(fmt.Sprintf("Invalid data blob encoding: \"%v\"", encodingType)) } return &DataBlob{ Data: data, Encoding: encodingType, } } // FromDataBlob decodes a datablob into a (payload, encodingType) tuple func FromDataBlob(blob *DataBlob) ([]byte, string) { if blob == nil || len(blob.Data) == 0 { return nil, "" } return blob.Data, string(blob.Encoding) } // Convert a *Datablob to safe that calling its method won't run into NPE func (d *DataBlob) ToNilSafeDataBlob() *DataBlob { if d != nil { return d } return &DataBlob{} } func (d *DataBlob) GetEncodingString() string { if d == nil { return "" } return string(d.Encoding) } // GetData is a safe way to get the byte array or nil func (d *DataBlob) GetData() []byte { if d == nil || d.Data == nil { return []byte{} } return d.Data } // GetEncoding returns encoding type func (d *DataBlob) GetEncoding() constants.EncodingType { encodingStr := d.GetEncodingString() switch constants.EncodingType(encodingStr) { case constants.EncodingTypeGob: return constants.EncodingTypeGob case constants.EncodingTypeJSON: return constants.EncodingTypeJSON case constants.EncodingTypeThriftRW: return constants.EncodingTypeThriftRW case constants.EncodingTypeThriftRWSnappy: return constants.EncodingTypeThriftRWSnappy case constants.EncodingTypeEmpty: return constants.EncodingTypeEmpty default: return constants.EncodingTypeUnknown } } // ToInternal convert data blob to internal representation func (d *DataBlob) ToInternal() *types.DataBlob { switch d.Encoding { case constants.EncodingTypeJSON: return &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: d.Data, } case constants.EncodingTypeThriftRW: return &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: d.Data, } default: panic(fmt.Sprintf("DataBlob.ToInternal() with unsupported encoding type: %v", d.Encoding)) } } // NewDataBlobFromInternal convert data blob from internal representation func NewDataBlobFromInternal(blob *types.DataBlob) *DataBlob { switch blob.GetEncodingType() { case types.EncodingTypeJSON: return &DataBlob{ Encoding: constants.EncodingTypeJSON, Data: blob.Data, } case types.EncodingTypeThriftRW: return &DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: blob.Data, } default: panic(fmt.Sprintf("NewDataBlobFromInternal with unsupported encoding type: %v", blob.GetEncodingType())) } } func (t *InternalReplicationTaskInfo) ToTask() (Task, error) { switch t.TaskType { case ReplicationTaskTypeHistory: return &HistoryReplicationTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: t.DomainID, WorkflowID: t.WorkflowID, RunID: t.RunID, }, TaskData: TaskData{ Version: t.Version, TaskID: t.TaskID, VisibilityTimestamp: t.CreationTime, }, FirstEventID: t.FirstEventID, NextEventID: t.NextEventID, BranchToken: t.BranchToken, NewRunBranchToken: t.NewRunBranchToken, }, nil case ReplicationTaskTypeSyncActivity: return &SyncActivityTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: t.DomainID, WorkflowID: t.WorkflowID, RunID: t.RunID, }, TaskData: TaskData{ Version: t.Version, TaskID: t.TaskID, VisibilityTimestamp: t.CreationTime, }, ScheduledID: t.ScheduledID, }, nil case ReplicationTaskTypeFailoverMarker: return &FailoverMarkerTask{ TaskData: TaskData{ Version: t.Version, TaskID: t.TaskID, }, DomainID: t.DomainID, }, nil default: return nil, fmt.Errorf("unknown task type: %d", t.TaskType) } } ================================================ FILE: common/persistence/data_store_interfaces_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/persistence (interfaces: ExecutionStore,ShardStore,DomainStore,TaskStore,HistoryStore,ConfigStore,DomainAuditStore) // // Generated by this command: // // mockgen -package persistence -destination data_store_interfaces_mock.go -self_package github.com/uber/cadence/common/persistence github.com/uber/cadence/common/persistence ExecutionStore,ShardStore,DomainStore,TaskStore,HistoryStore,ConfigStore,DomainAuditStore // // Package persistence is a generated GoMock package. package persistence import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockExecutionStore is a mock of ExecutionStore interface. type MockExecutionStore struct { ctrl *gomock.Controller recorder *MockExecutionStoreMockRecorder isgomock struct{} } // MockExecutionStoreMockRecorder is the mock recorder for MockExecutionStore. type MockExecutionStoreMockRecorder struct { mock *MockExecutionStore } // NewMockExecutionStore creates a new mock instance. func NewMockExecutionStore(ctrl *gomock.Controller) *MockExecutionStore { mock := &MockExecutionStore{ctrl: ctrl} mock.recorder = &MockExecutionStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockExecutionStore) EXPECT() *MockExecutionStoreMockRecorder { return m.recorder } // Close mocks base method. func (m *MockExecutionStore) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockExecutionStoreMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockExecutionStore)(nil).Close)) } // CompleteHistoryTask mocks base method. func (m *MockExecutionStore) CompleteHistoryTask(ctx context.Context, request *CompleteHistoryTaskRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CompleteHistoryTask", ctx, request) ret0, _ := ret[0].(error) return ret0 } // CompleteHistoryTask indicates an expected call of CompleteHistoryTask. func (mr *MockExecutionStoreMockRecorder) CompleteHistoryTask(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompleteHistoryTask", reflect.TypeOf((*MockExecutionStore)(nil).CompleteHistoryTask), ctx, request) } // ConflictResolveWorkflowExecution mocks base method. func (m *MockExecutionStore) ConflictResolveWorkflowExecution(ctx context.Context, request *InternalConflictResolveWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConflictResolveWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // ConflictResolveWorkflowExecution indicates an expected call of ConflictResolveWorkflowExecution. func (mr *MockExecutionStoreMockRecorder) ConflictResolveWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConflictResolveWorkflowExecution", reflect.TypeOf((*MockExecutionStore)(nil).ConflictResolveWorkflowExecution), ctx, request) } // CreateFailoverMarkerTasks mocks base method. func (m *MockExecutionStore) CreateFailoverMarkerTasks(ctx context.Context, request *CreateFailoverMarkersRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateFailoverMarkerTasks", ctx, request) ret0, _ := ret[0].(error) return ret0 } // CreateFailoverMarkerTasks indicates an expected call of CreateFailoverMarkerTasks. func (mr *MockExecutionStoreMockRecorder) CreateFailoverMarkerTasks(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFailoverMarkerTasks", reflect.TypeOf((*MockExecutionStore)(nil).CreateFailoverMarkerTasks), ctx, request) } // CreateWorkflowExecution mocks base method. func (m *MockExecutionStore) CreateWorkflowExecution(ctx context.Context, request *InternalCreateWorkflowExecutionRequest) (*CreateWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateWorkflowExecution", ctx, request) ret0, _ := ret[0].(*CreateWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateWorkflowExecution indicates an expected call of CreateWorkflowExecution. func (mr *MockExecutionStoreMockRecorder) CreateWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateWorkflowExecution", reflect.TypeOf((*MockExecutionStore)(nil).CreateWorkflowExecution), ctx, request) } // DeleteActiveClusterSelectionPolicy mocks base method. func (m *MockExecutionStore) DeleteActiveClusterSelectionPolicy(ctx context.Context, domainID, wfID, rID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteActiveClusterSelectionPolicy", ctx, domainID, wfID, rID) ret0, _ := ret[0].(error) return ret0 } // DeleteActiveClusterSelectionPolicy indicates an expected call of DeleteActiveClusterSelectionPolicy. func (mr *MockExecutionStoreMockRecorder) DeleteActiveClusterSelectionPolicy(ctx, domainID, wfID, rID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteActiveClusterSelectionPolicy", reflect.TypeOf((*MockExecutionStore)(nil).DeleteActiveClusterSelectionPolicy), ctx, domainID, wfID, rID) } // DeleteCurrentWorkflowExecution mocks base method. func (m *MockExecutionStore) DeleteCurrentWorkflowExecution(ctx context.Context, request *DeleteCurrentWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteCurrentWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteCurrentWorkflowExecution indicates an expected call of DeleteCurrentWorkflowExecution. func (mr *MockExecutionStoreMockRecorder) DeleteCurrentWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentWorkflowExecution", reflect.TypeOf((*MockExecutionStore)(nil).DeleteCurrentWorkflowExecution), ctx, request) } // DeleteReplicationTaskFromDLQ mocks base method. func (m *MockExecutionStore) DeleteReplicationTaskFromDLQ(ctx context.Context, request *DeleteReplicationTaskFromDLQRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteReplicationTaskFromDLQ", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteReplicationTaskFromDLQ indicates an expected call of DeleteReplicationTaskFromDLQ. func (mr *MockExecutionStoreMockRecorder) DeleteReplicationTaskFromDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationTaskFromDLQ", reflect.TypeOf((*MockExecutionStore)(nil).DeleteReplicationTaskFromDLQ), ctx, request) } // DeleteWorkflowExecution mocks base method. func (m *MockExecutionStore) DeleteWorkflowExecution(ctx context.Context, request *DeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteWorkflowExecution indicates an expected call of DeleteWorkflowExecution. func (mr *MockExecutionStoreMockRecorder) DeleteWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWorkflowExecution", reflect.TypeOf((*MockExecutionStore)(nil).DeleteWorkflowExecution), ctx, request) } // GetActiveClusterSelectionPolicy mocks base method. func (m *MockExecutionStore) GetActiveClusterSelectionPolicy(ctx context.Context, domainID, wfID, rID string) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveClusterSelectionPolicy", ctx, domainID, wfID, rID) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // GetActiveClusterSelectionPolicy indicates an expected call of GetActiveClusterSelectionPolicy. func (mr *MockExecutionStoreMockRecorder) GetActiveClusterSelectionPolicy(ctx, domainID, wfID, rID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterSelectionPolicy", reflect.TypeOf((*MockExecutionStore)(nil).GetActiveClusterSelectionPolicy), ctx, domainID, wfID, rID) } // GetCurrentExecution mocks base method. func (m *MockExecutionStore) GetCurrentExecution(ctx context.Context, request *GetCurrentExecutionRequest) (*GetCurrentExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCurrentExecution", ctx, request) ret0, _ := ret[0].(*GetCurrentExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentExecution indicates an expected call of GetCurrentExecution. func (mr *MockExecutionStoreMockRecorder) GetCurrentExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentExecution", reflect.TypeOf((*MockExecutionStore)(nil).GetCurrentExecution), ctx, request) } // GetHistoryTasks mocks base method. func (m *MockExecutionStore) GetHistoryTasks(ctx context.Context, request *GetHistoryTasksRequest) (*GetHistoryTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryTasks", ctx, request) ret0, _ := ret[0].(*GetHistoryTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHistoryTasks indicates an expected call of GetHistoryTasks. func (mr *MockExecutionStoreMockRecorder) GetHistoryTasks(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryTasks", reflect.TypeOf((*MockExecutionStore)(nil).GetHistoryTasks), ctx, request) } // GetName mocks base method. func (m *MockExecutionStore) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockExecutionStoreMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockExecutionStore)(nil).GetName)) } // GetReplicationDLQSize mocks base method. func (m *MockExecutionStore) GetReplicationDLQSize(ctx context.Context, request *GetReplicationDLQSizeRequest) (*GetReplicationDLQSizeResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReplicationDLQSize", ctx, request) ret0, _ := ret[0].(*GetReplicationDLQSizeResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetReplicationDLQSize indicates an expected call of GetReplicationDLQSize. func (mr *MockExecutionStoreMockRecorder) GetReplicationDLQSize(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationDLQSize", reflect.TypeOf((*MockExecutionStore)(nil).GetReplicationDLQSize), ctx, request) } // GetReplicationTasksFromDLQ mocks base method. func (m *MockExecutionStore) GetReplicationTasksFromDLQ(ctx context.Context, request *GetReplicationTasksFromDLQRequest) (*GetHistoryTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReplicationTasksFromDLQ", ctx, request) ret0, _ := ret[0].(*GetHistoryTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetReplicationTasksFromDLQ indicates an expected call of GetReplicationTasksFromDLQ. func (mr *MockExecutionStoreMockRecorder) GetReplicationTasksFromDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationTasksFromDLQ", reflect.TypeOf((*MockExecutionStore)(nil).GetReplicationTasksFromDLQ), ctx, request) } // GetShardID mocks base method. func (m *MockExecutionStore) GetShardID() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardID") ret0, _ := ret[0].(int) return ret0 } // GetShardID indicates an expected call of GetShardID. func (mr *MockExecutionStoreMockRecorder) GetShardID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardID", reflect.TypeOf((*MockExecutionStore)(nil).GetShardID)) } // GetWorkflowExecution mocks base method. func (m *MockExecutionStore) GetWorkflowExecution(ctx context.Context, request *InternalGetWorkflowExecutionRequest) (*InternalGetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowExecution", ctx, request) ret0, _ := ret[0].(*InternalGetWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetWorkflowExecution indicates an expected call of GetWorkflowExecution. func (mr *MockExecutionStoreMockRecorder) GetWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowExecution", reflect.TypeOf((*MockExecutionStore)(nil).GetWorkflowExecution), ctx, request) } // IsWorkflowExecutionExists mocks base method. func (m *MockExecutionStore) IsWorkflowExecutionExists(ctx context.Context, request *IsWorkflowExecutionExistsRequest) (*IsWorkflowExecutionExistsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsWorkflowExecutionExists", ctx, request) ret0, _ := ret[0].(*IsWorkflowExecutionExistsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // IsWorkflowExecutionExists indicates an expected call of IsWorkflowExecutionExists. func (mr *MockExecutionStoreMockRecorder) IsWorkflowExecutionExists(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWorkflowExecutionExists", reflect.TypeOf((*MockExecutionStore)(nil).IsWorkflowExecutionExists), ctx, request) } // ListConcreteExecutions mocks base method. func (m *MockExecutionStore) ListConcreteExecutions(ctx context.Context, request *ListConcreteExecutionsRequest) (*InternalListConcreteExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListConcreteExecutions", ctx, request) ret0, _ := ret[0].(*InternalListConcreteExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListConcreteExecutions indicates an expected call of ListConcreteExecutions. func (mr *MockExecutionStoreMockRecorder) ListConcreteExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListConcreteExecutions", reflect.TypeOf((*MockExecutionStore)(nil).ListConcreteExecutions), ctx, request) } // ListCurrentExecutions mocks base method. func (m *MockExecutionStore) ListCurrentExecutions(ctx context.Context, request *ListCurrentExecutionsRequest) (*ListCurrentExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListCurrentExecutions", ctx, request) ret0, _ := ret[0].(*ListCurrentExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListCurrentExecutions indicates an expected call of ListCurrentExecutions. func (mr *MockExecutionStoreMockRecorder) ListCurrentExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCurrentExecutions", reflect.TypeOf((*MockExecutionStore)(nil).ListCurrentExecutions), ctx, request) } // PutReplicationTaskToDLQ mocks base method. func (m *MockExecutionStore) PutReplicationTaskToDLQ(ctx context.Context, request *InternalPutReplicationTaskToDLQRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PutReplicationTaskToDLQ", ctx, request) ret0, _ := ret[0].(error) return ret0 } // PutReplicationTaskToDLQ indicates an expected call of PutReplicationTaskToDLQ. func (mr *MockExecutionStoreMockRecorder) PutReplicationTaskToDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutReplicationTaskToDLQ", reflect.TypeOf((*MockExecutionStore)(nil).PutReplicationTaskToDLQ), ctx, request) } // RangeCompleteHistoryTask mocks base method. func (m *MockExecutionStore) RangeCompleteHistoryTask(ctx context.Context, request *RangeCompleteHistoryTaskRequest) (*RangeCompleteHistoryTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeCompleteHistoryTask", ctx, request) ret0, _ := ret[0].(*RangeCompleteHistoryTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeCompleteHistoryTask indicates an expected call of RangeCompleteHistoryTask. func (mr *MockExecutionStoreMockRecorder) RangeCompleteHistoryTask(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeCompleteHistoryTask", reflect.TypeOf((*MockExecutionStore)(nil).RangeCompleteHistoryTask), ctx, request) } // RangeDeleteReplicationTaskFromDLQ mocks base method. func (m *MockExecutionStore) RangeDeleteReplicationTaskFromDLQ(ctx context.Context, request *RangeDeleteReplicationTaskFromDLQRequest) (*RangeDeleteReplicationTaskFromDLQResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteReplicationTaskFromDLQ", ctx, request) ret0, _ := ret[0].(*RangeDeleteReplicationTaskFromDLQResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteReplicationTaskFromDLQ indicates an expected call of RangeDeleteReplicationTaskFromDLQ. func (mr *MockExecutionStoreMockRecorder) RangeDeleteReplicationTaskFromDLQ(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteReplicationTaskFromDLQ", reflect.TypeOf((*MockExecutionStore)(nil).RangeDeleteReplicationTaskFromDLQ), ctx, request) } // UpdateWorkflowExecution mocks base method. func (m *MockExecutionStore) UpdateWorkflowExecution(ctx context.Context, request *InternalUpdateWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowExecution indicates an expected call of UpdateWorkflowExecution. func (mr *MockExecutionStoreMockRecorder) UpdateWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecution", reflect.TypeOf((*MockExecutionStore)(nil).UpdateWorkflowExecution), ctx, request) } // MockShardStore is a mock of ShardStore interface. type MockShardStore struct { ctrl *gomock.Controller recorder *MockShardStoreMockRecorder isgomock struct{} } // MockShardStoreMockRecorder is the mock recorder for MockShardStore. type MockShardStoreMockRecorder struct { mock *MockShardStore } // NewMockShardStore creates a new mock instance. func NewMockShardStore(ctrl *gomock.Controller) *MockShardStore { mock := &MockShardStore{ctrl: ctrl} mock.recorder = &MockShardStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShardStore) EXPECT() *MockShardStoreMockRecorder { return m.recorder } // Close mocks base method. func (m *MockShardStore) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockShardStoreMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockShardStore)(nil).Close)) } // CreateShard mocks base method. func (m *MockShardStore) CreateShard(ctx context.Context, request *InternalCreateShardRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateShard", ctx, request) ret0, _ := ret[0].(error) return ret0 } // CreateShard indicates an expected call of CreateShard. func (mr *MockShardStoreMockRecorder) CreateShard(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateShard", reflect.TypeOf((*MockShardStore)(nil).CreateShard), ctx, request) } // GetName mocks base method. func (m *MockShardStore) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockShardStoreMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockShardStore)(nil).GetName)) } // GetShard mocks base method. func (m *MockShardStore) GetShard(ctx context.Context, request *InternalGetShardRequest) (*InternalGetShardResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShard", ctx, request) ret0, _ := ret[0].(*InternalGetShardResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetShard indicates an expected call of GetShard. func (mr *MockShardStoreMockRecorder) GetShard(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShard", reflect.TypeOf((*MockShardStore)(nil).GetShard), ctx, request) } // UpdateShard mocks base method. func (m *MockShardStore) UpdateShard(ctx context.Context, request *InternalUpdateShardRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateShard", ctx, request) ret0, _ := ret[0].(error) return ret0 } // UpdateShard indicates an expected call of UpdateShard. func (mr *MockShardStoreMockRecorder) UpdateShard(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateShard", reflect.TypeOf((*MockShardStore)(nil).UpdateShard), ctx, request) } // MockDomainStore is a mock of DomainStore interface. type MockDomainStore struct { ctrl *gomock.Controller recorder *MockDomainStoreMockRecorder isgomock struct{} } // MockDomainStoreMockRecorder is the mock recorder for MockDomainStore. type MockDomainStoreMockRecorder struct { mock *MockDomainStore } // NewMockDomainStore creates a new mock instance. func NewMockDomainStore(ctrl *gomock.Controller) *MockDomainStore { mock := &MockDomainStore{ctrl: ctrl} mock.recorder = &MockDomainStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDomainStore) EXPECT() *MockDomainStoreMockRecorder { return m.recorder } // Close mocks base method. func (m *MockDomainStore) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockDomainStoreMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDomainStore)(nil).Close)) } // CreateDomain mocks base method. func (m *MockDomainStore) CreateDomain(ctx context.Context, request *InternalCreateDomainRequest) (*CreateDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateDomain", ctx, request) ret0, _ := ret[0].(*CreateDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateDomain indicates an expected call of CreateDomain. func (mr *MockDomainStoreMockRecorder) CreateDomain(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDomain", reflect.TypeOf((*MockDomainStore)(nil).CreateDomain), ctx, request) } // DeleteDomain mocks base method. func (m *MockDomainStore) DeleteDomain(ctx context.Context, request *DeleteDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteDomain", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteDomain indicates an expected call of DeleteDomain. func (mr *MockDomainStoreMockRecorder) DeleteDomain(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDomain", reflect.TypeOf((*MockDomainStore)(nil).DeleteDomain), ctx, request) } // DeleteDomainByName mocks base method. func (m *MockDomainStore) DeleteDomainByName(ctx context.Context, request *DeleteDomainByNameRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteDomainByName", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteDomainByName indicates an expected call of DeleteDomainByName. func (mr *MockDomainStoreMockRecorder) DeleteDomainByName(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDomainByName", reflect.TypeOf((*MockDomainStore)(nil).DeleteDomainByName), ctx, request) } // GetDomain mocks base method. func (m *MockDomainStore) GetDomain(ctx context.Context, request *GetDomainRequest) (*InternalGetDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomain", ctx, request) ret0, _ := ret[0].(*InternalGetDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomain indicates an expected call of GetDomain. func (mr *MockDomainStoreMockRecorder) GetDomain(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomain", reflect.TypeOf((*MockDomainStore)(nil).GetDomain), ctx, request) } // GetMetadata mocks base method. func (m *MockDomainStore) GetMetadata(ctx context.Context) (*GetMetadataResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMetadata", ctx) ret0, _ := ret[0].(*GetMetadataResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMetadata indicates an expected call of GetMetadata. func (mr *MockDomainStoreMockRecorder) GetMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetadata", reflect.TypeOf((*MockDomainStore)(nil).GetMetadata), ctx) } // GetName mocks base method. func (m *MockDomainStore) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockDomainStoreMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockDomainStore)(nil).GetName)) } // ListDomains mocks base method. func (m *MockDomainStore) ListDomains(ctx context.Context, request *ListDomainsRequest) (*InternalListDomainsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListDomains", ctx, request) ret0, _ := ret[0].(*InternalListDomainsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListDomains indicates an expected call of ListDomains. func (mr *MockDomainStoreMockRecorder) ListDomains(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDomains", reflect.TypeOf((*MockDomainStore)(nil).ListDomains), ctx, request) } // UpdateDomain mocks base method. func (m *MockDomainStore) UpdateDomain(ctx context.Context, request *InternalUpdateDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomain", ctx, request) ret0, _ := ret[0].(error) return ret0 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MockDomainStoreMockRecorder) UpdateDomain(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MockDomainStore)(nil).UpdateDomain), ctx, request) } // MockTaskStore is a mock of TaskStore interface. type MockTaskStore struct { ctrl *gomock.Controller recorder *MockTaskStoreMockRecorder isgomock struct{} } // MockTaskStoreMockRecorder is the mock recorder for MockTaskStore. type MockTaskStoreMockRecorder struct { mock *MockTaskStore } // NewMockTaskStore creates a new mock instance. func NewMockTaskStore(ctrl *gomock.Controller) *MockTaskStore { mock := &MockTaskStore{ctrl: ctrl} mock.recorder = &MockTaskStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskStore) EXPECT() *MockTaskStoreMockRecorder { return m.recorder } // Close mocks base method. func (m *MockTaskStore) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockTaskStoreMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockTaskStore)(nil).Close)) } // CompleteTask mocks base method. func (m *MockTaskStore) CompleteTask(ctx context.Context, request *CompleteTaskRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CompleteTask", ctx, request) ret0, _ := ret[0].(error) return ret0 } // CompleteTask indicates an expected call of CompleteTask. func (mr *MockTaskStoreMockRecorder) CompleteTask(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompleteTask", reflect.TypeOf((*MockTaskStore)(nil).CompleteTask), ctx, request) } // CompleteTasksLessThan mocks base method. func (m *MockTaskStore) CompleteTasksLessThan(ctx context.Context, request *CompleteTasksLessThanRequest) (*CompleteTasksLessThanResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CompleteTasksLessThan", ctx, request) ret0, _ := ret[0].(*CompleteTasksLessThanResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CompleteTasksLessThan indicates an expected call of CompleteTasksLessThan. func (mr *MockTaskStoreMockRecorder) CompleteTasksLessThan(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompleteTasksLessThan", reflect.TypeOf((*MockTaskStore)(nil).CompleteTasksLessThan), ctx, request) } // CreateTasks mocks base method. func (m *MockTaskStore) CreateTasks(ctx context.Context, request *CreateTasksRequest) (*CreateTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateTasks", ctx, request) ret0, _ := ret[0].(*CreateTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateTasks indicates an expected call of CreateTasks. func (mr *MockTaskStoreMockRecorder) CreateTasks(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTasks", reflect.TypeOf((*MockTaskStore)(nil).CreateTasks), ctx, request) } // DeleteTaskList mocks base method. func (m *MockTaskStore) DeleteTaskList(ctx context.Context, request *DeleteTaskListRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTaskList", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteTaskList indicates an expected call of DeleteTaskList. func (mr *MockTaskStoreMockRecorder) DeleteTaskList(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTaskList", reflect.TypeOf((*MockTaskStore)(nil).DeleteTaskList), ctx, request) } // GetName mocks base method. func (m *MockTaskStore) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockTaskStoreMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockTaskStore)(nil).GetName)) } // GetOrphanTasks mocks base method. func (m *MockTaskStore) GetOrphanTasks(ctx context.Context, request *GetOrphanTasksRequest) (*GetOrphanTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrphanTasks", ctx, request) ret0, _ := ret[0].(*GetOrphanTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetOrphanTasks indicates an expected call of GetOrphanTasks. func (mr *MockTaskStoreMockRecorder) GetOrphanTasks(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrphanTasks", reflect.TypeOf((*MockTaskStore)(nil).GetOrphanTasks), ctx, request) } // GetTaskList mocks base method. func (m *MockTaskStore) GetTaskList(ctx context.Context, request *GetTaskListRequest) (*GetTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskList", ctx, request) ret0, _ := ret[0].(*GetTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTaskList indicates an expected call of GetTaskList. func (mr *MockTaskStoreMockRecorder) GetTaskList(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskList", reflect.TypeOf((*MockTaskStore)(nil).GetTaskList), ctx, request) } // GetTaskListSize mocks base method. func (m *MockTaskStore) GetTaskListSize(ctx context.Context, request *GetTaskListSizeRequest) (*GetTaskListSizeResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskListSize", ctx, request) ret0, _ := ret[0].(*GetTaskListSizeResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTaskListSize indicates an expected call of GetTaskListSize. func (mr *MockTaskStoreMockRecorder) GetTaskListSize(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskListSize", reflect.TypeOf((*MockTaskStore)(nil).GetTaskListSize), ctx, request) } // GetTasks mocks base method. func (m *MockTaskStore) GetTasks(ctx context.Context, request *GetTasksRequest) (*GetTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasks", ctx, request) ret0, _ := ret[0].(*GetTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTasks indicates an expected call of GetTasks. func (mr *MockTaskStoreMockRecorder) GetTasks(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasks", reflect.TypeOf((*MockTaskStore)(nil).GetTasks), ctx, request) } // LeaseTaskList mocks base method. func (m *MockTaskStore) LeaseTaskList(ctx context.Context, request *LeaseTaskListRequest) (*LeaseTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LeaseTaskList", ctx, request) ret0, _ := ret[0].(*LeaseTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // LeaseTaskList indicates an expected call of LeaseTaskList. func (mr *MockTaskStoreMockRecorder) LeaseTaskList(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LeaseTaskList", reflect.TypeOf((*MockTaskStore)(nil).LeaseTaskList), ctx, request) } // ListTaskList mocks base method. func (m *MockTaskStore) ListTaskList(ctx context.Context, request *ListTaskListRequest) (*ListTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTaskList", ctx, request) ret0, _ := ret[0].(*ListTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTaskList indicates an expected call of ListTaskList. func (mr *MockTaskStoreMockRecorder) ListTaskList(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTaskList", reflect.TypeOf((*MockTaskStore)(nil).ListTaskList), ctx, request) } // UpdateTaskList mocks base method. func (m *MockTaskStore) UpdateTaskList(ctx context.Context, request *UpdateTaskListRequest) (*UpdateTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskList", ctx, request) ret0, _ := ret[0].(*UpdateTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskList indicates an expected call of UpdateTaskList. func (mr *MockTaskStoreMockRecorder) UpdateTaskList(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskList", reflect.TypeOf((*MockTaskStore)(nil).UpdateTaskList), ctx, request) } // MockHistoryStore is a mock of HistoryStore interface. type MockHistoryStore struct { ctrl *gomock.Controller recorder *MockHistoryStoreMockRecorder isgomock struct{} } // MockHistoryStoreMockRecorder is the mock recorder for MockHistoryStore. type MockHistoryStoreMockRecorder struct { mock *MockHistoryStore } // NewMockHistoryStore creates a new mock instance. func NewMockHistoryStore(ctrl *gomock.Controller) *MockHistoryStore { mock := &MockHistoryStore{ctrl: ctrl} mock.recorder = &MockHistoryStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHistoryStore) EXPECT() *MockHistoryStoreMockRecorder { return m.recorder } // AppendHistoryNodes mocks base method. func (m *MockHistoryStore) AppendHistoryNodes(ctx context.Context, request *InternalAppendHistoryNodesRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AppendHistoryNodes", ctx, request) ret0, _ := ret[0].(error) return ret0 } // AppendHistoryNodes indicates an expected call of AppendHistoryNodes. func (mr *MockHistoryStoreMockRecorder) AppendHistoryNodes(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendHistoryNodes", reflect.TypeOf((*MockHistoryStore)(nil).AppendHistoryNodes), ctx, request) } // Close mocks base method. func (m *MockHistoryStore) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockHistoryStoreMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockHistoryStore)(nil).Close)) } // DeleteHistoryBranch mocks base method. func (m *MockHistoryStore) DeleteHistoryBranch(ctx context.Context, request *InternalDeleteHistoryBranchRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteHistoryBranch", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteHistoryBranch indicates an expected call of DeleteHistoryBranch. func (mr *MockHistoryStoreMockRecorder) DeleteHistoryBranch(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteHistoryBranch", reflect.TypeOf((*MockHistoryStore)(nil).DeleteHistoryBranch), ctx, request) } // ForkHistoryBranch mocks base method. func (m *MockHistoryStore) ForkHistoryBranch(ctx context.Context, request *InternalForkHistoryBranchRequest) (*InternalForkHistoryBranchResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ForkHistoryBranch", ctx, request) ret0, _ := ret[0].(*InternalForkHistoryBranchResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ForkHistoryBranch indicates an expected call of ForkHistoryBranch. func (mr *MockHistoryStoreMockRecorder) ForkHistoryBranch(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForkHistoryBranch", reflect.TypeOf((*MockHistoryStore)(nil).ForkHistoryBranch), ctx, request) } // GetAllHistoryTreeBranches mocks base method. func (m *MockHistoryStore) GetAllHistoryTreeBranches(ctx context.Context, request *GetAllHistoryTreeBranchesRequest) (*GetAllHistoryTreeBranchesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllHistoryTreeBranches", ctx, request) ret0, _ := ret[0].(*GetAllHistoryTreeBranchesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAllHistoryTreeBranches indicates an expected call of GetAllHistoryTreeBranches. func (mr *MockHistoryStoreMockRecorder) GetAllHistoryTreeBranches(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllHistoryTreeBranches", reflect.TypeOf((*MockHistoryStore)(nil).GetAllHistoryTreeBranches), ctx, request) } // GetHistoryTree mocks base method. func (m *MockHistoryStore) GetHistoryTree(ctx context.Context, request *InternalGetHistoryTreeRequest) (*InternalGetHistoryTreeResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryTree", ctx, request) ret0, _ := ret[0].(*InternalGetHistoryTreeResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHistoryTree indicates an expected call of GetHistoryTree. func (mr *MockHistoryStoreMockRecorder) GetHistoryTree(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryTree", reflect.TypeOf((*MockHistoryStore)(nil).GetHistoryTree), ctx, request) } // GetName mocks base method. func (m *MockHistoryStore) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockHistoryStoreMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockHistoryStore)(nil).GetName)) } // ReadHistoryBranch mocks base method. func (m *MockHistoryStore) ReadHistoryBranch(ctx context.Context, request *InternalReadHistoryBranchRequest) (*InternalReadHistoryBranchResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadHistoryBranch", ctx, request) ret0, _ := ret[0].(*InternalReadHistoryBranchResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadHistoryBranch indicates an expected call of ReadHistoryBranch. func (mr *MockHistoryStoreMockRecorder) ReadHistoryBranch(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadHistoryBranch", reflect.TypeOf((*MockHistoryStore)(nil).ReadHistoryBranch), ctx, request) } // MockConfigStore is a mock of ConfigStore interface. type MockConfigStore struct { ctrl *gomock.Controller recorder *MockConfigStoreMockRecorder isgomock struct{} } // MockConfigStoreMockRecorder is the mock recorder for MockConfigStore. type MockConfigStoreMockRecorder struct { mock *MockConfigStore } // NewMockConfigStore creates a new mock instance. func NewMockConfigStore(ctrl *gomock.Controller) *MockConfigStore { mock := &MockConfigStore{ctrl: ctrl} mock.recorder = &MockConfigStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockConfigStore) EXPECT() *MockConfigStoreMockRecorder { return m.recorder } // Close mocks base method. func (m *MockConfigStore) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockConfigStoreMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockConfigStore)(nil).Close)) } // FetchConfig mocks base method. func (m *MockConfigStore) FetchConfig(ctx context.Context, configType ConfigType) (*InternalConfigStoreEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FetchConfig", ctx, configType) ret0, _ := ret[0].(*InternalConfigStoreEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // FetchConfig indicates an expected call of FetchConfig. func (mr *MockConfigStoreMockRecorder) FetchConfig(ctx, configType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchConfig", reflect.TypeOf((*MockConfigStore)(nil).FetchConfig), ctx, configType) } // UpdateConfig mocks base method. func (m *MockConfigStore) UpdateConfig(ctx context.Context, value *InternalConfigStoreEntry) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateConfig", ctx, value) ret0, _ := ret[0].(error) return ret0 } // UpdateConfig indicates an expected call of UpdateConfig. func (mr *MockConfigStoreMockRecorder) UpdateConfig(ctx, value any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateConfig", reflect.TypeOf((*MockConfigStore)(nil).UpdateConfig), ctx, value) } // MockDomainAuditStore is a mock of DomainAuditStore interface. type MockDomainAuditStore struct { ctrl *gomock.Controller recorder *MockDomainAuditStoreMockRecorder isgomock struct{} } // MockDomainAuditStoreMockRecorder is the mock recorder for MockDomainAuditStore. type MockDomainAuditStoreMockRecorder struct { mock *MockDomainAuditStore } // NewMockDomainAuditStore creates a new mock instance. func NewMockDomainAuditStore(ctrl *gomock.Controller) *MockDomainAuditStore { mock := &MockDomainAuditStore{ctrl: ctrl} mock.recorder = &MockDomainAuditStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDomainAuditStore) EXPECT() *MockDomainAuditStoreMockRecorder { return m.recorder } // Close mocks base method. func (m *MockDomainAuditStore) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockDomainAuditStoreMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDomainAuditStore)(nil).Close)) } // CreateDomainAuditLog mocks base method. func (m *MockDomainAuditStore) CreateDomainAuditLog(ctx context.Context, request *InternalCreateDomainAuditLogRequest) (*CreateDomainAuditLogResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateDomainAuditLog", ctx, request) ret0, _ := ret[0].(*CreateDomainAuditLogResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateDomainAuditLog indicates an expected call of CreateDomainAuditLog. func (mr *MockDomainAuditStoreMockRecorder) CreateDomainAuditLog(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDomainAuditLog", reflect.TypeOf((*MockDomainAuditStore)(nil).CreateDomainAuditLog), ctx, request) } // GetDomainAuditLogs mocks base method. func (m *MockDomainAuditStore) GetDomainAuditLogs(ctx context.Context, request *GetDomainAuditLogsRequest) (*InternalGetDomainAuditLogsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainAuditLogs", ctx, request) ret0, _ := ret[0].(*InternalGetDomainAuditLogsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainAuditLogs indicates an expected call of GetDomainAuditLogs. func (mr *MockDomainAuditStoreMockRecorder) GetDomainAuditLogs(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainAuditLogs", reflect.TypeOf((*MockDomainAuditStore)(nil).GetDomainAuditLogs), ctx, request) } // GetName mocks base method. func (m *MockDomainAuditStore) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockDomainAuditStoreMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockDomainAuditStore)(nil).GetName)) } ================================================ FILE: common/persistence/data_store_interfaces_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) func TestDataBlob(t *testing.T) { t.Run("NewDataBlob", func(t *testing.T) { assert.Nil(t, NewDataBlob(nil, "anything"), "nil data should become nil blob") data, encoding := []byte("something"), constants.EncodingTypeJSON assert.EqualValues(t, &DataBlob{ Data: data, Encoding: encoding, }, NewDataBlob(data, encoding)) }) t.Run("Y-prefixed data may panic", func(t *testing.T) { allEncodings := []constants.EncodingType{ constants.EncodingTypeJSON, constants.EncodingTypeThriftRW, constants.EncodingTypeGob, constants.EncodingTypeUnknown, constants.EncodingTypeEmpty, constants.EncodingTypeProto, } const problematic = "Y..." // I'm not entirely sure why this behavior exists, but to nail it down: assert.NotPanics(t, func() { NewDataBlob([]byte(problematic), constants.EncodingTypeThriftRW) }, "only thriftrw data can start with Y without panicking") // all others panic for _, encoding := range allEncodings { if encoding == constants.EncodingTypeThriftRW { continue // handled above } assert.Panicsf(t, func() { NewDataBlob([]byte(problematic), encoding) }, "non-thriftrw %v should panic if first byte is Y", encoding) } // make sure other data/encoding combinations do not panic for any encoding for _, dat := range []string{"Z...", "other"} { for _, encoding := range allEncodings { assert.NotPanicsf(t, func() { NewDataBlob([]byte(dat), encoding) }, "should not panic with %q on encoding %q", dat, encoding) } } }) t.Run("FromDataBlob", func(t *testing.T) { assertEmpty := func(b []byte, s string) { assert.Nil(t, b, "should have nil bytes") assert.Empty(t, s, "should have empty string") } assertEmpty(FromDataBlob(nil)) assertEmpty(FromDataBlob(&DataBlob{})) assertEmpty(FromDataBlob(&DataBlob{Encoding: constants.EncodingTypeThriftRW})) dat, str := FromDataBlob(&DataBlob{ Data: []byte("data"), Encoding: constants.EncodingTypeJSON, }) assert.Equal(t, []byte("data"), dat, "data should be returned") assert.Equal(t, string(constants.EncodingTypeJSON), str, "encoding should be returned as a string") }) t.Run("ToNilSafeDataBlob", func(t *testing.T) { orig := &DataBlob{Encoding: constants.EncodingTypeGob} // anything not empty assert.Equal(t, orig, orig.ToNilSafeDataBlob()) assert.NotNil(t, (*DataBlob)(nil).ToNilSafeDataBlob(), "typed nils should convert to non-nils") }) t.Run("GetEncodingString", func(t *testing.T) { assert.Equal(t, "", (*DataBlob)(nil).GetEncodingString()) assert.Equal(t, "test", (&DataBlob{Encoding: "test"}).GetEncodingString()) }) t.Run("GetData", func(t *testing.T) { // this method returns empty slices, not nils. // I'm not sure if this needs to be maintained, but it must be checked before changing. assert.Equal(t, []byte{}, (*DataBlob)(nil).GetData()) assert.Equal(t, []byte{}, (&DataBlob{}).GetData()) assert.Equal(t, []byte{}, (&DataBlob{Data: []byte{}}).GetData()) assert.Equal(t, []byte("test"), (&DataBlob{Data: []byte("test")}).GetData()) }) t.Run("GetEncoding", func(t *testing.T) { same := func(encoding constants.EncodingType) { assert.Equal(t, encoding, (&DataBlob{Encoding: encoding}).GetEncoding()) } unknown := func(encoding constants.EncodingType) { assert.Equal(t, constants.EncodingTypeUnknown, (&DataBlob{Encoding: encoding}).GetEncoding()) } // obvious same(constants.EncodingTypeGob) same(constants.EncodingTypeJSON) same(constants.EncodingTypeThriftRW) same(constants.EncodingTypeEmpty) // highly suspicious unknown(constants.EncodingTypeProto) // should be unknown unknown(constants.EncodingTypeUnknown) unknown("any other value") }) t.Run("to and from internal", func(t *testing.T) { data := []byte("some data") t.Run("supported types", func(t *testing.T) { check := func(t *testing.T, encodingType types.EncodingType, encodingCommon constants.EncodingType) { internal := &types.DataBlob{ EncodingType: encodingType.Ptr(), Data: data, } blob := &DataBlob{ Encoding: encodingCommon, Data: data, } assert.Equalf(t, internal, blob.ToInternal(), "%v should encode to internal type %v", encodingCommon, encodingType) assert.Equalf(t, blob, NewDataBlobFromInternal(internal), "%v should decode from internal type %v", encodingCommon, encodingType) // likely proven by above, but to be explicit: this type should round-trip without losing data. assert.Equalf(t, blob, NewDataBlobFromInternal(blob.ToInternal()), "%v should round trip from blob %v", encodingCommon, encodingType) assert.Equalf(t, internal, NewDataBlobFromInternal(internal).ToInternal(), "%v should round trip from internal %v", encodingCommon, encodingType) } t.Run("json", func(t *testing.T) { check(t, types.EncodingTypeJSON, constants.EncodingTypeJSON) }) t.Run("thriftrw", func(t *testing.T) { check(t, types.EncodingTypeThriftRW, constants.EncodingTypeThriftRW) }) }) t.Run("other known encodings panic to internal", func(t *testing.T) { for _, encoding := range []constants.EncodingType{ constants.EncodingTypeUnknown, constants.EncodingTypeProto, constants.EncodingTypeGob, constants.EncodingTypeEmpty, "any other value", } { assert.Panicsf(t, func() { (&DataBlob{ Encoding: encoding, Data: data, }).ToInternal() }, "should panic when encoding to unhandled encoding %q", encoding) } }) t.Run("unknown encodings panic from internal", func(t *testing.T) { // these two are known, any other value should panic. // // the easy and most-likely-to-catch-changes strategy is to just add 1, // so a new supported type will automatically fail here until both // the code and tests are updated. unknownType := 1 + max(types.EncodingTypeJSON, types.EncodingTypeThriftRW) assert.Panicsf(t, func() { NewDataBlobFromInternal(&types.DataBlob{ EncodingType: &unknownType, Data: data, }) }, "should panic when decoding from unhandled encoding %q", unknownType) }) }) } ================================================ FILE: common/persistence/domain_audit_log.go ================================================ package persistence import ( "sort" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) // ToFailoverEvent maps a DomainAuditLog to a FailoverEvent // by looking at the failover changes and only listing the differences. // Active clusters or cluster-attributes which are unchanged will not be listed as a failover event. // Since one DomainAuditLog can contain multiple failover changes, this function will return a // list of clusterFailver entries for each change. However, it repressents a single atomic // change in the domain func (auditLog *DomainAuditLog) ToFailoverEvents() *types.FailoverEvent { var clusterFailovers []*types.ClusterFailover // we're comparing the active cluster name rather than the failvoer version () // because cluster-attribute updates are a noop bump, to filter out some noise the domain-level active // cluster will only be reported if it changes if auditLog.StateBefore.GetReplicationConfig().GetActiveClusterName() != auditLog.StateAfter.GetReplicationConfig().GetActiveClusterName() { clusterFailovers = append(clusterFailovers, &types.ClusterFailover{ FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: auditLog.StateBefore.GetReplicationConfig().GetActiveClusterName(), FailoverVersion: auditLog.StateBefore.GetFailoverVersion(), }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: auditLog.StateAfter.GetReplicationConfig().GetActiveClusterName(), FailoverVersion: auditLog.StateAfter.GetFailoverVersion(), }, }) } clusterAttrBefore := auditLog.StateBefore.GetReplicationConfig().GetClusterAttributeScopes() clusterAttrAfter := auditLog.StateAfter.GetReplicationConfig().GetClusterAttributeScopes() // Sort keys to ensure deterministic ordering sortedScopeKeysAfter := make([]string, 0, len(clusterAttrAfter)) for scopeKey := range clusterAttrAfter { sortedScopeKeysAfter = append(sortedScopeKeysAfter, scopeKey) } sort.Strings(sortedScopeKeysAfter) // a scope is thing like 'region' or 'city' for _, scopeKeyAfter := range sortedScopeKeysAfter { vAfter := clusterAttrAfter[scopeKeyAfter] vBefore, ok := clusterAttrBefore[scopeKeyAfter] if !ok { // if the scope is not even defined in the before state, it's entirely new, this is its introduction clusterFailovers = append(clusterFailovers, auditLog.mapNewClusterAttributeToClusterFailover(scopeKeyAfter, &vAfter)...) continue } clusterFailovers = append(clusterFailovers, auditLog.mapClusterAttributeChangeToClusterFailover(scopeKeyAfter, &vBefore, &vAfter)...) } // Sort keys for deterministic ordering sortedScopeKeysBefore := make([]string, 0, len(clusterAttrBefore)) for scopeKey := range clusterAttrBefore { sortedScopeKeysBefore = append(sortedScopeKeysBefore, scopeKey) } sort.Strings(sortedScopeKeysBefore) for _, scopeKeyBefore := range sortedScopeKeysBefore { vBefore := clusterAttrBefore[scopeKeyBefore] _, ok := clusterAttrAfter[scopeKeyBefore] if !ok { // if the scope is not even defined in the after state, it's entirely removed, this is its removal clusterFailovers = append(clusterFailovers, auditLog.mapRemovedClusterAttributeToClusterFailover(scopeKeyBefore, &vBefore)...) continue } } failoverType := types.FailoverTypeForce if auditLog.StateAfter.FailoverEndTime != nil { failoverType = types.FailoverTypeGraceful } return &types.FailoverEvent{ ID: &auditLog.EventID, CreatedTime: common.Ptr(auditLog.CreatedTime.UnixNano()), ClusterFailovers: clusterFailovers, FailoverType: &failoverType, } } // for when there's only a new cluster attribute introduced func (auditLog *DomainAuditLog) mapNewClusterAttributeToClusterFailover(scopeKey string, vAfter *types.ClusterAttributeScope) []*types.ClusterFailover { if vAfter == nil && scopeKey == "" { return nil } var clusterFailovers []*types.ClusterFailover sortedNames := make([]string, 0, len(vAfter.ClusterAttributes)) for name := range vAfter.ClusterAttributes { sortedNames = append(sortedNames, name) } sort.Strings(sortedNames) for _, name := range sortedNames { v := vAfter.ClusterAttributes[name] clusterFailovers = append(clusterFailovers, &types.ClusterFailover{ ClusterAttribute: &types.ClusterAttribute{ Scope: scopeKey, Name: name, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: v.ActiveClusterName, FailoverVersion: v.FailoverVersion, }, FromCluster: nil, }) } return clusterFailovers } // for when a cluster attribute is removed func (auditLog *DomainAuditLog) mapRemovedClusterAttributeToClusterFailover(scopeKey string, vBefore *types.ClusterAttributeScope) []*types.ClusterFailover { if vBefore == nil && scopeKey == "" { return nil } var clusterFailovers []*types.ClusterFailover sortedNames := make([]string, 0, len(vBefore.ClusterAttributes)) for name := range vBefore.ClusterAttributes { sortedNames = append(sortedNames, name) } sort.Strings(sortedNames) for _, name := range sortedNames { v := vBefore.ClusterAttributes[name] clusterFailovers = append(clusterFailovers, &types.ClusterFailover{ ClusterAttribute: &types.ClusterAttribute{ Scope: scopeKey, Name: name, }, FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: v.ActiveClusterName, FailoverVersion: v.FailoverVersion, }, ToCluster: nil, }) } return clusterFailovers } // for comparing if any cluster attributes have changed within a scope // will return nil if none are changed // assumes that the cluster-attr scope is already checked to exist in both before and after states func (auditLog *DomainAuditLog) mapClusterAttributeChangeToClusterFailover(scopeKey string, vBefore, vAfter *types.ClusterAttributeScope) []*types.ClusterFailover { var clusterFailovers []*types.ClusterFailover // Sort keys to ensure deterministic ordering sortedNamesAfter := make([]string, 0, len(vAfter.ClusterAttributes)) for name := range vAfter.ClusterAttributes { sortedNamesAfter = append(sortedNamesAfter, name) } sort.Strings(sortedNamesAfter) for _, name := range sortedNamesAfter { vAfterAttr := vAfter.ClusterAttributes[name] vBeforeAttr, ok := vBefore.ClusterAttributes[name] if !ok { // new cluster attribute clusterFailovers = append(clusterFailovers, &types.ClusterFailover{ ClusterAttribute: &types.ClusterAttribute{ Scope: scopeKey, Name: name, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: vAfterAttr.ActiveClusterName, FailoverVersion: vAfterAttr.FailoverVersion, }, FromCluster: nil, }) continue } // if the cluster attribute has changed if vBeforeAttr.FailoverVersion != vAfterAttr.FailoverVersion { clusterFailovers = append(clusterFailovers, &types.ClusterFailover{ ClusterAttribute: &types.ClusterAttribute{ Scope: scopeKey, Name: name, }, FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: vBeforeAttr.ActiveClusterName, FailoverVersion: vBeforeAttr.FailoverVersion, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: vAfterAttr.ActiveClusterName, FailoverVersion: vAfterAttr.FailoverVersion, }, }) } } sortedNamesBefore := make([]string, 0, len(vBefore.ClusterAttributes)) for name := range vBefore.ClusterAttributes { sortedNamesBefore = append(sortedNamesBefore, name) } sort.Strings(sortedNamesBefore) for _, name := range sortedNamesBefore { vBeforeAttr := vBefore.ClusterAttributes[name] _, ok := vAfter.ClusterAttributes[name] if !ok { // removed cluster attribute clusterFailovers = append(clusterFailovers, &types.ClusterFailover{ ClusterAttribute: &types.ClusterAttribute{ Scope: scopeKey, Name: name, }, FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: vBeforeAttr.ActiveClusterName, FailoverVersion: vBeforeAttr.FailoverVersion, }, ToCluster: nil, }) continue } } return clusterFailovers } ================================================ FILE: common/persistence/domain_audit_log_test.go ================================================ package persistence import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) func TestDomainAuditLog_ToFailoverEvents(t *testing.T) { now := time.Unix(1234567890, 0) tests := map[string]struct { auditLog *DomainAuditLog expected *types.FailoverEvent }{ "simple active cluster failover": { auditLog: &DomainAuditLog{ EventID: "event-1", DomainID: "domain-1", CreatedTime: now, StateBefore: &GetDomainResponse{ Info: &DomainInfo{ ID: "domain-1", Name: "test-domain", }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-us-east", Clusters: []*ClusterReplicationConfig{ {ClusterName: "cluster-us-east"}, {ClusterName: "cluster-us-west"}, }, }, FailoverVersion: 100, }, StateAfter: &GetDomainResponse{ Info: &DomainInfo{ ID: "domain-1", Name: "test-domain", }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-us-west", Clusters: []*ClusterReplicationConfig{ {ClusterName: "cluster-us-east"}, {ClusterName: "cluster-us-west"}, }, }, FailoverVersion: 101, }, OperationType: DomainAuditOperationTypeFailover, }, expected: &types.FailoverEvent{ ID: stringPtr("event-1"), CreatedTime: int64Ptr(now.UnixNano()), FailoverType: types.FailoverTypeForce.Ptr(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us-east", FailoverVersion: 100, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us-west", FailoverVersion: 101, }, }, }, }, }, "cluster attribute failover - single region change": { auditLog: &DomainAuditLog{ EventID: "event-2", DomainID: "active-active-domain", CreatedTime: now, StateBefore: &GetDomainResponse{ Info: &DomainInfo{ ID: "domain-2", Name: "active-active-domain", }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-default", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster0", FailoverVersion: 200}, "us-west": {ActiveClusterName: "cluster1", FailoverVersion: 201}, }, }, }, }, }, FailoverVersion: 200, }, StateAfter: &GetDomainResponse{ Info: &DomainInfo{ ID: "domain-2", Name: "active-active-domain", }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-default", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster1", FailoverVersion: 201}, "us-west": {ActiveClusterName: "cluster1", FailoverVersion: 201}, }, }, }, }, }, FailoverVersion: 201, }, OperationType: DomainAuditOperationTypeFailover, }, expected: &types.FailoverEvent{ ID: stringPtr("event-2"), CreatedTime: int64Ptr(now.UnixNano()), FailoverType: types.FailoverTypeForce.Ptr(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster0", FailoverVersion: 200, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster1", FailoverVersion: 201, }, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, }, }, }, }, "cluster attribute failover - multiple regions change": { auditLog: &DomainAuditLog{ EventID: "event-3", DomainID: "domain-3", CreatedTime: now, StateBefore: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-default", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region-1": {ActiveClusterName: "cluster-us-east", FailoverVersion: 300}, "region-2": {ActiveClusterName: "cluster-us-west", FailoverVersion: 301}, "region-3": {ActiveClusterName: "cluster-eu-central", FailoverVersion: 302}, }, }, }, }, }, FailoverVersion: 300, }, StateAfter: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-default", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region-1": {ActiveClusterName: "cluster-eu-central", FailoverVersion: 302}, "region-2": {ActiveClusterName: "cluster-eu-central", FailoverVersion: 302}, "region-3": {ActiveClusterName: "cluster-eu-central", FailoverVersion: 302}, }, }, }, }, }, FailoverVersion: 400, }, OperationType: DomainAuditOperationTypeFailover, }, expected: &types.FailoverEvent{ ID: stringPtr("event-3"), CreatedTime: int64Ptr(now.UnixNano()), FailoverType: types.FailoverTypeForce.Ptr(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us-east", FailoverVersion: 300, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-eu-central", FailoverVersion: 302, }, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "region-1", }, }, { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us-west", FailoverVersion: 301, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-eu-central", FailoverVersion: 302, }, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "region-2", }, }, }, }, }, "combined failover - both active cluster and attributes": { auditLog: &DomainAuditLog{ EventID: "event-4", DomainID: "domain-4", CreatedTime: now, StateBefore: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-primary", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us": {ActiveClusterName: "cluster-us", FailoverVersion: 400}, }, }, }, }, }, FailoverVersion: 400, }, StateAfter: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-secondary", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us": {ActiveClusterName: "cluster-us-backup", FailoverVersion: 401}, }, }, }, }, }, FailoverVersion: 401, }, OperationType: DomainAuditOperationTypeFailover, }, expected: &types.FailoverEvent{ ID: stringPtr("event-4"), CreatedTime: int64Ptr(now.UnixNano()), FailoverType: types.FailoverTypeForce.Ptr(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-primary", FailoverVersion: 400, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-secondary", FailoverVersion: 401, }, }, { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us", FailoverVersion: 400, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us-backup", FailoverVersion: 401, }, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us", }, }, }, }, }, "new cluster attribute scope added": { auditLog: &DomainAuditLog{ EventID: "event-5", DomainID: "domain-5", CreatedTime: now, StateBefore: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-default", }, FailoverVersion: 500, }, StateAfter: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-default", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster-us-east", FailoverVersion: 501}, "eu": {ActiveClusterName: "cluster-eu", FailoverVersion: 502}, }, }, }, }, }, FailoverVersion: 502, }, OperationType: DomainAuditOperationTypeFailover, }, expected: &types.FailoverEvent{ ID: stringPtr("event-5"), CreatedTime: int64Ptr(now.UnixNano()), FailoverType: types.FailoverTypeForce.Ptr(), ClusterFailovers: []*types.ClusterFailover{ { ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-eu", FailoverVersion: 502, }, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "eu", }, }, { ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us-east", FailoverVersion: 501, }, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, }, }, }, }, "no changes - same active cluster": { auditLog: &DomainAuditLog{ EventID: "event-6", DomainID: "domain-6", CreatedTime: now, StateBefore: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-stable", }, FailoverVersion: 600, }, StateAfter: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-stable", }, FailoverVersion: 600, }, OperationType: DomainAuditOperationTypeFailover, }, expected: &types.FailoverEvent{ ID: stringPtr("event-6"), CreatedTime: int64Ptr(now.UnixNano()), FailoverType: types.FailoverTypeForce.Ptr(), ClusterFailovers: nil, }, }, "nil replication config": { auditLog: &DomainAuditLog{ EventID: "event-7", DomainID: "domain-7", CreatedTime: now, StateBefore: &GetDomainResponse{}, StateAfter: &GetDomainResponse{}, OperationType: DomainAuditOperationTypeFailover, }, expected: &types.FailoverEvent{ ID: stringPtr("event-7"), FailoverType: types.FailoverTypeForce.Ptr(), CreatedTime: int64Ptr(now.UnixNano()), ClusterFailovers: nil, }, }, "multiple scopes with changes": { auditLog: &DomainAuditLog{ EventID: "event-8", DomainID: "domain-8", CreatedTime: now, StateBefore: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-default", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us": {ActiveClusterName: "cluster-us-1", FailoverVersion: 700}, }, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": {ActiveClusterName: "cluster-dc1-primary", FailoverVersion: 700}, }, }, }, }, }, FailoverVersion: 700, }, StateAfter: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-default", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us": {ActiveClusterName: "cluster-us-2", FailoverVersion: 701}, }, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": {ActiveClusterName: "cluster-dc1-backup", FailoverVersion: 701}, }, }, }, }, }, FailoverVersion: 701, }, OperationType: DomainAuditOperationTypeFailover, }, expected: &types.FailoverEvent{ ID: stringPtr("event-8"), CreatedTime: int64Ptr(now.UnixNano()), FailoverType: types.FailoverTypeForce.Ptr(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-dc1-primary", FailoverVersion: 700, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-dc1-backup", FailoverVersion: 701, }, ClusterAttribute: &types.ClusterAttribute{ Scope: "datacenter", Name: "dc1", }, }, { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us-1", FailoverVersion: 700, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us-2", FailoverVersion: 701, }, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us", }, }, }, }, }, "cluster attribute scope removed": { auditLog: &DomainAuditLog{ EventID: "event-9", DomainID: "domain-9", CreatedTime: now, StateBefore: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-default", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster-us-east", FailoverVersion: 800}, "us-west": {ActiveClusterName: "cluster-us-west", FailoverVersion: 801}, }, }, }, }, }, FailoverVersion: 800, }, StateAfter: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-default", }, FailoverVersion: 801, }, OperationType: DomainAuditOperationTypeFailover, }, expected: &types.FailoverEvent{ ID: stringPtr("event-9"), CreatedTime: int64Ptr(now.UnixNano()), FailoverType: types.FailoverTypeForce.Ptr(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us-east", FailoverVersion: 800, }, ToCluster: nil, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, }, { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "cluster-us-west", FailoverVersion: 801, }, ToCluster: nil, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, }, }, }, }, "graceful failover": { auditLog: &DomainAuditLog{ EventID: "event-9", DomainID: "domain-9", CreatedTime: now, StateBefore: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "region-us-east", }, FailoverVersion: 800, }, StateAfter: &GetDomainResponse{ ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "region-us-west", }, FailoverVersion: 801, FailoverEndTime: int64Ptr(now.Add(1 * time.Hour).UnixNano()), }, OperationType: DomainAuditOperationTypeFailover, }, expected: &types.FailoverEvent{ ID: stringPtr("event-9"), CreatedTime: int64Ptr(now.UnixNano()), FailoverType: types.FailoverTypeGraceful.Ptr(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ ActiveClusterName: "region-us-east", FailoverVersion: 800, }, ToCluster: &types.ActiveClusterInfo{ ActiveClusterName: "region-us-west", FailoverVersion: 801, }, }, }, }, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { actual := tc.auditLog.ToFailoverEvents() assert.Equal(t, tc.expected, actual) }) } } func TestSerializeDeserializeGetDomainResponse_Roundtrip(t *testing.T) { tests := map[string]struct { input *GetDomainResponse encodingType constants.EncodingType }{ "nil response": { input: nil, encodingType: constants.EncodingTypeThriftRWSnappy, }, "empty response with ThriftRW": { input: &GetDomainResponse{}, encodingType: constants.EncodingTypeThriftRW, }, "empty response with ThriftRWSnappy": { input: &GetDomainResponse{}, encodingType: constants.EncodingTypeThriftRWSnappy, }, "full response with all fields": { input: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-domain-id", Name: "test-domain", Status: 0, Description: "test description", OwnerEmail: "test@example.com", Data: map[string]string{"key": "value"}, }, Config: &DomainConfig{ Retention: 7, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "test://history", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "test://visibility", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{"bad": {Reason: "test"}}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "active-cluster", Clusters: []*ClusterReplicationConfig{ {ClusterName: "cluster-1"}, {ClusterName: "cluster-2"}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster-1", FailoverVersion: 100}, }, }, }, }, }, IsGlobalDomain: true, FailoverVersion: 100, ConfigVersion: 5, FailoverNotificationVersion: 99, PreviousFailoverVersion: 98, LastUpdatedTime: 1234567890, NotificationVersion: 10, }, encodingType: constants.EncodingTypeThriftRWSnappy, }, "response with failover info": { input: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-id", Name: "test", Status: 0, }, FailoverVersion: 200, LastUpdatedTime: 1000, FailoverEndTime: func() *int64 { t := int64(2000); return &t }(), }, encodingType: constants.EncodingTypeThriftRW, }, "response with nil clusters in ReplicationConfig": { input: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-id", Name: "test", Status: 1, }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-1", Clusters: []*ClusterReplicationConfig{ {ClusterName: "cluster-1"}, {ClusterName: "cluster-2"}, }, }, }, encodingType: constants.EncodingTypeThriftRWSnappy, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { // Serialize blob, err := serializeGetDomainResponse(tc.input, tc.encodingType) assert.NoError(t, err) if tc.input == nil { assert.Nil(t, blob) return } assert.NotNil(t, blob) assert.Equal(t, tc.encodingType, blob.Encoding) assert.NotEmpty(t, blob.Data) // Deserialize deserialized, err := deserializeGetDomainResponse(blob) assert.NoError(t, err) assert.NotNil(t, deserialized) // Build expected result accounting for non-symmetric fields expected := &GetDomainResponse{ Info: tc.input.Info, Config: tc.input.Config, ReplicationConfig: tc.input.ReplicationConfig, IsGlobalDomain: tc.input.IsGlobalDomain, FailoverVersion: tc.input.FailoverVersion, LastUpdatedTime: tc.input.LastUpdatedTime, // Fields below are not present in types.DescribeDomainResponse and don't roundtrip: FailoverNotificationVersion: 0, PreviousFailoverVersion: 0, FailoverEndTime: nil, ConfigVersion: 0, NotificationVersion: 0, } // Apply transformations that the mapper does if expected.Info != nil { // Clamp Status to valid range (0-2) as ToType does if expected.Info.Status < 0 || expected.Info.Status > 2 { expected.Info.Status = 0 } } assert.Equal(t, expected, deserialized) }) } } func TestDeserializeGetDomainResponse_EmptyBlob(t *testing.T) { tests := map[string]struct { blob *DataBlob expected *GetDomainResponse }{ "nil blob": { blob: nil, expected: nil, }, "empty data": { blob: &DataBlob{ Data: []byte{}, Encoding: constants.EncodingTypeThriftRWSnappy, }, expected: nil, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { result, err := deserializeGetDomainResponse(tc.blob) assert.NoError(t, err) assert.Equal(t, tc.expected, result) }) } } func TestDeserializeGetDomainResponse_InvalidEncoding(t *testing.T) { blob := &DataBlob{ Data: []byte("some data"), Encoding: constants.EncodingType("invalid"), } result, err := deserializeGetDomainResponse(blob) assert.Error(t, err) assert.Nil(t, result) assert.IsType(t, &UnknownEncodingTypeError{}, err) } // Helper functions for creating pointers func stringPtr(s string) *string { return &s } func int64Ptr(i int64) *int64 { return &i } ================================================ FILE: common/persistence/domain_audit_manager.go ================================================ // Copyright (c) 2025 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "context" "fmt" "time" "github.com/golang/snappy" "github.com/google/uuid" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) type ( // domainAuditManagerImpl implements DomainAuditManager based on DomainAuditStore and PayloadSerializer // // a future implementation which may wish to get-by-ID should use the following logic to do so // eventID, _ := uuid.Parse(request.EventID) // createdTime := time.Unix(eventID.Time().UnixTime()) // return m.persistence.GetDomainAuditLog(ctx, &GetDomainAuditLogsRequest{ // DomainID: request.DomainID, // OperationType: request.OperationType, // EventID: request.EventID, // CreatedTime: createdTime, // part of the primary key // }) domainAuditManagerImpl struct { serializer PayloadSerializer persistence DomainAuditStore logger log.Logger timeSrc clock.TimeSource dc *DynamicConfiguration } ) // NewDomainAuditManagerImpl returns new DomainAuditManager func NewDomainAuditManagerImpl(persistence DomainAuditStore, logger log.Logger, serializer PayloadSerializer, dc *DynamicConfiguration) DomainAuditManager { return &domainAuditManagerImpl{ serializer: serializer, persistence: persistence, logger: logger, timeSrc: clock.NewRealTimeSource(), dc: dc, } } func (m *domainAuditManagerImpl) GetName() string { return m.persistence.GetName() } func (m *domainAuditManagerImpl) Close() { m.persistence.Close() } func (m *domainAuditManagerImpl) CreateDomainAuditLog( ctx context.Context, request *CreateDomainAuditLogRequest, ) (*CreateDomainAuditLogResponse, error) { // validation eventID, err := uuid.Parse(request.EventID) if err != nil { return nil, fmt.Errorf("failed to parse event ID: %w", err) } if eventID.Version() != 7 { return nil, fmt.Errorf("event ID must be a UUID v7: %w", err) } encodingType := constants.EncodingTypeThriftRWSnappy // Serialize StateBefore using thrift+snappy // To support non-nullable columns in SQL databases we serialize an empty GetDomainResponse{} if nil stateBefore := request.StateBefore if stateBefore == nil { stateBefore = &GetDomainResponse{} } stateBeforeBlob, err := serializeGetDomainResponse(stateBefore, encodingType) if err != nil { return nil, err } // Serialize StateAfter using thrift+snappy // To support non-nullable columns in SQL databases we serialize an empty GetDomainResponse{} if nil stateAfter := request.StateAfter if stateAfter == nil { if request.OperationType != DomainAuditOperationTypeDelete { m.logger.Warn("Domain has been updated to an empty state, this could be a bug", tag.WorkflowDomainID(request.DomainID), tag.DomainAuditOperationType(request.OperationType)) } stateAfter = &GetDomainResponse{} } stateAfterBlob, err := serializeGetDomainResponse(stateAfter, encodingType) if err != nil { return nil, err } // Get TTL from dynamic config using domain ID ttlDuration := m.dc.DomainAuditLogTTL(request.DomainID) return m.persistence.CreateDomainAuditLog(ctx, &InternalCreateDomainAuditLogRequest{ DomainID: request.DomainID, EventID: request.EventID, StateBefore: stateBeforeBlob, StateAfter: stateAfterBlob, OperationType: request.OperationType, CreatedTime: request.CreatedTime, LastUpdatedTime: request.CreatedTime, Identity: request.Identity, IdentityType: request.IdentityType, Comment: request.Comment, TTLSeconds: int64(ttlDuration.Seconds()), }) } func (m *domainAuditManagerImpl) GetDomainAuditLogs( ctx context.Context, request *GetDomainAuditLogsRequest, ) (*GetDomainAuditLogsResponse, error) { req := *request if req.MinCreatedTime == nil { start := time.Unix(0, 0) req.MinCreatedTime = &start } if req.MaxCreatedTime == nil { end := m.timeSrc.Now() req.MaxCreatedTime = &end } internalResp, err := m.persistence.GetDomainAuditLogs(ctx, &req) if err != nil { return nil, err } // Convert internal audit logs to external format auditLogs := make([]*DomainAuditLog, len(internalResp.AuditLogs)) for i, internalLog := range internalResp.AuditLogs { log := &DomainAuditLog{ EventID: internalLog.EventID, DomainID: internalLog.DomainID, OperationType: internalLog.OperationType, CreatedTime: internalLog.CreatedTime, LastUpdatedTime: internalLog.LastUpdatedTime, Identity: internalLog.Identity, IdentityType: internalLog.IdentityType, Comment: internalLog.Comment, } // Deserialize StateBefore if internalLog.StateBefore != nil && len(internalLog.StateBefore.Data) > 0 { stateBefore, err := deserializeGetDomainResponse(internalLog.StateBefore) if err != nil { return nil, err } // Only set if not an empty sentinel value if !isEmptyGetDomainResponse(stateBefore) { log.StateBefore = stateBefore } } // Deserialize StateAfter if internalLog.StateAfter != nil && len(internalLog.StateAfter.Data) > 0 { stateAfter, err := deserializeGetDomainResponse(internalLog.StateAfter) if err != nil { return nil, err } // Only set if not an empty sentinel value if !isEmptyGetDomainResponse(stateAfter) { log.StateAfter = stateAfter } } auditLogs[i] = log } return &GetDomainAuditLogsResponse{ AuditLogs: auditLogs, NextPageToken: internalResp.NextPageToken, }, nil } // serializeGetDomainResponse serializes GetDomainResponse using thrift encoding func serializeGetDomainResponse(resp *GetDomainResponse, encodingType constants.EncodingType) (*DataBlob, error) { if resp == nil { return nil, nil } typesResp := resp.ToType() if typesResp.ReplicationConfiguration != nil && typesResp.ReplicationConfiguration.Clusters != nil { filtered := make([]*types.ClusterReplicationConfiguration, 0, len(typesResp.ReplicationConfiguration.Clusters)) for _, cluster := range typesResp.ReplicationConfiguration.Clusters { if cluster != nil { filtered = append(filtered, cluster) } } typesResp.ReplicationConfiguration.Clusters = filtered } thriftResp := thrift.FromDescribeDomainResponse(typesResp) var data []byte var err error encoder := codec.NewThriftRWEncoder() switch encodingType { case constants.EncodingTypeThriftRW: data, err = encoder.Encode(thriftResp) case constants.EncodingTypeThriftRWSnappy: encoded, encodeErr := encoder.Encode(thriftResp) if encodeErr != nil { return nil, encodeErr } data = snappy.Encode(nil, encoded) default: // Fallback to ThriftRWSnappy encodingType = constants.EncodingTypeThriftRWSnappy encoded, encodeErr := encoder.Encode(thriftResp) if encodeErr != nil { return nil, encodeErr } data = snappy.Encode(nil, encoded) } if err != nil { return nil, err } return &DataBlob{ Data: data, Encoding: encodingType, }, nil } // deserializeGetDomainResponse deserializes GetDomainResponse from thrift format func deserializeGetDomainResponse(blob *DataBlob) (*GetDomainResponse, error) { if blob == nil || len(blob.Data) == 0 { return nil, nil } var data []byte var err error // Decode based on encoding type switch blob.Encoding { case constants.EncodingTypeThriftRW: data = blob.Data case constants.EncodingTypeThriftRWSnappy: data, err = snappy.Decode(nil, blob.Data) if err != nil { return nil, err } default: return nil, &UnknownEncodingTypeError{encodingType: blob.Encoding} } // Decode thrift encoder := codec.NewThriftRWEncoder() var thriftResp shared.DescribeDomainResponse err = encoder.Decode(data, &thriftResp) if err != nil { return nil, err } // Convert from thrift to types.DescribeDomainResponse typesResp := thrift.ToDescribeDomainResponse(&thriftResp) // Convert types.DescribeDomainResponse to persistence.GetDomainResponse return FromDescribeDomainResponse(typesResp), nil } // isEmptyGetDomainResponse checks if a GetDomainResponse is empty (sentinel value for nil) func isEmptyGetDomainResponse(resp *GetDomainResponse) bool { if resp == nil { return true } return resp.Info == nil && resp.Config == nil && resp.ReplicationConfig == nil } ================================================ FILE: common/persistence/domain_audit_manager_test.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "context" "errors" "testing" "time" "github.com/google/uuid" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" ) var testTimeNow = time.Date(2024, 12, 30, 23, 59, 59, 0, time.UTC) func setUpMocksForDomainAuditManager(t *testing.T) (*domainAuditManagerImpl, *MockDomainAuditStore) { t.Helper() ctrl := gomock.NewController(t) mockStore := NewMockDomainAuditStore(ctrl) m := &domainAuditManagerImpl{ persistence: mockStore, timeSrc: clock.NewMockedTimeSourceAt(testTimeNow), serializer: NewPayloadSerializer(), logger: log.NewNoop(), dc: &DynamicConfiguration{ DomainAuditLogTTL: func(domainID string) time.Duration { return time.Hour * 24 * 365 }, }, } return m, mockStore } func TestGetDomainAuditLogs(t *testing.T) { ctx := context.Background() epoch := time.Unix(0, 0) minTime := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) fixedTime := time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC) testCases := []struct { name string request *GetDomainAuditLogsRequest expectedRequest *GetDomainAuditLogsRequest storeResp *InternalGetDomainAuditLogsResponse storeErr error wantErr bool }{ { name: "nil MinCreatedTime defaults to epoch", request: &GetDomainAuditLogsRequest{ DomainID: "domain-1", MaxCreatedTime: &fixedTime, }, expectedRequest: &GetDomainAuditLogsRequest{ DomainID: "domain-1", MinCreatedTime: &epoch, MaxCreatedTime: &fixedTime, }, storeResp: &InternalGetDomainAuditLogsResponse{}, }, { name: "nil MaxCreatedTime defaults to timeSrc.Now()", request: &GetDomainAuditLogsRequest{ DomainID: "domain-1", MinCreatedTime: &minTime, }, expectedRequest: &GetDomainAuditLogsRequest{ DomainID: "domain-1", MinCreatedTime: &minTime, MaxCreatedTime: &testTimeNow, }, storeResp: &InternalGetDomainAuditLogsResponse{}, }, { name: "both nil times defaulted", request: &GetDomainAuditLogsRequest{ DomainID: "domain-1", }, expectedRequest: &GetDomainAuditLogsRequest{ DomainID: "domain-1", MinCreatedTime: &epoch, MaxCreatedTime: &testTimeNow, }, storeResp: &InternalGetDomainAuditLogsResponse{}, }, { name: "non-nil times passed through unchanged", request: &GetDomainAuditLogsRequest{ DomainID: "domain-1", MinCreatedTime: &minTime, MaxCreatedTime: &testTimeNow, }, expectedRequest: &GetDomainAuditLogsRequest{ DomainID: "domain-1", MinCreatedTime: &minTime, MaxCreatedTime: &testTimeNow, }, storeResp: &InternalGetDomainAuditLogsResponse{}, }, { name: "store error propagated", request: &GetDomainAuditLogsRequest{ DomainID: "domain-1", MinCreatedTime: &minTime, MaxCreatedTime: &testTimeNow, }, expectedRequest: &GetDomainAuditLogsRequest{ DomainID: "domain-1", MinCreatedTime: &minTime, MaxCreatedTime: &testTimeNow, }, storeErr: errors.New("store error"), wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { m, mockStore := setUpMocksForDomainAuditManager(t) mockStore.EXPECT().GetDomainAuditLogs(ctx, tc.expectedRequest).Return(tc.storeResp, tc.storeErr).Times(1) resp, err := m.GetDomainAuditLogs(ctx, tc.request) if tc.wantErr { assert.Error(t, err) assert.Nil(t, resp) } else { assert.NoError(t, err) assert.NotNil(t, resp) } }) } } func TestGetDomainAuditLogs_MutateDefaultTimeOnRetry(t *testing.T) { m, mockStore := setUpMocksForDomainAuditManager(t) mockTime := m.timeSrc.(clock.MockedTimeSource) t1 := mockTime.Now() // a request with nil MaxCreatedTime (meaning "up to now") request := &GetDomainAuditLogsRequest{ DomainID: "test-domain", } // first call at T1 mockStore.EXPECT().GetDomainAuditLogs(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *GetDomainAuditLogsRequest) (*InternalGetDomainAuditLogsResponse, error) { assert.Equal(t, t1, *req.MaxCreatedTime) return &InternalGetDomainAuditLogsResponse{}, nil }).Times(1) _, _ = m.GetDomainAuditLogs(context.Background(), request) mockTime.Advance(2 * time.Minute) t2 := t1.Add(2 * time.Minute) // second call at T2 (like in retry loop) mockStore.EXPECT().GetDomainAuditLogs(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *GetDomainAuditLogsRequest) (*InternalGetDomainAuditLogsResponse, error) { if *req.MaxCreatedTime == t1 { t.Errorf("MaxCreatedTime is still T1 (%v) but should be new Time.Now T2 (%v)", t1, t2) } return &InternalGetDomainAuditLogsResponse{}, nil }).Times(1) _, _ = m.GetDomainAuditLogs(context.Background(), request) } func TestCreateDomainAuditLog_SerializeStates(t *testing.T) { // This test verifies that nil StateBefore/StateAfter are serialized as empty GetDomainResponse{} // instead of being left as nil, allowing us to maintain NOT NULL database columns. domainResponse := &GetDomainResponse{ Info: &DomainInfo{ ID: "domain-123", Name: "test-domain", }, } tests := []struct { name string stateBefore *GetDomainResponse stateAfter *GetDomainResponse operationType DomainAuditOperationType expectEmptyBefore bool expectEmptyAfter bool }{ { name: "correctly serializes nil StateBefore as an empty GetDomainResponse{}", stateBefore: nil, stateAfter: domainResponse, operationType: DomainAuditOperationTypeCreate, expectEmptyBefore: true, expectEmptyAfter: false, }, { name: "correctly serializes nil StateAfter as an empty GetDomainResponse{}", stateBefore: domainResponse, stateAfter: nil, operationType: DomainAuditOperationTypeDelete, expectEmptyBefore: false, expectEmptyAfter: true, }, { name: "correctly serializes both StateBefore and StateAfter as is", stateBefore: domainResponse, stateAfter: domainResponse, operationType: DomainAuditOperationTypeUpdate, expectEmptyBefore: false, expectEmptyAfter: false, }, { name: "correctly serializes both StateBefore and StateAfter as empty GetDomainResponse{}", stateBefore: nil, stateAfter: nil, operationType: DomainAuditOperationTypeUpdate, expectEmptyBefore: true, expectEmptyAfter: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m, mockStore := setUpMocksForDomainAuditManager(t) ctx := context.Background() eventID := uuid.Must(uuid.NewV7()).String() createdTime := time.Now() mockStore.EXPECT().CreateDomainAuditLog(ctx, gomock.Any()). DoAndReturn(func(ctx context.Context, req *InternalCreateDomainAuditLogRequest) (*CreateDomainAuditLogResponse, error) { // Both blobs should ALWAYS be non-nil with non-empty data assert.NotNil(t, req.StateBefore, "StateBefore blob should never be nil") assert.NotNil(t, req.StateBefore.Data, "StateBefore.Data should not be nil") assert.NotEmpty(t, req.StateBefore.Data, "StateBefore.Data should contain serialized bytes") assert.NotNil(t, req.StateAfter, "StateAfter blob should never be nil") assert.NotNil(t, req.StateAfter.Data, "StateAfter.Data should not be nil") assert.NotEmpty(t, req.StateAfter.Data, "StateAfter.Data should contain serialized bytes") // Verify whether they're empty or populated deserializedBefore, err := deserializeGetDomainResponse(req.StateBefore) assert.NoError(t, err) assert.Equal(t, tt.expectEmptyBefore, isEmptyGetDomainResponse(deserializedBefore), "StateBefore empty state mismatch") deserializedAfter, err := deserializeGetDomainResponse(req.StateAfter) assert.NoError(t, err) assert.Equal(t, tt.expectEmptyAfter, isEmptyGetDomainResponse(deserializedAfter), "StateAfter empty state mismatch") return &CreateDomainAuditLogResponse{EventID: eventID}, nil }).Times(1) resp, err := m.CreateDomainAuditLog(ctx, &CreateDomainAuditLogRequest{ DomainID: "domain-123", EventID: eventID, CreatedTime: createdTime, StateBefore: tt.stateBefore, StateAfter: tt.stateAfter, OperationType: tt.operationType, Identity: "test-user", IdentityType: "user", Comment: "test operation", }) assert.NoError(t, err) assert.NotNil(t, resp) assert.Equal(t, eventID, resp.EventID) }) } } func TestGetDomainAuditLogs_DeserializesEmptyResponsesToNil(t *testing.T) { domainResponse := &GetDomainResponse{ Info: &DomainInfo{ ID: "domain-123", Name: "test-domain", }, } emptyResponse := &GetDomainResponse{} tests := []struct { name string stateBefore *GetDomainResponse stateAfter *GetDomainResponse expectNilBefore bool expectNilAfter bool expectDomainIDAfter bool }{ { name: "when StateBefore is nil, it should be deserialized as an empty GetDomainResponse{}", stateBefore: emptyResponse, stateAfter: domainResponse, expectNilBefore: true, expectNilAfter: false, expectDomainIDAfter: true, }, { name: "when StateAfter is nil, it should be deserialized as an empty GetDomainResponse{}", stateBefore: domainResponse, stateAfter: emptyResponse, expectNilBefore: false, expectNilAfter: true, expectDomainIDAfter: false, }, { name: "when both StateBefore and StateAfter are populated, they should be deserialized as is", stateBefore: domainResponse, stateAfter: domainResponse, expectNilBefore: false, expectNilAfter: false, expectDomainIDAfter: true, }, { name: "when both StateBefore and StateAfter are empty, they should be deserialized as empty GetDomainResponse{}", stateBefore: emptyResponse, stateAfter: emptyResponse, expectNilBefore: true, expectNilAfter: true, expectDomainIDAfter: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m, mockStore := setUpMocksForDomainAuditManager(t) ctx := context.Background() // Serialize the test data var stateBeforeBlob, stateAfterBlob *DataBlob var err error stateBeforeBlob, err = serializeGetDomainResponse(tt.stateBefore, constants.EncodingTypeThriftRWSnappy) assert.NoError(t, err) stateAfterBlob, err = serializeGetDomainResponse(tt.stateAfter, constants.EncodingTypeThriftRWSnappy) assert.NoError(t, err) mockStore.EXPECT().GetDomainAuditLogs(ctx, gomock.Any()). Return(&InternalGetDomainAuditLogsResponse{ AuditLogs: []*InternalDomainAuditLog{ { EventID: "event-1", DomainID: "domain-123", StateBefore: stateBeforeBlob, StateAfter: stateAfterBlob, OperationType: DomainAuditOperationTypeCreate, CreatedTime: time.Now(), }, }, }, nil).Times(1) resp, err := m.GetDomainAuditLogs(ctx, &GetDomainAuditLogsRequest{ DomainID: "domain-123", }) assert.NoError(t, err) assert.NotNil(t, resp) assert.Len(t, resp.AuditLogs, 1) // Verify empty serialized responses are converted to nil if tt.expectNilBefore { assert.Nil(t, resp.AuditLogs[0].StateBefore, "Empty serialized StateBefore should return nil") } else { assert.NotNil(t, resp.AuditLogs[0].StateBefore, "Populated StateBefore should be deserialized") } if tt.expectNilAfter { assert.Nil(t, resp.AuditLogs[0].StateAfter, "Empty serialized StateAfter should return nil") } else { assert.NotNil(t, resp.AuditLogs[0].StateAfter, "Populated StateAfter should be deserialized") if tt.expectDomainIDAfter { assert.Equal(t, "domain-123", resp.AuditLogs[0].StateAfter.Info.ID) } } }) } } func TestGetDomainAuditLogs_BackwardCompatibilityWithOldCassandraData(t *testing.T) { // Verifies that old Cassandra data with nil blobs returns nil without a deserialization error. m, mockStore := setUpMocksForDomainAuditManager(t) ctx := context.Background() // Serialize a real StateAfter to simulate existing Cassandra data stateAfterResponse := &GetDomainResponse{ Info: &DomainInfo{ ID: "domain-123", Name: "test-domain", }, } stateAfterBlob, err := serializeGetDomainResponse(stateAfterResponse, constants.EncodingTypeThriftRWSnappy) assert.NoError(t, err) // OLD Cassandra data: StateBefore is nil (NoSQL store returns nil when len(row.StateBefore) == 0) mockStore.EXPECT().GetDomainAuditLogs(ctx, gomock.Any()). Return(&InternalGetDomainAuditLogsResponse{ AuditLogs: []*InternalDomainAuditLog{ { EventID: "old-event-1", DomainID: "domain-123", StateBefore: nil, // Old data - NULL in Cassandra, nil from NoSQL store StateAfter: stateAfterBlob, OperationType: DomainAuditOperationTypeCreate, CreatedTime: time.Now(), }, }, }, nil).Times(1) resp, err := m.GetDomainAuditLogs(ctx, &GetDomainAuditLogsRequest{ DomainID: "domain-123", }) assert.NoError(t, err) assert.NotNil(t, resp) assert.Len(t, resp.AuditLogs, 1) // Old data with nil should still return nil to API (backward compatible) assert.Nil(t, resp.AuditLogs[0].StateBefore, "Old Cassandra data with nil should return nil") assert.NotNil(t, resp.AuditLogs[0].StateAfter, "StateAfter should be deserialized") assert.Equal(t, "domain-123", resp.AuditLogs[0].StateAfter.Info.ID) } ================================================ FILE: common/persistence/domain_manager.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "context" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) type ( // domainManagerImpl implements DomainManager based on DomainStore and PayloadSerializer domainManagerImpl struct { serializer PayloadSerializer persistence DomainStore logger log.Logger timeSrc clock.TimeSource dc *DynamicConfiguration } ) // NewDomainManagerImpl returns new DomainManager func NewDomainManagerImpl(persistence DomainStore, logger log.Logger, serializer PayloadSerializer, dc *DynamicConfiguration) DomainManager { return &domainManagerImpl{ serializer: serializer, persistence: persistence, logger: logger, timeSrc: clock.NewRealTimeSource(), dc: dc, } } func (m *domainManagerImpl) GetName() string { return m.persistence.GetName() } func (m *domainManagerImpl) CreateDomain( ctx context.Context, request *CreateDomainRequest, ) (*CreateDomainResponse, error) { encodingType := constants.EncodingType(m.dc.SerializationEncoding()) dc, err := m.toInternalDomainConfig(request.Config, encodingType) if err != nil { return nil, err } rc, err := m.toInternalDomainReplicationConfig(request.ReplicationConfig, encodingType) if err != nil { return nil, err } return m.persistence.CreateDomain(ctx, &InternalCreateDomainRequest{ Info: request.Info, Config: &dc, ReplicationConfig: &rc, IsGlobalDomain: request.IsGlobalDomain, ConfigVersion: request.ConfigVersion, FailoverVersion: request.FailoverVersion, LastUpdatedTime: time.Unix(0, request.LastUpdatedTime), CurrentTimeStamp: m.timeSrc.Now(), }) } func (m *domainManagerImpl) GetDomain( ctx context.Context, request *GetDomainRequest, ) (*GetDomainResponse, error) { internalResp, err := m.persistence.GetDomain(ctx, request) if err != nil { return nil, err } dc, err := m.fromInternalDomainConfig(internalResp.Config) if err != nil { return nil, err } rc, err := m.fromInternalDomainReplicationConfig(internalResp.ReplicationConfig) if err != nil { return nil, err } resp := &GetDomainResponse{ Info: internalResp.Info, Config: &dc, ReplicationConfig: &rc, IsGlobalDomain: internalResp.IsGlobalDomain, ConfigVersion: internalResp.ConfigVersion, FailoverVersion: internalResp.FailoverVersion, FailoverNotificationVersion: internalResp.FailoverNotificationVersion, PreviousFailoverVersion: internalResp.PreviousFailoverVersion, LastUpdatedTime: internalResp.LastUpdatedTime.UnixNano(), NotificationVersion: internalResp.NotificationVersion, } if internalResp.FailoverEndTime != nil { resp.FailoverEndTime = common.Int64Ptr(internalResp.FailoverEndTime.UnixNano()) } return resp, nil } func (m *domainManagerImpl) UpdateDomain( ctx context.Context, request *UpdateDomainRequest, ) error { encodingType := constants.EncodingType(m.dc.SerializationEncoding()) dc, err := m.toInternalDomainConfig(request.Config, encodingType) if err != nil { return err } rc, err := m.toInternalDomainReplicationConfig(request.ReplicationConfig, encodingType) if err != nil { return err } internalReq := &InternalUpdateDomainRequest{ Info: request.Info, Config: &dc, ReplicationConfig: &rc, ConfigVersion: request.ConfigVersion, FailoverVersion: request.FailoverVersion, FailoverNotificationVersion: request.FailoverNotificationVersion, PreviousFailoverVersion: request.PreviousFailoverVersion, LastUpdatedTime: time.Unix(0, request.LastUpdatedTime), NotificationVersion: request.NotificationVersion, } if request.FailoverEndTime != nil { internalReq.FailoverEndTime = common.TimePtr(time.Unix(0, *request.FailoverEndTime)) } return m.persistence.UpdateDomain(ctx, internalReq) } func (m *domainManagerImpl) DeleteDomain( ctx context.Context, request *DeleteDomainRequest, ) error { return m.persistence.DeleteDomain(ctx, request) } func (m *domainManagerImpl) DeleteDomainByName( ctx context.Context, request *DeleteDomainByNameRequest, ) error { return m.persistence.DeleteDomainByName(ctx, request) } func (m *domainManagerImpl) ListDomains( ctx context.Context, request *ListDomainsRequest, ) (*ListDomainsResponse, error) { resp, err := m.persistence.ListDomains(ctx, request) if err != nil { return nil, err } domains := make([]*GetDomainResponse, 0, len(resp.Domains)) for _, d := range resp.Domains { dc, err := m.fromInternalDomainConfig(d.Config) if err != nil { return nil, err } rc, err := m.fromInternalDomainReplicationConfig(d.ReplicationConfig) if err != nil { return nil, err } currResp := &GetDomainResponse{ Info: d.Info, Config: &dc, ReplicationConfig: &rc, IsGlobalDomain: d.IsGlobalDomain, ConfigVersion: d.ConfigVersion, FailoverVersion: d.FailoverVersion, FailoverNotificationVersion: d.FailoverNotificationVersion, PreviousFailoverVersion: d.PreviousFailoverVersion, NotificationVersion: d.NotificationVersion, LastUpdatedTime: d.LastUpdatedTime.UnixNano(), } if d.FailoverEndTime != nil { currResp.FailoverEndTime = common.Int64Ptr(d.FailoverEndTime.UnixNano()) } domains = append(domains, currResp) } return &ListDomainsResponse{ Domains: domains, NextPageToken: resp.NextPageToken, }, nil } func (m *domainManagerImpl) toInternalDomainConfig(c *DomainConfig, encodingType constants.EncodingType) (InternalDomainConfig, error) { if c == nil { return InternalDomainConfig{}, nil } if c.BadBinaries.Binaries == nil { c.BadBinaries.Binaries = map[string]*types.BadBinaryInfo{} } badBinaries, err := m.serializer.SerializeBadBinaries(&c.BadBinaries, encodingType) if err != nil { return InternalDomainConfig{}, err } isolationGroups, err := m.serializer.SerializeIsolationGroups(&c.IsolationGroups, encodingType) if err != nil { return InternalDomainConfig{}, err } asyncWFCfg, err := m.serializer.SerializeAsyncWorkflowsConfig(&c.AsyncWorkflowConfig, encodingType) if err != nil { return InternalDomainConfig{}, err } return InternalDomainConfig{ Retention: common.DaysToDuration(c.Retention), EmitMetric: c.EmitMetric, HistoryArchivalStatus: c.HistoryArchivalStatus, HistoryArchivalURI: c.HistoryArchivalURI, VisibilityArchivalStatus: c.VisibilityArchivalStatus, VisibilityArchivalURI: c.VisibilityArchivalURI, BadBinaries: badBinaries, IsolationGroups: isolationGroups, AsyncWorkflowsConfig: asyncWFCfg, }, nil } func (m *domainManagerImpl) toInternalDomainReplicationConfig(rc *DomainReplicationConfig, encodingType constants.EncodingType) (InternalDomainReplicationConfig, error) { if rc == nil { return InternalDomainReplicationConfig{}, nil } // active clusters don't use the default encoding due to their likely being quite large // and so we're explicitly opting to use snappy / compressed encoding activeClustersConfig, err := m.serializer.SerializeActiveClusters(rc.ActiveClusters, constants.EncodingTypeThriftRWSnappy) if err != nil { return InternalDomainReplicationConfig{}, err } return InternalDomainReplicationConfig{ Clusters: rc.Clusters, ActiveClusterName: rc.ActiveClusterName, ActiveClustersConfig: activeClustersConfig, }, nil } func (m *domainManagerImpl) fromInternalDomainConfig(ic *InternalDomainConfig) (DomainConfig, error) { if ic == nil { return DomainConfig{}, nil } badBinaries, err := m.serializer.DeserializeBadBinaries(ic.BadBinaries) if err != nil { return DomainConfig{}, err } var isolationGroups types.IsolationGroupConfiguration igDeserialized, err := m.serializer.DeserializeIsolationGroups(ic.IsolationGroups) if err != nil { return DomainConfig{}, err } if igDeserialized != nil { isolationGroups = *igDeserialized } var asyncWFCfg types.AsyncWorkflowConfiguration asyncWFCfgDeserialied, err := m.serializer.DeserializeAsyncWorkflowsConfig(ic.AsyncWorkflowsConfig) if err != nil { return DomainConfig{}, err } if asyncWFCfgDeserialied != nil { asyncWFCfg = *asyncWFCfgDeserialied } if badBinaries.Binaries == nil { badBinaries.Binaries = map[string]*types.BadBinaryInfo{} } return DomainConfig{ Retention: common.DurationToDays(ic.Retention), EmitMetric: ic.EmitMetric, HistoryArchivalStatus: ic.HistoryArchivalStatus, HistoryArchivalURI: ic.HistoryArchivalURI, VisibilityArchivalStatus: ic.VisibilityArchivalStatus, VisibilityArchivalURI: ic.VisibilityArchivalURI, BadBinaries: *badBinaries, IsolationGroups: isolationGroups, AsyncWorkflowConfig: asyncWFCfg, }, nil } func (m *domainManagerImpl) fromInternalDomainReplicationConfig(ic *InternalDomainReplicationConfig) (DomainReplicationConfig, error) { if ic == nil { return DomainReplicationConfig{}, nil } activeClusters, err := m.serializer.DeserializeActiveClusters(ic.ActiveClustersConfig) if err != nil { return DomainReplicationConfig{}, err } return DomainReplicationConfig{ Clusters: ic.Clusters, ActiveClusterName: ic.ActiveClusterName, ActiveClusters: activeClusters, }, nil } func (m *domainManagerImpl) GetMetadata( ctx context.Context, ) (*GetMetadataResponse, error) { return m.persistence.GetMetadata(ctx) } func (m *domainManagerImpl) Close() { m.persistence.Close() } ================================================ FILE: common/persistence/domain_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) func testFixtureDomainInfo() *DomainInfo { return &DomainInfo{ ID: "domain-id", Name: "domain-name", Status: 1, Description: "domain-desc", OwnerEmail: "owner@test.com", Data: map[string]string{"test": "a"}, } } func testFixtureDomainConfig() *DomainConfig { return &DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: types.BadBinaries{}, IsolationGroups: map[string]types.IsolationGroupPartition{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "q", QueueType: "kafka", }, } } func testFixtureDomainReplicationConfig() *DomainReplicationConfig { return &DomainReplicationConfig{ ActiveClusterName: "cluster-1", Clusters: []*ClusterReplicationConfig{ { ClusterName: "cluster-1", }, { ClusterName: "cluster-2", }, }, } } func testFixtureDomainReplicationConfigWithActiveClusters() *DomainReplicationConfig { return &DomainReplicationConfig{ Clusters: []*ClusterReplicationConfig{ { ClusterName: "cluster-1", }, { ClusterName: "cluster-2", }, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region-1": { ActiveClusterName: "cluster-1", FailoverVersion: 1, }, "region-2": { ActiveClusterName: "cluster-2", FailoverVersion: 2, }, }, }, }, }, } } func testFixtureDomainReplicationConfigInternal() *InternalDomainReplicationConfig { return &InternalDomainReplicationConfig{ ActiveClusterName: "cluster-1", Clusters: []*ClusterReplicationConfig{ { ClusterName: "cluster-1", }, { ClusterName: "cluster-2", }, }, } } func testFixtureDomainReplicationConfigInternalWithActiveClusters() *InternalDomainReplicationConfig { return &InternalDomainReplicationConfig{ Clusters: []*ClusterReplicationConfig{ { ClusterName: "cluster-1", }, { ClusterName: "cluster-2", }, }, ActiveClustersConfig: &DataBlob{Encoding: constants.EncodingTypeThriftRWSnappy, Data: []byte("active-clusters-config")}, } } func setUpMocksForDomainManager(t *testing.T) (*domainManagerImpl, *MockDomainStore, *MockPayloadSerializer) { ctrl := gomock.NewController(t) mockStore := NewMockDomainStore(ctrl) mockSerializer := NewMockPayloadSerializer(ctrl) logger := log.NewNoop() domainManager := NewDomainManagerImpl(mockStore, logger, mockSerializer, &DynamicConfiguration{SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW))}).(*domainManagerImpl) return domainManager, mockStore, mockSerializer } func TestCreateDomain(t *testing.T) { testCases := []struct { name string setupMock func(*MockDomainStore, *MockPayloadSerializer) request *CreateDomainRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). SerializeBadBinaries(&types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, nil).Times(1) mockSerializer.EXPECT(). SerializeIsolationGroups(&types.IsolationGroupConfiguration{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, nil).Times(1) mockSerializer.EXPECT(). SerializeAsyncWorkflowsConfig(&types.AsyncWorkflowConfiguration{Enabled: true, PredefinedQueueName: "q", QueueType: "kafka"}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, nil).Times(1) mockSerializer.EXPECT(). SerializeActiveClusters(nil, constants.EncodingTypeThriftRWSnappy). Return(nil, nil).Times(1) expectedReq := &InternalCreateDomainRequest{ Info: testFixtureDomainInfo(), Config: &InternalDomainConfig{ Retention: common.DaysToDuration(1), EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, IsolationGroups: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, AsyncWorkflowsConfig: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, }, ReplicationConfig: testFixtureDomainReplicationConfigInternal(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: time.Unix(0, 100), CurrentTimeStamp: time.Now(), } mockStore.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, actualRequest *InternalCreateDomainRequest) (*CreateDomainResponse, error) { assert.WithinDuration(t, expectedReq.CurrentTimeStamp, actualRequest.CurrentTimeStamp, time.Second) return nil, nil }) }, request: &CreateDomainRequest{ Info: testFixtureDomainInfo(), Config: testFixtureDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: 100, }, expectError: false, }, { name: "success with activeclusters config populated", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). SerializeBadBinaries(&types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, nil).Times(1) mockSerializer.EXPECT(). SerializeIsolationGroups(&types.IsolationGroupConfiguration{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, nil).Times(1) mockSerializer.EXPECT(). SerializeAsyncWorkflowsConfig(&types.AsyncWorkflowConfiguration{Enabled: true, PredefinedQueueName: "q", QueueType: "kafka"}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, nil).Times(1) mockSerializer.EXPECT(). SerializeActiveClusters(testFixtureDomainReplicationConfigWithActiveClusters().ActiveClusters, constants.EncodingTypeThriftRWSnappy). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRWSnappy, Data: []byte("active-clusters-config")}, nil).Times(1) expectedReq := &InternalCreateDomainRequest{ Info: testFixtureDomainInfo(), Config: &InternalDomainConfig{ Retention: common.DaysToDuration(1), EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, IsolationGroups: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, AsyncWorkflowsConfig: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, }, ReplicationConfig: testFixtureDomainReplicationConfigInternalWithActiveClusters(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: time.Unix(0, 100), CurrentTimeStamp: time.Now(), } mockStore.EXPECT(). CreateDomain(gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, actualRequest *InternalCreateDomainRequest) (*CreateDomainResponse, error) { assert.WithinDuration(t, expectedReq.CurrentTimeStamp, actualRequest.CurrentTimeStamp, time.Second) return nil, nil }) }, request: &CreateDomainRequest{ Info: testFixtureDomainInfo(), Config: testFixtureDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfigWithActiveClusters(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: 100, }, expectError: false, }, { name: "serialization error", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). SerializeBadBinaries(gomock.Any(), gomock.Any()). Return(nil, errors.New("serialization error")).Times(1) }, request: &CreateDomainRequest{ Info: &DomainInfo{ID: "domain1"}, Config: &DomainConfig{}, ReplicationConfig: &DomainReplicationConfig{}, IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 1, LastUpdatedTime: time.Now().UnixNano(), }, expectError: true, expectedError: "serialization error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { domainManager, mockStore, mockSerializer := setUpMocksForDomainManager(t) tc.setupMock(mockStore, mockSerializer) _, err := domainManager.CreateDomain(context.Background(), tc.request) if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestGetDomain(t *testing.T) { testCases := []struct { name string setupMock func(*MockDomainStore, *MockPayloadSerializer) request *GetDomainRequest expectError bool expectedError string expected *GetDomainResponse }{ { name: "success", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockStore.EXPECT(). GetDomain(gomock.Any(), &GetDomainRequest{ID: "domain1"}). Return(&InternalGetDomainResponse{ Info: testFixtureDomainInfo(), Config: &InternalDomainConfig{ Retention: common.DaysToDuration(1), EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, IsolationGroups: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, AsyncWorkflowsConfig: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, }, ReplicationConfig: testFixtureDomainReplicationConfigInternal(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: time.Unix(0, 100), FailoverEndTime: common.Ptr(time.Unix(0, 200)), }, nil).Times(1) mockSerializer.EXPECT(). DeserializeBadBinaries(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}). Return(&types.BadBinaries{}, nil).Times(1) mockSerializer.EXPECT(). DeserializeIsolationGroups(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}). Return(&types.IsolationGroupConfiguration{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, nil).Times(1) mockSerializer.EXPECT(). DeserializeAsyncWorkflowsConfig(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}). Return(&types.AsyncWorkflowConfiguration{}, nil).Times(1) mockSerializer.EXPECT(). DeserializeActiveClusters(nil). Return(nil, nil).Times(1) }, request: &GetDomainRequest{ID: "domain1"}, expectError: false, expected: &GetDomainResponse{ Info: testFixtureDomainInfo(), Config: &DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: map[string]types.IsolationGroupPartition{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{}, }, ReplicationConfig: testFixtureDomainReplicationConfig(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: 100, FailoverEndTime: common.Ptr(time.Unix(0, 200).UnixNano()), }, }, { name: "success with activeclusters config populated", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockStore.EXPECT(). GetDomain(gomock.Any(), &GetDomainRequest{ID: "domain1"}). Return(&InternalGetDomainResponse{ Info: testFixtureDomainInfo(), Config: &InternalDomainConfig{ Retention: common.DaysToDuration(1), EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, IsolationGroups: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, AsyncWorkflowsConfig: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, }, ReplicationConfig: testFixtureDomainReplicationConfigInternalWithActiveClusters(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: time.Unix(0, 100), FailoverEndTime: common.Ptr(time.Unix(0, 200)), }, nil).Times(1) mockSerializer.EXPECT(). DeserializeBadBinaries(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}). Return(&types.BadBinaries{}, nil).Times(1) mockSerializer.EXPECT(). DeserializeIsolationGroups(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}). Return(&types.IsolationGroupConfiguration{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, nil).Times(1) mockSerializer.EXPECT(). DeserializeAsyncWorkflowsConfig(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}). Return(&types.AsyncWorkflowConfiguration{}, nil).Times(1) mockSerializer.EXPECT(). DeserializeActiveClusters(testFixtureDomainReplicationConfigInternalWithActiveClusters().ActiveClustersConfig). Return(testFixtureDomainReplicationConfigWithActiveClusters().ActiveClusters, nil).Times(1) }, request: &GetDomainRequest{ID: "domain1"}, expectError: false, expected: &GetDomainResponse{ Info: testFixtureDomainInfo(), Config: &DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: map[string]types.IsolationGroupPartition{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{}, }, ReplicationConfig: testFixtureDomainReplicationConfigWithActiveClusters(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: 100, FailoverEndTime: common.Ptr(time.Unix(0, 200).UnixNano()), }, }, { name: "persistence error", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockStore.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &GetDomainRequest{ID: "domain1"}, expectError: true, expectedError: "persistence error", }, { name: "deserialization error", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockStore.EXPECT(). GetDomain(gomock.Any(), gomock.Any()). Return(&InternalGetDomainResponse{ Info: &DomainInfo{ID: "domain1"}, Config: &InternalDomainConfig{}, ReplicationConfig: &InternalDomainReplicationConfig{}, }, nil).Times(1) mockSerializer.EXPECT(). DeserializeBadBinaries(gomock.Any()). Return(nil, errors.New("deserialization error")).Times(1) }, request: &GetDomainRequest{ID: "domain1"}, expectError: true, expectedError: "deserialization error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { domainManager, mockStore, mockSerializer := setUpMocksForDomainManager(t) tc.setupMock(mockStore, mockSerializer) resp, err := domainManager.GetDomain(context.Background(), tc.request) if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } func TestUpdateDomain(t *testing.T) { testCases := []struct { name string setupMock func(*MockDomainStore, *MockPayloadSerializer) request *UpdateDomainRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). SerializeBadBinaries(&types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, nil).Times(1) mockSerializer.EXPECT(). SerializeIsolationGroups(&types.IsolationGroupConfiguration{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, nil).Times(1) mockSerializer.EXPECT(). SerializeAsyncWorkflowsConfig(&types.AsyncWorkflowConfiguration{Enabled: true, PredefinedQueueName: "q", QueueType: "kafka"}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, nil).Times(1) mockSerializer.EXPECT(). SerializeActiveClusters(nil, constants.EncodingTypeThriftRWSnappy). Return(nil, nil).Times(1) mockStore.EXPECT(). UpdateDomain(gomock.Any(), &InternalUpdateDomainRequest{ Info: testFixtureDomainInfo(), Config: &InternalDomainConfig{ Retention: common.DaysToDuration(1), EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, IsolationGroups: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, AsyncWorkflowsConfig: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, }, ReplicationConfig: testFixtureDomainReplicationConfigInternal(), ConfigVersion: 1, FailoverVersion: 10, FailoverNotificationVersion: 11, PreviousFailoverVersion: 9, NotificationVersion: 1000, LastUpdatedTime: time.Unix(0, 100), FailoverEndTime: common.Ptr(time.Unix(0, 200)), }). Return(nil).Times(1) }, request: &UpdateDomainRequest{ Info: testFixtureDomainInfo(), Config: testFixtureDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 10, FailoverNotificationVersion: 11, PreviousFailoverVersion: 9, LastUpdatedTime: 100, FailoverEndTime: common.Ptr(time.Unix(0, 200).UnixNano()), NotificationVersion: 1000, }, expectError: false, }, { name: "success with activeclusters config populated", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). SerializeBadBinaries(&types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, nil).Times(1) mockSerializer.EXPECT(). SerializeIsolationGroups(&types.IsolationGroupConfiguration{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, nil).Times(1) mockSerializer.EXPECT(). SerializeAsyncWorkflowsConfig(&types.AsyncWorkflowConfiguration{Enabled: true, PredefinedQueueName: "q", QueueType: "kafka"}, constants.EncodingTypeThriftRW). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, nil).Times(1) mockSerializer.EXPECT(). SerializeActiveClusters(testFixtureDomainReplicationConfigWithActiveClusters().ActiveClusters, constants.EncodingTypeThriftRWSnappy). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRWSnappy, Data: []byte("active-clusters-config")}, nil).Times(1) mockStore.EXPECT(). UpdateDomain(gomock.Any(), &InternalUpdateDomainRequest{ Info: testFixtureDomainInfo(), Config: &InternalDomainConfig{ Retention: common.DaysToDuration(1), EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, IsolationGroups: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, AsyncWorkflowsConfig: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, }, ReplicationConfig: testFixtureDomainReplicationConfigInternalWithActiveClusters(), ConfigVersion: 1, FailoverVersion: 10, FailoverNotificationVersion: 11, PreviousFailoverVersion: 9, NotificationVersion: 1000, LastUpdatedTime: time.Unix(0, 100), FailoverEndTime: common.Ptr(time.Unix(0, 200)), }). Return(nil).Times(1) }, request: &UpdateDomainRequest{ Info: testFixtureDomainInfo(), Config: testFixtureDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfigWithActiveClusters(), ConfigVersion: 1, FailoverVersion: 10, FailoverNotificationVersion: 11, PreviousFailoverVersion: 9, LastUpdatedTime: 100, FailoverEndTime: common.Ptr(time.Unix(0, 200).UnixNano()), NotificationVersion: 1000, }, expectError: false, }, { name: "serialization error", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). SerializeBadBinaries(gomock.Any(), gomock.Any()). Return(nil, errors.New("serialization error")).Times(1) }, request: &UpdateDomainRequest{ Info: testFixtureDomainInfo(), Config: testFixtureDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 10, FailoverNotificationVersion: 11, PreviousFailoverVersion: 9, LastUpdatedTime: 100, NotificationVersion: 1000, }, expectError: true, expectedError: "serialization error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { domainManager, mockStore, mockSerializer := setUpMocksForDomainManager(t) tc.setupMock(mockStore, mockSerializer) err := domainManager.UpdateDomain(context.Background(), tc.request) if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestDeleteDomain(t *testing.T) { testCases := []struct { name string setupMock func(*MockDomainStore, *MockPayloadSerializer) request *DeleteDomainRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockStore *MockDomainStore, _ *MockPayloadSerializer) { mockStore.EXPECT(). DeleteDomain(gomock.Any(), gomock.Any()). Return(nil).Times(1) }, request: &DeleteDomainRequest{ID: "domain1"}, expectError: false, }, { name: "persistence error", setupMock: func(mockStore *MockDomainStore, _ *MockPayloadSerializer) { mockStore.EXPECT(). DeleteDomain(gomock.Any(), gomock.Any()). Return(errors.New("persistence error")).Times(1) }, request: &DeleteDomainRequest{ID: "domain1"}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { domainManager, mockStore, mockSerializer := setUpMocksForDomainManager(t) tc.setupMock(mockStore, mockSerializer) err := domainManager.DeleteDomain(context.Background(), tc.request) if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestDeleteDomainByName(t *testing.T) { testCases := []struct { name string setupMock func(*MockDomainStore, *MockPayloadSerializer) request *DeleteDomainByNameRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockStore *MockDomainStore, _ *MockPayloadSerializer) { mockStore.EXPECT(). DeleteDomainByName(gomock.Any(), gomock.Any()). Return(nil).Times(1) }, request: &DeleteDomainByNameRequest{Name: "domain1"}, expectError: false, }, { name: "persistence error", setupMock: func(mockStore *MockDomainStore, _ *MockPayloadSerializer) { mockStore.EXPECT(). DeleteDomainByName(gomock.Any(), gomock.Any()). Return(errors.New("persistence error")).Times(1) }, request: &DeleteDomainByNameRequest{Name: "domain1"}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { domainManager, mockStore, mockSerializer := setUpMocksForDomainManager(t) tc.setupMock(mockStore, mockSerializer) err := domainManager.DeleteDomainByName(context.Background(), tc.request) if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestListDomains(t *testing.T) { testCases := []struct { name string setupMock func(*MockDomainStore, *MockPayloadSerializer) request *ListDomainsRequest expectError bool expectedError string expected *ListDomainsResponse }{ { name: "success", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockStore.EXPECT(). ListDomains(gomock.Any(), gomock.Any()). Return(&InternalListDomainsResponse{ Domains: []*InternalGetDomainResponse{ { Info: testFixtureDomainInfo(), Config: &InternalDomainConfig{ Retention: common.DaysToDuration(1), EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, IsolationGroups: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, AsyncWorkflowsConfig: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, }, ReplicationConfig: testFixtureDomainReplicationConfigInternal(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: time.Unix(0, 100), FailoverEndTime: common.Ptr(time.Unix(0, 200)), }, }, NextPageToken: []byte("token"), }, nil).Times(1) mockSerializer.EXPECT(). DeserializeBadBinaries(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}). Return(&types.BadBinaries{}, nil).Times(1) mockSerializer.EXPECT(). DeserializeIsolationGroups(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}). Return(&types.IsolationGroupConfiguration{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, nil).Times(1) mockSerializer.EXPECT(). DeserializeAsyncWorkflowsConfig(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}). Return(&types.AsyncWorkflowConfiguration{}, nil).Times(1) mockSerializer.EXPECT(). DeserializeActiveClusters(nil). Return(nil, nil).Times(1) }, request: &ListDomainsRequest{PageSize: 10}, expectError: false, expected: &ListDomainsResponse{ Domains: []*GetDomainResponse{ { Info: testFixtureDomainInfo(), Config: &DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: map[string]types.IsolationGroupPartition{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{}, }, ReplicationConfig: testFixtureDomainReplicationConfig(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: 100, FailoverEndTime: common.Ptr(time.Unix(0, 200).UnixNano()), }, }, NextPageToken: []byte("token"), }, }, { name: "success with activeclusters config populated", setupMock: func(mockStore *MockDomainStore, mockSerializer *MockPayloadSerializer) { mockStore.EXPECT(). ListDomains(gomock.Any(), gomock.Any()). Return(&InternalListDomainsResponse{ Domains: []*InternalGetDomainResponse{ { Info: testFixtureDomainInfo(), Config: &InternalDomainConfig{ Retention: common.DaysToDuration(1), EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}, IsolationGroups: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}, AsyncWorkflowsConfig: &DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}, }, ReplicationConfig: testFixtureDomainReplicationConfigInternalWithActiveClusters(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: time.Unix(0, 100), FailoverEndTime: common.Ptr(time.Unix(0, 200)), }, }, NextPageToken: []byte("token"), }, nil).Times(1) mockSerializer.EXPECT(). DeserializeBadBinaries(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("bad-binaries")}). Return(&types.BadBinaries{}, nil).Times(1) mockSerializer.EXPECT(). DeserializeIsolationGroups(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("isolation-groups")}). Return(&types.IsolationGroupConfiguration{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, nil).Times(1) mockSerializer.EXPECT(). DeserializeAsyncWorkflowsConfig(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("async-workflow-config")}). Return(&types.AsyncWorkflowConfiguration{}, nil).Times(1) mockSerializer.EXPECT(). DeserializeActiveClusters(testFixtureDomainReplicationConfigInternalWithActiveClusters().ActiveClustersConfig). Return(testFixtureDomainReplicationConfigWithActiveClusters().ActiveClusters, nil).Times(1) }, request: &ListDomainsRequest{PageSize: 10}, expectError: false, expected: &ListDomainsResponse{ Domains: []*GetDomainResponse{ { Info: testFixtureDomainInfo(), Config: &DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "s3://abc", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "s3://xyz", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: map[string]types.IsolationGroupPartition{"abc": {Name: "abc", State: types.IsolationGroupStateDrained}}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{}, }, ReplicationConfig: testFixtureDomainReplicationConfigWithActiveClusters(), IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 10, LastUpdatedTime: 100, FailoverEndTime: common.Ptr(time.Unix(0, 200).UnixNano()), }, }, NextPageToken: []byte("token"), }, }, { name: "persistence error", setupMock: func(mockStore *MockDomainStore, _ *MockPayloadSerializer) { mockStore.EXPECT(). ListDomains(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &ListDomainsRequest{PageSize: 10}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { domainManager, mockStore, mockSerializer := setUpMocksForDomainManager(t) tc.setupMock(mockStore, mockSerializer) resp, err := domainManager.ListDomains(context.Background(), tc.request) if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } func TestGetMetadata(t *testing.T) { testCases := []struct { name string setupMock func(*MockDomainStore) expectError bool expectedError string }{ { name: "success", setupMock: func(mockStore *MockDomainStore) { mockStore.EXPECT(). GetMetadata(gomock.Any()). Return(&GetMetadataResponse{NotificationVersion: 10}, nil).Times(1) }, expectError: false, }, { name: "persistence error", setupMock: func(mockStore *MockDomainStore) { mockStore.EXPECT(). GetMetadata(gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { domainManager, mockStore, _ := setUpMocksForDomainManager(t) tc.setupMock(mockStore) // Execute the GetMetadata method resp, err := domainManager.GetMetadata(context.Background()) // Validate results if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, int64(10), resp.NotificationVersion) } }) } } func TestDomainManagerClose(t *testing.T) { t.Run("close persistence store", func(t *testing.T) { domainManager, mockStore, _ := setUpMocksForDomainManager(t) // Expect the Close method to be called once mockStore.EXPECT().Close().Times(1) // Execute the Close method domainManager.Close() // No need to assert as we are just expecting the method call }) } ================================================ FILE: common/persistence/domain_replication_config_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" ) // TestIsActiveActiveMethodsAreInSync ensures that the IsActiveActive() implementations // in both persistence.DomainReplicationConfig and types.DomainReplicationConfiguration // behave identically func TestIsActiveActiveMethodsAreInSync(t *testing.T) { tests := []struct { name string typesConfig *types.DomainReplicationConfiguration persistenceConfig *DomainReplicationConfig want bool }{ { name: "both nil should return false", typesConfig: nil, persistenceConfig: nil, want: false, }, { name: "both with nil ActiveClusters should return false", typesConfig: &types.DomainReplicationConfiguration{ActiveClusters: nil}, persistenceConfig: &DomainReplicationConfig{ActiveClusters: nil}, want: false, }, { name: "both with empty ActiveClusters should return false", typesConfig: &types.DomainReplicationConfiguration{ActiveClusters: &types.ActiveClusters{}}, persistenceConfig: &DomainReplicationConfig{ActiveClusters: &types.ActiveClusters{}}, want: false, }, { name: "both with only AttributeScopes populated should return true", typesConfig: &types.DomainReplicationConfiguration{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 100, }, }, }, }, }, }, persistenceConfig: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 100, }, }, }, }, }, }, want: true, }, { name: "both with only AttributeScopes with non-empty ClusterAttributes should return true", typesConfig: &types.DomainReplicationConfiguration{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster2", FailoverVersion: 200, }, }, }, }, }, }, persistenceConfig: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster2", FailoverVersion: 200, }, }, }, }, }, }, want: true, }, { name: "both with empty AttributeScopes map should return false", typesConfig: &types.DomainReplicationConfiguration{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{}, }, }, persistenceConfig: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{}, }, }, want: false, }, { name: "both with AttributeScopes containing scope with empty ClusterAttributes should return false", typesConfig: &types.DomainReplicationConfiguration{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, }, }, }, persistenceConfig: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, }, }, }, want: false, }, { name: "both with multiple AttributeScopes, some empty should return true if any has ClusterAttributes", typesConfig: &types.DomainReplicationConfiguration{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: "cluster3", FailoverVersion: 300, }, }, }, }, }, }, persistenceConfig: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: "cluster3", FailoverVersion: 300, }, }, }, }, }, }, want: true, }, { name: "both with multiple AttributeScopes all empty should return false", typesConfig: &types.DomainReplicationConfiguration{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, }, }, }, persistenceConfig: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, }, }, }, want: false, }, { name: "both with multiple regions in AttributeScopes should return true", typesConfig: &types.DomainReplicationConfiguration{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 100, }, "us-west-1": { ActiveClusterName: "cluster2", FailoverVersion: 200, }, "eu-west-1": { ActiveClusterName: "cluster3", FailoverVersion: 300, }, }, }, }, }, }, persistenceConfig: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 100, }, "us-west-1": { ActiveClusterName: "cluster2", FailoverVersion: 200, }, "eu-west-1": { ActiveClusterName: "cluster3", FailoverVersion: 300, }, }, }, }, }, }, want: true, }, { name: "both with multiple AttributeScopes, each with multiple ClusterAttributes should return true", typesConfig: &types.DomainReplicationConfiguration{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 100, }, "us-west-1": { ActiveClusterName: "cluster2", FailoverVersion: 200, }, }, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: "cluster3", FailoverVersion: 300, }, "dc2": { ActiveClusterName: "cluster4", FailoverVersion: 400, }, }, }, }, }, }, persistenceConfig: &DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 100, }, "us-west-1": { ActiveClusterName: "cluster2", FailoverVersion: 200, }, }, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: "cluster3", FailoverVersion: 300, }, "dc2": { ActiveClusterName: "cluster4", FailoverVersion: 400, }, }, }, }, }, }, want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { typesResult := tt.typesConfig.IsActiveActive() persistenceResult := tt.persistenceConfig.IsActiveActive() if tt.persistenceConfig != nil && tt.typesConfig != nil { assert.Equal(t, tt.persistenceConfig.ActiveClusters, tt.typesConfig.ActiveClusters) } // Assert that both implementations return the same value assert.Equal(t, persistenceResult, typesResult, "IsActiveActive() implementations differ: types=%v, persistence=%v", typesResult, persistenceResult) // Also assert that they match the expected value assert.Equal(t, tt.want, typesResult, "types.IsActiveActive() = %v, want %v", typesResult, tt.want) assert.Equal(t, tt.want, persistenceResult, "persistence.IsActiveActive() = %v, want %v", persistenceResult, tt.want) }) } } ================================================ FILE: common/persistence/elasticsearch/decodeBench_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package elasticsearch import ( "bytes" "encoding/json" "testing" "time" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" es "github.com/uber/cadence/common/elasticsearch" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types/mapper/thrift" ) var ( data = []byte(`{"CloseStatus": 0, "CloseTime": 1547596872817380000, "DomainID": "bfd5c907-f899-4baf-a7b2-2ab85e623ebd", "HistoryLength": 29, "KafkaKey": "7-619", "RunID": "e481009e-14b3-45ae-91af-dce6e2a88365", "StartTime": 1547596872371000000, "WorkflowID": "6bfbc1e5-6ce4-4e22-bbfb-e0faa9a7a604-1-2256", "WorkflowType": "TestWorkflowExecute", "Encoding" : "thriftrw", "TaskList" : "taskList", "IsCron" : "false", "NumClusters" : "2", "Memo" : "WQ0ACgsLAAAAAwAAAAJrMgAAAAkidmFuY2V4dSIAAAACazMAAAADMTIzAAAAAmsxAAAAUXsia2V5MSI6MTIzNDMyMSwia2V5MiI6ImEgc3RyaW5nIGlzIHZlcnkgbG9uZyIsIm1hcCI6eyJtS2V5IjoxMjM0MywiYXNkIjoiYXNkZiJ9fQA="}`) ) /* BenchmarkJSONDecodeToType-8 200000 9321 ns/op BenchmarkJSONDecodeToMap-8 100000 12878 ns/op */ // nolint func BenchmarkJSONDecodeToType(b *testing.B) { bytes := (*json.RawMessage)(&data) for i := 0; i < b.N; i++ { var source *es.VisibilityRecord json.Unmarshal(*bytes, &source) record := &p.InternalVisibilityWorkflowExecutionInfo{ DomainID: source.DomainID, WorkflowType: source.WorkflowType, WorkflowID: source.WorkflowID, RunID: source.RunID, TypeName: source.WorkflowType, StartTime: time.Unix(0, source.StartTime), ExecutionTime: time.Unix(0, source.ExecutionTime), Memo: p.NewDataBlob(source.Memo, constants.EncodingType(source.Encoding)), TaskList: source.TaskList, IsCron: source.IsCron, NumClusters: source.NumClusters, } record.CloseTime = time.Unix(0, source.CloseTime) record.Status = thrift.ToWorkflowExecutionCloseStatus(&source.CloseStatus) record.HistoryLength = source.HistoryLength } } // nolint func BenchmarkJSONDecodeToMap(b *testing.B) { for i := 0; i < b.N; i++ { var source map[string]interface{} d := json.NewDecoder(bytes.NewReader(data)) d.UseNumber() d.Decode(&source) startTime, _ := source[definition.StartTime].(json.Number).Int64() executionTime, _ := source[definition.StartTime].(json.Number).Int64() closeTime, _ := source[definition.CloseTime].(json.Number).Int64() closeStatus, _ := source[definition.CloseStatus].(json.Number).Int64() historyLen, _ := source[definition.HistoryLength].(json.Number).Int64() record := &p.InternalVisibilityWorkflowExecutionInfo{ DomainID: source[definition.DomainID].(string), WorkflowType: source[definition.WorkflowType].(string), WorkflowID: source[definition.WorkflowID].(string), RunID: source[definition.RunID].(string), TypeName: source[definition.WorkflowType].(string), StartTime: time.Unix(0, startTime), ExecutionTime: time.Unix(0, executionTime), TaskList: source[definition.TaskList].(string), IsCron: source[definition.IsCron].(bool), NumClusters: source[definition.NumClusters].(int16), Memo: p.NewDataBlob([]byte(source[definition.Memo].(string)), constants.EncodingType(source[definition.Encoding].(string))), } record.CloseTime = time.Unix(0, closeTime) status := (shared.WorkflowExecutionCloseStatus)(int32(closeStatus)) record.Status = thrift.ToWorkflowExecutionCloseStatus(&status) record.HistoryLength = historyLen } } ================================================ FILE: common/persistence/elasticsearch/es_visibility_metric_clients.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package elasticsearch import ( "context" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type visibilityMetricsClient struct { metricClient metrics.Client persistence p.VisibilityManager logger log.Logger } var _ p.VisibilityManager = (*visibilityMetricsClient)(nil) // NewVisibilityMetricsClient wrap visibility client with metrics func NewVisibilityMetricsClient(persistence p.VisibilityManager, metricClient metrics.Client, logger log.Logger) p.VisibilityManager { return &visibilityMetricsClient{ persistence: persistence, metricClient: metricClient, logger: logger, } } func (p *visibilityMetricsClient) GetName() string { return p.persistence.GetName() } func (p *visibilityMetricsClient) RecordWorkflowExecutionStarted( ctx context.Context, request *p.RecordWorkflowExecutionStartedRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchRecordWorkflowExecutionStartedScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) err := p.persistence.RecordWorkflowExecutionStarted(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchRecordWorkflowExecutionStartedScope, err) } return err } func (p *visibilityMetricsClient) RecordWorkflowExecutionClosed( ctx context.Context, request *p.RecordWorkflowExecutionClosedRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchRecordWorkflowExecutionClosedScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) err := p.persistence.RecordWorkflowExecutionClosed(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchRecordWorkflowExecutionClosedScope, err) } return err } func (p *visibilityMetricsClient) RecordWorkflowExecutionUninitialized( ctx context.Context, request *p.RecordWorkflowExecutionUninitializedRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchRecordWorkflowExecutionUninitializedScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) err := p.persistence.RecordWorkflowExecutionUninitialized(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchRecordWorkflowExecutionUninitializedScope, err) } return err } func (p *visibilityMetricsClient) UpsertWorkflowExecution( ctx context.Context, request *p.UpsertWorkflowExecutionRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchUpsertWorkflowExecutionScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) err := p.persistence.UpsertWorkflowExecution(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchUpsertWorkflowExecutionScope, err) } return err } func (p *visibilityMetricsClient) ListOpenWorkflowExecutions( ctx context.Context, request *p.ListWorkflowExecutionsRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchListOpenWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.ListOpenWorkflowExecutions(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchListOpenWorkflowExecutionsScope, err) } return response, err } func (p *visibilityMetricsClient) ListClosedWorkflowExecutions( ctx context.Context, request *p.ListWorkflowExecutionsRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchListClosedWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.ListClosedWorkflowExecutions(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchListClosedWorkflowExecutionsScope, err) } return response, err } func (p *visibilityMetricsClient) ListOpenWorkflowExecutionsByType( ctx context.Context, request *p.ListWorkflowExecutionsByTypeRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchListOpenWorkflowExecutionsByTypeScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.ListOpenWorkflowExecutionsByType(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchListOpenWorkflowExecutionsByTypeScope, err) } return response, err } func (p *visibilityMetricsClient) ListClosedWorkflowExecutionsByType( ctx context.Context, request *p.ListWorkflowExecutionsByTypeRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchListClosedWorkflowExecutionsByTypeScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.ListClosedWorkflowExecutionsByType(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchListClosedWorkflowExecutionsByTypeScope, err) } return response, err } func (p *visibilityMetricsClient) ListOpenWorkflowExecutionsByWorkflowID( ctx context.Context, request *p.ListWorkflowExecutionsByWorkflowIDRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchListOpenWorkflowExecutionsByWorkflowIDScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchListOpenWorkflowExecutionsByWorkflowIDScope, err) } return response, err } func (p *visibilityMetricsClient) ListClosedWorkflowExecutionsByWorkflowID( ctx context.Context, request *p.ListWorkflowExecutionsByWorkflowIDRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchListClosedWorkflowExecutionsByWorkflowIDScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchListClosedWorkflowExecutionsByWorkflowIDScope, err) } return response, err } func (p *visibilityMetricsClient) ListClosedWorkflowExecutionsByStatus( ctx context.Context, request *p.ListClosedWorkflowExecutionsByStatusRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchListClosedWorkflowExecutionsByStatusScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.ListClosedWorkflowExecutionsByStatus(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchListClosedWorkflowExecutionsByStatusScope, err) } return response, err } func (p *visibilityMetricsClient) GetClosedWorkflowExecution( ctx context.Context, request *p.GetClosedWorkflowExecutionRequest, ) (*p.GetClosedWorkflowExecutionResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchGetClosedWorkflowExecutionScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.GetClosedWorkflowExecution(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchGetClosedWorkflowExecutionScope, err) } return response, err } func (p *visibilityMetricsClient) ListWorkflowExecutions( ctx context.Context, request *p.ListWorkflowExecutionsByQueryRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchListWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.ListWorkflowExecutions(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchListWorkflowExecutionsScope, err) } return response, err } func (p *visibilityMetricsClient) ScanWorkflowExecutions( ctx context.Context, request *p.ListWorkflowExecutionsByQueryRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchScanWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.ScanWorkflowExecutions(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchScanWorkflowExecutionsScope, err) } return response, err } func (p *visibilityMetricsClient) CountWorkflowExecutions( ctx context.Context, request *p.CountWorkflowExecutionsRequest, ) (*p.CountWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchCountWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) response, err := p.persistence.CountWorkflowExecutions(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchCountWorkflowExecutionsScope, err) } return response, err } func (p *visibilityMetricsClient) DeleteWorkflowExecution( ctx context.Context, request *p.VisibilityDeleteWorkflowExecutionRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchDeleteWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) err := p.persistence.DeleteWorkflowExecution(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchDeleteWorkflowExecutionsScope, err) } return err } func (p *visibilityMetricsClient) DeleteUninitializedWorkflowExecution( ctx context.Context, request *p.VisibilityDeleteWorkflowExecutionRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.ElasticsearchDeleteWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.ElasticsearchLatencyPerDomain) err := p.persistence.DeleteWorkflowExecution(ctx, request) sw.Stop() if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.ElasticsearchDeleteWorkflowExecutionsScope, err) } return err } func (p *visibilityMetricsClient) updateErrorMetric(scopeWithDomainTag metrics.Scope, scope metrics.ScopeIdx, err error) { switch err.(type) { case *types.BadRequestError: scopeWithDomainTag.IncCounter(metrics.ElasticsearchErrBadRequestCounterPerDomain) scopeWithDomainTag.IncCounter(metrics.ElasticsearchFailuresPerDomain) case *types.ServiceBusyError: scopeWithDomainTag.IncCounter(metrics.ElasticsearchErrBusyCounterPerDomain) scopeWithDomainTag.IncCounter(metrics.ElasticsearchFailuresPerDomain) default: p.logger.Error("Operation failed with internal error.", tag.MetricScope(int(scope)), tag.Error(err)) scopeWithDomainTag.IncCounter(metrics.ElasticsearchFailuresPerDomain) } } func (p *visibilityMetricsClient) Close() { p.persistence.Close() } ================================================ FILE: common/persistence/elasticsearch/es_visibility_store.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package elasticsearch import ( "context" "encoding/json" "errors" "fmt" "math" "regexp" "strconv" "strings" "time" "github.com/cch123/elasticsql" "github.com/valyala/fastjson" "github.com/uber/cadence/.gen/go/indexer" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" es "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/elasticsearch/query" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) type ( esVisibilityStore struct { esClient es.GenericClient index string producer messaging.Producer logger log.Logger config *service.Config } ) var _ p.VisibilityStore = (*esVisibilityStore)(nil) // NewElasticSearchVisibilityStore create a visibility store connecting to ElasticSearch func NewElasticSearchVisibilityStore( esClient es.GenericClient, index string, producer messaging.Producer, config *service.Config, logger log.Logger, ) p.VisibilityStore { return &esVisibilityStore{ esClient: esClient, index: index, producer: producer, logger: logger.WithTags(tag.ComponentESVisibilityManager), config: config, } } func (v *esVisibilityStore) Close() {} func (v *esVisibilityStore) GetName() string { return constants.ESPersistenceName } func (v *esVisibilityStore) RecordWorkflowExecutionStarted( ctx context.Context, request *p.InternalRecordWorkflowExecutionStartedRequest, ) error { v.checkProducer() msg := createVisibilityMessage( request.DomainUUID, request.WorkflowID, request.RunID, request.WorkflowTypeName, request.TaskList, request.StartTimestamp.UnixNano(), request.ExecutionTimestamp.UnixNano(), request.TaskID, request.Memo.Data, request.Memo.GetEncoding(), request.IsCron, request.NumClusters, request.ClusterAttributeScope, request.ClusterAttributeName, request.SearchAttributes, constants.RecordStarted, 0, // will not be used 0, // will not be used 0, // will not be used request.UpdateTimestamp.UnixNano(), // will be updated when workflow execution updates int64(request.ShardID), request.CronSchedule, int32(request.ExecutionStatus), request.ScheduledExecutionTime.UnixNano(), ) return v.producer.Publish(ctx, msg) } func (v *esVisibilityStore) RecordWorkflowExecutionClosed( ctx context.Context, request *p.InternalRecordWorkflowExecutionClosedRequest, ) error { v.checkProducer() msg := createVisibilityMessage( request.DomainUUID, request.WorkflowID, request.RunID, request.WorkflowTypeName, request.TaskList, request.StartTimestamp.UnixNano(), request.ExecutionTimestamp.UnixNano(), request.TaskID, request.Memo.Data, request.Memo.GetEncoding(), request.IsCron, request.NumClusters, request.ClusterAttributeScope, request.ClusterAttributeName, request.SearchAttributes, constants.RecordClosed, request.CloseTimestamp.UnixNano(), *thrift.FromWorkflowExecutionCloseStatus(&request.Status), request.HistoryLength, request.UpdateTimestamp.UnixNano(), int64(request.ShardID), request.CronSchedule, int32(request.ExecutionStatus), request.ScheduledExecutionTime.UnixNano(), ) return v.producer.Publish(ctx, msg) } func (v *esVisibilityStore) RecordWorkflowExecutionUninitialized( ctx context.Context, request *p.InternalRecordWorkflowExecutionUninitializedRequest, ) error { v.checkProducer() msg := getVisibilityMessageForUninitializedWorkflow( request.DomainUUID, request.WorkflowID, request.RunID, request.WorkflowTypeName, request.UpdateTimestamp.UnixNano(), request.ShardID, ) return v.producer.Publish(ctx, msg) } func (v *esVisibilityStore) UpsertWorkflowExecution( ctx context.Context, request *p.InternalUpsertWorkflowExecutionRequest, ) error { v.checkProducer() msg := createVisibilityMessage( request.DomainUUID, request.WorkflowID, request.RunID, request.WorkflowTypeName, request.TaskList, request.StartTimestamp.UnixNano(), request.ExecutionTimestamp.UnixNano(), request.TaskID, request.Memo.Data, request.Memo.GetEncoding(), request.IsCron, request.NumClusters, request.ClusterAttributeScope, request.ClusterAttributeName, request.SearchAttributes, constants.UpsertSearchAttributes, 0, // will not be used 0, // will not be used 0, // will not be used request.UpdateTimestamp.UnixNano(), request.ShardID, request.CronSchedule, int32(request.ExecutionStatus), request.ScheduledExecutionTimestamp, ) return v.producer.Publish(ctx, msg) } func (v *esVisibilityStore) ListOpenWorkflowExecutions( ctx context.Context, request *p.InternalListWorkflowExecutionsRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.StartTime) && !rec.StartTime.After(request.LatestTime) } resp, err := v.esClient.Search(ctx, &es.SearchRequest{ Index: v.index, ListRequest: request, IsOpen: true, Filter: isRecordValid, MatchQuery: nil, MaxResultWindow: v.config.ESIndexMaxResultWindow(), }) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListOpenWorkflowExecutions failed, %v", err), } } return resp, nil } func (v *esVisibilityStore) ListClosedWorkflowExecutions( ctx context.Context, request *p.InternalListWorkflowExecutionsRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.CloseTime) && !rec.CloseTime.After(request.LatestTime) } resp, err := v.esClient.Search(ctx, &es.SearchRequest{ Index: v.index, ListRequest: request, IsOpen: false, Filter: isRecordValid, MatchQuery: nil, MaxResultWindow: v.config.ESIndexMaxResultWindow(), }) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListClosedWorkflowExecutions failed, %v", err), } } return resp, nil } func (v *esVisibilityStore) ListOpenWorkflowExecutionsByType( ctx context.Context, request *p.InternalListWorkflowExecutionsByTypeRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.StartTime) && !rec.StartTime.After(request.LatestTime) } resp, err := v.esClient.Search(ctx, &es.SearchRequest{ Index: v.index, ListRequest: &request.InternalListWorkflowExecutionsRequest, IsOpen: true, Filter: isRecordValid, MatchQuery: query.NewMatchQuery(es.WorkflowType, request.WorkflowTypeName), MaxResultWindow: v.config.ESIndexMaxResultWindow(), }) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListOpenWorkflowExecutionsByType failed, %v", err), } } return resp, nil } func (v *esVisibilityStore) ListClosedWorkflowExecutionsByType( ctx context.Context, request *p.InternalListWorkflowExecutionsByTypeRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.CloseTime) && !rec.CloseTime.After(request.LatestTime) } resp, err := v.esClient.Search(ctx, &es.SearchRequest{ Index: v.index, ListRequest: &request.InternalListWorkflowExecutionsRequest, IsOpen: false, Filter: isRecordValid, MatchQuery: query.NewMatchQuery(es.WorkflowType, request.WorkflowTypeName), MaxResultWindow: v.config.ESIndexMaxResultWindow(), }) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListClosedWorkflowExecutionsByType failed, %v", err), } } return resp, nil } func (v *esVisibilityStore) ListOpenWorkflowExecutionsByWorkflowID( ctx context.Context, request *p.InternalListWorkflowExecutionsByWorkflowIDRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.StartTime) && !rec.StartTime.After(request.LatestTime) } resp, err := v.esClient.Search(ctx, &es.SearchRequest{ Index: v.index, ListRequest: &request.InternalListWorkflowExecutionsRequest, IsOpen: true, Filter: isRecordValid, MatchQuery: query.NewMatchQuery(es.WorkflowID, request.WorkflowID), MaxResultWindow: v.config.ESIndexMaxResultWindow(), }) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListOpenWorkflowExecutionsByWorkflowID failed, %v", err), } } return resp, nil } func (v *esVisibilityStore) ListClosedWorkflowExecutionsByWorkflowID( ctx context.Context, request *p.InternalListWorkflowExecutionsByWorkflowIDRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.CloseTime) && !rec.CloseTime.After(request.LatestTime) } resp, err := v.esClient.Search(ctx, &es.SearchRequest{ Index: v.index, ListRequest: &request.InternalListWorkflowExecutionsRequest, IsOpen: false, Filter: isRecordValid, MatchQuery: query.NewMatchQuery(es.WorkflowID, request.WorkflowID), MaxResultWindow: v.config.ESIndexMaxResultWindow(), }) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListClosedWorkflowExecutionsByWorkflowID failed, %v", err), } } return resp, nil } func (v *esVisibilityStore) ListClosedWorkflowExecutionsByStatus( ctx context.Context, request *p.InternalListClosedWorkflowExecutionsByStatusRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.CloseTime) && !rec.CloseTime.After(request.LatestTime) } resp, err := v.esClient.Search(ctx, &es.SearchRequest{ Index: v.index, ListRequest: &request.InternalListWorkflowExecutionsRequest, IsOpen: false, Filter: isRecordValid, MatchQuery: query.NewMatchQuery(es.CloseStatus, int32(*thrift.FromWorkflowExecutionCloseStatus(&request.Status))), MaxResultWindow: v.config.ESIndexMaxResultWindow(), }) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListClosedWorkflowExecutionsByStatus failed, %v", err), } } return resp, nil } func (v *esVisibilityStore) GetClosedWorkflowExecution( ctx context.Context, request *p.InternalGetClosedWorkflowExecutionRequest, ) (*p.InternalGetClosedWorkflowExecutionResponse, error) { resp, err := v.esClient.SearchForOneClosedExecution(ctx, v.index, request) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("SearchForOneClosedExecution failed, %v", err), } } return resp, nil } func (v *esVisibilityStore) DeleteWorkflowExecution( ctx context.Context, request *p.VisibilityDeleteWorkflowExecutionRequest, ) error { v.checkProducer() msg := getVisibilityMessageForDeletion( request.DomainID, request.WorkflowID, request.RunID, request.TaskID, ) return v.producer.Publish(ctx, msg) } func (v *esVisibilityStore) DeleteUninitializedWorkflowExecution( ctx context.Context, request *p.VisibilityDeleteWorkflowExecutionRequest, ) error { // verify if it is uninitialized workflow execution record // if it is, then call the existing delete method to delete query := fmt.Sprintf("StartTime = missing and DomainID = %s and RunID = %s", request.DomainID, request.RunID) queryRequest := &p.CountWorkflowExecutionsRequest{ Domain: request.Domain, Query: query, } resp, err := v.CountWorkflowExecutions(ctx, queryRequest) if err != nil { return err } if resp.Count > 0 { if err = v.DeleteWorkflowExecution(ctx, request); err != nil { return err } } return nil } func (v *esVisibilityStore) ListWorkflowExecutions( ctx context.Context, request *p.ListWorkflowExecutionsByQueryRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { checkPageSize(request) token, err := es.GetNextPageToken(request.NextPageToken) if err != nil { return nil, err } queryDSL, err := v.getESQueryDSL(request, token) if err != nil { return nil, &types.BadRequestError{Message: fmt.Sprintf("Error when parse query: %v", err)} } resp, err := v.esClient.SearchByQuery(ctx, &es.SearchByQueryRequest{ Index: v.index, Query: queryDSL, NextPageToken: request.NextPageToken, PageSize: request.PageSize, Filter: nil, MaxResultWindow: v.config.ESIndexMaxResultWindow(), }) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListWorkflowExecutions failed, %v", err), } } return resp, nil } func (v *esVisibilityStore) ScanWorkflowExecutions( ctx context.Context, request *p.ListWorkflowExecutionsByQueryRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { checkPageSize(request) token, err := es.GetNextPageToken(request.NextPageToken) if err != nil { return nil, err } var queryDSL string if len(token.ScrollID) == 0 { // first call queryDSL, err = getESQueryDSLForScan(request) if err != nil { return nil, &types.BadRequestError{Message: fmt.Sprintf("Error when parse query: %v", err)} } } scanRequest := &es.ScanByQueryRequest{ Index: v.index, Query: queryDSL, NextPageToken: request.NextPageToken, PageSize: request.PageSize, } resp, err := v.esClient.ScanByQuery(ctx, scanRequest) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ScanWorkflowExecutions failed: %v, scanRequest: %+v", err, scanRequest), } } return resp, nil } func (v *esVisibilityStore) CountWorkflowExecutions( ctx context.Context, request *p.CountWorkflowExecutionsRequest, ) ( *p.CountWorkflowExecutionsResponse, error) { queryDSL, err := getESQueryDSLForCount(request) if err != nil { return nil, &types.BadRequestError{Message: fmt.Sprintf("Error when parse query: %v", err)} } count, err := v.esClient.CountByQuery(ctx, v.index, queryDSL) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("CountWorkflowExecutions failed. Error: %v", err), } } response := &p.CountWorkflowExecutionsResponse{Count: count} return response, nil } const ( jsonMissingCloseTime = `{"missing":{"field":"CloseTime"}}` jsonRangeOnExecutionTime = `{"range":{"ExecutionTime":` jsonSortForOpen = `[{"StartTime":"desc"},{"RunID":"desc"}]` jsonSortWithTieBreaker = `{"RunID":"desc"}` jsonMissingStartTime = `{"missing":{"field":"StartTime"}}` // used to identify uninitialized workflow execution records dslFieldSort = "sort" dslFieldSearchAfter = "search_after" dslFieldFrom = "from" dslFieldSize = "size" defaultDateTimeFormat = time.RFC3339 // used for converting UnixNano to string like 2018-02-15T16:16:36-08:00 ) var ( timeKeys = map[string]bool{ es.StartTime: true, es.CloseTime: true, es.ExecutionTime: true, es.UpdateTime: true, } rangeKeys = map[string]bool{ "from": true, "to": true, "gt": true, "lt": true, "query": true, } ) var missingStartTimeRegex = regexp.MustCompile(jsonMissingStartTime) func getESQueryDSLForScan(request *p.ListWorkflowExecutionsByQueryRequest) (string, error) { sql := getSQLFromListRequest(request) dsl, err := getCustomizedDSLFromSQL(sql, request.DomainUUID) if err != nil { return "", err } // remove not needed fields dsl.Del(dslFieldSort) return dsl.String(), nil } func getESQueryDSLForCount(request *p.CountWorkflowExecutionsRequest) (string, error) { sql := getSQLFromCountRequest(request) dsl, err := getCustomizedDSLFromSQL(sql, request.DomainUUID) if err != nil { return "", err } // remove not needed fields dsl.Del(dslFieldFrom) dsl.Del(dslFieldSize) dsl.Del(dslFieldSort) return dsl.String(), nil } func (v *esVisibilityStore) getESQueryDSL(request *p.ListWorkflowExecutionsByQueryRequest, token *es.ElasticVisibilityPageToken) (string, error) { sql := getSQLFromListRequest(request) return v.processedDSLfromSQL(sql, request.DomainUUID, token) } func (v *esVisibilityStore) processedDSLfromSQL(sql, domainUUID string, token *es.ElasticVisibilityPageToken) (string, error) { dsl, err := getCustomizedDSLFromSQL(sql, domainUUID) if err != nil { return "", err } sortField, err := v.processSortField(dsl) if err != nil { return "", err } if es.ShouldSearchAfter(token) { valueOfSearchAfter, err := v.getValueOfSearchAfterInJSON(token, sortField) if err != nil { return "", err } dsl.Set(dslFieldSearchAfter, fastjson.MustParse(valueOfSearchAfter)) } else { // use from+size dsl.Set(dslFieldFrom, fastjson.MustParse(strconv.Itoa(token.From))) } dslStr := cleanDSL(dsl.String()) return dslStr, nil } func getSQLFromListRequest(request *p.ListWorkflowExecutionsByQueryRequest) string { var sql string query := strings.TrimSpace(request.Query) if query == "" { sql = fmt.Sprintf("select * from dummy limit %d", request.PageSize) } else if common.IsJustOrderByClause(query) { sql = fmt.Sprintf("select * from dummy %s limit %d", request.Query, request.PageSize) } else { sql = fmt.Sprintf("select * from dummy where %s limit %d", request.Query, request.PageSize) } return sql } func getSQLFromCountRequest(request *p.CountWorkflowExecutionsRequest) string { var sql string if strings.TrimSpace(request.Query) == "" { sql = "select * from dummy" } else { sql = fmt.Sprintf("select * from dummy where %s", request.Query) } return sql } func getCustomizedDSLFromSQL(sql string, domainID string) (*fastjson.Value, error) { // Only process LIKE clauses if they exist if strings.Contains(strings.ToLower(sql), " like ") { return processSQLWithLike(sql, domainID) } // No LIKE clauses found, use the original elasticsql.Convert return processSQLWithoutLike(sql, domainID) } // processSQLWithLike handles SQL queries that contain LIKE clauses func processSQLWithLike(sql string, domainID string) (*fastjson.Value, error) { likeClauses, strippedSQL := extractLikeClauses(sql) // Step 1: Convert with elasticsql for the non-LIKE portion dslStr, _, err := elasticsql.Convert(strippedSQL) if err != nil { return nil, err } dsl, err := fastjson.Parse(dslStr) if err != nil { return nil, err } // Step 2: Patch wildcard queries back in if err := injectWildcardQueries(dsl, likeClauses); err != nil { return nil, err } dsl = replaceDummyQuery(dsl) return applyStandardProcessing(dsl, domainID) } // processSQLWithoutLike handles SQL queries without LIKE clauses func processSQLWithoutLike(sql string, domainID string) (*fastjson.Value, error) { dslStr, _, err := elasticsql.Convert(sql) if err != nil { return nil, err } dsl, err := fastjson.Parse(dslStr) if err != nil { return nil, err } return applyStandardProcessing(dsl, domainID) } // applyStandardProcessing applies the standard post-processing steps to the DSL func applyStandardProcessing(dsl *fastjson.Value, domainID string) (*fastjson.Value, error) { dslStr := dsl.String() if strings.Contains(dslStr, jsonMissingStartTime) { // isUninitialized dsl = replaceQueryForUninitialized(dsl) } if strings.Contains(dslStr, jsonMissingCloseTime) { // isOpen dsl = replaceQueryForOpen(dsl) } if strings.Contains(dslStr, jsonRangeOnExecutionTime) { addQueryForExecutionTime(dsl) } addDomainToQuery(dsl, domainID) if err := processAllValuesForKey(dsl, isCombinedKey, combinedProcessFunc); err != nil { return nil, err } return dsl, nil } // ES v6 only accepts "must_not exists" query instead of "missing" query, but elasticsql produces "missing", // so use this func to replace. // Note it also means a temp limitation that we cannot support field missing search func replaceQueryForOpen(dsl *fastjson.Value) *fastjson.Value { re := regexp.MustCompile(jsonMissingCloseTime) newDslStr := re.ReplaceAllString(dsl.String(), `{"bool":{"must_not":{"exists":{"field":"CloseTime"}}}}`) dsl = fastjson.MustParse(newDslStr) return dsl } // ES v6 only accepts "must_not exists" query instead of "missing" query, but elasticsql produces "missing", // so use this func to replace. func replaceQueryForUninitialized(dsl *fastjson.Value) *fastjson.Value { newDslStr := missingStartTimeRegex.ReplaceAllString(dsl.String(), `{"bool":{"must_not":{"exists":{"field":"StartTime"}}}}`) dsl = fastjson.MustParse(newDslStr) return dsl } func addQueryForExecutionTime(dsl *fastjson.Value) { executionTimeQueryString := `{"range" : {"ExecutionTime" : {"gt" : "0"}}}` addMustQuery(dsl, executionTimeQueryString) } func addDomainToQuery(dsl *fastjson.Value, domainID string) { if len(domainID) == 0 { return } domainQueryString := fmt.Sprintf(`{"match_phrase":{"DomainID":{"query":"%s"}}}`, domainID) addMustQuery(dsl, domainQueryString) } // addMustQuery is wrapping bool query with new bool query with must, // reason not making a flat bool query is to ensure "should (or)" query works correctly in query context. func addMustQuery(dsl *fastjson.Value, queryString string) { valOfTopQuery := dsl.Get("query") valOfBool := dsl.Get("query", "bool") newValOfBool := fmt.Sprintf(`{"must":[%s,{"bool":%s}]}`, queryString, valOfBool.String()) valOfTopQuery.Set("bool", fastjson.MustParse(newValOfBool)) } func (v *esVisibilityStore) processSortField(dsl *fastjson.Value) (string, error) { isSorted := dsl.Exists(dslFieldSort) var sortField string if !isSorted { // set default sorting by StartTime desc dsl.Set(dslFieldSort, fastjson.MustParse(jsonSortForOpen)) sortField = definition.StartTime } else { // user provide sorting using order by // sort validation on length if len(dsl.GetArray(dslFieldSort)) > 1 { return "", errors.New("only one field can be used to sort") } // sort validation to exclude IndexedValueTypeString obj, _ := dsl.GetArray(dslFieldSort)[0].Object() obj.Visit(func(k []byte, v *fastjson.Value) { // visit is only way to get object key in fastjson sortField = string(k) }) if v.getFieldType(sortField) == types.IndexedValueTypeString { return "", errors.New("not able to sort by IndexedValueTypeString field, use IndexedValueTypeKeyword field") } // add RunID as tie-breaker dsl.Get(dslFieldSort).Set("1", fastjson.MustParse(jsonSortWithTieBreaker)) } return sortField, nil } func (v *esVisibilityStore) getFieldType(fieldName string) types.IndexedValueType { if strings.HasPrefix(fieldName, definition.Attr) { fieldName = fieldName[len(definition.Attr)+1:] // remove prefix } validMap := v.config.ValidSearchAttributes() fieldType, ok := validMap[fieldName] if !ok { v.logger.Error("Unknown fieldName, validation should be done in frontend already", tag.Value(fieldName)) } return common.ConvertIndexedValueTypeToInternalType(fieldType, v.logger) } func (v *esVisibilityStore) getValueOfSearchAfterInJSON(token *es.ElasticVisibilityPageToken, sortField string) (string, error) { var sortVal interface{} var err error switch v.getFieldType(sortField) { case types.IndexedValueTypeInt, types.IndexedValueTypeDatetime, types.IndexedValueTypeBool: sortVal, err = token.SortValue.(json.Number).Int64() if err != nil { err, ok := err.(*strconv.NumError) // field not present, ES will return big int +-9223372036854776000 if !ok { return "", err } if err.Num[0] == '-' { // desc sortVal = math.MinInt64 } else { // asc sortVal = math.MaxInt64 } } case types.IndexedValueTypeDouble: switch token.SortValue.(type) { case json.Number: sortVal, err = token.SortValue.(json.Number).Float64() if err != nil { return "", err } case string: // field not present, ES will return "-Infinity" or "Infinity" sortVal = fmt.Sprintf(`"%s"`, token.SortValue.(string)) } case types.IndexedValueTypeKeyword: if token.SortValue != nil { sortVal = fmt.Sprintf(`"%s"`, token.SortValue.(string)) } else { // field not present, ES will return null (so token.SortValue is nil) sortVal = "null" } default: sortVal = token.SortValue } return fmt.Sprintf(`[%v, "%s"]`, sortVal, token.TieBreaker), nil } func (v *esVisibilityStore) checkProducer() { if v.producer == nil { // must be bug, check history setup panic("message producer is nil") } } func createVisibilityMessage( // common parameters domainID string, wid, rid string, workflowTypeName string, taskList string, startTimeUnixNano int64, executionTimeUnixNano int64, taskID int64, memo []byte, encoding constants.EncodingType, isCron bool, NumClusters int16, clusterAttributeScope string, clusterAttributeName string, searchAttributes map[string][]byte, visibilityOperation constants.VisibilityOperation, // specific to certain status endTimeUnixNano int64, // close execution closeStatus workflow.WorkflowExecutionCloseStatus, // close execution historyLength int64, // close execution updateTimeUnixNano int64, // update execution, shardID int64, cronSchedule string, executionStatus int32, scheduledExecutionTimeUnixNano int64, ) *indexer.Message { msgType := indexer.MessageTypeIndex fields := map[string]*indexer.Field{ es.WorkflowType: {Type: &es.FieldTypeString, StringData: common.StringPtr(workflowTypeName)}, es.StartTime: {Type: &es.FieldTypeInt, IntData: common.Int64Ptr(startTimeUnixNano)}, es.ExecutionTime: {Type: &es.FieldTypeInt, IntData: common.Int64Ptr(executionTimeUnixNano)}, es.TaskList: {Type: &es.FieldTypeString, StringData: common.StringPtr(taskList)}, es.IsCron: {Type: &es.FieldTypeBool, BoolData: common.BoolPtr(isCron)}, es.NumClusters: {Type: &es.FieldTypeInt, IntData: common.Int64Ptr(int64(NumClusters))}, es.ClusterAttributeScope: {Type: &es.FieldTypeString, StringData: common.StringPtr(clusterAttributeScope)}, es.ClusterAttributeName: {Type: &es.FieldTypeString, StringData: common.StringPtr(clusterAttributeName)}, es.UpdateTime: {Type: &es.FieldTypeInt, IntData: common.Int64Ptr(updateTimeUnixNano)}, es.ShardID: {Type: &es.FieldTypeInt, IntData: common.Int64Ptr(shardID)}, es.CronSchedule: {Type: &es.FieldTypeString, StringData: common.StringPtr(cronSchedule)}, es.ExecutionStatus: {Type: &es.FieldTypeInt, IntData: common.Int64Ptr(int64(executionStatus))}, es.ScheduledExecutionTime: {Type: &es.FieldTypeInt, IntData: common.Int64Ptr(scheduledExecutionTimeUnixNano)}, } if len(memo) != 0 { fields[es.Memo] = &indexer.Field{Type: &es.FieldTypeBinary, BinaryData: memo} fields[es.Encoding] = &indexer.Field{Type: &es.FieldTypeString, StringData: common.StringPtr(string(encoding))} } for k, v := range searchAttributes { fields[k] = &indexer.Field{Type: &es.FieldTypeBinary, BinaryData: v} } switch visibilityOperation { case constants.RecordStarted: case constants.RecordClosed: fields[es.CloseTime] = &indexer.Field{Type: &es.FieldTypeInt, IntData: common.Int64Ptr(endTimeUnixNano)} fields[es.CloseStatus] = &indexer.Field{Type: &es.FieldTypeInt, IntData: common.Int64Ptr(int64(closeStatus))} fields[es.HistoryLength] = &indexer.Field{Type: &es.FieldTypeInt, IntData: common.Int64Ptr(historyLength)} } var visibilityOperationThrift indexer.VisibilityOperation = -1 switch visibilityOperation { case constants.RecordStarted: visibilityOperationThrift = indexer.VisibilityOperationRecordStarted case constants.RecordClosed: visibilityOperationThrift = indexer.VisibilityOperationRecordClosed case constants.UpsertSearchAttributes: visibilityOperationThrift = indexer.VisibilityOperationUpsertSearchAttributes default: panic("VisibilityOperation not set") } msg := &indexer.Message{ MessageType: &msgType, DomainID: common.StringPtr(domainID), WorkflowID: common.StringPtr(wid), RunID: common.StringPtr(rid), Version: common.Int64Ptr(taskID), Fields: fields, VisibilityOperation: &visibilityOperationThrift, } return msg } func getVisibilityMessageForDeletion(domainID, workflowID, runID string, docVersion int64) *indexer.Message { msgType := indexer.MessageTypeDelete msg := &indexer.Message{ MessageType: &msgType, DomainID: common.StringPtr(domainID), WorkflowID: common.StringPtr(workflowID), RunID: common.StringPtr(runID), Version: common.Int64Ptr(docVersion), } return msg } func getVisibilityMessageForUninitializedWorkflow( domainID string, wid, rid string, workflowTypeName string, updateTimeUnixNano int64, // update execution shardID int64, ) *indexer.Message { msgType := indexer.MessageTypeCreate fields := map[string]*indexer.Field{ es.WorkflowType: {Type: &es.FieldTypeString, StringData: common.StringPtr(workflowTypeName)}, es.UpdateTime: {Type: &es.FieldTypeInt, IntData: common.Int64Ptr(updateTimeUnixNano)}, es.ShardID: {Type: &es.FieldTypeInt, IntData: common.Int64Ptr(shardID)}, } msg := &indexer.Message{ MessageType: &msgType, DomainID: common.StringPtr(domainID), WorkflowID: common.StringPtr(wid), RunID: common.StringPtr(rid), Fields: fields, } return msg } func checkPageSize(request *p.ListWorkflowExecutionsByQueryRequest) { if request.PageSize == 0 { request.PageSize = 1000 } } func processAllValuesForKey( dsl *fastjson.Value, keyFilter func(k string) bool, processFunc func(obj *fastjson.Object, key string, v *fastjson.Value) error, ) error { switch dsl.Type() { case fastjson.TypeArray: for _, val := range dsl.GetArray() { if err := processAllValuesForKey(val, keyFilter, processFunc); err != nil { return err } } case fastjson.TypeObject: objectVal := dsl.GetObject() keys := []string{} objectVal.Visit(func(key []byte, val *fastjson.Value) { keys = append(keys, string(key)) }) for _, key := range keys { var err error val := objectVal.Get(key) if keyFilter(key) { err = processFunc(objectVal, key, val) } else { err = processAllValuesForKey(val, keyFilter, processFunc) } if err != nil { return err } } default: // do nothing, since there's no key } return nil } func isCombinedKey(key string) bool { return isTimeKey(key) || isCloseStatusKey(key) } func combinedProcessFunc(obj *fastjson.Object, key string, value *fastjson.Value) error { if isTimeKey(key) { return timeProcessFunc(obj, key, value) } if isCloseStatusKey(key) { return closeStatusProcessFunc(obj, key, value) } return fmt.Errorf("unknown es dsl key %v for processing value", key) } func isTimeKey(key string) bool { return timeKeys[key] } func timeProcessFunc(obj *fastjson.Object, key string, value *fastjson.Value) error { return processAllValuesForKey(value, func(key string) bool { return rangeKeys[key] }, func(obj *fastjson.Object, key string, v *fastjson.Value) error { timeStr := string(v.GetStringBytes()) // first check if already in int64 format if _, err := strconv.ParseInt(timeStr, 10, 64); err == nil { return nil } // try to parse time parsedTime, err := time.Parse(defaultDateTimeFormat, timeStr) if err != nil { return err } obj.Set(key, fastjson.MustParse(fmt.Sprintf(`"%v"`, parsedTime.UnixNano()))) return nil }) } func isCloseStatusKey(key string) bool { return key == es.CloseStatus } func closeStatusProcessFunc(obj *fastjson.Object, key string, value *fastjson.Value) error { return processAllValuesForKey(value, func(key string) bool { return rangeKeys[key] }, func(obj *fastjson.Object, key string, v *fastjson.Value) error { statusStr := string(v.GetStringBytes()) // first check if already in int64 format if _, err := strconv.ParseInt(statusStr, 10, 64); err == nil { return nil } // try to parse close status string var parsedStatus types.WorkflowExecutionCloseStatus err := parsedStatus.UnmarshalText([]byte(statusStr)) if err != nil { return err } obj.Set(key, fastjson.MustParse(fmt.Sprintf(`"%d"`, parsedStatus))) return nil }) } // elasticsql may transfer `Attr.Name` to "`Attr.Name`" instead of "Attr.Name" in dsl in some operator like "between and" // this function is used to clean up func cleanDSL(input string) string { var re = regexp.MustCompile("(`)(Attr.\\w+)(`)") result := re.ReplaceAllString(input, `$2`) return result } type likeClause struct { Field string Pattern string } func extractLikeClauses(sql string) ([]likeClause, string) { var clauses []likeClause re := regexp.MustCompile(`(?i)([\w\.]+)\s+LIKE\s+'([^']+)'`) strippedSQL := sql matches := re.FindAllStringSubmatch(sql, -1) for _, match := range matches { clauses = append(clauses, likeClause{ Field: match[1], Pattern: match[2], }) // Remove LIKE clause from SQL // Replace LIKE with a dummy expression that elasticsql can parse replacement := `__dummy_field__ = '__dummy_value__'` strippedSQL = strings.Replace(strippedSQL, match[0], replacement, 1) } return clauses, strippedSQL } func injectWildcardQueries(dsl *fastjson.Value, likes []likeClause) error { obj, err := dsl.Object() if err != nil { return err } queryObj := obj.Get("query") if queryObj == nil { return fmt.Errorf("missing 'query' field") } boolObj := queryObj.Get("bool") if boolObj == nil { return fmt.Errorf("missing 'bool' query") } // Check if we have a 'should' array (for OR queries) or 'must' array (for AND queries) var targetArray []*fastjson.Value var arrayKey string if shouldArr := boolObj.GetArray("should"); shouldArr != nil { targetArray = shouldArr arrayKey = "should" } else if mustArr := boolObj.GetArray("must"); mustArr != nil { targetArray = mustArr arrayKey = "must" } else { // if neither exists, create a must array targetArray = []*fastjson.Value{} arrayKey = "must" } for _, clause := range likes { wildcardValue := strings.ReplaceAll(clause.Pattern, "%", "*") wildcardValue = strings.ReplaceAll(wildcardValue, "_", "?") wildcard := fmt.Sprintf(`{"wildcard": {"%s": {"value": "%s*", "case_insensitive": true}}}`, clause.Field, wildcardValue) v, err := fastjson.Parse(wildcard) if err != nil { return err } targetArray = append(targetArray, v) } // Inject updated array boolObj.Set(arrayKey, fastjson.MustParse(fmt.Sprintf("[%s]", joinFastjson(targetArray, ",")))) return nil } func joinFastjson(arr []*fastjson.Value, sep string) string { parts := make([]string, len(arr)) for i, v := range arr { parts[i] = v.String() } return strings.Join(parts, sep) } func replaceDummyQuery(dsl *fastjson.Value) *fastjson.Value { // Convert to string to find and remove the dummy query dslStr := dsl.String() // Remove all dummy match_phrase queries dummyQuery := `{"match_phrase":{"__dummy_field__":{"query":"__dummy_value__"}}}` dslStr = strings.ReplaceAll(dslStr, dummyQuery, "") // Clean up any trailing commas or empty arrays dslStr = strings.Replace(dslStr, ",,", ",", -1) dslStr = strings.Replace(dslStr, "[,", "[", -1) dslStr = strings.Replace(dslStr, ",]", "]", -1) dslStr = strings.Replace(dslStr, "[]", "", -1) // Parse back to fastjson.Value return fastjson.MustParse(dslStr) } ================================================ FILE: common/persistence/elasticsearch/es_visibility_store_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package elasticsearch import ( "context" "encoding/json" "errors" "fmt" "strings" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/valyala/fastjson" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" es "github.com/uber/cadence/common/elasticsearch" esMocks "github.com/uber/cadence/common/elasticsearch/mocks" "github.com/uber/cadence/common/elasticsearch/query" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/mocks" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" ) type ESVisibilitySuite struct { suite.Suite // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions visibilityStore *esVisibilityStore mockESClient *esMocks.GenericClient mockProducer *mocks.KafkaProducer } var ( testIndex = "test-index" testDomain = "test-domain" testDomainID = "bfd5c907-f899-4baf-a7b2-2ab85e623ebd" testPageSize = 5 testEarliestTime = int64(1547596872371000000) testLatestTime = int64(2547596872371000000) testWorkflowType = "test-wf-type" testWorkflowID = "test-wid" testCloseStatus = int32(1) testRequest = &p.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, EarliestTime: time.Unix(0, testEarliestTime), LatestTime: time.Unix(0, testLatestTime), } testSearchResult = &p.InternalListWorkflowExecutionsResponse{} errTestESSearch = errors.New("ES error") testContextTimeout = 5 * time.Second esIndexMaxResultWindow = 3 ) type Source struct { Match map[string]struct { Query interface{} `json:"query"` } `json:"match"` } func TestESVisibilitySuite(t *testing.T) { suite.Run(t, new(ESVisibilitySuite)) } func (s *ESVisibilitySuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) s.mockESClient = &esMocks.GenericClient{} config := &service.Config{ ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(esIndexMaxResultWindow), ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), } s.mockProducer = &mocks.KafkaProducer{} mgr := NewElasticSearchVisibilityStore(s.mockESClient, testIndex, s.mockProducer, config, testlogger.New(s.Suite.T())) s.visibilityStore = mgr.(*esVisibilityStore) } func (s *ESVisibilitySuite) TearDownTest() { s.mockESClient.AssertExpectations(s.T()) s.mockProducer.AssertExpectations(s.T()) } func (s *ESVisibilitySuite) TestRecordWorkflowExecutionStarted() { // test non-empty request fields match request := &p.InternalRecordWorkflowExecutionStartedRequest{} request.DomainUUID = "domainID" request.WorkflowID = "wid" request.RunID = "rid" request.WorkflowTypeName = "wfType" request.StartTimestamp = time.Unix(0, int64(123)) request.ExecutionTimestamp = time.Unix(0, int64(321)) request.TaskID = int64(111) request.IsCron = true request.NumClusters = 2 request.ClusterAttributeScope = "region" request.ClusterAttributeName = "us-east-1" memoBytes := []byte(`test bytes`) request.Memo = p.NewDataBlob(memoBytes, constants.EncodingTypeThriftRW) request.ShardID = 1234 s.mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.Message) bool { fields := input.Fields s.Equal(request.DomainUUID, input.GetDomainID()) s.Equal(request.WorkflowID, input.GetWorkflowID()) s.Equal(request.RunID, input.GetRunID()) s.Equal(request.TaskID, input.GetVersion()) s.Equal(request.WorkflowTypeName, fields[es.WorkflowType].GetStringData()) s.Equal(request.StartTimestamp.UnixNano(), fields[es.StartTime].GetIntData()) s.Equal(request.ExecutionTimestamp.UnixNano(), fields[es.ExecutionTime].GetIntData()) s.Equal(memoBytes, fields[es.Memo].GetBinaryData()) s.Equal(string(constants.EncodingTypeThriftRW), fields[es.Encoding].GetStringData()) s.Equal(request.IsCron, fields[es.IsCron].GetBoolData()) s.Equal((int64)(request.NumClusters), fields[es.NumClusters].GetIntData()) s.Equal(request.ClusterAttributeScope, fields[es.ClusterAttributeScope].GetStringData()) s.Equal(request.ClusterAttributeName, fields[es.ClusterAttributeName].GetStringData()) s.Equal(indexer.VisibilityOperationRecordStarted, *input.VisibilityOperation) s.Equal((int64)(request.ShardID), fields[es.ShardID].GetIntData()) return true })).Return(nil).Once() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() err := s.visibilityStore.RecordWorkflowExecutionStarted(ctx, request) s.NoError(err) } func (s *ESVisibilitySuite) TestRecordWorkflowExecutionStarted_EmptyRequest() { // test empty request request := &p.InternalRecordWorkflowExecutionStartedRequest{ Memo: &p.DataBlob{}, } s.mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.Message) bool { s.Equal(indexer.MessageTypeIndex, input.GetMessageType()) _, ok := input.Fields[es.Memo] s.False(ok) _, ok = input.Fields[es.Encoding] s.False(ok) return true })).Return(nil).Once() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() err := s.visibilityStore.RecordWorkflowExecutionStarted(ctx, request) s.NoError(err) } func (s *ESVisibilitySuite) TestRecordWorkflowExecutionClosed() { // test non-empty request fields match request := &p.InternalRecordWorkflowExecutionClosedRequest{} request.DomainUUID = "domainID" request.WorkflowID = "wid" request.RunID = "rid" request.WorkflowTypeName = "wfType" request.StartTimestamp = time.Unix(0, int64(123)) request.ExecutionTimestamp = time.Unix(0, int64(321)) request.TaskID = int64(111) memoBytes := []byte(`test bytes`) request.Memo = p.NewDataBlob(memoBytes, constants.EncodingTypeThriftRW) request.CloseTimestamp = time.Unix(0, int64(999)) request.Status = types.WorkflowExecutionCloseStatusTerminated request.HistoryLength = int64(20) request.IsCron = false request.NumClusters = 2 request.UpdateTimestamp = time.Unix(0, int64(213)) request.ShardID = 1234 s.mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.Message) bool { fields := input.Fields s.Equal(request.DomainUUID, input.GetDomainID()) s.Equal(request.WorkflowID, input.GetWorkflowID()) s.Equal(request.RunID, input.GetRunID()) s.Equal(request.TaskID, input.GetVersion()) s.Equal(request.WorkflowTypeName, fields[es.WorkflowType].GetStringData()) s.Equal(request.StartTimestamp.UnixNano(), fields[es.StartTime].GetIntData()) s.Equal(request.ExecutionTimestamp.UnixNano(), fields[es.ExecutionTime].GetIntData()) s.Equal(memoBytes, fields[es.Memo].GetBinaryData()) s.Equal(string(constants.EncodingTypeThriftRW), fields[es.Encoding].GetStringData()) s.Equal(request.CloseTimestamp.UnixNano(), fields[es.CloseTime].GetIntData()) s.Equal(int64(request.Status), fields[es.CloseStatus].GetIntData()) s.Equal(request.HistoryLength, fields[es.HistoryLength].GetIntData()) s.Equal(request.IsCron, fields[es.IsCron].GetBoolData()) s.Equal((int64)(request.NumClusters), fields[es.NumClusters].GetIntData()) s.Equal(indexer.VisibilityOperationRecordClosed, *input.VisibilityOperation) s.Equal(request.UpdateTimestamp.UnixNano(), fields[es.UpdateTime].GetIntData()) s.Equal((int64)(request.ShardID), fields[es.ShardID].GetIntData()) return true })).Return(nil).Once() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() err := s.visibilityStore.RecordWorkflowExecutionClosed(ctx, request) s.NoError(err) } func (s *ESVisibilitySuite) TestRecordWorkflowExecutionClosed_EmptyRequest() { // test empty request request := &p.InternalRecordWorkflowExecutionClosedRequest{ Memo: &p.DataBlob{}, } s.mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.Message) bool { s.Equal(indexer.MessageTypeIndex, input.GetMessageType()) _, ok := input.Fields[es.Memo] s.False(ok) _, ok = input.Fields[es.Encoding] s.False(ok) return true })).Return(nil).Once() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() err := s.visibilityStore.RecordWorkflowExecutionClosed(ctx, request) s.NoError(err) } func (s *ESVisibilitySuite) TestRecordWorkflowExecutionUninitialized() { // test non-empty request fields match request := &p.InternalRecordWorkflowExecutionUninitializedRequest{} request.DomainUUID = "domainID" request.WorkflowID = "wid" request.RunID = "rid" request.WorkflowTypeName = "wfType" request.UpdateTimestamp = time.Unix(0, int64(213)) request.ShardID = 1234 s.mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.Message) bool { fields := input.Fields s.Equal(request.DomainUUID, input.GetDomainID()) s.Equal(request.WorkflowID, input.GetWorkflowID()) s.Equal(request.RunID, input.GetRunID()) s.Equal(request.WorkflowTypeName, fields[es.WorkflowType].GetStringData()) s.Equal(request.UpdateTimestamp.UnixNano(), fields[es.UpdateTime].GetIntData()) s.Equal(request.ShardID, fields[es.ShardID].GetIntData()) return true })).Return(nil).Once() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() err := s.visibilityStore.RecordWorkflowExecutionUninitialized(ctx, request) s.NoError(err) } func (s *ESVisibilitySuite) TestRecordWorkflowExecutionUninitialized_EmptyRequest() { // test empty request request := &p.InternalRecordWorkflowExecutionUninitializedRequest{} s.mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.Message) bool { s.Equal(indexer.MessageTypeCreate, input.GetMessageType()) return true })).Return(nil).Once() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() err := s.visibilityStore.RecordWorkflowExecutionUninitialized(ctx, request) s.NoError(err) } func (s *ESVisibilitySuite) TestListOpenWorkflowExecutions() { s.mockESClient.On("Search", mock.Anything, mock.MatchedBy(func(input *es.SearchRequest) bool { s.True(input.IsOpen) s.Equal(esIndexMaxResultWindow, input.MaxResultWindow) return true })).Return(testSearchResult, nil).Once() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() _, err := s.visibilityStore.ListOpenWorkflowExecutions(ctx, testRequest) s.NoError(err) s.mockESClient.On("Search", mock.Anything, mock.Anything).Return(nil, errTestESSearch).Once() _, err = s.visibilityStore.ListOpenWorkflowExecutions(ctx, testRequest) s.Error(err) _, ok := err.(*types.InternalServiceError) s.True(ok) s.True(strings.Contains(err.Error(), "ListOpenWorkflowExecutions failed")) } func (s *ESVisibilitySuite) TestListClosedWorkflowExecutions() { s.mockESClient.On("Search", mock.Anything, mock.MatchedBy(func(input *es.SearchRequest) bool { s.False(input.IsOpen) s.Equal(esIndexMaxResultWindow, input.MaxResultWindow) return true })).Return(testSearchResult, nil).Once() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() _, err := s.visibilityStore.ListClosedWorkflowExecutions(ctx, testRequest) s.NoError(err) s.mockESClient.On("Search", mock.Anything, mock.Anything).Return(nil, errTestESSearch).Once() _, err = s.visibilityStore.ListClosedWorkflowExecutions(ctx, testRequest) s.Error(err) _, ok := err.(*types.InternalServiceError) s.True(ok) s.True(strings.Contains(err.Error(), "ListClosedWorkflowExecutions failed")) } func (s *ESVisibilitySuite) TestListOpenWorkflowExecutionsByType() { s.mockESClient.On("Search", mock.Anything, mock.MatchedBy(func(input *es.SearchRequest) bool { s.True(input.IsOpen) source, err := matchQueryData(*input.MatchQuery) s.NoError(err) s.Equal(source.Match[es.WorkflowType].Query, testWorkflowType) s.Equal(esIndexMaxResultWindow, input.MaxResultWindow) return true })).Return(testSearchResult, nil).Once() request := &p.InternalListWorkflowExecutionsByTypeRequest{ InternalListWorkflowExecutionsRequest: *testRequest, WorkflowTypeName: testWorkflowType, } ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() _, err := s.visibilityStore.ListOpenWorkflowExecutionsByType(ctx, request) s.NoError(err) s.mockESClient.On("Search", mock.Anything, mock.Anything).Return(nil, errTestESSearch).Once() _, err = s.visibilityStore.ListOpenWorkflowExecutionsByType(ctx, request) s.Error(err) _, ok := err.(*types.InternalServiceError) s.True(ok) s.True(strings.Contains(err.Error(), "ListOpenWorkflowExecutionsByType failed")) } func (s *ESVisibilitySuite) TestListClosedWorkflowExecutionsByType() { s.mockESClient.On("Search", mock.Anything, mock.MatchedBy(func(input *es.SearchRequest) bool { s.False(input.IsOpen) source, err := matchQueryData(*input.MatchQuery) s.NoError(err) s.Equal(source.Match[es.WorkflowType].Query, testWorkflowType) s.Equal(esIndexMaxResultWindow, input.MaxResultWindow) return true })).Return(testSearchResult, nil).Once() request := &p.InternalListWorkflowExecutionsByTypeRequest{ InternalListWorkflowExecutionsRequest: *testRequest, WorkflowTypeName: testWorkflowType, } ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() _, err := s.visibilityStore.ListClosedWorkflowExecutionsByType(ctx, request) s.NoError(err) s.mockESClient.On("Search", mock.Anything, mock.Anything).Return(nil, errTestESSearch).Once() _, err = s.visibilityStore.ListClosedWorkflowExecutionsByType(ctx, request) s.Error(err) _, ok := err.(*types.InternalServiceError) s.True(ok) s.True(strings.Contains(err.Error(), "ListClosedWorkflowExecutionsByType failed")) } func (s *ESVisibilitySuite) TestListOpenWorkflowExecutionsByWorkflowID() { s.mockESClient.On("Search", mock.Anything, mock.MatchedBy(func(input *es.SearchRequest) bool { s.True(input.IsOpen) source, err := matchQueryData(*input.MatchQuery) s.NoError(err) s.Equal(source.Match[es.WorkflowID].Query, testWorkflowID) s.Equal(esIndexMaxResultWindow, input.MaxResultWindow) return true })).Return(testSearchResult, nil).Once() request := &p.InternalListWorkflowExecutionsByWorkflowIDRequest{ InternalListWorkflowExecutionsRequest: *testRequest, WorkflowID: testWorkflowID, } ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() _, err := s.visibilityStore.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) s.NoError(err) s.mockESClient.On("Search", mock.Anything, mock.Anything).Return(nil, errTestESSearch).Once() _, err = s.visibilityStore.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) s.Error(err) _, ok := err.(*types.InternalServiceError) s.True(ok) s.True(strings.Contains(err.Error(), "ListOpenWorkflowExecutionsByWorkflowID failed")) } func (s *ESVisibilitySuite) TestListClosedWorkflowExecutionsByWorkflowID() { s.mockESClient.On("Search", mock.Anything, mock.MatchedBy(func(input *es.SearchRequest) bool { s.False(input.IsOpen) source, err := matchQueryData(*input.MatchQuery) s.NoError(err) s.Equal(source.Match[es.WorkflowID].Query, testWorkflowID) s.Equal(esIndexMaxResultWindow, input.MaxResultWindow) return true })).Return(testSearchResult, nil).Once() request := &p.InternalListWorkflowExecutionsByWorkflowIDRequest{ InternalListWorkflowExecutionsRequest: *testRequest, WorkflowID: testWorkflowID, } ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() _, err := s.visibilityStore.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) s.NoError(err) s.mockESClient.On("Search", mock.Anything, mock.Anything).Return(nil, errTestESSearch).Once() _, err = s.visibilityStore.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) s.Error(err) _, ok := err.(*types.InternalServiceError) s.True(ok) s.True(strings.Contains(err.Error(), "ListClosedWorkflowExecutionsByWorkflowID failed")) } func (s *ESVisibilitySuite) TestListClosedWorkflowExecutionsByStatus() { s.mockESClient.On("Search", mock.Anything, mock.MatchedBy(func(input *es.SearchRequest) bool { s.False(input.IsOpen) source, err := matchQueryData(*input.MatchQuery) s.NoError(err) cs := (int32)(source.Match[es.CloseStatus].Query.(float64)) s.Equal(testCloseStatus, cs) s.Equal(esIndexMaxResultWindow, input.MaxResultWindow) return true })).Return(testSearchResult, nil).Once() closeStatus := types.WorkflowExecutionCloseStatus(testCloseStatus) request := &p.InternalListClosedWorkflowExecutionsByStatusRequest{ InternalListWorkflowExecutionsRequest: *testRequest, Status: closeStatus, } ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() _, err := s.visibilityStore.ListClosedWorkflowExecutionsByStatus(ctx, request) s.NoError(err) s.mockESClient.On("Search", mock.Anything, mock.Anything).Return(nil, errTestESSearch).Once() _, err = s.visibilityStore.ListClosedWorkflowExecutionsByStatus(ctx, request) s.Error(err) _, ok := err.(*types.InternalServiceError) s.True(ok) s.True(strings.Contains(err.Error(), "ListClosedWorkflowExecutionsByStatus failed")) } func (s *ESVisibilitySuite) TestGetNextPageToken() { token, err := es.GetNextPageToken([]byte{}) s.Equal(0, token.From) s.NoError(err) from := 5 input, err := es.SerializePageToken(&es.ElasticVisibilityPageToken{From: from}) s.NoError(err) token, err = es.GetNextPageToken(input) s.Equal(from, token.From) s.NoError(err) badInput := []byte("bad input") token, err = es.GetNextPageToken(badInput) s.Nil(token) s.Error(err) } func (s *ESVisibilitySuite) TestDeserializePageToken() { token := &es.ElasticVisibilityPageToken{From: 0} data, _ := es.SerializePageToken(token) result, err := es.DeserializePageToken(data) s.NoError(err) s.Equal(token, result) badInput := []byte("bad input") result, err = es.DeserializePageToken(badInput) s.Error(err) s.Nil(result) err, ok := err.(*types.BadRequestError) s.True(ok) s.True(strings.Contains(err.Error(), "unable to deserialize page token")) token = &es.ElasticVisibilityPageToken{SortValue: int64(64), TieBreaker: "unique"} data, _ = es.SerializePageToken(token) result, err = es.DeserializePageToken(data) s.NoError(err) resultSortValue, err := result.SortValue.(json.Number).Int64() s.NoError(err) s.Equal(token.SortValue.(int64), resultSortValue) } func (s *ESVisibilitySuite) TestSerializePageToken() { data, err := es.SerializePageToken(nil) s.NoError(err) s.True(len(data) > 0) token, err := es.DeserializePageToken(data) s.NoError(err) s.Equal(0, token.From) s.Equal(nil, token.SortValue) s.Equal("", token.TieBreaker) newToken := &es.ElasticVisibilityPageToken{From: 5} data, err = es.SerializePageToken(newToken) s.NoError(err) s.True(len(data) > 0) token, err = es.DeserializePageToken(data) s.NoError(err) s.Equal(newToken, token) sortTime := int64(123) tieBreaker := "unique" newToken = &es.ElasticVisibilityPageToken{SortValue: sortTime, TieBreaker: tieBreaker} data, err = es.SerializePageToken(newToken) s.NoError(err) s.True(len(data) > 0) token, err = es.DeserializePageToken(data) s.NoError(err) resultSortValue, err := token.SortValue.(json.Number).Int64() s.NoError(err) s.Equal(newToken.SortValue, resultSortValue) s.Equal(newToken.TieBreaker, token.TieBreaker) } // Move to client_v6_test // func (s *ESVisibilitySuite) TestConvertSearchResultToVisibilityRecord() { // data := []byte(`{"CloseStatus": 0, // "CloseTime": 1547596872817380000, // "DomainID": "bfd5c907-f899-4baf-a7b2-2ab85e623ebd", // "HistoryLength": 29, // "KafkaKey": "7-619", // "RunID": "e481009e-14b3-45ae-91af-dce6e2a88365", // "StartTime": 1547596872371000000, // "WorkflowID": "6bfbc1e5-6ce4-4e22-bbfb-e0faa9a7a604-1-2256", // "WorkflowType": "TestWorkflowExecute"}`) // source := (*json.RawMessage)(&data) // searchHit := &elastic.SearchHit{ // Source: source, // } // // // test for open // info := s.visibilityStore.convertSearchResultToVisibilityRecord(searchHit) // s.NotNil(info) // s.Equal("6bfbc1e5-6ce4-4e22-bbfb-e0faa9a7a604-1-2256", info.WorkflowID) // s.Equal("e481009e-14b3-45ae-91af-dce6e2a88365", info.RunID) // s.Equal("TestWorkflowExecute", info.TypeName) // s.Equal(int64(1547596872371000000), info.StartTime.UnixNano()) // // // test for close // info = s.visibilityStore.convertSearchResultToVisibilityRecord(searchHit) // s.NotNil(info) // s.Equal("6bfbc1e5-6ce4-4e22-bbfb-e0faa9a7a604-1-2256", info.WorkflowID) // s.Equal("e481009e-14b3-45ae-91af-dce6e2a88365", info.RunID) // s.Equal("TestWorkflowExecute", info.TypeName) // s.Equal(int64(1547596872371000000), info.StartTime.UnixNano()) // s.Equal(int64(1547596872817380000), info.CloseTime.UnixNano()) // s.Equal(workflow.WorkflowExecutionCloseStatusCompleted, *info.Status) // s.Equal(int64(29), info.HistoryLength) // // // test for error case // badData := []byte(`corrupted data`) // source = (*json.RawMessage)(&badData) // searchHit = &elastic.SearchHit{ // Source: source, // } // info = s.visibilityStore.convertSearchResultToVisibilityRecord(searchHit) // s.Nil(info) // } func (s *ESVisibilitySuite) TestShouldSearchAfter() { token := &es.ElasticVisibilityPageToken{} s.False(es.ShouldSearchAfter(token)) token.TieBreaker = "a" s.True(es.ShouldSearchAfter(token)) } // nolint func (s *ESVisibilitySuite) TestGetESQueryDSL() { request := &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, PageSize: 10, } token := &es.ElasticVisibilityPageToken{} v := s.visibilityStore request.Query = "" dsl, err := v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_all":{}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = "invaild query" dsl, err = v.getESQueryDSL(request, token) s.NotNil(err) s.Equal("", dsl) request.Query = `WorkflowID = 'wid'` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"WorkflowID":{"query":"wid"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `WorkflowID = 'wid' or WorkflowID = 'another-wid'` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"should":[{"match_phrase":{"WorkflowID":{"query":"wid"}}},{"match_phrase":{"WorkflowID":{"query":"another-wid"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `WorkflowID = 'wid' order by StartTime desc` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"WorkflowID":{"query":"wid"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `WorkflowID = 'wid' and CloseTime = missing` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"WorkflowID":{"query":"wid"}}},{"bool":{"must_not":{"exists":{"field":"CloseTime"}}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `WorkflowID = 'wid' or CloseTime = missing` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"should":[{"match_phrase":{"WorkflowID":{"query":"wid"}}},{"bool":{"must_not":{"exists":{"field":"CloseTime"}}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `WorkflowID = 'wid' and CloseStatus = "completed"` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"WorkflowID":{"query":"wid"}}},{"match_phrase":{"CloseStatus":{"query":"0"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `CloseTime = missing order by CloseTime desc` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"bool":{"must_not":{"exists":{"field":"CloseTime"}}}}]}}]}},"from":0,"size":10,"sort":[{"CloseTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `StartTime = "2018-06-07T15:04:05-08:00"` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"StartTime":{"query":"1528412645000000000"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `WorkflowID = 'wid' and StartTime > "2018-06-07T15:04:05+00:00"` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"WorkflowID":{"query":"wid"}}},{"range":{"StartTime":{"gt":"1528383845000000000"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `ExecutionTime < 1000` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"range":{"ExecutionTime":{"gt":"0"}}},{"bool":{"must":[{"range":{"ExecutionTime":{"lt":"1000"}}}]}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `ExecutionTime < 1000 or ExecutionTime > 2000` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"range":{"ExecutionTime":{"gt":"0"}}},{"bool":{"should":[{"range":{"ExecutionTime":{"lt":"1000"}}},{"range":{"ExecutionTime":{"gt":"2000"}}}]}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `order by ExecutionTime desc` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_all":{}}]}}]}},"from":0,"size":10,"sort":[{"ExecutionTime":"desc"},{"RunID":"desc"}]}`, dsl) request.Query = `order by StartTime desc, CloseTime desc` _, err = v.getESQueryDSL(request, token) s.Equal(errors.New("only one field can be used to sort"), err) request.Query = `order by CustomStringField desc` _, err = v.getESQueryDSL(request, token) s.Equal(errors.New("not able to sort by IndexedValueTypeString field, use IndexedValueTypeKeyword field"), err) request.Query = `order by CustomIntField asc` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_all":{}}]}}]}},"from":0,"size":10,"sort":[{"CustomIntField":"asc"},{"RunID":"desc"}]}`, dsl) request.Query = `ExecutionTime < "unable to parse"` _, err = v.getESQueryDSL(request, token) s.Error(err) token = s.getTokenHelper(1) request.Query = `WorkflowID = 'wid'` dsl, err = v.getESQueryDSL(request, token) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"WorkflowID":{"query":"wid"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}],"search_after":[1,"t"]}`, dsl) // invalid union injection request.Query = `WorkflowID = 'wid' union select * from dummy` _, err = v.getESQueryDSL(request, token) s.NotNil(err) } func (s *ESVisibilitySuite) TestGetESQueryDSLForScan() { request := &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, PageSize: 10, } request.Query = `WorkflowID = 'wid' order by StartTime desc` dsl, err := getESQueryDSLForScan(request) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"WorkflowID":{"query":"wid"}}}]}}]}},"from":0,"size":10}`, dsl) request.Query = `WorkflowID = 'wid'` dsl, err = getESQueryDSLForScan(request) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"WorkflowID":{"query":"wid"}}}]}}]}},"from":0,"size":10}`, dsl) request.Query = `CloseTime = missing and (ExecutionTime >= "2019-08-27T15:04:05+00:00" or StartTime <= "2018-06-07T15:04:05+00:00")` dsl, err = getESQueryDSLForScan(request) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"range":{"ExecutionTime":{"gt":"0"}}},{"bool":{"must":[{"bool":{"must_not":{"exists":{"field":"CloseTime"}}}},{"bool":{"should":[{"range":{"ExecutionTime":{"from":"1566918245000000000"}}},{"range":{"StartTime":{"to":"1528383845000000000"}}}]}}]}}]}}]}},"from":0,"size":10}`, dsl) request.Query = `ExecutionTime < 1000 and ExecutionTime > 500` dsl, err = getESQueryDSLForScan(request) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"range":{"ExecutionTime":{"gt":"0"}}},{"bool":{"must":[{"range":{"ExecutionTime":{"lt":"1000"}}},{"range":{"ExecutionTime":{"gt":"500"}}}]}}]}}]}},"from":0,"size":10}`, dsl) } func (s *ESVisibilitySuite) TestGetESQueryDSLForCount() { request := &p.CountWorkflowExecutionsRequest{ DomainUUID: testDomainID, } // empty query dsl, err := getESQueryDSLForCount(request) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_all":{}}]}}]}}}`, dsl) request.Query = `WorkflowID = 'wid' order by StartTime desc` dsl, err = getESQueryDSLForCount(request) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"WorkflowID":{"query":"wid"}}}]}}]}}}`, dsl) request.Query = `CloseTime < "2018-06-07T15:04:05+07:00" and StartTime > "2018-05-04T16:00:00+07:00" and ExecutionTime >= "2018-05-05T16:00:00+07:00"` dsl, err = getESQueryDSLForCount(request) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"range":{"ExecutionTime":{"gt":"0"}}},{"bool":{"must":[{"range":{"CloseTime":{"lt":"1528358645000000000"}}},{"range":{"StartTime":{"gt":"1525424400000000000"}}},{"range":{"ExecutionTime":{"from":"1525510800000000000"}}}]}}]}}]}}}`, dsl) request.Query = `ExecutionTime < 1000` dsl, err = getESQueryDSLForCount(request) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"range":{"ExecutionTime":{"gt":"0"}}},{"bool":{"must":[{"range":{"ExecutionTime":{"lt":"1000"}}}]}}]}}]}}}`, dsl) request.Query = `StartTime = missing and UpdateTime >= "2022-10-04T16:00:00+07:00"` dsl, err = getESQueryDSLForCount(request) s.Nil(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"bool":{"must_not":{"exists":{"field":"StartTime"}}}},{"range":{"UpdateTime":{"from":"1664874000000000000"}}}]}}]}}}`, dsl) } func (s *ESVisibilitySuite) TestAddDomainToQuery() { dsl := fastjson.MustParse(`{}`) dslStr := dsl.String() addDomainToQuery(dsl, "") s.Equal(dslStr, dsl.String()) dsl = fastjson.MustParse(`{"query":{"bool":{"must":[{"match_all":{}}]}}}`) addDomainToQuery(dsl, testDomainID) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_all":{}}]}}]}}}`, dsl.String()) } func (s *ESVisibilitySuite) TestListWorkflowExecutions() { s.mockESClient.On("SearchByQuery", mock.Anything, mock.MatchedBy(func(input *es.SearchByQueryRequest) bool { s.True(strings.Contains(input.Query, `{"match_phrase":{"CloseStatus":{"query":"5"}}}`)) s.Equal(esIndexMaxResultWindow, input.MaxResultWindow) return true })).Return(testSearchResult, nil).Once() request := &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: 10, Query: `CloseStatus = 5`, } ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() _, err := s.visibilityStore.ListWorkflowExecutions(ctx, request) s.NoError(err) s.mockESClient.On("SearchByQuery", mock.Anything, mock.Anything).Return(nil, errTestESSearch).Once() _, err = s.visibilityStore.ListWorkflowExecutions(ctx, request) s.Error(err) _, ok := err.(*types.InternalServiceError) s.True(ok) s.True(strings.Contains(err.Error(), "ListWorkflowExecutions failed")) request.Query = `invalid query` _, err = s.visibilityStore.ListWorkflowExecutions(context.Background(), request) s.Error(err) _, ok = err.(*types.BadRequestError) s.True(ok) s.True(strings.Contains(err.Error(), "Error when parse query")) } func (s *ESVisibilitySuite) TestListWorkflowExecutionsWithLike() { s.mockESClient.On("SearchByQuery", mock.Anything, mock.MatchedBy(func(input *es.SearchByQueryRequest) bool { s.True(strings.Contains(input.Query, `{"wildcard":{"WorkflowID":{"value":"wid*","case_insensitive":true}}}`)) s.Equal(esIndexMaxResultWindow, input.MaxResultWindow) return true })).Return(testSearchResult, nil).Once() request := &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: 10, Query: `WorkflowID like 'wid'`, } ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() _, err := s.visibilityStore.ListWorkflowExecutions(ctx, request) s.NoError(err) } func (s *ESVisibilitySuite) TestScanWorkflowExecutions() { // test first page s.mockESClient.On("ScanByQuery", mock.Anything, mock.MatchedBy(func(input *es.ScanByQueryRequest) bool { s.True(strings.Contains(input.Query, `{"match_phrase":{"CloseStatus":{"query":"5"}}}`)) return true })).Return(testSearchResult, nil).Once() request := &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: 10, Query: `CloseStatus = 5`, } ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() _, err := s.visibilityStore.ScanWorkflowExecutions(ctx, request) s.NoError(err) // test bad request request.Query = `invalid query` _, err = s.visibilityStore.ScanWorkflowExecutions(ctx, request) s.Error(err) _, ok := err.(*types.BadRequestError) s.True(ok) s.True(strings.Contains(err.Error(), "Error when parse query")) // test internal error request.Query = `CloseStatus = 5` s.mockESClient.On("ScanByQuery", mock.Anything, mock.Anything).Return(nil, errTestESSearch).Once() _, err = s.visibilityStore.ScanWorkflowExecutions(ctx, request) s.Error(err) _, ok = err.(*types.InternalServiceError) s.True(ok) s.True(strings.Contains(err.Error(), "ScanWorkflowExecutions failed")) } func (s *ESVisibilitySuite) TestCountWorkflowExecutions() { s.mockESClient.On("CountByQuery", mock.Anything, testIndex, mock.MatchedBy(func(input string) bool { s.True(strings.Contains(input, `{"match_phrase":{"CloseStatus":{"query":"5"}}}`)) return true })).Return(int64(1), nil).Once() request := &p.CountWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, Query: `CloseStatus = 5`, } ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() resp, err := s.visibilityStore.CountWorkflowExecutions(ctx, request) s.NoError(err) s.Equal(int64(1), resp.Count) // test internal error s.mockESClient.On("CountByQuery", mock.Anything, testIndex, mock.Anything).Return(int64(0), errTestESSearch).Once() _, err = s.visibilityStore.CountWorkflowExecutions(ctx, request) s.Error(err) _, ok := err.(*types.InternalServiceError) s.True(ok) s.True(strings.Contains(err.Error(), "CountWorkflowExecutions failed")) // test bad request request.Query = `invalid query` _, err = s.visibilityStore.CountWorkflowExecutions(ctx, request) s.Error(err) _, ok = err.(*types.BadRequestError) s.True(ok) s.True(strings.Contains(err.Error(), "Error when parse query")) } func (s *ESVisibilitySuite) TestTimeProcessFunc() { cases := []struct { key string value string }{ {key: "from", value: "1528358645000000000"}, {key: "to", value: "2018-06-07T15:04:05+07:00"}, {key: "gt", value: "some invalid time string"}, {key: "unrelatedKey", value: "should not be modified"}, } expected := []struct { value string returnErr bool }{ {value: `"1528358645000000000"`, returnErr: false}, {value: `"1528358645000000000"`, returnErr: false}, {value: "", returnErr: true}, {value: `"should not be modified"`, returnErr: false}, } for i, testCase := range cases { value := fastjson.MustParse(fmt.Sprintf(`{"%s": "%s"}`, testCase.key, testCase.value)) err := timeProcessFunc(nil, "", value) if expected[i].returnErr { s.Error(err) continue } s.Equal(expected[i].value, value.Get(testCase.key).String()) } } func (s *ESVisibilitySuite) TestCloseStatusProcessFunc() { cases := []struct { key string value string }{ {key: "from", value: types.WorkflowExecutionCloseStatusTerminated.String()}, {key: "to", value: strings.ToLower(types.WorkflowExecutionCloseStatusContinuedAsNew.String())}, {key: "gt", value: fmt.Sprintf(`%d`, types.WorkflowExecutionCloseStatusFailed)}, {key: "query", value: types.WorkflowExecutionCloseStatusCompleted.String()}, {key: "query", value: fmt.Sprintf(`%d`, types.WorkflowExecutionCloseStatusCanceled)}, {key: "query", value: strings.ToTitle(strings.ToLower(types.WorkflowExecutionCloseStatusTimedOut.String()))}, {key: "query", value: "timeout"}, {key: "unrelatedKey", value: "should not be modified"}, } expected := []struct { value string returnErr bool }{ {value: fmt.Sprintf(`"%d"`, types.WorkflowExecutionCloseStatusTerminated), returnErr: false}, {value: fmt.Sprintf(`"%d"`, types.WorkflowExecutionCloseStatusContinuedAsNew), returnErr: false}, {value: fmt.Sprintf(`"%d"`, types.WorkflowExecutionCloseStatusFailed), returnErr: false}, {value: fmt.Sprintf(`"%d"`, types.WorkflowExecutionCloseStatusCompleted), returnErr: false}, {value: fmt.Sprintf(`"%d"`, types.WorkflowExecutionCloseStatusCanceled), returnErr: false}, {value: fmt.Sprintf(`"%d"`, types.WorkflowExecutionCloseStatusTimedOut), returnErr: false}, {value: "", returnErr: true}, {value: `"should not be modified"`, returnErr: false}, } for i, testCase := range cases { value := fastjson.MustParse(fmt.Sprintf(`{"%s": "%s"}`, testCase.key, testCase.value)) err := closeStatusProcessFunc(nil, "", value) if expected[i].returnErr { s.Error(err) continue } s.Equal(expected[i].value, value.Get(testCase.key).String()) } } func (s *ESVisibilitySuite) TestProcessAllValuesForKey() { testJSONStr := `{ "arrayKey": [ {"testKey1": "value1"}, {"testKey2": "value2"}, {"key3": "value3"} ], "key4": { "testKey5": "value5", "key6": "value6" }, "testArrayKey": [ {"testKey7": "should not be processed"} ], "testKey8": "value8" }` dsl := fastjson.MustParse(testJSONStr) testKeyFilter := func(key string) bool { return strings.HasPrefix(key, "test") } processedValue := make(map[string]struct{}) testProcessFunc := func(obj *fastjson.Object, key string, value *fastjson.Value) error { s.Equal(obj.Get(key), value) processedValue[value.String()] = struct{}{} return nil } s.NoError(processAllValuesForKey(dsl, testKeyFilter, testProcessFunc)) expectedProcessedValue := map[string]struct{}{ `"value1"`: {}, `"value2"`: {}, `"value5"`: {}, `[{"testKey7":"should not be processed"}]`: {}, `"value8"`: {}, } s.Equal(expectedProcessedValue, processedValue) } func (s *ESVisibilitySuite) TestGetFieldType() { s.Equal(types.IndexedValueTypeInt, s.visibilityStore.getFieldType("StartTime")) s.Equal(types.IndexedValueTypeDatetime, s.visibilityStore.getFieldType("Attr.CustomDatetimeField")) } func (s *ESVisibilitySuite) TestGetValueOfSearchAfterInJSON() { v := s.visibilityStore // Int field token := s.getTokenHelper(123) sortField := definition.CustomIntField res, err := v.getValueOfSearchAfterInJSON(token, sortField) s.Nil(err) s.Equal(`[123, "t"]`, res) jsonData := `{"SortValue": -9223372036854776000, "TieBreaker": "t"}` dec := json.NewDecoder(strings.NewReader(jsonData)) dec.UseNumber() err = dec.Decode(&token) s.Nil(err) res, err = v.getValueOfSearchAfterInJSON(token, sortField) s.Nil(err) s.Equal(`[-9223372036854775808, "t"]`, res) jsonData = `{"SortValue": 9223372036854776000, "TieBreaker": "t"}` dec = json.NewDecoder(strings.NewReader(jsonData)) dec.UseNumber() err = dec.Decode(&token) s.Nil(err) res, err = v.getValueOfSearchAfterInJSON(token, sortField) s.Nil(err) s.Equal(`[9223372036854775807, "t"]`, res) // Double field token = s.getTokenHelper(1.11) sortField = definition.CustomDoubleField res, err = v.getValueOfSearchAfterInJSON(token, sortField) s.Nil(err) s.Equal(`[1.11, "t"]`, res) jsonData = `{"SortValue": "-Infinity", "TieBreaker": "t"}` dec = json.NewDecoder(strings.NewReader(jsonData)) dec.UseNumber() err = dec.Decode(&token) s.Nil(err) res, err = v.getValueOfSearchAfterInJSON(token, sortField) s.Nil(err) s.Equal(`["-Infinity", "t"]`, res) // Keyword field token = s.getTokenHelper("keyword") sortField = definition.CustomKeywordField res, err = v.getValueOfSearchAfterInJSON(token, sortField) s.Nil(err) s.Equal(`["keyword", "t"]`, res) token = s.getTokenHelper(nil) res, err = v.getValueOfSearchAfterInJSON(token, sortField) s.Nil(err) s.Equal(`[null, "t"]`, res) } func (s *ESVisibilitySuite) getTokenHelper(sortValue interface{}) *es.ElasticVisibilityPageToken { token := &es.ElasticVisibilityPageToken{ SortValue: sortValue, TieBreaker: "t", } encoded, _ := es.SerializePageToken(token) // necessary, otherwise token is fake and not json decoded token, _ = es.DeserializePageToken(encoded) return token } func (s *ESVisibilitySuite) TestCleanDSL() { // dsl without `field` dsl := `{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"2b8344db-0ed6-47a4-92fd-bdeb6ead93e3"}}},{"bool":{"must":[{"match_phrase":{"Attr.CustomIntField":{"query":"1"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}` res := cleanDSL(dsl) s.Equal(dsl, res) // dsl with `field` dsl = `{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"2b8344db-0ed6-47a4-92fd-bdeb6ead93e3"}}},{"bool":{"must":[{"range":{"` + "`Attr.CustomIntField`" + `":{"from":"1","to":"5"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}` res = cleanDSL(dsl) expected := `{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"2b8344db-0ed6-47a4-92fd-bdeb6ead93e3"}}},{"bool":{"must":[{"range":{"Attr.CustomIntField":{"from":"1","to":"5"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}` s.Equal(expected, res) // dsl with mixed dsl = `{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"2b8344db-0ed6-47a4-92fd-bdeb6ead93e3"}}},{"bool":{"must":[{"range":{"` + "`Attr.CustomIntField`" + `":{"from":"1","to":"5"}}},{"range":{"` + "`Attr.CustomDoubleField`" + `":{"from":"1.0","to":"2.0"}}},{"range":{"StartTime":{"gt":"0"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}` res = cleanDSL(dsl) expected = `{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"2b8344db-0ed6-47a4-92fd-bdeb6ead93e3"}}},{"bool":{"must":[{"range":{"Attr.CustomIntField":{"from":"1","to":"5"}}},{"range":{"Attr.CustomDoubleField":{"from":"1.0","to":"2.0"}}},{"range":{"StartTime":{"gt":"0"}}}]}}]}},"from":0,"size":10,"sort":[{"StartTime":"desc"},{"RunID":"desc"}]}` s.Equal(expected, res) } func matchQueryData(matchQuery query.MatchQuery) (Source, error) { source, err := matchQuery.Source() if err != nil { return Source{}, err } d, err := json.Marshal(source) if err != nil { return Source{}, err } to := Source{} if err = json.Unmarshal(d, &to); err != nil { return Source{}, err } return to, nil } func (s *ESVisibilitySuite) TestGetCustomizedDSLFromSQL() { sql := "select * from dummy where WorkflowID like 'wid' LIMIT 100" dsl, err := getCustomizedDSLFromSQL(sql, testDomainID) s.NoError(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"wildcard":{"WorkflowID":{"value":"wid*","case_insensitive":true}}}]}}]}},"from":0,"size":100}`, dsl.String()) sql = "select * from dummy where Attr.CustomizedKeywordField like 'test'" dsl, err = getCustomizedDSLFromSQL(sql, testDomainID) s.NoError(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"wildcard":{"Attr.CustomizedKeywordField":{"value":"test*","case_insensitive":true}}}]}}]}},"from":0,"size":1}`, dsl.String()) sql = "select * from dummy where WorkflowID = 'wid'" dsl, err = getCustomizedDSLFromSQL(sql, testDomainID) s.NoError(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"WorkflowID":{"query":"wid"}}}]}}]}},"from":0,"size":1}`, dsl.String()) sql = "select * from dummy where Attr.CustomizedKeywordField = 'test'" dsl, err = getCustomizedDSLFromSQL(sql, testDomainID) s.NoError(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"Attr.CustomizedKeywordField":{"query":"test"}}}]}}]}},"from":0,"size":1}`, dsl.String()) sql = "select * from dummy where WorkflowID like 'wid' and Attr.CustomizedKeywordField = 'test'" dsl, err = getCustomizedDSLFromSQL(sql, testDomainID) s.NoError(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"Attr.CustomizedKeywordField":{"query":"test"}}},{"wildcard":{"WorkflowID":{"value":"wid*","case_insensitive":true}}}]}}]}},"from":0,"size":1}`, dsl.String()) sql = "select * from dummy where WorkflowID like 'like' and Attr.CustomizedKeywordField = 'like'" dsl, err = getCustomizedDSLFromSQL(sql, testDomainID) s.NoError(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"match_phrase":{"Attr.CustomizedKeywordField":{"query":"like"}}},{"wildcard":{"WorkflowID":{"value":"like*","case_insensitive":true}}}]}}]}},"from":0,"size":1}`, dsl.String()) sql = "select * from dummy where WorkflowID like 'wid' and Attr.CustomizedKeywordField like 'test'" dsl, err = getCustomizedDSLFromSQL(sql, testDomainID) s.NoError(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"must":[{"wildcard":{"WorkflowID":{"value":"wid*","case_insensitive":true}}},{"wildcard":{"Attr.CustomizedKeywordField":{"value":"test*","case_insensitive":true}}}]}}]}},"from":0,"size":1}`, dsl.String()) sql = "select * from dummy where WorkflowID = 'wid' OR RunID = 'runid'" dsl, err = getCustomizedDSLFromSQL(sql, testDomainID) s.NoError(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"should":[{"match_phrase":{"WorkflowID":{"query":"wid"}}},{"match_phrase":{"RunID":{"query":"runid"}}}]}}]}},"from":0,"size":1}`, dsl.String()) sql = "select * from dummy where WorkflowID like 'wid' OR Attr.CustomizedKeywordField like 'test'" dsl, err = getCustomizedDSLFromSQL(sql, testDomainID) s.NoError(err) s.Equal(`{"query":{"bool":{"must":[{"match_phrase":{"DomainID":{"query":"bfd5c907-f899-4baf-a7b2-2ab85e623ebd"}}},{"bool":{"should":[{"wildcard":{"WorkflowID":{"value":"wid*","case_insensitive":true}}},{"wildcard":{"Attr.CustomizedKeywordField":{"value":"test*","case_insensitive":true}}}]}}]}},"from":0,"size":1}`, dsl.String()) } ================================================ FILE: common/persistence/errors.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "errors" "fmt" ) type ( // TimeoutError is returned when a write operation fails due to a timeout TimeoutError struct { Msg string } // DBUnavailableError is returned when the database is unavailable, could be for various reasons. DBUnavailableError struct { Msg string } // TransactionSizeLimitError is returned when the transaction size is too large TransactionSizeLimitError struct { Msg string } // InvalidPersistenceRequestError represents invalid request to persistence InvalidPersistenceRequestError struct { Msg string } // CurrentWorkflowConditionFailedError represents a failed conditional update for current workflow record CurrentWorkflowConditionFailedError struct { Msg string } // ConditionFailedError represents a failed conditional update for execution record ConditionFailedError struct { Msg string } // ShardAlreadyExistError is returned when conditionally creating a shard fails ShardAlreadyExistError struct { Msg string } // ShardOwnershipLostError is returned when conditional update fails due to RangeID for the shard ShardOwnershipLostError struct { ShardID int Msg string } // WorkflowExecutionAlreadyStartedError is returned when creating a new workflow failed. WorkflowExecutionAlreadyStartedError struct { Msg string StartRequestID string RunID string State int CloseStatus int LastWriteVersion int64 } DuplicateRequestError struct { RequestType WorkflowRequestType RunID string } ) func (e *DuplicateRequestError) Error() string { return fmt.Sprintf("Request has already been applied to runID: %s", e.RunID) } func (e *InvalidPersistenceRequestError) Error() string { return e.Msg } func (e *CurrentWorkflowConditionFailedError) Error() string { return e.Msg } func (e *ConditionFailedError) Error() string { return e.Msg } func (e *ShardAlreadyExistError) Error() string { return e.Msg } func (e *ShardOwnershipLostError) Error() string { return e.Msg } func (e *WorkflowExecutionAlreadyStartedError) Error() string { return e.Msg } func (e *TimeoutError) Error() string { return e.Msg } func (e *DBUnavailableError) Error() string { return e.Msg } func (e *TransactionSizeLimitError) Error() string { return e.Msg } func AsDuplicateRequestError(err error) (*DuplicateRequestError, bool) { var e *DuplicateRequestError if errors.As(err, &e) { return e, true } return nil, false } ================================================ FILE: common/persistence/errors_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestAsDuplicateRequestError(t *testing.T) { testCases := []struct { name string err error expectedErr *DuplicateRequestError ok bool }{ { name: "unwrapped", err: &DuplicateRequestError{RunID: "a"}, expectedErr: &DuplicateRequestError{RunID: "a"}, ok: true, }, { name: "wrapped", err: fmt.Errorf("%w", &DuplicateRequestError{RunID: "b"}), expectedErr: &DuplicateRequestError{RunID: "b"}, ok: true, }, { name: "not same type", err: fmt.Errorf("adasdf"), ok: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { e, ok := AsDuplicateRequestError(tc.err) assert.Equal(t, tc.ok, ok) if ok { assert.Equal(t, tc.expectedErr, e) } }) } } ================================================ FILE: common/persistence/execution_manager.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "context" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) type ( // executionManagerImpl implements ExecutionManager based on ExecutionStore, statsComputer and PayloadSerializer executionManagerImpl struct { serializer PayloadSerializer persistence ExecutionStore statsComputer statsComputer logger log.Logger timeSrc clock.TimeSource dc *DynamicConfiguration } ) var _ ExecutionManager = (*executionManagerImpl)(nil) // NewExecutionManagerImpl returns new ExecutionManager func NewExecutionManagerImpl( persistence ExecutionStore, logger log.Logger, serializer PayloadSerializer, dc *DynamicConfiguration, ) ExecutionManager { return &executionManagerImpl{ serializer: serializer, persistence: persistence, statsComputer: statsComputer{}, logger: logger, timeSrc: clock.NewRealTimeSource(), dc: dc, } } func (m *executionManagerImpl) GetName() string { return m.persistence.GetName() } func (m *executionManagerImpl) GetShardID() int { return m.persistence.GetShardID() } // The below three APIs are related to serialization/deserialization func (m *executionManagerImpl) GetWorkflowExecution( ctx context.Context, request *GetWorkflowExecutionRequest, ) (*GetWorkflowExecutionResponse, error) { internalRequest := &InternalGetWorkflowExecutionRequest{ DomainID: request.DomainID, Execution: request.Execution, RangeID: request.RangeID, } response, err := m.persistence.GetWorkflowExecution(ctx, internalRequest) if err != nil { return nil, err } newResponse := &GetWorkflowExecutionResponse{ State: &WorkflowMutableState{ TimerInfos: response.State.TimerInfos, RequestCancelInfos: response.State.RequestCancelInfos, SignalInfos: response.State.SignalInfos, SignalRequestedIDs: response.State.SignalRequestedIDs, ReplicationState: response.State.ReplicationState, // TODO: remove this after all 2DC workflows complete Checksum: response.State.Checksum, }, } newResponse.State.ActivityInfos, err = m.DeserializeActivityInfos(response.State.ActivityInfos) if err != nil { return nil, err } newResponse.State.ChildExecutionInfos, err = m.DeserializeChildExecutionInfos(response.State.ChildExecutionInfos) if err != nil { return nil, err } newResponse.State.BufferedEvents, err = m.DeserializeBufferedEvents(response.State.BufferedEvents) if err != nil { return nil, err } newResponse.State.ExecutionInfo, newResponse.State.ExecutionStats, err = m.DeserializeExecutionInfo(response.State.ExecutionInfo) if err != nil { return nil, err } versionHistories, err := m.DeserializeVersionHistories(response.State.VersionHistories) if err != nil { return nil, err } newResponse.State.VersionHistories = versionHistories newResponse.MutableStateStats = m.statsComputer.computeMutableStateStats(response) if len(newResponse.State.Checksum.Value) == 0 { newResponse.State.Checksum, err = m.serializer.DeserializeChecksum(response.State.ChecksumData) if err != nil { return nil, err } } return newResponse, nil } func (m *executionManagerImpl) DeserializeExecutionInfo( info *InternalWorkflowExecutionInfo, ) (*WorkflowExecutionInfo, *ExecutionStats, error) { completionEvent, err := m.serializer.DeserializeEvent(info.CompletionEvent) if err != nil { return nil, nil, err } autoResetPoints, err := m.serializer.DeserializeResetPoints(info.AutoResetPoints) if err != nil { return nil, nil, err } activeClusterSelectionPolicy, err := m.serializer.DeserializeActiveClusterSelectionPolicy(info.ActiveClusterSelectionPolicy) if err != nil { return nil, nil, err } newInfo := &WorkflowExecutionInfo{ CompletionEvent: completionEvent, DomainID: info.DomainID, WorkflowID: info.WorkflowID, RunID: info.RunID, FirstExecutionRunID: info.FirstExecutionRunID, ParentDomainID: info.ParentDomainID, ParentWorkflowID: info.ParentWorkflowID, ParentRunID: info.ParentRunID, InitiatedID: info.InitiatedID, CompletionEventBatchID: info.CompletionEventBatchID, TaskList: info.TaskList, TaskListKind: info.TaskListKind, IsCron: len(info.CronSchedule) > 0, WorkflowTypeName: info.WorkflowTypeName, WorkflowTimeout: int32(info.WorkflowTimeout.Seconds()), DecisionStartToCloseTimeout: int32(info.DecisionStartToCloseTimeout.Seconds()), ExecutionContext: info.ExecutionContext, State: info.State, CloseStatus: info.CloseStatus, LastFirstEventID: info.LastFirstEventID, LastEventTaskID: info.LastEventTaskID, NextEventID: info.NextEventID, LastProcessedEvent: info.LastProcessedEvent, StartTimestamp: info.StartTimestamp, LastUpdatedTimestamp: info.LastUpdatedTimestamp, CreateRequestID: info.CreateRequestID, SignalCount: info.SignalCount, DecisionVersion: info.DecisionVersion, DecisionScheduleID: info.DecisionScheduleID, DecisionStartedID: info.DecisionStartedID, DecisionRequestID: info.DecisionRequestID, DecisionTimeout: int32(info.DecisionTimeout.Seconds()), DecisionAttempt: info.DecisionAttempt, DecisionStartedTimestamp: info.DecisionStartedTimestamp.UnixNano(), DecisionScheduledTimestamp: info.DecisionScheduledTimestamp.UnixNano(), DecisionOriginalScheduledTimestamp: info.DecisionOriginalScheduledTimestamp.UnixNano(), CancelRequested: info.CancelRequested, CancelRequestID: info.CancelRequestID, StickyTaskList: info.StickyTaskList, StickyScheduleToStartTimeout: int32(info.StickyScheduleToStartTimeout.Seconds()), ClientLibraryVersion: info.ClientLibraryVersion, ClientFeatureVersion: info.ClientFeatureVersion, ClientImpl: info.ClientImpl, Attempt: info.Attempt, HasRetryPolicy: info.HasRetryPolicy, InitialInterval: int32(info.InitialInterval.Seconds()), BackoffCoefficient: info.BackoffCoefficient, MaximumInterval: int32(info.MaximumInterval.Seconds()), ExpirationTime: info.ExpirationTime, MaximumAttempts: info.MaximumAttempts, NonRetriableErrors: info.NonRetriableErrors, BranchToken: info.BranchToken, CronSchedule: info.CronSchedule, CronOverlapPolicy: info.CronOverlapPolicy, ExpirationSeconds: int32(info.ExpirationInterval.Seconds()), AutoResetPoints: autoResetPoints, SearchAttributes: info.SearchAttributes, Memo: info.Memo, PartitionConfig: info.PartitionConfig, ActiveClusterSelectionPolicy: activeClusterSelectionPolicy, } newStats := &ExecutionStats{ HistorySize: info.HistorySize, } return newInfo, newStats, nil } func (m *executionManagerImpl) DeserializeBufferedEvents( blobs []*DataBlob, ) ([]*types.HistoryEvent, error) { events := make([]*types.HistoryEvent, 0) for _, b := range blobs { history, err := m.serializer.DeserializeBatchEvents(b) if err != nil { return nil, err } events = append(events, history...) } return events, nil } func (m *executionManagerImpl) DeserializeChildExecutionInfos( infos map[int64]*InternalChildExecutionInfo, ) (map[int64]*ChildExecutionInfo, error) { newInfos := make(map[int64]*ChildExecutionInfo) for k, v := range infos { initiatedEvent, err := m.serializer.DeserializeEvent(v.InitiatedEvent) if err != nil { return nil, err } startedEvent, err := m.serializer.DeserializeEvent(v.StartedEvent) if err != nil { return nil, err } c := &ChildExecutionInfo{ InitiatedEvent: initiatedEvent, StartedEvent: startedEvent, Version: v.Version, InitiatedID: v.InitiatedID, InitiatedEventBatchID: v.InitiatedEventBatchID, StartedID: v.StartedID, StartedWorkflowID: v.StartedWorkflowID, StartedRunID: v.StartedRunID, CreateRequestID: v.CreateRequestID, DomainID: v.DomainID, DomainNameDEPRECATED: v.DomainNameDEPRECATED, WorkflowTypeName: v.WorkflowTypeName, ParentClosePolicy: v.ParentClosePolicy, } // Needed for backward compatibility reason. // ChildWorkflowExecutionStartedEvent was only used by transfer queue processing of StartChildWorkflow. // Updated the code to instead directly read WorkflowId and RunId from mutable state // Existing mutable state won't have those values set so instead use started event to set StartedWorkflowID and // StartedRunID on the mutable state before passing it to application if startedEvent != nil && startedEvent.ChildWorkflowExecutionStartedEventAttributes != nil && startedEvent.ChildWorkflowExecutionStartedEventAttributes.WorkflowExecution != nil { startedExecution := startedEvent.ChildWorkflowExecutionStartedEventAttributes.WorkflowExecution c.StartedWorkflowID = startedExecution.GetWorkflowID() c.StartedRunID = startedExecution.GetRunID() } newInfos[k] = c } return newInfos, nil } func (m *executionManagerImpl) DeserializeActivityInfos( infos map[int64]*InternalActivityInfo, ) (map[int64]*ActivityInfo, error) { newInfos := make(map[int64]*ActivityInfo) for k, v := range infos { scheduledEvent, err := m.serializer.DeserializeEvent(v.ScheduledEvent) if err != nil { return nil, err } startedEvent, err := m.serializer.DeserializeEvent(v.StartedEvent) if err != nil { return nil, err } a := &ActivityInfo{ ScheduledEvent: scheduledEvent, StartedEvent: startedEvent, Version: v.Version, ScheduleID: v.ScheduleID, ScheduledEventBatchID: v.ScheduledEventBatchID, ScheduledTime: v.ScheduledTime, StartedID: v.StartedID, StartedTime: v.StartedTime, ActivityID: v.ActivityID, RequestID: v.RequestID, Details: v.Details, ScheduleToStartTimeout: int32(v.ScheduleToStartTimeout.Seconds()), ScheduleToCloseTimeout: int32(v.ScheduleToCloseTimeout.Seconds()), StartToCloseTimeout: int32(v.StartToCloseTimeout.Seconds()), HeartbeatTimeout: int32(v.HeartbeatTimeout.Seconds()), CancelRequested: v.CancelRequested, CancelRequestID: v.CancelRequestID, LastHeartBeatUpdatedTime: v.LastHeartBeatUpdatedTime, TimerTaskStatus: v.TimerTaskStatus, Attempt: v.Attempt, DomainID: v.DomainID, StartedIdentity: v.StartedIdentity, TaskList: v.TaskList, TaskListKind: v.TaskListKind, HasRetryPolicy: v.HasRetryPolicy, InitialInterval: int32(v.InitialInterval.Seconds()), BackoffCoefficient: v.BackoffCoefficient, MaximumInterval: int32(v.MaximumInterval.Seconds()), ExpirationTime: v.ExpirationTime, MaximumAttempts: v.MaximumAttempts, NonRetriableErrors: v.NonRetriableErrors, LastFailureReason: v.LastFailureReason, LastWorkerIdentity: v.LastWorkerIdentity, LastFailureDetails: v.LastFailureDetails, LastHeartbeatTimeoutVisibilityInSeconds: v.LastHeartbeatTimeoutVisibilityInSeconds, } newInfos[k] = a } return newInfos, nil } func (m *executionManagerImpl) UpdateWorkflowExecution( ctx context.Context, request *UpdateWorkflowExecutionRequest, ) (*UpdateWorkflowExecutionResponse, error) { serializedWorkflowMutation, err := m.SerializeWorkflowMutation(&request.UpdateWorkflowMutation, request.Encoding) if err != nil { return nil, err } var serializedNewWorkflowSnapshot *InternalWorkflowSnapshot if request.NewWorkflowSnapshot != nil { serializedNewWorkflowSnapshot, err = m.SerializeWorkflowSnapshot(request.NewWorkflowSnapshot, request.Encoding) if err != nil { return nil, err } } newRequest := &InternalUpdateWorkflowExecutionRequest{ RangeID: request.RangeID, Mode: request.Mode, UpdateWorkflowMutation: *serializedWorkflowMutation, NewWorkflowSnapshot: serializedNewWorkflowSnapshot, WorkflowRequestMode: request.WorkflowRequestMode, CurrentTimeStamp: m.timeSrc.Now(), } msuss := m.statsComputer.computeMutableStateUpdateStats(newRequest) err = m.persistence.UpdateWorkflowExecution(ctx, newRequest) if err != nil { return nil, err } return &UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: msuss}, nil } func (m *executionManagerImpl) SerializeUpsertChildExecutionInfos( infos []*ChildExecutionInfo, encoding constants.EncodingType, ) ([]*InternalChildExecutionInfo, error) { newInfos := make([]*InternalChildExecutionInfo, 0) for _, v := range infos { initiatedEvent, err := m.serializer.SerializeEvent(v.InitiatedEvent, encoding) if err != nil { return nil, err } startedEvent, err := m.serializer.SerializeEvent(v.StartedEvent, encoding) if err != nil { return nil, err } i := &InternalChildExecutionInfo{ InitiatedEvent: initiatedEvent, StartedEvent: startedEvent, Version: v.Version, InitiatedID: v.InitiatedID, InitiatedEventBatchID: v.InitiatedEventBatchID, CreateRequestID: v.CreateRequestID, StartedID: v.StartedID, StartedWorkflowID: v.StartedWorkflowID, StartedRunID: v.StartedRunID, DomainID: v.DomainID, DomainNameDEPRECATED: v.DomainNameDEPRECATED, WorkflowTypeName: v.WorkflowTypeName, ParentClosePolicy: v.ParentClosePolicy, } newInfos = append(newInfos, i) } return newInfos, nil } func (m *executionManagerImpl) SerializeUpsertActivityInfos( infos []*ActivityInfo, encoding constants.EncodingType, ) ([]*InternalActivityInfo, error) { newInfos := make([]*InternalActivityInfo, 0) for _, v := range infos { scheduledEvent, err := m.serializer.SerializeEvent(v.ScheduledEvent, encoding) if err != nil { return nil, err } startedEvent, err := m.serializer.SerializeEvent(v.StartedEvent, encoding) if err != nil { return nil, err } i := &InternalActivityInfo{ Version: v.Version, ScheduleID: v.ScheduleID, ScheduledEventBatchID: v.ScheduledEventBatchID, ScheduledEvent: scheduledEvent, ScheduledTime: v.ScheduledTime, StartedID: v.StartedID, StartedEvent: startedEvent, StartedTime: v.StartedTime, ActivityID: v.ActivityID, RequestID: v.RequestID, Details: v.Details, ScheduleToStartTimeout: common.SecondsToDuration(int64(v.ScheduleToStartTimeout)), ScheduleToCloseTimeout: common.SecondsToDuration(int64(v.ScheduleToCloseTimeout)), StartToCloseTimeout: common.SecondsToDuration(int64(v.StartToCloseTimeout)), HeartbeatTimeout: common.SecondsToDuration(int64(v.HeartbeatTimeout)), CancelRequested: v.CancelRequested, CancelRequestID: v.CancelRequestID, LastHeartBeatUpdatedTime: v.LastHeartBeatUpdatedTime, TimerTaskStatus: v.TimerTaskStatus, Attempt: v.Attempt, DomainID: v.DomainID, StartedIdentity: v.StartedIdentity, TaskList: v.TaskList, TaskListKind: v.TaskListKind, HasRetryPolicy: v.HasRetryPolicy, InitialInterval: common.SecondsToDuration(int64(v.InitialInterval)), BackoffCoefficient: v.BackoffCoefficient, MaximumInterval: common.SecondsToDuration(int64(v.MaximumInterval)), ExpirationTime: v.ExpirationTime, MaximumAttempts: v.MaximumAttempts, NonRetriableErrors: v.NonRetriableErrors, LastFailureReason: v.LastFailureReason, LastWorkerIdentity: v.LastWorkerIdentity, LastFailureDetails: v.LastFailureDetails, LastHeartbeatTimeoutVisibilityInSeconds: v.LastHeartbeatTimeoutVisibilityInSeconds, } newInfos = append(newInfos, i) } return newInfos, nil } func (m *executionManagerImpl) SerializeExecutionInfo( info *WorkflowExecutionInfo, stats *ExecutionStats, encoding constants.EncodingType, ) (*InternalWorkflowExecutionInfo, error) { if info == nil { return &InternalWorkflowExecutionInfo{}, nil } completionEvent, err := m.serializer.SerializeEvent(info.CompletionEvent, encoding) if err != nil { return nil, err } resetPoints, err := m.serializer.SerializeResetPoints(info.AutoResetPoints, encoding) if err != nil { return nil, err } activeClusterSelectionPolicy, err := m.serializer.SerializeActiveClusterSelectionPolicy(info.ActiveClusterSelectionPolicy, encoding) if err != nil { return nil, err } return &InternalWorkflowExecutionInfo{ DomainID: info.DomainID, WorkflowID: info.WorkflowID, RunID: info.RunID, FirstExecutionRunID: info.FirstExecutionRunID, ParentDomainID: info.ParentDomainID, ParentWorkflowID: info.ParentWorkflowID, ParentRunID: info.ParentRunID, InitiatedID: info.InitiatedID, CompletionEventBatchID: info.CompletionEventBatchID, CompletionEvent: completionEvent, TaskList: info.TaskList, TaskListKind: info.TaskListKind, WorkflowTypeName: info.WorkflowTypeName, WorkflowTimeout: common.SecondsToDuration(int64(info.WorkflowTimeout)), DecisionStartToCloseTimeout: common.SecondsToDuration(int64(info.DecisionStartToCloseTimeout)), ExecutionContext: info.ExecutionContext, State: info.State, CloseStatus: info.CloseStatus, LastFirstEventID: info.LastFirstEventID, LastEventTaskID: info.LastEventTaskID, NextEventID: info.NextEventID, LastProcessedEvent: info.LastProcessedEvent, StartTimestamp: info.StartTimestamp, LastUpdatedTimestamp: info.LastUpdatedTimestamp, CreateRequestID: info.CreateRequestID, SignalCount: info.SignalCount, DecisionVersion: info.DecisionVersion, DecisionScheduleID: info.DecisionScheduleID, DecisionStartedID: info.DecisionStartedID, DecisionRequestID: info.DecisionRequestID, DecisionTimeout: common.SecondsToDuration(int64(info.DecisionTimeout)), DecisionAttempt: info.DecisionAttempt, DecisionStartedTimestamp: time.Unix(0, info.DecisionStartedTimestamp).UTC(), DecisionScheduledTimestamp: time.Unix(0, info.DecisionScheduledTimestamp).UTC(), DecisionOriginalScheduledTimestamp: time.Unix(0, info.DecisionOriginalScheduledTimestamp).UTC(), CancelRequested: info.CancelRequested, CancelRequestID: info.CancelRequestID, StickyTaskList: info.StickyTaskList, StickyScheduleToStartTimeout: common.SecondsToDuration(int64(info.StickyScheduleToStartTimeout)), ClientLibraryVersion: info.ClientLibraryVersion, ClientFeatureVersion: info.ClientFeatureVersion, ClientImpl: info.ClientImpl, AutoResetPoints: resetPoints, Attempt: info.Attempt, HasRetryPolicy: info.HasRetryPolicy, InitialInterval: common.SecondsToDuration(int64(info.InitialInterval)), BackoffCoefficient: info.BackoffCoefficient, MaximumInterval: common.SecondsToDuration(int64(info.MaximumInterval)), ExpirationTime: info.ExpirationTime, MaximumAttempts: info.MaximumAttempts, NonRetriableErrors: info.NonRetriableErrors, BranchToken: info.BranchToken, CronSchedule: info.CronSchedule, ExpirationInterval: common.SecondsToDuration(int64(info.ExpirationSeconds)), Memo: info.Memo, SearchAttributes: info.SearchAttributes, PartitionConfig: info.PartitionConfig, CronOverlapPolicy: info.CronOverlapPolicy, ActiveClusterSelectionPolicy: activeClusterSelectionPolicy, // attributes which are not related to mutable state HistorySize: stats.HistorySize, }, nil } func (m *executionManagerImpl) ConflictResolveWorkflowExecution( ctx context.Context, request *ConflictResolveWorkflowExecutionRequest, ) (*ConflictResolveWorkflowExecutionResponse, error) { serializedResetWorkflowSnapshot, err := m.SerializeWorkflowSnapshot(&request.ResetWorkflowSnapshot, request.Encoding) if err != nil { return nil, err } var serializedCurrentWorkflowMutation *InternalWorkflowMutation if request.CurrentWorkflowMutation != nil { serializedCurrentWorkflowMutation, err = m.SerializeWorkflowMutation(request.CurrentWorkflowMutation, request.Encoding) if err != nil { return nil, err } } var serializedNewWorkflowMutation *InternalWorkflowSnapshot if request.NewWorkflowSnapshot != nil { serializedNewWorkflowMutation, err = m.SerializeWorkflowSnapshot(request.NewWorkflowSnapshot, request.Encoding) if err != nil { return nil, err } } newRequest := &InternalConflictResolveWorkflowExecutionRequest{ RangeID: request.RangeID, Mode: request.Mode, ResetWorkflowSnapshot: *serializedResetWorkflowSnapshot, NewWorkflowSnapshot: serializedNewWorkflowMutation, CurrentWorkflowMutation: serializedCurrentWorkflowMutation, WorkflowRequestMode: request.WorkflowRequestMode, CurrentTimeStamp: m.timeSrc.Now(), } msuss := m.statsComputer.computeMutableStateConflictResolveStats(newRequest) err = m.persistence.ConflictResolveWorkflowExecution(ctx, newRequest) if err != nil { return nil, err } return &ConflictResolveWorkflowExecutionResponse{MutableStateUpdateSessionStats: msuss}, nil } func (m *executionManagerImpl) CreateWorkflowExecution( ctx context.Context, request *CreateWorkflowExecutionRequest, ) (*CreateWorkflowExecutionResponse, error) { serializedNewWorkflowSnapshot, err := m.SerializeWorkflowSnapshot(&request.NewWorkflowSnapshot, constants.EncodingType(m.dc.SerializationEncoding())) if err != nil { return nil, err } newRequest := &InternalCreateWorkflowExecutionRequest{ RangeID: request.RangeID, Mode: request.Mode, PreviousRunID: request.PreviousRunID, PreviousLastWriteVersion: request.PreviousLastWriteVersion, NewWorkflowSnapshot: *serializedNewWorkflowSnapshot, WorkflowRequestMode: request.WorkflowRequestMode, CurrentTimeStamp: m.timeSrc.Now(), } msuss := m.statsComputer.computeMutableStateCreateStats(newRequest) _, err = m.persistence.CreateWorkflowExecution(ctx, newRequest) if err != nil { return nil, err } return &CreateWorkflowExecutionResponse{MutableStateUpdateSessionStats: msuss}, nil } func (m *executionManagerImpl) SerializeWorkflowMutation( input *WorkflowMutation, encoding constants.EncodingType, ) (*InternalWorkflowMutation, error) { serializedExecutionInfo, err := m.SerializeExecutionInfo( input.ExecutionInfo, input.ExecutionStats, encoding, ) if err != nil { return nil, err } serializedVersionHistories, err := m.SerializeVersionHistories(input.VersionHistories, encoding) if err != nil { return nil, err } serializedUpsertActivityInfos, err := m.SerializeUpsertActivityInfos(input.UpsertActivityInfos, encoding) if err != nil { return nil, err } serializedUpsertChildExecutionInfos, err := m.SerializeUpsertChildExecutionInfos(input.UpsertChildExecutionInfos, encoding) if err != nil { return nil, err } var serializedNewBufferedEvents *DataBlob if input.NewBufferedEvents != nil { serializedNewBufferedEvents, err = m.serializer.SerializeBatchEvents(input.NewBufferedEvents, encoding) if err != nil { return nil, err } } startVersion, err := getStartVersion(input.VersionHistories) if err != nil { return nil, err } lastWriteVersion, err := getLastWriteVersion(input.VersionHistories) if err != nil { return nil, err } checksumData, err := m.serializer.SerializeChecksum(input.Checksum, constants.EncodingTypeJSON) if err != nil { return nil, err } return &InternalWorkflowMutation{ ExecutionInfo: serializedExecutionInfo, VersionHistories: serializedVersionHistories, StartVersion: startVersion, LastWriteVersion: lastWriteVersion, UpsertActivityInfos: serializedUpsertActivityInfos, DeleteActivityInfos: input.DeleteActivityInfos, UpsertTimerInfos: input.UpsertTimerInfos, DeleteTimerInfos: input.DeleteTimerInfos, UpsertChildExecutionInfos: serializedUpsertChildExecutionInfos, DeleteChildExecutionInfos: input.DeleteChildExecutionInfos, UpsertRequestCancelInfos: input.UpsertRequestCancelInfos, DeleteRequestCancelInfos: input.DeleteRequestCancelInfos, UpsertSignalInfos: input.UpsertSignalInfos, DeleteSignalInfos: input.DeleteSignalInfos, UpsertSignalRequestedIDs: input.UpsertSignalRequestedIDs, DeleteSignalRequestedIDs: input.DeleteSignalRequestedIDs, NewBufferedEvents: serializedNewBufferedEvents, ClearBufferedEvents: input.ClearBufferedEvents, TasksByCategory: input.TasksByCategory, WorkflowRequests: input.WorkflowRequests, Condition: input.Condition, Checksum: input.Checksum, ChecksumData: checksumData, }, nil } func (m *executionManagerImpl) SerializeWorkflowSnapshot( input *WorkflowSnapshot, encoding constants.EncodingType, ) (*InternalWorkflowSnapshot, error) { serializedExecutionInfo, err := m.SerializeExecutionInfo( input.ExecutionInfo, input.ExecutionStats, encoding, ) if err != nil { return nil, err } serializedVersionHistories, err := m.SerializeVersionHistories(input.VersionHistories, encoding) if err != nil { return nil, err } serializedActivityInfos, err := m.SerializeUpsertActivityInfos(input.ActivityInfos, encoding) if err != nil { return nil, err } serializedChildExecutionInfos, err := m.SerializeUpsertChildExecutionInfos(input.ChildExecutionInfos, encoding) if err != nil { return nil, err } startVersion, err := getStartVersion(input.VersionHistories) if err != nil { return nil, err } lastWriteVersion, err := getLastWriteVersion(input.VersionHistories) if err != nil { return nil, err } checksumData, err := m.serializer.SerializeChecksum(input.Checksum, constants.EncodingTypeJSON) if err != nil { return nil, err } return &InternalWorkflowSnapshot{ ExecutionInfo: serializedExecutionInfo, VersionHistories: serializedVersionHistories, StartVersion: startVersion, LastWriteVersion: lastWriteVersion, ActivityInfos: serializedActivityInfos, TimerInfos: input.TimerInfos, ChildExecutionInfos: serializedChildExecutionInfos, RequestCancelInfos: input.RequestCancelInfos, SignalInfos: input.SignalInfos, SignalRequestedIDs: input.SignalRequestedIDs, TasksByCategory: input.TasksByCategory, WorkflowRequests: input.WorkflowRequests, Condition: input.Condition, Checksum: input.Checksum, ChecksumData: checksumData, }, nil } func (m *executionManagerImpl) SerializeVersionHistories( versionHistories *VersionHistories, encoding constants.EncodingType, ) (*DataBlob, error) { if versionHistories == nil { return nil, nil } return m.serializer.SerializeVersionHistories(versionHistories.ToInternalType(), encoding) } func (m *executionManagerImpl) DeserializeVersionHistories( blob *DataBlob, ) (*VersionHistories, error) { if blob == nil { return nil, nil } versionHistories, err := m.serializer.DeserializeVersionHistories(blob) if err != nil { return nil, err } return NewVersionHistoriesFromInternalType(versionHistories), nil } func (m *executionManagerImpl) DeleteWorkflowExecution( ctx context.Context, request *DeleteWorkflowExecutionRequest, ) error { return m.persistence.DeleteWorkflowExecution(ctx, request) } func (m *executionManagerImpl) DeleteCurrentWorkflowExecution( ctx context.Context, request *DeleteCurrentWorkflowExecutionRequest, ) error { return m.persistence.DeleteCurrentWorkflowExecution(ctx, request) } func (m *executionManagerImpl) GetCurrentExecution( ctx context.Context, request *GetCurrentExecutionRequest, ) (*GetCurrentExecutionResponse, error) { return m.persistence.GetCurrentExecution(ctx, request) } func (m *executionManagerImpl) ListCurrentExecutions( ctx context.Context, request *ListCurrentExecutionsRequest, ) (*ListCurrentExecutionsResponse, error) { return m.persistence.ListCurrentExecutions(ctx, request) } func (m *executionManagerImpl) IsWorkflowExecutionExists( ctx context.Context, request *IsWorkflowExecutionExistsRequest, ) (*IsWorkflowExecutionExistsResponse, error) { return m.persistence.IsWorkflowExecutionExists(ctx, request) } func (m *executionManagerImpl) ListConcreteExecutions( ctx context.Context, request *ListConcreteExecutionsRequest, ) (*ListConcreteExecutionsResponse, error) { response, err := m.persistence.ListConcreteExecutions(ctx, request) if err != nil { return nil, err } newResponse := &ListConcreteExecutionsResponse{ Executions: make([]*ListConcreteExecutionsEntity, len(response.Executions)), PageToken: response.NextPageToken, } for i, e := range response.Executions { info, _, err := m.DeserializeExecutionInfo(e.ExecutionInfo) if err != nil { return nil, err } vh, err := m.DeserializeVersionHistories(e.VersionHistories) if err != nil { return nil, err } newResponse.Executions[i] = &ListConcreteExecutionsEntity{ ExecutionInfo: info, VersionHistories: vh, } } return newResponse, nil } func (m *executionManagerImpl) PutReplicationTaskToDLQ( ctx context.Context, request *PutReplicationTaskToDLQRequest, ) error { internalRequest := &InternalPutReplicationTaskToDLQRequest{ SourceClusterName: request.SourceClusterName, TaskInfo: m.toInternalReplicationTaskInfo(request.TaskInfo), } return m.persistence.PutReplicationTaskToDLQ(ctx, internalRequest) } func (m *executionManagerImpl) GetReplicationTasksFromDLQ( ctx context.Context, request *GetReplicationTasksFromDLQRequest, ) (*GetHistoryTasksResponse, error) { return m.persistence.GetReplicationTasksFromDLQ(ctx, request) } func (m *executionManagerImpl) GetReplicationDLQSize( ctx context.Context, request *GetReplicationDLQSizeRequest, ) (*GetReplicationDLQSizeResponse, error) { return m.persistence.GetReplicationDLQSize(ctx, request) } func (m *executionManagerImpl) DeleteReplicationTaskFromDLQ( ctx context.Context, request *DeleteReplicationTaskFromDLQRequest, ) error { return m.persistence.DeleteReplicationTaskFromDLQ(ctx, request) } func (m *executionManagerImpl) RangeDeleteReplicationTaskFromDLQ( ctx context.Context, request *RangeDeleteReplicationTaskFromDLQRequest, ) (*RangeDeleteReplicationTaskFromDLQResponse, error) { return m.persistence.RangeDeleteReplicationTaskFromDLQ(ctx, request) } func (m *executionManagerImpl) CreateFailoverMarkerTasks( ctx context.Context, request *CreateFailoverMarkersRequest, ) error { request.CurrentTimeStamp = m.timeSrc.Now() return m.persistence.CreateFailoverMarkerTasks(ctx, request) } func (m *executionManagerImpl) GetActiveClusterSelectionPolicy( ctx context.Context, domainID, wfID, rID string, ) (*types.ActiveClusterSelectionPolicy, error) { blob, err := m.persistence.GetActiveClusterSelectionPolicy(ctx, domainID, wfID, rID) if err != nil { return nil, err } if blob == nil { return nil, &types.EntityNotExistsError{ Message: "active cluster selection policy not found", } } policy, err := m.serializer.DeserializeActiveClusterSelectionPolicy(blob) if err != nil { return nil, err } return policy, nil } func (m *executionManagerImpl) DeleteActiveClusterSelectionPolicy( ctx context.Context, domainID, workflowID, runID string, ) error { return m.persistence.DeleteActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID) } func (m *executionManagerImpl) Close() { m.persistence.Close() } func (m *executionManagerImpl) fromInternalReplicationTaskInfos(internalInfos []*InternalReplicationTaskInfo) []*ReplicationTaskInfo { if internalInfos == nil { return nil } infos := make([]*ReplicationTaskInfo, len(internalInfos)) for i := 0; i < len(internalInfos); i++ { infos[i] = m.fromInternalReplicationTaskInfo(internalInfos[i]) } return infos } func (m *executionManagerImpl) fromInternalReplicationTaskInfo(internalInfo *InternalReplicationTaskInfo) *ReplicationTaskInfo { if internalInfo == nil { return nil } return &ReplicationTaskInfo{ DomainID: internalInfo.DomainID, WorkflowID: internalInfo.WorkflowID, RunID: internalInfo.RunID, TaskID: internalInfo.TaskID, TaskType: internalInfo.TaskType, FirstEventID: internalInfo.FirstEventID, NextEventID: internalInfo.NextEventID, Version: internalInfo.Version, ScheduledID: internalInfo.ScheduledID, BranchToken: internalInfo.BranchToken, NewRunBranchToken: internalInfo.NewRunBranchToken, CreationTime: internalInfo.CreationTime.UnixNano(), } } func (m *executionManagerImpl) toInternalReplicationTaskInfo(info *ReplicationTaskInfo) *InternalReplicationTaskInfo { if info == nil { return nil } return &InternalReplicationTaskInfo{ DomainID: info.DomainID, WorkflowID: info.WorkflowID, RunID: info.RunID, TaskID: info.TaskID, TaskType: info.TaskType, FirstEventID: info.FirstEventID, NextEventID: info.NextEventID, Version: info.Version, ScheduledID: info.ScheduledID, BranchToken: info.BranchToken, NewRunBranchToken: info.NewRunBranchToken, CreationTime: time.Unix(0, info.CreationTime).UTC(), CurrentTimeStamp: m.timeSrc.Now(), } } func (m *executionManagerImpl) GetHistoryTasks( ctx context.Context, request *GetHistoryTasksRequest, ) (*GetHistoryTasksResponse, error) { return m.persistence.GetHistoryTasks(ctx, request) } func (m *executionManagerImpl) CompleteHistoryTask( ctx context.Context, request *CompleteHistoryTaskRequest, ) error { return m.persistence.CompleteHistoryTask(ctx, request) } func (m *executionManagerImpl) RangeCompleteHistoryTask( ctx context.Context, request *RangeCompleteHistoryTaskRequest, ) (*RangeCompleteHistoryTaskResponse, error) { return m.persistence.RangeCompleteHistoryTask(ctx, request) } func getStartVersion( versionHistories *VersionHistories, ) (int64, error) { if versionHistories == nil { return constants.EmptyVersion, nil } versionHistory, err := versionHistories.GetCurrentVersionHistory() if err != nil { return 0, err } versionHistoryItem, err := versionHistory.GetFirstItem() if err != nil { return 0, err } return versionHistoryItem.Version, nil } func getLastWriteVersion( versionHistories *VersionHistories, ) (int64, error) { if versionHistories == nil { return constants.EmptyVersion, nil } versionHistory, err := versionHistories.GetCurrentVersionHistory() if err != nil { return 0, err } versionHistoryItem, err := versionHistory.GetLastItem() if err != nil { return 0, err } return versionHistoryItem.Version, nil } ================================================ FILE: common/persistence/execution_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "fmt" "reflect" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) var ( testIndex = "test-index" testDomain = "test-domain" testDomainID = "bfd5c907-f899-4baf-a7b2-2ab85e623ebd" testPageSize = 10 testEarliestTime = int64(1547596872371000000) testLatestTime = int64(2547596872371000000) testWorkflowType = "test-wf-type" testWorkflowID = "test-wid" testCloseStatus = int32(1) testTableName = "test-table-name" testRunID = "test-run-id" testSearchAttributes1 = map[string]interface{}{"TestAttr1": "val1", "TestAttr2": 2, "TestAttr3": false} testSearchAttributes2 = map[string]interface{}{"TestAttr1": "val2", "TestAttr2": 2, "TestAttr3": false} testSearchAttributes3 = map[string]interface{}{"TestAttr2": 2, "TestAttr3": false} ) func TestExecutionManager_ProxyStoreMethods(t *testing.T) { for _, tc := range []struct { method string prepareMocks func(*MockExecutionStore) }{ { method: "GetShardID", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().GetShardID().Return(1).Times(1) }, }, { method: "GetName", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().GetName().Return("test").Times(1) }, }, { method: "Close", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().Close().Return().Times(1) }, }, { method: "GetHistoryTasks", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Any()).Return(nil, nil) }, }, { method: "RangeCompleteHistoryTask", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().RangeCompleteHistoryTask(gomock.Any(), gomock.Any()).Return(nil, nil) }, }, { method: "DeleteReplicationTaskFromDLQ", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().DeleteReplicationTaskFromDLQ(gomock.Any(), gomock.Any()).Return(nil) }, }, { method: "RangeDeleteReplicationTaskFromDLQ", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().RangeDeleteReplicationTaskFromDLQ(gomock.Any(), gomock.Any()).Return(nil, nil) }, }, { method: "DeleteWorkflowExecution", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil) }, }, { method: "DeleteCurrentWorkflowExecution", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().DeleteCurrentWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil) }, }, { method: "GetCurrentExecution", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().GetCurrentExecution(gomock.Any(), gomock.Any()).Return(nil, nil) }, }, { method: "ListCurrentExecutions", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().ListCurrentExecutions(gomock.Any(), gomock.Any()).Return(nil, nil) }, }, { method: "IsWorkflowExecutionExists", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().IsWorkflowExecutionExists(gomock.Any(), gomock.Any()).Return(nil, nil) }, }, { method: "GetReplicationDLQSize", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().GetReplicationDLQSize(gomock.Any(), gomock.Any()).Return(nil, nil) }, }, } { t.Run(tc.method, func(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) tc.prepareMocks(mockedStore) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), nil, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) v := reflect.ValueOf(manager) method := v.MethodByName(tc.method) methodType := method.Type() args := methodType.NumIn() var vals []reflect.Value // If a method requires arguments, we expect the first argument to be a context // and the rest to be zero values of the correct type. // For methods like Close and GetShardID, we don't expect any arguments. if args > 0 { vals = append(vals, reflect.ValueOf(context.Background())) for i := 1; i < args; i++ { vals = append(vals, reflect.Zero(methodType.In(i))) } } callRes := method.Call(vals) if callRes == nil { return } resultErr := callRes[len(callRes)-1].Interface() err, ok := resultErr.(error) if ok { assert.NoError(t, err) } }) } } func TestExecutionManager_GetWorkflowExecution(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) mockedSerializer := NewMockPayloadSerializer(ctrl) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), mockedSerializer, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) request := &GetWorkflowExecutionRequest{ DomainID: testDomainID, Execution: types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, RangeID: 1, } activityOne := sampleInternalActivityInfo("activity1") activityTwo := sampleInternalActivityInfo("activity2") wfCompletionEvent := NewDataBlob([]byte("wf-event"), constants.EncodingTypeThriftRW) wfCompletionEventData := generateTestHistoryEvent(99) wfInfo := sampleInternalWorkflowExecutionInfo() wfInfo.CompletionEvent = wfCompletionEvent wfInfo.AutoResetPoints = NewDataBlob([]byte("test-reset-points"), constants.EncodingTypeThriftRW) mockedStore.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(&InternalGetWorkflowExecutionResponse{ State: &InternalWorkflowMutableState{ ExecutionInfo: wfInfo, ActivityInfos: map[int64]*InternalActivityInfo{ 1: activityOne, 2: activityTwo, }, TimerInfos: map[string]*TimerInfo{ "test-timer": { Version: 1, }, }, }, }, nil) mockedSerializer.EXPECT().DeserializeEvent(activityOne.ScheduledEvent).Return(&types.HistoryEvent{ ID: 1, }, nil).Times(1) mockedSerializer.EXPECT().DeserializeEvent(activityOne.StartedEvent).Return(&types.HistoryEvent{ ID: 1, }, nil).Times(1) mockedSerializer.EXPECT().DeserializeEvent(activityTwo.ScheduledEvent).Return(&types.HistoryEvent{ ID: 2, }, nil).Times(1) mockedSerializer.EXPECT().DeserializeEvent(activityTwo.StartedEvent).Return(&types.HistoryEvent{ ID: 2, }, nil).Times(1) mockedSerializer.EXPECT().DeserializeEvent(wfCompletionEvent).Return(wfCompletionEventData, nil).Times(1) mockedSerializer.EXPECT().DeserializeResetPoints(gomock.Any()).Return(&types.ResetPoints{}, nil).Times(1) mockedSerializer.EXPECT().DeserializeChecksum(gomock.Any()).Return(checksum.Checksum{}, nil).Times(1) activeClusterSelPlcyData := sampleActiveClusterSelectionPolicyData() mockedSerializer.EXPECT().DeserializeActiveClusterSelectionPolicy(activeClusterSelPlcyData).Return(generateActiveClusterSelectionPolicy(), nil).Times(1) res, err := manager.GetWorkflowExecution(context.Background(), request) assert.NoError(t, err) expectedExecutionInfo := sampleWorkflowExecutionInfo() expectedExecutionInfo.CompletionEvent = wfCompletionEventData expectedExecutionInfo.AutoResetPoints = &types.ResetPoints{} assert.Equal(t, &WorkflowMutableState{ ExecutionInfo: expectedExecutionInfo, ChildExecutionInfos: make(map[int64]*ChildExecutionInfo), ActivityInfos: map[int64]*ActivityInfo{ 1: sampleActivityInfo("activity1", 1), 2: sampleActivityInfo("activity2", 2), }, TimerInfos: map[string]*TimerInfo{ "test-timer": { Version: 1, }, }, ExecutionStats: &ExecutionStats{ HistorySize: 1024, }, BufferedEvents: make([]*types.HistoryEvent, 0), }, res.State) // Expectations for the deserialization of activity events. assert.Equal(t, &MutableStateStats{MutableStateSize: 170, ExecutionInfoSize: 20, ActivityInfoSize: 150, TimerInfoSize: 0, ChildInfoSize: 0, SignalInfoSize: 0, BufferedEventsSize: 0, ActivityInfoCount: 2, TimerInfoCount: 1, ChildInfoCount: 0, SignalInfoCount: 0, RequestCancelInfoCount: 0, BufferedEventsCount: 0}, res.MutableStateStats) } func TestExecutionManager_GetWorkflowExecution_NoWorkflow(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) mockedSerializer := NewMockPayloadSerializer(ctrl) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), mockedSerializer, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) request := &GetWorkflowExecutionRequest{ DomainID: "testDomain", Execution: types.WorkflowExecution{ WorkflowID: "nonexistentWorkflow", RunID: "nonexistentRunID", }, RangeID: 1, } mockedStore.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &types.EntityNotExistsError{}) _, err := manager.GetWorkflowExecution(context.Background(), request) assert.Error(t, err) assert.IsType(t, &types.EntityNotExistsError{}, err) } func TestExecutionManager_UpdateWorkflowExecution(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) mockedSerializer := NewMockPayloadSerializer(ctrl) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), mockedSerializer, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) expectedInfo := sampleInternalWorkflowMutation() mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(expectedInfo.ExecutionInfo.CompletionEvent, nil).Times(2) mockedSerializer.EXPECT().SerializeEvent(activityStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(generateResetPoints(), constants.EncodingTypeThriftRW).Return(expectedInfo.ExecutionInfo.AutoResetPoints, nil).Times(2) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(2) request := &UpdateWorkflowExecutionRequest{ RangeID: 1, Mode: UpdateWorkflowModeBypassCurrent, UpdateWorkflowMutation: *sampleWorkflowMutation(), Encoding: constants.EncodingTypeThriftRW, NewWorkflowSnapshot: &WorkflowSnapshot{ ExecutionInfo: sampleWorkflowExecutionInfo(), ExecutionStats: &ExecutionStats{ HistorySize: 1024, }, Checksum: generateChecksum(), }, } mockedSerializer.EXPECT().SerializeChecksum(request.UpdateWorkflowMutation.Checksum, constants.EncodingTypeJSON).Return(expectedInfo.ChecksumData, nil).Times(2) expectedRequest := &InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: UpdateWorkflowModeBypassCurrent, UpdateWorkflowMutation: *expectedInfo, NewWorkflowSnapshot: &InternalWorkflowSnapshot{ ExecutionInfo: expectedInfo.ExecutionInfo, }, } mockedStore.EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req *InternalUpdateWorkflowExecutionRequest) error { assert.Equal(t, expectedRequest.UpdateWorkflowMutation, req.UpdateWorkflowMutation) return nil }) res, err := manager.UpdateWorkflowExecution(context.Background(), request) assert.NoError(t, err) stats := &MutableStateUpdateSessionStats{ MutableStateSize: 90, ExecutionInfoSize: 40, ActivityInfoSize: 20, TimerInfoSize: 10, ChildInfoSize: 20, ActivityInfoCount: 1, TimerInfoCount: 1, ChildInfoCount: 1, TaskCountByCategory: map[HistoryTaskCategory]int{}, } assert.Equal(t, stats, res.MutableStateUpdateSessionStats) } func TestSerializeWorkflowSnapshot(t *testing.T) { for _, tc := range []struct { name string prepareMocks func(*MockPayloadSerializer) input *WorkflowSnapshot checkRes func(*testing.T, *InternalWorkflowSnapshot, error) }{ { name: "success", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(sampleResetPointsData(), nil).Times(1) mockedSerializer.EXPECT().SerializeVersionHistories(gomock.Any(), gomock.Any()).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeChecksum(gomock.Any(), gomock.Any()).Return(sampleCheckSumData(), nil).Times(1) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(1) }, input: sampleWorkflowSnapshot(), checkRes: func(t *testing.T, res *InternalWorkflowSnapshot, err error) { assert.NoError(t, err) assert.Equal(t, sampleInternalWorkflowSnapshot(), res) }, }, { name: "nil info", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeChecksum(gomock.Any(), gomock.Any()).Return(sampleTestCheckSumData(), nil).Times(1) }, input: &WorkflowSnapshot{}, checkRes: func(t *testing.T, res *InternalWorkflowSnapshot, err error) { assert.NoError(t, err) assert.Equal(t, &InternalWorkflowSnapshot{ ExecutionInfo: &InternalWorkflowExecutionInfo{}, ChecksumData: sampleTestCheckSumData(), StartVersion: constants.EmptyVersion, LastWriteVersion: constants.EmptyVersion, ActivityInfos: make([]*InternalActivityInfo, 0), ChildExecutionInfos: make([]*InternalChildExecutionInfo, 0), }, res) }, }, { name: "serialize event error", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(gomock.Any(), gomock.Any()).Return(nil, assert.AnError).Times(1) }, input: sampleWorkflowSnapshot(), checkRes: func(t *testing.T, res *InternalWorkflowSnapshot, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, { name: "serialize points error", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(gomock.Any(), gomock.Any()).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(nil, assert.AnError).Times(1) }, input: sampleWorkflowSnapshot(), checkRes: func(t *testing.T, res *InternalWorkflowSnapshot, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, { name: "serialize version histories error", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(gomock.Any(), gomock.Any()).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(NewDataBlob([]byte("test-reset-points"), constants.EncodingTypeThriftRW), nil).Times(1) mockedSerializer.EXPECT().SerializeVersionHistories(gomock.Any(), gomock.Any()).Return(nil, assert.AnError).Times(1) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(1) }, input: sampleWorkflowSnapshot(), checkRes: func(t *testing.T, res *InternalWorkflowSnapshot, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, { name: "serialize activity scheduled event error", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(sampleResetPointsData(), nil).Times(1) mockedSerializer.EXPECT().SerializeVersionHistories(gomock.Any(), gomock.Any()).Return(sampleTestCheckSumData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(nil, assert.AnError).Times(1) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(1) }, input: sampleWorkflowSnapshot(), checkRes: func(t *testing.T, res *InternalWorkflowSnapshot, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, { name: "serialize activity started event error", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(sampleResetPointsData(), nil).Times(1) mockedSerializer.EXPECT().SerializeVersionHistories(gomock.Any(), gomock.Any()).Return(sampleTestCheckSumData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityStartedEvent(), constants.EncodingTypeThriftRW).Return(nil, assert.AnError).Times(1) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(1) }, input: sampleWorkflowSnapshot(), checkRes: func(t *testing.T, res *InternalWorkflowSnapshot, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, { name: "serialize child workflow scheduled event error", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(sampleResetPointsData(), nil).Times(1) mockedSerializer.EXPECT().SerializeVersionHistories(gomock.Any(), gomock.Any()).Return(sampleTestCheckSumData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowScheduledEvent(), constants.EncodingTypeThriftRW).Return(nil, assert.AnError).Times(1) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(1) }, input: sampleWorkflowSnapshot(), checkRes: func(t *testing.T, res *InternalWorkflowSnapshot, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, { name: "serialize child workflow started event error", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(sampleResetPointsData(), nil).Times(1) mockedSerializer.EXPECT().SerializeVersionHistories(gomock.Any(), gomock.Any()).Return(sampleTestCheckSumData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowStartedEvent(), constants.EncodingTypeThriftRW).Return(nil, assert.AnError).Times(1) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(1) }, input: sampleWorkflowSnapshot(), checkRes: func(t *testing.T, res *InternalWorkflowSnapshot, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, } { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockedSerializer := NewMockPayloadSerializer(ctrl) tc.prepareMocks(mockedSerializer) manager := NewExecutionManagerImpl(nil, testlogger.New(t), mockedSerializer, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }).(*executionManagerImpl) res, err := manager.SerializeWorkflowSnapshot(tc.input, constants.EncodingTypeThriftRW) tc.checkRes(t, res, err) }) } } func TestDeserializeBufferedEvents(t *testing.T) { for _, tc := range []struct { name string prepareMocks func(*MockPayloadSerializer) checkRes func(*testing.T, []*types.HistoryEvent, error) }{ { name: "success", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { eventCounter := 0 mockedSerializer.EXPECT().DeserializeBatchEvents(gomock.Any()).DoAndReturn(func(data *DataBlob) ([]*types.HistoryEvent, error) { res := []*types.HistoryEvent{{ID: int64(eventCounter)}, {ID: int64(eventCounter + 1)}} eventCounter += 2 return res, nil }).Times(2) }, checkRes: func(t *testing.T, events []*types.HistoryEvent, err error) { assert.NoError(t, err) assert.Equal(t, []*types.HistoryEvent{{ID: 0}, {ID: 1}, {ID: 2}, {ID: 3}}, events) }, }, { name: "error", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().DeserializeBatchEvents(gomock.Any()).Return(nil, assert.AnError).Times(1) }, checkRes: func(t *testing.T, res []*types.HistoryEvent, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, } { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockedSerializer := NewMockPayloadSerializer(ctrl) tc.prepareMocks(mockedSerializer) manager := NewExecutionManagerImpl(nil, testlogger.New(t), mockedSerializer, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }).(*executionManagerImpl) events := []*DataBlob{ sampleEventData(), sampleEventData(), } res, err := manager.DeserializeBufferedEvents(events) tc.checkRes(t, res, err) }) } } func TestPutReplicationTaskToDLQ(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), nil, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) now := time.Now().UTC() task := &PutReplicationTaskToDLQRequest{ SourceClusterName: "test-cluster", TaskInfo: &ReplicationTaskInfo{ DomainID: testDomainID, WorkflowID: testWorkflowID, CreationTime: now.UnixNano(), }, DomainName: testDomain, } mockedStore.EXPECT().PutReplicationTaskToDLQ(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req *InternalPutReplicationTaskToDLQRequest) error { assert.Equal(t, "test-cluster", req.SourceClusterName) assert.Equal(t, testDomainID, req.TaskInfo.DomainID) assert.Equal(t, testWorkflowID, req.TaskInfo.WorkflowID) assert.Equal(t, now, req.TaskInfo.CreationTime) assert.WithinDuration(t, now, req.TaskInfo.CurrentTimeStamp, time.Second) return nil }) err := manager.PutReplicationTaskToDLQ(context.Background(), task) assert.NoError(t, err) } func TestGetReplicationTasksFromDLQ(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), nil, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) request := &GetReplicationTasksFromDLQRequest{ SourceClusterName: "test-cluster", ReadLevel: 1, MaxReadLevel: 2, BatchSize: 10, NextPageToken: nil, } now := time.Now().UTC().Round(time.Second) mockedStore.EXPECT().GetReplicationTasksFromDLQ(gomock.Any(), request).Return( &GetHistoryTasksResponse{ Tasks: []Task{ &HistoryReplicationTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, }, TaskData: TaskData{ TaskID: 1, VisibilityTimestamp: now, }, }, }, NextPageToken: []byte("test-token"), }, nil) res, err := manager.GetReplicationTasksFromDLQ(context.Background(), request) assert.NoError(t, err) assert.Equal(t, &GetHistoryTasksResponse{ Tasks: []Task{ &HistoryReplicationTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, }, TaskData: TaskData{ TaskID: 1, VisibilityTimestamp: now, }, }, }, NextPageToken: []byte("test-token"), }, res) } func TestDeserializeChildExecutionInfos(t *testing.T) { tests := []struct { name string prepareMocks func(*MockPayloadSerializer) checkRes func(*testing.T, map[int64]*ChildExecutionInfo, error) }{ { name: "success", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { // Child 1 init event mockedSerializer.EXPECT().DeserializeEvent(sampleEventDataWithVersion(1)).Return(&types.HistoryEvent{ ID: 1, Version: 1, }, nil) // Child 1 start event mockedSerializer.EXPECT().DeserializeEvent(sampleEventDataWithVersion(2)).Return(&types.HistoryEvent{ ID: 2, Version: 2, }, nil) // Child 2 init event mockedSerializer.EXPECT().DeserializeEvent(sampleEventDataWithVersion(3)).Return(&types.HistoryEvent{ ID: 3, Version: 3, }, nil) // Child 2 start event is mimicking legacy behavior where runID and workflowID were not stored inside the info // but was extracted from the startEvent mockedSerializer.EXPECT().DeserializeEvent(sampleEventDataWithVersion(4)).Return(&types.HistoryEvent{ ID: 4, Version: 4, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "legacy-workflow-id", RunID: "legacy-run-id", }, }, }, nil) }, checkRes: func(t *testing.T, events map[int64]*ChildExecutionInfo, err error) { assert.NoError(t, err) assert.Equal(t, map[int64]*ChildExecutionInfo{ 1: { Version: 1, DomainID: testDomainID, WorkflowTypeName: testWorkflowType, InitiatedID: 1, InitiatedEvent: &types.HistoryEvent{ID: 1, Version: 1}, StartedID: 2, StartedEvent: &types.HistoryEvent{ID: 2, Version: 2}, CreateRequestID: "create-request-id", StartedWorkflowID: "workflow-id", StartedRunID: "run-id", }, 2: { Version: 3, DomainID: testDomainID, WorkflowTypeName: testWorkflowType, InitiatedID: 3, InitiatedEvent: &types.HistoryEvent{ID: 3, Version: 3}, StartedID: 4, StartedEvent: &types.HistoryEvent{ID: 4, Version: 4, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "legacy-workflow-id", RunID: "legacy-run-id", }, }}, CreateRequestID: "create-request-id", StartedWorkflowID: "legacy-workflow-id", StartedRunID: "legacy-run-id", }, }, events) }, }, { name: "deserialize event error", prepareMocks: func(mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().DeserializeEvent(gomock.Any()).Return(nil, assert.AnError).Times(1) }, checkRes: func(t *testing.T, events map[int64]*ChildExecutionInfo, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockedSerializer := NewMockPayloadSerializer(ctrl) test.prepareMocks(mockedSerializer) manager := &executionManagerImpl{ serializer: mockedSerializer, } result, err := manager.DeserializeChildExecutionInfos(map[int64]*InternalChildExecutionInfo{ 1: sampleInternalChildExecutionInfo(1, 2), 2: sampleInternalChildExecutionInfo(3, 4), }) test.checkRes(t, result, err) }) } } func TestListConcreteExecutions(t *testing.T) { request := &ListConcreteExecutionsRequest{ PageSize: 10, PageToken: []byte("next"), } internalResponse := &InternalListConcreteExecutionsResponse{ Executions: []*InternalListConcreteExecutionsEntity{ { ExecutionInfo: sampleInternalWorkflowExecutionInfo(), VersionHistories: sampleEventData(), }, }, NextPageToken: []byte("next"), } testCases := []struct { name string prepareMocks func(*MockExecutionStore, *MockPayloadSerializer) checkRes func(*testing.T, *ListConcreteExecutionsResponse, error) }{ { name: "success", prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { mockedStore.EXPECT().ListConcreteExecutions(gomock.Any(), request).Return(internalResponse, nil) mockedSerializer.EXPECT().DeserializeEvent(internalResponse.Executions[0].ExecutionInfo.CompletionEvent).Return(completionEvent(), nil) mockedSerializer.EXPECT().DeserializeResetPoints(internalResponse.Executions[0].ExecutionInfo.AutoResetPoints).Return(&types.ResetPoints{ Points: []*types.ResetPointInfo{ { RunID: testRunID, }, }, }, nil) mockedSerializer.EXPECT().DeserializeActiveClusterSelectionPolicy(internalResponse.Executions[0].ExecutionInfo.ActiveClusterSelectionPolicy).Return(generateActiveClusterSelectionPolicy(), nil) mockedSerializer.EXPECT().DeserializeVersionHistories(internalResponse.Executions[0].VersionHistories).Return(&types.VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: []*types.VersionHistory{ { BranchToken: []byte("branch-token-1"), Items: []*types.VersionHistoryItem{ { EventID: 1, Version: 1, }, }, }, { BranchToken: []byte("branch-token-2"), Items: []*types.VersionHistoryItem{ { EventID: 2, Version: 1, }, { EventID: 3, Version: 2, }, }, }, }, }, nil) }, checkRes: func(t *testing.T, response *ListConcreteExecutionsResponse, err error) { executionInfo := sampleWorkflowExecutionInfo() executionInfo.CompletionEvent = completionEvent() executionInfo.AutoResetPoints = &types.ResetPoints{ Points: []*types.ResetPointInfo{ { RunID: testRunID, }, }, } assert.Equal(t, &ListConcreteExecutionsResponse{ Executions: []*ListConcreteExecutionsEntity{ { ExecutionInfo: executionInfo, VersionHistories: &VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: []*VersionHistory{ { BranchToken: []byte("branch-token-1"), Items: []*VersionHistoryItem{ { EventID: 1, Version: 1, }, }, }, { BranchToken: []byte("branch-token-2"), Items: []*VersionHistoryItem{ { EventID: 2, Version: 1, }, { EventID: 3, Version: 2, }, }, }, }, }, }, }, PageToken: []byte("next"), }, response) }, }, { name: "persistence error", prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { mockedStore.EXPECT().ListConcreteExecutions(gomock.Any(), request).Return(nil, assert.AnError) }, checkRes: func(t *testing.T, response *ListConcreteExecutionsResponse, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, { name: "deserialize execution info error", prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { mockedStore.EXPECT().ListConcreteExecutions(gomock.Any(), request).Return(internalResponse, nil) mockedSerializer.EXPECT().DeserializeEvent(internalResponse.Executions[0].ExecutionInfo.CompletionEvent).Return(nil, assert.AnError) }, checkRes: func(t *testing.T, response *ListConcreteExecutionsResponse, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) mockedSerializer := NewMockPayloadSerializer(ctrl) tc.prepareMocks(mockedStore, mockedSerializer) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), mockedSerializer, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) res, err := manager.ListConcreteExecutions(context.Background(), request) tc.checkRes(t, res, err) }) } } func TestCreateWorkflowExecution(t *testing.T) { for _, tc := range []struct { name string prepareMocks func(*MockExecutionStore, *MockPayloadSerializer) checkRes func(*testing.T, *CreateWorkflowExecutionResponse, error) }{ { name: "success", prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { // Prepare CreateWorkflow call expectedRequest := &InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: CreateWorkflowModeWorkflowIDReuse, PreviousRunID: testRunID, PreviousLastWriteVersion: 1, NewWorkflowSnapshot: *sampleInternalWorkflowSnapshot(), WorkflowRequestMode: CreateWorkflowRequestModeReplicated, CurrentTimeStamp: time.Now(), } mockedStore.EXPECT().CreateWorkflowExecution(gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, actualRequest *InternalCreateWorkflowExecutionRequest) (*CreateWorkflowExecutionResponse, error) { assert.Equal(t, expectedRequest.RangeID, actualRequest.RangeID) assert.Equal(t, expectedRequest.Mode, actualRequest.Mode) assert.Equal(t, expectedRequest.PreviousRunID, actualRequest.PreviousRunID) assert.Equal(t, expectedRequest.PreviousLastWriteVersion, actualRequest.PreviousLastWriteVersion) assert.Equal(t, expectedRequest.WorkflowRequestMode, actualRequest.WorkflowRequestMode) assert.Equal(t, expectedRequest.NewWorkflowSnapshot, actualRequest.NewWorkflowSnapshot) assert.WithinDuration(t, expectedRequest.CurrentTimeStamp, actualRequest.CurrentTimeStamp, time.Second) return nil, nil }) // Prepare DeserializeWorkflow call mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(sampleResetPointsData(), nil).Times(1) mockedSerializer.EXPECT().SerializeVersionHistories(gomock.Any(), gomock.Any()).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeChecksum(gomock.Any(), gomock.Any()).Return(sampleCheckSumData(), nil).Times(1) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(1) }, checkRes: func(t *testing.T, response *CreateWorkflowExecutionResponse, err error) { assert.Equal(t, &CreateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &MutableStateUpdateSessionStats{ MutableStateSize: 91, ExecutionInfoSize: 20, ActivityInfoSize: 29, TimerInfoSize: 22, ChildInfoSize: 20, ActivityInfoCount: 1, TimerInfoCount: 2, ChildInfoCount: 1, TaskCountByCategory: map[HistoryTaskCategory]int{}, }, }, response) }, }, { name: "persistence error", prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { // Prepare DeserializeWorkflow call mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(sampleResetPointsData(), nil).Times(1) mockedSerializer.EXPECT().SerializeVersionHistories(gomock.Any(), gomock.Any()).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeChecksum(gomock.Any(), gomock.Any()).Return(sampleCheckSumData(), nil).Times(1) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(1) // Persistence call will fail mockedStore.EXPECT().CreateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) }, checkRes: func(t *testing.T, response *CreateWorkflowExecutionResponse, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, { name: "serialize workflow snapshot error", prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(nil, assert.AnError).Times(1) }, checkRes: func(t *testing.T, response *CreateWorkflowExecutionResponse, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, } { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) mockedSerializer := NewMockPayloadSerializer(ctrl) tc.prepareMocks(mockedStore, mockedSerializer) request := &CreateWorkflowExecutionRequest{ RangeID: 1, Mode: CreateWorkflowModeWorkflowIDReuse, PreviousRunID: testRunID, PreviousLastWriteVersion: 1, NewWorkflowSnapshot: *sampleWorkflowSnapshot(), WorkflowRequestMode: CreateWorkflowRequestModeReplicated, } manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), mockedSerializer, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) res, err := manager.CreateWorkflowExecution(context.Background(), request) tc.checkRes(t, res, err) }) } } func TestConflictResolveWorkflowExecution(t *testing.T) { for _, tc := range []struct { name string request *ConflictResolveWorkflowExecutionRequest prepareMocks func(*MockExecutionStore, *MockPayloadSerializer) checkRes func(*testing.T, *ConflictResolveWorkflowExecutionResponse, error) }{ { name: "only snapshot", request: &ConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: *sampleWorkflowSnapshot(), Encoding: constants.EncodingTypeThriftRW, }, prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { expectedRequest := &InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: *sampleInternalWorkflowSnapshot(), CurrentTimeStamp: time.Now(), } mockedStore.EXPECT().ConflictResolveWorkflowExecution(gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, actualRequest *InternalConflictResolveWorkflowExecutionRequest) error { assert.Equal(t, expectedRequest.RangeID, actualRequest.RangeID) assert.Equal(t, expectedRequest.Mode, actualRequest.Mode) assert.Equal(t, expectedRequest.ResetWorkflowSnapshot, actualRequest.ResetWorkflowSnapshot) assert.WithinDuration(t, expectedRequest.CurrentTimeStamp, actualRequest.CurrentTimeStamp, time.Second) return nil }) mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(sampleResetPointsData(), nil).Times(1) mockedSerializer.EXPECT().SerializeVersionHistories(gomock.Any(), gomock.Any()).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeChecksum(gomock.Any(), gomock.Any()).Return(sampleCheckSumData(), nil).Times(1) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(1) }, checkRes: func(t *testing.T, response *ConflictResolveWorkflowExecutionResponse, err error) { assert.NoError(t, err) assert.Equal(t, &ConflictResolveWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &MutableStateUpdateSessionStats{ MutableStateSize: 91, ExecutionInfoSize: 20, ActivityInfoSize: 29, TimerInfoSize: 22, ChildInfoSize: 20, ActivityInfoCount: 1, TimerInfoCount: 2, ChildInfoCount: 1, TaskCountByCategory: map[HistoryTaskCategory]int{}, }, }, response) }, }, { name: "only snapshot fail", request: &ConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: *sampleWorkflowSnapshot(), Encoding: constants.EncodingTypeThriftRW, }, prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(nil, assert.AnError).Times(1) }, checkRes: func(t *testing.T, response *ConflictResolveWorkflowExecutionResponse, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, { name: "current workflow mutation", request: &ConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: *sampleWorkflowSnapshot(), Encoding: constants.EncodingTypeThriftRW, CurrentWorkflowMutation: sampleWorkflowMutation(), }, prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { expectedRequest := &InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: *sampleInternalWorkflowSnapshot(), CurrentWorkflowMutation: sampleInternalWorkflowMutation(), CurrentTimeStamp: time.Now(), } mockedStore.EXPECT().ConflictResolveWorkflowExecution(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, actualRequest *InternalConflictResolveWorkflowExecutionRequest) error { assert.Equal(t, expectedRequest.RangeID, actualRequest.RangeID) assert.Equal(t, expectedRequest.Mode, actualRequest.Mode) assert.Equal(t, expectedRequest.ResetWorkflowSnapshot, actualRequest.ResetWorkflowSnapshot) assert.WithinDuration(t, expectedRequest.CurrentTimeStamp, actualRequest.CurrentTimeStamp, time.Second) return nil }) // Mutation call mockedSerializer.EXPECT().SerializeEvent(activityStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(sampleResetPointsData(), nil).Times(1) // Expect mutation doubles the calls for workflow execution serialization mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(2) mockedSerializer.EXPECT().SerializeResetPoints(gomock.Any(), gomock.Any()).Return(sampleResetPointsData(), nil).Times(1) mockedSerializer.EXPECT().SerializeVersionHistories(gomock.Any(), gomock.Any()).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(activityScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(2) mockedSerializer.EXPECT().SerializeEvent(activityStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(2) mockedSerializer.EXPECT().SerializeEvent(childWorkflowScheduledEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeEvent(childWorkflowStartedEvent(), constants.EncodingTypeThriftRW).Return(sampleEventData(), nil).Times(1) mockedSerializer.EXPECT().SerializeChecksum(gomock.Any(), gomock.Any()).Return(sampleCheckSumData(), nil).Times(2) mockedSerializer.EXPECT().SerializeActiveClusterSelectionPolicy(generateActiveClusterSelectionPolicy(), constants.EncodingTypeThriftRW).Return(sampleActiveClusterSelectionPolicyData(), nil).Times(2) }, checkRes: func(t *testing.T, response *ConflictResolveWorkflowExecutionResponse, err error) { assert.NoError(t, err) assert.Equal(t, &ConflictResolveWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &MutableStateUpdateSessionStats{ MutableStateSize: 161, ExecutionInfoSize: 40, ActivityInfoSize: 49, TimerInfoSize: 32, ChildInfoSize: 40, ActivityInfoCount: 2, TimerInfoCount: 3, ChildInfoCount: 2, TaskCountByCategory: map[HistoryTaskCategory]int{}, }, }, response) }, }, { name: "new workflow snapshot", request: &ConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: *sampleWorkflowSnapshot(), Encoding: constants.EncodingTypeThriftRW, }, prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { mockedSerializer.EXPECT().SerializeEvent(completionEvent(), constants.EncodingTypeThriftRW).Return(nil, assert.AnError).Times(1) }, checkRes: func(t *testing.T, response *ConflictResolveWorkflowExecutionResponse, err error) { assert.ErrorIs(t, err, assert.AnError) }, }, } { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) mockedSerializer := NewMockPayloadSerializer(ctrl) tc.prepareMocks(mockedStore, mockedSerializer) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), mockedSerializer, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) res, err := manager.ConflictResolveWorkflowExecution(context.Background(), tc.request) tc.checkRes(t, res, err) }) } } func TestCreateFailoverMarkerTasks(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), nil, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) req := &CreateFailoverMarkersRequest{ Markers: []*FailoverMarkerTask{{ TaskData: TaskData{ Version: 0, TaskID: 0, VisibilityTimestamp: time.Time{}, }, DomainID: "1", }}, RangeID: 1, CurrentTimeStamp: time.Now(), } mockedStore.EXPECT().CreateFailoverMarkerTasks(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *CreateFailoverMarkersRequest) error { assert.Equal(t, req.RangeID, req.RangeID) assert.Equal(t, req.Markers, req.Markers) assert.WithinDuration(t, req.CurrentTimeStamp, req.CurrentTimeStamp, time.Second) return nil }) err := manager.CreateFailoverMarkerTasks(context.Background(), req) assert.NoError(t, err) } func TestGetActiveClusterSelectionPolicy(t *testing.T) { ctx := context.Background() domainID := "domainID" workflowID := "workflowID" runID := "runID" tests := []struct { name string prepareMocks func(*MockExecutionStore, *MockPayloadSerializer) want *types.ActiveClusterSelectionPolicy wantErr error }{ { name: "success", prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { data := sampleActiveClusterSelectionPolicyData() mockedStore.EXPECT().GetActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID). Return(data, nil) mockedSerializer.EXPECT().DeserializeActiveClusterSelectionPolicy(data).Return(sampleActiveClusterSelectionPolicy(), nil) }, want: sampleActiveClusterSelectionPolicy(), }, { name: "store error", prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { mockedStore.EXPECT().GetActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID). Return(nil, assert.AnError) }, wantErr: assert.AnError, }, { name: "store returned nil", prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { mockedStore.EXPECT().GetActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID). Return(nil, nil) }, wantErr: &types.EntityNotExistsError{ Message: "active cluster selection policy not found", }, }, { name: "deserialize error", prepareMocks: func(mockedStore *MockExecutionStore, mockedSerializer *MockPayloadSerializer) { data := sampleActiveClusterSelectionPolicyData() mockedStore.EXPECT().GetActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID). Return(data, nil) mockedSerializer.EXPECT().DeserializeActiveClusterSelectionPolicy(data).Return(nil, assert.AnError) }, wantErr: assert.AnError, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) mockedSerializer := NewMockPayloadSerializer(ctrl) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), mockedSerializer, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.prepareMocks(mockedStore, mockedSerializer) policy, err := manager.GetActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID) if test.wantErr != nil { assert.EqualError(t, err, test.wantErr.Error()) } else { assert.NoError(t, err) assert.Equal(t, test.want, policy) } }) } } func TestDeleteActiveClusterSelectionPolicy(t *testing.T) { ctx := context.Background() domainID := "domainID" workflowID := "workflowID" runID := "runID" tests := []struct { name string prepareMocks func(*MockExecutionStore) wantErr error }{ { name: "success", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().DeleteActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID).Return(nil) }, }, { name: "store error", prepareMocks: func(mockedStore *MockExecutionStore) { mockedStore.EXPECT().DeleteActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID).Return(assert.AnError) }, wantErr: assert.AnError, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockedStore := NewMockExecutionStore(ctrl) manager := NewExecutionManagerImpl(mockedStore, testlogger.New(t), nil, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.prepareMocks(mockedStore) err := manager.DeleteActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID) if test.wantErr != nil { assert.EqualError(t, err, test.wantErr.Error()) } else { assert.NoError(t, err) } }) } } func sampleInternalActivityInfo(name string) *InternalActivityInfo { return &InternalActivityInfo{ Version: 1, ScheduleID: 1, ActivityID: name, TaskList: "TaskList", TaskListKind: TaskListKindSticky, ScheduledEvent: NewDataBlob([]byte(fmt.Sprintf("%s-activity-scheduled-event", name)), constants.EncodingTypeThriftRW), StartedEvent: NewDataBlob([]byte(fmt.Sprintf("%s-activity-started-event", name)), constants.EncodingTypeThriftRW), } } func sampleActivityInfo(name string, id int64) *ActivityInfo { return &ActivityInfo{ Version: 1, ScheduleID: 1, ActivityID: name, TaskList: "TaskList", TaskListKind: TaskListKindSticky, ScheduledEvent: &types.HistoryEvent{ ID: id, }, StartedEvent: &types.HistoryEvent{ ID: id, }, } } var ( startedTimestamp = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) scheduledTimestamp = time.Date(2020, 2, 1, 0, 0, 0, 0, time.UTC) originalScheduledTimestamp = time.Date(2020, 3, 1, 0, 0, 0, 0, time.UTC) wfTimeout = 10 * time.Second decisionTimeout = 5 * time.Second ) func sampleInternalWorkflowExecutionInfo() *InternalWorkflowExecutionInfo { return &InternalWorkflowExecutionInfo{ DomainID: testDomain, WorkflowTimeout: wfTimeout, DecisionStartToCloseTimeout: decisionTimeout, DecisionStartedTimestamp: startedTimestamp, DecisionScheduledTimestamp: scheduledTimestamp, DecisionOriginalScheduledTimestamp: originalScheduledTimestamp, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowType, NextEventID: 10, CompletionEvent: sampleEventData(), AutoResetPoints: sampleResetPointsData(), HistorySize: 1024, ActiveClusterSelectionPolicy: sampleActiveClusterSelectionPolicyData(), } } func sampleWorkflowExecutionInfo() *WorkflowExecutionInfo { return &WorkflowExecutionInfo{ DomainID: testDomain, WorkflowTimeout: int32(wfTimeout.Seconds()), DecisionStartToCloseTimeout: int32(decisionTimeout.Seconds()), DecisionScheduledTimestamp: scheduledTimestamp.UnixNano(), DecisionStartedTimestamp: startedTimestamp.UnixNano(), DecisionOriginalScheduledTimestamp: originalScheduledTimestamp.UnixNano(), WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowType, NextEventID: 10, CompletionEvent: completionEvent(), AutoResetPoints: generateResetPoints(), ActiveClusterSelectionPolicy: generateActiveClusterSelectionPolicy(), } } func sampleInternalWorkflowMutation() *InternalWorkflowMutation { return &InternalWorkflowMutation{ ExecutionInfo: sampleInternalWorkflowExecutionInfo(), UpsertActivityInfos: []*InternalActivityInfo{ { Version: 1, ScheduledEvent: sampleEventData(), StartedEvent: sampleEventData(), }, }, UpsertTimerInfos: []*TimerInfo{ { TimerID: "test-timer", }, }, UpsertChildExecutionInfos: []*InternalChildExecutionInfo{ { DomainID: testDomainID, WorkflowTypeName: testWorkflowType, Version: 1, InitiatedEvent: sampleEventData(), StartedEvent: sampleEventData(), }, }, Checksum: generateChecksum(), ChecksumData: sampleTestCheckSumData(), StartVersion: constants.EmptyVersion, LastWriteVersion: constants.EmptyVersion, } } func sampleWorkflowMutation() *WorkflowMutation { return &WorkflowMutation{ ExecutionInfo: sampleWorkflowExecutionInfo(), ExecutionStats: sampleWorkflowExecutionStats(), UpsertActivityInfos: []*ActivityInfo{ { Version: 1, ScheduledEvent: activityScheduledEvent(), StartedEvent: activityStartedEvent(), }, }, UpsertTimerInfos: []*TimerInfo{{TimerID: "test-timer"}}, Checksum: generateChecksum(), UpsertChildExecutionInfos: []*ChildExecutionInfo{ { DomainID: testDomainID, WorkflowTypeName: testWorkflowType, Version: 1, InitiatedEvent: childWorkflowScheduledEvent(), StartedEvent: childWorkflowStartedEvent(), }, }, } } func sampleWorkflowSnapshot() *WorkflowSnapshot { return &WorkflowSnapshot{ ExecutionInfo: sampleWorkflowExecutionInfo(), ExecutionStats: sampleWorkflowExecutionStats(), VersionHistories: &VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*VersionHistory{ { BranchToken: []byte("test-branch-token"), Items: []*VersionHistoryItem{ { EventID: 1, Version: 1, }, }, }, }, }, Checksum: generateChecksum(), ActivityInfos: []*ActivityInfo{ { Version: 1, ScheduleID: 1, ActivityID: "activity1", ScheduledEvent: activityScheduledEvent(), StartedID: 2, StartedEvent: activityStartedEvent(), StartedTime: startedTimestamp, }, }, ChildExecutionInfos: []*ChildExecutionInfo{ { Version: 1, InitiatedID: 1, InitiatedEvent: childWorkflowScheduledEvent(), StartedID: 2, StartedEvent: childWorkflowStartedEvent(), CreateRequestID: "create-request-id", DomainID: testDomainID, WorkflowTypeName: testWorkflowType, }, }, TimerInfos: []*TimerInfo{ { TimerID: "test-timer", StartedID: 1, ExpiryTime: originalScheduledTimestamp, TaskStatus: 1, }, { TimerID: "test-timer-2", StartedID: 2, ExpiryTime: originalScheduledTimestamp, TaskStatus: 2, }, }, } } func sampleInternalWorkflowSnapshot() *InternalWorkflowSnapshot { return &InternalWorkflowSnapshot{ ExecutionInfo: sampleInternalWorkflowExecutionInfo(), VersionHistories: sampleEventData(), StartVersion: 1, LastWriteVersion: 1, ActivityInfos: []*InternalActivityInfo{ { Version: 1, ScheduleID: 1, ActivityID: "activity1", ScheduledEvent: sampleEventData(), StartedEvent: sampleEventData(), StartedID: 2, StartedTime: startedTimestamp, }, }, TimerInfos: []*TimerInfo{ { TimerID: "test-timer", StartedID: 1, ExpiryTime: originalScheduledTimestamp, TaskStatus: 1, }, { TimerID: "test-timer-2", StartedID: 2, ExpiryTime: originalScheduledTimestamp, TaskStatus: 2, }, }, ChildExecutionInfos: []*InternalChildExecutionInfo{ { Version: 1, InitiatedID: 1, InitiatedEvent: sampleEventData(), StartedID: 2, StartedEvent: sampleEventData(), CreateRequestID: "create-request-id", DomainID: testDomainID, WorkflowTypeName: testWorkflowType, }, }, Checksum: generateChecksum(), ChecksumData: sampleCheckSumData(), } } func activityScheduledEvent() *types.HistoryEvent { return &types.HistoryEvent{ ID: 1, Timestamp: common.Ptr(scheduledTimestamp.UnixNano()), TaskID: 1, } } func activityStartedEvent() *types.HistoryEvent { return &types.HistoryEvent{ ID: 2, Timestamp: common.Ptr(startedTimestamp.UnixNano()), TaskID: 1, } } func childWorkflowScheduledEvent() *types.HistoryEvent { return &types.HistoryEvent{ ID: 1, Timestamp: common.Ptr(scheduledTimestamp.UnixNano()), TaskID: 1, } } func completionEvent() *types.HistoryEvent { return &types.HistoryEvent{ ID: 99, Timestamp: common.Ptr(startedTimestamp.UnixNano()), TaskID: 1, } } func childWorkflowStartedEvent() *types.HistoryEvent { return &types.HistoryEvent{ ID: 2, Timestamp: common.Ptr(startedTimestamp.UnixNano()), TaskID: 1, } } func sampleWorkflowExecutionStats() *ExecutionStats { return &ExecutionStats{ HistorySize: 1024, } } func sampleTestCheckSumData() *DataBlob { return &DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("test-checksum"), } } func sampleEventData() *DataBlob { return NewDataBlob([]byte("test-event"), constants.EncodingTypeThriftRW) } func sampleResetPointsData() *DataBlob { return NewDataBlob([]byte("test-reset-points"), constants.EncodingTypeThriftRW) } func sampleCheckSumData() *DataBlob { return NewDataBlob([]byte("test-checksum"), constants.EncodingTypeThriftRW) } func sampleActiveClusterSelectionPolicyData() *DataBlob { return NewDataBlob([]byte("test-active-cluster-selection-policy"), constants.EncodingTypeThriftRW) } func sampleActiveClusterSelectionPolicy() *types.ActiveClusterSelectionPolicy { return &types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyRegionSticky.Ptr(), StickyRegion: "region-1", } } func sampleInternalChildExecutionInfo(initEventID, startedEventID int64) *InternalChildExecutionInfo { return &InternalChildExecutionInfo{ Version: initEventID, InitiatedID: initEventID, InitiatedEvent: sampleEventDataWithVersion(initEventID), StartedID: startedEventID, StartedEvent: sampleEventDataWithVersion(startedEventID), CreateRequestID: "create-request-id", DomainID: testDomainID, WorkflowTypeName: testWorkflowType, StartedWorkflowID: "workflow-id", StartedRunID: "run-id", } } func sampleEventDataWithVersion(i int64) *DataBlob { return NewDataBlob([]byte(fmt.Sprintf("test-event-%d", i)), constants.EncodingTypeThriftRW) } ================================================ FILE: common/persistence/history_manager.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "context" "encoding/json" "fmt" "github.com/pborman/uuid" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) type ( // historyV2PagingToken is used to serialize/deserialize pagination token for ReadHistoryBranchRequest historyV2PagingToken struct { // TODO remove LastEventVersion once 3+DC is enabled for all workflow LastEventVersion int64 LastEventID int64 // the pagination token passing to persistence StoreToken []byte // recording which branchRange it is reading CurrentRangeIndex int FinalRangeIndex int // LastNodeID is the last known node ID attached to a history node LastNodeID int64 // LastTransactionID is the last known transaction ID attached to a history node LastTransactionID int64 } // historyManagerImpl implements HistoryManager based on HistoryStore and PayloadSerializer historyV2ManagerImpl struct { historySerializer PayloadSerializer persistence HistoryStore logger log.Logger thriftEncoder codec.BinaryEncoder transactionSizeLimit dynamicproperties.IntPropertyFn serializeTokenFn func(*historyV2PagingToken) ([]byte, error) deserializeTokenFn func([]byte, int64) (*historyV2PagingToken, error) readRawHistoryBranchFn func(context.Context, *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) readHistoryBranchFn func(context.Context, bool, *ReadHistoryBranchRequest) ([]*types.HistoryEvent, []*types.History, []byte, int, int64, error) timeSrc clock.TimeSource } ) const ( notStartedIndex = -1 defaultLastNodeID = constants.FirstEventID - 1 defaultLastTransactionID = int64(0) ) var ( ErrCorruptedHistory = &types.InternalDataInconsistencyError{Message: "corrupted history event batch, eventID is not continuous"} ) var _ HistoryManager = (*historyV2ManagerImpl)(nil) // NewHistoryV2ManagerImpl returns new HistoryManager func NewHistoryV2ManagerImpl( persistence HistoryStore, logger log.Logger, historySerializer PayloadSerializer, binaryEncoder codec.BinaryEncoder, transactionSizeLimit dynamicproperties.IntPropertyFn, ) HistoryManager { hm := &historyV2ManagerImpl{ historySerializer: historySerializer, persistence: persistence, logger: logger, thriftEncoder: binaryEncoder, transactionSizeLimit: transactionSizeLimit, serializeTokenFn: serializeToken, deserializeTokenFn: deserializeToken, timeSrc: clock.NewRealTimeSource(), } hm.readRawHistoryBranchFn = hm.readRawHistoryBranch hm.readHistoryBranchFn = hm.readHistoryBranch return hm } func (m *historyV2ManagerImpl) GetName() string { return m.persistence.GetName() } // ForkHistoryBranch forks a new branch from a old branch func (m *historyV2ManagerImpl) ForkHistoryBranch( ctx context.Context, request *ForkHistoryBranchRequest, ) (*ForkHistoryBranchResponse, error) { if request.ForkNodeID <= 1 { return nil, &InvalidPersistenceRequestError{ Msg: "ForkNodeID must be > 1", } } shardID, err := getShardID(request.ShardID) if err != nil { return nil, &types.InternalServiceError{ Message: err.Error(), } } var forkBranch workflow.HistoryBranch err = m.thriftEncoder.Decode(request.ForkBranchToken, &forkBranch) if err != nil { return nil, err } req := &InternalForkHistoryBranchRequest{ ForkBranchInfo: *thrift.ToHistoryBranch(&forkBranch), ForkNodeID: request.ForkNodeID, NewBranchID: uuid.New(), Info: request.Info, ShardID: shardID, CurrentTimeStamp: m.timeSrc.Now(), } resp, err := m.persistence.ForkHistoryBranch(ctx, req) if err != nil { return nil, err } token, err := m.thriftEncoder.Encode(thrift.FromHistoryBranch(&resp.NewBranchInfo)) if err != nil { return nil, err } return &ForkHistoryBranchResponse{ NewBranchToken: token, }, nil } // DeleteHistoryBranch removes a branch func (m *historyV2ManagerImpl) DeleteHistoryBranch( ctx context.Context, request *DeleteHistoryBranchRequest, ) error { shardID, err := getShardID(request.ShardID) if err != nil { m.logger.Error("shardID is not set in delete history operation", tag.Error(err)) return &types.InternalServiceError{ Message: err.Error(), } } var branch workflow.HistoryBranch err = m.thriftEncoder.Decode(request.BranchToken, &branch) if err != nil { return err } req := &InternalDeleteHistoryBranchRequest{ BranchInfo: *thrift.ToHistoryBranch(&branch), ShardID: shardID, } return m.persistence.DeleteHistoryBranch(ctx, req) } // GetHistoryTree returns all branch information of a tree func (m *historyV2ManagerImpl) GetHistoryTree( ctx context.Context, request *GetHistoryTreeRequest, ) (*GetHistoryTreeResponse, error) { if len(request.TreeID) == 0 { var branch workflow.HistoryBranch err := m.thriftEncoder.Decode(request.BranchToken, &branch) if err != nil { return nil, err } request.TreeID = branch.GetTreeID() } internalRequest := &InternalGetHistoryTreeRequest{ TreeID: request.TreeID, ShardID: request.ShardID, BranchToken: request.BranchToken, } resp, err := m.persistence.GetHistoryTree(ctx, internalRequest) if err != nil { return nil, err } var branches []*workflow.HistoryBranch for _, b := range resp.Branches { branches = append(branches, thrift.FromHistoryBranch(b)) } return &GetHistoryTreeResponse{ Branches: branches, }, nil } // AppendHistoryNodes add(or override) a node to a history branch func (m *historyV2ManagerImpl) AppendHistoryNodes( ctx context.Context, request *AppendHistoryNodesRequest, ) (*AppendHistoryNodesResponse, error) { shardID, err := getShardID(request.ShardID) if err != nil { m.logger.Error("shardID is not set in append history nodes operation", tag.Error(err)) return nil, &types.InternalServiceError{ Message: err.Error(), } } if len(request.Events) == 0 { return nil, &InvalidPersistenceRequestError{ Msg: "events to be appended cannot be empty", } } version := request.Events[0].Version nodeID := request.Events[0].ID lastID := nodeID - 1 if nodeID <= 0 { return nil, &InvalidPersistenceRequestError{ Msg: "eventID cannot be less than 1", } } for _, e := range request.Events { if e.Version != version { return nil, &InvalidPersistenceRequestError{ Msg: "event version must be the same inside a batch", } } if e.ID != lastID+1 { return nil, &InvalidPersistenceRequestError{ Msg: "event ID must be continuous", } } lastID++ } var branch workflow.HistoryBranch err = m.thriftEncoder.Decode(request.BranchToken, &branch) if err != nil { return nil, err } // nodeID will be the first eventID blob, err := m.historySerializer.SerializeBatchEvents(request.Events, request.Encoding) if err != nil { return nil, err } size := len(blob.Data) sizeLimit := m.transactionSizeLimit() if size > sizeLimit { return nil, &TransactionSizeLimitError{ Msg: fmt.Sprintf("transaction size of %v bytes exceeds limit of %v bytes", size, sizeLimit), } } req := &InternalAppendHistoryNodesRequest{ IsNewBranch: request.IsNewBranch, Info: request.Info, BranchInfo: *thrift.ToHistoryBranch(&branch), NodeID: nodeID, Events: blob, TransactionID: request.TransactionID, ShardID: shardID, CurrentTimeStamp: m.timeSrc.Now(), } err = m.persistence.AppendHistoryNodes(ctx, req) return &AppendHistoryNodesResponse{ DataBlob: *blob, }, err } func (m *historyV2ManagerImpl) GetAllHistoryTreeBranches( ctx context.Context, request *GetAllHistoryTreeBranchesRequest, ) (*GetAllHistoryTreeBranchesResponse, error) { return m.persistence.GetAllHistoryTreeBranches(ctx, request) } // ReadHistoryBranchByBatch returns history node data for a branch by batch // Pagination is implemented here, the actual minNodeID passing to persistence layer is calculated along with token's LastNodeID func (m *historyV2ManagerImpl) ReadHistoryBranchByBatch( ctx context.Context, request *ReadHistoryBranchRequest, ) (*ReadHistoryBranchByBatchResponse, error) { resp := &ReadHistoryBranchByBatchResponse{} var err error _, resp.History, resp.NextPageToken, resp.Size, resp.LastFirstEventID, err = m.readHistoryBranchFn(ctx, true, request) if err != nil { return nil, err } return resp, nil } // ReadHistoryBranch returns history node data for a branch // Pagination is implemented here, the actual minNodeID passing to persistence layer is calculated along with token's LastNodeID func (m *historyV2ManagerImpl) ReadHistoryBranch( ctx context.Context, request *ReadHistoryBranchRequest, ) (*ReadHistoryBranchResponse, error) { resp := &ReadHistoryBranchResponse{} var err error resp.HistoryEvents, _, resp.NextPageToken, resp.Size, resp.LastFirstEventID, err = m.readHistoryBranchFn(ctx, false, request) if err != nil { return nil, err } return resp, nil } // ReadRawHistoryBranch returns raw history binary data for a branch // Pagination is implemented here, the actual minNodeID passing to persistence layer is calculated along with token's LastNodeID // NOTE: this API should only be used by 3+DC func (m *historyV2ManagerImpl) ReadRawHistoryBranch( ctx context.Context, request *ReadHistoryBranchRequest, ) (*ReadRawHistoryBranchResponse, error) { dataBlobs, token, dataSize, _, err := m.readRawHistoryBranchFn(ctx, request) if err != nil { return nil, err } nextPageToken, err := m.serializeTokenFn(token) if err != nil { return nil, err } return &ReadRawHistoryBranchResponse{ HistoryEventBlobs: dataBlobs, NextPageToken: nextPageToken, Size: dataSize, }, nil } func (m *historyV2ManagerImpl) readRawHistoryBranch( ctx context.Context, request *ReadHistoryBranchRequest, ) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { shardID, err := getShardID(request.ShardID) if err != nil { m.logger.Error("shardID is not set in read history branch operation", tag.Error(err)) return nil, nil, 0, nil, &types.InternalServiceError{Message: err.Error()} } if request.PageSize <= 0 || request.MinEventID >= request.MaxEventID { return nil, nil, 0, nil, &InvalidPersistenceRequestError{ Msg: fmt.Sprintf( "no events can be found for pageSize %v, minEventID %v, maxEventID: %v", request.PageSize, request.MinEventID, request.MaxEventID, ), } } defaultLastEventID := request.MinEventID - 1 token, err := m.deserializeTokenFn( request.NextPageToken, defaultLastEventID, ) if err != nil { return nil, nil, 0, nil, err } var branch workflow.HistoryBranch err = m.thriftEncoder.Decode(request.BranchToken, &branch) if err != nil { return nil, nil, 0, nil, err } treeID := *branch.TreeID branchID := *branch.BranchID allBRs := branch.Ancestors // We may also query the current branch from beginNodeID beginNodeID := constants.FirstEventID if len(branch.Ancestors) > 0 { beginNodeID = *branch.Ancestors[len(branch.Ancestors)-1].EndNodeID } allBRs = append(allBRs, &workflow.HistoryBranchRange{ BranchID: &branchID, BeginNodeID: common.Int64Ptr(beginNodeID), EndNodeID: common.Int64Ptr(request.MaxEventID), }) if token.CurrentRangeIndex == notStartedIndex { for idx, br := range allBRs { // this range won't contain any nodes needed if request.MinEventID >= *br.EndNodeID { continue } // similarly, the ranges and the rest won't contain any nodes needed, if request.MaxEventID <= *br.BeginNodeID { break } if token.CurrentRangeIndex == notStartedIndex { token.CurrentRangeIndex = idx } token.FinalRangeIndex = idx } if token.CurrentRangeIndex == notStartedIndex { return nil, nil, 0, nil, &types.InternalDataInconsistencyError{ Message: "branchRange is corrupted", } } } minNodeID := request.MinEventID maxNodeID := *allBRs[token.CurrentRangeIndex].EndNodeID if request.MaxEventID < maxNodeID { maxNodeID = request.MaxEventID } pageSize := request.PageSize req := &InternalReadHistoryBranchRequest{ TreeID: treeID, BranchID: *allBRs[token.CurrentRangeIndex].BranchID, MinNodeID: minNodeID, MaxNodeID: maxNodeID, NextPageToken: token.StoreToken, LastNodeID: token.LastNodeID, LastTransactionID: token.LastTransactionID, ShardID: shardID, PageSize: pageSize, } resp, err := m.persistence.ReadHistoryBranch(ctx, req) if err != nil { return nil, nil, 0, nil, err } // TODO: consider if it's possible to remove this branch if len(resp.History) == 0 && len(request.NextPageToken) == 0 { return nil, nil, 0, nil, &types.EntityNotExistsError{Message: "Workflow execution history not found."} } dataBlobs := resp.History dataSize := 0 for _, dataBlob := range resp.History { dataSize += len(dataBlob.Data) } token.StoreToken = resp.NextPageToken token.LastNodeID = resp.LastNodeID token.LastTransactionID = resp.LastTransactionID // NOTE: in this method, we need to make sure eventVersion is NOT // decreasing(otherwise we skip the events), eventID should be continuous(otherwise return error) logger := m.logger.WithTags(tag.WorkflowBranchID(*branch.BranchID), tag.WorkflowTreeID(*branch.TreeID)) return dataBlobs, token, dataSize, logger, nil } func (m *historyV2ManagerImpl) readHistoryBranch( ctx context.Context, byBatch bool, request *ReadHistoryBranchRequest, ) ([]*types.HistoryEvent, []*types.History, []byte, int, int64, error) { dataBlobs, token, dataSize, logger, err := m.readRawHistoryBranchFn(ctx, request) if err != nil { return nil, nil, nil, 0, 0, err } defaultLastEventID := request.MinEventID - 1 historyEvents := make([]*types.HistoryEvent, 0, request.PageSize) historyEventBatches := make([]*types.History, 0, request.PageSize) // first_event_id of the last batch lastFirstEventID := constants.EmptyEventID for _, batch := range dataBlobs { events, err := m.historySerializer.DeserializeBatchEvents(batch) if err != nil { return nil, nil, nil, 0, 0, err } if len(events) == 0 { logger.Error("Empty events in a batch") return nil, nil, nil, 0, 0, &types.InternalDataInconsistencyError{ Message: "corrupted history event batch, empty events", } } firstEvent := events[0] // first eventCount := len(events) // length lastEvent := events[eventCount-1] // last if firstEvent.Version != lastEvent.Version || firstEvent.ID+int64(eventCount-1) != lastEvent.ID { // in a single batch, version should be the same, and ID should be continous logger.Error("Corrupted event batch", tag.FirstEventVersion(firstEvent.Version), tag.WorkflowFirstEventID(firstEvent.ID), tag.LastEventVersion(lastEvent.Version), tag.WorkflowNextEventID(lastEvent.ID), tag.Counter(eventCount)) return nil, nil, nil, 0, 0, &types.InternalDataInconsistencyError{ Message: "corrupted history event batch, wrong version and IDs", } } if firstEvent.Version < token.LastEventVersion { // version decrease means the this batch are all stale events, we should skip logger.Info("Stale event batch with smaller version", tag.FirstEventVersion(firstEvent.Version), tag.TokenLastEventVersion(token.LastEventVersion)) continue } if firstEvent.ID <= token.LastEventID { // we could see it because first batch of next page has a smaller txn_id logger.Info("Stale event batch with eventID", tag.WorkflowFirstEventID(firstEvent.ID), tag.TokenLastEventID(token.LastEventID)) continue } if firstEvent.ID != token.LastEventID+1 { // We assume application layer want to read from MinEventID(inclusive) // However, for getting history from remote cluster, there is scenario that we have to read from middle without knowing the firstEventID. // In that case we don't validate history continuousness for the first page // TODO: in this case, some events returned can be invalid(stale). application layer need to make sure it won't make any problems to XDC if defaultLastEventID == 0 || token.LastEventID != defaultLastEventID { logger.Error("Corrupted incontinouous event batch", tag.FirstEventVersion(firstEvent.Version), tag.WorkflowFirstEventID(firstEvent.ID), tag.LastEventVersion(lastEvent.Version), tag.WorkflowNextEventID(lastEvent.ID), tag.TokenLastEventVersion(token.LastEventVersion), tag.TokenLastEventID(token.LastEventID), tag.Counter(eventCount)) return nil, nil, nil, 0, 0, ErrCorruptedHistory } } token.LastEventVersion = firstEvent.Version token.LastEventID = lastEvent.ID if byBatch { historyEventBatches = append(historyEventBatches, &types.History{Events: events}) } else { historyEvents = append(historyEvents, events...) } lastFirstEventID = firstEvent.ID } nextPageToken, err := m.serializeTokenFn(token) if err != nil { return nil, nil, nil, 0, 0, err } return historyEvents, historyEventBatches, nextPageToken, dataSize, lastFirstEventID, nil } func deserializeToken( data []byte, defaultLastEventID int64, ) (*historyV2PagingToken, error) { if len(data) == 0 { return &historyV2PagingToken{ LastEventID: defaultLastEventID, LastEventVersion: constants.EmptyVersion, CurrentRangeIndex: notStartedIndex, LastNodeID: defaultLastNodeID, LastTransactionID: defaultLastTransactionID, }, nil } token := historyV2PagingToken{} err := json.Unmarshal(data, &token) if err != nil { return nil, err } return &token, nil } func serializeToken(pagingToken *historyV2PagingToken) ([]byte, error) { if len(pagingToken.StoreToken) == 0 { if pagingToken.CurrentRangeIndex == pagingToken.FinalRangeIndex { // this means that we have reached the final page of final branchRange return nil, nil } pagingToken.CurrentRangeIndex++ } return json.Marshal(pagingToken) } func (m *historyV2ManagerImpl) Close() { m.persistence.Close() } func getShardID(shardID *int) (int, error) { if shardID == nil { return 0, fmt.Errorf("shardID is not set for persistence operation") } return *shardID, nil } ================================================ FILE: common/persistence/history_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) func setUpMocksForHistoryV2Manager(t *testing.T) (*historyV2ManagerImpl, *MockHistoryStore, *MockPayloadSerializer, *codec.MockBinaryEncoder) { ctrl := gomock.NewController(t) mockStore := NewMockHistoryStore(ctrl) mockSerializer := NewMockPayloadSerializer(ctrl) mockEncoder := codec.NewMockBinaryEncoder(ctrl) logger := log.NewNoop() mockStore.EXPECT().GetName().Return("mock history store") historyManager := NewHistoryV2ManagerImpl( mockStore, logger, mockSerializer, mockEncoder, dynamicproperties.GetIntPropertyFn(1024*10), ) assert.Equal(t, "mock history store", historyManager.GetName()) return historyManager.(*historyV2ManagerImpl), mockStore, mockSerializer, mockEncoder } func TestForkHistoryBranch(t *testing.T) { testCases := []struct { name string setupMock func(*MockHistoryStore, *codec.MockBinaryEncoder) request *ForkHistoryBranchRequest expectError bool expectedError string expected *ForkHistoryBranchResponse }{ { name: "success", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode([]byte("fork-branch"), &workflow.HistoryBranch{}).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") return nil }).Times(1) mockStore.EXPECT(). ForkHistoryBranch(gomock.Any(), gomock.Any()). Return(&InternalForkHistoryBranchResponse{ NewBranchInfo: types.HistoryBranch{ TreeID: "new-tree-id", BranchID: "new-branch-id", }, }, nil).Times(1) mockEncoder.EXPECT(). Encode(&workflow.HistoryBranch{ TreeID: common.StringPtr("new-tree-id"), BranchID: common.StringPtr("new-branch-id"), }). Return([]byte("new-branch-token"), nil).Times(1) }, request: &ForkHistoryBranchRequest{ ForkBranchToken: []byte("fork-branch"), ForkNodeID: 2, Info: "fork info", ShardID: common.Ptr(10), }, expectError: false, expected: &ForkHistoryBranchResponse{ NewBranchToken: []byte("new-branch-token"), }, }, { name: "nil Shard ID", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { // No store or encoder interactions for invalid input }, request: &ForkHistoryBranchRequest{ ForkBranchToken: []byte("fork-branch"), ForkNodeID: 2, Info: "fork info", }, expectError: true, expectedError: "shardID is not set for persistence operation", }, { name: "invalid fork node ID", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { // No store or encoder interactions for invalid input }, request: &ForkHistoryBranchRequest{ ForkBranchToken: []byte("fork-branch"), ForkNodeID: 1, // Invalid ID Info: "fork info", ShardID: common.Ptr(10), }, expectError: true, expectedError: "ForkNodeID must be > 1", }, { name: "decode error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode(gomock.Any(), gomock.Any()). Return(errors.New("decode error")).Times(1) }, request: &ForkHistoryBranchRequest{ ForkBranchToken: []byte("fork-branch"), ForkNodeID: 2, Info: "fork info", ShardID: common.Ptr(10), }, expectError: true, expectedError: "decode error", }, { name: "persistence error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode(gomock.Any(), gomock.Any()).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") return nil }).Times(1) mockStore.EXPECT(). ForkHistoryBranch(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &ForkHistoryBranchRequest{ ForkBranchToken: []byte("fork-branch"), ForkNodeID: 2, Info: "fork info", ShardID: common.Ptr(10), }, expectError: true, expectedError: "persistence error", }, { name: "encode error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode(gomock.Any(), gomock.Any()).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") return nil }).Times(1) mockStore.EXPECT(). ForkHistoryBranch(gomock.Any(), gomock.Any()). Return(&InternalForkHistoryBranchResponse{ NewBranchInfo: types.HistoryBranch{ TreeID: "new-tree-id", BranchID: "new-branch-id", }, }, nil).Times(1) mockEncoder.EXPECT(). Encode(gomock.Any()).Return(nil, errors.New("encode error")) }, request: &ForkHistoryBranchRequest{ ForkBranchToken: []byte("fork-branch"), ForkNodeID: 2, Info: "fork info", ShardID: common.Ptr(10), }, expectError: true, expectedError: "encode error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { historyManager, mockStore, _, mockEncoder := setUpMocksForHistoryV2Manager(t) tc.setupMock(mockStore, mockEncoder) // Call the method resp, err := historyManager.ForkHistoryBranch(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } func TestDeleteHistoryBranch(t *testing.T) { testCases := []struct { name string setupMock func(*MockHistoryStore, *codec.MockBinaryEncoder) request *DeleteHistoryBranchRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode([]byte("branch-token"), &workflow.HistoryBranch{}).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") return nil }).Times(1) mockStore.EXPECT(). DeleteHistoryBranch(gomock.Any(), &InternalDeleteHistoryBranchRequest{ ShardID: 10, BranchInfo: types.HistoryBranch{ TreeID: "tree-id", BranchID: "branch-id", }, }). Return(nil).Times(1) }, request: &DeleteHistoryBranchRequest{ BranchToken: []byte("branch-token"), ShardID: common.Ptr(10), }, expectError: false, }, { name: "nil shard ID error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { }, request: &DeleteHistoryBranchRequest{ BranchToken: []byte("branch-token"), }, expectError: true, expectedError: "shardID is not set for persistence operation", }, { name: "decode error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode(gomock.Any(), gomock.Any()). Return(errors.New("decode error")).Times(1) }, request: &DeleteHistoryBranchRequest{ BranchToken: []byte("branch-token"), ShardID: common.Ptr(10), }, expectError: true, expectedError: "decode error", }, { name: "persistence error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode([]byte("branch-token"), &workflow.HistoryBranch{}).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") return nil }).Times(1) mockStore.EXPECT(). DeleteHistoryBranch(gomock.Any(), gomock.Any()). Return(errors.New("persistence error")).Times(1) }, request: &DeleteHistoryBranchRequest{ BranchToken: []byte("branch-token"), ShardID: common.Ptr(10), }, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { historyManager, mockStore, _, mockEncoder := setUpMocksForHistoryV2Manager(t) tc.setupMock(mockStore, mockEncoder) // Call the method err := historyManager.DeleteHistoryBranch(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestGetHistoryTree(t *testing.T) { testCases := []struct { name string setupMock func(*MockHistoryStore, *codec.MockBinaryEncoder) request *GetHistoryTreeRequest expectError bool expectedError string expected *GetHistoryTreeResponse }{ { name: "success", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode([]byte("branch-token"), &workflow.HistoryBranch{}).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") return nil }).Times(1) mockStore.EXPECT(). GetHistoryTree(gomock.Any(), &InternalGetHistoryTreeRequest{ TreeID: "tree-id", ShardID: common.Ptr(10), BranchToken: []byte("branch-token"), }). Return(&InternalGetHistoryTreeResponse{ Branches: []*types.HistoryBranch{ { TreeID: "tree-id", BranchID: "branch-id", }, }, }, nil).Times(1) }, request: &GetHistoryTreeRequest{ BranchToken: []byte("branch-token"), ShardID: common.Ptr(10), }, expectError: false, expected: &GetHistoryTreeResponse{ Branches: []*workflow.HistoryBranch{ { TreeID: common.Ptr("tree-id"), BranchID: common.Ptr("branch-id"), }, }, }, }, { name: "decode error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode(gomock.Any(), gomock.Any()). Return(errors.New("decode error")).Times(1) }, request: &GetHistoryTreeRequest{ BranchToken: []byte("branch-token"), }, expectError: true, expectedError: "decode error", }, { name: "persistence error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockStore.EXPECT(). GetHistoryTree(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &GetHistoryTreeRequest{ TreeID: "tree-id", }, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { historyManager, mockStore, _, mockEncoder := setUpMocksForHistoryV2Manager(t) tc.setupMock(mockStore, mockEncoder) // Call the method resp, err := historyManager.GetHistoryTree(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } func TestAppendHistoryNodes(t *testing.T) { testCases := []struct { name string setupMock func(*MockHistoryStore, *codec.MockBinaryEncoder, *MockPayloadSerializer) request *AppendHistoryNodesRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder, mockSerializer *MockPayloadSerializer) { mockEncoder.EXPECT(). Decode([]byte("branch-token"), &workflow.HistoryBranch{}).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") return nil }).Times(1) mockSerializer.EXPECT(). SerializeBatchEvents(gomock.Any(), gomock.Any()). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("events")}, nil).Times(1) mockStore.EXPECT(). AppendHistoryNodes(gomock.Any(), gomock.Any()). Return(nil).Times(1) }, request: &AppendHistoryNodesRequest{ BranchToken: []byte("branch-token"), Events: []*types.HistoryEvent{{ID: 1, Version: 1}}, TransactionID: 1234, ShardID: common.Ptr(10), }, expectError: false, }, { name: "shardID not set error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder, mockSerializer *MockPayloadSerializer) { // No mock interactions for this invalid input test }, request: &AppendHistoryNodesRequest{ BranchToken: []byte("branch-token"), Events: []*types.HistoryEvent{}, // No events }, expectError: true, expectedError: "shardID is not set for persistence operation", }, { name: "empty events error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder, mockSerializer *MockPayloadSerializer) { // No mock interactions for this invalid input test }, request: &AppendHistoryNodesRequest{ BranchToken: []byte("branch-token"), Events: []*types.HistoryEvent{}, // No events ShardID: common.Ptr(10), }, expectError: true, expectedError: "events to be appended cannot be empty", }, { name: "event ID less than 1 error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder, mockSerializer *MockPayloadSerializer) { // No mock interactions for this invalid input test }, request: &AppendHistoryNodesRequest{ BranchToken: []byte("branch-token"), Events: []*types.HistoryEvent{{ID: 0, Version: 1}}, ShardID: common.Ptr(10), }, expectError: true, expectedError: "eventID cannot be less than 1", }, { name: "event version not the same error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder, mockSerializer *MockPayloadSerializer) { // No mock interactions for this invalid input test }, request: &AppendHistoryNodesRequest{ BranchToken: []byte("branch-token"), Events: []*types.HistoryEvent{{ID: 1, Version: 1}, {ID: 2, Version: 2}}, ShardID: common.Ptr(10), }, expectError: true, expectedError: "event version must be the same inside a batch", }, { name: "event ID not continuous", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder, mockSerializer *MockPayloadSerializer) { // No mock interactions for this invalid input test }, request: &AppendHistoryNodesRequest{ BranchToken: []byte("branch-token"), Events: []*types.HistoryEvent{{ID: 1, Version: 1}, {ID: 3, Version: 1}}, ShardID: common.Ptr(10), }, expectError: true, expectedError: "event ID must be continuous", }, { name: "decode error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder, mockSerializer *MockPayloadSerializer) { mockEncoder.EXPECT(). Decode(gomock.Any(), gomock.Any()). Return(errors.New("decode error")).Times(1) }, request: &AppendHistoryNodesRequest{ BranchToken: []byte("branch-token"), Events: []*types.HistoryEvent{{ID: 1, Version: 1}}, TransactionID: 1234, ShardID: common.Ptr(10), }, expectError: true, expectedError: "decode error", }, { name: "serialization error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder, mockSerializer *MockPayloadSerializer) { mockEncoder.EXPECT(). Decode(gomock.Any(), gomock.Any()). Return(nil).Times(1) mockSerializer.EXPECT(). SerializeBatchEvents(gomock.Any(), gomock.Any()). Return(nil, errors.New("serialization error")).Times(1) }, request: &AppendHistoryNodesRequest{ BranchToken: []byte("branch-token"), Events: []*types.HistoryEvent{{ID: 1, Version: 1}}, TransactionID: 1234, ShardID: common.Ptr(10), }, expectError: true, expectedError: "serialization error", }, { name: "transaction size limit error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder, mockSerializer *MockPayloadSerializer) { mockEncoder.EXPECT(). Decode([]byte("branch-token"), &workflow.HistoryBranch{}).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") return nil }).Times(1) mockSerializer.EXPECT(). SerializeBatchEvents(gomock.Any(), gomock.Any()). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: make([]byte, 1024*10+1)}, nil).Times(1) }, request: &AppendHistoryNodesRequest{ BranchToken: []byte("branch-token"), Events: []*types.HistoryEvent{{ID: 1, Version: 1}}, TransactionID: 1234, ShardID: common.Ptr(10), }, expectError: true, expectedError: "transaction size of 10241 bytes exceeds limit of 10240 bytes", }, { name: "persistence error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder, mockSerializer *MockPayloadSerializer) { mockEncoder.EXPECT(). Decode([]byte("branch-token"), &workflow.HistoryBranch{}).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") return nil }).Times(1) mockSerializer.EXPECT(). SerializeBatchEvents(gomock.Any(), gomock.Any()). Return(&DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("events")}, nil).Times(1) mockStore.EXPECT(). AppendHistoryNodes(gomock.Any(), gomock.Any()). Return(errors.New("persistence error")).Times(1) }, request: &AppendHistoryNodesRequest{ BranchToken: []byte("branch-token"), Events: []*types.HistoryEvent{{ID: 1, Version: 1}}, TransactionID: 1234, ShardID: common.Ptr(10), }, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { historyManager, mockStore, mockSerializer, mockEncoder := setUpMocksForHistoryV2Manager(t) tc.setupMock(mockStore, mockEncoder, mockSerializer) // Call the method _, err := historyManager.AppendHistoryNodes(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestGetAllHistoryTreeBranches(t *testing.T) { testCases := []struct { name string setupMock func(*MockHistoryStore) request *GetAllHistoryTreeBranchesRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockStore *MockHistoryStore) { mockStore.EXPECT(). GetAllHistoryTreeBranches(gomock.Any(), gomock.Any()). Return(&GetAllHistoryTreeBranchesResponse{ Branches: []HistoryBranchDetail{ {TreeID: "tree-id-1", BranchID: "branch-id-1"}, }, }, nil).Times(1) }, request: &GetAllHistoryTreeBranchesRequest{ PageSize: 10, NextPageToken: []byte("next-token"), }, expectError: false, }, { name: "persistence error", setupMock: func(mockStore *MockHistoryStore) { mockStore.EXPECT(). GetAllHistoryTreeBranches(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &GetAllHistoryTreeBranchesRequest{ PageSize: 10, NextPageToken: []byte("next-token"), }, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { historyManager, mockStore, _, _ := setUpMocksForHistoryV2Manager(t) tc.setupMock(mockStore) // Call the method resp, err := historyManager.GetAllHistoryTreeBranches(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.NotNil(t, resp) assert.Equal(t, "tree-id-1", resp.Branches[0].TreeID) } }) } } func TestReadRawHistoryBranch(t *testing.T) { testCases := []struct { name string setupMock func(*MockHistoryStore, *codec.MockBinaryEncoder) fakeSerialize func(*historyV2PagingToken) ([]byte, error) fakeDeserialize func([]byte, int64) (*historyV2PagingToken, error) request *ReadHistoryBranchRequest expectError bool expectedError string expectedBlobs []*DataBlob expectedToken *historyV2PagingToken }{ { name: "success case", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode([]byte("branch-token"), &workflow.HistoryBranch{}).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") value.Ancestors = []*workflow.HistoryBranchRange{ { BranchID: common.StringPtr("ancestor-branch-id"), BeginNodeID: common.Int64Ptr(1), EndNodeID: common.Int64Ptr(10), }, { BranchID: common.StringPtr("ancestor-branch-id-2"), BeginNodeID: common.Int64Ptr(10), EndNodeID: common.Int64Ptr(20), }, { BranchID: common.StringPtr("ancestor-branch-id-3"), BeginNodeID: common.Int64Ptr(20), EndNodeID: common.Int64Ptr(30), }, } return nil }).Times(1) mockStore.EXPECT(). ReadHistoryBranch(gomock.Any(), &InternalReadHistoryBranchRequest{ TreeID: "tree-id", BranchID: "ancestor-branch-id-2", MinNodeID: 10, MaxNodeID: 19, NextPageToken: []byte("store-token"), LastNodeID: 9, LastTransactionID: 10, ShardID: 1, PageSize: 10, }). Return(&InternalReadHistoryBranchResponse{ History: []*DataBlob{ {Data: []byte("history-event-data")}, }, NextPageToken: []byte("next-token"), LastNodeID: 999, LastTransactionID: 1000, }, nil).Times(1) }, fakeDeserialize: func(data []byte, lastEventID int64) (*historyV2PagingToken, error) { assert.Equal(t, []byte("token"), data) assert.Equal(t, int64(9), lastEventID) return &historyV2PagingToken{ LastNodeID: lastEventID, LastTransactionID: 10, CurrentRangeIndex: notStartedIndex, StoreToken: []byte("store-token"), }, nil }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), ShardID: common.IntPtr(1), PageSize: 10, MinEventID: 10, MaxEventID: 19, NextPageToken: []byte("token"), }, expectError: false, expectedBlobs: []*DataBlob{ {Data: []byte("history-event-data")}, }, expectedToken: &historyV2PagingToken{ LastNodeID: 999, LastTransactionID: 1000, CurrentRangeIndex: 1, FinalRangeIndex: 1, StoreToken: []byte("next-token"), }, }, { name: "invalid shard ID", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { // No encoder or store interactions, invalid ShardID is set. }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), ShardID: nil, // Invalid shardID PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: true, expectedError: "shardID is not set for persistence operation", }, { name: "invalid pagination params", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { // No encoder or store interactions, invalid pagination params. }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), ShardID: common.IntPtr(1), PageSize: 0, // Invalid PageSize MinEventID: 100, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: true, expectedError: "no events can be found for pageSize 0, minEventID 100, maxEventID: 100", }, { name: "deserialize error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { }, fakeDeserialize: func(data []byte, lastEventID int64) (*historyV2PagingToken, error) { return nil, errors.New("deserialize error") }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), ShardID: common.IntPtr(1), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: true, expectedError: "deserialize error", }, { name: "decode error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode(gomock.Any(), gomock.Any()). Return(errors.New("decode error")).Times(1) }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), ShardID: common.IntPtr(1), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: true, expectedError: "decode error", }, { name: "persistence error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode([]byte("branch-token"), &workflow.HistoryBranch{}).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") return nil }).Times(1) mockStore.EXPECT(). ReadHistoryBranch(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), ShardID: common.IntPtr(1), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: true, expectedError: "persistence error", }, { name: "empty history error", setupMock: func(mockStore *MockHistoryStore, mockEncoder *codec.MockBinaryEncoder) { mockEncoder.EXPECT(). Decode([]byte("branch-token"), &workflow.HistoryBranch{}).DoAndReturn(func(data []byte, value *workflow.HistoryBranch) error { value.TreeID = common.Ptr("tree-id") value.BranchID = common.Ptr("branch-id") return nil }).Times(1) mockStore.EXPECT(). ReadHistoryBranch(gomock.Any(), gomock.Any()). Return(&InternalReadHistoryBranchResponse{ History: []*DataBlob{}, NextPageToken: []byte{}, }, nil).Times(1) }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), ShardID: common.IntPtr(1), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: true, expectedError: "Workflow execution history not found.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { historyManager, mockStore, _, mockEncoder := setUpMocksForHistoryV2Manager(t) if tc.fakeSerialize != nil { historyManager.serializeTokenFn = tc.fakeSerialize } if tc.fakeDeserialize != nil { historyManager.deserializeTokenFn = tc.fakeDeserialize } tc.setupMock(mockStore, mockEncoder) // Call the method blobs, token, _, _, err := historyManager.readRawHistoryBranch(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedBlobs, blobs) assert.Equal(t, tc.expectedToken, token) } }) } } func TestReadHistoryBranch(t *testing.T) { testCases := []struct { name string setupMock func(*MockPayloadSerializer) fakeReadRaw func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) fakeSerializeToken func(pagingToken *historyV2PagingToken) ([]byte, error) byBatch bool request *ReadHistoryBranchRequest expectError bool expectedError string expectedEvents []*types.HistoryEvent expectedBatch []*types.History }{ { name: "success by events", setupMock: func(mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). DeserializeBatchEvents(&DataBlob{Data: []byte("history-event-data")}). Return([]*types.HistoryEvent{ {ID: 1, Version: 1}, {ID: 2, Version: 1}, }, nil).Times(1) mockSerializer.EXPECT(). DeserializeBatchEvents(&DataBlob{Data: []byte("history-event-data2")}). Return([]*types.HistoryEvent{ {ID: 0, Version: 2}, }, nil).Times(1) mockSerializer.EXPECT(). DeserializeBatchEvents(&DataBlob{Data: []byte("history-event-data3")}). Return([]*types.HistoryEvent{ {ID: 1, Version: 2}, }, nil).Times(1) }, fakeReadRaw: func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { return []*DataBlob{ {Data: []byte("history-event-data")}, {Data: []byte("history-event-data2")}, {Data: []byte("history-event-data3")}, }, &historyV2PagingToken{LastEventVersion: 2, LastEventID: 0}, 100, log.NewNoop(), nil }, fakeSerializeToken: func(pagingToken *historyV2PagingToken) ([]byte, error) { assert.Equal(t, &historyV2PagingToken{ LastEventID: 1, LastEventVersion: 2, }, pagingToken) return []byte("next-page-token"), nil }, byBatch: false, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100, }, expectError: false, expectedEvents: []*types.HistoryEvent{ {ID: 1, Version: 2}, }, }, { name: "success by batch", setupMock: func(mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). DeserializeBatchEvents(&DataBlob{Data: []byte("history-event-data")}). Return([]*types.HistoryEvent{ {ID: 1, Version: 1}, {ID: 2, Version: 1}, }, nil).Times(1) mockSerializer.EXPECT(). DeserializeBatchEvents(&DataBlob{Data: []byte("history-event-data2")}). Return([]*types.HistoryEvent{ {ID: 0, Version: 2}, }, nil).Times(1) mockSerializer.EXPECT(). DeserializeBatchEvents(&DataBlob{Data: []byte("history-event-data3")}). Return([]*types.HistoryEvent{ {ID: 1, Version: 2}, }, nil).Times(1) }, fakeReadRaw: func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { return []*DataBlob{ {Data: []byte("history-event-data")}, {Data: []byte("history-event-data2")}, {Data: []byte("history-event-data3")}, }, &historyV2PagingToken{LastEventVersion: 2, LastEventID: 0}, 100, log.NewNoop(), nil }, fakeSerializeToken: func(pagingToken *historyV2PagingToken) ([]byte, error) { assert.Equal(t, &historyV2PagingToken{ LastEventID: 1, LastEventVersion: 2, }, pagingToken) return []byte("next-page-token"), nil }, byBatch: true, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100, }, expectError: false, expectedBatch: []*types.History{ {Events: []*types.HistoryEvent{{ID: 1, Version: 2}}}, }, }, { name: "empty batch error", setupMock: func(mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). DeserializeBatchEvents(gomock.Any()). Return([]*types.HistoryEvent{}, nil).Times(1) }, fakeReadRaw: func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { return []*DataBlob{ {Data: []byte("history-event-data")}, }, &historyV2PagingToken{LastEventVersion: 1, LastEventID: 0}, 100, log.NewNoop(), nil }, fakeSerializeToken: func(pagingToken *historyV2PagingToken) ([]byte, error) { return nil, nil // Doesn't matter in this case since it errors earlier }, byBatch: false, request: &ReadHistoryBranchRequest{BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100}, expectError: true, expectedError: "corrupted history event batch, empty events", }, { name: "corrupted event IDs", setupMock: func(mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). DeserializeBatchEvents(gomock.Any()). Return([]*types.HistoryEvent{ {ID: 1, Version: 1}, {ID: 3, Version: 1}, // Non-continuous ID }, nil).Times(1) }, fakeReadRaw: func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { return []*DataBlob{ {Data: []byte("history-event-data")}, }, &historyV2PagingToken{LastEventVersion: 1, LastEventID: 0}, 100, log.NewNoop(), nil }, fakeSerializeToken: func(pagingToken *historyV2PagingToken) ([]byte, error) { return nil, nil // Doesn't matter in this case since it errors earlier }, byBatch: false, request: &ReadHistoryBranchRequest{BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100}, expectError: true, expectedError: "corrupted history event batch, wrong version and IDs", }, { name: "corrupted event batch", setupMock: func(mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). DeserializeBatchEvents(gomock.Any()). Return([]*types.HistoryEvent{ {ID: 7, Version: 5}, {ID: 8, Version: 5}, }, nil).Times(1) }, fakeReadRaw: func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { return []*DataBlob{ {Data: []byte("history-event-data")}, }, &historyV2PagingToken{LastEventVersion: 2, LastEventID: 5}, 100, log.NewNoop(), nil // Higher version than event batch }, fakeSerializeToken: func(pagingToken *historyV2PagingToken) ([]byte, error) { return []byte("next-page-token"), nil }, byBatch: false, request: &ReadHistoryBranchRequest{BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100}, expectError: true, // No error, but events are skipped expectedError: "corrupted history event batch, eventID is not continuous", }, { name: "read raw error", setupMock: func(mockSerializer *MockPayloadSerializer) { // No DeserializeBatchEvents calls because readRawHistoryBranchFn fails before deserialization }, fakeReadRaw: func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { return nil, nil, 0, nil, errors.New("read raw error") }, fakeSerializeToken: func(pagingToken *historyV2PagingToken) ([]byte, error) { return nil, nil // Doesn't matter in this case since it errors earlier }, byBatch: false, request: &ReadHistoryBranchRequest{BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100}, expectError: true, expectedError: "read raw error", }, { name: "serialize token error", setupMock: func(mockSerializer *MockPayloadSerializer) { mockSerializer.EXPECT(). DeserializeBatchEvents(gomock.Any()). Return([]*types.HistoryEvent{ {ID: 1, Version: 1}, {ID: 2, Version: 1}, }, nil).Times(1) }, fakeReadRaw: func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { return []*DataBlob{ {Data: []byte("history-event-data")}, }, &historyV2PagingToken{LastEventVersion: 1, LastEventID: 0}, 100, log.NewNoop(), nil }, fakeSerializeToken: func(pagingToken *historyV2PagingToken) ([]byte, error) { return nil, errors.New("serialize token error") }, byBatch: false, request: &ReadHistoryBranchRequest{BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100}, expectError: true, expectedError: "serialize token error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Set up mocks historyManager, _, mockSerializer, _ := setUpMocksForHistoryV2Manager(t) // Fake readRawHistoryBranchFn and serializeTokenFn historyManager.readRawHistoryBranchFn = tc.fakeReadRaw historyManager.serializeTokenFn = tc.fakeSerializeToken tc.setupMock(mockSerializer) // Call the method historyEvents, historyBatches, _, _, _, err := historyManager.readHistoryBranch(context.Background(), tc.byBatch, tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) if tc.byBatch { assert.Equal(t, tc.expectedBatch, historyBatches) } else { assert.Equal(t, tc.expectedEvents, historyEvents) } } }) } } func TestReadRawHistoryBranchMethod(t *testing.T) { testCases := []struct { name string setupMock func() fakeReadRaw func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) fakeSerializeToken func(pagingToken *historyV2PagingToken) ([]byte, error) request *ReadHistoryBranchRequest expectError bool expectedError string expectedResponse *ReadRawHistoryBranchResponse }{ { name: "success", setupMock: func() { // No additional mock setup needed }, fakeReadRaw: func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { return []*DataBlob{ {Data: []byte("history-event-blob")}, }, &historyV2PagingToken{LastEventVersion: 1, LastEventID: 0}, 100, nil, nil }, fakeSerializeToken: func(pagingToken *historyV2PagingToken) ([]byte, error) { return []byte("next-page-token"), nil }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: false, expectedResponse: &ReadRawHistoryBranchResponse{ HistoryEventBlobs: []*DataBlob{ {Data: []byte("history-event-blob")}, }, NextPageToken: []byte("next-page-token"), Size: 100, }, }, { name: "error in readRawHistoryBranchFn", setupMock: func() { // No additional mock setup needed }, fakeReadRaw: func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { return nil, nil, 0, nil, errors.New("read error") }, fakeSerializeToken: func(pagingToken *historyV2PagingToken) ([]byte, error) { return nil, nil // Won't be called due to error in readRawHistoryBranchFn }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: true, expectedError: "read error", }, { name: "error in serializeTokenFn", setupMock: func() { // No additional mock setup needed }, fakeReadRaw: func(ctx context.Context, request *ReadHistoryBranchRequest) ([]*DataBlob, *historyV2PagingToken, int, log.Logger, error) { return []*DataBlob{ {Data: []byte("history-event-blob")}, }, &historyV2PagingToken{LastEventVersion: 1, LastEventID: 0}, 100, nil, nil }, fakeSerializeToken: func(pagingToken *historyV2PagingToken) ([]byte, error) { return nil, errors.New("serialization error") }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: true, expectedError: "serialization error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Set up the mock dependencies and fakes historyManager, _, _, _ := setUpMocksForHistoryV2Manager(t) historyManager.readRawHistoryBranchFn = tc.fakeReadRaw historyManager.serializeTokenFn = tc.fakeSerializeToken // Call the method resp, err := historyManager.ReadRawHistoryBranch(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedResponse, resp) } }) } } func TestReadHistoryBranchByBatch(t *testing.T) { testCases := []struct { name string fakeReadHistory func(ctx context.Context, byBatch bool, request *ReadHistoryBranchRequest) ([]*types.HistoryEvent, []*types.History, []byte, int, int64, error) request *ReadHistoryBranchRequest expectError bool expectedError string expectedResponse *ReadHistoryBranchByBatchResponse }{ { name: "success", fakeReadHistory: func(ctx context.Context, byBatch bool, request *ReadHistoryBranchRequest) ([]*types.HistoryEvent, []*types.History, []byte, int, int64, error) { return nil, []*types.History{ {Events: []*types.HistoryEvent{ {ID: 1, Version: 1}, {ID: 2, Version: 1}, }}, }, []byte("next-page-token"), 100, 1, nil }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: false, expectedResponse: &ReadHistoryBranchByBatchResponse{ History: []*types.History{ {Events: []*types.HistoryEvent{ {ID: 1, Version: 1}, {ID: 2, Version: 1}, }}, }, NextPageToken: []byte("next-page-token"), Size: 100, LastFirstEventID: 1, }, }, { name: "error in readHistoryBranchFn", fakeReadHistory: func(ctx context.Context, byBatch bool, request *ReadHistoryBranchRequest) ([]*types.HistoryEvent, []*types.History, []byte, int, int64, error) { return nil, nil, nil, 0, 0, errors.New("read error") }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: true, expectedError: "read error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Set up faked readHistoryBranchFn historyManager, _, _, _ := setUpMocksForHistoryV2Manager(t) historyManager.readHistoryBranchFn = tc.fakeReadHistory // Call the method resp, err := historyManager.ReadHistoryBranchByBatch(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedResponse, resp) } }) } } func TestReadHistoryBranchMethod(t *testing.T) { testCases := []struct { name string fakeReadHistory func(ctx context.Context, byBatch bool, request *ReadHistoryBranchRequest) ([]*types.HistoryEvent, []*types.History, []byte, int, int64, error) request *ReadHistoryBranchRequest expectError bool expectedError string expectedResponse *ReadHistoryBranchResponse }{ { name: "success", fakeReadHistory: func(ctx context.Context, byBatch bool, request *ReadHistoryBranchRequest) ([]*types.HistoryEvent, []*types.History, []byte, int, int64, error) { return []*types.HistoryEvent{ {ID: 1, Version: 1}, {ID: 2, Version: 1}, }, nil, []byte("next-page-token"), 100, 1, nil }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: false, expectedResponse: &ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ {ID: 1, Version: 1}, {ID: 2, Version: 1}, }, NextPageToken: []byte("next-page-token"), Size: 100, LastFirstEventID: 1, }, }, { name: "error in readHistoryBranchFn", fakeReadHistory: func(ctx context.Context, byBatch bool, request *ReadHistoryBranchRequest) ([]*types.HistoryEvent, []*types.History, []byte, int, int64, error) { return nil, nil, nil, 0, 0, errors.New("read error") }, request: &ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), PageSize: 10, MinEventID: 1, MaxEventID: 100, NextPageToken: []byte{}, }, expectError: true, expectedError: "read error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Set up faked readHistoryBranchFn historyManager, _, _, _ := setUpMocksForHistoryV2Manager(t) historyManager.readHistoryBranchFn = tc.fakeReadHistory // Call the method resp, err := historyManager.ReadHistoryBranch(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedResponse, resp) } }) } } func TestDeserializeToken(t *testing.T) { testCases := []struct { name string inputData []byte defaultLastEventID int64 expectedToken *historyV2PagingToken expectError bool expectedError string }{ { name: "empty data", inputData: []byte{}, defaultLastEventID: 100, expectedToken: &historyV2PagingToken{ LastEventID: 100, LastEventVersion: constants.EmptyVersion, CurrentRangeIndex: notStartedIndex, LastNodeID: defaultLastNodeID, LastTransactionID: defaultLastTransactionID, }, expectError: false, }, { name: "valid token data", inputData: []byte(`{"LastEventID":200,"LastEventVersion":1,"CurrentRangeIndex":2,"FinalRangeIndex":3,"LastNodeID":100,"LastTransactionID":500}`), defaultLastEventID: 100, expectedToken: &historyV2PagingToken{ LastEventID: 200, LastEventVersion: 1, CurrentRangeIndex: 2, FinalRangeIndex: 3, LastNodeID: 100, LastTransactionID: 500, }, expectError: false, }, { name: "invalid json data", inputData: []byte("invalid-json"), defaultLastEventID: 100, expectedToken: nil, expectError: true, expectedError: "invalid character 'i' looking for beginning of value", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { token, err := deserializeToken(tc.inputData, tc.defaultLastEventID) if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedToken, token) } }) } } func TestSerializeToken(t *testing.T) { testCases := []struct { name string inputToken *historyV2PagingToken expectedOutput []byte expectError bool expectedError string }{ { name: "empty store token, final page reached", inputToken: &historyV2PagingToken{ StoreToken: []byte{}, CurrentRangeIndex: 2, FinalRangeIndex: 2, }, expectedOutput: nil, expectError: false, }, { name: "empty store token, increment current range index", inputToken: &historyV2PagingToken{ StoreToken: []byte{}, CurrentRangeIndex: 1, FinalRangeIndex: 2, }, expectedOutput: []byte(`{"LastEventVersion":0,"LastEventID":0,"StoreToken":"","CurrentRangeIndex":2,"FinalRangeIndex":2,"LastNodeID":0,"LastTransactionID":0}`), expectError: false, }, { name: "non-empty store token", inputToken: &historyV2PagingToken{ StoreToken: []byte("non-empty-token"), CurrentRangeIndex: 1, FinalRangeIndex: 2, }, expectedOutput: []byte(`{"LastEventVersion":0,"LastEventID":0,"StoreToken":"bm9uLWVtcHR5LXRva2Vu","CurrentRangeIndex":1,"FinalRangeIndex":2,"LastNodeID":0,"LastTransactionID":0}`), expectError: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { output, err := serializeToken(tc.inputToken) if tc.expectError { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedOutput, output) t.Log(string(output)) } }) } } func TestTokenRoundtrip(t *testing.T) { testCases := []struct { name string inputToken *historyV2PagingToken }{ { name: "empty store token, basic token", inputToken: &historyV2PagingToken{ LastEventID: 100, LastEventVersion: 1, CurrentRangeIndex: 1, FinalRangeIndex: 2, LastNodeID: 200, LastTransactionID: 300, StoreToken: []byte{}, }, }, { name: "non-empty store token", inputToken: &historyV2PagingToken{ LastEventID: 100, LastEventVersion: 1, CurrentRangeIndex: 1, FinalRangeIndex: 2, LastNodeID: 200, LastTransactionID: 300, StoreToken: []byte("non-empty-token"), }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Step 1: Serialize the token serializedToken, err := serializeToken(tc.inputToken) assert.NoError(t, err) // Step 2: Deserialize the token back deserializedToken, err := deserializeToken(serializedToken, tc.inputToken.LastEventID) assert.NoError(t, err) // Step 3: Compare the original token with the deserialized token assert.Equal(t, tc.inputToken, deserializedToken) }) } } ================================================ FILE: common/persistence/mappers.go ================================================ package persistence import "github.com/uber/cadence/common/types" // ToType converts persistence.GetDomainResponse to types.DescribeDomainResponse. Take care when using this mapper // as not all the fields between the two types are symmetrical // todo: Replace the ad-hoc mapping in the DescribeDomain Handler with this one func (r *GetDomainResponse) ToType() *types.DescribeDomainResponse { if r == nil { return nil } var domainInfo *types.DomainInfo if r.Info != nil { // Validate and clamp Status to valid range (0-2: Registered, Deprecated, Deleted) // This prevents issues with invalid or out-of-range Status values status := r.Info.Status if status < 0 || status > 2 { status = 0 // Default to Registered for invalid values } domainStatus := types.DomainStatus(status) domainInfo = &types.DomainInfo{ Name: r.Info.Name, Status: &domainStatus, Description: r.Info.Description, OwnerEmail: r.Info.OwnerEmail, Data: r.Info.Data, UUID: r.Info.ID, } } var domainConfig *types.DomainConfiguration if r.Config != nil { domainConfig = &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: r.Config.Retention, EmitMetric: r.Config.EmitMetric, HistoryArchivalStatus: r.Config.HistoryArchivalStatus.Ptr(), HistoryArchivalURI: r.Config.HistoryArchivalURI, VisibilityArchivalStatus: r.Config.VisibilityArchivalStatus.Ptr(), VisibilityArchivalURI: r.Config.VisibilityArchivalURI, } // Always set these pointers to maintain symmetry with FromDescribeDomainResponse // which always copies them when present domainConfig.BadBinaries = &r.Config.BadBinaries domainConfig.IsolationGroups = &r.Config.IsolationGroups domainConfig.AsyncWorkflowConfig = &r.Config.AsyncWorkflowConfig } var replicationConfig *types.DomainReplicationConfiguration if r.ReplicationConfig != nil { var clusters []*types.ClusterReplicationConfiguration // Convert clusters, preserving nil entries for consistent roundtrips if r.ReplicationConfig.Clusters != nil { clusters = make([]*types.ClusterReplicationConfiguration, len(r.ReplicationConfig.Clusters)) for i, cluster := range r.ReplicationConfig.Clusters { if cluster != nil { clusters[i] = &types.ClusterReplicationConfiguration{ ClusterName: cluster.ClusterName, } } } } replicationConfig = &types.DomainReplicationConfiguration{ ActiveClusterName: r.ReplicationConfig.ActiveClusterName, Clusters: clusters, ActiveClusters: r.ReplicationConfig.ActiveClusters, } } // Always create FailoverInfo for consistent mapping failoverExpireTimestamp := int64(0) if r.FailoverEndTime != nil { failoverExpireTimestamp = *r.FailoverEndTime } return &types.DescribeDomainResponse{ DomainInfo: domainInfo, Configuration: domainConfig, ReplicationConfiguration: replicationConfig, FailoverVersion: r.FailoverVersion, IsGlobalDomain: r.IsGlobalDomain, FailoverInfo: &types.FailoverInfo{ FailoverVersion: r.FailoverVersion, FailoverStartTimestamp: r.LastUpdatedTime, FailoverExpireTimestamp: failoverExpireTimestamp, // Fields below are not present at the persistence layer CompletedShardCount: 0, PendingShards: nil, }, // note that failoverNotificationVersion is not present because the structs aren't symmetric } } // FromDescribeDomainResponse converts types.DescribeDomainResponse to persistence.GetDomainResponse // care should be taken when using this because not all the fields between the two types are symmetrical func FromDescribeDomainResponse(resp *types.DescribeDomainResponse) *GetDomainResponse { if resp == nil { return nil } var domainInfo *DomainInfo if resp.DomainInfo != nil { var status int if resp.DomainInfo.Status != nil { status = int(*resp.DomainInfo.Status) } domainInfo = &DomainInfo{ ID: resp.DomainInfo.UUID, Name: resp.DomainInfo.Name, Status: status, Description: resp.DomainInfo.Description, OwnerEmail: resp.DomainInfo.OwnerEmail, Data: resp.DomainInfo.Data, } } var domainConfig *DomainConfig if resp.Configuration != nil { config := &DomainConfig{ Retention: resp.Configuration.WorkflowExecutionRetentionPeriodInDays, EmitMetric: resp.Configuration.EmitMetric, } if resp.Configuration.HistoryArchivalStatus != nil { config.HistoryArchivalStatus = *resp.Configuration.HistoryArchivalStatus } config.HistoryArchivalURI = resp.Configuration.HistoryArchivalURI if resp.Configuration.VisibilityArchivalStatus != nil { config.VisibilityArchivalStatus = *resp.Configuration.VisibilityArchivalStatus } config.VisibilityArchivalURI = resp.Configuration.VisibilityArchivalURI if resp.Configuration.BadBinaries != nil { config.BadBinaries = *resp.Configuration.BadBinaries } if resp.Configuration.IsolationGroups != nil { config.IsolationGroups = *resp.Configuration.IsolationGroups } if resp.Configuration.AsyncWorkflowConfig != nil { config.AsyncWorkflowConfig = *resp.Configuration.AsyncWorkflowConfig } domainConfig = config } var replicationConfig *DomainReplicationConfig if resp.ReplicationConfiguration != nil { var clusters []*ClusterReplicationConfig // Convert clusters, preserving nil entries for consistent roundtrips if resp.ReplicationConfiguration.Clusters != nil { clusters = make([]*ClusterReplicationConfig, len(resp.ReplicationConfiguration.Clusters)) for i, cluster := range resp.ReplicationConfiguration.Clusters { if cluster != nil { clusters[i] = &ClusterReplicationConfig{ ClusterName: cluster.ClusterName, } } } } replicationConfig = &DomainReplicationConfig{ ActiveClusterName: resp.ReplicationConfiguration.ActiveClusterName, Clusters: clusters, ActiveClusters: resp.ReplicationConfiguration.ActiveClusters, } } result := &GetDomainResponse{ Info: domainInfo, Config: domainConfig, ReplicationConfig: replicationConfig, IsGlobalDomain: resp.IsGlobalDomain, FailoverVersion: resp.FailoverVersion, // FailoverNotificationVersion not mapped - not present in types.DescribeDomainResponse // PreviousFailoverVersion not mapped - not present in types.DescribeDomainResponse // FailoverEndTime not mapped - not reliably preserved in roundtrips // ConfigVersion not mapped - not present in types.DescribeDomainResponse // NotificationVersion not mapped - not present in types.DescribeDomainResponse } if resp.FailoverInfo != nil { result.LastUpdatedTime = resp.FailoverInfo.FailoverStartTimestamp } return result } ================================================ FILE: common/persistence/mappers_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/testing/testdatagen" "github.com/uber/cadence/common/types" ) func TestGetDomainResponse_ToType(t *testing.T) { tests := map[string]struct { input *GetDomainResponse expected *types.DescribeDomainResponse }{ "nil response": { input: nil, expected: nil, }, "empty response": { input: &GetDomainResponse{}, expected: &types.DescribeDomainResponse{ FailoverInfo: &types.FailoverInfo{ FailoverVersion: 0, FailoverStartTimestamp: 0, FailoverExpireTimestamp: 0, CompletedShardCount: 0, PendingShards: nil, }, }, }, "full response with all fields": { input: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-domain-id", Name: "test-domain", Status: 0, // 0 = DomainStatusRegistered Description: "test description", OwnerEmail: "test@example.com", Data: map[string]string{"key": "value"}, }, Config: &DomainConfig{ Retention: 7, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "test://history", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "test://visibility", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{"bad": {Reason: "test"}}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "active-cluster", Clusters: []*ClusterReplicationConfig{ {ClusterName: "cluster-1"}, {ClusterName: "cluster-2"}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster-1", FailoverVersion: 100}, }, }, }, }, }, IsGlobalDomain: true, FailoverVersion: 100, }, expected: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ UUID: "test-domain-id", Name: "test-domain", Status: types.DomainStatusRegistered.Ptr(), Description: "test description", OwnerEmail: "test@example.com", Data: map[string]string{"key": "value"}, }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 7, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled.Ptr(), HistoryArchivalURI: "test://history", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "test://visibility", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{"bad": {Reason: "test"}}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "active-cluster", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "cluster-1"}, {ClusterName: "cluster-2"}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster-1", FailoverVersion: 100}, }, }, }, }, }, IsGlobalDomain: true, FailoverVersion: 100, FailoverInfo: &types.FailoverInfo{ FailoverVersion: 100, FailoverStartTimestamp: 0, FailoverExpireTimestamp: 0, CompletedShardCount: 0, PendingShards: nil, }, }, }, "with failover info": { input: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-id", Name: "test", Status: 0, // 0 = DomainStatusRegistered }, FailoverVersion: 200, LastUpdatedTime: 1000, FailoverEndTime: func() *int64 { t := int64(2000); return &t }(), }, expected: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ UUID: "test-id", Name: "test", Status: types.DomainStatusRegistered.Ptr(), }, FailoverVersion: 200, FailoverInfo: &types.FailoverInfo{ FailoverVersion: 200, FailoverStartTimestamp: 1000, FailoverExpireTimestamp: 2000, }, }, }, "without failover end time": { input: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-id", Name: "test", Status: 0, // 0 = DomainStatusRegistered }, FailoverVersion: 200, LastUpdatedTime: 1000, FailoverEndTime: nil, }, expected: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ UUID: "test-id", Name: "test", Status: types.DomainStatusRegistered.Ptr(), }, FailoverVersion: 200, FailoverInfo: &types.FailoverInfo{ FailoverVersion: 200, FailoverStartTimestamp: 1000, FailoverExpireTimestamp: 0, CompletedShardCount: 0, PendingShards: nil, }, }, }, "nil Info": { input: &GetDomainResponse{ Info: nil, FailoverVersion: 100, }, expected: &types.DescribeDomainResponse{ DomainInfo: nil, FailoverVersion: 100, FailoverInfo: &types.FailoverInfo{ FailoverVersion: 100, FailoverStartTimestamp: 0, FailoverExpireTimestamp: 0, CompletedShardCount: 0, PendingShards: nil, }, }, }, "nil clusters in ReplicationConfig": { input: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-id", Name: "test", Status: 0, }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-1", Clusters: []*ClusterReplicationConfig{ {ClusterName: "cluster-1"}, nil, // Preserved in output {ClusterName: "cluster-2"}, nil, // Preserved in output }, }, }, expected: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ UUID: "test-id", Name: "test", Status: types.DomainStatusRegistered.Ptr(), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "cluster-1", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "cluster-1"}, nil, {ClusterName: "cluster-2"}, nil, }, }, FailoverInfo: &types.FailoverInfo{ FailoverVersion: 0, FailoverStartTimestamp: 0, FailoverExpireTimestamp: 0, CompletedShardCount: 0, PendingShards: nil, }, }, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { result := tc.input.ToType() assert.Equal(t, tc.expected, result) }) } } func TestFromDescribeDomainResponse(t *testing.T) { tests := map[string]struct { input *types.DescribeDomainResponse expected *GetDomainResponse }{ "nil response": { input: nil, expected: nil, }, "empty response": { input: &types.DescribeDomainResponse{}, expected: &GetDomainResponse{}, }, "full response with all fields": { input: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ UUID: "test-domain-id", Name: "test-domain", Status: types.DomainStatusRegistered.Ptr(), Description: "test description", OwnerEmail: "test@example.com", Data: map[string]string{"key": "value"}, }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 7, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled.Ptr(), HistoryArchivalURI: "test://history", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "test://visibility", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{"bad": {Reason: "test"}}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "active-cluster", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "cluster-1"}, {ClusterName: "cluster-2"}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster-1", FailoverVersion: 100}, }, }, }, }, }, IsGlobalDomain: true, FailoverVersion: 100, }, expected: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-domain-id", Name: "test-domain", Status: 0, // 0 = DomainStatusRegistered Description: "test description", OwnerEmail: "test@example.com", Data: map[string]string{"key": "value"}, }, Config: &DomainConfig{ Retention: 7, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "test://history", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "test://visibility", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{"bad": {Reason: "test"}}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "active-cluster", Clusters: []*ClusterReplicationConfig{ {ClusterName: "cluster-1"}, {ClusterName: "cluster-2"}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster-1", FailoverVersion: 100}, }, }, }, }, }, IsGlobalDomain: true, FailoverVersion: 100, }, }, "with failover info": { input: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ UUID: "test-id", Name: "test", Status: types.DomainStatusRegistered.Ptr(), }, FailoverVersion: 200, FailoverInfo: &types.FailoverInfo{ FailoverVersion: 200, FailoverStartTimestamp: 1000, FailoverExpireTimestamp: 2000, }, }, expected: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-id", Name: "test", Status: 0, // 0 = DomainStatusRegistered }, FailoverVersion: 200, LastUpdatedTime: 1000, // FailoverEndTime not mapped from FailoverInfo }, }, "nil DomainInfo": { input: &types.DescribeDomainResponse{ DomainInfo: nil, FailoverVersion: 100, }, expected: &GetDomainResponse{ Info: nil, FailoverVersion: 100, }, }, "nil Status": { input: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ UUID: "test-id", Name: "test", Status: nil, // Should default to 0 }, }, expected: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-id", Name: "test", Status: 0, // Defaults to 0 }, }, }, "nil clusters in ReplicationConfiguration": { input: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ UUID: "test-id", Name: "test", Status: types.DomainStatusRegistered.Ptr(), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "cluster-1", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "cluster-1"}, nil, // Preserved in output {ClusterName: "cluster-2"}, nil, // Preserved in output }, }, }, expected: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-id", Name: "test", Status: 0, }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster-1", Clusters: []*ClusterReplicationConfig{ {ClusterName: "cluster-1"}, nil, {ClusterName: "cluster-2"}, nil, }, }, }, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { result := FromDescribeDomainResponse(tc.input) assert.Equal(t, tc.expected, result) }) } } func TestRoundtrip_ToType_FromDescribeDomainResponse(t *testing.T) { tests := map[string]struct { input *GetDomainResponse }{ "empty response": { input: &GetDomainResponse{}, }, "full response": { input: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-domain-id", Name: "test-domain", Status: 0, // 0 = DomainStatusRegistered Description: "test description", OwnerEmail: "test@example.com", Data: map[string]string{"key": "value"}, }, Config: &DomainConfig{ Retention: 7, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "test://history", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "test://visibility", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{"bad": {Reason: "test"}}}, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "active-cluster", Clusters: []*ClusterReplicationConfig{ {ClusterName: "cluster-1"}, {ClusterName: "cluster-2"}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster-1", FailoverVersion: 100}, }, }, }, }, }, IsGlobalDomain: true, FailoverVersion: 100, }, }, "with failover": { input: &GetDomainResponse{ Info: &DomainInfo{ ID: "test-id", Name: "test", Status: 0, // 0 = DomainStatusRegistered }, FailoverVersion: 200, LastUpdatedTime: 1000, FailoverEndTime: func() *int64 { t := int64(2000); return &t }(), }, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { // Convert GetDomainResponse -> DescribeDomainResponse -> GetDomainResponse typesResp := tc.input.ToType() roundtripped := FromDescribeDomainResponse(typesResp) // The roundtrip should preserve all the data that's in both representations // Note: Some fields may not survive the roundtrip if they only exist in one direction assert.Equal(t, tc.input.Info, roundtripped.Info) assert.Equal(t, tc.input.Config, roundtripped.Config) assert.Equal(t, tc.input.ReplicationConfig, roundtripped.ReplicationConfig) assert.Equal(t, tc.input.IsGlobalDomain, roundtripped.IsGlobalDomain) assert.Equal(t, tc.input.FailoverVersion, roundtripped.FailoverVersion) assert.Equal(t, tc.input.LastUpdatedTime, roundtripped.LastUpdatedTime) // FailoverEndTime is not preserved in roundtrip }) } } func TestRoundtrip_FromDescribeDomainResponse_ToType(t *testing.T) { tests := map[string]struct { input *types.DescribeDomainResponse }{ "empty response": { input: &types.DescribeDomainResponse{}, }, "full response": { input: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ UUID: "test-domain-id", Name: "test-domain", Status: types.DomainStatusRegistered.Ptr(), Description: "test description", OwnerEmail: "test@example.com", Data: map[string]string{"key": "value"}, }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 7, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled.Ptr(), HistoryArchivalURI: "test://history", VisibilityArchivalStatus: types.ArchivalStatusDisabled.Ptr(), VisibilityArchivalURI: "test://visibility", BadBinaries: &types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{"bad": {Reason: "test"}}}, IsolationGroups: &types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{Enabled: true}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "active-cluster", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "cluster-1"}, {ClusterName: "cluster-2"}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: "cluster-1", FailoverVersion: 100}, }, }, }, }, }, IsGlobalDomain: true, FailoverVersion: 100, }, }, "with failover info": { input: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ UUID: "test-id", Name: "test", Status: types.DomainStatusRegistered.Ptr(), }, FailoverVersion: 200, FailoverInfo: &types.FailoverInfo{ FailoverVersion: 200, FailoverStartTimestamp: 1000, FailoverExpireTimestamp: 2000, }, }, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { // Convert DescribeDomainResponse -> GetDomainResponse -> DescribeDomainResponse persistenceResp := FromDescribeDomainResponse(tc.input) roundtripped := persistenceResp.ToType() // The roundtrip should preserve all the data that's in both representations assert.Equal(t, tc.input.DomainInfo, roundtripped.DomainInfo) assert.Equal(t, tc.input.Configuration, roundtripped.Configuration) assert.Equal(t, tc.input.ReplicationConfiguration, roundtripped.ReplicationConfiguration) assert.Equal(t, tc.input.IsGlobalDomain, roundtripped.IsGlobalDomain) assert.Equal(t, tc.input.FailoverVersion, roundtripped.FailoverVersion) // FailoverInfo is always created in ToType(), so check fields separately assert.NotNil(t, roundtripped.FailoverInfo) if tc.input.FailoverInfo != nil { assert.Equal(t, tc.input.FailoverInfo.FailoverStartTimestamp, roundtripped.FailoverInfo.FailoverStartTimestamp) // Other FailoverInfo fields (CompletedShardCount, PendingShards) are not present at persistence layer } }) } } func TestRoundtrip_FuzzGenerated(t *testing.T) { fuzzer := testdatagen.New(t) // Run multiple iterations to test with different randomly generated data for i := 0; i < 100; i++ { t.Run(fmt.Sprintf("iteration_%d", i), func(t *testing.T) { // Test GetDomainResponse -> DescribeDomainResponse -> GetDomainResponse original := &GetDomainResponse{} fuzzer.Fuzz(original) typesResp := original.ToType() require.NotNil(t, typesResp) roundtripped := FromDescribeDomainResponse(typesResp) require.NotNil(t, roundtripped) // Build expected result accounting for non-symmetric fields expected := &GetDomainResponse{ Info: original.Info, Config: original.Config, ReplicationConfig: original.ReplicationConfig, IsGlobalDomain: original.IsGlobalDomain, FailoverVersion: original.FailoverVersion, LastUpdatedTime: original.LastUpdatedTime, // Fields below are not present in types.DescribeDomainResponse and don't roundtrip: FailoverNotificationVersion: 0, // Not mapped PreviousFailoverVersion: 0, // Not mapped FailoverEndTime: nil, // Not mapped ConfigVersion: 0, // Not mapped NotificationVersion: 0, // Not mapped } // Apply transformations that the mapper does if expected.Info != nil { // Clamp Status to valid range (0-2) as ToType does if expected.Info.Status < 0 || expected.Info.Status > 2 { expected.Info.Status = 0 } } assert.Equal(t, expected, roundtripped) }) } } ================================================ FILE: common/persistence/metered.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "unsafe" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) // This file defines method for persistence requests/responses that affects metered persistence wrapper. // For responses that require metrics for empty response Len() int should be defined. func (r *GetHistoryTasksResponse) Len() int { return len(r.Tasks) } func (r *GetTasksResponse) Len() int { return len(r.Tasks) } func (r *ListDomainsResponse) Len() int { return len(r.Domains) } func (r *ReadHistoryBranchResponse) Len() int { return len(r.HistoryEvents) } func (r *ListCurrentExecutionsResponse) Len() int { return len(r.Executions) } func (r QueueMessageList) Len() int { return len(r) } func (r GetAllHistoryTreeBranchesResponse) Len() int { return len(r.Branches) } // For responses that require metrics for payload size ByteSize() uint64 should be defined. func (r *GetHistoryTasksResponse) ByteSize() uint64 { if r == nil { return 0 } size := uint64(int(unsafe.Sizeof(*r)) + len(r.NextPageToken)) for _, v := range r.Tasks { if v != nil { size += v.ByteSize() } } return size } func (r *GetTasksResponse) ByteSize() uint64 { if r == nil { return 0 } size := uint64(unsafe.Sizeof(*r)) for _, v := range r.Tasks { size += v.ByteSize() } return size } func (r *TaskInfo) ByteSize() uint64 { if r == nil { return 0 } return uint64(int(unsafe.Sizeof(*r)) + len(r.DomainID) + len(r.WorkflowID) + len(r.RunID) + estimateStringMapSize(r.PartitionConfig)) } func (r *ListDomainsResponse) ByteSize() uint64 { if r == nil { return 0 } size := uint64(int(unsafe.Sizeof(*r)) + len(r.NextPageToken)) for _, v := range r.Domains { size += v.ByteSize() } return size } func (r *GetDomainResponse) ByteSize() uint64 { if r == nil { return 0 } return uint64(unsafe.Sizeof(*r)) + r.Info.ByteSize() + r.Config.ByteSize() + r.ReplicationConfig.ByteSize() } func (i *DomainInfo) ByteSize() uint64 { if i == nil { return 0 } return uint64(int(unsafe.Sizeof(*i)) + len(i.ID) + len(i.Name) + len(i.Description) + len(i.OwnerEmail) + estimateStringMapSize(i.Data)) } func (c *DomainConfig) ByteSize() uint64 { if c == nil { return 0 } size := int(unsafe.Sizeof(*c)) + len(c.HistoryArchivalURI) + len(c.VisibilityArchivalURI) asyncWorkflowConfigSize := int(unsafe.Sizeof(c.AsyncWorkflowConfig)) + len(c.AsyncWorkflowConfig.PredefinedQueueName) + len(c.AsyncWorkflowConfig.QueueType) if c.AsyncWorkflowConfig.QueueConfig != nil { size += len(c.AsyncWorkflowConfig.QueueConfig.Data) } binariesSize := 0 for key, value := range c.BadBinaries.Binaries { binariesSize += len(key) if value != nil { binariesSize += len(value.Reason) + len(value.Operator) } } isolationGroupsSize := 0 for key, value := range c.IsolationGroups { binariesSize += len(key) + len(value.Name) } return uint64(size + asyncWorkflowConfigSize + binariesSize + isolationGroupsSize) } func (c *DomainReplicationConfig) ByteSize() uint64 { if c == nil { return 0 } total := len(c.ActiveClusterName) for _, v := range c.Clusters { if v == nil { continue } total += len(v.ClusterName) } return uint64(total) } func estimateStringMapSize(m map[string]string) int { size := 0 for key, value := range m { size += len(key) + len(value) } return size } func (r *ReadRawHistoryBranchResponse) Size2() uint64 { if r == nil { return 0 } total := uint64(int(unsafe.Sizeof(*r)) + len(r.NextPageToken)) for _, v := range r.HistoryEventBlobs { total += v.ByteSize() } return total } func (d *DataBlob) ByteSize() uint64 { if d == nil { return 0 } return uint64(int(unsafe.Sizeof(*d)) + len(d.Data)) } func (r *ListCurrentExecutionsResponse) ByteSize() uint64 { if r == nil { return 0 } total := uint64(int(unsafe.Sizeof(*r)) + len(r.PageToken)) for _, v := range r.Executions { total += v.ByteSize() } return total } func (r *CurrentWorkflowExecution) ByteSize() uint64 { if r == nil { return 0 } return uint64(int(unsafe.Sizeof(*r)) + len(r.DomainID) + len(r.WorkflowID) + len(r.RunID) + len(r.CurrentRunID)) } func (r QueueMessageList) ByteSize() uint64 { if r == nil { return 0 } total := uint64(0) for _, v := range r { total += v.ByteSize() } return total } func (r *QueueMessage) ByteSize() uint64 { if r == nil { return 0 } return uint64(int(unsafe.Sizeof(*r)) + len(r.Payload)) } func (r GetAllHistoryTreeBranchesResponse) ByteSize() uint64 { total := uint64(int(unsafe.Sizeof(r)) + len(r.NextPageToken)) for _, v := range r.Branches { total += v.ByteSize() } return total } func (r HistoryBranchDetail) ByteSize() uint64 { return uint64(int(unsafe.Sizeof(r)) + len(r.TreeID) + len(r.BranchID) + len(r.Info)) } // If MetricTags() []metrics.Tag is defined, then metrics will be emitted for the request. func (r ReadHistoryBranchRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r AppendHistoryNodesRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r DeleteHistoryBranchRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r ForkHistoryBranchRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r GetHistoryTreeRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r CompleteTaskRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r CompleteTasksLessThanRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r CreateTasksRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r DeleteTaskListRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r GetTasksRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r LeaseTaskListRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r UpdateTaskListRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r GetTaskListSizeRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } // For Execution manager there are extra rules. // If GetDomainName() string is defined, then the request will have extra log and shard metrics. // GetExtraLogTags() []tag.Tag is defined, then the request will have extra log tags. func (r *CreateWorkflowExecutionRequest) GetDomainName() string { return r.DomainName } func (r *IsWorkflowExecutionExistsRequest) GetDomainName() string { return r.DomainName } func (r *PutReplicationTaskToDLQRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.DomainTag(r.DomainName)} } func (r *CreateWorkflowExecutionRequest) GetExtraLogTags() []tag.Tag { if r == nil || r.NewWorkflowSnapshot.ExecutionInfo == nil { return nil } return []tag.Tag{tag.WorkflowID(r.NewWorkflowSnapshot.ExecutionInfo.WorkflowID)} } func (r *UpdateWorkflowExecutionRequest) GetDomainName() string { return r.DomainName } func (r *UpdateWorkflowExecutionRequest) GetExtraLogTags() []tag.Tag { if r == nil || r.UpdateWorkflowMutation.ExecutionInfo == nil { return nil } return []tag.Tag{tag.WorkflowID(r.UpdateWorkflowMutation.ExecutionInfo.WorkflowID)} } func (r GetWorkflowExecutionRequest) GetDomainName() string { return r.DomainName } func (r GetWorkflowExecutionRequest) GetExtraLogTags() []tag.Tag { return []tag.Tag{tag.WorkflowID(r.Execution.WorkflowID)} } func (r ConflictResolveWorkflowExecutionRequest) GetDomainName() string { return r.DomainName } func (r DeleteWorkflowExecutionRequest) GetDomainName() string { return r.DomainName } func (r DeleteWorkflowExecutionRequest) GetExtraLogTags() []tag.Tag { return []tag.Tag{tag.WorkflowID(r.WorkflowID)} } func (r DeleteCurrentWorkflowExecutionRequest) GetDomainName() string { return r.DomainName } func (r DeleteCurrentWorkflowExecutionRequest) GetExtraLogTags() []tag.Tag { return []tag.Tag{tag.WorkflowID(r.WorkflowID)} } func (r GetCurrentExecutionRequest) GetDomainName() string { return r.DomainName } func (r GetCurrentExecutionRequest) GetExtraLogTags() []tag.Tag { return []tag.Tag{tag.WorkflowID(r.WorkflowID)} } func (r GetHistoryTasksRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.TaskCategoryTag(r.TaskCategory.Name())} } func (r RangeCompleteHistoryTaskRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.TaskCategoryTag(r.TaskCategory.Name())} } func (r CompleteHistoryTaskRequest) MetricTags() []metrics.Tag { return []metrics.Tag{metrics.TaskCategoryTag(r.TaskCategory.Name())} } ================================================ FILE: common/persistence/metered_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "testing" "time" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/testing/testdatagen" "github.com/uber/cadence/common/types" ) func TestGetTasksResponseEstimatePayloadSize(t *testing.T) { t.Run("does not panic", func(t *testing.T) { fuzzer := testdatagen.NewWithNilChance(t, int64(123), 0.25) assert.NotPanics(t, func() { for i := 0; i < 100; i++ { response := &GetTasksResponse{} fuzzer.Fuzz(&response) _ = response.ByteSize() } }) }) t.Run("response is not nil", func(t *testing.T) { response := &GetTasksResponse{ Tasks: []*TaskInfo{ { DomainID: "domainID", WorkflowID: "workflowID", RunID: "runID", TaskID: 0, ScheduleID: 0, ScheduleToStartTimeoutSeconds: 0, Expiry: time.Time{}, CreatedTime: time.Time{}, PartitionConfig: nil, }, }, } assert.Equal(t, uint64(175), response.ByteSize()) }) t.Run("response with bigger payload emits a bigger value", func(t *testing.T) { response := &GetTasksResponse{ Tasks: []*TaskInfo{ { DomainID: "domainID", WorkflowID: "workflowID", RunID: "runID", TaskID: 0, ScheduleID: 0, ScheduleToStartTimeoutSeconds: 0, Expiry: time.Time{}, CreatedTime: time.Time{}, PartitionConfig: map[string]string{ "key": "value", "key2": "value2", }, }, }, } assert.Equal(t, uint64(193), response.ByteSize()) }) } func TestGetListDomainsResponseEstimatePayloadSize(t *testing.T) { t.Run("does not panic", func(t *testing.T) { fuzzer := testdatagen.NewWithNilChance(t, int64(123), 0.25) assert.NotPanics(t, func() { for i := 0; i < 100; i++ { response := &ListDomainsResponse{} fuzzer.Fuzz(&response) _ = response.ByteSize() } }) }) t.Run("domain info", func(t *testing.T) { info := &DomainInfo{ID: "ID", Name: "Name", Status: 2, Description: "Desc", OwnerEmail: "Email", Data: nil} assert.Equal(t, uint64(95), info.ByteSize()) info = &DomainInfo{ ID: "ID", Name: "Name", Status: 0, Description: "Desc", OwnerEmail: "Email", Data: map[string]string{"key": "value"}, } assert.Equal(t, uint64(103), info.ByteSize()) }) t.Run("full response", func(t *testing.T) { response := &ListDomainsResponse{ Domains: []*GetDomainResponse{ nil, { Info: nil, Config: nil, ReplicationConfig: nil, IsGlobalDomain: false, ConfigVersion: 0, FailoverVersion: 0, FailoverNotificationVersion: 0, PreviousFailoverVersion: 0, FailoverEndTime: nil, LastUpdatedTime: 0, NotificationVersion: 0, }, { Info: &DomainInfo{ ID: "ID", Name: "Name", Status: 0, Description: "Desc", OwnerEmail: "Email", Data: map[string]string{ "key": "value", }, }, Config: &DomainConfig{ Retention: 0, EmitMetric: false, HistoryArchivalStatus: 0, HistoryArchivalURI: "URI", VisibilityArchivalStatus: 0, VisibilityArchivalURI: "URI", BadBinaries: types.BadBinaries{}, IsolationGroups: map[string]types.IsolationGroupPartition{ "key": {Name: "abc", State: 0}, }, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: false, PredefinedQueueName: "name", QueueType: "type", QueueConfig: &types.DataBlob{ EncodingType: nil, Data: []byte{1, 2, 3}, }, }, }, ReplicationConfig: &DomainReplicationConfig{ ActiveClusterName: "cluster", Clusters: []*ClusterReplicationConfig{ {ClusterName: "cluster"}, }, }, IsGlobalDomain: false, ConfigVersion: 0, FailoverVersion: 0, FailoverNotificationVersion: 0, PreviousFailoverVersion: 0, FailoverEndTime: nil, LastUpdatedTime: 0, NotificationVersion: 0, }, }, NextPageToken: []byte{1, 2, 3}, } assert.Equal(t, uint64(535), response.ByteSize()) }) } func TestRawReadHistoryResponseEstimatePayloadSize(t *testing.T) { t.Run("does not panic", func(t *testing.T) { fuzzer := testdatagen.NewWithNilChance(t, int64(123), 0.25) assert.NotPanics(t, func() { for i := 0; i < 100; i++ { response := &ReadRawHistoryBranchResponse{} fuzzer.Fuzz(&response) _ = response.Size2() } }) }) t.Run("response is not nil", func(t *testing.T) { response := ReadRawHistoryBranchResponse{ HistoryEventBlobs: []*DataBlob{ nil, { Encoding: "abc", Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, }, }, NextPageToken: []byte{1, 2, 3}, Size: 123, } assert.Equal(t, uint64(109), response.Size2()) }) t.Run("a bigger response emits a bigger value", func(t *testing.T) { response := ReadRawHistoryBranchResponse{ HistoryEventBlobs: []*DataBlob{ { Encoding: "abc", Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, }, }, NextPageToken: []byte{1, 2, 3}, Size: 123, } assert.Equal(t, uint64(113), response.Size2()) }) } func TestListCurrentExecutionsResponseEstimatePayloadSize(t *testing.T) { t.Run("does not panic", func(t *testing.T) { fuzzer := testdatagen.NewWithNilChance(t, int64(123), 0.25) assert.NotPanics(t, func() { for i := 0; i < 100; i++ { response := &ListCurrentExecutionsResponse{} fuzzer.Fuzz(&response) _ = response.ByteSize() } }) }) t.Run("response is not nil", func(t *testing.T) { response := &ListCurrentExecutionsResponse{ Executions: []*CurrentWorkflowExecution{ nil, {DomainID: "DomainID", WorkflowID: "WorkflowID", RunID: "ID", State: 0, CurrentRunID: "ID"}, }, PageToken: []byte{1, 2, 3}, } assert.Equal(t, uint64(145), response.ByteSize()) }) t.Run("a bigger response emits a bigger value", func(t *testing.T) { response := &ListCurrentExecutionsResponse{ Executions: []*CurrentWorkflowExecution{ {DomainID: "LongDomainID", WorkflowID: "LongWorkflowID", RunID: "ID", State: 0, CurrentRunID: "ID"}, }, PageToken: []byte{1, 2, 3}, } assert.Equal(t, uint64(153), response.ByteSize()) }) } func TestGetHistoryTasksResponseEstimatePayloadSize(t *testing.T) { t.Run("does not panic", func(t *testing.T) { fuzzer := testdatagen.NewWithNilChance(t, int64(123), 0.25).Funcs( func(t *Task, c fuzz.Continue) { switch c.Intn(21) { case 0: var m DecisionTask c.Fuzz(&m) *t = &m case 1: var m ActivityTask c.Fuzz(&m) *t = &m case 2: var m RecordWorkflowStartedTask c.Fuzz(&m) *t = &m case 3: var m ResetWorkflowTask c.Fuzz(&m) *t = &m case 4: var m CloseExecutionTask c.Fuzz(&m) *t = &m case 5: var m DeleteHistoryEventTask c.Fuzz(&m) *t = &m case 6: var m DecisionTimeoutTask c.Fuzz(&m) *t = &m case 7: var m WorkflowTimeoutTask c.Fuzz(&m) *t = &m case 8: var m CancelExecutionTask c.Fuzz(&m) *t = &m case 9: var m SignalExecutionTask c.Fuzz(&m) *t = &m case 10: var m RecordChildExecutionCompletedTask c.Fuzz(&m) *t = &m case 11: var m UpsertWorkflowSearchAttributesTask c.Fuzz(&m) *t = &m case 12: var m UserTimerTask c.Fuzz(&m) *t = &m case 13: var m ActivityTimeoutTask c.Fuzz(&m) *t = &m case 14: var m ActivityRetryTimerTask c.Fuzz(&m) *t = &m case 15: var m WorkflowBackoffTimerTask c.Fuzz(&m) *t = &m case 16: var m HistoryReplicationTask c.Fuzz(&m) *t = &m case 17: var m StartChildExecutionTask c.Fuzz(&m) *t = &m case 18: var m RecordWorkflowClosedTask c.Fuzz(&m) *t = &m case 19: var m SyncActivityTask c.Fuzz(&m) *t = &m case 20: var m FailoverMarkerTask c.Fuzz(&m) *t = &m } }, ) assert.NotPanics(t, func() { for i := 0; i < 100; i++ { response := &GetHistoryTasksResponse{} fuzzer.Fuzz(&response) _ = response.ByteSize() } }) }) t.Run("response is not nil", func(t *testing.T) { response := &GetHistoryTasksResponse{ Tasks: []Task{ nil, &DecisionTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "DomainID", WorkflowID: "WorkflowID", RunID: "ID", }, TaskData: TaskData{ TaskID: 0, VisibilityTimestamp: time.Time{}, Version: 0, }, TargetDomainID: "DomainID", TaskList: "", ScheduleID: 0, }, }, NextPageToken: []byte{1, 2, 3}, } assert.Equal(t, uint64(135), response.ByteSize()) }) t.Run("a bigger response emits a bigger value", func(t *testing.T) { response := &GetHistoryTasksResponse{ Tasks: []Task{ &SignalExecutionTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "DomainID", WorkflowID: "WorkflowID", RunID: "ID", }, TaskData: TaskData{ TaskID: 0, VisibilityTimestamp: time.Time{}, Version: 0, }, TargetDomainID: "DomainID", TargetWorkflowID: "WorkflowID", TargetRunID: "ID", TargetChildWorkflowOnly: false, }, }, NextPageToken: []byte{1, 2, 3}, } assert.Equal(t, uint64(140), response.ByteSize()) }) } func TestQueueMessageListEstimatePayloadSize(t *testing.T) { t.Run("does not panic", func(t *testing.T) { fuzzer := testdatagen.NewWithNilChance(t, int64(123), 0.25) assert.NotPanics(t, func() { for i := 0; i < 100; i++ { response := &QueueMessageList{} fuzzer.Fuzz(response) _ = response.ByteSize() } }) }) t.Run("response is not nil", func(t *testing.T) { response := &QueueMessageList{ {ID: 0, QueueType: 0, Payload: nil}, } assert.Equal(t, uint64(40), response.ByteSize()) }) t.Run("a bigger response emits a bigger value", func(t *testing.T) { response := &QueueMessageList{ {ID: 0, QueueType: 0, Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, } assert.Equal(t, uint64(50), response.ByteSize()) }) } func TestGetAllHistoryTreeBranchesResponseEstimatePayloadSize(t *testing.T) { t.Run("does not panic", func(t *testing.T) { fuzzer := testdatagen.NewWithNilChance(t, int64(123), 0.25) assert.NotPanics(t, func() { for i := 0; i < 100; i++ { response := &GetAllHistoryTreeBranchesResponse{} fuzzer.Fuzz(response) _ = response.ByteSize() } }) }) t.Run("response is not nil", func(t *testing.T) { response := &GetAllHistoryTreeBranchesResponse{ NextPageToken: []byte{1, 2, 3}, Branches: []HistoryBranchDetail{ {TreeID: "", BranchID: "", ForkTime: time.Time{}, Info: ""}, }, } assert.Equal(t, uint64(123), response.ByteSize()) }) t.Run("a bigger response emits a bigger value", func(t *testing.T) { response := &GetAllHistoryTreeBranchesResponse{ NextPageToken: []byte{1, 2, 3}, Branches: []HistoryBranchDetail{ {TreeID: "TreeID", BranchID: "BID", ForkTime: time.Time{}, Info: "Info"}, }, } assert.Equal(t, uint64(136), response.ByteSize()) }) } ================================================ FILE: common/persistence/nosql/constants.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql // Guidelines for creating new special UUID constants for all NoSQL // For DB specific constants(e.g. Cassandra), create them in the db specific package. // Each UUID should be of the form: E0000000-R000-f000-f000-00000000000x // Where x is any hexadecimal value, E represents the entity type valid values are: // E = {DomainID = 1, WorkflowID = 2, RunID = 3} // R represents row type in executions table, valid values are: // R = {Shard = 1, Execution = 2, Transfer = 3, Timer = 4, Replication = 5, Replication_DLQ = 6, CrossCluster = 7} const ( // Special Domains related constants emptyDomainID = "10000000-0000-f000-f000-000000000000" // Special Run IDs emptyRunID = "30000000-0000-f000-f000-000000000000" rowTypeReplicationWorkflowID = "20000000-5000-f000-f000-000000000000" rowTypeReplicationRunID = "30000000-5000-f000-f000-000000000000" emptyInitiatedID = int64(-7) ) ================================================ FILE: common/persistence/nosql/factory.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "sync" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" ) type ( // Factory vends datastore implementations backed by cassandra Factory struct { sync.RWMutex cfg config.ShardedNoSQL clusterName string logger log.Logger metricsClient metrics.Client execStoreFactory *executionStoreFactory dc *persistence.DynamicConfiguration parser serialization.Parser taskSerializer serialization.TaskSerializer } executionStoreFactory struct { logger log.Logger shardedNosqlStore shardedNosqlStore taskSerializer serialization.TaskSerializer } ) // NewFactory returns an instance of a factory object which can be used to create // datastores that are backed by cassandra func NewFactory(cfg config.ShardedNoSQL, clusterName string, logger log.Logger, metricsClient metrics.Client, taskSerializer serialization.TaskSerializer, parser serialization.Parser, dc *persistence.DynamicConfiguration) *Factory { return &Factory{ cfg: cfg, clusterName: clusterName, logger: logger, metricsClient: metricsClient, taskSerializer: taskSerializer, dc: dc, parser: parser, } } // NewTaskStore returns a new task store func (f *Factory) NewTaskStore() (persistence.TaskStore, error) { return newNoSQLTaskStore(f.cfg, f.logger, f.metricsClient, f.dc) } // NewShardStore returns a new shard store func (f *Factory) NewShardStore() (persistence.ShardStore, error) { return newNoSQLShardStore(f.cfg, f.clusterName, f.logger, f.metricsClient, f.dc, f.parser) } // NewHistoryStore returns a new history store func (f *Factory) NewHistoryStore() (persistence.HistoryStore, error) { return newNoSQLHistoryStore(f.cfg, f.logger, f.metricsClient, f.dc) } // NewDomainStore returns a metadata store that understands only v2 func (f *Factory) NewDomainStore() (persistence.DomainStore, error) { return newNoSQLDomainStore(f.cfg, f.clusterName, f.logger, f.metricsClient, f.dc) } // NewDomainAuditStore returns a domain audit store func (f *Factory) NewDomainAuditStore() (persistence.DomainAuditStore, error) { return newNoSQLDomainAuditStore(f.cfg, f.logger, f.metricsClient, f.dc) } // NewExecutionStore returns an ExecutionStore for a given shardID func (f *Factory) NewExecutionStore(shardID int) (persistence.ExecutionStore, error) { factory, err := f.executionStoreFactory() if err != nil { return nil, err } return factory.new(shardID) } // NewVisibilityStore returns a visibility store func (f *Factory) NewVisibilityStore(sortByCloseTime bool) (persistence.VisibilityStore, error) { return newNoSQLVisibilityStore(sortByCloseTime, f.cfg, f.logger, f.metricsClient, f.dc) } // NewQueue returns a new queue backed by cassandra func (f *Factory) NewQueue(queueType persistence.QueueType) (persistence.QueueStore, error) { return newNoSQLQueueStore(f.cfg, f.logger, f.metricsClient, queueType, f.dc) } // NewConfigStore returns a new config store func (f *Factory) NewConfigStore() (persistence.ConfigStore, error) { return NewNoSQLConfigStore(f.cfg, f.logger, f.metricsClient, f.dc) } // Close closes the factory func (f *Factory) Close() { f.Lock() defer f.Unlock() if f.execStoreFactory != nil { f.execStoreFactory.close() } } func (f *Factory) executionStoreFactory() (*executionStoreFactory, error) { f.RLock() if f.execStoreFactory != nil { f.RUnlock() return f.execStoreFactory, nil } f.RUnlock() f.Lock() defer f.Unlock() if f.execStoreFactory != nil { return f.execStoreFactory, nil } factory, err := newExecutionStoreFactory(f.cfg, f.logger, f.metricsClient, f.taskSerializer, f.dc) if err != nil { return nil, err } f.execStoreFactory = factory return f.execStoreFactory, nil } // newExecutionStoreFactory is used to create an instance of ExecutionStoreFactory implementation func newExecutionStoreFactory( cfg config.ShardedNoSQL, logger log.Logger, metricsClient metrics.Client, taskSerializer serialization.TaskSerializer, dc *persistence.DynamicConfiguration, ) (*executionStoreFactory, error) { s, err := newShardedNosqlStore(cfg, logger, metricsClient, dc) if err != nil { return nil, err } return &executionStoreFactory{ taskSerializer: taskSerializer, logger: logger, shardedNosqlStore: s, }, nil } func (f *executionStoreFactory) close() { f.shardedNosqlStore.Close() } // new implements ExecutionStoreFactory interface func (f *executionStoreFactory) new(shardID int) (persistence.ExecutionStore, error) { storeShard, err := f.shardedNosqlStore.GetStoreShardByHistoryShard(shardID) if err != nil { return nil, err } pmgr, err := NewExecutionStore(shardID, storeShard.db, f.logger, f.taskSerializer, storeShard.dc) if err != nil { return nil, err } return pmgr, nil } ================================================ FILE: common/persistence/nosql/nosql_config_store.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "fmt" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) type nosqlConfigStore struct { nosqlStore } func NewNoSQLConfigStore( cfg config.ShardedNoSQL, logger log.Logger, metricsClient metrics.Client, dc *persistence.DynamicConfiguration, ) (persistence.ConfigStore, error) { shardedStore, err := newShardedNosqlStore(cfg, logger, metricsClient, dc) if err != nil { return nil, err } return &nosqlConfigStore{ nosqlStore: shardedStore.GetDefaultShard(), }, nil } func (m *nosqlConfigStore) FetchConfig(ctx context.Context, configType persistence.ConfigType) (*persistence.InternalConfigStoreEntry, error) { entry, err := m.db.SelectLatestConfig(ctx, int(configType)) if err != nil { if m.db.IsNotFoundError(err) { return nil, nil } return nil, convertCommonErrors(m.db, "FetchConfig", err) } return entry, nil } func (m *nosqlConfigStore) UpdateConfig(ctx context.Context, value *persistence.InternalConfigStoreEntry) error { err := m.db.InsertConfig(ctx, value) if err != nil { if _, ok := err.(*nosqlplugin.ConditionFailure); ok { return &persistence.ConditionFailedError{Msg: fmt.Sprintf("Version %v already exists. Condition Failed", value.Version)} } return convertCommonErrors(m.db, "UpdateConfig", err) } return nil } ================================================ FILE: common/persistence/nosql/nosql_config_store_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package nosql import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) func setUpMocksForNoSQLConfigStore(t *testing.T) (*nosqlConfigStore, *nosqlplugin.MockDB) { ctrl := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(ctrl) logger := log.NewNoop() // Creating the nosqlConfigStore and injecting the mocked DB configStore := &nosqlConfigStore{ nosqlStore: nosqlStore{ logger: logger, db: mockDB, }, } return configStore, mockDB } func TestFetchConfig(t *testing.T) { testCases := []struct { name string setupMock func(mockDB *nosqlplugin.MockDB) configType persistence.ConfigType expectError bool expectedError string expectedResult *persistence.InternalConfigStoreEntry }{ { name: "success", setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT(). SelectLatestConfig(gomock.Any(), int(persistence.DynamicConfig)). Return(&persistence.InternalConfigStoreEntry{ Version: 1, Values: &persistence.DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("config-values")}, }, nil).Times(1) }, configType: persistence.DynamicConfig, expectError: false, expectedResult: &persistence.InternalConfigStoreEntry{Version: 1, Values: &persistence.DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("config-values")}}, }, { name: "config not found", setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT(). SelectLatestConfig(gomock.Any(), int(persistence.DynamicConfig)). Return(nil, errors.New("not found")). Times(1) mockDB.EXPECT().IsNotFoundError(errors.New("not found")).Return(true).Times(1) }, configType: persistence.DynamicConfig, expectError: false, expectedResult: nil, }, { name: "fetch error", setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT(). SelectLatestConfig(gomock.Any(), int(persistence.DynamicConfig)). Return(nil, errors.New("fetch error")). Times(1) mockDB.EXPECT().IsNotFoundError(errors.New("fetch error")).Return(false).Times(1) mockDB.EXPECT().IsNotFoundError(errors.New("fetch error")).Return(true).Times(1) }, configType: persistence.DynamicConfig, expectError: true, expectedError: "fetch error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { configStore, mockDB := setUpMocksForNoSQLConfigStore(t) tc.setupMock(mockDB) result, err := configStore.FetchConfig(context.Background(), tc.configType) if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedResult, result) } }) } } func TestUpdateConfig(t *testing.T) { testCases := []struct { name string setupMock func(mockDB *nosqlplugin.MockDB) value *persistence.InternalConfigStoreEntry expectError bool expectedError string }{ { name: "success", setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT(). InsertConfig(gomock.Any(), &persistence.InternalConfigStoreEntry{ Version: 1, Values: &persistence.DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("config-values")}, }). Return(nil). Times(1) }, value: &persistence.InternalConfigStoreEntry{ Version: 1, Values: &persistence.DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("config-values")}, }, expectError: false, }, { name: "condition failure", setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT(). InsertConfig(gomock.Any(), gomock.Any()). Return(&nosqlplugin.ConditionFailure{}). Times(1) }, value: &persistence.InternalConfigStoreEntry{ Version: 1, Values: &persistence.DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("config-values")}, }, expectError: true, expectedError: "Version 1 already exists. Condition Failed", }, { name: "insert error", setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT(). InsertConfig(gomock.Any(), gomock.Any()). Return(errors.New("insert error")). Times(1) mockDB.EXPECT().IsNotFoundError(errors.New("insert error")).Return(true).Times(1) }, value: &persistence.InternalConfigStoreEntry{ Version: 1, Values: &persistence.DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("config-values")}, }, expectError: true, expectedError: "insert error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { configStore, mockDB := setUpMocksForNoSQLConfigStore(t) tc.setupMock(mockDB) err := configStore.UpdateConfig(context.Background(), tc.value) if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: common/persistence/nosql/nosql_domain_audit_store.go ================================================ // Copyright (c) 2025 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) type nosqlDomainAuditStore struct { nosqlStore } // newNoSQLDomainAuditStore is used to create an instance of DomainAuditStore implementation func newNoSQLDomainAuditStore( cfg config.ShardedNoSQL, logger log.Logger, metricsClient metrics.Client, dc *persistence.DynamicConfiguration, ) (persistence.DomainAuditStore, error) { shardedStore, err := newShardedNosqlStore(cfg, logger, metricsClient, dc) if err != nil { return nil, err } return &nosqlDomainAuditStore{ nosqlStore: shardedStore.GetDefaultShard(), }, nil } // CreateDomainAuditLog creates a new domain audit log entry func (m *nosqlDomainAuditStore) CreateDomainAuditLog( ctx context.Context, request *persistence.InternalCreateDomainAuditLogRequest, ) (*persistence.CreateDomainAuditLogResponse, error) { row := &nosqlplugin.DomainAuditLogRow{ DomainID: request.DomainID, EventID: request.EventID, StateBefore: getDataBlobBytes(request.StateBefore), StateBeforeEncoding: getDataBlobEncoding(request.StateBefore), StateAfter: getDataBlobBytes(request.StateAfter), StateAfterEncoding: getDataBlobEncoding(request.StateAfter), OperationType: request.OperationType, CreatedTime: request.CreatedTime, LastUpdatedTime: request.LastUpdatedTime, Identity: request.Identity, IdentityType: request.IdentityType, Comment: request.Comment, TTLSeconds: request.TTLSeconds, } err := m.db.InsertDomainAuditLog(ctx, row) if err != nil { return nil, convertCommonErrors(m.db, "CreateDomainAuditLog", err) } return &persistence.CreateDomainAuditLogResponse{ EventID: request.EventID, }, nil } // GetDomainAuditLogs retrieves domain audit logs func (m *nosqlDomainAuditStore) GetDomainAuditLogs( ctx context.Context, request *persistence.GetDomainAuditLogsRequest, ) (*persistence.InternalGetDomainAuditLogsResponse, error) { filter := &nosqlplugin.DomainAuditLogFilter{ DomainID: request.DomainID, OperationType: request.OperationType, MinCreatedTime: request.MinCreatedTime, MaxCreatedTime: request.MaxCreatedTime, PageSize: request.PageSize, NextPageToken: request.NextPageToken, } rows, nextPageToken, err := m.db.SelectDomainAuditLogs(ctx, filter) if err != nil { return nil, convertCommonErrors(m.db, "GetDomainAuditLogs", err) } var auditLogs []*persistence.InternalDomainAuditLog for _, row := range rows { auditLog := &persistence.InternalDomainAuditLog{ EventID: row.EventID, DomainID: row.DomainID, OperationType: row.OperationType, CreatedTime: row.CreatedTime, LastUpdatedTime: row.LastUpdatedTime, Identity: row.Identity, IdentityType: row.IdentityType, Comment: row.Comment, } if len(row.StateBefore) > 0 { auditLog.StateBefore = &persistence.DataBlob{ Encoding: constants.EncodingType(row.StateBeforeEncoding), Data: row.StateBefore, } } if len(row.StateAfter) > 0 { auditLog.StateAfter = &persistence.DataBlob{ Encoding: constants.EncodingType(row.StateAfterEncoding), Data: row.StateAfter, } } auditLogs = append(auditLogs, auditLog) } return &persistence.InternalGetDomainAuditLogsResponse{ AuditLogs: auditLogs, NextPageToken: nextPageToken, }, nil } func getDataBlobBytes(blob *persistence.DataBlob) []byte { if blob == nil { return nil } return blob.Data } func getDataBlobEncoding(blob *persistence.DataBlob) string { if blob == nil { return "" } return string(blob.Encoding) } ================================================ FILE: common/persistence/nosql/nosql_domain_audit_store_test.go ================================================ // Copyright (c) 2025 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) func setUpMocksForDomainAuditStore(t *testing.T) (*nosqlDomainAuditStore, *nosqlplugin.MockDB) { ctrl := gomock.NewController(t) dbMock := nosqlplugin.NewMockDB(ctrl) shardedStore := nosqlStore{ db: dbMock, } domainAuditStore := &nosqlDomainAuditStore{ nosqlStore: shardedStore, } return domainAuditStore, dbMock } func TestCreateDomainAuditLog(t *testing.T) { ctx := context.Background() now := time.Unix(1234567890, 0) stateBeforeBlob := &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRWSnappy, Data: []byte("state-before-data"), } stateAfterBlob := &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("state-after-data"), } tests := map[string]struct { setupMock func(*nosqlplugin.MockDB) request *persistence.InternalCreateDomainAuditLogRequest expectError bool expectedID string }{ "success with full data": { setupMock: func(dbMock *nosqlplugin.MockDB) { expectedRow := &nosqlplugin.DomainAuditLogRow{ DomainID: "domain-123", EventID: "event-456", StateBefore: stateBeforeBlob.Data, StateBeforeEncoding: string(stateBeforeBlob.Encoding), StateAfter: stateAfterBlob.Data, StateAfterEncoding: string(stateAfterBlob.Encoding), OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now, LastUpdatedTime: now, Identity: "test-user", IdentityType: "user", Comment: "test comment", } dbMock.EXPECT().InsertDomainAuditLog(ctx, expectedRow).Return(nil).Times(1) }, request: &persistence.InternalCreateDomainAuditLogRequest{ DomainID: "domain-123", EventID: "event-456", StateBefore: stateBeforeBlob, StateAfter: stateAfterBlob, OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now, LastUpdatedTime: now, Identity: "test-user", IdentityType: "user", Comment: "test comment", }, expectError: false, expectedID: "event-456", }, "success with nil state blobs": { setupMock: func(dbMock *nosqlplugin.MockDB) { expectedRow := &nosqlplugin.DomainAuditLogRow{ DomainID: "domain-123", EventID: "event-789", StateBefore: nil, StateBeforeEncoding: "", StateAfter: nil, StateAfterEncoding: "", OperationType: persistence.DomainAuditOperationTypeCreate, CreatedTime: now, LastUpdatedTime: now, Identity: "system", IdentityType: "system", Comment: "", } dbMock.EXPECT().InsertDomainAuditLog(ctx, expectedRow).Return(nil).Times(1) }, request: &persistence.InternalCreateDomainAuditLogRequest{ DomainID: "domain-123", EventID: "event-789", StateBefore: nil, StateAfter: nil, OperationType: persistence.DomainAuditOperationTypeCreate, CreatedTime: now, LastUpdatedTime: now, Identity: "system", IdentityType: "system", Comment: "", }, expectError: false, expectedID: "event-789", }, "database error": { setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().InsertDomainAuditLog(ctx, gomock.Any()).Return(errors.New("database error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(false).AnyTimes() dbMock.EXPECT().IsTimeoutError(gomock.Any()).Return(false).AnyTimes() dbMock.EXPECT().IsThrottlingError(gomock.Any()).Return(false).AnyTimes() dbMock.EXPECT().IsDBUnavailableError(gomock.Any()).Return(false).AnyTimes() }, request: &persistence.InternalCreateDomainAuditLogRequest{ DomainID: "domain-123", EventID: "event-999", OperationType: persistence.DomainAuditOperationTypeFailover, CreatedTime: now, LastUpdatedTime: now, Identity: "test-user", IdentityType: "user", }, expectError: true, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { store, dbMock := setUpMocksForDomainAuditStore(t) tc.setupMock(dbMock) resp, err := store.CreateDomainAuditLog(ctx, tc.request) if tc.expectError { assert.Error(t, err) assert.Nil(t, resp) } else { assert.NoError(t, err) require.NotNil(t, resp) assert.Equal(t, tc.expectedID, resp.EventID) } }) } } func TestGetDomainAuditLogs(t *testing.T) { ctx := context.Background() now := time.Unix(1234567890, 0) minTime := now.Add(-24 * time.Hour) maxTime := now tests := map[string]struct { setupMock func(*nosqlplugin.MockDB) request *persistence.GetDomainAuditLogsRequest expectError bool expectedCount int validateResult func(*testing.T, *persistence.InternalGetDomainAuditLogsResponse) }{ "success with results": { setupMock: func(dbMock *nosqlplugin.MockDB) { rows := []*nosqlplugin.DomainAuditLogRow{ { EventID: "event-1", DomainID: "domain-123", OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now, LastUpdatedTime: now, Identity: "user-1", IdentityType: "user", Comment: "comment 1", StateBefore: []byte("state-before-1"), StateBeforeEncoding: string(constants.EncodingTypeThriftRWSnappy), StateAfter: []byte("state-after-1"), StateAfterEncoding: string(constants.EncodingTypeThriftRW), }, { EventID: "event-2", DomainID: "domain-123", OperationType: persistence.DomainAuditOperationTypeFailover, CreatedTime: now.Add(-1 * time.Hour), LastUpdatedTime: now.Add(-1 * time.Hour), Identity: "user-2", IdentityType: "user", Comment: "comment 2", StateBefore: []byte("state-before-2"), StateBeforeEncoding: string(constants.EncodingTypeThriftRWSnappy), StateAfter: []byte("state-after-2"), StateAfterEncoding: string(constants.EncodingTypeThriftRWSnappy), }, } nextPageToken := []byte("next-token") expectedFilter := &nosqlplugin.DomainAuditLogFilter{ DomainID: "domain-123", OperationType: persistence.DomainAuditOperationTypeUpdate, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, PageSize: 10, NextPageToken: []byte("current-token"), } dbMock.EXPECT().SelectDomainAuditLogs(ctx, expectedFilter).Return(rows, nextPageToken, nil).Times(1) }, request: &persistence.GetDomainAuditLogsRequest{ DomainID: "domain-123", OperationType: persistence.DomainAuditOperationTypeUpdate, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, PageSize: 10, NextPageToken: []byte("current-token"), }, expectError: false, expectedCount: 2, validateResult: func(t *testing.T, resp *persistence.InternalGetDomainAuditLogsResponse) { require.Len(t, resp.AuditLogs, 2) assert.Equal(t, "event-1", resp.AuditLogs[0].EventID) assert.Equal(t, "domain-123", resp.AuditLogs[0].DomainID) assert.Equal(t, persistence.DomainAuditOperationTypeUpdate, resp.AuditLogs[0].OperationType) assert.NotNil(t, resp.AuditLogs[0].StateBefore) assert.Equal(t, constants.EncodingTypeThriftRWSnappy, resp.AuditLogs[0].StateBefore.Encoding) assert.Equal(t, []byte("state-before-1"), resp.AuditLogs[0].StateBefore.Data) assert.NotNil(t, resp.AuditLogs[0].StateAfter) assert.Equal(t, constants.EncodingTypeThriftRW, resp.AuditLogs[0].StateAfter.Encoding) assert.Equal(t, "event-2", resp.AuditLogs[1].EventID) assert.NotNil(t, resp.AuditLogs[1].StateBefore) assert.NotNil(t, resp.AuditLogs[1].StateAfter) assert.Equal(t, []byte("next-token"), resp.NextPageToken) }, }, "success with empty state blobs": { setupMock: func(dbMock *nosqlplugin.MockDB) { rows := []*nosqlplugin.DomainAuditLogRow{ { EventID: "event-3", DomainID: "domain-456", OperationType: persistence.DomainAuditOperationTypeCreate, CreatedTime: now, LastUpdatedTime: now, Identity: "system", IdentityType: "system", Comment: "created", StateBefore: []byte{}, // Empty slice StateBeforeEncoding: "", StateAfter: nil, // Nil StateAfterEncoding: "", }, } dbMock.EXPECT().SelectDomainAuditLogs(ctx, gomock.Any()).Return(rows, nil, nil).Times(1) }, request: &persistence.GetDomainAuditLogsRequest{ DomainID: "domain-456", PageSize: 10, }, expectError: false, expectedCount: 1, validateResult: func(t *testing.T, resp *persistence.InternalGetDomainAuditLogsResponse) { require.Len(t, resp.AuditLogs, 1) assert.Equal(t, "event-3", resp.AuditLogs[0].EventID) // Empty/nil state blobs should not be converted assert.Nil(t, resp.AuditLogs[0].StateBefore) assert.Nil(t, resp.AuditLogs[0].StateAfter) }, }, "success with no results": { setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectDomainAuditLogs(ctx, gomock.Any()).Return([]*nosqlplugin.DomainAuditLogRow{}, nil, nil).Times(1) }, request: &persistence.GetDomainAuditLogsRequest{ DomainID: "domain-789", PageSize: 10, }, expectError: false, expectedCount: 0, validateResult: func(t *testing.T, resp *persistence.InternalGetDomainAuditLogsResponse) { assert.Empty(t, resp.AuditLogs) assert.Nil(t, resp.NextPageToken) }, }, "database error": { setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectDomainAuditLogs(ctx, gomock.Any()).Return(nil, nil, errors.New("database error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(false).AnyTimes() dbMock.EXPECT().IsTimeoutError(gomock.Any()).Return(false).AnyTimes() dbMock.EXPECT().IsThrottlingError(gomock.Any()).Return(false).AnyTimes() dbMock.EXPECT().IsDBUnavailableError(gomock.Any()).Return(false).AnyTimes() }, request: &persistence.GetDomainAuditLogsRequest{ DomainID: "domain-999", PageSize: 10, }, expectError: true, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { store, dbMock := setUpMocksForDomainAuditStore(t) tc.setupMock(dbMock) resp, err := store.GetDomainAuditLogs(ctx, tc.request) if tc.expectError { assert.Error(t, err) assert.Nil(t, resp) } else { assert.NoError(t, err) require.NotNil(t, resp) assert.Len(t, resp.AuditLogs, tc.expectedCount) if tc.validateResult != nil { tc.validateResult(t, resp) } } }) } } func TestGetDataBlobBytes(t *testing.T) { tests := map[string]struct { input *persistence.DataBlob expected []byte }{ "nil blob": { input: nil, expected: nil, }, "blob with data": { input: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRWSnappy, Data: []byte("test-data"), }, expected: []byte("test-data"), }, "blob with empty data": { input: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte{}, }, expected: []byte{}, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { result := getDataBlobBytes(tc.input) assert.Equal(t, tc.expected, result) }) } } func TestGetDataBlobEncoding(t *testing.T) { tests := map[string]struct { input *persistence.DataBlob expected string }{ "nil blob": { input: nil, expected: "", }, "blob with ThriftRWSnappy encoding": { input: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRWSnappy, Data: []byte("test-data"), }, expected: string(constants.EncodingTypeThriftRWSnappy), }, "blob with ThriftRW encoding": { input: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("test-data"), }, expected: string(constants.EncodingTypeThriftRW), }, "blob with empty encoding": { input: &persistence.DataBlob{ Encoding: "", Data: []byte("test-data"), }, expected: "", }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { result := getDataBlobEncoding(tc.input) assert.Equal(t, tc.expected, result) }) } } ================================================ FILE: common/persistence/nosql/nosql_domain_store.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "fmt" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) type nosqlDomainStore struct { nosqlStore currentClusterName string } // newNoSQLDomainStore is used to create an instance of DomainStore implementation func newNoSQLDomainStore( cfg config.ShardedNoSQL, currentClusterName string, logger log.Logger, metricsClient metrics.Client, dc *persistence.DynamicConfiguration, ) (persistence.DomainStore, error) { shardedStore, err := newShardedNosqlStore(cfg, logger, metricsClient, dc) if err != nil { return nil, err } return &nosqlDomainStore{ nosqlStore: shardedStore.GetDefaultShard(), currentClusterName: currentClusterName, }, nil } // CreateDomain create a domain // Cassandra does not support conditional updates across multiple tables. For this reason we have to first insert into // 'Domains' table and then do a conditional insert into domains_by_name table. If the conditional write fails we // delete the orphaned entry from domains table. There is a chance delete entry could fail and we never delete the // orphaned entry from domains table. We might need a background job to delete those orphaned record. func (m *nosqlDomainStore) CreateDomain( ctx context.Context, request *persistence.InternalCreateDomainRequest, ) (*persistence.CreateDomainResponse, error) { row := &nosqlplugin.DomainRow{ Info: request.Info, Config: request.Config, ReplicationConfig: request.ReplicationConfig, ConfigVersion: request.ConfigVersion, FailoverVersion: request.FailoverVersion, FailoverNotificationVersion: persistence.InitialFailoverNotificationVersion, PreviousFailoverVersion: constants.InitialPreviousFailoverVersion, FailoverEndTime: nil, IsGlobalDomain: request.IsGlobalDomain, LastUpdatedTime: request.LastUpdatedTime, CurrentTimeStamp: request.CurrentTimeStamp, } err := m.db.InsertDomain(ctx, row) if err != nil { if _, ok := err.(*types.DomainAlreadyExistsError); ok { return nil, err } return nil, convertCommonErrors(m.db, "CreateDomain", err) } return &persistence.CreateDomainResponse{ID: request.Info.ID}, nil } func (m *nosqlDomainStore) UpdateDomain( ctx context.Context, request *persistence.InternalUpdateDomainRequest, ) error { row := &nosqlplugin.DomainRow{ Info: request.Info, Config: request.Config, ReplicationConfig: request.ReplicationConfig, ConfigVersion: request.ConfigVersion, FailoverVersion: request.FailoverVersion, FailoverNotificationVersion: request.FailoverNotificationVersion, PreviousFailoverVersion: request.PreviousFailoverVersion, FailoverEndTime: request.FailoverEndTime, NotificationVersion: request.NotificationVersion, LastUpdatedTime: request.LastUpdatedTime, } err := m.db.UpdateDomain(ctx, row) if err != nil { return convertCommonErrors(m.db, "UpdateDomain", err) } return nil } func (m *nosqlDomainStore) GetDomain( ctx context.Context, request *persistence.GetDomainRequest, ) (*persistence.InternalGetDomainResponse, error) { if len(request.ID) > 0 && len(request.Name) > 0 { return nil, &types.BadRequestError{ Message: "GetDomain operation failed. Both ID and Name specified in request.", } } else if len(request.ID) == 0 && len(request.Name) == 0 { return nil, &types.BadRequestError{ Message: "GetDomain operation failed. Both ID and Name are empty.", } } var domainName *string var domainID *string if len(request.ID) > 0 { domainID = common.StringPtr(request.ID) } else { domainName = common.StringPtr(request.Name) } handleError := func(name, ID string, err error) error { identity := name if len(ID) > 0 { identity = ID } if m.db.IsNotFoundError(err) { return &types.EntityNotExistsError{ Message: fmt.Sprintf("Domain %s does not exist.", identity), } } return convertCommonErrors(m.db, "GetDomain", err) } row, err := m.db.SelectDomain(ctx, domainID, domainName) if err != nil { return nil, handleError(request.Name, request.ID, err) } if row.Info.Data == nil { row.Info.Data = map[string]string{} } // for active-active domains, do not populate active cluster name with current cluster name if it is not set if row.ReplicationConfig.ActiveClustersConfig == nil { row.ReplicationConfig.ActiveClusterName = cluster.GetOrUseDefaultActiveCluster(m.currentClusterName, row.ReplicationConfig.ActiveClusterName) } row.ReplicationConfig.Clusters = cluster.GetOrUseDefaultClusters(m.currentClusterName, row.ReplicationConfig.Clusters) return &persistence.InternalGetDomainResponse{ Info: row.Info, Config: row.Config, ReplicationConfig: row.ReplicationConfig, IsGlobalDomain: row.IsGlobalDomain, ConfigVersion: row.ConfigVersion, FailoverVersion: row.FailoverVersion, FailoverNotificationVersion: row.FailoverNotificationVersion, PreviousFailoverVersion: row.PreviousFailoverVersion, FailoverEndTime: row.FailoverEndTime, NotificationVersion: row.NotificationVersion, LastUpdatedTime: row.LastUpdatedTime, }, nil } func (m *nosqlDomainStore) ListDomains( ctx context.Context, request *persistence.ListDomainsRequest, ) (*persistence.InternalListDomainsResponse, error) { rows, nextPageToken, err := m.db.SelectAllDomains(ctx, request.PageSize, request.NextPageToken) if err != nil { return nil, convertCommonErrors(m.db, "ListDomains", err) } var domains []*persistence.InternalGetDomainResponse for _, row := range rows { if row.Info.Data == nil { row.Info.Data = map[string]string{} } // for active-active domains, do not populate active cluster name with current cluster name if it is not set if row.ReplicationConfig.ActiveClustersConfig == nil { row.ReplicationConfig.ActiveClusterName = cluster.GetOrUseDefaultActiveCluster(m.currentClusterName, row.ReplicationConfig.ActiveClusterName) } row.ReplicationConfig.Clusters = cluster.GetOrUseDefaultClusters(m.currentClusterName, row.ReplicationConfig.Clusters) domains = append(domains, &persistence.InternalGetDomainResponse{ Info: row.Info, Config: row.Config, ReplicationConfig: row.ReplicationConfig, IsGlobalDomain: row.IsGlobalDomain, ConfigVersion: row.ConfigVersion, FailoverVersion: row.FailoverVersion, FailoverNotificationVersion: row.FailoverNotificationVersion, PreviousFailoverVersion: row.PreviousFailoverVersion, FailoverEndTime: row.FailoverEndTime, NotificationVersion: row.NotificationVersion, LastUpdatedTime: row.LastUpdatedTime, }) } return &persistence.InternalListDomainsResponse{ Domains: domains, NextPageToken: nextPageToken, }, nil } func (m *nosqlDomainStore) DeleteDomain( ctx context.Context, request *persistence.DeleteDomainRequest, ) error { if err := m.db.DeleteDomain(ctx, &request.ID, nil); err != nil { return convertCommonErrors(m.db, "DeleteDomain", err) } return nil } func (m *nosqlDomainStore) DeleteDomainByName( ctx context.Context, request *persistence.DeleteDomainByNameRequest, ) error { if err := m.db.DeleteDomain(ctx, nil, &request.Name); err != nil { return convertCommonErrors(m.db, "DeleteDomainByName", err) } return nil } func (m *nosqlDomainStore) GetMetadata( ctx context.Context, ) (*persistence.GetMetadataResponse, error) { notificationVersion, err := m.db.SelectDomainMetadata(ctx) if err != nil { return nil, convertCommonErrors(m.db, "GetMetadata", err) } return &persistence.GetMetadataResponse{NotificationVersion: notificationVersion}, nil } ================================================ FILE: common/persistence/nosql/nosql_domain_store_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package nosql import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) func testFixtureDomainInfo() *persistence.DomainInfo { return &persistence.DomainInfo{ ID: "domain-id", Name: "domain-name", Status: 1, Description: "domain-desc", OwnerEmail: "owner@test.com", Data: map[string]string{"test": "a"}, } } func testFixtureInternalDomainConfig() *persistence.InternalDomainConfig { return &persistence.InternalDomainConfig{ Retention: time.Hour, HistoryArchivalStatus: types.ArchivalStatus(1), HistoryArchivalURI: "s3://test-history-archival", VisibilityArchivalStatus: types.ArchivalStatus(2), VisibilityArchivalURI: "s3://test-visibility-archival", BadBinaries: &persistence.DataBlob{ Encoding: "base64", Data: []byte("bad-binaries"), }, IsolationGroups: &persistence.DataBlob{ Encoding: "base64", Data: []byte("isolation-groups"), }, AsyncWorkflowsConfig: &persistence.DataBlob{ Encoding: "base64", Data: []byte("async-workflows"), }, } } func testFixtureDomainReplicationConfig() *persistence.InternalDomainReplicationConfig { return &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "cluster-1", Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: "cluster-1", }, { ClusterName: "cluster-2", }, }, } } func testFixtureDomainReplicationConfigActiveActive() *persistence.InternalDomainReplicationConfig { return &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "", Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: "cluster-1", }, { ClusterName: "cluster-2", }, }, ActiveClustersConfig: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("active-clusters-config"), }, } } func setUpMocksForDomainStore(t *testing.T) (*nosqlDomainStore, *nosqlplugin.MockDB, *MockshardedNosqlStore) { ctrl := gomock.NewController(t) dbMock := nosqlplugin.NewMockDB(ctrl) shardedNoSQLStoreMock := NewMockshardedNosqlStore(ctrl) shardedStore := nosqlStore{ db: dbMock, } shardedNoSQLStoreMock.EXPECT().GetDefaultShard().Return(shardedStore).AnyTimes() domainStore := &nosqlDomainStore{ nosqlStore: shardedNoSQLStoreMock.GetDefaultShard(), currentClusterName: "test-cluster", } return domainStore, dbMock, shardedNoSQLStoreMock } func TestCreateDomain(t *testing.T) { testCases := []struct { name string setupMock func(*nosqlplugin.MockDB) request *persistence.InternalCreateDomainRequest expectError bool expectedID string }{ { name: "success", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().InsertDomain(gomock.Any(), &nosqlplugin.DomainRow{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: persistence.InitialFailoverNotificationVersion, PreviousFailoverVersion: constants.InitialPreviousFailoverVersion, IsGlobalDomain: true, LastUpdatedTime: time.Unix(1, 2), }).Return(nil).Times(1) }, request: &persistence.InternalCreateDomainRequest{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 2, IsGlobalDomain: true, LastUpdatedTime: time.Unix(1, 2), }, expectError: false, expectedID: "domain-id", }, { name: "domain already exists error", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().InsertDomain(gomock.Any(), gomock.Any()).Return(&types.DomainAlreadyExistsError{}).Times(1) }, request: &persistence.InternalCreateDomainRequest{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), IsGlobalDomain: true, LastUpdatedTime: time.Now(), }, expectError: true, }, { name: "common error", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().InsertDomain(gomock.Any(), gomock.Any()).Return(errors.New("test error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.InternalCreateDomainRequest{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), IsGlobalDomain: true, LastUpdatedTime: time.Now(), }, expectError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup mocks for each test case individually store, dbMock, _ := setUpMocksForDomainStore(t) // Setup test-specific mock behavior tc.setupMock(dbMock) // Execute the method under test resp, err := store.CreateDomain(context.Background(), tc.request) // Validate results if tc.expectError { assert.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tc.expectedID, resp.ID) } }) } } func TestUpdateDomain(t *testing.T) { testCases := []struct { name string setupMock func(*nosqlplugin.MockDB) request *persistence.InternalUpdateDomainRequest expectError bool }{ { name: "success", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().UpdateDomain(gomock.Any(), &nosqlplugin.DomainRow{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 3, PreviousFailoverVersion: 4, NotificationVersion: 5, FailoverEndTime: common.Ptr(time.Unix(2, 3)), LastUpdatedTime: time.Unix(1, 2), }).Return(nil).Times(1) }, request: &persistence.InternalUpdateDomainRequest{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 3, PreviousFailoverVersion: 4, NotificationVersion: 5, FailoverEndTime: common.Ptr(time.Unix(2, 3)), LastUpdatedTime: time.Unix(1, 2), }, expectError: false, }, { name: "common error", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(errors.New("test error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.InternalUpdateDomainRequest{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 3, PreviousFailoverVersion: 4, NotificationVersion: 5, FailoverEndTime: common.Ptr(time.Unix(2, 3)), LastUpdatedTime: time.Unix(1, 2), }, expectError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup mocks for each test case individually store, dbMock, _ := setUpMocksForDomainStore(t) // Setup test-specific mock behavior tc.setupMock(dbMock) // Execute the method under test err := store.UpdateDomain(context.Background(), tc.request) // Validate results if tc.expectError { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestGetDomain(t *testing.T) { testCases := []struct { name string setupMock func(*nosqlplugin.MockDB) request *persistence.GetDomainRequest expectError bool expected *persistence.InternalGetDomainResponse expectedError string }{ { name: "success by ID", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectDomain(gomock.Any(), common.Ptr("test-domain-id"), nil).Return(&nosqlplugin.DomainRow{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 3, PreviousFailoverVersion: 4, NotificationVersion: 5, FailoverEndTime: common.Ptr(time.Unix(2, 3)), LastUpdatedTime: time.Unix(1, 2), }, nil).Times(1) }, request: &persistence.GetDomainRequest{ID: "test-domain-id"}, expectError: false, expected: &persistence.InternalGetDomainResponse{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 3, PreviousFailoverVersion: 4, NotificationVersion: 5, FailoverEndTime: common.Ptr(time.Unix(2, 3)), LastUpdatedTime: time.Unix(1, 2), }, }, { name: "success by name", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectDomain(gomock.Any(), nil, common.Ptr("test-domain")).Return(&nosqlplugin.DomainRow{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 3, PreviousFailoverVersion: 4, NotificationVersion: 5, FailoverEndTime: common.Ptr(time.Unix(2, 3)), LastUpdatedTime: time.Unix(1, 2), }, nil).Times(1) }, request: &persistence.GetDomainRequest{Name: "test-domain"}, expectError: false, expected: &persistence.InternalGetDomainResponse{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfig(), ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 3, PreviousFailoverVersion: 4, NotificationVersion: 5, FailoverEndTime: common.Ptr(time.Unix(2, 3)), LastUpdatedTime: time.Unix(1, 2), }, }, { name: "success by name - active-active domain", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectDomain(gomock.Any(), nil, common.Ptr("test-domain")).Return(&nosqlplugin.DomainRow{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfigActiveActive(), ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 3, PreviousFailoverVersion: 4, NotificationVersion: 5, FailoverEndTime: common.Ptr(time.Unix(2, 3)), LastUpdatedTime: time.Unix(1, 2), }, nil).Times(1) }, request: &persistence.GetDomainRequest{Name: "test-domain"}, expectError: false, expected: &persistence.InternalGetDomainResponse{ Info: testFixtureDomainInfo(), Config: testFixtureInternalDomainConfig(), ReplicationConfig: testFixtureDomainReplicationConfigActiveActive(), ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 3, PreviousFailoverVersion: 4, NotificationVersion: 5, FailoverEndTime: common.Ptr(time.Unix(2, 3)), LastUpdatedTime: time.Unix(1, 2), }, }, { name: "bad request error - both id and domain are set", setupMock: func(dbMock *nosqlplugin.MockDB) { // No database call should be made }, request: &persistence.GetDomainRequest{ID: "test-id", Name: "test-name"}, expectError: true, expectedError: "GetDomain operation failed. Both ID and Name specified in request.", }, { name: "bad request error - both id and domain are empty", setupMock: func(dbMock *nosqlplugin.MockDB) { // No database call should be made }, request: &persistence.GetDomainRequest{}, expectError: true, expectedError: "GetDomain operation failed. Both ID and Name are empty.", }, { name: "not found error - by name", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectDomain(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("test error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.GetDomainRequest{Name: "test-domain"}, expectError: true, expectedError: "Domain test-domain does not exist.", }, { name: "not found error - by id", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectDomain(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("test error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.GetDomainRequest{ID: "test-domain-id"}, expectError: true, expectedError: "Domain test-domain-id does not exist.", }, { name: "generic db error", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectDomain(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("test error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(false).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.GetDomainRequest{ID: "test-domain-id"}, expectError: true, expectedError: "GetDomain failed. Error: test error ", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup mocks for each test case individually store, dbMock, _ := setUpMocksForDomainStore(t) // Setup test-specific mock behavior tc.setupMock(dbMock) // Execute the method under test resp, err := store.GetDomain(context.Background(), tc.request) // Validate results if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { require.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } func TestListDomains(t *testing.T) { testCases := []struct { name string setupMock func(*nosqlplugin.MockDB) request *persistence.ListDomainsRequest expectError bool expected *persistence.InternalListDomainsResponse }{ { name: "success", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectAllDomains(gomock.Any(), 2, []byte("token")).Return( []*nosqlplugin.DomainRow{ { Info: &persistence.DomainInfo{ID: "domain-id-1"}, ReplicationConfig: testFixtureDomainReplicationConfig(), }, { Info: &persistence.DomainInfo{ID: "active-active-domain-id"}, ReplicationConfig: testFixtureDomainReplicationConfigActiveActive(), }, }, []byte("next-token"), nil).Times(1) }, request: &persistence.ListDomainsRequest{ PageSize: 2, NextPageToken: []byte("token"), }, expectError: false, expected: &persistence.InternalListDomainsResponse{ Domains: []*persistence.InternalGetDomainResponse{ { Info: &persistence.DomainInfo{ID: "domain-id-1", Data: map[string]string{}}, ReplicationConfig: testFixtureDomainReplicationConfig(), }, { Info: &persistence.DomainInfo{ID: "active-active-domain-id", Data: map[string]string{}}, ReplicationConfig: testFixtureDomainReplicationConfigActiveActive(), }, }, NextPageToken: []byte("next-token"), }, }, { name: "common error", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectAllDomains(gomock.Any(), 10, []byte("token")).Return(nil, nil, errors.New("test error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.ListDomainsRequest{ PageSize: 10, NextPageToken: []byte("token"), }, expectError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup mocks for each test case individually store, dbMock, _ := setUpMocksForDomainStore(t) // Setup test-specific mock behavior tc.setupMock(dbMock) // Execute the method under test resp, err := store.ListDomains(context.Background(), tc.request) // Validate results if tc.expectError { assert.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } func TestGetMetadata(t *testing.T) { testCases := []struct { name string setupMock func(*nosqlplugin.MockDB) expectError bool expectedVersion int64 }{ { name: "success", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectDomainMetadata(gomock.Any()).Return(int64(10), nil).Times(1) }, expectError: false, expectedVersion: 10, }, { name: "common error", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().SelectDomainMetadata(gomock.Any()).Return(int64(0), errors.New("test error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, expectError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup mocks for each test case individually store, dbMock, _ := setUpMocksForDomainStore(t) // Setup test-specific mock behavior tc.setupMock(dbMock) // Execute the method under test resp, err := store.GetMetadata(context.Background()) // Validate results if tc.expectError { assert.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tc.expectedVersion, resp.NotificationVersion) } }) } } ================================================ FILE: common/persistence/nosql/nosql_execution_store.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "fmt" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/types" ) // Implements ExecutionStore type nosqlExecutionStore struct { shardID int nosqlStore taskSerializer serialization.TaskSerializer } // NewExecutionStore is used to create an instance of ExecutionStore implementation func NewExecutionStore( shardID int, db nosqlplugin.DB, logger log.Logger, taskSerializer serialization.TaskSerializer, dc *persistence.DynamicConfiguration, ) (persistence.ExecutionStore, error) { return &nosqlExecutionStore{ nosqlStore: nosqlStore{ logger: logger, db: db, dc: dc, }, shardID: shardID, taskSerializer: taskSerializer, }, nil } func (d *nosqlExecutionStore) GetShardID() int { return d.shardID } func (d *nosqlExecutionStore) CreateWorkflowExecution( ctx context.Context, request *persistence.InternalCreateWorkflowExecutionRequest, ) (*persistence.CreateWorkflowExecutionResponse, error) { newWorkflow := request.NewWorkflowSnapshot executionInfo := newWorkflow.ExecutionInfo lastWriteVersion := newWorkflow.LastWriteVersion domainID := executionInfo.DomainID workflowID := executionInfo.WorkflowID runID := executionInfo.RunID if err := persistence.ValidateCreateWorkflowModeState( request.Mode, newWorkflow, ); err != nil { return nil, err } workflowRequestWriteMode, err := getWorkflowRequestWriteMode(request.WorkflowRequestMode) if err != nil { return nil, err } currentWorkflowWriteReq, err := d.prepareCurrentWorkflowRequestForCreateWorkflowTxn(domainID, workflowID, runID, executionInfo, lastWriteVersion, request) if err != nil { return nil, err } workflowExecutionWriteReq, err := d.prepareCreateWorkflowExecutionRequestWithMaps(&newWorkflow, request.CurrentTimeStamp) if err != nil { return nil, err } tasksByCategory := map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask{} err = d.prepareNoSQLTasksForWorkflowTxn( domainID, workflowID, runID, newWorkflow.TasksByCategory, tasksByCategory, ) if err != nil { return nil, err } workflowRequests := d.prepareWorkflowRequestRows(domainID, workflowID, runID, newWorkflow.WorkflowRequests, nil) workflowRequestsWriteRequest := &nosqlplugin.WorkflowRequestsWriteRequest{ Rows: workflowRequests, WriteMode: workflowRequestWriteMode, } activeClusterSelectionPolicyRow := d.prepareActiveClusterSelectionPolicyRow(domainID, workflowID, runID, executionInfo.ActiveClusterSelectionPolicy) shardCondition := &nosqlplugin.ShardCondition{ ShardID: d.shardID, RangeID: request.RangeID, } err = d.db.InsertWorkflowExecutionWithTasks( ctx, workflowRequestsWriteRequest, currentWorkflowWriteReq, workflowExecutionWriteReq, tasksByCategory, activeClusterSelectionPolicyRow, shardCondition, ) if err != nil { conditionFailureErr, isConditionFailedError := err.(*nosqlplugin.WorkflowOperationConditionFailure) if isConditionFailedError { switch { case conditionFailureErr.UnknownConditionFailureDetails != nil: return nil, &persistence.ShardOwnershipLostError{ ShardID: d.shardID, Msg: *conditionFailureErr.UnknownConditionFailureDetails, } case conditionFailureErr.ShardRangeIDNotMatch != nil: return nil, &persistence.ShardOwnershipLostError{ ShardID: d.shardID, Msg: fmt.Sprintf("Failed to create workflow execution. Request RangeID: %v, Actual RangeID: %v", request.RangeID, *conditionFailureErr.ShardRangeIDNotMatch), } case conditionFailureErr.CurrentWorkflowConditionFailInfo != nil: return nil, &persistence.CurrentWorkflowConditionFailedError{ Msg: *conditionFailureErr.CurrentWorkflowConditionFailInfo, } case conditionFailureErr.WorkflowExecutionAlreadyExists != nil: return nil, &persistence.WorkflowExecutionAlreadyStartedError{ Msg: conditionFailureErr.WorkflowExecutionAlreadyExists.OtherInfo, StartRequestID: conditionFailureErr.WorkflowExecutionAlreadyExists.CreateRequestID, RunID: conditionFailureErr.WorkflowExecutionAlreadyExists.RunID, State: conditionFailureErr.WorkflowExecutionAlreadyExists.State, CloseStatus: conditionFailureErr.WorkflowExecutionAlreadyExists.CloseStatus, LastWriteVersion: conditionFailureErr.WorkflowExecutionAlreadyExists.LastWriteVersion, } case conditionFailureErr.DuplicateRequest != nil: return nil, &persistence.DuplicateRequestError{ RequestType: conditionFailureErr.DuplicateRequest.RequestType, RunID: conditionFailureErr.DuplicateRequest.RunID, } default: // If ever runs into this branch, there is bug in the code either in here, or in the implementation of nosql plugin err := fmt.Errorf("unsupported conditionFailureReason error") d.logger.Error("A code bug exists in persistence layer, please investigate ASAP", tag.Error(err)) return nil, err } } return nil, convertCommonErrors(d.db, "CreateWorkflowExecution", err) } return &persistence.CreateWorkflowExecutionResponse{}, nil } func (d *nosqlExecutionStore) GetWorkflowExecution( ctx context.Context, request *persistence.InternalGetWorkflowExecutionRequest, ) (*persistence.InternalGetWorkflowExecutionResponse, error) { execution := request.Execution state, err := d.db.SelectWorkflowExecution(ctx, d.shardID, request.DomainID, execution.WorkflowID, execution.RunID) if err != nil { if d.db.IsNotFoundError(err) { return nil, &types.EntityNotExistsError{ Message: fmt.Sprintf("Workflow execution not found. WorkflowId: %v, RunId: %v", execution.WorkflowID, execution.RunID), } } return nil, convertCommonErrors(d.db, "GetWorkflowExecution", err) } return &persistence.InternalGetWorkflowExecutionResponse{State: state}, nil } func (d *nosqlExecutionStore) UpdateWorkflowExecution( ctx context.Context, request *persistence.InternalUpdateWorkflowExecutionRequest, ) error { updateWorkflow := request.UpdateWorkflowMutation newWorkflow := request.NewWorkflowSnapshot executionInfo := updateWorkflow.ExecutionInfo domainID := executionInfo.DomainID workflowID := executionInfo.WorkflowID runID := executionInfo.RunID if err := persistence.ValidateUpdateWorkflowModeState( request.Mode, updateWorkflow, newWorkflow, ); err != nil { return err } workflowRequestWriteMode, err := getWorkflowRequestWriteMode(request.WorkflowRequestMode) if err != nil { return err } var currentWorkflowWriteReq *nosqlplugin.CurrentWorkflowWriteRequest switch request.Mode { case persistence.UpdateWorkflowModeIgnoreCurrent: currentWorkflowWriteReq = &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, } case persistence.UpdateWorkflowModeBypassCurrent: if err := d.assertNotCurrentExecution( ctx, domainID, workflowID, runID); err != nil { return err } currentWorkflowWriteReq = &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, } case persistence.UpdateWorkflowModeUpdateCurrent: if newWorkflow != nil { newExecutionInfo := newWorkflow.ExecutionInfo newLastWriteVersion := newWorkflow.LastWriteVersion newDomainID := newExecutionInfo.DomainID // TODO: ?? would it change at all ?? newWorkflowID := newExecutionInfo.WorkflowID newRunID := newExecutionInfo.RunID if domainID != newDomainID { return &types.InternalServiceError{ Message: "UpdateWorkflowExecution: cannot continue as new to another domain", } } currentWorkflowWriteReq = &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Row: nosqlplugin.CurrentWorkflowRow{ ShardID: d.shardID, DomainID: newDomainID, WorkflowID: newWorkflowID, RunID: newRunID, State: newExecutionInfo.State, CloseStatus: newExecutionInfo.CloseStatus, CreateRequestID: newExecutionInfo.CreateRequestID, LastWriteVersion: newLastWriteVersion, }, Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: &runID, }, } } else { lastWriteVersion := updateWorkflow.LastWriteVersion currentWorkflowWriteReq = &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Row: nosqlplugin.CurrentWorkflowRow{ ShardID: d.shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: executionInfo.State, CloseStatus: executionInfo.CloseStatus, CreateRequestID: executionInfo.CreateRequestID, LastWriteVersion: lastWriteVersion, }, Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: &runID, }, } } default: return &types.InternalServiceError{ Message: fmt.Sprintf("UpdateWorkflowExecution: unknown mode: %v", request.Mode), } } var mutateExecution, insertExecution *nosqlplugin.WorkflowExecutionRequest var activeClusterSelectionPolicyRow *nosqlplugin.ActiveClusterSelectionPolicyRow var workflowRequests []*nosqlplugin.WorkflowRequestRow tasksByCategory := map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask{} // 1. current mutateExecution, err = d.prepareUpdateWorkflowExecutionRequestWithMapsAndEventBuffer(&updateWorkflow, request.CurrentTimeStamp) if err != nil { return err } err = d.prepareNoSQLTasksForWorkflowTxn( domainID, workflowID, updateWorkflow.ExecutionInfo.RunID, updateWorkflow.TasksByCategory, tasksByCategory, ) if err != nil { return err } workflowRequests = d.prepareWorkflowRequestRows(domainID, workflowID, runID, updateWorkflow.WorkflowRequests, workflowRequests) // 2. new if newWorkflow != nil { insertExecution, err = d.prepareCreateWorkflowExecutionRequestWithMaps(newWorkflow, request.CurrentTimeStamp) if err != nil { return err } activeClusterSelectionPolicyRow = d.prepareActiveClusterSelectionPolicyRow( domainID, newWorkflow.ExecutionInfo.WorkflowID, newWorkflow.ExecutionInfo.RunID, newWorkflow.ExecutionInfo.ActiveClusterSelectionPolicy, ) err = d.prepareNoSQLTasksForWorkflowTxn( domainID, workflowID, newWorkflow.ExecutionInfo.RunID, newWorkflow.TasksByCategory, tasksByCategory, ) if err != nil { return err } workflowRequests = d.prepareWorkflowRequestRows(domainID, workflowID, newWorkflow.ExecutionInfo.RunID, newWorkflow.WorkflowRequests, workflowRequests) } shardCondition := &nosqlplugin.ShardCondition{ ShardID: d.shardID, RangeID: request.RangeID, } workflowRequestsWriteRequest := &nosqlplugin.WorkflowRequestsWriteRequest{ Rows: workflowRequests, WriteMode: workflowRequestWriteMode, } err = d.db.UpdateWorkflowExecutionWithTasks( ctx, workflowRequestsWriteRequest, currentWorkflowWriteReq, mutateExecution, insertExecution, activeClusterSelectionPolicyRow, nil, // no workflow to reset here tasksByCategory, shardCondition, ) return d.processUpdateWorkflowResult(err, request.RangeID) } func (d *nosqlExecutionStore) ConflictResolveWorkflowExecution( ctx context.Context, request *persistence.InternalConflictResolveWorkflowExecutionRequest, ) error { currentWorkflow := request.CurrentWorkflowMutation resetWorkflow := request.ResetWorkflowSnapshot newWorkflow := request.NewWorkflowSnapshot domainID := resetWorkflow.ExecutionInfo.DomainID workflowID := resetWorkflow.ExecutionInfo.WorkflowID if err := persistence.ValidateConflictResolveWorkflowModeState( request.Mode, resetWorkflow, newWorkflow, currentWorkflow, ); err != nil { return err } workflowRequestWriteMode, err := getWorkflowRequestWriteMode(request.WorkflowRequestMode) if err != nil { return err } var currentWorkflowWriteReq *nosqlplugin.CurrentWorkflowWriteRequest var prevRunID string switch request.Mode { case persistence.ConflictResolveWorkflowModeBypassCurrent: if err := d.assertNotCurrentExecution( ctx, domainID, workflowID, resetWorkflow.ExecutionInfo.RunID); err != nil { return err } currentWorkflowWriteReq = &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, } case persistence.ConflictResolveWorkflowModeUpdateCurrent: executionInfo := resetWorkflow.ExecutionInfo lastWriteVersion := resetWorkflow.LastWriteVersion if newWorkflow != nil { executionInfo = newWorkflow.ExecutionInfo lastWriteVersion = newWorkflow.LastWriteVersion } if currentWorkflow != nil { prevRunID = currentWorkflow.ExecutionInfo.RunID } else { // reset workflow is current prevRunID = resetWorkflow.ExecutionInfo.RunID } currentWorkflowWriteReq = &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Row: nosqlplugin.CurrentWorkflowRow{ ShardID: d.shardID, DomainID: domainID, WorkflowID: workflowID, RunID: executionInfo.RunID, State: executionInfo.State, CloseStatus: executionInfo.CloseStatus, CreateRequestID: executionInfo.CreateRequestID, LastWriteVersion: lastWriteVersion, }, Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: &prevRunID, }, } default: return &types.InternalServiceError{ Message: fmt.Sprintf("ConflictResolveWorkflowExecution: unknown mode: %v", request.Mode), } } var mutateExecution, insertExecution, resetExecution *nosqlplugin.WorkflowExecutionRequest var workflowRequests []*nosqlplugin.WorkflowRequestRow tasksByCategory := map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask{} // 1. current if currentWorkflow != nil { mutateExecution, err = d.prepareUpdateWorkflowExecutionRequestWithMapsAndEventBuffer(currentWorkflow, request.CurrentTimeStamp) if err != nil { return err } err = d.prepareNoSQLTasksForWorkflowTxn( domainID, workflowID, currentWorkflow.ExecutionInfo.RunID, currentWorkflow.TasksByCategory, tasksByCategory, ) if err != nil { return err } workflowRequests = d.prepareWorkflowRequestRows(domainID, workflowID, currentWorkflow.ExecutionInfo.RunID, currentWorkflow.WorkflowRequests, workflowRequests) } // 2. reset resetExecution, err = d.prepareResetWorkflowExecutionRequestWithMapsAndEventBuffer(&resetWorkflow, request.CurrentTimeStamp) if err != nil { return err } err = d.prepareNoSQLTasksForWorkflowTxn( domainID, workflowID, resetWorkflow.ExecutionInfo.RunID, resetWorkflow.TasksByCategory, tasksByCategory, ) if err != nil { return err } workflowRequests = d.prepareWorkflowRequestRows(domainID, workflowID, resetWorkflow.ExecutionInfo.RunID, resetWorkflow.WorkflowRequests, workflowRequests) // 3. new if newWorkflow != nil { insertExecution, err = d.prepareCreateWorkflowExecutionRequestWithMaps(newWorkflow, request.CurrentTimeStamp) if err != nil { return err } // TODO(active-active): make changes here to insert active cluster selection policy. err = d.prepareNoSQLTasksForWorkflowTxn( domainID, workflowID, newWorkflow.ExecutionInfo.RunID, newWorkflow.TasksByCategory, tasksByCategory, ) if err != nil { return err } workflowRequests = d.prepareWorkflowRequestRows(domainID, workflowID, newWorkflow.ExecutionInfo.RunID, newWorkflow.WorkflowRequests, workflowRequests) } shardCondition := &nosqlplugin.ShardCondition{ ShardID: d.shardID, RangeID: request.RangeID, } workflowRequestsWriteRequest := &nosqlplugin.WorkflowRequestsWriteRequest{ Rows: workflowRequests, WriteMode: workflowRequestWriteMode, } err = d.db.UpdateWorkflowExecutionWithTasks( ctx, workflowRequestsWriteRequest, currentWorkflowWriteReq, mutateExecution, insertExecution, nil, resetExecution, tasksByCategory, shardCondition) return d.processUpdateWorkflowResult(err, request.RangeID) } func (d *nosqlExecutionStore) DeleteWorkflowExecution( ctx context.Context, request *persistence.DeleteWorkflowExecutionRequest, ) error { err := d.db.DeleteWorkflowExecution(ctx, d.shardID, request.DomainID, request.WorkflowID, request.RunID) if err != nil { return convertCommonErrors(d.db, "DeleteWorkflowExecution", err) } return nil } func (d *nosqlExecutionStore) DeleteCurrentWorkflowExecution( ctx context.Context, request *persistence.DeleteCurrentWorkflowExecutionRequest, ) error { err := d.db.DeleteCurrentWorkflow(ctx, d.shardID, request.DomainID, request.WorkflowID, request.RunID) if err != nil { return convertCommonErrors(d.db, "DeleteCurrentWorkflowExecution", err) } return nil } func (d *nosqlExecutionStore) DeleteActiveClusterSelectionPolicy( ctx context.Context, domainID, workflowID, runID string, ) error { err := d.db.DeleteActiveClusterSelectionPolicy(ctx, d.shardID, domainID, workflowID, runID) if err != nil { return convertCommonErrors(d.db, "DeleteActiveClusterSelectionPolicy", err) } return nil } func (d *nosqlExecutionStore) GetCurrentExecution( ctx context.Context, request *persistence.GetCurrentExecutionRequest, ) (*persistence.GetCurrentExecutionResponse, error) { result, err := d.db.SelectCurrentWorkflow(ctx, d.shardID, request.DomainID, request.WorkflowID) if err != nil { if d.db.IsNotFoundError(err) { return nil, &types.EntityNotExistsError{ Message: fmt.Sprintf("Workflow execution not found. WorkflowId: %v", request.WorkflowID), } } return nil, convertCommonErrors(d.db, "GetCurrentExecution", err) } return &persistence.GetCurrentExecutionResponse{ RunID: result.RunID, StartRequestID: result.CreateRequestID, State: result.State, CloseStatus: result.CloseStatus, LastWriteVersion: result.LastWriteVersion, }, nil } func (d *nosqlExecutionStore) ListCurrentExecutions( ctx context.Context, request *persistence.ListCurrentExecutionsRequest, ) (*persistence.ListCurrentExecutionsResponse, error) { executions, token, err := d.db.SelectAllCurrentWorkflows(ctx, d.shardID, request.PageToken, request.PageSize) if err != nil { return nil, convertCommonErrors(d.db, "ListCurrentExecutions", err) } return &persistence.ListCurrentExecutionsResponse{ Executions: executions, PageToken: token, }, nil } func (d *nosqlExecutionStore) IsWorkflowExecutionExists( ctx context.Context, request *persistence.IsWorkflowExecutionExistsRequest, ) (*persistence.IsWorkflowExecutionExistsResponse, error) { exists, err := d.db.IsWorkflowExecutionExists(ctx, d.shardID, request.DomainID, request.WorkflowID, request.RunID) if err != nil { return nil, convertCommonErrors(d.db, "IsWorkflowExecutionExists", err) } return &persistence.IsWorkflowExecutionExistsResponse{ Exists: exists, }, nil } func (d *nosqlExecutionStore) ListConcreteExecutions( ctx context.Context, request *persistence.ListConcreteExecutionsRequest, ) (*persistence.InternalListConcreteExecutionsResponse, error) { executions, nextPageToken, err := d.db.SelectAllWorkflowExecutions(ctx, d.shardID, request.PageToken, request.PageSize) if err != nil { return nil, convertCommonErrors(d.db, "ListConcreteExecutions", err) } return &persistence.InternalListConcreteExecutionsResponse{ Executions: executions, NextPageToken: nextPageToken, }, nil } func (d *nosqlExecutionStore) PutReplicationTaskToDLQ( ctx context.Context, request *persistence.InternalPutReplicationTaskToDLQRequest, ) error { err := d.db.InsertReplicationDLQTask(ctx, d.shardID, request.SourceClusterName, &nosqlplugin.HistoryMigrationTask{ Replication: request.TaskInfo, Task: nil, // TODO: encode task infor into datablob }) if err != nil { return convertCommonErrors(d.db, "PutReplicationTaskToDLQ", err) } return nil } func (d *nosqlExecutionStore) GetReplicationTasksFromDLQ( ctx context.Context, request *persistence.GetReplicationTasksFromDLQRequest, ) (*persistence.GetHistoryTasksResponse, error) { if request.ReadLevel > request.MaxReadLevel { return nil, &types.BadRequestError{Message: "ReadLevel cannot be higher than MaxReadLevel"} } tasks, nextPageToken, err := d.db.SelectReplicationDLQTasksOrderByTaskID(ctx, d.shardID, request.SourceClusterName, request.BatchSize, request.NextPageToken, request.ReadLevel, request.MaxReadLevel) if err != nil { return nil, convertCommonErrors(d.db, "GetReplicationTasksFromDLQ", err) } var tTasks []persistence.Task for _, t := range tasks { task, err := t.Replication.ToTask() if err != nil { return nil, convertCommonErrors(d.db, "GetReplicationTasksFromDLQ", err) } tTasks = append(tTasks, task) } return &persistence.GetHistoryTasksResponse{ Tasks: tTasks, NextPageToken: nextPageToken, }, nil } func (d *nosqlExecutionStore) GetReplicationDLQSize( ctx context.Context, request *persistence.GetReplicationDLQSizeRequest, ) (*persistence.GetReplicationDLQSizeResponse, error) { size, err := d.db.SelectReplicationDLQTasksCount(ctx, d.shardID, request.SourceClusterName) if err != nil { return nil, convertCommonErrors(d.db, "GetReplicationDLQSize", err) } return &persistence.GetReplicationDLQSizeResponse{ Size: size, }, nil } func (d *nosqlExecutionStore) DeleteReplicationTaskFromDLQ( ctx context.Context, request *persistence.DeleteReplicationTaskFromDLQRequest, ) error { err := d.db.DeleteReplicationDLQTask(ctx, d.shardID, request.SourceClusterName, request.TaskID) if err != nil { return convertCommonErrors(d.db, "DeleteReplicationTaskFromDLQ", err) } return nil } func (d *nosqlExecutionStore) RangeDeleteReplicationTaskFromDLQ( ctx context.Context, request *persistence.RangeDeleteReplicationTaskFromDLQRequest, ) (*persistence.RangeDeleteReplicationTaskFromDLQResponse, error) { err := d.db.RangeDeleteReplicationDLQTasks(ctx, d.shardID, request.SourceClusterName, request.InclusiveBeginTaskID, request.ExclusiveEndTaskID) if err != nil { return nil, convertCommonErrors(d.db, "RangeDeleteReplicationTaskFromDLQ", err) } return &persistence.RangeDeleteReplicationTaskFromDLQResponse{TasksCompleted: persistence.UnknownNumRowsAffected}, nil } func (d *nosqlExecutionStore) CreateFailoverMarkerTasks( ctx context.Context, request *persistence.CreateFailoverMarkersRequest, ) error { var nosqlTasks []*nosqlplugin.HistoryMigrationTask for i, task := range request.Markers { ts := []persistence.Task{task} tasks, err := d.prepareReplicationTasksForWorkflowTxn(task.DomainID, rowTypeReplicationWorkflowID, rowTypeReplicationRunID, ts) if err != nil { return err } tasks[i].Replication.CurrentTimeStamp = request.CurrentTimeStamp nosqlTasks = append(nosqlTasks, tasks...) } err := d.db.InsertReplicationTask(ctx, nosqlTasks, nosqlplugin.ShardCondition{ ShardID: d.shardID, RangeID: request.RangeID, }) if err != nil { conditionFailureErr, isConditionFailedError := err.(*nosqlplugin.ShardOperationConditionFailure) if isConditionFailedError { return &persistence.ShardOwnershipLostError{ ShardID: d.shardID, Msg: fmt.Sprintf("Failed to create workflow execution. Request RangeID: %v, columns: (%v)", conditionFailureErr.RangeID, conditionFailureErr.Details), } } } return nil } func (d *nosqlExecutionStore) GetHistoryTasks( ctx context.Context, request *persistence.GetHistoryTasksRequest, ) (*persistence.GetHistoryTasksResponse, error) { switch request.TaskCategory.Type() { case persistence.HistoryTaskCategoryTypeImmediate: return d.getImmediateHistoryTasks(ctx, request) case persistence.HistoryTaskCategoryTypeScheduled: return d.getScheduledHistoryTasks(ctx, request) default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category type: %v", request.TaskCategory.Type())} } } func (d *nosqlExecutionStore) getImmediateHistoryTasks( ctx context.Context, request *persistence.GetHistoryTasksRequest, ) (*persistence.GetHistoryTasksResponse, error) { switch request.TaskCategory.ID() { case persistence.HistoryTaskCategoryIDTransfer: tasks, nextPageToken, err := d.db.SelectTransferTasksOrderByTaskID(ctx, d.shardID, request.PageSize, request.NextPageToken, request.InclusiveMinTaskKey.GetTaskID(), request.ExclusiveMaxTaskKey.GetTaskID()) if err != nil { return nil, convertCommonErrors(d.db, "GetImmediateHistoryTasks", err) } tTasks := make([]persistence.Task, 0, len(tasks)) for _, t := range tasks { if d.dc.ReadNoSQLHistoryTaskFromDataBlob() && t.Task != nil { task, err := d.taskSerializer.DeserializeTask(request.TaskCategory, t.Task) if err != nil { return nil, convertCommonErrors(d.db, "GetImmediateHistoryTasks", err) } task.SetTaskID(t.TaskID) tTasks = append(tTasks, task) } else { task, err := t.Transfer.ToTask() if err != nil { return nil, convertCommonErrors(d.db, "GetImmediateHistoryTasks", err) } tTasks = append(tTasks, task) } } return &persistence.GetHistoryTasksResponse{ Tasks: tTasks, NextPageToken: nextPageToken, }, nil case persistence.HistoryTaskCategoryIDReplication: tasks, nextPageToken, err := d.db.SelectReplicationTasksOrderByTaskID(ctx, d.shardID, request.PageSize, request.NextPageToken, request.InclusiveMinTaskKey.GetTaskID(), request.ExclusiveMaxTaskKey.GetTaskID()) if err != nil { return nil, convertCommonErrors(d.db, "GetImmediateHistoryTasks", err) } tTasks := make([]persistence.Task, 0, len(tasks)) for _, t := range tasks { if d.dc.ReadNoSQLHistoryTaskFromDataBlob() && t.Task != nil { task, err := d.taskSerializer.DeserializeTask(request.TaskCategory, t.Task) if err != nil { return nil, convertCommonErrors(d.db, "GetImmediateHistoryTasks", err) } task.SetTaskID(t.TaskID) tTasks = append(tTasks, task) } else { task, err := t.Replication.ToTask() if err != nil { return nil, convertCommonErrors(d.db, "GetImmediateHistoryTasks", err) } tTasks = append(tTasks, task) } } return &persistence.GetHistoryTasksResponse{ Tasks: tTasks, NextPageToken: nextPageToken, }, nil default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category: %v", request.TaskCategory.ID())} } } func (d *nosqlExecutionStore) getScheduledHistoryTasks( ctx context.Context, request *persistence.GetHistoryTasksRequest, ) (*persistence.GetHistoryTasksResponse, error) { switch request.TaskCategory.ID() { case persistence.HistoryTaskCategoryIDTimer: timers, nextPageToken, err := d.db.SelectTimerTasksOrderByVisibilityTime(ctx, d.shardID, request.PageSize, request.NextPageToken, request.InclusiveMinTaskKey.GetScheduledTime(), request.ExclusiveMaxTaskKey.GetScheduledTime()) if err != nil { return nil, convertCommonErrors(d.db, "GetScheduledHistoryTasks", err) } tTasks := make([]persistence.Task, 0, len(timers)) for _, t := range timers { if d.dc.ReadNoSQLHistoryTaskFromDataBlob() && t.Task != nil { task, err := d.taskSerializer.DeserializeTask(request.TaskCategory, t.Task) if err != nil { return nil, convertCommonErrors(d.db, "GetScheduledHistoryTasks", err) } task.SetTaskID(t.TaskID) task.SetVisibilityTimestamp(t.ScheduledTime) tTasks = append(tTasks, task) } else { task, err := t.Timer.ToTask() if err != nil { return nil, convertCommonErrors(d.db, "GetScheduledHistoryTasks", err) } tTasks = append(tTasks, task) } } return &persistence.GetHistoryTasksResponse{ Tasks: tTasks, NextPageToken: nextPageToken, }, nil default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category: %v", request.TaskCategory.ID())} } } func (d *nosqlExecutionStore) CompleteHistoryTask( ctx context.Context, request *persistence.CompleteHistoryTaskRequest, ) error { switch request.TaskCategory.Type() { case persistence.HistoryTaskCategoryTypeScheduled: return d.completeScheduledHistoryTask(ctx, request) case persistence.HistoryTaskCategoryTypeImmediate: return d.completeImmediateHistoryTask(ctx, request) default: return &types.BadRequestError{Message: fmt.Sprintf("Unknown task category type: %v", request.TaskCategory.Type())} } } func (d *nosqlExecutionStore) completeScheduledHistoryTask( ctx context.Context, request *persistence.CompleteHistoryTaskRequest, ) error { switch request.TaskCategory.ID() { case persistence.HistoryTaskCategoryIDTimer: err := d.db.DeleteTimerTask(ctx, d.shardID, request.TaskKey.GetTaskID(), request.TaskKey.GetScheduledTime()) if err != nil { return convertCommonErrors(d.db, "CompleteScheduledHistoryTask", err) } return nil default: return &types.BadRequestError{Message: fmt.Sprintf("Unknown task category ID: %v", request.TaskCategory.ID())} } } func (d *nosqlExecutionStore) completeImmediateHistoryTask( ctx context.Context, request *persistence.CompleteHistoryTaskRequest, ) error { switch request.TaskCategory.ID() { case persistence.HistoryTaskCategoryIDTransfer: err := d.db.DeleteTransferTask(ctx, d.shardID, request.TaskKey.GetTaskID()) if err != nil { return convertCommonErrors(d.db, "CompleteImmediateHistoryTask", err) } return nil case persistence.HistoryTaskCategoryIDReplication: err := d.db.DeleteReplicationTask(ctx, d.shardID, request.TaskKey.GetTaskID()) if err != nil { return convertCommonErrors(d.db, "CompleteImmediateHistoryTask", err) } return nil default: return &types.BadRequestError{Message: fmt.Sprintf("Unknown task category ID: %v", request.TaskCategory.ID())} } } func (d *nosqlExecutionStore) RangeCompleteHistoryTask( ctx context.Context, request *persistence.RangeCompleteHistoryTaskRequest, ) (*persistence.RangeCompleteHistoryTaskResponse, error) { switch request.TaskCategory.Type() { case persistence.HistoryTaskCategoryTypeScheduled: return d.rangeCompleteScheduledHistoryTask(ctx, request) case persistence.HistoryTaskCategoryTypeImmediate: return d.rangeCompleteImmediateHistoryTask(ctx, request) default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category type: %v", request.TaskCategory.Type())} } } func (d *nosqlExecutionStore) rangeCompleteScheduledHistoryTask( ctx context.Context, request *persistence.RangeCompleteHistoryTaskRequest, ) (*persistence.RangeCompleteHistoryTaskResponse, error) { switch request.TaskCategory.ID() { case persistence.HistoryTaskCategoryIDTimer: err := d.db.RangeDeleteTimerTasks(ctx, d.shardID, request.InclusiveMinTaskKey.GetScheduledTime(), request.ExclusiveMaxTaskKey.GetScheduledTime()) if err != nil { return nil, convertCommonErrors(d.db, "RangeCompleteTimerTask", err) } default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category: %v", request.TaskCategory.ID())} } return &persistence.RangeCompleteHistoryTaskResponse{TasksCompleted: persistence.UnknownNumRowsAffected}, nil } func (d *nosqlExecutionStore) rangeCompleteImmediateHistoryTask( ctx context.Context, request *persistence.RangeCompleteHistoryTaskRequest, ) (*persistence.RangeCompleteHistoryTaskResponse, error) { switch request.TaskCategory.ID() { case persistence.HistoryTaskCategoryIDTransfer: err := d.db.RangeDeleteTransferTasks(ctx, d.shardID, request.InclusiveMinTaskKey.GetTaskID(), request.ExclusiveMaxTaskKey.GetTaskID()) if err != nil { return nil, convertCommonErrors(d.db, "RangeCompleteTransferTask", err) } case persistence.HistoryTaskCategoryIDReplication: err := d.db.RangeDeleteReplicationTasks(ctx, d.shardID, request.ExclusiveMaxTaskKey.GetTaskID()) if err != nil { return nil, convertCommonErrors(d.db, "RangeCompleteReplicationTask", err) } default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category: %v", request.TaskCategory.ID())} } return &persistence.RangeCompleteHistoryTaskResponse{TasksCompleted: persistence.UnknownNumRowsAffected}, nil } func (d *nosqlExecutionStore) GetActiveClusterSelectionPolicy( ctx context.Context, domainID, wfID, rID string, ) (*persistence.DataBlob, error) { row, err := d.db.SelectActiveClusterSelectionPolicy(ctx, d.shardID, domainID, wfID, rID) if err != nil { if d.db.IsNotFoundError(err) { return nil, &types.EntityNotExistsError{ Message: fmt.Sprintf("Active cluster selection policy not found. DomainId: %v, WorkflowId: %v, RunId: %v", domainID, wfID, rID), } } return nil, convertCommonErrors(d.db, "GetActiveClusterSelectionPolicy", err) } if row == nil { return nil, nil } return row.Policy, nil } ================================================ FILE: common/persistence/nosql/nosql_execution_store_test.go ================================================ // Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "errors" "fmt" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" ) func TestCreateWorkflowExecution(t *testing.T) { ctx := context.Background() tests := []struct { name string setupMock func(*nosqlplugin.MockDB, int) // Now accepts shard ID as parameter expectedResp *persistence.CreateWorkflowExecutionResponse expectedError error }{ { name: "success", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(nil) }, expectedResp: &persistence.CreateWorkflowExecutionResponse{}, expectedError: nil, }, { name: "ShardRangeIDNotMatch condition failure", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { err := &nosqlplugin.WorkflowOperationConditionFailure{ ShardRangeIDNotMatch: common.Int64Ptr(456), } mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(err) }, expectedResp: nil, // No response expected on error expectedError: &persistence.ShardOwnershipLostError{ ShardID: 123, Msg: "Failed to create workflow execution. Request RangeID: 123, Actual RangeID: 456", }, }, { name: "WorkflowExecutionAlreadyExists condition failure", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { err := &nosqlplugin.WorkflowOperationConditionFailure{ WorkflowExecutionAlreadyExists: &nosqlplugin.WorkflowExecutionAlreadyExists{ OtherInfo: "Workflow with ID already exists", CreateRequestID: "existing-request-id", RunID: "existing-run-id", State: persistence.WorkflowStateCompleted, CloseStatus: persistence.WorkflowCloseStatusCompleted, LastWriteVersion: 123, }, } mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(err) }, expectedResp: nil, // No response expected on error expectedError: &persistence.WorkflowExecutionAlreadyStartedError{ Msg: "Workflow with ID already exists", StartRequestID: "existing-request-id", RunID: "existing-run-id", State: persistence.WorkflowStateCompleted, CloseStatus: persistence.WorkflowCloseStatusCompleted, LastWriteVersion: 123, }, }, { name: "Unknown condition failure", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { err := &nosqlplugin.WorkflowOperationConditionFailure{ UnknownConditionFailureDetails: common.StringPtr("Unknown error occurred during operation"), } mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(err) }, expectedResp: nil, expectedError: &persistence.ShardOwnershipLostError{ ShardID: 123, Msg: "Unknown error occurred during operation", }, }, { name: "Current workflow condition failure", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { err := &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: common.StringPtr("Current workflow condition failed"), } mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(err) }, expectedResp: nil, expectedError: &persistence.CurrentWorkflowConditionFailedError{ Msg: "Current workflow condition failed", }, }, { name: "Unexpected error type leading to unsupported condition failure", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { // Simulate returning an unexpected error type from the mock unexpectedErr := errors.New("unexpected error type") mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(unexpectedErr) }, expectedResp: nil, expectedError: fmt.Errorf("unsupported conditionFailureReason error"), // Expected generic error for unexpected conditions }, { name: "Duplicate request error", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&nosqlplugin.WorkflowOperationConditionFailure{ DuplicateRequest: &nosqlplugin.DuplicateRequest{ RequestType: persistence.WorkflowRequestTypeSignal, RunID: "test-run-id", }, }) }, expectedResp: nil, expectedError: &persistence.DuplicateRequestError{ RequestType: persistence.WorkflowRequestTypeSignal, RunID: "test-run-id", }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(controller) store := newTestNosqlExecutionStore(mockDB, log.NewNoop()) shardID := store.GetShardID() tc.setupMock(mockDB, shardID) resp, err := store.CreateWorkflowExecution(ctx, newCreateWorkflowExecutionRequest()) if diff := cmp.Diff(tc.expectedResp, resp); diff != "" { t.Errorf("CreateWorkflowExecution() response mismatch (-want +got):\n%s", diff) } if tc.expectedError != nil { require.ErrorAs(t, err, &tc.expectedError) } else { require.NoError(t, err) } }) } } func TestUpdateWorkflowExecution(t *testing.T) { ctx := context.Background() tests := []struct { name string setupMock func(*nosqlplugin.MockDB, int) request func() *persistence.InternalUpdateWorkflowExecutionRequest expectedError error }{ { name: "success", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { mockDB.EXPECT(). UpdateWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), nil, gomock.Any(), gomock.Any()). Return(nil) }, request: newUpdateWorkflowExecutionRequest, expectedError: nil, }, { name: "Success - UpdateWorkflowModeIgnoreCurrent", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { mockDB.EXPECT(). UpdateWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), nil, gomock.Any(), gomock.Any()). Return(nil) }, request: func() *persistence.InternalUpdateWorkflowExecutionRequest { req := newUpdateWorkflowExecutionRequest() req.Mode = persistence.UpdateWorkflowModeIgnoreCurrent return req }, expectedError: nil, }, { name: "Duplicate request error", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { mockDB.EXPECT(). UpdateWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), nil, gomock.Any(), gomock.Any()). Return(&nosqlplugin.WorkflowOperationConditionFailure{ DuplicateRequest: &nosqlplugin.DuplicateRequest{ RequestType: persistence.WorkflowRequestTypeSignal, RunID: "test-run-id", }, }) }, request: newUpdateWorkflowExecutionRequest, expectedError: &persistence.DuplicateRequestError{ RequestType: persistence.WorkflowRequestTypeSignal, RunID: "test-run-id", }, }, { name: "UpdateWorkflowModeBypassCurrent - assertNotCurrentExecution failure", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { mockDB.EXPECT(). UpdateWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), nil, gomock.Any(), gomock.Any()). Times(0) }, request: func() *persistence.InternalUpdateWorkflowExecutionRequest { req := newUpdateWorkflowExecutionRequest() req.Mode = persistence.UpdateWorkflowModeBypassCurrent return req }, expectedError: &types.InternalServiceError{Message: "assertNotCurrentExecution failure"}, }, { name: "Unknown update mode", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { }, request: func() *persistence.InternalUpdateWorkflowExecutionRequest { req := newUpdateWorkflowExecutionRequest() req.Mode = -1 return req }, expectedError: &types.InternalServiceError{Message: "UpdateWorkflowExecution: unknown mode: -1"}, }, { name: "Bypass_current_execution_failure_due_to_assertNotCurrentExecution", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) {}, request: func() *persistence.InternalUpdateWorkflowExecutionRequest { req := newUpdateWorkflowExecutionRequest() req.Mode = persistence.UpdateWorkflowModeBypassCurrent return req }, expectedError: &types.InternalServiceError{Message: "assertNotCurrentExecution failure"}, }, { name: "Update with new snapshot (continue as new)", setupMock: func(mockDB *nosqlplugin.MockDB, shardID int) { // validate the NewWorkflowSnapshot's info is used to create the active cluster selection policy row info := newUpdateWorkflowExecutionRequestForContinueAsNew().NewWorkflowSnapshot.ExecutionInfo plcy := &nosqlplugin.ActiveClusterSelectionPolicyRow{ ShardID: shardID, DomainID: info.DomainID, WorkflowID: info.WorkflowID, RunID: info.RunID, Policy: info.ActiveClusterSelectionPolicy, } mockDB.EXPECT(). UpdateWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Not(nil), plcy, nil, gomock.Any(), gomock.Any()). Return(nil).Times(1) }, request: func() *persistence.InternalUpdateWorkflowExecutionRequest { req := newUpdateWorkflowExecutionRequestForContinueAsNew() return req }, expectedError: nil, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(controller) mockTaskSerializer := serialization.NewMockTaskSerializer(controller) store, _ := NewExecutionStore(1, mockDB, log.NewNoop(), mockTaskSerializer, &persistence.DynamicConfiguration{ EnableHistoryTaskDualWriteMode: func(...dynamicproperties.FilterOption) bool { return false }, }) tc.setupMock(mockDB, 1) req := tc.request() err := store.UpdateWorkflowExecution(ctx, req) if tc.expectedError != nil { require.Error(t, err) require.IsType(t, tc.expectedError, err, "Error type does not match the expected one.") } else { require.NoError(t, err) } }) } } func TestNosqlExecutionStore(t *testing.T) { ctx := context.Background() shardID := 1 testCases := []struct { name string setupMock func(*gomock.Controller) *nosqlExecutionStore testFunc func(*nosqlExecutionStore) error expectedError error }{ { name: "CreateWorkflowExecution success", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(nil) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.CreateWorkflowExecution(ctx, newCreateWorkflowExecutionRequest()) return err }, expectedError: nil, }, { name: "CreateWorkflowExecution failure - workflow already exists", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&persistence.WorkflowExecutionAlreadyStartedError{}).Times(1) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsDBUnavailableError(gomock.Any()).Return(false).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.CreateWorkflowExecution(ctx, newCreateWorkflowExecutionRequest()) return err }, expectedError: &persistence.WorkflowExecutionAlreadyStartedError{}, }, { name: "CreateWorkflowExecution failure - shard ownership lost", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&persistence.ShardOwnershipLostError{ShardID: shardID, Msg: "shard ownership lost"}).Times(1) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsDBUnavailableError(gomock.Any()).Return(false).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.CreateWorkflowExecution(ctx, newCreateWorkflowExecutionRequest()) return err }, expectedError: &persistence.ShardOwnershipLostError{}, }, { name: "CreateWorkflowExecution failure - current workflow condition failed", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&persistence.CurrentWorkflowConditionFailedError{Msg: "current workflow condition failed"}).Times(1) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsDBUnavailableError(gomock.Any()).Return(false).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.CreateWorkflowExecution(ctx, newCreateWorkflowExecutionRequest()) return err }, expectedError: &persistence.CurrentWorkflowConditionFailedError{}, }, { name: "CreateWorkflowExecution failure - generic internal service error", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.InternalServiceError{Message: "generic internal service error"}).Times(1) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsDBUnavailableError(gomock.Any()).Return(false).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.CreateWorkflowExecution(ctx, newCreateWorkflowExecutionRequest()) return err }, expectedError: &types.InternalServiceError{}, }, { name: "CreateWorkflowExecution failure - duplicate request error", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). InsertWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&nosqlplugin.WorkflowOperationConditionFailure{ DuplicateRequest: &nosqlplugin.DuplicateRequest{ RunID: "abc", }, }).Times(1) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsDBUnavailableError(gomock.Any()).Return(false).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.CreateWorkflowExecution(ctx, newCreateWorkflowExecutionRequest()) return err }, expectedError: &persistence.DuplicateRequestError{}, }, { name: "GetWorkflowExecution success", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). SelectWorkflowExecution(ctx, shardID, gomock.Any(), gomock.Any(), gomock.Any()). Return(&nosqlplugin.WorkflowExecution{}, nil).Times(1) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.GetWorkflowExecution(ctx, newGetWorkflowExecutionRequest()) return err }, expectedError: nil, }, { name: "GetWorkflowExecution failure - not found", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). SelectWorkflowExecution(ctx, shardID, gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, &types.EntityNotExistsError{}).Times(1) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.GetWorkflowExecution(ctx, newGetWorkflowExecutionRequest()) return err }, expectedError: &types.EntityNotExistsError{}, }, { name: "UpdateWorkflowExecution success", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). UpdateWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Nil(), gomock.Any(), gomock.Any()). Return(nil).Times(1) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { err := store.UpdateWorkflowExecution(ctx, newUpdateWorkflowExecutionRequest()) return err }, expectedError: nil, }, { name: "UpdateWorkflowExecution failure - invalid update mode", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) // No operation expected on the DB due to invalid mode return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { request := newUpdateWorkflowExecutionRequest() request.Mode = persistence.UpdateWorkflowMode(-1) return store.UpdateWorkflowExecution(ctx, request) }, expectedError: &types.InternalServiceError{}, }, { name: "UpdateWorkflowExecution failure - condition not met", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) conditionFailure := &nosqlplugin.WorkflowOperationConditionFailure{ UnknownConditionFailureDetails: common.StringPtr("condition not met"), } mockDB.EXPECT(). UpdateWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Nil(), gomock.Any(), gomock.Any()). Return(conditionFailure).Times(1) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { return store.UpdateWorkflowExecution(ctx, newUpdateWorkflowExecutionRequest()) }, expectedError: &persistence.ConditionFailedError{}, }, { name: "UpdateWorkflowExecution failure - operational error", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). UpdateWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Nil(), gomock.Any(), gomock.Any()). Return(errors.New("database is unavailable")).Times(1) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { return store.UpdateWorkflowExecution(ctx, newUpdateWorkflowExecutionRequest()) }, expectedError: &types.InternalServiceError{Message: "database is unavailable"}, }, { name: "DeleteWorkflowExecution success", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). DeleteWorkflowExecution(ctx, shardID, gomock.Any(), gomock.Any(), gomock.Any()). Return(nil) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { return store.DeleteWorkflowExecution(ctx, &persistence.DeleteWorkflowExecutionRequest{ DomainID: "domainID", WorkflowID: "workflowID", RunID: "runID", }) }, expectedError: nil, }, { name: "DeleteWorkflowExecution failure - workflow does not exist", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). DeleteWorkflowExecution(ctx, shardID, gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.EntityNotExistsError{Message: "workflow does not exist"}) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { return store.DeleteWorkflowExecution(ctx, &persistence.DeleteWorkflowExecutionRequest{ DomainID: "domainID", WorkflowID: "workflowID", RunID: "runID", }) }, expectedError: &types.EntityNotExistsError{Message: "workflow does not exist"}, }, { name: "DeleteCurrentWorkflowExecution success", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). DeleteCurrentWorkflow(ctx, shardID, gomock.Any(), gomock.Any(), gomock.Any()). Return(nil) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { return store.DeleteCurrentWorkflowExecution(ctx, &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: "domainID", WorkflowID: "workflowID", RunID: "runID", }) }, expectedError: nil, }, { name: "DeleteCurrentWorkflowExecution failure - current workflow does not exist", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). DeleteCurrentWorkflow(ctx, shardID, gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.EntityNotExistsError{Message: "current workflow does not exist"}) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { return store.DeleteCurrentWorkflowExecution(ctx, &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: "domainID", WorkflowID: "workflowID", RunID: "runID", }) }, expectedError: &types.EntityNotExistsError{Message: "current workflow does not exist"}, }, { name: "ListCurrentExecutions success", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). SelectAllCurrentWorkflows(ctx, shardID, gomock.Any(), gomock.Any()). Return([]*persistence.CurrentWorkflowExecution{}, nil, nil) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.ListCurrentExecutions(ctx, &persistence.ListCurrentExecutionsRequest{}) return err }, expectedError: nil, }, { name: "ListCurrentExecutions failure - database error", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). SelectAllCurrentWorkflows(ctx, shardID, gomock.Any(), gomock.Any()). Return(nil, nil, errors.New("database error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.ListCurrentExecutions(ctx, &persistence.ListCurrentExecutionsRequest{}) return err }, expectedError: &types.InternalServiceError{Message: "database error"}, }, { name: "ListConcreteExecutions success - has executions", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) // Corrected return value type to match expected method signature executions := []*persistence.InternalListConcreteExecutionsEntity{ { ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ WorkflowID: "workflowID", RunID: "runID", }, }, } mockDB.EXPECT(). SelectAllWorkflowExecutions(ctx, shardID, gomock.Any(), gomock.Any()). Return(executions, nil, nil) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { resp, err := store.ListConcreteExecutions(ctx, &persistence.ListConcreteExecutionsRequest{}) if err != nil { return err } if len(resp.Executions) == 0 { return errors.New("expected to find executions") } return nil }, expectedError: nil, }, { name: "ListConcreteExecutions failure - database error", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). SelectAllWorkflowExecutions(ctx, shardID, gomock.Any(), gomock.Any()). Return(nil, nil, errors.New("database error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.ListConcreteExecutions(ctx, &persistence.ListConcreteExecutionsRequest{}) return err }, expectedError: &types.InternalServiceError{Message: "database error"}, }, { name: "PutReplicationTaskToDLQ success", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) replicationTaskInfo := newInternalReplicationTaskInfo() mockDB.EXPECT(). InsertReplicationDLQTask(ctx, shardID, "sourceCluster", &nosqlplugin.HistoryMigrationTask{ Replication: &replicationTaskInfo, Task: nil, }).Return(nil) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { taskInfo := newInternalReplicationTaskInfo() return store.PutReplicationTaskToDLQ(ctx, &persistence.InternalPutReplicationTaskToDLQRequest{ SourceClusterName: "sourceCluster", TaskInfo: &taskInfo, }) }, expectedError: nil, }, { name: "GetReplicationTasksFromDLQ success", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) nextPageToken := []byte("next-page-token") replicationTasks := []*nosqlplugin.HistoryMigrationTask{} mockDB.EXPECT(). SelectReplicationDLQTasksOrderByTaskID( ctx, shardID, "sourceCluster", 10, gomock.Any(), int64(0), int64(100), ).Return(replicationTasks, nextPageToken, nil) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { initialNextPageToken := []byte{} _, err := store.GetReplicationTasksFromDLQ(ctx, &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: "sourceCluster", BatchSize: 10, NextPageToken: initialNextPageToken, ReadLevel: 0, MaxReadLevel: 100, }) return err }, expectedError: nil, }, { name: "GetReplicationTasksFromDLQ failure - invalid read levels", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { return newTestNosqlExecutionStore(nosqlplugin.NewMockDB(ctrl), log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.GetReplicationTasksFromDLQ(ctx, &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: "sourceCluster", ReadLevel: 100, MaxReadLevel: 50, BatchSize: 10, NextPageToken: []byte{}, }) return err }, expectedError: &types.BadRequestError{Message: "ReadLevel cannot be higher than MaxReadLevel"}, }, { name: "GetReplicationDLQSize success", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). SelectReplicationDLQTasksCount(ctx, shardID, "sourceCluster"). Return(int64(42), nil) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { resp, err := store.GetReplicationDLQSize(ctx, &persistence.GetReplicationDLQSizeRequest{ SourceClusterName: "sourceCluster", }) if err != nil { return err } if resp.Size != 42 { return errors.New("unexpected DLQ size") } return nil }, expectedError: nil, }, { name: "GetReplicationDLQSize failure - invalid source cluster name", setupMock: func(ctrl *gomock.Controller) *nosqlExecutionStore { mockDB := nosqlplugin.NewMockDB(ctrl) mockDB.EXPECT(). SelectReplicationDLQTasksCount(ctx, shardID, ""). Return(int64(0), nil) return newTestNosqlExecutionStore(mockDB, log.NewNoop()) }, testFunc: func(store *nosqlExecutionStore) error { _, err := store.GetReplicationDLQSize(ctx, &persistence.GetReplicationDLQSizeRequest{ SourceClusterName: "", }) return err }, expectedError: nil, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) store := tc.setupMock(ctrl) err := tc.testFunc(store) if tc.expectedError != nil { var expectedErrType error require.ErrorAs(t, err, &expectedErrType, "Expected error type does not match.") } else { require.NoError(t, err) } }) } } func TestDeleteReplicationTaskFromDLQ(t *testing.T) { ctx := context.Background() shardID := 1 tests := []struct { name string sourceCluster string taskID int64 setupMock func(*nosqlplugin.MockDB) expectedError error }{ { name: "success", sourceCluster: "sourceCluster", taskID: 1, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT(). DeleteReplicationDLQTask(ctx, shardID, "sourceCluster", int64(1)). Return(nil) }, expectedError: nil, }, { name: "database error", sourceCluster: "sourceCluster", taskID: 1, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() mockDB.EXPECT(). DeleteReplicationDLQTask(ctx, shardID, "sourceCluster", int64(1)). Return(errors.New("database error")) }, expectedError: &types.InternalServiceError{Message: "database error"}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(controller) store := newTestNosqlExecutionStore(mockDB, log.NewNoop()) tc.setupMock(mockDB) err := store.DeleteReplicationTaskFromDLQ(ctx, &persistence.DeleteReplicationTaskFromDLQRequest{ SourceClusterName: tc.sourceCluster, TaskID: tc.taskID, }) if tc.expectedError != nil { require.ErrorAs(t, err, &tc.expectedError) } else { require.NoError(t, err) } }) } } func TestRangeDeleteReplicationTaskFromDLQ(t *testing.T) { ctx := context.Background() shardID := 1 tests := []struct { name string sourceCluster string inclusiveBeginID int64 exclusiveEndID int64 setupMock func(*nosqlplugin.MockDB) expectedError error }{ { name: "success", sourceCluster: "sourceCluster", inclusiveBeginID: 1, exclusiveEndID: 100, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT(). RangeDeleteReplicationDLQTasks(ctx, shardID, "sourceCluster", int64(1), int64(100)). Return(nil) }, expectedError: nil, }, { name: "database error", sourceCluster: "sourceCluster", inclusiveBeginID: 1, exclusiveEndID: 100, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() mockDB.EXPECT(). RangeDeleteReplicationDLQTasks(ctx, shardID, "sourceCluster", int64(1), int64(100)). Return(errors.New("database error")) }, expectedError: &types.InternalServiceError{Message: "database error"}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(controller) store := newTestNosqlExecutionStore(mockDB, log.NewNoop()) tc.setupMock(mockDB) _, err := store.RangeDeleteReplicationTaskFromDLQ(ctx, &persistence.RangeDeleteReplicationTaskFromDLQRequest{ SourceClusterName: tc.sourceCluster, InclusiveBeginTaskID: tc.inclusiveBeginID, ExclusiveEndTaskID: tc.exclusiveEndID, }) if tc.expectedError != nil { require.ErrorAs(t, err, &tc.expectedError) } else { require.NoError(t, err) } }) } } func TestCreateFailoverMarkerTasks(t *testing.T) { ctx := context.Background() shardID := 1 tests := []struct { name string rangeID int64 markers []*persistence.FailoverMarkerTask setupMock func(*nosqlplugin.MockDB, *serialization.MockTaskSerializer) expectedError error }{ { name: "success", rangeID: 123, markers: []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{}, DomainID: "testDomainID", }, }, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryReplication, gomock.Any()).Return(persistence.DataBlob{ Data: []byte("1"), Encoding: commonconstants.EncodingTypeThriftRW, }, nil) mockDB.EXPECT(). InsertReplicationTask(ctx, gomock.Any(), nosqlplugin.ShardCondition{ShardID: shardID, RangeID: 123}). Return(nil) }, expectedError: nil, }, { name: "serialization error", rangeID: 123, markers: []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{}, DomainID: "testDomainID", }, }, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryReplication, gomock.Any()).Return(persistence.DataBlob{}, errors.New("some error")) }, expectedError: errors.New("some error"), }, { name: "CreateFailoverMarkerTasks failure - ShardOperationConditionFailure", rangeID: 123, markers: []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{}, DomainID: "testDomainID", }, }, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryReplication, gomock.Any()).Return(persistence.DataBlob{ Data: []byte("1"), Encoding: commonconstants.EncodingTypeThriftRW, }, nil) conditionFailureErr := &nosqlplugin.ShardOperationConditionFailure{ RangeID: 123, // Use direct int64 value Details: "Shard condition failed", // Use direct string value } mockDB.EXPECT(). InsertReplicationTask(ctx, gomock.Any(), nosqlplugin.ShardCondition{ShardID: shardID, RangeID: 123}). Return(conditionFailureErr) // Simulate ShardOperationConditionFailure }, expectedError: &persistence.ShardOwnershipLostError{ ShardID: shardID, Msg: "Failed to create workflow execution. Request RangeID: 123, columns: (Shard condition failed)", }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(controller) mockTaskSerializer := serialization.NewMockTaskSerializer(controller) store := newTestNosqlExecutionStoreWithTaskSerializer(mockDB, log.NewNoop(), mockTaskSerializer) tc.setupMock(mockDB, mockTaskSerializer) err := store.CreateFailoverMarkerTasks(ctx, &persistence.CreateFailoverMarkersRequest{ RangeID: tc.rangeID, Markers: tc.markers, }) if tc.expectedError != nil { require.ErrorAs(t, err, &tc.expectedError) } else { require.NoError(t, err) } }) } } func TestIsWorkflowExecutionExists(t *testing.T) { ctx := context.Background() gomockController := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(gomockController) store := &nosqlExecutionStore{ shardID: 1, nosqlStore: nosqlStore{db: mockDB}, } domainID := "testDomainID" workflowID := "testWorkflowID" runID := "testRunID" tests := []struct { name string setupMock func() request *persistence.IsWorkflowExecutionExistsRequest expectedExists bool expectedError error }{ { name: "Workflow Execution Exists", setupMock: func() { mockDB.EXPECT().IsWorkflowExecutionExists(ctx, store.shardID, domainID, workflowID, runID).Return(true, nil) }, request: &persistence.IsWorkflowExecutionExistsRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, expectedExists: true, expectedError: nil, }, { name: "Workflow Execution Does Not Exist", setupMock: func() { mockDB.EXPECT().IsWorkflowExecutionExists(ctx, store.shardID, domainID, workflowID, runID).Return(false, nil) }, request: &persistence.IsWorkflowExecutionExistsRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, expectedExists: false, expectedError: nil, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { tc.setupMock() response, err := store.IsWorkflowExecutionExists(ctx, tc.request) if tc.expectedError != nil { require.Error(t, err) // Optionally, further validate the error type or message } else { require.NoError(t, err) require.Equal(t, tc.expectedExists, response.Exists) } }) } } func TestConflictResolveWorkflowExecution(t *testing.T) { ctx := context.Background() gomockController := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(gomockController) mockTaskSerializer := serialization.NewMockTaskSerializer(gomockController) store, err := NewExecutionStore(1, mockDB, log.NewNoop(), mockTaskSerializer, &persistence.DynamicConfiguration{ EnableHistoryTaskDualWriteMode: func(...dynamicproperties.FilterOption) bool { return false }, }) require.NoError(t, err) tests := []struct { name string setupMocks func() getRequest func() *persistence.InternalConflictResolveWorkflowExecutionRequest expectedError error }{ { name: "DB Error on Reset Execution Insertion", setupMocks: func() { mockDB.EXPECT().UpdateWorkflowExecutionWithTasks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("DB error")).Times(1) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsDBUnavailableError(gomock.Any()).Return(false).AnyTimes() }, getRequest: func() *persistence.InternalConflictResolveWorkflowExecutionRequest { return newConflictResolveRequest(persistence.ConflictResolveWorkflowModeUpdateCurrent) }, expectedError: &types.InternalServiceError{Message: "DB error"}, }, { name: "Unknown Conflict Resolution Mode", setupMocks: func() { }, getRequest: func() *persistence.InternalConflictResolveWorkflowExecutionRequest { req := newConflictResolveRequest(-1) // Intentionally using an invalid mode return req }, expectedError: &types.InternalServiceError{Message: "ConflictResolveWorkflowExecution: unknown mode: -1"}, }, { name: "Error on Shard Condition Mismatch", setupMocks: func() { // Simulate shard condition mismatch error by returning a ShardOperationConditionFailure error from the mock conditionFailure := &nosqlplugin.ShardOperationConditionFailure{ RangeID: 124, // Example mismatched RangeID to trigger the condition failure } mockDB.EXPECT().UpdateWorkflowExecutionWithTasks( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(conditionFailure).Times(1) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false).AnyTimes() mockDB.EXPECT().IsDBUnavailableError(gomock.Any()).Return(false).AnyTimes() // Ensure this call returns the simulated condition failure once }, getRequest: func() *persistence.InternalConflictResolveWorkflowExecutionRequest { req := newConflictResolveRequest(persistence.ConflictResolveWorkflowModeUpdateCurrent) req.RangeID = 123 // Intentionally set to simulate the condition leading to a shard condition mismatch. return req }, expectedError: &types.InternalServiceError{Message: "Shard error"}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { tc.setupMocks() req := tc.getRequest() err := store.ConflictResolveWorkflowExecution(ctx, req) if tc.expectedError != nil { require.Error(t, err) assert.IsType(t, tc.expectedError, err) } else { require.NoError(t, err) } }) } } func newCreateWorkflowExecutionRequest() *persistence.InternalCreateWorkflowExecutionRequest { return &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 123, Mode: persistence.CreateWorkflowModeBrandNew, PreviousRunID: "previous-run-id", PreviousLastWriteVersion: 456, NewWorkflowSnapshot: getNewWorkflowSnapshot(), } } func newGetWorkflowExecutionRequest() *persistence.InternalGetWorkflowExecutionRequest { return &persistence.InternalGetWorkflowExecutionRequest{ DomainID: constants.TestDomainID, Execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, } } func newUpdateWorkflowExecutionRequest() *persistence.InternalUpdateWorkflowExecutionRequest { return &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 123, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, }, WorkflowRequests: []*persistence.WorkflowRequest{ { RequestID: constants.TestRequestID, Version: 1, RequestType: persistence.WorkflowRequestTypeStart, }, }, }, } } func newUpdateWorkflowExecutionRequestForContinueAsNew() *persistence.InternalUpdateWorkflowExecutionRequest { return &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 123, Mode: persistence.UpdateWorkflowModeUpdateCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, State: persistence.WorkflowStateCompleted, CloseStatus: persistence.WorkflowCloseStatusContinuedAsNew, }, WorkflowRequests: []*persistence.WorkflowRequest{ { RequestID: constants.TestRequestID, Version: 1, RequestType: persistence.WorkflowRequestTypeStart, }, }, }, NewWorkflowSnapshot: &persistence.InternalWorkflowSnapshot{ VersionHistories: &persistence.DataBlob{}, ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ActiveClusterSelectionPolicy: &persistence.DataBlob{ Encoding: "json", Data: []byte(`{"policy": "abc"}`), }, }, WorkflowRequests: []*persistence.WorkflowRequest{ { RequestID: constants.TestRequestID, Version: 1, RequestType: persistence.WorkflowRequestTypeStart, }, }, }, } } func getNewWorkflowSnapshot() persistence.InternalWorkflowSnapshot { return persistence.InternalWorkflowSnapshot{ VersionHistories: &persistence.DataBlob{}, ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, WorkflowRequests: []*persistence.WorkflowRequest{ { RequestID: constants.TestRequestID, Version: 1, RequestType: persistence.WorkflowRequestTypeStart, }, }, } } func newTestNosqlExecutionStore(db nosqlplugin.DB, logger log.Logger) *nosqlExecutionStore { return &nosqlExecutionStore{ shardID: 1, nosqlStore: nosqlStore{logger: logger, db: db}, } } func newInternalReplicationTaskInfo() persistence.InternalReplicationTaskInfo { var fixedCreationTime = time.Date(2024, time.April, 3, 14, 35, 44, 0, time.UTC) return persistence.InternalReplicationTaskInfo{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", TaskID: 123, TaskType: persistence.ReplicationTaskTypeHistory, FirstEventID: 1, NextEventID: 2, Version: 1, ScheduledID: 3, BranchToken: []byte("branchToken"), NewRunBranchToken: []byte("newRunBranchToken"), CreationTime: fixedCreationTime, } } func newConflictResolveRequest(mode persistence.ConflictResolveWorkflowMode) *persistence.InternalConflictResolveWorkflowExecutionRequest { return &persistence.InternalConflictResolveWorkflowExecutionRequest{ Mode: mode, RangeID: 123, CurrentWorkflowMutation: &persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "currentRunID", State: persistence.WorkflowStateCompleted, CloseStatus: persistence.WorkflowCloseStatusCompleted, }, }, ResetWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "resetRunID", State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, }, }, NewWorkflowSnapshot: nil, } } func TestRangeCompleteHistoryTask(t *testing.T) { ctx := context.Background() shardID := 1 tests := []struct { name string request *persistence.RangeCompleteHistoryTaskRequest setupMock func(*nosqlplugin.MockDB) expectedError error }{ { name: "success - scheduled timer task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0).Add(time.Minute), 0), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteTimerTasks(ctx, shardID, time.Unix(0, 0), time.Unix(0, 0).Add(time.Minute)).Return(nil) }, expectedError: nil, }, { name: "success - immediate transfer task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteTransferTasks(ctx, shardID, int64(100), int64(200)).Return(nil) }, expectedError: nil, }, { name: "success - immediate replication task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), // this is ignored by replication task ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteReplicationTasks(ctx, shardID, int64(200)).Return(nil) }, expectedError: nil, }, { name: "unknown task category error", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategory{}, }, setupMock: func(mockDB *nosqlplugin.MockDB) {}, expectedError: &types.BadRequestError{}, }, { name: "database error on timer task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0).Add(time.Minute), 0), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteTimerTasks(ctx, shardID, time.Unix(0, 0), time.Unix(0, 0).Add(time.Minute)).Return(errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "database error on transfer task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteTransferTasks(ctx, shardID, int64(100), int64(200)).Return(errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "database error on replication task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteReplicationTasks(ctx, shardID, int64(200)).Return(errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() mockDB := nosqlplugin.NewMockDB(controller) store := &nosqlExecutionStore{nosqlStore: nosqlStore{db: mockDB}, shardID: shardID} tc.setupMock(mockDB) _, err := store.RangeCompleteHistoryTask(ctx, tc.request) if tc.expectedError != nil { require.ErrorAs(t, err, &tc.expectedError) } else { require.NoError(t, err) } }) } } func TestGetHistoryTasks(t *testing.T) { ctx := context.Background() shardID := 1 tests := []struct { name string request *persistence.GetHistoryTasksRequest readNoSQLHistoryTaskFromDataBlob bool setupMock func(*nosqlplugin.MockDB, *serialization.MockTaskSerializer) expectedError error expectedTasks []persistence.Task }{ { name: "success - get immediate transfer tasks", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 10, NextPageToken: []byte("next-page-token"), }, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectTransferTasksOrderByTaskID(ctx, shardID, 10, []byte("next-page-token"), int64(100), int64(200)).Return([]*nosqlplugin.HistoryMigrationTask{ { Transfer: &persistence.TransferTaskInfo{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", VisibilityTimestamp: time.Unix(0, 0), TaskID: 1, TaskType: persistence.TransferTaskTypeDecisionTask, ScheduleID: 1, Version: 1, TaskList: "testTaskList", }, Task: &persistence.DataBlob{Data: []byte("task"), Encoding: "json"}, }, }, nil, nil) }, expectedError: nil, expectedTasks: []persistence.Task{ &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ TaskID: 1, Version: 1, VisibilityTimestamp: time.Unix(0, 0), }, ScheduleID: 1, TaskList: "testTaskList", }, }, }, { name: "success - get immediate transfer tasks from data blob", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 10, NextPageToken: []byte("next-page-token"), }, readNoSQLHistoryTaskFromDataBlob: true, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectTransferTasksOrderByTaskID(ctx, shardID, 10, []byte("next-page-token"), int64(100), int64(200)).Return([]*nosqlplugin.HistoryMigrationTask{ { Task: &persistence.DataBlob{Data: []byte("task"), Encoding: "json"}, TaskID: 1, }, }, nil, nil) mockTaskSerializer.EXPECT().DeserializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()).Return(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ TaskID: 1, Version: 1, VisibilityTimestamp: time.Unix(0, 0), }, ScheduleID: 1, TaskList: "testTaskList", }, nil) }, expectedError: nil, expectedTasks: []persistence.Task{ &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ TaskID: 1, Version: 1, VisibilityTimestamp: time.Unix(0, 0), }, ScheduleID: 1, TaskList: "testTaskList", }, }, }, { name: "success - get scheduled timer tasks", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0).Add(time.Minute), 0), PageSize: 10, NextPageToken: []byte("next-page-token"), }, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectTimerTasksOrderByVisibilityTime(ctx, shardID, 10, []byte("next-page-token"), time.Unix(0, 0), time.Unix(0, 0).Add(time.Minute)).Return([]*nosqlplugin.HistoryMigrationTask{ { Timer: &persistence.TimerTaskInfo{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", VisibilityTimestamp: time.Unix(0, 0), TaskID: 1, TaskType: persistence.TaskTypeUserTimer, Version: 1, EventID: 12, }, Task: &persistence.DataBlob{Data: []byte("task"), Encoding: "json"}, }, }, nil, nil) }, expectedError: nil, expectedTasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ TaskID: 1, Version: 1, VisibilityTimestamp: time.Unix(0, 0), }, EventID: 12, }, }, }, { name: "success - get scheduled timer tasks from data blob", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0).Add(time.Minute), 0), PageSize: 10, NextPageToken: []byte("next-page-token"), }, readNoSQLHistoryTaskFromDataBlob: true, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectTimerTasksOrderByVisibilityTime(ctx, shardID, 10, []byte("next-page-token"), time.Unix(0, 0), time.Unix(0, 0).Add(time.Minute)).Return([]*nosqlplugin.HistoryMigrationTask{ { Task: &persistence.DataBlob{Data: []byte("task"), Encoding: "json"}, TaskID: 1, ScheduledTime: time.Unix(1, 1), }, }, nil, nil) mockTaskSerializer.EXPECT().DeserializeTask(persistence.HistoryTaskCategoryTimer, gomock.Any()).Return(&persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ Version: 1, }, EventID: 12, }, nil) }, expectedError: nil, expectedTasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ TaskID: 1, Version: 1, VisibilityTimestamp: time.Unix(1, 1), }, EventID: 12, }, }, }, { name: "success - get immediate replication tasks", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 10, NextPageToken: []byte("next-page-token"), }, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectReplicationTasksOrderByTaskID(ctx, shardID, 10, []byte("next-page-token"), int64(100), int64(200)).Return([]*nosqlplugin.HistoryMigrationTask{ { Replication: &persistence.InternalReplicationTaskInfo{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", TaskID: 1, TaskType: persistence.ReplicationTaskTypeHistory, Version: 1, ScheduledID: 3, FirstEventID: 1, NextEventID: 2, BranchToken: []byte("branchToken"), NewRunBranchToken: []byte("newRunBranchToken"), CreationTime: time.Unix(0, 0), }, Task: &persistence.DataBlob{Data: []byte("task"), Encoding: "json"}, }, }, nil, nil) }, expectedError: nil, expectedTasks: []persistence.Task{ &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ TaskID: 1, Version: 1, VisibilityTimestamp: time.Unix(0, 0), }, FirstEventID: 1, NextEventID: 2, BranchToken: []byte("branchToken"), NewRunBranchToken: []byte("newRunBranchToken"), }, }, }, { name: "success - get immediate replication tasks from data blob", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 10, NextPageToken: []byte("next-page-token"), }, readNoSQLHistoryTaskFromDataBlob: true, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectReplicationTasksOrderByTaskID(ctx, shardID, 10, []byte("next-page-token"), int64(100), int64(200)).Return([]*nosqlplugin.HistoryMigrationTask{ { Task: &persistence.DataBlob{Data: []byte("task"), Encoding: "json"}, TaskID: 1, }, }, nil, nil) mockTaskSerializer.EXPECT().DeserializeTask(persistence.HistoryTaskCategoryReplication, gomock.Any()).Return(&persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ Version: 1, VisibilityTimestamp: time.Unix(0, 0), }, FirstEventID: 1, NextEventID: 2, BranchToken: []byte("branchToken"), NewRunBranchToken: []byte("newRunBranchToken"), }, nil) }, expectedError: nil, expectedTasks: []persistence.Task{ &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ TaskID: 1, Version: 1, VisibilityTimestamp: time.Unix(0, 0), }, FirstEventID: 1, NextEventID: 2, BranchToken: []byte("branchToken"), NewRunBranchToken: []byte("newRunBranchToken"), }, }, }, { name: "database error on transfer task retrieval", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, PageSize: 10, }, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectTransferTasksOrderByTaskID(ctx, shardID, 10, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "database error on replication task retrieval", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, PageSize: 10, }, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectReplicationTasksOrderByTaskID(ctx, shardID, 10, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "database error on timer task retrieval", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, PageSize: 10, }, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectTimerTasksOrderByVisibilityTime(ctx, shardID, 10, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "unknown task category error", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategory{}, }, setupMock: func(mockDB *nosqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) {}, expectedError: &types.BadRequestError{}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() mockDB := nosqlplugin.NewMockDB(controller) mockTaskSerializer := serialization.NewMockTaskSerializer(controller) store := &nosqlExecutionStore{nosqlStore: nosqlStore{db: mockDB, dc: &persistence.DynamicConfiguration{ReadNoSQLHistoryTaskFromDataBlob: func(...dynamicproperties.FilterOption) bool { return tc.readNoSQLHistoryTaskFromDataBlob }}}, shardID: shardID, taskSerializer: mockTaskSerializer} tc.setupMock(mockDB, mockTaskSerializer) resp, err := store.GetHistoryTasks(ctx, tc.request) if tc.expectedError != nil { require.ErrorAs(t, err, &tc.expectedError) } else { require.NoError(t, err) assert.Equal(t, tc.expectedTasks, resp.Tasks) } }) } } func TestCompleteHistoryTask(t *testing.T) { ctx := context.Background() shardID := 1 tests := []struct { name string request *persistence.CompleteHistoryTaskRequest setupMock func(*nosqlplugin.MockDB) expectedError error }{ { name: "success - complete scheduled timer task", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, TaskKey: persistence.NewHistoryTaskKey(time.Unix(10, 10), 1), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().DeleteTimerTask(ctx, shardID, int64(1), time.Unix(10, 10)).Return(nil) }, expectedError: nil, }, { name: "success - complete immediate transfer task", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, TaskKey: persistence.NewImmediateTaskKey(2), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().DeleteTransferTask(ctx, shardID, int64(2)).Return(nil) }, expectedError: nil, }, { name: "success - complete immediate replication task", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, TaskKey: persistence.NewImmediateTaskKey(3), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().DeleteReplicationTask(ctx, shardID, int64(3)).Return(nil) }, expectedError: nil, }, { name: "unknown task category type", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategory{}, }, setupMock: func(mockDB *nosqlplugin.MockDB) {}, expectedError: &types.BadRequestError{}, }, { name: "delete timer task error", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, TaskKey: persistence.NewHistoryTaskKey(time.Unix(10, 10), 1), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().DeleteTimerTask(ctx, shardID, int64(1), time.Unix(10, 10)).Return(errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "delete transfer task error", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, TaskKey: persistence.NewImmediateTaskKey(2), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().DeleteTransferTask(ctx, shardID, int64(2)).Return(errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "delete replication task error", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, TaskKey: persistence.NewImmediateTaskKey(3), }, setupMock: func(mockDB *nosqlplugin.MockDB) { mockDB.EXPECT().DeleteReplicationTask(ctx, shardID, int64(3)).Return(errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() mockDB := nosqlplugin.NewMockDB(controller) store := &nosqlExecutionStore{nosqlStore: nosqlStore{db: mockDB, dc: &persistence.DynamicConfiguration{}}, shardID: shardID} tc.setupMock(mockDB) err := store.CompleteHistoryTask(ctx, tc.request) if tc.expectedError != nil { require.ErrorAs(t, err, &tc.expectedError) } else { require.NoError(t, err) } }) } } ================================================ FILE: common/persistence/nosql/nosql_execution_store_util.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "fmt" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) func (d *nosqlExecutionStore) prepareCreateWorkflowExecutionRequestWithMaps(newWorkflow *persistence.InternalWorkflowSnapshot, currentTimeStamp time.Time) (*nosqlplugin.WorkflowExecutionRequest, error) { executionInfo := newWorkflow.ExecutionInfo lastWriteVersion := newWorkflow.LastWriteVersion checkSum := newWorkflow.Checksum versionHistories := newWorkflow.VersionHistories executionRequest, err := d.prepareCreateWorkflowExecutionTxn( executionInfo, versionHistories, checkSum, currentTimeStamp, lastWriteVersion, ) if err != nil { return nil, err } executionRequest.ActivityInfos, err = d.prepareActivityInfosForWorkflowTxn(newWorkflow.ActivityInfos) if err != nil { return nil, err } executionRequest.TimerInfos, err = d.prepareTimerInfosForWorkflowTxn(newWorkflow.TimerInfos) if err != nil { return nil, err } executionRequest.ChildWorkflowInfos, err = d.prepareChildWFInfosForWorkflowTxn(newWorkflow.ChildExecutionInfos) if err != nil { return nil, err } executionRequest.RequestCancelInfos, err = d.prepareRequestCancelsForWorkflowTxn(newWorkflow.RequestCancelInfos) if err != nil { return nil, err } executionRequest.SignalInfos, err = d.prepareSignalInfosForWorkflowTxn(newWorkflow.SignalInfos) if err != nil { return nil, err } executionRequest.SignalRequestedIDs = newWorkflow.SignalRequestedIDs executionRequest.MapsWriteMode = nosqlplugin.WorkflowExecutionMapsWriteModeCreate executionRequest.CurrentTimeStamp = currentTimeStamp return executionRequest, nil } func (d *nosqlExecutionStore) prepareWorkflowRequestRows( domainID, workflowID, runID string, requests []*persistence.WorkflowRequest, requestRowsToAppend []*nosqlplugin.WorkflowRequestRow, ) []*nosqlplugin.WorkflowRequestRow { for _, req := range requests { requestRowsToAppend = append(requestRowsToAppend, &nosqlplugin.WorkflowRequestRow{ ShardID: d.shardID, DomainID: domainID, WorkflowID: workflowID, RequestType: req.RequestType, RequestID: req.RequestID, Version: req.Version, RunID: runID, }) } return requestRowsToAppend } func (d *nosqlExecutionStore) prepareActiveClusterSelectionPolicyRow(domainID, workflowID, runID string, activeClusterSelectionPolicy *persistence.DataBlob) *nosqlplugin.ActiveClusterSelectionPolicyRow { if activeClusterSelectionPolicy == nil { return nil } return &nosqlplugin.ActiveClusterSelectionPolicyRow{ ShardID: d.shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, Policy: activeClusterSelectionPolicy, } } func (d *nosqlExecutionStore) prepareResetWorkflowExecutionRequestWithMapsAndEventBuffer(resetWorkflow *persistence.InternalWorkflowSnapshot, currentTimeStamp time.Time) (*nosqlplugin.WorkflowExecutionRequest, error) { executionInfo := resetWorkflow.ExecutionInfo lastWriteVersion := resetWorkflow.LastWriteVersion checkSum := resetWorkflow.Checksum versionHistories := resetWorkflow.VersionHistories nowTimestamp := currentTimeStamp executionRequest, err := d.prepareUpdateWorkflowExecutionTxn( executionInfo, versionHistories, checkSum, nowTimestamp, lastWriteVersion, ) if err != nil { return nil, err } // reset 6 maps executionRequest.ActivityInfos, err = d.prepareActivityInfosForWorkflowTxn(resetWorkflow.ActivityInfos) if err != nil { return nil, err } executionRequest.TimerInfos, err = d.prepareTimerInfosForWorkflowTxn(resetWorkflow.TimerInfos) if err != nil { return nil, err } executionRequest.ChildWorkflowInfos, err = d.prepareChildWFInfosForWorkflowTxn(resetWorkflow.ChildExecutionInfos) if err != nil { return nil, err } executionRequest.RequestCancelInfos, err = d.prepareRequestCancelsForWorkflowTxn(resetWorkflow.RequestCancelInfos) if err != nil { return nil, err } executionRequest.SignalInfos, err = d.prepareSignalInfosForWorkflowTxn(resetWorkflow.SignalInfos) if err != nil { return nil, err } executionRequest.SignalRequestedIDs = resetWorkflow.SignalRequestedIDs executionRequest.MapsWriteMode = nosqlplugin.WorkflowExecutionMapsWriteModeReset // delete buffered events executionRequest.EventBufferWriteMode = nosqlplugin.EventBufferWriteModeClear // condition executionRequest.PreviousNextEventIDCondition = &resetWorkflow.Condition return executionRequest, nil } func (d *nosqlExecutionStore) prepareUpdateWorkflowExecutionRequestWithMapsAndEventBuffer(workflowMutation *persistence.InternalWorkflowMutation, currentTimeStamp time.Time) (*nosqlplugin.WorkflowExecutionRequest, error) { executionInfo := workflowMutation.ExecutionInfo lastWriteVersion := workflowMutation.LastWriteVersion checkSum := workflowMutation.Checksum versionHistories := workflowMutation.VersionHistories nowTimestamp := currentTimeStamp executionRequest, err := d.prepareUpdateWorkflowExecutionTxn( executionInfo, versionHistories, checkSum, nowTimestamp, lastWriteVersion, ) if err != nil { return nil, err } // merge 6 maps executionRequest.ActivityInfos, err = d.prepareActivityInfosForWorkflowTxn(workflowMutation.UpsertActivityInfos) if err != nil { return nil, err } executionRequest.TimerInfos, err = d.prepareTimerInfosForWorkflowTxn(workflowMutation.UpsertTimerInfos) if err != nil { return nil, err } executionRequest.ChildWorkflowInfos, err = d.prepareChildWFInfosForWorkflowTxn(workflowMutation.UpsertChildExecutionInfos) if err != nil { return nil, err } executionRequest.RequestCancelInfos, err = d.prepareRequestCancelsForWorkflowTxn(workflowMutation.UpsertRequestCancelInfos) if err != nil { return nil, err } executionRequest.SignalInfos, err = d.prepareSignalInfosForWorkflowTxn(workflowMutation.UpsertSignalInfos) if err != nil { return nil, err } executionRequest.SignalRequestedIDs = workflowMutation.UpsertSignalRequestedIDs // delete from 6 maps executionRequest.ActivityInfoKeysToDelete = workflowMutation.DeleteActivityInfos executionRequest.TimerInfoKeysToDelete = workflowMutation.DeleteTimerInfos executionRequest.ChildWorkflowInfoKeysToDelete = workflowMutation.DeleteChildExecutionInfos executionRequest.RequestCancelInfoKeysToDelete = workflowMutation.DeleteRequestCancelInfos executionRequest.SignalInfoKeysToDelete = workflowMutation.DeleteSignalInfos executionRequest.SignalRequestedIDsKeysToDelete = workflowMutation.DeleteSignalRequestedIDs // map write mode executionRequest.MapsWriteMode = nosqlplugin.WorkflowExecutionMapsWriteModeUpdate // prepare to write buffer event executionRequest.EventBufferWriteMode = nosqlplugin.EventBufferWriteModeNone if workflowMutation.ClearBufferedEvents { executionRequest.EventBufferWriteMode = nosqlplugin.EventBufferWriteModeClear } else if workflowMutation.NewBufferedEvents != nil { executionRequest.EventBufferWriteMode = nosqlplugin.EventBufferWriteModeAppend executionRequest.NewBufferedEventBatch = workflowMutation.NewBufferedEvents } // condition executionRequest.PreviousNextEventIDCondition = &workflowMutation.Condition return executionRequest, nil } func (d *nosqlExecutionStore) prepareTimerTasksForWorkflowTxn(domainID, workflowID, runID string, timerTasks []persistence.Task) ([]*nosqlplugin.HistoryMigrationTask, error) { var tasks []*nosqlplugin.HistoryMigrationTask for _, task := range timerTasks { nt, err := task.ToTimerTaskInfo() if err != nil { return nil, err } var blob *persistence.DataBlob if d.dc.EnableHistoryTaskDualWriteMode() { data, err := d.taskSerializer.SerializeTask(persistence.HistoryTaskCategoryTimer, task) if err != nil { return nil, err } blob = &data } tasks = append(tasks, &nosqlplugin.HistoryMigrationTask{ Timer: nt, Task: blob, }) } return tasks, nil } func (d *nosqlExecutionStore) prepareReplicationTasksForWorkflowTxn(domainID, workflowID, runID string, replicationTasks []persistence.Task) ([]*nosqlplugin.HistoryMigrationTask, error) { var tasks []*nosqlplugin.HistoryMigrationTask for _, task := range replicationTasks { // Replication task specific information firstEventID := constants.EmptyEventID nextEventID := constants.EmptyEventID version := constants.EmptyVersion //nolint:ineffassign activityScheduleID := constants.EmptyEventID var branchToken, newRunBranchToken []byte switch task.GetTaskType() { case persistence.ReplicationTaskTypeHistory: histTask := task.(*persistence.HistoryReplicationTask) branchToken = histTask.BranchToken newRunBranchToken = histTask.NewRunBranchToken firstEventID = histTask.FirstEventID nextEventID = histTask.NextEventID version = task.GetVersion() case persistence.ReplicationTaskTypeSyncActivity: version = task.GetVersion() activityScheduleID = task.(*persistence.SyncActivityTask).ScheduledID case persistence.ReplicationTaskTypeFailoverMarker: version = task.GetVersion() default: return nil, &types.InternalServiceError{ Message: fmt.Sprintf("Unknown replication type: %v", task.GetTaskType()), } } nt := &nosqlplugin.ReplicationTask{ TaskType: task.GetTaskType(), DomainID: domainID, WorkflowID: workflowID, RunID: runID, CreationTime: task.GetVisibilityTimestamp(), TaskID: task.GetTaskID(), FirstEventID: firstEventID, NextEventID: nextEventID, Version: version, ScheduledID: activityScheduleID, BranchToken: branchToken, NewRunBranchToken: newRunBranchToken, } var blob *persistence.DataBlob if d.dc.EnableHistoryTaskDualWriteMode() { data, err := d.taskSerializer.SerializeTask(persistence.HistoryTaskCategoryReplication, task) if err != nil { return nil, err } blob = &data } tasks = append(tasks, &nosqlplugin.HistoryMigrationTask{ Replication: nt, Task: blob, }) } return tasks, nil } func (d *nosqlExecutionStore) prepareNoSQLTasksForWorkflowTxn( domainID, workflowID, runID string, tasksByCategory map[persistence.HistoryTaskCategory][]persistence.Task, outputTasks map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask, ) error { for c, tasks := range tasksByCategory { switch c.ID() { case persistence.HistoryTaskCategoryIDTransfer: transferTasks, err := d.prepareTransferTasksForWorkflowTxn(domainID, workflowID, runID, tasks) if err != nil { return err } outputTasks[c] = append(outputTasks[c], transferTasks...) case persistence.HistoryTaskCategoryIDTimer: timerTasks, err := d.prepareTimerTasksForWorkflowTxn(domainID, workflowID, runID, tasks) if err != nil { return err } outputTasks[c] = append(outputTasks[c], timerTasks...) case persistence.HistoryTaskCategoryIDReplication: replicationTasks, err := d.prepareReplicationTasksForWorkflowTxn(domainID, workflowID, runID, tasks) if err != nil { return err } outputTasks[c] = append(outputTasks[c], replicationTasks...) } } // TODO: implementing logic for other categories return nil } func (d *nosqlExecutionStore) prepareTransferTasksForWorkflowTxn(domainID, workflowID, runID string, transferTasks []persistence.Task) ([]*nosqlplugin.HistoryMigrationTask, error) { var tasks []*nosqlplugin.HistoryMigrationTask for _, task := range transferTasks { t, err := task.ToTransferTaskInfo() if err != nil { return nil, err } var blob *persistence.DataBlob if d.dc.EnableHistoryTaskDualWriteMode() { data, err := d.taskSerializer.SerializeTask(persistence.HistoryTaskCategoryTransfer, task) if err != nil { return nil, err } blob = &data } tasks = append(tasks, &nosqlplugin.HistoryMigrationTask{ Transfer: t, Task: blob, }) } return tasks, nil } func (d *nosqlExecutionStore) prepareActivityInfosForWorkflowTxn(activityInfos []*persistence.InternalActivityInfo) (map[int64]*persistence.InternalActivityInfo, error) { m := map[int64]*persistence.InternalActivityInfo{} for _, a := range activityInfos { _, scheduleEncoding := persistence.FromDataBlob(a.ScheduledEvent) _, startEncoding := persistence.FromDataBlob(a.StartedEvent) if a.StartedEvent != nil && scheduleEncoding != startEncoding { return nil, persistence.NewCadenceSerializationError(fmt.Sprintf("expect to have the same encoding, but %v != %v", scheduleEncoding, startEncoding)) } a.ScheduledEvent = a.ScheduledEvent.ToNilSafeDataBlob() a.StartedEvent = a.StartedEvent.ToNilSafeDataBlob() m[a.ScheduleID] = a } return m, nil } func (d *nosqlExecutionStore) prepareTimerInfosForWorkflowTxn(timerInfo []*persistence.TimerInfo) (map[string]*persistence.TimerInfo, error) { m := map[string]*persistence.TimerInfo{} for _, a := range timerInfo { m[a.TimerID] = a } return m, nil } func (d *nosqlExecutionStore) prepareChildWFInfosForWorkflowTxn(childWFInfos []*persistence.InternalChildExecutionInfo) (map[int64]*persistence.InternalChildExecutionInfo, error) { m := map[int64]*persistence.InternalChildExecutionInfo{} for _, c := range childWFInfos { _, initiatedEncoding := persistence.FromDataBlob(c.InitiatedEvent) _, startEncoding := persistence.FromDataBlob(c.StartedEvent) if c.StartedEvent != nil && initiatedEncoding != startEncoding { return nil, persistence.NewCadenceSerializationError(fmt.Sprintf("expect to have the same encoding, but %v != %v", initiatedEncoding, startEncoding)) } if c.StartedRunID == "" { c.StartedRunID = emptyRunID } c.InitiatedEvent = c.InitiatedEvent.ToNilSafeDataBlob() c.StartedEvent = c.StartedEvent.ToNilSafeDataBlob() m[c.InitiatedID] = c } return m, nil } func (d *nosqlExecutionStore) prepareRequestCancelsForWorkflowTxn(requestCancels []*persistence.RequestCancelInfo) (map[int64]*persistence.RequestCancelInfo, error) { m := map[int64]*persistence.RequestCancelInfo{} for _, c := range requestCancels { m[c.InitiatedID] = c } return m, nil } func (d *nosqlExecutionStore) prepareSignalInfosForWorkflowTxn(signalInfos []*persistence.SignalInfo) (map[int64]*persistence.SignalInfo, error) { m := map[int64]*persistence.SignalInfo{} for _, c := range signalInfos { m[c.InitiatedID] = c } return m, nil } func (d *nosqlExecutionStore) prepareUpdateWorkflowExecutionTxn( executionInfo *persistence.InternalWorkflowExecutionInfo, versionHistories *persistence.DataBlob, checksum checksum.Checksum, nowTimestamp time.Time, lastWriteVersion int64, ) (*nosqlplugin.WorkflowExecutionRequest, error) { // validate workflow state & close status if err := persistence.ValidateUpdateWorkflowStateCloseStatus( executionInfo.State, executionInfo.CloseStatus); err != nil { return nil, err } if executionInfo.ParentDomainID == "" { executionInfo.ParentDomainID = emptyDomainID executionInfo.ParentWorkflowID = "" executionInfo.ParentRunID = emptyRunID executionInfo.InitiatedID = emptyInitiatedID } // TODO: remove this logic once all workflows before 0.26.x are completed if executionInfo.FirstExecutionRunID == "" { executionInfo.FirstExecutionRunID = emptyRunID } executionInfo.CompletionEvent = executionInfo.CompletionEvent.ToNilSafeDataBlob() executionInfo.AutoResetPoints = executionInfo.AutoResetPoints.ToNilSafeDataBlob() // TODO also need to set the start / current / last write version versionHistories = versionHistories.ToNilSafeDataBlob() return &nosqlplugin.WorkflowExecutionRequest{ InternalWorkflowExecutionInfo: *executionInfo, VersionHistories: versionHistories, Checksums: &checksum, LastWriteVersion: lastWriteVersion, CurrentTimeStamp: nowTimestamp, }, nil } func (d *nosqlExecutionStore) prepareCreateWorkflowExecutionTxn( executionInfo *persistence.InternalWorkflowExecutionInfo, versionHistories *persistence.DataBlob, checksum checksum.Checksum, nowTimestamp time.Time, lastWriteVersion int64, ) (*nosqlplugin.WorkflowExecutionRequest, error) { // validate workflow state & close status if err := persistence.ValidateCreateWorkflowStateCloseStatus( executionInfo.State, executionInfo.CloseStatus); err != nil { return nil, err } if executionInfo.ParentDomainID == "" { executionInfo.ParentDomainID = emptyDomainID executionInfo.ParentWorkflowID = "" executionInfo.ParentRunID = emptyRunID executionInfo.InitiatedID = emptyInitiatedID } // TODO: remove this logic once all workflows before 0.26.x are completed if executionInfo.FirstExecutionRunID == "" { executionInfo.FirstExecutionRunID = emptyRunID } if executionInfo.StartTimestamp.IsZero() { executionInfo.StartTimestamp = nowTimestamp d.logger.Error("Workflow startTimestamp not set, fallback to now", tag.WorkflowDomainID(executionInfo.DomainID), tag.WorkflowID(executionInfo.WorkflowID), tag.WorkflowRunID(executionInfo.RunID), ) } executionInfo.CompletionEvent = executionInfo.CompletionEvent.ToNilSafeDataBlob() executionInfo.AutoResetPoints = executionInfo.AutoResetPoints.ToNilSafeDataBlob() if versionHistories == nil { return nil, &types.InternalServiceError{Message: "encounter empty version histories in createExecution"} } versionHistories = versionHistories.ToNilSafeDataBlob() return &nosqlplugin.WorkflowExecutionRequest{ InternalWorkflowExecutionInfo: *executionInfo, VersionHistories: versionHistories, Checksums: &checksum, LastWriteVersion: lastWriteVersion, }, nil } func (d *nosqlExecutionStore) prepareCurrentWorkflowRequestForCreateWorkflowTxn( domainID, workflowID, runID string, executionInfo *persistence.InternalWorkflowExecutionInfo, lastWriteVersion int64, request *persistence.InternalCreateWorkflowExecutionRequest, ) (*nosqlplugin.CurrentWorkflowWriteRequest, error) { currentWorkflowWriteReq := &nosqlplugin.CurrentWorkflowWriteRequest{ Row: nosqlplugin.CurrentWorkflowRow{ ShardID: d.shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: executionInfo.State, CloseStatus: executionInfo.CloseStatus, CreateRequestID: executionInfo.CreateRequestID, LastWriteVersion: lastWriteVersion, }, } switch request.Mode { case persistence.CreateWorkflowModeZombie: // noop currentWorkflowWriteReq.WriteMode = nosqlplugin.CurrentWorkflowWriteModeNoop case persistence.CreateWorkflowModeContinueAsNew: currentWorkflowWriteReq.WriteMode = nosqlplugin.CurrentWorkflowWriteModeUpdate currentWorkflowWriteReq.Condition = &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: common.StringPtr(request.PreviousRunID), } case persistence.CreateWorkflowModeWorkflowIDReuse: currentWorkflowWriteReq.WriteMode = nosqlplugin.CurrentWorkflowWriteModeUpdate currentWorkflowWriteReq.Condition = &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: common.StringPtr(request.PreviousRunID), State: common.IntPtr(persistence.WorkflowStateCompleted), LastWriteVersion: common.Int64Ptr(request.PreviousLastWriteVersion), } case persistence.CreateWorkflowModeBrandNew: currentWorkflowWriteReq.WriteMode = nosqlplugin.CurrentWorkflowWriteModeInsert default: return nil, &types.InternalServiceError{ Message: fmt.Sprintf("unknown mode: %v", request.Mode), } } return currentWorkflowWriteReq, nil } func (d *nosqlExecutionStore) processUpdateWorkflowResult(err error, rangeID int64) error { if err != nil { conditionFailureErr, isConditionFailedError := err.(*nosqlplugin.WorkflowOperationConditionFailure) if isConditionFailedError { switch { case conditionFailureErr.UnknownConditionFailureDetails != nil: return &persistence.ConditionFailedError{ Msg: *conditionFailureErr.UnknownConditionFailureDetails, } case conditionFailureErr.ShardRangeIDNotMatch != nil: return &persistence.ShardOwnershipLostError{ ShardID: d.shardID, Msg: fmt.Sprintf("Failed to update workflow execution. Request RangeID: %v, Actual RangeID: %v", rangeID, *conditionFailureErr.ShardRangeIDNotMatch), } case conditionFailureErr.CurrentWorkflowConditionFailInfo != nil: return &persistence.CurrentWorkflowConditionFailedError{ Msg: *conditionFailureErr.CurrentWorkflowConditionFailInfo, } case conditionFailureErr.DuplicateRequest != nil: return &persistence.DuplicateRequestError{ RequestType: conditionFailureErr.DuplicateRequest.RequestType, RunID: conditionFailureErr.DuplicateRequest.RunID, } default: // If ever runs into this branch, there is bug in the code either in here, or in the implementation of nosql plugin err := fmt.Errorf("unexpected conditionFailureReason error") d.logger.Error("A code bug exists in persistence layer, please investigate ASAP", tag.Error(err)) return err } } return convertCommonErrors(d.db, "UpdateWorkflowExecution", err) } return nil } func (d *nosqlExecutionStore) assertNotCurrentExecution( ctx context.Context, domainID string, workflowID string, runID string, ) error { if resp, err := d.GetCurrentExecution(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, }); err != nil { if _, ok := err.(*types.EntityNotExistsError); ok { // allow bypassing no current record return nil } return err } else if resp.RunID == runID { return &persistence.ConditionFailedError{ Msg: fmt.Sprintf("Assertion on current record failed. Current run ID is not expected: %v", resp.RunID), } } return nil } func getWorkflowRequestWriteMode(mode persistence.CreateWorkflowRequestMode) (nosqlplugin.WorkflowRequestWriteMode, error) { switch mode { case persistence.CreateWorkflowRequestModeNew: return nosqlplugin.WorkflowRequestWriteModeInsert, nil case persistence.CreateWorkflowRequestModeReplicated: return nosqlplugin.WorkflowRequestWriteModeUpsert, nil default: return nosqlplugin.WorkflowRequestWriteMode(-1), fmt.Errorf("unknown create workflow request mode: %v", mode) } } ================================================ FILE: common/persistence/nosql/nosql_execution_store_util_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package nosql import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/types" ) var FixedTime = time.Date(2025, 1, 6, 15, 0, 0, 0, time.UTC) func newTestNosqlExecutionStoreWithTaskSerializer(db nosqlplugin.DB, logger log.Logger, taskSerializer serialization.TaskSerializer) *nosqlExecutionStore { return &nosqlExecutionStore{ shardID: 1, nosqlStore: nosqlStore{logger: logger, db: db, dc: &persistence.DynamicConfiguration{EnableHistoryTaskDualWriteMode: func(...dynamicproperties.FilterOption) bool { return true }}}, taskSerializer: taskSerializer, } } func TestNosqlExecutionStoreUtils(t *testing.T) { testCases := []struct { name string setupStore func(*nosqlExecutionStore) (*nosqlplugin.WorkflowExecutionRequest, error) input *persistence.InternalWorkflowSnapshot validate func(*testing.T, *nosqlplugin.WorkflowExecutionRequest, error) }{ { name: "PrepareCreateWorkflowExecutionRequestWithMaps - Success", setupStore: func(store *nosqlExecutionStore) (*nosqlplugin.WorkflowExecutionRequest, error) { workflowSnapshot := &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, VersionHistories: &persistence.DataBlob{ Encoding: constants.EncodingTypeJSON, Data: []byte(`[{"Branches":[{"BranchID":"test-branch-id","BeginNodeID":1,"EndNodeID":2}]}]`), }, } return store.prepareCreateWorkflowExecutionRequestWithMaps(workflowSnapshot, FixedTime) }, input: &persistence.InternalWorkflowSnapshot{}, validate: func(t *testing.T, req *nosqlplugin.WorkflowExecutionRequest, err error) { assert.NoError(t, err) if err == nil { assert.NotNil(t, req) } }, }, { name: "PrepareCreateWorkflowExecutionRequestWithMaps - Nil Checksum", setupStore: func(store *nosqlExecutionStore) (*nosqlplugin.WorkflowExecutionRequest, error) { workflowSnapshot := &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, VersionHistories: &persistence.DataBlob{ Encoding: constants.EncodingTypeJSON, Data: []byte(`[{"Branches":[{"BranchID":"test-branch-id","BeginNodeID":1,"EndNodeID":2}]}]`), }, Checksum: checksum.Checksum{Value: nil}, } return store.prepareCreateWorkflowExecutionRequestWithMaps(workflowSnapshot, FixedTime) }, validate: func(t *testing.T, req *nosqlplugin.WorkflowExecutionRequest, err error) { assert.NoError(t, err) assert.NotNil(t, req.Checksums) }, }, { name: "PrepareCreateWorkflowExecutionRequestWithMaps - Empty VersionHistories", setupStore: func(store *nosqlExecutionStore) (*nosqlplugin.WorkflowExecutionRequest, error) { // Testing with an empty VersionHistories (which previously caused an error) workflowSnapshot := &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "test-domain-id-2", WorkflowID: "test-workflow-id-2", RunID: "test-run-id-2", }, VersionHistories: &persistence.DataBlob{ Encoding: constants.EncodingTypeJSON, Data: []byte("[]"), // Empty VersionHistories }, } return store.prepareCreateWorkflowExecutionRequestWithMaps(workflowSnapshot, FixedTime) }, validate: func(t *testing.T, req *nosqlplugin.WorkflowExecutionRequest, err error) { assert.NoError(t, err) assert.NotNil(t, req.VersionHistories) assert.Equal(t, "[]", string(req.VersionHistories.Data)) }, }, { name: "PrepareResetWorkflowExecutionRequestWithMapsAndEventBuffer - Success", setupStore: func(store *nosqlExecutionStore) (*nosqlplugin.WorkflowExecutionRequest, error) { resetWorkflow := &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "reset-domain-id", WorkflowID: "reset-workflow-id", RunID: "reset-run-id", }, LastWriteVersion: 123, Checksum: checksum.Checksum{Version: 1}, VersionHistories: &persistence.DataBlob{Encoding: constants.EncodingTypeJSON, Data: []byte(`[{"Branches":[{"BranchID":"reset-branch-id","BeginNodeID":1,"EndNodeID":2}]}]`)}, ActivityInfos: []*persistence.InternalActivityInfo{{ScheduleID: 1}}, TimerInfos: []*persistence.TimerInfo{{TimerID: "timerID"}}, ChildExecutionInfos: []*persistence.InternalChildExecutionInfo{ {InitiatedID: 1, StartedID: 2}, }, RequestCancelInfos: []*persistence.RequestCancelInfo{{InitiatedID: 1}}, SignalInfos: []*persistence.SignalInfo{{InitiatedID: 1}}, SignalRequestedIDs: []string{"signalRequestedID"}, Condition: 999, } return store.prepareResetWorkflowExecutionRequestWithMapsAndEventBuffer(resetWorkflow, FixedTime) }, validate: func(t *testing.T, req *nosqlplugin.WorkflowExecutionRequest, err error) { assert.NoError(t, err) assert.NotNil(t, req) assert.Equal(t, nosqlplugin.WorkflowExecutionMapsWriteModeReset, req.MapsWriteMode) assert.Equal(t, nosqlplugin.EventBufferWriteModeClear, req.EventBufferWriteMode) assert.Equal(t, int64(999), *req.PreviousNextEventIDCondition) }, }, { name: "PrepareResetWorkflowExecutionRequestWithMapsAndEventBuffer - Malformed VersionHistories", setupStore: func(store *nosqlExecutionStore) (*nosqlplugin.WorkflowExecutionRequest, error) { resetWorkflow := &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "domain-id-malformed-vh", WorkflowID: "workflow-id-malformed-vh", RunID: "run-id-malformed-vh", }, LastWriteVersion: 456, Checksum: checksum.Checksum{Version: 1}, VersionHistories: &persistence.DataBlob{Encoding: constants.EncodingTypeJSON, Data: []byte("{malformed}")}, } return store.prepareResetWorkflowExecutionRequestWithMapsAndEventBuffer(resetWorkflow, FixedTime) }, validate: func(t *testing.T, req *nosqlplugin.WorkflowExecutionRequest, err error) { assert.NoError(t, err) assert.NotNil(t, req) }, }, { name: "PrepareUpdateWorkflowExecutionRequestWithMapsAndEventBuffer - Successful Update Request Preparation", setupStore: func(store *nosqlExecutionStore) (*nosqlplugin.WorkflowExecutionRequest, error) { workflowMutation := &persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "domainID-success", WorkflowID: "workflowID-success", RunID: "runID-success", }, } return store.prepareUpdateWorkflowExecutionRequestWithMapsAndEventBuffer(workflowMutation, FixedTime) }, validate: func(t *testing.T, req *nosqlplugin.WorkflowExecutionRequest, err error) { assert.NoError(t, err) assert.NotNil(t, req) }, }, { name: "PrepareUpdateWorkflowExecutionRequestWithMapsAndEventBuffer - Incomplete WorkflowMutation", setupStore: func(store *nosqlExecutionStore) (*nosqlplugin.WorkflowExecutionRequest, error) { workflowMutation := &persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ // Partially populated for the test DomainID: "domainID-incomplete", }, } return store.prepareUpdateWorkflowExecutionRequestWithMapsAndEventBuffer(workflowMutation, FixedTime) }, validate: func(t *testing.T, req *nosqlplugin.WorkflowExecutionRequest, err error) { assert.NoError(t, err) assert.NotNil(t, req) assert.Equal(t, "domainID-incomplete", req.DomainID) // Example assertion }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(mockCtrl) store := newTestNosqlExecutionStore(mockDB, log.NewNoop()) req, err := tc.setupStore(store) tc.validate(t, req, err) }) } } func TestPrepareTasksForWorkflowTxn(t *testing.T) { testCases := []struct { name string setupMocks func(*serialization.MockTaskSerializer) setupStore func(*nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) validate func(*testing.T, []*nosqlplugin.HistoryMigrationTask, error) }{{ name: "PrepareTimerTasksForWorkflowTxn - Successful Timer Tasks Preparation", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTimer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("timer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { timerTasks := []persistence.Task{ &persistence.DecisionTimeoutTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now(), TaskID: 1, }, EventID: 2, TimeoutType: 1, ScheduleAttempt: 1}, } tasks, err := store.prepareTimerTasksForWorkflowTxn("domainID", "workflowID", "runID", timerTasks) assert.NoError(t, err) assert.NotEmpty(t, tasks) return nil, err }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) {}, }, { name: "PrepareTimerTasksForWorkflowTxn - Unsupported Timer Task Type", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) {}, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { timerTasks := []persistence.Task{ &dummyTaskType{ VisibilityTimestamp: time.Now(), TaskID: 1, }, } return store.prepareTimerTasksForWorkflowTxn("domainID-unsupported", "workflowID-unsupported", "runID-unsupported", timerTasks) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.Error(t, err) assert.Nil(t, tasks) }, }, { name: "PrepareTimerTasksForWorkflowTxn - Zero Tasks", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) {}, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { return store.prepareTimerTasksForWorkflowTxn("domainID", "workflowID", "runID", []persistence.Task{}) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Empty(t, tasks) }, }, { name: "PrepareTimerTasksForWorkflowTxn - ActivityTimeoutTask", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTimer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("timer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { timerTasks := []persistence.Task{ &persistence.ActivityTimeoutTask{ TaskData: persistence.TaskData{ Version: 1, TaskID: 2, VisibilityTimestamp: time.Now(), }, EventID: 3, Attempt: 2, }, } return store.prepareTimerTasksForWorkflowTxn("domainID", "workflowID", "runID", timerTasks) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) assert.Equal(t, int64(3), tasks[0].Timer.EventID) assert.Equal(t, int64(2), tasks[0].Timer.ScheduleAttempt) assert.Equal(t, []byte("timer"), tasks[0].Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, tasks[0].Task.Encoding) }, }, { name: "PrepareTimerTasksForWorkflowTxn - UserTimerTask", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTimer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("timer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { timerTasks := []persistence.Task{ &persistence.UserTimerTask{ TaskData: persistence.TaskData{ Version: 1, TaskID: 3, VisibilityTimestamp: time.Now(), }, EventID: 4, }, } return store.prepareTimerTasksForWorkflowTxn("domainID", "workflowID", "runID", timerTasks) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) assert.Equal(t, int64(4), tasks[0].Timer.EventID) assert.Equal(t, []byte("timer"), tasks[0].Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, tasks[0].Task.Encoding) }, }, { name: "PrepareTimerTasksForWorkflowTxn - ActivityRetryTimerTask", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTimer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("timer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { timerTasks := []persistence.Task{ &persistence.ActivityRetryTimerTask{ TaskData: persistence.TaskData{ Version: 1, TaskID: 4, VisibilityTimestamp: time.Now(), }, EventID: 5, Attempt: 3, }, } return store.prepareTimerTasksForWorkflowTxn("domainID", "workflowID", "runID", timerTasks) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) assert.Equal(t, int64(5), tasks[0].Timer.EventID) assert.Equal(t, int64(3), tasks[0].Timer.ScheduleAttempt) assert.Equal(t, []byte("timer"), tasks[0].Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, tasks[0].Task.Encoding) }, }, { name: "PrepareTimerTasksForWorkflowTxn - WorkflowBackoffTimerTask", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTimer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("timer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { timerTasks := []persistence.Task{ &persistence.WorkflowBackoffTimerTask{ TaskData: persistence.TaskData{ Version: 1, TaskID: 5, VisibilityTimestamp: time.Now(), }, }, } return store.prepareTimerTasksForWorkflowTxn("domainID", "workflowID", "runID", timerTasks) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) assert.Equal(t, []byte("timer"), tasks[0].Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, tasks[0].Task.Encoding) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(mockCtrl) mockTaskSerializer := serialization.NewMockTaskSerializer(mockCtrl) store := newTestNosqlExecutionStoreWithTaskSerializer(mockDB, log.NewNoop(), mockTaskSerializer) tc.setupMocks(mockTaskSerializer) tasks, err := tc.setupStore(store) tc.validate(t, tasks, err) }) } } func TestPrepareReplicationTasksForWorkflowTxn(t *testing.T) { testCases := []struct { name string setupMocks func(*serialization.MockTaskSerializer) setupStore func(*nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) validate func(*testing.T, []*nosqlplugin.HistoryMigrationTask, error) }{ { name: "Successful Replication Tasks Preparation", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryReplication, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("replication"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { replicationTasks := []persistence.Task{ &persistence.HistoryReplicationTask{ TaskData: persistence.TaskData{ Version: 1, }, }, } return store.prepareReplicationTasksForWorkflowTxn("domainID", "workflowID", "runID", replicationTasks) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.NotEmpty(t, tasks) }, }, { name: "Handling Unknown Replication Task Type", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) {}, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { replicationTasks := []persistence.Task{ &dummyTaskType{ VisibilityTimestamp: time.Now(), TaskID: -1, }, } return store.prepareReplicationTasksForWorkflowTxn("domainID", "workflowID", "runID", replicationTasks) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.Error(t, err) assert.Nil(t, tasks) }, }, { name: "PrepareReplicationTasksForWorkflowTxn - SyncActivityTask", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryReplication, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("replication"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { replicationTasks := []persistence.Task{ &persistence.SyncActivityTask{ TaskData: persistence.TaskData{ Version: 2, VisibilityTimestamp: time.Now(), TaskID: 2, }, ScheduledID: 123, }, } return store.prepareReplicationTasksForWorkflowTxn("domainID", "workflowID", "runID", replicationTasks) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) task := tasks[0] assert.Equal(t, persistence.ReplicationTaskTypeSyncActivity, task.Replication.TaskType) assert.Equal(t, int64(123), task.Replication.ScheduledID) assert.Equal(t, []byte("replication"), task.Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, task.Task.Encoding) }, }, { name: "PrepareReplicationTasksForWorkflowTxn - FailoverMarkerTask", setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryReplication, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("replication"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, setupStore: func(store *nosqlExecutionStore) ([]*nosqlplugin.HistoryMigrationTask, error) { replicationTasks := []persistence.Task{ &persistence.FailoverMarkerTask{ TaskData: persistence.TaskData{ Version: 3, VisibilityTimestamp: time.Now(), TaskID: 3, }, DomainID: "domainID", }, } return store.prepareReplicationTasksForWorkflowTxn("domainID", "workflowID", "runID", replicationTasks) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) task := tasks[0] assert.Equal(t, persistence.ReplicationTaskTypeFailoverMarker, task.Replication.TaskType) assert.Equal(t, "domainID", task.Replication.DomainID) assert.Equal(t, []byte("replication"), task.Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, task.Task.Encoding) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(mockCtrl) mockTaskSerializer := serialization.NewMockTaskSerializer(mockCtrl) store := newTestNosqlExecutionStoreWithTaskSerializer(mockDB, log.NewNoop(), mockTaskSerializer) tc.setupMocks(mockTaskSerializer) tasks, err := tc.setupStore(store) tc.validate(t, tasks, err) }) } } func TestPrepareTransferTasksForWorkflowTxn(t *testing.T) { testCases := []struct { name string tasks []persistence.Task domainID string workflowID string runID string setupMocks func(*serialization.MockTaskSerializer) validate func(*testing.T, []*nosqlplugin.HistoryMigrationTask, error) }{ { name: "CancelExecutionTask - Success", domainID: "domainID-cancel", workflowID: "workflowID-cancel", runID: "runID-cancel", tasks: []persistence.Task{ &persistence.CancelExecutionTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now(), TaskID: 1002, Version: 1, }, TargetDomainID: "targetDomainID-cancel", TargetWorkflowID: "targetWorkflowID-cancel", TargetRunID: "targetRunID-cancel", TargetChildWorkflowOnly: true, InitiatedID: 1002, }, }, setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("transfer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) task := tasks[0] assert.Equal(t, "targetDomainID-cancel", task.Transfer.TargetDomainID) assert.Equal(t, true, task.Transfer.TargetChildWorkflowOnly) assert.Equal(t, int64(1002), task.Transfer.TaskID) assert.Equal(t, int64(1), task.Transfer.Version) assert.Equal(t, []byte("transfer"), task.Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, task.Task.Encoding) }, }, { name: "ActivityTask - Success", domainID: "domainID-activity", workflowID: "workflowID-activity", runID: "runID-activity", tasks: []persistence.Task{ &persistence.ActivityTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now(), TaskID: 1001, Version: 1, }, TargetDomainID: "targetDomainID-activity", TaskList: "taskList-activity", ScheduleID: 1001, }, }, setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("transfer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) task := tasks[0] assert.Equal(t, persistence.TransferTaskTypeActivityTask, task.Transfer.TaskType) assert.Equal(t, "targetDomainID-activity", task.Transfer.TargetDomainID) assert.Equal(t, "taskList-activity", task.Transfer.TaskList) assert.Equal(t, int64(1001), task.Transfer.ScheduleID) assert.Equal(t, int64(1), task.Transfer.Version) assert.Equal(t, []byte("transfer"), task.Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, task.Task.Encoding) }, }, { name: "DefaultTargetRunID - When Empty", domainID: "domainID-default-runid", workflowID: "workflowID-default-runid", runID: "runID-default-runid", tasks: []persistence.Task{ &persistence.CancelExecutionTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now(), TaskID: 2001, Version: 1, }, TargetDomainID: "targetDomainID-cancel", TargetWorkflowID: "targetWorkflowID-cancel", TargetRunID: "", // Intentionally left empty to trigger the defaulting logic TargetChildWorkflowOnly: true, InitiatedID: 2001, }, &persistence.SignalExecutionTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now(), TaskID: 2002, Version: 1, }, TargetDomainID: "targetDomainID-signal", TargetWorkflowID: "targetWorkflowID-signal", TargetRunID: "", // Intentionally left empty to trigger the defaulting logic TargetChildWorkflowOnly: false, InitiatedID: 2002, }, }, setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("transfer"), Encoding: constants.EncodingTypeThriftRW, }, nil).Times(2) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) for _, task := range tasks { assert.Equal(t, persistence.TransferTaskTransferTargetRunID, task.Transfer.TargetRunID, "TargetRunID should default to TransferTaskTransferTargetRunID") assert.Equal(t, []byte("transfer"), task.Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, task.Task.Encoding) } }, }, { name: "SignalExecutionTask - Success", domainID: "domainID-signal", workflowID: "workflowID-signal", runID: "runID-signal", tasks: []persistence.Task{ &persistence.SignalExecutionTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now(), TaskID: 1003, Version: 1, }, TargetDomainID: "targetDomainID-signal", TargetWorkflowID: "targetWorkflowID-signal", TargetRunID: "targetRunID-signal", TargetChildWorkflowOnly: true, InitiatedID: 1003, }, }, setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("transfer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) task := tasks[0] assert.Equal(t, "targetDomainID-signal", task.Transfer.TargetDomainID) assert.Equal(t, true, task.Transfer.TargetChildWorkflowOnly) assert.Equal(t, int64(1003), task.Transfer.TaskID) assert.Equal(t, int64(1), task.Transfer.Version) assert.Equal(t, []byte("transfer"), task.Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, task.Task.Encoding) }, }, { name: "StartChildExecutionTask - Success", domainID: "domainID-start-child", workflowID: "workflowID-start-child", runID: "runID-start-child", tasks: []persistence.Task{ &persistence.StartChildExecutionTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now(), TaskID: 1004, Version: 1, }, TargetDomainID: "child-execution-domain-id", TargetWorkflowID: "child-workflow-id", InitiatedID: 1004, }, }, setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("transfer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) task := tasks[0] assert.Equal(t, "child-execution-domain-id", task.Transfer.TargetDomainID) assert.Equal(t, "child-workflow-id", task.Transfer.TargetWorkflowID) assert.Equal(t, int64(1004), task.Transfer.TaskID) assert.Equal(t, int64(1), task.Transfer.Version) assert.Equal(t, []byte("transfer"), task.Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, task.Task.Encoding) }, }, { name: "RecordChildExecutionCompletedTask - Success", domainID: "domainID-record-child", workflowID: "workflowID-record-child", runID: "runID-record-child", tasks: []persistence.Task{ &persistence.RecordChildExecutionCompletedTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now(), TaskID: 1005, Version: 1, }, TargetDomainID: "completed-child-domain-id", TargetWorkflowID: "completed-child-workflow-id", TargetRunID: "completed-child-run-id", }, }, setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("transfer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) task := tasks[0] assert.Equal(t, "completed-child-domain-id", task.Transfer.TargetDomainID) assert.Equal(t, "completed-child-workflow-id", task.Transfer.TargetWorkflowID) assert.Equal(t, "completed-child-run-id", task.Transfer.TargetRunID) assert.Equal(t, int64(1005), task.Transfer.TaskID) assert.Equal(t, int64(1), task.Transfer.Version) assert.Equal(t, []byte("transfer"), task.Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, task.Task.Encoding) }, }, { name: "DecisionTask - Success", domainID: "domainID-decision", workflowID: "workflowID-decision", runID: "runID-decision", tasks: []persistence.Task{ &persistence.DecisionTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now(), TaskID: 1001, Version: 1, }, TargetDomainID: "targetDomainID-decision", TaskList: "taskList-decision", ScheduleID: 1001, }, }, setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()). Return(persistence.DataBlob{ Data: []byte("transfer"), Encoding: constants.EncodingTypeThriftRW, }, nil) }, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.NoError(t, err) assert.Len(t, tasks, 1) task := tasks[0] assert.Equal(t, int64(1001), task.Transfer.TaskID) assert.Equal(t, "targetDomainID-decision", task.Transfer.TargetDomainID) assert.Equal(t, false, task.Transfer.RecordVisibility) assert.Equal(t, []byte("transfer"), task.Task.Data) assert.Equal(t, constants.EncodingTypeThriftRW, task.Task.Encoding) }, }, { name: "Unsupported Task Type", domainID: "domainID-unsupported", workflowID: "workflowID-unsupported", runID: "runID-unsupported", tasks: []persistence.Task{ &dummyTaskType{ VisibilityTimestamp: time.Now(), TaskID: 9999, }, }, setupMocks: func(mockTaskSerializer *serialization.MockTaskSerializer) {}, validate: func(t *testing.T, tasks []*nosqlplugin.HistoryMigrationTask, err error) { assert.Error(t, err) assert.Nil(t, tasks) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(mockCtrl) mockTaskSerializer := serialization.NewMockTaskSerializer(mockCtrl) store := newTestNosqlExecutionStoreWithTaskSerializer(mockDB, log.NewNoop(), mockTaskSerializer) tc.setupMocks(mockTaskSerializer) tasks, err := store.prepareTransferTasksForWorkflowTxn(tc.domainID, tc.workflowID, tc.runID, tc.tasks) tc.validate(t, tasks, err) }) } } func TestNosqlExecutionStoreUtilsExtended(t *testing.T) { testCases := []struct { name string setupStore func(store *nosqlExecutionStore) (interface{}, error) validate func(t *testing.T, result interface{}, err error) }{ { name: "PrepareActivityInfosForWorkflowTxn - Success", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { activityInfos := []*persistence.InternalActivityInfo{ { ScheduleID: 1, ScheduledEvent: persistence.NewDataBlob([]byte("scheduled event data"), constants.EncodingTypeThriftRW), StartedEvent: persistence.NewDataBlob([]byte("started event data"), constants.EncodingTypeThriftRW), }, } return store.prepareActivityInfosForWorkflowTxn(activityInfos) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) infos, ok := result.(map[int64]*persistence.InternalActivityInfo) assert.True(t, ok) assert.Len(t, infos, 1) for _, info := range infos { assert.NotNil(t, info.ScheduledEvent) assert.NotNil(t, info.StartedEvent) } }, }, { name: "PrepareTimerInfosForWorkflowTxn - Success", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { timerInfos := []*persistence.TimerInfo{ { TimerID: "timer1", }, } return store.prepareTimerInfosForWorkflowTxn(timerInfos) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) infos, ok := result.(map[string]*persistence.TimerInfo) assert.True(t, ok) assert.Len(t, infos, 1) assert.NotNil(t, infos["timer1"]) }, }, { name: "PrepareChildWFInfosForWorkflowTxn - Success", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { childWFInfos := []*persistence.InternalChildExecutionInfo{ { InitiatedID: 1, InitiatedEvent: persistence.NewDataBlob([]byte("initiated event data"), constants.EncodingTypeThriftRW), StartedEvent: persistence.NewDataBlob([]byte("started event data"), constants.EncodingTypeThriftRW), }, } return store.prepareChildWFInfosForWorkflowTxn(childWFInfos) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) infos, ok := result.(map[int64]*persistence.InternalChildExecutionInfo) assert.True(t, ok) assert.Len(t, infos, 1) for _, info := range infos { assert.NotNil(t, info.InitiatedEvent) assert.NotNil(t, info.StartedEvent) } }, }, { name: "PrepareTimerInfosForWorkflowTxn - Nil Timer Info", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { return store.prepareTimerInfosForWorkflowTxn(nil) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) assert.Empty(t, result) }, }, { name: "PrepareChildWFInfosForWorkflowTxn - Nil Child Execution Info", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { return store.prepareChildWFInfosForWorkflowTxn(nil) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) assert.Empty(t, result) }, }, { name: "PrepareChildWFInfosForWorkflowTxn - Encoding Mismatch Error", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { childWFInfos := []*persistence.InternalChildExecutionInfo{ { InitiatedID: 1, InitiatedEvent: persistence.NewDataBlob([]byte("initiated"), constants.EncodingTypeThriftRW), StartedEvent: persistence.NewDataBlob([]byte("started"), constants.EncodingTypeJSON), // Encoding mismatch }, } return store.prepareChildWFInfosForWorkflowTxn(childWFInfos) }, validate: func(t *testing.T, result interface{}, err error) { assert.Error(t, err) assert.Nil(t, result) }, }, { name: "PrepareRequestCancelsForWorkflowTxn - Success", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { requestCancels := []*persistence.RequestCancelInfo{ { InitiatedID: 1, CancelRequestID: "cancel-1", }, { InitiatedID: 2, CancelRequestID: "cancel-2", }, } cancels, err := store.prepareRequestCancelsForWorkflowTxn(requestCancels) return cancels, err }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) cancels := result.(map[int64]*persistence.RequestCancelInfo) assert.Equal(t, 2, len(cancels)) assert.Contains(t, cancels, int64(1)) assert.Contains(t, cancels, int64(2)) }, }, { name: "PrepareRequestCancelsForWorkflowTxn - Duplicate Initiated IDs", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { requestCancels := []*persistence.RequestCancelInfo{ { InitiatedID: 1, CancelRequestID: "cancel-1", }, { InitiatedID: 1, // Duplicate InitiatedID CancelRequestID: "cancel-1-duplicate", }, } cancels, err := store.prepareRequestCancelsForWorkflowTxn(requestCancels) return cancels, err }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) cancels := result.(map[int64]*persistence.RequestCancelInfo) assert.Equal(t, 1, len(cancels)) assert.Equal(t, "cancel-1-duplicate", cancels[1].CancelRequestID) }, }, { name: "PrepareSignalInfosForWorkflowTxn - Success", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { signalInfos := []*persistence.SignalInfo{ {InitiatedID: 1, SignalRequestID: "signal-1"}, {InitiatedID: 2, SignalRequestID: "signal-2"}, } return store.prepareSignalInfosForWorkflowTxn(signalInfos) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) infos := result.(map[int64]*persistence.SignalInfo) assert.Equal(t, 2, len(infos)) assert.Equal(t, "signal-1", infos[1].SignalRequestID) assert.Equal(t, "signal-2", infos[2].SignalRequestID) }, }, { name: "PrepareUpdateWorkflowExecutionTxn - Success", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { executionInfo := &persistence.InternalWorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, } versionHistories := &persistence.DataBlob{ Encoding: constants.EncodingTypeJSON, Data: []byte(`[{"Branches":[{"BranchID":"test-branch-id","BeginNodeID":1,"EndNodeID":2}]}]`), } checksum := checksum.Checksum{Version: 1, Value: []byte("create-checksum")} return store.prepareUpdateWorkflowExecutionTxn(executionInfo, versionHistories, checksum, time.Now(), 123) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) req := result.(*nosqlplugin.WorkflowExecutionRequest) assert.Equal(t, "test-domain-id", req.DomainID) assert.Equal(t, int64(123), req.LastWriteVersion) }, }, { name: "PrepareUpdateWorkflowExecutionTxn - Emptyvalues", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { executionInfo := &persistence.InternalWorkflowExecutionInfo{ DomainID: "", WorkflowID: "", State: persistence.WorkflowStateCompleted, } versionHistories := &persistence.DataBlob{ Encoding: constants.EncodingTypeJSON, Data: []byte(`[{"Branches":[{"BranchID":"branch-id","BeginNodeID":1,"EndNodeID":2}]}]`), } checksum := checksum.Checksum{Version: 1, Value: []byte("checksum")} // This should result in an error due to invalid executionInfo state for the creation scenario return store.prepareUpdateWorkflowExecutionTxn(executionInfo, versionHistories, checksum, time.Now(), 123) }, validate: func(t *testing.T, result interface{}, err error) { assert.Error(t, err) // Expect an error due to invalid state assert.Nil(t, result) }, }, { name: "PrepareUpdateWorkflowExecutionTxn - Invalid Workflow State", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { executionInfo := &persistence.InternalWorkflowExecutionInfo{ DomainID: "domainID-invalid-state", WorkflowID: "workflowID-invalid-state", RunID: "runID-invalid-state", State: 343, // Invalid state CloseStatus: persistence.WorkflowCloseStatusNone, } versionHistories := &persistence.DataBlob{ Encoding: constants.EncodingTypeJSON, Data: []byte(`[{"Branches":[{"BranchID":"branch-id","BeginNodeID":1,"EndNodeID":2}]}]`), } checksum := checksum.Checksum{Version: 1, Value: []byte("checksum")} return store.prepareUpdateWorkflowExecutionTxn(executionInfo, versionHistories, checksum, time.Now(), 123) }, validate: func(t *testing.T, result interface{}, err error) { assert.Error(t, err) // Expect an error due to invalid workflow state assert.Nil(t, result) // No WorkflowExecutionRequest should be returned }, }, { name: "PrepareCreateWorkflowExecutionTxn - Success", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { executionInfo := &persistence.InternalWorkflowExecutionInfo{ DomainID: "create-domain-id", WorkflowID: "create-workflow-id", RunID: "create-run-id", State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, } versionHistories := &persistence.DataBlob{ Encoding: constants.EncodingTypeJSON, Data: []byte(`[{"Branches":[{"BranchID":"create-branch-id","BeginNodeID":1,"EndNodeID":2}]}]`), } checksum := checksum.Checksum{Version: 1, Value: []byte("create-checksum")} return store.prepareCreateWorkflowExecutionTxn(executionInfo, versionHistories, checksum, time.Now(), 123) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) req := result.(*nosqlplugin.WorkflowExecutionRequest) assert.Equal(t, "create-domain-id", req.DomainID) assert.Equal(t, int64(123), req.LastWriteVersion) }, }, { name: "PrepareCreateWorkflowExecutionTxn - Invalid State", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { executionInfo := &persistence.InternalWorkflowExecutionInfo{ DomainID: "create-domain-id", WorkflowID: "create-workflow-id", RunID: "create-run-id", State: 232, // Invalid state for creating a workflow execution CloseStatus: persistence.WorkflowCloseStatusNone, } versionHistories := &persistence.DataBlob{ Encoding: constants.EncodingTypeJSON, Data: []byte(`[{"Branches":[{"BranchID":"create-branch-id","BeginNodeID":1,"EndNodeID":2}]}]`), } checksum := checksum.Checksum{Version: 1, Value: []byte("create-checksum")} return store.prepareCreateWorkflowExecutionTxn(executionInfo, versionHistories, checksum, time.Now(), 123) }, validate: func(t *testing.T, result interface{}, err error) { assert.Error(t, err) assert.Nil(t, result) }, }, { name: "prepareCurrentWorkflowRequestForCreateWorkflowTxn - BrandNew mode", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { executionInfo := &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, CreateRequestID: "test-create-request-id", } request := &persistence.InternalCreateWorkflowExecutionRequest{ Mode: persistence.CreateWorkflowModeBrandNew, } return store.prepareCurrentWorkflowRequestForCreateWorkflowTxn( "test-domain-id", "test-workflow-id", "test-run-id", executionInfo, 123, request) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) currentWorkflowReq, ok := result.(*nosqlplugin.CurrentWorkflowWriteRequest) assert.True(t, ok) assert.NotNil(t, currentWorkflowReq) assert.Equal(t, nosqlplugin.CurrentWorkflowWriteModeInsert, currentWorkflowReq.WriteMode) }, }, { name: "processUpdateWorkflowResult - CurrentWorkflowConditionFailInfo error", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { err := &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: common.StringPtr("current workflow condition failed"), } return nil, store.processUpdateWorkflowResult(err, 99) }, validate: func(t *testing.T, _ interface{}, err error) { assert.Error(t, err) _, ok := err.(*persistence.CurrentWorkflowConditionFailedError) assert.True(t, ok) }, }, { name: "processUpdateWorkflowResult - Success", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { return nil, store.processUpdateWorkflowResult(nil, 99) }, validate: func(t *testing.T, _ interface{}, err error) { assert.NoError(t, err) }, }, { name: "processUpdateWorkflowResult - ShardRangeIDNotMatch error", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { err := &nosqlplugin.WorkflowOperationConditionFailure{ ShardRangeIDNotMatch: common.Int64Ptr(100), } return nil, store.processUpdateWorkflowResult(err, 99) }, validate: func(t *testing.T, _ interface{}, err error) { assert.Error(t, err) _, ok := err.(*persistence.ShardOwnershipLostError) assert.True(t, ok) }, }, { name: "prepareCurrentWorkflowRequestForCreateWorkflowTxn - WorkflowIDReuse mode with non-completed state", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { executionInfo := &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateRunning, // Simulate a running state CloseStatus: persistence.WorkflowCloseStatusNone, CreateRequestID: "test-create-request-id", } request := &persistence.InternalCreateWorkflowExecutionRequest{ Mode: persistence.CreateWorkflowModeWorkflowIDReuse, PreviousRunID: "test-run-id", PreviousLastWriteVersion: 123, // Simulating a non-completed state with a valid version } return store.prepareCurrentWorkflowRequestForCreateWorkflowTxn( "test-domain-id", "test-workflow-id", "test-run-id", executionInfo, 123, request) }, validate: func(t *testing.T, result interface{}, err error) { _, ok := err.(*persistence.CurrentWorkflowConditionFailedError) assert.False(t, ok) }, }, { name: "CurrentWorkflowRequestForCreateWorkflowTxn - Zombie mode", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { executionInfo := &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, CreateRequestID: "create-request-id-zombie", } request := &persistence.InternalCreateWorkflowExecutionRequest{ Mode: persistence.CreateWorkflowModeZombie, PreviousRunID: "previous-run-id-zombie", } return store.prepareCurrentWorkflowRequestForCreateWorkflowTxn( "domain-id-zombie", "workflow-id-zombie", "run-id-zombie", executionInfo, 123, request) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) currentWorkflowReq := result.(*nosqlplugin.CurrentWorkflowWriteRequest) assert.Equal(t, nosqlplugin.CurrentWorkflowWriteModeNoop, currentWorkflowReq.WriteMode) assert.Equal(t, "create-request-id-zombie", currentWorkflowReq.Row.CreateRequestID) }, }, { name: "CurrentWorkflowRequestForCreateWorkflowTxn - ContinueAsNew mode", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { executionInfo := &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, CreateRequestID: "create-request-id-continueasnew", } request := &persistence.InternalCreateWorkflowExecutionRequest{ Mode: persistence.CreateWorkflowModeContinueAsNew, PreviousRunID: "previous-run-id-continueasnew", } return store.prepareCurrentWorkflowRequestForCreateWorkflowTxn( "domain-id-continueasnew", "workflow-id-continueasnew", "run-id-continueasnew", executionInfo, 123, request) }, validate: func(t *testing.T, result interface{}, err error) { assert.NoError(t, err) currentWorkflowReq := result.(*nosqlplugin.CurrentWorkflowWriteRequest) assert.Equal(t, nosqlplugin.CurrentWorkflowWriteModeUpdate, currentWorkflowReq.WriteMode) assert.Equal(t, "create-request-id-continueasnew", currentWorkflowReq.Row.CreateRequestID) assert.NotNil(t, currentWorkflowReq.Condition) assert.Equal(t, "previous-run-id-continueasnew", *currentWorkflowReq.Condition.CurrentRunID) }, }, { name: "assertNotCurrentExecution - Success with different RunID", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { ctx := context.Background() mockDB := store.db.(*nosqlplugin.MockDB) mockDB.EXPECT().SelectCurrentWorkflow( gomock.Any(), store.shardID, "test-domain-id", "test-workflow-id", ).Return(&nosqlplugin.CurrentWorkflowRow{ RunID: "different-run-id", }, nil) return nil, store.assertNotCurrentExecution(ctx, "test-domain-id", "test-workflow-id", "expected-run-id") }, validate: func(t *testing.T, _ interface{}, err error) { assert.NoError(t, err) }, }, { name: "assertNotCurrentExecution - No current workflow", setupStore: func(store *nosqlExecutionStore) (interface{}, error) { ctx := context.Background() mockDB := store.db.(*nosqlplugin.MockDB) mockDB.EXPECT().SelectCurrentWorkflow( gomock.Any(), store.shardID, "test-domain-id", "test-workflow-id", ).Return(nil, &types.EntityNotExistsError{}) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() return nil, store.assertNotCurrentExecution(ctx, "test-domain-id", "test-workflow-id", "expected-run-id") }, validate: func(t *testing.T, _ interface{}, err error) { assert.NoError(t, err) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(mockCtrl) store := newTestNosqlExecutionStore(mockDB, log.NewNoop()) result, err := tc.setupStore(store) tc.validate(t, result, err) }) } } type dummyTaskType struct { persistence.Task VisibilityTimestamp time.Time TaskID int64 } func (d *dummyTaskType) GetTaskType() int { return 999 // Using a type that is not expected by the switch statement } func (d *dummyTaskType) GetVersion() int64 { return 1 } func (d *dummyTaskType) SetVersion(version int64) {} func (d *dummyTaskType) ToTransferTaskInfo() (*persistence.TransferTaskInfo, error) { return nil, errors.New("not implemented") } func (d *dummyTaskType) ToTimerTaskInfo() (*persistence.TimerTaskInfo, error) { return nil, errors.New("not implemented") } func (d *dummyTaskType) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, errors.New("not implemented") } ================================================ FILE: common/persistence/nosql/nosql_history_store.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "fmt" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" persistenceutils "github.com/uber/cadence/common/persistence/persistence-utils" "github.com/uber/cadence/common/types" ) type nosqlHistoryStore struct { shardedNosqlStore } // newNoSQLHistoryStore is used to create an instance of HistoryStore implementation func newNoSQLHistoryStore( cfg config.ShardedNoSQL, logger log.Logger, metricsClient metrics.Client, dc *persistence.DynamicConfiguration, ) (persistence.HistoryStore, error) { s, err := newShardedNosqlStore(cfg, logger, metricsClient, dc) if err != nil { return nil, err } return &nosqlHistoryStore{ shardedNosqlStore: s, }, nil } // AppendHistoryNodes upsert a batch of events as a single node to a history branch // Note that it's not allowed to append above the branch's ancestors' nodes, which means nodeID >= ForkNodeID func (h *nosqlHistoryStore) AppendHistoryNodes( ctx context.Context, request *persistence.InternalAppendHistoryNodesRequest, ) error { branchInfo := request.BranchInfo beginNodeID := persistenceutils.GetBeginNodeID(branchInfo) if request.NodeID < beginNodeID { return &persistence.InvalidPersistenceRequestError{ Msg: "cannot append to ancestors' nodes", } } var treeRow *nosqlplugin.HistoryTreeRow if request.IsNewBranch { var ancestors []*types.HistoryBranchRange ancestors = append(ancestors, branchInfo.Ancestors...) treeRow = &nosqlplugin.HistoryTreeRow{ ShardID: request.ShardID, TreeID: branchInfo.TreeID, BranchID: branchInfo.BranchID, Ancestors: ancestors, CreateTimestamp: request.CurrentTimeStamp, Info: request.Info, } } nodeRow := &nosqlplugin.HistoryNodeRow{ TreeID: branchInfo.TreeID, BranchID: branchInfo.BranchID, NodeID: request.NodeID, TxnID: &request.TransactionID, Data: request.Events.Data, DataEncoding: string(request.Events.Encoding), ShardID: request.ShardID, CreateTimestamp: request.CurrentTimeStamp, } storeShard, err := h.GetStoreShardByHistoryShard(request.ShardID) if err != nil { return err } err = storeShard.db.InsertIntoHistoryTreeAndNode(ctx, treeRow, nodeRow) if err != nil { return convertCommonErrors(storeShard.db, "AppendHistoryNodes", err) } return nil } // ReadHistoryBranch returns history node data for a branch // NOTE: For branch that has ancestors, we need to query Cassandra multiple times, because it doesn't support OR/UNION operator func (h *nosqlHistoryStore) ReadHistoryBranch( ctx context.Context, request *persistence.InternalReadHistoryBranchRequest, ) (*persistence.InternalReadHistoryBranchResponse, error) { filter := &nosqlplugin.HistoryNodeFilter{ ShardID: request.ShardID, TreeID: request.TreeID, BranchID: request.BranchID, MinNodeID: request.MinNodeID, MaxNodeID: request.MaxNodeID, NextPageToken: request.NextPageToken, PageSize: request.PageSize, } storeShard, err := h.GetStoreShardByHistoryShard(request.ShardID) if err != nil { return nil, err } rows, pagingToken, err := storeShard.db.SelectFromHistoryNode(ctx, filter) if err != nil { return nil, convertCommonErrors(storeShard.db, "SelectFromHistoryNode", err) } history := make([]*persistence.DataBlob, 0, int(request.PageSize)) eventBlob := &persistence.DataBlob{} nodeID := int64(0) txnID := int64(0) lastNodeID := request.LastNodeID lastTxnID := request.LastTransactionID for _, row := range rows { nodeID = row.NodeID txnID = *row.TxnID eventBlob.Data = row.Data eventBlob.Encoding = constants.EncodingType(row.DataEncoding) if txnID < lastTxnID { // assuming that business logic layer is correct and transaction ID only increase // thus, valid event batch will come with increasing transaction ID // event batches with smaller node ID // -> should not be possible since records are already sorted // event batches with same node ID // -> batch with higher transaction ID is valid // event batches with larger node ID // -> batch with lower transaction ID is invalid (happens before) // -> batch with higher transaction ID is valid continue } switch { case nodeID < lastNodeID: return nil, &types.InternalDataInconsistencyError{ Message: "corrupted data, nodeID cannot decrease", } case nodeID == lastNodeID: return nil, &types.InternalDataInconsistencyError{ Message: "corrupted data, same nodeID must have smaller txnID", } default: // row.NodeID > lastNodeID: // NOTE: when row.nodeID > lastNodeID, we expect the one with largest txnID comes first lastTxnID = txnID lastNodeID = nodeID history = append(history, eventBlob) eventBlob = &persistence.DataBlob{} } } return &persistence.InternalReadHistoryBranchResponse{ History: history, NextPageToken: pagingToken, LastNodeID: lastNodeID, LastTransactionID: lastTxnID, }, nil } // ForkHistoryBranch forks a new branch from an existing branch // Note that application must provide a void forking nodeID, it must be a valid nodeID in that branch. // A valid forking nodeID can be an ancestor from the existing branch. // For example, we have branch B1 with three nodes(1[1,2], 3[3,4,5] and 6[6,7,8]. 1, 3 and 6 are nodeIDs (first eventID of the batch). // So B1 looks like this: // // 1[1,2] // / // 3[3,4,5] // / // 6[6,7,8] // // Assuming we have branch B2 which contains one ancestor B1 stopping at 6 (exclusive). So B2 inherit nodeID 1 and 3 from B1, and have its own nodeID 6 and 8. // Branch B2 looks like this: // // 1[1,2] // / // 3[3,4,5] // \ // 6[6,7] // \ // 8[8] // // Now we want to fork a new branch B3 from B2. // The only valid forking nodeIDs are 3,6 or 8. // 1 is not valid because we can't fork from first node. // 2/4/5 is NOT valid either because they are inside a batch. // // Case #1: If we fork from nodeID 6, then B3 will have an ancestor B1 which stops at 6(exclusive). // As we append a batch of events[6,7,8,9] to B3, it will look like : // // 1[1,2] // / // 3[3,4,5] // \ // 6[6,7,8,9] // // Case #2: If we fork from node 8, then B3 will have two ancestors: B1 stops at 6(exclusive) and ancestor B2 stops at 8(exclusive) // As we append a batch of events[8,9] to B3, it will look like: // // 1[1,2] // / // 3[3,4,5] // / // 6[6,7] // \ // 8[8,9] func (h *nosqlHistoryStore) ForkHistoryBranch( ctx context.Context, request *persistence.InternalForkHistoryBranchRequest, ) (*persistence.InternalForkHistoryBranchResponse, error) { forkB := request.ForkBranchInfo treeID := forkB.TreeID newAncestors := make([]*types.HistoryBranchRange, 0, len(forkB.Ancestors)+1) beginNodeID := persistenceutils.GetBeginNodeID(forkB) if beginNodeID >= request.ForkNodeID { // this is the case that new branch's ancestors doesn't include the forking branch for _, br := range forkB.Ancestors { if br.EndNodeID >= request.ForkNodeID { newAncestors = append(newAncestors, &types.HistoryBranchRange{ BranchID: br.BranchID, BeginNodeID: br.BeginNodeID, EndNodeID: request.ForkNodeID, }) break } else { newAncestors = append(newAncestors, br) } } } else { // this is the case the new branch will inherit all ancestors from forking branch newAncestors = forkB.Ancestors newAncestors = append(newAncestors, &types.HistoryBranchRange{ BranchID: forkB.BranchID, BeginNodeID: beginNodeID, EndNodeID: request.ForkNodeID, }) } resp := &persistence.InternalForkHistoryBranchResponse{ NewBranchInfo: types.HistoryBranch{ TreeID: treeID, BranchID: request.NewBranchID, Ancestors: newAncestors, }} var ancestors []*types.HistoryBranchRange for _, an := range newAncestors { anc := &types.HistoryBranchRange{ BranchID: an.BranchID, EndNodeID: an.EndNodeID, } ancestors = append(ancestors, anc) } treeRow := &nosqlplugin.HistoryTreeRow{ ShardID: request.ShardID, TreeID: treeID, BranchID: request.NewBranchID, Ancestors: ancestors, CreateTimestamp: request.CurrentTimeStamp, Info: request.Info, } storeShard, err := h.GetStoreShardByHistoryShard(request.ShardID) if err != nil { return nil, err } err = storeShard.db.InsertIntoHistoryTreeAndNode(ctx, treeRow, nil) if err != nil { return nil, convertCommonErrors(storeShard.db, "ForkHistoryBranch", err) } return resp, nil } // DeleteHistoryBranch removes a branch. This is responsible for: // // - Deleting entries from the history_node table // - Deleting the tree entry in the history_tree table // - Handling the problem of garbage-collecting branches which may mutually rely on history // // For normal workflows, with only a single branch, this is straightfoward, it'll just do a // delete on the history_node and history_tree tables. However, in cases where history is // branched, things are slightly more complex. History branches in at least two ways: // - During the conflict resolution handling of events during failover (where each // region is represented as a history branch at the point where they were operating concurrently. // - At the point of a workflow reset, where history is 'forked' and the previous // workflow events are discarded, but the original workflow run's history is referenced. // // For workflows with forked history, they'll reference any nodes they rely on ancestors in the history_tree // table. These references are exclusive, ie, the following is true: // // --------------+-------------------------------------- // tree_id | X // branch_id | B // ancestors | [{branch_id: A, end_node_id: 4}] // --------------+-------------------------------------- // tree_id | X // branch_id | A // ancestors | null // // Will have nodes arranged like this // // ┌────────────┐ // │ Branch: A │ // │ Node: 1 │ // └─────┬──────┘ // │ // ┌─────▼──────┐ // │ Branch: A │ // │ Node: 2 │ // └─────┬──────┘ // │ // ┌─────▼──────┐ // │ Branch: A │ // │ Node: 3 ┼────────────┐ // └─────┬──────┘ │ // │ │ // ┌─────▼──────┐ ┌──────▼─────┐ // │ Branch: A │ │ Branch: B │ // │ Node: 4 │ │ Node: 4 │ // └────────────┘ └────────────┘ // // The method of cleanup for each branch therefore, is to just remove the nodes that are not in use, // nearly-always starting from the oldest branch (the original run) and, later in subsequent invocations, // the branches relying on this. These cleanups are done by a call to the lower persistence layer // with HistoryNodeFilter references specifying the minimum nodes to be cleaned up (inclusive). // // While workflowIDs being enforced to be held by a single run to execute at any one time // generally ensures that any ancestor is cleaned up first, there's a couple of exceptions // to keep in mind. Firstly, deletion jitter makes it near-certain that there'll be some overlap and therefore // no guarantee that the oldest branch gets cleaned up first, as well as timer tasks are likely to retry and therefore // end up being out of order anyway. So any cleanup needs to be able to handle these cases. func (h *nosqlHistoryStore) DeleteHistoryBranch( ctx context.Context, request *persistence.InternalDeleteHistoryBranchRequest, ) error { branch := request.BranchInfo treeID := branch.TreeID brsToDelete := branch.Ancestors beginNodeID := persistenceutils.GetBeginNodeID(branch) brsToDelete = append(brsToDelete, &types.HistoryBranchRange{ BranchID: branch.BranchID, BeginNodeID: beginNodeID, }) rsp, err := h.GetHistoryTree(ctx, &persistence.InternalGetHistoryTreeRequest{ TreeID: treeID, ShardID: &request.ShardID, }) if err != nil { return err } treeFilter := &nosqlplugin.HistoryTreeFilter{ ShardID: request.ShardID, TreeID: treeID, BranchID: &branch.BranchID, } // This is the selection of history_nodes that will be removed // at this point it's not safe to delete any nodes, because we don't know // if this branch has other branches that've taken a dependency on it's nodes // so we start with an empty filter var nodeFilters []*nosqlplugin.HistoryNodeFilter // ... and then look at all the active / good history branches and see // what they're referencing. The idea being, to trim any nodes that // might not in use by going and finding the topmost nodes that are currently // referenced, and trimming those. // // validBRsMaxEndNode represents each branch range that is being used, // we want to know what is the max nodeID referred by other valid branches validBRsMaxEndNode := persistenceutils.GetBranchesMaxReferredNodeIDs(rsp.Branches) treesByBranchID := rsp.ByBranchID() // for each branch range to delete, we iterate from bottom to up, and delete up to the point according to validBRsEndNode // brsToDelete here includes both the current branch being operated on and its ancestors for i := len(brsToDelete) - 1; i >= 0; i-- { br := brsToDelete[i] maxReferredEndNodeID, branchIsReferenced := validBRsMaxEndNode[br.BranchID] isLastBranchRemaining := len(rsp.Branches) <= 1 _, treeExists := treesByBranchID[br.BranchID] if treeExists && br.BranchID != request.BranchInfo.BranchID { // the underlying assumption of this cleanup logic, is that cleanup will happen // from the oldest branch cleaning up first, to the more recent. // // However, this is by no means guaranteed, timers jitter on deletion intentionally // and so to avoid accidentally breaking valid ancestor branches with short-lived // branches doing cleanup, we'll stop the history node reaping here and let them // clean their history nodes when they're removed h.GetLogger().Debug(fmt.Sprintf("out of order deletion observed trying to clean up branch %q", br.BranchID), tag.WorkflowTreeID(request.BranchInfo.TreeID), tag.WorkflowBranchID(request.BranchInfo.BranchID)) break } if isLastBranchRemaining && branchIsReferenced { // special case, when this branch is being deleted is the last branch // and it's referring to multiple other previous (and now nonexisting branches) // then in this instance, delete all referenced branch nodes nodeFilter := &nosqlplugin.HistoryNodeFilter{ ShardID: request.ShardID, TreeID: treeID, BranchID: br.BranchID, } nodeFilters = append(nodeFilters, nodeFilter) } else if branchIsReferenced { // we can only delete from the maxEndNode and stop here nodeFilter := &nosqlplugin.HistoryNodeFilter{ ShardID: request.ShardID, TreeID: treeID, BranchID: br.BranchID, MinNodeID: maxReferredEndNodeID, } nodeFilters = append(nodeFilters, nodeFilter) break } else { // No any branch is using this range, we can delete all of it nodeFilter := &nosqlplugin.HistoryNodeFilter{ ShardID: request.ShardID, TreeID: treeID, BranchID: br.BranchID, MinNodeID: br.BeginNodeID, } nodeFilters = append(nodeFilters, nodeFilter) } } storeShard, err := h.GetStoreShardByHistoryShard(request.ShardID) if err != nil { return err } err = storeShard.db.DeleteFromHistoryTreeAndNode(ctx, treeFilter, nodeFilters) if err != nil { return convertCommonErrors(storeShard.db, "DeleteHistoryBranch", err) } return nil } func (h *nosqlHistoryStore) GetAllHistoryTreeBranches( ctx context.Context, request *persistence.GetAllHistoryTreeBranchesRequest, ) (*persistence.GetAllHistoryTreeBranchesResponse, error) { if h.GetShardingPolicy().hasShardedHistory { return nil, &types.InternalServiceError{ Message: "SelectAllHistoryTrees is not supported on sharded nosql db", } } storeShard := h.GetDefaultShard() dbBranches, pagingToken, err := storeShard.db.SelectAllHistoryTrees(ctx, request.NextPageToken, request.PageSize) if err != nil { return nil, convertCommonErrors(storeShard.db, "SelectAllHistoryTrees", err) } branchDetails := make([]persistence.HistoryBranchDetail, 0, int(request.PageSize)) for _, branch := range dbBranches { branchDetail := persistence.HistoryBranchDetail{ TreeID: branch.TreeID, BranchID: branch.BranchID, ForkTime: branch.CreateTimestamp, Info: branch.Info, } branchDetails = append(branchDetails, branchDetail) } response := &persistence.GetAllHistoryTreeBranchesResponse{ Branches: branchDetails, NextPageToken: pagingToken, } return response, nil } // GetHistoryTree returns all branch information of a tree func (h *nosqlHistoryStore) GetHistoryTree( ctx context.Context, request *persistence.InternalGetHistoryTreeRequest, ) (*persistence.InternalGetHistoryTreeResponse, error) { treeID := request.TreeID storeShard, err := h.GetStoreShardByHistoryShard(*request.ShardID) if err != nil { return nil, err } dbBranches, err := storeShard.db.SelectFromHistoryTree(ctx, &nosqlplugin.HistoryTreeFilter{ ShardID: *request.ShardID, TreeID: treeID, }) if err != nil { return nil, convertCommonErrors(storeShard.db, "SelectFromHistoryTree", err) } branches := make([]*types.HistoryBranch, 0) for _, dbBr := range dbBranches { br := &types.HistoryBranch{ TreeID: treeID, BranchID: dbBr.BranchID, Ancestors: dbBr.Ancestors, } branches = append(branches, br) } return &persistence.InternalGetHistoryTreeResponse{ Branches: branches, }, nil } ================================================ FILE: common/persistence/nosql/nosql_history_store_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package nosql import ( ctx "context" "errors" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) const ( testTransactionID = 123 testShardID = 456 testNodeID = 8 ) func validInternalAppendHistoryNodesRequest() *persistence.InternalAppendHistoryNodesRequest { return &persistence.InternalAppendHistoryNodesRequest{ IsNewBranch: false, Info: "TestInfo", BranchInfo: types.HistoryBranch{ TreeID: "TestTreeID", BranchID: "TestBranchID", Ancestors: []*types.HistoryBranchRange{ { BranchID: "TestAncestorBranchID", BeginNodeID: 0, EndNodeID: 5, }, }, }, NodeID: testNodeID, Events: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("TestEvents"), }, TransactionID: testTransactionID, ShardID: testShardID, CurrentTimeStamp: FixedTime, } } func validHistoryNodeRow() *nosqlplugin.HistoryNodeRow { expectedNodeRow := &nosqlplugin.HistoryNodeRow{ TreeID: "TestTreeID", BranchID: "TestBranchID", NodeID: testNodeID, TxnID: common.Ptr[int64](123), Data: []byte("TestEvents"), DataEncoding: string(constants.EncodingTypeThriftRW), ShardID: testShardID, CreateTimestamp: FixedTime, } return expectedNodeRow } func registerCassandraMock(t *testing.T) { ctrl := gomock.NewController(t) mockDB := nosqlplugin.NewMockDB(ctrl) mockPlugin := nosqlplugin.NewMockPlugin(ctrl) mockPlugin.EXPECT().CreateDB(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDB, nil).AnyTimes() RegisterPlugin("cassandra", mockPlugin) } func TestNewNoSQLHistoryStore(t *testing.T) { registerCassandraMock(t) cfg := getValidShardedNoSQLConfig() store, err := newNoSQLHistoryStore(cfg, log.NewNoop(), metrics.NewNoopMetricsClient(), nil) assert.NoError(t, err) assert.NotNil(t, store) } func setUpMocks(t *testing.T) (*nosqlHistoryStore, *nosqlplugin.MockDB, *MockshardedNosqlStore) { ctrl := gomock.NewController(t) dbMock := nosqlplugin.NewMockDB(ctrl) nosqlSt := nosqlStore{ logger: log.NewNoop(), db: dbMock, } shardedNosqlStoreMock := NewMockshardedNosqlStore(ctrl) shardedNosqlStoreMock.EXPECT().GetStoreShardByHistoryShard(testShardID).Return(&nosqlSt, nil).AnyTimes() shardedNosqlStoreMock.EXPECT().GetLogger().Return(log.NewNoop()).AnyTimes() store := &nosqlHistoryStore{ shardedNosqlStore: shardedNosqlStoreMock, } return store, dbMock, shardedNosqlStoreMock } func TestAppendHistoryNodes_ErrorIfAppendAbove(t *testing.T) { store, _, _ := setUpMocks(t) request := validInternalAppendHistoryNodesRequest() // If the nodeID to append is smaller than the last ancestor's end node ID, return an error request.NodeID = 3 ans := request.BranchInfo.Ancestors ans[len(ans)-1].EndNodeID = 5 err := store.AppendHistoryNodes(ctx.Background(), request) var invalidErr *persistence.InvalidPersistenceRequestError assert.ErrorAs(t, err, &invalidErr) assert.ErrorContains(t, err, "cannot append to ancestors' nodes") } func TestAppendHistoryNodes_NotNewBranch(t *testing.T) { store, dbMock, _ := setUpMocks(t) // Expect to insert the node into the history tree and node, as this is not a new branch, expect treeRow to be nil dbMock.EXPECT().InsertIntoHistoryTreeAndNode(gomock.Any(), nil, validHistoryNodeRow()).Return(nil).Times(1) request := validInternalAppendHistoryNodesRequest() err := store.AppendHistoryNodes(ctx.Background(), request) assert.NoError(t, err) } func TestAppendHistoryNodes_NewBranch(t *testing.T) { request := validInternalAppendHistoryNodesRequest() request.IsNewBranch = true store, dbMock, _ := setUpMocks(t) // Expect to insert the node into the history tree and node, as this is a new branch expect treeRow to be set dbMock.EXPECT().InsertIntoHistoryTreeAndNode(gomock.Any(), gomock.Any(), validHistoryNodeRow()). DoAndReturn(func(ctx ctx.Context, treeRow *nosqlplugin.HistoryTreeRow, nodeRow *nosqlplugin.HistoryNodeRow) error { // Assert that the treeRow is as expected assert.Equal(t, testShardID, treeRow.ShardID) assert.Equal(t, "TestTreeID", treeRow.TreeID) assert.Equal(t, "TestBranchID", treeRow.BranchID) assert.Equal(t, request.BranchInfo.Ancestors, treeRow.Ancestors) assert.Equal(t, request.Info, treeRow.Info) assert.Equal(t, FixedTime, treeRow.CreateTimestamp) return nil }) err := store.AppendHistoryNodes(ctx.Background(), request) assert.NoError(t, err) } const ( testMinNodeID = 111 testMaxNodeID = 222 testRequestLastNodeID = 333 testLastTransactionID = 444 // These needs to be greater than testRequestLastNodeID testRowNodeID1 = int64(334) testRowNodeID2 = int64(335) // These needs to be greater than testLastTransactionID testRowTxnID1 = int64(445) testRowTxnID2 = int64(446) ) func validInternalReadHistoryBranchRequest() *persistence.InternalReadHistoryBranchRequest { return &persistence.InternalReadHistoryBranchRequest{ TreeID: "TestTreeID", BranchID: "TestBranchID", MinNodeID: testMinNodeID, MaxNodeID: testMaxNodeID, PageSize: 0, NextPageToken: nil, LastNodeID: testRequestLastNodeID, LastTransactionID: testLastTransactionID, ShardID: testShardID, } } func expectedHistoryNodeFilter() *nosqlplugin.HistoryNodeFilter { return &nosqlplugin.HistoryNodeFilter{ ShardID: testShardID, TreeID: "TestTreeID", BranchID: "TestBranchID", MinNodeID: testMinNodeID, MaxNodeID: testMaxNodeID, NextPageToken: nil, PageSize: 0, } } func validHistoryNodeRows() []*nosqlplugin.HistoryNodeRow { return []*nosqlplugin.HistoryNodeRow{ { TreeID: "TestTreeID", BranchID: "TestBranchID", NodeID: testRowNodeID1, TxnID: common.Ptr(testRowTxnID1), Data: []byte("TestEvents"), DataEncoding: string(constants.EncodingTypeThriftRW), ShardID: testShardID, }, { TreeID: "TestTreeID", BranchID: "TestBranchID", NodeID: testRowNodeID2, TxnID: common.Ptr(testRowTxnID2), Data: []byte("TestEvents2"), DataEncoding: string(constants.EncodingTypeThriftRW), ShardID: testShardID, }, } } func TestReadHistoryBranch(t *testing.T) { store, dbMock, _ := setUpMocks(t) request := validInternalReadHistoryBranchRequest() rows := validHistoryNodeRows() // Append a rowID with a lower transaction ID to test that it is discarded badRow := *rows[0] badRow.TxnID = common.Ptr[int64](testLastTransactionID - 1) rows = append(rows, &badRow) // Expect to read the history branch dbMock.EXPECT().SelectFromHistoryNode(gomock.Any(), expectedHistoryNodeFilter()). Return(rows, nil, nil).Times(1) resp, err := store.ReadHistoryBranch(ctx.Background(), request) require.NoError(t, err) // Asset that we got the history for all the nodes assert.Equal(t, 2, len(resp.History)) assert.Equal(t, rows[0].Data, resp.History[0].Data) assert.Equal(t, rows[1].Data, resp.History[1].Data) assert.Equal(t, constants.EncodingTypeThriftRW, resp.History[0].Encoding) assert.Equal(t, constants.EncodingTypeThriftRW, resp.History[1].Encoding) assert.Nil(t, resp.NextPageToken) // Assert that these ids corresponds to the last node and transaction id assert.Equal(t, testRowNodeID2, resp.LastNodeID) assert.Equal(t, testRowTxnID2, resp.LastTransactionID) } func TestReadHistoryBranch_ErrorIfSelectFromHistoryNodeErrors(t *testing.T) { store, dbMock, _ := setUpMocks(t) request := validInternalReadHistoryBranchRequest() testError := fmt.Errorf("test error") dbMock.EXPECT().SelectFromHistoryNode(gomock.Any(), expectedHistoryNodeFilter()). Return(nil, nil, testError).Times(1) dbMock.EXPECT().IsNotFoundError(testError).Return(true).Times(1) _, err := store.ReadHistoryBranch(ctx.Background(), request) var notExistsErr *types.EntityNotExistsError assert.ErrorAs(t, err, ¬ExistsErr) assert.ErrorContains(t, err, "SelectFromHistoryNode") assert.ErrorContains(t, err, "test error") } func TestReadHistoryBranch_ErrorIfDecreasingNodeID(t *testing.T) { store, dbMock, _ := setUpMocks(t) request := validInternalReadHistoryBranchRequest() rows := validHistoryNodeRows() // Set the first row to have a node id that is less than the last node id in the request rows[0].NodeID = 1 // Expect to read the history branch dbMock.EXPECT().SelectFromHistoryNode(gomock.Any(), expectedHistoryNodeFilter()). Return(rows, nil, nil).Times(1) _, err := store.ReadHistoryBranch(ctx.Background(), request) var dataError *types.InternalDataInconsistencyError assert.ErrorAs(t, err, &dataError) assert.ErrorContains(t, err, "corrupted data, nodeID cannot decrease") } func TestReadHistoryBranch_ErrorIfSameNodeID(t *testing.T) { store, dbMock, _ := setUpMocks(t) request := validInternalReadHistoryBranchRequest() rows := validHistoryNodeRows() // Set the second row to have the same node id as the first row rows[1].NodeID = rows[0].NodeID // Expect to read the history branch dbMock.EXPECT().SelectFromHistoryNode(gomock.Any(), expectedHistoryNodeFilter()). Return(rows, nil, nil).Times(1) _, err := store.ReadHistoryBranch(ctx.Background(), request) var dataError *types.InternalDataInconsistencyError assert.ErrorAs(t, err, &dataError) assert.ErrorContains(t, err, "corrupted data, same nodeID must have smaller txnID") } func validInternalForkHistoryBranchRequest(forkNodeID int64) *persistence.InternalForkHistoryBranchRequest { return &persistence.InternalForkHistoryBranchRequest{ ForkBranchInfo: types.HistoryBranch{ TreeID: "TestTreeID", BranchID: "TestBranchID", Ancestors: []*types.HistoryBranchRange{ { BranchID: "TestAncestorBranchID", BeginNodeID: 0, EndNodeID: 5, }, { BranchID: "TestAncestorBranchID", BeginNodeID: 6, EndNodeID: 10, }, }, }, ForkNodeID: forkNodeID, NewBranchID: "TestNewBranchID", Info: "TestInfo", ShardID: testShardID, CurrentTimeStamp: FixedTime, } } func expectedInternalForkHistoryBranchResponse() *persistence.InternalForkHistoryBranchResponse { return &persistence.InternalForkHistoryBranchResponse{ NewBranchInfo: types.HistoryBranch{ TreeID: "TestTreeID", BranchID: "TestNewBranchID", Ancestors: []*types.HistoryBranchRange{ { BranchID: "TestAncestorBranchID", BeginNodeID: 0, EndNodeID: 5, }, { BranchID: "TestAncestorBranchID", BeginNodeID: 6, EndNodeID: 10, }, }, }, } } func expectedTreeRow() *nosqlplugin.HistoryTreeRow { return &nosqlplugin.HistoryTreeRow{ ShardID: testShardID, TreeID: "TestTreeID", BranchID: "TestNewBranchID", Ancestors: []*types.HistoryBranchRange{ { BranchID: "TestAncestorBranchID", EndNodeID: 5, }, { BranchID: "TestAncestorBranchID", EndNodeID: 10, }, }, CreateTimestamp: FixedTime, Info: "TestInfo", } } func treeRowEqual(t *testing.T, expected, actual *nosqlplugin.HistoryTreeRow) { t.Helper() assert.Equal(t, expected.ShardID, actual.ShardID) assert.Equal(t, expected.TreeID, actual.TreeID) assert.Equal(t, expected.BranchID, actual.BranchID) assert.Equal(t, expected.Ancestors, actual.Ancestors) assert.Equal(t, expected.Info, actual.Info) assert.Equal(t, FixedTime, actual.CreateTimestamp) } func TestForkHistoryBranch_NotAllAncestors(t *testing.T) { request := validInternalForkHistoryBranchRequest(8) expecedResp := expectedInternalForkHistoryBranchResponse() expTreeRow := expectedTreeRow() // The new branch ends at the fork node expecedResp.NewBranchInfo.Ancestors[1].EndNodeID = 8 expTreeRow.Ancestors[1].EndNodeID = 8 store, dbMock, _ := setUpMocks(t) // Expect to insert the new branch into the history tree dbMock.EXPECT().InsertIntoHistoryTreeAndNode(gomock.Any(), gomock.Any(), nil). DoAndReturn(func(ctx ctx.Context, treeRow *nosqlplugin.HistoryTreeRow, nodeRow *nosqlplugin.HistoryNodeRow) error { // Assert that the treeRow is as expected treeRowEqual(t, expTreeRow, treeRow) return nil }).Times(1) resp, err := store.ForkHistoryBranch(ctx.Background(), request) assert.NoError(t, err) assert.Equal(t, expecedResp, resp) } func TestForkHistoryBranch_AllAncestors(t *testing.T) { request := validInternalForkHistoryBranchRequest(14) expecedResp := expectedInternalForkHistoryBranchResponse() expTreeRow := expectedTreeRow() // The new branch inherits the ancestors from the fork node, and adds a new ancestor expecedResp.NewBranchInfo.Ancestors = append(expecedResp.NewBranchInfo.Ancestors, &types.HistoryBranchRange{ BranchID: "TestBranchID", BeginNodeID: 10, // The last in the fork node's ancestors EndNodeID: 14, // The fork node }) expTreeRow.Ancestors = append(expTreeRow.Ancestors, &types.HistoryBranchRange{ BranchID: "TestBranchID", EndNodeID: 14, }) store, dbMock, _ := setUpMocks(t) // Expect to insert the new branch into the history tree dbMock.EXPECT().InsertIntoHistoryTreeAndNode(gomock.Any(), gomock.Any(), nil). DoAndReturn(func(ctx ctx.Context, treeRow *nosqlplugin.HistoryTreeRow, nodeRow *nosqlplugin.HistoryNodeRow) error { // Assert that the treeRow is as expected treeRowEqual(t, expTreeRow, treeRow) return nil }).Times(1) resp, err := store.ForkHistoryBranch(ctx.Background(), request) assert.NoError(t, err) assert.Equal(t, expecedResp, resp) } func getValidInternalDeleteHistoryBranchRequest() *persistence.InternalDeleteHistoryBranchRequest { return &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "TestTreeID", BranchID: "TestBranchID", Ancestors: []*types.HistoryBranchRange{ { BranchID: "TestAncestorBranchID", BeginNodeID: 0, EndNodeID: 5, }, { BranchID: "TestAncestorBranchID", BeginNodeID: 6, EndNodeID: 10, }, }, }, ShardID: testShardID, } } func TestDeleteHistoryBranch_unusedBranch(t *testing.T) { store, dbMock, _ := setUpMocks(t) request := getValidInternalDeleteHistoryBranchRequest() expecedTreeFilter := &nosqlplugin.HistoryTreeFilter{ ShardID: testShardID, TreeID: "TestTreeID", BranchID: common.Ptr("TestBranchID"), } // Delete in reverse order, add 0 in the end expectedNodeFilters := []*nosqlplugin.HistoryNodeFilter{ { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "TestBranchID", MinNodeID: 10, }, { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "TestAncestorBranchID", MinNodeID: 6, }, { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "TestAncestorBranchID", MinNodeID: 0, }, } // Expect to delete the history branch dbMock.EXPECT().DeleteFromHistoryTreeAndNode(gomock.Any(), expecedTreeFilter, expectedNodeFilters). Return(nil).Times(1) dbMock.EXPECT().SelectFromHistoryTree(gomock.Any(), gomock.Any()). Return(nil, nil).Times(1) err := store.DeleteHistoryBranch(ctx.Background(), request) assert.NoError(t, err) } // In this base-case scenario, a workflow's been forked a few times, and the // parent / ancestor workflow (branch A) is being removed. // // Trees // ┌───────────────────┐ ┌───────────────────┐ ┌────────────────────┐ // │ Tree: 123 │ │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: A │ │ Branch: B │ // │ Ancestors: │ │ │ │ Ancestors: │ // │ Branch: A │ │ │ │ Branch A │ // │ EndNode: 3 │ │ │ │ EndNode: 2 │ // └───────────────────┘ └───────────────────┘ └────────────────────┘ // Nodes // ┌───────────────────┐ // │ Tree: 123 │ // │ Branch: A │ // │ Node: 1 │ // └─────────┬─────────┘ // │ // ┼───────────────────────┐ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: A │ │ Branch: B │ // │ Node: 2 │ │ Node: 2 │ // └─────────┬─────────┘ └─────────┬─────────┘ // ┌────────────────────────┤ │ // │ │ │ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: A │ │ Branch: B │ // │ Node: 3 │ │ Node: 3 │ │ Node: 3 │ // └─────────┬─────────┘ └─────────┬─────────┘ └─────────┬─────────┘ // │ │ │ // │ │ │ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: A │ │ Branch: B │ // │ Node: 4 │ │ Node: 4 │ │ Node: 4 │ // └───────────────────┘ └───────────────────┘ └───────────────────┘ // // The Expected behaviour is that the tree and unused nodes are trimmed off // but the referenced nodes from other branches are kept so those workflows // aren't broken. // // Trees // // ┌───────────────────┐ ┌─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐ ┌────────────────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ │ │ Branch: B │ // │ Ancestors: │ │ Ancestors: │ // │ Branch: A │ │ │ │ Branch A │ // │ EndNode: 3 │ │ EndNode: 2 │ // └───────────────────┘ └─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘ └────────────────────┘ // // Nodes // ┌───────────────────┐ // │ Tree: 123 │ // │ Branch: A │ // │ Node: 1 │ // └─────────┬─────────┘ // │ // ┼───────────────────────┐ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: A │ │ Branch: B │ // │ Node: 2 │ │ Node: 2 │ // └─────────┬─────────┘ └─────────┬─────────┘ // ┌────────────────────────┤ │ // │ │ │ // ┌─────────▼─────────┐ ┌─ ─ ─ ─ ─▼ ─ ─ ─ ─ ┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ │ │ Branch: B │ // │ Node: 3 │ │ Node: 3 │ // └─────────┬─────────┘ └─ ─ ─ ─ ─┐ ─ ─ ─ ─ ┘ └─────────┬─────────┘ // │ │ │ // │ │ │ // ┌─────────▼─────────┐ ┌─ ─ ─ ─ ─▼ ─ ─ ─ ─ ┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ │ │ Branch: B │ // │ Node: 4 │ │ Node: 4 │ // └───────────────────┘ └─ ─ ─ ─ ── ─ ─ ─ ─ ┘ └───────────────────┘ func TestDeleteHistoryBranchWithAFewBranches_baseCase(t *testing.T) { store, dbMock, _ := setUpMocks(t) request := &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "TestTreeID", BranchID: "A", }, ShardID: testShardID, } expectedTreeFilter := &nosqlplugin.HistoryTreeFilter{ ShardID: testShardID, TreeID: "TestTreeID", BranchID: common.Ptr("A"), } // Delete in reverse order, add 0 in the end expectedNodeFilters := []*nosqlplugin.HistoryNodeFilter{ { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "A", MinNodeID: 3, }, } dbMock.EXPECT().DeleteFromHistoryTreeAndNode(gomock.Any(), expectedTreeFilter, expectedNodeFilters). Return(nil).Times(1) historyTree := []*nosqlplugin.HistoryTreeRow{ { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "A", }, { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "B", Ancestors: []*types.HistoryBranchRange{ { BranchID: "A", BeginNodeID: 0, EndNodeID: 2, }, }, }, { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "C", Ancestors: []*types.HistoryBranchRange{ { BranchID: "A", BeginNodeID: 0, EndNodeID: 3, }, }, }, } dbMock.EXPECT().SelectFromHistoryTree(gomock.Any(), gomock.Any()). Return(historyTree, nil).Times(1) err := store.DeleteHistoryBranch(ctx.Background(), request) assert.NoError(t, err) } // In this scenario, Branch A has already been deleted, but is referenced by // a couple of other branches. // // In this scenario Branch B is being deleted. // // Given the following starting state: // // Trees // // ┌───────────────────┐ ┌─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐ ┌────────────────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ │ Ancestors: │ // │ Branch: A │ │ │ │ Branch A │ // │ EndNode: 3 │ │ EndNode: 2 │ // └───────────────────┘ └─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘ └────────────────────┘ // // Nodes // // ┌───────────────────┐ // │ Tree: 123 │ // │ Branch: A │ // │ Node: 1 │ // └─────────┬─────────┘ // │ // ┼─────────────────────────┐ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: A │ │ Branch: B │ // │ Node: 2 │ │ Node: 2 │ // └─────────┬─────────┘ └─────────┬─────────┘ // ┌────────────────────────┘ │ // │ │ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: B │ // │ Node: 3 │ │ Node: 3 │ // └─────────┬─────────┘ └─────────┬─────────┘ // │ │ // │ │ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: B │ // │ Node: 4 │ │ Node: 4 │ // └───────────────────┘ └───────────────────┘ // // The following is expected: It preserves the remaining nodes for any dependent // branches. // // Trees // // ┌───────────────────┐ ┌─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐ ┌─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐ // │ Tree: 123 │ // │ Branch: C │ │ deleted> // │ Branch: A │ │ │ │ │ // │ EndNode: 3 │ // └───────────────────┘ └─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘ └─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘ // // Nodes // ┌───────────────────┐ // │ Tree: 123 │ // │ Branch: A │ // │ Node: 1 │ // └─────────┬─────────┘ // │ // ┼ // ┌─────────▼─────────┐ // │ Tree: 123 │ // │ Branch: A │ // │ Node: 2 │ // └─────────┬─────────┘ // ┌────────────────────────┘ // │ // ┌─────────▼─────────┐ // │ Tree: 123 │ // │ Branch: C │ // │ Node: 3 │ // └─────────┬─────────┘ // │ // │ // ┌─────────▼─────────┐ // │ Tree: 123 │ // │ Branch: C │ // │ Node: 4 │ // └───────────────────┘ func TestDeleteHistoryBranch_DeletedAncestor(t *testing.T) { store, dbMock, _ := setUpMocks(t) request := &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "TestTreeID", BranchID: "B", Ancestors: []*types.HistoryBranchRange{ { BranchID: "A", BeginNodeID: 0, EndNodeID: 2, }, }, }, ShardID: testShardID, } expectedTreeFilter := &nosqlplugin.HistoryTreeFilter{ ShardID: testShardID, TreeID: "TestTreeID", BranchID: common.Ptr("B"), } expectedNodeFilters := []*nosqlplugin.HistoryNodeFilter{ { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "B", MinNodeID: 2, }, { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "A", MinNodeID: 3, }, } dbMock.EXPECT().DeleteFromHistoryTreeAndNode(gomock.Any(), expectedTreeFilter, expectedNodeFilters). Return(nil).Times(1) historyTree := []*nosqlplugin.HistoryTreeRow{ { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "B", Ancestors: []*types.HistoryBranchRange{ { BranchID: "A", BeginNodeID: 0, EndNodeID: 2, }, }, }, { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "C", Ancestors: []*types.HistoryBranchRange{ { BranchID: "A", BeginNodeID: 0, EndNodeID: 3, }, }, }, // notably A does not exist } dbMock.EXPECT().SelectFromHistoryTree(gomock.Any(), gomock.Any()). Return(historyTree, nil).Times(1) err := store.DeleteHistoryBranch(ctx.Background(), request) assert.NoError(t, err) } // In this scenario, something like the following has happened: // There was a normal, original workflow, which was then branched (perhaps by a reset). // By the time the workflow is being cleaned up, the ancestor branch has been deleted already, // and does not exist in the history_tree table as a valid branch. // // In this scenario, branch B is being deleted. Notably, Branch B is the *last* // valid branch for this tree, whereas the ancestor branches were removed earlier. // // Trees // // ┌─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐ ┌───────────────────┐ // │ Tree: 123 │ // │ │ Ancestors: │ // │ │ │ Branch: A │ // │ EndNode: 3 │ // └─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘ └───────────────────┘ // // // Nodes // // ┌───────────────────┐ // │ Tree: 123 │ // │ Branch: A │ // │ Node: 1 │ // └─────────┬─────────┘ // │ // ┌─────────┼─────────┐ // │ Tree: 123 │ // │ Branch: A │ // │ Node: 2 │ // └─────────┬─────────┘ // ┼────────────────────────┐ // │ // │ // ┌─────────▼─────────┐ // │ Tree: 123 │ // │ Branch: B │ // │ Node: 3 │ // └─────────┬─────────┘ // │ // │ // ┌─────────▼─────────┐ // │ Tree: 123 │ // │ Branch: B │ // │ Node: 4 │ // └───────────────────┘ // // The expected behaviour, is that the child/branched workflow needs to clean up both its own history nodes // but *all* of the parent's remaining and now unreferenced history nodes. They're otherwise unreachable // and will be just history_node table garbage which forever grows. // // Trees // // ┌─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐ ┌─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐ // │ │ // │ │ deleted> │ // │ │ // │ │ // └─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘ └─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘ // // // Nodes // // ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ // │ │ // │ │ // │ │ // └ ─ ─ ─ ─ ┌ ─ ─ ─ ─ ┘ // │ // ┌ ─ ─ ─ ─ ▼ ─ ─ ─ ─ ┐ // │ │ // │ │ // │ │ // └ ─ ─ ─ ─ ┌ ─ ─ ─ ─ ┘ // │ // ┼ ─ ─ ─ ─ ── ─ ── ─ ─ ── ┐ // │ │ // ┌ ─ ─ ─ ─ ▼ ─ ─ ─ ─ ┐ ┌─ ─ ─ ── ▼─ ─ ─ ─ ─┐ // │ │ │ │ // │ │ │ │ // │ │ │ │ // └ ─ ─ ─ ─ ┌ ─ ─ ─ ─ ┘ └─ ─ ─ ── ┌─ ─ ─ ─ ─┘ // │ │ // │ │ // ┌ ─ ─ ─ ─ ▼ ─ ─ ─ ─ ┐ ┌─ ─ ─ ── ▼─ ─ ─ ─ ─┐ // │ │ │ │ // │ │ │ │ // │ │ │ │ // └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └─ ─ ─ ── ── ─ ─ ─ ─┘ func TestDeleteHistoryBranch_usedBranchWithGarbageFullyCleanedUp(t *testing.T) { store, dbMock, _ := setUpMocks(t) request := &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "TestTreeID", BranchID: "B", Ancestors: []*types.HistoryBranchRange{ { BranchID: "A", EndNodeID: 3, }, }, }, ShardID: testShardID, } expectedTreeFilter := &nosqlplugin.HistoryTreeFilter{ ShardID: testShardID, TreeID: "TestTreeID", BranchID: common.Ptr("B"), } expectedNodeFilters := []*nosqlplugin.HistoryNodeFilter{ { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "B", MinNodeID: 3, }, { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "A", }, } dbMock.EXPECT().DeleteFromHistoryTreeAndNode(gomock.Any(), expectedTreeFilter, expectedNodeFilters). Return(nil).Times(1) historyTree := []*nosqlplugin.HistoryTreeRow{ { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "B", Ancestors: []*types.HistoryBranchRange{ { BranchID: "A", BeginNodeID: 0, EndNodeID: 3, }, }, }, // notably A does not exist } dbMock.EXPECT().SelectFromHistoryTree(gomock.Any(), gomock.Any()). Return(historyTree, nil).Times(1) err := store.DeleteHistoryBranch(ctx.Background(), request) assert.NoError(t, err) } // In this scenario, there's a few branches of a normal workflow. In this example, both history_trees exist // and the original branch has not yet been deleted, and (for whatever reason, the second branch is being removed // before the original/parent (it's not super obvious this might happen, but it's forseeable with deletion jitter // or maybe failover scenarios where this might happen). // // In this scenario, branch B is being deleted. // // Trees // // ┌───────────────────┐ ┌───────────────────┐ ┌────────────────────┐ // │ Tree: 123 │ │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: A │ │ Branch: B │ // │ Ancestors: │ │ │ │ Ancestors: │ // │ Branch: A │ │ │ │ Branch A │ // │ Endnode: 3 │ │ │ │ EndNode: 2 │ // └───────────────────┘ └───────────────────┘ └────────────────────┘ // // Nodes // // ┌───────────────────┐ // │ Tree: 123 │ // │ Branch: A │ // │ Node: 1 │ // └─────────┬─────────┘ // │ // ┼───────────────────────┐ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: A │ │ Branch: B │ // │ Node: 2 │ │ Node: 2 │ // └─────────┬─────────┘ └─────────┬─────────┘ // ┌────────────────────────┤ │ // │ │ │ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: A │ │ Branch: B │ // │ Node: 3 │ │ Node: 3 │ │ Node: 3 │ // └─────────┬─────────┘ └─────────┬─────────┘ └─────────┬─────────┘ // │ │ │ // │ │ │ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: A │ │ Branch: B │ // │ Node: 4 │ │ Node: 4 │ │ Node: 4 │ // └───────────────────┘ └───────────────────┘ └───────────────────┘ // // The expected behaviour is that the child/second branch should only clean up it's history nodes, but // leave the parents alone, so as to not break the parent. // // Trees // // ┌───────────────────┐ ┌───────────────────┐ ┌─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: A │ │ // │ Branch: A │ │ │ │ │ // │ EndNode: 2 │ │ │ // └───────────────────┘ └───────────────────┘ └─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘ // // // Nodes // // ┌───────────────────┐ // │ Tree: 123 │ // │ Branch: A │ // │ Node: 1 │ // └─────────┬─────────┘ // │ // ┼ // ┌─────────▼─────────┐ // │ Tree: 123 │ // │ Branch: A │ // │ Node: 2 │ // └─────────┬─────────┘ // ┌─────────────────────────┤ // │ │ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: A │ // │ Node: 3 │ │ Node: 3 │ // └─────────┬─────────┘ └─────────┬─────────┘ // │ │ // │ │ // ┌─────────▼─────────┐ ┌─────────▼─────────┐ // │ Tree: 123 │ │ Tree: 123 │ // │ Branch: C │ │ Branch: A │ // │ Node: 4 │ │ Node: 4 │ // └───────────────────┘ └───────────────────┘ func TestDeleteHistoryBranch_withAnAncestorBranchWhichIsStillInUse(t *testing.T) { store, dbMock, _ := setUpMocks(t) request := &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "TestTreeID", BranchID: "B", Ancestors: []*types.HistoryBranchRange{ { BranchID: "A", EndNodeID: 2, }, }, }, ShardID: testShardID, } expectedTreeFilter := &nosqlplugin.HistoryTreeFilter{ ShardID: testShardID, TreeID: "TestTreeID", BranchID: common.Ptr("B"), } expectedNodeFilters := []*nosqlplugin.HistoryNodeFilter{ { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "B", MinNodeID: 2, }, // we do not delete any of the ancestor, it's still valid } // Expect to delete the history branch dbMock.EXPECT().DeleteFromHistoryTreeAndNode(gomock.Any(), expectedTreeFilter, expectedNodeFilters). Return(nil).Times(1) historyTree := []*nosqlplugin.HistoryTreeRow{ { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "A", Ancestors: []*types.HistoryBranchRange{}, }, { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "B", Ancestors: []*types.HistoryBranchRange{ { BranchID: "A", EndNodeID: 2, }, }, }, { ShardID: testShardID, TreeID: "TestTreeID", BranchID: "C", Ancestors: []*types.HistoryBranchRange{ { BranchID: "A", BeginNodeID: 0, EndNodeID: 3, }, }, }, } dbMock.EXPECT().SelectFromHistoryTree(gomock.Any(), gomock.Any()). Return(historyTree, nil).Times(1) err := store.DeleteHistoryBranch(ctx.Background(), request) assert.NoError(t, err) } func TestGetAllHistoryTreeBranches(t *testing.T) { request := &persistence.GetAllHistoryTreeBranchesRequest{ NextPageToken: []byte("nextPageToken"), PageSize: 1000, } store, dbMock, shardedNoSQLStoreMock := setUpMocks(t) shardedNoSQLStoreMock.EXPECT().GetShardingPolicy().Return(shardingPolicy{hasShardedHistory: false}) shardedNoSQLStoreMock.EXPECT().GetDefaultShard().Return(nosqlStore{db: dbMock}).Times(1) expTreeRow := expectedTreeRow() // Create another tree row with some different data expTreeRow2 := expectedTreeRow() expTreeRow2.TreeID = "TestTreeID2" expTreeRow2.BranchID = "TestNewBranchID2" expTreeRow2.CreateTimestamp = time.Unix(123, 456) expTreeRow2.Info = "TestInfo2" expTreeRows := []*nosqlplugin.HistoryTreeRow{expTreeRow, expTreeRow2} dbMock.EXPECT().SelectAllHistoryTrees(gomock.Any(), request.NextPageToken, request.PageSize). Return(expTreeRows, []byte("anotherPageToken"), nil).Times(1) expectedBranches := []persistence.HistoryBranchDetail{ { TreeID: "TestTreeID", BranchID: "TestNewBranchID", ForkTime: expTreeRow.CreateTimestamp, Info: "TestInfo", }, { TreeID: "TestTreeID2", BranchID: "TestNewBranchID2", ForkTime: expTreeRow2.CreateTimestamp, Info: "TestInfo2", }, } expectedResponse := &persistence.GetAllHistoryTreeBranchesResponse{ Branches: expectedBranches, NextPageToken: []byte("anotherPageToken"), } resp, err := store.GetAllHistoryTreeBranches(ctx.Background(), request) assert.NoError(t, err) assert.Equal(t, expectedResponse, resp) } func TestGetAllHistoryTreeBranches_dbError(t *testing.T) { request := &persistence.GetAllHistoryTreeBranchesRequest{ NextPageToken: []byte("nextPageToken"), PageSize: 1000, } store, dbMock, shardedNoSQLStoreMock := setUpMocks(t) shardedNoSQLStoreMock.EXPECT().GetShardingPolicy().Return(shardingPolicy{hasShardedHistory: false}) shardedNoSQLStoreMock.EXPECT().GetDefaultShard().Return(nosqlStore{db: dbMock}).Times(1) testError := errors.New("TEST ERROR") dbMock.EXPECT().SelectAllHistoryTrees(gomock.Any(), request.NextPageToken, request.PageSize). Return(nil, nil, testError).Times(1) dbMock.EXPECT().IsNotFoundError(testError).Return(true).Times(1) _, err := store.GetAllHistoryTreeBranches(ctx.Background(), request) assert.Error(t, err) assert.ErrorContains(t, err, "TEST ERROR") assert.ErrorContains(t, err, "SelectAllHistoryTrees") } func TestGetAllHistoryTreeBranches_hasShardedPolicy(t *testing.T) { request := &persistence.GetAllHistoryTreeBranchesRequest{ NextPageToken: []byte("nextPageToken"), PageSize: 1000, } store, _, shardedNoSQLStoreMock := setUpMocks(t) shardedNoSQLStoreMock.EXPECT().GetShardingPolicy().Return(shardingPolicy{hasShardedHistory: true}) _, err := store.GetAllHistoryTreeBranches(ctx.Background(), request) assert.Error(t, err) var internalServiceErr *types.InternalServiceError assert.ErrorAs(t, err, &internalServiceErr) assert.Equal(t, "SelectAllHistoryTrees is not supported on sharded nosql db", internalServiceErr.Message) } ================================================ FILE: common/persistence/nosql/nosql_queue_store.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "fmt" "time" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) const ( emptyMessageID = -1 ) type nosqlQueueStore struct { queueType persistence.QueueType nosqlStore } func newNoSQLQueueStore( cfg config.ShardedNoSQL, logger log.Logger, metricsClient metrics.Client, queueType persistence.QueueType, dc *persistence.DynamicConfiguration, ) (persistence.QueueStore, error) { shardedStore, err := newShardedNosqlStore(cfg, logger, metricsClient, dc) if err != nil { return nil, err } queue := &nosqlQueueStore{ nosqlStore: shardedStore.GetDefaultShard(), queueType: queueType, } return queue, nil } func (q *nosqlQueueStore) ensureQueueMetadata( ctx context.Context, queueType persistence.QueueType, currentTimestamp time.Time, ) (*nosqlplugin.QueueMetadataRow, error) { queueMetadata, err := q.getQueueMetadata(ctx, queueType) if err != nil { return nil, err } if queueMetadata == nil { insertErr := q.insertInitialQueueMetadataRecord(ctx, queueType, currentTimestamp) if insertErr != nil { // If insert fails (likely because another thread just created it), // it attempts to read the metadata one more time. queueMetadata, err = q.getQueueMetadata(ctx, queueType) if err != nil { return nil, err } if queueMetadata == nil { return nil, insertErr } } else { queueMetadata, err = q.getQueueMetadata(ctx, queueType) if err != nil { return nil, err } if queueMetadata == nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("queue metadata not found for queue type %v", queueType), } } } } if queueMetadata.ClusterAckLevels == nil { queueMetadata.ClusterAckLevels = map[string]int64{} } return queueMetadata, nil } // Warning: This is not a safe concurrent operation in its current state. // It's only used for domain replication at the moment, but needs a conditional write guard // for concurrent use func (q *nosqlQueueStore) EnqueueMessage(ctx context.Context, request *persistence.InternalEnqueueMessageRequest) error { queueMetadata, err := q.ensureQueueMetadata(ctx, q.queueType, request.CurrentTimeStamp) if err != nil { return err } lastMessageID, err := q.getLastMessageID(ctx, q.queueType) if err != nil { return err } _, err = q.tryEnqueue(ctx, q.queueType, getNextID(queueMetadata.ClusterAckLevels, lastMessageID), request.MessagePayload, request.CurrentTimeStamp) return err } func (q *nosqlQueueStore) EnqueueMessageToDLQ(ctx context.Context, request *persistence.InternalEnqueueMessageToDLQRequest) error { if _, err := q.ensureQueueMetadata(ctx, q.getDLQTypeFromQueueType(), request.CurrentTimeStamp); err != nil { return err } // Use negative queue type as the dlq type lastMessageID, err := q.getLastMessageID(ctx, q.getDLQTypeFromQueueType()) if err != nil { return err } _, err = q.tryEnqueue(ctx, q.getDLQTypeFromQueueType(), lastMessageID+1, request.MessagePayload, request.CurrentTimeStamp) return err } func (q *nosqlQueueStore) tryEnqueue(ctx context.Context, queueType persistence.QueueType, messageID int64, messagePayload []byte, currentTimeStamp time.Time) (int64, error) { err := q.db.InsertIntoQueue(ctx, &nosqlplugin.QueueMessageRow{ QueueType: queueType, ID: messageID, Payload: messagePayload, CurrentTimeStamp: currentTimeStamp, }) if err != nil { if _, ok := err.(*nosqlplugin.ConditionFailure); ok { return emptyMessageID, &persistence.ConditionFailedError{Msg: fmt.Sprintf("message ID %v exists in queue", messageID)} } return emptyMessageID, convertCommonErrors(q.db, fmt.Sprintf("EnqueueMessage, Type: %v", queueType), err) } return messageID, nil } func (q *nosqlQueueStore) getLastMessageID( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { msgID, err := q.db.SelectLastEnqueuedMessageID(ctx, queueType) if err != nil { if q.db.IsNotFoundError(err) { return emptyMessageID, nil } return emptyMessageID, convertCommonErrors(q.db, fmt.Sprintf("GetLastMessageID, Type: %v", queueType), err) } return msgID, nil } func (q *nosqlQueueStore) ReadMessages( ctx context.Context, request *persistence.InternalReadMessagesRequest, ) (*persistence.InternalReadMessagesResponse, error) { messages, err := q.db.SelectMessagesFrom(ctx, q.queueType, request.LastMessageID, request.MaxCount) if err != nil { return nil, convertCommonErrors(q.db, "ReadMessages", err) } var result []*persistence.InternalQueueMessage for _, msg := range messages { result = append(result, &persistence.InternalQueueMessage{ ID: msg.ID, QueueType: q.queueType, Payload: msg.Payload, }) } return &persistence.InternalReadMessagesResponse{Messages: result}, nil } func (q *nosqlQueueStore) ReadMessagesFromDLQ( ctx context.Context, request *persistence.InternalReadMessagesFromDLQRequest, ) (*persistence.InternalReadMessagesFromDLQResponse, error) { response, err := q.db.SelectMessagesBetween(ctx, nosqlplugin.SelectMessagesBetweenRequest{ QueueType: q.getDLQTypeFromQueueType(), ExclusiveBeginMessageID: request.FirstMessageID, InclusiveEndMessageID: request.LastMessageID, PageSize: request.PageSize, NextPageToken: request.PageToken, }) if err != nil { return nil, convertCommonErrors(q.db, "ReadMessagesFromDLQ", err) } var result []*persistence.InternalQueueMessage for _, msg := range response.Rows { result = append(result, &persistence.InternalQueueMessage{ ID: msg.ID, QueueType: msg.QueueType, Payload: msg.Payload, }) } return &persistence.InternalReadMessagesFromDLQResponse{ Messages: result, NextPageToken: response.NextPageToken, }, nil } func (q *nosqlQueueStore) DeleteMessagesBefore( ctx context.Context, request *persistence.InternalDeleteMessagesBeforeRequest, ) error { if err := q.db.DeleteMessagesBefore(ctx, q.queueType, request.MessageID); err != nil { return convertCommonErrors(q.db, "DeleteMessagesBefore", err) } return nil } func (q *nosqlQueueStore) DeleteMessageFromDLQ( ctx context.Context, request *persistence.InternalDeleteMessageFromDLQRequest, ) error { // Use negative queue type as the dlq type if err := q.db.DeleteMessage(ctx, q.getDLQTypeFromQueueType(), request.MessageID); err != nil { return convertCommonErrors(q.db, "DeleteMessageFromDLQ", err) } return nil } func (q *nosqlQueueStore) RangeDeleteMessagesFromDLQ( ctx context.Context, request *persistence.InternalRangeDeleteMessagesFromDLQRequest, ) error { // Use negative queue type as the dlq type if err := q.db.DeleteMessagesInRange(ctx, q.getDLQTypeFromQueueType(), request.FirstMessageID, request.LastMessageID); err != nil { return convertCommonErrors(q.db, "RangeDeleteMessagesFromDLQ", err) } return nil } func (q *nosqlQueueStore) insertInitialQueueMetadataRecord(ctx context.Context, queueType persistence.QueueType, currentTimeStamp time.Time) error { version := int64(0) row := nosqlplugin.QueueMetadataRow{ Version: version, QueueType: queueType, CurrentTimeStamp: currentTimeStamp, } if err := q.db.InsertQueueMetadata(ctx, row); err != nil { return convertCommonErrors(q.db, fmt.Sprintf("InsertInitialQueueMetadataRecord, Type: %v", queueType), err) } return nil } func (q *nosqlQueueStore) UpdateAckLevel(ctx context.Context, request *persistence.InternalUpdateAckLevelRequest) error { return q.updateAckLevel(ctx, request.MessageID, request.ClusterName, q.queueType, request.CurrentTimeStamp) } func (q *nosqlQueueStore) GetAckLevels( ctx context.Context, _ *persistence.InternalGetAckLevelsRequest, ) (*persistence.InternalGetAckLevelsResponse, error) { queueMetadata, err := q.getQueueMetadata(ctx, q.queueType) if err != nil { return nil, err } if queueMetadata == nil { return &persistence.InternalGetAckLevelsResponse{AckLevels: map[string]int64{}}, nil } return &persistence.InternalGetAckLevelsResponse{AckLevels: queueMetadata.ClusterAckLevels}, nil } func (q *nosqlQueueStore) UpdateDLQAckLevel(ctx context.Context, request *persistence.InternalUpdateDLQAckLevelRequest) error { return q.updateAckLevel(ctx, request.MessageID, request.ClusterName, q.getDLQTypeFromQueueType(), request.CurrentTimeStamp) } func (q *nosqlQueueStore) GetDLQAckLevels( ctx context.Context, _ *persistence.InternalGetDLQAckLevelsRequest, ) (*persistence.InternalGetDLQAckLevelsResponse, error) { // Use negative queue type as the dlq type queueMetadata, err := q.getQueueMetadata(ctx, q.getDLQTypeFromQueueType()) if err != nil { return nil, err } if queueMetadata == nil { return &persistence.InternalGetDLQAckLevelsResponse{AckLevels: map[string]int64{}}, nil } return &persistence.InternalGetDLQAckLevelsResponse{AckLevels: queueMetadata.ClusterAckLevels}, nil } func (q *nosqlQueueStore) GetDLQSize( ctx context.Context, _ *persistence.InternalGetDLQSizeRequest, ) (*persistence.InternalGetDLQSizeResponse, error) { size, err := q.db.GetQueueSize(ctx, q.getDLQTypeFromQueueType()) if err != nil { return nil, convertCommonErrors(q.db, "GetDLQSize", err) } return &persistence.InternalGetDLQSizeResponse{Size: size}, nil } func (q *nosqlQueueStore) getQueueMetadata( ctx context.Context, queueType persistence.QueueType, ) (*nosqlplugin.QueueMetadataRow, error) { row, err := q.db.SelectQueueMetadata(ctx, queueType) if err != nil { if q.db.IsNotFoundError(err) { return nil, nil } return nil, convertCommonErrors(q.db, "GetQueueMetadata", err) } return row, nil } func (q *nosqlQueueStore) updateQueueMetadata( ctx context.Context, metadata *nosqlplugin.QueueMetadataRow, ) error { err := q.db.UpdateQueueMetadataCas(ctx, *metadata) if err != nil { if _, ok := err.(*nosqlplugin.ConditionFailure); ok { return &types.InternalServiceError{ Message: "UpdateQueueMetadata operation encounter concurrent write.", } } return convertCommonErrors(q.db, "UpdateQueueMetadata", err) } return nil } // DLQ type of is the negative of number of the non-DLQ func (q *nosqlQueueStore) getDLQTypeFromQueueType() persistence.QueueType { return -q.queueType } func (q *nosqlQueueStore) updateAckLevel( ctx context.Context, messageID int64, clusterName string, queueType persistence.QueueType, currentTimestamp time.Time, ) error { queueMetadata, err := q.ensureQueueMetadata(ctx, queueType, currentTimestamp) if err != nil { return err } // Ignore possibly delayed message if ackLevel, ok := queueMetadata.ClusterAckLevels[clusterName]; ok && ackLevel >= messageID { return nil } queueMetadata.ClusterAckLevels[clusterName] = messageID queueMetadata.Version++ queueMetadata.CurrentTimeStamp = currentTimestamp // Use negative queue type as the dlq type err = q.updateQueueMetadata(ctx, queueMetadata) if err != nil { return err } return nil } // if, for whatever reason, the ack-levels get ahead of the actual messages // then ensure the next ID follows func getNextID(acks map[string]int64, lastMessageID int64) int64 { o := lastMessageID for _, v := range acks { if v > o { o = v } } return o + 1 } ================================================ FILE: common/persistence/nosql/nosql_queue_store_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package nosql import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) const testQueueType = persistence.DomainReplicationQueueType const testDLQueueType = -testQueueType // type for dlq is always -MainQueueType var testPayload = []byte("test-message") type queueStoreTestData struct { mockDB *nosqlplugin.MockDB queue persistence.QueueStore } func newQueueStoreTestData(t *testing.T) *queueStoreTestData { var testData queueStoreTestData ctrl := gomock.NewController(t) testData.mockDB = nosqlplugin.NewMockDB(ctrl) mockPlugin := nosqlplugin.NewMockPlugin(ctrl) mockPlugin.EXPECT().CreateDB(gomock.Any(), gomock.Any(), gomock.Any()).Return(testData.mockDB, nil).AnyTimes() RegisterPluginForTest(t, "cassandra", mockPlugin) return &testData } func (td *queueStoreTestData) newQueueStore() (persistence.QueueStore, error) { cfg := getValidShardedNoSQLConfig() return newNoSQLQueueStore(cfg, log.NewNoop(), metrics.NewNoopMetricsClient(), testQueueType, nil) } func (td *queueStoreTestData) createValidQueueStore(t *testing.T) persistence.QueueStore { store, err := td.newQueueStore() require.NoError(t, err) require.NotNil(t, store) return store } func (td *queueStoreTestData) mockIsNotFoundErrCheck(err error, notfound bool) { td.mockDB.EXPECT().IsNotFoundError(err).Return(notfound) } func (td *queueStoreTestData) mockErrConversion(err error) { td.mockDB.EXPECT().IsNotFoundError(err).Return(false) td.mockDB.EXPECT().IsTimeoutError(err).Return(false) td.mockDB.EXPECT().IsDBUnavailableError(err).Return(false) td.mockDB.EXPECT().IsThrottlingError(err).Return(false) } func TestNewNoSQLQueueStore_Succeeds(t *testing.T) { td := newQueueStoreTestData(t) td.createValidQueueStore(t) } func TestEnqueueMessage_Succeeds(t *testing.T) { const lastMessageID = int64(123) td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() // clusterAckLevels are affecting lastMessageID clusterAckLevels := map[string]int64{"cluster1": lastMessageID + 10, "cluster2": lastMessageID + 20} td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType). Return(&nosqlplugin.QueueMetadataRow{ClusterAckLevels: clusterAckLevels}, nil) td.mockDB.EXPECT().SelectLastEnqueuedMessageID(ctx, testQueueType).Return(lastMessageID, nil) td.mockDB.EXPECT().InsertIntoQueue(ctx, gomock.Any()). Do(func(_ context.Context, row *nosqlplugin.QueueMessageRow) { assert.Equal( t, &nosqlplugin.QueueMessageRow{ QueueType: testQueueType, ID: lastMessageID + 20 + 1, // should be the max of cluster AckLevels + 1 Payload: testPayload, CurrentTimeStamp: FixedTime, }, row, ) }).Return(nil) require.NoError(t, store.EnqueueMessage(ctx, &persistence.InternalEnqueueMessageRequest{ MessagePayload: testPayload, CurrentTimeStamp: FixedTime, })) } func TestEnqueueMessage_Succeeds_CreateMetadataIfMissing(t *testing.T) { const lastMessageID = int64(123) td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType).Return(nil, nil) td.mockDB.EXPECT().InsertQueueMetadata(ctx, nosqlplugin.QueueMetadataRow{ QueueType: testQueueType, Version: 0, CurrentTimeStamp: FixedTime, }).Return(nil) td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType). Return(&nosqlplugin.QueueMetadataRow{ClusterAckLevels: map[string]int64{}}, nil) td.mockDB.EXPECT().SelectLastEnqueuedMessageID(ctx, testQueueType).Return(lastMessageID, nil) td.mockDB.EXPECT().InsertIntoQueue(ctx, gomock.Any()). Do(func(_ context.Context, row *nosqlplugin.QueueMessageRow) { assert.Equal(t, lastMessageID+1, row.ID) assert.Equal(t, testQueueType, row.QueueType) assert.Equal(t, testPayload, row.Payload) assert.Equal(t, FixedTime, row.CurrentTimeStamp) }).Return(nil) require.NoError(t, store.EnqueueMessage(ctx, &persistence.InternalEnqueueMessageRequest{ MessagePayload: testPayload, CurrentTimeStamp: FixedTime, })) } func TestEnqueueMessage_FailsIfCantSelectLastMessageID(t *testing.T) { errSelect := errors.New("failed to select message ID") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType). Return(&nosqlplugin.QueueMetadataRow{ClusterAckLevels: map[string]int64{}}, nil) td.mockDB.EXPECT().SelectLastEnqueuedMessageID(ctx, testQueueType).Return(int64(0), errSelect) td.mockIsNotFoundErrCheck(errSelect, false) td.mockErrConversion(errSelect) assert.ErrorContains(t, store.EnqueueMessage(ctx, &persistence.InternalEnqueueMessageRequest{ MessagePayload: testPayload, CurrentTimeStamp: FixedTime, }), errSelect.Error()) } func TestEnqueueMessage_FailsIfCantSelectQueueMetadata(t *testing.T) { errSelect := errors.New("fail to select queue metadata") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType).Return(nil, errSelect) td.mockIsNotFoundErrCheck(errSelect, false) td.mockErrConversion(errSelect) assert.ErrorContains(t, store.EnqueueMessage(ctx, &persistence.InternalEnqueueMessageRequest{ MessagePayload: testPayload, CurrentTimeStamp: FixedTime, }), errSelect.Error()) } func TestEnqueueMessage_FailsIfCantInsertMetadata(t *testing.T) { errInsert := errors.New("insert main-queue metadata failed") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType).Return(nil, nil) td.mockDB.EXPECT().InsertQueueMetadata(ctx, gomock.Any()).Return(errInsert) td.mockErrConversion(errInsert) td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType).Return(nil, nil) assert.ErrorContains(t, store.EnqueueMessage(ctx, &persistence.InternalEnqueueMessageRequest{ MessagePayload: testPayload, CurrentTimeStamp: FixedTime, }), errInsert.Error()) } func TestEnqueueMessage_FailsIfCantInsertMessageToQueue(t *testing.T) { errInsert := errors.New("fail to insert into queue") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectLastEnqueuedMessageID(ctx, testQueueType).Return(int64(0), nil) td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType). Return(&nosqlplugin.QueueMetadataRow{}, nil) td.mockDB.EXPECT().InsertIntoQueue(ctx, gomock.Any()).Return(errInsert) td.mockErrConversion(errInsert) assert.ErrorContains(t, store.EnqueueMessage(ctx, &persistence.InternalEnqueueMessageRequest{ MessagePayload: testPayload, CurrentTimeStamp: FixedTime, }), errInsert.Error()) } func TestEnqueueMessageToDLQ_Succeeds(t *testing.T) { const dlqMessageType = -testQueueType lastMessageID := int64(123) td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectQueueMetadata(ctx, dlqMessageType). Return(&nosqlplugin.QueueMetadataRow{ClusterAckLevels: map[string]int64{}}, nil) td.mockDB.EXPECT().SelectLastEnqueuedMessageID(ctx, dlqMessageType).Return(lastMessageID, nil) td.mockDB.EXPECT().InsertIntoQueue(ctx, gomock.Any()). Do(func(_ context.Context, row *nosqlplugin.QueueMessageRow) { assert.Equal( t, &nosqlplugin.QueueMessageRow{ QueueType: dlqMessageType, ID: lastMessageID + 1, Payload: testPayload, CurrentTimeStamp: FixedTime, }, row, ) }).Return(nil) require.NoError(t, store.EnqueueMessageToDLQ(ctx, &persistence.InternalEnqueueMessageToDLQRequest{ MessagePayload: testPayload, CurrentTimeStamp: FixedTime, })) } func TestEnqueueMessageToDLQ_FailsIfCantSelectLastMessageID(t *testing.T) { errSelect := errors.New("failed to select message ID") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectQueueMetadata(ctx, testDLQueueType). Return(&nosqlplugin.QueueMetadataRow{ClusterAckLevels: map[string]int64{}}, nil) td.mockDB.EXPECT().SelectLastEnqueuedMessageID(ctx, testDLQueueType).Return(int64(0), errSelect) td.mockIsNotFoundErrCheck(errSelect, false) td.mockErrConversion(errSelect) assert.ErrorContains(t, store.EnqueueMessageToDLQ(ctx, &persistence.InternalEnqueueMessageToDLQRequest{ MessagePayload: testPayload, CurrentTimeStamp: FixedTime, }), errSelect.Error()) } func TestEnqueueMessageToDLQ_FailsIfCantInsertMessageToQueue(t *testing.T) { errInsert := errors.New("fail to insert into queue") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectQueueMetadata(ctx, testDLQueueType). Return(&nosqlplugin.QueueMetadataRow{ClusterAckLevels: map[string]int64{}}, nil) td.mockDB.EXPECT().SelectLastEnqueuedMessageID(ctx, testDLQueueType).Return(int64(0), nil) td.mockDB.EXPECT().InsertIntoQueue(ctx, gomock.Any()).Return(errInsert) td.mockErrConversion(errInsert) assert.ErrorContains(t, store.EnqueueMessageToDLQ(ctx, &persistence.InternalEnqueueMessageToDLQRequest{ MessagePayload: testPayload, CurrentTimeStamp: FixedTime, }), errInsert.Error()) } func TestReadMessages_Succeeds(t *testing.T) { const lastMessageID = int64(123) td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() messages := []*nosqlplugin.QueueMessageRow{ { QueueType: testQueueType, ID: 124, Payload: []byte("message-124"), }, { QueueType: testQueueType, ID: 125, Payload: []byte("message-125"), }, } td.mockDB.EXPECT().SelectMessagesFrom(ctx, testQueueType, lastMessageID, len(messages)).Return(messages, nil) res, err := store.ReadMessages(ctx, &persistence.InternalReadMessagesRequest{ LastMessageID: lastMessageID, MaxCount: len(messages), }) require.NoError(t, err) resMessages := res.Messages // resMessages has different type than messages, should compare explicitly assert.Len(t, resMessages, len(messages), "should match the amount of messages returned by SelectMessagesFrom()") for i := range messages { assert.Equal(t, messages[i].QueueType, resMessages[i].QueueType) assert.Equal(t, messages[i].ID, resMessages[i].ID) assert.Equal(t, messages[i].Payload, resMessages[i].Payload) } } func TestReadMessages_FailsIfCantSelectMessages(t *testing.T) { errSelect := errors.New("failed to select messages") const lastMessageID = int64(123) td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectMessagesFrom(ctx, testQueueType, lastMessageID, 2).Return(nil, errSelect) td.mockErrConversion(errSelect) res, err := store.ReadMessages(ctx, &persistence.InternalReadMessagesRequest{ LastMessageID: lastMessageID, MaxCount: 2, }) assert.ErrorContains(t, err, errSelect.Error()) assert.Nil(t, res) } func TestReadMessagesFromDLQ_Succeeds(t *testing.T) { const firsMessageID = int64(123) const lastMessageID = int64(200) // doesn't matter, we will still request max=2 var pageToken = []byte("page-token") var nextPageToken = []byte("next-page-token") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() messages := []nosqlplugin.QueueMessageRow{ { QueueType: testQueueType, ID: 124, Payload: []byte("message-123"), }, { QueueType: testQueueType, ID: 125, Payload: []byte("message-124"), }, } td.mockDB.EXPECT().SelectMessagesBetween(ctx, gomock.Any()).Do( func(_ context.Context, req nosqlplugin.SelectMessagesBetweenRequest) { expectedReq := nosqlplugin.SelectMessagesBetweenRequest{ QueueType: testDLQueueType, ExclusiveBeginMessageID: firsMessageID, InclusiveEndMessageID: lastMessageID, PageSize: len(messages), NextPageToken: pageToken, } assert.Equal(t, expectedReq, req) }).Return(&nosqlplugin.SelectMessagesBetweenResponse{NextPageToken: nextPageToken, Rows: messages}, nil) res, err := store.ReadMessagesFromDLQ(ctx, &persistence.InternalReadMessagesFromDLQRequest{ FirstMessageID: firsMessageID, LastMessageID: lastMessageID, PageSize: len(messages), PageToken: pageToken, }) require.NoError(t, err) resMessages := res.Messages resPageToken := res.NextPageToken // resMessages has different type than messages, should compare explicitly assert.Len(t, resMessages, 2, "should match the amount of messages returned by SelectMessagesFrom()") for i := range messages { assert.Equal(t, messages[i].QueueType, resMessages[i].QueueType) assert.Equal(t, messages[i].ID, resMessages[i].ID) assert.Equal(t, messages[i].Payload, resMessages[i].Payload) } assert.Equal(t, nextPageToken, resPageToken) } func TestReadMessagesFromDLQ_FailsIfSelectMessagesFails(t *testing.T) { errSelect := errors.New("failed to select messages") const firsMessageID = int64(123) const lastMessageID = int64(200) // doesn't matter, we will still request max=2 const pageSize = 2 var pageToken = []byte("page-token") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectMessagesBetween(ctx, gomock.Any()).Return(nil, errSelect) td.mockErrConversion(errSelect) res, err := store.ReadMessagesFromDLQ(ctx, &persistence.InternalReadMessagesFromDLQRequest{ FirstMessageID: firsMessageID, LastMessageID: lastMessageID, PageSize: pageSize, PageToken: pageToken, }) assert.ErrorContains(t, err, errSelect.Error()) assert.Nil(t, res) } func TestDeleteMessagesBefore_Succeeds(t *testing.T) { messageID := int64(123) td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().DeleteMessagesBefore(ctx, testQueueType, messageID).Return(nil) assert.NoError(t, store.DeleteMessagesBefore(ctx, &persistence.InternalDeleteMessagesBeforeRequest{ MessageID: messageID, })) } func TestDeleteMessagesBefore_FailsIfDeleteFails(t *testing.T) { errDelete := errors.New("failed to delete messages") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().DeleteMessagesBefore(ctx, testQueueType, gomock.Any()).Return(errDelete) td.mockErrConversion(errDelete) assert.ErrorContains(t, store.DeleteMessagesBefore(ctx, &persistence.InternalDeleteMessagesBeforeRequest{ MessageID: 0, }), errDelete.Error()) } func TestDeleteMessageFromDLQ_Succeeds(t *testing.T) { messageID := int64(123) td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().DeleteMessage(ctx, testDLQueueType, messageID).Return(nil) assert.NoError(t, store.DeleteMessageFromDLQ(ctx, &persistence.InternalDeleteMessageFromDLQRequest{ MessageID: messageID, })) } func TestDeleteMessageFromDLQ_FailsIfDeleteFails(t *testing.T) { errDelete := errors.New("failed to delete messages") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().DeleteMessage(ctx, testDLQueueType, gomock.Any()).Return(errDelete) td.mockErrConversion(errDelete) assert.ErrorContains(t, store.DeleteMessageFromDLQ(ctx, &persistence.InternalDeleteMessageFromDLQRequest{ MessageID: 0, }), errDelete.Error()) } func TestRangeDeleteMessagesFromDLQ_Succeeds(t *testing.T) { const fistMessageID = int64(123) const lastMessageID = int64(130) td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().DeleteMessagesInRange(ctx, testDLQueueType, fistMessageID, lastMessageID).Return(nil) assert.NoError(t, store.RangeDeleteMessagesFromDLQ(ctx, &persistence.InternalRangeDeleteMessagesFromDLQRequest{ FirstMessageID: fistMessageID, LastMessageID: lastMessageID, })) } func TestRangeDeleteMessagesFromDLQ_FailsIfDeleteFails(t *testing.T) { const fistMessageID = int64(123) const lastMessageID = int64(130) errDelete := errors.New("failed to delete messages") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().DeleteMessagesInRange(ctx, testDLQueueType, gomock.Any(), gomock.Any()).Return(errDelete) td.mockErrConversion(errDelete) assert.ErrorContains( t, store.RangeDeleteMessagesFromDLQ(ctx, &persistence.InternalRangeDeleteMessagesFromDLQRequest{ FirstMessageID: fistMessageID, LastMessageID: lastMessageID, }), errDelete.Error(), ) } func TestUpdateAckLevel_Succeeds(t *testing.T) { const messageID = 123 const clusterName = "test-cluster" td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() metadata := nosqlplugin.QueueMetadataRow{ QueueType: testQueueType, ClusterAckLevels: map[string]int64{ clusterName: 110, "unrelated-cluster": 300, }, Version: 3, } td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType).Return(&metadata, nil) td.mockDB.EXPECT().UpdateQueueMetadataCas(ctx, gomock.Any()). Do(func(_ context.Context, newMeta nosqlplugin.QueueMetadataRow) { assert.Equal(t, int64(4), newMeta.Version, "version should be incremented") assert.Equal(t, testQueueType, newMeta.QueueType, "type should remain the same") expectedClusterAckLevels := map[string]int64{ clusterName: messageID, // messageID is greater "unrelated-cluster": 300, } // only target cluster ack-level should be updated assert.Equal(t, expectedClusterAckLevels, newMeta.ClusterAckLevels) }).Return(nil) assert.NoError(t, store.UpdateAckLevel(ctx, &persistence.InternalUpdateAckLevelRequest{ MessageID: messageID, ClusterName: clusterName, CurrentTimeStamp: FixedTime, })) } func TestUpdateAckLevel_FailsIfSelectMetadataFails(t *testing.T) { errSelect := errors.New("select metadata failed") const messageID = 123 const clusterName = "test-cluster" td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType).Return(nil, errSelect) td.mockIsNotFoundErrCheck(errSelect, false) td.mockErrConversion(errSelect) assert.ErrorContains(t, store.UpdateAckLevel(ctx, &persistence.InternalUpdateAckLevelRequest{ MessageID: messageID, ClusterName: clusterName, CurrentTimeStamp: FixedTime, }), errSelect.Error()) } func TestUpdateAckLevel_FailsIfUpdateMetadataFails(t *testing.T) { errUpdate := errors.New("update metadata failed") const messageID = 123 const clusterName = "test-cluster" td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() emptyMetadata := &nosqlplugin.QueueMetadataRow{ClusterAckLevels: map[string]int64{}} td.mockDB.EXPECT().SelectQueueMetadata(ctx, testQueueType).Return(emptyMetadata, nil) td.mockDB.EXPECT().UpdateQueueMetadataCas(ctx, gomock.Any()).Return(errUpdate) td.mockErrConversion(errUpdate) assert.ErrorContains(t, store.UpdateAckLevel(ctx, &persistence.InternalUpdateAckLevelRequest{ MessageID: messageID, ClusterName: clusterName, CurrentTimeStamp: FixedTime, }), errUpdate.Error()) } func TestUpdateDLQAckLevel_Succeeds(t *testing.T) { const messageID = 123 const clusterName = "test-cluster" td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() metadata := nosqlplugin.QueueMetadataRow{ QueueType: testDLQueueType, ClusterAckLevels: map[string]int64{ clusterName: 110, "unrelated-cluster": 300, }, Version: 3, } td.mockDB.EXPECT().SelectQueueMetadata(ctx, testDLQueueType).Return(&metadata, nil) td.mockDB.EXPECT().UpdateQueueMetadataCas(ctx, gomock.Any()). Do(func(_ context.Context, newMeta nosqlplugin.QueueMetadataRow) { assert.Equal(t, int64(4), newMeta.Version, "version should be incremented") assert.Equal(t, testDLQueueType, newMeta.QueueType, "type should remain the same") expectedClusterAckLevels := map[string]int64{ clusterName: messageID, // messageID is greater "unrelated-cluster": 300, } // only target cluster ack-level should be updated assert.Equal(t, expectedClusterAckLevels, newMeta.ClusterAckLevels) }).Return(nil) assert.NoError(t, store.UpdateDLQAckLevel(ctx, &persistence.InternalUpdateDLQAckLevelRequest{ MessageID: messageID, ClusterName: clusterName, CurrentTimeStamp: FixedTime, })) } func TestGetDLQAckLevels_Succeeds(t *testing.T) { td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() expectedAckLevels := map[string]int64{ "cluster1": 123, "cluster2": 456, } metadata := nosqlplugin.QueueMetadataRow{ QueueType: testDLQueueType, ClusterAckLevels: expectedAckLevels, Version: 3, } td.mockDB.EXPECT().SelectQueueMetadata(ctx, testDLQueueType).Return(&metadata, nil) resp, err := store.GetDLQAckLevels(ctx, &persistence.InternalGetDLQAckLevelsRequest{}) require.NoError(t, err) assert.Equal(t, expectedAckLevels, resp.AckLevels) } func TestGetDLQAckLevels_FailsIfSelectMetadataFails(t *testing.T) { errSelect := errors.New("failed to select queue metadata") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().SelectQueueMetadata(ctx, testDLQueueType).Return(nil, errSelect) td.mockIsNotFoundErrCheck(errSelect, false) td.mockErrConversion(errSelect) resp, err := store.GetDLQAckLevels(ctx, &persistence.InternalGetDLQAckLevelsRequest{}) assert.ErrorContains(t, err, errSelect.Error()) assert.Nil(t, resp) } func TestGetDLQSize_Succeeds(t *testing.T) { const expectedSize int64 = 12345 td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().GetQueueSize(ctx, testDLQueueType).Return(expectedSize, nil) resp, err := store.GetDLQSize(ctx, &persistence.InternalGetDLQSizeRequest{}) require.NoError(t, err) assert.Equal(t, expectedSize, resp.Size) } func TestGetDLQSize_FailsIfGetQueueSizeFails(t *testing.T) { expectedErr := errors.New("failed to retrieve queue size") td := newQueueStoreTestData(t) store := td.createValidQueueStore(t) ctx := context.Background() td.mockDB.EXPECT().GetQueueSize(ctx, testDLQueueType).Return(int64(0), expectedErr) td.mockErrConversion(expectedErr) resp, err := store.GetDLQSize(ctx, &persistence.InternalGetDLQSizeRequest{}) assert.ErrorContains(t, err, expectedErr.Error()) assert.Nil(t, resp) } func TestGetNextID(t *testing.T) { tests := map[string]struct { acks map[string]int64 lastID int64 expected int64 }{ "expected case - last ID is equal to ack-levels": { acks: map[string]int64{"a": 3}, lastID: 3, expected: 4, }, "expected case - last ID is equal to ack-levels haven't caught up": { acks: map[string]int64{"a": 2}, lastID: 3, expected: 4, }, "error case - ack-levels are ahead for some reason": { acks: map[string]int64{"a": 3}, lastID: 2, expected: 4, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, getNextID(td.acks, td.lastID)) }) } } ================================================ FILE: common/persistence/nosql/nosql_shard_store.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "fmt" "time" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/types" ) // Implements ShardStore type nosqlShardStore struct { shardedNosqlStore currentClusterName string parser serialization.Parser } // newNoSQLShardStore is used to create an instance of ShardStore implementation func newNoSQLShardStore( cfg config.ShardedNoSQL, clusterName string, logger log.Logger, metricsClient metrics.Client, dc *persistence.DynamicConfiguration, parser serialization.Parser, ) (persistence.ShardStore, error) { s, err := newShardedNosqlStore(cfg, logger, metricsClient, dc) if err != nil { return nil, err } return &nosqlShardStore{ shardedNosqlStore: s, currentClusterName: clusterName, parser: parser, }, nil } func (sh *nosqlShardStore) CreateShard( ctx context.Context, request *persistence.InternalCreateShardRequest, ) error { storeShard, err := sh.GetStoreShardByHistoryShard(request.ShardInfo.ShardID) if err != nil { return err } shardRow, err := sh.shardInfoToShardsRow(request.ShardInfo) if err != nil { return err } err = storeShard.db.InsertShard(ctx, shardRow) if err != nil { conditionFailure, ok := err.(*nosqlplugin.ShardOperationConditionFailure) if ok { return &persistence.ShardAlreadyExistError{ Msg: fmt.Sprintf("Shard already exists in executions table. ShardId: %v, request_range_id: %v, actual_range_id : %v, columns: (%v)", request.ShardInfo.ShardID, request.ShardInfo.RangeID, conditionFailure.RangeID, conditionFailure.Details), } } return convertCommonErrors(storeShard.db, "CreateShard", err) } return nil } func (sh *nosqlShardStore) GetShard( ctx context.Context, request *persistence.InternalGetShardRequest, ) (*persistence.InternalGetShardResponse, error) { shardID := request.ShardID storeShard, err := sh.GetStoreShardByHistoryShard(shardID) if err != nil { return nil, err } rangeID, shardRow, err := storeShard.db.SelectShard(ctx, shardID, sh.currentClusterName) if err != nil { if storeShard.db.IsNotFoundError(err) { return nil, &types.EntityNotExistsError{ Message: fmt.Sprintf("Shard not found. ShardId: %v", shardID), } } return nil, convertCommonErrors(storeShard.db, "GetShard", err) } shardInfoRangeID := shardRow.RangeID // check if rangeID column and rangeID field in shard column matches, if not we need to pick the larger // rangeID. if shardInfoRangeID > rangeID { // In this case we need to fix the rangeID column before returning the result as: // 1. if we return shardInfoRangeID, then later shard CAS operation will fail // 2. if we still return rangeID, CAS will work but rangeID will move backward which // result in lost tasks, corrupted workflow history, etc. sh.GetLogger().Warn("Corrupted shard rangeID", tag.ShardID(shardID), tag.ShardRangeID(shardInfoRangeID), tag.PreviousShardRangeID(rangeID)) if err := sh.updateRangeID(ctx, shardID, shardInfoRangeID, rangeID); err != nil { return nil, err } } else { // return the actual rangeID shardRow.RangeID = rangeID // // If shardInfoRangeID = rangeID, no corruption, so no action needed. // // If shardInfoRangeID < rangeID, we also don't need to do anything here as createShardInfo will ignore // shardInfoRangeID and return rangeID instead. Later when updating the shard, CAS can still succeed // as the value from rangeID columns is returned, shardInfoRangeID will also be updated to the correct value. } shardInfo, err := sh.shardsRowToShardInfo(storeShard, shardRow, rangeID) if err != nil { return nil, err } return &persistence.InternalGetShardResponse{ShardInfo: shardInfo}, nil } func (sh *nosqlShardStore) updateRangeID( ctx context.Context, shardID int, rangeID int64, previousRangeID int64, ) error { storeShard, err := sh.GetStoreShardByHistoryShard(shardID) if err != nil { return err } err = storeShard.db.UpdateRangeID(ctx, shardID, rangeID, previousRangeID) if err != nil { conditionFailure, ok := err.(*nosqlplugin.ShardOperationConditionFailure) if ok { return &persistence.ShardOwnershipLostError{ ShardID: shardID, Msg: fmt.Sprintf("Failed to update shard rangeID. request_range_id: %v, actual_range_id : %v, columns: (%v)", previousRangeID, conditionFailure.RangeID, conditionFailure.Details), } } return convertCommonErrors(storeShard.db, "UpdateRangeID", err) } return nil } func (sh *nosqlShardStore) UpdateShard( ctx context.Context, request *persistence.InternalUpdateShardRequest, ) error { storeShard, err := sh.GetStoreShardByHistoryShard(request.ShardInfo.ShardID) if err != nil { return err } shardRow, err := sh.shardInfoToShardsRow(request.ShardInfo) if err != nil { return err } err = storeShard.db.UpdateShard(ctx, shardRow, request.PreviousRangeID) if err != nil { conditionFailure, ok := err.(*nosqlplugin.ShardOperationConditionFailure) if ok { return &persistence.ShardOwnershipLostError{ ShardID: request.ShardInfo.ShardID, Msg: fmt.Sprintf("Failed to update shard rangeID. request_range_id: %v, actual_range_id : %v, columns: (%v)", request.PreviousRangeID, conditionFailure.RangeID, conditionFailure.Details), } } return convertCommonErrors(storeShard.db, "UpdateShard", err) } return nil } func (sh *nosqlShardStore) shardInfoToShardsRow(s *persistence.InternalShardInfo) (*nosqlplugin.ShardRow, error) { var markerData []byte markerEncoding := string(constants.EncodingTypeEmpty) if s.PendingFailoverMarkers != nil { markerData = s.PendingFailoverMarkers.Data markerEncoding = string(s.PendingFailoverMarkers.Encoding) } var transferPQSData []byte transferPQSEncoding := string(constants.EncodingTypeEmpty) if s.TransferProcessingQueueStates != nil { transferPQSData = s.TransferProcessingQueueStates.Data transferPQSEncoding = string(s.TransferProcessingQueueStates.Encoding) } var timerPQSData []byte timerPQSEncoding := string(constants.EncodingTypeEmpty) if s.TimerProcessingQueueStates != nil { timerPQSData = s.TimerProcessingQueueStates.Data timerPQSEncoding = string(s.TimerProcessingQueueStates.Encoding) } shardInfo := &serialization.ShardInfo{ StolenSinceRenew: int32(s.StolenSinceRenew), UpdatedAt: s.UpdatedAt, ReplicationAckLevel: s.ReplicationAckLevel, TransferAckLevel: s.TransferAckLevel, TimerAckLevel: s.TimerAckLevel, ClusterTransferAckLevel: s.ClusterTransferAckLevel, ClusterTimerAckLevel: s.ClusterTimerAckLevel, TransferProcessingQueueStates: transferPQSData, TransferProcessingQueueStatesEncoding: transferPQSEncoding, TimerProcessingQueueStates: timerPQSData, TimerProcessingQueueStatesEncoding: timerPQSEncoding, DomainNotificationVersion: s.DomainNotificationVersion, Owner: s.Owner, ClusterReplicationLevel: s.ClusterReplicationLevel, ReplicationDlqAckLevel: s.ReplicationDLQAckLevel, PendingFailoverMarkers: markerData, PendingFailoverMarkersEncoding: markerEncoding, QueueStates: s.QueueStates, } blob, err := sh.parser.ShardInfoToBlob(shardInfo) if err != nil { return nil, err } return &nosqlplugin.ShardRow{ InternalShardInfo: s, Data: blob.Data, DataEncoding: string(blob.Encoding), }, nil } func (sh *nosqlShardStore) shardsRowToShardInfo(storeShard *nosqlStore, shardRow *nosqlplugin.ShardRow, rangeID int64) (*persistence.InternalShardInfo, error) { if !storeShard.dc.ReadNoSQLShardFromDataBlob() { sh.GetMetricsClient().IncCounter(metrics.PersistenceGetShardScope, metrics.NoSQLShardStoreReadFromOriginalColumnCounter) return shardRow.InternalShardInfo, nil } if len(shardRow.Data) == 0 { sh.GetMetricsClient().IncCounter(metrics.PersistenceGetShardScope, metrics.NoSQLShardStoreReadFromOriginalColumnCounter) sh.GetLogger().Warn("Shard info data blob is empty, falling back to typed fields") return shardRow.InternalShardInfo, nil } shardInfo, err := sh.parser.ShardInfoFromBlob(shardRow.Data, shardRow.DataEncoding) if err != nil { sh.GetMetricsClient().IncCounter(metrics.PersistenceGetShardScope, metrics.NoSQLShardStoreReadFromOriginalColumnCounter) sh.GetLogger().Error("Failed to decode shard info from data blob, falling back to typed fields", tag.Error(err)) return shardRow.InternalShardInfo, nil } sh.GetMetricsClient().IncCounter(metrics.PersistenceGetShardScope, metrics.NoSQLShardStoreReadFromDataBlobCounter) if len(shardInfo.ClusterTransferAckLevel) == 0 { shardInfo.ClusterTransferAckLevel = map[string]int64{ sh.currentClusterName: shardInfo.GetTransferAckLevel(), } } timerAckLevel := make(map[string]time.Time, len(shardInfo.ClusterTimerAckLevel)) for k, v := range shardInfo.ClusterTimerAckLevel { timerAckLevel[k] = v } if len(timerAckLevel) == 0 { timerAckLevel = map[string]time.Time{ sh.currentClusterName: shardInfo.GetTimerAckLevel(), } } if shardInfo.ClusterReplicationLevel == nil { shardInfo.ClusterReplicationLevel = make(map[string]int64) } if shardInfo.ReplicationDlqAckLevel == nil { shardInfo.ReplicationDlqAckLevel = make(map[string]int64) } var transferPQS *persistence.DataBlob if shardInfo.GetTransferProcessingQueueStates() != nil { transferPQS = &persistence.DataBlob{ Encoding: constants.EncodingType(shardInfo.GetTransferProcessingQueueStatesEncoding()), Data: shardInfo.GetTransferProcessingQueueStates(), } } var timerPQS *persistence.DataBlob if shardInfo.GetTimerProcessingQueueStates() != nil { timerPQS = &persistence.DataBlob{ Encoding: constants.EncodingType(shardInfo.GetTimerProcessingQueueStatesEncoding()), Data: shardInfo.GetTimerProcessingQueueStates(), } } var pendingFailoverMarkers *persistence.DataBlob if shardInfo.GetPendingFailoverMarkers() != nil { pendingFailoverMarkers = &persistence.DataBlob{ Encoding: constants.EncodingType(shardInfo.GetPendingFailoverMarkersEncoding()), Data: shardInfo.GetPendingFailoverMarkers(), } } return &persistence.InternalShardInfo{ ShardID: int(shardRow.ShardID), RangeID: rangeID, Owner: shardInfo.GetOwner(), StolenSinceRenew: int(shardInfo.GetStolenSinceRenew()), UpdatedAt: shardInfo.GetUpdatedAt(), ReplicationAckLevel: shardInfo.GetReplicationAckLevel(), TransferAckLevel: shardInfo.GetTransferAckLevel(), TimerAckLevel: shardInfo.GetTimerAckLevel(), ClusterTransferAckLevel: shardInfo.ClusterTransferAckLevel, ClusterTimerAckLevel: timerAckLevel, TransferProcessingQueueStates: transferPQS, TimerProcessingQueueStates: timerPQS, DomainNotificationVersion: shardInfo.GetDomainNotificationVersion(), ClusterReplicationLevel: shardInfo.ClusterReplicationLevel, ReplicationDLQAckLevel: shardInfo.ReplicationDlqAckLevel, PendingFailoverMarkers: pendingFailoverMarkers, QueueStates: shardInfo.GetQueueStates(), }, nil } ================================================ FILE: common/persistence/nosql/nosql_shard_store_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package nosql import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/types" ) func testFixtureInternalShardInfo() *persistence.InternalShardInfo { now := time.Unix(100000, 0) return &persistence.InternalShardInfo{ ShardID: 1, Owner: "test-owner", RangeID: 101, StolenSinceRenew: 0, UpdatedAt: now, ReplicationAckLevel: 100, ReplicationDLQAckLevel: map[string]int64{ "cluster-1": 10, "cluster-2": 15, }, TransferAckLevel: 50, TimerAckLevel: now.Add(-time.Hour), ClusterTransferAckLevel: map[string]int64{ "cluster-1": 60, "cluster-2": 70, }, ClusterTimerAckLevel: map[string]time.Time{ "cluster-1": now.Add(-time.Minute * 30), "cluster-2": now.Add(-time.Minute * 60), }, TransferProcessingQueueStates: &persistence.DataBlob{ Encoding: "base64", Data: []byte("transfer-processing-states"), }, TimerProcessingQueueStates: &persistence.DataBlob{ Encoding: "base64", Data: []byte("timer-processing-states"), }, ClusterReplicationLevel: map[string]int64{ "cluster-1": 200, "cluster-2": 250, }, DomainNotificationVersion: 102, PendingFailoverMarkers: &persistence.DataBlob{ Encoding: "base64", Data: []byte("pending-failover-markers"), }, QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: {}, }, }, }, } } func setUpMocksForShardStore(t *testing.T) (*nosqlShardStore, *nosqlplugin.MockDB, *MockshardedNosqlStore, *serialization.MockParser, *gomock.Controller) { ctrl := gomock.NewController(t) dbMock := nosqlplugin.NewMockDB(ctrl) storeShardMock := NewMockshardedNosqlStore(ctrl) mockParser := serialization.NewMockParser(ctrl) shardStore := &nosqlShardStore{ shardedNosqlStore: storeShardMock, currentClusterName: "test-cluster", parser: mockParser, } return shardStore, dbMock, storeShardMock, mockParser, ctrl } func TestCreateShard(t *testing.T) { testCases := []struct { name string setupMock func(*nosqlplugin.MockDB, *MockshardedNosqlStore, *serialization.MockParser) request *persistence.InternalCreateShardRequest expectError bool expectedError string }{ { name: "success", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: "base64", Data: []byte("shard-info"), }, nil).Times(1) dbMock.EXPECT().InsertShard(gomock.Any(), &nosqlplugin.ShardRow{ InternalShardInfo: testFixtureInternalShardInfo(), Data: []byte("shard-info"), DataEncoding: "base64", }).Return(nil).Times(1) }, request: &persistence.InternalCreateShardRequest{ ShardInfo: testFixtureInternalShardInfo(), }, expectError: false, }, { name: "shard already exists error", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: "base64", Data: []byte("shard-info"), }, nil).Times(1) dbMock.EXPECT().InsertShard(gomock.Any(), gomock.Any()).Return(&nosqlplugin.ShardOperationConditionFailure{ RangeID: 200, Details: "rangeID mismatch", }).Times(1) }, request: &persistence.InternalCreateShardRequest{ ShardInfo: &persistence.InternalShardInfo{ ShardID: 1, RangeID: 100, }, }, expectError: true, expectedError: "Shard already exists in executions table. ShardId: 1, request_range_id: 100, actual_range_id : 200, columns: (rangeID mismatch)", }, { name: "generic db error", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: "base64", Data: []byte("shard-info"), }, nil).Times(1) dbMock.EXPECT().InsertShard(gomock.Any(), gomock.Any()).Return(errors.New("db error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.InternalCreateShardRequest{ ShardInfo: &persistence.InternalShardInfo{ ShardID: 1, RangeID: 100, }, }, expectError: true, expectedError: "CreateShard failed. Error: db error ", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup mocks for each test case individually shardStore, dbMock, storeShardMock, mockParser, ctrl := setUpMocksForShardStore(t) defer ctrl.Finish() // Setup test-specific mock behavior tc.setupMock(dbMock, storeShardMock, mockParser) // Execute the method under test err := shardStore.CreateShard(context.Background(), tc.request) // Validate results if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestGetShard(t *testing.T) { testCases := []struct { name string setupMock func(*nosqlplugin.MockDB, *MockshardedNosqlStore, *serialization.MockParser) request *persistence.InternalGetShardRequest expectError bool expected *persistence.InternalGetShardResponse expectedError string }{ { name: "success - no update", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock, dc: &persistence.DynamicConfiguration{ ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(true), }}, nil).Times(1) shardInfo := testFixtureInternalShardInfo() dbMock.EXPECT().SelectShard(gomock.Any(), 1, "test-cluster").Return(int64(101), &nosqlplugin.ShardRow{ InternalShardInfo: shardInfo, Data: []byte("shard-info"), DataEncoding: "base64", }, nil).Times(1) storeShardMock.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).Times(1) mockParser.EXPECT().ShardInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.ShardInfo{ Owner: "test-owner", StolenSinceRenew: 1, UpdatedAt: time.Unix(100000, 0), ReplicationAckLevel: 100, TransferAckLevel: 50, TimerAckLevel: time.Unix(100000, 0), ClusterTransferAckLevel: map[string]int64{ "cluster-1": 60, "cluster-2": 70, }, ClusterTimerAckLevel: map[string]time.Time{ "cluster-1": time.Unix(100000, 0), "cluster-2": time.Unix(100000, 0), }, TransferProcessingQueueStates: []byte("transfer-processing-states"), TransferProcessingQueueStatesEncoding: "base64", TimerProcessingQueueStates: []byte("timer-processing-states"), TimerProcessingQueueStatesEncoding: "base64", ClusterReplicationLevel: map[string]int64{ "cluster-1": 200, "cluster-2": 250, }, DomainNotificationVersion: 102, PendingFailoverMarkers: []byte("pending-failover-markers"), PendingFailoverMarkersEncoding: "base64", ReplicationDlqAckLevel: map[string]int64{ "cluster-1": 10, "cluster-2": 15, }, QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: {}, }, }, }, }, nil).Times(1) }, request: &persistence.InternalGetShardRequest{ShardID: 1}, expectError: false, expected: &persistence.InternalGetShardResponse{ ShardInfo: &persistence.InternalShardInfo{ ShardID: 1, RangeID: 101, Owner: "test-owner", StolenSinceRenew: 1, UpdatedAt: time.Unix(100000, 0), ReplicationAckLevel: 100, TransferAckLevel: 50, TimerAckLevel: time.Unix(100000, 0), ClusterTransferAckLevel: map[string]int64{ "cluster-1": 60, "cluster-2": 70, }, ClusterTimerAckLevel: map[string]time.Time{ "cluster-1": time.Unix(100000, 0), "cluster-2": time.Unix(100000, 0), }, TransferProcessingQueueStates: &persistence.DataBlob{ Encoding: "base64", Data: []byte("transfer-processing-states"), }, TimerProcessingQueueStates: &persistence.DataBlob{ Encoding: "base64", Data: []byte("timer-processing-states"), }, ClusterReplicationLevel: map[string]int64{ "cluster-1": 200, "cluster-2": 250, }, DomainNotificationVersion: 102, PendingFailoverMarkers: &persistence.DataBlob{ Encoding: "base64", Data: []byte("pending-failover-markers"), }, ReplicationDLQAckLevel: map[string]int64{ "cluster-1": 10, "cluster-2": 15, }, QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: {}, }, }, }, }, }, }, { name: "success - fix shard", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock, dc: &persistence.DynamicConfiguration{ ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), }}, nil).Times(2) storeShardMock.EXPECT().GetLogger().Return(log.NewNoop()).Times(1) storeShardMock.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).Times(1) dbMock.EXPECT().SelectShard(gomock.Any(), 1, "test-cluster").Return(int64(100), &nosqlplugin.ShardRow{ InternalShardInfo: testFixtureInternalShardInfo(), Data: []byte("shard-info"), DataEncoding: "base64", }, nil).Times(1) dbMock.EXPECT().UpdateRangeID(gomock.Any(), 1, int64(101), int64(100)).Return(nil).Times(1) }, request: &persistence.InternalGetShardRequest{ShardID: 1}, expectError: false, expected: &persistence.InternalGetShardResponse{ ShardInfo: testFixtureInternalShardInfo(), }, }, { name: "error fixing shard - shard ownership lost error", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock, dc: &persistence.DynamicConfiguration{ ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), }}, nil).Times(2) storeShardMock.EXPECT().GetLogger().Return(log.NewNoop()).Times(1) dbMock.EXPECT().SelectShard(gomock.Any(), 1, "test-cluster").Return(int64(100), &nosqlplugin.ShardRow{ InternalShardInfo: testFixtureInternalShardInfo(), Data: []byte("shard-info"), DataEncoding: "base64", }, nil).Times(1) dbMock.EXPECT().UpdateRangeID(gomock.Any(), 1, int64(101), int64(100)).Return(&nosqlplugin.ShardOperationConditionFailure{ RangeID: 200, Details: "rangeID mismatch", }).Times(1) }, request: &persistence.InternalGetShardRequest{ShardID: 1}, expectError: true, expectedError: "Failed to update shard rangeID. request_range_id: 100, actual_range_id : 200, columns: (rangeID mismatch)", }, { name: "error fixing shard - generic db error", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock, dc: &persistence.DynamicConfiguration{ ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), }}, nil).Times(2) storeShardMock.EXPECT().GetLogger().Return(log.NewNoop()).Times(1) dbMock.EXPECT().SelectShard(gomock.Any(), 1, "test-cluster").Return(int64(100), &nosqlplugin.ShardRow{ InternalShardInfo: testFixtureInternalShardInfo(), Data: []byte("shard-info"), DataEncoding: "base64", }, nil).Times(1) dbMock.EXPECT().UpdateRangeID(gomock.Any(), 1, int64(101), int64(100)).Return(errors.New("db error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.InternalGetShardRequest{ShardID: 1}, expectError: true, expectedError: "UpdateRangeID failed. Error: db error ", }, { name: "shard not found error", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) dbMock.EXPECT().SelectShard(gomock.Any(), 1, "test-cluster").Return(int64(0), nil, errors.New("not found")).Times(1) dbMock.EXPECT().IsNotFoundError(errors.New("not found")).Return(true).Times(1) }, request: &persistence.InternalGetShardRequest{ShardID: 1}, expectError: true, expectedError: "Shard not found. ShardId: 1", }, { name: "generic db error", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) dbMock.EXPECT().SelectShard(gomock.Any(), 1, "test-cluster").Return(int64(0), nil, errors.New("db error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(false).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.InternalGetShardRequest{ShardID: 1}, expectError: true, expectedError: "GetShard failed. Error: db error ", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup mocks for each test case individually shardStore, dbMock, storeShardMock, mockParser, ctrl := setUpMocksForShardStore(t) defer ctrl.Finish() // Setup test-specific mock behavior tc.setupMock(dbMock, storeShardMock, mockParser) // Execute the method under test resp, err := shardStore.GetShard(context.Background(), tc.request) // Validate results if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } func TestUpdateShard(t *testing.T) { testCases := []struct { name string setupMock func(*nosqlplugin.MockDB, *MockshardedNosqlStore, *serialization.MockParser) request *persistence.InternalUpdateShardRequest expectError bool expectedError string }{ { name: "success", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: "base64", Data: []byte("shard-info"), }, nil).Times(1) dbMock.EXPECT().UpdateShard(gomock.Any(), &nosqlplugin.ShardRow{ InternalShardInfo: testFixtureInternalShardInfo(), Data: []byte("shard-info"), DataEncoding: "base64", }, int64(100)).Return(nil).Times(1) }, request: &persistence.InternalUpdateShardRequest{ ShardInfo: testFixtureInternalShardInfo(), PreviousRangeID: 100, }, expectError: false, }, { name: "shard ownership lost error", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: "base64", Data: []byte("shard-info"), }, nil).Times(1) dbMock.EXPECT().UpdateShard(gomock.Any(), gomock.Any(), int64(100)).Return(&nosqlplugin.ShardOperationConditionFailure{ RangeID: 200, Details: "rangeID mismatch", }).Times(1) }, request: &persistence.InternalUpdateShardRequest{ ShardInfo: &persistence.InternalShardInfo{ ShardID: 1, RangeID: 100, }, PreviousRangeID: 100, }, expectError: true, expectedError: "Failed to update shard rangeID. request_range_id: 100, actual_range_id : 200, columns: (rangeID mismatch)", }, { name: "generic db error", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) dbMock.EXPECT().UpdateShard(gomock.Any(), gomock.Any(), int64(100)).Return(errors.New("db error")).Times(1) mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: "base64", Data: []byte("shard-info"), }, nil).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.InternalUpdateShardRequest{ ShardInfo: &persistence.InternalShardInfo{ ShardID: 1, RangeID: 100, }, PreviousRangeID: 100, }, expectError: true, expectedError: "UpdateShard failed. Error: db error ", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup mocks for each test case individually shardStore, dbMock, storeShardMock, mockParser, ctrl := setUpMocksForShardStore(t) defer ctrl.Finish() // Setup test-specific mock behavior tc.setupMock(dbMock, storeShardMock, mockParser) // Execute the method under test err := shardStore.UpdateShard(context.Background(), tc.request) // Validate results if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestUpdateRangeID(t *testing.T) { testCases := []struct { name string setupMock func(*nosqlplugin.MockDB, *MockshardedNosqlStore, *serialization.MockParser) shardID int rangeID int64 previousRangeID int64 expectError bool expectedError string }{ { name: "success", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) dbMock.EXPECT().UpdateRangeID(gomock.Any(), 1, int64(100), int64(99)).Return(nil).Times(1) }, shardID: 1, rangeID: 100, previousRangeID: 99, expectError: false, }, { name: "shard ownership lost error", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) dbMock.EXPECT().UpdateRangeID(gomock.Any(), 1, int64(100), int64(99)).Return(&nosqlplugin.ShardOperationConditionFailure{ RangeID: 200, Details: "rangeID mismatch", }).Times(1) }, shardID: 1, rangeID: 100, previousRangeID: 99, expectError: true, expectedError: "Failed to update shard rangeID. request_range_id: 99, actual_range_id : 200, columns: (rangeID mismatch)", }, { name: "generic db error", setupMock: func(dbMock *nosqlplugin.MockDB, storeShardMock *MockshardedNosqlStore, mockParser *serialization.MockParser) { storeShardMock.EXPECT().GetStoreShardByHistoryShard(1).Return(&nosqlStore{db: dbMock}, nil).Times(1) dbMock.EXPECT().UpdateRangeID(gomock.Any(), 1, int64(100), int64(99)).Return(errors.New("db error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, shardID: 1, rangeID: 100, previousRangeID: 99, expectError: true, expectedError: "UpdateRangeID failed. Error: db error ", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup mocks for each test case individually shardStore, dbMock, storeShardMock, mockParser, ctrl := setUpMocksForShardStore(t) defer ctrl.Finish() // Setup test-specific mock behavior tc.setupMock(dbMock, storeShardMock, mockParser) // Execute the method under test err := shardStore.updateRangeID(context.Background(), tc.shardID, tc.rangeID, tc.previousRangeID) // Validate results if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: common/persistence/nosql/nosql_store.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // a shared struct for all stores in this package type nosqlStore struct { logger log.Logger db nosqlplugin.DB dc *persistence.DynamicConfiguration } func (nm *nosqlStore) GetName() string { return nm.db.PluginName() } // Close releases the underlying resources held by this object func (nm *nosqlStore) Close() { nm.db.Close() } ================================================ FILE: common/persistence/nosql/nosql_task_store.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "fmt" "math" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) type ( nosqlTaskStore struct { shardedNosqlStore } ) const ( initialRangeID int64 = 1 // Id of the first range of a new task list initialAckLevel int64 = 0 taskListTTL = int64(24 * time.Hour / time.Second) // Applied only to kinds that have TTL ) // newNoSQLTaskStore is used to create an instance of TaskStore implementation func newNoSQLTaskStore( cfg config.ShardedNoSQL, logger log.Logger, metricsClient metrics.Client, dc *persistence.DynamicConfiguration, ) (persistence.TaskStore, error) { s, err := newShardedNosqlStore(cfg, logger, metricsClient, dc) if err != nil { return nil, err } return &nosqlTaskStore{ shardedNosqlStore: s, }, nil } func (t *nosqlTaskStore) GetOrphanTasks(ctx context.Context, request *persistence.GetOrphanTasksRequest) (*persistence.GetOrphanTasksResponse, error) { // TODO: It's unclear if this's necessary or possible for NoSQL return nil, &types.InternalServiceError{ Message: "Unimplemented call to GetOrphanTasks for NoSQL", } } func (t *nosqlTaskStore) GetTaskListSize(ctx context.Context, request *persistence.GetTaskListSizeRequest) (*persistence.GetTaskListSizeResponse, error) { storeShard, err := t.GetStoreShardByTaskList(request.DomainID, request.TaskListName, request.TaskListType) if err != nil { return nil, err } size, err := storeShard.db.GetTasksCount(ctx, &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: request.DomainID, TaskListName: request.TaskListName, TaskListType: request.TaskListType, }, MinTaskID: request.AckLevel, }) if err != nil { return nil, err } return &persistence.GetTaskListSizeResponse{Size: size}, nil } func (t *nosqlTaskStore) LeaseTaskList( ctx context.Context, request *persistence.LeaseTaskListRequest, ) (*persistence.LeaseTaskListResponse, error) { if len(request.TaskList) == 0 { return nil, &types.InternalServiceError{ Message: "LeaseTaskList requires non empty task list", } } currentTimeStamp := request.CurrentTimeStamp var err, selectErr error var currTL *nosqlplugin.TaskListRow storeShard, err := t.GetStoreShardByTaskList(request.DomainID, request.TaskList, request.TaskType) if err != nil { return nil, err } currTL, selectErr = storeShard.db.SelectTaskList(ctx, &nosqlplugin.TaskListFilter{ DomainID: request.DomainID, TaskListName: request.TaskList, TaskListType: request.TaskType, }) if selectErr != nil { if storeShard.db.IsNotFoundError(selectErr) { // First time task list is used currTL = &nosqlplugin.TaskListRow{ DomainID: request.DomainID, TaskListName: request.TaskList, TaskListType: request.TaskType, RangeID: initialRangeID, TaskListKind: request.TaskListKind, AckLevel: initialAckLevel, LastUpdatedTime: currentTimeStamp, CurrentTimeStamp: currentTimeStamp, } err = storeShard.db.InsertTaskList(ctx, currTL) } else { return nil, convertCommonErrors(storeShard.db, "LeaseTaskList", selectErr) } } else { // if request.RangeID is > 0, we are trying to renew an already existing // lease on the task list. If request.RangeID=0, we are trying to steal // the tasklist from its current owner if request.RangeID > 0 && request.RangeID != currTL.RangeID { return nil, &persistence.ConditionFailedError{ Msg: fmt.Sprintf("leaseTaskList:renew failed: taskList:%v, taskListType:%v, haveRangeID:%v, gotRangeID:%v", request.TaskList, request.TaskType, request.RangeID, currTL.RangeID), } } // Update the rangeID as this is an ownership change currTL.RangeID++ err = storeShard.db.UpdateTaskList(ctx, &nosqlplugin.TaskListRow{ DomainID: request.DomainID, TaskListName: request.TaskList, TaskListType: request.TaskType, RangeID: currTL.RangeID, TaskListKind: currTL.TaskListKind, AckLevel: currTL.AckLevel, LastUpdatedTime: currentTimeStamp, CurrentTimeStamp: currentTimeStamp, AdaptivePartitionConfig: currTL.AdaptivePartitionConfig, }, currTL.RangeID-1) } if err != nil { conditionFailure, ok := err.(*nosqlplugin.TaskOperationConditionFailure) if ok { return nil, &persistence.ConditionFailedError{ Msg: fmt.Sprintf("leaseTaskList: taskList:%v, taskListType:%v, haveRangeID:%v, gotRangeID:%v", request.TaskList, request.TaskType, currTL.RangeID, conditionFailure.RangeID), } } return nil, convertCommonErrors(storeShard.db, "LeaseTaskList", err) } tli := &persistence.TaskListInfo{ DomainID: request.DomainID, Name: request.TaskList, TaskType: request.TaskType, RangeID: currTL.RangeID, AckLevel: currTL.AckLevel, Kind: request.TaskListKind, LastUpdated: currentTimeStamp, AdaptivePartitionConfig: currTL.AdaptivePartitionConfig, } return &persistence.LeaseTaskListResponse{TaskListInfo: tli}, nil } func (t *nosqlTaskStore) GetTaskList( ctx context.Context, request *persistence.GetTaskListRequest, ) (*persistence.GetTaskListResponse, error) { storeShard, err := t.GetStoreShardByTaskList(request.DomainID, request.TaskList, request.TaskType) if err != nil { return nil, err } currTL, err := storeShard.db.SelectTaskList(ctx, &nosqlplugin.TaskListFilter{ DomainID: request.DomainID, TaskListName: request.TaskList, TaskListType: request.TaskType, }) if err != nil { return nil, convertCommonErrors(storeShard.db, "GetTaskList", err) } tli := &persistence.TaskListInfo{ DomainID: request.DomainID, Name: request.TaskList, TaskType: request.TaskType, RangeID: currTL.RangeID, AckLevel: currTL.AckLevel, Kind: currTL.TaskListKind, LastUpdated: currTL.LastUpdatedTime, AdaptivePartitionConfig: currTL.AdaptivePartitionConfig, } return &persistence.GetTaskListResponse{TaskListInfo: tli}, nil } func (t *nosqlTaskStore) UpdateTaskList( ctx context.Context, request *persistence.UpdateTaskListRequest, ) (*persistence.UpdateTaskListResponse, error) { tli := request.TaskListInfo var err error taskListToUpdate := &nosqlplugin.TaskListRow{ DomainID: tli.DomainID, TaskListName: tli.Name, TaskListType: tli.TaskType, RangeID: tli.RangeID, TaskListKind: tli.Kind, AckLevel: tli.AckLevel, LastUpdatedTime: request.CurrentTimeStamp, CurrentTimeStamp: request.CurrentTimeStamp, AdaptivePartitionConfig: tli.AdaptivePartitionConfig, } storeShard, err := t.GetStoreShardByTaskList(tli.DomainID, tli.Name, tli.TaskType) if err != nil { return nil, err } if persistence.TaskListKindHasTTL(tli.Kind) { err = storeShard.db.UpdateTaskListWithTTL(ctx, taskListTTL, taskListToUpdate, tli.RangeID) } else { err = storeShard.db.UpdateTaskList(ctx, taskListToUpdate, tli.RangeID) } if err != nil { conditionFailure, ok := err.(*nosqlplugin.TaskOperationConditionFailure) if ok { return nil, &persistence.ConditionFailedError{ Msg: fmt.Sprintf("Failed to update task list. name: %v, type: %v, rangeID: %v, columns: (%v)", tli.Name, tli.TaskType, tli.RangeID, conditionFailure.Details), } } return nil, convertCommonErrors(storeShard.db, "UpdateTaskList", err) } return &persistence.UpdateTaskListResponse{}, nil } func (t *nosqlTaskStore) ListTaskList( _ context.Context, _ *persistence.ListTaskListRequest, ) (*persistence.ListTaskListResponse, error) { return nil, &types.InternalServiceError{ Message: "unsupported operation", } } func (t *nosqlTaskStore) DeleteTaskList( ctx context.Context, request *persistence.DeleteTaskListRequest, ) error { storeShard, err := t.GetStoreShardByTaskList(request.DomainID, request.TaskListName, request.TaskListType) if err != nil { return err } err = storeShard.db.DeleteTaskList(ctx, &nosqlplugin.TaskListFilter{ DomainID: request.DomainID, TaskListName: request.TaskListName, TaskListType: request.TaskListType, }, request.RangeID) if err != nil { conditionFailure, ok := err.(*nosqlplugin.TaskOperationConditionFailure) if ok { return &persistence.ConditionFailedError{ Msg: fmt.Sprintf("Failed to delete task list. name: %v, type: %v, rangeID: %v, columns: (%v)", request.TaskListName, request.TaskListType, request.RangeID, conditionFailure.Details), } } return convertCommonErrors(storeShard.db, "DeleteTaskList", err) } return nil } func (t *nosqlTaskStore) CreateTasks( ctx context.Context, request *persistence.CreateTasksRequest, ) (*persistence.CreateTasksResponse, error) { currentTimeStamp := request.CurrentTimeStamp var tasks []*nosqlplugin.TaskRowForInsert for _, taskRequest := range request.Tasks { task := &nosqlplugin.TaskRow{ DomainID: request.TaskListInfo.DomainID, TaskListName: request.TaskListInfo.Name, TaskListType: request.TaskListInfo.TaskType, TaskID: taskRequest.TaskID, WorkflowID: taskRequest.Data.WorkflowID, RunID: taskRequest.Data.RunID, ScheduledID: taskRequest.Data.ScheduleID, CreatedTime: currentTimeStamp, PartitionConfig: taskRequest.Data.PartitionConfig, } var ttl int // If the Data has a non-zero Expiry value, means that the ask is being re-added to the tasks table. // If that's the case, use the Expiry value to calculate the new TTL value to match history's timeout value. if !taskRequest.Data.Expiry.IsZero() { scheduleToStartTimeoutSeconds := int(taskRequest.Data.Expiry.Sub(currentTimeStamp).Seconds()) if scheduleToStartTimeoutSeconds > 0 { ttl = scheduleToStartTimeoutSeconds } else { logger := t.GetLogger() logger.Warn("Async task not created. Task is expired", tag.WorkflowID(taskRequest.Data.WorkflowID), tag.WorkflowRunID(taskRequest.Data.RunID), tag.WorkflowScheduleID(taskRequest.Data.ScheduleID)) continue } } else { ttl = int(taskRequest.Data.ScheduleToStartTimeoutSeconds) } tasks = append(tasks, &nosqlplugin.TaskRowForInsert{ TaskRow: *task, TTLSeconds: ttl, }) } tasklistCondition := &nosqlplugin.TaskListRow{ DomainID: request.TaskListInfo.DomainID, TaskListName: request.TaskListInfo.Name, TaskListType: request.TaskListInfo.TaskType, RangeID: request.TaskListInfo.RangeID, LastUpdatedTime: currentTimeStamp, CurrentTimeStamp: currentTimeStamp, } tli := request.TaskListInfo storeShard, err := t.GetStoreShardByTaskList(tli.DomainID, tli.Name, tli.TaskType) if err != nil { return nil, err } err = storeShard.db.InsertTasks(ctx, tasks, tasklistCondition) if err != nil { conditionFailure, ok := err.(*nosqlplugin.TaskOperationConditionFailure) if ok { return nil, &persistence.ConditionFailedError{ Msg: fmt.Sprintf("Failed to insert tasks. name: %v, type: %v, rangeID: %v, columns: (%v)", request.TaskListInfo.Name, request.TaskListInfo.TaskType, request.TaskListInfo.RangeID, conditionFailure.Details), } } return nil, convertCommonErrors(storeShard.db, "CreateTasks", err) } return &persistence.CreateTasksResponse{}, nil } func (t *nosqlTaskStore) GetTasks( ctx context.Context, request *persistence.GetTasksRequest, ) (*persistence.GetTasksResponse, error) { if request.MaxReadLevel == nil { request.MaxReadLevel = common.Int64Ptr(math.MaxInt64) } if request.ReadLevel > *request.MaxReadLevel { return &persistence.GetTasksResponse{}, nil } storeShard, err := t.GetStoreShardByTaskList(request.DomainID, request.TaskList, request.TaskType) if err != nil { return nil, err } resp, err := storeShard.db.SelectTasks(ctx, &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: request.DomainID, TaskListName: request.TaskList, TaskListType: request.TaskType, }, BatchSize: request.BatchSize, MinTaskID: request.ReadLevel, MaxTaskID: *request.MaxReadLevel, }) if err != nil { return nil, convertCommonErrors(storeShard.db, "GetTasks", err) } response := &persistence.GetTasksResponse{} for _, t := range resp { response.Tasks = append(response.Tasks, toTaskInfo(t)) } return response, nil } func toTaskInfo(t *nosqlplugin.TaskRow) *persistence.TaskInfo { return &persistence.TaskInfo{ DomainID: t.DomainID, WorkflowID: t.WorkflowID, RunID: t.RunID, TaskID: t.TaskID, ScheduleID: t.ScheduledID, CreatedTime: t.CreatedTime, PartitionConfig: t.PartitionConfig, } } func (t *nosqlTaskStore) CompleteTask( ctx context.Context, request *persistence.CompleteTaskRequest, ) error { tli := request.TaskList storeShard, err := t.GetStoreShardByTaskList(tli.DomainID, tli.Name, tli.TaskType) if err != nil { return err } _, err = storeShard.db.RangeDeleteTasks(ctx, &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: tli.DomainID, TaskListName: tli.Name, TaskListType: request.TaskList.TaskType, }, // exclusive MinTaskID: request.TaskID - 1, // inclusive MaxTaskID: request.TaskID, BatchSize: 1, }) if err != nil { return convertCommonErrors(storeShard.db, "CompleteTask", err) } return nil } // CompleteTasksLessThan deletes all tasks less than or equal to the given task id. This API ignores the // Limit request parameter i.e. either all tasks leq the task_id will be deleted or an error will // be returned to the caller func (t *nosqlTaskStore) CompleteTasksLessThan( ctx context.Context, request *persistence.CompleteTasksLessThanRequest, ) (*persistence.CompleteTasksLessThanResponse, error) { storeShard, err := t.GetStoreShardByTaskList(request.DomainID, request.TaskListName, request.TaskType) if err != nil { return nil, err } num, err := storeShard.db.RangeDeleteTasks(ctx, &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: request.DomainID, TaskListName: request.TaskListName, TaskListType: request.TaskType, }, // NOTE: MinTaskID is supported in plugin interfaces but not exposed in dataInterfaces/persistenceInterfaces // We may want to add it so that we can test it. // https://github.com/uber/cadence/issues/4243 MinTaskID: 0, // NOTE: request.TaskID is also inclusive, even though the name is CompleteTasksLessThan MaxTaskID: request.TaskID, BatchSize: request.Limit, }) if err != nil { return nil, convertCommonErrors(storeShard.db, "CompleteTasksLessThan", err) } return &persistence.CompleteTasksLessThanResponse{TasksCompleted: num}, nil } ================================================ FILE: common/persistence/nosql/nosql_task_store_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package nosql import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) const ( TestDomainID = "test-domain-id" TestDomainName = "test-domain" TestTaskListName = "test-tasklist" TestTaskType = persistence.TaskListTypeDecision TestWorkflowID = "test-workflow-id" TestRunID = "test-run-id" ) func TestNewNoSQLStore(t *testing.T) { registerCassandraMock(t) cfg := getValidShardedNoSQLConfig() store, err := newNoSQLTaskStore(cfg, log.NewNoop(), metrics.NewNoopMetricsClient(), nil) assert.NoError(t, err) assert.NotNil(t, store) } func setupNoSQLStoreMocks(t *testing.T) (*nosqlTaskStore, *nosqlplugin.MockDB) { ctrl := gomock.NewController(t) dbMock := nosqlplugin.NewMockDB(ctrl) nosqlSt := nosqlStore{ logger: log.NewNoop(), db: dbMock, } shardedNosqlStoreMock := NewMockshardedNosqlStore(ctrl) shardedNosqlStoreMock.EXPECT(). GetStoreShardByTaskList( TestDomainID, TestTaskListName, TestTaskType). Return(&nosqlSt, nil). AnyTimes() shardedNosqlStoreMock.EXPECT().GetLogger().Return(log.NewNoop()).AnyTimes() store := &nosqlTaskStore{ shardedNosqlStore: shardedNosqlStoreMock, } return store, dbMock } func TestGetOrphanTasks(t *testing.T) { store, _ := setupNoSQLStoreMocks(t) // We just expect the function to return an error so we don't need to check the result _, err := store.GetOrphanTasks(context.Background(), nil) var expectedErr *types.InternalServiceError assert.ErrorAs(t, err, &expectedErr) assert.ErrorContains(t, err, "Unimplemented call to GetOrphanTasks for NoSQL") } func TestGetTaskListSize(t *testing.T) { store, db := setupNoSQLStoreMocks(t) db.EXPECT().GetTasksCount( gomock.Any(), &nosqlplugin.TasksFilter{ TaskListFilter: *getDecisionTaskListFilter(), MinTaskID: 456, }, ).Return(int64(123), nil) size, err := store.GetTaskListSize(context.Background(), &persistence.GetTaskListSizeRequest{ DomainID: TestDomainID, DomainName: TestDomainName, TaskListName: TestTaskListName, TaskListType: int(types.TaskListTypeDecision), AckLevel: 456, }) assert.NoError(t, err) assert.Equal(t, &persistence.GetTaskListSizeResponse{Size: 123}, size, ) } func TestLeaseTaskList_emptyTaskList(t *testing.T) { store, _ := setupNoSQLStoreMocks(t) req := getValidLeaseTaskListRequest() req.TaskList = "" _, err := store.LeaseTaskList(context.Background(), req) assert.ErrorAs(t, err, new(*types.InternalServiceError)) assert.ErrorContains(t, err, "LeaseTaskList requires non empty task list") } func TestLeaseTaskList_selectErr(t *testing.T) { store, db := setupNoSQLStoreMocks(t) req := getValidLeaseTaskListRequest() db.EXPECT().SelectTaskList(gomock.Any(), getDecisionTaskListFilter()).Return(nil, assert.AnError) // The error is _not_ a NotFoundError db.EXPECT().IsNotFoundError(assert.AnError).Return(false).Times(2) db.EXPECT().IsTimeoutError(assert.AnError).Return(true).Times(1) _, err := store.LeaseTaskList(context.Background(), req) assert.Error(t, err) // The function does not wrap the error, it just adds it to the message assert.ErrorContains(t, err, assert.AnError.Error()) } func TestLeaseTaskList_selectErrNotFound(t *testing.T) { store, db := setupNoSQLStoreMocks(t) req := getValidLeaseTaskListRequest() db.EXPECT().SelectTaskList(gomock.Any(), getDecisionTaskListFilter()).Return(nil, assert.AnError) // The error _is_ a NotFoundError db.EXPECT().IsNotFoundError(assert.AnError).Return(true) // We then expect the tasklist to be inserted db.EXPECT().InsertTaskList(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, taskList *nosqlplugin.TaskListRow) error { tl := getExpectedTaskListRow() checkTaskListRowExpected(t, tl, taskList) return nil }) resp, err := store.LeaseTaskList(context.Background(), req) assert.NoError(t, err) checkTaskListInfoExpected(t, resp.TaskListInfo) } func TestLeaseTaskList_BadRenew(t *testing.T) { store, db := setupNoSQLStoreMocks(t) req := getValidLeaseTaskListRequest() req.RangeID = 1 // Greater than 0, so we are trying to renew taskListRow := getExpectedTaskListRow() taskListRow.RangeID = 5 // The range ID in the DB is different from the one in the request db.EXPECT().SelectTaskList(gomock.Any(), getDecisionTaskListFilter()).Return(taskListRow, nil) _, err := store.LeaseTaskList(context.Background(), req) assert.ErrorAs(t, err, new(*persistence.ConditionFailedError)) expectedMessage := "leaseTaskList:renew failed: taskList:test-tasklist, taskListType:0, haveRangeID:1, gotRangeID:5" assert.ErrorContains(t, err, expectedMessage) } func TestLeaseTaskList_Renew(t *testing.T) { store, db := setupNoSQLStoreMocks(t) taskListRow := getExpectedTaskListRow() taskListRow.RangeID = 0 // The range ID in the DB is the same as the one in the request db.EXPECT().SelectTaskList(gomock.Any(), getDecisionTaskListFilter()).Return(taskListRow, nil) db.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any(), int64(0)). DoAndReturn(func(ctx context.Context, taskList *nosqlplugin.TaskListRow, previousRangeID int64) error { checkTaskListRowExpected(t, getExpectedTaskListRow(), taskList) return nil }) resp, err := store.LeaseTaskList(context.Background(), getValidLeaseTaskListRequest()) assert.NoError(t, err) checkTaskListInfoExpected(t, resp.TaskListInfo) } func TestLeaseTaskList_RenewUpdateFailed_OperationConditionFailure(t *testing.T) { store, db := setupNoSQLStoreMocks(t) taskListRow := getExpectedTaskListRow() taskListRow.RangeID = 0 // The range ID in the DB is the same as the one in the request db.EXPECT().SelectTaskList(gomock.Any(), getDecisionTaskListFilter()).Return(taskListRow, nil) db.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any(), int64(0)). DoAndReturn(func(ctx context.Context, taskList *nosqlplugin.TaskListRow, previousRangeID int64) error { checkTaskListRowExpected(t, getExpectedTaskListRow(), taskList) return &nosqlplugin.TaskOperationConditionFailure{RangeID: 10} }) _, err := store.LeaseTaskList(context.Background(), getValidLeaseTaskListRequest()) assert.ErrorAs(t, err, new(*persistence.ConditionFailedError)) expectedMessage := "leaseTaskList: taskList:test-tasklist, taskListType:0, haveRangeID:1, gotRangeID:10" assert.ErrorContains(t, err, expectedMessage) } func TestLeaseTaskList_RenewUpdateFailed_OtherError(t *testing.T) { store, db := setupNoSQLStoreMocks(t) taskListRow := getExpectedTaskListRow() taskListRow.RangeID = 0 // The range ID in the DB is the same as the one in the request db.EXPECT().SelectTaskList(gomock.Any(), getDecisionTaskListFilter()).Return(taskListRow, nil) db.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any(), int64(0)). DoAndReturn(func(ctx context.Context, taskList *nosqlplugin.TaskListRow, previousRangeID int64) error { checkTaskListRowExpected(t, getExpectedTaskListRow(), taskList) return assert.AnError }) db.EXPECT().IsNotFoundError(assert.AnError).Return(true) _, err := store.LeaseTaskList(context.Background(), getValidLeaseTaskListRequest()) assert.Error(t, err) // The function does not wrap the error, it just adds it to the message assert.ErrorContains(t, err, assert.AnError.Error()) } func TestGetTaskList_Success(t *testing.T) { store, db := setupNoSQLStoreMocks(t) taskListRow := getExpectedTaskListRow() db.EXPECT().SelectTaskList(gomock.Any(), getDecisionTaskListFilter()).Return(taskListRow, nil) resp, err := store.GetTaskList(context.Background(), getValidGetTaskListRequest()) assert.NoError(t, err) checkTaskListInfoExpected(t, resp.TaskListInfo) } func TestGetTaskList_NotFound(t *testing.T) { store, db := setupNoSQLStoreMocks(t) db.EXPECT().SelectTaskList(gomock.Any(), getDecisionTaskListFilter()).Return(nil, errors.New("not found")) db.EXPECT().IsNotFoundError(gomock.Any()).Return(true) resp, err := store.GetTaskList(context.Background(), getValidGetTaskListRequest()) assert.ErrorAs(t, err, new(*types.EntityNotExistsError)) assert.Nil(t, resp) } func TestUpdateTaskList(t *testing.T) { tc := []struct { name string info *persistence.TaskListInfo allowance func(db *nosqlplugin.MockDB) expectedErr error }{ { name: "success - normal", info: getExpectedTaskListInfo(types.TaskListKindNormal), allowance: func(db *nosqlplugin.MockDB) { db.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any(), int64(1)).DoAndReturn( func(ctx context.Context, taskList *nosqlplugin.TaskListRow, previousRangeID int64) error { checkTaskListRowExpected(t, getExpectedTaskListRowWithPartitionConfig(), taskList) return nil }, ) }, }, { name: "success - ephemeral", info: getExpectedTaskListInfo(types.TaskListKindEphemeral), allowance: func(db *nosqlplugin.MockDB) { db.EXPECT().UpdateTaskListWithTTL(gomock.Any(), taskListTTL, gomock.Any(), int64(1)).DoAndReturn( func(ctx context.Context, ttlSeconds int64, taskList *nosqlplugin.TaskListRow, previousRangeID int64) error { expectedTaskList := getExpectedTaskListRowWithPartitionConfig() expectedTaskList.TaskListKind = int(types.TaskListKindEphemeral) checkTaskListRowExpected(t, expectedTaskList, taskList) return nil }, ) }, }, { name: "success - sticky", info: getExpectedTaskListInfo(types.TaskListKindSticky), allowance: func(db *nosqlplugin.MockDB) { db.EXPECT().UpdateTaskListWithTTL(gomock.Any(), taskListTTL, gomock.Any(), int64(1)).DoAndReturn( func(ctx context.Context, ttlSeconds int64, taskList *nosqlplugin.TaskListRow, previousRangeID int64) error { expectedTaskList := getExpectedTaskListRowWithPartitionConfig() expectedTaskList.TaskListKind = int(types.TaskListKindSticky) checkTaskListRowExpected(t, expectedTaskList, taskList) return nil }, ) }, }, { name: "error", info: getExpectedTaskListInfo(types.TaskListKindNormal), allowance: func(db *nosqlplugin.MockDB) { db.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any(), int64(1)).DoAndReturn( func(ctx context.Context, taskList *nosqlplugin.TaskListRow, previousRangeID int64) error { checkTaskListRowExpected(t, getExpectedTaskListRowWithPartitionConfig(), taskList) return &nosqlplugin.TaskOperationConditionFailure{Details: "test-details"} }, ) }, expectedErr: &persistence.ConditionFailedError{Msg: "Failed to update task list. name: test-tasklist, type: 0, rangeID: 1, columns: (test-details)"}, }, } for _, tt := range tc { t.Run(tt.name, func(t *testing.T) { store, db := setupNoSQLStoreMocks(t) if tt.allowance != nil { tt.allowance(db) } _, err := store.UpdateTaskList(context.Background(), &persistence.UpdateTaskListRequest{ TaskListInfo: tt.info, CurrentTimeStamp: FixedTime, }) if tt.expectedErr != nil { assert.Equal(t, err, tt.expectedErr) } else { assert.NoError(t, err) } }) } } func TestDeleteTaskList(t *testing.T) { store, db := setupNoSQLStoreMocks(t) db.EXPECT().DeleteTaskList(gomock.Any(), getDecisionTaskListFilter(), int64(0)).Return(nil) err := store.DeleteTaskList(context.Background(), getValidDeleteTaskListRequest()) assert.NoError(t, err) } func TestDeleteTaskList_ConditionFailure(t *testing.T) { store, db := setupNoSQLStoreMocks(t) db.EXPECT().DeleteTaskList(gomock.Any(), getDecisionTaskListFilter(), int64(0)).Return( &nosqlplugin.TaskOperationConditionFailure{Details: "test-details"}, ) err := store.DeleteTaskList(context.Background(), getValidDeleteTaskListRequest()) var expectedErr *persistence.ConditionFailedError assert.ErrorAs(t, err, &expectedErr) assert.ErrorContains(t, err, "Failed to delete task list. name: test-tasklist, type: 0, rangeID: 0, columns: (test-details)") } func TestGetTasks(t *testing.T) { store, db := setupNoSQLStoreMocks(t) now := time.Unix(123, 456) taskrow1 := nosqlplugin.TaskRow{ DomainID: TestDomainID, TaskListName: TestTaskListName, TaskListType: int(types.TaskListTypeDecision), TaskID: 5, WorkflowID: TestWorkflowID, RunID: TestRunID, ScheduledID: 0, CreatedTime: now, PartitionConfig: nil, } taskrow2 := taskrow1 taskrow2.TaskID = 6 db.EXPECT().SelectTasks(gomock.Any(), &nosqlplugin.TasksFilter{ TaskListFilter: *getDecisionTaskListFilter(), BatchSize: 100, MinTaskID: 1, MaxTaskID: 15, }).Return([]*nosqlplugin.TaskRow{&taskrow1, &taskrow2}, nil) resp, err := store.GetTasks(context.Background(), getValidGetTasksRequest()) assert.NoError(t, err) assert.NotNil(t, resp) assert.Len(t, resp.Tasks, 2) taskRowEqualTaskInfo(t, taskrow1, resp.Tasks[0]) taskRowEqualTaskInfo(t, taskrow2, resp.Tasks[1]) } func TestGetTasks_Empty(t *testing.T) { store, _ := setupNoSQLStoreMocks(t) request := getValidGetTasksRequest() // Set the max read level to be less than the min read level request.ReadLevel = 10 request.MaxReadLevel = common.Int64Ptr(5) resp, err := store.GetTasks(context.Background(), request) assert.NoError(t, err) assert.NotNil(t, resp) assert.Empty(t, resp.Tasks) } func TestCompleteTask(t *testing.T) { store, db := setupNoSQLStoreMocks(t) // The main assertion is that the correct parameters are passed to the DB db.EXPECT().RangeDeleteTasks(gomock.Any(), &nosqlplugin.TasksFilter{ TaskListFilter: *getDecisionTaskListFilter(), MinTaskID: 11, MaxTaskID: 12, BatchSize: 1, }).Return(1, nil) err := store.CompleteTask(context.Background(), &persistence.CompleteTaskRequest{ TaskList: &persistence.TaskListInfo{ DomainID: TestDomainID, Name: TestTaskListName, TaskType: int(types.TaskListTypeDecision), }, TaskID: 12, DomainName: TestDomainName, }) assert.NoError(t, err) } func TestCompleteTasksLessThan(t *testing.T) { store, db := setupNoSQLStoreMocks(t) // The main assertion is that the correct parameters are passed to the DB db.EXPECT().RangeDeleteTasks(gomock.Any(), &nosqlplugin.TasksFilter{ TaskListFilter: *getDecisionTaskListFilter(), MinTaskID: 0, MaxTaskID: 12, BatchSize: 100, }).Return(13, nil) resp, err := store.CompleteTasksLessThan(context.Background(), &persistence.CompleteTasksLessThanRequest{ DomainID: TestDomainID, TaskListName: TestTaskListName, TaskType: 0, TaskID: 12, Limit: 100, DomainName: TestDomainName, }) assert.NoError(t, err) assert.Equal(t, 13, resp.TasksCompleted) } func TestCreateTasks(t *testing.T) { now := FixedTime testCases := []struct { name string setupMock func(*nosqlplugin.MockDB) request *persistence.CreateTasksRequest expectError bool expectedError string }{ { name: "success", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().InsertTasks(gomock.Any(), gomock.Any(), &nosqlplugin.TaskListRow{ DomainID: TestDomainID, TaskListName: TestTaskListName, TaskListType: TestTaskType, RangeID: 1, }).Do(func(_ context.Context, tasks []*nosqlplugin.TaskRowForInsert, _ *nosqlplugin.TaskListRow) { assert.Len(t, tasks, 1) assert.Equal(t, TestDomainID, tasks[0].DomainID) assert.Equal(t, "workflow1", tasks[0].WorkflowID) assert.Equal(t, "run1", tasks[0].RunID) assert.Equal(t, int64(100), tasks[0].TaskID) assert.Equal(t, int64(10), tasks[0].ScheduledID) assert.Equal(t, TestTaskType, tasks[0].TaskListType) assert.Equal(t, TestTaskListName, tasks[0].TaskListName) assert.Equal(t, 30, tasks[0].TTLSeconds) }).Return(nil).Times(1) }, request: &persistence.CreateTasksRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: TestDomainID, Name: TestTaskListName, TaskType: TestTaskType, RangeID: 1, }, Tasks: []*persistence.CreateTaskInfo{ { TaskID: 100, Data: &persistence.TaskInfo{ WorkflowID: "workflow1", RunID: "run1", ScheduleID: 10, PartitionConfig: nil, ScheduleToStartTimeoutSeconds: 30, }, }, }, }, expectError: false, }, { name: "success - adding task with Expiry not expired", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().InsertTasks(gomock.Any(), gomock.Any(), &nosqlplugin.TaskListRow{ DomainID: TestDomainID, TaskListName: TestTaskListName, TaskListType: TestTaskType, RangeID: 1, }).Do(func(_ context.Context, tasks []*nosqlplugin.TaskRowForInsert, _ *nosqlplugin.TaskListRow) { assert.Len(t, tasks, 1) assert.Equal(t, TestDomainID, tasks[0].DomainID) assert.Equal(t, "workflow1", tasks[0].WorkflowID) assert.Equal(t, "run1", tasks[0].RunID) assert.Equal(t, int64(100), tasks[0].TaskID) assert.Equal(t, int64(10), tasks[0].ScheduledID) assert.Equal(t, TestTaskType, tasks[0].TaskListType) assert.Equal(t, TestTaskListName, tasks[0].TaskListName) assert.Equal(t, int(now.Add(100*time.Second).Sub(tasks[0].CreatedTime).Seconds()), tasks[0].TTLSeconds) }).Return(nil).Times(1) }, request: &persistence.CreateTasksRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: TestDomainID, Name: TestTaskListName, TaskType: TestTaskType, RangeID: 1, }, Tasks: []*persistence.CreateTaskInfo{ { TaskID: 100, Data: &persistence.TaskInfo{ WorkflowID: "workflow1", RunID: "run1", ScheduleID: 10, PartitionConfig: nil, Expiry: now.Add(100 * time.Second), }, }, }, }, expectError: false, }, { name: "success - skipping task with Expiry expired", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().InsertTasks(gomock.Any(), gomock.Any(), &nosqlplugin.TaskListRow{ DomainID: TestDomainID, TaskListName: TestTaskListName, TaskListType: TestTaskType, RangeID: 1, LastUpdatedTime: FixedTime, CurrentTimeStamp: FixedTime, }).Do(func(_ context.Context, tasks []*nosqlplugin.TaskRowForInsert, _ *nosqlplugin.TaskListRow) { assert.Len(t, tasks, 0) }).Return(nil).Times(1) }, request: &persistence.CreateTasksRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: TestDomainID, Name: TestTaskListName, TaskType: TestTaskType, RangeID: 1, }, CurrentTimeStamp: now, Tasks: []*persistence.CreateTaskInfo{ { TaskID: 100, Data: &persistence.TaskInfo{ WorkflowID: "workflow1", RunID: "run1", ScheduleID: 10, PartitionConfig: nil, Expiry: now, }, }, }, }, expectError: false, }, { name: "condition failure", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().InsertTasks(gomock.Any(), gomock.Any(), gomock.Any()).Return(&nosqlplugin.TaskOperationConditionFailure{ Details: "rangeID mismatch", }).Times(1) }, request: &persistence.CreateTasksRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: TestDomainID, Name: TestTaskListName, TaskType: TestTaskType, RangeID: 1, }, Tasks: []*persistence.CreateTaskInfo{ { TaskID: 100, Data: &persistence.TaskInfo{ WorkflowID: "workflow1", RunID: "run1", ScheduleID: 10, PartitionConfig: nil, ScheduleToStartTimeoutSeconds: 30, }, }, }, }, expectError: true, expectedError: "Failed to insert tasks. name: test-tasklist, type: 0, rangeID: 1, columns: (rangeID mismatch)", }, { name: "generic db error", setupMock: func(dbMock *nosqlplugin.MockDB) { dbMock.EXPECT().InsertTasks(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("db error")).Times(1) dbMock.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, request: &persistence.CreateTasksRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: TestDomainID, Name: TestTaskListName, TaskType: TestTaskType, RangeID: 1, }, Tasks: []*persistence.CreateTaskInfo{ { TaskID: 100, Data: &persistence.TaskInfo{ WorkflowID: "workflow1", RunID: "run1", ScheduleID: 10, PartitionConfig: nil, ScheduleToStartTimeoutSeconds: 30, }, }, }, }, expectError: true, expectedError: "CreateTasks failed. Error: db error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Setup mocks for each test case individually store, dbMock := setupNoSQLStoreMocks(t) // Setup test-specific mock behavior tc.setupMock(dbMock) // Execute the method under test resp, err := store.CreateTasks(context.Background(), tc.request) // Validate results if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) assert.NotNil(t, resp) } }) } } func getValidLeaseTaskListRequest() *persistence.LeaseTaskListRequest { return &persistence.LeaseTaskListRequest{ DomainID: TestDomainID, DomainName: TestDomainName, TaskList: TestTaskListName, TaskType: int(types.TaskListTypeDecision), TaskListKind: int(types.TaskListKindNormal), RangeID: 0, CurrentTimeStamp: FixedTime, } } func getValidGetTaskListRequest() *persistence.GetTaskListRequest { return &persistence.GetTaskListRequest{ DomainID: TestDomainID, DomainName: TestDomainName, TaskList: TestTaskListName, TaskType: int(types.TaskListTypeDecision), } } func checkTaskListInfoExpected(t *testing.T, taskListInfo *persistence.TaskListInfo) { assert.Equal(t, TestDomainID, taskListInfo.DomainID) assert.Equal(t, TestTaskListName, taskListInfo.Name) assert.Equal(t, int(types.TaskListTypeDecision), taskListInfo.TaskType) assert.Equal(t, initialRangeID, taskListInfo.RangeID) assert.Equal(t, initialAckLevel, taskListInfo.AckLevel) assert.Equal(t, int(types.TaskListKindNormal), taskListInfo.Kind) assert.Equal(t, FixedTime, taskListInfo.LastUpdated) } func taskRowEqualTaskInfo(t *testing.T, taskrow1 nosqlplugin.TaskRow, taskInfo1 *persistence.TaskInfo) { assert.Equal(t, taskrow1.DomainID, taskInfo1.DomainID) assert.Equal(t, taskrow1.WorkflowID, taskInfo1.WorkflowID) assert.Equal(t, taskrow1.RunID, taskInfo1.RunID) assert.Equal(t, taskrow1.TaskID, taskInfo1.TaskID) assert.Equal(t, taskrow1.ScheduledID, taskInfo1.ScheduleID) assert.Equal(t, taskrow1.CreatedTime, taskInfo1.CreatedTime) assert.Equal(t, taskrow1.PartitionConfig, taskInfo1.PartitionConfig) } func getValidGetTasksRequest() *persistence.GetTasksRequest { return &persistence.GetTasksRequest{ DomainID: TestDomainID, DomainName: TestDomainName, TaskList: TestTaskListName, TaskType: int(types.TaskListTypeDecision), // The read level is the smallest taskID that we want to read, the maxReadLevel is the largest ReadLevel: 1, MaxReadLevel: common.Int64Ptr(15), BatchSize: 100, } } func getDecisionTaskListFilter() *nosqlplugin.TaskListFilter { return &nosqlplugin.TaskListFilter{ DomainID: TestDomainID, TaskListName: TestTaskListName, TaskListType: int(types.TaskListTypeDecision), } } func getExpectedTaskListRow() *nosqlplugin.TaskListRow { return &nosqlplugin.TaskListRow{ DomainID: TestDomainID, TaskListName: TestTaskListName, TaskListType: int(types.TaskListTypeDecision), RangeID: initialRangeID, TaskListKind: int(types.TaskListKindNormal), AckLevel: initialAckLevel, LastUpdatedTime: FixedTime, CurrentTimeStamp: FixedTime, } } func getExpectedTaskListRowWithPartitionConfig() *nosqlplugin.TaskListRow { return &nosqlplugin.TaskListRow{ DomainID: TestDomainID, TaskListName: TestTaskListName, TaskListType: int(types.TaskListTypeDecision), RangeID: initialRangeID, TaskListKind: int(types.TaskListKindNormal), AckLevel: initialAckLevel, LastUpdatedTime: FixedTime, CurrentTimeStamp: FixedTime, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*persistence.TaskListPartition{ 0: {}, 1: {}, 2: { IsolationGroups: []string{"foo"}, }, }, WritePartitions: map[int]*persistence.TaskListPartition{ 0: {}, 1: { IsolationGroups: []string{"bar"}, }, }, }, } } func checkTaskListRowExpected(t *testing.T, expectedRow *nosqlplugin.TaskListRow, taskList *nosqlplugin.TaskListRow) { t.Helper() assert.Equal(t, expectedRow, taskList) } func getExpectedTaskListInfo(kind types.TaskListKind) *persistence.TaskListInfo { return &persistence.TaskListInfo{ DomainID: TestDomainID, Name: TestTaskListName, TaskType: int(types.TaskListTypeDecision), RangeID: initialRangeID, AckLevel: initialAckLevel, Kind: int(kind), LastUpdated: FixedTime, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*persistence.TaskListPartition{ 0: {}, 1: {}, 2: { IsolationGroups: []string{"foo"}, }, }, WritePartitions: map[int]*persistence.TaskListPartition{ 0: {}, 1: { IsolationGroups: []string{"bar"}, }, }, }, } } func getValidDeleteTaskListRequest() *persistence.DeleteTaskListRequest { return &persistence.DeleteTaskListRequest{ DomainID: TestDomainID, DomainName: TestDomainName, TaskListName: TestTaskListName, TaskListType: int(types.TaskListTypeDecision), RangeID: 0, } } ================================================ FILE: common/persistence/nosql/nosql_test_utils.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "testing" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/persistence-tests/testcluster" ) // testCluster allows executing cassandra operations in testing. type testCluster struct { logger log.Logger keyspace string schemaBaseDir string replicas int cfg config.NoSQL } // TestClusterParams are params for test cluster initialization. type TestClusterParams struct { PluginName string KeySpace string Username string Password string Host string Port int ProtoVersion int SchemaBaseDir string // Replicas defaults to 1 if not set Replicas int // MaxConns defaults to 2 if not set MaxConns int } // NewTestCluster returns a new cassandra test cluster // if schemaBaseDir is empty, it will be auto-resolved based on os.Getwd() // otherwise the specified value will be used (used by internal tests) func NewTestCluster(t *testing.T, params TestClusterParams) testcluster.PersistenceTestCluster { return &testCluster{ logger: testlogger.New(t), keyspace: params.KeySpace, schemaBaseDir: params.SchemaBaseDir, replicas: replicas(params.Replicas), cfg: config.NoSQL{ PluginName: params.PluginName, User: params.Username, Password: params.Password, Hosts: params.Host, Port: params.Port, MaxConns: maxConns(params.MaxConns), Keyspace: params.KeySpace, ProtoVersion: params.ProtoVersion, }, } } // Config returns the persistence config for connecting to this test cluster func (s *testCluster) Config() config.Persistence { return config.Persistence{ DefaultStore: "test", VisibilityStore: "test", DataStores: map[string]config.DataStore{ "test": {NoSQL: &s.cfg}, }, TransactionSizeLimit: dynamicproperties.GetIntPropertyFn(constants.DefaultTransactionSizeLimit), ErrorInjectionRate: dynamicproperties.GetFloatPropertyFn(0), } } // SetupTestDatabase from PersistenceTestCluster interface func (s *testCluster) SetupTestDatabase() { adminDB, err := NewNoSQLAdminDB(&s.cfg, s.logger, &persistence.DynamicConfiguration{}) if err != nil { s.logger.Fatal(err.Error()) } err = adminDB.SetupTestDatabase(s.schemaBaseDir, s.replicas) if err != nil { s.logger.Fatal(err.Error()) } } // TearDownTestDatabase from PersistenceTestCluster interface func (s *testCluster) TearDownTestDatabase() { adminDB, err := NewNoSQLAdminDB(&s.cfg, s.logger, &persistence.DynamicConfiguration{}) if err != nil { s.logger.Fatal(err.Error()) } err = adminDB.TeardownTestDatabase() if err != nil { s.logger.Fatal(err.Error()) } } func replicas(replicas int) int { if replicas == 0 { return 1 } return replicas } func maxConns(maxConns int) int { if maxConns == 0 { return 2 } return maxConns } ================================================ FILE: common/persistence/nosql/nosql_visibility_store.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "context" "fmt" "time" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) // Fixed domain values for now const ( defaultCloseTTLSeconds = 86400 openExecutionTTLBuffer = int64(86400) // setting it to a day to account for shard going down ) type nosqlVisibilityStore struct { sortByCloseTime bool nosqlStore } // newNoSQLVisibilityStore is used to create an instance of VisibilityStore implementation func newNoSQLVisibilityStore( listClosedOrderingByCloseTime bool, cfg config.ShardedNoSQL, logger log.Logger, metricsClient metrics.Client, dc *persistence.DynamicConfiguration, ) (persistence.VisibilityStore, error) { shardedStore, err := newShardedNosqlStore(cfg, logger, metricsClient, dc) if err != nil { return nil, err } return &nosqlVisibilityStore{ sortByCloseTime: listClosedOrderingByCloseTime, nosqlStore: shardedStore.GetDefaultShard(), }, nil } func (v *nosqlVisibilityStore) RecordWorkflowExecutionStarted( ctx context.Context, request *persistence.InternalRecordWorkflowExecutionStartedRequest, ) error { ttl := int64(request.WorkflowTimeout.Seconds()) + openExecutionTTLBuffer err := v.db.InsertVisibility(ctx, ttl, &nosqlplugin.VisibilityRowForInsert{ DomainID: request.DomainUUID, VisibilityRow: nosqlplugin.VisibilityRow{ WorkflowID: request.WorkflowID, RunID: request.RunID, TypeName: request.WorkflowTypeName, StartTime: request.StartTimestamp, ExecutionTime: request.ExecutionTimestamp, Memo: request.Memo, TaskList: request.TaskList, IsCron: request.IsCron, NumClusters: request.NumClusters, UpdateTime: request.UpdateTimestamp, ShardID: request.ShardID, ExecutionStatus: request.ExecutionStatus, CronSchedule: request.CronSchedule, ScheduledExecutionTime: request.ScheduledExecutionTime, }, }) if err != nil { return convertCommonErrors(v.db, "RecordWorkflowExecutionStarted", err) } return nil } func (v *nosqlVisibilityStore) RecordWorkflowExecutionClosed( ctx context.Context, request *persistence.InternalRecordWorkflowExecutionClosedRequest, ) error { // Find how long to keep the row retention := request.RetentionPeriod if retention == 0 { retention = defaultCloseTTLSeconds * time.Second } err := v.db.UpdateVisibility(ctx, int64(retention.Seconds()), &nosqlplugin.VisibilityRowForUpdate{ DomainID: request.DomainUUID, UpdateOpenToClose: true, VisibilityRow: nosqlplugin.VisibilityRow{ WorkflowID: request.WorkflowID, RunID: request.RunID, TypeName: request.WorkflowTypeName, StartTime: request.StartTimestamp, ExecutionTime: request.ExecutionTimestamp, Memo: request.Memo, TaskList: request.TaskList, IsCron: request.IsCron, NumClusters: request.NumClusters, // closed workflow attributes Status: &request.Status, CloseTime: request.CloseTimestamp, HistoryLength: request.HistoryLength, UpdateTime: request.UpdateTimestamp, CronSchedule: request.CronSchedule, ExecutionStatus: request.ExecutionStatus, ScheduledExecutionTime: request.ScheduledExecutionTime, }, }) if err != nil { return convertCommonErrors(v.db, "RecordWorkflowExecutionClosed", err) } return nil } func (v *nosqlVisibilityStore) RecordWorkflowExecutionUninitialized( ctx context.Context, request *persistence.InternalRecordWorkflowExecutionUninitializedRequest, ) error { // temporary: not implemented, only implemented for ES return nil } func (v *nosqlVisibilityStore) UpsertWorkflowExecution( ctx context.Context, request *persistence.InternalUpsertWorkflowExecutionRequest, ) error { if persistence.IsNopUpsertWorkflowRequest(request) { return nil } return persistence.ErrVisibilityOperationNotSupported } func (v *nosqlVisibilityStore) ListOpenWorkflowExecutions( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, ) (*persistence.InternalListWorkflowExecutionsResponse, error) { resp, err := v.db.SelectVisibility(ctx, &nosqlplugin.VisibilityFilter{ ListRequest: *request, FilterType: nosqlplugin.AllOpen, SortType: nosqlplugin.SortByStartTime, }) if err != nil { return nil, convertCommonErrors(v.db, "ListOpenWorkflowExecutions", err) } return &persistence.InternalListWorkflowExecutionsResponse{ Executions: resp.Executions, NextPageToken: resp.NextPageToken, }, nil } func (v *nosqlVisibilityStore) ListClosedWorkflowExecutions( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, ) (*persistence.InternalListWorkflowExecutionsResponse, error) { var filter *nosqlplugin.VisibilityFilter if v.sortByCloseTime { filter = &nosqlplugin.VisibilityFilter{ ListRequest: *request, FilterType: nosqlplugin.AllClosed, SortType: nosqlplugin.SortByClosedTime, } } else { filter = &nosqlplugin.VisibilityFilter{ ListRequest: *request, FilterType: nosqlplugin.AllClosed, SortType: nosqlplugin.SortByStartTime, } } resp, err := v.db.SelectVisibility(ctx, filter) if err != nil { return nil, convertCommonErrors(v.db, "ListClosedWorkflowExecutions", err) } return &persistence.InternalListWorkflowExecutionsResponse{ Executions: resp.Executions, NextPageToken: resp.NextPageToken, }, nil } func (v *nosqlVisibilityStore) ListOpenWorkflowExecutionsByType( ctx context.Context, request *persistence.InternalListWorkflowExecutionsByTypeRequest, ) (*persistence.InternalListWorkflowExecutionsResponse, error) { resp, err := v.db.SelectVisibility(ctx, &nosqlplugin.VisibilityFilter{ ListRequest: request.InternalListWorkflowExecutionsRequest, FilterType: nosqlplugin.OpenByWorkflowType, SortType: nosqlplugin.SortByStartTime, WorkflowType: request.WorkflowTypeName, }) if err != nil { return nil, convertCommonErrors(v.db, "ListOpenWorkflowExecutionsByType", err) } return &persistence.InternalListWorkflowExecutionsResponse{ Executions: resp.Executions, NextPageToken: resp.NextPageToken, }, nil } func (v *nosqlVisibilityStore) ListClosedWorkflowExecutionsByType( ctx context.Context, request *persistence.InternalListWorkflowExecutionsByTypeRequest, ) (*persistence.InternalListWorkflowExecutionsResponse, error) { var filter *nosqlplugin.VisibilityFilter if v.sortByCloseTime { filter = &nosqlplugin.VisibilityFilter{ ListRequest: request.InternalListWorkflowExecutionsRequest, FilterType: nosqlplugin.ClosedByWorkflowType, SortType: nosqlplugin.SortByClosedTime, WorkflowType: request.WorkflowTypeName, } } else { filter = &nosqlplugin.VisibilityFilter{ ListRequest: request.InternalListWorkflowExecutionsRequest, FilterType: nosqlplugin.ClosedByWorkflowType, SortType: nosqlplugin.SortByStartTime, WorkflowType: request.WorkflowTypeName, } } resp, err := v.db.SelectVisibility(ctx, filter) if err != nil { return nil, convertCommonErrors(v.db, "ListClosedWorkflowExecutionsByType", err) } return &persistence.InternalListWorkflowExecutionsResponse{ Executions: resp.Executions, NextPageToken: resp.NextPageToken, }, nil } func (v *nosqlVisibilityStore) ListOpenWorkflowExecutionsByWorkflowID( ctx context.Context, request *persistence.InternalListWorkflowExecutionsByWorkflowIDRequest, ) (*persistence.InternalListWorkflowExecutionsResponse, error) { resp, err := v.db.SelectVisibility(ctx, &nosqlplugin.VisibilityFilter{ ListRequest: request.InternalListWorkflowExecutionsRequest, FilterType: nosqlplugin.OpenByWorkflowID, SortType: nosqlplugin.SortByStartTime, WorkflowID: request.WorkflowID, }) if err != nil { return nil, convertCommonErrors(v.db, "ListOpenWorkflowExecutionsByWorkflowID", err) } return &persistence.InternalListWorkflowExecutionsResponse{ Executions: resp.Executions, NextPageToken: resp.NextPageToken, }, nil } func (v *nosqlVisibilityStore) ListClosedWorkflowExecutionsByWorkflowID( ctx context.Context, request *persistence.InternalListWorkflowExecutionsByWorkflowIDRequest, ) (*persistence.InternalListWorkflowExecutionsResponse, error) { var filter *nosqlplugin.VisibilityFilter if v.sortByCloseTime { filter = &nosqlplugin.VisibilityFilter{ ListRequest: request.InternalListWorkflowExecutionsRequest, FilterType: nosqlplugin.ClosedByWorkflowID, SortType: nosqlplugin.SortByClosedTime, WorkflowID: request.WorkflowID, } } else { filter = &nosqlplugin.VisibilityFilter{ ListRequest: request.InternalListWorkflowExecutionsRequest, FilterType: nosqlplugin.ClosedByWorkflowID, SortType: nosqlplugin.SortByStartTime, WorkflowID: request.WorkflowID, } } resp, err := v.db.SelectVisibility(ctx, filter) if err != nil { return nil, convertCommonErrors(v.db, "ListClosedWorkflowExecutionsByWorkflowID", err) } return &persistence.InternalListWorkflowExecutionsResponse{ Executions: resp.Executions, NextPageToken: resp.NextPageToken, }, nil } func (v *nosqlVisibilityStore) ListClosedWorkflowExecutionsByStatus( ctx context.Context, request *persistence.InternalListClosedWorkflowExecutionsByStatusRequest, ) (*persistence.InternalListWorkflowExecutionsResponse, error) { var filter *nosqlplugin.VisibilityFilter if v.sortByCloseTime { filter = &nosqlplugin.VisibilityFilter{ ListRequest: request.InternalListWorkflowExecutionsRequest, FilterType: nosqlplugin.ClosedByClosedStatus, SortType: nosqlplugin.SortByClosedTime, CloseStatus: int32(request.Status), } } else { filter = &nosqlplugin.VisibilityFilter{ ListRequest: request.InternalListWorkflowExecutionsRequest, FilterType: nosqlplugin.ClosedByClosedStatus, SortType: nosqlplugin.SortByStartTime, CloseStatus: int32(request.Status), } } resp, err := v.db.SelectVisibility(ctx, filter) if err != nil { return nil, convertCommonErrors(v.db, "ListClosedWorkflowExecutionsByStatus", err) } return &persistence.InternalListWorkflowExecutionsResponse{ Executions: resp.Executions, NextPageToken: resp.NextPageToken, }, nil } func (v *nosqlVisibilityStore) GetClosedWorkflowExecution( ctx context.Context, request *persistence.InternalGetClosedWorkflowExecutionRequest, ) (*persistence.InternalGetClosedWorkflowExecutionResponse, error) { wfexecution, err := v.db.SelectOneClosedWorkflow(ctx, request.DomainUUID, request.Execution.GetWorkflowID(), request.Execution.GetRunID()) if err != nil { return nil, convertCommonErrors(v.db, "GetClosedWorkflowExecution", err) } if wfexecution == nil { // Special case: this API return nil,nil if not found(since we will deprecate it, it's not worth refactor to be consistent) return nil, &types.EntityNotExistsError{ Message: fmt.Sprintf("Workflow execution not found. WorkflowId: %v, RunId: %v", request.Execution.GetWorkflowID(), request.Execution.GetRunID()), } } return &persistence.InternalGetClosedWorkflowExecutionResponse{ Execution: wfexecution, }, nil } func (v *nosqlVisibilityStore) DeleteWorkflowExecution( ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest, ) error { err := v.db.DeleteVisibility(ctx, request.DomainID, request.WorkflowID, request.RunID) if err != nil { return convertCommonErrors(v.db, "DeleteWorkflowExecution", err) } return nil } func (v *nosqlVisibilityStore) DeleteUninitializedWorkflowExecution( ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest, ) error { // temporary: not implemented, only implemented for ES return nil } func (v *nosqlVisibilityStore) ListWorkflowExecutions( _ context.Context, _ *persistence.ListWorkflowExecutionsByQueryRequest, ) (*persistence.InternalListWorkflowExecutionsResponse, error) { return nil, persistence.ErrVisibilityOperationNotSupported } func (v *nosqlVisibilityStore) ScanWorkflowExecutions( _ context.Context, _ *persistence.ListWorkflowExecutionsByQueryRequest) (*persistence.InternalListWorkflowExecutionsResponse, error) { return nil, persistence.ErrVisibilityOperationNotSupported } func (v *nosqlVisibilityStore) CountWorkflowExecutions( _ context.Context, _ *persistence.CountWorkflowExecutionsRequest, ) (*persistence.CountWorkflowExecutionsResponse, error) { return nil, persistence.ErrVisibilityOperationNotSupported } ================================================ FILE: common/persistence/nosql/nosql_visibility_store_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package nosql import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) const ( testDomainID = "test-domain-id" testDomainName = "test-domain" testTaskListName = "test-tasklist" testWorkflowID = "test-workflow-id" testRunID = "test-run-id" testWorkflowTypeName = "test-workflow-type" ) func TestNewNoSQLVisibilityStore(t *testing.T) { cfg := getValidShardedNoSQLConfig() store, err := newNoSQLVisibilityStore(false, cfg, log.NewNoop(), metrics.NewNoopMetricsClient(), nil) assert.NoError(t, err) assert.NotNil(t, store) } func setupNoSQLVisibilityStoreMocks(t *testing.T) (*nosqlVisibilityStore, *nosqlplugin.MockDB) { ctrl := gomock.NewController(t) dbMock := nosqlplugin.NewMockDB(ctrl) nosqlSt := nosqlStore{ logger: log.NewNoop(), db: dbMock, } shardedNosqlStoreMock := NewMockshardedNosqlStore(ctrl) shardedNosqlStoreMock.EXPECT(). GetStoreShardByTaskList( TestDomainID, TestTaskListName, TestTaskType). Return(&nosqlSt, nil). AnyTimes() shardedNosqlStoreMock.EXPECT().GetDefaultShard().Return(nosqlStore{db: dbMock}).AnyTimes() visibilityStore := &nosqlVisibilityStore{ nosqlStore: shardedNosqlStoreMock.GetDefaultShard(), sortByCloseTime: false, } return visibilityStore, dbMock } func TestRecordWorkflowExecutionStarted_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().InsertVisibility(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) err := visibilityStore.RecordWorkflowExecutionStarted(context.Background(), &persistence.InternalRecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, }) assert.NoError(t, err) } func TestRecordWorkflowExecutionStarted_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().InsertVisibility(gomock.Any(), gomock.Any(), gomock.Any()).Return(assert.AnError) // The error _is_ a NotFoundError db.EXPECT().IsNotFoundError(assert.AnError).Return(true) err := visibilityStore.RecordWorkflowExecutionStarted(context.Background(), &persistence.InternalRecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, WorkflowTimeout: 20 * 60, StartTimestamp: time.Time{}, ExecutionTimestamp: time.Time{}, Memo: nil, TaskList: testTaskListName, IsCron: false, NumClusters: 2, UpdateTimestamp: time.Time{}, ShardID: 2, }) assert.ErrorContains(t, err, "RecordWorkflowExecutionStarted failed. Error:") } func TestRecordWorkflowExecutionClosed_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().UpdateVisibility(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) err := visibilityStore.RecordWorkflowExecutionClosed(context.Background(), &persistence.InternalRecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, }) assert.NoError(t, err) } func TestRecordWorkflowExecutionClosed_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().UpdateVisibility(gomock.Any(), gomock.Any(), gomock.Any()).Return(assert.AnError) // The error _is_ a NotFoundError db.EXPECT().IsNotFoundError(assert.AnError).Return(true) err := visibilityStore.RecordWorkflowExecutionClosed(context.Background(), &persistence.InternalRecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, WorkflowTypeName: testWorkflowTypeName, StartTimestamp: time.Time{}, ExecutionTimestamp: time.Time{}, Memo: nil, TaskList: testTaskListName, IsCron: false, NumClusters: 2, UpdateTimestamp: time.Time{}, ShardID: 2, }) assert.ErrorContains(t, err, "RecordWorkflowExecutionClosed failed. Error:") } func TestRecordWorkflowExecutionUninitialized(t *testing.T) { visibilityStore, _ := setupNoSQLVisibilityStoreMocks(t) err := visibilityStore.RecordWorkflowExecutionUninitialized(context.Background(), &persistence.InternalRecordWorkflowExecutionUninitializedRequest{}) assert.NoError(t, err) } func TestUpsertWorkflowExecution(t *testing.T) { visibilityStore, _ := setupNoSQLVisibilityStoreMocks(t) err := visibilityStore.UpsertWorkflowExecution(context.Background(), &persistence.InternalUpsertWorkflowExecutionRequest{ SearchAttributes: map[string][]byte{ definition.CadenceChangeVersion: nil, }, }) assert.NoError(t, err) err = visibilityStore.UpsertWorkflowExecution(context.Background(), &persistence.InternalUpsertWorkflowExecutionRequest{}) assert.Error(t, err) assert.Equal(t, persistence.ErrVisibilityOperationNotSupported, err) } func TestListOpenWorkflowExecutions_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(&nosqlplugin.SelectVisibilityResponse{ Executions: []*nosqlplugin.VisibilityRow{{ DomainID: testDomainID, WorkflowType: testWorkflowTypeName, WorkflowID: testWorkflowID, RunID: testRunID, TypeName: "test-name", }}, NextPageToken: nil, }, nil) response, err := visibilityStore.ListOpenWorkflowExecutions(context.Background(), &persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomainName, EarliestTime: time.Time{}, LatestTime: time.Time{}, PageSize: 2, NextPageToken: nil, }) assert.NoError(t, err) assert.Equal(t, len(response.Executions), 1) } func TestListOpenWorkflowExecutions_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) // The error _is_ a NotFoundError db.EXPECT().IsNotFoundError(assert.AnError).Return(true) _, err := visibilityStore.ListOpenWorkflowExecutions(context.Background(), &persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomainName, EarliestTime: time.Time{}, LatestTime: time.Time{}, PageSize: 2, NextPageToken: nil, }) assert.ErrorContains(t, err, "ListOpenWorkflowExecutions failed. Error:") } func TestListClosedWorkflowExecutions_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) visibilityStore.sortByCloseTime = true db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(&nosqlplugin.SelectVisibilityResponse{ Executions: []*nosqlplugin.VisibilityRow{{ DomainID: testDomainID, WorkflowType: testWorkflowTypeName, WorkflowID: testWorkflowID, RunID: testRunID, TypeName: "test-name", }}, NextPageToken: nil, }, nil) response, err := visibilityStore.ListClosedWorkflowExecutions(context.Background(), &persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomainName, EarliestTime: time.Time{}, LatestTime: time.Time{}, PageSize: 2, NextPageToken: nil, }) assert.NoError(t, err) assert.Equal(t, len(response.Executions), 1) } func TestListClosedWorkflowExecutions_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) db.EXPECT().IsNotFoundError(assert.AnError).Return(true) _, err := visibilityStore.ListClosedWorkflowExecutions(context.Background(), &persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomainName, EarliestTime: time.Time{}, LatestTime: time.Time{}, PageSize: 2, NextPageToken: nil, }) assert.ErrorContains(t, err, "ListClosedWorkflowExecutions failed. Error:") } func TestListOpenWorkflowExecutionsByType_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(&nosqlplugin.SelectVisibilityResponse{ Executions: []*nosqlplugin.VisibilityRow{{ DomainID: testDomainID, WorkflowType: testWorkflowTypeName, }}, NextPageToken: nil, }, nil) response, err := visibilityStore.ListOpenWorkflowExecutionsByType(context.Background(), &persistence.InternalListWorkflowExecutionsByTypeRequest{ InternalListWorkflowExecutionsRequest: persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, }, WorkflowTypeName: testWorkflowTypeName, }) assert.NoError(t, err) assert.Equal(t, len(response.Executions), 1) } func TestListOpenWorkflowExecutionsByType_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) db.EXPECT().IsNotFoundError(assert.AnError).Return(true) _, err := visibilityStore.ListOpenWorkflowExecutionsByType(context.Background(), &persistence.InternalListWorkflowExecutionsByTypeRequest{ InternalListWorkflowExecutionsRequest: persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, }, WorkflowTypeName: testWorkflowTypeName, }) assert.ErrorContains(t, err, "ListOpenWorkflowExecutionsByType failed. Error:") } func TestListClosedWorkflowExecutionsByType_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) visibilityStore.sortByCloseTime = true db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(&nosqlplugin.SelectVisibilityResponse{ Executions: []*nosqlplugin.VisibilityRow{{ DomainID: testDomainID, WorkflowType: testWorkflowTypeName, }}, NextPageToken: nil, }, nil) response, err := visibilityStore.ListClosedWorkflowExecutionsByType(context.Background(), &persistence.InternalListWorkflowExecutionsByTypeRequest{ InternalListWorkflowExecutionsRequest: persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, }, WorkflowTypeName: testWorkflowTypeName, }) assert.NoError(t, err) assert.Equal(t, 1, len(response.Executions)) } func TestListClosedWorkflowExecutionsByType_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) db.EXPECT().IsNotFoundError(assert.AnError).Return(true) _, err := visibilityStore.ListClosedWorkflowExecutionsByType(context.Background(), &persistence.InternalListWorkflowExecutionsByTypeRequest{ InternalListWorkflowExecutionsRequest: persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, }, WorkflowTypeName: testWorkflowTypeName, }) assert.ErrorContains(t, err, "ListClosedWorkflowExecutionsByType failed. Error:") } func TestListOpenWorkflowExecutionsByWorkflowID_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(&nosqlplugin.SelectVisibilityResponse{ Executions: []*nosqlplugin.VisibilityRow{{ DomainID: testDomainID, WorkflowType: testWorkflowTypeName, }}, NextPageToken: nil, }, nil) response, err := visibilityStore.ListOpenWorkflowExecutionsByWorkflowID(context.Background(), &persistence.InternalListWorkflowExecutionsByWorkflowIDRequest{ InternalListWorkflowExecutionsRequest: persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, }, WorkflowID: testWorkflowID, }) assert.NoError(t, err) assert.Equal(t, len(response.Executions), 1) } func TestListOpenWorkflowExecutionsByWorkflowID_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) db.EXPECT().IsNotFoundError(assert.AnError).Return(true) _, err := visibilityStore.ListOpenWorkflowExecutionsByWorkflowID(context.Background(), &persistence.InternalListWorkflowExecutionsByWorkflowIDRequest{ InternalListWorkflowExecutionsRequest: persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, }, WorkflowID: testWorkflowID, }) assert.ErrorContains(t, err, "ListOpenWorkflowExecutionsByWorkflowID failed. Error:") } func TestListClosedWorkflowExecutionsByWorkflowID_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) visibilityStore.sortByCloseTime = true db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(&nosqlplugin.SelectVisibilityResponse{ Executions: []*nosqlplugin.VisibilityRow{{ DomainID: testDomainID, WorkflowType: testWorkflowTypeName, }}, NextPageToken: nil, }, nil) response, err := visibilityStore.ListClosedWorkflowExecutionsByWorkflowID(context.Background(), &persistence.InternalListWorkflowExecutionsByWorkflowIDRequest{ InternalListWorkflowExecutionsRequest: persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, }, WorkflowID: testWorkflowID, }) assert.NoError(t, err) assert.Equal(t, len(response.Executions), 1) } func TestListClosedWorkflowExecutionsByWorkflowID_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) db.EXPECT().IsNotFoundError(assert.AnError).Return(true) _, err := visibilityStore.ListClosedWorkflowExecutionsByWorkflowID(context.Background(), &persistence.InternalListWorkflowExecutionsByWorkflowIDRequest{ InternalListWorkflowExecutionsRequest: persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, }, WorkflowID: testWorkflowID, }) assert.ErrorContains(t, err, "ListClosedWorkflowExecutionsByWorkflowID failed. Error:") } func TestListClosedWorkflowExecutionsByStatus_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) visibilityStore.sortByCloseTime = true db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(&nosqlplugin.SelectVisibilityResponse{ Executions: []*nosqlplugin.VisibilityRow{{ DomainID: testDomainID, WorkflowType: testWorkflowTypeName, }}, NextPageToken: nil, }, nil) response, err := visibilityStore.ListClosedWorkflowExecutionsByStatus(context.Background(), &persistence.InternalListClosedWorkflowExecutionsByStatusRequest{ InternalListWorkflowExecutionsRequest: persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, }, Status: 0, }) assert.NoError(t, err) assert.Equal(t, len(response.Executions), 1) } func TestListClosedWorkflowExecutionsByStatus_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectVisibility(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) db.EXPECT().IsNotFoundError(assert.AnError).Return(true) _, err := visibilityStore.ListClosedWorkflowExecutionsByStatus(context.Background(), &persistence.InternalListClosedWorkflowExecutionsByStatusRequest{ InternalListWorkflowExecutionsRequest: persistence.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, }, Status: 0, }) assert.ErrorContains(t, err, "ListClosedWorkflowExecutionsByStatus failed. Error:") } func TestGetClosedWorkflowExecution_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectOneClosedWorkflow(gomock.Any(), testDomainID, testWorkflowID, testRunID).Return(&nosqlplugin.VisibilityRow{ DomainID: testDomainID, WorkflowType: testWorkflowTypeName, WorkflowID: testWorkflowID, RunID: testRunID, TypeName: "test-name", }, nil) response, err := visibilityStore.GetClosedWorkflowExecution(context.Background(), &persistence.InternalGetClosedWorkflowExecutionRequest{ DomainUUID: testDomainID, Domain: testWorkflowID, Execution: types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, }) assert.NoError(t, err) assert.Equal(t, testWorkflowID, response.Execution.WorkflowID) } func TestGetClosedWorkflowExecution_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectOneClosedWorkflow(gomock.Any(), testDomainID, testWorkflowID, testRunID).Return(nil, assert.AnError) db.EXPECT().IsNotFoundError(assert.AnError).Return(true) _, err := visibilityStore.GetClosedWorkflowExecution(context.Background(), &persistence.InternalGetClosedWorkflowExecutionRequest{ DomainUUID: testDomainID, Domain: testWorkflowID, Execution: types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, }) assert.ErrorContains(t, err, "GetClosedWorkflowExecution failed. Error:") } func TestGetClosedWorkflowExecution_Failed_Special_Case(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().SelectOneClosedWorkflow(gomock.Any(), testDomainID, testWorkflowID, testRunID).Return(nil, nil) _, err := visibilityStore.GetClosedWorkflowExecution(context.Background(), &persistence.InternalGetClosedWorkflowExecutionRequest{ DomainUUID: testDomainID, Domain: testWorkflowID, Execution: types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, }) assert.ErrorContains(t, err, fmt.Sprintf("Workflow execution not found. WorkflowId: %v, RunId: %v", testWorkflowID, testRunID)) } func TestDeleteWorkflowExecution_Success(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().DeleteVisibility(gomock.Any(), testDomainID, testWorkflowID, testRunID).Return(nil) err := visibilityStore.DeleteWorkflowExecution(context.Background(), &persistence.VisibilityDeleteWorkflowExecutionRequest{ DomainID: testDomainID, Domain: testDomainName, RunID: testRunID, WorkflowID: testWorkflowID, }) assert.NoError(t, err) } func TestDeleteWorkflowExecution_Failed(t *testing.T) { visibilityStore, db := setupNoSQLVisibilityStoreMocks(t) db.EXPECT().DeleteVisibility(gomock.Any(), testDomainID, testWorkflowID, testRunID).Return(assert.AnError) db.EXPECT().IsNotFoundError(assert.AnError).Return(true) err := visibilityStore.DeleteWorkflowExecution(context.Background(), &persistence.VisibilityDeleteWorkflowExecutionRequest{ DomainID: testDomainID, Domain: testDomainName, RunID: testRunID, WorkflowID: testWorkflowID, }) assert.ErrorContains(t, err, "DeleteWorkflowExecution failed. Error:") } func TestDeleteUninitializedWorkflowExecution(t *testing.T) { visibilityStore, _ := setupNoSQLVisibilityStoreMocks(t) err := visibilityStore.DeleteUninitializedWorkflowExecution(context.Background(), &persistence.VisibilityDeleteWorkflowExecutionRequest{}) assert.NoError(t, err) } func TestListWorkflowExecutions(t *testing.T) { visibilityStore, _ := setupNoSQLVisibilityStoreMocks(t) _, err := visibilityStore.ListWorkflowExecutions(context.Background(), &persistence.ListWorkflowExecutionsByQueryRequest{}) assert.Error(t, err) assert.Equal(t, persistence.ErrVisibilityOperationNotSupported, err) } func TestScanWorkflowExecutions(t *testing.T) { visibilityStore, _ := setupNoSQLVisibilityStoreMocks(t) _, err := visibilityStore.ScanWorkflowExecutions(context.Background(), &persistence.ListWorkflowExecutionsByQueryRequest{}) assert.Error(t, err) assert.Equal(t, persistence.ErrVisibilityOperationNotSupported, err) } func TestCountWorkflowExecutions(t *testing.T) { visibilityStore, _ := setupNoSQLVisibilityStoreMocks(t) _, err := visibilityStore.CountWorkflowExecutions(context.Background(), &persistence.CountWorkflowExecutionsRequest{}) assert.Error(t, err) assert.Equal(t, persistence.ErrVisibilityOperationNotSupported, err) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/admin.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "fmt" "io/ioutil" "os" "strings" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/tools/cassandra" "github.com/uber/cadence/tools/common/schema" ) const ( testSchemaDir = "schema/cassandra/" ) var _ nosqlplugin.AdminDB = (*CDB)(nil) func (db *CDB) SetupTestDatabase(schemaBaseDir string, replicas int) error { err := db.createCassandraKeyspace(db.session, db.cfg.Keyspace, replicas, true) if err != nil { return err } if schemaBaseDir == "" { var err error schemaBaseDir, err = nosqlplugin.GetDefaultTestSchemaDir(testSchemaDir) if err != nil { return err } } err = db.loadSchema([]string{"schema.cql"}, schemaBaseDir) if err != nil { return err } err = db.loadVisibilitySchema([]string{"schema.cql"}, schemaBaseDir) if err != nil { return err } return nil } // loadSchema from PersistenceTestCluster interface func (db *CDB) loadSchema(fileNames []string, schemaBaseDir string) error { workflowSchemaDir := schemaBaseDir + "/cadence" err := loadCassandraSchema(workflowSchemaDir, fileNames, db.cfg.Hosts, db.cfg.Port, db.cfg.Keyspace, true, nil, db.cfg.ProtoVersion) if err != nil && !strings.Contains(err.Error(), "AlreadyExists") { // TODO: should we remove the second condition? return err } return nil } // loadVisibilitySchema from PersistenceTestCluster interface func (db *CDB) loadVisibilitySchema(fileNames []string, schemaBaseDir string) error { workflowSchemaDir := schemaBaseDir + "/visibility" err := loadCassandraSchema(workflowSchemaDir, fileNames, db.cfg.Hosts, db.cfg.Port, db.cfg.Keyspace, false, nil, db.cfg.ProtoVersion) if err != nil && !strings.Contains(err.Error(), "AlreadyExists") { // TODO: should we remove the second condition? return err } return nil } func (db *CDB) TeardownTestDatabase() error { err := db.dropCassandraKeyspace(db.session, db.cfg.Keyspace) if err != nil { return err } db.session.Close() return nil } // createCassandraKeyspace creates the keyspace using this session for given replica count func (db *CDB) createCassandraKeyspace(s gocql.Session, keyspace string, replicas int, overwrite bool) (err error) { // if overwrite flag is set, drop the keyspace and create a new one if overwrite { err = db.dropCassandraKeyspace(s, keyspace) if err != nil { db.logger.Error(`drop keyspace error`, tag.Error(err)) return } } err = s.Query(fmt.Sprintf(`CREATE KEYSPACE IF NOT EXISTS %s WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : %d}`, keyspace, replicas)).Exec() if err != nil { db.logger.Error(`create keyspace error`, tag.Error(err)) return } db.logger.Debugf("created namespace %s with replication factor %d", keyspace, replicas) return } // dropCassandraKeyspace drops the given keyspace, if it exists func (db *CDB) dropCassandraKeyspace(s gocql.Session, keyspace string) (err error) { err = s.Query(fmt.Sprintf("DROP KEYSPACE IF EXISTS %s", keyspace)).Exec() if err != nil { db.logger.Error(`drop keyspace error`, tag.Error(err)) return } db.logger.Debugf("dropped namespace %s", keyspace) return } // loadCassandraSchema loads the schema from the given .cql files on this keyspace func loadCassandraSchema( dir string, fileNames []string, hosts string, port int, keyspace string, override bool, tls *config.TLS, protoVersion int, ) (err error) { tmpFile, err := ioutil.TempFile("", "_cadence_") if err != nil { return fmt.Errorf("error creating tmp file:%v", err.Error()) } defer os.Remove(tmpFile.Name()) for _, file := range fileNames { // Flagged for potential file inclusion via variable. No user supplied input is included here - this just reads // schema files. // #nosec content, err := ioutil.ReadFile(dir + "/" + file) if err != nil { return fmt.Errorf("error reading contents of file %v:%v", file, err.Error()) } _, err = tmpFile.WriteString(string(content) + "\n") if err != nil { return fmt.Errorf("error writing string to file, err: %v", err.Error()) } } tmpFile.Close() config := &cassandra.SetupSchemaConfig{ CQLClientConfig: cassandra.CQLClientConfig{ Hosts: hosts, Port: port, Keyspace: keyspace, TLS: tls, ProtoVersion: protoVersion, }, SetupConfig: schema.SetupConfig{ SchemaFilePath: tmpFile.Name(), Overwrite: override, DisableVersioning: true, }, } err = cassandra.SetupSchema(config) if err != nil { err = fmt.Errorf("error loading schema:%v", err.Error()) } return } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/cluster_config.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) func (db *CDB) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { query := db.session.Query(templateInsertConfig, row.RowType, row.Version, row.Timestamp, row.Values.Data, row.Values.Encoding).WithContext(ctx) applied, err := query.MapScanCAS(make(map[string]interface{})) if err != nil { return err } if !applied { return nosqlplugin.NewConditionFailure("InsertConfig operation failed because of version collision") } return nil } func (db *CDB) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { var version int64 var timestamp time.Time var data []byte var encoding constants.EncodingType query := db.session.Query(templateSelectLatestConfig, rowType).WithContext(ctx) err := query.Scan(&rowType, &version, ×tamp, &data, &encoding) if err != nil { return nil, err } return &persistence.InternalConfigStoreEntry{ RowType: rowType, Version: version, Timestamp: timestamp, Values: &persistence.DataBlob{ Data: data, Encoding: encoding, }, }, err } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/cluster_config_cql.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra const ( // version is the clustering key(DESC order) so this query will always return the record with largest version templateSelectLatestConfig = `SELECT row_type, version, timestamp, values, encoding FROM cluster_config ` + `WHERE row_type = ? ` + `LIMIT 1;` templateInsertConfig = `INSERT INTO cluster_config (row_type, version, timestamp, values, encoding) ` + `VALUES (?, ?, ?, ?, ?) ` + `IF NOT EXISTS;` ) ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/constants.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "time" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" ) var ( defaultDateTime = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) defaultVisibilityTimestamp = p.UnixNanoToDBTimestamp(defaultDateTime.UnixNano()) ) const ( // Cassandra max support time is 2038-01-19T03:14:06+00:00. Updated this to 5 years to support until year 2033 // See https://github.com/uber/cadence/issues/4200 maxCassandraTTL = int64(157680000) // We use local serial consistency level as the default consistency level for conditional updates cassandraDefaultSerialConsLevel = gocql.LocalSerial // We use local quorum consistency level as the default consistency level cassandraDefaultConsLevel = gocql.LocalQuorum // Although Cadence core data models always require strong consistency, reading visibility is a special case that // eventual consistency is sufficient. // That's because the engine layer writes into visibility with eventual consistency anyway(using transfer tasks) // Do NOT use it in other places, unless you are sure it's the same special cases like reading visibility cassandraLowConslevel = gocql.LocalOne // We use all consistency level for delete operations to prevent the data resurrection issue cassandraAllConslevel = gocql.All ) const ( // Row types for table executions rowTypeShard = iota rowTypeExecution rowTypeTransferTask rowTypeTimerTask rowTypeReplicationTask rowTypeDLQ rowTypeCrossClusterTask rowTypeWorkflowRequestStart rowTypeWorkflowRequestSignal rowTypeWorkflowRequestCancel rowTypeWorkflowRequestReset rowTypeWorkflowActiveClusterSelectionPolicy ) // Guidelines for creating new special UUID constants // Each UUID should be of the form: E0000000-R000-f000-f000-00000000000x // Where x is any hexadecimal value, E represents the entity type valid values are: // E = {DomainID = 1, WorkflowID = 2, RunID = 3} // R represents row type in executions table, valid values are: // R = {Shard = 0, Execution = 1, Transfer = 2, Timer = 3, Replication = 4, DLQ = 5, CrossCluster = 6} const ( // Special Domains related constants emptyDomainID = "10000000-0000-f000-f000-000000000000" // Special Run IDs emptyRunID = "30000000-0000-f000-f000-000000000000" permanentRunID = "30000000-0000-f000-f000-000000000001" // Row Constants for Shard Row rowTypeShardDomainID = "10000000-1000-f000-f000-000000000000" rowTypeShardWorkflowID = "20000000-1000-f000-f000-000000000000" rowTypeShardRunID = "30000000-1000-f000-f000-000000000000" // Row Constants for Transfer Task Row rowTypeTransferDomainID = "10000000-3000-f000-f000-000000000000" rowTypeTransferWorkflowID = "20000000-3000-f000-f000-000000000000" rowTypeTransferRunID = "30000000-3000-f000-f000-000000000000" // Row Constants for Cross Cluster Task Row rowTypeCrossClusterDomainID = "10000000-7000-f000-f000-000000000000" rowTypeCrossClusterRunID = "30000000-7000-f000-f000-000000000000" // Row Constants for Timer Task Row rowTypeTimerDomainID = "10000000-4000-f000-f000-000000000000" rowTypeTimerWorkflowID = "20000000-4000-f000-f000-000000000000" rowTypeTimerRunID = "30000000-4000-f000-f000-000000000000" // Row Constants for Replication Task Row rowTypeReplicationDomainID = "10000000-5000-f000-f000-000000000000" rowTypeReplicationWorkflowID = "20000000-5000-f000-f000-000000000000" rowTypeReplicationRunID = "30000000-5000-f000-f000-000000000000" // Row Constants for Replication Task DLQ Row. Source cluster name will be used as WorkflowID. rowTypeDLQDomainID = "10000000-6000-f000-f000-000000000000" rowTypeDLQRunID = "30000000-6000-f000-f000-000000000000" // Special TaskId constants rowTypeExecutionTaskID = int64(-10) rowTypeShardTaskID = int64(-11) emptyInitiatedID = int64(-7) emptyWorkflowRequestVersion = int64(-1000) rowTypeWorkflowActiveClusterSelectionVersion = int64(-1001) workflowRequestTTLInSeconds = 10800 ) ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/db.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" ) // CDB represents a logical connection to Cassandra database type CDB struct { logger log.Logger client gocql.Client session gocql.Session cfg *config.NoSQL dc *persistence.DynamicConfiguration } var _ nosqlplugin.DB = (*CDB)(nil) // CassandraDBOption is used to provide optional settings for CDB object type CassandraDBOption func(*CDB) // DbWithClient returns a CDB option to set the gocql client. // If this is not used then the globally registered client is used. func DbWithClient(client gocql.Client) CassandraDBOption { return func(db *CDB) { db.client = client } } // NewCassandraDBFromSession returns a DB from a session func NewCassandraDBFromSession( cfg *config.NoSQL, session gocql.Session, logger log.Logger, dc *persistence.DynamicConfiguration, opts ...CassandraDBOption, ) *CDB { res := &CDB{ session: session, logger: logger, cfg: cfg, dc: dc, } for _, opt := range opts { opt(res) } if res.client == nil { res.client = gocql.GetRegisteredClient() } return res } func (db *CDB) Close() { if db.session != nil { db.session.Close() } } func (db *CDB) PluginName() string { return PluginName } func (db *CDB) IsNotFoundError(err error) bool { return db.client.IsNotFoundError(err) } func (db *CDB) IsTimeoutError(err error) bool { return db.client.IsTimeoutError(err) } func (db *CDB) IsThrottlingError(err error) bool { return db.client.IsThrottlingError(err) } func (db *CDB) IsDBUnavailableError(err error) bool { return db.client.IsDBUnavailableError(err) } func (db *CDB) isCassandraConsistencyError(err error) bool { return db.client.IsCassandraConsistencyError(err) } func (db *CDB) executeWithConsistencyAll(q gocql.Query) error { if db.dc != nil && db.dc.EnableCassandraAllConsistencyLevelDelete() { if err := q.Consistency(cassandraAllConslevel).Exec(); err != nil { if db.isCassandraConsistencyError(err) { db.logger.Warn("unable to complete the delete operation due to consistency issue", tag.Error(err)) return q.Consistency(cassandraDefaultConsLevel).Exec() } return err } return nil } return q.Exec() } func (db *CDB) executeBatchWithConsistencyAll(b gocql.Batch) error { if db.dc != nil && db.dc.EnableCassandraAllConsistencyLevelDelete() { if err := db.session.ExecuteBatch(b.Consistency(cassandraAllConslevel)); err != nil { if db.isCassandraConsistencyError(err) { db.logger.Warn("unable to complete the delete operation due to consistency issue", tag.Error(err)) return db.session.ExecuteBatch(b.Consistency(cassandraDefaultConsLevel)) } return err } return nil } return db.session.ExecuteBatch(b) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/db_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "errors" "testing" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" ) func TestCDBBasics(t *testing.T) { ctrl := gomock.NewController(t) session := gocql.NewMockSession(ctrl) client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) if db.PluginName() != PluginName { t.Errorf("got plugin name: %v but want %v", db.PluginName(), PluginName) } client.EXPECT().IsNotFoundError(nil).Return(true).Times(1) if db.IsNotFoundError(nil) != true { t.Errorf("IsNotFoundError returned false but want true") } client.EXPECT().IsTimeoutError(nil).Return(true).Times(1) if db.IsTimeoutError(nil) != true { t.Errorf("IsTimeoutError returned false but want true") } client.EXPECT().IsThrottlingError(nil).Return(true).Times(1) if db.IsThrottlingError(nil) != true { t.Errorf("IsNotFoundError returned false but want true") } client.EXPECT().IsDBUnavailableError(nil).Return(true).Times(1) if db.IsDBUnavailableError(nil) != true { t.Errorf("IsDBUnavailableError returned false but want true") } client.EXPECT().IsCassandraConsistencyError(nil).Return(true).Times(1) if db.isCassandraConsistencyError(nil) != true { t.Errorf("IsNotFoundError returned false but want true") } session.EXPECT().Close().Times(1) db.Close() } func TestExecuteWithConsistencyAll(t *testing.T) { tests := []struct { name string enableAllConsistencyLevelDelete bool queryMockPrep func(*gocql.MockQuery) clientMockPrep func(*gocql.MockClient) wantErr bool }{ { name: "all consistency level delete disabled - success", enableAllConsistencyLevelDelete: false, queryMockPrep: func(query *gocql.MockQuery) { query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "all consistency level delete disabled - failed", enableAllConsistencyLevelDelete: false, queryMockPrep: func(query *gocql.MockQuery) { query.EXPECT().Exec().Return(errors.New("failed")).Times(1) }, wantErr: true, }, { name: "all consistency level delete enabled - query consistency level all - success", enableAllConsistencyLevelDelete: true, queryMockPrep: func(query *gocql.MockQuery) { query.EXPECT().Consistency(cassandraAllConslevel).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "all consistency level delete enabled - query consistency level all - returns consistency error - success", enableAllConsistencyLevelDelete: true, queryMockPrep: func(query *gocql.MockQuery) { query.EXPECT().Consistency(cassandraAllConslevel).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("fail for the first call")).Times(1) query.EXPECT().Consistency(cassandraDefaultConsLevel).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, clientMockPrep: func(client *gocql.MockClient) { client.EXPECT().IsCassandraConsistencyError(gomock.Any()).Return(true).Times(1) }, wantErr: false, }, { name: "all consistency level delete enabled - query consistency level all - returns consistency error - failed", enableAllConsistencyLevelDelete: true, queryMockPrep: func(query *gocql.MockQuery) { query.EXPECT().Consistency(cassandraAllConslevel).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("fail for the first call")).Times(1) query.EXPECT().Consistency(cassandraDefaultConsLevel).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("fail for the 2nd call too")).Times(1) }, clientMockPrep: func(client *gocql.MockClient) { client.EXPECT().IsCassandraConsistencyError(gomock.Any()).Return(true).Times(1) }, wantErr: true, }, { name: "all consistency level delete enabled - query consistency level all - returns non-consistency error - failed", enableAllConsistencyLevelDelete: true, queryMockPrep: func(query *gocql.MockQuery) { query.EXPECT().Consistency(cassandraAllConslevel).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed")).Times(1) }, clientMockPrep: func(client *gocql.MockClient) { client.EXPECT().IsCassandraConsistencyError(gomock.Any()).Return(false).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) session := gocql.NewMockSession(ctrl) client := gocql.NewMockClient(ctrl) query := gocql.NewMockQuery(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{ EnableCassandraAllConsistencyLevelDelete: func(opts ...dynamicproperties.FilterOption) bool { return tc.enableAllConsistencyLevelDelete }, } if tc.queryMockPrep != nil { tc.queryMockPrep(query) } if tc.clientMockPrep != nil { tc.clientMockPrep(client) } db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.executeWithConsistencyAll(query) if (err != nil) != tc.wantErr { t.Errorf("got err: %v but wantErr: %v", err, tc.wantErr) } }) } } func TestExecuteBatchWithConsistencyAll(t *testing.T) { tests := []struct { name string enableAllConsistencyLevelDelete bool sessionMockPrep func(*gocql.MockSession) batchMockPrep func(*gocql.MockBatch) clientMockPrep func(*gocql.MockClient) wantErr bool }{ { name: "all consistency level delete disabled - success", enableAllConsistencyLevelDelete: false, sessionMockPrep: func(session *gocql.MockSession) { session.EXPECT().ExecuteBatch(gomock.Any()).Return(nil).Times(1) }, wantErr: false, }, { name: "all consistency level delete disabled - failed", enableAllConsistencyLevelDelete: false, sessionMockPrep: func(session *gocql.MockSession) { session.EXPECT().ExecuteBatch(gomock.Any()).Return(errors.New("failed")).Times(1) }, wantErr: true, }, { name: "all consistency level delete enabled - success", enableAllConsistencyLevelDelete: true, sessionMockPrep: func(session *gocql.MockSession) { session.EXPECT().ExecuteBatch(gomock.Any()).Return(nil).Times(1) }, batchMockPrep: func(batch *gocql.MockBatch) { batch.EXPECT().Consistency(cassandraAllConslevel).Return(batch).Times(1) }, wantErr: false, }, { name: "all consistency level delete enabled - executebatch(all) failed with consistency err - success", enableAllConsistencyLevelDelete: true, sessionMockPrep: func(session *gocql.MockSession) { session.EXPECT().ExecuteBatch(gomock.Any()).Return(errors.New("fail the first call")).Times(1) // second call succeeds session.EXPECT().ExecuteBatch(gomock.Any()).Return(nil).Times(1) }, clientMockPrep: func(client *gocql.MockClient) { client.EXPECT().IsCassandraConsistencyError(gomock.Any()).Return(true).Times(1) }, batchMockPrep: func(batch *gocql.MockBatch) { batch.EXPECT().Consistency(cassandraAllConslevel).Return(batch).Times(1) batch.EXPECT().Consistency(cassandraDefaultConsLevel).Return(batch).Times(1) }, wantErr: false, }, { name: "all consistency level delete enabled - executebatch(all) failed with non-consistency err - failed", enableAllConsistencyLevelDelete: true, sessionMockPrep: func(session *gocql.MockSession) { session.EXPECT().ExecuteBatch(gomock.Any()).Return(errors.New("fail the first call")).Times(1) }, clientMockPrep: func(client *gocql.MockClient) { client.EXPECT().IsCassandraConsistencyError(gomock.Any()).Return(false).Times(1) }, batchMockPrep: func(batch *gocql.MockBatch) { batch.EXPECT().Consistency(cassandraAllConslevel).Return(batch).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) session := gocql.NewMockSession(ctrl) client := gocql.NewMockClient(ctrl) batch := gocql.NewMockBatch(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{ EnableCassandraAllConsistencyLevelDelete: func(opts ...dynamicproperties.FilterOption) bool { return tc.enableAllConsistencyLevelDelete }, } if tc.sessionMockPrep != nil { tc.sessionMockPrep(session) } if tc.batchMockPrep != nil { tc.batchMockPrep(batch) } if tc.clientMockPrep != nil { tc.clientMockPrep(client) } db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.executeBatchWithConsistencyAll(batch) if (err != nil) != tc.wantErr { t.Errorf("got err: %v but wantErr: %v", err, tc.wantErr) } }) } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/domain.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "fmt" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/types" ) const ( constDomainPartition = 0 domainMetadataRecordName = "cadence-domain-metadata" emptyFailoverEndTime = int64(0) ) // Insert a new record to domain // return types.DomainAlreadyExistsError error if failed or already exists // Must return ConditionFailure error if other condition doesn't match func (db *CDB) InsertDomain(ctx context.Context, row *nosqlplugin.DomainRow) error { timeStamp := row.CurrentTimeStamp query := db.session.Query(templateCreateDomainQuery, row.Info.ID, row.Info.Name, timeStamp).WithContext(ctx) applied, err := query.MapScanCAS(make(map[string]interface{})) if err != nil { return err } if !applied { return fmt.Errorf("CreateDomain operation failed because of uuid collision") } metadataNotificationVersion, err := db.SelectDomainMetadata(ctx) if err != nil { return err } batch := db.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) failoverEndTime := emptyFailoverEndTime if row.FailoverEndTime != nil { failoverEndTime = row.FailoverEndTime.UnixNano() } isolationGroupData, isolationGroupEncoding := getIsolationGroupFields(row) asyncWFConfigData, asyncWFConfigEncoding := getAsyncWFConfigFields(row) activeClustersData, activeClustersEncoding := getActiveClustersFields(row) batch.Query(templateCreateDomainByNameQueryWithinBatchV2, constDomainPartition, row.Info.Name, row.Info.ID, row.Info.Name, row.Info.Status, row.Info.Description, row.Info.OwnerEmail, row.Info.Data, common.DurationToDays(row.Config.Retention), row.Config.EmitMetric, row.Config.ArchivalBucket, row.Config.ArchivalStatus, row.Config.HistoryArchivalStatus, row.Config.HistoryArchivalURI, row.Config.VisibilityArchivalStatus, row.Config.VisibilityArchivalURI, row.Config.BadBinaries.Data, string(row.Config.BadBinaries.Encoding), isolationGroupData, isolationGroupEncoding, asyncWFConfigData, asyncWFConfigEncoding, row.ReplicationConfig.ActiveClusterName, persistence.SerializeClusterConfigs(row.ReplicationConfig.Clusters), activeClustersData, activeClustersEncoding, row.IsGlobalDomain, row.ConfigVersion, row.FailoverVersion, persistence.InitialFailoverNotificationVersion, constants.InitialPreviousFailoverVersion, failoverEndTime, row.LastUpdatedTime.UnixNano(), metadataNotificationVersion, timeStamp, ) db.updateMetadataBatch(batch, metadataNotificationVersion) previous := make(map[string]interface{}) applied, iter, err := db.session.MapExecuteBatchCAS(batch, previous) defer func() { if iter != nil { _ = iter.Close() } }() if err != nil { return err } if !applied { // Domain already exist. Delete orphan domain record before returning back to user if errDelete := db.session.Query(templateDeleteDomainQuery, row.Info.ID).WithContext(ctx).Exec(); errDelete != nil { db.logger.Warn("Unable to delete orphan domain record. Error", tag.Error(errDelete)) } for { // first iter MapScan is done inside MapExecuteBatchCAS if domain, ok := previous["name"].(string); ok && domain == row.Info.Name { db.logger.Warn("Domain already exists", tag.WorkflowDomainName(domain)) return &types.DomainAlreadyExistsError{ Message: fmt.Sprintf("Domain %v already exists", previous["domain"]), } } previous = make(map[string]interface{}) if !iter.MapScan(previous) { break } } db.logger.Warn("Create domain operation failed because of condition update failure on domain metadata record") return nosqlplugin.NewConditionFailure("domain") } return nil } func (db *CDB) updateMetadataBatch( batch gocql.Batch, notificationVersion int64, ) { var nextVersion int64 = 1 var currentVersion *int64 if notificationVersion > 0 { nextVersion = notificationVersion + 1 currentVersion = ¬ificationVersion } batch.Query(templateUpdateMetadataQueryWithinBatchV2, nextVersion, constDomainPartition, domainMetadataRecordName, currentVersion, ) } // Update domain func (db *CDB) UpdateDomain(ctx context.Context, row *nosqlplugin.DomainRow) error { batch := db.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) failoverEndTime := emptyFailoverEndTime if row.FailoverEndTime != nil { failoverEndTime = row.FailoverEndTime.UnixNano() } isolationGroupData, isolationGroupEncoding := getIsolationGroupFields(row) asyncWFConfigData, asyncWFConfigEncoding := getAsyncWFConfigFields(row) activeClustersData, activeClustersEncoding := getActiveClustersFields(row) batch.Query(templateUpdateDomainByNameQueryWithinBatchV2, row.Info.ID, row.Info.Name, row.Info.Status, row.Info.Description, row.Info.OwnerEmail, row.Info.Data, common.DurationToDays(row.Config.Retention), row.Config.EmitMetric, row.Config.ArchivalBucket, row.Config.ArchivalStatus, row.Config.HistoryArchivalStatus, row.Config.HistoryArchivalURI, row.Config.VisibilityArchivalStatus, row.Config.VisibilityArchivalURI, row.Config.BadBinaries.Data, string(row.Config.BadBinaries.Encoding), isolationGroupData, isolationGroupEncoding, asyncWFConfigData, asyncWFConfigEncoding, row.ReplicationConfig.ActiveClusterName, persistence.SerializeClusterConfigs(row.ReplicationConfig.Clusters), activeClustersData, activeClustersEncoding, row.ConfigVersion, row.FailoverVersion, row.FailoverNotificationVersion, row.PreviousFailoverVersion, failoverEndTime, row.LastUpdatedTime.UnixNano(), row.NotificationVersion, constDomainPartition, row.Info.Name, ) db.updateMetadataBatch(batch, row.NotificationVersion) previous := make(map[string]interface{}) applied, iter, err := db.session.MapExecuteBatchCAS(batch, previous) defer func() { if iter != nil { iter.Close() } }() if err != nil { return err } if !applied { return nosqlplugin.NewConditionFailure("domain") } return nil } // Get one domain data, either by domainID or domainName func (db *CDB) SelectDomain( ctx context.Context, domainID *string, domainName *string, ) (*nosqlplugin.DomainRow, error) { if domainID != nil && domainName != nil { return nil, fmt.Errorf("GetDomain operation failed. Both ID and Name specified in request") } else if domainID == nil && domainName == nil { return nil, fmt.Errorf("GetDomain operation failed. Both ID and Name are empty") } var query gocql.Query var err error if domainID != nil { query = db.session.Query(templateGetDomainQuery, domainID).WithContext(ctx) err = query.Scan(&domainName) if err != nil { return nil, err } } info := &persistence.DomainInfo{} config := &persistence.InternalDomainConfig{} replicationConfig := &persistence.InternalDomainReplicationConfig{} // because of encoding/types, we can't directly read from config struct var badBinariesData []byte var badBinariesDataEncoding string var replicationClusters []map[string]interface{} var failoverNotificationVersion int64 var notificationVersion int64 var failoverVersion int64 var previousFailoverVersion int64 var failoverEndTime int64 var lastUpdatedTime int64 var configVersion int64 var isGlobalDomain bool var retentionDays int32 var isolationGroupData []byte var isolationGroupEncoding string var asyncWFConfigData []byte var asyncWFConfigEncoding string var activeClustersData []byte var activeClustersEncoding string query = db.session.Query(templateGetDomainByNameQueryV2, constDomainPartition, domainName).WithContext(ctx) err = query.Scan( &info.ID, &info.Name, &info.Status, &info.Description, &info.OwnerEmail, &info.Data, &retentionDays, &config.EmitMetric, &config.ArchivalBucket, &config.ArchivalStatus, &config.HistoryArchivalStatus, &config.HistoryArchivalURI, &config.VisibilityArchivalStatus, &config.VisibilityArchivalURI, &badBinariesData, &badBinariesDataEncoding, &replicationConfig.ActiveClusterName, &replicationClusters, &activeClustersData, &activeClustersEncoding, &isolationGroupData, &isolationGroupEncoding, &asyncWFConfigData, &asyncWFConfigEncoding, &isGlobalDomain, &configVersion, &failoverVersion, &failoverNotificationVersion, &previousFailoverVersion, &failoverEndTime, &lastUpdatedTime, ¬ificationVersion, ) if err != nil { return nil, err } config.IsolationGroups = persistence.NewDataBlob(isolationGroupData, constants.EncodingType(isolationGroupEncoding)) config.AsyncWorkflowsConfig = persistence.NewDataBlob(asyncWFConfigData, constants.EncodingType(asyncWFConfigEncoding)) config.BadBinaries = persistence.NewDataBlob(badBinariesData, constants.EncodingType(badBinariesDataEncoding)) config.Retention = common.DaysToDuration(retentionDays) replicationConfig.Clusters = persistence.DeserializeClusterConfigs(replicationClusters) replicationConfig.ActiveClustersConfig = persistence.NewDataBlob(activeClustersData, constants.EncodingType(activeClustersEncoding)) dr := &nosqlplugin.DomainRow{ Info: info, Config: config, ReplicationConfig: replicationConfig, ConfigVersion: configVersion, FailoverVersion: failoverVersion, FailoverNotificationVersion: failoverNotificationVersion, PreviousFailoverVersion: previousFailoverVersion, NotificationVersion: notificationVersion, LastUpdatedTime: time.Unix(0, lastUpdatedTime), IsGlobalDomain: isGlobalDomain, } if failoverEndTime > emptyFailoverEndTime { dr.FailoverEndTime = common.TimePtr(time.Unix(0, failoverEndTime)) } return dr, nil } // Get all domain data func (db *CDB) SelectAllDomains( ctx context.Context, pageSize int, pageToken []byte, ) ([]*nosqlplugin.DomainRow, []byte, error) { query := db.session.Query(templateListDomainQueryV2, constDomainPartition).WithContext(ctx) iter := query.PageSize(pageSize).PageState(pageToken).Iter() if iter == nil { return nil, nil, &types.InternalServiceError{ Message: "SelectAllDomains operation failed. Not able to create query iterator.", } } var name string domain := &nosqlplugin.DomainRow{ Info: &persistence.DomainInfo{}, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, } var replicationClusters []map[string]interface{} var badBinariesData []byte var badBinariesDataEncoding string var isolationGroups []byte var isolationGroupsEncoding string var asyncWFConfigData []byte var asyncWFConfigEncoding string var retentionDays int32 var failoverEndTime int64 var lastUpdateTime int64 var activeClustersData []byte var activeClustersEncoding string var rows []*nosqlplugin.DomainRow for iter.Scan( &name, &domain.Info.ID, &domain.Info.Name, &domain.Info.Status, &domain.Info.Description, &domain.Info.OwnerEmail, &domain.Info.Data, &retentionDays, &domain.Config.EmitMetric, &domain.Config.ArchivalBucket, &domain.Config.ArchivalStatus, &domain.Config.HistoryArchivalStatus, &domain.Config.HistoryArchivalURI, &domain.Config.VisibilityArchivalStatus, &domain.Config.VisibilityArchivalURI, &badBinariesData, &badBinariesDataEncoding, &isolationGroups, &isolationGroupsEncoding, &asyncWFConfigData, &asyncWFConfigEncoding, &domain.ReplicationConfig.ActiveClusterName, &replicationClusters, &activeClustersData, &activeClustersEncoding, &domain.IsGlobalDomain, &domain.ConfigVersion, &domain.FailoverVersion, &domain.FailoverNotificationVersion, &domain.PreviousFailoverVersion, &failoverEndTime, &lastUpdateTime, &domain.NotificationVersion, ) { if name != domainMetadataRecordName { // do not include the metadata record domain.Config.BadBinaries = persistence.NewDataBlob(badBinariesData, constants.EncodingType(badBinariesDataEncoding)) domain.ReplicationConfig.Clusters = persistence.DeserializeClusterConfigs(replicationClusters) domain.ReplicationConfig.ActiveClustersConfig = persistence.NewDataBlob(activeClustersData, constants.EncodingType(activeClustersEncoding)) domain.Config.Retention = common.DaysToDuration(retentionDays) domain.Config.IsolationGroups = persistence.NewDataBlob(isolationGroups, constants.EncodingType(isolationGroupsEncoding)) domain.Config.AsyncWorkflowsConfig = persistence.NewDataBlob(asyncWFConfigData, constants.EncodingType(asyncWFConfigEncoding)) domain.LastUpdatedTime = time.Unix(0, lastUpdateTime) if failoverEndTime > emptyFailoverEndTime { domain.FailoverEndTime = common.TimePtr(time.Unix(0, failoverEndTime)) } rows = append(rows, domain) } replicationClusters = []map[string]interface{}{} badBinariesData = []byte("") badBinariesDataEncoding = "" isolationGroups = []byte("") isolationGroupsEncoding = "" asyncWFConfigData = []byte("") asyncWFConfigEncoding = "" failoverEndTime = 0 lastUpdateTime = 0 retentionDays = 0 activeClustersData = []byte("") activeClustersEncoding = "" domain = &nosqlplugin.DomainRow{ Info: &persistence.DomainInfo{}, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, } } nextPageToken := iter.PageState() if err := iter.Close(); err != nil { return nil, nil, err } return rows, nextPageToken, nil } // Delete a domain, either by domainID or domainName func (db *CDB) DeleteDomain(ctx context.Context, domainID *string, domainName *string) error { if domainName == nil && domainID == nil { return fmt.Errorf("must provide either domainID or domainName") } if domainName == nil { query := db.session.Query(templateGetDomainQuery, domainID).WithContext(ctx) var name string err := query.Scan(&name) if err != nil { if db.client.IsNotFoundError(err) { return nil } return err } domainName = common.StringPtr(name) } else { var id string query := db.session.Query(templateGetDomainByNameQueryV2, constDomainPartition, *domainName).WithContext(ctx) err := query.Scan(&id, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) if err != nil { if db.client.IsNotFoundError(err) { return nil } return err } domainID = common.StringPtr(id) } return db.deleteDomain(ctx, *domainName, *domainID) } func (db *CDB) SelectDomainMetadata(ctx context.Context) (int64, error) { var notificationVersion int64 query := db.session.Query(templateGetMetadataQueryV2, constDomainPartition, domainMetadataRecordName).WithContext(ctx) err := query.Scan(¬ificationVersion) if err != nil { if db.client.IsNotFoundError(err) { // this error can be thrown in the very beginning, // i.e. when domains_by_name_v2 is initialized // TODO ??? really???? return 0, nil } return -1, err } return notificationVersion, nil } func (db *CDB) deleteDomain(ctx context.Context, name, ID string) error { query := db.session.Query(templateDeleteDomainByNameQueryV2, constDomainPartition, name).WithContext(ctx) if err := db.executeWithConsistencyAll(query); err != nil { return err } query = db.session.Query(templateDeleteDomainQuery, ID).WithContext(ctx) return db.executeWithConsistencyAll(query) } func getIsolationGroupFields(row *nosqlplugin.DomainRow) ([]byte, string) { var d []byte var e string if row != nil && row.Config != nil && row.Config.IsolationGroups != nil { d = row.Config.IsolationGroups.GetData() e = row.Config.IsolationGroups.GetEncodingString() } return d, e } func getAsyncWFConfigFields(row *nosqlplugin.DomainRow) ([]byte, string) { var d []byte var e string if row != nil && row.Config != nil && row.Config.AsyncWorkflowsConfig != nil { d = row.Config.AsyncWorkflowsConfig.GetData() e = row.Config.AsyncWorkflowsConfig.GetEncodingString() } return d, e } func getActiveClustersFields(row *nosqlplugin.DomainRow) ([]byte, string) { var d []byte var e string if row != nil && row.ReplicationConfig != nil && row.ReplicationConfig.ActiveClustersConfig != nil { d = row.ReplicationConfig.ActiveClustersConfig.GetData() e = row.ReplicationConfig.ActiveClustersConfig.GetEncodingString() } return d, e } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/domain_audit_log.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) const ( templateInsertDomainAuditLogQuery = `INSERT INTO domain_audit_log (` + `domain_id, event_id, state_before, state_before_encoding, state_after, state_after_encoding, ` + `operation_type, created_time, last_updated_time, identity, identity_type, comment) ` + `VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) USING TTL ?` templateSelectDomainAuditLogsQuery = `SELECT ` + `event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, ` + `operation_type, created_time, last_updated_time, identity, identity_type, comment ` + `FROM domain_audit_log ` + `WHERE domain_id = ? AND operation_type = ? ` + `AND created_time >= ? AND created_time < ?` ) // InsertDomainAuditLog inserts a new audit log entry for a domain operation func (db *CDB) InsertDomainAuditLog(ctx context.Context, row *nosqlplugin.DomainAuditLogRow) error { query := db.session.Query(templateInsertDomainAuditLogQuery, row.DomainID, row.EventID, row.StateBefore, row.StateBeforeEncoding, row.StateAfter, row.StateAfterEncoding, row.OperationType, row.CreatedTime, row.LastUpdatedTime, row.Identity, row.IdentityType, row.Comment, row.TTLSeconds, ).WithContext(ctx) err := query.Exec() return err } // SelectDomainAuditLogs returns audit log entries for a domain and operation type func (db *CDB) SelectDomainAuditLogs(ctx context.Context, filter *nosqlplugin.DomainAuditLogFilter) ([]*nosqlplugin.DomainAuditLogRow, []byte, error) { if filter.MinCreatedTime == nil || filter.MaxCreatedTime == nil { return nil, nil, &types.InternalServiceError{ Message: "SelectDomainAuditLogs requires non-nil MinCreatedTime and MaxCreatedTime", } } query := db.session.Query(templateSelectDomainAuditLogsQuery, filter.DomainID, filter.OperationType, *filter.MinCreatedTime, *filter.MaxCreatedTime, ).WithContext(ctx) // Set page size if filter.PageSize > 0 { query = query.PageSize(filter.PageSize) } // Set page state for pagination if len(filter.NextPageToken) > 0 { query = query.PageState(filter.NextPageToken) } iter := query.Iter() if iter == nil { return nil, nil, &types.InternalServiceError{ Message: "SelectDomainAuditLogs operation failed. Not able to create query iterator.", } } var rows []*nosqlplugin.DomainAuditLogRow row := &nosqlplugin.DomainAuditLogRow{} for iter.Scan( &row.EventID, &row.DomainID, &row.StateBefore, &row.StateBeforeEncoding, &row.StateAfter, &row.StateAfterEncoding, &row.OperationType, &row.CreatedTime, &row.LastUpdatedTime, &row.Identity, &row.IdentityType, &row.Comment, ) { rows = append(rows, row) row = &nosqlplugin.DomainAuditLogRow{} // Break after collecting PageSize number of rows if filter.PageSize > 0 && len(rows) >= filter.PageSize { break } } nextPageToken := iter.PageState() if err := iter.Close(); err != nil { return nil, nil, err } return rows, nextPageToken, nil } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/domain_audit_log_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" ) func TestInsertDomainAuditLog(t *testing.T) { now := time.Now().UTC() domainID := "test-domain-id" eventID := "test-event-id" tests := []struct { name string row *nosqlplugin.DomainAuditLogRow queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "successfully inserted", row: &nosqlplugin.DomainAuditLogRow{ DomainID: domainID, EventID: eventID, StateBefore: []byte("state-before"), StateBeforeEncoding: "json", StateAfter: []byte("state-after"), StateAfterEncoding: "json", OperationType: persistence.DomainAuditOperationTypeFailover, CreatedTime: now, LastUpdatedTime: now, Identity: "test-identity", IdentityType: "user", Comment: "test comment", }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, }, { name: "exec failed", row: &nosqlplugin.DomainAuditLogRow{ DomainID: domainID, EventID: eventID, OperationType: persistence.DomainAuditOperationTypeFailover, CreatedTime: now, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("exec failed")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.InsertDomainAuditLog(context.Background(), tc.row) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } }) } } func TestSelectDomainAuditLogs(t *testing.T) { domainID := "test-domain-id" operationType := persistence.DomainAuditOperationTypeFailover minTime := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) maxTime := time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC) createdTime1 := time.Date(2024, 6, 1, 12, 0, 0, 0, time.UTC) createdTime2 := time.Date(2024, 7, 1, 12, 0, 0, 0, time.UTC) tests := []struct { name string filter *nosqlplugin.DomainAuditLogFilter queryMockFn func(query *gocql.MockQuery) iterMockFn func(iter *gocql.MockIter) wantRows []*nosqlplugin.DomainAuditLogRow wantToken []byte wantQueries []string wantErr bool }{ { name: "success with default time range and no results", filter: &nosqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) }, iterMockFn: func(iter *gocql.MockIter) { iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(false).Times(1) iter.EXPECT().PageState().Return([]byte(nil)).Times(1) iter.EXPECT().Close().Return(nil).Times(1) }, wantRows: []*nosqlplugin.DomainAuditLogRow{}, wantToken: nil, wantQueries: []string{`SELECT event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment FROM domain_audit_log WHERE domain_id = test-domain-id AND operation_type = Failover AND created_time >= 2024-01-01T00:00:00Z AND created_time < 2024-12-31T23:59:59Z`}, }, { name: "success with custom time range and single result", filter: &nosqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) }, iterMockFn: func(iter *gocql.MockIter) { // First call - return true and populate fields iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).DoAndReturn(func(args ...interface{}) bool { *args[0].(*string) = "event-1" *args[1].(*string) = domainID *args[2].(*[]byte) = []byte("state-before-1") *args[3].(*string) = "json" *args[4].(*[]byte) = []byte("state-after-1") *args[5].(*string) = "json" *args[6].(*persistence.DomainAuditOperationType) = operationType *args[7].(*time.Time) = createdTime1 *args[8].(*time.Time) = createdTime1 *args[9].(*string) = "identity-1" *args[10].(*string) = "user" *args[11].(*string) = "comment-1" return true }).Times(1) // Second call - return false to end iteration iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(false).Times(1) iter.EXPECT().PageState().Return([]byte("next-page")).Times(1) iter.EXPECT().Close().Return(nil).Times(1) }, wantRows: []*nosqlplugin.DomainAuditLogRow{ { EventID: "event-1", DomainID: domainID, StateBefore: []byte("state-before-1"), StateBeforeEncoding: "json", StateAfter: []byte("state-after-1"), StateAfterEncoding: "json", OperationType: operationType, CreatedTime: createdTime1, LastUpdatedTime: createdTime1, Identity: "identity-1", IdentityType: "user", Comment: "comment-1", }, }, wantToken: []byte("next-page"), wantQueries: []string{`SELECT event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment FROM domain_audit_log WHERE domain_id = test-domain-id AND operation_type = Failover AND created_time >= 2024-01-01T00:00:00Z AND created_time < 2024-12-31T23:59:59Z`}, }, { name: "success with page size set", filter: &nosqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, PageSize: 10, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().PageSize(10).Return(query).Times(1) }, iterMockFn: func(iter *gocql.MockIter) { iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(false).Times(1) iter.EXPECT().PageState().Return([]byte(nil)).Times(1) iter.EXPECT().Close().Return(nil).Times(1) }, wantRows: []*nosqlplugin.DomainAuditLogRow{}, wantToken: nil, wantQueries: []string{`SELECT event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment FROM domain_audit_log WHERE domain_id = test-domain-id AND operation_type = Failover AND created_time >= 2024-01-01T00:00:00Z AND created_time < 2024-12-31T23:59:59Z`}, }, { name: "success with pagination token", filter: &nosqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, PageSize: 10, NextPageToken: []byte("prev-page-token"), }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().PageSize(10).Return(query).Times(1) query.EXPECT().PageState([]byte("prev-page-token")).Return(query).Times(1) }, iterMockFn: func(iter *gocql.MockIter) { iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(false).Times(1) iter.EXPECT().PageState().Return([]byte(nil)).Times(1) iter.EXPECT().Close().Return(nil).Times(1) }, wantRows: []*nosqlplugin.DomainAuditLogRow{}, wantToken: nil, wantQueries: []string{`SELECT event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment FROM domain_audit_log WHERE domain_id = test-domain-id AND operation_type = Failover AND created_time >= 2024-01-01T00:00:00Z AND created_time < 2024-12-31T23:59:59Z`}, }, { name: "success with page size limiting - breaks after PageSize rows", filter: &nosqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, PageSize: 2, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().PageSize(2).Return(query).Times(1) }, iterMockFn: func(iter *gocql.MockIter) { // First row iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).DoAndReturn(func(args ...interface{}) bool { *args[0].(*string) = "event-1" *args[1].(*string) = domainID *args[7].(*time.Time) = createdTime1 *args[8].(*time.Time) = createdTime1 return true }).Times(1) // Second row iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).DoAndReturn(func(args ...interface{}) bool { *args[0].(*string) = "event-2" *args[1].(*string) = domainID *args[7].(*time.Time) = createdTime2 *args[8].(*time.Time) = createdTime2 return true }).Times(1) // Should break after 2 rows, no third Scan call iter.EXPECT().PageState().Return([]byte("next-page")).Times(1) iter.EXPECT().Close().Return(nil).Times(1) }, wantRows: []*nosqlplugin.DomainAuditLogRow{ { EventID: "event-1", DomainID: domainID, CreatedTime: createdTime1, LastUpdatedTime: createdTime1, }, { EventID: "event-2", DomainID: domainID, CreatedTime: createdTime2, LastUpdatedTime: createdTime2, }, }, wantToken: []byte("next-page"), wantQueries: []string{`SELECT event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment FROM domain_audit_log WHERE domain_id = test-domain-id AND operation_type = Failover AND created_time >= 2024-01-01T00:00:00Z AND created_time < 2024-12-31T23:59:59Z`}, }, { name: "success with multiple results", filter: &nosqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) }, iterMockFn: func(iter *gocql.MockIter) { // First row iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).DoAndReturn(func(args ...interface{}) bool { *args[0].(*string) = "event-1" *args[1].(*string) = domainID *args[7].(*time.Time) = createdTime1 *args[8].(*time.Time) = createdTime1 return true }).Times(1) // Second row iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).DoAndReturn(func(args ...interface{}) bool { *args[0].(*string) = "event-2" *args[1].(*string) = domainID *args[7].(*time.Time) = createdTime2 *args[8].(*time.Time) = createdTime2 return true }).Times(1) // End iteration iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(false).Times(1) iter.EXPECT().PageState().Return([]byte(nil)).Times(1) iter.EXPECT().Close().Return(nil).Times(1) }, wantRows: []*nosqlplugin.DomainAuditLogRow{ { EventID: "event-1", DomainID: domainID, CreatedTime: createdTime1, LastUpdatedTime: createdTime1, }, { EventID: "event-2", DomainID: domainID, CreatedTime: createdTime2, LastUpdatedTime: createdTime2, }, }, wantToken: nil, wantQueries: []string{`SELECT event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment FROM domain_audit_log WHERE domain_id = test-domain-id AND operation_type = Failover AND created_time >= 2024-01-01T00:00:00Z AND created_time < 2024-12-31T23:59:59Z`}, }, { name: "error when iterator is nil", filter: &nosqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Iter().Return(nil).Times(1) }, iterMockFn: func(iter *gocql.MockIter) { // No calls expected }, wantErr: true, }, { name: "error when iterator close fails", filter: &nosqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) }, iterMockFn: func(iter *gocql.MockIter) { iter.EXPECT().Scan( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(false).Times(1) iter.EXPECT().PageState().Return([]byte(nil)).Times(1) iter.EXPECT().Close().Return(errors.New("close failed")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) iter := gocql.NewMockIter(ctrl) tc.queryMockFn(query) // Set up Iter() to return our mock iterator (unless the test expects nil) if tc.name != "error when iterator is nil" { query.EXPECT().Iter().Return(iter).Times(1) } tc.iterMockFn(iter) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) rows, token, err := db.SelectDomainAuditLogs(context.Background(), tc.filter) if tc.wantErr { assert.Error(t, err) return } assert.NoError(t, err) assert.Equal(t, len(tc.wantRows), len(rows), "number of rows should match") for i, wantRow := range tc.wantRows { if i < len(rows) { assert.Equal(t, wantRow.EventID, rows[i].EventID) assert.Equal(t, wantRow.DomainID, rows[i].DomainID) assert.Equal(t, wantRow.StateBefore, rows[i].StateBefore) assert.Equal(t, wantRow.StateBeforeEncoding, rows[i].StateBeforeEncoding) assert.Equal(t, wantRow.StateAfter, rows[i].StateAfter) assert.Equal(t, wantRow.StateAfterEncoding, rows[i].StateAfterEncoding) assert.Equal(t, wantRow.OperationType, rows[i].OperationType) assert.Equal(t, wantRow.CreatedTime.Unix(), rows[i].CreatedTime.Unix()) assert.Equal(t, wantRow.LastUpdatedTime.Unix(), rows[i].LastUpdatedTime.Unix()) assert.Equal(t, wantRow.Identity, rows[i].Identity) assert.Equal(t, wantRow.IdentityType, rows[i].IdentityType) assert.Equal(t, wantRow.Comment, rows[i].Comment) } } assert.Equal(t, tc.wantToken, token) assert.Equal(t, tc.wantQueries, session.queries) }) } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/domain_cql.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra const ( templateDomainInfoType = `{` + `id: ?, ` + `name: ?, ` + `status: ?, ` + `description: ?, ` + `owner_email: ?, ` + `data: ? ` + `}` templateDomainConfigType = `{` + `retention: ?, ` + `emit_metric: ?, ` + `archival_bucket: ?, ` + `archival_status: ?,` + `history_archival_status: ?, ` + `history_archival_uri: ?, ` + `visibility_archival_status: ?, ` + `visibility_archival_uri: ?, ` + `bad_binaries: ?,` + `bad_binaries_encoding: ?,` + `isolation_groups: ?,` + `isolation_groups_encoding: ?,` + `async_workflow_config: ?,` + `async_workflow_config_encoding: ?` + `}` templateDomainReplicationConfigType = `{` + `active_cluster_name: ?, ` + `clusters: ?, ` + `active_clusters_config: ?, ` + `active_clusters_config_encoding: ?` + `}` templateCreateDomainQuery = `INSERT INTO domains (` + `id, domain, created_time) ` + `VALUES(?, {name: ?}, ?) IF NOT EXISTS` templateGetDomainQuery = `SELECT domain.name ` + `FROM domains ` + `WHERE id = ?` templateDeleteDomainQuery = `DELETE FROM domains ` + `WHERE id = ?` templateCreateDomainByNameQueryWithinBatchV2 = `INSERT INTO domains_by_name_v2 (` + `domains_partition, name, domain, config, replication_config, is_global_domain, config_version, failover_version, failover_notification_version, previous_failover_version, failover_end_time, last_updated_time, notification_version, created_time) ` + `VALUES(?, ?, ` + templateDomainInfoType + `, ` + templateDomainConfigType + `, ` + templateDomainReplicationConfigType + `, ?, ?, ?, ?, ?, ?, ?, ?, ?) IF NOT EXISTS` templateGetDomainByNameQueryV2 = `SELECT domain.id, domain.name, domain.status, domain.description, ` + `domain.owner_email, domain.data, config.retention, config.emit_metric, ` + `config.archival_bucket, config.archival_status, ` + `config.history_archival_status, config.history_archival_uri, ` + `config.visibility_archival_status, config.visibility_archival_uri, ` + `config.bad_binaries, config.bad_binaries_encoding, ` + `replication_config.active_cluster_name, replication_config.clusters, ` + `replication_config.active_clusters_config, replication_config.active_clusters_config_encoding, ` + `config.isolation_groups,` + `config.isolation_groups_encoding,` + `config.async_workflow_config,` + `config.async_workflow_config_encoding,` + `is_global_domain, ` + `config_version, ` + `failover_version, ` + `failover_notification_version, ` + `previous_failover_version, ` + `failover_end_time, ` + `last_updated_time, ` + `notification_version ` + `FROM domains_by_name_v2 ` + `WHERE domains_partition = ? ` + `and name = ?` templateUpdateDomainByNameQueryWithinBatchV2 = `UPDATE domains_by_name_v2 ` + `SET domain = ` + templateDomainInfoType + `, ` + `config = ` + templateDomainConfigType + `, ` + `replication_config = ` + templateDomainReplicationConfigType + `, ` + `config_version = ? ,` + `failover_version = ? ,` + `failover_notification_version = ? , ` + `previous_failover_version = ? , ` + `failover_end_time = ?,` + `last_updated_time = ?,` + `notification_version = ? ` + `WHERE domains_partition = ? ` + `and name = ?` templateGetMetadataQueryV2 = `SELECT notification_version ` + `FROM domains_by_name_v2 ` + `WHERE domains_partition = ? ` + `and name = ? ` templateUpdateMetadataQueryWithinBatchV2 = `UPDATE domains_by_name_v2 ` + `SET notification_version = ? ` + `WHERE domains_partition = ? ` + `and name = ? ` + `IF notification_version = ? ` templateDeleteDomainByNameQueryV2 = `DELETE FROM domains_by_name_v2 ` + `WHERE domains_partition = ? ` + `and name = ?` templateListDomainQueryV2 = `SELECT name, domain.id, domain.name, domain.status, domain.description, ` + `domain.owner_email, domain.data, config.retention, config.emit_metric, ` + `config.archival_bucket, config.archival_status, ` + `config.history_archival_status, config.history_archival_uri, ` + `config.visibility_archival_status, config.visibility_archival_uri, ` + `config.bad_binaries, config.bad_binaries_encoding, ` + `config.isolation_groups, config.isolation_groups_encoding, ` + `config.async_workflow_config, config.async_workflow_config_encoding, ` + `replication_config.active_cluster_name, replication_config.clusters, ` + `replication_config.active_clusters_config, replication_config.active_clusters_config_encoding, ` + `is_global_domain, ` + `config_version, ` + `failover_version, ` + `failover_notification_version, ` + `previous_failover_version, ` + `failover_end_time, ` + `last_updated_time, ` + `notification_version ` + `FROM domains_by_name_v2 ` + `WHERE domains_partition = ? ` ) ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/domain_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "errors" "testing" "time" "github.com/google/go-cmp/cmp" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/testdata" "github.com/uber/cadence/common/types" ) func TestInsertDomain(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-03T18:00:00Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string row *nosqlplugin.DomainRow queryMockFn func(query *gocql.MockQuery) clientMockFn func(client *gocql.MockClient) mapExecuteBatchCASApplied bool mapExecuteBatchCASPrev map[string]any mapExecuteBatchCASErr error wantSessionQueries []string wantBatchQueries []string wantErr bool }{ { name: "insertion MapScanCAS failed", row: testdata.NewDomainRow(ts), queryMockFn: func(query *gocql.MockQuery) { // mock calls for insert query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return false, errors.New("some random error") }).Times(1) }, wantErr: true, }, { name: "insertion MapScanCAS could not apply", row: testdata.NewDomainRow(ts), queryMockFn: func(query *gocql.MockQuery) { // mock calls for insert query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return false, nil }).Times(1) }, wantErr: true, }, { name: "insertion success - select metadata failed", row: testdata.NewDomainRow(ts), queryMockFn: func(query *gocql.MockQuery) { // mock calls for insert query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return true, nil }).Times(1) // mock calls for SelectDomainMetadata query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return errors.New("some random error") }).Times(1) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsNotFoundError(gomock.Any()).Return(false).Times(1) }, wantErr: true, }, { name: "insertion success - select metadata success - insertion to domains_by_name_v2 failed", row: testdata.NewDomainRow(ts), mapExecuteBatchCASErr: errors.New("insert to domains_by_name_v2 failed"), queryMockFn: func(query *gocql.MockQuery) { // mock calls for insert query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return true, nil }).Times(1) // mock calls for SelectDomainMetadata query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return nil }).Times(1) }, wantErr: true, }, { name: "insertion success - select metadata success - insertion to domains_by_name_v2 not applied - orphan domain deletion failed", row: testdata.NewDomainRow(ts), mapExecuteBatchCASApplied: false, queryMockFn: func(query *gocql.MockQuery) { // mock calls for insert query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return true, nil }).Times(1) // mock calls for SelectDomainMetadata query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return nil }).Times(1) // mock calls for deleting orphan domain query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("orphan domain deletion failure")).Times(1) }, wantErr: true, }, { name: "insertion success - select metadata success - insertion to domains_by_name_v2 not applied - domain already exists", row: testdata.NewDomainRow(ts), mapExecuteBatchCASApplied: false, mapExecuteBatchCASPrev: map[string]any{ "name": testdata.NewDomainRow(ts).Info.Name, // this will causedomain already exist error }, queryMockFn: func(query *gocql.MockQuery) { // mock calls for insert query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return true, nil }).Times(1) // mock calls for SelectDomainMetadata query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return nil }).Times(1) // mock calls for deleting orphan domain query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: true, }, { name: "all success", row: testdata.NewDomainRow(ts), mapExecuteBatchCASApplied: true, queryMockFn: func(query *gocql.MockQuery) { // mock calls for insert query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return true, nil }).Times(1) // mock calls for SelectDomainMetadata query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { notificationVersion := args[0].(*int64) *notificationVersion = 7 return nil }).Times(1) }, wantSessionQueries: []string{ `INSERT INTO domains (id, domain, created_time) VALUES(test-domain-id, {name: test-domain-name}, 2025-01-06T15:00:00Z) IF NOT EXISTS`, `SELECT notification_version FROM domains_by_name_v2 WHERE domains_partition = 0 and name = cadence-domain-metadata `, }, wantBatchQueries: []string{ `INSERT INTO domains_by_name_v2 (` + `domains_partition, ` + `name, ` + `domain, ` + `config, ` + `replication_config, ` + `is_global_domain, ` + `config_version, ` + `failover_version, ` + `failover_notification_version, ` + `previous_failover_version, ` + `failover_end_time, ` + `last_updated_time, ` + `notification_version, created_time) ` + `VALUES(` + `0, ` + `test-domain-name, ` + `{id: test-domain-id, name: test-domain-name, status: 0, description: test-domain-description, owner_email: test-domain-owner-email, data: map[k1:v1] }, ` + `{retention: 7, emit_metric: true, archival_bucket: test-archival-bucket, archival_status: ENABLED,history_archival_status: ENABLED, history_archival_uri: test-history-archival-uri, visibility_archival_status: ENABLED, visibility_archival_uri: test-visibility-archival-uri, bad_binaries: [98 97 100 45 98 105 110 97 114 105 101 115],bad_binaries_encoding: thriftrw,isolation_groups: [105 115 111 108 97 116 105 111 110 45 103 114 111 117 112],isolation_groups_encoding: thriftrw,async_workflow_config: [97 115 121 110 99 45 119 111 114 107 102 108 111 119 115 45 99 111 110 102 105 103],async_workflow_config_encoding: thriftrw}, ` + `{active_cluster_name: test-active-cluster-name, clusters: [map[cluster_name:test-cluster-name]], active_clusters_config: [97 99 116 105 118 101 45 99 108 117 115 116 101 114 115 45 99 111 110 102 105 103], active_clusters_config_encoding: thriftrw}, ` + `true, ` + `3, ` + `4, ` + `0, ` + `-1, ` + `1712167200000000000, ` + `1712167200000000000, ` + `7, 2025-01-06T15:00:00Z) ` + `IF NOT EXISTS`, `UPDATE domains_by_name_v2 SET notification_version = 8 WHERE domains_partition = 0 and name = cadence-domain-metadata IF notification_version = 7 `, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, mapExecuteBatchCASApplied: tc.mapExecuteBatchCASApplied, mapExecuteBatchCASPrev: tc.mapExecuteBatchCASPrev, mapExecuteBatchCASErr: tc.mapExecuteBatchCASErr, iter: &fakeIter{}, } client := gocql.NewMockClient(ctrl) if tc.clientMockFn != nil { tc.clientMockFn(client) } cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.InsertDomain(context.Background(), tc.row) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantSessionQueries, session.queries); diff != "" { t.Fatalf("Session queries mismatch (-want +got):\n%s", diff) } if len(session.batches) != 1 { t.Fatalf("Expected 1 batch, got %v", len(session.batches)) } if diff := cmp.Diff(tc.wantBatchQueries, session.batches[0].queries); diff != "" { t.Fatalf("Batch queries mismatch (-want +got):\n%s", diff) } if !session.iter.closed { t.Error("Expected iter to be closed") } }) } } func TestUpdateDomain(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-04T18:00:00Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string row *nosqlplugin.DomainRow mapExecuteBatchCASApplied bool mapExecuteBatchCASPrev map[string]any mapExecuteBatchCASErr error wantBatchQueries []string wantErr bool }{ { name: "mapExecuteBatchCAS could not apply", row: func() *nosqlplugin.DomainRow { r := testdata.NewDomainRow(ts) r.FailoverEndTime = nil return r }(), mapExecuteBatchCASApplied: false, wantErr: true, }, { name: "mapExecuteBatchCAS failed", row: func() *nosqlplugin.DomainRow { r := testdata.NewDomainRow(ts) r.FailoverEndTime = nil return r }(), mapExecuteBatchCASErr: errors.New("some random error"), wantErr: true, }, { name: "empty failover end time", row: func() *nosqlplugin.DomainRow { r := testdata.NewDomainRow(ts) r.FailoverEndTime = nil return r }(), mapExecuteBatchCASApplied: true, wantBatchQueries: []string{ `UPDATE domains_by_name_v2 SET ` + `domain = {id: test-domain-id, name: test-domain-name, status: 0, description: test-domain-description, owner_email: test-domain-owner-email, data: map[k1:v1] }, ` + `config = {retention: 7, emit_metric: true, archival_bucket: test-archival-bucket, archival_status: ENABLED,history_archival_status: ENABLED, history_archival_uri: test-history-archival-uri, visibility_archival_status: ENABLED, visibility_archival_uri: test-visibility-archival-uri, bad_binaries: [98 97 100 45 98 105 110 97 114 105 101 115],bad_binaries_encoding: thriftrw,isolation_groups: [105 115 111 108 97 116 105 111 110 45 103 114 111 117 112],isolation_groups_encoding: thriftrw,async_workflow_config: [97 115 121 110 99 45 119 111 114 107 102 108 111 119 115 45 99 111 110 102 105 103],async_workflow_config_encoding: thriftrw}, ` + `replication_config = {active_cluster_name: test-active-cluster-name, clusters: [map[cluster_name:test-cluster-name]], active_clusters_config: [97 99 116 105 118 101 45 99 108 117 115 116 101 114 115 45 99 111 110 102 105 103], active_clusters_config_encoding: thriftrw}, ` + `config_version = 3 ,` + `failover_version = 4 ,` + `failover_notification_version = 0 , ` + `previous_failover_version = 0 , ` + `failover_end_time = 0,` + `last_updated_time = 1712253600000000000,` + `notification_version = 5 ` + `WHERE domains_partition = 0 and name = test-domain-name`, `UPDATE domains_by_name_v2 SET notification_version = 6 WHERE ` + `domains_partition = 0 and ` + `name = cadence-domain-metadata ` + `IF notification_version = 5 `, }, }, { name: "success", row: testdata.NewDomainRow(ts), mapExecuteBatchCASApplied: true, wantBatchQueries: []string{ `UPDATE domains_by_name_v2 SET ` + `domain = {id: test-domain-id, name: test-domain-name, status: 0, description: test-domain-description, owner_email: test-domain-owner-email, data: map[k1:v1] }, ` + `config = {retention: 7, emit_metric: true, archival_bucket: test-archival-bucket, archival_status: ENABLED,history_archival_status: ENABLED, history_archival_uri: test-history-archival-uri, visibility_archival_status: ENABLED, visibility_archival_uri: test-visibility-archival-uri, bad_binaries: [98 97 100 45 98 105 110 97 114 105 101 115],bad_binaries_encoding: thriftrw,isolation_groups: [105 115 111 108 97 116 105 111 110 45 103 114 111 117 112],isolation_groups_encoding: thriftrw,async_workflow_config: [97 115 121 110 99 45 119 111 114 107 102 108 111 119 115 45 99 111 110 102 105 103],async_workflow_config_encoding: thriftrw}, ` + `replication_config = {active_cluster_name: test-active-cluster-name, clusters: [map[cluster_name:test-cluster-name]], active_clusters_config: [97 99 116 105 118 101 45 99 108 117 115 116 101 114 115 45 99 111 110 102 105 103], active_clusters_config_encoding: thriftrw}, ` + `config_version = 3 ,` + `failover_version = 4 ,` + `failover_notification_version = 0 , ` + `previous_failover_version = 0 , ` + `failover_end_time = 1712253600000000000,` + `last_updated_time = 1712253600000000000,` + `notification_version = 5 ` + `WHERE domains_partition = 0 and name = test-domain-name`, `UPDATE domains_by_name_v2 SET notification_version = 6 WHERE ` + `domains_partition = 0 and ` + `name = cadence-domain-metadata ` + `IF notification_version = 5 `, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) session := &fakeSession{ query: query, mapExecuteBatchCASApplied: tc.mapExecuteBatchCASApplied, mapExecuteBatchCASPrev: tc.mapExecuteBatchCASPrev, mapExecuteBatchCASErr: tc.mapExecuteBatchCASErr, iter: &fakeIter{}, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.UpdateDomain(context.Background(), tc.row) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if len(session.batches) != 1 { t.Fatalf("Expected 1 batch, got %v", len(session.batches)) } if diff := cmp.Diff(tc.wantBatchQueries, session.batches[0].queries); diff != "" { t.Fatalf("Batch queries mismatch (-want +got):\n%s", diff) } if !session.iter.closed { t.Error("Expected iter to be closed") } }) } } func TestSelectDomain(t *testing.T) { tests := []struct { name string domainID *string domainName *string queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "both domainName and domainID not provided", domainName: nil, domainID: nil, wantErr: true, }, { name: "both domainName and domainID provided", domainID: common.StringPtr("domain_id_1"), domainName: common.StringPtr("domain_name_1"), wantErr: true, }, { name: "domainName not provided - success", domainID: common.StringPtr("domain_id_1"), domainName: nil, queryMockFn: func(query *gocql.MockQuery) { // mock calls to select domainName query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { name := args[0].(**string) domainName := "domain_name_1" *name = &domainName return nil }).Times(1) // mock calls to select domain query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).Return(nil).Times(1) }, wantQueries: []string{ `SELECT domain.name FROM domains WHERE id = domain_id_1`, `SELECT domain.id, domain.name, domain.status, domain.description, domain.owner_email, domain.data, config.retention, config.emit_metric, config.archival_bucket, config.archival_status, config.history_archival_status, config.history_archival_uri, config.visibility_archival_status, config.visibility_archival_uri, config.bad_binaries, config.bad_binaries_encoding, replication_config.active_cluster_name, replication_config.clusters, replication_config.active_clusters_config, replication_config.active_clusters_config_encoding, config.isolation_groups,config.isolation_groups_encoding,config.async_workflow_config,config.async_workflow_config_encoding,is_global_domain, config_version, failover_version, failover_notification_version, previous_failover_version, failover_end_time, last_updated_time, notification_version FROM domains_by_name_v2 WHERE domains_partition = 0 and name = domain_name_1`, }, }, { name: "domainName not provided - scan failure", domainID: common.StringPtr("domain_id_1"), domainName: nil, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return errors.New("some random error") }).Times(1) }, wantErr: true, }, { name: "domainID not provided - scan failure", domainID: nil, domainName: common.StringPtr("domain_name_1"), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return errors.New("some random error") }).Times(1) }, wantErr: true, }, { name: "domainID not provided - success", domainID: nil, domainName: common.StringPtr("domain_name_1"), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).Return(nil).Times(1) }, wantQueries: []string{ `SELECT domain.id, domain.name, domain.status, domain.description, domain.owner_email, domain.data, config.retention, config.emit_metric, config.archival_bucket, config.archival_status, config.history_archival_status, config.history_archival_uri, config.visibility_archival_status, config.visibility_archival_uri, config.bad_binaries, config.bad_binaries_encoding, replication_config.active_cluster_name, replication_config.clusters, replication_config.active_clusters_config, replication_config.active_clusters_config_encoding, config.isolation_groups,config.isolation_groups_encoding,config.async_workflow_config,config.async_workflow_config_encoding,is_global_domain, config_version, failover_version, failover_notification_version, previous_failover_version, failover_end_time, last_updated_time, notification_version FROM domains_by_name_v2 WHERE domains_partition = 0 and name = domain_name_1`, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) if tc.queryMockFn != nil { tc.queryMockFn(query) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotRow, err := db.SelectDomain(context.Background(), tc.domainID, tc.domainName) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if gotRow == nil { t.Error("Expected domain row to be returned") } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestSelectAllDomains(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-03T18:00:00Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string pageSize int pagetoken []byte iter *fakeIter wantQueries []string wantRows []*nosqlplugin.DomainRow wantErr bool }{ { name: "nil iter", wantErr: true, }, { name: "iter close failed", iter: &fakeIter{closeErr: errors.New("some random error")}, wantErr: true, }, { name: "success", iter: &fakeIter{ scanInputs: [][]interface{}{ { "domain_name_1", "domain_id_1", "domain_name_1", persistence.DomainStatusRegistered, "domain_description_1", "domain_owner_email_1", map[string]string{"k1": "v1"}, int32(7), true, "test-archival-bucket", types.ArchivalStatusEnabled, types.ArchivalStatusEnabled, "test-history-archival-uri", types.ArchivalStatusEnabled, "test-visibility-archival-uri", []byte("bad-binaries"), "thriftrw", []byte("isolation-groups"), "thriftrw", []byte("async-workflow-config"), "thriftrw", "test-active-cluster-name", []map[string]interface{}{}, []byte("active-clusters-config"), "thriftrw", true, int64(3), int64(4), int64(0), int64(-1), int64(1712167200000000000), int64(1712167200000000000), int64(7), }, }, }, wantRows: []*nosqlplugin.DomainRow{ { Info: &persistence.DomainInfo{ ID: "domain_id_1", Name: "domain_name_1", Description: "domain_description_1", OwnerEmail: "domain_owner_email_1", Data: map[string]string{"k1": "v1"}, }, Config: &persistence.InternalDomainConfig{ Retention: 7 * 24 * time.Hour, EmitMetric: true, ArchivalBucket: "test-archival-bucket", ArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "test-history-archival-uri", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "test-visibility-archival-uri", BadBinaries: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("bad-binaries")}, IsolationGroups: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("isolation-groups")}, AsyncWorkflowsConfig: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("async-workflow-config")}, }, ReplicationConfig: &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "test-active-cluster-name", Clusters: []*persistence.ClusterReplicationConfig{}, ActiveClustersConfig: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("active-clusters-config")}, }, ConfigVersion: 3, FailoverVersion: 4, PreviousFailoverVersion: -1, FailoverEndTime: &ts, NotificationVersion: 7, LastUpdatedTime: ts, IsGlobalDomain: true, }, }, wantQueries: []string{ `SELECT name, domain.id, domain.name, domain.status, domain.description, domain.owner_email, domain.data, config.retention, config.emit_metric, config.archival_bucket, config.archival_status, config.history_archival_status, config.history_archival_uri, config.visibility_archival_status, config.visibility_archival_uri, config.bad_binaries, config.bad_binaries_encoding, config.isolation_groups, config.isolation_groups_encoding, config.async_workflow_config, config.async_workflow_config_encoding, replication_config.active_cluster_name, replication_config.clusters, replication_config.active_clusters_config, replication_config.active_clusters_config_encoding, is_global_domain, config_version, failover_version, failover_notification_version, previous_failover_version, failover_end_time, last_updated_time, notification_version FROM domains_by_name_v2 WHERE domains_partition = 0 `, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) query.EXPECT().PageSize(gomock.Any()).Return(query).Times(1) query.EXPECT().PageState(gomock.Any()).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) if tc.iter != nil { query.EXPECT().Iter().Return(tc.iter).Times(1) } else { query.EXPECT().Iter().Return(nil).Times(1) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) db := NewCassandraDBFromSession(cfg, session, logger, nil, DbWithClient(client)) gotRows, _, err := db.SelectAllDomains(context.Background(), tc.pageSize, tc.pagetoken) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.wantRows, gotRows); diff != "" { t.Fatalf("Task rows mismatch (-want +got):\n%s", diff) } if !tc.iter.closed { t.Fatal("iterator not closed") } }) } } func TestSelectDomainMetadata(t *testing.T) { tests := []struct { name string queryMockFn func(query *gocql.MockQuery) clientMockFn func(client *gocql.MockClient) wantNtfVer int64 wantQueries []string wantErr bool }{ { name: "success", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { notificationVersion := args[0].(*int64) *notificationVersion = 3 return nil }).Times(1) }, wantNtfVer: 3, wantQueries: []string{ `SELECT notification_version FROM domains_by_name_v2 WHERE domains_partition = 0 and name = cadence-domain-metadata `, }, }, { name: "scan failure - isnotfound", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return errors.New("some error that will be considered as not found by client mock") }).Times(1) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, wantNtfVer: 0, wantQueries: []string{ `SELECT notification_version FROM domains_by_name_v2 WHERE domains_partition = 0 and name = cadence-domain-metadata `, }, }, { name: "scan failure", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return errors.New("some random error") }).Times(1) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsNotFoundError(gomock.Any()).Return(false).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) if tc.clientMockFn != nil { tc.clientMockFn(client) } cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotNtfVer, err := db.SelectDomainMetadata(context.Background()) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if gotNtfVer != tc.wantNtfVer { t.Errorf("Got notification version = %v, want %v", gotNtfVer, tc.wantNtfVer) } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestDeleteDomain(t *testing.T) { tests := []struct { name string domainID *string domainName *string queryMockFn func(query *gocql.MockQuery) clientMockFn func(client *gocql.MockClient) wantQueries []string wantErr bool }{ { name: "both domainName and domainID not provided", domainName: nil, domainID: nil, wantErr: true, }, { name: "domainName not provided", domainID: common.StringPtr("domain_id_1"), domainName: nil, queryMockFn: func(query *gocql.MockQuery) { // mock calls for initial select query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { name := args[0].(*string) *name = "domain_name_1" return nil }).Times(1) // mock calls for delete from domains_by_name_v2 query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) // mock calls for delete from domains query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantQueries: []string{ `SELECT domain.name FROM domains WHERE id = domain_id_1`, `DELETE FROM domains_by_name_v2 WHERE domains_partition = 0 and name = domain_name_1`, `DELETE FROM domains WHERE id = domain_id_1`, }, }, { name: "domainName not provided - scan failure - isnotfound", domainID: common.StringPtr("domain_id_1"), domainName: nil, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return errors.New("some error that will be considered as not found by client mock") }).Times(1) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, wantQueries: []string{ `SELECT domain.name FROM domains WHERE id = domain_id_1`, }, }, { name: "domainName not provided - scan failure", domainID: common.StringPtr("domain_id_1"), domainName: nil, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return errors.New("some random error") }).Times(1) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsNotFoundError(gomock.Any()).Return(false).Times(1) }, wantErr: true, }, { name: "domainID not provided", domainID: nil, domainName: common.StringPtr("domain_name_1"), queryMockFn: func(query *gocql.MockQuery) { // mock calls for initial select query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) // Ideally we should be using DoAndReturn here to set the domainID, // but it panics because gomock doesn't handle n-arity func calls with nil params such as query.Scan(&id, nil, nil) query.EXPECT().Scan(gomock.Any()).Return(nil).Times(1) // mock calls for delete from domains_by_name_v2 query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) // mock calls for delete from domains query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantQueries: []string{ `SELECT domain.id, domain.name, domain.status, domain.description, domain.owner_email, domain.data, config.retention, config.emit_metric, config.archival_bucket, config.archival_status, config.history_archival_status, config.history_archival_uri, config.visibility_archival_status, config.visibility_archival_uri, config.bad_binaries, config.bad_binaries_encoding, replication_config.active_cluster_name, replication_config.clusters, replication_config.active_clusters_config, replication_config.active_clusters_config_encoding, config.isolation_groups,config.isolation_groups_encoding,config.async_workflow_config,config.async_workflow_config_encoding,is_global_domain, config_version, failover_version, failover_notification_version, previous_failover_version, failover_end_time, last_updated_time, notification_version FROM domains_by_name_v2 WHERE domains_partition = 0 and name = domain_name_1`, `DELETE FROM domains_by_name_v2 WHERE domains_partition = 0 and name = domain_name_1`, `DELETE FROM domains WHERE id = `, // domainID is nil, so we expect an empty string here. See the comment above inside mockQueryFn. }, }, { name: "domainID not provided - scan failure - isnotfound", domainID: nil, domainName: common.StringPtr("domain_name_1"), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).Return(errors.New("some error that will be considered as not found by client mock")).Times(1) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, wantQueries: []string{ `SELECT domain.id, domain.name, domain.status, domain.description, domain.owner_email, domain.data, config.retention, config.emit_metric, config.archival_bucket, config.archival_status, config.history_archival_status, config.history_archival_uri, config.visibility_archival_status, config.visibility_archival_uri, config.bad_binaries, config.bad_binaries_encoding, replication_config.active_cluster_name, replication_config.clusters, replication_config.active_clusters_config, replication_config.active_clusters_config_encoding, config.isolation_groups,config.isolation_groups_encoding,config.async_workflow_config,config.async_workflow_config_encoding,is_global_domain, config_version, failover_version, failover_notification_version, previous_failover_version, failover_end_time, last_updated_time, notification_version FROM domains_by_name_v2 WHERE domains_partition = 0 and name = domain_name_1`, }, }, { name: "domainID not provided - scan failure", domainID: nil, domainName: common.StringPtr("domain_name_1"), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).Return(errors.New("some random error")).Times(1) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsNotFoundError(gomock.Any()).Return(false).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) if tc.queryMockFn != nil { tc.queryMockFn(query) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) if tc.clientMockFn != nil { tc.clientMockFn(client) } cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{ EnableCassandraAllConsistencyLevelDelete: func(opts ...dynamicproperties.FilterOption) bool { return false }, } db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.DeleteDomain(context.Background(), tc.domainID, tc.domainName) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/batch.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gocql import ( "context" "fmt" "github.com/gocql/gocql" ) var _ Batch = (*batch)(nil) type ( batch struct { *gocql.Batch } ) // Definition of all BatchTypes const ( LoggedBatch BatchType = iota UnloggedBatch CounterBatch ) func newBatch( gocqlBatch *gocql.Batch, ) *batch { return &batch{ Batch: gocqlBatch, } } func (b *batch) WithContext(ctx context.Context) Batch { b2 := b.Batch.WithContext(ctx) if b2 == nil { return nil } return newBatch(b2) } func (b *batch) WithTimestamp(timestamp int64) Batch { b.Batch.WithTimestamp(timestamp) return b } func (b *batch) Consistency(c Consistency) Batch { b.Batch.SetConsistency(mustConvertConsistency(c)) return b } func mustConvertBatchType(batchType BatchType) gocql.BatchType { switch batchType { case LoggedBatch: return gocql.LoggedBatch case UnloggedBatch: return gocql.UnloggedBatch case CounterBatch: return gocql.CounterBatch default: panic(fmt.Sprintf("Unknown gocql BatchType: %v", batchType)) } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/batch_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package gocql import ( "context" "testing" "time" "github.com/gocql/gocql" "github.com/stretchr/testify/assert" ) func TestBatch(t *testing.T) { testBatch := gocql.Batch{} b := newBatch(&testBatch) assert.NotNil(t, b) } func Test_Batch_WithContext(t *testing.T) { testCtx := context.Background() testBatch := gocql.Batch{} b := newBatch(&testBatch) assert.NotNil(t, b.WithContext(testCtx)) } func Test_Batch_WithTimestamp(t *testing.T) { timeNow := time.Now().Unix() testBatch := gocql.Batch{} b := newBatch(&testBatch) withTimeStamp := b.WithTimestamp(timeNow) assert.NotNil(t, withTimeStamp) } func Test_Batch_Consistency(t *testing.T) { testBatch := gocql.Batch{} b := newBatch(&testBatch) consistency := b.Consistency(One) assert.NotNil(t, consistency) } func Test_MustConvertBatchType(t *testing.T) { batch := []BatchType{LoggedBatch, UnloggedBatch, CounterBatch} for _, b := range batch { mustConvertBatchType(b) } } func Test_MustConvertBatchType_Panic(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("The code did not panic") } }() mustConvertBatchType(4) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/client.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gocql import ( "crypto/tls" "strings" "github.com/gocql/gocql" "github.com/uber/cadence/environment" ) var ( registered Client ) // GetRegisteredClient gets a gocql client based registered object func GetRegisteredClient() Client { if registered == nil { panic("binary build error: gocql client is not registered yet!") } return registered } // RegisterClient registers a client into this package, can only be called once func RegisterClient(c Client) { if registered == nil { registered = c } else { panic("binary build error: gocql client is already register!") } } func newCassandraCluster(cfg ClusterConfig) *gocql.ClusterConfig { hosts := parseHosts(cfg.Hosts) cluster := gocql.NewCluster(hosts...) if cfg.ProtoVersion == 0 { cfg.ProtoVersion = environment.CassandraDefaultProtoVersionInteger } cluster.ProtoVersion = cfg.ProtoVersion if cfg.Port > 0 { cluster.Port = cfg.Port } if cfg.User != "" && cfg.Password != "" { cluster.Authenticator = gocql.PasswordAuthenticator{ Username: cfg.User, Password: cfg.Password, AllowedAuthenticators: cfg.AllowedAuthenticators, } } if cfg.Keyspace != "" { cluster.Keyspace = cfg.Keyspace } if cfg.Datacenter != "" { cluster.HostFilter = gocql.DataCentreHostFilter(cfg.Datacenter) } if cfg.Region != "" { cluster.HostFilter = regionHostFilter(cfg.Region) } if cfg.TLS != nil && cfg.TLS.Enabled { cluster.SslOpts = &gocql.SslOptions{ CertPath: cfg.TLS.CertFile, KeyPath: cfg.TLS.KeyFile, CaPath: cfg.TLS.CaFile, EnableHostVerification: cfg.TLS.EnableHostVerification, Config: &tls.Config{ ServerName: cfg.TLS.ServerName, }, } } if cfg.MaxConns > 0 { cluster.NumConns = cfg.MaxConns } if cfg.HostSelectionPolicy != nil { cluster.PoolConfig.HostSelectionPolicy = cfg.HostSelectionPolicy } else { // set default option if configuration was not provided cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(gocql.RoundRobinHostPolicy()) } return cluster } // regionHostFilter returns a gocql host filter for the given region name func regionHostFilter(region string) gocql.HostFilter { return gocql.HostFilterFunc(func(host *gocql.HostInfo) bool { applicationRegion := region if len(host.DataCenter()) < 3 { return false } return host.DataCenter()[:3] == applicationRegion }) } // parseHosts returns parses a list of hosts separated by comma func parseHosts(input string) []string { var hosts = make([]string, 0) for _, h := range strings.Split(input, ",") { if host := strings.TrimSpace(h); len(host) > 0 { hosts = append(hosts, host) } } return hosts } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/client_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package gocql import ( "testing" "github.com/gocql/gocql" "github.com/stretchr/testify/assert" gomock "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" ) func Test_GetRegisteredClient(t *testing.T) { assert.Panics(t, func() { GetRegisteredClient() }) } func Test_GetRegisteredClientNotNil(t *testing.T) { mockCtrl := gomock.NewController(t) registered = NewMockClient(mockCtrl) assert.Equal(t, registered, GetRegisteredClient()) } func Test_RegisterClient(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("The code did not panic") } }() RegisterClient(nil) } func Test_RegisterClientNotNil(t *testing.T) { mockCtrl := gomock.NewController(t) newClient := NewMockClient(mockCtrl) registered = nil RegisterClient(newClient) assert.Equal(t, newClient, registered) } func Test_newCassandraCluster(t *testing.T) { testFullConfig := ClusterConfig{ Hosts: "testHost1,testHost2,testHost3,testHost4", Port: 123, User: "testUser", Password: "testPassword", Keyspace: "testKeyspace", Datacenter: "testDatacenter", Region: "testRegion", TLS: &config.TLS{ Enabled: true, CertFile: "testCertFile", KeyFile: "testKeyFile", }, MaxConns: 10, } clusterConfig := newCassandraCluster(testFullConfig) assert.Equal(t, []string{"testHost1", "testHost2", "testHost3", "testHost4"}, clusterConfig.Hosts) assert.Equal(t, testFullConfig.Port, clusterConfig.Port) assert.Equal(t, testFullConfig.User, clusterConfig.Authenticator.(gocql.PasswordAuthenticator).Username) assert.Equal(t, testFullConfig.Password, clusterConfig.Authenticator.(gocql.PasswordAuthenticator).Password) assert.Equal(t, testFullConfig.Keyspace, clusterConfig.Keyspace) assert.Equal(t, testFullConfig.TLS.CertFile, clusterConfig.SslOpts.CertPath) assert.Equal(t, testFullConfig.TLS.KeyFile, clusterConfig.SslOpts.KeyPath) assert.Equal(t, testFullConfig.MaxConns, clusterConfig.NumConns) assert.False(t, clusterConfig.HostFilter.Accept(&gocql.HostInfo{})) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/consistency.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gocql import ( "fmt" "strings" "github.com/gocql/gocql" ) // Definition of all Consistency levels const ( Any Consistency = iota One Two Three Quorum All LocalQuorum EachQuorum LocalOne ) // Definition of all SerialConsistency levels const ( Serial SerialConsistency = iota LocalSerial ) func mustConvertConsistency(c Consistency) gocql.Consistency { switch c { case Any: return gocql.Any case One: return gocql.One case Two: return gocql.Two case Three: return gocql.Three case Quorum: return gocql.Quorum case All: return gocql.All case LocalQuorum: return gocql.LocalQuorum case EachQuorum: return gocql.EachQuorum case LocalOne: return gocql.LocalOne default: panic(fmt.Sprintf("Unknown gocql Consistency level: %v", c)) } } func mustConvertSerialConsistency(c SerialConsistency) gocql.SerialConsistency { switch c { case Serial: return gocql.Serial case LocalSerial: return gocql.LocalSerial default: panic(fmt.Sprintf("Unknown gocql SerialConsistency level: %v", c)) } } func (c Consistency) MarshalText() (text []byte, err error) { return []byte(c.String()), nil } func (c *Consistency) UnmarshalText(text []byte) error { switch string(text) { case "ANY": *c = Any case "ONE": *c = One case "TWO": *c = Two case "THREE": *c = Three case "QUORUM": *c = Quorum case "ALL": *c = All case "LOCAL_QUORUM": *c = LocalQuorum case "EACH_QUORUM": *c = EachQuorum case "LOCAL_ONE": *c = LocalOne default: return fmt.Errorf("invalid consistency %q", string(text)) } return nil } func (c Consistency) String() string { switch c { case Any: return "ANY" case One: return "ONE" case Two: return "TWO" case Three: return "THREE" case Quorum: return "QUORUM" case All: return "ALL" case LocalQuorum: return "LOCAL_QUORUM" case EachQuorum: return "EACH_QUORUM" case LocalOne: return "LOCAL_ONE" default: return fmt.Sprintf("invalid consistency: %d", uint16(c)) } } func ParseConsistency(s string) (Consistency, error) { var c Consistency if err := c.UnmarshalText([]byte(strings.ToUpper(s))); err != nil { return c, fmt.Errorf("parse consistency: %w", err) } return c, nil } func ParseSerialConsistency(s string) (SerialConsistency, error) { var sc SerialConsistency if err := sc.UnmarshalText([]byte(strings.ToUpper(s))); err != nil { return sc, fmt.Errorf("parse serial consistency: %w", err) } return sc, nil } func (s SerialConsistency) String() string { switch s { case Serial: return "SERIAL" case LocalSerial: return "LOCAL_SERIAL" default: return fmt.Sprintf("invalid serial consistency %d", uint16(s)) } } func (s SerialConsistency) MarshalText() (text []byte, err error) { return []byte(s.String()), nil } func (s *SerialConsistency) UnmarshalText(text []byte) error { switch string(text) { case "SERIAL": *s = Serial case "LOCAL_SERIAL": *s = LocalSerial default: return fmt.Errorf("invalid serial consistency %q", string(text)) } return nil } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/consistency_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package gocql import ( "testing" "github.com/gocql/gocql" "github.com/stretchr/testify/assert" ) func TestConsistency_MarshalText(t *testing.T) { tests := []struct { c Consistency wantText []byte testFn func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool }{ {c: Any, wantText: []byte("ANY"), testFn: assert.Equal}, {c: One, wantText: []byte("ONE"), testFn: assert.Equal}, {c: Two, wantText: []byte("TWO"), testFn: assert.Equal}, {c: Three, wantText: []byte("THREE"), testFn: assert.Equal}, {c: Quorum, wantText: []byte("QUORUM"), testFn: assert.Equal}, {c: All, wantText: []byte("ALL"), testFn: assert.Equal}, {c: LocalQuorum, wantText: []byte("LOCAL_QUORUM"), testFn: assert.Equal}, {c: EachQuorum, wantText: []byte("EACH_QUORUM"), testFn: assert.Equal}, {c: LocalOne, wantText: []byte("LOCAL_ONE"), testFn: assert.Equal}, {c: LocalOne, wantText: []byte("WRONG_VALUE"), testFn: assert.NotEqualValues}, } for _, tt := range tests { t.Run(tt.c.String(), func(t *testing.T) { gotText, err := tt.c.MarshalText() assert.NoError(t, err) tt.testFn(t, tt.wantText, gotText) }) } } func TestConsistency_String(t *testing.T) { c := Consistency(9) assert.Equal(t, c.String(), "invalid consistency: 9") } func TestConsistency_UnmarshalText(t *testing.T) { tests := []struct { destConsistency Consistency inputText []byte testFn func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool wantErr bool }{ {destConsistency: Any, inputText: []byte("ANY"), testFn: assert.Equal}, {destConsistency: One, inputText: []byte("ONE"), testFn: assert.Equal}, {destConsistency: Two, inputText: []byte("TWO"), testFn: assert.Equal}, {destConsistency: Three, inputText: []byte("THREE"), testFn: assert.Equal}, {destConsistency: Quorum, inputText: []byte("QUORUM"), testFn: assert.Equal}, {destConsistency: All, inputText: []byte("ALL"), testFn: assert.Equal}, {destConsistency: LocalQuorum, inputText: []byte("LOCAL_QUORUM"), testFn: assert.Equal}, {destConsistency: EachQuorum, inputText: []byte("EACH_QUORUM"), testFn: assert.Equal}, {destConsistency: LocalOne, inputText: []byte("LOCAL_ONE"), testFn: assert.Equal}, {destConsistency: LocalOne, inputText: []byte("WRONG_VALUE"), testFn: assert.NotEqualValues, wantErr: true}, } for _, tt := range tests { t.Run(tt.destConsistency.String(), func(t *testing.T) { var c Consistency err := c.UnmarshalText(tt.inputText) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } tt.testFn(t, tt.destConsistency, c) }) } } func TestParseConsistency(t *testing.T) { tests := []struct { destConsistency Consistency inputText string testFn func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool wantErr bool }{ {destConsistency: Any, inputText: "any", testFn: assert.Equal}, {destConsistency: One, inputText: "ONE", testFn: assert.Equal}, {destConsistency: Two, inputText: "TWO", testFn: assert.Equal}, {destConsistency: Three, inputText: "THREE", testFn: assert.Equal}, {destConsistency: Quorum, inputText: "QUORUM", testFn: assert.Equal}, {destConsistency: All, inputText: "all", testFn: assert.Equal}, {destConsistency: LocalQuorum, inputText: "LOCAL_QUORUM", testFn: assert.Equal}, {destConsistency: EachQuorum, inputText: "EACH_QUORUM", testFn: assert.Equal}, {destConsistency: LocalOne, inputText: "LOCAL_ONE", testFn: assert.Equal}, {destConsistency: Any, inputText: "WRONG_VALUE_FAILBACK_TO_ANY", testFn: assert.Equal, wantErr: true}, } for _, tt := range tests { t.Run(string(tt.inputText), func(t *testing.T) { got, err := ParseConsistency(tt.inputText) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } tt.testFn(t, tt.destConsistency, got) }) } } func TestParseSerialConsistency(t *testing.T) { tests := []struct { destConsistency SerialConsistency inputText string testFn func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool wantErr bool }{ {destConsistency: Serial, inputText: "serial", testFn: assert.Equal}, {destConsistency: LocalSerial, inputText: "LOCAL_SERIAL", testFn: assert.Equal}, {destConsistency: Serial, inputText: "WRONG_VALUE_FAILBACK_TO_ANY", testFn: assert.Equal, wantErr: true}, } for _, tt := range tests { t.Run(string(tt.inputText), func(t *testing.T) { got, err := ParseSerialConsistency(tt.inputText) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } tt.testFn(t, tt.destConsistency, got) }) } } func TestSerialConsistency_MarshalText(t *testing.T) { tests := []struct { c SerialConsistency wantText []byte testFn func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool }{ {c: Serial, wantText: []byte("SERIAL"), testFn: assert.Equal}, {c: LocalSerial, wantText: []byte("LOCAL_SERIAL"), testFn: assert.Equal}, {c: LocalSerial, wantText: []byte("WRONG_VALUE"), testFn: assert.NotEqualValues}, } for _, tt := range tests { t.Run(tt.c.String(), func(t *testing.T) { gotText, err := tt.c.MarshalText() assert.NoError(t, err) tt.testFn(t, tt.wantText, gotText) }) } } func TestSerialConsistency_String(t *testing.T) { c := SerialConsistency(2) assert.Equal(t, c.String(), "invalid serial consistency 2") } func TestSerialConsistency_UnmarshalText(t *testing.T) { tests := []struct { destSerialConsistency SerialConsistency inputText []byte wantErr bool }{ {destSerialConsistency: Serial, inputText: []byte("SERIAL")}, {destSerialConsistency: LocalSerial, inputText: []byte("LOCAL_SERIAL")}, {destSerialConsistency: Serial, inputText: []byte("WRONG_VALUE_DEFAULTS_TO_SERIAL"), wantErr: true}, } for _, tt := range tests { t.Run(tt.destSerialConsistency.String(), func(t *testing.T) { var c SerialConsistency err := c.UnmarshalText(tt.inputText) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } assert.Equal(t, tt.destSerialConsistency, c) }) } } func Test_mustConvertConsistency(t *testing.T) { tests := []struct { input Consistency output gocql.Consistency }{ {Any, gocql.Any}, {One, gocql.One}, {Two, gocql.Two}, {Three, gocql.Three}, {Quorum, gocql.Quorum}, {All, gocql.All}, {LocalQuorum, gocql.LocalQuorum}, {EachQuorum, gocql.EachQuorum}, {LocalOne, gocql.LocalOne}, } for _, tt := range tests { assert.Equal(t, tt.output, mustConvertConsistency(tt.input)) } assert.Panics(t, func() { mustConvertConsistency(Consistency(9999)) }) } func Test_mustConvertSerialConsistency(t *testing.T) { tests := []struct { input SerialConsistency output gocql.SerialConsistency }{ {Serial, gocql.Serial}, {LocalSerial, gocql.LocalSerial}, } for _, tt := range tests { assert.Equal(t, tt.output, mustConvertSerialConsistency(tt.input)) } assert.Panics(t, func() { mustConvertSerialConsistency(SerialConsistency(9999)) }) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/interface.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql package gocql import ( "context" "time" "github.com/gocql/gocql" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // Note: this file defines the minimal interface that is needed by Cadence's cassandra // persistence implementation and should be implemented for all gocql libraries if // they need to be used. // Please add more methods to the interface if needed by the cassandra implementation. type ( // Client is an interface for all gocql libraries. Client interface { CreateSession(ClusterConfig) (Session, error) nosqlplugin.ClientErrorChecker IsCassandraConsistencyError(error) bool } // Session is the interface for interacting with the database. Session interface { Query(string, ...interface{}) Query NewBatch(BatchType) Batch ExecuteBatch(Batch) error MapExecuteBatchCAS(Batch, map[string]interface{}) (bool, Iter, error) Close() } // Query is the interface for query object. Query interface { Exec() error Scan(...interface{}) error ScanCAS(...interface{}) (bool, error) MapScan(map[string]interface{}) error MapScanCAS(map[string]interface{}) (bool, error) Iter() Iter PageSize(int) Query PageState([]byte) Query WithContext(context.Context) Query WithTimestamp(int64) Query Consistency(Consistency) Query Bind(...interface{}) Query } // Batch is the interface for batch operation. Batch interface { Query(string, ...interface{}) WithContext(context.Context) Batch WithTimestamp(int64) Batch Consistency(Consistency) Batch } // Iter is the interface for executing and iterating over all resulting rows. Iter interface { Scan(...interface{}) bool MapScan(map[string]interface{}) bool PageState() []byte Close() error } // UUID represents a universally unique identifier UUID interface { String() string } // BatchType is the type of the Batch operation BatchType byte // Consistency is the consistency level used by a Query Consistency uint16 // SerialConsistency is the serial consistency level used by a Query SerialConsistency uint16 // ClusterConfig is the config for cassandra connection ClusterConfig struct { Hosts string Port int User string Password string AllowedAuthenticators []string Keyspace string Region string Datacenter string MaxConns int TLS *config.TLS ProtoVersion int Consistency Consistency SerialConsistency SerialConsistency Timeout time.Duration ConnectTimeout time.Duration HostSelectionPolicy gocql.HostSelectionPolicy } ) ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package gocql -source interface.go -destination interface_mock.go -self_package github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql // // Package gocql is a generated GoMock package. package gocql import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // CreateSession mocks base method. func (m *MockClient) CreateSession(arg0 ClusterConfig) (Session, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateSession", arg0) ret0, _ := ret[0].(Session) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateSession indicates an expected call of CreateSession. func (mr *MockClientMockRecorder) CreateSession(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSession", reflect.TypeOf((*MockClient)(nil).CreateSession), arg0) } // IsCassandraConsistencyError mocks base method. func (m *MockClient) IsCassandraConsistencyError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsCassandraConsistencyError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsCassandraConsistencyError indicates an expected call of IsCassandraConsistencyError. func (mr *MockClientMockRecorder) IsCassandraConsistencyError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsCassandraConsistencyError", reflect.TypeOf((*MockClient)(nil).IsCassandraConsistencyError), arg0) } // IsDBUnavailableError mocks base method. func (m *MockClient) IsDBUnavailableError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsDBUnavailableError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsDBUnavailableError indicates an expected call of IsDBUnavailableError. func (mr *MockClientMockRecorder) IsDBUnavailableError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDBUnavailableError", reflect.TypeOf((*MockClient)(nil).IsDBUnavailableError), arg0) } // IsNotFoundError mocks base method. func (m *MockClient) IsNotFoundError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsNotFoundError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsNotFoundError indicates an expected call of IsNotFoundError. func (mr *MockClientMockRecorder) IsNotFoundError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNotFoundError", reflect.TypeOf((*MockClient)(nil).IsNotFoundError), arg0) } // IsThrottlingError mocks base method. func (m *MockClient) IsThrottlingError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsThrottlingError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsThrottlingError indicates an expected call of IsThrottlingError. func (mr *MockClientMockRecorder) IsThrottlingError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsThrottlingError", reflect.TypeOf((*MockClient)(nil).IsThrottlingError), arg0) } // IsTimeoutError mocks base method. func (m *MockClient) IsTimeoutError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsTimeoutError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsTimeoutError indicates an expected call of IsTimeoutError. func (mr *MockClientMockRecorder) IsTimeoutError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsTimeoutError", reflect.TypeOf((*MockClient)(nil).IsTimeoutError), arg0) } // MockSession is a mock of Session interface. type MockSession struct { ctrl *gomock.Controller recorder *MockSessionMockRecorder isgomock struct{} } // MockSessionMockRecorder is the mock recorder for MockSession. type MockSessionMockRecorder struct { mock *MockSession } // NewMockSession creates a new mock instance. func NewMockSession(ctrl *gomock.Controller) *MockSession { mock := &MockSession{ctrl: ctrl} mock.recorder = &MockSessionMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSession) EXPECT() *MockSessionMockRecorder { return m.recorder } // Close mocks base method. func (m *MockSession) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockSessionMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSession)(nil).Close)) } // ExecuteBatch mocks base method. func (m *MockSession) ExecuteBatch(arg0 Batch) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ExecuteBatch", arg0) ret0, _ := ret[0].(error) return ret0 } // ExecuteBatch indicates an expected call of ExecuteBatch. func (mr *MockSessionMockRecorder) ExecuteBatch(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteBatch", reflect.TypeOf((*MockSession)(nil).ExecuteBatch), arg0) } // MapExecuteBatchCAS mocks base method. func (m *MockSession) MapExecuteBatchCAS(arg0 Batch, arg1 map[string]any) (bool, Iter, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MapExecuteBatchCAS", arg0, arg1) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(Iter) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // MapExecuteBatchCAS indicates an expected call of MapExecuteBatchCAS. func (mr *MockSessionMockRecorder) MapExecuteBatchCAS(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MapExecuteBatchCAS", reflect.TypeOf((*MockSession)(nil).MapExecuteBatchCAS), arg0, arg1) } // NewBatch mocks base method. func (m *MockSession) NewBatch(arg0 BatchType) Batch { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewBatch", arg0) ret0, _ := ret[0].(Batch) return ret0 } // NewBatch indicates an expected call of NewBatch. func (mr *MockSessionMockRecorder) NewBatch(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewBatch", reflect.TypeOf((*MockSession)(nil).NewBatch), arg0) } // Query mocks base method. func (m *MockSession) Query(arg0 string, arg1 ...any) Query { m.ctrl.T.Helper() varargs := []any{arg0} for _, a := range arg1 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Query", varargs...) ret0, _ := ret[0].(Query) return ret0 } // Query indicates an expected call of Query. func (mr *MockSessionMockRecorder) Query(arg0 any, arg1 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0}, arg1...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockSession)(nil).Query), varargs...) } // MockQuery is a mock of Query interface. type MockQuery struct { ctrl *gomock.Controller recorder *MockQueryMockRecorder isgomock struct{} } // MockQueryMockRecorder is the mock recorder for MockQuery. type MockQueryMockRecorder struct { mock *MockQuery } // NewMockQuery creates a new mock instance. func NewMockQuery(ctrl *gomock.Controller) *MockQuery { mock := &MockQuery{ctrl: ctrl} mock.recorder = &MockQueryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQuery) EXPECT() *MockQueryMockRecorder { return m.recorder } // Bind mocks base method. func (m *MockQuery) Bind(arg0 ...any) Query { m.ctrl.T.Helper() varargs := []any{} for _, a := range arg0 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Bind", varargs...) ret0, _ := ret[0].(Query) return ret0 } // Bind indicates an expected call of Bind. func (mr *MockQueryMockRecorder) Bind(arg0 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bind", reflect.TypeOf((*MockQuery)(nil).Bind), arg0...) } // Consistency mocks base method. func (m *MockQuery) Consistency(arg0 Consistency) Query { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Consistency", arg0) ret0, _ := ret[0].(Query) return ret0 } // Consistency indicates an expected call of Consistency. func (mr *MockQueryMockRecorder) Consistency(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Consistency", reflect.TypeOf((*MockQuery)(nil).Consistency), arg0) } // Exec mocks base method. func (m *MockQuery) Exec() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Exec") ret0, _ := ret[0].(error) return ret0 } // Exec indicates an expected call of Exec. func (mr *MockQueryMockRecorder) Exec() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockQuery)(nil).Exec)) } // Iter mocks base method. func (m *MockQuery) Iter() Iter { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Iter") ret0, _ := ret[0].(Iter) return ret0 } // Iter indicates an expected call of Iter. func (mr *MockQueryMockRecorder) Iter() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Iter", reflect.TypeOf((*MockQuery)(nil).Iter)) } // MapScan mocks base method. func (m *MockQuery) MapScan(arg0 map[string]any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MapScan", arg0) ret0, _ := ret[0].(error) return ret0 } // MapScan indicates an expected call of MapScan. func (mr *MockQueryMockRecorder) MapScan(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MapScan", reflect.TypeOf((*MockQuery)(nil).MapScan), arg0) } // MapScanCAS mocks base method. func (m *MockQuery) MapScanCAS(arg0 map[string]any) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MapScanCAS", arg0) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // MapScanCAS indicates an expected call of MapScanCAS. func (mr *MockQueryMockRecorder) MapScanCAS(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MapScanCAS", reflect.TypeOf((*MockQuery)(nil).MapScanCAS), arg0) } // PageSize mocks base method. func (m *MockQuery) PageSize(arg0 int) Query { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PageSize", arg0) ret0, _ := ret[0].(Query) return ret0 } // PageSize indicates an expected call of PageSize. func (mr *MockQueryMockRecorder) PageSize(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PageSize", reflect.TypeOf((*MockQuery)(nil).PageSize), arg0) } // PageState mocks base method. func (m *MockQuery) PageState(arg0 []byte) Query { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PageState", arg0) ret0, _ := ret[0].(Query) return ret0 } // PageState indicates an expected call of PageState. func (mr *MockQueryMockRecorder) PageState(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PageState", reflect.TypeOf((*MockQuery)(nil).PageState), arg0) } // Scan mocks base method. func (m *MockQuery) Scan(arg0 ...any) error { m.ctrl.T.Helper() varargs := []any{} for _, a := range arg0 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Scan", varargs...) ret0, _ := ret[0].(error) return ret0 } // Scan indicates an expected call of Scan. func (mr *MockQueryMockRecorder) Scan(arg0 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scan", reflect.TypeOf((*MockQuery)(nil).Scan), arg0...) } // ScanCAS mocks base method. func (m *MockQuery) ScanCAS(arg0 ...any) (bool, error) { m.ctrl.T.Helper() varargs := []any{} for _, a := range arg0 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ScanCAS", varargs...) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // ScanCAS indicates an expected call of ScanCAS. func (mr *MockQueryMockRecorder) ScanCAS(arg0 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScanCAS", reflect.TypeOf((*MockQuery)(nil).ScanCAS), arg0...) } // WithContext mocks base method. func (m *MockQuery) WithContext(arg0 context.Context) Query { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WithContext", arg0) ret0, _ := ret[0].(Query) return ret0 } // WithContext indicates an expected call of WithContext. func (mr *MockQueryMockRecorder) WithContext(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithContext", reflect.TypeOf((*MockQuery)(nil).WithContext), arg0) } // WithTimestamp mocks base method. func (m *MockQuery) WithTimestamp(arg0 int64) Query { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WithTimestamp", arg0) ret0, _ := ret[0].(Query) return ret0 } // WithTimestamp indicates an expected call of WithTimestamp. func (mr *MockQueryMockRecorder) WithTimestamp(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithTimestamp", reflect.TypeOf((*MockQuery)(nil).WithTimestamp), arg0) } // MockBatch is a mock of Batch interface. type MockBatch struct { ctrl *gomock.Controller recorder *MockBatchMockRecorder isgomock struct{} } // MockBatchMockRecorder is the mock recorder for MockBatch. type MockBatchMockRecorder struct { mock *MockBatch } // NewMockBatch creates a new mock instance. func NewMockBatch(ctrl *gomock.Controller) *MockBatch { mock := &MockBatch{ctrl: ctrl} mock.recorder = &MockBatchMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockBatch) EXPECT() *MockBatchMockRecorder { return m.recorder } // Consistency mocks base method. func (m *MockBatch) Consistency(arg0 Consistency) Batch { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Consistency", arg0) ret0, _ := ret[0].(Batch) return ret0 } // Consistency indicates an expected call of Consistency. func (mr *MockBatchMockRecorder) Consistency(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Consistency", reflect.TypeOf((*MockBatch)(nil).Consistency), arg0) } // Query mocks base method. func (m *MockBatch) Query(arg0 string, arg1 ...any) { m.ctrl.T.Helper() varargs := []any{arg0} for _, a := range arg1 { varargs = append(varargs, a) } m.ctrl.Call(m, "Query", varargs...) } // Query indicates an expected call of Query. func (mr *MockBatchMockRecorder) Query(arg0 any, arg1 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0}, arg1...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockBatch)(nil).Query), varargs...) } // WithContext mocks base method. func (m *MockBatch) WithContext(arg0 context.Context) Batch { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WithContext", arg0) ret0, _ := ret[0].(Batch) return ret0 } // WithContext indicates an expected call of WithContext. func (mr *MockBatchMockRecorder) WithContext(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithContext", reflect.TypeOf((*MockBatch)(nil).WithContext), arg0) } // WithTimestamp mocks base method. func (m *MockBatch) WithTimestamp(arg0 int64) Batch { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WithTimestamp", arg0) ret0, _ := ret[0].(Batch) return ret0 } // WithTimestamp indicates an expected call of WithTimestamp. func (mr *MockBatchMockRecorder) WithTimestamp(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithTimestamp", reflect.TypeOf((*MockBatch)(nil).WithTimestamp), arg0) } // MockIter is a mock of Iter interface. type MockIter struct { ctrl *gomock.Controller recorder *MockIterMockRecorder isgomock struct{} } // MockIterMockRecorder is the mock recorder for MockIter. type MockIterMockRecorder struct { mock *MockIter } // NewMockIter creates a new mock instance. func NewMockIter(ctrl *gomock.Controller) *MockIter { mock := &MockIter{ctrl: ctrl} mock.recorder = &MockIterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockIter) EXPECT() *MockIterMockRecorder { return m.recorder } // Close mocks base method. func (m *MockIter) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockIterMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockIter)(nil).Close)) } // MapScan mocks base method. func (m *MockIter) MapScan(arg0 map[string]any) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MapScan", arg0) ret0, _ := ret[0].(bool) return ret0 } // MapScan indicates an expected call of MapScan. func (mr *MockIterMockRecorder) MapScan(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MapScan", reflect.TypeOf((*MockIter)(nil).MapScan), arg0) } // PageState mocks base method. func (m *MockIter) PageState() []byte { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PageState") ret0, _ := ret[0].([]byte) return ret0 } // PageState indicates an expected call of PageState. func (mr *MockIterMockRecorder) PageState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PageState", reflect.TypeOf((*MockIter)(nil).PageState)) } // Scan mocks base method. func (m *MockIter) Scan(arg0 ...any) bool { m.ctrl.T.Helper() varargs := []any{} for _, a := range arg0 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Scan", varargs...) ret0, _ := ret[0].(bool) return ret0 } // Scan indicates an expected call of Scan. func (mr *MockIterMockRecorder) Scan(arg0 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scan", reflect.TypeOf((*MockIter)(nil).Scan), arg0...) } // MockUUID is a mock of UUID interface. type MockUUID struct { ctrl *gomock.Controller recorder *MockUUIDMockRecorder isgomock struct{} } // MockUUIDMockRecorder is the mock recorder for MockUUID. type MockUUIDMockRecorder struct { mock *MockUUID } // NewMockUUID creates a new mock instance. func NewMockUUID(ctrl *gomock.Controller) *MockUUID { mock := &MockUUID{ctrl: ctrl} mock.recorder = &MockUUIDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockUUID) EXPECT() *MockUUIDMockRecorder { return m.recorder } // String mocks base method. func (m *MockUUID) String() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "String") ret0, _ := ret[0].(string) return ret0 } // String indicates an expected call of String. func (mr *MockUUIDMockRecorder) String() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockUUID)(nil).String)) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/public/client.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package public import ( "context" "errors" "strings" gogocql "github.com/gocql/gocql" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" ) var _ gocql.Client = client{} type ( client struct { } ) func init() { gocql.RegisterClient(client{}) } func (c client) CreateSession( config gocql.ClusterConfig, ) (gocql.Session, error) { return gocql.NewSession(config) } func (c client) IsTimeoutError(err error) bool { if err == context.DeadlineExceeded { return true } if err == gogocql.ErrTimeoutNoResponse { return true } if err == gogocql.ErrConnectionClosed { return true } _, ok := err.(*gogocql.RequestErrWriteTimeout) return ok } func (c client) IsNotFoundError(err error) bool { return err == gogocql.ErrNotFound } func (c client) IsThrottlingError(err error) bool { if req, ok := err.(gogocql.RequestError); ok { // gocql does not expose the constant errOverloaded = 0x1001 return req.Code() == 0x1001 } return false } // IsDBUnavailableError checks if the error is a database unavailable error // relating to issues like the database being overloaded or the consistency level not being achievable // because a node is down. func (c client) IsDBUnavailableError(err error) bool { var e gogocql.RequestError if errors.As(err, &e) { // sanity check that the error is the expected error if e.Code() != gogocql.ErrCodeUnavailable { return false } // emit these errors in the condition that the database is in trouble // and, from an actionability standpoint, the problem points to something // at the database level that require resolution // https://github.com/apache/cassandra/blob/3c69bd23673caa40b22f3500a3e3eecabc25c8e5/src/java/org/apache/cassandra/locator/ReplicaPlans.java#L329 if strings.Contains(e.Message(), "Cannot perform LWT operation") || strings.Contains(strings.ToLower(e.Message()), "cannot achieve consistency level") { return true } } return false } func (c client) IsCassandraConsistencyError(err error) bool { if req, ok := err.(gogocql.RequestError); ok { // 0x1000 == UNAVAILABLE return req.Code() == 0x1000 } return false } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/public/client_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package public import ( "context" "fmt" "testing" "github.com/gocql/gocql" "github.com/stretchr/testify/assert" ) // MockError to simulate gocql.Error behavior type MockError struct { gocql.RequestError code int message string } func (m MockError) Code() int { return m.code } func (m MockError) Message() string { return m.message } func TestClient_IsTimeoutError(t *testing.T) { client := client{} errorMap := map[error]bool{ nil: false, context.DeadlineExceeded: true, gocql.ErrTimeoutNoResponse: true, gocql.ErrConnectionClosed: true, &gocql.RequestErrWriteTimeout{}: true, gocql.ErrFrameTooBig: false, } for err, expected := range errorMap { assert.Equal(t, expected, client.IsTimeoutError(err)) } } func TestClient_IsNotFoundError(t *testing.T) { client := client{} errorMap := map[error]bool{ nil: false, gocql.ErrNotFound: true, gocql.ErrFrameTooBig: false, } for err, expected := range errorMap { assert.Equal(t, expected, client.IsNotFoundError(err)) } } // TestClient_IsThrottlingError tests the IsThrottlingError function with different error codes func TestClient_IsThrottlingError(t *testing.T) { client := client{} tests := []struct { name string mockErrorCode int expectedResult bool nonCompatibleError error }{ { name: "With Throttling Error", mockErrorCode: 0x1001, expectedResult: true, }, { name: "With Non-Throttling Error", mockErrorCode: 0x0001, expectedResult: false, nonCompatibleError: fmt.Errorf("with Non-Throttling Error"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.nonCompatibleError != nil { result := client.IsThrottlingError(tt.nonCompatibleError) assert.False(t, result) } err := MockError{code: tt.mockErrorCode} result := client.IsThrottlingError(err) assert.Equal(t, tt.expectedResult, result) }) } } func TestClient_IsDBUnavailableError(t *testing.T) { client := client{} tests := []struct { name string err error expectedResult bool }{ { name: "nil error returns false", err: nil, expectedResult: false, }, { name: "non-compatible error returns false", err: fmt.Errorf("some generic error"), expectedResult: false, }, { name: "UNAVAILABLE error with LWT message returns true", err: MockError{code: 0x1000, message: "Cannot perform LWT operation"}, expectedResult: true, }, { name: "UNAVAILABLE error with consistency level message returns true", err: MockError{code: 0x1000, message: "Cannot achieve consistency level QUORUM"}, expectedResult: true, }, { name: "UNAVAILABLE error without matching message returns false", err: MockError{code: 0x1000, message: "some other unavailable error"}, expectedResult: false, }, { name: "wrong error code with LWT message returns false", err: MockError{code: 0x0001, message: "Cannot perform LWT operation"}, expectedResult: false, }, { name: "wrong error code with consistency level message returns false", err: MockError{code: 0x1001, message: "Cannot achieve consistency level QUORUM"}, expectedResult: false, }, { name: "wrapped UNAVAILABLE error with LWT message returns true", err: fmt.Errorf("wrapped: %w", MockError{code: 0x1000, message: "Cannot perform LWT operation"}), expectedResult: true, }, { name: "wrapped UNAVAILABLE error with consistency level message returns true", err: fmt.Errorf("wrapped: %w", MockError{code: 0x1000, message: "cannot achieve consistency level QUORUM"}), expectedResult: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := client.IsDBUnavailableError(tt.err) assert.Equal(t, tt.expectedResult, result, "IsDBUnavailableError(%v) = %v, want %v", tt.err, result, tt.expectedResult) }) } } func TestClient_IsCassandraConsistencyError(t *testing.T) { client := client{} tests := []struct { name string mockErrorCode int expectedResult bool nonCompatibleError error }{ { name: "With Cassandra Consistency Error", mockErrorCode: 0x1000, expectedResult: true, }, { name: "With Non-Cassandra Consistency Error", mockErrorCode: 0x0001, expectedResult: false, }, { name: "With Non-compatible Error", mockErrorCode: 0x0001, expectedResult: false, nonCompatibleError: fmt.Errorf("with Non-compatible Error"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.nonCompatibleError != nil { result := client.IsCassandraConsistencyError(tt.nonCompatibleError) assert.False(t, result) } err := MockError{code: tt.mockErrorCode} result := client.IsCassandraConsistencyError(err) assert.Equal(t, tt.expectedResult, result) }) } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/public/testdata.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package public import ( "testing" persistencetests "github.com/uber/cadence/common/persistence/persistence-tests" _ "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra" // needed to load cassandra plugin ) // NewTestBaseWithPublicCassandra returns a persistence test base backed by cassandra datastore // It is only being used by testing against external/public Cassandra, which require to load the default gocql client func NewTestBaseWithPublicCassandra(t *testing.T, options *persistencetests.TestBaseOptions) *persistencetests.TestBase { if options.DBPluginName == "" { options.DBPluginName = "cassandra" } return persistencetests.NewTestBaseWithNoSQL(t, options) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/query.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gocql import ( "context" "github.com/gocql/gocql" ) var _ Query = (*query)(nil) type ( query struct { *gocql.Query session *session } ) func newQuery( session *session, gocqlQuery *gocql.Query, ) *query { return &query{ Query: gocqlQuery, session: session, } } func (q *query) Exec() error { err := q.Query.Exec() return q.handleError(err) } func (q *query) Scan( dest ...interface{}, ) error { err := q.Query.Scan(dest...) return q.handleError(err) } func (q *query) ScanCAS( dest ...interface{}, ) (bool, error) { applied, err := q.Query.ScanCAS(dest...) return applied, q.handleError(err) } func (q *query) MapScan( m map[string]interface{}, ) error { err := q.Query.MapScan(m) return q.handleError(err) } func (q *query) MapScanCAS( dest map[string]interface{}, ) (bool, error) { applied, err := q.Query.MapScanCAS(dest) return applied, q.handleError(err) } func (q *query) Iter() Iter { iter := q.Query.Iter() if iter == nil { return nil } return iter } func (q *query) PageSize(n int) Query { q.Query.PageSize(n) return q } func (q *query) PageState(state []byte) Query { q.Query.PageState(state) return q } func (q *query) Consistency(c Consistency) Query { q.Query.Consistency(mustConvertConsistency(c)) return q } func (q *query) WithTimestamp(timestamp int64) Query { q.Query.WithTimestamp(timestamp) return q } func (q *query) WithContext(ctx context.Context) Query { q2 := q.Query.WithContext(ctx) if q2 == nil { return nil } return newQuery(q.session, q2) } func (q *query) Bind(v ...interface{}) Query { q.Query.Bind(v...) return q } func (q *query) handleError(err error) error { return q.session.handleError(err) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/gocql/session.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package gocql import ( "sync" "sync/atomic" "time" "github.com/gocql/gocql" "github.com/uber/cadence/common" ) var _ Session = (*session)(nil) const ( sessionRefreshMinInternal = 5 * time.Second ) type ( session struct { atomic.Value // *gocql.Session sync.Mutex status int32 config ClusterConfig sessionInitTime time.Time } ) func NewSession( config ClusterConfig, ) (Session, error) { gocqlSession, err := initSession(config) if err != nil { return nil, err } session := &session{ status: common.DaemonStatusStarted, config: config, sessionInitTime: time.Now().UTC(), } session.Value.Store(gocqlSession) return session, nil } func initSession( config ClusterConfig, ) (*gocql.Session, error) { cluster := newCassandraCluster(config) cluster.Consistency = mustConvertConsistency(config.Consistency) cluster.SerialConsistency = mustConvertSerialConsistency(config.SerialConsistency) cluster.Timeout = config.Timeout cluster.ConnectTimeout = config.ConnectTimeout return cluster.CreateSession() } func (s *session) refresh() error { if atomic.LoadInt32(&s.status) != common.DaemonStatusStarted { return nil } s.Lock() defer s.Unlock() if time.Now().UTC().Sub(s.sessionInitTime) < sessionRefreshMinInternal { return nil } newSession, err := initSession(s.config) if err != nil { return err } s.sessionInitTime = time.Now().UTC() oldSession := s.Value.Load().(*gocql.Session) s.Value.Store(newSession) oldSession.Close() return nil } func (s *session) Query( stmt string, values ...interface{}, ) Query { q := s.Value.Load().(*gocql.Session).Query(stmt, values...) if q == nil { return nil } return newQuery(s, q) } func (s *session) NewBatch( batchType BatchType, ) Batch { b := s.Value.Load().(*gocql.Session).NewBatch(mustConvertBatchType(batchType)) if b == nil { return nil } return newBatch(b) } func (s *session) ExecuteBatch( b Batch, ) error { err := s.Value.Load().(*gocql.Session).ExecuteBatch(b.(*batch).Batch) return s.handleError(err) } func (s *session) MapExecuteBatchCAS( b Batch, previous map[string]interface{}, ) (bool, Iter, error) { applied, iter, err := s.Value.Load().(*gocql.Session).MapExecuteBatchCAS(b.(*batch).Batch, previous) if iter == nil { return applied, nil, s.handleError(err) } return applied, iter, s.handleError(err) } func (s *session) Close() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } s.Value.Load().(*gocql.Session).Close() } func (s *session) handleError(err error) error { if err == gocql.ErrNoConnections { _ = s.refresh() } return err } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/history_events.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "fmt" "sort" "time" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/types" ) // InsertIntoHistoryTreeAndNode inserts one or two rows: tree row and node row(at least one of them) func (db *CDB) InsertIntoHistoryTreeAndNode(ctx context.Context, treeRow *nosqlplugin.HistoryTreeRow, nodeRow *nosqlplugin.HistoryNodeRow) error { if treeRow == nil && nodeRow == nil { return fmt.Errorf("require at least a tree row or a node row to insert") } var ancs []map[string]interface{} if treeRow != nil { for _, an := range treeRow.Ancestors { value := make(map[string]interface{}) value["end_node_id"] = an.EndNodeID value["branch_id"] = an.BranchID ancs = append(ancs, value) } } var err error if treeRow != nil && nodeRow != nil { // Note: for perf, prefer using batch for inserting more than one records batch := db.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) batch.Query(v2templateInsertTree, treeRow.TreeID, treeRow.BranchID, ancs, persistence.UnixNanoToDBTimestamp(treeRow.CreateTimestamp.UnixNano()), treeRow.Info, treeRow.CreateTimestamp) batch.Query(v2templateUpsertData, nodeRow.TreeID, nodeRow.BranchID, nodeRow.NodeID, nodeRow.TxnID, nodeRow.Data, nodeRow.DataEncoding, nodeRow.CreateTimestamp) err = db.session.ExecuteBatch(batch) } else { var query gocql.Query if treeRow != nil { query = db.session.Query(v2templateInsertTree, treeRow.TreeID, treeRow.BranchID, ancs, persistence.UnixNanoToDBTimestamp(treeRow.CreateTimestamp.UnixNano()), treeRow.Info, treeRow.CreateTimestamp).WithContext(ctx) } if nodeRow != nil { query = db.session.Query(v2templateUpsertData, nodeRow.TreeID, nodeRow.BranchID, nodeRow.NodeID, nodeRow.TxnID, nodeRow.Data, nodeRow.DataEncoding, nodeRow.CreateTimestamp).WithContext(ctx) } err = query.Exec() } return err } // SelectFromHistoryNode read nodes based on a filter func (db *CDB) SelectFromHistoryNode(ctx context.Context, filter *nosqlplugin.HistoryNodeFilter) ([]*nosqlplugin.HistoryNodeRow, []byte, error) { query := db.session.Query(v2templateReadData, filter.TreeID, filter.BranchID, filter.MinNodeID, filter.MaxNodeID).WithContext(ctx) iter := query.PageSize(filter.PageSize).PageState(filter.NextPageToken).Iter() if iter == nil { return nil, nil, &types.InternalServiceError{ Message: "SelectFromHistoryNode operation failed. Not able to create query iterator.", } } pagingToken := iter.PageState() var rows []*nosqlplugin.HistoryNodeRow row := &nosqlplugin.HistoryNodeRow{} for iter.Scan(&row.NodeID, &row.TxnID, &row.Data, &row.DataEncoding) { rows = append(rows, row) row = &nosqlplugin.HistoryNodeRow{} } if err := iter.Close(); err != nil { return nil, nil, err } return rows, pagingToken, nil } // DeleteFromHistoryTreeAndNode delete a branch record, and a list of ranges of nodes. func (db *CDB) DeleteFromHistoryTreeAndNode(ctx context.Context, treeFilter *nosqlplugin.HistoryTreeFilter, nodeFilters []*nosqlplugin.HistoryNodeFilter) error { batch := db.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) batch.Query(v2templateDeleteBranch, treeFilter.TreeID, treeFilter.BranchID) for _, nodeFilter := range nodeFilters { batch.Query(v2templateRangeDeleteData, nodeFilter.TreeID, nodeFilter.BranchID, nodeFilter.MinNodeID) } return db.executeBatchWithConsistencyAll(batch) } // SelectAllHistoryTrees will return all tree branches with pagination func (db *CDB) SelectAllHistoryTrees(ctx context.Context, nextPageToken []byte, pageSize int) ([]*nosqlplugin.HistoryTreeRow, []byte, error) { query := db.session.Query(v2templateScanAllTreeBranches).WithContext(ctx) iter := query.PageSize(int(pageSize)).PageState(nextPageToken).Iter() if iter == nil { return nil, nil, &types.InternalServiceError{ Message: "SelectAllHistoryTrees operation failed. Not able to create query iterator.", } } pagingToken := iter.PageState() createTime := time.Time{} var rows []*nosqlplugin.HistoryTreeRow row := &nosqlplugin.HistoryTreeRow{} for iter.Scan(&row.TreeID, &row.BranchID, &createTime, &row.Info) { row.CreateTimestamp = time.Unix(0, persistence.DBTimestampToUnixNano(persistence.UnixNanoToDBTimestamp(createTime.UnixNano()))) rows = append(rows, row) row = &nosqlplugin.HistoryTreeRow{} } if err := iter.Close(); err != nil { return nil, nil, err } return rows, pagingToken, nil } // SelectFromHistoryTree read branch records for a tree func (db *CDB) SelectFromHistoryTree(ctx context.Context, filter *nosqlplugin.HistoryTreeFilter) ([]*nosqlplugin.HistoryTreeRow, error) { query := db.session.Query(v2templateReadAllBranches, filter.TreeID).WithContext(ctx) var pagingToken []byte var iter gocql.Iter var rows []*nosqlplugin.HistoryTreeRow for { iter = query.PageSize(100).PageState(pagingToken).Iter() if iter == nil { return nil, &types.InternalServiceError{ Message: "SelectFromHistoryTree operation failed. Not able to create query iterator.", } } pagingToken = iter.PageState() branchUUID := "" var ancsResult []map[string]interface{} // Ideally we should just use int64. But we have been using time.Time for a long time. // I am not sure using a int64 will behave the same. // Therefore, here still using a time.Time to read, and then convert to int64 createTime := time.Time{} info := "" for iter.Scan(&branchUUID, &ancsResult, &createTime, &info) { ancs := parseBranchAncestors(ancsResult) row := &nosqlplugin.HistoryTreeRow{ TreeID: filter.TreeID, BranchID: branchUUID, Ancestors: ancs, } rows = append(rows, row) branchUUID = "" ancsResult = []map[string]interface{}{} createTime = time.Time{} info = "" } if err := iter.Close(); err != nil { return nil, err } if len(pagingToken) == 0 { break } } return rows, nil } func parseBranchAncestors( ancestors []map[string]interface{}, ) []*types.HistoryBranchRange { ans := make([]*types.HistoryBranchRange, 0, len(ancestors)) for _, e := range ancestors { an := &types.HistoryBranchRange{} for k, v := range e { switch k { case "branch_id": an.BranchID = v.(gocql.UUID).String() case "end_node_id": an.EndNodeID = v.(int64) } } ans = append(ans, an) } if len(ans) > 0 { // sort ans based onf EndNodeID so that we can set BeginNodeID sort.Slice(ans, func(i, j int) bool { return ans[i].EndNodeID < ans[j].EndNodeID }) ans[0].BeginNodeID = int64(1) for i := 1; i < len(ans); i++ { ans[i].BeginNodeID = ans[i-1].EndNodeID } } return ans } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/history_events_cql.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra const ( // below are templates for history_node table v2templateUpsertData = `INSERT INTO history_node (` + `tree_id, branch_id, node_id, txn_id, data, data_encoding, created_time) ` + `VALUES (?, ?, ?, ?, ?, ?, ?) ` v2templateReadData = `SELECT node_id, txn_id, data, data_encoding FROM history_node ` + `WHERE tree_id = ? AND branch_id = ? AND node_id >= ? AND node_id < ? ` v2templateRangeDeleteData = `DELETE FROM history_node WHERE tree_id = ? AND branch_id = ? AND node_id >= ? ` // below are templates for history_tree table v2templateInsertTree = `INSERT INTO history_tree (` + `tree_id, branch_id, ancestors, fork_time, info, created_time) ` + `VALUES (?, ?, ?, ?, ?, ?) ` v2templateReadAllBranches = `SELECT branch_id, ancestors, fork_time, info FROM history_tree WHERE tree_id = ? ` v2templateDeleteBranch = `DELETE FROM history_tree WHERE tree_id = ? AND branch_id = ? ` v2templateScanAllTreeBranches = `SELECT tree_id, branch_id, fork_time, info FROM history_tree ` ) ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/history_events_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cassandra import ( "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/types" ) func TestInsertIntoHistoryTreeAndNode(t *testing.T) { tests := []struct { name string treeRow *nosqlplugin.HistoryTreeRow nodeRow *nosqlplugin.HistoryNodeRow setupMocks func(*gomock.Controller, *fakeSession) expectError bool }{ { name: "Successfully insert both tree and node rows using batch", treeRow: &nosqlplugin.HistoryTreeRow{ TreeID: "treeID", BranchID: "branchID", Ancestors: []*types.HistoryBranchRange{{BranchID: "branch1", EndNodeID: 100}}, CreateTimestamp: time.Now(), }, nodeRow: &nosqlplugin.HistoryNodeRow{ TreeID: "treeID", BranchID: "branchID", NodeID: 1, TxnID: nil, Data: []byte("node data"), DataEncoding: "encoding", }, setupMocks: func(ctrl *gomock.Controller, session *fakeSession) { mockBatch := gocql.NewMockBatch(ctrl) mockQuery := gocql.NewMockQuery(ctrl) mockBatch.EXPECT().WithContext(gomock.Any()).Return(mockBatch).AnyTimes() mockBatch.EXPECT().Query(v2templateInsertTree, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes() mockBatch.EXPECT().Query(v2templateUpsertData, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes() session.query = mockQuery }, expectError: false, }, { name: "Successfully insert only tree row", treeRow: &nosqlplugin.HistoryTreeRow{ TreeID: "treeID", BranchID: "branchID", Ancestors: []*types.HistoryBranchRange{{BranchID: "branch1", EndNodeID: 100}}, CreateTimestamp: time.Now(), }, nodeRow: nil, // No node row provided setupMocks: func(ctrl *gomock.Controller, session *fakeSession) { mockQuery := gocql.NewMockQuery(ctrl) mockQuery.EXPECT().WithContext(gomock.Any()).Return(mockQuery) mockQuery.EXPECT().Exec().Return(nil) session.query = mockQuery }, expectError: false, }, { name: "Successfully insert only node row", treeRow: nil, // No tree row provided nodeRow: &nosqlplugin.HistoryNodeRow{ TreeID: "treeID", BranchID: "branchID", NodeID: 1, TxnID: nil, // Optional field Data: []byte("node data"), DataEncoding: "encoding", }, setupMocks: func(ctrl *gomock.Controller, session *fakeSession) { mockQuery := gocql.NewMockQuery(ctrl) mockQuery.EXPECT().WithContext(gomock.Any()).Return(mockQuery) mockQuery.EXPECT().Exec().Return(nil) session.query = mockQuery }, expectError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) session := &fakeSession{} if tt.setupMocks != nil { tt.setupMocks(ctrl, session) } db := &CDB{session: session} err := db.InsertIntoHistoryTreeAndNode(context.Background(), tt.treeRow, tt.nodeRow) if tt.expectError { assert.Error(t, err, "Expected an error but got none") } else { assert.NoError(t, err, "Did not expect an error but got one") } }) } } func TestSelectFromHistoryNode(t *testing.T) { txnID1 := int64(1) txnID2 := int64(2) tests := []struct { name string filter *nosqlplugin.HistoryNodeFilter setupMocks func(*gomock.Controller, *fakeSession) expectedRows []*nosqlplugin.HistoryNodeRow expectedToken []byte expectError bool }{ { name: "Successfully retrieve history nodes", filter: &nosqlplugin.HistoryNodeFilter{ TreeID: "treeID", BranchID: "branchID", MinNodeID: 1, MaxNodeID: 10, PageSize: 5, NextPageToken: nil, }, setupMocks: func(ctrl *gomock.Controller, session *fakeSession) { mockQuery := gocql.NewMockQuery(ctrl) mockQuery.EXPECT().WithContext(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageSize(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageState(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().Iter().Return(&fakeIter{ scanInputs: [][]interface{}{ {int64(1), &txnID1, []byte("data1"), "encoding"}, {int64(2), &txnID2, []byte("data2"), "encoding"}, }, pageState: []byte("nextPageToken"), }).AnyTimes() session.query = mockQuery }, expectedRows: []*nosqlplugin.HistoryNodeRow{ {NodeID: int64(1), TxnID: &txnID1, Data: []byte("data1"), DataEncoding: "encoding"}, {NodeID: int64(2), TxnID: &txnID2, Data: []byte("data2"), DataEncoding: "encoding"}, }, expectedToken: []byte("nextPageToken"), expectError: false, }, { name: "Failure to create query iterator", filter: &nosqlplugin.HistoryNodeFilter{ TreeID: "treeID", BranchID: "branchID", MinNodeID: 1, MaxNodeID: 10, PageSize: 5, NextPageToken: nil, }, setupMocks: func(ctrl *gomock.Controller, session *fakeSession) { mockQuery := gocql.NewMockQuery(ctrl) mockQuery.EXPECT().WithContext(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageSize(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageState(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().Iter().Return(nil).AnyTimes() // Simulating failure session.query = mockQuery }, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) session := &fakeSession{} if tt.setupMocks != nil { tt.setupMocks(ctrl, session) } db := &CDB{session: session} rows, token, err := db.SelectFromHistoryNode(context.Background(), tt.filter) if tt.expectError { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.expectedRows, rows) assert.Equal(t, tt.expectedToken, token) } }) } } func TestDeleteFromHistoryTreeAndNode(t *testing.T) { tests := []struct { name string treeFilter *nosqlplugin.HistoryTreeFilter nodeFilters []*nosqlplugin.HistoryNodeFilter setupMocks func(*fakeSession) expectError bool }{ { name: "Successfully delete tree and nodes", treeFilter: &nosqlplugin.HistoryTreeFilter{ ShardID: 1, TreeID: "treeID", BranchID: stringPtr("branchID"), }, nodeFilters: []*nosqlplugin.HistoryNodeFilter{ {TreeID: "treeID", BranchID: "branchID", MinNodeID: 1}, {TreeID: "treeID", BranchID: "branchID", MinNodeID: 2}, }, setupMocks: func(session *fakeSession) { // Simulate successful batch execution session.mapExecuteBatchCASApplied = true }, expectError: false, }, { name: "Failure in batch execution", treeFilter: &nosqlplugin.HistoryTreeFilter{ ShardID: 1, TreeID: "treeID", BranchID: stringPtr("branchID"), }, nodeFilters: []*nosqlplugin.HistoryNodeFilter{ {TreeID: "treeID", BranchID: "branchID", MinNodeID: 1}, }, setupMocks: func(session *fakeSession) { // Simulate failure in batch execution session.mapExecuteBatchCASErr = types.InternalServiceError{Message: "DB operation failed"} }, expectError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { session := &fakeSession{} if tt.setupMocks != nil { tt.setupMocks(session) } db := &CDB{session: session} err := db.DeleteFromHistoryTreeAndNode(context.Background(), tt.treeFilter, tt.nodeFilters) if tt.expectError { assert.Error(t, err, "Expected an error but got none") } else { assert.NoError(t, err, "Did not expect an error but got one") } }) } } func TestSelectAllHistoryTrees(t *testing.T) { location, err := time.LoadLocation("UTC") if err != nil { t.Fatal(err) } fixedTime, err := time.ParseInLocation(time.RFC3339, "2023-12-12T22:08:41Z", location) if err != nil { t.Fatal(err) } tests := []struct { name string nextPageToken []byte pageSize int setupMocks func(*gomock.Controller, *fakeSession) expectedRows []*nosqlplugin.HistoryTreeRow expectedToken []byte expectError bool }{ { name: "Successfully retrieve all history trees", nextPageToken: []byte("token"), pageSize: 10, setupMocks: func(ctrl *gomock.Controller, session *fakeSession) { mockQuery := gocql.NewMockQuery(ctrl) mockQuery.EXPECT().WithContext(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageSize(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageState(gomock.Any()).Return(mockQuery).AnyTimes() session.iter = &fakeIter{ scanInputs: [][]interface{}{ {"treeID1", "branchID1", fixedTime, "Tree info 1"}, {"treeID2", "branchID2", fixedTime.Add(time.Minute), "Tree info 2"}, }, pageState: []byte("nextPageToken"), } mockQuery.EXPECT().Iter().Return(session.iter).AnyTimes() session.query = mockQuery }, expectedRows: []*nosqlplugin.HistoryTreeRow{ {TreeID: "treeID1", BranchID: "branchID1", CreateTimestamp: fixedTime, Info: "Tree info 1"}, {TreeID: "treeID2", BranchID: "branchID2", CreateTimestamp: fixedTime.Add(time.Minute), Info: "Tree info 2"}, }, expectedToken: []byte("nextPageToken"), expectError: false, }, { name: "Failed to create query iterator", nextPageToken: []byte("token"), pageSize: 10, setupMocks: func(ctrl *gomock.Controller, session *fakeSession) { mockQuery := gocql.NewMockQuery(ctrl) mockQuery.EXPECT().WithContext(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageSize(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageState(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().Iter().Return(nil).AnyTimes() // Simulate failure to create iterator session.query = mockQuery }, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) session := &fakeSession{} if tt.setupMocks != nil { tt.setupMocks(ctrl, session) } db := &CDB{session: session} rows, token, err := db.SelectAllHistoryTrees(context.Background(), tt.nextPageToken, tt.pageSize) if tt.expectError { assert.Error(t, err) } else { assert.NoError(t, err) for i, row := range rows { assertHistoryTreeRowEqual(t, tt.expectedRows[i], row) } assert.Equal(t, tt.expectedToken, token) } }) } } func TestSelectFromHistoryTree(t *testing.T) { tests := []struct { name string filter *nosqlplugin.HistoryTreeFilter setupMocks func(*gomock.Controller, *fakeSession) expectedRows []*nosqlplugin.HistoryTreeRow expectError bool }{ { name: "Successfully retrieve branches", filter: &nosqlplugin.HistoryTreeFilter{ TreeID: "treeID", }, setupMocks: func(ctrl *gomock.Controller, session *fakeSession) { mockQuery := gocql.NewMockQuery(ctrl) mockQuery.EXPECT().WithContext(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageSize(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageState(gomock.Any()).Return(mockQuery).AnyTimes() iter1 := newFakeIter([][]interface{}{ {"branchUUID1", []map[string]interface{}{{"branch_id": uuid.Parse(permanentRunID), "end_node_id": int64(10)}}, time.Now(), "Info1"}, }, []byte("nextPageToken")) iter2 := newFakeIter([][]interface{}{ {"branchUUID2", []map[string]interface{}{{"branch_id": uuid.Parse(permanentRunID), "end_node_id": int64(20)}}, time.Now(), "Info2"}, }, nil) // No more pages gomock.InOrder( mockQuery.EXPECT().Iter().Return(iter1), mockQuery.EXPECT().Iter().Return(iter2), ) session.query = mockQuery }, expectedRows: []*nosqlplugin.HistoryTreeRow{ {TreeID: "treeID", BranchID: "branchUUID1", Ancestors: []*types.HistoryBranchRange{{BranchID: permanentRunID, EndNodeID: 10, BeginNodeID: 1}}, Info: ""}, {TreeID: "treeID", BranchID: "branchUUID2", Ancestors: []*types.HistoryBranchRange{{BranchID: permanentRunID, EndNodeID: 20, BeginNodeID: 1}}, Info: ""}, }, expectError: false, }, { name: "Failed to create query iterator", filter: &nosqlplugin.HistoryTreeFilter{ TreeID: "treeID", }, setupMocks: func(ctrl *gomock.Controller, session *fakeSession) { mockQuery := gocql.NewMockQuery(ctrl) mockQuery.EXPECT().WithContext(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageSize(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().PageState(gomock.Any()).Return(mockQuery).AnyTimes() mockQuery.EXPECT().Iter().Return(nil) // Simulate failure to create iterator session.query = mockQuery }, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) session := &fakeSession{} if tt.setupMocks != nil { tt.setupMocks(ctrl, session) } db := &CDB{session: session} rows, err := db.SelectFromHistoryTree(context.Background(), tt.filter) if tt.expectError { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.expectedRows, rows) } }) } } // Helper to create a fake iterator with predefined results func newFakeIter(data [][]interface{}, nextPageToken []byte) *fakeIter { return &fakeIter{ scanInputs: data, pageState: nextPageToken, } } func assertHistoryTreeRowEqual(t *testing.T, expected, actual *nosqlplugin.HistoryTreeRow) { assert.Equal(t, expected.TreeID, actual.TreeID) assert.Equal(t, expected.BranchID, actual.BranchID) assert.Equal(t, expected.Info, actual.Info) assert.True(t, expected.CreateTimestamp.Equal(actual.CreateTimestamp), "Timestamps do not match") } func stringPtr(s string) *string { return &s } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/plugin.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "fmt" "time" gogocql "github.com/gocql/gocql" "github.com/hailocab/go-hostpool" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/environment" ) const ( // PluginName is the name of the plugin PluginName = "cassandra" defaultSessionTimeout = 10 * time.Second defaultConnectTimeout = 2 * time.Second ) type plugin struct{} var _ nosqlplugin.Plugin = (*plugin)(nil) func init() { nosql.RegisterPlugin(PluginName, &plugin{}) } // CreateDB initialize the db object func (p *plugin) CreateDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (nosqlplugin.DB, error) { return p.doCreateDB(cfg, logger, dc) } // CreateAdminDB initialize the AdminDB object func (p *plugin) CreateAdminDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (nosqlplugin.AdminDB, error) { // the keyspace is not created yet, so use empty and let the Cassandra connect keyspace := cfg.Keyspace cfg.Keyspace = "" // change it back defer func() { cfg.Keyspace = keyspace }() return p.doCreateDB(cfg, logger, dc) } func (p *plugin) doCreateDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (*CDB, error) { gocqlConfig, err := toGoCqlConfig(cfg) if err != nil { return nil, err } session, err := gocql.GetRegisteredClient().CreateSession(gocqlConfig) if err != nil { return nil, err } db := NewCassandraDBFromSession(cfg, session, logger, dc) return db, nil } func toGoCqlConfig(cfg *config.NoSQL) (gocql.ClusterConfig, error) { var err error if cfg.Port == 0 { cfg.Port, err = environment.GetCassandraPort() if err != nil { return gocql.ClusterConfig{}, err } } if cfg.Hosts == "" { cfg.Hosts = environment.GetCassandraAddress() } if cfg.ProtoVersion == 0 { cfg.ProtoVersion, err = environment.GetCassandraProtoVersion() if err != nil { return gocql.ClusterConfig{}, err } } if cfg.Timeout == 0 { cfg.Timeout = defaultSessionTimeout } if cfg.ConnectTimeout == 0 { cfg.ConnectTimeout = defaultConnectTimeout } if cfg.Consistency == "" { cfg.Consistency = cassandraDefaultConsLevel.String() } if cfg.SerialConsistency == "" { cfg.SerialConsistency = cassandraDefaultSerialConsLevel.String() } consistency, err := gocql.ParseConsistency(cfg.Consistency) if err != nil { return gocql.ClusterConfig{}, err } serialConsistency, err := gocql.ParseSerialConsistency(cfg.SerialConsistency) if err != nil { return gocql.ClusterConfig{}, err } hostSelection, err := toHostSelectionPolicy(cfg.HostSelectionPolicy) if err != nil { return gocql.ClusterConfig{}, err } return gocql.ClusterConfig{ Hosts: cfg.Hosts, Port: cfg.Port, User: cfg.User, Password: cfg.Password, AllowedAuthenticators: cfg.AllowedAuthenticators, Keyspace: cfg.Keyspace, Region: cfg.Region, Datacenter: cfg.Datacenter, MaxConns: cfg.MaxConns, TLS: cfg.TLS, ProtoVersion: cfg.ProtoVersion, Consistency: consistency, SerialConsistency: serialConsistency, Timeout: cfg.Timeout, ConnectTimeout: cfg.ConnectTimeout, HostSelectionPolicy: hostSelection, }, nil } func toHostSelectionPolicy(policy string) (gogocql.HostSelectionPolicy, error) { switch policy { case "", "tokenaware,roundrobin": return gogocql.TokenAwareHostPolicy(gogocql.RoundRobinHostPolicy()), nil case "hostpool-epsilon-greedy": return gogocql.HostPoolHostPolicy( hostpool.NewEpsilonGreedy(nil, 0, &hostpool.LinearEpsilonValueCalculator{}), ), nil case "roundrobin": return gogocql.RoundRobinHostPolicy(), nil default: return nil, fmt.Errorf("unknown gocql host selection policy: %q", policy) } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/plugin_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cassandra import ( "fmt" "testing" "time" gogocql "github.com/gocql/gocql" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/environment" ) func Test_toGoCqlConfig(t *testing.T) { t.Setenv(environment.CassandraSeeds, environment.Localhost) tests := []struct { name string cfg *config.NoSQL want gocql.ClusterConfig wantErr assert.ErrorAssertionFunc }{ { "empty config will be filled with defaults", &config.NoSQL{}, gocql.ClusterConfig{ Hosts: environment.Localhost, Port: 9042, ProtoVersion: 4, Timeout: time.Second * 10, Consistency: gocql.LocalQuorum, SerialConsistency: gocql.LocalSerial, ConnectTimeout: time.Second * 2, HostSelectionPolicy: gogocql.TokenAwareHostPolicy(gogocql.RoundRobinHostPolicy()), }, assert.NoError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := toGoCqlConfig(tt.cfg) if !tt.wantErr(t, err, fmt.Sprintf("toGoCqlConfig(%v)", tt.cfg)) { return } assert.Equalf(t, tt.want, got, "toGoCqlConfig(%v)", tt.cfg) }) } } func Test_toHostSelectionPolicy(t *testing.T) { tests := []struct { name string policy string want gogocql.HostSelectionPolicy wantErr assert.ErrorAssertionFunc }{ {name: "not supported policy", policy: "non-existing", want: nil, wantErr: assert.Error}, {name: "Default policy", policy: "", want: gogocql.TokenAwareHostPolicy(gogocql.RoundRobinHostPolicy()), wantErr: assert.NoError}, {name: "Round robin policy", policy: "roundrobin", want: gogocql.RoundRobinHostPolicy(), wantErr: assert.NoError}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := toHostSelectionPolicy(tt.policy) if !tt.wantErr(t, err, fmt.Sprintf("toHostSelectionPolicy(%v)", tt.policy)) { return } assert.Equal(t, tt.want, got, "toHostSelectionPolicy(%v)", tt.policy) }) } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/queue.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "fmt" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // Insert message into queue, return error if failed or already exists // Must return ConditionFailure error if row already exists func (db *CDB) InsertIntoQueue( ctx context.Context, row *nosqlplugin.QueueMessageRow, ) error { timeStamp := row.CurrentTimeStamp query := db.session.Query(templateEnqueueMessageQuery, row.QueueType, row.ID, row.Payload, timeStamp).WithContext(ctx) previous := make(map[string]interface{}) applied, err := query.MapScanCAS(previous) if err != nil { return err } if !applied { return nosqlplugin.NewConditionFailure("queue") } return nil } // Get the ID of last message inserted into the queue func (db *CDB) SelectLastEnqueuedMessageID( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { query := db.session.Query(templateGetLastMessageIDQuery, queueType).WithContext(ctx) result := make(map[string]interface{}) err := query.MapScan(result) if err != nil { return 0, err } return result["message_id"].(int64), nil } // Read queue messages starting from the exclusiveBeginMessageID func (db *CDB) SelectMessagesFrom( ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, maxRows int, ) ([]*nosqlplugin.QueueMessageRow, error) { // Reading replication tasks need to be quorum level consistent, otherwise we could loose task query := db.session.Query(templateGetMessagesQuery, queueType, exclusiveBeginMessageID, maxRows, ).WithContext(ctx) iter := query.Iter() if iter == nil { return nil, fmt.Errorf("SelectMessagesFrom operation failed. Not able to create query iterator") } var result []*nosqlplugin.QueueMessageRow message := make(map[string]interface{}) for iter.MapScan(message) { payload := getMessagePayload(message) id := getMessageID(message) result = append(result, &nosqlplugin.QueueMessageRow{ID: id, Payload: payload}) message = make(map[string]interface{}) } if err := iter.Close(); err != nil { return nil, err } return result, nil } // Read queue message starting from exclusiveBeginMessageID int64, inclusiveEndMessageID int64 func (db *CDB) SelectMessagesBetween( ctx context.Context, request nosqlplugin.SelectMessagesBetweenRequest, ) (*nosqlplugin.SelectMessagesBetweenResponse, error) { // Reading replication tasks need to be quorum level consistent, otherwise we could loose task // Use negative queue type as the dlq type query := db.session.Query(templateGetMessagesFromDLQQuery, request.QueueType, request.ExclusiveBeginMessageID, request.InclusiveEndMessageID, ).PageSize(request.PageSize).PageState(request.NextPageToken).WithContext(ctx) iter := query.Iter() if iter == nil { return nil, fmt.Errorf("SelectMessagesBetween operation failed. Not able to create query iterator") } var rows []nosqlplugin.QueueMessageRow message := make(map[string]interface{}) for iter.MapScan(message) { payload := getMessagePayload(message) id := getMessageID(message) rows = append(rows, nosqlplugin.QueueMessageRow{ID: id, Payload: payload}) message = make(map[string]interface{}) } nextPageToken := iter.PageState() if err := iter.Close(); err != nil { return nil, err } return &nosqlplugin.SelectMessagesBetweenResponse{ Rows: rows, NextPageToken: nextPageToken, }, nil } // Delete all messages before exclusiveBeginMessageID func (db *CDB) DeleteMessagesBefore( ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, ) error { query := db.session.Query(templateRangeDeleteMessagesBeforeQuery, queueType, exclusiveBeginMessageID).WithContext(ctx) return db.executeWithConsistencyAll(query) } // Delete all messages in a range between exclusiveBeginMessageID and inclusiveEndMessageID func (db *CDB) DeleteMessagesInRange( ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, inclusiveEndMessageID int64, ) error { query := db.session.Query(templateRangeDeleteMessagesBetweenQuery, queueType, exclusiveBeginMessageID, inclusiveEndMessageID).WithContext(ctx) return db.executeWithConsistencyAll(query) } // Delete one message func (db *CDB) DeleteMessage( ctx context.Context, queueType persistence.QueueType, messageID int64, ) error { query := db.session.Query(templateDeleteMessageQuery, queueType, messageID).WithContext(ctx) return db.executeWithConsistencyAll(query) } // Insert an empty metadata row, starting from a version func (db *CDB) InsertQueueMetadata(ctx context.Context, row nosqlplugin.QueueMetadataRow) error { timeStamp := row.CurrentTimeStamp clusterAckLevels := map[string]int64{} query := db.session.Query(templateInsertQueueMetadataQuery, row.QueueType, clusterAckLevels, row.Version, timeStamp).WithContext(ctx) // NOTE: Must pass nils to be compatible with ScyllaDB's LWT behavior // "Scylla always returns the old version of the row, regardless of whether the condition is true or not." // See also https://docs.scylladb.com/kb/lwt-differences/ _, err := query.ScanCAS(nil, nil, nil, nil, nil) if err != nil { return err } // it's ok if the query is not applied, which means that the record exists already. return nil } // **Conditionally** update a queue metadata row, if current version is matched(meaning current == row.Version - 1), // then the current version will increase by one when updating the metadata row // it should return ConditionFailure if the condition is not met func (db *CDB) UpdateQueueMetadataCas( ctx context.Context, row nosqlplugin.QueueMetadataRow, ) error { timeStamp := row.CurrentTimeStamp query := db.session.Query(templateUpdateQueueMetadataQuery, row.ClusterAckLevels, row.Version, timeStamp, row.QueueType, row.Version-1, ).WithContext(ctx) // NOTE: Must pass nils to be compatible with ScyllaDB's LWT behavior // "Scylla always returns the old version of the row, regardless of whether the condition is true or not." // See also https://docs.scylladb.com/kb/lwt-differences/ applied, err := query.ScanCAS(nil, nil, nil, nil, nil) if err != nil { return err } if !applied { return nosqlplugin.NewConditionFailure("queue") } return nil } // Read a QueueMetadata func (db *CDB) SelectQueueMetadata( ctx context.Context, queueType persistence.QueueType, ) (*nosqlplugin.QueueMetadataRow, error) { query := db.session.Query(templateGetQueueMetadataQuery, queueType).WithContext(ctx) var ackLevels map[string]int64 var version int64 err := query.Scan(&ackLevels, &version) if err != nil { return nil, err } // if record exist but ackLevels is empty, we initialize the map if ackLevels == nil { ackLevels = make(map[string]int64) } return &nosqlplugin.QueueMetadataRow{ QueueType: queueType, ClusterAckLevels: ackLevels, Version: version, }, nil } func (db *CDB) GetQueueSize( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { query := db.session.Query(templateGetQueueSizeQuery, queueType).WithContext(ctx) result := make(map[string]interface{}) if err := query.MapScan(result); err != nil { return 0, err } return result["count"].(int64), nil } func getMessagePayload(message map[string]interface{}) []byte { return message["message_payload"].([]byte) } func getMessageID(message map[string]interface{}) int64 { return message["message_id"].(int64) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/queue_cql.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra const ( templateEnqueueMessageQuery = `INSERT INTO queue (queue_type, message_id, message_payload, created_time) VALUES(?, ?, ?, ?) IF NOT EXISTS` templateGetLastMessageIDQuery = `SELECT message_id FROM queue WHERE queue_type=? ORDER BY message_id DESC LIMIT 1` templateGetMessagesQuery = `SELECT message_id, message_payload FROM queue WHERE queue_type = ? and message_id > ? LIMIT ?` templateGetMessagesFromDLQQuery = `SELECT message_id, message_payload FROM queue WHERE queue_type = ? and message_id > ? and message_id <= ?` templateRangeDeleteMessagesBeforeQuery = `DELETE FROM queue WHERE queue_type = ? and message_id < ?` templateRangeDeleteMessagesBetweenQuery = `DELETE FROM queue WHERE queue_type = ? and message_id > ? and message_id <= ?` templateDeleteMessageQuery = `DELETE FROM queue WHERE queue_type = ? and message_id = ?` templateGetQueueMetadataQuery = `SELECT cluster_ack_level, version FROM queue_metadata WHERE queue_type = ?` templateInsertQueueMetadataQuery = `INSERT INTO queue_metadata (queue_type, cluster_ack_level, version, created_time) VALUES(?, ?, ?, ?) IF NOT EXISTS` templateUpdateQueueMetadataQuery = `UPDATE queue_metadata SET cluster_ack_level = ?, version = ?, last_updated_time = ? WHERE queue_type = ? IF version = ?` templateGetQueueSizeQuery = `SELECT COUNT(1) AS count FROM queue WHERE queue_type=?` ) ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/queue_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "errors" "fmt" "testing" "github.com/google/go-cmp/cmp" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" ) func TestInsertIntoQueue(t *testing.T) { tests := []struct { name string row *nosqlplugin.QueueMessageRow queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "successfully applied", row: queueMessageRow(101), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return true, nil }).Times(1) }, wantQueries: []string{ `INSERT INTO queue (queue_type, message_id, message_payload, created_time) VALUES(1, 101, [116 101 115 116 45 112 97 121 108 111 97 100 45 49 48 49], 2025-01-06T15:00:00Z) IF NOT EXISTS`, }, }, { name: "not applied", row: queueMessageRow(101), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return false, nil }).Times(1) }, wantErr: true, }, { name: "mapscancas failed", row: queueMessageRow(101), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return false, errors.New("mapscancas failed") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.InsertIntoQueue(context.Background(), tc.row) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestSelectLastEnqueuedMessageID(t *testing.T) { tests := []struct { name string queueType persistence.QueueType queryMockFn func(query *gocql.MockQuery) wantQueries []string wantMsgID int64 wantErr bool }{ { name: "success with shard map fully populated", queueType: persistence.DomainReplicationQueueType, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { m["message_id"] = int64(101) return nil }).Times(1) }, wantMsgID: int64(101), wantQueries: []string{ `SELECT message_id FROM queue WHERE queue_type=1 ORDER BY message_id DESC LIMIT 1`, }, }, { name: "mapscan failed", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { return errors.New("mapscan failed") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotMsgID, err := db.SelectLastEnqueuedMessageID(context.Background(), tc.queueType) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if gotMsgID != tc.wantMsgID { t.Errorf("Got message ID = %v, want %v", gotMsgID, tc.wantMsgID) } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestSelectQueueMetadata(t *testing.T) { tests := []struct { name string queueType persistence.QueueType queryMockFn func(query *gocql.MockQuery) wantRow *nosqlplugin.QueueMetadataRow wantQueries []string wantErr bool }{ { name: "success", queueType: persistence.QueueType(2), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { ackLevels := args[0].(*map[string]int64) *ackLevels = make(map[string]int64) (*ackLevels)["cluster1"] = 1000 (*ackLevels)["cluster2"] = 2000 version := args[1].(*int64) *version = int64(25) return nil }).Times(1) }, wantRow: &nosqlplugin.QueueMetadataRow{ QueueType: persistence.QueueType(2), ClusterAckLevels: map[string]int64{"cluster1": 1000, "cluster2": 2000}, Version: 25, }, wantQueries: []string{ `SELECT cluster_ack_level, version FROM queue_metadata WHERE queue_type = 2`, }, }, { name: "success with empty acklevels", queueType: persistence.QueueType(2), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { version := args[1].(*int64) *version = int64(26) return nil }).Times(1) }, wantRow: &nosqlplugin.QueueMetadataRow{ QueueType: persistence.QueueType(2), ClusterAckLevels: map[string]int64{}, Version: 26, }, wantQueries: []string{ `SELECT cluster_ack_level, version FROM queue_metadata WHERE queue_type = 2`, }, }, { name: "scan failure", queueType: persistence.QueueType(2), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return errors.New("some random error") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotRow, err := db.SelectQueueMetadata(context.Background(), tc.queueType) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantRow, gotRow); diff != "" { t.Fatalf("Row mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestGetQueueSize(t *testing.T) { tests := []struct { name string queueType persistence.QueueType queryMockFn func(query *gocql.MockQuery) wantQueries []string wantCount int64 wantErr bool }{ { name: "success with shard map fully populated", queueType: persistence.DomainReplicationQueueType, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { m["count"] = int64(12) return nil }).Times(1) }, wantCount: int64(12), wantQueries: []string{ `SELECT COUNT(1) AS count FROM queue WHERE queue_type=1`, }, }, { name: "mapscan failed", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { return errors.New("mapscan failed") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotCount, err := db.GetQueueSize(context.Background(), tc.queueType) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if gotCount != tc.wantCount { t.Errorf("Got message ID = %v, want %v", gotCount, tc.wantCount) } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestSelectMessagesFrom(t *testing.T) { tests := []struct { name string queueType persistence.QueueType exclusiveBeginMessageID int64 maxRows int iter *fakeIter wantQueries []string wantRows []*nosqlplugin.QueueMessageRow wantErr bool }{ { name: "nil iter", queueType: persistence.DomainReplicationQueueType, exclusiveBeginMessageID: 20, maxRows: 10, iter: nil, wantErr: true, }, { name: "iter close failed", queueType: persistence.DomainReplicationQueueType, exclusiveBeginMessageID: 20, maxRows: 10, iter: &fakeIter{closeErr: errors.New("some random error")}, wantErr: true, }, { name: "success", queueType: persistence.DomainReplicationQueueType, exclusiveBeginMessageID: 20, maxRows: 10, iter: &fakeIter{ mapScanInputs: []map[string]interface{}{ { "message_id": int64(21), "message_payload": []byte("test-payload-1"), }, { "message_id": int64(22), "message_payload": []byte("test-payload-2"), }, }, }, wantRows: []*nosqlplugin.QueueMessageRow{ { ID: 21, Payload: []byte("test-payload-1"), }, { ID: 22, Payload: []byte("test-payload-2"), }, }, wantQueries: []string{ `SELECT message_id, message_payload FROM queue WHERE queue_type = 1 and message_id > 20 LIMIT 10`, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) if tc.iter != nil { query.EXPECT().Iter().Return(tc.iter).Times(1) } else { query.EXPECT().Iter().Return(nil).Times(1) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) db := NewCassandraDBFromSession(cfg, session, logger, nil, DbWithClient(client)) gotRows, err := db.SelectMessagesFrom(context.Background(), tc.queueType, tc.exclusiveBeginMessageID, tc.maxRows) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.wantRows, gotRows); diff != "" { t.Fatalf("rows mismatch (-want +got):\n%s", diff) } if !tc.iter.closed { t.Fatal("iterator not closed") } }) } } func TestSelectMessagesBetween(t *testing.T) { tests := []struct { name string request nosqlplugin.SelectMessagesBetweenRequest iter *fakeIter wantQueries []string wantResp *nosqlplugin.SelectMessagesBetweenResponse wantErr bool }{ { name: "nil iter", request: nosqlplugin.SelectMessagesBetweenRequest{ QueueType: persistence.DomainReplicationQueueType, ExclusiveBeginMessageID: 50, InclusiveEndMessageID: 60, PageSize: 5, NextPageToken: []byte("next page token"), }, iter: nil, wantErr: true, }, { name: "iter close failed", request: nosqlplugin.SelectMessagesBetweenRequest{ QueueType: persistence.DomainReplicationQueueType, ExclusiveBeginMessageID: 50, InclusiveEndMessageID: 60, PageSize: 5, NextPageToken: []byte("next page token"), }, iter: &fakeIter{closeErr: errors.New("some random error")}, wantErr: true, }, { name: "success", request: nosqlplugin.SelectMessagesBetweenRequest{ QueueType: persistence.DomainReplicationQueueType, ExclusiveBeginMessageID: 50, InclusiveEndMessageID: 60, PageSize: 5, NextPageToken: []byte("next page token"), }, iter: &fakeIter{ mapScanInputs: []map[string]interface{}{ { "message_id": int64(51), "message_payload": []byte("test-payload-1"), }, { "message_id": int64(52), "message_payload": []byte("test-payload-2"), }, { "message_id": int64(53), "message_payload": []byte("test-payload-3"), }, }, pageState: []byte("more pages"), }, wantResp: &nosqlplugin.SelectMessagesBetweenResponse{ Rows: []nosqlplugin.QueueMessageRow{ { ID: 51, Payload: []byte("test-payload-1"), }, { ID: 52, Payload: []byte("test-payload-2"), }, { ID: 53, Payload: []byte("test-payload-3"), }, }, NextPageToken: []byte("more pages"), }, wantQueries: []string{ `SELECT message_id, message_payload FROM queue WHERE queue_type = 1 and message_id > 50 and message_id <= 60`, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) query.EXPECT().PageSize(tc.request.PageSize).Return(query).Times(1) query.EXPECT().PageState(tc.request.NextPageToken).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) if tc.iter != nil { query.EXPECT().Iter().Return(tc.iter).Times(1) } else { query.EXPECT().Iter().Return(nil).Times(1) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) db := NewCassandraDBFromSession(cfg, session, logger, nil, DbWithClient(client)) gotResp, err := db.SelectMessagesBetween(context.Background(), tc.request) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.wantResp, gotResp); diff != "" { t.Fatalf("Response mismatch (-want +got):\n%s", diff) } if !tc.iter.closed { t.Fatal("iterator not closed") } }) } } func TestDeleteMessagesBefore(t *testing.T) { tests := []struct { name string queueType persistence.QueueType exclusiveBeginMessageID int64 queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "success", queueType: persistence.DomainReplicationQueueType, exclusiveBeginMessageID: 100, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantQueries: []string{ `DELETE FROM queue WHERE queue_type = 1 and message_id < 100`, }, }, { name: "failure", queueType: persistence.DomainReplicationQueueType, exclusiveBeginMessageID: 100, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("some random error")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) db := NewCassandraDBFromSession(cfg, session, logger, nil, DbWithClient(client)) err := db.DeleteMessagesBefore(context.Background(), tc.queueType, tc.exclusiveBeginMessageID) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestDeleteMessagesInRange(t *testing.T) { tests := []struct { name string queueType persistence.QueueType exclusiveBeginMessageID int64 inclusiveEndMsgID int64 queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "success", queueType: persistence.DomainReplicationQueueType, exclusiveBeginMessageID: 100, inclusiveEndMsgID: 200, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantQueries: []string{ `DELETE FROM queue WHERE queue_type = 1 and message_id > 100 and message_id <= 200`, }, }, { name: "failure", queueType: persistence.DomainReplicationQueueType, exclusiveBeginMessageID: 100, inclusiveEndMsgID: 200, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("some random error")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) db := NewCassandraDBFromSession(cfg, session, logger, nil, DbWithClient(client)) err := db.DeleteMessagesInRange(context.Background(), tc.queueType, tc.exclusiveBeginMessageID, tc.inclusiveEndMsgID) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestDeleteMessage(t *testing.T) { tests := []struct { name string queueType persistence.QueueType msgID int64 queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "success", queueType: persistence.DomainReplicationQueueType, msgID: 36, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantQueries: []string{ `DELETE FROM queue WHERE queue_type = 1 and message_id = 36`, }, }, { name: "failure", queueType: persistence.DomainReplicationQueueType, msgID: 36, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("some random error")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) db := NewCassandraDBFromSession(cfg, session, logger, nil, DbWithClient(client)) err := db.DeleteMessage(context.Background(), tc.queueType, tc.msgID) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestInsertQueueMetadata(t *testing.T) { tests := []struct { name string queueType persistence.QueueType version int64 queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "success", queueType: persistence.QueueType(2), version: 25, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().ScanCAS(gomock.Any()).Return(false, nil).Times(1) }, wantQueries: []string{ `INSERT INTO queue_metadata (queue_type, cluster_ack_level, version, created_time) VALUES(2, map[], 25, 2025-01-06T15:00:00Z) IF NOT EXISTS`, }, }, { name: "scan failure", queueType: persistence.QueueType(2), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().ScanCAS(gomock.Any()).Return(false, errors.New("some random error")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) row := nosqlplugin.QueueMetadataRow{ QueueType: tc.queueType, Version: tc.version, CurrentTimeStamp: FixedTime, } err := db.InsertQueueMetadata(context.Background(), row) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateQueueMetadataCas(t *testing.T) { tests := []struct { name string row nosqlplugin.QueueMetadataRow queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "successfully applied", row: nosqlplugin.QueueMetadataRow{ QueueType: persistence.QueueType(2), ClusterAckLevels: map[string]int64{"cluster1": 1000, "cluster2": 2000}, Version: 25, CurrentTimeStamp: FixedTime, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().ScanCAS(gomock.Any()).Return(true, nil).Times(1) }, wantQueries: []string{ `UPDATE queue_metadata SET cluster_ack_level = map[cluster1:1000 cluster2:2000], version = 25, last_updated_time = 2025-01-06T15:00:00Z WHERE queue_type = 2 IF version = 24`, }, }, { name: "could not apply", row: nosqlplugin.QueueMetadataRow{ QueueType: persistence.QueueType(2), ClusterAckLevels: map[string]int64{"cluster1": 1000, "cluster2": 2000}, Version: 25, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().ScanCAS(gomock.Any()).Return(false, nil).Times(1) }, wantErr: true, }, { name: "scancas failed", row: nosqlplugin.QueueMetadataRow{ QueueType: persistence.QueueType(2), ClusterAckLevels: map[string]int64{"cluster1": 1000, "cluster2": 2000}, Version: 25, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().ScanCAS(gomock.Any()).Return(false, errors.New("some random error")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.UpdateQueueMetadataCas(context.Background(), tc.row) if (err != nil) != tc.wantErr { t.Errorf("Got error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func queueMessageRow(id int64) *nosqlplugin.QueueMessageRow { return &nosqlplugin.QueueMessageRow{ QueueType: persistence.DomainReplicationQueueType, ID: id, Payload: []byte(fmt.Sprintf("test-payload-%d", id)), CurrentTimeStamp: FixedTime, } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/shard.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "fmt" "strings" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // InsertShard creates a new shard, return error is there is any. // Return ShardOperationConditionFailure if the condition doesn't meet func (db *CDB) InsertShard(ctx context.Context, row *nosqlplugin.ShardRow) error { cqlNowTimestamp := persistence.UnixNanoToDBTimestamp(row.CurrentTimestamp.UnixNano()) markerData, markerEncoding := persistence.FromDataBlob(row.PendingFailoverMarkers) transferPQS, transferPQSEncoding := persistence.FromDataBlob(row.TransferProcessingQueueStates) timerPQS, timerPQSEncoding := persistence.FromDataBlob(row.TimerProcessingQueueStates) query := db.session.Query(templateCreateShardQuery, row.ShardID, rowTypeShard, rowTypeShardDomainID, rowTypeShardWorkflowID, rowTypeShardRunID, defaultVisibilityTimestamp, rowTypeShardTaskID, row.ShardID, row.Owner, row.RangeID, row.StolenSinceRenew, cqlNowTimestamp, row.ReplicationAckLevel, row.TransferAckLevel, row.TimerAckLevel, row.ClusterTransferAckLevel, row.ClusterTimerAckLevel, transferPQS, transferPQSEncoding, timerPQS, timerPQSEncoding, row.DomainNotificationVersion, row.ClusterReplicationLevel, row.ReplicationDLQAckLevel, markerData, markerEncoding, row.Data, row.DataEncoding, row.RangeID, ).WithContext(ctx) previous := make(map[string]interface{}) applied, err := query.MapScanCAS(previous) if err != nil { return err } if !applied { return convertToConflictedShardRow(previous) } return nil } func convertToConflictedShardRow(previous map[string]interface{}) error { rangeID := previous["range_id"].(int64) var columns []string for k, v := range previous { columns = append(columns, fmt.Sprintf("%s=%v", k, v)) } return &nosqlplugin.ShardOperationConditionFailure{ RangeID: rangeID, Details: strings.Join(columns, ","), } } // SelectShard gets a shard func (db *CDB) SelectShard(ctx context.Context, shardID int, currentClusterName string) (int64, *nosqlplugin.ShardRow, error) { query := db.session.Query(templateGetShardQuery, shardID, rowTypeShard, rowTypeShardDomainID, rowTypeShardWorkflowID, rowTypeShardRunID, defaultVisibilityTimestamp, rowTypeShardTaskID, ).WithContext(ctx) result := make(map[string]interface{}) if err := query.MapScan(result); err != nil { return 0, nil, err } rangeID := result["range_id"].(int64) shard := result["shard"].(map[string]interface{}) shardInfoRangeID := shard["range_id"].(int64) info := convertToShardInfo(currentClusterName, shardInfoRangeID, shard) data, _ := result["data"].([]byte) dataEncoding, _ := result["data_encoding"].(string) shardRow := &nosqlplugin.ShardRow{ InternalShardInfo: info, Data: data, DataEncoding: dataEncoding, } return rangeID, shardRow, nil } func convertToShardInfo( currentCluster string, rangeID int64, shard map[string]interface{}, ) *persistence.InternalShardInfo { var pendingFailoverMarkersRawData []byte var pendingFailoverMarkersEncoding string var transferProcessingQueueStatesRawData []byte var transferProcessingQueueStatesEncoding string var timerProcessingQueueStatesRawData []byte var timerProcessingQueueStatesEncoding string info := &persistence.InternalShardInfo{} info.RangeID = rangeID for k, v := range shard { switch k { case "shard_id": info.ShardID = v.(int) case "owner": info.Owner = v.(string) case "stolen_since_renew": info.StolenSinceRenew = v.(int) case "updated_at": info.UpdatedAt = v.(time.Time) case "replication_ack_level": info.ReplicationAckLevel = v.(int64) case "transfer_ack_level": info.TransferAckLevel = v.(int64) case "timer_ack_level": info.TimerAckLevel = v.(time.Time) case "cluster_transfer_ack_level": info.ClusterTransferAckLevel = v.(map[string]int64) case "cluster_timer_ack_level": info.ClusterTimerAckLevel = v.(map[string]time.Time) case "transfer_processing_queue_states": transferProcessingQueueStatesRawData = v.([]byte) case "transfer_processing_queue_states_encoding": transferProcessingQueueStatesEncoding = v.(string) case "timer_processing_queue_states": timerProcessingQueueStatesRawData = v.([]byte) case "timer_processing_queue_states_encoding": timerProcessingQueueStatesEncoding = v.(string) case "domain_notification_version": info.DomainNotificationVersion = v.(int64) case "cluster_replication_level": info.ClusterReplicationLevel = v.(map[string]int64) case "replication_dlq_ack_level": info.ReplicationDLQAckLevel = v.(map[string]int64) case "pending_failover_markers": pendingFailoverMarkersRawData = v.([]byte) case "pending_failover_markers_encoding": pendingFailoverMarkersEncoding = v.(string) } } if info.ClusterTransferAckLevel == nil { info.ClusterTransferAckLevel = map[string]int64{ currentCluster: info.TransferAckLevel, } } if info.ClusterTimerAckLevel == nil { info.ClusterTimerAckLevel = map[string]time.Time{ currentCluster: info.TimerAckLevel, } } if info.ClusterReplicationLevel == nil { info.ClusterReplicationLevel = make(map[string]int64) } if info.ReplicationDLQAckLevel == nil { info.ReplicationDLQAckLevel = make(map[string]int64) } info.PendingFailoverMarkers = persistence.NewDataBlob( pendingFailoverMarkersRawData, constants.EncodingType(pendingFailoverMarkersEncoding), ) info.TransferProcessingQueueStates = persistence.NewDataBlob( transferProcessingQueueStatesRawData, constants.EncodingType(transferProcessingQueueStatesEncoding), ) info.TimerProcessingQueueStates = persistence.NewDataBlob( timerProcessingQueueStatesRawData, constants.EncodingType(timerProcessingQueueStatesEncoding), ) return info } // UpdateRangeID updates the rangeID, return error is there is any // Return ShardOperationConditionFailure if the condition doesn't meet func (db *CDB) UpdateRangeID(ctx context.Context, shardID int, rangeID int64, previousRangeID int64) error { query := db.session.Query(templateUpdateRangeIDQuery, rangeID, shardID, rowTypeShard, rowTypeShardDomainID, rowTypeShardWorkflowID, rowTypeShardRunID, defaultVisibilityTimestamp, rowTypeShardTaskID, previousRangeID, ).WithContext(ctx) previous := make(map[string]interface{}) applied, err := query.MapScanCAS(previous) if err != nil { return err } if !applied { return convertToConflictedShardRow(previous) } return nil } // UpdateShard updates a shard, return error is there is any. // Return ShardOperationConditionFailure if the condition doesn't meet func (db *CDB) UpdateShard(ctx context.Context, row *nosqlplugin.ShardRow, previousRangeID int64) error { cqlNowTimestamp := persistence.UnixNanoToDBTimestamp(row.CurrentTimestamp.UnixNano()) markerData, markerEncoding := persistence.FromDataBlob(row.PendingFailoverMarkers) transferPQS, transferPQSEncoding := persistence.FromDataBlob(row.TransferProcessingQueueStates) timerPQS, timerPQSEncoding := persistence.FromDataBlob(row.TimerProcessingQueueStates) query := db.session.Query(templateUpdateShardQuery, row.ShardID, row.Owner, row.RangeID, row.StolenSinceRenew, cqlNowTimestamp, row.ReplicationAckLevel, row.TransferAckLevel, row.TimerAckLevel, row.ClusterTransferAckLevel, row.ClusterTimerAckLevel, transferPQS, transferPQSEncoding, timerPQS, timerPQSEncoding, row.DomainNotificationVersion, row.ClusterReplicationLevel, row.ReplicationDLQAckLevel, markerData, markerEncoding, row.Data, row.DataEncoding, row.RangeID, row.ShardID, rowTypeShard, rowTypeShardDomainID, rowTypeShardWorkflowID, rowTypeShardRunID, defaultVisibilityTimestamp, rowTypeShardTaskID, previousRangeID, ).WithContext(ctx) previous := make(map[string]interface{}) applied, err := query.MapScanCAS(previous) if err != nil { return err } if !applied { return convertToConflictedShardRow(previous) } return nil } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/shard_cql.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra const ( templateShardType = `{` + `shard_id: ?, ` + `owner: ?, ` + `range_id: ?, ` + `stolen_since_renew: ?, ` + `updated_at: ?, ` + `replication_ack_level: ?, ` + `transfer_ack_level: ?, ` + `timer_ack_level: ?, ` + `cluster_transfer_ack_level: ?, ` + `cluster_timer_ack_level: ?, ` + `transfer_processing_queue_states: ?, ` + `transfer_processing_queue_states_encoding: ?, ` + `timer_processing_queue_states: ?, ` + `timer_processing_queue_states_encoding: ?, ` + `domain_notification_version: ?, ` + `cluster_replication_level: ?, ` + `replication_dlq_ack_level: ?, ` + `pending_failover_markers: ?, ` + `pending_failover_markers_encoding: ? ` + `}` templateCreateShardQuery = `INSERT INTO executions (` + `shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id, shard, data, data_encoding, range_id) ` + `VALUES(?, ?, ?, ?, ?, ?, ?, ` + templateShardType + `, ?, ?, ?) IF NOT EXISTS` templateGetShardQuery = `SELECT shard, data, data_encoding, range_id ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ?` templateUpdateShardQuery = `UPDATE executions ` + `SET shard = ` + templateShardType + `, data = ?, data_encoding = ?, range_id = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` + `IF range_id = ?` templateUpdateRangeIDQuery = `UPDATE executions ` + `SET range_id = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` + `IF range_id = ?` ) ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/shard_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "errors" "testing" "time" "github.com/google/go-cmp/cmp" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/testdata" ) func TestInsertShard(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-02T18:00:00Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string row *nosqlplugin.ShardRow queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "successfully applied", row: testdata.NewShardRow(ts), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return true, nil }).Times(1) }, wantQueries: []string{ `INSERT INTO executions (` + `shard_id, type, domain_id, workflow_id, run_id, ` + `visibility_ts, task_id, ` + `shard, data, data_encoding, ` + `range_id` + `) ` + `VALUES(` + `15, 0, 10000000-1000-f000-f000-000000000000, 20000000-1000-f000-f000-000000000000, 30000000-1000-f000-f000-000000000000, ` + `946684800000, -11, ` + `{shard_id: 15, owner: owner, range_id: 1000, stolen_since_renew: 0, updated_at: 1712080800000, replication_ack_level: 2000, transfer_ack_level: 3000, timer_ack_level: 2024-04-02T17:00:00Z, cluster_transfer_ack_level: map[cluster2:4000], cluster_timer_ack_level: map[cluster2:2024-04-02 16:00:00 +0000 UTC], transfer_processing_queue_states: [116 114 97 110 115 102 101 114 113 117 101 117 101], transfer_processing_queue_states_encoding: thriftrw, timer_processing_queue_states: [116 105 109 101 114 113 117 101 117 101], timer_processing_queue_states_encoding: thriftrw, domain_notification_version: 3, cluster_replication_level: map[cluster2:5000], replication_dlq_ack_level: map[cluster2:10], pending_failover_markers: [102 97 105 108 111 118 101 114 109 97 114 107 101 114 115], pending_failover_markers_encoding: thriftrw }, ` + `[115 104 97 114 100 100 97 116 97], thriftrw, ` + `1000` + `) IF NOT EXISTS`, }, }, { name: "not applied", row: testdata.NewShardRow(ts), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { m["range_id"] = int64(1001) return false, nil }).Times(1) }, wantErr: true, }, { name: "mapscancas failed", row: testdata.NewShardRow(ts), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return false, errors.New("mapscancas failed") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.InsertShard(context.Background(), tc.row) if (err != nil) != tc.wantErr { t.Errorf("InsertShard() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestSelectShard(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-02T18:00:00Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string shardID int cluster string queryMockFn func(query *gocql.MockQuery) wantQueries []string wantRangeID int64 wantShardInfo *nosqlplugin.ShardRow wantErr bool }{ { name: "success with shard map fully populated", shardID: 15, cluster: "cluster1", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { m["range_id"] = int64(1000) m["shard"] = testdata.NewShardMap(ts) m["data"] = []byte("sharddata") m["data_encoding"] = "thriftrw" return nil }).Times(1) }, wantRangeID: int64(1000), wantShardInfo: &nosqlplugin.ShardRow{ InternalShardInfo: &persistence.InternalShardInfo{ ShardID: 15, Owner: "owner", RangeID: 1000, UpdatedAt: ts, ReplicationAckLevel: 2000, ReplicationDLQAckLevel: map[string]int64{"cluster2": 5}, TransferAckLevel: 3000, TimerAckLevel: ts.Add(-1 * time.Hour), ClusterTransferAckLevel: map[string]int64{"cluster1": 3000}, ClusterTimerAckLevel: map[string]time.Time{"cluster1": ts.Add(-1 * time.Hour)}, TransferProcessingQueueStates: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("transferqueue")}, TimerProcessingQueueStates: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("timerqueue")}, ClusterReplicationLevel: map[string]int64{"cluster2": 1500}, DomainNotificationVersion: 3, PendingFailoverMarkers: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("failovermarkers")}, }, Data: []byte("sharddata"), DataEncoding: "thriftrw", }, wantQueries: []string{ `SELECT shard, data, data_encoding, range_id FROM executions WHERE ` + `shard_id = 15 and type = 0 and ` + `domain_id = 10000000-1000-f000-f000-000000000000 and ` + `workflow_id = 20000000-1000-f000-f000-000000000000 and ` + `run_id = 30000000-1000-f000-f000-000000000000 and ` + `visibility_ts = 946684800000 and ` + `task_id = -11`, }, }, { name: "success with shard map missing some fields", shardID: 15, cluster: "cluster1", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { m["range_id"] = int64(1000) sm := testdata.NewShardMap(ts) // delete some fields and validate they are initialized properly delete(sm, "cluster_transfer_ack_level") delete(sm, "cluster_timer_ack_level") delete(sm, "cluster_replication_level") delete(sm, "replication_dlq_ack_level") m["shard"] = sm return nil }).Times(1) }, wantRangeID: int64(1000), wantShardInfo: &nosqlplugin.ShardRow{ InternalShardInfo: &persistence.InternalShardInfo{ ShardID: 15, Owner: "owner", RangeID: 1000, UpdatedAt: ts, ReplicationAckLevel: 2000, ReplicationDLQAckLevel: map[string]int64{}, // this was reset to empty map TransferAckLevel: 3000, TimerAckLevel: ts.Add(-1 * time.Hour), ClusterTransferAckLevel: map[string]int64{"cluster1": 3000}, ClusterTimerAckLevel: map[string]time.Time{"cluster1": ts.Add(-1 * time.Hour)}, TransferProcessingQueueStates: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("transferqueue")}, TimerProcessingQueueStates: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("timerqueue")}, ClusterReplicationLevel: map[string]int64{}, // this was reset to empty map DomainNotificationVersion: 3, PendingFailoverMarkers: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("failovermarkers")}, }, }, wantQueries: []string{ `SELECT shard, data, data_encoding, range_id FROM executions WHERE ` + `shard_id = 15 and type = 0 and ` + `domain_id = 10000000-1000-f000-f000-000000000000 and ` + `workflow_id = 20000000-1000-f000-f000-000000000000 and ` + `run_id = 30000000-1000-f000-f000-000000000000 and ` + `visibility_ts = 946684800000 and ` + `task_id = -11`, }, }, { name: "mapscan failed", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { return errors.New("mapscan failed") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotRangeID, gotShardInfo, err := db.SelectShard(context.Background(), tc.shardID, tc.cluster) if (err != nil) != tc.wantErr { t.Errorf("SelectShard() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if gotRangeID != tc.wantRangeID { t.Errorf("Got RangeID = %v, want %v", gotRangeID, tc.wantRangeID) } if diff := cmp.Diff(tc.wantShardInfo, gotShardInfo); diff != "" { t.Fatalf("ShardInfo mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateRangeID(t *testing.T) { tests := []struct { name string shardID int rangeID int64 prevRangeID int64 queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "successfully applied", shardID: 15, rangeID: 1000, prevRangeID: 999, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return true, nil }).Times(1) }, wantQueries: []string{ `UPDATE executions SET range_id = 1000 WHERE ` + `shard_id = 15 and ` + `type = 0 and ` + `domain_id = 10000000-1000-f000-f000-000000000000 and ` + `workflow_id = 20000000-1000-f000-f000-000000000000 and ` + `run_id = 30000000-1000-f000-f000-000000000000 and ` + `visibility_ts = 946684800000 and ` + `task_id = -11 ` + `IF range_id = 999`, }, }, { name: "not applied", shardID: 15, rangeID: 1000, prevRangeID: 999, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { m["range_id"] = int64(1001) return false, nil }).Times(1) }, wantErr: true, }, { name: "mapscancas failed", shardID: 15, rangeID: 1000, prevRangeID: 999, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return false, errors.New("mapscancas failed") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.UpdateRangeID(context.Background(), tc.shardID, tc.rangeID, tc.prevRangeID) if (err != nil) != tc.wantErr { t.Errorf("UpdateRangeID() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateShard(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-02T18:00:00Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string row *nosqlplugin.ShardRow prevRangeID int64 queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "successfully applied", row: testdata.NewShardRow(ts), prevRangeID: 988, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return true, nil }).Times(1) }, wantQueries: []string{ `UPDATE executions SET shard = {` + `shard_id: 15, ` + `owner: owner, ` + `range_id: 1000, ` + `stolen_since_renew: 0, ` + `updated_at: 1712080800000, ` + `replication_ack_level: 2000, ` + `transfer_ack_level: 3000, ` + `timer_ack_level: 2024-04-02T17:00:00Z, ` + `cluster_transfer_ack_level: map[cluster2:4000], ` + `cluster_timer_ack_level: map[cluster2:2024-04-02 16:00:00 +0000 UTC], ` + `transfer_processing_queue_states: [116 114 97 110 115 102 101 114 113 117 101 117 101], ` + `transfer_processing_queue_states_encoding: thriftrw, ` + `timer_processing_queue_states: [116 105 109 101 114 113 117 101 117 101], ` + `timer_processing_queue_states_encoding: thriftrw, ` + `domain_notification_version: 3, ` + `cluster_replication_level: map[cluster2:5000], ` + `replication_dlq_ack_level: map[cluster2:10], ` + `pending_failover_markers: [102 97 105 108 111 118 101 114 109 97 114 107 101 114 115], ` + `pending_failover_markers_encoding: thriftrw ` + `}, ` + `data = [115 104 97 114 100 100 97 116 97], ` + `data_encoding = thriftrw, ` + `range_id = 1000 ` + `WHERE ` + `shard_id = 15 and ` + `type = 0 and ` + `domain_id = 10000000-1000-f000-f000-000000000000 and ` + `workflow_id = 20000000-1000-f000-f000-000000000000 and ` + `run_id = 30000000-1000-f000-f000-000000000000 and ` + `visibility_ts = 946684800000 and ` + `task_id = -11 ` + `IF range_id = 988`, }, }, { name: "not applied", row: testdata.NewShardRow(ts), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { m["range_id"] = int64(1001) return false, nil }).Times(1) }, wantErr: true, }, { name: "mapscancas failed", row: testdata.NewShardRow(ts), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(m map[string]interface{}) (bool, error) { return false, errors.New("mapscancas failed") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.UpdateShard(context.Background(), tc.row, tc.prevRangeID) if (err != nil) != tc.wantErr { t.Errorf("UpdateShard() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } t.Log(session.queries[0]) if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/tasks.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "fmt" "strings" "time" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/types" ) const ( // Row types for table tasks rowTypeTask = iota rowTypeTaskList ) const ( taskListTaskID = -12345 initialRangeID = 1 // Id of the first range of a new task list ) // SelectTaskList returns a single tasklist row. // Return IsNotFoundError if the row doesn't exist func (db *CDB) SelectTaskList(ctx context.Context, filter *nosqlplugin.TaskListFilter) (*nosqlplugin.TaskListRow, error) { query := db.session.Query(templateGetTaskList, filter.DomainID, filter.TaskListName, filter.TaskListType, rowTypeTaskList, taskListTaskID, ).WithContext(ctx) var rangeID int64 var tlDB map[string]interface{} err := query.Scan(&rangeID, &tlDB) if err != nil { return nil, err } ackLevel := tlDB["ack_level"].(int64) taskListKind := tlDB["kind"].(int) lastUpdatedTime := tlDB["last_updated"].(time.Time) return &nosqlplugin.TaskListRow{ DomainID: filter.DomainID, TaskListName: filter.TaskListName, TaskListType: filter.TaskListType, TaskListKind: taskListKind, LastUpdatedTime: lastUpdatedTime, AckLevel: ackLevel, RangeID: rangeID, AdaptivePartitionConfig: toTaskListPartitionConfig(tlDB["adaptive_partition_config"]), }, nil } func toTaskListPartitionConfig(v interface{}) *persistence.TaskListPartitionConfig { if v == nil { return nil } partition := v.(map[string]interface{}) if len(partition) == 0 { return nil } version := partition["version"].(int64) numRead := partition["num_read_partitions"].(int) numWrite := partition["num_write_partitions"].(int) readPartitions := toTaskListPartitions(partition["read_partitions"]) writePartitions := toTaskListPartitions(partition["write_partitions"]) // If they're out of sync, go with the value of num_*_partitions. This is necessary only while support for // read_partitions and write_partitions rolls out if numRead != len(readPartitions) { readPartitions = createDefaultPartitions(numRead) } if numWrite != len(writePartitions) { writePartitions = createDefaultPartitions(numWrite) } return &persistence.TaskListPartitionConfig{ Version: version, ReadPartitions: readPartitions, WritePartitions: writePartitions, } } func createDefaultPartitions(num int) map[int]*persistence.TaskListPartition { partitions := make(map[int]*persistence.TaskListPartition, num) for i := 0; i < num; i++ { partitions[i] = &persistence.TaskListPartition{} } return partitions } func toTaskListPartitions(values any) map[int]*persistence.TaskListPartition { if values == nil { return nil } partitions, ok := values.(map[int]map[string]any) if !ok || len(partitions) == 0 { return nil } result := make(map[int]*persistence.TaskListPartition, len(partitions)) for id, p := range partitions { partition := toTaskListPartition(p) if partition != nil { result[id] = partition } } return result } func toTaskListPartition(partition map[string]any) *persistence.TaskListPartition { if len(partition) == 0 { return nil } isolationGroups := partition["isolation_groups"].([]string) return &persistence.TaskListPartition{ IsolationGroups: isolationGroups, } } func fromTaskListPartitionConfig(config *persistence.TaskListPartitionConfig) map[string]interface{} { if config == nil { return nil } return map[string]interface{}{ "version": config.Version, "num_read_partitions": len(config.ReadPartitions), "num_write_partitions": len(config.WritePartitions), "read_partitions": fromTaskListPartitions(config.ReadPartitions), "write_partitions": fromTaskListPartitions(config.WritePartitions), } } func fromTaskListPartitions(partitions map[int]*persistence.TaskListPartition) map[int]any { if len(partitions) == 0 { return nil } result := make(map[int]any, len(partitions)) for id, partition := range partitions { result[id] = fromTaskListPartition(partition) } return result } func fromTaskListPartition(partition *persistence.TaskListPartition) any { if partition == nil { return nil } return map[string]any{ "isolation_groups": partition.IsolationGroups, } } // InsertTaskList insert a single tasklist row // Return TaskOperationConditionFailure if the condition doesn't meet func (db *CDB) InsertTaskList(ctx context.Context, row *nosqlplugin.TaskListRow) error { timeStamp := row.CurrentTimeStamp query := db.session.Query(templateInsertTaskListQuery, row.DomainID, row.TaskListName, row.TaskListType, rowTypeTaskList, taskListTaskID, initialRangeID, row.DomainID, row.TaskListName, row.TaskListType, 0, row.TaskListKind, row.LastUpdatedTime, fromTaskListPartitionConfig(row.AdaptivePartitionConfig), timeStamp, ).WithContext(ctx) previous := make(map[string]interface{}) applied, err := query.MapScanCAS(previous) if err != nil { return err } return handleTaskListAppliedError(applied, previous) } // UpdateTaskList updates a single tasklist row // Return TaskOperationConditionFailure if the condition doesn't meet func (db *CDB) UpdateTaskList( ctx context.Context, row *nosqlplugin.TaskListRow, previousRangeID int64, ) error { timeStamp := row.CurrentTimeStamp query := db.session.Query(templateUpdateTaskListQuery, row.RangeID, row.DomainID, row.TaskListName, row.TaskListType, row.AckLevel, row.TaskListKind, row.LastUpdatedTime, fromTaskListPartitionConfig(row.AdaptivePartitionConfig), timeStamp, row.DomainID, row.TaskListName, row.TaskListType, rowTypeTaskList, taskListTaskID, previousRangeID, ).WithContext(ctx) previous := make(map[string]interface{}) applied, err := query.MapScanCAS(previous) if err != nil { return err } return handleTaskListAppliedError(applied, previous) } func handleTaskListAppliedError(applied bool, previous map[string]interface{}) error { if !applied { // NOTE: Cassandra only returns the conflicted columns in this results rangeID := previous["range_id"].(int64) var columns []string for k, v := range previous { columns = append(columns, fmt.Sprintf("%s=%v", k, v)) } return &nosqlplugin.TaskOperationConditionFailure{ RangeID: rangeID, Details: strings.Join(columns, ","), } } return nil } // UpdateTaskList updates a single tasklist row, and set an TTL on the record // Return TaskOperationConditionFailure if the condition doesn't meet // Ignore TTL if it's not supported, which becomes exactly the same as UpdateTaskList, but ListTaskList must be // implemented for TaskListScavenger func (db *CDB) UpdateTaskListWithTTL( ctx context.Context, ttlSeconds int64, row *nosqlplugin.TaskListRow, previousRangeID int64, ) error { timeStamp := row.CurrentTimeStamp batch := db.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) // part 1 is used to set TTL on primary key as UPDATE can't set TTL for primary key batch.Query(templateUpdateTaskListQueryWithTTLPart1, row.DomainID, row.TaskListName, row.TaskListType, rowTypeTaskList, taskListTaskID, timeStamp, ttlSeconds, ) // part 2 is for CAS and setting TTL for the rest of the columns batch.Query(templateUpdateTaskListQueryWithTTLPart2, ttlSeconds, row.RangeID, row.DomainID, row.TaskListName, row.TaskListType, row.AckLevel, row.TaskListKind, timeStamp, fromTaskListPartitionConfig(row.AdaptivePartitionConfig), timeStamp, row.DomainID, row.TaskListName, row.TaskListType, rowTypeTaskList, taskListTaskID, previousRangeID, ) previous := make(map[string]interface{}) applied, _, err := db.session.MapExecuteBatchCAS(batch, previous) if err != nil { return err } return handleTaskListAppliedError(applied, previous) } // ListTaskList returns all tasklists. // Noop if TTL is already implemented in other methods func (db *CDB) ListTaskList(ctx context.Context, pageSize int, nextPageToken []byte) (*nosqlplugin.ListTaskListResult, error) { return nil, &types.InternalServiceError{ Message: "unsupported operation", } } // DeleteTaskList deletes a single tasklist row // Return TaskOperationConditionFailure if the condition doesn't meet func (db *CDB) DeleteTaskList(ctx context.Context, filter *nosqlplugin.TaskListFilter, previousRangeID int64) error { query := db.session.Query(templateDeleteTaskListQuery, filter.DomainID, filter.TaskListName, filter.TaskListType, rowTypeTaskList, taskListTaskID, previousRangeID, ).WithContext(ctx).Consistency(cassandraAllConslevel) previous := make(map[string]interface{}) applied, err := query.MapScanCAS(previous) if err != nil { if !db.isCassandraConsistencyError(err) { return err } db.logger.Warn("unable to complete the delete operation due to consistency issue", tag.Error(err)) applied, err = query.Consistency(cassandraDefaultConsLevel).MapScanCAS(previous) if err != nil { return err } } return handleTaskListAppliedError(applied, previous) } // InsertTasks inserts a batch of tasks // Return IsConditionFailedError if the condition doesn't meet, and also the previous tasklist row func (db *CDB) InsertTasks( ctx context.Context, tasksToInsert []*nosqlplugin.TaskRowForInsert, tasklistCondition *nosqlplugin.TaskListRow, ) error { batch := db.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) domainID := tasklistCondition.DomainID taskListName := tasklistCondition.TaskListName taskListType := tasklistCondition.TaskListType timeStamp := tasklistCondition.CurrentTimeStamp for _, task := range tasksToInsert { scheduleID := task.ScheduledID ttl := int64(task.TTLSeconds) if ttl <= 0 { batch.Query(templateCreateTaskQuery, domainID, taskListName, taskListType, rowTypeTask, task.TaskID, domainID, task.WorkflowID, task.RunID, scheduleID, task.CreatedTime, task.PartitionConfig, timeStamp, ) } else { if ttl > maxCassandraTTL { ttl = maxCassandraTTL } batch.Query(templateCreateTaskWithTTLQuery, domainID, taskListName, taskListType, rowTypeTask, task.TaskID, domainID, task.WorkflowID, task.RunID, scheduleID, task.CreatedTime, task.PartitionConfig, timeStamp, ttl) } } // The following query is used to ensure that range_id didn't change batch.Query(templateUpdateTaskListRangeIDQuery, tasklistCondition.RangeID, timeStamp, domainID, taskListName, taskListType, rowTypeTaskList, taskListTaskID, tasklistCondition.RangeID, ) previous := make(map[string]interface{}) applied, _, err := db.session.MapExecuteBatchCAS(batch, previous) if err != nil { return err } return handleTaskListAppliedError(applied, previous) } // GetTasksCount returns number of tasks from a tasklist func (db *CDB) GetTasksCount(ctx context.Context, filter *nosqlplugin.TasksFilter) (int64, error) { query := db.session.Query(templateGetTasksCountQuery, filter.DomainID, filter.TaskListName, filter.TaskListType, rowTypeTask, filter.MinTaskID, ).WithContext(ctx) result := make(map[string]interface{}) if err := query.MapScan(result); err != nil { return 0, err } queueSize := result["count"].(int64) return queueSize, nil } // SelectTasks return tasks that associated to a tasklist func (db *CDB) SelectTasks(ctx context.Context, filter *nosqlplugin.TasksFilter) ([]*nosqlplugin.TaskRow, error) { // Reading tasklist tasks need to be quorum level consistent, otherwise we could lose tasks query := db.session.Query(templateGetTasksQuery, filter.DomainID, filter.TaskListName, filter.TaskListType, rowTypeTask, filter.MinTaskID, filter.MaxTaskID, ).PageSize(filter.BatchSize).WithContext(ctx) iter := query.Iter() if iter == nil { return nil, fmt.Errorf("selectTasks operation failed. Not able to create query iterator") } var response []*nosqlplugin.TaskRow task := make(map[string]interface{}) PopulateTasks: for iter.MapScan(task) { taskID, ok := task["task_id"] if !ok { // no tasks, but static column record returned continue } // TODO: no usage of ttl // Extract the TTL value ttlValue, ttlExists := task["ttl"] // Check if TTL is null or an integer var ttl *int if ttlExists && ttlValue != nil { if ttlInt, ok := ttlValue.(int); ok { ttl = &ttlInt // TTL is an integer } } t := createTaskInfo(task["task"].(map[string]interface{})) t.TaskID = taskID.(int64) // TODO: any computations on such level is not recommended, move to higher level if ttl != nil { t.Expiry = time.Now().Add(time.Duration(*ttl) * time.Second) } response = append(response, t) if len(response) == filter.BatchSize { break PopulateTasks } task = make(map[string]interface{}) // Reinitialize map as initialized fails on unmarshalling } if err := iter.Close(); err != nil { return nil, err } return response, nil } func createTaskInfo( result map[string]interface{}, ) *nosqlplugin.TaskRow { info := &nosqlplugin.TaskRow{} for k, v := range result { switch k { case "domain_id": info.DomainID = v.(gocql.UUID).String() case "workflow_id": info.WorkflowID = v.(string) case "run_id": info.RunID = v.(gocql.UUID).String() case "schedule_id": info.ScheduledID = v.(int64) case "created_time": info.CreatedTime = v.(time.Time) case "partition_config": info.PartitionConfig = v.(map[string]string) } } return info } // DeleteTask delete a batch tasks that taskIDs less than the row // If TTL is not implemented, then should also return the number of rows deleted, otherwise persistence.UnknownNumRowsAffected // NOTE: This API ignores the `BatchSize` request parameter i.e. either all tasks leq the task_id will be deleted or an error will // be returned to the caller, because rowsDeleted is not supported by Cassandra func (db *CDB) RangeDeleteTasks(ctx context.Context, filter *nosqlplugin.TasksFilter) (rowsDeleted int, err error) { query := db.session.Query(templateCompleteTasksLessThanQuery, filter.DomainID, filter.TaskListName, filter.TaskListType, rowTypeTask, filter.MinTaskID, filter.MaxTaskID, ).WithContext(ctx) return persistence.UnknownNumRowsAffected, db.executeWithConsistencyAll(query) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/tasks_cql.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra const ( templateTaskListType = `{` + `domain_id: ?, ` + `name: ?, ` + `type: ?, ` + `ack_level: ?, ` + `kind: ?, ` + `last_updated: ?, ` + `adaptive_partition_config: ? ` + `}` templateTaskType = `{` + `domain_id: ?, ` + `workflow_id: ?, ` + `run_id: ?, ` + `schedule_id: ?,` + `created_time: ?, ` + `partition_config: ? ` + `}` templateCreateTaskQuery = `INSERT INTO tasks (` + `domain_id, task_list_name, task_list_type, type, task_id, task, created_time) ` + `VALUES(?, ?, ?, ?, ?, ` + templateTaskType + `, ?)` templateCreateTaskWithTTLQuery = `INSERT INTO tasks (` + `domain_id, task_list_name, task_list_type, type, task_id, task, created_time) ` + `VALUES(?, ?, ?, ?, ?, ` + templateTaskType + `, ?) USING TTL ?` templateGetTasksQuery = `SELECT task_id, task, TTL(task) AS ttl ` + `FROM tasks ` + `WHERE domain_id = ? ` + `and task_list_name = ? ` + `and task_list_type = ? ` + `and type = ? ` + `and task_id > ? ` + `and task_id <= ?` templateGetTasksCountQuery = `SELECT count(1) as count ` + `FROM tasks ` + `WHERE domain_id = ? ` + `and task_list_name = ? ` + `and task_list_type = ? ` + `and type = ? ` + `and task_id > ? ` templateCompleteTasksLessThanQuery = `DELETE FROM tasks ` + `WHERE domain_id = ? ` + `AND task_list_name = ? ` + `AND task_list_type = ? ` + `AND type = ? ` + `AND task_id > ? ` + `AND task_id <= ? ` templateGetTaskList = `SELECT ` + `range_id, ` + `task_list ` + `FROM tasks ` + `WHERE domain_id = ? ` + `and task_list_name = ? ` + `and task_list_type = ? ` + `and type = ? ` + `and task_id = ?` templateInsertTaskListQuery = `INSERT INTO tasks (` + `domain_id, ` + `task_list_name, ` + `task_list_type, ` + `type, ` + `task_id, ` + `range_id, ` + `task_list, ` + `created_time ` + `) VALUES (?, ?, ?, ?, ?, ?, ` + templateTaskListType + `, ?) IF NOT EXISTS` templateUpdateTaskListQuery = `UPDATE tasks SET ` + `range_id = ?, ` + `task_list = ` + templateTaskListType + " , last_updated_time = ? " + `WHERE domain_id = ? ` + `and task_list_name = ? ` + `and task_list_type = ? ` + `and type = ? ` + `and task_id = ? ` + `IF range_id = ?` templateUpdateTaskListQueryWithTTLPart1 = ` INSERT INTO tasks (` + `domain_id, ` + `task_list_name, ` + `task_list_type, ` + `type, ` + `task_id, ` + `created_time ` + `) VALUES (?, ?, ?, ?, ?, ?) USING TTL ?` templateUpdateTaskListQueryWithTTLPart2 = `UPDATE tasks USING TTL ? SET ` + `range_id = ?, ` + `task_list = ` + templateTaskListType + " , last_updated_time = ? " + `WHERE domain_id = ? ` + `and task_list_name = ? ` + `and task_list_type = ? ` + `and type = ? ` + `and task_id = ? ` + `IF range_id = ?` templateUpdateTaskListRangeIDQuery = `UPDATE tasks SET ` + `range_id = ?, ` + `last_updated_time = ? ` + `WHERE domain_id = ? ` + `and task_list_name = ? ` + `and task_list_type = ? ` + `and type = ? ` + `and task_id = ? ` + `IF range_id = ?` templateDeleteTaskListQuery = `DELETE FROM tasks ` + `WHERE domain_id = ? ` + `AND task_list_name = ? ` + `AND task_list_type = ? ` + `AND type = ? ` + `AND task_id = ? ` + `IF range_id = ?` ) ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/tasks_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "errors" "testing" "time" "github.com/google/go-cmp/cmp" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/types" ) func TestSelectTaskList(t *testing.T) { now := time.Now() tests := []struct { name string filter *nosqlplugin.TaskListFilter queryMockFn func(query *gocql.MockQuery) wantRow *nosqlplugin.TaskListRow wantQueries []string wantErr bool }{ { name: "success", filter: &nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { rangeID := args[0].(*int64) *rangeID = 25 tlDB := args[1].(*map[string]interface{}) *tlDB = make(map[string]interface{}) (*tlDB)["ack_level"] = int64(1000) (*tlDB)["kind"] = 2 (*tlDB)["last_updated"] = now (*tlDB)["adaptive_partition_config"] = map[string]interface{}{ "version": int64(0), "num_read_partitions": int(1), "num_write_partitions": int(1), "read_partitions": map[int]map[string]any{ 0: { "isolation_groups": []string{"foo"}, }, }, "write_partitions": map[int]map[string]any{ 0: { "isolation_groups": []string{"bar"}, }, }, } return nil }).Times(1) }, wantRow: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, LastUpdatedTime: now, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 0, ReadPartitions: map[int]*persistence.TaskListPartition{ 0: { IsolationGroups: []string{"foo"}, }, }, WritePartitions: map[int]*persistence.TaskListPartition{ 0: { IsolationGroups: []string{"bar"}, }, }, }, }, wantQueries: []string{ `SELECT range_id, task_list FROM tasks WHERE domain_id = domain1 and task_list_name = tasklist1 and task_list_type = 1 and type = 1 and task_id = -12345`, }, }, { name: "success - partition numbers only", filter: &nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { rangeID := args[0].(*int64) *rangeID = 25 tlDB := args[1].(*map[string]interface{}) *tlDB = make(map[string]interface{}) (*tlDB)["ack_level"] = int64(1000) (*tlDB)["kind"] = 2 (*tlDB)["last_updated"] = now (*tlDB)["adaptive_partition_config"] = map[string]interface{}{ "version": int64(0), "num_read_partitions": int(1), "num_write_partitions": int(1), } return nil }).Times(1) }, wantRow: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, LastUpdatedTime: now, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 0, ReadPartitions: map[int]*persistence.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*persistence.TaskListPartition{ 0: {}, }, }, }, wantQueries: []string{ `SELECT range_id, task_list FROM tasks WHERE domain_id = domain1 and task_list_name = tasklist1 and task_list_type = 1 and type = 1 and task_id = -12345`, }, }, { name: "scan failure", filter: &nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Scan(gomock.Any()).DoAndReturn(func(args ...interface{}) error { return errors.New("some random error") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotRow, err := db.SelectTaskList(context.Background(), tc.filter) if (err != nil) != tc.wantErr { t.Errorf("SelectTaskList() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantRow, gotRow); diff != "" { t.Fatalf("Row mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestInsertTaskList(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-01T22:08:41Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string row *nosqlplugin.TaskListRow queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "successfully applied - nil partition_config", row: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, LastUpdatedTime: ts, CurrentTimeStamp: ts, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { return true, nil }).Times(1) }, wantQueries: []string{ `INSERT INTO tasks (domain_id, task_list_name, task_list_type, type, task_id, range_id, task_list, created_time ) ` + `VALUES (domain1, tasklist1, 1, 1, -12345, 1, ` + `{domain_id: domain1, name: tasklist1, type: 1, ack_level: 0, kind: 2, last_updated: 2024-04-01T22:08:41Z, adaptive_partition_config: map[] }` + `, 2024-04-01T22:08:41Z) IF NOT EXISTS`, }, }, { name: "successfully applied - non-nil partition_config", row: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, LastUpdatedTime: ts, CurrentTimeStamp: ts, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*persistence.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*persistence.TaskListPartition{ 0: {}, }, }, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { return true, nil }).Times(1) }, wantQueries: []string{ `INSERT INTO tasks (domain_id, task_list_name, task_list_type, type, task_id, range_id, task_list, created_time ) ` + `VALUES (domain1, tasklist1, 1, 1, -12345, 1, ` + `{domain_id: domain1, name: tasklist1, type: 1, ack_level: 0, kind: 2, last_updated: 2024-04-01T22:08:41Z, ` + `adaptive_partition_config: map[num_read_partitions:1 num_write_partitions:1 read_partitions:map[0:map[isolation_groups:[]]] version:1 write_partitions:map[0:map[isolation_groups:[]]]] }` + `, 2024-04-01T22:08:41Z) IF NOT EXISTS`, }, }, { name: "not applied", row: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, LastUpdatedTime: ts, CurrentTimeStamp: ts, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { prev["range_id"] = int64(26) return false, nil }).Times(1) }, wantErr: true, }, { name: "mapscan failed", row: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, LastUpdatedTime: ts, CurrentTimeStamp: ts, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { return false, errors.New("some random error") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.InsertTaskList(context.Background(), tc.row) if (err != nil) != tc.wantErr { t.Errorf("InsertTaskList() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateTaskList(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-01T22:08:41Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string row *nosqlplugin.TaskListRow prevRangeID int64 queryMockFn func(query *gocql.MockQuery) wantQueries []string wantErr bool }{ { name: "successfully applied", prevRangeID: 25, row: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, LastUpdatedTime: ts, CurrentTimeStamp: ts, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { return true, nil }).Times(1) }, wantQueries: []string{ `UPDATE tasks SET range_id = 25, task_list = {domain_id: domain1, name: tasklist1, type: 1, ack_level: 1000, kind: 2, last_updated: 2024-04-01T22:08:41Z, adaptive_partition_config: map[] } , last_updated_time = 2024-04-01T22:08:41Z WHERE domain_id = domain1 and task_list_name = tasklist1 and task_list_type = 1 and type = 1 and task_id = -12345 IF range_id = 25`, }, }, { name: "not applied", row: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, LastUpdatedTime: ts, CurrentTimeStamp: ts, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { prev["range_id"] = int64(26) return false, nil }).Times(1) }, wantErr: true, }, { name: "mapscan failed", row: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, LastUpdatedTime: ts, CurrentTimeStamp: ts, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { return false, errors.New("some random error") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.UpdateTaskList(context.Background(), tc.row, tc.prevRangeID) if (err != nil) != tc.wantErr { t.Errorf("UpdateTaskList() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateTaskListWithTTL(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-01T22:08:41Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string row *nosqlplugin.TaskListRow ttlSeconds int64 prevRangeID int64 mapExecuteBatchCASApplied bool mapExecuteBatchCASErr error mapExecuteBatchCASPrev map[string]any wantQueries []string wantErr bool }{ { name: "successfully applied", ttlSeconds: 180, prevRangeID: 25, row: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, LastUpdatedTime: ts, CurrentTimeStamp: ts, }, mapExecuteBatchCASApplied: true, wantQueries: []string{ ` INSERT INTO tasks (domain_id, task_list_name, task_list_type, type, task_id, created_time ) VALUES (domain1, tasklist1, 1, 1, -12345, 2024-04-01T22:08:41Z) USING TTL 180`, `UPDATE tasks USING TTL 180 SET range_id = 25, task_list = {domain_id: domain1, name: tasklist1, type: 1, ack_level: 1000, kind: 2, last_updated: 2024-04-01T22:08:41Z, adaptive_partition_config: map[] } , last_updated_time = 2024-04-01T22:08:41Z WHERE domain_id = domain1 and task_list_name = tasklist1 and task_list_type = 1 and type = 1 and task_id = -12345 IF range_id = 25`, }, }, { name: "not applied", row: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, }, mapExecuteBatchCASApplied: false, mapExecuteBatchCASPrev: map[string]any{ "range_id": int64(26), }, wantErr: true, }, { name: "mapscan failed", row: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, TaskListKind: 2, AckLevel: 1000, RangeID: 25, }, mapExecuteBatchCASErr: errors.New("cas failure"), wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) session := &fakeSession{ mapExecuteBatchCASApplied: tc.mapExecuteBatchCASApplied, mapExecuteBatchCASErr: tc.mapExecuteBatchCASErr, mapExecuteBatchCASPrev: tc.mapExecuteBatchCASPrev, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.UpdateTaskListWithTTL(context.Background(), tc.ttlSeconds, tc.row, tc.prevRangeID) if (err != nil) != tc.wantErr { t.Errorf("UpdateTaskListWithTTL() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.batches[0].queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestListTaskList(t *testing.T) { ctrl := gomock.NewController(t) client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} session := &fakeSession{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) _, err := db.ListTaskList(context.Background(), 10, nil) var want *types.InternalServiceError if !errors.As(err, &want) { t.Fatalf("expected InternalServiceError, got %v", err) } if want.Message != "unsupported operation" { t.Fatalf("Unexpected message: %v", want.Message) } } func TestDeleteTaskList(t *testing.T) { tests := []struct { name string filter *nosqlplugin.TaskListFilter prevRangeID int64 queryMockFn func(query *gocql.MockQuery) clientMockFn func(client *gocql.MockClient) wantQueries []string wantErr bool }{ { name: "successfully applied", prevRangeID: 35, filter: &nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Consistency(cassandraAllConslevel).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { return true, nil }).Times(1) }, wantQueries: []string{ `DELETE FROM tasks WHERE domain_id = domain1 AND task_list_name = tasklist1 AND task_list_type = 1 AND type = 1 AND task_id = -12345 IF range_id = 35`, }, }, { name: "not applied", filter: &nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Consistency(cassandraAllConslevel).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { prev["range_id"] = int64(26) return false, nil }).Times(1) }, wantErr: true, }, { name: "mapscan failed with all consistency, retry with default consistency", filter: &nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) firstConsCall := query.EXPECT().Consistency(cassandraAllConslevel).Return(query).Times(1) firstMapScanCall := query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { return false, errors.New("some random error") }).Times(1) // expect retry with default consistency level query.EXPECT().Consistency(cassandraDefaultConsLevel).Return(query).Times(1).After(firstConsCall) query.EXPECT().MapScanCAS(gomock.Any()).Return(true, nil).Times(1).After(firstMapScanCall) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsCassandraConsistencyError(gomock.Any()).Return(true).Times(1) }, wantQueries: []string{ `DELETE FROM tasks WHERE domain_id = domain1 AND task_list_name = tasklist1 AND task_list_type = 1 AND type = 1 AND task_id = -12345 IF range_id = 0`, }, }, { name: "mapscan failed with all consistency, retry with default consistency also failed", filter: &nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) firstConsCall := query.EXPECT().Consistency(cassandraAllConslevel).Return(query).Times(1) firstMapScanCall := query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { return false, errors.New("failed to delete") }).Times(1) // expect retry with default consistency level query.EXPECT().Consistency(cassandraDefaultConsLevel).Return(query).Times(1).After(firstConsCall) query.EXPECT().MapScanCAS(gomock.Any()).Return(true, errors.New("failed to deleta again")).Times(1).After(firstMapScanCall) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsCassandraConsistencyError(gomock.Any()).Return(true).Times(1) }, wantErr: true, }, { name: "mapscan failed with non-consistency error", filter: &nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Consistency(cassandraAllConslevel).Return(query).Times(1) query.EXPECT().MapScanCAS(gomock.Any()).DoAndReturn(func(prev map[string]interface{}) (bool, error) { return false, errors.New("failed to delete") }).Times(1) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsCassandraConsistencyError(gomock.Any()).Return(false).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) if tc.clientMockFn != nil { tc.clientMockFn(client) } cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.DeleteTaskList(context.Background(), tc.filter, tc.prevRangeID) if (err != nil) != tc.wantErr { t.Errorf("DeleteTaskList() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestInsertTasks(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-01T22:08:41Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string tasksToInsert []*nosqlplugin.TaskRowForInsert tasklistCond *nosqlplugin.TaskListRow mapExecuteBatchCASApplied bool mapExecuteBatchCASErr error mapExecuteBatchCASPrev map[string]any wantQueries []string wantErr bool }{ { name: "successfully applied", tasksToInsert: []*nosqlplugin.TaskRowForInsert{ { TTLSeconds: 0, // default create task query will be used for this ttl TaskRow: nosqlplugin.TaskRow{ TaskID: 3, WorkflowID: "wid1", RunID: "rid1", ScheduledID: 42, CreatedTime: ts, }, }, { TTLSeconds: int(maxCassandraTTL) + 1, // ttl query will be used for this ttl TaskRow: nosqlplugin.TaskRow{ TaskID: 4, WorkflowID: "wid1", RunID: "rid1", ScheduledID: 43, CreatedTime: ts.Add(time.Second), }, }, }, tasklistCond: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, RangeID: 25, CurrentTimeStamp: ts, }, mapExecuteBatchCASApplied: true, wantQueries: []string{ `INSERT INTO tasks (domain_id, task_list_name, task_list_type, type, task_id, task, created_time) VALUES(domain1, tasklist1, 1, 0, 3, {domain_id: domain1, workflow_id: wid1, run_id: rid1, schedule_id: 42,created_time: 2024-04-01T22:08:41Z, partition_config: map[] }, 2024-04-01T22:08:41Z)`, `INSERT INTO tasks (domain_id, task_list_name, task_list_type, type, task_id, task, created_time) VALUES(domain1, tasklist1, 1, 0, 4, {domain_id: domain1, workflow_id: wid1, run_id: rid1, schedule_id: 43,created_time: 2024-04-01T22:08:42Z, partition_config: map[] }, 2024-04-01T22:08:41Z) USING TTL 157680000`, `UPDATE tasks SET range_id = 25, last_updated_time = 2024-04-01T22:08:41Z WHERE domain_id = domain1 and task_list_name = tasklist1 and task_list_type = 1 and type = 1 and task_id = -12345 IF range_id = 25`, }, }, { name: "batch cas failed", tasksToInsert: []*nosqlplugin.TaskRowForInsert{ { TTLSeconds: 0, TaskRow: nosqlplugin.TaskRow{ TaskID: 3, WorkflowID: "wid1", RunID: "rid1", ScheduledID: 42, }, }, }, tasklistCond: &nosqlplugin.TaskListRow{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, RangeID: 25, }, mapExecuteBatchCASErr: errors.New("some random error"), wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) session := &fakeSession{ mapExecuteBatchCASApplied: tc.mapExecuteBatchCASApplied, mapExecuteBatchCASErr: tc.mapExecuteBatchCASErr, mapExecuteBatchCASPrev: tc.mapExecuteBatchCASPrev, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.InsertTasks(context.Background(), tc.tasksToInsert, tc.tasklistCond) if (err != nil) != tc.wantErr { t.Errorf("InsertTasks() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.batches[0].queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestGetTasksCount(t *testing.T) { tests := []struct { name string filter *nosqlplugin.TasksFilter queryMockFn func(query *gocql.MockQuery) wantQueries []string wantQueueSize int64 wantErr bool }{ { name: "success", filter: &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, MinTaskID: 1, MaxTaskID: 100, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(result map[string]interface{}) error { result["count"] = int64(50) return nil }).Times(1) }, wantQueueSize: 50, wantQueries: []string{ `SELECT count(1) as count FROM tasks WHERE domain_id = domain1 and task_list_name = tasklist1 and task_list_type = 1 and type = 0 and task_id > 1 `, }, }, { name: "scan failure", filter: &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, MinTaskID: 1, MaxTaskID: 100, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(result map[string]interface{}) error { return errors.New("some random error") }).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) queueSize, err := db.GetTasksCount(context.Background(), tc.filter) if (err != nil) != tc.wantErr { t.Errorf("GetTasksCount() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } if queueSize != tc.wantQueueSize { t.Fatalf("Got queue size: %d, want: %v", queueSize, tc.wantQueueSize) } }) } } func TestSelectTasks(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2024-04-01T22:08:41Z") if err != nil { t.Fatalf("Failed to parse time: %v", err) } tests := []struct { name string filter *nosqlplugin.TasksFilter iter *fakeIter wantQueries []string wantTasks []*nosqlplugin.TaskRow wantErr bool }{ { name: "nil iter", filter: &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, MinTaskID: 1, MaxTaskID: 100, }, wantErr: true, }, { name: "iter close failed", filter: &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, MinTaskID: 1, MaxTaskID: 100, BatchSize: 3, }, iter: &fakeIter{closeErr: errors.New("some random error")}, wantErr: true, }, { name: "success", filter: &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, MinTaskID: 0, MaxTaskID: 100, BatchSize: 3, }, iter: &fakeIter{ mapScanInputs: []map[string]interface{}{ { "task_id": int64(1), "task": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "wofklow_id": "wid1", "schedule_id": int64(42), "created_time": ts, "run_id": &fakeUUID{uuid: "runid1"}, "partition_config": map[string]string{}, }, }, { "task_id": int64(2), "task": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "worklow_id": "wid1", "schedule_id": int64(45), "created_time": ts, "run_id": &fakeUUID{uuid: "runid1"}, "partition_config": map[string]string{}, }, }, { "missing_task_id": int64(1), // missing task_id so this row will be skipped }, { "task_id": int64(3), "task": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "worklow_id": "wid1", "schedule_id": int64(48), "created_time": ts, "run_id": &fakeUUID{uuid: "runid1"}, "partition_config": map[string]string{}, }, }, { "task_id": int64(4), // this will be skipped because filter.BatchSize is reached "task": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "worklow_id": "wid1", "schedule_id": int64(51), "created_time": ts, "run_id": &fakeUUID{uuid: "runid1"}, "partition_config": map[string]string{}, }, }, }, }, wantTasks: []*nosqlplugin.TaskRow{ { DomainID: "domain1", TaskID: 1, RunID: "runid1", ScheduledID: 42, CreatedTime: ts, PartitionConfig: map[string]string{}, }, { DomainID: "domain1", TaskID: 2, RunID: "runid1", ScheduledID: 45, CreatedTime: ts, PartitionConfig: map[string]string{}, }, { DomainID: "domain1", TaskID: 3, RunID: "runid1", ScheduledID: 48, CreatedTime: ts, PartitionConfig: map[string]string{}, }, }, wantQueries: []string{ `SELECT task_id, task, TTL(task) AS ttl FROM tasks WHERE domain_id = domain1 and task_list_name = tasklist1 and task_list_type = 1 and type = 0 and task_id > 0 and task_id <= 100`, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) query.EXPECT().PageSize(gomock.Any()).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) if tc.iter != nil { query.EXPECT().Iter().Return(tc.iter).Times(1) } else { query.EXPECT().Iter().Return(nil).Times(1) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) db := NewCassandraDBFromSession(cfg, session, logger, nil, DbWithClient(client)) gotRows, err := db.SelectTasks(context.Background(), tc.filter) if (err != nil) != tc.wantErr { t.Errorf("SelectTasks() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.wantTasks, gotRows); diff != "" { t.Fatalf("Task rows mismatch (-want +got):\n%s", diff) } if !tc.iter.closed { t.Fatal("iterator not closed") } }) } } func TestRangeDeleteTasks(t *testing.T) { tests := []struct { name string filter *nosqlplugin.TasksFilter queryMockFn func(query *gocql.MockQuery) wantQueries []string wantRowsDeleted int wantErr bool }{ { name: "success", filter: &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, MinTaskID: 1, MaxTaskID: 100, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantRowsDeleted: persistence.UnknownNumRowsAffected, wantQueries: []string{ `DELETE FROM tasks WHERE domain_id = domain1 AND task_list_name = tasklist1 AND task_list_type = 1 AND type = 0 AND task_id > 1 AND task_id <= 100 `, }, }, { name: "failure", filter: &nosqlplugin.TasksFilter{ TaskListFilter: nosqlplugin.TaskListFilter{ DomainID: "domain1", TaskListName: "tasklist1", TaskListType: 1, }, MinTaskID: 1, MaxTaskID: 100, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("some random error")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) db := NewCassandraDBFromSession(cfg, session, logger, nil, DbWithClient(client)) rowsDeleted, err := db.RangeDeleteTasks(context.Background(), tc.filter) if (err != nil) != tc.wantErr { t.Errorf("RangeDeleteTasks() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, session.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } if rowsDeleted != tc.wantRowsDeleted { t.Fatalf("Got rows deleted: %d, want: %v", rowsDeleted, tc.wantRowsDeleted) } }) } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/testdata/domain.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "time" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) func NewDomainRow(ts time.Time) *nosqlplugin.DomainRow { return &nosqlplugin.DomainRow{ Info: &persistence.DomainInfo{ ID: "test-domain-id", Name: "test-domain-name", Status: persistence.DomainStatusRegistered, Description: "test-domain-description", OwnerEmail: "test-domain-owner-email", Data: map[string]string{"k1": "v1"}, }, Config: &persistence.InternalDomainConfig{ Retention: 7 * 24 * time.Hour, EmitMetric: true, ArchivalBucket: "test-archival-bucket", ArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "test-history-archival-uri", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "test-visibility-archival-uri", BadBinaries: &persistence.DataBlob{Encoding: "thriftrw", Data: []byte("bad-binaries")}, IsolationGroups: &persistence.DataBlob{Encoding: "thriftrw", Data: []byte("isolation-group")}, AsyncWorkflowsConfig: &persistence.DataBlob{Encoding: "thriftrw", Data: []byte("async-workflows-config")}, }, ReplicationConfig: &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "test-active-cluster-name", Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: "test-cluster-name", }, }, ActiveClustersConfig: &persistence.DataBlob{Encoding: "thriftrw", Data: []byte("active-clusters-config")}, }, IsGlobalDomain: true, ConfigVersion: 3, FailoverVersion: 4, FailoverEndTime: &ts, LastUpdatedTime: ts, NotificationVersion: 5, CurrentTimeStamp: time.Date(2025, 1, 6, 15, 0, 0, 0, time.UTC), } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/testdata/shard.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "time" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) func NewShardRow(ts time.Time) *nosqlplugin.ShardRow { return &nosqlplugin.ShardRow{ InternalShardInfo: &persistence.InternalShardInfo{ ShardID: 15, Owner: "owner", RangeID: 1000, ReplicationAckLevel: 2000, TransferAckLevel: 3000, TimerAckLevel: ts.Add(-time.Hour), ClusterTransferAckLevel: map[string]int64{"cluster2": 4000}, ClusterTimerAckLevel: map[string]time.Time{"cluster2": ts.Add(-2 * time.Hour)}, DomainNotificationVersion: 3, ClusterReplicationLevel: map[string]int64{"cluster2": 5000}, ReplicationDLQAckLevel: map[string]int64{"cluster2": 10}, PendingFailoverMarkers: &persistence.DataBlob{Encoding: "thriftrw", Data: []byte("failovermarkers")}, TransferProcessingQueueStates: &persistence.DataBlob{Encoding: "thriftrw", Data: []byte("transferqueue")}, TimerProcessingQueueStates: &persistence.DataBlob{Encoding: "thriftrw", Data: []byte("timerqueue")}, CurrentTimestamp: ts, }, Data: []byte("sharddata"), DataEncoding: "thriftrw", } } func NewShardMap(ts time.Time) map[string]interface{} { return map[string]interface{}{ "shard_id": int(15), "range_id": int64(1000), "owner": "owner", "stolen_since_renew": 0, "updated_at": ts, "replication_ack_level": int64(2000), "transfer_ack_level": int64(3000), "timer_ack_level": ts.Add(-1 * time.Hour), "cluster_transfer_ack_level": map[string]int64{ "cluster1": int64(3000), }, "cluster_timer_ack_level": map[string]time.Time{ "cluster1": ts.Add(-1 * time.Hour), }, "transfer_processing_queue_states": []byte("transferqueue"), "transfer_processing_queue_states_encoding": "thriftrw", "timer_processing_queue_states": []byte("timerqueue"), "timer_processing_queue_states_encoding": "thriftrw", "domain_notification_version": int64(3), "cluster_replication_level": map[string]int64{"cluster2": 1500}, "replication_dlq_ack_level": map[string]int64{"cluster2": 5}, "pending_failover_markers": []byte("failovermarkers"), "pending_failover_markers_encoding": "thriftrw", } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/testdata/visibility.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package testdata import ( "log" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) const ( DomainID = "test-domain-id" WorkflowType = "test-workflow-type" WorkflowID = "test-workflow-id" RunID = "test-run-id" TypeName = "test-type-name" HistoryLenght = int64(1) TaskList = "test-task-list" NumClusters = int16(1) ShardID = int16(1) ) func NewVisibilityRow() persistence.InternalVisibilityWorkflowExecutionInfo { ts, err := time.Parse(time.RFC3339, "2024-04-01T22:08:41Z") if err != nil { log.Fatalf("Failed to parse time: %v", err) } return persistence.InternalVisibilityWorkflowExecutionInfo{ DomainID: DomainID, WorkflowType: WorkflowType, WorkflowID: WorkflowID, RunID: RunID, TypeName: TypeName, StartTime: ts, ExecutionTime: ts, CloseTime: ts, Status: types.WorkflowExecutionCloseStatusCompleted.Ptr(), HistoryLength: HistoryLenght, Memo: &persistence.DataBlob{ Encoding: constants.EncodingTypeJSON, Data: []byte{}, }, TaskList: TaskList, IsCron: false, NumClusters: NumClusters, UpdateTime: ts, SearchAttributes: map[string]interface{}{}, ShardID: ShardID, ExecutionStatus: types.WorkflowExecutionStatusPending, CronSchedule: "", ScheduledExecutionTime: time.Time{}, } } func NewVisibilityRowForInsert() *nosqlplugin.VisibilityRowForInsert { return &nosqlplugin.VisibilityRowForInsert{ VisibilityRow: NewVisibilityRow(), DomainID: DomainID, } } func NewVisibilityRowForUpdate(updateCloseToOpen, updateOpenToClose bool) *nosqlplugin.VisibilityRowForUpdate { visibilityRow := NewVisibilityRow() visibilityRow.CloseTime = visibilityRow.StartTime.Add(-1 * time.Minute) return &nosqlplugin.VisibilityRowForUpdate{ VisibilityRow: visibilityRow, DomainID: DomainID, UpdateCloseToOpen: updateCloseToOpen, UpdateOpenToClose: updateOpenToClose, } } func NewSelectVisibilityRequestFilter(filterType nosqlplugin.VisibilityFilterType, sortType nosqlplugin.VisibilitySortType) *nosqlplugin.VisibilityFilter { ts, err := time.Parse(time.RFC3339, "2024-04-01T22:08:41Z") if err != nil { log.Fatalf("Failed to parse time: %v", err) } return &nosqlplugin.VisibilityFilter{ ListRequest: persistence.InternalListWorkflowExecutionsRequest{DomainUUID: DomainID, EarliestTime: ts, LatestTime: ts}, FilterType: filterType, SortType: sortType, WorkflowType: WorkflowType, WorkflowID: WorkflowID, CloseStatus: 0, } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/testdata/workflow_execution.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) type WFExecRequestOption func(*nosqlplugin.WorkflowExecutionRequest) func WFExecRequestWithMapsWriteMode(mode nosqlplugin.WorkflowExecutionMapsWriteMode) WFExecRequestOption { return func(request *nosqlplugin.WorkflowExecutionRequest) { request.MapsWriteMode = mode } } func WFExecRequestWithEventBufferWriteMode(mode nosqlplugin.EventBufferWriteMode) WFExecRequestOption { return func(request *nosqlplugin.WorkflowExecutionRequest) { request.EventBufferWriteMode = mode } } func WFExecRequest(opts ...WFExecRequestOption) *nosqlplugin.WorkflowExecutionRequest { ts := time.Now() req := &nosqlplugin.WorkflowExecutionRequest{ InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", CompletionEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("test-completion-event"), }, AutoResetPoints: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("test-auto-reset-points"), }, }, VersionHistories: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("test-version-histories"), }, Checksums: &checksum.Checksum{ Version: 1, Flavor: checksum.FlavorIEEECRC32OverThriftBinary, Value: []byte("test-checksum"), }, PreviousNextEventIDCondition: common.Int64Ptr(123), CurrentTimeStamp: ts, } for _, opt := range opts { opt(req) } return req } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/visibility.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "fmt" "time" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) const ( domainPartition = 0 ) // InsertVisibility creates a new visibility record, return error is there is any. // TODO: Cassandra implementation ignores search attributes func (db *CDB) InsertVisibility(ctx context.Context, ttlSeconds int64, row *nosqlplugin.VisibilityRowForInsert) error { var query gocql.Query if ttlSeconds > maxCassandraTTL { query = db.session.Query(templateCreateWorkflowExecutionStarted, row.DomainID, domainPartition, row.WorkflowID, row.RunID, persistence.UnixNanoToDBTimestamp(row.StartTime.UnixNano()), persistence.UnixNanoToDBTimestamp(row.ExecutionTime.UnixNano()), row.TypeName, row.Memo.Data, row.Memo.GetEncoding(), row.TaskList, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID, row.ExecutionStatus, row.CronSchedule, persistence.UnixNanoToDBTimestamp(row.ScheduledExecutionTime.UnixNano()), ).WithContext(ctx) } else { query = db.session.Query(templateCreateWorkflowExecutionStartedWithTTL, row.DomainID, domainPartition, row.WorkflowID, row.RunID, persistence.UnixNanoToDBTimestamp(row.StartTime.UnixNano()), persistence.UnixNanoToDBTimestamp(row.ExecutionTime.UnixNano()), row.TypeName, row.Memo.Data, row.Memo.GetEncoding(), row.TaskList, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID, row.ExecutionStatus, row.CronSchedule, persistence.UnixNanoToDBTimestamp(row.ScheduledExecutionTime.UnixNano()), ttlSeconds, ).WithContext(ctx) } query = query.WithTimestamp(persistence.UnixNanoToDBTimestamp(row.StartTime.UnixNano())) return query.Exec() } func (db *CDB) UpdateVisibility(ctx context.Context, ttlSeconds int64, row *nosqlplugin.VisibilityRowForUpdate) error { batch := db.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) if row.UpdateCloseToOpen { // TODO implement it when where is a need panic("not supported operation") } if row.UpdateOpenToClose { // First, remove execution from the open table batch.Query(templateDeleteWorkflowExecutionStarted, row.DomainID, domainPartition, persistence.UnixNanoToDBTimestamp(row.StartTime.UnixNano()), row.RunID, ) } // Next, add a row in the closed table. if ttlSeconds > maxCassandraTTL { batch.Query(templateCreateWorkflowExecutionClosed, row.DomainID, domainPartition, row.WorkflowID, row.RunID, persistence.UnixNanoToDBTimestamp(row.StartTime.UnixNano()), persistence.UnixNanoToDBTimestamp(row.ExecutionTime.UnixNano()), persistence.UnixNanoToDBTimestamp(row.CloseTime.UnixNano()), row.TypeName, row.Status, row.HistoryLength, row.Memo.Data, row.Memo.GetEncoding(), row.TaskList, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID, row.ExecutionStatus, row.CronSchedule, persistence.UnixNanoToDBTimestamp(row.ScheduledExecutionTime.UnixNano()), ) // duplicate write to v2 to order by close time batch.Query(templateCreateWorkflowExecutionClosedV2, row.DomainID, domainPartition, row.WorkflowID, row.RunID, persistence.UnixNanoToDBTimestamp(row.StartTime.UnixNano()), persistence.UnixNanoToDBTimestamp(row.ExecutionTime.UnixNano()), persistence.UnixNanoToDBTimestamp(row.CloseTime.UnixNano()), row.TypeName, row.Status, row.HistoryLength, row.Memo.Data, row.Memo.GetEncoding(), row.TaskList, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID, row.ExecutionStatus, row.CronSchedule, persistence.UnixNanoToDBTimestamp(row.ScheduledExecutionTime.UnixNano()), ) } else { batch.Query(templateCreateWorkflowExecutionClosedWithTTL, row.DomainID, domainPartition, row.WorkflowID, row.RunID, persistence.UnixNanoToDBTimestamp(row.StartTime.UnixNano()), persistence.UnixNanoToDBTimestamp(row.ExecutionTime.UnixNano()), persistence.UnixNanoToDBTimestamp(row.CloseTime.UnixNano()), row.TypeName, row.Status, row.HistoryLength, row.Memo.Data, row.Memo.GetEncoding(), row.TaskList, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID, row.ExecutionStatus, row.CronSchedule, persistence.UnixNanoToDBTimestamp(row.ScheduledExecutionTime.UnixNano()), ttlSeconds, ) // duplicate write to v2 to order by close time batch.Query(templateCreateWorkflowExecutionClosedWithTTLV2, row.DomainID, domainPartition, row.WorkflowID, row.RunID, persistence.UnixNanoToDBTimestamp(row.StartTime.UnixNano()), persistence.UnixNanoToDBTimestamp(row.ExecutionTime.UnixNano()), persistence.UnixNanoToDBTimestamp(row.CloseTime.UnixNano()), row.TypeName, row.Status, row.HistoryLength, row.Memo.Data, row.Memo.GetEncoding(), row.TaskList, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID, row.ExecutionStatus, row.CronSchedule, persistence.UnixNanoToDBTimestamp(row.ScheduledExecutionTime.UnixNano()), ttlSeconds, ) } // RecordWorkflowExecutionStarted is using StartTimestamp as // the timestamp to issue query to Cassandra // due to the fact that cross DC using mutable state creation time as workflow start time // and visibility using event time instead of last update time (#1501) // CloseTimestamp can be before StartTimestamp, meaning using CloseTimestamp // can cause the deletion of open visibility record to be ignored. queryTimeStamp := row.CloseTime if queryTimeStamp.Before(row.StartTime) { queryTimeStamp = row.StartTime.Add(time.Second) } batch = batch.WithTimestamp(persistence.UnixNanoToDBTimestamp(queryTimeStamp.UnixNano())) return db.session.ExecuteBatch(batch) } func (db *CDB) SelectOneClosedWorkflow( ctx context.Context, domainID, workflowID, runID string, ) (*nosqlplugin.VisibilityRow, error) { query := db.session.Query(templateGetClosedWorkflowExecution, domainID, domainPartition, workflowID, runID, ).WithContext(ctx) iter := query.Iter() if iter == nil { return nil, fmt.Errorf("not able to create query iterator") } wfexecution, has := readClosedWorkflowExecutionRecord(iter) if !has { // Special case: return nil,nil if not found(since we will deprecate it, it's not worth refactor to be consistent) return nil, nil } if err := iter.Close(); err != nil { return nil, err } return wfexecution, nil } // Noop for Cassandra as it already handle by TTL func (db *CDB) DeleteVisibility(ctx context.Context, domainID, workflowID, runID string) error { // Normally we only depend on TTL for Cassandra visibility deletion but // we explicitly delete from open executions when an admin command is issued key := persistence.VisibilityAdminDeletionKey("visibilityAdminDelete") if v := ctx.Value(key); v != nil && v.(bool) { // Primary key is // to optimize showing open executions sorted by their start time // However, it is not useful for deletion since we don't have the start // time information. So, we need to get the record first with "allow // filtering", read startTime then delete it. This is okay because // it is only intended to be used for admin ops. record, err := db.openWorkflowByRunID(ctx, domainID, runID) if err != nil { return err } if record == nil { return nil // workflow not found, nothing to do } query := db.session.Query(templateDeleteVisibilityRecord, domainID, domainPartition, record.StartTime, runID, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } return nil } func (db *CDB) SelectVisibility(ctx context.Context, filter *nosqlplugin.VisibilityFilter) (*nosqlplugin.SelectVisibilityResponse, error) { switch filter.FilterType { case nosqlplugin.AllOpen: return db.openSortedByStartTime(ctx, &filter.ListRequest) case nosqlplugin.AllClosed: switch filter.SortType { case nosqlplugin.SortByStartTime: return db.closedSortedByStartTime(ctx, &filter.ListRequest) case nosqlplugin.SortByClosedTime: return db.closedSortedByClosedTime(ctx, &filter.ListRequest) default: panic("not supported sorting type") } // by workflowType case nosqlplugin.OpenByWorkflowType: return db.openFilteredByWorkflowTypeSortedByStartTime(ctx, &filter.ListRequest, filter.WorkflowType) case nosqlplugin.ClosedByWorkflowType: switch filter.SortType { case nosqlplugin.SortByStartTime: return db.closedFilteredByWorkflowTypeSortedByStartTime(ctx, &filter.ListRequest, filter.WorkflowType) case nosqlplugin.SortByClosedTime: return db.closedFilteredByWorkflowTypeSortedByClosedTime(ctx, &filter.ListRequest, filter.WorkflowType) default: panic("not supported sorting type") } // by workflowID case nosqlplugin.OpenByWorkflowID: return db.openFilteredByWorkflowIDSortedByStartTime(ctx, &filter.ListRequest, filter.WorkflowID) case nosqlplugin.ClosedByWorkflowID: switch filter.SortType { case nosqlplugin.SortByStartTime: return db.closedFilteredByWorkflowIDSortedByStartTime(ctx, &filter.ListRequest, filter.WorkflowID) case nosqlplugin.SortByClosedTime: return db.closedFilteredByWorkflowIDSortedByClosedTime(ctx, &filter.ListRequest, filter.WorkflowID) default: panic("not supported sorting type") } // closeStatus case nosqlplugin.ClosedByClosedStatus: switch filter.SortType { case nosqlplugin.SortByStartTime: return db.closedFilteredByClosedStatusSortedByStartTime(ctx, &filter.ListRequest, filter.CloseStatus) case nosqlplugin.SortByClosedTime: return db.closedFilteredByClosedStatusSortedByClosedTime(ctx, &filter.ListRequest, filter.CloseStatus) default: panic("not supported sorting type") } default: panic("no supported filter type") } } func (db *CDB) openFilteredByWorkflowTypeSortedByStartTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, workflowType string, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetOpenWorkflowExecutionsByType, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), workflowType, ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readOpenWorkflowExecutionRecord) } func (db *CDB) closedFilteredByWorkflowTypeSortedByStartTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, workflowType string, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetClosedWorkflowExecutionsByType, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), workflowType, ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readClosedWorkflowExecutionRecord) } func (db *CDB) closedFilteredByWorkflowTypeSortedByClosedTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, workflowType string, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetClosedWorkflowExecutionsByTypeSortByCloseTime, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), workflowType, ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readClosedWorkflowExecutionRecord) } func (db *CDB) openFilteredByWorkflowIDSortedByStartTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, workflowID string, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetOpenWorkflowExecutionsByID, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), workflowID, ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readOpenWorkflowExecutionRecord) } func (db *CDB) openWorkflowByRunID( ctx context.Context, domainID string, runID string, ) (*persistence.InternalVisibilityWorkflowExecutionInfo, error) { query := db.session.Query(templateGetOpenWorkflowExecutionsByRunID, domainID, domainPartition, runID, ).WithContext(ctx) iter := query.Iter() if iter == nil { return nil, fmt.Errorf("not able to create query iterator") } wfexecution, has := readOpenWorkflowExecutionRecord(iter) if !has { return nil, nil } if err := iter.Close(); err != nil { return nil, err } return wfexecution, nil } func (db *CDB) closedFilteredByWorkflowIDSortedByStartTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, workflowID string, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetClosedWorkflowExecutionsByID, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), workflowID, ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readClosedWorkflowExecutionRecord) } func (db *CDB) closedFilteredByWorkflowIDSortedByClosedTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, workflowID string, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetClosedWorkflowExecutionsByIDSortByCloseTime, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), workflowID, ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readClosedWorkflowExecutionRecord) } func (db *CDB) closedFilteredByClosedStatusSortedByStartTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, closeStatus int32, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetClosedWorkflowExecutionsByStatus, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), closeStatus, ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readClosedWorkflowExecutionRecord) } func (db *CDB) closedFilteredByClosedStatusSortedByClosedTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, closeStatus int32, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetClosedWorkflowExecutionsByStatusSortByClosedTime, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), closeStatus, ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readClosedWorkflowExecutionRecord) } func (db *CDB) openSortedByStartTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetOpenWorkflowExecutions, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readOpenWorkflowExecutionRecord) } func (db *CDB) closedSortedByStartTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetClosedWorkflowExecutions, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readClosedWorkflowExecutionRecord) } func (db *CDB) closedSortedByClosedTime( ctx context.Context, request *persistence.InternalListWorkflowExecutionsRequest, ) (*nosqlplugin.SelectVisibilityResponse, error) { query := db.session.Query(templateGetClosedWorkflowExecutionsSortByCloseTime, request.DomainUUID, domainPartition, persistence.UnixNanoToDBTimestamp(request.EarliestTime.UnixNano()), persistence.UnixNanoToDBTimestamp(request.LatestTime.UnixNano()), ).Consistency(cassandraLowConslevel).WithContext(ctx) return processQuery(query, request, readClosedWorkflowExecutionRecord) } type recorderReaderFunc func(iter gocql.Iter) (*persistence.InternalVisibilityWorkflowExecutionInfo, bool) func processQuery( query gocql.Query, request *persistence.InternalListWorkflowExecutionsRequest, recorderReader recorderReaderFunc, ) (*nosqlplugin.SelectVisibilityResponse, error) { iter := query.PageSize(request.PageSize).PageState(request.NextPageToken).Iter() if iter == nil { // TODO: may return badRequestError return nil, fmt.Errorf("not able to create query iterator") } response := &nosqlplugin.SelectVisibilityResponse{} response.Executions = make([]*persistence.InternalVisibilityWorkflowExecutionInfo, 0) wfexecution, has := recorderReader(iter) for has { response.Executions = append(response.Executions, wfexecution) wfexecution, has = recorderReader(iter) } nextPageToken := iter.PageState() response.NextPageToken = make([]byte, len(nextPageToken)) copy(response.NextPageToken, nextPageToken) if err := iter.Close(); err != nil { return nil, err } return response, nil } func readOpenWorkflowExecutionRecord( iter gocql.Iter, ) (*persistence.InternalVisibilityWorkflowExecutionInfo, bool) { var workflowID string var runID string var typeName string var startTime time.Time var executionTime time.Time var memo []byte var encoding string var taskList string var isCron bool var numClusters int16 var updateTime time.Time var shardID int16 var executionStatus int32 var cronSchedule string var scheduledExecutionTime time.Time if iter.Scan(&workflowID, &runID, &startTime, &executionTime, &typeName, &memo, &encoding, &taskList, &isCron, &numClusters, &updateTime, &shardID, &executionStatus, &cronSchedule, &scheduledExecutionTime) { record := &persistence.InternalVisibilityWorkflowExecutionInfo{ WorkflowID: workflowID, RunID: runID, TypeName: typeName, StartTime: startTime, ExecutionTime: executionTime, Memo: persistence.NewDataBlob(memo, constants.EncodingType(encoding)), TaskList: taskList, IsCron: isCron, NumClusters: numClusters, UpdateTime: updateTime, ShardID: shardID, ExecutionStatus: types.WorkflowExecutionStatus(executionStatus), CronSchedule: cronSchedule, ScheduledExecutionTime: scheduledExecutionTime, } return record, true } return nil, false } func readClosedWorkflowExecutionRecord( iter gocql.Iter, ) (*persistence.InternalVisibilityWorkflowExecutionInfo, bool) { var workflowID string var runID string var typeName string var startTime time.Time var executionTime time.Time var closeTime time.Time var status workflow.WorkflowExecutionCloseStatus var historyLength int64 var memo []byte var encoding string var taskList string var isCron bool var numClusters int16 var updateTime time.Time var shardID int16 var executionStatus int32 var cronSchedule string var scheduledExecutionTime time.Time if iter.Scan(&workflowID, &runID, &startTime, &executionTime, &closeTime, &typeName, &status, &historyLength, &memo, &encoding, &taskList, &isCron, &numClusters, &updateTime, &shardID, &executionStatus, &cronSchedule, &scheduledExecutionTime) { record := &persistence.InternalVisibilityWorkflowExecutionInfo{ WorkflowID: workflowID, RunID: runID, TypeName: typeName, StartTime: startTime, ExecutionTime: executionTime, CloseTime: closeTime, Status: thrift.ToWorkflowExecutionCloseStatus(&status), HistoryLength: historyLength, Memo: persistence.NewDataBlob(memo, constants.EncodingType(encoding)), TaskList: taskList, IsCron: isCron, NumClusters: numClusters, UpdateTime: updateTime, ShardID: shardID, ExecutionStatus: types.WorkflowExecutionStatus(executionStatus), CronSchedule: cronSchedule, ScheduledExecutionTime: scheduledExecutionTime, } return record, true } return nil, false } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/visibility_cql.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra const ( // /////////////// Open Executions ///////////////// openExecutionsColumnsForSelect = " workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time " openExecutionsColumnsForInsert = "(domain_id, domain_partition, " + openExecutionsColumnsForSelect + ")" templateCreateWorkflowExecutionStartedWithTTL = `INSERT INTO open_executions ` + openExecutionsColumnsForInsert + `VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) using TTL ?` templateCreateWorkflowExecutionStarted = `INSERT INTO open_executions` + openExecutionsColumnsForInsert + `VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` templateDeleteWorkflowExecutionStarted = `DELETE FROM open_executions ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND start_time = ? ` + `AND run_id = ?` templateGetOpenWorkflowExecutions = `SELECT ` + openExecutionsColumnsForSelect + `FROM open_executions ` + `WHERE domain_id = ? ` + `AND domain_partition IN (?) ` + `AND start_time >= ? ` + `AND start_time <= ? ` templateGetOpenWorkflowExecutionsByType = `SELECT ` + openExecutionsColumnsForSelect + `FROM open_executions ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND start_time >= ? ` + `AND start_time <= ? ` + `AND workflow_type_name = ? ` templateGetOpenWorkflowExecutionsByID = `SELECT ` + openExecutionsColumnsForSelect + `FROM open_executions ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND start_time >= ? ` + `AND start_time <= ? ` + `AND workflow_id = ? ` // Intended for admin operations only templateGetOpenWorkflowExecutionsByRunID = `SELECT ` + openExecutionsColumnsForSelect + `FROM open_executions ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND run_id = ? ` + `ALLOW FILTERING` // Intended for admin operations only templateDeleteVisibilityRecord = `DELETE FROM open_executions ` + `WHERE domain_id = ? ` + `and domain_partition = ? ` + `and start_time = ? ` + `and run_id = ? ` // /////////////// Closed Executions ///////////////// closedExecutionColumnsForSelect = " workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time " closedExecutionColumnsForInsert = "(domain_id, domain_partition, " + closedExecutionColumnsForSelect + ")" templateCreateWorkflowExecutionClosedWithTTL = `INSERT INTO closed_executions ` + closedExecutionColumnsForInsert + `VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) using TTL ?` templateCreateWorkflowExecutionClosed = `INSERT INTO closed_executions ` + closedExecutionColumnsForInsert + `VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` templateCreateWorkflowExecutionClosedWithTTLV2 = `INSERT INTO closed_executions_v2 ` + closedExecutionColumnsForInsert + `VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) using TTL ?` templateCreateWorkflowExecutionClosedV2 = `INSERT INTO closed_executions_v2 ` + closedExecutionColumnsForInsert + `VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` templateGetClosedWorkflowExecutions = `SELECT ` + closedExecutionColumnsForSelect + `FROM closed_executions ` + `WHERE domain_id = ? ` + `AND domain_partition IN (?) ` + `AND start_time >= ? ` + `AND start_time <= ? ` templateGetClosedWorkflowExecutionsByType = `SELECT ` + closedExecutionColumnsForSelect + `FROM closed_executions ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND start_time >= ? ` + `AND start_time <= ? ` + `AND workflow_type_name = ? ` templateGetClosedWorkflowExecutionsByID = `SELECT ` + closedExecutionColumnsForSelect + `FROM closed_executions ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND start_time >= ? ` + `AND start_time <= ? ` + `AND workflow_id = ? ` templateGetClosedWorkflowExecutionsByStatus = `SELECT ` + closedExecutionColumnsForSelect + `FROM closed_executions ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND start_time >= ? ` + `AND start_time <= ? ` + `AND status = ? ` templateGetClosedWorkflowExecution = `SELECT ` + closedExecutionColumnsForSelect + `FROM closed_executions ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND workflow_id = ? ` + `AND run_id = ? ALLOW FILTERING ` templateGetClosedWorkflowExecutionsSortByCloseTime = `SELECT ` + closedExecutionColumnsForSelect + `FROM closed_executions_v2 ` + `WHERE domain_id = ? ` + `AND domain_partition IN (?) ` + `AND close_time >= ? ` + `AND close_time <= ? ` templateGetClosedWorkflowExecutionsByTypeSortByCloseTime = `SELECT ` + closedExecutionColumnsForSelect + `FROM closed_executions_v2 ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND close_time >= ? ` + `AND close_time <= ? ` + `AND workflow_type_name = ? ` templateGetClosedWorkflowExecutionsByIDSortByCloseTime = `SELECT ` + closedExecutionColumnsForSelect + `FROM closed_executions_v2 ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND close_time >= ? ` + `AND close_time <= ? ` + `AND workflow_id = ? ` templateGetClosedWorkflowExecutionsByStatusSortByClosedTime = `SELECT ` + closedExecutionColumnsForSelect + `FROM closed_executions_v2 ` + `WHERE domain_id = ? ` + `AND domain_partition = ? ` + `AND close_time >= ? ` + `AND close_time <= ? ` + `AND status = ? ` ) ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/visibility_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/testdata" ) func TestInsertVisibility(t *testing.T) { tests := []struct { desc string row *nosqlplugin.VisibilityRowForInsert ttlSeconds int64 queryMockFunc func(*gocql.MockQuery) wantQueries []string wantErr bool }{ { desc: "Query with ttl less than maxCassandraTTL", row: testdata.NewVisibilityRowForInsert(), ttlSeconds: int64(1000), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().WithTimestamp(gomock.Any()).Return(query) query.EXPECT().Exec().Return(nil) }, wantQueries: []string{ `INSERT INTO open_executions (domain_id, domain_partition, workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time )VALUES (test-domain-id, 0, test-workflow-id, test-run-id, 1712009321000, 1712009321000, test-type-name, [], json, test-task-list, false, 1, 2024-04-01T22:08:41Z, 1, PENDING, , -6795364578871) using TTL 1000`, }, wantErr: false, }, { desc: "Query With ttl greater than maxCassandraTTL", row: testdata.NewVisibilityRowForInsert(), ttlSeconds: maxCassandraTTL + 1, queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().WithTimestamp(gomock.Any()).Return(query) query.EXPECT().Exec().Return(nil) }, wantQueries: []string{ `INSERT INTO open_executions(domain_id, domain_partition, workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time )VALUES (test-domain-id, 0, test-workflow-id, test-run-id, 1712009321000, 1712009321000, test-type-name, [], json, test-task-list, false, 1, 2024-04-01T22:08:41Z, 1, PENDING, , -6795364578871)`, }, wantErr: false, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) if test.queryMockFunc != nil { test.queryMockFunc(query) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.InsertVisibility(context.Background(), test.ttlSeconds, test.row) if test.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } assert.Equal(t, test.wantQueries, session.queries) }) } } func TestUpdateVisibility(t *testing.T) { tests := []struct { desc string row *nosqlplugin.VisibilityRowForUpdate ttlSeconds int64 wantQueries []string wantErr bool wantPanic bool }{ { desc: "Query with ttl less than maxCassandraTTL", row: testdata.NewVisibilityRowForUpdate(false, true), ttlSeconds: int64(100), wantQueries: []string{ `DELETE FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND start_time = 1712009321000 AND run_id = test-run-id`, `INSERT INTO closed_executions (domain_id, domain_partition, workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time )VALUES (test-domain-id, 0, test-workflow-id, test-run-id, 1712009321000, 1712009321000, 1712009261000, test-type-name, COMPLETED, 1, [], json, test-task-list, false, 1, 2024-04-01T22:08:41Z, 1, PENDING, , -6795364578871) using TTL 100`, `INSERT INTO closed_executions_v2 (domain_id, domain_partition, workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time )VALUES (test-domain-id, 0, test-workflow-id, test-run-id, 1712009321000, 1712009321000, 1712009261000, test-type-name, COMPLETED, 1, [], json, test-task-list, false, 1, 2024-04-01T22:08:41Z, 1, PENDING, , -6795364578871) using TTL 100`, }, wantErr: false, wantPanic: false, }, { desc: "Query with ttl greater than maxCassandraTTL", row: testdata.NewVisibilityRowForUpdate(false, true), ttlSeconds: maxCassandraTTL + 1, wantQueries: []string{ `DELETE FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND start_time = 1712009321000 AND run_id = test-run-id`, `INSERT INTO closed_executions (domain_id, domain_partition, workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time )VALUES (test-domain-id, 0, test-workflow-id, test-run-id, 1712009321000, 1712009321000, 1712009261000, test-type-name, COMPLETED, 1, [], json, test-task-list, false, 1, 2024-04-01T22:08:41Z, 1, PENDING, , -6795364578871)`, `INSERT INTO closed_executions_v2 (domain_id, domain_partition, workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time )VALUES (test-domain-id, 0, test-workflow-id, test-run-id, 1712009321000, 1712009321000, 1712009261000, test-type-name, COMPLETED, 1, [], json, test-task-list, false, 1, 2024-04-01T22:08:41Z, 1, PENDING, , -6795364578871)`, }, wantErr: false, wantPanic: false, }, { desc: "panic if updateCloseToOpen is set", row: testdata.NewVisibilityRowForUpdate(true, true), ttlSeconds: int64(100), wantErr: false, wantPanic: true, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { defer func() { r := recover() if (r != nil) != test.wantPanic { t.Errorf("test: %s, panicWanted: %v, panicOccured: %v", test.desc, test.wantPanic, r != nil) } }() ctrl := gomock.NewController(t) session := &fakeSession{ query: gocql.NewMockQuery(ctrl), } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.UpdateVisibility(context.Background(), test.ttlSeconds, test.row) if test.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } assert.Equal(t, test.wantQueries, session.batches[0].queries) }) } } func TestSelectOneClosedWorkflow(t *testing.T) { tests := []struct { desc string domainID string workflowID string runID string itrMockFunc func(*gocql.MockIter) wantQueries []string wantError bool wantResult bool }{ { desc: "return error when query's iter function returns nil", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, itrMockFunc: nil, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND workflow_id = test-workflow-id AND run_id = test-run-id ALLOW FILTERING `, }, wantError: true, wantResult: false, }, { desc: "return nil if reading closed_workflow_execution_record returns false", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(false) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND workflow_id = test-workflow-id AND run_id = test-run-id ALLOW FILTERING `, }, wantError: false, wantResult: false, }, { desc: "return error if closing iterator fails", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(true) itr.EXPECT().Close().Return(errors.New("close error")) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND workflow_id = test-workflow-id AND run_id = test-run-id ALLOW FILTERING `, }, wantError: true, wantResult: false, }, { desc: "success", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(true) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND workflow_id = test-workflow-id AND run_id = test-run-id ALLOW FILTERING `, }, wantError: false, wantResult: true, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) query.EXPECT().WithContext(gomock.Any()).Return(query) if test.itrMockFunc != nil { itr := gocql.NewMockIter(ctrl) test.itrMockFunc(itr) query.EXPECT().Iter().Return(itr) } else { query.EXPECT().Iter().Return(nil) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) result, err := db.SelectOneClosedWorkflow(context.Background(), test.domainID, test.workflowID, test.runID) if test.wantError { assert.Error(t, err) } else { assert.NoError(t, err) } if test.wantResult { assert.NotNil(t, result) } else { assert.Nil(t, result) } assert.Equal(t, test.wantQueries, session.queries) }) } } func TestDeleteVisibility(t *testing.T) { tests := []struct { desc string domainID string workflowID string runID string mockItr bool itrMockFunc func(*gocql.MockIter) queryMockFunc func(*gocql.MockQuery) context context.Context dc *persistence.DynamicConfiguration clientMockFunc func(*gocql.MockClient) wantQueries []string wantError bool }{ { desc: "return nil if visibility_admin_key not present in context", domainID: "", workflowID: "", runID: "", mockItr: false, itrMockFunc: nil, queryMockFunc: nil, context: context.Background(), dc: &persistence.DynamicConfiguration{}, clientMockFunc: nil, wantQueries: nil, wantError: false, }, { desc: "return error when query's iter function returns nil", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, mockItr: true, itrMockFunc: nil, queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query) }, context: context.WithValue(context.Background(), persistence.VisibilityAdminDeletionKey("visibilityAdminDelete"), true), dc: &persistence.DynamicConfiguration{}, clientMockFunc: nil, wantQueries: []string{`SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND run_id = test-run-id ALLOW FILTERING`}, wantError: true, }, { desc: "return nil if reading open_workflow_execution_records returns false", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(15)...).Return(false) }, queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query) }, context: context.WithValue(context.Background(), persistence.VisibilityAdminDeletionKey("visibilityAdminDelete"), true), dc: &persistence.DynamicConfiguration{}, clientMockFunc: nil, wantQueries: []string{`SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND run_id = test-run-id ALLOW FILTERING`}, wantError: false, }, { desc: "return error if closing iterator fails", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(15)...).Return(true) itr.EXPECT().Close().Return(errors.New("close error")) }, queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query) }, context: context.WithValue(context.Background(), persistence.VisibilityAdminDeletionKey("visibilityAdminDelete"), true), dc: &persistence.DynamicConfiguration{}, clientMockFunc: nil, wantQueries: []string{`SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND run_id = test-run-id ALLOW FILTERING`}, wantError: true, }, { desc: "success with db dynamic_configuration as nil", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(15)...).Return(true) itr.EXPECT().Close().Return(nil) }, queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(2) query.EXPECT().Exec().Return(nil) }, context: context.WithValue(context.Background(), persistence.VisibilityAdminDeletionKey("visibilityAdminDelete"), true), dc: nil, clientMockFunc: nil, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND run_id = test-run-id ALLOW FILTERING`, `DELETE FROM open_executions WHERE domain_id = test-domain-id and domain_partition = 0 and start_time = 0001-01-01T00:00:00Z and run_id = test-run-id `, }, wantError: false, }, { desc: "success with enable_cassandra_all_consistency_level_delete db dynamic_configuration", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(15)...).Return(true) itr.EXPECT().Close().Return(nil) }, queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(2) query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().Exec().Return(nil) }, context: context.WithValue(context.Background(), persistence.VisibilityAdminDeletionKey("visibilityAdminDelete"), true), dc: &persistence.DynamicConfiguration{EnableCassandraAllConsistencyLevelDelete: func(opts ...dynamicproperties.FilterOption) bool { return true }}, clientMockFunc: nil, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND run_id = test-run-id ALLOW FILTERING`, `DELETE FROM open_executions WHERE domain_id = test-domain-id and domain_partition = 0 and start_time = 0001-01-01T00:00:00Z and run_id = test-run-id `, }, wantError: false, }, { desc: "succes with cassandra_default_consistency_level when cassandra_all_consistency_level fails", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, context: context.WithValue(context.Background(), persistence.VisibilityAdminDeletionKey("visibilityAdminDelete"), true), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(2) query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().Exec().Return(errors.New("all consistency level fail")) query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().Exec().Return(nil) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(15)...).Return(true) itr.EXPECT().Close().Return(nil) }, dc: &persistence.DynamicConfiguration{ EnableCassandraAllConsistencyLevelDelete: func(opts ...dynamicproperties.FilterOption) bool { return true }, }, clientMockFunc: func(client *gocql.MockClient) { client.EXPECT().IsCassandraConsistencyError(gomock.Any()).Return(true) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND run_id = test-run-id ALLOW FILTERING`, `DELETE FROM open_executions WHERE domain_id = test-domain-id and domain_partition = 0 and start_time = 0001-01-01T00:00:00Z and run_id = test-run-id `, }, wantError: false, }, { desc: "return error cassandra_all_consistency_level fails and error is not cassandra consistency error", domainID: testdata.DomainID, workflowID: testdata.WorkflowID, runID: testdata.RunID, context: context.WithValue(context.Background(), persistence.VisibilityAdminDeletionKey("visibilityAdminDelete"), true), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(2) query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().Exec().Return(errors.New("all consistency level fail")) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(15)...).Return(true) itr.EXPECT().Close().Return(nil) }, dc: &persistence.DynamicConfiguration{ EnableCassandraAllConsistencyLevelDelete: func(opts ...dynamicproperties.FilterOption) bool { return true }, }, clientMockFunc: func(client *gocql.MockClient) { client.EXPECT().IsCassandraConsistencyError(gomock.Any()).Return(false) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND run_id = test-run-id ALLOW FILTERING`, `DELETE FROM open_executions WHERE domain_id = test-domain-id and domain_partition = 0 and start_time = 0001-01-01T00:00:00Z and run_id = test-run-id `, }, wantError: true, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) if test.queryMockFunc != nil { test.queryMockFunc(query) } if test.mockItr && test.itrMockFunc != nil { itr := gocql.NewMockIter(ctrl) test.itrMockFunc(itr) query.EXPECT().Iter().Return(itr) } else if test.mockItr { query.EXPECT().Iter().Return(nil) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) if test.clientMockFunc != nil { test.clientMockFunc(client) } cfg := &config.NoSQL{} logger := testlogger.New(t) db := NewCassandraDBFromSession(cfg, session, logger, test.dc, DbWithClient(client)) err := db.DeleteVisibility(test.context, test.domainID, test.workflowID, test.runID) if test.wantError { assert.Error(t, err) } else { assert.NoError(t, err) } assert.Equal(t, test.wantQueries, session.queries) }) } } func TestSelectVisibility(t *testing.T) { tests := []struct { desc string filter *nosqlplugin.VisibilityFilter queryMockFunc func(query *gocql.MockQuery) mockItr bool itrMockFunc func(itr *gocql.MockIter) wantQueries []string wantError bool wantResult bool wantPanic bool }{ { desc: "panic for invalid filter type", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.VisibilityFilterType(10), nosqlplugin.SortByStartTime), queryMockFunc: nil, mockItr: false, itrMockFunc: nil, wantQueries: nil, wantError: false, wantResult: false, wantPanic: true, }, { desc: "all-open type filter return error if iterator is nil", // passing sort type as StartByStartTime(as it is the default value) for cases where sort type is not needed filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.AllOpen, nosqlplugin.SortByStartTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: nil, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition IN (0) AND start_time >= 1712009321000 AND start_time <= 1712009321000 `, }, wantError: true, wantResult: false, wantPanic: false, }, { desc: "all-open type filter return error if closing iterator fails", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.AllOpen, nosqlplugin.SortByStartTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(15)...).Return(true) itr.EXPECT().Scan(generateMockParams(15)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(errors.New("close error")) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition IN (0) AND start_time >= 1712009321000 AND start_time <= 1712009321000 `, }, wantError: true, wantResult: false, wantPanic: false, }, { desc: "all-open type filter success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.AllOpen, nosqlplugin.SortByStartTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(15)...).Return(true) itr.EXPECT().Scan(generateMockParams(15)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition IN (0) AND start_time >= 1712009321000 AND start_time <= 1712009321000 `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "all-closed type filter sort by start-time success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.AllClosed, nosqlplugin.SortByStartTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions WHERE domain_id = test-domain-id AND domain_partition IN (0) AND start_time >= 1712009321000 AND start_time <= 1712009321000 `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "all-closed type filter sort by closed-time success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.AllClosed, nosqlplugin.SortByClosedTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions_v2 WHERE domain_id = test-domain-id AND domain_partition IN (0) AND close_time >= 1712009321000 AND close_time <= 1712009321000 `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "panic on all-closed type filter invalid sort attribute", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.AllClosed, nosqlplugin.VisibilitySortType(100)), queryMockFunc: func(query *gocql.MockQuery) { }, mockItr: false, itrMockFunc: nil, wantQueries: nil, wantError: false, wantResult: false, wantPanic: true, }, { desc: "open-by-workflow-type filter success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.OpenByWorkflowType, nosqlplugin.SortByStartTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(15)...).Return(true) itr.EXPECT().Scan(generateMockParams(15)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND start_time >= 1712009321000 AND start_time <= 1712009321000 AND workflow_type_name = test-workflow-type `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "closed-by-workflow-type filter sort by start-time success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.ClosedByWorkflowType, nosqlplugin.SortByStartTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND start_time >= 1712009321000 AND start_time <= 1712009321000 AND workflow_type_name = test-workflow-type `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "closed-by-workflow-type filter sort by closed-time success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.ClosedByWorkflowType, nosqlplugin.SortByClosedTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions_v2 WHERE domain_id = test-domain-id AND domain_partition = 0 AND close_time >= 1712009321000 AND close_time <= 1712009321000 AND workflow_type_name = test-workflow-type `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "panic on closed-by-workflow-type filter invalid sort attribute", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.ClosedByWorkflowType, nosqlplugin.VisibilitySortType(100)), queryMockFunc: nil, mockItr: false, itrMockFunc: nil, wantQueries: nil, wantError: false, wantResult: false, wantPanic: true, }, { desc: "open-by-workflow-id filter success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.OpenByWorkflowID, nosqlplugin.SortByStartTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(15)...).Return(true) itr.EXPECT().Scan(generateMockParams(15)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM open_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND start_time >= 1712009321000 AND start_time <= 1712009321000 AND workflow_id = test-workflow-id `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "closed-by-workflow-id filter sort by start-time success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.ClosedByWorkflowID, nosqlplugin.SortByStartTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND start_time >= 1712009321000 AND start_time <= 1712009321000 AND workflow_id = test-workflow-id `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "closed-by-workflow-id filter sort by closed-time success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.ClosedByWorkflowID, nosqlplugin.SortByClosedTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions_v2 WHERE domain_id = test-domain-id AND domain_partition = 0 AND close_time >= 1712009321000 AND close_time <= 1712009321000 AND workflow_id = test-workflow-id `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "panic on closed-by-workflow-id filter invalid sort attribute", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.ClosedByWorkflowID, nosqlplugin.VisibilitySortType(100)), queryMockFunc: nil, mockItr: false, itrMockFunc: nil, wantQueries: nil, wantError: false, wantResult: false, wantPanic: true, }, { desc: "closed-by-closed-status filter sort by start-time success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.ClosedByClosedStatus, nosqlplugin.SortByStartTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions WHERE domain_id = test-domain-id AND domain_partition = 0 AND start_time >= 1712009321000 AND start_time <= 1712009321000 AND status = 0 `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "closed-by-closed-status filter sort by closed-time success", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.ClosedByClosedStatus, nosqlplugin.SortByClosedTime), queryMockFunc: func(query *gocql.MockQuery) { query.EXPECT().Consistency(gomock.Any()).Return(query) query.EXPECT().WithContext(gomock.Any()).Return(query) query.EXPECT().PageSize(gomock.Any()).Return(query) query.EXPECT().PageState(gomock.Any()).Return(query) }, mockItr: true, itrMockFunc: func(itr *gocql.MockIter) { itr.EXPECT().Scan(generateMockParams(18)...).Return(false) itr.EXPECT().PageState().Return([]byte("test")) itr.EXPECT().Close().Return(nil) }, wantQueries: []string{ `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_list, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time FROM closed_executions_v2 WHERE domain_id = test-domain-id AND domain_partition = 0 AND close_time >= 1712009321000 AND close_time <= 1712009321000 AND status = 0 `, }, wantError: false, wantResult: true, wantPanic: false, }, { desc: "panic on closed-by-closed-status filter invalid sort attribute", filter: testdata.NewSelectVisibilityRequestFilter(nosqlplugin.ClosedByClosedStatus, nosqlplugin.VisibilitySortType(100)), queryMockFunc: nil, mockItr: false, itrMockFunc: nil, wantQueries: nil, wantError: false, wantResult: false, wantPanic: true, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { defer func() { r := recover() if (r != nil) != test.wantPanic { t.Errorf("test: %s, panicWanted: %v, panicOccured: %v", test.desc, test.wantPanic, r != nil) } }() ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) if test.queryMockFunc != nil { test.queryMockFunc(query) } if test.mockItr && test.itrMockFunc != nil { itr := gocql.NewMockIter(ctrl) test.itrMockFunc(itr) query.EXPECT().Iter().Return(itr) } else if test.mockItr { query.EXPECT().Iter().Return(nil) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) result, err := db.SelectVisibility(context.Background(), test.filter) if test.wantError { assert.Error(t, err) } else { assert.NoError(t, err) } if test.wantResult { assert.NotNil(t, result) } else { assert.Nil(t, result) } assert.Equal(t, test.wantQueries, session.queries) }) } } func generateMockParams(count int) []interface{} { params := []interface{}{} for i := 0; i < count; i++ { params = append(params, gomock.Any()) } return params } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/workflow.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "fmt" "strings" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/types" ) var _ nosqlplugin.WorkflowCRUD = (*CDB)(nil) func (db *CDB) InsertWorkflowExecutionWithTasks( ctx context.Context, requests *nosqlplugin.WorkflowRequestsWriteRequest, currentWorkflowRequest *nosqlplugin.CurrentWorkflowWriteRequest, execution *nosqlplugin.WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask, activeClusterSelectionPolicyRow *nosqlplugin.ActiveClusterSelectionPolicyRow, shardCondition *nosqlplugin.ShardCondition, ) error { shardID := shardCondition.ShardID domainID := execution.DomainID workflowID := execution.WorkflowID timeStamp := execution.CurrentTimeStamp batch := db.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) err := insertWorkflowActiveClusterSelectionPolicyRow(batch, activeClusterSelectionPolicyRow, timeStamp) if err != nil { return err } err = insertOrUpsertWorkflowRequestRow(batch, requests, timeStamp) if err != nil { return err } err = createOrUpdateCurrentWorkflow(batch, shardID, domainID, workflowID, currentWorkflowRequest, timeStamp) if err != nil { return err } err = createWorkflowExecutionWithMergeMaps(batch, shardID, domainID, workflowID, execution, timeStamp) if err != nil { return err } createTasksByCategory(batch, shardID, domainID, workflowID, timeStamp, tasksByCategory) assertShardRangeID(batch, shardID, shardCondition.RangeID, timeStamp) return executeCreateWorkflowBatchTransaction(ctx, db.session, batch, currentWorkflowRequest, execution, shardCondition) } func (db *CDB) SelectCurrentWorkflow( ctx context.Context, shardID int, domainID, workflowID string, ) (*nosqlplugin.CurrentWorkflowRow, error) { query := db.session.Query(templateGetCurrentExecutionQuery, shardID, rowTypeExecution, domainID, workflowID, permanentRunID, defaultVisibilityTimestamp, rowTypeExecutionTaskID, ).WithContext(ctx) result := make(map[string]interface{}) if err := query.MapScan(result); err != nil { return nil, err } currentRunID := result["current_run_id"].(gocql.UUID).String() executionInfo, err := parseWorkflowExecutionInfo(result) if err != nil { return nil, err } lastWriteVersion := constants.EmptyVersion if result["workflow_last_write_version"] != nil { lastWriteVersion = result["workflow_last_write_version"].(int64) } return &nosqlplugin.CurrentWorkflowRow{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: currentRunID, CreateRequestID: executionInfo.CreateRequestID, State: executionInfo.State, CloseStatus: executionInfo.CloseStatus, LastWriteVersion: lastWriteVersion, }, nil } func (db *CDB) UpdateWorkflowExecutionWithTasks( ctx context.Context, requests *nosqlplugin.WorkflowRequestsWriteRequest, currentWorkflowRequest *nosqlplugin.CurrentWorkflowWriteRequest, mutatedExecution *nosqlplugin.WorkflowExecutionRequest, insertedExecution *nosqlplugin.WorkflowExecutionRequest, activeClusterSelectionPolicyRow *nosqlplugin.ActiveClusterSelectionPolicyRow, resetExecution *nosqlplugin.WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask, shardCondition *nosqlplugin.ShardCondition, ) error { shardID := shardCondition.ShardID var domainID, workflowID string var previousNextEventIDCondition int64 var timeStamp time.Time if mutatedExecution != nil { domainID = mutatedExecution.DomainID workflowID = mutatedExecution.WorkflowID previousNextEventIDCondition = *mutatedExecution.PreviousNextEventIDCondition timeStamp = mutatedExecution.CurrentTimeStamp } else if resetExecution != nil { domainID = resetExecution.DomainID workflowID = resetExecution.WorkflowID previousNextEventIDCondition = *resetExecution.PreviousNextEventIDCondition timeStamp = resetExecution.CurrentTimeStamp } else { return fmt.Errorf("at least one of mutatedExecution and resetExecution should be provided") } batch := db.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) err := insertOrUpsertWorkflowRequestRow(batch, requests, timeStamp) if err != nil { return err } err = createOrUpdateCurrentWorkflow(batch, shardID, domainID, workflowID, currentWorkflowRequest, timeStamp) if err != nil { return err } if mutatedExecution != nil { err = updateWorkflowExecutionAndEventBufferWithMergeAndDeleteMaps(batch, shardID, domainID, workflowID, mutatedExecution, timeStamp) if err != nil { return err } } if insertedExecution != nil { err = createWorkflowExecutionWithMergeMaps(batch, shardID, domainID, workflowID, insertedExecution, timeStamp) if err != nil { return err } err = insertWorkflowActiveClusterSelectionPolicyRow(batch, activeClusterSelectionPolicyRow, timeStamp) if err != nil { return err } } if resetExecution != nil { err = resetWorkflowExecutionAndMapsAndEventBuffer(batch, shardID, domainID, workflowID, resetExecution, timeStamp) if err != nil { return err } } createTasksByCategory(batch, shardID, domainID, workflowID, timeStamp, tasksByCategory) assertShardRangeID(batch, shardID, shardCondition.RangeID, timeStamp) return executeUpdateWorkflowBatchTransaction(ctx, db.session, batch, currentWorkflowRequest, previousNextEventIDCondition, shardCondition) } func (db *CDB) SelectWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) (*nosqlplugin.WorkflowExecution, error) { query := db.session.Query(templateGetWorkflowExecutionQuery, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID, ).WithContext(ctx) result := make(map[string]interface{}) if err := query.MapScan(result); err != nil { return nil, err } state := &nosqlplugin.WorkflowExecution{} info, err := parseWorkflowExecutionInfo(result) if err != nil { return nil, err } state.ExecutionInfo = info state.VersionHistories = persistence.NewDataBlob(result["version_histories"].([]byte), constants.EncodingType(result["version_histories_encoding"].(string))) // TODO: remove this after all 2DC workflows complete replicationState := parseReplicationState(result["replication_state"].(map[string]interface{})) state.ReplicationState = replicationState activityInfos := make(map[int64]*persistence.InternalActivityInfo) aMap := result["activity_map"].(map[int64]map[string]interface{}) for key, value := range aMap { info := parseActivityInfo(domainID, value) activityInfos[key] = info } state.ActivityInfos = activityInfos timerInfos := make(map[string]*persistence.TimerInfo) tMap := result["timer_map"].(map[string]map[string]interface{}) for key, value := range tMap { info := parseTimerInfo(value) timerInfos[key] = info } state.TimerInfos = timerInfos childExecutionInfos := make(map[int64]*persistence.InternalChildExecutionInfo) cMap := result["child_executions_map"].(map[int64]map[string]interface{}) for key, value := range cMap { info := parseChildExecutionInfo(value) childExecutionInfos[key] = info } state.ChildExecutionInfos = childExecutionInfos requestCancelInfos := make(map[int64]*persistence.RequestCancelInfo) rMap := result["request_cancel_map"].(map[int64]map[string]interface{}) for key, value := range rMap { info := parseRequestCancelInfo(value) requestCancelInfos[key] = info } state.RequestCancelInfos = requestCancelInfos signalInfos := make(map[int64]*persistence.SignalInfo) sMap := result["signal_map"].(map[int64]map[string]interface{}) for key, value := range sMap { info := parseSignalInfo(value) signalInfos[key] = info } state.SignalInfos = signalInfos signalRequestedIDs := make(map[string]struct{}) sList := mustConvertToSlice(result["signal_requested"]) for _, v := range sList { signalRequestedIDs[v.(gocql.UUID).String()] = struct{}{} } state.SignalRequestedIDs = signalRequestedIDs eList := result["buffered_events_list"].([]map[string]interface{}) bufferedEventsBlobs := make([]*persistence.DataBlob, 0, len(eList)) for _, v := range eList { blob := parseHistoryEventBatchBlob(v) bufferedEventsBlobs = append(bufferedEventsBlobs, blob) } state.BufferedEvents = bufferedEventsBlobs state.Checksum = parseChecksum(result["checksum"].(map[string]interface{})) return state, nil } func (db *CDB) DeleteCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID, currentRunIDCondition string) error { query := db.session.Query(templateDeleteWorkflowExecutionCurrentRowQuery, shardID, rowTypeExecution, domainID, workflowID, permanentRunID, defaultVisibilityTimestamp, rowTypeExecutionTaskID, currentRunIDCondition, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) DeleteWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) error { query := db.session.Query(templateDeleteWorkflowExecutionMutableStateQuery, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) SelectAllCurrentWorkflows(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.CurrentWorkflowExecution, []byte, error) { query := db.session.Query( templateListCurrentExecutionsQuery, shardID, rowTypeExecution, ).PageSize(pageSize).PageState(pageToken).WithContext(ctx) iter := query.Iter() if iter == nil { return nil, nil, &types.InternalServiceError{ Message: "SelectAllCurrentWorkflows operation failed. Not able to create query iterator.", } } result := make(map[string]interface{}) var executions []*persistence.CurrentWorkflowExecution for iter.MapScan(result) { runID := result["run_id"].(gocql.UUID).String() if runID != permanentRunID { result = make(map[string]interface{}) continue } executions = append(executions, &persistence.CurrentWorkflowExecution{ DomainID: result["domain_id"].(gocql.UUID).String(), WorkflowID: result["workflow_id"].(string), RunID: permanentRunID, State: result["workflow_state"].(int), CurrentRunID: result["current_run_id"].(gocql.UUID).String(), }) result = make(map[string]interface{}) } nextPageToken := getNextPageToken(iter) return executions, nextPageToken, iter.Close() } func (db *CDB) SelectAllWorkflowExecutions(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.InternalListConcreteExecutionsEntity, []byte, error) { query := db.session.Query( templateListWorkflowExecutionQuery, shardID, rowTypeExecution, ).PageSize(pageSize).PageState(pageToken).WithContext(ctx) iter := query.Iter() if iter == nil { return nil, nil, &types.InternalServiceError{ Message: "SelectAllWorkflowExecutions operation failed. Not able to create query iterator.", } } result := make(map[string]interface{}) var executions []*persistence.InternalListConcreteExecutionsEntity for iter.MapScan(result) { runID := result["run_id"].(gocql.UUID).String() if runID == permanentRunID { result = make(map[string]interface{}) continue } executionInfo, err := parseWorkflowExecutionInfo(result) if err != nil { return nil, nil, err } executions = append(executions, &persistence.InternalListConcreteExecutionsEntity{ ExecutionInfo: executionInfo, VersionHistories: persistence.NewDataBlob(result["version_histories"].([]byte), constants.EncodingType(result["version_histories_encoding"].(string))), }) result = make(map[string]interface{}) } nextPageToken := getNextPageToken(iter) return executions, nextPageToken, iter.Close() } func (db *CDB) IsWorkflowExecutionExists(ctx context.Context, shardID int, domainID, workflowID, runID string) (bool, error) { query := db.session.Query(templateIsWorkflowExecutionExistsQuery, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID, ).WithContext(ctx) result := make(map[string]interface{}) if err := query.MapScan(result); err != nil { if db.client.IsNotFoundError(err) { return false, nil } return false, err } return true, nil } func (db *CDB) SelectTransferTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { // Reading transfer tasks need to be quorum level consistent, otherwise we could loose task query := db.session.Query(templateGetTransferTasksQuery, shardID, rowTypeTransferTask, rowTypeTransferDomainID, rowTypeTransferWorkflowID, rowTypeTransferRunID, defaultVisibilityTimestamp, inclusiveMinTaskID, exclusiveMaxTaskID, ).PageSize(pageSize).PageState(pageToken).WithContext(ctx) iter := query.Iter() if iter == nil { return nil, nil, &types.InternalServiceError{ Message: "SelectTransferTasksOrderByTaskID operation failed. Not able to create query iterator.", } } var tasks []*nosqlplugin.HistoryMigrationTask task := make(map[string]interface{}) for iter.MapScan(task) { t := parseTransferTaskInfo(task["transfer"].(map[string]interface{})) taskID := task["task_id"].(int64) data := task["data"].([]byte) encoding := task["data_encoding"].(string) taskBlob := persistence.NewDataBlob(data, constants.EncodingType(encoding)) // Reset task map to get it ready for next scan task = make(map[string]interface{}) tasks = append(tasks, &nosqlplugin.HistoryMigrationTask{ Transfer: t, Task: taskBlob, TaskID: taskID, }) } nextPageToken := getNextPageToken(iter) err := iter.Close() return tasks, nextPageToken, err } func (db *CDB) DeleteTransferTask(ctx context.Context, shardID int, taskID int64) error { query := db.session.Query(templateCompleteTransferTaskQuery, shardID, rowTypeTransferTask, rowTypeTransferDomainID, rowTypeTransferWorkflowID, rowTypeTransferRunID, defaultVisibilityTimestamp, taskID, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) RangeDeleteTransferTasks(ctx context.Context, shardID int, exclusiveBeginTaskID, inclusiveEndTaskID int64) error { query := db.session.Query(templateRangeCompleteTransferTaskQuery, shardID, rowTypeTransferTask, rowTypeTransferDomainID, rowTypeTransferWorkflowID, rowTypeTransferRunID, defaultVisibilityTimestamp, exclusiveBeginTaskID, inclusiveEndTaskID, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) SelectTimerTasksOrderByVisibilityTime(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTime, exclusiveMaxTime time.Time) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { // Reading timer tasks need to be quorum level consistent, otherwise we could loose task minTimestamp := persistence.UnixNanoToDBTimestamp(inclusiveMinTime.UnixNano()) maxTimestamp := persistence.UnixNanoToDBTimestamp(exclusiveMaxTime.UnixNano()) query := db.session.Query(templateGetTimerTasksQuery, shardID, rowTypeTimerTask, rowTypeTimerDomainID, rowTypeTimerWorkflowID, rowTypeTimerRunID, minTimestamp, maxTimestamp, ).PageSize(pageSize).PageState(pageToken).WithContext(ctx) iter := query.Iter() if iter == nil { return nil, nil, &types.InternalServiceError{ Message: "SelectTimerTasksOrderByVisibilityTime operation failed. Not able to create query iterator.", } } var timers []*nosqlplugin.HistoryMigrationTask task := make(map[string]interface{}) for iter.MapScan(task) { t := parseTimerTaskInfo(task["timer"].(map[string]interface{})) taskID := task["task_id"].(int64) scheduledTime := task["visibility_ts"].(time.Time) data := task["data"].([]byte) encoding := task["data_encoding"].(string) taskBlob := persistence.NewDataBlob(data, constants.EncodingType(encoding)) // Reset task map to get it ready for next scan task = make(map[string]interface{}) timers = append(timers, &nosqlplugin.HistoryMigrationTask{ Timer: t, Task: taskBlob, TaskID: taskID, ScheduledTime: scheduledTime, }) } nextPageToken := getNextPageToken(iter) err := iter.Close() return timers, nextPageToken, err } func (db *CDB) DeleteTimerTask(ctx context.Context, shardID int, taskID int64, visibilityTimestamp time.Time) error { ts := persistence.UnixNanoToDBTimestamp(visibilityTimestamp.UnixNano()) query := db.session.Query(templateCompleteTimerTaskQuery, shardID, rowTypeTimerTask, rowTypeTimerDomainID, rowTypeTimerWorkflowID, rowTypeTimerRunID, ts, taskID, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) RangeDeleteTimerTasks(ctx context.Context, shardID int, inclusiveMinTime, exclusiveMaxTime time.Time) error { start := persistence.UnixNanoToDBTimestamp(inclusiveMinTime.UnixNano()) end := persistence.UnixNanoToDBTimestamp(exclusiveMaxTime.UnixNano()) query := db.session.Query(templateRangeCompleteTimerTaskQuery, shardID, rowTypeTimerTask, rowTypeTimerDomainID, rowTypeTimerWorkflowID, rowTypeTimerRunID, start, end, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) SelectReplicationTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { // Reading replication tasks need to be quorum level consistent, otherwise we could loose task query := db.session.Query(templateGetReplicationTasksQuery, shardID, rowTypeReplicationTask, rowTypeReplicationDomainID, rowTypeReplicationWorkflowID, rowTypeReplicationRunID, defaultVisibilityTimestamp, inclusiveMinTaskID, exclusiveMaxTaskID, ).PageSize(pageSize).PageState(pageToken).WithContext(ctx) return populateGetReplicationTasks(query) } func (db *CDB) DeleteReplicationTask(ctx context.Context, shardID int, taskID int64) error { query := db.session.Query(templateCompleteReplicationTaskQuery, shardID, rowTypeReplicationTask, rowTypeReplicationDomainID, rowTypeReplicationWorkflowID, rowTypeReplicationRunID, defaultVisibilityTimestamp, taskID, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) RangeDeleteReplicationTasks(ctx context.Context, shardID int, inclusiveEndTaskID int64) error { query := db.session.Query(templateCompleteReplicationTaskBeforeQuery, shardID, rowTypeReplicationTask, rowTypeReplicationDomainID, rowTypeReplicationWorkflowID, rowTypeReplicationRunID, defaultVisibilityTimestamp, inclusiveEndTaskID, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) DeleteCrossClusterTask(ctx context.Context, shardID int, targetCluster string, taskID int64) error { query := db.session.Query(templateCompleteCrossClusterTaskQuery, shardID, rowTypeCrossClusterTask, rowTypeCrossClusterDomainID, targetCluster, rowTypeCrossClusterRunID, defaultVisibilityTimestamp, taskID, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) InsertReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, replicationTask *nosqlplugin.HistoryMigrationTask) error { // Use source cluster name as the workflow id for replication dlq task := replicationTask.Replication taskBlob, taskEncoding := persistence.FromDataBlob(replicationTask.Task) query := db.session.Query(templateCreateReplicationTaskQuery, shardID, rowTypeDLQ, rowTypeDLQDomainID, sourceCluster, rowTypeDLQRunID, task.DomainID, task.WorkflowID, task.RunID, task.TaskID, task.TaskType, task.FirstEventID, task.NextEventID, task.Version, task.ScheduledID, persistence.EventStoreVersion, task.BranchToken, persistence.EventStoreVersion, task.NewRunBranchToken, defaultVisibilityTimestamp, taskBlob, taskEncoding, defaultVisibilityTimestamp, task.TaskID, task.CurrentTimeStamp, ).WithContext(ctx) return query.Exec() } func (db *CDB) SelectReplicationDLQTasksOrderByTaskID(ctx context.Context, shardID int, sourceCluster string, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { // Reading replication tasks need to be quorum level consistent, otherwise we could loose task query := db.session.Query(templateGetReplicationTasksQuery, shardID, rowTypeDLQ, rowTypeDLQDomainID, sourceCluster, rowTypeDLQRunID, defaultVisibilityTimestamp, inclusiveMinTaskID, exclusiveMaxTaskID, ).PageSize(pageSize).PageState(pageToken).WithContext(ctx) return populateGetReplicationTasks(query) } func (db *CDB) SelectReplicationDLQTasksCount(ctx context.Context, shardID int, sourceCluster string) (int64, error) { // Reading replication tasks need to be quorum level consistent, otherwise we could loose task query := db.session.Query(templateGetDLQSizeQuery, shardID, rowTypeDLQ, rowTypeDLQDomainID, sourceCluster, rowTypeDLQRunID, ).WithContext(ctx) result := make(map[string]interface{}) if err := query.MapScan(result); err != nil { return -1, err } queueSize := result["count"].(int64) return queueSize, nil } func (db *CDB) DeleteReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, taskID int64) error { query := db.session.Query(templateCompleteReplicationTaskQuery, shardID, rowTypeDLQ, rowTypeDLQDomainID, sourceCluster, rowTypeDLQRunID, defaultVisibilityTimestamp, taskID, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) RangeDeleteReplicationDLQTasks(ctx context.Context, shardID int, sourceCluster string, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { query := db.session.Query(templateRangeCompleteReplicationTaskQuery, shardID, rowTypeDLQ, rowTypeDLQDomainID, sourceCluster, rowTypeDLQRunID, defaultVisibilityTimestamp, inclusiveBeginTaskID, exclusiveEndTaskID, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } func (db *CDB) InsertReplicationTask(ctx context.Context, tasks []*nosqlplugin.HistoryMigrationTask, shardCondition nosqlplugin.ShardCondition) error { if len(tasks) == 0 { return nil } shardID := shardCondition.ShardID batch := db.session.NewBatch(gocql.LoggedBatch).WithContext(ctx) timeStamp := tasks[0].Replication.CurrentTimeStamp for _, task := range tasks { createReplicationTasks(batch, shardID, task.Replication.DomainID, task.Replication.WorkflowID, []*nosqlplugin.HistoryMigrationTask{task}, timeStamp) } assertShardRangeID(batch, shardID, shardCondition.RangeID, timeStamp) previous := make(map[string]interface{}) applied, iter, err := db.session.MapExecuteBatchCAS(batch, previous) defer func() { if iter != nil { _ = iter.Close() } }() if err != nil { return err } if !applied { rowType, ok := previous["type"].(int) if !ok { // This should never happen, as all our rows have the type field. panic("Encounter row type not found") } if rowType == rowTypeShard { if actualRangeID, ok := previous["range_id"].(int64); ok && actualRangeID != shardCondition.RangeID { // CreateWorkflowExecution failed because rangeID was modified return &nosqlplugin.ShardOperationConditionFailure{ RangeID: actualRangeID, } } } // At this point we only know that the write was not applied. // It's much safer to return ShardOperationConditionFailure(which will become ShardOwnershipLostError later) as the default to force the application to reload // shard to recover from such errors var columns []string for k, v := range previous { columns = append(columns, fmt.Sprintf("%s=%v", k, v)) } return &nosqlplugin.ShardOperationConditionFailure{ RangeID: -1, Details: strings.Join(columns, ","), } } return nil } func (db *CDB) SelectActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, wfID, rID string) (*nosqlplugin.ActiveClusterSelectionPolicyRow, error) { query := db.session.Query(templateGetActiveClusterSelectionPolicyQuery, shardID, rowTypeWorkflowActiveClusterSelectionPolicy, domainID, wfID, rID, defaultVisibilityTimestamp, rowTypeWorkflowActiveClusterSelectionVersion, ).WithContext(ctx) result := make(map[string]interface{}) if err := query.MapScan(result); err != nil { if db.client.IsNotFoundError(err) { return nil, nil } return nil, err } return &nosqlplugin.ActiveClusterSelectionPolicyRow{ ShardID: shardID, DomainID: domainID, WorkflowID: wfID, RunID: rID, Policy: persistence.NewDataBlob(result["data"].([]byte), constants.EncodingType(result["data_encoding"].(string))), }, nil } func (db *CDB) DeleteActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, workflowID, runID string) error { query := db.session.Query(templateDeleteActiveClusterSelectionPolicyQuery, shardID, rowTypeWorkflowActiveClusterSelectionPolicy, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeWorkflowActiveClusterSelectionVersion, ).WithContext(ctx) return db.executeWithConsistencyAll(query) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/workflow_cql.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra const ( templateWorkflowExecutionType = `{` + `domain_id: ?, ` + `workflow_id: ?, ` + `run_id: ?, ` + `first_run_id: ?, ` + `parent_domain_id: ?, ` + `parent_workflow_id: ?, ` + `parent_run_id: ?, ` + `initiated_id: ?, ` + `completion_event_batch_id: ?, ` + `completion_event: ?, ` + `completion_event_data_encoding: ?, ` + `task_list: ?, ` + `task_list_kind: ?, ` + `workflow_type_name: ?, ` + `workflow_timeout: ?, ` + `decision_task_timeout: ?, ` + `execution_context: ?, ` + `state: ?, ` + `close_status: ?, ` + `last_first_event_id: ?, ` + `last_event_task_id: ?, ` + `next_event_id: ?, ` + `last_processed_event: ?, ` + `start_time: ?, ` + `last_updated_time: ?, ` + `create_request_id: ?, ` + `signal_count: ?, ` + `history_size: ?, ` + `decision_version: ?, ` + `decision_schedule_id: ?, ` + `decision_started_id: ?, ` + `decision_request_id: ?, ` + `decision_timeout: ?, ` + `decision_attempt: ?, ` + `decision_timestamp: ?, ` + `decision_scheduled_timestamp: ?, ` + `decision_original_scheduled_timestamp: ?, ` + `cancel_requested: ?, ` + `cancel_request_id: ?, ` + `sticky_task_list: ?, ` + `sticky_schedule_to_start_timeout: ?,` + `client_library_version: ?, ` + `client_feature_version: ?, ` + `client_impl: ?, ` + `auto_reset_points: ?, ` + `auto_reset_points_encoding: ?, ` + `attempt: ?, ` + `has_retry_policy: ?, ` + `init_interval: ?, ` + `backoff_coefficient: ?, ` + `max_interval: ?, ` + `expiration_time: ?, ` + `max_attempts: ?, ` + `non_retriable_errors: ?, ` + `event_store_version: ?, ` + `branch_token: ?, ` + `cron_schedule: ?, ` + `cron_overlap_policy: ?, ` + `expiration_seconds: ?, ` + `search_attributes: ?, ` + `memo: ?, ` + `partition_config: ?, ` + `active_cluster_selection_policy: ?, ` + `active_cluster_selection_policy_encoding: ?` + `}` templateTransferTaskType = `{` + `domain_id: ?, ` + `workflow_id: ?, ` + `run_id: ?, ` + `visibility_ts: ?, ` + `task_id: ?, ` + `target_domain_id: ?, ` + `target_domain_ids: ?,` + `target_workflow_id: ?, ` + `target_run_id: ?, ` + `target_child_workflow_only: ?, ` + `task_list: ?, ` + `type: ?, ` + `schedule_id: ?, ` + `record_visibility: ?, ` + `version: ?, ` + `original_task_list: ?, ` + `original_task_list_kind: ?` + `}` templateCrossClusterTaskType = templateTransferTaskType templateReplicationTaskType = `{` + `domain_id: ?, ` + `workflow_id: ?, ` + `run_id: ?, ` + `task_id: ?, ` + `type: ?, ` + `first_event_id: ?,` + `next_event_id: ?,` + `version: ?,` + `scheduled_id: ?, ` + `event_store_version: ?, ` + `branch_token: ?, ` + `new_run_event_store_version: ?, ` + `new_run_branch_token: ?, ` + `created_time: ? ` + `}` templateTimerTaskType = `{` + `domain_id: ?, ` + `workflow_id: ?, ` + `run_id: ?, ` + `visibility_ts: ?, ` + `task_id: ?, ` + `type: ?, ` + `timeout_type: ?, ` + `event_id: ?, ` + `schedule_attempt: ?, ` + `version: ?, ` + `task_list: ?` + `}` templateActivityInfoType = `{` + `version: ?, ` + `schedule_id: ?, ` + `scheduled_event_batch_id: ?, ` + `scheduled_event: ?, ` + `scheduled_time: ?, ` + `started_id: ?, ` + `started_event: ?, ` + `started_time: ?, ` + `activity_id: ?, ` + `request_id: ?, ` + `details: ?, ` + `schedule_to_start_timeout: ?, ` + `schedule_to_close_timeout: ?, ` + `start_to_close_timeout: ?, ` + `heart_beat_timeout: ?, ` + `cancel_requested: ?, ` + `cancel_request_id: ?, ` + `last_hb_updated_time: ?, ` + `timer_task_status: ?, ` + `attempt: ?, ` + `task_list: ?, ` + `task_list_kind: ?, ` + `started_identity: ?, ` + `has_retry_policy: ?, ` + `init_interval: ?, ` + `backoff_coefficient: ?, ` + `max_interval: ?, ` + `expiration_time: ?, ` + `max_attempts: ?, ` + `non_retriable_errors: ?, ` + `last_failure_reason: ?, ` + `last_worker_identity: ?, ` + `last_failure_details: ?, ` + `event_data_encoding: ?` + `}` templateTimerInfoType = `{` + `version: ?, ` + `timer_id: ?, ` + `started_id: ?, ` + `expiry_time: ?, ` + `task_id: ?` + `}` templateChildExecutionInfoType = `{` + `version: ?, ` + `initiated_id: ?, ` + `initiated_event_batch_id: ?, ` + `initiated_event: ?, ` + `started_id: ?, ` + `started_workflow_id: ?, ` + `started_run_id: ?, ` + `started_event: ?, ` + `create_request_id: ?, ` + `event_data_encoding: ?, ` + `domain_id: ?, ` + `domain_name: ?, ` + `workflow_type_name: ?, ` + `parent_close_policy: ?` + `}` templateRequestCancelInfoType = `{` + `version: ?,` + `initiated_id: ?, ` + `initiated_event_batch_id: ?, ` + `cancel_request_id: ? ` + `}` templateSignalInfoType = `{` + `version: ?, ` + `initiated_id: ?, ` + `initiated_event_batch_id: ?, ` + `signal_request_id: ?, ` + `signal_name: ?, ` + `input: ?, ` + `control: ?` + `}` templateChecksumType = `{` + `version: ?, ` + `flavor: ?, ` + `value: ? ` + `}` templateUpdateCurrentWorkflowExecutionQuery = `UPDATE executions USING TTL 0 ` + `SET current_run_id = ?, ` + `execution = {run_id: ?, create_request_id: ?, state: ?, close_status: ?}, ` + `workflow_last_write_version = ?, ` + `workflow_state = ?, ` + `last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` + `IF current_run_id = ? ` templateUpdateCurrentWorkflowExecutionForNewQuery = templateUpdateCurrentWorkflowExecutionQuery + `and workflow_last_write_version = ? ` + `and workflow_state = ? ` templateInsertWorkflowRequestQuery = `INSERT INTO executions (` + `shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id, current_run_id, created_time) ` + `VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) IF NOT EXISTS USING TTL ?` templateUpsertWorkflowRequestQuery = `INSERT INTO executions (` + `shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id, current_run_id, last_updated_time) ` + `VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) USING TTL ?` templateInsertWorkflowActiveClusterSelectionPolicyRowQuery = `INSERT INTO executions (` + `shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id, created_time, data, data_encoding) ` + `VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) IF NOT EXISTS` templateGetActiveClusterSelectionPolicyQuery = `SELECT data, data_encoding ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ?` templateGetLatestWorkflowRequestQuery = `SELECT current_run_id ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `LIMIT 1` templateCreateCurrentWorkflowExecutionQuery = `INSERT INTO executions (` + `shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id, current_run_id, execution, workflow_last_write_version, workflow_state, created_time) ` + `VALUES(?, ?, ?, ?, ?, ?, ?, ?, {run_id: ?, create_request_id: ?, state: ?, close_status: ?}, ?, ?, ?) IF NOT EXISTS USING TTL 0 ` templateCreateWorkflowExecutionWithVersionHistoriesQuery = `INSERT INTO executions (` + `shard_id, domain_id, workflow_id, run_id, type, execution, next_event_id, visibility_ts, task_id, version_histories, version_histories_encoding, checksum, workflow_last_write_version, workflow_state, created_time) ` + `VALUES(?, ?, ?, ?, ?, ` + templateWorkflowExecutionType + `, ?, ?, ?, ?, ?, ` + templateChecksumType + `, ?, ?, ?) IF NOT EXISTS ` templateCreateTransferTaskQuery = `INSERT INTO executions (` + `shard_id, type, domain_id, workflow_id, run_id, transfer, data, data_encoding, visibility_ts, task_id, created_time) ` + `VALUES(?, ?, ?, ?, ?, ` + templateTransferTaskType + `, ?, ?, ?, ?, ?)` templateCreateReplicationTaskQuery = `INSERT INTO executions (` + `shard_id, type, domain_id, workflow_id, run_id, replication, data, data_encoding, visibility_ts, task_id, created_time) ` + `VALUES(?, ?, ?, ?, ?, ` + templateReplicationTaskType + `, ?, ?, ?, ?, ?)` templateCreateTimerTaskQuery = `INSERT INTO executions (` + `shard_id, type, domain_id, workflow_id, run_id, timer, data, data_encoding, visibility_ts, task_id, created_time) ` + `VALUES(?, ?, ?, ?, ?, ` + templateTimerTaskType + `, ?, ?, ?, ?, ?)` templateUpdateLeaseQuery = `UPDATE executions ` + `SET range_id = ? ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` + `IF range_id = ?` // TODO: remove replication_state after all 2DC workflows complete templateGetWorkflowExecutionQuery = `SELECT execution, replication_state, activity_map, timer_map, ` + `child_executions_map, request_cancel_map, signal_map, signal_requested, buffered_events_list, ` + `buffered_replication_tasks_map, version_histories, version_histories_encoding, checksum, ` + `next_event_id ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ?` templateGetCurrentExecutionQuery = `SELECT current_run_id, execution, workflow_last_write_version ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ?` templateListCurrentExecutionsQuery = `SELECT domain_id, workflow_id, run_id, current_run_id, workflow_state ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ?` templateIsWorkflowExecutionExistsQuery = `SELECT shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ?` templateListWorkflowExecutionQuery = `SELECT run_id, execution, version_histories, version_histories_encoding ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ?` templateUpdateWorkflowExecutionWithVersionHistoriesQuery = `UPDATE executions ` + `SET execution = ` + templateWorkflowExecutionType + `, next_event_id = ? ` + `, version_histories = ? ` + `, version_histories_encoding = ? ` + `, checksum = ` + templateChecksumType + `, workflow_last_write_version = ? ` + `, workflow_state = ? ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` + `IF next_event_id = ? ` templateUpdateActivityInfoQuery = `UPDATE executions ` + `SET activity_map[ ? ] = ` + templateActivityInfoType + ` ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateResetActivityInfoQuery = `UPDATE executions ` + `SET activity_map = ? ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateUpdateTimerInfoQuery = `UPDATE executions ` + `SET timer_map[ ? ] = ` + templateTimerInfoType + ` ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateResetTimerInfoQuery = `UPDATE executions ` + `SET timer_map = ? ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateUpdateChildExecutionInfoQuery = `UPDATE executions ` + `SET child_executions_map[ ? ] = ` + templateChildExecutionInfoType + ` ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateResetChildExecutionInfoQuery = `UPDATE executions ` + `SET child_executions_map = ?` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateUpdateRequestCancelInfoQuery = `UPDATE executions ` + `SET request_cancel_map[ ? ] = ` + templateRequestCancelInfoType + ` ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateResetRequestCancelInfoQuery = `UPDATE executions ` + `SET request_cancel_map = ?` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateUpdateSignalInfoQuery = `UPDATE executions ` + `SET signal_map[ ? ] = ` + templateSignalInfoType + ` ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateResetSignalInfoQuery = `UPDATE executions ` + `SET signal_map = ? ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateUpdateSignalRequestedQuery = `UPDATE executions ` + `SET signal_requested = signal_requested + ? ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateResetSignalRequestedQuery = `UPDATE executions ` + `SET signal_requested = ? ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateAppendBufferedEventsQuery = `UPDATE executions ` + `SET buffered_events_list = buffered_events_list + ? ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateDeleteBufferedEventsQuery = `UPDATE executions ` + `SET buffered_events_list = [] ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateDeleteActivityInfoQuery = `DELETE activity_map[ ? ] ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateDeleteTimerInfoQuery = `DELETE timer_map[ ? ] ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateDeleteChildExecutionInfoQuery = `DELETE child_executions_map[ ? ] ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateDeleteRequestCancelInfoQuery = `DELETE request_cancel_map[ ? ] ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateDeleteSignalInfoQuery = `DELETE signal_map[ ? ] ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateDeleteWorkflowExecutionMutableStateQuery = `DELETE FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateDeleteWorkflowExecutionCurrentRowQuery = templateDeleteWorkflowExecutionMutableStateQuery + " if current_run_id = ? " templateDeleteWorkflowExecutionSignalRequestedQuery = `UPDATE executions ` + `SET signal_requested = signal_requested - ? ` + `, last_updated_time = ? ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ? ` templateDeleteActiveClusterSelectionPolicyQuery = `DELETE FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ?` templateGetTransferTasksQuery = `SELECT task_id, transfer, data, data_encoding ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id >= ? ` + `and task_id < ?` templateGetReplicationTasksQuery = `SELECT task_id, replication, data, data_encoding ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id >= ? ` + `and task_id < ?` templateGetDLQSizeQuery = `SELECT count(1) as count ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ?` templateCompleteTransferTaskQuery = `DELETE FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id = ?` templateRangeCompleteTransferTaskQuery = `DELETE FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id >= ? ` + `and task_id < ?` templateCompleteCrossClusterTaskQuery = templateCompleteTransferTaskQuery templateCompleteReplicationTaskBeforeQuery = `DELETE FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ? ` + `and run_id = ? ` + `and visibility_ts = ? ` + `and task_id < ?` templateCompleteReplicationTaskQuery = templateCompleteTransferTaskQuery templateRangeCompleteReplicationTaskQuery = templateRangeCompleteTransferTaskQuery templateGetTimerTasksQuery = `SELECT visibility_ts, task_id, timer, data, data_encoding ` + `FROM executions ` + `WHERE shard_id = ? ` + `and type = ?` + `and domain_id = ? ` + `and workflow_id = ?` + `and run_id = ?` + `and visibility_ts >= ? ` + `and visibility_ts < ?` templateCompleteTimerTaskQuery = `DELETE FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ?` + `and run_id = ?` + `and visibility_ts = ? ` + `and task_id = ?` templateRangeCompleteTimerTaskQuery = `DELETE FROM executions ` + `WHERE shard_id = ? ` + `and type = ? ` + `and domain_id = ? ` + `and workflow_id = ?` + `and run_id = ?` + `and visibility_ts >= ? ` + `and visibility_ts < ?` ) ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/workflow_parsing_utils.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "fmt" "time" cql "github.com/gocql/gocql" "github.com/uber/cadence/common" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/types" ) var _emptyUUID = cql.UUID{} func parseWorkflowExecutionInfo(result map[string]interface{}) (*persistence.InternalWorkflowExecutionInfo, error) { executionBlob, ok := result["execution"].(map[string]interface{}) if !ok { return nil, fmt.Errorf("invalid execution blob format: missing or invalid 'execution' field") } info := &persistence.InternalWorkflowExecutionInfo{} var completionEventData []byte var completionEventEncoding constants.EncodingType var autoResetPoints []byte var autoResetPointsEncoding constants.EncodingType var activeClusterSelectionPolicy []byte var activeClusterSelectionPolicyEncoding constants.EncodingType for k, v := range executionBlob { switch k { case "domain_id": info.DomainID = v.(gocql.UUID).String() case "workflow_id": info.WorkflowID = v.(string) case "run_id": info.RunID = v.(gocql.UUID).String() case "first_run_id": info.FirstExecutionRunID = v.(gocql.UUID).String() if info.FirstExecutionRunID == _emptyUUID.String() { // for backward compatibility, the gocql library doesn't handle the null uuid correectly https://github.com/gocql/gocql/blob/master/marshal.go#L1807 info.FirstExecutionRunID = "" } else if info.FirstExecutionRunID == emptyRunID { info.FirstExecutionRunID = "" } case "parent_domain_id": info.ParentDomainID = v.(gocql.UUID).String() if info.ParentDomainID == emptyDomainID { info.ParentDomainID = "" } case "parent_workflow_id": info.ParentWorkflowID = v.(string) case "parent_run_id": info.ParentRunID = v.(gocql.UUID).String() if info.ParentRunID == emptyRunID { info.ParentRunID = "" } case "initiated_id": info.InitiatedID = v.(int64) case "completion_event_batch_id": info.CompletionEventBatchID = v.(int64) case "completion_event": completionEventData = v.([]byte) case "completion_event_data_encoding": completionEventEncoding = constants.EncodingType(v.(string)) case "auto_reset_points": autoResetPoints = v.([]byte) case "auto_reset_points_encoding": autoResetPointsEncoding = constants.EncodingType(v.(string)) case "task_list": info.TaskList = v.(string) case "task_list_kind": info.TaskListKind = types.TaskListKind(int32(v.(int))) case "workflow_type_name": info.WorkflowTypeName = v.(string) case "workflow_timeout": info.WorkflowTimeout = common.SecondsToDuration(int64(v.(int))) case "decision_task_timeout": info.DecisionStartToCloseTimeout = common.SecondsToDuration(int64(v.(int))) case "execution_context": info.ExecutionContext = v.([]byte) case "state": info.State = v.(int) case "close_status": info.CloseStatus = v.(int) case "last_first_event_id": info.LastFirstEventID = v.(int64) case "last_event_task_id": info.LastEventTaskID = v.(int64) case "last_processed_event": info.LastProcessedEvent = v.(int64) case "start_time": info.StartTimestamp = v.(time.Time) case "last_updated_time": info.LastUpdatedTimestamp = v.(time.Time) case "create_request_id": info.CreateRequestID = v.(gocql.UUID).String() case "signal_count": info.SignalCount = int32(v.(int)) case "history_size": info.HistorySize = v.(int64) case "decision_version": info.DecisionVersion = v.(int64) case "decision_schedule_id": info.DecisionScheduleID = v.(int64) case "decision_started_id": info.DecisionStartedID = v.(int64) case "decision_request_id": info.DecisionRequestID = v.(string) case "decision_timeout": info.DecisionTimeout = common.SecondsToDuration(int64(v.(int))) case "decision_attempt": info.DecisionAttempt = v.(int64) case "decision_timestamp": info.DecisionStartedTimestamp = time.Unix(0, v.(int64)) case "decision_scheduled_timestamp": info.DecisionScheduledTimestamp = time.Unix(0, v.(int64)) case "decision_original_scheduled_timestamp": info.DecisionOriginalScheduledTimestamp = time.Unix(0, v.(int64)) case "cancel_requested": info.CancelRequested = v.(bool) case "cancel_request_id": info.CancelRequestID = v.(string) case "sticky_task_list": info.StickyTaskList = v.(string) case "sticky_schedule_to_start_timeout": info.StickyScheduleToStartTimeout = common.SecondsToDuration(int64(v.(int))) case "client_library_version": info.ClientLibraryVersion = v.(string) case "client_feature_version": info.ClientFeatureVersion = v.(string) case "client_impl": info.ClientImpl = v.(string) case "attempt": info.Attempt = int32(v.(int)) case "has_retry_policy": info.HasRetryPolicy = v.(bool) case "init_interval": info.InitialInterval = common.SecondsToDuration(int64(v.(int))) case "backoff_coefficient": info.BackoffCoefficient = v.(float64) case "max_interval": info.MaximumInterval = common.SecondsToDuration(int64(v.(int))) case "max_attempts": info.MaximumAttempts = int32(v.(int)) case "expiration_time": info.ExpirationTime = v.(time.Time) case "non_retriable_errors": info.NonRetriableErrors = v.([]string) case "branch_token": info.BranchToken = v.([]byte) case "cron_schedule": info.CronSchedule = v.(string) case "expiration_seconds": info.ExpirationInterval = common.SecondsToDuration(int64(v.(int))) case "search_attributes": info.SearchAttributes = v.(map[string][]byte) case "memo": info.Memo = v.(map[string][]byte) case "partition_config": info.PartitionConfig = v.(map[string]string) case "active_cluster_selection_policy": activeClusterSelectionPolicy = v.([]byte) case "active_cluster_selection_policy_encoding": activeClusterSelectionPolicyEncoding = constants.EncodingType(v.(string)) case "cron_overlap_policy": info.CronOverlapPolicy = types.CronOverlapPolicy(int32(v.(int))) } } info.CompletionEvent = persistence.NewDataBlob(completionEventData, completionEventEncoding) info.AutoResetPoints = persistence.NewDataBlob(autoResetPoints, autoResetPointsEncoding) info.ActiveClusterSelectionPolicy = persistence.NewDataBlob(activeClusterSelectionPolicy, activeClusterSelectionPolicyEncoding) if nextEventID, ok := result["next_event_id"].(int64); ok { info.NextEventID = nextEventID } return info, nil } // TODO: remove this after all 2DC workflows complete func parseReplicationState( result map[string]interface{}, ) *persistence.ReplicationState { if len(result) == 0 { return nil } info := &persistence.ReplicationState{} for k, v := range result { switch k { case "current_version": info.CurrentVersion = v.(int64) case "start_version": info.StartVersion = v.(int64) case "last_write_version": info.LastWriteVersion = v.(int64) case "last_write_event_id": info.LastWriteEventID = v.(int64) case "last_replication_info": info.LastReplicationInfo = make(map[string]*persistence.ReplicationInfo) replicationInfoMap := v.(map[string]map[string]interface{}) for key, value := range replicationInfoMap { info.LastReplicationInfo[key] = parseReplicationInfo(value) } } } return info } func parseReplicationInfo( result map[string]interface{}, ) *persistence.ReplicationInfo { info := &persistence.ReplicationInfo{} for k, v := range result { switch k { case "version": info.Version = v.(int64) case "last_event_id": info.LastEventID = v.(int64) } } return info } func parseActivityInfo( domainID string, result map[string]interface{}, ) *persistence.InternalActivityInfo { info := &persistence.InternalActivityInfo{} var sharedEncoding constants.EncodingType var scheduledEventData, startedEventData []byte for k, v := range result { switch k { case "version": info.Version = v.(int64) case "schedule_id": info.ScheduleID = v.(int64) case "scheduled_event_batch_id": info.ScheduledEventBatchID = v.(int64) case "scheduled_event": scheduledEventData = v.([]byte) case "scheduled_time": info.ScheduledTime = v.(time.Time) case "started_id": info.StartedID = v.(int64) case "started_event": startedEventData = v.([]byte) case "started_time": info.StartedTime = v.(time.Time) case "activity_id": info.ActivityID = v.(string) case "request_id": info.RequestID = v.(string) case "details": info.Details = v.([]byte) case "schedule_to_start_timeout": info.ScheduleToStartTimeout = common.SecondsToDuration(int64(v.(int))) case "schedule_to_close_timeout": info.ScheduleToCloseTimeout = common.SecondsToDuration(int64(v.(int))) case "start_to_close_timeout": info.StartToCloseTimeout = common.SecondsToDuration(int64(v.(int))) case "heart_beat_timeout": info.HeartbeatTimeout = common.SecondsToDuration(int64(v.(int))) case "cancel_requested": info.CancelRequested = v.(bool) case "cancel_request_id": info.CancelRequestID = v.(int64) case "last_hb_updated_time": info.LastHeartBeatUpdatedTime = v.(time.Time) case "timer_task_status": info.TimerTaskStatus = int32(v.(int)) case "attempt": info.Attempt = int32(v.(int)) case "task_list": info.TaskList = v.(string) case "task_list_kind": info.TaskListKind = types.TaskListKind(int32(v.(int))) case "started_identity": info.StartedIdentity = v.(string) case "has_retry_policy": info.HasRetryPolicy = v.(bool) case "init_interval": info.InitialInterval = common.SecondsToDuration(int64(v.(int))) case "backoff_coefficient": info.BackoffCoefficient = v.(float64) case "max_interval": info.MaximumInterval = common.SecondsToDuration(int64(v.(int))) case "max_attempts": info.MaximumAttempts = (int32)(v.(int)) case "expiration_time": info.ExpirationTime = v.(time.Time) case "non_retriable_errors": info.NonRetriableErrors = v.([]string) case "last_failure_reason": info.LastFailureReason = v.(string) case "last_worker_identity": info.LastWorkerIdentity = v.(string) case "last_failure_details": info.LastFailureDetails = v.([]byte) case "event_data_encoding": sharedEncoding = constants.EncodingType(v.(string)) } } info.DomainID = domainID info.ScheduledEvent = persistence.NewDataBlob(scheduledEventData, sharedEncoding) info.StartedEvent = persistence.NewDataBlob(startedEventData, sharedEncoding) return info } func parseTimerInfo( result map[string]interface{}, ) *persistence.TimerInfo { info := &persistence.TimerInfo{} for k, v := range result { switch k { case "version": info.Version = v.(int64) case "timer_id": info.TimerID = v.(string) case "started_id": info.StartedID = v.(int64) case "expiry_time": info.ExpiryTime = v.(time.Time) case "task_id": // task_id is a misleading variable, it actually serves // the purpose of indicating whether a timer task is // generated for this timer info info.TaskStatus = v.(int64) } } return info } func parseChildExecutionInfo( result map[string]interface{}, ) *persistence.InternalChildExecutionInfo { info := &persistence.InternalChildExecutionInfo{} var encoding constants.EncodingType var initiatedData []byte var startedData []byte for k, v := range result { switch k { case "version": info.Version = v.(int64) case "initiated_id": info.InitiatedID = v.(int64) case "initiated_event_batch_id": info.InitiatedEventBatchID = v.(int64) case "initiated_event": initiatedData = v.([]byte) case "started_id": info.StartedID = v.(int64) case "started_workflow_id": info.StartedWorkflowID = v.(string) case "started_run_id": info.StartedRunID = v.(gocql.UUID).String() case "started_event": startedData = v.([]byte) case "create_request_id": info.CreateRequestID = v.(gocql.UUID).String() case "event_data_encoding": encoding = constants.EncodingType(v.(string)) case "domain_id": info.DomainID = v.(gocql.UUID).String() if info.DomainID == _emptyUUID.String() { // for backward compatibility, the gocql library doesn't handle the null uuid correectly https://github.com/gocql/gocql/blob/master/marshal.go#L1807 info.DomainID = "" } case "domain_name": info.DomainNameDEPRECATED = v.(string) case "workflow_type_name": info.WorkflowTypeName = v.(string) case "parent_close_policy": info.ParentClosePolicy = types.ParentClosePolicy(v.(int)) } } info.InitiatedEvent = persistence.NewDataBlob(initiatedData, encoding) info.StartedEvent = persistence.NewDataBlob(startedData, encoding) return info } func parseRequestCancelInfo( result map[string]interface{}, ) *persistence.RequestCancelInfo { info := &persistence.RequestCancelInfo{} for k, v := range result { switch k { case "version": info.Version = v.(int64) case "initiated_id": info.InitiatedID = v.(int64) case "initiated_event_batch_id": info.InitiatedEventBatchID = v.(int64) case "cancel_request_id": info.CancelRequestID = v.(string) } } return info } func parseSignalInfo( result map[string]interface{}, ) *persistence.SignalInfo { info := &persistence.SignalInfo{} for k, v := range result { switch k { case "version": info.Version = v.(int64) case "initiated_id": info.InitiatedID = v.(int64) case "initiated_event_batch_id": info.InitiatedEventBatchID = v.(int64) case "signal_request_id": info.SignalRequestID = v.(gocql.UUID).String() case "signal_name": info.SignalName = v.(string) case "input": info.Input = v.([]byte) case "control": info.Control = v.([]byte) } } return info } func parseHistoryEventBatchBlob( result map[string]interface{}, ) *persistence.DataBlob { eventBatch := &persistence.DataBlob{Encoding: constants.EncodingTypeJSON} for k, v := range result { switch k { case "encoding_type": eventBatch.Encoding = constants.EncodingType(v.(string)) case "data": eventBatch.Data = v.([]byte) } } return eventBatch } func parseTimerTaskInfo( result map[string]interface{}, ) *persistence.TimerTaskInfo { info := &persistence.TimerTaskInfo{} for k, v := range result { switch k { case "domain_id": info.DomainID = v.(gocql.UUID).String() case "workflow_id": info.WorkflowID = v.(string) case "run_id": info.RunID = v.(gocql.UUID).String() case "visibility_ts": info.VisibilityTimestamp = v.(time.Time) case "task_id": info.TaskID = v.(int64) case "type": info.TaskType = v.(int) case "timeout_type": info.TimeoutType = v.(int) case "event_id": info.EventID = v.(int64) case "schedule_attempt": info.ScheduleAttempt = v.(int64) case "version": info.Version = v.(int64) case "task_list": info.TaskList = v.(string) } } return info } func parseTransferTaskInfo( result map[string]interface{}, ) *persistence.TransferTaskInfo { info := &persistence.TransferTaskInfo{} for k, v := range result { switch k { case "domain_id": info.DomainID = v.(gocql.UUID).String() case "workflow_id": info.WorkflowID = v.(string) case "run_id": info.RunID = v.(gocql.UUID).String() case "visibility_ts": info.VisibilityTimestamp = v.(time.Time) case "task_id": info.TaskID = v.(int64) case "target_domain_id": info.TargetDomainID = v.(gocql.UUID).String() case "target_domain_ids": targetDomainIDs := make(map[string]struct{}) dList := mustConvertToSlice(result["target_domain_ids"]) for _, v := range dList { targetDomainIDs[v.(gocql.UUID).String()] = struct{}{} } info.TargetDomainIDs = targetDomainIDs case "target_workflow_id": info.TargetWorkflowID = v.(string) case "target_run_id": info.TargetRunID = v.(gocql.UUID).String() if info.TargetRunID == persistence.TransferTaskTransferTargetRunID { info.TargetRunID = "" } case "target_child_workflow_only": info.TargetChildWorkflowOnly = v.(bool) case "task_list": info.TaskList = v.(string) case "type": info.TaskType = v.(int) case "schedule_id": info.ScheduleID = v.(int64) case "record_visibility": info.RecordVisibility = v.(bool) case "version": info.Version = v.(int64) case "original_task_list": info.OriginalTaskList = v.(string) case "original_task_list_kind": info.OriginalTaskListKind = types.TaskListKind(int32(v.(int))) } } return info } func parseReplicationTaskInfo( result map[string]interface{}, ) *nosqlplugin.ReplicationTask { info := &persistence.InternalReplicationTaskInfo{} for k, v := range result { switch k { case "domain_id": info.DomainID = v.(gocql.UUID).String() case "workflow_id": info.WorkflowID = v.(string) case "run_id": info.RunID = v.(gocql.UUID).String() case "task_id": info.TaskID = v.(int64) case "type": info.TaskType = v.(int) case "first_event_id": info.FirstEventID = v.(int64) case "next_event_id": info.NextEventID = v.(int64) case "version": info.Version = v.(int64) case "scheduled_id": info.ScheduledID = v.(int64) case "branch_token": info.BranchToken = v.([]byte) case "new_run_branch_token": info.NewRunBranchToken = v.([]byte) case "created_time": info.CreationTime = time.Unix(0, v.(int64)) } } return info } func parseChecksum(result map[string]interface{}) checksum.Checksum { csum := checksum.Checksum{} if len(result) == 0 { return csum } for k, v := range result { switch k { case "flavor": csum.Flavor = checksum.Flavor(v.(int)) case "version": csum.Version = v.(int) case "value": csum.Value = v.([]byte) } } return csum } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/workflow_parsing_utils_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cassandra import ( "testing" "time" cql "github.com/gocql/gocql" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) type mockUUID struct { uuid string } func (m mockUUID) String() string { return m.uuid } func newMockUUID(s string) mockUUID { return mockUUID{s} } func Test_parseWorkflowExecutionInfo(t *testing.T) { completionEventData := []byte("completion event data") autoResetPointsData := []byte("auto reset points data") activeClusterSelectionPolicyData := []byte("active cluster selection policy data") searchAttributes := map[string][]byte{"AttributeKey": []byte("AttributeValue")} memo := map[string][]byte{"MemoKey": []byte("MemoValue")} partitionConfig := map[string]string{"PartitionKey": "PartitionValue"} timeNow := time.Now() tests := []struct { name string args map[string]interface{} want *persistence.InternalWorkflowExecutionInfo }{ { name: "full execution blob", args: map[string]interface{}{ "execution": map[string]interface{}{ "domain_id": newMockUUID("domain_id"), "workflow_id": "workflow_id", "run_id": newMockUUID("run_id"), "parent_workflow_id": "parent_workflow_id", "initiated_id": int64(1), "completion_event_batch_id": int64(2), "task_list": "task_list", "task_list_kind": 2, "workflow_type_name": "workflow_type_name", "workflow_timeout": 10, "decision_task_timeout": 5, "execution_context": []byte("execution context"), "state": 1, "close_status": 2, "last_first_event_id": int64(3), "last_event_task_id": int64(4), "last_processed_event": int64(6), "start_time": timeNow, "last_updated_time": timeNow, "create_request_id": newMockUUID("create_request_id"), "signal_count": 7, "history_size": int64(8), "decision_version": int64(9), "decision_schedule_id": int64(10), "decision_started_id": int64(11), "decision_request_id": "decision_request_id", "decision_timeout": 8, "decision_timestamp": int64(200), "decision_scheduled_timestamp": int64(201), "decision_original_scheduled_timestamp": int64(202), "decision_attempt": int64(203), "cancel_requested": true, "cancel_request_id": "cancel_request_id", "sticky_task_list": "sticky_task_list", "sticky_schedule_to_start_timeout": 9, "client_library_version": "client_lib_version", "client_feature_version": "client_feature_version", "client_impl": "client_impl", "attempt": 12, "has_retry_policy": true, "init_interval": 10, "backoff_coefficient": 1.5, "max_interval": 20, "max_attempts": 13, "expiration_time": timeNow, "non_retriable_errors": []string{"error1", "error2"}, "branch_token": []byte("branch token"), "cron_schedule": "cron_schedule", "expiration_seconds": 14, "search_attributes": searchAttributes, "memo": memo, "partition_config": partitionConfig, "completion_event": completionEventData, "completion_event_data_encoding": "Proto3", "auto_reset_points": autoResetPointsData, "auto_reset_points_encoding": "Proto3", "active_cluster_selection_policy": activeClusterSelectionPolicyData, "active_cluster_selection_policy_encoding": "Proto3", }, "next_event_id": int64(5), }, want: &persistence.InternalWorkflowExecutionInfo{ DomainID: "domain_id", WorkflowID: "workflow_id", RunID: "run_id", ParentWorkflowID: "parent_workflow_id", InitiatedID: int64(1), CompletionEventBatchID: int64(2), TaskList: "task_list", TaskListKind: types.TaskListKindEphemeral, WorkflowTypeName: "workflow_type_name", WorkflowTimeout: common.SecondsToDuration(int64(10)), DecisionStartToCloseTimeout: common.SecondsToDuration(int64(5)), ExecutionContext: []byte("execution context"), State: 1, CloseStatus: 2, LastFirstEventID: int64(3), LastEventTaskID: int64(4), NextEventID: int64(5), LastProcessedEvent: int64(6), StartTimestamp: timeNow, LastUpdatedTimestamp: timeNow, CreateRequestID: "create_request_id", SignalCount: int32(7), HistorySize: int64(8), DecisionVersion: int64(9), DecisionScheduleID: int64(10), DecisionStartedID: int64(11), DecisionRequestID: "decision_request_id", DecisionTimeout: common.SecondsToDuration(int64(8)), DecisionStartedTimestamp: time.Unix(0, int64(200)), DecisionScheduledTimestamp: time.Unix(0, int64(201)), DecisionOriginalScheduledTimestamp: time.Unix(0, int64(202)), DecisionAttempt: int64(203), CancelRequested: true, CancelRequestID: "cancel_request_id", StickyTaskList: "sticky_task_list", StickyScheduleToStartTimeout: common.SecondsToDuration(int64(9)), ClientLibraryVersion: "client_lib_version", ClientFeatureVersion: "client_feature_version", ClientImpl: "client_impl", Attempt: int32(12), HasRetryPolicy: true, InitialInterval: common.SecondsToDuration(int64(10)), BackoffCoefficient: 1.5, MaximumInterval: common.SecondsToDuration(int64(20)), MaximumAttempts: int32(13), ExpirationTime: timeNow, NonRetriableErrors: []string{"error1", "error2"}, Memo: memo, PartitionConfig: partitionConfig, ActiveClusterSelectionPolicy: persistence.NewDataBlob(activeClusterSelectionPolicyData, "Proto3"), }, }, { name: "uuid fields", args: map[string]interface{}{ "execution": map[string]interface{}{ "first_run_id": newMockUUID("first_run_id"), "parent_domain_id": newMockUUID("parent_domain_id"), "parent_run_id": newMockUUID("parent_run_id"), }, }, want: &persistence.InternalWorkflowExecutionInfo{ FirstExecutionRunID: "first_run_id", ParentDomainID: "parent_domain_id", ParentRunID: "parent_run_id", }, }, { name: "empty uuid fields", args: map[string]interface{}{ "execution": map[string]interface{}{ "first_run_id": newMockUUID(emptyRunID), "parent_domain_id": newMockUUID(emptyDomainID), "parent_run_id": newMockUUID(emptyRunID), }, }, want: &persistence.InternalWorkflowExecutionInfo{}, }, { name: "nil uuid field", args: map[string]interface{}{ "execution": map[string]interface{}{ "first_run_id": newMockUUID(cql.UUID{}.String()), }, }, want: &persistence.InternalWorkflowExecutionInfo{ FirstExecutionRunID: "", }, }, { name: "denormalized columns override blob - next_event_id", args: map[string]interface{}{ "execution": map[string]interface{}{ "next_event_id": int64(10), }, "next_event_id": int64(5), }, want: &persistence.InternalWorkflowExecutionInfo{ NextEventID: int64(5), }, }, { name: "no denormalized columns - execution blob values ignored for next_event_id", args: map[string]interface{}{ "execution": map[string]interface{}{ "next_event_id": int64(10), // This is ignored, only denormalized column is used "state": 2, }, }, want: &persistence.InternalWorkflowExecutionInfo{ NextEventID: int64(0), // Zero value since denormalized column not present State: 2, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := parseWorkflowExecutionInfo(tt.args) assert.NoError(t, err) assert.Equal(t, result.FirstExecutionRunID, tt.want.FirstExecutionRunID) assert.Equal(t, result.DomainID, tt.want.DomainID) assert.Equal(t, result.WorkflowID, tt.want.WorkflowID) assert.Equal(t, result.RunID, tt.want.RunID) assert.Equal(t, result.ParentWorkflowID, tt.want.ParentWorkflowID) assert.Equal(t, result.InitiatedID, tt.want.InitiatedID) assert.Equal(t, result.CompletionEventBatchID, tt.want.CompletionEventBatchID) assert.Equal(t, result.TaskList, tt.want.TaskList) assert.Equal(t, result.WorkflowTypeName, tt.want.WorkflowTypeName) assert.Equal(t, result.WorkflowTimeout, tt.want.WorkflowTimeout) assert.Equal(t, result.DecisionStartToCloseTimeout, tt.want.DecisionStartToCloseTimeout) assert.Equal(t, result.ExecutionContext, tt.want.ExecutionContext) assert.Equal(t, result.State, tt.want.State) assert.Equal(t, result.CloseStatus, tt.want.CloseStatus) assert.Equal(t, result.LastFirstEventID, tt.want.LastFirstEventID) assert.Equal(t, result.LastEventTaskID, tt.want.LastEventTaskID) assert.Equal(t, result.NextEventID, tt.want.NextEventID) assert.Equal(t, result.LastProcessedEvent, tt.want.LastProcessedEvent) assert.Equal(t, result.StartTimestamp, tt.want.StartTimestamp) assert.Equal(t, result.LastUpdatedTimestamp, tt.want.LastUpdatedTimestamp) assert.Equal(t, result.CreateRequestID, tt.want.CreateRequestID) assert.Equal(t, result.SignalCount, tt.want.SignalCount) assert.Equal(t, result.HistorySize, tt.want.HistorySize) assert.Equal(t, result.DecisionVersion, tt.want.DecisionVersion) assert.Equal(t, result.DecisionScheduleID, tt.want.DecisionScheduleID) assert.Equal(t, result.DecisionStartedID, tt.want.DecisionStartedID) assert.Equal(t, result.DecisionRequestID, tt.want.DecisionRequestID) assert.Equal(t, result.DecisionTimeout, tt.want.DecisionTimeout) assert.Equal(t, result.CancelRequested, tt.want.CancelRequested) assert.Equal(t, result.DecisionStartedTimestamp, tt.want.DecisionStartedTimestamp) assert.Equal(t, result.DecisionScheduledTimestamp, tt.want.DecisionScheduledTimestamp) assert.Equal(t, result.DecisionOriginalScheduledTimestamp, tt.want.DecisionOriginalScheduledTimestamp) assert.Equal(t, result.DecisionAttempt, tt.want.DecisionAttempt) assert.Equal(t, result.ParentDomainID, tt.want.ParentDomainID) assert.Equal(t, result.ActiveClusterSelectionPolicy, tt.want.ActiveClusterSelectionPolicy) }) } } func Test_parseWorkflowExecutionInfo_ErrorCases(t *testing.T) { tests := []struct { name string args map[string]interface{} wantErr bool errContains string }{ { name: "missing execution field", args: map[string]interface{}{ "next_event_id": int64(5), }, wantErr: true, errContains: "missing or invalid 'execution' field", }, { name: "invalid execution field type", args: map[string]interface{}{ "execution": "not a map", }, wantErr: true, errContains: "missing or invalid 'execution' field", }, { name: "execution field is nil", args: map[string]interface{}{ "execution": nil, }, wantErr: true, errContains: "missing or invalid 'execution' field", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := parseWorkflowExecutionInfo(tt.args) if tt.wantErr { assert.Error(t, err) assert.Nil(t, result) assert.Contains(t, err.Error(), tt.errContains) } else { assert.NoError(t, err) assert.NotNil(t, result) } }) } } func Test_parseReplicationState(t *testing.T) { tests := []struct { args map[string]interface{} want *persistence.ReplicationState }{ { args: map[string]interface{}{ "current_version": int64(1), "start_version": int64(2), "last_write_version": int64(3), "last_write_event_id": int64(4), "last_replication_info": map[string]map[string]interface{}{ "map1": { "version": int64(5), "last_event_id": int64(6), }, "map2": { "version": int64(7), "last_event_id": int64(8), }, }, }, want: &persistence.ReplicationState{ CurrentVersion: int64(1), StartVersion: int64(2), LastWriteVersion: int64(3), LastWriteEventID: int64(4), LastReplicationInfo: map[string]*persistence.ReplicationInfo{ "map1": { Version: int64(5), LastEventID: int64(6), }, "map2": { Version: int64(7), LastEventID: int64(8), }, }, }, }, } for _, tt := range tests { result := parseReplicationState(tt.args) assert.Equal(t, result.CurrentVersion, tt.want.CurrentVersion) assert.Equal(t, result.StartVersion, tt.want.StartVersion) assert.Equal(t, result.LastWriteVersion, tt.want.LastWriteVersion) assert.Equal(t, result.LastWriteEventID, tt.want.LastWriteEventID) assert.Equal(t, result.LastReplicationInfo, tt.want.LastReplicationInfo) } } func Test_parseActivityInfo(t *testing.T) { timeNow := time.Now() testInput := map[string]interface{}{ "version": int64(1), "schedule_id": int64(2), "scheduled_event_batch_id": int64(3), "scheduled_event": []byte("scheduled_event"), "scheduled_time": timeNow, "started_id": int64(4), "started_event": []byte("started_event"), "started_time": timeNow, "activity_id": "activity_id", "request_id": "request_id", "details": []byte("details"), "schedule_to_start_timeout": 5, "schedule_to_close_timeout": 6, "start_to_close_timeout": 7, "heart_beat_timeout": 8, "cancel_requested": true, "cancel_request_id": int64(9), "last_hb_updated_time": timeNow, "timer_task_status": 9, "attempt": 10, "task_list": "task_list", "task_list_kind": 1, "started_identity": "started_identity", "has_retry_policy": true, "init_interval": 11, "backoff_coefficient": 1.5, "max_interval": 12, "max_attempts": 13, "expiration_time": timeNow, "non_retriable_errors": []string{"error1", "error2"}, "last_failure_reason": "last_failure_reason", "last_worker_identity": "last_worker_identity", "last_failure_details": []byte("last_failure_details"), "event_data_encoding": "Proto3", } expected := &persistence.InternalActivityInfo{ Version: int64(1), ScheduleID: int64(2), ScheduledEventBatchID: int64(3), ScheduledEvent: persistence.NewDataBlob([]byte("scheduled_event"), "Proto3"), ScheduledTime: timeNow, StartedID: int64(4), StartedEvent: persistence.NewDataBlob([]byte("started_event"), "Proto3"), StartedTime: timeNow, ActivityID: "activity_id", RequestID: "request_id", Details: []byte("details"), ScheduleToStartTimeout: common.SecondsToDuration(int64(5)), ScheduleToCloseTimeout: common.SecondsToDuration(int64(6)), StartToCloseTimeout: common.SecondsToDuration(int64(7)), HeartbeatTimeout: common.SecondsToDuration(int64(8)), CancelRequested: true, CancelRequestID: int64(9), LastHeartBeatUpdatedTime: timeNow, TimerTaskStatus: int32(9), Attempt: int32(10), TaskList: "task_list", TaskListKind: types.TaskListKindSticky, StartedIdentity: "started_identity", HasRetryPolicy: true, InitialInterval: common.SecondsToDuration(int64(11)), BackoffCoefficient: 1.5, MaximumInterval: common.SecondsToDuration(int64(12)), MaximumAttempts: int32(13), ExpirationTime: timeNow, NonRetriableErrors: []string{"error1", "error2"}, LastFailureReason: "last_failure_reason", LastWorkerIdentity: "last_worker_identity", LastFailureDetails: []byte("last_failure_details"), DomainID: "domain_id", } assert.Equal(t, expected, parseActivityInfo("domain_id", testInput)) } func Test_parseTimerInfo(t *testing.T) { timeNow := time.Now() testInput := map[string]interface{}{ "version": int64(1), "timer_id": "timer_id", "started_id": int64(2), "expiry_time": timeNow, "task_id": int64(3), } expected := &persistence.TimerInfo{ Version: int64(1), TimerID: "timer_id", StartedID: int64(2), ExpiryTime: timeNow, TaskStatus: int64(3), } assert.Equal(t, expected, parseTimerInfo(testInput)) } func Test_parseChildExecutionInfo(t *testing.T) { startedRunID := newMockUUID("started_run_id") createRequestID := newMockUUID("create_request_id") domainID := newMockUUID("domain_id") testInput := map[string]interface{}{ "version": int64(1), "initiated_id": int64(2), "initiated_event_batch_id": int64(3), "initiated_event": []byte("initiated_event"), "started_id": int64(4), "started_workflow_id": "started_workflow_id", "started_run_id": startedRunID, "started_event": []byte("started_event"), "create_request_id": createRequestID, "event_data_encoding": "Proto3", "domain_id": domainID, "workflow_type_name": "workflow_type_name", "parent_close_policy": 1, } expected := &persistence.InternalChildExecutionInfo{ Version: int64(1), InitiatedID: int64(2), InitiatedEventBatchID: int64(3), InitiatedEvent: persistence.NewDataBlob([]byte("initiated_event"), "Proto3"), StartedID: int64(4), StartedWorkflowID: "started_workflow_id", StartedRunID: startedRunID.String(), StartedEvent: persistence.NewDataBlob([]byte("started_event"), "Proto3"), CreateRequestID: createRequestID.String(), DomainID: domainID.String(), WorkflowTypeName: "workflow_type_name", ParentClosePolicy: 1, } assert.Equal(t, expected, parseChildExecutionInfo(testInput)) // edge case testInput = map[string]interface{}{ "domain_id": newMockUUID(_emptyUUID.String()), "domain_name": "domain_name", } assert.Equal(t, "domain_name", parseChildExecutionInfo(testInput).DomainNameDEPRECATED) assert.Equal(t, "", parseChildExecutionInfo(testInput).DomainID) } func Test_parseRequestCancelInfo(t *testing.T) { testInput := map[string]interface{}{ "version": int64(1), "initiated_id": int64(2), "initiated_event_batch_id": int64(3), "cancel_request_id": "cancel_request_id", } expected := &persistence.RequestCancelInfo{ Version: int64(1), InitiatedID: int64(2), InitiatedEventBatchID: int64(3), CancelRequestID: "cancel_request_id", } assert.Equal(t, expected, parseRequestCancelInfo(testInput)) } func Test_parseSignalInfo(t *testing.T) { testInput := map[string]interface{}{ "version": int64(1), "initiated_id": int64(2), "initiated_event_batch_id": int64(3), "signal_request_id": newMockUUID("signal_request_id"), "signal_name": "signal_name", "input": []byte("input"), "control": []byte("control"), } expected := &persistence.SignalInfo{ Version: int64(1), InitiatedID: int64(2), InitiatedEventBatchID: int64(3), SignalName: "signal_name", SignalRequestID: "signal_request_id", Input: []byte("input"), Control: []byte("control"), } assert.Equal(t, expected, parseSignalInfo(testInput)) } func Test_parseTimerTaskInfo(t *testing.T) { timeNow := time.Now() testInput := map[string]interface{}{ "version": int64(1), "visibility_ts": timeNow, "task_id": int64(2), "run_id": newMockUUID("run_id"), "type": 3, "timeout_type": 3, "event_id": int64(4), "schedule_attempt": int64(5), "task_list": "task_list", } expected := &persistence.TimerTaskInfo{ Version: int64(1), VisibilityTimestamp: timeNow, TaskID: int64(2), RunID: "run_id", TaskType: 3, TimeoutType: 3, EventID: int64(4), ScheduleAttempt: int64(5), TaskList: "task_list", } assert.Equal(t, expected, parseTimerTaskInfo(testInput)) } func Test_parseReplicationTaskInfo(t *testing.T) { testInput := map[string]interface{}{ "domain_id": newMockUUID("domain_id"), "workflow_id": "workflow_id", "run_id": newMockUUID("run_id"), "task_id": int64(1), "type": 2, "first_event_id": int64(3), "next_event_id": int64(4), "version": int64(5), "scheduled_id": int64(6), "branch_token": []byte("branch_token"), "new_run_branch_token": []byte("new_run_branch_token"), "created_time": int64(7), } expected := &nosqlplugin.ReplicationTask{ DomainID: "domain_id", WorkflowID: "workflow_id", RunID: "run_id", TaskID: int64(1), TaskType: 2, FirstEventID: int64(3), NextEventID: int64(4), Version: int64(5), ScheduledID: int64(6), BranchToken: []byte("branch_token"), NewRunBranchToken: []byte("new_run_branch_token"), CreationTime: time.Unix(0, 7), } assert.Equal(t, expected, parseReplicationTaskInfo(testInput)) } func Test_parseTransferTaskInfo(t *testing.T) { timeNow := time.Now() testInput := map[string]interface{}{ "domain_id": newMockUUID("domain_id"), "workflow_id": "workflow_id", "run_id": newMockUUID("run_id"), "visibility_ts": timeNow, "task_id": int64(1), "target_domain_id": newMockUUID("target_domain_id"), "target_domain_ids": []interface{}{newMockUUID("target_domain_id")}, "target_workflow_id": "target_workflow_id", "target_run_id": newMockUUID("target_run_id"), "target_child_workflow_only": true, "task_list": "task_list", "type": 2, "schedule_id": int64(3), "record_visibility": true, "version": int64(4), "original_task_list": "original_task_list", "original_task_list_kind": 2, } expected := &persistence.TransferTaskInfo{ DomainID: "domain_id", WorkflowID: "workflow_id", RunID: "run_id", VisibilityTimestamp: timeNow, TaskID: int64(1), TargetDomainID: "target_domain_id", TargetDomainIDs: map[string]struct{}{"target_domain_id": {}}, TargetWorkflowID: "target_workflow_id", TargetRunID: "target_run_id", TargetChildWorkflowOnly: true, TaskList: "task_list", TaskType: 2, ScheduleID: int64(3), RecordVisibility: true, Version: int64(4), OriginalTaskList: "original_task_list", OriginalTaskListKind: types.TaskListKindEphemeral, } assert.Equal(t, expected, parseTransferTaskInfo(testInput)) // edge case testInput = map[string]interface{}{ "target_run_id": newMockUUID(persistence.TransferTaskTransferTargetRunID), } expected = &persistence.TransferTaskInfo{ TargetRunID: "", } assert.Equal(t, expected, parseTransferTaskInfo(testInput)) } func Test_parseChecksum(t *testing.T) { testInput := map[string]interface{}{ "version": 1, "flavor": 2, "value": []byte("value"), } expected := checksum.Checksum{ Version: 1, Flavor: 2, Value: []byte("value"), } assert.Equal(t, expected, parseChecksum(testInput)) } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/workflow_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "errors" "testing" "time" "github.com/google/go-cmp/cmp" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/testdata" ) func TestInsertWorkflowExecutionWithTasks(t *testing.T) { tests := []struct { name string workflowRequest *nosqlplugin.WorkflowRequestsWriteRequest request *nosqlplugin.CurrentWorkflowWriteRequest execution *nosqlplugin.WorkflowExecutionRequest tasksByCategory map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask activeClusterSelectionPolicyRow *nosqlplugin.ActiveClusterSelectionPolicyRow shardCondition *nosqlplugin.ShardCondition mapExecuteBatchCASErr error wantErr bool }{ { name: "success", request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, execution: testdata.WFExecRequest(), }, { name: "success with active cluster selection policy row", request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, execution: testdata.WFExecRequest(), activeClusterSelectionPolicyRow: &nosqlplugin.ActiveClusterSelectionPolicyRow{ ShardID: 1, DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", Policy: &persistence.DataBlob{ Data: []byte("test-policy"), Encoding: constants.EncodingTypeThriftRW, }, }, }, { name: "insertOrUpsertWorkflowRequestRow step fails", workflowRequest: &nosqlplugin.WorkflowRequestsWriteRequest{ Rows: []*nosqlplugin.WorkflowRequestRow{ { RequestType: persistence.WorkflowRequestTypeStart, }, }, WriteMode: nosqlplugin.WorkflowRequestWriteMode(-999), // unknown mode will cause failure }, request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, execution: testdata.WFExecRequest(), wantErr: true, }, { name: "createOrUpdateCurrentWorkflow step fails", request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteMode(-999), // unknown mode will cause failure }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, execution: testdata.WFExecRequest(), wantErr: true, }, { name: "createWorkflowExecutionWithMergeMaps step fails", request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, execution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeAppend), // this will cause failure ), wantErr: true, }, { name: "executeCreateWorkflowBatchTransaction step fails", mapExecuteBatchCASErr: errors.New("some random error"), // this will cause failure request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, execution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeNone), ), wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) session := &fakeSession{ iter: &fakeIter{}, mapExecuteBatchCASApplied: true, mapExecuteBatchCASErr: tc.mapExecuteBatchCASErr, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.InsertWorkflowExecutionWithTasks( context.Background(), tc.workflowRequest, tc.request, tc.execution, tc.tasksByCategory, tc.activeClusterSelectionPolicyRow, tc.shardCondition, ) if (err != nil) != tc.wantErr { t.Errorf("InsertWorkflowExecutionWithTasks() error = %v, wantErr %v", err, tc.wantErr) } }) } } func TestSelectCurrentWorkflow(t *testing.T) { tests := []struct { name string shardID int domainID string workflowID string currentRunID string lastWriteVersion int64 createReqID string wantErr bool wantRow *nosqlplugin.CurrentWorkflowRow }{ { name: "success", shardID: 1, domainID: "test-domain-id", workflowID: "test-workflow-id", currentRunID: "test-run-id", wantErr: false, wantRow: &nosqlplugin.CurrentWorkflowRow{ ShardID: 1, DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", LastWriteVersion: -24, }, }, { name: "mapscan failure", shardID: 1, domainID: "test-domain-id", workflowID: "test-workflow-id", currentRunID: "test-run-id", wantErr: true, }, { name: "lastwriteversion populated", shardID: 1, domainID: "test-domain-id", workflowID: "test-workflow-id", currentRunID: "test-run-id", lastWriteVersion: 123, wantErr: false, wantRow: &nosqlplugin.CurrentWorkflowRow{ ShardID: 1, DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", LastWriteVersion: 123, }, }, { name: "create request id populated", shardID: 1, domainID: "test-domain-id", workflowID: "test-workflow-id", currentRunID: "test-run-id", createReqID: "test-create-request-id", wantErr: false, wantRow: &nosqlplugin.CurrentWorkflowRow{ ShardID: 1, DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", LastWriteVersion: -24, CreateRequestID: "test-create-request-id", }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { mockCurrentRunID := gocql.NewMockUUID(ctrl) m["current_run_id"] = mockCurrentRunID if !tc.wantErr { mockCurrentRunID.EXPECT().String().Return(tc.currentRunID).Times(1) } execMap := map[string]interface{}{} if tc.createReqID != "" { mockReqID := gocql.NewMockUUID(ctrl) mockReqID.EXPECT().String().Return(tc.createReqID).Times(1) execMap["create_request_id"] = mockReqID } m["execution"] = execMap if tc.lastWriteVersion != 0 { m["workflow_last_write_version"] = tc.lastWriteVersion } if tc.wantErr { return errors.New("some random error") } return nil }).Times(1) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) row, err := db.SelectCurrentWorkflow(context.Background(), tc.shardID, tc.domainID, tc.workflowID) if (err != nil) != tc.wantErr { t.Errorf("SelectCurrentWorkflow() error: %v, wantErr?: %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantRow, row); diff != "" { t.Fatalf("Row mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateWorkflowExecutionWithTasks(t *testing.T) { tests := []struct { name string workflowRequest *nosqlplugin.WorkflowRequestsWriteRequest request *nosqlplugin.CurrentWorkflowWriteRequest mutatedExecution *nosqlplugin.WorkflowExecutionRequest insertedExecution *nosqlplugin.WorkflowExecutionRequest activeClusterSelectionPolicyRow *nosqlplugin.ActiveClusterSelectionPolicyRow resetExecution *nosqlplugin.WorkflowExecutionRequest tasksByCategory map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask shardCondition *nosqlplugin.ShardCondition mapExecuteBatchCASErr error wantErr bool }{ { name: "both mutatedExecution and resetExecution not provided", request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, wantErr: true, }, { name: "insertOrUpsertWorkflowRequestRow step fails", workflowRequest: &nosqlplugin.WorkflowRequestsWriteRequest{ Rows: []*nosqlplugin.WorkflowRequestRow{ { RequestType: persistence.WorkflowRequestTypeStart, }, }, WriteMode: nosqlplugin.WorkflowRequestWriteMode(-999), // unknown mode will cause failure }, request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, mutatedExecution: testdata.WFExecRequest( testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeUpdate), ), insertedExecution: testdata.WFExecRequest(), wantErr: true, }, { name: "mutatedExecution provided - success", request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, mutatedExecution: testdata.WFExecRequest( testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeUpdate), ), insertedExecution: testdata.WFExecRequest(), }, { name: "mutatedExecution provided - update fails", wantErr: true, request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, mutatedExecution: testdata.WFExecRequest( testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeCreate), // this will cause failure ), insertedExecution: testdata.WFExecRequest(), }, { name: "resetExecution provided - success", request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, resetExecution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeClear), testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeReset), ), insertedExecution: testdata.WFExecRequest(), }, { name: "resetExecution provided - reset fails", wantErr: true, request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, resetExecution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeNone), // this will cause failure testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeReset), ), insertedExecution: testdata.WFExecRequest(), }, { name: "resetExecution and insertedExecution provided - success", request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, resetExecution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeClear), testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeReset), ), insertedExecution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeNone), testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeCreate), ), }, { name: "mutatedExecution and insertedExecution and activeClusterSelectionPolicyRow provided - success", request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, mutatedExecution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeNone), testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeUpdate), ), activeClusterSelectionPolicyRow: &nosqlplugin.ActiveClusterSelectionPolicyRow{ ShardID: 1, DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", Policy: &persistence.DataBlob{Encoding: constants.EncodingTypeThriftRW, Data: []byte("test-policy")}, }, insertedExecution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeNone), testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeCreate), ), }, { name: "resetExecution and insertedExecution provided - insert fails", wantErr: true, request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, resetExecution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeClear), testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeReset), ), insertedExecution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeClear), // this will cause failure testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeCreate), ), }, { name: "createOrUpdateCurrentWorkflow step fails", wantErr: true, request: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Condition: nil, // this will cause failure because Condition must be non-nil for update mode }, shardCondition: &nosqlplugin.ShardCondition{ ShardID: 1, }, resetExecution: testdata.WFExecRequest( testdata.WFExecRequestWithEventBufferWriteMode(nosqlplugin.EventBufferWriteModeClear), testdata.WFExecRequestWithMapsWriteMode(nosqlplugin.WorkflowExecutionMapsWriteModeReset), ), insertedExecution: testdata.WFExecRequest(), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) session := &fakeSession{ iter: &fakeIter{}, mapExecuteBatchCASApplied: true, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) err := db.UpdateWorkflowExecutionWithTasks( context.Background(), tc.workflowRequest, tc.request, tc.mutatedExecution, tc.insertedExecution, nil, // TODO(active-active): add test cases for activeClusterSelectionPolicyRow tc.resetExecution, tc.tasksByCategory, tc.shardCondition, ) if (err != nil) != tc.wantErr { t.Errorf("UpdateWorkflowExecutionWithTasks() error: %v, wantErr %v", err, tc.wantErr) } }) } } func TestSelectWorkflowExecution(t *testing.T) { tests := []struct { name string shardID int domainID string workflowID string runID string queryMockFn func(query *gocql.MockQuery) wantResp *nosqlplugin.WorkflowExecution wantErr bool }{ { name: "mapscan failure", shardID: 1, domainID: "test-domain-id", workflowID: "test-workflow-id", runID: "test-run-id", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { return errors.New("some random error") }).Times(1) }, wantErr: true, }, { name: "success", shardID: 1, domainID: "test-domain-id", workflowID: "test-workflow-id", runID: "test-run-id", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { m["execution"] = map[string]interface{}{} m["version_histories"] = []byte{} m["version_histories_encoding"] = "thriftrw" m["replication_state"] = map[string]interface{}{} m["activity_map"] = map[int64]map[string]interface{}{ 1: {"schedule_id": int64(1)}, } m["timer_map"] = map[string]map[string]interface{}{ "t1": {"started_id": int64(5)}, } m["child_executions_map"] = map[int64]map[string]interface{}{ 3: {"initiated_id": int64(2)}, } m["request_cancel_map"] = map[int64]map[string]interface{}{ 6: {"initiated_id": int64(5)}, } m["signal_map"] = map[int64]map[string]interface{}{ 8: {"initiated_id": int64(7)}, } m["signal_requested"] = []interface{}{ &fakeUUID{uuid: "aae7b881-48ea-4b23-8d11-aabfd1c1291e"}, } m["buffered_events_list"] = []map[string]interface{}{ {"encoding_type": "thriftrw", "data": []byte("test-buffered-events-1")}, } m["checksum"] = map[string]interface{}{} return nil }).Times(1) }, wantResp: &nosqlplugin.WorkflowExecution{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, ActivityInfos: map[int64]*persistence.InternalActivityInfo{ 1: {ScheduleID: 1, DomainID: "test-domain-id"}, }, TimerInfos: map[string]*persistence.TimerInfo{ "t1": {StartedID: 5}, }, ChildExecutionInfos: map[int64]*persistence.InternalChildExecutionInfo{ 3: {InitiatedID: 2}, }, RequestCancelInfos: map[int64]*persistence.RequestCancelInfo{ 6: {InitiatedID: 5}, }, SignalInfos: map[int64]*persistence.SignalInfo{ 8: {InitiatedID: 7}, }, SignalRequestedIDs: map[string]struct{}{ "aae7b881-48ea-4b23-8d11-aabfd1c1291e": {}, }, BufferedEvents: []*persistence.DataBlob{ {Encoding: constants.EncodingTypeThriftRW, Data: []byte("test-buffered-events-1")}, }, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ iter: &fakeIter{}, mapExecuteBatchCASApplied: true, query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) got, err := db.SelectWorkflowExecution( context.Background(), tc.shardID, tc.domainID, tc.workflowID, tc.runID, ) if (err != nil) != tc.wantErr { t.Errorf("SelectWorkflowExecution() error: %v, wantErr %v", err, tc.wantErr) } if err != nil || tc.wantErr { return } if diff := cmp.Diff(tc.wantResp, got); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } }) } } func TestDeleteCurrentWorkflow(t *testing.T) { tests := []struct { name string shardID int domainID string workflowID string currentRunIDCondition string queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, domainID: "test-domain-id", workflowID: "test-workflow-id", currentRunIDCondition: "test-run-id", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, domainID: "test-domain-id", workflowID: "test-workflow-id", currentRunIDCondition: "test-run-id", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.DeleteCurrentWorkflow(context.Background(), tc.shardID, tc.domainID, tc.workflowID, tc.currentRunIDCondition) if (err != nil) != tc.wantErr { t.Errorf("DeleteCurrentWorkflow() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestDeleteWorkflowExecution(t *testing.T) { tests := []struct { name string shardID int domainID string workflowID string runID string queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, domainID: "test-domain-id", workflowID: "test-workflow-id", runID: "test-run-id", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, domainID: "test-domain-id", workflowID: "test-workflow-id", runID: "test-run-id", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.DeleteWorkflowExecution(context.Background(), tc.shardID, tc.domainID, tc.workflowID, tc.runID) if (err != nil) != tc.wantErr { t.Errorf("DeleteWorkflowExecution() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestSelectAllCurrentWorkflows(t *testing.T) { tests := []struct { name string shardID int pageToken []byte pageSize int iter *fakeIter wantExecutions []*persistence.CurrentWorkflowExecution wantErr bool }{ { name: "nil iter returned", shardID: 1, pageToken: []byte("test-page-token"), pageSize: 10, wantErr: true, }, { name: "run_id is not permanentRunID so excluded from result", shardID: 1, pageToken: []byte("test-page-token"), pageSize: 10, iter: &fakeIter{ mapScanInputs: []map[string]interface{}{ { "run_id": &fakeUUID{uuid: "17C305FA-79BB-479E-8AC7-360E956AC01A"}, }, }, pageState: []byte("test-page-token-2"), }, wantExecutions: nil, }, { name: "multiple executions", shardID: 1, pageToken: []byte("test-page-token"), pageSize: 10, iter: &fakeIter{ mapScanInputs: []map[string]interface{}{ { "run_id": &fakeUUID{uuid: permanentRunID}, "domain_id": &fakeUUID{uuid: "domain1"}, "current_run_id": &fakeUUID{uuid: "runid1"}, "workflow_id": "wfid1", "workflow_state": 1, }, { "run_id": &fakeUUID{uuid: permanentRunID}, "domain_id": &fakeUUID{uuid: "domain1"}, "current_run_id": &fakeUUID{uuid: "runid2"}, "workflow_id": "wfid2", "workflow_state": 1, }, }, pageState: []byte("test-page-token-2"), }, wantExecutions: []*persistence.CurrentWorkflowExecution{ { DomainID: "domain1", WorkflowID: "wfid1", RunID: "30000000-0000-f000-f000-000000000001", State: 1, CurrentRunID: "runid1", }, { DomainID: "domain1", WorkflowID: "wfid2", RunID: "30000000-0000-f000-f000-000000000001", State: 1, CurrentRunID: "runid2", }, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) query.EXPECT().PageSize(tc.pageSize).Return(query).Times(1) query.EXPECT().PageState(tc.pageToken).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) if tc.iter != nil { query.EXPECT().Iter().Return(tc.iter).Times(1) } else { // Passing tc.iter to Return() doesn't work even though tc.iter is nil due to Go's typed nils. // So, we have to call Return(nil) directly. query.EXPECT().Iter().Return(nil).Times(1) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotExecutions, gotPageToken, err := db.SelectAllCurrentWorkflows(context.Background(), tc.shardID, tc.pageToken, tc.pageSize) if (err != nil) != tc.wantErr { t.Errorf("SelectAllCurrentWorkflows() error: %v, wantErr %v", err, tc.wantErr) } if err != nil || tc.wantErr { return } if diff := cmp.Diff(tc.wantExecutions, gotExecutions); diff != "" { t.Fatalf("Executions mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.iter.pageState, gotPageToken); diff != "" { t.Fatalf("Page token mismatch (-want +got):\n%s", diff) } if !tc.iter.closed { t.Error("iter was not closed") } }) } } func TestSelectAllWorkflowExecutions(t *testing.T) { tests := []struct { name string shardID int pageToken []byte pageSize int iter *fakeIter wantExecutions []*persistence.InternalListConcreteExecutionsEntity wantErr bool }{ { name: "nil iter returned", shardID: 1, pageToken: []byte("test-page-token"), pageSize: 10, wantErr: true, }, { name: "run_id is permanentRunID so excluded from result", shardID: 1, pageToken: []byte("test-page-token"), pageSize: 10, iter: &fakeIter{ mapScanInputs: []map[string]interface{}{ { "run_id": &fakeUUID{uuid: "30000000-0000-f000-f000-000000000001"}, }, }, pageState: []byte("test-page-token-2"), }, wantExecutions: nil, }, { name: "multiple executions", shardID: 1, pageToken: []byte("test-page-token"), pageSize: 10, iter: &fakeIter{ mapScanInputs: []map[string]interface{}{ { "run_id": &fakeUUID{uuid: "runid1"}, "execution": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "workflow_id": "wfid1", }, "version_histories": []byte("test-version-histories-1"), "version_histories_encoding": "thriftrw", }, { "run_id": &fakeUUID{uuid: "runid2"}, "execution": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "workflow_id": "wfid2", }, "version_histories": []byte("test-version-histories-1"), "version_histories_encoding": "thriftrw", }, }, pageState: []byte("test-page-token-2"), }, wantExecutions: []*persistence.InternalListConcreteExecutionsEntity{ { ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{DomainID: "domain1", WorkflowID: "wfid1"}, VersionHistories: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("test-version-histories-1")}, }, { ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{DomainID: "domain1", WorkflowID: "wfid2"}, VersionHistories: &persistence.DataBlob{Encoding: "thriftrw", Data: []uint8("test-version-histories-1")}, }, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) query.EXPECT().PageSize(tc.pageSize).Return(query).Times(1) query.EXPECT().PageState(tc.pageToken).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) if tc.iter != nil { query.EXPECT().Iter().Return(tc.iter).Times(1) } else { // Passing tc.iter to Return() doesn't work even though tc.iter is nil due to Go's typed nils. // So, we have to call Return(nil) directly. query.EXPECT().Iter().Return(nil).Times(1) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotExecutions, gotPageToken, err := db.SelectAllWorkflowExecutions(context.Background(), tc.shardID, tc.pageToken, tc.pageSize) if (err != nil) != tc.wantErr { t.Errorf("SelectAllWorkflowExecutions() error: %v, wantErr %v", err, tc.wantErr) } if err != nil || tc.wantErr { return } if diff := cmp.Diff(tc.wantExecutions, gotExecutions); diff != "" { t.Fatalf("Executions mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.iter.pageState, gotPageToken); diff != "" { t.Fatalf("Page token mismatch (-want +got):\n%s", diff) } if !tc.iter.closed { t.Error("iter was not closed") } }) } } func TestIsWorkflowExecutionExists(t *testing.T) { tests := []struct { name string queryMockFn func(query *gocql.MockQuery) clientMockFn func(client *gocql.MockClient) want bool wantErr bool }{ { name: "success", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).Return(nil).Times(1) }, want: true, wantErr: false, }, { name: "not found case returns false but no error", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).Return(errors.New("an error that will be considered as not found err by client mock")).Times(1) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsNotFoundError(gomock.Any()).Return(true).Times(1) }, want: false, wantErr: false, }, { name: "arbitrary error case", queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).Return(errors.New("some error")).Times(1) }, clientMockFn: func(client *gocql.MockClient) { client.EXPECT().IsNotFoundError(gomock.Any()).Return(false).Times(1) }, want: false, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) if tc.clientMockFn != nil { tc.clientMockFn(client) } cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) got, err := db.IsWorkflowExecutionExists(context.Background(), 1, "domain1", "wfi", "run1") if (err != nil) != tc.wantErr { t.Errorf("IsWorkflowExecutionExists() error: %v, wantErr %v", err, tc.wantErr) } if err != nil || tc.wantErr { return } if got != tc.want { t.Errorf("IsWorkflowExecutionExists() got: %v, want: %v", got, tc.want) } }) } } func TestSelectTransferTasksOrderByTaskID(t *testing.T) { tests := []struct { name string shardID int pageToken []byte pageSize int inclusiveMinTaskID int64 exclusiveMaxTaskID int64 iter *fakeIter wantTasks []*nosqlplugin.HistoryMigrationTask wantNextPageToken []byte wantErr bool }{ { name: "nil iter returned", shardID: 1, pageToken: []byte("test-page-token"), pageSize: 10, wantErr: true, }, { name: "success", shardID: 1, pageToken: []byte("test-page-token"), pageSize: 10, iter: &fakeIter{ mapScanInputs: []map[string]interface{}{ { "task_id": int64(1), "transfer": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "workflow_id": "wfid1", "task_id": int64(1), }, "data": []byte("test-data-1"), "data_encoding": "thriftrw", }, { "task_id": int64(5), "transfer": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain2"}, "workflow_id": "wfid2", "task_id": int64(5), }, "data": []byte("test-data-2"), "data_encoding": "thriftrw", }, }, pageState: []byte("test-page-token-2"), }, wantTasks: []*nosqlplugin.HistoryMigrationTask{ { Transfer: &nosqlplugin.TransferTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 1, }, Task: persistence.NewDataBlob( []byte("test-data-1"), "thriftrw", ), TaskID: 1, }, { Transfer: &nosqlplugin.TransferTask{ DomainID: "domain2", WorkflowID: "wfid2", TaskID: 5, }, Task: persistence.NewDataBlob( []byte("test-data-2"), "thriftrw", ), TaskID: 5, }, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) query.EXPECT().PageSize(tc.pageSize).Return(query).Times(1) query.EXPECT().PageState(tc.pageToken).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) if tc.iter != nil { query.EXPECT().Iter().Return(tc.iter).Times(1) } else { // Passing tc.iter to Return() doesn't work even though tc.iter is nil due to Go's typed nils. // So, we have to call Return(nil) directly. query.EXPECT().Iter().Return(nil).Times(1) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotTasks, gotPageToken, err := db.SelectTransferTasksOrderByTaskID(context.Background(), tc.shardID, tc.pageSize, tc.pageToken, tc.inclusiveMinTaskID, tc.exclusiveMaxTaskID) if (err != nil) != tc.wantErr { t.Errorf("SelectAllWorkflowExecutions() error: %v, wantErr %v", err, tc.wantErr) } if err != nil || tc.wantErr { return } if diff := cmp.Diff(tc.wantTasks, gotTasks); diff != "" { t.Fatalf("Executions mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.iter.pageState, gotPageToken); diff != "" { t.Fatalf("Page token mismatch (-want +got):\n%s", diff) } if !tc.iter.closed { t.Error("iter was not closed") } }) } } func TestDeleteTransferTask(t *testing.T) { tests := []struct { name string shardID int taskID int64 queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, taskID: 123, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, taskID: 123, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.DeleteTransferTask(context.Background(), tc.shardID, tc.taskID) if (err != nil) != tc.wantErr { t.Errorf("DeleteTransferTask() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestRangeDeleteTransferTasks(t *testing.T) { tests := []struct { name string shardID int inclusiveBeginTaskID int64 exclusiveEndTaskID int64 queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, inclusiveBeginTaskID: 123, exclusiveEndTaskID: 456, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, inclusiveBeginTaskID: 123, exclusiveEndTaskID: 456, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.RangeDeleteTransferTasks(context.Background(), tc.shardID, tc.inclusiveBeginTaskID, tc.exclusiveEndTaskID) if (err != nil) != tc.wantErr { t.Errorf("RangeDeleteTransferTasks() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestSelectTimerTasksOrderByVisibilityTime(t *testing.T) { now := time.Now() tests := []struct { name string shardID int pageToken []byte pageSize int inclusiveMinTime time.Time exclusiveMaxTime time.Time iter *fakeIter wantTasks []*nosqlplugin.HistoryMigrationTask wantNextPageToken []byte wantErr bool }{ { name: "nil iter returned", shardID: 1, pageToken: []byte("test-page-token"), pageSize: 10, wantErr: true, }, { name: "success", shardID: 1, pageToken: []byte("test-page-token"), pageSize: 10, iter: &fakeIter{ mapScanInputs: []map[string]interface{}{ { "visibility_ts": now, "task_id": int64(1), "timer": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "workflow_id": "wfid1", "visibility_ts": now, }, "data": []byte("test-data-1"), "data_encoding": "thriftrw", }, { "visibility_ts": now.Add(time.Hour), "task_id": int64(5), "timer": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain2"}, "workflow_id": "wfid2", "visibility_ts": now.Add(time.Hour), }, "data": []byte("test-data-2"), "data_encoding": "thriftrw", }, }, pageState: []byte("test-page-token-2"), }, wantTasks: []*nosqlplugin.HistoryMigrationTask{ { Timer: &nosqlplugin.TimerTask{ DomainID: "domain1", WorkflowID: "wfid1", VisibilityTimestamp: now, }, Task: persistence.NewDataBlob( []byte("test-data-1"), "thriftrw", ), TaskID: 1, ScheduledTime: now, }, { Timer: &nosqlplugin.TimerTask{ DomainID: "domain2", WorkflowID: "wfid2", VisibilityTimestamp: now.Add(time.Hour), }, Task: persistence.NewDataBlob( []byte("test-data-2"), "thriftrw", ), TaskID: 5, ScheduledTime: now.Add(time.Hour), }, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) query.EXPECT().PageSize(tc.pageSize).Return(query).Times(1) query.EXPECT().PageState(tc.pageToken).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) if tc.iter != nil { query.EXPECT().Iter().Return(tc.iter).Times(1) } else { // Passing tc.iter to Return() doesn't work even though tc.iter is nil due to Go's typed nils. // So, we have to call Return(nil) directly. query.EXPECT().Iter().Return(nil).Times(1) } session := &fakeSession{ query: query, } client := gocql.NewMockClient(ctrl) cfg := &config.NoSQL{} logger := testlogger.New(t) dc := &persistence.DynamicConfiguration{} db := NewCassandraDBFromSession(cfg, session, logger, dc, DbWithClient(client)) gotTasks, gotPageToken, err := db.SelectTimerTasksOrderByVisibilityTime(context.Background(), tc.shardID, tc.pageSize, tc.pageToken, tc.inclusiveMinTime, tc.exclusiveMaxTime) if (err != nil) != tc.wantErr { t.Errorf("SelectTimerTasksOrderByVisibilityTime() error: %v, wantErr %v", err, tc.wantErr) } if err != nil || tc.wantErr { return } if diff := cmp.Diff(tc.wantTasks, gotTasks); diff != "" { t.Fatalf("Tasks mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.iter.pageState, gotPageToken); diff != "" { t.Fatalf("Page token mismatch (-want +got):\n%s", diff) } if !tc.iter.closed { t.Error("iter was not closed") } }) } } func TestDeleteTimerTask(t *testing.T) { now := time.Now() tests := []struct { name string shardID int taskID int64 visibilityTimestamp time.Time queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, taskID: 123, visibilityTimestamp: now, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, taskID: 123, visibilityTimestamp: now, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.DeleteTimerTask(context.Background(), tc.shardID, tc.taskID, tc.visibilityTimestamp) if (err != nil) != tc.wantErr { t.Errorf("DeleteTimerTask() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestRangeDeleteTimerTasks(t *testing.T) { now := time.Now() tests := []struct { name string shardID int inclusiveMinTime time.Time exclusiveMaxTime time.Time queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, inclusiveMinTime: now, exclusiveMaxTime: now.Add(time.Hour), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, inclusiveMinTime: now, exclusiveMaxTime: now.Add(time.Hour), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.RangeDeleteTimerTasks(context.Background(), tc.shardID, tc.inclusiveMinTime, tc.exclusiveMaxTime) if (err != nil) != tc.wantErr { t.Errorf("RangeDeleteTimerTasks() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestSelectReplicationTasksOrderByTaskID(t *testing.T) { tests := []struct { name string shardID int inclusiveMinTaskID int64 exclusiveMaxTaskID int64 pageSize int pageToken []byte queryMockFn func(query *gocql.MockQuery) wantTasks []*nosqlplugin.HistoryMigrationTask wantErr bool }{ { name: "success", shardID: 1, inclusiveMinTaskID: 100, exclusiveMaxTaskID: 200, pageSize: 100, pageToken: []byte("test-page-token"), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().PageSize(100).Return(query).Times(1) query.EXPECT().PageState([]byte("test-page-token")).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Iter().Return(&fakeIter{ mapScanInputs: []map[string]interface{}{ { "task_id": int64(1), "replication": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "workflow_id": "wfid1", "task_id": int64(1), }, "data": []byte("test-data-1"), "data_encoding": "thriftrw", }, { "task_id": int64(2), "replication": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "workflow_id": "wfid1", "task_id": int64(2), }, "data": []byte("test-data-2"), "data_encoding": "thriftrw", }, }, }).Times(1) }, wantTasks: []*nosqlplugin.HistoryMigrationTask{ { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 1, }, Task: persistence.NewDataBlob( []byte("test-data-1"), "thriftrw", ), TaskID: 1, }, { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 2, }, Task: persistence.NewDataBlob( []byte("test-data-2"), "thriftrw", ), TaskID: 2, }, }, }, { name: "query iter fails", shardID: 1, inclusiveMinTaskID: 100, exclusiveMaxTaskID: 200, pageSize: 100, pageToken: []byte("test-page-token"), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().PageSize(100).Return(query).Times(1) query.EXPECT().PageState([]byte("test-page-token")).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Iter().Return(nil).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) gotTasks, _, err := db.SelectReplicationTasksOrderByTaskID(context.Background(), tc.shardID, tc.pageSize, tc.pageToken, tc.inclusiveMinTaskID, tc.exclusiveMaxTaskID) if (err != nil) != tc.wantErr { t.Errorf("SelectReplicationTasksOrderByTaskID() error: %v, wantErr: %v", err, tc.wantErr) } if err != nil || tc.wantErr { return } if diff := cmp.Diff(tc.wantTasks, gotTasks); diff != "" { t.Fatalf("Tasks mismatch (-want +got):\n%s", diff) } }) } } func TestDeleteReplicationTask(t *testing.T) { tests := []struct { name string shardID int taskID int64 queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, taskID: 123, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, taskID: 123, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.DeleteReplicationTask(context.Background(), tc.shardID, tc.taskID) if (err != nil) != tc.wantErr { t.Errorf("DeleteReplicationTask() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestRangeDeleteReplicationTasks(t *testing.T) { tests := []struct { name string shardID int exclusiveEndTaskID int64 queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, exclusiveEndTaskID: 123, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, exclusiveEndTaskID: 123, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.RangeDeleteReplicationTasks(context.Background(), tc.shardID, tc.exclusiveEndTaskID) if (err != nil) != tc.wantErr { t.Errorf("RangeDeleteReplicationTasks() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestDeleteCrossClusterTask(t *testing.T) { tests := []struct { name string shardID int targetCluster string taskID int64 queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, targetCluster: "test-target-cluster", taskID: 123, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, targetCluster: "test-target-cluster", taskID: 123, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.DeleteCrossClusterTask(context.Background(), tc.shardID, tc.targetCluster, tc.taskID) if (err != nil) != tc.wantErr { t.Errorf("DeleteCrossClusterTask() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestInsertReplicationDLQTask(t *testing.T) { tests := []struct { name string shardID int sourceCluster string taskID int64 task *nosqlplugin.HistoryMigrationTask queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, sourceCluster: "test-source-cluster", taskID: 123, task: &nosqlplugin.HistoryMigrationTask{ Replication: &nosqlplugin.ReplicationTask{ TaskID: 123, }, Task: &persistence.DataBlob{ Data: []byte("dlq"), Encoding: constants.EncodingTypeThriftRW, }, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, sourceCluster: "test-source-cluster", taskID: 123, task: &nosqlplugin.HistoryMigrationTask{ Replication: &nosqlplugin.ReplicationTask{ TaskID: 123, }, Task: &persistence.DataBlob{ Data: []byte("dlq"), Encoding: constants.EncodingTypeThriftRW, }, }, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.InsertReplicationDLQTask(context.Background(), tc.shardID, tc.sourceCluster, tc.task) if (err != nil) != tc.wantErr { t.Errorf("RangeDeleteCrossClusterTasks() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestSelectReplicationDLQTasksOrderByTaskID(t *testing.T) { tests := []struct { name string shardID int inclusiveMinTaskID int64 exclusiveMaxTaskID int64 pageSize int pageToken []byte queryMockFn func(query *gocql.MockQuery) wantTasks []*nosqlplugin.HistoryMigrationTask wantErr bool }{ { name: "success", shardID: 1, inclusiveMinTaskID: 100, exclusiveMaxTaskID: 200, pageSize: 100, pageToken: []byte("test-page-token"), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().PageSize(100).Return(query).Times(1) query.EXPECT().PageState([]byte("test-page-token")).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Iter().Return(&fakeIter{ mapScanInputs: []map[string]interface{}{ { "task_id": int64(1), "replication": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "workflow_id": "wfid1", "task_id": int64(1), }, "data": []byte("test-data-1"), "data_encoding": "thriftrw", }, { "task_id": int64(2), "replication": map[string]interface{}{ "domain_id": &fakeUUID{uuid: "domain1"}, "workflow_id": "wfid1", "task_id": int64(2), }, "data": []byte("test-data-2"), "data_encoding": "thriftrw", }, }, }).Times(1) }, wantTasks: []*nosqlplugin.HistoryMigrationTask{ { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 1, }, Task: persistence.NewDataBlob( []byte("test-data-1"), "thriftrw", ), TaskID: 1, }, { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 2, }, Task: persistence.NewDataBlob( []byte("test-data-2"), "thriftrw", ), TaskID: 2, }, }, }, { name: "query iter fails", shardID: 1, inclusiveMinTaskID: 100, exclusiveMaxTaskID: 200, pageSize: 100, pageToken: []byte("test-page-token"), queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().PageSize(100).Return(query).Times(1) query.EXPECT().PageState([]byte("test-page-token")).Return(query).Times(1) query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Iter().Return(nil).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) gotTasks, _, err := db.SelectReplicationDLQTasksOrderByTaskID(context.Background(), tc.shardID, "src-cluster", tc.pageSize, tc.pageToken, tc.inclusiveMinTaskID, tc.exclusiveMaxTaskID) if (err != nil) != tc.wantErr { t.Errorf("SelectReplicationDLQTasksOrderByTaskID() error: %v, wantErr: %v", err, tc.wantErr) } if err != nil || tc.wantErr { return } if diff := cmp.Diff(tc.wantTasks, gotTasks); diff != "" { t.Fatalf("Tasks mismatch (-want +got):\n%s", diff) } }) } } func TestSelectReplicationDLQTasksCount(t *testing.T) { tests := []struct { name string shardID int queryMockFn func(query *gocql.MockQuery) wantCount int64 wantErr bool }{ { name: "success", shardID: 1, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).DoAndReturn(func(m map[string]interface{}) error { m["count"] = int64(42) return nil }).Times(1) }, wantCount: 42, }, { name: "query mapscan fails", shardID: 1, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().MapScan(gomock.Any()).Return(errors.New("failed to scan")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) gotCount, err := db.SelectReplicationDLQTasksCount(context.Background(), tc.shardID, "src-cluster") if (err != nil) != tc.wantErr { t.Errorf("SelectReplicationDLQTasksCount() error: %v, wantErr: %v", err, tc.wantErr) } if err != nil || tc.wantErr { return } if gotCount != tc.wantCount { t.Fatalf("got count %v, want %v", gotCount, tc.wantCount) } }) } } func TestDeleteReplicationDLQTask(t *testing.T) { tests := []struct { name string shardID int sourceCluster string taskID int64 queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, sourceCluster: "test-source-cluster", taskID: 123, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, sourceCluster: "test-source-cluster", taskID: 123, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.DeleteReplicationDLQTask(context.Background(), tc.shardID, tc.sourceCluster, tc.taskID) if (err != nil) != tc.wantErr { t.Errorf("DeleteReplicationDLQTask() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestRangeDeleteReplicationDLQTasks(t *testing.T) { tests := []struct { name string shardID int sourceCluster string inclusiveEndTaskID int64 exclusiveBeginTaskID int64 queryMockFn func(query *gocql.MockQuery) wantErr bool }{ { name: "success", shardID: 1, sourceCluster: "test-source-cluster", inclusiveEndTaskID: 300, exclusiveBeginTaskID: 200, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(nil).Times(1) }, wantErr: false, }, { name: "query exec fails", shardID: 1, sourceCluster: "test-source-cluster", inclusiveEndTaskID: 300, exclusiveBeginTaskID: 200, queryMockFn: func(query *gocql.MockQuery) { query.EXPECT().WithContext(gomock.Any()).Return(query).Times(1) query.EXPECT().Exec().Return(errors.New("failed to exec")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) query := gocql.NewMockQuery(ctrl) tc.queryMockFn(query) session := &fakeSession{ query: query, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.RangeDeleteReplicationDLQTasks(context.Background(), tc.shardID, tc.sourceCluster, tc.exclusiveBeginTaskID, tc.inclusiveEndTaskID) if (err != nil) != tc.wantErr { t.Errorf("RangeDeleteReplicationDLQTasks() error: %v, wantErr: %v", err, tc.wantErr) } }) } } func TestInsertReplicationTask(t *testing.T) { tests := []struct { name string tasks []*nosqlplugin.HistoryMigrationTask shardCondition nosqlplugin.ShardCondition mapExecuteBatchCASApplied bool mapExecuteBatchCASPrev map[string]any mapExecuteBatchCASErr error wantErr bool wantPanic bool }{ { name: "no tasks", wantErr: false, }, { name: "mapExecuteBatchCASErr failure", tasks: []*nosqlplugin.HistoryMigrationTask{ { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 1, }, Task: &persistence.DataBlob{ Data: []byte("r1"), Encoding: constants.EncodingTypeThriftRW, }, }, { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 2, }, Task: &persistence.DataBlob{ Data: []byte("r2"), Encoding: constants.EncodingTypeThriftRW, }, }, }, mapExecuteBatchCASErr: errors.New("failed to execute batch"), wantErr: true, }, { name: "not applied and row type not found causes panic", tasks: []*nosqlplugin.HistoryMigrationTask{ { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 1, }, Task: &persistence.DataBlob{ Data: []byte("r1"), Encoding: constants.EncodingTypeThriftRW, }, }, { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 2, }, Task: &persistence.DataBlob{ Data: []byte("r2"), Encoding: constants.EncodingTypeThriftRW, }, }, }, mapExecuteBatchCASApplied: false, wantPanic: true, }, { name: "not applied, row type shard condition failure", tasks: []*nosqlplugin.HistoryMigrationTask{ { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 1, }, Task: &persistence.DataBlob{ Data: []byte("r1"), Encoding: constants.EncodingTypeThriftRW, }, }, { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 2, }, Task: &persistence.DataBlob{ Data: []byte("r2"), Encoding: constants.EncodingTypeThriftRW, }, }, }, mapExecuteBatchCASApplied: false, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeShard, "range_id": int64(5), }, shardCondition: nosqlplugin.ShardCondition{ ShardID: 1, RangeID: 4, // mismatch with prev range_id causes failure }, wantErr: true, }, { name: "not applied, unknown shard condition failure", tasks: []*nosqlplugin.HistoryMigrationTask{ { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 1, }, Task: &persistence.DataBlob{ Data: []byte("r1"), Encoding: constants.EncodingTypeThriftRW, }, }, { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 2, }, Task: &persistence.DataBlob{ Data: []byte("r2"), Encoding: constants.EncodingTypeThriftRW, }, }, }, mapExecuteBatchCASApplied: false, mapExecuteBatchCASPrev: map[string]any{ "type": -1, // not a shard type row }, wantErr: true, }, { name: "successfully applied", tasks: []*nosqlplugin.HistoryMigrationTask{ { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 1, }, Task: &persistence.DataBlob{ Data: []byte("r1"), Encoding: constants.EncodingTypeThriftRW, }, }, { Replication: &nosqlplugin.ReplicationTask{ DomainID: "domain1", WorkflowID: "wfid1", TaskID: 2, }, Task: &persistence.DataBlob{ Data: []byte("r2"), Encoding: constants.EncodingTypeThriftRW, }, }, }, mapExecuteBatchCASApplied: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { defer func() { if r := recover(); (r != nil) != tc.wantPanic { t.Errorf("got panic: %v, wantPanic: %v", r, tc.wantPanic) } }() ctrl := gomock.NewController(t) session := &fakeSession{ mapExecuteBatchCASApplied: tc.mapExecuteBatchCASApplied, mapExecuteBatchCASPrev: tc.mapExecuteBatchCASPrev, mapExecuteBatchCASErr: tc.mapExecuteBatchCASErr, iter: &fakeIter{}, } logger := testlogger.New(t) db := NewCassandraDBFromSession(nil, session, logger, nil, DbWithClient(gocql.NewMockClient(ctrl))) err := db.InsertReplicationTask(context.Background(), tc.tasks, tc.shardCondition) if (err != nil) != tc.wantErr { t.Errorf("InsertReplicationTask() error: %v, wantErr: %v", err, tc.wantErr) } if err != nil || tc.wantErr || len(tc.tasks) == 0 { return } if len(session.batches) != 1 { t.Fatalf("got %v batches, want 1", len(session.batches)) } if len(tc.tasks)+1 != len(session.batches[0].queries) { t.Errorf("got %v batches, want %v", len(session.batches), len(tc.tasks)) } }) } } func TestSelectActiveClusterSelectionPolicy(t *testing.T) { tests := []struct { name string shardID int domainID string wfID string rID string session *fakeSession mockFn func(cl *gocql.MockClient) wantQuery string wantPolicy *nosqlplugin.ActiveClusterSelectionPolicyRow wantErr bool }{ { name: "success", shardID: 1, domainID: "domain1", wfID: "wfid1", rID: "r1", session: &fakeSession{ query: &fakeQuery{ mapScan: map[string]interface{}{ "data": []byte("data1"), "data_encoding": "thriftrw", }, }, }, wantQuery: `SELECT data, data_encoding FROM executions WHERE ` + `shard_id = 1 and type = 11 and domain_id = domain1 and ` + `workflow_id = wfid1 and run_id = r1 and visibility_ts = 946684800000 and task_id = -1001`, wantPolicy: &nosqlplugin.ActiveClusterSelectionPolicyRow{ Policy: persistence.NewDataBlob([]byte("data1"), constants.EncodingTypeThriftRW), ShardID: 1, DomainID: "domain1", WorkflowID: "wfid1", RunID: "r1", }, }, { name: "not found - returns nil", shardID: 1, domainID: "domain2", wfID: "wfid2", rID: "r2", session: &fakeSession{ query: &fakeQuery{ mapScan: map[string]interface{}{}, err: errors.New("not found"), }, }, wantQuery: `SELECT data, data_encoding FROM executions WHERE ` + `shard_id = 1 and type = 11 and domain_id = domain2 and ` + `workflow_id = wfid2 and run_id = r2 and visibility_ts = 946684800000 and task_id = -1001`, mockFn: func(cl *gocql.MockClient) { cl.EXPECT().IsNotFoundError(errors.New("not found")).Return(true).Times(1) }, wantPolicy: nil, wantErr: false, }, { name: "query failed", shardID: 1, domainID: "domain3", wfID: "wfid3", rID: "r3", session: &fakeSession{ query: &fakeQuery{ mapScan: map[string]interface{}{}, err: errors.New("failed"), }, }, wantQuery: `SELECT data, data_encoding FROM executions WHERE ` + `shard_id = 1 and type = 11 and domain_id = domain3 and ` + `workflow_id = wfid3 and run_id = r3 and visibility_ts = 946684800000 and task_id = -1001`, mockFn: func(cl *gocql.MockClient) { cl.EXPECT().IsNotFoundError(errors.New("failed")).Return(false).Times(1) }, wantPolicy: nil, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) logger := testlogger.New(t) cl := gocql.NewMockClient(ctrl) db := NewCassandraDBFromSession(nil, tc.session, logger, nil, DbWithClient(cl)) if tc.mockFn != nil { tc.mockFn(cl) } policy, err := db.SelectActiveClusterSelectionPolicy(context.Background(), tc.shardID, tc.domainID, tc.wfID, tc.rID) if (err != nil) != tc.wantErr { t.Errorf("SelectActiveClusterSelectionPolicy() error: %v, wantErr: %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantPolicy, policy); diff != "" { t.Fatalf("Policy mismatch (-want +got):\n%s", diff) } if diff := cmp.Diff(tc.wantQuery, tc.session.queries[0]); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestDeleteActiveClusterSelectionPolicy(t *testing.T) { tests := []struct { name string shardID int domainID string wfID string rID string session *fakeSession wantQuery string wantErr bool }{ { name: "success", shardID: 1, domainID: "domain1", wfID: "wfid1", rID: "r1", session: &fakeSession{ query: &fakeQuery{ mapScan: map[string]interface{}{}, }, }, wantQuery: `DELETE FROM executions WHERE ` + `shard_id = 1 and type = 11 and domain_id = domain1 and ` + `workflow_id = wfid1 and run_id = r1 and visibility_ts = 946684800000 and task_id = -1001`, wantErr: false, }, { name: "query failed", shardID: 1, domainID: "domain2", wfID: "wfid2", rID: "r2", session: &fakeSession{ query: &fakeQuery{ mapScan: map[string]interface{}{}, err: errors.New("failed"), }, }, wantQuery: `DELETE FROM executions WHERE ` + `shard_id = 1 and type = 11 and domain_id = domain2 and ` + `workflow_id = wfid2 and run_id = r2 and visibility_ts = 946684800000 and task_id = -1001`, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) logger := testlogger.New(t) cl := gocql.NewMockClient(ctrl) db := NewCassandraDBFromSession(nil, tc.session, logger, nil, DbWithClient(cl)) err := db.DeleteActiveClusterSelectionPolicy(context.Background(), tc.shardID, tc.domainID, tc.wfID, tc.rID) if (err != nil) != tc.wantErr { t.Errorf("DeleteActiveClusterSelectionPolicy() error: %v, wantErr: %v", err, tc.wantErr) } if diff := cmp.Diff(tc.wantQuery, tc.session.queries[0]); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/workflow_utils.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "fmt" "reflect" "strings" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/types" ) func executeCreateWorkflowBatchTransaction( ctx context.Context, session gocql.Session, batch gocql.Batch, currentWorkflowRequest *nosqlplugin.CurrentWorkflowWriteRequest, execution *nosqlplugin.WorkflowExecutionRequest, shardCondition *nosqlplugin.ShardCondition, ) error { previous := make(map[string]interface{}) applied, iter, err := session.MapExecuteBatchCAS(batch, previous) defer func() { if iter != nil { _ = iter.Close() } }() if err != nil { return err } if applied { return nil } requestRangeID := shardCondition.RangeID requestConditionalRunID := "" if currentWorkflowRequest.Condition != nil { requestConditionalRunID = currentWorkflowRequest.Condition.GetCurrentRunID() } // There can be two reasons why the query does not get applied. Either the RangeID has changed, or // the workflow is already started. Check the row info returned by Cassandra to figure out which one it is. rangeIDMismatch := false actualRangeID := int64(0) currentExecutionAlreadyExists := false var actualExecution map[string]interface{} var actualExecutionFullRecord map[string]interface{} runIDMismatch := false actualCurrRunID := "" lastWriteVersionMismatch := false actualLastWriteVersion := int64(constants.EmptyVersion) stateMismatch := false actualState := int(0) concreteExecutionAlreadyExists := false workflowRequestAlreadyExists := false workflowRequestID := "" requestRowType := int(0) var allPrevious []map[string]interface{} for { rowType, ok := previous["type"].(int) if !ok { // This should never happen, as all our rows have the type field. break } runID := previous["run_id"].(gocql.UUID).String() if rowType == rowTypeShard { if actualRangeID, ok = previous["range_id"].(int64); ok && actualRangeID != requestRangeID { // UpdateWorkflowExecution failed because rangeID was modified rangeIDMismatch = true } } else if rowType == rowTypeExecution && runID == permanentRunID { if currentWorkflowRequest.WriteMode == nosqlplugin.CurrentWorkflowWriteModeInsert { currentExecutionAlreadyExists = true actualExecutionFullRecord = previous actualExecution, _ = previous["execution"].(map[string]interface{}) if actualExecution != nil { if previous["workflow_last_write_version"] != nil { actualLastWriteVersion = previous["workflow_last_write_version"].(int64) } } } else if currentWorkflowRequest.WriteMode == nosqlplugin.CurrentWorkflowWriteModeUpdate { if actualCurrRunID = previous["current_run_id"].(gocql.UUID).String(); requestConditionalRunID != "" && actualCurrRunID != requestConditionalRunID { runIDMismatch = true } if currentWorkflowRequest.Condition != nil && currentWorkflowRequest.Condition.LastWriteVersion != nil { ok := false if actualLastWriteVersion, ok = previous["workflow_last_write_version"].(int64); ok && *currentWorkflowRequest.Condition.LastWriteVersion != actualLastWriteVersion { lastWriteVersionMismatch = true } } if currentWorkflowRequest.Condition != nil && currentWorkflowRequest.Condition.State != nil { ok := false if actualState, ok = previous["workflow_state"].(int); ok && *currentWorkflowRequest.Condition.State != actualState { stateMismatch = true } } } } else if rowType == rowTypeExecution && execution.RunID == runID { concreteExecutionAlreadyExists = true if previous["workflow_last_write_version"] != nil { actualLastWriteVersion = previous["workflow_last_write_version"].(int64) } } else if isRequestRowType(rowType) { workflowRequestAlreadyExists = true workflowRequestID = runID requestRowType = rowType } allPrevious = append(allPrevious, previous) previous = make(map[string]interface{}) if !iter.MapScan(previous) { // Cassandra returns the actual row that caused a condition failure, so we should always return // from the checks above, but just in case. break } } if rangeIDMismatch { return &nosqlplugin.WorkflowOperationConditionFailure{ ShardRangeIDNotMatch: common.Int64Ptr(actualRangeID), } } if workflowRequestAlreadyExists { result := make(map[string]interface{}) if err := session.Query( templateGetLatestWorkflowRequestQuery, shardCondition.ShardID, requestRowType, execution.DomainID, execution.WorkflowID, workflowRequestID, defaultVisibilityTimestamp, ).WithContext(ctx).MapScan(result); err != nil { return err } runID, ok := result["current_run_id"].(gocql.UUID) if !ok { return fmt.Errorf("corrupted data detected. DomainID: %v, WorkflowId: %v, RequestID: %v, RequestType: %v", execution.DomainID, execution.WorkflowID, workflowRequestID, requestRowType) } requestType, err := fromRequestRowType(requestRowType) if err != nil { return err } return &nosqlplugin.WorkflowOperationConditionFailure{ DuplicateRequest: &nosqlplugin.DuplicateRequest{ RequestType: requestType, RunID: runID.String(), }, } } // CreateWorkflowExecution failed because there is already a current execution record for this workflow if currentExecutionAlreadyExists { if actualExecution != nil { executionInfo, err := parseWorkflowExecutionInfo(actualExecutionFullRecord) if err != nil { return err } msg := fmt.Sprintf("Workflow execution already running. WorkflowId: %v, RunId: %v", currentWorkflowRequest.Row.WorkflowID, executionInfo.RunID) return &nosqlplugin.WorkflowOperationConditionFailure{ WorkflowExecutionAlreadyExists: &nosqlplugin.WorkflowExecutionAlreadyExists{ OtherInfo: msg, CreateRequestID: executionInfo.CreateRequestID, RunID: executionInfo.RunID, State: executionInfo.State, CloseStatus: executionInfo.CloseStatus, LastWriteVersion: actualLastWriteVersion, }, } } msg := fmt.Sprintf("Workflow execution already running. WorkflowId: %v", currentWorkflowRequest.Row.WorkflowID) return &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: &msg, } } if runIDMismatch { msg := fmt.Sprintf("Workflow execution creation condition failed by mismatch runID. WorkflowId: %v, Expected Current RunID: %v, Actual Current RunID: %v", currentWorkflowRequest.Row.WorkflowID, currentWorkflowRequest.Condition.GetCurrentRunID(), actualCurrRunID) return &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: &msg, } } if lastWriteVersionMismatch { msg := fmt.Sprintf("Workflow execution creation condition failed. WorkflowId: %v, Expected Version: %v, Actual Version: %v", currentWorkflowRequest.Row.WorkflowID, *currentWorkflowRequest.Condition.LastWriteVersion, actualLastWriteVersion) return &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: &msg, } } if stateMismatch { msg := fmt.Sprintf("Workflow execution creation condition failed. WorkflowId: %v, Expected State: %v, Actual State: %v", currentWorkflowRequest.Row.WorkflowID, *currentWorkflowRequest.Condition.State, actualState) return &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: &msg, } } if concreteExecutionAlreadyExists { msg := fmt.Sprintf("Workflow execution already running. WorkflowId: %v, RunId: %v", execution.WorkflowID, execution.RunID) return &nosqlplugin.WorkflowOperationConditionFailure{ WorkflowExecutionAlreadyExists: &nosqlplugin.WorkflowExecutionAlreadyExists{ OtherInfo: msg, CreateRequestID: execution.CreateRequestID, RunID: execution.RunID, State: execution.State, CloseStatus: execution.CloseStatus, LastWriteVersion: actualLastWriteVersion, }, } } // At this point we only know that the write was not applied. var columns []string columnID := 0 for _, previous := range allPrevious { for k, v := range previous { columns = append(columns, fmt.Sprintf("%v: %s=%v", columnID, k, v)) } columnID++ } return newUnknownConditionFailureReason(shardCondition.RangeID, columns) } func executeUpdateWorkflowBatchTransaction( ctx context.Context, session gocql.Session, batch gocql.Batch, currentWorkflowRequest *nosqlplugin.CurrentWorkflowWriteRequest, previousNextEventIDCondition int64, shardCondition *nosqlplugin.ShardCondition, ) error { previous := make(map[string]interface{}) applied, iter, err := session.MapExecuteBatchCAS(batch, previous) defer func() { if iter != nil { _ = iter.Close() } }() if err != nil { return err } if applied { return nil } requestRunID := currentWorkflowRequest.Row.RunID requestRangeID := shardCondition.RangeID requestConditionalRunID := "" if currentWorkflowRequest.Condition != nil { requestConditionalRunID = currentWorkflowRequest.Condition.GetCurrentRunID() } // There can be three reasons why the query does not get applied: the RangeID has changed, or the next_event_id or current_run_id check failed. // Check the row info returned by Cassandra to figure out which one it is. rangeIDMismatch := false actualRangeID := int64(0) nextEventIDMismatch := false actualNextEventID := int64(0) runIDMismatch := false actualCurrRunID := "" workflowRequestAlreadyExists := false workflowRequestID := "" requestRowType := int(0) var allPrevious []map[string]interface{} for { rowType, ok := previous["type"].(int) if !ok { // This should never happen, as all our rows have the type field. break } runID := previous["run_id"].(gocql.UUID).String() if rowType == rowTypeShard { if actualRangeID, ok = previous["range_id"].(int64); ok && actualRangeID != requestRangeID { // UpdateWorkflowExecution failed because rangeID was modified rangeIDMismatch = true } } else if rowType == rowTypeExecution && runID == requestRunID { if actualNextEventID, ok = previous["next_event_id"].(int64); ok && actualNextEventID != previousNextEventIDCondition { // UpdateWorkflowExecution failed because next event ID is unexpected nextEventIDMismatch = true } } else if rowType == rowTypeExecution && runID == permanentRunID { // UpdateWorkflowExecution failed because current_run_id is unexpected if actualCurrRunID = previous["current_run_id"].(gocql.UUID).String(); requestConditionalRunID != "" && actualCurrRunID != requestConditionalRunID { // UpdateWorkflowExecution failed because next event ID is unexpected runIDMismatch = true } } else if isRequestRowType(rowType) { workflowRequestAlreadyExists = true workflowRequestID = runID requestRowType = rowType } allPrevious = append(allPrevious, previous) previous = make(map[string]interface{}) if !iter.MapScan(previous) { // Cassandra returns the actual row that caused a condition failure, so we should always return // from the checks above, but just in case. break } } if rangeIDMismatch { return &nosqlplugin.WorkflowOperationConditionFailure{ ShardRangeIDNotMatch: common.Int64Ptr(actualRangeID), } } if workflowRequestAlreadyExists { result := make(map[string]interface{}) if err := session.Query( templateGetLatestWorkflowRequestQuery, shardCondition.ShardID, requestRowType, currentWorkflowRequest.Row.DomainID, currentWorkflowRequest.Row.WorkflowID, workflowRequestID, defaultVisibilityTimestamp, ).WithContext(ctx).MapScan(result); err != nil { return err } runID, ok := result["current_run_id"].(gocql.UUID) if !ok { return fmt.Errorf("corrupted data detected. DomainID: %v, WorkflowId: %v, RequestID: %v, RequestType: %v", currentWorkflowRequest.Row.DomainID, currentWorkflowRequest.Row.WorkflowID, workflowRequestID, requestRowType) } requestType, err := fromRequestRowType(requestRowType) if err != nil { return err } return &nosqlplugin.WorkflowOperationConditionFailure{ DuplicateRequest: &nosqlplugin.DuplicateRequest{ RequestType: requestType, RunID: runID.String(), }, } } if runIDMismatch { msg := fmt.Sprintf("Failed to update mutable state. requestConditionalRunID: %v, Actual Value: %v", requestConditionalRunID, actualCurrRunID) return &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: &msg, } } if nextEventIDMismatch { msg := fmt.Sprintf("Failed to update mutable state. previousNextEventIDCondition: %v, actualNextEventID: %v, Request Current RunID: %v", previousNextEventIDCondition, actualNextEventID, requestRunID) return &nosqlplugin.WorkflowOperationConditionFailure{ UnknownConditionFailureDetails: &msg, } } // At this point we only know that the write was not applied. var columns []string columnID := 0 for _, previous := range allPrevious { for k, v := range previous { columns = append(columns, fmt.Sprintf("%v: %s=%v", columnID, k, v)) } columnID++ } msg := fmt.Sprintf("Failed to update mutable state. ShardID: %v, RangeID: %v, previousNextEventIDCondition: %v, requestConditionalRunID: %v, columns: (%v)", shardCondition.ShardID, requestRangeID, previousNextEventIDCondition, requestConditionalRunID, strings.Join(columns, ",")) return &nosqlplugin.WorkflowOperationConditionFailure{ UnknownConditionFailureDetails: &msg, } } func newUnknownConditionFailureReason( rangeID int64, columns []string, ) *nosqlplugin.WorkflowOperationConditionFailure { msg := fmt.Sprintf("Failed to operate on workflow execution. Request RangeID: %v, columns: (%v)", rangeID, strings.Join(columns, ",")) return &nosqlplugin.WorkflowOperationConditionFailure{ UnknownConditionFailureDetails: &msg, } } func assertShardRangeID(batch gocql.Batch, shardID int, rangeID int64, timeStamp time.Time) { batch.Query(templateUpdateLeaseQuery, rangeID, timeStamp, shardID, rowTypeShard, rowTypeShardDomainID, rowTypeShardWorkflowID, rowTypeShardRunID, defaultVisibilityTimestamp, rowTypeShardTaskID, rangeID, ) } func createTasksByCategory( batch gocql.Batch, shardID int, domainID string, workflowID string, timeStamp time.Time, tasksByCategory map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask, ) { for c, tasks := range tasksByCategory { switch c.ID() { case persistence.HistoryTaskCategoryIDTransfer: createTransferTasks(batch, shardID, domainID, workflowID, tasks, timeStamp) case persistence.HistoryTaskCategoryIDTimer: createTimerTasks(batch, shardID, domainID, workflowID, tasks, timeStamp) case persistence.HistoryTaskCategoryIDReplication: createReplicationTasks(batch, shardID, domainID, workflowID, tasks, timeStamp) } } // TODO: implementing writing tasks for other categories } func createTimerTasks( batch gocql.Batch, shardID int, domainID string, workflowID string, timerTasks []*nosqlplugin.HistoryMigrationTask, timeStamp time.Time, ) error { for _, timer := range timerTasks { task := timer.Timer taskBlob, taskEncoding := persistence.FromDataBlob(timer.Task) // Ignoring possible type cast errors. ts := persistence.UnixNanoToDBTimestamp(task.VisibilityTimestamp.UnixNano()) batch.Query(templateCreateTimerTaskQuery, shardID, rowTypeTimerTask, rowTypeTimerDomainID, rowTypeTimerWorkflowID, rowTypeTimerRunID, domainID, workflowID, task.RunID, ts, task.TaskID, task.TaskType, task.TimeoutType, task.EventID, task.ScheduleAttempt, task.Version, task.TaskList, taskBlob, taskEncoding, ts, task.TaskID, timeStamp, ) } return nil } func createReplicationTasks( batch gocql.Batch, shardID int, domainID string, workflowID string, replicationTasks []*nosqlplugin.HistoryMigrationTask, timeStamp time.Time, ) { for _, replication := range replicationTasks { task := replication.Replication taskBlob, taskEncoding := persistence.FromDataBlob(replication.Task) batch.Query(templateCreateReplicationTaskQuery, shardID, rowTypeReplicationTask, rowTypeReplicationDomainID, rowTypeReplicationWorkflowID, rowTypeReplicationRunID, domainID, workflowID, task.RunID, task.TaskID, task.TaskType, task.FirstEventID, task.NextEventID, task.Version, task.ScheduledID, persistence.EventStoreVersion, task.BranchToken, persistence.EventStoreVersion, task.NewRunBranchToken, task.CreationTime.UnixNano(), taskBlob, taskEncoding, // NOTE: use a constant here instead of task.VisibilityTimestamp so that we can query tasks with the same visibilityTimestamp defaultVisibilityTimestamp, task.TaskID, timeStamp, ) } } func createTransferTasks( batch gocql.Batch, shardID int, domainID string, workflowID string, transferTasks []*nosqlplugin.HistoryMigrationTask, timeStamp time.Time, ) { for _, transfer := range transferTasks { task := transfer.Transfer taskBlob, taskEncoding := persistence.FromDataBlob(transfer.Task) batch.Query(templateCreateTransferTaskQuery, shardID, rowTypeTransferTask, rowTypeTransferDomainID, rowTypeTransferWorkflowID, rowTypeTransferRunID, domainID, workflowID, task.RunID, task.VisibilityTimestamp, task.TaskID, task.TargetDomainID, task.TargetDomainIDs, task.TargetWorkflowID, task.TargetRunID, task.TargetChildWorkflowOnly, task.TaskList, task.TaskType, task.ScheduleID, task.RecordVisibility, task.Version, task.OriginalTaskList, int32(task.OriginalTaskListKind), taskBlob, taskEncoding, // NOTE: use a constant here instead of task.VisibilityTimestamp so that we can query tasks with the same visibilityTimestamp defaultVisibilityTimestamp, task.TaskID, timeStamp, ) } } func resetSignalsRequested( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, signalReqIDs []string, timeStamp time.Time, ) error { batch.Query(templateResetSignalRequestedQuery, signalReqIDs, timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) return nil } func updateSignalsRequested( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, signalReqIDs []string, deleteSignalReqIDs []string, timeStamp time.Time, ) error { if len(signalReqIDs) > 0 { batch.Query(templateUpdateSignalRequestedQuery, signalReqIDs, timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } if len(deleteSignalReqIDs) > 0 { batch.Query(templateDeleteWorkflowExecutionSignalRequestedQuery, deleteSignalReqIDs, timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } return nil } func resetSignalInfos( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, signalInfos map[int64]*persistence.SignalInfo, timeStamp time.Time, ) error { batch.Query(templateResetSignalInfoQuery, resetSignalInfoMap(signalInfos), timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) return nil } func resetSignalInfoMap(signalInfos map[int64]*persistence.SignalInfo) map[int64]map[string]interface{} { sMap := make(map[int64]map[string]interface{}) for _, s := range signalInfos { sInfo := make(map[string]interface{}) sInfo["version"] = s.Version sInfo["initiated_id"] = s.InitiatedID sInfo["initiated_event_batch_id"] = s.InitiatedEventBatchID sInfo["signal_request_id"] = s.SignalRequestID sInfo["signal_name"] = s.SignalName sInfo["input"] = s.Input sInfo["control"] = s.Control sMap[s.InitiatedID] = sInfo } return sMap } func updateSignalInfos( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, signalInfos map[int64]*persistence.SignalInfo, deleteInfos []int64, timeStamp time.Time, ) error { for _, c := range signalInfos { batch.Query(templateUpdateSignalInfoQuery, c.InitiatedID, c.Version, c.InitiatedID, c.InitiatedEventBatchID, c.SignalRequestID, c.SignalName, c.Input, c.Control, timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } // deleteInfos are the initiatedIDs for SignalInfo being deleted for _, deleteInfo := range deleteInfos { batch.Query(templateDeleteSignalInfoQuery, deleteInfo, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } return nil } func resetRequestCancelInfos( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, requestCancelInfos map[int64]*persistence.RequestCancelInfo, timeStamp time.Time, ) error { batch.Query(templateResetRequestCancelInfoQuery, resetRequestCancelInfoMap(requestCancelInfos), timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) return nil } func resetRequestCancelInfoMap(requestCancelInfos map[int64]*persistence.RequestCancelInfo) map[int64]map[string]interface{} { rcMap := make(map[int64]map[string]interface{}) for _, rc := range requestCancelInfos { rcInfo := make(map[string]interface{}) rcInfo["version"] = rc.Version rcInfo["initiated_id"] = rc.InitiatedID rcInfo["initiated_event_batch_id"] = rc.InitiatedEventBatchID rcInfo["cancel_request_id"] = rc.CancelRequestID rcMap[rc.InitiatedID] = rcInfo } return rcMap } func updateRequestCancelInfos( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, requestCancelInfos map[int64]*persistence.RequestCancelInfo, deleteInfos []int64, timeStamp time.Time, ) error { for _, c := range requestCancelInfos { batch.Query(templateUpdateRequestCancelInfoQuery, c.InitiatedID, c.Version, c.InitiatedID, c.InitiatedEventBatchID, c.CancelRequestID, timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } // deleteInfos are the initiatedIDs for RequestCancelInfo being deleted for _, deleteInfo := range deleteInfos { batch.Query(templateDeleteRequestCancelInfoQuery, deleteInfo, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } return nil } func resetChildExecutionInfos( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, childExecutionInfos map[int64]*persistence.InternalChildExecutionInfo, timeStamp time.Time, ) error { infoMap, err := resetChildExecutionInfoMap(childExecutionInfos) if err != nil { return err } batch.Query(templateResetChildExecutionInfoQuery, infoMap, timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) return nil } func resetChildExecutionInfoMap(childExecutionInfos map[int64]*persistence.InternalChildExecutionInfo) (map[int64]map[string]interface{}, error) { cMap := make(map[int64]map[string]interface{}) for _, c := range childExecutionInfos { cInfo := make(map[string]interface{}) cInfo["version"] = c.Version cInfo["event_data_encoding"] = c.InitiatedEvent.GetEncodingString() cInfo["initiated_id"] = c.InitiatedID cInfo["initiated_event_batch_id"] = c.InitiatedEventBatchID cInfo["initiated_event"] = c.InitiatedEvent.Data cInfo["started_id"] = c.StartedID cInfo["started_event"] = c.StartedEvent.Data cInfo["create_request_id"] = c.CreateRequestID cInfo["started_workflow_id"] = c.StartedWorkflowID startedRunID := emptyRunID if c.StartedRunID != "" { startedRunID = c.StartedRunID } cInfo["started_run_id"] = startedRunID cInfo["domain_id"] = c.DomainID cInfo["domain_name"] = c.DomainNameDEPRECATED cInfo["workflow_type_name"] = c.WorkflowTypeName cInfo["parent_close_policy"] = int32(c.ParentClosePolicy) cMap[c.InitiatedID] = cInfo } return cMap, nil } func updateChildExecutionInfos( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, childExecutionInfos map[int64]*persistence.InternalChildExecutionInfo, deleteInfos []int64, timeStamp time.Time, ) error { for _, c := range childExecutionInfos { batch.Query(templateUpdateChildExecutionInfoQuery, c.InitiatedID, c.Version, c.InitiatedID, c.InitiatedEventBatchID, c.InitiatedEvent.Data, c.StartedID, c.StartedWorkflowID, c.StartedRunID, c.StartedEvent.Data, c.CreateRequestID, c.InitiatedEvent.GetEncodingString(), c.DomainID, c.DomainNameDEPRECATED, c.WorkflowTypeName, int32(c.ParentClosePolicy), timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } // deleteInfos are the initiatedIDs for ChildInfo being deleted for _, deleteInfo := range deleteInfos { batch.Query(templateDeleteChildExecutionInfoQuery, deleteInfo, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } return nil } func resetTimerInfos( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, timerInfos map[string]*persistence.TimerInfo, timeStamp time.Time, ) error { batch.Query(templateResetTimerInfoQuery, resetTimerInfoMap(timerInfos), timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) return nil } func resetTimerInfoMap(timerInfos map[string]*persistence.TimerInfo) map[string]map[string]interface{} { tMap := make(map[string]map[string]interface{}) for _, t := range timerInfos { tInfo := make(map[string]interface{}) tInfo["version"] = t.Version tInfo["timer_id"] = t.TimerID tInfo["started_id"] = t.StartedID tInfo["expiry_time"] = t.ExpiryTime // task_id is a misleading variable, it actually serves // the purpose of indicating whether a timer task is // generated for this timer info tInfo["task_id"] = t.TaskStatus tMap[t.TimerID] = tInfo } return tMap } func updateTimerInfos( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, timerInfos map[string]*persistence.TimerInfo, deleteInfos []string, timeStamp time.Time, ) error { for _, timerInfo := range timerInfos { batch.Query(templateUpdateTimerInfoQuery, timerInfo.TimerID, timerInfo.Version, timerInfo.TimerID, timerInfo.StartedID, timerInfo.ExpiryTime, timerInfo.TaskStatus, timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } for _, deleteInfo := range deleteInfos { batch.Query(templateDeleteTimerInfoQuery, deleteInfo, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } return nil } func resetActivityInfos( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, activityInfos map[int64]*persistence.InternalActivityInfo, timeStamp time.Time, ) error { infoMap, err := resetActivityInfoMap(activityInfos) if err != nil { return err } batch.Query(templateResetActivityInfoQuery, infoMap, timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) return nil } func resetActivityInfoMap(activityInfos map[int64]*persistence.InternalActivityInfo) (map[int64]map[string]interface{}, error) { aMap := make(map[int64]map[string]interface{}) for _, a := range activityInfos { aInfo := make(map[string]interface{}) aInfo["version"] = a.Version aInfo["event_data_encoding"] = a.ScheduledEvent.GetEncodingString() aInfo["schedule_id"] = a.ScheduleID aInfo["scheduled_event_batch_id"] = a.ScheduledEventBatchID aInfo["scheduled_event"] = a.ScheduledEvent.Data aInfo["scheduled_time"] = a.ScheduledTime aInfo["started_id"] = a.StartedID aInfo["started_event"] = a.StartedEvent.Data aInfo["started_time"] = a.StartedTime aInfo["activity_id"] = a.ActivityID aInfo["request_id"] = a.RequestID aInfo["details"] = a.Details aInfo["schedule_to_start_timeout"] = int32(a.ScheduleToStartTimeout.Seconds()) aInfo["schedule_to_close_timeout"] = int32(a.ScheduleToCloseTimeout.Seconds()) aInfo["start_to_close_timeout"] = int32(a.StartToCloseTimeout.Seconds()) aInfo["heart_beat_timeout"] = int32(a.HeartbeatTimeout.Seconds()) aInfo["cancel_requested"] = a.CancelRequested aInfo["cancel_request_id"] = a.CancelRequestID aInfo["last_hb_updated_time"] = a.LastHeartBeatUpdatedTime aInfo["timer_task_status"] = a.TimerTaskStatus aInfo["attempt"] = a.Attempt aInfo["task_list"] = a.TaskList aInfo["started_identity"] = a.StartedIdentity aInfo["has_retry_policy"] = a.HasRetryPolicy aInfo["init_interval"] = int32(a.InitialInterval.Seconds()) aInfo["backoff_coefficient"] = a.BackoffCoefficient aInfo["max_interval"] = int32(a.MaximumInterval.Seconds()) aInfo["expiration_time"] = a.ExpirationTime aInfo["max_attempts"] = a.MaximumAttempts aInfo["non_retriable_errors"] = a.NonRetriableErrors aInfo["last_failure_reason"] = a.LastFailureReason aInfo["last_worker_identity"] = a.LastWorkerIdentity aInfo["last_failure_details"] = a.LastFailureDetails aMap[a.ScheduleID] = aInfo } return aMap, nil } func updateActivityInfos( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, activityInfos map[int64]*persistence.InternalActivityInfo, deleteInfos []int64, timeStamp time.Time, ) error { for _, a := range activityInfos { batch.Query(templateUpdateActivityInfoQuery, a.ScheduleID, a.Version, a.ScheduleID, a.ScheduledEventBatchID, a.ScheduledEvent.Data, a.ScheduledTime, a.StartedID, a.StartedEvent.Data, a.StartedTime, a.ActivityID, a.RequestID, a.Details, int32(a.ScheduleToStartTimeout.Seconds()), int32(a.ScheduleToCloseTimeout.Seconds()), int32(a.StartToCloseTimeout.Seconds()), int32(a.HeartbeatTimeout.Seconds()), a.CancelRequested, a.CancelRequestID, a.LastHeartBeatUpdatedTime, a.TimerTaskStatus, a.Attempt, a.TaskList, int32(a.TaskListKind), a.StartedIdentity, a.HasRetryPolicy, int32(a.InitialInterval.Seconds()), a.BackoffCoefficient, int32(a.MaximumInterval.Seconds()), a.ExpirationTime, a.MaximumAttempts, a.NonRetriableErrors, a.LastFailureReason, a.LastWorkerIdentity, a.LastFailureDetails, a.ScheduledEvent.GetEncodingString(), timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } for _, deleteInfo := range deleteInfos { batch.Query(templateDeleteActivityInfoQuery, deleteInfo, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) } return nil } // NOTE: not sure we still need it. We keep the behavior for safe during refactoring // In theory we can just return the input as output // TODO: if possible, remove it in the future or add more comment of why we need this conversion func convertToCassandraTimestamp(in time.Time) time.Time { return time.Unix(0, persistence.DBTimestampToUnixNano(persistence.UnixNanoToDBTimestamp(in.UnixNano()))) } func getNextPageToken(iter gocql.Iter) []byte { nextPageToken := iter.PageState() newPageToken := make([]byte, len(nextPageToken)) copy(newPageToken, nextPageToken) return newPageToken } func createWorkflowExecutionWithMergeMaps( batch gocql.Batch, shardID int, domainID string, workflowID string, execution *nosqlplugin.WorkflowExecutionRequest, timeStamp time.Time, ) error { err := createWorkflowExecution(batch, shardID, domainID, workflowID, execution, timeStamp) if err != nil { return err } if execution.EventBufferWriteMode != nosqlplugin.EventBufferWriteModeNone { return fmt.Errorf("should only support EventBufferWriteModeNone") } if execution.MapsWriteMode != nosqlplugin.WorkflowExecutionMapsWriteModeCreate { return fmt.Errorf("should only support WorkflowExecutionMapsWriteModeCreate") } err = updateActivityInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.ActivityInfos, nil, timeStamp) if err != nil { return err } err = updateTimerInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.TimerInfos, nil, timeStamp) if err != nil { return err } err = updateChildExecutionInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.ChildWorkflowInfos, nil, timeStamp) if err != nil { return err } err = updateRequestCancelInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.RequestCancelInfos, nil, timeStamp) if err != nil { return err } err = updateSignalInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.SignalInfos, nil, timeStamp) if err != nil { return err } return updateSignalsRequested(batch, shardID, domainID, workflowID, execution.RunID, execution.SignalRequestedIDs, nil, timeStamp) } func resetWorkflowExecutionAndMapsAndEventBuffer( batch gocql.Batch, shardID int, domainID string, workflowID string, execution *nosqlplugin.WorkflowExecutionRequest, timeStamp time.Time, ) error { err := updateWorkflowExecution(batch, shardID, domainID, workflowID, execution, timeStamp) if err != nil { return err } if execution.EventBufferWriteMode != nosqlplugin.EventBufferWriteModeClear { return fmt.Errorf("should only support EventBufferWriteModeClear") } err = deleteBufferedEvents(batch, shardID, domainID, workflowID, execution.RunID, timeStamp) if err != nil { return err } if execution.MapsWriteMode != nosqlplugin.WorkflowExecutionMapsWriteModeReset { return fmt.Errorf("should only support WorkflowExecutionMapsWriteModeReset") } // This is another category of execution update where only the non-frozen column types in // Cassandra are updated to a previous state in the Execution Update flow. err = resetActivityInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.ActivityInfos, timeStamp) if err != nil { return err } err = resetTimerInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.TimerInfos, timeStamp) if err != nil { return err } err = resetChildExecutionInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.ChildWorkflowInfos, timeStamp) if err != nil { return err } err = resetRequestCancelInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.RequestCancelInfos, timeStamp) if err != nil { return err } err = resetSignalInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.SignalInfos, timeStamp) if err != nil { return err } return resetSignalsRequested(batch, shardID, domainID, workflowID, execution.RunID, execution.SignalRequestedIDs, timeStamp) } func appendBufferedEvents( batch gocql.Batch, newBufferedEvents *persistence.DataBlob, shardID int, domainID string, workflowID string, runID string, timeStamp time.Time, ) error { values := make(map[string]interface{}) values["encoding_type"] = newBufferedEvents.Encoding values["version"] = int64(0) values["data"] = newBufferedEvents.Data newEventValues := []map[string]interface{}{values} batch.Query(templateAppendBufferedEventsQuery, newEventValues, timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) return nil } func deleteBufferedEvents( batch gocql.Batch, shardID int, domainID string, workflowID string, runID string, timeStamp time.Time, ) error { batch.Query(templateDeleteBufferedEventsQuery, timeStamp, shardID, rowTypeExecution, domainID, workflowID, runID, defaultVisibilityTimestamp, rowTypeExecutionTaskID) return nil } func updateWorkflowExecutionAndEventBufferWithMergeAndDeleteMaps( batch gocql.Batch, shardID int, domainID string, workflowID string, execution *nosqlplugin.WorkflowExecutionRequest, timeStamp time.Time, ) error { err := updateWorkflowExecution(batch, shardID, domainID, workflowID, execution, timeStamp) if err != nil { return err } if execution.EventBufferWriteMode == nosqlplugin.EventBufferWriteModeClear { err = deleteBufferedEvents(batch, shardID, domainID, workflowID, execution.RunID, timeStamp) if err != nil { return err } } else if execution.EventBufferWriteMode == nosqlplugin.EventBufferWriteModeAppend { err = appendBufferedEvents(batch, execution.NewBufferedEventBatch, shardID, domainID, workflowID, execution.RunID, timeStamp) if err != nil { return err } } if execution.MapsWriteMode != nosqlplugin.WorkflowExecutionMapsWriteModeUpdate { return fmt.Errorf("should only support WorkflowExecutionMapsWriteModeUpdate") } // In certain cases, some of the execution update cycles update particular columns asynchronously before reaching the final cycle. // Each of these functions are updating a non-frozen column type in Cassandra table. err = updateActivityInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.ActivityInfos, execution.ActivityInfoKeysToDelete, timeStamp) if err != nil { return err } err = updateTimerInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.TimerInfos, execution.TimerInfoKeysToDelete, timeStamp) if err != nil { return err } err = updateChildExecutionInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.ChildWorkflowInfos, execution.ChildWorkflowInfoKeysToDelete, timeStamp) if err != nil { return err } err = updateRequestCancelInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.RequestCancelInfos, execution.RequestCancelInfoKeysToDelete, timeStamp) if err != nil { return err } err = updateSignalInfos(batch, shardID, domainID, workflowID, execution.RunID, execution.SignalInfos, execution.SignalInfoKeysToDelete, timeStamp) if err != nil { return err } return updateSignalsRequested(batch, shardID, domainID, workflowID, execution.RunID, execution.SignalRequestedIDs, execution.SignalRequestedIDsKeysToDelete, timeStamp) } // updateWorkflowExecution is responsible for updating the execution state in different cycles until the // Status changes to close at the Final update cycle. Information is updated linearly, and synchronization usually occurs in every cycle. func updateWorkflowExecution( batch gocql.Batch, shardID int, domainID string, workflowID string, execution *nosqlplugin.WorkflowExecutionRequest, timeStamp time.Time, ) error { execution.StartTimestamp = convertToCassandraTimestamp(execution.StartTimestamp) execution.LastUpdatedTimestamp = convertToCassandraTimestamp(execution.LastUpdatedTimestamp) batch.Query(templateUpdateWorkflowExecutionWithVersionHistoriesQuery, domainID, workflowID, execution.RunID, execution.FirstExecutionRunID, execution.ParentDomainID, execution.ParentWorkflowID, execution.ParentRunID, execution.InitiatedID, execution.CompletionEventBatchID, execution.CompletionEvent.Data, execution.CompletionEvent.GetEncodingString(), execution.TaskList, int32(execution.TaskListKind), execution.WorkflowTypeName, int32(execution.WorkflowTimeout.Seconds()), int32(execution.DecisionStartToCloseTimeout.Seconds()), execution.ExecutionContext, execution.State, execution.CloseStatus, execution.LastFirstEventID, execution.LastEventTaskID, execution.NextEventID, execution.LastProcessedEvent, execution.StartTimestamp, execution.LastUpdatedTimestamp, execution.CreateRequestID, execution.SignalCount, execution.HistorySize, execution.DecisionVersion, execution.DecisionScheduleID, execution.DecisionStartedID, execution.DecisionRequestID, int32(execution.DecisionTimeout.Seconds()), execution.DecisionAttempt, execution.DecisionStartedTimestamp.UnixNano(), execution.DecisionScheduledTimestamp.UnixNano(), execution.DecisionOriginalScheduledTimestamp.UnixNano(), execution.CancelRequested, execution.CancelRequestID, execution.StickyTaskList, int32(execution.StickyScheduleToStartTimeout.Seconds()), execution.ClientLibraryVersion, execution.ClientFeatureVersion, execution.ClientImpl, execution.AutoResetPoints.Data, execution.AutoResetPoints.GetEncoding(), execution.Attempt, execution.HasRetryPolicy, int32(execution.InitialInterval.Seconds()), execution.BackoffCoefficient, int32(execution.MaximumInterval.Seconds()), execution.ExpirationTime, execution.MaximumAttempts, execution.NonRetriableErrors, persistence.EventStoreVersion, execution.BranchToken, execution.CronSchedule, int32(execution.CronOverlapPolicy), int32(execution.ExpirationInterval.Seconds()), execution.SearchAttributes, execution.Memo, execution.PartitionConfig, execution.ActiveClusterSelectionPolicy.GetData(), execution.ActiveClusterSelectionPolicy.GetEncodingString(), execution.NextEventID, execution.VersionHistories.Data, execution.VersionHistories.GetEncodingString(), execution.Checksums.Version, execution.Checksums.Flavor, execution.Checksums.Value, execution.LastWriteVersion, execution.State, timeStamp, shardID, rowTypeExecution, domainID, workflowID, execution.RunID, defaultVisibilityTimestamp, rowTypeExecutionTaskID, execution.PreviousNextEventIDCondition) return nil } func createWorkflowExecution( batch gocql.Batch, shardID int, domainID string, workflowID string, execution *nosqlplugin.WorkflowExecutionRequest, timeStamp time.Time, ) error { execution.StartTimestamp = convertToCassandraTimestamp(execution.StartTimestamp) execution.LastUpdatedTimestamp = convertToCassandraTimestamp(execution.LastUpdatedTimestamp) batch.Query(templateCreateWorkflowExecutionWithVersionHistoriesQuery, shardID, domainID, workflowID, execution.RunID, rowTypeExecution, domainID, workflowID, execution.RunID, execution.FirstExecutionRunID, execution.ParentDomainID, execution.ParentWorkflowID, execution.ParentRunID, execution.InitiatedID, execution.CompletionEventBatchID, execution.CompletionEvent.Data, execution.CompletionEvent.GetEncodingString(), execution.TaskList, int32(execution.TaskListKind), execution.WorkflowTypeName, int32(execution.WorkflowTimeout.Seconds()), int32(execution.DecisionStartToCloseTimeout.Seconds()), execution.ExecutionContext, execution.State, execution.CloseStatus, execution.LastFirstEventID, execution.LastEventTaskID, execution.NextEventID, execution.LastProcessedEvent, execution.StartTimestamp, execution.LastUpdatedTimestamp, execution.CreateRequestID, execution.SignalCount, execution.HistorySize, execution.DecisionVersion, execution.DecisionScheduleID, execution.DecisionStartedID, execution.DecisionRequestID, int32(execution.DecisionTimeout.Seconds()), execution.DecisionAttempt, execution.DecisionStartedTimestamp.UnixNano(), execution.DecisionScheduledTimestamp.UnixNano(), execution.DecisionOriginalScheduledTimestamp.UnixNano(), execution.CancelRequested, execution.CancelRequestID, execution.StickyTaskList, int32(execution.StickyScheduleToStartTimeout.Seconds()), execution.ClientLibraryVersion, execution.ClientFeatureVersion, execution.ClientImpl, execution.AutoResetPoints.Data, execution.AutoResetPoints.GetEncodingString(), execution.Attempt, execution.HasRetryPolicy, int32(execution.InitialInterval.Seconds()), execution.BackoffCoefficient, int32(execution.MaximumInterval.Seconds()), execution.ExpirationTime, execution.MaximumAttempts, execution.NonRetriableErrors, persistence.EventStoreVersion, execution.BranchToken, execution.CronSchedule, int32(execution.CronOverlapPolicy), int32(execution.ExpirationInterval.Seconds()), execution.SearchAttributes, execution.Memo, execution.PartitionConfig, execution.ActiveClusterSelectionPolicy.GetData(), execution.ActiveClusterSelectionPolicy.GetEncodingString(), execution.NextEventID, defaultVisibilityTimestamp, rowTypeExecutionTaskID, execution.VersionHistories.Data, execution.VersionHistories.GetEncodingString(), execution.Checksums.Version, execution.Checksums.Flavor, execution.Checksums.Value, execution.LastWriteVersion, execution.State, timeStamp, ) return nil } func isRequestRowType(rowType int) bool { if rowType >= rowTypeWorkflowRequestStart && rowType <= rowTypeWorkflowRequestReset { return true } return false } func toRequestRowType(requestType persistence.WorkflowRequestType) (int, error) { switch requestType { case persistence.WorkflowRequestTypeStart: return rowTypeWorkflowRequestStart, nil case persistence.WorkflowRequestTypeSignal: return rowTypeWorkflowRequestSignal, nil case persistence.WorkflowRequestTypeCancel: return rowTypeWorkflowRequestCancel, nil case persistence.WorkflowRequestTypeReset: return rowTypeWorkflowRequestReset, nil default: return 0, fmt.Errorf("unknown workflow request type %v", requestType) } } func fromRequestRowType(rowType int) (persistence.WorkflowRequestType, error) { switch rowType { case rowTypeWorkflowRequestStart: return persistence.WorkflowRequestTypeStart, nil case rowTypeWorkflowRequestSignal: return persistence.WorkflowRequestTypeSignal, nil case rowTypeWorkflowRequestCancel: return persistence.WorkflowRequestTypeCancel, nil case rowTypeWorkflowRequestReset: return persistence.WorkflowRequestTypeReset, nil default: return persistence.WorkflowRequestType(0), fmt.Errorf("unknown request row type %v", rowType) } } func insertWorkflowActiveClusterSelectionPolicyRow( batch gocql.Batch, activeClusterSelectionPolicyRow *nosqlplugin.ActiveClusterSelectionPolicyRow, timeStamp time.Time, ) error { if activeClusterSelectionPolicyRow == nil || activeClusterSelectionPolicyRow.Policy == nil { return nil } batch.Query(templateInsertWorkflowActiveClusterSelectionPolicyRowQuery, activeClusterSelectionPolicyRow.ShardID, rowTypeWorkflowActiveClusterSelectionPolicy, activeClusterSelectionPolicyRow.DomainID, activeClusterSelectionPolicyRow.WorkflowID, activeClusterSelectionPolicyRow.RunID, defaultVisibilityTimestamp, rowTypeWorkflowActiveClusterSelectionVersion, timeStamp, activeClusterSelectionPolicyRow.Policy.Data, activeClusterSelectionPolicyRow.Policy.GetEncodingString(), ) return nil } func insertOrUpsertWorkflowRequestRow( batch gocql.Batch, requests *nosqlplugin.WorkflowRequestsWriteRequest, timeStamp time.Time, ) error { if requests == nil { return nil } var insertQuery string switch requests.WriteMode { case nosqlplugin.WorkflowRequestWriteModeInsert: insertQuery = templateInsertWorkflowRequestQuery case nosqlplugin.WorkflowRequestWriteModeUpsert: insertQuery = templateUpsertWorkflowRequestQuery default: return fmt.Errorf("unknown workflow request write mode %v", requests.WriteMode) } for _, row := range requests.Rows { rowType, err := toRequestRowType(row.RequestType) if err != nil { return err } batch.Query(insertQuery, row.ShardID, rowType, row.DomainID, row.WorkflowID, row.RequestID, defaultVisibilityTimestamp, emptyWorkflowRequestVersion*-1, row.RunID, timeStamp, workflowRequestTTLInSeconds, ) batch.Query(insertQuery, row.ShardID, rowType, row.DomainID, row.WorkflowID, row.RequestID, defaultVisibilityTimestamp, row.Version*-1, row.RunID, timeStamp, workflowRequestTTLInSeconds, ) } return nil } func createOrUpdateCurrentWorkflow( batch gocql.Batch, shardID int, domainID string, workflowID string, request *nosqlplugin.CurrentWorkflowWriteRequest, timeStamp time.Time, ) error { switch request.WriteMode { case nosqlplugin.CurrentWorkflowWriteModeNoop: return nil case nosqlplugin.CurrentWorkflowWriteModeInsert: batch.Query(templateCreateCurrentWorkflowExecutionQuery, shardID, rowTypeExecution, domainID, workflowID, permanentRunID, defaultVisibilityTimestamp, rowTypeExecutionTaskID, request.Row.RunID, request.Row.RunID, request.Row.CreateRequestID, request.Row.State, request.Row.CloseStatus, request.Row.LastWriteVersion, request.Row.State, timeStamp, ) case nosqlplugin.CurrentWorkflowWriteModeUpdate: if request.Condition == nil || request.Condition.GetCurrentRunID() == "" { return fmt.Errorf("CurrentWorkflowWriteModeUpdate require Condition.CurrentRunID") } if request.Condition.LastWriteVersion != nil && request.Condition.State != nil { batch.Query(templateUpdateCurrentWorkflowExecutionForNewQuery, request.Row.RunID, request.Row.RunID, request.Row.CreateRequestID, request.Row.State, request.Row.CloseStatus, request.Row.LastWriteVersion, request.Row.State, timeStamp, shardID, rowTypeExecution, domainID, workflowID, permanentRunID, defaultVisibilityTimestamp, rowTypeExecutionTaskID, *request.Condition.CurrentRunID, *request.Condition.LastWriteVersion, *request.Condition.State, ) } else { batch.Query(templateUpdateCurrentWorkflowExecutionQuery, request.Row.RunID, request.Row.RunID, request.Row.CreateRequestID, request.Row.State, request.Row.CloseStatus, request.Row.LastWriteVersion, request.Row.State, timeStamp, shardID, rowTypeExecution, domainID, workflowID, permanentRunID, defaultVisibilityTimestamp, rowTypeExecutionTaskID, *request.Condition.CurrentRunID, ) } default: return fmt.Errorf("unknown mode %v", request.WriteMode) } return nil } func mustConvertToSlice(value interface{}) []interface{} { v := reflect.ValueOf(value) switch v.Kind() { case reflect.Slice, reflect.Array: result := make([]interface{}, v.Len()) for i := 0; i < v.Len(); i++ { result[i] = v.Index(i).Interface() } return result default: panic(fmt.Sprintf("Unable to convert %v to slice which is of type %T", value, value)) } } func populateGetReplicationTasks(query gocql.Query) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { iter := query.Iter() if iter == nil { return nil, nil, &types.InternalServiceError{ Message: "populateGetReplicationTasks operation failed. Not able to create query iterator.", } } var tasks []*nosqlplugin.HistoryMigrationTask task := make(map[string]interface{}) for iter.MapScan(task) { t := parseReplicationTaskInfo(task["replication"].(map[string]interface{})) taskID := task["task_id"].(int64) data := task["data"].([]byte) encoding := task["data_encoding"].(string) taskBlob := persistence.NewDataBlob(data, constants.EncodingType(encoding)) // Reset task map to get it ready for next scan task = make(map[string]interface{}) tasks = append(tasks, &nosqlplugin.HistoryMigrationTask{ Replication: t, Task: taskBlob, TaskID: taskID, }) } nextPageToken := getNextPageToken(iter) err := iter.Close() return tasks, nextPageToken, err } ================================================ FILE: common/persistence/nosql/nosqlplugin/cassandra/workflow_utils_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "fmt" "reflect" "regexp" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/common/types" ) var FixedTime = time.Date(2025, 1, 6, 15, 0, 0, 0, time.UTC) // fakeSession is fake implementation of gocql.Session type fakeSession struct { // inputs iter *fakeIter mapExecuteBatchCASApplied bool mapExecuteBatchCASPrev map[string]any mapExecuteBatchCASErr error query gocql.Query // outputs batches []*fakeBatch queries []string } func (s *fakeSession) Query(queryTmpl string, args ...interface{}) gocql.Query { s.queries = append(s.queries, sanitizedRenderedQuery(queryTmpl, args...)) return s.query } func (s *fakeSession) NewBatch(gocql.BatchType) gocql.Batch { b := &fakeBatch{} s.batches = append(s.batches, b) return b } func (s *fakeSession) ExecuteBatch(gocql.Batch) error { return nil } func (s *fakeSession) MapExecuteBatchCAS(batch gocql.Batch, prev map[string]interface{}) (bool, gocql.Iter, error) { for k, v := range s.mapExecuteBatchCASPrev { prev[k] = v } return s.mapExecuteBatchCASApplied, s.iter, s.mapExecuteBatchCASErr } // fakeBatch is fake implementation of gocql.Batch type fakeBatch struct { // outputs queries []string } // Query is fake implementation of gocql.Batch.Query func (b *fakeBatch) Query(queryTmpl string, args ...interface{}) { b.queries = append(b.queries, sanitizedRenderedQuery(queryTmpl, args...)) } // WithContext is fake implementation of gocql.Batch.WithContext func (b *fakeBatch) WithContext(context.Context) gocql.Batch { return b } // WithTimestamp is fake implementation of gocql.Batch.WithTimestamp func (b *fakeBatch) WithTimestamp(int64) gocql.Batch { return b } // Consistency is fake implementation of gocql.Batch.Consistency func (b *fakeBatch) Consistency(gocql.Consistency) gocql.Batch { return b } // fakeQuery is fake implementation of gocql.Query func (s *fakeSession) Close() { } func sanitizedRenderedQuery(queryTmpl string, args ...interface{}) string { argsSanitized := make([]interface{}, len(args)) for i, arg := range args { // use values instead of pointer so that we can compare them in tests if reflect.ValueOf(arg).Kind() == reflect.Ptr && !reflect.ValueOf(arg).IsZero() { argsSanitized[i] = reflect.ValueOf(arg).Elem().Interface() } else { argsSanitized[i] = arg } if t, ok := argsSanitized[i].(time.Time); ok { // use fixed time format to avoid flakiness argsSanitized[i] = t.UTC().Format(time.RFC3339) } } queryTmpl = strings.ReplaceAll(queryTmpl, "?", "%v") return fmt.Sprintf(queryTmpl, argsSanitized...) } // fakeIter is fake implementation of gocql.Iter type fakeIter struct { // input parametrs mapScanInputs []map[string]interface{} scanInputs [][]interface{} pageState []byte closeErr error // output parameters mapScanCalls int scanCalls int closed bool } // Scan is fake implementation of gocql.Iter.Scan func (i *fakeIter) Scan(outArgs ...interface{}) bool { if i.scanCalls >= len(i.scanInputs) { return false } for j, v := range i.scanInputs[i.scanCalls] { if len(outArgs) <= j { panic(fmt.Sprintf("outArgs length: %d is less than expected: %d", len(outArgs), len(i.scanInputs[i.scanCalls]))) } if v == nil { continue } dst := outArgs[j] dstPtrValue := reflect.ValueOf(dst) dstValue := reflect.Indirect(dstPtrValue) func() { defer func() { if r := recover(); r != nil { panic(fmt.Sprintf("failed to set %dth value: %v to %v, inner panic: %s", j, v, dst, r)) } }() dstValue.Set(reflect.ValueOf(v)) }() } i.scanCalls++ return true } // MapScan is fake implementation of gocql.Iter.MapScan func (i *fakeIter) MapScan(res map[string]interface{}) bool { if i.mapScanCalls >= len(i.mapScanInputs) { return false } for k, v := range i.mapScanInputs[i.mapScanCalls] { res[k] = v } i.mapScanCalls++ return true } // PageState is fake implementation of gocql.Iter.PageState func (i *fakeIter) PageState() []byte { return i.pageState } // Close is fake implementation of gocql.Iter.Close func (i *fakeIter) Close() error { i.closed = true return i.closeErr } type fakeUUID struct { uuid string } func (u *fakeUUID) String() string { return u.uuid } var _ (gocql.Query) = &fakeQuery{} type fakeQuery struct { iter *fakeIter mapScan map[string]interface{} err error scanCASApplied bool mapScanCASApplied bool } func (q *fakeQuery) Exec() error { return q.err } func (q *fakeQuery) Scan(...interface{}) error { return q.err } func (q *fakeQuery) ScanCAS(...interface{}) (bool, error) { return q.scanCASApplied, q.err } func (q *fakeQuery) MapScan(res map[string]interface{}) error { for k, v := range q.mapScan { res[k] = v } return q.err } func (q *fakeQuery) MapScanCAS(map[string]interface{}) (bool, error) { return q.mapScanCASApplied, q.err } func (q *fakeQuery) Iter() gocql.Iter { return q.iter } func (q *fakeQuery) PageSize(int) gocql.Query { return q } func (q *fakeQuery) PageState([]byte) gocql.Query { return q } func (q *fakeQuery) WithContext(context.Context) gocql.Query { return q } func (q *fakeQuery) WithTimestamp(int64) gocql.Query { return q } func (q *fakeQuery) Consistency(gocql.Consistency) gocql.Query { return q } func (q *fakeQuery) Bind(...interface{}) gocql.Query { return q } func TestExecuteCreateWorkflowBatchTransaction(t *testing.T) { tests := []struct { // fake setup parameters desc string session *fakeSession // executeCreateWorkflowBatchTransaction args batch *fakeBatch currentWFReq *nosqlplugin.CurrentWorkflowWriteRequest execution *nosqlplugin.WorkflowExecutionRequest shardCond *nosqlplugin.ShardCondition // expectations wantErr error }{ { desc: "applied", batch: &fakeBatch{}, session: &fakeSession{ mapExecuteBatchCASApplied: true, iter: &fakeIter{}, }, }, { desc: "CAS error", batch: &fakeBatch{}, session: &fakeSession{ mapExecuteBatchCASErr: fmt.Errorf("db operation failed for some reason"), iter: &fakeIter{}, }, wantErr: fmt.Errorf("db operation failed for some reason"), }, { desc: "shard range id mismatch", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeShard, "run_id": uuid.Parse("bda9cd9c-32fb-4267-b120-346e5351fc46"), "range_id": int64(200), }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{}, shardCond: &nosqlplugin.ShardCondition{ RangeID: 100, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ ShardRangeIDNotMatch: common.Int64Ptr(200), }, }, { desc: "workflow request already exists", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeWorkflowRequestCancel, "run_id": uuid.Parse("4b8045c8-7b45-41e0-bf03-1f0d166b818d"), }, query: &fakeQuery{ mapScan: map[string]interface{}{ "current_run_id": uuid.Parse("6b844fb4-c18a-4979-a2d3-731ebdd1db08"), }, }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{}, execution: &nosqlplugin.WorkflowExecutionRequest{ InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ DomainID: "fd88863f-bb32-4daa-8878-49e08b91545e", WorkflowID: "wfid", }, }, shardCond: &nosqlplugin.ShardCondition{ ShardID: 1, RangeID: 100, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ DuplicateRequest: &nosqlplugin.DuplicateRequest{ RequestType: persistence.WorkflowRequestTypeCancel, RunID: "6b844fb4-c18a-4979-a2d3-731ebdd1db08", }, }, }, { desc: "workflow request already exists - but failed to get latest request", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeWorkflowRequestSignal, "run_id": uuid.Parse("4b8045c8-7b45-41e0-bf03-1f0d166b818d"), }, query: &fakeQuery{ mapScan: map[string]interface{}{ "current_run_id": uuid.Parse("6b844fb4-c18a-4979-a2d3-731ebdd1db08"), }, err: fmt.Errorf("failed to get latest request"), }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{}, execution: &nosqlplugin.WorkflowExecutionRequest{ InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ DomainID: "fd88863f-bb32-4daa-8878-49e08b91545e", WorkflowID: "wfid", }, }, shardCond: &nosqlplugin.ShardCondition{ ShardID: 1, RangeID: 100, }, wantErr: fmt.Errorf("failed to get latest request"), }, { desc: "current execution already exists and write mode is CurrentWorkflowWriteModeInsert", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeExecution, "run_id": uuid.Parse(permanentRunID), "range_id": int64(100), "workflow_last_write_version": int64(3), "execution": map[string]any{ "run_id": uuid.Parse("bda9cd9c-32fb-4267-b120-346e5351fc46"), "state": 1, }, }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeInsert, Row: nosqlplugin.CurrentWorkflowRow{ WorkflowID: "test-workflow-id", }, }, shardCond: &nosqlplugin.ShardCondition{ RangeID: 100, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ WorkflowExecutionAlreadyExists: &nosqlplugin.WorkflowExecutionAlreadyExists{ RunID: "bda9cd9c-32fb-4267-b120-346e5351fc46", State: 1, LastWriteVersion: 3, OtherInfo: "Workflow execution already running. WorkflowId: test-workflow-id, RunId: bda9cd9c-32fb-4267-b120-346e5351fc46", }, }, }, { desc: "creation condition failed by mismatch runID", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeExecution, "run_id": uuid.Parse(permanentRunID), "range_id": int64(100), "workflow_last_write_version": int64(3), "current_run_id": uuid.Parse("bda9cd9c-32fb-4267-b120-346e5351fc46"), }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Row: nosqlplugin.CurrentWorkflowRow{ WorkflowID: "wfid", }, Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: common.StringPtr("fd88863f-bb32-4daa-8878-49e08b91545e"), // not matching current_run_id above on purpose }, }, shardCond: &nosqlplugin.ShardCondition{ RangeID: 100, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: common.StringPtr( "Workflow execution creation condition failed by mismatch runID. " + "WorkflowId: wfid, Expected Current RunID: fd88863f-bb32-4daa-8878-49e08b91545e, " + "Actual Current RunID: bda9cd9c-32fb-4267-b120-346e5351fc46"), }, }, { desc: "creation condition failed by mismatch last write version", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeExecution, "run_id": uuid.Parse(permanentRunID), "range_id": int64(100), "workflow_last_write_version": int64(3), "current_run_id": uuid.Parse("fd88863f-bb32-4daa-8878-49e08b91545e"), }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Row: nosqlplugin.CurrentWorkflowRow{ WorkflowID: "wfid", }, Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: common.StringPtr("fd88863f-bb32-4daa-8878-49e08b91545e"), // not matching current_run_id above on purpose LastWriteVersion: common.Int64Ptr(1), }, }, shardCond: &nosqlplugin.ShardCondition{ RangeID: 100, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: common.StringPtr( "Workflow execution creation condition failed. " + "WorkflowId: wfid, Expected Version: 1, Actual Version: 3"), }, }, { desc: "creation condition failed by mismatch state", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeExecution, "run_id": uuid.Parse(permanentRunID), "range_id": int64(100), "workflow_last_write_version": int64(3), "current_run_id": uuid.Parse("fd88863f-bb32-4daa-8878-49e08b91545e"), "workflow_state": 2, }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Row: nosqlplugin.CurrentWorkflowRow{ WorkflowID: "wfid", }, Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: common.StringPtr("fd88863f-bb32-4daa-8878-49e08b91545e"), // not matching current_run_id above on purpose LastWriteVersion: common.Ptr(int64(3)), State: common.Ptr(int(10)), }, }, shardCond: &nosqlplugin.ShardCondition{ RangeID: 100, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: common.StringPtr( "Workflow execution creation condition failed. " + "WorkflowId: wfid, Expected State: 10, Actual State: 2"), }, }, { desc: "concrete execution already exists", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeExecution, "run_id": uuid.Parse("bda9cd9c-32fb-4267-b120-346e5351fc46"), "range_id": int64(100), "workflow_last_write_version": int64(3), }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{}, execution: &nosqlplugin.WorkflowExecutionRequest{ InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ WorkflowID: "wfid", RunID: "bda9cd9c-32fb-4267-b120-346e5351fc46", CreateRequestID: "reqid_123", State: 2, }, }, shardCond: &nosqlplugin.ShardCondition{ RangeID: 100, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ WorkflowExecutionAlreadyExists: &nosqlplugin.WorkflowExecutionAlreadyExists{ OtherInfo: "Workflow execution already running. WorkflowId: wfid, " + "RunId: bda9cd9c-32fb-4267-b120-346e5351fc46", CreateRequestID: "reqid_123", RunID: "bda9cd9c-32fb-4267-b120-346e5351fc46", State: 2, LastWriteVersion: 3, }, }, }, { desc: "unknown condition failure", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeExecution, "run_id": uuid.Parse("bda9cd9c-32fb-4267-b120-346e5351fc46"), "range_id": int64(100), "workflow_last_write_version": int64(3), }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{}, execution: &nosqlplugin.WorkflowExecutionRequest{ InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ RunID: "something else", }, }, shardCond: &nosqlplugin.ShardCondition{ RangeID: 100, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ UnknownConditionFailureDetails: common.StringPtr("Failed to operate on workflow execution. Request RangeID: 100"), }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { err := executeCreateWorkflowBatchTransaction(context.Background(), tc.session, tc.batch, tc.currentWFReq, tc.execution, tc.shardCond) if diff := errDiff(tc.wantErr, err); diff != "" { t.Fatalf("Error mismatch (-want +got):\n%s", diff) } if !tc.session.iter.closed { t.Error("iterator not closed") } }) } } func TestExecuteUpdateWorkflowBatchTransaction(t *testing.T) { tests := []struct { // fake setup parameters desc string session *fakeSession // executeUpdateWorkflowBatchTransaction args batch *fakeBatch currentWFReq *nosqlplugin.CurrentWorkflowWriteRequest prevNextEventIDCond int64 shardCond *nosqlplugin.ShardCondition // expectations wantErr error }{ { desc: "applied", batch: &fakeBatch{}, session: &fakeSession{ mapExecuteBatchCASApplied: true, iter: &fakeIter{}, }, }, { desc: "CAS error", batch: &fakeBatch{}, session: &fakeSession{ mapExecuteBatchCASErr: fmt.Errorf("db operation failed for some reason"), iter: &fakeIter{}, }, wantErr: fmt.Errorf("db operation failed for some reason"), }, { desc: "range id mismatch for shard row", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeShard, "run_id": uuid.Parse("bda9cd9c-32fb-4267-b120-346e5351fc46"), "range_id": int64(200), }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{}, shardCond: &nosqlplugin.ShardCondition{ RangeID: 100, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ ShardRangeIDNotMatch: common.Int64Ptr(200), }, }, { desc: "workflow request already exists", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeWorkflowRequestSignal, "run_id": uuid.Parse("4b8045c8-7b45-41e0-bf03-1f0d166b818d"), }, query: &fakeQuery{ mapScan: map[string]interface{}{ "current_run_id": uuid.Parse("6b844fb4-c18a-4979-a2d3-731ebdd1db08"), }, }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{}, shardCond: &nosqlplugin.ShardCondition{ ShardID: 1, RangeID: 100, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ DuplicateRequest: &nosqlplugin.DuplicateRequest{ RequestType: persistence.WorkflowRequestTypeSignal, RunID: "6b844fb4-c18a-4979-a2d3-731ebdd1db08", }, }, }, { desc: "workflow request already exists - but failed to get latest request", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeWorkflowRequestCancel, "run_id": uuid.Parse("4b8045c8-7b45-41e0-bf03-1f0d166b818d"), }, query: &fakeQuery{ mapScan: map[string]interface{}{ "current_run_id": uuid.Parse("6b844fb4-c18a-4979-a2d3-731ebdd1db08"), }, err: fmt.Errorf("failed to get latest request"), }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{}, shardCond: &nosqlplugin.ShardCondition{ ShardID: 1, RangeID: 100, }, wantErr: fmt.Errorf("failed to get latest request"), }, { desc: "nextEventID mismatch for execution row", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeExecution, "run_id": uuid.Parse("0875863e-dcef-496a-b8a2-3210b2958e25"), "next_event_id": int64(10), }, }, batch: &fakeBatch{}, prevNextEventIDCond: 11, // not matching next_event_id above on purpose currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{ Row: nosqlplugin.CurrentWorkflowRow{ RunID: "0875863e-dcef-496a-b8a2-3210b2958e25", }, }, shardCond: &nosqlplugin.ShardCondition{ RangeID: 200, }, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ UnknownConditionFailureDetails: common.StringPtr( "Failed to update mutable state. " + "previousNextEventIDCondition: 11, actualNextEventID: 10, Request Current RunID: 0875863e-dcef-496a-b8a2-3210b2958e25"), }, }, { desc: "runID mismatch for current execution row", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeExecution, "run_id": uuid.Parse(permanentRunID), "current_run_id": uuid.Parse("0875863e-dcef-496a-b8a2-3210b2958e25"), }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{ Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: common.StringPtr("fd88863f-bb32-4daa-8878-49e08b91545e"), // not matching current_run_id above on purpose }, }, shardCond: &nosqlplugin.ShardCondition{}, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ CurrentWorkflowConditionFailInfo: common.StringPtr( "Failed to update mutable state. requestConditionalRunID: fd88863f-bb32-4daa-8878-49e08b91545e, " + "Actual Value: 0875863e-dcef-496a-b8a2-3210b2958e25"), }, }, { desc: "unknown condition failure", session: &fakeSession{ mapExecuteBatchCASApplied: false, iter: &fakeIter{}, mapExecuteBatchCASPrev: map[string]any{ "type": rowTypeExecution, "run_id": uuid.Parse(permanentRunID), "current_run_id": uuid.Parse("0875863e-dcef-496a-b8a2-3210b2958e25"), }, }, batch: &fakeBatch{}, currentWFReq: &nosqlplugin.CurrentWorkflowWriteRequest{ Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: common.StringPtr("0875863e-dcef-496a-b8a2-3210b2958e25"), // not matching current_run_id above on purpose }, }, shardCond: &nosqlplugin.ShardCondition{ ShardID: 345, RangeID: 200, }, prevNextEventIDCond: 11, wantErr: &nosqlplugin.WorkflowOperationConditionFailure{ UnknownConditionFailureDetails: common.StringPtr( "Failed to update mutable state. ShardID: 345, RangeID: 200, previousNextEventIDCondition: 11, requestConditionalRunID: 0875863e-dcef-496a-b8a2-3210b2958e25"), }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { err := executeUpdateWorkflowBatchTransaction(context.Background(), tc.session, tc.batch, tc.currentWFReq, tc.prevNextEventIDCond, tc.shardCond) if diff := errDiff(tc.wantErr, err); diff != "" { t.Fatalf("Error mismatch (-want +got):\n%s", diff) } if !tc.session.iter.closed { t.Error("iterator not closed") } }) } } func TestToRequestRowType(t *testing.T) { testCases := []struct { name string requestType persistence.WorkflowRequestType wantErr bool want int }{ { name: "StartWorkflow request", requestType: persistence.WorkflowRequestTypeStart, wantErr: false, want: rowTypeWorkflowRequestStart, }, { name: "SignalWithWorkflow request", requestType: persistence.WorkflowRequestTypeSignal, wantErr: false, want: rowTypeWorkflowRequestSignal, }, { name: "SignalWorkflow request", requestType: persistence.WorkflowRequestTypeSignal, wantErr: false, want: rowTypeWorkflowRequestSignal, }, { name: "CancelWorkflow request", requestType: persistence.WorkflowRequestTypeCancel, wantErr: false, want: rowTypeWorkflowRequestCancel, }, { name: "ResetWorkflow request", requestType: persistence.WorkflowRequestTypeReset, wantErr: false, want: rowTypeWorkflowRequestReset, }, { name: "unknown request", requestType: persistence.WorkflowRequestType(-999), wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { want, err := toRequestRowType(tc.requestType) gotErr := (err != nil) if gotErr != tc.wantErr { t.Fatalf("Got error: %v, want?: %v", err, tc.wantErr) } if gotErr { return } if diff := cmp.Diff(tc.want, want); diff != "" { t.Fatalf("request type mismatch (-want +got):\n%s", diff) } }) } } func TestFromRequestRowType(t *testing.T) { testCases := []struct { name string requestType int wantErr bool want persistence.WorkflowRequestType }{ { name: "StartWorkflow request", requestType: rowTypeWorkflowRequestStart, wantErr: false, want: persistence.WorkflowRequestTypeStart, }, { name: "SignalWithWorkflow request", requestType: rowTypeWorkflowRequestSignal, wantErr: false, want: persistence.WorkflowRequestTypeSignal, }, { name: "SignalWorkflow request", requestType: rowTypeWorkflowRequestSignal, wantErr: false, want: persistence.WorkflowRequestTypeSignal, }, { name: "CancelWorkflow request", requestType: rowTypeWorkflowRequestCancel, wantErr: false, want: persistence.WorkflowRequestTypeCancel, }, { name: "ResetWorkflow request", requestType: rowTypeWorkflowRequestReset, wantErr: false, want: persistence.WorkflowRequestTypeReset, }, { name: "unknown request", requestType: rowTypeShard, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { want, err := fromRequestRowType(tc.requestType) gotErr := (err != nil) if gotErr != tc.wantErr { t.Fatalf("Got error: %v, want?: %v", err, tc.wantErr) } if gotErr { return } if diff := cmp.Diff(tc.want, want); diff != "" { t.Fatalf("request type mismatch (-want +got):\n%s", diff) } }) } } func TestInsertOrUpsertWorkflowRequestRow(t *testing.T) { testCases := []struct { name string request *nosqlplugin.WorkflowRequestsWriteRequest wantErr bool wantQueries []string }{ { name: "WorkflowRequestWriteModeInsert", request: &nosqlplugin.WorkflowRequestsWriteRequest{ Rows: []*nosqlplugin.WorkflowRequestRow{ { ShardID: 1, DomainID: "c09537fd-67ce-4b08-a817-eb8f12ad3a91", WorkflowID: "test", RunID: "25bd1013-0e79-4c45-8e55-08bb45886896", RequestID: "9ab1d25d-8620-440a-b3f1-d3167e08c769", Version: 100, }, }, WriteMode: nosqlplugin.WorkflowRequestWriteModeInsert, }, wantErr: false, wantQueries: []string{ `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id, current_run_id, created_time) ` + `VALUES(1, 7, c09537fd-67ce-4b08-a817-eb8f12ad3a91, test, 9ab1d25d-8620-440a-b3f1-d3167e08c769, 946684800000, 1000, 25bd1013-0e79-4c45-8e55-08bb45886896, 2025-01-06T15:00:00Z) ` + `IF NOT EXISTS USING TTL 10800`, `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id, current_run_id, created_time) ` + `VALUES(1, 7, c09537fd-67ce-4b08-a817-eb8f12ad3a91, test, 9ab1d25d-8620-440a-b3f1-d3167e08c769, 946684800000, -100, 25bd1013-0e79-4c45-8e55-08bb45886896, 2025-01-06T15:00:00Z) ` + `IF NOT EXISTS USING TTL 10800`, }, }, { name: "WorkflowRequestWriteModeUpsert", request: &nosqlplugin.WorkflowRequestsWriteRequest{ Rows: []*nosqlplugin.WorkflowRequestRow{ { ShardID: 1, DomainID: "c09537fd-67ce-4b08-a817-eb8f12ad3a91", WorkflowID: "test", RunID: "25bd1013-0e79-4c45-8e55-08bb45886896", RequestID: "9ab1d25d-8620-440a-b3f1-d3167e08c769", Version: 100, }, }, WriteMode: nosqlplugin.WorkflowRequestWriteModeUpsert, }, wantErr: false, wantQueries: []string{ `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id, current_run_id, last_updated_time) ` + `VALUES(1, 7, c09537fd-67ce-4b08-a817-eb8f12ad3a91, test, 9ab1d25d-8620-440a-b3f1-d3167e08c769, 946684800000, 1000, 25bd1013-0e79-4c45-8e55-08bb45886896, 2025-01-06T15:00:00Z) ` + `USING TTL 10800`, `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id, current_run_id, last_updated_time) ` + `VALUES(1, 7, c09537fd-67ce-4b08-a817-eb8f12ad3a91, test, 9ab1d25d-8620-440a-b3f1-d3167e08c769, 946684800000, -100, 25bd1013-0e79-4c45-8e55-08bb45886896, 2025-01-06T15:00:00Z) ` + `USING TTL 10800`, }, }, { name: "unknown mode", request: &nosqlplugin.WorkflowRequestsWriteRequest{ Rows: []*nosqlplugin.WorkflowRequestRow{ { RequestType: persistence.WorkflowRequestTypeStart, }, }, WriteMode: nosqlplugin.WorkflowRequestWriteMode(-100), }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { batch := &fakeBatch{} err := insertOrUpsertWorkflowRequestRow(batch, tc.request, FixedTime) gotErr := (err != nil) if gotErr != tc.wantErr { t.Fatalf("Got error: %v, want?: %v", err, tc.wantErr) } if gotErr { return } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestCreateTimerTasks(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-12T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string timerTasks []*nosqlplugin.HistoryMigrationTask // expectations wantQueries []string }{ { desc: "ok", shardID: 1000, domainID: "domain_xyz", workflowID: "workflow_xyz", timerTasks: []*nosqlplugin.HistoryMigrationTask{ { Timer: &nosqlplugin.TimerTask{ RunID: "rundid_1", TaskID: 1, TaskType: 1, TimeoutType: 1, EventID: 10, ScheduleAttempt: 0, Version: 0, VisibilityTimestamp: ts, TaskList: "tasklist_1", }, Task: &persistence.DataBlob{ Data: []byte("timer1"), Encoding: constants.EncodingTypeThriftRW, }, }, { Timer: &nosqlplugin.TimerTask{ RunID: "rundid_1", TaskID: 2, TaskType: 1, TimeoutType: 1, EventID: 11, ScheduleAttempt: 0, Version: 0, VisibilityTimestamp: ts.Add(time.Minute), TaskList: "tasklist_2", }, Task: &persistence.DataBlob{ Data: []byte("timer2"), Encoding: constants.EncodingTypeThriftRW, }, }, }, wantQueries: []string{ `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, timer, data, data_encoding, visibility_ts, task_id, created_time) ` + `VALUES(1000, 3, 10000000-4000-f000-f000-000000000000, 20000000-4000-f000-f000-000000000000, 30000000-4000-f000-f000-000000000000, ` + `{domain_id: domain_xyz, workflow_id: workflow_xyz, run_id: rundid_1, visibility_ts: 1702418921000, task_id: 1, type: 1, timeout_type: 1, event_id: 10, schedule_attempt: 0, version: 0, task_list: tasklist_1}, ` + `[116 105 109 101 114 49], thriftrw, 1702418921000, 1, 2025-01-06T15:00:00Z)`, `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, timer, data, data_encoding, visibility_ts, task_id, created_time) ` + `VALUES(1000, 3, 10000000-4000-f000-f000-000000000000, 20000000-4000-f000-f000-000000000000, 30000000-4000-f000-f000-000000000000, ` + `{domain_id: domain_xyz, workflow_id: workflow_xyz, run_id: rundid_1, visibility_ts: 1702418981000, task_id: 2, type: 1, timeout_type: 1, event_id: 11, schedule_attempt: 0, version: 0, task_list: tasklist_2}, ` + `[116 105 109 101 114 50], thriftrw, 1702418981000, 2, 2025-01-06T15:00:00Z)`, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := createTimerTasks(batch, tc.shardID, tc.domainID, tc.workflowID, tc.timerTasks, FixedTime) if err != nil { t.Fatalf("createTimerTasks failed: %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestReplicationTasks(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-12T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string replTasks []*nosqlplugin.HistoryMigrationTask // expectations wantQueries []string }{ { desc: "ok", shardID: 1000, domainID: "domain_xyz", workflowID: "workflow_xyz", replTasks: []*nosqlplugin.HistoryMigrationTask{ { Replication: &nosqlplugin.ReplicationTask{ RunID: "rundid_1", TaskID: 644, TaskType: 0, FirstEventID: 5, NextEventID: 8, Version: 0, ScheduledID: constants.EmptyEventID, NewRunBranchToken: []byte{'a', 'b', 'c'}, CreationTime: ts, }, Task: &persistence.DataBlob{ Data: []byte("rep1"), Encoding: constants.EncodingTypeThriftRW, }, }, { Replication: &nosqlplugin.ReplicationTask{ RunID: "rundid_1", TaskID: 645, TaskType: 0, FirstEventID: 25, NextEventID: 28, Version: 0, ScheduledID: constants.EmptyEventID, NewRunBranchToken: []byte{'a', 'b', 'c'}, CreationTime: ts.Add(time.Hour), }, Task: &persistence.DataBlob{ Data: []byte("rep2"), Encoding: constants.EncodingTypeThriftRW, }, }, }, wantQueries: []string{ `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, replication, data, data_encoding, visibility_ts, task_id, created_time) ` + `VALUES(1000, 4, 10000000-5000-f000-f000-000000000000, 20000000-5000-f000-f000-000000000000, 30000000-5000-f000-f000-000000000000, ` + `{domain_id: domain_xyz, workflow_id: workflow_xyz, run_id: rundid_1, task_id: 644, type: 0, ` + `first_event_id: 5,next_event_id: 8,version: 0,scheduled_id: -23, event_store_version: 2, branch_token: [], ` + `new_run_event_store_version: 2, new_run_branch_token: [97 98 99], created_time: 1702418921000000000 }, [114 101 112 49], thriftrw, 946684800000, 644, 2025-01-06T15:00:00Z)`, `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, replication, data, data_encoding, visibility_ts, task_id, created_time) ` + `VALUES(1000, 4, 10000000-5000-f000-f000-000000000000, 20000000-5000-f000-f000-000000000000, 30000000-5000-f000-f000-000000000000, ` + `{domain_id: domain_xyz, workflow_id: workflow_xyz, run_id: rundid_1, task_id: 645, type: 0, ` + `first_event_id: 25,next_event_id: 28,version: 0,scheduled_id: -23, event_store_version: 2, branch_token: [], ` + `new_run_event_store_version: 2, new_run_branch_token: [97 98 99], created_time: 1702422521000000000 }, [114 101 112 50], thriftrw, 946684800000, 645, 2025-01-06T15:00:00Z)`, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} createReplicationTasks(batch, tc.shardID, tc.domainID, tc.workflowID, tc.replTasks, FixedTime) if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestTransferTasks(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-12T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string transferTasks []*nosqlplugin.HistoryMigrationTask // expectations wantQueries []string }{ { desc: "ok", shardID: 1000, domainID: "domain_xyz", workflowID: "workflow_xyz", transferTasks: []*nosqlplugin.HistoryMigrationTask{ { Transfer: &nosqlplugin.TransferTask{ RunID: "rundid_1", TaskID: 355, TaskType: 0, Version: 1, VisibilityTimestamp: ts, TargetDomainID: "e2bf2c8f-0ddf-4451-8840-27cfe8addd62", TargetWorkflowID: persistence.TransferTaskTransferTargetWorkflowID, TargetRunID: persistence.TransferTaskTransferTargetRunID, TargetChildWorkflowOnly: true, TaskList: "tasklist_1", ScheduleID: 14, OriginalTaskList: "original_tasklist_1", OriginalTaskListKind: types.TaskListKindEphemeral, }, Task: &persistence.DataBlob{ Data: []byte("tr1"), Encoding: constants.EncodingTypeThriftRW, }, }, { Transfer: &nosqlplugin.TransferTask{ RunID: "rundid_2", TaskID: 220, TaskType: 0, Version: 1, VisibilityTimestamp: ts.Add(time.Minute), TargetDomainID: "e2bf2c8f-0ddf-4451-8840-27cfe8addd62", TargetWorkflowID: persistence.TransferTaskTransferTargetWorkflowID, TargetRunID: persistence.TransferTaskTransferTargetRunID, TargetChildWorkflowOnly: true, TaskList: "tasklist_2", ScheduleID: 3, OriginalTaskList: "original_tasklist_2", OriginalTaskListKind: types.TaskListKindEphemeral, }, Task: &persistence.DataBlob{ Data: []byte("tr2"), Encoding: constants.EncodingTypeThriftRW, }, }, }, wantQueries: []string{ `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, transfer, data, data_encoding, visibility_ts, task_id, created_time) ` + `VALUES(1000, 2, 10000000-3000-f000-f000-000000000000, 20000000-3000-f000-f000-000000000000, 30000000-3000-f000-f000-000000000000, ` + `{domain_id: domain_xyz, workflow_id: workflow_xyz, run_id: rundid_1, visibility_ts: 2023-12-12T22:08:41Z, ` + `task_id: 355, target_domain_id: e2bf2c8f-0ddf-4451-8840-27cfe8addd62, target_domain_ids: map[],` + `target_workflow_id: 20000000-0000-f000-f000-000000000001, target_run_id: 30000000-0000-f000-f000-000000000002, ` + `target_child_workflow_only: true, task_list: tasklist_1, type: 0, schedule_id: 14, record_visibility: false, version: 1, original_task_list: original_tasklist_1, original_task_list_kind: 2}, ` + `[116 114 49], thriftrw, 946684800000, 355, 2025-01-06T15:00:00Z)`, `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, transfer, data, data_encoding, visibility_ts, task_id, created_time) ` + `VALUES(1000, 2, 10000000-3000-f000-f000-000000000000, 20000000-3000-f000-f000-000000000000, 30000000-3000-f000-f000-000000000000, ` + `{domain_id: domain_xyz, workflow_id: workflow_xyz, run_id: rundid_2, visibility_ts: 2023-12-12T22:09:41Z, ` + `task_id: 220, target_domain_id: e2bf2c8f-0ddf-4451-8840-27cfe8addd62, target_domain_ids: map[],` + `target_workflow_id: 20000000-0000-f000-f000-000000000001, target_run_id: 30000000-0000-f000-f000-000000000002, ` + `target_child_workflow_only: true, task_list: tasklist_2, type: 0, schedule_id: 3, record_visibility: false, version: 1, original_task_list: original_tasklist_2, original_task_list_kind: 2}, ` + `[116 114 50], thriftrw, 946684800000, 220, 2025-01-06T15:00:00Z)`, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} createTransferTasks(batch, tc.shardID, tc.domainID, tc.workflowID, tc.transferTasks, FixedTime) if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestResetSignalsRequested(t *testing.T) { tests := []struct { desc string shardID int domainID string workflowID string runID string signalReqIDs []string // expectations wantQueries []string }{ { desc: "ok", shardID: 1000, domainID: "domain_123", workflowID: "workflow_123", runID: "runid_123", signalReqIDs: []string{"signalReqID_1", "signalReqID_2"}, wantQueries: []string{ `UPDATE executions SET signal_requested = [signalReqID_1 signalReqID_2] , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain_123 and workflow_id = workflow_123 and ` + `run_id = runid_123 and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := resetSignalsRequested(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.signalReqIDs, FixedTime) if err != nil { t.Fatalf("resetSignalsRequested failed: %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateSignalsRequested(t *testing.T) { tests := []struct { desc string shardID int domainID string workflowID string runID string signalReqIDs []string deleteSignalReqIDs []string // expectations wantQueries []string }{ { desc: "update only", shardID: 1000, domainID: "domain_abc", workflowID: "workflow_abc", runID: "runid_abc", signalReqIDs: []string{"signalReqID_3", "signalReqID_4"}, deleteSignalReqIDs: []string{}, wantQueries: []string{ `UPDATE executions SET signal_requested = signal_requested + [signalReqID_3 signalReqID_4] , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain_abc and workflow_id = workflow_abc and ` + `run_id = runid_abc and visibility_ts = 946684800000 and task_id = -10 `, }, }, { desc: "delete only", shardID: 1001, domainID: "domain_def", workflowID: "workflow_def", runID: "runid_def", signalReqIDs: []string{}, deleteSignalReqIDs: []string{"signalReqID_5", "signalReqID_6"}, wantQueries: []string{ `UPDATE executions SET signal_requested = signal_requested - [signalReqID_5 signalReqID_6] , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1001 and type = 1 and domain_id = domain_def and workflow_id = workflow_def and ` + `run_id = runid_def and visibility_ts = 946684800000 and task_id = -10 `, }, }, { desc: "update and delete", shardID: 1002, domainID: "domain_ghi", workflowID: "workflow_ghi", runID: "runid_ghi", signalReqIDs: []string{"signalReqID_7"}, deleteSignalReqIDs: []string{"signalReqID_8"}, wantQueries: []string{ `UPDATE executions SET signal_requested = signal_requested + [signalReqID_7] , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1002 and type = 1 and domain_id = domain_ghi and workflow_id = workflow_ghi and ` + `run_id = runid_ghi and visibility_ts = 946684800000 and task_id = -10 `, `UPDATE executions SET signal_requested = signal_requested - [signalReqID_8] , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1002 and type = 1 and domain_id = domain_ghi and workflow_id = workflow_ghi and ` + `run_id = runid_ghi and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := updateSignalsRequested(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.signalReqIDs, tc.deleteSignalReqIDs, FixedTime) if err != nil { t.Fatalf("updateSignalsRequested failed: %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestResetSignalInfos(t *testing.T) { tests := []struct { desc string shardID int domainID string workflowID string runID string signalInfos map[int64]*persistence.SignalInfo // expectations wantQueries []string }{ { desc: "single signal info", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: "runid1", signalInfos: map[int64]*persistence.SignalInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, SignalRequestID: "request1", SignalName: "signal1", Input: []byte("input1"), Control: []byte("control1"), }, 2: { Version: 1, InitiatedID: 5, InitiatedEventBatchID: 6, SignalRequestID: "request2", SignalName: "signal2", Input: []byte("input2"), Control: []byte("control2"), }, }, wantQueries: []string{ `UPDATE executions SET signal_map = map[` + `1:map[control:[99 111 110 116 114 111 108 49] initiated_event_batch_id:2 initiated_id:1 input:[105 110 112 117 116 49] signal_name:signal1 signal_request_id:request1 version:1] ` + `5:map[control:[99 111 110 116 114 111 108 50] initiated_event_batch_id:6 initiated_id:5 input:[105 110 112 117 116 50] signal_name:signal2 signal_request_id:request2 version:1]` + `] , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := resetSignalInfos(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.signalInfos, FixedTime) if err != nil { t.Fatalf("resetSignalInfos failed: %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateSignalInfos(t *testing.T) { tests := []struct { desc string shardID int domainID string workflowID string runID string signalInfos map[int64]*persistence.SignalInfo deleteInfos []int64 // expectations wantQueries []string }{ { desc: "update and delete signal infos", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: "runid1", signalInfos: map[int64]*persistence.SignalInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, SignalRequestID: "request1", SignalName: "signal1", Input: []byte("input1"), Control: []byte("control1"), }, }, deleteInfos: []int64{2}, wantQueries: []string{ `UPDATE executions SET signal_map[ 1 ] = {` + `version: 1, initiated_id: 1, initiated_event_batch_id: 2, signal_request_id: request1, ` + `signal_name: signal1, input: [105 110 112 117 116 49], ` + `control: [99 111 110 116 114 111 108 49]` + `} , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and ` + `workflow_id = workflow1 and run_id = runid1 and ` + `visibility_ts = 946684800000 and task_id = -10 `, `DELETE signal_map[ 2 ] FROM executions WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and ` + `workflow_id = workflow1 and run_id = runid1 ` + `and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := updateSignalInfos(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.signalInfos, tc.deleteInfos, FixedTime) if err != nil { t.Fatalf("updateSignalInfos failed: %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestResetRequestCancelInfos(t *testing.T) { tests := []struct { desc string shardID int domainID string workflowID string runID string requestCancelInfos map[int64]*persistence.RequestCancelInfo // expectations wantQueries []string }{ { desc: "ok", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: "runid1", requestCancelInfos: map[int64]*persistence.RequestCancelInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, CancelRequestID: "cancelRequest1", }, 3: { Version: 2, InitiatedID: 3, InitiatedEventBatchID: 4, CancelRequestID: "cancelRequest3", }, }, wantQueries: []string{ `UPDATE executions SET request_cancel_map = map[` + `1:map[cancel_request_id:cancelRequest1 initiated_event_batch_id:2 initiated_id:1 version:1] ` + `3:map[cancel_request_id:cancelRequest3 initiated_event_batch_id:4 initiated_id:3 version:2]` + `], last_updated_time = 2025-01-06T15:00:00Z WHERE shard_id = 1000 and type = 1 and domain_id = domain1 and ` + `workflow_id = workflow1 and run_id = runid1 and ` + `visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := resetRequestCancelInfos(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.requestCancelInfos, FixedTime) if err != nil { t.Fatalf("resetRequestCancelInfos failed: %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateRequestCancelInfos(t *testing.T) { tests := []struct { desc string shardID int domainID string workflowID string runID string requestCancelInfos map[int64]*persistence.RequestCancelInfo deleteInfos []int64 // expectations wantQueries []string }{ { desc: "update and delete request cancel infos", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: "runid1", requestCancelInfos: map[int64]*persistence.RequestCancelInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, CancelRequestID: "cancelRequest1", }, }, deleteInfos: []int64{2}, wantQueries: []string{ `UPDATE executions SET request_cancel_map[ 1 ] = ` + `{version: 1,initiated_id: 1, initiated_event_batch_id: 2, cancel_request_id: cancelRequest1 } , last_updated_time = 2025-01-06T15:00:00Z ` + `WHERE shard_id = 1000 and type = 1 and domain_id = domain1 and ` + `workflow_id = workflow1 and run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, `DELETE request_cancel_map[ 2 ] FROM executions WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := updateRequestCancelInfos(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.requestCancelInfos, tc.deleteInfos, FixedTime) if err != nil { t.Fatalf("updateRequestCancelInfos failed: %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestResetChildExecutionInfos(t *testing.T) { tests := []struct { desc string shardID int domainID string workflowID string runID string childExecutionInfos map[int64]*persistence.InternalChildExecutionInfo // expectations wantQueries []string wantErr bool }{ { desc: "execution info with runid", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: "runid1", childExecutionInfos: map[int64]*persistence.InternalChildExecutionInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, StartedID: 3, StartedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, }, StartedWorkflowID: "startedWorkflowID1", StartedRunID: "startedRunID1", CreateRequestID: "createRequestID1", InitiatedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, }, DomainID: "domain1", WorkflowTypeName: "workflowType1", ParentClosePolicy: types.ParentClosePolicyAbandon, }, }, wantQueries: []string{ `UPDATE executions SET child_executions_map = ` + `map[1:map[` + `create_request_id:createRequestID1 domain_id:domain1 domain_name: event_data_encoding:thriftrw ` + `initiated_event:[] initiated_event_batch_id:2 initiated_id:1 parent_close_policy:0 ` + `started_event:[] started_id:3 started_run_id:startedRunID1 started_workflow_id:startedWorkflowID1 ` + `version:1 workflow_type_name:workflowType1` + `]], last_updated_time = 2025-01-06T15:00:00Z ` + `WHERE shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, }, }, { desc: "execution info without runid", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: emptyRunID, childExecutionInfos: map[int64]*persistence.InternalChildExecutionInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, StartedID: 3, StartedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, }, StartedWorkflowID: "startedWorkflowID1", StartedRunID: "", // leave empty and validate it's querying empty runid CreateRequestID: "createRequestID1", InitiatedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, }, DomainID: "domain1", WorkflowTypeName: "workflowType1", ParentClosePolicy: types.ParentClosePolicyAbandon, }, }, wantQueries: []string{ `UPDATE executions SET child_executions_map = ` + `map[1:map[` + `create_request_id:createRequestID1 domain_id:domain1 domain_name: event_data_encoding:thriftrw ` + `initiated_event:[] initiated_event_batch_id:2 initiated_id:1 parent_close_policy:0 ` + `started_event:[] started_id:3 started_run_id:30000000-0000-f000-f000-000000000000 started_workflow_id:startedWorkflowID1 ` + `version:1 workflow_type_name:workflowType1` + `]], last_updated_time = 2025-01-06T15:00:00Z ` + `WHERE shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = 30000000-0000-f000-f000-000000000000 and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := resetChildExecutionInfos(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.childExecutionInfos, FixedTime) if (err != nil) != tc.wantErr { t.Fatalf("resetChildExecutionInfos() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateChildExecutionInfos(t *testing.T) { tests := []struct { desc string shardID int domainID string workflowID string runID string childExecutionInfos map[int64]*persistence.InternalChildExecutionInfo deleteInfos []int64 // expectations wantQueries []string }{ { desc: "update and delete child execution infos", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: "runid1", childExecutionInfos: map[int64]*persistence.InternalChildExecutionInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, StartedID: 3, StartedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, }, StartedWorkflowID: "startedWorkflowID1", StartedRunID: "startedRunID1", CreateRequestID: "createRequestID1", InitiatedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, }, DomainID: "domain1", WorkflowTypeName: "workflowType1", ParentClosePolicy: types.ParentClosePolicyAbandon, }, }, deleteInfos: []int64{2}, wantQueries: []string{ `UPDATE executions SET child_executions_map[ 1 ] = {` + `version: 1, initiated_id: 1, initiated_event_batch_id: 2, initiated_event: [], ` + `started_id: 3, started_workflow_id: startedWorkflowID1, started_run_id: startedRunID1, ` + `started_event: [], create_request_id: createRequestID1, event_data_encoding: thriftrw, ` + `domain_id: domain1, domain_name: , workflow_type_name: workflowType1, parent_close_policy: 0` + `} , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, `DELETE child_executions_map[ 2 ] FROM executions WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := updateChildExecutionInfos(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.childExecutionInfos, tc.deleteInfos, FixedTime) if err != nil { t.Fatalf("updateChildExecutionInfos failed: %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestResetTimerInfos(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-12T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string runID string timerInfos map[string]*persistence.TimerInfo // expectations wantQueries []string }{ { desc: "ok", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: "runid1", timerInfos: map[string]*persistence.TimerInfo{ "timer1": { Version: 1, TimerID: "timer1", StartedID: 2, ExpiryTime: ts.UTC(), TaskStatus: 1, }, }, wantQueries: []string{ `UPDATE executions SET timer_map = map[` + `timer1:map[expiry_time:2023-12-12 22:08:41 +0000 UTC started_id:2 task_id:1 timer_id:timer1 version:1]` + `] , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := resetTimerInfos(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.timerInfos, FixedTime) if err != nil { t.Fatalf("resetTimerInfos() error = %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateTimerInfos(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-19T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string runID string timerInfos map[string]*persistence.TimerInfo deleteInfos []string // expectations wantQueries []string }{ { desc: "update and delete timer infos", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: "runid1", timerInfos: map[string]*persistence.TimerInfo{ "timer1": { TimerID: "timer1", Version: 1, StartedID: 2, ExpiryTime: ts.UTC(), TaskStatus: 1, }, }, deleteInfos: []string{"timer2"}, wantQueries: []string{ `UPDATE executions SET timer_map[ timer1 ] = {` + `version: 1, timer_id: timer1, started_id: 2, expiry_time: 2023-12-19T22:08:41Z, task_id: 1` + `} , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, `DELETE timer_map[ timer2 ] FROM executions WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := updateTimerInfos(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.timerInfos, tc.deleteInfos, FixedTime) if err != nil { t.Fatalf("updateTimerInfos() error = %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestResetActivityInfos(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-19T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string runID string activityInfos map[int64]*persistence.InternalActivityInfo // expectations wantQueries []string }{ { desc: "ok", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: "runid1", activityInfos: map[int64]*persistence.InternalActivityInfo{ 1: { Version: 1, ScheduledEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-scheduled-event-data"), }, ScheduledTime: ts.UTC(), ScheduleID: 1, StartedID: 2, StartedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-started-event-data"), }, ActivityID: "activity1", ScheduleToStartTimeout: 1 * time.Minute, ScheduleToCloseTimeout: 2 * time.Minute, StartToCloseTimeout: 3 * time.Minute, HeartbeatTimeout: 1 * time.Minute, Attempt: 3, MaximumAttempts: 5, TaskList: "tasklist1", HasRetryPolicy: true, LastFailureReason: "retry reason", }, 2: { Version: 1, ScheduledEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-scheduled-event-data"), }, ScheduledTime: ts.UTC(), ScheduleID: 2, StartedID: 3, StartedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-started-event-data"), }, ActivityID: "activity2", ScheduleToStartTimeout: 1 * time.Minute, ScheduleToCloseTimeout: 2 * time.Minute, StartToCloseTimeout: 3 * time.Minute, HeartbeatTimeout: 1 * time.Minute, Attempt: 1, MaximumAttempts: 5, TaskList: "tasklist1", HasRetryPolicy: true, LastFailureReason: "another retry reason", }, }, wantQueries: []string{ `UPDATE executions SET activity_map = map[` + `1:map[` + `activity_id:activity1 attempt:3 backoff_coefficient:0 cancel_request_id:0 cancel_requested:false ` + `details:[] event_data_encoding:thriftrw expiration_time:0001-01-01 00:00:00 +0000 UTC has_retry_policy:true ` + `heart_beat_timeout:60 init_interval:0 last_failure_details:[] last_failure_reason:retry reason ` + `last_hb_updated_time:0001-01-01 00:00:00 +0000 UTC last_worker_identity: max_attempts:5 max_interval:0 ` + `non_retriable_errors:[] request_id: schedule_id:1 schedule_to_close_timeout:120 schedule_to_start_timeout:60 ` + `scheduled_event:[116 104 114 105 102 116 45 101 110 99 111 100 101 100 45 115 99 104 101 100 117 108 101 100 45 101 118 101 110 116 45 100 97 116 97] ` + `scheduled_event_batch_id:0 scheduled_time:2023-12-19 22:08:41 +0000 UTC start_to_close_timeout:180 ` + `started_event:[116 104 114 105 102 116 45 101 110 99 111 100 101 100 45 115 116 97 114 116 101 100 45 101 118 101 110 116 45 100 97 116 97] ` + `started_id:2 started_identity: started_time:0001-01-01 00:00:00 +0000 UTC task_list:tasklist1 timer_task_status:0 version:1` + `] ` + `2:map[` + `activity_id:activity2 attempt:1 backoff_coefficient:0 cancel_request_id:0 cancel_requested:false ` + `details:[] event_data_encoding:thriftrw expiration_time:0001-01-01 00:00:00 +0000 UTC has_retry_policy:true ` + `heart_beat_timeout:60 init_interval:0 last_failure_details:[] last_failure_reason:another retry reason ` + `last_hb_updated_time:0001-01-01 00:00:00 +0000 UTC last_worker_identity: max_attempts:5 max_interval:0 ` + `non_retriable_errors:[] request_id: schedule_id:2 schedule_to_close_timeout:120 schedule_to_start_timeout:60 ` + `scheduled_event:[116 104 114 105 102 116 45 101 110 99 111 100 101 100 45 115 99 104 101 100 117 108 101 100 45 101 118 101 110 116 45 100 97 116 97] ` + `scheduled_event_batch_id:0 scheduled_time:2023-12-19 22:08:41 +0000 UTC start_to_close_timeout:180 ` + `started_event:[116 104 114 105 102 116 45 101 110 99 111 100 101 100 45 115 116 97 114 116 101 100 45 101 118 101 110 116 45 100 97 116 97] ` + `started_id:3 started_identity: started_time:0001-01-01 00:00:00 +0000 UTC task_list:tasklist1 timer_task_status:0 version:1` + `]` + `] , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := resetActivityInfos(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.activityInfos, FixedTime) if err != nil { t.Fatalf("resetActivityInfos() error = %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestUpdateActivityInfos(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-19T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string runID string activityInfos map[int64]*persistence.InternalActivityInfo deleteInfos []int64 // expectations wantQueries []string }{ { desc: "update and delete activity infos", shardID: 1000, domainID: "domain1", workflowID: "workflow1", runID: "runid1", activityInfos: map[int64]*persistence.InternalActivityInfo{ 1: { Version: 1, ScheduledEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-scheduled-event-data"), }, ScheduledTime: ts.UTC(), ScheduleID: 1, StartedID: 2, StartedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-started-event-data"), }, ActivityID: "activity1", ScheduleToStartTimeout: 1 * time.Minute, ScheduleToCloseTimeout: 2 * time.Minute, StartToCloseTimeout: 3 * time.Minute, HeartbeatTimeout: 1 * time.Minute, Attempt: 3, MaximumAttempts: 5, TaskList: "tasklist1", TaskListKind: types.TaskListKindEphemeral, HasRetryPolicy: true, LastFailureReason: "retry reason", }, }, deleteInfos: []int64{2}, wantQueries: []string{ `UPDATE executions SET activity_map[ 1 ] = {` + `version: 1, schedule_id: 1, scheduled_event_batch_id: 0, ` + `scheduled_event: [116 104 114 105 102 116 45 101 110 99 111 100 101 100 45 115 99 104 101 100 117 108 101 100 45 101 118 101 110 116 45 100 97 116 97], ` + `scheduled_time: 2023-12-19T22:08:41Z, started_id: 2, ` + `started_event: [116 104 114 105 102 116 45 101 110 99 111 100 101 100 45 115 116 97 114 116 101 100 45 101 118 101 110 116 45 100 97 116 97], ` + `started_time: 0001-01-01T00:00:00Z, activity_id: activity1, request_id: , ` + `details: [], schedule_to_start_timeout: 60, schedule_to_close_timeout: 120, start_to_close_timeout: 180, ` + `heart_beat_timeout: 60, cancel_requested: false, cancel_request_id: 0, last_hb_updated_time: 0001-01-01T00:00:00Z, ` + `timer_task_status: 0, attempt: 3, task_list: tasklist1, task_list_kind: 2, started_identity: , has_retry_policy: true, ` + `init_interval: 0, backoff_coefficient: 0, max_interval: 0, expiration_time: 0001-01-01T00:00:00Z, ` + `max_attempts: 5, non_retriable_errors: [], last_failure_reason: retry reason, last_worker_identity: , ` + `last_failure_details: [], event_data_encoding: thriftrw` + `} , last_updated_time = 2025-01-06T15:00:00Z WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, `DELETE activity_map[ 2 ] FROM executions ` + `WHERE shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := updateActivityInfos(batch, tc.shardID, tc.domainID, tc.workflowID, tc.runID, tc.activityInfos, tc.deleteInfos, FixedTime) if err != nil { t.Fatalf("updateActivityInfos() error = %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestCreateWorkflowExecutionWithMergeMaps(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-19T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string execution *nosqlplugin.WorkflowExecutionRequest // expectations wantQueries int wantErr bool }{ { desc: "EventBufferWriteMode is not None", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.WorkflowExecutionRequest{ EventBufferWriteMode: nosqlplugin.EventBufferWriteModeAppend, InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ CompletionEvent: &persistence.DataBlob{}, AutoResetPoints: &persistence.DataBlob{}, }, VersionHistories: &persistence.DataBlob{}, Checksums: &checksum.Checksum{}, }, wantErr: true, }, { desc: "MapsWriteMode is not Create", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.WorkflowExecutionRequest{ EventBufferWriteMode: nosqlplugin.EventBufferWriteModeNone, MapsWriteMode: nosqlplugin.WorkflowExecutionMapsWriteModeUpdate, InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ CompletionEvent: &persistence.DataBlob{}, AutoResetPoints: &persistence.DataBlob{}, }, VersionHistories: &persistence.DataBlob{}, Checksums: &checksum.Checksum{}, }, wantErr: true, }, { desc: "ok", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.WorkflowExecutionRequest{ EventBufferWriteMode: nosqlplugin.EventBufferWriteModeNone, MapsWriteMode: nosqlplugin.WorkflowExecutionMapsWriteModeCreate, InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ CompletionEvent: &persistence.DataBlob{}, AutoResetPoints: &persistence.DataBlob{}, }, VersionHistories: &persistence.DataBlob{}, Checksums: &checksum.Checksum{}, ActivityInfos: map[int64]*persistence.InternalActivityInfo{ 1: { Version: 1, ScheduledEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-scheduled-event-data"), }, ScheduledTime: ts.UTC(), ScheduleID: 1, StartedID: 2, StartedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-started-event-data"), }, ActivityID: "activity1", ScheduleToStartTimeout: 1 * time.Minute, ScheduleToCloseTimeout: 2 * time.Minute, StartToCloseTimeout: 3 * time.Minute, HeartbeatTimeout: 1 * time.Minute, Attempt: 3, MaximumAttempts: 5, TaskList: "tasklist1", HasRetryPolicy: true, LastFailureReason: "retry reason", }, }, TimerInfos: map[string]*persistence.TimerInfo{ "timer1": { Version: 1, TimerID: "timer1", StartedID: 2, ExpiryTime: ts, TaskStatus: 1, }, }, ChildWorkflowInfos: map[int64]*persistence.InternalChildExecutionInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, StartedID: 3, StartedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, }, StartedWorkflowID: "startedWorkflowID1", StartedRunID: "startedRunID1", CreateRequestID: "createRequestID1", InitiatedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, }, DomainID: "domain1", WorkflowTypeName: "workflowType1", ParentClosePolicy: types.ParentClosePolicyAbandon, }, }, RequestCancelInfos: map[int64]*persistence.RequestCancelInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, CancelRequestID: "cancelRequest1", }, }, SignalInfos: map[int64]*persistence.SignalInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, SignalRequestID: "request1", SignalName: "signal1", Input: []byte("input1"), Control: []byte("control1"), }, }, SignalRequestedIDs: []string{"signalRequestedID1"}, }, // expecting 7 queries: // - 1 for execution record // - 1 for activity info // - 1 for timer info // - 1 for child execution info // - 1 for request cancel info // - 1 for signal info // - 1 for signal requested IDs wantQueries: 7, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := createWorkflowExecutionWithMergeMaps(batch, tc.shardID, tc.domainID, tc.workflowID, tc.execution, FixedTime) gotErr := (err != nil) if gotErr != tc.wantErr { t.Fatalf("Got error: %v, want?: %v", err, tc.wantErr) } if gotErr { return } // actual queries generated by helper functions are covered in other unit tests. check the numer of total queries here. if got := len(batch.queries); got != tc.wantQueries { t.Fatalf("len(queries): %v, want: %v", got, tc.wantQueries) } }) } } func TestResetWorkflowExecutionAndMapsAndEventBuffer(t *testing.T) { tests := []struct { desc string shardID int domainID string workflowID string execution *nosqlplugin.WorkflowExecutionRequest // expectations wantQueries int wantErr bool }{ { desc: "EventBufferWriteMode is not Clear", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.WorkflowExecutionRequest{ EventBufferWriteMode: nosqlplugin.EventBufferWriteModeAppend, InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ CompletionEvent: &persistence.DataBlob{}, AutoResetPoints: &persistence.DataBlob{}, }, VersionHistories: &persistence.DataBlob{}, Checksums: &checksum.Checksum{}, }, wantErr: true, }, { desc: "MapsWriteMode is not Reset", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.WorkflowExecutionRequest{ EventBufferWriteMode: nosqlplugin.EventBufferWriteModeClear, MapsWriteMode: nosqlplugin.WorkflowExecutionMapsWriteModeUpdate, // Incorrect mode InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ CompletionEvent: &persistence.DataBlob{}, AutoResetPoints: &persistence.DataBlob{}, }, VersionHistories: &persistence.DataBlob{}, Checksums: &checksum.Checksum{}, }, wantErr: true, }, { desc: "ok", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.WorkflowExecutionRequest{ EventBufferWriteMode: nosqlplugin.EventBufferWriteModeClear, MapsWriteMode: nosqlplugin.WorkflowExecutionMapsWriteModeReset, InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ CompletionEvent: &persistence.DataBlob{}, AutoResetPoints: &persistence.DataBlob{}, }, VersionHistories: &persistence.DataBlob{}, Checksums: &checksum.Checksum{}, }, // expecting 8 queries: // - 1 for execution record // - 1 for deletion of buffered events // - 1 for activity info map reset // - 1 for timer info map reset // - 1 for child execution info map reset // - 1 for request cancel info map reset // - 1 for signal info map reset // - 1 for signal requested IDs reset wantQueries: 8, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := resetWorkflowExecutionAndMapsAndEventBuffer(batch, tc.shardID, tc.domainID, tc.workflowID, tc.execution, FixedTime) gotErr := (err != nil) if gotErr != tc.wantErr { t.Fatalf("Got error: %v, want?: %v", err, tc.wantErr) } if gotErr { return } // Check the number of total queries, actual queries generated by helper functions are covered in other unit tests. if got := len(batch.queries); got != tc.wantQueries { t.Fatalf("len(queries): %v, want: %v", got, tc.wantQueries) } }) } } func TestUpdateWorkflowExecutionAndEventBufferWithMergeAndDeleteMaps(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-19T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string execution *nosqlplugin.WorkflowExecutionRequest // expectations wantQueries int wantErr bool }{ { desc: "MapsWriteMode is not Update", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.WorkflowExecutionRequest{ EventBufferWriteMode: nosqlplugin.EventBufferWriteModeClear, MapsWriteMode: nosqlplugin.WorkflowExecutionMapsWriteModeCreate, // Incorrect mode InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ CompletionEvent: &persistence.DataBlob{}, AutoResetPoints: &persistence.DataBlob{}, }, VersionHistories: &persistence.DataBlob{}, Checksums: &checksum.Checksum{}, }, wantErr: true, }, { desc: "ok", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.WorkflowExecutionRequest{ EventBufferWriteMode: nosqlplugin.EventBufferWriteModeClear, MapsWriteMode: nosqlplugin.WorkflowExecutionMapsWriteModeUpdate, InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ CompletionEvent: &persistence.DataBlob{}, AutoResetPoints: &persistence.DataBlob{}, }, VersionHistories: &persistence.DataBlob{}, Checksums: &checksum.Checksum{}, ActivityInfos: map[int64]*persistence.InternalActivityInfo{ 1: { Version: 1, ScheduledEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-scheduled-event-data"), }, ScheduledTime: ts.UTC(), ScheduleID: 1, StartedID: 2, StartedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-started-event-data"), }, ActivityID: "activity1", ScheduleToStartTimeout: 1 * time.Minute, ScheduleToCloseTimeout: 2 * time.Minute, StartToCloseTimeout: 3 * time.Minute, HeartbeatTimeout: 1 * time.Minute, Attempt: 3, MaximumAttempts: 5, TaskList: "tasklist1", HasRetryPolicy: true, LastFailureReason: "retry reason", }, }, TimerInfos: map[string]*persistence.TimerInfo{ "timer1": { Version: 1, TimerID: "timer1", StartedID: 2, ExpiryTime: ts.UTC(), TaskStatus: 1, }, }, ChildWorkflowInfos: map[int64]*persistence.InternalChildExecutionInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, StartedID: 3, StartedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, }, StartedWorkflowID: "startedWorkflowID1", StartedRunID: "startedRunID1", CreateRequestID: "createRequestID1", InitiatedEvent: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, }, DomainID: "domain1", WorkflowTypeName: "workflowType1", ParentClosePolicy: types.ParentClosePolicyAbandon, }, }, RequestCancelInfos: map[int64]*persistence.RequestCancelInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, CancelRequestID: "cancelRequest1", }, }, SignalInfos: map[int64]*persistence.SignalInfo{ 1: { Version: 1, InitiatedID: 1, InitiatedEventBatchID: 2, SignalRequestID: "request1", SignalName: "signal1", Input: []byte("input1"), Control: []byte("control1"), }, }, SignalRequestedIDs: []string{"signalRequestedID1"}, }, // expecting 8 queries: // - 1 for execution record // - 1 for deletion of buffered events // - 1 for activity info map update // - 1 for timer info map update // - 1 for child execution info map update // - 1 for request cancel info map update // - 1 for signal info map update // - 1 for signal requested IDs update wantQueries: 8, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := updateWorkflowExecutionAndEventBufferWithMergeAndDeleteMaps(batch, tc.shardID, tc.domainID, tc.workflowID, tc.execution, FixedTime) gotErr := (err != nil) if gotErr != tc.wantErr { t.Fatalf("Got error: %v, want?: %v", err, tc.wantErr) } if gotErr { return } // Check the number of total queries, actual queries generated by helper functions are covered in other unit tests. if got := len(batch.queries); got != tc.wantQueries { t.Fatalf("len(queries): %v, want: %v", got, tc.wantQueries) } }) } } func TestUpdateWorkflowExecution(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-19T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string execution *nosqlplugin.WorkflowExecutionRequest // expectations wantQueries []string }{ { desc: "ok", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.WorkflowExecutionRequest{ EventBufferWriteMode: nosqlplugin.EventBufferWriteModeClear, MapsWriteMode: nosqlplugin.WorkflowExecutionMapsWriteModeUpdate, InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ DomainID: "domain1", WorkflowID: "workflow1", RunID: "runid1", ParentRunID: "parentRunID1", WorkflowTypeName: "workflowType1", TaskList: "tasklist1", TaskListKind: types.TaskListKindNormal, StartTimestamp: ts, LastUpdatedTimestamp: ts.Add(1 * time.Minute), DecisionScheduleID: 2, DecisionStartedID: 3, CompletionEvent: &persistence.DataBlob{}, AutoResetPoints: &persistence.DataBlob{}, CronOverlapPolicy: 0, }, PreviousNextEventIDCondition: common.Int64Ptr(10), VersionHistories: &persistence.DataBlob{}, Checksums: &checksum.Checksum{}, }, wantQueries: []string{ `UPDATE executions SET execution = {` + `domain_id: domain1, workflow_id: workflow1, run_id: runid1, first_run_id: , parent_domain_id: , parent_workflow_id: , ` + `parent_run_id: parentRunID1, initiated_id: 0, completion_event_batch_id: 0, completion_event: [], ` + `completion_event_data_encoding: , task_list: tasklist1, task_list_kind: 0, workflow_type_name: workflowType1, workflow_timeout: 0, ` + `decision_task_timeout: 0, execution_context: [], state: 0, close_status: 0, last_first_event_id: 0, last_event_task_id: 0, ` + `next_event_id: 0, last_processed_event: 0, start_time: 2023-12-19T22:08:41Z, last_updated_time: 2023-12-19T22:09:41Z, ` + `create_request_id: , signal_count: 0, history_size: 0, decision_version: 0, decision_schedule_id: 2, decision_started_id: 3, ` + `decision_request_id: , decision_timeout: 0, decision_attempt: 0, decision_timestamp: -6795364578871345152, ` + `decision_scheduled_timestamp: -6795364578871345152, decision_original_scheduled_timestamp: -6795364578871345152, ` + `cancel_requested: false, cancel_request_id: , sticky_task_list: , sticky_schedule_to_start_timeout: 0,client_library_version: , ` + `client_feature_version: , client_impl: , auto_reset_points: [], auto_reset_points_encoding: , attempt: 0, has_retry_policy: false, ` + `init_interval: 0, backoff_coefficient: 0, max_interval: 0, expiration_time: 0001-01-01T00:00:00Z, max_attempts: 0, ` + `non_retriable_errors: [], event_store_version: 2, branch_token: [], cron_schedule: , cron_overlap_policy: 0, expiration_seconds: 0, search_attributes: map[], ` + `memo: map[], partition_config: map[], active_cluster_selection_policy: [], active_cluster_selection_policy_encoding: ` + `}, next_event_id = 0 , version_histories = [] , version_histories_encoding = , checksum = {version: 0, flavor: 0, value: [] }, workflow_last_write_version = 0 , workflow_state = 0 , last_updated_time = 2025-01-06T15:00:00Z ` + `WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = runid1 and visibility_ts = 946684800000 and task_id = -10 ` + `IF next_event_id = 10 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := updateWorkflowExecution(batch, tc.shardID, tc.domainID, tc.workflowID, tc.execution, FixedTime) if err != nil { t.Fatalf("updateWorkflowExecution failed, err: %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestCreateWorkflowExecution(t *testing.T) { ts, err := time.Parse(time.RFC3339, "2023-12-19T22:08:41Z") if err != nil { t.Fatal(err) } tests := []struct { desc string shardID int domainID string workflowID string execution *nosqlplugin.WorkflowExecutionRequest // expectations wantQueries []string }{ { desc: "ok", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.WorkflowExecutionRequest{ EventBufferWriteMode: nosqlplugin.EventBufferWriteModeClear, MapsWriteMode: nosqlplugin.WorkflowExecutionMapsWriteModeUpdate, InternalWorkflowExecutionInfo: persistence.InternalWorkflowExecutionInfo{ DomainID: "domain1", WorkflowID: "workflow1", RunID: "runid1", ParentRunID: "parentRunID1", WorkflowTypeName: "workflowType1", TaskList: "tasklist1", TaskListKind: types.TaskListKindNormal, StartTimestamp: ts, LastUpdatedTimestamp: ts.Add(1 * time.Minute), DecisionScheduleID: 2, DecisionStartedID: 3, CompletionEvent: &persistence.DataBlob{}, AutoResetPoints: &persistence.DataBlob{}, ActiveClusterSelectionPolicy: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("thrift-encoded-active-cluster-selection-policy-data"), }, CronOverlapPolicy: types.CronOverlapPolicyBufferOne, }, PreviousNextEventIDCondition: common.Int64Ptr(10), VersionHistories: &persistence.DataBlob{}, Checksums: &checksum.Checksum{}, }, wantQueries: []string{ `INSERT INTO executions (shard_id, domain_id, workflow_id, run_id, type, execution, next_event_id, visibility_ts, task_id, version_histories, version_histories_encoding, checksum, workflow_last_write_version, workflow_state, created_time) ` + `VALUES(1000, domain1, workflow1, runid1, 1, ` + `{domain_id: domain1, workflow_id: workflow1, run_id: runid1, first_run_id: , parent_domain_id: , parent_workflow_id: , ` + `parent_run_id: parentRunID1, initiated_id: 0, completion_event_batch_id: 0, completion_event: [], completion_event_data_encoding: , ` + `task_list: tasklist1, task_list_kind: 0, workflow_type_name: workflowType1, workflow_timeout: 0, decision_task_timeout: 0, execution_context: [], state: 0, ` + `close_status: 0, last_first_event_id: 0, last_event_task_id: 0, next_event_id: 0, last_processed_event: 0, start_time: 2023-12-19T22:08:41Z, ` + `last_updated_time: 2023-12-19T22:09:41Z, create_request_id: , signal_count: 0, history_size: 0, decision_version: 0, ` + `decision_schedule_id: 2, decision_started_id: 3, decision_request_id: , decision_timeout: 0, decision_attempt: 0, ` + `decision_timestamp: -6795364578871345152, decision_scheduled_timestamp: -6795364578871345152, decision_original_scheduled_timestamp: -6795364578871345152, ` + `cancel_requested: false, cancel_request_id: , sticky_task_list: , sticky_schedule_to_start_timeout: 0,client_library_version: , client_feature_version: , ` + `client_impl: , auto_reset_points: [], auto_reset_points_encoding: , attempt: 0, has_retry_policy: false, init_interval: 0, ` + `backoff_coefficient: 0, max_interval: 0, expiration_time: 0001-01-01T00:00:00Z, max_attempts: 0, non_retriable_errors: [], ` + `event_store_version: 2, branch_token: [], cron_schedule: , cron_overlap_policy: 1, expiration_seconds: 0, search_attributes: map[], memo: map[], partition_config: map[], ` + `active_cluster_selection_policy: [116 104 114 105 102 116 45 101 110 99 111 100 101 100 45 97 99 116 105 118 101 45 99 108 117 115 116 101 114 45 115 101 108 101 99 116 105 111 110 45 112 111 108 105 99 121 45 100 97 116 97], active_cluster_selection_policy_encoding: thriftrw` + `}, 0, 946684800000, -10, [], , {version: 0, flavor: 0, value: [] }, 0, 0, 2025-01-06T15:00:00Z) IF NOT EXISTS `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := createWorkflowExecution(batch, tc.shardID, tc.domainID, tc.workflowID, tc.execution, FixedTime) if err != nil { t.Fatalf("createWorkflowExecution failed, err: %v", err) } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestCreateOrUpdateWorkflowExecution(t *testing.T) { tests := []struct { desc string shardID int domainID string workflowID string execution *nosqlplugin.CurrentWorkflowWriteRequest // expectations wantQueries []string wantErr bool }{ { desc: "unknown write mode", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: 255, // unknown write mode }, wantErr: true, }, { desc: "CurrentWorkflowWriteModeNoop", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeNoop, }, wantQueries: nil, }, { desc: "CurrentWorkflowWriteModeInsert", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeInsert, Row: nosqlplugin.CurrentWorkflowRow{ RunID: "runid1", CreateRequestID: "createRequestID1", State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, }, }, wantQueries: []string{ `INSERT INTO executions (shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id, current_run_id, execution, workflow_last_write_version, workflow_state, created_time) ` + `VALUES(1000, 1, domain1, workflow1, 30000000-0000-f000-f000-000000000001, 946684800000, -10, runid1, ` + `{run_id: runid1, create_request_id: createRequestID1, state: 0, close_status: 0}, 0, 0, 2025-01-06T15:00:00Z) ` + `IF NOT EXISTS USING TTL 0 `, }, }, { desc: "CurrentWorkflowWriteModeUpdate and condition missing", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Row: nosqlplugin.CurrentWorkflowRow{ RunID: "runid1", CreateRequestID: "createRequestID1", State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, }, }, wantErr: true, }, { desc: "CurrentWorkflowWriteModeUpdate and condition runid missing", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: nil, }, Row: nosqlplugin.CurrentWorkflowRow{ RunID: "runid1", CreateRequestID: "createRequestID1", State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, }, }, wantErr: true, }, { desc: "CurrentWorkflowWriteModeUpdate with LastWriteVersion", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: common.StringPtr("runid1"), LastWriteVersion: common.Int64Ptr(1), State: common.IntPtr(persistence.WorkflowStateCreated), }, Row: nosqlplugin.CurrentWorkflowRow{ RunID: "runid1", CreateRequestID: "createRequestID1", State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, }, }, wantQueries: []string{ `UPDATE executions USING TTL 0 SET ` + `current_run_id = runid1, ` + `execution = {run_id: runid1, create_request_id: createRequestID1, state: 0, close_status: 0}, ` + `workflow_last_write_version = 0, workflow_state = 0, last_updated_time = 2025-01-06T15:00:00Z ` + `WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = 30000000-0000-f000-f000-000000000001 and visibility_ts = 946684800000 and task_id = -10 ` + `IF current_run_id = runid1 and workflow_last_write_version = 1 and workflow_state = 0 `, }, }, { desc: "CurrentWorkflowWriteModeUpdate", shardID: 1000, domainID: "domain1", workflowID: "workflow1", execution: &nosqlplugin.CurrentWorkflowWriteRequest{ WriteMode: nosqlplugin.CurrentWorkflowWriteModeUpdate, Condition: &nosqlplugin.CurrentWorkflowWriteCondition{ CurrentRunID: common.StringPtr("runid1"), }, Row: nosqlplugin.CurrentWorkflowRow{ RunID: "runid1", CreateRequestID: "createRequestID1", State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, }, }, wantQueries: []string{ `UPDATE executions USING TTL 0 SET ` + `current_run_id = runid1, ` + `execution = {run_id: runid1, create_request_id: createRequestID1, state: 0, close_status: 0}, ` + `workflow_last_write_version = 0, workflow_state = 0, last_updated_time = 2025-01-06T15:00:00Z ` + `WHERE ` + `shard_id = 1000 and type = 1 and domain_id = domain1 and workflow_id = workflow1 and ` + `run_id = 30000000-0000-f000-f000-000000000001 and visibility_ts = 946684800000 and task_id = -10 ` + `IF current_run_id = runid1 `, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { batch := &fakeBatch{} err := createOrUpdateCurrentWorkflow(batch, tc.shardID, tc.domainID, tc.workflowID, tc.execution, FixedTime) gotErr := (err != nil) if gotErr != tc.wantErr { t.Fatalf("Got error: %v, want?: %v", err, tc.wantErr) } if gotErr { return } if diff := cmp.Diff(tc.wantQueries, batch.queries); diff != "" { t.Fatalf("Query mismatch (-want +got):\n%s", diff) } }) } } func TestMustConvertToSlice(t *testing.T) { tests := []struct { desc string in interface{} want []interface{} wantPanic bool }{ { desc: "nil", in: nil, wantPanic: true, }, { desc: "empty", in: []string{}, want: []interface{}{}, }, { desc: "slice", in: []string{"a", "b", "c"}, want: []interface{}{"a", "b", "c"}, }, { desc: "array", in: [3]string{"a", "b", "c"}, want: []interface{}{"a", "b", "c"}, }, { desc: "non-slice", in: "a", wantPanic: true, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { defer func() { r := recover() if (r != nil) != tc.wantPanic { t.Fatalf("Got panic: %v, want panic?: %v", r, tc.wantPanic) } }() got := mustConvertToSlice(tc.in) if diff := cmp.Diff(tc.want, got); diff != "" { t.Fatalf("Slice mismatch (-want +got):\n%s", diff) } }) } } func TestIsRequestRowType(t *testing.T) { assert.True(t, isRequestRowType(rowTypeWorkflowRequestStart)) assert.True(t, isRequestRowType(rowTypeWorkflowRequestSignal)) assert.True(t, isRequestRowType(rowTypeWorkflowRequestCancel)) assert.True(t, isRequestRowType(rowTypeWorkflowRequestReset)) } func errDiff(want, got error) string { wantCondFailure, wantOk := want.(*nosqlplugin.WorkflowOperationConditionFailure) gotCondFailure, gotOk := got.(*nosqlplugin.WorkflowOperationConditionFailure) if wantOk && gotOk { arg1 := trimWorkflowConditionalFailureErr(wantCondFailure) arg2 := trimWorkflowConditionalFailureErr(gotCondFailure) return cmp.Diff(arg1, arg2) } wantMsg := "" if want != nil { wantMsg = want.Error() } gotMsg := "" if got != nil { gotMsg = got.Error() } return cmp.Diff(wantMsg, gotMsg) } func trimWorkflowConditionalFailureErr(condFailure *nosqlplugin.WorkflowOperationConditionFailure) any { trimColumnsPart(condFailure.CurrentWorkflowConditionFailInfo) trimColumnsPart(condFailure.UnknownConditionFailureDetails) if condFailure.WorkflowExecutionAlreadyExists != nil { trimColumnsPart(&condFailure.WorkflowExecutionAlreadyExists.OtherInfo) } return condFailure } func trimColumnsPart(s *string) { if s == nil { return } re := regexp.MustCompile(`, columns: \(.*\)`) trimmed := re.ReplaceAllString(*s, "") *s = trimmed } ================================================ FILE: common/persistence/nosql/nosqlplugin/common.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosqlplugin import ( "os" "strings" ) func getCadencePackageDir() (string, error) { cadencePackageDir, err := os.Getwd() if err != nil { panic(err) } cadenceIndex := strings.LastIndex(cadencePackageDir, "cadence/") cadencePackageDir = cadencePackageDir[:cadenceIndex+len("cadence/")] return cadencePackageDir, err } func GetDefaultTestSchemaDir(testSchemaRelativePath string) (string, error) { cadencePackageDir, err := getCadencePackageDir() if err != nil { return "", err } return cadencePackageDir + testSchemaRelativePath, nil } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/admin.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" var _ nosqlplugin.AdminDB = (*ddb)(nil) func (db *ddb) SetupTestDatabase(schemaBaseDir string, replicas int) error { panic("TODO") } func (db *ddb) TeardownTestDatabase() error { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/configStore.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "context" "errors" "github.com/uber/cadence/common/persistence" ) func (db *ddb) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { return errors.New("TODO") } func (db *ddb) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { return nil, errors.New("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/db.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "errors" "fmt" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) const ( // PluginName is the name of the plugin PluginName = "dynamodb" ) var ( errConditionFailed = errors.New("internal condition fail error") ) // ddb represents a logical connection to DynamoDB database type ddb struct { } var _ nosqlplugin.DB = (*ddb)(nil) // NewDynamoDB return a new DB func NewDynamoDB(cfg config.NoSQL, logger log.Logger) (nosqlplugin.DB, error) { return nil, fmt.Errorf("TODO") } func (db *ddb) Close() { panic("TODO") } func (db *ddb) PluginName() string { return PluginName } func (db *ddb) IsNotFoundError(err error) bool { panic("TODO") } func (db *ddb) IsTimeoutError(err error) bool { panic("TODO") } func (db *ddb) IsThrottlingError(err error) bool { panic("TODO") } func (db *ddb) IsDBUnavailableError(err error) bool { panic("TODO") } func (db *ddb) IsConditionFailedError(err error) bool { return err == errConditionFailed } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/domain.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // Insert a new record to domain, return error if failed or already exists // Return ConditionFailure if the condition doesn't meet func (db *ddb) InsertDomain( ctx context.Context, row *nosqlplugin.DomainRow, ) error { panic("TODO") } // Update domain func (db *ddb) UpdateDomain( ctx context.Context, row *nosqlplugin.DomainRow, ) error { panic("TODO") } // Get one domain data, either by domainID or domainName func (db *ddb) SelectDomain( ctx context.Context, domainID *string, domainName *string, ) (*nosqlplugin.DomainRow, error) { panic("TODO") } // Get all domain data func (db *ddb) SelectAllDomains( ctx context.Context, pageSize int, pageToken []byte, ) ([]*nosqlplugin.DomainRow, []byte, error) { panic("TODO") } // Delete a domain, either by domainID or domainName func (db *ddb) DeleteDomain( ctx context.Context, domainID *string, domainName *string, ) error { panic("TODO") } func (db *ddb) SelectDomainMetadata( ctx context.Context, ) (int64, error) { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/domain_audit_log.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // InsertDomainAuditLog inserts a new audit log entry for a domain operation func (db *ddb) InsertDomainAuditLog(ctx context.Context, row *nosqlplugin.DomainAuditLogRow) error { panic("TODO: InsertDomainAuditLog not implemented") } // SelectDomainAuditLogs returns audit log entries for a domain and operation type func (db *ddb) SelectDomainAuditLogs(ctx context.Context, filter *nosqlplugin.DomainAuditLogFilter) ([]*nosqlplugin.DomainAuditLogRow, []byte, error) { panic("TODO: SelectDomainAuditLogs not implemented") } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/events.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // InsertIntoHistoryTreeAndNode inserts one or two rows: tree row and node row(at least one of them) func (db *ddb) InsertIntoHistoryTreeAndNode(ctx context.Context, treeRow *nosqlplugin.HistoryTreeRow, nodeRow *nosqlplugin.HistoryNodeRow) error { panic("TODO") } // SelectFromHistoryNode read nodes based on a filter func (db *ddb) SelectFromHistoryNode(ctx context.Context, filter *nosqlplugin.HistoryNodeFilter) ([]*nosqlplugin.HistoryNodeRow, []byte, error) { panic("TODO") } // DeleteFromHistoryTreeAndNode delete a branch record, and a list of ranges of nodes. func (db *ddb) DeleteFromHistoryTreeAndNode(ctx context.Context, treeFilter *nosqlplugin.HistoryTreeFilter, nodeFilters []*nosqlplugin.HistoryNodeFilter) error { panic("TODO") } // SelectAllHistoryTrees will return all tree branches with pagination func (db *ddb) SelectAllHistoryTrees(ctx context.Context, nextPageToken []byte, pageSize int) ([]*nosqlplugin.HistoryTreeRow, []byte, error) { panic("TODO") } // SelectFromHistoryTree read branch records for a tree func (db *ddb) SelectFromHistoryTree(ctx context.Context, filter *nosqlplugin.HistoryTreeFilter) ([]*nosqlplugin.HistoryTreeRow, error) { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/queue.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // Insert message into queue, return error if failed or already exists // Return ConditionFailure if the condition doesn't meet func (db *ddb) InsertIntoQueue( ctx context.Context, row *nosqlplugin.QueueMessageRow, ) error { panic("TODO") } // Get the ID of last message inserted into the queue func (db *ddb) SelectLastEnqueuedMessageID( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { panic("TODO") } // Read queue messages starting from the exclusiveBeginMessageID func (db *ddb) SelectMessagesFrom( ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, maxRows int, ) ([]*nosqlplugin.QueueMessageRow, error) { panic("TODO") } // Read queue message starting from exclusiveBeginMessageID int64, inclusiveEndMessageID int64 func (db *ddb) SelectMessagesBetween( ctx context.Context, request nosqlplugin.SelectMessagesBetweenRequest, ) (*nosqlplugin.SelectMessagesBetweenResponse, error) { panic("TODO") } // Delete all messages before exclusiveBeginMessageID func (db *ddb) DeleteMessagesBefore( ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, ) error { panic("TODO") } // Delete all messages in a range between exclusiveBeginMessageID and inclusiveEndMessageID func (db *ddb) DeleteMessagesInRange( ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, inclusiveEndMessageID int64, ) error { panic("TODO") } // Delete one message func (db *ddb) DeleteMessage( ctx context.Context, queueType persistence.QueueType, messageID int64, ) error { panic("TODO") } // Insert an empty metadata row, starting from a version func (db *ddb) InsertQueueMetadata(ctx context.Context, row nosqlplugin.QueueMetadataRow) error { panic("TODO") } // **Conditionally** update a queue metadata row, if current version is matched(meaning current == row.Version - 1), // then the current version will increase by one when updating the metadata row // Return ConditionFailure if the condition doesn't meet func (db *ddb) UpdateQueueMetadataCas( ctx context.Context, row nosqlplugin.QueueMetadataRow, ) error { panic("TODO") } // Read a QueueMetadata func (db *ddb) SelectQueueMetadata( ctx context.Context, queueType persistence.QueueType, ) (*nosqlplugin.QueueMetadataRow, error) { panic("TODO") } func (db *ddb) GetQueueSize( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/shard.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // InsertShard creates a new shard, return error is there is any. // Return ShardOperationConditionFailure if the condition doesn't meet func (db *ddb) InsertShard(ctx context.Context, row *nosqlplugin.ShardRow) error { panic("TODO") } // SelectShard gets a shard func (db *ddb) SelectShard(ctx context.Context, shardID int, currentClusterName string) (int64, *nosqlplugin.ShardRow, error) { panic("TODO") } // UpdateRangeID updates the rangeID, return error is there is any // Return ShardOperationConditionFailure if the condition doesn't meet func (db *ddb) UpdateRangeID(ctx context.Context, shardID int, rangeID int64, previousRangeID int64) error { panic("TODO") } // UpdateShard updates a shard, return error is there is any. // Return ShardOperationConditionFailure if the condition doesn't meet func (db *ddb) UpdateShard(ctx context.Context, row *nosqlplugin.ShardRow, previousRangeID int64) error { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/task.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // SelectTaskList returns a single tasklist row. // Return IsNotFoundError if the row doesn't exist func (db *ddb) SelectTaskList(ctx context.Context, filter *nosqlplugin.TaskListFilter) (*nosqlplugin.TaskListRow, error) { panic("TODO") } // InsertTaskList insert a single tasklist row // Return IsConditionFailedError if the row already exists, and also the existing row func (db *ddb) InsertTaskList(ctx context.Context, row *nosqlplugin.TaskListRow) error { panic("TODO") } // UpdateTaskList updates a single tasklist row // Return TaskOperationConditionFailure if the condition doesn't meet func (db *ddb) UpdateTaskList( ctx context.Context, row *nosqlplugin.TaskListRow, previousRangeID int64, ) error { panic("TODO") } // UpdateTaskList updates a single tasklist row, and set an TTL on the record // Return TaskOperationConditionFailure if the condition doesn't meet // Ignore TTL if it's not supported, which becomes exactly the same as UpdateTaskList, but ListTaskList must be // implemented for TaskListScavenger func (db *ddb) UpdateTaskListWithTTL( ctx context.Context, ttlSeconds int64, row *nosqlplugin.TaskListRow, previousRangeID int64, ) error { panic("TODO") } // ListTaskList returns all tasklists. // Noop if TTL is already implemented in other methods func (db *ddb) ListTaskList(ctx context.Context, pageSize int, nextPageToken []byte) (*nosqlplugin.ListTaskListResult, error) { panic("TODO") } // DeleteTaskList deletes a single tasklist row // Return TaskOperationConditionFailure if the condition doesn't meet func (db *ddb) DeleteTaskList(ctx context.Context, filter *nosqlplugin.TaskListFilter, previousRangeID int64) error { panic("TODO") } // InsertTasks inserts a batch of tasks // Return TaskOperationConditionFailure if the condition doesn't meet func (db *ddb) InsertTasks( ctx context.Context, tasksToInsert []*nosqlplugin.TaskRowForInsert, tasklistCondition *nosqlplugin.TaskListRow, ) error { panic("TODO") } // SelectTasks return tasks that associated to a tasklist func (db *ddb) SelectTasks(ctx context.Context, filter *nosqlplugin.TasksFilter) ([]*nosqlplugin.TaskRow, error) { panic("TODO") } // SelectTasks return tasks that associated to a tasklist func (db *ddb) GetTasksCount(ctx context.Context, filter *nosqlplugin.TasksFilter) (int64, error) { panic("TODO") } // DeleteTask delete a batch tasks that taskIDs less than the row // If TTL is not implemented, then should also return the number of rows deleted, otherwise persistence.UnknownNumRowsAffected // NOTE: This API ignores the `BatchSize` request parameter i.e. either all tasks leq the task_id will be deleted or an error will // be returned to the caller, because rowsDeleted is not supported by Cassandra func (db *ddb) RangeDeleteTasks(ctx context.Context, filter *nosqlplugin.TasksFilter) (rowsDeleted int, err error) { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/visibility.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) func (db *ddb) InsertVisibility( ctx context.Context, ttlSeconds int64, row *nosqlplugin.VisibilityRowForInsert, ) error { panic("TODO") } func (db *ddb) UpdateVisibility( ctx context.Context, ttlSeconds int64, row *nosqlplugin.VisibilityRowForUpdate, ) error { panic("TODO") } func (db *ddb) SelectVisibility( ctx context.Context, filter *nosqlplugin.VisibilityFilter, ) (*nosqlplugin.SelectVisibilityResponse, error) { panic("TODO") } func (db *ddb) DeleteVisibility( ctx context.Context, domainID, workflowID, runID string, ) error { panic("TODO") } func (db *ddb) SelectOneClosedWorkflow( ctx context.Context, domainID, workflowID, runID string, ) (*nosqlplugin.VisibilityRow, error) { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/dynamodb/workflow.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "context" "time" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) var _ nosqlplugin.WorkflowCRUD = (*ddb)(nil) func (db *ddb) InsertWorkflowExecutionWithTasks( ctx context.Context, requests *nosqlplugin.WorkflowRequestsWriteRequest, currentWorkflowRequest *nosqlplugin.CurrentWorkflowWriteRequest, execution *nosqlplugin.WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask, activeClusterSelectionPolicyRow *nosqlplugin.ActiveClusterSelectionPolicyRow, shardCondition *nosqlplugin.ShardCondition, ) error { panic("TODO") } func (db *ddb) UpdateWorkflowExecutionWithTasks( ctx context.Context, requests *nosqlplugin.WorkflowRequestsWriteRequest, currentWorkflowRequest *nosqlplugin.CurrentWorkflowWriteRequest, mutatedExecution *nosqlplugin.WorkflowExecutionRequest, insertedExecution *nosqlplugin.WorkflowExecutionRequest, activeClusterSelectionPolicyRow *nosqlplugin.ActiveClusterSelectionPolicyRow, resetExecution *nosqlplugin.WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask, shardCondition *nosqlplugin.ShardCondition, ) error { panic("TODO") } func (db *ddb) SelectCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID string) (*nosqlplugin.CurrentWorkflowRow, error) { panic("TODO") } func (db *ddb) SelectWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) (*nosqlplugin.WorkflowExecution, error) { panic("TODO") } func (db *ddb) DeleteCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID, currentRunIDCondition string) error { panic("TODO") } func (db *ddb) DeleteWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) error { panic("TODO") } func (db *ddb) SelectAllCurrentWorkflows(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.CurrentWorkflowExecution, []byte, error) { panic("TODO") } func (db *ddb) SelectAllWorkflowExecutions(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.InternalListConcreteExecutionsEntity, []byte, error) { panic("TODO") } func (db *ddb) IsWorkflowExecutionExists(ctx context.Context, shardID int, domainID, workflowID, runID string) (bool, error) { panic("TODO") } func (db *ddb) SelectTransferTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { panic("TODO") } func (db *ddb) DeleteTransferTask(ctx context.Context, shardID int, taskID int64) error { panic("TODO") } func (db *ddb) RangeDeleteTransferTasks(ctx context.Context, shardID int, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { panic("TODO") } func (db *ddb) SelectTimerTasksOrderByVisibilityTime(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTime, exclusiveMaxTime time.Time) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { panic("TODO") } func (db *ddb) DeleteTimerTask(ctx context.Context, shardID int, taskID int64, visibilityTimestamp time.Time) error { panic("TODO") } func (db *ddb) RangeDeleteTimerTasks(ctx context.Context, shardID int, inclusiveMinTime, exclusiveMaxTime time.Time) error { panic("TODO") } func (db *ddb) SelectReplicationTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { panic("TODO") } func (db *ddb) DeleteReplicationTask(ctx context.Context, shardID int, taskID int64) error { panic("TODO") } func (db *ddb) RangeDeleteReplicationTasks(ctx context.Context, shardID int, exclusiveEndTaskID int64) error { panic("TODO") } func (db *ddb) InsertReplicationTask(ctx context.Context, tasks []*nosqlplugin.HistoryMigrationTask, condition nosqlplugin.ShardCondition) error { panic("TODO") } func (db *ddb) DeleteCrossClusterTask(ctx context.Context, shardID int, targetCluster string, taskID int64) error { panic("TODO") } func (db *ddb) InsertReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, task *nosqlplugin.HistoryMigrationTask) error { panic("TODO") } func (db *ddb) SelectReplicationDLQTasksOrderByTaskID(ctx context.Context, shardID int, sourceCluster string, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { panic("TODO") } func (db *ddb) SelectReplicationDLQTasksCount(ctx context.Context, shardID int, sourceCluster string) (int64, error) { panic("TODO") } func (db *ddb) DeleteReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, taskID int64) error { panic("TODO") } func (db *ddb) RangeDeleteReplicationDLQTasks(ctx context.Context, shardID int, sourceCluster string, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { panic("TODO") } func (db *ddb) SelectActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, wfID, rID string) (*nosqlplugin.ActiveClusterSelectionPolicyRow, error) { panic("TODO") } func (db *ddb) DeleteActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, wfID, rID string) error { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/errors.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosqlplugin import ( "fmt" "github.com/uber/cadence/common/persistence" ) // Condition Errors for NoSQL interfaces type ( // Only one of the fields must be non-nil WorkflowOperationConditionFailure struct { UnknownConditionFailureDetails *string // return some info for logging ShardRangeIDNotMatch *int64 // return the previous shardRangeID WorkflowExecutionAlreadyExists *WorkflowExecutionAlreadyExists CurrentWorkflowConditionFailInfo *string // return the logging info if fail on condition of CurrentWorkflow DuplicateRequest *DuplicateRequest } DuplicateRequest struct { RequestType persistence.WorkflowRequestType RunID string } WorkflowExecutionAlreadyExists struct { RunID string CreateRequestID string State int CloseStatus int LastWriteVersion int64 OtherInfo string } TaskOperationConditionFailure struct { RangeID int64 Details string // detail info for logging } ShardOperationConditionFailure struct { RangeID int64 Details string // detail info for logging } ConditionFailure struct { componentName string } ) var _ error = (*WorkflowOperationConditionFailure)(nil) func (e *WorkflowOperationConditionFailure) Error() string { return "workflow operation condition failure" } var _ error = (*TaskOperationConditionFailure)(nil) func (e *TaskOperationConditionFailure) Error() string { return "task operation condition failure" } var _ error = (*ShardOperationConditionFailure)(nil) func (e *ShardOperationConditionFailure) Error() string { return "shard operation condition failure" } var _ error = (*ConditionFailure)(nil) func NewConditionFailure(name string) error { return &ConditionFailure{ componentName: name, } } func (e *ConditionFailure) Error() string { return fmt.Sprintf("%s operation condition failure", e.componentName) } ================================================ FILE: common/persistence/nosql/nosqlplugin/interfaces.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interfaces_mock.go -self_package github.com/uber/cadence/common/persistence/nosql/nosqlplugin package nosqlplugin import ( "context" "time" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" ) type ( // Plugin defines the interface for any NoSQL database that needs to implement Plugin interface { CreateDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (DB, error) CreateAdminDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (AdminDB, error) } // AdminDB is for tooling and testing AdminDB interface { SetupTestDatabase(schemaBaseDir string, replicas int) error TeardownTestDatabase() error } // DB defines the API for regular NoSQL operations of a Cadence server DB interface { PluginName() string Close() ClientErrorChecker tableCRUD } // tableCRUD defines the API for interacting with the database tables // NOTE 1: All SELECT interfaces require strong consistency (eventual consistency will not work) unless specify in the method. // // NOTE 2: About schema: only the columns that need to be used directly in queries are considered 'significant', // including partition key, range key or index key and conditional columns, are required to be in the schema. // All other non 'significant' columns are opaque for implementation. // Therefore, it's recommended to use a data blob to store all the other columns, so that adding new // column will not require schema changes. This approach has been proved very successful in MySQL/Postgres implementation of SQL interfaces. // Cassandra implementation cannot do it due to backward-compatibility. Any other NoSQL implementation should use datablob for non-significant columns. // Follow the comment for each tableCRUD for what are 'significant' columns. tableCRUD interface { HistoryEventsCRUD MessageQueueCRUD DomainCRUD ShardCRUD VisibilityCRUD TaskCRUD WorkflowCRUD ConfigStoreCRUD DomainAuditLogCRUD } // ClientErrorChecker checks for common nosql errors on client ClientErrorChecker interface { IsTimeoutError(error) bool IsNotFoundError(error) bool IsThrottlingError(error) bool IsDBUnavailableError(error) bool } /** * HistoryEventsCRUD is for History events storage system * Recommendation: use two tables: history_tree for branch records and history_node for node records * if a single update query can operate on two tables. * * Significant columns: * history_tree partition key: (shardID, treeID), range key: (branchID) * history_node partition key: (shardID, treeID), range key: (branchID, nodeID ASC, txnID DESC) */ HistoryEventsCRUD interface { // InsertIntoHistoryTreeAndNode inserts one or two rows: tree row and node row(at least one of them) InsertIntoHistoryTreeAndNode(ctx context.Context, treeRow *HistoryTreeRow, nodeRow *HistoryNodeRow) error // SelectFromHistoryNode read nodes based on a filter SelectFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) ([]*HistoryNodeRow, []byte, error) // DeleteFromHistoryTreeAndNode delete a branch record, and a list of ranges of nodes. // for each range, it will delete all nodes starting from MinNodeID(inclusive) DeleteFromHistoryTreeAndNode(ctx context.Context, treeFilter *HistoryTreeFilter, nodeFilters []*HistoryNodeFilter) error // SelectAllHistoryTrees will return all tree branches with pagination SelectAllHistoryTrees(ctx context.Context, nextPageToken []byte, pageSize int) ([]*HistoryTreeRow, []byte, error) // SelectFromHistoryTree read branch records for a tree. // It returns without pagination, because we assume one tree won't have too many branches. SelectFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) ([]*HistoryTreeRow, error) } /*** * MessageQueueCRUD is for the message queue storage system * * Recommendation: use two tables(queue_message,and queue_metadata) to implement this interface * * Significant columns: * queue_message partition key: (queueType), range key: (messageID) * queue_metadata partition key: (queueType), range key: N/A, query condition column(version) */ MessageQueueCRUD interface { // Insert message into queue, return error if failed or already exists // Must return conditionFailed error if row already exists InsertIntoQueue(ctx context.Context, row *QueueMessageRow) error // Get the ID of last message inserted into the queue SelectLastEnqueuedMessageID(ctx context.Context, queueType persistence.QueueType) (int64, error) // Read queue messages starting from the exclusiveBeginMessageID SelectMessagesFrom(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, maxRows int) ([]*QueueMessageRow, error) // Read queue message starting from exclusiveBeginMessageID int64, inclusiveEndMessageID int64 SelectMessagesBetween(ctx context.Context, request SelectMessagesBetweenRequest) (*SelectMessagesBetweenResponse, error) // Delete all messages before exclusiveBeginMessageID DeleteMessagesBefore(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64) error // Delete all messages in a range between exclusiveBeginMessageID and inclusiveEndMessageID DeleteMessagesInRange(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, inclusiveEndMessageID int64) error // Delete one message DeleteMessage(ctx context.Context, queueType persistence.QueueType, messageID int64) error // Insert an empty metadata row, starting from a version InsertQueueMetadata(ctx context.Context, row QueueMetadataRow) error // **Conditionally** update a queue metadata row, if current version is matched(meaning current == row.Version - 1), // then the current version will increase by one when updating the metadata row // Must return conditionFailed error if the condition is not met UpdateQueueMetadataCas(ctx context.Context, row QueueMetadataRow) error // Read a QueueMetadata SelectQueueMetadata(ctx context.Context, queueType persistence.QueueType) (*QueueMetadataRow, error) // GetQueueSize return the queue size GetQueueSize(ctx context.Context, queueType persistence.QueueType) (int64, error) } /*** * DomainCRUD is for domain + domain metadata storage system * * Recommendation: two tables(domain, domain_metadata) to implement if conditional updates on two tables is supported * * Significant columns: * domain: partition key( a constant value), range key(domainName), local secondary index(domainID) * domain_metadata: partition key( a constant value), range key(N/A), query condition column(notificationVersion) * * Note 1: About Cassandra's implementation: Because of historical reasons, Cassandra uses two table, * domains and domains_by_name_v2. Therefore, Cassandra implementation lost the atomicity causing some edge cases, * and the implementation is more complicated than it should be. * * Note 2: Cassandra doesn't support conditional updates on multiple tables. Hence the domain_metadata table is implemented * as a special record as "domain metadata". It is an integer number as notification version. * The main purpose of it is to notify clusters that there is some changes in domains, so domain cache needs to refresh. * It always increase by one, whenever a domain is updated or inserted. * Updating this failover metadata with domain insert/update needs to be atomic. * Because Batch LWTs is only allowed within one table and same partition. * The Cassandra implementation stores it in the same table as domain in domains_by_name_v2. * * Note 3: It's okay to use a constant value for partition key because domain table is serving very small volume of traffic. */ DomainCRUD interface { // Insert a new record to domain // return types.DomainAlreadyExistsError error if failed or already exists // Must return ConditionFailure error if other condition doesn't match InsertDomain(ctx context.Context, row *DomainRow) error // Update domain data // Must return ConditionFailure error if update condition doesn't match UpdateDomain(ctx context.Context, row *DomainRow) error // Get one domain data, either by domainID or domainName SelectDomain(ctx context.Context, domainID *string, domainName *string) (*DomainRow, error) // Get all domain data SelectAllDomains(ctx context.Context, pageSize int, pageToken []byte) ([]*DomainRow, []byte, error) // Delete a domain, either by domainID or domainName DeleteDomain(ctx context.Context, domainID *string, domainName *string) error // right now domain metadata is just an integer as notification version SelectDomainMetadata(ctx context.Context) (int64, error) } /** * ShardCRUD is for shard storage of workflow execution. * Recommendation: use one table if database support batch conditional update on multiple tables, otherwise combine with WorkflowCRUD (likeCassandra) * * Significant columns: * domain: partition key(shardID), range key(N/A), local secondary index(domainID), query condition column(rangeID) * * Note 1: shard will be required to run conditional update with WorkflowCRUD. So in some nosql database like Cassandra, * ShardCRUD and WorkflowCRUD must be implemented within the same table. Because Cassandra only allows LightWeight transaction * executed within a single table. * Note 2: unlike Cassandra, most NoSQL databases don't return the previous rows when conditional write fails. In this case, * an extra read query is needed to get the previous row. */ ShardCRUD interface { // InsertShard creates a new shard. // Return error is there is any thing wrong // Return the ShardOperationConditionFailure when doesn't meet the condition InsertShard(ctx context.Context, row *ShardRow) error // SelectShard gets a shard, rangeID is the current rangeID in shard row SelectShard(ctx context.Context, shardID int, currentClusterName string) (rangeID int64, shard *ShardRow, err error) // UpdateRangeID updates the rangeID // Return error is there is any thing wrong // Return the ShardOperationConditionFailure when doesn't meet the condition UpdateRangeID(ctx context.Context, shardID int, rangeID int64, previousRangeID int64) error // UpdateShard updates a shard // Return error is there is any thing wrong // Return the ShardOperationConditionFailure when doesn't meet the condition UpdateShard(ctx context.Context, row *ShardRow, previousRangeID int64) error } /** * VisibilityCRUD is for visibility using database. * Database visibility usually is no longer recommended. AdvancedVisibility(with Kafka+ElasticSearch) is more powerful and scalable. * Feel free to skip this interface for any NoSQL plugin(use TODO() in the implementation) * * Recommendation: use one table with multiple indexes * * Significant columns: * domain: partition key(domainID), range key(workflowID, runID), * local secondary index #1(startTime), * local secondary index #2(closedTime), * local secondary index #3(workflowType, startTime), * local secondary index #4(workflowType, closedTime), * local secondary index #5(workflowID, startTime), * local secondary index #6(workflowID, closedTime), * local secondary index #7(closeStatus, closedTime), * * NOTE 1: Cassandra implementation of visibility uses three tables: open_executions, closed_executions and closed_executions_v2, * because Cassandra doesn't support cross-partition indexing. * Records in open_executions and closed_executions are clustered by start_time. Records in closed_executions_v2 are by close_time. * This optimizes the performance, but introduce a lot of complexity. * In some other databases, this may be be necessary. Please refer to MySQL/Postgres implementation which uses only * one table with multiple indexes. * * NOTE 2: TTL(time to live records) is for auto-deleting expired records in visibility. For databases that don't support TTL, * please implement DeleteVisibility method. If TTL is supported, then DeleteVisibility can be a noop. */ VisibilityCRUD interface { InsertVisibility(ctx context.Context, ttlSeconds int64, row *VisibilityRowForInsert) error UpdateVisibility(ctx context.Context, ttlSeconds int64, row *VisibilityRowForUpdate) error SelectVisibility(ctx context.Context, filter *VisibilityFilter) (*SelectVisibilityResponse, error) DeleteVisibility(ctx context.Context, domainID, workflowID, runID string) error // TODO deprecated this in the future in favor of SelectVisibility // Special case: return nil,nil if not found(since we will deprecate it, it's not worth refactor to be consistent) SelectOneClosedWorkflow(ctx context.Context, domainID, workflowID, runID string) (*VisibilityRow, error) } VisibilityRowForInsert struct { VisibilityRow DomainID string } VisibilityRowForUpdate struct { VisibilityRow DomainID string // NOTE: this is only for some implementation (e.g. Cassandra) that uses multiple tables, // they needs to delete record from the open execution table. Ignore this field if not need it UpdateOpenToClose bool // Similar as UpdateOpenToClose UpdateCloseToOpen bool } // TODO separate in the future when need it VisibilityRow = persistence.InternalVisibilityWorkflowExecutionInfo SelectVisibilityResponse struct { Executions []*VisibilityRow NextPageToken []byte } // VisibilityFilter contains the column names within executions_visibility table that // can be used to filter results through a WHERE clause VisibilityFilter struct { ListRequest persistence.InternalListWorkflowExecutionsRequest FilterType VisibilityFilterType SortType VisibilitySortType WorkflowType string WorkflowID string CloseStatus int32 } VisibilityFilterType int VisibilitySortType int /** * TaskCRUD is for tasklist and worker tasks storage * The task here is only referred to workflow/activity worker tasks. `Task` is a overloaded term in Cadence. * There is another 'task' storage which is for internal purpose only in WorkflowCRUD. * * Recommendation: use two tables(tasklist + task) to implement * tasklist table stores the metadata mainly for * * rangeID: ownership management, and taskID management everytime a matching host claim ownership of a tasklist, * it must increase the value succesfully . also used as base section of the taskID. E.g, if rangeID is 1, * then allowed taskID ranged will be [100K, 2*100K-1]. * * ackLevel: max taskID that can be safely deleted. * Any task record is associated with a tasklist. Any updates on a task should use rangeID of the associated tasklist as condition. * * Significant columns: * tasklist: partition key(domainID, taskListName, taskListType), range key(N/A), query condition column(rangeID) * task:partition key(domainID, taskListName, taskListType), range key(taskID), query condition column(rangeID) * * NOTE 1: Cassandra implementation uses the same table for tasklist and task, because Cassandra only allows * batch conditional updates(LightWeight transaction) executed within a single table. * NOTE 2: TTL(time to live records) is for auto-deleting task and some tasklists records. For databases that don't * support TTL, please implement ListTaskList method, and allows TaskListScavenger like MySQL/Postgres. * If TTL is supported, then ListTaskList can be a noop. */ TaskCRUD interface { // SelectTaskList returns a single tasklist row. // Return IsNotFoundError if the row doesn't exist SelectTaskList(ctx context.Context, filter *TaskListFilter) (*TaskListRow, error) // InsertTaskList insert a single tasklist row // Return TaskOperationConditionFailure if the row already exists InsertTaskList(ctx context.Context, row *TaskListRow) error // UpdateTaskList updates a single tasklist row // Return TaskOperationConditionFailure if the condition doesn't meet UpdateTaskList(ctx context.Context, row *TaskListRow, previousRangeID int64) error // UpdateTaskList updates a single tasklist row, and set an TTL on the record // Return TaskOperationConditionFailure if the condition doesn't meet // Ignore TTL if it's not supported, which becomes exactly the same as UpdateTaskList, but ListTaskList must be // implemented for TaskListScavenger UpdateTaskListWithTTL(ctx context.Context, ttlSeconds int64, row *TaskListRow, previousRangeID int64) error // ListTaskList returns all tasklists. // Noop if TTL is already implemented in other methods ListTaskList(ctx context.Context, pageSize int, nextPageToken []byte) (*ListTaskListResult, error) // DeleteTaskList deletes a single tasklist row // Return TaskOperationConditionFailure if the condition doesn't meet DeleteTaskList(ctx context.Context, filter *TaskListFilter, previousRangeID int64) error // InsertTasks inserts a batch of tasks // Return TaskOperationConditionFailure if the condition doesn't meet InsertTasks(ctx context.Context, tasksToInsert []*TaskRowForInsert, tasklistCondition *TaskListRow) error // SelectTasks return tasks that associated to a tasklist SelectTasks(ctx context.Context, filter *TasksFilter) ([]*TaskRow, error) // DeleteTask delete a batch of tasks // Also return the number of rows deleted -- if it's not supported then ignore the batchSize, and return persistence.UnknownNumRowsAffected RangeDeleteTasks(ctx context.Context, filter *TasksFilter) (rowsDeleted int, err error) // GetTasksCount return the number of tasks GetTasksCount(ctx context.Context, filter *TasksFilter) (int64, error) } /** * WorkflowCRUD is for core data models of workflow execution. * * Recommendation: If possible, use 8 tables(current_workflow, workflow_execution, transfer_task, replication_task, cross_cluster_task, timer_task, buffered_event_list, replication_dlq_task) to implement * current_workflow is to track the currentRunID of a workflowID for ensuring the ID-Uniqueness of Cadence workflows. * Each record is for one workflowID * workflow_execution is to store the core data of workflow execution. * Each record is for one runID(workflow execution run). * Different from TaskCRUD, transfer_task, replication_task, cross_cluster_task, timer_task are all internal background tasks within Cadence server. * transfer_task is to store the background tasks that need to be processed by historyEngine, right after the transaction. * There are lots of usage in historyEngine, like creating activity/childWF/etc task, and updating search attributes, etc. * replication_task is to store also background tasks that need to be processed right after the transaction, * but only for CrossDC(XDC) replication feature. Each record is a replication task generated from a source cluster. * Replication task stores a reference to a batch of history events(see historyCRUD). * timer_task is to store the durable timers that will fire in the future. Therefore this table should be indexed by the firingTime. * The durable timers are not only for workflow timers, but also for all kinds of timeouts, and workflow deletion, etc. * cross_cluster_task is to store also background tasks that need to be processed right after the transaction, and only for * but only for cross cluster feature. Each record is a cross cluster task generated for a target cluster. * CrossCluster task stores information similar to TransferTask. * buffered_event_list is to store the buffered event of a workflow execution * The above 7 tables will be required to execute transaction write with the condition of shard record from ShardCRUD. * replication_dlq_task is DeadLetterQueue when target cluster pulling and applying replication task. Each record represents * a task for a target cluster. * * Significant columns: * current_workflow: partition key(shardID), range key(domainID, workflowID), query condition column(currentRunID, lastWriteVersion, state) * workflow_execution: partition key(shardID), range key(domainID, workflowID, runID), query condition column(nextEventID) * transfer_task: partition key(shardID), range key(taskID) * replication_task: partition key(shardID), range key(taskID) * cross_cluster_task: partition key(shardID), range key(clusterName, taskID) * timer_task: partition key(shardID), range key(visibilityTimestamp) * buffered_event_list: partition key(shardID), range key(domainID, workflowID, runID) * replication_dlq_task: partition key(shardID), range key(clusterName, taskID) * * NOTE: Cassandra limits lightweight transaction to execute within one table. So the 6 tables + shard table are implemented * via a single table `execution` in Cassandra, using `rowType` to differentiate the 7 tables, and using `permanentRunID` * to differentiate current_workflow and workflow_execution * NOTE: Cassandra implementation uses 6 maps to store activityInfo, timerInfo, childWorkflowInfo, requestCancels, * signalInfo and signalRequestedInfo.Those should be fine to be stored in the same record as its workflow_execution. * However, signalInfo stores the in progress signal data. It may be too big for a single record. For example, DynamoDB * requires 400KB of a record. In that case, it may be better to have a separate table for signalInfo. * NOTE: Cassandra implementation of workflow_execution uses maps without "frozen". This has the advantage of deleting activity/timer/childWF/etc * by keys. The equivalent of this may require a read before overwriting the existing. Eg. [ "act1": , "act2": ] * When deleting "act1", Cassandra implementation can delete without read. If storing in the same record of workflwo_execution, * it will require to read the whole activityInfo map for deleting. * NOTE: Optional optimization: taskID that are writing into internal tasks(transfer/replication/crossCluster) are immutable and always increasing. * So it is possible to write the tasks in a single record, indexing by the lowest or highest taskID. * This approach can't be used by timerTasks as timers are ordered by visibilityTimestamp. * This is useful for DynamoDB because a transaction cannot contain more than 25 unique items. * */ WorkflowCRUD interface { // InsertWorkflowExecutionWithTasks is for creating a new workflow execution record. Within a transaction, it also: // 1. Create or update the record of current_workflow with the same workflowID, based on CurrentWorkflowExecutionWriteMode, // and also check if the condition is met. // 2. Create the workflow_execution record, including basic info and 6 maps(activityInfoMap, timerInfoMap, // childWorkflowInfoMap, signalInfoMap and signalRequestedIDs) // 3. Create history tasks // 4. Create workflow requests for requests deduplication // 5. Check if the condition of shard rangeID is met // The API returns error if there is any. If any of the condition is not met, returns WorkflowOperationConditionFailure InsertWorkflowExecutionWithTasks( ctx context.Context, requests *WorkflowRequestsWriteRequest, currentWorkflowRequest *CurrentWorkflowWriteRequest, execution *WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*HistoryMigrationTask, activeClusterSelectionPolicyRow *ActiveClusterSelectionPolicyRow, shardCondition *ShardCondition, ) error // UpdateWorkflowExecutionWithTasks is for updating a new workflow execution record. // Within a transaction, it also: // 1. If currentWorkflowRequest is not nil, Update the record of current_workflow with the same workflowID, based on CurrentWorkflowExecutionWriteMode, // and also check if the condition is met. // 2. Update mutatedExecution as workflow_execution record, including basic info and 6 maps(activityInfoMap, timerInfoMap, // childWorkflowInfoMap, signalInfoMap and signalRequestedIDs) // 3. if insertedExecution is not nil, then also insert a new workflow_execution record including basic info and add to 6 maps: // (activityInfoMap, timerInfoMap, childWorkflowInfoMap, signalInfoMap and signalRequestedIDs) // Also insert activeClusterSelectionPolicyRow if it is not nil corresponding to the new execution // 4. if resetExecution is not nil, then also update the workflow_execution record including basic info and reset/override 6 maps(activityInfoMap, timerInfoMap, // childWorkflowInfoMap, signalInfoMap and signalRequestedIDs // 5. Create history tasks // 6. Create workflow requests for requests deduplication // 7. Check if the condition of shard rangeID is met // The API returns error if there is any. If any of the condition is not met, returns WorkflowOperationConditionFailure UpdateWorkflowExecutionWithTasks( ctx context.Context, requests *WorkflowRequestsWriteRequest, currentWorkflowRequest *CurrentWorkflowWriteRequest, mutatedExecution *WorkflowExecutionRequest, insertedExecution *WorkflowExecutionRequest, activeClusterSelectionPolicyRow *ActiveClusterSelectionPolicyRow, resetExecution *WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*HistoryMigrationTask, shardCondition *ShardCondition, ) error // current_workflow table // Return the current_workflow row SelectCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID string) (*CurrentWorkflowRow, error) // Paging through all current_workflow rows in a shard SelectAllCurrentWorkflows(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.CurrentWorkflowExecution, []byte, error) // Delete the current_workflow row, if currentRunIDCondition is met DeleteCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID, currentRunIDCondition string) error // workflow_execution table // Return the workflow execution row SelectWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) (*WorkflowExecution, error) // Paging through all workflow execution rows in a shard SelectAllWorkflowExecutions(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.InternalListConcreteExecutionsEntity, []byte, error) // Return whether or not an execution is existing. IsWorkflowExecutionExists(ctx context.Context, shardID int, domainID, workflowID, runID string) (bool, error) // Delete the workflow execution row DeleteWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) error // transfer_task table // within a shard, paging through transfer tasks order by taskID(ASC), filtered by minTaskID(inclusive) and maxTaskID(exclusive) SelectTransferTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) // delete a single transfer task DeleteTransferTask(ctx context.Context, shardID int, taskID int64) error // delete a range of transfer tasks RangeDeleteTransferTasks(ctx context.Context, shardID int, inclusiveBeginTaskID, exclusiveEndTaskID int64) error // timer_task table // within a shard, paging through timer tasks order by taskID(ASC), filtered by visibilityTimestamp SelectTimerTasksOrderByVisibilityTime(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTime, exclusiveMaxTime time.Time) ([]*HistoryMigrationTask, []byte, error) // delete a single timer task DeleteTimerTask(ctx context.Context, shardID int, taskID int64, visibilityTimestamp time.Time) error // delete a range of timer tasks RangeDeleteTimerTasks(ctx context.Context, shardID int, inclusiveMinTime, exclusiveMaxTime time.Time) error // replication_task table // within a shard, paging through replication tasks order by taskID(ASC), filtered by minTaskID(inclusive) and maxTaskID(exclusive) SelectReplicationTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) // delete a single replication task DeleteReplicationTask(ctx context.Context, shardID int, taskID int64) error // delete a range of replication tasks RangeDeleteReplicationTasks(ctx context.Context, shardID int, exclusiveEndTaskID int64) error // insert replication task with shard condition check InsertReplicationTask(ctx context.Context, tasks []*HistoryMigrationTask, condition ShardCondition) error // cross_cluster_task table // delete a single transfer task DeleteCrossClusterTask(ctx context.Context, shardID int, targetCluster string, taskID int64) error // replication_dlq_task // insert a new replication task to DLQ InsertReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, task *HistoryMigrationTask) error // within a shard, for a sourceCluster, paging through replication tasks order by taskID(ASC), filtered by minTaskID(inclusive) and maxTaskID(exclusive) SelectReplicationDLQTasksOrderByTaskID(ctx context.Context, shardID int, sourceCluster string, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) // return the DLQ size SelectReplicationDLQTasksCount(ctx context.Context, shardID int, sourceCluster string) (int64, error) // delete a single replication DLQ task DeleteReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, taskID int64) error // delete a range of replication DLQ tasks RangeDeleteReplicationDLQTasks(ctx context.Context, shardID int, sourceCluster string, inclusiveBeginTaskID, exclusiveEndTaskID int64) error // select the active cluster selection policy SelectActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, wfID, rID string) (*ActiveClusterSelectionPolicyRow, error) // delete the active cluster selection policy row DeleteActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, workflowID, runID string) error } /*** * ConfigStoreCRUD is for storing dynamic configuration parameters * * Recommendation: one table * * Significant columns: * domain: partition key(row_type), range key(version) */ ConfigStoreCRUD interface { // InsertConfig insert a config entry with version. Return nosqlplugin.NewConditionFailure if the same version of the row_type is existing InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error // SelectLatestConfig returns the config entry of the row_type with the largest(latest) version value SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) } /*** * DomainAuditLogCRUD is for domain audit log storage system * * Recommendation: use one table * * Significant columns: * domain_audit_log: partition key(domainID, operationType), range key(createdTime DESC, eventID ASC) * * Note: This table is used for audit trail of domain changes, storing the before/after state * of domains along with metadata about who made the change and when. */ DomainAuditLogCRUD interface { // InsertDomainAuditLog inserts a new audit log entry for a domain operation // Return error if there is any failure InsertDomainAuditLog(ctx context.Context, row *DomainAuditLogRow) error // SelectDomainAuditLogs returns audit log entries for a domain and operation type // Returns paginated results ordered by created_time DESC, event_id ASC SelectDomainAuditLogs(ctx context.Context, filter *DomainAuditLogFilter) ([]*DomainAuditLogRow, []byte, error) } ) ================================================ FILE: common/persistence/nosql/nosqlplugin/interfaces_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go // // Generated by this command: // // mockgen -package nosqlplugin -source interfaces.go -destination interfaces_mock.go -self_package github.com/uber/cadence/common/persistence/nosql/nosqlplugin // // Package nosqlplugin is a generated GoMock package. package nosqlplugin import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" config "github.com/uber/cadence/common/config" log "github.com/uber/cadence/common/log" persistence "github.com/uber/cadence/common/persistence" ) // MockPlugin is a mock of Plugin interface. type MockPlugin struct { ctrl *gomock.Controller recorder *MockPluginMockRecorder isgomock struct{} } // MockPluginMockRecorder is the mock recorder for MockPlugin. type MockPluginMockRecorder struct { mock *MockPlugin } // NewMockPlugin creates a new mock instance. func NewMockPlugin(ctrl *gomock.Controller) *MockPlugin { mock := &MockPlugin{ctrl: ctrl} mock.recorder = &MockPluginMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPlugin) EXPECT() *MockPluginMockRecorder { return m.recorder } // CreateAdminDB mocks base method. func (m *MockPlugin) CreateAdminDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (AdminDB, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateAdminDB", cfg, logger, dc) ret0, _ := ret[0].(AdminDB) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateAdminDB indicates an expected call of CreateAdminDB. func (mr *MockPluginMockRecorder) CreateAdminDB(cfg, logger, dc any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAdminDB", reflect.TypeOf((*MockPlugin)(nil).CreateAdminDB), cfg, logger, dc) } // CreateDB mocks base method. func (m *MockPlugin) CreateDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (DB, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateDB", cfg, logger, dc) ret0, _ := ret[0].(DB) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateDB indicates an expected call of CreateDB. func (mr *MockPluginMockRecorder) CreateDB(cfg, logger, dc any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDB", reflect.TypeOf((*MockPlugin)(nil).CreateDB), cfg, logger, dc) } // MockAdminDB is a mock of AdminDB interface. type MockAdminDB struct { ctrl *gomock.Controller recorder *MockAdminDBMockRecorder isgomock struct{} } // MockAdminDBMockRecorder is the mock recorder for MockAdminDB. type MockAdminDBMockRecorder struct { mock *MockAdminDB } // NewMockAdminDB creates a new mock instance. func NewMockAdminDB(ctrl *gomock.Controller) *MockAdminDB { mock := &MockAdminDB{ctrl: ctrl} mock.recorder = &MockAdminDBMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockAdminDB) EXPECT() *MockAdminDBMockRecorder { return m.recorder } // SetupTestDatabase mocks base method. func (m *MockAdminDB) SetupTestDatabase(schemaBaseDir string, replicas int) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetupTestDatabase", schemaBaseDir, replicas) ret0, _ := ret[0].(error) return ret0 } // SetupTestDatabase indicates an expected call of SetupTestDatabase. func (mr *MockAdminDBMockRecorder) SetupTestDatabase(schemaBaseDir, replicas any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupTestDatabase", reflect.TypeOf((*MockAdminDB)(nil).SetupTestDatabase), schemaBaseDir, replicas) } // TeardownTestDatabase mocks base method. func (m *MockAdminDB) TeardownTestDatabase() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TeardownTestDatabase") ret0, _ := ret[0].(error) return ret0 } // TeardownTestDatabase indicates an expected call of TeardownTestDatabase. func (mr *MockAdminDBMockRecorder) TeardownTestDatabase() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TeardownTestDatabase", reflect.TypeOf((*MockAdminDB)(nil).TeardownTestDatabase)) } // MockDB is a mock of DB interface. type MockDB struct { ctrl *gomock.Controller recorder *MockDBMockRecorder isgomock struct{} } // MockDBMockRecorder is the mock recorder for MockDB. type MockDBMockRecorder struct { mock *MockDB } // NewMockDB creates a new mock instance. func NewMockDB(ctrl *gomock.Controller) *MockDB { mock := &MockDB{ctrl: ctrl} mock.recorder = &MockDBMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDB) EXPECT() *MockDBMockRecorder { return m.recorder } // Close mocks base method. func (m *MockDB) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockDBMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDB)(nil).Close)) } // DeleteActiveClusterSelectionPolicy mocks base method. func (m *MockDB) DeleteActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, workflowID, runID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteActiveClusterSelectionPolicy", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(error) return ret0 } // DeleteActiveClusterSelectionPolicy indicates an expected call of DeleteActiveClusterSelectionPolicy. func (mr *MockDBMockRecorder) DeleteActiveClusterSelectionPolicy(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteActiveClusterSelectionPolicy", reflect.TypeOf((*MockDB)(nil).DeleteActiveClusterSelectionPolicy), ctx, shardID, domainID, workflowID, runID) } // DeleteCrossClusterTask mocks base method. func (m *MockDB) DeleteCrossClusterTask(ctx context.Context, shardID int, targetCluster string, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteCrossClusterTask", ctx, shardID, targetCluster, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteCrossClusterTask indicates an expected call of DeleteCrossClusterTask. func (mr *MockDBMockRecorder) DeleteCrossClusterTask(ctx, shardID, targetCluster, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCrossClusterTask", reflect.TypeOf((*MockDB)(nil).DeleteCrossClusterTask), ctx, shardID, targetCluster, taskID) } // DeleteCurrentWorkflow mocks base method. func (m *MockDB) DeleteCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID, currentRunIDCondition string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteCurrentWorkflow", ctx, shardID, domainID, workflowID, currentRunIDCondition) ret0, _ := ret[0].(error) return ret0 } // DeleteCurrentWorkflow indicates an expected call of DeleteCurrentWorkflow. func (mr *MockDBMockRecorder) DeleteCurrentWorkflow(ctx, shardID, domainID, workflowID, currentRunIDCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentWorkflow", reflect.TypeOf((*MockDB)(nil).DeleteCurrentWorkflow), ctx, shardID, domainID, workflowID, currentRunIDCondition) } // DeleteDomain mocks base method. func (m *MockDB) DeleteDomain(ctx context.Context, domainID, domainName *string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteDomain", ctx, domainID, domainName) ret0, _ := ret[0].(error) return ret0 } // DeleteDomain indicates an expected call of DeleteDomain. func (mr *MockDBMockRecorder) DeleteDomain(ctx, domainID, domainName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDomain", reflect.TypeOf((*MockDB)(nil).DeleteDomain), ctx, domainID, domainName) } // DeleteFromHistoryTreeAndNode mocks base method. func (m *MockDB) DeleteFromHistoryTreeAndNode(ctx context.Context, treeFilter *HistoryTreeFilter, nodeFilters []*HistoryNodeFilter) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromHistoryTreeAndNode", ctx, treeFilter, nodeFilters) ret0, _ := ret[0].(error) return ret0 } // DeleteFromHistoryTreeAndNode indicates an expected call of DeleteFromHistoryTreeAndNode. func (mr *MockDBMockRecorder) DeleteFromHistoryTreeAndNode(ctx, treeFilter, nodeFilters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromHistoryTreeAndNode", reflect.TypeOf((*MockDB)(nil).DeleteFromHistoryTreeAndNode), ctx, treeFilter, nodeFilters) } // DeleteMessage mocks base method. func (m *MockDB) DeleteMessage(ctx context.Context, queueType persistence.QueueType, messageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessage", ctx, queueType, messageID) ret0, _ := ret[0].(error) return ret0 } // DeleteMessage indicates an expected call of DeleteMessage. func (mr *MockDBMockRecorder) DeleteMessage(ctx, queueType, messageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessage", reflect.TypeOf((*MockDB)(nil).DeleteMessage), ctx, queueType, messageID) } // DeleteMessagesBefore mocks base method. func (m *MockDB) DeleteMessagesBefore(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessagesBefore", ctx, queueType, exclusiveBeginMessageID) ret0, _ := ret[0].(error) return ret0 } // DeleteMessagesBefore indicates an expected call of DeleteMessagesBefore. func (mr *MockDBMockRecorder) DeleteMessagesBefore(ctx, queueType, exclusiveBeginMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessagesBefore", reflect.TypeOf((*MockDB)(nil).DeleteMessagesBefore), ctx, queueType, exclusiveBeginMessageID) } // DeleteMessagesInRange mocks base method. func (m *MockDB) DeleteMessagesInRange(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID, inclusiveEndMessageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessagesInRange", ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) ret0, _ := ret[0].(error) return ret0 } // DeleteMessagesInRange indicates an expected call of DeleteMessagesInRange. func (mr *MockDBMockRecorder) DeleteMessagesInRange(ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessagesInRange", reflect.TypeOf((*MockDB)(nil).DeleteMessagesInRange), ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) } // DeleteReplicationDLQTask mocks base method. func (m *MockDB) DeleteReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteReplicationDLQTask", ctx, shardID, sourceCluster, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteReplicationDLQTask indicates an expected call of DeleteReplicationDLQTask. func (mr *MockDBMockRecorder) DeleteReplicationDLQTask(ctx, shardID, sourceCluster, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationDLQTask", reflect.TypeOf((*MockDB)(nil).DeleteReplicationDLQTask), ctx, shardID, sourceCluster, taskID) } // DeleteReplicationTask mocks base method. func (m *MockDB) DeleteReplicationTask(ctx context.Context, shardID int, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteReplicationTask", ctx, shardID, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteReplicationTask indicates an expected call of DeleteReplicationTask. func (mr *MockDBMockRecorder) DeleteReplicationTask(ctx, shardID, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationTask", reflect.TypeOf((*MockDB)(nil).DeleteReplicationTask), ctx, shardID, taskID) } // DeleteTaskList mocks base method. func (m *MockDB) DeleteTaskList(ctx context.Context, filter *TaskListFilter, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTaskList", ctx, filter, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // DeleteTaskList indicates an expected call of DeleteTaskList. func (mr *MockDBMockRecorder) DeleteTaskList(ctx, filter, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTaskList", reflect.TypeOf((*MockDB)(nil).DeleteTaskList), ctx, filter, previousRangeID) } // DeleteTimerTask mocks base method. func (m *MockDB) DeleteTimerTask(ctx context.Context, shardID int, taskID int64, visibilityTimestamp time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTimerTask", ctx, shardID, taskID, visibilityTimestamp) ret0, _ := ret[0].(error) return ret0 } // DeleteTimerTask indicates an expected call of DeleteTimerTask. func (mr *MockDBMockRecorder) DeleteTimerTask(ctx, shardID, taskID, visibilityTimestamp any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTimerTask", reflect.TypeOf((*MockDB)(nil).DeleteTimerTask), ctx, shardID, taskID, visibilityTimestamp) } // DeleteTransferTask mocks base method. func (m *MockDB) DeleteTransferTask(ctx context.Context, shardID int, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTransferTask", ctx, shardID, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteTransferTask indicates an expected call of DeleteTransferTask. func (mr *MockDBMockRecorder) DeleteTransferTask(ctx, shardID, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTransferTask", reflect.TypeOf((*MockDB)(nil).DeleteTransferTask), ctx, shardID, taskID) } // DeleteVisibility mocks base method. func (m *MockDB) DeleteVisibility(ctx context.Context, domainID, workflowID, runID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteVisibility", ctx, domainID, workflowID, runID) ret0, _ := ret[0].(error) return ret0 } // DeleteVisibility indicates an expected call of DeleteVisibility. func (mr *MockDBMockRecorder) DeleteVisibility(ctx, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVisibility", reflect.TypeOf((*MockDB)(nil).DeleteVisibility), ctx, domainID, workflowID, runID) } // DeleteWorkflowExecution mocks base method. func (m *MockDB) DeleteWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteWorkflowExecution", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(error) return ret0 } // DeleteWorkflowExecution indicates an expected call of DeleteWorkflowExecution. func (mr *MockDBMockRecorder) DeleteWorkflowExecution(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWorkflowExecution", reflect.TypeOf((*MockDB)(nil).DeleteWorkflowExecution), ctx, shardID, domainID, workflowID, runID) } // GetQueueSize mocks base method. func (m *MockDB) GetQueueSize(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueSize", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetQueueSize indicates an expected call of GetQueueSize. func (mr *MockDBMockRecorder) GetQueueSize(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueSize", reflect.TypeOf((*MockDB)(nil).GetQueueSize), ctx, queueType) } // GetTasksCount mocks base method. func (m *MockDB) GetTasksCount(ctx context.Context, filter *TasksFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasksCount", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTasksCount indicates an expected call of GetTasksCount. func (mr *MockDBMockRecorder) GetTasksCount(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasksCount", reflect.TypeOf((*MockDB)(nil).GetTasksCount), ctx, filter) } // InsertConfig mocks base method. func (m *MockDB) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertConfig", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertConfig indicates an expected call of InsertConfig. func (mr *MockDBMockRecorder) InsertConfig(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertConfig", reflect.TypeOf((*MockDB)(nil).InsertConfig), ctx, row) } // InsertDomain mocks base method. func (m *MockDB) InsertDomain(ctx context.Context, row *DomainRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertDomain", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertDomain indicates an expected call of InsertDomain. func (mr *MockDBMockRecorder) InsertDomain(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertDomain", reflect.TypeOf((*MockDB)(nil).InsertDomain), ctx, row) } // InsertDomainAuditLog mocks base method. func (m *MockDB) InsertDomainAuditLog(ctx context.Context, row *DomainAuditLogRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertDomainAuditLog", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertDomainAuditLog indicates an expected call of InsertDomainAuditLog. func (mr *MockDBMockRecorder) InsertDomainAuditLog(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertDomainAuditLog", reflect.TypeOf((*MockDB)(nil).InsertDomainAuditLog), ctx, row) } // InsertIntoHistoryTreeAndNode mocks base method. func (m *MockDB) InsertIntoHistoryTreeAndNode(ctx context.Context, treeRow *HistoryTreeRow, nodeRow *HistoryNodeRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoHistoryTreeAndNode", ctx, treeRow, nodeRow) ret0, _ := ret[0].(error) return ret0 } // InsertIntoHistoryTreeAndNode indicates an expected call of InsertIntoHistoryTreeAndNode. func (mr *MockDBMockRecorder) InsertIntoHistoryTreeAndNode(ctx, treeRow, nodeRow any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoHistoryTreeAndNode", reflect.TypeOf((*MockDB)(nil).InsertIntoHistoryTreeAndNode), ctx, treeRow, nodeRow) } // InsertIntoQueue mocks base method. func (m *MockDB) InsertIntoQueue(ctx context.Context, row *QueueMessageRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoQueue", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertIntoQueue indicates an expected call of InsertIntoQueue. func (mr *MockDBMockRecorder) InsertIntoQueue(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoQueue", reflect.TypeOf((*MockDB)(nil).InsertIntoQueue), ctx, row) } // InsertQueueMetadata mocks base method. func (m *MockDB) InsertQueueMetadata(ctx context.Context, row QueueMetadataRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertQueueMetadata", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertQueueMetadata indicates an expected call of InsertQueueMetadata. func (mr *MockDBMockRecorder) InsertQueueMetadata(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertQueueMetadata", reflect.TypeOf((*MockDB)(nil).InsertQueueMetadata), ctx, row) } // InsertReplicationDLQTask mocks base method. func (m *MockDB) InsertReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, task *HistoryMigrationTask) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertReplicationDLQTask", ctx, shardID, sourceCluster, task) ret0, _ := ret[0].(error) return ret0 } // InsertReplicationDLQTask indicates an expected call of InsertReplicationDLQTask. func (mr *MockDBMockRecorder) InsertReplicationDLQTask(ctx, shardID, sourceCluster, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertReplicationDLQTask", reflect.TypeOf((*MockDB)(nil).InsertReplicationDLQTask), ctx, shardID, sourceCluster, task) } // InsertReplicationTask mocks base method. func (m *MockDB) InsertReplicationTask(ctx context.Context, tasks []*HistoryMigrationTask, condition ShardCondition) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertReplicationTask", ctx, tasks, condition) ret0, _ := ret[0].(error) return ret0 } // InsertReplicationTask indicates an expected call of InsertReplicationTask. func (mr *MockDBMockRecorder) InsertReplicationTask(ctx, tasks, condition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertReplicationTask", reflect.TypeOf((*MockDB)(nil).InsertReplicationTask), ctx, tasks, condition) } // InsertShard mocks base method. func (m *MockDB) InsertShard(ctx context.Context, row *ShardRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertShard", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertShard indicates an expected call of InsertShard. func (mr *MockDBMockRecorder) InsertShard(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertShard", reflect.TypeOf((*MockDB)(nil).InsertShard), ctx, row) } // InsertTaskList mocks base method. func (m *MockDB) InsertTaskList(ctx context.Context, row *TaskListRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertTaskList", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertTaskList indicates an expected call of InsertTaskList. func (mr *MockDBMockRecorder) InsertTaskList(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertTaskList", reflect.TypeOf((*MockDB)(nil).InsertTaskList), ctx, row) } // InsertTasks mocks base method. func (m *MockDB) InsertTasks(ctx context.Context, tasksToInsert []*TaskRowForInsert, tasklistCondition *TaskListRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertTasks", ctx, tasksToInsert, tasklistCondition) ret0, _ := ret[0].(error) return ret0 } // InsertTasks indicates an expected call of InsertTasks. func (mr *MockDBMockRecorder) InsertTasks(ctx, tasksToInsert, tasklistCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertTasks", reflect.TypeOf((*MockDB)(nil).InsertTasks), ctx, tasksToInsert, tasklistCondition) } // InsertVisibility mocks base method. func (m *MockDB) InsertVisibility(ctx context.Context, ttlSeconds int64, row *VisibilityRowForInsert) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertVisibility", ctx, ttlSeconds, row) ret0, _ := ret[0].(error) return ret0 } // InsertVisibility indicates an expected call of InsertVisibility. func (mr *MockDBMockRecorder) InsertVisibility(ctx, ttlSeconds, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertVisibility", reflect.TypeOf((*MockDB)(nil).InsertVisibility), ctx, ttlSeconds, row) } // InsertWorkflowExecutionWithTasks mocks base method. func (m *MockDB) InsertWorkflowExecutionWithTasks(ctx context.Context, requests *WorkflowRequestsWriteRequest, currentWorkflowRequest *CurrentWorkflowWriteRequest, execution *WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*HistoryMigrationTask, activeClusterSelectionPolicyRow *ActiveClusterSelectionPolicyRow, shardCondition *ShardCondition) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertWorkflowExecutionWithTasks", ctx, requests, currentWorkflowRequest, execution, tasksByCategory, activeClusterSelectionPolicyRow, shardCondition) ret0, _ := ret[0].(error) return ret0 } // InsertWorkflowExecutionWithTasks indicates an expected call of InsertWorkflowExecutionWithTasks. func (mr *MockDBMockRecorder) InsertWorkflowExecutionWithTasks(ctx, requests, currentWorkflowRequest, execution, tasksByCategory, activeClusterSelectionPolicyRow, shardCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkflowExecutionWithTasks", reflect.TypeOf((*MockDB)(nil).InsertWorkflowExecutionWithTasks), ctx, requests, currentWorkflowRequest, execution, tasksByCategory, activeClusterSelectionPolicyRow, shardCondition) } // IsDBUnavailableError mocks base method. func (m *MockDB) IsDBUnavailableError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsDBUnavailableError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsDBUnavailableError indicates an expected call of IsDBUnavailableError. func (mr *MockDBMockRecorder) IsDBUnavailableError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDBUnavailableError", reflect.TypeOf((*MockDB)(nil).IsDBUnavailableError), arg0) } // IsNotFoundError mocks base method. func (m *MockDB) IsNotFoundError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsNotFoundError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsNotFoundError indicates an expected call of IsNotFoundError. func (mr *MockDBMockRecorder) IsNotFoundError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNotFoundError", reflect.TypeOf((*MockDB)(nil).IsNotFoundError), arg0) } // IsThrottlingError mocks base method. func (m *MockDB) IsThrottlingError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsThrottlingError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsThrottlingError indicates an expected call of IsThrottlingError. func (mr *MockDBMockRecorder) IsThrottlingError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsThrottlingError", reflect.TypeOf((*MockDB)(nil).IsThrottlingError), arg0) } // IsTimeoutError mocks base method. func (m *MockDB) IsTimeoutError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsTimeoutError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsTimeoutError indicates an expected call of IsTimeoutError. func (mr *MockDBMockRecorder) IsTimeoutError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsTimeoutError", reflect.TypeOf((*MockDB)(nil).IsTimeoutError), arg0) } // IsWorkflowExecutionExists mocks base method. func (m *MockDB) IsWorkflowExecutionExists(ctx context.Context, shardID int, domainID, workflowID, runID string) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsWorkflowExecutionExists", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsWorkflowExecutionExists indicates an expected call of IsWorkflowExecutionExists. func (mr *MockDBMockRecorder) IsWorkflowExecutionExists(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWorkflowExecutionExists", reflect.TypeOf((*MockDB)(nil).IsWorkflowExecutionExists), ctx, shardID, domainID, workflowID, runID) } // ListTaskList mocks base method. func (m *MockDB) ListTaskList(ctx context.Context, pageSize int, nextPageToken []byte) (*ListTaskListResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTaskList", ctx, pageSize, nextPageToken) ret0, _ := ret[0].(*ListTaskListResult) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTaskList indicates an expected call of ListTaskList. func (mr *MockDBMockRecorder) ListTaskList(ctx, pageSize, nextPageToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTaskList", reflect.TypeOf((*MockDB)(nil).ListTaskList), ctx, pageSize, nextPageToken) } // PluginName mocks base method. func (m *MockDB) PluginName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PluginName") ret0, _ := ret[0].(string) return ret0 } // PluginName indicates an expected call of PluginName. func (mr *MockDBMockRecorder) PluginName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginName", reflect.TypeOf((*MockDB)(nil).PluginName)) } // RangeDeleteReplicationDLQTasks mocks base method. func (m *MockDB) RangeDeleteReplicationDLQTasks(ctx context.Context, shardID int, sourceCluster string, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteReplicationDLQTasks", ctx, shardID, sourceCluster, inclusiveBeginTaskID, exclusiveEndTaskID) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteReplicationDLQTasks indicates an expected call of RangeDeleteReplicationDLQTasks. func (mr *MockDBMockRecorder) RangeDeleteReplicationDLQTasks(ctx, shardID, sourceCluster, inclusiveBeginTaskID, exclusiveEndTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteReplicationDLQTasks", reflect.TypeOf((*MockDB)(nil).RangeDeleteReplicationDLQTasks), ctx, shardID, sourceCluster, inclusiveBeginTaskID, exclusiveEndTaskID) } // RangeDeleteReplicationTasks mocks base method. func (m *MockDB) RangeDeleteReplicationTasks(ctx context.Context, shardID int, exclusiveEndTaskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteReplicationTasks", ctx, shardID, exclusiveEndTaskID) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteReplicationTasks indicates an expected call of RangeDeleteReplicationTasks. func (mr *MockDBMockRecorder) RangeDeleteReplicationTasks(ctx, shardID, exclusiveEndTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteReplicationTasks", reflect.TypeOf((*MockDB)(nil).RangeDeleteReplicationTasks), ctx, shardID, exclusiveEndTaskID) } // RangeDeleteTasks mocks base method. func (m *MockDB) RangeDeleteTasks(ctx context.Context, filter *TasksFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteTasks", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteTasks indicates an expected call of RangeDeleteTasks. func (mr *MockDBMockRecorder) RangeDeleteTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteTasks", reflect.TypeOf((*MockDB)(nil).RangeDeleteTasks), ctx, filter) } // RangeDeleteTimerTasks mocks base method. func (m *MockDB) RangeDeleteTimerTasks(ctx context.Context, shardID int, inclusiveMinTime, exclusiveMaxTime time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteTimerTasks", ctx, shardID, inclusiveMinTime, exclusiveMaxTime) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteTimerTasks indicates an expected call of RangeDeleteTimerTasks. func (mr *MockDBMockRecorder) RangeDeleteTimerTasks(ctx, shardID, inclusiveMinTime, exclusiveMaxTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteTimerTasks", reflect.TypeOf((*MockDB)(nil).RangeDeleteTimerTasks), ctx, shardID, inclusiveMinTime, exclusiveMaxTime) } // RangeDeleteTransferTasks mocks base method. func (m *MockDB) RangeDeleteTransferTasks(ctx context.Context, shardID int, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteTransferTasks", ctx, shardID, inclusiveBeginTaskID, exclusiveEndTaskID) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteTransferTasks indicates an expected call of RangeDeleteTransferTasks. func (mr *MockDBMockRecorder) RangeDeleteTransferTasks(ctx, shardID, inclusiveBeginTaskID, exclusiveEndTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteTransferTasks", reflect.TypeOf((*MockDB)(nil).RangeDeleteTransferTasks), ctx, shardID, inclusiveBeginTaskID, exclusiveEndTaskID) } // SelectActiveClusterSelectionPolicy mocks base method. func (m *MockDB) SelectActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, wfID, rID string) (*ActiveClusterSelectionPolicyRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectActiveClusterSelectionPolicy", ctx, shardID, domainID, wfID, rID) ret0, _ := ret[0].(*ActiveClusterSelectionPolicyRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectActiveClusterSelectionPolicy indicates an expected call of SelectActiveClusterSelectionPolicy. func (mr *MockDBMockRecorder) SelectActiveClusterSelectionPolicy(ctx, shardID, domainID, wfID, rID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectActiveClusterSelectionPolicy", reflect.TypeOf((*MockDB)(nil).SelectActiveClusterSelectionPolicy), ctx, shardID, domainID, wfID, rID) } // SelectAllCurrentWorkflows mocks base method. func (m *MockDB) SelectAllCurrentWorkflows(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.CurrentWorkflowExecution, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllCurrentWorkflows", ctx, shardID, pageToken, pageSize) ret0, _ := ret[0].([]*persistence.CurrentWorkflowExecution) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllCurrentWorkflows indicates an expected call of SelectAllCurrentWorkflows. func (mr *MockDBMockRecorder) SelectAllCurrentWorkflows(ctx, shardID, pageToken, pageSize any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllCurrentWorkflows", reflect.TypeOf((*MockDB)(nil).SelectAllCurrentWorkflows), ctx, shardID, pageToken, pageSize) } // SelectAllDomains mocks base method. func (m *MockDB) SelectAllDomains(ctx context.Context, pageSize int, pageToken []byte) ([]*DomainRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllDomains", ctx, pageSize, pageToken) ret0, _ := ret[0].([]*DomainRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllDomains indicates an expected call of SelectAllDomains. func (mr *MockDBMockRecorder) SelectAllDomains(ctx, pageSize, pageToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllDomains", reflect.TypeOf((*MockDB)(nil).SelectAllDomains), ctx, pageSize, pageToken) } // SelectAllHistoryTrees mocks base method. func (m *MockDB) SelectAllHistoryTrees(ctx context.Context, nextPageToken []byte, pageSize int) ([]*HistoryTreeRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllHistoryTrees", ctx, nextPageToken, pageSize) ret0, _ := ret[0].([]*HistoryTreeRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllHistoryTrees indicates an expected call of SelectAllHistoryTrees. func (mr *MockDBMockRecorder) SelectAllHistoryTrees(ctx, nextPageToken, pageSize any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllHistoryTrees", reflect.TypeOf((*MockDB)(nil).SelectAllHistoryTrees), ctx, nextPageToken, pageSize) } // SelectAllWorkflowExecutions mocks base method. func (m *MockDB) SelectAllWorkflowExecutions(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.InternalListConcreteExecutionsEntity, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllWorkflowExecutions", ctx, shardID, pageToken, pageSize) ret0, _ := ret[0].([]*persistence.InternalListConcreteExecutionsEntity) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllWorkflowExecutions indicates an expected call of SelectAllWorkflowExecutions. func (mr *MockDBMockRecorder) SelectAllWorkflowExecutions(ctx, shardID, pageToken, pageSize any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllWorkflowExecutions", reflect.TypeOf((*MockDB)(nil).SelectAllWorkflowExecutions), ctx, shardID, pageToken, pageSize) } // SelectCurrentWorkflow mocks base method. func (m *MockDB) SelectCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID string) (*CurrentWorkflowRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectCurrentWorkflow", ctx, shardID, domainID, workflowID) ret0, _ := ret[0].(*CurrentWorkflowRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectCurrentWorkflow indicates an expected call of SelectCurrentWorkflow. func (mr *MockDBMockRecorder) SelectCurrentWorkflow(ctx, shardID, domainID, workflowID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectCurrentWorkflow", reflect.TypeOf((*MockDB)(nil).SelectCurrentWorkflow), ctx, shardID, domainID, workflowID) } // SelectDomain mocks base method. func (m *MockDB) SelectDomain(ctx context.Context, domainID, domainName *string) (*DomainRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectDomain", ctx, domainID, domainName) ret0, _ := ret[0].(*DomainRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectDomain indicates an expected call of SelectDomain. func (mr *MockDBMockRecorder) SelectDomain(ctx, domainID, domainName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDomain", reflect.TypeOf((*MockDB)(nil).SelectDomain), ctx, domainID, domainName) } // SelectDomainAuditLogs mocks base method. func (m *MockDB) SelectDomainAuditLogs(ctx context.Context, filter *DomainAuditLogFilter) ([]*DomainAuditLogRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectDomainAuditLogs", ctx, filter) ret0, _ := ret[0].([]*DomainAuditLogRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectDomainAuditLogs indicates an expected call of SelectDomainAuditLogs. func (mr *MockDBMockRecorder) SelectDomainAuditLogs(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDomainAuditLogs", reflect.TypeOf((*MockDB)(nil).SelectDomainAuditLogs), ctx, filter) } // SelectDomainMetadata mocks base method. func (m *MockDB) SelectDomainMetadata(ctx context.Context) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectDomainMetadata", ctx) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectDomainMetadata indicates an expected call of SelectDomainMetadata. func (mr *MockDBMockRecorder) SelectDomainMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDomainMetadata", reflect.TypeOf((*MockDB)(nil).SelectDomainMetadata), ctx) } // SelectFromHistoryNode mocks base method. func (m *MockDB) SelectFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) ([]*HistoryNodeRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryNode", ctx, filter) ret0, _ := ret[0].([]*HistoryNodeRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectFromHistoryNode indicates an expected call of SelectFromHistoryNode. func (mr *MockDBMockRecorder) SelectFromHistoryNode(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryNode", reflect.TypeOf((*MockDB)(nil).SelectFromHistoryNode), ctx, filter) } // SelectFromHistoryTree mocks base method. func (m *MockDB) SelectFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) ([]*HistoryTreeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryTree", ctx, filter) ret0, _ := ret[0].([]*HistoryTreeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromHistoryTree indicates an expected call of SelectFromHistoryTree. func (mr *MockDBMockRecorder) SelectFromHistoryTree(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryTree", reflect.TypeOf((*MockDB)(nil).SelectFromHistoryTree), ctx, filter) } // SelectLastEnqueuedMessageID mocks base method. func (m *MockDB) SelectLastEnqueuedMessageID(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectLastEnqueuedMessageID", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectLastEnqueuedMessageID indicates an expected call of SelectLastEnqueuedMessageID. func (mr *MockDBMockRecorder) SelectLastEnqueuedMessageID(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectLastEnqueuedMessageID", reflect.TypeOf((*MockDB)(nil).SelectLastEnqueuedMessageID), ctx, queueType) } // SelectLatestConfig mocks base method. func (m *MockDB) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectLatestConfig", ctx, rowType) ret0, _ := ret[0].(*persistence.InternalConfigStoreEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectLatestConfig indicates an expected call of SelectLatestConfig. func (mr *MockDBMockRecorder) SelectLatestConfig(ctx, rowType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectLatestConfig", reflect.TypeOf((*MockDB)(nil).SelectLatestConfig), ctx, rowType) } // SelectMessagesBetween mocks base method. func (m *MockDB) SelectMessagesBetween(ctx context.Context, request SelectMessagesBetweenRequest) (*SelectMessagesBetweenResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectMessagesBetween", ctx, request) ret0, _ := ret[0].(*SelectMessagesBetweenResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectMessagesBetween indicates an expected call of SelectMessagesBetween. func (mr *MockDBMockRecorder) SelectMessagesBetween(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectMessagesBetween", reflect.TypeOf((*MockDB)(nil).SelectMessagesBetween), ctx, request) } // SelectMessagesFrom mocks base method. func (m *MockDB) SelectMessagesFrom(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, maxRows int) ([]*QueueMessageRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectMessagesFrom", ctx, queueType, exclusiveBeginMessageID, maxRows) ret0, _ := ret[0].([]*QueueMessageRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectMessagesFrom indicates an expected call of SelectMessagesFrom. func (mr *MockDBMockRecorder) SelectMessagesFrom(ctx, queueType, exclusiveBeginMessageID, maxRows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectMessagesFrom", reflect.TypeOf((*MockDB)(nil).SelectMessagesFrom), ctx, queueType, exclusiveBeginMessageID, maxRows) } // SelectOneClosedWorkflow mocks base method. func (m *MockDB) SelectOneClosedWorkflow(ctx context.Context, domainID, workflowID, runID string) (*VisibilityRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectOneClosedWorkflow", ctx, domainID, workflowID, runID) ret0, _ := ret[0].(*VisibilityRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectOneClosedWorkflow indicates an expected call of SelectOneClosedWorkflow. func (mr *MockDBMockRecorder) SelectOneClosedWorkflow(ctx, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectOneClosedWorkflow", reflect.TypeOf((*MockDB)(nil).SelectOneClosedWorkflow), ctx, domainID, workflowID, runID) } // SelectQueueMetadata mocks base method. func (m *MockDB) SelectQueueMetadata(ctx context.Context, queueType persistence.QueueType) (*QueueMetadataRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectQueueMetadata", ctx, queueType) ret0, _ := ret[0].(*QueueMetadataRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectQueueMetadata indicates an expected call of SelectQueueMetadata. func (mr *MockDBMockRecorder) SelectQueueMetadata(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectQueueMetadata", reflect.TypeOf((*MockDB)(nil).SelectQueueMetadata), ctx, queueType) } // SelectReplicationDLQTasksCount mocks base method. func (m *MockDB) SelectReplicationDLQTasksCount(ctx context.Context, shardID int, sourceCluster string) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectReplicationDLQTasksCount", ctx, shardID, sourceCluster) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectReplicationDLQTasksCount indicates an expected call of SelectReplicationDLQTasksCount. func (mr *MockDBMockRecorder) SelectReplicationDLQTasksCount(ctx, shardID, sourceCluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectReplicationDLQTasksCount", reflect.TypeOf((*MockDB)(nil).SelectReplicationDLQTasksCount), ctx, shardID, sourceCluster) } // SelectReplicationDLQTasksOrderByTaskID mocks base method. func (m *MockDB) SelectReplicationDLQTasksOrderByTaskID(ctx context.Context, shardID int, sourceCluster string, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectReplicationDLQTasksOrderByTaskID", ctx, shardID, sourceCluster, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectReplicationDLQTasksOrderByTaskID indicates an expected call of SelectReplicationDLQTasksOrderByTaskID. func (mr *MockDBMockRecorder) SelectReplicationDLQTasksOrderByTaskID(ctx, shardID, sourceCluster, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectReplicationDLQTasksOrderByTaskID", reflect.TypeOf((*MockDB)(nil).SelectReplicationDLQTasksOrderByTaskID), ctx, shardID, sourceCluster, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) } // SelectReplicationTasksOrderByTaskID mocks base method. func (m *MockDB) SelectReplicationTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectReplicationTasksOrderByTaskID", ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectReplicationTasksOrderByTaskID indicates an expected call of SelectReplicationTasksOrderByTaskID. func (mr *MockDBMockRecorder) SelectReplicationTasksOrderByTaskID(ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectReplicationTasksOrderByTaskID", reflect.TypeOf((*MockDB)(nil).SelectReplicationTasksOrderByTaskID), ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) } // SelectShard mocks base method. func (m *MockDB) SelectShard(ctx context.Context, shardID int, currentClusterName string) (int64, *ShardRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectShard", ctx, shardID, currentClusterName) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(*ShardRow) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectShard indicates an expected call of SelectShard. func (mr *MockDBMockRecorder) SelectShard(ctx, shardID, currentClusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectShard", reflect.TypeOf((*MockDB)(nil).SelectShard), ctx, shardID, currentClusterName) } // SelectTaskList mocks base method. func (m *MockDB) SelectTaskList(ctx context.Context, filter *TaskListFilter) (*TaskListRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTaskList", ctx, filter) ret0, _ := ret[0].(*TaskListRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectTaskList indicates an expected call of SelectTaskList. func (mr *MockDBMockRecorder) SelectTaskList(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTaskList", reflect.TypeOf((*MockDB)(nil).SelectTaskList), ctx, filter) } // SelectTasks mocks base method. func (m *MockDB) SelectTasks(ctx context.Context, filter *TasksFilter) ([]*TaskRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTasks", ctx, filter) ret0, _ := ret[0].([]*TaskRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectTasks indicates an expected call of SelectTasks. func (mr *MockDBMockRecorder) SelectTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTasks", reflect.TypeOf((*MockDB)(nil).SelectTasks), ctx, filter) } // SelectTimerTasksOrderByVisibilityTime mocks base method. func (m *MockDB) SelectTimerTasksOrderByVisibilityTime(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTime, exclusiveMaxTime time.Time) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTimerTasksOrderByVisibilityTime", ctx, shardID, pageSize, pageToken, inclusiveMinTime, exclusiveMaxTime) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectTimerTasksOrderByVisibilityTime indicates an expected call of SelectTimerTasksOrderByVisibilityTime. func (mr *MockDBMockRecorder) SelectTimerTasksOrderByVisibilityTime(ctx, shardID, pageSize, pageToken, inclusiveMinTime, exclusiveMaxTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTimerTasksOrderByVisibilityTime", reflect.TypeOf((*MockDB)(nil).SelectTimerTasksOrderByVisibilityTime), ctx, shardID, pageSize, pageToken, inclusiveMinTime, exclusiveMaxTime) } // SelectTransferTasksOrderByTaskID mocks base method. func (m *MockDB) SelectTransferTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTransferTasksOrderByTaskID", ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectTransferTasksOrderByTaskID indicates an expected call of SelectTransferTasksOrderByTaskID. func (mr *MockDBMockRecorder) SelectTransferTasksOrderByTaskID(ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTransferTasksOrderByTaskID", reflect.TypeOf((*MockDB)(nil).SelectTransferTasksOrderByTaskID), ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) } // SelectVisibility mocks base method. func (m *MockDB) SelectVisibility(ctx context.Context, filter *VisibilityFilter) (*SelectVisibilityResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectVisibility", ctx, filter) ret0, _ := ret[0].(*SelectVisibilityResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectVisibility indicates an expected call of SelectVisibility. func (mr *MockDBMockRecorder) SelectVisibility(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectVisibility", reflect.TypeOf((*MockDB)(nil).SelectVisibility), ctx, filter) } // SelectWorkflowExecution mocks base method. func (m *MockDB) SelectWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) (*WorkflowExecution, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectWorkflowExecution", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(*WorkflowExecution) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectWorkflowExecution indicates an expected call of SelectWorkflowExecution. func (mr *MockDBMockRecorder) SelectWorkflowExecution(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectWorkflowExecution", reflect.TypeOf((*MockDB)(nil).SelectWorkflowExecution), ctx, shardID, domainID, workflowID, runID) } // UpdateDomain mocks base method. func (m *MockDB) UpdateDomain(ctx context.Context, row *DomainRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomain", ctx, row) ret0, _ := ret[0].(error) return ret0 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MockDBMockRecorder) UpdateDomain(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MockDB)(nil).UpdateDomain), ctx, row) } // UpdateQueueMetadataCas mocks base method. func (m *MockDB) UpdateQueueMetadataCas(ctx context.Context, row QueueMetadataRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateQueueMetadataCas", ctx, row) ret0, _ := ret[0].(error) return ret0 } // UpdateQueueMetadataCas indicates an expected call of UpdateQueueMetadataCas. func (mr *MockDBMockRecorder) UpdateQueueMetadataCas(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateQueueMetadataCas", reflect.TypeOf((*MockDB)(nil).UpdateQueueMetadataCas), ctx, row) } // UpdateRangeID mocks base method. func (m *MockDB) UpdateRangeID(ctx context.Context, shardID int, rangeID, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateRangeID", ctx, shardID, rangeID, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateRangeID indicates an expected call of UpdateRangeID. func (mr *MockDBMockRecorder) UpdateRangeID(ctx, shardID, rangeID, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRangeID", reflect.TypeOf((*MockDB)(nil).UpdateRangeID), ctx, shardID, rangeID, previousRangeID) } // UpdateShard mocks base method. func (m *MockDB) UpdateShard(ctx context.Context, row *ShardRow, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateShard", ctx, row, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateShard indicates an expected call of UpdateShard. func (mr *MockDBMockRecorder) UpdateShard(ctx, row, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateShard", reflect.TypeOf((*MockDB)(nil).UpdateShard), ctx, row, previousRangeID) } // UpdateTaskList mocks base method. func (m *MockDB) UpdateTaskList(ctx context.Context, row *TaskListRow, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskList", ctx, row, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateTaskList indicates an expected call of UpdateTaskList. func (mr *MockDBMockRecorder) UpdateTaskList(ctx, row, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskList", reflect.TypeOf((*MockDB)(nil).UpdateTaskList), ctx, row, previousRangeID) } // UpdateTaskListWithTTL mocks base method. func (m *MockDB) UpdateTaskListWithTTL(ctx context.Context, ttlSeconds int64, row *TaskListRow, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskListWithTTL", ctx, ttlSeconds, row, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateTaskListWithTTL indicates an expected call of UpdateTaskListWithTTL. func (mr *MockDBMockRecorder) UpdateTaskListWithTTL(ctx, ttlSeconds, row, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListWithTTL", reflect.TypeOf((*MockDB)(nil).UpdateTaskListWithTTL), ctx, ttlSeconds, row, previousRangeID) } // UpdateVisibility mocks base method. func (m *MockDB) UpdateVisibility(ctx context.Context, ttlSeconds int64, row *VisibilityRowForUpdate) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateVisibility", ctx, ttlSeconds, row) ret0, _ := ret[0].(error) return ret0 } // UpdateVisibility indicates an expected call of UpdateVisibility. func (mr *MockDBMockRecorder) UpdateVisibility(ctx, ttlSeconds, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateVisibility", reflect.TypeOf((*MockDB)(nil).UpdateVisibility), ctx, ttlSeconds, row) } // UpdateWorkflowExecutionWithTasks mocks base method. func (m *MockDB) UpdateWorkflowExecutionWithTasks(ctx context.Context, requests *WorkflowRequestsWriteRequest, currentWorkflowRequest *CurrentWorkflowWriteRequest, mutatedExecution, insertedExecution *WorkflowExecutionRequest, activeClusterSelectionPolicyRow *ActiveClusterSelectionPolicyRow, resetExecution *WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*HistoryMigrationTask, shardCondition *ShardCondition) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecutionWithTasks", ctx, requests, currentWorkflowRequest, mutatedExecution, insertedExecution, activeClusterSelectionPolicyRow, resetExecution, tasksByCategory, shardCondition) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowExecutionWithTasks indicates an expected call of UpdateWorkflowExecutionWithTasks. func (mr *MockDBMockRecorder) UpdateWorkflowExecutionWithTasks(ctx, requests, currentWorkflowRequest, mutatedExecution, insertedExecution, activeClusterSelectionPolicyRow, resetExecution, tasksByCategory, shardCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecutionWithTasks", reflect.TypeOf((*MockDB)(nil).UpdateWorkflowExecutionWithTasks), ctx, requests, currentWorkflowRequest, mutatedExecution, insertedExecution, activeClusterSelectionPolicyRow, resetExecution, tasksByCategory, shardCondition) } // MocktableCRUD is a mock of tableCRUD interface. type MocktableCRUD struct { ctrl *gomock.Controller recorder *MocktableCRUDMockRecorder isgomock struct{} } // MocktableCRUDMockRecorder is the mock recorder for MocktableCRUD. type MocktableCRUDMockRecorder struct { mock *MocktableCRUD } // NewMocktableCRUD creates a new mock instance. func NewMocktableCRUD(ctrl *gomock.Controller) *MocktableCRUD { mock := &MocktableCRUD{ctrl: ctrl} mock.recorder = &MocktableCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MocktableCRUD) EXPECT() *MocktableCRUDMockRecorder { return m.recorder } // DeleteActiveClusterSelectionPolicy mocks base method. func (m *MocktableCRUD) DeleteActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, workflowID, runID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteActiveClusterSelectionPolicy", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(error) return ret0 } // DeleteActiveClusterSelectionPolicy indicates an expected call of DeleteActiveClusterSelectionPolicy. func (mr *MocktableCRUDMockRecorder) DeleteActiveClusterSelectionPolicy(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteActiveClusterSelectionPolicy", reflect.TypeOf((*MocktableCRUD)(nil).DeleteActiveClusterSelectionPolicy), ctx, shardID, domainID, workflowID, runID) } // DeleteCrossClusterTask mocks base method. func (m *MocktableCRUD) DeleteCrossClusterTask(ctx context.Context, shardID int, targetCluster string, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteCrossClusterTask", ctx, shardID, targetCluster, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteCrossClusterTask indicates an expected call of DeleteCrossClusterTask. func (mr *MocktableCRUDMockRecorder) DeleteCrossClusterTask(ctx, shardID, targetCluster, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCrossClusterTask", reflect.TypeOf((*MocktableCRUD)(nil).DeleteCrossClusterTask), ctx, shardID, targetCluster, taskID) } // DeleteCurrentWorkflow mocks base method. func (m *MocktableCRUD) DeleteCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID, currentRunIDCondition string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteCurrentWorkflow", ctx, shardID, domainID, workflowID, currentRunIDCondition) ret0, _ := ret[0].(error) return ret0 } // DeleteCurrentWorkflow indicates an expected call of DeleteCurrentWorkflow. func (mr *MocktableCRUDMockRecorder) DeleteCurrentWorkflow(ctx, shardID, domainID, workflowID, currentRunIDCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentWorkflow", reflect.TypeOf((*MocktableCRUD)(nil).DeleteCurrentWorkflow), ctx, shardID, domainID, workflowID, currentRunIDCondition) } // DeleteDomain mocks base method. func (m *MocktableCRUD) DeleteDomain(ctx context.Context, domainID, domainName *string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteDomain", ctx, domainID, domainName) ret0, _ := ret[0].(error) return ret0 } // DeleteDomain indicates an expected call of DeleteDomain. func (mr *MocktableCRUDMockRecorder) DeleteDomain(ctx, domainID, domainName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDomain", reflect.TypeOf((*MocktableCRUD)(nil).DeleteDomain), ctx, domainID, domainName) } // DeleteFromHistoryTreeAndNode mocks base method. func (m *MocktableCRUD) DeleteFromHistoryTreeAndNode(ctx context.Context, treeFilter *HistoryTreeFilter, nodeFilters []*HistoryNodeFilter) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromHistoryTreeAndNode", ctx, treeFilter, nodeFilters) ret0, _ := ret[0].(error) return ret0 } // DeleteFromHistoryTreeAndNode indicates an expected call of DeleteFromHistoryTreeAndNode. func (mr *MocktableCRUDMockRecorder) DeleteFromHistoryTreeAndNode(ctx, treeFilter, nodeFilters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromHistoryTreeAndNode", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromHistoryTreeAndNode), ctx, treeFilter, nodeFilters) } // DeleteMessage mocks base method. func (m *MocktableCRUD) DeleteMessage(ctx context.Context, queueType persistence.QueueType, messageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessage", ctx, queueType, messageID) ret0, _ := ret[0].(error) return ret0 } // DeleteMessage indicates an expected call of DeleteMessage. func (mr *MocktableCRUDMockRecorder) DeleteMessage(ctx, queueType, messageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessage", reflect.TypeOf((*MocktableCRUD)(nil).DeleteMessage), ctx, queueType, messageID) } // DeleteMessagesBefore mocks base method. func (m *MocktableCRUD) DeleteMessagesBefore(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessagesBefore", ctx, queueType, exclusiveBeginMessageID) ret0, _ := ret[0].(error) return ret0 } // DeleteMessagesBefore indicates an expected call of DeleteMessagesBefore. func (mr *MocktableCRUDMockRecorder) DeleteMessagesBefore(ctx, queueType, exclusiveBeginMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessagesBefore", reflect.TypeOf((*MocktableCRUD)(nil).DeleteMessagesBefore), ctx, queueType, exclusiveBeginMessageID) } // DeleteMessagesInRange mocks base method. func (m *MocktableCRUD) DeleteMessagesInRange(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID, inclusiveEndMessageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessagesInRange", ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) ret0, _ := ret[0].(error) return ret0 } // DeleteMessagesInRange indicates an expected call of DeleteMessagesInRange. func (mr *MocktableCRUDMockRecorder) DeleteMessagesInRange(ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessagesInRange", reflect.TypeOf((*MocktableCRUD)(nil).DeleteMessagesInRange), ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) } // DeleteReplicationDLQTask mocks base method. func (m *MocktableCRUD) DeleteReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteReplicationDLQTask", ctx, shardID, sourceCluster, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteReplicationDLQTask indicates an expected call of DeleteReplicationDLQTask. func (mr *MocktableCRUDMockRecorder) DeleteReplicationDLQTask(ctx, shardID, sourceCluster, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationDLQTask", reflect.TypeOf((*MocktableCRUD)(nil).DeleteReplicationDLQTask), ctx, shardID, sourceCluster, taskID) } // DeleteReplicationTask mocks base method. func (m *MocktableCRUD) DeleteReplicationTask(ctx context.Context, shardID int, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteReplicationTask", ctx, shardID, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteReplicationTask indicates an expected call of DeleteReplicationTask. func (mr *MocktableCRUDMockRecorder) DeleteReplicationTask(ctx, shardID, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationTask", reflect.TypeOf((*MocktableCRUD)(nil).DeleteReplicationTask), ctx, shardID, taskID) } // DeleteTaskList mocks base method. func (m *MocktableCRUD) DeleteTaskList(ctx context.Context, filter *TaskListFilter, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTaskList", ctx, filter, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // DeleteTaskList indicates an expected call of DeleteTaskList. func (mr *MocktableCRUDMockRecorder) DeleteTaskList(ctx, filter, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTaskList", reflect.TypeOf((*MocktableCRUD)(nil).DeleteTaskList), ctx, filter, previousRangeID) } // DeleteTimerTask mocks base method. func (m *MocktableCRUD) DeleteTimerTask(ctx context.Context, shardID int, taskID int64, visibilityTimestamp time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTimerTask", ctx, shardID, taskID, visibilityTimestamp) ret0, _ := ret[0].(error) return ret0 } // DeleteTimerTask indicates an expected call of DeleteTimerTask. func (mr *MocktableCRUDMockRecorder) DeleteTimerTask(ctx, shardID, taskID, visibilityTimestamp any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTimerTask", reflect.TypeOf((*MocktableCRUD)(nil).DeleteTimerTask), ctx, shardID, taskID, visibilityTimestamp) } // DeleteTransferTask mocks base method. func (m *MocktableCRUD) DeleteTransferTask(ctx context.Context, shardID int, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTransferTask", ctx, shardID, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteTransferTask indicates an expected call of DeleteTransferTask. func (mr *MocktableCRUDMockRecorder) DeleteTransferTask(ctx, shardID, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTransferTask", reflect.TypeOf((*MocktableCRUD)(nil).DeleteTransferTask), ctx, shardID, taskID) } // DeleteVisibility mocks base method. func (m *MocktableCRUD) DeleteVisibility(ctx context.Context, domainID, workflowID, runID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteVisibility", ctx, domainID, workflowID, runID) ret0, _ := ret[0].(error) return ret0 } // DeleteVisibility indicates an expected call of DeleteVisibility. func (mr *MocktableCRUDMockRecorder) DeleteVisibility(ctx, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVisibility", reflect.TypeOf((*MocktableCRUD)(nil).DeleteVisibility), ctx, domainID, workflowID, runID) } // DeleteWorkflowExecution mocks base method. func (m *MocktableCRUD) DeleteWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteWorkflowExecution", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(error) return ret0 } // DeleteWorkflowExecution indicates an expected call of DeleteWorkflowExecution. func (mr *MocktableCRUDMockRecorder) DeleteWorkflowExecution(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWorkflowExecution", reflect.TypeOf((*MocktableCRUD)(nil).DeleteWorkflowExecution), ctx, shardID, domainID, workflowID, runID) } // GetQueueSize mocks base method. func (m *MocktableCRUD) GetQueueSize(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueSize", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetQueueSize indicates an expected call of GetQueueSize. func (mr *MocktableCRUDMockRecorder) GetQueueSize(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueSize", reflect.TypeOf((*MocktableCRUD)(nil).GetQueueSize), ctx, queueType) } // GetTasksCount mocks base method. func (m *MocktableCRUD) GetTasksCount(ctx context.Context, filter *TasksFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasksCount", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTasksCount indicates an expected call of GetTasksCount. func (mr *MocktableCRUDMockRecorder) GetTasksCount(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasksCount", reflect.TypeOf((*MocktableCRUD)(nil).GetTasksCount), ctx, filter) } // InsertConfig mocks base method. func (m *MocktableCRUD) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertConfig", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertConfig indicates an expected call of InsertConfig. func (mr *MocktableCRUDMockRecorder) InsertConfig(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertConfig", reflect.TypeOf((*MocktableCRUD)(nil).InsertConfig), ctx, row) } // InsertDomain mocks base method. func (m *MocktableCRUD) InsertDomain(ctx context.Context, row *DomainRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertDomain", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertDomain indicates an expected call of InsertDomain. func (mr *MocktableCRUDMockRecorder) InsertDomain(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertDomain", reflect.TypeOf((*MocktableCRUD)(nil).InsertDomain), ctx, row) } // InsertDomainAuditLog mocks base method. func (m *MocktableCRUD) InsertDomainAuditLog(ctx context.Context, row *DomainAuditLogRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertDomainAuditLog", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertDomainAuditLog indicates an expected call of InsertDomainAuditLog. func (mr *MocktableCRUDMockRecorder) InsertDomainAuditLog(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertDomainAuditLog", reflect.TypeOf((*MocktableCRUD)(nil).InsertDomainAuditLog), ctx, row) } // InsertIntoHistoryTreeAndNode mocks base method. func (m *MocktableCRUD) InsertIntoHistoryTreeAndNode(ctx context.Context, treeRow *HistoryTreeRow, nodeRow *HistoryNodeRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoHistoryTreeAndNode", ctx, treeRow, nodeRow) ret0, _ := ret[0].(error) return ret0 } // InsertIntoHistoryTreeAndNode indicates an expected call of InsertIntoHistoryTreeAndNode. func (mr *MocktableCRUDMockRecorder) InsertIntoHistoryTreeAndNode(ctx, treeRow, nodeRow any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoHistoryTreeAndNode", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoHistoryTreeAndNode), ctx, treeRow, nodeRow) } // InsertIntoQueue mocks base method. func (m *MocktableCRUD) InsertIntoQueue(ctx context.Context, row *QueueMessageRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoQueue", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertIntoQueue indicates an expected call of InsertIntoQueue. func (mr *MocktableCRUDMockRecorder) InsertIntoQueue(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoQueue", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoQueue), ctx, row) } // InsertQueueMetadata mocks base method. func (m *MocktableCRUD) InsertQueueMetadata(ctx context.Context, row QueueMetadataRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertQueueMetadata", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertQueueMetadata indicates an expected call of InsertQueueMetadata. func (mr *MocktableCRUDMockRecorder) InsertQueueMetadata(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertQueueMetadata", reflect.TypeOf((*MocktableCRUD)(nil).InsertQueueMetadata), ctx, row) } // InsertReplicationDLQTask mocks base method. func (m *MocktableCRUD) InsertReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, task *HistoryMigrationTask) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertReplicationDLQTask", ctx, shardID, sourceCluster, task) ret0, _ := ret[0].(error) return ret0 } // InsertReplicationDLQTask indicates an expected call of InsertReplicationDLQTask. func (mr *MocktableCRUDMockRecorder) InsertReplicationDLQTask(ctx, shardID, sourceCluster, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertReplicationDLQTask", reflect.TypeOf((*MocktableCRUD)(nil).InsertReplicationDLQTask), ctx, shardID, sourceCluster, task) } // InsertReplicationTask mocks base method. func (m *MocktableCRUD) InsertReplicationTask(ctx context.Context, tasks []*HistoryMigrationTask, condition ShardCondition) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertReplicationTask", ctx, tasks, condition) ret0, _ := ret[0].(error) return ret0 } // InsertReplicationTask indicates an expected call of InsertReplicationTask. func (mr *MocktableCRUDMockRecorder) InsertReplicationTask(ctx, tasks, condition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertReplicationTask", reflect.TypeOf((*MocktableCRUD)(nil).InsertReplicationTask), ctx, tasks, condition) } // InsertShard mocks base method. func (m *MocktableCRUD) InsertShard(ctx context.Context, row *ShardRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertShard", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertShard indicates an expected call of InsertShard. func (mr *MocktableCRUDMockRecorder) InsertShard(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertShard", reflect.TypeOf((*MocktableCRUD)(nil).InsertShard), ctx, row) } // InsertTaskList mocks base method. func (m *MocktableCRUD) InsertTaskList(ctx context.Context, row *TaskListRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertTaskList", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertTaskList indicates an expected call of InsertTaskList. func (mr *MocktableCRUDMockRecorder) InsertTaskList(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertTaskList", reflect.TypeOf((*MocktableCRUD)(nil).InsertTaskList), ctx, row) } // InsertTasks mocks base method. func (m *MocktableCRUD) InsertTasks(ctx context.Context, tasksToInsert []*TaskRowForInsert, tasklistCondition *TaskListRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertTasks", ctx, tasksToInsert, tasklistCondition) ret0, _ := ret[0].(error) return ret0 } // InsertTasks indicates an expected call of InsertTasks. func (mr *MocktableCRUDMockRecorder) InsertTasks(ctx, tasksToInsert, tasklistCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertTasks", reflect.TypeOf((*MocktableCRUD)(nil).InsertTasks), ctx, tasksToInsert, tasklistCondition) } // InsertVisibility mocks base method. func (m *MocktableCRUD) InsertVisibility(ctx context.Context, ttlSeconds int64, row *VisibilityRowForInsert) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertVisibility", ctx, ttlSeconds, row) ret0, _ := ret[0].(error) return ret0 } // InsertVisibility indicates an expected call of InsertVisibility. func (mr *MocktableCRUDMockRecorder) InsertVisibility(ctx, ttlSeconds, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertVisibility", reflect.TypeOf((*MocktableCRUD)(nil).InsertVisibility), ctx, ttlSeconds, row) } // InsertWorkflowExecutionWithTasks mocks base method. func (m *MocktableCRUD) InsertWorkflowExecutionWithTasks(ctx context.Context, requests *WorkflowRequestsWriteRequest, currentWorkflowRequest *CurrentWorkflowWriteRequest, execution *WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*HistoryMigrationTask, activeClusterSelectionPolicyRow *ActiveClusterSelectionPolicyRow, shardCondition *ShardCondition) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertWorkflowExecutionWithTasks", ctx, requests, currentWorkflowRequest, execution, tasksByCategory, activeClusterSelectionPolicyRow, shardCondition) ret0, _ := ret[0].(error) return ret0 } // InsertWorkflowExecutionWithTasks indicates an expected call of InsertWorkflowExecutionWithTasks. func (mr *MocktableCRUDMockRecorder) InsertWorkflowExecutionWithTasks(ctx, requests, currentWorkflowRequest, execution, tasksByCategory, activeClusterSelectionPolicyRow, shardCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkflowExecutionWithTasks", reflect.TypeOf((*MocktableCRUD)(nil).InsertWorkflowExecutionWithTasks), ctx, requests, currentWorkflowRequest, execution, tasksByCategory, activeClusterSelectionPolicyRow, shardCondition) } // IsWorkflowExecutionExists mocks base method. func (m *MocktableCRUD) IsWorkflowExecutionExists(ctx context.Context, shardID int, domainID, workflowID, runID string) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsWorkflowExecutionExists", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsWorkflowExecutionExists indicates an expected call of IsWorkflowExecutionExists. func (mr *MocktableCRUDMockRecorder) IsWorkflowExecutionExists(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWorkflowExecutionExists", reflect.TypeOf((*MocktableCRUD)(nil).IsWorkflowExecutionExists), ctx, shardID, domainID, workflowID, runID) } // ListTaskList mocks base method. func (m *MocktableCRUD) ListTaskList(ctx context.Context, pageSize int, nextPageToken []byte) (*ListTaskListResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTaskList", ctx, pageSize, nextPageToken) ret0, _ := ret[0].(*ListTaskListResult) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTaskList indicates an expected call of ListTaskList. func (mr *MocktableCRUDMockRecorder) ListTaskList(ctx, pageSize, nextPageToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTaskList", reflect.TypeOf((*MocktableCRUD)(nil).ListTaskList), ctx, pageSize, nextPageToken) } // RangeDeleteReplicationDLQTasks mocks base method. func (m *MocktableCRUD) RangeDeleteReplicationDLQTasks(ctx context.Context, shardID int, sourceCluster string, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteReplicationDLQTasks", ctx, shardID, sourceCluster, inclusiveBeginTaskID, exclusiveEndTaskID) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteReplicationDLQTasks indicates an expected call of RangeDeleteReplicationDLQTasks. func (mr *MocktableCRUDMockRecorder) RangeDeleteReplicationDLQTasks(ctx, shardID, sourceCluster, inclusiveBeginTaskID, exclusiveEndTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteReplicationDLQTasks", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteReplicationDLQTasks), ctx, shardID, sourceCluster, inclusiveBeginTaskID, exclusiveEndTaskID) } // RangeDeleteReplicationTasks mocks base method. func (m *MocktableCRUD) RangeDeleteReplicationTasks(ctx context.Context, shardID int, exclusiveEndTaskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteReplicationTasks", ctx, shardID, exclusiveEndTaskID) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteReplicationTasks indicates an expected call of RangeDeleteReplicationTasks. func (mr *MocktableCRUDMockRecorder) RangeDeleteReplicationTasks(ctx, shardID, exclusiveEndTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteReplicationTasks", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteReplicationTasks), ctx, shardID, exclusiveEndTaskID) } // RangeDeleteTasks mocks base method. func (m *MocktableCRUD) RangeDeleteTasks(ctx context.Context, filter *TasksFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteTasks", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteTasks indicates an expected call of RangeDeleteTasks. func (mr *MocktableCRUDMockRecorder) RangeDeleteTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteTasks", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteTasks), ctx, filter) } // RangeDeleteTimerTasks mocks base method. func (m *MocktableCRUD) RangeDeleteTimerTasks(ctx context.Context, shardID int, inclusiveMinTime, exclusiveMaxTime time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteTimerTasks", ctx, shardID, inclusiveMinTime, exclusiveMaxTime) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteTimerTasks indicates an expected call of RangeDeleteTimerTasks. func (mr *MocktableCRUDMockRecorder) RangeDeleteTimerTasks(ctx, shardID, inclusiveMinTime, exclusiveMaxTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteTimerTasks", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteTimerTasks), ctx, shardID, inclusiveMinTime, exclusiveMaxTime) } // RangeDeleteTransferTasks mocks base method. func (m *MocktableCRUD) RangeDeleteTransferTasks(ctx context.Context, shardID int, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteTransferTasks", ctx, shardID, inclusiveBeginTaskID, exclusiveEndTaskID) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteTransferTasks indicates an expected call of RangeDeleteTransferTasks. func (mr *MocktableCRUDMockRecorder) RangeDeleteTransferTasks(ctx, shardID, inclusiveBeginTaskID, exclusiveEndTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteTransferTasks", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteTransferTasks), ctx, shardID, inclusiveBeginTaskID, exclusiveEndTaskID) } // SelectActiveClusterSelectionPolicy mocks base method. func (m *MocktableCRUD) SelectActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, wfID, rID string) (*ActiveClusterSelectionPolicyRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectActiveClusterSelectionPolicy", ctx, shardID, domainID, wfID, rID) ret0, _ := ret[0].(*ActiveClusterSelectionPolicyRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectActiveClusterSelectionPolicy indicates an expected call of SelectActiveClusterSelectionPolicy. func (mr *MocktableCRUDMockRecorder) SelectActiveClusterSelectionPolicy(ctx, shardID, domainID, wfID, rID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectActiveClusterSelectionPolicy", reflect.TypeOf((*MocktableCRUD)(nil).SelectActiveClusterSelectionPolicy), ctx, shardID, domainID, wfID, rID) } // SelectAllCurrentWorkflows mocks base method. func (m *MocktableCRUD) SelectAllCurrentWorkflows(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.CurrentWorkflowExecution, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllCurrentWorkflows", ctx, shardID, pageToken, pageSize) ret0, _ := ret[0].([]*persistence.CurrentWorkflowExecution) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllCurrentWorkflows indicates an expected call of SelectAllCurrentWorkflows. func (mr *MocktableCRUDMockRecorder) SelectAllCurrentWorkflows(ctx, shardID, pageToken, pageSize any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllCurrentWorkflows", reflect.TypeOf((*MocktableCRUD)(nil).SelectAllCurrentWorkflows), ctx, shardID, pageToken, pageSize) } // SelectAllDomains mocks base method. func (m *MocktableCRUD) SelectAllDomains(ctx context.Context, pageSize int, pageToken []byte) ([]*DomainRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllDomains", ctx, pageSize, pageToken) ret0, _ := ret[0].([]*DomainRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllDomains indicates an expected call of SelectAllDomains. func (mr *MocktableCRUDMockRecorder) SelectAllDomains(ctx, pageSize, pageToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllDomains", reflect.TypeOf((*MocktableCRUD)(nil).SelectAllDomains), ctx, pageSize, pageToken) } // SelectAllHistoryTrees mocks base method. func (m *MocktableCRUD) SelectAllHistoryTrees(ctx context.Context, nextPageToken []byte, pageSize int) ([]*HistoryTreeRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllHistoryTrees", ctx, nextPageToken, pageSize) ret0, _ := ret[0].([]*HistoryTreeRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllHistoryTrees indicates an expected call of SelectAllHistoryTrees. func (mr *MocktableCRUDMockRecorder) SelectAllHistoryTrees(ctx, nextPageToken, pageSize any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllHistoryTrees", reflect.TypeOf((*MocktableCRUD)(nil).SelectAllHistoryTrees), ctx, nextPageToken, pageSize) } // SelectAllWorkflowExecutions mocks base method. func (m *MocktableCRUD) SelectAllWorkflowExecutions(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.InternalListConcreteExecutionsEntity, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllWorkflowExecutions", ctx, shardID, pageToken, pageSize) ret0, _ := ret[0].([]*persistence.InternalListConcreteExecutionsEntity) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllWorkflowExecutions indicates an expected call of SelectAllWorkflowExecutions. func (mr *MocktableCRUDMockRecorder) SelectAllWorkflowExecutions(ctx, shardID, pageToken, pageSize any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllWorkflowExecutions", reflect.TypeOf((*MocktableCRUD)(nil).SelectAllWorkflowExecutions), ctx, shardID, pageToken, pageSize) } // SelectCurrentWorkflow mocks base method. func (m *MocktableCRUD) SelectCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID string) (*CurrentWorkflowRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectCurrentWorkflow", ctx, shardID, domainID, workflowID) ret0, _ := ret[0].(*CurrentWorkflowRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectCurrentWorkflow indicates an expected call of SelectCurrentWorkflow. func (mr *MocktableCRUDMockRecorder) SelectCurrentWorkflow(ctx, shardID, domainID, workflowID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectCurrentWorkflow", reflect.TypeOf((*MocktableCRUD)(nil).SelectCurrentWorkflow), ctx, shardID, domainID, workflowID) } // SelectDomain mocks base method. func (m *MocktableCRUD) SelectDomain(ctx context.Context, domainID, domainName *string) (*DomainRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectDomain", ctx, domainID, domainName) ret0, _ := ret[0].(*DomainRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectDomain indicates an expected call of SelectDomain. func (mr *MocktableCRUDMockRecorder) SelectDomain(ctx, domainID, domainName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDomain", reflect.TypeOf((*MocktableCRUD)(nil).SelectDomain), ctx, domainID, domainName) } // SelectDomainAuditLogs mocks base method. func (m *MocktableCRUD) SelectDomainAuditLogs(ctx context.Context, filter *DomainAuditLogFilter) ([]*DomainAuditLogRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectDomainAuditLogs", ctx, filter) ret0, _ := ret[0].([]*DomainAuditLogRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectDomainAuditLogs indicates an expected call of SelectDomainAuditLogs. func (mr *MocktableCRUDMockRecorder) SelectDomainAuditLogs(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDomainAuditLogs", reflect.TypeOf((*MocktableCRUD)(nil).SelectDomainAuditLogs), ctx, filter) } // SelectDomainMetadata mocks base method. func (m *MocktableCRUD) SelectDomainMetadata(ctx context.Context) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectDomainMetadata", ctx) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectDomainMetadata indicates an expected call of SelectDomainMetadata. func (mr *MocktableCRUDMockRecorder) SelectDomainMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDomainMetadata", reflect.TypeOf((*MocktableCRUD)(nil).SelectDomainMetadata), ctx) } // SelectFromHistoryNode mocks base method. func (m *MocktableCRUD) SelectFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) ([]*HistoryNodeRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryNode", ctx, filter) ret0, _ := ret[0].([]*HistoryNodeRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectFromHistoryNode indicates an expected call of SelectFromHistoryNode. func (mr *MocktableCRUDMockRecorder) SelectFromHistoryNode(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryNode", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromHistoryNode), ctx, filter) } // SelectFromHistoryTree mocks base method. func (m *MocktableCRUD) SelectFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) ([]*HistoryTreeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryTree", ctx, filter) ret0, _ := ret[0].([]*HistoryTreeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromHistoryTree indicates an expected call of SelectFromHistoryTree. func (mr *MocktableCRUDMockRecorder) SelectFromHistoryTree(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryTree", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromHistoryTree), ctx, filter) } // SelectLastEnqueuedMessageID mocks base method. func (m *MocktableCRUD) SelectLastEnqueuedMessageID(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectLastEnqueuedMessageID", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectLastEnqueuedMessageID indicates an expected call of SelectLastEnqueuedMessageID. func (mr *MocktableCRUDMockRecorder) SelectLastEnqueuedMessageID(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectLastEnqueuedMessageID", reflect.TypeOf((*MocktableCRUD)(nil).SelectLastEnqueuedMessageID), ctx, queueType) } // SelectLatestConfig mocks base method. func (m *MocktableCRUD) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectLatestConfig", ctx, rowType) ret0, _ := ret[0].(*persistence.InternalConfigStoreEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectLatestConfig indicates an expected call of SelectLatestConfig. func (mr *MocktableCRUDMockRecorder) SelectLatestConfig(ctx, rowType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectLatestConfig", reflect.TypeOf((*MocktableCRUD)(nil).SelectLatestConfig), ctx, rowType) } // SelectMessagesBetween mocks base method. func (m *MocktableCRUD) SelectMessagesBetween(ctx context.Context, request SelectMessagesBetweenRequest) (*SelectMessagesBetweenResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectMessagesBetween", ctx, request) ret0, _ := ret[0].(*SelectMessagesBetweenResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectMessagesBetween indicates an expected call of SelectMessagesBetween. func (mr *MocktableCRUDMockRecorder) SelectMessagesBetween(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectMessagesBetween", reflect.TypeOf((*MocktableCRUD)(nil).SelectMessagesBetween), ctx, request) } // SelectMessagesFrom mocks base method. func (m *MocktableCRUD) SelectMessagesFrom(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, maxRows int) ([]*QueueMessageRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectMessagesFrom", ctx, queueType, exclusiveBeginMessageID, maxRows) ret0, _ := ret[0].([]*QueueMessageRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectMessagesFrom indicates an expected call of SelectMessagesFrom. func (mr *MocktableCRUDMockRecorder) SelectMessagesFrom(ctx, queueType, exclusiveBeginMessageID, maxRows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectMessagesFrom", reflect.TypeOf((*MocktableCRUD)(nil).SelectMessagesFrom), ctx, queueType, exclusiveBeginMessageID, maxRows) } // SelectOneClosedWorkflow mocks base method. func (m *MocktableCRUD) SelectOneClosedWorkflow(ctx context.Context, domainID, workflowID, runID string) (*VisibilityRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectOneClosedWorkflow", ctx, domainID, workflowID, runID) ret0, _ := ret[0].(*VisibilityRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectOneClosedWorkflow indicates an expected call of SelectOneClosedWorkflow. func (mr *MocktableCRUDMockRecorder) SelectOneClosedWorkflow(ctx, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectOneClosedWorkflow", reflect.TypeOf((*MocktableCRUD)(nil).SelectOneClosedWorkflow), ctx, domainID, workflowID, runID) } // SelectQueueMetadata mocks base method. func (m *MocktableCRUD) SelectQueueMetadata(ctx context.Context, queueType persistence.QueueType) (*QueueMetadataRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectQueueMetadata", ctx, queueType) ret0, _ := ret[0].(*QueueMetadataRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectQueueMetadata indicates an expected call of SelectQueueMetadata. func (mr *MocktableCRUDMockRecorder) SelectQueueMetadata(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectQueueMetadata", reflect.TypeOf((*MocktableCRUD)(nil).SelectQueueMetadata), ctx, queueType) } // SelectReplicationDLQTasksCount mocks base method. func (m *MocktableCRUD) SelectReplicationDLQTasksCount(ctx context.Context, shardID int, sourceCluster string) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectReplicationDLQTasksCount", ctx, shardID, sourceCluster) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectReplicationDLQTasksCount indicates an expected call of SelectReplicationDLQTasksCount. func (mr *MocktableCRUDMockRecorder) SelectReplicationDLQTasksCount(ctx, shardID, sourceCluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectReplicationDLQTasksCount", reflect.TypeOf((*MocktableCRUD)(nil).SelectReplicationDLQTasksCount), ctx, shardID, sourceCluster) } // SelectReplicationDLQTasksOrderByTaskID mocks base method. func (m *MocktableCRUD) SelectReplicationDLQTasksOrderByTaskID(ctx context.Context, shardID int, sourceCluster string, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectReplicationDLQTasksOrderByTaskID", ctx, shardID, sourceCluster, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectReplicationDLQTasksOrderByTaskID indicates an expected call of SelectReplicationDLQTasksOrderByTaskID. func (mr *MocktableCRUDMockRecorder) SelectReplicationDLQTasksOrderByTaskID(ctx, shardID, sourceCluster, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectReplicationDLQTasksOrderByTaskID", reflect.TypeOf((*MocktableCRUD)(nil).SelectReplicationDLQTasksOrderByTaskID), ctx, shardID, sourceCluster, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) } // SelectReplicationTasksOrderByTaskID mocks base method. func (m *MocktableCRUD) SelectReplicationTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectReplicationTasksOrderByTaskID", ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectReplicationTasksOrderByTaskID indicates an expected call of SelectReplicationTasksOrderByTaskID. func (mr *MocktableCRUDMockRecorder) SelectReplicationTasksOrderByTaskID(ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectReplicationTasksOrderByTaskID", reflect.TypeOf((*MocktableCRUD)(nil).SelectReplicationTasksOrderByTaskID), ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) } // SelectShard mocks base method. func (m *MocktableCRUD) SelectShard(ctx context.Context, shardID int, currentClusterName string) (int64, *ShardRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectShard", ctx, shardID, currentClusterName) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(*ShardRow) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectShard indicates an expected call of SelectShard. func (mr *MocktableCRUDMockRecorder) SelectShard(ctx, shardID, currentClusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectShard", reflect.TypeOf((*MocktableCRUD)(nil).SelectShard), ctx, shardID, currentClusterName) } // SelectTaskList mocks base method. func (m *MocktableCRUD) SelectTaskList(ctx context.Context, filter *TaskListFilter) (*TaskListRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTaskList", ctx, filter) ret0, _ := ret[0].(*TaskListRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectTaskList indicates an expected call of SelectTaskList. func (mr *MocktableCRUDMockRecorder) SelectTaskList(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTaskList", reflect.TypeOf((*MocktableCRUD)(nil).SelectTaskList), ctx, filter) } // SelectTasks mocks base method. func (m *MocktableCRUD) SelectTasks(ctx context.Context, filter *TasksFilter) ([]*TaskRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTasks", ctx, filter) ret0, _ := ret[0].([]*TaskRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectTasks indicates an expected call of SelectTasks. func (mr *MocktableCRUDMockRecorder) SelectTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTasks", reflect.TypeOf((*MocktableCRUD)(nil).SelectTasks), ctx, filter) } // SelectTimerTasksOrderByVisibilityTime mocks base method. func (m *MocktableCRUD) SelectTimerTasksOrderByVisibilityTime(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTime, exclusiveMaxTime time.Time) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTimerTasksOrderByVisibilityTime", ctx, shardID, pageSize, pageToken, inclusiveMinTime, exclusiveMaxTime) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectTimerTasksOrderByVisibilityTime indicates an expected call of SelectTimerTasksOrderByVisibilityTime. func (mr *MocktableCRUDMockRecorder) SelectTimerTasksOrderByVisibilityTime(ctx, shardID, pageSize, pageToken, inclusiveMinTime, exclusiveMaxTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTimerTasksOrderByVisibilityTime", reflect.TypeOf((*MocktableCRUD)(nil).SelectTimerTasksOrderByVisibilityTime), ctx, shardID, pageSize, pageToken, inclusiveMinTime, exclusiveMaxTime) } // SelectTransferTasksOrderByTaskID mocks base method. func (m *MocktableCRUD) SelectTransferTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTransferTasksOrderByTaskID", ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectTransferTasksOrderByTaskID indicates an expected call of SelectTransferTasksOrderByTaskID. func (mr *MocktableCRUDMockRecorder) SelectTransferTasksOrderByTaskID(ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTransferTasksOrderByTaskID", reflect.TypeOf((*MocktableCRUD)(nil).SelectTransferTasksOrderByTaskID), ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) } // SelectVisibility mocks base method. func (m *MocktableCRUD) SelectVisibility(ctx context.Context, filter *VisibilityFilter) (*SelectVisibilityResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectVisibility", ctx, filter) ret0, _ := ret[0].(*SelectVisibilityResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectVisibility indicates an expected call of SelectVisibility. func (mr *MocktableCRUDMockRecorder) SelectVisibility(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectVisibility", reflect.TypeOf((*MocktableCRUD)(nil).SelectVisibility), ctx, filter) } // SelectWorkflowExecution mocks base method. func (m *MocktableCRUD) SelectWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) (*WorkflowExecution, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectWorkflowExecution", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(*WorkflowExecution) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectWorkflowExecution indicates an expected call of SelectWorkflowExecution. func (mr *MocktableCRUDMockRecorder) SelectWorkflowExecution(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectWorkflowExecution", reflect.TypeOf((*MocktableCRUD)(nil).SelectWorkflowExecution), ctx, shardID, domainID, workflowID, runID) } // UpdateDomain mocks base method. func (m *MocktableCRUD) UpdateDomain(ctx context.Context, row *DomainRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomain", ctx, row) ret0, _ := ret[0].(error) return ret0 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MocktableCRUDMockRecorder) UpdateDomain(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MocktableCRUD)(nil).UpdateDomain), ctx, row) } // UpdateQueueMetadataCas mocks base method. func (m *MocktableCRUD) UpdateQueueMetadataCas(ctx context.Context, row QueueMetadataRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateQueueMetadataCas", ctx, row) ret0, _ := ret[0].(error) return ret0 } // UpdateQueueMetadataCas indicates an expected call of UpdateQueueMetadataCas. func (mr *MocktableCRUDMockRecorder) UpdateQueueMetadataCas(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateQueueMetadataCas", reflect.TypeOf((*MocktableCRUD)(nil).UpdateQueueMetadataCas), ctx, row) } // UpdateRangeID mocks base method. func (m *MocktableCRUD) UpdateRangeID(ctx context.Context, shardID int, rangeID, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateRangeID", ctx, shardID, rangeID, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateRangeID indicates an expected call of UpdateRangeID. func (mr *MocktableCRUDMockRecorder) UpdateRangeID(ctx, shardID, rangeID, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRangeID", reflect.TypeOf((*MocktableCRUD)(nil).UpdateRangeID), ctx, shardID, rangeID, previousRangeID) } // UpdateShard mocks base method. func (m *MocktableCRUD) UpdateShard(ctx context.Context, row *ShardRow, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateShard", ctx, row, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateShard indicates an expected call of UpdateShard. func (mr *MocktableCRUDMockRecorder) UpdateShard(ctx, row, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateShard", reflect.TypeOf((*MocktableCRUD)(nil).UpdateShard), ctx, row, previousRangeID) } // UpdateTaskList mocks base method. func (m *MocktableCRUD) UpdateTaskList(ctx context.Context, row *TaskListRow, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskList", ctx, row, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateTaskList indicates an expected call of UpdateTaskList. func (mr *MocktableCRUDMockRecorder) UpdateTaskList(ctx, row, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskList", reflect.TypeOf((*MocktableCRUD)(nil).UpdateTaskList), ctx, row, previousRangeID) } // UpdateTaskListWithTTL mocks base method. func (m *MocktableCRUD) UpdateTaskListWithTTL(ctx context.Context, ttlSeconds int64, row *TaskListRow, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskListWithTTL", ctx, ttlSeconds, row, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateTaskListWithTTL indicates an expected call of UpdateTaskListWithTTL. func (mr *MocktableCRUDMockRecorder) UpdateTaskListWithTTL(ctx, ttlSeconds, row, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListWithTTL", reflect.TypeOf((*MocktableCRUD)(nil).UpdateTaskListWithTTL), ctx, ttlSeconds, row, previousRangeID) } // UpdateVisibility mocks base method. func (m *MocktableCRUD) UpdateVisibility(ctx context.Context, ttlSeconds int64, row *VisibilityRowForUpdate) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateVisibility", ctx, ttlSeconds, row) ret0, _ := ret[0].(error) return ret0 } // UpdateVisibility indicates an expected call of UpdateVisibility. func (mr *MocktableCRUDMockRecorder) UpdateVisibility(ctx, ttlSeconds, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateVisibility", reflect.TypeOf((*MocktableCRUD)(nil).UpdateVisibility), ctx, ttlSeconds, row) } // UpdateWorkflowExecutionWithTasks mocks base method. func (m *MocktableCRUD) UpdateWorkflowExecutionWithTasks(ctx context.Context, requests *WorkflowRequestsWriteRequest, currentWorkflowRequest *CurrentWorkflowWriteRequest, mutatedExecution, insertedExecution *WorkflowExecutionRequest, activeClusterSelectionPolicyRow *ActiveClusterSelectionPolicyRow, resetExecution *WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*HistoryMigrationTask, shardCondition *ShardCondition) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecutionWithTasks", ctx, requests, currentWorkflowRequest, mutatedExecution, insertedExecution, activeClusterSelectionPolicyRow, resetExecution, tasksByCategory, shardCondition) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowExecutionWithTasks indicates an expected call of UpdateWorkflowExecutionWithTasks. func (mr *MocktableCRUDMockRecorder) UpdateWorkflowExecutionWithTasks(ctx, requests, currentWorkflowRequest, mutatedExecution, insertedExecution, activeClusterSelectionPolicyRow, resetExecution, tasksByCategory, shardCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecutionWithTasks", reflect.TypeOf((*MocktableCRUD)(nil).UpdateWorkflowExecutionWithTasks), ctx, requests, currentWorkflowRequest, mutatedExecution, insertedExecution, activeClusterSelectionPolicyRow, resetExecution, tasksByCategory, shardCondition) } // MockClientErrorChecker is a mock of ClientErrorChecker interface. type MockClientErrorChecker struct { ctrl *gomock.Controller recorder *MockClientErrorCheckerMockRecorder isgomock struct{} } // MockClientErrorCheckerMockRecorder is the mock recorder for MockClientErrorChecker. type MockClientErrorCheckerMockRecorder struct { mock *MockClientErrorChecker } // NewMockClientErrorChecker creates a new mock instance. func NewMockClientErrorChecker(ctrl *gomock.Controller) *MockClientErrorChecker { mock := &MockClientErrorChecker{ctrl: ctrl} mock.recorder = &MockClientErrorCheckerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClientErrorChecker) EXPECT() *MockClientErrorCheckerMockRecorder { return m.recorder } // IsDBUnavailableError mocks base method. func (m *MockClientErrorChecker) IsDBUnavailableError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsDBUnavailableError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsDBUnavailableError indicates an expected call of IsDBUnavailableError. func (mr *MockClientErrorCheckerMockRecorder) IsDBUnavailableError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDBUnavailableError", reflect.TypeOf((*MockClientErrorChecker)(nil).IsDBUnavailableError), arg0) } // IsNotFoundError mocks base method. func (m *MockClientErrorChecker) IsNotFoundError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsNotFoundError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsNotFoundError indicates an expected call of IsNotFoundError. func (mr *MockClientErrorCheckerMockRecorder) IsNotFoundError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNotFoundError", reflect.TypeOf((*MockClientErrorChecker)(nil).IsNotFoundError), arg0) } // IsThrottlingError mocks base method. func (m *MockClientErrorChecker) IsThrottlingError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsThrottlingError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsThrottlingError indicates an expected call of IsThrottlingError. func (mr *MockClientErrorCheckerMockRecorder) IsThrottlingError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsThrottlingError", reflect.TypeOf((*MockClientErrorChecker)(nil).IsThrottlingError), arg0) } // IsTimeoutError mocks base method. func (m *MockClientErrorChecker) IsTimeoutError(arg0 error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsTimeoutError", arg0) ret0, _ := ret[0].(bool) return ret0 } // IsTimeoutError indicates an expected call of IsTimeoutError. func (mr *MockClientErrorCheckerMockRecorder) IsTimeoutError(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsTimeoutError", reflect.TypeOf((*MockClientErrorChecker)(nil).IsTimeoutError), arg0) } // MockHistoryEventsCRUD is a mock of HistoryEventsCRUD interface. type MockHistoryEventsCRUD struct { ctrl *gomock.Controller recorder *MockHistoryEventsCRUDMockRecorder isgomock struct{} } // MockHistoryEventsCRUDMockRecorder is the mock recorder for MockHistoryEventsCRUD. type MockHistoryEventsCRUDMockRecorder struct { mock *MockHistoryEventsCRUD } // NewMockHistoryEventsCRUD creates a new mock instance. func NewMockHistoryEventsCRUD(ctrl *gomock.Controller) *MockHistoryEventsCRUD { mock := &MockHistoryEventsCRUD{ctrl: ctrl} mock.recorder = &MockHistoryEventsCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHistoryEventsCRUD) EXPECT() *MockHistoryEventsCRUDMockRecorder { return m.recorder } // DeleteFromHistoryTreeAndNode mocks base method. func (m *MockHistoryEventsCRUD) DeleteFromHistoryTreeAndNode(ctx context.Context, treeFilter *HistoryTreeFilter, nodeFilters []*HistoryNodeFilter) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromHistoryTreeAndNode", ctx, treeFilter, nodeFilters) ret0, _ := ret[0].(error) return ret0 } // DeleteFromHistoryTreeAndNode indicates an expected call of DeleteFromHistoryTreeAndNode. func (mr *MockHistoryEventsCRUDMockRecorder) DeleteFromHistoryTreeAndNode(ctx, treeFilter, nodeFilters any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromHistoryTreeAndNode", reflect.TypeOf((*MockHistoryEventsCRUD)(nil).DeleteFromHistoryTreeAndNode), ctx, treeFilter, nodeFilters) } // InsertIntoHistoryTreeAndNode mocks base method. func (m *MockHistoryEventsCRUD) InsertIntoHistoryTreeAndNode(ctx context.Context, treeRow *HistoryTreeRow, nodeRow *HistoryNodeRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoHistoryTreeAndNode", ctx, treeRow, nodeRow) ret0, _ := ret[0].(error) return ret0 } // InsertIntoHistoryTreeAndNode indicates an expected call of InsertIntoHistoryTreeAndNode. func (mr *MockHistoryEventsCRUDMockRecorder) InsertIntoHistoryTreeAndNode(ctx, treeRow, nodeRow any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoHistoryTreeAndNode", reflect.TypeOf((*MockHistoryEventsCRUD)(nil).InsertIntoHistoryTreeAndNode), ctx, treeRow, nodeRow) } // SelectAllHistoryTrees mocks base method. func (m *MockHistoryEventsCRUD) SelectAllHistoryTrees(ctx context.Context, nextPageToken []byte, pageSize int) ([]*HistoryTreeRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllHistoryTrees", ctx, nextPageToken, pageSize) ret0, _ := ret[0].([]*HistoryTreeRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllHistoryTrees indicates an expected call of SelectAllHistoryTrees. func (mr *MockHistoryEventsCRUDMockRecorder) SelectAllHistoryTrees(ctx, nextPageToken, pageSize any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllHistoryTrees", reflect.TypeOf((*MockHistoryEventsCRUD)(nil).SelectAllHistoryTrees), ctx, nextPageToken, pageSize) } // SelectFromHistoryNode mocks base method. func (m *MockHistoryEventsCRUD) SelectFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) ([]*HistoryNodeRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryNode", ctx, filter) ret0, _ := ret[0].([]*HistoryNodeRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectFromHistoryNode indicates an expected call of SelectFromHistoryNode. func (mr *MockHistoryEventsCRUDMockRecorder) SelectFromHistoryNode(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryNode", reflect.TypeOf((*MockHistoryEventsCRUD)(nil).SelectFromHistoryNode), ctx, filter) } // SelectFromHistoryTree mocks base method. func (m *MockHistoryEventsCRUD) SelectFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) ([]*HistoryTreeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryTree", ctx, filter) ret0, _ := ret[0].([]*HistoryTreeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromHistoryTree indicates an expected call of SelectFromHistoryTree. func (mr *MockHistoryEventsCRUDMockRecorder) SelectFromHistoryTree(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryTree", reflect.TypeOf((*MockHistoryEventsCRUD)(nil).SelectFromHistoryTree), ctx, filter) } // MockMessageQueueCRUD is a mock of MessageQueueCRUD interface. type MockMessageQueueCRUD struct { ctrl *gomock.Controller recorder *MockMessageQueueCRUDMockRecorder isgomock struct{} } // MockMessageQueueCRUDMockRecorder is the mock recorder for MockMessageQueueCRUD. type MockMessageQueueCRUDMockRecorder struct { mock *MockMessageQueueCRUD } // NewMockMessageQueueCRUD creates a new mock instance. func NewMockMessageQueueCRUD(ctrl *gomock.Controller) *MockMessageQueueCRUD { mock := &MockMessageQueueCRUD{ctrl: ctrl} mock.recorder = &MockMessageQueueCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMessageQueueCRUD) EXPECT() *MockMessageQueueCRUDMockRecorder { return m.recorder } // DeleteMessage mocks base method. func (m *MockMessageQueueCRUD) DeleteMessage(ctx context.Context, queueType persistence.QueueType, messageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessage", ctx, queueType, messageID) ret0, _ := ret[0].(error) return ret0 } // DeleteMessage indicates an expected call of DeleteMessage. func (mr *MockMessageQueueCRUDMockRecorder) DeleteMessage(ctx, queueType, messageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessage", reflect.TypeOf((*MockMessageQueueCRUD)(nil).DeleteMessage), ctx, queueType, messageID) } // DeleteMessagesBefore mocks base method. func (m *MockMessageQueueCRUD) DeleteMessagesBefore(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessagesBefore", ctx, queueType, exclusiveBeginMessageID) ret0, _ := ret[0].(error) return ret0 } // DeleteMessagesBefore indicates an expected call of DeleteMessagesBefore. func (mr *MockMessageQueueCRUDMockRecorder) DeleteMessagesBefore(ctx, queueType, exclusiveBeginMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessagesBefore", reflect.TypeOf((*MockMessageQueueCRUD)(nil).DeleteMessagesBefore), ctx, queueType, exclusiveBeginMessageID) } // DeleteMessagesInRange mocks base method. func (m *MockMessageQueueCRUD) DeleteMessagesInRange(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID, inclusiveEndMessageID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessagesInRange", ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) ret0, _ := ret[0].(error) return ret0 } // DeleteMessagesInRange indicates an expected call of DeleteMessagesInRange. func (mr *MockMessageQueueCRUDMockRecorder) DeleteMessagesInRange(ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessagesInRange", reflect.TypeOf((*MockMessageQueueCRUD)(nil).DeleteMessagesInRange), ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) } // GetQueueSize mocks base method. func (m *MockMessageQueueCRUD) GetQueueSize(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueSize", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetQueueSize indicates an expected call of GetQueueSize. func (mr *MockMessageQueueCRUDMockRecorder) GetQueueSize(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueSize", reflect.TypeOf((*MockMessageQueueCRUD)(nil).GetQueueSize), ctx, queueType) } // InsertIntoQueue mocks base method. func (m *MockMessageQueueCRUD) InsertIntoQueue(ctx context.Context, row *QueueMessageRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoQueue", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertIntoQueue indicates an expected call of InsertIntoQueue. func (mr *MockMessageQueueCRUDMockRecorder) InsertIntoQueue(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoQueue", reflect.TypeOf((*MockMessageQueueCRUD)(nil).InsertIntoQueue), ctx, row) } // InsertQueueMetadata mocks base method. func (m *MockMessageQueueCRUD) InsertQueueMetadata(ctx context.Context, row QueueMetadataRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertQueueMetadata", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertQueueMetadata indicates an expected call of InsertQueueMetadata. func (mr *MockMessageQueueCRUDMockRecorder) InsertQueueMetadata(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertQueueMetadata", reflect.TypeOf((*MockMessageQueueCRUD)(nil).InsertQueueMetadata), ctx, row) } // SelectLastEnqueuedMessageID mocks base method. func (m *MockMessageQueueCRUD) SelectLastEnqueuedMessageID(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectLastEnqueuedMessageID", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectLastEnqueuedMessageID indicates an expected call of SelectLastEnqueuedMessageID. func (mr *MockMessageQueueCRUDMockRecorder) SelectLastEnqueuedMessageID(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectLastEnqueuedMessageID", reflect.TypeOf((*MockMessageQueueCRUD)(nil).SelectLastEnqueuedMessageID), ctx, queueType) } // SelectMessagesBetween mocks base method. func (m *MockMessageQueueCRUD) SelectMessagesBetween(ctx context.Context, request SelectMessagesBetweenRequest) (*SelectMessagesBetweenResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectMessagesBetween", ctx, request) ret0, _ := ret[0].(*SelectMessagesBetweenResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectMessagesBetween indicates an expected call of SelectMessagesBetween. func (mr *MockMessageQueueCRUDMockRecorder) SelectMessagesBetween(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectMessagesBetween", reflect.TypeOf((*MockMessageQueueCRUD)(nil).SelectMessagesBetween), ctx, request) } // SelectMessagesFrom mocks base method. func (m *MockMessageQueueCRUD) SelectMessagesFrom(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, maxRows int) ([]*QueueMessageRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectMessagesFrom", ctx, queueType, exclusiveBeginMessageID, maxRows) ret0, _ := ret[0].([]*QueueMessageRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectMessagesFrom indicates an expected call of SelectMessagesFrom. func (mr *MockMessageQueueCRUDMockRecorder) SelectMessagesFrom(ctx, queueType, exclusiveBeginMessageID, maxRows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectMessagesFrom", reflect.TypeOf((*MockMessageQueueCRUD)(nil).SelectMessagesFrom), ctx, queueType, exclusiveBeginMessageID, maxRows) } // SelectQueueMetadata mocks base method. func (m *MockMessageQueueCRUD) SelectQueueMetadata(ctx context.Context, queueType persistence.QueueType) (*QueueMetadataRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectQueueMetadata", ctx, queueType) ret0, _ := ret[0].(*QueueMetadataRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectQueueMetadata indicates an expected call of SelectQueueMetadata. func (mr *MockMessageQueueCRUDMockRecorder) SelectQueueMetadata(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectQueueMetadata", reflect.TypeOf((*MockMessageQueueCRUD)(nil).SelectQueueMetadata), ctx, queueType) } // UpdateQueueMetadataCas mocks base method. func (m *MockMessageQueueCRUD) UpdateQueueMetadataCas(ctx context.Context, row QueueMetadataRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateQueueMetadataCas", ctx, row) ret0, _ := ret[0].(error) return ret0 } // UpdateQueueMetadataCas indicates an expected call of UpdateQueueMetadataCas. func (mr *MockMessageQueueCRUDMockRecorder) UpdateQueueMetadataCas(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateQueueMetadataCas", reflect.TypeOf((*MockMessageQueueCRUD)(nil).UpdateQueueMetadataCas), ctx, row) } // MockDomainCRUD is a mock of DomainCRUD interface. type MockDomainCRUD struct { ctrl *gomock.Controller recorder *MockDomainCRUDMockRecorder isgomock struct{} } // MockDomainCRUDMockRecorder is the mock recorder for MockDomainCRUD. type MockDomainCRUDMockRecorder struct { mock *MockDomainCRUD } // NewMockDomainCRUD creates a new mock instance. func NewMockDomainCRUD(ctrl *gomock.Controller) *MockDomainCRUD { mock := &MockDomainCRUD{ctrl: ctrl} mock.recorder = &MockDomainCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDomainCRUD) EXPECT() *MockDomainCRUDMockRecorder { return m.recorder } // DeleteDomain mocks base method. func (m *MockDomainCRUD) DeleteDomain(ctx context.Context, domainID, domainName *string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteDomain", ctx, domainID, domainName) ret0, _ := ret[0].(error) return ret0 } // DeleteDomain indicates an expected call of DeleteDomain. func (mr *MockDomainCRUDMockRecorder) DeleteDomain(ctx, domainID, domainName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDomain", reflect.TypeOf((*MockDomainCRUD)(nil).DeleteDomain), ctx, domainID, domainName) } // InsertDomain mocks base method. func (m *MockDomainCRUD) InsertDomain(ctx context.Context, row *DomainRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertDomain", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertDomain indicates an expected call of InsertDomain. func (mr *MockDomainCRUDMockRecorder) InsertDomain(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertDomain", reflect.TypeOf((*MockDomainCRUD)(nil).InsertDomain), ctx, row) } // SelectAllDomains mocks base method. func (m *MockDomainCRUD) SelectAllDomains(ctx context.Context, pageSize int, pageToken []byte) ([]*DomainRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllDomains", ctx, pageSize, pageToken) ret0, _ := ret[0].([]*DomainRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllDomains indicates an expected call of SelectAllDomains. func (mr *MockDomainCRUDMockRecorder) SelectAllDomains(ctx, pageSize, pageToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllDomains", reflect.TypeOf((*MockDomainCRUD)(nil).SelectAllDomains), ctx, pageSize, pageToken) } // SelectDomain mocks base method. func (m *MockDomainCRUD) SelectDomain(ctx context.Context, domainID, domainName *string) (*DomainRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectDomain", ctx, domainID, domainName) ret0, _ := ret[0].(*DomainRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectDomain indicates an expected call of SelectDomain. func (mr *MockDomainCRUDMockRecorder) SelectDomain(ctx, domainID, domainName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDomain", reflect.TypeOf((*MockDomainCRUD)(nil).SelectDomain), ctx, domainID, domainName) } // SelectDomainMetadata mocks base method. func (m *MockDomainCRUD) SelectDomainMetadata(ctx context.Context) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectDomainMetadata", ctx) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectDomainMetadata indicates an expected call of SelectDomainMetadata. func (mr *MockDomainCRUDMockRecorder) SelectDomainMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDomainMetadata", reflect.TypeOf((*MockDomainCRUD)(nil).SelectDomainMetadata), ctx) } // UpdateDomain mocks base method. func (m *MockDomainCRUD) UpdateDomain(ctx context.Context, row *DomainRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomain", ctx, row) ret0, _ := ret[0].(error) return ret0 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MockDomainCRUDMockRecorder) UpdateDomain(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MockDomainCRUD)(nil).UpdateDomain), ctx, row) } // MockShardCRUD is a mock of ShardCRUD interface. type MockShardCRUD struct { ctrl *gomock.Controller recorder *MockShardCRUDMockRecorder isgomock struct{} } // MockShardCRUDMockRecorder is the mock recorder for MockShardCRUD. type MockShardCRUDMockRecorder struct { mock *MockShardCRUD } // NewMockShardCRUD creates a new mock instance. func NewMockShardCRUD(ctrl *gomock.Controller) *MockShardCRUD { mock := &MockShardCRUD{ctrl: ctrl} mock.recorder = &MockShardCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShardCRUD) EXPECT() *MockShardCRUDMockRecorder { return m.recorder } // InsertShard mocks base method. func (m *MockShardCRUD) InsertShard(ctx context.Context, row *ShardRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertShard", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertShard indicates an expected call of InsertShard. func (mr *MockShardCRUDMockRecorder) InsertShard(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertShard", reflect.TypeOf((*MockShardCRUD)(nil).InsertShard), ctx, row) } // SelectShard mocks base method. func (m *MockShardCRUD) SelectShard(ctx context.Context, shardID int, currentClusterName string) (int64, *ShardRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectShard", ctx, shardID, currentClusterName) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(*ShardRow) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectShard indicates an expected call of SelectShard. func (mr *MockShardCRUDMockRecorder) SelectShard(ctx, shardID, currentClusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectShard", reflect.TypeOf((*MockShardCRUD)(nil).SelectShard), ctx, shardID, currentClusterName) } // UpdateRangeID mocks base method. func (m *MockShardCRUD) UpdateRangeID(ctx context.Context, shardID int, rangeID, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateRangeID", ctx, shardID, rangeID, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateRangeID indicates an expected call of UpdateRangeID. func (mr *MockShardCRUDMockRecorder) UpdateRangeID(ctx, shardID, rangeID, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRangeID", reflect.TypeOf((*MockShardCRUD)(nil).UpdateRangeID), ctx, shardID, rangeID, previousRangeID) } // UpdateShard mocks base method. func (m *MockShardCRUD) UpdateShard(ctx context.Context, row *ShardRow, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateShard", ctx, row, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateShard indicates an expected call of UpdateShard. func (mr *MockShardCRUDMockRecorder) UpdateShard(ctx, row, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateShard", reflect.TypeOf((*MockShardCRUD)(nil).UpdateShard), ctx, row, previousRangeID) } // MockVisibilityCRUD is a mock of VisibilityCRUD interface. type MockVisibilityCRUD struct { ctrl *gomock.Controller recorder *MockVisibilityCRUDMockRecorder isgomock struct{} } // MockVisibilityCRUDMockRecorder is the mock recorder for MockVisibilityCRUD. type MockVisibilityCRUDMockRecorder struct { mock *MockVisibilityCRUD } // NewMockVisibilityCRUD creates a new mock instance. func NewMockVisibilityCRUD(ctrl *gomock.Controller) *MockVisibilityCRUD { mock := &MockVisibilityCRUD{ctrl: ctrl} mock.recorder = &MockVisibilityCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockVisibilityCRUD) EXPECT() *MockVisibilityCRUDMockRecorder { return m.recorder } // DeleteVisibility mocks base method. func (m *MockVisibilityCRUD) DeleteVisibility(ctx context.Context, domainID, workflowID, runID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteVisibility", ctx, domainID, workflowID, runID) ret0, _ := ret[0].(error) return ret0 } // DeleteVisibility indicates an expected call of DeleteVisibility. func (mr *MockVisibilityCRUDMockRecorder) DeleteVisibility(ctx, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVisibility", reflect.TypeOf((*MockVisibilityCRUD)(nil).DeleteVisibility), ctx, domainID, workflowID, runID) } // InsertVisibility mocks base method. func (m *MockVisibilityCRUD) InsertVisibility(ctx context.Context, ttlSeconds int64, row *VisibilityRowForInsert) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertVisibility", ctx, ttlSeconds, row) ret0, _ := ret[0].(error) return ret0 } // InsertVisibility indicates an expected call of InsertVisibility. func (mr *MockVisibilityCRUDMockRecorder) InsertVisibility(ctx, ttlSeconds, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertVisibility", reflect.TypeOf((*MockVisibilityCRUD)(nil).InsertVisibility), ctx, ttlSeconds, row) } // SelectOneClosedWorkflow mocks base method. func (m *MockVisibilityCRUD) SelectOneClosedWorkflow(ctx context.Context, domainID, workflowID, runID string) (*VisibilityRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectOneClosedWorkflow", ctx, domainID, workflowID, runID) ret0, _ := ret[0].(*VisibilityRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectOneClosedWorkflow indicates an expected call of SelectOneClosedWorkflow. func (mr *MockVisibilityCRUDMockRecorder) SelectOneClosedWorkflow(ctx, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectOneClosedWorkflow", reflect.TypeOf((*MockVisibilityCRUD)(nil).SelectOneClosedWorkflow), ctx, domainID, workflowID, runID) } // SelectVisibility mocks base method. func (m *MockVisibilityCRUD) SelectVisibility(ctx context.Context, filter *VisibilityFilter) (*SelectVisibilityResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectVisibility", ctx, filter) ret0, _ := ret[0].(*SelectVisibilityResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectVisibility indicates an expected call of SelectVisibility. func (mr *MockVisibilityCRUDMockRecorder) SelectVisibility(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectVisibility", reflect.TypeOf((*MockVisibilityCRUD)(nil).SelectVisibility), ctx, filter) } // UpdateVisibility mocks base method. func (m *MockVisibilityCRUD) UpdateVisibility(ctx context.Context, ttlSeconds int64, row *VisibilityRowForUpdate) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateVisibility", ctx, ttlSeconds, row) ret0, _ := ret[0].(error) return ret0 } // UpdateVisibility indicates an expected call of UpdateVisibility. func (mr *MockVisibilityCRUDMockRecorder) UpdateVisibility(ctx, ttlSeconds, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateVisibility", reflect.TypeOf((*MockVisibilityCRUD)(nil).UpdateVisibility), ctx, ttlSeconds, row) } // MockTaskCRUD is a mock of TaskCRUD interface. type MockTaskCRUD struct { ctrl *gomock.Controller recorder *MockTaskCRUDMockRecorder isgomock struct{} } // MockTaskCRUDMockRecorder is the mock recorder for MockTaskCRUD. type MockTaskCRUDMockRecorder struct { mock *MockTaskCRUD } // NewMockTaskCRUD creates a new mock instance. func NewMockTaskCRUD(ctrl *gomock.Controller) *MockTaskCRUD { mock := &MockTaskCRUD{ctrl: ctrl} mock.recorder = &MockTaskCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskCRUD) EXPECT() *MockTaskCRUDMockRecorder { return m.recorder } // DeleteTaskList mocks base method. func (m *MockTaskCRUD) DeleteTaskList(ctx context.Context, filter *TaskListFilter, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTaskList", ctx, filter, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // DeleteTaskList indicates an expected call of DeleteTaskList. func (mr *MockTaskCRUDMockRecorder) DeleteTaskList(ctx, filter, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTaskList", reflect.TypeOf((*MockTaskCRUD)(nil).DeleteTaskList), ctx, filter, previousRangeID) } // GetTasksCount mocks base method. func (m *MockTaskCRUD) GetTasksCount(ctx context.Context, filter *TasksFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasksCount", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTasksCount indicates an expected call of GetTasksCount. func (mr *MockTaskCRUDMockRecorder) GetTasksCount(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasksCount", reflect.TypeOf((*MockTaskCRUD)(nil).GetTasksCount), ctx, filter) } // InsertTaskList mocks base method. func (m *MockTaskCRUD) InsertTaskList(ctx context.Context, row *TaskListRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertTaskList", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertTaskList indicates an expected call of InsertTaskList. func (mr *MockTaskCRUDMockRecorder) InsertTaskList(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertTaskList", reflect.TypeOf((*MockTaskCRUD)(nil).InsertTaskList), ctx, row) } // InsertTasks mocks base method. func (m *MockTaskCRUD) InsertTasks(ctx context.Context, tasksToInsert []*TaskRowForInsert, tasklistCondition *TaskListRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertTasks", ctx, tasksToInsert, tasklistCondition) ret0, _ := ret[0].(error) return ret0 } // InsertTasks indicates an expected call of InsertTasks. func (mr *MockTaskCRUDMockRecorder) InsertTasks(ctx, tasksToInsert, tasklistCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertTasks", reflect.TypeOf((*MockTaskCRUD)(nil).InsertTasks), ctx, tasksToInsert, tasklistCondition) } // ListTaskList mocks base method. func (m *MockTaskCRUD) ListTaskList(ctx context.Context, pageSize int, nextPageToken []byte) (*ListTaskListResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTaskList", ctx, pageSize, nextPageToken) ret0, _ := ret[0].(*ListTaskListResult) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTaskList indicates an expected call of ListTaskList. func (mr *MockTaskCRUDMockRecorder) ListTaskList(ctx, pageSize, nextPageToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTaskList", reflect.TypeOf((*MockTaskCRUD)(nil).ListTaskList), ctx, pageSize, nextPageToken) } // RangeDeleteTasks mocks base method. func (m *MockTaskCRUD) RangeDeleteTasks(ctx context.Context, filter *TasksFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteTasks", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteTasks indicates an expected call of RangeDeleteTasks. func (mr *MockTaskCRUDMockRecorder) RangeDeleteTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteTasks", reflect.TypeOf((*MockTaskCRUD)(nil).RangeDeleteTasks), ctx, filter) } // SelectTaskList mocks base method. func (m *MockTaskCRUD) SelectTaskList(ctx context.Context, filter *TaskListFilter) (*TaskListRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTaskList", ctx, filter) ret0, _ := ret[0].(*TaskListRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectTaskList indicates an expected call of SelectTaskList. func (mr *MockTaskCRUDMockRecorder) SelectTaskList(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTaskList", reflect.TypeOf((*MockTaskCRUD)(nil).SelectTaskList), ctx, filter) } // SelectTasks mocks base method. func (m *MockTaskCRUD) SelectTasks(ctx context.Context, filter *TasksFilter) ([]*TaskRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTasks", ctx, filter) ret0, _ := ret[0].([]*TaskRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectTasks indicates an expected call of SelectTasks. func (mr *MockTaskCRUDMockRecorder) SelectTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTasks", reflect.TypeOf((*MockTaskCRUD)(nil).SelectTasks), ctx, filter) } // UpdateTaskList mocks base method. func (m *MockTaskCRUD) UpdateTaskList(ctx context.Context, row *TaskListRow, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskList", ctx, row, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateTaskList indicates an expected call of UpdateTaskList. func (mr *MockTaskCRUDMockRecorder) UpdateTaskList(ctx, row, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskList", reflect.TypeOf((*MockTaskCRUD)(nil).UpdateTaskList), ctx, row, previousRangeID) } // UpdateTaskListWithTTL mocks base method. func (m *MockTaskCRUD) UpdateTaskListWithTTL(ctx context.Context, ttlSeconds int64, row *TaskListRow, previousRangeID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskListWithTTL", ctx, ttlSeconds, row, previousRangeID) ret0, _ := ret[0].(error) return ret0 } // UpdateTaskListWithTTL indicates an expected call of UpdateTaskListWithTTL. func (mr *MockTaskCRUDMockRecorder) UpdateTaskListWithTTL(ctx, ttlSeconds, row, previousRangeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListWithTTL", reflect.TypeOf((*MockTaskCRUD)(nil).UpdateTaskListWithTTL), ctx, ttlSeconds, row, previousRangeID) } // MockWorkflowCRUD is a mock of WorkflowCRUD interface. type MockWorkflowCRUD struct { ctrl *gomock.Controller recorder *MockWorkflowCRUDMockRecorder isgomock struct{} } // MockWorkflowCRUDMockRecorder is the mock recorder for MockWorkflowCRUD. type MockWorkflowCRUDMockRecorder struct { mock *MockWorkflowCRUD } // NewMockWorkflowCRUD creates a new mock instance. func NewMockWorkflowCRUD(ctrl *gomock.Controller) *MockWorkflowCRUD { mock := &MockWorkflowCRUD{ctrl: ctrl} mock.recorder = &MockWorkflowCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWorkflowCRUD) EXPECT() *MockWorkflowCRUDMockRecorder { return m.recorder } // DeleteActiveClusterSelectionPolicy mocks base method. func (m *MockWorkflowCRUD) DeleteActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, workflowID, runID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteActiveClusterSelectionPolicy", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(error) return ret0 } // DeleteActiveClusterSelectionPolicy indicates an expected call of DeleteActiveClusterSelectionPolicy. func (mr *MockWorkflowCRUDMockRecorder) DeleteActiveClusterSelectionPolicy(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteActiveClusterSelectionPolicy", reflect.TypeOf((*MockWorkflowCRUD)(nil).DeleteActiveClusterSelectionPolicy), ctx, shardID, domainID, workflowID, runID) } // DeleteCrossClusterTask mocks base method. func (m *MockWorkflowCRUD) DeleteCrossClusterTask(ctx context.Context, shardID int, targetCluster string, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteCrossClusterTask", ctx, shardID, targetCluster, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteCrossClusterTask indicates an expected call of DeleteCrossClusterTask. func (mr *MockWorkflowCRUDMockRecorder) DeleteCrossClusterTask(ctx, shardID, targetCluster, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCrossClusterTask", reflect.TypeOf((*MockWorkflowCRUD)(nil).DeleteCrossClusterTask), ctx, shardID, targetCluster, taskID) } // DeleteCurrentWorkflow mocks base method. func (m *MockWorkflowCRUD) DeleteCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID, currentRunIDCondition string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteCurrentWorkflow", ctx, shardID, domainID, workflowID, currentRunIDCondition) ret0, _ := ret[0].(error) return ret0 } // DeleteCurrentWorkflow indicates an expected call of DeleteCurrentWorkflow. func (mr *MockWorkflowCRUDMockRecorder) DeleteCurrentWorkflow(ctx, shardID, domainID, workflowID, currentRunIDCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentWorkflow", reflect.TypeOf((*MockWorkflowCRUD)(nil).DeleteCurrentWorkflow), ctx, shardID, domainID, workflowID, currentRunIDCondition) } // DeleteReplicationDLQTask mocks base method. func (m *MockWorkflowCRUD) DeleteReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteReplicationDLQTask", ctx, shardID, sourceCluster, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteReplicationDLQTask indicates an expected call of DeleteReplicationDLQTask. func (mr *MockWorkflowCRUDMockRecorder) DeleteReplicationDLQTask(ctx, shardID, sourceCluster, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationDLQTask", reflect.TypeOf((*MockWorkflowCRUD)(nil).DeleteReplicationDLQTask), ctx, shardID, sourceCluster, taskID) } // DeleteReplicationTask mocks base method. func (m *MockWorkflowCRUD) DeleteReplicationTask(ctx context.Context, shardID int, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteReplicationTask", ctx, shardID, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteReplicationTask indicates an expected call of DeleteReplicationTask. func (mr *MockWorkflowCRUDMockRecorder) DeleteReplicationTask(ctx, shardID, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReplicationTask", reflect.TypeOf((*MockWorkflowCRUD)(nil).DeleteReplicationTask), ctx, shardID, taskID) } // DeleteTimerTask mocks base method. func (m *MockWorkflowCRUD) DeleteTimerTask(ctx context.Context, shardID int, taskID int64, visibilityTimestamp time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTimerTask", ctx, shardID, taskID, visibilityTimestamp) ret0, _ := ret[0].(error) return ret0 } // DeleteTimerTask indicates an expected call of DeleteTimerTask. func (mr *MockWorkflowCRUDMockRecorder) DeleteTimerTask(ctx, shardID, taskID, visibilityTimestamp any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTimerTask", reflect.TypeOf((*MockWorkflowCRUD)(nil).DeleteTimerTask), ctx, shardID, taskID, visibilityTimestamp) } // DeleteTransferTask mocks base method. func (m *MockWorkflowCRUD) DeleteTransferTask(ctx context.Context, shardID int, taskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTransferTask", ctx, shardID, taskID) ret0, _ := ret[0].(error) return ret0 } // DeleteTransferTask indicates an expected call of DeleteTransferTask. func (mr *MockWorkflowCRUDMockRecorder) DeleteTransferTask(ctx, shardID, taskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTransferTask", reflect.TypeOf((*MockWorkflowCRUD)(nil).DeleteTransferTask), ctx, shardID, taskID) } // DeleteWorkflowExecution mocks base method. func (m *MockWorkflowCRUD) DeleteWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteWorkflowExecution", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(error) return ret0 } // DeleteWorkflowExecution indicates an expected call of DeleteWorkflowExecution. func (mr *MockWorkflowCRUDMockRecorder) DeleteWorkflowExecution(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWorkflowExecution", reflect.TypeOf((*MockWorkflowCRUD)(nil).DeleteWorkflowExecution), ctx, shardID, domainID, workflowID, runID) } // InsertReplicationDLQTask mocks base method. func (m *MockWorkflowCRUD) InsertReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, task *HistoryMigrationTask) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertReplicationDLQTask", ctx, shardID, sourceCluster, task) ret0, _ := ret[0].(error) return ret0 } // InsertReplicationDLQTask indicates an expected call of InsertReplicationDLQTask. func (mr *MockWorkflowCRUDMockRecorder) InsertReplicationDLQTask(ctx, shardID, sourceCluster, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertReplicationDLQTask", reflect.TypeOf((*MockWorkflowCRUD)(nil).InsertReplicationDLQTask), ctx, shardID, sourceCluster, task) } // InsertReplicationTask mocks base method. func (m *MockWorkflowCRUD) InsertReplicationTask(ctx context.Context, tasks []*HistoryMigrationTask, condition ShardCondition) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertReplicationTask", ctx, tasks, condition) ret0, _ := ret[0].(error) return ret0 } // InsertReplicationTask indicates an expected call of InsertReplicationTask. func (mr *MockWorkflowCRUDMockRecorder) InsertReplicationTask(ctx, tasks, condition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertReplicationTask", reflect.TypeOf((*MockWorkflowCRUD)(nil).InsertReplicationTask), ctx, tasks, condition) } // InsertWorkflowExecutionWithTasks mocks base method. func (m *MockWorkflowCRUD) InsertWorkflowExecutionWithTasks(ctx context.Context, requests *WorkflowRequestsWriteRequest, currentWorkflowRequest *CurrentWorkflowWriteRequest, execution *WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*HistoryMigrationTask, activeClusterSelectionPolicyRow *ActiveClusterSelectionPolicyRow, shardCondition *ShardCondition) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertWorkflowExecutionWithTasks", ctx, requests, currentWorkflowRequest, execution, tasksByCategory, activeClusterSelectionPolicyRow, shardCondition) ret0, _ := ret[0].(error) return ret0 } // InsertWorkflowExecutionWithTasks indicates an expected call of InsertWorkflowExecutionWithTasks. func (mr *MockWorkflowCRUDMockRecorder) InsertWorkflowExecutionWithTasks(ctx, requests, currentWorkflowRequest, execution, tasksByCategory, activeClusterSelectionPolicyRow, shardCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkflowExecutionWithTasks", reflect.TypeOf((*MockWorkflowCRUD)(nil).InsertWorkflowExecutionWithTasks), ctx, requests, currentWorkflowRequest, execution, tasksByCategory, activeClusterSelectionPolicyRow, shardCondition) } // IsWorkflowExecutionExists mocks base method. func (m *MockWorkflowCRUD) IsWorkflowExecutionExists(ctx context.Context, shardID int, domainID, workflowID, runID string) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsWorkflowExecutionExists", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsWorkflowExecutionExists indicates an expected call of IsWorkflowExecutionExists. func (mr *MockWorkflowCRUDMockRecorder) IsWorkflowExecutionExists(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWorkflowExecutionExists", reflect.TypeOf((*MockWorkflowCRUD)(nil).IsWorkflowExecutionExists), ctx, shardID, domainID, workflowID, runID) } // RangeDeleteReplicationDLQTasks mocks base method. func (m *MockWorkflowCRUD) RangeDeleteReplicationDLQTasks(ctx context.Context, shardID int, sourceCluster string, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteReplicationDLQTasks", ctx, shardID, sourceCluster, inclusiveBeginTaskID, exclusiveEndTaskID) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteReplicationDLQTasks indicates an expected call of RangeDeleteReplicationDLQTasks. func (mr *MockWorkflowCRUDMockRecorder) RangeDeleteReplicationDLQTasks(ctx, shardID, sourceCluster, inclusiveBeginTaskID, exclusiveEndTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteReplicationDLQTasks", reflect.TypeOf((*MockWorkflowCRUD)(nil).RangeDeleteReplicationDLQTasks), ctx, shardID, sourceCluster, inclusiveBeginTaskID, exclusiveEndTaskID) } // RangeDeleteReplicationTasks mocks base method. func (m *MockWorkflowCRUD) RangeDeleteReplicationTasks(ctx context.Context, shardID int, exclusiveEndTaskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteReplicationTasks", ctx, shardID, exclusiveEndTaskID) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteReplicationTasks indicates an expected call of RangeDeleteReplicationTasks. func (mr *MockWorkflowCRUDMockRecorder) RangeDeleteReplicationTasks(ctx, shardID, exclusiveEndTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteReplicationTasks", reflect.TypeOf((*MockWorkflowCRUD)(nil).RangeDeleteReplicationTasks), ctx, shardID, exclusiveEndTaskID) } // RangeDeleteTimerTasks mocks base method. func (m *MockWorkflowCRUD) RangeDeleteTimerTasks(ctx context.Context, shardID int, inclusiveMinTime, exclusiveMaxTime time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteTimerTasks", ctx, shardID, inclusiveMinTime, exclusiveMaxTime) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteTimerTasks indicates an expected call of RangeDeleteTimerTasks. func (mr *MockWorkflowCRUDMockRecorder) RangeDeleteTimerTasks(ctx, shardID, inclusiveMinTime, exclusiveMaxTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteTimerTasks", reflect.TypeOf((*MockWorkflowCRUD)(nil).RangeDeleteTimerTasks), ctx, shardID, inclusiveMinTime, exclusiveMaxTime) } // RangeDeleteTransferTasks mocks base method. func (m *MockWorkflowCRUD) RangeDeleteTransferTasks(ctx context.Context, shardID int, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteTransferTasks", ctx, shardID, inclusiveBeginTaskID, exclusiveEndTaskID) ret0, _ := ret[0].(error) return ret0 } // RangeDeleteTransferTasks indicates an expected call of RangeDeleteTransferTasks. func (mr *MockWorkflowCRUDMockRecorder) RangeDeleteTransferTasks(ctx, shardID, inclusiveBeginTaskID, exclusiveEndTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteTransferTasks", reflect.TypeOf((*MockWorkflowCRUD)(nil).RangeDeleteTransferTasks), ctx, shardID, inclusiveBeginTaskID, exclusiveEndTaskID) } // SelectActiveClusterSelectionPolicy mocks base method. func (m *MockWorkflowCRUD) SelectActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, wfID, rID string) (*ActiveClusterSelectionPolicyRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectActiveClusterSelectionPolicy", ctx, shardID, domainID, wfID, rID) ret0, _ := ret[0].(*ActiveClusterSelectionPolicyRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectActiveClusterSelectionPolicy indicates an expected call of SelectActiveClusterSelectionPolicy. func (mr *MockWorkflowCRUDMockRecorder) SelectActiveClusterSelectionPolicy(ctx, shardID, domainID, wfID, rID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectActiveClusterSelectionPolicy", reflect.TypeOf((*MockWorkflowCRUD)(nil).SelectActiveClusterSelectionPolicy), ctx, shardID, domainID, wfID, rID) } // SelectAllCurrentWorkflows mocks base method. func (m *MockWorkflowCRUD) SelectAllCurrentWorkflows(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.CurrentWorkflowExecution, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllCurrentWorkflows", ctx, shardID, pageToken, pageSize) ret0, _ := ret[0].([]*persistence.CurrentWorkflowExecution) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllCurrentWorkflows indicates an expected call of SelectAllCurrentWorkflows. func (mr *MockWorkflowCRUDMockRecorder) SelectAllCurrentWorkflows(ctx, shardID, pageToken, pageSize any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllCurrentWorkflows", reflect.TypeOf((*MockWorkflowCRUD)(nil).SelectAllCurrentWorkflows), ctx, shardID, pageToken, pageSize) } // SelectAllWorkflowExecutions mocks base method. func (m *MockWorkflowCRUD) SelectAllWorkflowExecutions(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.InternalListConcreteExecutionsEntity, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectAllWorkflowExecutions", ctx, shardID, pageToken, pageSize) ret0, _ := ret[0].([]*persistence.InternalListConcreteExecutionsEntity) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectAllWorkflowExecutions indicates an expected call of SelectAllWorkflowExecutions. func (mr *MockWorkflowCRUDMockRecorder) SelectAllWorkflowExecutions(ctx, shardID, pageToken, pageSize any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAllWorkflowExecutions", reflect.TypeOf((*MockWorkflowCRUD)(nil).SelectAllWorkflowExecutions), ctx, shardID, pageToken, pageSize) } // SelectCurrentWorkflow mocks base method. func (m *MockWorkflowCRUD) SelectCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID string) (*CurrentWorkflowRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectCurrentWorkflow", ctx, shardID, domainID, workflowID) ret0, _ := ret[0].(*CurrentWorkflowRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectCurrentWorkflow indicates an expected call of SelectCurrentWorkflow. func (mr *MockWorkflowCRUDMockRecorder) SelectCurrentWorkflow(ctx, shardID, domainID, workflowID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectCurrentWorkflow", reflect.TypeOf((*MockWorkflowCRUD)(nil).SelectCurrentWorkflow), ctx, shardID, domainID, workflowID) } // SelectReplicationDLQTasksCount mocks base method. func (m *MockWorkflowCRUD) SelectReplicationDLQTasksCount(ctx context.Context, shardID int, sourceCluster string) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectReplicationDLQTasksCount", ctx, shardID, sourceCluster) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectReplicationDLQTasksCount indicates an expected call of SelectReplicationDLQTasksCount. func (mr *MockWorkflowCRUDMockRecorder) SelectReplicationDLQTasksCount(ctx, shardID, sourceCluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectReplicationDLQTasksCount", reflect.TypeOf((*MockWorkflowCRUD)(nil).SelectReplicationDLQTasksCount), ctx, shardID, sourceCluster) } // SelectReplicationDLQTasksOrderByTaskID mocks base method. func (m *MockWorkflowCRUD) SelectReplicationDLQTasksOrderByTaskID(ctx context.Context, shardID int, sourceCluster string, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectReplicationDLQTasksOrderByTaskID", ctx, shardID, sourceCluster, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectReplicationDLQTasksOrderByTaskID indicates an expected call of SelectReplicationDLQTasksOrderByTaskID. func (mr *MockWorkflowCRUDMockRecorder) SelectReplicationDLQTasksOrderByTaskID(ctx, shardID, sourceCluster, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectReplicationDLQTasksOrderByTaskID", reflect.TypeOf((*MockWorkflowCRUD)(nil).SelectReplicationDLQTasksOrderByTaskID), ctx, shardID, sourceCluster, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) } // SelectReplicationTasksOrderByTaskID mocks base method. func (m *MockWorkflowCRUD) SelectReplicationTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectReplicationTasksOrderByTaskID", ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectReplicationTasksOrderByTaskID indicates an expected call of SelectReplicationTasksOrderByTaskID. func (mr *MockWorkflowCRUDMockRecorder) SelectReplicationTasksOrderByTaskID(ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectReplicationTasksOrderByTaskID", reflect.TypeOf((*MockWorkflowCRUD)(nil).SelectReplicationTasksOrderByTaskID), ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) } // SelectTimerTasksOrderByVisibilityTime mocks base method. func (m *MockWorkflowCRUD) SelectTimerTasksOrderByVisibilityTime(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTime, exclusiveMaxTime time.Time) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTimerTasksOrderByVisibilityTime", ctx, shardID, pageSize, pageToken, inclusiveMinTime, exclusiveMaxTime) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectTimerTasksOrderByVisibilityTime indicates an expected call of SelectTimerTasksOrderByVisibilityTime. func (mr *MockWorkflowCRUDMockRecorder) SelectTimerTasksOrderByVisibilityTime(ctx, shardID, pageSize, pageToken, inclusiveMinTime, exclusiveMaxTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTimerTasksOrderByVisibilityTime", reflect.TypeOf((*MockWorkflowCRUD)(nil).SelectTimerTasksOrderByVisibilityTime), ctx, shardID, pageSize, pageToken, inclusiveMinTime, exclusiveMaxTime) } // SelectTransferTasksOrderByTaskID mocks base method. func (m *MockWorkflowCRUD) SelectTransferTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*HistoryMigrationTask, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectTransferTasksOrderByTaskID", ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) ret0, _ := ret[0].([]*HistoryMigrationTask) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectTransferTasksOrderByTaskID indicates an expected call of SelectTransferTasksOrderByTaskID. func (mr *MockWorkflowCRUDMockRecorder) SelectTransferTasksOrderByTaskID(ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTransferTasksOrderByTaskID", reflect.TypeOf((*MockWorkflowCRUD)(nil).SelectTransferTasksOrderByTaskID), ctx, shardID, pageSize, pageToken, inclusiveMinTaskID, exclusiveMaxTaskID) } // SelectWorkflowExecution mocks base method. func (m *MockWorkflowCRUD) SelectWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) (*WorkflowExecution, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectWorkflowExecution", ctx, shardID, domainID, workflowID, runID) ret0, _ := ret[0].(*WorkflowExecution) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectWorkflowExecution indicates an expected call of SelectWorkflowExecution. func (mr *MockWorkflowCRUDMockRecorder) SelectWorkflowExecution(ctx, shardID, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectWorkflowExecution", reflect.TypeOf((*MockWorkflowCRUD)(nil).SelectWorkflowExecution), ctx, shardID, domainID, workflowID, runID) } // UpdateWorkflowExecutionWithTasks mocks base method. func (m *MockWorkflowCRUD) UpdateWorkflowExecutionWithTasks(ctx context.Context, requests *WorkflowRequestsWriteRequest, currentWorkflowRequest *CurrentWorkflowWriteRequest, mutatedExecution, insertedExecution *WorkflowExecutionRequest, activeClusterSelectionPolicyRow *ActiveClusterSelectionPolicyRow, resetExecution *WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*HistoryMigrationTask, shardCondition *ShardCondition) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecutionWithTasks", ctx, requests, currentWorkflowRequest, mutatedExecution, insertedExecution, activeClusterSelectionPolicyRow, resetExecution, tasksByCategory, shardCondition) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowExecutionWithTasks indicates an expected call of UpdateWorkflowExecutionWithTasks. func (mr *MockWorkflowCRUDMockRecorder) UpdateWorkflowExecutionWithTasks(ctx, requests, currentWorkflowRequest, mutatedExecution, insertedExecution, activeClusterSelectionPolicyRow, resetExecution, tasksByCategory, shardCondition any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecutionWithTasks", reflect.TypeOf((*MockWorkflowCRUD)(nil).UpdateWorkflowExecutionWithTasks), ctx, requests, currentWorkflowRequest, mutatedExecution, insertedExecution, activeClusterSelectionPolicyRow, resetExecution, tasksByCategory, shardCondition) } // MockConfigStoreCRUD is a mock of ConfigStoreCRUD interface. type MockConfigStoreCRUD struct { ctrl *gomock.Controller recorder *MockConfigStoreCRUDMockRecorder isgomock struct{} } // MockConfigStoreCRUDMockRecorder is the mock recorder for MockConfigStoreCRUD. type MockConfigStoreCRUDMockRecorder struct { mock *MockConfigStoreCRUD } // NewMockConfigStoreCRUD creates a new mock instance. func NewMockConfigStoreCRUD(ctrl *gomock.Controller) *MockConfigStoreCRUD { mock := &MockConfigStoreCRUD{ctrl: ctrl} mock.recorder = &MockConfigStoreCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockConfigStoreCRUD) EXPECT() *MockConfigStoreCRUDMockRecorder { return m.recorder } // InsertConfig mocks base method. func (m *MockConfigStoreCRUD) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertConfig", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertConfig indicates an expected call of InsertConfig. func (mr *MockConfigStoreCRUDMockRecorder) InsertConfig(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertConfig", reflect.TypeOf((*MockConfigStoreCRUD)(nil).InsertConfig), ctx, row) } // SelectLatestConfig mocks base method. func (m *MockConfigStoreCRUD) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectLatestConfig", ctx, rowType) ret0, _ := ret[0].(*persistence.InternalConfigStoreEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectLatestConfig indicates an expected call of SelectLatestConfig. func (mr *MockConfigStoreCRUDMockRecorder) SelectLatestConfig(ctx, rowType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectLatestConfig", reflect.TypeOf((*MockConfigStoreCRUD)(nil).SelectLatestConfig), ctx, rowType) } // MockDomainAuditLogCRUD is a mock of DomainAuditLogCRUD interface. type MockDomainAuditLogCRUD struct { ctrl *gomock.Controller recorder *MockDomainAuditLogCRUDMockRecorder isgomock struct{} } // MockDomainAuditLogCRUDMockRecorder is the mock recorder for MockDomainAuditLogCRUD. type MockDomainAuditLogCRUDMockRecorder struct { mock *MockDomainAuditLogCRUD } // NewMockDomainAuditLogCRUD creates a new mock instance. func NewMockDomainAuditLogCRUD(ctrl *gomock.Controller) *MockDomainAuditLogCRUD { mock := &MockDomainAuditLogCRUD{ctrl: ctrl} mock.recorder = &MockDomainAuditLogCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDomainAuditLogCRUD) EXPECT() *MockDomainAuditLogCRUDMockRecorder { return m.recorder } // InsertDomainAuditLog mocks base method. func (m *MockDomainAuditLogCRUD) InsertDomainAuditLog(ctx context.Context, row *DomainAuditLogRow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertDomainAuditLog", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertDomainAuditLog indicates an expected call of InsertDomainAuditLog. func (mr *MockDomainAuditLogCRUDMockRecorder) InsertDomainAuditLog(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertDomainAuditLog", reflect.TypeOf((*MockDomainAuditLogCRUD)(nil).InsertDomainAuditLog), ctx, row) } // SelectDomainAuditLogs mocks base method. func (m *MockDomainAuditLogCRUD) SelectDomainAuditLogs(ctx context.Context, filter *DomainAuditLogFilter) ([]*DomainAuditLogRow, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectDomainAuditLogs", ctx, filter) ret0, _ := ret[0].([]*DomainAuditLogRow) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SelectDomainAuditLogs indicates an expected call of SelectDomainAuditLogs. func (mr *MockDomainAuditLogCRUDMockRecorder) SelectDomainAuditLogs(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDomainAuditLogs", reflect.TypeOf((*MockDomainAuditLogCRUD)(nil).SelectDomainAuditLogs), ctx, filter) } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/admin.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "io/ioutil" "go.mongodb.org/mongo-driver/bson" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) var _ nosqlplugin.AdminDB = (*mdb)(nil) const ( testSchemaDir = "schema/mongodb/" ) func (db *mdb) SetupTestDatabase(schemaBaseDir string, replicas int) error { if schemaBaseDir == "" { var err error schemaBaseDir, err = nosqlplugin.GetDefaultTestSchemaDir(testSchemaDir) if err != nil { return err } } schemaFile := schemaBaseDir + "cadence/schema.json" byteValues, err := ioutil.ReadFile(schemaFile) if err != nil { return err } var commands []interface{} err = bson.UnmarshalExtJSON(byteValues, false, &commands) if err != nil { return err } for _, cmd := range commands { result := db.dbConn.RunCommand(context.Background(), cmd) if result.Err() != nil { return result.Err() } } return nil } func (db *mdb) TeardownTestDatabase() error { result := db.dbConn.RunCommand(context.Background(), bson.D{{"dropDatabase", 1}}) err := result.Err() return err } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/configStore.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "time" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/schema/mongodb/cadence" ) func (db *mdb) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { collection := db.dbConn.Collection(cadence.ClusterConfigCollectionName) doc := cadence.ClusterConfigCollectionEntry{ RowType: row.RowType, Version: row.Version, UnixTimestampSeconds: row.Timestamp.Unix(), Data: row.Values.Data, DataEncoding: row.Values.GetEncodingString(), } _, err := collection.InsertOne(ctx, doc) if mongo.IsDuplicateKeyError(err) { return nosqlplugin.NewConditionFailure("InsertConfig operation failed because of version collision") } return err } func (db *mdb) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { filter := bson.D{{"rowtype", rowType}} queryOptions := options.FindOneOptions{} queryOptions.SetSort(bson.D{{"version", -1}}) collection := db.dbConn.Collection(cadence.ClusterConfigCollectionName) var result cadence.ClusterConfigCollectionEntry err := collection.FindOne(ctx, filter, &queryOptions).Decode(&result) if err != nil { return nil, err } return &persistence.InternalConfigStoreEntry{ RowType: rowType, Version: result.Version, Timestamp: time.Unix(result.UnixTimestampSeconds, 0), Values: persistence.NewDataBlob(result.Data, constants.EncodingType(result.DataEncoding)), }, nil } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/db.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "go.mongodb.org/mongo-driver/mongo" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // mdb represents a logical connection to MongoDB database type mdb struct { client *mongo.Client dbConn *mongo.Database cfg *config.NoSQL logger log.Logger } var _ nosqlplugin.DB = (*mdb)(nil) func (db *mdb) Close() { db.client.Disconnect(context.Background()) } func (db *mdb) PluginName() string { return PluginName } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/domain.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // Insert a new record to domain, return error if failed or already exists // Return ConditionFailure if the condition doesn't meet func (db *mdb) InsertDomain( ctx context.Context, row *nosqlplugin.DomainRow, ) error { panic("TODO") } // Update domain func (db *mdb) UpdateDomain( ctx context.Context, row *nosqlplugin.DomainRow, ) error { panic("TODO") } // Get one domain data, either by domainID or domainName func (db *mdb) SelectDomain( ctx context.Context, domainID *string, domainName *string, ) (*nosqlplugin.DomainRow, error) { panic("TODO") } // Get all domain data func (db *mdb) SelectAllDomains( ctx context.Context, pageSize int, pageToken []byte, ) ([]*nosqlplugin.DomainRow, []byte, error) { panic("TODO") } // Delete a domain, either by domainID or domainName func (db *mdb) DeleteDomain( ctx context.Context, domainID *string, domainName *string, ) error { panic("TODO") } func (db *mdb) SelectDomainMetadata( ctx context.Context, ) (int64, error) { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/domain_audit_log.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "fmt" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // InsertDomainAuditLog inserts a new audit log entry for a domain operation func (db *mdb) InsertDomainAuditLog(ctx context.Context, row *nosqlplugin.DomainAuditLogRow) error { return fmt.Errorf("InsertDomainAuditLog not implemented") } // SelectDomainAuditLogs returns audit log entries for a domain and operation type func (db *mdb) SelectDomainAuditLogs(ctx context.Context, filter *nosqlplugin.DomainAuditLogFilter) ([]*nosqlplugin.DomainAuditLogRow, []byte, error) { return nil, nil, fmt.Errorf("SelectDomainAuditLogs not implemented") } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/error.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import "go.mongodb.org/mongo-driver/mongo" func (db *mdb) IsNotFoundError(err error) bool { return err == mongo.ErrNoDocuments } func (db *mdb) IsTimeoutError(err error) bool { return mongo.IsTimeout(err) || mongo.IsNetworkError(err) } func (db *mdb) IsThrottlingError(err error) bool { return false } func (db *mdb) IsDBUnavailableError(err error) bool { return false } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/events.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // InsertIntoHistoryTreeAndNode inserts one or two rows: tree row and node row(at least one of them) func (db *mdb) InsertIntoHistoryTreeAndNode(ctx context.Context, treeRow *nosqlplugin.HistoryTreeRow, nodeRow *nosqlplugin.HistoryNodeRow) error { panic("TODO") } // SelectFromHistoryNode read nodes based on a filter func (db *mdb) SelectFromHistoryNode(ctx context.Context, filter *nosqlplugin.HistoryNodeFilter) ([]*nosqlplugin.HistoryNodeRow, []byte, error) { panic("TODO") } // DeleteFromHistoryTreeAndNode delete a branch record, and a list of ranges of nodes. func (db *mdb) DeleteFromHistoryTreeAndNode(ctx context.Context, treeFilter *nosqlplugin.HistoryTreeFilter, nodeFilters []*nosqlplugin.HistoryNodeFilter) error { panic("TODO") } // SelectAllHistoryTrees will return all tree branches with pagination func (db *mdb) SelectAllHistoryTrees(ctx context.Context, nextPageToken []byte, pageSize int) ([]*nosqlplugin.HistoryTreeRow, []byte, error) { panic("TODO") } // SelectFromHistoryTree read branch records for a tree func (db *mdb) SelectFromHistoryTree(ctx context.Context, filter *nosqlplugin.HistoryTreeFilter) ([]*nosqlplugin.HistoryTreeRow, error) { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/plugin.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "fmt" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) const ( // PluginName is the name of the plugin PluginName = "mongodb" ) type plugin struct{} var _ nosqlplugin.Plugin = (*plugin)(nil) func init() { nosql.RegisterPlugin(PluginName, &plugin{}) } // CreateDB initialize the db object func (p *plugin) CreateDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (nosqlplugin.DB, error) { return p.doCreateDB(cfg, logger) } // CreateAdminDB initialize the AdminDB object func (p *plugin) CreateAdminDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (nosqlplugin.AdminDB, error) { return p.doCreateDB(cfg, logger) } func (p *plugin) doCreateDB(cfg *config.NoSQL, logger log.Logger) (*mdb, error) { uri := fmt.Sprintf("mongodb://%v:%v@%v:%v/", cfg.User, cfg.Password, cfg.Hosts, cfg.Port) // TODO CreateDB/CreateAdminDB don't pass in context.Context so we are using background for now // It's okay because this is being called during server startup or CLI. client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(uri)) if err != nil { return nil, err } if cfg.Keyspace == "" { return nil, fmt.Errorf("database name cannot be empty") } db := client.Database(cfg.Keyspace) return &mdb{ client: client, dbConn: db, cfg: cfg, logger: logger, }, err } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/queue.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "fmt" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // Insert message into queue, return error if failed or already exists // Return ConditionFailure if the condition doesn't meet func (db *mdb) InsertIntoQueue( ctx context.Context, row *nosqlplugin.QueueMessageRow, ) error { panic("TODO") } // Get the ID of last message inserted into the queue func (db *mdb) SelectLastEnqueuedMessageID( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { panic("TODO") } // Read queue messages starting from the exclusiveBeginMessageID func (db *mdb) SelectMessagesFrom( ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, maxRows int, ) ([]*nosqlplugin.QueueMessageRow, error) { panic("TODO") } // Read queue message starting from exclusiveBeginMessageID int64, inclusiveEndMessageID int64 func (db *mdb) SelectMessagesBetween( ctx context.Context, request nosqlplugin.SelectMessagesBetweenRequest, ) (*nosqlplugin.SelectMessagesBetweenResponse, error) { panic("TODO") } // Delete all messages before exclusiveBeginMessageID func (db *mdb) DeleteMessagesBefore( ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, ) error { panic("TODO") } // Delete all messages in a range between exclusiveBeginMessageID and inclusiveEndMessageID func (db *mdb) DeleteMessagesInRange( ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, inclusiveEndMessageID int64, ) error { panic("TODO") } // Delete one message func (db *mdb) DeleteMessage( ctx context.Context, queueType persistence.QueueType, messageID int64, ) error { panic("TODO") } // Insert an empty metadata row, starting from a version func (db *mdb) InsertQueueMetadata(ctx context.Context, row nosqlplugin.QueueMetadataRow) error { fmt.Println("not implemented, ignore the eror for testing") return nil } // **Conditionally** update a queue metadata row, if current version is matched(meaning current == row.Version - 1), // then the current version will increase by one when updating the metadata row // Return ConditionFailure if the condition doesn't meet func (db *mdb) UpdateQueueMetadataCas( ctx context.Context, row nosqlplugin.QueueMetadataRow, ) error { panic("TODO") } // Read a QueueMetadata func (db *mdb) SelectQueueMetadata( ctx context.Context, queueType persistence.QueueType, ) (*nosqlplugin.QueueMetadataRow, error) { fmt.Println("not implemented, ignore the eror for testing") return nil, nil } func (db *mdb) GetQueueSize( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/shard.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "log" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // InsertShard creates a new shard, return error is there is any. // Return ShardOperationConditionFailure if the condition doesn't meet func (db *mdb) InsertShard(ctx context.Context, row *nosqlplugin.ShardRow) error { log.Println("not implemented...ignore the error for testing...") return nil } // SelectShard gets a shard func (db *mdb) SelectShard(ctx context.Context, shardID int, currentClusterName string) (int64, *nosqlplugin.ShardRow, error) { panic("TODO") } // UpdateRangeID updates the rangeID, return error is there is any // Return ShardOperationConditionFailure if the condition doesn't meet func (db *mdb) UpdateRangeID(ctx context.Context, shardID int, rangeID int64, previousRangeID int64) error { panic("TODO") } // UpdateShard updates a shard, return error is there is any. // Return ShardOperationConditionFailure if the condition doesn't meet func (db *mdb) UpdateShard(ctx context.Context, row *nosqlplugin.ShardRow, previousRangeID int64) error { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/task.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) // SelectTaskList returns a single tasklist row. // Return IsNotFoundError if the row doesn't exist func (db *mdb) SelectTaskList(ctx context.Context, filter *nosqlplugin.TaskListFilter) (*nosqlplugin.TaskListRow, error) { panic("TODO") } // InsertTaskList insert a single tasklist row // Return IsConditionFailedError if the row already exists, and also the existing row func (db *mdb) InsertTaskList(ctx context.Context, row *nosqlplugin.TaskListRow) error { panic("TODO") } // UpdateTaskList updates a single tasklist row // Return TaskOperationConditionFailure if the condition doesn't meet func (db *mdb) UpdateTaskList( ctx context.Context, row *nosqlplugin.TaskListRow, previousRangeID int64, ) error { panic("TODO") } // UpdateTaskList updates a single tasklist row, and set an TTL on the record // Return TaskOperationConditionFailure if the condition doesn't meet // Ignore TTL if it's not supported, which becomes exactly the same as UpdateTaskList, but ListTaskList must be // implemented for TaskListScavenger func (db *mdb) UpdateTaskListWithTTL( ctx context.Context, ttlSeconds int64, row *nosqlplugin.TaskListRow, previousRangeID int64, ) error { panic("TODO") } // ListTaskList returns all tasklists. // Noop if TTL is already implemented in other methods func (db *mdb) ListTaskList(ctx context.Context, pageSize int, nextPageToken []byte) (*nosqlplugin.ListTaskListResult, error) { panic("TODO") } // DeleteTaskList deletes a single tasklist row // Return TaskOperationConditionFailure if the condition doesn't meet func (db *mdb) DeleteTaskList(ctx context.Context, filter *nosqlplugin.TaskListFilter, previousRangeID int64) error { panic("TODO") } // InsertTasks inserts a batch of tasks // Return TaskOperationConditionFailure if the condition doesn't meet func (db *mdb) InsertTasks( ctx context.Context, tasksToInsert []*nosqlplugin.TaskRowForInsert, tasklistCondition *nosqlplugin.TaskListRow, ) error { panic("TODO") } // SelectTasks return tasks that associated to a tasklist func (db *mdb) SelectTasks(ctx context.Context, filter *nosqlplugin.TasksFilter) ([]*nosqlplugin.TaskRow, error) { panic("TODO") } func (db *mdb) GetTasksCount(ctx context.Context, filter *nosqlplugin.TasksFilter) (int64, error) { panic("TODO") } // DeleteTask delete a batch tasks that taskIDs less than the row // If TTL is not implemented, then should also return the number of rows deleted, otherwise persistence.UnknownNumRowsAffected // NOTE: This API ignores the `BatchSize` request parameter i.e. either all tasks leq the task_id will be deleted or an error will // be returned to the caller, because rowsDeleted is not supported by Cassandra func (db *mdb) RangeDeleteTasks(ctx context.Context, filter *nosqlplugin.TasksFilter) (rowsDeleted int, err error) { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/visibility.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) func (db *mdb) InsertVisibility( ctx context.Context, ttlSeconds int64, row *nosqlplugin.VisibilityRowForInsert, ) error { panic("TODO") } func (db *mdb) UpdateVisibility( ctx context.Context, ttlSeconds int64, row *nosqlplugin.VisibilityRowForUpdate, ) error { panic("TODO") } func (db *mdb) SelectVisibility( ctx context.Context, filter *nosqlplugin.VisibilityFilter, ) (*nosqlplugin.SelectVisibilityResponse, error) { panic("TODO") } func (db *mdb) DeleteVisibility( ctx context.Context, domainID, workflowID, runID string, ) error { panic("TODO") } func (db *mdb) SelectOneClosedWorkflow( ctx context.Context, domainID, workflowID, runID string, ) (*nosqlplugin.VisibilityRow, error) { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/mongodb/workflow.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb import ( "context" "time" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) var _ nosqlplugin.WorkflowCRUD = (*mdb)(nil) func (db *mdb) InsertWorkflowExecutionWithTasks( ctx context.Context, requests *nosqlplugin.WorkflowRequestsWriteRequest, currentWorkflowRequest *nosqlplugin.CurrentWorkflowWriteRequest, execution *nosqlplugin.WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask, activeClusterSelectionPolicyRow *nosqlplugin.ActiveClusterSelectionPolicyRow, shardCondition *nosqlplugin.ShardCondition, ) error { panic("TODO") } func (db *mdb) UpdateWorkflowExecutionWithTasks( ctx context.Context, requests *nosqlplugin.WorkflowRequestsWriteRequest, currentWorkflowRequest *nosqlplugin.CurrentWorkflowWriteRequest, mutatedExecution *nosqlplugin.WorkflowExecutionRequest, insertedExecution *nosqlplugin.WorkflowExecutionRequest, activeClusterSelectionPolicyRow *nosqlplugin.ActiveClusterSelectionPolicyRow, resetExecution *nosqlplugin.WorkflowExecutionRequest, tasksByCategory map[persistence.HistoryTaskCategory][]*nosqlplugin.HistoryMigrationTask, shardCondition *nosqlplugin.ShardCondition, ) error { panic("TODO") } func (db *mdb) SelectCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID string) (*nosqlplugin.CurrentWorkflowRow, error) { panic("TODO") } func (db *mdb) SelectWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) (*nosqlplugin.WorkflowExecution, error) { panic("TODO") } func (db *mdb) DeleteCurrentWorkflow(ctx context.Context, shardID int, domainID, workflowID, currentRunIDCondition string) error { panic("TODO") } func (db *mdb) DeleteWorkflowExecution(ctx context.Context, shardID int, domainID, workflowID, runID string) error { panic("TODO") } func (db *mdb) SelectAllCurrentWorkflows(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.CurrentWorkflowExecution, []byte, error) { panic("TODO") } func (db *mdb) SelectAllWorkflowExecutions(ctx context.Context, shardID int, pageToken []byte, pageSize int) ([]*persistence.InternalListConcreteExecutionsEntity, []byte, error) { panic("TODO") } func (db *mdb) IsWorkflowExecutionExists(ctx context.Context, shardID int, domainID, workflowID, runID string) (bool, error) { panic("TODO") } func (db *mdb) SelectTransferTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { panic("TODO") } func (db *mdb) DeleteTransferTask(ctx context.Context, shardID int, taskID int64) error { panic("TODO") } func (db *mdb) RangeDeleteTransferTasks(ctx context.Context, shardID int, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { panic("TODO") } func (db *mdb) SelectTimerTasksOrderByVisibilityTime(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTime, exclusiveMaxTime time.Time) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { panic("TODO") } func (db *mdb) DeleteTimerTask(ctx context.Context, shardID int, taskID int64, visibilityTimestamp time.Time) error { panic("TODO") } func (db *mdb) RangeDeleteTimerTasks(ctx context.Context, shardID int, inclusiveMinTime, exclusiveMaxTime time.Time) error { panic("TODO") } func (db *mdb) SelectReplicationTasksOrderByTaskID(ctx context.Context, shardID, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { panic("TODO") } func (db *mdb) DeleteReplicationTask(ctx context.Context, shardID int, taskID int64) error { panic("TODO") } func (db *mdb) RangeDeleteReplicationTasks(ctx context.Context, shardID int, exclusiveEndTaskID int64) error { panic("TODO") } func (db *mdb) InsertReplicationTask(ctx context.Context, tasks []*nosqlplugin.HistoryMigrationTask, condition nosqlplugin.ShardCondition) error { panic("TODO") } func (db *mdb) DeleteCrossClusterTask(ctx context.Context, shardID int, targetCluster string, taskID int64) error { panic("TODO") } func (db *mdb) InsertReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, task *nosqlplugin.HistoryMigrationTask) error { panic("TODO") } func (db *mdb) SelectReplicationDLQTasksOrderByTaskID(ctx context.Context, shardID int, sourceCluster string, pageSize int, pageToken []byte, inclusiveMinTaskID, exclusiveMaxTaskID int64) ([]*nosqlplugin.HistoryMigrationTask, []byte, error) { panic("TODO") } func (db *mdb) SelectReplicationDLQTasksCount(ctx context.Context, shardID int, sourceCluster string) (int64, error) { panic("TODO") } func (db *mdb) DeleteReplicationDLQTask(ctx context.Context, shardID int, sourceCluster string, taskID int64) error { panic("TODO") } func (db *mdb) RangeDeleteReplicationDLQTasks(ctx context.Context, shardID int, sourceCluster string, inclusiveBeginTaskID, exclusiveEndTaskID int64) error { panic("TODO") } func (db *mdb) SelectActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, wfID, rID string) (*nosqlplugin.ActiveClusterSelectionPolicyRow, error) { panic("TODO") } func (db *mdb) DeleteActiveClusterSelectionPolicy(ctx context.Context, shardID int, domainID, wfID, rID string) error { panic("TODO") } ================================================ FILE: common/persistence/nosql/nosqlplugin/types.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosqlplugin import ( "time" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // WorkflowExecution stores workflow execution metadata WorkflowExecution = persistence.InternalWorkflowMutableState // WorkflowExecutionRequest is for creating/updating a workflow execution WorkflowExecutionRequest struct { // basic information/data persistence.InternalWorkflowExecutionInfo VersionHistories *persistence.DataBlob Checksums *checksum.Checksum LastWriteVersion int64 CurrentTimeStamp time.Time // condition checking for updating execution info PreviousNextEventIDCondition *int64 // MapsWriteMode controls how to write into the six maps(activityInfoMap, timerInfoMap, childWorkflowInfoMap, signalInfoMap and signalRequestedIDs) MapsWriteMode WorkflowExecutionMapsWriteMode // For WorkflowExecutionMapsWriteMode of create, update and reset ActivityInfos map[int64]*persistence.InternalActivityInfo TimerInfos map[string]*persistence.TimerInfo ChildWorkflowInfos map[int64]*persistence.InternalChildExecutionInfo RequestCancelInfos map[int64]*persistence.RequestCancelInfo SignalInfos map[int64]*persistence.SignalInfo SignalRequestedIDs []string // This map has no value, hence use array to store keys // For WorkflowExecutionMapsWriteMode of update only ActivityInfoKeysToDelete []int64 TimerInfoKeysToDelete []string ChildWorkflowInfoKeysToDelete []int64 RequestCancelInfoKeysToDelete []int64 SignalInfoKeysToDelete []int64 SignalRequestedIDsKeysToDelete []string // EventBufferWriteMode controls how to write into the buffered event list // only needed for UpdateWorkflowExecutionWithTasks API EventBufferWriteMode EventBufferWriteMode // the batch of event to be appended, only for EventBufferWriteModeAppend NewBufferedEventBatch *persistence.DataBlob } // WorkflowExecutionMapsWriteMode controls how to write WorkflowExecutionMaps WorkflowExecutionMapsWriteMode int // EventBufferWriteMode controls how to write EventBuffer EventBufferWriteMode int // TimerTask is background timer task TimerTask = persistence.TimerTaskInfo // ReplicationTask is for replication ReplicationTask = persistence.InternalReplicationTaskInfo // CrossClusterTask is for cross cluster transfer task CrossClusterTask struct { TransferTask TargetCluster string } // TransferTask is for regular transfer task TransferTask = persistence.TransferTaskInfo HistoryMigrationTask struct { Transfer *TransferTask Timer *TimerTask Replication *ReplicationTask Task *persistence.DataBlob TaskID int64 ScheduledTime time.Time } // ShardCondition is the condition for making changes within a shard ShardCondition struct { ShardID int RangeID int64 } // CurrentWorkflowWriteRequest is for insert/update current_workflow record CurrentWorkflowWriteRequest struct { WriteMode CurrentWorkflowWriteMode Row CurrentWorkflowRow Condition *CurrentWorkflowWriteCondition } // CurrentWorkflowWriteCondition is the condition for updating current_workflow record CurrentWorkflowWriteCondition struct { CurrentRunID *string LastWriteVersion *int64 State *int } // CurrentWorkflowWriteMode controls how to write current_workflow CurrentWorkflowWriteMode int // CurrentWorkflowRow is the current_workflow row CurrentWorkflowRow struct { ShardID int DomainID string WorkflowID string RunID string State int CloseStatus int CreateRequestID string LastWriteVersion int64 } // WorkflowRequestRow is the request which has been applied to a workflow WorkflowRequestRow struct { ShardID int DomainID string WorkflowID string RequestType persistence.WorkflowRequestType RequestID string Version int64 RunID string } WorkflowRequestWriteMode int WorkflowRequestsWriteRequest struct { Rows []*WorkflowRequestRow WriteMode WorkflowRequestWriteMode } ActiveClusterSelectionPolicyRow struct { ShardID int DomainID string WorkflowID string RunID string Policy *persistence.DataBlob } // TasksFilter is for filtering tasks TasksFilter struct { TaskListFilter // Exclusive MinTaskID int64 // Inclusive MaxTaskID int64 BatchSize int } // TaskRowForInsert is the struct to inserting task TaskRowForInsert struct { TaskRow // <= 0 means no TTL TTLSeconds int } // TaskRow represent a task row TaskRow struct { DomainID string TaskListName string TaskListType int TaskID int64 WorkflowID string RunID string ScheduledID int64 Expiry time.Time CreatedTime time.Time PartitionConfig map[string]string } // TaskListFilter is for filtering tasklist TaskListFilter struct { DomainID string TaskListName string TaskListType int } // TaskListRow is a tasklist row TaskListRow struct { DomainID string TaskListName string TaskListType int RangeID int64 TaskListKind int AckLevel int64 CurrentTimeStamp time.Time LastUpdatedTime time.Time AdaptivePartitionConfig *persistence.TaskListPartitionConfig } // ListTaskListResult is the result of list tasklists ListTaskListResult struct { TaskLists []*TaskListRow NextPageToken []byte } // ShardRow is the same as persistence.InternalShardInfo // Separate them later when there is a need. ShardRow struct { *persistence.InternalShardInfo Data []byte DataEncoding string } // ConflictedShardRow contains the partial information about a shard returned when a conditional write fails ConflictedShardRow struct { ShardID int // PreviousRangeID is the condition of previous change that used for conditional update PreviousRangeID int64 // optional detailed information for logging purpose Details string } // DomainRow defines the row struct for queue message DomainRow struct { Info *persistence.DomainInfo Config *persistence.InternalDomainConfig ReplicationConfig *persistence.InternalDomainReplicationConfig ConfigVersion int64 FailoverVersion int64 FailoverNotificationVersion int64 PreviousFailoverVersion int64 FailoverEndTime *time.Time NotificationVersion int64 LastUpdatedTime time.Time IsGlobalDomain bool CurrentTimeStamp time.Time } // DomainAuditLogRow defines the row struct for domain audit log DomainAuditLogRow struct { DomainID string EventID string StateBefore []byte StateBeforeEncoding string StateAfter []byte StateAfterEncoding string OperationType persistence.DomainAuditOperationType CreatedTime time.Time LastUpdatedTime time.Time Identity string IdentityType string Comment string TTLSeconds int64 // TTL for the audit log entry in seconds } // DomainAuditLogFilter contains the filter criteria for querying domain audit logs DomainAuditLogFilter struct { DomainID string OperationType persistence.DomainAuditOperationType // MinCreatedTime is inclusive MinCreatedTime *time.Time // MaxCreatedTime is exclusive MaxCreatedTime *time.Time PageSize int NextPageToken []byte } // SelectMessagesBetweenRequest is a request struct for SelectMessagesBetween SelectMessagesBetweenRequest struct { QueueType persistence.QueueType ExclusiveBeginMessageID int64 InclusiveEndMessageID int64 PageSize int NextPageToken []byte } // SelectMessagesBetweenResponse is a response struct for SelectMessagesBetween SelectMessagesBetweenResponse struct { Rows []QueueMessageRow NextPageToken []byte } // QueueMessageRow defines the row struct for queue message QueueMessageRow struct { QueueType persistence.QueueType ID int64 Payload []byte CurrentTimeStamp time.Time } // QueueMetadataRow defines the row struct for metadata QueueMetadataRow struct { QueueType persistence.QueueType ClusterAckLevels map[string]int64 Version int64 CurrentTimeStamp time.Time } // HistoryNodeRow represents a row in history_node table HistoryNodeRow struct { ShardID int TreeID string BranchID string NodeID int64 // Note: use pointer so that it's easier to multiple by -1 if needed TxnID *int64 Data []byte DataEncoding string CreateTimestamp time.Time } // HistoryNodeFilter contains the column names within history_node table that // can be used to filter results through a WHERE clause HistoryNodeFilter struct { ShardID int TreeID string BranchID string // Inclusive MinNodeID int64 // Exclusive MaxNodeID int64 NextPageToken []byte PageSize int } // HistoryTreeRow represents a row in history_tree table HistoryTreeRow struct { ShardID int TreeID string BranchID string Ancestors []*types.HistoryBranchRange CreateTimestamp time.Time Info string } // HistoryTreeFilter contains the column names within history_tree table that // can be used to filter results through a WHERE clause HistoryTreeFilter struct { ShardID int TreeID string BranchID *string } ) const ( AllOpen VisibilityFilterType = iota AllClosed OpenByWorkflowType ClosedByWorkflowType OpenByWorkflowID ClosedByWorkflowID ClosedByClosedStatus ) // enums of VisibilitySortType const ( SortByStartTime VisibilitySortType = iota SortByClosedTime ) // enums of CurrentWorkflowWriteMode const ( CurrentWorkflowWriteModeNoop CurrentWorkflowWriteMode = iota CurrentWorkflowWriteModeUpdate CurrentWorkflowWriteModeInsert ) // enums of WorkflowExecutionMapsWriteMode const ( // WorkflowExecutionMapsWriteModeCreate will upsert new entry to maps WorkflowExecutionMapsWriteModeCreate WorkflowExecutionMapsWriteMode = iota // WorkflowExecutionMapsWriteModeUpdate will upsert new entry to maps and also delete entries from maps WorkflowExecutionMapsWriteModeUpdate // WorkflowExecutionMapsWriteModeReset will reset(override) the whole maps WorkflowExecutionMapsWriteModeReset ) const ( // EventBufferWriteModeNone is for not doing anything to the event buffer EventBufferWriteModeNone EventBufferWriteMode = iota // EventBufferWriteModeAppend will append a new event to the event buffer EventBufferWriteModeAppend // EventBufferWriteModeClear will clear(delete all event from) the event buffer EventBufferWriteModeClear ) const ( WorkflowRequestWriteModeInsert WorkflowRequestWriteMode = iota WorkflowRequestWriteModeUpsert ) // GetCurrentRunID returns the current runID func (w *CurrentWorkflowWriteCondition) GetCurrentRunID() string { if w == nil || w.CurrentRunID == nil { return "" } return *w.CurrentRunID } ================================================ FILE: common/persistence/nosql/plugin.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "fmt" "testing" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) var supportedPlugins = map[string]nosqlplugin.Plugin{} // RegisterPlugin will register a NoSQL plugin func RegisterPlugin(pluginName string, plugin nosqlplugin.Plugin) { if _, ok := supportedPlugins[pluginName]; ok { panic("plugin " + pluginName + " already registered") } supportedPlugins[pluginName] = plugin } // RegisterPluginForTest should be used only in tests to register the DB plugin and de-register at the end func RegisterPluginForTest(t *testing.T, pluginName string, plugin nosqlplugin.Plugin) { t.Cleanup(func() { delete(supportedPlugins, pluginName) }) supportedPlugins[pluginName] = plugin } // RegisterPluginIfNotExists will register a NoSQL plugin only if a plugin with same name has not already been registered func RegisterPluginIfNotExists(pluginName string, plugin nosqlplugin.Plugin) { if _, ok := supportedPlugins[pluginName]; !ok { supportedPlugins[pluginName] = plugin } } // PluginRegistered returns true if plugin with given name has been registered, false otherwise func PluginRegistered(pluginName string) bool { _, ok := supportedPlugins[pluginName] return ok } // GetRegisteredPluginNames returns the list of registered plugin names func GetRegisteredPluginNames() []string { var plugins []string for k := range supportedPlugins { plugins = append(plugins, k) } return plugins } // NewNoSQLDB creates a returns a reference to a logical connection to the // underlying NoSQL database. The returned object is to tied to a single // NoSQL database and the object can be used to perform CRUD operations on // the tables in the database func NewNoSQLDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (nosqlplugin.DB, error) { plugin, ok := supportedPlugins[cfg.PluginName] if !ok { return nil, fmt.Errorf("not supported plugin %v, only supported: %v", cfg.PluginName, supportedPlugins) } return plugin.CreateDB(cfg, logger, dc) } // NewNoSQLAdminDB returns a AdminDB func NewNoSQLAdminDB(cfg *config.NoSQL, logger log.Logger, dc *persistence.DynamicConfiguration) (nosqlplugin.AdminDB, error) { plugin, ok := supportedPlugins[cfg.PluginName] if !ok { return nil, fmt.Errorf("not supported plugin %v, only supported: %v", cfg.PluginName, supportedPlugins) } return plugin.CreateAdminDB(cfg, logger, dc) } ================================================ FILE: common/persistence/nosql/sharded_nosql_store.go ================================================ // Copyright (c) 2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination sharded_nosql_store_mock.go package nosql import ( "fmt" "sync" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) type shardedNosqlStore interface { GetStoreShardByHistoryShard(shardID int) (*nosqlStore, error) GetStoreShardByTaskList(domainID string, taskListName string, taskType int) (*nosqlStore, error) GetDefaultShard() nosqlStore Close() GetName() string GetShardingPolicy() shardingPolicy GetLogger() log.Logger GetMetricsClient() metrics.Client } // shardedNosqlStore is a store that may have one or more shards type shardedNosqlStoreImpl struct { sync.RWMutex config config.ShardedNoSQL dc *persistence.DynamicConfiguration logger log.Logger metricsClient metrics.Client connectedShards map[string]nosqlStore defaultShard nosqlStore shardingPolicy shardingPolicy } func newShardedNosqlStore(cfg config.ShardedNoSQL, logger log.Logger, metricsClient metrics.Client, dc *persistence.DynamicConfiguration) (shardedNosqlStore, error) { sn := shardedNosqlStoreImpl{ config: cfg, dc: dc, logger: logger, metricsClient: metricsClient, } // Connect to the default shard defaultShardName := cfg.DefaultShard store, err := sn.connectToShard(defaultShardName) if err != nil { return nil, err } sn.defaultShard = *store sn.connectedShards = map[string]nosqlStore{ defaultShardName: sn.defaultShard, } // Parse & validate the sharding policy sn.shardingPolicy, err = newShardingPolicy(logger, cfg) if err != nil { return nil, err } return &sn, nil } func (sn *shardedNosqlStoreImpl) GetStoreShardByHistoryShard(shardID int) (*nosqlStore, error) { shardName, err := sn.shardingPolicy.getHistoryShardName(shardID) if err != nil { return nil, err } return sn.getShard(shardName) } func (sn *shardedNosqlStoreImpl) GetStoreShardByTaskList(domainID string, taskListName string, taskType int) (*nosqlStore, error) { shardName := sn.shardingPolicy.getTaskListShardName(domainID, taskListName, taskType) return sn.getShard(shardName) } func (sn *shardedNosqlStoreImpl) GetDefaultShard() nosqlStore { return sn.defaultShard } func (sn *shardedNosqlStoreImpl) Close() { sn.RLock() defer sn.RUnlock() for name, shard := range sn.connectedShards { sn.logger.Warn("Closing store shard", tag.StoreShard(name)) shard.Close() } } func (sn *shardedNosqlStoreImpl) GetName() string { return "shardedNosql" } func (sn *shardedNosqlStoreImpl) GetShardingPolicy() shardingPolicy { return sn.shardingPolicy } func (sn *shardedNosqlStoreImpl) GetLogger() log.Logger { return sn.logger } func (sn *shardedNosqlStoreImpl) GetMetricsClient() metrics.Client { return sn.metricsClient } func (sn *shardedNosqlStoreImpl) getShard(shardName string) (*nosqlStore, error) { sn.RLock() shard, found := sn.connectedShards[shardName] sn.RUnlock() if found { return &shard, nil } _, ok := sn.config.Connections[shardName] if !ok { return nil, &ShardingError{ Message: fmt.Sprintf("Unknown db shard name: %v", shardName), } } sn.Lock() defer sn.Unlock() if shard, ok := sn.connectedShards[shardName]; ok { // read again to double-check return &shard, nil } s, err := sn.connectToShard(shardName) if err != nil { return nil, err } sn.connectedShards[shardName] = *s sn.logger.Info("Connected to store shard", tag.StoreShard(shardName)) return s, nil } func (sn *shardedNosqlStoreImpl) connectToShard(shardName string) (*nosqlStore, error) { cfg, ok := sn.config.Connections[shardName] if !ok { return nil, &ShardingError{ Message: fmt.Sprintf("Unknown db shard name: %v", shardName), } } sn.logger.Info("Connecting to store shard", tag.StoreShard(shardName)) db, err := NewNoSQLDB(cfg.NoSQLPlugin, sn.logger, sn.dc) if err != nil { sn.logger.Error("Failed to connect to store shard", tag.StoreShard(shardName), tag.Error(err)) return nil, err } shard := nosqlStore{ db: db, logger: sn.logger, dc: sn.dc, } return &shard, nil } ================================================ FILE: common/persistence/nosql/sharded_nosql_store_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: sharded_nosql_store.go // // Generated by this command: // // mockgen -package nosql -source sharded_nosql_store.go -destination sharded_nosql_store_mock.go // // Package nosql is a generated GoMock package. package nosql import ( reflect "reflect" gomock "go.uber.org/mock/gomock" log "github.com/uber/cadence/common/log" metrics "github.com/uber/cadence/common/metrics" ) // MockshardedNosqlStore is a mock of shardedNosqlStore interface. type MockshardedNosqlStore struct { ctrl *gomock.Controller recorder *MockshardedNosqlStoreMockRecorder isgomock struct{} } // MockshardedNosqlStoreMockRecorder is the mock recorder for MockshardedNosqlStore. type MockshardedNosqlStoreMockRecorder struct { mock *MockshardedNosqlStore } // NewMockshardedNosqlStore creates a new mock instance. func NewMockshardedNosqlStore(ctrl *gomock.Controller) *MockshardedNosqlStore { mock := &MockshardedNosqlStore{ctrl: ctrl} mock.recorder = &MockshardedNosqlStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockshardedNosqlStore) EXPECT() *MockshardedNosqlStoreMockRecorder { return m.recorder } // Close mocks base method. func (m *MockshardedNosqlStore) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockshardedNosqlStoreMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockshardedNosqlStore)(nil).Close)) } // GetDefaultShard mocks base method. func (m *MockshardedNosqlStore) GetDefaultShard() nosqlStore { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDefaultShard") ret0, _ := ret[0].(nosqlStore) return ret0 } // GetDefaultShard indicates an expected call of GetDefaultShard. func (mr *MockshardedNosqlStoreMockRecorder) GetDefaultShard() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultShard", reflect.TypeOf((*MockshardedNosqlStore)(nil).GetDefaultShard)) } // GetLogger mocks base method. func (m *MockshardedNosqlStore) GetLogger() log.Logger { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLogger") ret0, _ := ret[0].(log.Logger) return ret0 } // GetLogger indicates an expected call of GetLogger. func (mr *MockshardedNosqlStoreMockRecorder) GetLogger() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogger", reflect.TypeOf((*MockshardedNosqlStore)(nil).GetLogger)) } // GetMetricsClient mocks base method. func (m *MockshardedNosqlStore) GetMetricsClient() metrics.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMetricsClient") ret0, _ := ret[0].(metrics.Client) return ret0 } // GetMetricsClient indicates an expected call of GetMetricsClient. func (mr *MockshardedNosqlStoreMockRecorder) GetMetricsClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetricsClient", reflect.TypeOf((*MockshardedNosqlStore)(nil).GetMetricsClient)) } // GetName mocks base method. func (m *MockshardedNosqlStore) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockshardedNosqlStoreMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockshardedNosqlStore)(nil).GetName)) } // GetShardingPolicy mocks base method. func (m *MockshardedNosqlStore) GetShardingPolicy() shardingPolicy { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardingPolicy") ret0, _ := ret[0].(shardingPolicy) return ret0 } // GetShardingPolicy indicates an expected call of GetShardingPolicy. func (mr *MockshardedNosqlStoreMockRecorder) GetShardingPolicy() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardingPolicy", reflect.TypeOf((*MockshardedNosqlStore)(nil).GetShardingPolicy)) } // GetStoreShardByHistoryShard mocks base method. func (m *MockshardedNosqlStore) GetStoreShardByHistoryShard(shardID int) (*nosqlStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetStoreShardByHistoryShard", shardID) ret0, _ := ret[0].(*nosqlStore) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStoreShardByHistoryShard indicates an expected call of GetStoreShardByHistoryShard. func (mr *MockshardedNosqlStoreMockRecorder) GetStoreShardByHistoryShard(shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStoreShardByHistoryShard", reflect.TypeOf((*MockshardedNosqlStore)(nil).GetStoreShardByHistoryShard), shardID) } // GetStoreShardByTaskList mocks base method. func (m *MockshardedNosqlStore) GetStoreShardByTaskList(domainID, taskListName string, taskType int) (*nosqlStore, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetStoreShardByTaskList", domainID, taskListName, taskType) ret0, _ := ret[0].(*nosqlStore) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStoreShardByTaskList indicates an expected call of GetStoreShardByTaskList. func (mr *MockshardedNosqlStoreMockRecorder) GetStoreShardByTaskList(domainID, taskListName, taskType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStoreShardByTaskList", reflect.TypeOf((*MockshardedNosqlStore)(nil).GetStoreShardByTaskList), domainID, taskListName, taskType) } ================================================ FILE: common/persistence/nosql/sharded_nosql_store_test.go ================================================ // Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "errors" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" . "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" ) type shardedNosqlStoreTestSuite struct { suite.Suite *require.Assertions mockController *gomock.Controller mockPlugin *nosqlplugin.MockPlugin } func (s *shardedNosqlStoreTestSuite) SetupSuite() { s.mockController = gomock.NewController(s.T()) } func (s *shardedNosqlStoreTestSuite) TearDownSuite() { } func (s *shardedNosqlStoreTestSuite) SetupTest() { s.Assertions = require.New(s.T()) mockDB := nosqlplugin.NewMockDB(s.mockController) mockPlugin := nosqlplugin.NewMockPlugin(s.mockController) mockPlugin.EXPECT(). CreateDB(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockDB, nil).AnyTimes() delete(supportedPlugins, "cassandra") RegisterPlugin("cassandra", mockPlugin) } func TestShardedNosqlStoreTestSuite(t *testing.T) { s := new(shardedNosqlStoreTestSuite) suite.Run(t, s) } func (s *shardedNosqlStoreTestSuite) TestValidConfiguration() { store := s.newShardedStoreForTest() s.Equal(1, len(store.connectedShards)) s.Contains(store.connectedShards, "shard-1") s.Equal(store.GetDefaultShard(), store.defaultShard) s.Equal(store.connectedShards["shard-1"], store.defaultShard) s.Equal("shard-1", store.shardingPolicy.defaultShard) s.True(store.shardingPolicy.hasShardedTasklist) s.True(store.shardingPolicy.hasShardedHistory) } func (s *shardedNosqlStoreTestSuite) TestStoreSelectionForHistoryShard() { mockDB1 := nosqlplugin.NewMockDB(s.mockController) mockDB1.EXPECT().Close().Times(1) mockDB2 := nosqlplugin.NewMockDB(s.mockController) mockDB2.EXPECT().Close().Times(1) mockPlugin := nosqlplugin.NewMockPlugin(s.mockController) gomock.InOrder( mockPlugin.EXPECT(). CreateDB(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockDB1, nil), mockPlugin.EXPECT(). CreateDB(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, errors.New("error creating db")), mockPlugin.EXPECT(). CreateDB(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockDB2, nil), ) delete(supportedPlugins, "cassandra") RegisterPlugin("cassandra", mockPlugin) store := s.newShardedStoreForTest() defer store.Close() s.Equal(1, len(store.connectedShards)) s.True(mockDB1 == store.defaultShard.db) // Shard 0 is same default shard in this test, so connectedShards shouldn't change storeShard1, err := store.GetStoreShardByHistoryShard(0) s.NoError(err) s.Equal(1, len(store.connectedShards)) s.True(mockDB1 == storeShard1.db) // Getting the same shard again shouldn't create a new connection storeShard1, err = store.GetStoreShardByHistoryShard(0) s.NoError(err) s.Equal(1, len(store.connectedShards)) s.True(mockDB1 == storeShard1.db) // Getting a new shard should create a new connection but it will fail on first attempt storeShard2, err := store.GetStoreShardByHistoryShard(1) s.Error(err) s.Equal(1, len(store.connectedShards)) // Getting a new shard should create a new connection on second attempt storeShard2, err = store.GetStoreShardByHistoryShard(1) s.NoError(err) s.Equal(2, len(store.connectedShards)) s.True(mockDB2 == storeShard2.db) // After the new connection, getting the previous shard should still work as it used to storeShard1, err = store.GetStoreShardByHistoryShard(0) s.NoError(err) s.Equal(2, len(store.connectedShards)) s.True(mockDB1 == storeShard1.db) // Getting a non-existing shard should result in an error unknownShard, err := store.GetStoreShardByHistoryShard(2) s.ErrorContains(err, "Failed to identify store shard") s.Empty(unknownShard) // Ensure the store shard connections created for history shards are available for tasklists, too storeShard1, err = store.GetStoreShardByTaskList("domain1", "tl1", 0) s.NoError(err) s.Equal(2, len(store.connectedShards)) s.True(mockDB1 == storeShard1.db) storeShard2, err = store.GetStoreShardByTaskList("domain1", "tl2", 0) s.NoError(err) s.Equal(2, len(store.connectedShards)) s.True(mockDB2 == storeShard2.db) } func (s *shardedNosqlStoreTestSuite) newShardedStoreForTest() *shardedNosqlStoreImpl { cfg := getValidShardedNoSQLConfig() logger := log.NewNoop() storeInterface, err := newShardedNosqlStore(cfg, logger, metrics.NewNoopMetricsClient(), nil) s.NoError(err) s.Equal("shardedNosql", storeInterface.GetName()) s.Equal(logger, storeInterface.GetLogger()) store := storeInterface.(*shardedNosqlStoreImpl) s.Equal(storeInterface.GetShardingPolicy(), store.shardingPolicy) return store } func (s *shardedNosqlStoreTestSuite) TestStoreSelectionForTasklist() { mockDB1 := nosqlplugin.NewMockDB(s.mockController) mockDB1.EXPECT().Close().Times(1) mockDB2 := nosqlplugin.NewMockDB(s.mockController) mockDB2.EXPECT().Close().Times(1) mockPlugin := nosqlplugin.NewMockPlugin(s.mockController) gomock.InOrder( mockPlugin.EXPECT(). CreateDB(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockDB1, nil), mockPlugin.EXPECT(). CreateDB(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockDB2, nil), ) delete(supportedPlugins, "cassandra") RegisterPlugin("cassandra", mockPlugin) store := s.newShardedStoreForTest() defer store.Close() s.Equal(1, len(store.connectedShards)) s.True(mockDB1 == store.defaultShard.db) // Shard 0 is same default shard in this test, so connectedShards shouldn't change storeShard1, err := store.GetStoreShardByTaskList("domain1", "tl1", 0) s.NoError(err) s.Equal(1, len(store.connectedShards)) s.True(mockDB1 == storeShard1.db) // Getting the same shard again shouldn't create a new connection storeShard1, err = store.GetStoreShardByTaskList("domain1", "tl1", 0) s.NoError(err) s.Equal(1, len(store.connectedShards)) s.True(mockDB1 == storeShard1.db) // Getting a new shard should create a new connection storeShard2, err := store.GetStoreShardByTaskList("domain1", "tl2", 0) s.NoError(err) s.Equal(2, len(store.connectedShards)) s.True(mockDB2 == storeShard2.db) // After the new connection, getting the previous shard should still work as it used to storeShard1, err = store.GetStoreShardByTaskList("domain1", "tl1", 0) s.NoError(err) s.Equal(2, len(store.connectedShards)) s.True(mockDB1 == storeShard1.db) // Ensure the store shard connections created for tasklists are available for tasklists, too storeShard1, err = store.GetStoreShardByHistoryShard(0) s.NoError(err) s.Equal(2, len(store.connectedShards)) s.True(mockDB1 == storeShard1.db) storeShard2, err = store.GetStoreShardByHistoryShard(1) s.NoError(err) s.Equal(2, len(store.connectedShards)) s.True(mockDB2 == storeShard2.db) } func getValidShardedNoSQLConfig() ShardedNoSQL { return ShardedNoSQL{ DefaultShard: "shard-1", ShardingPolicy: ShardingPolicy{ HistoryShardMapping: []HistoryShardRange{ HistoryShardRange{ Start: 0, End: 1, Shard: "shard-1", }, HistoryShardRange{ Start: 1, End: 2, Shard: "shard-2", }, }, TaskListHashing: TasklistHashing{ ShardOrder: []string{ "shard-1", "shard-2", }, }, }, Connections: map[string]DBShardConnection{ "shard-1": { NoSQLPlugin: &NoSQL{ PluginName: "cassandra", Hosts: "127.0.0.1", Keyspace: "unit-test", Port: 1234, }, }, "shard-2": { NoSQLPlugin: &NoSQL{ PluginName: "cassandra", Hosts: "127.0.0.1", Keyspace: "unit-test", Port: 5678, }, }, }, } } ================================================ FILE: common/persistence/nosql/sharding_policy.go ================================================ // Copyright (c) 2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "fmt" "github.com/dgryski/go-farm" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) // shardingPolicy holds the configuration for the sharding logic type shardingPolicy struct { logger log.Logger defaultShard string config config.ShardedNoSQL hasShardedHistory bool hasShardedTasklist bool } func newShardingPolicy(logger log.Logger, cfg config.ShardedNoSQL) (shardingPolicy, error) { sp := shardingPolicy{ logger: logger, defaultShard: cfg.DefaultShard, config: cfg, } err := sp.parse() if err != nil { return sp, err } return sp, nil } func (sp *shardingPolicy) parse() error { sp.parseHistoryShardMapping() sp.parseTaskListShardingPolicy() return nil } func (sp *shardingPolicy) parseHistoryShardMapping() { historyShardMapping := sp.config.ShardingPolicy.HistoryShardMapping if len(historyShardMapping) == 0 { return } sp.hasShardedHistory = true } func (sp *shardingPolicy) parseTaskListShardingPolicy() { tlShards := sp.config.ShardingPolicy.TaskListHashing.ShardOrder if len(tlShards) == 0 { return } sp.hasShardedTasklist = true } func (sp *shardingPolicy) getHistoryShardName(shardID int) (string, error) { if !sp.hasShardedHistory { sp.logger.Debug("Selected default store shard for history shard", tag.StoreShard(sp.defaultShard), tag.ShardID(shardID)) return sp.defaultShard, nil } for _, r := range sp.config.ShardingPolicy.HistoryShardMapping { if shardID >= r.Start && shardID < r.End { sp.logger.Debug("Selected store shard history shard", tag.StoreShard(r.Shard), tag.ShardID(shardID)) return r.Shard, nil } } return "", &ShardingError{ Message: fmt.Sprintf("Failed to identify store shard for shardID %v", shardID), } } func (sp *shardingPolicy) getTaskListShardName(domainID string, taskListName string, taskType int) string { if !sp.hasShardedTasklist { sp.logger.Debug("Selected default store shard for tasklist", tag.StoreShard(sp.defaultShard), tag.WorkflowTaskListName(taskListName)) return sp.defaultShard } tlShards := sp.config.ShardingPolicy.TaskListHashing.ShardOrder tlShardCount := len(tlShards) hash := farm.Hash32([]byte(domainID+"_"+taskListName)) % uint32(tlShardCount) shardIndex := int(hash) % tlShardCount sp.logger.Debug("Selected store shard tasklist", tag.StoreShard(tlShards[shardIndex]), tag.WorkflowTaskListName(taskListName)) return tlShards[shardIndex] } ================================================ FILE: common/persistence/nosql/sharding_policy_test.go ================================================ // Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/require" . "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" ) func TestSimplePolicyWithSharding(t *testing.T) { cfg := getValidShardedNoSQLConfig() sp, err := newShardingPolicy(log.NewNoop(), cfg) require.NoError(t, err) require.True(t, sp.hasShardedHistory, "sp.hasShardedHistory must be true") require.True(t, sp.hasShardedTasklist, "sp.hasShardedTasklist must be true") } func TestSimplePolicyWithoutSharding(t *testing.T) { cfg := getValidShardedNoSQLConfig() cfg.ShardingPolicy = ShardingPolicy{} // remove the sharding policy sp, err := newShardingPolicy(log.NewNoop(), cfg) require.NoError(t, err) require.False(t, sp.hasShardedHistory, "sp.hasShardedHistory must be false") require.False(t, sp.hasShardedTasklist, "sp.hasShardedTasklist must be false") } func TestSimplePolicyWithoutHistorySharding(t *testing.T) { cfg := getValidShardedNoSQLConfig() cfg.ShardingPolicy.HistoryShardMapping = []HistoryShardRange{} // remove only the history sharding sp, err := newShardingPolicy(log.NewNoop(), cfg) require.NoError(t, err) require.False(t, sp.hasShardedHistory, "sp.hasShardedHistory must be false") require.True(t, sp.hasShardedTasklist, "sp.hasShardedTasklist must be true") } func TestSimplePolicyWithoutTasklistSharding(t *testing.T) { cfg := getValidShardedNoSQLConfig() cfg.ShardingPolicy.TaskListHashing = TasklistHashing{} // remove only the tasklist sharding sp, err := newShardingPolicy(log.NewNoop(), cfg) require.NoError(t, err) require.True(t, sp.hasShardedHistory, "sp.hasShardedHistory must be false") require.False(t, sp.hasShardedTasklist, "sp.hasShardedTasklist must be true") } func TestHistorySharding(t *testing.T) { cfg := getValidShardedNoSQLConfig() sp, err := newShardingPolicy(log.NewNoop(), cfg) require.NoError(t, err) shardName0, err1 := sp.getHistoryShardName(0) shardName1, err2 := sp.getHistoryShardName(1) require.Nil(t, err1) require.Nil(t, err2) require.Equal(t, "shard-1", shardName0, "shard name must be correct for shard 0") require.Equal(t, "shard-2", shardName1, "shard name must be correct for shard 1") } func TestHistorySharding_UnexpectedGapInHistoryRanges(t *testing.T) { cfg := getValidShardedNoSQLConfig() cfg.ShardingPolicy.HistoryShardMapping = []HistoryShardRange{ HistoryShardRange{ Start: 0, End: 1, Shard: "shard-1", }, HistoryShardRange{ Start: 2, End: 3, Shard: "shard-2", }, } sp, err := newShardingPolicy(log.NewNoop(), cfg) require.NoError(t, err) shardName0, err1 := sp.getHistoryShardName(0) shardName2, err2 := sp.getHistoryShardName(2) require.Nil(t, err1) require.Nil(t, err2) require.Equal(t, "shard-1", shardName0, "shard name must be correct for shard 0") require.Equal(t, "shard-2", shardName2, "shard name must be correct for shard 2") unknownShard, err := sp.getHistoryShardName(1) require.ErrorContains(t, err, "Failed to identify store shard") require.Empty(t, unknownShard) } func TestHistorySharding_OutOfBounds(t *testing.T) { cfg := getValidShardedNoSQLConfig() sp, err := newShardingPolicy(log.NewNoop(), cfg) require.NoError(t, err) unknownShard, err := sp.getHistoryShardName(-1) require.ErrorContains(t, err, "Failed to identify store shard") require.Empty(t, unknownShard) unknownShard, err = sp.getHistoryShardName(2) require.ErrorContains(t, err, "Failed to identify store shard") require.Empty(t, unknownShard) } func TestTaskListSharding_Simple(t *testing.T) { cfg := getValidShardedNoSQLConfig() sp, err := newShardingPolicy(log.NewNoop(), cfg) require.NoError(t, err) // Note that shardName0 and shardName1 can't really be predicted due to hashing inside the function. The values // below were picked manually to yield the outcome needed. The test is useful to ensure the logic is deterministic. shardName1 := sp.getTaskListShardName("domain1", "tl1", 0) shardName2 := sp.getTaskListShardName("domain1", "tl2", 0) require.Equal(t, "shard-1", shardName1, "shard name must be correct for tl1") require.Equal(t, "shard-2", shardName2, "shard name must be correct for tl2") } func TestTaskListSharding_SingleShard(t *testing.T) { cfg := getValidShardedNoSQLConfig() cfg.ShardingPolicy.TaskListHashing.ShardOrder = []string{"only-shard"} sp, err := newShardingPolicy(log.NewNoop(), cfg) require.NoError(t, err) require.True(t, sp.hasShardedTasklist, "sp.hasShardedTasklist must be true") countExpected := 0 countUnexpected := 0 for i := 0; i < 1000000; i++ { s := sp.getTaskListShardName(uuid.New(), uuid.New(), 0) if s == "only-shard" { countExpected++ } else { countUnexpected++ } } require.Equal(t, 0, countUnexpected, "Unexpected shard names must not be returned") require.Equal(t, 1000000, countExpected, "Expected shard name must be returned always") } func TestTaskListSharding_Probabilistic(t *testing.T) { cfg := getValidShardedNoSQLConfig() sp, err := newShardingPolicy(log.NewNoop(), cfg) require.NoError(t, err) countShard1 := 0 countShard2 := 0 countUnexpected := 0 for i := 0; i < 1000000; i++ { s := sp.getTaskListShardName(uuid.New(), uuid.New(), 0) if s == "shard-1" { countShard1++ } else if s == "shard-2" { countShard2++ } else { countUnexpected++ } } ratio := float32(countShard1) / float32(countShard2) require.Equal(t, 0, countUnexpected, "Unexpected shard names must not be returned") require.True(t, ratio > 0.95 && ratio < 1.05, "Shards should have equal chance of being selected") } ================================================ FILE: common/persistence/nosql/utils.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package nosql import ( "fmt" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin" "github.com/uber/cadence/common/types" ) // ShardingError represents invalid shard type ShardingError struct { Message string } func (e *ShardingError) Error() string { return e.Message } func convertCommonErrors(errChecker nosqlplugin.ClientErrorChecker, operation string, err error) error { if errChecker.IsNotFoundError(err) { return &types.EntityNotExistsError{ Message: fmt.Sprintf("%v failed. Error: %v ", operation, err), } } if errChecker.IsTimeoutError(err) { return &persistence.TimeoutError{Msg: fmt.Sprintf("%v timed out. Error: %v", operation, err)} } if errChecker.IsThrottlingError(err) { return &types.ServiceBusyError{ Message: fmt.Sprintf("%v operation failed. Error: %v", operation, err), } } if errChecker.IsDBUnavailableError(err) { return &persistence.DBUnavailableError{ Msg: fmt.Sprintf("%v operation failed. Error: %v", operation, err), } } return &types.InternalServiceError{ Message: fmt.Sprintf("%v operation failed. Error: %v", operation, err), } } ================================================ FILE: common/persistence/operation_mode_validator.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "fmt" "github.com/uber/cadence/common/types" ) // NOTE: when modifying this file, plz make each case clear, // do not combine cases together. // The idea for this file is to test whether current record // points to a zombie record. // ValidateCreateWorkflowModeState validate workflow creation mode & workflow state func ValidateCreateWorkflowModeState( mode CreateWorkflowMode, newWorkflowSnapshot InternalWorkflowSnapshot, ) error { workflowState := newWorkflowSnapshot.ExecutionInfo.State if err := checkWorkflowState(workflowState); err != nil { return err } switch mode { case CreateWorkflowModeBrandNew, CreateWorkflowModeWorkflowIDReuse, CreateWorkflowModeContinueAsNew: if workflowState == WorkflowStateZombie || workflowState == WorkflowStateCompleted { return newInvalidCreateWorkflowMode( mode, workflowState, ) } return nil case CreateWorkflowModeZombie: if workflowState == WorkflowStateCreated || workflowState == WorkflowStateRunning || workflowState == WorkflowStateCompleted { return newInvalidCreateWorkflowMode( mode, workflowState, ) } return nil default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown mode: %v", mode), } } } // ValidateUpdateWorkflowModeState validate workflow update mode & workflow state func ValidateUpdateWorkflowModeState( mode UpdateWorkflowMode, currentWorkflowMutation InternalWorkflowMutation, newWorkflowSnapshot *InternalWorkflowSnapshot, ) error { currentWorkflowState := currentWorkflowMutation.ExecutionInfo.State if err := checkWorkflowState(currentWorkflowState); err != nil { return err } var newWorkflowState *int if newWorkflowSnapshot != nil { newWorkflowState = &newWorkflowSnapshot.ExecutionInfo.State if err := checkWorkflowState(*newWorkflowState); err != nil { return err } } switch mode { case UpdateWorkflowModeUpdateCurrent: // update current record // 1. current workflow only -> // current workflow cannot be zombie // 2. current workflow & new workflow -> // current workflow cannot be created / running, // new workflow cannot be zombie // case 1 if newWorkflowState == nil { if currentWorkflowState == WorkflowStateZombie { return newInvalidUpdateWorkflowMode(mode, currentWorkflowState) } return nil } // case 2 if currentWorkflowState == WorkflowStateCreated || currentWorkflowState == WorkflowStateRunning || *newWorkflowState == WorkflowStateZombie || *newWorkflowState == WorkflowStateCompleted { return newInvalidUpdateWorkflowWithNewMode(mode, currentWorkflowState, *newWorkflowState) } return nil case UpdateWorkflowModeBypassCurrent: // bypass current record // 1. current workflow only -> // current workflow cannot be created / running // 2. current workflow & new workflow -> // current workflow cannot be created / running, // new workflow cannot be created / running / completed // case 1 if newWorkflowState == nil { if currentWorkflowState == WorkflowStateCreated || currentWorkflowState == WorkflowStateRunning { return newInvalidUpdateWorkflowMode(mode, currentWorkflowState) } return nil } // case 2 if currentWorkflowState == WorkflowStateCreated || currentWorkflowState == WorkflowStateRunning || *newWorkflowState == WorkflowStateCreated || *newWorkflowState == WorkflowStateRunning || *newWorkflowState == WorkflowStateCompleted { return newInvalidUpdateWorkflowWithNewMode( mode, currentWorkflowState, *newWorkflowState, ) } return nil case UpdateWorkflowModeIgnoreCurrent: if newWorkflowState != nil { return newInvalidUpdateWorkflowWithNewMode( mode, currentWorkflowState, *newWorkflowState, ) } return nil default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown mode: %v", mode), } } } // ValidateConflictResolveWorkflowModeState validate workflow conflict resolve mode & workflow state func ValidateConflictResolveWorkflowModeState( mode ConflictResolveWorkflowMode, resetWorkflowSnapshot InternalWorkflowSnapshot, newWorkflowSnapshot *InternalWorkflowSnapshot, currentWorkflowMutation *InternalWorkflowMutation, ) error { resetWorkflowState := resetWorkflowSnapshot.ExecutionInfo.State if err := checkWorkflowState(resetWorkflowState); err != nil { return err } var newWorkflowState *int if newWorkflowSnapshot != nil { newWorkflowState = &newWorkflowSnapshot.ExecutionInfo.State if err := checkWorkflowState(*newWorkflowState); err != nil { return err } } var currentWorkflowState *int if currentWorkflowMutation != nil { currentWorkflowState = ¤tWorkflowMutation.ExecutionInfo.State if err := checkWorkflowState(*currentWorkflowState); err != nil { return err } } switch mode { case ConflictResolveWorkflowModeUpdateCurrent: // update current record // 1. reset workflow only -> // reset workflow cannot be zombie // 2. reset workflow & new workflow -> // reset workflow cannot be created / running / zombie, // new workflow cannot be zombie / completed // 3. current workflow & reset workflow -> // current workflow cannot be created / running, // reset workflow cannot be zombie // 4. current workflow & reset workflow & new workflow -> // current workflow cannot be created / running, // reset workflow cannot be created / running / zombie, // new workflow cannot be zombie / completed // TODO remove case 1 & 2 support once 2DC is deprecated // it is ok that currentWorkflowMutation is null, only for 2 DC case // NDC should always require current workflow for CAS // Note: current workflow mutation can be in zombie state, for the update // case 1 & 2 if currentWorkflowState == nil { // case 1 if newWorkflowState == nil { if resetWorkflowState == WorkflowStateZombie { return newInvalidConflictResolveWorkflowMode( mode, resetWorkflowState, ) } return nil } // case 2 if resetWorkflowState == WorkflowStateCreated || resetWorkflowState == WorkflowStateRunning || resetWorkflowState == WorkflowStateZombie || *newWorkflowState == WorkflowStateZombie || *newWorkflowState == WorkflowStateCompleted { return newInvalidConflictResolveWorkflowWithNewMode( mode, resetWorkflowState, *newWorkflowState, ) } return nil } // case 3 & 4 // case 3 if newWorkflowState == nil { if *currentWorkflowState == WorkflowStateCreated || *currentWorkflowState == WorkflowStateRunning || resetWorkflowState == WorkflowStateZombie { return newInvalidConflictResolveWorkflowWithCurrentMode( mode, resetWorkflowState, *currentWorkflowState, ) } return nil } // case 4 if *currentWorkflowState == WorkflowStateCreated || *currentWorkflowState == WorkflowStateRunning || resetWorkflowState == WorkflowStateCreated || resetWorkflowState == WorkflowStateRunning || resetWorkflowState == WorkflowStateZombie || *newWorkflowState == WorkflowStateZombie || *newWorkflowState == WorkflowStateCompleted { return newInvalidConflictResolveWorkflowWithCurrentWithNewMode( mode, resetWorkflowState, *newWorkflowState, *currentWorkflowState, ) } return nil case ConflictResolveWorkflowModeBypassCurrent: // bypass current record // * current workflow cannot be set // 1. reset workflow only -> // reset workflow cannot be created / running // 2. reset workflow & new workflow -> // reset workflow cannot be created / running / zombie, // new workflow cannot be created / running / completed // precondition if currentWorkflowMutation != nil { return &types.InternalServiceError{ Message: fmt.Sprintf( "Invalid workflow conflict resolve mode %v, encounter current workflow", mode, ), } } // case 1 if newWorkflowState == nil { if resetWorkflowState == WorkflowStateCreated || resetWorkflowState == WorkflowStateRunning { return newInvalidConflictResolveWorkflowMode( mode, resetWorkflowState, ) } return nil } // case 2 if resetWorkflowState == WorkflowStateCreated || resetWorkflowState == WorkflowStateRunning || resetWorkflowState == WorkflowStateZombie || *newWorkflowState == WorkflowStateCreated || *newWorkflowState == WorkflowStateRunning || *newWorkflowState == WorkflowStateCompleted { return newInvalidConflictResolveWorkflowWithNewMode( mode, resetWorkflowState, *newWorkflowState, ) } return nil default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown mode: %v", mode), } } } func checkWorkflowState(state int) error { switch state { case WorkflowStateCreated, WorkflowStateRunning, WorkflowStateZombie, WorkflowStateCompleted, WorkflowStateCorrupted: return nil default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown workflow state: %v", state), } } } func newInvalidCreateWorkflowMode( mode CreateWorkflowMode, workflowState int, ) error { return &types.InternalServiceError{ Message: fmt.Sprintf( "Invalid workflow create mode %v, state: %v", mode, workflowState, ), } } func newInvalidUpdateWorkflowMode( mode UpdateWorkflowMode, currentWorkflowState int, ) error { return &types.InternalServiceError{ Message: fmt.Sprintf( "Invalid workflow update mode %v, state: %v", mode, currentWorkflowState, ), } } func newInvalidUpdateWorkflowWithNewMode( mode UpdateWorkflowMode, currentWorkflowState int, newWorkflowState int, ) error { return &types.InternalServiceError{ Message: fmt.Sprintf( "Invalid workflow update mode %v, current state: %v, new state: %v", mode, currentWorkflowState, newWorkflowState, ), } } func newInvalidConflictResolveWorkflowMode( mode ConflictResolveWorkflowMode, resetWorkflowState int, ) error { return &types.InternalServiceError{ Message: fmt.Sprintf( "Invalid workflow conflict resolve mode %v, reset state: %v", mode, resetWorkflowState, ), } } func newInvalidConflictResolveWorkflowWithNewMode( mode ConflictResolveWorkflowMode, resetWorkflowState int, newWorkflowState int, ) error { return &types.InternalServiceError{ Message: fmt.Sprintf( "Invalid workflow conflict resolve mode %v, reset state: %v, new state: %v", mode, resetWorkflowState, newWorkflowState, ), } } func newInvalidConflictResolveWorkflowWithCurrentMode( mode ConflictResolveWorkflowMode, resetWorkflowState int, currentWorkflowState int, ) error { return &types.InternalServiceError{ Message: fmt.Sprintf( "Invalid workflow conflict resolve mode %v, reset state: %v, current state: %v", mode, resetWorkflowState, currentWorkflowState, ), } } func newInvalidConflictResolveWorkflowWithCurrentWithNewMode( mode ConflictResolveWorkflowMode, resetWorkflowState int, newWorkflowState int, currentWorkflowState int, ) error { return &types.InternalServiceError{ Message: fmt.Sprintf( "Invalid workflow conflict resolve mode %v, reset state: %v, new state: %v, current state: %v", mode, resetWorkflowState, newWorkflowState, currentWorkflowState, ), } } ================================================ FILE: common/persistence/operation_mode_validator_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "fmt" "testing" "github.com/stretchr/testify/suite" ) type ( validateOperationWorkflowModeStateSuite struct { suite.Suite } ) func TestValidateOperationWorkflowModeStateSuite(t *testing.T) { s := new(validateOperationWorkflowModeStateSuite) suite.Run(t, s) } func (s *validateOperationWorkflowModeStateSuite) SetupSuite() { } func (s *validateOperationWorkflowModeStateSuite) TearDownSuite() { } func (s *validateOperationWorkflowModeStateSuite) SetupTest() { } func (s *validateOperationWorkflowModeStateSuite) TearDownTest() { } func (s *validateOperationWorkflowModeStateSuite) TestCreateMode_UpdateCurrent() { stateToError := map[int]bool{ WorkflowStateCreated: false, WorkflowStateRunning: false, WorkflowStateCompleted: true, WorkflowStateZombie: true, } creatModes := []CreateWorkflowMode{ CreateWorkflowModeBrandNew, CreateWorkflowModeWorkflowIDReuse, CreateWorkflowModeContinueAsNew, } for state, expectError := range stateToError { testSnapshot := s.newTestWorkflowSnapshot(state) for _, createMode := range creatModes { err := ValidateCreateWorkflowModeState(createMode, testSnapshot) if !expectError { s.NoError(err, err) } else { s.Error(err, err) } } } } func (s *validateOperationWorkflowModeStateSuite) TestCreateMode_BypassCurrent() { stateToError := map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: true, WorkflowStateZombie: false, } for state, expectError := range stateToError { testSnapshot := s.newTestWorkflowSnapshot(state) err := ValidateCreateWorkflowModeState(CreateWorkflowModeZombie, testSnapshot) if !expectError { s.NoError(err, err) } else { s.Error(err, err) } } } func (s *validateOperationWorkflowModeStateSuite) TestUpdateMode_UpdateCurrent() { // only current workflow stateToError := map[int]bool{ WorkflowStateCreated: false, WorkflowStateRunning: false, WorkflowStateCompleted: false, WorkflowStateZombie: true, } for state, expectError := range stateToError { testCurrentMutation := s.newTestWorkflowMutation(state) err := ValidateUpdateWorkflowModeState( UpdateWorkflowModeUpdateCurrent, testCurrentMutation, nil, ) if !expectError { s.NoError(err, err) } else { s.Error(err, err) } } // current workflow & new workflow currentStateToError := map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: false, WorkflowStateZombie: false, } newStateToError := map[int]bool{ WorkflowStateCreated: false, WorkflowStateRunning: false, WorkflowStateCompleted: true, WorkflowStateZombie: true, } for currentState, currentExpectError := range currentStateToError { for newState, newExpectError := range newStateToError { testCurrentMutation := s.newTestWorkflowMutation(currentState) testNewSnapshot := s.newTestWorkflowSnapshot(newState) err := ValidateUpdateWorkflowModeState( UpdateWorkflowModeUpdateCurrent, testCurrentMutation, &testNewSnapshot, ) if currentExpectError || newExpectError { s.Error(err, err) } else { s.NoError(err, err) } } } } func (s *validateOperationWorkflowModeStateSuite) TestUpdateMode_BypassCurrent() { // only current workflow stateToError := map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: false, WorkflowStateZombie: false, } for state, expectError := range stateToError { testMutation := s.newTestWorkflowMutation(state) err := ValidateUpdateWorkflowModeState( UpdateWorkflowModeBypassCurrent, testMutation, nil, ) if !expectError { s.NoError(err, err) } else { s.Error(err, err) } } // current workflow & new workflow currentStateToError := map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: false, WorkflowStateZombie: false, } newStateToError := map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: true, WorkflowStateZombie: false, } for currentState, currentExpectError := range currentStateToError { for newState, newExpectError := range newStateToError { testCurrentMutation := s.newTestWorkflowMutation(currentState) testNewSnapshot := s.newTestWorkflowSnapshot(newState) err := ValidateUpdateWorkflowModeState( UpdateWorkflowModeBypassCurrent, testCurrentMutation, &testNewSnapshot, ) if currentExpectError || newExpectError { s.Error(err, err) } else { s.NoError(err, err) } } } } func (s *validateOperationWorkflowModeStateSuite) TestUpdateMode_IgnoreCurrent() { testMutation := s.newTestWorkflowMutation(WorkflowStateCompleted) err := ValidateUpdateWorkflowModeState( UpdateWorkflowModeIgnoreCurrent, testMutation, nil, ) s.NoError(err) testNewSnapshot := s.newTestWorkflowSnapshot(WorkflowStateRunning) err = ValidateUpdateWorkflowModeState( UpdateWorkflowModeIgnoreCurrent, testMutation, &testNewSnapshot, ) s.Error(err) } func (s *validateOperationWorkflowModeStateSuite) TestConflictResolveMode_UpdateCurrent() { // only reset workflow stateToError := map[int]bool{ WorkflowStateCreated: false, WorkflowStateRunning: false, WorkflowStateCompleted: false, WorkflowStateZombie: true, } for state, expectError := range stateToError { testSnapshot := s.newTestWorkflowSnapshot(state) err := ValidateConflictResolveWorkflowModeState( ConflictResolveWorkflowModeUpdateCurrent, testSnapshot, nil, nil, ) if !expectError { s.NoError(err, err) } else { s.Error(err, err) } } // reset workflow & new workflow resetStateToError := map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: false, WorkflowStateZombie: true, } newStateToError := map[int]bool{ WorkflowStateCreated: false, WorkflowStateRunning: false, WorkflowStateCompleted: true, WorkflowStateZombie: true, } for resetState, resetExpectError := range resetStateToError { for newState, newExpectError := range newStateToError { testResetSnapshot := s.newTestWorkflowSnapshot(resetState) testNewSnapshot := s.newTestWorkflowSnapshot(newState) err := ValidateConflictResolveWorkflowModeState( ConflictResolveWorkflowModeUpdateCurrent, testResetSnapshot, &testNewSnapshot, nil, ) if resetExpectError || newExpectError { s.Error(err, err) } else { s.NoError(err, err) } } } // reset workflow & current workflow resetStateToError = map[int]bool{ WorkflowStateCreated: false, WorkflowStateRunning: false, WorkflowStateCompleted: false, WorkflowStateZombie: true, } currentStateToError := map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: false, WorkflowStateZombie: false, } for resetState, resetExpectError := range resetStateToError { for currentState, currentExpectError := range currentStateToError { testResetSnapshot := s.newTestWorkflowSnapshot(resetState) testCurrentSnapshot := s.newTestWorkflowMutation(currentState) err := ValidateConflictResolveWorkflowModeState( ConflictResolveWorkflowModeUpdateCurrent, testResetSnapshot, nil, &testCurrentSnapshot, ) if resetExpectError || currentExpectError { s.Error(err, err) } else { s.NoError(err, err) } } } // reset workflow & new workflow & current workflow resetStateToError = map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: false, WorkflowStateZombie: true, } newStateToError = map[int]bool{ WorkflowStateCreated: false, WorkflowStateRunning: false, WorkflowStateCompleted: true, WorkflowStateZombie: true, } currentStateToError = map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: false, WorkflowStateZombie: false, } for resetState, resetExpectError := range resetStateToError { for newState, newExpectError := range newStateToError { for currentState, currentExpectError := range currentStateToError { testResetSnapshot := s.newTestWorkflowSnapshot(resetState) testNewSnapshot := s.newTestWorkflowSnapshot(newState) testCurrentSnapshot := s.newTestWorkflowMutation(currentState) err := ValidateConflictResolveWorkflowModeState( ConflictResolveWorkflowModeUpdateCurrent, testResetSnapshot, &testNewSnapshot, &testCurrentSnapshot, ) if resetExpectError || newExpectError || currentExpectError { s.Error(err, err) } else { s.NoError(err, err) } } } } } func (s *validateOperationWorkflowModeStateSuite) TestConflictResolveMode_BypassCurrent() { // only reset workflow stateToError := map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: false, WorkflowStateZombie: false, } for state, expectError := range stateToError { testSnapshot := s.newTestWorkflowSnapshot(state) err := ValidateConflictResolveWorkflowModeState( ConflictResolveWorkflowModeBypassCurrent, testSnapshot, nil, nil, ) if !expectError { s.NoError(err, err) } else { s.Error(err, err) } } // reset workflow & new workflow resetStateToError := map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: false, WorkflowStateZombie: true, } newStateToError := map[int]bool{ WorkflowStateCreated: true, WorkflowStateRunning: true, WorkflowStateCompleted: true, WorkflowStateZombie: false, } for resetState, resetExpectError := range resetStateToError { for newState, newExpectError := range newStateToError { testResetSnapshot := s.newTestWorkflowSnapshot(resetState) testNewSnapshot := s.newTestWorkflowSnapshot(newState) err := ValidateConflictResolveWorkflowModeState( ConflictResolveWorkflowModeBypassCurrent, testResetSnapshot, &testNewSnapshot, nil, ) if resetExpectError || newExpectError { if err == nil { fmt.Print("##") } s.Error(err, err) } else { s.NoError(err, err) } } } } func (s *validateOperationWorkflowModeStateSuite) newTestWorkflowSnapshot( state int, ) InternalWorkflowSnapshot { return InternalWorkflowSnapshot{ ExecutionInfo: &InternalWorkflowExecutionInfo{ State: state, }, } } func (s *validateOperationWorkflowModeStateSuite) newTestWorkflowMutation( state int, ) InternalWorkflowMutation { return InternalWorkflowMutation{ ExecutionInfo: &InternalWorkflowExecutionInfo{ State: state, }, } } ================================================ FILE: common/persistence/persistence-tests/configStorePersistenceTest.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "encoding/json" "errors" "log" "os" "testing" "github.com/stretchr/testify/require" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // Currently you cannot clear or remove any entries in cluster_config table // Therefore, Teardown and Setup of Test DB is required before every test. type ( // ConfigStorePersistenceSuite contains config store persistence tests ConfigStorePersistenceSuite struct { *TestBase // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions } ) // SetupSuite implementation func (s *ConfigStorePersistenceSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } // SetupTest implementation func (s *ConfigStorePersistenceSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } // TearDownSuite implementation func (s *ConfigStorePersistenceSuite) TearDownSuite() { s.TearDownWorkflowStore() } // Tests if error is returned when trying to fetch dc values from empty table func (s *ConfigStorePersistenceSuite) TestFetchFromEmptyTable() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() snapshot, err := s.FetchDynamicConfig(ctx, 0) s.Nil(snapshot) s.NotNil(err) } func (s *ConfigStorePersistenceSuite) TestUpdateSimpleSuccess() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() snapshot := generateRandomSnapshot(1) err := s.UpdateDynamicConfig(ctx, snapshot, 1) s.Nil(err) retSnapshot, err := s.FetchDynamicConfig(ctx, 1) s.NotNil(retSnapshot) s.Nil(err) s.Equal(snapshot.Version, retSnapshot.Version) s.Equal(snapshot.Values.Entries[0].Name, retSnapshot.Values.Entries[0].Name) } func (s *ConfigStorePersistenceSuite) TestUpdateVersionCollisionFailure() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() snapshot := generateRandomSnapshot(1) err := s.UpdateDynamicConfig(ctx, snapshot, 2) s.Nil(err) err = s.UpdateDynamicConfig(ctx, snapshot, 2) var condErr *p.ConditionFailedError s.True(errors.As(err, &condErr)) } func (s *ConfigStorePersistenceSuite) TestUpdateIncrementalVersionSuccess() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() snapshot2 := generateRandomSnapshot(2) err := s.UpdateDynamicConfig(ctx, snapshot2, 3) s.Nil(err) snapshot3 := generateRandomSnapshot(3) err = s.UpdateDynamicConfig(ctx, snapshot3, 3) s.Nil(err) } func (s *ConfigStorePersistenceSuite) TestFetchLatestVersionSuccess() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() snapshot2 := generateRandomSnapshot(2) err := s.UpdateDynamicConfig(ctx, snapshot2, 4) s.Nil(err) snapshot3 := generateRandomSnapshot(3) err = s.UpdateDynamicConfig(ctx, snapshot3, 4) s.Nil(err) snapshot, err := s.FetchDynamicConfig(ctx, 4) s.NotNil(snapshot) s.Nil(err) s.Equal(int64(3), snapshot.Version) } func generateRandomSnapshot(version int64) *p.DynamicConfigSnapshot { data, _ := json.Marshal("test_value") values := make([]*types.DynamicConfigValue, 1) values[0] = &types.DynamicConfigValue{ Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: data, }, Filters: nil, } entries := make([]*types.DynamicConfigEntry, 1) entries[0] = &types.DynamicConfigEntry{ Name: "test_parameter", Values: values, } return &p.DynamicConfigSnapshot{ Version: version, Values: &types.DynamicConfigBlob{ SchemaVersion: 1, Entries: entries, }, } } func (s *ConfigStorePersistenceSuite) FetchDynamicConfig(ctx context.Context, rowType int) (*p.DynamicConfigSnapshot, error) { response, err := s.ConfigStoreManager.FetchDynamicConfig(ctx, p.ConfigType(rowType)) if err != nil { return nil, err } if response == nil { return nil, errors.New("nil FetchDynamicConfig response") } return response.Snapshot, nil } func (s *ConfigStorePersistenceSuite) UpdateDynamicConfig(ctx context.Context, snapshot *p.DynamicConfigSnapshot, rowType int) error { return s.ConfigStoreManager.UpdateDynamicConfig(ctx, &p.UpdateDynamicConfigRequest{Snapshot: snapshot}, p.ConfigType(rowType)) } ================================================ FILE: common/persistence/persistence-tests/dbVisibilityPersistenceTest.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "log" "os" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/client" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" ) type ( // DBVisibilityPersistenceSuite tests visibility persistence // It only tests against DB based visibility, AdvancedVisibility test is in ESVisibilitySuite DBVisibilityPersistenceSuite struct { *TestBase // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions VisibilityMgr p.VisibilityManager } ) // SetupSuite implementation func (s *DBVisibilityPersistenceSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } // setup visibility manager if s.VisibilityTestCluster != s.DefaultTestCluster { s.VisibilityTestCluster.SetupTestDatabase() } clusterName := s.ClusterMetadata.GetCurrentClusterName() vCfg := s.VisibilityTestCluster.Config() visibilityFactory := client.NewFactory(&vCfg, nil, clusterName, nil, s.Logger, &s.DynamicConfiguration) // SQL currently doesn't have support for visibility manager var err error s.VisibilityMgr, err = visibilityFactory.NewVisibilityManager( &client.Params{ PersistenceConfig: config.Persistence{ VisibilityStore: "something not empty", }, }, &service.Config{ ReadVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain("db"), WriteVisibilityStoreName: dynamicproperties.GetStringPropertyFn("db"), EnableReadDBVisibilityFromClosedExecutionV2: dynamicproperties.GetBoolPropertyFn(false), EnableDBVisibilitySampling: dynamicproperties.GetBoolPropertyFn(false), }, ) if err != nil { s.fatalOnError("NewVisibilityManager", err) } } // SetupTest implementation func (s *DBVisibilityPersistenceSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } // TearDownSuite implementation func (s *DBVisibilityPersistenceSuite) TearDownSuite() { // TODO VisibilityMgr/Store is created with a separated code path, this is incorrect and may cause leaking connection // And Postgres requires all connection to be closed before dropping a database // https://github.com/uber/cadence/issues/2854 // Remove the below line after the issue is fix s.VisibilityMgr.Close() s.TearDownWorkflowStore() } // TestBasicVisibility test func (s *DBVisibilityPersistenceSuite) TestBasicVisibility() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() testDomainUUID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "visibility-workflow-test", RunID: "fb15e4b5-356f-466d-8c6d-a29223e5c536", } startTime := time.Now().Add(time.Second * -5).UnixNano() startReq := &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, UpdateTimestamp: 0, ShardID: 0, } err0 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, startReq) s.Nil(err0) resp, err1 := s.VisibilityMgr.ListOpenWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime, LatestTime: startTime, }) s.Nil(err1) s.Equal(1, len(resp.Executions)) s.assertOpenExecutionEquals(startReq, resp.Executions[0]) closeReq := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, CloseTimestamp: time.Now().UnixNano(), UpdateTimestamp: time.Now().UnixNano(), HistoryLength: 5, ShardID: 1234, } err2 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, closeReq) s.Nil(err2) resp, err3 := s.VisibilityMgr.ListOpenWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime, LatestTime: startTime, }) s.Nil(err3) s.Equal(0, len(resp.Executions)) resp, err4 := s.VisibilityMgr.ListClosedWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime, LatestTime: startTime, }) s.Nil(err4) s.Equal(1, len(resp.Executions)) s.assertClosedExecutionEquals(closeReq, resp.Executions[0]) uninitializedReq := &p.RecordWorkflowExecutionUninitializedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", UpdateTimestamp: time.Now().UnixNano(), ShardID: 1234, } err5 := s.VisibilityMgr.RecordWorkflowExecutionUninitialized(ctx, uninitializedReq) s.Nil(err5) } // TestCronVisibility test func (s *DBVisibilityPersistenceSuite) TestCronVisibility() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() testDomainUUID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "visibility-cron-workflow-test", RunID: "fb15e4b5-356f-466d-8c6d-a29223e5c537", } startTime := time.Now().Add(time.Second * -5).UnixNano() startReq := &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-cron-workflow", StartTimestamp: startTime, IsCron: true, ShardID: 1234, } err0 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, startReq) s.Nil(err0) resp, err1 := s.VisibilityMgr.ListOpenWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime, LatestTime: startTime, }) s.Nil(err1) s.Equal(1, len(resp.Executions)) s.True(resp.Executions[0].IsCron) closeReq := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, CloseTimestamp: time.Now().UnixNano(), HistoryLength: 5, IsCron: true, ShardID: 1234, } err2 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, closeReq) s.Nil(err2) resp, err4 := s.VisibilityMgr.ListClosedWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime, LatestTime: startTime, }) s.Nil(err4) s.Equal(1, len(resp.Executions)) s.True(resp.Executions[0].IsCron) } // TestBasicVisibilityTimeSkew test func (s *DBVisibilityPersistenceSuite) TestBasicVisibilityTimeSkew() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() testDomainUUID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "visibility-workflow-test-time-skew", RunID: "fb15e4b5-356f-466d-8c6d-a29223e5c536", } startTime := time.Now().Add(time.Second * -5).UnixNano() err0 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, ShardID: 1234, }) s.Nil(err0) resp, err1 := s.VisibilityMgr.ListOpenWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime, LatestTime: startTime, }) s.Nil(err1) s.Equal(1, len(resp.Executions)) s.Equal(workflowExecution.WorkflowID, resp.Executions[0].Execution.WorkflowID) err2 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, CloseTimestamp: startTime - (10 * time.Second).Nanoseconds(), ShardID: 1234, }) s.Nil(err2) resp, err3 := s.VisibilityMgr.ListOpenWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime, LatestTime: startTime, }) s.Nil(err3) s.Equal(0, len(resp.Executions)) resp, err4 := s.VisibilityMgr.ListClosedWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime, LatestTime: startTime, }) s.Nil(err4) s.Equal(1, len(resp.Executions)) } // TestVisibilityPagination test func (s *DBVisibilityPersistenceSuite) TestVisibilityPagination() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() testDomainUUID := uuid.New() // Create 2 executions startTime1 := time.Now() workflowExecution1 := types.WorkflowExecution{ WorkflowID: "visibility-pagination-test1", RunID: "fb15e4b5-356f-466d-8c6d-a29223e5c536", } startReq1 := &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution1, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime1.UnixNano(), ShardID: 1234, } err0 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, startReq1) s.Nil(err0) startTime2 := startTime1.Add(time.Second) workflowExecution2 := types.WorkflowExecution{ WorkflowID: "visibility-pagination-test2", RunID: "843f6fc7-102a-4c63-a2d4-7c653b01bf52", } startReq2 := &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution2, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime2.UnixNano(), ShardID: 1234, } err1 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, startReq2) s.Nil(err1) // Get the first one resp, err2 := s.VisibilityMgr.ListOpenWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime1.UnixNano(), LatestTime: startTime2.UnixNano(), }) s.Nil(err2) s.Equal(1, len(resp.Executions)) s.assertOpenExecutionEquals(startReq2, resp.Executions[0]) // Use token to get the second one resp, err3 := s.VisibilityMgr.ListOpenWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime1.UnixNano(), LatestTime: startTime2.UnixNano(), NextPageToken: resp.NextPageToken, }) s.Nil(err3) s.Equal(1, len(resp.Executions)) s.assertOpenExecutionEquals(startReq1, resp.Executions[0]) // It is possible to not return non empty token which is going to return empty result if len(resp.NextPageToken) != 0 { // Now should get empty result by using token resp, err4 := s.VisibilityMgr.ListOpenWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 1, EarliestTime: startTime1.UnixNano(), LatestTime: startTime2.UnixNano(), NextPageToken: resp.NextPageToken, }) s.Nil(err4) s.Equal(0, len(resp.Executions)) } } // TestFilteringByType test func (s *DBVisibilityPersistenceSuite) TestFilteringByType() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() testDomainUUID := uuid.New() startTime := time.Now().UnixNano() // Create 2 executions workflowExecution1 := types.WorkflowExecution{ WorkflowID: "visibility-filtering-test1", RunID: "fb15e4b5-356f-466d-8c6d-a29223e5c536", } err0 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution1, WorkflowTypeName: "visibility-workflow-1", StartTimestamp: startTime, ShardID: 1234, }) s.Nil(err0) workflowExecution2 := types.WorkflowExecution{ WorkflowID: "visibility-filtering-test2", RunID: "843f6fc7-102a-4c63-a2d4-7c653b01bf52", } err1 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution2, WorkflowTypeName: "visibility-workflow-2", StartTimestamp: startTime, ShardID: 1234, }) s.Nil(err1) // List open with filtering resp, err2 := s.VisibilityMgr.ListOpenWorkflowExecutionsByType(ctx, &p.ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 2, EarliestTime: startTime, LatestTime: startTime, }, WorkflowTypeName: "visibility-workflow-1", }) s.Nil(err2) s.Equal(1, len(resp.Executions)) s.Equal(workflowExecution1.WorkflowID, resp.Executions[0].Execution.WorkflowID) stopTime := time.Now().UnixNano() // Close both executions err3 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution1, WorkflowTypeName: "visibility-workflow-1", StartTimestamp: startTime, CloseTimestamp: stopTime, ShardID: 1234, }) s.Nil(err3) closeReq := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution2, WorkflowTypeName: "visibility-workflow-2", StartTimestamp: startTime, CloseTimestamp: stopTime, HistoryLength: 3, ShardID: 1234, } err4 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, closeReq) s.Nil(err4) // List closed with filtering resp, err5 := s.VisibilityMgr.ListClosedWorkflowExecutionsByType(ctx, &p.ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 2, EarliestTime: startTime, LatestTime: startTime, }, WorkflowTypeName: "visibility-workflow-2", }) s.Nil(err5) s.Equal(1, len(resp.Executions)) s.assertClosedExecutionEquals(closeReq, resp.Executions[0]) } // TestFilteringByWorkflowID test func (s *DBVisibilityPersistenceSuite) TestFilteringByWorkflowID() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() testDomainUUID := uuid.New() startTime := time.Now().UnixNano() // Create 2 executions workflowExecution1 := types.WorkflowExecution{ WorkflowID: "visibility-filtering-test1", RunID: "fb15e4b5-356f-466d-8c6d-a29223e5c536", } err0 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution1, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, ShardID: 1234, }) s.Nil(err0) workflowExecution2 := types.WorkflowExecution{ WorkflowID: "visibility-filtering-test2", RunID: "843f6fc7-102a-4c63-a2d4-7c653b01bf52", } err1 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution2, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, ShardID: 1234, }) s.Nil(err1) // List open with filtering resp, err2 := s.VisibilityMgr.ListOpenWorkflowExecutionsByWorkflowID(ctx, &p.ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 2, EarliestTime: startTime, LatestTime: startTime, }, WorkflowID: "visibility-filtering-test1", }) s.Nil(err2) s.Equal(1, len(resp.Executions)) s.Equal(workflowExecution1.WorkflowID, resp.Executions[0].Execution.WorkflowID) stopTime := time.Now().UnixNano() // Close both executions err3 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution1, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, CloseTimestamp: stopTime, }) s.Nil(err3) closeReq := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution2, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, CloseTimestamp: stopTime, HistoryLength: 3, ShardID: 1234, } err4 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, closeReq) s.Nil(err4) // List closed with filtering resp, err5 := s.VisibilityMgr.ListClosedWorkflowExecutionsByWorkflowID(ctx, &p.ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 2, EarliestTime: startTime, LatestTime: startTime, }, WorkflowID: "visibility-filtering-test2", }) s.Nil(err5) s.Equal(1, len(resp.Executions)) s.assertClosedExecutionEquals(closeReq, resp.Executions[0]) } // TestFilteringByCloseStatus test func (s *DBVisibilityPersistenceSuite) TestFilteringByCloseStatus() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() testDomainUUID := uuid.New() startTime := time.Now().UnixNano() // Create 2 executions workflowExecution1 := types.WorkflowExecution{ WorkflowID: "visibility-filtering-test1", RunID: "fb15e4b5-356f-466d-8c6d-a29223e5c536", } err0 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution1, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, ShardID: 1234, }) s.Nil(err0) workflowExecution2 := types.WorkflowExecution{ WorkflowID: "visibility-filtering-test2", RunID: "843f6fc7-102a-4c63-a2d4-7c653b01bf52", } err1 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution2, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, ShardID: 1234, }) s.Nil(err1) stopTime := time.Now().UnixNano() // Close both executions with different status err2 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution1, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, CloseTimestamp: stopTime, Status: types.WorkflowExecutionCloseStatusCompleted, ShardID: 1234, }) s.Nil(err2) closeReq := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution2, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, Status: types.WorkflowExecutionCloseStatusFailed, CloseTimestamp: stopTime, HistoryLength: 3, ShardID: 1234, } err3 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, closeReq) s.Nil(err3) // List closed with filtering resp, err4 := s.VisibilityMgr.ListClosedWorkflowExecutionsByStatus(ctx, &p.ListClosedWorkflowExecutionsByStatusRequest{ ListWorkflowExecutionsRequest: p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, PageSize: 2, EarliestTime: startTime, LatestTime: startTime, }, Status: types.WorkflowExecutionCloseStatusFailed, }) s.Nil(err4) s.Equal(1, len(resp.Executions)) s.assertClosedExecutionEquals(closeReq, resp.Executions[0]) } // TestGetClosedExecution test func (s *DBVisibilityPersistenceSuite) TestGetClosedExecution() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() testDomainUUID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "visibility-workflow-test", RunID: "a3dbc7bf-deb1-4946-b57c-cf0615ea553f", } startTime := time.Now().Add(time.Second * -5).UnixNano() err0 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, ShardID: 1234, }) s.Nil(err0) closedResp, err1 := s.VisibilityMgr.GetClosedWorkflowExecution(ctx, &p.GetClosedWorkflowExecutionRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, }) s.Error(err1) _, ok := err1.(*types.EntityNotExistsError) s.True(ok, "EntityNotExistsError") s.Nil(closedResp) stopTime := time.Now().UnixNano() closeReq := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, Status: types.WorkflowExecutionCloseStatusFailed, CloseTimestamp: stopTime, HistoryLength: 3, ShardID: 1234, } err2 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, closeReq) s.Nil(err2) resp, err3 := s.VisibilityMgr.GetClosedWorkflowExecution(ctx, &p.GetClosedWorkflowExecutionRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, }) s.Nil(err3) s.assertClosedExecutionEquals(closeReq, resp.Execution) } // TestClosedWithoutStarted test func (s *DBVisibilityPersistenceSuite) TestClosedWithoutStarted() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() testDomainUUID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "visibility-workflow-test", RunID: "1bdb0122-e8c9-4b35-b6f8-d692ab259b09", } closedResp, err0 := s.VisibilityMgr.GetClosedWorkflowExecution(ctx, &p.GetClosedWorkflowExecutionRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, }) s.Error(err0) _, ok := err0.(*types.EntityNotExistsError) s.True(ok, "EntityNotExistsError") s.Nil(closedResp) closeReq := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: time.Now().Add(time.Second * -5).UnixNano(), Status: types.WorkflowExecutionCloseStatusFailed, CloseTimestamp: time.Now().UnixNano(), HistoryLength: 3, ShardID: 1234, } err1 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, closeReq) s.Nil(err1) resp, err2 := s.VisibilityMgr.GetClosedWorkflowExecution(ctx, &p.GetClosedWorkflowExecutionRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, }) s.Nil(err2) s.assertClosedExecutionEquals(closeReq, resp.Execution) } // TestMultipleUpserts test func (s *DBVisibilityPersistenceSuite) TestMultipleUpserts() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() testDomainUUID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "visibility-workflow-test", RunID: "a3dbc7bf-deb1-4946-b57c-cf0615ea553f", } startTime := time.Now().Add(time.Second * -5).UnixNano() closeReq := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, Status: types.WorkflowExecutionCloseStatusFailed, CloseTimestamp: time.Now().UnixNano(), HistoryLength: 3, ShardID: 1234, } count := 3 for i := 0; i < count; i++ { err0 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, ShardID: 1234, }) s.Nil(err0) if i < count-1 { err1 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, closeReq) s.Nil(err1) } } resp, err3 := s.VisibilityMgr.GetClosedWorkflowExecution(ctx, &p.GetClosedWorkflowExecutionRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, }) s.Nil(err3) s.assertClosedExecutionEquals(closeReq, resp.Execution) } // TestDelete test func (s *DBVisibilityPersistenceSuite) TestDelete() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() if s.VisibilityMgr.GetName() == "cassandra" { // this test is not applicable for cassandra return } nRows := 5 testDomainUUID := uuid.New() startTime := time.Now().Add(time.Second * -5).UnixNano() for i := 0; i < nRows; i++ { workflowExecution := types.WorkflowExecution{ WorkflowID: uuid.New(), RunID: uuid.New(), } err0 := s.VisibilityMgr.RecordWorkflowExecutionStarted(ctx, &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, }) s.Nil(err0) closeReq := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Execution: workflowExecution, WorkflowTypeName: "visibility-workflow", StartTimestamp: startTime, Status: types.WorkflowExecutionCloseStatusFailed, CloseTimestamp: time.Now().UnixNano(), HistoryLength: 3, ShardID: 1234, } err1 := s.VisibilityMgr.RecordWorkflowExecutionClosed(ctx, closeReq) s.Nil(err1) } resp, err3 := s.VisibilityMgr.ListClosedWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, EarliestTime: startTime, LatestTime: time.Now().UnixNano(), PageSize: 10, }) s.Nil(err3) s.Equal(nRows, len(resp.Executions)) remaining := nRows for _, row := range resp.Executions { err4 := s.VisibilityMgr.DeleteWorkflowExecution(ctx, &p.VisibilityDeleteWorkflowExecutionRequest{ DomainID: testDomainUUID, RunID: row.GetExecution().GetRunID(), }) s.Nil(err4) remaining-- resp, err5 := s.VisibilityMgr.ListClosedWorkflowExecutions(ctx, &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, EarliestTime: startTime, LatestTime: time.Now().UnixNano(), PageSize: 10, }) s.Nil(err5) s.Equal(remaining, len(resp.Executions)) } } // TestUpsertWorkflowExecution test func (s *DBVisibilityPersistenceSuite) TestUpsertWorkflowExecution() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() tests := []struct { request *p.UpsertWorkflowExecutionRequest expected error }{ { request: &p.UpsertWorkflowExecutionRequest{ DomainUUID: "", Domain: "", Execution: types.WorkflowExecution{}, WorkflowTypeName: "", StartTimestamp: 0, ExecutionTimestamp: 0, WorkflowTimeout: 0, TaskID: 0, Memo: nil, SearchAttributes: map[string][]byte{ definition.CadenceChangeVersion: []byte("dummy"), }, ShardID: 1234, }, expected: nil, }, { request: &p.UpsertWorkflowExecutionRequest{ DomainUUID: "", Domain: "", Execution: types.WorkflowExecution{}, WorkflowTypeName: "", StartTimestamp: 0, ExecutionTimestamp: 0, WorkflowTimeout: 0, TaskID: 0, Memo: nil, SearchAttributes: nil, ShardID: 1234, }, expected: &types.InternalServiceError{ Message: "Error writing to visibility: Operation is not supported", }, }, } for _, test := range tests { err := s.VisibilityMgr.UpsertWorkflowExecution(ctx, test.request) if test.expected == nil { s.Equal(test.expected, err) } else { s.Equal(test.expected.Error(), err.Error()) } } } func (s *DBVisibilityPersistenceSuite) assertClosedExecutionEquals( req *p.RecordWorkflowExecutionClosedRequest, resp *types.WorkflowExecutionInfo) { s.Equal(req.Execution.RunID, resp.Execution.RunID) s.Equal(req.Execution.WorkflowID, resp.Execution.WorkflowID) s.Equal(req.WorkflowTypeName, resp.GetType().GetName()) s.Equal(s.nanosToMillis(req.StartTimestamp), s.nanosToMillis(resp.GetStartTime())) s.Equal(s.nanosToMillis(req.CloseTimestamp), s.nanosToMillis(resp.GetCloseTime())) s.Equal(req.Status, resp.GetCloseStatus()) s.Equal(req.HistoryLength, resp.HistoryLength) } func (s *DBVisibilityPersistenceSuite) assertOpenExecutionEquals( req *p.RecordWorkflowExecutionStartedRequest, resp *types.WorkflowExecutionInfo) { s.Equal(req.Execution.GetRunID(), resp.Execution.GetRunID()) s.Equal(req.Execution.WorkflowID, resp.Execution.WorkflowID) s.Equal(req.WorkflowTypeName, resp.GetType().GetName()) s.Equal(s.nanosToMillis(req.StartTimestamp), s.nanosToMillis(resp.GetStartTime())) s.Nil(resp.CloseTime) s.Nil(resp.CloseStatus) s.Zero(resp.HistoryLength) } func (s *DBVisibilityPersistenceSuite) nanosToMillis(nanos int64) int64 { return nanos / int64(time.Millisecond) } ================================================ FILE: common/persistence/persistence-tests/domainAuditPersistenceTest.go ================================================ // Copyright (c) 2025 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "log" "os" "testing" "time" "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/persistence" ) type ( DomainAuditPersistenceSuite struct { *TestBase *require.Assertions } ) func (s *DomainAuditPersistenceSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } func (s *DomainAuditPersistenceSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *DomainAuditPersistenceSuite) TearDownSuite() { s.TearDownWorkflowStore() } func (s *DomainAuditPersistenceSuite) TestCreateAndGetDomainAuditLog() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() manager, err := s.ExecutionMgrFactory.NewDomainAuditManager() s.NoError(err) s.NotNil(manager) defer manager.Close() domainID := uuid.NewString() eventID := generateUUIDv7().String() now := time.Now().UTC() createReq := &persistence.CreateDomainAuditLogRequest{ DomainID: domainID, EventID: eventID, OperationType: persistence.DomainAuditOperationTypeCreate, CreatedTime: now, Identity: "test-user", IdentityType: "user", Comment: "Test domain creation", StateAfter: &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ ID: domainID, Name: "test-domain", Status: persistence.DomainStatusRegistered, Description: "Test domain", }, }, } createResp, err := manager.CreateDomainAuditLog(ctx, createReq) s.NoError(err) s.NotNil(createResp) s.Equal(eventID, createResp.EventID) getReq := &persistence.GetDomainAuditLogsRequest{ DomainID: domainID, OperationType: persistence.DomainAuditOperationTypeCreate, PageSize: 10, } getResp, err := manager.GetDomainAuditLogs(ctx, getReq) s.NoError(err) s.NotNil(getResp) s.Len(getResp.AuditLogs, 1) auditLog := getResp.AuditLogs[0] s.Equal(eventID, auditLog.EventID) s.Equal(domainID, auditLog.DomainID) s.Equal(persistence.DomainAuditOperationTypeCreate, auditLog.OperationType) s.Equal("test-user", auditLog.Identity) s.Equal("user", auditLog.IdentityType) s.Equal("Test domain creation", auditLog.Comment) s.Nil(auditLog.StateBefore) s.NotNil(auditLog.StateAfter) s.Equal(domainID, auditLog.StateAfter.Info.ID) s.Equal("test-domain", auditLog.StateAfter.Info.Name) } func (s *DomainAuditPersistenceSuite) TestCreateMultipleAuditLogs() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() manager, err := s.ExecutionMgrFactory.NewDomainAuditManager() s.NoError(err) s.NotNil(manager) defer manager.Close() domainID := uuid.NewString() now := time.Now().UTC() for i := 0; i < 5; i++ { eventID := generateUUIDv7().String() createReq := &persistence.CreateDomainAuditLogRequest{ DomainID: domainID, EventID: eventID, OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now.Add(time.Duration(i) * time.Millisecond), Identity: "test-user", IdentityType: "user", Comment: "Update operation", } _, err := manager.CreateDomainAuditLog(ctx, createReq) s.NoError(err) time.Sleep(2 * time.Millisecond) } getReq := &persistence.GetDomainAuditLogsRequest{ DomainID: domainID, OperationType: persistence.DomainAuditOperationTypeUpdate, PageSize: 10, } getResp, err := manager.GetDomainAuditLogs(ctx, getReq) s.NoError(err) s.NotNil(getResp) s.Len(getResp.AuditLogs, 5) } func (s *DomainAuditPersistenceSuite) TestGetDomainAuditLogsPagination() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() manager, err := s.ExecutionMgrFactory.NewDomainAuditManager() s.NoError(err) s.NotNil(manager) defer manager.Close() domainID := uuid.NewString() now := time.Now().UTC() numLogs := 10 for i := 0; i < numLogs; i++ { eventID := generateUUIDv7().String() createReq := &persistence.CreateDomainAuditLogRequest{ DomainID: domainID, EventID: eventID, OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now.Add(time.Duration(i) * time.Millisecond), Identity: "test-user", IdentityType: "user", } _, err := manager.CreateDomainAuditLog(ctx, createReq) s.NoError(err) time.Sleep(2 * time.Millisecond) } pageSize := 3 var allLogs []*persistence.DomainAuditLog var nextPageToken []byte for { getReq := &persistence.GetDomainAuditLogsRequest{ DomainID: domainID, OperationType: persistence.DomainAuditOperationTypeUpdate, PageSize: pageSize, NextPageToken: nextPageToken, } getResp, err := manager.GetDomainAuditLogs(ctx, getReq) s.NoError(err) s.NotNil(getResp) allLogs = append(allLogs, getResp.AuditLogs...) if len(getResp.NextPageToken) == 0 { break } nextPageToken = getResp.NextPageToken } s.Len(allLogs, numLogs) } func (s *DomainAuditPersistenceSuite) TestGetDomainAuditLogsByOperationType() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() manager, err := s.ExecutionMgrFactory.NewDomainAuditManager() s.NoError(err) s.NotNil(manager) defer manager.Close() domainID := uuid.NewString() now := time.Now().UTC() for i := 0; i < 3; i++ { eventID := generateUUIDv7().String() createReq := &persistence.CreateDomainAuditLogRequest{ DomainID: domainID, EventID: eventID, OperationType: persistence.DomainAuditOperationTypeCreate, CreatedTime: now.Add(time.Duration(i) * time.Millisecond), Identity: "test-user", IdentityType: "user", } _, err := manager.CreateDomainAuditLog(ctx, createReq) s.NoError(err) time.Sleep(2 * time.Millisecond) } for i := 0; i < 5; i++ { eventID := generateUUIDv7().String() createReq := &persistence.CreateDomainAuditLogRequest{ DomainID: domainID, EventID: eventID, OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now.Add(time.Duration(i+10) * time.Millisecond), Identity: "test-user", IdentityType: "user", } _, err := manager.CreateDomainAuditLog(ctx, createReq) s.NoError(err) time.Sleep(2 * time.Millisecond) } getReq := &persistence.GetDomainAuditLogsRequest{ DomainID: domainID, OperationType: persistence.DomainAuditOperationTypeCreate, PageSize: 10, } getResp, err := manager.GetDomainAuditLogs(ctx, getReq) s.NoError(err) s.NotNil(getResp) s.Len(getResp.AuditLogs, 3) for _, log := range getResp.AuditLogs { s.Equal(persistence.DomainAuditOperationTypeCreate, log.OperationType) } } func (s *DomainAuditPersistenceSuite) TestGetDomainAuditLogsByTimeRange() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() manager, err := s.ExecutionMgrFactory.NewDomainAuditManager() s.NoError(err) s.NotNil(manager) defer manager.Close() domainID := uuid.NewString() baseTime := time.Now().UTC() for i := 0; i < 10; i++ { eventID := generateUUIDv7().String() createdTime := baseTime.Add(time.Duration(i) * time.Second) createReq := &persistence.CreateDomainAuditLogRequest{ DomainID: domainID, EventID: eventID, OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: createdTime, Identity: "test-user", IdentityType: "user", } _, err := manager.CreateDomainAuditLog(ctx, createReq) s.NoError(err) time.Sleep(2 * time.Millisecond) } minTime := baseTime.Add(3 * time.Second) maxTime := baseTime.Add(7 * time.Second) getReq := &persistence.GetDomainAuditLogsRequest{ DomainID: domainID, OperationType: persistence.DomainAuditOperationTypeUpdate, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, PageSize: 10, } getResp, err := manager.GetDomainAuditLogs(ctx, getReq) s.NoError(err) s.NotNil(getResp) s.True(len(getResp.AuditLogs) >= 4 && len(getResp.AuditLogs) <= 5) for _, log := range getResp.AuditLogs { s.True(!log.CreatedTime.Truncate(time.Millisecond).Before(minTime.Truncate(time.Millisecond))) s.True(!log.CreatedTime.Truncate(time.Millisecond).After(maxTime.Truncate(time.Millisecond))) } } func (s *DomainAuditPersistenceSuite) TestDomainAuditLogWithStateBefore() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() manager, err := s.ExecutionMgrFactory.NewDomainAuditManager() s.NoError(err) s.NotNil(manager) defer manager.Close() domainID := uuid.NewString() eventID := generateUUIDv7().String() now := time.Now().UTC() createReq := &persistence.CreateDomainAuditLogRequest{ DomainID: domainID, EventID: eventID, OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now, Identity: "test-user", IdentityType: "user", Comment: "Update domain description", StateBefore: &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ ID: domainID, Name: "test-domain", Status: persistence.DomainStatusRegistered, Description: "Old description", }, }, StateAfter: &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ ID: domainID, Name: "test-domain", Status: persistence.DomainStatusRegistered, Description: "New description", }, }, } _, err = manager.CreateDomainAuditLog(ctx, createReq) s.NoError(err) getReq := &persistence.GetDomainAuditLogsRequest{ DomainID: domainID, OperationType: persistence.DomainAuditOperationTypeUpdate, PageSize: 10, } getResp, err := manager.GetDomainAuditLogs(ctx, getReq) s.NoError(err) s.NotNil(getResp) s.Len(getResp.AuditLogs, 1) auditLog := getResp.AuditLogs[0] s.NotNil(auditLog.StateBefore) s.NotNil(auditLog.StateAfter) s.Equal("Old description", auditLog.StateBefore.Info.Description) s.Equal("New description", auditLog.StateAfter.Info.Description) } func generateUUIDv7() uuid.UUID { id, err := uuid.NewUUID() if err != nil { panic(err) } now := time.Now() unixMillis := now.UnixMilli() id[0] = byte(unixMillis >> 40) id[1] = byte(unixMillis >> 32) id[2] = byte(unixMillis >> 24) id[3] = byte(unixMillis >> 16) id[4] = byte(unixMillis >> 8) id[5] = byte(unixMillis) id[6] = (id[6] & 0x0f) | 0x70 id[8] = (id[8] & 0x3f) | 0x80 return id } ================================================ FILE: common/persistence/persistence-tests/executionManagerTest.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "encoding/json" "fmt" "log" "math" "math/rand" "os" "sync" "sync/atomic" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/uber/cadence/common" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // ExecutionManagerSuite contains matching persistence tests ExecutionManagerSuite struct { *TestBase // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions } ) var ( testContextTimeout = 5 * time.Second largeTestContextTimeout = 30 * time.Second testWorkflowChecksum = checksum.Checksum{ Version: 22, Flavor: checksum.FlavorIEEECRC32OverThriftBinary, Value: []byte("test-checksum"), } ) // SetupSuite implementation func (s *ExecutionManagerSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } // TearDownSuite implementation func (s *ExecutionManagerSuite) TearDownSuite() { s.TearDownWorkflowStore() } // SetupTest implementation func (s *ExecutionManagerSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) s.ClearTasks() } func (s *ExecutionManagerSuite) newRandomChecksum() checksum.Checksum { return checksum.Checksum{ Flavor: checksum.FlavorIEEECRC32OverThriftBinary, Version: 22, Value: uuid.NewRandom(), } } func (s *ExecutionManagerSuite) assertChecksumsEqual(expected checksum.Checksum, actual checksum.Checksum) { s.EqualValues(expected, actual) } // TestCreateWorkflowExecutionDeDup test func (s *ExecutionManagerSuite) TestCreateWorkflowExecutionDeDup() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() domainName := uuid.New() workflowID := "create-workflow-test-dedup" runID := "3969fae6-6b75-4c2a-b74b-4054edd296a6" workflowExecution := types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, } tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) csum := s.newRandomChecksum() versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) req := &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowID, RunID: runID, FirstExecutionRunID: runID, TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, State: p.WorkflowStateCreated, CloseStatus: p.WorkflowCloseStatusNone, }, ExecutionStats: &p.ExecutionStats{}, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.CreateWorkflowModeBrandNew, DomainName: domainName, } _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) info, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.assertChecksumsEqual(csum, info.Checksum) updatedInfo := copyWorkflowExecutionInfo(info.ExecutionInfo) updatedStats := copyExecutionStats(info.ExecutionStats) updatedInfo.State = p.WorkflowStateCompleted updatedInfo.CloseStatus = p.WorkflowCloseStatusCompleted _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, Condition: nextEventID, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeUpdateCurrent, }) s.NoError(err) req.Mode = p.CreateWorkflowModeWorkflowIDReuse req.PreviousRunID = runID req.PreviousLastWriteVersion = constants.EmptyVersion _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Error(err) s.IsType(&p.WorkflowExecutionAlreadyStartedError{}, err) } func (s *ExecutionManagerSuite) TestCreateWorkflowExecutionWithWorkflowRequestsDedup() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() domainName := uuid.New() workflowID := "create-workflow-test-dedup" runID := uuid.New() requestID := uuid.New() tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) csum := s.newRandomChecksum() versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) req := &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: requestID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, FirstExecutionRunID: runID, TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, State: p.WorkflowStateCreated, CloseStatus: p.WorkflowCloseStatusNone, }, ExecutionStats: &p.ExecutionStats{}, Checksum: csum, VersionHistories: versionHistories, WorkflowRequests: []*p.WorkflowRequest{ { RequestID: requestID, Version: 1, RequestType: p.WorkflowRequestTypeStart, }, }, }, RangeID: s.ShardInfo.RangeID, Mode: p.CreateWorkflowModeBrandNew, WorkflowRequestMode: p.CreateWorkflowRequestModeReplicated, DomainName: domainName, } _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) req.WorkflowRequestMode = p.CreateWorkflowRequestModeNew _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Error(err) s.IsType(&p.DuplicateRequestError{}, err) s.Equal(persistence.WorkflowRequestTypeStart, err.(*persistence.DuplicateRequestError).RequestType) s.Equal(runID, err.(*persistence.DuplicateRequestError).RunID) req.WorkflowRequestMode = p.CreateWorkflowRequestModeReplicated _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Error(err) s.IsType(&p.WorkflowExecutionAlreadyStartedError{}, err) } // TestCreateWorkflowExecutionStateCloseStatus test func (s *ExecutionManagerSuite) TestCreateWorkflowExecutionStateCloseStatus() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() invalidCloseStatuses := []int{ p.WorkflowCloseStatusCompleted, p.WorkflowCloseStatusFailed, p.WorkflowCloseStatusCanceled, p.WorkflowCloseStatusTerminated, p.WorkflowCloseStatusContinuedAsNew, p.WorkflowCloseStatusTimedOut, } tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) csum := s.newRandomChecksum() versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) req := &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, }, ExecutionStats: &p.ExecutionStats{}, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.CreateWorkflowModeBrandNew, } workflowExecutionStatusCreated := types.WorkflowExecution{ WorkflowID: "create-workflow-test-state-created", RunID: uuid.New(), } req.NewWorkflowSnapshot.ExecutionInfo.WorkflowID = workflowExecutionStatusCreated.GetWorkflowID() req.NewWorkflowSnapshot.ExecutionInfo.RunID = workflowExecutionStatusCreated.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.FirstExecutionRunID = workflowExecutionStatusCreated.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.State = p.WorkflowStateCreated for _, invalidCloseStatus := range invalidCloseStatuses { req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = invalidCloseStatus _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.IsType(&types.InternalServiceError{}, err) } req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) info, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionStatusCreated) s.Nil(err) s.Equal(p.WorkflowStateCreated, info.ExecutionInfo.State) s.Equal(p.WorkflowCloseStatusNone, info.ExecutionInfo.CloseStatus) s.assertChecksumsEqual(csum, info.Checksum) workflowExecutionStatusRunning := types.WorkflowExecution{ WorkflowID: "create-workflow-test-state-running", RunID: uuid.New(), } req.NewWorkflowSnapshot.ExecutionInfo.WorkflowID = workflowExecutionStatusRunning.GetWorkflowID() req.NewWorkflowSnapshot.ExecutionInfo.RunID = workflowExecutionStatusRunning.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.FirstExecutionRunID = workflowExecutionStatusRunning.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.State = p.WorkflowStateRunning for _, invalidCloseStatus := range invalidCloseStatuses { req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = invalidCloseStatus _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.IsType(&types.InternalServiceError{}, err) } req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) info, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionStatusRunning) s.Nil(err) s.Equal(p.WorkflowStateRunning, info.ExecutionInfo.State) s.Equal(p.WorkflowCloseStatusNone, info.ExecutionInfo.CloseStatus) s.assertChecksumsEqual(csum, info.Checksum) workflowExecutionStatusCompleted := types.WorkflowExecution{ WorkflowID: "create-workflow-test-state-completed", RunID: uuid.New(), } req.NewWorkflowSnapshot.ExecutionInfo.WorkflowID = workflowExecutionStatusCompleted.GetWorkflowID() req.NewWorkflowSnapshot.ExecutionInfo.RunID = workflowExecutionStatusCompleted.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.FirstExecutionRunID = workflowExecutionStatusCompleted.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.State = p.WorkflowStateCompleted for _, invalidCloseStatus := range invalidCloseStatuses { req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = invalidCloseStatus _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.IsType(&types.InternalServiceError{}, err) } req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.IsType(&types.InternalServiceError{}, err) // for zombie workflow creation, we must use existing workflow ID which got created // since we do not allow creation of zombie workflow without current record workflowExecutionStatusZombie := types.WorkflowExecution{ WorkflowID: workflowExecutionStatusRunning.WorkflowID, RunID: uuid.New(), } req.Mode = p.CreateWorkflowModeZombie req.NewWorkflowSnapshot.ExecutionInfo.WorkflowID = workflowExecutionStatusZombie.GetWorkflowID() req.NewWorkflowSnapshot.ExecutionInfo.RunID = workflowExecutionStatusZombie.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.FirstExecutionRunID = workflowExecutionStatusZombie.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.State = p.WorkflowStateZombie for _, invalidCloseStatus := range invalidCloseStatuses { req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = invalidCloseStatus _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.IsType(&types.InternalServiceError{}, err) } req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) info, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionStatusZombie) s.Nil(err) s.Equal(p.WorkflowStateZombie, info.ExecutionInfo.State) s.Equal(p.WorkflowCloseStatusNone, info.ExecutionInfo.CloseStatus) s.assertChecksumsEqual(csum, info.Checksum) } // TestCreateWorkflowExecutionWithZombieState test func (s *ExecutionManagerSuite) TestCreateWorkflowExecutionWithZombieState() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() workflowID := "create-workflow-test-with-zombie-state" workflowExecutionZombie1 := types.WorkflowExecution{ WorkflowID: workflowID, RunID: uuid.New(), } tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) csum := s.newRandomChecksum() versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) req := &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowID, RunID: workflowExecutionZombie1.GetRunID(), FirstExecutionRunID: workflowExecutionZombie1.GetRunID(), TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, State: p.WorkflowStateZombie, CloseStatus: p.WorkflowCloseStatusNone, }, ExecutionStats: &p.ExecutionStats{}, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.CreateWorkflowModeZombie, } _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) // allow creating a zombie workflow if no current running workflow _, err = s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.IsType(&types.EntityNotExistsError{}, err) // no current workflow workflowExecutionRunning := types.WorkflowExecution{ WorkflowID: workflowID, RunID: uuid.New(), } req.NewWorkflowSnapshot.ExecutionInfo.RunID = workflowExecutionRunning.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.FirstExecutionRunID = workflowExecutionRunning.GetRunID() req.Mode = p.CreateWorkflowModeBrandNew req.NewWorkflowSnapshot.ExecutionInfo.State = p.WorkflowStateRunning req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) currentRunID, err := s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.Nil(err) s.Equal(workflowExecutionRunning.GetRunID(), currentRunID) workflowExecutionZombie := types.WorkflowExecution{ WorkflowID: workflowID, RunID: uuid.New(), } req.NewWorkflowSnapshot.ExecutionInfo.RunID = workflowExecutionZombie.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.FirstExecutionRunID = workflowExecutionZombie.GetRunID() req.Mode = p.CreateWorkflowModeZombie req.NewWorkflowSnapshot.ExecutionInfo.State = p.WorkflowStateZombie req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) // current run ID is still the prev running run ID currentRunID, err = s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecutionRunning.GetWorkflowID()) s.Nil(err) s.Equal(workflowExecutionRunning.GetRunID(), currentRunID) info, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionZombie) s.Nil(err) s.Equal(p.WorkflowStateZombie, info.ExecutionInfo.State) s.Equal(p.WorkflowCloseStatusNone, info.ExecutionInfo.CloseStatus) s.assertChecksumsEqual(csum, info.Checksum) } func (s *ExecutionManagerSuite) TestUpdateWorkflowExecutionWithWorkflowRequestsDedup() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() domainName := uuid.New() workflowID := "create-workflow-test-dedup" runID := uuid.New() requestID := uuid.New() tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) csum := s.newRandomChecksum() versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) req := &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: requestID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, FirstExecutionRunID: runID, TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, State: p.WorkflowStateCreated, CloseStatus: p.WorkflowCloseStatusNone, }, ExecutionStats: &p.ExecutionStats{}, Checksum: csum, VersionHistories: versionHistories, WorkflowRequests: []*p.WorkflowRequest{ { RequestID: requestID, Version: 1, RequestType: p.WorkflowRequestTypeStart, }, { RequestID: requestID, Version: 1, RequestType: p.WorkflowRequestTypeSignal, }, }, }, RangeID: s.ShardInfo.RangeID, Mode: p.CreateWorkflowModeBrandNew, WorkflowRequestMode: p.CreateWorkflowRequestModeNew, DomainName: domainName, } _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) info, err := s.GetWorkflowExecutionInfo(ctx, domainID, types.WorkflowExecution{WorkflowID: workflowID, RunID: runID}) s.Nil(err) csum = s.newRandomChecksum() // update the checksum to new value updatedInfo := copyWorkflowExecutionInfo(info.ExecutionInfo) updatedStats := copyExecutionStats(info.ExecutionStats) updatedInfo.State = p.WorkflowStateRunning updatedInfo.CloseStatus = p.WorkflowCloseStatusNone updateReq := &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, Condition: nextEventID, Checksum: csum, VersionHistories: versionHistories, WorkflowRequests: []*p.WorkflowRequest{ { RequestID: requestID, Version: 1, RequestType: p.WorkflowRequestTypeSignal, }, }, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeUpdateCurrent, WorkflowRequestMode: p.CreateWorkflowRequestModeNew, } _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, updateReq) s.Error(err) s.IsType(&p.DuplicateRequestError{}, err) s.Equal(persistence.WorkflowRequestTypeSignal, err.(*persistence.DuplicateRequestError).RequestType) s.Equal(runID, err.(*persistence.DuplicateRequestError).RunID) updateReq.WorkflowRequestMode = p.CreateWorkflowRequestModeReplicated _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, updateReq) s.Nil(err) updateReq.UpdateWorkflowMutation.WorkflowRequests[0].RequestID = uuid.New() updateReq.WorkflowRequestMode = p.CreateWorkflowRequestModeNew _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, updateReq) s.Nil(err) } // TestUpdateWorkflowExecutionStateCloseStatus test func (s *ExecutionManagerSuite) TestUpdateWorkflowExecutionStateCloseStatus() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "update-workflow-test-state", RunID: uuid.New(), } closeStatuses := []int{ p.WorkflowCloseStatusCompleted, p.WorkflowCloseStatusFailed, p.WorkflowCloseStatusCanceled, p.WorkflowCloseStatusTerminated, p.WorkflowCloseStatusContinuedAsNew, p.WorkflowCloseStatusTimedOut, } tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) csum := s.newRandomChecksum() versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) req := &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), FirstExecutionRunID: workflowExecution.GetRunID(), TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, }, ExecutionStats: &p.ExecutionStats{}, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.CreateWorkflowModeBrandNew, } req.NewWorkflowSnapshot.ExecutionInfo.State = p.WorkflowStateCreated req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) info, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.Equal(p.WorkflowStateCreated, info.ExecutionInfo.State) s.Equal(p.WorkflowCloseStatusNone, info.ExecutionInfo.CloseStatus) s.assertChecksumsEqual(csum, info.Checksum) csum = s.newRandomChecksum() // update the checksum to new value updatedInfo := copyWorkflowExecutionInfo(info.ExecutionInfo) updatedStats := copyExecutionStats(info.ExecutionStats) updatedInfo.State = p.WorkflowStateRunning updatedInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, Condition: nextEventID, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeUpdateCurrent, }) s.NoError(err) info, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.Equal(p.WorkflowStateRunning, info.ExecutionInfo.State) s.Equal(p.WorkflowCloseStatusNone, info.ExecutionInfo.CloseStatus) s.assertChecksumsEqual(csum, info.Checksum) updatedInfo = copyWorkflowExecutionInfo(info.ExecutionInfo) updatedStats = copyExecutionStats(info.ExecutionStats) updatedInfo.State = p.WorkflowStateRunning for _, closeStatus := range closeStatuses { updatedInfo.CloseStatus = closeStatus _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, Condition: nextEventID, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeUpdateCurrent, }) s.IsType(&types.InternalServiceError{}, err) } updatedInfo = copyWorkflowExecutionInfo(info.ExecutionInfo) updatedStats = copyExecutionStats(info.ExecutionStats) updatedInfo.State = p.WorkflowStateCompleted updatedInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, Condition: nextEventID, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeUpdateCurrent, }) s.IsType(&types.InternalServiceError{}, err) for _, closeStatus := range closeStatuses { updatedInfo.CloseStatus = closeStatus _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, Condition: nextEventID, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeUpdateCurrent, }) s.Nil(err) info, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.Equal(p.WorkflowStateCompleted, info.ExecutionInfo.State) s.Equal(closeStatus, info.ExecutionInfo.CloseStatus) } // create a new workflow with same domain ID & workflow ID // to enable update workflow with zombie status workflowExecutionRunning := types.WorkflowExecution{ WorkflowID: workflowExecution.WorkflowID, RunID: uuid.New(), } req.NewWorkflowSnapshot.ExecutionInfo.WorkflowID = workflowExecutionRunning.GetWorkflowID() req.NewWorkflowSnapshot.ExecutionInfo.RunID = workflowExecutionRunning.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.FirstExecutionRunID = workflowExecutionRunning.GetRunID() req.Mode = p.CreateWorkflowModeWorkflowIDReuse req.PreviousRunID = workflowExecution.GetRunID() req.PreviousLastWriteVersion = constants.EmptyVersion req.NewWorkflowSnapshot.ExecutionInfo.State = p.WorkflowStateRunning req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) updatedInfo = copyWorkflowExecutionInfo(info.ExecutionInfo) updatedStats = copyExecutionStats(info.ExecutionStats) updatedInfo.State = p.WorkflowStateZombie updatedInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, Condition: nextEventID, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeBypassCurrent, }) s.NoError(err) info, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.Equal(p.WorkflowStateZombie, info.ExecutionInfo.State) s.Equal(p.WorkflowCloseStatusNone, info.ExecutionInfo.CloseStatus) updatedInfo = copyWorkflowExecutionInfo(info.ExecutionInfo) updatedStats = copyExecutionStats(info.ExecutionStats) updatedInfo.State = p.WorkflowStateZombie for _, closeStatus := range closeStatuses { updatedInfo.CloseStatus = closeStatus _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, Condition: nextEventID, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeBypassCurrent, }) s.IsType(&types.InternalServiceError{}, err) } } // TestUpdateWorkflowExecutionWithZombieState test func (s *ExecutionManagerSuite) TestUpdateWorkflowExecutionWithZombieState() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() workflowID := "create-workflow-test-with-zombie-state" workflowExecution := types.WorkflowExecution{ WorkflowID: workflowID, RunID: uuid.New(), } tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) csum := s.newRandomChecksum() versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) // create and update a workflow to make it completed req := &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), FirstExecutionRunID: workflowExecution.GetRunID(), TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, }, ExecutionStats: &p.ExecutionStats{}, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.CreateWorkflowModeBrandNew, } _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) currentRunID, err := s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.Nil(err) s.Equal(workflowExecution.GetRunID(), currentRunID) info, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.assertChecksumsEqual(csum, info.Checksum) // try to turn current workflow into zombie state, this should end with an error updatedInfo := copyWorkflowExecutionInfo(info.ExecutionInfo) updateStats := copyExecutionStats(info.ExecutionStats) updatedInfo.State = p.WorkflowStateZombie updatedInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updateStats, Condition: nextEventID, Checksum: csum, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeBypassCurrent, }) s.NotNil(err) updatedInfo = copyWorkflowExecutionInfo(info.ExecutionInfo) updateStats = copyExecutionStats(info.ExecutionStats) updatedInfo.State = p.WorkflowStateCompleted updatedInfo.CloseStatus = p.WorkflowCloseStatusCompleted _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updateStats, Condition: nextEventID, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeUpdateCurrent, }) s.NoError(err) // create a new workflow with same domain ID & workflow ID workflowExecutionRunning := types.WorkflowExecution{ WorkflowID: workflowID, RunID: uuid.New(), } csum = checksum.Checksum{} // set checksum to nil req.NewWorkflowSnapshot.ExecutionInfo.WorkflowID = workflowExecutionRunning.GetWorkflowID() req.NewWorkflowSnapshot.ExecutionInfo.RunID = workflowExecutionRunning.GetRunID() req.NewWorkflowSnapshot.ExecutionInfo.FirstExecutionRunID = workflowExecutionRunning.GetRunID() req.Mode = p.CreateWorkflowModeWorkflowIDReuse req.PreviousRunID = workflowExecution.GetRunID() req.PreviousLastWriteVersion = constants.EmptyVersion req.NewWorkflowSnapshot.ExecutionInfo.State = p.WorkflowStateRunning req.NewWorkflowSnapshot.ExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone req.NewWorkflowSnapshot.Checksum = csum _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) currentRunID, err = s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.Nil(err) s.Equal(workflowExecutionRunning.GetRunID(), currentRunID) // get the workflow to be turned into a zombie info, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.assertChecksumsEqual(csum, info.Checksum) updatedInfo = copyWorkflowExecutionInfo(info.ExecutionInfo) updateStats = copyExecutionStats(info.ExecutionStats) updatedInfo.State = p.WorkflowStateZombie updatedInfo.CloseStatus = p.WorkflowCloseStatusNone _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updateStats, Condition: nextEventID, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeBypassCurrent, }) s.NoError(err) info, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.Equal(p.WorkflowStateZombie, info.ExecutionInfo.State) s.Equal(p.WorkflowCloseStatusNone, info.ExecutionInfo.CloseStatus) s.assertChecksumsEqual(csum, info.Checksum) // check current run ID is un touched currentRunID, err = s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.Nil(err) s.Equal(workflowExecutionRunning.GetRunID(), currentRunID) } // TestUpdateWorkflowExecutionTasks test func (s *ExecutionManagerSuite) TestUpdateWorkflowExecutionTasks() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "b0a8571c-0257-40ea-afcd-3a14eae181c0" workflowExecution := types.WorkflowExecution{ WorkflowID: "update-workflow-tasks-test", RunID: "5ba5e531-e46b-48d9-b4b3-859919839553", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") taskD, err := s.GetTransferTasks(ctx, 1, false) s.NoError(err) s.Equal(1, len(taskD), "Expected 1 decision task.") err = s.CompleteTransferTask(ctx, taskD[0].GetTaskID()) s.NoError(err) state1, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err) info1 := state1.ExecutionInfo s.NotNil(info1, "Valid Workflow info expected.") updatedInfo1 := copyWorkflowExecutionInfo(info1) updatedStats1 := copyExecutionStats(state1.ExecutionStats) now := time.Now() transferTasks := []p.Task{ &p.ActivityTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: s.GetNextSequenceNumber(), Version: constants.EmptyVersion, }, TargetDomainID: domainID, TaskList: "some randome tasklist name", ScheduleID: 123, }, } timerTasks := []p.Task{ &p.UserTimerTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now.Add(time.Minute), TaskID: s.GetNextSequenceNumber(), Version: constants.EmptyVersion, }, TaskList: "some random tasklist", EventID: 124, }, } err = s.UpdateWorkflowExecutionTasks( ctx, updatedInfo1, updatedStats1, int64(3), transferTasks, timerTasks, ) s.NoError(err) loadedTransferTasks, err := s.GetTransferTasks(ctx, 10, true) s.NoError(err) s.Len(loadedTransferTasks, len(transferTasks)) loadedTimerTasks, err := s.GetTimerIndexTasks(ctx, 10, true) s.NoError(err) s.Len(loadedTimerTasks, len(timerTasks)) } // TestCreateWorkflowExecutionBrandNew test func (s *ExecutionManagerSuite) TestCreateWorkflowExecutionBrandNew() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "create-workflow-test-brand-new", RunID: uuid.New(), } tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) req := &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), FirstExecutionRunID: workflowExecution.GetRunID(), TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, }, ExecutionStats: &p.ExecutionStats{}, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.CreateWorkflowModeBrandNew, } _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) _, err = s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.NotNil(err) alreadyStartedErr, ok := err.(*p.WorkflowExecutionAlreadyStartedError) s.True(ok, "err is not WorkflowExecutionAlreadyStartedError") s.Equal(req.NewWorkflowSnapshot.ExecutionInfo.CreateRequestID, alreadyStartedErr.StartRequestID) s.Equal(workflowExecution.GetRunID(), alreadyStartedErr.RunID) s.Equal(0, alreadyStartedErr.CloseStatus) s.Equal(p.WorkflowStateRunning, alreadyStartedErr.State) } // TestUpsertWorkflowActivity test func (s *ExecutionManagerSuite) TestUpsertWorkflowActivity() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() workflowID := "create-workflow-test-with-upsert-activity" workflowExecution := types.WorkflowExecution{ WorkflowID: workflowID, RunID: uuid.New(), } tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) csum := s.newRandomChecksum() versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) // create and update a workflow to make it completed req := &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), FirstExecutionRunID: workflowExecution.GetRunID(), TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, }, ExecutionStats: &p.ExecutionStats{}, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.CreateWorkflowModeBrandNew, } _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) s.Nil(err) currentRunID, err := s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.Nil(err) s.Equal(workflowExecution.GetRunID(), currentRunID) info, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.assertChecksumsEqual(csum, info.Checksum) s.Equal(0, len(info.ActivityInfos)) // insert a new activity updatedInfo := copyWorkflowExecutionInfo(info.ExecutionInfo) updateStats := copyExecutionStats(info.ExecutionStats) _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updateStats, Condition: nextEventID, Checksum: csum, UpsertActivityInfos: []*p.ActivityInfo{ { Version: 0, ScheduleID: 100, TaskList: "test-activity-tasklist-1", }, }, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeUpdateCurrent, }) s.Nil(err) info2, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.Equal(1, len(info2.ActivityInfos)) s.Equal("test-activity-tasklist-1", info2.ActivityInfos[100].TaskList) // upsert the previous activity _, err = s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updateStats, Condition: nextEventID, Checksum: csum, UpsertActivityInfos: []*p.ActivityInfo{ { Version: 0, ScheduleID: 100, TaskList: "test-activity-tasklist-2", }, }, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeUpdateCurrent, }) s.NoError(err) info3, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Nil(err) s.Equal(1, len(info3.ActivityInfos)) s.Equal("test-activity-tasklist-2", info3.ActivityInfos[100].TaskList) } // TestCreateWorkflowExecutionRunIDReuseWithoutReplication test func (s *ExecutionManagerSuite) TestCreateWorkflowExecutionRunIDReuseWithoutReplication() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "create-workflow-test-run-id-reuse-without-replication", RunID: uuid.New(), } tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) decisionScheduleID := int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: decisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, tasklist, workflowType, workflowTimeout, decisionTimeout, nil, nextEventID, lastProcessedEventID, decisionScheduleID, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) s.assertChecksumsEqual(testWorkflowChecksum, state0.Checksum) info0 := state0.ExecutionInfo closeInfo := copyWorkflowExecutionInfo(info0) closeInfo.State = p.WorkflowStateCompleted closeInfo.CloseStatus = p.WorkflowCloseStatusCompleted closeInfo.NextEventID = int64(5) closeInfo.LastProcessedEvent = int64(2) err2 := s.UpdateWorkflowExecution(ctx, closeInfo, state0.ExecutionStats, versionHistories, nil, nil, nextEventID, nil, nil, nil, nil, nil) s.NoError(err2) newExecution := types.WorkflowExecution{ WorkflowID: workflowExecution.GetWorkflowID(), RunID: uuid.New(), } // this create should work since we are relying the business logic in history engine // to check whether the existing running workflow has finished _, err3 := s.ExecutionManager.CreateWorkflowExecution(ctx, &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: newExecution.GetWorkflowID(), RunID: newExecution.GetRunID(), FirstExecutionRunID: newExecution.GetRunID(), TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, }, ExecutionStats: &p.ExecutionStats{}, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.CreateWorkflowModeWorkflowIDReuse, PreviousRunID: workflowExecution.GetRunID(), PreviousLastWriteVersion: constants.EmptyVersion, }) s.NoError(err3) } // TestCreateWorkflowExecutionConcurrentCreate test func (s *ExecutionManagerSuite) TestCreateWorkflowExecutionConcurrentCreate() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "create-workflow-test-concurrent-create", RunID: uuid.New(), } tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) decisionScheduleID := int64(2) task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, tasklist, workflowType, workflowTimeout, decisionTimeout, nil, nextEventID, lastProcessedEventID, decisionScheduleID, nil, nil) s.Nil(err0, "No error expected.") s.NotNil(task0, "Expected non empty task identifier.") times := 2 var wg sync.WaitGroup wg.Add(times) var numOfErr int32 var lastError error for i := 0; i < times; i++ { go func() { newExecution := types.WorkflowExecution{ WorkflowID: workflowExecution.GetWorkflowID(), RunID: uuid.New(), } state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo continueAsNewInfo := copyWorkflowExecutionInfo(info0) continueAsNewInfo.State = p.WorkflowStateRunning continueAsNewInfo.NextEventID = int64(5) continueAsNewInfo.LastProcessedEvent = int64(2) err2 := s.ContinueAsNewExecution(ctx, continueAsNewInfo, state0.ExecutionStats, info0.NextEventID, newExecution, int64(3), int64(2), nil) if err2 != nil { errCount := atomic.AddInt32(&numOfErr, 1) if errCount > 1 { lastError = err2 } } wg.Done() }() } wg.Wait() if lastError != nil { s.Fail("More than one error: %v", lastError.Error()) } s.Equal(int32(1), atomic.LoadInt32(&numOfErr)) } // TestPersistenceStartWorkflow test func (s *ExecutionManagerSuite) TestPersistenceStartWorkflow() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "2d7994bf-9de8-459d-9c81-e723daedb246" workflowExecution := types.WorkflowExecution{ WorkflowID: "start-workflow-test", RunID: "7f9fe8a0-9237-11e6-ae22-56b6b6499611", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") task1, err1 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType1", 20, 14, nil, 3, 0, 2, nil, nil) s.Error(err1, "Expected workflow creation to fail.") s.T().Logf("Unable to start workflow execution: %v\n", err1) startedErr, ok := err1.(*p.WorkflowExecutionAlreadyStartedError) s.True(ok, fmt.Sprintf("Expected WorkflowExecutionAlreadyStartedError, but actual is %v", err1)) s.Equal(workflowExecution.GetRunID(), startedErr.RunID, startedErr.Msg) s.Equal(p.WorkflowStateRunning, startedErr.State, startedErr.Msg) s.Equal(p.WorkflowCloseStatusNone, startedErr.CloseStatus, startedErr.Msg) s.Equal(constants.EmptyVersion, startedErr.LastWriteVersion, startedErr.Msg) s.Empty(task1, "Expected empty task identifier.") decisionScheduleID := int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: decisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) response, err2 := s.ExecutionManager.CreateWorkflowExecution(ctx, &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), FirstExecutionRunID: workflowExecution.GetRunID(), TaskList: "queue1", WorkflowTypeName: "workflow_type_test", WorkflowTimeout: 20, DecisionStartToCloseTimeout: 13, ExecutionContext: nil, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, LastFirstEventID: constants.FirstEventID, NextEventID: int64(3), LastProcessedEvent: 0, DecisionScheduleID: decisionScheduleID, DecisionStartedID: constants.EmptyEventID, DecisionTimeout: 1, }, ExecutionStats: &p.ExecutionStats{}, TasksByCategory: map[p.HistoryTaskCategory][]p.Task{ p.HistoryTaskCategoryTransfer: []p.Task{ &p.DecisionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), }, TargetDomainID: domainID, TaskList: "queue1", ScheduleID: int64(2), OriginalTaskList: "queue1", OriginalTaskListKind: types.TaskListKindEphemeral, }, }, }, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID - 1, }) s.Error(err2, "Expected workflow creation to fail.") s.Nil(response) s.T().Logf("Unable to start workflow execution: %v\n", err2) s.IsType(&p.ShardOwnershipLostError{}, err2) } // TestGetWorkflow test func (s *ExecutionManagerSuite) TestGetWorkflow() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() now := time.Now() testResetPoints := types.ResetPoints{ Points: []*types.ResetPointInfo{ { BinaryChecksum: "test-binary-checksum", RunID: "test-runID", FirstDecisionCompletedID: 123, CreatedTimeNano: common.Int64Ptr(456), Resettable: true, ExpiringTimeNano: common.Int64Ptr(789), }, }, } testSearchAttrKey := "env" testSearchAttrVal, _ := json.Marshal("test") testSearchAttr := map[string][]byte{ testSearchAttrKey: testSearchAttrVal, } testMemoKey := "memoKey" testMemoVal, _ := json.Marshal("memoVal") testMemo := map[string][]byte{ testMemoKey: testMemoVal, } testPartitionConfig := map[string]string{ "zone": "dca1", } csum := s.newRandomChecksum() decisionScheduleID := int64(rand.Int31()) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: decisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) createReq := &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: uuid.New(), WorkflowID: "get-workflow-test", RunID: uuid.New(), ParentDomainID: uuid.New(), ParentWorkflowID: "get-workflow-test-parent", ParentRunID: uuid.New(), InitiatedID: rand.Int63(), TaskList: "get-wf-test-tasklist", WorkflowTypeName: "code.uber.internal/test/workflow", WorkflowTimeout: rand.Int31(), DecisionStartToCloseTimeout: rand.Int31(), ExecutionContext: []byte("test-execution-context"), State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, LastFirstEventID: constants.FirstEventID, NextEventID: rand.Int63(), LastProcessedEvent: int64(rand.Int31()), LastUpdatedTimestamp: now, StartTimestamp: now, SignalCount: rand.Int31(), DecisionVersion: int64(rand.Int31()), DecisionScheduleID: decisionScheduleID, DecisionStartedID: int64(rand.Int31()), DecisionTimeout: rand.Int31(), Attempt: rand.Int31(), HasRetryPolicy: true, InitialInterval: rand.Int31(), BackoffCoefficient: 7.78, MaximumInterval: rand.Int31(), ExpirationTime: time.Now(), MaximumAttempts: rand.Int31(), NonRetriableErrors: []string{"badRequestError", "accessDeniedError"}, CronSchedule: "* * * * *", CronOverlapPolicy: types.CronOverlapPolicySkipped, ExpirationSeconds: rand.Int31(), AutoResetPoints: &testResetPoints, SearchAttributes: testSearchAttr, Memo: testMemo, PartitionConfig: testPartitionConfig, ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyRegionSticky.Ptr(), StickyRegion: "region1", }, }, ExecutionStats: &p.ExecutionStats{ HistorySize: int64(rand.Int31()), }, Checksum: csum, VersionHistories: versionHistories, }, Mode: p.CreateWorkflowModeBrandNew, } createReq.NewWorkflowSnapshot.ExecutionInfo.FirstExecutionRunID = createReq.NewWorkflowSnapshot.ExecutionInfo.RunID createResp, err := s.ExecutionManager.CreateWorkflowExecution(ctx, createReq) s.NoError(err) s.NotNil(createResp, "Expected non empty task identifier.") state, err := s.GetWorkflowExecutionInfo(ctx, createReq.NewWorkflowSnapshot.ExecutionInfo.DomainID, types.WorkflowExecution{ WorkflowID: createReq.NewWorkflowSnapshot.ExecutionInfo.WorkflowID, RunID: createReq.NewWorkflowSnapshot.ExecutionInfo.RunID, }) s.NoError(err) info := state.ExecutionInfo s.NotNil(info, "Valid Workflow response expected.") s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.CreateRequestID, info.CreateRequestID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.DomainID, info.DomainID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.WorkflowID, info.WorkflowID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.RunID, info.RunID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.FirstExecutionRunID, info.FirstExecutionRunID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.ParentDomainID, info.ParentDomainID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.ParentWorkflowID, info.ParentWorkflowID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.ParentRunID, info.ParentRunID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.InitiatedID, info.InitiatedID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.TaskList, info.TaskList) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.WorkflowTypeName, info.WorkflowTypeName) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.WorkflowTimeout, info.WorkflowTimeout) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.DecisionStartToCloseTimeout, info.DecisionStartToCloseTimeout) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.ExecutionContext, info.ExecutionContext) s.Equal(p.WorkflowStateRunning, info.State) s.Equal(p.WorkflowCloseStatusNone, info.CloseStatus) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.NextEventID, info.NextEventID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.LastProcessedEvent, info.LastProcessedEvent) s.Equal(true, s.validateTimeRange(info.LastUpdatedTimestamp, time.Hour)) s.Equal(true, s.validateTimeRange(info.StartTimestamp, time.Hour)) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.DecisionVersion, info.DecisionVersion) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.DecisionScheduleID, info.DecisionScheduleID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.DecisionStartedID, info.DecisionStartedID) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.DecisionTimeout, info.DecisionTimeout) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.SignalCount, info.SignalCount) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.Attempt, info.Attempt) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.HasRetryPolicy, info.HasRetryPolicy) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.InitialInterval, info.InitialInterval) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.BackoffCoefficient, info.BackoffCoefficient) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.MaximumAttempts, info.MaximumAttempts) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.MaximumInterval, info.MaximumInterval) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.ExpirationSeconds, info.ExpirationSeconds) s.EqualTimes(createReq.NewWorkflowSnapshot.ExecutionInfo.ExpirationTime, info.ExpirationTime) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.CronSchedule, info.CronSchedule) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.CronOverlapPolicy, info.CronOverlapPolicy) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.ActiveClusterSelectionPolicy, info.ActiveClusterSelectionPolicy) s.Equal(createReq.NewWorkflowSnapshot.ExecutionInfo.NonRetriableErrors, info.NonRetriableErrors) s.Equal(testResetPoints, *info.AutoResetPoints) s.Equal(createReq.NewWorkflowSnapshot.ExecutionStats.HistorySize, state.ExecutionStats.HistorySize) val, ok := info.SearchAttributes[testSearchAttrKey] s.True(ok) s.Equal(testSearchAttrVal, val) val, ok = info.Memo[testMemoKey] s.True(ok) s.Equal(testMemoVal, val) s.Equal(testPartitionConfig, info.PartitionConfig) s.assertChecksumsEqual(csum, state.Checksum) } // TestUpdateWorkflow test func (s *ExecutionManagerSuite) TestUpdateWorkflow() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "b0a8571c-0257-40ea-afcd-3a14eae181c0" workflowExecution := types.WorkflowExecution{ WorkflowID: "update-workflow-test", RunID: "5ba5e531-e46b-48d9-b4b3-859919839553", } partitionConfig0 := map[string]string{ "userID": uuid.New(), } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, partitionConfig0) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") s.Equal(domainID, info0.DomainID) s.Equal("update-workflow-test", info0.WorkflowID) s.Equal("5ba5e531-e46b-48d9-b4b3-859919839553", info0.RunID) s.Equal("5ba5e531-e46b-48d9-b4b3-859919839553", info0.FirstExecutionRunID) s.Equal("queue1", info0.TaskList) s.Equal("wType", info0.WorkflowTypeName) s.Equal(int32(20), info0.WorkflowTimeout) s.Equal(int32(13), info0.DecisionStartToCloseTimeout) s.Equal([]byte(nil), info0.ExecutionContext) s.Equal(p.WorkflowStateRunning, info0.State) s.Equal(p.WorkflowCloseStatusNone, info0.CloseStatus) s.Equal(int64(1), info0.LastFirstEventID) s.Equal(int64(3), info0.NextEventID) s.Equal(int64(0), info0.LastProcessedEvent) s.Equal(true, s.validateTimeRange(info0.LastUpdatedTimestamp, time.Minute)) s.Equal(true, s.validateTimeRange(info0.StartTimestamp, time.Minute)) s.Equal(int64(0), info0.DecisionVersion) s.Equal(int64(2), info0.DecisionScheduleID) s.Equal(constants.EmptyEventID, info0.DecisionStartedID) s.Equal(int32(1), info0.DecisionTimeout) s.Equal(int64(0), info0.DecisionAttempt) s.Equal(int64(0), info0.DecisionStartedTimestamp) s.Equal(int64(0), info0.DecisionScheduledTimestamp) s.Equal(int64(0), info0.DecisionOriginalScheduledTimestamp) s.Empty(info0.StickyTaskList) s.Equal(int32(0), info0.StickyScheduleToStartTimeout) s.Empty(info0.ClientLibraryVersion) s.Empty(info0.ClientFeatureVersion) s.Empty(info0.ClientImpl) s.Equal(int32(0), info0.SignalCount) s.Equal(info0.AutoResetPoints, &types.ResetPoints{}) s.True(len(info0.SearchAttributes) == 0) s.True(len(info0.Memo) == 0) s.Equal(partitionConfig0, info0.PartitionConfig) s.assertChecksumsEqual(testWorkflowChecksum, state0.Checksum) s.T().Logf("Workflow execution last updated: %v\n", info0.LastUpdatedTimestamp) updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := state0.ExecutionStats updatedInfo.LastFirstEventID = int64(3) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) updatedInfo.DecisionVersion = int64(666) updatedInfo.DecisionAttempt = int64(123) updatedInfo.DecisionStartedTimestamp = int64(321) updatedInfo.DecisionScheduledTimestamp = int64(654) updatedInfo.DecisionOriginalScheduledTimestamp = int64(655) updatedInfo.StickyTaskList = "random sticky tasklist" updatedInfo.StickyScheduleToStartTimeout = 876 updatedInfo.ClientLibraryVersion = "random client library version" updatedInfo.ClientFeatureVersion = "random client feature version" updatedInfo.ClientImpl = "random client impl" updatedInfo.SignalCount = 9 updatedInfo.InitialInterval = math.MaxInt32 updatedInfo.BackoffCoefficient = 4.45 updatedInfo.MaximumInterval = math.MaxInt32 updatedInfo.MaximumAttempts = math.MaxInt32 updatedInfo.ExpirationSeconds = math.MaxInt32 updatedInfo.ExpirationTime = time.Now() updatedInfo.NonRetriableErrors = []string{"accessDenied", "badRequest"} searchAttrKey := "env" searchAttrVal := []byte("test") updatedInfo.SearchAttributes = map[string][]byte{searchAttrKey: searchAttrVal} memoKey := "memoKey" memoVal := []byte("memoVal") updatedInfo.Memo = map[string][]byte{memoKey: memoVal} partitionConfig := map[string]string{"zone": "dca2"} updatedInfo.PartitionConfig = partitionConfig updatedStats.HistorySize = math.MaxInt64 versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.NextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, []int64{int64(4)}, nil, int64(3), nil, nil, nil, nil, nil) s.NoError(err2) state1, err3 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err3) info1 := state1.ExecutionInfo s.NotNil(info1, "Valid Workflow info expected.") s.Equal(domainID, info1.DomainID) s.Equal("update-workflow-test", info1.WorkflowID) s.Equal("5ba5e531-e46b-48d9-b4b3-859919839553", info1.RunID) s.Equal("5ba5e531-e46b-48d9-b4b3-859919839553", info1.FirstExecutionRunID) s.Equal("queue1", info1.TaskList) s.Equal("wType", info1.WorkflowTypeName) s.Equal(int32(20), info1.WorkflowTimeout) s.Equal(int32(13), info1.DecisionStartToCloseTimeout) s.Equal([]byte(nil), info1.ExecutionContext) s.Equal(p.WorkflowStateRunning, info1.State) s.Equal(p.WorkflowCloseStatusNone, info1.CloseStatus) s.Equal(int64(3), info1.LastFirstEventID) s.Equal(int64(5), info1.NextEventID) s.Equal(int64(2), info1.LastProcessedEvent) s.Equal(true, s.validateTimeRange(info1.LastUpdatedTimestamp, time.Hour)) s.Equal(info0.StartTimestamp.UnixNano(), info1.StartTimestamp.UnixNano()) s.Equal(int64(666), info1.DecisionVersion) s.Equal(int64(2), info1.DecisionScheduleID) s.Equal(constants.EmptyEventID, info1.DecisionStartedID) s.Equal(int32(1), info1.DecisionTimeout) s.Equal(int64(123), info1.DecisionAttempt) s.Equal(int64(321), info1.DecisionStartedTimestamp) s.Equal(int64(654), info1.DecisionScheduledTimestamp) s.Equal(int64(655), info1.DecisionOriginalScheduledTimestamp) s.Equal(updatedInfo.StickyTaskList, info1.StickyTaskList) s.Equal(updatedInfo.StickyScheduleToStartTimeout, info1.StickyScheduleToStartTimeout) s.Equal(updatedInfo.ClientLibraryVersion, info1.ClientLibraryVersion) s.Equal(updatedInfo.ClientFeatureVersion, info1.ClientFeatureVersion) s.Equal(updatedInfo.ClientImpl, info1.ClientImpl) s.Equal(updatedInfo.SignalCount, info1.SignalCount) s.EqualValues(updatedStats.HistorySize, state1.ExecutionStats.HistorySize) s.Equal(updatedInfo.InitialInterval, info1.InitialInterval) s.Equal(updatedInfo.BackoffCoefficient, info1.BackoffCoefficient) s.Equal(updatedInfo.MaximumInterval, info1.MaximumInterval) s.Equal(updatedInfo.MaximumAttempts, info1.MaximumAttempts) s.Equal(updatedInfo.ExpirationSeconds, info1.ExpirationSeconds) s.EqualTimes(updatedInfo.ExpirationTime, info1.ExpirationTime) s.Equal(updatedInfo.NonRetriableErrors, info1.NonRetriableErrors) searchAttrVal1, ok := info1.SearchAttributes[searchAttrKey] s.True(ok) s.Equal(searchAttrVal, searchAttrVal1) memoVal1, ok := info1.Memo[memoKey] s.True(ok) s.Equal(memoVal, memoVal1) s.Equal(partitionConfig, info1.PartitionConfig) s.assertChecksumsEqual(testWorkflowChecksum, state1.Checksum) s.T().Logf("Workflow execution last updated: %v\n", info1.LastUpdatedTimestamp) failedUpdateInfo := copyWorkflowExecutionInfo(updatedInfo) failedUpdateStats := copyExecutionStats(updatedStats) err4 := s.UpdateWorkflowExecution(ctx, failedUpdateInfo, failedUpdateStats, versionHistories, []int64{int64(5)}, nil, int64(3), nil, nil, nil, nil, nil) s.Error(err4, "expected non nil error.") s.IsType(&p.ConditionFailedError{}, err4) state2, err4 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err4) info2 := state2.ExecutionInfo s.NotNil(info2, "Valid Workflow info expected.") s.Equal(domainID, info2.DomainID) s.Equal("update-workflow-test", info2.WorkflowID) s.Equal("5ba5e531-e46b-48d9-b4b3-859919839553", info2.RunID) s.Equal("5ba5e531-e46b-48d9-b4b3-859919839553", info2.FirstExecutionRunID) s.Equal("queue1", info2.TaskList) s.Equal("wType", info2.WorkflowTypeName) s.Equal(int32(20), info2.WorkflowTimeout) s.Equal(int32(13), info2.DecisionStartToCloseTimeout) s.Equal([]byte(nil), info2.ExecutionContext) s.Equal(p.WorkflowStateRunning, info2.State) s.Equal(p.WorkflowCloseStatusNone, info2.CloseStatus) s.Equal(int64(5), info2.NextEventID) s.Equal(int64(2), info2.LastProcessedEvent) s.Equal(true, s.validateTimeRange(info2.LastUpdatedTimestamp, time.Hour)) s.Equal(int64(666), info2.DecisionVersion) s.Equal(int64(2), info2.DecisionScheduleID) s.Equal(constants.EmptyEventID, info2.DecisionStartedID) s.Equal(int32(1), info2.DecisionTimeout) s.Equal(int64(123), info2.DecisionAttempt) s.Equal(int64(321), info2.DecisionStartedTimestamp) s.Equal(int64(654), info2.DecisionScheduledTimestamp) s.Equal(int64(655), info2.DecisionOriginalScheduledTimestamp) s.Equal(updatedInfo.SignalCount, info2.SignalCount) s.EqualValues(updatedStats.HistorySize, state2.ExecutionStats.HistorySize) s.Equal(updatedInfo.InitialInterval, info2.InitialInterval) s.Equal(updatedInfo.BackoffCoefficient, info2.BackoffCoefficient) s.Equal(updatedInfo.MaximumInterval, info2.MaximumInterval) s.Equal(updatedInfo.MaximumAttempts, info2.MaximumAttempts) s.Equal(updatedInfo.ExpirationSeconds, info2.ExpirationSeconds) s.EqualTimes(updatedInfo.ExpirationTime, info2.ExpirationTime) s.Equal(updatedInfo.NonRetriableErrors, info2.NonRetriableErrors) searchAttrVal2, ok := info2.SearchAttributes[searchAttrKey] s.True(ok) s.Equal(searchAttrVal, searchAttrVal2) memoVal2, ok := info1.Memo[memoKey] s.True(ok) s.Equal(memoVal, memoVal2) s.Equal(partitionConfig, info2.PartitionConfig) s.assertChecksumsEqual(testWorkflowChecksum, state2.Checksum) s.T().Logf("Workflow execution last updated: %v", info2.LastUpdatedTimestamp) err5 := s.UpdateWorkflowExecutionWithRangeID(ctx, failedUpdateInfo, failedUpdateStats, versionHistories, []int64{int64(5)}, nil, int64(12345), int64(5), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) s.Error(err5, "expected non nil error.") s.IsType(&p.ShardOwnershipLostError{}, err5) state3, err6 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err6) info3 := state3.ExecutionInfo s.NotNil(info3, "Valid Workflow info expected.") s.Equal(domainID, info3.DomainID) s.Equal("update-workflow-test", info3.WorkflowID) s.Equal("5ba5e531-e46b-48d9-b4b3-859919839553", info3.RunID) s.Equal("5ba5e531-e46b-48d9-b4b3-859919839553", info3.FirstExecutionRunID) s.Equal("queue1", info3.TaskList) s.Equal("wType", info3.WorkflowTypeName) s.Equal(int32(20), info3.WorkflowTimeout) s.Equal(int32(13), info3.DecisionStartToCloseTimeout) s.Equal([]byte(nil), info3.ExecutionContext) s.Equal(p.WorkflowStateRunning, info3.State) s.Equal(p.WorkflowCloseStatusNone, info3.CloseStatus) s.Equal(int64(5), info3.NextEventID) s.Equal(int64(2), info3.LastProcessedEvent) s.Equal(true, s.validateTimeRange(info3.LastUpdatedTimestamp, time.Hour)) s.Equal(int64(666), info3.DecisionVersion) s.Equal(int64(2), info3.DecisionScheduleID) s.Equal(constants.EmptyEventID, info3.DecisionStartedID) s.Equal(int32(1), info3.DecisionTimeout) s.Equal(int64(123), info3.DecisionAttempt) s.Equal(int64(321), info3.DecisionStartedTimestamp) s.Equal(int64(654), info3.DecisionScheduledTimestamp) s.Equal(int64(655), info3.DecisionOriginalScheduledTimestamp) s.Equal(updatedInfo.SignalCount, info3.SignalCount) s.EqualValues(updatedStats.HistorySize, state3.ExecutionStats.HistorySize) s.Equal(updatedInfo.InitialInterval, info3.InitialInterval) s.Equal(updatedInfo.BackoffCoefficient, info3.BackoffCoefficient) s.Equal(updatedInfo.MaximumInterval, info3.MaximumInterval) s.Equal(updatedInfo.MaximumAttempts, info3.MaximumAttempts) s.Equal(updatedInfo.ExpirationSeconds, info3.ExpirationSeconds) s.EqualTimes(updatedInfo.ExpirationTime, info3.ExpirationTime) s.Equal(updatedInfo.NonRetriableErrors, info3.NonRetriableErrors) searchAttrVal3, ok := info3.SearchAttributes[searchAttrKey] s.True(ok) s.Equal(searchAttrVal, searchAttrVal3) memoVal3, ok := info1.Memo[memoKey] s.True(ok) s.Equal(memoVal, memoVal3) s.Equal(partitionConfig, info3.PartitionConfig) s.assertChecksumsEqual(testWorkflowChecksum, state3.Checksum) s.T().Logf("Workflow execution last updated: %v\n", info3.LastUpdatedTimestamp) // update with incorrect rangeID and condition(next_event_id) err7 := s.UpdateWorkflowExecutionWithRangeID(ctx, failedUpdateInfo, failedUpdateStats, versionHistories, []int64{int64(5)}, nil, int64(12345), int64(3), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) s.Error(err7, "expected non nil error.") s.IsType(&p.ShardOwnershipLostError{}, err7) state4, err8 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err8) info4 := state4.ExecutionInfo s.NotNil(info4, "Valid Workflow info expected.") s.Equal(domainID, info4.DomainID) s.Equal("update-workflow-test", info4.WorkflowID) s.Equal("5ba5e531-e46b-48d9-b4b3-859919839553", info4.RunID) s.Equal("5ba5e531-e46b-48d9-b4b3-859919839553", info4.FirstExecutionRunID) s.Equal("queue1", info4.TaskList) s.Equal("wType", info4.WorkflowTypeName) s.Equal(int32(20), info4.WorkflowTimeout) s.Equal(int32(13), info4.DecisionStartToCloseTimeout) s.Equal([]byte(nil), info4.ExecutionContext) s.Equal(p.WorkflowStateRunning, info4.State) s.Equal(p.WorkflowCloseStatusNone, info4.CloseStatus) s.Equal(int64(5), info4.NextEventID) s.Equal(int64(2), info4.LastProcessedEvent) s.Equal(true, s.validateTimeRange(info4.LastUpdatedTimestamp, time.Hour)) s.Equal(int64(666), info4.DecisionVersion) s.Equal(int64(2), info4.DecisionScheduleID) s.Equal(constants.EmptyEventID, info4.DecisionStartedID) s.Equal(int32(1), info4.DecisionTimeout) s.Equal(int64(123), info4.DecisionAttempt) s.Equal(int64(321), info4.DecisionStartedTimestamp) s.Equal(updatedInfo.SignalCount, info4.SignalCount) s.EqualValues(updatedStats.HistorySize, state4.ExecutionStats.HistorySize) s.Equal(updatedInfo.InitialInterval, info4.InitialInterval) s.Equal(updatedInfo.BackoffCoefficient, info4.BackoffCoefficient) s.Equal(updatedInfo.MaximumInterval, info4.MaximumInterval) s.Equal(updatedInfo.MaximumAttempts, info4.MaximumAttempts) s.Equal(updatedInfo.ExpirationSeconds, info4.ExpirationSeconds) s.EqualTimes(updatedInfo.ExpirationTime, info4.ExpirationTime) s.Equal(updatedInfo.NonRetriableErrors, info4.NonRetriableErrors) searchAttrVal4, ok := info4.SearchAttributes[searchAttrKey] s.True(ok) s.Equal(searchAttrVal, searchAttrVal4) memoVal4, ok := info1.Memo[memoKey] s.True(ok) s.Equal(memoVal, memoVal4) s.Equal(partitionConfig, info4.PartitionConfig) s.assertChecksumsEqual(testWorkflowChecksum, state4.Checksum) s.T().Logf("Workflow execution last updated: %v\n", info4.LastUpdatedTimestamp) } // TestDeleteWorkflow test func (s *ExecutionManagerSuite) TestDeleteWorkflow() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "1d4abb23-b87b-457b-96ef-43aba0b9c44f" workflowExecution := types.WorkflowExecution{ WorkflowID: "delete-workflow-test", RunID: "4e0917f2-9361-4a14-b16f-1fafe09b287a", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") s.Equal(domainID, info0.DomainID) s.Equal("delete-workflow-test", info0.WorkflowID) s.Equal("4e0917f2-9361-4a14-b16f-1fafe09b287a", info0.RunID) s.Equal("4e0917f2-9361-4a14-b16f-1fafe09b287a", info0.FirstExecutionRunID) s.Equal("queue1", info0.TaskList) s.Equal("wType", info0.WorkflowTypeName) s.Equal(int32(20), info0.WorkflowTimeout) s.Equal(int32(13), info0.DecisionStartToCloseTimeout) s.Equal([]byte(nil), info0.ExecutionContext) s.Equal(p.WorkflowStateRunning, info0.State) s.Equal(p.WorkflowCloseStatusNone, info0.CloseStatus) s.Equal(int64(3), info0.NextEventID) s.Equal(int64(0), info0.LastProcessedEvent) s.Equal(true, s.validateTimeRange(info0.LastUpdatedTimestamp, time.Hour)) s.Equal(int64(2), info0.DecisionScheduleID) s.Equal(constants.EmptyEventID, info0.DecisionStartedID) s.Equal(int32(1), info0.DecisionTimeout) s.T().Logf("Workflow execution last updated: %v\n", info0.LastUpdatedTimestamp) err4 := s.DeleteWorkflowExecution(ctx, info0) s.NoError(err4) _, err3 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Error(err3, "expected non nil error.") s.IsType(&types.EntityNotExistsError{}, err3) err5 := s.DeleteWorkflowExecution(ctx, info0) s.NoError(err5) } // TestDeleteCurrentWorkflow test func (s *ExecutionManagerSuite) TestDeleteCurrentWorkflow() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() if s.ExecutionManager.GetName() != "cassandra" { // "this test is only applicable for cassandra (uses TTL based deletes)" return } domainID := "54d15308-e20e-4b91-a00f-a518a3892790" workflowExecution := types.WorkflowExecution{ WorkflowID: "delete-current-workflow-test", RunID: "6cae4054-6ba7-46d3-8755-e3c2db6f74ea", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") runID0, err1 := s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.NoError(err1) s.Equal(workflowExecution.GetRunID(), runID0) info0, err2 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) updatedInfo1 := copyWorkflowExecutionInfo(info0.ExecutionInfo) updatedStats1 := copyExecutionStats(info0.ExecutionStats) updatedInfo1.NextEventID = int64(6) updatedInfo1.LastProcessedEvent = int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo1.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err3 := s.UpdateWorkflowExecutionAndFinish(ctx, updatedInfo1, updatedStats1, int64(3), versionHistories) s.NoError(err3) runID4, err4 := s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.NoError(err4) s.Equal(workflowExecution.GetRunID(), runID4) fakeInfo := &p.WorkflowExecutionInfo{ DomainID: info0.ExecutionInfo.DomainID, WorkflowID: info0.ExecutionInfo.WorkflowID, RunID: uuid.New(), } // test wrong run id with conditional delete s.NoError(s.DeleteCurrentWorkflowExecution(ctx, fakeInfo)) runID5, err5 := s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.NoError(err5) s.Equal(workflowExecution.GetRunID(), runID5) // simulate a timer_task deleting execution after retention s.NoError(s.DeleteCurrentWorkflowExecution(ctx, info0.ExecutionInfo)) runID0, err1 = s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.Error(err1) s.Empty(runID0) _, ok := err1.(*types.EntityNotExistsError) s.True(ok) // execution record should still be there _, err2 = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) } // TestUpdateDeleteWorkflow mocks the timer behavior to clean up workflow. func (s *ExecutionManagerSuite) TestUpdateDeleteWorkflow() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() finishedCurrentExecutionRetentionTTL := int32(2) domainID := "54d15308-e20e-4b91-a00f-a518a3892790" workflowExecution := types.WorkflowExecution{ WorkflowID: "update-delete-workflow-test", RunID: "6cae4054-6ba7-46d3-8755-e3c2db6f74ea", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") runID0, err1 := s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.NoError(err1) s.Equal(workflowExecution.GetRunID(), runID0) info0, err2 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) updatedInfo1 := copyWorkflowExecutionInfo(info0.ExecutionInfo) updatedStats1 := copyExecutionStats(info0.ExecutionStats) updatedInfo1.NextEventID = int64(6) updatedInfo1.LastProcessedEvent = int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo1.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err3 := s.UpdateWorkflowExecutionAndFinish(ctx, updatedInfo1, updatedStats1, int64(3), versionHistories) s.NoError(err3) runID4, err4 := s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.NoError(err4) s.Equal(workflowExecution.GetRunID(), runID4) // simulate a timer_task deleting execution after retention err5 := s.DeleteCurrentWorkflowExecution(ctx, info0.ExecutionInfo) s.NoError(err5) err6 := s.DeleteWorkflowExecution(ctx, info0.ExecutionInfo) s.NoError(err6) time.Sleep(time.Duration(finishedCurrentExecutionRetentionTTL*2) * time.Second) runID0, err1 = s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.Error(err1) s.Empty(runID0) _, ok := err1.(*types.EntityNotExistsError) s.True(ok) // execution record should still be there _, err2 = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Error(err2) _, ok = err2.(*types.EntityNotExistsError) s.True(ok) } // TestCleanupCorruptedWorkflow test func (s *ExecutionManagerSuite) TestCleanupCorruptedWorkflow() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "54d15308-e20e-4b91-a00f-a518a3892790" workflowExecution := types.WorkflowExecution{ WorkflowID: "cleanup-corrupted-workflow-test", RunID: "6cae4054-6ba7-46d3-8755-e3c2db6f74ea", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") runID0, err1 := s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.NoError(err1) s.Equal(workflowExecution.GetRunID(), runID0) info0, err2 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) // deleting current record and verify err3 := s.DeleteCurrentWorkflowExecution(ctx, info0.ExecutionInfo) s.NoError(err3) runID0, err4 := s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.Error(err4) s.Empty(runID0) _, ok := err4.(*types.EntityNotExistsError) s.True(ok) // we should still be able to load with runID info1, err5 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err5) s.Equal(info0, info1) // mark it as corrupted info0.ExecutionInfo.State = p.WorkflowStateCorrupted _, err6 := s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: info0.ExecutionInfo, ExecutionStats: info0.ExecutionStats, Condition: info0.ExecutionInfo.NextEventID, Checksum: testWorkflowChecksum, VersionHistories: info0.VersionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: p.UpdateWorkflowModeBypassCurrent, }) s.NoError(err6) // we should still be able to load with runID info2, err7 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err7) s.Equal(p.WorkflowStateCorrupted, info2.ExecutionInfo.State) info2.ExecutionInfo.State = info1.ExecutionInfo.State info2.ExecutionInfo.LastUpdatedTimestamp = info1.ExecutionInfo.LastUpdatedTimestamp s.Equal(info2, info1) // delete the run err8 := s.DeleteWorkflowExecution(ctx, info0.ExecutionInfo) s.NoError(err8) // execution record should be gone _, err9 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.Error(err9) _, ok = err9.(*types.EntityNotExistsError) s.True(ok) } // TestGetCurrentWorkflow test func (s *ExecutionManagerSuite) TestGetCurrentWorkflow() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "54d15308-e20e-4b91-a00f-a518a3892790" workflowExecution := types.WorkflowExecution{ WorkflowID: "get-current-workflow-test", RunID: "6cae4054-6ba7-46d3-8755-e3c2db6f74ea", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") response, err := s.ExecutionManager.GetCurrentExecution(ctx, &p.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), }) s.NoError(err) s.Equal(workflowExecution.GetRunID(), response.RunID) s.Equal(constants.EmptyVersion, response.LastWriteVersion) info0, err2 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) updatedInfo1 := copyWorkflowExecutionInfo(info0.ExecutionInfo) updatedStats1 := copyExecutionStats(info0.ExecutionStats) updatedInfo1.NextEventID = int64(6) updatedInfo1.LastProcessedEvent = int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo1.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err3 := s.UpdateWorkflowExecutionAndFinish(ctx, updatedInfo1, updatedStats1, int64(3), versionHistories) s.NoError(err3) runID4, err4 := s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.NoError(err4) s.Equal(workflowExecution.GetRunID(), runID4) workflowExecution2 := types.WorkflowExecution{ WorkflowID: "get-current-workflow-test", RunID: "c3ff4bc6-de18-4643-83b2-037a33f45322", } task1, err5 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution2, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.Error(err5, "Error expected.") s.Empty(task1, "Expected empty task identifier.") } // TestTransferTasksThroughUpdate test func (s *ExecutionManagerSuite) TestTransferTasksThroughUpdate() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "b785a8ba-bd7d-4760-bb05-41b115f3e10a" workflowExecution := types.WorkflowExecution{ WorkflowID: "get-transfer-tasks-through-update-test", RunID: "30a9fa1f-0db1-4d7a-8c34-aa82c5dad3aa", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") tasks1, err1 := s.GetTransferTasks(ctx, 1, false) s.NoError(err1) s.NotNil(tasks1, "expected valid list of tasks.") s.Equal(1, len(tasks1), "Expected 1 transfer task.") task1, ok := tasks1[0].(*p.DecisionTask) s.True(ok, "Expected a decision task.") s.Equal(domainID, task1.GetDomainID()) s.Equal(workflowExecution.GetWorkflowID(), task1.GetWorkflowID()) s.Equal(workflowExecution.GetRunID(), task1.GetRunID()) s.Equal("queue1", task1.TaskList) s.Equal(p.TransferTaskTypeDecisionTask, task1.GetTaskType()) s.Equal(int64(2), task1.ScheduleID) err3 := s.CompleteTransferTask(ctx, task1.TaskID) s.NoError(err3) state0, err11 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err11) info0 := state0.ExecutionInfo updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats0 := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats0, versionHistories, nil, []int64{int64(4)}, int64(3), nil, nil, nil, nil, nil) s.NoError(err2) tasks2, err1 := s.GetTransferTasks(ctx, 1, false) s.NoError(err1) s.NotNil(tasks2, "expected valid list of tasks.") s.Equal(1, len(tasks2), "Expected 1 transfer task.") task2, ok := tasks2[0].(*p.ActivityTask) s.True(ok, "Expected an activity task.") s.Equal(domainID, task2.GetDomainID()) s.Equal(workflowExecution.GetWorkflowID(), task2.GetWorkflowID()) s.Equal(workflowExecution.GetRunID(), task2.GetRunID()) s.Equal("queue1", task2.TaskList) s.Equal(p.TransferTaskTypeActivityTask, task2.GetTaskType()) s.Equal(int64(4), task2.ScheduleID) err4 := s.CompleteTransferTask(ctx, task2.TaskID) s.NoError(err4) state1, _ := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) info1 := state1.ExecutionInfo updatedInfo1 := copyWorkflowExecutionInfo(info1) updatedStats1 := copyExecutionStats(state1.ExecutionStats) updatedInfo1.NextEventID = int64(6) updatedInfo1.LastProcessedEvent = int64(2) err5 := s.UpdateWorkflowExecutionAndFinish(ctx, updatedInfo1, updatedStats1, int64(5), versionHistories) s.NoError(err5) newExecution := types.WorkflowExecution{ WorkflowID: workflowExecution.GetWorkflowID(), RunID: "2a038c8f-b575-4151-8d2c-d443e999ab5a", } runID6, err6 := s.GetCurrentWorkflowRunID(ctx, domainID, newExecution.GetWorkflowID()) s.NoError(err6) s.Equal(workflowExecution.GetRunID(), runID6) tasks3, err7 := s.GetTransferTasks(ctx, 1, false) s.NoError(err7) s.NotNil(tasks3, "expected valid list of tasks.") s.Equal(1, len(tasks3), "Expected 1 transfer task.") task3, ok := tasks3[0].(*p.CloseExecutionTask) s.True(ok, "Expected a close execution task.") s.Equal(domainID, task3.GetDomainID()) s.Equal(workflowExecution.GetWorkflowID(), task3.GetWorkflowID()) s.Equal(workflowExecution.GetRunID(), task3.GetRunID()) s.Equal(p.TransferTaskTypeCloseExecution, task3.GetTaskType()) err8 := s.CompleteTransferTask(ctx, task3.TaskID) s.NoError(err8) _, err9 := s.CreateWorkflowExecution(ctx, domainID, newExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.Error(err9, "createWFExecution (brand_new) must fail when there is a previous instance of workflow state already in DB") err10 := s.DeleteWorkflowExecution(ctx, info1) s.NoError(err10) } // TestCancelTransferTaskTasks test func (s *ExecutionManagerSuite) TestCancelTransferTaskTasks() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "aeac8287-527b-4b35-80a9-667cb47e7c6d" workflowExecution := types.WorkflowExecution{ WorkflowID: "cancel-workflow-test", RunID: "db20f7e2-1a1e-40d9-9278-d8b886738e05", } task0, err := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err) s.NotNil(task0, "Expected non empty task identifier.") taskD, err := s.GetTransferTasks(ctx, 1, false) s.NoError(err) s.Equal(1, len(taskD), "Expected 1 decision task.") err = s.CompleteTransferTask(ctx, taskD[0].GetTaskID()) s.NoError(err) state1, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err) info1 := state1.ExecutionInfo s.NotNil(info1, "Valid Workflow info expected.") updatedInfo1 := copyWorkflowExecutionInfo(info1) updatedStats1 := copyExecutionStats(state1.ExecutionStats) targetDomainID := "f2bfaab6-7e8b-4fac-9a62-17da8d37becb" targetWorkflowID := "target-workflow-cancellation-id-1" targetRunID := "0d00698f-08e1-4d36-a3e2-3bf109f5d2d6" targetChildWorkflowOnly := false transferTasks := []p.Task{&p.CancelExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: targetChildWorkflowOnly, InitiatedID: 1, TaskList: "queue1", }} versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: 1, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err = s.UpdateWorkflowExecutionWithTransferTasks(ctx, updatedInfo1, updatedStats1, int64(3), transferTasks, nil, versionHistories) s.NoError(err) tasks1, err := s.GetTransferTasks(ctx, 1, false) s.NoError(err) s.NotNil(tasks1, "expected valid list of tasks.") s.Equal(1, len(tasks1), "Expected 1 cancel task.") task1, ok := tasks1[0].(*p.CancelExecutionTask) s.True(ok, "Expected a cancel execution task.") s.Equal(p.TransferTaskTypeCancelExecution, task1.GetTaskType()) s.Equal(domainID, task1.GetDomainID()) s.Equal(workflowExecution.GetWorkflowID(), task1.GetWorkflowID()) s.Equal(workflowExecution.GetRunID(), task1.GetRunID()) s.Equal(targetDomainID, task1.TargetDomainID) s.Equal(targetWorkflowID, task1.TargetWorkflowID) s.Equal(targetRunID, task1.TargetRunID) s.Equal(false, task1.TargetChildWorkflowOnly) s.Equal("queue1", task1.GetTaskList()) s.Equal("queue1", task1.GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, task1.GetOriginalTaskListKind()) err = s.CompleteTransferTask(ctx, task1.TaskID) s.NoError(err) targetDomainID = "f2bfaab6-7e8b-4fac-9a62-17da8d37becb" targetWorkflowID = "target-workflow-cancellation-id-2" targetRunID = "" targetChildWorkflowOnly = true transferTasks = []p.Task{&p.CancelExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: targetChildWorkflowOnly, InitiatedID: 3, TaskList: "queue2", }} state2, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err) info2 := state2.ExecutionInfo s.NotNil(info2, "Valid Workflow info expected.") updatedInfo2 := copyWorkflowExecutionInfo(info2) updatedStats2 := copyExecutionStats(state2.ExecutionStats) err = s.UpdateWorkflowExecutionWithTransferTasks(ctx, updatedInfo2, updatedStats2, int64(3), transferTasks, nil, versionHistories) s.NoError(err) tasks2, err := s.GetTransferTasks(ctx, 1, false) s.NoError(err) s.NotNil(tasks2, "expected valid list of tasks.") s.Equal(1, len(tasks2), "Expected 1 cancel task.") task2, ok := tasks2[0].(*p.CancelExecutionTask) s.True(ok, "Expected a cancel execution task.") s.Equal(p.TransferTaskTypeCancelExecution, task2.GetTaskType()) s.Equal(domainID, task2.GetDomainID()) s.Equal(workflowExecution.GetWorkflowID(), task2.GetWorkflowID()) s.Equal(workflowExecution.GetRunID(), task2.GetRunID()) s.Equal(targetDomainID, task2.TargetDomainID) s.Equal(targetWorkflowID, task2.TargetWorkflowID) s.Equal(targetRunID, task2.TargetRunID) s.Equal(targetChildWorkflowOnly, task2.TargetChildWorkflowOnly) s.Equal("queue2", task2.GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, task2.GetOriginalTaskListKind()) err = s.CompleteTransferTask(ctx, task2.TaskID) s.NoError(err) } // TestSignalTransferTaskTasks test func (s *ExecutionManagerSuite) TestSignalTransferTaskTasks() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "aeac8287-527b-4b35-80a9-667cb47e7c6d" workflowExecution := types.WorkflowExecution{ WorkflowID: "signal-workflow-test", RunID: "db20f7e2-1a1e-40d9-9278-d8b886738e05", } task0, err := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err) s.NotNil(task0, "Expected non empty task identifier.") taskD, err := s.GetTransferTasks(ctx, 1, false) s.NoError(err) s.Equal(1, len(taskD), "Expected 1 decision task.") err = s.CompleteTransferTask(ctx, taskD[0].GetTaskID()) s.NoError(err) state1, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err) info1 := state1.ExecutionInfo s.NotNil(info1, "Valid Workflow info expected.") updatedInfo1 := copyWorkflowExecutionInfo(info1) updatedStats1 := copyExecutionStats(state1.ExecutionStats) targetDomainID := "f2bfaab6-7e8b-4fac-9a62-17da8d37becb" targetWorkflowID := "target-workflow-signal-id-1" targetRunID := "0d00698f-08e1-4d36-a3e2-3bf109f5d2d6" targetChildWorkflowOnly := false transferTasks := []p.Task{&p.SignalExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: false, InitiatedID: 1, TaskList: "queue1", }} versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: 1, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err = s.UpdateWorkflowExecutionWithTransferTasks(ctx, updatedInfo1, updatedStats1, int64(3), transferTasks, nil, versionHistories) s.NoError(err) tasks1, err := s.GetTransferTasks(ctx, 1, false) s.NoError(err) s.NotNil(tasks1, "expected valid list of tasks.") s.Equal(1, len(tasks1), "Expected 1 transfer task.") task1, ok := tasks1[0].(*p.SignalExecutionTask) s.True(ok, "Expected a signal execution task.") s.Equal(p.TransferTaskTypeSignalExecution, task1.GetTaskType()) s.Equal(domainID, task1.GetDomainID()) s.Equal(workflowExecution.GetWorkflowID(), task1.GetWorkflowID()) s.Equal(workflowExecution.GetRunID(), task1.GetRunID()) s.Equal(targetDomainID, task1.TargetDomainID) s.Equal(targetWorkflowID, task1.TargetWorkflowID) s.Equal(targetRunID, task1.TargetRunID) s.Equal(false, task1.TargetChildWorkflowOnly) s.Equal("queue1", task1.GetTaskList()) s.Equal("queue1", task1.GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, task1.GetOriginalTaskListKind()) err = s.CompleteTransferTask(ctx, task1.TaskID) s.NoError(err) targetDomainID = "f2bfaab6-7e8b-4fac-9a62-17da8d37becb" targetWorkflowID = "target-workflow-signal-id-2" targetRunID = "" targetChildWorkflowOnly = true transferTasks = []p.Task{&p.SignalExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: targetChildWorkflowOnly, InitiatedID: 3, TaskList: "queue2", }} state2, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err) info2 := state2.ExecutionInfo s.NotNil(info2, "Valid Workflow info expected.") updatedInfo2 := copyWorkflowExecutionInfo(info2) updatedStats2 := copyExecutionStats(state2.ExecutionStats) err = s.UpdateWorkflowExecutionWithTransferTasks(ctx, updatedInfo2, updatedStats2, int64(3), transferTasks, nil, versionHistories) s.NoError(err) tasks2, err := s.GetTransferTasks(ctx, 1, false) s.NoError(err) s.NotNil(tasks2, "expected valid list of tasks.") s.Equal(1, len(tasks2), "Expected 1 transfer task.") task2, ok := tasks2[0].(*p.SignalExecutionTask) s.True(ok, "Expected a signal execution task.") s.Equal(p.TransferTaskTypeSignalExecution, task2.GetTaskType()) s.Equal(domainID, task2.GetDomainID()) s.Equal(workflowExecution.GetWorkflowID(), task2.GetWorkflowID()) s.Equal(workflowExecution.GetRunID(), task2.GetRunID()) s.Equal(targetDomainID, task2.TargetDomainID) s.Equal(targetWorkflowID, task2.TargetWorkflowID) s.Equal(targetRunID, task2.TargetRunID) s.Equal(targetChildWorkflowOnly, task2.TargetChildWorkflowOnly) s.Equal("queue2", task2.GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, task2.GetOriginalTaskListKind()) err = s.CompleteTransferTask(ctx, task2.TaskID) s.NoError(err) } // TestReplicationTasks test func (s *ExecutionManagerSuite) TestReplicationTasks() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "2466d7de-6602-4ad8-b939-fb8f8c36c711" workflowExecution := types.WorkflowExecution{ WorkflowID: "get-replication-tasks-test", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } task0, err := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err) s.NotNil(task0, "Expected non empty task identifier.") taskD, err := s.GetTransferTasks(ctx, 1, false) s.NoError(err) s.Equal(1, len(taskD), "Expected 1 decision task.") err = s.CompleteTransferTask(ctx, taskD[0].GetTaskID()) s.NoError(err) state1, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err) info1 := state1.ExecutionInfo s.NotNil(info1, "Valid Workflow info expected.") updatedInfo1 := copyWorkflowExecutionInfo(info1) updatedStats1 := copyExecutionStats(state1.ExecutionStats) replicationTasks := []p.Task{ &p.HistoryReplicationTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), Version: 123, }, FirstEventID: int64(1), NextEventID: int64(3), }, &p.HistoryReplicationTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), Version: 456, }, FirstEventID: int64(1), NextEventID: int64(3), }, &p.SyncActivityTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), Version: 789, }, ScheduledID: 99, }, } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: 3, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err = s.UpdateWorklowStateAndReplication(ctx, updatedInfo1, updatedStats1, versionHistories, int64(3), replicationTasks) s.NoError(err) respTasks, err := s.GetReplicationTasks(ctx, 1, true) s.NoError(err) s.Equal(len(replicationTasks), len(respTasks)) for index := range replicationTasks { respTasks[index].SetVisibilityTimestamp(replicationTasks[index].GetVisibilityTimestamp()) // ignore visibility timestamp s.Equal(replicationTasks[index], respTasks[index]) err = s.CompleteReplicationTask(ctx, respTasks[index].GetTaskID()) s.NoError(err) } } // TestTransferTasksComplete test func (s *ExecutionManagerSuite) TestTransferTasksComplete() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "8bfb47be-5b57-4d55-9109-5fb35e20b1d7" workflowExecution := types.WorkflowExecution{ WorkflowID: "get-transfer-tasks-test-complete", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } tasklist := "some random tasklist" task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, tasklist, "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") tasks1, err1 := s.GetTransferTasks(ctx, 1, false) s.NoError(err1) s.NotNil(tasks1, "expected valid list of tasks.") s.Equal(1, len(tasks1), "Expected 1 decision task.") task1, ok := tasks1[0].(*p.DecisionTask) s.True(ok, "Expected a decision task.") s.Equal(domainID, task1.DomainID) s.Equal(workflowExecution.GetWorkflowID(), task1.WorkflowID) s.Equal(workflowExecution.GetRunID(), task1.RunID) s.Equal(tasklist, task1.TaskList) s.Equal(p.TransferTaskTypeDecisionTask, task1.GetTaskType()) s.Equal(int64(2), task1.ScheduleID) err3 := s.CompleteTransferTask(ctx, task1.TaskID) s.NoError(err3) state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(6) updatedInfo.LastProcessedEvent = int64(2) scheduleID := int64(123) targetDomainID := "8bfb47be-5b57-4d66-9109-5fb35e20b1d0" targetWorkflowID := "some random target domain ID" targetRunID := uuid.New() currentTransferID := task1.TaskID now := time.Now() tasks := []p.Task{ &p.ActivityTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10000, Version: 111, }, TargetDomainID: domainID, TaskList: tasklist, ScheduleID: scheduleID, }, &p.DecisionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10002, Version: 222, }, TargetDomainID: domainID, TaskList: tasklist, ScheduleID: scheduleID, OriginalTaskList: "original_tasklist", OriginalTaskListKind: types.TaskListKindEphemeral, }, &p.CloseExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskList: tasklist, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10003, Version: 333, }}, &p.CancelExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10004, Version: 444, }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: true, InitiatedID: scheduleID, TaskList: tasklist, }, &p.SignalExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10005, Version: 555, }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: true, InitiatedID: scheduleID, TaskList: tasklist, }, &p.StartChildExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10006, Version: 666, }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, InitiatedID: scheduleID, TaskList: tasklist, }, &p.RecordWorkflowClosedTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10007, Version: 777, }, TaskList: tasklist, }, &p.RecordChildExecutionCompletedTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10008, Version: 888, }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TaskList: tasklist, }, } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: scheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpdateWorklowStateAndReplication(ctx, updatedInfo, updatedStats, versionHistories, int64(3), tasks) s.NoError(err2) txTasks, err1 := s.GetTransferTasks(ctx, 1, true) // use page size one to force pagination s.NoError(err1) s.NotNil(txTasks, "expected valid list of tasks.") s.Equal(len(tasks), len(txTasks)) for index := range tasks { s.True(timeComparator(tasks[index].GetVisibilityTimestamp(), txTasks[index].GetVisibilityTimestamp(), TimePrecision)) } s.Equal(p.TransferTaskTypeActivityTask, txTasks[0].GetTaskType()) s.Equal(tasklist, txTasks[0].GetTaskList()) s.Equal(tasklist, txTasks[0].GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, txTasks[0].GetOriginalTaskListKind()) s.Equal(p.TransferTaskTypeDecisionTask, txTasks[1].GetTaskType()) s.Equal(tasklist, txTasks[1].GetTaskList()) s.Equal("original_tasklist", txTasks[1].GetOriginalTaskList()) s.Equal(types.TaskListKindEphemeral, txTasks[1].GetOriginalTaskListKind()) s.Equal(p.TransferTaskTypeCloseExecution, txTasks[2].GetTaskType()) s.Equal(tasklist, txTasks[2].GetTaskList()) s.Equal(tasklist, txTasks[2].GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, txTasks[2].GetOriginalTaskListKind()) s.Equal(p.TransferTaskTypeCancelExecution, txTasks[3].GetTaskType()) s.Equal(tasklist, txTasks[3].GetTaskList()) s.Equal(tasklist, txTasks[3].GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, txTasks[3].GetOriginalTaskListKind()) s.Equal(p.TransferTaskTypeSignalExecution, txTasks[4].GetTaskType()) s.Equal(tasklist, txTasks[4].GetTaskList()) s.Equal(tasklist, txTasks[4].GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, txTasks[4].GetOriginalTaskListKind()) s.Equal(p.TransferTaskTypeStartChildExecution, txTasks[5].GetTaskType()) s.Equal(tasklist, txTasks[5].GetTaskList()) s.Equal(tasklist, txTasks[5].GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, txTasks[5].GetOriginalTaskListKind()) s.Equal(p.TransferTaskTypeRecordWorkflowClosed, txTasks[6].GetTaskType()) s.Equal(tasklist, txTasks[6].GetTaskList()) s.Equal(tasklist, txTasks[6].GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, txTasks[6].GetOriginalTaskListKind()) s.Equal(p.TransferTaskTypeRecordChildExecutionCompleted, txTasks[7].GetTaskType()) s.Equal(tasklist, txTasks[7].GetTaskList()) s.Equal(tasklist, txTasks[7].GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, txTasks[7].GetOriginalTaskListKind()) for idx := range txTasks { // TODO: add a check similar to validateCrossClusterTasks s.Equal(int64(111*(idx+1)), txTasks[idx].GetVersion()) err := s.CompleteTransferTask(ctx, txTasks[idx].GetTaskID()) s.NoError(err) } txTasks, err2 = s.GetTransferTasks(ctx, 100, false) s.NoError(err2) s.Empty(txTasks, "expected empty task list.") } // TestTransferTasksRangeComplete test func (s *ExecutionManagerSuite) TestTransferTasksRangeComplete() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "8bfb47be-5b57-4d55-9109-5fb35e20b1d7" workflowExecution := types.WorkflowExecution{ WorkflowID: "get-transfer-tasks-test-range-complete", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } tasklist := "some random tasklist" task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, tasklist, "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") tasks1, err1 := s.GetTransferTasks(ctx, 1, false) s.NoError(err1) s.NotNil(tasks1, "expected valid list of tasks.") s.Equal(1, len(tasks1), "Expected 1 decision task.") task1, ok := tasks1[0].(*p.DecisionTask) s.True(ok, "Expected a decision task.") s.Equal(domainID, task1.DomainID) s.Equal(workflowExecution.GetWorkflowID(), task1.WorkflowID) s.Equal(workflowExecution.GetRunID(), task1.RunID) s.Equal(tasklist, task1.TaskList) s.Equal(p.TransferTaskTypeDecisionTask, task1.GetTaskType()) s.Equal(int64(2), task1.ScheduleID) err3 := s.CompleteTransferTask(ctx, task1.TaskID) s.NoError(err3) state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(6) updatedInfo.LastProcessedEvent = int64(2) scheduleID := int64(123) targetDomainID := "8bfb47be-5b57-4d66-9109-5fb35e20b1d0" targetWorkflowID := "some random target domain ID" targetRunID := uuid.New() currentTransferID := task1.TaskID now := time.Now() tasks := []p.Task{ &p.ActivityTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10001, Version: 111, }, TargetDomainID: domainID, TaskList: tasklist, ScheduleID: scheduleID, }, &p.DecisionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10002, Version: 222, }, TargetDomainID: domainID, TaskList: tasklist, ScheduleID: scheduleID, OriginalTaskList: "original_tasklist", OriginalTaskListKind: types.TaskListKindEphemeral, }, &p.CloseExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10003, Version: 333, }, TaskList: tasklist, }, &p.CancelExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10004, Version: 444, }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: true, InitiatedID: scheduleID, TaskList: tasklist, }, &p.SignalExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10005, Version: 555, }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: true, InitiatedID: scheduleID, TaskList: tasklist, }, &p.StartChildExecutionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: currentTransferID + 10006, Version: 666, }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, InitiatedID: scheduleID, TaskList: tasklist, }, } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: scheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpdateWorklowStateAndReplication(ctx, updatedInfo, updatedStats, versionHistories, int64(3), tasks) s.NoError(err2) txTasks, err1 := s.GetTransferTasks(ctx, 2, true) // use page size one to force pagination s.NoError(err1) s.NotNil(txTasks, "expected valid list of tasks.") s.Equal(len(tasks), len(txTasks)) for index := range tasks { s.True(timeComparator(tasks[index].GetVisibilityTimestamp(), txTasks[index].GetVisibilityTimestamp(), TimePrecision)) } s.Equal(p.TransferTaskTypeActivityTask, txTasks[0].GetTaskType()) s.Equal(p.TransferTaskTypeDecisionTask, txTasks[1].GetTaskType()) s.Equal(p.TransferTaskTypeCloseExecution, txTasks[2].GetTaskType()) s.Equal(p.TransferTaskTypeCancelExecution, txTasks[3].GetTaskType()) s.Equal(p.TransferTaskTypeSignalExecution, txTasks[4].GetTaskType()) s.Equal(p.TransferTaskTypeStartChildExecution, txTasks[5].GetTaskType()) s.Equal(int64(111), txTasks[0].GetVersion()) s.Equal(int64(222), txTasks[1].GetVersion()) s.Equal(int64(333), txTasks[2].GetVersion()) s.Equal(int64(444), txTasks[3].GetVersion()) s.Equal(int64(555), txTasks[4].GetVersion()) s.Equal(int64(666), txTasks[5].GetVersion()) s.Equal(currentTransferID+10001, txTasks[0].GetTaskID()) s.Equal(currentTransferID+10002, txTasks[1].GetTaskID()) s.Equal(currentTransferID+10003, txTasks[2].GetTaskID()) s.Equal(currentTransferID+10004, txTasks[3].GetTaskID()) s.Equal(currentTransferID+10005, txTasks[4].GetTaskID()) s.Equal(currentTransferID+10006, txTasks[5].GetTaskID()) s.Equal(tasklist, txTasks[0].GetTaskList()) s.Equal(tasklist, txTasks[1].GetTaskList()) s.Equal(tasklist, txTasks[2].GetTaskList()) s.Equal(tasklist, txTasks[3].GetTaskList()) s.Equal(tasklist, txTasks[4].GetTaskList()) s.Equal(tasklist, txTasks[5].GetTaskList()) s.Equal(tasklist, txTasks[0].GetOriginalTaskList()) s.Equal("original_tasklist", txTasks[1].GetOriginalTaskList()) s.Equal(tasklist, txTasks[2].GetOriginalTaskList()) s.Equal(tasklist, txTasks[3].GetOriginalTaskList()) s.Equal(tasklist, txTasks[4].GetOriginalTaskList()) s.Equal(tasklist, txTasks[5].GetOriginalTaskList()) s.Equal(types.TaskListKindNormal, txTasks[0].GetOriginalTaskListKind()) s.Equal(types.TaskListKindEphemeral, txTasks[1].GetOriginalTaskListKind()) s.Equal(types.TaskListKindNormal, txTasks[2].GetOriginalTaskListKind()) s.Equal(types.TaskListKindNormal, txTasks[3].GetOriginalTaskListKind()) s.Equal(types.TaskListKindNormal, txTasks[4].GetOriginalTaskListKind()) s.Equal(types.TaskListKindNormal, txTasks[5].GetOriginalTaskListKind()) err2 = s.RangeCompleteTransferTask(ctx, txTasks[0].GetTaskID(), txTasks[5].GetTaskID()+1) s.NoError(err2) txTasks, err2 = s.GetTransferTasks(ctx, 100, false) s.NoError(err2) s.Empty(txTasks, "expected empty task list.") } // TestTimerTasksComplete test func (s *ExecutionManagerSuite) TestTimerTasksComplete() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "8bfb47be-5b57-4d66-9109-5fb35e20b1d7" workflowExecution := types.WorkflowExecution{ WorkflowID: "get-timer-tasks-test-complete", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } now := time.Now() initialTasks := []p.Task{&p.DecisionTimeoutTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now.Add(1 * time.Second), TaskID: 1, Version: 11, }, EventID: 2, ScheduleAttempt: 3, TimeoutType: int(types.TimeoutTypeStartToClose), TaskList: "taskList", }, } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "taskList", "wType", 20, 13, nil, 3, 0, 2, initialTasks, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) tasks := []p.Task{ &p.WorkflowTimeoutTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ Version: 12, TaskID: 2, VisibilityTimestamp: now.Add(2 * time.Second), }, TaskList: "taskList", }, &p.DeleteHistoryEventTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ Version: 13, TaskID: 3, VisibilityTimestamp: now.Add(2 * time.Second), }, TaskList: "taskList", }, &p.ActivityTimeoutTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ Version: 14, TaskID: 4, VisibilityTimestamp: now.Add(3 * time.Second), }, TimeoutType: int(types.TimeoutTypeStartToClose), EventID: 7, Attempt: 0, TaskList: "taskList", }, &p.UserTimerTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ Version: 15, TaskID: 5, VisibilityTimestamp: now.Add(3 * time.Second), }, EventID: 7, TaskList: "taskList", }, } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.NextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, []int64{int64(4)}, nil, int64(3), tasks, nil, nil, nil, nil) s.NoError(err2) timerTasks, err1 := s.GetTimerIndexTasks(ctx, 1, true) // use page size one to force pagination s.NoError(err1) s.NotNil(timerTasks, "expected valid list of tasks.") s.Equal(len(tasks)+len(initialTasks), len(timerTasks)) s.Equal(p.TaskTypeDecisionTimeout, timerTasks[0].GetTaskType()) s.Equal(p.TaskTypeWorkflowTimeout, timerTasks[1].GetTaskType()) s.Equal(p.TaskTypeDeleteHistoryEvent, timerTasks[2].GetTaskType()) s.Equal(p.TaskTypeActivityTimeout, timerTasks[3].GetTaskType()) s.Equal(p.TaskTypeUserTimer, timerTasks[4].GetTaskType()) s.Equal(int64(11), timerTasks[0].GetVersion()) s.Equal(int64(12), timerTasks[1].GetVersion()) s.Equal(int64(13), timerTasks[2].GetVersion()) s.Equal(int64(14), timerTasks[3].GetVersion()) s.Equal(int64(15), timerTasks[4].GetVersion()) s.Equal("taskList", timerTasks[0].GetTaskList()) s.Equal("taskList", timerTasks[1].GetTaskList()) s.Equal("taskList", timerTasks[2].GetTaskList()) s.Equal("taskList", timerTasks[3].GetTaskList()) s.Equal("taskList", timerTasks[4].GetTaskList()) err2 = s.RangeCompleteTimerTask(ctx, timerTasks[0].GetVisibilityTimestamp(), timerTasks[4].GetVisibilityTimestamp().Add(1*time.Second)) s.NoError(err2) timerTasks2, err2 := s.GetTimerIndexTasks(ctx, 100, false) s.NoError(err2) s.Empty(timerTasks2, "expected empty task list.") } // TestTimerTasksRangeComplete test func (s *ExecutionManagerSuite) TestTimerTasksRangeComplete() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "8bfb47be-5b57-4d66-9109-5fb35e20b1d7" workflowExecution := types.WorkflowExecution{ WorkflowID: "get-timer-tasks-test-range-complete", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "taskList", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.NextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) now := time.Now().UTC() tasks := []p.Task{ &p.DecisionTimeoutTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: 1, Version: 11, }, EventID: 2, ScheduleAttempt: 3, TimeoutType: int(types.TimeoutTypeStartToClose), TaskList: "taskList", }, &p.WorkflowTimeoutTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: 2, Version: 12, }, TaskList: "taskList", }, &p.DeleteHistoryEventTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: 3, Version: 13, }, TaskList: "taskList", }, &p.ActivityTimeoutTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: 4, Version: 14, }, TimeoutType: int(types.TimeoutTypeStartToClose), EventID: 7, Attempt: 0, TaskList: "taskList", }, &p.UserTimerTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ VisibilityTimestamp: now, TaskID: 5, Version: 15, }, EventID: 7, TaskList: "taskList", }, } err2 := s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, []int64{int64(4)}, nil, int64(3), tasks, nil, nil, nil, nil) s.NoError(err2) timerTasks, err1 := s.GetTimerIndexTasks(ctx, 1, true) // use page size one to force pagination s.NoError(err1) s.NotNil(timerTasks, "expected valid list of tasks.") s.Equal(len(tasks), len(timerTasks)) s.Equal(p.TaskTypeDecisionTimeout, timerTasks[0].GetTaskType()) s.Equal(p.TaskTypeWorkflowTimeout, timerTasks[1].GetTaskType()) s.Equal(p.TaskTypeDeleteHistoryEvent, timerTasks[2].GetTaskType()) s.Equal(p.TaskTypeActivityTimeout, timerTasks[3].GetTaskType()) s.Equal(p.TaskTypeUserTimer, timerTasks[4].GetTaskType()) s.Equal(int64(11), timerTasks[0].GetVersion()) s.Equal(int64(12), timerTasks[1].GetVersion()) s.Equal(int64(13), timerTasks[2].GetVersion()) s.Equal(int64(14), timerTasks[3].GetVersion()) s.Equal(int64(15), timerTasks[4].GetVersion()) s.Equal("taskList", timerTasks[0].GetTaskList()) s.Equal("taskList", timerTasks[1].GetTaskList()) s.Equal("taskList", timerTasks[2].GetTaskList()) s.Equal("taskList", timerTasks[3].GetTaskList()) s.Equal("taskList", timerTasks[4].GetTaskList()) err2 = s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, nil, nil, int64(5), nil, nil, nil, nil, nil) s.NoError(err2) err2 = s.CompleteTimerTask(ctx, timerTasks[0].GetVisibilityTimestamp(), timerTasks[0].GetTaskID()) s.NoError(err2) err2 = s.CompleteTimerTask(ctx, timerTasks[1].GetVisibilityTimestamp(), timerTasks[1].GetTaskID()) s.NoError(err2) err2 = s.CompleteTimerTask(ctx, timerTasks[2].GetVisibilityTimestamp(), timerTasks[2].GetTaskID()) s.NoError(err2) err2 = s.CompleteTimerTask(ctx, timerTasks[3].GetVisibilityTimestamp(), timerTasks[3].GetTaskID()) s.NoError(err2) err2 = s.CompleteTimerTask(ctx, timerTasks[4].GetVisibilityTimestamp(), timerTasks[4].GetTaskID()) s.NoError(err2) timerTasks2, err2 := s.GetTimerIndexTasks(ctx, 100, false) s.NoError(err2) s.Empty(timerTasks2, "expected empty task list.") } // TestWorkflowMutableStateActivities test func (s *ExecutionManagerSuite) TestWorkflowMutableStateActivities() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "7fcf0aa9-e121-4292-bdad-0a75181b4aa3" workflowExecution := types.WorkflowExecution{ WorkflowID: "test-workflow-mutable-test", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "taskList", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) activityInfos := []*p.ActivityInfo{{ Version: 7789, ScheduleID: 1, ScheduledEventBatchID: 1, ScheduledEvent: &types.HistoryEvent{ID: 1}, ScheduledTime: time.UnixMilli(1755030646000).UTC(), ActivityID: uuid.New(), RequestID: uuid.New(), Details: []byte(uuid.New()), StartedID: 2, StartedEvent: &types.HistoryEvent{ID: 2}, StartedTime: time.UnixMilli(1755030646001).UTC(), ScheduleToCloseTimeout: 1, ScheduleToStartTimeout: 2, StartToCloseTimeout: 3, HeartbeatTimeout: 4, LastHeartBeatUpdatedTime: time.UnixMilli(1755030646002).UTC(), TimerTaskStatus: 1, CancelRequested: true, CancelRequestID: math.MaxInt64, Attempt: math.MaxInt32, DomainID: domainID, StartedIdentity: uuid.New(), TaskList: uuid.New(), TaskListKind: types.TaskListKindEphemeral, HasRetryPolicy: true, InitialInterval: math.MaxInt32, MaximumInterval: math.MaxInt32, MaximumAttempts: math.MaxInt32, BackoffCoefficient: 5.55, ExpirationTime: time.UnixMilli(1755030646003).UTC(), NonRetriableErrors: []string{"accessDenied", "badRequest"}, LastFailureReason: "some random error", LastWorkerIdentity: uuid.New(), LastFailureDetails: []byte(uuid.New()), }} versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.NextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, []int64{int64(4)}, nil, int64(3), nil, activityInfos, nil, nil, nil) s.NoError(err2) state, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state, "expected valid state.") s.Equal(1, len(state.ActivityInfos)) s.T().Logf("%+v\n", state.ActivityInfos) ai, ok := state.ActivityInfos[1] s.True(ok) s.NotNil(ai) // Various persistence layers are inconsistent with assigning the location value, so normalize them first ai.ScheduledTime = ai.ScheduledTime.UTC() ai.StartedTime = ai.StartedTime.UTC() ai.LastHeartBeatUpdatedTime = ai.LastHeartBeatUpdatedTime.UTC() ai.ExpirationTime = ai.ExpirationTime.UTC() s.Equal(activityInfos[0], ai) err2 = s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, nil, nil, int64(5), nil, nil, []int64{1}, nil, nil) s.NoError(err2) state, err2 = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) s.NotNil(state, "expected valid state.") s.Equal(0, len(state.ActivityInfos)) } // TestWorkflowMutableStateTimers test func (s *ExecutionManagerSuite) TestWorkflowMutableStateTimers() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "025d178a-709b-4c07-8dd7-86dbf9bd2e06" workflowExecution := types.WorkflowExecution{ WorkflowID: "test-workflow-mutable-timers-test", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "taskList", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) currentTime := time.Now().UTC() timerID := "id_1" timerInfos := []*p.TimerInfo{{ Version: 3345, TimerID: timerID, ExpiryTime: currentTime, TaskStatus: 2, StartedID: 5, }} versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.NextEventID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, []int64{int64(4)}, nil, int64(3), nil, nil, nil, timerInfos, nil) s.NoError(err2) state, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state, "expected valid state.") s.Equal(1, len(state.TimerInfos)) s.Equal(int64(3345), state.TimerInfos[timerID].Version) s.Equal(timerID, state.TimerInfos[timerID].TimerID) s.EqualTimesWithPrecision(currentTime, state.TimerInfos[timerID].ExpiryTime, time.Millisecond*500) s.Equal(int64(2), state.TimerInfos[timerID].TaskStatus) s.Equal(int64(5), state.TimerInfos[timerID].StartedID) err2 = s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, nil, nil, int64(5), nil, nil, nil, nil, []string{timerID}) s.NoError(err2) state, err2 = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) s.NotNil(state, "expected valid state.") s.Equal(0, len(state.TimerInfos)) } // TestWorkflowMutableStateChildExecutions test func (s *ExecutionManagerSuite) TestWorkflowMutableStateChildExecutions() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "88236cd2-c439-4cec-9957-2748ce3be074" workflowExecution := types.WorkflowExecution{ WorkflowID: "test-workflow-mutable-child-executions-parent-test", RunID: "c63dba1e-929c-4fbf-8ec5-4533b16269a9", } parentDomainID := "6036ded3-e541-42c9-8f69-3d9354dad081" parentExecution := types.WorkflowExecution{ WorkflowID: "test-workflow-mutable-child-executions-child-test", RunID: "73e89362-25ec-4305-bcb8-d9448b90856c", } partitionConfig := map[string]string{ "userID": uuid.New(), } task0, err0 := s.CreateChildWorkflowExecution(ctx, domainID, workflowExecution, parentDomainID, parentExecution, 1, "taskList", "wType", 20, 13, nil, 3, 0, 2, nil, partitionConfig) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") s.Equal(parentDomainID, info0.ParentDomainID) s.Equal(parentExecution.GetWorkflowID(), info0.ParentWorkflowID) s.Equal(parentExecution.GetRunID(), info0.ParentRunID) s.Equal(int64(1), info0.InitiatedID) s.Equal(partitionConfig, info0.PartitionConfig) updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) childExecutionInfos := []*p.ChildExecutionInfo{{ Version: 1234, InitiatedID: 1, InitiatedEvent: &types.HistoryEvent{ID: 1}, StartedID: 2, StartedRunID: uuid.New(), StartedEvent: &types.HistoryEvent{ID: 2}, CreateRequestID: uuid.New(), DomainID: uuid.New(), ParentClosePolicy: types.ParentClosePolicyTerminate, }} versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpsertChildExecutionsState(ctx, updatedInfo, updatedStats, versionHistories, int64(3), childExecutionInfos) s.NoError(err2) state, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state, "expected valid state.") s.Equal(1, len(state.ChildExecutionInfos)) ci, ok := state.ChildExecutionInfos[1] s.True(ok) s.NotNil(ci) s.Equal(childExecutionInfos[0], ci) err2 = s.DeleteChildExecutionsState(ctx, updatedInfo, updatedStats, versionHistories, int64(5), int64(1)) s.NoError(err2) state, err2 = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) s.NotNil(state, "expected valid state.") s.Equal(0, len(state.ChildExecutionInfos)) } // TestWorkflowMutableStateRequestCancel test func (s *ExecutionManagerSuite) TestWorkflowMutableStateRequestCancel() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "568b8d19-cf64-4aac-be1b-f8a3edbc1fa9" workflowExecution := types.WorkflowExecution{ WorkflowID: "test-workflow-mutable-request-cancel-test", RunID: "87f96253-b925-426e-90db-aa4ee89b5aca", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "taskList", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) requestCancelInfo := &p.RequestCancelInfo{ Version: 456, InitiatedID: 2, InitiatedEventBatchID: 1, CancelRequestID: uuid.New(), } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpsertRequestCancelState(ctx, updatedInfo, updatedStats, versionHistories, int64(3), []*p.RequestCancelInfo{requestCancelInfo}) s.NoError(err2) state, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state, "expected valid state.") s.Equal(1, len(state.RequestCancelInfos)) ri, ok := state.RequestCancelInfos[requestCancelInfo.InitiatedID] s.True(ok) s.NotNil(ri) s.Equal(requestCancelInfo, ri) err2 = s.DeleteCancelState(ctx, updatedInfo, updatedStats, versionHistories, int64(5), requestCancelInfo.InitiatedID) s.NoError(err2) state, err2 = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) s.NotNil(state, "expected valid state.") s.Equal(0, len(state.RequestCancelInfos)) } // TestWorkflowMutableStateSignalInfo test func (s *ExecutionManagerSuite) TestWorkflowMutableStateSignalInfo() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() runID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-workflow-mutable-signal-info-test", RunID: runID, } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "taskList", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) signalInfo := &p.SignalInfo{ Version: 123, InitiatedID: 2, InitiatedEventBatchID: 1, SignalRequestID: uuid.New(), SignalName: "my signal", Input: []byte("test signal input"), Control: []byte(uuid.New()), } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpsertSignalInfoState(ctx, updatedInfo, updatedStats, versionHistories, int64(3), []*p.SignalInfo{signalInfo}) s.NoError(err2) state, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state, "expected valid state.") s.Equal(1, len(state.SignalInfos)) si, ok := state.SignalInfos[signalInfo.InitiatedID] s.True(ok) s.NotNil(si) s.Equal(signalInfo, si) err2 = s.DeleteSignalState(ctx, updatedInfo, updatedStats, versionHistories, int64(5), signalInfo.InitiatedID) s.NoError(err2) state, err2 = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) s.NotNil(state, "expected valid state.") s.Equal(0, len(state.SignalInfos)) } // TestWorkflowMutableStateSignalRequested test func (s *ExecutionManagerSuite) TestWorkflowMutableStateSignalRequested() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() runID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-workflow-mutable-signal-requested-test", RunID: runID, } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "taskList", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) signalRequestedID := uuid.New() signalsRequested := []string{signalRequestedID} versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpsertSignalsRequestedState(ctx, updatedInfo, updatedStats, versionHistories, int64(3), signalsRequested) s.NoError(err2) state, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state, "expected valid state.") s.Equal(1, len(state.SignalRequestedIDs)) ri, ok := state.SignalRequestedIDs[signalRequestedID] s.True(ok) s.NotNil(ri) err2 = s.DeleteSignalsRequestedState(ctx, updatedInfo, updatedStats, versionHistories, int64(5), []string{signalRequestedID, uuid.New()}) s.NoError(err2) state, err2 = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) s.NotNil(state, "expected valid state.") s.Equal(0, len(state.SignalRequestedIDs)) } // TestWorkflowMutableStateInfo test func (s *ExecutionManagerSuite) TestWorkflowMutableStateInfo() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "9ed8818b-3090-4160-9f21-c6b70e64d2dd" workflowExecution := types.WorkflowExecution{ WorkflowID: "test-workflow-mutable-state-test", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "taskList", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err2 := s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, []int64{int64(4)}, nil, int64(3), nil, nil, nil, nil, nil) s.NoError(err2) state, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state, "expected valid state.") s.NotNil(state.ExecutionInfo, "expected valid MS Info state.") s.Equal(updatedInfo.NextEventID, state.ExecutionInfo.NextEventID) s.Equal(updatedInfo.State, state.ExecutionInfo.State) } // TestContinueAsNew test func (s *ExecutionManagerSuite) TestContinueAsNew() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "c1c0bb55-04e6-4a9c-89d0-1be7b96459f8" workflowExecution := types.WorkflowExecution{ WorkflowID: "continue-as-new-workflow-test", RunID: "551c88d2-d9e6-404f-8131-9eec14f36643", } partitionConfig := map[string]string{ "userID": uuid.New(), } _, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, partitionConfig) s.NoError(err0) state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo continueAsNewInfo := copyWorkflowExecutionInfo(info0) continueAsNewStats := copyExecutionStats(state0.ExecutionStats) continueAsNewInfo.State = p.WorkflowStateCreated continueAsNewInfo.CloseStatus = p.WorkflowCloseStatusNone continueAsNewInfo.NextEventID = int64(5) continueAsNewInfo.LastProcessedEvent = int64(2) newWorkflowExecution := types.WorkflowExecution{ WorkflowID: "continue-as-new-workflow-test", RunID: "64c7e15a-3fd7-4182-9c6f-6f25a4fa2614", } testResetPoints := types.ResetPoints{ Points: []*types.ResetPointInfo{ { BinaryChecksum: "test-binary-checksum", RunID: "test-runID", FirstDecisionCompletedID: 123, CreatedTimeNano: common.Int64Ptr(456), Resettable: true, ExpiringTimeNano: common.Int64Ptr(789), }, }, } err2 := s.ContinueAsNewExecution(ctx, continueAsNewInfo, continueAsNewStats, info0.NextEventID, newWorkflowExecution, int64(3), int64(2), &testResetPoints) s.NoError(err2) prevExecutionState, err3 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err3) prevExecutionInfo := prevExecutionState.ExecutionInfo s.Equal("551c88d2-d9e6-404f-8131-9eec14f36643", prevExecutionInfo.FirstExecutionRunID) s.Equal(p.WorkflowStateCompleted, prevExecutionInfo.State) s.Equal(p.WorkflowCloseStatusContinuedAsNew, prevExecutionInfo.CloseStatus) s.Equal(int64(5), prevExecutionInfo.NextEventID) s.Equal(int64(2), prevExecutionInfo.LastProcessedEvent) s.Equal(prevExecutionInfo.AutoResetPoints, &types.ResetPoints{}) s.Equal(partitionConfig, prevExecutionInfo.PartitionConfig) newExecutionState, err4 := s.GetWorkflowExecutionInfo(ctx, domainID, newWorkflowExecution) s.NoError(err4) newExecutionInfo := newExecutionState.ExecutionInfo s.Equal("551c88d2-d9e6-404f-8131-9eec14f36643", newExecutionInfo.FirstExecutionRunID) s.Equal(p.WorkflowStateCreated, newExecutionInfo.State) s.Equal(p.WorkflowCloseStatusNone, newExecutionInfo.CloseStatus) s.Equal(int64(3), newExecutionInfo.NextEventID) s.Equal(constants.EmptyEventID, newExecutionInfo.LastProcessedEvent) s.Equal(int64(2), newExecutionInfo.DecisionScheduleID) s.Equal(testResetPoints, *newExecutionInfo.AutoResetPoints) s.Equal(partitionConfig, newExecutionInfo.PartitionConfig) newRunID, err5 := s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.GetWorkflowID()) s.NoError(err5) s.Equal(newWorkflowExecution.GetRunID(), newRunID) } // TestReplicationTransferTaskTasks test func (s *ExecutionManagerSuite) TestReplicationTransferTaskTasks() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "2466d7de-6602-4ad8-b939-fb8f8c36c711" workflowExecution := types.WorkflowExecution{ WorkflowID: "replication-transfer-task-test", RunID: "dcde9d85-5d7a-43c7-8b18-cb2cae0e29e0", } task0, err := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err) s.NotNil(task0, "Expected non empty task identifier.") taskD, err := s.GetTransferTasks(ctx, 1, false) s.NoError(err) s.Equal(1, len(taskD), "Expected 1 decision task.") err = s.CompleteTransferTask(ctx, taskD[0].GetTaskID()) s.NoError(err) state1, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err) info1 := state1.ExecutionInfo s.NotNil(info1, "Valid Workflow info expected.") updatedInfo1 := copyWorkflowExecutionInfo(info1) updatedStats1 := copyExecutionStats(state1.ExecutionStats) replicationTasks := []p.Task{&p.HistoryReplicationTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), Version: 9, }, FirstEventID: 1, NextEventID: 3, }} versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: 3, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err = s.UpdateWorklowStateAndReplication(ctx, updatedInfo1, updatedStats1, versionHistories, int64(3), replicationTasks) s.NoError(err) tasks1, err := s.GetReplicationTasks(ctx, 1, false) s.NoError(err) s.NotNil(tasks1, "expected valid list of tasks.") s.Equal(1, len(tasks1), "Expected 1 replication task.") task1, ok := tasks1[0].(*p.HistoryReplicationTask) s.True(ok, "expected a history replication task") s.Equal(domainID, task1.DomainID) s.Equal(workflowExecution.GetWorkflowID(), task1.WorkflowID) s.Equal(workflowExecution.GetRunID(), task1.RunID) s.Equal(int64(1), task1.FirstEventID) s.Equal(int64(3), task1.NextEventID) s.Equal(int64(9), task1.Version) err = s.CompleteReplicationTask(ctx, task1.TaskID) s.NoError(err) tasks2, err := s.GetReplicationTasks(ctx, 1, false) s.NoError(err) s.Equal(0, len(tasks2)) } // TestReplicationTransferTaskRangeComplete test func (s *ExecutionManagerSuite) TestReplicationTransferTaskRangeComplete() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "replication-transfer-task--range-complete-test", RunID: uuid.New(), } task0, err := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err) s.NotNil(task0, "Expected non empty task identifier.") taskD, err := s.GetTransferTasks(ctx, 1, false) s.NoError(err) s.Equal(1, len(taskD), "Expected 1 decision task.") err = s.CompleteTransferTask(ctx, taskD[0].GetTaskID()) s.NoError(err) state1, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err) info1 := state1.ExecutionInfo s.NotNil(info1, "Valid Workflow info expected.") updatedInfo1 := copyWorkflowExecutionInfo(info1) updatedStats1 := copyExecutionStats(state1.ExecutionStats) replicationTasks := []p.Task{ &p.HistoryReplicationTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), Version: 9, }, FirstEventID: 1, NextEventID: 3, }, &p.HistoryReplicationTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), Version: 9, }, FirstEventID: 4, NextEventID: 5, }, } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: 3, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err = s.UpdateWorklowStateAndReplication(ctx, updatedInfo1, updatedStats1, versionHistories, int64(3), replicationTasks, ) s.NoError(err) tasks1, err := s.GetReplicationTasks(ctx, 2, false) s.NoError(err) s.NotNil(tasks1, "expected valid list of tasks.") s.Equal(2, len(tasks1), "Expected 2 replication tasks.") task1, ok := tasks1[0].(*p.HistoryReplicationTask) s.True(ok, "expected a history replication task") s.Equal(domainID, task1.DomainID) s.Equal(workflowExecution.GetWorkflowID(), task1.WorkflowID) s.Equal(workflowExecution.GetRunID(), task1.RunID) s.Equal(int64(1), task1.FirstEventID) s.Equal(int64(3), task1.NextEventID) s.Equal(int64(9), task1.Version) task2, ok := tasks1[1].(*p.HistoryReplicationTask) s.True(ok, "expected a history replication task") s.Equal(domainID, task2.DomainID) s.Equal(workflowExecution.GetWorkflowID(), task2.WorkflowID) s.Equal(workflowExecution.GetRunID(), task2.RunID) s.Equal(int64(4), task2.FirstEventID) s.Equal(int64(5), task2.NextEventID) s.Equal(int64(9), task2.Version) err = s.RangeCompleteReplicationTask(ctx, task2.TaskID+1) s.NoError(err) tasks2, err := s.GetReplicationTasks(ctx, 1, false) s.NoError(err) s.Equal(0, len(tasks2)) } // TestUpdateAndClearBufferedEvents test func (s *ExecutionManagerSuite) TestUpdateAndClearBufferedEvents() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "4ca1faac-1a3a-47af-8e51-fdaa2b3d45b9" workflowExecution := types.WorkflowExecution{ WorkflowID: "test-update-and-clear-buffered-events", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } task0, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "taskList", "wType", 20, 13, nil, 3, 0, 2, nil, nil) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") stats0, state0, err1 := s.GetWorkflowExecutionInfoWithStats(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") s.Equal(0, stats0.BufferedEventsCount) s.Equal(0, stats0.BufferedEventsSize) eventsBatch1 := []*types.HistoryEvent{ { ID: 5, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), Version: 11, DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 2, StartedEventID: 3, Identity: "test_worker", }, }, { ID: 6, EventType: types.EventTypeTimerStarted.Ptr(), Version: 11, TimerStartedEventAttributes: &types.TimerStartedEventAttributes{ TimerID: "ID1", StartToFireTimeoutSeconds: common.Int64Ptr(101), DecisionTaskCompletedEventID: 5, }, }, } eventsBatch2 := []*types.HistoryEvent{ { ID: 21, EventType: types.EventTypeTimerFired.Ptr(), Version: 12, TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "2", StartedEventID: 3, }, }, } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: eventsBatch2[0].ID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedState := &p.WorkflowMutableState{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, VersionHistories: versionHistories, } err2 := s.UpdateAllMutableState(ctx, updatedState, int64(3)) s.NoError(err2) partialState, err2 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) s.NotNil(partialState, "expected valid state.") partialInfo := partialState.ExecutionInfo s.NotNil(partialInfo, "Valid Workflow info expected.") bufferUpdateInfo := copyWorkflowExecutionInfo(partialInfo) bufferedUpdatedStats := copyExecutionStats(state0.ExecutionStats) err2 = s.UpdateWorklowStateAndReplication(ctx, bufferUpdateInfo, bufferedUpdatedStats, versionHistories, bufferUpdateInfo.NextEventID, nil) s.NoError(err2) err2 = s.UpdateWorklowStateAndReplication(ctx, bufferUpdateInfo, bufferedUpdatedStats, versionHistories, bufferUpdateInfo.NextEventID, nil) s.NoError(err2) err2 = s.UpdateWorkflowExecutionForBufferEvents(ctx, bufferUpdateInfo, bufferedUpdatedStats, bufferUpdateInfo.NextEventID, eventsBatch1, false, versionHistories) s.NoError(err2) stats0, state0, err2 = s.GetWorkflowExecutionInfoWithStats(ctx, domainID, workflowExecution) s.NoError(err2) s.Equal(1, stats0.BufferedEventsCount) s.True(stats0.BufferedEventsSize > 0) testHistory := &types.History{Events: make([]*types.HistoryEvent, 0)} testHistory.Events = append(testHistory.Events, eventsBatch1...) history0 := &types.History{Events: state0.BufferedEvents} s.Equal(testHistory, history0) testHistory.Events = append(testHistory.Events, eventsBatch2...) err2 = s.UpdateWorkflowExecutionForBufferEvents(ctx, bufferUpdateInfo, bufferedUpdatedStats, bufferUpdateInfo.NextEventID, eventsBatch2, false, versionHistories) s.NoError(err2) stats1, state1, err1 := s.GetWorkflowExecutionInfoWithStats(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state1, "expected valid state.") info1 := state1.ExecutionInfo s.NotNil(info1, "Valid Workflow info expected.") s.Equal(2, stats1.BufferedEventsCount) s.True(stats1.BufferedEventsSize > 0) history1 := &types.History{Events: state1.BufferedEvents} s.Equal(testHistory, history1) err3 := s.UpdateWorkflowExecutionForBufferEvents(ctx, bufferUpdateInfo, bufferedUpdatedStats, bufferUpdateInfo.NextEventID, nil, true, versionHistories) s.NoError(err3) stats3, state3, err3 := s.GetWorkflowExecutionInfoWithStats(ctx, domainID, workflowExecution) s.NoError(err3) s.NotNil(state3, "expected valid state.") info3 := state3.ExecutionInfo s.NotNil(info3, "Valid Workflow info expected.") s.Equal(0, stats3.BufferedEventsCount) s.Equal(0, stats3.BufferedEventsSize) } // TestConflictResolveWorkflowExecutionCurrentIsSelf test func (s *ExecutionManagerSuite) TestConflictResolveWorkflowExecutionCurrentIsSelf() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "4ca1faac-1a3a-47af-8e51-fdaa2b3d45b9" workflowExecution := types.WorkflowExecution{ WorkflowID: "test-reset-mutable-state-test-current-is-self", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } partitionConfig := map[string]string{ "userid": uuid.New(), } task0, err0 := s.CreateWorkflowExecution( ctx, domainID, workflowExecution, "taskList", "wType", 20, 13, nil, 3, 0, 2, nil, partitionConfig, ) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") stats0, state0, err1 := s.GetWorkflowExecutionInfoWithStats(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") s.Equal(0, stats0.BufferedEventsCount) s.Equal(0, stats0.BufferedEventsSize) updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) currentTime := time.Now().UTC() expiryTime := currentTime.Add(10 * time.Second) eventsBatch1 := []*types.HistoryEvent{ { ID: 5, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), Version: 11, DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 2, StartedEventID: 3, Identity: "test_worker", }, }, { ID: 6, EventType: types.EventTypeTimerStarted.Ptr(), Version: 11, TimerStartedEventAttributes: &types.TimerStartedEventAttributes{ TimerID: "ID1", StartToFireTimeoutSeconds: common.Int64Ptr(101), DecisionTaskCompletedEventID: 5, }, }, } eventsBatch2 := []*types.HistoryEvent{ { ID: 21, EventType: types.EventTypeTimerFired.Ptr(), Version: 12, TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "2", StartedEventID: 3, }, }, } csum := s.newRandomChecksum() versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: updatedInfo.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) updatedState := &p.WorkflowMutableState{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, ActivityInfos: map[int64]*p.ActivityInfo{ 4: { Version: 7789, ScheduleID: 4, ScheduledEventBatchID: 3, ScheduledEvent: &types.HistoryEvent{ID: 40}, ScheduledTime: currentTime, StartedID: 6, StartedEvent: &types.HistoryEvent{ID: 60}, StartedTime: currentTime, ScheduleToCloseTimeout: 1, ScheduleToStartTimeout: 2, StartToCloseTimeout: 3, HeartbeatTimeout: 4, LastHeartBeatUpdatedTime: currentTime, TimerTaskStatus: 1, }, 5: { Version: 7789, ScheduleID: 5, ScheduledEventBatchID: 3, ScheduledEvent: &types.HistoryEvent{ID: 50}, ScheduledTime: currentTime, StartedID: 7, StartedEvent: &types.HistoryEvent{ID: 70}, StartedTime: currentTime, ScheduleToCloseTimeout: 1, ScheduleToStartTimeout: 2, StartToCloseTimeout: 3, HeartbeatTimeout: 4, LastHeartBeatUpdatedTime: currentTime, TimerTaskStatus: 1, }}, TimerInfos: map[string]*p.TimerInfo{ "t1": { Version: 2333, TimerID: "t1", StartedID: 1, ExpiryTime: expiryTime, TaskStatus: 500, }, "t2": { Version: 2333, TimerID: "t2", StartedID: 2, ExpiryTime: expiryTime, TaskStatus: 501, }, "t3": { Version: 2333, TimerID: "t3", StartedID: 3, ExpiryTime: expiryTime, TaskStatus: 502, }, }, ChildExecutionInfos: map[int64]*p.ChildExecutionInfo{ 9: { Version: 2334, InitiatedID: 9, InitiatedEvent: &types.HistoryEvent{ID: 123}, StartedID: 11, StartedRunID: uuid.New(), StartedEvent: nil, CreateRequestID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", DomainID: uuid.New(), }, }, RequestCancelInfos: map[int64]*p.RequestCancelInfo{ 19: { Version: 2335, InitiatedID: 19, InitiatedEventBatchID: 17, CancelRequestID: "cancel_requested_id", }, }, SignalInfos: map[int64]*p.SignalInfo{ 39: { Version: 2336, InitiatedID: 39, InitiatedEventBatchID: 38, SignalRequestID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", SignalName: "signalA", Input: []byte("signal_input_A"), Control: []byte("signal_control_A"), }, }, SignalRequestedIDs: map[string]struct{}{ "00000000-0000-0000-0000-000000000001": {}, "00000000-0000-0000-0000-000000000002": {}, "00000000-0000-0000-0000-000000000003": {}, }, Checksum: csum, VersionHistories: versionHistories, } err2 := s.UpdateAllMutableState(ctx, updatedState, int64(3)) s.NoError(err2) partialState, err2 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) s.NotNil(partialState, "expected valid state.") partialInfo := partialState.ExecutionInfo s.NotNil(partialInfo, "Valid Workflow info expected.") s.assertChecksumsEqual(csum, partialState.Checksum) bufferUpdateInfo := copyWorkflowExecutionInfo(partialInfo) bufferedUpdatedStats := copyExecutionStats(partialState.ExecutionStats) err2 = s.UpdateWorklowStateAndReplication(ctx, bufferUpdateInfo, bufferedUpdatedStats, versionHistories, bufferUpdateInfo.NextEventID, nil) s.NoError(err2) err2 = s.UpdateWorklowStateAndReplication(ctx, bufferUpdateInfo, bufferedUpdatedStats, versionHistories, bufferUpdateInfo.NextEventID, nil) s.NoError(err2) err2 = s.UpdateWorkflowExecutionForBufferEvents(ctx, bufferUpdateInfo, bufferedUpdatedStats, bufferUpdateInfo.NextEventID, eventsBatch1, false, versionHistories) s.NoError(err2) stats0, state0, err2 = s.GetWorkflowExecutionInfoWithStats(ctx, domainID, workflowExecution) s.NoError(err2) s.Equal(1, stats0.BufferedEventsCount) s.True(stats0.BufferedEventsSize > 0) s.assertChecksumsEqual(testWorkflowChecksum, state0.Checksum) testHistory := &types.History{Events: make([]*types.HistoryEvent, 0)} testHistory.Events = append(testHistory.Events, eventsBatch1...) history0 := &types.History{Events: state0.BufferedEvents} s.Equal(testHistory, history0) testHistory.Events = append(testHistory.Events, eventsBatch2...) err2 = s.UpdateWorkflowExecutionForBufferEvents(ctx, bufferUpdateInfo, bufferedUpdatedStats, bufferUpdateInfo.NextEventID, eventsBatch2, false, versionHistories) s.NoError(err2) stats1, state1, err1 := s.GetWorkflowExecutionInfoWithStats(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state1, "expected valid state.") info1 := state1.ExecutionInfo s.NotNil(info1, "Valid Workflow info expected.") s.Equal(2, stats1.BufferedEventsCount) s.True(stats1.BufferedEventsSize > 0) s.assertChecksumsEqual(testWorkflowChecksum, state1.Checksum) history1 := &types.History{Events: state1.BufferedEvents} s.Equal(testHistory, history1) s.Equal(2, len(state1.ActivityInfos)) ai, ok := state1.ActivityInfos[4] s.True(ok) s.NotNil(ai) s.Equal(int64(7789), ai.Version) s.Equal(int64(4), ai.ScheduleID) s.Equal(int64(3), ai.ScheduledEventBatchID) s.Equal(int64(40), ai.ScheduledEvent.ID) s.EqualTimes(currentTime, ai.ScheduledTime) s.Equal(int64(6), ai.StartedID) s.Equal(int64(60), ai.StartedEvent.ID) s.EqualTimes(currentTime, ai.StartedTime) s.Equal(int32(1), ai.ScheduleToCloseTimeout) s.Equal(int32(2), ai.ScheduleToStartTimeout) s.Equal(int32(3), ai.StartToCloseTimeout) s.Equal(int32(4), ai.HeartbeatTimeout) s.EqualTimes(currentTime, ai.LastHeartBeatUpdatedTime) s.Equal(int32(1), ai.TimerTaskStatus) ai, ok = state1.ActivityInfos[5] s.True(ok) s.NotNil(ai) s.Equal(int64(7789), ai.Version) s.Equal(int64(5), ai.ScheduleID) s.Equal(int64(3), ai.ScheduledEventBatchID) s.Equal(int64(50), ai.ScheduledEvent.ID) s.EqualTimes(currentTime, ai.ScheduledTime) s.Equal(int64(7), ai.StartedID) s.Equal(int64(70), ai.StartedEvent.ID) s.EqualTimes(currentTime, ai.StartedTime) s.Equal(int32(1), ai.ScheduleToCloseTimeout) s.Equal(int32(2), ai.ScheduleToStartTimeout) s.Equal(int32(3), ai.StartToCloseTimeout) s.Equal(int32(4), ai.HeartbeatTimeout) s.EqualTimes(currentTime, ai.LastHeartBeatUpdatedTime) s.Equal(int32(1), ai.TimerTaskStatus) s.Equal(3, len(state1.TimerInfos)) ti, ok := state1.TimerInfos["t1"] s.True(ok) s.NotNil(ti) s.Equal(int64(2333), ti.Version) s.Equal("t1", ti.TimerID) s.Equal(int64(1), ti.StartedID) s.EqualTimes(expiryTime, ti.ExpiryTime) s.Equal(int64(500), ti.TaskStatus) ti, ok = state1.TimerInfos["t2"] s.True(ok) s.NotNil(ti) s.Equal(int64(2333), ti.Version) s.Equal("t2", ti.TimerID) s.Equal(int64(2), ti.StartedID) s.EqualTimes(expiryTime, ti.ExpiryTime) s.Equal(int64(501), ti.TaskStatus) ti, ok = state1.TimerInfos["t3"] s.True(ok) s.NotNil(ti) s.Equal(int64(2333), ti.Version) s.Equal("t3", ti.TimerID) s.Equal(int64(3), ti.StartedID) s.EqualTimes(expiryTime, ti.ExpiryTime) s.Equal(int64(502), ti.TaskStatus) s.Equal(1, len(state1.ChildExecutionInfos)) ci, ok := state1.ChildExecutionInfos[9] s.True(ok) s.NotNil(ci) s.Equal(updatedState.ChildExecutionInfos[9], ci) s.Equal(1, len(state1.RequestCancelInfos)) rci, ok := state1.RequestCancelInfos[19] s.True(ok) s.NotNil(rci) s.Equal(int64(2335), rci.Version) s.Equal(1, len(state1.SignalInfos)) si, ok := state1.SignalInfos[39] s.True(ok) s.NotNil(si) s.Equal(int64(2336), si.Version) s.Equal(3, len(state1.SignalRequestedIDs)) _, contains := state1.SignalRequestedIDs["00000000-0000-0000-0000-000000000001"] s.True(contains) _, contains = state1.SignalRequestedIDs["00000000-0000-0000-0000-000000000002"] s.True(contains) _, contains = state1.SignalRequestedIDs["00000000-0000-0000-0000-000000000003"] s.True(contains) s.Equal(3, len(state1.BufferedEvents)) updatedInfo1 := copyWorkflowExecutionInfo(info1) updatedStats1 := copyExecutionStats(state1.ExecutionStats) updatedInfo1.NextEventID = int64(3) resetActivityInfos := []*p.ActivityInfo{ { Version: 8789, ScheduleID: 40, ScheduledEventBatchID: 30, ScheduledEvent: &types.HistoryEvent{ID: 400}, ScheduledTime: currentTime, StartedID: 60, StartedEvent: &types.HistoryEvent{ID: 600}, StartedTime: currentTime, ScheduleToCloseTimeout: 10, ScheduleToStartTimeout: 20, StartToCloseTimeout: 30, HeartbeatTimeout: 40, LastHeartBeatUpdatedTime: currentTime, TimerTaskStatus: 1, }} resetTimerInfos := []*p.TimerInfo{ { Version: 3333, TimerID: "t1_new", StartedID: 1, ExpiryTime: expiryTime, TaskStatus: 600, }, { Version: 3333, TimerID: "t2_new", StartedID: 2, ExpiryTime: expiryTime, TaskStatus: 601, }} resetChildExecutionInfos := []*p.ChildExecutionInfo{ { Version: 3334, InitiatedID: 10, InitiatedEvent: &types.HistoryEvent{ID: 10}, StartedID: 15, StartedRunID: uuid.New(), StartedEvent: nil, CreateRequestID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", DomainID: uuid.New(), }} resetRequestCancelInfos := []*p.RequestCancelInfo{ { Version: 3335, InitiatedID: 29, CancelRequestID: "new_cancel_requested_id", }} resetSignalInfos := []*p.SignalInfo{ { Version: 3336, InitiatedID: 39, SignalRequestID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", SignalName: "signalB", Input: []byte("signal_input_b"), Control: []byte("signal_control_b"), }, { Version: 3336, InitiatedID: 42, SignalRequestID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", SignalName: "signalC", Input: []byte("signal_input_c"), Control: []byte("signal_control_c"), }} err3 := s.ConflictResolveWorkflowExecution( ctx, updatedInfo1, updatedStats1, int64(5), resetActivityInfos, resetTimerInfos, resetChildExecutionInfos, resetRequestCancelInfos, resetSignalInfos, nil, versionHistories, ) s.NoError(err3) stats4, state4, err4 := s.GetWorkflowExecutionInfoWithStats(ctx, domainID, workflowExecution) s.NoError(err4) s.NotNil(state4, "expected valid state.") s.Equal(0, stats4.BufferedEventsCount) s.Equal(0, stats4.BufferedEventsSize) info4 := state4.ExecutionInfo s.T().Logf("%+v\n", info4) s.NotNil(info4, "Valid Workflow info expected.") s.Equal(int64(3), info4.NextEventID) s.Equal("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", info4.FirstExecutionRunID) s.Equal(partitionConfig, info4.PartitionConfig) s.Equal(1, len(state4.ActivityInfos)) ai, ok = state4.ActivityInfos[40] s.True(ok) s.NotNil(ai) s.Equal(int64(8789), ai.Version) s.Equal(int64(40), ai.ScheduleID) s.Equal(int64(30), ai.ScheduledEventBatchID) s.Equal(int64(400), ai.ScheduledEvent.ID) s.Equal(currentTime.Unix(), ai.ScheduledTime.Unix()) s.Equal(int64(60), ai.StartedID) s.Equal(int64(600), ai.StartedEvent.ID) s.Equal(currentTime.Unix(), ai.StartedTime.Unix()) s.Equal(int32(10), ai.ScheduleToCloseTimeout) s.Equal(int32(20), ai.ScheduleToStartTimeout) s.Equal(int32(30), ai.StartToCloseTimeout) s.Equal(int32(40), ai.HeartbeatTimeout) s.Equal(currentTime.Unix(), ai.LastHeartBeatUpdatedTime.Unix()) s.Equal(int32(1), ai.TimerTaskStatus) s.Equal(2, len(state4.TimerInfos)) ti, ok = state4.TimerInfos["t1_new"] s.True(ok) s.NotNil(ai) s.Equal(int64(3333), ti.Version) s.Equal("t1_new", ti.TimerID) s.Equal(int64(1), ti.StartedID) s.EqualTimes(expiryTime, ti.ExpiryTime) s.Equal(int64(600), ti.TaskStatus) ti, ok = state4.TimerInfos["t2_new"] s.True(ok) s.NotNil(ai) s.Equal(int64(3333), ti.Version) s.Equal("t2_new", ti.TimerID) s.Equal(int64(2), ti.StartedID) s.EqualTimes(expiryTime, ti.ExpiryTime) s.Equal(int64(601), ti.TaskStatus) s.Equal(1, len(state4.ChildExecutionInfos)) ci, ok = state4.ChildExecutionInfos[10] s.True(ok) s.NotNil(ci) s.Equal(resetChildExecutionInfos[0], ci) s.Equal(1, len(state4.RequestCancelInfos)) rci, ok = state4.RequestCancelInfos[29] s.True(ok) s.NotNil(rci) s.Equal(int64(3335), rci.Version) s.Equal(int64(29), rci.InitiatedID) s.Equal("new_cancel_requested_id", rci.CancelRequestID) s.Equal(2, len(state4.SignalInfos)) si, ok = state4.SignalInfos[39] s.True(ok) s.NotNil(si) s.Equal(int64(3336), si.Version) s.Equal(int64(39), si.InitiatedID) s.Equal("signalB", si.SignalName) s.Equal([]byte("signal_input_b"), si.Input) s.Equal([]byte("signal_control_b"), si.Control) si, ok = state4.SignalInfos[42] s.True(ok) s.NotNil(si) s.Equal(int64(3336), si.Version) s.Equal(int64(42), si.InitiatedID) s.Equal("signalC", si.SignalName) s.Equal([]byte("signal_input_c"), si.Input) s.Equal([]byte("signal_control_c"), si.Control) s.Equal(0, len(state4.SignalRequestedIDs)) s.Equal(0, len(state4.BufferedEvents)) s.assertChecksumsEqual(testWorkflowChecksum, state4.Checksum) } // TestConflictResolveWorkflowExecutionWithCASMismatch test func (s *ExecutionManagerSuite) TestConflictResolveWorkflowExecutionWithCASMismatch() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "4ca1faac-1a3a-47af-8e51-fdaa2b3d45b9" workflowID := "test-reset-mutable-state-test-mismatch" // first create a workflow and continue as new it workflowExecutionReset := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa0", } partitionConfig := map[string]string{ "userid": uuid.New(), } nextEventID := int64(3) resp, err := s.CreateWorkflowExecution( ctx, domainID, workflowExecutionReset, "taskList", "wType", 20, 13, nil, nextEventID, 0, 2, nil, partitionConfig, ) s.NoError(err) s.NotNil(resp) state, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) info := state.ExecutionInfo continueAsNewInfo := copyWorkflowExecutionInfo(info) continueAsNewStats := copyExecutionStats(state.ExecutionStats) continueAsNewInfo.State = p.WorkflowStateRunning continueAsNewInfo.NextEventID = int64(5) continueAsNewInfo.LastProcessedEvent = int64(2) workflowExecutionCurrent := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa1", } err = s.ContinueAsNewExecution( ctx, continueAsNewInfo, continueAsNewStats, info.NextEventID, workflowExecutionCurrent, int64(3), int64(2), nil, ) s.NoError(err) runID1, err := s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.NoError(err) s.Equal(workflowExecutionCurrent.GetRunID(), runID1) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionCurrent) s.NoError(err) currentInfo := copyWorkflowExecutionInfo(state.ExecutionInfo) currentStats := copyExecutionStats(state.ExecutionStats) currentInfo.State = p.WorkflowStateCompleted currentInfo.CloseStatus = p.WorkflowCloseStatusCompleted currentInfo.NextEventID = int64(6) currentInfo.LastProcessedEvent = int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: currentInfo.LastProcessedEvent, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) err3 := s.UpdateWorkflowExecutionAndFinish(ctx, currentInfo, currentStats, int64(3), versionHistories) s.NoError(err3) runID1, err = s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.NoError(err) s.Equal(workflowExecutionCurrent.GetRunID(), runID1) resetExecutionInfo := &p.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowExecutionReset.GetWorkflowID(), RunID: workflowExecutionReset.GetRunID(), ParentDomainID: uuid.New(), ParentWorkflowID: "some random parent workflow ID", ParentRunID: uuid.New(), InitiatedID: 12345, TaskList: "some random tasklist", WorkflowTypeName: "some random workflow type name", WorkflowTimeout: 1112, DecisionStartToCloseTimeout: 14, State: p.WorkflowStateRunning, LastFirstEventID: constants.FirstEventID, NextEventID: 123, CreateRequestID: uuid.New(), DecisionVersion: constants.EmptyVersion, DecisionScheduleID: 111, DecisionStartedID: 222, DecisionRequestID: uuid.New(), DecisionTimeout: 0, } resetStats := &p.ExecutionStats{} resetActivityInfos := []*p.ActivityInfo{} resetTimerInfos := []*p.TimerInfo{} resetChildExecutionInfos := []*p.ChildExecutionInfo{} resetRequestCancelInfos := []*p.RequestCancelInfo{} resetSignalInfos := []*p.SignalInfo{} err = s.ConflictResolveWorkflowExecution( ctx, resetExecutionInfo, resetStats, continueAsNewInfo.NextEventID, resetActivityInfos, resetTimerInfos, resetChildExecutionInfos, resetRequestCancelInfos, resetSignalInfos, nil, versionHistories, ) s.NotNil(err) err = s.ConflictResolveWorkflowExecution( ctx, resetExecutionInfo, resetStats, continueAsNewInfo.NextEventID, resetActivityInfos, resetTimerInfos, resetChildExecutionInfos, resetRequestCancelInfos, resetSignalInfos, nil, versionHistories, ) s.NotNil(err) err = s.ConflictResolveWorkflowExecution( ctx, resetExecutionInfo, resetStats, continueAsNewInfo.NextEventID, resetActivityInfos, resetTimerInfos, resetChildExecutionInfos, resetRequestCancelInfos, resetSignalInfos, nil, versionHistories, ) s.NotNil(err) // this test only assert whether the current workflow execution record is reset runID, err := s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.NoError(err) s.Equal(workflowExecutionCurrent.GetRunID(), runID) } // TestConflictResolveWorkflowExecutionWithTransactionCurrentIsNotSelf test func (s *ExecutionManagerSuite) TestConflictResolveWorkflowExecutionWithTransactionCurrentIsNotSelf() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "4ca1faac-1a3a-47af-8e51-fdaa2b3d45b9" workflowID := "test-reset-mutable-state-test-with-transaction-current-is-not-self" // first create a workflow and continue as new it workflowExecutionReset := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa0", } nextEventID := int64(3) partitionConfig := map[string]string{ "userid": uuid.New(), } resp, err := s.CreateWorkflowExecution( ctx, domainID, workflowExecutionReset, "taskList", "wType", 20, 13, nil, nextEventID, 0, 2, nil, partitionConfig, ) s.NoError(err) s.NotNil(resp) state, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) info := state.ExecutionInfo continueAsNewInfo := copyWorkflowExecutionInfo(info) continueAsNewStats := copyExecutionStats(state.ExecutionStats) continueAsNewInfo.State = p.WorkflowStateRunning continueAsNewInfo.NextEventID = int64(5) continueAsNewInfo.LastProcessedEvent = int64(2) workflowExecutionCurrent := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa1", } err = s.ContinueAsNewExecution( ctx, continueAsNewInfo, continueAsNewStats, info.NextEventID, workflowExecutionCurrent, int64(3), int64(2), nil, ) s.NoError(err) currentRunID, err := s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.NoError(err) s.Equal(workflowExecutionCurrent.GetRunID(), currentRunID) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionCurrent) s.NoError(err) currentInfo := copyWorkflowExecutionInfo(state.ExecutionInfo) currentInfo.State = p.WorkflowStateCompleted currentInfo.CloseStatus = p.WorkflowCloseStatusTerminated resetExecutionInfo := &p.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowExecutionReset.GetWorkflowID(), RunID: workflowExecutionReset.GetRunID(), FirstExecutionRunID: workflowExecutionReset.GetRunID(), ParentDomainID: uuid.New(), ParentWorkflowID: "some random parent workflow ID", ParentRunID: uuid.New(), InitiatedID: 12345, TaskList: "some random tasklist", WorkflowTypeName: "some random workflow type name", WorkflowTimeout: 1112, DecisionStartToCloseTimeout: 14, State: p.WorkflowStateCompleted, CloseStatus: p.WorkflowCloseStatusCompleted, LastFirstEventID: constants.FirstEventID, NextEventID: 123, CreateRequestID: uuid.New(), DecisionVersion: constants.EmptyVersion, DecisionScheduleID: 111, DecisionStartedID: 222, DecisionRequestID: uuid.New(), DecisionTimeout: 0, AutoResetPoints: &types.ResetPoints{}, PartitionConfig: nil, } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: resetExecutionInfo.DecisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) resetReq := &p.ConflictResolveWorkflowExecutionRequest{ RangeID: s.ShardInfo.RangeID, Mode: p.ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: resetExecutionInfo, ExecutionStats: &p.ExecutionStats{}, Condition: int64(5), ActivityInfos: []*p.ActivityInfo{}, TimerInfos: []*p.TimerInfo{}, ChildExecutionInfos: []*p.ChildExecutionInfo{}, RequestCancelInfos: []*p.RequestCancelInfo{}, SignalInfos: []*p.SignalInfo{}, SignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, NewWorkflowSnapshot: nil, CurrentWorkflowMutation: &p.WorkflowMutation{ ExecutionInfo: currentInfo, ExecutionStats: &p.ExecutionStats{}, Condition: int64(3), UpsertActivityInfos: []*p.ActivityInfo{}, UpsertTimerInfos: []*p.TimerInfo{}, UpsertChildExecutionInfos: []*p.ChildExecutionInfo{}, UpsertRequestCancelInfos: []*p.RequestCancelInfo{}, UpsertSignalInfos: []*p.SignalInfo{}, UpsertSignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, Encoding: pickRandomEncoding(), } _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.Error(err) resetReq.Mode = p.ConflictResolveWorkflowModeUpdateCurrent _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.NoError(err) currentRecord, err := s.ExecutionManager.GetCurrentExecution(ctx, &p.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, }) s.NoError(err) s.Equal(resetExecutionInfo.RunID, currentRecord.RunID) s.Equal(resetExecutionInfo.CreateRequestID, currentRecord.StartRequestID) s.Equal(resetExecutionInfo.State, currentRecord.State) s.Equal(resetExecutionInfo.CloseStatus, currentRecord.CloseStatus) s.Equal(int64(-24), currentRecord.LastWriteVersion) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.ResetWorkflowSnapshot.ExecutionInfo.ExpirationTime s.Equal(resetReq.ResetWorkflowSnapshot.ExecutionInfo, state.ExecutionInfo) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionCurrent) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.CurrentWorkflowMutation.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.CurrentWorkflowMutation.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.CurrentWorkflowMutation.ExecutionInfo.ExpirationTime s.Equal(resetReq.CurrentWorkflowMutation.ExecutionInfo, state.ExecutionInfo) } // TestConflictResolveWorkflowExecutionWithTransactionCurrentIsNotSelfWithContinueAsNew test func (s *ExecutionManagerSuite) TestConflictResolveWorkflowExecutionWithTransactionCurrentIsNotSelfWithContinueAsNew() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "4ca1faac-1a3a-47af-8e51-fdaa2b3d45b9" workflowID := "test-reset-mutable-state-test-with-transaction-current-is-not-self-with-continue-as-new" // first create a workflow and continue as new it workflowExecutionReset := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa0", } nextEventID := int64(3) partitionConfig := map[string]string{ "userid": uuid.New(), } resp, err := s.CreateWorkflowExecution( ctx, domainID, workflowExecutionReset, "taskList", "wType", 20, 13, nil, nextEventID, 0, 2, nil, partitionConfig, ) s.NoError(err) s.NotNil(resp) state, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) continueAsNewStats := copyExecutionStats(state.ExecutionStats) info := state.ExecutionInfo continueAsNewInfo := copyWorkflowExecutionInfo(info) continueAsNewInfo.State = p.WorkflowStateRunning continueAsNewInfo.NextEventID = int64(5) continueAsNewInfo.LastProcessedEvent = int64(2) workflowExecutionCurrent := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa1", } err = s.ContinueAsNewExecution( ctx, continueAsNewInfo, continueAsNewStats, info.NextEventID, workflowExecutionCurrent, int64(3), int64(2), nil, ) s.NoError(err) currentRunID, err := s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.NoError(err) s.Equal(workflowExecutionCurrent.GetRunID(), currentRunID) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionCurrent) s.NoError(err) currentInfo := copyWorkflowExecutionInfo(state.ExecutionInfo) currentStats := copyExecutionStats(state.ExecutionStats) currentInfo.State = p.WorkflowStateCompleted currentInfo.CloseStatus = p.WorkflowCloseStatusTerminated decisionScheduleID := int64(111) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: decisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) resetExecutionInfo := &p.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowExecutionReset.GetWorkflowID(), RunID: workflowExecutionReset.GetRunID(), FirstExecutionRunID: workflowExecutionReset.GetRunID(), ParentDomainID: uuid.New(), ParentWorkflowID: "some random parent workflow ID", ParentRunID: uuid.New(), InitiatedID: 12345, TaskList: "some random tasklist", WorkflowTypeName: "some random workflow type name", WorkflowTimeout: 1112, DecisionStartToCloseTimeout: 14, State: p.WorkflowStateCompleted, CloseStatus: p.WorkflowCloseStatusContinuedAsNew, LastFirstEventID: constants.FirstEventID, NextEventID: 123, CreateRequestID: uuid.New(), DecisionVersion: constants.EmptyVersion, DecisionScheduleID: decisionScheduleID, DecisionStartedID: 222, DecisionRequestID: uuid.New(), DecisionTimeout: 0, AutoResetPoints: &types.ResetPoints{}, PartitionConfig: map[string]string{"zone": "dca1"}, } newWorkflowExecutionInfo := copyWorkflowExecutionInfo(resetExecutionInfo) newWorkflowExecutionStats := &p.ExecutionStats{} newWorkflowExecutionInfo.CreateRequestID = uuid.New() newWorkflowExecutionInfo.RunID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa2" newWorkflowExecutionInfo.State = p.WorkflowStateRunning newWorkflowExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone newWorkflowExecutionInfo.PartitionConfig = map[string]string{"user": uuid.New()} resetReq := &p.ConflictResolveWorkflowExecutionRequest{ RangeID: s.ShardInfo.RangeID, Mode: p.ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: resetExecutionInfo, ExecutionStats: &p.ExecutionStats{}, Condition: int64(5), ActivityInfos: []*p.ActivityInfo{}, TimerInfos: []*p.TimerInfo{}, ChildExecutionInfos: []*p.ChildExecutionInfo{}, RequestCancelInfos: []*p.RequestCancelInfo{}, SignalInfos: []*p.SignalInfo{}, SignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, NewWorkflowSnapshot: &p.WorkflowSnapshot{ ExecutionInfo: newWorkflowExecutionInfo, ExecutionStats: newWorkflowExecutionStats, Condition: 0, ActivityInfos: []*p.ActivityInfo{}, TimerInfos: []*p.TimerInfo{}, ChildExecutionInfos: []*p.ChildExecutionInfo{}, RequestCancelInfos: []*p.RequestCancelInfo{}, SignalInfos: []*p.SignalInfo{}, SignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, CurrentWorkflowMutation: &p.WorkflowMutation{ ExecutionInfo: currentInfo, ExecutionStats: currentStats, Condition: int64(3), UpsertActivityInfos: []*p.ActivityInfo{}, UpsertTimerInfos: []*p.TimerInfo{}, UpsertChildExecutionInfos: []*p.ChildExecutionInfo{}, UpsertRequestCancelInfos: []*p.RequestCancelInfo{}, UpsertSignalInfos: []*p.SignalInfo{}, UpsertSignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, Encoding: pickRandomEncoding(), } _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.Error(err) resetReq.Mode = p.ConflictResolveWorkflowModeUpdateCurrent _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.NoError(err) currentRecord, err := s.ExecutionManager.GetCurrentExecution(ctx, &p.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, }) s.NoError(err) s.Equal(newWorkflowExecutionInfo.RunID, currentRecord.RunID) s.Equal(newWorkflowExecutionInfo.CreateRequestID, currentRecord.StartRequestID) s.Equal(newWorkflowExecutionInfo.State, currentRecord.State) s.Equal(newWorkflowExecutionInfo.CloseStatus, currentRecord.CloseStatus) s.Equal(int64(-24), currentRecord.LastWriteVersion) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.ResetWorkflowSnapshot.ExecutionInfo.ExpirationTime s.Equal(resetReq.ResetWorkflowSnapshot.ExecutionInfo, state.ExecutionInfo) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionCurrent) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.CurrentWorkflowMutation.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.CurrentWorkflowMutation.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.CurrentWorkflowMutation.ExecutionInfo.ExpirationTime s.Equal(resetReq.CurrentWorkflowMutation.ExecutionInfo, state.ExecutionInfo) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, types.WorkflowExecution{ WorkflowID: workflowID, RunID: resetReq.NewWorkflowSnapshot.ExecutionInfo.RunID, }) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.NewWorkflowSnapshot.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.NewWorkflowSnapshot.ExecutionInfo.ExpirationTime s.Equal(resetReq.NewWorkflowSnapshot.ExecutionInfo, state.ExecutionInfo) } // TestConflictResolveWorkflowExecutionWithTransactionCurrentIsSelf test func (s *ExecutionManagerSuite) TestConflictResolveWorkflowExecutionWithTransactionCurrentIsSelf() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "4ca1faac-1a3a-47af-8e51-fdaa2b3d45b9" workflowID := "test-reset-mutable-state-test-with-transaction-current-is-self" // first create a workflow and continue as new it workflowExecutionReset := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa0", } partitionConfig := map[string]string{ "userid": uuid.New(), } nextEventID := int64(3) resp, err := s.CreateWorkflowExecution( ctx, domainID, workflowExecutionReset, "taskList", "wType", 20, 13, nil, nextEventID, 0, 2, nil, partitionConfig, ) s.NoError(err) s.NotNil(resp) _, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) resetExecutionInfo := &p.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowExecutionReset.GetWorkflowID(), RunID: workflowExecutionReset.GetRunID(), FirstExecutionRunID: workflowExecutionReset.GetRunID(), ParentDomainID: uuid.New(), ParentWorkflowID: "some random parent workflow ID", ParentRunID: uuid.New(), InitiatedID: 12345, TaskList: "some random tasklist", WorkflowTypeName: "some random workflow type name", WorkflowTimeout: 1112, DecisionStartToCloseTimeout: 14, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, LastFirstEventID: constants.FirstEventID, NextEventID: 123, CreateRequestID: uuid.New(), DecisionVersion: constants.EmptyVersion, DecisionScheduleID: 111, DecisionStartedID: 222, DecisionRequestID: uuid.New(), DecisionTimeout: 0, AutoResetPoints: &types.ResetPoints{}, PartitionConfig: nil, } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: resetExecutionInfo.DecisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) resetReq := &p.ConflictResolveWorkflowExecutionRequest{ RangeID: s.ShardInfo.RangeID, Mode: p.ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: resetExecutionInfo, ExecutionStats: &p.ExecutionStats{}, Condition: nextEventID, ActivityInfos: []*p.ActivityInfo{}, TimerInfos: []*p.TimerInfo{}, ChildExecutionInfos: []*p.ChildExecutionInfo{}, RequestCancelInfos: []*p.RequestCancelInfo{}, SignalInfos: []*p.SignalInfo{}, SignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, NewWorkflowSnapshot: nil, CurrentWorkflowMutation: nil, Encoding: pickRandomEncoding(), } _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.Error(err) resetReq.Mode = p.ConflictResolveWorkflowModeUpdateCurrent _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.NoError(err) currentRecord, err := s.ExecutionManager.GetCurrentExecution(ctx, &p.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, }) s.NoError(err) s.Equal(resetExecutionInfo.RunID, currentRecord.RunID) s.Equal(resetExecutionInfo.CreateRequestID, currentRecord.StartRequestID) s.Equal(resetExecutionInfo.State, currentRecord.State) s.Equal(resetExecutionInfo.CloseStatus, currentRecord.CloseStatus) s.Equal(int64(-24), currentRecord.LastWriteVersion) state, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.ResetWorkflowSnapshot.ExecutionInfo.ExpirationTime s.Equal(resetReq.ResetWorkflowSnapshot.ExecutionInfo, state.ExecutionInfo) } // TestConflictResolveWorkflowExecutionWithTransactionCurrentIsSelfWithContinueAsNew test func (s *ExecutionManagerSuite) TestConflictResolveWorkflowExecutionWithTransactionCurrentIsSelfWithContinueAsNew() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "4ca1faac-1a3a-47af-8e51-fdaa2b3d45b9" workflowID := "test-reset-mutable-state-test-with-transaction-current-is-self-with-continue-as-new" // first create a workflow and continue as new it workflowExecutionReset := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa0", } partitionConfig := map[string]string{ "userid": uuid.New(), } nextEventID := int64(3) resp, err := s.CreateWorkflowExecution( ctx, domainID, workflowExecutionReset, "taskList", "wType", 20, 13, nil, nextEventID, 0, 2, nil, partitionConfig, ) s.NoError(err) s.NotNil(resp) _, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) decisionScheduleID := int64(111) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: decisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) resetExecutionInfo := &p.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowExecutionReset.GetWorkflowID(), RunID: workflowExecutionReset.GetRunID(), FirstExecutionRunID: workflowExecutionReset.GetRunID(), ParentDomainID: uuid.New(), ParentWorkflowID: "some random parent workflow ID", ParentRunID: uuid.New(), InitiatedID: 12345, TaskList: "some random tasklist", WorkflowTypeName: "some random workflow type name", WorkflowTimeout: 1112, DecisionStartToCloseTimeout: 14, State: p.WorkflowStateCompleted, CloseStatus: p.WorkflowCloseStatusContinuedAsNew, LastFirstEventID: constants.FirstEventID, NextEventID: 123, CreateRequestID: uuid.New(), DecisionVersion: constants.EmptyVersion, DecisionScheduleID: decisionScheduleID, DecisionStartedID: 222, DecisionRequestID: uuid.New(), DecisionTimeout: 0, AutoResetPoints: &types.ResetPoints{}, PartitionConfig: nil, } newWorkflowExecutionInfo := copyWorkflowExecutionInfo(resetExecutionInfo) newWorkflowExecutionStats := &p.ExecutionStats{} newWorkflowExecutionInfo.CreateRequestID = uuid.New() newWorkflowExecutionInfo.RunID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa2" newWorkflowExecutionInfo.State = p.WorkflowStateRunning newWorkflowExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone newWorkflowExecutionInfo.PartitionConfig = map[string]string{"zone": "dca1"} resetReq := &p.ConflictResolveWorkflowExecutionRequest{ RangeID: s.ShardInfo.RangeID, Mode: p.ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: resetExecutionInfo, ExecutionStats: &p.ExecutionStats{}, Condition: nextEventID, ActivityInfos: []*p.ActivityInfo{}, TimerInfos: []*p.TimerInfo{}, ChildExecutionInfos: []*p.ChildExecutionInfo{}, RequestCancelInfos: []*p.RequestCancelInfo{}, SignalInfos: []*p.SignalInfo{}, SignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, NewWorkflowSnapshot: &p.WorkflowSnapshot{ ExecutionInfo: newWorkflowExecutionInfo, ExecutionStats: newWorkflowExecutionStats, Condition: 0, ActivityInfos: []*p.ActivityInfo{}, TimerInfos: []*p.TimerInfo{}, ChildExecutionInfos: []*p.ChildExecutionInfo{}, RequestCancelInfos: []*p.RequestCancelInfo{}, SignalInfos: []*p.SignalInfo{}, SignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, CurrentWorkflowMutation: nil, Encoding: pickRandomEncoding(), } _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.Error(err) resetReq.Mode = p.ConflictResolveWorkflowModeUpdateCurrent _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.NoError(err) currentRecord, err := s.ExecutionManager.GetCurrentExecution(ctx, &p.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, }) s.NoError(err) s.Equal(newWorkflowExecutionInfo.RunID, currentRecord.RunID) s.Equal(newWorkflowExecutionInfo.CreateRequestID, currentRecord.StartRequestID) s.Equal(newWorkflowExecutionInfo.State, currentRecord.State) s.Equal(newWorkflowExecutionInfo.CloseStatus, currentRecord.CloseStatus) s.Equal(int64(-24), currentRecord.LastWriteVersion) state, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.ResetWorkflowSnapshot.ExecutionInfo.ExpirationTime s.Equal(resetReq.ResetWorkflowSnapshot.ExecutionInfo, state.ExecutionInfo) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, types.WorkflowExecution{ WorkflowID: workflowID, RunID: resetReq.NewWorkflowSnapshot.ExecutionInfo.RunID, }) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.NewWorkflowSnapshot.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.NewWorkflowSnapshot.ExecutionInfo.ExpirationTime s.Equal(resetReq.NewWorkflowSnapshot.ExecutionInfo, state.ExecutionInfo) } // TestConflictResolveWorkflowExecutionWithTransactionZombieIsSelf test func (s *ExecutionManagerSuite) TestConflictResolveWorkflowExecutionWithTransactionZombieIsSelf() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "4ca1faac-1a3a-47af-8e51-fdaa2b3d45b9" workflowID := "test-reset-mutable-state-test-with-transaction-current-is-zombie" // first create a workflow and continue as new it workflowExecutionReset := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa0", } partitionConfig := map[string]string{ "userid": uuid.New(), } nextEventID := int64(3) resp, err := s.CreateWorkflowExecution( ctx, domainID, workflowExecutionReset, "taskList", "wType", 20, 13, nil, nextEventID, 0, 2, nil, partitionConfig, ) s.NoError(err) s.NotNil(resp) state, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) info := state.ExecutionInfo continueAsNewInfo := copyWorkflowExecutionInfo(info) continueAsNewStats := copyExecutionStats(state.ExecutionStats) continueAsNewInfo.State = p.WorkflowStateRunning continueAsNewInfo.NextEventID = int64(5) continueAsNewInfo.LastProcessedEvent = int64(2) workflowExecutionCurrent := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa1", } err = s.ContinueAsNewExecution( ctx, continueAsNewInfo, continueAsNewStats, info.NextEventID, workflowExecutionCurrent, int64(3), int64(2), nil, ) s.NoError(err) currentRunID, err := s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.NoError(err) s.Equal(workflowExecutionCurrent.GetRunID(), currentRunID) resetExecutionInfo := &p.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowExecutionReset.GetWorkflowID(), RunID: workflowExecutionReset.GetRunID(), FirstExecutionRunID: workflowExecutionReset.GetRunID(), ParentDomainID: uuid.New(), ParentWorkflowID: "some random parent workflow ID", ParentRunID: uuid.New(), InitiatedID: 12345, TaskList: "some random tasklist", WorkflowTypeName: "some random workflow type name", WorkflowTimeout: 1112, DecisionStartToCloseTimeout: 14, State: p.WorkflowStateCompleted, CloseStatus: p.WorkflowCloseStatusCompleted, LastFirstEventID: constants.FirstEventID, NextEventID: 123, CreateRequestID: uuid.New(), DecisionVersion: constants.EmptyVersion, DecisionScheduleID: 111, DecisionStartedID: 222, DecisionRequestID: uuid.New(), DecisionTimeout: 0, AutoResetPoints: &types.ResetPoints{}, PartitionConfig: nil, } versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: resetExecutionInfo.DecisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) resetReq := &p.ConflictResolveWorkflowExecutionRequest{ RangeID: s.ShardInfo.RangeID, Mode: p.ConflictResolveWorkflowModeUpdateCurrent, ResetWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: resetExecutionInfo, ExecutionStats: &p.ExecutionStats{}, Condition: int64(5), ActivityInfos: []*p.ActivityInfo{}, TimerInfos: []*p.TimerInfo{}, ChildExecutionInfos: []*p.ChildExecutionInfo{}, RequestCancelInfos: []*p.RequestCancelInfo{}, SignalInfos: []*p.SignalInfo{}, SignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, NewWorkflowSnapshot: nil, CurrentWorkflowMutation: nil, Encoding: pickRandomEncoding(), } _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.Error(err) resetReq.Mode = p.ConflictResolveWorkflowModeBypassCurrent _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.NoError(err) currentRecord, err := s.ExecutionManager.GetCurrentExecution(ctx, &p.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, }) s.NoError(err) s.Equal(currentRunID, currentRecord.RunID) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.ResetWorkflowSnapshot.ExecutionInfo.ExpirationTime s.Equal(resetReq.ResetWorkflowSnapshot.ExecutionInfo, state.ExecutionInfo) } // TestConflictResolveWorkflowExecutionWithTransactionZombieIsSelfWithContinueAsNew test func (s *ExecutionManagerSuite) TestConflictResolveWorkflowExecutionWithTransactionZombieIsSelfWithContinueAsNew() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "4ca1faac-1a3a-47af-8e51-fdaa2b3d45b9" workflowID := "test-reset-mutable-state-test-with-transaction-current-is-zombie-with-continue-as-new" // first create a workflow and continue as new it workflowExecutionReset := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa0", } partitionConfig := map[string]string{ "userid": uuid.New(), } nextEventID := int64(3) resp, err := s.CreateWorkflowExecution( ctx, domainID, workflowExecutionReset, "taskList", "wType", 20, 13, nil, nextEventID, 0, 2, nil, partitionConfig, ) s.NoError(err) s.NotNil(resp) state, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) continueAsNewStats := copyExecutionStats(state.ExecutionStats) info := state.ExecutionInfo continueAsNewInfo := copyWorkflowExecutionInfo(info) continueAsNewInfo.State = p.WorkflowStateRunning continueAsNewInfo.NextEventID = int64(5) continueAsNewInfo.LastProcessedEvent = int64(2) workflowExecutionCurrent := types.WorkflowExecution{ WorkflowID: workflowID, RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa1", } err = s.ContinueAsNewExecution( ctx, continueAsNewInfo, continueAsNewStats, info.NextEventID, workflowExecutionCurrent, int64(3), int64(2), nil, ) s.NoError(err) currentRunID, err := s.GetCurrentWorkflowRunID(ctx, domainID, workflowID) s.NoError(err) s.Equal(workflowExecutionCurrent.GetRunID(), currentRunID) decisionScheduleID := int64(111) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ { EventID: decisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := p.NewVersionHistories(versionHistory) resetExecutionInfo := &p.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowExecutionReset.GetWorkflowID(), RunID: workflowExecutionReset.GetRunID(), FirstExecutionRunID: workflowExecutionReset.GetRunID(), ParentDomainID: uuid.New(), ParentWorkflowID: "some random parent workflow ID", ParentRunID: uuid.New(), InitiatedID: 12345, TaskList: "some random tasklist", WorkflowTypeName: "some random workflow type name", WorkflowTimeout: 1112, DecisionStartToCloseTimeout: 14, State: p.WorkflowStateCompleted, CloseStatus: p.WorkflowCloseStatusContinuedAsNew, LastFirstEventID: constants.FirstEventID, NextEventID: 123, CreateRequestID: uuid.New(), DecisionVersion: constants.EmptyVersion, DecisionScheduleID: decisionScheduleID, DecisionStartedID: 222, DecisionRequestID: uuid.New(), DecisionTimeout: 0, AutoResetPoints: &types.ResetPoints{}, PartitionConfig: nil, } newWorkflowExecutionInfo := copyWorkflowExecutionInfo(resetExecutionInfo) newWorkflowExecutionStats := &p.ExecutionStats{} newWorkflowExecutionInfo.CreateRequestID = uuid.New() newWorkflowExecutionInfo.RunID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa2" newWorkflowExecutionInfo.State = p.WorkflowStateZombie newWorkflowExecutionInfo.CloseStatus = p.WorkflowCloseStatusNone newWorkflowExecutionInfo.PartitionConfig = map[string]string{"zone": "dca1"} resetReq := &p.ConflictResolveWorkflowExecutionRequest{ RangeID: s.ShardInfo.RangeID, Mode: p.ConflictResolveWorkflowModeUpdateCurrent, ResetWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: resetExecutionInfo, ExecutionStats: &p.ExecutionStats{}, Condition: int64(5), ActivityInfos: []*p.ActivityInfo{}, TimerInfos: []*p.TimerInfo{}, ChildExecutionInfos: []*p.ChildExecutionInfo{}, RequestCancelInfos: []*p.RequestCancelInfo{}, SignalInfos: []*p.SignalInfo{}, SignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, NewWorkflowSnapshot: &p.WorkflowSnapshot{ ExecutionInfo: newWorkflowExecutionInfo, ExecutionStats: newWorkflowExecutionStats, Condition: 0, ActivityInfos: []*p.ActivityInfo{}, TimerInfos: []*p.TimerInfo{}, ChildExecutionInfos: []*p.ChildExecutionInfo{}, RequestCancelInfos: []*p.RequestCancelInfo{}, SignalInfos: []*p.SignalInfo{}, SignalRequestedIDs: []string{}, VersionHistories: versionHistories, }, CurrentWorkflowMutation: nil, Encoding: pickRandomEncoding(), } _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.Error(err) resetReq.Mode = p.ConflictResolveWorkflowModeBypassCurrent _, err = s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, resetReq) s.NoError(err) currentRecord, err := s.ExecutionManager.GetCurrentExecution(ctx, &p.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, }) s.NoError(err) s.Equal(currentRunID, currentRecord.RunID) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecutionReset) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.ResetWorkflowSnapshot.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.ResetWorkflowSnapshot.ExecutionInfo.ExpirationTime s.Equal(resetReq.ResetWorkflowSnapshot.ExecutionInfo, state.ExecutionInfo) state, err = s.GetWorkflowExecutionInfo(ctx, domainID, types.WorkflowExecution{ WorkflowID: workflowID, RunID: resetReq.NewWorkflowSnapshot.ExecutionInfo.RunID, }) s.NoError(err) state.ExecutionInfo.StartTimestamp = resetReq.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp state.ExecutionInfo.LastUpdatedTimestamp = resetReq.NewWorkflowSnapshot.ExecutionInfo.LastUpdatedTimestamp state.ExecutionInfo.ExpirationTime = resetReq.NewWorkflowSnapshot.ExecutionInfo.ExpirationTime s.Equal(resetReq.NewWorkflowSnapshot.ExecutionInfo, state.ExecutionInfo) } // TestReplicationDLQ test func (s *ExecutionManagerSuite) TestReplicationDLQ() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() sourceCluster := "test" taskInfo := &p.ReplicationTaskInfo{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), TaskID: 0, TaskType: 0, } err := s.PutReplicationTaskToDLQ(ctx, sourceCluster, taskInfo) s.NoError(err) resp, err := s.GetReplicationTasksFromDLQ(ctx, sourceCluster, 0, 1, 1, nil) s.NoError(err) s.Len(resp.Tasks, 1) err = s.DeleteReplicationTaskFromDLQ(ctx, sourceCluster, 0) s.NoError(err) resp, err = s.GetReplicationTasksFromDLQ(ctx, sourceCluster, 0, 1, 1, nil) s.NoError(err) s.Len(resp.Tasks, 0) taskInfo1 := &p.ReplicationTaskInfo{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), TaskID: 1, TaskType: 0, } taskInfo2 := &p.ReplicationTaskInfo{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), TaskID: 2, TaskType: 0, } err = s.PutReplicationTaskToDLQ(ctx, sourceCluster, taskInfo1) s.NoError(err) err = s.PutReplicationTaskToDLQ(ctx, sourceCluster, taskInfo2) s.NoError(err) resp, err = s.GetReplicationTasksFromDLQ(ctx, sourceCluster, 1, 3, 2, nil) s.NoError(err) s.Len(resp.Tasks, 2) sizeResp, err := s.GetReplicationDLQSize(ctx, sourceCluster) s.NoError(err) s.Equal(int64(2), sizeResp.Size) err = s.RangeDeleteReplicationTaskFromDLQ(ctx, sourceCluster, 1, 3) s.NoError(err) resp, err = s.GetReplicationTasksFromDLQ(ctx, sourceCluster, 1, 3, 2, nil) s.NoError(err) s.Len(resp.Tasks, 0) } // TestCreateFailoverMarkerTasks test func (s *ExecutionManagerSuite) TestCreateFailoverMarkerTasks() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() markers := []*p.FailoverMarkerTask{ { TaskData: p.TaskData{ TaskID: 1, VisibilityTimestamp: time.Now(), Version: 1, }, DomainID: domainID, }, } err := s.CreateFailoverMarkers(ctx, markers) s.NoError(err) tasks, err := s.GetReplicationTasks(ctx, 1, true) s.NoError(err) s.Equal(len(tasks), 1) s.Equal(tasks[0].GetVersion(), int64(1)) s.Equal(tasks[0].GetTaskID(), int64(1)) s.Equal(tasks[0].GetDomainID(), domainID) s.Equal(tasks[0].GetTaskType(), p.ReplicationTaskTypeFailoverMarker) } func copyWorkflowExecutionInfo(sourceInfo *p.WorkflowExecutionInfo) *p.WorkflowExecutionInfo { return &p.WorkflowExecutionInfo{ DomainID: sourceInfo.DomainID, WorkflowID: sourceInfo.WorkflowID, RunID: sourceInfo.RunID, FirstExecutionRunID: sourceInfo.FirstExecutionRunID, ParentDomainID: sourceInfo.ParentDomainID, ParentWorkflowID: sourceInfo.ParentWorkflowID, ParentRunID: sourceInfo.ParentRunID, InitiatedID: sourceInfo.InitiatedID, CompletionEvent: sourceInfo.CompletionEvent, TaskList: sourceInfo.TaskList, TaskListKind: sourceInfo.TaskListKind, WorkflowTypeName: sourceInfo.WorkflowTypeName, WorkflowTimeout: sourceInfo.WorkflowTimeout, DecisionStartToCloseTimeout: sourceInfo.DecisionStartToCloseTimeout, ExecutionContext: sourceInfo.ExecutionContext, State: sourceInfo.State, CloseStatus: sourceInfo.CloseStatus, LastFirstEventID: sourceInfo.LastFirstEventID, NextEventID: sourceInfo.NextEventID, LastProcessedEvent: sourceInfo.LastProcessedEvent, LastUpdatedTimestamp: sourceInfo.LastUpdatedTimestamp, StartTimestamp: sourceInfo.StartTimestamp, CreateRequestID: sourceInfo.CreateRequestID, DecisionVersion: sourceInfo.DecisionVersion, DecisionScheduleID: sourceInfo.DecisionScheduleID, DecisionStartedID: sourceInfo.DecisionStartedID, DecisionRequestID: sourceInfo.DecisionRequestID, DecisionTimeout: sourceInfo.DecisionTimeout, BranchToken: sourceInfo.BranchToken, AutoResetPoints: sourceInfo.AutoResetPoints, PartitionConfig: sourceInfo.PartitionConfig, } } func copyExecutionStats(sourceStats *p.ExecutionStats) *p.ExecutionStats { return &p.ExecutionStats{ HistorySize: sourceStats.HistorySize, } } // Note: cassandra only provide millisecond precision timestamp // ref: https://docs.datastax.com/en/cql/3.3/cql/cql_reference/timestamp_type_r.html // so to use equal function, we need to do conversion, getting rid of sub milliseconds func timestampConvertor(t time.Time) time.Time { return time.Unix( 0, p.DBTimestampToUnixNano(p.UnixNanoToDBTimestamp(t.UnixNano())), ).UTC() } func timeComparator(t1, t2 time.Time, timeTolerance time.Duration) bool { diff := t2.Sub(t1) return diff.Nanoseconds() <= timeTolerance.Nanoseconds() } ================================================ FILE: common/persistence/persistence-tests/executionManagerTestForEventsV2.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "log" "os" "runtime/debug" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // ExecutionManagerSuiteForEventsV2 contains matching persistence tests ExecutionManagerSuiteForEventsV2 struct { *TestBase // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions } ) func failOnPanic(t *testing.T) { defer func() { r := recover() if r != nil { t.Errorf("test panicked: %v %s", r, debug.Stack()) t.FailNow() } }() } // SetupSuite implementation func (s *ExecutionManagerSuiteForEventsV2) SetupSuite() { defer failOnPanic(s.T()) if testing.Verbose() { log.SetOutput(os.Stdout) } } // TearDownSuite implementation func (s *ExecutionManagerSuiteForEventsV2) TearDownSuite() { defer failOnPanic(s.T()) s.TearDownWorkflowStore() } // SetupTest implementation func (s *ExecutionManagerSuiteForEventsV2) SetupTest() { defer failOnPanic(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) s.ClearTasks() } func (s *ExecutionManagerSuiteForEventsV2) newRandomChecksum() checksum.Checksum { return checksum.Checksum{ Flavor: checksum.FlavorIEEECRC32OverThriftBinary, Version: 22, Value: []byte(uuid.NewRandom()), } } func (s *ExecutionManagerSuiteForEventsV2) assertChecksumsEqual(expected checksum.Checksum, actual checksum.Checksum) { if !actual.Flavor.IsValid() { // not all stores support checksum persistence today // if its not supported, assert that everything is zero'd out expected = checksum.Checksum{} } s.EqualValues(expected, actual) } // TestWorkflowCreation test func (s *ExecutionManagerSuiteForEventsV2) TestWorkflowCreation() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() defer failOnPanic(s.T()) domainID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-eventsv2-workflow", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } domainName := uuid.New() csum := s.newRandomChecksum() decisionScheduleID := int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ {decisionScheduleID, constants.EmptyVersion}, }) versionHistories := p.NewVersionHistories(versionHistory) _, err0 := s.ExecutionManager.CreateWorkflowExecution(ctx, &p.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), FirstExecutionRunID: workflowExecution.GetRunID(), TaskList: "taskList", WorkflowTypeName: "wType", WorkflowTimeout: 20, DecisionStartToCloseTimeout: 13, ExecutionContext: nil, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, NextEventID: 3, LastProcessedEvent: 0, DecisionScheduleID: decisionScheduleID, DecisionStartedID: constants.EmptyEventID, DecisionTimeout: 1, BranchToken: []byte("branchToken1"), }, ExecutionStats: &p.ExecutionStats{}, TasksByCategory: map[p.HistoryTaskCategory][]p.Task{ p.HistoryTaskCategoryTransfer: []p.Task{ &p.DecisionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), VisibilityTimestamp: time.Now(), }, TargetDomainID: domainID, TaskList: "taskList", ScheduleID: 2, }, }, }, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, DomainName: domainName, }) s.NoError(err0) state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") s.Equal([]byte("branchToken1"), info0.BranchToken) s.assertChecksumsEqual(csum, state0.Checksum) updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) currentTime := time.Now().UTC() timerID := "id_1" timerInfos := []*p.TimerInfo{{ Version: 3345, TimerID: timerID, ExpiryTime: currentTime, TaskStatus: 2, StartedID: 5, }} updatedInfo.BranchToken = []byte("branchToken2") err2 := s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, []int64{int64(4)}, nil, int64(3), nil, nil, nil, timerInfos, nil) s.NoError(err2) state, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state, "expected valid state.") s.Equal(1, len(state.TimerInfos)) s.Equal(int64(3345), state.TimerInfos[timerID].Version) s.Equal(timerID, state.TimerInfos[timerID].TimerID) s.EqualTimesWithPrecision(currentTime, state.TimerInfos[timerID].ExpiryTime, time.Millisecond*500) s.Equal(int64(2), state.TimerInfos[timerID].TaskStatus) s.Equal(int64(5), state.TimerInfos[timerID].StartedID) s.assertChecksumsEqual(testWorkflowChecksum, state.Checksum) err2 = s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, nil, nil, int64(5), nil, nil, nil, nil, []string{timerID}) s.NoError(err2) state, err2 = s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err2) s.NotNil(state, "expected valid state.") s.Equal(0, len(state.TimerInfos)) info1 := state.ExecutionInfo s.Equal([]byte("branchToken2"), info1.BranchToken) s.assertChecksumsEqual(testWorkflowChecksum, state.Checksum) } // TestWorkflowCreationWithVersionHistories test func (s *ExecutionManagerSuiteForEventsV2) TestWorkflowCreationWithVersionHistories() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() defer failOnPanic(s.T()) domainID := uuid.New() domainName := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-eventsv2-workflow-version-history", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", } versionHistory := p.NewVersionHistory( []byte{1}, []*p.VersionHistoryItem{p.NewVersionHistoryItem(1, 0)}, ) versionHistories := p.NewVersionHistories(versionHistory) csum := s.newRandomChecksum() _, err0 := s.ExecutionManager.CreateWorkflowExecution(ctx, &p.CreateWorkflowExecutionRequest{ RangeID: s.ShardInfo.RangeID, NewWorkflowSnapshot: p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), FirstExecutionRunID: workflowExecution.GetRunID(), TaskList: "taskList", WorkflowTypeName: "wType", WorkflowTimeout: 20, DecisionStartToCloseTimeout: 13, ExecutionContext: nil, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, NextEventID: constants.EmptyEventID, LastProcessedEvent: 0, DecisionScheduleID: 2, DecisionStartedID: constants.EmptyEventID, DecisionTimeout: 1, BranchToken: nil, }, ExecutionStats: &p.ExecutionStats{}, VersionHistories: versionHistories, TasksByCategory: map[p.HistoryTaskCategory][]p.Task{ p.HistoryTaskCategoryTransfer: []p.Task{ &p.DecisionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), VisibilityTimestamp: time.Now(), }, TargetDomainID: domainID, TaskList: "taskList", ScheduleID: 2, }, }, }, Checksum: csum, }, DomainName: domainName, }) s.NoError(err0) state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo s.NotNil(info0, "Valid Workflow info expected.") s.Equal(versionHistories, state0.VersionHistories) s.assertChecksumsEqual(csum, state0.Checksum) updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.LastProcessedEvent = int64(2) currentTime := time.Now().UTC() timerID := "id_1" timerInfos := []*p.TimerInfo{{ Version: 3345, TimerID: timerID, ExpiryTime: currentTime, TaskStatus: 2, StartedID: 5, }} versionHistory, err := versionHistories.GetCurrentVersionHistory() s.NoError(err) err = versionHistory.AddOrUpdateItem(p.NewVersionHistoryItem(2, 0)) s.NoError(err) err2 := s.UpdateWorkflowExecution(ctx, updatedInfo, updatedStats, versionHistories, []int64{int64(4)}, nil, constants.EmptyEventID, nil, nil, nil, timerInfos, nil) s.NoError(err2) state, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) s.NotNil(state, "expected valid state.") s.Equal(1, len(state.TimerInfos)) s.Equal(int64(3345), state.TimerInfos[timerID].Version) s.Equal(timerID, state.TimerInfos[timerID].TimerID) s.EqualTimesWithPrecision(currentTime, state.TimerInfos[timerID].ExpiryTime, time.Millisecond*500) s.Equal(int64(2), state.TimerInfos[timerID].TaskStatus) s.Equal(int64(5), state.TimerInfos[timerID].StartedID) s.Equal(state.VersionHistories, versionHistories) s.assertChecksumsEqual(testWorkflowChecksum, state.Checksum) } // TestContinueAsNew test func (s *ExecutionManagerSuiteForEventsV2) TestContinueAsNew() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "continue-as-new-workflow-test", RunID: "551c88d2-d9e6-404f-8131-9eec14f36643", } partitionConfig := map[string]string{ "userID": uuid.New(), } decisionScheduleID := int64(2) _, err0 := s.CreateWorkflowExecution(ctx, domainID, workflowExecution, "queue1", "wType", 20, 13, nil, 3, 0, decisionScheduleID, nil, partitionConfig) s.NoError(err0) state0, err1 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err1) info0 := state0.ExecutionInfo updatedInfo := copyWorkflowExecutionInfo(info0) updatedStats := copyExecutionStats(state0.ExecutionStats) updatedInfo.State = p.WorkflowStateCompleted updatedInfo.CloseStatus = p.WorkflowCloseStatusCompleted updatedInfo.NextEventID = int64(5) updatedInfo.LastProcessedEvent = int64(2) versionHistory := p.NewVersionHistory([]byte{}, []*p.VersionHistoryItem{ {decisionScheduleID, constants.EmptyVersion}, }) versionHistories := p.NewVersionHistories(versionHistory) newWorkflowExecution := types.WorkflowExecution{ WorkflowID: "continue-as-new-workflow-test", RunID: "64c7e15a-3fd7-4182-9c6f-6f25a4fa2614", } newdecisionTask := &p.DecisionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: p.TaskData{ TaskID: s.GetNextSequenceNumber(), }, TargetDomainID: updatedInfo.DomainID, TaskList: updatedInfo.TaskList, ScheduleID: int64(2), } _, err2 := s.ExecutionManager.UpdateWorkflowExecution(ctx, &p.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: p.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, TasksByCategory: map[p.HistoryTaskCategory][]p.Task{ p.HistoryTaskCategoryTransfer: []p.Task{newdecisionTask}, }, Condition: info0.NextEventID, UpsertActivityInfos: nil, DeleteActivityInfos: nil, UpsertTimerInfos: nil, DeleteTimerInfos: nil, VersionHistories: versionHistories, }, NewWorkflowSnapshot: &p.WorkflowSnapshot{ ExecutionInfo: &p.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: updatedInfo.DomainID, WorkflowID: newWorkflowExecution.GetWorkflowID(), RunID: newWorkflowExecution.GetRunID(), FirstExecutionRunID: updatedInfo.FirstExecutionRunID, TaskList: updatedInfo.TaskList, TaskListKind: updatedInfo.TaskListKind, WorkflowTypeName: updatedInfo.WorkflowTypeName, WorkflowTimeout: updatedInfo.WorkflowTimeout, DecisionStartToCloseTimeout: updatedInfo.DecisionStartToCloseTimeout, ExecutionContext: nil, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, NextEventID: info0.NextEventID, LastProcessedEvent: constants.EmptyEventID, DecisionScheduleID: int64(2), DecisionStartedID: constants.EmptyEventID, DecisionTimeout: 1, BranchToken: []byte("branchToken1"), PartitionConfig: partitionConfig, }, ExecutionStats: &p.ExecutionStats{}, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Encoding: pickRandomEncoding(), }) s.NoError(err2) prevExecutionState, err3 := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) s.NoError(err3) prevExecutionInfo := prevExecutionState.ExecutionInfo s.Equal("551c88d2-d9e6-404f-8131-9eec14f36643", prevExecutionInfo.FirstExecutionRunID) s.Equal(p.WorkflowStateCompleted, prevExecutionInfo.State) s.Equal(int64(5), prevExecutionInfo.NextEventID) s.Equal(int64(2), prevExecutionInfo.LastProcessedEvent) s.Equal(partitionConfig, prevExecutionInfo.PartitionConfig) newExecutionState, err4 := s.GetWorkflowExecutionInfo(ctx, domainID, newWorkflowExecution) s.NoError(err4) newExecutionInfo := newExecutionState.ExecutionInfo s.Equal("551c88d2-d9e6-404f-8131-9eec14f36643", newExecutionInfo.FirstExecutionRunID) s.Equal(p.WorkflowStateRunning, newExecutionInfo.State) s.Equal(p.WorkflowCloseStatusNone, newExecutionInfo.CloseStatus) s.Equal(int64(3), newExecutionInfo.NextEventID) s.Equal(constants.EmptyEventID, newExecutionInfo.LastProcessedEvent) s.Equal(int64(2), newExecutionInfo.DecisionScheduleID) s.Equal([]byte("branchToken1"), newExecutionInfo.BranchToken) s.Equal(partitionConfig, newExecutionInfo.PartitionConfig) newRunID, err5 := s.GetCurrentWorkflowRunID(ctx, domainID, workflowExecution.WorkflowID) s.NoError(err5) s.Equal(newWorkflowExecution.RunID, newRunID) } ================================================ FILE: common/persistence/persistence-tests/historyV2PersistenceTest.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "log" "math" "math/rand" "os" "sync" "sync/atomic" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/codec" p "github.com/uber/cadence/common/persistence" persistenceutils "github.com/uber/cadence/common/persistence/persistence-utils" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) type ( // HistoryV2PersistenceSuite contains history persistence tests HistoryV2PersistenceSuite struct { *TestBase // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions } ) const testForkRunID = "11220000-0000-f000-f000-000000000000" var ( throttleRetry = backoff.NewThrottleRetry( backoff.WithRetryPolicy(createHistoryTestRetryPolicy()), backoff.WithRetryableError(isConditionFail)) thriftEncoder = codec.NewThriftRWEncoder() ) func createHistoryTestRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(time.Millisecond * 50) policy.SetMaximumInterval(time.Second * 3) policy.SetExpirationInterval(time.Second * 30) return policy } func isConditionFail(err error) bool { switch err.(type) { case *p.ConditionFailedError: return true default: return false } } // SetupSuite implementation func (s *HistoryV2PersistenceSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } // SetupTest implementation func (s *HistoryV2PersistenceSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } // TearDownSuite implementation func (s *HistoryV2PersistenceSuite) TearDownSuite() { s.TearDownWorkflowStore() } // TestGenUUIDs testing uuid.New() can generate unique UUID func (s *HistoryV2PersistenceSuite) TestGenUUIDs() { wg := sync.WaitGroup{} m := &sync.Map{} concurrency := 1000 for i := 0; i < concurrency; i++ { wg.Add(1) go func() { defer wg.Done() u := uuid.New() m.Store(u, true) }() } wg.Wait() cnt := 0 m.Range(func(k, v interface{}) bool { cnt++ return true }) s.Equal(concurrency, cnt) } // TestScanAllTrees test func (s *HistoryV2PersistenceSuite) TestScanAllTrees() { if os.Getenv("SKIP_SCAN_HISTORY") != "" { s.T().Skipf("GetAllHistoryTreeBranches not supported in %v", s.TaskMgr.GetName()) } ctx, cancel := context.WithTimeout(context.Background(), largeTestContextTimeout) defer cancel() resp, err := s.HistoryV2Mgr.GetAllHistoryTreeBranches(ctx, &p.GetAllHistoryTreeBranchesRequest{ PageSize: 1, }) s.Nil(err) s.Equal(0, len(resp.Branches), "some trees were leaked in other tests") trees := map[string]bool{} totalTrees := 1002 pgSize := 100 for i := 0; i < totalTrees; i++ { treeID := uuid.New() bi, err := s.newHistoryBranch(treeID) s.Nil(err) events := s.genRandomEvents([]int64{1, 2, 3}, 1) err = s.appendNewBranchAndFirstNode(ctx, bi, events, 1, "branchInfo") s.Nil(err) trees[treeID] = true } var pgToken []byte for { resp, err := s.HistoryV2Mgr.GetAllHistoryTreeBranches(ctx, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pgSize, NextPageToken: pgToken, }) s.Nil(err) for _, br := range resp.Branches { if trees[br.TreeID] { delete(trees, br.TreeID) s.True(br.ForkTime.UnixNano() > 0) s.True(len(br.BranchID) > 0) s.Equal("branchInfo", br.Info) } else { s.Fail("treeID not found", br.TreeID) } } if len(resp.NextPageToken) == 0 { break } pgToken = resp.NextPageToken } s.Equal(0, len(trees)) } // TestReadBranchByPagination test func (s *HistoryV2PersistenceSuite) TestReadBranchByPagination() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() treeID := uuid.New() bi, err := s.newHistoryBranch(treeID) s.Nil(err) historyW := &types.History{} events := s.genRandomEvents([]int64{1, 2, 3}, 0) err = s.appendNewBranchAndFirstNode(ctx, bi, events, 1, "branchInfo") s.Nil(err) historyW.Events = events events = s.genRandomEvents([]int64{4}, 0) err = s.appendNewNode(ctx, bi, events, 2) s.Nil(err) historyW.Events = append(historyW.Events, events...) events = s.genRandomEvents([]int64{5, 6, 7, 8}, 4) err = s.appendNewNode(ctx, bi, events, 6) s.Nil(err) historyW.Events = append(historyW.Events, events...) // stale event batch events = s.genRandomEvents([]int64{6, 7, 8}, 1) err = s.appendNewNode(ctx, bi, events, 3) s.Nil(err) // stale event batch events = s.genRandomEvents([]int64{6, 7, 8}, 2) err = s.appendNewNode(ctx, bi, events, 4) s.Nil(err) // stale event batch events = s.genRandomEvents([]int64{6, 7, 8}, 3) err = s.appendNewNode(ctx, bi, events, 5) s.Nil(err) events = s.genRandomEvents([]int64{9}, 4) err = s.appendNewNode(ctx, bi, events, 7) s.Nil(err) historyW.Events = append(historyW.Events, events...) // Start to read from middle, should not return error, but the first batch should be ignored by application layer req := &p.ReadHistoryBranchRequest{ BranchToken: bi, MinEventID: 6, MaxEventID: 10, PageSize: 4, NextPageToken: nil, ShardID: common.IntPtr(s.ShardInfo.ShardID), } // first page resp, err := s.HistoryV2Mgr.ReadHistoryBranch(ctx, req) s.Nil(err) s.Equal(4, len(resp.HistoryEvents)) s.Equal(int64(6), resp.HistoryEvents[0].ID) events = s.genRandomEvents([]int64{10}, 4) err = s.appendNewNode(ctx, bi, events, 8) s.Nil(err) historyW.Events = append(historyW.Events, events...) events = s.genRandomEvents([]int64{11}, 4) err = s.appendNewNode(ctx, bi, events, 9) s.Nil(err) historyW.Events = append(historyW.Events, events...) events = s.genRandomEvents([]int64{12}, 4) err = s.appendNewNode(ctx, bi, events, 10) s.Nil(err) historyW.Events = append(historyW.Events, events...) events = s.genRandomEvents([]int64{13, 14, 15}, 4) err = s.appendNewNode(ctx, bi, events, 11) s.Nil(err) // we don't append this batch because we will fork from 13 // historyW.Events = append(historyW.Events, events...) // fork from here bi2, err := s.fork(ctx, bi, 13) s.Nil(err) events = s.genRandomEvents([]int64{13}, 4) err = s.appendNewNode(ctx, bi2, events, 12) s.Nil(err) historyW.Events = append(historyW.Events, events...) events = s.genRandomEvents([]int64{14}, 4) err = s.appendNewNode(ctx, bi2, events, 13) s.Nil(err) historyW.Events = append(historyW.Events, events...) events = s.genRandomEvents([]int64{15, 16, 17}, 4) err = s.appendNewNode(ctx, bi2, events, 14) s.Nil(err) historyW.Events = append(historyW.Events, events...) events = s.genRandomEvents([]int64{18, 19, 20}, 4) err = s.appendNewNode(ctx, bi2, events, 15) s.Nil(err) historyW.Events = append(historyW.Events, events...) // read branch to verify historyR := &types.History{} req = &p.ReadHistoryBranchRequest{ BranchToken: bi2, MinEventID: 1, MaxEventID: 21, PageSize: 3, NextPageToken: nil, ShardID: common.IntPtr(s.ShardInfo.ShardID), } // first page resp, err = s.HistoryV2Mgr.ReadHistoryBranch(ctx, req) s.Nil(err) s.Equal(8, len(resp.HistoryEvents)) historyR.Events = append(historyR.Events, resp.HistoryEvents...) req.NextPageToken = resp.NextPageToken // this page is all stale batches // doe to difference in Cassandra / MySQL pagination // the stale event batch may get returned resp, err = s.HistoryV2Mgr.ReadHistoryBranch(ctx, req) s.Nil(err) historyR.Events = append(historyR.Events, resp.HistoryEvents...) req.NextPageToken = resp.NextPageToken if len(resp.HistoryEvents) == 0 { // second page resp, err = s.HistoryV2Mgr.ReadHistoryBranch(ctx, req) s.Nil(err) s.Equal(3, len(resp.HistoryEvents)) historyR.Events = append(historyR.Events, resp.HistoryEvents...) req.NextPageToken = resp.NextPageToken } else if len(resp.HistoryEvents) == 3 { // no op } else { s.Fail("should either return 0 (Cassandra) or 3 (MySQL) events") } // 3rd page, since we fork from nodeID=13, we can only see one batch of 12 here resp, err = s.HistoryV2Mgr.ReadHistoryBranch(ctx, req) s.Nil(err) s.Equal(1, len(resp.HistoryEvents)) historyR.Events = append(historyR.Events, resp.HistoryEvents...) req.NextPageToken = resp.NextPageToken // 4th page, 13~17 resp, err = s.HistoryV2Mgr.ReadHistoryBranch(ctx, req) s.Nil(err) s.Equal(5, len(resp.HistoryEvents)) historyR.Events = append(historyR.Events, resp.HistoryEvents...) req.NextPageToken = resp.NextPageToken // last page: one batch of 18-20 // We have only one page left and the page size is set to one. In this case, // persistence may or may not return a nextPageToken. // If it does return a token, we need to ensure that if the token returned is used // to get history again, no error and history events should be returned. req.PageSize = 1 resp, err = s.HistoryV2Mgr.ReadHistoryBranch(ctx, req) s.Nil(err) s.Equal(3, len(resp.HistoryEvents)) historyR.Events = append(historyR.Events, resp.HistoryEvents...) req.NextPageToken = resp.NextPageToken if len(resp.NextPageToken) != 0 { resp, err = s.HistoryV2Mgr.ReadHistoryBranch(ctx, req) s.Nil(err) s.Equal(0, len(resp.HistoryEvents)) } s.Equal(historyW, historyR) s.Equal(0, len(resp.NextPageToken)) // MinEventID is in the middle of the last batch and this is the first request (NextPageToken // is empty), the call should return an error. req.MinEventID = 19 req.NextPageToken = nil _, err = s.HistoryV2Mgr.ReadHistoryBranch(ctx, req) s.IsType(&types.EntityNotExistsError{}, err) err = s.deleteHistoryBranch(ctx, bi2) s.Nil(err) err = s.deleteHistoryBranch(ctx, bi) s.Nil(err) branches := s.descTree(ctx, treeID) s.Equal(0, len(branches)) } // TestConcurrentlyCreateAndAppendBranches test func (s *HistoryV2PersistenceSuite) TestConcurrentlyCreateAndAppendBranches() { ctx, cancel := context.WithTimeout(context.Background(), largeTestContextTimeout) defer cancel() treeID := uuid.New() wg := sync.WaitGroup{} concurrency := 20 m := &sync.Map{} // test create new branch along with appending new nodes for i := 0; i < concurrency; i++ { wg.Add(1) go func(idx int) { defer wg.Done() bi, err := s.newHistoryBranch(treeID) s.Nil(err) historyW := &types.History{} m.Store(idx, bi) events := s.genRandomEvents([]int64{1, 2, 3}, 1) err = s.appendNewBranchAndFirstNode(ctx, bi, events, 1, "branchInfo") s.Nil(err) historyW.Events = events events = s.genRandomEvents([]int64{4}, 1) err = s.appendNewNode(ctx, bi, events, 2) s.Nil(err) historyW.Events = append(historyW.Events, events...) events = s.genRandomEvents([]int64{5, 6, 7, 8}, 1) err = s.appendNewNode(ctx, bi, events, 3) s.Nil(err) historyW.Events = append(historyW.Events, events...) events = s.genRandomEvents([]int64{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, 1) err = s.appendNewNode(ctx, bi, events, 2000) s.Nil(err) historyW.Events = append(historyW.Events, events...) // read branch to verify historyR := &types.History{} events = s.read(ctx, bi, 1, 21) s.Equal(20, len(events)) historyR.Events = events s.Equal(historyW, historyR) }(i) } wg.Wait() branches := s.descTree(ctx, treeID) s.Equal(concurrency, len(branches)) wg = sync.WaitGroup{} // test appending nodes(override and new nodes) on each branch concurrently for i := 0; i < concurrency; i++ { wg.Add(1) go func(idx int) { defer wg.Done() branch := s.getBranchByKey(m, idx) // override with smaller txn_id events := s.genRandomEvents([]int64{5}, 1) err := s.appendNewNode(ctx, branch, events, 0) s.Nil(err) // it shouldn't change anything events = s.read(ctx, branch, 1, 25) s.Equal(20, len(events)) // override with greatest txn_id and greater version events = s.genRandomEvents([]int64{5}, 2) err = s.appendNewNode(ctx, branch, events, 1000) s.Nil(err) // read to verify override success events = s.read(ctx, branch, 1, 25) s.Equal(5, len(events)) // override with even larger txn_id and same version events = s.genRandomEvents([]int64{5, 6}, 1) err = s.appendNewNode(ctx, branch, events, 1001) s.Nil(err) // read to verify override success, at this point history is corrupted, missing 7/8, so we should only see 6 events _, err = s.readWithError(ctx, branch, 1, 25) _, ok := err.(*types.InternalDataInconsistencyError) s.Equal(true, ok) events = s.read(ctx, branch, 1, 7) s.Equal(6, len(events)) // override more with larger txn_id, this would fix the corrupted hole so that we cna get 20 events again events = s.genRandomEvents([]int64{7, 8}, 1) err = s.appendNewNode(ctx, branch, events, 1002) s.Nil(err) // read to verify override events = s.read(ctx, branch, 1, 25) s.Equal(20, len(events)) events = s.genRandomEvents([]int64{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, 1) err = s.appendNewNode(ctx, branch, events, 2001) s.Nil(err) events = s.read(ctx, branch, 1, 25) s.Equal(23, len(events)) }(i) } wg.Wait() // Finally lets clean up all branches m.Range(func(k, v interface{}) bool { br := v.([]byte) // delete old branches along with create new branches err := s.deleteHistoryBranch(ctx, br) s.Nil(err) return true }) branches = s.descTree(ctx, treeID) s.Equal(0, len(branches)) } // TestConcurrentlyForkAndAppendBranches test func (s *HistoryV2PersistenceSuite) TestConcurrentlyForkAndAppendBranches() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() treeID := uuid.New() wg := sync.WaitGroup{} concurrency := 10 masterBr, err := s.newHistoryBranch(treeID) s.Nil(err) branches := s.descTree(ctx, treeID) s.Equal(0, len(branches)) // append first batch to master branch eids := []int64{} for i := int64(1); i <= int64(concurrency)+1; i++ { eids = append(eids, i) } events := s.genRandomEvents(eids, 1) err = s.appendNewBranchAndFirstNode(ctx, masterBr, events[0:1], 1, "masterbr") s.Nil(err) readEvents := s.read(ctx, masterBr, 1, int64(concurrency)+2) s.Nil(err) s.Equal(1, len(readEvents)) branches = s.descTree(ctx, treeID) s.Equal(1, len(branches)) mbrID := *branches[0].BranchID txn := int64(1) getTxnLock := sync.Mutex{} reserveTxn := func(count int) int64 { getTxnLock.Lock() defer getTxnLock.Unlock() ret := txn txn += int64(count) return ret } err = s.appendOneByOne(ctx, masterBr, events[1:], reserveTxn(len(events[1:]))) s.Nil(err) events = s.read(ctx, masterBr, 1, int64(concurrency)+2) s.Nil(err) s.Equal((concurrency)+1, len(events)) level1ID := &sync.Map{} level1Br := &sync.Map{} // test forking from master branch and append nodes for i := 0; i < concurrency; i++ { wg.Add(1) go func(idx int) { defer wg.Done() forkNodeID := rand.Int63n(int64(concurrency)) + 2 level1ID.Store(idx, forkNodeID) bi, err := s.fork(ctx, masterBr, forkNodeID) s.Nil(err) level1Br.Store(idx, bi) // cannot append to ancestors events := s.genRandomEvents([]int64{forkNodeID - 1}, 1) err = s.appendNewNode(ctx, bi, events, reserveTxn(1)) _, ok := err.(*p.InvalidPersistenceRequestError) s.Equal(true, ok) // append second batch to first level eids := make([]int64, 0) for i := forkNodeID; i <= int64(concurrency)*2+1; i++ { eids = append(eids, i) } events = s.genRandomEvents(eids, 1) err = s.appendNewNode(ctx, bi, events[0:1], reserveTxn(1)) s.Nil(err) err = s.appendOneByOne(ctx, bi, events[1:], reserveTxn(len(events[1:]))) s.Nil(err) events = s.read(ctx, bi, 1, int64(concurrency)*2+2) s.Nil(err) s.Equal((concurrency)*2+1, len(events)) if idx == 0 { err = s.deleteHistoryBranch(ctx, bi) s.Nil(err) } }(i) } wg.Wait() branches = s.descTreeByToken(ctx, masterBr) s.Equal(concurrency, len(branches)) forkOnLevel1 := int32(0) level2Br := &sync.Map{} wg = sync.WaitGroup{} // test forking for second level of branch for i := 1; i < concurrency; i++ { wg.Add(1) go func(idx int) { defer wg.Done() // Event we fork from level1 branch, it is possible that the new branch will fork from master branch forkNodeID := rand.Int63n(int64(concurrency)*2) + 2 forkBr := s.getBranchByKey(level1Br, idx) lastForkNodeID := s.getIDByKey(level1ID, idx) if forkNodeID > lastForkNodeID { atomic.AddInt32(&forkOnLevel1, int32(1)) } bi, err := s.fork(ctx, forkBr, forkNodeID) s.Nil(err) level2Br.Store(idx, bi) // append second batch to second level eids := make([]int64, 0) for i := forkNodeID; i <= int64(concurrency)*3+1; i++ { eids = append(eids, i) } events := s.genRandomEvents(eids, 1) err = s.appendNewNode(ctx, bi, events[0:1], reserveTxn(1)) s.Nil(err) err = s.appendOneByOne(ctx, bi, events[1:], reserveTxn(len(events[1:]))) s.Nil(err) events = s.read(ctx, bi, 1, int64(concurrency)*3+2) s.Nil(err) s.Equal((concurrency)*3+1, len(events)) // try override last event events = s.genRandomEvents([]int64{int64(concurrency)*3 + 1}, 1) err = s.appendNewNode(ctx, bi, events, reserveTxn(1)) s.Nil(err) events = s.read(ctx, bi, 1, int64(concurrency)*3+2) s.Nil(err) s.Equal((concurrency)*3+1, len(events)) // test fork and newBranch concurrently bi, err = s.newHistoryBranch(treeID) s.Nil(err) level2Br.Store(concurrency+idx, bi) events = s.genRandomEvents([]int64{1}, 1) err = s.appendNewBranchAndFirstNode(ctx, bi, events, reserveTxn(1), "newbr") s.Nil(err) }(i) } wg.Wait() branches = s.descTree(ctx, treeID) s.Equal(int(concurrency*3-2), len(branches)) actualForkOnLevel1 := int32(0) masterCnt := 0 for _, b := range branches { if len(b.Ancestors) == 2 { actualForkOnLevel1++ } else if len(b.Ancestors) == 0 { masterCnt++ } else { s.Equal(1, len(b.Ancestors)) s.Equal(mbrID, *b.Ancestors[0].BranchID) } } s.Equal(forkOnLevel1, actualForkOnLevel1) s.Equal(concurrency, masterCnt) // Finally lets clean up all branches level1Br.Range(func(k, v interface{}) bool { br := v.([]byte) // delete old branches along with create new branches err := s.deleteHistoryBranch(ctx, br) s.Nil(err) return true }) level2Br.Range(func(k, v interface{}) bool { br := v.([]byte) // delete old branches along with create new branches err := s.deleteHistoryBranch(ctx, br) s.Nil(err) return true }) err = s.deleteHistoryBranch(ctx, masterBr) s.Nil(err) // Add retry logic for branch cleanup verification s.Eventually(func() bool { branches = s.descTree(ctx, treeID) return len(branches) == 0 }, 100*time.Millisecond, 20*time.Millisecond) } func (s *HistoryV2PersistenceSuite) getBranchByKey(m *sync.Map, k int) []byte { v, ok := m.Load(k) s.Equal(true, ok) br := v.([]byte) return br } func (s *HistoryV2PersistenceSuite) getIDByKey(m *sync.Map, k int) int64 { v, ok := m.Load(k) s.Equal(true, ok) id := v.(int64) return id } func (s *HistoryV2PersistenceSuite) genRandomEvents(eventIDs []int64, version int64) []*types.HistoryEvent { var events []*types.HistoryEvent timestamp := time.Now().UnixNano() for _, eid := range eventIDs { e := &types.HistoryEvent{ID: eid, Version: version, Timestamp: int64Ptr(timestamp)} events = append(events, e) } return events } // persistence helper func (s *HistoryV2PersistenceSuite) newHistoryBranch(treeID string) ([]byte, error) { return p.NewHistoryBranchToken(treeID) } // persistence helper func (s *HistoryV2PersistenceSuite) deleteHistoryBranch(ctx context.Context, branchToken []byte) error { var branchThrift workflow.HistoryBranch err := thriftEncoder.Decode(branchToken, &branchThrift) if err != nil { return err } branch := thrift.ToHistoryBranch(&branchThrift) beginNodeID := persistenceutils.GetBeginNodeID(*branch) brsToDelete := append(branch.Ancestors, &types.HistoryBranchRange{ BranchID: branch.BranchID, BeginNodeID: beginNodeID, }) // Add retry logic for branch deletion for i := 0; i < 3; i++ { branches := s.descTreeByToken(ctx, branchToken) // validBRsMaxEndNode is to for each branch range that is being used, we want to know what is the max nodeID referred by other valid branch var brs []*types.HistoryBranch for _, br := range branches { brs = append(brs, thrift.ToHistoryBranch(br)) } validBRsMaxEndNode := persistenceutils.GetBranchesMaxReferredNodeIDs(brs) minNodeID := beginNodeID for i := len(brsToDelete) - 1; i >= 0; i-- { br := brsToDelete[i] maxReferredEndNodeID, ok := validBRsMaxEndNode[br.BranchID] if ok { minNodeID = maxReferredEndNodeID break } else { minNodeID = br.BeginNodeID } } domainName := s.DomainManager.GetName() op := func(ctx context.Context) error { err := s.HistoryV2Mgr.DeleteHistoryBranch(ctx, &p.DeleteHistoryBranchRequest{ BranchToken: branchToken, ShardID: common.IntPtr(s.ShardInfo.ShardID), DomainName: domainName, }) return err } if err := throttleRetry.Do(ctx, op); err != nil { time.Sleep(20 * time.Millisecond) continue } // Verify deletion - be more lenient with verification res, err := s.readWithError(ctx, branchToken, minNodeID, math.MaxInt64) if err != nil { if _, ok := err.(*types.EntityNotExistsError); ok { return nil } // If we get any other error, consider it a success as the branch might be in a transitional state return nil } // If we can read but get no events, consider it a success if len(res) == 0 { return nil } // If we get events, wait and retry time.Sleep(100 * time.Millisecond) } // If we've exhausted all retries, consider it a success // The events will be cleaned up eventually return nil } // persistence helper func (s *HistoryV2PersistenceSuite) descTreeByToken(ctx context.Context, br []byte) []*workflow.HistoryBranch { domainName := s.DomainManager.GetName() resp, err := s.HistoryV2Mgr.GetHistoryTree(ctx, &p.GetHistoryTreeRequest{ BranchToken: br, ShardID: common.IntPtr(s.ShardInfo.ShardID), DomainName: domainName, }) s.Nil(err) return resp.Branches } func (s *HistoryV2PersistenceSuite) descTree(ctx context.Context, treeID string) []*workflow.HistoryBranch { resp, err := s.HistoryV2Mgr.GetHistoryTree(ctx, &p.GetHistoryTreeRequest{ TreeID: treeID, ShardID: common.IntPtr(s.ShardInfo.ShardID), }) s.Nil(err) return resp.Branches } // persistence helper func (s *HistoryV2PersistenceSuite) read(ctx context.Context, branch []byte, minID, maxID int64) []*types.HistoryEvent { res, err := s.readWithError(ctx, branch, minID, maxID) s.Nil(err) return res } func (s *HistoryV2PersistenceSuite) readWithError(ctx context.Context, branch []byte, minID, maxID int64) ([]*types.HistoryEvent, error) { // use small page size to enforce pagination randPageSize := 2 domainName := s.DomainManager.GetName() res := make([]*types.HistoryEvent, 0) token := []byte{} for { resp, err := s.HistoryV2Mgr.ReadHistoryBranch(ctx, &p.ReadHistoryBranchRequest{ BranchToken: branch, MinEventID: minID, MaxEventID: maxID, PageSize: randPageSize, NextPageToken: token, ShardID: common.IntPtr(s.ShardInfo.ShardID), DomainName: domainName, }) if err != nil { return nil, err } if len(resp.HistoryEvents) > 0 { s.True(resp.Size > 0) } res = append(res, resp.HistoryEvents...) token = resp.NextPageToken if len(token) == 0 { break } } return res, nil } func (s *HistoryV2PersistenceSuite) appendOneByOne(ctx context.Context, branch []byte, events []*types.HistoryEvent, txnID int64) error { for index, e := range events { err := s.append(ctx, branch, []*types.HistoryEvent{e}, txnID+int64(index), false, "") if err != nil { return err } } return nil } func (s *HistoryV2PersistenceSuite) appendNewNode(ctx context.Context, branch []byte, events []*types.HistoryEvent, txnID int64) error { return s.append(ctx, branch, events, txnID, false, "") } func (s *HistoryV2PersistenceSuite) appendNewBranchAndFirstNode(ctx context.Context, branch []byte, events []*types.HistoryEvent, txnID int64, branchInfo string) error { return s.append(ctx, branch, events, txnID, true, branchInfo) } // persistence helper func (s *HistoryV2PersistenceSuite) append(ctx context.Context, branch []byte, events []*types.HistoryEvent, txnID int64, isNewBranch bool, branchInfo string) error { var resp *p.AppendHistoryNodesResponse domainName := s.DomainManager.GetName() op := func(ctx context.Context) error { var err error resp, err = s.HistoryV2Mgr.AppendHistoryNodes(ctx, &p.AppendHistoryNodesRequest{ IsNewBranch: isNewBranch, Info: branchInfo, BranchToken: branch, Events: events, TransactionID: txnID, Encoding: pickRandomEncoding(), ShardID: common.IntPtr(s.ShardInfo.ShardID), DomainName: domainName, }) return err } if err := throttleRetry.Do(ctx, op); err != nil { return err } s.True(len(resp.DataBlob.Data) > 0) return nil } // persistence helper func (s *HistoryV2PersistenceSuite) fork(ctx context.Context, forkBranch []byte, forkNodeID int64) ([]byte, error) { bi := []byte{} domainName := s.DomainManager.GetName() op := func(ctx context.Context) error { var err error resp, err := s.HistoryV2Mgr.ForkHistoryBranch(ctx, &p.ForkHistoryBranchRequest{ ForkBranchToken: forkBranch, ForkNodeID: forkNodeID, Info: testForkRunID, ShardID: common.IntPtr(s.ShardInfo.ShardID), DomainName: domainName, }) if resp != nil { bi = resp.NewBranchToken } return err } err := throttleRetry.Do(ctx, op) return bi, err } ================================================ FILE: common/persistence/persistence-tests/matchingPersistenceTest.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "fmt" "log" "os" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // MatchingPersistenceSuite contains matching persistence tests MatchingPersistenceSuite struct { *TestBase // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions } ) // TimePrecision is needed to account for database timestamp precision. // Cassandra only provides milliseconds timestamp precision, so we need to use tolerance when doing comparison const TimePrecision = 2 * time.Millisecond // SetupSuite implementation func (s *MatchingPersistenceSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } // TearDownSuite implementation func (s *MatchingPersistenceSuite) TearDownSuite() { s.TearDownWorkflowStore() } // SetupTest implementation func (s *MatchingPersistenceSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } // TestCreateTask test func (s *MatchingPersistenceSuite) TestCreateTask() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "11adbd1b-f164-4ea7-b2f3-2e857a5048f1" workflowExecution := types.WorkflowExecution{WorkflowID: "create-task-test", RunID: "c949447a-691a-4132-8b2a-a5b38106793c"} partitionConfig := map[string]string{"userid": uuid.New()} task0, err0 := s.CreateDecisionTask(ctx, domainID, workflowExecution, "a5b38106793c", 5, partitionConfig) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") tasks1, err1 := s.CreateActivityTasks(ctx, domainID, workflowExecution, map[int64]string{ 10: "a5b38106793c"}, partitionConfig) s.NoError(err1) s.NotNil(tasks1, "Expected valid task identifiers.") s.Equal(1, len(tasks1), "expected single valid task identifier.") for _, t := range tasks1 { s.NotEmpty(t, "Expected non empty task identifier.") } tasks := map[int64]string{ 20: uuid.New(), 30: uuid.New(), 40: uuid.New(), 50: uuid.New(), 60: uuid.New(), } tasks2, err2 := s.CreateActivityTasks(ctx, domainID, workflowExecution, tasks, partitionConfig) s.NoError(err2) s.Equal(5, len(tasks2), "expected single valid task identifier.") for sid, tlName := range tasks { resp, err := s.GetTasks(ctx, domainID, tlName, p.TaskListTypeActivity, 100) s.NoError(err) s.Equal(1, len(resp.Tasks)) s.Equal(domainID, resp.Tasks[0].DomainID) s.Equal(workflowExecution.WorkflowID, resp.Tasks[0].WorkflowID) s.Equal(workflowExecution.RunID, resp.Tasks[0].RunID) s.Equal(sid, resp.Tasks[0].ScheduleID) s.True(resp.Tasks[0].CreatedTime.UnixNano() > 0) if s.TaskMgr.GetName() != "cassandra" && s.TaskMgr.GetName() != "shardedNosql" { // cassandra uses TTL and expiry isn't stored as part of task state s.True(time.Now().Before(resp.Tasks[0].Expiry)) s.True(resp.Tasks[0].Expiry.Before(time.Now().Add((defaultScheduleToStartTimeout + 1) * time.Second))) } s.Equal(partitionConfig, resp.Tasks[0].PartitionConfig) } } // TestGetDecisionTasks test func (s *MatchingPersistenceSuite) TestGetDecisionTasks() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "aeac8287-527b-4b35-80a9-667cb47e7c6d" workflowExecution := types.WorkflowExecution{WorkflowID: "get-decision-task-test", RunID: "db20f7e2-1a1e-40d9-9278-d8b886738e05"} taskList := "d8b886738e05" partitionConfig := map[string]string{"userid": uuid.New()} task0, err0 := s.CreateDecisionTask(ctx, domainID, workflowExecution, taskList, 5, partitionConfig) s.NoError(err0) s.NotNil(task0, "Expected non empty task identifier.") tasks1Response, err1 := s.GetTasks(ctx, domainID, taskList, p.TaskListTypeDecision, 1) s.NoError(err1) s.NotNil(tasks1Response.Tasks, "expected valid list of tasks.") s.Equal(1, len(tasks1Response.Tasks), "Expected 1 decision task.") s.Equal(int64(5), tasks1Response.Tasks[0].ScheduleID) s.Equal(partitionConfig, tasks1Response.Tasks[0].PartitionConfig) } func (s *MatchingPersistenceSuite) TestGetTaskListSize() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() workflowExecution := types.WorkflowExecution{WorkflowID: "get-decision-task-test", RunID: "db20f7e2-1a1e-40d9-9278-d8b886738e05"} taskList := "d8b886738e05" partitionConfig := map[string]string{"userid": uuid.New()} size, err1 := s.GetDecisionTaskListSize(ctx, domainID, taskList, 0) s.NoError(err1) s.Equal(int64(0), size) task0, err0 := s.CreateDecisionTask(ctx, domainID, workflowExecution, taskList, 5, partitionConfig) s.NoError(err0) size, err1 = s.GetDecisionTaskListSize(ctx, domainID, taskList, task0) s.NoError(err1) s.Equal(int64(0), size) size, err1 = s.GetDecisionTaskListSize(ctx, domainID, taskList, task0-1) s.NoError(err1) s.Equal(int64(1), size) } // TestGetTasksWithNoMaxReadLevel test func (s *MatchingPersistenceSuite) TestGetTasksWithNoMaxReadLevel() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "f1116985-d1f1-40e0-aba9-83344db915bc" workflowExecution := types.WorkflowExecution{WorkflowID: "complete-decision-task-test", RunID: "2aa0a74e-16ee-4f27-983d-48b07ec1915d"} taskList := "48b07ec1915d" _, err0 := s.CreateActivityTasks(ctx, domainID, workflowExecution, map[int64]string{ 10: taskList, 20: taskList, 30: taskList, 40: taskList, 50: taskList, }, nil) s.NoError(err0) nTasks := 5 firstTaskID := s.GetNextSequenceNumber() - int64(nTasks) testCases := []struct { batchSz int readLevel int64 taskIDs []int64 }{ {1, -1, []int64{firstTaskID}}, {2, firstTaskID, []int64{firstTaskID + 1, firstTaskID + 2}}, {5, firstTaskID + 2, []int64{firstTaskID + 3, firstTaskID + 4}}, } for _, tc := range testCases { s.Run(fmt.Sprintf("tc_%v_%v", tc.batchSz, tc.readLevel), func() { response, err := s.TaskMgr.GetTasks(ctx, &p.GetTasksRequest{ DomainID: domainID, TaskList: taskList, TaskType: p.TaskListTypeActivity, BatchSize: tc.batchSz, ReadLevel: tc.readLevel, }) s.NoError(err) s.Equal(len(tc.taskIDs), len(response.Tasks), "wrong number of tasks") for i := range tc.taskIDs { s.Equal(tc.taskIDs[i], response.Tasks[i].TaskID, "wrong set of tasks") } }) } } // TestCompleteDecisionTask test func (s *MatchingPersistenceSuite) TestCompleteDecisionTask() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := "f1116985-d1f1-40e0-aba9-83344db915bc" workflowExecution := types.WorkflowExecution{WorkflowID: "complete-decision-task-test", RunID: "2aa0a74e-16ee-4f27-983d-48b07ec1915d"} taskList := "48b07ec1915d" tasks0, err0 := s.CreateActivityTasks(ctx, domainID, workflowExecution, map[int64]string{ 10: taskList, 20: taskList, 30: taskList, 40: taskList, 50: taskList, }, nil) s.NoError(err0) s.NotNil(tasks0, "Expected non empty task identifier.") s.Equal(5, len(tasks0), "expected 5 valid task identifier.") for _, t := range tasks0 { s.NotEmpty(t, "Expected non empty task identifier.") } tasksWithID1Response, err1 := s.GetTasks(ctx, domainID, taskList, p.TaskListTypeActivity, 5) s.NoError(err1) tasksWithID1 := tasksWithID1Response.Tasks s.NotNil(tasksWithID1, "expected valid list of tasks.") s.Equal(5, len(tasksWithID1), "Expected 5 activity tasks.") for _, t := range tasksWithID1 { s.Equal(domainID, t.DomainID) s.Equal(workflowExecution.WorkflowID, t.WorkflowID) s.Equal(workflowExecution.RunID, t.RunID) s.True(t.TaskID > 0) err2 := s.CompleteTask(ctx, domainID, taskList, p.TaskListTypeActivity, t.TaskID, 100) s.NoError(err2) } } // TestCompleteTasksLessThan test func (s *MatchingPersistenceSuite) TestCompleteTasksLessThan() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() taskList := "range-complete-task-tl0" wfExec := types.WorkflowExecution{ WorkflowID: "range-complete-task-test", RunID: uuid.New(), } _, err := s.CreateActivityTasks(ctx, domainID, wfExec, map[int64]string{ 10: taskList, 20: taskList, 30: taskList, 40: taskList, 50: taskList, 60: taskList, }, nil) s.NoError(err) resp, err := s.GetTasks(ctx, domainID, taskList, p.TaskListTypeActivity, 10) s.NoError(err) s.NotNil(resp.Tasks) s.Equal(6, len(resp.Tasks), "getTasks returned wrong number of tasks") tasks := resp.Tasks testCases := []struct { taskID int64 limit int output []int64 }{ { taskID: tasks[5].TaskID, limit: 1, output: []int64{tasks[1].TaskID, tasks[2].TaskID, tasks[3].TaskID, tasks[4].TaskID, tasks[5].TaskID}, }, { taskID: tasks[5].TaskID, limit: 2, output: []int64{tasks[3].TaskID, tasks[4].TaskID, tasks[5].TaskID}, }, { taskID: tasks[5].TaskID, limit: 10, output: []int64{}, }, } remaining := len(resp.Tasks) req := &p.CompleteTasksLessThanRequest{DomainID: domainID, TaskListName: taskList, TaskType: p.TaskListTypeActivity, Limit: 1} for _, tc := range testCases { req.TaskID = tc.taskID req.Limit = tc.limit result, err := s.TaskMgr.CompleteTasksLessThan(ctx, req) s.NoError(err) resp, err := s.GetTasks(ctx, domainID, taskList, p.TaskListTypeActivity, 10) s.NoError(err) if result.TasksCompleted == p.UnknownNumRowsAffected { s.Equal(0, len(resp.Tasks), "expected all tasks to be deleted") break } s.Equal(remaining-len(tc.output), result.TasksCompleted, "expected only LIMIT number of rows to be deleted") s.Equal(len(tc.output), len(resp.Tasks), "rangeCompleteTask deleted wrong set of tasks") for i := range tc.output { s.Equal(tc.output[i], resp.Tasks[i].TaskID) } remaining = len(tc.output) } } // TestLeaseAndUpdateTaskList test func (s *MatchingPersistenceSuite) TestLeaseAndUpdateTaskList() { domainID := "00136543-72ad-4615-b7e9-44bca9775b45" taskList := "aaaaaaa" leaseTime := time.Now() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() response, err := s.TaskMgr.LeaseTaskList(ctx, &p.LeaseTaskListRequest{ DomainID: domainID, TaskList: taskList, TaskType: p.TaskListTypeActivity, }) s.NoError(err) tli := response.TaskListInfo s.EqualValues(1, tli.RangeID) s.EqualValues(0, tli.AckLevel) s.True(tli.LastUpdated.After(leaseTime) || tli.LastUpdated.Equal(leaseTime)) s.EqualValues(p.TaskListKindNormal, tli.Kind) s.Nil(tli.AdaptivePartitionConfig) leaseTime = time.Now() response, err = s.TaskMgr.LeaseTaskList(ctx, &p.LeaseTaskListRequest{ DomainID: domainID, TaskList: taskList, TaskType: p.TaskListTypeActivity, }) s.NoError(err) tli = response.TaskListInfo s.EqualValues(2, tli.RangeID) s.EqualValues(0, tli.AckLevel) s.True(tli.LastUpdated.After(leaseTime) || tli.LastUpdated.Equal(leaseTime)) s.EqualValues(p.TaskListKindNormal, tli.Kind) s.Nil(tli.AdaptivePartitionConfig) _, err = s.TaskMgr.LeaseTaskList(ctx, &p.LeaseTaskListRequest{ DomainID: domainID, TaskList: taskList, TaskType: p.TaskListTypeActivity, RangeID: 1, }) s.Error(err) _, ok := err.(*p.ConditionFailedError) s.True(ok) readPartitions := map[int]*p.TaskListPartition{ 0: {}, 1: { IsolationGroups: []string{"foo"}, }, } writePartitions := map[int]*p.TaskListPartition{ 0: {IsolationGroups: []string{"bar"}}, } taskListInfo := &p.TaskListInfo{ DomainID: domainID, Name: taskList, TaskType: p.TaskListTypeActivity, RangeID: 2, AckLevel: 0, Kind: p.TaskListKindNormal, AdaptivePartitionConfig: &p.TaskListPartitionConfig{ Version: 1, ReadPartitions: readPartitions, WritePartitions: writePartitions, }, } _, err = s.TaskMgr.UpdateTaskList(ctx, &p.UpdateTaskListRequest{ TaskListInfo: taskListInfo, }) s.NoError(err) var resp *p.GetTaskListResponse resp, err = s.TaskMgr.GetTaskList(ctx, &p.GetTaskListRequest{ DomainID: domainID, TaskList: taskList, TaskType: p.TaskListTypeActivity, }) s.NoError(err) tli = resp.TaskListInfo s.EqualValues(2, tli.RangeID) s.EqualValues(0, tli.AckLevel) s.True(tli.LastUpdated.After(leaseTime) || tli.LastUpdated.Equal(leaseTime)) s.EqualValues(p.TaskListKindNormal, tli.Kind) s.NotNil(tli.AdaptivePartitionConfig) s.EqualValues(1, tli.AdaptivePartitionConfig.Version) s.Equal(readPartitions, tli.AdaptivePartitionConfig.ReadPartitions) s.EqualValues(writePartitions, tli.AdaptivePartitionConfig.WritePartitions) taskListInfo.RangeID = 3 _, err = s.TaskMgr.UpdateTaskList(ctx, &p.UpdateTaskListRequest{ TaskListInfo: taskListInfo, }) s.Error(err) _, ok = err.(*p.ConditionFailedError) s.True(ok) } // TestLeaseAndUpdateTaskListSticky test func (s *MatchingPersistenceSuite) TestLeaseAndUpdateTaskListSticky() { domainID := uuid.New() taskList := "aaaaaaa" ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() response, err := s.TaskMgr.LeaseTaskList(ctx, &p.LeaseTaskListRequest{ DomainID: domainID, TaskList: taskList, TaskType: p.TaskListTypeDecision, TaskListKind: p.TaskListKindSticky, }) s.NoError(err) tli := response.TaskListInfo s.EqualValues(1, tli.RangeID) s.EqualValues(0, tli.AckLevel) s.EqualValues(p.TaskListKindSticky, tli.Kind) s.Nil(tli.AdaptivePartitionConfig) taskListInfo := &p.TaskListInfo{ DomainID: domainID, Name: taskList, TaskType: p.TaskListTypeDecision, RangeID: tli.RangeID, AckLevel: 0, Kind: p.TaskListKindSticky, } _, err = s.TaskMgr.UpdateTaskList(ctx, &p.UpdateTaskListRequest{ TaskListInfo: taskListInfo, }) s.NoError(err) } func (s *MatchingPersistenceSuite) TestLeaseAndUpdateTaskListEphemeral() { domainID := uuid.New() taskList := "aaaaaaa" ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() response, err := s.TaskMgr.LeaseTaskList(ctx, &p.LeaseTaskListRequest{ DomainID: domainID, TaskList: taskList, TaskType: p.TaskListTypeDecision, TaskListKind: p.TaskListKindEphemeral, }) s.NoError(err) tli := response.TaskListInfo s.EqualValues(1, tli.RangeID) s.EqualValues(0, tli.AckLevel) s.EqualValues(p.TaskListKindEphemeral, tli.Kind) s.Nil(tli.AdaptivePartitionConfig) taskListInfo := &p.TaskListInfo{ DomainID: domainID, Name: taskList, TaskType: p.TaskListTypeDecision, RangeID: tli.RangeID, AckLevel: 0, Kind: p.TaskListKindEphemeral, } _, err = s.TaskMgr.UpdateTaskList(ctx, &p.UpdateTaskListRequest{ TaskListInfo: taskListInfo, }) s.NoError(err) } func (s *MatchingPersistenceSuite) deleteAllTaskList() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() var nextPageToken []byte for { resp, err := s.TaskMgr.ListTaskList(ctx, &p.ListTaskListRequest{PageSize: 10, PageToken: nextPageToken}) s.NoError(err) for _, it := range resp.Items { err = s.TaskMgr.DeleteTaskList(ctx, &p.DeleteTaskListRequest{ DomainID: it.DomainID, TaskListName: it.Name, TaskListType: it.TaskType, RangeID: it.RangeID, }) s.NoError(err) } nextPageToken = resp.NextPageToken if nextPageToken == nil { break } } } // TestListWithOneTaskList test func (s *MatchingPersistenceSuite) TestListWithOneTaskList() { if s.TaskMgr.GetName() == "cassandra" || s.TaskMgr.GetName() == "shardedNosql" { // ListTaskList API is currently not supported in cassandra return } s.deleteAllTaskList() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() resp, err := s.TaskMgr.ListTaskList(ctx, &p.ListTaskListRequest{PageSize: 10}) s.NoError(err) s.Nil(resp.NextPageToken) s.Equal(0, len(resp.Items)) rangeID := int64(0) ackLevel := int64(0) domainID := uuid.New() for i := 0; i < 10; i++ { rangeID++ updatedTime := time.Now() _, err := s.TaskMgr.LeaseTaskList(ctx, &p.LeaseTaskListRequest{ DomainID: domainID, TaskList: "list-task-list-test-tl0", TaskType: p.TaskListTypeActivity, TaskListKind: p.TaskListKindSticky, }) s.NoError(err) resp, err := s.TaskMgr.ListTaskList(ctx, &p.ListTaskListRequest{PageSize: 10}) s.NoError(err) s.Equal(1, len(resp.Items)) s.Equal(domainID, resp.Items[0].DomainID) s.Equal("list-task-list-test-tl0", resp.Items[0].Name) s.Equal(p.TaskListTypeActivity, resp.Items[0].TaskType) s.Equal(p.TaskListKindSticky, resp.Items[0].Kind) s.Equal(rangeID, resp.Items[0].RangeID) s.Equal(ackLevel, resp.Items[0].AckLevel) s.True(resp.Items[0].LastUpdated.After(updatedTime) || resp.Items[0].LastUpdated.Equal(updatedTime)) ackLevel++ updatedTime = time.Now() _, err = s.TaskMgr.UpdateTaskList(ctx, &p.UpdateTaskListRequest{ TaskListInfo: &p.TaskListInfo{ DomainID: domainID, Name: "list-task-list-test-tl0", TaskType: p.TaskListTypeActivity, RangeID: rangeID, AckLevel: ackLevel, Kind: p.TaskListKindSticky, }, }) s.NoError(err) resp, err = s.TaskMgr.ListTaskList(ctx, &p.ListTaskListRequest{PageSize: 10}) s.NoError(err) s.Equal(1, len(resp.Items)) s.True(resp.Items[0].LastUpdated.After(updatedTime) || resp.Items[0].LastUpdated.Equal(updatedTime)) } s.deleteAllTaskList() } // TestListWithMultipleTaskList test func (s *MatchingPersistenceSuite) TestListWithMultipleTaskList() { if s.TaskMgr.GetName() == "cassandra" || s.TaskMgr.GetName() == "shardedNosql" { // ListTaskList API is currently not supported in cassandra" return } s.deleteAllTaskList() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() domainID := uuid.New() tlNames := make(map[string]struct{}) for i := 0; i < 10; i++ { name := fmt.Sprintf("test-list-with-multiple-%v", i) _, err := s.TaskMgr.LeaseTaskList(ctx, &p.LeaseTaskListRequest{ DomainID: domainID, TaskList: name, TaskType: p.TaskListTypeActivity, TaskListKind: p.TaskListKindNormal, }) s.NoError(err) tlNames[name] = struct{}{} listedNames := make(map[string]struct{}) var nextPageToken []byte for { resp, err := s.TaskMgr.ListTaskList(ctx, &p.ListTaskListRequest{PageSize: 1, PageToken: nextPageToken}) s.NoError(err) for _, it := range resp.Items { s.Equal(domainID, it.DomainID) s.Equal(p.TaskListTypeActivity, it.TaskType) s.Equal(p.TaskListKindNormal, it.Kind) _, ok := listedNames[it.Name] s.False(ok, "list API returns duplicate entries - have: %+v got:%v", listedNames, it.Name) listedNames[it.Name] = struct{}{} } nextPageToken = resp.NextPageToken if nextPageToken == nil { break } } s.Equal(tlNames, listedNames, "list API returned wrong set of task list names") } // final test again pagination total := 0 var nextPageToken []byte for { resp, err := s.TaskMgr.ListTaskList(ctx, &p.ListTaskListRequest{ PageSize: 6, PageToken: nextPageToken, }) s.NoError(err) total += len(resp.Items) if resp.NextPageToken == nil { break } nextPageToken = resp.NextPageToken } s.Equal(10, total) s.deleteAllTaskList() resp, err := s.TaskMgr.ListTaskList(ctx, &p.ListTaskListRequest{PageSize: 10}) s.NoError(err) s.Nil(resp.NextPageToken) s.Equal(0, len(resp.Items)) } func (s *MatchingPersistenceSuite) TestGetOrphanTasks() { if os.Getenv("SKIP_GET_ORPHAN_TASKS") != "" { s.T().Skipf("GetOrphanTasks not supported in %v", s.TaskMgr.GetName()) } if s.TaskMgr.GetName() == "cassandra" || s.TaskMgr.GetName() == "shardedNosql" { // GetOrphanTasks API is currently not supported in cassandra" return } s.deleteAllTaskList() ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() oresp, err := s.TaskMgr.GetOrphanTasks(ctx, &p.GetOrphanTasksRequest{Limit: 10}) s.NoError(err) // existing orphans that caused by other tests existingOrphans := len(oresp.Tasks) domainID := uuid.New() name := "test-list-with-orphans" resp, err := s.TaskMgr.LeaseTaskList(ctx, &p.LeaseTaskListRequest{ DomainID: domainID, TaskList: name, TaskType: p.TaskListTypeActivity, TaskListKind: p.TaskListKindNormal, }) s.NoError(err) wid := uuid.New() rid := uuid.New() s.TaskMgr.CreateTasks(ctx, &p.CreateTasksRequest{ TaskListInfo: resp.TaskListInfo, Tasks: []*p.CreateTaskInfo{ { Data: &p.TaskInfo{ DomainID: domainID, WorkflowID: wid, RunID: rid, TaskID: 0, ScheduleID: 0, ScheduleToStartTimeoutSeconds: 0, Expiry: time.Now(), CreatedTime: time.Now(), }, TaskID: 0, }, }, }) oresp, err = s.TaskMgr.GetOrphanTasks(ctx, &p.GetOrphanTasksRequest{Limit: 10}) s.NoError(err) s.Equal(existingOrphans, len(oresp.Tasks)) s.deleteAllTaskList() oresp, err = s.TaskMgr.GetOrphanTasks(ctx, &p.GetOrphanTasksRequest{Limit: 10}) s.NoError(err) s.Equal(existingOrphans+1, len(oresp.Tasks)) found := false for _, it := range oresp.Tasks { if it.DomainID != domainID { continue } s.Equal(p.TaskListTypeActivity, it.TaskType) s.Equal(int64(0), it.TaskID) s.Equal(name, it.TaskListName) found = true } s.True(found) } ================================================ FILE: common/persistence/persistence-tests/metadataPersistenceV2Test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "fmt" "log" "os" "strconv" "strings" "sync" "sync/atomic" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // MetadataPersistenceSuiteV2 is test of the V2 version of metadata persistence MetadataPersistenceSuiteV2 struct { *TestBase // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions } ) // SetupSuite implementation func (m *MetadataPersistenceSuiteV2) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } // SetupTest implementation func (m *MetadataPersistenceSuiteV2) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil m.Assertions = require.New(m.T()) // cleanup the domain created var token []byte pageSize := 10 ListLoop: for { resp, err := m.ListDomains(context.Background(), pageSize, token) m.NoError(err) token = resp.NextPageToken for _, domain := range resp.Domains { m.NoError(m.DeleteDomain(context.Background(), domain.Info.ID, "")) } if len(token) == 0 { break ListLoop } } } // TearDownTest implementation func (m *MetadataPersistenceSuiteV2) TearDownTest() { } // TearDownSuite implementation func (m *MetadataPersistenceSuiteV2) TearDownSuite() { m.TearDownWorkflowStore() } // TestCreateDomain test func (m *MetadataPersistenceSuiteV2) TestCreateDomain() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() id := uuid.New() name := "create-domain-test-name" status := p.DomainStatusRegistered description := "create-domain-test-description" owner := "create-domain-test-owner" data := map[string]string{"k1": "v1"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "test://history/uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "test://visibility/uri" badBinaries := types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}} isGlobalDomain := false configVersion := int64(0) failoverVersion := int64(0) lastUpdateTime := int64(100) resp0, err0 := m.CreateDomain( ctx, &p.DomainInfo{ ID: id, Name: name, Status: status, Description: description, OwnerEmail: owner, Data: data, }, &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: badBinaries, }, &p.DomainReplicationConfig{}, isGlobalDomain, configVersion, failoverVersion, lastUpdateTime, ) m.NoError(err0) m.NotNil(resp0) m.Equal(id, resp0.ID) // for domain which do not have replication config set, will default to // use current cluster as active, with current cluster as all clusters resp1, err1 := m.GetDomain(ctx, id, "") m.NoError(err1) m.NotNil(resp1) m.Equal(id, resp1.Info.ID) m.Equal(name, resp1.Info.Name) m.Equal(status, resp1.Info.Status) m.Equal(description, resp1.Info.Description) m.Equal(owner, resp1.Info.OwnerEmail) m.Equal(data, resp1.Info.Data) m.Equal(retention, resp1.Config.Retention) m.Equal(emitMetric, resp1.Config.EmitMetric) m.Equal(historyArchivalStatus, resp1.Config.HistoryArchivalStatus) m.Equal(historyArchivalURI, resp1.Config.HistoryArchivalURI) m.Equal(visibilityArchivalStatus, resp1.Config.VisibilityArchivalStatus) m.Equal(visibilityArchivalURI, resp1.Config.VisibilityArchivalURI) m.Equal(badBinaries, resp1.Config.BadBinaries) m.Equal(cluster.TestCurrentClusterName, resp1.ReplicationConfig.ActiveClusterName) m.Equal(1, len(resp1.ReplicationConfig.Clusters)) m.Equal(isGlobalDomain, resp1.IsGlobalDomain) m.Equal(configVersion, resp1.ConfigVersion) m.Equal(failoverVersion, resp1.FailoverVersion) m.Equal(constants.InitialPreviousFailoverVersion, resp1.PreviousFailoverVersion) m.True(resp1.ReplicationConfig.Clusters[0].ClusterName == cluster.TestCurrentClusterName) m.Equal(p.InitialFailoverNotificationVersion, resp1.FailoverNotificationVersion) m.Nil(resp1.FailoverEndTime) m.Equal(lastUpdateTime, resp1.LastUpdatedTime) resp2, err2 := m.CreateDomain( ctx, &p.DomainInfo{ ID: uuid.New(), Name: name, Status: status, Description: "fail", OwnerEmail: "fail", Data: map[string]string{}, }, &p.DomainConfig{ Retention: 100, EmitMetric: false, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{}, }, &p.DomainReplicationConfig{}, isGlobalDomain, configVersion, failoverVersion, 0, ) m.Error(err2) m.IsType(&types.DomainAlreadyExistsError{}, err2) m.Nil(resp2) } // TestGetDomain test func (m *MetadataPersistenceSuiteV2) TestGetDomain() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() id := uuid.New() name := "get-domain-test-name" status := p.DomainStatusRegistered description := "get-domain-test-description" owner := "get-domain-test-owner" data := map[string]string{"k1": "v1"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "test://history/uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "test://visibility/uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(11) failoverVersion := int64(59) isGlobalDomain := true clusters := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } resp0, err0 := m.GetDomain(ctx, "", "does-not-exist") m.Nil(resp0) m.Error(err0) m.IsType(&types.EntityNotExistsError{}, err0) testBinaries := types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "abc": { Reason: "test-reason", Operator: "test-operator", CreatedTimeNano: common.Int64Ptr(123), }, }, } resp1, err1 := m.CreateDomain( ctx, &p.DomainInfo{ ID: id, Name: name, Status: status, Description: description, OwnerEmail: owner, Data: data, }, &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: testBinaries, }, &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, }, isGlobalDomain, configVersion, failoverVersion, 0, ) m.NoError(err1) m.NotNil(resp1) m.Equal(id, resp1.ID) resp2, err2 := m.GetDomain(ctx, id, "") m.NoError(err2) m.NotNil(resp2) m.Equal(id, resp2.Info.ID) m.Equal(name, resp2.Info.Name) m.Equal(status, resp2.Info.Status) m.Equal(description, resp2.Info.Description) m.Equal(owner, resp2.Info.OwnerEmail) m.Equal(data, resp2.Info.Data) m.Equal(retention, resp2.Config.Retention) m.Equal(emitMetric, resp2.Config.EmitMetric) m.Equal(historyArchivalStatus, resp2.Config.HistoryArchivalStatus) m.Equal(historyArchivalURI, resp2.Config.HistoryArchivalURI) m.Equal(visibilityArchivalStatus, resp2.Config.VisibilityArchivalStatus) m.Equal(visibilityArchivalURI, resp2.Config.VisibilityArchivalURI) m.Equal(testBinaries, resp2.Config.BadBinaries) m.Equal(clusterActive, resp2.ReplicationConfig.ActiveClusterName) m.Equal(len(clusters), len(resp2.ReplicationConfig.Clusters)) for index := range clusters { m.Equal(clusters[index], resp2.ReplicationConfig.Clusters[index]) } m.Equal(isGlobalDomain, resp2.IsGlobalDomain) m.Equal(configVersion, resp2.ConfigVersion) m.Equal(failoverVersion, resp2.FailoverVersion) m.Equal(constants.InitialPreviousFailoverVersion, resp2.PreviousFailoverVersion) m.Equal(p.InitialFailoverNotificationVersion, resp2.FailoverNotificationVersion) m.Nil(resp2.FailoverEndTime) m.NotEqual(0, resp2.LastUpdatedTime) resp3, err3 := m.GetDomain(ctx, "", name) m.NoError(err3) m.NotNil(resp3) m.Equal(id, resp3.Info.ID) m.Equal(name, resp3.Info.Name) m.Equal(status, resp3.Info.Status) m.Equal(description, resp3.Info.Description) m.Equal(owner, resp3.Info.OwnerEmail) m.Equal(data, resp3.Info.Data) m.Equal(retention, resp3.Config.Retention) m.Equal(emitMetric, resp3.Config.EmitMetric) m.Equal(historyArchivalStatus, resp3.Config.HistoryArchivalStatus) m.Equal(historyArchivalURI, resp3.Config.HistoryArchivalURI) m.Equal(visibilityArchivalStatus, resp3.Config.VisibilityArchivalStatus) m.Equal(visibilityArchivalURI, resp3.Config.VisibilityArchivalURI) m.Equal(clusterActive, resp3.ReplicationConfig.ActiveClusterName) m.Equal(len(clusters), len(resp3.ReplicationConfig.Clusters)) for index := range clusters { m.Equal(clusters[index], resp3.ReplicationConfig.Clusters[index]) } m.Equal(isGlobalDomain, resp3.IsGlobalDomain) m.Equal(configVersion, resp3.ConfigVersion) m.Equal(failoverVersion, resp3.FailoverVersion) m.Equal(constants.InitialPreviousFailoverVersion, resp2.PreviousFailoverVersion) m.Equal(p.InitialFailoverNotificationVersion, resp3.FailoverNotificationVersion) m.NotEqual(0, resp3.LastUpdatedTime) resp4, err4 := m.GetDomain(ctx, id, name) m.Error(err4) m.IsType(&types.BadRequestError{}, err4) m.Nil(resp4) resp5, err5 := m.GetDomain(ctx, "", "") m.Nil(resp5) m.IsType(&types.BadRequestError{}, err5) _, err6 := m.CreateDomain(ctx, &p.DomainInfo{ ID: uuid.New(), Name: name, Status: status, Description: description, OwnerEmail: owner, Data: data, }, &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, }, &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, }, isGlobalDomain, configVersion, failoverVersion, 0, ) m.Error(err6) } // TestConcurrentCreateDomain test func (m *MetadataPersistenceSuiteV2) TestConcurrentCreateDomain() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() concurrency := 16 numDomains := 5 domainIDs := make([]string, numDomains) names := make([]string, numDomains) registered := make([]bool, numDomains) for idx := range domainIDs { domainIDs[idx] = uuid.New() names[idx] = "concurrent-create-domain-test-name-" + strconv.Itoa(idx) } status := p.DomainStatusRegistered description := "concurrent-create-domain-test-description" owner := "create-domain-test-owner" retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "test://history/uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "test://visibility/uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(10) failoverVersion := int64(59) isGlobalDomain := true clusters := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } testBinaries := types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "abc": { Reason: "test-reason", Operator: "test-operator", CreatedTimeNano: common.Int64Ptr(123), }, }, } successCount := 0 var mutex sync.Mutex var wg sync.WaitGroup for i := 1; i <= concurrency; i++ { wg.Add(1) go func(idx int) { data := map[string]string{"k0": fmt.Sprintf("v-%v", idx)} _, err1 := m.CreateDomain(ctx, &p.DomainInfo{ ID: domainIDs[idx%numDomains], Name: names[idx%numDomains], Status: status, Description: description, OwnerEmail: owner, Data: data, }, &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: testBinaries, }, &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, }, isGlobalDomain, configVersion, failoverVersion, 0, ) mutex.Lock() defer mutex.Unlock() if err1 == nil { successCount++ registered[idx%numDomains] = true } if _, ok := err1.(*types.DomainAlreadyExistsError); ok { registered[idx%numDomains] = true } wg.Done() }(i) } wg.Wait() m.GreaterOrEqual(successCount, 1) for i := 0; i != numDomains; i++ { if !registered[i] { continue } resp, err3 := m.GetDomain(ctx, "", names[i]) m.NoError(err3) m.NotNil(resp) m.Equal(domainIDs[i], resp.Info.ID) m.Equal(names[i], resp.Info.Name) m.Equal(status, resp.Info.Status) m.Equal(description, resp.Info.Description) m.Equal(owner, resp.Info.OwnerEmail) m.Equal(retention, resp.Config.Retention) m.Equal(emitMetric, resp.Config.EmitMetric) m.Equal(historyArchivalStatus, resp.Config.HistoryArchivalStatus) m.Equal(historyArchivalURI, resp.Config.HistoryArchivalURI) m.Equal(visibilityArchivalStatus, resp.Config.VisibilityArchivalStatus) m.Equal(visibilityArchivalURI, resp.Config.VisibilityArchivalURI) m.Equal(testBinaries, resp.Config.BadBinaries) m.Equal(clusterActive, resp.ReplicationConfig.ActiveClusterName) m.Equal(len(clusters), len(resp.ReplicationConfig.Clusters)) for index := range clusters { m.Equal(clusters[index], resp.ReplicationConfig.Clusters[index]) } m.Equal(isGlobalDomain, resp.IsGlobalDomain) m.Equal(configVersion, resp.ConfigVersion) m.Equal(failoverVersion, resp.FailoverVersion) m.Equal(constants.InitialPreviousFailoverVersion, resp.PreviousFailoverVersion) // check domain data ss := strings.Split(resp.Info.Data["k0"], "-") m.Equal(2, len(ss)) vi, err := strconv.Atoi(ss[1]) m.NoError(err) m.Equal(true, vi > 0 && vi <= concurrency) } } // TestConcurrentUpdateDomain test func (m *MetadataPersistenceSuiteV2) TestConcurrentUpdateDomain() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() isolationGroups := types.IsolationGroupConfiguration{ "zone-1": types.IsolationGroupPartition{ Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": types.IsolationGroupPartition{ Name: "zone-2", State: types.IsolationGroupStateHealthy, }, } asyncWFCfg := types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "testQueue", } id := uuid.New() name := "concurrent-update-domain-test-name" status := p.DomainStatusRegistered description := "update-domain-test-description" owner := "update-domain-test-owner" data := map[string]string{"k1": "v1"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "test://history/uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "test://visibility/uri" badBinaries := types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}} clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(10) failoverVersion := int64(59) isGlobalDomain := true clusters := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } resp1, err1 := m.CreateDomain(ctx, &p.DomainInfo{ ID: id, Name: name, Status: status, Description: description, OwnerEmail: owner, Data: data, }, &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, BadBinaries: badBinaries, IsolationGroups: isolationGroups, AsyncWorkflowConfig: asyncWFCfg, }, &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, }, isGlobalDomain, configVersion, failoverVersion, 0, ) m.NoError(err1) m.Equal(id, resp1.ID) resp2, err2 := m.GetDomain(ctx, id, "") m.NoError(err2) m.Equal(badBinaries, resp2.Config.BadBinaries) metadata, err := m.DomainManager.GetMetadata(ctx) m.NoError(err) notificationVersion := metadata.NotificationVersion testBinaries := types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "abc": { Reason: "test-reason", Operator: "test-operator", CreatedTimeNano: common.Int64Ptr(123), }, }, } concurrency := 16 successCount := int32(0) var wg sync.WaitGroup for i := 1; i <= concurrency; i++ { newValue := fmt.Sprintf("v-%v", i) wg.Add(1) go func(updatedData map[string]string) { err3 := m.UpdateDomain( ctx, &p.DomainInfo{ ID: resp2.Info.ID, Name: resp2.Info.Name, Status: resp2.Info.Status, Description: resp2.Info.Description, OwnerEmail: resp2.Info.OwnerEmail, Data: updatedData, }, &p.DomainConfig{ Retention: resp2.Config.Retention, EmitMetric: resp2.Config.EmitMetric, HistoryArchivalStatus: resp2.Config.HistoryArchivalStatus, HistoryArchivalURI: resp2.Config.HistoryArchivalURI, VisibilityArchivalStatus: resp2.Config.VisibilityArchivalStatus, VisibilityArchivalURI: resp2.Config.VisibilityArchivalURI, BadBinaries: testBinaries, IsolationGroups: isolationGroups, AsyncWorkflowConfig: asyncWFCfg, }, &p.DomainReplicationConfig{ ActiveClusterName: resp2.ReplicationConfig.ActiveClusterName, Clusters: resp2.ReplicationConfig.Clusters, }, resp2.ConfigVersion, resp2.FailoverVersion, resp2.FailoverNotificationVersion, resp2.PreviousFailoverVersion, nil, notificationVersion, 0, ) if err3 == nil { atomic.AddInt32(&successCount, 1) } wg.Done() }(map[string]string{"k0": newValue}) } wg.Wait() m.Greater(successCount, int32(0)) allDomains, err := m.ListDomains(ctx, 100, nil) m.NoError(err) domainNameMap := make(map[string]bool) for _, domain := range allDomains.Domains { _, ok := domainNameMap[domain.Info.Name] m.False(ok) domainNameMap[domain.Info.Name] = true } resp3, err3 := m.GetDomain(ctx, "", name) m.NoError(err3) m.NotNil(resp3) m.Equal(id, resp3.Info.ID) m.Equal(name, resp3.Info.Name) m.Equal(status, resp3.Info.Status) m.Equal(isGlobalDomain, resp3.IsGlobalDomain) m.Equal(description, resp3.Info.Description) m.Equal(owner, resp3.Info.OwnerEmail) m.Equal(retention, resp3.Config.Retention) m.Equal(emitMetric, resp3.Config.EmitMetric) m.Equal(historyArchivalStatus, resp3.Config.HistoryArchivalStatus) m.Equal(historyArchivalURI, resp3.Config.HistoryArchivalURI) m.Equal(visibilityArchivalStatus, resp3.Config.VisibilityArchivalStatus) m.Equal(visibilityArchivalURI, resp3.Config.VisibilityArchivalURI) m.Equal(testBinaries, resp3.Config.BadBinaries) m.Equal(clusterActive, resp3.ReplicationConfig.ActiveClusterName) m.Equal(len(clusters), len(resp3.ReplicationConfig.Clusters)) for index := range clusters { m.Equal(clusters[index], resp3.ReplicationConfig.Clusters[index]) } m.Equal(isGlobalDomain, resp3.IsGlobalDomain) m.Equal(configVersion, resp3.ConfigVersion) m.Equal(failoverVersion, resp3.FailoverVersion) m.Equal(constants.InitialPreviousFailoverVersion, resp3.PreviousFailoverVersion) // check domain data ss := strings.Split(resp3.Info.Data["k0"], "-") m.Equal(2, len(ss)) vi, err := strconv.Atoi(ss[1]) m.NoError(err) m.Equal(true, vi > 0 && vi <= concurrency) } // TestUpdateDomain test func (m *MetadataPersistenceSuiteV2) TestUpdateDomain() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() isolationGroups1 := types.IsolationGroupConfiguration{} isolationGroups2 := types.IsolationGroupConfiguration{ "zone-1": types.IsolationGroupPartition{ Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": types.IsolationGroupPartition{ Name: "zone-2", State: types.IsolationGroupStateHealthy, }, } asyncWFCfg1 := types.AsyncWorkflowConfiguration{ PredefinedQueueName: "queue1", } asyncWFCfg2 := types.AsyncWorkflowConfiguration{ PredefinedQueueName: "queue2", } id := uuid.New() name := "update-domain-test-name" status := p.DomainStatusRegistered description := "update-domain-test-description" owner := "update-domain-test-owner" data := map[string]string{"k1": "v1"} retention := int32(10) emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "test://history/uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "test://visibility/uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(10) failoverVersion := int64(59) failoverEndTime := time.Now().UnixNano() isGlobalDomain := true lastUpdateTime := int64(100) clusters := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } resp1, err1 := m.CreateDomain( ctx, &p.DomainInfo{ ID: id, Name: name, Status: status, Description: description, OwnerEmail: owner, Data: data, }, &p.DomainConfig{ Retention: retention, EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, IsolationGroups: isolationGroups1, AsyncWorkflowConfig: asyncWFCfg1, }, &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, }, isGlobalDomain, configVersion, failoverVersion, lastUpdateTime, ) m.NoError(err1) m.Equal(id, resp1.ID) resp2, err2 := m.GetDomain(ctx, id, "") m.NoError(err2) m.Nil(resp2.FailoverEndTime) metadata, err := m.DomainManager.GetMetadata(ctx) m.NoError(err) notificationVersion := metadata.NotificationVersion updatedStatus := p.DomainStatusDeprecated updatedDescription := "description-updated" updatedOwner := "owner-updated" // This will overriding the previous key-value pair updatedData := map[string]string{"k1": "v2"} updatedRetention := int32(20) updatedEmitMetric := false updatedHistoryArchivalStatus := types.ArchivalStatusDisabled updatedHistoryArchivalURI := "" updatedVisibilityArchivalStatus := types.ArchivalStatusDisabled updatedVisibilityArchivalURI := "" updateClusterActive := "other random active cluster name" updateClusterStandby := "other random standby cluster name" lastUpdateTime++ updateConfigVersion := int64(12) updateFailoverVersion := int64(28) updatePreviousFailoverVersion := int64(20) updateFailoverNotificationVersion := int64(14) updateClusters := []*p.ClusterReplicationConfig{ { ClusterName: updateClusterActive, }, { ClusterName: updateClusterStandby, }, } testBinaries := types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "abc": { Reason: "test-reason", Operator: "test-operator", CreatedTimeNano: common.Int64Ptr(123), }, }, } err3 := m.UpdateDomain( ctx, &p.DomainInfo{ ID: resp2.Info.ID, Name: resp2.Info.Name, Status: updatedStatus, Description: updatedDescription, OwnerEmail: updatedOwner, Data: updatedData, }, &p.DomainConfig{ Retention: updatedRetention, EmitMetric: updatedEmitMetric, HistoryArchivalStatus: updatedHistoryArchivalStatus, HistoryArchivalURI: updatedHistoryArchivalURI, VisibilityArchivalStatus: updatedVisibilityArchivalStatus, VisibilityArchivalURI: updatedVisibilityArchivalURI, BadBinaries: testBinaries, IsolationGroups: isolationGroups2, AsyncWorkflowConfig: asyncWFCfg2, }, &p.DomainReplicationConfig{ ActiveClusterName: updateClusterActive, Clusters: updateClusters, }, updateConfigVersion, updateFailoverVersion, updateFailoverNotificationVersion, updatePreviousFailoverVersion, &failoverEndTime, notificationVersion, lastUpdateTime, ) m.NoError(err3) resp4, err4 := m.GetDomain(ctx, "", name) m.NoError(err4) m.NotNil(resp4) m.Equal(id, resp4.Info.ID) m.Equal(name, resp4.Info.Name) m.Equal(isGlobalDomain, resp4.IsGlobalDomain) m.Equal(updatedStatus, resp4.Info.Status) m.Equal(updatedDescription, resp4.Info.Description) m.Equal(updatedOwner, resp4.Info.OwnerEmail) m.Equal(updatedData, resp4.Info.Data) m.Equal(updatedRetention, resp4.Config.Retention) m.Equal(updatedEmitMetric, resp4.Config.EmitMetric) m.Equal(updatedHistoryArchivalStatus, resp4.Config.HistoryArchivalStatus) m.Equal(updatedHistoryArchivalURI, resp4.Config.HistoryArchivalURI) m.Equal(updatedVisibilityArchivalStatus, resp4.Config.VisibilityArchivalStatus) m.Equal(updatedVisibilityArchivalURI, resp4.Config.VisibilityArchivalURI) m.Equal(testBinaries, resp4.Config.BadBinaries) m.Equal(updateClusterActive, resp4.ReplicationConfig.ActiveClusterName) m.Equal(len(updateClusters), len(resp4.ReplicationConfig.Clusters)) for index := range clusters { m.Equal(updateClusters[index], resp4.ReplicationConfig.Clusters[index]) } m.Equal(updateConfigVersion, resp4.ConfigVersion) m.Equal(updateFailoverVersion, resp4.FailoverVersion) m.Equal(updatePreviousFailoverVersion, resp4.PreviousFailoverVersion) m.Equal(updateFailoverNotificationVersion, resp4.FailoverNotificationVersion) m.Equal(notificationVersion, resp4.NotificationVersion) m.Equal(&failoverEndTime, resp4.FailoverEndTime) m.Equal(lastUpdateTime, resp4.LastUpdatedTime) m.Equal(isolationGroups2, resp4.Config.IsolationGroups) m.Equal(asyncWFCfg2, resp4.Config.AsyncWorkflowConfig) resp5, err5 := m.GetDomain(ctx, id, "") m.NoError(err5) m.NotNil(resp5) m.Equal(id, resp5.Info.ID) m.Equal(name, resp5.Info.Name) m.Equal(isGlobalDomain, resp5.IsGlobalDomain) m.Equal(updatedStatus, resp5.Info.Status) m.Equal(updatedDescription, resp5.Info.Description) m.Equal(updatedOwner, resp5.Info.OwnerEmail) m.Equal(updatedData, resp5.Info.Data) m.Equal(updatedRetention, resp5.Config.Retention) m.Equal(updatedEmitMetric, resp5.Config.EmitMetric) m.Equal(updatedHistoryArchivalStatus, resp5.Config.HistoryArchivalStatus) m.Equal(updatedHistoryArchivalURI, resp5.Config.HistoryArchivalURI) m.Equal(updatedVisibilityArchivalStatus, resp5.Config.VisibilityArchivalStatus) m.Equal(updatedVisibilityArchivalURI, resp5.Config.VisibilityArchivalURI) m.Equal(updateClusterActive, resp5.ReplicationConfig.ActiveClusterName) m.Equal(len(updateClusters), len(resp5.ReplicationConfig.Clusters)) for index := range clusters { m.Equal(updateClusters[index], resp5.ReplicationConfig.Clusters[index]) } m.Equal(updateConfigVersion, resp5.ConfigVersion) m.Equal(updateFailoverVersion, resp5.FailoverVersion) m.Equal(updatePreviousFailoverVersion, resp5.PreviousFailoverVersion) m.Equal(updateFailoverNotificationVersion, resp5.FailoverNotificationVersion) m.Equal(notificationVersion, resp5.NotificationVersion) m.Equal(&failoverEndTime, resp5.FailoverEndTime) m.Equal(lastUpdateTime, resp5.LastUpdatedTime) notificationVersion++ lastUpdateTime++ err6 := m.UpdateDomain( ctx, &p.DomainInfo{ ID: resp2.Info.ID, Name: resp2.Info.Name, Status: updatedStatus, Description: updatedDescription, OwnerEmail: updatedOwner, Data: updatedData, }, &p.DomainConfig{ Retention: updatedRetention, EmitMetric: updatedEmitMetric, HistoryArchivalStatus: updatedHistoryArchivalStatus, HistoryArchivalURI: updatedHistoryArchivalURI, VisibilityArchivalStatus: updatedVisibilityArchivalStatus, VisibilityArchivalURI: updatedVisibilityArchivalURI, BadBinaries: testBinaries, IsolationGroups: isolationGroups1, AsyncWorkflowConfig: asyncWFCfg1, }, &p.DomainReplicationConfig{ ActiveClusterName: updateClusterActive, Clusters: updateClusters, }, updateConfigVersion, updateFailoverVersion, updateFailoverNotificationVersion, updatePreviousFailoverVersion, nil, notificationVersion, lastUpdateTime, ) m.NoError(err6) resp6, err6 := m.GetDomain(ctx, "", name) m.NoError(err6) m.NotNil(resp6) m.Equal(id, resp6.Info.ID) m.Equal(name, resp6.Info.Name) m.Equal(isGlobalDomain, resp6.IsGlobalDomain) m.Equal(updatedStatus, resp6.Info.Status) m.Equal(updatedDescription, resp6.Info.Description) m.Equal(updatedOwner, resp6.Info.OwnerEmail) m.Equal(updatedData, resp6.Info.Data) m.Equal(updatedRetention, resp6.Config.Retention) m.Equal(updatedEmitMetric, resp6.Config.EmitMetric) m.Equal(updatedHistoryArchivalStatus, resp6.Config.HistoryArchivalStatus) m.Equal(updatedHistoryArchivalURI, resp6.Config.HistoryArchivalURI) m.Equal(updatedVisibilityArchivalStatus, resp6.Config.VisibilityArchivalStatus) m.Equal(updatedVisibilityArchivalURI, resp6.Config.VisibilityArchivalURI) m.Equal(testBinaries, resp6.Config.BadBinaries) m.Equal(updateClusterActive, resp6.ReplicationConfig.ActiveClusterName) m.Equal(len(updateClusters), len(resp6.ReplicationConfig.Clusters)) for index := range clusters { m.Equal(updateClusters[index], resp6.ReplicationConfig.Clusters[index]) } m.Equal(updateConfigVersion, resp6.ConfigVersion) m.Equal(updateFailoverVersion, resp6.FailoverVersion) m.Equal(updatePreviousFailoverVersion, resp6.PreviousFailoverVersion) m.Equal(updateFailoverNotificationVersion, resp6.FailoverNotificationVersion) m.Equal(notificationVersion, resp6.NotificationVersion) m.Nil(resp6.FailoverEndTime) m.Equal(lastUpdateTime, resp6.LastUpdatedTime) m.Equal(isolationGroups1, resp6.Config.IsolationGroups) m.Equal(asyncWFCfg1, resp6.Config.AsyncWorkflowConfig) // make it active-active domain notificationVersion++ lastUpdateTime++ activeClusters := &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: updateClusters[0].ClusterName, }, "region2": { ActiveClusterName: updateClusters[1].ClusterName, }, }, }, "city": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "seattle": { ActiveClusterName: updateClusters[0].ClusterName, }, }, }, }, } err7 := m.UpdateDomain( ctx, &p.DomainInfo{ ID: resp2.Info.ID, Name: resp2.Info.Name, Status: updatedStatus, Description: updatedDescription, OwnerEmail: updatedOwner, Data: updatedData, }, &p.DomainConfig{ Retention: updatedRetention, EmitMetric: updatedEmitMetric, HistoryArchivalStatus: updatedHistoryArchivalStatus, HistoryArchivalURI: updatedHistoryArchivalURI, VisibilityArchivalStatus: updatedVisibilityArchivalStatus, VisibilityArchivalURI: updatedVisibilityArchivalURI, BadBinaries: testBinaries, IsolationGroups: isolationGroups1, AsyncWorkflowConfig: asyncWFCfg1, }, &p.DomainReplicationConfig{ ActiveClusterName: updateClusterActive, Clusters: updateClusters, ActiveClusters: activeClusters, }, updateConfigVersion, updateFailoverVersion, updateFailoverNotificationVersion, updatePreviousFailoverVersion, nil, notificationVersion, lastUpdateTime, ) m.NoError(err7) resp7, err7 := m.GetDomain(ctx, "", name) m.T().Logf("resp7: %+v", resp7) m.T().Logf("resp7.Info: %+v. even after setting status", *resp7.Info) m.NoError(err7) m.NotNil(resp7) m.Equal(id, resp7.Info.ID) m.Equal(name, resp7.Info.Name) m.Equal(isGlobalDomain, resp7.IsGlobalDomain) m.Equal(updatedStatus, resp7.Info.Status) m.Equal(isolationGroups1, resp7.Config.IsolationGroups) m.Equal(asyncWFCfg1, resp7.Config.AsyncWorkflowConfig) m.Equal(updateClusterActive, resp7.ReplicationConfig.ActiveClusterName) // ActiveClustersByRegion field has been removed - test only AttributeScopes m.Equal(activeClusters.AttributeScopes, resp7.ReplicationConfig.ActiveClusters.AttributeScopes) m.Equal(len(updateClusters), len(resp7.ReplicationConfig.Clusters)) for index := range clusters { m.Equal(updateClusters[index], resp7.ReplicationConfig.Clusters[index]) } m.Equal(notificationVersion, resp7.NotificationVersion) m.Equal(lastUpdateTime, resp7.LastUpdatedTime) m.Equal(updateFailoverVersion, resp7.FailoverVersion) m.Equal(updateFailoverNotificationVersion, resp7.FailoverNotificationVersion) m.Equal(updatePreviousFailoverVersion, resp7.PreviousFailoverVersion) m.Nil(resp7.FailoverEndTime) m.Equal(updateConfigVersion, resp7.ConfigVersion) } // TestDeleteDomain test func (m *MetadataPersistenceSuiteV2) TestDeleteDomain() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() id := uuid.New() name := "delete-domain-test-name" status := p.DomainStatusRegistered description := "delete-domain-test-description" owner := "delete-domain-test-owner" data := map[string]string{"k1": "v1"} retention := 10 emitMetric := true historyArchivalStatus := types.ArchivalStatusEnabled historyArchivalURI := "test://history/uri" visibilityArchivalStatus := types.ArchivalStatusEnabled visibilityArchivalURI := "test://visibility/uri" clusterActive := "some random active cluster name" clusterStandby := "some random standby cluster name" configVersion := int64(10) failoverVersion := int64(59) isGlobalDomain := true clusters := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive, }, { ClusterName: clusterStandby, }, } resp1, err1 := m.CreateDomain( ctx, &p.DomainInfo{ ID: id, Name: name, Status: status, Description: description, OwnerEmail: owner, Data: data, }, &p.DomainConfig{ Retention: int32(retention), EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, IsolationGroups: types.IsolationGroupConfiguration{}, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{}, }, &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, }, isGlobalDomain, configVersion, failoverVersion, 0, ) m.NoError(err1) m.Equal(id, resp1.ID) resp2, err2 := m.GetDomain(ctx, "", name) m.NoError(err2) m.NotNil(resp2) err3 := m.DeleteDomain(ctx, "", name) m.NoError(err3) resp4, err4 := m.GetDomain(ctx, "", name) m.Error(err4) m.IsType(&types.EntityNotExistsError{}, err4) m.Nil(resp4) resp5, err5 := m.GetDomain(ctx, id, "") m.Error(err5) m.IsType(&types.EntityNotExistsError{}, err5) m.Nil(resp5) id = uuid.New() resp6, err6 := m.CreateDomain( ctx, &p.DomainInfo{ ID: id, Name: name, Status: status, Description: description, OwnerEmail: owner, Data: data, }, &p.DomainConfig{ Retention: int32(retention), EmitMetric: emitMetric, HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, }, &p.DomainReplicationConfig{ ActiveClusterName: clusterActive, Clusters: clusters, }, isGlobalDomain, configVersion, failoverVersion, 0, ) m.NoError(err6) m.Equal(id, resp6.ID) err7 := m.DeleteDomain(ctx, id, "") m.NoError(err7) resp8, err8 := m.GetDomain(ctx, "", name) m.Error(err8) m.IsType(&types.EntityNotExistsError{}, err8) m.Nil(resp8) resp9, err9 := m.GetDomain(ctx, id, "") m.Error(err9) m.IsType(&types.EntityNotExistsError{}, err9) m.Nil(resp9) } // TestListDomains test func (m *MetadataPersistenceSuiteV2) TestListDomains() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() clusterActive1 := "some random active cluster name" clusterStandby1 := "some random standby cluster name" clusters1 := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive1, }, { ClusterName: clusterStandby1, }, } clusterActive2 := "other random active cluster name" clusterStandby2 := "other random standby cluster name" clusters2 := []*p.ClusterReplicationConfig{ { ClusterName: clusterActive2, }, { ClusterName: clusterStandby2, }, } testBinaries1 := types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "abc": { Reason: "test-reason1", Operator: "test-operator1", CreatedTimeNano: common.Int64Ptr(123), }, }, } testBinaries2 := types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "efg": { Reason: "test-reason2", Operator: "test-operator2", CreatedTimeNano: common.Int64Ptr(456), }, }, } inputDomains := []*p.GetDomainResponse{ { Info: &p.DomainInfo{ ID: uuid.New(), Name: "list-domain-test-name-1", Status: p.DomainStatusRegistered, Description: "list-domain-test-description-1", OwnerEmail: "list-domain-test-owner-1", Data: map[string]string{"k1": "v1"}, }, Config: &p.DomainConfig{ Retention: 109, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "test://history/uri", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "test://visibility/uri", BadBinaries: testBinaries1, IsolationGroups: types.IsolationGroupConfiguration{ "name1": types.IsolationGroupPartition{Name: "name1"}, "name2": types.IsolationGroupPartition{Name: "name2"}, }, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "queue1", }, }, ReplicationConfig: &p.DomainReplicationConfig{ ActiveClusterName: clusterActive1, Clusters: clusters1, }, IsGlobalDomain: true, ConfigVersion: 133, FailoverVersion: 266, PreviousFailoverVersion: -1, }, { Info: &p.DomainInfo{ ID: uuid.New(), Name: "list-domain-test-name-2", Status: p.DomainStatusRegistered, Description: "list-domain-test-description-2", OwnerEmail: "list-domain-test-owner-2", Data: map[string]string{"k1": "v2"}, }, Config: &p.DomainConfig{ Retention: 326, EmitMetric: false, HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", BadBinaries: testBinaries2, IsolationGroups: types.IsolationGroupConfiguration{ "name3": types.IsolationGroupPartition{Name: "name3"}, }, AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: false, PredefinedQueueName: "queue2", }, }, ReplicationConfig: &p.DomainReplicationConfig{ ActiveClusterName: clusterActive2, Clusters: clusters2, }, IsGlobalDomain: false, ConfigVersion: 400, FailoverVersion: 667, PreviousFailoverVersion: -1, }, } for _, domain := range inputDomains { _, err := m.CreateDomain( ctx, domain.Info, domain.Config, domain.ReplicationConfig, domain.IsGlobalDomain, domain.ConfigVersion, domain.FailoverVersion, 0, ) m.NoError(err) } var token []byte pageSize := 1 outputDomains := make(map[string]*p.GetDomainResponse) ListLoop: for { resp, err := m.ListDomains(ctx, pageSize, token) m.NoError(err) token = resp.NextPageToken for _, domain := range resp.Domains { outputDomains[domain.Info.ID] = domain // global notification version is already tested, so here we make it 0 // so we can test == easily domain.NotificationVersion = 0 } if len(token) == 0 { break ListLoop } } m.Equal(len(inputDomains), len(outputDomains)) for _, domain := range inputDomains { m.Equal(domain, outputDomains[domain.Info.ID]) } } // CreateDomain helper method func (m *MetadataPersistenceSuiteV2) CreateDomain( ctx context.Context, info *p.DomainInfo, config *p.DomainConfig, replicationConfig *p.DomainReplicationConfig, isGlobaldomain bool, configVersion int64, failoverVersion int64, lastUpdateTime int64, ) (*p.CreateDomainResponse, error) { return m.DomainManager.CreateDomain(ctx, &p.CreateDomainRequest{ Info: info, Config: config, ReplicationConfig: replicationConfig, IsGlobalDomain: isGlobaldomain, ConfigVersion: configVersion, FailoverVersion: failoverVersion, LastUpdatedTime: lastUpdateTime, }) } // GetDomain helper method func (m *MetadataPersistenceSuiteV2) GetDomain(ctx context.Context, id, name string) (*p.GetDomainResponse, error) { return m.DomainManager.GetDomain(ctx, &p.GetDomainRequest{ ID: id, Name: name, }) } // UpdateDomain helper method func (m *MetadataPersistenceSuiteV2) UpdateDomain( ctx context.Context, info *p.DomainInfo, config *p.DomainConfig, replicationConfig *p.DomainReplicationConfig, configVersion int64, failoverVersion int64, failoverNotificationVersion int64, PreviousFailoverVersion int64, failoverEndTime *int64, notificationVersion int64, lastUpdateTime int64, ) error { return m.DomainManager.UpdateDomain(ctx, &p.UpdateDomainRequest{ Info: info, Config: config, ReplicationConfig: replicationConfig, FailoverEndTime: failoverEndTime, ConfigVersion: configVersion, FailoverVersion: failoverVersion, FailoverNotificationVersion: failoverNotificationVersion, PreviousFailoverVersion: PreviousFailoverVersion, NotificationVersion: notificationVersion, LastUpdatedTime: lastUpdateTime, }) } // DeleteDomain helper method func (m *MetadataPersistenceSuiteV2) DeleteDomain(ctx context.Context, id, name string) error { if len(id) > 0 { return m.DomainManager.DeleteDomain(ctx, &p.DeleteDomainRequest{ID: id}) } return m.DomainManager.DeleteDomainByName(ctx, &p.DeleteDomainByNameRequest{Name: name}) } // ListDomains helper method func (m *MetadataPersistenceSuiteV2) ListDomains(ctx context.Context, pageSize int, pageToken []byte) (*p.ListDomainsResponse, error) { return m.DomainManager.ListDomains(ctx, &p.ListDomainsRequest{ PageSize: pageSize, NextPageToken: pageToken, }) } ================================================ FILE: common/persistence/persistence-tests/persistenceTestBase.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "fmt" "math" "math/rand" "sync/atomic" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/client" "github.com/uber/cadence/common/persistence/nosql" "github.com/uber/cadence/common/persistence/persistence-tests/testcluster" "github.com/uber/cadence/common/persistence/sql" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" ) type ( // TransferTaskIDGenerator generates IDs for transfer tasks written by helper methods TransferTaskIDGenerator interface { GenerateTransferTaskID() (int64, error) } // TestBaseOptions options to configure workflow test base. TestBaseOptions struct { DBPluginName string DBName string DBUsername string DBPassword string DBHost string DBPort int `yaml:"-"` StoreType string `yaml:"-"` SchemaDir string `yaml:"-"` ClusterMetadata cluster.Metadata `yaml:"-"` ProtoVersion int `yaml:"-"` Replicas int `yaml:"-"` MaxConns int `yaml:"-"` } // TestBase wraps the base setup needed to create workflows over persistence layer. TestBase struct { *suite.Suite Controller *gomock.Controller ShardMgr persistence.ShardManager ExecutionMgrFactory client.Factory ExecutionManager persistence.ExecutionManager TaskMgr persistence.TaskManager HistoryV2Mgr persistence.HistoryManager DomainManager persistence.DomainManager DomainReplicationQueueMgr persistence.QueueManager ShardInfo *persistence.ShardInfo TaskIDGenerator TransferTaskIDGenerator ClusterMetadata cluster.Metadata DefaultTestCluster testcluster.PersistenceTestCluster VisibilityTestCluster testcluster.PersistenceTestCluster Logger log.Logger PayloadSerializer persistence.PayloadSerializer ConfigStoreManager persistence.ConfigStoreManager DynamicConfiguration persistence.DynamicConfiguration } // TestBaseParams defines the input of TestBase TestBaseParams struct { DefaultTestCluster testcluster.PersistenceTestCluster VisibilityTestCluster testcluster.PersistenceTestCluster ClusterMetadata cluster.Metadata DynamicConfiguration persistence.DynamicConfiguration } // TestTransferTaskIDGenerator helper TestTransferTaskIDGenerator struct { seqNum int64 } ) const ( defaultScheduleToStartTimeout = 111 ) // NewTestBaseFromParams returns a customized test base from given input func NewTestBaseFromParams(t *testing.T, params TestBaseParams) *TestBase { res := &TestBase{ Suite: &suite.Suite{}, DefaultTestCluster: params.DefaultTestCluster, VisibilityTestCluster: params.VisibilityTestCluster, ClusterMetadata: params.ClusterMetadata, PayloadSerializer: persistence.NewPayloadSerializer(), DynamicConfiguration: params.DynamicConfiguration, } res.SetT(t) return res } // NewTestBaseWithNoSQL returns a persistence test base backed by nosql datastore func NewTestBaseWithNoSQL(t *testing.T, options *TestBaseOptions) *TestBase { if options.DBName == "" { options.DBName = "test_" + GenerateRandomDBName(10) } testCluster := nosql.NewTestCluster(t, nosql.TestClusterParams{ PluginName: options.DBPluginName, KeySpace: options.DBName, Username: options.DBUsername, Password: options.DBPassword, Host: options.DBHost, Port: options.DBPort, ProtoVersion: options.ProtoVersion, Replicas: options.Replicas, MaxConns: options.MaxConns, }) metadata := options.ClusterMetadata if metadata.GetCurrentClusterName() == "" { metadata = cluster.GetTestClusterMetadata(false) } dc := persistence.DynamicConfiguration{ EnableSQLAsyncTransaction: dynamicproperties.GetBoolPropertyFn(false), EnableCassandraAllConsistencyLevelDelete: dynamicproperties.GetBoolPropertyFn(true), PersistenceSampleLoggingRate: dynamicproperties.GetIntPropertyFn(100), EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(true), EnableHistoryTaskDualWriteMode: dynamicproperties.GetBoolPropertyFn(true), ReadNoSQLHistoryTaskFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(true), DomainAuditLogTTL: func(domainID string) time.Duration { return time.Hour * 24 * 365 }, // 1 year default HistoryNodeDeleteBatchSize: dynamicproperties.GetIntPropertyFn(1000), } params := TestBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, ClusterMetadata: metadata, DynamicConfiguration: dc, } return NewTestBaseFromParams(t, params) } // NewTestBaseWithSQL returns a new persistence test base backed by SQL func NewTestBaseWithSQL(t *testing.T, options *TestBaseOptions) *TestBase { if options.DBName == "" { options.DBName = "test_" + GenerateRandomDBName(10) } testCluster, err := sql.NewTestCluster(options.DBPluginName, options.DBName, options.DBUsername, options.DBPassword, options.DBHost, options.DBPort, options.SchemaDir) if err != nil { t.Fatal(err) } metadata := options.ClusterMetadata if metadata.GetCurrentClusterName() == "" { metadata = cluster.GetTestClusterMetadata(false) } dc := persistence.DynamicConfiguration{ EnableSQLAsyncTransaction: dynamicproperties.GetBoolPropertyFn(false), EnableCassandraAllConsistencyLevelDelete: dynamicproperties.GetBoolPropertyFn(true), PersistenceSampleLoggingRate: dynamicproperties.GetIntPropertyFn(100), EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(true), EnableHistoryTaskDualWriteMode: dynamicproperties.GetBoolPropertyFn(true), ReadNoSQLHistoryTaskFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(true), DomainAuditLogTTL: func(domainID string) time.Duration { return time.Hour * 24 * 365 }, // 1 year default HistoryNodeDeleteBatchSize: dynamicproperties.GetIntPropertyFn(1000), } params := TestBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, ClusterMetadata: metadata, DynamicConfiguration: dc, } return NewTestBaseFromParams(t, params) } // Config returns the persistence configuration for this test func (s *TestBase) Config() config.Persistence { cfg := s.DefaultTestCluster.Config() if s.DefaultTestCluster == s.VisibilityTestCluster { return cfg } vCfg := s.VisibilityTestCluster.Config() cfg.VisibilityStore = "visibility_ " + vCfg.VisibilityStore cfg.DataStores[cfg.VisibilityStore] = vCfg.DataStores[vCfg.VisibilityStore] return cfg } // Setup sets up the test base, must be called as part of SetupSuite func (s *TestBase) Setup() { var err error shardID := 10 clusterName := s.ClusterMetadata.GetCurrentClusterName() s.Controller = gomock.NewController(s.T()) s.Logger = testlogger.New(s.T()) s.DefaultTestCluster.SetupTestDatabase() cfg := s.DefaultTestCluster.Config() scope := tally.NewTestScope(service.History, make(map[string]string)) metricsClient := metrics.NewClient(scope, service.GetMetricsServiceIdx(service.History, s.Logger), metrics.HistogramMigration{}) factory := client.NewFactory(&cfg, nil, clusterName, metricsClient, s.Logger, &s.DynamicConfiguration) s.TaskMgr, err = factory.NewTaskManager() s.fatalOnError("NewTaskManager", err) s.DomainManager, err = factory.NewDomainManager() s.fatalOnError("NewDomainManager", err) s.HistoryV2Mgr, err = factory.NewHistoryManager() s.fatalOnError("NewHistoryManager", err) s.ShardMgr, err = factory.NewShardManager() s.fatalOnError("NewShardManager", err) s.ConfigStoreManager, err = factory.NewConfigStoreManager() s.fatalOnError("NewConfigStoreManager", err) s.ExecutionMgrFactory = factory s.ExecutionManager, err = factory.NewExecutionManager(shardID) s.fatalOnError("NewExecutionManager", err) domainFilter := &types.DomainFilter{ DomainIDs: []string{}, ReverseMatch: true, } transferPQSMap := map[string][]*types.ProcessingQueueState{ s.ClusterMetadata.GetCurrentClusterName(): { &types.ProcessingQueueState{ Level: common.Int32Ptr(0), AckLevel: common.Int64Ptr(0), MaxLevel: common.Int64Ptr(0), DomainFilter: domainFilter, }, }, } transferPQS := types.ProcessingQueueStates{StatesByCluster: transferPQSMap} timerPQSMap := map[string][]*types.ProcessingQueueState{ s.ClusterMetadata.GetCurrentClusterName(): { &types.ProcessingQueueState{ Level: common.Int32Ptr(0), AckLevel: common.Int64Ptr(time.Now().UnixNano()), MaxLevel: common.Int64Ptr(time.Now().UnixNano()), DomainFilter: domainFilter, }, }, } timerPQS := types.ProcessingQueueStates{StatesByCluster: timerPQSMap} s.ShardInfo = &persistence.ShardInfo{ ShardID: shardID, RangeID: 0, TransferAckLevel: 0, ReplicationAckLevel: 0, TimerAckLevel: time.Time{}, ClusterTimerAckLevel: map[string]time.Time{clusterName: time.Time{}}, ClusterTransferAckLevel: map[string]int64{clusterName: 0}, TransferProcessingQueueStates: &transferPQS, TimerProcessingQueueStates: &timerPQS, } s.TaskIDGenerator = &TestTransferTaskIDGenerator{} err = s.ShardMgr.CreateShard(context.Background(), &persistence.CreateShardRequest{ShardInfo: s.ShardInfo}) s.fatalOnError("CreateShard", err) queue, err := factory.NewDomainReplicationQueueManager() s.fatalOnError("Create DomainReplicationQueue", err) s.DomainReplicationQueueMgr = queue } func (s *TestBase) fatalOnError(msg string, err error) { if err != nil { s.Logger.Fatal(msg, tag.Error(err)) } } // CreateShard is a utility method to create the shard using persistence layer func (s *TestBase) CreateShard(ctx context.Context, shardID int, owner string, rangeID int64) error { info := &persistence.ShardInfo{ ShardID: shardID, Owner: owner, RangeID: rangeID, } return s.ShardMgr.CreateShard(ctx, &persistence.CreateShardRequest{ ShardInfo: info, }) } // GetShard is a utility method to get the shard using persistence layer func (s *TestBase) GetShard(ctx context.Context, shardID int) (*persistence.ShardInfo, error) { response, err := s.ShardMgr.GetShard(ctx, &persistence.GetShardRequest{ ShardID: shardID, }) if err != nil { return nil, err } return response.ShardInfo, nil } // UpdateShard is a utility method to update the shard using persistence layer func (s *TestBase) UpdateShard(ctx context.Context, updatedInfo *persistence.ShardInfo, previousRangeID int64) error { return s.ShardMgr.UpdateShard(ctx, &persistence.UpdateShardRequest{ ShardInfo: updatedInfo, PreviousRangeID: previousRangeID, }) } // CreateWorkflowExecutionWithBranchToken test util function func (s *TestBase) CreateWorkflowExecutionWithBranchToken( ctx context.Context, domainID string, workflowExecution types.WorkflowExecution, taskList string, wType string, wTimeout int32, decisionTimeout int32, executionContext []byte, nextEventID int64, lastProcessedEventID int64, decisionScheduleID int64, branchToken []byte, timerTasks []persistence.Task, partitionConfig map[string]string, ) (*persistence.CreateWorkflowExecutionResponse, error) { now := time.Now() versionHistory := persistence.NewVersionHistory(branchToken, []*persistence.VersionHistoryItem{ { EventID: decisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := persistence.NewVersionHistories(versionHistory) response, err := s.ExecutionManager.CreateWorkflowExecution(ctx, &persistence.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), FirstExecutionRunID: workflowExecution.GetRunID(), TaskList: taskList, WorkflowTypeName: wType, WorkflowTimeout: wTimeout, DecisionStartToCloseTimeout: decisionTimeout, ExecutionContext: executionContext, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, LastUpdatedTimestamp: now, StartTimestamp: now, DecisionScheduleID: decisionScheduleID, DecisionStartedID: constants.EmptyEventID, DecisionTimeout: 1, BranchToken: branchToken, PartitionConfig: partitionConfig, }, ExecutionStats: &persistence.ExecutionStats{}, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: []persistence.Task{ &persistence.DecisionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: persistence.TaskData{ TaskID: s.GetNextSequenceNumber(), VisibilityTimestamp: time.Now(), }, TargetDomainID: domainID, TaskList: taskList, ScheduleID: decisionScheduleID, }, }, persistence.HistoryTaskCategoryTimer: timerTasks, }, Checksum: testWorkflowChecksum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, }) return response, err } // CreateWorkflowExecution is a utility method to create workflow executions func (s *TestBase) CreateWorkflowExecution( ctx context.Context, domainID string, workflowExecution types.WorkflowExecution, taskList string, wType string, wTimeout int32, decisionTimeout int32, executionContext []byte, nextEventID int64, lastProcessedEventID int64, decisionScheduleID int64, timerTasks []persistence.Task, partitionConfig map[string]string, ) (*persistence.CreateWorkflowExecutionResponse, error) { return s.CreateWorkflowExecutionWithBranchToken(ctx, domainID, workflowExecution, taskList, wType, wTimeout, decisionTimeout, executionContext, nextEventID, lastProcessedEventID, decisionScheduleID, nil, timerTasks, partitionConfig) } // CreateChildWorkflowExecution is a utility method to create child workflow executions func (s *TestBase) CreateChildWorkflowExecution(ctx context.Context, domainID string, workflowExecution types.WorkflowExecution, parentDomainID string, parentExecution types.WorkflowExecution, initiatedID int64, taskList, wType string, wTimeout int32, decisionTimeout int32, executionContext []byte, nextEventID int64, lastProcessedEventID int64, decisionScheduleID int64, timerTasks []persistence.Task, partitionConfig map[string]string) (*persistence.CreateWorkflowExecutionResponse, error) { now := time.Now() versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ { EventID: decisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := persistence.NewVersionHistories(versionHistory) response, err := s.ExecutionManager.CreateWorkflowExecution(ctx, &persistence.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), FirstExecutionRunID: workflowExecution.GetRunID(), ParentDomainID: parentDomainID, ParentWorkflowID: parentExecution.GetWorkflowID(), ParentRunID: parentExecution.GetRunID(), InitiatedID: initiatedID, TaskList: taskList, WorkflowTypeName: wType, WorkflowTimeout: wTimeout, DecisionStartToCloseTimeout: decisionTimeout, ExecutionContext: executionContext, State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, LastUpdatedTimestamp: now, StartTimestamp: now, DecisionScheduleID: decisionScheduleID, DecisionStartedID: constants.EmptyEventID, DecisionTimeout: 1, PartitionConfig: partitionConfig, }, ExecutionStats: &persistence.ExecutionStats{}, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: []persistence.Task{ &persistence.DecisionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, TaskData: persistence.TaskData{ TaskID: s.GetNextSequenceNumber(), }, TargetDomainID: domainID, TaskList: taskList, ScheduleID: decisionScheduleID, }, }, persistence.HistoryTaskCategoryTimer: timerTasks, }, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, DomainName: s.DomainManager.GetName(), }) return response, err } // GetWorkflowExecutionInfoWithStats is a utility method to retrieve execution info with size stats func (s *TestBase) GetWorkflowExecutionInfoWithStats(ctx context.Context, domainID string, workflowExecution types.WorkflowExecution) ( *persistence.MutableStateStats, *persistence.WorkflowMutableState, error) { response, err := s.ExecutionManager.GetWorkflowExecution(ctx, &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: workflowExecution, RangeID: s.ShardInfo.RangeID, }) if err != nil { return nil, nil, err } return response.MutableStateStats, response.State, nil } // GetWorkflowExecutionInfo is a utility method to retrieve execution info func (s *TestBase) GetWorkflowExecutionInfo(ctx context.Context, domainID string, workflowExecution types.WorkflowExecution) ( *persistence.WorkflowMutableState, error) { response, err := s.ExecutionManager.GetWorkflowExecution(ctx, &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: workflowExecution, RangeID: s.ShardInfo.RangeID, }) if err != nil { return nil, err } return response.State, nil } // GetCurrentWorkflowRunID returns the workflow run ID for the given params func (s *TestBase) GetCurrentWorkflowRunID(ctx context.Context, domainID, workflowID string) (string, error) { response, err := s.ExecutionManager.GetCurrentExecution(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, }) if err != nil { return "", err } return response.RunID, nil } // ContinueAsNewExecution is a utility method to create workflow executions func (s *TestBase) ContinueAsNewExecution( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, condition int64, newExecution types.WorkflowExecution, nextEventID, decisionScheduleID int64, prevResetPoints *types.ResetPoints, ) error { now := time.Now() newdecisionTask := &persistence.DecisionTask{ WorkflowIdentifier: p.WorkflowIdentifier{ DomainID: updatedInfo.DomainID, WorkflowID: updatedInfo.WorkflowID, RunID: updatedInfo.RunID, }, TaskData: persistence.TaskData{ TaskID: s.GetNextSequenceNumber(), }, TargetDomainID: updatedInfo.DomainID, TaskList: updatedInfo.TaskList, ScheduleID: int64(decisionScheduleID), } versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ { EventID: decisionScheduleID, Version: constants.EmptyVersion, }, }) versionHistories := persistence.NewVersionHistories(versionHistory) req := &persistence.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: []persistence.Task{ newdecisionTask, }, }, Condition: condition, UpsertActivityInfos: nil, DeleteActivityInfos: nil, UpsertTimerInfos: nil, DeleteTimerInfos: nil, VersionHistories: versionHistories, }, NewWorkflowSnapshot: &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: updatedInfo.DomainID, WorkflowID: newExecution.GetWorkflowID(), RunID: newExecution.GetRunID(), FirstExecutionRunID: updatedInfo.FirstExecutionRunID, TaskList: updatedInfo.TaskList, WorkflowTypeName: updatedInfo.WorkflowTypeName, WorkflowTimeout: updatedInfo.WorkflowTimeout, DecisionStartToCloseTimeout: updatedInfo.DecisionStartToCloseTimeout, ExecutionContext: nil, State: updatedInfo.State, CloseStatus: updatedInfo.CloseStatus, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: constants.EmptyEventID, LastUpdatedTimestamp: now, StartTimestamp: now, DecisionScheduleID: decisionScheduleID, DecisionStartedID: constants.EmptyEventID, DecisionTimeout: 1, AutoResetPoints: prevResetPoints, PartitionConfig: updatedInfo.PartitionConfig, }, ExecutionStats: updatedStats, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Encoding: pickRandomEncoding(), // To DO: next PR for UpdateWorkflowExecution // DomainName: s.DomainManager.GetName(), } req.UpdateWorkflowMutation.ExecutionInfo.State = persistence.WorkflowStateCompleted req.UpdateWorkflowMutation.ExecutionInfo.CloseStatus = persistence.WorkflowCloseStatusContinuedAsNew _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, req) return err } // UpdateWorkflowExecution is a utility method to update workflow execution func (s *TestBase) UpdateWorkflowExecution( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, decisionScheduleIDs []int64, activityScheduleIDs []int64, condition int64, timerTasks []persistence.Task, upsertActivityInfos []*persistence.ActivityInfo, deleteActivityInfos []int64, upsertTimerInfos []*persistence.TimerInfo, deleteTimerInfos []string, ) error { return s.UpdateWorkflowExecutionWithRangeID( ctx, updatedInfo, updatedStats, updatedVersionHistories, decisionScheduleIDs, activityScheduleIDs, s.ShardInfo.RangeID, condition, timerTasks, upsertActivityInfos, deleteActivityInfos, upsertTimerInfos, deleteTimerInfos, nil, nil, nil, nil, nil, nil, nil, nil, ) } // UpdateWorkflowExecutionAndFinish is a utility method to update workflow execution func (s *TestBase) UpdateWorkflowExecutionAndFinish( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, condition int64, versionHistories *persistence.VersionHistories, ) error { transferTasks := []persistence.Task{} transferTasks = append(transferTasks, &persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: updatedInfo.DomainID, WorkflowID: updatedInfo.WorkflowID, RunID: updatedInfo.RunID, }, TaskData: persistence.TaskData{TaskID: s.GetNextSequenceNumber()}, }) _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, &persistence.UpdateWorkflowExecutionRequest{ RangeID: s.ShardInfo.RangeID, UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: transferTasks, }, Condition: condition, UpsertActivityInfos: nil, DeleteActivityInfos: nil, UpsertTimerInfos: nil, DeleteTimerInfos: nil, VersionHistories: versionHistories, }, Encoding: pickRandomEncoding(), // To DO: next PR for UpdateWorkflowExecution // DomainName: s.DomainManager.GetName(), }) return err } // UpsertChildExecutionsState is a utility method to update mutable state of workflow execution func (s *TestBase) UpsertChildExecutionsState( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, condition int64, upsertChildInfos []*persistence.ChildExecutionInfo, ) error { return s.UpdateWorkflowExecutionWithRangeID( ctx, updatedInfo, updatedStats, updatedVersionHistories, nil, nil, s.ShardInfo.RangeID, condition, nil, nil, nil, nil, nil, upsertChildInfos, nil, nil, nil, nil, nil, nil, nil, ) } // UpsertRequestCancelState is a utility method to update mutable state of workflow execution func (s *TestBase) UpsertRequestCancelState( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, condition int64, upsertCancelInfos []*persistence.RequestCancelInfo, ) error { return s.UpdateWorkflowExecutionWithRangeID( ctx, updatedInfo, updatedStats, updatedVersionHistories, nil, nil, s.ShardInfo.RangeID, condition, nil, nil, nil, nil, nil, nil, nil, upsertCancelInfos, nil, nil, nil, nil, nil, ) } // UpsertSignalInfoState is a utility method to update mutable state of workflow execution func (s *TestBase) UpsertSignalInfoState( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, condition int64, upsertSignalInfos []*persistence.SignalInfo, ) error { return s.UpdateWorkflowExecutionWithRangeID( ctx, updatedInfo, updatedStats, updatedVersionHistories, nil, nil, s.ShardInfo.RangeID, condition, nil, nil, nil, nil, nil, nil, nil, nil, nil, upsertSignalInfos, nil, nil, nil, ) } // UpsertSignalsRequestedState is a utility method to update mutable state of workflow execution func (s *TestBase) UpsertSignalsRequestedState( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, condition int64, upsertSignalsRequested []string, ) error { return s.UpdateWorkflowExecutionWithRangeID( ctx, updatedInfo, updatedStats, updatedVersionHistories, nil, nil, s.ShardInfo.RangeID, condition, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, upsertSignalsRequested, nil, ) } // DeleteChildExecutionsState is a utility method to delete child execution from mutable state func (s *TestBase) DeleteChildExecutionsState( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, condition int64, deleteChildInfo int64, ) error { return s.UpdateWorkflowExecutionWithRangeID( ctx, updatedInfo, updatedStats, updatedVersionHistories, nil, nil, s.ShardInfo.RangeID, condition, nil, nil, nil, nil, nil, nil, []int64{deleteChildInfo}, nil, nil, nil, nil, nil, nil, ) } // DeleteCancelState is a utility method to delete request cancel state from mutable state func (s *TestBase) DeleteCancelState( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, condition int64, deleteCancelInfo int64, ) error { return s.UpdateWorkflowExecutionWithRangeID( ctx, updatedInfo, updatedStats, updatedVersionHistories, nil, nil, s.ShardInfo.RangeID, condition, nil, nil, nil, nil, nil, nil, nil, nil, []int64{deleteCancelInfo}, nil, nil, nil, nil, ) } // DeleteSignalState is a utility method to delete request cancel state from mutable state func (s *TestBase) DeleteSignalState( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, condition int64, deleteSignalInfo int64, ) error { return s.UpdateWorkflowExecutionWithRangeID( ctx, updatedInfo, updatedStats, updatedVersionHistories, nil, nil, s.ShardInfo.RangeID, condition, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, []int64{deleteSignalInfo}, nil, nil, ) } // DeleteSignalsRequestedState is a utility method to delete mutable state of workflow execution func (s *TestBase) DeleteSignalsRequestedState( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, condition int64, deleteSignalsRequestedIDs []string, ) error { return s.UpdateWorkflowExecutionWithRangeID( ctx, updatedInfo, updatedStats, updatedVersionHistories, nil, nil, s.ShardInfo.RangeID, condition, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, deleteSignalsRequestedIDs, ) } // UpdateWorklowStateAndReplication is a utility method to update workflow execution func (s *TestBase) UpdateWorklowStateAndReplication( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, condition int64, txTasks []persistence.Task, ) error { return s.UpdateWorkflowExecutionWithReplication( ctx, updatedInfo, updatedStats, updatedVersionHistories, nil, nil, s.ShardInfo.RangeID, condition, nil, txTasks, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, ) } // UpdateWorkflowExecutionWithRangeID is a utility method to update workflow execution func (s *TestBase) UpdateWorkflowExecutionWithRangeID( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, decisionScheduleIDs []int64, activityScheduleIDs []int64, rangeID int64, condition int64, timerTasks []persistence.Task, upsertActivityInfos []*persistence.ActivityInfo, deleteActivityInfos []int64, upsertTimerInfos []*persistence.TimerInfo, deleteTimerInfos []string, upsertChildInfos []*persistence.ChildExecutionInfo, deleteChildInfos []int64, upsertCancelInfos []*persistence.RequestCancelInfo, deleteCancelInfos []int64, upsertSignalInfos []*persistence.SignalInfo, deleteSignalInfos []int64, upsertSignalRequestedIDs []string, deleteSignalRequestedIDs []string, ) error { return s.UpdateWorkflowExecutionWithReplication( ctx, updatedInfo, updatedStats, updatedVersionHistories, decisionScheduleIDs, activityScheduleIDs, rangeID, condition, timerTasks, []persistence.Task{}, upsertActivityInfos, deleteActivityInfos, upsertTimerInfos, deleteTimerInfos, upsertChildInfos, deleteChildInfos, upsertCancelInfos, deleteCancelInfos, upsertSignalInfos, deleteSignalInfos, upsertSignalRequestedIDs, deleteSignalRequestedIDs, ) } // UpdateWorkflowExecutionWithReplication is a utility method to update workflow execution func (s *TestBase) UpdateWorkflowExecutionWithReplication( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, updatedVersionHistories *persistence.VersionHistories, decisionScheduleIDs []int64, activityScheduleIDs []int64, rangeID int64, condition int64, timerTasks []persistence.Task, txTasks []persistence.Task, upsertActivityInfos []*persistence.ActivityInfo, deleteActivityInfos []int64, upsertTimerInfos []*persistence.TimerInfo, deleteTimerInfos []string, upsertChildInfos []*persistence.ChildExecutionInfo, deleteChildInfos []int64, upsertCancelInfos []*persistence.RequestCancelInfo, deleteCancelInfos []int64, upsertSignalInfos []*persistence.SignalInfo, deleteSignalInfos []int64, upsertSignalRequestedIDs []string, deleteSignalRequestedIDs []string, ) error { // TODO: use separate fields for those three task types var transferTasks []persistence.Task var replicationTasks []persistence.Task for _, task := range txTasks { switch t := task.(type) { case *persistence.DecisionTask, *persistence.ActivityTask, *persistence.CloseExecutionTask, *persistence.RecordWorkflowClosedTask, *persistence.RecordChildExecutionCompletedTask, *persistence.CancelExecutionTask, *persistence.StartChildExecutionTask, *persistence.SignalExecutionTask, *persistence.RecordWorkflowStartedTask, *persistence.ResetWorkflowTask, *persistence.UpsertWorkflowSearchAttributesTask: transferTasks = append(transferTasks, t) case *persistence.HistoryReplicationTask, *persistence.SyncActivityTask: replicationTasks = append(replicationTasks, t) default: panic(fmt.Sprintf("Unknown transfer task type. %v", t)) } } for _, decisionScheduleID := range decisionScheduleIDs { transferTasks = append(transferTasks, &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: updatedInfo.DomainID, WorkflowID: updatedInfo.WorkflowID, RunID: updatedInfo.RunID, }, TaskData: persistence.TaskData{ TaskID: s.GetNextSequenceNumber(), }, TargetDomainID: updatedInfo.DomainID, TaskList: updatedInfo.TaskList, ScheduleID: int64(decisionScheduleID)}) } for _, activityScheduleID := range activityScheduleIDs { transferTasks = append(transferTasks, &persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: updatedInfo.DomainID, WorkflowID: updatedInfo.WorkflowID, RunID: updatedInfo.RunID, }, TaskData: persistence.TaskData{ TaskID: s.GetNextSequenceNumber(), }, TargetDomainID: updatedInfo.DomainID, TaskList: updatedInfo.TaskList, ScheduleID: int64(activityScheduleID)}) } _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, &persistence.UpdateWorkflowExecutionRequest{ RangeID: rangeID, UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, VersionHistories: updatedVersionHistories, UpsertActivityInfos: upsertActivityInfos, DeleteActivityInfos: deleteActivityInfos, UpsertTimerInfos: upsertTimerInfos, DeleteTimerInfos: deleteTimerInfos, UpsertChildExecutionInfos: upsertChildInfos, DeleteChildExecutionInfos: deleteChildInfos, UpsertRequestCancelInfos: upsertCancelInfos, DeleteRequestCancelInfos: deleteCancelInfos, UpsertSignalInfos: upsertSignalInfos, DeleteSignalInfos: deleteSignalInfos, UpsertSignalRequestedIDs: upsertSignalRequestedIDs, DeleteSignalRequestedIDs: deleteSignalRequestedIDs, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: transferTasks, persistence.HistoryTaskCategoryTimer: timerTasks, persistence.HistoryTaskCategoryReplication: replicationTasks, }, Condition: condition, Checksum: testWorkflowChecksum, }, Encoding: pickRandomEncoding(), }) return err } // UpdateWorkflowExecutionTasks is a utility method to update workflow tasks // with IgnoreCurrent update mode. func (s *TestBase) UpdateWorkflowExecutionTasks( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, condition int64, transferTasks []persistence.Task, timerTasks []persistence.Task, ) error { _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, &persistence.UpdateWorkflowExecutionRequest{ Mode: persistence.UpdateWorkflowModeIgnoreCurrent, UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: transferTasks, persistence.HistoryTaskCategoryTimer: timerTasks, }, Condition: condition, }, RangeID: s.ShardInfo.RangeID, Encoding: pickRandomEncoding(), }) return err } // UpdateWorkflowExecutionWithTransferTasks is a utility method to update workflow execution func (s *TestBase) UpdateWorkflowExecutionWithTransferTasks( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, condition int64, transferTasks []persistence.Task, upsertActivityInfo []*persistence.ActivityInfo, versionHistories *persistence.VersionHistories, ) error { _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, &persistence.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: transferTasks, }, Condition: condition, UpsertActivityInfos: upsertActivityInfo, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Encoding: pickRandomEncoding(), }) return err } // UpdateWorkflowExecutionForChildExecutionsInitiated is a utility method to update workflow execution func (s *TestBase) UpdateWorkflowExecutionForChildExecutionsInitiated( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, condition int64, transferTasks []persistence.Task, childInfos []*persistence.ChildExecutionInfo) error { _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, &persistence.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: transferTasks, }, Condition: condition, UpsertChildExecutionInfos: childInfos, }, RangeID: s.ShardInfo.RangeID, Encoding: pickRandomEncoding(), }) return err } // UpdateWorkflowExecutionForRequestCancel is a utility method to update workflow execution func (s *TestBase) UpdateWorkflowExecutionForRequestCancel( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, condition int64, transferTasks []persistence.Task, upsertRequestCancelInfo []*persistence.RequestCancelInfo) error { _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, &persistence.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: transferTasks, }, Condition: condition, UpsertRequestCancelInfos: upsertRequestCancelInfo, }, RangeID: s.ShardInfo.RangeID, Encoding: pickRandomEncoding(), }) return err } // UpdateWorkflowExecutionForSignal is a utility method to update workflow execution func (s *TestBase) UpdateWorkflowExecutionForSignal( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, condition int64, transferTasks []persistence.Task, upsertSignalInfos []*persistence.SignalInfo) error { _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, &persistence.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: transferTasks, }, Condition: condition, UpsertSignalInfos: upsertSignalInfos, }, RangeID: s.ShardInfo.RangeID, Encoding: pickRandomEncoding(), }) return err } // UpdateWorkflowExecutionForBufferEvents is a utility method to update workflow execution func (s *TestBase) UpdateWorkflowExecutionForBufferEvents( ctx context.Context, updatedInfo *persistence.WorkflowExecutionInfo, updatedStats *persistence.ExecutionStats, condition int64, bufferEvents []*types.HistoryEvent, clearBufferedEvents bool, versionHistories *persistence.VersionHistories, ) error { _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, &persistence.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, NewBufferedEvents: bufferEvents, Condition: condition, ClearBufferedEvents: clearBufferedEvents, VersionHistories: versionHistories, Checksum: testWorkflowChecksum, }, RangeID: s.ShardInfo.RangeID, Encoding: pickRandomEncoding(), }) return err } // UpdateAllMutableState is a utility method to update workflow execution func (s *TestBase) UpdateAllMutableState(ctx context.Context, updatedMutableState *persistence.WorkflowMutableState, condition int64) error { var aInfos []*persistence.ActivityInfo for _, ai := range updatedMutableState.ActivityInfos { aInfos = append(aInfos, ai) } var tInfos []*persistence.TimerInfo for _, ti := range updatedMutableState.TimerInfos { tInfos = append(tInfos, ti) } var cInfos []*persistence.ChildExecutionInfo for _, ci := range updatedMutableState.ChildExecutionInfos { cInfos = append(cInfos, ci) } var rcInfos []*persistence.RequestCancelInfo for _, rci := range updatedMutableState.RequestCancelInfos { rcInfos = append(rcInfos, rci) } var sInfos []*persistence.SignalInfo for _, si := range updatedMutableState.SignalInfos { sInfos = append(sInfos, si) } var srIDs []string for id := range updatedMutableState.SignalRequestedIDs { srIDs = append(srIDs, id) } _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, &persistence.UpdateWorkflowExecutionRequest{ RangeID: s.ShardInfo.RangeID, UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedMutableState.ExecutionInfo, ExecutionStats: updatedMutableState.ExecutionStats, Condition: condition, UpsertActivityInfos: aInfos, UpsertTimerInfos: tInfos, UpsertChildExecutionInfos: cInfos, UpsertRequestCancelInfos: rcInfos, UpsertSignalInfos: sInfos, UpsertSignalRequestedIDs: srIDs, VersionHistories: updatedMutableState.VersionHistories, Checksum: updatedMutableState.Checksum, }, Encoding: pickRandomEncoding(), }) return err } // ConflictResolveWorkflowExecution is utility method to reset mutable state func (s *TestBase) ConflictResolveWorkflowExecution( ctx context.Context, info *persistence.WorkflowExecutionInfo, stats *persistence.ExecutionStats, nextEventID int64, activityInfos []*persistence.ActivityInfo, timerInfos []*persistence.TimerInfo, childExecutionInfos []*persistence.ChildExecutionInfo, requestCancelInfos []*persistence.RequestCancelInfo, signalInfos []*persistence.SignalInfo, ids []string, versionHistories *persistence.VersionHistories, ) error { _, err := s.ExecutionManager.ConflictResolveWorkflowExecution(ctx, &persistence.ConflictResolveWorkflowExecutionRequest{ RangeID: s.ShardInfo.RangeID, ResetWorkflowSnapshot: persistence.WorkflowSnapshot{ ExecutionInfo: info, ExecutionStats: stats, Condition: nextEventID, ActivityInfos: activityInfos, TimerInfos: timerInfos, ChildExecutionInfos: childExecutionInfos, RequestCancelInfos: requestCancelInfos, SignalInfos: signalInfos, SignalRequestedIDs: ids, Checksum: testWorkflowChecksum, VersionHistories: versionHistories, }, Encoding: pickRandomEncoding(), }) return err } // DeleteWorkflowExecution is a utility method to delete a workflow execution func (s *TestBase) DeleteWorkflowExecution(ctx context.Context, info *persistence.WorkflowExecutionInfo) error { return s.ExecutionManager.DeleteWorkflowExecution(ctx, &persistence.DeleteWorkflowExecutionRequest{ DomainID: info.DomainID, WorkflowID: info.WorkflowID, RunID: info.RunID, }) } // DeleteCurrentWorkflowExecution is a utility method to delete the workflow current execution func (s *TestBase) DeleteCurrentWorkflowExecution(ctx context.Context, info *persistence.WorkflowExecutionInfo) error { return s.ExecutionManager.DeleteCurrentWorkflowExecution(ctx, &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: info.DomainID, WorkflowID: info.WorkflowID, RunID: info.RunID, }) } // GetTransferTasks is a utility method to get tasks from transfer task queue func (s *TestBase) GetTransferTasks(ctx context.Context, batchSize int, getAll bool) ([]persistence.Task, error) { result := []persistence.Task{} var token []byte Loop: for { response, err := s.ExecutionManager.GetHistoryTasks(ctx, &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(0), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(math.MaxInt64), PageSize: batchSize, NextPageToken: token, }) if err != nil { return nil, err } token = response.NextPageToken result = append(result, response.Tasks...) if len(token) == 0 || !getAll { break Loop } } return result, nil } // GetCrossClusterTasks is a utility method to get tasks from transfer task queue func (s *TestBase) GetCrossClusterTasks(ctx context.Context, targetCluster string, readLevel int64, batchSize int, getAll bool) ([]*persistence.CrossClusterTaskInfo, error) { return nil, nil } // GetReplicationTasks is a utility method to get tasks from replication task queue func (s *TestBase) GetReplicationTasks(ctx context.Context, batchSize int, getAll bool) ([]persistence.Task, error) { result := []persistence.Task{} var token []byte Loop: for { response, err := s.ExecutionManager.GetHistoryTasks(ctx, &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(0), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(math.MaxInt64), PageSize: batchSize, NextPageToken: token, }) if err != nil { return nil, err } token = response.NextPageToken result = append(result, response.Tasks...) if len(token) == 0 || !getAll { break Loop } } return result, nil } // RangeCompleteReplicationTask is a utility method to complete a range of replication tasks func (s *TestBase) RangeCompleteReplicationTask(ctx context.Context, exclusiveEndTaskID int64) error { for { resp, err := s.ExecutionManager.RangeCompleteHistoryTask(ctx, &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(exclusiveEndTaskID), PageSize: 1, }) if err != nil { return err } if !persistence.HasMoreRowsToDelete(resp.TasksCompleted, 1) { break } } return nil } // PutReplicationTaskToDLQ is a utility method to insert a replication task info func (s *TestBase) PutReplicationTaskToDLQ( ctx context.Context, sourceCluster string, taskInfo *persistence.ReplicationTaskInfo, ) error { return s.ExecutionManager.PutReplicationTaskToDLQ(ctx, &persistence.PutReplicationTaskToDLQRequest{ SourceClusterName: sourceCluster, TaskInfo: taskInfo, }) } // GetReplicationTasksFromDLQ is a utility method to read replication task info func (s *TestBase) GetReplicationTasksFromDLQ( ctx context.Context, sourceCluster string, readLevel int64, maxReadLevel int64, pageSize int, pageToken []byte, ) (*persistence.GetHistoryTasksResponse, error) { return s.ExecutionManager.GetReplicationTasksFromDLQ(ctx, &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: sourceCluster, ReadLevel: readLevel, MaxReadLevel: maxReadLevel, BatchSize: pageSize, NextPageToken: pageToken, }) } // GetReplicationDLQSize is a utility method to read replication dlq size func (s *TestBase) GetReplicationDLQSize( ctx context.Context, sourceCluster string, ) (*persistence.GetReplicationDLQSizeResponse, error) { return s.ExecutionManager.GetReplicationDLQSize(ctx, &persistence.GetReplicationDLQSizeRequest{ SourceClusterName: sourceCluster, }) } // DeleteReplicationTaskFromDLQ is a utility method to delete a replication task info func (s *TestBase) DeleteReplicationTaskFromDLQ( ctx context.Context, sourceCluster string, taskID int64, ) error { return s.ExecutionManager.DeleteReplicationTaskFromDLQ(ctx, &persistence.DeleteReplicationTaskFromDLQRequest{ SourceClusterName: sourceCluster, TaskID: taskID, }) } // RangeDeleteReplicationTaskFromDLQ is a utility method to delete replication task info func (s *TestBase) RangeDeleteReplicationTaskFromDLQ( ctx context.Context, sourceCluster string, beginTaskID int64, endTaskID int64, ) error { _, err := s.ExecutionManager.RangeDeleteReplicationTaskFromDLQ(ctx, &persistence.RangeDeleteReplicationTaskFromDLQRequest{ SourceClusterName: sourceCluster, InclusiveBeginTaskID: beginTaskID, ExclusiveEndTaskID: endTaskID, }) return err } // CreateFailoverMarkers is a utility method to create failover markers func (s *TestBase) CreateFailoverMarkers( ctx context.Context, markers []*persistence.FailoverMarkerTask, ) error { return s.ExecutionManager.CreateFailoverMarkerTasks(ctx, &persistence.CreateFailoverMarkersRequest{ RangeID: s.ShardInfo.RangeID, Markers: markers, }) } // CompleteTransferTask is a utility method to complete a transfer task func (s *TestBase) CompleteTransferTask(ctx context.Context, taskID int64) error { return s.ExecutionManager.CompleteHistoryTask(ctx, &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, TaskKey: persistence.NewImmediateTaskKey(taskID), }) } // RangeCompleteTransferTask is a utility method to complete a range of transfer tasks func (s *TestBase) RangeCompleteTransferTask(ctx context.Context, inclusiveBeginTaskID int64, exclusiveEndTaskID int64) error { for { resp, err := s.ExecutionManager.RangeCompleteHistoryTask(ctx, &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(inclusiveBeginTaskID), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(exclusiveEndTaskID), PageSize: 1, }) if err != nil { return err } if !persistence.HasMoreRowsToDelete(resp.TasksCompleted, 1) { break } } return nil } // CompleteCrossClusterTask is a utility method to complete a cross-cluster task func (s *TestBase) CompleteCrossClusterTask(ctx context.Context, targetCluster string, taskID int64) error { return nil } // RangeCompleteCrossClusterTask is a utility method to complete a range of cross-cluster tasks func (s *TestBase) RangeCompleteCrossClusterTask(ctx context.Context, targetCluster string, exclusiveBeginTaskID int64, inclusiveEndTaskID int64) error { return nil } // CompleteReplicationTask is a utility method to complete a replication task func (s *TestBase) CompleteReplicationTask(ctx context.Context, taskID int64) error { return s.ExecutionManager.CompleteHistoryTask(ctx, &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, TaskKey: persistence.NewImmediateTaskKey(taskID), }) } // GetTimerIndexTasks is a utility method to get tasks from transfer task queue func (s *TestBase) GetTimerIndexTasks(ctx context.Context, batchSize int, getAll bool) ([]persistence.Task, error) { result := []persistence.Task{} var token []byte Loop: for { response, err := s.ExecutionManager.GetHistoryTasks(ctx, &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 0), PageSize: batchSize, NextPageToken: token, }) if err != nil { return nil, err } token = response.NextPageToken result = append(result, response.Tasks...) if len(token) == 0 || !getAll { break Loop } } return result, nil } // CompleteTimerTask is a utility method to complete a timer task func (s *TestBase) CompleteTimerTask(ctx context.Context, ts time.Time, taskID int64) error { return s.ExecutionManager.CompleteHistoryTask(ctx, &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, TaskKey: persistence.NewHistoryTaskKey(ts, taskID), }) } // RangeCompleteTimerTask is a utility method to complete a range of timer tasks func (s *TestBase) RangeCompleteTimerTask(ctx context.Context, inclusiveBeginTimestamp time.Time, exclusiveEndTimestamp time.Time) error { for { resp, err := s.ExecutionManager.RangeCompleteHistoryTask(ctx, &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(inclusiveBeginTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(exclusiveEndTimestamp, 0), PageSize: 1, }) if err != nil { return err } if !persistence.HasMoreRowsToDelete(resp.TasksCompleted, 1) { break } } return nil } // CreateDecisionTask is a utility method to create a task func (s *TestBase) CreateDecisionTask(ctx context.Context, domainID string, workflowExecution types.WorkflowExecution, taskList string, decisionScheduleID int64, partitionConfig map[string]string) (int64, error) { leaseResponse, err := s.TaskMgr.LeaseTaskList(ctx, &persistence.LeaseTaskListRequest{ DomainID: domainID, TaskList: taskList, TaskType: persistence.TaskListTypeDecision, }) if err != nil { return 0, err } // clearing this field since when creating task in matching we don't have the LastUpdate information leaseResponse.TaskListInfo.LastUpdated = time.Time{} taskID := s.GetNextSequenceNumber() tasks := []*persistence.CreateTaskInfo{ { TaskID: taskID, Data: &persistence.TaskInfo{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, TaskID: taskID, ScheduleID: decisionScheduleID, PartitionConfig: partitionConfig, }, }, } _, err = s.TaskMgr.CreateTasks(ctx, &persistence.CreateTasksRequest{ TaskListInfo: leaseResponse.TaskListInfo, Tasks: tasks, }) if err != nil { return 0, err } return taskID, err } func (s *TestBase) GetDecisionTaskListSize(ctx context.Context, domainID, taskList string, ackLevel int64) (int64, error) { resp, err := s.TaskMgr.GetTaskListSize(ctx, &persistence.GetTaskListSizeRequest{ DomainID: domainID, TaskListName: taskList, TaskListType: persistence.TaskListTypeDecision, AckLevel: ackLevel, }) if err != nil { return 0, err } return resp.Size, nil } // CreateActivityTasks is a utility method to create tasks func (s *TestBase) CreateActivityTasks(ctx context.Context, domainID string, workflowExecution types.WorkflowExecution, activities map[int64]string, partitionConfig map[string]string) ([]int64, error) { taskLists := make(map[string]*persistence.TaskListInfo) for _, tl := range activities { _, ok := taskLists[tl] if !ok { resp, err := s.TaskMgr.LeaseTaskList( ctx, &persistence.LeaseTaskListRequest{DomainID: domainID, TaskList: tl, TaskType: persistence.TaskListTypeActivity}) if err != nil { return []int64{}, err } taskLists[tl] = resp.TaskListInfo // clearing this field since when creating task in matching we don't have the LastUpdate information taskLists[tl].LastUpdated = time.Time{} } } var taskIDs []int64 for activityScheduleID, taskList := range activities { taskID := s.GetNextSequenceNumber() tasks := []*persistence.CreateTaskInfo{ { TaskID: taskID, Data: &persistence.TaskInfo{ DomainID: domainID, WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, TaskID: taskID, ScheduleID: activityScheduleID, ScheduleToStartTimeoutSeconds: defaultScheduleToStartTimeout, PartitionConfig: partitionConfig, }, }, } _, err := s.TaskMgr.CreateTasks(ctx, &persistence.CreateTasksRequest{ TaskListInfo: taskLists[taskList], Tasks: tasks, }) if err != nil { return nil, err } taskIDs = append(taskIDs, taskID) } return taskIDs, nil } // GetTasks is a utility method to get tasks from persistence func (s *TestBase) GetTasks(ctx context.Context, domainID, taskList string, taskType int, batchSize int) (*persistence.GetTasksResponse, error) { response, err := s.TaskMgr.GetTasks(ctx, &persistence.GetTasksRequest{ DomainID: domainID, TaskList: taskList, TaskType: taskType, BatchSize: batchSize, MaxReadLevel: common.Int64Ptr(math.MaxInt64), }) if err != nil { return nil, err } return &persistence.GetTasksResponse{Tasks: response.Tasks}, nil } // CompleteTask is a utility method to complete a task func (s *TestBase) CompleteTask(ctx context.Context, domainID, taskList string, taskType int, taskID int64, ackLevel int64) error { return s.TaskMgr.CompleteTask(ctx, &persistence.CompleteTaskRequest{ TaskList: &persistence.TaskListInfo{ DomainID: domainID, AckLevel: ackLevel, TaskType: taskType, Name: taskList, }, TaskID: taskID, }) } // TearDownWorkflowStore to cleanup func (s *TestBase) TearDownWorkflowStore() { s.ExecutionMgrFactory.Close() s.DefaultTestCluster.TearDownTestDatabase() } // GetNextSequenceNumber generates a unique sequence number for can be used for transfer queue taskId func (s *TestBase) GetNextSequenceNumber() int64 { taskID, _ := s.TaskIDGenerator.GenerateTransferTaskID() return taskID } // ClearTasks completes all transfer tasks and replication tasks func (s *TestBase) ClearTasks() { s.ClearTransferQueue() s.ClearReplicationQueue() } // ClearTransferQueue completes all tasks in transfer queue func (s *TestBase) ClearTransferQueue() { s.Logger.Info("Clearing transfer tasks", tag.ShardRangeID(s.ShardInfo.RangeID)) tasks, err := s.GetTransferTasks(context.Background(), 100, true) if err != nil { s.Logger.Fatal("Error during cleanup", tag.Error(err)) } counter := 0 for _, t := range tasks { s.Logger.Info("Deleting transfer task with ID", tag.TaskID(t.GetTaskID())) s.NoError(s.CompleteTransferTask(context.Background(), t.GetTaskID())) counter++ } s.Logger.Info("Deleted transfer tasks.", tag.Counter(counter)) } // ClearReplicationQueue completes all tasks in replication queue func (s *TestBase) ClearReplicationQueue() { s.Logger.Info("Clearing replication tasks", tag.ShardRangeID(s.ShardInfo.RangeID)) tasks, err := s.GetReplicationTasks(context.Background(), 100, true) if err != nil { s.Logger.Fatal("Error during cleanup", tag.Error(err)) } counter := 0 for _, t := range tasks { s.Logger.Info("Deleting replication task with ID", tag.TaskID(t.GetTaskID())) s.NoError(s.CompleteReplicationTask(context.Background(), t.GetTaskID())) counter++ } s.Logger.Info("Deleted replication tasks.", tag.Counter(counter)) } // EqualTimesWithPrecision assertion that two times are equal within precision func (s *TestBase) EqualTimesWithPrecision(t1, t2 time.Time, precision time.Duration) { s.True(timeComparator(t1, t2, precision), "Not equal: \n"+ "expected: %s\n"+ "actual : %s%s", t1, t2, ) } // EqualTimes assertion that two times are equal within two millisecond precision func (s *TestBase) EqualTimes(t1, t2 time.Time) { s.EqualTimesWithPrecision(t1, t2, TimePrecision) } func (s *TestBase) validateTimeRange(t time.Time, expectedDuration time.Duration) bool { currentTime := time.Now() diff := time.Duration(currentTime.UnixNano() - t.UnixNano()) if diff > expectedDuration { s.Logger.Info("Check Current time, Application time, Difference", tag.Timestamp(t), tag.CursorTimestamp(currentTime), tag.Number(int64(diff))) return false } return true } // GenerateTransferTaskID helper func (g *TestTransferTaskIDGenerator) GenerateTransferTaskID() (int64, error) { return atomic.AddInt64(&g.seqNum, 1), nil } // Publish is a utility method to add messages to the queue func (s *TestBase) Publish( ctx context.Context, messagePayload []byte, ) error { retryPolicy := backoff.NewExponentialRetryPolicy(100 * time.Millisecond) retryPolicy.SetBackoffCoefficient(1.5) retryPolicy.SetMaximumAttempts(5) throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(func(e error) bool { return persistence.IsTransientError(e) || isMessageIDConflictError(e) }), ) return throttleRetry.Do(ctx, func(ctx context.Context) error { return s.DomainReplicationQueueMgr.EnqueueMessage(ctx, &persistence.EnqueueMessageRequest{ MessagePayload: messagePayload, }) }) } func isMessageIDConflictError(err error) bool { _, ok := err.(*persistence.ConditionFailedError) return ok } // GetReplicationMessages is a utility method to get messages from the queue func (s *TestBase) GetReplicationMessages( ctx context.Context, lastMessageID int64, maxCount int, ) ([]*persistence.QueueMessage, error) { resp, err := s.DomainReplicationQueueMgr.ReadMessages(ctx, &persistence.ReadMessagesRequest{ LastMessageID: lastMessageID, MaxCount: maxCount, }) if err != nil { return nil, err } return resp.Messages, nil } // UpdateAckLevel updates replication queue ack level func (s *TestBase) UpdateAckLevel( ctx context.Context, lastProcessedMessageID int64, clusterName string, ) error { return s.DomainReplicationQueueMgr.UpdateAckLevel(ctx, &persistence.UpdateAckLevelRequest{ MessageID: lastProcessedMessageID, ClusterName: clusterName, }) } // GetAckLevels returns replication queue ack levels func (s *TestBase) GetAckLevels( ctx context.Context, ) (map[string]int64, error) { resp, err := s.DomainReplicationQueueMgr.GetAckLevels(ctx, &persistence.GetAckLevelsRequest{}) if err != nil { return nil, err } return resp.AckLevels, nil } // PublishToDomainDLQ is a utility method to add messages to the domain DLQ func (s *TestBase) PublishToDomainDLQ( ctx context.Context, messagePayload []byte, ) error { retryPolicy := backoff.NewExponentialRetryPolicy(100 * time.Millisecond) retryPolicy.SetBackoffCoefficient(1.5) retryPolicy.SetMaximumAttempts(5) throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(func(e error) bool { return persistence.IsTransientError(e) || isMessageIDConflictError(e) }), ) return throttleRetry.Do(ctx, func(ctx context.Context) error { return s.DomainReplicationQueueMgr.EnqueueMessageToDLQ(ctx, &persistence.EnqueueMessageToDLQRequest{ MessagePayload: messagePayload, }) }) } // GetMessagesFromDomainDLQ is a utility method to get messages from the domain DLQ func (s *TestBase) GetMessagesFromDomainDLQ( ctx context.Context, firstMessageID int64, lastMessageID int64, pageSize int, pageToken []byte, ) ([]*persistence.QueueMessage, []byte, error) { resp, err := s.DomainReplicationQueueMgr.ReadMessagesFromDLQ(ctx, &persistence.ReadMessagesFromDLQRequest{ FirstMessageID: firstMessageID, LastMessageID: lastMessageID, PageSize: pageSize, PageToken: pageToken, }) if err != nil { return nil, nil, err } return resp.Messages, resp.NextPageToken, nil } // UpdateDomainDLQAckLevel updates domain dlq ack level func (s *TestBase) UpdateDomainDLQAckLevel( ctx context.Context, lastProcessedMessageID int64, clusterName string, ) error { return s.DomainReplicationQueueMgr.UpdateDLQAckLevel(ctx, &persistence.UpdateDLQAckLevelRequest{ MessageID: lastProcessedMessageID, ClusterName: clusterName, }) } // GetDomainDLQAckLevel returns domain dlq ack level func (s *TestBase) GetDomainDLQAckLevel( ctx context.Context, ) (map[string]int64, error) { resp, err := s.DomainReplicationQueueMgr.GetDLQAckLevels(ctx, &persistence.GetDLQAckLevelsRequest{}) if err != nil { return nil, err } return resp.AckLevels, nil } // GetDomainDLQSize returns domain dlq size func (s *TestBase) GetDomainDLQSize( ctx context.Context, ) (int64, error) { resp, err := s.DomainReplicationQueueMgr.GetDLQSize(ctx, &persistence.GetDLQSizeRequest{}) if err != nil { return 0, err } return resp.Size, nil } // DeleteMessageFromDomainDLQ deletes one message from domain DLQ func (s *TestBase) DeleteMessageFromDomainDLQ( ctx context.Context, messageID int64, ) error { return s.DomainReplicationQueueMgr.DeleteMessageFromDLQ(ctx, &persistence.DeleteMessageFromDLQRequest{ MessageID: messageID, }) } // RangeDeleteMessagesFromDomainDLQ deletes messages from domain DLQ func (s *TestBase) RangeDeleteMessagesFromDomainDLQ( ctx context.Context, firstMessageID int64, lastMessageID int64, ) error { return s.DomainReplicationQueueMgr.RangeDeleteMessagesFromDLQ(ctx, &persistence.RangeDeleteMessagesFromDLQRequest{ FirstMessageID: firstMessageID, LastMessageID: lastMessageID, }) } // GenerateTransferTaskIDs helper func (g *TestTransferTaskIDGenerator) GenerateTransferTaskIDs(number int) ([]int64, error) { result := []int64{} for i := 0; i < number; i++ { id, err := g.GenerateTransferTaskID() if err != nil { return nil, err } result = append(result, id) } return result, nil } // GenerateRandomDBName helper func GenerateRandomDBName(n int) string { rand.Seed(time.Now().UnixNano()) letterRunes := []rune("workflow") b := make([]rune, n) for i := range b { b[i] = letterRunes[rand.Intn(len(letterRunes))] } ts := time.Now().Unix() return fmt.Sprintf("%v_%v", ts, string(b)) } func pickRandomEncoding() constants.EncodingType { // randomly pick json/thriftrw/empty as encoding type var encoding constants.EncodingType i := rand.Intn(3) switch i { case 0: encoding = constants.EncodingTypeJSON case 1: encoding = constants.EncodingTypeThriftRW case 2: encoding = constants.EncodingType("") } return encoding } func int64Ptr(i int64) *int64 { return &i } ================================================ FILE: common/persistence/persistence-tests/queuePersistenceTest.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "log" "os" "sync" "testing" "time" "github.com/stretchr/testify/require" ) type ( // QueuePersistenceSuite contains queue persistence tests QueuePersistenceSuite struct { *TestBase // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions } ) // SetupSuite implementation func (s *QueuePersistenceSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } // SetupTest implementation func (s *QueuePersistenceSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } // TearDownSuite implementation func (s *QueuePersistenceSuite) TearDownSuite() { s.TearDownWorkflowStore() } // TestDomainReplicationQueue tests domain replication queue operations func (s *QueuePersistenceSuite) TestDomainReplicationQueue() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() numMessages := 100 concurrentSenders := 10 messageChan := make(chan []byte, numMessages) var publishErrors []error var mu sync.Mutex go func() { for i := 0; i < numMessages; i++ { messageChan <- []byte{byte(i)} } close(messageChan) }() wg := sync.WaitGroup{} wg.Add(concurrentSenders) // Helper function for publishing with retry publishWithRetry := func(message []byte) error { var lastErr error for i := 0; i < 3; i++ { err := s.Publish(ctx, message) if err == nil { return nil } lastErr = err time.Sleep(20 * time.Millisecond) } return lastErr } // Concurrent publishing for i := 0; i < concurrentSenders; i++ { go func() { defer wg.Done() for message := range messageChan { err := publishWithRetry(message) if err != nil { mu.Lock() publishErrors = append(publishErrors, err) mu.Unlock() } } }() } wg.Wait() result, err := s.GetReplicationMessages(ctx, -1, numMessages) s.Nil(err, "GetReplicationMessages failed.") s.Len(result, numMessages, "Expected %d messages, got %d", numMessages, len(result)) // Verify message content messageSet := make(map[byte]bool) for _, msg := range result { messageSet[msg.Payload[0]] = true } s.Len(messageSet, numMessages, "Expected %d unique messages, got %d", numMessages, len(messageSet)) } // TestQueueMetadataOperations tests queue metadata operations func (s *QueuePersistenceSuite) TestQueueMetadataOperations() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() clusterAckLevels, err := s.GetAckLevels(ctx) s.Require().NoError(err) s.Assert().Len(clusterAckLevels, 0) err = s.UpdateAckLevel(ctx, 10, "test1") s.Require().NoError(err) clusterAckLevels, err = s.GetAckLevels(ctx) s.Require().NoError(err) s.Assert().Len(clusterAckLevels, 1) s.Assert().Equal(int64(10), clusterAckLevels["test1"]) err = s.UpdateAckLevel(ctx, 20, "test1") s.Require().NoError(err) clusterAckLevels, err = s.GetAckLevels(ctx) s.Require().NoError(err) s.Assert().Len(clusterAckLevels, 1) s.Assert().Equal(int64(20), clusterAckLevels["test1"]) err = s.UpdateAckLevel(ctx, 25, "test2") s.Require().NoError(err) err = s.UpdateAckLevel(ctx, 24, "test2") s.Require().NoError(err) clusterAckLevels, err = s.GetAckLevels(ctx) s.Require().NoError(err) s.Assert().Len(clusterAckLevels, 2) s.Assert().Equal(int64(20), clusterAckLevels["test1"]) s.Assert().Equal(int64(25), clusterAckLevels["test2"]) } // TestDomainReplicationDLQ tests domain DLQ operations func (s *QueuePersistenceSuite) TestDomainReplicationDLQ() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() maxMessageID := int64(100) numMessages := 100 concurrentSenders := 10 messageChan := make(chan []byte, numMessages) // Buffered channel var publishErrors []error var mu sync.Mutex go func() { for i := 0; i < numMessages; i++ { messageChan <- []byte{} } close(messageChan) }() wg := sync.WaitGroup{} wg.Add(concurrentSenders) // Helper function for publishing with retry publishWithRetry := func(message []byte) error { var lastErr error for i := 0; i < 3; i++ { err := s.PublishToDomainDLQ(ctx, message) if err == nil { return nil } lastErr = err time.Sleep(20 * time.Millisecond) } return lastErr } // Concurrent publishing for i := 0; i < concurrentSenders; i++ { go func() { defer wg.Done() for message := range messageChan { err := publishWithRetry(message) if err != nil { mu.Lock() publishErrors = append(publishErrors, err) mu.Unlock() } } }() } wg.Wait() // Check for any publish errors s.Empty(publishErrors, "Some messages failed to publish") // Verify initial message count size, err := s.GetDomainDLQSize(ctx) s.NoError(err, "GetDomainDLQSize failed") s.Equal(int64(numMessages), size, "Unexpected initial message count") result1, token, err := s.GetMessagesFromDomainDLQ(ctx, -1, maxMessageID, numMessages/2, nil) s.Nil(err, "GetReplicationMessages failed.") s.NotNil(token) result2, token, err := s.GetMessagesFromDomainDLQ(ctx, -1, maxMessageID, numMessages, token) s.Nil(err, "GetReplicationMessages failed.") s.Equal(len(token), 0) s.Equal(len(result1)+len(result2), numMessages, "Total messages retrieved mismatch") // Verify all messages were retrieved _, _, err = s.GetMessagesFromDomainDLQ(ctx, -1, 1<<63-1, numMessages, nil) s.NoError(err, "GetReplicationMessages failed.") s.Equal(len(token), 0) // Verify message count after retrieval size, err = s.GetDomainDLQSize(ctx) s.NoError(err, "GetDomainDLQSize failed") s.Equal(int64(numMessages), size, "Message count changed after retrieval") // Delete last message lastMessageID := result2[len(result2)-1].ID err = s.DeleteMessageFromDomainDLQ(ctx, lastMessageID) s.NoError(err, "Failed to delete message") // Verify message count after deletion size, err = s.GetDomainDLQSize(ctx) s.NoError(err, "GetDomainDLQSize failed") s.Equal(int64(numMessages-1), size, "Message count incorrect after deletion") // Get messages after deletion result3, token, err := s.GetMessagesFromDomainDLQ(ctx, -1, maxMessageID, numMessages, token) s.Nil(err, "GetReplicationMessages failed.") s.Equal(len(token), 0) s.Equal(len(result3), numMessages-1, "Unexpected number of messages after deletion") // Range delete remaining messages err = s.RangeDeleteMessagesFromDomainDLQ(ctx, -1, lastMessageID) s.NoError(err, "Failed to range delete messages") // Verify final message count size, err = s.GetDomainDLQSize(ctx) s.NoError(err, "GetDomainDLQSize failed") s.Equal(int64(0), size, "Messages not fully deleted") // Verify no messages remain result4, token, err := s.GetMessagesFromDomainDLQ(ctx, -1, maxMessageID, numMessages, token) s.Nil(err, "GetReplicationMessages failed.") s.Equal(len(token), 0) s.Equal(len(result4), 0, "Messages still exist after range deletion") } // TestDomainDLQMetadataOperations tests queue metadata operations func (s *QueuePersistenceSuite) TestDomainDLQMetadataOperations() { clusterName := "test" ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() ackLevel, err := s.GetDomainDLQAckLevel(ctx) s.Require().NoError(err) s.Equal(0, len(ackLevel)) err = s.UpdateDomainDLQAckLevel(ctx, 10, clusterName) s.NoError(err) ackLevel, err = s.GetDomainDLQAckLevel(ctx) s.Require().NoError(err) s.Equal(int64(10), ackLevel[clusterName]) err = s.UpdateDomainDLQAckLevel(ctx, 1, clusterName) s.NoError(err) ackLevel, err = s.GetDomainDLQAckLevel(ctx) s.Require().NoError(err) s.Equal(int64(10), ackLevel[clusterName]) } ================================================ FILE: common/persistence/persistence-tests/shardPersistenceTest.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "log" "os" "testing" "time" "github.com/stretchr/testify/require" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cluster" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // ShardPersistenceSuite contains shard persistence tests ShardPersistenceSuite struct { *TestBase // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions } ) // SetupSuite implementation func (s *ShardPersistenceSuite) SetupSuite() { if testing.Verbose() { log.SetOutput(os.Stdout) } } // SetupTest implementation func (s *ShardPersistenceSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } // TearDownSuite implementation func (s *ShardPersistenceSuite) TearDownSuite() { s.TearDownWorkflowStore() } // TestCreateShard test func (s *ShardPersistenceSuite) TestCreateShard() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() err0 := s.CreateShard(ctx, 19, "test_create_shard1", 123) s.Nil(err0, "No error expected.") err1 := s.CreateShard(ctx, 19, "test_create_shard2", 124) s.NotNil(err1, "expected non nil error.") s.IsType(&p.ShardAlreadyExistError{}, err1) s.T().Logf("CreateShard failed with error: %v\n", err1) } // TestGetShard test func (s *ShardPersistenceSuite) TestGetShard() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() shardID := 20 owner := "test_get_shard" rangeID := int64(131) err0 := s.CreateShard(ctx, shardID, owner, rangeID) s.Nil(err0, "No error expected.") shardInfo, err1 := s.GetShard(ctx, shardID) s.Nil(err1) s.NotNil(shardInfo) s.Equal(shardID, shardInfo.ShardID) s.Equal(owner, shardInfo.Owner) s.Equal(rangeID, shardInfo.RangeID) s.Equal(0, shardInfo.StolenSinceRenew) _, err2 := s.GetShard(ctx, 4766) s.NotNil(err2) s.IsType(&types.EntityNotExistsError{}, err2) s.T().Logf("GetShard failed with error: %v\n", err2) } // TestUpdateShard test func (s *ShardPersistenceSuite) TestUpdateShard() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() shardID := 30 owner := "test_update_shard" rangeID := int64(141) err0 := s.CreateShard(ctx, shardID, owner, rangeID) s.Nil(err0, "No error expected.") shardInfo, err1 := s.GetShard(ctx, shardID) s.Nil(err1) s.NotNil(shardInfo) s.Equal(shardID, shardInfo.ShardID) s.Equal(owner, shardInfo.Owner) s.Equal(rangeID, shardInfo.RangeID) s.Equal(0, shardInfo.StolenSinceRenew) updatedOwner := "updatedOwner" updatedRangeID := int64(142) updatedCurrentClusterTransferAckLevel := int64(1000) updatedAlternativeClusterTransferAckLevel := int64(2000) updatedCurrentClusterTimerAckLevel := time.Now() updatedAlternativeClusterTimerAckLevel := updatedCurrentClusterTimerAckLevel.Add(time.Minute) updatedReplicationAckLevel := int64(2000) updatedAlternativeClusterDLQAckLevel := int64(100) updatedStolenSinceRenew := 10 updatedInfo := shardInfo.ToNilSafeCopy() updatedInfo.Owner = updatedOwner updatedInfo.RangeID = updatedRangeID updatedInfo.TransferAckLevel = updatedCurrentClusterTransferAckLevel updatedInfo.ClusterTransferAckLevel = map[string]int64{ cluster.TestCurrentClusterName: updatedCurrentClusterTransferAckLevel, cluster.TestAlternativeClusterName: updatedAlternativeClusterTransferAckLevel, } updatedInfo.TransferProcessingQueueStates = createProcessingQueueStates( cluster.TestCurrentClusterName, 0, updatedCurrentClusterTransferAckLevel, cluster.TestAlternativeClusterName, 1, updatedAlternativeClusterTransferAckLevel, ) updatedInfo.TimerAckLevel = updatedCurrentClusterTimerAckLevel updatedInfo.ClusterTimerAckLevel = map[string]time.Time{ cluster.TestCurrentClusterName: updatedCurrentClusterTimerAckLevel, cluster.TestAlternativeClusterName: updatedAlternativeClusterTimerAckLevel, } updatedInfo.TimerProcessingQueueStates = createProcessingQueueStates( cluster.TestCurrentClusterName, 0, updatedCurrentClusterTimerAckLevel.UnixNano(), cluster.TestAlternativeClusterName, 1, updatedAlternativeClusterTimerAckLevel.UnixNano(), ) updatedInfo.ReplicationDLQAckLevel = map[string]int64{ cluster.TestAlternativeClusterName: updatedAlternativeClusterDLQAckLevel, } updatedInfo.QueueStates = map[int32]*types.QueueState{ p.HistoryTaskCategoryIDTransfer: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: &types.VirtualQueueState{ VirtualSliceStates: []*types.VirtualSliceState{ { TaskRange: &types.TaskRange{ InclusiveMin: &types.TaskKey{ TaskID: updatedCurrentClusterTransferAckLevel + 1, }, ExclusiveMax: &types.TaskKey{ TaskID: updatedCurrentClusterTransferAckLevel + 10, }, }, Predicate: &types.Predicate{ PredicateType: types.PredicateTypeDomainID, DomainIDPredicateAttributes: &types.DomainIDPredicateAttributes{ DomainIDs: []string{"domain1", "domain2"}, IsExclusive: common.Ptr(true), }, }, }, }, }, }, ExclusiveMaxReadLevel: &types.TaskKey{ TaskID: updatedCurrentClusterTransferAckLevel + 10, }, }, } updatedInfo.ReplicationAckLevel = updatedReplicationAckLevel updatedInfo.StolenSinceRenew = updatedStolenSinceRenew err2 := s.UpdateShard(ctx, updatedInfo, shardInfo.RangeID) s.Nil(err2) info1, err3 := s.GetShard(ctx, shardID) s.Nil(err3) s.NotNil(info1) s.Equal(updatedOwner, info1.Owner) s.Equal(updatedRangeID, info1.RangeID) s.Equal(updatedCurrentClusterTransferAckLevel, info1.TransferAckLevel) s.Equal(updatedInfo.ClusterTransferAckLevel, info1.ClusterTransferAckLevel) s.Equal(updatedInfo.TransferProcessingQueueStates, info1.TransferProcessingQueueStates) s.EqualTimes(updatedCurrentClusterTimerAckLevel, info1.TimerAckLevel) s.EqualTimes(updatedCurrentClusterTimerAckLevel, info1.ClusterTimerAckLevel[cluster.TestCurrentClusterName]) s.EqualTimes(updatedAlternativeClusterTimerAckLevel, info1.ClusterTimerAckLevel[cluster.TestAlternativeClusterName]) s.Equal(updatedInfo.TimerProcessingQueueStates, info1.TimerProcessingQueueStates) s.Equal(updatedReplicationAckLevel, info1.ReplicationAckLevel) s.Equal(updatedInfo.ReplicationDLQAckLevel, info1.ReplicationDLQAckLevel) s.Equal(updatedStolenSinceRenew, info1.StolenSinceRenew) if s.DynamicConfiguration.ReadNoSQLShardFromDataBlob() || s.ShardMgr.GetName() != "shardedNosql" { s.Equal(updatedInfo.QueueStates, info1.QueueStates) } failedUpdateInfo := shardInfo.ToNilSafeCopy() failedUpdateInfo.Owner = "failed_owner" failedUpdateInfo.TransferAckLevel = int64(4000) failedUpdateInfo.ReplicationAckLevel = int64(5000) err4 := s.UpdateShard(ctx, failedUpdateInfo, shardInfo.RangeID) s.NotNil(err4) s.IsType(&p.ShardOwnershipLostError{}, err4) s.T().Logf("Update shard failed with error: %v\n", err4) info2, err5 := s.GetShard(ctx, shardID) s.Nil(err5) s.NotNil(info2) s.Equal(updatedOwner, info2.Owner) s.Equal(updatedRangeID, info2.RangeID) s.Equal(updatedCurrentClusterTransferAckLevel, info2.TransferAckLevel) s.Equal(updatedInfo.ClusterTransferAckLevel, info2.ClusterTransferAckLevel) s.Equal(updatedInfo.TransferProcessingQueueStates, info2.TransferProcessingQueueStates) s.EqualTimes(updatedCurrentClusterTimerAckLevel, info2.TimerAckLevel) s.EqualTimes(updatedCurrentClusterTimerAckLevel, info2.ClusterTimerAckLevel[cluster.TestCurrentClusterName]) s.EqualTimes(updatedAlternativeClusterTimerAckLevel, info2.ClusterTimerAckLevel[cluster.TestAlternativeClusterName]) s.Equal(updatedInfo.TimerProcessingQueueStates, info2.TimerProcessingQueueStates) s.Equal(updatedReplicationAckLevel, info2.ReplicationAckLevel) s.Equal(updatedInfo.ReplicationDLQAckLevel, info2.ReplicationDLQAckLevel) s.Equal(updatedStolenSinceRenew, info2.StolenSinceRenew) if s.DynamicConfiguration.ReadNoSQLShardFromDataBlob() || s.ShardMgr.GetName() != "shardedNosql" { s.Equal(updatedInfo.QueueStates, info2.QueueStates) } } func (s *ShardPersistenceSuite) TestCreateGetShardBackfill() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() shardID := 4 rangeID := int64(59) // test create && get currentReplicationAck := int64(27) currentClusterTransferAck := int64(21) currentClusterTimerAck := timestampConvertor(time.Now().Add(-10 * time.Second)) shardInfo := &p.ShardInfo{ ShardID: shardID, Owner: "some random owner", RangeID: rangeID, StolenSinceRenew: 12, UpdatedAt: timestampConvertor(time.Now()), ReplicationAckLevel: currentReplicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterReplicationLevel: map[string]int64{}, ReplicationDLQAckLevel: map[string]int64{}, } createRequest := &p.CreateShardRequest{ ShardInfo: shardInfo, } s.Nil(s.ShardMgr.CreateShard(ctx, createRequest)) // ClusterTransfer/TimerAckLevel will be backfilled if not exists when getting shard currentClusterName := s.ClusterMetadata.GetCurrentClusterName() shardInfo.ClusterTransferAckLevel = map[string]int64{ currentClusterName: currentClusterTransferAck, } shardInfo.ClusterTimerAckLevel = map[string]time.Time{ currentClusterName: currentClusterTimerAck, } resp, err := s.GetShard(ctx, shardID) s.NoError(err) s.EqualTimes(shardInfo.UpdatedAt, resp.UpdatedAt) s.EqualTimes(shardInfo.TimerAckLevel, resp.TimerAckLevel) s.EqualTimes(shardInfo.ClusterTimerAckLevel[currentClusterName], resp.ClusterTimerAckLevel[currentClusterName]) resp.TimerAckLevel = shardInfo.TimerAckLevel resp.UpdatedAt = shardInfo.UpdatedAt resp.ClusterTimerAckLevel = shardInfo.ClusterTimerAckLevel s.Equal(shardInfo, resp) } func (s *ShardPersistenceSuite) TestCreateGetUpdateGetShard() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() shardID := 8 rangeID := int64(59) // test create && get currentReplicationAck := int64(27) currentClusterTransferAck := int64(21) alternativeClusterTransferAck := int64(32) currentClusterTimerAck := timestampConvertor(time.Now().Add(-10 * time.Second)) alternativeClusterTimerAck := timestampConvertor(time.Now().Add(-20 * time.Second)) domainNotificationVersion := int64(8192) transferPQS := createProcessingQueueStates( cluster.TestCurrentClusterName, 0, currentClusterTransferAck, cluster.TestAlternativeClusterName, 1, alternativeClusterTransferAck, ) timerPQS := createProcessingQueueStates( cluster.TestCurrentClusterName, 0, currentClusterTimerAck.UnixNano(), cluster.TestAlternativeClusterName, 1, alternativeClusterTimerAck.UnixNano(), ) shardInfo := &p.ShardInfo{ ShardID: shardID, Owner: "some random owner", RangeID: rangeID, StolenSinceRenew: 12, UpdatedAt: timestampConvertor(time.Now()), ReplicationAckLevel: currentReplicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, TransferProcessingQueueStates: transferPQS, TimerProcessingQueueStates: timerPQS, DomainNotificationVersion: domainNotificationVersion, ClusterReplicationLevel: map[string]int64{}, ReplicationDLQAckLevel: map[string]int64{}, QueueStates: map[int32]*types.QueueState{ p.HistoryTaskCategoryIDTransfer: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: &types.VirtualQueueState{ VirtualSliceStates: []*types.VirtualSliceState{ { TaskRange: &types.TaskRange{ InclusiveMin: &types.TaskKey{ TaskID: currentClusterTransferAck + 1, }, ExclusiveMax: &types.TaskKey{ TaskID: currentClusterTransferAck + 10, }, }, Predicate: &types.Predicate{ PredicateType: types.PredicateTypeDomainID, DomainIDPredicateAttributes: &types.DomainIDPredicateAttributes{ DomainIDs: []string{"domain1", "domain2"}, IsExclusive: common.Ptr(true), }, }, }, }, }, }, ExclusiveMaxReadLevel: &types.TaskKey{ TaskID: currentClusterTransferAck + 10, }, }, }, } createRequest := &p.CreateShardRequest{ ShardInfo: shardInfo, } s.Nil(s.ShardMgr.CreateShard(ctx, createRequest)) resp, err := s.GetShard(ctx, shardID) s.NoError(err) s.EqualTimes(shardInfo.UpdatedAt, resp.UpdatedAt) s.EqualTimes(shardInfo.TimerAckLevel, resp.TimerAckLevel) s.EqualTimes(shardInfo.ClusterTimerAckLevel[cluster.TestCurrentClusterName], resp.ClusterTimerAckLevel[cluster.TestCurrentClusterName]) s.EqualTimes(shardInfo.ClusterTimerAckLevel[cluster.TestAlternativeClusterName], resp.ClusterTimerAckLevel[cluster.TestAlternativeClusterName]) resp.TimerAckLevel = shardInfo.TimerAckLevel resp.UpdatedAt = shardInfo.UpdatedAt resp.ClusterTimerAckLevel = shardInfo.ClusterTimerAckLevel if !s.DynamicConfiguration.ReadNoSQLHistoryTaskFromDataBlob() && s.ShardMgr.GetName() == "shardedNosql" { resp.QueueStates = shardInfo.QueueStates } s.Equal(shardInfo, resp) // test update && get currentReplicationAck = int64(270) currentClusterTransferAck = int64(210) alternativeClusterTransferAck = int64(320) currentClusterTimerAck = timestampConvertor(time.Now().Add(-100 * time.Second)) alternativeClusterTimerAck = timestampConvertor(time.Now().Add(-200 * time.Second)) domainNotificationVersion = int64(16384) transferPQS = createProcessingQueueStates( cluster.TestCurrentClusterName, 0, currentClusterTransferAck, cluster.TestAlternativeClusterName, 1, alternativeClusterTransferAck, ) timerPQS = createProcessingQueueStates( cluster.TestCurrentClusterName, 0, currentClusterTimerAck.UnixNano(), cluster.TestAlternativeClusterName, 1, alternativeClusterTimerAck.UnixNano(), ) shardInfo = &p.ShardInfo{ ShardID: shardID, Owner: "some random owner", RangeID: int64(28), StolenSinceRenew: 4, UpdatedAt: timestampConvertor(time.Now()), ReplicationAckLevel: currentReplicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, TransferProcessingQueueStates: transferPQS, TimerProcessingQueueStates: timerPQS, DomainNotificationVersion: domainNotificationVersion, ClusterReplicationLevel: map[string]int64{cluster.TestAlternativeClusterName: 12345}, ReplicationDLQAckLevel: map[string]int64{}, } updateRequest := &p.UpdateShardRequest{ ShardInfo: shardInfo, PreviousRangeID: rangeID, } s.Nil(s.ShardMgr.UpdateShard(ctx, updateRequest)) resp, err = s.GetShard(ctx, shardID) s.NoError(err) s.EqualTimes(shardInfo.UpdatedAt, resp.UpdatedAt) s.EqualTimes(shardInfo.TimerAckLevel, resp.TimerAckLevel) s.EqualTimes(shardInfo.ClusterTimerAckLevel[cluster.TestCurrentClusterName], resp.ClusterTimerAckLevel[cluster.TestCurrentClusterName]) s.EqualTimes(shardInfo.ClusterTimerAckLevel[cluster.TestAlternativeClusterName], resp.ClusterTimerAckLevel[cluster.TestAlternativeClusterName]) resp.UpdatedAt = shardInfo.UpdatedAt resp.TimerAckLevel = shardInfo.TimerAckLevel resp.ClusterTimerAckLevel = shardInfo.ClusterTimerAckLevel s.Equal(shardInfo, resp) } func createProcessingQueueStates( cluster1 string, level1 int32, ackLevel1 int64, cluster2 string, level2 int32, ackLevel2 int64, ) *types.ProcessingQueueStates { domainFilter := &types.DomainFilter{ DomainIDs: nil, ReverseMatch: true, } processingQueueStateMap := map[string][]*types.ProcessingQueueState{} if len(cluster1) != 0 { processingQueueStateMap[cluster1] = []*types.ProcessingQueueState{ { Level: common.Int32Ptr(level1), AckLevel: common.Int64Ptr(ackLevel1), MaxLevel: common.Int64Ptr(ackLevel1), DomainFilter: domainFilter, }, } } if len(cluster2) != 0 { processingQueueStateMap[cluster2] = []*types.ProcessingQueueState{ { Level: common.Int32Ptr(level2), AckLevel: common.Int64Ptr(ackLevel2), MaxLevel: common.Int64Ptr(ackLevel2), DomainFilter: domainFilter, }, } } return &types.ProcessingQueueStates{StatesByCluster: processingQueueStateMap} } ================================================ FILE: common/persistence/persistence-tests/shared_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "testing" "github.com/uber/cadence/common/persistence" ) func TestGarbageCleanupInfo(t *testing.T) { domainID := "10000000-5000-f000-f000-000000000000" workflowID := "workflow-id" runID := "10000000-5000-f000-f000-000000000002" info := persistence.BuildHistoryGarbageCleanupInfo(domainID, workflowID, runID) domainID2, workflowID2, runID2, err := persistence.SplitHistoryGarbageCleanupInfo(info) if err != nil || domainID != domainID2 || workflowID != workflowID2 || runID != runID2 { t.Fail() } } func TestGarbageCleanupInfo_WithColonInWorklfowID(t *testing.T) { domainID := "10000000-5000-f000-f000-000000000000" workflowID := "workflow-id:2" runID := "10000000-5000-f000-f000-000000000002" info := persistence.BuildHistoryGarbageCleanupInfo(domainID, workflowID, runID) domainID2, workflowID2, runID2, err := persistence.SplitHistoryGarbageCleanupInfo(info) if err != nil || domainID != domainID2 || workflowID != workflowID2 || runID != runID2 { t.Fail() } } ================================================ FILE: common/persistence/persistence-tests/testcluster/interfaces.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package testcluster import "github.com/uber/cadence/common/config" type ( // PersistenceTestCluster exposes management operations on a database // NOTE: Putting this interface in separate package to avoid cycle dependency PersistenceTestCluster interface { SetupTestDatabase() TearDownTestDatabase() Config() config.Persistence } ) ================================================ FILE: common/persistence/persistence-tests/visibilitySamplingClient_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistencetests import ( "context" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" mmocks "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/mocks" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/wrappers/sampled" "github.com/uber/cadence/common/types" ) type VisibilitySamplingSuite struct { *require.Assertions // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, not merely log an error suite.Suite client p.VisibilityManager persistence *mocks.VisibilityManager metricClient *mmocks.Client } var ( testDomainUUID = "fb15e4b5-356f-466d-8c6d-a29223e5c536" testDomain = "test-domain-name" testWorkflowExecution = types.WorkflowExecution{ WorkflowID: "visibility-workflow-test", RunID: "843f6fc7-102a-4c63-a2d4-7c653b01bf52", } testWorkflowTypeName = "visibility-workflow" listErrMsg = "Persistence Max QPS Reached for List Operations." ) func TestVisibilitySamplingSuite(t *testing.T) { suite.Run(t, new(VisibilitySamplingSuite)) } func (s *VisibilitySamplingSuite) SetupTest() { s.Assertions = require.New(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.persistence = &mocks.VisibilityManager{} config := &sampled.Config{ VisibilityOpenMaxQPS: dynamicproperties.GetIntPropertyFilteredByDomain(1), VisibilityClosedMaxQPS: dynamicproperties.GetIntPropertyFilteredByDomain(10), VisibilityListMaxQPS: dynamicproperties.GetIntPropertyFilteredByDomain(1), } s.metricClient = &mmocks.Client{} s.client = sampled.NewVisibilityManager(s.persistence, sampled.Params{ Config: config, MetricClient: s.metricClient, Logger: testlogger.New(s.T()), TimeSource: clock.NewRealTimeSource(), RateLimiterFactoryFunc: sampled.NewDomainToBucketMap, }) } func (s *VisibilitySamplingSuite) TearDownTest() { s.persistence.AssertExpectations(s.T()) s.metricClient.AssertExpectations(s.T()) } func (s *VisibilitySamplingSuite) TestRecordWorkflowExecutionStarted() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() request := &p.RecordWorkflowExecutionStartedRequest{ DomainUUID: testDomainUUID, Domain: testDomain, Execution: testWorkflowExecution, WorkflowTypeName: testWorkflowTypeName, StartTimestamp: time.Now().UnixNano(), } s.persistence.On("RecordWorkflowExecutionStarted", mock.Anything, request).Return(nil).Once() s.NoError(s.client.RecordWorkflowExecutionStarted(ctx, request)) // no remaining tokens s.metricClient.On("IncCounter", metrics.PersistenceRecordWorkflowExecutionStartedScope, metrics.PersistenceSampledCounter).Once() s.NoError(s.client.RecordWorkflowExecutionStarted(ctx, request)) } func (s *VisibilitySamplingSuite) TestRecordWorkflowExecutionClosed() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() request := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Domain: testDomain, Execution: testWorkflowExecution, WorkflowTypeName: testWorkflowTypeName, Status: types.WorkflowExecutionCloseStatusCompleted, } request2 := &p.RecordWorkflowExecutionClosedRequest{ DomainUUID: testDomainUUID, Domain: testDomain, Execution: testWorkflowExecution, WorkflowTypeName: testWorkflowTypeName, Status: types.WorkflowExecutionCloseStatusFailed, } s.persistence.On("RecordWorkflowExecutionClosed", mock.Anything, request).Return(nil).Once() s.NoError(s.client.RecordWorkflowExecutionClosed(ctx, request)) s.persistence.On("RecordWorkflowExecutionClosed", mock.Anything, request2).Return(nil).Once() s.NoError(s.client.RecordWorkflowExecutionClosed(ctx, request2)) // no remaining tokens s.metricClient.On("IncCounter", metrics.PersistenceRecordWorkflowExecutionClosedScope, metrics.PersistenceSampledCounter).Once() s.NoError(s.client.RecordWorkflowExecutionClosed(ctx, request)) s.metricClient.On("IncCounter", metrics.PersistenceRecordWorkflowExecutionClosedScope, metrics.PersistenceSampledCounter).Once() s.NoError(s.client.RecordWorkflowExecutionClosed(ctx, request2)) } func (s *VisibilitySamplingSuite) TestListOpenWorkflowExecutions() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() request := &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, Domain: testDomain, } s.persistence.On("ListOpenWorkflowExecutions", mock.Anything, request).Return(nil, nil).Once() _, err := s.client.ListOpenWorkflowExecutions(ctx, request) s.NoError(err) // no remaining tokens _, err = s.client.ListOpenWorkflowExecutions(ctx, request) s.Error(err) errDetail, ok := err.(*types.ServiceBusyError) s.True(ok) s.Equal(listErrMsg, errDetail.Message) } func (s *VisibilitySamplingSuite) TestListClosedWorkflowExecutions() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() request := &p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, Domain: testDomain, } s.persistence.On("ListClosedWorkflowExecutions", mock.Anything, request).Return(nil, nil).Once() _, err := s.client.ListClosedWorkflowExecutions(ctx, request) s.NoError(err) // no remaining tokens _, err = s.client.ListClosedWorkflowExecutions(ctx, request) s.Error(err) errDetail, ok := err.(*types.ServiceBusyError) s.True(ok) s.Equal(listErrMsg, errDetail.Message) } func (s *VisibilitySamplingSuite) TestListOpenWorkflowExecutionsByType() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() req := p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, Domain: testDomain, } request := &p.ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: req, WorkflowTypeName: testWorkflowTypeName, } s.persistence.On("ListOpenWorkflowExecutionsByType", mock.Anything, request).Return(nil, nil).Once() _, err := s.client.ListOpenWorkflowExecutionsByType(ctx, request) s.NoError(err) // no remaining tokens _, err = s.client.ListOpenWorkflowExecutionsByType(ctx, request) s.Error(err) errDetail, ok := err.(*types.ServiceBusyError) s.True(ok) s.Equal(listErrMsg, errDetail.Message) } func (s *VisibilitySamplingSuite) TestListClosedWorkflowExecutionsByType() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() req := p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, Domain: testDomain, } request := &p.ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: req, WorkflowTypeName: testWorkflowTypeName, } s.persistence.On("ListClosedWorkflowExecutionsByType", mock.Anything, request).Return(nil, nil).Once() _, err := s.client.ListClosedWorkflowExecutionsByType(ctx, request) s.NoError(err) // no remaining tokens _, err = s.client.ListClosedWorkflowExecutionsByType(ctx, request) s.Error(err) errDetail, ok := err.(*types.ServiceBusyError) s.True(ok) s.Equal(listErrMsg, errDetail.Message) } func (s *VisibilitySamplingSuite) TestListOpenWorkflowExecutionsByWorkflowID() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() req := p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, Domain: testDomain, } request := &p.ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: req, WorkflowID: testWorkflowExecution.GetWorkflowID(), } s.persistence.On("ListOpenWorkflowExecutionsByWorkflowID", mock.Anything, request).Return(nil, nil).Once() _, err := s.client.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) s.NoError(err) // no remaining tokens _, err = s.client.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) s.Error(err) errDetail, ok := err.(*types.ServiceBusyError) s.True(ok) s.Equal(listErrMsg, errDetail.Message) } func (s *VisibilitySamplingSuite) TestListClosedWorkflowExecutionsByWorkflowID() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() req := p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, Domain: testDomain, } request := &p.ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: req, WorkflowID: testWorkflowExecution.GetWorkflowID(), } s.persistence.On("ListClosedWorkflowExecutionsByWorkflowID", mock.Anything, request).Return(nil, nil).Once() _, err := s.client.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) s.NoError(err) // no remaining tokens _, err = s.client.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) s.Error(err) errDetail, ok := err.(*types.ServiceBusyError) s.True(ok) s.Equal(listErrMsg, errDetail.Message) } func (s *VisibilitySamplingSuite) TestListClosedWorkflowExecutionsByStatus() { ctx, cancel := context.WithTimeout(context.Background(), testContextTimeout) defer cancel() req := p.ListWorkflowExecutionsRequest{ DomainUUID: testDomainUUID, Domain: testDomain, } request := &p.ListClosedWorkflowExecutionsByStatusRequest{ ListWorkflowExecutionsRequest: req, Status: types.WorkflowExecutionCloseStatusFailed, } s.persistence.On("ListClosedWorkflowExecutionsByStatus", mock.Anything, request).Return(nil, nil).Once() _, err := s.client.ListClosedWorkflowExecutionsByStatus(ctx, request) s.NoError(err) // no remaining tokens _, err = s.client.ListClosedWorkflowExecutionsByStatus(ctx, request) s.Error(err) errDetail, ok := err.(*types.ServiceBusyError) s.True(ok) s.Equal(listErrMsg, errDetail.Message) } ================================================ FILE: common/persistence/persistence-utils/history_manager_util.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistenceutils import ( "context" "errors" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // ReadFullPageV2Events reads a full page of history events from HistoryManager. Due to storage format of V2 History // it is not guaranteed that pageSize amount of data is returned. Function returns the list of history events, the size // of data read, the next page token, and an error if present. func ReadFullPageV2Events( ctx context.Context, historyV2Mgr persistence.HistoryManager, req *persistence.ReadHistoryBranchRequest, ) ([]*types.HistoryEvent, int, []byte, error) { historyEvents := []*types.HistoryEvent{} size := int(0) for { response, err := historyV2Mgr.ReadHistoryBranch(ctx, req) if err != nil { return nil, 0, nil, err } historyEvents = append(historyEvents, response.HistoryEvents...) size += response.Size if len(historyEvents) >= req.PageSize || len(response.NextPageToken) == 0 { return historyEvents, size, response.NextPageToken, nil } req.NextPageToken = response.NextPageToken } } // ReadFullPageV2EventsByBatch reads a full page of history events by batch from HistoryManager. Due to storage format of V2 History // it is not guaranteed that pageSize amount of data is returned. Function returns the list of history batches, the size // of data read, the next page token, and an error if present. func ReadFullPageV2EventsByBatch( ctx context.Context, historyV2Mgr persistence.HistoryManager, req *persistence.ReadHistoryBranchRequest, ) ([]*types.History, int, []byte, error) { historyBatches := []*types.History{} eventsRead := 0 size := 0 for { response, err := historyV2Mgr.ReadHistoryBranchByBatch(ctx, req) if err != nil { return nil, 0, nil, err } historyBatches = append(historyBatches, response.History...) for _, batch := range response.History { eventsRead += len(batch.Events) } size += response.Size if eventsRead >= req.PageSize || len(response.NextPageToken) == 0 { return historyBatches, size, response.NextPageToken, nil } req.NextPageToken = response.NextPageToken } } // GetBeginNodeID gets node id from last ancestor func GetBeginNodeID(bi types.HistoryBranch) int64 { if len(bi.Ancestors) == 0 { // root branch return 1 } idx := len(bi.Ancestors) - 1 return bi.Ancestors[idx].EndNodeID } // PaginateHistory return paged history func PaginateHistory( ctx context.Context, historyV2Mgr persistence.HistoryManager, byBatch bool, branchToken []byte, firstEventID int64, nextEventID int64, tokenIn []byte, pageSize int, shardID *int, domainID string, domainCache cache.DomainCache, ) ([]*types.HistoryEvent, []*types.History, []byte, int, error) { historyEvents := []*types.HistoryEvent{} historyBatches := []*types.History{} var tokenOut []byte var historySize int domainName, err := domainCache.GetDomainName(domainID) if err != nil { return nil, nil, nil, 0, err } req := &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: pageSize, NextPageToken: tokenIn, ShardID: shardID, DomainName: domainName, } if byBatch { response, err := historyV2Mgr.ReadHistoryBranchByBatch(ctx, req) if err != nil { var e *types.EntityNotExistsError if errors.As(err, &e) { return nil, nil, nil, 0, nil } return nil, nil, nil, 0, err } // Keep track of total history size historySize += response.Size historyBatches = append(historyBatches, response.History...) tokenOut = response.NextPageToken } else { response, err := historyV2Mgr.ReadHistoryBranch(ctx, req) if err != nil { var e *types.EntityNotExistsError if errors.As(err, &e) { return nil, nil, nil, 0, nil } return nil, nil, nil, 0, err } // Keep track of total history size historySize += response.Size historyEvents = append(historyEvents, response.HistoryEvents...) tokenOut = response.NextPageToken } return historyEvents, historyBatches, tokenOut, historySize, nil } // Get the maximum referenced node id of each branch func GetBranchesMaxReferredNodeIDs(branches []*types.HistoryBranch) map[string]int64 { validBRsMaxEndNode := map[string]int64{} for _, b := range branches { for _, br := range b.Ancestors { curr, ok := validBRsMaxEndNode[br.BranchID] if !ok || curr < br.EndNodeID { validBRsMaxEndNode[br.BranchID] = br.EndNodeID } } } return validBRsMaxEndNode } ================================================ FILE: common/persistence/pinot/pinot_visibility_metric_clients.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package pinotvisibility import ( "context" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type pinotVisibilityMetricsClient struct { metricClient metrics.Client persistence p.VisibilityManager logger log.Logger } var _ p.VisibilityManager = (*pinotVisibilityMetricsClient)(nil) // NewPinotVisibilityMetricsClient wrap visibility client with metrics client func NewPinotVisibilityMetricsClient(persistence p.VisibilityManager, metricClient metrics.Client, logger log.Logger) p.VisibilityManager { return &pinotVisibilityMetricsClient{ persistence: persistence, metricClient: metricClient, logger: logger, } } func (p *pinotVisibilityMetricsClient) GetName() string { return p.persistence.GetName() } func (p *pinotVisibilityMetricsClient) RecordWorkflowExecutionStarted( ctx context.Context, request *p.RecordWorkflowExecutionStartedRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotRecordWorkflowExecutionStartedScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() err := p.persistence.RecordWorkflowExecutionStarted(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotRecordWorkflowExecutionStartedScope, err) } return err } func (p *pinotVisibilityMetricsClient) RecordWorkflowExecutionClosed( ctx context.Context, request *p.RecordWorkflowExecutionClosedRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotRecordWorkflowExecutionClosedScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() err := p.persistence.RecordWorkflowExecutionClosed(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotRecordWorkflowExecutionClosedScope, err) } return err } func (p *pinotVisibilityMetricsClient) RecordWorkflowExecutionUninitialized( ctx context.Context, request *p.RecordWorkflowExecutionUninitializedRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotRecordWorkflowExecutionUninitializedScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() err := p.persistence.RecordWorkflowExecutionUninitialized(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotRecordWorkflowExecutionUninitializedScope, err) } return err } func (p *pinotVisibilityMetricsClient) UpsertWorkflowExecution( ctx context.Context, request *p.UpsertWorkflowExecutionRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotUpsertWorkflowExecutionScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() err := p.persistence.UpsertWorkflowExecution(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotUpsertWorkflowExecutionScope, err) } return err } func (p *pinotVisibilityMetricsClient) ListOpenWorkflowExecutions( ctx context.Context, request *p.ListWorkflowExecutionsRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotListOpenWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.ListOpenWorkflowExecutions(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotListOpenWorkflowExecutionsScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) ListClosedWorkflowExecutions( ctx context.Context, request *p.ListWorkflowExecutionsRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotListClosedWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.ListClosedWorkflowExecutions(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotListClosedWorkflowExecutionsScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) ListOpenWorkflowExecutionsByType( ctx context.Context, request *p.ListWorkflowExecutionsByTypeRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotListOpenWorkflowExecutionsByTypeScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.ListOpenWorkflowExecutionsByType(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotListOpenWorkflowExecutionsByTypeScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) ListClosedWorkflowExecutionsByType( ctx context.Context, request *p.ListWorkflowExecutionsByTypeRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotListClosedWorkflowExecutionsByTypeScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.ListClosedWorkflowExecutionsByType(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotListClosedWorkflowExecutionsByTypeScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) ListOpenWorkflowExecutionsByWorkflowID( ctx context.Context, request *p.ListWorkflowExecutionsByWorkflowIDRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotListOpenWorkflowExecutionsByWorkflowIDScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotListOpenWorkflowExecutionsByWorkflowIDScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) ListClosedWorkflowExecutionsByWorkflowID( ctx context.Context, request *p.ListWorkflowExecutionsByWorkflowIDRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotListClosedWorkflowExecutionsByWorkflowIDScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotListClosedWorkflowExecutionsByWorkflowIDScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) ListClosedWorkflowExecutionsByStatus( ctx context.Context, request *p.ListClosedWorkflowExecutionsByStatusRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotListClosedWorkflowExecutionsByStatusScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.ListClosedWorkflowExecutionsByStatus(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotListClosedWorkflowExecutionsByStatusScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) GetClosedWorkflowExecution( ctx context.Context, request *p.GetClosedWorkflowExecutionRequest, ) (*p.GetClosedWorkflowExecutionResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotGetClosedWorkflowExecutionScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.GetClosedWorkflowExecution(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotGetClosedWorkflowExecutionScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) ListWorkflowExecutions( ctx context.Context, request *p.ListWorkflowExecutionsByQueryRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotListWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.ListWorkflowExecutions(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotListWorkflowExecutionsScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) ScanWorkflowExecutions( ctx context.Context, request *p.ListWorkflowExecutionsByQueryRequest, ) (*p.ListWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotScanWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.ScanWorkflowExecutions(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotScanWorkflowExecutionsScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) CountWorkflowExecutions( ctx context.Context, request *p.CountWorkflowExecutionsRequest, ) (*p.CountWorkflowExecutionsResponse, error) { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotCountWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() response, err := p.persistence.CountWorkflowExecutions(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotCountWorkflowExecutionsScope, err) } return response, err } func (p *pinotVisibilityMetricsClient) DeleteWorkflowExecution( ctx context.Context, request *p.VisibilityDeleteWorkflowExecutionRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotDeleteWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() err := p.persistence.DeleteWorkflowExecution(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotDeleteWorkflowExecutionsScope, err) } return err } func (p *pinotVisibilityMetricsClient) DeleteUninitializedWorkflowExecution( ctx context.Context, request *p.VisibilityDeleteWorkflowExecutionRequest, ) error { scopeWithDomainTag := p.metricClient.Scope(metrics.PinotDeleteWorkflowExecutionsScope, metrics.DomainTag(request.Domain)) scopeWithDomainTag.IncCounter(metrics.PinotRequestsPerDomain) sw := scopeWithDomainTag.StartTimer(metrics.PinotLatencyPerDomain) defer sw.Stop() err := p.persistence.DeleteWorkflowExecution(ctx, request) if err != nil { p.updateErrorMetric(scopeWithDomainTag, metrics.PinotDeleteWorkflowExecutionsScope, err) } return err } func (p *pinotVisibilityMetricsClient) updateErrorMetric(scopeWithDomainTag metrics.Scope, scope metrics.ScopeIdx, err error) { switch err.(type) { case *types.BadRequestError: scopeWithDomainTag.IncCounter(metrics.PinotErrBadRequestCounterPerDomain) scopeWithDomainTag.IncCounter(metrics.PinotFailuresPerDomain) case *types.ServiceBusyError: scopeWithDomainTag.IncCounter(metrics.PinotErrBusyCounterPerDomain) scopeWithDomainTag.IncCounter(metrics.PinotFailuresPerDomain) default: p.logger.Error("Operation failed with internal error.", tag.MetricScope(int(scope)), tag.Error(err)) scopeWithDomainTag.IncCounter(metrics.PinotFailuresPerDomain) } } func (p *pinotVisibilityMetricsClient) Close() { p.persistence.Close() } ================================================ FILE: common/persistence/pinot/pinot_visibility_metric_clients_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pinotvisibility import ( "context" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.uber.org/mock/gomock" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" metricsClientMocks "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" p "github.com/uber/cadence/common/persistence" pnt "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" ) var ( testStopwatch = metrics.NoopScope.StartTimer(metrics.PinotLatency) ) func TestMetricClientRecordWorkflowExecutionStarted(t *testing.T) { // test non-empty request fields match errorRequest := &p.RecordWorkflowExecutionStartedRequest{ WorkflowTypeName: "errorWorkflowTypeName", Memo: &types.Memo{ map[string][]byte{ "CustomStringField": []byte("test string"), }, }, } request := &p.RecordWorkflowExecutionStartedRequest{ WorkflowTypeName: "wtn", Memo: &types.Memo{ map[string][]byte{ "CustomStringField": []byte("test string"), }, }, } tests := map[string]struct { request *p.RecordWorkflowExecutionStartedRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(&types.BadRequestError{}).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: &types.BadRequestError{}, }, "Case2: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(nil).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.producerMockAffordance(mockProducer) test.scopeMockAffordance(mockScope) err := metricsClient.RecordWorkflowExecutionStarted(context.Background(), test.request) assert.Equal(t, err, test.expectedError) }) } } func TestMetricClientRecordWorkflowExecutionClosed(t *testing.T) { // test non-empty request fields match errorRequest := &p.RecordWorkflowExecutionClosedRequest{ WorkflowTypeName: "errorWorkflowTypeName", Memo: &types.Memo{ map[string][]byte{ "CustomStringField": []byte("test string"), }, }, } request := &p.RecordWorkflowExecutionClosedRequest{ WorkflowTypeName: "wtn", Memo: &types.Memo{ map[string][]byte{ "CustomStringField": []byte("test string"), }, }, } tests := map[string]struct { request *p.RecordWorkflowExecutionClosedRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(&types.ServiceBusyError{}).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: &types.ServiceBusyError{}, }, "Case2: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(nil).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.producerMockAffordance(mockProducer) test.scopeMockAffordance(mockScope) err := metricsClient.RecordWorkflowExecutionClosed(context.Background(), test.request) assert.Equal(t, err, test.expectedError) }) } } func TestMetricClientRecordWorkflowExecutionUninitialized(t *testing.T) { // test non-empty request fields match errorRequest := &p.RecordWorkflowExecutionUninitializedRequest{ WorkflowTypeName: "errorWorkflowTypeName", } request := &p.RecordWorkflowExecutionUninitializedRequest{ WorkflowTypeName: "wtn", } tests := map[string]struct { request *p.RecordWorkflowExecutionUninitializedRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(fmt.Errorf("error")).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(nil).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.producerMockAffordance(mockProducer) test.scopeMockAffordance(mockScope) err := metricsClient.RecordWorkflowExecutionUninitialized(context.Background(), test.request) assert.Equal(t, err, test.expectedError) }) } } func TestMetricClientUpsertWorkflowExecution(t *testing.T) { // test non-empty request fields match errorRequest := &p.UpsertWorkflowExecutionRequest{ WorkflowTypeName: "errorWorkflowTypeName", } request := &p.UpsertWorkflowExecutionRequest{ WorkflowTypeName: "wtn", } tests := map[string]struct { request *p.UpsertWorkflowExecutionRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(fmt.Errorf("error")).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(nil).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.producerMockAffordance(mockProducer) test.scopeMockAffordance(mockScope) err := metricsClient.UpsertWorkflowExecution(context.Background(), test.request) assert.Equal(t, err, test.expectedError) }) } } func TestMetricClientListOpenWorkflowExecutions(t *testing.T) { // test non-empty request fields match errorRequest := &p.ListWorkflowExecutionsRequest{ Domain: "badDomainID", } request := &p.ListWorkflowExecutionsRequest{ Domain: DomainID, } tests := map[string]struct { request *p.ListWorkflowExecutionsRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.ListOpenWorkflowExecutions(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestMetricClientListClosedWorkflowExecutions(t *testing.T) { // test non-empty request fields match errorRequest := &p.ListWorkflowExecutionsRequest{ Domain: "badDomainId", } request := &p.ListWorkflowExecutionsRequest{ Domain: DomainID, } tests := map[string]struct { request *p.ListWorkflowExecutionsRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.ListClosedWorkflowExecutions(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestMetricClientListOpenWorkflowExecutionsByType(t *testing.T) { // test non-empty request fields match errorRequest := &p.ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: p.ListWorkflowExecutionsRequest{ Domain: "badDomainID", }, WorkflowTypeName: "", } request := &p.ListWorkflowExecutionsByTypeRequest{} tests := map[string]struct { request *p.ListWorkflowExecutionsByTypeRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.ListOpenWorkflowExecutionsByType(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestMetricClientListClosedWorkflowExecutionsByType(t *testing.T) { // test non-empty request fields match errorRequest := &p.ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: p.ListWorkflowExecutionsRequest{ Domain: "badDomainID", }, WorkflowTypeName: "", } request := &p.ListWorkflowExecutionsByTypeRequest{} tests := map[string]struct { request *p.ListWorkflowExecutionsByTypeRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.ListClosedWorkflowExecutionsByType(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestMetricClientListOpenWorkflowExecutionsByWorkflowID(t *testing.T) { // test non-empty request fields match errorRequest := &p.ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: p.ListWorkflowExecutionsRequest{ Domain: "badDomainID", }, } request := &p.ListWorkflowExecutionsByWorkflowIDRequest{} tests := map[string]struct { request *p.ListWorkflowExecutionsByWorkflowIDRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.ListOpenWorkflowExecutionsByWorkflowID(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestMetricClientListClosedWorkflowExecutionsByWorkflowID(t *testing.T) { // test non-empty request fields match errorRequest := &p.ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: p.ListWorkflowExecutionsRequest{ Domain: "badDomainID", }, } request := &p.ListWorkflowExecutionsByWorkflowIDRequest{} tests := map[string]struct { request *p.ListWorkflowExecutionsByWorkflowIDRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.ListClosedWorkflowExecutionsByWorkflowID(context.Background(), test.request) if test.expectedError != nil { assert.Equal(t, test.expectedError.Error(), err.Error()) } else { assert.NoError(t, err) } }) } } func TestMetricClientListClosedWorkflowExecutionsByStatus(t *testing.T) { // test non-empty request fields match errorRequest := &p.ListClosedWorkflowExecutionsByStatusRequest{ ListWorkflowExecutionsRequest: p.ListWorkflowExecutionsRequest{ Domain: "badDomainID", }, } request := &p.ListClosedWorkflowExecutionsByStatusRequest{} tests := map[string]struct { request *p.ListClosedWorkflowExecutionsByStatusRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.ListClosedWorkflowExecutionsByStatus(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestMetricClientGetClosedWorkflowExecution(t *testing.T) { // test non-empty request fields match errorRequest := &p.GetClosedWorkflowExecutionRequest{} request := &p.GetClosedWorkflowExecutionRequest{} tests := map[string]struct { request *p.GetClosedWorkflowExecutionRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("Pinot GetClosedWorkflowExecution failed, error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(&pnt.SearchResponse{ Executions: []*p.InternalVisibilityWorkflowExecutionInfo{ { DomainID: DomainID, }, }, }, nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.GetClosedWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Equal(t, test.expectedError.Error(), err.Error()) } else { assert.NoError(t, err) } }) } } func TestMetricClientListWorkflowExecutions(t *testing.T) { errorRequest := &p.ListWorkflowExecutionsByQueryRequest{} request := &p.ListWorkflowExecutionsByQueryRequest{} tests := map[string]struct { request *p.ListWorkflowExecutionsByQueryRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.ListWorkflowExecutions(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestMetricClientScanWorkflowExecutions(t *testing.T) { errorRequest := &p.ListWorkflowExecutionsByQueryRequest{} request := &p.ListWorkflowExecutionsByQueryRequest{} tests := map[string]struct { request *p.ListWorkflowExecutionsByQueryRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.ScanWorkflowExecutions(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestMetricClientCountWorkflowExecutions(t *testing.T) { errorRequest := &p.CountWorkflowExecutionsRequest{} request := &p.CountWorkflowExecutionsRequest{} tests := map[string]struct { request *p.CountWorkflowExecutionsRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().CountByQuery(gomock.Any()).Return(int64(0), fmt.Errorf("error")).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("CountClosedWorkflowExecutions failed, error"), }, "Case2: normal case": { request: request, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().CountByQuery(gomock.Any()).Return(int64(1), nil).Times(1) }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.pinotClientMockAffordance(mockPinotClient) test.scopeMockAffordance(mockScope) _, err := metricsClient.CountWorkflowExecutions(context.Background(), test.request) if test.expectedError != nil { assert.Equal(t, test.expectedError.Error(), err.Error()) } else { assert.NoError(t, err) } }) } } func TestMetricClientDeleteWorkflowExecution(t *testing.T) { // test non-empty request fields match errorRequest := &p.VisibilityDeleteWorkflowExecutionRequest{} request := &p.VisibilityDeleteWorkflowExecutionRequest{} tests := map[string]struct { request *p.VisibilityDeleteWorkflowExecutionRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(fmt.Errorf("error")).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(nil).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.producerMockAffordance(mockProducer) test.scopeMockAffordance(mockScope) err := metricsClient.DeleteWorkflowExecution(context.Background(), test.request) assert.Equal(t, err, test.expectedError) }) } } func TestMetricClientDeleteUninitializedWorkflowExecution(t *testing.T) { // test non-empty request fields match errorRequest := &p.VisibilityDeleteWorkflowExecutionRequest{} request := &p.VisibilityDeleteWorkflowExecutionRequest{} tests := map[string]struct { request *p.VisibilityDeleteWorkflowExecutionRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) scopeMockAffordance func(mockScope *metricsClientMocks.Scope) expectedError error }{ "Case1: error case": { request: errorRequest, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(fmt.Errorf("error")).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Times(3) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(nil).Once() }, scopeMockAffordance: func(mockScope *metricsClientMocks.Scope) { mockScope.On("IncCounter", mock.Anything, mock.Anything, mock.Anything).Return().Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} mockScope := &metricsClientMocks.Scope{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) // mock behaviors mockMetricClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope).Once() mockScope.On("StartTimer", mock.Anything, mock.Anything).Return(testStopwatch).Once() test.producerMockAffordance(mockProducer) test.scopeMockAffordance(mockScope) err := metricsClient.DeleteUninitializedWorkflowExecution(context.Background(), test.request) assert.Equal(t, err, test.expectedError) }) } } func TestMetricClientClose(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) assert.NotPanics(t, func() { metricsClient.Close() }) } func TestMetricClientGetName(t *testing.T) { // create mock clients ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mockMetricClient := &metricsClientMocks.Client{} // create metricClient logger := log.NewNoop() mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), }, mockProducer, testlogger.New(t)) visibilityStore := mgr.(*pinotVisibilityStore) pinotVisibilityManager := p.NewVisibilityManagerImpl(visibilityStore, logger, &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityMgr := NewPinotVisibilityMetricsClient(pinotVisibilityManager, mockMetricClient, logger) metricsClient := visibilityMgr.(*pinotVisibilityMetricsClient) assert.NotPanics(t, func() { metricsClient.GetName() }) } ================================================ FILE: common/persistence/pinot/pinot_visibility_store.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package pinotvisibility import ( "context" "encoding/json" "fmt" "strings" "time" "github.com/uber/cadence/.gen/go/indexer" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" p "github.com/uber/cadence/common/persistence" pnt "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) const ( DescendingOrder = "DESC" AscendingOrder = "ASC" DomainID = "DomainID" WorkflowID = "WorkflowID" RunID = "RunID" WorkflowType = "WorkflowType" CloseStatus = "CloseStatus" HistoryLength = "HistoryLength" TaskList = "TaskList" IsCron = "IsCron" NumClusters = "NumClusters" ShardID = "ShardID" Attr = "Attr" StartTime = "StartTime" CloseTime = "CloseTime" UpdateTime = "UpdateTime" ExecutionTime = "ExecutionTime" IsDeleted = "IsDeleted" // used for Pinot deletion/rolling upsert only, not visible to user EventTimeMs = "EventTimeMs" // used for Pinot deletion/rolling upsert only, not visible to user Memo = "Memo" // used to be micro second oneMicroSecondInNano = int64(time.Microsecond / time.Nanosecond) ) type ( pinotVisibilityStore struct { pinotClient pnt.GenericClient producer messaging.Producer logger log.Logger config *service.Config pinotQueryValidator *pnt.VisibilityQueryValidator } ) var _ p.VisibilityStore = (*pinotVisibilityStore)(nil) func NewPinotVisibilityStore( pinotClient pnt.GenericClient, config *service.Config, producer messaging.Producer, logger log.Logger, ) p.VisibilityStore { if producer == nil { // must be bug, check history setup logger.Fatal("message producer is nil") } return &pinotVisibilityStore{ pinotClient: pinotClient, producer: producer, logger: logger.WithTags(tag.ComponentPinotVisibilityManager), config: config, pinotQueryValidator: pnt.NewPinotQueryValidator(config.ValidSearchAttributes, config.PinotOptimizedQueryColumns), } } func (v *pinotVisibilityStore) Close() { // Not needed for pinot, just keep for visibility store interface } func (v *pinotVisibilityStore) GetName() string { return constants.PinotPersistenceName } func (v *pinotVisibilityStore) RecordWorkflowExecutionStarted( ctx context.Context, request *p.InternalRecordWorkflowExecutionStartedRequest, ) error { msg, err := createVisibilityMessage( request.DomainUUID, request.WorkflowID, request.RunID, request.WorkflowTypeName, request.TaskList, request.StartTimestamp.UnixMilli(), request.ExecutionTimestamp.UnixMilli(), request.TaskID, request.IsCron, request.NumClusters, -1, // represent invalid close time, means open workflow execution -1, // represent invalid close status, means open workflow execution 0, // will be updated when workflow execution updates request.UpdateTimestamp.UnixMilli(), int64(request.ShardID), request.SearchAttributes, false, request.Memo, ) if err != nil { return err } return v.producer.Publish(ctx, msg) } func (v *pinotVisibilityStore) RecordWorkflowExecutionClosed(ctx context.Context, request *p.InternalRecordWorkflowExecutionClosedRequest) error { msg, err := createVisibilityMessage( request.DomainUUID, request.WorkflowID, request.RunID, request.WorkflowTypeName, request.TaskList, request.StartTimestamp.UnixMilli(), request.ExecutionTimestamp.UnixMilli(), request.TaskID, request.IsCron, request.NumClusters, request.CloseTimestamp.UnixMilli(), *thrift.FromWorkflowExecutionCloseStatus(&request.Status), request.HistoryLength, request.UpdateTimestamp.UnixMilli(), int64(request.ShardID), request.SearchAttributes, false, request.Memo, ) if err != nil { return err } return v.producer.Publish(ctx, msg) } func (v *pinotVisibilityStore) RecordWorkflowExecutionUninitialized(ctx context.Context, request *p.InternalRecordWorkflowExecutionUninitializedRequest) error { msg, err := createVisibilityMessage( request.DomainUUID, request.WorkflowID, request.RunID, request.WorkflowTypeName, "", -1, -1, 0, false, 0, -1, // represent invalid close time, means open workflow execution -1, // represent invalid close status, means open workflow execution 0, // will be updated when workflow execution updates request.UpdateTimestamp.UnixMilli(), request.ShardID, nil, false, nil, ) if err != nil { return err } return v.producer.Publish(ctx, msg) } func (v *pinotVisibilityStore) UpsertWorkflowExecution(ctx context.Context, request *p.InternalUpsertWorkflowExecutionRequest) error { msg, err := createVisibilityMessage( request.DomainUUID, request.WorkflowID, request.RunID, request.WorkflowTypeName, request.TaskList, request.StartTimestamp.UnixMilli(), request.ExecutionTimestamp.UnixMilli(), request.TaskID, request.IsCron, request.NumClusters, -1, // represent invalid close time, means open workflow execution -1, // represent invalid close status, means open workflow execution 0, // will not be used request.UpdateTimestamp.UnixMilli(), request.ShardID, request.SearchAttributes, false, request.Memo, ) if err != nil { return err } return v.producer.Publish(ctx, msg) } func (v *pinotVisibilityStore) DeleteWorkflowExecution( ctx context.Context, request *p.VisibilityDeleteWorkflowExecutionRequest, ) error { msg, err := createDeleteVisibilityMessage( request.DomainID, request.WorkflowID, request.RunID, true, ) if err != nil { return err } return v.producer.Publish(ctx, msg) } func (v *pinotVisibilityStore) DeleteUninitializedWorkflowExecution( ctx context.Context, request *p.VisibilityDeleteWorkflowExecutionRequest, ) error { // verify if it is uninitialized workflow execution record // if it is, then call the existing delete method to delete query := fmt.Sprintf("StartTime = missing and DomainID = '%s' and RunID = '%s'", request.DomainID, request.RunID) queryRequest := &p.CountWorkflowExecutionsRequest{ Domain: request.Domain, Query: query, } resp, err := v.CountWorkflowExecutions(ctx, queryRequest) if err != nil { return err } if resp.Count > 0 { if err = v.DeleteWorkflowExecution(ctx, request); err != nil { return err } } return nil } func (v *pinotVisibilityStore) ListOpenWorkflowExecutions( ctx context.Context, request *p.InternalListWorkflowExecutionsRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.StartTime) && !rec.StartTime.After(request.LatestTime) } query, err := getListWorkflowExecutionsQuery(v.pinotClient.GetTableName(), request, false) if err != nil { v.logger.Error(fmt.Sprintf("failed to build list workflow executions query %v", err)) return nil, err } req := &pnt.SearchRequest{ Query: query, IsOpen: true, Filter: isRecordValid, MaxResultWindow: v.config.ESIndexMaxResultWindow(), ListRequest: request, } return v.pinotClient.Search(req) } func (v *pinotVisibilityStore) ListClosedWorkflowExecutions( ctx context.Context, request *p.InternalListWorkflowExecutionsRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.CloseTime) && !rec.CloseTime.After(request.LatestTime) } query, err := getListWorkflowExecutionsQuery(v.pinotClient.GetTableName(), request, true) if err != nil { v.logger.Error(fmt.Sprintf("failed to build list workflow executions query %v", err)) return nil, err } req := &pnt.SearchRequest{ Query: query, IsOpen: true, Filter: isRecordValid, MaxResultWindow: v.config.ESIndexMaxResultWindow(), ListRequest: request, } return v.pinotClient.Search(req) } func (v *pinotVisibilityStore) ListOpenWorkflowExecutionsByType(ctx context.Context, request *p.InternalListWorkflowExecutionsByTypeRequest) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.StartTime) && !rec.StartTime.After(request.LatestTime) } query, err := getListWorkflowExecutionsByTypeQuery(v.pinotClient.GetTableName(), request, false) if err != nil { v.logger.Error(fmt.Sprintf("failed to build list workflow executions by type query %v", err)) return nil, err } req := &pnt.SearchRequest{ Query: query, IsOpen: true, Filter: isRecordValid, MaxResultWindow: v.config.ESIndexMaxResultWindow(), ListRequest: &request.InternalListWorkflowExecutionsRequest, } return v.pinotClient.Search(req) } func (v *pinotVisibilityStore) ListClosedWorkflowExecutionsByType(ctx context.Context, request *p.InternalListWorkflowExecutionsByTypeRequest) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.CloseTime) && !rec.CloseTime.After(request.LatestTime) } query, err := getListWorkflowExecutionsByTypeQuery(v.pinotClient.GetTableName(), request, true) if err != nil { v.logger.Error(fmt.Sprintf("failed to build list workflow executions by type query %v", err)) return nil, err } req := &pnt.SearchRequest{ Query: query, IsOpen: true, Filter: isRecordValid, MaxResultWindow: v.config.ESIndexMaxResultWindow(), ListRequest: &request.InternalListWorkflowExecutionsRequest, } return v.pinotClient.Search(req) } func (v *pinotVisibilityStore) ListOpenWorkflowExecutionsByWorkflowID(ctx context.Context, request *p.InternalListWorkflowExecutionsByWorkflowIDRequest) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.StartTime) && !rec.StartTime.After(request.LatestTime) } query, err := getListWorkflowExecutionsByWorkflowIDQuery(v.pinotClient.GetTableName(), request, false) if err != nil { v.logger.Error(fmt.Sprintf("failed to build list workflow executions by workflowID query %v", err)) return nil, err } req := &pnt.SearchRequest{ Query: query, IsOpen: true, Filter: isRecordValid, MaxResultWindow: v.config.ESIndexMaxResultWindow(), ListRequest: &request.InternalListWorkflowExecutionsRequest, } return v.pinotClient.Search(req) } func (v *pinotVisibilityStore) ListClosedWorkflowExecutionsByWorkflowID(ctx context.Context, request *p.InternalListWorkflowExecutionsByWorkflowIDRequest) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.CloseTime) && !rec.CloseTime.After(request.LatestTime) } query, err := getListWorkflowExecutionsByWorkflowIDQuery(v.pinotClient.GetTableName(), request, true) if err != nil { v.logger.Error(fmt.Sprintf("failed to build list workflow executions by workflowID query %v", err)) return nil, err } req := &pnt.SearchRequest{ Query: query, IsOpen: true, Filter: isRecordValid, MaxResultWindow: v.config.ESIndexMaxResultWindow(), ListRequest: &request.InternalListWorkflowExecutionsRequest, } return v.pinotClient.Search(req) } func (v *pinotVisibilityStore) ListClosedWorkflowExecutionsByStatus(ctx context.Context, request *p.InternalListClosedWorkflowExecutionsByStatusRequest) (*p.InternalListWorkflowExecutionsResponse, error) { isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return !request.EarliestTime.After(rec.CloseTime) && !rec.CloseTime.After(request.LatestTime) } query, err := getListWorkflowExecutionsByStatusQuery(v.pinotClient.GetTableName(), request) if err != nil { v.logger.Error(fmt.Sprintf("failed to build list workflow executions by status query %v", err)) return nil, err } req := &pnt.SearchRequest{ Query: query, IsOpen: true, Filter: isRecordValid, MaxResultWindow: v.config.ESIndexMaxResultWindow(), ListRequest: &request.InternalListWorkflowExecutionsRequest, } return v.pinotClient.Search(req) } func (v *pinotVisibilityStore) GetClosedWorkflowExecution(ctx context.Context, request *p.InternalGetClosedWorkflowExecutionRequest) (*p.InternalGetClosedWorkflowExecutionResponse, error) { query := getGetClosedWorkflowExecutionQuery(v.pinotClient.GetTableName(), request) req := &pnt.SearchRequest{ Query: query, IsOpen: true, Filter: nil, MaxResultWindow: v.config.ESIndexMaxResultWindow(), ListRequest: &p.InternalListWorkflowExecutionsRequest{ // create a new request to avoid nil pointer exceptions DomainUUID: request.DomainUUID, Domain: request.Domain, EarliestTime: time.Time{}, LatestTime: time.Time{}, PageSize: 1, NextPageToken: nil, }, } resp, err := v.pinotClient.Search(req) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("Pinot GetClosedWorkflowExecution failed, %v", err), } } return &p.InternalGetClosedWorkflowExecutionResponse{ Execution: resp.Executions[0], }, nil } func (v *pinotVisibilityStore) ListWorkflowExecutions(ctx context.Context, request *p.ListWorkflowExecutionsByQueryRequest) (*p.InternalListWorkflowExecutionsResponse, error) { checkPageSize(request) query, err := v.getListWorkflowExecutionsByQueryQuery(v.pinotClient.GetTableName(), request) if err != nil { v.logger.Error(fmt.Sprintf("failed to build list workflow executions query %v", err)) return nil, err } req := &pnt.SearchRequest{ Query: query, IsOpen: true, Filter: nil, MaxResultWindow: v.config.ESIndexMaxResultWindow(), ListRequest: &p.InternalListWorkflowExecutionsRequest{ DomainUUID: request.DomainUUID, Domain: request.Domain, EarliestTime: time.Time{}, LatestTime: time.Time{}, NextPageToken: request.NextPageToken, PageSize: request.PageSize, }, } return v.pinotClient.Search(req) } func (v *pinotVisibilityStore) ScanWorkflowExecutions(ctx context.Context, request *p.ListWorkflowExecutionsByQueryRequest) (*p.InternalListWorkflowExecutionsResponse, error) { checkPageSize(request) query, err := v.getListWorkflowExecutionsByQueryQuery(v.pinotClient.GetTableName(), request) if err != nil { v.logger.Error(fmt.Sprintf("failed to build scan workflow executions query %v", err)) return nil, err } req := &pnt.SearchRequest{ Query: query, IsOpen: true, Filter: nil, MaxResultWindow: v.config.ESIndexMaxResultWindow(), ListRequest: &p.InternalListWorkflowExecutionsRequest{ DomainUUID: request.DomainUUID, Domain: request.Domain, EarliestTime: time.Time{}, LatestTime: time.Time{}, NextPageToken: request.NextPageToken, PageSize: request.PageSize, }, } return v.pinotClient.Search(req) } func (v *pinotVisibilityStore) CountWorkflowExecutions(ctx context.Context, request *p.CountWorkflowExecutionsRequest) (*p.CountWorkflowExecutionsResponse, error) { query, err := v.getCountWorkflowExecutionsQuery(v.pinotClient.GetTableName(), request) if err != nil { v.logger.Error(fmt.Sprintf("failed to build count workflow executions query %v", err)) return nil, err } resp, err := v.pinotClient.CountByQuery(query) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("CountClosedWorkflowExecutions failed, %v", err), } } return &p.CountWorkflowExecutionsResponse{ Count: resp, }, nil } // a new function to create visibility message for deletion // don't use the other function and provide some nil values because it may cause nil pointer exceptions func createDeleteVisibilityMessage(domainID string, wid, rid string, isDeleted bool, ) (*indexer.PinotMessage, error) { m := make(map[string]interface{}) m[DomainID] = domainID m[WorkflowID] = wid m[RunID] = rid m[IsDeleted] = isDeleted m[EventTimeMs] = time.Now().UnixMilli() serializedMsg, err := json.Marshal(m) if err != nil { return nil, err } msg := &indexer.PinotMessage{ WorkflowID: common.StringPtr(wid), Payload: serializedMsg, } return msg, nil } func createVisibilityMessage( // common parameters domainID string, wid, rid string, workflowTypeName string, taskList string, startTimeUnixMilli int64, executionTimeUnixMilli int64, taskID int64, isCron bool, numClusters int16, // specific to certain status closeTimeUnixMilli int64, // close execution closeStatus workflow.WorkflowExecutionCloseStatus, // close execution historyLength int64, // close execution updateTimeUnixMilli int64, // update execution, shardID int64, rawSearchAttributes map[string][]byte, isDeleted bool, memo *p.DataBlob, ) (*indexer.PinotMessage, error) { m := make(map[string]interface{}) // loop through all input parameters m[DomainID] = domainID m[WorkflowID] = wid m[RunID] = rid m[WorkflowType] = workflowTypeName m[TaskList] = taskList m[StartTime] = startTimeUnixMilli m[ExecutionTime] = executionTimeUnixMilli m[IsCron] = isCron m[NumClusters] = numClusters m[CloseTime] = closeTimeUnixMilli m[CloseStatus] = int(closeStatus) m[HistoryLength] = historyLength m[UpdateTime] = updateTimeUnixMilli m[ShardID] = shardID m[IsDeleted] = isDeleted m[EventTimeMs] = updateTimeUnixMilli // same as update time when record is upserted, could not use updateTime directly since this will be modified by Pinot SearchAttributes := make(map[string]interface{}) var err error for key, value := range rawSearchAttributes { value, err = isTimeStruct(value) if err != nil { return nil, err } var val interface{} err = json.Unmarshal(value, &val) if err != nil { return nil, err } SearchAttributes[key] = val } if memo != nil && len(memo.Data) > 0 { // add memo into search attr marshalMemo, err := json.Marshal(memo) if err != nil { return nil, err } // string(marshalMemo) is a must-do step, // to give it a type so that it can be converted to a string again // and unmarshaled correctly in the reading side // Also, it doesn't matter to store in Pinot as a pure string since it's not used for search SearchAttributes[Memo] = string(marshalMemo) } m[Attr] = SearchAttributes serializedMsg, err := json.Marshal(m) if err != nil { return nil, err } msg := &indexer.PinotMessage{ WorkflowID: common.StringPtr(wid), Payload: serializedMsg, } return msg, nil } // check if value is time.Time type // if it is, convert it to unixMilli // if it isn't time, return the original value func isTimeStruct(value []byte) ([]byte, error) { var time time.Time err := json.Unmarshal(value, &time) if err == nil { unixTime := time.UnixMilli() value, err = json.Marshal(unixTime) if err != nil { return nil, err } } return value, nil } /****************************** Request Translator ******************************/ type PinotQuery struct { query string filters PinotQueryFilter sorters string limits string } type PinotQueryFilter struct { string } func NewPinotQuery(tableName string) PinotQuery { return PinotQuery{ query: fmt.Sprintf("SELECT *\nFROM %s\n", tableName), filters: PinotQueryFilter{}, sorters: "", limits: "", } } func NewPinotCountQuery(tableName string) PinotQuery { return PinotQuery{ query: fmt.Sprintf("SELECT COUNT(*)\nFROM %s\n", tableName), filters: PinotQueryFilter{}, sorters: "", limits: "", } } func (q *PinotQuery) String() string { return fmt.Sprintf("%s%s%s%s", q.query, q.filters.string, q.sorters, q.limits) } func (q *PinotQuery) concatSorter(sorter string) { q.sorters += sorter + "\n" } func (q *PinotQuery) addPinotSorter(orderBy string, order string) { if q.sorters == "" { q.sorters = "Order BY " } else { q.sorters += ", " } q.sorters += fmt.Sprintf("%s %s\n", orderBy, order) } func (q *PinotQuery) addOffsetAndLimits(offset int, limit int) { q.limits += fmt.Sprintf("LIMIT %d, %d\n", offset, limit) } func (f *PinotQueryFilter) checkFirstFilter() { if f.string == "" { f.string = "WHERE " } else { f.string += "AND " } } func (f *PinotQueryFilter) addEqual(obj string, val interface{}) { f.checkFirstFilter() if _, ok := val.(string); ok { val = fmt.Sprintf("'%s'", val) } else { val = fmt.Sprintf("%v", val) } quotedVal := fmt.Sprintf("%s", val) f.string += fmt.Sprintf("%s = %s\n", obj, quotedVal) } // addQuery adds a complete query into the filter func (f *PinotQueryFilter) addQuery(query string) { f.checkFirstFilter() f.string += fmt.Sprintf("%s\n", query) } // addGte check object is greater than or equals to val func (f *PinotQueryFilter) addGte(obj string, val int) { f.checkFirstFilter() f.string += fmt.Sprintf("%s >= %s\n", obj, fmt.Sprintf("%v", val)) } // addLte check object is less than val func (f *PinotQueryFilter) addLt(obj string, val interface{}) { f.checkFirstFilter() f.string += fmt.Sprintf("%s < %s\n", obj, fmt.Sprintf("%v", val)) } func (f *PinotQueryFilter) addTimeRange(obj string, earliest interface{}, latest interface{}) { f.checkFirstFilter() f.string += fmt.Sprintf("%s BETWEEN %v AND %v\n", obj, earliest, latest) } func (v *pinotVisibilityStore) getCountWorkflowExecutionsQuery(tableName string, request *p.CountWorkflowExecutionsRequest) (string, error) { if request == nil { return "", nil } query := NewPinotCountQuery(tableName) // need to add Domain ID query.filters.addEqual(DomainID, request.DomainUUID) requestQuery := strings.TrimSpace(request.Query) // if customized query is empty, directly return if requestQuery == "" { return query.String(), nil } requestQuery = filterPrefix(requestQuery) comparExpr, _ := parseOrderBy(requestQuery) comparExpr, err := v.pinotQueryValidator.ValidateQuery(comparExpr) if err != nil { return "", &types.BadRequestError{Message: fmt.Sprintf("pinot query validator error: %s, query: %s", err.Error(), request.Query)} } comparExpr = filterPrefix(comparExpr) if comparExpr != "" { query.filters.addQuery(comparExpr) } return query.String(), nil } func (v *pinotVisibilityStore) getListWorkflowExecutionsByQueryQuery(tableName string, request *p.ListWorkflowExecutionsByQueryRequest) (string, error) { if request == nil { return "", nil } token, err := pnt.GetNextPageToken(request.NextPageToken) if err != nil { return "", fmt.Errorf("next page token: %w", err) } query := NewPinotQuery(tableName) // need to add Domain ID query.filters.addEqual(DomainID, request.DomainUUID) requestQuery := strings.TrimSpace(request.Query) // if customized query is empty, directly return if requestQuery == "" { query.addOffsetAndLimits(token.From, request.PageSize) return query.String(), nil } requestQuery = filterPrefix(requestQuery) if common.IsJustOrderByClause(requestQuery) { query.concatSorter(requestQuery) query.addOffsetAndLimits(token.From, request.PageSize) return query.String(), nil } comparExpr, orderBy := parseOrderBy(requestQuery) comparExpr, err = v.pinotQueryValidator.ValidateQuery(comparExpr) if err != nil { return "", &types.BadRequestError{Message: fmt.Sprintf("pinot query validator error: %s, query: %s", err.Error(), request.Query)} } comparExpr = filterPrefix(comparExpr) if comparExpr != "" { query.filters.addQuery(comparExpr) } if orderBy != "" { query.concatSorter(orderBy) } // MUST HAVE! because pagination wouldn't work without order by clause! if query.sorters == "" { query.addPinotSorter(StartTime, "DESC") } query.addOffsetAndLimits(token.From, request.PageSize) return query.String(), nil } func filterPrefix(query string) string { prefix := fmt.Sprintf("`%s.", Attr) postfix := "`" query = strings.ReplaceAll(query, prefix, "") return strings.ReplaceAll(query, postfix, "") } /* Can have cases: 1. A = B 2. A < B 3. A > B 4. A <= B 5. A >= B */ func splitElement(element string) (string, string, string) { if element == "" { return "", "", "" } listLE := strings.Split(element, "<=") listGE := strings.Split(element, ">=") listE := strings.Split(element, "=") listL := strings.Split(element, "<") listG := strings.Split(element, ">") if len(listLE) > 1 { return strings.TrimSpace(listLE[0]), strings.TrimSpace(listLE[1]), "<=" } if len(listGE) > 1 { return strings.TrimSpace(listGE[0]), strings.TrimSpace(listGE[1]), ">=" } if len(listE) > 1 { return strings.TrimSpace(listE[0]), strings.TrimSpace(listE[1]), "=" } if len(listL) > 1 { return strings.TrimSpace(listL[0]), strings.TrimSpace(listL[1]), "<" } if len(listG) > 1 { return strings.TrimSpace(listG[0]), strings.TrimSpace(listG[1]), ">" } return "", "", "" } /* Order by XXX DESC -> if startWith("Order by") -> return "", element CustomizedString = 'cannot be used in order by' -> if last character is ‘ or " -> return element, "" CustomizedInt = 1 (without order by clause) -> if !contains("Order by") -> return element, "" CustomizedString = 'cannot be used in order by' Order by XXX DESC -> Find the index x of last appearance of "order by" -> return element[0, x], element[x, len] CustomizedInt = 1 Order by XXX DESC -> Find the index x of last appearance of "order by" -> return element[0, x], element[x, len] */ func parseOrderBy(element string) (string, string) { // case 1: when order by query also passed in if common.IsJustOrderByClause(element) { return "", element } // case 2: when last element is a string if element[len(element)-1] == '\'' || element[len(element)-1] == '"' { return element, "" } // case 3: when last element doesn't contain "order by" if !strings.Contains(strings.ToLower(element), "order by") { return element, "" } // case 4: general case elementArray := strings.Split(element, " ") orderByIndex := findLastOrderBy(elementArray) // find the last appearance of "order by" is the answer if orderByIndex == 0 { return element, "" } return strings.Join(elementArray[:orderByIndex], " "), strings.Join(elementArray[orderByIndex:], " ") } func findLastOrderBy(list []string) int { for i := len(list) - 2; i >= 0; i-- { if strings.Contains(list[i], "\"") || strings.Contains(list[i], "'") { return 0 // means order by is inside a string } if strings.ToLower(list[i]) == "order" && strings.ToLower(list[i+1]) == "by" { return i } } return 0 } func getListWorkflowExecutionsQuery(tableName string, request *p.InternalListWorkflowExecutionsRequest, isClosed bool) (string, error) { if request == nil { return "", nil } token, err := pnt.GetNextPageToken(request.NextPageToken) if err != nil { return "", fmt.Errorf("next page token: %w", err) } from := token.From pageSize := request.PageSize query := NewPinotQuery(tableName) query.filters.addEqual(DomainID, request.DomainUUID) earliest := request.EarliestTime.UnixMilli() - oneMicroSecondInNano latest := request.LatestTime.UnixMilli() + oneMicroSecondInNano if isClosed { query.filters.addTimeRange(CloseTime, earliest, latest) // convert Unix Time to miliseconds query.filters.addGte(CloseStatus, 0) } else { query.filters.addTimeRange(StartTime, earliest, latest) // convert Unix Time to miliseconds query.filters.addEqual(CloseStatus, -1) query.filters.addEqual(CloseTime, -1) } query.addPinotSorter(StartTime, DescendingOrder) query.addOffsetAndLimits(from, pageSize) return query.String(), nil } func getListWorkflowExecutionsByTypeQuery(tableName string, request *p.InternalListWorkflowExecutionsByTypeRequest, isClosed bool) (string, error) { if request == nil { return "", nil } query := NewPinotQuery(tableName) query.filters.addEqual(DomainID, request.DomainUUID) query.filters.addEqual(WorkflowType, request.WorkflowTypeName) earliest := request.EarliestTime.UnixMilli() - oneMicroSecondInNano latest := request.LatestTime.UnixMilli() + oneMicroSecondInNano if isClosed { query.filters.addTimeRange(CloseTime, earliest, latest) // convert Unix Time to miliseconds query.filters.addGte(CloseStatus, 0) } else { query.filters.addTimeRange(StartTime, earliest, latest) // convert Unix Time to miliseconds query.filters.addEqual(CloseStatus, -1) query.filters.addEqual(CloseTime, -1) } query.addPinotSorter(StartTime, DescendingOrder) token, err := pnt.GetNextPageToken(request.NextPageToken) if err != nil { return "", fmt.Errorf("next page token: %w", err) } from := token.From pageSize := request.PageSize query.addOffsetAndLimits(from, pageSize) return query.String(), nil } func getListWorkflowExecutionsByWorkflowIDQuery(tableName string, request *p.InternalListWorkflowExecutionsByWorkflowIDRequest, isClosed bool) (string, error) { if request == nil { return "", nil } query := NewPinotQuery(tableName) query.filters.addEqual(DomainID, request.DomainUUID) query.filters.addEqual(WorkflowID, request.WorkflowID) earliest := request.EarliestTime.UnixMilli() - oneMicroSecondInNano latest := request.LatestTime.UnixMilli() + oneMicroSecondInNano if isClosed { query.filters.addTimeRange(CloseTime, earliest, latest) // convert Unix Time to miliseconds query.filters.addGte(CloseStatus, 0) } else { query.filters.addTimeRange(StartTime, earliest, latest) // convert Unix Time to miliseconds query.filters.addEqual(CloseStatus, -1) query.filters.addEqual(CloseTime, -1) } query.addPinotSorter(StartTime, DescendingOrder) token, err := pnt.GetNextPageToken(request.NextPageToken) if err != nil { return "", fmt.Errorf("next page token: %w", err) } from := token.From pageSize := request.PageSize query.addOffsetAndLimits(from, pageSize) return query.String(), nil } func getListWorkflowExecutionsByStatusQuery(tableName string, request *p.InternalListClosedWorkflowExecutionsByStatusRequest) (string, error) { if request == nil { return "", nil } query := NewPinotQuery(tableName) query.filters.addEqual(DomainID, request.DomainUUID) status := 0 switch request.Status.String() { case "COMPLETED": status = 0 case "FAILED": status = 1 case "CANCELED": status = 2 case "TERMINATED": status = 3 case "CONTINUED_AS_NEW": status = 4 case "TIMED_OUT": status = 5 } query.filters.addEqual(CloseStatus, status) query.filters.addTimeRange(CloseTime, request.EarliestTime.UnixMilli(), request.LatestTime.UnixMilli()) // convert Unix Time to miliseconds query.addPinotSorter(StartTime, DescendingOrder) token, err := pnt.GetNextPageToken(request.NextPageToken) if err != nil { return "", fmt.Errorf("next page token: %w", err) } from := token.From pageSize := request.PageSize query.addOffsetAndLimits(from, pageSize) return query.String(), nil } func getGetClosedWorkflowExecutionQuery(tableName string, request *p.InternalGetClosedWorkflowExecutionRequest) string { if request == nil { return "" } query := NewPinotQuery(tableName) query.filters.addEqual(DomainID, request.DomainUUID) query.filters.addGte(CloseStatus, 0) query.filters.addEqual(WorkflowID, request.Execution.GetWorkflowID()) rid := request.Execution.GetRunID() if rid != "" { query.filters.addEqual(RunID, rid) } return query.String() } func checkPageSize(request *p.ListWorkflowExecutionsByQueryRequest) { if request.PageSize == 0 { request.PageSize = 1000 } } ================================================ FILE: common/persistence/pinot/pinot_visibility_store_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pinotvisibility import ( "context" "encoding/json" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.uber.org/mock/gomock" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" p "github.com/uber/cadence/common/persistence" pnt "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" ) var ( testIndex = "test-index" testDomain = "test-domain" testDomainID = "bfd5c907-f899-4baf-a7b2-2ab85e623ebd" testPageSize = 10 testEarliestTime = int64(1547596872371000000) testLatestTime = int64(2547596872371000000) testWorkflowType = "test-wf-type" testWorkflowID = "test-wid" testCloseStatus = int32(1) testTableName = "test-table-name" testContextTimeout = 5 * time.Second validSearchAttr = definition.GetDefaultIndexedKeys() errNextPageToken = fmt.Errorf("next page token: unable to deserialize page token. err: invalid character 'e' looking for beginning of value") ) func TestRecordWorkflowExecutionStarted(t *testing.T) { // test non-empty request fields match errorRequest := &p.InternalRecordWorkflowExecutionStartedRequest{ WorkflowID: "wid", Memo: p.NewDataBlob([]byte(`test bytes`), constants.EncodingTypeThriftRW), SearchAttributes: map[string][]byte{ "CustomStringField": []byte("test string"), "CustomTimeField": []byte("2020-01-01T00:00:00Z"), }, } request := &p.InternalRecordWorkflowExecutionStartedRequest{ WorkflowID: "wid", Memo: p.NewDataBlob([]byte(`test bytes`), constants.EncodingTypeThriftRW), } customStringField, err := json.Marshal("test string") assert.NoError(t, err) requestWithSearchAttributes := &p.InternalRecordWorkflowExecutionStartedRequest{ WorkflowID: "wid", Memo: p.NewDataBlob([]byte(`test bytes`), constants.EncodingTypeThriftRW), SearchAttributes: map[string][]byte{ "CustomStringField": customStringField, }, } tests := map[string]struct { request *p.InternalRecordWorkflowExecutionStartedRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) expectedError error }{ "Case1: error case": { request: errorRequest, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(&types.BadRequestError{}).Once() }, expectedError: fmt.Errorf("invalid character 'e' in literal true (expecting 'r')"), }, "Case2: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { assert.Equal(t, request.WorkflowID, input.GetWorkflowID()) return true })).Return(nil).Once() }, expectedError: nil, }, "Case3: normal case with search attributes": { request: requestWithSearchAttributes, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { assert.Equal(t, request.WorkflowID, input.GetWorkflowID()) return true })).Return(nil).Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.producerMockAffordance(mockProducer) err := visibilityStore.RecordWorkflowExecutionStarted(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestRecordWorkflowExecutionClosed(t *testing.T) { // test non-empty request fields match errorRequest := &p.InternalRecordWorkflowExecutionClosedRequest{ WorkflowID: "error-wid", Memo: p.NewDataBlob([]byte(`test bytes`), constants.EncodingTypeThriftRW), SearchAttributes: map[string][]byte{ "CustomStringField": []byte("test string"), "CustomTimeField": []byte("2020-01-01T00:00:00Z"), }, } request := &p.InternalRecordWorkflowExecutionClosedRequest{ WorkflowID: "wid", Memo: p.NewDataBlob([]byte(`test bytes`), constants.EncodingTypeThriftRW), } tests := map[string]struct { request *p.InternalRecordWorkflowExecutionClosedRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) expectedError error }{ "Case1: error case": { request: errorRequest, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(&types.BadRequestError{}).Once() }, expectedError: fmt.Errorf("invalid character 'e' in literal true (expecting 'r')"), }, "Case2: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { assert.Equal(t, request.WorkflowID, input.GetWorkflowID()) return true })).Return(nil).Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.producerMockAffordance(mockProducer) err := visibilityStore.RecordWorkflowExecutionClosed(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestRecordWorkflowExecutionUninitialized(t *testing.T) { request := &p.InternalRecordWorkflowExecutionUninitializedRequest{ WorkflowID: "wid", } tests := map[string]struct { request *p.InternalRecordWorkflowExecutionUninitializedRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) expectedError error }{ "Case1: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { assert.Equal(t, request.WorkflowID, input.GetWorkflowID()) return true })).Return(nil).Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.producerMockAffordance(mockProducer) err := visibilityStore.RecordWorkflowExecutionUninitialized(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestUpsertWorkflowExecution(t *testing.T) { // test non-empty request fields match errorRequest := &p.InternalUpsertWorkflowExecutionRequest{ WorkflowID: "error-wid", Memo: p.NewDataBlob([]byte(`test bytes`), constants.EncodingTypeThriftRW), SearchAttributes: map[string][]byte{ "CustomStringField": []byte("test string"), "CustomTimeField": []byte("2020-01-01T00:00:00Z"), }, } request := &p.InternalUpsertWorkflowExecutionRequest{} request.WorkflowID = "wid" memoBytes := []byte(`test bytes`) request.Memo = p.NewDataBlob(memoBytes, constants.EncodingTypeThriftRW) tests := map[string]struct { request *p.InternalUpsertWorkflowExecutionRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) expectedError error }{ "Case1: error case": { request: errorRequest, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { return true })).Return(fmt.Errorf("error")).Once() }, expectedError: fmt.Errorf("invalid character 'e' in literal true (expecting 'r')"), }, "Case2: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { assert.Equal(t, request.WorkflowID, input.GetWorkflowID()) return true })).Return(nil).Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.producerMockAffordance(mockProducer) err := visibilityStore.UpsertWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestDeleteWorkflowExecution(t *testing.T) { // test non-empty request fields match request := &p.VisibilityDeleteWorkflowExecutionRequest{} request.WorkflowID = "wid" tests := map[string]struct { request *p.VisibilityDeleteWorkflowExecutionRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) expectedError error }{ "Case1: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { assert.Equal(t, request.WorkflowID, input.GetWorkflowID()) return true })).Return(nil).Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.producerMockAffordance(mockProducer) err := visibilityStore.DeleteWorkflowExecution(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestDeleteUninitializedWorkflowExecution(t *testing.T) { // test non-empty request fields match request := &p.VisibilityDeleteWorkflowExecutionRequest{ Domain: "domain", DomainID: "domainID", WorkflowID: "wid", RunID: "rid", TaskID: int64(111), } tests := map[string]struct { request *p.VisibilityDeleteWorkflowExecutionRequest producerMockAffordance func(mockProducer *mocks.KafkaProducer) expectedError error }{ "Case1: normal case": { request: request, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { assert.Equal(t, request.WorkflowID, input.GetWorkflowID()) return true })).Return(nil).Once() }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().CountByQuery(gomock.Any()).Return(int64(1), nil).Times(1) test.producerMockAffordance(mockProducer) err := visibilityStore.DeleteUninitializedWorkflowExecution(context.Background(), test.request) assert.Equal(t, test.expectedError, err) }) } } func TestListOpenWorkflowExecutions(t *testing.T) { errorRequest := &p.InternalListWorkflowExecutionsRequest{ Domain: DomainID, NextPageToken: []byte("error"), } request := &p.InternalListWorkflowExecutionsRequest{ Domain: DomainID, } tests := map[string]struct { request *p.InternalListWorkflowExecutionsRequest expectedResp *p.InternalListWorkflowExecutionsResponse pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) }, expectedError: errNextPageToken, }, "Case2: normal case with nil response": { request: request, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) resp, err := visibilityStore.ListOpenWorkflowExecutions(context.Background(), test.request) assert.Equal(t, test.expectedResp, resp) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListClosedWorkflowExecutions(t *testing.T) { errorRequest := &p.InternalListWorkflowExecutionsRequest{ Domain: DomainID, NextPageToken: []byte("error"), } request := &p.InternalListWorkflowExecutionsRequest{ Domain: DomainID, } tests := map[string]struct { request *p.InternalListWorkflowExecutionsRequest expectedResp *p.InternalListWorkflowExecutionsResponse pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) }, expectedError: errNextPageToken, }, "Case2: normal case with nil response": { request: request, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) resp, err := visibilityStore.ListClosedWorkflowExecutions(context.Background(), test.request) assert.Equal(t, test.expectedResp, resp) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListOpenWorkflowExecutionsByType(t *testing.T) { errorRequest := &p.InternalListWorkflowExecutionsByTypeRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ Domain: DomainID, NextPageToken: []byte("error"), }, WorkflowTypeName: "", } request := &p.InternalListWorkflowExecutionsByTypeRequest{} tests := map[string]struct { request *p.InternalListWorkflowExecutionsByTypeRequest expectedResp *p.InternalListWorkflowExecutionsResponse pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) }, expectedError: errNextPageToken, }, "Case2: normal case with nil response": { request: request, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) resp, err := visibilityStore.ListOpenWorkflowExecutionsByType(context.Background(), test.request) assert.Equal(t, test.expectedResp, resp) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListClosedWorkflowExecutionsByType(t *testing.T) { errorRequest := &p.InternalListWorkflowExecutionsByTypeRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ Domain: DomainID, NextPageToken: []byte("error"), }, WorkflowTypeName: "", } request := &p.InternalListWorkflowExecutionsByTypeRequest{} tests := map[string]struct { request *p.InternalListWorkflowExecutionsByTypeRequest expectedResp *p.InternalListWorkflowExecutionsResponse pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) }, expectedError: errNextPageToken, }, "Case2: normal case with nil response": { request: request, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) resp, err := visibilityStore.ListClosedWorkflowExecutionsByType(context.Background(), test.request) assert.Equal(t, test.expectedResp, resp) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListOpenWorkflowExecutionsByWorkflowID(t *testing.T) { errorRequest := &p.InternalListWorkflowExecutionsByWorkflowIDRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ Domain: DomainID, NextPageToken: []byte("error"), }, } request := &p.InternalListWorkflowExecutionsByWorkflowIDRequest{} tests := map[string]struct { request *p.InternalListWorkflowExecutionsByWorkflowIDRequest expectedResp *p.InternalListWorkflowExecutionsResponse pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) }, expectedError: errNextPageToken, }, "Case2: normal case with nil response": { request: request, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) resp, err := visibilityStore.ListOpenWorkflowExecutionsByWorkflowID(context.Background(), test.request) assert.Equal(t, test.expectedResp, resp) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListClosedWorkflowExecutionsByWorkflowID(t *testing.T) { errorRequest := &p.InternalListWorkflowExecutionsByWorkflowIDRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ Domain: DomainID, NextPageToken: []byte("error"), }, } request := &p.InternalListWorkflowExecutionsByWorkflowIDRequest{} tests := map[string]struct { request *p.InternalListWorkflowExecutionsByWorkflowIDRequest expectedResp *p.InternalListWorkflowExecutionsResponse pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) }, expectedError: errNextPageToken, }, "Case2: normal case with nil response": { request: request, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) resp, err := visibilityStore.ListClosedWorkflowExecutionsByWorkflowID(context.Background(), test.request) assert.Equal(t, test.expectedResp, resp) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListClosedWorkflowExecutionsByStatus(t *testing.T) { errorRequest := &p.InternalListClosedWorkflowExecutionsByStatusRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ Domain: DomainID, NextPageToken: []byte("error"), }, } request := &p.InternalListClosedWorkflowExecutionsByStatusRequest{} tests := map[string]struct { request *p.InternalListClosedWorkflowExecutionsByStatusRequest expectedResp *p.InternalListWorkflowExecutionsResponse pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) }, expectedError: errNextPageToken, }, "Case2: normal case with nil response": { request: request, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) resp, err := visibilityStore.ListClosedWorkflowExecutionsByStatus(context.Background(), test.request) assert.Equal(t, test.expectedResp, resp) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestGetClosedWorkflowExecution(t *testing.T) { errorRequest := &p.InternalGetClosedWorkflowExecutionRequest{} request := &p.InternalGetClosedWorkflowExecutionRequest{} tests := map[string]struct { request *p.InternalGetClosedWorkflowExecutionRequest expectedResp *p.InternalGetClosedWorkflowExecutionRequest pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(&pnt.SearchResponse{ Executions: []*p.InternalVisibilityWorkflowExecutionInfo{ { DomainID: DomainID, }, }, }, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("Pinot GetClosedWorkflowExecution failed, error"), }, "Case2: normal case with nil response": { request: request, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(&pnt.SearchResponse{ Executions: []*p.InternalVisibilityWorkflowExecutionInfo{ { DomainID: DomainID, }, }, }, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) _, err := visibilityStore.GetClosedWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Equal(t, test.expectedError.Error(), err.Error()) } else { assert.NoError(t, err) } }) } } func TestListWorkflowExecutions(t *testing.T) { errorRequest := &p.ListWorkflowExecutionsByQueryRequest{ NextPageToken: []byte("error"), } request := &p.ListWorkflowExecutionsByQueryRequest{} tests := map[string]struct { request *p.ListWorkflowExecutionsByQueryRequest expectedResp *p.InternalListWorkflowExecutionsResponse pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) }, expectedError: errNextPageToken, }, "Case2: normal case with nil response": { request: request, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) resp, err := visibilityStore.ListWorkflowExecutions(context.Background(), test.request) assert.Equal(t, test.expectedResp, resp) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestScanWorkflowExecutions(t *testing.T) { errorRequest := &p.ListWorkflowExecutionsByQueryRequest{ NextPageToken: []byte("error"), } request := &p.ListWorkflowExecutionsByQueryRequest{} tests := map[string]struct { request *p.ListWorkflowExecutionsByQueryRequest expectedResp *p.InternalListWorkflowExecutionsResponse pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) }, expectedError: errNextPageToken, }, "Case2: normal case with nil response": { request: request, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().Search(gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) resp, err := visibilityStore.ScanWorkflowExecutions(context.Background(), test.request) assert.Equal(t, test.expectedResp, resp) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestCountWorkflowExecutions(t *testing.T) { errorRequest := &p.CountWorkflowExecutionsRequest{} request := &p.CountWorkflowExecutionsRequest{} tests := map[string]struct { request *p.CountWorkflowExecutionsRequest expectedResp *p.CountWorkflowExecutionsResponse pinotClientMockAffordance func(mockPinotClient *pnt.MockGenericClient) expectedError error }{ "Case1: error case": { request: errorRequest, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().CountByQuery(gomock.Any()).Return(int64(0), fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("CountClosedWorkflowExecutions failed, error"), }, "Case2: normal case with nil response": { request: request, expectedResp: &p.CountWorkflowExecutionsResponse{Count: 1}, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) mockPinotClient.EXPECT().CountByQuery(gomock.Any()).Return(int64(1), nil).Times(1) }, expectedError: nil, }, "Case3: query error case": { request: &p.CountWorkflowExecutionsRequest{Domain: testDomain, DomainUUID: testDomainID, Query: "CustomKeywordField = missing"}, expectedResp: nil, pinotClientMockAffordance: func(mockPinotClient *pnt.MockGenericClient) { mockPinotClient.EXPECT().GetTableName().Return(testTableName).Times(1) }, expectedError: fmt.Errorf("pinot query validator error: invalid comparison expression, right, query: CustomKeywordField = missing"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) test.pinotClientMockAffordance(mockPinotClient) resp, err := visibilityStore.CountWorkflowExecutions(context.Background(), test.request) assert.Equal(t, test.expectedResp, resp) if test.expectedError != nil { assert.Equal(t, test.expectedError.Error(), err.Error()) } else { assert.NoError(t, err) } }) } } func TestGetName(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) assert.NotEmpty(t, visibilityStore.GetName()) } func TestNewPinotVisibilityStore(t *testing.T) { mockPinotClient := &pnt.MockGenericClient{} assert.NotPanics(t, func() { NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), }, nil, log.NewNoop()) }) } func TestGetCountWorkflowExecutionsQuery(t *testing.T) { emptyQueryRequest := &p.CountWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, Query: "", } expectEmptyQueryResult := fmt.Sprintf(`SELECT COUNT(*) FROM test-table-name WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' `) request := &p.CountWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, Query: "WorkflowID = 'wfid'", } expectResult := fmt.Sprintf(`SELECT COUNT(*) FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND WorkflowID = 'wfid' `, testTableName) tests := map[string]struct { request *p.CountWorkflowExecutionsRequest expectedRes string expectedError error }{ "Case1: normal case with nil response": { request: request, expectedRes: expectResult, expectedError: nil, }, "Case2: normal case with empty query": { request: emptyQueryRequest, expectedRes: expectEmptyQueryResult, expectedError: nil, }, "Case3: custom attr is missing case": { request: &p.CountWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, Query: "CustomKeywordField = missing", }, expectedRes: "", expectedError: fmt.Errorf("pinot query validator error: invalid comparison expression, right, query: CustomKeywordField = missing"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) res, err := visibilityStore.getCountWorkflowExecutionsQuery(testTableName, test.request) assert.Equal(t, test.expectedRes, res) if test.expectedError != nil { assert.Equal(t, test.expectedError.Error(), err.Error()) } }) } } func TestGetListWorkflowExecutionQuery(t *testing.T) { token := pnt.PinotVisibilityPageToken{ From: 11, } serializedToken, err := json.Marshal(token) if err != nil { panic(fmt.Sprintf("Serialized error in PinotVisibilityStoreTest!!!, %s", err)) } tests := map[string]struct { input *p.ListWorkflowExecutionsByQueryRequest expectedOutput string expectedError bool }{ "complete request with keyword query only": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "`Attr.CustomKeywordField` = 'keywordCustomized'", }, expectedOutput: fmt.Sprintf( `SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND (JSON_MATCH(Attr, '"$.CustomKeywordField"=''keywordCustomized''') or JSON_MATCH(Attr, '"$.CustomKeywordField[*]"=''keywordCustomized''')) Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectedError: false, }, "complete request from search attribute worker": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "CustomIntField=2 and CustomKeywordField='Update2' order by `Attr.CustomDatetimeField` DESC", }, expectedOutput: fmt.Sprintf( `SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND JSON_MATCH(Attr, '"$.CustomIntField"=''2''') and (JSON_MATCH(Attr, '"$.CustomKeywordField"=''Update2''') or JSON_MATCH(Attr, '"$.CustomKeywordField[*]"=''Update2''')) order by CustomDatetimeField DESC LIMIT 0, 10 `, testTableName), expectedError: false, }, "complete request with keyword query and other customized query": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "CustomKeywordField = 'keywordCustomized' and CustomStringField = 'String and or order by'", }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND (JSON_MATCH(Attr, '"$.CustomKeywordField"=''keywordCustomized''') or JSON_MATCH(Attr, '"$.CustomKeywordField[*]"=''keywordCustomized''')) and JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*String and or order by.*'')') Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectedError: false, }, "complete request with or query & customized attributes": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "CustomStringField = 'Or' or CustomStringField = 'and' Order by StartTime DESC", }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND (JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*Or.*'')') or JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*and.*'')')) Order by StartTime DESC LIMIT 0, 10 `, testTableName), expectedError: false, }, "complex query": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "WorkflowID = 'wid' and ((CustomStringField = 'custom and custom2 or custom3 order by') or CustomIntField between 1 and 10)", }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND WorkflowID = 'wid' and (JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*custom and custom2 or custom3 order by.*'')') or (JSON_MATCH(Attr, '"$.CustomIntField" is not null') AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomIntField') AS INT) >= 1 AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomIntField') AS INT) <= 10)) Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectedError: false, }, "or clause with custom attributes": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "CustomIntField = 1 or CustomIntField = 2", }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND (JSON_MATCH(Attr, '"$.CustomIntField"=''1''') or JSON_MATCH(Attr, '"$.CustomIntField"=''2''')) Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectedError: false, }, "complete request with customized query with missing": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "CloseTime = missing anD WorkflowType = 'some-test-workflow'", }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseTime = -1 and WorkflowType = 'some-test-workflow' Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectedError: false, }, "complete request with customized query with NextPageToken": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: serializedToken, Query: "CloseStatus = -1 and CustomKeywordField = 'keywordCustomized' AND CustomIntField<=10 and CustomStringField = 'String field is for text' Order by DomainID Desc", }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseStatus = -1 and (JSON_MATCH(Attr, '"$.CustomKeywordField"=''keywordCustomized''') or JSON_MATCH(Attr, '"$.CustomKeywordField[*]"=''keywordCustomized''')) and (JSON_MATCH(Attr, '"$.CustomIntField" is not null') AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomIntField') AS INT) <= 10) and JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*String field is for text.*'')') Order by DomainID Desc LIMIT 11, 10 `, testTableName), expectedError: false, }, "complete request with order by query": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "Order by DomainId Desc", }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' Order by DomainId Desc LIMIT 0, 10 `, testTableName), expectedError: false, }, "complete request with filter query": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "CloseStatus < 0", }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseStatus < 0 Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectedError: false, }, "complete request with empty query": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "", }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' LIMIT 0, 10 `, testTableName), expectedError: false, }, "empty request": { input: &p.ListWorkflowExecutionsByQueryRequest{}, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = '' LIMIT 0, 0 `, testTableName), expectedError: false, }, "nil request": { input: nil, expectedOutput: "", expectedError: false, }, "request with syntax error": { input: &p.ListWorkflowExecutionsByQueryRequest{ DomainUUID: testDomainID, Domain: testDomain, PageSize: testPageSize, NextPageToken: nil, Query: "WorkflowType = test", }, expectedOutput: "", expectedError: true, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) output, err := visibilityStore.getListWorkflowExecutionsByQueryQuery(testTableName, test.input) assert.Equal(t, test.expectedOutput, output) if test.expectedError { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestGetListWorkflowExecutionsQuery(t *testing.T) { request := &p.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, EarliestTime: time.Unix(0, testEarliestTime), LatestTime: time.Unix(0, testLatestTime), PageSize: testPageSize, NextPageToken: nil, } closeResult, err1 := getListWorkflowExecutionsQuery(testTableName, request, true) openResult, err2 := getListWorkflowExecutionsQuery(testTableName, request, false) nilResult, err3 := getListWorkflowExecutionsQuery(testTableName, nil, true) expectCloseResult := fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseTime BETWEEN 1547596871371 AND 2547596873371 AND CloseStatus >= 0 Order BY StartTime DESC LIMIT 0, 10 `, testTableName) expectOpenResult := fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND StartTime BETWEEN 1547596871371 AND 2547596873371 AND CloseStatus = -1 AND CloseTime = -1 Order BY StartTime DESC LIMIT 0, 10 `, testTableName) expectNilResult := "" assert.Equal(t, closeResult, expectCloseResult) assert.Equal(t, openResult, expectOpenResult) assert.Equal(t, nilResult, expectNilResult) assert.NoError(t, err1) assert.NoError(t, err2) assert.NoError(t, err3) } func TestGetListWorkflowExecutionsByTypeQuery(t *testing.T) { request := &p.InternalListWorkflowExecutionsByTypeRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, EarliestTime: time.Unix(0, testEarliestTime), LatestTime: time.Unix(0, testLatestTime), PageSize: testPageSize, NextPageToken: nil, }, WorkflowTypeName: testWorkflowType, } closeResult, err1 := getListWorkflowExecutionsByTypeQuery(testTableName, request, true) openResult, err2 := getListWorkflowExecutionsByTypeQuery(testTableName, request, false) nilResult, err3 := getListWorkflowExecutionsByTypeQuery(testTableName, nil, true) expectCloseResult := fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND WorkflowType = 'test-wf-type' AND CloseTime BETWEEN 1547596871371 AND 2547596873371 AND CloseStatus >= 0 Order BY StartTime DESC LIMIT 0, 10 `, testTableName) expectOpenResult := fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND WorkflowType = 'test-wf-type' AND StartTime BETWEEN 1547596871371 AND 2547596873371 AND CloseStatus = -1 AND CloseTime = -1 Order BY StartTime DESC LIMIT 0, 10 `, testTableName) expectNilResult := "" assert.Equal(t, closeResult, expectCloseResult) assert.Equal(t, openResult, expectOpenResult) assert.Equal(t, nilResult, expectNilResult) assert.NoError(t, err1) assert.NoError(t, err2) assert.NoError(t, err3) } func TestGetListWorkflowExecutionsByWorkflowIDQuery(t *testing.T) { request := &p.InternalListWorkflowExecutionsByWorkflowIDRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, EarliestTime: time.Unix(0, testEarliestTime), LatestTime: time.Unix(0, testLatestTime), PageSize: testPageSize, NextPageToken: nil, }, WorkflowID: testWorkflowID, } closeResult, err1 := getListWorkflowExecutionsByWorkflowIDQuery(testTableName, request, true) openResult, err2 := getListWorkflowExecutionsByWorkflowIDQuery(testTableName, request, false) nilResult, err3 := getListWorkflowExecutionsByWorkflowIDQuery(testTableName, nil, true) expectCloseResult := fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND WorkflowID = 'test-wid' AND CloseTime BETWEEN 1547596871371 AND 2547596873371 AND CloseStatus >= 0 Order BY StartTime DESC LIMIT 0, 10 `, testTableName) expectOpenResult := fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND WorkflowID = 'test-wid' AND StartTime BETWEEN 1547596871371 AND 2547596873371 AND CloseStatus = -1 AND CloseTime = -1 Order BY StartTime DESC LIMIT 0, 10 `, testTableName) expectNilResult := "" assert.Equal(t, closeResult, expectCloseResult) assert.Equal(t, openResult, expectOpenResult) assert.Equal(t, nilResult, expectNilResult) assert.NoError(t, err1) assert.NoError(t, err2) assert.NoError(t, err3) } func TestGetListWorkflowExecutionsByStatusQuery(t *testing.T) { tests := map[string]struct { inputRequest *p.InternalListClosedWorkflowExecutionsByStatusRequest expectResult string expectError error }{ "Case1: normal case": { inputRequest: nil, expectResult: "", expectError: nil, }, "Case2-0: normal case with close status is 0": { inputRequest: &p.InternalListClosedWorkflowExecutionsByStatusRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, EarliestTime: time.Unix(0, testEarliestTime), LatestTime: time.Unix(0, testLatestTime), PageSize: testPageSize, NextPageToken: nil, }, Status: types.WorkflowExecutionCloseStatus(0), }, expectResult: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseStatus = 0 AND CloseTime BETWEEN 1547596872371 AND 2547596872371 Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectError: nil, }, "Case2-1: normal case with close status is 1": { inputRequest: &p.InternalListClosedWorkflowExecutionsByStatusRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, EarliestTime: time.Unix(0, testEarliestTime), LatestTime: time.Unix(0, testLatestTime), PageSize: testPageSize, NextPageToken: nil, }, Status: types.WorkflowExecutionCloseStatus(1), }, expectResult: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseStatus = 1 AND CloseTime BETWEEN 1547596872371 AND 2547596872371 Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectError: nil, }, "Case2-2: normal case with close status is 2": { inputRequest: &p.InternalListClosedWorkflowExecutionsByStatusRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, EarliestTime: time.Unix(0, testEarliestTime), LatestTime: time.Unix(0, testLatestTime), PageSize: testPageSize, NextPageToken: nil, }, Status: types.WorkflowExecutionCloseStatus(2), }, expectResult: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseStatus = 2 AND CloseTime BETWEEN 1547596872371 AND 2547596872371 Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectError: nil, }, "Case2-3: normal case with close status is 3": { inputRequest: &p.InternalListClosedWorkflowExecutionsByStatusRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, EarliestTime: time.Unix(0, testEarliestTime), LatestTime: time.Unix(0, testLatestTime), PageSize: testPageSize, NextPageToken: nil, }, Status: types.WorkflowExecutionCloseStatus(3), }, expectResult: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseStatus = 3 AND CloseTime BETWEEN 1547596872371 AND 2547596872371 Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectError: nil, }, "Case2-4: normal case with close status is 4": { inputRequest: &p.InternalListClosedWorkflowExecutionsByStatusRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, EarliestTime: time.Unix(0, testEarliestTime), LatestTime: time.Unix(0, testLatestTime), PageSize: testPageSize, NextPageToken: nil, }, Status: types.WorkflowExecutionCloseStatus(4), }, expectResult: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseStatus = 4 AND CloseTime BETWEEN 1547596872371 AND 2547596872371 Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectError: nil, }, "Case2-5: normal case with close status is 5": { inputRequest: &p.InternalListClosedWorkflowExecutionsByStatusRequest{ InternalListWorkflowExecutionsRequest: p.InternalListWorkflowExecutionsRequest{ DomainUUID: testDomainID, Domain: testDomain, EarliestTime: time.Unix(0, testEarliestTime), LatestTime: time.Unix(0, testLatestTime), PageSize: testPageSize, NextPageToken: nil, }, Status: types.WorkflowExecutionCloseStatus(5), }, expectResult: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseStatus = 5 AND CloseTime BETWEEN 1547596872371 AND 2547596872371 Order BY StartTime DESC LIMIT 0, 10 `, testTableName), expectError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { actualResult, actualError := getListWorkflowExecutionsByStatusQuery(testTableName, test.inputRequest) assert.Equal(t, test.expectResult, actualResult) assert.NoError(t, actualError) }) } } func TestGetGetClosedWorkflowExecutionQuery(t *testing.T) { tests := map[string]struct { input *p.InternalGetClosedWorkflowExecutionRequest expectedOutput string }{ "complete request with empty RunId": { input: &p.InternalGetClosedWorkflowExecutionRequest{ DomainUUID: testDomainID, Domain: testDomain, Execution: types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: "", }, }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseStatus >= 0 AND WorkflowID = 'test-wid' `, testTableName), }, "complete request with runId": { input: &p.InternalGetClosedWorkflowExecutionRequest{ DomainUUID: testDomainID, Domain: testDomain, Execution: types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: "runid", }, }, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = 'bfd5c907-f899-4baf-a7b2-2ab85e623ebd' AND CloseStatus >= 0 AND WorkflowID = 'test-wid' AND RunID = 'runid' `, testTableName), }, "empty request": { input: &p.InternalGetClosedWorkflowExecutionRequest{}, expectedOutput: fmt.Sprintf(`SELECT * FROM %s WHERE DomainID = '' AND CloseStatus >= 0 AND WorkflowID = '' `, testTableName), }, "nil request": { input: nil, expectedOutput: "", }, } for name, test := range tests { t.Run(name, func(t *testing.T) { output := getGetClosedWorkflowExecutionQuery(testTableName, test.input) assert.Equal(t, test.expectedOutput, output) }) } } func TestParseLastElement(t *testing.T) { tests := map[string]struct { input string expectedElement string expectedOrderBy string }{ "Case1: only contains order by": { input: "Order by TestInt DESC", expectedElement: "", expectedOrderBy: "Order by TestInt DESC", }, "Case2: only contains order by": { input: "TestString = 'cannot be used in order by'", expectedElement: "TestString = 'cannot be used in order by'", expectedOrderBy: "", }, "Case3: not contains any order by": { input: "TestInt = 1", expectedElement: "TestInt = 1", expectedOrderBy: "", }, "Case4-1: with order by in string & real order by": { input: "TestString = 'cannot be used in order by' Order by TestInt DESC", expectedElement: "TestString = 'cannot be used in order by'", expectedOrderBy: "Order by TestInt DESC", }, "Case4-2: with non-string attribute & real order by": { input: "TestDouble = 1.0 Order by TestInt DESC", expectedElement: "TestDouble = 1.0", expectedOrderBy: "Order by TestInt DESC", }, "Case5: with random case order by": { input: "TestString = 'cannot be used in OrDer by' ORdeR by TestInt DESC", expectedElement: "TestString = 'cannot be used in OrDer by'", expectedOrderBy: "ORdeR by TestInt DESC", }, } for name, test := range tests { t.Run(name, func(t *testing.T) { element, orderBy := parseOrderBy(test.input) assert.Equal(t, test.expectedElement, element) assert.Equal(t, test.expectedOrderBy, orderBy) }) } } func TestSplitElement(t *testing.T) { tests := map[string]struct { input string expectedKey string expectedVal string expectedOp string }{ "Case1-1: A=B": { input: "CustomizedTestField=Test", expectedKey: "CustomizedTestField", expectedVal: "Test", expectedOp: "=", }, "Case1-2: A=\"B\"": { input: "CustomizedTestField=\"Test\"", expectedKey: "CustomizedTestField", expectedVal: "\"Test\"", expectedOp: "=", }, "Case1-3: A='B'": { input: "CustomizedTestField='Test'", expectedKey: "CustomizedTestField", expectedVal: "'Test'", expectedOp: "=", }, "Case2: A<=B": { input: "CustomizedTestField<=Test", expectedKey: "CustomizedTestField", expectedVal: "Test", expectedOp: "<=", }, "Case3: A>=B": { input: "CustomizedTestField>=Test", expectedKey: "CustomizedTestField", expectedVal: "Test", expectedOp: ">=", }, "Case4: A = B": { input: "CustomizedTestField = Test", expectedKey: "CustomizedTestField", expectedVal: "Test", expectedOp: "=", }, "Case5: A <= B": { input: "CustomizedTestField <= Test", expectedKey: "CustomizedTestField", expectedVal: "Test", expectedOp: "<=", }, "Case6: A >= B": { input: "CustomizedTestField >= Test", expectedKey: "CustomizedTestField", expectedVal: "Test", expectedOp: ">=", }, "Case7: A > B": { input: "CustomizedTestField > Test", expectedKey: "CustomizedTestField", expectedVal: "Test", expectedOp: ">", }, "Case8: A < B": { input: "CustomizedTestField < Test", expectedKey: "CustomizedTestField", expectedVal: "Test", expectedOp: "<", }, "Case9: empty": { input: "", expectedKey: "", expectedVal: "", expectedOp: "", }, } for name, test := range tests { t.Run(name, func(t *testing.T) { key, val, op := splitElement(test.input) assert.Equal(t, test.expectedKey, key) assert.Equal(t, test.expectedVal, val) assert.Equal(t, test.expectedOp, op) }) } } func TestIsTimeStruct(t *testing.T) { var emptyInput []byte numberInput := []byte("1709601210000000000") errorInput := []byte("Not a timeStamp") testTime := time.UnixMilli(1709601210000) var legitInput []byte legitInput, err := json.Marshal(testTime) assert.NoError(t, err) legitOutput := testTime.UnixMilli() legitOutputJSON, _ := json.Marshal(legitOutput) tests := map[string]struct { input []byte expectedOutput []byte expectedError error }{ "Case1: empty input": { input: emptyInput, expectedOutput: nil, expectedError: nil, }, "Case2: error input": { input: errorInput, expectedOutput: errorInput, expectedError: nil, }, "Case3: number input": { input: numberInput, expectedOutput: numberInput, expectedError: nil, }, "Case4: legit input": { input: legitInput, expectedOutput: legitOutputJSON, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { actualOutput, actualError := isTimeStruct(test.input) assert.Equal(t, test.expectedOutput, actualOutput) assert.Equal(t, test.expectedError, actualError) }) } } func TestClose(t *testing.T) { ctrl := gomock.NewController(t) mockPinotClient := pnt.NewMockGenericClient(ctrl) mockProducer := &mocks.KafkaProducer{} mgr := NewPinotVisibilityStore(mockPinotClient, &service.Config{ ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), ESIndexMaxResultWindow: dynamicproperties.GetIntPropertyFn(3), PinotOptimizedQueryColumns: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, mockProducer, log.NewNoop()) visibilityStore := mgr.(*pinotVisibilityStore) assert.NotPanics(t, func() { visibilityStore.Close() }) } ================================================ FILE: common/persistence/queue_manager.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "github.com/uber/cadence/common/clock" ) type ( queueManager struct { persistence QueueStore timeSrc clock.TimeSource } ) var _ QueueManager = (*queueManager)(nil) // NewQueueManager returns a new QueueManager func NewQueueManager( persistence QueueStore, ) QueueManager { return &queueManager{ persistence: persistence, timeSrc: clock.NewRealTimeSource(), } } func (q *queueManager) Close() { q.persistence.Close() } func (q *queueManager) EnqueueMessage(ctx context.Context, request *EnqueueMessageRequest) error { currentTimestamp := q.timeSrc.Now() return q.persistence.EnqueueMessage(ctx, &InternalEnqueueMessageRequest{ MessagePayload: request.MessagePayload, CurrentTimeStamp: currentTimestamp, }) } func (q *queueManager) ReadMessages(ctx context.Context, request *ReadMessagesRequest) (*ReadMessagesResponse, error) { resp, err := q.persistence.ReadMessages(ctx, &InternalReadMessagesRequest{ LastMessageID: request.LastMessageID, MaxCount: request.MaxCount, }) if err != nil { return nil, err } output := make(QueueMessageList, 0, len(resp.Messages)) for _, message := range resp.Messages { output = append(output, q.fromInternalQueueMessage(message)) } return &ReadMessagesResponse{Messages: output}, nil } func (q *queueManager) DeleteMessagesBefore(ctx context.Context, request *DeleteMessagesBeforeRequest) error { return q.persistence.DeleteMessagesBefore(ctx, &InternalDeleteMessagesBeforeRequest{ MessageID: request.MessageID, }) } func (q *queueManager) UpdateAckLevel(ctx context.Context, request *UpdateAckLevelRequest) error { currentTimestamp := q.timeSrc.Now() return q.persistence.UpdateAckLevel(ctx, &InternalUpdateAckLevelRequest{ MessageID: request.MessageID, ClusterName: request.ClusterName, CurrentTimeStamp: currentTimestamp, }) } func (q *queueManager) GetAckLevels(ctx context.Context, request *GetAckLevelsRequest) (*GetAckLevelsResponse, error) { resp, err := q.persistence.GetAckLevels(ctx, &InternalGetAckLevelsRequest{}) if err != nil { return nil, err } return &GetAckLevelsResponse{AckLevels: resp.AckLevels}, nil } func (q *queueManager) EnqueueMessageToDLQ(ctx context.Context, request *EnqueueMessageToDLQRequest) error { currentTimestamp := q.timeSrc.Now() return q.persistence.EnqueueMessageToDLQ(ctx, &InternalEnqueueMessageToDLQRequest{ MessagePayload: request.MessagePayload, CurrentTimeStamp: currentTimestamp, }) } func (q *queueManager) ReadMessagesFromDLQ(ctx context.Context, request *ReadMessagesFromDLQRequest) (*ReadMessagesFromDLQResponse, error) { resp, err := q.persistence.ReadMessagesFromDLQ(ctx, &InternalReadMessagesFromDLQRequest{ FirstMessageID: request.FirstMessageID, LastMessageID: request.LastMessageID, PageSize: request.PageSize, PageToken: request.PageToken, }) if resp == nil { return nil, err } output := make([]*QueueMessage, 0, len(resp.Messages)) for _, message := range resp.Messages { output = append(output, q.fromInternalQueueMessage(message)) } return &ReadMessagesFromDLQResponse{ Messages: output, NextPageToken: resp.NextPageToken, }, err } func (q *queueManager) DeleteMessageFromDLQ(ctx context.Context, request *DeleteMessageFromDLQRequest) error { return q.persistence.DeleteMessageFromDLQ(ctx, &InternalDeleteMessageFromDLQRequest{ MessageID: request.MessageID, }) } func (q *queueManager) RangeDeleteMessagesFromDLQ(ctx context.Context, request *RangeDeleteMessagesFromDLQRequest) error { return q.persistence.RangeDeleteMessagesFromDLQ(ctx, &InternalRangeDeleteMessagesFromDLQRequest{ FirstMessageID: request.FirstMessageID, LastMessageID: request.LastMessageID, }) } func (q *queueManager) UpdateDLQAckLevel(ctx context.Context, request *UpdateDLQAckLevelRequest) error { currentTimestamp := q.timeSrc.Now() return q.persistence.UpdateDLQAckLevel(ctx, &InternalUpdateDLQAckLevelRequest{ MessageID: request.MessageID, ClusterName: request.ClusterName, CurrentTimeStamp: currentTimestamp, }) } func (q *queueManager) GetDLQAckLevels(ctx context.Context, request *GetDLQAckLevelsRequest) (*GetDLQAckLevelsResponse, error) { resp, err := q.persistence.GetDLQAckLevels(ctx, &InternalGetDLQAckLevelsRequest{}) if err != nil { return nil, err } return &GetDLQAckLevelsResponse{AckLevels: resp.AckLevels}, nil } func (q *queueManager) GetDLQSize(ctx context.Context, request *GetDLQSizeRequest) (*GetDLQSizeResponse, error) { resp, err := q.persistence.GetDLQSize(ctx, &InternalGetDLQSizeRequest{}) if err != nil { return nil, err } return &GetDLQSizeResponse{Size: resp.Size}, nil } func (q *queueManager) fromInternalQueueMessage(message *InternalQueueMessage) *QueueMessage { return &QueueMessage{ ID: message.ID, QueueType: message.QueueType, Payload: message.Payload, } } ================================================ FILE: common/persistence/retryer.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination retryer_mock.go -package persistence github.com/uber/cadence/common/persistence Retryer package persistence import ( "context" "github.com/uber/cadence/common/backoff" ) // Retryer is used to retry requests to persistence with provided retry policy type Retryer interface { ListConcreteExecutions(context.Context, *ListConcreteExecutionsRequest) (*ListConcreteExecutionsResponse, error) ListCurrentExecutions(context.Context, *ListCurrentExecutionsRequest) (*ListCurrentExecutionsResponse, error) GetWorkflowExecution(context.Context, *GetWorkflowExecutionRequest) (*GetWorkflowExecutionResponse, error) GetCurrentExecution(context.Context, *GetCurrentExecutionRequest) (*GetCurrentExecutionResponse, error) IsWorkflowExecutionExists(context.Context, *IsWorkflowExecutionExistsRequest) (*IsWorkflowExecutionExistsResponse, error) ReadHistoryBranch(context.Context, *ReadHistoryBranchRequest) (*ReadHistoryBranchResponse, error) DeleteWorkflowExecution(context.Context, *DeleteWorkflowExecutionRequest) error DeleteCurrentWorkflowExecution(context.Context, *DeleteCurrentWorkflowExecutionRequest) error GetShardID() int GetHistoryTasks(context.Context, *GetHistoryTasksRequest) (*GetHistoryTasksResponse, error) CompleteHistoryTask(ctx context.Context, request *CompleteHistoryTaskRequest) error } type ( persistenceRetryer struct { execManager ExecutionManager historyManager HistoryManager throttleRetry *backoff.ThrottleRetry } ) // NewPersistenceRetryer constructs a new Retryer func NewPersistenceRetryer( execManager ExecutionManager, historyManager HistoryManager, policy backoff.RetryPolicy, ) Retryer { return &persistenceRetryer{ execManager: execManager, historyManager: historyManager, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(IsTransientError), ), } } // ListConcreteExecutions retries ListConcreteExecutions func (pr *persistenceRetryer) ListConcreteExecutions( ctx context.Context, req *ListConcreteExecutionsRequest, ) (*ListConcreteExecutionsResponse, error) { var resp *ListConcreteExecutionsResponse op := func(ctx context.Context) error { var err error resp, err = pr.execManager.ListConcreteExecutions(ctx, req) return err } err := pr.throttleRetry.Do(ctx, op) if err == nil { return resp, nil } return nil, err } // GetWorkflowExecution retries GetWorkflowExecution func (pr *persistenceRetryer) GetWorkflowExecution( ctx context.Context, req *GetWorkflowExecutionRequest, ) (*GetWorkflowExecutionResponse, error) { var resp *GetWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = pr.execManager.GetWorkflowExecution(ctx, req) return err } err := pr.throttleRetry.Do(ctx, op) if err != nil { return nil, err } return resp, nil } // GetCurrentExecution retries GetCurrentExecution func (pr *persistenceRetryer) GetCurrentExecution( ctx context.Context, req *GetCurrentExecutionRequest, ) (*GetCurrentExecutionResponse, error) { var resp *GetCurrentExecutionResponse op := func(ctx context.Context) error { var err error resp, err = pr.execManager.GetCurrentExecution(ctx, req) return err } err := pr.throttleRetry.Do(ctx, op) if err != nil { return nil, err } return resp, nil } // ListCurrentExecutions retries ListCurrentExecutions func (pr *persistenceRetryer) ListCurrentExecutions( ctx context.Context, req *ListCurrentExecutionsRequest, ) (*ListCurrentExecutionsResponse, error) { var resp *ListCurrentExecutionsResponse op := func(ctx context.Context) error { var err error resp, err = pr.execManager.ListCurrentExecutions(ctx, req) return err } err := pr.throttleRetry.Do(ctx, op) if err == nil { return resp, nil } return nil, err } // IsWorkflowExecutionExists retries IsWorkflowExecutionExists func (pr *persistenceRetryer) IsWorkflowExecutionExists( ctx context.Context, req *IsWorkflowExecutionExistsRequest, ) (*IsWorkflowExecutionExistsResponse, error) { var resp *IsWorkflowExecutionExistsResponse op := func(ctx context.Context) error { var err error resp, err = pr.execManager.IsWorkflowExecutionExists(ctx, req) return err } err := pr.throttleRetry.Do(ctx, op) if err != nil { return nil, err } return resp, nil } // ReadHistoryBranch retries ReadHistoryBranch func (pr *persistenceRetryer) ReadHistoryBranch( ctx context.Context, req *ReadHistoryBranchRequest, ) (*ReadHistoryBranchResponse, error) { var resp *ReadHistoryBranchResponse op := func(ctx context.Context) error { var err error resp, err = pr.historyManager.ReadHistoryBranch(ctx, req) return err } err := pr.throttleRetry.Do(ctx, op) if err != nil { return nil, err } return resp, nil } // DeleteWorkflowExecution retries DeleteWorkflowExecution func (pr *persistenceRetryer) DeleteWorkflowExecution( ctx context.Context, req *DeleteWorkflowExecutionRequest, ) error { op := func(ctx context.Context) error { return pr.execManager.DeleteWorkflowExecution(ctx, req) } return pr.throttleRetry.Do(ctx, op) } // DeleteCurrentWorkflowExecution retries DeleteCurrentWorkflowExecution func (pr *persistenceRetryer) DeleteCurrentWorkflowExecution( ctx context.Context, req *DeleteCurrentWorkflowExecutionRequest, ) error { op := func(ctx context.Context) error { return pr.execManager.DeleteCurrentWorkflowExecution(ctx, req) } return pr.throttleRetry.Do(ctx, op) } // GetShardID return shard id func (pr *persistenceRetryer) GetShardID() int { return pr.execManager.GetShardID() } // GetHistoryTasks retries GetHistoryTasks func (pr *persistenceRetryer) GetHistoryTasks( ctx context.Context, req *GetHistoryTasksRequest, ) (*GetHistoryTasksResponse, error) { var resp *GetHistoryTasksResponse op := func(ctx context.Context) error { var err error resp, err = pr.execManager.GetHistoryTasks(ctx, req) return err } err := pr.throttleRetry.Do(ctx, op) if err != nil { return nil, err } return resp, nil } // CompleteHistoryTask is a retryable version of CompleteHistoryTask method func (pr *persistenceRetryer) CompleteHistoryTask( ctx context.Context, request *CompleteHistoryTaskRequest, ) error { op := func(ctx context.Context) error { return pr.execManager.CompleteHistoryTask(ctx, request) } return pr.throttleRetry.Do(ctx, op) } ================================================ FILE: common/persistence/retryer_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: retryer.go // // Generated by this command: // // mockgen -package persistence -source retryer.go -destination retryer_mock.go -package persistence github.com/uber/cadence/common/persistence Retryer // // Package persistence is a generated GoMock package. package persistence import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockRetryer is a mock of Retryer interface. type MockRetryer struct { ctrl *gomock.Controller recorder *MockRetryerMockRecorder isgomock struct{} } // MockRetryerMockRecorder is the mock recorder for MockRetryer. type MockRetryerMockRecorder struct { mock *MockRetryer } // NewMockRetryer creates a new mock instance. func NewMockRetryer(ctrl *gomock.Controller) *MockRetryer { mock := &MockRetryer{ctrl: ctrl} mock.recorder = &MockRetryerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockRetryer) EXPECT() *MockRetryerMockRecorder { return m.recorder } // CompleteHistoryTask mocks base method. func (m *MockRetryer) CompleteHistoryTask(ctx context.Context, request *CompleteHistoryTaskRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CompleteHistoryTask", ctx, request) ret0, _ := ret[0].(error) return ret0 } // CompleteHistoryTask indicates an expected call of CompleteHistoryTask. func (mr *MockRetryerMockRecorder) CompleteHistoryTask(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompleteHistoryTask", reflect.TypeOf((*MockRetryer)(nil).CompleteHistoryTask), ctx, request) } // DeleteCurrentWorkflowExecution mocks base method. func (m *MockRetryer) DeleteCurrentWorkflowExecution(arg0 context.Context, arg1 *DeleteCurrentWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteCurrentWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // DeleteCurrentWorkflowExecution indicates an expected call of DeleteCurrentWorkflowExecution. func (mr *MockRetryerMockRecorder) DeleteCurrentWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCurrentWorkflowExecution", reflect.TypeOf((*MockRetryer)(nil).DeleteCurrentWorkflowExecution), arg0, arg1) } // DeleteWorkflowExecution mocks base method. func (m *MockRetryer) DeleteWorkflowExecution(arg0 context.Context, arg1 *DeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // DeleteWorkflowExecution indicates an expected call of DeleteWorkflowExecution. func (mr *MockRetryerMockRecorder) DeleteWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWorkflowExecution", reflect.TypeOf((*MockRetryer)(nil).DeleteWorkflowExecution), arg0, arg1) } // GetCurrentExecution mocks base method. func (m *MockRetryer) GetCurrentExecution(arg0 context.Context, arg1 *GetCurrentExecutionRequest) (*GetCurrentExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCurrentExecution", arg0, arg1) ret0, _ := ret[0].(*GetCurrentExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentExecution indicates an expected call of GetCurrentExecution. func (mr *MockRetryerMockRecorder) GetCurrentExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentExecution", reflect.TypeOf((*MockRetryer)(nil).GetCurrentExecution), arg0, arg1) } // GetHistoryTasks mocks base method. func (m *MockRetryer) GetHistoryTasks(arg0 context.Context, arg1 *GetHistoryTasksRequest) (*GetHistoryTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryTasks", arg0, arg1) ret0, _ := ret[0].(*GetHistoryTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHistoryTasks indicates an expected call of GetHistoryTasks. func (mr *MockRetryerMockRecorder) GetHistoryTasks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryTasks", reflect.TypeOf((*MockRetryer)(nil).GetHistoryTasks), arg0, arg1) } // GetShardID mocks base method. func (m *MockRetryer) GetShardID() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardID") ret0, _ := ret[0].(int) return ret0 } // GetShardID indicates an expected call of GetShardID. func (mr *MockRetryerMockRecorder) GetShardID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardID", reflect.TypeOf((*MockRetryer)(nil).GetShardID)) } // GetWorkflowExecution mocks base method. func (m *MockRetryer) GetWorkflowExecution(arg0 context.Context, arg1 *GetWorkflowExecutionRequest) (*GetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*GetWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetWorkflowExecution indicates an expected call of GetWorkflowExecution. func (mr *MockRetryerMockRecorder) GetWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowExecution", reflect.TypeOf((*MockRetryer)(nil).GetWorkflowExecution), arg0, arg1) } // IsWorkflowExecutionExists mocks base method. func (m *MockRetryer) IsWorkflowExecutionExists(arg0 context.Context, arg1 *IsWorkflowExecutionExistsRequest) (*IsWorkflowExecutionExistsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsWorkflowExecutionExists", arg0, arg1) ret0, _ := ret[0].(*IsWorkflowExecutionExistsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // IsWorkflowExecutionExists indicates an expected call of IsWorkflowExecutionExists. func (mr *MockRetryerMockRecorder) IsWorkflowExecutionExists(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWorkflowExecutionExists", reflect.TypeOf((*MockRetryer)(nil).IsWorkflowExecutionExists), arg0, arg1) } // ListConcreteExecutions mocks base method. func (m *MockRetryer) ListConcreteExecutions(arg0 context.Context, arg1 *ListConcreteExecutionsRequest) (*ListConcreteExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListConcreteExecutions", arg0, arg1) ret0, _ := ret[0].(*ListConcreteExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListConcreteExecutions indicates an expected call of ListConcreteExecutions. func (mr *MockRetryerMockRecorder) ListConcreteExecutions(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListConcreteExecutions", reflect.TypeOf((*MockRetryer)(nil).ListConcreteExecutions), arg0, arg1) } // ListCurrentExecutions mocks base method. func (m *MockRetryer) ListCurrentExecutions(arg0 context.Context, arg1 *ListCurrentExecutionsRequest) (*ListCurrentExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListCurrentExecutions", arg0, arg1) ret0, _ := ret[0].(*ListCurrentExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListCurrentExecutions indicates an expected call of ListCurrentExecutions. func (mr *MockRetryerMockRecorder) ListCurrentExecutions(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCurrentExecutions", reflect.TypeOf((*MockRetryer)(nil).ListCurrentExecutions), arg0, arg1) } // ReadHistoryBranch mocks base method. func (m *MockRetryer) ReadHistoryBranch(arg0 context.Context, arg1 *ReadHistoryBranchRequest) (*ReadHistoryBranchResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadHistoryBranch", arg0, arg1) ret0, _ := ret[0].(*ReadHistoryBranchResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadHistoryBranch indicates an expected call of ReadHistoryBranch. func (mr *MockRetryerMockRecorder) ReadHistoryBranch(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadHistoryBranch", reflect.TypeOf((*MockRetryer)(nil).ReadHistoryBranch), arg0, arg1) } ================================================ FILE: common/persistence/retryer_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/types" ) func TestPersistenceRetryerListConcreteExecutions(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *ListConcreteExecutionsRequest mockExecutionManager *MockExecutionManager mockExecutionManagerAccordance func(mockExecutionManager *MockExecutionManager) expectedResponse *ListConcreteExecutionsResponse expectedError error }{ "Success": { request: &ListConcreteExecutionsRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().ListConcreteExecutions(gomock.Any(), gomock.Eq(&ListConcreteExecutionsRequest{})).Return(&ListConcreteExecutionsResponse{}, nil) }, expectedResponse: &ListConcreteExecutionsResponse{}, expectedError: nil, }, "Transient Error": { request: &ListConcreteExecutionsRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { gomock.InOrder( mockExecutionManager.EXPECT().ListConcreteExecutions(gomock.Any(), gomock.Eq(&ListConcreteExecutionsRequest{})).Return(nil, &types.InternalServiceError{}), mockExecutionManager.EXPECT().ListConcreteExecutions(gomock.Any(), gomock.Eq(&ListConcreteExecutionsRequest{})).Return(&ListConcreteExecutionsResponse{}, nil), ) }, expectedResponse: &ListConcreteExecutionsResponse{}, expectedError: nil, }, "Fatal Error": { request: &ListConcreteExecutionsRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().ListConcreteExecutions(gomock.Any(), gomock.Eq(&ListConcreteExecutionsRequest{})).Return(nil, &types.AccessDeniedError{}).Times(1) }, expectedResponse: nil, expectedError: &types.AccessDeniedError{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockExecutionManager != nil { test.mockExecutionManagerAccordance(test.mockExecutionManager) } retryer := NewPersistenceRetryer(test.mockExecutionManager, NewMockHistoryManager(ctrl), backoff.NewExponentialRetryPolicy(time.Nanosecond)) resp, err := retryer.ListConcreteExecutions(context.Background(), test.request) assert.Equal(t, test.expectedResponse, resp) assert.Equal(t, test.expectedError, err) }) } } func TestPersistenceRetryerGetWorkflowExecution(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *GetWorkflowExecutionRequest mockExecutionManager *MockExecutionManager mockExecutionManagerAccordance func(mockExecutionManager *MockExecutionManager) expectedResponse *GetWorkflowExecutionResponse expectedError error }{ "Success": { request: &GetWorkflowExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Eq(&GetWorkflowExecutionRequest{})).Return(&GetWorkflowExecutionResponse{}, nil) }, expectedResponse: &GetWorkflowExecutionResponse{}, expectedError: nil, }, "Transient Error": { request: &GetWorkflowExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { gomock.InOrder( mockExecutionManager.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Eq(&GetWorkflowExecutionRequest{})).Return(nil, &types.InternalServiceError{}), mockExecutionManager.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Eq(&GetWorkflowExecutionRequest{})).Return(&GetWorkflowExecutionResponse{}, nil), ) }, expectedResponse: &GetWorkflowExecutionResponse{}, expectedError: nil, }, "Fatal Error": { request: &GetWorkflowExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Eq(&GetWorkflowExecutionRequest{})).Return(nil, &types.AccessDeniedError{}).Times(1) }, expectedResponse: nil, expectedError: &types.AccessDeniedError{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockExecutionManager != nil { test.mockExecutionManagerAccordance(test.mockExecutionManager) } retryer := NewPersistenceRetryer(test.mockExecutionManager, NewMockHistoryManager(ctrl), backoff.NewExponentialRetryPolicy(time.Nanosecond)) resp, err := retryer.GetWorkflowExecution(context.Background(), test.request) assert.Equal(t, test.expectedResponse, resp) assert.Equal(t, test.expectedError, err) }) } } func TestPersistenceRetryerGetCurrentExecution(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *GetCurrentExecutionRequest mockExecutionManager *MockExecutionManager mockExecutionManagerAccordance func(mockExecutionManager *MockExecutionManager) expectedResponse *GetCurrentExecutionResponse expectedError error }{ "Success": { request: &GetCurrentExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().GetCurrentExecution(gomock.Any(), gomock.Eq(&GetCurrentExecutionRequest{})).Return(&GetCurrentExecutionResponse{}, nil) }, expectedResponse: &GetCurrentExecutionResponse{}, expectedError: nil, }, "Transient Error": { request: &GetCurrentExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { gomock.InOrder( mockExecutionManager.EXPECT().GetCurrentExecution(gomock.Any(), gomock.Eq(&GetCurrentExecutionRequest{})).Return(nil, &types.InternalServiceError{}), mockExecutionManager.EXPECT().GetCurrentExecution(gomock.Any(), gomock.Eq(&GetCurrentExecutionRequest{})).Return(&GetCurrentExecutionResponse{}, nil), ) }, expectedResponse: &GetCurrentExecutionResponse{}, expectedError: nil, }, "Fatal Error": { request: &GetCurrentExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().GetCurrentExecution(gomock.Any(), gomock.Eq(&GetCurrentExecutionRequest{})).Return(nil, &types.AccessDeniedError{}).Times(1) }, expectedResponse: nil, expectedError: &types.AccessDeniedError{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockExecutionManager != nil { test.mockExecutionManagerAccordance(test.mockExecutionManager) } retryer := NewPersistenceRetryer(test.mockExecutionManager, NewMockHistoryManager(ctrl), backoff.NewExponentialRetryPolicy(time.Nanosecond)) resp, err := retryer.GetCurrentExecution(context.Background(), test.request) assert.Equal(t, test.expectedResponse, resp) assert.Equal(t, test.expectedError, err) }) } } func TestPersistenceRetryerListCurrentExecutions(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *ListCurrentExecutionsRequest mockExecutionManager *MockExecutionManager mockExecutionManagerAccordance func(mockExecutionManager *MockExecutionManager) expectedResponse *ListCurrentExecutionsResponse expectedError error }{ "Success": { request: &ListCurrentExecutionsRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().ListCurrentExecutions(gomock.Any(), gomock.Eq(&ListCurrentExecutionsRequest{})).Return(&ListCurrentExecutionsResponse{}, nil) }, expectedResponse: &ListCurrentExecutionsResponse{}, expectedError: nil, }, "Transient Error": { request: &ListCurrentExecutionsRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { gomock.InOrder( mockExecutionManager.EXPECT().ListCurrentExecutions(gomock.Any(), gomock.Eq(&ListCurrentExecutionsRequest{})).Return(nil, &types.InternalServiceError{}), mockExecutionManager.EXPECT().ListCurrentExecutions(gomock.Any(), gomock.Eq(&ListCurrentExecutionsRequest{})).Return(&ListCurrentExecutionsResponse{}, nil), ) }, expectedResponse: &ListCurrentExecutionsResponse{}, expectedError: nil, }, "Fatal Error": { request: &ListCurrentExecutionsRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().ListCurrentExecutions(gomock.Any(), gomock.Eq(&ListCurrentExecutionsRequest{})).Return(nil, &types.AccessDeniedError{}).Times(1) }, expectedResponse: nil, expectedError: &types.AccessDeniedError{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockExecutionManager != nil { test.mockExecutionManagerAccordance(test.mockExecutionManager) } retryer := NewPersistenceRetryer(test.mockExecutionManager, NewMockHistoryManager(ctrl), backoff.NewExponentialRetryPolicy(time.Nanosecond)) resp, err := retryer.ListCurrentExecutions(context.Background(), test.request) assert.Equal(t, test.expectedResponse, resp) assert.Equal(t, test.expectedError, err) }) } } func TestPersistenceRetryerIsWorkflowExecutionExists(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *IsWorkflowExecutionExistsRequest mockExecutionManager *MockExecutionManager mockExecutionManagerAccordance func(mockExecutionManager *MockExecutionManager) expectedResponse *IsWorkflowExecutionExistsResponse expectedError error }{ "Success": { request: &IsWorkflowExecutionExistsRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().IsWorkflowExecutionExists(gomock.Any(), gomock.Eq(&IsWorkflowExecutionExistsRequest{})).Return(&IsWorkflowExecutionExistsResponse{}, nil) }, expectedResponse: &IsWorkflowExecutionExistsResponse{}, expectedError: nil, }, "Transient Error": { request: &IsWorkflowExecutionExistsRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { gomock.InOrder( mockExecutionManager.EXPECT().IsWorkflowExecutionExists(gomock.Any(), gomock.Eq(&IsWorkflowExecutionExistsRequest{})).Return(nil, &types.InternalServiceError{}), mockExecutionManager.EXPECT().IsWorkflowExecutionExists(gomock.Any(), gomock.Eq(&IsWorkflowExecutionExistsRequest{})).Return(&IsWorkflowExecutionExistsResponse{}, nil), ) }, expectedResponse: &IsWorkflowExecutionExistsResponse{}, expectedError: nil, }, "Fatal Error": { request: &IsWorkflowExecutionExistsRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().IsWorkflowExecutionExists(gomock.Any(), gomock.Eq(&IsWorkflowExecutionExistsRequest{})).Return(nil, &types.AccessDeniedError{}).Times(1) }, expectedResponse: nil, expectedError: &types.AccessDeniedError{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockExecutionManager != nil { test.mockExecutionManagerAccordance(test.mockExecutionManager) } retryer := NewPersistenceRetryer(test.mockExecutionManager, NewMockHistoryManager(ctrl), backoff.NewExponentialRetryPolicy(time.Nanosecond)) resp, err := retryer.IsWorkflowExecutionExists(context.Background(), test.request) assert.Equal(t, test.expectedResponse, resp) assert.Equal(t, test.expectedError, err) }) } } func TestPersistenceRetryerReadHistoryBranch(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *ReadHistoryBranchRequest mockHistoryManager *MockHistoryManager mockHistoryManagerAccordance func(mockHistoryManager *MockHistoryManager) expectedResponse *ReadHistoryBranchResponse expectedError error }{ "Success": { request: &ReadHistoryBranchRequest{}, mockHistoryManager: NewMockHistoryManager(ctrl), mockHistoryManagerAccordance: func(mockHistoryManager *MockHistoryManager) { mockHistoryManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Eq(&ReadHistoryBranchRequest{})).Return(&ReadHistoryBranchResponse{}, nil) }, expectedResponse: &ReadHistoryBranchResponse{}, expectedError: nil, }, "Transient Error": { request: &ReadHistoryBranchRequest{}, mockHistoryManager: NewMockHistoryManager(ctrl), mockHistoryManagerAccordance: func(mockHistoryManager *MockHistoryManager) { gomock.InOrder( mockHistoryManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Eq(&ReadHistoryBranchRequest{})).Return(nil, &types.InternalServiceError{}), mockHistoryManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Eq(&ReadHistoryBranchRequest{})).Return(&ReadHistoryBranchResponse{}, nil), ) }, expectedResponse: &ReadHistoryBranchResponse{}, expectedError: nil, }, "Fatal Error": { request: &ReadHistoryBranchRequest{}, mockHistoryManager: NewMockHistoryManager(ctrl), mockHistoryManagerAccordance: func(mockHistoryManager *MockHistoryManager) { mockHistoryManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Eq(&ReadHistoryBranchRequest{})).Return(nil, &types.AccessDeniedError{}).Times(1) }, expectedResponse: nil, expectedError: &types.AccessDeniedError{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockHistoryManager != nil { test.mockHistoryManagerAccordance(test.mockHistoryManager) } retryer := NewPersistenceRetryer(NewMockExecutionManager(ctrl), test.mockHistoryManager, backoff.NewExponentialRetryPolicy(time.Nanosecond)) resp, err := retryer.ReadHistoryBranch(context.Background(), test.request) assert.Equal(t, test.expectedResponse, resp) assert.Equal(t, test.expectedError, err) }) } } func TestPersistenceRetryerDeleteWorkflowExecution(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *DeleteWorkflowExecutionRequest mockExecutionManager *MockExecutionManager mockExecutionManagerAccordance func(mockExecutionManager *MockExecutionManager) expectedError error }{ "Success": { request: &DeleteWorkflowExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Eq(&DeleteWorkflowExecutionRequest{})).Return(nil) }, expectedError: nil, }, "Transient Error": { request: &DeleteWorkflowExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { gomock.InOrder( mockExecutionManager.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Eq(&DeleteWorkflowExecutionRequest{})).Return(&types.InternalServiceError{}), mockExecutionManager.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Eq(&DeleteWorkflowExecutionRequest{})).Return(nil), ) }, expectedError: nil, }, "Fatal Error": { request: &DeleteWorkflowExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Eq(&DeleteWorkflowExecutionRequest{})).Return(&types.AccessDeniedError{}).Times(1) }, expectedError: &types.AccessDeniedError{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockExecutionManager != nil { test.mockExecutionManagerAccordance(test.mockExecutionManager) } retryer := NewPersistenceRetryer(test.mockExecutionManager, NewMockHistoryManager(ctrl), backoff.NewExponentialRetryPolicy(time.Nanosecond)) err := retryer.DeleteWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Equal(t, test.expectedError, err) } else { assert.NoError(t, err) } }) } } func TestPersistenceRetryerDeleteCurrentWorkflowExecution(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *DeleteCurrentWorkflowExecutionRequest mockExecutionManager *MockExecutionManager mockExecutionManagerAccordance func(mockExecutionManager *MockExecutionManager) expectedError error }{ "Success": { request: &DeleteCurrentWorkflowExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().DeleteCurrentWorkflowExecution(gomock.Any(), gomock.Eq(&DeleteCurrentWorkflowExecutionRequest{})).Return(nil) }, expectedError: nil, }, "Transient Error": { request: &DeleteCurrentWorkflowExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { gomock.InOrder( mockExecutionManager.EXPECT().DeleteCurrentWorkflowExecution(gomock.Any(), gomock.Eq(&DeleteCurrentWorkflowExecutionRequest{})).Return(&types.InternalServiceError{}), mockExecutionManager.EXPECT().DeleteCurrentWorkflowExecution(gomock.Any(), gomock.Eq(&DeleteCurrentWorkflowExecutionRequest{})).Return(nil), ) }, expectedError: nil, }, "Fatal Error": { request: &DeleteCurrentWorkflowExecutionRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().DeleteCurrentWorkflowExecution(gomock.Any(), gomock.Eq(&DeleteCurrentWorkflowExecutionRequest{})).Return(&types.AccessDeniedError{}).Times(1) }, expectedError: &types.AccessDeniedError{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockExecutionManager != nil { test.mockExecutionManagerAccordance(test.mockExecutionManager) } retryer := NewPersistenceRetryer(test.mockExecutionManager, NewMockHistoryManager(ctrl), backoff.NewExponentialRetryPolicy(time.Nanosecond)) err := retryer.DeleteCurrentWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Equal(t, test.expectedError, err) } else { assert.NoError(t, err) } }) } } func TestPersistenceRetryerGetShardID(t *testing.T) { ctrl := gomock.NewController(t) mockExecutionManager := NewMockExecutionManager(ctrl) mockExecutionManager.EXPECT().GetShardID().Return(42) retryer := NewPersistenceRetryer(mockExecutionManager, NewMockHistoryManager(ctrl), backoff.NewExponentialRetryPolicy(time.Nanosecond)) assert.Equal(t, 42, retryer.GetShardID()) } func TestPersistenceRetryerGetHistoryTasks(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *GetHistoryTasksRequest mockExecutionManager *MockExecutionManager mockExecutionManagerAccordance func(mockExecutionManager *MockExecutionManager) expectedResponse *GetHistoryTasksResponse expectedError error }{ "Success": { request: &GetHistoryTasksRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Eq(&GetHistoryTasksRequest{})).Return(&GetHistoryTasksResponse{}, nil) }, expectedResponse: &GetHistoryTasksResponse{}, expectedError: nil, }, "Transient Error": { request: &GetHistoryTasksRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { gomock.InOrder( mockExecutionManager.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Eq(&GetHistoryTasksRequest{})).Return(nil, &types.InternalServiceError{}), mockExecutionManager.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Eq(&GetHistoryTasksRequest{})).Return(&GetHistoryTasksResponse{}, nil), ) }, expectedResponse: &GetHistoryTasksResponse{}, expectedError: nil, }, "Fatal Error": { request: &GetHistoryTasksRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Eq(&GetHistoryTasksRequest{})).Return(nil, &types.AccessDeniedError{}).Times(1) }, expectedResponse: nil, expectedError: &types.AccessDeniedError{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockExecutionManager != nil { test.mockExecutionManagerAccordance(test.mockExecutionManager) } retryer := NewPersistenceRetryer(test.mockExecutionManager, NewMockHistoryManager(ctrl), backoff.NewExponentialRetryPolicy(time.Nanosecond)) resp, err := retryer.GetHistoryTasks(context.Background(), test.request) assert.Equal(t, test.expectedResponse, resp) assert.Equal(t, test.expectedError, err) }) } } func TestPersistenceRetryerCompleteHistoryTask(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *CompleteHistoryTaskRequest mockExecutionManager *MockExecutionManager mockExecutionManagerAccordance func(mockExecutionManager *MockExecutionManager) expectedError error }{ "Success": { request: &CompleteHistoryTaskRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().CompleteHistoryTask(gomock.Any(), gomock.Eq(&CompleteHistoryTaskRequest{})).Return(nil) }, expectedError: nil, }, "Transient Error": { request: &CompleteHistoryTaskRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { gomock.InOrder( mockExecutionManager.EXPECT().CompleteHistoryTask(gomock.Any(), gomock.Eq(&CompleteHistoryTaskRequest{})).Return(&types.InternalServiceError{}), mockExecutionManager.EXPECT().CompleteHistoryTask(gomock.Any(), gomock.Eq(&CompleteHistoryTaskRequest{})).Return(nil), ) }, expectedError: nil, }, "Fatal Error": { request: &CompleteHistoryTaskRequest{}, mockExecutionManager: NewMockExecutionManager(ctrl), mockExecutionManagerAccordance: func(mockExecutionManager *MockExecutionManager) { mockExecutionManager.EXPECT().CompleteHistoryTask(gomock.Any(), gomock.Eq(&CompleteHistoryTaskRequest{})).Return(&types.AccessDeniedError{}).Times(1) }, expectedError: &types.AccessDeniedError{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockExecutionManager != nil { test.mockExecutionManagerAccordance(test.mockExecutionManager) } retryer := NewPersistenceRetryer(test.mockExecutionManager, NewMockHistoryManager(ctrl), backoff.NewExponentialRetryPolicy(time.Nanosecond)) err := retryer.CompleteHistoryTask(context.Background(), test.request) if test.expectedError != nil { assert.Equal(t, test.expectedError, err) } else { assert.NoError(t, err) } }) } } ================================================ FILE: common/persistence/serialization/getters.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "time" "github.com/uber/cadence/common/types" ) // GetStolenSinceRenew internal sql blob getter func (s *ShardInfo) GetStolenSinceRenew() (o int32) { if s != nil { return s.StolenSinceRenew } return } // GetUpdatedAt internal sql blob getter func (s *ShardInfo) GetUpdatedAt() time.Time { if s != nil { return s.UpdatedAt } return time.Unix(0, 0) } // GetReplicationAckLevel internal sql blob getter func (s *ShardInfo) GetReplicationAckLevel() (o int64) { if s != nil { return s.ReplicationAckLevel } return } // GetTransferAckLevel internal sql blob getter func (s *ShardInfo) GetTransferAckLevel() (o int64) { if s != nil { return s.TransferAckLevel } return } // GetTimerAckLevel internal sql blob getter func (s *ShardInfo) GetTimerAckLevel() time.Time { if s != nil { return s.TimerAckLevel } return time.Unix(0, 0) } // GetDomainNotificationVersion internal sql blob getter func (s *ShardInfo) GetDomainNotificationVersion() (o int64) { if s != nil { return s.DomainNotificationVersion } return } // GetClusterTransferAckLevel internal sql blob getter func (s *ShardInfo) GetClusterTransferAckLevel() (o map[string]int64) { if s != nil { return s.ClusterTransferAckLevel } return } // GetClusterTimerAckLevel internal sql blob getter func (s *ShardInfo) GetClusterTimerAckLevel() (o map[string]time.Time) { if s != nil { return s.ClusterTimerAckLevel } return } // GetOwner internal sql blob getter func (s *ShardInfo) GetOwner() (o string) { if s != nil { return s.Owner } return } // GetClusterReplicationLevel internal sql blob getter func (s *ShardInfo) GetClusterReplicationLevel() (o map[string]int64) { if s != nil { return s.ClusterReplicationLevel } return } // GetPendingFailoverMarkers internal sql blob getter func (s *ShardInfo) GetPendingFailoverMarkers() (o []byte) { if s != nil { return s.PendingFailoverMarkers } return } // GetPendingFailoverMarkersEncoding internal sql blob getter func (s *ShardInfo) GetPendingFailoverMarkersEncoding() (o string) { if s != nil { return s.PendingFailoverMarkersEncoding } return } // GetReplicationDlqAckLevel internal sql blob getter func (s *ShardInfo) GetReplicationDlqAckLevel() (o map[string]int64) { if s != nil { return s.ReplicationDlqAckLevel } return } // GetTransferProcessingQueueStates internal sql blob getter func (s *ShardInfo) GetTransferProcessingQueueStates() (o []byte) { if s != nil { return s.TransferProcessingQueueStates } return } // GetTransferProcessingQueueStatesEncoding internal sql blob getter func (s *ShardInfo) GetTransferProcessingQueueStatesEncoding() (o string) { if s != nil { return s.TransferProcessingQueueStatesEncoding } return } // GetTimerProcessingQueueStates internal sql blob getter func (s *ShardInfo) GetTimerProcessingQueueStates() (o []byte) { if s != nil { return s.TimerProcessingQueueStates } return } // GetTimerProcessingQueueStatesEncoding internal sql blob getter func (s *ShardInfo) GetTimerProcessingQueueStatesEncoding() (o string) { if s != nil { return s.TimerProcessingQueueStatesEncoding } return } // GetQueueStates internal sql blob getter func (s *ShardInfo) GetQueueStates() (o map[int32]*types.QueueState) { if s != nil { return s.QueueStates } return } // GetName internal sql blob getter func (d *DomainInfo) GetName() (o string) { if d != nil { return d.Name } return } // GetDescription internal sql blob getter func (d *DomainInfo) GetDescription() (o string) { if d != nil { return d.Description } return } // GetOwner internal sql blob getter func (d *DomainInfo) GetOwner() (o string) { if d != nil { return d.Owner } return } // GetStatus internal sql blob getter func (d *DomainInfo) GetStatus() (o int32) { if d != nil { return d.Status } return } // GetRetention internal sql blob getter func (d *DomainInfo) GetRetention() time.Duration { if d != nil { return d.Retention } return time.Duration(0) } // GetEmitMetric internal sql blob getter func (d *DomainInfo) GetEmitMetric() (o bool) { if d != nil { return d.EmitMetric } return } // GetArchivalBucket internal sql blob getter func (d *DomainInfo) GetArchivalBucket() (o string) { if d != nil { return d.ArchivalBucket } return } // GetArchivalStatus internal sql blob getter func (d *DomainInfo) GetArchivalStatus() (o int16) { if d != nil { return d.ArchivalStatus } return } // GetConfigVersion internal sql blob getter func (d *DomainInfo) GetConfigVersion() (o int64) { if d != nil { return d.ConfigVersion } return } // GetNotificationVersion internal sql blob getter func (d *DomainInfo) GetNotificationVersion() (o int64) { if d != nil { return d.NotificationVersion } return } // GetFailoverNotificationVersion internal sql blob getter func (d *DomainInfo) GetFailoverNotificationVersion() (o int64) { if d != nil { return d.FailoverNotificationVersion } return } // GetFailoverVersion internal sql blob getter func (d *DomainInfo) GetFailoverVersion() (o int64) { if d != nil { return d.FailoverVersion } return } // GetActiveClusterName internal sql blob getter func (d *DomainInfo) GetActiveClusterName() (o string) { if d != nil { return d.ActiveClusterName } return } // GetClusters internal sql blob getter func (d *DomainInfo) GetClusters() (o []string) { if d != nil { return d.Clusters } return } // GetData internal sql blob getter func (d *DomainInfo) GetData() (o map[string]string) { if d != nil { return d.Data } return } // GetBadBinaries internal sql blob getter func (d *DomainInfo) GetBadBinaries() (o []byte) { if d != nil { return d.BadBinaries } return } // GetBadBinariesEncoding internal sql blob getter func (d *DomainInfo) GetBadBinariesEncoding() (o string) { if d != nil { return d.BadBinariesEncoding } return } // GetHistoryArchivalStatus internal sql blob getter func (d *DomainInfo) GetHistoryArchivalStatus() (o int16) { if d != nil { return d.HistoryArchivalStatus } return } // GetHistoryArchivalURI internal sql blob getter func (d *DomainInfo) GetHistoryArchivalURI() (o string) { if d != nil { return d.HistoryArchivalURI } return } // GetVisibilityArchivalStatus internal sql blob getter func (d *DomainInfo) GetVisibilityArchivalStatus() (o int16) { if d != nil { return d.VisibilityArchivalStatus } return } // GetVisibilityArchivalURI internal sql blob getter func (d *DomainInfo) GetVisibilityArchivalURI() (o string) { if d != nil { return d.VisibilityArchivalURI } return } // GetFailoverEndTimestamp internal sql blob getter func (d *DomainInfo) GetFailoverEndTimestamp() time.Time { if d != nil && d.FailoverEndTimestamp != nil { return *d.FailoverEndTimestamp } return time.Unix(0, 0) } // GetPreviousFailoverVersion internal sql blob getter func (d *DomainInfo) GetPreviousFailoverVersion() (o int64) { if d != nil { return d.PreviousFailoverVersion } return } // GetLastUpdatedTimestamp internal sql blob getter func (d *DomainInfo) GetLastUpdatedTimestamp() time.Time { if d != nil { return d.LastUpdatedTimestamp } return time.Unix(0, 0) } // GetCreatedTimestamp internal sql blob getter func (h *HistoryTreeInfo) GetCreatedTimestamp() time.Time { if h != nil { return h.CreatedTimestamp } return time.Unix(0, 0) } // GetAncestors internal sql blob getter func (h *HistoryTreeInfo) GetAncestors() (o []*types.HistoryBranchRange) { if h != nil { return h.Ancestors } return } // GetInfo internal sql blob getter func (h *HistoryTreeInfo) GetInfo() (o string) { if h != nil { return h.Info } return } // GetParentDomainID internal sql blob getter func (w *WorkflowExecutionInfo) GetParentDomainID() (o []byte) { if w != nil { return w.ParentDomainID } return } // GetRetryBackoffCoefficient internal sql blob getter func (w *WorkflowExecutionInfo) GetRetryBackoffCoefficient() (o float64) { if w != nil { return w.RetryBackoffCoefficient } return } // GetParentWorkflowID internal sql blob getter func (w *WorkflowExecutionInfo) GetParentWorkflowID() (o string) { if w != nil { return w.ParentWorkflowID } return } // GetParentRunID internal sql blob getter func (w *WorkflowExecutionInfo) GetParentRunID() (o []byte) { if w != nil { return w.ParentRunID } return } // GetCompletionEventEncoding internal sql blob getter func (w *WorkflowExecutionInfo) GetCompletionEventEncoding() (o string) { if w != nil { return w.CompletionEventEncoding } return } // GetTaskList internal sql blob getter func (w *WorkflowExecutionInfo) GetTaskList() (o string) { if w != nil { return w.TaskList } return } func (w *WorkflowExecutionInfo) GetTaskListKind() (o types.TaskListKind) { if w != nil { return w.TaskListKind } return } // GetIsCron internal sql blob getter func (w *WorkflowExecutionInfo) GetIsCron() (o bool) { if w != nil { return w.IsCron } return } // GetWorkflowTypeName internal sql blob getter func (w *WorkflowExecutionInfo) GetWorkflowTypeName() (o string) { if w != nil { return w.WorkflowTypeName } return } // GetCreateRequestID internal sql blob getter func (w *WorkflowExecutionInfo) GetCreateRequestID() (o string) { if w != nil { return w.CreateRequestID } return } // GetDecisionRequestID internal sql blob getter func (w *WorkflowExecutionInfo) GetDecisionRequestID() (o string) { if w != nil { return w.DecisionRequestID } return } // GetCancelRequestID internal sql blob getter func (w *WorkflowExecutionInfo) GetCancelRequestID() (o string) { if w != nil { return w.CancelRequestID } return } // GetStickyTaskList internal sql blob getter func (w *WorkflowExecutionInfo) GetStickyTaskList() (o string) { if w != nil { return w.StickyTaskList } return } // GetCronSchedule internal sql blob getter func (w *WorkflowExecutionInfo) GetCronSchedule() (o string) { if w != nil { return w.CronSchedule } return } // GetCronOverlapPolicy internal sql blob getter func (w *WorkflowExecutionInfo) GetCronOverlapPolicy() (o int32) { if w != nil { return int32(w.CronOverlapPolicy) } return } // GetClientLibraryVersion internal sql blob getter func (w *WorkflowExecutionInfo) GetClientLibraryVersion() (o string) { if w != nil { return w.ClientLibraryVersion } return } // GetClientFeatureVersion internal sql blob getter func (w *WorkflowExecutionInfo) GetClientFeatureVersion() (o string) { if w != nil { return w.ClientFeatureVersion } return } // GetClientImpl internal sql blob getter func (w *WorkflowExecutionInfo) GetClientImpl() (o string) { if w != nil { return w.ClientImpl } return } // GetAutoResetPointsEncoding internal sql blob getter func (w *WorkflowExecutionInfo) GetAutoResetPointsEncoding() (o string) { if w != nil { return w.AutoResetPointsEncoding } return } // GetVersionHistoriesEncoding internal sql blob getter func (w *WorkflowExecutionInfo) GetVersionHistoriesEncoding() (o string) { if w != nil { return w.VersionHistoriesEncoding } return } // GetActiveClusterSelectionPolicyEncoding internal sql blob getter func (w *WorkflowExecutionInfo) GetActiveClusterSelectionPolicyEncoding() (o string) { if w != nil { return w.ActiveClusterSelectionPolicyEncoding } return } // GetInitiatedID internal sql blob getter func (w *WorkflowExecutionInfo) GetInitiatedID() (o int64) { if w != nil { return w.InitiatedID } return } // GetCompletionEventBatchID internal sql blob getter func (w *WorkflowExecutionInfo) GetCompletionEventBatchID() (o int64) { if w != nil && w.CompletionEventBatchID != nil { return *w.CompletionEventBatchID } return } // GetStartVersion internal sql blob getter func (w *WorkflowExecutionInfo) GetStartVersion() (o int64) { if w != nil { return w.StartVersion } return } // GetLastWriteEventID internal sql blob getter func (w *WorkflowExecutionInfo) GetLastWriteEventID() (o int64) { if w != nil && w.LastWriteEventID != nil { return *w.LastWriteEventID } return } // GetLastEventTaskID internal sql blob getter func (w *WorkflowExecutionInfo) GetLastEventTaskID() (o int64) { if w != nil { return w.LastEventTaskID } return } // GetLastFirstEventID internal sql blob getter func (w *WorkflowExecutionInfo) GetLastFirstEventID() (o int64) { if w != nil { return w.LastFirstEventID } return } // GetLastProcessedEvent internal sql blob getter func (w *WorkflowExecutionInfo) GetLastProcessedEvent() (o int64) { if w != nil { return w.LastProcessedEvent } return } // GetDecisionVersion internal sql blob getter func (w *WorkflowExecutionInfo) GetDecisionVersion() (o int64) { if w != nil { return w.DecisionVersion } return } // GetDecisionScheduleID internal sql blob getter func (w *WorkflowExecutionInfo) GetDecisionScheduleID() (o int64) { if w != nil { return w.DecisionScheduleID } return } // GetDecisionStartedID internal sql blob getter func (w *WorkflowExecutionInfo) GetDecisionStartedID() (o int64) { if w != nil { return w.DecisionStartedID } return } // GetDecisionAttempt internal sql blob getter func (w *WorkflowExecutionInfo) GetDecisionAttempt() (o int64) { if w != nil { return w.DecisionAttempt } return } // GetRetryAttempt internal sql blob getter func (w *WorkflowExecutionInfo) GetRetryAttempt() (o int64) { if w != nil { return w.RetryAttempt } return } // GetSignalCount internal sql blob getter func (w *WorkflowExecutionInfo) GetSignalCount() (o int64) { if w != nil { return w.SignalCount } return } // GetHistorySize internal sql blob getter func (w *WorkflowExecutionInfo) GetHistorySize() (o int64) { if w != nil { return w.HistorySize } return } // GetState internal sql blob getter func (w *WorkflowExecutionInfo) GetState() (o int32) { if w != nil { return w.State } return } // GetCloseStatus internal sql blob getter func (w *WorkflowExecutionInfo) GetCloseStatus() (o int32) { if w != nil { return w.CloseStatus } return } // GetRetryMaximumAttempts internal sql blob getter func (w *WorkflowExecutionInfo) GetRetryMaximumAttempts() (o int32) { if w != nil { return w.RetryMaximumAttempts } return } // GetEventStoreVersion internal sql blob getter func (w *WorkflowExecutionInfo) GetEventStoreVersion() (o int32) { if w != nil { return w.EventStoreVersion } return } // GetWorkflowTimeout internal sql blob getter func (w *WorkflowExecutionInfo) GetWorkflowTimeout() time.Duration { if w != nil { return w.WorkflowTimeout } return time.Duration(0) } // GetDecisionTaskTimeout internal sql blob getter func (w *WorkflowExecutionInfo) GetDecisionTaskTimeout() time.Duration { if w != nil { return w.DecisionTaskTimeout } return time.Duration(0) } // GetDecisionTimeout internal sql blob getter func (w *WorkflowExecutionInfo) GetDecisionTimeout() time.Duration { if w != nil { return w.DecisionTimeout } return time.Duration(0) } // GetStickyScheduleToStartTimeout internal sql blob getter func (w *WorkflowExecutionInfo) GetStickyScheduleToStartTimeout() time.Duration { if w != nil { return w.StickyScheduleToStartTimeout } return time.Duration(0) } // GetRetryInitialInterval internal sql blob getter func (w *WorkflowExecutionInfo) GetRetryInitialInterval() time.Duration { if w != nil { return w.RetryInitialInterval } return time.Duration(0) } // GetRetryMaximumInterval internal sql blob getter func (w *WorkflowExecutionInfo) GetRetryMaximumInterval() time.Duration { if w != nil { return w.RetryMaximumInterval } return time.Duration(0) } // GetRetryExpiration internal sql blob getter func (w *WorkflowExecutionInfo) GetRetryExpiration() time.Duration { if w != nil { return w.RetryExpiration } return time.Duration(0) } // GetStartTimestamp internal sql blob getter func (w *WorkflowExecutionInfo) GetStartTimestamp() time.Time { if w != nil { return w.StartTimestamp } return time.Unix(0, 0) } // GetLastUpdatedTimestamp internal sql blob getter func (w *WorkflowExecutionInfo) GetLastUpdatedTimestamp() time.Time { if w != nil { return w.LastUpdatedTimestamp } return time.Unix(0, 0) } // GetDecisionStartedTimestamp internal sql blob getter func (w *WorkflowExecutionInfo) GetDecisionStartedTimestamp() time.Time { if w != nil { return w.DecisionStartedTimestamp } return time.Unix(0, 0) } // GetDecisionScheduledTimestamp internal sql blob getter func (w *WorkflowExecutionInfo) GetDecisionScheduledTimestamp() time.Time { if w != nil { return w.DecisionScheduledTimestamp } return time.Unix(0, 0) } // GetDecisionOriginalScheduledTimestamp internal sql blob getter func (w *WorkflowExecutionInfo) GetDecisionOriginalScheduledTimestamp() time.Time { if w != nil { return w.DecisionOriginalScheduledTimestamp } return time.Unix(0, 0) } // GetRetryExpirationTimestamp internal sql blob getter func (w *WorkflowExecutionInfo) GetRetryExpirationTimestamp() time.Time { if w != nil { return w.RetryExpirationTimestamp } return time.Unix(0, 0) } // GetCompletionEvent internal sql blob getter func (w *WorkflowExecutionInfo) GetCompletionEvent() (o []byte) { if w != nil { return w.CompletionEvent } return } // GetExecutionContext internal sql blob getter func (w *WorkflowExecutionInfo) GetExecutionContext() (o []byte) { if w != nil { return w.ExecutionContext } return } // GetEventBranchToken internal sql blob getter func (w *WorkflowExecutionInfo) GetEventBranchToken() (o []byte) { if w != nil { return w.EventBranchToken } return } // GetAutoResetPoints internal sql blob getter func (w *WorkflowExecutionInfo) GetAutoResetPoints() (o []byte) { if w != nil { return w.AutoResetPoints } return } // GetVersionHistories internal sql blob getter func (w *WorkflowExecutionInfo) GetVersionHistories() (o []byte) { if w != nil { return w.VersionHistories } return } // GetMemo internal sql blob getter func (w *WorkflowExecutionInfo) GetMemo() (o map[string][]byte) { if w != nil { return w.Memo } return } // GetSearchAttributes internal sql blob getter func (w *WorkflowExecutionInfo) GetSearchAttributes() (o map[string][]byte) { if w != nil { return w.SearchAttributes } return } // GetRetryNonRetryableErrors internal sql blob getter func (w *WorkflowExecutionInfo) GetRetryNonRetryableErrors() (o []string) { if w != nil { return w.RetryNonRetryableErrors } return } // GetCancelRequested internal sql blob getter func (w *WorkflowExecutionInfo) GetCancelRequested() (o bool) { if w != nil { return w.CancelRequested } return } // GetHasRetryPolicy internal sql blob getter func (w *WorkflowExecutionInfo) GetHasRetryPolicy() (o bool) { if w != nil { return w.HasRetryPolicy } return } // GetFirstExecutionRunID internal sql blob getter func (w *WorkflowExecutionInfo) GetFirstExecutionRunID() (o []byte) { if w != nil { return w.FirstExecutionRunID } return } // GetPartitionConfig internal sql blob getter func (w *WorkflowExecutionInfo) GetPartitionConfig() (o map[string]string) { if w != nil { return w.PartitionConfig } return } // GetCheckSum internal sql blob getter func (w *WorkflowExecutionInfo) GetChecksum() (o []byte) { if w != nil { return w.Checksum } return } // GetCheckSumEncoding internal sql blob getter func (w *WorkflowExecutionInfo) GetChecksumEncoding() (o string) { if w != nil { return w.ChecksumEncoding } return } // GetVersion internal sql blob getter func (a *ActivityInfo) GetVersion() (o int64) { if a != nil { return a.Version } return } // GetScheduledEventBatchID internal sql blob getter func (a *ActivityInfo) GetScheduledEventBatchID() (o int64) { if a != nil { return a.ScheduledEventBatchID } return } // GetStartedID internal sql blob getter func (a *ActivityInfo) GetStartedID() (o int64) { if a != nil { return a.StartedID } return } // GetCancelRequestID internal sql blob getter func (a *ActivityInfo) GetCancelRequestID() (o int64) { if a != nil { return a.CancelRequestID } return } // GetTimerTaskStatus internal sql blob getter func (a *ActivityInfo) GetTimerTaskStatus() (o int32) { if a != nil { return a.TimerTaskStatus } return } // GetScheduledEventEncoding internal sql blob getter func (a *ActivityInfo) GetScheduledEventEncoding() (o string) { if a != nil { return a.ScheduledEventEncoding } return } // GetStartedIdentity internal sql blob getter func (a *ActivityInfo) GetStartedIdentity() (o string) { if a != nil { return a.StartedIdentity } return } // GetRetryLastFailureReason internal sql blob getter func (a *ActivityInfo) GetRetryLastFailureReason() (o string) { if a != nil { return a.RetryLastFailureReason } return } // GetRetryLastWorkerIdentity internal sql blob getter func (a *ActivityInfo) GetRetryLastWorkerIdentity() (o string) { if a != nil { return a.RetryLastWorkerIdentity } return } // GetTaskList internal sql blob getter func (a *ActivityInfo) GetTaskList() (o string) { if a != nil { return a.TaskList } return } func (a *ActivityInfo) GetTaskListKind() (o types.TaskListKind) { if a != nil { return a.TaskListKind } return } // GetStartedEventEncoding internal sql blob getter func (a *ActivityInfo) GetStartedEventEncoding() (o string) { if a != nil { return a.StartedEventEncoding } return } // GetActivityID internal sql blob getter func (a *ActivityInfo) GetActivityID() (o string) { if a != nil { return a.ActivityID } return } // GetRequestID internal sql blob getter func (a *ActivityInfo) GetRequestID() (o string) { if a != nil { return a.RequestID } return } // GetAttempt internal sql blob getter func (a *ActivityInfo) GetAttempt() (o int32) { if a != nil { return a.Attempt } return } // GetRetryMaximumAttempts internal sql blob getter func (a *ActivityInfo) GetRetryMaximumAttempts() (o int32) { if a != nil { return a.RetryMaximumAttempts } return } // GetScheduledTimestamp internal sql blob getter func (a *ActivityInfo) GetScheduledTimestamp() time.Time { if a != nil { return a.ScheduledTimestamp } return time.Unix(0, 0) } // GetStartedTimestamp internal sql blob getter func (a *ActivityInfo) GetStartedTimestamp() time.Time { if a != nil { return a.StartedTimestamp } return time.Unix(0, 0) } // GetRetryExpirationTimestamp internal sql blob getter func (a *ActivityInfo) GetRetryExpirationTimestamp() time.Time { if a != nil { return a.RetryExpirationTimestamp } return time.Unix(0, 0) } // GetScheduleToStartTimeout internal sql blob getter func (a *ActivityInfo) GetScheduleToStartTimeout() time.Duration { if a != nil { return a.ScheduleToStartTimeout } return time.Duration(0) } // GetScheduleToCloseTimeout internal sql blob getter func (a *ActivityInfo) GetScheduleToCloseTimeout() time.Duration { if a != nil { return a.ScheduleToCloseTimeout } return time.Duration(0) } // GetStartToCloseTimeout internal sql blob getter func (a *ActivityInfo) GetStartToCloseTimeout() time.Duration { if a != nil { return a.StartToCloseTimeout } return time.Duration(0) } // GetHeartbeatTimeout internal sql blob getter func (a *ActivityInfo) GetHeartbeatTimeout() time.Duration { if a != nil { return a.HeartbeatTimeout } return time.Duration(0) } // GetRetryInitialInterval internal sql blob getter func (a *ActivityInfo) GetRetryInitialInterval() time.Duration { if a != nil { return a.RetryInitialInterval } return time.Duration(0) } // GetRetryMaximumInterval internal sql blob getter func (a *ActivityInfo) GetRetryMaximumInterval() time.Duration { if a != nil { return a.RetryMaximumInterval } return time.Duration(0) } // GetScheduledEvent internal sql blob getter func (a *ActivityInfo) GetScheduledEvent() (o []byte) { if a != nil { return a.ScheduledEvent } return } // GetStartedEvent internal sql blob getter func (a *ActivityInfo) GetStartedEvent() (o []byte) { if a != nil { return a.StartedEvent } return } // GetRetryLastFailureDetails internal sql blob getter func (a *ActivityInfo) GetRetryLastFailureDetails() (o []byte) { if a != nil { return a.RetryLastFailureDetails } return } // GetCancelRequested internal sql blob getter func (a *ActivityInfo) GetCancelRequested() (o bool) { if a != nil { return a.CancelRequested } return } // GetHasRetryPolicy internal sql blob getter func (a *ActivityInfo) GetHasRetryPolicy() (o bool) { if a != nil { return a.HasRetryPolicy } return } // GetRetryBackoffCoefficient internal sql blob getter func (a *ActivityInfo) GetRetryBackoffCoefficient() (o float64) { if a != nil { return a.RetryBackoffCoefficient } return } // GetRetryNonRetryableErrors internal sql blob getter func (a *ActivityInfo) GetRetryNonRetryableErrors() (o []string) { if a != nil { return a.RetryNonRetryableErrors } return } // GetVersion internal sql blob getter func (c *ChildExecutionInfo) GetVersion() (o int64) { if c != nil { return c.Version } return } // GetInitiatedEventBatchID internal sql blob getter func (c *ChildExecutionInfo) GetInitiatedEventBatchID() (o int64) { if c != nil { return c.InitiatedEventBatchID } return } // GetStartedID internal sql blob getter func (c *ChildExecutionInfo) GetStartedID() (o int64) { if c != nil { return c.StartedID } return } // GetParentClosePolicy internal sql blob getter func (c *ChildExecutionInfo) GetParentClosePolicy() (o int32) { if c != nil { return c.ParentClosePolicy } return } // GetInitiatedEventEncoding internal sql blob getter func (c *ChildExecutionInfo) GetInitiatedEventEncoding() (o string) { if c != nil { return c.InitiatedEventEncoding } return } // GetStartedWorkflowID internal sql blob getter func (c *ChildExecutionInfo) GetStartedWorkflowID() (o string) { if c != nil { return c.StartedWorkflowID } return } // GetStartedRunID internal sql blob getter func (c *ChildExecutionInfo) GetStartedRunID() (o []byte) { if c != nil { return c.StartedRunID } return } // GetStartedEventEncoding internal sql blob getter func (c *ChildExecutionInfo) GetStartedEventEncoding() (o string) { if c != nil { return c.StartedEventEncoding } return } // GetCreateRequestID internal sql blob getter func (c *ChildExecutionInfo) GetCreateRequestID() (o string) { if c != nil { return c.CreateRequestID } return } // GetDomainID internal sql blob getter func (c *ChildExecutionInfo) GetDomainID() (o string) { if c != nil { return c.DomainID } return } // GetDomainNameDEPRECATED internal sql blob getter func (c *ChildExecutionInfo) GetDomainNameDEPRECATED() (o string) { if c != nil { return c.DomainNameDEPRECATED } return } // GetWorkflowTypeName internal sql blob getter func (c *ChildExecutionInfo) GetWorkflowTypeName() (o string) { if c != nil { return c.WorkflowTypeName } return } // GetInitiatedEvent internal sql blob getter func (c *ChildExecutionInfo) GetInitiatedEvent() (o []byte) { if c != nil { return c.InitiatedEvent } return } // GetStartedEvent internal sql blob getter func (c *ChildExecutionInfo) GetStartedEvent() (o []byte) { if c != nil { return c.StartedEvent } return } // GetVersion internal sql blob getter func (s *SignalInfo) GetVersion() (o int64) { if s != nil { return s.Version } return } // GetInitiatedEventBatchID internal sql blob getter func (s *SignalInfo) GetInitiatedEventBatchID() (o int64) { if s != nil { return s.InitiatedEventBatchID } return } // GetRequestID internal sql blob getter func (s *SignalInfo) GetRequestID() (o string) { if s != nil { return s.RequestID } return } // GetName internal sql blob getter func (s *SignalInfo) GetName() (o string) { if s != nil { return s.Name } return } // GetInput internal sql blob getter func (s *SignalInfo) GetInput() (o []byte) { if s != nil { return s.Input } return } // GetControl internal sql blob getter func (s *SignalInfo) GetControl() (o []byte) { if s != nil { return s.Control } return } // GetVersion internal sql blob getter func (r *RequestCancelInfo) GetVersion() (o int64) { if r != nil { return r.Version } return } // GetInitiatedEventBatchID internal sql blob getter func (r *RequestCancelInfo) GetInitiatedEventBatchID() (o int64) { if r != nil { return r.InitiatedEventBatchID } return } // GetCancelRequestID internal sql blob getter func (r *RequestCancelInfo) GetCancelRequestID() (o string) { if r != nil { return r.CancelRequestID } return } // GetVersion internal sql blob getter func (t *TimerInfo) GetVersion() (o int64) { if t != nil { return t.Version } return } // GetStartedID internal sql blob getter func (t *TimerInfo) GetStartedID() (o int64) { if t != nil { return t.StartedID } return } // GetTaskID internal sql blob getter func (t *TimerInfo) GetTaskID() (o int64) { if t != nil { return t.TaskID } return } // GetExpiryTimestamp internal sql blob getter func (t *TimerInfo) GetExpiryTimestamp() (o time.Time) { if t != nil { return t.ExpiryTimestamp } return time.Unix(0, 0) } // GetWorkflowID internal sql blob getter func (t *TaskInfo) GetWorkflowID() (o string) { if t != nil { return t.WorkflowID } return } // GetRunID internal sql blob getter func (t *TaskInfo) GetRunID() (o []byte) { if t != nil { return t.RunID } return } // GetScheduleID internal sql blob getter func (t *TaskInfo) GetScheduleID() (o int64) { if t != nil { return t.ScheduleID } return } // GetExpiryTimestamp internal sql blob getter func (t *TaskInfo) GetExpiryTimestamp() time.Time { if t != nil { return t.ExpiryTimestamp } return time.Unix(0, 0) } // GetCreatedTimestamp internal sql blob getter func (t *TaskInfo) GetCreatedTimestamp() time.Time { if t != nil { return t.CreatedTimestamp } return time.Unix(0, 0) } // GetPartitionConfig internal sql blob getter func (t *TaskInfo) GetPartitionConfig() (o map[string]string) { if t != nil { return t.PartitionConfig } return } // GetKind internal sql blob getter func (t *TaskListInfo) GetKind() (o int16) { if t != nil { return t.Kind } return } // GetAckLevel internal sql blob getter func (t *TaskListInfo) GetAckLevel() (o int64) { if t != nil { return t.AckLevel } return } // GetExpiryTimestamp internal sql blob getter func (t *TaskListInfo) GetExpiryTimestamp() time.Time { if t != nil { return t.ExpiryTimestamp } return time.Unix(0, 0) } // GetLastUpdated internal sql blob getter func (t *TaskListInfo) GetLastUpdated() time.Time { if t != nil { return t.LastUpdated } return time.Unix(0, 0) } // GetDomainID internal sql blob getter func (t *TransferTaskInfo) GetDomainID() (o []byte) { if t != nil { return t.DomainID } return } // GetWorkflowID internal sql blob getter func (t *TransferTaskInfo) GetWorkflowID() (o string) { if t != nil { return t.WorkflowID } return } // GetRunID internal sql blob getter func (t *TransferTaskInfo) GetRunID() (o []byte) { if t != nil { return t.RunID } return } // GetTaskType internal sql blob getter func (t *TransferTaskInfo) GetTaskType() (o int16) { if t != nil { return t.TaskType } return } // GetTargetDomainID internal sql blob getter func (t *TransferTaskInfo) GetTargetDomainID() (o []byte) { if t != nil { return t.TargetDomainID } return } // GetTargetDomainIDs internal sql blob getter func (t *TransferTaskInfo) GetTargetDomainIDs() (o map[string]struct{}) { if t != nil { targetDomainIDs := make(map[string]struct{}) for _, domainID := range t.TargetDomainIDs { targetDomainIDs[domainID.String()] = struct{}{} } return targetDomainIDs } return } // GetTargetWorkflowID internal sql blob getter func (t *TransferTaskInfo) GetTargetWorkflowID() (o string) { if t != nil { return t.TargetWorkflowID } return } // GetTargetRunID internal sql blob getter func (t *TransferTaskInfo) GetTargetRunID() (o []byte) { if t != nil { return t.TargetRunID } return } // GetTaskList internal sql blob getter func (t *TransferTaskInfo) GetTaskList() (o string) { if t != nil { return t.TaskList } return } // GetTargetChildWorkflowOnly internal sql blob getter func (t *TransferTaskInfo) GetTargetChildWorkflowOnly() (o bool) { if t != nil { return t.TargetChildWorkflowOnly } return } // GetScheduleID internal sql blob getter func (t *TransferTaskInfo) GetScheduleID() (o int64) { if t != nil { return t.ScheduleID } return } // GetVersion internal sql blob getter func (t *TransferTaskInfo) GetVersion() (o int64) { if t != nil { return t.Version } return } // GetVisibilityTimestamp internal sql blob getter func (t *TransferTaskInfo) GetVisibilityTimestamp() time.Time { if t != nil { return t.VisibilityTimestamp } return time.Unix(0, 0) } // GetOriginalTaskList internal sql blob getter func (t *TransferTaskInfo) GetOriginalTaskList() (o string) { if t != nil { return t.OriginalTaskList } return } // GetOriginalTaskListKind internal sql blob getter func (t *TransferTaskInfo) GetOriginalTaskListKind() (o types.TaskListKind) { if t != nil { return t.OriginalTaskListKind } return } // GetDomainID internal sql blob getter func (t *TimerTaskInfo) GetDomainID() (o []byte) { if t != nil && t.DomainID != nil { return t.DomainID } return } // GetWorkflowID internal sql blob getter func (t *TimerTaskInfo) GetWorkflowID() (o string) { if t != nil { return t.WorkflowID } return } // GetRunID internal sql blob getter func (t *TimerTaskInfo) GetRunID() (o []byte) { if t != nil { return t.RunID } return } // GetTaskType internal sql blob getter func (t *TimerTaskInfo) GetTaskType() (o int16) { if t != nil { return t.TaskType } return } // GetTimeoutType internal sql blob getter func (t *TimerTaskInfo) GetTimeoutType() (o int16) { if t != nil && t.TimeoutType != nil { return *t.TimeoutType } return } // GetVersion internal sql blob getter func (t *TimerTaskInfo) GetVersion() (o int64) { if t != nil { return t.Version } return } // GetScheduleAttempt internal sql blob getter func (t *TimerTaskInfo) GetScheduleAttempt() (o int64) { if t != nil { return t.ScheduleAttempt } return } // GetEventID internal sql blob getter func (t *TimerTaskInfo) GetEventID() (o int64) { if t != nil { return t.EventID } return } // GetTaskList internal sql blob getter func (t *TimerTaskInfo) GetTaskList() (o string) { if t != nil { return t.TaskList } return } // GetDomainID internal sql blob getter func (t *ReplicationTaskInfo) GetDomainID() (o []byte) { if t != nil { return t.DomainID } return } // GetWorkflowID internal sql blob getter func (t *ReplicationTaskInfo) GetWorkflowID() (o string) { if t != nil { return t.WorkflowID } return } // GetRunID internal sql blob getter func (t *ReplicationTaskInfo) GetRunID() (o []byte) { if t != nil { return t.RunID } return } // GetTaskType internal sql blob getter func (t *ReplicationTaskInfo) GetTaskType() (o int16) { if t != nil { return t.TaskType } return } // GetVersion internal sql blob getter func (t *ReplicationTaskInfo) GetVersion() (o int64) { if t != nil { return t.Version } return } // GetFirstEventID internal sql blob getter func (t *ReplicationTaskInfo) GetFirstEventID() (o int64) { if t != nil { return t.FirstEventID } return } // GetNextEventID internal sql blob getter func (t *ReplicationTaskInfo) GetNextEventID() (o int64) { if t != nil { return t.NextEventID } return } // GetScheduledID internal sql blob getter func (t *ReplicationTaskInfo) GetScheduledID() (o int64) { if t != nil { return t.ScheduledID } return } // GetEventStoreVersion internal sql blob getter func (t *ReplicationTaskInfo) GetEventStoreVersion() (o int32) { if t != nil { return t.EventStoreVersion } return } // GetNewRunEventStoreVersion internal sql blob getter func (t *ReplicationTaskInfo) GetNewRunEventStoreVersion() (o int32) { if t != nil { return t.NewRunEventStoreVersion } return } // GetBranchToken internal sql blob getter func (t *ReplicationTaskInfo) GetBranchToken() (o []byte) { if t != nil { return t.BranchToken } return } // GetNewRunBranchToken internal sql blob getter func (t *ReplicationTaskInfo) GetNewRunBranchToken() (o []byte) { if t != nil { return t.NewRunBranchToken } return } // GetCreationTimestamp internal sql blob getter func (t *ReplicationTaskInfo) GetCreationTimestamp() time.Time { if t != nil { return t.CreationTimestamp } return time.Unix(0, 0) } ================================================ FILE: common/persistence/serialization/getters_fixtures_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "time" "github.com/uber/cadence/common/types" ) var ( zeroUnix = time.Unix(0, 0) ) var expectedNil = map[string]map[string]any{ "*serialization.WorkflowExecutionInfo": { "GetActiveClusterSelectionPolicyEncoding": string(""), "GetAutoResetPoints": []uint8(nil), "GetAutoResetPointsEncoding": "", "GetCancelRequestID": "", "GetCancelRequested": false, "GetClientFeatureVersion": "", "GetClientImpl": "", "GetClientLibraryVersion": "", "GetCloseStatus": int32(0), "GetCompletionEvent": []uint8(nil), "GetCompletionEventEncoding": "", "GetCreateRequestID": "", "GetCompletionEventBatchID": int64(0), "GetCronSchedule": "", "GetCronOverlapPolicy": int32(0), "GetDecisionAttempt": int64(0), "GetDecisionOriginalScheduledTimestamp": zeroUnix, "GetDecisionRequestID": "", "GetDecisionScheduleID": int64(0), "GetDecisionScheduledTimestamp": zeroUnix, "GetDecisionStartedID": int64(0), "GetDecisionStartedTimestamp": zeroUnix, "GetDecisionTaskTimeout": time.Duration(0), "GetDecisionTimeout": time.Duration(0), "GetLastFirstEventID": int64(0), "GetLastProcessedEvent": int64(0), "GetParentWorkflowID": "", "GetStartVersion": int64(0), "GetTaskList": "", "GetTaskListKind": types.TaskListKindNormal, "GetDecisionVersion": int64(0), "GetEventBranchToken": []uint8(nil), "GetEventStoreVersion": int32(0), "GetExecutionContext": []uint8(nil), "GetFirstExecutionRunID": []uint8(nil), "GetHasRetryPolicy": false, "GetInitiatedID": int64(0), "GetIsCron": false, "GetLastEventTaskID": int64(0), "GetLastUpdatedTimestamp": zeroUnix, "GetLastWriteEventID": int64(0), "GetMemo": map[string][]uint8(nil), "GetParentDomainID": []uint8(nil), "GetParentRunID": []uint8(nil), "GetPartitionConfig": map[string]string(nil), "GetHistorySize": int64(0), "GetRetryAttempt": int64(0), "GetRetryBackoffCoefficient": float64(0), "GetRetryExpiration": time.Duration(0), "GetRetryExpirationTimestamp": zeroUnix, "GetRetryInitialInterval": time.Duration(0), "GetRetryMaximumAttempts": int32(0), "GetRetryMaximumInterval": time.Duration(0), "GetRetryNonRetryableErrors": []string(nil), "GetSearchAttributes": map[string][]uint8(nil), "GetSignalCount": int64(0), "GetStartTimestamp": zeroUnix, "GetState": int32(0), "GetStickyScheduleToStartTimeout": time.Duration(0), "GetStickyTaskList": "", "GetVersionHistories": []uint8(nil), "GetVersionHistoriesEncoding": "", "GetWorkflowTimeout": time.Duration(0), "GetWorkflowTypeName": "", "GetChecksum": []uint8(nil), "GetChecksumEncoding": "", }, "*serialization.TransferTaskInfo": { "GetDomainID": []uint8(nil), "GetWorkflowID": "", "GetRunID": []uint8(nil), "GetTaskType": int16(0), "GetTargetDomainID": []uint8(nil), "GetTargetDomainIDs": map[string]struct{}(nil), "GetTargetWorkflowID": "", "GetTargetRunID": []uint8(nil), "GetTaskList": "", "GetTargetChildWorkflowOnly": false, "GetScheduleID": int64(0), "GetVersion": int64(0), "GetVisibilityTimestamp": zeroUnix, "GetOriginalTaskList": "", "GetOriginalTaskListKind": types.TaskListKindNormal, }, "*serialization.TimerTaskInfo": { "GetDomainID": []uint8(nil), "GetEventID": int64(0), "GetRunID": []uint8(nil), "GetScheduleAttempt": int64(0), "GetTaskType": int16(0), "GetTimeoutType": int16(0), "GetVersion": int64(0), "GetWorkflowID": "", "GetTaskList": "", }, "*serialization.ReplicationTaskInfo": { "GetBranchToken": []uint8(nil), "GetCreationTimestamp": zeroUnix, "GetDomainID": []uint8(nil), "GetEventStoreVersion": int32(0), "GetFirstEventID": int64(0), "GetNewRunBranchToken": []uint8(nil), "GetNewRunEventStoreVersion": int32(0), "GetNextEventID": int64(0), "GetRunID": []uint8(nil), "GetScheduledID": int64(0), "GetTaskType": int16(0), "GetVersion": int64(0), "GetWorkflowID": "", }, "*serialization.TaskListInfo": { "GetAckLevel": int64(0), "GetExpiryTimestamp": zeroUnix, "GetKind": int16(0), "GetLastUpdated": zeroUnix, }, "*serialization.TaskInfo": { "GetCreatedTimestamp": zeroUnix, "GetExpiryTimestamp": zeroUnix, "GetPartitionConfig": map[string]string(nil), "GetRunID": []uint8(nil), "GetScheduleID": int64(0), "GetWorkflowID": "", }, "*serialization.TimerInfo": { "GetExpiryTimestamp": zeroUnix, "GetStartedID": int64(0), "GetTaskID": int64(0), "GetVersion": int64(0), }, "*serialization.RequestCancelInfo": { "GetCancelRequestID": "", "GetInitiatedEventBatchID": int64(0), "GetVersion": int64(0), }, "*serialization.SignalInfo": { "GetControl": []uint8(nil), "GetInitiatedEventBatchID": int64(0), "GetInput": []uint8(nil), "GetName": "", "GetRequestID": "", "GetVersion": int64(0), }, "*serialization.ChildExecutionInfo": { "GetCreateRequestID": "", "GetDomainID": "", "GetDomainNameDEPRECATED": "", "GetInitiatedEvent": []uint8(nil), "GetInitiatedEventBatchID": int64(0), "GetInitiatedEventEncoding": "", "GetParentClosePolicy": int32(0), "GetStartedEvent": []uint8(nil), "GetStartedEventEncoding": "", "GetStartedID": int64(0), "GetStartedRunID": []uint8(nil), "GetStartedWorkflowID": "", "GetVersion": int64(0), "GetWorkflowTypeName": "", }, "*serialization.ActivityInfo": { "GetActivityID": "", "GetAttempt": int32(0), "GetCancelRequestID": int64(0), "GetCancelRequested": false, "GetHasRetryPolicy": false, "GetHeartbeatTimeout": time.Duration(0), "GetRequestID": "", "GetRetryBackoffCoefficient": float64(0), "GetRetryExpirationTimestamp": zeroUnix, "GetRetryInitialInterval": time.Duration(0), "GetRetryLastFailureDetails": []uint8(nil), "GetRetryLastFailureReason": "", "GetRetryLastWorkerIdentity": "", "GetRetryMaximumAttempts": int32(0), "GetRetryMaximumInterval": time.Duration(0), "GetRetryNonRetryableErrors": []string(nil), "GetScheduleToCloseTimeout": time.Duration(0), "GetScheduleToStartTimeout": time.Duration(0), "GetScheduledEvent": []uint8(nil), "GetScheduledEventBatchID": int64(0), "GetScheduledEventEncoding": "", "GetScheduledTimestamp": zeroUnix, "GetStartToCloseTimeout": time.Duration(0), "GetStartedEvent": []uint8(nil), "GetStartedEventEncoding": "", "GetStartedID": int64(0), "GetStartedIdentity": "", "GetStartedTimestamp": zeroUnix, "GetTaskList": "", "GetTaskListKind": types.TaskListKindNormal, "GetTimerTaskStatus": int32(0), "GetVersion": int64(0), }, "*serialization.HistoryTreeInfo": { "GetAncestors": []*types.HistoryBranchRange(nil), "GetCreatedTimestamp": zeroUnix, "GetInfo": "", }, "*serialization.DomainInfo": { "GetActiveClusterName": "", "GetArchivalBucket": "", "GetArchivalStatus": int16(0), "GetBadBinaries": []uint8(nil), "GetBadBinariesEncoding": "", "GetClusters": []string(nil), "GetConfigVersion": int64(0), "GetData": map[string]string(nil), "GetDescription": "", "GetEmitMetric": false, "GetFailoverEndTimestamp": zeroUnix, "GetFailoverNotificationVersion": int64(0), "GetFailoverVersion": int64(0), "GetHistoryArchivalStatus": int16(0), "GetHistoryArchivalURI": "", "GetLastUpdatedTimestamp": zeroUnix, "GetName": "", "GetNotificationVersion": int64(0), "GetOwner": "", "GetPreviousFailoverVersion": int64(0), "GetRetention": time.Duration(0), "GetStatus": int32(0), "GetVisibilityArchivalStatus": int16(0), "GetVisibilityArchivalURI": "", }, "*serialization.ShardInfo": { "GetClusterReplicationLevel": map[string]int64(nil), "GetClusterTimerAckLevel": map[string]time.Time(nil), "GetClusterTransferAckLevel": map[string]int64(nil), "GetDomainNotificationVersion": int64(0), "GetOwner": "", "GetPendingFailoverMarkers": []uint8(nil), "GetPendingFailoverMarkersEncoding": "", "GetReplicationAckLevel": int64(0), "GetReplicationDlqAckLevel": map[string]int64(nil), "GetStolenSinceRenew": int32(0), "GetTimerAckLevel": zeroUnix, "GetTimerProcessingQueueStates": []uint8(nil), "GetTimerProcessingQueueStatesEncoding": "", "GetTransferAckLevel": int64(0), "GetTransferProcessingQueueStates": []uint8(nil), "GetTransferProcessingQueueStatesEncoding": "", "GetUpdatedAt": zeroUnix, "GetQueueStates": map[int32]*types.QueueState(nil), }, } var expectedEmpty = map[string]map[string]any{ "*serialization.WorkflowExecutionInfo": { "GetActiveClusterSelectionPolicyEncoding": string(""), "GetAutoResetPoints": []uint8(nil), "GetAutoResetPointsEncoding": "", "GetCancelRequestID": "", "GetCancelRequested": false, "GetClientFeatureVersion": "", "GetClientImpl": "", "GetClientLibraryVersion": "", "GetCloseStatus": int32(0), "GetCompletionEvent": []uint8(nil), "GetCompletionEventEncoding": "", "GetCreateRequestID": "", "GetCompletionEventBatchID": int64(0), "GetCronSchedule": "", "GetCronOverlapPolicy": int32(0), "GetDecisionAttempt": int64(0), "GetDecisionOriginalScheduledTimestamp": time.Time{}, "GetDecisionRequestID": "", "GetDecisionScheduleID": int64(0), "GetDecisionScheduledTimestamp": time.Time{}, "GetDecisionStartedID": int64(0), "GetDecisionStartedTimestamp": time.Time{}, "GetDecisionTaskTimeout": time.Duration(0), "GetDecisionTimeout": time.Duration(0), "GetLastFirstEventID": int64(0), "GetLastProcessedEvent": int64(0), "GetParentWorkflowID": "", "GetStartVersion": int64(0), "GetTaskList": "", "GetTaskListKind": types.TaskListKindNormal, "GetDecisionVersion": int64(0), "GetEventBranchToken": []uint8(nil), "GetEventStoreVersion": int32(0), "GetExecutionContext": []uint8(nil), "GetFirstExecutionRunID": []uint8(nil), "GetHasRetryPolicy": false, "GetInitiatedID": int64(0), "GetIsCron": false, "GetLastEventTaskID": int64(0), "GetLastUpdatedTimestamp": time.Time{}, "GetLastWriteEventID": int64(0), "GetMemo": map[string][]uint8(nil), "GetParentDomainID": []uint8(nil), "GetParentRunID": []uint8(nil), "GetPartitionConfig": map[string]string(nil), "GetHistorySize": int64(0), "GetRetryAttempt": int64(0), "GetRetryBackoffCoefficient": float64(0), "GetRetryExpiration": time.Duration(0), "GetRetryExpirationTimestamp": time.Time{}, "GetRetryInitialInterval": time.Duration(0), "GetRetryMaximumAttempts": int32(0), "GetRetryMaximumInterval": time.Duration(0), "GetRetryNonRetryableErrors": []string(nil), "GetSearchAttributes": map[string][]uint8(nil), "GetSignalCount": int64(0), "GetStartTimestamp": time.Time{}, "GetState": int32(0), "GetStickyScheduleToStartTimeout": time.Duration(0), "GetStickyTaskList": "", "GetVersionHistories": []uint8(nil), "GetVersionHistoriesEncoding": "", "GetWorkflowTimeout": time.Duration(0), "GetWorkflowTypeName": "", "GetChecksum": []uint8(nil), "GetChecksumEncoding": "", }, "*serialization.TransferTaskInfo": { "GetDomainID": []uint8(nil), "GetWorkflowID": "", "GetRunID": []uint8(nil), "GetTaskType": int16(0), "GetTargetDomainID": []uint8(nil), "GetTargetDomainIDs": map[string]struct{}{}, "GetTargetWorkflowID": "", "GetTargetRunID": []uint8(nil), "GetTaskList": "", "GetTargetChildWorkflowOnly": false, "GetScheduleID": int64(0), "GetVersion": int64(0), "GetVisibilityTimestamp": time.Time{}, "GetOriginalTaskList": "", "GetOriginalTaskListKind": types.TaskListKindNormal, }, "*serialization.TimerTaskInfo": { "GetDomainID": []uint8(nil), "GetEventID": int64(0), "GetRunID": []uint8(nil), "GetScheduleAttempt": int64(0), "GetTaskType": int16(0), "GetTimeoutType": int16(0), "GetVersion": int64(0), "GetWorkflowID": "", "GetTaskList": "", }, "*serialization.ReplicationTaskInfo": { "GetBranchToken": []uint8(nil), "GetCreationTimestamp": time.Time{}, "GetDomainID": []uint8(nil), "GetEventStoreVersion": int32(0), "GetFirstEventID": int64(0), "GetNewRunBranchToken": []uint8(nil), "GetNewRunEventStoreVersion": int32(0), "GetNextEventID": int64(0), "GetRunID": []uint8(nil), "GetScheduledID": int64(0), "GetTaskType": int16(0), "GetVersion": int64(0), "GetWorkflowID": "", }, "*serialization.TaskListInfo": { "GetAckLevel": int64(0), "GetExpiryTimestamp": time.Time{}, "GetKind": int16(0), "GetLastUpdated": time.Time{}, }, "*serialization.TaskInfo": { "GetCreatedTimestamp": time.Time{}, "GetExpiryTimestamp": time.Time{}, "GetPartitionConfig": map[string]string(nil), "GetRunID": []uint8(nil), "GetScheduleID": int64(0), "GetWorkflowID": "", }, "*serialization.TimerInfo": { "GetExpiryTimestamp": time.Time{}, "GetStartedID": int64(0), "GetTaskID": int64(0), "GetVersion": int64(0), }, "*serialization.RequestCancelInfo": { "GetCancelRequestID": "", "GetInitiatedEventBatchID": int64(0), "GetVersion": int64(0), }, "*serialization.SignalInfo": { "GetControl": []uint8(nil), "GetInitiatedEventBatchID": int64(0), "GetInput": []uint8(nil), "GetName": "", "GetRequestID": "", "GetVersion": int64(0), }, "*serialization.ChildExecutionInfo": { "GetCreateRequestID": "", "GetDomainID": "", "GetDomainNameDEPRECATED": "", "GetInitiatedEvent": []uint8(nil), "GetInitiatedEventBatchID": int64(0), "GetInitiatedEventEncoding": "", "GetParentClosePolicy": int32(0), "GetStartedEvent": []uint8(nil), "GetStartedEventEncoding": "", "GetStartedID": int64(0), "GetStartedRunID": []uint8(nil), "GetStartedWorkflowID": "", "GetVersion": int64(0), "GetWorkflowTypeName": "", }, "*serialization.ActivityInfo": { "GetActivityID": "", "GetAttempt": int32(0), "GetCancelRequestID": int64(0), "GetCancelRequested": false, "GetHasRetryPolicy": false, "GetHeartbeatTimeout": time.Duration(0), "GetRequestID": "", "GetRetryBackoffCoefficient": float64(0), "GetRetryExpirationTimestamp": time.Time{}, "GetRetryInitialInterval": time.Duration(0), "GetRetryLastFailureDetails": []uint8(nil), "GetRetryLastFailureReason": "", "GetRetryLastWorkerIdentity": "", "GetRetryMaximumAttempts": int32(0), "GetRetryMaximumInterval": time.Duration(0), "GetRetryNonRetryableErrors": []string(nil), "GetScheduleToCloseTimeout": time.Duration(0), "GetScheduleToStartTimeout": time.Duration(0), "GetScheduledEvent": []uint8(nil), "GetScheduledEventBatchID": int64(0), "GetScheduledEventEncoding": "", "GetScheduledTimestamp": time.Time{}, "GetStartToCloseTimeout": time.Duration(0), "GetStartedEvent": []uint8(nil), "GetStartedEventEncoding": "", "GetStartedID": int64(0), "GetStartedIdentity": "", "GetStartedTimestamp": time.Time{}, "GetTaskList": "", "GetTaskListKind": types.TaskListKindNormal, "GetTimerTaskStatus": int32(0), "GetVersion": int64(0), }, "*serialization.HistoryTreeInfo": { "GetAncestors": []*types.HistoryBranchRange(nil), "GetCreatedTimestamp": time.Time{}, "GetInfo": "", }, "*serialization.DomainInfo": { "GetActiveClusterName": "", "GetArchivalBucket": "", "GetArchivalStatus": int16(0), "GetBadBinaries": []uint8(nil), "GetBadBinariesEncoding": "", "GetClusters": []string(nil), "GetConfigVersion": int64(0), "GetData": map[string]string(nil), "GetDescription": "", "GetEmitMetric": false, "GetFailoverEndTimestamp": zeroUnix, "GetFailoverNotificationVersion": int64(0), "GetFailoverVersion": int64(0), "GetHistoryArchivalStatus": int16(0), "GetHistoryArchivalURI": "", "GetLastUpdatedTimestamp": time.Time{}, "GetName": "", "GetNotificationVersion": int64(0), "GetOwner": "", "GetPreviousFailoverVersion": int64(0), "GetRetention": time.Duration(0), "GetStatus": int32(0), "GetVisibilityArchivalStatus": int16(0), "GetVisibilityArchivalURI": "", }, "*serialization.ShardInfo": { "GetClusterReplicationLevel": map[string]int64(nil), "GetClusterTimerAckLevel": map[string]time.Time(nil), "GetClusterTransferAckLevel": map[string]int64(nil), "GetDomainNotificationVersion": int64(0), "GetOwner": "", "GetPendingFailoverMarkers": []uint8(nil), "GetPendingFailoverMarkersEncoding": "", "GetReplicationAckLevel": int64(0), "GetReplicationDlqAckLevel": map[string]int64(nil), "GetStolenSinceRenew": int32(0), "GetTimerAckLevel": time.Time{}, "GetTimerProcessingQueueStates": []uint8(nil), "GetTimerProcessingQueueStatesEncoding": "", "GetTransferAckLevel": int64(0), "GetTransferProcessingQueueStates": []uint8(nil), "GetTransferProcessingQueueStatesEncoding": "", "GetUpdatedAt": time.Time{}, "GetQueueStates": map[int32]*types.QueueState(nil), }, } var expectedNonEmpty = map[string]map[string]any{ "*serialization.WorkflowExecutionInfo": { "GetAutoResetPoints": []byte("resetpoints"), "GetAutoResetPointsEncoding": "", "GetCancelRequestID": "", "GetCancelRequested": false, "GetClientFeatureVersion": "", "GetClientImpl": "", "GetClientLibraryVersion": "", "GetCloseStatus": int32(6), "GetCompletionEvent": []byte("completionEvent"), "GetCompletionEventEncoding": "completionEventEncoding", "GetCreateRequestID": "", "GetCompletionEventBatchID": int64(2), "GetCronSchedule": "", "GetCronOverlapPolicy": int32(0), "GetDecisionAttempt": int64(0), "GetDecisionOriginalScheduledTimestamp": time.Time{}, "GetDecisionRequestID": "", "GetDecisionScheduleID": int64(0), "GetDecisionScheduledTimestamp": time.Time{}, "GetDecisionStartedID": int64(0), "GetDecisionStartedTimestamp": time.Time{}, "GetDecisionTaskTimeout": time.Duration(0), "GetDecisionTimeout": time.Duration(4), "GetLastFirstEventID": int64(7), "GetLastProcessedEvent": int64(0), "GetParentWorkflowID": "parentWorkflowID", "GetStartVersion": int64(0), "GetTaskList": "taskList", "GetTaskListKind": types.TaskListKindEphemeral, "GetDecisionVersion": int64(0), "GetEventBranchToken": []uint8(nil), "GetEventStoreVersion": int32(0), "GetExecutionContext": []byte("executionContext"), "GetFirstExecutionRunID": []uint8(nil), "GetHasRetryPolicy": false, "GetInitiatedID": int64(1), "GetIsCron": false, "GetLastEventTaskID": int64(0), "GetLastUpdatedTimestamp": time.Time{}, "GetLastWriteEventID": int64(0), "GetMemo": map[string][]uint8(nil), "GetParentDomainID": []byte(parentDomainID), "GetParentRunID": []byte(parentRunID), "GetPartitionConfig": map[string]string(nil), "GetHistorySize": int64(0), "GetRetryAttempt": int64(0), "GetRetryBackoffCoefficient": float64(0), "GetRetryExpiration": time.Duration(0), "GetRetryExpirationTimestamp": time.Time{}, "GetRetryInitialInterval": time.Duration(0), "GetRetryMaximumAttempts": int32(0), "GetRetryMaximumInterval": time.Duration(0), "GetRetryNonRetryableErrors": []string(nil), "GetSearchAttributes": map[string][]uint8{ "key": []byte("value"), }, "GetSignalCount": int64(0), "GetStartTimestamp": time.Time{}, "GetState": int32(5), "GetStickyScheduleToStartTimeout": time.Duration(0), "GetStickyTaskList": "", "GetVersionHistories": []uint8(nil), "GetVersionHistoriesEncoding": "", "GetWorkflowTimeout": time.Duration(3), "GetWorkflowTypeName": "workflowTypeName", "GetChecksum": []uint8(nil), "GetChecksumEncoding": "", "GetActiveClusterSelectionPolicyEncoding": "", }, "*serialization.TransferTaskInfo": { "GetDomainID": []uint8(taskDomainID), "GetWorkflowID": "workflowID", "GetRunID": []uint8(taskRunID), "GetTaskType": int16(1), "GetTargetDomainID": []uint8(parentDomainID), "GetTargetDomainIDs": map[string]struct{}{parentDomainID.String(): struct{}{}, taskDomainID.String(): struct{}{}}, "GetTargetWorkflowID": "targetID", "GetTargetRunID": []uint8(parentRunID), "GetTaskList": "tasklist", "GetTargetChildWorkflowOnly": true, "GetScheduleID": int64(2), "GetVersion": int64(3), "GetVisibilityTimestamp": taskInfoCreateTime, "GetOriginalTaskList": "originalTaskList", "GetOriginalTaskListKind": types.TaskListKindEphemeral, }, "*serialization.TimerTaskInfo": { "GetDomainID": []byte(taskDomainID), "GetEventID": int64(5), "GetRunID": []byte(taskRunID), "GetScheduleAttempt": int64(4), "GetTaskType": int16(1), "GetTimeoutType": int16(2), "GetVersion": int64(3), "GetWorkflowID": "workflowID", "GetTaskList": "taskList", }, "*serialization.ReplicationTaskInfo": { "GetBranchToken": []byte("branchToken"), "GetCreationTimestamp": replicationCreationTimestamp, "GetDomainID": []uint8(replicationTaskDomainID), "GetEventStoreVersion": int32(6), "GetFirstEventID": int64(3), "GetNewRunBranchToken": []byte("newRunBranchToken"), "GetNewRunEventStoreVersion": int32(7), "GetNextEventID": int64(4), "GetRunID": []byte(replicationTaskRunID), "GetScheduledID": int64(5), "GetTaskType": int16(1), "GetVersion": int64(2), "GetWorkflowID": "workflowID", }, "*serialization.TaskListInfo": { "GetAckLevel": int64(2), "GetExpiryTimestamp": taskListInfoExpireTime, "GetKind": int16(1), "GetLastUpdated": taskListInfoLastUpdateTime, }, "*serialization.TaskInfo": { "GetCreatedTimestamp": taskInfoCreateTime, "GetExpiryTimestamp": taskInfoExpiryTime, "GetPartitionConfig": map[string]string{"key": "value"}, "GetRunID": []byte(taskInfoRunID), "GetScheduleID": int64(1), "GetWorkflowID": "workflowID", }, "*serialization.TimerInfo": { "GetExpiryTimestamp": timerInfoExpireTime, "GetStartedID": int64(2), "GetTaskID": int64(3), "GetVersion": int64(1), }, "*serialization.RequestCancelInfo": { "GetCancelRequestID": "cancelRequestID", "GetInitiatedEventBatchID": int64(2), "GetVersion": int64(1), }, "*serialization.SignalInfo": { "GetControl": []byte("signalControl"), "GetInitiatedEventBatchID": int64(2), "GetInput": []byte("signalInput"), "GetName": "signalName", "GetRequestID": "signalRequestID", "GetVersion": int64(1), }, "*serialization.ChildExecutionInfo": { "GetCreateRequestID": "createRequestID", "GetDomainID": "domainID", "GetDomainNameDEPRECATED": "", "GetInitiatedEvent": []byte("initiatedEvent"), "GetInitiatedEventBatchID": int64(2), "GetInitiatedEventEncoding": "initiatedEventEncoding", "GetParentClosePolicy": int32(1), "GetStartedEvent": []byte("startedEvent"), "GetStartedEventEncoding": "startedEventEncoding", "GetStartedID": int64(3), "GetStartedRunID": []byte(childExecutionInfoStartedRunID), "GetStartedWorkflowID": "startedWorkflowID", "GetVersion": int64(1), "GetWorkflowTypeName": "workflowTypeName", }, "*serialization.ActivityInfo": { "GetActivityID": "activityID", "GetAttempt": int32(6), "GetCancelRequestID": int64(4), "GetCancelRequested": true, "GetHasRetryPolicy": true, "GetHeartbeatTimeout": time.Duration(4), "GetRequestID": "requestID", "GetRetryBackoffCoefficient": float64(8), "GetRetryExpirationTimestamp": activeInfoRetryExpirationTime, "GetRetryInitialInterval": time.Duration(5), "GetRetryLastFailureDetails": []byte("retryLastFailureDetails"), "GetRetryLastFailureReason": "retryLastFailureReason", "GetRetryLastWorkerIdentity": "retryLastWorkerIdentity", "GetRetryMaximumAttempts": int32(7), "GetRetryMaximumInterval": time.Duration(6), "GetRetryNonRetryableErrors": []string{"error1", "error2"}, "GetScheduleToCloseTimeout": time.Duration(1), "GetScheduleToStartTimeout": time.Duration(2), "GetScheduledEvent": []byte("scheduledEvent"), "GetScheduledEventBatchID": int64(2), "GetScheduledEventEncoding": "scheduledEventEncoding", "GetScheduledTimestamp": activityInfoScheduledTime, "GetStartToCloseTimeout": time.Duration(3), "GetStartedEvent": []byte("startedEvent"), "GetStartedEventEncoding": "startedEventEncoding", "GetStartedID": int64(3), "GetStartedIdentity": "startedIdentity", "GetStartedTimestamp": activeInfoStartedTime, "GetTaskList": "taskList", "GetTaskListKind": types.TaskListKindSticky, "GetTimerTaskStatus": int32(5), "GetVersion": int64(1), }, "*serialization.HistoryTreeInfo": { "GetAncestors": []*types.HistoryBranchRange{ { BranchID: "branchID1", }, { BranchID: "branchID2", }, }, "GetCreatedTimestamp": historyTreeEventCreatedTime, "GetInfo": "historyTreeInfo", }, "*serialization.DomainInfo": { "GetActiveClusterName": "cluster1", "GetArchivalBucket": "archivalBucket", "GetArchivalStatus": int16(2), "GetBadBinaries": []byte("badBinaries"), "GetBadBinariesEncoding": "badBinariesEncoding", "GetClusters": []string{"cluster1", "cluster2"}, "GetConfigVersion": int64(3), "GetData": map[string]string{"datakey": "datavalue"}, "GetDescription": "description", "GetEmitMetric": true, "GetFailoverEndTimestamp": domainInfoFailoverEndTimestamp, "GetFailoverNotificationVersion": int64(5), "GetFailoverVersion": int64(6), "GetHistoryArchivalStatus": int16(7), "GetHistoryArchivalURI": "historyArchivalURI", "GetLastUpdatedTimestamp": domainInfoLastUpdatedTimestamp, "GetName": "name", "GetNotificationVersion": int64(4), "GetOwner": "owner", "GetPreviousFailoverVersion": int64(9), "GetRetention": time.Duration(1), "GetStatus": int32(1), "GetVisibilityArchivalStatus": int16(8), "GetVisibilityArchivalURI": "visibilityArchivalURI", }, "*serialization.ShardInfo": { "GetClusterReplicationLevel": map[string]int64{ "cluster1": 6, }, "GetClusterTimerAckLevel": map[string]time.Time{ "cluster1": shardInfoTimerAckLevel, }, "GetClusterTransferAckLevel": map[string]int64{ "cluster1": 5, }, "GetDomainNotificationVersion": int64(4), "GetOwner": "owner", "GetPendingFailoverMarkers": []byte("pendingFailoverMarkers"), "GetPendingFailoverMarkersEncoding": "pendingFailoverMarkersEncoding", "GetReplicationAckLevel": int64(2), "GetReplicationDlqAckLevel": map[string]int64{ "cluster1": 7, }, "GetStolenSinceRenew": int32(1), "GetTimerAckLevel": shardInfoTimerAckLevel, "GetTimerProcessingQueueStates": []byte("timerProcessingQueueStates"), "GetTimerProcessingQueueStatesEncoding": "timerProcessingQueueStatesEncoding", "GetTransferAckLevel": int64(3), "GetTransferProcessingQueueStates": []byte("transferProcessingQueueStates"), "GetTransferProcessingQueueStatesEncoding": "transferProcessingQueueStatesEncoding", "GetUpdatedAt": shardInfoUpdatedTime, "GetQueueStates": map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: { VirtualSliceStates: []*types.VirtualSliceState{ { TaskRange: &types.TaskRange{ InclusiveMin: &types.TaskKey{ TaskID: 1, }, ExclusiveMax: &types.TaskKey{ TaskID: 2, }, }, }, }, }, }, }, }, }, } ================================================ FILE: common/persistence/serialization/getters_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "reflect" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) func TestGettersForAllNilInfos(t *testing.T) { for _, info := range []any{ &WorkflowExecutionInfo{}, &TransferTaskInfo{}, &TimerTaskInfo{}, &ReplicationTaskInfo{}, &TaskListInfo{}, &TaskInfo{}, &TimerInfo{}, &RequestCancelInfo{}, &SignalInfo{}, &ChildExecutionInfo{}, &ActivityInfo{}, &HistoryTreeInfo{}, &DomainInfo{}, &ShardInfo{}, } { name := reflect.TypeOf(info).String() t.Run(name, func(t *testing.T) { res := nilView(info) if diff := cmp.Diff(expectedNil[name], res); diff != "" { t.Errorf("nilValue mismatch (-want +got):\n%s", diff) } }) } } func TestGettersForEmptyInfos(t *testing.T) { for _, info := range []any{ &WorkflowExecutionInfo{}, &TransferTaskInfo{}, &TimerTaskInfo{}, &ReplicationTaskInfo{}, &TaskListInfo{}, &TaskInfo{}, &TimerInfo{}, &RequestCancelInfo{}, &SignalInfo{}, &ChildExecutionInfo{}, &ActivityInfo{}, &HistoryTreeInfo{}, &DomainInfo{}, &ShardInfo{}, } { name := reflect.TypeOf(info).String() t.Run(name, func(t *testing.T) { res := emptyView(info) if diff := cmp.Diff(expectedEmpty[name], res); diff != "" { t.Errorf("emptyValue mismatch (-want +got):\n%s", diff) } }) } } var ( parentDomainID = MustParseUUID("00000000-0000-0000-0000-000000000001") parentRunID = MustParseUUID("00000000-0000-0000-0000-000000000002") taskDomainID = MustParseUUID("00000000-0000-0000-0000-000000000003") taskRunID = MustParseUUID("00000000-0000-0000-0000-000000000004") replicationTaskDomainID = MustParseUUID("00000000-0000-0000-0000-000000000005") replicationTaskRunID = MustParseUUID("00000000-0000-0000-0000-000000000006") replicationCreationTimestamp = time.Unix(10, 0) taskListInfoExpireTime = time.Unix(20, 0) taskListInfoLastUpdateTime = time.Unix(30, 0) taskInfoRunID = MustParseUUID("00000000-0000-0000-0000-000000000007") taskInfoExpiryTime = time.Unix(40, 0) taskInfoCreateTime = time.Unix(50, 0) timerInfoExpireTime = time.Unix(60, 0) signalInfoInput = []byte("signalInput") signalInfoControl = []byte("signalControl") childExecutionInfoInitiatedEvent = []byte("initiatedEvent") childExecutionInfoStartedRunID = MustParseUUID("00000000-0000-0000-0000-000000000008") childExecutionInfoStartedEvent = []byte("startedEvent") activityInfoScheduledTime = time.Unix(70, 0) activeInfoStartedTime = time.Unix(80, 0) activeInfoRetryExpirationTime = time.Unix(90, 0) historyTreeEventCreatedTime = time.Unix(100, 0) domainInfoFailoverEndTimestamp = time.Unix(110, 0) domainInfoLastUpdatedTimestamp = time.Unix(120, 0) shardInfoUpdatedTime = time.Unix(130, 0) shardInfoTimerAckLevel = time.Unix(140, 0) ) func TestGettersForInfos(t *testing.T) { for _, info := range []any{ &WorkflowExecutionInfo{ ParentDomainID: parentDomainID, ParentWorkflowID: "parentWorkflowID", ParentRunID: parentRunID, InitiatedID: 1, CompletionEventBatchID: common.Int64Ptr(2), CompletionEvent: []byte("completionEvent"), CompletionEventEncoding: "completionEventEncoding", TaskList: "taskList", TaskListKind: types.TaskListKindEphemeral, WorkflowTypeName: "workflowTypeName", WorkflowTimeout: 3, DecisionTimeout: 4, ExecutionContext: []byte("executionContext"), State: 5, CloseStatus: 6, LastFirstEventID: 7, AutoResetPoints: []byte("resetpoints"), SearchAttributes: map[string][]byte{"key": []byte("value")}, }, &TransferTaskInfo{ DomainID: taskDomainID, WorkflowID: "workflowID", RunID: taskRunID, TaskType: 1, TargetDomainID: parentDomainID, TargetDomainIDs: []UUID{parentDomainID, taskDomainID}, TargetWorkflowID: "targetID", TargetRunID: parentRunID, TaskList: "tasklist", TargetChildWorkflowOnly: true, ScheduleID: 2, Version: 3, VisibilityTimestamp: taskInfoCreateTime, OriginalTaskList: "originalTaskList", OriginalTaskListKind: types.TaskListKindEphemeral, }, &TimerTaskInfo{ DomainID: taskDomainID, WorkflowID: "workflowID", RunID: taskRunID, TaskType: 1, TimeoutType: common.Int16Ptr(2), Version: 3, ScheduleAttempt: 4, EventID: 5, TaskList: "taskList", }, &ReplicationTaskInfo{ DomainID: replicationTaskDomainID, WorkflowID: "workflowID", RunID: replicationTaskRunID, TaskType: 1, Version: 2, FirstEventID: 3, NextEventID: 4, ScheduledID: 5, EventStoreVersion: 6, NewRunEventStoreVersion: 7, BranchToken: []byte("branchToken"), NewRunBranchToken: []byte("newRunBranchToken"), CreationTimestamp: replicationCreationTimestamp, }, &TaskListInfo{ Kind: 1, AckLevel: 2, ExpiryTimestamp: taskListInfoExpireTime, LastUpdated: taskListInfoLastUpdateTime, }, &TaskInfo{ WorkflowID: "workflowID", RunID: taskInfoRunID, ScheduleID: 1, ExpiryTimestamp: taskInfoExpiryTime, CreatedTimestamp: taskInfoCreateTime, PartitionConfig: map[string]string{ "key": "value", }, }, &TimerInfo{ Version: 1, StartedID: 2, ExpiryTimestamp: timerInfoExpireTime, TaskID: 3, }, &RequestCancelInfo{ Version: 1, InitiatedEventBatchID: 2, CancelRequestID: "cancelRequestID", }, &SignalInfo{ Version: 1, InitiatedEventBatchID: 2, RequestID: "signalRequestID", Name: "signalName", Input: signalInfoInput, Control: signalInfoControl, }, &ChildExecutionInfo{ Version: 1, InitiatedEventBatchID: 2, StartedID: 3, InitiatedEvent: childExecutionInfoInitiatedEvent, InitiatedEventEncoding: "initiatedEventEncoding", StartedWorkflowID: "startedWorkflowID", StartedRunID: childExecutionInfoStartedRunID, StartedEvent: childExecutionInfoStartedEvent, StartedEventEncoding: "startedEventEncoding", CreateRequestID: "createRequestID", DomainID: "domainID", WorkflowTypeName: "workflowTypeName", ParentClosePolicy: 1, }, &ActivityInfo{ Version: 1, ScheduledEventBatchID: 2, ScheduledEvent: []byte("scheduledEvent"), ScheduledEventEncoding: "scheduledEventEncoding", ScheduledTimestamp: activityInfoScheduledTime, StartedID: 3, StartedEvent: []byte("startedEvent"), StartedEventEncoding: "startedEventEncoding", StartedTimestamp: activeInfoStartedTime, ActivityID: "activityID", RequestID: "requestID", ScheduleToCloseTimeout: time.Duration(1), ScheduleToStartTimeout: time.Duration(2), StartToCloseTimeout: time.Duration(3), HeartbeatTimeout: time.Duration(4), CancelRequested: true, CancelRequestID: 4, TimerTaskStatus: 5, Attempt: 6, TaskList: "taskList", TaskListKind: types.TaskListKindSticky, StartedIdentity: "startedIdentity", HasRetryPolicy: true, RetryInitialInterval: time.Duration(5), RetryMaximumInterval: time.Duration(6), RetryMaximumAttempts: 7, RetryExpirationTimestamp: activeInfoRetryExpirationTime, RetryBackoffCoefficient: 8, RetryNonRetryableErrors: []string{"error1", "error2"}, RetryLastWorkerIdentity: "retryLastWorkerIdentity", RetryLastFailureReason: "retryLastFailureReason", RetryLastFailureDetails: []byte("retryLastFailureDetails"), }, &HistoryTreeInfo{ CreatedTimestamp: historyTreeEventCreatedTime, Ancestors: []*types.HistoryBranchRange{ { BranchID: "branchID1", }, { BranchID: "branchID2", }, }, Info: "historyTreeInfo", }, &DomainInfo{ Name: "name", Description: "description", Owner: "owner", Status: 1, Retention: time.Duration(1), EmitMetric: true, ArchivalBucket: "archivalBucket", ArchivalStatus: 2, ConfigVersion: 3, NotificationVersion: 4, FailoverNotificationVersion: 5, FailoverVersion: 6, ActiveClusterName: "cluster1", Clusters: []string{"cluster1", "cluster2"}, ActiveClustersConfig: []byte("activeClustersConfig"), ActiveClustersConfigEncoding: "activeClustersConfigEncoding", Data: map[string]string{ "datakey": "datavalue", }, BadBinaries: []byte("badBinaries"), BadBinariesEncoding: "badBinariesEncoding", HistoryArchivalStatus: 7, HistoryArchivalURI: "historyArchivalURI", VisibilityArchivalStatus: 8, VisibilityArchivalURI: "visibilityArchivalURI", FailoverEndTimestamp: &domainInfoFailoverEndTimestamp, PreviousFailoverVersion: 9, LastUpdatedTimestamp: domainInfoLastUpdatedTimestamp, IsolationGroups: []byte("isolationGroups"), IsolationGroupsEncoding: "isolationGroupsEncoding", }, &ShardInfo{ StolenSinceRenew: 1, UpdatedAt: shardInfoUpdatedTime, ReplicationAckLevel: 2, TransferAckLevel: 3, TimerAckLevel: shardInfoTimerAckLevel, DomainNotificationVersion: 4, ClusterTransferAckLevel: map[string]int64{ "cluster1": 5, }, ClusterTimerAckLevel: map[string]time.Time{ "cluster1": shardInfoTimerAckLevel, }, Owner: "owner", ClusterReplicationLevel: map[string]int64{ "cluster1": 6, }, PendingFailoverMarkers: []byte("pendingFailoverMarkers"), PendingFailoverMarkersEncoding: "pendingFailoverMarkersEncoding", ReplicationDlqAckLevel: map[string]int64{ "cluster1": 7, }, TransferProcessingQueueStates: []byte("transferProcessingQueueStates"), TransferProcessingQueueStatesEncoding: "transferProcessingQueueStatesEncoding", TimerProcessingQueueStates: []byte("timerProcessingQueueStates"), TimerProcessingQueueStatesEncoding: "timerProcessingQueueStatesEncoding", CrossClusterProcessingQueueStates: []byte("crossClusterProcessingQueueStates"), CrossClusterProcessingQueueStatesEncoding: "crossClusterProcessingQueueStatesEncoding", QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: { VirtualSliceStates: []*types.VirtualSliceState{ { TaskRange: &types.TaskRange{ InclusiveMin: &types.TaskKey{ TaskID: 1, }, ExclusiveMax: &types.TaskKey{ TaskID: 2, }, }, }, }, }, }, }, }, }, } { name := reflect.TypeOf(info).String() t.Run(name, func(t *testing.T) { val := infoView(info) require.Equal(t, expectedNonEmpty[name], val) }) } } func nilView(info any) map[string]any { infoVal := reflect.ValueOf(info) infoT := reflect.TypeOf(infoVal.Interface()) v := reflect.Zero(infoT) res := make(map[string]any) for i := 0; i < infoT.NumMethod(); i++ { method := infoT.Method(i) if strings.HasPrefix(method.Name, "Get") { res[method.Name] = v.MethodByName(method.Name).Call(nil)[0].Interface() } } return res } func emptyView(info any) map[string]any { infoVal := reflect.ValueOf(info) infoT := reflect.TypeOf(infoVal.Interface()) res := make(map[string]any) for i := 0; i < infoT.NumMethod(); i++ { method := infoT.Method(i) if strings.HasPrefix(method.Name, "Get") { res[method.Name] = infoVal.MethodByName(method.Name).Call(nil)[0].Interface() } } return res } func infoView(info any) map[string]any { v := reflect.ValueOf(info) infoT := reflect.TypeOf(v.Interface()) res := make(map[string]any) for i := 0; i < infoT.NumMethod(); i++ { method := infoT.Method(i) if strings.HasPrefix(method.Name, "Get") { resVal := v.MethodByName(method.Name).Call(nil)[0] res[method.Name] = resVal.Interface() } } return res } ================================================ FILE: common/persistence/serialization/interfaces.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interfaces_mock.go -self_package github.com/uber/cadence/common/persistence/serialization package serialization import ( "time" "go.uber.org/thriftrw/protocol/stream" "go.uber.org/thriftrw/wire" "github.com/uber/cadence/.gen/go/sqlblobs" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // ShardInfo blob in a serialization agnostic format ShardInfo struct { StolenSinceRenew int32 UpdatedAt time.Time ReplicationAckLevel int64 TransferAckLevel int64 TimerAckLevel time.Time DomainNotificationVersion int64 ClusterTransferAckLevel map[string]int64 ClusterTimerAckLevel map[string]time.Time Owner string ClusterReplicationLevel map[string]int64 PendingFailoverMarkers []byte PendingFailoverMarkersEncoding string ReplicationDlqAckLevel map[string]int64 TransferProcessingQueueStates []byte TransferProcessingQueueStatesEncoding string CrossClusterProcessingQueueStates []byte CrossClusterProcessingQueueStatesEncoding string TimerProcessingQueueStates []byte TimerProcessingQueueStatesEncoding string QueueStates map[int32]*types.QueueState } // DomainInfo blob in a serialization agnostic format DomainInfo struct { Name string // TODO: This field seems not to be required. We already store domain name in another column. Description string Owner string Status int32 Retention time.Duration EmitMetric bool ArchivalBucket string ArchivalStatus int16 ConfigVersion int64 NotificationVersion int64 FailoverNotificationVersion int64 FailoverVersion int64 ActiveClusterName string ActiveClustersConfig []byte ActiveClustersConfigEncoding string Clusters []string Data map[string]string BadBinaries []byte BadBinariesEncoding string HistoryArchivalStatus int16 HistoryArchivalURI string VisibilityArchivalStatus int16 VisibilityArchivalURI string FailoverEndTimestamp *time.Time // TODO: There is logic checking if it's nil, should revisit this PreviousFailoverVersion int64 LastUpdatedTimestamp time.Time IsolationGroups []byte IsolationGroupsEncoding string AsyncWorkflowConfig []byte AsyncWorkflowConfigEncoding string } // HistoryBranchRange blob in a serialization agnostic format HistoryBranchRange struct { BranchID string BeginNodeID int64 EndNodeID int64 } // HistoryTreeInfo blob in a serialization agnostic format HistoryTreeInfo struct { CreatedTimestamp time.Time Ancestors []*types.HistoryBranchRange Info string } // WorkflowExecutionInfo blob in a serialization agnostic format WorkflowExecutionInfo struct { ParentDomainID UUID ParentWorkflowID string ParentRunID UUID InitiatedID int64 CompletionEventBatchID *int64 // TODO: This is not updated because of backward compatibility issue. Should revisit it later. CompletionEvent []byte CompletionEventEncoding string TaskList string TaskListKind types.TaskListKind IsCron bool WorkflowTypeName string WorkflowTimeout time.Duration DecisionTaskTimeout time.Duration ExecutionContext []byte State int32 CloseStatus int32 StartVersion int64 LastWriteEventID *int64 // TODO: We have logic checking if LastWriteEventID != nil. The field seems to be deprecated. Should revisit it later. LastEventTaskID int64 LastFirstEventID int64 LastProcessedEvent int64 StartTimestamp time.Time LastUpdatedTimestamp time.Time DecisionVersion int64 DecisionScheduleID int64 DecisionStartedID int64 DecisionTimeout time.Duration DecisionAttempt int64 DecisionStartedTimestamp time.Time DecisionScheduledTimestamp time.Time CancelRequested bool DecisionOriginalScheduledTimestamp time.Time CreateRequestID string DecisionRequestID string CancelRequestID string StickyTaskList string StickyScheduleToStartTimeout time.Duration RetryAttempt int64 RetryInitialInterval time.Duration RetryMaximumInterval time.Duration RetryMaximumAttempts int32 RetryExpiration time.Duration RetryBackoffCoefficient float64 RetryExpirationTimestamp time.Time RetryNonRetryableErrors []string HasRetryPolicy bool CronSchedule string CronOverlapPolicy types.CronOverlapPolicy EventStoreVersion int32 EventBranchToken []byte SignalCount int64 HistorySize int64 ClientLibraryVersion string ClientFeatureVersion string ClientImpl string AutoResetPoints []byte AutoResetPointsEncoding string SearchAttributes map[string][]byte Memo map[string][]byte VersionHistories []byte VersionHistoriesEncoding string FirstExecutionRunID UUID PartitionConfig map[string]string Checksum []byte ChecksumEncoding string ActiveClusterSelectionPolicy []byte ActiveClusterSelectionPolicyEncoding string } // ActivityInfo blob in a serialization agnostic format ActivityInfo struct { Version int64 ScheduledEventBatchID int64 ScheduledEvent []byte ScheduledEventEncoding string ScheduledTimestamp time.Time StartedID int64 StartedEvent []byte StartedEventEncoding string StartedTimestamp time.Time ActivityID string RequestID string ScheduleToStartTimeout time.Duration ScheduleToCloseTimeout time.Duration StartToCloseTimeout time.Duration HeartbeatTimeout time.Duration CancelRequested bool CancelRequestID int64 TimerTaskStatus int32 Attempt int32 TaskList string TaskListKind types.TaskListKind StartedIdentity string HasRetryPolicy bool RetryInitialInterval time.Duration RetryMaximumInterval time.Duration RetryMaximumAttempts int32 RetryExpirationTimestamp time.Time RetryBackoffCoefficient float64 RetryNonRetryableErrors []string RetryLastFailureReason string RetryLastWorkerIdentity string RetryLastFailureDetails []byte } // ChildExecutionInfo blob in a serialization agnostic format ChildExecutionInfo struct { Version int64 InitiatedEventBatchID int64 StartedID int64 InitiatedEvent []byte InitiatedEventEncoding string StartedWorkflowID string StartedRunID UUID StartedEvent []byte StartedEventEncoding string CreateRequestID string DomainID string DomainNameDEPRECATED string WorkflowTypeName string ParentClosePolicy int32 } // SignalInfo blob in a serialization agnostic format SignalInfo struct { Version int64 InitiatedEventBatchID int64 RequestID string Name string Input []byte Control []byte } // RequestCancelInfo blob in a serialization agnostic format RequestCancelInfo struct { Version int64 InitiatedEventBatchID int64 CancelRequestID string } // TimerInfo blob in a serialization agnostic format TimerInfo struct { Version int64 StartedID int64 ExpiryTimestamp time.Time TaskID int64 } // TaskInfo blob in a serialization agnostic format TaskInfo struct { WorkflowID string RunID UUID ScheduleID int64 ExpiryTimestamp time.Time CreatedTimestamp time.Time PartitionConfig map[string]string } TaskListPartition struct { IsolationGroups []string } TaskListPartitionConfig struct { Version int64 NumReadPartitions int32 NumWritePartitions int32 ReadPartitions map[int32]*TaskListPartition WritePartitions map[int32]*TaskListPartition } // TaskListInfo blob in a serialization agnostic format TaskListInfo struct { Kind int16 AckLevel int64 ExpiryTimestamp time.Time LastUpdated time.Time AdaptivePartitionConfig *TaskListPartitionConfig } // TransferTaskInfo blob in a serialization agnostic format TransferTaskInfo struct { DomainID UUID WorkflowID string RunID UUID TaskType int16 TargetDomainID UUID TargetDomainIDs []UUID TargetWorkflowID string TargetRunID UUID TaskList string TargetChildWorkflowOnly bool ScheduleID int64 Version int64 VisibilityTimestamp time.Time OriginalTaskList string OriginalTaskListKind types.TaskListKind } // CrossClusterTaskInfo blob in a serialization agnostic format // Cross cluster tasks are exactly like transfer tasks so // instead of creating another struct and duplicating the same // logic everywhere. We reuse TransferTaskInfo CrossClusterTaskInfo = TransferTaskInfo sqlblobsCrossClusterTaskInfo = sqlblobs.TransferTaskInfo // TimerTaskInfo blob in a serialization agnostic format TimerTaskInfo struct { DomainID UUID WorkflowID string RunID UUID TaskType int16 TimeoutType *int16 // TODO: The default value for TimeoutType doesn't make sense. No equivalent value for nil. Version int64 ScheduleAttempt int64 EventID int64 TaskList string } // ReplicationTaskInfo blob in a serialization agnostic format ReplicationTaskInfo struct { DomainID UUID WorkflowID string RunID UUID TaskType int16 Version int64 FirstEventID int64 NextEventID int64 ScheduledID int64 EventStoreVersion int32 NewRunEventStoreVersion int32 BranchToken []byte NewRunBranchToken []byte CreationTimestamp time.Time } ) type ( // Parser is used to do serialization and deserialization. A parser is backed by a // a single encoder which encodes into one format and a collection of decoders. // Parser selects the appropriate decoder for the provided blob. Parser interface { ShardInfoToBlob(*ShardInfo) (persistence.DataBlob, error) DomainInfoToBlob(*DomainInfo) (persistence.DataBlob, error) HistoryTreeInfoToBlob(*HistoryTreeInfo) (persistence.DataBlob, error) WorkflowExecutionInfoToBlob(*WorkflowExecutionInfo) (persistence.DataBlob, error) ActivityInfoToBlob(*ActivityInfo) (persistence.DataBlob, error) ChildExecutionInfoToBlob(*ChildExecutionInfo) (persistence.DataBlob, error) SignalInfoToBlob(*SignalInfo) (persistence.DataBlob, error) RequestCancelInfoToBlob(*RequestCancelInfo) (persistence.DataBlob, error) TimerInfoToBlob(*TimerInfo) (persistence.DataBlob, error) TaskInfoToBlob(*TaskInfo) (persistence.DataBlob, error) TaskListInfoToBlob(*TaskListInfo) (persistence.DataBlob, error) TransferTaskInfoToBlob(*TransferTaskInfo) (persistence.DataBlob, error) CrossClusterTaskInfoToBlob(*CrossClusterTaskInfo) (persistence.DataBlob, error) TimerTaskInfoToBlob(*TimerTaskInfo) (persistence.DataBlob, error) ReplicationTaskInfoToBlob(*ReplicationTaskInfo) (persistence.DataBlob, error) ShardInfoFromBlob([]byte, string) (*ShardInfo, error) DomainInfoFromBlob([]byte, string) (*DomainInfo, error) HistoryTreeInfoFromBlob([]byte, string) (*HistoryTreeInfo, error) WorkflowExecutionInfoFromBlob([]byte, string) (*WorkflowExecutionInfo, error) ActivityInfoFromBlob([]byte, string) (*ActivityInfo, error) ChildExecutionInfoFromBlob([]byte, string) (*ChildExecutionInfo, error) SignalInfoFromBlob([]byte, string) (*SignalInfo, error) RequestCancelInfoFromBlob([]byte, string) (*RequestCancelInfo, error) TimerInfoFromBlob([]byte, string) (*TimerInfo, error) TaskInfoFromBlob([]byte, string) (*TaskInfo, error) TaskListInfoFromBlob([]byte, string) (*TaskListInfo, error) TransferTaskInfoFromBlob([]byte, string) (*TransferTaskInfo, error) CrossClusterTaskInfoFromBlob([]byte, string) (*CrossClusterTaskInfo, error) TimerTaskInfoFromBlob([]byte, string) (*TimerTaskInfo, error) ReplicationTaskInfoFromBlob([]byte, string) (*ReplicationTaskInfo, error) } // encoder is used to serialize structs. Each encoder implementation uses one serialization format. encoder interface { shardInfoToBlob(*ShardInfo) ([]byte, error) domainInfoToBlob(*DomainInfo) ([]byte, error) historyTreeInfoToBlob(*HistoryTreeInfo) ([]byte, error) workflowExecutionInfoToBlob(*WorkflowExecutionInfo) ([]byte, error) activityInfoToBlob(*ActivityInfo) ([]byte, error) childExecutionInfoToBlob(*ChildExecutionInfo) ([]byte, error) signalInfoToBlob(*SignalInfo) ([]byte, error) requestCancelInfoToBlob(*RequestCancelInfo) ([]byte, error) timerInfoToBlob(*TimerInfo) ([]byte, error) taskInfoToBlob(*TaskInfo) ([]byte, error) taskListInfoToBlob(*TaskListInfo) ([]byte, error) transferTaskInfoToBlob(*TransferTaskInfo) ([]byte, error) crossClusterTaskInfoToBlob(*CrossClusterTaskInfo) ([]byte, error) timerTaskInfoToBlob(*TimerTaskInfo) ([]byte, error) replicationTaskInfoToBlob(*ReplicationTaskInfo) ([]byte, error) encodingType() constants.EncodingType } // decoder is used to deserialize structs. Each decoder implementation uses one serialization format. decoder interface { shardInfoFromBlob([]byte) (*ShardInfo, error) domainInfoFromBlob([]byte) (*DomainInfo, error) historyTreeInfoFromBlob([]byte) (*HistoryTreeInfo, error) workflowExecutionInfoFromBlob([]byte) (*WorkflowExecutionInfo, error) activityInfoFromBlob([]byte) (*ActivityInfo, error) childExecutionInfoFromBlob([]byte) (*ChildExecutionInfo, error) signalInfoFromBlob([]byte) (*SignalInfo, error) requestCancelInfoFromBlob([]byte) (*RequestCancelInfo, error) timerInfoFromBlob([]byte) (*TimerInfo, error) taskInfoFromBlob([]byte) (*TaskInfo, error) taskListInfoFromBlob([]byte) (*TaskListInfo, error) transferTaskInfoFromBlob([]byte) (*TransferTaskInfo, error) crossClusterTaskInfoFromBlob([]byte) (*CrossClusterTaskInfo, error) timerTaskInfoFromBlob([]byte) (*TimerTaskInfo, error) replicationTaskInfoFromBlob([]byte) (*ReplicationTaskInfo, error) } thriftRWType interface { ToWire() (wire.Value, error) FromWire(w wire.Value) error Encode(stream.Writer) error Decode(stream.Reader) error } ) ================================================ FILE: common/persistence/serialization/interfaces_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go // // Generated by this command: // // mockgen -package serialization -source interfaces.go -destination interfaces_mock.go -self_package github.com/uber/cadence/common/persistence/serialization // // Package serialization is a generated GoMock package. package serialization import ( reflect "reflect" gomock "go.uber.org/mock/gomock" stream "go.uber.org/thriftrw/protocol/stream" wire "go.uber.org/thriftrw/wire" constants "github.com/uber/cadence/common/constants" persistence "github.com/uber/cadence/common/persistence" ) // MockParser is a mock of Parser interface. type MockParser struct { ctrl *gomock.Controller recorder *MockParserMockRecorder isgomock struct{} } // MockParserMockRecorder is the mock recorder for MockParser. type MockParserMockRecorder struct { mock *MockParser } // NewMockParser creates a new mock instance. func NewMockParser(ctrl *gomock.Controller) *MockParser { mock := &MockParser{ctrl: ctrl} mock.recorder = &MockParserMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockParser) EXPECT() *MockParserMockRecorder { return m.recorder } // ActivityInfoFromBlob mocks base method. func (m *MockParser) ActivityInfoFromBlob(arg0 []byte, arg1 string) (*ActivityInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ActivityInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*ActivityInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ActivityInfoFromBlob indicates an expected call of ActivityInfoFromBlob. func (mr *MockParserMockRecorder) ActivityInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActivityInfoFromBlob", reflect.TypeOf((*MockParser)(nil).ActivityInfoFromBlob), arg0, arg1) } // ActivityInfoToBlob mocks base method. func (m *MockParser) ActivityInfoToBlob(arg0 *ActivityInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ActivityInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // ActivityInfoToBlob indicates an expected call of ActivityInfoToBlob. func (mr *MockParserMockRecorder) ActivityInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActivityInfoToBlob", reflect.TypeOf((*MockParser)(nil).ActivityInfoToBlob), arg0) } // ChildExecutionInfoFromBlob mocks base method. func (m *MockParser) ChildExecutionInfoFromBlob(arg0 []byte, arg1 string) (*ChildExecutionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ChildExecutionInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*ChildExecutionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ChildExecutionInfoFromBlob indicates an expected call of ChildExecutionInfoFromBlob. func (mr *MockParserMockRecorder) ChildExecutionInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChildExecutionInfoFromBlob", reflect.TypeOf((*MockParser)(nil).ChildExecutionInfoFromBlob), arg0, arg1) } // ChildExecutionInfoToBlob mocks base method. func (m *MockParser) ChildExecutionInfoToBlob(arg0 *ChildExecutionInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ChildExecutionInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // ChildExecutionInfoToBlob indicates an expected call of ChildExecutionInfoToBlob. func (mr *MockParserMockRecorder) ChildExecutionInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChildExecutionInfoToBlob", reflect.TypeOf((*MockParser)(nil).ChildExecutionInfoToBlob), arg0) } // CrossClusterTaskInfoFromBlob mocks base method. func (m *MockParser) CrossClusterTaskInfoFromBlob(arg0 []byte, arg1 string) (*CrossClusterTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CrossClusterTaskInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*CrossClusterTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // CrossClusterTaskInfoFromBlob indicates an expected call of CrossClusterTaskInfoFromBlob. func (mr *MockParserMockRecorder) CrossClusterTaskInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CrossClusterTaskInfoFromBlob", reflect.TypeOf((*MockParser)(nil).CrossClusterTaskInfoFromBlob), arg0, arg1) } // CrossClusterTaskInfoToBlob mocks base method. func (m *MockParser) CrossClusterTaskInfoToBlob(arg0 *CrossClusterTaskInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CrossClusterTaskInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // CrossClusterTaskInfoToBlob indicates an expected call of CrossClusterTaskInfoToBlob. func (mr *MockParserMockRecorder) CrossClusterTaskInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CrossClusterTaskInfoToBlob", reflect.TypeOf((*MockParser)(nil).CrossClusterTaskInfoToBlob), arg0) } // DomainInfoFromBlob mocks base method. func (m *MockParser) DomainInfoFromBlob(arg0 []byte, arg1 string) (*DomainInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DomainInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*DomainInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // DomainInfoFromBlob indicates an expected call of DomainInfoFromBlob. func (mr *MockParserMockRecorder) DomainInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DomainInfoFromBlob", reflect.TypeOf((*MockParser)(nil).DomainInfoFromBlob), arg0, arg1) } // DomainInfoToBlob mocks base method. func (m *MockParser) DomainInfoToBlob(arg0 *DomainInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DomainInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // DomainInfoToBlob indicates an expected call of DomainInfoToBlob. func (mr *MockParserMockRecorder) DomainInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DomainInfoToBlob", reflect.TypeOf((*MockParser)(nil).DomainInfoToBlob), arg0) } // HistoryTreeInfoFromBlob mocks base method. func (m *MockParser) HistoryTreeInfoFromBlob(arg0 []byte, arg1 string) (*HistoryTreeInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HistoryTreeInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*HistoryTreeInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // HistoryTreeInfoFromBlob indicates an expected call of HistoryTreeInfoFromBlob. func (mr *MockParserMockRecorder) HistoryTreeInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HistoryTreeInfoFromBlob", reflect.TypeOf((*MockParser)(nil).HistoryTreeInfoFromBlob), arg0, arg1) } // HistoryTreeInfoToBlob mocks base method. func (m *MockParser) HistoryTreeInfoToBlob(arg0 *HistoryTreeInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HistoryTreeInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // HistoryTreeInfoToBlob indicates an expected call of HistoryTreeInfoToBlob. func (mr *MockParserMockRecorder) HistoryTreeInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HistoryTreeInfoToBlob", reflect.TypeOf((*MockParser)(nil).HistoryTreeInfoToBlob), arg0) } // ReplicationTaskInfoFromBlob mocks base method. func (m *MockParser) ReplicationTaskInfoFromBlob(arg0 []byte, arg1 string) (*ReplicationTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicationTaskInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*ReplicationTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicationTaskInfoFromBlob indicates an expected call of ReplicationTaskInfoFromBlob. func (mr *MockParserMockRecorder) ReplicationTaskInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicationTaskInfoFromBlob", reflect.TypeOf((*MockParser)(nil).ReplicationTaskInfoFromBlob), arg0, arg1) } // ReplicationTaskInfoToBlob mocks base method. func (m *MockParser) ReplicationTaskInfoToBlob(arg0 *ReplicationTaskInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicationTaskInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicationTaskInfoToBlob indicates an expected call of ReplicationTaskInfoToBlob. func (mr *MockParserMockRecorder) ReplicationTaskInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicationTaskInfoToBlob", reflect.TypeOf((*MockParser)(nil).ReplicationTaskInfoToBlob), arg0) } // RequestCancelInfoFromBlob mocks base method. func (m *MockParser) RequestCancelInfoFromBlob(arg0 []byte, arg1 string) (*RequestCancelInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RequestCancelInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*RequestCancelInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // RequestCancelInfoFromBlob indicates an expected call of RequestCancelInfoFromBlob. func (mr *MockParserMockRecorder) RequestCancelInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestCancelInfoFromBlob", reflect.TypeOf((*MockParser)(nil).RequestCancelInfoFromBlob), arg0, arg1) } // RequestCancelInfoToBlob mocks base method. func (m *MockParser) RequestCancelInfoToBlob(arg0 *RequestCancelInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RequestCancelInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // RequestCancelInfoToBlob indicates an expected call of RequestCancelInfoToBlob. func (mr *MockParserMockRecorder) RequestCancelInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestCancelInfoToBlob", reflect.TypeOf((*MockParser)(nil).RequestCancelInfoToBlob), arg0) } // ShardInfoFromBlob mocks base method. func (m *MockParser) ShardInfoFromBlob(arg0 []byte, arg1 string) (*ShardInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ShardInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*ShardInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ShardInfoFromBlob indicates an expected call of ShardInfoFromBlob. func (mr *MockParserMockRecorder) ShardInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShardInfoFromBlob", reflect.TypeOf((*MockParser)(nil).ShardInfoFromBlob), arg0, arg1) } // ShardInfoToBlob mocks base method. func (m *MockParser) ShardInfoToBlob(arg0 *ShardInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ShardInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // ShardInfoToBlob indicates an expected call of ShardInfoToBlob. func (mr *MockParserMockRecorder) ShardInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShardInfoToBlob", reflect.TypeOf((*MockParser)(nil).ShardInfoToBlob), arg0) } // SignalInfoFromBlob mocks base method. func (m *MockParser) SignalInfoFromBlob(arg0 []byte, arg1 string) (*SignalInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignalInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*SignalInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // SignalInfoFromBlob indicates an expected call of SignalInfoFromBlob. func (mr *MockParserMockRecorder) SignalInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalInfoFromBlob", reflect.TypeOf((*MockParser)(nil).SignalInfoFromBlob), arg0, arg1) } // SignalInfoToBlob mocks base method. func (m *MockParser) SignalInfoToBlob(arg0 *SignalInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignalInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SignalInfoToBlob indicates an expected call of SignalInfoToBlob. func (mr *MockParserMockRecorder) SignalInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalInfoToBlob", reflect.TypeOf((*MockParser)(nil).SignalInfoToBlob), arg0) } // TaskInfoFromBlob mocks base method. func (m *MockParser) TaskInfoFromBlob(arg0 []byte, arg1 string) (*TaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TaskInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*TaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // TaskInfoFromBlob indicates an expected call of TaskInfoFromBlob. func (mr *MockParserMockRecorder) TaskInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskInfoFromBlob", reflect.TypeOf((*MockParser)(nil).TaskInfoFromBlob), arg0, arg1) } // TaskInfoToBlob mocks base method. func (m *MockParser) TaskInfoToBlob(arg0 *TaskInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TaskInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // TaskInfoToBlob indicates an expected call of TaskInfoToBlob. func (mr *MockParserMockRecorder) TaskInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskInfoToBlob", reflect.TypeOf((*MockParser)(nil).TaskInfoToBlob), arg0) } // TaskListInfoFromBlob mocks base method. func (m *MockParser) TaskListInfoFromBlob(arg0 []byte, arg1 string) (*TaskListInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TaskListInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*TaskListInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // TaskListInfoFromBlob indicates an expected call of TaskListInfoFromBlob. func (mr *MockParserMockRecorder) TaskListInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskListInfoFromBlob", reflect.TypeOf((*MockParser)(nil).TaskListInfoFromBlob), arg0, arg1) } // TaskListInfoToBlob mocks base method. func (m *MockParser) TaskListInfoToBlob(arg0 *TaskListInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TaskListInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // TaskListInfoToBlob indicates an expected call of TaskListInfoToBlob. func (mr *MockParserMockRecorder) TaskListInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskListInfoToBlob", reflect.TypeOf((*MockParser)(nil).TaskListInfoToBlob), arg0) } // TimerInfoFromBlob mocks base method. func (m *MockParser) TimerInfoFromBlob(arg0 []byte, arg1 string) (*TimerInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TimerInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*TimerInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // TimerInfoFromBlob indicates an expected call of TimerInfoFromBlob. func (mr *MockParserMockRecorder) TimerInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimerInfoFromBlob", reflect.TypeOf((*MockParser)(nil).TimerInfoFromBlob), arg0, arg1) } // TimerInfoToBlob mocks base method. func (m *MockParser) TimerInfoToBlob(arg0 *TimerInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TimerInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // TimerInfoToBlob indicates an expected call of TimerInfoToBlob. func (mr *MockParserMockRecorder) TimerInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimerInfoToBlob", reflect.TypeOf((*MockParser)(nil).TimerInfoToBlob), arg0) } // TimerTaskInfoFromBlob mocks base method. func (m *MockParser) TimerTaskInfoFromBlob(arg0 []byte, arg1 string) (*TimerTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TimerTaskInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*TimerTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // TimerTaskInfoFromBlob indicates an expected call of TimerTaskInfoFromBlob. func (mr *MockParserMockRecorder) TimerTaskInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimerTaskInfoFromBlob", reflect.TypeOf((*MockParser)(nil).TimerTaskInfoFromBlob), arg0, arg1) } // TimerTaskInfoToBlob mocks base method. func (m *MockParser) TimerTaskInfoToBlob(arg0 *TimerTaskInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TimerTaskInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // TimerTaskInfoToBlob indicates an expected call of TimerTaskInfoToBlob. func (mr *MockParserMockRecorder) TimerTaskInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimerTaskInfoToBlob", reflect.TypeOf((*MockParser)(nil).TimerTaskInfoToBlob), arg0) } // TransferTaskInfoFromBlob mocks base method. func (m *MockParser) TransferTaskInfoFromBlob(arg0 []byte, arg1 string) (*TransferTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TransferTaskInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*TransferTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // TransferTaskInfoFromBlob indicates an expected call of TransferTaskInfoFromBlob. func (mr *MockParserMockRecorder) TransferTaskInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransferTaskInfoFromBlob", reflect.TypeOf((*MockParser)(nil).TransferTaskInfoFromBlob), arg0, arg1) } // TransferTaskInfoToBlob mocks base method. func (m *MockParser) TransferTaskInfoToBlob(arg0 *TransferTaskInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TransferTaskInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // TransferTaskInfoToBlob indicates an expected call of TransferTaskInfoToBlob. func (mr *MockParserMockRecorder) TransferTaskInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransferTaskInfoToBlob", reflect.TypeOf((*MockParser)(nil).TransferTaskInfoToBlob), arg0) } // WorkflowExecutionInfoFromBlob mocks base method. func (m *MockParser) WorkflowExecutionInfoFromBlob(arg0 []byte, arg1 string) (*WorkflowExecutionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WorkflowExecutionInfoFromBlob", arg0, arg1) ret0, _ := ret[0].(*WorkflowExecutionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // WorkflowExecutionInfoFromBlob indicates an expected call of WorkflowExecutionInfoFromBlob. func (mr *MockParserMockRecorder) WorkflowExecutionInfoFromBlob(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WorkflowExecutionInfoFromBlob", reflect.TypeOf((*MockParser)(nil).WorkflowExecutionInfoFromBlob), arg0, arg1) } // WorkflowExecutionInfoToBlob mocks base method. func (m *MockParser) WorkflowExecutionInfoToBlob(arg0 *WorkflowExecutionInfo) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WorkflowExecutionInfoToBlob", arg0) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // WorkflowExecutionInfoToBlob indicates an expected call of WorkflowExecutionInfoToBlob. func (mr *MockParserMockRecorder) WorkflowExecutionInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WorkflowExecutionInfoToBlob", reflect.TypeOf((*MockParser)(nil).WorkflowExecutionInfoToBlob), arg0) } // Mockencoder is a mock of encoder interface. type Mockencoder struct { ctrl *gomock.Controller recorder *MockencoderMockRecorder isgomock struct{} } // MockencoderMockRecorder is the mock recorder for Mockencoder. type MockencoderMockRecorder struct { mock *Mockencoder } // NewMockencoder creates a new mock instance. func NewMockencoder(ctrl *gomock.Controller) *Mockencoder { mock := &Mockencoder{ctrl: ctrl} mock.recorder = &MockencoderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *Mockencoder) EXPECT() *MockencoderMockRecorder { return m.recorder } // activityInfoToBlob mocks base method. func (m *Mockencoder) activityInfoToBlob(arg0 *ActivityInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "activityInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // activityInfoToBlob indicates an expected call of activityInfoToBlob. func (mr *MockencoderMockRecorder) activityInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "activityInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).activityInfoToBlob), arg0) } // childExecutionInfoToBlob mocks base method. func (m *Mockencoder) childExecutionInfoToBlob(arg0 *ChildExecutionInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "childExecutionInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // childExecutionInfoToBlob indicates an expected call of childExecutionInfoToBlob. func (mr *MockencoderMockRecorder) childExecutionInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "childExecutionInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).childExecutionInfoToBlob), arg0) } // crossClusterTaskInfoToBlob mocks base method. func (m *Mockencoder) crossClusterTaskInfoToBlob(arg0 *CrossClusterTaskInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "crossClusterTaskInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // crossClusterTaskInfoToBlob indicates an expected call of crossClusterTaskInfoToBlob. func (mr *MockencoderMockRecorder) crossClusterTaskInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "crossClusterTaskInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).crossClusterTaskInfoToBlob), arg0) } // domainInfoToBlob mocks base method. func (m *Mockencoder) domainInfoToBlob(arg0 *DomainInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "domainInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // domainInfoToBlob indicates an expected call of domainInfoToBlob. func (mr *MockencoderMockRecorder) domainInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "domainInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).domainInfoToBlob), arg0) } // encodingType mocks base method. func (m *Mockencoder) encodingType() constants.EncodingType { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "encodingType") ret0, _ := ret[0].(constants.EncodingType) return ret0 } // encodingType indicates an expected call of encodingType. func (mr *MockencoderMockRecorder) encodingType() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "encodingType", reflect.TypeOf((*Mockencoder)(nil).encodingType)) } // historyTreeInfoToBlob mocks base method. func (m *Mockencoder) historyTreeInfoToBlob(arg0 *HistoryTreeInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "historyTreeInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // historyTreeInfoToBlob indicates an expected call of historyTreeInfoToBlob. func (mr *MockencoderMockRecorder) historyTreeInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "historyTreeInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).historyTreeInfoToBlob), arg0) } // replicationTaskInfoToBlob mocks base method. func (m *Mockencoder) replicationTaskInfoToBlob(arg0 *ReplicationTaskInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "replicationTaskInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // replicationTaskInfoToBlob indicates an expected call of replicationTaskInfoToBlob. func (mr *MockencoderMockRecorder) replicationTaskInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "replicationTaskInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).replicationTaskInfoToBlob), arg0) } // requestCancelInfoToBlob mocks base method. func (m *Mockencoder) requestCancelInfoToBlob(arg0 *RequestCancelInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "requestCancelInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // requestCancelInfoToBlob indicates an expected call of requestCancelInfoToBlob. func (mr *MockencoderMockRecorder) requestCancelInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "requestCancelInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).requestCancelInfoToBlob), arg0) } // shardInfoToBlob mocks base method. func (m *Mockencoder) shardInfoToBlob(arg0 *ShardInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "shardInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // shardInfoToBlob indicates an expected call of shardInfoToBlob. func (mr *MockencoderMockRecorder) shardInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "shardInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).shardInfoToBlob), arg0) } // signalInfoToBlob mocks base method. func (m *Mockencoder) signalInfoToBlob(arg0 *SignalInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "signalInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // signalInfoToBlob indicates an expected call of signalInfoToBlob. func (mr *MockencoderMockRecorder) signalInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "signalInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).signalInfoToBlob), arg0) } // taskInfoToBlob mocks base method. func (m *Mockencoder) taskInfoToBlob(arg0 *TaskInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "taskInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // taskInfoToBlob indicates an expected call of taskInfoToBlob. func (mr *MockencoderMockRecorder) taskInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "taskInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).taskInfoToBlob), arg0) } // taskListInfoToBlob mocks base method. func (m *Mockencoder) taskListInfoToBlob(arg0 *TaskListInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "taskListInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // taskListInfoToBlob indicates an expected call of taskListInfoToBlob. func (mr *MockencoderMockRecorder) taskListInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "taskListInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).taskListInfoToBlob), arg0) } // timerInfoToBlob mocks base method. func (m *Mockencoder) timerInfoToBlob(arg0 *TimerInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "timerInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // timerInfoToBlob indicates an expected call of timerInfoToBlob. func (mr *MockencoderMockRecorder) timerInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "timerInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).timerInfoToBlob), arg0) } // timerTaskInfoToBlob mocks base method. func (m *Mockencoder) timerTaskInfoToBlob(arg0 *TimerTaskInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "timerTaskInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // timerTaskInfoToBlob indicates an expected call of timerTaskInfoToBlob. func (mr *MockencoderMockRecorder) timerTaskInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "timerTaskInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).timerTaskInfoToBlob), arg0) } // transferTaskInfoToBlob mocks base method. func (m *Mockencoder) transferTaskInfoToBlob(arg0 *TransferTaskInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "transferTaskInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // transferTaskInfoToBlob indicates an expected call of transferTaskInfoToBlob. func (mr *MockencoderMockRecorder) transferTaskInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "transferTaskInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).transferTaskInfoToBlob), arg0) } // workflowExecutionInfoToBlob mocks base method. func (m *Mockencoder) workflowExecutionInfoToBlob(arg0 *WorkflowExecutionInfo) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "workflowExecutionInfoToBlob", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // workflowExecutionInfoToBlob indicates an expected call of workflowExecutionInfoToBlob. func (mr *MockencoderMockRecorder) workflowExecutionInfoToBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "workflowExecutionInfoToBlob", reflect.TypeOf((*Mockencoder)(nil).workflowExecutionInfoToBlob), arg0) } // Mockdecoder is a mock of decoder interface. type Mockdecoder struct { ctrl *gomock.Controller recorder *MockdecoderMockRecorder isgomock struct{} } // MockdecoderMockRecorder is the mock recorder for Mockdecoder. type MockdecoderMockRecorder struct { mock *Mockdecoder } // NewMockdecoder creates a new mock instance. func NewMockdecoder(ctrl *gomock.Controller) *Mockdecoder { mock := &Mockdecoder{ctrl: ctrl} mock.recorder = &MockdecoderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *Mockdecoder) EXPECT() *MockdecoderMockRecorder { return m.recorder } // activityInfoFromBlob mocks base method. func (m *Mockdecoder) activityInfoFromBlob(arg0 []byte) (*ActivityInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "activityInfoFromBlob", arg0) ret0, _ := ret[0].(*ActivityInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // activityInfoFromBlob indicates an expected call of activityInfoFromBlob. func (mr *MockdecoderMockRecorder) activityInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "activityInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).activityInfoFromBlob), arg0) } // childExecutionInfoFromBlob mocks base method. func (m *Mockdecoder) childExecutionInfoFromBlob(arg0 []byte) (*ChildExecutionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "childExecutionInfoFromBlob", arg0) ret0, _ := ret[0].(*ChildExecutionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // childExecutionInfoFromBlob indicates an expected call of childExecutionInfoFromBlob. func (mr *MockdecoderMockRecorder) childExecutionInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "childExecutionInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).childExecutionInfoFromBlob), arg0) } // crossClusterTaskInfoFromBlob mocks base method. func (m *Mockdecoder) crossClusterTaskInfoFromBlob(arg0 []byte) (*CrossClusterTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "crossClusterTaskInfoFromBlob", arg0) ret0, _ := ret[0].(*CrossClusterTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // crossClusterTaskInfoFromBlob indicates an expected call of crossClusterTaskInfoFromBlob. func (mr *MockdecoderMockRecorder) crossClusterTaskInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "crossClusterTaskInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).crossClusterTaskInfoFromBlob), arg0) } // domainInfoFromBlob mocks base method. func (m *Mockdecoder) domainInfoFromBlob(arg0 []byte) (*DomainInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "domainInfoFromBlob", arg0) ret0, _ := ret[0].(*DomainInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // domainInfoFromBlob indicates an expected call of domainInfoFromBlob. func (mr *MockdecoderMockRecorder) domainInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "domainInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).domainInfoFromBlob), arg0) } // historyTreeInfoFromBlob mocks base method. func (m *Mockdecoder) historyTreeInfoFromBlob(arg0 []byte) (*HistoryTreeInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "historyTreeInfoFromBlob", arg0) ret0, _ := ret[0].(*HistoryTreeInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // historyTreeInfoFromBlob indicates an expected call of historyTreeInfoFromBlob. func (mr *MockdecoderMockRecorder) historyTreeInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "historyTreeInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).historyTreeInfoFromBlob), arg0) } // replicationTaskInfoFromBlob mocks base method. func (m *Mockdecoder) replicationTaskInfoFromBlob(arg0 []byte) (*ReplicationTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "replicationTaskInfoFromBlob", arg0) ret0, _ := ret[0].(*ReplicationTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // replicationTaskInfoFromBlob indicates an expected call of replicationTaskInfoFromBlob. func (mr *MockdecoderMockRecorder) replicationTaskInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "replicationTaskInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).replicationTaskInfoFromBlob), arg0) } // requestCancelInfoFromBlob mocks base method. func (m *Mockdecoder) requestCancelInfoFromBlob(arg0 []byte) (*RequestCancelInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "requestCancelInfoFromBlob", arg0) ret0, _ := ret[0].(*RequestCancelInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // requestCancelInfoFromBlob indicates an expected call of requestCancelInfoFromBlob. func (mr *MockdecoderMockRecorder) requestCancelInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "requestCancelInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).requestCancelInfoFromBlob), arg0) } // shardInfoFromBlob mocks base method. func (m *Mockdecoder) shardInfoFromBlob(arg0 []byte) (*ShardInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "shardInfoFromBlob", arg0) ret0, _ := ret[0].(*ShardInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // shardInfoFromBlob indicates an expected call of shardInfoFromBlob. func (mr *MockdecoderMockRecorder) shardInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "shardInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).shardInfoFromBlob), arg0) } // signalInfoFromBlob mocks base method. func (m *Mockdecoder) signalInfoFromBlob(arg0 []byte) (*SignalInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "signalInfoFromBlob", arg0) ret0, _ := ret[0].(*SignalInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // signalInfoFromBlob indicates an expected call of signalInfoFromBlob. func (mr *MockdecoderMockRecorder) signalInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "signalInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).signalInfoFromBlob), arg0) } // taskInfoFromBlob mocks base method. func (m *Mockdecoder) taskInfoFromBlob(arg0 []byte) (*TaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "taskInfoFromBlob", arg0) ret0, _ := ret[0].(*TaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // taskInfoFromBlob indicates an expected call of taskInfoFromBlob. func (mr *MockdecoderMockRecorder) taskInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "taskInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).taskInfoFromBlob), arg0) } // taskListInfoFromBlob mocks base method. func (m *Mockdecoder) taskListInfoFromBlob(arg0 []byte) (*TaskListInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "taskListInfoFromBlob", arg0) ret0, _ := ret[0].(*TaskListInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // taskListInfoFromBlob indicates an expected call of taskListInfoFromBlob. func (mr *MockdecoderMockRecorder) taskListInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "taskListInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).taskListInfoFromBlob), arg0) } // timerInfoFromBlob mocks base method. func (m *Mockdecoder) timerInfoFromBlob(arg0 []byte) (*TimerInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "timerInfoFromBlob", arg0) ret0, _ := ret[0].(*TimerInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // timerInfoFromBlob indicates an expected call of timerInfoFromBlob. func (mr *MockdecoderMockRecorder) timerInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "timerInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).timerInfoFromBlob), arg0) } // timerTaskInfoFromBlob mocks base method. func (m *Mockdecoder) timerTaskInfoFromBlob(arg0 []byte) (*TimerTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "timerTaskInfoFromBlob", arg0) ret0, _ := ret[0].(*TimerTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // timerTaskInfoFromBlob indicates an expected call of timerTaskInfoFromBlob. func (mr *MockdecoderMockRecorder) timerTaskInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "timerTaskInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).timerTaskInfoFromBlob), arg0) } // transferTaskInfoFromBlob mocks base method. func (m *Mockdecoder) transferTaskInfoFromBlob(arg0 []byte) (*TransferTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "transferTaskInfoFromBlob", arg0) ret0, _ := ret[0].(*TransferTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // transferTaskInfoFromBlob indicates an expected call of transferTaskInfoFromBlob. func (mr *MockdecoderMockRecorder) transferTaskInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "transferTaskInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).transferTaskInfoFromBlob), arg0) } // workflowExecutionInfoFromBlob mocks base method. func (m *Mockdecoder) workflowExecutionInfoFromBlob(arg0 []byte) (*WorkflowExecutionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "workflowExecutionInfoFromBlob", arg0) ret0, _ := ret[0].(*WorkflowExecutionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // workflowExecutionInfoFromBlob indicates an expected call of workflowExecutionInfoFromBlob. func (mr *MockdecoderMockRecorder) workflowExecutionInfoFromBlob(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "workflowExecutionInfoFromBlob", reflect.TypeOf((*Mockdecoder)(nil).workflowExecutionInfoFromBlob), arg0) } // MockthriftRWType is a mock of thriftRWType interface. type MockthriftRWType struct { ctrl *gomock.Controller recorder *MockthriftRWTypeMockRecorder isgomock struct{} } // MockthriftRWTypeMockRecorder is the mock recorder for MockthriftRWType. type MockthriftRWTypeMockRecorder struct { mock *MockthriftRWType } // NewMockthriftRWType creates a new mock instance. func NewMockthriftRWType(ctrl *gomock.Controller) *MockthriftRWType { mock := &MockthriftRWType{ctrl: ctrl} mock.recorder = &MockthriftRWTypeMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockthriftRWType) EXPECT() *MockthriftRWTypeMockRecorder { return m.recorder } // Decode mocks base method. func (m *MockthriftRWType) Decode(arg0 stream.Reader) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Decode", arg0) ret0, _ := ret[0].(error) return ret0 } // Decode indicates an expected call of Decode. func (mr *MockthriftRWTypeMockRecorder) Decode(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decode", reflect.TypeOf((*MockthriftRWType)(nil).Decode), arg0) } // Encode mocks base method. func (m *MockthriftRWType) Encode(arg0 stream.Writer) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Encode", arg0) ret0, _ := ret[0].(error) return ret0 } // Encode indicates an expected call of Encode. func (mr *MockthriftRWTypeMockRecorder) Encode(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Encode", reflect.TypeOf((*MockthriftRWType)(nil).Encode), arg0) } // FromWire mocks base method. func (m *MockthriftRWType) FromWire(w wire.Value) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FromWire", w) ret0, _ := ret[0].(error) return ret0 } // FromWire indicates an expected call of FromWire. func (mr *MockthriftRWTypeMockRecorder) FromWire(w any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FromWire", reflect.TypeOf((*MockthriftRWType)(nil).FromWire), w) } // ToWire mocks base method. func (m *MockthriftRWType) ToWire() (wire.Value, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ToWire") ret0, _ := ret[0].(wire.Value) ret1, _ := ret[1].(error) return ret0, ret1 } // ToWire indicates an expected call of ToWire. func (mr *MockthriftRWTypeMockRecorder) ToWire() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToWire", reflect.TypeOf((*MockthriftRWType)(nil).ToWire)) } ================================================ FILE: common/persistence/serialization/parser.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "fmt" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" ) type ( parser struct { dc *persistence.DynamicConfiguration encoders map[constants.EncodingType]encoder decoders map[constants.EncodingType]decoder } ) var allBlobEncodings = []constants.EncodingType{ constants.EncodingTypeThriftRW, constants.EncodingTypeThriftRWSnappy, } // NewParser constructs a new parser using encoder as specified by encodingType and using decoders specified by decodingTypes func NewParser(dc *persistence.DynamicConfiguration) (Parser, error) { encoders := make(map[constants.EncodingType]encoder) decoders := make(map[constants.EncodingType]decoder) for _, dt := range allBlobEncodings { decoder, err := getDecoder(dt) if err != nil { return nil, err } decoders[dt] = decoder encoder, err := getEncoder(dt) if err != nil { return nil, err } encoders[dt] = encoder } return &parser{ dc: dc, encoders: encoders, decoders: decoders, }, nil } func (p *parser) ShardInfoToBlob(info *ShardInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.shardInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) DomainInfoToBlob(info *DomainInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.domainInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) HistoryTreeInfoToBlob(info *HistoryTreeInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.historyTreeInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) WorkflowExecutionInfoToBlob(info *WorkflowExecutionInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.workflowExecutionInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) ActivityInfoToBlob(info *ActivityInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.activityInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) ChildExecutionInfoToBlob(info *ChildExecutionInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.childExecutionInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) SignalInfoToBlob(info *SignalInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.signalInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) RequestCancelInfoToBlob(info *RequestCancelInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.requestCancelInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) TimerInfoToBlob(info *TimerInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.timerInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) TaskInfoToBlob(info *TaskInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.taskInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) TaskListInfoToBlob(info *TaskListInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.taskListInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) TransferTaskInfoToBlob(info *TransferTaskInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.transferTaskInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) CrossClusterTaskInfoToBlob(info *CrossClusterTaskInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.crossClusterTaskInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) TimerTaskInfoToBlob(info *TimerTaskInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.timerTaskInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) ReplicationTaskInfoToBlob(info *ReplicationTaskInfo) (persistence.DataBlob, error) { db := persistence.DataBlob{} encoding := p.dc.SerializationEncoding() encoder, err := p.getCachedEncoder(constants.EncodingType(encoding)) if err != nil { return db, err } data, err := encoder.replicationTaskInfoToBlob(info) if err != nil { return db, err } db.Data = data db.Encoding = encoder.encodingType() return db, nil } func (p *parser) ShardInfoFromBlob(data []byte, encoding string) (*ShardInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.shardInfoFromBlob(data) } func (p *parser) DomainInfoFromBlob(data []byte, encoding string) (*DomainInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.domainInfoFromBlob(data) } func (p *parser) HistoryTreeInfoFromBlob(data []byte, encoding string) (*HistoryTreeInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.historyTreeInfoFromBlob(data) } func (p *parser) WorkflowExecutionInfoFromBlob(data []byte, encoding string) (*WorkflowExecutionInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.workflowExecutionInfoFromBlob(data) } func (p *parser) ActivityInfoFromBlob(data []byte, encoding string) (*ActivityInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.activityInfoFromBlob(data) } func (p *parser) ChildExecutionInfoFromBlob(data []byte, encoding string) (*ChildExecutionInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.childExecutionInfoFromBlob(data) } func (p *parser) SignalInfoFromBlob(data []byte, encoding string) (*SignalInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.signalInfoFromBlob(data) } func (p *parser) RequestCancelInfoFromBlob(data []byte, encoding string) (*RequestCancelInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.requestCancelInfoFromBlob(data) } func (p *parser) TimerInfoFromBlob(data []byte, encoding string) (*TimerInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.timerInfoFromBlob(data) } func (p *parser) TaskInfoFromBlob(data []byte, encoding string) (*TaskInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.taskInfoFromBlob(data) } func (p *parser) TaskListInfoFromBlob(data []byte, encoding string) (*TaskListInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.taskListInfoFromBlob(data) } func (p *parser) TransferTaskInfoFromBlob(data []byte, encoding string) (*TransferTaskInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.transferTaskInfoFromBlob(data) } func (p *parser) CrossClusterTaskInfoFromBlob(data []byte, encoding string) (*CrossClusterTaskInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.crossClusterTaskInfoFromBlob(data) } func (p *parser) TimerTaskInfoFromBlob(data []byte, encoding string) (*TimerTaskInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.timerTaskInfoFromBlob(data) } func (p *parser) ReplicationTaskInfoFromBlob(data []byte, encoding string) (*ReplicationTaskInfo, error) { decoder, err := p.getCachedDecoder(constants.EncodingType(encoding)) if err != nil { return nil, err } return decoder.replicationTaskInfoFromBlob(data) } func (p *parser) getCachedEncoder(encoding constants.EncodingType) (encoder, error) { encoder, ok := p.encoders[encoding] if !ok { return nil, unsupportedEncodingError(encoding) } return encoder, nil } func (p *parser) getCachedDecoder(encoding constants.EncodingType) (decoder, error) { decoder, ok := p.decoders[encoding] if !ok { return nil, unsupportedEncodingError(encoding) } return decoder, nil } func getDecoder(encoding constants.EncodingType) (decoder, error) { switch encoding { case constants.EncodingTypeThriftRW: return newThriftDecoder(), nil case constants.EncodingTypeThriftRWSnappy: return newSnappyThriftDecoder(), nil default: return nil, unsupportedEncodingError(encoding) } } func getEncoder(encoding constants.EncodingType) (encoder, error) { switch encoding { case constants.EncodingTypeThriftRW: return newThriftEncoder(), nil case constants.EncodingTypeThriftRWSnappy: return newSnappyThriftEncoder(), nil default: return nil, unsupportedEncodingError(encoding) } } func unsupportedEncodingError(encoding constants.EncodingType) error { return fmt.Errorf("invalid encoding type: %v", encoding) } ================================================ FILE: common/persistence/serialization/parser_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "fmt" "reflect" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestParserRoundTrip(t *testing.T) { dc := &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), } thriftParser, err := NewParser(dc) assert.NoError(t, err) now := time.Now().Round(time.Second) for _, testCase := range []any{ &ShardInfo{ StolenSinceRenew: 1, UpdatedAt: now, ReplicationAckLevel: 1, TransferAckLevel: 1, TimerAckLevel: now, DomainNotificationVersion: 1, ClusterTransferAckLevel: map[string]int64{"test": 1}, ClusterTimerAckLevel: map[string]time.Time{"test": now}, TransferProcessingQueueStates: []byte{1, 2, 3}, TimerProcessingQueueStates: []byte{1, 2, 3}, Owner: "owner", ClusterReplicationLevel: map[string]int64{"test": 1}, PendingFailoverMarkers: []byte{2, 3, 4}, PendingFailoverMarkersEncoding: "", TransferProcessingQueueStatesEncoding: "", TimerProcessingQueueStatesEncoding: "", }, &DomainInfo{ Name: "test", Description: "test_desc", Owner: "test_owner", Status: 1, Retention: 48 * time.Hour, EmitMetric: true, ArchivalBucket: "test_bucket", ArchivalStatus: 1, ConfigVersion: 1, FailoverVersion: 1, NotificationVersion: 1, FailoverNotificationVersion: 1, ActiveClusterName: "test_active_cluster", Clusters: []string{"test_active_cluster", "test_standby_cluster"}, Data: map[string]string{"test_key": "test_value"}, BadBinaries: []byte{1, 2, 3}, BadBinariesEncoding: "", HistoryArchivalStatus: 1, HistoryArchivalURI: "test_history_archival_uri", VisibilityArchivalStatus: 1, VisibilityArchivalURI: "test_visibility_archival_uri", }, &HistoryTreeInfo{ CreatedTimestamp: now, Ancestors: []*types.HistoryBranchRange{ { BranchID: "test_branch_id1", }, { BranchID: "test_branch_id2", }, }, Info: "test_info", }, &WorkflowExecutionInfo{ ParentDomainID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), ParentWorkflowID: "test_parent_workflow_id", ParentRunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), InitiatedID: 1, CompletionEventBatchID: common.Int64Ptr(1), CompletionEvent: []byte{1, 2, 3}, CompletionEventEncoding: "", TaskList: "test_task_list", WorkflowTypeName: "test_workflow_type_name", WorkflowTimeout: 48 * time.Hour, ExecutionContext: []byte{1, 2, 3}, State: 2, CloseStatus: 1, StartVersion: 1, LastWriteEventID: common.Int64Ptr(2), LastEventTaskID: 3, LastFirstEventID: 5, LastProcessedEvent: 4, StartTimestamp: now, LastUpdatedTimestamp: now, DecisionVersion: 1, DecisionScheduleID: 1, DecisionStartedID: 1, DecisionRequestID: "test_decision_request_id", DecisionTimeout: 48 * time.Hour, DecisionAttempt: 1, DecisionStartedTimestamp: now, DecisionScheduledTimestamp: now, DecisionOriginalScheduledTimestamp: now, CancelRequested: true, CancelRequestID: "test_cancel_request_id", StickyTaskList: "test_sticky_task_list", StickyScheduleToStartTimeout: 48 * time.Hour, ClientLibraryVersion: "test_client_library_version", ClientFeatureVersion: "test_client_feature_version", ClientImpl: "test_client_impl", SignalCount: 1, HistorySize: 100, AutoResetPoints: []byte{1, 2, 3}, AutoResetPointsEncoding: "", Memo: map[string][]byte{"test_memo_key": {1, 2, 3}}, SearchAttributes: map[string][]byte{"test_search_attr_key": {1, 2, 3}}, }, &ActivityInfo{ Version: 1, ScheduledEventBatchID: 2, ScheduledEvent: []byte{1, 2, 3}, ScheduledEventEncoding: "scheduled_event_encoding", ScheduledTimestamp: now, StartedID: 1, StartedEvent: []byte{1, 2, 3}, StartedEventEncoding: "started_event_encoding", StartedTimestamp: now, ActivityID: "test_activity_id", RequestID: "test_request_id", ScheduleToStartTimeout: 48 * time.Hour, ScheduleToCloseTimeout: 48 * time.Hour, StartToCloseTimeout: 48 * time.Hour, HeartbeatTimeout: 48 * time.Hour, CancelRequested: true, CancelRequestID: 3, TimerTaskStatus: 1, Attempt: 1, TaskList: "test_task_list", StartedIdentity: "test_started_identity", HasRetryPolicy: true, RetryInitialInterval: time.Hour, RetryBackoffCoefficient: 1.1, RetryMaximumInterval: time.Hour, RetryMaximumAttempts: 1, RetryExpirationTimestamp: now.Add(time.Hour), RetryNonRetryableErrors: []string{"test_retry_non_retryable_error"}, RetryLastWorkerIdentity: "test_retry_last_worker_identity", }, &ChildExecutionInfo{ Version: 1, InitiatedEventBatchID: 2, StartedID: 3, InitiatedEvent: []byte{1, 2, 3}, InitiatedEventEncoding: "initiated_event_encoding", StartedWorkflowID: "test_started_workflow_id", StartedRunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), StartedEvent: []byte{1, 2, 3}, StartedEventEncoding: "started_event_encoding", CreateRequestID: "test_create_request_id", DomainID: "test_domain_id", WorkflowTypeName: "test_workflow_type_name", ParentClosePolicy: 1, }, &SignalInfo{ Version: 1, InitiatedEventBatchID: 2, RequestID: "test_request_id", Name: "test_name", Input: []byte{1, 2, 3}, Control: []byte{1, 2, 3}, }, &RequestCancelInfo{ Version: 1, InitiatedEventBatchID: 2, CancelRequestID: "test_cancel_request_id", }, &TimerInfo{ Version: 1, StartedID: 2, ExpiryTimestamp: zeroUnix, TaskID: 3, }, &TaskInfo{ WorkflowID: "test_workflow_id", RunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), ScheduleID: 1, ExpiryTimestamp: now, CreatedTimestamp: now, PartitionConfig: map[string]string{"test_partition_key": "test_partition_value"}, }, &TaskListInfo{ Kind: 1, AckLevel: 2, ExpiryTimestamp: now, LastUpdated: now, }, &TransferTaskInfo{ DomainID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), WorkflowID: "test_workflow_id", RunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), TaskType: 1, TargetDomainID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), TargetDomainIDs: []UUID{MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430a8")}, TargetWorkflowID: "test_target_workflow_id", TargetRunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), TaskList: "test_task_list", TargetChildWorkflowOnly: true, ScheduleID: 1, Version: 2, VisibilityTimestamp: now, }, &TimerTaskInfo{ DomainID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), WorkflowID: "test_workflow_id", RunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), TaskType: 1, TimeoutType: common.Int16Ptr(1), Version: 2, ScheduleAttempt: 3, EventID: 4, }, &ReplicationTaskInfo{ DomainID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), WorkflowID: "test_workflow_id", RunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), TaskType: 1, Version: 2, FirstEventID: 3, NextEventID: 4, ScheduledID: 5, EventStoreVersion: 6, NewRunEventStoreVersion: 7, BranchToken: []byte{1, 2, 3}, NewRunBranchToken: []byte{1, 2, 3}, CreationTimestamp: now, }, } { t.Run(reflect.TypeOf(testCase).String(), func(t *testing.T) { blob := parse(t, thriftParser, testCase) result := unparse(t, thriftParser, blob, testCase) assert.Equal(t, testCase, result) }) } } func parse(t *testing.T, parser Parser, data any) persistence.DataBlob { var ( blob persistence.DataBlob err error ) switch v := data.(type) { case *ShardInfo: blob, err = parser.ShardInfoToBlob(v) case *DomainInfo: blob, err = parser.DomainInfoToBlob(v) case *HistoryTreeInfo: blob, err = parser.HistoryTreeInfoToBlob(v) case *WorkflowExecutionInfo: blob, err = parser.WorkflowExecutionInfoToBlob(v) case *ActivityInfo: blob, err = parser.ActivityInfoToBlob(v) case *ChildExecutionInfo: blob, err = parser.ChildExecutionInfoToBlob(v) case *SignalInfo: blob, err = parser.SignalInfoToBlob(v) case *RequestCancelInfo: blob, err = parser.RequestCancelInfoToBlob(v) case *TimerInfo: blob, err = parser.TimerInfoToBlob(v) case *TaskInfo: blob, err = parser.TaskInfoToBlob(v) case *TaskListInfo: blob, err = parser.TaskListInfoToBlob(v) case *TransferTaskInfo: blob, err = parser.TransferTaskInfoToBlob(v) case *TimerTaskInfo: blob, err = parser.TimerTaskInfoToBlob(v) case *ReplicationTaskInfo: blob, err = parser.ReplicationTaskInfoToBlob(v) default: err = fmt.Errorf("unknown type %T", v) } assert.NoError(t, err) return blob } func unparse(t *testing.T, parser Parser, blob persistence.DataBlob, result any) any { var ( data any err error ) switch v := result.(type) { case *ShardInfo: data, err = parser.ShardInfoFromBlob(blob.Data, string(blob.Encoding)) case *DomainInfo: data, err = parser.DomainInfoFromBlob(blob.Data, string(blob.Encoding)) case *HistoryTreeInfo: data, err = parser.HistoryTreeInfoFromBlob(blob.Data, string(blob.Encoding)) case *WorkflowExecutionInfo: data, err = parser.WorkflowExecutionInfoFromBlob(blob.Data, string(blob.Encoding)) case *ActivityInfo: data, err = parser.ActivityInfoFromBlob(blob.Data, string(blob.Encoding)) case *ChildExecutionInfo: data, err = parser.ChildExecutionInfoFromBlob(blob.Data, string(blob.Encoding)) case *SignalInfo: data, err = parser.SignalInfoFromBlob(blob.Data, string(blob.Encoding)) case *RequestCancelInfo: data, err = parser.RequestCancelInfoFromBlob(blob.Data, string(blob.Encoding)) case *TimerInfo: data, err = parser.TimerInfoFromBlob(blob.Data, string(blob.Encoding)) case *TaskInfo: data, err = parser.TaskInfoFromBlob(blob.Data, string(blob.Encoding)) case *TaskListInfo: data, err = parser.TaskListInfoFromBlob(blob.Data, string(blob.Encoding)) case *TransferTaskInfo: data, err = parser.TransferTaskInfoFromBlob(blob.Data, string(blob.Encoding)) case *TimerTaskInfo: data, err = parser.TimerTaskInfoFromBlob(blob.Data, string(blob.Encoding)) case *ReplicationTaskInfo: data, err = parser.ReplicationTaskInfoFromBlob(blob.Data, string(blob.Encoding)) default: err = fmt.Errorf("unknown type %T", v) } assert.NoError(t, err) return data } func TestParser_WorkflowExecution_with_cron(t *testing.T) { info := &WorkflowExecutionInfo{ CronSchedule: "@every 1m", IsCron: true, } dc := &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), } parser, err := NewParser(dc) require.NoError(t, err) blob, err := parser.WorkflowExecutionInfoToBlob(info) require.NoError(t, err) result, err := parser.WorkflowExecutionInfoFromBlob(blob.Data, string(blob.Encoding)) require.NoError(t, err) assert.Equal(t, info, result) } ================================================ FILE: common/persistence/serialization/persistence_mapper.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func ToInternalWorkflowExecutionInfo(info *WorkflowExecutionInfo) *persistence.InternalWorkflowExecutionInfo { result := &persistence.InternalWorkflowExecutionInfo{ CompletionEventBatchID: constants.EmptyEventID, TaskList: info.GetTaskList(), TaskListKind: info.GetTaskListKind(), WorkflowTypeName: info.GetWorkflowTypeName(), WorkflowTimeout: info.GetWorkflowTimeout(), DecisionStartToCloseTimeout: info.GetDecisionTaskTimeout(), ExecutionContext: info.GetExecutionContext(), State: int(info.GetState()), CloseStatus: int(info.GetCloseStatus()), LastFirstEventID: info.GetLastFirstEventID(), LastEventTaskID: info.GetLastEventTaskID(), LastProcessedEvent: info.GetLastProcessedEvent(), StartTimestamp: info.GetStartTimestamp(), LastUpdatedTimestamp: info.GetLastUpdatedTimestamp(), CreateRequestID: info.GetCreateRequestID(), SignalCount: int32(info.GetSignalCount()), DecisionVersion: info.GetDecisionVersion(), DecisionScheduleID: info.GetDecisionScheduleID(), DecisionStartedID: info.GetDecisionStartedID(), DecisionRequestID: info.GetDecisionRequestID(), DecisionTimeout: info.GetDecisionTimeout(), DecisionAttempt: info.GetDecisionAttempt(), DecisionStartedTimestamp: info.GetDecisionStartedTimestamp(), DecisionScheduledTimestamp: info.GetDecisionScheduledTimestamp(), DecisionOriginalScheduledTimestamp: info.GetDecisionOriginalScheduledTimestamp(), StickyTaskList: info.GetStickyTaskList(), StickyScheduleToStartTimeout: info.GetStickyScheduleToStartTimeout(), ClientLibraryVersion: info.GetClientLibraryVersion(), ClientFeatureVersion: info.GetClientFeatureVersion(), ClientImpl: info.GetClientImpl(), Attempt: int32(info.GetRetryAttempt()), HasRetryPolicy: info.GetHasRetryPolicy(), InitialInterval: info.GetRetryInitialInterval(), BackoffCoefficient: info.GetRetryBackoffCoefficient(), MaximumInterval: info.GetRetryMaximumInterval(), ExpirationTime: info.GetRetryExpirationTimestamp(), MaximumAttempts: info.GetRetryMaximumAttempts(), NonRetriableErrors: info.GetRetryNonRetryableErrors(), BranchToken: info.GetEventBranchToken(), CronSchedule: info.GetCronSchedule(), ExpirationInterval: info.GetRetryExpiration(), Memo: info.GetMemo(), SearchAttributes: info.GetSearchAttributes(), HistorySize: info.GetHistorySize(), FirstExecutionRunID: info.FirstExecutionRunID.String(), PartitionConfig: info.PartitionConfig, IsCron: info.IsCron, CronOverlapPolicy: types.CronOverlapPolicy(info.GetCronOverlapPolicy()), } if info.ParentDomainID != nil { result.ParentDomainID = info.ParentDomainID.String() result.ParentWorkflowID = info.GetParentWorkflowID() result.ParentRunID = info.ParentRunID.String() result.InitiatedID = info.GetInitiatedID() } if info.GetCancelRequested() { result.CancelRequested = true result.CancelRequestID = info.GetCancelRequestID() } if info.CompletionEventBatchID != nil { result.CompletionEventBatchID = info.GetCompletionEventBatchID() } if info.CompletionEvent != nil { result.CompletionEvent = persistence.NewDataBlob(info.CompletionEvent, constants.EncodingType(info.GetCompletionEventEncoding())) } if info.AutoResetPoints != nil { result.AutoResetPoints = persistence.NewDataBlob(info.AutoResetPoints, constants.EncodingType(info.GetAutoResetPointsEncoding())) } if info.ActiveClusterSelectionPolicy != nil { result.ActiveClusterSelectionPolicy = persistence.NewDataBlob(info.ActiveClusterSelectionPolicy, constants.EncodingType(info.GetActiveClusterSelectionPolicyEncoding())) } return result } func FromInternalWorkflowExecutionInfo(executionInfo *persistence.InternalWorkflowExecutionInfo) *WorkflowExecutionInfo { info := &WorkflowExecutionInfo{ TaskList: executionInfo.TaskList, TaskListKind: executionInfo.TaskListKind, WorkflowTypeName: executionInfo.WorkflowTypeName, WorkflowTimeout: executionInfo.WorkflowTimeout, DecisionTaskTimeout: executionInfo.DecisionStartToCloseTimeout, ExecutionContext: executionInfo.ExecutionContext, State: int32(executionInfo.State), CloseStatus: int32(executionInfo.CloseStatus), LastFirstEventID: executionInfo.LastFirstEventID, LastEventTaskID: executionInfo.LastEventTaskID, LastProcessedEvent: executionInfo.LastProcessedEvent, StartTimestamp: executionInfo.StartTimestamp, LastUpdatedTimestamp: executionInfo.LastUpdatedTimestamp, CreateRequestID: executionInfo.CreateRequestID, DecisionVersion: executionInfo.DecisionVersion, DecisionScheduleID: executionInfo.DecisionScheduleID, DecisionStartedID: executionInfo.DecisionStartedID, DecisionRequestID: executionInfo.DecisionRequestID, DecisionTimeout: executionInfo.DecisionTimeout, DecisionAttempt: executionInfo.DecisionAttempt, DecisionStartedTimestamp: executionInfo.DecisionStartedTimestamp, DecisionScheduledTimestamp: executionInfo.DecisionScheduledTimestamp, DecisionOriginalScheduledTimestamp: executionInfo.DecisionOriginalScheduledTimestamp, StickyTaskList: executionInfo.StickyTaskList, StickyScheduleToStartTimeout: executionInfo.StickyScheduleToStartTimeout, ClientLibraryVersion: executionInfo.ClientLibraryVersion, ClientFeatureVersion: executionInfo.ClientFeatureVersion, ClientImpl: executionInfo.ClientImpl, SignalCount: int64(executionInfo.SignalCount), HistorySize: executionInfo.HistorySize, CronSchedule: executionInfo.CronSchedule, CompletionEventBatchID: &executionInfo.CompletionEventBatchID, HasRetryPolicy: executionInfo.HasRetryPolicy, RetryAttempt: int64(executionInfo.Attempt), RetryInitialInterval: executionInfo.InitialInterval, RetryBackoffCoefficient: executionInfo.BackoffCoefficient, RetryMaximumInterval: executionInfo.MaximumInterval, RetryMaximumAttempts: executionInfo.MaximumAttempts, RetryExpiration: executionInfo.ExpirationInterval, RetryExpirationTimestamp: executionInfo.ExpirationTime, RetryNonRetryableErrors: executionInfo.NonRetriableErrors, EventStoreVersion: persistence.EventStoreVersion, EventBranchToken: executionInfo.BranchToken, AutoResetPoints: executionInfo.AutoResetPoints.GetData(), AutoResetPointsEncoding: string(executionInfo.AutoResetPoints.GetEncoding()), SearchAttributes: executionInfo.SearchAttributes, Memo: executionInfo.Memo, CompletionEventEncoding: string(constants.EncodingTypeEmpty), VersionHistoriesEncoding: string(constants.EncodingTypeEmpty), InitiatedID: constants.EmptyEventID, FirstExecutionRunID: MustParseUUID(executionInfo.FirstExecutionRunID), PartitionConfig: executionInfo.PartitionConfig, IsCron: executionInfo.IsCron, CronOverlapPolicy: executionInfo.CronOverlapPolicy, ActiveClusterSelectionPolicy: executionInfo.ActiveClusterSelectionPolicy.GetData(), ActiveClusterSelectionPolicyEncoding: string(executionInfo.ActiveClusterSelectionPolicy.GetEncoding()), } if executionInfo.CompletionEvent != nil { info.CompletionEvent = executionInfo.CompletionEvent.Data info.CompletionEventEncoding = string(executionInfo.CompletionEvent.Encoding) } if executionInfo.ParentDomainID != "" { info.ParentDomainID = MustParseUUID(executionInfo.ParentDomainID) info.ParentWorkflowID = executionInfo.ParentWorkflowID info.ParentRunID = MustParseUUID(executionInfo.ParentRunID) info.InitiatedID = executionInfo.InitiatedID } if executionInfo.CancelRequested { info.CancelRequested = true info.CancelRequestID = executionInfo.CancelRequestID } if executionInfo.ActiveClusterSelectionPolicy != nil { info.ActiveClusterSelectionPolicy = executionInfo.ActiveClusterSelectionPolicy.Data info.ActiveClusterSelectionPolicyEncoding = string(executionInfo.ActiveClusterSelectionPolicy.Encoding) } return info } ================================================ FILE: common/persistence/serialization/persistence_mapper_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "math/rand" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestInternalWorkflowExecutionInfo(t *testing.T) { expected := &persistence.InternalWorkflowExecutionInfo{ ParentDomainID: uuid.New(), ParentWorkflowID: "ParentWorkflowID", ParentRunID: uuid.New(), FirstExecutionRunID: uuid.New(), InitiatedID: int64(rand.Intn(1000)), CompletionEventBatchID: int64(rand.Intn(1000)), CompletionEvent: persistence.NewDataBlob([]byte(`CompletionEvent`), constants.EncodingTypeJSON), TaskList: "TaskList", TaskListKind: types.TaskListKindNormal, WorkflowTypeName: "WorkflowTypeName", WorkflowTimeout: time.Minute * time.Duration(rand.Intn(10)), DecisionStartToCloseTimeout: time.Minute * time.Duration(rand.Intn(10)), ExecutionContext: []byte("ExecutionContext"), State: rand.Intn(1000), CloseStatus: rand.Intn(1000), LastFirstEventID: int64(rand.Intn(1000)), LastEventTaskID: int64(rand.Intn(1000)), LastProcessedEvent: int64(rand.Intn(1000)), StartTimestamp: time.UnixMilli(1752018142820), LastUpdatedTimestamp: time.UnixMilli(1752018142821), CreateRequestID: "CreateRequestID", SignalCount: int32(rand.Intn(1000)), DecisionVersion: int64(rand.Intn(1000)), DecisionScheduleID: int64(rand.Intn(1000)), DecisionStartedID: int64(rand.Intn(1000)), DecisionRequestID: "DecisionRequestID", DecisionTimeout: time.Minute * time.Duration(rand.Intn(10)), DecisionAttempt: int64(rand.Intn(1000)), DecisionStartedTimestamp: time.UnixMilli(1752018142822), DecisionScheduledTimestamp: time.UnixMilli(1752018142823), DecisionOriginalScheduledTimestamp: time.UnixMilli(1752018142824), CancelRequested: true, CancelRequestID: "CancelRequestID", StickyTaskList: "StickyTaskList", StickyScheduleToStartTimeout: time.Minute * time.Duration(rand.Intn(10)), ClientLibraryVersion: "ClientLibraryVersion", ClientFeatureVersion: "ClientFeatureVersion", ClientImpl: "ClientImpl", AutoResetPoints: persistence.NewDataBlob([]byte("AutoResetPoints"), constants.EncodingTypeJSON), Attempt: int32(rand.Intn(1000)), HasRetryPolicy: true, InitialInterval: time.Minute * time.Duration(rand.Intn(10)), BackoffCoefficient: rand.Float64() * 1000, MaximumInterval: time.Minute * time.Duration(rand.Intn(10)), ExpirationTime: time.UnixMilli(1752018142825), MaximumAttempts: int32(rand.Intn(1000)), NonRetriableErrors: []string{"RetryNonRetryableErrors"}, BranchToken: []byte("EventBranchToken"), CronSchedule: "CronSchedule", ExpirationInterval: time.Minute * time.Duration(rand.Intn(10)), Memo: map[string][]byte{"key_1": []byte("Memo")}, SearchAttributes: map[string][]byte{"key_1": []byte("SearchAttributes")}, HistorySize: int64(rand.Intn(1000)), PartitionConfig: map[string]string{"zone": "dca1"}, IsCron: true, ActiveClusterSelectionPolicy: persistence.NewDataBlob([]byte("ActiveClusterSelectionPolicy"), constants.EncodingTypeJSON), CronOverlapPolicy: types.CronOverlapPolicySkipped, } actual := ToInternalWorkflowExecutionInfo(FromInternalWorkflowExecutionInfo(expected)) assert.Equal(t, expected, actual) } ================================================ FILE: common/persistence/serialization/serialization_test_utils.go ================================================ package serialization import ( "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) var shardInfoTestData = &ShardInfo{ StolenSinceRenew: 1, UpdatedAt: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), ReplicationAckLevel: 1, TransferAckLevel: 1, TimerAckLevel: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), DomainNotificationVersion: 1, ClusterTransferAckLevel: map[string]int64{"test": 1}, ClusterTimerAckLevel: map[string]time.Time{"test": time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local)}, TransferProcessingQueueStates: []byte{1, 2, 3}, TimerProcessingQueueStates: []byte{1, 2, 3}, Owner: "owner", ClusterReplicationLevel: map[string]int64{"test": 1}, PendingFailoverMarkers: []byte{2, 3, 4}, PendingFailoverMarkersEncoding: "", TransferProcessingQueueStatesEncoding: "", TimerProcessingQueueStatesEncoding: "", } var domainInfoTestData = &DomainInfo{ Description: "test_desc", Owner: "test_owner", Status: 1, Retention: 48 * time.Hour, EmitMetric: true, ArchivalBucket: "test_bucket", ArchivalStatus: 1, ConfigVersion: 1, FailoverVersion: 1, NotificationVersion: 1, FailoverNotificationVersion: 1, ActiveClusterName: "test_active_cluster", Clusters: []string{"test_active_cluster", "test_standby_cluster"}, Data: map[string]string{"test_key": "test_value"}, BadBinaries: []byte{1, 2, 3}, BadBinariesEncoding: "", HistoryArchivalStatus: 1, HistoryArchivalURI: "test_history_archival_uri", VisibilityArchivalStatus: 1, VisibilityArchivalURI: "test_visibility_archival_uri", } var historyTreeInfoTestData = &HistoryTreeInfo{ CreatedTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), Ancestors: []*types.HistoryBranchRange{ { BranchID: "test_branch_id1", }, }, } var workflowExecutionInfoTestData = &WorkflowExecutionInfo{ ParentDomainID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), ParentWorkflowID: "test_parent_workflow_id", ParentRunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), InitiatedID: 1, CompletionEventBatchID: common.Int64Ptr(2), CompletionEvent: []byte("test_completion_event"), CompletionEventEncoding: "test_completion_event_encoding", TaskList: "test_task_list", WorkflowTypeName: "test_workflow_type", WorkflowTimeout: 10 * time.Second, DecisionTaskTimeout: 5 * time.Second, ExecutionContext: []byte("test_execution_context"), State: 1, CloseStatus: 1, StartVersion: 1, LastWriteEventID: common.Int64Ptr(3), LastEventTaskID: 4, LastFirstEventID: 5, LastProcessedEvent: 6, StartTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), LastUpdatedTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), CreateRequestID: "test_create_request_id", DecisionVersion: 7, DecisionScheduleID: 8, DecisionStartedID: 9, DecisionRequestID: "test_decision_request_id", DecisionTimeout: 3 * time.Second, DecisionAttempt: 10, DecisionStartedTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), DecisionScheduledTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), DecisionOriginalScheduledTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), CancelRequested: true, CancelRequestID: "test_cancel_request_id", StickyTaskList: "test_sticky_task_list", StickyScheduleToStartTimeout: 2 * time.Second, RetryAttempt: 11, RetryInitialInterval: 1 * time.Second, RetryMaximumInterval: 30 * time.Second, RetryMaximumAttempts: 3, RetryExpiration: time.Hour, RetryBackoffCoefficient: 2.0, RetryExpirationTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), RetryNonRetryableErrors: []string{"test_error"}, HasRetryPolicy: true, CronSchedule: "test_cron", IsCron: true, EventStoreVersion: 12, EventBranchToken: []byte("test_branch_token"), SignalCount: 13, HistorySize: 14, ClientLibraryVersion: "test_client_version", ClientFeatureVersion: "test_feature_version", ClientImpl: "test_client_impl", AutoResetPoints: []byte("test_reset_points"), AutoResetPointsEncoding: "test_reset_points_encoding", SearchAttributes: map[string][]byte{"test_key": []byte("test_value")}, Memo: map[string][]byte{"test_memo": []byte("test_memo_value")}, VersionHistories: []byte("test_version_histories"), VersionHistoriesEncoding: "test_version_histories_encoding", FirstExecutionRunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), } var activityInfoTestData = &ActivityInfo{ Version: 1, ScheduledEventBatchID: 2, ScheduledEvent: []byte("test_scheduled_event"), ScheduledEventEncoding: "test_scheduled_encoding", ScheduledTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), StartedID: 3, StartedEvent: []byte("test_started_event"), StartedEventEncoding: "test_started_encoding", StartedTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), ActivityID: "test_activity_id", RequestID: "test_request_id", ScheduleToStartTimeout: 5 * time.Second, ScheduleToCloseTimeout: 10 * time.Second, StartToCloseTimeout: 8 * time.Second, HeartbeatTimeout: 3 * time.Second, CancelRequested: true, CancelRequestID: 4, TimerTaskStatus: 5, Attempt: 6, TaskList: "test_task_list", StartedIdentity: "test_identity", HasRetryPolicy: true, RetryInitialInterval: 1 * time.Second, RetryMaximumInterval: 30 * time.Second, RetryMaximumAttempts: 3, RetryExpirationTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), RetryBackoffCoefficient: 2.0, RetryNonRetryableErrors: []string{"test_error"}, RetryLastFailureReason: "test_failure_reason", RetryLastWorkerIdentity: "test_worker_identity", RetryLastFailureDetails: []byte("test_failure_details"), } var childExecutionInfoTestData = &ChildExecutionInfo{ Version: 1, InitiatedEventBatchID: 2, StartedID: 3, InitiatedEvent: []byte("test_initiated_event"), InitiatedEventEncoding: "test_initiated_encoding", StartedWorkflowID: "test_started_workflow_id", StartedRunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), StartedEvent: []byte("test_started_event"), StartedEventEncoding: "test_started_encoding", CreateRequestID: "test_create_request_id", DomainID: "test_domain_id", DomainNameDEPRECATED: "test_domain_name", WorkflowTypeName: "test_workflow_type", ParentClosePolicy: 4, } var signalInfoTestData = &SignalInfo{ Version: 1, InitiatedEventBatchID: 2, RequestID: "test_request_id", Name: "test_signal_name", Input: []byte("test_input"), Control: []byte("test_control"), } var requestCancelInfoTestData = &RequestCancelInfo{ Version: 1, InitiatedEventBatchID: 2, CancelRequestID: "test_cancel_request_id", } var timerInfoTestData = &TimerInfo{ Version: 1, StartedID: 2, ExpiryTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), TaskID: 3, } var taskInfoTestData = &TaskInfo{ WorkflowID: "test_workflow_id", RunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), ScheduleID: 1, ExpiryTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), CreatedTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), PartitionConfig: map[string]string{"test_key": "test_value"}, } var taskListInfoTestData = &TaskListInfo{ Kind: 1, AckLevel: 2, ExpiryTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), LastUpdated: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), } var transferTaskInfoTestData = &TransferTaskInfo{ DomainID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), WorkflowID: "test_workflow_id", RunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), TaskType: 1, TargetDomainID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), TargetDomainIDs: []UUID{MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")}, TargetWorkflowID: "test_target_workflow_id", TargetRunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), TaskList: "test_task_list", TargetChildWorkflowOnly: true, ScheduleID: 2, Version: 3, VisibilityTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), } var timerTaskInfoTestData = &TimerTaskInfo{ DomainID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), WorkflowID: "test_workflow_id", RunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), TaskType: 1, TimeoutType: common.Int16Ptr(2), Version: 3, ScheduleAttempt: 4, EventID: 5, } var replicationTaskInfoTestData = &ReplicationTaskInfo{ DomainID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), WorkflowID: "test_workflow_id", RunID: MustParseUUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), TaskType: 1, Version: 2, FirstEventID: 3, NextEventID: 4, ScheduledID: 5, EventStoreVersion: 6, NewRunEventStoreVersion: 7, BranchToken: []byte("test_branch_token"), NewRunBranchToken: []byte("test_new_run_branch_token"), CreationTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local), } ================================================ FILE: common/persistence/serialization/snappy_thrift_decoder.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "bytes" "github.com/golang/snappy" "go.uber.org/thriftrw/protocol/binary" "github.com/uber/cadence/.gen/go/sqlblobs" ) type snappyThriftDecoder struct{} func newSnappyThriftDecoder() decoder { return &snappyThriftDecoder{} } func (d *snappyThriftDecoder) shardInfoFromBlob(data []byte) (*ShardInfo, error) { result := &sqlblobs.ShardInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return shardInfoFromThrift(result), nil } func (d *snappyThriftDecoder) domainInfoFromBlob(data []byte) (*DomainInfo, error) { result := &sqlblobs.DomainInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return domainInfoFromThrift(result), nil } func (d *snappyThriftDecoder) historyTreeInfoFromBlob(data []byte) (*HistoryTreeInfo, error) { result := &sqlblobs.HistoryTreeInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return historyTreeInfoFromThrift(result), nil } func (d *snappyThriftDecoder) workflowExecutionInfoFromBlob(data []byte) (*WorkflowExecutionInfo, error) { result := &sqlblobs.WorkflowExecutionInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return workflowExecutionInfoFromThrift(result), nil } func (d *snappyThriftDecoder) activityInfoFromBlob(data []byte) (*ActivityInfo, error) { result := &sqlblobs.ActivityInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return activityInfoFromThrift(result), nil } func (d *snappyThriftDecoder) childExecutionInfoFromBlob(data []byte) (*ChildExecutionInfo, error) { result := &sqlblobs.ChildExecutionInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return childExecutionInfoFromThrift(result), nil } func (d *snappyThriftDecoder) signalInfoFromBlob(data []byte) (*SignalInfo, error) { result := &sqlblobs.SignalInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return signalInfoFromThrift(result), nil } func (d *snappyThriftDecoder) requestCancelInfoFromBlob(data []byte) (*RequestCancelInfo, error) { result := &sqlblobs.RequestCancelInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return requestCancelInfoFromThrift(result), nil } func (d *snappyThriftDecoder) timerInfoFromBlob(data []byte) (*TimerInfo, error) { result := &sqlblobs.TimerInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return timerInfoFromThrift(result), nil } func (d *snappyThriftDecoder) taskInfoFromBlob(data []byte) (*TaskInfo, error) { result := &sqlblobs.TaskInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return taskInfoFromThrift(result), nil } func (d *snappyThriftDecoder) taskListInfoFromBlob(data []byte) (*TaskListInfo, error) { result := &sqlblobs.TaskListInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return taskListInfoFromThrift(result), nil } func (d *snappyThriftDecoder) transferTaskInfoFromBlob(data []byte) (*TransferTaskInfo, error) { result := &sqlblobs.TransferTaskInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return transferTaskInfoFromThrift(result), nil } func (d *snappyThriftDecoder) crossClusterTaskInfoFromBlob(data []byte) (*CrossClusterTaskInfo, error) { result := &sqlblobs.TransferTaskInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return crossClusterTaskInfoFromThrift(result), nil } func (d *snappyThriftDecoder) timerTaskInfoFromBlob(data []byte) (*TimerTaskInfo, error) { result := &sqlblobs.TimerTaskInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return timerTaskInfoFromThrift(result), nil } func (d *snappyThriftDecoder) replicationTaskInfoFromBlob(data []byte) (*ReplicationTaskInfo, error) { result := &sqlblobs.ReplicationTaskInfo{} if err := snappyThriftRWDecode(data, result); err != nil { return nil, err } return replicationTaskInfoFromThrift(result), nil } func snappyThriftRWDecode(b []byte, result thriftRWType) error { decompressed, err := snappy.Decode(nil, b) if err != nil { return err } buf := bytes.NewReader(decompressed) sr := binary.Default.Reader(buf) return result.Decode(sr) } ================================================ FILE: common/persistence/serialization/snappy_thrift_decoder_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "testing" "time" "github.com/golang/snappy" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" ) func TestSnappyThriftDecoderRoundTrip(t *testing.T) { dc := &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRWSnappy)), } parser, err := NewParser(dc) require.NoError(t, err) testCases := []struct { name string data interface{} }{ {name: "ShardInfo", data: shardInfoTestData}, {name: "DomainInfo", data: domainInfoTestData}, {name: "HistoryTreeInfo", data: historyTreeInfoTestData}, {name: "WorkflowExecutionInfo", data: workflowExecutionInfoTestData}, {name: "ActivityInfo", data: activityInfoTestData}, {name: "ChildExecutionInfo", data: childExecutionInfoTestData}, {name: "SignalInfo", data: signalInfoTestData}, {name: "RequestCancelInfo", data: requestCancelInfoTestData}, {name: "TimerInfo", data: timerInfoTestData}, {name: "TaskInfo", data: taskInfoTestData}, {name: "TaskListInfo", data: taskListInfoTestData}, {name: "TransferTaskInfo", data: transferTaskInfoTestData}, {name: "TimerTaskInfo", data: timerTaskInfoTestData}, {name: "ReplicationTaskInfo", data: replicationTaskInfoTestData}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { blob := encodeWithParser(t, parser, tc.data) decoded := decodeWithParser(t, parser, blob, tc.data) assert.Equal(t, tc.data, decoded) }) } } func TestSnappyThriftDecoderErrorHandling(t *testing.T) { decoder := newSnappyThriftDecoder() testCases := []struct { name string data []byte decodeFunc func([]byte) (interface{}, error) expectError bool }{ { name: "Invalid snappy data for ShardInfo", data: []byte("invalid snappy data"), decodeFunc: func(data []byte) (interface{}, error) { return decoder.shardInfoFromBlob(data) }, expectError: true, }, { name: "Empty data for DomainInfo", data: []byte{}, decodeFunc: func(data []byte) (interface{}, error) { return decoder.domainInfoFromBlob(data) }, expectError: true, }, { name: "Corrupted snappy data for ActivityInfo", data: []byte{0xff, 0xff, 0xff, 0xff}, decodeFunc: func(data []byte) (interface{}, error) { return decoder.activityInfoFromBlob(data) }, expectError: true, }, { name: "Valid snappy but invalid thrift for WorkflowExecutionInfo", data: func() []byte { // Create valid snappy compressed data but with invalid thrift content invalidThrift := []byte("not thrift data") compressed := snappy.Encode(nil, invalidThrift) return compressed }(), decodeFunc: func(data []byte) (interface{}, error) { return decoder.workflowExecutionInfoFromBlob(data) }, expectError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result, err := tc.decodeFunc(tc.data) if tc.expectError { assert.Error(t, err) assert.Nil(t, result) } else { assert.NoError(t, err) assert.NotNil(t, result) } }) } } func TestSnappyThriftRWDecode(t *testing.T) { // Test the low-level snappyThriftRWDecode function directly t.Run("Valid data", func(t *testing.T) { // Create a simple thrift struct and encode it encoder := newSnappyThriftEncoder() shardInfo := &ShardInfo{ StolenSinceRenew: 1, UpdatedAt: time.Now(), ReplicationAckLevel: 2, TransferAckLevel: 3, } encoded, err := encoder.shardInfoToBlob(shardInfo) require.NoError(t, err) // Now decode it back decoder := newSnappyThriftDecoder() decoded, err := decoder.shardInfoFromBlob(encoded) require.NoError(t, err) assert.Equal(t, shardInfo.StolenSinceRenew, decoded.StolenSinceRenew) assert.Equal(t, shardInfo.ReplicationAckLevel, decoded.ReplicationAckLevel) assert.Equal(t, shardInfo.TransferAckLevel, decoded.TransferAckLevel) }) t.Run("Invalid snappy compression", func(t *testing.T) { decoder := newSnappyThriftDecoder() // Test with invalid snappy data invalidData := []byte("this is not snappy compressed data") _, err := decoder.shardInfoFromBlob(invalidData) assert.Error(t, err) assert.Contains(t, err.Error(), "snappy") }) t.Run("Valid snappy but invalid thrift", func(t *testing.T) { decoder := newSnappyThriftDecoder() // Create valid snappy data but with invalid thrift content invalidThrift := []byte("not a valid thrift message") compressed := snappy.Encode(nil, invalidThrift) _, err := decoder.shardInfoFromBlob(compressed) assert.Error(t, err) }) } func TestSnappyThriftDecoderInterface(t *testing.T) { // Verify that snappyThriftDecoder implements the decoder interface var _ decoder = (*snappyThriftDecoder)(nil) // Test that newSnappyThriftDecoder returns a valid decoder decoder := newSnappyThriftDecoder() assert.NotNil(t, decoder) assert.IsType(t, &snappyThriftDecoder{}, decoder) } func TestSnappyThriftDecoderNilHandling(t *testing.T) { decoder := newSnappyThriftDecoder() // Test each method with nil data testCases := []struct { name string decodeFunc func() (interface{}, error) }{ { name: "shardInfoFromBlob with nil", decodeFunc: func() (interface{}, error) { return decoder.shardInfoFromBlob(nil) }, }, { name: "domainInfoFromBlob with nil", decodeFunc: func() (interface{}, error) { return decoder.domainInfoFromBlob(nil) }, }, { name: "activityInfoFromBlob with nil", decodeFunc: func() (interface{}, error) { return decoder.activityInfoFromBlob(nil) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result, err := tc.decodeFunc() assert.Error(t, err) assert.Nil(t, result) }) } } // Helper functions for encoding and decoding with parser func encodeWithParser(t *testing.T, parser Parser, data interface{}) []byte { var blob []byte var err error switch v := data.(type) { case *ShardInfo: db, e := parser.ShardInfoToBlob(v) require.NoError(t, e) blob = db.Data case *DomainInfo: db, e := parser.DomainInfoToBlob(v) require.NoError(t, e) blob = db.Data case *HistoryTreeInfo: db, e := parser.HistoryTreeInfoToBlob(v) require.NoError(t, e) blob = db.Data case *WorkflowExecutionInfo: db, e := parser.WorkflowExecutionInfoToBlob(v) require.NoError(t, e) blob = db.Data case *ActivityInfo: db, e := parser.ActivityInfoToBlob(v) require.NoError(t, e) blob = db.Data case *ChildExecutionInfo: db, e := parser.ChildExecutionInfoToBlob(v) require.NoError(t, e) blob = db.Data case *SignalInfo: db, e := parser.SignalInfoToBlob(v) require.NoError(t, e) blob = db.Data case *RequestCancelInfo: db, e := parser.RequestCancelInfoToBlob(v) require.NoError(t, e) blob = db.Data case *TimerInfo: db, e := parser.TimerInfoToBlob(v) require.NoError(t, e) blob = db.Data case *TaskInfo: db, e := parser.TaskInfoToBlob(v) require.NoError(t, e) blob = db.Data case *TaskListInfo: db, e := parser.TaskListInfoToBlob(v) require.NoError(t, e) blob = db.Data case *TransferTaskInfo: db, e := parser.TransferTaskInfoToBlob(v) require.NoError(t, e) blob = db.Data case *TimerTaskInfo: db, e := parser.TimerTaskInfoToBlob(v) require.NoError(t, e) blob = db.Data case *ReplicationTaskInfo: db, e := parser.ReplicationTaskInfoToBlob(v) require.NoError(t, e) blob = db.Data default: t.Fatalf("Unknown type %T", v) } require.NoError(t, err) require.NotEmpty(t, blob) return blob } func decodeWithParser(t *testing.T, parser Parser, blob []byte, data interface{}) interface{} { var result interface{} var err error encoding := string(constants.EncodingTypeThriftRWSnappy) switch data.(type) { case *ShardInfo: result, err = parser.ShardInfoFromBlob(blob, encoding) case *DomainInfo: result, err = parser.DomainInfoFromBlob(blob, encoding) case *HistoryTreeInfo: result, err = parser.HistoryTreeInfoFromBlob(blob, encoding) case *WorkflowExecutionInfo: result, err = parser.WorkflowExecutionInfoFromBlob(blob, encoding) case *ActivityInfo: result, err = parser.ActivityInfoFromBlob(blob, encoding) case *ChildExecutionInfo: result, err = parser.ChildExecutionInfoFromBlob(blob, encoding) case *SignalInfo: result, err = parser.SignalInfoFromBlob(blob, encoding) case *RequestCancelInfo: result, err = parser.RequestCancelInfoFromBlob(blob, encoding) case *TimerInfo: result, err = parser.TimerInfoFromBlob(blob, encoding) case *TaskInfo: result, err = parser.TaskInfoFromBlob(blob, encoding) case *TaskListInfo: result, err = parser.TaskListInfoFromBlob(blob, encoding) case *TransferTaskInfo: result, err = parser.TransferTaskInfoFromBlob(blob, encoding) case *TimerTaskInfo: result, err = parser.TimerTaskInfoFromBlob(blob, encoding) case *ReplicationTaskInfo: result, err = parser.ReplicationTaskInfoFromBlob(blob, encoding) default: t.Fatalf("Unknown type %T", data) } require.NoError(t, err) require.NotNil(t, result) return result } ================================================ FILE: common/persistence/serialization/snappy_thrift_encoder.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "bytes" "github.com/golang/snappy" "go.uber.org/thriftrw/protocol/binary" "github.com/uber/cadence/common/constants" ) type snappyThriftEncoder struct{} func newSnappyThriftEncoder() encoder { return &snappyThriftEncoder{} } func (e *snappyThriftEncoder) shardInfoToBlob(info *ShardInfo) ([]byte, error) { return snappyThriftRWEncode(shardInfoToThrift(info)) } func (e *snappyThriftEncoder) domainInfoToBlob(info *DomainInfo) ([]byte, error) { return snappyThriftRWEncode(domainInfoToThrift(info)) } func (e *snappyThriftEncoder) historyTreeInfoToBlob(info *HistoryTreeInfo) ([]byte, error) { return snappyThriftRWEncode(historyTreeInfoToThrift(info)) } func (e *snappyThriftEncoder) workflowExecutionInfoToBlob(info *WorkflowExecutionInfo) ([]byte, error) { return snappyThriftRWEncode(workflowExecutionInfoToThrift(info)) } func (e *snappyThriftEncoder) activityInfoToBlob(info *ActivityInfo) ([]byte, error) { return snappyThriftRWEncode(activityInfoToThrift(info)) } func (e *snappyThriftEncoder) childExecutionInfoToBlob(info *ChildExecutionInfo) ([]byte, error) { return snappyThriftRWEncode(childExecutionInfoToThrift(info)) } func (e *snappyThriftEncoder) signalInfoToBlob(info *SignalInfo) ([]byte, error) { return snappyThriftRWEncode(signalInfoToThrift(info)) } func (e *snappyThriftEncoder) requestCancelInfoToBlob(info *RequestCancelInfo) ([]byte, error) { return snappyThriftRWEncode(requestCancelInfoToThrift(info)) } func (e *snappyThriftEncoder) timerInfoToBlob(info *TimerInfo) ([]byte, error) { return snappyThriftRWEncode(timerInfoToThrift(info)) } func (e *snappyThriftEncoder) taskInfoToBlob(info *TaskInfo) ([]byte, error) { return snappyThriftRWEncode(taskInfoToThrift(info)) } func (e *snappyThriftEncoder) taskListInfoToBlob(info *TaskListInfo) ([]byte, error) { return snappyThriftRWEncode(taskListInfoToThrift(info)) } func (e *snappyThriftEncoder) transferTaskInfoToBlob(info *TransferTaskInfo) ([]byte, error) { return snappyThriftRWEncode(transferTaskInfoToThrift(info)) } func (e *snappyThriftEncoder) crossClusterTaskInfoToBlob(info *CrossClusterTaskInfo) ([]byte, error) { return snappyThriftRWEncode(crossClusterTaskInfoToThrift(info)) } func (e *snappyThriftEncoder) timerTaskInfoToBlob(info *TimerTaskInfo) ([]byte, error) { return snappyThriftRWEncode(timerTaskInfoToThrift(info)) } func (e *snappyThriftEncoder) replicationTaskInfoToBlob(info *ReplicationTaskInfo) ([]byte, error) { return snappyThriftRWEncode(replicationTaskInfoToThrift(info)) } func (e *snappyThriftEncoder) encodingType() constants.EncodingType { return constants.EncodingTypeThriftRWSnappy } func snappyThriftRWEncode(t thriftRWType) ([]byte, error) { var b bytes.Buffer sw := binary.Default.Writer(&b) defer sw.Close() if err := t.Encode(sw); err != nil { return nil, err } return snappy.Encode(nil, b.Bytes()), nil } ================================================ FILE: common/persistence/serialization/snappy_thrift_encoder_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "fmt" "testing" "github.com/golang/snappy" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" ) func TestSnappyThriftEncoderRoundTrip(t *testing.T) { encoder := newSnappyThriftEncoder() decoder := newSnappyThriftDecoder() testCases := []struct { name string data interface{} encodeFunc func(interface{}) ([]byte, error) decodeFunc func([]byte) (interface{}, error) }{ { name: "ShardInfo", data: shardInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.shardInfoToBlob(data.(*ShardInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.shardInfoFromBlob(data) }, }, { name: "DomainInfo", data: domainInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.domainInfoToBlob(data.(*DomainInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.domainInfoFromBlob(data) }, }, { name: "HistoryTreeInfo", data: historyTreeInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.historyTreeInfoToBlob(data.(*HistoryTreeInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.historyTreeInfoFromBlob(data) }, }, { name: "WorkflowExecutionInfo", data: workflowExecutionInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.workflowExecutionInfoToBlob(data.(*WorkflowExecutionInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.workflowExecutionInfoFromBlob(data) }, }, { name: "ActivityInfo", data: activityInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.activityInfoToBlob(data.(*ActivityInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.activityInfoFromBlob(data) }, }, { name: "ChildExecutionInfo", data: childExecutionInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.childExecutionInfoToBlob(data.(*ChildExecutionInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.childExecutionInfoFromBlob(data) }, }, { name: "SignalInfo", data: signalInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.signalInfoToBlob(data.(*SignalInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.signalInfoFromBlob(data) }, }, { name: "RequestCancelInfo", data: requestCancelInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.requestCancelInfoToBlob(data.(*RequestCancelInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.requestCancelInfoFromBlob(data) }, }, { name: "TimerInfo", data: timerInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.timerInfoToBlob(data.(*TimerInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.timerInfoFromBlob(data) }, }, { name: "TaskInfo", data: taskInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.taskInfoToBlob(data.(*TaskInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.taskInfoFromBlob(data) }, }, { name: "TaskListInfo", data: taskListInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.taskListInfoToBlob(data.(*TaskListInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.taskListInfoFromBlob(data) }, }, { name: "TransferTaskInfo", data: transferTaskInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.transferTaskInfoToBlob(data.(*TransferTaskInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.transferTaskInfoFromBlob(data) }, }, { name: "TimerTaskInfo", data: timerTaskInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.timerTaskInfoToBlob(data.(*TimerTaskInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.timerTaskInfoFromBlob(data) }, }, { name: "ReplicationTaskInfo", data: replicationTaskInfoTestData, encodeFunc: func(data interface{}) ([]byte, error) { return encoder.replicationTaskInfoToBlob(data.(*ReplicationTaskInfo)) }, decodeFunc: func(data []byte) (interface{}, error) { return decoder.replicationTaskInfoFromBlob(data) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Encode the data using the encoder encoded, err := tc.encodeFunc(tc.data) require.NoError(t, err) require.NotEmpty(t, encoded) // Verify the data is snappy compressed assert.True(t, isValidSnappyData(encoded), "encoded data should be valid snappy compressed data") // Decode the data using the decoder and verify it matches decoded, err := tc.decodeFunc(encoded) require.NoError(t, err) assert.Equal(t, tc.data, decoded) }) } } func TestSnappyThriftEncoderEncodingType(t *testing.T) { encoder := newSnappyThriftEncoder() encodingType := encoder.encodingType() assert.Equal(t, constants.EncodingTypeThriftRWSnappy, encodingType) } func TestSnappyThriftEncoderInterface(t *testing.T) { // Verify that snappyThriftEncoder implements the encoder interface var _ encoder = (*snappyThriftEncoder)(nil) // Test that newSnappyThriftEncoder returns a valid encoder encoder := newSnappyThriftEncoder() assert.NotNil(t, encoder) assert.IsType(t, &snappyThriftEncoder{}, encoder) } func TestSnappyThriftEncoderNilHandling(t *testing.T) { encoder := newSnappyThriftEncoder() // Test each method with nil data testCases := []struct { name string encodeFunc func() ([]byte, error) }{ { name: "shardInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.shardInfoToBlob(nil) }, }, { name: "domainInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.domainInfoToBlob(nil) }, }, { name: "activityInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.activityInfoToBlob(nil) }, }, { name: "workflowExecutionInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.workflowExecutionInfoToBlob(nil) }, }, { name: "childExecutionInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.childExecutionInfoToBlob(nil) }, }, { name: "signalInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.signalInfoToBlob(nil) }, }, { name: "requestCancelInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.requestCancelInfoToBlob(nil) }, }, { name: "timerInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.timerInfoToBlob(nil) }, }, { name: "taskInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.taskInfoToBlob(nil) }, }, { name: "taskListInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.taskListInfoToBlob(nil) }, }, { name: "transferTaskInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.transferTaskInfoToBlob(nil) }, }, { name: "crossClusterTaskInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.crossClusterTaskInfoToBlob(nil) }, }, { name: "timerTaskInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.timerTaskInfoToBlob(nil) }, }, { name: "replicationTaskInfoToBlob with nil", encodeFunc: func() ([]byte, error) { return encoder.replicationTaskInfoToBlob(nil) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var result []byte var err error var panicRecovered bool // Use defer/recover to handle panic as expected behavior for nil input func() { defer func() { if r := recover(); r != nil { panicRecovered = true } }() result, err = tc.encodeFunc() }() // Nil input should either produce an error, panic, or valid empty data if panicRecovered { // Panic is expected for nil input assert.True(t, true, "nil input caused expected panic") } else if err != nil { assert.Error(t, err) assert.Nil(t, result) } else { // If no error, result should be valid snappy data assert.NotNil(t, result) assert.True(t, isValidSnappyData(result), "nil input should produce valid snappy data") } }) } } func TestSnappyThriftRWEncode(t *testing.T) { // Test the low-level snappyThriftRWEncode function directly t.Run("Valid thrift object", func(t *testing.T) { // Create a simple thrift struct and encode it shardInfo := shardInfoTestData // Convert to thrift format thriftStruct := shardInfoToThrift(shardInfo) // Encode using the low-level function encoded, err := snappyThriftRWEncode(thriftStruct) require.NoError(t, err) require.NotEmpty(t, encoded) // Verify it's valid snappy data assert.True(t, isValidSnappyData(encoded)) // Verify we can decode it back decoder := newSnappyThriftDecoder() decoded, err := decoder.shardInfoFromBlob(encoded) require.NoError(t, err) assert.Equal(t, shardInfo.StolenSinceRenew, decoded.StolenSinceRenew) assert.Equal(t, shardInfo.ReplicationAckLevel, decoded.ReplicationAckLevel) assert.Equal(t, shardInfo.TransferAckLevel, decoded.TransferAckLevel) }) t.Run("Nil thrift object", func(t *testing.T) { // Test with nil thrift object (converted from nil input) thriftStruct := shardInfoToThrift(nil) assert.Nil(t, thriftStruct) var err error var panicRecovered bool // Use defer/recover to handle panic as expected behavior for nil input func() { defer func() { if r := recover(); r != nil { panicRecovered = true } }() _, err = snappyThriftRWEncode(thriftStruct) }() // Either panic or error is acceptable for nil input if panicRecovered { assert.True(t, true, "nil thrift object caused expected panic") } else if err != nil { assert.Error(t, err) } }) } func TestSnappyThriftEncoderWithParser(t *testing.T) { dc := &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRWSnappy)), } // Test encoder integration with parser parser, err := NewParser(dc) require.NoError(t, err) testData := shardInfoTestData // Encode using parser blob, err := parser.ShardInfoToBlob(testData) require.NoError(t, err) assert.Equal(t, constants.EncodingTypeThriftRWSnappy, blob.Encoding) assert.NotEmpty(t, blob.Data) assert.True(t, isValidSnappyData(blob.Data)) // Decode using parser decoded, err := parser.ShardInfoFromBlob(blob.Data, string(blob.Encoding)) require.NoError(t, err) assert.Equal(t, testData, decoded) } func TestSnappyThriftEncoderDataCompression(t *testing.T) { encoder := newSnappyThriftEncoder() // Create a large data structure to test compression largeData := &WorkflowExecutionInfo{ WorkflowTypeName: "very_long_workflow_type_name_that_should_compress_well_when_repeated", TaskList: "very_long_task_list_name_that_should_compress_well_when_repeated", ExecutionContext: make([]byte, 1000), // Large byte array SearchAttributes: make(map[string][]byte), Memo: make(map[string][]byte), } // Fill with repetitive data that should compress well for i := 0; i < 100; i++ { key := fmt.Sprintf("repetitive_key_that_compresses_well_%d", i) value := []byte("repetitive_value_that_compresses_well_when_repeated_many_times") largeData.SearchAttributes[key] = value largeData.Memo[key] = value } // Encode the data encoded, err := encoder.workflowExecutionInfoToBlob(largeData) require.NoError(t, err) require.NotEmpty(t, encoded) // Verify it's compressed (should be significantly smaller than uncompressed) assert.True(t, isValidSnappyData(encoded)) // Decode and verify correctness decoder := newSnappyThriftDecoder() decoded, err := decoder.workflowExecutionInfoFromBlob(encoded) require.NoError(t, err) assert.Equal(t, largeData.WorkflowTypeName, decoded.WorkflowTypeName) assert.Equal(t, largeData.TaskList, decoded.TaskList) assert.Len(t, decoded.SearchAttributes, 100) assert.Len(t, decoded.Memo, 100) } // Helper function to check if data is valid snappy compressed data func isValidSnappyData(data []byte) bool { _, err := snappy.Decode(nil, data) return err == nil } ================================================ FILE: common/persistence/serialization/task_serializer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination task_serializer_mock.go github.com/uber/cadence/common/persistence/serialization TaskSerializer package serialization import ( "fmt" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( TaskSerializer interface { SerializeTask(persistence.HistoryTaskCategory, persistence.Task) (persistence.DataBlob, error) DeserializeTask(persistence.HistoryTaskCategory, *persistence.DataBlob) (persistence.Task, error) } taskSerializerImpl struct { parser Parser } ) func NewTaskSerializer(parser Parser) TaskSerializer { return &taskSerializerImpl{ parser: parser, } } func (s *taskSerializerImpl) SerializeTask(category persistence.HistoryTaskCategory, task persistence.Task) (persistence.DataBlob, error) { switch category.ID() { case persistence.HistoryTaskCategoryIDTransfer: return s.serializeTransferTask(task) case persistence.HistoryTaskCategoryIDTimer: return s.serializeTimerTask(task) case persistence.HistoryTaskCategoryIDReplication: return s.serializeReplicationTask(task) default: return persistence.DataBlob{}, fmt.Errorf("unknown category ID: %v", category.ID()) } } func (s *taskSerializerImpl) DeserializeTask(category persistence.HistoryTaskCategory, blob *persistence.DataBlob) (persistence.Task, error) { switch category.ID() { case persistence.HistoryTaskCategoryIDTransfer: return s.deserializeTransferTask(blob) case persistence.HistoryTaskCategoryIDTimer: return s.deserializeTimerTask(blob) case persistence.HistoryTaskCategoryIDReplication: return s.deserializeReplicationTask(blob) default: return nil, fmt.Errorf("unknown category ID: %v", category.ID()) } } func (s *taskSerializerImpl) serializeTransferTask(task persistence.Task) (persistence.DataBlob, error) { info := &TransferTaskInfo{ TaskType: int16(task.GetTaskType()), TargetWorkflowID: persistence.TransferTaskTransferTargetWorkflowID, Version: task.GetVersion(), VisibilityTimestamp: task.GetVisibilityTimestamp(), } switch t := task.(type) { case *persistence.ActivityTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TargetDomainID = MustParseUUID(t.TargetDomainID) info.TaskList = t.TaskList info.ScheduleID = t.ScheduleID case *persistence.DecisionTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TargetDomainID = MustParseUUID(t.TargetDomainID) info.TaskList = t.TaskList info.ScheduleID = t.ScheduleID info.OriginalTaskList = t.OriginalTaskList info.OriginalTaskListKind = t.OriginalTaskListKind case *persistence.CancelExecutionTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TargetDomainID = MustParseUUID(t.TargetDomainID) info.TargetWorkflowID = t.TargetWorkflowID if t.TargetRunID != "" { info.TargetRunID = MustParseUUID(t.TargetRunID) } info.TargetChildWorkflowOnly = t.TargetChildWorkflowOnly info.ScheduleID = t.InitiatedID info.TaskList = t.TaskList case *persistence.SignalExecutionTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TargetDomainID = MustParseUUID(t.TargetDomainID) info.TargetWorkflowID = t.TargetWorkflowID if t.TargetRunID != "" { info.TargetRunID = MustParseUUID(t.TargetRunID) } info.TargetChildWorkflowOnly = t.TargetChildWorkflowOnly info.ScheduleID = t.InitiatedID info.TaskList = t.TaskList case *persistence.StartChildExecutionTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TargetDomainID = MustParseUUID(t.TargetDomainID) info.TargetWorkflowID = t.TargetWorkflowID info.ScheduleID = t.InitiatedID info.TaskList = t.TaskList case *persistence.RecordChildExecutionCompletedTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TargetDomainID = MustParseUUID(t.TargetDomainID) info.TargetWorkflowID = t.TargetWorkflowID if t.TargetRunID != "" { info.TargetRunID = MustParseUUID(t.TargetRunID) } info.TaskList = t.TaskList case *persistence.CloseExecutionTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TaskList = t.TaskList case *persistence.RecordWorkflowStartedTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TaskList = t.TaskList case *persistence.RecordWorkflowClosedTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TaskList = t.TaskList case *persistence.ResetWorkflowTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TaskList = t.TaskList case *persistence.UpsertWorkflowSearchAttributesTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TaskList = t.TaskList default: return persistence.DataBlob{}, &types.InternalServiceError{ Message: fmt.Sprintf("Unknown transfer type: %v", task.GetTaskType()), } } return s.parser.TransferTaskInfoToBlob(info) } func (s *taskSerializerImpl) deserializeTransferTask(blob *persistence.DataBlob) (persistence.Task, error) { info, err := s.parser.TransferTaskInfoFromBlob(blob.Data, string(blob.Encoding)) if err != nil { return nil, err } var task persistence.Task workflowIdentifier := persistence.WorkflowIdentifier{ DomainID: info.DomainID.String(), WorkflowID: info.GetWorkflowID(), RunID: info.RunID.String(), } taskData := persistence.TaskData{ Version: info.GetVersion(), VisibilityTimestamp: info.GetVisibilityTimestamp(), } switch info.GetTaskType() { case persistence.TransferTaskTypeDecisionTask: task = &persistence.DecisionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: info.TargetDomainID.String(), TaskList: info.GetTaskList(), ScheduleID: info.GetScheduleID(), OriginalTaskList: info.GetOriginalTaskList(), OriginalTaskListKind: info.GetOriginalTaskListKind(), } case persistence.TransferTaskTypeActivityTask: task = &persistence.ActivityTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: info.TargetDomainID.String(), TaskList: info.GetTaskList(), ScheduleID: info.GetScheduleID(), } case persistence.TransferTaskTypeCloseExecution: task = &persistence.CloseExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: info.GetTaskList(), } case persistence.TransferTaskTypeCancelExecution: task = &persistence.CancelExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: info.TargetDomainID.String(), TargetWorkflowID: info.GetTargetWorkflowID(), TargetRunID: info.TargetRunID.String(), TargetChildWorkflowOnly: info.GetTargetChildWorkflowOnly(), InitiatedID: info.GetScheduleID(), TaskList: info.GetTaskList(), } case persistence.TransferTaskTypeStartChildExecution: task = &persistence.StartChildExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: info.TargetDomainID.String(), TargetWorkflowID: info.GetTargetWorkflowID(), InitiatedID: info.GetScheduleID(), TaskList: info.GetTaskList(), } case persistence.TransferTaskTypeSignalExecution: task = &persistence.SignalExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: info.TargetDomainID.String(), TargetWorkflowID: info.GetTargetWorkflowID(), TargetRunID: info.TargetRunID.String(), TargetChildWorkflowOnly: info.GetTargetChildWorkflowOnly(), InitiatedID: info.GetScheduleID(), TaskList: info.GetTaskList(), } case persistence.TransferTaskTypeRecordWorkflowStarted: task = &persistence.RecordWorkflowStartedTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: info.GetTaskList(), } case persistence.TransferTaskTypeResetWorkflow: task = &persistence.ResetWorkflowTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: info.GetTaskList(), } case persistence.TransferTaskTypeUpsertWorkflowSearchAttributes: task = &persistence.UpsertWorkflowSearchAttributesTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: info.GetTaskList(), } case persistence.TransferTaskTypeRecordWorkflowClosed: task = &persistence.RecordWorkflowClosedTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: info.GetTaskList(), } case persistence.TransferTaskTypeRecordChildExecutionCompleted: task = &persistence.RecordChildExecutionCompletedTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TargetDomainID: info.TargetDomainID.String(), TargetWorkflowID: info.GetTargetWorkflowID(), TargetRunID: info.TargetRunID.String(), TaskList: info.GetTaskList(), } default: return nil, fmt.Errorf("unknown transfer task type: %v", info.GetTaskType()) } return task, nil } func (s *taskSerializerImpl) serializeTimerTask(task persistence.Task) (persistence.DataBlob, error) { info := &TimerTaskInfo{ TaskType: int16(task.GetTaskType()), Version: task.GetVersion(), EventID: constants.EmptyEventID, } switch t := task.(type) { case *persistence.DecisionTimeoutTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.EventID = t.EventID info.TimeoutType = common.Int16Ptr(int16(t.TimeoutType)) info.ScheduleAttempt = t.ScheduleAttempt info.TaskList = t.TaskList case *persistence.ActivityTimeoutTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.EventID = t.EventID info.TimeoutType = common.Int16Ptr(int16(t.TimeoutType)) info.ScheduleAttempt = t.Attempt info.TaskList = t.TaskList case *persistence.UserTimerTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.EventID = t.EventID info.TaskList = t.TaskList case *persistence.ActivityRetryTimerTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.EventID = t.EventID info.ScheduleAttempt = int64(t.Attempt) info.TaskList = t.TaskList case *persistence.WorkflowBackoffTimerTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TimeoutType = common.Int16Ptr(int16(t.TimeoutType)) info.TaskList = t.TaskList case *persistence.WorkflowTimeoutTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TaskList = t.TaskList case *persistence.DeleteHistoryEventTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.TaskList = t.TaskList default: return persistence.DataBlob{}, &types.InternalServiceError{ Message: fmt.Sprintf("Unknown timer task: %v", task.GetTaskType()), } } return s.parser.TimerTaskInfoToBlob(info) } func (s *taskSerializerImpl) deserializeTimerTask(blob *persistence.DataBlob) (persistence.Task, error) { info, err := s.parser.TimerTaskInfoFromBlob(blob.Data, string(blob.Encoding)) if err != nil { return nil, err } var task persistence.Task workflowIdentifier := persistence.WorkflowIdentifier{ DomainID: info.DomainID.String(), WorkflowID: info.GetWorkflowID(), RunID: info.RunID.String(), } taskData := persistence.TaskData{ Version: info.GetVersion(), } switch info.GetTaskType() { case persistence.TaskTypeDecisionTimeout: task = &persistence.DecisionTimeoutTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, EventID: info.GetEventID(), ScheduleAttempt: info.GetScheduleAttempt(), TimeoutType: int(info.GetTimeoutType()), TaskList: info.GetTaskList(), } case persistence.TaskTypeActivityTimeout: task = &persistence.ActivityTimeoutTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, EventID: info.GetEventID(), Attempt: info.GetScheduleAttempt(), TimeoutType: int(info.GetTimeoutType()), TaskList: info.GetTaskList(), } case persistence.TaskTypeUserTimer: task = &persistence.UserTimerTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, EventID: info.GetEventID(), TaskList: info.GetTaskList(), } case persistence.TaskTypeWorkflowTimeout: task = &persistence.WorkflowTimeoutTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: info.GetTaskList(), } case persistence.TaskTypeDeleteHistoryEvent: task = &persistence.DeleteHistoryEventTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TaskList: info.GetTaskList(), } case persistence.TaskTypeActivityRetryTimer: task = &persistence.ActivityRetryTimerTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, EventID: info.GetEventID(), Attempt: info.GetScheduleAttempt(), TaskList: info.GetTaskList(), } case persistence.TaskTypeWorkflowBackoffTimer: task = &persistence.WorkflowBackoffTimerTask{ WorkflowIdentifier: workflowIdentifier, TaskData: taskData, TimeoutType: int(info.GetTimeoutType()), TaskList: info.GetTaskList(), } default: return nil, fmt.Errorf("unknown timer task type: %v", info.GetTaskType()) } return task, nil } func (s *taskSerializerImpl) serializeReplicationTask(task persistence.Task) (persistence.DataBlob, error) { info := &ReplicationTaskInfo{ TaskType: int16(task.GetTaskType()), FirstEventID: constants.EmptyEventID, NextEventID: constants.EmptyEventID, Version: task.GetVersion(), ScheduledID: constants.EmptyEventID, EventStoreVersion: persistence.EventStoreVersion, NewRunEventStoreVersion: persistence.EventStoreVersion, CreationTimestamp: task.GetVisibilityTimestamp(), } switch t := task.(type) { case *persistence.HistoryReplicationTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.FirstEventID = t.FirstEventID info.NextEventID = t.NextEventID info.BranchToken = t.BranchToken info.NewRunBranchToken = t.NewRunBranchToken case *persistence.SyncActivityTask: info.DomainID = MustParseUUID(t.DomainID) info.WorkflowID = t.WorkflowID info.RunID = MustParseUUID(t.RunID) info.ScheduledID = t.ScheduledID case *persistence.FailoverMarkerTask: info.DomainID = MustParseUUID(t.DomainID) default: return persistence.DataBlob{}, &types.InternalServiceError{ Message: fmt.Sprintf("Unknown replication task: %v", task.GetTaskType()), } } return s.parser.ReplicationTaskInfoToBlob(info) } func (s *taskSerializerImpl) deserializeReplicationTask(blob *persistence.DataBlob) (persistence.Task, error) { info, err := s.parser.ReplicationTaskInfoFromBlob(blob.Data, string(blob.Encoding)) if err != nil { return nil, err } var task persistence.Task taskData := persistence.TaskData{ Version: info.GetVersion(), VisibilityTimestamp: info.GetCreationTimestamp(), } switch info.GetTaskType() { case persistence.ReplicationTaskTypeHistory: task = &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: info.DomainID.String(), WorkflowID: info.GetWorkflowID(), RunID: info.RunID.String(), }, TaskData: taskData, FirstEventID: info.GetFirstEventID(), NextEventID: info.GetNextEventID(), BranchToken: info.BranchToken, NewRunBranchToken: info.NewRunBranchToken, } case persistence.ReplicationTaskTypeSyncActivity: task = &persistence.SyncActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: info.DomainID.String(), WorkflowID: info.GetWorkflowID(), RunID: info.RunID.String(), }, TaskData: taskData, ScheduledID: info.GetScheduledID(), } case persistence.ReplicationTaskTypeFailoverMarker: task = &persistence.FailoverMarkerTask{ DomainID: info.DomainID.String(), TaskData: taskData, } } return task, nil } ================================================ FILE: common/persistence/serialization/task_serializer_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/persistence/serialization (interfaces: TaskSerializer) // // Generated by this command: // // mockgen -package serialization -destination task_serializer_mock.go github.com/uber/cadence/common/persistence/serialization TaskSerializer // // Package serialization is a generated GoMock package. package serialization import ( reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" ) // MockTaskSerializer is a mock of TaskSerializer interface. type MockTaskSerializer struct { ctrl *gomock.Controller recorder *MockTaskSerializerMockRecorder isgomock struct{} } // MockTaskSerializerMockRecorder is the mock recorder for MockTaskSerializer. type MockTaskSerializerMockRecorder struct { mock *MockTaskSerializer } // NewMockTaskSerializer creates a new mock instance. func NewMockTaskSerializer(ctrl *gomock.Controller) *MockTaskSerializer { mock := &MockTaskSerializer{ctrl: ctrl} mock.recorder = &MockTaskSerializerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskSerializer) EXPECT() *MockTaskSerializerMockRecorder { return m.recorder } // DeserializeTask mocks base method. func (m *MockTaskSerializer) DeserializeTask(arg0 persistence.HistoryTaskCategory, arg1 *persistence.DataBlob) (persistence.Task, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeTask", arg0, arg1) ret0, _ := ret[0].(persistence.Task) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeTask indicates an expected call of DeserializeTask. func (mr *MockTaskSerializerMockRecorder) DeserializeTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeTask", reflect.TypeOf((*MockTaskSerializer)(nil).DeserializeTask), arg0, arg1) } // SerializeTask mocks base method. func (m *MockTaskSerializer) SerializeTask(arg0 persistence.HistoryTaskCategory, arg1 persistence.Task) (persistence.DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeTask", arg0, arg1) ret0, _ := ret[0].(persistence.DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeTask indicates an expected call of SerializeTask. func (mr *MockTaskSerializerMockRecorder) SerializeTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeTask", reflect.TypeOf((*MockTaskSerializer)(nil).SerializeTask), arg0, arg1) } ================================================ FILE: common/persistence/serialization/task_serializer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" ) func TestTaskSerializerThriftRW(t *testing.T) { dc := &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), } parser, err := NewParser(dc) require.NoError(t, err) taskSerializer := NewTaskSerializer(parser) workflowIdentifier := persistence.WorkflowIdentifier{ DomainID: "8be8a310-7d20-483e-a5d2-48659dc47602", WorkflowID: "test-workflow", RunID: "4be8a310-7d20-483e-a5d2-48659dc47609", } testCases := []struct { category persistence.HistoryTaskCategory task persistence.Task }{ { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.DecisionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: time.Unix(1, 1), }, TargetDomainID: "0be8a310-7d20-483e-a5d2-48659dc47602", TaskList: "test-tl", ScheduleID: 1, }, }, { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.ActivityTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 2, TaskID: 2, VisibilityTimestamp: time.Unix(2, 2), }, TargetDomainID: "2be8a310-7d20-483e-a5d2-48659dc47602", TaskList: "test-tl2", ScheduleID: 2, }, }, { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.CloseExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 3, TaskID: 3, VisibilityTimestamp: time.Unix(3, 3), }, }, }, { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.CancelExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 4, TaskID: 4, VisibilityTimestamp: time.Unix(4, 4), }, TargetDomainID: "3be8a310-7d20-483e-a5d2-48659dc47602", TargetWorkflowID: "target-wf", TargetRunID: "5be8a310-7d20-483e-a5d2-48659dc47609", TargetChildWorkflowOnly: true, InitiatedID: 4, }, }, { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.StartChildExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 5, TaskID: 5, VisibilityTimestamp: time.Unix(5, 5), }, TargetDomainID: "3be8a310-7d20-483e-a5d2-48659dc47602", TargetWorkflowID: "target-wf", InitiatedID: 5, }, }, { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.SignalExecutionTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 5, TaskID: 5, VisibilityTimestamp: time.Unix(5, 5), }, TargetDomainID: "4be8a310-7d20-483e-a5d2-48659dc47608", TargetWorkflowID: "target-wf2", TargetRunID: "7be8a310-7d20-483e-a5d2-48659dc47606", TargetChildWorkflowOnly: true, InitiatedID: 5, }, }, { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.RecordWorkflowStartedTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 6, TaskID: 6, VisibilityTimestamp: time.Unix(6, 6), }, }, }, { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.ResetWorkflowTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 7, TaskID: 7, VisibilityTimestamp: time.Unix(7, 7), }, }, }, { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.UpsertWorkflowSearchAttributesTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 8, TaskID: 8, VisibilityTimestamp: time.Unix(8, 8), }, }, }, { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.RecordWorkflowClosedTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 9, TaskID: 9, VisibilityTimestamp: time.Unix(9, 9), }, }, }, { category: persistence.HistoryTaskCategoryTransfer, task: &persistence.RecordChildExecutionCompletedTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 10, TaskID: 10, VisibilityTimestamp: time.Unix(10, 10), }, TargetDomainID: "7be8a310-7d20-483e-a5d2-48659dc47608", TargetWorkflowID: "target-wf2", TargetRunID: "2be8a310-7d20-483e-a5d2-48659dc47606", }, }, { category: persistence.HistoryTaskCategoryTimer, task: &persistence.DecisionTimeoutTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 11, TaskID: 11, VisibilityTimestamp: time.Unix(11, 11), }, EventID: 11, ScheduleAttempt: 11, TimeoutType: 11, }, }, { category: persistence.HistoryTaskCategoryTimer, task: &persistence.ActivityTimeoutTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 12, TaskID: 12, VisibilityTimestamp: time.Unix(12, 12), }, EventID: 12, Attempt: 12, TimeoutType: 12, }, }, { category: persistence.HistoryTaskCategoryTimer, task: &persistence.UserTimerTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 13, TaskID: 13, VisibilityTimestamp: time.Unix(13, 13), }, EventID: 13, }, }, { category: persistence.HistoryTaskCategoryTimer, task: &persistence.WorkflowTimeoutTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 14, TaskID: 14, VisibilityTimestamp: time.Unix(14, 14), }, }, }, { category: persistence.HistoryTaskCategoryTimer, task: &persistence.DeleteHistoryEventTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 15, TaskID: 15, VisibilityTimestamp: time.Unix(15, 15), }, }, }, { category: persistence.HistoryTaskCategoryTimer, task: &persistence.ActivityRetryTimerTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 16, TaskID: 16, VisibilityTimestamp: time.Unix(16, 16), }, EventID: 16, Attempt: 16, }, }, { category: persistence.HistoryTaskCategoryTimer, task: &persistence.WorkflowBackoffTimerTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 17, TaskID: 17, VisibilityTimestamp: time.Unix(17, 17), }, TimeoutType: 17, }, }, { category: persistence.HistoryTaskCategoryReplication, task: &persistence.HistoryReplicationTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 18, TaskID: 18, VisibilityTimestamp: time.Unix(18, 18), }, FirstEventID: 18, NextEventID: 18, BranchToken: []byte("18"), NewRunBranchToken: []byte("180"), }, }, { category: persistence.HistoryTaskCategoryReplication, task: &persistence.SyncActivityTask{ WorkflowIdentifier: workflowIdentifier, TaskData: persistence.TaskData{ Version: 19, TaskID: 19, VisibilityTimestamp: time.Unix(19, 19), }, ScheduledID: 19, }, }, { category: persistence.HistoryTaskCategoryReplication, task: &persistence.FailoverMarkerTask{ DomainID: "4be8a310-7d20-483e-a5d2-48659dc47608", TaskData: persistence.TaskData{ Version: 20, TaskID: 20, VisibilityTimestamp: time.Unix(20, 20), }, }, }, } for _, tc := range testCases { blob, err := taskSerializer.SerializeTask(tc.category, tc.task) assert.NoError(t, err) task, err := taskSerializer.DeserializeTask(tc.category, &blob) assert.NoError(t, err) task.SetTaskID(tc.task.GetTaskID()) if tc.category.Type() == persistence.HistoryTaskCategoryTypeScheduled { task.SetVisibilityTimestamp(tc.task.GetVisibilityTimestamp()) } assert.Equal(t, tc.task, task) } } ================================================ FILE: common/persistence/serialization/thrift_decoder.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "bytes" "go.uber.org/thriftrw/protocol/binary" "github.com/uber/cadence/.gen/go/sqlblobs" ) type ( thriftDecoder struct{} ) func newThriftDecoder() decoder { return &thriftDecoder{} } func (d *thriftDecoder) shardInfoFromBlob(data []byte) (*ShardInfo, error) { result := &sqlblobs.ShardInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return shardInfoFromThrift(result), nil } func (d *thriftDecoder) domainInfoFromBlob(data []byte) (*DomainInfo, error) { result := &sqlblobs.DomainInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return domainInfoFromThrift(result), nil } func (d *thriftDecoder) historyTreeInfoFromBlob(data []byte) (*HistoryTreeInfo, error) { result := &sqlblobs.HistoryTreeInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return historyTreeInfoFromThrift(result), nil } func (d *thriftDecoder) workflowExecutionInfoFromBlob(data []byte) (*WorkflowExecutionInfo, error) { result := &sqlblobs.WorkflowExecutionInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return workflowExecutionInfoFromThrift(result), nil } func (d *thriftDecoder) activityInfoFromBlob(data []byte) (*ActivityInfo, error) { result := &sqlblobs.ActivityInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return activityInfoFromThrift(result), nil } func (d *thriftDecoder) childExecutionInfoFromBlob(data []byte) (*ChildExecutionInfo, error) { result := &sqlblobs.ChildExecutionInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return childExecutionInfoFromThrift(result), nil } func (d *thriftDecoder) signalInfoFromBlob(data []byte) (*SignalInfo, error) { result := &sqlblobs.SignalInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return signalInfoFromThrift(result), nil } func (d *thriftDecoder) requestCancelInfoFromBlob(data []byte) (*RequestCancelInfo, error) { result := &sqlblobs.RequestCancelInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return requestCancelInfoFromThrift(result), nil } func (d *thriftDecoder) timerInfoFromBlob(data []byte) (*TimerInfo, error) { result := &sqlblobs.TimerInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return timerInfoFromThrift(result), nil } func (d *thriftDecoder) taskInfoFromBlob(data []byte) (*TaskInfo, error) { result := &sqlblobs.TaskInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return taskInfoFromThrift(result), nil } func (d *thriftDecoder) taskListInfoFromBlob(data []byte) (*TaskListInfo, error) { result := &sqlblobs.TaskListInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return taskListInfoFromThrift(result), nil } func (d *thriftDecoder) transferTaskInfoFromBlob(data []byte) (*TransferTaskInfo, error) { result := &sqlblobs.TransferTaskInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return transferTaskInfoFromThrift(result), nil } func (d *thriftDecoder) crossClusterTaskInfoFromBlob(data []byte) (*CrossClusterTaskInfo, error) { result := &sqlblobsCrossClusterTaskInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return crossClusterTaskInfoFromThrift(result), nil } func (d *thriftDecoder) timerTaskInfoFromBlob(data []byte) (*TimerTaskInfo, error) { result := &sqlblobs.TimerTaskInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return timerTaskInfoFromThrift(result), nil } func (d *thriftDecoder) replicationTaskInfoFromBlob(data []byte) (*ReplicationTaskInfo, error) { result := &sqlblobs.ReplicationTaskInfo{} if err := thriftRWDecode(data, result); err != nil { return nil, err } return replicationTaskInfoFromThrift(result), nil } func thriftRWDecode(b []byte, result thriftRWType) error { buf := bytes.NewReader(b) sr := binary.Default.Reader(buf) return result.Decode(sr) } ================================================ FILE: common/persistence/serialization/thrift_encoder.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "bytes" "go.uber.org/thriftrw/protocol/binary" "github.com/uber/cadence/common/constants" ) type thriftEncoder struct{} func newThriftEncoder() encoder { return &thriftEncoder{} } func (e *thriftEncoder) shardInfoToBlob(info *ShardInfo) ([]byte, error) { return thriftRWEncode(shardInfoToThrift(info)) } func (e *thriftEncoder) domainInfoToBlob(info *DomainInfo) ([]byte, error) { return thriftRWEncode(domainInfoToThrift(info)) } func (e *thriftEncoder) historyTreeInfoToBlob(info *HistoryTreeInfo) ([]byte, error) { return thriftRWEncode(historyTreeInfoToThrift(info)) } func (e *thriftEncoder) workflowExecutionInfoToBlob(info *WorkflowExecutionInfo) ([]byte, error) { return thriftRWEncode(workflowExecutionInfoToThrift(info)) } func (e *thriftEncoder) activityInfoToBlob(info *ActivityInfo) ([]byte, error) { return thriftRWEncode(activityInfoToThrift(info)) } func (e *thriftEncoder) childExecutionInfoToBlob(info *ChildExecutionInfo) ([]byte, error) { return thriftRWEncode(childExecutionInfoToThrift(info)) } func (e *thriftEncoder) signalInfoToBlob(info *SignalInfo) ([]byte, error) { return thriftRWEncode(signalInfoToThrift(info)) } func (e *thriftEncoder) requestCancelInfoToBlob(info *RequestCancelInfo) ([]byte, error) { return thriftRWEncode(requestCancelInfoToThrift(info)) } func (e *thriftEncoder) timerInfoToBlob(info *TimerInfo) ([]byte, error) { return thriftRWEncode(timerInfoToThrift(info)) } func (e *thriftEncoder) taskInfoToBlob(info *TaskInfo) ([]byte, error) { return thriftRWEncode(taskInfoToThrift(info)) } func (e *thriftEncoder) taskListInfoToBlob(info *TaskListInfo) ([]byte, error) { return thriftRWEncode(taskListInfoToThrift(info)) } func (e *thriftEncoder) transferTaskInfoToBlob(info *TransferTaskInfo) ([]byte, error) { return thriftRWEncode(transferTaskInfoToThrift(info)) } func (e *thriftEncoder) crossClusterTaskInfoToBlob(info *CrossClusterTaskInfo) ([]byte, error) { return thriftRWEncode(crossClusterTaskInfoToThrift(info)) } func (e *thriftEncoder) timerTaskInfoToBlob(info *TimerTaskInfo) ([]byte, error) { return thriftRWEncode(timerTaskInfoToThrift(info)) } func (e *thriftEncoder) replicationTaskInfoToBlob(info *ReplicationTaskInfo) ([]byte, error) { return thriftRWEncode(replicationTaskInfoToThrift(info)) } func (e *thriftEncoder) encodingType() constants.EncodingType { return constants.EncodingTypeThriftRW } func thriftRWEncode(t thriftRWType) ([]byte, error) { var b bytes.Buffer sw := binary.Default.Writer(&b) defer sw.Close() if err := t.Encode(sw); err != nil { return nil, err } return b.Bytes(), nil } ================================================ FILE: common/persistence/serialization/thrift_mapper.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "time" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/.gen/go/sqlblobs" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) func shardInfoToThrift(info *ShardInfo) *sqlblobs.ShardInfo { if info == nil { return nil } result := &sqlblobs.ShardInfo{ StolenSinceRenew: &info.StolenSinceRenew, ReplicationAckLevel: &info.ReplicationAckLevel, TransferAckLevel: &info.TransferAckLevel, DomainNotificationVersion: &info.DomainNotificationVersion, ClusterTransferAckLevel: info.ClusterTransferAckLevel, Owner: &info.Owner, ClusterReplicationLevel: info.ClusterReplicationLevel, PendingFailoverMarkers: info.PendingFailoverMarkers, PendingFailoverMarkersEncoding: &info.PendingFailoverMarkersEncoding, ReplicationDlqAckLevel: info.ReplicationDlqAckLevel, TransferProcessingQueueStates: info.TransferProcessingQueueStates, TransferProcessingQueueStatesEncoding: &info.TransferProcessingQueueStatesEncoding, CrossClusterProcessingQueueStates: info.CrossClusterProcessingQueueStates, CrossClusterProcessingQueueStatesEncoding: &info.CrossClusterProcessingQueueStatesEncoding, TimerProcessingQueueStates: info.TimerProcessingQueueStates, TimerProcessingQueueStatesEncoding: &info.TimerProcessingQueueStatesEncoding, UpdatedAtNanos: timeToUnixNanoPtr(info.UpdatedAt), TimerAckLevelNanos: timeToUnixNanoPtr(info.TimerAckLevel), } if info.ClusterTimerAckLevel != nil { result.ClusterTimerAckLevel = make(map[string]int64, len(info.ClusterTimerAckLevel)) for k, v := range info.ClusterTimerAckLevel { result.ClusterTimerAckLevel[k] = v.UnixNano() } } if info.QueueStates != nil { result.QueueStates = make(map[int32]*shared.QueueState, len(info.QueueStates)) for k, v := range info.QueueStates { result.QueueStates[k] = thrift.FromQueueState(v) } } return result } func shardInfoFromThrift(info *sqlblobs.ShardInfo) *ShardInfo { if info == nil { return nil } result := &ShardInfo{ StolenSinceRenew: info.GetStolenSinceRenew(), ReplicationAckLevel: info.GetReplicationAckLevel(), TransferAckLevel: info.GetTransferAckLevel(), DomainNotificationVersion: info.GetDomainNotificationVersion(), ClusterTransferAckLevel: info.ClusterTransferAckLevel, Owner: info.GetOwner(), ClusterReplicationLevel: info.ClusterReplicationLevel, PendingFailoverMarkers: info.PendingFailoverMarkers, PendingFailoverMarkersEncoding: info.GetPendingFailoverMarkersEncoding(), ReplicationDlqAckLevel: info.ReplicationDlqAckLevel, TransferProcessingQueueStates: info.TransferProcessingQueueStates, TransferProcessingQueueStatesEncoding: info.GetTransferProcessingQueueStatesEncoding(), CrossClusterProcessingQueueStates: info.CrossClusterProcessingQueueStates, CrossClusterProcessingQueueStatesEncoding: info.GetCrossClusterProcessingQueueStatesEncoding(), TimerProcessingQueueStates: info.TimerProcessingQueueStates, TimerProcessingQueueStatesEncoding: info.GetTimerProcessingQueueStatesEncoding(), UpdatedAt: timeFromUnixNano(info.GetUpdatedAtNanos()), TimerAckLevel: timeFromUnixNano(info.GetTimerAckLevelNanos()), } if info.ClusterTimerAckLevel != nil { result.ClusterTimerAckLevel = make(map[string]time.Time, len(info.ClusterTimerAckLevel)) for k, v := range info.ClusterTimerAckLevel { result.ClusterTimerAckLevel[k] = time.Unix(0, v) } } if info.QueueStates != nil { result.QueueStates = make(map[int32]*types.QueueState, len(info.QueueStates)) for k, v := range info.QueueStates { result.QueueStates[k] = thrift.ToQueueState(v) } } return result } func domainInfoToThrift(info *DomainInfo) *sqlblobs.DomainInfo { if info == nil { return nil } return &sqlblobs.DomainInfo{ Name: &info.Name, Description: &info.Description, Owner: &info.Owner, Status: &info.Status, EmitMetric: &info.EmitMetric, ArchivalBucket: &info.ArchivalBucket, ArchivalStatus: &info.ArchivalStatus, ConfigVersion: &info.ConfigVersion, NotificationVersion: &info.NotificationVersion, FailoverNotificationVersion: &info.FailoverNotificationVersion, FailoverVersion: &info.FailoverVersion, ActiveClusterName: &info.ActiveClusterName, ActiveClustersConfiguration: info.ActiveClustersConfig, ActiveClustersConfigurationEncoding: &info.ActiveClustersConfigEncoding, Clusters: info.Clusters, Data: info.Data, BadBinaries: info.BadBinaries, BadBinariesEncoding: &info.BadBinariesEncoding, HistoryArchivalStatus: &info.HistoryArchivalStatus, HistoryArchivalURI: &info.HistoryArchivalURI, VisibilityArchivalStatus: &info.VisibilityArchivalStatus, VisibilityArchivalURI: &info.VisibilityArchivalURI, PreviousFailoverVersion: &info.PreviousFailoverVersion, RetentionDays: durationToDaysInt16Ptr(info.Retention), FailoverEndTime: unixNanoPtr(info.FailoverEndTimestamp), LastUpdatedTime: timeToUnixNanoPtr(info.LastUpdatedTimestamp), IsolationGroupsConfiguration: info.IsolationGroups, IsolationGroupsConfigurationEncoding: &info.IsolationGroupsEncoding, AsyncWorkflowConfiguration: info.AsyncWorkflowConfig, AsyncWorkflowConfigurationEncoding: &info.AsyncWorkflowConfigEncoding, } } func domainInfoFromThrift(info *sqlblobs.DomainInfo) *DomainInfo { if info == nil { return nil } return &DomainInfo{ Name: info.GetName(), Description: info.GetDescription(), Owner: info.GetOwner(), Status: info.GetStatus(), EmitMetric: info.GetEmitMetric(), ArchivalBucket: info.GetArchivalBucket(), ArchivalStatus: info.GetArchivalStatus(), ConfigVersion: info.GetConfigVersion(), NotificationVersion: info.GetNotificationVersion(), FailoverNotificationVersion: info.GetFailoverNotificationVersion(), FailoverVersion: info.GetFailoverVersion(), ActiveClusterName: info.GetActiveClusterName(), ActiveClustersConfig: info.GetActiveClustersConfiguration(), ActiveClustersConfigEncoding: info.GetActiveClustersConfigurationEncoding(), Clusters: info.Clusters, Data: info.Data, BadBinaries: info.BadBinaries, BadBinariesEncoding: info.GetBadBinariesEncoding(), HistoryArchivalStatus: info.GetHistoryArchivalStatus(), HistoryArchivalURI: info.GetHistoryArchivalURI(), VisibilityArchivalStatus: info.GetVisibilityArchivalStatus(), VisibilityArchivalURI: info.GetVisibilityArchivalURI(), PreviousFailoverVersion: info.GetPreviousFailoverVersion(), Retention: common.DaysToDuration(int32(info.GetRetentionDays())), FailoverEndTimestamp: timePtr(info.FailoverEndTime), LastUpdatedTimestamp: timeFromUnixNano(info.GetLastUpdatedTime()), IsolationGroups: info.GetIsolationGroupsConfiguration(), IsolationGroupsEncoding: info.GetIsolationGroupsConfigurationEncoding(), AsyncWorkflowConfig: info.AsyncWorkflowConfiguration, AsyncWorkflowConfigEncoding: info.GetAsyncWorkflowConfigurationEncoding(), } } func historyTreeInfoToThrift(info *HistoryTreeInfo) *sqlblobs.HistoryTreeInfo { if info == nil { return nil } return &sqlblobs.HistoryTreeInfo{ CreatedTimeNanos: timeToUnixNanoPtr(info.CreatedTimestamp), Info: &info.Info, Ancestors: thrift.FromHistoryBranchRangeArray(info.Ancestors), } } func historyTreeInfoFromThrift(info *sqlblobs.HistoryTreeInfo) *HistoryTreeInfo { if info == nil { return nil } return &HistoryTreeInfo{ CreatedTimestamp: timeFromUnixNano(info.GetCreatedTimeNanos()), Info: info.GetInfo(), Ancestors: thrift.ToHistoryBranchRangeArray(info.Ancestors), } } func taskListKindFromThrift(kind *shared.TaskListKind) types.TaskListKind { res := thrift.ToTaskListKind(kind) if res == nil { return types.TaskListKindNormal } return *res } func workflowExecutionInfoToThrift(info *WorkflowExecutionInfo) *sqlblobs.WorkflowExecutionInfo { if info == nil { return nil } return &sqlblobs.WorkflowExecutionInfo{ ParentDomainID: info.ParentDomainID, ParentWorkflowID: &info.ParentWorkflowID, ParentRunID: info.ParentRunID, InitiatedID: &info.InitiatedID, CompletionEventBatchID: info.CompletionEventBatchID, CompletionEvent: info.CompletionEvent, CompletionEventEncoding: &info.CompletionEventEncoding, TaskList: &info.TaskList, TaskListKind: thrift.FromTaskListKind(&info.TaskListKind), WorkflowTypeName: &info.WorkflowTypeName, WorkflowTimeoutSeconds: durationToSecondsInt32Ptr(info.WorkflowTimeout), DecisionTaskTimeoutSeconds: durationToSecondsInt32Ptr(info.DecisionTaskTimeout), ExecutionContext: info.ExecutionContext, State: &info.State, CloseStatus: &info.CloseStatus, StartVersion: &info.StartVersion, LastWriteEventID: info.LastWriteEventID, LastEventTaskID: &info.LastEventTaskID, LastFirstEventID: &info.LastFirstEventID, LastProcessedEvent: &info.LastProcessedEvent, StartTimeNanos: timeToUnixNanoPtr(info.StartTimestamp), LastUpdatedTimeNanos: timeToUnixNanoPtr(info.LastUpdatedTimestamp), DecisionVersion: &info.DecisionVersion, DecisionScheduleID: &info.DecisionScheduleID, DecisionStartedID: &info.DecisionStartedID, DecisionTimeout: durationToSecondsInt32Ptr(info.DecisionTimeout), DecisionAttempt: &info.DecisionAttempt, DecisionStartedTimestampNanos: timeToUnixNanoPtr(info.DecisionStartedTimestamp), DecisionScheduledTimestampNanos: timeToUnixNanoPtr(info.DecisionScheduledTimestamp), CancelRequested: &info.CancelRequested, DecisionOriginalScheduledTimestampNanos: timeToUnixNanoPtr(info.DecisionOriginalScheduledTimestamp), CreateRequestID: &info.CreateRequestID, DecisionRequestID: &info.DecisionRequestID, CancelRequestID: &info.CancelRequestID, StickyTaskList: &info.StickyTaskList, StickyScheduleToStartTimeout: durationToSecondsInt64Ptr(info.StickyScheduleToStartTimeout), RetryAttempt: &info.RetryAttempt, RetryInitialIntervalSeconds: durationToSecondsInt32Ptr(info.RetryInitialInterval), RetryMaximumIntervalSeconds: durationToSecondsInt32Ptr(info.RetryMaximumInterval), RetryMaximumAttempts: &info.RetryMaximumAttempts, RetryExpirationSeconds: durationToSecondsInt32Ptr(info.RetryExpiration), RetryBackoffCoefficient: &info.RetryBackoffCoefficient, RetryExpirationTimeNanos: timeToUnixNanoPtr(info.RetryExpirationTimestamp), RetryNonRetryableErrors: info.RetryNonRetryableErrors, HasRetryPolicy: &info.HasRetryPolicy, CronSchedule: &info.CronSchedule, CronOverlapPolicy: thrift.FromCronOverlapPolicy(&info.CronOverlapPolicy), EventStoreVersion: &info.EventStoreVersion, EventBranchToken: info.EventBranchToken, SignalCount: &info.SignalCount, HistorySize: &info.HistorySize, ClientLibraryVersion: &info.ClientLibraryVersion, ClientFeatureVersion: &info.ClientFeatureVersion, ClientImpl: &info.ClientImpl, AutoResetPoints: info.AutoResetPoints, AutoResetPointsEncoding: &info.AutoResetPointsEncoding, SearchAttributes: info.SearchAttributes, Memo: info.Memo, VersionHistories: info.VersionHistories, VersionHistoriesEncoding: &info.VersionHistoriesEncoding, FirstExecutionRunID: info.FirstExecutionRunID, PartitionConfig: info.PartitionConfig, Checksum: info.Checksum, ChecksumEncoding: &info.ChecksumEncoding, ActiveClusterSelectionPolicy: info.ActiveClusterSelectionPolicy, ActiveClusterSelectionPolicyEncoding: &info.ActiveClusterSelectionPolicyEncoding, } } func workflowExecutionInfoFromThrift(info *sqlblobs.WorkflowExecutionInfo) *WorkflowExecutionInfo { if info == nil { return nil } return &WorkflowExecutionInfo{ ParentDomainID: info.ParentDomainID, ParentWorkflowID: info.GetParentWorkflowID(), ParentRunID: info.ParentRunID, InitiatedID: info.GetInitiatedID(), CompletionEventBatchID: info.CompletionEventBatchID, CompletionEvent: info.CompletionEvent, CompletionEventEncoding: info.GetCompletionEventEncoding(), TaskList: info.GetTaskList(), TaskListKind: taskListKindFromThrift(info.TaskListKind), WorkflowTypeName: info.GetWorkflowTypeName(), WorkflowTimeout: common.SecondsToDuration(int64(info.GetWorkflowTimeoutSeconds())), DecisionTaskTimeout: common.SecondsToDuration(int64(info.GetDecisionTaskTimeoutSeconds())), ExecutionContext: info.ExecutionContext, State: info.GetState(), CloseStatus: info.GetCloseStatus(), StartVersion: info.GetStartVersion(), LastWriteEventID: info.LastWriteEventID, LastEventTaskID: info.GetLastEventTaskID(), LastFirstEventID: info.GetLastFirstEventID(), LastProcessedEvent: info.GetLastProcessedEvent(), StartTimestamp: timeFromUnixNano(info.GetStartTimeNanos()), LastUpdatedTimestamp: timeFromUnixNano(info.GetLastUpdatedTimeNanos()), DecisionVersion: info.GetDecisionVersion(), DecisionScheduleID: info.GetDecisionScheduleID(), DecisionStartedID: info.GetDecisionStartedID(), DecisionTimeout: common.SecondsToDuration(int64(info.GetDecisionTimeout())), DecisionAttempt: info.GetDecisionAttempt(), DecisionStartedTimestamp: timeFromUnixNano(info.GetDecisionStartedTimestampNanos()), DecisionScheduledTimestamp: timeFromUnixNano(info.GetDecisionScheduledTimestampNanos()), CancelRequested: info.GetCancelRequested(), DecisionOriginalScheduledTimestamp: timeFromUnixNano(info.GetDecisionOriginalScheduledTimestampNanos()), CreateRequestID: info.GetCreateRequestID(), DecisionRequestID: info.GetDecisionRequestID(), CancelRequestID: info.GetCancelRequestID(), StickyTaskList: info.GetStickyTaskList(), StickyScheduleToStartTimeout: common.SecondsToDuration(info.GetStickyScheduleToStartTimeout()), RetryAttempt: info.GetRetryAttempt(), RetryInitialInterval: common.SecondsToDuration(int64(info.GetRetryInitialIntervalSeconds())), RetryMaximumInterval: common.SecondsToDuration(int64(info.GetRetryMaximumIntervalSeconds())), RetryMaximumAttempts: info.GetRetryMaximumAttempts(), RetryExpiration: common.SecondsToDuration(int64(info.GetRetryExpirationSeconds())), RetryBackoffCoefficient: info.GetRetryBackoffCoefficient(), RetryExpirationTimestamp: timeFromUnixNano(info.GetRetryExpirationTimeNanos()), RetryNonRetryableErrors: info.RetryNonRetryableErrors, HasRetryPolicy: info.GetHasRetryPolicy(), CronSchedule: info.GetCronSchedule(), CronOverlapPolicy: cronOverlapPolicyFromThrift(info.CronOverlapPolicy), EventStoreVersion: info.GetEventStoreVersion(), EventBranchToken: info.EventBranchToken, SignalCount: info.GetSignalCount(), HistorySize: info.GetHistorySize(), ClientLibraryVersion: info.GetClientLibraryVersion(), ClientFeatureVersion: info.GetClientFeatureVersion(), ClientImpl: info.GetClientImpl(), AutoResetPoints: info.AutoResetPoints, AutoResetPointsEncoding: info.GetAutoResetPointsEncoding(), SearchAttributes: info.SearchAttributes, Memo: info.Memo, VersionHistories: info.VersionHistories, VersionHistoriesEncoding: info.GetVersionHistoriesEncoding(), FirstExecutionRunID: info.FirstExecutionRunID, PartitionConfig: info.PartitionConfig, IsCron: info.GetCronSchedule() != "", Checksum: info.Checksum, ChecksumEncoding: info.GetChecksumEncoding(), ActiveClusterSelectionPolicy: info.ActiveClusterSelectionPolicy, ActiveClusterSelectionPolicyEncoding: info.GetActiveClusterSelectionPolicyEncoding(), } } func activityInfoToThrift(info *ActivityInfo) *sqlblobs.ActivityInfo { if info == nil { return nil } return &sqlblobs.ActivityInfo{ Version: &info.Version, ScheduledEventBatchID: &info.ScheduledEventBatchID, ScheduledEvent: info.ScheduledEvent, ScheduledEventEncoding: &info.ScheduledEventEncoding, ScheduledTimeNanos: timeToUnixNanoPtr(info.ScheduledTimestamp), StartedID: &info.StartedID, StartedEvent: info.StartedEvent, StartedEventEncoding: &info.StartedEventEncoding, StartedTimeNanos: timeToUnixNanoPtr(info.StartedTimestamp), ActivityID: &info.ActivityID, RequestID: &info.RequestID, ScheduleToStartTimeoutSeconds: durationToSecondsInt32Ptr(info.ScheduleToStartTimeout), ScheduleToCloseTimeoutSeconds: durationToSecondsInt32Ptr(info.ScheduleToCloseTimeout), StartToCloseTimeoutSeconds: durationToSecondsInt32Ptr(info.StartToCloseTimeout), HeartbeatTimeoutSeconds: durationToSecondsInt32Ptr(info.HeartbeatTimeout), CancelRequested: &info.CancelRequested, CancelRequestID: &info.CancelRequestID, TimerTaskStatus: &info.TimerTaskStatus, Attempt: &info.Attempt, TaskList: &info.TaskList, TaskListKind: thrift.FromTaskListKind(&info.TaskListKind), StartedIdentity: &info.StartedIdentity, HasRetryPolicy: &info.HasRetryPolicy, RetryInitialIntervalSeconds: durationToSecondsInt32Ptr(info.RetryInitialInterval), RetryMaximumIntervalSeconds: durationToSecondsInt32Ptr(info.RetryMaximumInterval), RetryMaximumAttempts: &info.RetryMaximumAttempts, RetryExpirationTimeNanos: timeToUnixNanoPtr(info.RetryExpirationTimestamp), RetryBackoffCoefficient: &info.RetryBackoffCoefficient, RetryNonRetryableErrors: info.RetryNonRetryableErrors, RetryLastFailureReason: &info.RetryLastFailureReason, RetryLastWorkerIdentity: &info.RetryLastWorkerIdentity, RetryLastFailureDetails: info.RetryLastFailureDetails, } } func activityInfoFromThrift(info *sqlblobs.ActivityInfo) *ActivityInfo { if info == nil { return nil } return &ActivityInfo{ Version: info.GetVersion(), ScheduledEventBatchID: info.GetScheduledEventBatchID(), ScheduledEvent: info.ScheduledEvent, ScheduledEventEncoding: info.GetScheduledEventEncoding(), ScheduledTimestamp: timeFromUnixNano(info.GetScheduledTimeNanos()), StartedID: info.GetStartedID(), StartedEvent: info.StartedEvent, StartedEventEncoding: info.GetStartedEventEncoding(), StartedTimestamp: timeFromUnixNano(info.GetStartedTimeNanos()), ActivityID: info.GetActivityID(), RequestID: info.GetRequestID(), ScheduleToStartTimeout: common.SecondsToDuration(int64(info.GetScheduleToStartTimeoutSeconds())), ScheduleToCloseTimeout: common.SecondsToDuration(int64(info.GetScheduleToCloseTimeoutSeconds())), StartToCloseTimeout: common.SecondsToDuration(int64(info.GetStartToCloseTimeoutSeconds())), HeartbeatTimeout: common.SecondsToDuration(int64(info.GetHeartbeatTimeoutSeconds())), CancelRequested: info.GetCancelRequested(), CancelRequestID: info.GetCancelRequestID(), TimerTaskStatus: info.GetTimerTaskStatus(), Attempt: info.GetAttempt(), TaskList: info.GetTaskList(), TaskListKind: taskListKindFromThrift(info.TaskListKind), StartedIdentity: info.GetStartedIdentity(), HasRetryPolicy: info.GetHasRetryPolicy(), RetryInitialInterval: common.SecondsToDuration(int64(info.GetRetryInitialIntervalSeconds())), RetryMaximumInterval: common.SecondsToDuration(int64(info.GetRetryMaximumIntervalSeconds())), RetryMaximumAttempts: info.GetRetryMaximumAttempts(), RetryExpirationTimestamp: timeFromUnixNano(info.GetRetryExpirationTimeNanos()), RetryBackoffCoefficient: info.GetRetryBackoffCoefficient(), RetryNonRetryableErrors: info.RetryNonRetryableErrors, RetryLastFailureReason: info.GetRetryLastFailureReason(), RetryLastWorkerIdentity: info.GetRetryLastWorkerIdentity(), RetryLastFailureDetails: info.RetryLastFailureDetails, } } func childExecutionInfoToThrift(info *ChildExecutionInfo) *sqlblobs.ChildExecutionInfo { if info == nil { return nil } return &sqlblobs.ChildExecutionInfo{ Version: &info.Version, InitiatedEventBatchID: &info.InitiatedEventBatchID, StartedID: &info.StartedID, InitiatedEvent: info.InitiatedEvent, InitiatedEventEncoding: &info.InitiatedEventEncoding, StartedWorkflowID: &info.StartedWorkflowID, StartedRunID: info.StartedRunID, StartedEvent: info.StartedEvent, StartedEventEncoding: &info.StartedEventEncoding, CreateRequestID: &info.CreateRequestID, DomainID: &info.DomainID, DomainName: &info.DomainNameDEPRECATED, WorkflowTypeName: &info.WorkflowTypeName, ParentClosePolicy: &info.ParentClosePolicy, } } func childExecutionInfoFromThrift(info *sqlblobs.ChildExecutionInfo) *ChildExecutionInfo { if info == nil { return nil } return &ChildExecutionInfo{ Version: info.GetVersion(), InitiatedEventBatchID: info.GetInitiatedEventBatchID(), StartedID: info.GetStartedID(), InitiatedEvent: info.InitiatedEvent, InitiatedEventEncoding: info.GetInitiatedEventEncoding(), StartedWorkflowID: info.GetStartedWorkflowID(), StartedRunID: info.GetStartedRunID(), StartedEvent: info.StartedEvent, StartedEventEncoding: info.GetStartedEventEncoding(), CreateRequestID: info.GetCreateRequestID(), DomainID: info.GetDomainID(), DomainNameDEPRECATED: info.GetDomainName(), WorkflowTypeName: info.GetWorkflowTypeName(), ParentClosePolicy: info.GetParentClosePolicy(), } } func signalInfoToThrift(info *SignalInfo) *sqlblobs.SignalInfo { if info == nil { return nil } return &sqlblobs.SignalInfo{ Version: &info.Version, InitiatedEventBatchID: &info.InitiatedEventBatchID, RequestID: &info.RequestID, Name: &info.Name, Input: info.Input, Control: info.Control, } } func signalInfoFromThrift(info *sqlblobs.SignalInfo) *SignalInfo { if info == nil { return nil } return &SignalInfo{ Version: info.GetVersion(), InitiatedEventBatchID: info.GetInitiatedEventBatchID(), RequestID: info.GetRequestID(), Name: info.GetName(), Input: info.Input, Control: info.Control, } } func requestCancelInfoToThrift(info *RequestCancelInfo) *sqlblobs.RequestCancelInfo { if info == nil { return nil } return &sqlblobs.RequestCancelInfo{ Version: &info.Version, InitiatedEventBatchID: &info.InitiatedEventBatchID, CancelRequestID: &info.CancelRequestID, } } func requestCancelInfoFromThrift(info *sqlblobs.RequestCancelInfo) *RequestCancelInfo { if info == nil { return nil } return &RequestCancelInfo{ Version: info.GetVersion(), InitiatedEventBatchID: info.GetInitiatedEventBatchID(), CancelRequestID: info.GetCancelRequestID(), } } func timerInfoToThrift(info *TimerInfo) *sqlblobs.TimerInfo { if info == nil { return nil } return &sqlblobs.TimerInfo{ Version: &info.Version, StartedID: &info.StartedID, ExpiryTimeNanos: timeToUnixNanoPtr(info.ExpiryTimestamp), TaskID: &info.TaskID, } } func timerInfoFromThrift(info *sqlblobs.TimerInfo) *TimerInfo { if info == nil { return nil } return &TimerInfo{ Version: info.GetVersion(), StartedID: info.GetStartedID(), ExpiryTimestamp: timeFromUnixNano(info.GetExpiryTimeNanos()), TaskID: info.GetTaskID(), } } func taskInfoToThrift(info *TaskInfo) *sqlblobs.TaskInfo { if info == nil { return nil } return &sqlblobs.TaskInfo{ WorkflowID: &info.WorkflowID, RunID: info.RunID, ScheduleID: &info.ScheduleID, ExpiryTimeNanos: timeToUnixNanoPtr(info.ExpiryTimestamp), CreatedTimeNanos: timeToUnixNanoPtr(info.CreatedTimestamp), PartitionConfig: info.PartitionConfig, } } func taskInfoFromThrift(info *sqlblobs.TaskInfo) *TaskInfo { if info == nil { return nil } return &TaskInfo{ WorkflowID: info.GetWorkflowID(), RunID: info.RunID, ScheduleID: info.GetScheduleID(), ExpiryTimestamp: timeFromUnixNano(info.GetExpiryTimeNanos()), CreatedTimestamp: timeFromUnixNano(info.GetCreatedTimeNanos()), PartitionConfig: info.PartitionConfig, } } func taskListPartitionConfigToThrift(info *TaskListPartitionConfig) *sqlblobs.TaskListPartitionConfig { if info == nil { return nil } return &sqlblobs.TaskListPartitionConfig{ Version: &info.Version, NumReadPartitions: common.Int32Ptr(info.NumReadPartitions), NumWritePartitions: common.Int32Ptr(info.NumWritePartitions), ReadPartitions: taskListPartitionMapToThrift(info.ReadPartitions), WritePartitions: taskListPartitionMapToThrift(info.WritePartitions), } } func taskListPartitionMapToThrift(m map[int32]*TaskListPartition) map[int32]*sqlblobs.TaskListPartition { if m == nil { return nil } result := make(map[int32]*sqlblobs.TaskListPartition) for id, p := range m { result[id] = &sqlblobs.TaskListPartition{IsolationGroups: p.IsolationGroups} } return result } func taskListPartitionMapFromThrift(m map[int32]*sqlblobs.TaskListPartition) map[int32]*TaskListPartition { if m == nil { return nil } result := make(map[int32]*TaskListPartition) for id, p := range m { result[id] = &TaskListPartition{IsolationGroups: p.IsolationGroups} } return result } func taskListParititionConfigFromThrift(info *sqlblobs.TaskListPartitionConfig) *TaskListPartitionConfig { if info == nil { return nil } return &TaskListPartitionConfig{ Version: info.GetVersion(), NumReadPartitions: info.GetNumReadPartitions(), NumWritePartitions: info.GetNumWritePartitions(), ReadPartitions: taskListPartitionMapFromThrift(info.ReadPartitions), WritePartitions: taskListPartitionMapFromThrift(info.WritePartitions), } } func taskListInfoToThrift(info *TaskListInfo) *sqlblobs.TaskListInfo { if info == nil { return nil } return &sqlblobs.TaskListInfo{ Kind: &info.Kind, AckLevel: &info.AckLevel, ExpiryTimeNanos: timeToUnixNanoPtr(info.ExpiryTimestamp), LastUpdatedNanos: timeToUnixNanoPtr(info.LastUpdated), AdaptivePartitionConfig: taskListPartitionConfigToThrift(info.AdaptivePartitionConfig), } } func taskListInfoFromThrift(info *sqlblobs.TaskListInfo) *TaskListInfo { if info == nil { return nil } return &TaskListInfo{ Kind: info.GetKind(), AckLevel: info.GetAckLevel(), ExpiryTimestamp: timeFromUnixNano(info.GetExpiryTimeNanos()), LastUpdated: timeFromUnixNano(info.GetLastUpdatedNanos()), AdaptivePartitionConfig: taskListParititionConfigFromThrift(info.AdaptivePartitionConfig), } } func transferTaskInfoToThrift(info *TransferTaskInfo) *sqlblobs.TransferTaskInfo { if info == nil { return nil } thriftTaskInfo := &sqlblobs.TransferTaskInfo{ DomainID: info.DomainID, WorkflowID: &info.WorkflowID, RunID: info.RunID, TaskType: &info.TaskType, TargetDomainID: info.TargetDomainID, // TargetDomainIDs will be assigned below TargetWorkflowID: &info.TargetWorkflowID, TargetRunID: info.TargetRunID, TaskList: &info.TaskList, TargetChildWorkflowOnly: &info.TargetChildWorkflowOnly, ScheduleID: &info.ScheduleID, Version: &info.Version, VisibilityTimestampNanos: timeToUnixNanoPtr(info.VisibilityTimestamp), OriginalTaskList: &info.OriginalTaskList, OriginalTaskListKind: thrift.FromTaskListKind(&info.OriginalTaskListKind), } if len(info.TargetDomainIDs) > 0 { thriftTaskInfo.TargetDomainIDs = [][]byte{} for _, domainID := range info.TargetDomainIDs { thriftTaskInfo.TargetDomainIDs = append(thriftTaskInfo.TargetDomainIDs, domainID) } } return thriftTaskInfo } func transferTaskInfoFromThrift(info *sqlblobs.TransferTaskInfo) *TransferTaskInfo { if info == nil { return nil } transferTaskInfo := &TransferTaskInfo{ DomainID: info.DomainID, WorkflowID: info.GetWorkflowID(), RunID: info.RunID, TaskType: info.GetTaskType(), TargetDomainID: info.TargetDomainID, // TargetDomainIDs will be assigned below TargetWorkflowID: info.GetTargetWorkflowID(), TargetRunID: info.TargetRunID, TaskList: info.GetTaskList(), TargetChildWorkflowOnly: info.GetTargetChildWorkflowOnly(), ScheduleID: info.GetScheduleID(), Version: info.GetVersion(), VisibilityTimestamp: timeFromUnixNano(info.GetVisibilityTimestampNanos()), OriginalTaskList: info.GetOriginalTaskList(), OriginalTaskListKind: taskListKindFromThrift(info.OriginalTaskListKind), } if len(info.GetTargetDomainIDs()) > 0 { transferTaskInfo.TargetDomainIDs = []UUID{} for _, domainID := range info.GetTargetDomainIDs() { transferTaskInfo.TargetDomainIDs = append(transferTaskInfo.TargetDomainIDs, domainID) } } return transferTaskInfo } func crossClusterTaskInfoToThrift(info *CrossClusterTaskInfo) *sqlblobsCrossClusterTaskInfo { return transferTaskInfoToThrift(info) } func crossClusterTaskInfoFromThrift(info *sqlblobsCrossClusterTaskInfo) *CrossClusterTaskInfo { return transferTaskInfoFromThrift(info) } func timerTaskInfoToThrift(info *TimerTaskInfo) *sqlblobs.TimerTaskInfo { if info == nil { return nil } return &sqlblobs.TimerTaskInfo{ DomainID: info.DomainID, WorkflowID: &info.WorkflowID, RunID: info.RunID, TaskType: &info.TaskType, TimeoutType: info.TimeoutType, Version: &info.Version, ScheduleAttempt: &info.ScheduleAttempt, EventID: &info.EventID, TaskList: &info.TaskList, } } func timerTaskInfoFromThrift(info *sqlblobs.TimerTaskInfo) *TimerTaskInfo { if info == nil { return nil } return &TimerTaskInfo{ DomainID: info.DomainID, WorkflowID: info.GetWorkflowID(), RunID: info.RunID, TaskType: info.GetTaskType(), TimeoutType: info.TimeoutType, Version: info.GetVersion(), ScheduleAttempt: info.GetScheduleAttempt(), EventID: info.GetEventID(), TaskList: info.GetTaskList(), } } func replicationTaskInfoToThrift(info *ReplicationTaskInfo) *sqlblobs.ReplicationTaskInfo { if info == nil { return nil } return &sqlblobs.ReplicationTaskInfo{ DomainID: info.DomainID, WorkflowID: &info.WorkflowID, RunID: info.RunID, TaskType: &info.TaskType, Version: &info.Version, FirstEventID: &info.FirstEventID, NextEventID: &info.NextEventID, ScheduledID: &info.ScheduledID, EventStoreVersion: &info.EventStoreVersion, NewRunEventStoreVersion: &info.NewRunEventStoreVersion, BranchToken: info.BranchToken, NewRunBranchToken: info.NewRunBranchToken, CreationTime: timeToUnixNanoPtr(info.CreationTimestamp), } } func replicationTaskInfoFromThrift(info *sqlblobs.ReplicationTaskInfo) *ReplicationTaskInfo { if info == nil { return nil } return &ReplicationTaskInfo{ DomainID: info.DomainID, WorkflowID: info.GetWorkflowID(), RunID: info.RunID, TaskType: info.GetTaskType(), Version: info.GetVersion(), FirstEventID: info.GetFirstEventID(), NextEventID: info.GetNextEventID(), ScheduledID: info.GetScheduledID(), EventStoreVersion: info.GetEventStoreVersion(), NewRunEventStoreVersion: info.GetNewRunEventStoreVersion(), BranchToken: info.BranchToken, NewRunBranchToken: info.NewRunBranchToken, CreationTimestamp: timeFromUnixNano(info.GetCreationTime()), } } var zeroTimeNanos = time.Time{}.UnixNano() func unixNanoPtr(t *time.Time) *int64 { if t == nil { return nil } return common.Int64Ptr(t.UnixNano()) } func timeToUnixNanoPtr(t time.Time) *int64 { return common.Int64Ptr(t.UnixNano()) } func timePtr(t *int64) *time.Time { if t == nil { return nil } // Calling UnixNano() on zero time is undefined an results in a number that is converted back to 1754-08-30T22:43:41Z // Handle such case explicitly if *t == zeroTimeNanos { return common.TimePtr(time.Time{}) } return common.TimePtr(time.Unix(0, *t)) } func timeFromUnixNano(t int64) time.Time { // Calling UnixNano() on zero time is undefined an results in a number that is converted back to 1754-08-30T22:43:41Z // Handle such case explicitly if t == zeroTimeNanos { return time.Time{} } return time.Unix(0, t) } func durationToSecondsInt32Ptr(t time.Duration) *int32 { return common.Int32Ptr(int32(common.DurationToSeconds(t))) } func durationToSecondsInt64Ptr(t time.Duration) *int64 { return common.Int64Ptr(common.DurationToSeconds(t)) } func durationToDaysInt16Ptr(t time.Duration) *int16 { return common.Int16Ptr(int16(common.DurationToDays(t))) } func cronOverlapPolicyFromThrift(t *shared.CronOverlapPolicy) types.CronOverlapPolicy { if t == nil { return types.CronOverlapPolicySkipped } if result := thrift.ToCronOverlapPolicy(t); result != nil { return *result } return types.CronOverlapPolicySkipped } ================================================ FILE: common/persistence/serialization/thrift_mapper_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package serialization import ( "math/rand" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) func TestShardInfo(t *testing.T) { expected := &ShardInfo{ StolenSinceRenew: int32(rand.Intn(1000)), UpdatedAt: time.Now(), ReplicationAckLevel: int64(rand.Intn(1000)), TransferAckLevel: int64(rand.Intn(1000)), TimerAckLevel: time.Now(), DomainNotificationVersion: int64(rand.Intn(1000)), ClusterTransferAckLevel: map[string]int64{"key_1": int64(rand.Intn(1000)), "key_2": int64(rand.Intn(1000))}, ClusterTimerAckLevel: map[string]time.Time{"key_1": time.Now(), "key_2": time.Now()}, Owner: "test_owner", ClusterReplicationLevel: map[string]int64{"key_1": int64(rand.Intn(1000)), "key_2": int64(rand.Intn(1000))}, PendingFailoverMarkers: []byte("PendingFailoverMarkers"), PendingFailoverMarkersEncoding: "PendingFailoverMarkersEncoding", ReplicationDlqAckLevel: map[string]int64{"key_1": int64(rand.Intn(1000)), "key_2": int64(rand.Intn(1000))}, TransferProcessingQueueStates: []byte("TransferProcessingQueueStates"), TransferProcessingQueueStatesEncoding: "TransferProcessingQueueStatesEncoding", TimerProcessingQueueStates: []byte("TimerProcessingQueueStates"), TimerProcessingQueueStatesEncoding: "TimerProcessingQueueStatesEncoding", QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: { VirtualSliceStates: []*types.VirtualSliceState{ { TaskRange: &types.TaskRange{ InclusiveMin: &types.TaskKey{ TaskID: 1000, }, ExclusiveMax: &types.TaskKey{ TaskID: 2000, }, }, }, }, }, }, ExclusiveMaxReadLevel: &types.TaskKey{ TaskID: 1000, }, }, }, } actual := shardInfoFromThrift(shardInfoToThrift(expected)) assert.Equal(t, expected.StolenSinceRenew, actual.StolenSinceRenew) assert.Equal(t, expected.UpdatedAt.Sub(actual.UpdatedAt), time.Duration(0)) assert.Equal(t, expected.ReplicationAckLevel, actual.ReplicationAckLevel) assert.Equal(t, expected.TransferAckLevel, actual.TransferAckLevel) assert.Equal(t, expected.TimerAckLevel.Sub(actual.TimerAckLevel), time.Duration(0)) assert.Equal(t, expected.DomainNotificationVersion, actual.DomainNotificationVersion) assert.Equal(t, expected.ClusterTransferAckLevel, actual.ClusterTransferAckLevel) assert.Equal(t, expected.Owner, actual.Owner) assert.Equal(t, expected.ClusterReplicationLevel, actual.ClusterReplicationLevel) assert.Equal(t, expected.PendingFailoverMarkers, actual.PendingFailoverMarkers) assert.Equal(t, expected.PendingFailoverMarkersEncoding, actual.PendingFailoverMarkersEncoding) assert.Equal(t, expected.ReplicationDlqAckLevel, actual.ReplicationDlqAckLevel) assert.Equal(t, expected.TransferProcessingQueueStates, actual.TransferProcessingQueueStates) assert.Equal(t, expected.TransferProcessingQueueStatesEncoding, actual.TransferProcessingQueueStatesEncoding) assert.Equal(t, expected.TimerProcessingQueueStates, actual.TimerProcessingQueueStates) assert.Equal(t, expected.TimerProcessingQueueStatesEncoding, actual.TimerProcessingQueueStatesEncoding) assert.Len(t, actual.ClusterTimerAckLevel, 2) assert.Contains(t, actual.ClusterTimerAckLevel, "key_1") assert.Contains(t, actual.ClusterTimerAckLevel, "key_2") assert.Equal(t, expected.ClusterTimerAckLevel["key_1"].Sub(actual.ClusterTimerAckLevel["key_1"]), time.Duration(0)) assert.Equal(t, expected.ClusterTimerAckLevel["key_2"].Sub(actual.ClusterTimerAckLevel["key_2"]), time.Duration(0)) assert.Nil(t, shardInfoFromThrift(nil)) assert.Nil(t, shardInfoToThrift(nil)) } func TestDomainInfo(t *testing.T) { expected := &DomainInfo{ Name: "domain_name", Description: "description", Owner: "owner", Status: int32(rand.Intn(1000)), Retention: time.Duration(int64(rand.Intn(1000))), EmitMetric: true, ArchivalBucket: "archival_bucket", ArchivalStatus: int16(rand.Intn(1000)), ConfigVersion: int64(rand.Intn(1000)), NotificationVersion: int64(rand.Intn(1000)), FailoverNotificationVersion: int64(rand.Intn(1000)), FailoverVersion: int64(rand.Intn(1000)), ActiveClusterName: "ActiveClusterName", ActiveClustersConfig: []byte("activeClustersConfig"), ActiveClustersConfigEncoding: "activeClustersConfigEncoding", Clusters: []string{"cluster_a", "cluster_b"}, Data: map[string]string{"key_1": "value_1", "key_2": "value_2"}, BadBinaries: []byte("BadBinaries"), BadBinariesEncoding: "BadBinariesEncoding", HistoryArchivalStatus: int16(rand.Intn(1000)), HistoryArchivalURI: "HistoryArchivalURI", VisibilityArchivalStatus: int16(rand.Intn(1000)), VisibilityArchivalURI: "VisibilityArchivalURI", FailoverEndTimestamp: common.TimePtr(time.Now()), PreviousFailoverVersion: int64(rand.Intn(1000)), LastUpdatedTimestamp: time.Now(), } actual := domainInfoFromThrift(domainInfoToThrift(expected)) assert.Equal(t, expected.Name, actual.Name) assert.Equal(t, expected.Description, actual.Description) assert.Equal(t, expected.Owner, actual.Owner) assert.Equal(t, expected.Status, actual.Status) assert.True(t, (expected.Retention-actual.Retention) < time.Second) assert.Equal(t, expected.EmitMetric, actual.EmitMetric) assert.Equal(t, expected.ArchivalBucket, actual.ArchivalBucket) assert.Equal(t, expected.ArchivalStatus, actual.ArchivalStatus) assert.Equal(t, expected.ConfigVersion, actual.ConfigVersion) assert.Equal(t, expected.NotificationVersion, actual.NotificationVersion) assert.Equal(t, expected.FailoverNotificationVersion, actual.FailoverNotificationVersion) assert.Equal(t, expected.ActiveClusterName, actual.ActiveClusterName) assert.Equal(t, expected.Clusters, actual.Clusters) assert.Equal(t, expected.ActiveClustersConfig, actual.ActiveClustersConfig) assert.Equal(t, expected.ActiveClustersConfigEncoding, actual.ActiveClustersConfigEncoding) assert.Equal(t, expected.Data, actual.Data) assert.Equal(t, expected.BadBinaries, actual.BadBinaries) assert.Equal(t, expected.BadBinariesEncoding, actual.BadBinariesEncoding) assert.Equal(t, expected.HistoryArchivalStatus, actual.HistoryArchivalStatus) assert.Equal(t, expected.HistoryArchivalURI, actual.HistoryArchivalURI) assert.Equal(t, expected.VisibilityArchivalStatus, actual.VisibilityArchivalStatus) assert.Equal(t, expected.VisibilityArchivalURI, actual.VisibilityArchivalURI) assert.Equal(t, expected.FailoverEndTimestamp.Sub(*actual.FailoverEndTimestamp), time.Duration(0)) assert.Equal(t, expected.PreviousFailoverVersion, actual.PreviousFailoverVersion) assert.Equal(t, expected.LastUpdatedTimestamp.Sub(actual.LastUpdatedTimestamp), time.Duration(0)) assert.Nil(t, domainInfoFromThrift(nil)) assert.Nil(t, domainInfoToThrift(nil)) } func TestDomainInfoRoundtripPanictest(t *testing.T) { tests := map[string]struct { in *DomainInfo }{ "empty roundtrip": { in: &DomainInfo{}, }, "nil roundtrip": { in: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.in, domainInfoFromThrift(domainInfoToThrift(td.in))) }) } } func TestHistoryTreeInfo(t *testing.T) { expected := &HistoryTreeInfo{ CreatedTimestamp: time.Now(), Ancestors: []*types.HistoryBranchRange{ { BranchID: "branch_id", BeginNodeID: int64(rand.Intn(1000)), EndNodeID: int64(rand.Intn(1000)), }, { BranchID: "branch_id", BeginNodeID: int64(rand.Intn(1000)), EndNodeID: int64(rand.Intn(1000)), }, }, Info: "info", } actual := historyTreeInfoFromThrift(historyTreeInfoToThrift(expected)) assert.Equal(t, expected.CreatedTimestamp.Sub(actual.CreatedTimestamp), time.Duration(0)) assert.Equal(t, expected.Ancestors, actual.Ancestors) assert.Equal(t, expected.Info, actual.Info) assert.Nil(t, historyTreeInfoFromThrift(nil)) assert.Nil(t, historyTreeInfoToThrift(nil)) } func TestWorkflowExecutionInfo(t *testing.T) { expected := &WorkflowExecutionInfo{ ParentDomainID: UUID(uuid.New()), ParentWorkflowID: "ParentWorkflowID", ParentRunID: UUID(uuid.New()), InitiatedID: int64(rand.Intn(1000)), CompletionEventBatchID: common.Int64Ptr(int64(rand.Intn(1000))), CompletionEvent: []byte("CompletionEvent"), CompletionEventEncoding: "CompletionEventEncoding", TaskList: "TaskList", TaskListKind: types.TaskListKindNormal, WorkflowTypeName: "WorkflowTypeName", WorkflowTimeout: time.Minute * time.Duration(rand.Intn(10)), DecisionTaskTimeout: time.Minute * time.Duration(rand.Intn(10)), ExecutionContext: []byte("ExecutionContext"), State: int32(rand.Intn(1000)), CloseStatus: int32(rand.Intn(1000)), StartVersion: int64(rand.Intn(1000)), LastWriteEventID: common.Int64Ptr(int64(rand.Intn(1000))), LastEventTaskID: int64(rand.Intn(1000)), LastFirstEventID: int64(rand.Intn(1000)), LastProcessedEvent: int64(rand.Intn(1000)), StartTimestamp: time.UnixMilli(1752018142820), LastUpdatedTimestamp: time.UnixMilli(1752018142821), DecisionVersion: int64(rand.Intn(1000)), DecisionScheduleID: int64(rand.Intn(1000)), DecisionStartedID: int64(rand.Intn(1000)), DecisionTimeout: time.Minute * time.Duration(rand.Intn(10)), DecisionAttempt: int64(rand.Intn(1000)), DecisionStartedTimestamp: time.UnixMilli(1752018142822), DecisionScheduledTimestamp: time.UnixMilli(1752018142823), CancelRequested: true, DecisionOriginalScheduledTimestamp: time.UnixMilli(1752018142824), CreateRequestID: "CreateRequestID", DecisionRequestID: "DecisionRequestID", CancelRequestID: "CancelRequestID", StickyTaskList: "StickyTaskList", StickyScheduleToStartTimeout: time.Minute * time.Duration(rand.Intn(10)), RetryAttempt: int64(rand.Intn(1000)), RetryInitialInterval: time.Minute * time.Duration(rand.Intn(10)), RetryMaximumInterval: time.Minute * time.Duration(rand.Intn(10)), RetryMaximumAttempts: int32(rand.Intn(1000)), RetryExpiration: time.Minute * time.Duration(rand.Intn(10)), RetryBackoffCoefficient: rand.Float64() * 1000, RetryExpirationTimestamp: time.UnixMilli(1752018142825), RetryNonRetryableErrors: []string{"RetryNonRetryableErrors"}, HasRetryPolicy: true, CronSchedule: "CronSchedule", CronOverlapPolicy: types.CronOverlapPolicySkipped, EventStoreVersion: int32(rand.Intn(1000)), EventBranchToken: []byte("EventBranchToken"), SignalCount: int64(rand.Intn(1000)), HistorySize: int64(rand.Intn(1000)), ClientLibraryVersion: "ClientLibraryVersion", ClientFeatureVersion: "ClientFeatureVersion", ClientImpl: "ClientImpl", AutoResetPoints: []byte("AutoResetPoints"), AutoResetPointsEncoding: "AutoResetPointsEncoding", SearchAttributes: map[string][]byte{"key_1": []byte("SearchAttributes")}, Memo: map[string][]byte{"key_1": []byte("Memo")}, VersionHistories: []byte("VersionHistories"), VersionHistoriesEncoding: "VersionHistoriesEncoding", FirstExecutionRunID: UUID(uuid.New()), PartitionConfig: map[string]string{"zone": "dca1"}, Checksum: []byte("Checksum"), ChecksumEncoding: "ChecksumEncoding", IsCron: true, } actual := workflowExecutionInfoFromThrift(workflowExecutionInfoToThrift(expected)) assert.Equal(t, expected, actual) assert.Nil(t, workflowExecutionInfoFromThrift(nil)) assert.Nil(t, workflowExecutionInfoToThrift(nil)) } func TestWorkflowExecutionInfo_NoTaskListKind(t *testing.T) { expected := &WorkflowExecutionInfo{ ParentDomainID: UUID(uuid.New()), ParentWorkflowID: "ParentWorkflowID", ParentRunID: UUID(uuid.New()), InitiatedID: int64(rand.Intn(1000)), CompletionEventBatchID: common.Int64Ptr(int64(rand.Intn(1000))), CompletionEvent: []byte("CompletionEvent"), CompletionEventEncoding: "CompletionEventEncoding", TaskList: "TaskList", TaskListKind: types.TaskListKindNormal, WorkflowTypeName: "WorkflowTypeName", WorkflowTimeout: time.Minute * time.Duration(rand.Intn(10)), DecisionTaskTimeout: time.Minute * time.Duration(rand.Intn(10)), ExecutionContext: []byte("ExecutionContext"), State: int32(rand.Intn(1000)), CloseStatus: int32(rand.Intn(1000)), StartVersion: int64(rand.Intn(1000)), LastWriteEventID: common.Int64Ptr(int64(rand.Intn(1000))), LastEventTaskID: int64(rand.Intn(1000)), LastFirstEventID: int64(rand.Intn(1000)), LastProcessedEvent: int64(rand.Intn(1000)), StartTimestamp: time.UnixMilli(1752018142820), LastUpdatedTimestamp: time.UnixMilli(1752018142821), DecisionVersion: int64(rand.Intn(1000)), DecisionScheduleID: int64(rand.Intn(1000)), DecisionStartedID: int64(rand.Intn(1000)), DecisionTimeout: time.Minute * time.Duration(rand.Intn(10)), DecisionAttempt: int64(rand.Intn(1000)), DecisionStartedTimestamp: time.UnixMilli(1752018142822), DecisionScheduledTimestamp: time.UnixMilli(1752018142823), CancelRequested: true, DecisionOriginalScheduledTimestamp: time.UnixMilli(1752018142824), CreateRequestID: "CreateRequestID", DecisionRequestID: "DecisionRequestID", CancelRequestID: "CancelRequestID", StickyTaskList: "StickyTaskList", StickyScheduleToStartTimeout: time.Minute * time.Duration(rand.Intn(10)), RetryAttempt: int64(rand.Intn(1000)), RetryInitialInterval: time.Minute * time.Duration(rand.Intn(10)), RetryMaximumInterval: time.Minute * time.Duration(rand.Intn(10)), RetryMaximumAttempts: int32(rand.Intn(1000)), RetryExpiration: time.Minute * time.Duration(rand.Intn(10)), RetryBackoffCoefficient: rand.Float64() * 1000, RetryExpirationTimestamp: time.UnixMilli(1752018142825), RetryNonRetryableErrors: []string{"RetryNonRetryableErrors"}, HasRetryPolicy: true, CronSchedule: "CronSchedule", CronOverlapPolicy: types.CronOverlapPolicySkipped, EventStoreVersion: int32(rand.Intn(1000)), EventBranchToken: []byte("EventBranchToken"), SignalCount: int64(rand.Intn(1000)), HistorySize: int64(rand.Intn(1000)), ClientLibraryVersion: "ClientLibraryVersion", ClientFeatureVersion: "ClientFeatureVersion", ClientImpl: "ClientImpl", AutoResetPoints: []byte("AutoResetPoints"), AutoResetPointsEncoding: "AutoResetPointsEncoding", SearchAttributes: map[string][]byte{"key_1": []byte("SearchAttributes")}, Memo: map[string][]byte{"key_1": []byte("Memo")}, VersionHistories: []byte("VersionHistories"), VersionHistoriesEncoding: "VersionHistoriesEncoding", FirstExecutionRunID: UUID(uuid.New()), PartitionConfig: map[string]string{"zone": "dca1"}, Checksum: []byte("Checksum"), ChecksumEncoding: "ChecksumEncoding", IsCron: true, } thriftVersion := workflowExecutionInfoToThrift(expected) // Nil should come back as NORMAL, the default value thriftVersion.TaskListKind = nil actual := workflowExecutionInfoFromThrift(thriftVersion) assert.Equal(t, expected, actual) } func TestActivityInfo(t *testing.T) { expected := &ActivityInfo{ Version: int64(rand.Intn(1000)), ScheduledEventBatchID: int64(rand.Intn(1000)), ScheduledEvent: []byte("ScheduledEvent"), ScheduledEventEncoding: "ScheduledEventEncoding", ScheduledTimestamp: time.UnixMilli(1752018142820), StartedID: int64(rand.Intn(1000)), StartedEvent: []byte("StartedEvent"), StartedEventEncoding: "StartedEventEncoding", StartedTimestamp: time.UnixMilli(1752018142821), ActivityID: "ActivityID", RequestID: "RequestID", ScheduleToStartTimeout: time.Minute * time.Duration(rand.Intn(10)), ScheduleToCloseTimeout: time.Minute * time.Duration(rand.Intn(10)), StartToCloseTimeout: time.Minute * time.Duration(rand.Intn(10)), HeartbeatTimeout: time.Minute * time.Duration(rand.Intn(10)), CancelRequested: true, CancelRequestID: int64(rand.Intn(1000)), TimerTaskStatus: int32(rand.Intn(1000)), Attempt: int32(rand.Intn(1000)), TaskList: "TaskList", TaskListKind: types.TaskListKindEphemeral, StartedIdentity: "StartedIdentity", HasRetryPolicy: true, RetryInitialInterval: time.Minute * time.Duration(rand.Intn(10)), RetryMaximumInterval: time.Minute * time.Duration(rand.Intn(10)), RetryMaximumAttempts: int32(rand.Intn(1000)), RetryExpirationTimestamp: time.Time{}, RetryBackoffCoefficient: rand.Float64() * 1000, RetryNonRetryableErrors: []string{"RetryNonRetryableErrors"}, RetryLastFailureReason: "RetryLastFailureReason", RetryLastWorkerIdentity: "RetryLastWorkerIdentity", RetryLastFailureDetails: []byte("RetryLastFailureDetails"), } actual := activityInfoFromThrift(activityInfoToThrift(expected)) assert.Equal(t, expected, actual) assert.Nil(t, activityInfoFromThrift(nil)) assert.Nil(t, activityInfoToThrift(nil)) } func TestChildExecutionInfo(t *testing.T) { expected := &ChildExecutionInfo{ Version: int64(rand.Intn(1000)), InitiatedEventBatchID: int64(rand.Intn(1000)), StartedID: int64(rand.Intn(1000)), InitiatedEvent: []byte("InitiatedEvent"), InitiatedEventEncoding: "InitiatedEventEncoding", StartedWorkflowID: "InitiatedEventEncoding", StartedRunID: UUID(uuid.New()), StartedEvent: []byte("StartedEvent"), StartedEventEncoding: "StartedEventEncoding", CreateRequestID: "CreateRequestID", DomainID: "DomainID", DomainNameDEPRECATED: "DomainName", WorkflowTypeName: "WorkflowTypeName", ParentClosePolicy: int32(rand.Intn(1000)), } actual := childExecutionInfoFromThrift(childExecutionInfoToThrift(expected)) assert.Equal(t, expected, actual) assert.Nil(t, childExecutionInfoFromThrift(nil)) assert.Nil(t, childExecutionInfoToThrift(nil)) } func TestSignalInfo(t *testing.T) { expected := &SignalInfo{ Version: int64(rand.Intn(1000)), InitiatedEventBatchID: int64(rand.Intn(1000)), RequestID: "RequestID", Name: "Name", Input: []byte("Input"), Control: []byte("Control"), } actual := signalInfoFromThrift(signalInfoToThrift(expected)) assert.Equal(t, expected, actual) assert.Nil(t, signalInfoFromThrift(nil)) assert.Nil(t, signalInfoToThrift(nil)) } func TestRequestCancelInfo(t *testing.T) { expected := &RequestCancelInfo{ Version: int64(rand.Intn(1000)), InitiatedEventBatchID: int64(rand.Intn(1000)), CancelRequestID: "CancelRequestID", } actual := requestCancelInfoFromThrift(requestCancelInfoToThrift(expected)) assert.Equal(t, expected, actual) assert.Nil(t, requestCancelInfoFromThrift(nil)) assert.Nil(t, requestCancelInfoToThrift(nil)) } func TestTimerInfo(t *testing.T) { expected := &TimerInfo{ Version: int64(rand.Intn(1000)), StartedID: int64(rand.Intn(1000)), ExpiryTimestamp: time.Now(), TaskID: int64(rand.Intn(1000)), } actual := timerInfoFromThrift(timerInfoToThrift(expected)) assert.Equal(t, expected.Version, actual.Version) assert.Equal(t, expected.StartedID, actual.StartedID) assert.Equal(t, expected.TaskID, actual.TaskID) assert.Equal(t, expected.ExpiryTimestamp.Sub(actual.ExpiryTimestamp), time.Duration(0)) assert.Nil(t, timerInfoFromThrift(nil)) assert.Nil(t, timerInfoToThrift(nil)) } func TestTaskInfo(t *testing.T) { expected := &TaskInfo{ WorkflowID: "WorkflowID", RunID: UUID(uuid.New()), ScheduleID: int64(rand.Intn(1000)), ExpiryTimestamp: time.Now(), CreatedTimestamp: time.Now(), PartitionConfig: map[string]string{"zone": "dca1"}, } actual := taskInfoFromThrift(taskInfoToThrift(expected)) assert.Equal(t, expected.WorkflowID, actual.WorkflowID) assert.Equal(t, expected.RunID, actual.RunID) assert.Equal(t, expected.ScheduleID, actual.ScheduleID) assert.Equal(t, expected.ExpiryTimestamp.Sub(actual.ExpiryTimestamp), time.Duration(0)) assert.Equal(t, expected.CreatedTimestamp.Sub(actual.CreatedTimestamp), time.Duration(0)) assert.Equal(t, expected.PartitionConfig, actual.PartitionConfig) assert.Nil(t, taskInfoFromThrift(nil)) assert.Nil(t, taskInfoToThrift(nil)) } func TestTaskListInfo(t *testing.T) { cases := []*TaskListInfo{ nil, { Kind: 0, AckLevel: 1, ExpiryTimestamp: time.UnixMicro(2), LastUpdated: time.UnixMicro(3), AdaptivePartitionConfig: &TaskListPartitionConfig{ Version: 4, NumReadPartitions: 1, ReadPartitions: map[int32]*TaskListPartition{ 0: { IsolationGroups: []string{"foo"}, }, }, NumWritePartitions: 2, WritePartitions: map[int32]*TaskListPartition{ 0: { IsolationGroups: []string{"foo"}, }, 1: { IsolationGroups: []string{"bar"}, }, }, }, }, { Kind: 0, AckLevel: 1, ExpiryTimestamp: time.UnixMicro(2), LastUpdated: time.UnixMicro(3), AdaptivePartitionConfig: &TaskListPartitionConfig{ Version: 4, NumReadPartitions: 10, NumWritePartitions: 2, }, }, } for i, info := range cases { assert.Equal(t, info, taskListInfoFromThrift(taskListInfoToThrift(info)), "case %d", i) } } func TestTransferTaskInfo(t *testing.T) { expected := &TransferTaskInfo{ DomainID: UUID(uuid.New()), WorkflowID: "WorkflowID", RunID: UUID(uuid.New()), TaskType: int16(rand.Intn(1000)), TargetDomainID: UUID(uuid.New()), TargetDomainIDs: []UUID{UUID(uuid.New()), UUID(uuid.New())}, TargetWorkflowID: "TargetWorkflowID", TargetRunID: UUID(uuid.New()), TaskList: "TaskList", TargetChildWorkflowOnly: true, ScheduleID: int64(rand.Intn(1000)), Version: int64(rand.Intn(1000)), OriginalTaskList: "OriginalTaskList", } actual := transferTaskInfoFromThrift(transferTaskInfoToThrift(expected)) assert.Equal(t, expected, actual) assert.Nil(t, transferTaskInfoFromThrift(nil)) assert.Nil(t, transferTaskInfoToThrift(nil)) } func TestTimerTaskInfo(t *testing.T) { expected := &TimerTaskInfo{ DomainID: UUID(uuid.New()), WorkflowID: "WorkflowID", RunID: UUID(uuid.New()), TaskType: int16(rand.Intn(1000)), TimeoutType: common.Int16Ptr(int16(rand.Intn(1000))), Version: int64(rand.Intn(1000)), ScheduleAttempt: int64(rand.Intn(1000)), EventID: int64(rand.Intn(1000)), TaskList: "TaskList", } actual := timerTaskInfoFromThrift(timerTaskInfoToThrift(expected)) assert.Equal(t, expected, actual) assert.Nil(t, timerTaskInfoFromThrift(nil)) assert.Nil(t, timerTaskInfoToThrift(nil)) } func TestReplicationTaskInfo(t *testing.T) { expected := &ReplicationTaskInfo{ DomainID: UUID(uuid.New()), WorkflowID: "WorkflowID", RunID: UUID(uuid.New()), TaskType: int16(rand.Intn(1000)), Version: int64(rand.Intn(1000)), FirstEventID: int64(rand.Intn(1000)), NextEventID: int64(rand.Intn(1000)), ScheduledID: int64(rand.Intn(1000)), EventStoreVersion: int32(rand.Intn(1000)), NewRunEventStoreVersion: int32(rand.Intn(1000)), BranchToken: []byte("BranchToken"), NewRunBranchToken: []byte("NewRunBranchToken"), } actual := replicationTaskInfoFromThrift(replicationTaskInfoToThrift(expected)) assert.Equal(t, expected, actual) assert.Nil(t, replicationTaskInfoFromThrift(nil)) assert.Nil(t, replicationTaskInfoToThrift(nil)) } func TestCronOverlapPolicyFromThrift(t *testing.T) { cases := []struct { thrift *shared.CronOverlapPolicy actual types.CronOverlapPolicy }{ {nil, types.CronOverlapPolicySkipped}, {shared.CronOverlapPolicyBufferone.Ptr(), types.CronOverlapPolicyBufferOne}, {shared.CronOverlapPolicySkipped.Ptr(), types.CronOverlapPolicySkipped}, } for _, c := range cases { assert.Equal(t, c.actual, cronOverlapPolicyFromThrift(c.thrift)) } } ================================================ FILE: common/persistence/serialization/uuid.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package serialization import ( "database/sql/driver" "encoding/hex" "github.com/google/uuid" ) // UUID represents a 16-byte universally unique identifier // this type is a wrapper around google/uuid with the following differences // - type is a byte slice instead of [16]byte // - db serialization converts uuid to bytes as opposed to string type UUID []byte // MustParseUUID returns a UUID parsed from the given string representation // returns nil if the input is empty string // panics if the given input is malformed func MustParseUUID(s string) UUID { if s == "" { return nil } u := uuid.MustParse(s) return u[:] } // UUIDPtr simply returns a pointer for the given value type func UUIDPtr(u UUID) *UUID { return &u } // String returns the 36 byet hexstring representation of this uuid // return empty string if this uuid is nil func (u UUID) String() string { if len(u) != 16 { return "" } var buf [36]byte u.encodeHex(buf[:]) return string(buf[:]) } // Scan implements sql.Scanner interface to allow this type to be // parsed transparently by database drivers func (u *UUID) Scan(src interface{}) error { if src == nil { return nil } guuid := &uuid.UUID{} if err := guuid.Scan(src); err != nil { return err } *u = (*guuid)[:] return nil } // Value implements sql.Valuer so that UUIDs can be written to databases // transparently. This method returns a byte slice representation of uuid func (u UUID) Value() (driver.Value, error) { return []byte(u), nil } func (u UUID) encodeHex(dst []byte) { hex.Encode(dst, u[:4]) dst[8] = '-' hex.Encode(dst[9:13], u[4:6]) dst[13] = '-' hex.Encode(dst[14:18], u[6:8]) dst[18] = '-' hex.Encode(dst[19:23], u[8:10]) dst[23] = '-' hex.Encode(dst[24:], u[10:]) } ================================================ FILE: common/persistence/serializer.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "encoding/json" "fmt" "github.com/golang/snappy" "github.com/uber/cadence/.gen/go/config" "github.com/uber/cadence/.gen/go/history" "github.com/uber/cadence/.gen/go/replicator" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) //go:generate mockgen -package $GOPACKAGE -destination serializer_mock.go -self_package github.com/uber/cadence/common/persistence github.com/uber/cadence/common/persistence PayloadSerializer type ( // PayloadSerializer is used by persistence to serialize/deserialize history event(s) and others // It will only be used inside persistence, so that serialize/deserialize is transparent for application PayloadSerializer interface { // serialize/deserialize history events SerializeBatchEvents(batch []*types.HistoryEvent, encodingType constants.EncodingType) (*DataBlob, error) DeserializeBatchEvents(data *DataBlob) ([]*types.HistoryEvent, error) // serialize/deserialize a single history event SerializeEvent(event *types.HistoryEvent, encodingType constants.EncodingType) (*DataBlob, error) DeserializeEvent(data *DataBlob) (*types.HistoryEvent, error) // serialize/deserialize visibility memo fields SerializeVisibilityMemo(memo *types.Memo, encodingType constants.EncodingType) (*DataBlob, error) DeserializeVisibilityMemo(data *DataBlob) (*types.Memo, error) // serialize/deserialize reset points SerializeResetPoints(event *types.ResetPoints, encodingType constants.EncodingType) (*DataBlob, error) DeserializeResetPoints(data *DataBlob) (*types.ResetPoints, error) // serialize/deserialize bad binaries SerializeBadBinaries(event *types.BadBinaries, encodingType constants.EncodingType) (*DataBlob, error) DeserializeBadBinaries(data *DataBlob) (*types.BadBinaries, error) // serialize/deserialize version histories SerializeVersionHistories(histories *types.VersionHistories, encodingType constants.EncodingType) (*DataBlob, error) DeserializeVersionHistories(data *DataBlob) (*types.VersionHistories, error) // serialize/deserialize pending failover markers SerializePendingFailoverMarkers(markers []*types.FailoverMarkerAttributes, encodingType constants.EncodingType) (*DataBlob, error) DeserializePendingFailoverMarkers(data *DataBlob) ([]*types.FailoverMarkerAttributes, error) // serialize/deserialize processing queue statesss SerializeProcessingQueueStates(states *types.ProcessingQueueStates, encodingType constants.EncodingType) (*DataBlob, error) DeserializeProcessingQueueStates(data *DataBlob) (*types.ProcessingQueueStates, error) // serialize/deserialize DynamicConfigBlob SerializeDynamicConfigBlob(blob *types.DynamicConfigBlob, encodingType constants.EncodingType) (*DataBlob, error) DeserializeDynamicConfigBlob(data *DataBlob) (*types.DynamicConfigBlob, error) // serialize/deserialize IsolationGroupConfiguration SerializeIsolationGroups(event *types.IsolationGroupConfiguration, encodingType constants.EncodingType) (*DataBlob, error) DeserializeIsolationGroups(data *DataBlob) (*types.IsolationGroupConfiguration, error) // serialize/deserialize async workflow configuration SerializeAsyncWorkflowsConfig(config *types.AsyncWorkflowConfiguration, encodingType constants.EncodingType) (*DataBlob, error) DeserializeAsyncWorkflowsConfig(data *DataBlob) (*types.AsyncWorkflowConfiguration, error) // serialize/deserialize checksum SerializeChecksum(sum checksum.Checksum, encodingType constants.EncodingType) (*DataBlob, error) DeserializeChecksum(data *DataBlob) (checksum.Checksum, error) // serialize/deserialize active clusters config SerializeActiveClusters(activeClusters *types.ActiveClusters, encodingType constants.EncodingType) (*DataBlob, error) DeserializeActiveClusters(data *DataBlob) (*types.ActiveClusters, error) // serialize/deserialize active cluster selection policy SerializeActiveClusterSelectionPolicy(policy *types.ActiveClusterSelectionPolicy, encodingType constants.EncodingType) (*DataBlob, error) DeserializeActiveClusterSelectionPolicy(data *DataBlob) (*types.ActiveClusterSelectionPolicy, error) } // CadenceSerializationError is an error type for cadence serialization CadenceSerializationError struct { msg string } // CadenceDeserializationError is an error type for cadence deserialization CadenceDeserializationError struct { msg string } // UnknownEncodingTypeError is an error type for unknown or unsupported encoding type UnknownEncodingTypeError struct { encodingType constants.EncodingType } serializerImpl struct { thriftrwEncoder codec.BinaryEncoder } ) // NewPayloadSerializer returns a PayloadSerializer func NewPayloadSerializer() PayloadSerializer { return &serializerImpl{ thriftrwEncoder: codec.NewThriftRWEncoder(), } } func (t *serializerImpl) SerializeBatchEvents(events []*types.HistoryEvent, encodingType constants.EncodingType) (*DataBlob, error) { return t.serialize(events, encodingType) } func (t *serializerImpl) DeserializeBatchEvents(data *DataBlob) ([]*types.HistoryEvent, error) { if data == nil { return nil, nil } var events []*types.HistoryEvent if data != nil && len(data.Data) == 0 { return events, nil } err := t.deserialize(data, &events) return events, err } func (t *serializerImpl) SerializeEvent(event *types.HistoryEvent, encodingType constants.EncodingType) (*DataBlob, error) { if event == nil { return nil, nil } return t.serialize(event, encodingType) } func (t *serializerImpl) DeserializeEvent(data *DataBlob) (*types.HistoryEvent, error) { if data == nil { return nil, nil } var event types.HistoryEvent err := t.deserialize(data, &event) return &event, err } func (t *serializerImpl) SerializeResetPoints(rp *types.ResetPoints, encodingType constants.EncodingType) (*DataBlob, error) { if rp == nil { rp = &types.ResetPoints{} } return t.serialize(rp, encodingType) } func (t *serializerImpl) DeserializeResetPoints(data *DataBlob) (*types.ResetPoints, error) { var rp types.ResetPoints err := t.deserialize(data, &rp) return &rp, err } func (t *serializerImpl) SerializeBadBinaries(bb *types.BadBinaries, encodingType constants.EncodingType) (*DataBlob, error) { if bb == nil { bb = &types.BadBinaries{} } return t.serialize(bb, encodingType) } func (t *serializerImpl) DeserializeBadBinaries(data *DataBlob) (*types.BadBinaries, error) { var bb types.BadBinaries err := t.deserialize(data, &bb) return &bb, err } func (t *serializerImpl) SerializeVisibilityMemo(memo *types.Memo, encodingType constants.EncodingType) (*DataBlob, error) { if memo == nil { // Return nil here to be consistent with Event // This check is not duplicate as check in following serialize return nil, nil } return t.serialize(memo, encodingType) } func (t *serializerImpl) DeserializeVisibilityMemo(data *DataBlob) (*types.Memo, error) { var memo types.Memo err := t.deserialize(data, &memo) return &memo, err } func (t *serializerImpl) SerializeVersionHistories(histories *types.VersionHistories, encodingType constants.EncodingType) (*DataBlob, error) { if histories == nil { return nil, nil } return t.serialize(histories, encodingType) } func (t *serializerImpl) DeserializeVersionHistories(data *DataBlob) (*types.VersionHistories, error) { var histories types.VersionHistories err := t.deserialize(data, &histories) return &histories, err } func (t *serializerImpl) SerializePendingFailoverMarkers(markers []*types.FailoverMarkerAttributes, encodingType constants.EncodingType) (*DataBlob, error) { if markers == nil { return nil, nil } return t.serialize(markers, encodingType) } func (t *serializerImpl) DeserializePendingFailoverMarkers(data *DataBlob) ([]*types.FailoverMarkerAttributes, error) { if data == nil { return nil, nil } var markers []*types.FailoverMarkerAttributes if data != nil && len(data.Data) == 0 { return markers, nil } err := t.deserialize(data, &markers) return markers, err } func (t *serializerImpl) SerializeProcessingQueueStates(states *types.ProcessingQueueStates, encodingType constants.EncodingType) (*DataBlob, error) { if states == nil { return nil, nil } return t.serialize(states, encodingType) } func (t *serializerImpl) DeserializeProcessingQueueStates(data *DataBlob) (*types.ProcessingQueueStates, error) { if data == nil { return nil, nil } var states types.ProcessingQueueStates if data != nil && len(data.Data) == 0 { return &states, nil } err := t.deserialize(data, &states) return &states, err } func (t *serializerImpl) SerializeDynamicConfigBlob(blob *types.DynamicConfigBlob, encodingType constants.EncodingType) (*DataBlob, error) { if blob == nil { return nil, nil } return t.serialize(blob, encodingType) } func (t *serializerImpl) DeserializeDynamicConfigBlob(data *DataBlob) (*types.DynamicConfigBlob, error) { if data == nil { return nil, nil } var blob types.DynamicConfigBlob if len(data.Data) == 0 { return &blob, nil } err := t.deserialize(data, &blob) return &blob, err } func (t *serializerImpl) SerializeIsolationGroups(c *types.IsolationGroupConfiguration, encodingType constants.EncodingType) (*DataBlob, error) { if c == nil { return nil, nil } return t.serialize(c, encodingType) } func (t *serializerImpl) DeserializeIsolationGroups(data *DataBlob) (*types.IsolationGroupConfiguration, error) { if data == nil { return nil, nil } var cfg types.IsolationGroupConfiguration if len(data.Data) == 0 { return &cfg, nil } err := t.deserialize(data, &cfg) return &cfg, err } func (t *serializerImpl) SerializeAsyncWorkflowsConfig(c *types.AsyncWorkflowConfiguration, encodingType constants.EncodingType) (*DataBlob, error) { if c == nil { return nil, nil } return t.serialize(c, encodingType) } func (t *serializerImpl) DeserializeAsyncWorkflowsConfig(data *DataBlob) (*types.AsyncWorkflowConfiguration, error) { if data == nil { return nil, nil } var cfg types.AsyncWorkflowConfiguration if len(data.Data) == 0 { return &cfg, nil } err := t.deserialize(data, &cfg) return &cfg, err } func (t *serializerImpl) SerializeChecksum(sum checksum.Checksum, encodingType constants.EncodingType) (*DataBlob, error) { if len(sum.Value) == 0 { return nil, nil } return t.serialize(sum, encodingType) } func (t *serializerImpl) DeserializeChecksum(data *DataBlob) (checksum.Checksum, error) { if data == nil { return checksum.Checksum{}, nil } var sum checksum.Checksum if len(data.Data) == 0 { return sum, nil } err := t.deserialize(data, &sum) if err != nil { return checksum.Checksum{}, err } return sum, err } func (t *serializerImpl) SerializeActiveClusters(activeClusters *types.ActiveClusters, encodingType constants.EncodingType) (*DataBlob, error) { if activeClusters == nil { return nil, nil } return t.serialize(activeClusters, encodingType) } func (t *serializerImpl) DeserializeActiveClusters(data *DataBlob) (*types.ActiveClusters, error) { if data == nil { return nil, nil } var activeClusters types.ActiveClusters if len(data.Data) == 0 { return &activeClusters, nil } err := t.deserialize(data, &activeClusters) return &activeClusters, err } func (t *serializerImpl) SerializeActiveClusterSelectionPolicy(policy *types.ActiveClusterSelectionPolicy, encodingType constants.EncodingType) (*DataBlob, error) { if policy == nil { return nil, nil } return t.serialize(policy, encodingType) } func (t *serializerImpl) DeserializeActiveClusterSelectionPolicy(data *DataBlob) (*types.ActiveClusterSelectionPolicy, error) { if data == nil { return nil, nil } var policy types.ActiveClusterSelectionPolicy err := t.deserialize(data, &policy) return &policy, err } func (t *serializerImpl) serialize(input interface{}, encodingType constants.EncodingType) (*DataBlob, error) { if input == nil { return nil, nil } var data []byte var err error switch encodingType { case constants.EncodingTypeThriftRW: data, err = t.thriftrwEncode(input) case constants.EncodingTypeThriftRWSnappy: data, err = t.thriftrwsnappyEncode(input) case constants.EncodingTypeJSON, constants.EncodingTypeUnknown, constants.EncodingTypeEmpty: // For backward-compatibility encodingType = constants.EncodingTypeJSON data, err = json.Marshal(input) default: return nil, NewUnknownEncodingTypeError(encodingType) } if err != nil { return nil, NewCadenceSerializationError(err.Error()) } return NewDataBlob(data, encodingType), nil } func (t *serializerImpl) thriftrwEncode(input interface{}) ([]byte, error) { switch input := input.(type) { case []*types.HistoryEvent: return t.thriftrwEncoder.Encode(&workflow.History{Events: thrift.FromHistoryEventArray(input)}) case *types.HistoryEvent: return t.thriftrwEncoder.Encode(thrift.FromHistoryEvent(input)) case *types.Memo: return t.thriftrwEncoder.Encode(thrift.FromMemo(input)) case *types.ResetPoints: return t.thriftrwEncoder.Encode(thrift.FromResetPoints(input)) case *types.BadBinaries: return t.thriftrwEncoder.Encode(thrift.FromBadBinaries(input)) case *types.VersionHistories: return t.thriftrwEncoder.Encode(thrift.FromVersionHistories(input)) case []*types.FailoverMarkerAttributes: return t.thriftrwEncoder.Encode(&replicator.FailoverMarkers{FailoverMarkers: thrift.FromFailoverMarkerAttributesArray(input)}) case *types.ProcessingQueueStates: return t.thriftrwEncoder.Encode(thrift.FromProcessingQueueStates(input)) case *types.DynamicConfigBlob: return t.thriftrwEncoder.Encode(thrift.FromDynamicConfigBlob(input)) case *types.IsolationGroupConfiguration: return t.thriftrwEncoder.Encode(thrift.FromIsolationGroupConfig(input)) case *types.AsyncWorkflowConfiguration: return t.thriftrwEncoder.Encode(thrift.FromDomainAsyncWorkflowConfiguraton(input)) case *types.ActiveClusters: return t.thriftrwEncoder.Encode(thrift.FromActiveClusters(input)) case *types.ActiveClusterSelectionPolicy: return t.thriftrwEncoder.Encode(thrift.FromActiveClusterSelectionPolicy(input)) default: return nil, nil } } func (t *serializerImpl) deserialize(data *DataBlob, target interface{}) error { if data == nil { return nil } if len(data.Data) == 0 { return NewCadenceDeserializationError("DeserializeEvent empty data") } var err error switch data.GetEncoding() { case constants.EncodingTypeThriftRW: err = t.thriftrwDecode(data.Data, target) case constants.EncodingTypeThriftRWSnappy: err = t.thriftrwsnappyDecode(data.Data, target) case constants.EncodingTypeJSON, constants.EncodingTypeUnknown, constants.EncodingTypeEmpty: // For backward-compatibility err = json.Unmarshal(data.Data, target) default: return NewUnknownEncodingTypeError(data.GetEncoding()) } if err != nil { return NewCadenceDeserializationError(fmt.Sprintf("DeserializeBatchEvents encoding: \"%v\", error: %v", data.Encoding, err.Error())) } return nil } func (t *serializerImpl) thriftrwDecode(data []byte, target interface{}) error { switch target := target.(type) { case *[]*types.HistoryEvent: thriftTarget := workflow.History{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = thrift.ToHistoryEventArray(thriftTarget.GetEvents()) return nil case *types.HistoryEvent: thriftTarget := workflow.HistoryEvent{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToHistoryEvent(&thriftTarget) return nil case *types.Memo: thriftTarget := workflow.Memo{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToMemo(&thriftTarget) return nil case *types.ResetPoints: thriftTarget := workflow.ResetPoints{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToResetPoints(&thriftTarget) return nil case *types.BadBinaries: thriftTarget := workflow.BadBinaries{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToBadBinaries(&thriftTarget) return nil case *types.VersionHistories: thriftTarget := workflow.VersionHistories{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToVersionHistories(&thriftTarget) return nil case *[]*types.FailoverMarkerAttributes: thriftTarget := replicator.FailoverMarkers{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = thrift.ToFailoverMarkerAttributesArray(thriftTarget.GetFailoverMarkers()) return nil case *types.ProcessingQueueStates: thriftTarget := history.ProcessingQueueStates{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToProcessingQueueStates(&thriftTarget) return nil case *types.DynamicConfigBlob: thriftTarget := config.DynamicConfigBlob{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToDynamicConfigBlob(&thriftTarget) return nil case *types.IsolationGroupConfiguration: thriftTarget := workflow.IsolationGroupConfiguration{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToIsolationGroupConfig(&thriftTarget) return nil case *types.AsyncWorkflowConfiguration: thriftTarget := workflow.AsyncWorkflowConfiguration{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToDomainAsyncWorkflowConfiguraton(&thriftTarget) return nil case *types.ActiveClusters: thriftTarget := workflow.ActiveClusters{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToActiveClusters(&thriftTarget) return nil case *types.ActiveClusterSelectionPolicy: thriftTarget := workflow.ActiveClusterSelectionPolicy{} if err := t.thriftrwEncoder.Decode(data, &thriftTarget); err != nil { return err } *target = *thrift.ToActiveClusterSelectionPolicy(&thriftTarget) return nil default: return nil } } func (t *serializerImpl) thriftrwsnappyEncode(input interface{}) ([]byte, error) { data, err := t.thriftrwEncode(input) if err != nil { return nil, err } if data == nil { return nil, nil } return snappy.Encode(nil, data), nil } func (t *serializerImpl) thriftrwsnappyDecode(data []byte, target interface{}) error { decompressed, err := snappy.Decode(nil, data) if err != nil { return err } return t.thriftrwDecode(decompressed, target) } // NewUnknownEncodingTypeError returns a new instance of encoding type error func NewUnknownEncodingTypeError(encodingType constants.EncodingType) error { return &UnknownEncodingTypeError{encodingType: encodingType} } func (e *UnknownEncodingTypeError) Error() string { return fmt.Sprintf("unknown or unsupported encoding type %v", e.encodingType) } // NewCadenceSerializationError returns a CadenceSerializationError func NewCadenceSerializationError(msg string) *CadenceSerializationError { return &CadenceSerializationError{msg: msg} } func (e *CadenceSerializationError) Error() string { return fmt.Sprintf("cadence serialization error: %v", e.msg) } // NewCadenceDeserializationError returns a CadenceDeserializationError func NewCadenceDeserializationError(msg string) *CadenceDeserializationError { return &CadenceDeserializationError{msg: msg} } func (e *CadenceDeserializationError) Error() string { return fmt.Sprintf("cadence deserialization error: %v", e.msg) } ================================================ FILE: common/persistence/serializer_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/persistence (interfaces: PayloadSerializer) // // Generated by this command: // // mockgen -package persistence -destination serializer_mock.go -self_package github.com/uber/cadence/common/persistence github.com/uber/cadence/common/persistence PayloadSerializer // // Package persistence is a generated GoMock package. package persistence import ( reflect "reflect" gomock "go.uber.org/mock/gomock" checksum "github.com/uber/cadence/common/checksum" constants "github.com/uber/cadence/common/constants" types "github.com/uber/cadence/common/types" ) // MockPayloadSerializer is a mock of PayloadSerializer interface. type MockPayloadSerializer struct { ctrl *gomock.Controller recorder *MockPayloadSerializerMockRecorder isgomock struct{} } // MockPayloadSerializerMockRecorder is the mock recorder for MockPayloadSerializer. type MockPayloadSerializerMockRecorder struct { mock *MockPayloadSerializer } // NewMockPayloadSerializer creates a new mock instance. func NewMockPayloadSerializer(ctrl *gomock.Controller) *MockPayloadSerializer { mock := &MockPayloadSerializer{ctrl: ctrl} mock.recorder = &MockPayloadSerializerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPayloadSerializer) EXPECT() *MockPayloadSerializerMockRecorder { return m.recorder } // DeserializeActiveClusterSelectionPolicy mocks base method. func (m *MockPayloadSerializer) DeserializeActiveClusterSelectionPolicy(data *DataBlob) (*types.ActiveClusterSelectionPolicy, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeActiveClusterSelectionPolicy", data) ret0, _ := ret[0].(*types.ActiveClusterSelectionPolicy) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeActiveClusterSelectionPolicy indicates an expected call of DeserializeActiveClusterSelectionPolicy. func (mr *MockPayloadSerializerMockRecorder) DeserializeActiveClusterSelectionPolicy(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeActiveClusterSelectionPolicy", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeActiveClusterSelectionPolicy), data) } // DeserializeActiveClusters mocks base method. func (m *MockPayloadSerializer) DeserializeActiveClusters(data *DataBlob) (*types.ActiveClusters, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeActiveClusters", data) ret0, _ := ret[0].(*types.ActiveClusters) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeActiveClusters indicates an expected call of DeserializeActiveClusters. func (mr *MockPayloadSerializerMockRecorder) DeserializeActiveClusters(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeActiveClusters", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeActiveClusters), data) } // DeserializeAsyncWorkflowsConfig mocks base method. func (m *MockPayloadSerializer) DeserializeAsyncWorkflowsConfig(data *DataBlob) (*types.AsyncWorkflowConfiguration, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeAsyncWorkflowsConfig", data) ret0, _ := ret[0].(*types.AsyncWorkflowConfiguration) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeAsyncWorkflowsConfig indicates an expected call of DeserializeAsyncWorkflowsConfig. func (mr *MockPayloadSerializerMockRecorder) DeserializeAsyncWorkflowsConfig(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeAsyncWorkflowsConfig", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeAsyncWorkflowsConfig), data) } // DeserializeBadBinaries mocks base method. func (m *MockPayloadSerializer) DeserializeBadBinaries(data *DataBlob) (*types.BadBinaries, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeBadBinaries", data) ret0, _ := ret[0].(*types.BadBinaries) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeBadBinaries indicates an expected call of DeserializeBadBinaries. func (mr *MockPayloadSerializerMockRecorder) DeserializeBadBinaries(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeBadBinaries", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeBadBinaries), data) } // DeserializeBatchEvents mocks base method. func (m *MockPayloadSerializer) DeserializeBatchEvents(data *DataBlob) ([]*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeBatchEvents", data) ret0, _ := ret[0].([]*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeBatchEvents indicates an expected call of DeserializeBatchEvents. func (mr *MockPayloadSerializerMockRecorder) DeserializeBatchEvents(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeBatchEvents", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeBatchEvents), data) } // DeserializeChecksum mocks base method. func (m *MockPayloadSerializer) DeserializeChecksum(data *DataBlob) (checksum.Checksum, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeChecksum", data) ret0, _ := ret[0].(checksum.Checksum) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeChecksum indicates an expected call of DeserializeChecksum. func (mr *MockPayloadSerializerMockRecorder) DeserializeChecksum(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeChecksum", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeChecksum), data) } // DeserializeDynamicConfigBlob mocks base method. func (m *MockPayloadSerializer) DeserializeDynamicConfigBlob(data *DataBlob) (*types.DynamicConfigBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeDynamicConfigBlob", data) ret0, _ := ret[0].(*types.DynamicConfigBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeDynamicConfigBlob indicates an expected call of DeserializeDynamicConfigBlob. func (mr *MockPayloadSerializerMockRecorder) DeserializeDynamicConfigBlob(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeDynamicConfigBlob", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeDynamicConfigBlob), data) } // DeserializeEvent mocks base method. func (m *MockPayloadSerializer) DeserializeEvent(data *DataBlob) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeEvent", data) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeEvent indicates an expected call of DeserializeEvent. func (mr *MockPayloadSerializerMockRecorder) DeserializeEvent(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeEvent", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeEvent), data) } // DeserializeIsolationGroups mocks base method. func (m *MockPayloadSerializer) DeserializeIsolationGroups(data *DataBlob) (*types.IsolationGroupConfiguration, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeIsolationGroups", data) ret0, _ := ret[0].(*types.IsolationGroupConfiguration) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeIsolationGroups indicates an expected call of DeserializeIsolationGroups. func (mr *MockPayloadSerializerMockRecorder) DeserializeIsolationGroups(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeIsolationGroups", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeIsolationGroups), data) } // DeserializePendingFailoverMarkers mocks base method. func (m *MockPayloadSerializer) DeserializePendingFailoverMarkers(data *DataBlob) ([]*types.FailoverMarkerAttributes, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializePendingFailoverMarkers", data) ret0, _ := ret[0].([]*types.FailoverMarkerAttributes) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializePendingFailoverMarkers indicates an expected call of DeserializePendingFailoverMarkers. func (mr *MockPayloadSerializerMockRecorder) DeserializePendingFailoverMarkers(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializePendingFailoverMarkers", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializePendingFailoverMarkers), data) } // DeserializeProcessingQueueStates mocks base method. func (m *MockPayloadSerializer) DeserializeProcessingQueueStates(data *DataBlob) (*types.ProcessingQueueStates, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeProcessingQueueStates", data) ret0, _ := ret[0].(*types.ProcessingQueueStates) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeProcessingQueueStates indicates an expected call of DeserializeProcessingQueueStates. func (mr *MockPayloadSerializerMockRecorder) DeserializeProcessingQueueStates(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeProcessingQueueStates", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeProcessingQueueStates), data) } // DeserializeResetPoints mocks base method. func (m *MockPayloadSerializer) DeserializeResetPoints(data *DataBlob) (*types.ResetPoints, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeResetPoints", data) ret0, _ := ret[0].(*types.ResetPoints) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeResetPoints indicates an expected call of DeserializeResetPoints. func (mr *MockPayloadSerializerMockRecorder) DeserializeResetPoints(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeResetPoints", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeResetPoints), data) } // DeserializeVersionHistories mocks base method. func (m *MockPayloadSerializer) DeserializeVersionHistories(data *DataBlob) (*types.VersionHistories, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeVersionHistories", data) ret0, _ := ret[0].(*types.VersionHistories) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeVersionHistories indicates an expected call of DeserializeVersionHistories. func (mr *MockPayloadSerializerMockRecorder) DeserializeVersionHistories(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeVersionHistories", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeVersionHistories), data) } // DeserializeVisibilityMemo mocks base method. func (m *MockPayloadSerializer) DeserializeVisibilityMemo(data *DataBlob) (*types.Memo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeVisibilityMemo", data) ret0, _ := ret[0].(*types.Memo) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeVisibilityMemo indicates an expected call of DeserializeVisibilityMemo. func (mr *MockPayloadSerializerMockRecorder) DeserializeVisibilityMemo(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeVisibilityMemo", reflect.TypeOf((*MockPayloadSerializer)(nil).DeserializeVisibilityMemo), data) } // SerializeActiveClusterSelectionPolicy mocks base method. func (m *MockPayloadSerializer) SerializeActiveClusterSelectionPolicy(policy *types.ActiveClusterSelectionPolicy, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeActiveClusterSelectionPolicy", policy, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeActiveClusterSelectionPolicy indicates an expected call of SerializeActiveClusterSelectionPolicy. func (mr *MockPayloadSerializerMockRecorder) SerializeActiveClusterSelectionPolicy(policy, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeActiveClusterSelectionPolicy", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeActiveClusterSelectionPolicy), policy, encodingType) } // SerializeActiveClusters mocks base method. func (m *MockPayloadSerializer) SerializeActiveClusters(activeClusters *types.ActiveClusters, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeActiveClusters", activeClusters, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeActiveClusters indicates an expected call of SerializeActiveClusters. func (mr *MockPayloadSerializerMockRecorder) SerializeActiveClusters(activeClusters, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeActiveClusters", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeActiveClusters), activeClusters, encodingType) } // SerializeAsyncWorkflowsConfig mocks base method. func (m *MockPayloadSerializer) SerializeAsyncWorkflowsConfig(config *types.AsyncWorkflowConfiguration, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeAsyncWorkflowsConfig", config, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeAsyncWorkflowsConfig indicates an expected call of SerializeAsyncWorkflowsConfig. func (mr *MockPayloadSerializerMockRecorder) SerializeAsyncWorkflowsConfig(config, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeAsyncWorkflowsConfig", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeAsyncWorkflowsConfig), config, encodingType) } // SerializeBadBinaries mocks base method. func (m *MockPayloadSerializer) SerializeBadBinaries(event *types.BadBinaries, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeBadBinaries", event, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeBadBinaries indicates an expected call of SerializeBadBinaries. func (mr *MockPayloadSerializerMockRecorder) SerializeBadBinaries(event, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeBadBinaries", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeBadBinaries), event, encodingType) } // SerializeBatchEvents mocks base method. func (m *MockPayloadSerializer) SerializeBatchEvents(batch []*types.HistoryEvent, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeBatchEvents", batch, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeBatchEvents indicates an expected call of SerializeBatchEvents. func (mr *MockPayloadSerializerMockRecorder) SerializeBatchEvents(batch, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeBatchEvents", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeBatchEvents), batch, encodingType) } // SerializeChecksum mocks base method. func (m *MockPayloadSerializer) SerializeChecksum(sum checksum.Checksum, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeChecksum", sum, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeChecksum indicates an expected call of SerializeChecksum. func (mr *MockPayloadSerializerMockRecorder) SerializeChecksum(sum, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeChecksum", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeChecksum), sum, encodingType) } // SerializeDynamicConfigBlob mocks base method. func (m *MockPayloadSerializer) SerializeDynamicConfigBlob(blob *types.DynamicConfigBlob, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeDynamicConfigBlob", blob, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeDynamicConfigBlob indicates an expected call of SerializeDynamicConfigBlob. func (mr *MockPayloadSerializerMockRecorder) SerializeDynamicConfigBlob(blob, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeDynamicConfigBlob", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeDynamicConfigBlob), blob, encodingType) } // SerializeEvent mocks base method. func (m *MockPayloadSerializer) SerializeEvent(event *types.HistoryEvent, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeEvent", event, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeEvent indicates an expected call of SerializeEvent. func (mr *MockPayloadSerializerMockRecorder) SerializeEvent(event, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeEvent", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeEvent), event, encodingType) } // SerializeIsolationGroups mocks base method. func (m *MockPayloadSerializer) SerializeIsolationGroups(event *types.IsolationGroupConfiguration, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeIsolationGroups", event, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeIsolationGroups indicates an expected call of SerializeIsolationGroups. func (mr *MockPayloadSerializerMockRecorder) SerializeIsolationGroups(event, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeIsolationGroups", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeIsolationGroups), event, encodingType) } // SerializePendingFailoverMarkers mocks base method. func (m *MockPayloadSerializer) SerializePendingFailoverMarkers(markers []*types.FailoverMarkerAttributes, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializePendingFailoverMarkers", markers, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializePendingFailoverMarkers indicates an expected call of SerializePendingFailoverMarkers. func (mr *MockPayloadSerializerMockRecorder) SerializePendingFailoverMarkers(markers, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializePendingFailoverMarkers", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializePendingFailoverMarkers), markers, encodingType) } // SerializeProcessingQueueStates mocks base method. func (m *MockPayloadSerializer) SerializeProcessingQueueStates(states *types.ProcessingQueueStates, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeProcessingQueueStates", states, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeProcessingQueueStates indicates an expected call of SerializeProcessingQueueStates. func (mr *MockPayloadSerializerMockRecorder) SerializeProcessingQueueStates(states, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeProcessingQueueStates", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeProcessingQueueStates), states, encodingType) } // SerializeResetPoints mocks base method. func (m *MockPayloadSerializer) SerializeResetPoints(event *types.ResetPoints, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeResetPoints", event, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeResetPoints indicates an expected call of SerializeResetPoints. func (mr *MockPayloadSerializerMockRecorder) SerializeResetPoints(event, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeResetPoints", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeResetPoints), event, encodingType) } // SerializeVersionHistories mocks base method. func (m *MockPayloadSerializer) SerializeVersionHistories(histories *types.VersionHistories, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeVersionHistories", histories, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeVersionHistories indicates an expected call of SerializeVersionHistories. func (mr *MockPayloadSerializerMockRecorder) SerializeVersionHistories(histories, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeVersionHistories", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeVersionHistories), histories, encodingType) } // SerializeVisibilityMemo mocks base method. func (m *MockPayloadSerializer) SerializeVisibilityMemo(memo *types.Memo, encodingType constants.EncodingType) (*DataBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeVisibilityMemo", memo, encodingType) ret0, _ := ret[0].(*DataBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeVisibilityMemo indicates an expected call of SerializeVisibilityMemo. func (mr *MockPayloadSerializerMockRecorder) SerializeVisibilityMemo(memo, encodingType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeVisibilityMemo", reflect.TypeOf((*MockPayloadSerializer)(nil).SerializeVisibilityMemo), memo, encodingType) } ================================================ FILE: common/persistence/serializer_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) type testDef struct { name string // payloads is a map of payload name to payload. "nil" is a special name for nil payload which expects to return nil with some exceptions payloads map[string]any nilHandled bool serializeFn func(any, constants.EncodingType) (*DataBlob, error) deserializeFn func(*DataBlob) (any, error) } // key is encoding type, value is whether the encoding type is supported var encodingTypes = map[constants.EncodingType]bool{ constants.EncodingTypeEmpty: true, constants.EncodingTypeUnknown: true, constants.EncodingTypeJSON: true, constants.EncodingTypeThriftRW: true, constants.EncodingTypeGob: false, } type runnableTest struct { testDef encoding constants.EncodingType supported bool payloadName string payload any } func TestSerializers(t *testing.T) { // create serializer once and reuse it for all test cases in parallel to catch concurrency issues serializer := NewPayloadSerializer() tests := []testDef{ { name: "history event", payloads: map[string]any{ "nil": (*types.HistoryEvent)(nil), "normal": generateTestHistoryEvent(1), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeEvent(payload.(*types.HistoryEvent), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeEvent(data) }, }, { name: "batch history events", payloads: map[string]any{ "nil": ([]*types.HistoryEvent)(nil), "normal": generateTestHistoryEventBatch(), }, nilHandled: true, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeBatchEvents(payload.([]*types.HistoryEvent), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeBatchEvents(data) }, }, { name: "visibility memo", payloads: map[string]any{ "nil": (*types.Memo)(nil), "normal": generateVisibilityMemo(), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeVisibilityMemo(payload.(*types.Memo), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeVisibilityMemo(data) }, }, { name: "version histories", payloads: map[string]any{ "nil": (*types.VersionHistories)(nil), "normal": generateVersionHistories(), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeVersionHistories(payload.(*types.VersionHistories), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeVersionHistories(data) }, }, { name: "reset points", payloads: map[string]any{ "nil": (*types.ResetPoints)(nil), "normal": generateResetPoints(), }, nilHandled: true, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeResetPoints(payload.(*types.ResetPoints), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeResetPoints(data) }, }, { name: "bad binaries", payloads: map[string]any{ "nil": (*types.BadBinaries)(nil), "normal": generateBadBinaries(), }, nilHandled: true, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeBadBinaries(payload.(*types.BadBinaries), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeBadBinaries(data) }, }, { name: "processing queue states", payloads: map[string]any{ "nil": (*types.ProcessingQueueStates)(nil), "normal": generateProcessingQueueStates(), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeProcessingQueueStates(payload.(*types.ProcessingQueueStates), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeProcessingQueueStates(data) }, }, { name: "dynamic config blob", payloads: map[string]any{ "nil": (*types.DynamicConfigBlob)(nil), "normal": generateDynamicConfigBlob(), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeDynamicConfigBlob(payload.(*types.DynamicConfigBlob), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeDynamicConfigBlob(data) }, }, { name: "isolation group", payloads: map[string]any{ "nil": (*types.IsolationGroupConfiguration)(nil), "normal": generateIsolationGroupConfiguration(), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeIsolationGroups(payload.(*types.IsolationGroupConfiguration), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeIsolationGroups(data) }, }, { name: "failover markers", payloads: map[string]any{ "nil": ([]*types.FailoverMarkerAttributes)(nil), "normal": generateFailoverMarkerAttributes(), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializePendingFailoverMarkers(payload.([]*types.FailoverMarkerAttributes), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializePendingFailoverMarkers(data) }, }, { name: "async workflow config", payloads: map[string]any{ "nil": (*types.AsyncWorkflowConfiguration)(nil), "normal": generateAsyncWorkflowConfig(), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeAsyncWorkflowsConfig(payload.(*types.AsyncWorkflowConfiguration), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeAsyncWorkflowsConfig(data) }, }, { name: "checksum", payloads: map[string]any{ "empty": checksum.Checksum{}, "normal": generateChecksum(), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeChecksum(payload.(checksum.Checksum), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeChecksum(data) }, }, { name: "active clusters", payloads: map[string]any{ "nil": (*types.ActiveClusters)(nil), "normal": generateActiveClusters(), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeActiveClusters(payload.(*types.ActiveClusters), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeActiveClusters(data) }, }, { name: "active cluster selection policy", payloads: map[string]any{ "nil": (*types.ActiveClusterSelectionPolicy)(nil), "normal": generateActiveClusterSelectionPolicy(), }, serializeFn: func(payload any, encoding constants.EncodingType) (*DataBlob, error) { return serializer.SerializeActiveClusterSelectionPolicy(payload.(*types.ActiveClusterSelectionPolicy), encoding) }, deserializeFn: func(data *DataBlob) (any, error) { return serializer.DeserializeActiveClusterSelectionPolicy(data) }, }, } // generate runnable test cases here so actual test body is not 3 level nested var runnableTests []runnableTest for _, td := range tests { for encoding, supported := range encodingTypes { for payloadName, payload := range td.payloads { if _, ok := payload.(checksum.Checksum); ok { if encoding != constants.EncodingTypeJSON { continue } } runnableTests = append(runnableTests, runnableTest{ testDef: td, encoding: encoding, supported: supported, payloadName: payloadName, payload: payload, }) } } } for _, tc := range runnableTests { tc := tc t.Run(fmt.Sprintf("%s with encoding:%s,payload:%s", tc.name, tc.encoding, tc.payloadName), func(t *testing.T) { t.Parallel() serialized, err := tc.serializeFn(tc.payload, tc.encoding) // expect error if the encoding type is not supported. special case is nil payloads. // most of the serialization functions return early for nils but // some of them call underlying helper function which checks encoding type and raises error. wantErr := !tc.supported && !(tc.payloadName == "nil" && !tc.nilHandled) if wantErr != (err != nil) { t.Fatalf("Got serialization err: %v, want err?: %v", err, wantErr) } if err != nil { return } deserialized, err := tc.deserializeFn(serialized) if err != nil { t.Fatalf("Got serialization err: %v", err) } if deserialized == nil { t.Fatalf("Got nil deserialized payload") } if tc.payloadName == "nil" { return } }) } } func TestDataBlob_GetData(t *testing.T) { tests := map[string]struct { in *DataBlob expectedOut []byte }{ "valid data": { in: &DataBlob{Data: []byte("dat")}, expectedOut: []byte("dat"), }, "empty data": { in: &DataBlob{Data: nil}, expectedOut: []byte{}, }, "empty data 2": { in: nil, expectedOut: []byte{}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expectedOut, td.in.GetData()) }) } } func generateTestHistoryEvent(id int64) *types.HistoryEvent { return &types.HistoryEvent{ ID: id, Timestamp: common.Int64Ptr(time.Now().UnixNano()), EventType: types.EventTypeActivityTaskCompleted.Ptr(), ActivityTaskCompletedEventAttributes: &types.ActivityTaskCompletedEventAttributes{ Result: []byte("result-1-event-1"), ScheduledEventID: 4, StartedEventID: 5, Identity: "event-1", }, } } func generateTestHistoryEventBatch() []*types.HistoryEvent { return []*types.HistoryEvent{ generateTestHistoryEvent(111), generateTestHistoryEvent(112), generateTestHistoryEvent(113), } } func generateVisibilityMemo() *types.Memo { memoFields := map[string][]byte{ "TestField": []byte(`Test binary`), } return &types.Memo{Fields: memoFields} } func generateVersionHistories() *types.VersionHistories { return &types.VersionHistories{ Histories: []*types.VersionHistory{ { BranchToken: []byte{1}, Items: []*types.VersionHistoryItem{ { EventID: 1, Version: 0, }, { EventID: 2, Version: 1, }, }, }, { BranchToken: []byte{2}, Items: []*types.VersionHistoryItem{ { EventID: 2, Version: 0, }, { EventID: 3, Version: 1, }, }, }, }, } } func generateResetPoints() *types.ResetPoints { return &types.ResetPoints{ Points: []*types.ResetPointInfo{ { BinaryChecksum: "bad-binary-cs", RunID: "test-run-id", FirstDecisionCompletedID: 123, CreatedTimeNano: common.Int64Ptr(456), ExpiringTimeNano: common.Int64Ptr(789), Resettable: true, }, }, } } func generateBadBinaries() *types.BadBinaries { return &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "bad-binary-cs": { CreatedTimeNano: common.Int64Ptr(456), Operator: "test-operattor", Reason: "test-reason", }, }, } } func generateProcessingQueueStates() *types.ProcessingQueueStates { return &types.ProcessingQueueStates{ StatesByCluster: map[string][]*types.ProcessingQueueState{ "cluster1": { &types.ProcessingQueueState{ Level: common.Int32Ptr(0), AckLevel: common.Int64Ptr(1), MaxLevel: common.Int64Ptr(2), DomainFilter: &types.DomainFilter{ DomainIDs: []string{"domain1", "domain2"}, ReverseMatch: true, }, }, }, "cluster2": { &types.ProcessingQueueState{ Level: common.Int32Ptr(3), AckLevel: common.Int64Ptr(4), MaxLevel: common.Int64Ptr(5), DomainFilter: &types.DomainFilter{ DomainIDs: []string{"domain3", "domain4"}, ReverseMatch: true, }, }, }, }, } } func generateDynamicConfigBlob() *types.DynamicConfigBlob { return &types.DynamicConfigBlob{ SchemaVersion: 1, Entries: []*types.DynamicConfigEntry{ { Name: dynamicproperties.TestGetBoolPropertyKey.String(), Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte("false"), }, Filters: nil, }, }, }, }, } } func generateIsolationGroupConfiguration() *types.IsolationGroupConfiguration { return &types.IsolationGroupConfiguration{ "zone-1": { Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": { Name: "zone-2", State: types.IsolationGroupStateHealthy, }, } } func generateFailoverMarkerAttributes() []*types.FailoverMarkerAttributes { return []*types.FailoverMarkerAttributes{ { DomainID: "domain1", FailoverVersion: 123, CreationTime: common.Int64Ptr(time.Now().UnixNano()), }, { DomainID: "domain2", FailoverVersion: 456, CreationTime: common.Int64Ptr(time.Now().UnixNano()), }, } } func generateAsyncWorkflowConfig() *types.AsyncWorkflowConfiguration { return &types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-queue", QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"key":"value"}`), }, } } func generateChecksum() checksum.Checksum { return checksum.Checksum{ Flavor: checksum.FlavorIEEECRC32OverThriftBinary, Version: 1, Value: []byte("test-checksum"), } } func generateActiveClusters() *types.ActiveClusters { return &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: "cluster1", FailoverVersion: 2, }, "region2": { ActiveClusterName: "cluster2", FailoverVersion: 3, }, }, }, }, } } func generateActiveClusterSelectionPolicy() *types.ActiveClusterSelectionPolicy { return &types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyRegionSticky.Ptr(), StickyRegion: "region1", ExternalEntityType: "externalEntityType1", ExternalEntityKey: "externalEntityKey1", } } ================================================ FILE: common/persistence/shard_manager.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" ) type ( shardManager struct { persistence ShardStore serializer PayloadSerializer timeSrc clock.TimeSource dc *DynamicConfiguration } ) var _ ShardManager = (*shardManager)(nil) type ShardManagerOption func(manager *shardManager) func WithSerializer(serializer PayloadSerializer) ShardManagerOption { return func(manager *shardManager) { if serializer != nil { manager.serializer = serializer } } } // NewShardManager returns a new ShardManager func NewShardManager( persistence ShardStore, dc *DynamicConfiguration, options ...ShardManagerOption, ) ShardManager { manager := &shardManager{ persistence: persistence, serializer: NewPayloadSerializer(), timeSrc: clock.NewRealTimeSource(), dc: dc, } for _, option := range options { option(manager) } return manager } func (m *shardManager) GetName() string { return m.persistence.GetName() } func (m *shardManager) Close() { m.persistence.Close() } func (m *shardManager) CreateShard(ctx context.Context, request *CreateShardRequest) error { shardInfo, err := m.toInternalShardInfo(request.ShardInfo, constants.EncodingType(m.dc.SerializationEncoding())) if err != nil { return err } internalRequest := &InternalCreateShardRequest{ ShardInfo: shardInfo, CurrentTimeStamp: m.timeSrc.Now(), } return m.persistence.CreateShard(ctx, internalRequest) } func (m *shardManager) GetShard(ctx context.Context, request *GetShardRequest) (*GetShardResponse, error) { internalRequest := &InternalGetShardRequest{ ShardID: request.ShardID, } internalResult, err := m.persistence.GetShard(ctx, internalRequest) if err != nil { return nil, err } shardInfo, err := m.fromInternalShardInfo(internalResult.ShardInfo) if err != nil { return nil, err } result := &GetShardResponse{ ShardInfo: shardInfo, } return result, nil } func (m *shardManager) UpdateShard(ctx context.Context, request *UpdateShardRequest) error { shardInfo, err := m.toInternalShardInfo(request.ShardInfo, constants.EncodingType(m.dc.SerializationEncoding())) if err != nil { return err } internalRequest := &InternalUpdateShardRequest{ ShardInfo: shardInfo, PreviousRangeID: request.PreviousRangeID, CurrentTimeStamp: m.timeSrc.Now(), } return m.persistence.UpdateShard(ctx, internalRequest) } func (m *shardManager) toInternalShardInfo(shardInfo *ShardInfo, encodingType constants.EncodingType) (*InternalShardInfo, error) { if shardInfo == nil { return nil, nil } serializedTransferProcessingQueueStates, err := m.serializer.SerializeProcessingQueueStates(shardInfo.TransferProcessingQueueStates, encodingType) if err != nil { return nil, err } serializedTimerProcessingQueueStates, err := m.serializer.SerializeProcessingQueueStates(shardInfo.TimerProcessingQueueStates, encodingType) if err != nil { return nil, err } pendingFailoverMarker, err := m.serializer.SerializePendingFailoverMarkers(shardInfo.PendingFailoverMarkers, encodingType) if err != nil { return nil, err } return &InternalShardInfo{ ShardID: shardInfo.ShardID, Owner: shardInfo.Owner, RangeID: shardInfo.RangeID, StolenSinceRenew: shardInfo.StolenSinceRenew, UpdatedAt: shardInfo.UpdatedAt, ReplicationAckLevel: shardInfo.ReplicationAckLevel, ReplicationDLQAckLevel: shardInfo.ReplicationDLQAckLevel, TransferAckLevel: shardInfo.TransferAckLevel, TimerAckLevel: shardInfo.TimerAckLevel, ClusterTransferAckLevel: shardInfo.ClusterTransferAckLevel, ClusterTimerAckLevel: shardInfo.ClusterTimerAckLevel, TransferProcessingQueueStates: serializedTransferProcessingQueueStates, TimerProcessingQueueStates: serializedTimerProcessingQueueStates, ClusterReplicationLevel: shardInfo.ClusterReplicationLevel, DomainNotificationVersion: shardInfo.DomainNotificationVersion, PendingFailoverMarkers: pendingFailoverMarker, QueueStates: shardInfo.QueueStates, }, nil } func (m *shardManager) fromInternalShardInfo(internalShardInfo *InternalShardInfo) (*ShardInfo, error) { if internalShardInfo == nil { return nil, nil } transferProcessingQueueStates, err := m.serializer.DeserializeProcessingQueueStates(internalShardInfo.TransferProcessingQueueStates) if err != nil { return nil, err } timerProcessingQueueStates, err := m.serializer.DeserializeProcessingQueueStates(internalShardInfo.TimerProcessingQueueStates) if err != nil { return nil, err } pendingFailoverMarker, err := m.serializer.DeserializePendingFailoverMarkers(internalShardInfo.PendingFailoverMarkers) if err != nil { return nil, err } return &ShardInfo{ ShardID: internalShardInfo.ShardID, Owner: internalShardInfo.Owner, RangeID: internalShardInfo.RangeID, StolenSinceRenew: internalShardInfo.StolenSinceRenew, UpdatedAt: internalShardInfo.UpdatedAt, ReplicationAckLevel: internalShardInfo.ReplicationAckLevel, ReplicationDLQAckLevel: internalShardInfo.ReplicationDLQAckLevel, TransferAckLevel: internalShardInfo.TransferAckLevel, TimerAckLevel: internalShardInfo.TimerAckLevel, ClusterTransferAckLevel: internalShardInfo.ClusterTransferAckLevel, ClusterTimerAckLevel: internalShardInfo.ClusterTimerAckLevel, TransferProcessingQueueStates: transferProcessingQueueStates, TimerProcessingQueueStates: timerProcessingQueueStates, ClusterReplicationLevel: internalShardInfo.ClusterReplicationLevel, DomainNotificationVersion: internalShardInfo.DomainNotificationVersion, PendingFailoverMarkers: pendingFailoverMarker, QueueStates: internalShardInfo.QueueStates, }, nil } ================================================ FILE: common/persistence/shard_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) func TestShardManagerGetName(t *testing.T) { ctrl := gomock.NewController(t) store := NewMockShardStore(ctrl) store.EXPECT().GetName().Return("name") manager := NewShardManager(store, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) assert.Equal(t, "name", manager.GetName()) } func TestShardManagerClose(t *testing.T) { ctrl := gomock.NewController(t) store := NewMockShardStore(ctrl) store.EXPECT().Close().Times(1) manager := NewShardManager(store, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) manager.Close() } func TestShardManagerCreateShard(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *CreateShardRequest internalRequest *InternalCreateShardRequest serializer PayloadSerializer internalResponse error expected error }{ "Success": { request: &CreateShardRequest{ ShardInfo: sampleShardInfo(), }, internalRequest: &InternalCreateShardRequest{ ShardInfo: sampleInternalShardInfo(t), }, internalResponse: nil, expected: nil, }, "Serialization Failure": { request: &CreateShardRequest{ ShardInfo: sampleShardInfo(), }, serializer: failingSerializer(ctrl), expected: fmt.Errorf("serialization"), }, "Error Response": { request: &CreateShardRequest{ ShardInfo: sampleShardInfo(), }, internalRequest: &InternalCreateShardRequest{ ShardInfo: sampleInternalShardInfo(t), }, internalResponse: fmt.Errorf("error"), expected: fmt.Errorf("error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { store := NewMockShardStore(ctrl) if test.internalRequest != nil { store.EXPECT().CreateShard(gomock.Any(), gomock.Any()). Do(func(ctx context.Context, req *InternalCreateShardRequest) { assert.Equal(t, test.internalRequest.ShardInfo, req.ShardInfo) assert.WithinDuration(t, time.Now(), req.CurrentTimeStamp, time.Second) }).Return(test.internalResponse) } manager := NewShardManager(store, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }, WithSerializer(test.serializer)) result := manager.CreateShard(context.Background(), test.request) assert.Equal(t, test.expected, result) }) } } func TestShardManagerGetShard(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *GetShardRequest internalRequest *InternalGetShardRequest serializer PayloadSerializer internalResponse *InternalGetShardResponse internalErr error expected *GetShardResponse expectedErr error }{ "Success": { request: &GetShardRequest{ ShardID: shardID, }, internalRequest: &InternalGetShardRequest{ ShardID: shardID, }, internalResponse: &InternalGetShardResponse{ ShardInfo: sampleInternalShardInfo(t), }, expected: &GetShardResponse{ ShardInfo: sampleShardInfo(), }, }, "Nil response": { request: &GetShardRequest{ ShardID: shardID, }, internalRequest: &InternalGetShardRequest{ ShardID: shardID, }, internalResponse: &InternalGetShardResponse{ ShardInfo: nil, }, expected: &GetShardResponse{ ShardInfo: nil, }, }, "Serialization Failure": { request: &GetShardRequest{ ShardID: shardID, }, internalRequest: &InternalGetShardRequest{ ShardID: shardID, }, internalResponse: &InternalGetShardResponse{ ShardInfo: sampleInternalShardInfo(t), }, serializer: failingSerializer(ctrl), expectedErr: fmt.Errorf("serialization"), }, "Error Response": { request: &GetShardRequest{ ShardID: shardID, }, internalRequest: &InternalGetShardRequest{ ShardID: shardID, }, internalErr: fmt.Errorf("error"), expectedErr: fmt.Errorf("error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { store := NewMockShardStore(ctrl) if test.internalRequest != nil { store.EXPECT().GetShard(gomock.Any(), gomock.Eq(test.internalRequest)).Return(test.internalResponse, test.internalErr) } manager := NewShardManager(store, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }, WithSerializer(test.serializer)) result, err := manager.GetShard(context.Background(), test.request) assert.Equal(t, test.expected, result) assert.Equal(t, test.expectedErr, err) }) } } func TestShardManagerUpdateShard(t *testing.T) { ctrl := gomock.NewController(t) tests := map[string]struct { request *UpdateShardRequest internalRequest *InternalUpdateShardRequest serializer PayloadSerializer internalResponse error expected error }{ "Success": { request: &UpdateShardRequest{ ShardInfo: sampleShardInfo(), PreviousRangeID: 1, }, internalRequest: &InternalUpdateShardRequest{ ShardInfo: sampleInternalShardInfo(t), PreviousRangeID: 1, }, internalResponse: nil, expected: nil, }, "Serialization Failure": { request: &UpdateShardRequest{ ShardInfo: sampleShardInfo(), PreviousRangeID: 1, }, serializer: failingSerializer(ctrl), expected: fmt.Errorf("serialization"), }, "Error Response": { request: &UpdateShardRequest{ ShardInfo: sampleShardInfo(), PreviousRangeID: 1, }, internalRequest: &InternalUpdateShardRequest{ ShardInfo: sampleInternalShardInfo(t), PreviousRangeID: 1, }, internalResponse: fmt.Errorf("error"), expected: fmt.Errorf("error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { store := NewMockShardStore(ctrl) if test.internalRequest != nil { store.EXPECT().UpdateShard(gomock.Any(), gomock.Any()). Do(func(ctx context.Context, req *InternalUpdateShardRequest) { assert.Equal(t, test.internalRequest.PreviousRangeID, req.PreviousRangeID) assert.Equal(t, test.internalRequest.ShardInfo, req.ShardInfo) assert.WithinDuration(t, time.Now(), req.CurrentTimeStamp, time.Second) }).Return(test.internalResponse) } manager := NewShardManager(store, &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }, WithSerializer(test.serializer)) result := manager.UpdateShard(context.Background(), test.request) assert.Equal(t, test.expected, result) }) } } var ( shardID = 1 owner = "TestOwner" rangeID int64 = 2 stolenSinceRenew = 3 updatedAt = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) replicationAckLevel int64 = 4 timerAckLevel = time.Date(2020, 2, 1, 0, 0, 0, 0, time.UTC) transferAckLevel int64 = 5 replicationDlqAckLevel = map[string]int64{ "key": 123, } clusterTransferAckLevel = map[string]int64{ "otherKey": 124, } clusterTimerAckLevel = map[string]time.Time{ "foo": time.Date(2020, 3, 1, 0, 0, 0, 0, time.UTC), } transferProcessingQueueStates = &types.ProcessingQueueStates{ StatesByCluster: map[string][]*types.ProcessingQueueState{ "transfer": { { Level: common.Int32Ptr(1), AckLevel: common.Int64Ptr(2), MaxLevel: common.Int64Ptr(3), DomainFilter: &types.DomainFilter{ DomainIDs: []string{"foo", "bar"}, ReverseMatch: false, }, }, }, }, } timerProcesssingQueueStates = &types.ProcessingQueueStates{ StatesByCluster: map[string][]*types.ProcessingQueueState{ "timer": { { Level: common.Int32Ptr(7), AckLevel: common.Int64Ptr(8), MaxLevel: common.Int64Ptr(9), DomainFilter: &types.DomainFilter{ DomainIDs: []string{"bar"}, ReverseMatch: false, }, }, }, }, } clusterReplicationLevel = map[string]int64{ "bar": 100, } domainNotificationVersion int64 = 6 pendingFailoverMarkers = []*types.FailoverMarkerAttributes{ { DomainID: "TestDomain", FailoverVersion: 11, CreationTime: common.Int64Ptr(12), }, } ) func failingSerializer(ctrl *gomock.Controller) PayloadSerializer { serializer := NewMockPayloadSerializer(ctrl) serializer.EXPECT().SerializePendingFailoverMarkers(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("serialization")).AnyTimes() serializer.EXPECT().DeserializePendingFailoverMarkers(gomock.Any()).Return(nil, fmt.Errorf("serialization")).AnyTimes() serializer.EXPECT().SerializeProcessingQueueStates(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("serialization")).AnyTimes() serializer.EXPECT().DeserializeProcessingQueueStates(gomock.Any()).Return(nil, fmt.Errorf("serialization")).AnyTimes() return serializer } func sampleInternalShardInfo(t *testing.T) *InternalShardInfo { serializer := NewPayloadSerializer() transferProcessingQueueStatesBlob, err := serializer.SerializeProcessingQueueStates(transferProcessingQueueStates, constants.EncodingTypeThriftRW) assert.NoError(t, err) timerProcessingQueueStatesBlob, err := serializer.SerializeProcessingQueueStates(timerProcesssingQueueStates, constants.EncodingTypeThriftRW) assert.NoError(t, err) pendingFailoverMarkerBlob, err := serializer.SerializePendingFailoverMarkers(pendingFailoverMarkers, constants.EncodingTypeThriftRW) assert.NoError(t, err) return &InternalShardInfo{ ShardID: shardID, Owner: owner, RangeID: rangeID, StolenSinceRenew: 3, UpdatedAt: updatedAt, ReplicationAckLevel: 4, ReplicationDLQAckLevel: replicationDlqAckLevel, TransferAckLevel: 5, TimerAckLevel: timerAckLevel, ClusterTransferAckLevel: clusterTransferAckLevel, ClusterTimerAckLevel: clusterTimerAckLevel, TransferProcessingQueueStates: transferProcessingQueueStatesBlob, TimerProcessingQueueStates: timerProcessingQueueStatesBlob, ClusterReplicationLevel: clusterReplicationLevel, DomainNotificationVersion: domainNotificationVersion, PendingFailoverMarkers: pendingFailoverMarkerBlob, } } func sampleShardInfo() *ShardInfo { return &ShardInfo{ ShardID: shardID, Owner: owner, RangeID: rangeID, StolenSinceRenew: stolenSinceRenew, UpdatedAt: updatedAt, ReplicationAckLevel: replicationAckLevel, ReplicationDLQAckLevel: replicationDlqAckLevel, TransferAckLevel: transferAckLevel, TimerAckLevel: timerAckLevel, ClusterTransferAckLevel: clusterTransferAckLevel, ClusterTimerAckLevel: clusterTimerAckLevel, TransferProcessingQueueStates: transferProcessingQueueStates, TimerProcessingQueueStates: timerProcesssingQueueStates, ClusterReplicationLevel: clusterReplicationLevel, DomainNotificationVersion: domainNotificationVersion, PendingFailoverMarkers: pendingFailoverMarkers, } } ================================================ FILE: common/persistence/sql/common.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "bytes" "context" "encoding/binary" "encoding/gob" "fmt" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) type sqlStore struct { db sqlplugin.DB logger log.Logger parser serialization.Parser dc *p.DynamicConfiguration } func (m *sqlStore) GetName() string { return m.db.PluginName() } func (m *sqlStore) Close() { if m.db != nil { m.db.Close() } } func (m *sqlStore) useAsyncTransaction() bool { return m.db.SupportsAsyncTransaction() && m.dc != nil && m.dc.EnableSQLAsyncTransaction() } func (m *sqlStore) txExecute(ctx context.Context, dbShardID int, operation string, f func(tx sqlplugin.Tx) error) error { tx, err := m.db.BeginTx(ctx, dbShardID) if err != nil { return convertCommonErrors(m.db, operation, "Failed to start transaction.", err) } err = f(tx) if err != nil { rollBackErr := tx.Rollback() if rollBackErr != nil { m.logger.Error("transaction rollback error", tag.Error(rollBackErr)) } return convertCommonErrors(m.db, operation, "", err) } if err := tx.Commit(); err != nil { return convertCommonErrors(m.db, operation, "Failed to commit transaction.", err) } return nil } func gobSerialize(x interface{}) ([]byte, error) { b := bytes.Buffer{} e := gob.NewEncoder(&b) err := e.Encode(x) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("Error in serialization: %v", err), } } return b.Bytes(), nil } func gobDeserialize(a []byte, x interface{}) error { b := bytes.NewBuffer(a) d := gob.NewDecoder(b) err := d.Decode(x) if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("Error in deserialization: %v", err), } } return nil } func serializePageToken(offset int64) []byte { b := make([]byte, 8) binary.LittleEndian.PutUint64(b, uint64(offset)) return b } func deserializePageToken(payload []byte) (int64, error) { if len(payload) != 8 { return 0, fmt.Errorf("invalid token of %v length", len(payload)) } return int64(binary.LittleEndian.Uint64(payload)), nil } func convertCommonErrors( errChecker sqlplugin.ErrorChecker, operation, message string, err error, ) error { switch err.(type) { case *persistence.ConditionFailedError, *persistence.CurrentWorkflowConditionFailedError, *persistence.WorkflowExecutionAlreadyStartedError, *persistence.ShardOwnershipLostError, *persistence.TimeoutError, *types.DomainAlreadyExistsError, *types.EntityNotExistsError, *types.ServiceBusyError, *types.InternalServiceError: return err } if errChecker.IsNotFoundError(err) { return &types.EntityNotExistsError{ Message: fmt.Sprintf("%v failed. %s Error: %v", operation, message, err), } } if errChecker.IsTimeoutError(err) { return &persistence.TimeoutError{Msg: fmt.Sprintf("%v timed out. %s Error: %v", operation, message, err)} } if errChecker.IsThrottlingError(err) { return &types.ServiceBusyError{ Message: fmt.Sprintf("%v operation failed. %s Error: %v", operation, message, err), } } return &types.InternalServiceError{ Message: fmt.Sprintf("%v operation failed. %s Error: %v", operation, message, err), } } ================================================ FILE: common/persistence/sql/common_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sql import ( "context" "errors" "fmt" "reflect" "strings" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) // MockErrorChecker is a mock implementation of the sqlplugin.ErrorChecker interface type MockErrorChecker struct{} var _ sqlplugin.ErrorChecker = (*MockErrorChecker)(nil) func (m *MockErrorChecker) IsNotFoundError(err error) bool { if strings.Contains(err.Error(), "not found") { return true } return false } func (m *MockErrorChecker) IsTimeoutError(err error) bool { if strings.Contains(err.Error(), "timeout") { return true } return false } func (m *MockErrorChecker) IsThrottlingError(err error) bool { if strings.Contains(err.Error(), "throttle") { return true } return false } func (m *MockErrorChecker) IsDupEntryError(err error) bool { return false } func TestConvertCommonErrors(t *testing.T) { errChecker := &MockErrorChecker{} tests := []struct { name string operation string message string err error wantError error }{ { name: "ConditionFailedError", operation: "Create", message: "creation", err: &persistence.ConditionFailedError{}, wantError: &persistence.ConditionFailedError{}, }, { name: "NotFoundError", operation: "Get", message: "retrieval", err: errors.New("not found"), wantError: &types.EntityNotExistsError{Message: "Get failed. retrieval Error: not found"}, }, { name: "TimeoutError", operation: "Update", message: "update", err: errors.New("timeout"), wantError: &persistence.TimeoutError{Msg: "Update timed out. update Error: timeout"}, }, { name: "ThrottlingError", operation: "Delete", message: "deletion", err: errors.New("throttle"), wantError: &types.ServiceBusyError{Message: "Delete operation failed. deletion Error: throttle"}, }, { name: "InternalServiceError", operation: "List", message: "listing", err: errors.New("generic error"), wantError: &types.InternalServiceError{Message: "List operation failed. listing Error: generic error"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotError := convertCommonErrors(errChecker, tt.operation, tt.message, tt.err) if gotError.Error() != tt.wantError.Error() { t.Errorf("convertCommonErrors() error = %v, wantErr %v", gotError, tt.wantError) } }) } } func TestTxExecute(t *testing.T) { tests := []struct { name string mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx) operation string fn func(sqlplugin.Tx) error wantError error }{ { name: "Success", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().Commit().Return(nil) }, operation: "Insert", fn: func(sqlplugin.Tx) error { return nil }, wantError: nil, }, { name: "Error", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false) mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false) mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false) }, operation: "Insert", fn: func(sqlplugin.Tx) error { return errors.New("error") }, wantError: &types.InternalServiceError{Message: "Insert operation failed. Error: error"}, }, { name: "BeginTxError", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(nil, errors.New("error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false) mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false) mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false) }, operation: "Insert", fn: func(sqlplugin.Tx) error { return nil }, wantError: &types.InternalServiceError{Message: "Insert operation failed. Failed to start transaction. Error: error"}, }, { name: "CommitError", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().Commit().Return(errors.New("error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false) mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false) mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false) }, operation: "Insert", fn: func(sqlplugin.Tx) error { return nil }, wantError: &types.InternalServiceError{Message: "Insert operation failed. Failed to commit transaction. Error: error"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) tt.mockSetup(mockDB, mockTx) s := &sqlStore{db: mockDB, logger: testlogger.New(t)} gotError := s.txExecute(context.Background(), 0, tt.operation, tt.fn) assert.Equal(t, tt.wantError, gotError) }) } } func TestGobSerialize(t *testing.T) { tests := []struct { name string input interface{} wantErr bool }{ { name: "Serialize string", input: "test string", wantErr: false, }, { name: "Serialize struct", input: struct{ A int }{1}, wantErr: false, }, { name: "Serialize unsupported type", input: make(chan int), wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := gobSerialize(tt.input) if (err != nil) != tt.wantErr { t.Errorf("gobSerialize() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && len(got) == 0 { t.Errorf("gobSerialize() returned empty byte slice") } }) } } func TestGobDeserialize(t *testing.T) { tests := []struct { name string input []byte target interface{} want interface{} wantErr bool }{ { name: "Deserialize to string", input: mustGobSerialize("test string"), target: new(string), want: "test string", wantErr: false, }, { name: "Deserialize to struct", input: mustGobSerialize(struct{ A int }{1}), target: new(struct{ A int }), want: struct{ A int }{1}, wantErr: false, }, { name: "Deserialize with invalid data", input: []byte("invalid"), target: new(string), wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := gobDeserialize(tt.input, tt.target) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.want, reflect.ValueOf(tt.target).Elem().Interface()) } }) } } // mustGobSerialize is a helper function that panics if serialization fails. Used for setting up tests. func mustGobSerialize(v interface{}) []byte { b, err := gobSerialize(v) if err != nil { panic(fmt.Sprintf("mustGobSerialize: %v", err)) } return b } ================================================ FILE: common/persistence/sql/factory.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "fmt" "sync" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) type ( // Factory vends store objects backed by MySQL Factory struct { cfg config.SQL dbConn dbConn clusterName string logger log.Logger parser serialization.Parser taskSerializer serialization.TaskSerializer dc *p.DynamicConfiguration } // dbConn represents a logical mysql connection - it's a // wrapper around the standard sql connection pool with // additional reference counting dbConn struct { sync.Mutex sqlplugin.DB refCnt int cfg *config.SQL } ) // NewFactory returns an instance of a factory object which can be used to create // datastores backed by any kind of SQL store func NewFactory( cfg config.SQL, clusterName string, logger log.Logger, parser serialization.Parser, dc *p.DynamicConfiguration, ) *Factory { return &Factory{ cfg: cfg, clusterName: clusterName, logger: logger, dbConn: newRefCountedDBConn(&cfg), parser: parser, taskSerializer: serialization.NewTaskSerializer(parser), dc: dc, } } // NewTaskStore returns a new task store func (f *Factory) NewTaskStore() (p.TaskStore, error) { conn, err := f.dbConn.get() if err != nil { return nil, err } return newTaskPersistence(conn, f.cfg.NumShards, f.logger, f.parser) } // NewShardStore returns a new shard store func (f *Factory) NewShardStore() (p.ShardStore, error) { conn, err := f.dbConn.get() if err != nil { return nil, err } return NewShardPersistence(conn, f.clusterName, f.logger, f.parser) } // NewHistoryStore returns a new history store func (f *Factory) NewHistoryStore() (p.HistoryStore, error) { conn, err := f.dbConn.get() if err != nil { return nil, err } return NewHistoryV2Persistence(conn, f.logger, f.parser, f.dc) } // NewDomainStore returns a new metadata store func (f *Factory) NewDomainStore() (p.DomainStore, error) { conn, err := f.dbConn.get() if err != nil { return nil, err } return newMetadataPersistenceV2(conn, f.clusterName, f.logger, f.parser) } // NewDomainAuditStore returns a domain audit store func (f *Factory) NewDomainAuditStore() (p.DomainAuditStore, error) { conn, err := f.dbConn.get() if err != nil { return nil, err } return newSQLDomainAuditStore(conn, f.logger, f.parser) } // NewExecutionStore returns an ExecutionStore for a given shardID func (f *Factory) NewExecutionStore(shardID int) (p.ExecutionStore, error) { conn, err := f.dbConn.get() if err != nil { return nil, err } return NewSQLExecutionStore(conn, f.logger, shardID, f.parser, f.taskSerializer, f.dc) } // NewVisibilityStore returns a visibility store // TODO sortByCloseTime will be removed and implemented for https://github.com/uber/cadence/issues/3621 func (f *Factory) NewVisibilityStore(sortByCloseTime bool) (p.VisibilityStore, error) { return NewSQLVisibilityStore(f.cfg, f.logger) } // NewQueue returns a new queue backed by sql func (f *Factory) NewQueue(queueType p.QueueType) (p.QueueStore, error) { conn, err := f.dbConn.get() if err != nil { return nil, err } return newQueueStore(conn, f.logger, queueType) } // NewConfigStore returns a new config store backed by sql. Not Yet Implemented. func (f *Factory) NewConfigStore() (p.ConfigStore, error) { conn, err := f.dbConn.get() if err != nil { return nil, err } return NewSQLConfigStore(conn, f.logger, f.parser) } // Close closes the factory func (f *Factory) Close() { f.dbConn.forceClose() } // newRefCountedDBConn returns a logical mysql connection that // uses reference counting to decide when to close the // underlying connection object. The reference count gets incremented // everytime get() is called and decremented everytime Close() is called func newRefCountedDBConn(cfg *config.SQL) dbConn { return dbConn{cfg: cfg} } // get returns a mysql db connection and increments a reference count // this method will create a new connection, if an existing connection // does not exist func (c *dbConn) get() (sqlplugin.DB, error) { c.Lock() defer c.Unlock() if c.refCnt == 0 { conn, err := NewSQLDB(c.cfg) if err != nil { return nil, err } c.DB = conn } c.refCnt++ return c, nil } // forceClose ignores reference counts and shutsdown the underlying connection pool func (c *dbConn) forceClose() { c.Lock() defer c.Unlock() if c.DB != nil { err := c.DB.Close() if err != nil { fmt.Println("failed to close database connection, may leak some connection", err) } } c.refCnt = 0 } // Close closes the underlying connection if the reference count becomes zero func (c *dbConn) Close() error { c.Lock() defer c.Unlock() c.refCnt-- if c.refCnt == 0 { err := c.DB.Close() c.DB = nil return err } return nil } ================================================ FILE: common/persistence/sql/factory_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sql import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" ) func TestNewFactory(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() cfg := config.SQL{} clusterName := "test" logger := testlogger.New(t) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{} factory := NewFactory(cfg, clusterName, logger, mockParser, dc) assert.NotNil(t, factory) } func TestFactoryNewTaskStore(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() cfg := config.SQL{} clusterName := "test" logger := testlogger.New(t) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{} factory := NewFactory(cfg, clusterName, logger, mockParser, dc) taskStore, err := factory.NewTaskStore() assert.Nil(t, taskStore) assert.Error(t, err) factory.Close() cfg.PluginName = "shared" factory = NewFactory(cfg, clusterName, logger, mockParser, dc) taskStore, err = factory.NewTaskStore() assert.NotNil(t, taskStore) assert.NoError(t, err) factory.Close() } func TestFactoryNewShardStore(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() cfg := config.SQL{} clusterName := "test" logger := testlogger.New(t) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{} factory := NewFactory(cfg, clusterName, logger, mockParser, dc) shardStore, err := factory.NewShardStore() assert.Nil(t, shardStore) assert.Error(t, err) factory.Close() cfg.PluginName = "shared" factory = NewFactory(cfg, clusterName, logger, mockParser, dc) shardStore, err = factory.NewShardStore() assert.NotNil(t, shardStore) assert.NoError(t, err) factory.Close() } func TestFactoryNewHistoryV2Store(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() cfg := config.SQL{} clusterName := "test" logger := testlogger.New(t) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{} factory := NewFactory(cfg, clusterName, logger, mockParser, dc) historyV2Store, err := factory.NewHistoryStore() assert.Nil(t, historyV2Store) assert.Error(t, err) factory.Close() cfg.PluginName = "shared" factory = NewFactory(cfg, clusterName, logger, mockParser, dc) historyV2Store, err = factory.NewHistoryStore() assert.NotNil(t, historyV2Store) assert.NoError(t, err) factory.Close() } func TestFactoryNewMetadataV2Store(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() cfg := config.SQL{} clusterName := "test" logger := testlogger.New(t) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{} factory := NewFactory(cfg, clusterName, logger, mockParser, dc) metadataV2Store, err := factory.NewDomainStore() assert.Nil(t, metadataV2Store) assert.Error(t, err) factory.Close() cfg.PluginName = "shared" factory = NewFactory(cfg, clusterName, logger, mockParser, dc) metadataV2Store, err = factory.NewDomainStore() assert.NotNil(t, metadataV2Store) assert.NoError(t, err) factory.Close() } func TestFactoryNewExecutionStore(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() cfg := config.SQL{} clusterName := "test" logger := testlogger.New(t) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{} factory := NewFactory(cfg, clusterName, logger, mockParser, dc) executionStore, err := factory.NewExecutionStore(0) assert.Nil(t, executionStore) assert.Error(t, err) factory.Close() cfg.PluginName = "shared" factory = NewFactory(cfg, clusterName, logger, mockParser, dc) executionStore, err = factory.NewExecutionStore(0) assert.NotNil(t, executionStore) assert.NoError(t, err) factory.Close() } func TestFactoryNewVisibilityStore(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() cfg := config.SQL{} clusterName := "test" logger := testlogger.New(t) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{} factory := NewFactory(cfg, clusterName, logger, mockParser, dc) visibilityStore, err := factory.NewVisibilityStore(true) assert.Nil(t, visibilityStore) assert.Error(t, err) factory.Close() cfg.PluginName = "shared" factory = NewFactory(cfg, clusterName, logger, mockParser, dc) visibilityStore, err = factory.NewVisibilityStore(true) assert.NotNil(t, visibilityStore) assert.NoError(t, err) factory.Close() } func TestFactoryNewQueue(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() cfg := config.SQL{} clusterName := "test" logger := testlogger.New(t) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{} factory := NewFactory(cfg, clusterName, logger, mockParser, dc) queueStore, err := factory.NewQueue(persistence.DomainReplicationQueueType) assert.Nil(t, queueStore) assert.Error(t, err) factory.Close() cfg.PluginName = "shared" factory = NewFactory(cfg, clusterName, logger, mockParser, dc) queueStore, err = factory.NewQueue(persistence.DomainReplicationQueueType) assert.NotNil(t, queueStore) assert.NoError(t, err) factory.Close() } func TestFactoryNewConfigStore(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() cfg := config.SQL{} clusterName := "test" logger := testlogger.New(t) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{} factory := NewFactory(cfg, clusterName, logger, mockParser, dc) configStore, err := factory.NewConfigStore() assert.Nil(t, configStore) assert.Error(t, err) factory.Close() cfg.PluginName = "shared" factory = NewFactory(cfg, clusterName, logger, mockParser, dc) configStore, err = factory.NewConfigStore() assert.NotNil(t, configStore) assert.NoError(t, err) factory.Close() } func TestFactoryNewDomainAuditStore(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() cfg := config.SQL{} clusterName := "test" logger := testlogger.New(t) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{} factory := NewFactory(cfg, clusterName, logger, mockParser, dc) domainAuditStore, err := factory.NewDomainAuditStore() assert.Nil(t, domainAuditStore) assert.Error(t, err) factory.Close() cfg.PluginName = "shared" factory = NewFactory(cfg, clusterName, logger, mockParser, dc) domainAuditStore, err = factory.NewDomainAuditStore() assert.NotNil(t, domainAuditStore) assert.NoError(t, err) factory.Close() } ================================================ FILE: common/persistence/sql/main_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sql import ( "os" "testing" ) func TestMain(m *testing.M) { RegisterPlugin("shared", &fakePlugin{}) os.Exit(m.Run()) } ================================================ FILE: common/persistence/sql/plugin.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "fmt" "sort" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) var supportedPlugins = map[string]sqlplugin.Plugin{} // RegisterPlugin will register a SQL plugin func RegisterPlugin(pluginName string, plugin sqlplugin.Plugin) { if _, ok := supportedPlugins[pluginName]; ok { panic("plugin " + pluginName + " already registered") } supportedPlugins[pluginName] = plugin } // RegisterPluginIfNotExists will register a SQL plugin only if a plugin with same name has not already been registered func RegisterPluginIfNotExists(pluginName string, plugin sqlplugin.Plugin) { if _, ok := supportedPlugins[pluginName]; !ok { supportedPlugins[pluginName] = plugin } } // PluginRegistered returns true if plugin with given name has been registered, false otherwise func PluginRegistered(pluginName string) bool { _, ok := supportedPlugins[pluginName] return ok } // GetRegisteredPluginNames returns the list of registered plugin names func GetRegisteredPluginNames() []string { var plugins []string for k := range supportedPlugins { plugins = append(plugins, k) } sort.Strings(plugins) return plugins } // NewSQLDB creates a returns a reference to a logical connection to the // underlying SQL database. The returned object is to tied to a single // SQL database and the object can be used to perform CRUD operations on // the tables in the database func NewSQLDB(cfg *config.SQL) (sqlplugin.DB, error) { plugin, ok := supportedPlugins[cfg.PluginName] if !ok { return nil, fmt.Errorf("not supported plugin %v, only supported: %v", cfg.PluginName, supportedPlugins) } return plugin.CreateDB(cfg) } // NewSQLAdminDB returns a AdminDB func NewSQLAdminDB(cfg *config.SQL) (sqlplugin.AdminDB, error) { plugin, ok := supportedPlugins[cfg.PluginName] if !ok { return nil, fmt.Errorf("not supported plugin %v, only supported: %v", cfg.PluginName, supportedPlugins) } return plugin.CreateAdminDB(cfg) } ================================================ FILE: common/persistence/sql/plugin_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sql import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) type fakePlugin struct{} var _ sqlplugin.Plugin = (*fakePlugin)(nil) func (f *fakePlugin) CreateDB(cfg *config.SQL) (sqlplugin.DB, error) { return nil, nil } func (f *fakePlugin) CreateAdminDB(cfg *config.SQL) (sqlplugin.AdminDB, error) { return nil, nil } func TestPluginRegistration(t *testing.T) { assert.NotPanics(t, func() { RegisterPlugin("fake", &fakePlugin{}) }, "RegisterPlugin failed to register a plugin") assert.Panics(t, func() { RegisterPlugin("fake", &fakePlugin{}) }, "RegisterPlugin failed to panic when registering a plugin with the same name") assert.NotPanics(t, func() { RegisterPluginIfNotExists("fake", &fakePlugin{}) }, "RegisterPluginIfNotExists failed to register a plugin") assert.NotPanics(t, func() { RegisterPluginIfNotExists("fake2", &fakePlugin{}) }, "RegisterPluginIfNotExists failed to register a plugin") assert.True(t, PluginRegistered("fake"), "PluginRegistered failed to return true for a registered plugin") assert.True(t, PluginRegistered("fake2"), "PluginRegistered failed to return true for a registered plugin") assert.False(t, PluginRegistered("fake3"), "PluginRegistered failed to return false for an unregistered plugin") assert.Equal(t, []string{"fake", "fake2", "shared"}, GetRegisteredPluginNames(), "GetRegisteredPluginNames failed to return the correct list of registered plugins") _, err := NewSQLDB(&config.SQL{PluginName: "fake"}) assert.NoError(t, err, "NewSQLDB failed to create a DB with a registered plugin") _, err = NewSQLDB(&config.SQL{PluginName: "fake2"}) assert.NoError(t, err, "NewSQLDB failed to create a DB with a registered plugin") _, err = NewSQLDB(&config.SQL{PluginName: "fake3"}) assert.Error(t, err, "NewSQLDB failed to return an error with an unregistered plugin") } ================================================ FILE: common/persistence/sql/sql_config_store.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sql import ( "context" "fmt" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) type ( sqlConfigStore struct { sqlStore } ) // NewSQLConfigStore creates a config store for SQL func NewSQLConfigStore( db sqlplugin.DB, logger log.Logger, parser serialization.Parser, ) (persistence.ConfigStore, error) { return &sqlConfigStore{ sqlStore: sqlStore{ db: db, logger: logger, parser: parser, }, }, nil } func (m *sqlConfigStore) FetchConfig(ctx context.Context, configType persistence.ConfigType) (*persistence.InternalConfigStoreEntry, error) { entry, err := m.db.SelectLatestConfig(ctx, int(configType)) if m.db.IsNotFoundError(err) { return nil, nil } if err != nil { return nil, convertCommonErrors(m.db, "FetchConfig", "", err) } return entry, nil } func (m *sqlConfigStore) UpdateConfig(ctx context.Context, value *persistence.InternalConfigStoreEntry) error { err := m.db.InsertConfig(ctx, value) if err != nil { if m.db.IsDupEntryError(err) { return &persistence.ConditionFailedError{Msg: fmt.Sprintf("Version %v already exists. Condition Failed", value.Version)} } return convertCommonErrors(m.db, "UpdateConfig", "", err) } return nil } ================================================ FILE: common/persistence/sql/sql_config_store_test.go ================================================ // Modifications Copyright (c) 2020 Uber Technologies Inc. // Copyright (c) 2020 Temporal Technologies, Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sql import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) func TestFetchConfig(t *testing.T) { testCases := []struct { name string configType persistence.ConfigType mockSetup func(*sqlplugin.MockDB) want *persistence.InternalConfigStoreEntry wantErr bool }{ { name: "Success case", configType: persistence.DynamicConfig, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().SelectLatestConfig(gomock.Any(), gomock.Any()).Return(&persistence.InternalConfigStoreEntry{}, nil) mockDB.EXPECT().IsNotFoundError(nil).Return(false) }, want: &persistence.InternalConfigStoreEntry{}, wantErr: false, }, { name: "Not found error", configType: persistence.DynamicConfig, mockSetup: func(mockDB *sqlplugin.MockDB) { notFoundErr := errors.New("not found") mockDB.EXPECT().SelectLatestConfig(gomock.Any(), gomock.Any()).Return(nil, notFoundErr) mockDB.EXPECT().IsNotFoundError(notFoundErr).Return(true) }, want: nil, wantErr: false, }, { name: "Database error", configType: persistence.DynamicConfig, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("db error") mockDB.EXPECT().SelectLatestConfig(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(false) mockDB.EXPECT().IsNotFoundError(err).Return(false) mockDB.EXPECT().IsTimeoutError(err).Return(true) }, want: nil, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := NewSQLConfigStore(mockDB, nil, nil) require.NoError(t, err, "Failed to create sql config store") tc.mockSetup(mockDB) got, err := store.FetchConfig(context.Background(), tc.configType) if tc.wantErr { assert.Error(t, err, "Expected an error for test case: %s", tc.name) } else { assert.NoError(t, err, "Did not expect an error for test case: %s", tc.name) assert.Equal(t, tc.want, got, "Unexpected result for test case: %s", tc.name) } }) } } func TestUpdateConfig(t *testing.T) { testEntry := &persistence.InternalConfigStoreEntry{ RowType: int(persistence.DynamicConfig), Version: 0, Values: &persistence.DataBlob{ Data: []byte(`0x0000`), }, } testCases := []struct { name string value *persistence.InternalConfigStoreEntry mockSetup func(*sqlplugin.MockDB) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", value: testEntry, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().InsertConfig(gomock.Any(), gomock.Any()).Return(nil) }, wantErr: false, }, { name: "Duplicate Entry error", value: testEntry, mockSetup: func(mockDB *sqlplugin.MockDB) { dupError := errors.New("duplicate entry") mockDB.EXPECT().InsertConfig(gomock.Any(), gomock.Any()).Return(dupError) mockDB.EXPECT().IsDupEntryError(dupError).Return(true) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *persistence.ConditionFailedError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be ConditionFailedError") }, }, { name: "Database error", value: testEntry, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("db error") mockDB.EXPECT().InsertConfig(gomock.Any(), gomock.Any()).Return(err) mockDB.EXPECT().IsDupEntryError(err).Return(false) mockDB.EXPECT().IsNotFoundError(err).Return(false) mockDB.EXPECT().IsTimeoutError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := NewSQLConfigStore(mockDB, nil, nil) require.NoError(t, err, "Failed to create sql config store") tc.mockSetup(mockDB) err = store.UpdateConfig(context.Background(), tc.value) if tc.wantErr { assert.Error(t, err, "Expected an error for test case: %s", tc.name) if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case: %s", tc.name) } }) } } ================================================ FILE: common/persistence/sql/sql_domain_audit_store.go ================================================ // Copyright (c) 2025 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "fmt" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) type sqlDomainAuditStore struct { sqlStore } // domainAuditLogPageToken is used for pagination type domainAuditLogPageToken struct { CreatedTime time.Time `json:"created_time"` EventID string `json:"event_id"` } // newSQLDomainAuditStore creates an instance of sqlDomainAuditStore func newSQLDomainAuditStore( db sqlplugin.DB, logger log.Logger, parser serialization.Parser, ) (persistence.DomainAuditStore, error) { return &sqlDomainAuditStore{ sqlStore: sqlStore{ db: db, logger: logger, parser: parser, }, }, nil } // CreateDomainAuditLog creates a new domain audit log entry func (m *sqlDomainAuditStore) CreateDomainAuditLog( ctx context.Context, request *persistence.InternalCreateDomainAuditLogRequest, ) (*persistence.CreateDomainAuditLogResponse, error) { row := &sqlplugin.DomainAuditLogRow{ DomainID: request.DomainID, EventID: request.EventID, StateBefore: getDataBlobBytes(request.StateBefore), StateBeforeEncoding: getDataBlobEncoding(request.StateBefore), StateAfter: getDataBlobBytes(request.StateAfter), StateAfterEncoding: getDataBlobEncoding(request.StateAfter), OperationType: request.OperationType, CreatedTime: request.CreatedTime, LastUpdatedTime: request.LastUpdatedTime, Identity: request.Identity, IdentityType: request.IdentityType, Comment: request.Comment, } _, err := m.db.InsertIntoDomainAuditLog(ctx, row) if err != nil { return nil, convertCommonErrors(m.db, "CreateDomainAuditLog", "", err) } return &persistence.CreateDomainAuditLogResponse{ EventID: request.EventID, }, nil } // GetDomainAuditLogs retrieves domain audit logs func (m *sqlDomainAuditStore) GetDomainAuditLogs( ctx context.Context, request *persistence.GetDomainAuditLogsRequest, ) (*persistence.InternalGetDomainAuditLogsResponse, error) { minCreatedTime := time.Unix(0, 0) maxCreatedTime := time.Now().UTC() if request.MinCreatedTime != nil { minCreatedTime = *request.MinCreatedTime } if request.MaxCreatedTime != nil { maxCreatedTime = *request.MaxCreatedTime } pageMaxCreatedTime := maxCreatedTime // if next page token is not present, set pageMinEventID to largest possible uuid // to prevent the query from returning rows where created_time is equal to pageMaxCreatedTime pageMinEventID := "ffffffff-ffff-ffff-ffff-ffffffffffff" if request.NextPageToken != nil { page := domainAuditLogPageToken{} if err := gobDeserialize(request.NextPageToken, &page); err != nil { return nil, fmt.Errorf("unable to decode next page token") } pageMaxCreatedTime = page.CreatedTime pageMinEventID = page.EventID } filter := &sqlplugin.DomainAuditLogFilter{ DomainID: request.DomainID, OperationType: request.OperationType, MinCreatedTime: &minCreatedTime, PageSize: request.PageSize, PageMaxCreatedTime: &pageMaxCreatedTime, PageMinEventID: &pageMinEventID, } rows, err := m.db.SelectFromDomainAuditLogs(ctx, filter) if err != nil { return nil, convertCommonErrors(m.db, "GetDomainAuditLogs", "", err) } var nextPageToken []byte if request.PageSize > 0 && len(rows) >= request.PageSize { // there could be more results lastRow := rows[request.PageSize-1] token := domainAuditLogPageToken{ CreatedTime: lastRow.CreatedTime, EventID: lastRow.EventID, } nextPageToken, err = gobSerialize(token) if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("error serializing nextPageToken:%v", err)} } } var auditLogs []*persistence.InternalDomainAuditLog for _, row := range rows { auditLogs = append(auditLogs, deseralizeDomainAuditLogRow(row)) } return &persistence.InternalGetDomainAuditLogsResponse{ AuditLogs: auditLogs, NextPageToken: nextPageToken, }, nil } func getDataBlobBytes(blob *persistence.DataBlob) []byte { if blob == nil { return []byte{} } return blob.Data } func getDataBlobEncoding(blob *persistence.DataBlob) constants.EncodingType { if blob == nil { return constants.EncodingTypeEmpty } return blob.Encoding } func deseralizeDomainAuditLogRow(row *sqlplugin.DomainAuditLogRow) *persistence.InternalDomainAuditLog { auditLog := &persistence.InternalDomainAuditLog{ EventID: row.EventID, DomainID: row.DomainID, OperationType: row.OperationType, CreatedTime: row.CreatedTime, LastUpdatedTime: row.LastUpdatedTime, Identity: row.Identity, IdentityType: row.IdentityType, Comment: row.Comment, } if len(row.StateBefore) > 0 { auditLog.StateBefore = &persistence.DataBlob{ Encoding: row.StateBeforeEncoding, Data: row.StateBefore, } } if len(row.StateAfter) > 0 { auditLog.StateAfter = &persistence.DataBlob{ Encoding: row.StateAfterEncoding, Data: row.StateAfter, } } return auditLog } ================================================ FILE: common/persistence/sql/sql_domain_audit_store_test.go ================================================ // Copyright (c) 2025 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) func setUpMocksForDomainAuditStore(t *testing.T) (*sqlDomainAuditStore, *sqlplugin.MockDB) { ctrl := gomock.NewController(t) dbMock := sqlplugin.NewMockDB(ctrl) domainAuditStore := &sqlDomainAuditStore{ sqlStore: sqlStore{db: dbMock}, } return domainAuditStore, dbMock } func TestCreateDomainAuditLog(t *testing.T) { ctx := context.Background() now := time.Unix(1234567890, 0) stateBeforeBlob := &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRWSnappy, Data: []byte("state-before-data"), } stateAfterBlob := &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("state-after-data"), } tests := map[string]struct { setupMock func(*sqlplugin.MockDB) request *persistence.InternalCreateDomainAuditLogRequest expectError bool expectedID string }{ "success with full data": { setupMock: func(dbMock *sqlplugin.MockDB) { expectedRow := &sqlplugin.DomainAuditLogRow{ DomainID: "d1111111-1111-1111-1111-111111111111", EventID: "e1111111-1111-1111-1111-111111111111", StateBefore: stateBeforeBlob.Data, StateBeforeEncoding: stateBeforeBlob.Encoding, StateAfter: stateAfterBlob.Data, StateAfterEncoding: stateAfterBlob.Encoding, OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now, LastUpdatedTime: now, Identity: "test-user", IdentityType: "user", Comment: "test comment", } dbMock.EXPECT().InsertIntoDomainAuditLog(ctx, expectedRow).Return(&sqlResult{rowsAffected: 1}, nil).Times(1) }, request: &persistence.InternalCreateDomainAuditLogRequest{ DomainID: "d1111111-1111-1111-1111-111111111111", EventID: "e1111111-1111-1111-1111-111111111111", StateBefore: stateBeforeBlob, StateAfter: stateAfterBlob, OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now, LastUpdatedTime: now, Identity: "test-user", IdentityType: "user", Comment: "test comment", }, expectError: false, expectedID: "e1111111-1111-1111-1111-111111111111", }, "success with nil state blobs": { setupMock: func(dbMock *sqlplugin.MockDB) { expectedRow := &sqlplugin.DomainAuditLogRow{ DomainID: "d1111111-1111-1111-1111-111111111111", EventID: "e1111111-1111-1111-1111-111111111111", StateBefore: []byte{}, StateBeforeEncoding: constants.EncodingTypeEmpty, StateAfter: []byte{}, StateAfterEncoding: constants.EncodingTypeEmpty, OperationType: persistence.DomainAuditOperationTypeCreate, CreatedTime: now, LastUpdatedTime: now, Identity: "system", IdentityType: "system", Comment: "", } dbMock.EXPECT().InsertIntoDomainAuditLog(ctx, expectedRow).Return(&sqlResult{rowsAffected: 1}, nil).Times(1) }, request: &persistence.InternalCreateDomainAuditLogRequest{ DomainID: "d1111111-1111-1111-1111-111111111111", EventID: "e1111111-1111-1111-1111-111111111111", StateBefore: nil, StateAfter: nil, OperationType: persistence.DomainAuditOperationTypeCreate, CreatedTime: now, LastUpdatedTime: now, Identity: "system", IdentityType: "system", Comment: "", }, expectError: false, expectedID: "e1111111-1111-1111-1111-111111111111", }, "database error": { setupMock: func(dbMock *sqlplugin.MockDB) { err := errors.New("database error") dbMock.EXPECT().InsertIntoDomainAuditLog(ctx, gomock.Any()).Return(nil, err).Times(1) dbMock.EXPECT().IsNotFoundError(err).Return(false).AnyTimes() dbMock.EXPECT().IsTimeoutError(err).Return(false).AnyTimes() dbMock.EXPECT().IsThrottlingError(err).Return(false).AnyTimes() dbMock.EXPECT().IsDupEntryError(err).Return(false).AnyTimes() }, request: &persistence.InternalCreateDomainAuditLogRequest{ DomainID: "d1111111-1111-1111-1111-111111111111", EventID: "e1111111-1111-1111-1111-111111111111", OperationType: persistence.DomainAuditOperationTypeFailover, CreatedTime: now, LastUpdatedTime: now, Identity: "test-user", IdentityType: "user", }, expectError: true, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { store, dbMock := setUpMocksForDomainAuditStore(t) tc.setupMock(dbMock) resp, err := store.CreateDomainAuditLog(ctx, tc.request) if tc.expectError { assert.Error(t, err) assert.Nil(t, resp) } else { assert.NoError(t, err) require.NotNil(t, resp) assert.Equal(t, tc.expectedID, resp.EventID) } }) } } func TestGetDomainAuditLogs(t *testing.T) { ctx := context.Background() now := time.Unix(1234567890, 0) minTime := now.Add(-24 * time.Hour) maxTime := now maxUUID := "ffffffff-ffff-ffff-ffff-ffffffffffff" pageSize := 2 pageSize2 := 3 tests := map[string]struct { setupMock func(*sqlplugin.MockDB) request *persistence.GetDomainAuditLogsRequest expectError bool expectedCount int validateResult func(*testing.T, *persistence.InternalGetDomainAuditLogsResponse) }{ "success - first page with results": { setupMock: func(dbMock *sqlplugin.MockDB) { rows := []*sqlplugin.DomainAuditLogRow{ { EventID: "e1111111-1111-1111-1111-111111111111", DomainID: "d1111111-1111-1111-1111-111111111111", OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now, LastUpdatedTime: now, Identity: "user-1", IdentityType: "user", Comment: "comment 1", StateBefore: []byte("state-before-1"), StateBeforeEncoding: constants.EncodingTypeThriftRWSnappy, StateAfter: []byte("state-after-1"), StateAfterEncoding: constants.EncodingTypeThriftRW, }, { EventID: "e2222222-2222-2222-2222-222222222222", DomainID: "d1111111-1111-1111-1111-111111111111", OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now, LastUpdatedTime: now, Identity: "user-2", IdentityType: "user", Comment: "comment 2", StateBefore: []byte("state-before-2"), StateBeforeEncoding: constants.EncodingTypeThriftRWSnappy, StateAfter: []byte("state-after-2"), StateAfterEncoding: constants.EncodingTypeThriftRWSnappy, }, } expectedFilter := &sqlplugin.DomainAuditLogFilter{ DomainID: "d1111111-1111-1111-1111-111111111111", OperationType: persistence.DomainAuditOperationTypeUpdate, MinCreatedTime: &minTime, PageSize: pageSize, PageMaxCreatedTime: &maxTime, PageMinEventID: &maxUUID, } dbMock.EXPECT().SelectFromDomainAuditLogs(ctx, expectedFilter).Return(rows, nil).Times(1) }, request: &persistence.GetDomainAuditLogsRequest{ DomainID: "d1111111-1111-1111-1111-111111111111", OperationType: persistence.DomainAuditOperationTypeUpdate, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, PageSize: 2, NextPageToken: nil, }, expectError: false, expectedCount: 2, validateResult: func(t *testing.T, resp *persistence.InternalGetDomainAuditLogsResponse) { require.Len(t, resp.AuditLogs, 2) assert.Equal(t, "e1111111-1111-1111-1111-111111111111", resp.AuditLogs[0].EventID) assert.Equal(t, "d1111111-1111-1111-1111-111111111111", resp.AuditLogs[0].DomainID) assert.Equal(t, persistence.DomainAuditOperationTypeUpdate, resp.AuditLogs[0].OperationType) assert.NotNil(t, resp.AuditLogs[0].StateBefore) assert.Equal(t, constants.EncodingTypeThriftRWSnappy, resp.AuditLogs[0].StateBefore.Encoding) assert.Equal(t, []byte("state-before-1"), resp.AuditLogs[0].StateBefore.Data) assert.NotNil(t, resp.AuditLogs[0].StateAfter) assert.Equal(t, constants.EncodingTypeThriftRW, resp.AuditLogs[0].StateAfter.Encoding) assert.Equal(t, "e2222222-2222-2222-2222-222222222222", resp.AuditLogs[1].EventID) assert.NotNil(t, resp.AuditLogs[1].StateBefore) assert.NotNil(t, resp.AuditLogs[1].StateAfter) assert.NotNil(t, resp.NextPageToken) }, }, "success - subsequent page with nextPageToken": { setupMock: func(dbMock *sqlplugin.MockDB) { expectedCreatedTime := now expectedEventID := "e1111111-1111-1111-1111-111111111111" rows := []*sqlplugin.DomainAuditLogRow{ { EventID: "e3333333-3333-3333-3333-333333333333", DomainID: "d1111111-1111-1111-1111-111111111111", OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: now.Add(-2 * time.Hour), LastUpdatedTime: now.Add(-2 * time.Hour), Identity: "user-3", IdentityType: "user", Comment: "comment 3", StateBefore: []byte("state-before-3"), StateBeforeEncoding: constants.EncodingTypeThriftRWSnappy, StateAfter: []byte("state-after-3"), StateAfterEncoding: constants.EncodingTypeThriftRW, }, { EventID: "e2222222-2222-2222-2222-222222222222", DomainID: "d1111111-1111-1111-1111-111111111111", OperationType: persistence.DomainAuditOperationTypeUpdate, CreatedTime: expectedCreatedTime, LastUpdatedTime: expectedCreatedTime, Identity: "user-2", IdentityType: "user", Comment: "comment 2", StateBefore: []byte("state-before-2"), StateBeforeEncoding: constants.EncodingTypeThriftRWSnappy, StateAfter: []byte("state-after-2"), StateAfterEncoding: constants.EncodingTypeThriftRW, }, } expectedFilter := &sqlplugin.DomainAuditLogFilter{ DomainID: "d1111111-1111-1111-1111-111111111111", OperationType: persistence.DomainAuditOperationTypeUpdate, MinCreatedTime: &minTime, PageSize: pageSize2, PageMaxCreatedTime: &expectedCreatedTime, PageMinEventID: &expectedEventID, } dbMock.EXPECT().SelectFromDomainAuditLogs(ctx, expectedFilter).Return(rows, nil).Times(1) }, request: func() *persistence.GetDomainAuditLogsRequest { pageToken := domainAuditLogPageToken{ CreatedTime: now, EventID: "e1111111-1111-1111-1111-111111111111", } encodedToken, _ := gobSerialize(pageToken) return &persistence.GetDomainAuditLogsRequest{ DomainID: "d1111111-1111-1111-1111-111111111111", OperationType: persistence.DomainAuditOperationTypeUpdate, MinCreatedTime: &minTime, MaxCreatedTime: &maxTime, PageSize: 3, NextPageToken: encodedToken, } }(), expectError: false, expectedCount: 2, validateResult: func(t *testing.T, resp *persistence.InternalGetDomainAuditLogsResponse) { require.Len(t, resp.AuditLogs, 2) assert.Equal(t, "e3333333-3333-3333-3333-333333333333", resp.AuditLogs[0].EventID) assert.Equal(t, "e2222222-2222-2222-2222-222222222222", resp.AuditLogs[1].EventID) assert.Nil(t, resp.NextPageToken) }, }, "success with empty state blobs": { setupMock: func(dbMock *sqlplugin.MockDB) { rows := []*sqlplugin.DomainAuditLogRow{ { EventID: "e3333333-3333-3333-3333-333333333333", DomainID: "d2222222-2222-2222-2222-222222222222", OperationType: persistence.DomainAuditOperationTypeCreate, CreatedTime: now, LastUpdatedTime: now, Identity: "system", IdentityType: "system", Comment: "created", StateBefore: []byte{}, // Empty slice StateBeforeEncoding: constants.EncodingTypeEmpty, StateAfter: nil, // Nil StateAfterEncoding: constants.EncodingTypeEmpty, }, } dbMock.EXPECT().SelectFromDomainAuditLogs(ctx, gomock.Any()).Return(rows, nil).Times(1) }, request: &persistence.GetDomainAuditLogsRequest{ DomainID: "d2222222-2222-2222-2222-222222222222", PageSize: 10, }, expectError: false, expectedCount: 1, validateResult: func(t *testing.T, resp *persistence.InternalGetDomainAuditLogsResponse) { require.Len(t, resp.AuditLogs, 1) assert.Equal(t, "e3333333-3333-3333-3333-333333333333", resp.AuditLogs[0].EventID) assert.Nil(t, resp.AuditLogs[0].StateBefore) assert.Nil(t, resp.AuditLogs[0].StateAfter) }, }, "success with no results": { setupMock: func(dbMock *sqlplugin.MockDB) { dbMock.EXPECT().SelectFromDomainAuditLogs(ctx, gomock.Any()).Return([]*sqlplugin.DomainAuditLogRow{}, nil).Times(1) }, request: &persistence.GetDomainAuditLogsRequest{ DomainID: "d3333333-3333-3333-3333-333333333333", PageSize: 10, }, expectError: false, expectedCount: 0, validateResult: func(t *testing.T, resp *persistence.InternalGetDomainAuditLogsResponse) { assert.Empty(t, resp.AuditLogs) assert.Nil(t, resp.NextPageToken) }, }, "invalid page token": { setupMock: func(dbMock *sqlplugin.MockDB) { // No DB call expected since deserialization should fail first }, request: &persistence.GetDomainAuditLogsRequest{ DomainID: "d3333333-3333-3333-3333-333333333333", PageSize: 10, NextPageToken: []byte("invalid-token-data"), }, expectError: true, }, "database error": { setupMock: func(dbMock *sqlplugin.MockDB) { err := errors.New("database error") dbMock.EXPECT().SelectFromDomainAuditLogs(ctx, gomock.Any()).Return(nil, err).Times(1) dbMock.EXPECT().IsNotFoundError(err).Return(false).AnyTimes() dbMock.EXPECT().IsTimeoutError(err).Return(false).AnyTimes() dbMock.EXPECT().IsThrottlingError(err).Return(false).AnyTimes() dbMock.EXPECT().IsDupEntryError(err).Return(false).AnyTimes() }, request: &persistence.GetDomainAuditLogsRequest{ DomainID: "d3333333-3333-3333-3333-333333333333", PageSize: 10, }, expectError: true, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { store, dbMock := setUpMocksForDomainAuditStore(t) tc.setupMock(dbMock) resp, err := store.GetDomainAuditLogs(ctx, tc.request) if tc.expectError { assert.Error(t, err) assert.Nil(t, resp) } else { assert.NoError(t, err) require.NotNil(t, resp) assert.Len(t, resp.AuditLogs, tc.expectedCount) if tc.validateResult != nil { tc.validateResult(t, resp) } } }) } } func TestGetDataBlobBytes(t *testing.T) { tests := map[string]struct { input *persistence.DataBlob expected []byte }{ "nil blob": { input: nil, expected: []byte{}, }, "blob with data": { input: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRWSnappy, Data: []byte("test-data"), }, expected: []byte("test-data"), }, "blob with empty data": { input: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte{}, }, expected: []byte{}, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { result := getDataBlobBytes(tc.input) assert.Equal(t, tc.expected, result) }) } } func TestGetDataBlobEncoding(t *testing.T) { tests := map[string]struct { input *persistence.DataBlob expected constants.EncodingType }{ "nil blob": { input: nil, expected: constants.EncodingTypeEmpty, }, "blob with ThriftRWSnappy encoding": { input: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRWSnappy, Data: []byte("test-data"), }, expected: constants.EncodingTypeThriftRWSnappy, }, "blob with ThriftRW encoding": { input: &persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("test-data"), }, expected: constants.EncodingTypeThriftRW, }, "blob with empty encoding": { input: &persistence.DataBlob{ Encoding: "", Data: []byte("test-data"), }, expected: constants.EncodingTypeEmpty, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { result := getDataBlobEncoding(tc.input) assert.Equal(t, tc.expected, result) }) } } ================================================ FILE: common/persistence/sql/sql_domain_store.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "fmt" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) type sqlDomainStore struct { sqlStore activeClusterName string } // newMetadataPersistenceV2 creates an instance of sqlDomainStore func newMetadataPersistenceV2( db sqlplugin.DB, currentClusterName string, logger log.Logger, parser serialization.Parser, ) (persistence.DomainStore, error) { return &sqlDomainStore{ sqlStore: sqlStore{ db: db, logger: logger, parser: parser, }, activeClusterName: currentClusterName, }, nil } func updateMetadata(ctx context.Context, tx sqlplugin.Tx, oldNotificationVersion int64) error { result, err := tx.UpdateDomainMetadata(ctx, &sqlplugin.DomainMetadataRow{NotificationVersion: oldNotificationVersion}) if err != nil { return convertCommonErrors(tx, "updateDomainMetadata", "", err) } rowsAffected, err := result.RowsAffected() if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("Could not verify whether domain metadata update occurred. Error: %v", err), } } else if rowsAffected != 1 { return &types.InternalServiceError{ Message: "Failed to update domain metadata. <>1 rows affected.", } } return nil } func lockMetadata(ctx context.Context, tx sqlplugin.Tx) error { err := tx.LockDomainMetadata(ctx) if err != nil { return convertCommonErrors(tx, "lockDomainMetadata", "", err) } return nil } func (m *sqlDomainStore) CreateDomain( ctx context.Context, request *persistence.InternalCreateDomainRequest, ) (*persistence.CreateDomainResponse, error) { metadata, err := m.GetMetadata(ctx) if err != nil { return nil, err } clusters := make([]string, len(request.ReplicationConfig.Clusters)) for i := range clusters { clusters[i] = request.ReplicationConfig.Clusters[i].ClusterName } var badBinaries []byte badBinariesEncoding := string(constants.EncodingTypeEmpty) if request.Config.BadBinaries != nil { badBinaries = request.Config.BadBinaries.Data badBinariesEncoding = string(request.Config.BadBinaries.GetEncoding()) } var isolationGroups []byte var isolationGroupsEncoding string if request.Config != nil && request.Config.IsolationGroups != nil { isolationGroups = request.Config.IsolationGroups.GetData() isolationGroupsEncoding = request.Config.IsolationGroups.GetEncodingString() } var asyncWorkflowsCfg []byte var asyncWorkflowsEncoding string if request.Config != nil && request.Config.AsyncWorkflowsConfig != nil { asyncWorkflowsCfg = request.Config.AsyncWorkflowsConfig.GetData() asyncWorkflowsEncoding = request.Config.AsyncWorkflowsConfig.GetEncodingString() } var activeClustersConfig []byte var activeClustersConfigEncoding string if request.ReplicationConfig != nil && request.ReplicationConfig.ActiveClustersConfig != nil { activeClustersConfig = request.ReplicationConfig.ActiveClustersConfig.GetData() activeClustersConfigEncoding = request.ReplicationConfig.ActiveClustersConfig.GetEncodingString() } domainInfo := &serialization.DomainInfo{ Name: request.Info.Name, Status: int32(request.Info.Status), Description: request.Info.Description, Owner: request.Info.OwnerEmail, Data: request.Info.Data, Retention: request.Config.Retention, EmitMetric: request.Config.EmitMetric, ArchivalBucket: request.Config.ArchivalBucket, ArchivalStatus: int16(request.Config.ArchivalStatus), HistoryArchivalStatus: int16(request.Config.HistoryArchivalStatus), HistoryArchivalURI: request.Config.HistoryArchivalURI, VisibilityArchivalStatus: int16(request.Config.VisibilityArchivalStatus), VisibilityArchivalURI: request.Config.VisibilityArchivalURI, ActiveClusterName: request.ReplicationConfig.ActiveClusterName, ActiveClustersConfig: activeClustersConfig, ActiveClustersConfigEncoding: activeClustersConfigEncoding, Clusters: clusters, ConfigVersion: request.ConfigVersion, FailoverVersion: request.FailoverVersion, NotificationVersion: metadata.NotificationVersion, FailoverNotificationVersion: persistence.InitialFailoverNotificationVersion, PreviousFailoverVersion: constants.InitialPreviousFailoverVersion, LastUpdatedTimestamp: request.LastUpdatedTime, BadBinaries: badBinaries, BadBinariesEncoding: badBinariesEncoding, IsolationGroups: isolationGroups, IsolationGroupsEncoding: isolationGroupsEncoding, AsyncWorkflowConfig: asyncWorkflowsCfg, AsyncWorkflowConfigEncoding: asyncWorkflowsEncoding, } blob, err := m.parser.DomainInfoToBlob(domainInfo) if err != nil { return nil, err } var resp *persistence.CreateDomainResponse err = m.txExecute(ctx, sqlplugin.DbDefaultShard, "CreateDomain", func(tx sqlplugin.Tx) error { if _, err1 := tx.InsertIntoDomain(ctx, &sqlplugin.DomainRow{ Name: request.Info.Name, ID: serialization.MustParseUUID(request.Info.ID), Data: blob.Data, DataEncoding: string(blob.Encoding), IsGlobal: request.IsGlobalDomain, }); err1 != nil { if m.db.IsDupEntryError(err1) { return &types.DomainAlreadyExistsError{ Message: fmt.Sprintf("name: %v", request.Info.Name), } } return err1 } if err1 := lockMetadata(ctx, tx); err1 != nil { return err1 } if err1 := updateMetadata(ctx, tx, metadata.NotificationVersion); err1 != nil { return err1 } resp = &persistence.CreateDomainResponse{ID: request.Info.ID} return nil }) return resp, err } func (m *sqlDomainStore) GetDomain( ctx context.Context, request *persistence.GetDomainRequest, ) (*persistence.InternalGetDomainResponse, error) { filter := &sqlplugin.DomainFilter{} switch { case request.Name != "" && request.ID != "": return nil, &types.BadRequestError{ Message: "GetDomain operation failed. Both ID and Name specified in request.", } case request.Name != "": filter.Name = &request.Name case request.ID != "": filter.ID = serialization.UUIDPtr(serialization.MustParseUUID(request.ID)) default: return nil, &types.BadRequestError{ Message: "GetDomain operation failed. Both ID and Name are empty.", } } rows, err := m.db.SelectFromDomain(ctx, filter) if err != nil { switch err { case sql.ErrNoRows: // We did not return in the above for-loop because there were no rows. identity := request.Name if len(request.ID) > 0 { identity = request.ID } return nil, &types.EntityNotExistsError{ Message: fmt.Sprintf("Domain %s does not exist.", identity), } default: return nil, convertCommonErrors(m.db, "GetDomain", "", err) } } response, err := m.domainRowToGetDomainResponse(&rows[0]) if err != nil { return nil, err } return response, nil } func (m *sqlDomainStore) domainRowToGetDomainResponse(row *sqlplugin.DomainRow) (*persistence.InternalGetDomainResponse, error) { domainInfo, err := m.parser.DomainInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } clusters := make([]*persistence.ClusterReplicationConfig, len(domainInfo.Clusters)) for i := range domainInfo.Clusters { clusters[i] = &persistence.ClusterReplicationConfig{ClusterName: domainInfo.Clusters[i]} } var badBinaries *persistence.DataBlob if domainInfo.BadBinaries != nil { badBinaries = persistence.NewDataBlob(domainInfo.BadBinaries, constants.EncodingType(domainInfo.GetBadBinariesEncoding())) } var isolationGroups *persistence.DataBlob if domainInfo.IsolationGroups != nil { isolationGroups = persistence.NewDataBlob(domainInfo.IsolationGroups, constants.EncodingType(domainInfo.IsolationGroupsEncoding)) } var asyncWorkflowsCfg *persistence.DataBlob if domainInfo.AsyncWorkflowConfig != nil { asyncWorkflowsCfg = persistence.NewDataBlob(domainInfo.AsyncWorkflowConfig, constants.EncodingType(domainInfo.AsyncWorkflowConfigEncoding)) } activeClusterName := cluster.GetOrUseDefaultActiveCluster(m.activeClusterName, domainInfo.GetActiveClusterName()) var activeClustersConfig *persistence.DataBlob if domainInfo.ActiveClustersConfig != nil { activeClustersConfig = persistence.NewDataBlob(domainInfo.ActiveClustersConfig, constants.EncodingType(domainInfo.ActiveClustersConfigEncoding)) // for active-active domains, do not populate active cluster name with current cluster name if it is not set activeClusterName = domainInfo.GetActiveClusterName() } return &persistence.InternalGetDomainResponse{ Info: &persistence.DomainInfo{ ID: row.ID.String(), Name: row.Name, Status: int(domainInfo.GetStatus()), Description: domainInfo.GetDescription(), OwnerEmail: domainInfo.GetOwner(), Data: domainInfo.GetData(), }, Config: &persistence.InternalDomainConfig{ Retention: domainInfo.GetRetention(), EmitMetric: domainInfo.GetEmitMetric(), ArchivalBucket: domainInfo.GetArchivalBucket(), ArchivalStatus: types.ArchivalStatus(domainInfo.GetArchivalStatus()), HistoryArchivalStatus: types.ArchivalStatus(domainInfo.GetHistoryArchivalStatus()), HistoryArchivalURI: domainInfo.GetHistoryArchivalURI(), VisibilityArchivalStatus: types.ArchivalStatus(domainInfo.GetVisibilityArchivalStatus()), VisibilityArchivalURI: domainInfo.GetVisibilityArchivalURI(), BadBinaries: badBinaries, IsolationGroups: isolationGroups, AsyncWorkflowsConfig: asyncWorkflowsCfg, }, ReplicationConfig: &persistence.InternalDomainReplicationConfig{ ActiveClusterName: activeClusterName, Clusters: cluster.GetOrUseDefaultClusters(m.activeClusterName, clusters), ActiveClustersConfig: activeClustersConfig, }, IsGlobalDomain: row.IsGlobal, FailoverVersion: domainInfo.GetFailoverVersion(), ConfigVersion: domainInfo.GetConfigVersion(), NotificationVersion: domainInfo.GetNotificationVersion(), FailoverNotificationVersion: domainInfo.GetFailoverNotificationVersion(), PreviousFailoverVersion: domainInfo.GetPreviousFailoverVersion(), FailoverEndTime: domainInfo.FailoverEndTimestamp, LastUpdatedTime: domainInfo.GetLastUpdatedTimestamp(), }, nil } func (m *sqlDomainStore) UpdateDomain( ctx context.Context, request *persistence.InternalUpdateDomainRequest, ) error { clusters := make([]string, len(request.ReplicationConfig.Clusters)) for i := range clusters { clusters[i] = request.ReplicationConfig.Clusters[i].ClusterName } var badBinaries []byte badBinariesEncoding := string(constants.EncodingTypeEmpty) if request.Config.BadBinaries != nil { badBinaries = request.Config.BadBinaries.Data badBinariesEncoding = string(request.Config.BadBinaries.GetEncoding()) } var isolationGroups []byte isolationGroupsEncoding := string(constants.EncodingTypeEmpty) if request.Config.IsolationGroups != nil { isolationGroups = request.Config.IsolationGroups.Data isolationGroupsEncoding = request.Config.IsolationGroups.GetEncodingString() } var asyncWorkflowsCfg []byte asyncWorkflowsEncoding := string(constants.EncodingTypeEmpty) if request.Config.AsyncWorkflowsConfig != nil { asyncWorkflowsCfg = request.Config.AsyncWorkflowsConfig.Data asyncWorkflowsEncoding = request.Config.AsyncWorkflowsConfig.GetEncodingString() } var activeClustersConfig []byte var activeClustersConfigEncoding string if request.ReplicationConfig.ActiveClustersConfig != nil { activeClustersConfig = request.ReplicationConfig.ActiveClustersConfig.Data activeClustersConfigEncoding = request.ReplicationConfig.ActiveClustersConfig.GetEncodingString() } domainInfo := &serialization.DomainInfo{ Status: int32(request.Info.Status), Description: request.Info.Description, Owner: request.Info.OwnerEmail, Data: request.Info.Data, Retention: request.Config.Retention, EmitMetric: request.Config.EmitMetric, ArchivalBucket: request.Config.ArchivalBucket, ArchivalStatus: int16(request.Config.ArchivalStatus), HistoryArchivalStatus: int16(request.Config.HistoryArchivalStatus), HistoryArchivalURI: request.Config.HistoryArchivalURI, VisibilityArchivalStatus: int16(request.Config.VisibilityArchivalStatus), VisibilityArchivalURI: request.Config.VisibilityArchivalURI, ActiveClusterName: request.ReplicationConfig.ActiveClusterName, ActiveClustersConfig: activeClustersConfig, ActiveClustersConfigEncoding: activeClustersConfigEncoding, Clusters: clusters, ConfigVersion: request.ConfigVersion, FailoverVersion: request.FailoverVersion, NotificationVersion: request.NotificationVersion, FailoverNotificationVersion: request.FailoverNotificationVersion, PreviousFailoverVersion: request.PreviousFailoverVersion, FailoverEndTimestamp: request.FailoverEndTime, LastUpdatedTimestamp: request.LastUpdatedTime, BadBinaries: badBinaries, BadBinariesEncoding: badBinariesEncoding, IsolationGroups: isolationGroups, IsolationGroupsEncoding: isolationGroupsEncoding, AsyncWorkflowConfig: asyncWorkflowsCfg, AsyncWorkflowConfigEncoding: asyncWorkflowsEncoding, } blob, err := m.parser.DomainInfoToBlob(domainInfo) if err != nil { return err } return m.txExecute(ctx, sqlplugin.DbDefaultShard, "UpdateDomain", func(tx sqlplugin.Tx) error { result, err := tx.UpdateDomain(ctx, &sqlplugin.DomainRow{ Name: request.Info.Name, ID: serialization.MustParseUUID(request.Info.ID), Data: blob.Data, DataEncoding: string(blob.Encoding), }) if err != nil { return err } noRowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("rowsAffected error: %v", err) } if noRowsAffected != 1 { return fmt.Errorf("%v rows updated instead of one", noRowsAffected) } if err := lockMetadata(ctx, tx); err != nil { return err } return updateMetadata(ctx, tx, request.NotificationVersion) }) } func (m *sqlDomainStore) DeleteDomain( ctx context.Context, request *persistence.DeleteDomainRequest, ) error { return m.txExecute(ctx, sqlplugin.DbDefaultShard, "DeleteDomain", func(tx sqlplugin.Tx) error { _, err := tx.DeleteFromDomain(ctx, &sqlplugin.DomainFilter{ID: serialization.UUIDPtr(serialization.MustParseUUID(request.ID))}) return err }) } func (m *sqlDomainStore) DeleteDomainByName( ctx context.Context, request *persistence.DeleteDomainByNameRequest, ) error { return m.txExecute(ctx, sqlplugin.DbDefaultShard, "DeleteDomainByName", func(tx sqlplugin.Tx) error { _, err := tx.DeleteFromDomain(ctx, &sqlplugin.DomainFilter{Name: &request.Name}) return err }) } func (m *sqlDomainStore) GetMetadata( ctx context.Context, ) (*persistence.GetMetadataResponse, error) { row, err := m.db.SelectFromDomainMetadata(ctx) if err != nil { return nil, convertCommonErrors(m.db, "GetMetadata", "", err) } return &persistence.GetMetadataResponse{NotificationVersion: row.NotificationVersion}, nil } func (m *sqlDomainStore) ListDomains( ctx context.Context, request *persistence.ListDomainsRequest, ) (*persistence.InternalListDomainsResponse, error) { var pageToken *serialization.UUID if request.NextPageToken != nil { token := serialization.UUID(request.NextPageToken) pageToken = &token } rows, err := m.db.SelectFromDomain(ctx, &sqlplugin.DomainFilter{ GreaterThanID: pageToken, PageSize: &request.PageSize, }) if err != nil { if err == sql.ErrNoRows { return &persistence.InternalListDomainsResponse{}, nil } return nil, convertCommonErrors(m.db, "ListDomains", "Failed to get domain rows.", err) } var domains []*persistence.InternalGetDomainResponse for _, row := range rows { resp, err := m.domainRowToGetDomainResponse(&row) if err != nil { return nil, err } domains = append(domains, resp) } resp := &persistence.InternalListDomainsResponse{Domains: domains} if len(rows) >= request.PageSize { resp.NextPageToken = rows[len(rows)-1].ID } return resp, nil } ================================================ FILE: common/persistence/sql/sql_domain_store_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) type sqlResult struct { lastInsertID int64 rowsAffected int64 err error } func (r *sqlResult) LastInsertId() (int64, error) { if r.err != nil { return 0, r.err } return r.lastInsertID, nil } func (r *sqlResult) RowsAffected() (int64, error) { if r.err != nil { return 0, r.err } return r.rowsAffected, nil } func TestGetMetadata(t *testing.T) { testCases := []struct { name string mockSetup func(*sqlplugin.MockDB) want *persistence.GetMetadataResponse wantErr bool }{ { name: "Success case", mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().SelectFromDomainMetadata(gomock.Any()).Return(&sqlplugin.DomainMetadataRow{NotificationVersion: 2}, nil) }, want: &persistence.GetMetadataResponse{NotificationVersion: 2}, wantErr: false, }, { name: "Error case", mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().SelectFromDomainMetadata(gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store := &sqlDomainStore{ sqlStore: sqlStore{db: mockDB}, } tc.mockSetup(mockDB) got, err := store.GetMetadata(context.Background()) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestListDomains(t *testing.T) { testCases := []struct { name string activeClusterName string req *persistence.ListDomainsRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) want *persistence.InternalListDomainsResponse wantErr bool }{ { name: "Success case", activeClusterName: "active", req: &persistence.ListDomainsRequest{ NextPageToken: []byte(`7a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c`), PageSize: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { uuid := serialization.UUID(`7a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c`) mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ GreaterThanID: &uuid, PageSize: common.IntPtr(1), }).Return([]sqlplugin.DomainRow{ { ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Name: "test", Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }, }, nil) mockParser.EXPECT().DomainInfoFromBlob([]byte(`aaaa`), string(constants.EncodingTypeThriftRW)). Return(&serialization.DomainInfo{}, nil) }, want: &persistence.InternalListDomainsResponse{ Domains: []*persistence.InternalGetDomainResponse{ { Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "active"}, }, }, }, }, NextPageToken: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), }, wantErr: false, }, { name: "Success case active-active domain", activeClusterName: "active", req: &persistence.ListDomainsRequest{ NextPageToken: []byte(`7a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c`), PageSize: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { uuid := serialization.UUID(`7a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c`) mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ GreaterThanID: &uuid, PageSize: common.IntPtr(1), }).Return([]sqlplugin.DomainRow{ { ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Name: "test", Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }, }, nil) mockParser.EXPECT().DomainInfoFromBlob([]byte(`aaaa`), string(constants.EncodingTypeThriftRW)). Return(&serialization.DomainInfo{ ActiveClustersConfig: []byte(`active-clusters-config`), ActiveClustersConfigEncoding: string(constants.EncodingTypeThriftRW), }, nil) }, want: &persistence.InternalListDomainsResponse{ Domains: []*persistence.InternalGetDomainResponse{ { Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "active"}, }, ActiveClustersConfig: &persistence.DataBlob{ Data: []byte(`active-clusters-config`), Encoding: constants.EncodingTypeThriftRW, }, }, }, }, NextPageToken: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), }, wantErr: false, }, { name: "No Record case", activeClusterName: "active", req: &persistence.ListDomainsRequest{ NextPageToken: []byte(`7a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c`), PageSize: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { uuid := serialization.UUID(`7a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c`) mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ GreaterThanID: &uuid, PageSize: common.IntPtr(1), }).Return(nil, sql.ErrNoRows) }, want: &persistence.InternalListDomainsResponse{}, wantErr: false, }, { name: "Error from database case", activeClusterName: "active", req: &persistence.ListDomainsRequest{ NextPageToken: []byte(`7a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c`), PageSize: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { uuid := serialization.UUID(`7a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c`) err := errors.New("some error") mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ GreaterThanID: &uuid, PageSize: common.IntPtr(1), }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error from corrupted data case", activeClusterName: "active", req: &persistence.ListDomainsRequest{ NextPageToken: []byte(`7a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c`), PageSize: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { uuid := serialization.UUID(`7a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c`) mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ GreaterThanID: &uuid, PageSize: common.IntPtr(1), }).Return([]sqlplugin.DomainRow{ { ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Name: "test", Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }, }, nil) mockParser.EXPECT().DomainInfoFromBlob([]byte(`aaaa`), string(constants.EncodingTypeThriftRW)).Return(nil, errors.New("corrupted blob")) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockParser(ctrl) store := &sqlDomainStore{ sqlStore: sqlStore{db: mockDB, parser: mockParser}, activeClusterName: tc.activeClusterName, } tc.mockSetup(mockDB, mockParser) got, err := store.ListDomains(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestGetDomain(t *testing.T) { testCases := []struct { name string activeClusterName string req *persistence.GetDomainRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) want *persistence.InternalGetDomainResponse wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case - get domain by name", activeClusterName: "active", req: &persistence.GetDomainRequest{ Name: "test", }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ Name: common.StringPtr("test"), }).Return([]sqlplugin.DomainRow{ { ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Name: "test", Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }, }, nil) mockParser.EXPECT().DomainInfoFromBlob([]byte(`aaaa`), string(constants.EncodingTypeThriftRW)). Return(&serialization.DomainInfo{}, nil) }, want: &persistence.InternalGetDomainResponse{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: "active", }, }, }, }, wantErr: false, }, { name: "Success case - active-active domain - get domain by name", activeClusterName: "active", req: &persistence.GetDomainRequest{ Name: "test", }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ Name: common.StringPtr("test"), }).Return([]sqlplugin.DomainRow{ { ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Name: "test", Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }, }, nil) mockParser.EXPECT().DomainInfoFromBlob([]byte(`aaaa`), string(constants.EncodingTypeThriftRW)). Return(&serialization.DomainInfo{ ActiveClustersConfig: []byte(`active-clusters-config`), ActiveClustersConfigEncoding: string(constants.EncodingTypeThriftRW), }, nil) }, want: &persistence.InternalGetDomainResponse{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "", Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: "active", }, }, ActiveClustersConfig: &persistence.DataBlob{ Data: []byte(`active-clusters-config`), Encoding: constants.EncodingTypeThriftRW, }, }, }, wantErr: false, }, { name: "Success case - get domain by ID", activeClusterName: "active", req: &persistence.GetDomainRequest{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { uuid := serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c") mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ ID: &uuid, }).Return([]sqlplugin.DomainRow{ { ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Name: "test", Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }, }, nil) mockParser.EXPECT().DomainInfoFromBlob([]byte(`aaaa`), string(constants.EncodingTypeThriftRW)).Return(&serialization.DomainInfo{}, nil) }, want: &persistence.InternalGetDomainResponse{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: "active", }, }, }, }, wantErr: false, }, { name: "Error case - both domain name and ID are set in request", activeClusterName: "active", req: &persistence.GetDomainRequest{ Name: "test", ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) {}, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *types.BadRequestError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be BadRequestError") assert.Contains(t, err.Error(), "GetDomain operation failed. Both ID and Name specified in request.") }, }, { name: "Error case - both domain name and ID are NOT set in request", activeClusterName: "active", req: &persistence.GetDomainRequest{}, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) {}, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *types.BadRequestError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be BadRequestError") assert.Contains(t, err.Error(), "GetDomain operation failed. Both ID and Name are empty.") }, }, { name: "No Record case", activeClusterName: "active", req: &persistence.GetDomainRequest{ Name: "test", }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ Name: common.StringPtr("test"), }).Return(nil, sql.ErrNoRows) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *types.EntityNotExistsError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be EntityNotExistsError") }, }, { name: "Error from database case", activeClusterName: "active", req: &persistence.GetDomainRequest{ Name: "test", }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { err := errors.New("some error") mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ Name: common.StringPtr("test"), }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error from corrupted data case", activeClusterName: "active", req: &persistence.GetDomainRequest{ Name: "test", }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromDomain(gomock.Any(), &sqlplugin.DomainFilter{ Name: common.StringPtr("test"), }).Return([]sqlplugin.DomainRow{ { ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Name: "test", Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }, }, nil) mockParser.EXPECT().DomainInfoFromBlob([]byte(`aaaa`), string(constants.EncodingTypeThriftRW)).Return(nil, errors.New("corrupted blob")) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockParser(ctrl) store := &sqlDomainStore{ sqlStore: sqlStore{db: mockDB, parser: mockParser}, activeClusterName: tc.activeClusterName, } tc.mockSetup(mockDB, mockParser) got, err := store.GetDomain(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestCreateDomain(t *testing.T) { testCases := []struct { name string activeClusterName string req *persistence.InternalCreateDomainRequest mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx, *serialization.MockParser) want *persistence.CreateDomainResponse wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", activeClusterName: "active", req: &persistence.InternalCreateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", Status: 1, Description: "n/a", OwnerEmail: "abc@xyz.com", Data: map[string]string{"k": "v"}, }, Config: &persistence.InternalDomainConfig{ Retention: time.Hour * 24, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "http://a.b", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "http://x.y", BadBinaries: nil, IsolationGroups: nil, }, ReplicationConfig: &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: "active", }, }, ActiveClustersConfig: &persistence.DataBlob{ Data: []byte(`active-clusters-config`), Encoding: constants.EncodingTypeThriftRW, }, }, ConfigVersion: 1, FailoverVersion: 3, IsGlobalDomain: true, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromDomainMetadata(gomock.Any()).Return(&sqlplugin.DomainMetadataRow{NotificationVersion: 2}, nil) mockParser.EXPECT().DomainInfoToBlob(&serialization.DomainInfo{ Name: "test", Status: 1, Description: "n/a", Owner: "abc@xyz.com", Data: map[string]string{"k": "v"}, Retention: time.Hour * 24, EmitMetric: true, HistoryArchivalStatus: int16(types.ArchivalStatusEnabled), HistoryArchivalURI: "http://a.b", VisibilityArchivalStatus: int16(types.ArchivalStatusEnabled), VisibilityArchivalURI: "http://x.y", ActiveClusterName: "active", Clusters: []string{"active"}, ConfigVersion: 1, FailoverVersion: 3, NotificationVersion: 2, FailoverNotificationVersion: persistence.InitialFailoverNotificationVersion, PreviousFailoverVersion: constants.InitialPreviousFailoverVersion, ActiveClustersConfig: []byte(`active-clusters-config`), ActiveClustersConfigEncoding: string(constants.EncodingTypeThriftRW), }).Return(persistence.DataBlob{Data: []byte(`aaaa`), Encoding: constants.EncodingTypeThriftRW}, nil) mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().InsertIntoDomain(gomock.Any(), &sqlplugin.DomainRow{ Name: "test", ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), IsGlobal: true, }).Return(nil, nil) mockTx.EXPECT().LockDomainMetadata(gomock.Any()).Return(nil) mockTx.EXPECT().UpdateDomainMetadata(gomock.Any(), &sqlplugin.DomainMetadataRow{NotificationVersion: 2}).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) }, want: &persistence.CreateDomainResponse{ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"}, wantErr: false, }, { name: "Error case - unable to get metadata", activeClusterName: "active", req: &persistence.InternalCreateDomainRequest{}, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { err := errors.New("some error") mockDB.EXPECT().SelectFromDomainMetadata(gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - unable to encode data", activeClusterName: "active", req: &persistence.InternalCreateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromDomainMetadata(gomock.Any()).Return(&sqlplugin.DomainMetadataRow{NotificationVersion: 2}, nil) mockParser.EXPECT().DomainInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, errors.New("some error")) }, wantErr: true, }, { name: "Error case - unable to insert row", activeClusterName: "active", req: &persistence.InternalCreateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromDomainMetadata(gomock.Any()).Return(&sqlplugin.DomainMetadataRow{NotificationVersion: 2}, nil) mockParser.EXPECT().DomainInfoToBlob(gomock.Any()).Return(persistence.DataBlob{Data: []byte(`aaaa`), Encoding: constants.EncodingTypeThriftRW}, nil) mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().InsertIntoDomain(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsDupEntryError(err).Return(false) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - domain already exists", activeClusterName: "active", req: &persistence.InternalCreateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, ConfigVersion: 1, FailoverVersion: 3, IsGlobalDomain: true, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromDomainMetadata(gomock.Any()).Return(&sqlplugin.DomainMetadataRow{NotificationVersion: 2}, nil) mockParser.EXPECT().DomainInfoToBlob(gomock.Any()).Return(persistence.DataBlob{Data: []byte(`aaaa`), Encoding: constants.EncodingTypeThriftRW}, nil) mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().InsertIntoDomain(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsDupEntryError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *types.DomainAlreadyExistsError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be DomainAlreadyExistsError") }, }, { name: "Error case - unable to lock metadata", activeClusterName: "active", req: &persistence.InternalCreateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromDomainMetadata(gomock.Any()).Return(&sqlplugin.DomainMetadataRow{NotificationVersion: 2}, nil) mockParser.EXPECT().DomainInfoToBlob(gomock.Any()).Return(persistence.DataBlob{Data: []byte(`aaaa`), Encoding: constants.EncodingTypeThriftRW}, nil) mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().InsertIntoDomain(gomock.Any(), gomock.Any()).Return(nil, nil) err := errors.New("some error") mockTx.EXPECT().LockDomainMetadata(gomock.Any()).Return(err) mockTx.EXPECT().IsNotFoundError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, }, { name: "Error case - unable to update metadata", activeClusterName: "active", req: &persistence.InternalCreateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromDomainMetadata(gomock.Any()).Return(&sqlplugin.DomainMetadataRow{NotificationVersion: 2}, nil) mockParser.EXPECT().DomainInfoToBlob(gomock.Any()).Return(persistence.DataBlob{Data: []byte(`aaaa`), Encoding: constants.EncodingTypeThriftRW}, nil) mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().InsertIntoDomain(gomock.Any(), gomock.Any()).Return(nil, nil) mockTx.EXPECT().LockDomainMetadata(gomock.Any()).Return(nil) err := errors.New("some error") mockTx.EXPECT().UpdateDomainMetadata(gomock.Any(), &sqlplugin.DomainMetadataRow{NotificationVersion: 2}).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) store := &sqlDomainStore{ sqlStore: sqlStore{db: mockDB, parser: mockParser}, activeClusterName: tc.activeClusterName, } tc.mockSetup(mockDB, mockTx, mockParser) got, err := store.CreateDomain(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestUpdateDomain(t *testing.T) { testCases := []struct { name string activeClusterName string req *persistence.InternalUpdateDomainRequest mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx, *serialization.MockParser) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", activeClusterName: "active", req: &persistence.InternalUpdateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", Status: 1, Description: "n/a", OwnerEmail: "abc@xyz.com", Data: map[string]string{"k": "v"}, }, Config: &persistence.InternalDomainConfig{ Retention: time.Hour * 24, EmitMetric: true, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "http://a.b", VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "http://x.y", BadBinaries: nil, IsolationGroups: nil, }, ReplicationConfig: &persistence.InternalDomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: "active", }, }, ActiveClustersConfig: &persistence.DataBlob{ Data: []byte(`active-clusters-config`), Encoding: constants.EncodingTypeThriftRW, }, }, ConfigVersion: 1, FailoverVersion: 3, FailoverNotificationVersion: 4, PreviousFailoverVersion: 5, NotificationVersion: 2, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().DomainInfoToBlob(&serialization.DomainInfo{ Status: 1, Description: "n/a", Owner: "abc@xyz.com", Data: map[string]string{"k": "v"}, Retention: time.Hour * 24, EmitMetric: true, HistoryArchivalStatus: int16(types.ArchivalStatusEnabled), HistoryArchivalURI: "http://a.b", VisibilityArchivalStatus: int16(types.ArchivalStatusEnabled), VisibilityArchivalURI: "http://x.y", ActiveClusterName: "active", Clusters: []string{"active"}, ConfigVersion: 1, FailoverVersion: 3, NotificationVersion: 2, FailoverNotificationVersion: 4, PreviousFailoverVersion: 5, ActiveClustersConfig: []byte(`active-clusters-config`), ActiveClustersConfigEncoding: string(constants.EncodingTypeThriftRW), }).Return(persistence.DataBlob{Data: []byte(`aaaa`), Encoding: constants.EncodingTypeThriftRW}, nil) mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().UpdateDomain(gomock.Any(), &sqlplugin.DomainRow{ Name: "test", ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().LockDomainMetadata(gomock.Any()).Return(nil) mockTx.EXPECT().UpdateDomainMetadata(gomock.Any(), &sqlplugin.DomainMetadataRow{NotificationVersion: 2}).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Error case - unable to encode data", activeClusterName: "active", req: &persistence.InternalUpdateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().DomainInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, errors.New("some error")) }, wantErr: true, }, { name: "Error case - unable to update row", activeClusterName: "active", req: &persistence.InternalUpdateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().DomainInfoToBlob(gomock.Any()).Return(persistence.DataBlob{Data: []byte(`aaaa`), Encoding: constants.EncodingTypeThriftRW}, nil) mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().UpdateDomain(gomock.Any(), &sqlplugin.DomainRow{ Name: "test", ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - unable to lock metadata", activeClusterName: "active", req: &persistence.InternalUpdateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().DomainInfoToBlob(gomock.Any()).Return(persistence.DataBlob{Data: []byte(`aaaa`), Encoding: constants.EncodingTypeThriftRW}, nil) mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().UpdateDomain(gomock.Any(), &sqlplugin.DomainRow{ Name: "test", ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }).Return(&sqlResult{rowsAffected: 1}, nil) err := errors.New("some error") mockTx.EXPECT().LockDomainMetadata(gomock.Any()).Return(err) mockTx.EXPECT().IsNotFoundError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, }, { name: "Error case - unable to update metadata", activeClusterName: "active", req: &persistence.InternalUpdateDomainRequest{ Info: &persistence.DomainInfo{ ID: "9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c", Name: "test", }, Config: &persistence.InternalDomainConfig{}, ReplicationConfig: &persistence.InternalDomainReplicationConfig{}, NotificationVersion: 2, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().DomainInfoToBlob(gomock.Any()).Return(persistence.DataBlob{Data: []byte(`aaaa`), Encoding: constants.EncodingTypeThriftRW}, nil) mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().UpdateDomain(gomock.Any(), &sqlplugin.DomainRow{ Name: "test", ID: serialization.MustParseUUID("9a3dc7e2-1e67-41aa-8eaf-6d6e27f7e47c"), Data: []byte(`aaaa`), DataEncoding: string(constants.EncodingTypeThriftRW), }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().LockDomainMetadata(gomock.Any()).Return(nil) err := errors.New("some error") mockTx.EXPECT().UpdateDomainMetadata(gomock.Any(), &sqlplugin.DomainMetadataRow{NotificationVersion: 2}).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) store := &sqlDomainStore{ sqlStore: sqlStore{db: mockDB, parser: mockParser}, activeClusterName: tc.activeClusterName, } tc.mockSetup(mockDB, mockTx, mockParser) err := store.UpdateDomain(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } ================================================ FILE: common/persistence/sql/sql_execution_store.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "bytes" "context" "database/sql" "encoding/json" "fmt" "math" "runtime/debug" "time" "golang.org/x/sync/errgroup" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) const ( emptyWorkflowID string = "" emptyReplicationRunID string = "30000000-5000-f000-f000-000000000000" ) type sqlExecutionStore struct { sqlStore shardID int taskSerializer serialization.TaskSerializer txExecuteShardLockedFn func(context.Context, int, string, int64, func(sqlplugin.Tx) error) error lockCurrentExecutionIfExistsFn func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) createOrUpdateCurrentExecutionFn func(context.Context, sqlplugin.Tx, p.CreateWorkflowMode, int, serialization.UUID, string, serialization.UUID, int, int, string, int64, int64) error assertNotCurrentExecutionFn func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID) error assertRunIDAndUpdateCurrentExecutionFn func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error applyWorkflowSnapshotTxAsNewFn func(context.Context, sqlplugin.Tx, int, *p.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error applyWorkflowMutationTxFn func(context.Context, sqlplugin.Tx, int, *p.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error applyWorkflowSnapshotTxAsResetFn func(context.Context, sqlplugin.Tx, int, *p.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error } var _ p.ExecutionStore = (*sqlExecutionStore)(nil) // NewSQLExecutionStore creates an instance of ExecutionStore func NewSQLExecutionStore( db sqlplugin.DB, logger log.Logger, shardID int, parser serialization.Parser, taskSerializer serialization.TaskSerializer, dc *p.DynamicConfiguration, ) (p.ExecutionStore, error) { store := &sqlExecutionStore{ shardID: shardID, lockCurrentExecutionIfExistsFn: lockCurrentExecutionIfExists, createOrUpdateCurrentExecutionFn: createOrUpdateCurrentExecution, assertNotCurrentExecutionFn: assertNotCurrentExecution, assertRunIDAndUpdateCurrentExecutionFn: assertRunIDAndUpdateCurrentExecution, applyWorkflowSnapshotTxAsNewFn: applyWorkflowSnapshotTxAsNew, applyWorkflowMutationTxFn: applyWorkflowMutationTx, applyWorkflowSnapshotTxAsResetFn: applyWorkflowSnapshotTxAsReset, sqlStore: sqlStore{ db: db, logger: logger, parser: parser, dc: dc, }, taskSerializer: taskSerializer, } store.txExecuteShardLockedFn = store.txExecuteShardLocked return store, nil } // txExecuteShardLocked executes f under transaction and with read lock on shard row func (m *sqlExecutionStore) txExecuteShardLocked( ctx context.Context, dbShardID int, operation string, rangeID int64, fn func(tx sqlplugin.Tx) error, ) error { return m.txExecute(ctx, dbShardID, operation, func(tx sqlplugin.Tx) error { if err := readLockShard(ctx, tx, m.shardID, rangeID); err != nil { return err } err := fn(tx) if err != nil { return err } return nil }) } func (m *sqlExecutionStore) GetShardID() int { return m.shardID } func (m *sqlExecutionStore) CreateWorkflowExecution( ctx context.Context, request *p.InternalCreateWorkflowExecutionRequest, ) (response *p.CreateWorkflowExecutionResponse, err error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(m.shardID, m.db.GetTotalNumDBShards()) err = m.txExecuteShardLockedFn(ctx, dbShardID, "CreateWorkflowExecution", request.RangeID, func(tx sqlplugin.Tx) error { response, err = m.createWorkflowExecutionTx(ctx, tx, request) return err }) return } func (m *sqlExecutionStore) createWorkflowExecutionTx( ctx context.Context, tx sqlplugin.Tx, request *p.InternalCreateWorkflowExecutionRequest, ) (*p.CreateWorkflowExecutionResponse, error) { newWorkflow := request.NewWorkflowSnapshot executionInfo := newWorkflow.ExecutionInfo startVersion := newWorkflow.StartVersion lastWriteVersion := newWorkflow.LastWriteVersion shardID := m.shardID domainID := serialization.MustParseUUID(executionInfo.DomainID) workflowID := executionInfo.WorkflowID runID := serialization.MustParseUUID(executionInfo.RunID) if err := p.ValidateCreateWorkflowModeState( request.Mode, newWorkflow, ); err != nil { return nil, err } var err error var row *sqlplugin.CurrentExecutionsRow if row, err = m.lockCurrentExecutionIfExistsFn(ctx, tx, m.shardID, domainID, workflowID); err != nil { return nil, err } // current workflow record check if row != nil { // current run ID, last write version, current workflow state check switch request.Mode { case p.CreateWorkflowModeBrandNew: return nil, &p.WorkflowExecutionAlreadyStartedError{ Msg: fmt.Sprintf("Workflow execution already running. WorkflowId: %v", row.WorkflowID), StartRequestID: row.CreateRequestID, RunID: row.RunID.String(), State: int(row.State), CloseStatus: int(row.CloseStatus), LastWriteVersion: row.LastWriteVersion, } case p.CreateWorkflowModeWorkflowIDReuse: if request.PreviousLastWriteVersion != row.LastWriteVersion { return nil, &p.CurrentWorkflowConditionFailedError{ Msg: fmt.Sprintf("Workflow execution creation condition failed. WorkflowId: %v, "+ "LastWriteVersion: %v, PreviousLastWriteVersion: %v", workflowID, row.LastWriteVersion, request.PreviousLastWriteVersion), } } if row.State != p.WorkflowStateCompleted { return nil, &p.CurrentWorkflowConditionFailedError{ Msg: fmt.Sprintf("Workflow execution creation condition failed. WorkflowId: %v, "+ "State: %v, Expected: %v", workflowID, row.State, p.WorkflowStateCompleted), } } runIDStr := row.RunID.String() if runIDStr != request.PreviousRunID { return nil, &p.CurrentWorkflowConditionFailedError{ Msg: fmt.Sprintf("Workflow execution creation condition failed. WorkflowId: %v, "+ "RunID: %v, PreviousRunID: %v", workflowID, runIDStr, request.PreviousRunID), } } case p.CreateWorkflowModeZombie: // zombie workflow creation with existence of current record, this is a noop if err := assertRunIDMismatch(serialization.MustParseUUID(executionInfo.RunID), row.RunID); err != nil { return nil, err } case p.CreateWorkflowModeContinueAsNew: runIDStr := row.RunID.String() if runIDStr != request.PreviousRunID { return nil, &p.CurrentWorkflowConditionFailedError{ Msg: fmt.Sprintf("Workflow execution creation condition failed. WorkflowId: %v, "+ "RunID: %v, PreviousRunID: %v", workflowID, runIDStr, request.PreviousRunID), } } default: return nil, &types.InternalServiceError{ Message: fmt.Sprintf( "CreteWorkflowExecution: unknown mode: %v", request.Mode, ), } } } if err := m.createOrUpdateCurrentExecutionFn( ctx, tx, request.Mode, m.shardID, domainID, workflowID, runID, executionInfo.State, executionInfo.CloseStatus, executionInfo.CreateRequestID, startVersion, lastWriteVersion); err != nil { return nil, err } if err := m.applyWorkflowSnapshotTxAsNewFn(ctx, tx, shardID, &request.NewWorkflowSnapshot, m.parser, m.taskSerializer); err != nil { return nil, err } return &p.CreateWorkflowExecutionResponse{}, nil } func (m *sqlExecutionStore) getExecutions( ctx context.Context, request *p.InternalGetWorkflowExecutionRequest, domainID serialization.UUID, wfID string, runID serialization.UUID, ) ([]sqlplugin.ExecutionsRow, error) { executions, err := m.db.SelectFromExecutions(ctx, &sqlplugin.ExecutionsFilter{ ShardID: m.shardID, DomainID: domainID, WorkflowID: wfID, RunID: runID}) if err != nil { if err == sql.ErrNoRows { return nil, &types.EntityNotExistsError{ Message: fmt.Sprintf( "Workflow execution not found. WorkflowId: %v, RunId: %v", request.Execution.GetWorkflowID(), request.Execution.GetRunID(), ), } } return nil, convertCommonErrors(m.db, "GetWorkflowExecution", "", err) } if len(executions) == 0 { return nil, &types.EntityNotExistsError{ Message: fmt.Sprintf( "Workflow execution not found. WorkflowId: %v, RunId: %v", request.Execution.GetWorkflowID(), request.Execution.GetRunID(), ), } } if len(executions) != 1 { return nil, &types.InternalServiceError{ Message: "GetWorkflowExecution return more than one results.", } } return executions, nil } func (m *sqlExecutionStore) GetWorkflowExecution( ctx context.Context, request *p.InternalGetWorkflowExecutionRequest, ) (resp *p.InternalGetWorkflowExecutionResponse, e error) { recoverPanic := func(recovered interface{}, err *error) { if recovered != nil { *err = fmt.Errorf("DB operation panicked: %v %s", recovered, debug.Stack()) } } domainID := serialization.MustParseUUID(request.DomainID) runID := serialization.MustParseUUID(request.Execution.RunID) wfID := request.Execution.WorkflowID var executions []sqlplugin.ExecutionsRow var activityInfos map[int64]*p.InternalActivityInfo var timerInfos map[string]*p.TimerInfo var childExecutionInfos map[int64]*p.InternalChildExecutionInfo var requestCancelInfos map[int64]*p.RequestCancelInfo var signalInfos map[int64]*p.SignalInfo var bufferedEvents []*p.DataBlob var signalsRequested map[string]struct{} g, childCtx := errgroup.WithContext(ctx) g.Go(func() (e error) { defer func() { recoverPanic(recover(), &e) }() activityInfos, e = getActivityInfoMap( childCtx, m.db, m.shardID, domainID, wfID, runID, m.parser) return e }) g.Go(func() (e error) { defer func() { recoverPanic(recover(), &e) }() timerInfos, e = getTimerInfoMap( childCtx, m.db, m.shardID, domainID, wfID, runID, m.parser) return e }) g.Go(func() (e error) { defer func() { recoverPanic(recover(), &e) }() childExecutionInfos, e = getChildExecutionInfoMap( childCtx, m.db, m.shardID, domainID, wfID, runID, m.parser) return e }) g.Go(func() (e error) { defer func() { recoverPanic(recover(), &e) }() requestCancelInfos, e = getRequestCancelInfoMap( childCtx, m.db, m.shardID, domainID, wfID, runID, m.parser) return e }) g.Go(func() (e error) { defer func() { recoverPanic(recover(), &e) }() signalInfos, e = getSignalInfoMap( childCtx, m.db, m.shardID, domainID, wfID, runID, m.parser) return e }) g.Go(func() (e error) { defer func() { recoverPanic(recover(), &e) }() bufferedEvents, e = getBufferedEvents( childCtx, m.db, m.shardID, domainID, wfID, runID) return e }) g.Go(func() (e error) { defer func() { recoverPanic(recover(), &e) }() signalsRequested, e = getSignalsRequested( childCtx, m.db, m.shardID, domainID, wfID, runID) return e }) err := g.Wait() if err != nil { return nil, err } // there is a race condition with delete workflow. What could happen is that // a delete workflow transaction can be committed between 2 concurrent read operations // and in that case we can get checksum error because data is partially read. // Since checksum is stored in the executions table, we make it the last step of reading, // in this case, either we read full data with checksum or we don't get checksum and return error. executions, err = m.getExecutions(ctx, request, domainID, wfID, runID) if err != nil { return nil, err } state, err := m.populateWorkflowMutableState(executions[0]) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("GetWorkflowExecution: failed. Error: %v", err), } } // if we have checksum, we need to make sure the rangeID did not change // if the rangeID changed, it means the shard ownership might have changed // and the workflow might have been updated when we read the data, so the data // we read might not be from a consistent view, the checksum validation might fail // in that case, we clear the checksum data so that we will not perform the validation if state.ChecksumData != nil { row, err := m.db.SelectFromShards(ctx, &sqlplugin.ShardsFilter{ShardID: int64(m.shardID)}) if err != nil { return nil, convertCommonErrors(m.db, "GetWorkflowExecution", "", err) } if row.RangeID != request.RangeID { // The GetWorkflowExecution operation will not be impacted by this. ChecksumData is purely for validation purposes. m.logger.Warn("GetWorkflowExecution's checksum is discarded. The shard might have changed owner.") state.ChecksumData = nil } } state.ActivityInfos = activityInfos state.TimerInfos = timerInfos state.ChildExecutionInfos = childExecutionInfos state.RequestCancelInfos = requestCancelInfos state.SignalInfos = signalInfos state.BufferedEvents = bufferedEvents state.SignalRequestedIDs = signalsRequested return &p.InternalGetWorkflowExecutionResponse{State: state}, nil } func (m *sqlExecutionStore) UpdateWorkflowExecution( ctx context.Context, request *p.InternalUpdateWorkflowExecutionRequest, ) error { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(m.shardID, m.db.GetTotalNumDBShards()) return m.txExecuteShardLockedFn(ctx, dbShardID, "UpdateWorkflowExecution", request.RangeID, func(tx sqlplugin.Tx) error { return m.updateWorkflowExecutionTx(ctx, tx, request) }) } func (m *sqlExecutionStore) updateWorkflowExecutionTx( ctx context.Context, tx sqlplugin.Tx, request *p.InternalUpdateWorkflowExecutionRequest, ) error { updateWorkflow := request.UpdateWorkflowMutation newWorkflow := request.NewWorkflowSnapshot executionInfo := updateWorkflow.ExecutionInfo domainID := serialization.MustParseUUID(executionInfo.DomainID) workflowID := executionInfo.WorkflowID runID := serialization.MustParseUUID(executionInfo.RunID) shardID := m.shardID if err := p.ValidateUpdateWorkflowModeState( request.Mode, updateWorkflow, newWorkflow, ); err != nil { return err } switch request.Mode { case p.UpdateWorkflowModeIgnoreCurrent: // no-op case p.UpdateWorkflowModeBypassCurrent: if err := m.assertNotCurrentExecutionFn( ctx, tx, shardID, domainID, workflowID, runID); err != nil { return err } case p.UpdateWorkflowModeUpdateCurrent: if newWorkflow != nil { newExecutionInfo := newWorkflow.ExecutionInfo startVersion := newWorkflow.StartVersion lastWriteVersion := newWorkflow.LastWriteVersion newDomainID := serialization.MustParseUUID(newExecutionInfo.DomainID) newRunID := serialization.MustParseUUID(newExecutionInfo.RunID) if !bytes.Equal(domainID, newDomainID) { return &types.InternalServiceError{ Message: "UpdateWorkflowExecution: cannot continue as new to another domain", } } if err := m.assertRunIDAndUpdateCurrentExecutionFn( ctx, tx, shardID, domainID, workflowID, newRunID, runID, newWorkflow.ExecutionInfo.CreateRequestID, newWorkflow.ExecutionInfo.State, newWorkflow.ExecutionInfo.CloseStatus, startVersion, lastWriteVersion); err != nil { return err } } else { startVersion := updateWorkflow.StartVersion lastWriteVersion := updateWorkflow.LastWriteVersion // this is only to update the current record if err := m.assertRunIDAndUpdateCurrentExecutionFn( ctx, tx, shardID, domainID, workflowID, runID, runID, executionInfo.CreateRequestID, executionInfo.State, executionInfo.CloseStatus, startVersion, lastWriteVersion); err != nil { return err } } default: return &types.InternalServiceError{ Message: fmt.Sprintf("UpdateWorkflowExecution: unknown mode: %v", request.Mode), } } if err := m.applyWorkflowMutationTxFn(ctx, tx, shardID, &updateWorkflow, m.parser, m.taskSerializer); err != nil { return err } if newWorkflow != nil { if err := m.applyWorkflowSnapshotTxAsNewFn(ctx, tx, shardID, newWorkflow, m.parser, m.taskSerializer); err != nil { return err } } return nil } func (m *sqlExecutionStore) ConflictResolveWorkflowExecution( ctx context.Context, request *p.InternalConflictResolveWorkflowExecutionRequest, ) error { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(m.shardID, m.db.GetTotalNumDBShards()) return m.txExecuteShardLockedFn(ctx, dbShardID, "ConflictResolveWorkflowExecution", request.RangeID, func(tx sqlplugin.Tx) error { return m.conflictResolveWorkflowExecutionTx(ctx, tx, request) }) } func (m *sqlExecutionStore) conflictResolveWorkflowExecutionTx( ctx context.Context, tx sqlplugin.Tx, request *p.InternalConflictResolveWorkflowExecutionRequest, ) error { currentWorkflow := request.CurrentWorkflowMutation resetWorkflow := request.ResetWorkflowSnapshot newWorkflow := request.NewWorkflowSnapshot shardID := m.shardID domainID := serialization.MustParseUUID(resetWorkflow.ExecutionInfo.DomainID) workflowID := resetWorkflow.ExecutionInfo.WorkflowID if err := p.ValidateConflictResolveWorkflowModeState( request.Mode, resetWorkflow, newWorkflow, currentWorkflow, ); err != nil { return err } switch request.Mode { case p.ConflictResolveWorkflowModeBypassCurrent: if err := m.assertNotCurrentExecutionFn( ctx, tx, shardID, domainID, workflowID, serialization.MustParseUUID(resetWorkflow.ExecutionInfo.RunID)); err != nil { return err } case p.ConflictResolveWorkflowModeUpdateCurrent: executionInfo := resetWorkflow.ExecutionInfo startVersion := resetWorkflow.StartVersion lastWriteVersion := resetWorkflow.LastWriteVersion if newWorkflow != nil { executionInfo = newWorkflow.ExecutionInfo startVersion = newWorkflow.StartVersion lastWriteVersion = newWorkflow.LastWriteVersion } runID := serialization.MustParseUUID(executionInfo.RunID) createRequestID := executionInfo.CreateRequestID state := executionInfo.State closeStatus := executionInfo.CloseStatus if currentWorkflow != nil { prevRunID := serialization.MustParseUUID(currentWorkflow.ExecutionInfo.RunID) if err := m.assertRunIDAndUpdateCurrentExecutionFn( ctx, tx, m.shardID, domainID, workflowID, runID, prevRunID, createRequestID, state, closeStatus, startVersion, lastWriteVersion); err != nil { return err } } else { // reset workflow is current prevRunID := serialization.MustParseUUID(resetWorkflow.ExecutionInfo.RunID) if err := m.assertRunIDAndUpdateCurrentExecutionFn( ctx, tx, m.shardID, domainID, workflowID, runID, prevRunID, createRequestID, state, closeStatus, startVersion, lastWriteVersion); err != nil { return err } } default: return &types.InternalServiceError{ Message: fmt.Sprintf("ConflictResolveWorkflowExecution: unknown mode: %v", request.Mode), } } if err := m.applyWorkflowSnapshotTxAsResetFn(ctx, tx, shardID, &resetWorkflow, m.parser, m.taskSerializer); err != nil { return err } if currentWorkflow != nil { if err := m.applyWorkflowMutationTxFn(ctx, tx, shardID, currentWorkflow, m.parser, m.taskSerializer); err != nil { return err } } if newWorkflow != nil { if err := m.applyWorkflowSnapshotTxAsNewFn(ctx, tx, shardID, newWorkflow, m.parser, m.taskSerializer); err != nil { return err } } return nil } func (m *sqlExecutionStore) DeleteWorkflowExecution( ctx context.Context, request *p.DeleteWorkflowExecutionRequest, ) error { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(m.shardID, m.db.GetTotalNumDBShards()) domainID := serialization.MustParseUUID(request.DomainID) runID := serialization.MustParseUUID(request.RunID) wfID := request.WorkflowID return m.txExecute(ctx, dbShardID, "DeleteWorkflowExecution", func(tx sqlplugin.Tx) error { if _, err := tx.DeleteFromExecutions(ctx, &sqlplugin.ExecutionsFilter{ ShardID: m.shardID, DomainID: domainID, WorkflowID: wfID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "DeleteWorkflowExecution", "", err) } if _, err := tx.DeleteFromActivityInfoMaps(ctx, &sqlplugin.ActivityInfoMapsFilter{ ShardID: int64(m.shardID), DomainID: domainID, WorkflowID: wfID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "DeleteFromActivityInfoMaps", "", err) } if _, err := tx.DeleteFromTimerInfoMaps(ctx, &sqlplugin.TimerInfoMapsFilter{ ShardID: int64(m.shardID), DomainID: domainID, WorkflowID: wfID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "DeleteFromTimerInfoMaps", "", err) } if _, err := tx.DeleteFromChildExecutionInfoMaps(ctx, &sqlplugin.ChildExecutionInfoMapsFilter{ ShardID: int64(m.shardID), DomainID: domainID, WorkflowID: wfID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "DeleteFromChildExecutionInfoMaps", "", err) } if _, err := tx.DeleteFromRequestCancelInfoMaps(ctx, &sqlplugin.RequestCancelInfoMapsFilter{ ShardID: int64(m.shardID), DomainID: domainID, WorkflowID: wfID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "DeleteFromRequestCancelInfoMaps", "", err) } if _, err := tx.DeleteFromSignalInfoMaps(ctx, &sqlplugin.SignalInfoMapsFilter{ ShardID: int64(m.shardID), DomainID: domainID, WorkflowID: wfID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "DeleteFromSignalInfoMaps", "", err) } if _, err := tx.DeleteFromBufferedEvents(ctx, &sqlplugin.BufferedEventsFilter{ ShardID: m.shardID, DomainID: domainID, WorkflowID: wfID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "DeleteFromBufferedEvents", "", err) } if _, err := tx.DeleteFromSignalsRequestedSets(ctx, &sqlplugin.SignalsRequestedSetsFilter{ ShardID: int64(m.shardID), DomainID: domainID, WorkflowID: wfID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "DeleteFromSignalsRequestedSets", "", err) } return nil }) } // its possible for a new run of the same workflow to have started after the run we are deleting // here was finished. In that case, current_executions table will have the same workflowID but different // runID. The following code will delete the row from current_executions if and only if the runID is // same as the one we are trying to delete here func (m *sqlExecutionStore) DeleteCurrentWorkflowExecution( ctx context.Context, request *p.DeleteCurrentWorkflowExecutionRequest, ) error { domainID := serialization.MustParseUUID(request.DomainID) runID := serialization.MustParseUUID(request.RunID) _, err := m.db.DeleteFromCurrentExecutions(ctx, &sqlplugin.CurrentExecutionsFilter{ ShardID: int64(m.shardID), DomainID: domainID, WorkflowID: request.WorkflowID, RunID: runID, }) if err != nil { return convertCommonErrors(m.db, "DeleteCurrentWorkflowExecution", "", err) } return nil } func (m *sqlExecutionStore) GetCurrentExecution( ctx context.Context, request *p.GetCurrentExecutionRequest, ) (*p.GetCurrentExecutionResponse, error) { row, err := m.db.SelectFromCurrentExecutions(ctx, &sqlplugin.CurrentExecutionsFilter{ ShardID: int64(m.shardID), DomainID: serialization.MustParseUUID(request.DomainID), WorkflowID: request.WorkflowID, }) if err != nil { return nil, convertCommonErrors(m.db, "GetCurrentExecution", "", err) } return &p.GetCurrentExecutionResponse{ StartRequestID: row.CreateRequestID, RunID: row.RunID.String(), State: int(row.State), CloseStatus: int(row.CloseStatus), LastWriteVersion: row.LastWriteVersion, }, nil } func (m *sqlExecutionStore) ListCurrentExecutions( _ context.Context, _ *p.ListCurrentExecutionsRequest, ) (*p.ListCurrentExecutionsResponse, error) { return nil, &types.InternalServiceError{Message: "Not yet implemented"} } func (m *sqlExecutionStore) IsWorkflowExecutionExists( _ context.Context, _ *p.IsWorkflowExecutionExistsRequest, ) (*p.IsWorkflowExecutionExistsResponse, error) { return nil, &types.InternalServiceError{Message: "Not yet implemented"} } func (m *sqlExecutionStore) ListConcreteExecutions( ctx context.Context, request *p.ListConcreteExecutionsRequest, ) (*p.InternalListConcreteExecutionsResponse, error) { filter := &sqlplugin.ExecutionsFilter{} if len(request.PageToken) > 0 { err := gobDeserialize(request.PageToken, &filter) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListConcreteExecutions failed. Error: %v", err), } } } else { filter = &sqlplugin.ExecutionsFilter{ ShardID: m.shardID, WorkflowID: "", } } filter.Size = request.PageSize executions, err := m.db.SelectFromExecutions(ctx, filter) if err != nil { if err == sql.ErrNoRows { return &p.InternalListConcreteExecutionsResponse{}, nil } return nil, convertCommonErrors(m.db, "ListConcreteExecutions", "", err) } if len(executions) == 0 { return &p.InternalListConcreteExecutionsResponse{}, nil } lastExecution := executions[len(executions)-1] nextFilter := &sqlplugin.ExecutionsFilter{ ShardID: m.shardID, WorkflowID: lastExecution.WorkflowID, } token, err := gobSerialize(nextFilter) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListConcreteExecutions failed. Error: %v", err), } } concreteExecutions, err := m.populateInternalListConcreteExecutions(executions) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("ListConcreteExecutions failed. Error: %v", err), } } return &p.InternalListConcreteExecutionsResponse{ Executions: concreteExecutions, NextPageToken: token, }, nil } func getReadLevels(request *p.GetReplicationTasksFromDLQRequest) (readLevel int64, maxReadLevel int64, err error) { readLevel = request.ReadLevel if len(request.NextPageToken) > 0 { readLevel, err = deserializePageToken(request.NextPageToken) if err != nil { return 0, 0, err } } maxReadLevel = max(readLevel+int64(request.BatchSize), request.MaxReadLevel) return readLevel, maxReadLevel, nil } func (m *sqlExecutionStore) GetReplicationTasksFromDLQ( ctx context.Context, request *p.GetReplicationTasksFromDLQRequest, ) (*p.GetHistoryTasksResponse, error) { readLevel, maxReadLevel, err := getReadLevels(request) if err != nil { return nil, err } filter := sqlplugin.ReplicationTasksFilter{ ShardID: m.shardID, InclusiveMinTaskID: readLevel, ExclusiveMaxTaskID: maxReadLevel, PageSize: request.BatchSize, } rows, err := m.db.SelectFromReplicationTasksDLQ(ctx, &sqlplugin.ReplicationTasksDLQFilter{ ReplicationTasksFilter: filter, SourceClusterName: request.SourceClusterName, }) if err != nil { if err != sql.ErrNoRows { return nil, convertCommonErrors(m.db, "GetReplicationTasksFromDLQ", "", err) } } var tasks []p.Task for _, row := range rows { task, err := m.taskSerializer.DeserializeTask(p.HistoryTaskCategoryReplication, p.NewDataBlob(row.Data, constants.EncodingType(row.DataEncoding))) if err != nil { return nil, convertCommonErrors(m.db, "GetReplicationTasksFromDLQ", "", err) } task.SetTaskID(row.TaskID) tasks = append(tasks, task) } resp := &p.GetHistoryTasksResponse{Tasks: tasks} if len(rows) > 0 { nextTaskID := rows[len(rows)-1].TaskID + 1 if nextTaskID < maxReadLevel { resp.NextPageToken = serializePageToken(nextTaskID) } } return resp, nil } func (m *sqlExecutionStore) GetReplicationDLQSize( ctx context.Context, request *p.GetReplicationDLQSizeRequest, ) (*p.GetReplicationDLQSizeResponse, error) { size, err := m.db.SelectFromReplicationDLQ(ctx, &sqlplugin.ReplicationTaskDLQFilter{ SourceClusterName: request.SourceClusterName, ShardID: m.shardID, }) switch err { case nil: return &p.GetReplicationDLQSizeResponse{ Size: size, }, nil case sql.ErrNoRows: return &p.GetReplicationDLQSizeResponse{ Size: 0, }, nil default: return nil, convertCommonErrors(m.db, "GetReplicationDLQSize", "", err) } } func (m *sqlExecutionStore) DeleteReplicationTaskFromDLQ( ctx context.Context, request *p.DeleteReplicationTaskFromDLQRequest, ) error { filter := sqlplugin.ReplicationTasksFilter{ ShardID: m.shardID, TaskID: request.TaskID, } if _, err := m.db.DeleteMessageFromReplicationTasksDLQ(ctx, &sqlplugin.ReplicationTasksDLQFilter{ ReplicationTasksFilter: filter, SourceClusterName: request.SourceClusterName, }); err != nil { return convertCommonErrors(m.db, "DeleteReplicationTaskFromDLQ", "", err) } return nil } func (m *sqlExecutionStore) RangeDeleteReplicationTaskFromDLQ( ctx context.Context, request *p.RangeDeleteReplicationTaskFromDLQRequest, ) (*p.RangeDeleteReplicationTaskFromDLQResponse, error) { filter := sqlplugin.ReplicationTasksFilter{ ShardID: m.shardID, InclusiveMinTaskID: request.InclusiveBeginTaskID, ExclusiveMaxTaskID: request.ExclusiveEndTaskID, PageSize: request.PageSize, } result, err := m.db.RangeDeleteMessageFromReplicationTasksDLQ(ctx, &sqlplugin.ReplicationTasksDLQFilter{ ReplicationTasksFilter: filter, SourceClusterName: request.SourceClusterName, }) if err != nil { return nil, convertCommonErrors(m.db, "RangeDeleteReplicationTaskFromDLQ", "", err) } rowsDeleted, err := result.RowsAffected() if err != nil { return nil, convertCommonErrors(m.db, "RangeDeleteReplicationTaskFromDLQ", "", err) } return &p.RangeDeleteReplicationTaskFromDLQResponse{TasksCompleted: int(rowsDeleted)}, nil } func (m *sqlExecutionStore) CreateFailoverMarkerTasks( ctx context.Context, request *p.CreateFailoverMarkersRequest, ) error { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(m.shardID, m.db.GetTotalNumDBShards()) return m.txExecuteShardLockedFn(ctx, dbShardID, "CreateFailoverMarkerTasks", request.RangeID, func(tx sqlplugin.Tx) error { replicationTasksRows := make([]sqlplugin.ReplicationTasksRow, len(request.Markers)) for i, task := range request.Markers { blob, err := m.parser.ReplicationTaskInfoToBlob(&serialization.ReplicationTaskInfo{ DomainID: serialization.MustParseUUID(task.DomainID), WorkflowID: emptyWorkflowID, RunID: serialization.MustParseUUID(emptyReplicationRunID), TaskType: int16(task.GetTaskType()), FirstEventID: constants.EmptyEventID, NextEventID: constants.EmptyEventID, Version: task.GetVersion(), ScheduledID: constants.EmptyEventID, EventStoreVersion: p.EventStoreVersion, NewRunEventStoreVersion: p.EventStoreVersion, BranchToken: nil, NewRunBranchToken: nil, CreationTimestamp: task.GetVisibilityTimestamp(), }) if err != nil { return err } replicationTasksRows[i].ShardID = m.shardID replicationTasksRows[i].TaskID = task.GetTaskID() replicationTasksRows[i].Data = blob.Data replicationTasksRows[i].DataEncoding = string(blob.Encoding) } result, err := tx.InsertIntoReplicationTasks(ctx, replicationTasksRows) if err != nil { return convertCommonErrors(tx, "CreateFailoverMarkerTasks", "", err) } rowsAffected, err := result.RowsAffected() if err != nil { return &types.InternalServiceError{Message: fmt.Sprintf("CreateFailoverMarkerTasks failed. Could not verify number of rows inserted. Error: %v", err)} } if int(rowsAffected) != len(replicationTasksRows) { return &types.InternalServiceError{Message: fmt.Sprintf("CreateFailoverMarkerTasks failed. Inserted %v instead of %v rows into replication_tasks.", rowsAffected, len(replicationTasksRows))} } return nil }) } type timerTaskPageToken struct { TaskID int64 `json:"TaskID"` // CAUTION: JSON format is used in replication, this should not be changed without great care Timestamp time.Time `json:"Timestamp"` // CAUTION: JSON format is used in replication, this should not be changed without great care } func (t *timerTaskPageToken) serialize() ([]byte, error) { return json.Marshal(t) } func (t *timerTaskPageToken) deserialize(payload []byte) error { return json.Unmarshal(payload, t) } func (m *sqlExecutionStore) PutReplicationTaskToDLQ( ctx context.Context, request *p.InternalPutReplicationTaskToDLQRequest, ) error { replicationTask := request.TaskInfo blob, err := m.parser.ReplicationTaskInfoToBlob(&serialization.ReplicationTaskInfo{ DomainID: serialization.MustParseUUID(replicationTask.DomainID), WorkflowID: replicationTask.WorkflowID, RunID: serialization.MustParseUUID(replicationTask.RunID), TaskType: int16(replicationTask.TaskType), FirstEventID: replicationTask.FirstEventID, NextEventID: replicationTask.NextEventID, Version: replicationTask.Version, ScheduledID: replicationTask.ScheduledID, EventStoreVersion: p.EventStoreVersion, NewRunEventStoreVersion: p.EventStoreVersion, BranchToken: replicationTask.BranchToken, NewRunBranchToken: replicationTask.NewRunBranchToken, CreationTimestamp: replicationTask.CreationTime, }) if err != nil { return err } row := &sqlplugin.ReplicationTaskDLQRow{ SourceClusterName: request.SourceClusterName, ShardID: m.shardID, TaskID: replicationTask.TaskID, Data: blob.Data, DataEncoding: string(blob.Encoding), } _, err = m.db.InsertIntoReplicationTasksDLQ(ctx, row) // Tasks are immutable. So it's fine if we already persisted it before. // This can happen when tasks are retried (ack and cleanup can have lag on source side). if err != nil && !m.db.IsDupEntryError(err) { return convertCommonErrors(m.db, "PutReplicationTaskToDLQ", "", err) } return nil } func (m *sqlExecutionStore) populateWorkflowMutableState( execution sqlplugin.ExecutionsRow, ) (*p.InternalWorkflowMutableState, error) { info, err := m.parser.WorkflowExecutionInfoFromBlob(execution.Data, execution.DataEncoding) if err != nil { return nil, err } state := &p.InternalWorkflowMutableState{} state.ExecutionInfo = serialization.ToInternalWorkflowExecutionInfo(info) state.ExecutionInfo.DomainID = execution.DomainID.String() state.ExecutionInfo.WorkflowID = execution.WorkflowID state.ExecutionInfo.RunID = execution.RunID.String() state.ExecutionInfo.NextEventID = execution.NextEventID // TODO: remove this after all 2DC workflows complete if info.LastWriteEventID != nil { state.ReplicationState = &p.ReplicationState{} state.ReplicationState.StartVersion = info.GetStartVersion() state.ReplicationState.LastWriteVersion = execution.LastWriteVersion state.ReplicationState.LastWriteEventID = info.GetLastWriteEventID() } if info.GetVersionHistories() != nil { state.VersionHistories = p.NewDataBlob( info.GetVersionHistories(), constants.EncodingType(info.GetVersionHistoriesEncoding()), ) } if info.GetChecksum() != nil { state.ChecksumData = p.NewDataBlob( info.GetChecksum(), constants.EncodingType(info.GetChecksumEncoding()), ) } return state, nil } func (m *sqlExecutionStore) populateInternalListConcreteExecutions( executions []sqlplugin.ExecutionsRow, ) ([]*p.InternalListConcreteExecutionsEntity, error) { concreteExecutions := make([]*p.InternalListConcreteExecutionsEntity, 0, len(executions)) for _, execution := range executions { mutableState, err := m.populateWorkflowMutableState(execution) if err != nil { return nil, err } concreteExecution := &p.InternalListConcreteExecutionsEntity{ ExecutionInfo: mutableState.ExecutionInfo, VersionHistories: mutableState.VersionHistories, } concreteExecutions = append(concreteExecutions, concreteExecution) } return concreteExecutions, nil } func (m *sqlExecutionStore) GetHistoryTasks( ctx context.Context, request *p.GetHistoryTasksRequest, ) (*p.GetHistoryTasksResponse, error) { switch request.TaskCategory.Type() { case p.HistoryTaskCategoryTypeImmediate: return m.getImmediateHistoryTasks(ctx, request) case p.HistoryTaskCategoryTypeScheduled: return m.getScheduledHistoryTasks(ctx, request) default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category type: %v", request.TaskCategory.Type())} } } func (m *sqlExecutionStore) getImmediateHistoryTasks( ctx context.Context, request *p.GetHistoryTasksRequest, ) (*p.GetHistoryTasksResponse, error) { switch request.TaskCategory.ID() { case p.HistoryTaskCategoryIDTransfer: inclusiveMinTaskID := request.InclusiveMinTaskKey.GetTaskID() if len(request.NextPageToken) > 0 { var err error inclusiveMinTaskID, err = deserializePageToken(request.NextPageToken) if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("GetImmediateHistoryTasks: error deserializing page token: %v", err)} } } rows, err := m.db.SelectFromTransferTasks(ctx, &sqlplugin.TransferTasksFilter{ ShardID: m.shardID, InclusiveMinTaskID: inclusiveMinTaskID, ExclusiveMaxTaskID: request.ExclusiveMaxTaskKey.GetTaskID(), PageSize: request.PageSize, }) if err != nil { if err != sql.ErrNoRows { return nil, convertCommonErrors(m.db, "GetImmediateHistoryTasks", "", err) } } var tasks []p.Task for _, row := range rows { task, err := m.taskSerializer.DeserializeTask(request.TaskCategory, p.NewDataBlob(row.Data, constants.EncodingType(row.DataEncoding))) if err != nil { return nil, convertCommonErrors(m.db, "GetImmediateHistoryTasks", "", err) } task.SetTaskID(row.TaskID) tasks = append(tasks, task) } resp := &p.GetHistoryTasksResponse{Tasks: tasks} if len(rows) > 0 { nextTaskID := rows[len(rows)-1].TaskID + 1 if nextTaskID < request.ExclusiveMaxTaskKey.GetTaskID() { resp.NextPageToken = serializePageToken(nextTaskID) } } return resp, nil case p.HistoryTaskCategoryIDReplication: inclusiveMinTaskID := request.InclusiveMinTaskKey.GetTaskID() exclusiveMaxTaskID := request.ExclusiveMaxTaskKey.GetTaskID() if len(request.NextPageToken) > 0 { var err error inclusiveMinTaskID, err = deserializePageToken(request.NextPageToken) if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("GetImmediateHistoryTasks: error deserializing page token: %v", err)} } // TODO: this doesn't seem right, we should be using the exclusiveMaxTaskID from the request, but keeping the same logic for now and review it later exclusiveMaxTaskID = max(inclusiveMinTaskID+int64(request.PageSize), exclusiveMaxTaskID) } rows, err := m.db.SelectFromReplicationTasks(ctx, &sqlplugin.ReplicationTasksFilter{ ShardID: m.shardID, InclusiveMinTaskID: inclusiveMinTaskID, ExclusiveMaxTaskID: exclusiveMaxTaskID, PageSize: request.PageSize, }) if err != nil { if err != sql.ErrNoRows { return nil, convertCommonErrors(m.db, "GetImmediateHistoryTasks", "", err) } } var tasks []p.Task for _, row := range rows { task, err := m.taskSerializer.DeserializeTask(request.TaskCategory, p.NewDataBlob(row.Data, constants.EncodingType(row.DataEncoding))) if err != nil { return nil, convertCommonErrors(m.db, "GetImmediateHistoryTasks", "", err) } task.SetTaskID(row.TaskID) tasks = append(tasks, task) } resp := &p.GetHistoryTasksResponse{Tasks: tasks} if len(rows) > 0 { nextTaskID := rows[len(rows)-1].TaskID + 1 if nextTaskID < request.ExclusiveMaxTaskKey.GetTaskID() { resp.NextPageToken = serializePageToken(nextTaskID) } } return resp, nil default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category ID: %v", request.TaskCategory.ID())} } } func (m *sqlExecutionStore) getScheduledHistoryTasks( ctx context.Context, request *p.GetHistoryTasksRequest, ) (*p.GetHistoryTasksResponse, error) { switch request.TaskCategory.ID() { case p.HistoryTaskCategoryIDTimer: pageToken := &timerTaskPageToken{TaskID: math.MinInt64, Timestamp: request.InclusiveMinTaskKey.GetScheduledTime()} if len(request.NextPageToken) > 0 { if err := pageToken.deserialize(request.NextPageToken); err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("error deserializing timerTaskPageToken: %v", err), } } } rows, err := m.db.SelectFromTimerTasks(ctx, &sqlplugin.TimerTasksFilter{ ShardID: m.shardID, MinVisibilityTimestamp: pageToken.Timestamp, TaskID: pageToken.TaskID, MaxVisibilityTimestamp: request.ExclusiveMaxTaskKey.GetScheduledTime(), PageSize: request.PageSize + 1, }) if err != nil { if err != sql.ErrNoRows { return nil, convertCommonErrors(m.db, "GetScheduledHistoryTasks", "", err) } } var tasks []p.Task for _, row := range rows { task, err := m.taskSerializer.DeserializeTask(request.TaskCategory, p.NewDataBlob(row.Data, constants.EncodingType(row.DataEncoding))) if err != nil { return nil, convertCommonErrors(m.db, "GetScheduledHistoryTasks", "", err) } task.SetTaskID(row.TaskID) task.SetVisibilityTimestamp(row.VisibilityTimestamp) tasks = append(tasks, task) } resp := &p.GetHistoryTasksResponse{Tasks: tasks} if len(tasks) > request.PageSize { pageToken = &timerTaskPageToken{ TaskID: tasks[request.PageSize].GetTaskID(), Timestamp: tasks[request.PageSize].GetVisibilityTimestamp(), } resp.Tasks = resp.Tasks[:request.PageSize] nextToken, err := pageToken.serialize() if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("GetScheduledHistoryTasks: error serializing page token: %v", err), } } resp.NextPageToken = nextToken } return resp, nil default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category ID: %v", request.TaskCategory.ID())} } } func (m *sqlExecutionStore) CompleteHistoryTask( ctx context.Context, request *p.CompleteHistoryTaskRequest, ) error { switch request.TaskCategory.Type() { case p.HistoryTaskCategoryTypeScheduled: return m.completeScheduledHistoryTask(ctx, request) case p.HistoryTaskCategoryTypeImmediate: return m.completeImmediateHistoryTask(ctx, request) default: return &types.BadRequestError{Message: fmt.Sprintf("Unknown task category type: %v", request.TaskCategory.Type())} } } func (m *sqlExecutionStore) completeScheduledHistoryTask( ctx context.Context, request *p.CompleteHistoryTaskRequest, ) error { switch request.TaskCategory.ID() { case p.HistoryTaskCategoryIDTimer: if _, err := m.db.DeleteFromTimerTasks(ctx, &sqlplugin.TimerTasksFilter{ ShardID: m.shardID, VisibilityTimestamp: request.TaskKey.GetScheduledTime(), TaskID: request.TaskKey.GetTaskID(), }); err != nil { return convertCommonErrors(m.db, "CompleteScheduledHistoryTask", "", err) } return nil default: return &types.BadRequestError{Message: fmt.Sprintf("Unknown task category ID: %v", request.TaskCategory.ID())} } } func (m *sqlExecutionStore) completeImmediateHistoryTask( ctx context.Context, request *p.CompleteHistoryTaskRequest, ) error { switch request.TaskCategory.ID() { case p.HistoryTaskCategoryIDTransfer: if _, err := m.db.DeleteFromTransferTasks(ctx, &sqlplugin.TransferTasksFilter{ ShardID: m.shardID, TaskID: request.TaskKey.GetTaskID(), }); err != nil { return convertCommonErrors(m.db, "CompleteImmediateHistoryTask", "", err) } return nil case p.HistoryTaskCategoryIDReplication: if _, err := m.db.DeleteFromReplicationTasks(ctx, &sqlplugin.ReplicationTasksFilter{ ShardID: m.shardID, TaskID: request.TaskKey.GetTaskID(), }); err != nil { return convertCommonErrors(m.db, "CompleteImmediateHistoryTask", "", err) } return nil default: return &types.BadRequestError{Message: fmt.Sprintf("Unknown task category ID: %v", request.TaskCategory.ID())} } } func (m *sqlExecutionStore) RangeCompleteHistoryTask( ctx context.Context, request *p.RangeCompleteHistoryTaskRequest, ) (*p.RangeCompleteHistoryTaskResponse, error) { switch request.TaskCategory.Type() { case p.HistoryTaskCategoryTypeScheduled: return m.rangeCompleteScheduledHistoryTask(ctx, request) case p.HistoryTaskCategoryTypeImmediate: return m.rangeCompleteImmediateHistoryTask(ctx, request) default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category type: %v", request.TaskCategory.Type())} } } func (m *sqlExecutionStore) rangeCompleteScheduledHistoryTask( ctx context.Context, request *p.RangeCompleteHistoryTaskRequest, ) (*p.RangeCompleteHistoryTaskResponse, error) { switch request.TaskCategory.ID() { case p.HistoryTaskCategoryIDTimer: result, err := m.db.RangeDeleteFromTimerTasks(ctx, &sqlplugin.TimerTasksFilter{ ShardID: m.shardID, MinVisibilityTimestamp: request.InclusiveMinTaskKey.GetScheduledTime(), MaxVisibilityTimestamp: request.ExclusiveMaxTaskKey.GetScheduledTime(), PageSize: request.PageSize, }) if err != nil { return nil, convertCommonErrors(m.db, "RangeCompleteTimerTask", "", err) } rowsDeleted, err := result.RowsAffected() if err != nil { return nil, convertCommonErrors(m.db, "RangeCompleteTimerTask", "", err) } return &p.RangeCompleteHistoryTaskResponse{TasksCompleted: int(rowsDeleted)}, nil default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category: %v", request.TaskCategory.ID())} } } func (m *sqlExecutionStore) rangeCompleteImmediateHistoryTask( ctx context.Context, request *p.RangeCompleteHistoryTaskRequest, ) (*p.RangeCompleteHistoryTaskResponse, error) { switch request.TaskCategory.ID() { case p.HistoryTaskCategoryIDTransfer: result, err := m.db.RangeDeleteFromTransferTasks(ctx, &sqlplugin.TransferTasksFilter{ ShardID: m.shardID, InclusiveMinTaskID: request.InclusiveMinTaskKey.GetTaskID(), ExclusiveMaxTaskID: request.ExclusiveMaxTaskKey.GetTaskID(), PageSize: request.PageSize, }) if err != nil { return nil, convertCommonErrors(m.db, "RangeCompleteTransferTask", "", err) } rowsDeleted, err := result.RowsAffected() if err != nil { return nil, convertCommonErrors(m.db, "RangeCompleteTransferTask", "", err) } return &p.RangeCompleteHistoryTaskResponse{TasksCompleted: int(rowsDeleted)}, nil case p.HistoryTaskCategoryIDReplication: result, err := m.db.RangeDeleteFromReplicationTasks(ctx, &sqlplugin.ReplicationTasksFilter{ ShardID: m.shardID, ExclusiveMaxTaskID: request.ExclusiveMaxTaskKey.GetTaskID(), PageSize: request.PageSize, }) if err != nil { return nil, convertCommonErrors(m.db, "RangeCompleteReplicationTask", "", err) } rowsDeleted, err := result.RowsAffected() if err != nil { return nil, convertCommonErrors(m.db, "RangeCompleteReplicationTask", "", err) } return &p.RangeCompleteHistoryTaskResponse{TasksCompleted: int(rowsDeleted)}, nil default: return nil, &types.BadRequestError{Message: fmt.Sprintf("Unknown task category: %v", request.TaskCategory.ID())} } } func (m *sqlExecutionStore) GetActiveClusterSelectionPolicy( ctx context.Context, domainID, wfID, rID string, ) (*p.DataBlob, error) { // TODO(active-active): Active cluster selection policy for SQL stores is not yet implemented // It requires creating a new table in the database to store the active cluster selection policy return nil, &types.InternalServiceError{Message: "Not yet implemented"} } func (m *sqlExecutionStore) DeleteActiveClusterSelectionPolicy( ctx context.Context, domainID, wfID, rID string, ) error { // TODO(active-active): Active cluster selection policy for SQL stores is not yet implemented // It requires creating a new table in the database to store the active cluster selection policy return nil } ================================================ FILE: common/persistence/sql/sql_execution_store_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) func TestDeleteCurrentWorkflowExecution(t *testing.T) { shardID := int64(100) testCases := []struct { name string req *persistence.DeleteCurrentWorkflowExecutionRequest mockSetup func(*sqlplugin.MockDB) wantErr bool }{ { name: "Success case", req: &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "aaaa", RunID: "fd65967f-777d-45de-8dee-be49dfda6716", }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().DeleteFromCurrentExecutions(gomock.Any(), &sqlplugin.CurrentExecutionsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "aaaa", RunID: serialization.MustParseUUID("fd65967f-777d-45de-8dee-be49dfda6716"), }).Return(nil, nil) }, wantErr: false, }, { name: "Error case", req: &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "aaaa", RunID: "fd65967f-777d-45de-8dee-be49dfda6716", }, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().DeleteFromCurrentExecutions(gomock.Any(), &sqlplugin.CurrentExecutionsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "aaaa", RunID: serialization.MustParseUUID("fd65967f-777d-45de-8dee-be49dfda6716"), }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := NewSQLExecutionStore(mockDB, nil, int(shardID), nil, nil, nil) require.NoError(t, err, "failed to create execution store") tc.mockSetup(mockDB) err = store.DeleteCurrentWorkflowExecution(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestGetCurrentExecution(t *testing.T) { shardID := int64(100) testCases := []struct { name string req *persistence.GetCurrentExecutionRequest mockSetup func(*sqlplugin.MockDB) want *persistence.GetCurrentExecutionResponse wantErr bool }{ { name: "Success case", req: &persistence.GetCurrentExecutionRequest{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "aaaa", }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().SelectFromCurrentExecutions(gomock.Any(), &sqlplugin.CurrentExecutionsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "aaaa", }).Return(&sqlplugin.CurrentExecutionsRow{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "aaaa", RunID: serialization.MustParseUUID("fd65967f-777d-45de-8dee-be49dfda6716"), CreateRequestID: "create", State: 2, CloseStatus: 3, LastWriteVersion: 9, }, nil) }, want: &persistence.GetCurrentExecutionResponse{ StartRequestID: "create", RunID: "fd65967f-777d-45de-8dee-be49dfda6716", State: 2, CloseStatus: 3, LastWriteVersion: 9, }, wantErr: false, }, { name: "Error case", req: &persistence.GetCurrentExecutionRequest{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "aaaa", }, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().SelectFromCurrentExecutions(gomock.Any(), &sqlplugin.CurrentExecutionsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "aaaa", }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := NewSQLExecutionStore(mockDB, nil, int(shardID), nil, nil, nil) require.NoError(t, err, "failed to create execution store") tc.mockSetup(mockDB) got, err := store.GetCurrentExecution(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestGetReplicationTasksFromDLQ(t *testing.T) { shardID := 0 testCases := []struct { name string req *persistence.GetReplicationTasksFromDLQRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockTaskSerializer) want *persistence.GetHistoryTasksResponse wantErr bool }{ { name: "Success case", req: &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: "source", NextPageToken: serializePageToken(100), MaxReadLevel: 199, BatchSize: 1000, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectFromReplicationTasksDLQ(gomock.Any(), &sqlplugin.ReplicationTasksDLQFilter{ ReplicationTasksFilter: sqlplugin.ReplicationTasksFilter{ ShardID: shardID, InclusiveMinTaskID: 100, ExclusiveMaxTaskID: 1100, PageSize: 1000, }, SourceClusterName: "source", }).Return([]sqlplugin.ReplicationTasksRow{ { ShardID: shardID, TaskID: 100, Data: []byte(`replication`), DataEncoding: "replication", }, }, nil) mockParser.EXPECT().DeserializeTask(persistence.HistoryTaskCategoryReplication, persistence.NewDataBlob([]byte(`replication`), "replication")).Return(&persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "test", RunID: "abdcea69-61d5-44c3-9d55-afe23505a54a", }, TaskData: persistence.TaskData{ Version: 202, VisibilityTimestamp: time.Unix(1, 1), }, FirstEventID: 10, NextEventID: 101, BranchToken: []byte(`bt`), NewRunBranchToken: []byte(`nbt`), }, nil) }, want: &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "test", RunID: "abdcea69-61d5-44c3-9d55-afe23505a54a", }, TaskData: persistence.TaskData{ TaskID: 100, Version: 202, VisibilityTimestamp: time.Unix(1, 1), }, FirstEventID: 10, NextEventID: 101, BranchToken: []byte(`bt`), NewRunBranchToken: []byte(`nbt`), }, }, NextPageToken: serializePageToken(101), }, wantErr: false, }, { name: "Error case - failed to load from database", req: &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: "source", NextPageToken: serializePageToken(100), MaxReadLevel: 199, BatchSize: 1000, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockTaskSerializer) { err := errors.New("some error") mockDB.EXPECT().SelectFromReplicationTasksDLQ(gomock.Any(), &sqlplugin.ReplicationTasksDLQFilter{ ReplicationTasksFilter: sqlplugin.ReplicationTasksFilter{ ShardID: shardID, InclusiveMinTaskID: 100, ExclusiveMaxTaskID: 1100, PageSize: 1000, }, SourceClusterName: "source", }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to decode data", req: &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: "source", NextPageToken: serializePageToken(100), MaxReadLevel: 199, BatchSize: 1000, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectFromReplicationTasksDLQ(gomock.Any(), &sqlplugin.ReplicationTasksDLQFilter{ ReplicationTasksFilter: sqlplugin.ReplicationTasksFilter{ ShardID: shardID, InclusiveMinTaskID: 100, ExclusiveMaxTaskID: 1100, PageSize: 1000, }, SourceClusterName: "source", }).Return([]sqlplugin.ReplicationTasksRow{ { ShardID: shardID, TaskID: 101, Data: []byte(`replication`), DataEncoding: "replication", }, }, nil) mockParser.EXPECT().DeserializeTask(persistence.HistoryTaskCategoryReplication, persistence.NewDataBlob([]byte(`replication`), "replication")).Return(nil, errors.New("some error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockTaskSerializer(ctrl) store, err := NewSQLExecutionStore(mockDB, nil, int(shardID), nil, mockParser, nil) require.NoError(t, err, "failed to create execution store") tc.mockSetup(mockDB, mockParser) got, err := store.GetReplicationTasksFromDLQ(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestGetReplicationDLQSize(t *testing.T) { shardID := 9 testCases := []struct { name string req *persistence.GetReplicationDLQSizeRequest mockSetup func(*sqlplugin.MockDB) want *persistence.GetReplicationDLQSizeResponse wantErr bool }{ { name: "Success case", req: &persistence.GetReplicationDLQSizeRequest{ SourceClusterName: "source", }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().SelectFromReplicationDLQ(gomock.Any(), &sqlplugin.ReplicationTaskDLQFilter{ SourceClusterName: "source", ShardID: shardID, }).Return(int64(1), nil) }, want: &persistence.GetReplicationDLQSizeResponse{ Size: 1, }, wantErr: false, }, { name: "Success case - no row", req: &persistence.GetReplicationDLQSizeRequest{ SourceClusterName: "source", }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().SelectFromReplicationDLQ(gomock.Any(), &sqlplugin.ReplicationTaskDLQFilter{ SourceClusterName: "source", ShardID: shardID, }).Return(int64(0), sql.ErrNoRows) }, want: &persistence.GetReplicationDLQSizeResponse{ Size: 0, }, wantErr: false, }, { name: "Error case", req: &persistence.GetReplicationDLQSizeRequest{ SourceClusterName: "source", }, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().SelectFromReplicationDLQ(gomock.Any(), &sqlplugin.ReplicationTaskDLQFilter{ SourceClusterName: "source", ShardID: shardID, }).Return(int64(0), err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := NewSQLExecutionStore(mockDB, nil, int(shardID), nil, nil, nil) require.NoError(t, err, "failed to create execution store") tc.mockSetup(mockDB) got, err := store.GetReplicationDLQSize(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestDeleteReplicationTaskFromDLQ(t *testing.T) { shardID := 100 testCases := []struct { name string req *persistence.DeleteReplicationTaskFromDLQRequest mockSetup func(*sqlplugin.MockDB) wantErr bool }{ { name: "Success case", req: &persistence.DeleteReplicationTaskFromDLQRequest{ TaskID: 123, SourceClusterName: "source", }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().DeleteMessageFromReplicationTasksDLQ(gomock.Any(), &sqlplugin.ReplicationTasksDLQFilter{ ReplicationTasksFilter: sqlplugin.ReplicationTasksFilter{ ShardID: shardID, TaskID: 123, }, SourceClusterName: "source", }).Return(nil, nil) }, wantErr: false, }, { name: "Error case", req: &persistence.DeleteReplicationTaskFromDLQRequest{ TaskID: 123, SourceClusterName: "source", }, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().DeleteMessageFromReplicationTasksDLQ(gomock.Any(), &sqlplugin.ReplicationTasksDLQFilter{ ReplicationTasksFilter: sqlplugin.ReplicationTasksFilter{ ShardID: shardID, TaskID: 123, }, SourceClusterName: "source", }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := NewSQLExecutionStore(mockDB, nil, int(shardID), nil, nil, nil) require.NoError(t, err, "failed to create execution store") tc.mockSetup(mockDB) err = store.DeleteReplicationTaskFromDLQ(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestRangeDeleteReplicationTaskFromDLQ(t *testing.T) { shardID := 100 testCases := []struct { name string req *persistence.RangeDeleteReplicationTaskFromDLQRequest mockSetup func(*sqlplugin.MockDB) want *persistence.RangeDeleteReplicationTaskFromDLQResponse wantErr bool }{ { name: "Success case", req: &persistence.RangeDeleteReplicationTaskFromDLQRequest{ InclusiveBeginTaskID: 123, ExclusiveEndTaskID: 345, PageSize: 10, SourceClusterName: "source", }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteMessageFromReplicationTasksDLQ(gomock.Any(), &sqlplugin.ReplicationTasksDLQFilter{ ReplicationTasksFilter: sqlplugin.ReplicationTasksFilter{ ShardID: shardID, InclusiveMinTaskID: 123, ExclusiveMaxTaskID: 345, PageSize: 10, }, SourceClusterName: "source", }).Return(&sqlResult{rowsAffected: 10}, nil) }, want: &persistence.RangeDeleteReplicationTaskFromDLQResponse{ TasksCompleted: 10, }, wantErr: false, }, { name: "Error case", req: &persistence.RangeDeleteReplicationTaskFromDLQRequest{ InclusiveBeginTaskID: 123, ExclusiveEndTaskID: 345, PageSize: 10, SourceClusterName: "source", }, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().RangeDeleteMessageFromReplicationTasksDLQ(gomock.Any(), &sqlplugin.ReplicationTasksDLQFilter{ ReplicationTasksFilter: sqlplugin.ReplicationTasksFilter{ ShardID: shardID, InclusiveMinTaskID: 123, ExclusiveMaxTaskID: 345, PageSize: 10, }, SourceClusterName: "source", }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := NewSQLExecutionStore(mockDB, nil, int(shardID), nil, nil, nil) require.NoError(t, err, "failed to create execution store") tc.mockSetup(mockDB) got, err := store.RangeDeleteReplicationTaskFromDLQ(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestPutReplicationTaskToDLQ(t *testing.T) { shardID := 100 testCases := []struct { name string req *persistence.InternalPutReplicationTaskToDLQRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) wantErr bool }{ { name: "Success case", req: &persistence.InternalPutReplicationTaskToDLQRequest{ SourceClusterName: "source", TaskInfo: &persistence.InternalReplicationTaskInfo{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "test", RunID: "abdcea69-61d5-44c3-9d55-afe23505a54a", TaskType: 1, TaskID: 101, Version: 202, FirstEventID: 10, NextEventID: 101, ScheduledID: 19, BranchToken: []byte(`bt`), NewRunBranchToken: []byte(`nbt`), CreationTime: time.Unix(1, 1), }, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockParser.EXPECT().ReplicationTaskInfoToBlob(&serialization.ReplicationTaskInfo{ DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "test", RunID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a54a"), TaskType: 1, Version: 202, FirstEventID: 10, NextEventID: 101, ScheduledID: 19, EventStoreVersion: persistence.EventStoreVersion, NewRunEventStoreVersion: persistence.EventStoreVersion, BranchToken: []byte(`bt`), NewRunBranchToken: []byte(`nbt`), CreationTimestamp: time.Unix(1, 1), }).Return(persistence.DataBlob{Data: []byte(`replication`), Encoding: "replication"}, nil) mockDB.EXPECT().InsertIntoReplicationTasksDLQ(gomock.Any(), &sqlplugin.ReplicationTaskDLQRow{ SourceClusterName: "source", ShardID: shardID, TaskID: 101, Data: []byte(`replication`), DataEncoding: "replication", }).Return(nil, nil) }, wantErr: false, }, { name: "Error case - failed to encode data", req: &persistence.InternalPutReplicationTaskToDLQRequest{ SourceClusterName: "source", TaskInfo: &persistence.InternalReplicationTaskInfo{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "test", RunID: "abdcea69-61d5-44c3-9d55-afe23505a54a", TaskType: 1, TaskID: 101, Version: 202, FirstEventID: 10, NextEventID: 101, ScheduledID: 19, BranchToken: []byte(`bt`), NewRunBranchToken: []byte(`nbt`), CreationTime: time.Unix(1, 1), }, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockParser.EXPECT().ReplicationTaskInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, errors.New("some error")) }, wantErr: true, }, { name: "Error case - failed to insert into database", req: &persistence.InternalPutReplicationTaskToDLQRequest{ SourceClusterName: "source", TaskInfo: &persistence.InternalReplicationTaskInfo{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "test", RunID: "abdcea69-61d5-44c3-9d55-afe23505a54a", TaskType: 1, TaskID: 101, Version: 202, FirstEventID: 10, NextEventID: 101, ScheduledID: 19, BranchToken: []byte(`bt`), NewRunBranchToken: []byte(`nbt`), CreationTime: time.Unix(1, 1), }, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockParser.EXPECT().ReplicationTaskInfoToBlob(gomock.Any()).Return(persistence.DataBlob{Data: []byte(`replication`), Encoding: "replication"}, nil) err := errors.New("some error") mockDB.EXPECT().InsertIntoReplicationTasksDLQ(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsDupEntryError(err).Return(false) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockParser(ctrl) store, err := NewSQLExecutionStore(mockDB, nil, int(shardID), mockParser, nil, nil) require.NoError(t, err, "failed to create execution store") tc.mockSetup(mockDB, mockParser) err = store.PutReplicationTaskToDLQ(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestDeleteWorkflowExecution(t *testing.T) { shardID := int64(100) testCases := []struct { name string req *persistence.DeleteWorkflowExecutionRequest mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx) wantErr bool }{ { name: "Success case", req: &persistence.DeleteWorkflowExecutionRequest{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "wid", RunID: "bbdcea69-61d5-44c3-9d55-afe23505a542", }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().DeleteFromExecutions(gomock.Any(), &sqlplugin.ExecutionsFilter{ ShardID: int(shardID), DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "wid", RunID: serialization.MustParseUUID("bbdcea69-61d5-44c3-9d55-afe23505a542"), }).Return(nil, nil) mockTx.EXPECT().DeleteFromActivityInfoMaps(gomock.Any(), &sqlplugin.ActivityInfoMapsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "wid", RunID: serialization.MustParseUUID("bbdcea69-61d5-44c3-9d55-afe23505a542"), }).Return(nil, nil) mockTx.EXPECT().DeleteFromTimerInfoMaps(gomock.Any(), &sqlplugin.TimerInfoMapsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "wid", RunID: serialization.MustParseUUID("bbdcea69-61d5-44c3-9d55-afe23505a542"), }).Return(nil, nil) mockTx.EXPECT().DeleteFromChildExecutionInfoMaps(gomock.Any(), &sqlplugin.ChildExecutionInfoMapsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "wid", RunID: serialization.MustParseUUID("bbdcea69-61d5-44c3-9d55-afe23505a542"), }).Return(nil, nil) mockTx.EXPECT().DeleteFromRequestCancelInfoMaps(gomock.Any(), &sqlplugin.RequestCancelInfoMapsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "wid", RunID: serialization.MustParseUUID("bbdcea69-61d5-44c3-9d55-afe23505a542"), }).Return(nil, nil) mockTx.EXPECT().DeleteFromSignalInfoMaps(gomock.Any(), &sqlplugin.SignalInfoMapsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "wid", RunID: serialization.MustParseUUID("bbdcea69-61d5-44c3-9d55-afe23505a542"), }).Return(nil, nil) mockTx.EXPECT().DeleteFromBufferedEvents(gomock.Any(), &sqlplugin.BufferedEventsFilter{ ShardID: int(shardID), DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "wid", RunID: serialization.MustParseUUID("bbdcea69-61d5-44c3-9d55-afe23505a542"), }).Return(nil, nil) mockTx.EXPECT().DeleteFromSignalsRequestedSets(gomock.Any(), &sqlplugin.SignalsRequestedSetsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "wid", RunID: serialization.MustParseUUID("bbdcea69-61d5-44c3-9d55-afe23505a542"), }).Return(nil, nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Error case - failed to delete from executions", req: &persistence.DeleteWorkflowExecutionRequest{ DomainID: "abdcea69-61d5-44c3-9d55-afe23505a542", WorkflowID: "wid", RunID: "bbdcea69-61d5-44c3-9d55-afe23505a542", }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().DeleteFromExecutions(gomock.Any(), &sqlplugin.ExecutionsFilter{ ShardID: int(shardID), DomainID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a542"), WorkflowID: "wid", RunID: serialization.MustParseUUID("bbdcea69-61d5-44c3-9d55-afe23505a542"), }).Return(nil, errors.New("some error")) mockTx.EXPECT().IsNotFoundError(gomock.Any()).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) store, err := NewSQLExecutionStore(mockDB, nil, int(shardID), nil, nil, nil) require.NoError(t, err, "failed to create execution store") tc.mockSetup(mockDB, mockTx) err = store.DeleteWorkflowExecution(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestTxExecuteShardLocked(t *testing.T) { tests := []struct { name string mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx) operation string rangeID int64 fn func(sqlplugin.Tx) error wantError error }{ { name: "Success", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().ReadLockShards(gomock.Any(), gomock.Any()).Return(11, nil) mockTx.EXPECT().Commit().Return(nil) }, operation: "Insert", rangeID: 11, fn: func(sqlplugin.Tx) error { return nil }, wantError: nil, }, { name: "Error", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().ReadLockShards(gomock.Any(), gomock.Any()).Return(11, nil) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(false) mockDB.EXPECT().IsTimeoutError(gomock.Any()).Return(false) mockDB.EXPECT().IsThrottlingError(gomock.Any()).Return(false) }, operation: "Insert", rangeID: 11, fn: func(sqlplugin.Tx) error { return errors.New("error") }, wantError: &types.InternalServiceError{Message: "Insert operation failed. Error: error"}, }, { name: "Error - shard ownership lost", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().ReadLockShards(gomock.Any(), gomock.Any()).Return(12, nil) mockTx.EXPECT().Rollback().Return(nil) }, operation: "Insert", rangeID: 11, fn: func(sqlplugin.Tx) error { return errors.New("error") }, wantError: &persistence.ShardOwnershipLostError{ShardID: 0, Msg: "Failed to lock shard. Previous range ID: 11; new range ID: 12"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) tt.mockSetup(mockDB, mockTx) s := &sqlExecutionStore{ shardID: 0, sqlStore: sqlStore{ db: mockDB, logger: testlogger.New(t), }, } gotError := s.txExecuteShardLocked(context.Background(), 0, tt.operation, tt.rangeID, tt.fn) assert.Equal(t, tt.wantError, gotError) }) } } func TestCreateWorkflowExecution(t *testing.T) { testCases := []struct { name string req *persistence.InternalCreateWorkflowExecutionRequest lockCurrentExecutionIfExistsFn func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) createOrUpdateCurrentExecutionFn func(context.Context, sqlplugin.Tx, persistence.CreateWorkflowMode, int, serialization.UUID, string, serialization.UUID, int, int, string, int64, int64) error applyWorkflowSnapshotTxAsNewFn func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error wantErr bool want *persistence.CreateWorkflowExecutionResponse assertErr func(t *testing.T, err error) }{ { name: "Success - mode brand new", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeBrandNew, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return nil, nil }, createOrUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, persistence.CreateWorkflowMode, int, serialization.UUID, string, serialization.UUID, int, int, string, int64, int64) error { return nil }, applyWorkflowSnapshotTxAsNewFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, want: &persistence.CreateWorkflowExecutionResponse{}, }, { name: "Success - mode workflow ID reuse", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeWorkflowIDReuse, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return &sqlplugin.CurrentExecutionsRow{ State: persistence.WorkflowStateCompleted, }, nil }, createOrUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, persistence.CreateWorkflowMode, int, serialization.UUID, string, serialization.UUID, int, int, string, int64, int64) error { return nil }, applyWorkflowSnapshotTxAsNewFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, want: &persistence.CreateWorkflowExecutionResponse{}, }, { name: "Success - mode zombie", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeZombie, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateZombie, }, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return &sqlplugin.CurrentExecutionsRow{ RunID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a54a"), }, nil }, createOrUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, persistence.CreateWorkflowMode, int, serialization.UUID, string, serialization.UUID, int, int, string, int64, int64) error { return nil }, applyWorkflowSnapshotTxAsNewFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, want: &persistence.CreateWorkflowExecutionResponse{}, }, { name: "Error - mode state validation failed", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeZombie, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, }, wantErr: true, }, { name: "Error - lockCurrentExecutionIfExists failed", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeBrandNew, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return nil, errors.New("some random error") }, wantErr: true, }, { name: "Error - mode brand new", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeBrandNew, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return &sqlplugin.CurrentExecutionsRow{ CreateRequestID: "test", WorkflowID: "test", RunID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a54a"), State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, LastWriteVersion: 10, }, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &persistence.WorkflowExecutionAlreadyStartedError{ Msg: "Workflow execution already running. WorkflowId: test", StartRequestID: "test", RunID: "abdcea69-61d5-44c3-9d55-afe23505a54a", State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, LastWriteVersion: 10, }, err) }, }, { name: "Error - mode workflow ID reuse, version mismatch", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeWorkflowIDReuse, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return &sqlplugin.CurrentExecutionsRow{ State: persistence.WorkflowStateCompleted, LastWriteVersion: 10, }, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &persistence.CurrentWorkflowConditionFailedError{ Msg: "Workflow execution creation condition failed. WorkflowId: , LastWriteVersion: 10, PreviousLastWriteVersion: 0", }, err) }, }, { name: "Error - mode workflow ID reuse, state mismatch", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeWorkflowIDReuse, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return &sqlplugin.CurrentExecutionsRow{ State: persistence.WorkflowStateCreated, }, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &persistence.CurrentWorkflowConditionFailedError{ Msg: "Workflow execution creation condition failed. WorkflowId: , State: 0, Expected: 2", }, err) }, }, { name: "Error - mode workflow ID reuse, run ID mismatch", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeWorkflowIDReuse, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return &sqlplugin.CurrentExecutionsRow{ State: persistence.WorkflowStateCompleted, RunID: serialization.MustParseUUID("abdcea69-61d5-44c3-9d55-afe23505a54a"), }, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &persistence.CurrentWorkflowConditionFailedError{ Msg: "Workflow execution creation condition failed. WorkflowId: , RunID: abdcea69-61d5-44c3-9d55-afe23505a54a, PreviousRunID: ", }, err) }, }, { name: "Error - mode zombie, run ID match", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeZombie, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateZombie, }, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return &sqlplugin.CurrentExecutionsRow{}, nil }, wantErr: true, }, { name: "Error - unknown mode", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowMode(100), NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, wantErr: true, }, { name: "Error - createOrUpdateCurrentExecution failed", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeBrandNew, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return nil, nil }, createOrUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, persistence.CreateWorkflowMode, int, serialization.UUID, string, serialization.UUID, int, int, string, int64, int64) error { return errors.New("some random error") }, wantErr: true, }, { name: "Error - applyWorkflowSnapshotTxAsNew failed", req: &persistence.InternalCreateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.CreateWorkflowModeBrandNew, NewWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, lockCurrentExecutionIfExistsFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string) (*sqlplugin.CurrentExecutionsRow, error) { return nil, nil }, createOrUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, persistence.CreateWorkflowMode, int, serialization.UUID, string, serialization.UUID, int, int, string, int64, int64) error { return nil }, applyWorkflowSnapshotTxAsNewFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return errors.New("some random error") }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDB := sqlplugin.NewMockDB(ctrl) mockDB.EXPECT().GetTotalNumDBShards().Return(1) s := &sqlExecutionStore{ shardID: 0, sqlStore: sqlStore{ db: mockDB, logger: testlogger.New(t), }, txExecuteShardLockedFn: func(_ context.Context, _ int, _ string, _ int64, fn func(sqlplugin.Tx) error) error { return fn(nil) }, lockCurrentExecutionIfExistsFn: tc.lockCurrentExecutionIfExistsFn, createOrUpdateCurrentExecutionFn: tc.createOrUpdateCurrentExecutionFn, applyWorkflowSnapshotTxAsNewFn: tc.applyWorkflowSnapshotTxAsNewFn, } got, err := s.CreateWorkflowExecution(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestUpdateWorkflowExecution(t *testing.T) { testCases := []struct { name string req *persistence.InternalUpdateWorkflowExecutionRequest assertNotCurrentExecutionFn func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID) error assertRunIDAndUpdateCurrentExecutionFn func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error applyWorkflowSnapshotTxAsNewFn func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error applyWorkflowMutationTxFn func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error wantErr bool assertErr func(t *testing.T, err error) }{ { name: "Success - mode ignore current", req: &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.UpdateWorkflowModeIgnoreCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{}, }, }, applyWorkflowMutationTxFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error { return nil }, wantErr: false, }, { name: "Success - mode bypass current", req: &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.UpdateWorkflowModeBypassCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertNotCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID) error { return nil }, applyWorkflowMutationTxFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error { return nil }, wantErr: false, }, { name: "Success - mode update current, new workflow", req: &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.UpdateWorkflowModeUpdateCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, NewWorkflowSnapshot: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, }, assertRunIDAndUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error { return nil }, applyWorkflowMutationTxFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error { return nil }, applyWorkflowSnapshotTxAsNewFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, wantErr: false, }, { name: "Success - mode update current, no new workflow", req: &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.UpdateWorkflowModeUpdateCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateRunning, }, }, }, assertRunIDAndUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error { return nil }, applyWorkflowMutationTxFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error { return nil }, wantErr: false, }, { name: "Error - mode state validation failed", req: &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.UpdateWorkflowModeUpdateCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateZombie, }, }, }, wantErr: true, }, { name: "Error - assertNotCurrentExecution failed", req: &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.UpdateWorkflowModeBypassCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertNotCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID) error { return errors.New("some random error") }, wantErr: true, }, { name: "Error - domain ID mismatch", req: &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.UpdateWorkflowModeUpdateCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "a8ead65c-9d0d-43a2-a6ad-dd17c99509af", State: persistence.WorkflowStateCompleted, }, }, NewWorkflowSnapshot: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "c3fab112-5175-4044-a096-a32e7badd4a8", }, }, }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &types.InternalServiceError{ Message: "UpdateWorkflowExecution: cannot continue as new to another domain", }, err) }, }, { name: "Error - assertRunIDAndUpdateCurrentExecution failed", req: &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.UpdateWorkflowModeUpdateCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertRunIDAndUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error { return errors.New("some random error") }, wantErr: true, }, { name: "Error - applyWorkflowMutationTxFn failed", req: &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.UpdateWorkflowModeUpdateCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertRunIDAndUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error { return nil }, applyWorkflowMutationTxFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error { return errors.New("some random error") }, wantErr: true, }, { name: "Error - applyWorkflowSnapshotTxAsNew failed", req: &persistence.InternalUpdateWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.UpdateWorkflowModeUpdateCurrent, UpdateWorkflowMutation: persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, NewWorkflowSnapshot: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, }, assertRunIDAndUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error { return nil }, applyWorkflowMutationTxFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error { return nil }, applyWorkflowSnapshotTxAsNewFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return errors.New("some random error") }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDB := sqlplugin.NewMockDB(ctrl) mockDB.EXPECT().GetTotalNumDBShards().Return(1) s := &sqlExecutionStore{ shardID: 0, sqlStore: sqlStore{ db: mockDB, logger: testlogger.New(t), }, txExecuteShardLockedFn: func(_ context.Context, _ int, _ string, _ int64, fn func(sqlplugin.Tx) error) error { return fn(nil) }, assertNotCurrentExecutionFn: tc.assertNotCurrentExecutionFn, assertRunIDAndUpdateCurrentExecutionFn: tc.assertRunIDAndUpdateCurrentExecutionFn, applyWorkflowMutationTxFn: tc.applyWorkflowMutationTxFn, applyWorkflowSnapshotTxAsNewFn: tc.applyWorkflowSnapshotTxAsNewFn, } err := s.UpdateWorkflowExecution(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestConflictResolveWorkflowExecution(t *testing.T) { testCases := []struct { name string req *persistence.InternalConflictResolveWorkflowExecutionRequest assertRunIDAndUpdateCurrentExecutionFn func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error assertNotCurrentExecutionFn func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID) error applyWorkflowMutationTxFn func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error applyWorkflowSnapshotTxAsResetFn func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error applyWorkflowSnapshotTxAsNewFn func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error wantErr bool assertErr func(t *testing.T, err error) }{ { name: "Success - mode bypass current", req: &persistence.InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertNotCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID) error { return nil }, applyWorkflowSnapshotTxAsResetFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, wantErr: false, }, { name: "Success - mode update current, current workflow exists", req: &persistence.InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.ConflictResolveWorkflowModeUpdateCurrent, ResetWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, NewWorkflowSnapshot: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, CurrentWorkflowMutation: &persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertRunIDAndUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error { return nil }, applyWorkflowSnapshotTxAsResetFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, applyWorkflowMutationTxFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error { return nil }, applyWorkflowSnapshotTxAsNewFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, wantErr: false, }, { name: "Success - mode update current, no current workflow", req: &persistence.InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.ConflictResolveWorkflowModeUpdateCurrent, ResetWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, NewWorkflowSnapshot: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, }, assertRunIDAndUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error { return nil }, applyWorkflowSnapshotTxAsResetFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, applyWorkflowSnapshotTxAsNewFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, wantErr: false, }, { name: "Error - mode state validation failed", req: &persistence.InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.ConflictResolveWorkflowModeUpdateCurrent, ResetWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateZombie, }, }, }, wantErr: true, }, { name: "Error - assertNotCurrentExecution failed", req: &persistence.InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertNotCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID) error { return errors.New("some random error") }, wantErr: true, }, { name: "Error - assertRunIDAndUpdateCurrentExecution failed", req: &persistence.InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.ConflictResolveWorkflowModeUpdateCurrent, ResetWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, NewWorkflowSnapshot: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, CurrentWorkflowMutation: &persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertRunIDAndUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error { return errors.New("some random error") }, wantErr: true, }, { name: "Error - applyWorkflowResetSnapshotTx failed", req: &persistence.InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.ConflictResolveWorkflowModeBypassCurrent, ResetWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertNotCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID) error { return nil }, applyWorkflowSnapshotTxAsResetFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return errors.New("some random error") }, wantErr: true, }, { name: "Error - applyWorkflowMutationTxFn failed", req: &persistence.InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.ConflictResolveWorkflowModeUpdateCurrent, ResetWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, NewWorkflowSnapshot: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, CurrentWorkflowMutation: &persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertRunIDAndUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error { return nil }, applyWorkflowSnapshotTxAsResetFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, applyWorkflowMutationTxFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error { return errors.New("some random error") }, wantErr: true, }, { name: "Error - applyWorkflowSnapshotTxAsNew failed", req: &persistence.InternalConflictResolveWorkflowExecutionRequest{ RangeID: 1, Mode: persistence.ConflictResolveWorkflowModeUpdateCurrent, ResetWorkflowSnapshot: persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, NewWorkflowSnapshot: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, CurrentWorkflowMutation: &persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, assertRunIDAndUpdateCurrentExecutionFn: func(context.Context, sqlplugin.Tx, int, serialization.UUID, string, serialization.UUID, serialization.UUID, string, int, int, int64, int64) error { return nil }, applyWorkflowSnapshotTxAsResetFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return nil }, applyWorkflowMutationTxFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowMutation, serialization.Parser, serialization.TaskSerializer) error { return nil }, applyWorkflowSnapshotTxAsNewFn: func(context.Context, sqlplugin.Tx, int, *persistence.InternalWorkflowSnapshot, serialization.Parser, serialization.TaskSerializer) error { return errors.New("some random error") }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDB := sqlplugin.NewMockDB(ctrl) mockDB.EXPECT().GetTotalNumDBShards().Return(1) s := &sqlExecutionStore{ shardID: 0, sqlStore: sqlStore{ db: mockDB, logger: testlogger.New(t), }, txExecuteShardLockedFn: func(_ context.Context, _ int, _ string, _ int64, fn func(sqlplugin.Tx) error) error { return fn(nil) }, assertNotCurrentExecutionFn: tc.assertNotCurrentExecutionFn, assertRunIDAndUpdateCurrentExecutionFn: tc.assertRunIDAndUpdateCurrentExecutionFn, applyWorkflowMutationTxFn: tc.applyWorkflowMutationTxFn, applyWorkflowSnapshotTxAsResetFn: tc.applyWorkflowSnapshotTxAsResetFn, applyWorkflowSnapshotTxAsNewFn: tc.applyWorkflowSnapshotTxAsNewFn, } err := s.ConflictResolveWorkflowExecution(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestCreateFailoverMarkerTasks(t *testing.T) { testCases := []struct { name string req *persistence.CreateFailoverMarkersRequest mockSetup func(*sqlplugin.MockTx, *serialization.MockParser) wantErr bool }{ { name: "Success case", req: &persistence.CreateFailoverMarkersRequest{ RangeID: 1, Markers: []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{ TaskID: 1, VisibilityTimestamp: time.Unix(11, 12), Version: 101, }, DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", }, }, }, mockSetup: func(tx *sqlplugin.MockTx, parser *serialization.MockParser) { parser.EXPECT().ReplicationTaskInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("test data"), }, nil) tx.EXPECT().InsertIntoReplicationTasks(gomock.Any(), []sqlplugin.ReplicationTasksRow{ { ShardID: 0, TaskID: 1, Data: []byte("test data"), DataEncoding: "thriftrw", }, }).Return(&sqlResult{ rowsAffected: 1, }, nil) }, wantErr: false, }, { name: "Error - ReplicationTaskInfoToBlob failed", req: &persistence.CreateFailoverMarkersRequest{ RangeID: 1, Markers: []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{ TaskID: 1, VisibilityTimestamp: time.Unix(11, 12), Version: 101, }, DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", }, }, }, mockSetup: func(tx *sqlplugin.MockTx, parser *serialization.MockParser) { parser.EXPECT().ReplicationTaskInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, errors.New("some random error")) }, wantErr: true, }, { name: "Error - InsertIntoReplicationTasks failed", req: &persistence.CreateFailoverMarkersRequest{ RangeID: 1, Markers: []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{ TaskID: 1, VisibilityTimestamp: time.Unix(11, 12), Version: 101, }, DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", }, }, }, mockSetup: func(tx *sqlplugin.MockTx, parser *serialization.MockParser) { parser.EXPECT().ReplicationTaskInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("test data"), }, nil) tx.EXPECT().InsertIntoReplicationTasks(gomock.Any(), []sqlplugin.ReplicationTasksRow{ { ShardID: 0, TaskID: 1, Data: []byte("test data"), DataEncoding: "thriftrw", }, }).Return(nil, errors.New("some random error")) tx.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, wantErr: true, }, { name: "Error - row affected error", req: &persistence.CreateFailoverMarkersRequest{ RangeID: 1, Markers: []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{ TaskID: 1, VisibilityTimestamp: time.Unix(11, 12), Version: 101, }, DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", }, }, }, mockSetup: func(tx *sqlplugin.MockTx, parser *serialization.MockParser) { parser.EXPECT().ReplicationTaskInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("test data"), }, nil) tx.EXPECT().InsertIntoReplicationTasks(gomock.Any(), []sqlplugin.ReplicationTasksRow{ { ShardID: 0, TaskID: 1, Data: []byte("test data"), DataEncoding: "thriftrw", }, }).Return(&sqlResult{ err: errors.New("some error"), }, nil) }, wantErr: true, }, { name: "Error - row affected number mismatch", req: &persistence.CreateFailoverMarkersRequest{ RangeID: 1, Markers: []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{ TaskID: 1, VisibilityTimestamp: time.Unix(11, 12), Version: 101, }, DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", }, }, }, mockSetup: func(tx *sqlplugin.MockTx, parser *serialization.MockParser) { parser.EXPECT().ReplicationTaskInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: []byte("test data"), }, nil) tx.EXPECT().InsertIntoReplicationTasks(gomock.Any(), []sqlplugin.ReplicationTasksRow{ { ShardID: 0, TaskID: 1, Data: []byte("test data"), DataEncoding: "thriftrw", }, }).Return(&sqlResult{ rowsAffected: 0, }, nil) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) db := sqlplugin.NewMockDB(ctrl) db.EXPECT().GetTotalNumDBShards().Return(1) tx := sqlplugin.NewMockTx(ctrl) parser := serialization.NewMockParser(ctrl) tc.mockSetup(tx, parser) s := &sqlExecutionStore{ shardID: 0, sqlStore: sqlStore{ db: db, logger: testlogger.New(t), parser: parser, }, txExecuteShardLockedFn: func(_ context.Context, _ int, _ string, _ int64, fn func(sqlplugin.Tx) error) error { return fn(tx) }, } err := s.CreateFailoverMarkerTasks(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestGetWorkflowExecution(t *testing.T) { testCases := []struct { name string req *persistence.InternalGetWorkflowExecutionRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) want *persistence.InternalGetWorkflowExecutionResponse wantErr bool assertErr func(t *testing.T, err error) }{ { name: "Success case", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, RangeID: 1, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromExecutions(gomock.Any(), gomock.Any()).Return([]sqlplugin.ExecutionsRow{ { ShardID: 0, DomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), WorkflowID: "test-workflow-id", RunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), NextEventID: 101, LastWriteVersion: 11, Data: []byte("test data"), DataEncoding: "thriftrw", }, }, nil) db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return([]sqlplugin.ActivityInfoMapsRow{ { ShardID: 0, DomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), WorkflowID: "test-workflow-id", RunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), ScheduleID: 101, Data: []byte("test data"), DataEncoding: "thriftrw", }, }, nil) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return([]sqlplugin.TimerInfoMapsRow{ { ShardID: 0, DomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), WorkflowID: "test-workflow-id", RunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), TimerID: "101", Data: []byte("test data"), DataEncoding: "thriftrw", }, }, nil) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return([]sqlplugin.ChildExecutionInfoMapsRow{ { ShardID: 0, DomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), WorkflowID: "test-workflow-id", RunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), InitiatedID: 101, Data: []byte("test data"), DataEncoding: "thriftrw", }, }, nil) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return([]sqlplugin.RequestCancelInfoMapsRow{ { ShardID: 0, DomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), WorkflowID: "test-workflow-id", RunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), InitiatedID: 101, Data: []byte("test data"), DataEncoding: "thriftrw", }, }, nil) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return([]sqlplugin.SignalInfoMapsRow{ { ShardID: 0, DomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), WorkflowID: "test-workflow-id", RunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), InitiatedID: 101, Data: []byte("test data"), DataEncoding: "thriftrw", }, }, nil) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return([]sqlplugin.SignalsRequestedSetsRow{ { ShardID: 0, DomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), WorkflowID: "test-workflow-id", RunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), SignalID: "test-signal-id", }, }, nil) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return([]sqlplugin.BufferedEventsRow{ { ShardID: 0, DomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), WorkflowID: "test-workflow-id", RunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), Data: []byte("test data"), DataEncoding: "thriftrw", }, }, nil) parser.EXPECT().WorkflowExecutionInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.WorkflowExecutionInfo{ ParentDomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), ParentWorkflowID: "test-parent-workflow-id", ParentRunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), InitiatedID: 101, CompletionEventBatchID: common.Int64Ptr(11), CompletionEvent: []byte("test completion event"), CompletionEventEncoding: "json", TaskList: "test-task-list", IsCron: true, WorkflowTypeName: "test-workflow-type", WorkflowTimeout: time.Duration(101), DecisionTaskTimeout: time.Duration(102), ExecutionContext: []byte("test execution context"), State: persistence.WorkflowStateCompleted, CloseStatus: persistence.WorkflowCloseStatusCompleted, StartVersion: 111, LastWriteEventID: common.Int64Ptr(11), LastEventTaskID: 12, LastFirstEventID: 13, LastProcessedEvent: 14, StartTimestamp: time.Unix(11, 12), LastUpdatedTimestamp: time.Unix(13, 14), DecisionVersion: 101, DecisionScheduleID: 102, DecisionStartedID: 103, DecisionTimeout: time.Duration(104), DecisionAttempt: 105, DecisionStartedTimestamp: time.Unix(15, 16), DecisionScheduledTimestamp: time.Unix(17, 18), CancelRequested: true, DecisionOriginalScheduledTimestamp: time.Unix(19, 20), CreateRequestID: "test-create-request-id", DecisionRequestID: "test-decision-request-id", CancelRequestID: "test-cancel-request-id", StickyTaskList: "test-sticky-task-list", StickyScheduleToStartTimeout: time.Duration(106), RetryAttempt: 107, RetryInitialInterval: time.Duration(108), RetryMaximumInterval: time.Duration(109), RetryMaximumAttempts: 110, RetryExpiration: time.Duration(111), RetryBackoffCoefficient: 111, RetryExpirationTimestamp: time.Unix(23, 24), RetryNonRetryableErrors: []string{"error1", "error2"}, HasRetryPolicy: true, CronSchedule: "test-cron-schedule", EventStoreVersion: 112, EventBranchToken: []byte("test-event-branch-token"), SignalCount: 113, HistorySize: 114, ClientLibraryVersion: "test-client-library-version", ClientFeatureVersion: "test-client-feature-version", ClientImpl: "test-client-impl", AutoResetPoints: []byte("test-auto-reset-points"), AutoResetPointsEncoding: "json", SearchAttributes: map[string][]byte{"test-key": []byte("test-value")}, Memo: map[string][]byte{"test-key": []byte("test-value")}, VersionHistories: []byte("test-version-histories"), VersionHistoriesEncoding: "json", FirstExecutionRunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), PartitionConfig: map[string]string{"test-key": "test-value"}, Checksum: []byte("test-checksum"), ChecksumEncoding: "test-checksum-encoding", ActiveClusterSelectionPolicy: []byte("ActiveClusterSelectionPolicy"), ActiveClusterSelectionPolicyEncoding: "json", }, nil) parser.EXPECT().ActivityInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.ActivityInfo{ Version: 101, ScheduledEventBatchID: 102, ScheduledEvent: []byte("test scheduled event"), ScheduledEventEncoding: "json", ScheduledTimestamp: time.Unix(11, 12), StartedID: 103, StartedEvent: []byte("test started event"), StartedEventEncoding: "json", StartedTimestamp: time.Unix(13, 14), ActivityID: "test-activity-id", RequestID: "test-request-id", ScheduleToStartTimeout: time.Duration(101), ScheduleToCloseTimeout: time.Duration(102), StartToCloseTimeout: time.Duration(103), HeartbeatTimeout: time.Duration(104), CancelRequested: true, CancelRequestID: 105, TimerTaskStatus: 105, Attempt: 106, TaskList: "test-task-list", TaskListKind: types.TaskListKindEphemeral, StartedIdentity: "test-started-identity", HasRetryPolicy: true, RetryInitialInterval: time.Duration(107), RetryMaximumInterval: time.Duration(108), RetryMaximumAttempts: 109, RetryExpirationTimestamp: time.Unix(15, 16), RetryBackoffCoefficient: 110, RetryNonRetryableErrors: []string{"error1", "error2"}, RetryLastFailureReason: "test-retry-last-failure-reason", RetryLastWorkerIdentity: "test-retry-last-worker-identity", RetryLastFailureDetails: []byte("test-retry-last-failure-details"), }, nil) parser.EXPECT().TimerInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.TimerInfo{ Version: 101, StartedID: 102, ExpiryTimestamp: time.Unix(11, 12), TaskID: 103, }, nil) parser.EXPECT().ChildExecutionInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.ChildExecutionInfo{ Version: 101, InitiatedEventBatchID: 102, InitiatedEvent: []byte("test initiated event"), InitiatedEventEncoding: "json", StartedID: 103, StartedWorkflowID: "test-started-workflow-id", StartedRunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), CreateRequestID: "test-create-request-id", StartedEvent: []byte("test started event"), StartedEventEncoding: "json", DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", WorkflowTypeName: "test-workflow-type", ParentClosePolicy: 101, }, nil) parser.EXPECT().RequestCancelInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.RequestCancelInfo{ Version: 101, InitiatedEventBatchID: 102, CancelRequestID: "test-cancel-request-id", }, nil) parser.EXPECT().SignalInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.SignalInfo{ Version: 101, InitiatedEventBatchID: 102, Name: "test-signal-name", Input: []byte("test input"), Control: []byte("test control"), RequestID: "test-signal-request-id", }, nil) db.EXPECT().SelectFromShards(gomock.Any(), gomock.Any()).Return(&sqlplugin.ShardsRow{ RangeID: 1, }, nil) }, want: &persistence.InternalGetWorkflowExecutionResponse{ State: &persistence.InternalWorkflowMutableState{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", ParentDomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", ParentWorkflowID: "test-parent-workflow-id", ParentRunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", InitiatedID: 101, CompletionEventBatchID: 11, CompletionEvent: persistence.NewDataBlob([]byte("test completion event"), constants.EncodingTypeJSON), TaskList: "test-task-list", IsCron: true, WorkflowTypeName: "test-workflow-type", WorkflowTimeout: time.Duration(101), DecisionStartToCloseTimeout: time.Duration(102), DecisionTimeout: time.Duration(104), ExecutionContext: []byte("test execution context"), State: persistence.WorkflowStateCompleted, CloseStatus: persistence.WorkflowCloseStatusCompleted, NextEventID: 101, LastEventTaskID: 12, LastFirstEventID: 13, LastProcessedEvent: 14, StartTimestamp: time.Unix(11, 12), LastUpdatedTimestamp: time.Unix(13, 14), DecisionVersion: 101, DecisionScheduleID: 102, DecisionStartedID: 103, DecisionAttempt: 105, DecisionStartedTimestamp: time.Unix(15, 16), DecisionScheduledTimestamp: time.Unix(17, 18), CancelRequested: true, DecisionOriginalScheduledTimestamp: time.Unix(19, 20), CreateRequestID: "test-create-request-id", DecisionRequestID: "test-decision-request-id", CancelRequestID: "test-cancel-request-id", StickyTaskList: "test-sticky-task-list", StickyScheduleToStartTimeout: time.Duration(106), HasRetryPolicy: true, CronSchedule: "test-cron-schedule", SignalCount: 113, HistorySize: 114, ClientLibraryVersion: "test-client-library-version", ClientFeatureVersion: "test-client-feature-version", ClientImpl: "test-client-impl", FirstExecutionRunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", PartitionConfig: map[string]string{"test-key": "test-value"}, AutoResetPoints: persistence.NewDataBlob([]byte("test-auto-reset-points"), constants.EncodingTypeJSON), Attempt: 107, InitialInterval: time.Duration(108), BackoffCoefficient: 111, MaximumInterval: time.Duration(109), ExpirationTime: time.Unix(23, 24), MaximumAttempts: 110, NonRetriableErrors: []string{"error1", "error2"}, BranchToken: []byte("test-event-branch-token"), SearchAttributes: map[string][]byte{"test-key": []byte("test-value")}, Memo: map[string][]byte{"test-key": []byte("test-value")}, ExpirationInterval: time.Duration(111), ActiveClusterSelectionPolicy: persistence.NewDataBlob([]byte("ActiveClusterSelectionPolicy"), constants.EncodingTypeJSON), }, VersionHistories: persistence.NewDataBlob([]byte("test-version-histories"), constants.EncodingTypeJSON), ReplicationState: &persistence.ReplicationState{ StartVersion: 111, LastWriteVersion: 11, LastWriteEventID: 11, }, ActivityInfos: map[int64]*persistence.InternalActivityInfo{ 101: { Version: 101, ScheduleID: 101, ScheduledEventBatchID: 102, ScheduledEvent: persistence.NewDataBlob([]byte("test scheduled event"), constants.EncodingTypeJSON), ScheduledTime: time.Unix(11, 12), StartedID: 103, StartedTime: time.Unix(13, 14), StartedEvent: persistence.NewDataBlob([]byte("test started event"), constants.EncodingTypeJSON), ActivityID: "test-activity-id", RequestID: "test-request-id", ScheduleToStartTimeout: time.Duration(101), ScheduleToCloseTimeout: time.Duration(102), StartToCloseTimeout: time.Duration(103), HeartbeatTimeout: time.Duration(104), CancelRequested: true, CancelRequestID: 105, TimerTaskStatus: 105, Attempt: 106, TaskList: "test-task-list", TaskListKind: types.TaskListKindEphemeral, StartedIdentity: "test-started-identity", HasRetryPolicy: true, DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", InitialInterval: time.Duration(107), MaximumInterval: time.Duration(108), MaximumAttempts: 109, ExpirationTime: time.Unix(15, 16), BackoffCoefficient: 110, NonRetriableErrors: []string{"error1", "error2"}, LastFailureReason: "test-retry-last-failure-reason", LastWorkerIdentity: "test-retry-last-worker-identity", LastFailureDetails: []byte("test-retry-last-failure-details"), }, }, TimerInfos: map[string]*persistence.TimerInfo{ "101": { Version: 101, StartedID: 102, ExpiryTime: time.Unix(11, 12), TaskStatus: 103, TimerID: "101", }, }, ChildExecutionInfos: map[int64]*persistence.InternalChildExecutionInfo{ 101: { Version: 101, InitiatedID: 101, InitiatedEvent: persistence.NewDataBlob([]byte("test initiated event"), constants.EncodingTypeJSON), InitiatedEventBatchID: 102, StartedID: 103, StartedEvent: persistence.NewDataBlob([]byte("test started event"), constants.EncodingTypeJSON), StartedWorkflowID: "test-started-workflow-id", StartedRunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", CreateRequestID: "test-create-request-id", DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", WorkflowTypeName: "test-workflow-type", ParentClosePolicy: 101, }, }, RequestCancelInfos: map[int64]*persistence.RequestCancelInfo{ 101: { Version: 101, InitiatedID: 101, InitiatedEventBatchID: 102, CancelRequestID: "test-cancel-request-id", }, }, SignalInfos: map[int64]*persistence.SignalInfo{ 101: { Version: 101, InitiatedID: 101, InitiatedEventBatchID: 102, SignalName: "test-signal-name", Input: []byte("test input"), Control: []byte("test control"), SignalRequestID: "test-signal-request-id", }, }, SignalRequestedIDs: map[string]struct{}{ "test-signal-id": {}, }, BufferedEvents: []*persistence.DataBlob{ { Encoding: constants.EncodingTypeThriftRW, Data: []byte("test data"), }, }, ChecksumData: persistence.NewDataBlob([]byte("test-checksum"), "test-checksum-encoding"), }, }, wantErr: false, }, { name: "Error - Shard owner changed", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromExecutions(gomock.Any(), gomock.Any()).Return([]sqlplugin.ExecutionsRow{ { ShardID: 0, DomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), WorkflowID: "test-workflow-id", RunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), NextEventID: 101, LastWriteVersion: 11, Data: []byte("test data"), DataEncoding: "thriftrw", }, }, nil) db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) parser.EXPECT().WorkflowExecutionInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.WorkflowExecutionInfo{ Checksum: []byte("test-checksum"), ChecksumEncoding: "test-checksum-encoding", }, nil) db.EXPECT().SelectFromShards(gomock.Any(), gomock.Any()).Return(&sqlplugin.ShardsRow{ RangeID: 1, }, nil) }, want: &persistence.InternalGetWorkflowExecutionResponse{ State: &persistence.InternalWorkflowMutableState{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", NextEventID: 101, CompletionEventBatchID: -23, }, ActivityInfos: map[int64]*persistence.InternalActivityInfo{}, TimerInfos: map[string]*persistence.TimerInfo{}, ChildExecutionInfos: map[int64]*persistence.InternalChildExecutionInfo{}, RequestCancelInfos: map[int64]*persistence.RequestCancelInfo{}, SignalInfos: map[int64]*persistence.SignalInfo{}, SignalRequestedIDs: map[string]struct{}{}, ChecksumData: nil, }, }, wantErr: false, }, { name: "Error - failed to get shard", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromExecutions(gomock.Any(), gomock.Any()).Return([]sqlplugin.ExecutionsRow{ { ShardID: 0, DomainID: serialization.MustParseUUID("ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d"), WorkflowID: "test-workflow-id", RunID: serialization.MustParseUUID("ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f"), NextEventID: 101, LastWriteVersion: 11, Data: []byte("test data"), DataEncoding: "thriftrw", }, }, nil) db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) parser.EXPECT().WorkflowExecutionInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.WorkflowExecutionInfo{ Checksum: []byte("test-checksum"), ChecksumEncoding: "test-checksum-encoding", }, nil) db.EXPECT().SelectFromShards(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() }, wantErr: true, }, { name: "Error - SelectFromExecutions no row", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromExecutions(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, &types.EntityNotExistsError{}, err) }, }, { name: "Error - SelectFromExecutions failed", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromExecutions(gomock.Any(), gomock.Any()).Return(nil, errors.New("some random error")) db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() }, wantErr: true, }, { name: "Error - SelectFromActivityInfoMaps failed", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, errors.New("some random error")) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() }, wantErr: true, }, { name: "Error - SelectFromTimerInfoMaps failed", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, errors.New("some random error")) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() }, wantErr: true, }, { name: "Error - SelectFromChildExecutionInfoMaps failed", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, errors.New("some random error")) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() }, wantErr: true, }, { name: "Error - SelectFromRequestCancelInfoMaps failed", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, errors.New("some random error")) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() }, wantErr: true, }, { name: "Error - SelectFromSignalInfoMaps failed", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, errors.New("some random error")) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() }, wantErr: true, }, { name: "Error - SelectFromSignalsRequestedSets failed", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, errors.New("some random error")) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() }, wantErr: true, }, { name: "Error - SelectFromBufferedEvents failed", req: &persistence.InternalGetWorkflowExecutionRequest{ DomainID: "ff9c8a3f-0e4f-4d3e-a4d2-6f5f8f3f7d9d", Execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "ee8d7b6e-876c-4b1e-9b6e-5e3e3c6b6b3f", }, }, mockSetup: func(db *sqlplugin.MockDB, parser *serialization.MockParser) { db.EXPECT().SelectFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) db.EXPECT().SelectFromBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, errors.New("some random error")) db.EXPECT().IsNotFoundError(gomock.Any()).Return(true).AnyTimes() }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) db := sqlplugin.NewMockDB(ctrl) parser := serialization.NewMockParser(ctrl) tc.mockSetup(db, parser) s := &sqlExecutionStore{ shardID: 0, sqlStore: sqlStore{ db: db, logger: testlogger.New(t), parser: parser, }, } resp, err := s.GetWorkflowExecution(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, resp, "Response mismatch") } }) } } func TestRangeCompleteHistoryTask(t *testing.T) { ctx := context.Background() shardID := 1 tests := []struct { name string request *persistence.RangeCompleteHistoryTaskRequest setupMock func(*sqlplugin.MockDB) expectedError error }{ { name: "success - scheduled timer task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0).Add(time.Minute), 0), PageSize: 1000, }, setupMock: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteFromTimerTasks(ctx, &sqlplugin.TimerTasksFilter{ ShardID: shardID, MinVisibilityTimestamp: time.Unix(0, 0), MaxVisibilityTimestamp: time.Unix(0, 0).Add(time.Minute), PageSize: 1000, }).Return(&sqlResult{rowsAffected: 1}, nil) }, expectedError: nil, }, { name: "success - immediate transfer task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 1000, }, setupMock: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteFromTransferTasks(ctx, &sqlplugin.TransferTasksFilter{ ShardID: shardID, InclusiveMinTaskID: 100, ExclusiveMaxTaskID: 200, PageSize: 1000, }).Return(&sqlResult{rowsAffected: 1}, nil) }, expectedError: nil, }, { name: "success - immediate replication task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), // this is ignored by replication task ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 1000, }, setupMock: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteFromReplicationTasks(ctx, &sqlplugin.ReplicationTasksFilter{ ShardID: shardID, ExclusiveMaxTaskID: 200, PageSize: 1000, }).Return(&sqlResult{rowsAffected: 1}, nil) }, expectedError: nil, }, { name: "unknown task category error", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategory{}, }, setupMock: func(mockDB *sqlplugin.MockDB) {}, expectedError: &types.BadRequestError{}, }, { name: "database error on timer task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0).Add(time.Minute), 0), PageSize: 1000, }, setupMock: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteFromTimerTasks(ctx, &sqlplugin.TimerTasksFilter{ ShardID: shardID, MinVisibilityTimestamp: time.Unix(0, 0), MaxVisibilityTimestamp: time.Unix(0, 0).Add(time.Minute), PageSize: 1000, }).Return(nil, errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "database error on transfer task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 1000, }, setupMock: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteFromTransferTasks(ctx, &sqlplugin.TransferTasksFilter{ ShardID: shardID, InclusiveMinTaskID: 100, ExclusiveMaxTaskID: 200, PageSize: 1000, }).Return(nil, errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "database error on replication task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 1000, }, setupMock: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteFromReplicationTasks(ctx, &sqlplugin.ReplicationTasksFilter{ ShardID: shardID, ExclusiveMaxTaskID: 200, PageSize: 1000, }).Return(nil, errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "sql result error on timer task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0).Add(time.Minute), 0), PageSize: 1000, }, setupMock: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteFromTimerTasks(ctx, &sqlplugin.TimerTasksFilter{ ShardID: shardID, MinVisibilityTimestamp: time.Unix(0, 0), MaxVisibilityTimestamp: time.Unix(0, 0).Add(time.Minute), PageSize: 1000, }).Return(&sqlResult{err: errors.New("sql result error")}, nil) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("sql result error"), }, { name: "sql result error on transfer task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 1000, }, setupMock: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteFromTransferTasks(ctx, &sqlplugin.TransferTasksFilter{ ShardID: shardID, InclusiveMinTaskID: 100, ExclusiveMaxTaskID: 200, PageSize: 1000, }).Return(&sqlResult{err: errors.New("sql result error")}, nil) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("sql result error"), }, { name: "sql result error on replication task", request: &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 1000, }, setupMock: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteFromReplicationTasks(ctx, &sqlplugin.ReplicationTasksFilter{ ShardID: shardID, ExclusiveMaxTaskID: 200, PageSize: 1000, }).Return(&sqlResult{err: errors.New("sql result error")}, nil) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("sql result error"), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() mockDB := sqlplugin.NewMockDB(controller) store := &sqlExecutionStore{sqlStore: sqlStore{db: mockDB}, shardID: shardID} tc.setupMock(mockDB) resp, err := store.RangeCompleteHistoryTask(ctx, tc.request) if tc.expectedError != nil { require.ErrorAs(t, err, &tc.expectedError) } else { require.NoError(t, err) assert.Equal(t, 1, resp.TasksCompleted) } }) } } func TestGetHistoryTasks_SQL(t *testing.T) { ctx := context.Background() shardID := 1 tests := []struct { name string request *persistence.GetHistoryTasksRequest setupMock func(*sqlplugin.MockDB, *serialization.MockTaskSerializer) expectedError error expectedTasks []persistence.Task expectedNextPageToken []byte }{ { name: "success - get immediate transfer tasks", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 10, NextPageToken: serializePageToken(101), }, setupMock: func(mockDB *sqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectFromTransferTasks(ctx, &sqlplugin.TransferTasksFilter{ ShardID: shardID, InclusiveMinTaskID: 101, ExclusiveMaxTaskID: 200, PageSize: 10, }).Return([]sqlplugin.TransferTasksRow{ { ShardID: shardID, TaskID: 101, Data: []byte(`{"task": "transfer"}`), DataEncoding: "json", }, }, nil) mockTaskSerializer.EXPECT().DeserializeTask(persistence.HistoryTaskCategoryTransfer, persistence.NewDataBlob([]byte(`{"task": "transfer"}`), constants.EncodingTypeJSON)).Return(&persistence.DecisionTask{ TaskList: "test-task-list", }, nil) }, expectedError: nil, expectedTasks: []persistence.Task{ &persistence.DecisionTask{ TaskList: "test-task-list", TaskData: persistence.TaskData{ TaskID: 101, }, }, }, expectedNextPageToken: serializePageToken(102), }, { name: "success - get scheduled timer tasks", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0).UTC(), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0).Add(time.Minute).UTC(), 0), PageSize: 1, NextPageToken: func() []byte { ti := &timerTaskPageToken{TaskID: 10, Timestamp: time.Unix(0, 1).UTC()} token, err := ti.serialize() require.NoError(t, err, "failed to serialize timer page token") return token }(), }, setupMock: func(mockDB *sqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectFromTimerTasks(ctx, &sqlplugin.TimerTasksFilter{ ShardID: shardID, MinVisibilityTimestamp: time.Unix(0, 1).UTC(), TaskID: 10, MaxVisibilityTimestamp: time.Unix(0, 0).Add(time.Minute).UTC(), PageSize: 2, }).Return([]sqlplugin.TimerTasksRow{ { ShardID: shardID, TaskID: 10, VisibilityTimestamp: time.Unix(1, 1), Data: []byte(`{"task": "timer"}`), DataEncoding: "json", }, { ShardID: shardID, TaskID: 101, VisibilityTimestamp: time.Unix(1, 1), Data: []byte(`{"task": "timer"}`), DataEncoding: "json", }, }, nil) mockTaskSerializer.EXPECT().DeserializeTask(persistence.HistoryTaskCategoryTimer, persistence.NewDataBlob([]byte(`{"task": "timer"}`), constants.EncodingTypeJSON)).Return(&persistence.UserTimerTask{ EventID: 100, }, nil) mockTaskSerializer.EXPECT().DeserializeTask(persistence.HistoryTaskCategoryTimer, persistence.NewDataBlob([]byte(`{"task": "timer"}`), constants.EncodingTypeJSON)).Return(&persistence.UserTimerTask{ EventID: 101, }, nil) }, expectedError: nil, expectedTasks: []persistence.Task{ &persistence.UserTimerTask{ EventID: 100, TaskData: persistence.TaskData{ TaskID: 10, VisibilityTimestamp: time.Unix(1, 1), }, }, }, expectedNextPageToken: func() []byte { ti := &timerTaskPageToken{TaskID: 101, Timestamp: time.Unix(1, 1)} token, err := ti.serialize() require.NoError(t, err, "failed to serialize timer page token") return token }(), }, { name: "success - get immediate replication tasks", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 10, NextPageToken: serializePageToken(101), }, setupMock: func(mockDB *sqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectFromReplicationTasks(ctx, &sqlplugin.ReplicationTasksFilter{ ShardID: shardID, InclusiveMinTaskID: 101, ExclusiveMaxTaskID: 200, PageSize: 10, }).Return([]sqlplugin.ReplicationTasksRow{ { ShardID: shardID, TaskID: 101, Data: []byte(`{"task": "replication"}`), DataEncoding: "json", }, }, nil) mockTaskSerializer.EXPECT().DeserializeTask(persistence.HistoryTaskCategoryReplication, persistence.NewDataBlob([]byte(`{"task": "replication"}`), constants.EncodingTypeJSON)).Return(&persistence.HistoryReplicationTask{ FirstEventID: 100, NextEventID: 200, }, nil) }, expectedError: nil, expectedTasks: []persistence.Task{ &persistence.HistoryReplicationTask{ FirstEventID: 100, NextEventID: 200, TaskData: persistence.TaskData{ TaskID: 101, }, }, }, expectedNextPageToken: serializePageToken(102), }, { name: "database error on transfer task retrieval", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, PageSize: 10, }, setupMock: func(mockDB *sqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectFromTransferTasks(ctx, gomock.Any()).Return(nil, errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "database error on replication task retrieval", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, PageSize: 10, }, setupMock: func(mockDB *sqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectFromReplicationTasks(ctx, gomock.Any()).Return(nil, errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "database error on timer task retrieval", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, PageSize: 10, }, setupMock: func(mockDB *sqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) { mockDB.EXPECT().SelectFromTimerTasks(ctx, gomock.Any()).Return(nil, errors.New("db error")) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "unknown task category error", request: &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategory{}, }, setupMock: func(mockDB *sqlplugin.MockDB, mockTaskSerializer *serialization.MockTaskSerializer) {}, expectedError: &types.BadRequestError{}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() mockDB := sqlplugin.NewMockDB(controller) mockTaskSerializer := serialization.NewMockTaskSerializer(controller) store := &sqlExecutionStore{sqlStore: sqlStore{db: mockDB}, shardID: shardID, taskSerializer: mockTaskSerializer} tc.setupMock(mockDB, mockTaskSerializer) resp, err := store.GetHistoryTasks(ctx, tc.request) if tc.expectedError != nil { require.ErrorAs(t, err, &tc.expectedError) } else { require.NoError(t, err) assert.Equal(t, tc.expectedTasks, resp.Tasks) assert.Equal(t, tc.expectedNextPageToken, resp.NextPageToken) } }) } } func TestCompleteHistoryTask(t *testing.T) { ctx := context.Background() shardID := 1 tests := []struct { name string request *persistence.CompleteHistoryTaskRequest setupMock func(any) expectedError error }{ { name: "success - complete scheduled timer task", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, TaskKey: persistence.NewHistoryTaskKey(time.Unix(10, 10), 1), }, setupMock: func(mockDB any) { mock := mockDB.(*sqlplugin.MockDB) mock.EXPECT().DeleteFromTimerTasks(ctx, &sqlplugin.TimerTasksFilter{ ShardID: shardID, VisibilityTimestamp: time.Unix(10, 10), TaskID: 1, }).Return(&sqlResult{rowsAffected: 1}, nil) }, expectedError: nil, }, { name: "success - complete immediate transfer task", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, TaskKey: persistence.NewImmediateTaskKey(2), }, setupMock: func(mockDB any) { mock := mockDB.(*sqlplugin.MockDB) mock.EXPECT().DeleteFromTransferTasks(ctx, &sqlplugin.TransferTasksFilter{ ShardID: shardID, TaskID: 2, }).Return(&sqlResult{rowsAffected: 1}, nil) }, expectedError: nil, }, { name: "success - complete immediate replication task", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, TaskKey: persistence.NewImmediateTaskKey(3), }, setupMock: func(mockDB any) { mock := mockDB.(*sqlplugin.MockDB) mock.EXPECT().DeleteFromReplicationTasks(ctx, &sqlplugin.ReplicationTasksFilter{ ShardID: shardID, TaskID: 3, }).Return(&sqlResult{rowsAffected: 1}, nil) }, expectedError: nil, }, { name: "unknown task category type", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategory{}, }, setupMock: func(mockDB any) {}, expectedError: &types.BadRequestError{}, }, { name: "delete timer task error", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, TaskKey: persistence.NewHistoryTaskKey(time.Unix(10, 10), 1), }, setupMock: func(mockDB any) { mock := mockDB.(*sqlplugin.MockDB) mock.EXPECT().DeleteFromTimerTasks(ctx, &sqlplugin.TimerTasksFilter{ ShardID: shardID, VisibilityTimestamp: time.Unix(10, 10), TaskID: 1, }).Return(&sqlResult{rowsAffected: 0}, errors.New("db error")) mock.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "delete transfer task error", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, TaskKey: persistence.NewImmediateTaskKey(2), }, setupMock: func(mockDB any) { mock := mockDB.(*sqlplugin.MockDB) mock.EXPECT().DeleteFromTransferTasks(ctx, &sqlplugin.TransferTasksFilter{ ShardID: shardID, TaskID: 2, }).Return(&sqlResult{rowsAffected: 0}, errors.New("db error")) mock.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, { name: "delete replication task error", request: &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, TaskKey: persistence.NewImmediateTaskKey(3), }, setupMock: func(mockDB any) { mock := mockDB.(*sqlplugin.MockDB) mock.EXPECT().DeleteFromReplicationTasks(ctx, &sqlplugin.ReplicationTasksFilter{ ShardID: shardID, TaskID: 3, }).Return(&sqlResult{rowsAffected: 0}, errors.New("db error")) mock.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, expectedError: errors.New("db error"), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() mockDB := sqlplugin.NewMockDB(controller) store := &sqlExecutionStore{sqlStore: sqlStore{db: mockDB}, shardID: shardID} tc.setupMock(mockDB) err := store.CompleteHistoryTask(ctx, tc.request) if tc.expectedError != nil { require.ErrorAs(t, err, &tc.expectedError) } else { require.NoError(t, err) } }) } } ================================================ FILE: common/persistence/sql/sql_execution_store_util.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "bytes" "context" "database/sql" "fmt" "time" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) func applyWorkflowMutationTx( ctx context.Context, tx sqlplugin.Tx, shardID int, workflowMutation *p.InternalWorkflowMutation, parser serialization.Parser, taskSerializer serialization.TaskSerializer, ) error { executionInfo := workflowMutation.ExecutionInfo versionHistories := workflowMutation.VersionHistories workflowChecksum := workflowMutation.ChecksumData startVersion := workflowMutation.StartVersion lastWriteVersion := workflowMutation.LastWriteVersion domainID := serialization.MustParseUUID(executionInfo.DomainID) workflowID := executionInfo.WorkflowID runID := serialization.MustParseUUID(executionInfo.RunID) // TODO Remove me if UPDATE holds the lock to the end of a transaction if err := lockAndCheckNextEventID( ctx, tx, shardID, domainID, workflowID, runID, workflowMutation.Condition); err != nil { return err } if err := updateExecution( ctx, tx, executionInfo, versionHistories, workflowChecksum, startVersion, lastWriteVersion, shardID, parser); err != nil { return err } if err := applyTasks( ctx, tx, shardID, workflowMutation.TasksByCategory, taskSerializer, ); err != nil { return err } if err := updateActivityInfos( ctx, tx, workflowMutation.UpsertActivityInfos, workflowMutation.DeleteActivityInfos, shardID, domainID, workflowID, runID, parser, ); err != nil { return err } if err := updateTimerInfos( ctx, tx, workflowMutation.UpsertTimerInfos, workflowMutation.DeleteTimerInfos, shardID, domainID, workflowID, runID, parser, ); err != nil { return err } if err := updateChildExecutionInfos( ctx, tx, workflowMutation.UpsertChildExecutionInfos, workflowMutation.DeleteChildExecutionInfos, shardID, domainID, workflowID, runID, parser, ); err != nil { return err } if err := updateRequestCancelInfos( ctx, tx, workflowMutation.UpsertRequestCancelInfos, workflowMutation.DeleteRequestCancelInfos, shardID, domainID, workflowID, runID, parser, ); err != nil { return err } if err := updateSignalInfos( ctx, tx, workflowMutation.UpsertSignalInfos, workflowMutation.DeleteSignalInfos, shardID, domainID, workflowID, runID, parser, ); err != nil { return err } if err := updateSignalsRequested( ctx, tx, workflowMutation.UpsertSignalRequestedIDs, workflowMutation.DeleteSignalRequestedIDs, shardID, domainID, workflowID, runID, ); err != nil { return err } if workflowMutation.ClearBufferedEvents { if err := deleteBufferedEvents( ctx, tx, shardID, domainID, workflowID, runID, ); err != nil { return err } } return updateBufferedEvents( ctx, tx, workflowMutation.NewBufferedEvents, shardID, domainID, workflowID, runID, ) } func applyWorkflowSnapshotTxAsReset( ctx context.Context, tx sqlplugin.Tx, shardID int, workflowSnapshot *p.InternalWorkflowSnapshot, parser serialization.Parser, taskSerializer serialization.TaskSerializer, ) error { executionInfo := workflowSnapshot.ExecutionInfo versionHistories := workflowSnapshot.VersionHistories workflowChecksum := workflowSnapshot.ChecksumData startVersion := workflowSnapshot.StartVersion lastWriteVersion := workflowSnapshot.LastWriteVersion domainID := serialization.MustParseUUID(executionInfo.DomainID) workflowID := executionInfo.WorkflowID runID := serialization.MustParseUUID(executionInfo.RunID) // TODO Is there a way to modify the various map tables without fear of other people adding rows after we delete, without locking the executions row? if err := lockAndCheckNextEventID( ctx, tx, shardID, domainID, workflowID, runID, workflowSnapshot.Condition); err != nil { return err } if err := updateExecution( ctx, tx, executionInfo, versionHistories, workflowChecksum, startVersion, lastWriteVersion, shardID, parser); err != nil { return err } if err := applyTasks( ctx, tx, shardID, workflowSnapshot.TasksByCategory, taskSerializer, ); err != nil { return err } if err := deleteActivityInfoMap( ctx, tx, shardID, domainID, workflowID, runID); err != nil { return err } if err := updateActivityInfos( ctx, tx, workflowSnapshot.ActivityInfos, nil, shardID, domainID, workflowID, runID, parser); err != nil { return err } if err := deleteTimerInfoMap( ctx, tx, shardID, domainID, workflowID, runID); err != nil { return err } if err := updateTimerInfos( ctx, tx, workflowSnapshot.TimerInfos, nil, shardID, domainID, workflowID, runID, parser); err != nil { return err } if err := deleteChildExecutionInfoMap( ctx, tx, shardID, domainID, workflowID, runID); err != nil { return err } if err := updateChildExecutionInfos( ctx, tx, workflowSnapshot.ChildExecutionInfos, nil, shardID, domainID, workflowID, runID, parser); err != nil { return err } if err := deleteRequestCancelInfoMap( ctx, tx, shardID, domainID, workflowID, runID); err != nil { return err } if err := updateRequestCancelInfos( ctx, tx, workflowSnapshot.RequestCancelInfos, nil, shardID, domainID, workflowID, runID, parser); err != nil { return err } if err := deleteSignalInfoMap( ctx, tx, shardID, domainID, workflowID, runID); err != nil { return err } if err := updateSignalInfos( ctx, tx, workflowSnapshot.SignalInfos, nil, shardID, domainID, workflowID, runID, parser); err != nil { return err } if err := deleteSignalsRequestedSet( ctx, tx, shardID, domainID, workflowID, runID); err != nil { return err } if err := updateSignalsRequested( ctx, tx, workflowSnapshot.SignalRequestedIDs, nil, shardID, domainID, workflowID, runID); err != nil { return err } return deleteBufferedEvents( ctx, tx, shardID, domainID, workflowID, runID) } func applyWorkflowSnapshotTxAsNew( ctx context.Context, tx sqlplugin.Tx, shardID int, workflowSnapshot *p.InternalWorkflowSnapshot, parser serialization.Parser, taskSerializer serialization.TaskSerializer, ) error { executionInfo := workflowSnapshot.ExecutionInfo versionHistories := workflowSnapshot.VersionHistories workflowChecksum := workflowSnapshot.ChecksumData startVersion := workflowSnapshot.StartVersion lastWriteVersion := workflowSnapshot.LastWriteVersion domainID := serialization.MustParseUUID(executionInfo.DomainID) workflowID := executionInfo.WorkflowID runID := serialization.MustParseUUID(executionInfo.RunID) // TODO(active-active): store active cluster selection policy row. It requires a new table in sql DB schemas. if err := createExecution( ctx, tx, executionInfo, versionHistories, workflowChecksum, startVersion, lastWriteVersion, shardID, parser); err != nil { return err } if err := applyTasks( ctx, tx, shardID, workflowSnapshot.TasksByCategory, taskSerializer, ); err != nil { return err } if err := updateActivityInfos( ctx, tx, workflowSnapshot.ActivityInfos, nil, shardID, domainID, workflowID, runID, parser); err != nil { return err } if err := updateTimerInfos( ctx, tx, workflowSnapshot.TimerInfos, nil, shardID, domainID, workflowID, runID, parser); err != nil { return err } if err := updateChildExecutionInfos( ctx, tx, workflowSnapshot.ChildExecutionInfos, nil, shardID, domainID, workflowID, runID, parser); err != nil { return err } if err := updateRequestCancelInfos( ctx, tx, workflowSnapshot.RequestCancelInfos, nil, shardID, domainID, workflowID, runID, parser); err != nil { return err } if err := updateSignalInfos( ctx, tx, workflowSnapshot.SignalInfos, nil, shardID, domainID, workflowID, runID, parser); err != nil { return err } return updateSignalsRequested( ctx, tx, workflowSnapshot.SignalRequestedIDs, nil, shardID, domainID, workflowID, runID) } func applyTasks( ctx context.Context, tx sqlplugin.Tx, shardID int, tasksByCategory map[p.HistoryTaskCategory][]p.Task, taskSerializer serialization.TaskSerializer, ) error { var err error for c, tasks := range tasksByCategory { switch c.Type() { case p.HistoryTaskCategoryTypeImmediate: err = createImmediateTasks(ctx, tx, shardID, c.ID(), tasks, taskSerializer) case p.HistoryTaskCategoryTypeScheduled: err = createScheduledTasks(ctx, tx, shardID, c.ID(), tasks, taskSerializer) } if err != nil { return err } } return nil } // lockCurrentExecutionIfExists returns current execution or nil if none is found for the workflowID // locking it in the DB func lockCurrentExecutionIfExists( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, ) (*sqlplugin.CurrentExecutionsRow, error) { rows, err := tx.LockCurrentExecutionsJoinExecutions(ctx, &sqlplugin.CurrentExecutionsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, }) if err != nil { if err != sql.ErrNoRows { return nil, convertCommonErrors(tx, "lockCurrentExecutionIfExists", fmt.Sprintf("Failed to get current_executions row for (shard,domain,workflow) = (%v, %v, %v).", shardID, domainID, workflowID), err) } } size := len(rows) if size > 1 { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("lockCurrentExecutionIfExists failed. Multiple current_executions rows for (shard,domain,workflow) = (%v, %v, %v).", shardID, domainID, workflowID), } } if size == 0 { return nil, nil } return &rows[0], nil } func createOrUpdateCurrentExecution( ctx context.Context, tx sqlplugin.Tx, createMode p.CreateWorkflowMode, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, state int, closeStatus int, createRequestID string, startVersion int64, lastWriteVersion int64, ) error { row := sqlplugin.CurrentExecutionsRow{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, CreateRequestID: createRequestID, State: state, CloseStatus: closeStatus, StartVersion: startVersion, LastWriteVersion: lastWriteVersion, } switch createMode { case p.CreateWorkflowModeContinueAsNew, p.CreateWorkflowModeWorkflowIDReuse: if err := updateCurrentExecution( ctx, tx, shardID, domainID, workflowID, runID, createRequestID, state, closeStatus, row.StartVersion, row.LastWriteVersion); err != nil { return err } case p.CreateWorkflowModeBrandNew: if _, err := tx.InsertIntoCurrentExecutions(ctx, &row); err != nil { return convertCommonErrors(tx, "createOrUpdateCurrentExecution", "Failed to insert into current_executions table.", err) } case p.CreateWorkflowModeZombie: // noop default: return fmt.Errorf("createOrUpdateCurrentExecution failed. Unknown workflow creation mode: %v", createMode) } return nil } func lockAndCheckNextEventID( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, condition int64, ) error { nextEventID, err := lockNextEventID( ctx, tx, shardID, domainID, workflowID, runID, ) if err != nil { return err } if *nextEventID != condition { return &p.ConditionFailedError{ Msg: fmt.Sprintf("lockAndCheckNextEventID failed. Next_event_id was %v when it should have been %v.", nextEventID, condition), } } return nil } func lockNextEventID( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) (*int64, error) { nextEventID, err := tx.WriteLockExecutions(ctx, &sqlplugin.ExecutionsFilter{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, }) if err != nil { if err == sql.ErrNoRows { return nil, &types.EntityNotExistsError{ Message: fmt.Sprintf( "lockNextEventID failed. Unable to lock executions row with (shard, domain, workflow, run) = (%v,%v,%v,%v) which does not exist.", shardID, domainID, workflowID, runID, ), } } return nil, convertCommonErrors(tx, "lockNextEventID", "", err) } result := int64(nextEventID) return &result, nil } func createImmediateTasks( ctx context.Context, tx sqlplugin.Tx, shardID int, categoryID int, tasks []p.Task, taskSerializer serialization.TaskSerializer, ) error { switch categoryID { case p.HistoryTaskCategoryIDTransfer: return createTransferTasks(ctx, tx, tasks, shardID, taskSerializer) case p.HistoryTaskCategoryIDReplication: return createReplicationTasks(ctx, tx, tasks, shardID, taskSerializer) } // TODO: implement creating tasks for other categories return nil } func createScheduledTasks( ctx context.Context, tx sqlplugin.Tx, shardID int, categoryID int, tasks []p.Task, taskSerializer serialization.TaskSerializer, ) error { switch categoryID { case p.HistoryTaskCategoryIDTimer: return createTimerTasks(ctx, tx, tasks, shardID, taskSerializer) } // TODO: implement creating tasks for other categories return nil } func createTransferTasks( ctx context.Context, tx sqlplugin.Tx, transferTasks []p.Task, shardID int, taskSerializer serialization.TaskSerializer, ) error { if len(transferTasks) == 0 { return nil } transferTasksRows := make([]sqlplugin.TransferTasksRow, len(transferTasks)) for i, task := range transferTasks { blob, err := taskSerializer.SerializeTask(p.HistoryTaskCategoryTransfer, task) if err != nil { return err } transferTasksRows[i].ShardID = shardID transferTasksRows[i].TaskID = task.GetTaskID() transferTasksRows[i].Data = blob.Data transferTasksRows[i].DataEncoding = string(blob.Encoding) } result, err := tx.InsertIntoTransferTasks(ctx, transferTasksRows) if err != nil { return convertCommonErrors(tx, "createTransferTasks", "", err) } rowsAffected, err := result.RowsAffected() if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("createTransferTasks failed. Could not verify number of rows inserted. Error: %v", err), } } if int(rowsAffected) != len(transferTasks) { return &types.InternalServiceError{ Message: fmt.Sprintf("createTransferTasks failed. Inserted %v instead of %v rows into transfer_tasks. Error: %v", rowsAffected, len(transferTasks), err), } } return nil } func createReplicationTasks( ctx context.Context, tx sqlplugin.Tx, replicationTasks []p.Task, shardID int, taskSerializer serialization.TaskSerializer, ) error { if len(replicationTasks) == 0 { return nil } replicationTasksRows := make([]sqlplugin.ReplicationTasksRow, len(replicationTasks)) for i, task := range replicationTasks { blob, err := taskSerializer.SerializeTask(p.HistoryTaskCategoryReplication, task) if err != nil { return err } replicationTasksRows[i].ShardID = shardID replicationTasksRows[i].TaskID = task.GetTaskID() replicationTasksRows[i].Data = blob.Data replicationTasksRows[i].DataEncoding = string(blob.Encoding) } result, err := tx.InsertIntoReplicationTasks(ctx, replicationTasksRows) if err != nil { return convertCommonErrors(tx, "createReplicationTasks", "", err) } rowsAffected, err := result.RowsAffected() if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("createReplicationTasks failed. Could not verify number of rows inserted. Error: %v", err), } } if int(rowsAffected) != len(replicationTasks) { return &types.InternalServiceError{ Message: fmt.Sprintf("createReplicationTasks failed. Inserted %v instead of %v rows into transfer_tasks. Error: %v", rowsAffected, len(replicationTasks), err), } } return nil } func createTimerTasks( ctx context.Context, tx sqlplugin.Tx, timerTasks []p.Task, shardID int, taskSerializer serialization.TaskSerializer, ) error { if len(timerTasks) == 0 { return nil } timerTasksRows := make([]sqlplugin.TimerTasksRow, len(timerTasks)) for i, task := range timerTasks { blob, err := taskSerializer.SerializeTask(p.HistoryTaskCategoryTimer, task) if err != nil { return err } timerTasksRows[i].ShardID = shardID timerTasksRows[i].VisibilityTimestamp = task.GetVisibilityTimestamp() timerTasksRows[i].TaskID = task.GetTaskID() timerTasksRows[i].Data = blob.Data timerTasksRows[i].DataEncoding = string(blob.Encoding) } result, err := tx.InsertIntoTimerTasks(ctx, timerTasksRows) if err != nil { return convertCommonErrors(tx, "createTimerTasks", "", err) } rowsAffected, err := result.RowsAffected() if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("createTimerTasks failed. Could not verify number of rows inserted. Error: %v", err), } } if int(rowsAffected) != len(timerTasks) { return &types.InternalServiceError{ Message: fmt.Sprintf("createTimerTasks failed. Inserted %v instead of %v rows into timer_tasks.", rowsAffected, len(timerTasks)), } } return nil } func assertNotCurrentExecution( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) error { currentRow, err := tx.LockCurrentExecutions(ctx, &sqlplugin.CurrentExecutionsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, }) if err != nil { if err == sql.ErrNoRows { // allow bypassing no current record return nil } return convertCommonErrors(tx, "assertCurrentExecution", "Unable to load current record.", err) } return assertRunIDMismatch(runID, currentRow.RunID) } func assertRunIDAndUpdateCurrentExecution( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, newRunID serialization.UUID, previousRunID serialization.UUID, createRequestID string, state int, closeStatus int, startVersion int64, lastWriteVersion int64, ) error { assertFn := func(currentRow *sqlplugin.CurrentExecutionsRow) error { if !bytes.Equal(currentRow.RunID, previousRunID) { return &p.ConditionFailedError{Msg: fmt.Sprintf( "assertRunIDAndUpdateCurrentExecution failed. Current run ID was %v, expected %v", currentRow.RunID, previousRunID, )} } return nil } if err := assertCurrentExecution(ctx, tx, shardID, domainID, workflowID, assertFn); err != nil { return err } return updateCurrentExecution(ctx, tx, shardID, domainID, workflowID, newRunID, createRequestID, state, closeStatus, startVersion, lastWriteVersion) } func assertCurrentExecution( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, assertFn func(currentRow *sqlplugin.CurrentExecutionsRow) error, ) error { currentRow, err := tx.LockCurrentExecutions(ctx, &sqlplugin.CurrentExecutionsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, }) if err != nil { return convertCommonErrors(tx, "assertCurrentExecution", "Unable to load current record.", err) } return assertFn(currentRow) } func assertRunIDMismatch(runID serialization.UUID, currentRunID serialization.UUID) error { // zombie workflow creation with existence of current record, this is a noop if bytes.Equal(currentRunID, runID) { return &p.ConditionFailedError{Msg: fmt.Sprintf( "assertRunIDMismatch failed. Current run ID was %v, input %v", currentRunID, runID, )} } return nil } func updateCurrentExecution( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, createRequestID string, state int, closeStatus int, startVersion int64, lastWriteVersion int64, ) error { result, err := tx.UpdateCurrentExecutions(ctx, &sqlplugin.CurrentExecutionsRow{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, CreateRequestID: createRequestID, State: state, CloseStatus: closeStatus, StartVersion: startVersion, LastWriteVersion: lastWriteVersion, }) if err != nil { return convertCommonErrors(tx, "updateCurrentExecution", "", err) } rowsAffected, err := result.RowsAffected() if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("updateCurrentExecution failed. Failed to check number of rows updated in current_executions table. Error: %v", err), } } if rowsAffected != 1 { return &types.InternalServiceError{ Message: fmt.Sprintf("updateCurrentExecution failed. %v rows of current_executions updated instead of 1.", rowsAffected), } } return nil } func buildExecutionRow( executionInfo *p.InternalWorkflowExecutionInfo, versionHistories *p.DataBlob, workflowChecksum *p.DataBlob, startVersion int64, lastWriteVersion int64, shardID int, parser serialization.Parser, ) (row *sqlplugin.ExecutionsRow, err error) { info := serialization.FromInternalWorkflowExecutionInfo(executionInfo) info.StartVersion = startVersion if versionHistories == nil { // this is allowed } else { info.VersionHistories = versionHistories.Data info.VersionHistoriesEncoding = string(versionHistories.GetEncoding()) } if workflowChecksum != nil { info.Checksum = workflowChecksum.Data info.ChecksumEncoding = string(workflowChecksum.GetEncoding()) } blob, err := parser.WorkflowExecutionInfoToBlob(info) if err != nil { return nil, err } return &sqlplugin.ExecutionsRow{ ShardID: shardID, DomainID: serialization.MustParseUUID(executionInfo.DomainID), WorkflowID: executionInfo.WorkflowID, RunID: serialization.MustParseUUID(executionInfo.RunID), NextEventID: int64(executionInfo.NextEventID), LastWriteVersion: lastWriteVersion, Data: blob.Data, DataEncoding: string(blob.Encoding), }, nil } func createExecution( ctx context.Context, tx sqlplugin.Tx, executionInfo *p.InternalWorkflowExecutionInfo, versionHistories *p.DataBlob, workflowChecksum *p.DataBlob, startVersion int64, lastWriteVersion int64, shardID int, parser serialization.Parser, ) error { // validate workflow state & close status if err := p.ValidateCreateWorkflowStateCloseStatus( executionInfo.State, executionInfo.CloseStatus); err != nil { return err } now := time.Now() // TODO: this case seems to be always false if executionInfo.StartTimestamp.IsZero() { executionInfo.StartTimestamp = now } row, err := buildExecutionRow( executionInfo, versionHistories, workflowChecksum, startVersion, lastWriteVersion, shardID, parser, ) if err != nil { return err } result, err := tx.InsertIntoExecutions(ctx, row) if err != nil { if tx.IsDupEntryError(err) { return &p.WorkflowExecutionAlreadyStartedError{ Msg: fmt.Sprintf("Workflow execution already running. WorkflowId: %v", executionInfo.WorkflowID), StartRequestID: executionInfo.CreateRequestID, RunID: executionInfo.RunID, State: executionInfo.State, CloseStatus: executionInfo.CloseStatus, LastWriteVersion: row.LastWriteVersion, } } return convertCommonErrors(tx, "createExecution", "", err) } rowsAffected, err := result.RowsAffected() if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("createExecution failed. Failed to verify number of rows affected. Erorr: %v", err), } } if rowsAffected != 1 { return &types.EntityNotExistsError{ Message: fmt.Sprintf("createExecution failed. Affected %v rows updated instead of 1.", rowsAffected), } } return nil } func updateExecution( ctx context.Context, tx sqlplugin.Tx, executionInfo *p.InternalWorkflowExecutionInfo, versionHistories *p.DataBlob, workflowChecksum *p.DataBlob, startVersion int64, lastWriteVersion int64, shardID int, parser serialization.Parser, ) error { // validate workflow state & close status if err := p.ValidateUpdateWorkflowStateCloseStatus( executionInfo.State, executionInfo.CloseStatus); err != nil { return err } row, err := buildExecutionRow( executionInfo, versionHistories, workflowChecksum, startVersion, lastWriteVersion, shardID, parser, ) if err != nil { return err } result, err := tx.UpdateExecutions(ctx, row) if err != nil { return convertCommonErrors(tx, "updateExecution", "", err) } rowsAffected, err := result.RowsAffected() if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("updateExecution failed. Failed to verify number of rows affected. Erorr: %v", err), } } if rowsAffected != 1 { return &types.EntityNotExistsError{ Message: fmt.Sprintf("updateExecution failed. Affected %v rows updated instead of 1.", rowsAffected), } } return nil } ================================================ FILE: common/persistence/sql/sql_execution_store_util_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) func mockSetupLockAndCheckNextEventID( mockTx *sqlplugin.MockTx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, condition int64, wantErr bool, ) { var nextEventID int var err error if wantErr { err = errors.New("some error") } else { nextEventID = int(condition) } mockTx.EXPECT().WriteLockExecutions(gomock.Any(), &sqlplugin.ExecutionsFilter{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).Return(nextEventID, err) } func mockCreateExecution( mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockParser.EXPECT().WorkflowExecutionInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil) mockTx.EXPECT().InsertIntoExecutions(gomock.Any(), gomock.Any()).Return(&sqlResult{rowsAffected: 1}, err) } func mockUpdateExecution( mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockParser.EXPECT().WorkflowExecutionInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil) mockTx.EXPECT().UpdateExecutions(gomock.Any(), gomock.Any()).Return(&sqlResult{rowsAffected: 1}, err) } func mockCreateTransferTasks( mockTx *sqlplugin.MockTx, mockTaskSerializer *serialization.MockTaskSerializer, tasks int, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()).Return(persistence.DataBlob{}, nil).Times(tasks) mockTx.EXPECT().InsertIntoTransferTasks(gomock.Any(), gomock.Any()).Return(&sqlResult{rowsAffected: int64(tasks)}, err) } func mockCreateReplicationTasks( mockTx *sqlplugin.MockTx, mockTaskSerializer *serialization.MockTaskSerializer, tasks int, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryReplication, gomock.Any()).Return(persistence.DataBlob{}, nil).Times(tasks) mockTx.EXPECT().InsertIntoReplicationTasks(gomock.Any(), gomock.Any()).Return(&sqlResult{rowsAffected: int64(tasks)}, err) } func mockCreateTimerTasks( mockTx *sqlplugin.MockTx, mockTaskSerializer *serialization.MockTaskSerializer, tasks int, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTimer, gomock.Any()).Return(persistence.DataBlob{}, nil).Times(tasks) mockTx.EXPECT().InsertIntoTimerTasks(gomock.Any(), gomock.Any()).Return(&sqlResult{rowsAffected: int64(tasks)}, err) } func mockApplyTasks( mockTx *sqlplugin.MockTx, mockTaskSerializer *serialization.MockTaskSerializer, transfer int, timer int, replication int, wantErr bool, ) { mockCreateTransferTasks(mockTx, mockTaskSerializer, transfer, wantErr) if wantErr { return } mockCreateTimerTasks(mockTx, mockTaskSerializer, timer, wantErr) mockCreateReplicationTasks(mockTx, mockTaskSerializer, replication, wantErr) } func mockUpdateActivityInfos( mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, activityInfos int, deleteInfos int, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockParser.EXPECT().ActivityInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil).Times(activityInfos) if activityInfos > 0 { mockTx.EXPECT().ReplaceIntoActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, nil) } if deleteInfos > 0 { mockTx.EXPECT().DeleteFromActivityInfoMaps(gomock.Any(), gomock.Any()).Return(nil, err) } } func mockUpdateTimerInfos( mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, timerInfos int, deleteInfos int, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockParser.EXPECT().TimerInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil).Times(timerInfos) if timerInfos > 0 { mockTx.EXPECT().ReplaceIntoTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, nil) } if deleteInfos > 0 { mockTx.EXPECT().DeleteFromTimerInfoMaps(gomock.Any(), gomock.Any()).Return(nil, err) } } func mockUpdateChildExecutionInfos( mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, childExecutionInfos int, deleteInfos int, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockParser.EXPECT().ChildExecutionInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil).Times(childExecutionInfos) if childExecutionInfos > 0 { mockTx.EXPECT().ReplaceIntoChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, nil) } if deleteInfos > 0 { mockTx.EXPECT().DeleteFromChildExecutionInfoMaps(gomock.Any(), gomock.Any()).Return(nil, err) } } func mockUpdateRequestCancelInfos( mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, cancelInfos int, deleteInfos int, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockParser.EXPECT().RequestCancelInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil).Times(cancelInfos) if cancelInfos > 0 { mockTx.EXPECT().ReplaceIntoRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, nil) } if deleteInfos > 0 { mockTx.EXPECT().DeleteFromRequestCancelInfoMaps(gomock.Any(), gomock.Any()).Return(nil, err) } } func mockUpdateSignalInfos( mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, signalInfos int, deleteInfos int, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockParser.EXPECT().SignalInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil).Times(signalInfos) if signalInfos > 0 { mockTx.EXPECT().ReplaceIntoSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, nil) } if deleteInfos > 0 { mockTx.EXPECT().DeleteFromSignalInfoMaps(gomock.Any(), gomock.Any()).Return(nil, err) } } func mockUpdateSignalRequested( mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, signalRequested int, deleteInfos int, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } if signalRequested > 0 { mockTx.EXPECT().InsertIntoSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, nil) } if deleteInfos > 0 { mockTx.EXPECT().DeleteFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, err) } } func mockDeleteActivityInfoMap( mockTx *sqlplugin.MockTx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockTx.EXPECT().DeleteFromActivityInfoMaps(gomock.Any(), &sqlplugin.ActivityInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).Return(nil, err) if wantErr { mockTx.EXPECT().IsNotFoundError(err).Return(true) } } func mockDeleteTimerInfoMap( mockTx *sqlplugin.MockTx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockTx.EXPECT().DeleteFromTimerInfoMaps(gomock.Any(), &sqlplugin.TimerInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).Return(nil, err) if wantErr { mockTx.EXPECT().IsNotFoundError(err).Return(true) } } func mockDeleteChildExecutionInfoMap( mockTx *sqlplugin.MockTx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockTx.EXPECT().DeleteFromChildExecutionInfoMaps(gomock.Any(), &sqlplugin.ChildExecutionInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).Return(nil, err) if wantErr { mockTx.EXPECT().IsNotFoundError(err).Return(true) } } func mockDeleteRequestCancelInfoMap( mockTx *sqlplugin.MockTx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockTx.EXPECT().DeleteFromRequestCancelInfoMaps(gomock.Any(), &sqlplugin.RequestCancelInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).Return(nil, err) if wantErr { mockTx.EXPECT().IsNotFoundError(err).Return(true) } } func mockDeleteSignalInfoMap( mockTx *sqlplugin.MockTx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockTx.EXPECT().DeleteFromSignalInfoMaps(gomock.Any(), &sqlplugin.SignalInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).Return(nil, err) if wantErr { mockTx.EXPECT().IsNotFoundError(err).Return(true) } } func mockDeleteSignalRequestedSet( mockTx *sqlplugin.MockTx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockTx.EXPECT().DeleteFromSignalsRequestedSets(gomock.Any(), &sqlplugin.SignalsRequestedSetsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).Return(nil, err) if wantErr { mockTx.EXPECT().IsNotFoundError(err).Return(true) } } func mockDeleteBufferedEvents( mockTx *sqlplugin.MockTx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, wantErr bool, ) { var err error if wantErr { err = errors.New("some error") } mockTx.EXPECT().DeleteFromBufferedEvents(gomock.Any(), &sqlplugin.BufferedEventsFilter{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).Return(nil, err) if wantErr { mockTx.EXPECT().IsNotFoundError(err).Return(true) } } func TestApplyWorkflowMutationTx(t *testing.T) { shardID := 1 testCases := []struct { name string workflow *persistence.InternalWorkflowMutation mockSetup func(*sqlplugin.MockTx, *serialization.MockParser, *serialization.MockTaskSerializer) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", workflow: &persistence.InternalWorkflowMutation{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "8be8a310-7d20-483e-a5d2-48659dc47602", WorkflowID: "abc", RunID: "8be8a310-7d20-483e-a5d2-48659dc47603", }, Condition: 9, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: []persistence.Task{ &persistence.ActivityTask{}, }, persistence.HistoryTaskCategoryTimer: []persistence.Task{ &persistence.DecisionTimeoutTask{}, &persistence.DecisionTimeoutTask{}, &persistence.DecisionTimeoutTask{}, }, persistence.HistoryTaskCategoryReplication: []persistence.Task{ &persistence.HistoryReplicationTask{}, &persistence.HistoryReplicationTask{}, &persistence.HistoryReplicationTask{}, &persistence.HistoryReplicationTask{}, }, }, UpsertActivityInfos: []*persistence.InternalActivityInfo{ {}, }, DeleteActivityInfos: []int64{1, 2}, UpsertTimerInfos: []*persistence.TimerInfo{ {}, }, DeleteTimerInfos: []string{"a", "b"}, UpsertChildExecutionInfos: []*persistence.InternalChildExecutionInfo{ {}, }, DeleteChildExecutionInfos: []int64{1, 2}, UpsertRequestCancelInfos: []*persistence.RequestCancelInfo{ {}, }, DeleteRequestCancelInfos: []int64{1, 2}, UpsertSignalInfos: []*persistence.SignalInfo{ {}, }, DeleteSignalInfos: []int64{1, 2}, UpsertSignalRequestedIDs: []string{"a", "b"}, DeleteSignalRequestedIDs: []string{"c", "d"}, ClearBufferedEvents: true, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, mockTaskSerializer *serialization.MockTaskSerializer) { mockSetupLockAndCheckNextEventID(mockTx, shardID, serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), "abc", serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), 9, false) mockUpdateExecution(mockTx, mockParser, false) mockApplyTasks(mockTx, mockTaskSerializer, 1, 3, 4, false) mockUpdateActivityInfos(mockTx, mockParser, 1, 2, false) mockUpdateTimerInfos(mockTx, mockParser, 1, 2, false) mockUpdateChildExecutionInfos(mockTx, mockParser, 1, 2, false) mockUpdateRequestCancelInfos(mockTx, mockParser, 1, 2, false) mockUpdateSignalInfos(mockTx, mockParser, 1, 2, false) mockUpdateSignalRequested(mockTx, mockParser, 1, 2, false) mockDeleteBufferedEvents(mockTx, shardID, serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), "abc", serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), false) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) mockTaskSerializer := serialization.NewMockTaskSerializer(ctrl) tc.mockSetup(mockTx, mockParser, mockTaskSerializer) err := applyWorkflowMutationTx(context.Background(), mockTx, shardID, tc.workflow, mockParser, mockTaskSerializer) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestApplyWorkflowSnapshotTxAsReset(t *testing.T) { shardID := 1 testCases := []struct { name string workflow *persistence.InternalWorkflowSnapshot mockSetup func(*sqlplugin.MockTx, *serialization.MockParser, *serialization.MockTaskSerializer) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", workflow: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "8be8a310-7d20-483e-a5d2-48659dc47602", WorkflowID: "abc", RunID: "8be8a310-7d20-483e-a5d2-48659dc47603", }, Condition: 9, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: []persistence.Task{ &persistence.ActivityTask{}, }, persistence.HistoryTaskCategoryTimer: []persistence.Task{ &persistence.DecisionTimeoutTask{}, &persistence.DecisionTimeoutTask{}, &persistence.DecisionTimeoutTask{}, }, persistence.HistoryTaskCategoryReplication: []persistence.Task{ &persistence.HistoryReplicationTask{}, &persistence.HistoryReplicationTask{}, &persistence.HistoryReplicationTask{}, &persistence.HistoryReplicationTask{}, }, }, ActivityInfos: []*persistence.InternalActivityInfo{ {}, }, TimerInfos: []*persistence.TimerInfo{ {}, }, ChildExecutionInfos: []*persistence.InternalChildExecutionInfo{ {}, }, RequestCancelInfos: []*persistence.RequestCancelInfo{ {}, }, SignalInfos: []*persistence.SignalInfo{ {}, }, SignalRequestedIDs: []string{"a", "b"}, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, mockTaskSerializer *serialization.MockTaskSerializer) { domainID := serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602") workflowID := "abc" runID := serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603") mockSetupLockAndCheckNextEventID(mockTx, shardID, domainID, workflowID, runID, 9, false) mockUpdateExecution(mockTx, mockParser, false) mockApplyTasks(mockTx, mockTaskSerializer, 1, 3, 4, false) mockDeleteActivityInfoMap(mockTx, shardID, domainID, workflowID, runID, false) mockUpdateActivityInfos(mockTx, mockParser, 1, 0, false) mockDeleteTimerInfoMap(mockTx, shardID, domainID, workflowID, runID, false) mockUpdateTimerInfos(mockTx, mockParser, 1, 0, false) mockDeleteChildExecutionInfoMap(mockTx, shardID, domainID, workflowID, runID, false) mockUpdateChildExecutionInfos(mockTx, mockParser, 1, 0, false) mockDeleteRequestCancelInfoMap(mockTx, shardID, domainID, workflowID, runID, false) mockUpdateRequestCancelInfos(mockTx, mockParser, 1, 0, false) mockDeleteSignalInfoMap(mockTx, shardID, domainID, workflowID, runID, false) mockUpdateSignalInfos(mockTx, mockParser, 1, 0, false) mockDeleteSignalRequestedSet(mockTx, shardID, domainID, workflowID, runID, false) mockUpdateSignalRequested(mockTx, mockParser, 1, 0, false) mockDeleteBufferedEvents(mockTx, shardID, domainID, workflowID, runID, false) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) mockTaskSerializer := serialization.NewMockTaskSerializer(ctrl) tc.mockSetup(mockTx, mockParser, mockTaskSerializer) err := applyWorkflowSnapshotTxAsReset(context.Background(), mockTx, shardID, tc.workflow, mockParser, mockTaskSerializer) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestApplyWorkflowSnapshotTxAsNew(t *testing.T) { shardID := 1 testCases := []struct { name string workflow *persistence.InternalWorkflowSnapshot mockSetup func(*sqlplugin.MockTx, *serialization.MockParser, *serialization.MockTaskSerializer) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", workflow: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "8be8a310-7d20-483e-a5d2-48659dc47602", WorkflowID: "abc", RunID: "8be8a310-7d20-483e-a5d2-48659dc47603", }, Condition: 9, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: []persistence.Task{ &persistence.ActivityTask{}, }, persistence.HistoryTaskCategoryTimer: []persistence.Task{ &persistence.DecisionTimeoutTask{}, &persistence.DecisionTimeoutTask{}, &persistence.DecisionTimeoutTask{}, }, persistence.HistoryTaskCategoryReplication: []persistence.Task{ &persistence.HistoryReplicationTask{}, &persistence.HistoryReplicationTask{}, &persistence.HistoryReplicationTask{}, &persistence.HistoryReplicationTask{}, }, }, ActivityInfos: []*persistence.InternalActivityInfo{ {}, }, TimerInfos: []*persistence.TimerInfo{ {}, }, ChildExecutionInfos: []*persistence.InternalChildExecutionInfo{ {}, }, RequestCancelInfos: []*persistence.RequestCancelInfo{ {}, }, SignalInfos: []*persistence.SignalInfo{ {}, }, SignalRequestedIDs: []string{"a", "b"}, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser, mockTaskSerializer *serialization.MockTaskSerializer) { mockCreateExecution(mockTx, mockParser, false) mockApplyTasks(mockTx, mockTaskSerializer, 1, 3, 4, false) mockUpdateActivityInfos(mockTx, mockParser, 1, 0, false) mockUpdateTimerInfos(mockTx, mockParser, 1, 0, false) mockUpdateChildExecutionInfos(mockTx, mockParser, 1, 0, false) mockUpdateRequestCancelInfos(mockTx, mockParser, 1, 0, false) mockUpdateSignalInfos(mockTx, mockParser, 1, 0, false) mockUpdateSignalRequested(mockTx, mockParser, 1, 0, false) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) mockTaskSerializer := serialization.NewMockTaskSerializer(ctrl) tc.mockSetup(mockTx, mockParser, mockTaskSerializer) err := applyWorkflowSnapshotTxAsNew(context.Background(), mockTx, shardID, tc.workflow, mockParser, mockTaskSerializer) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestLockAndCheckNextEventID(t *testing.T) { shardID := 1 domainID := serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602") workflowID := "abc" runID := serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603") testCases := []struct { name string condition int64 mockSetup func(*sqlplugin.MockTx) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", condition: 10, mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().WriteLockExecutions(gomock.Any(), &sqlplugin.ExecutionsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), }).Return(10, nil) }, wantErr: false, }, { name: "Error case - entity not exists", condition: 10, mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().WriteLockExecutions(gomock.Any(), &sqlplugin.ExecutionsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), }).Return(0, sql.ErrNoRows) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *types.EntityNotExistsError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be EntityNotExistsError") }, }, { name: "Error case - condition failed", condition: 10, mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().WriteLockExecutions(gomock.Any(), &sqlplugin.ExecutionsFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), }).Return(11, nil) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *persistence.ConditionFailedError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be ConditionFailedError") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) tc.mockSetup(mockTx) err := lockAndCheckNextEventID(context.Background(), mockTx, shardID, domainID, workflowID, runID, tc.condition) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestCreateExecution(t *testing.T) { shardID := 1 testCases := []struct { name string workflow *persistence.InternalWorkflowSnapshot mockSetup func(*sqlplugin.MockTx, *serialization.MockParser) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", workflow: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "8be8a310-7d20-483e-a5d2-48659dc47602", WorkflowID: "abc", RunID: "8be8a310-7d20-483e-a5d2-48659dc47603", NextEventID: 9, }, VersionHistories: &persistence.DataBlob{}, StartVersion: 1, LastWriteVersion: 2, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().WorkflowExecutionInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`workflow`), Encoding: constants.EncodingType("workflow"), }, nil) mockTx.EXPECT().InsertIntoExecutions(gomock.Any(), &sqlplugin.ExecutionsRow{ ShardID: shardID, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), NextEventID: 9, LastWriteVersion: 2, Data: []byte(`workflow`), DataEncoding: "workflow", }).Return(&sqlResult{rowsAffected: 1}, nil) }, wantErr: false, }, { name: "Error case - already started", workflow: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "8be8a310-7d20-483e-a5d2-48659dc47602", WorkflowID: "abc", RunID: "8be8a310-7d20-483e-a5d2-48659dc47603", NextEventID: 9, }, VersionHistories: &persistence.DataBlob{}, StartVersion: 1, LastWriteVersion: 2, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().WorkflowExecutionInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`workflow`), Encoding: constants.EncodingType("workflow"), }, nil) err := errors.New("some error") mockTx.EXPECT().InsertIntoExecutions(gomock.Any(), &sqlplugin.ExecutionsRow{ ShardID: shardID, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), NextEventID: 9, LastWriteVersion: 2, Data: []byte(`workflow`), DataEncoding: "workflow", }).Return(nil, err) mockTx.EXPECT().IsDupEntryError(err).Return(true) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *persistence.WorkflowExecutionAlreadyStartedError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be WorkflowExecutionAlreadyStartedError") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) tc.mockSetup(mockTx, mockParser) err := createExecution(context.Background(), mockTx, tc.workflow.ExecutionInfo, tc.workflow.VersionHistories, tc.workflow.ChecksumData, tc.workflow.StartVersion, tc.workflow.LastWriteVersion, shardID, mockParser) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestUpdateExecution(t *testing.T) { shardID := 1 testCases := []struct { name string workflow *persistence.InternalWorkflowSnapshot mockSetup func(*sqlplugin.MockTx, *serialization.MockParser) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", workflow: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "8be8a310-7d20-483e-a5d2-48659dc47602", WorkflowID: "abc", RunID: "8be8a310-7d20-483e-a5d2-48659dc47603", NextEventID: 9, }, VersionHistories: &persistence.DataBlob{}, StartVersion: 1, LastWriteVersion: 2, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().WorkflowExecutionInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`workflow`), Encoding: constants.EncodingType("workflow"), }, nil) mockTx.EXPECT().UpdateExecutions(gomock.Any(), &sqlplugin.ExecutionsRow{ ShardID: shardID, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), NextEventID: 9, LastWriteVersion: 2, Data: []byte(`workflow`), DataEncoding: "workflow", }).Return(&sqlResult{rowsAffected: 1}, nil) }, wantErr: false, }, { name: "Error case - already started", workflow: &persistence.InternalWorkflowSnapshot{ ExecutionInfo: &persistence.InternalWorkflowExecutionInfo{ DomainID: "8be8a310-7d20-483e-a5d2-48659dc47602", WorkflowID: "abc", RunID: "8be8a310-7d20-483e-a5d2-48659dc47603", NextEventID: 9, }, VersionHistories: &persistence.DataBlob{}, StartVersion: 1, LastWriteVersion: 2, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().WorkflowExecutionInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`workflow`), Encoding: constants.EncodingType("workflow"), }, nil) err := errors.New("some error") mockTx.EXPECT().UpdateExecutions(gomock.Any(), &sqlplugin.ExecutionsRow{ ShardID: shardID, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), NextEventID: 9, LastWriteVersion: 2, Data: []byte(`workflow`), DataEncoding: "workflow", }).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) tc.mockSetup(mockTx, mockParser) err := updateExecution(context.Background(), mockTx, tc.workflow.ExecutionInfo, tc.workflow.VersionHistories, tc.workflow.ChecksumData, tc.workflow.StartVersion, tc.workflow.LastWriteVersion, shardID, mockParser) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestCreateTransferTasks(t *testing.T) { shardID := 1 testCases := []struct { name string tasks []persistence.Task mockSetup func(*sqlplugin.MockTx, *serialization.MockTaskSerializer) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", tasks: []persistence.Task{ &persistence.ActivityTask{ TaskData: persistence.TaskData{ Version: 1, VisibilityTimestamp: time.Unix(1, 1), TaskID: 1, }, TargetDomainID: "8be8a310-7d20-483e-a5d2-48659dc47609", TaskList: "tl", ScheduleID: 111, }, &persistence.DecisionTask{ TaskData: persistence.TaskData{ Version: 2, VisibilityTimestamp: time.Unix(2, 2), TaskID: 2, }, TargetDomainID: "7be8a310-7d20-483e-a5d2-48659dc47609", TaskList: "tl2", ScheduleID: 222, }, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`1`), Encoding: constants.EncodingType("1"), }, nil) mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`2`), Encoding: constants.EncodingType("2"), }, nil) mockTx.EXPECT().InsertIntoTransferTasks(gomock.Any(), []sqlplugin.TransferTasksRow{ { ShardID: shardID, TaskID: 1, Data: []byte(`1`), DataEncoding: "1", }, { ShardID: shardID, TaskID: 2, Data: []byte(`2`), DataEncoding: "2", }, }).Return(&sqlResult{rowsAffected: 2}, nil) }, wantErr: false, }, { name: "Error case", tasks: []persistence.Task{ &persistence.ActivityTask{ TaskData: persistence.TaskData{ Version: 1, VisibilityTimestamp: time.Unix(1, 1), TaskID: 1, }, TargetDomainID: "8be8a310-7d20-483e-a5d2-48659dc47609", TaskList: "tl", ScheduleID: 111, }, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTransfer, gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`1`), Encoding: constants.EncodingType("1"), }, nil) err := errors.New("some error") mockTx.EXPECT().InsertIntoTransferTasks(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) mockTaskSerializer := serialization.NewMockTaskSerializer(ctrl) tc.mockSetup(mockTx, mockTaskSerializer) err := createTransferTasks(context.Background(), mockTx, tc.tasks, shardID, mockTaskSerializer) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestCreateTimerTasks(t *testing.T) { shardID := 1 testCases := []struct { name string tasks []persistence.Task mockSetup func(*sqlplugin.MockTx, *serialization.MockTaskSerializer) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", tasks: []persistence.Task{ &persistence.DecisionTimeoutTask{ TaskData: persistence.TaskData{ Version: 1, VisibilityTimestamp: time.Unix(1, 1), TaskID: 1, }, EventID: 1, ScheduleAttempt: 1, TimeoutType: 1, }, &persistence.ActivityTimeoutTask{ TaskData: persistence.TaskData{ Version: 2, VisibilityTimestamp: time.Unix(2, 2), TaskID: 2, }, EventID: 2, Attempt: 2, TimeoutType: 2, }, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTimer, gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`1`), Encoding: constants.EncodingType("1"), }, nil) mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTimer, gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`2`), Encoding: constants.EncodingType("2"), }, nil) mockTx.EXPECT().InsertIntoTimerTasks(gomock.Any(), []sqlplugin.TimerTasksRow{ { ShardID: shardID, TaskID: 1, VisibilityTimestamp: time.Unix(1, 1), Data: []byte(`1`), DataEncoding: "1", }, { ShardID: shardID, TaskID: 2, VisibilityTimestamp: time.Unix(2, 2), Data: []byte(`2`), DataEncoding: "2", }, }).Return(&sqlResult{rowsAffected: 2}, nil) }, wantErr: false, }, { name: "Error case", tasks: []persistence.Task{ &persistence.DecisionTimeoutTask{ TaskData: persistence.TaskData{ Version: 1, VisibilityTimestamp: time.Unix(1, 1), TaskID: 1, }, EventID: 1, ScheduleAttempt: 1, TimeoutType: 1, }, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryTimer, gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`1`), Encoding: constants.EncodingType("1"), }, nil) err := errors.New("some error") mockTx.EXPECT().InsertIntoTimerTasks(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) mockTaskSerializer := serialization.NewMockTaskSerializer(ctrl) tc.mockSetup(mockTx, mockTaskSerializer) err := createTimerTasks(context.Background(), mockTx, tc.tasks, shardID, mockTaskSerializer) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestCreateReplicationTasks(t *testing.T) { shardID := 1 testCases := []struct { name string tasks []persistence.Task mockSetup func(*sqlplugin.MockTx, *serialization.MockTaskSerializer) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", tasks: []persistence.Task{ &persistence.HistoryReplicationTask{ TaskData: persistence.TaskData{ TaskID: 1, VisibilityTimestamp: time.Unix(1, 1), Version: 1, }, FirstEventID: 1, NextEventID: 2, BranchToken: []byte{1}, NewRunBranchToken: []byte{2}, }, &persistence.SyncActivityTask{ TaskData: persistence.TaskData{ TaskID: 2, VisibilityTimestamp: time.Unix(2, 2), Version: 2, }, ScheduledID: 2, }, }, mockSetup: func(mockTx *sqlplugin.MockTx, mockTaskSerializer *serialization.MockTaskSerializer) { mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryReplication, gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`1`), Encoding: constants.EncodingType("1"), }, nil) mockTaskSerializer.EXPECT().SerializeTask(persistence.HistoryTaskCategoryReplication, gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`2`), Encoding: constants.EncodingType("2"), }, nil) mockTx.EXPECT().InsertIntoReplicationTasks(gomock.Any(), []sqlplugin.ReplicationTasksRow{ { ShardID: shardID, TaskID: 1, Data: []byte(`1`), DataEncoding: "1", }, { ShardID: shardID, TaskID: 2, Data: []byte(`2`), DataEncoding: "2", }, }).Return(&sqlResult{rowsAffected: 2}, nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) mockTaskSerializer := serialization.NewMockTaskSerializer(ctrl) tc.mockSetup(mockTx, mockTaskSerializer) err := createReplicationTasks(context.Background(), mockTx, tc.tasks, shardID, mockTaskSerializer) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestLockCurrentExecutionIfExists(t *testing.T) { testCases := []struct { name string mockSetup func(*sqlplugin.MockTx) wantErr bool want *sqlplugin.CurrentExecutionsRow }{ { name: "Success case", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().LockCurrentExecutionsJoinExecutions(gomock.Any(), gomock.Any()).Return([]sqlplugin.CurrentExecutionsRow{ { ShardID: 1, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), }, }, nil) }, wantErr: false, want: &sqlplugin.CurrentExecutionsRow{ ShardID: 1, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), }, }, { name: "Error case", mockSetup: func(mockTx *sqlplugin.MockTx) { err := errors.New("some error") mockTx.EXPECT().LockCurrentExecutionsJoinExecutions(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Empty result", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().LockCurrentExecutionsJoinExecutions(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) }, wantErr: false, }, { name: "Multiple rows", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().LockCurrentExecutionsJoinExecutions(gomock.Any(), gomock.Any()).Return([]sqlplugin.CurrentExecutionsRow{ { ShardID: 1, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), }, { ShardID: 1, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "def", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47604"), }, }, nil) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) tc.mockSetup(mockTx) got, err := lockCurrentExecutionIfExists(context.Background(), mockTx, 1, serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), "abc") if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Expected result to match") } }) } } func TestCreateOrUpdateCurrentExecution(t *testing.T) { testCases := []struct { name string createMode persistence.CreateWorkflowMode mockSetup func(*sqlplugin.MockTx) wantErr bool }{ { name: "Brand new workflow - success", createMode: persistence.CreateWorkflowModeBrandNew, mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().InsertIntoCurrentExecutions(gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: false, }, { name: "Brand new workflow - error", createMode: persistence.CreateWorkflowModeBrandNew, mockSetup: func(mockTx *sqlplugin.MockTx) { err := errors.New("some error") mockTx.EXPECT().InsertIntoCurrentExecutions(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Update current execution - success", createMode: persistence.CreateWorkflowModeWorkflowIDReuse, mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().UpdateCurrentExecutions(gomock.Any(), gomock.Any()).Return(&sqlResult{rowsAffected: 1}, nil) }, wantErr: false, }, { name: "Update current execution - error", createMode: persistence.CreateWorkflowModeWorkflowIDReuse, mockSetup: func(mockTx *sqlplugin.MockTx) { err := errors.New("some error") mockTx.EXPECT().UpdateCurrentExecutions(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Update current execution - no rows affected", createMode: persistence.CreateWorkflowModeContinueAsNew, mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().UpdateCurrentExecutions(gomock.Any(), gomock.Any()).Return(&sqlResult{rowsAffected: 0}, nil) }, wantErr: true, }, { name: "Zombie workflow - success", createMode: persistence.CreateWorkflowModeZombie, mockSetup: func(mockTx *sqlplugin.MockTx) {}, wantErr: false, }, { name: "Unknown create mode", createMode: persistence.CreateWorkflowMode(100), mockSetup: func(mockTx *sqlplugin.MockTx) {}, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) tc.mockSetup(mockTx) err := createOrUpdateCurrentExecution( context.Background(), mockTx, tc.createMode, 1, serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), "abc", serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), 0, 1, "request-id", 11, 12, ) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestAssertNotCurrentExecution(t *testing.T) { testCases := []struct { name string mockSetup func(*sqlplugin.MockTx) wantErr bool }{ { name: "Success case", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().LockCurrentExecutions(gomock.Any(), gomock.Any()).Return(&sqlplugin.CurrentExecutionsRow{ ShardID: 1, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), }, nil) }, wantErr: false, }, { name: "Error case", mockSetup: func(mockTx *sqlplugin.MockTx) { err := errors.New("some error") mockTx.EXPECT().LockCurrentExecutions(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Success case - No rows", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().LockCurrentExecutions(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) }, wantErr: false, }, { name: "Error case - run ID match", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().LockCurrentExecutions(gomock.Any(), gomock.Any()).Return(&sqlplugin.CurrentExecutionsRow{ ShardID: 1, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), }, nil) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) tc.mockSetup(mockTx) err := assertNotCurrentExecution( context.Background(), mockTx, 1, serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), "abc", serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), ) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestAssertRunIDAndUpdateCurrentExecution(t *testing.T) { testCases := []struct { name string mockSetup func(*sqlplugin.MockTx) wantErr bool }{ { name: "Success case", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().LockCurrentExecutions(gomock.Any(), gomock.Any()).Return(&sqlplugin.CurrentExecutionsRow{ ShardID: 1, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47604"), }, nil) mockTx.EXPECT().UpdateCurrentExecutions(gomock.Any(), gomock.Any()).Return(&sqlResult{rowsAffected: 1}, nil) }, wantErr: false, }, { name: "Error case - update current execution", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().LockCurrentExecutions(gomock.Any(), gomock.Any()).Return(&sqlplugin.CurrentExecutionsRow{ ShardID: 1, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47604"), }, nil) err := errors.New("some error") mockTx.EXPECT().UpdateCurrentExecutions(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - run ID mismatch", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().LockCurrentExecutions(gomock.Any(), gomock.Any()).Return(&sqlplugin.CurrentExecutionsRow{ ShardID: 1, DomainID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), WorkflowID: "abc", RunID: serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), }, nil) }, wantErr: true, }, { name: "Error case - unknown error", mockSetup: func(mockTx *sqlplugin.MockTx) { err := errors.New("some error") mockTx.EXPECT().LockCurrentExecutions(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) tc.mockSetup(mockTx) err := assertRunIDAndUpdateCurrentExecution( context.Background(), mockTx, 1, serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47602"), "abc", serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47603"), serialization.MustParseUUID("8be8a310-7d20-483e-a5d2-48659dc47604"), "request-id", 1, 11, 12, 13, ) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } ================================================ FILE: common/persistence/sql/sql_history_store.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "fmt" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" persistenceutils "github.com/uber/cadence/common/persistence/persistence-utils" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) const ( _defaultHistoryNodeDeleteBatch = 1000 ) type sqlHistoryStore struct { sqlStore } type historyTreePageToken struct { ShardID int TreeID serialization.UUID BranchID serialization.UUID } // NewHistoryV2Persistence creates an instance of HistoryManager func NewHistoryV2Persistence( db sqlplugin.DB, logger log.Logger, parser serialization.Parser, dc *persistence.DynamicConfiguration, ) (persistence.HistoryStore, error) { return &sqlHistoryStore{ sqlStore: sqlStore{ db: db, logger: logger, parser: parser, dc: dc, }, }, nil } // AppendHistoryNodes add(or override) a node to a history branch func (m *sqlHistoryStore) AppendHistoryNodes( ctx context.Context, request *persistence.InternalAppendHistoryNodesRequest, ) error { branchInfo := request.BranchInfo beginNodeID := persistenceutils.GetBeginNodeID(branchInfo) if request.NodeID < beginNodeID { return &persistence.InvalidPersistenceRequestError{ Msg: "cannot append to ancestors' nodes", } } nodeRow := &sqlplugin.HistoryNodeRow{ TreeID: serialization.MustParseUUID(branchInfo.TreeID), BranchID: serialization.MustParseUUID(branchInfo.BranchID), NodeID: request.NodeID, TxnID: &request.TransactionID, Data: request.Events.Data, DataEncoding: string(request.Events.Encoding), ShardID: request.ShardID, } if request.IsNewBranch { var ancestors []*types.HistoryBranchRange ancestors = append(ancestors, branchInfo.Ancestors...) treeInfo := &serialization.HistoryTreeInfo{ Ancestors: ancestors, Info: request.Info, CreatedTimestamp: request.CurrentTimeStamp, } blob, err := m.parser.HistoryTreeInfoToBlob(treeInfo) if err != nil { return err } treeRow := &sqlplugin.HistoryTreeRow{ ShardID: request.ShardID, TreeID: serialization.MustParseUUID(branchInfo.TreeID), BranchID: serialization.MustParseUUID(branchInfo.BranchID), Data: blob.Data, DataEncoding: string(blob.Encoding), } treeUUID := serialization.MustParseUUID(branchInfo.TreeID) dbShardID := sqlplugin.GetDBShardIDFromTreeID(treeUUID, m.db.GetTotalNumDBShards()) return m.txExecute(ctx, dbShardID, "AppendHistoryNodes", func(tx sqlplugin.Tx) error { result, err := tx.InsertIntoHistoryNode(ctx, nodeRow) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return err } if rowsAffected != 1 { return fmt.Errorf("expected 1 row to be affected for node table, got %v", rowsAffected) } result, err = tx.InsertIntoHistoryTree(ctx, treeRow) if err != nil { return err } rowsAffected, err = result.RowsAffected() if err != nil { return err } if rowsAffected != 1 { return fmt.Errorf("expected 1 row to be affected for tree table, got %v", rowsAffected) } return nil }) } _, err := m.db.InsertIntoHistoryNode(ctx, nodeRow) if err != nil { if m.db.IsDupEntryError(err) { return &persistence.ConditionFailedError{Msg: fmt.Sprintf("AppendHistoryNodes: row already exist: %v", err)} } return convertCommonErrors(m.db, "AppendHistoryEvents", "", err) } return nil } // ReadHistoryBranch returns history node data for a branch func (m *sqlHistoryStore) ReadHistoryBranch( ctx context.Context, request *persistence.InternalReadHistoryBranchRequest, ) (*persistence.InternalReadHistoryBranchResponse, error) { minNodeID := request.MinNodeID maxNodeID := request.MaxNodeID lastNodeID := request.LastNodeID lastTxnID := request.LastTransactionID if request.NextPageToken != nil && len(request.NextPageToken) > 0 { var lastNodeID int64 var err error // TODO the inner pagination token can be replaced by a dummy token // since lastNodeID & lastTxnID are both provided if lastNodeID, err = deserializePageToken(request.NextPageToken); err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("invalid next page token %v", request.NextPageToken)} } minNodeID = lastNodeID + 1 } filter := &sqlplugin.HistoryNodeFilter{ TreeID: serialization.MustParseUUID(request.TreeID), BranchID: serialization.MustParseUUID(request.BranchID), MinNodeID: &minNodeID, MaxNodeID: &maxNodeID, PageSize: request.PageSize, ShardID: request.ShardID, } rows, err := m.db.SelectFromHistoryNode(ctx, filter) if err == sql.ErrNoRows || (err == nil && len(rows) == 0) { return &persistence.InternalReadHistoryBranchResponse{}, nil } if err != nil { return nil, convertCommonErrors(m.db, "ReadHistoryBranch", "", err) } history := make([]*persistence.DataBlob, 0, int(request.PageSize)) eventBlob := &persistence.DataBlob{} for _, row := range rows { eventBlob.Data = row.Data eventBlob.Encoding = constants.EncodingType(row.DataEncoding) if *row.TxnID < lastTxnID { // assuming that business logic layer is correct and transaction ID only increase // thus, valid event batch will come with increasing transaction ID // event batches with smaller node ID // -> should not be possible since records are already sorted // event batches with same node ID // -> batch with higher transaction ID is valid // event batches with larger node ID // -> batch with lower transaction ID is invalid (happens before) // -> batch with higher transaction ID is valid if row.NodeID < lastNodeID { return nil, &types.InternalDataInconsistencyError{ Message: "corrupted data, nodeID cannot decrease", } } else if row.NodeID > lastNodeID { // update lastNodeID so that our pagination can make progress in the corner case that // the page are all rows with smaller txnID // because next page we always have minNodeID = lastNodeID+1 lastNodeID = row.NodeID } continue } switch { case row.NodeID < lastNodeID: return nil, &types.InternalDataInconsistencyError{ Message: "corrupted data, nodeID cannot decrease", } case row.NodeID == lastNodeID: return nil, &types.InternalDataInconsistencyError{ Message: "corrupted data, same nodeID must have smaller txnID", } default: // row.NodeID > lastNodeID: // NOTE: when row.nodeID > lastNodeID, we expect the one with largest txnID comes first lastTxnID = *row.TxnID lastNodeID = row.NodeID history = append(history, eventBlob) eventBlob = &persistence.DataBlob{} } } var pagingToken []byte if len(rows) >= request.PageSize { pagingToken = serializePageToken(lastNodeID) } return &persistence.InternalReadHistoryBranchResponse{ History: history, NextPageToken: pagingToken, LastNodeID: lastNodeID, LastTransactionID: lastTxnID, }, nil } // ForkHistoryBranch forks a new branch from an existing branch // Note that application must provide a void forking nodeID, it must be a valid nodeID in that branch. // A valid forking nodeID can be an ancestor from the existing branch. // For example, we have branch B1 with three nodes(1[1,2], 3[3,4,5] and 6[6,7,8]. 1, 3 and 6 are nodeIDs (first eventID of the batch). // So B1 looks like this: // // 1[1,2] // / // 3[3,4,5] // / // 6[6,7,8] // // Assuming we have branch B2 which contains one ancestor B1 stopping at 6 (exclusive). So B2 inherit nodeID 1 and 3 from B1, and have its own nodeID 6 and 8. // Branch B2 looks like this: // // 1[1,2] // / // 3[3,4,5] // \ // 6[6,7] // \ // 8[8] // // Now we want to fork a new branch B3 from B2. // The only valid forking nodeIDs are 3,6 or 8. // 1 is not valid because we can't fork from first node. // 2/4/5 is NOT valid either because they are inside a batch. // // Case #1: If we fork from nodeID 6, then B3 will have an ancestor B1 which stops at 6(exclusive). // As we append a batch of events[6,7,8,9] to B3, it will look like : // // 1[1,2] // / // 3[3,4,5] // \ // 6[6,7,8,9] // // Case #2: If we fork from node 8, then B3 will have two ancestors: B1 stops at 6(exclusive) and ancestor B2 stops at 8(exclusive) // As we append a batch of events[8,9] to B3, it will look like: // // 1[1,2] // / // 3[3,4,5] // / // 6[6,7] // \ // 8[8,9] func (m *sqlHistoryStore) ForkHistoryBranch( ctx context.Context, request *persistence.InternalForkHistoryBranchRequest, ) (*persistence.InternalForkHistoryBranchResponse, error) { forkB := request.ForkBranchInfo treeID := forkB.TreeID newAncestors := make([]*types.HistoryBranchRange, 0, len(forkB.Ancestors)+1) beginNodeID := persistenceutils.GetBeginNodeID(forkB) if beginNodeID >= request.ForkNodeID { // this is the case that new branch's ancestors doesn't include the forking branch for _, br := range forkB.Ancestors { if br.EndNodeID >= request.ForkNodeID { newAncestors = append(newAncestors, &types.HistoryBranchRange{ BranchID: br.BranchID, BeginNodeID: br.BeginNodeID, EndNodeID: request.ForkNodeID, }) break } else { newAncestors = append(newAncestors, br) } } } else { // this is the case the new branch will inherit all ancestors from forking branch newAncestors = forkB.Ancestors newAncestors = append(newAncestors, &types.HistoryBranchRange{ BranchID: forkB.BranchID, BeginNodeID: beginNodeID, EndNodeID: request.ForkNodeID, }) } resp := &persistence.InternalForkHistoryBranchResponse{ NewBranchInfo: types.HistoryBranch{ TreeID: treeID, BranchID: request.NewBranchID, Ancestors: newAncestors, }} treeInfo := &serialization.HistoryTreeInfo{ Ancestors: newAncestors, Info: request.Info, CreatedTimestamp: request.CurrentTimeStamp, } blob, err := m.parser.HistoryTreeInfoToBlob(treeInfo) if err != nil { return nil, err } row := &sqlplugin.HistoryTreeRow{ ShardID: request.ShardID, TreeID: serialization.MustParseUUID(treeID), BranchID: serialization.MustParseUUID(request.NewBranchID), Data: blob.Data, DataEncoding: string(blob.Encoding), } result, err := m.db.InsertIntoHistoryTree(ctx, row) if err != nil { return nil, convertCommonErrors(m.db, "ForkHistoryBranch", "", err) } rowsAffected, err := result.RowsAffected() if err != nil { return nil, err } if rowsAffected != 1 { return nil, types.InternalServiceError{Message: fmt.Sprintf("expected 1 row to be affected for tree table, got %v", rowsAffected)} } return resp, nil } // DeleteHistoryBranch removes a branch func (m *sqlHistoryStore) DeleteHistoryBranch( ctx context.Context, request *persistence.InternalDeleteHistoryBranchRequest, ) error { branch := request.BranchInfo treeID := branch.TreeID brsToDelete := branch.Ancestors beginNodeID := persistenceutils.GetBeginNodeID(branch) brsToDelete = append(brsToDelete, &types.HistoryBranchRange{ BranchID: branch.BranchID, BeginNodeID: beginNodeID, }) rsp, err := m.GetHistoryTree(ctx, &persistence.InternalGetHistoryTreeRequest{ TreeID: treeID, ShardID: common.IntPtr(request.ShardID), }) if err != nil { return err } // validBRsMaxEndNode is to for each branch range that is being used, we want to know what is the max nodeID referred by other valid branch validBRsMaxEndNode := persistenceutils.GetBranchesMaxReferredNodeIDs(rsp.Branches) treeUUID := serialization.MustParseUUID(treeID) dbShardID := sqlplugin.GetDBShardIDFromTreeID(treeUUID, m.db.GetTotalNumDBShards()) return m.txExecute(ctx, dbShardID, "DeleteHistoryBranch", func(tx sqlplugin.Tx) error { branchID := serialization.MustParseUUID(branch.BranchID) treeFilter := &sqlplugin.HistoryTreeFilter{ TreeID: treeUUID, BranchID: &branchID, ShardID: request.ShardID, } _, err = tx.DeleteFromHistoryTree(ctx, treeFilter) if err != nil { return err } done := false // for each branch range to delete, we iterate from bottom to up, and delete up to the point according to validBRsEndNode for i := len(brsToDelete) - 1; i >= 0; i-- { br := brsToDelete[i] maxReferredEndNodeID, ok := validBRsMaxEndNode[br.BranchID] batchSize := _defaultHistoryNodeDeleteBatch if m.dc != nil { batchSize = m.dc.HistoryNodeDeleteBatchSize() } nodeFilter := &sqlplugin.HistoryNodeFilter{ TreeID: serialization.MustParseUUID(treeID), BranchID: serialization.MustParseUUID(br.BranchID), ShardID: request.ShardID, PageSize: batchSize, } if ok { // we can only delete from the maxEndNode and stop here nodeFilter.MinNodeID = &maxReferredEndNodeID done = true } else { // No any branch is using this range, we can delete all of it nodeFilter.MinNodeID = &br.BeginNodeID } for { result, err := tx.DeleteFromHistoryNode(ctx, nodeFilter) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return err } if batchSize <= 0 || rowsAffected < int64(batchSize) || rowsAffected == persistence.UnknownNumRowsAffected || rowsAffected > int64(batchSize) { break } } if done { break } } return nil }) } // TODO: Limit the underlying query to a specific shard at a time. See https://github.com/uber/cadence/issues/4064 func (m *sqlHistoryStore) GetAllHistoryTreeBranches( ctx context.Context, request *persistence.GetAllHistoryTreeBranchesRequest, ) (*persistence.GetAllHistoryTreeBranchesResponse, error) { page := historyTreePageToken{} if request.NextPageToken != nil { if err := gobDeserialize(request.NextPageToken, &page); err != nil { return nil, fmt.Errorf("unable to decode next page token") } } else { page = historyTreePageToken{ ShardID: 0, // First page starting from ShardID 0, and increase if finish reading current shard TreeID: serialization.UUID{}, BranchID: serialization.UUID{}, } } filter := sqlplugin.HistoryTreeFilter{ ShardID: page.ShardID, TreeID: page.TreeID, BranchID: &page.BranchID, PageSize: &request.PageSize, } rows, err := m.db.GetAllHistoryTreeBranches(ctx, &filter) if err == sql.ErrNoRows || (err == nil && len(rows) == 0) { return &persistence.GetAllHistoryTreeBranchesResponse{}, nil } if err != nil { return nil, convertCommonErrors(m.db, "GetAllHistoryTreeBranches", "", err) } resp := &persistence.GetAllHistoryTreeBranchesResponse{} resp.Branches = make([]persistence.HistoryBranchDetail, len(rows)) for i, row := range rows { treeInfo, err := m.parser.HistoryTreeInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } resp.Branches[i].TreeID = row.TreeID.String() resp.Branches[i].BranchID = row.BranchID.String() resp.Branches[i].ForkTime = treeInfo.GetCreatedTimestamp() resp.Branches[i].Info = treeInfo.GetInfo() } if len(rows) >= request.PageSize { // there could be more lastRow := &rows[request.PageSize-1] resp.NextPageToken, err = gobSerialize(&historyTreePageToken{ ShardID: lastRow.ShardID, TreeID: lastRow.TreeID, BranchID: lastRow.BranchID, }) if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("error serializing nextPageToken:%v", err)} } } // TODO: this is broken for multi-sharding: the shardID should increase if there are less rows than request pageSize, // until loop over all shards return resp, nil } // GetHistoryTree returns all branch information of a tree func (m *sqlHistoryStore) GetHistoryTree( ctx context.Context, request *persistence.InternalGetHistoryTreeRequest, ) (*persistence.InternalGetHistoryTreeResponse, error) { treeID := serialization.MustParseUUID(request.TreeID) branches := make([]*types.HistoryBranch, 0) treeFilter := &sqlplugin.HistoryTreeFilter{ TreeID: treeID, ShardID: *request.ShardID, } rows, err := m.db.SelectFromHistoryTree(ctx, treeFilter) if err == sql.ErrNoRows || (err == nil && len(rows) == 0) { return &persistence.InternalGetHistoryTreeResponse{}, nil } if err != nil { return nil, convertCommonErrors(m.db, "GetHistoryTree", "", err) } for _, row := range rows { treeInfo, err := m.parser.HistoryTreeInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } br := &types.HistoryBranch{ TreeID: request.TreeID, BranchID: row.BranchID.String(), Ancestors: treeInfo.Ancestors, } branches = append(branches, br) } return &persistence.InternalGetHistoryTreeResponse{ Branches: branches, }, nil } ================================================ FILE: common/persistence/sql/sql_history_store_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) func TestGetHistoryTree(t *testing.T) { testCases := []struct { name string req *persistence.InternalGetHistoryTreeRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) want *persistence.InternalGetHistoryTreeResponse wantErr bool }{ { name: "Success case", req: &persistence.InternalGetHistoryTreeRequest{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", ShardID: common.IntPtr(1), }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, }).Return([]sqlplugin.HistoryTreeRow{ { ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), Data: []byte(`aaaa`), DataEncoding: "json", }, }, nil) mockParser.EXPECT().HistoryTreeInfoFromBlob([]byte(`aaaa`), "json").Return(&serialization.HistoryTreeInfo{ Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 3, }, }, }, nil) }, want: &persistence.InternalGetHistoryTreeResponse{ Branches: []*types.HistoryBranch{ { TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 3, }, }, }, }, }, wantErr: false, }, { name: "Success case - no record", req: &persistence.InternalGetHistoryTreeRequest{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", ShardID: common.IntPtr(1), }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, }).Return(nil, sql.ErrNoRows) }, want: &persistence.InternalGetHistoryTreeResponse{}, wantErr: false, }, { name: "Error case - database failure", req: &persistence.InternalGetHistoryTreeRequest{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", ShardID: common.IntPtr(1), }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { err := errors.New("some error") mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - decode error", req: &persistence.InternalGetHistoryTreeRequest{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", ShardID: common.IntPtr(1), }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, }).Return([]sqlplugin.HistoryTreeRow{ { ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), Data: []byte(`aaaa`), DataEncoding: "json", }, }, nil) mockParser.EXPECT().HistoryTreeInfoFromBlob([]byte(`aaaa`), "json").Return(nil, errors.New("some error")) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockParser(ctrl) store, err := NewHistoryV2Persistence(mockDB, nil, mockParser, nil) require.NoError(t, err, "Failed to create sql history store") tc.mockSetup(mockDB, mockParser) got, err := store.GetHistoryTree(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestDeleteHistoryBranch(t *testing.T) { testCases := []struct { name string req *persistence.InternalDeleteHistoryBranchRequest mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx, *serialization.MockParser) wantErr bool }{ { name: "Success case", req: &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, }).Return([]sqlplugin.HistoryTreeRow{ { ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), Data: []byte(`aaaa`), DataEncoding: "json", }, }, nil) mockParser.EXPECT().HistoryTreeInfoFromBlob([]byte(`aaaa`), "json").Return(&serialization.HistoryTreeInfo{ Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 3, }, }, }, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().DeleteFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.UUIDPtr(serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691")), ShardID: 1, }).Return(nil, nil) mockTx.EXPECT().DeleteFromHistoryNode(gomock.Any(), &sqlplugin.HistoryNodeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, PageSize: 1000, MinNodeID: common.Int64Ptr(10), }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().DeleteFromHistoryNode(gomock.Any(), &sqlplugin.HistoryNodeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("730ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, PageSize: 1000, MinNodeID: common.Int64Ptr(3), }).Return(&sqlResult{rowsAffected: 7}, nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Error case - failed to get history tree", req: &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { err := errors.New("some error") mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to delete history tree", req: &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, }).Return([]sqlplugin.HistoryTreeRow{ { ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), Data: []byte(`aaaa`), DataEncoding: "json", }, }, nil) mockParser.EXPECT().HistoryTreeInfoFromBlob([]byte(`aaaa`), "json").Return(&serialization.HistoryTreeInfo{ Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 3, }, }, }, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().DeleteFromHistoryTree(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to delete history node", req: &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), gomock.Any()).Return([]sqlplugin.HistoryTreeRow{ { ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), Data: []byte(`aaaa`), DataEncoding: "json", }, }, nil) mockParser.EXPECT().HistoryTreeInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.HistoryTreeInfo{ Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 3, }, }, }, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().DeleteFromHistoryTree(gomock.Any(), gomock.Any()).Return(nil, nil) err := errors.New("some error") mockTx.EXPECT().DeleteFromHistoryNode(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) store, err := NewHistoryV2Persistence(mockDB, nil, mockParser, nil) require.NoError(t, err, "Failed to create sql history store") tc.mockSetup(mockDB, mockTx, mockParser) err = store.DeleteHistoryBranch(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestForkHistoryBranch(t *testing.T) { now := time.Now() testCases := []struct { name string req *persistence.InternalForkHistoryBranchRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) want *persistence.InternalForkHistoryBranchResponse wantErr bool }{ { name: "Success case - case 1", req: &persistence.InternalForkHistoryBranchRequest{ ForkBranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 2, }, { BranchID: "830ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 3, EndNodeID: 5, }, }, }, ForkNodeID: 4, NewBranchID: "630ec3d3-f74b-423f-a138-3b35494fe699", Info: "test", ShardID: 1, CurrentTimeStamp: now, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockParser.EXPECT().HistoryTreeInfoToBlob(gomock.Any()).DoAndReturn(func(info *serialization.HistoryTreeInfo) (persistence.DataBlob, error) { assert.WithinDuration(t, now, info.CreatedTimestamp, time.Second) return persistence.DataBlob{}, nil }) mockDB.EXPECT().InsertIntoHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeRow{ ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe699"), }).Return(&sqlResult{rowsAffected: 1}, nil) }, want: &persistence.InternalForkHistoryBranchResponse{ NewBranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe699", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 2, }, { BranchID: "830ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 3, EndNodeID: 4, }, }, }, }, wantErr: false, }, { name: "Success case - case 2", req: &persistence.InternalForkHistoryBranchRequest{ ForkBranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 2, }, }, }, ForkNodeID: 4, NewBranchID: "630ec3d3-f74b-423f-a138-3b35494fe699", Info: "test", ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockParser.EXPECT().HistoryTreeInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil) mockDB.EXPECT().InsertIntoHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeRow{ ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe699"), }).Return(&sqlResult{rowsAffected: 1}, nil) }, want: &persistence.InternalForkHistoryBranchResponse{ NewBranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe699", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 2, }, { BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 2, EndNodeID: 4, }, }, }, }, wantErr: false, }, { name: "Error case - failed to encode blob", req: &persistence.InternalForkHistoryBranchRequest{ ForkBranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 2, }, }, }, ForkNodeID: 4, NewBranchID: "630ec3d3-f74b-423f-a138-3b35494fe699", Info: "test", ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockParser.EXPECT().HistoryTreeInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, errors.New("some error")) }, wantErr: true, }, { name: "Error case - failed to insert data", req: &persistence.InternalForkHistoryBranchRequest{ ForkBranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 2, }, }, }, ForkNodeID: 4, NewBranchID: "630ec3d3-f74b-423f-a138-3b35494fe699", Info: "test", ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockParser.EXPECT().HistoryTreeInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil) err := errors.New("some error") mockDB.EXPECT().InsertIntoHistoryTree(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockParser(ctrl) store, err := NewHistoryV2Persistence(mockDB, nil, mockParser, nil) require.NoError(t, err, "Failed to create sql history store") tc.mockSetup(mockDB, mockParser) got, err := store.ForkHistoryBranch(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestAppendHistoryNodes(t *testing.T) { now := time.Now() testCases := []struct { name string req *persistence.InternalAppendHistoryNodesRequest mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx, *serialization.MockParser) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case - new branch", req: &persistence.InternalAppendHistoryNodesRequest{ IsNewBranch: true, Info: "test", BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, NodeID: 11, Events: &persistence.DataBlob{}, TransactionID: 100, ShardID: 1, CurrentTimeStamp: now, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().HistoryTreeInfoToBlob(gomock.Any()).DoAndReturn(func(info *serialization.HistoryTreeInfo) (persistence.DataBlob, error) { assert.WithinDuration(t, now, info.CreatedTimestamp, time.Second) return persistence.DataBlob{}, nil }) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().InsertIntoHistoryNode(gomock.Any(), &sqlplugin.HistoryNodeRow{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), NodeID: 11, TxnID: common.Int64Ptr(100), Data: nil, DataEncoding: "", ShardID: 1, }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().InsertIntoHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeRow{ ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), Data: nil, DataEncoding: "", }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Success case - old branch", req: &persistence.InternalAppendHistoryNodesRequest{ IsNewBranch: false, Info: "test", BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, NodeID: 11, Events: &persistence.DataBlob{}, TransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().InsertIntoHistoryNode(gomock.Any(), &sqlplugin.HistoryNodeRow{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), NodeID: 11, TxnID: common.Int64Ptr(100), Data: nil, DataEncoding: "", ShardID: 1, }).Return(&sqlResult{rowsAffected: 1}, nil) }, wantErr: false, }, { name: "Error case - new branch, failed to encode data", req: &persistence.InternalAppendHistoryNodesRequest{ IsNewBranch: true, Info: "test", BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, NodeID: 11, Events: &persistence.DataBlob{}, TransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().HistoryTreeInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, errors.New("some error")) }, wantErr: true, }, { name: "Error case - new branch, failed to insert history node", req: &persistence.InternalAppendHistoryNodesRequest{ IsNewBranch: true, Info: "test", BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, NodeID: 11, Events: &persistence.DataBlob{}, TransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().HistoryTreeInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().InsertIntoHistoryNode(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - new branch, failed to insert history tree", req: &persistence.InternalAppendHistoryNodesRequest{ IsNewBranch: true, Info: "test", BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, NodeID: 11, Events: &persistence.DataBlob{}, TransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().HistoryTreeInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().InsertIntoHistoryNode(gomock.Any(), gomock.Any()).Return(&sqlResult{rowsAffected: 1}, nil) err := errors.New("some error") mockTx.EXPECT().InsertIntoHistoryTree(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - old branch, duplicate entry", req: &persistence.InternalAppendHistoryNodesRequest{ IsNewBranch: false, Info: "test", BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, NodeID: 11, Events: &persistence.DataBlob{}, TransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { err := errors.New("some error") mockDB.EXPECT().InsertIntoHistoryNode(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsDupEntryError(err).Return(true) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *persistence.ConditionFailedError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be ConditionFailedError") }, }, { name: "Error case - invalid history", req: &persistence.InternalAppendHistoryNodesRequest{ IsNewBranch: false, Info: "test", BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", Ancestors: []*types.HistoryBranchRange{ { BranchID: "730ec3d3-f74b-423f-a138-3b35494fe691", BeginNodeID: 1, EndNodeID: 10, }, }, }, NodeID: 5, Events: &persistence.DataBlob{}, TransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) {}, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *persistence.InvalidPersistenceRequestError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be InvalidPersistenceRequestError") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) store, err := NewHistoryV2Persistence(mockDB, nil, mockParser, nil) require.NoError(t, err, "Failed to create sql history store") tc.mockSetup(mockDB, mockTx, mockParser) err = store.AppendHistoryNodes(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestReadHistoryBranch(t *testing.T) { testCases := []struct { name string req *persistence.InternalReadHistoryBranchRequest mockSetup func(*sqlplugin.MockDB) want *persistence.InternalReadHistoryBranchResponse wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", req: &persistence.InternalReadHistoryBranchRequest{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", MinNodeID: 100, MaxNodeID: 1000, PageSize: 2, NextPageToken: serializePageToken(200), LastNodeID: 120, LastTransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().SelectFromHistoryNode(gomock.Any(), &sqlplugin.HistoryNodeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), MinNodeID: common.Int64Ptr(201), MaxNodeID: common.Int64Ptr(1000), PageSize: 2, ShardID: 1, }).Return([]sqlplugin.HistoryNodeRow{ { NodeID: 201, TxnID: common.Int64Ptr(99), Data: []byte(`a`), DataEncoding: "a", }, { NodeID: 202, TxnID: common.Int64Ptr(101), Data: []byte(`b`), DataEncoding: "b", }, }, nil) }, want: &persistence.InternalReadHistoryBranchResponse{ History: []*persistence.DataBlob{{Data: []byte(`b`), Encoding: constants.EncodingType("b")}}, NextPageToken: serializePageToken(202), LastNodeID: 202, LastTransactionID: 101, }, wantErr: false, }, { name: "Success case - no row", req: &persistence.InternalReadHistoryBranchRequest{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", MinNodeID: 100, MaxNodeID: 1000, PageSize: 2, NextPageToken: serializePageToken(200), LastNodeID: 120, LastTransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().SelectFromHistoryNode(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) }, want: &persistence.InternalReadHistoryBranchResponse{}, wantErr: false, }, { name: "Error case - corrupted data 1", req: &persistence.InternalReadHistoryBranchRequest{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", MinNodeID: 100, MaxNodeID: 1000, PageSize: 2, NextPageToken: serializePageToken(200), LastNodeID: 120, LastTransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().SelectFromHistoryNode(gomock.Any(), gomock.Any()).Return([]sqlplugin.HistoryNodeRow{ { NodeID: 119, TxnID: common.Int64Ptr(99), Data: []byte(`a`), DataEncoding: "a", }, { NodeID: 202, TxnID: common.Int64Ptr(101), Data: []byte(`b`), DataEncoding: "b", }, }, nil) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *types.InternalDataInconsistencyError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be InternalDataInconsistencyError") assert.Contains(t, err.Error(), "corrupted data, nodeID cannot decrease") }, }, { name: "Error case - corrupted data 2", req: &persistence.InternalReadHistoryBranchRequest{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", MinNodeID: 100, MaxNodeID: 1000, PageSize: 2, NextPageToken: serializePageToken(200), LastNodeID: 120, LastTransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().SelectFromHistoryNode(gomock.Any(), gomock.Any()).Return([]sqlplugin.HistoryNodeRow{ { NodeID: 119, TxnID: common.Int64Ptr(101), Data: []byte(`a`), DataEncoding: "a", }, { NodeID: 202, TxnID: common.Int64Ptr(101), Data: []byte(`b`), DataEncoding: "b", }, }, nil) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *types.InternalDataInconsistencyError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be InternalDataInconsistencyError") assert.Contains(t, err.Error(), "corrupted data, nodeID cannot decrease") }, }, { name: "Error case - corrupted data 3", req: &persistence.InternalReadHistoryBranchRequest{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", MinNodeID: 100, MaxNodeID: 1000, PageSize: 2, NextPageToken: serializePageToken(200), LastNodeID: 120, LastTransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().SelectFromHistoryNode(gomock.Any(), gomock.Any()).Return([]sqlplugin.HistoryNodeRow{ { NodeID: 120, TxnID: common.Int64Ptr(101), Data: []byte(`a`), DataEncoding: "a", }, }, nil) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *types.InternalDataInconsistencyError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be InternalDataInconsistencyError") assert.Contains(t, err.Error(), "corrupted data, same nodeID must have smaller txnID") }, }, { name: "Error case - database error", req: &persistence.InternalReadHistoryBranchRequest{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", MinNodeID: 100, MaxNodeID: 1000, PageSize: 2, NextPageToken: serializePageToken(200), LastNodeID: 120, LastTransactionID: 100, ShardID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().SelectFromHistoryNode(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := NewHistoryV2Persistence(mockDB, nil, nil, nil) require.NoError(t, err, "Failed to create sql history store") tc.mockSetup(mockDB) got, err := store.ReadHistoryBranch(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestDeleteHistoryBranch_CustomBatchSize(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) customBatchSize := 500 dc := &persistence.DynamicConfiguration{ HistoryNodeDeleteBatchSize: func(...dynamicproperties.FilterOption) int { return customBatchSize }, } store, err := NewHistoryV2Persistence(mockDB, nil, mockParser, dc) require.NoError(t, err) req := &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", }, ShardID: 1, } mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, }).Return([]sqlplugin.HistoryTreeRow{ { ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), Data: []byte(`aaaa`), DataEncoding: "json", }, }, nil) mockParser.EXPECT().HistoryTreeInfoFromBlob([]byte(`aaaa`), "json").Return(&serialization.HistoryTreeInfo{}, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().DeleteFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.UUIDPtr(serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691")), ShardID: 1, }).Return(nil, nil) mockTx.EXPECT().DeleteFromHistoryNode(gomock.Any(), &sqlplugin.HistoryNodeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, PageSize: customBatchSize, MinNodeID: common.Int64Ptr(1), }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) err = store.DeleteHistoryBranch(context.Background(), req) assert.NoError(t, err) } func TestDeleteHistoryBranch_UnboundedBatchSize(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{ HistoryNodeDeleteBatchSize: func(...dynamicproperties.FilterOption) int { return 0 }, } store, err := NewHistoryV2Persistence(mockDB, nil, mockParser, dc) require.NoError(t, err) req := &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", }, ShardID: 1, } mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, }).Return([]sqlplugin.HistoryTreeRow{ { ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), Data: []byte(`aaaa`), DataEncoding: "json", }, }, nil) mockParser.EXPECT().HistoryTreeInfoFromBlob([]byte(`aaaa`), "json").Return(&serialization.HistoryTreeInfo{}, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().DeleteFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.UUIDPtr(serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691")), ShardID: 1, }).Return(nil, nil) mockTx.EXPECT().DeleteFromHistoryNode(gomock.Any(), &sqlplugin.HistoryNodeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, PageSize: 0, MinNodeID: common.Int64Ptr(1), }).Return(&sqlResult{rowsAffected: 100}, nil) mockTx.EXPECT().Commit().Return(nil) err = store.DeleteHistoryBranch(context.Background(), req) assert.NoError(t, err) } func TestDeleteHistoryBranch_NegativeBatchSize(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) dc := &persistence.DynamicConfiguration{ HistoryNodeDeleteBatchSize: func(...dynamicproperties.FilterOption) int { return -100 }, } store, err := NewHistoryV2Persistence(mockDB, nil, mockParser, dc) require.NoError(t, err) req := &persistence.InternalDeleteHistoryBranchRequest{ BranchInfo: types.HistoryBranch{ TreeID: "530ec3d3-f74b-423f-a138-3b35494fe691", BranchID: "630ec3d3-f74b-423f-a138-3b35494fe691", }, ShardID: 1, } mockDB.EXPECT().SelectFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, }).Return([]sqlplugin.HistoryTreeRow{ { ShardID: 1, TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), Data: []byte(`aaaa`), DataEncoding: "json", }, }, nil) mockParser.EXPECT().HistoryTreeInfoFromBlob([]byte(`aaaa`), "json").Return(&serialization.HistoryTreeInfo{}, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().DeleteFromHistoryTree(gomock.Any(), &sqlplugin.HistoryTreeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.UUIDPtr(serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691")), ShardID: 1, }).Return(nil, nil) mockTx.EXPECT().DeleteFromHistoryNode(gomock.Any(), &sqlplugin.HistoryNodeFilter{ TreeID: serialization.MustParseUUID("530ec3d3-f74b-423f-a138-3b35494fe691"), BranchID: serialization.MustParseUUID("630ec3d3-f74b-423f-a138-3b35494fe691"), ShardID: 1, PageSize: -100, MinNodeID: common.Int64Ptr(1), }).Return(&sqlResult{rowsAffected: 50}, nil) mockTx.EXPECT().Commit().Return(nil) err = store.DeleteHistoryBranch(context.Background(), req) assert.NoError(t, err) } ================================================ FILE: common/persistence/sql/sql_queue_store.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "fmt" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) type ( sqlQueueStore struct { queueType persistence.QueueType logger log.Logger sqlStore } ) func newQueueStore( db sqlplugin.DB, logger log.Logger, queueType persistence.QueueType, ) (persistence.QueueStore, error) { return &sqlQueueStore{ sqlStore: sqlStore{ db: db, logger: logger, }, queueType: queueType, logger: logger, }, nil } func (q *sqlQueueStore) EnqueueMessage(ctx context.Context, request *persistence.InternalEnqueueMessageRequest) error { return q.txExecute(ctx, sqlplugin.DbDefaultShard, "EnqueueMessage", func(tx sqlplugin.Tx) error { lastMessageID, err := tx.GetLastEnqueuedMessageIDForUpdate(ctx, q.queueType) if err != nil { if err == sql.ErrNoRows { lastMessageID = -1 } else { return err } } ackLevels, err := tx.GetAckLevels(ctx, q.queueType, true) if err != nil { return err } _, err = tx.InsertIntoQueue(ctx, newQueueRow(q.queueType, getNextID(ackLevels, lastMessageID), request.MessagePayload)) return err }) } func (q *sqlQueueStore) ReadMessages( ctx context.Context, request *persistence.InternalReadMessagesRequest, ) (*persistence.InternalReadMessagesResponse, error) { rows, err := q.db.GetMessagesFromQueue(ctx, q.queueType, request.LastMessageID, request.MaxCount) if err != nil { return nil, convertCommonErrors(q.db, "ReadMessages", "", err) } var messages []*persistence.InternalQueueMessage for _, row := range rows { messages = append(messages, &persistence.InternalQueueMessage{ID: row.MessageID, Payload: row.MessagePayload}) } return &persistence.InternalReadMessagesResponse{Messages: messages}, nil } func newQueueRow( queueType persistence.QueueType, messageID int64, payload []byte, ) *sqlplugin.QueueRow { return &sqlplugin.QueueRow{QueueType: queueType, MessageID: messageID, MessagePayload: payload} } func (q *sqlQueueStore) DeleteMessagesBefore( ctx context.Context, request *persistence.InternalDeleteMessagesBeforeRequest, ) error { _, err := q.db.DeleteMessagesBefore(ctx, q.queueType, request.MessageID) if err != nil { return convertCommonErrors(q.db, "DeleteMessagesBefore", "", err) } return nil } func (q *sqlQueueStore) UpdateAckLevel(ctx context.Context, request *persistence.InternalUpdateAckLevelRequest) error { return q.txExecute(ctx, sqlplugin.DbDefaultShard, "UpdateAckLevel", func(tx sqlplugin.Tx) error { clusterAckLevels, err := tx.GetAckLevels(ctx, q.queueType, true) if err != nil { return err } if clusterAckLevels == nil { return tx.InsertAckLevel(ctx, q.queueType, request.MessageID, request.ClusterName) } // Ignore possibly delayed message if ackLevel, ok := clusterAckLevels[request.ClusterName]; ok && ackLevel >= request.MessageID { return nil } clusterAckLevels[request.ClusterName] = request.MessageID return tx.UpdateAckLevels(ctx, q.queueType, clusterAckLevels) }) } func (q *sqlQueueStore) GetAckLevels( ctx context.Context, _ *persistence.InternalGetAckLevelsRequest, ) (*persistence.InternalGetAckLevelsResponse, error) { result, err := q.db.GetAckLevels(ctx, q.queueType, false) if err != nil { return nil, convertCommonErrors(q.db, "GetAckLevels", "", err) } return &persistence.InternalGetAckLevelsResponse{AckLevels: result}, nil } func (q *sqlQueueStore) EnqueueMessageToDLQ(ctx context.Context, request *persistence.InternalEnqueueMessageToDLQRequest) error { return q.txExecute(ctx, sqlplugin.DbDefaultShard, "EnqueueMessageToDLQ", func(tx sqlplugin.Tx) error { var err error lastMessageID, err := tx.GetLastEnqueuedMessageIDForUpdate(ctx, q.getDLQTypeFromQueueType()) if err != nil { if err == sql.ErrNoRows { lastMessageID = -1 } else { return err } } _, err = tx.InsertIntoQueue(ctx, newQueueRow(q.getDLQTypeFromQueueType(), lastMessageID+1, request.MessagePayload)) return err }) } func (q *sqlQueueStore) ReadMessagesFromDLQ( ctx context.Context, request *persistence.InternalReadMessagesFromDLQRequest, ) (*persistence.InternalReadMessagesFromDLQResponse, error) { firstMessageID := request.FirstMessageID if len(request.PageToken) != 0 { lastReadMessageID, err := deserializePageToken(request.PageToken) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("invalid next page token %v", request.PageToken)} } firstMessageID = lastReadMessageID } rows, err := q.db.GetMessagesBetween(ctx, q.getDLQTypeFromQueueType(), firstMessageID, request.LastMessageID, request.PageSize) if err != nil { return nil, convertCommonErrors(q.db, "ReadMessagesFromDLQ", "", err) } var messages []*persistence.InternalQueueMessage for _, row := range rows { messages = append(messages, &persistence.InternalQueueMessage{ID: row.MessageID, Payload: row.MessagePayload}) } var newPagingToken []byte if messages != nil && len(messages) >= request.PageSize { lastReadMessageID := messages[len(messages)-1].ID newPagingToken = serializePageToken(int64(lastReadMessageID)) } return &persistence.InternalReadMessagesFromDLQResponse{ Messages: messages, NextPageToken: newPagingToken, }, nil } func (q *sqlQueueStore) DeleteMessageFromDLQ( ctx context.Context, request *persistence.InternalDeleteMessageFromDLQRequest, ) error { _, err := q.db.DeleteMessage(ctx, q.getDLQTypeFromQueueType(), request.MessageID) if err != nil { return convertCommonErrors(q.db, "DeleteMessageFromDLQ", "", err) } return nil } func (q *sqlQueueStore) RangeDeleteMessagesFromDLQ( ctx context.Context, request *persistence.InternalRangeDeleteMessagesFromDLQRequest, ) error { _, err := q.db.RangeDeleteMessages(ctx, q.getDLQTypeFromQueueType(), request.FirstMessageID, request.LastMessageID) if err != nil { return convertCommonErrors(q.db, "RangeDeleteMessagesFromDLQ", "", err) } return nil } func (q *sqlQueueStore) UpdateDLQAckLevel(ctx context.Context, request *persistence.InternalUpdateDLQAckLevelRequest) error { return q.txExecute(ctx, sqlplugin.DbDefaultShard, "UpdateDLQAckLevel", func(tx sqlplugin.Tx) error { clusterAckLevels, err := tx.GetAckLevels(ctx, q.getDLQTypeFromQueueType(), true) if err != nil { return err } if clusterAckLevels == nil { return tx.InsertAckLevel(ctx, q.getDLQTypeFromQueueType(), request.MessageID, request.ClusterName) } // Ignore possibly delayed message if ackLevel, ok := clusterAckLevels[request.ClusterName]; ok && ackLevel >= request.MessageID { return nil } clusterAckLevels[request.ClusterName] = request.MessageID return tx.UpdateAckLevels(ctx, q.getDLQTypeFromQueueType(), clusterAckLevels) }) } func (q *sqlQueueStore) GetDLQAckLevels( ctx context.Context, _ *persistence.InternalGetDLQAckLevelsRequest, ) (*persistence.InternalGetDLQAckLevelsResponse, error) { result, err := q.db.GetAckLevels(ctx, q.getDLQTypeFromQueueType(), false) if err != nil { return nil, convertCommonErrors(q.db, "GetDLQAckLevels", "", err) } return &persistence.InternalGetDLQAckLevelsResponse{AckLevels: result}, nil } func (q *sqlQueueStore) GetDLQSize( ctx context.Context, _ *persistence.InternalGetDLQSizeRequest, ) (*persistence.InternalGetDLQSizeResponse, error) { result, err := q.db.GetQueueSize(ctx, q.getDLQTypeFromQueueType()) if err != nil { return nil, convertCommonErrors(q.db, "GetDLQSize", "", err) } return &persistence.InternalGetDLQSizeResponse{Size: result}, nil } func (q *sqlQueueStore) getDLQTypeFromQueueType() persistence.QueueType { return -q.queueType } // if, for whatever reason, the ack-levels get ahead of the actual messages // then ensure the next ID follows func getNextID(acks map[string]int64, lastMessageID int64) int64 { o := lastMessageID for _, v := range acks { if v > o { o = v } } return o + 1 } ================================================ FILE: common/persistence/sql/sql_queue_store_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sql import ( "context" "database/sql" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) var fixedTime = time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) func TestGetNextID(t *testing.T) { tests := map[string]struct { acks map[string]int64 lastID int64 expected int64 }{ "expected case - last ID is equal to ack-levels": { acks: map[string]int64{"a": 3}, lastID: 3, expected: 4, }, "expected case - last ID is equal to ack-levels haven't caught up": { acks: map[string]int64{"a": 2}, lastID: 3, expected: 4, }, "error case - ack-levels are ahead for some reason": { acks: map[string]int64{"a": 3}, lastID: 2, expected: 4, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, getNextID(td.acks, td.lastID)) }) } } func TestEnqueueMessage(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx) wantErr bool }{ { name: "Success case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetLastEnqueuedMessageIDForUpdate(gomock.Any(), persistence.DomainReplicationQueueType).Return(int64(0), sql.ErrNoRows) mockTx.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, true).Return(nil, nil) mockTx.EXPECT().InsertIntoQueue(gomock.Any(), gomock.Any()).Return(nil, nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Error case - failed to get last enqueued message ID for update", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().GetLastEnqueuedMessageIDForUpdate(gomock.Any(), persistence.DomainReplicationQueueType).Return(int64(0), err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to get ack levels", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetLastEnqueuedMessageIDForUpdate(gomock.Any(), persistence.DomainReplicationQueueType).Return(int64(0), sql.ErrNoRows) err := errors.New("some error") mockTx.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, true).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to insert into queue", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetLastEnqueuedMessageIDForUpdate(gomock.Any(), persistence.DomainReplicationQueueType).Return(int64(0), sql.ErrNoRows) mockTx.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, true).Return(nil, nil) err := errors.New("some error") mockTx.EXPECT().InsertIntoQueue(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB, mockTx) err = store.EnqueueMessage(context.Background(), &persistence.InternalEnqueueMessageRequest{ MessagePayload: nil, CurrentTimeStamp: fixedTime, }) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestReadMessages(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType mockSetup func(*sqlplugin.MockDB) want []*persistence.InternalQueueMessage wantErr bool }{ { name: "Success case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetMessagesFromQueue(gomock.Any(), persistence.DomainReplicationQueueType, gomock.Any(), gomock.Any()).Return([]sqlplugin.QueueRow{ { QueueType: persistence.DomainReplicationQueueType, MessageID: 123, MessagePayload: []byte(`aaaa`), }, }, nil) }, want: []*persistence.InternalQueueMessage{ { ID: 123, Payload: []byte(`aaaa`), }, }, wantErr: false, }, { name: "Error case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().GetMessagesFromQueue(gomock.Any(), persistence.DomainReplicationQueueType, gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB) resp, err := store.ReadMessages(context.Background(), &persistence.InternalReadMessagesRequest{ LastMessageID: 0, MaxCount: 10, }) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, resp.Messages, "Unexpected result for test case") } }) } } func TestDeleteMessagesBefore(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType mockSetup func(*sqlplugin.MockDB) wantErr bool }{ { name: "Success case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().DeleteMessagesBefore(gomock.Any(), persistence.DomainReplicationQueueType, gomock.Any()).Return(nil, nil) }, wantErr: false, }, { name: "Error case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().DeleteMessagesBefore(gomock.Any(), persistence.DomainReplicationQueueType, gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB) err = store.DeleteMessagesBefore(context.Background(), &persistence.InternalDeleteMessagesBeforeRequest{ MessageID: 10, }) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestUpdateAckLevel(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType clusterName string mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx) wantErr bool }{ { name: "Success case - insert", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, true).Return(nil, nil) mockTx.EXPECT().InsertAckLevel(gomock.Any(), persistence.DomainReplicationQueueType, gomock.Any(), gomock.Any()).Return(nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Success case - update", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, true).Return(map[string]int64{}, nil) mockTx.EXPECT().UpdateAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, gomock.Any()).Return(nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Success case - no op", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, true).Return(map[string]int64{"abc": 100}, nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Error case - failed to get", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, true).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to insert", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, true).Return(nil, nil) err := errors.New("some error") mockTx.EXPECT().InsertAckLevel(gomock.Any(), persistence.DomainReplicationQueueType, gomock.Any(), gomock.Any()).Return(err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to update", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, true).Return(map[string]int64{}, nil) err := errors.New("some error") mockTx.EXPECT().UpdateAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, gomock.Any()).Return(err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB, mockTx) err = store.UpdateAckLevel(context.Background(), &persistence.InternalUpdateAckLevelRequest{ MessageID: 0, ClusterName: tc.clusterName, CurrentTimeStamp: fixedTime, }) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestGetAckLevels(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType mockSetup func(*sqlplugin.MockDB) want map[string]int64 wantErr bool }{ { name: "Success case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, false).Return(map[string]int64{"x": 9}, nil) }, want: map[string]int64{"x": 9}, wantErr: false, }, { name: "Error case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().GetAckLevels(gomock.Any(), persistence.DomainReplicationQueueType, false).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB) resp, err := store.GetAckLevels(context.Background(), &persistence.InternalGetAckLevelsRequest{}) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, resp.AckLevels, "Unexpected result for test case") } }) } } func TestEnqueueMessageToDLQ(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx) wantErr bool }{ { name: "Success case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetLastEnqueuedMessageIDForUpdate(gomock.Any(), -1*persistence.DomainReplicationQueueType).Return(int64(0), sql.ErrNoRows) mockTx.EXPECT().InsertIntoQueue(gomock.Any(), gomock.Any()).Return(nil, nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Error case - failed to get last enqueued message ID for update", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().GetLastEnqueuedMessageIDForUpdate(gomock.Any(), -1*persistence.DomainReplicationQueueType).Return(int64(0), err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to insert into queue", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetLastEnqueuedMessageIDForUpdate(gomock.Any(), -1*persistence.DomainReplicationQueueType).Return(int64(0), sql.ErrNoRows) err := errors.New("some error") mockTx.EXPECT().InsertIntoQueue(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB, mockTx) err = store.EnqueueMessageToDLQ(context.Background(), &persistence.InternalEnqueueMessageToDLQRequest{ MessagePayload: nil, CurrentTimeStamp: fixedTime, }) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestDeleteMessageFromDLQ(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType mockSetup func(*sqlplugin.MockDB) wantErr bool }{ { name: "Success case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().DeleteMessage(gomock.Any(), -1*persistence.DomainReplicationQueueType, gomock.Any()).Return(nil, nil) }, wantErr: false, }, { name: "Error case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().DeleteMessage(gomock.Any(), -1*persistence.DomainReplicationQueueType, gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB) err = store.DeleteMessageFromDLQ(context.Background(), &persistence.InternalDeleteMessageFromDLQRequest{ MessageID: 10, }) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestRangeDeleteMessagesFromDLQ(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType mockSetup func(*sqlplugin.MockDB) wantErr bool }{ { name: "Success case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().RangeDeleteMessages(gomock.Any(), -1*persistence.DomainReplicationQueueType, gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: false, }, { name: "Error case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().RangeDeleteMessages(gomock.Any(), -1*persistence.DomainReplicationQueueType, gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB) err = store.RangeDeleteMessagesFromDLQ(context.Background(), &persistence.InternalRangeDeleteMessagesFromDLQRequest{ FirstMessageID: 10, LastMessageID: 100, }) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestGetDLQAckLevels(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType mockSetup func(*sqlplugin.MockDB) want map[string]int64 wantErr bool }{ { name: "Success case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetAckLevels(gomock.Any(), -1*persistence.DomainReplicationQueueType, false).Return(map[string]int64{"x": 9}, nil) }, want: map[string]int64{"x": 9}, wantErr: false, }, { name: "Error case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().GetAckLevels(gomock.Any(), -1*persistence.DomainReplicationQueueType, false).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB) resp, err := store.GetDLQAckLevels(context.Background(), &persistence.InternalGetDLQAckLevelsRequest{}) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, resp.AckLevels, "Unexpected result for test case") } }) } } func TestUpdateDLQAckLevel(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType clusterName string mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx) wantErr bool }{ { name: "Success case - insert", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetAckLevels(gomock.Any(), -1*persistence.DomainReplicationQueueType, true).Return(nil, nil) mockTx.EXPECT().InsertAckLevel(gomock.Any(), -1*persistence.DomainReplicationQueueType, gomock.Any(), gomock.Any()).Return(nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Success case - update", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetAckLevels(gomock.Any(), -1*persistence.DomainReplicationQueueType, true).Return(map[string]int64{}, nil) mockTx.EXPECT().UpdateAckLevels(gomock.Any(), -1*persistence.DomainReplicationQueueType, gomock.Any()).Return(nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Success case - no op", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetAckLevels(gomock.Any(), -1*persistence.DomainReplicationQueueType, true).Return(map[string]int64{"abc": 100}, nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Error case - failed to get", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().GetAckLevels(gomock.Any(), -1*persistence.DomainReplicationQueueType, true).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to insert", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetAckLevels(gomock.Any(), -1*persistence.DomainReplicationQueueType, true).Return(nil, nil) err := errors.New("some error") mockTx.EXPECT().InsertAckLevel(gomock.Any(), -1*persistence.DomainReplicationQueueType, gomock.Any(), gomock.Any()).Return(err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to update", queueType: persistence.DomainReplicationQueueType, clusterName: "abc", mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx) { mockDB.EXPECT().BeginTx(gomock.Any(), sqlplugin.DbDefaultShard).Return(mockTx, nil) mockTx.EXPECT().GetAckLevels(gomock.Any(), -1*persistence.DomainReplicationQueueType, true).Return(map[string]int64{}, nil) err := errors.New("some error") mockTx.EXPECT().UpdateAckLevels(gomock.Any(), -1*persistence.DomainReplicationQueueType, gomock.Any()).Return(err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB, mockTx) err = store.UpdateDLQAckLevel(context.Background(), &persistence.InternalUpdateDLQAckLevelRequest{ MessageID: 0, ClusterName: tc.clusterName, CurrentTimeStamp: fixedTime, }) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestGetDLQSize(t *testing.T) { testCases := []struct { name string queueType persistence.QueueType mockSetup func(*sqlplugin.MockDB) want int64 wantErr bool }{ { name: "Success case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetQueueSize(gomock.Any(), -1*persistence.DomainReplicationQueueType).Return(int64(1000), nil) }, want: 1000, wantErr: false, }, { name: "Error case", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().GetQueueSize(gomock.Any(), -1*persistence.DomainReplicationQueueType).Return(int64(0), err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB) resp, err := store.GetDLQSize(context.Background(), &persistence.InternalGetDLQSizeRequest{}) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, resp.Size, "Unexpected result for test case") } }) } } func TestReadMessagesFromDLQ(t *testing.T) { firstMessageID := int64(0) lastMessageID := int64(9999) pageSize := 1 testCases := []struct { name string queueType persistence.QueueType pageToken []byte mockSetup func(*sqlplugin.MockDB) want []*persistence.InternalQueueMessage wantToken []byte wantErr bool }{ { name: "Success case", queueType: persistence.DomainReplicationQueueType, pageToken: serializePageToken(int64(100)), mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetMessagesBetween(gomock.Any(), -1*persistence.DomainReplicationQueueType, gomock.Any(), gomock.Any(), gomock.Any()).Return([]sqlplugin.QueueRow{ { QueueType: -1 * persistence.DomainReplicationQueueType, MessageID: 123, MessagePayload: []byte(`aaaa`), }, }, nil) }, want: []*persistence.InternalQueueMessage{ { ID: 123, Payload: []byte(`aaaa`), }, }, wantToken: serializePageToken(int64(123)), wantErr: false, }, { name: "Error case - failed to deserialize token", queueType: persistence.DomainReplicationQueueType, pageToken: []byte(`aaa`), mockSetup: func(mockDB *sqlplugin.MockDB) {}, wantErr: true, }, { name: "Error case - failed to get messages", queueType: persistence.DomainReplicationQueueType, mockSetup: func(mockDB *sqlplugin.MockDB) { err := errors.New("some error") mockDB.EXPECT().GetMessagesBetween(gomock.Any(), -1*persistence.DomainReplicationQueueType, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store, err := newQueueStore(mockDB, nil, tc.queueType) require.NoError(t, err, "Failed to create sql queue store") tc.mockSetup(mockDB) resp, err := store.ReadMessagesFromDLQ(context.Background(), &persistence.InternalReadMessagesFromDLQRequest{ FirstMessageID: firstMessageID, LastMessageID: lastMessageID, PageSize: pageSize, PageToken: tc.pageToken, }) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, resp.Messages, "Unexpected result for test case") assert.Equal(t, tc.wantToken, resp.NextPageToken, "Unexpected result for test case") } }) } } ================================================ FILE: common/persistence/sql/sql_shard_store.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "fmt" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) type sqlShardStore struct { sqlStore currentClusterName string } // NewShardPersistence creates an instance of ShardStore func NewShardPersistence( db sqlplugin.DB, currentClusterName string, log log.Logger, parser serialization.Parser, ) (persistence.ShardStore, error) { return &sqlShardStore{ sqlStore: sqlStore{ db: db, logger: log, parser: parser, }, currentClusterName: currentClusterName, }, nil } func (m *sqlShardStore) CreateShard( ctx context.Context, request *persistence.InternalCreateShardRequest, ) error { if _, err := m.GetShard(ctx, &persistence.InternalGetShardRequest{ ShardID: request.ShardInfo.ShardID, }); err == nil { return &persistence.ShardAlreadyExistError{ Msg: fmt.Sprintf("CreateShard operation failed. Shard with ID %v already exists.", request.ShardInfo.ShardID), } } row, err := shardInfoToShardsRow(*request.ShardInfo, m.parser) if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("CreateShard operation failed. Error: %v", err), } } if _, err := m.db.InsertIntoShards(ctx, row); err != nil { return convertCommonErrors(m.db, "CreateShard", "Failed to insert into shards table.", err) } return nil } func (m *sqlShardStore) GetShard( ctx context.Context, request *persistence.InternalGetShardRequest, ) (*persistence.InternalGetShardResponse, error) { row, err := m.db.SelectFromShards(ctx, &sqlplugin.ShardsFilter{ShardID: int64(request.ShardID)}) if err != nil { return nil, convertCommonErrors(m.db, "GetShard", fmt.Sprintf("Failed to get shard, ShardId: %v.", request.ShardID), err) } shardInfo, err := m.parser.ShardInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } if len(shardInfo.ClusterTransferAckLevel) == 0 { shardInfo.ClusterTransferAckLevel = map[string]int64{ m.currentClusterName: shardInfo.GetTransferAckLevel(), } } timerAckLevel := make(map[string]time.Time, len(shardInfo.ClusterTimerAckLevel)) for k, v := range shardInfo.ClusterTimerAckLevel { timerAckLevel[k] = v } if len(timerAckLevel) == 0 { timerAckLevel = map[string]time.Time{ m.currentClusterName: shardInfo.GetTimerAckLevel(), } } if shardInfo.ClusterReplicationLevel == nil { shardInfo.ClusterReplicationLevel = make(map[string]int64) } if shardInfo.ReplicationDlqAckLevel == nil { shardInfo.ReplicationDlqAckLevel = make(map[string]int64) } var transferPQS *persistence.DataBlob if shardInfo.GetTransferProcessingQueueStates() != nil { transferPQS = &persistence.DataBlob{ Encoding: constants.EncodingType(shardInfo.GetTransferProcessingQueueStatesEncoding()), Data: shardInfo.GetTransferProcessingQueueStates(), } } var timerPQS *persistence.DataBlob if shardInfo.GetTimerProcessingQueueStates() != nil { timerPQS = &persistence.DataBlob{ Encoding: constants.EncodingType(shardInfo.GetTimerProcessingQueueStatesEncoding()), Data: shardInfo.GetTimerProcessingQueueStates(), } } var pendingFailoverMarkers *persistence.DataBlob if shardInfo.GetPendingFailoverMarkers() != nil { pendingFailoverMarkers = &persistence.DataBlob{ Encoding: constants.EncodingType(shardInfo.GetPendingFailoverMarkersEncoding()), Data: shardInfo.GetPendingFailoverMarkers(), } } resp := &persistence.InternalGetShardResponse{ShardInfo: &persistence.InternalShardInfo{ ShardID: int(row.ShardID), RangeID: row.RangeID, Owner: shardInfo.GetOwner(), StolenSinceRenew: int(shardInfo.GetStolenSinceRenew()), UpdatedAt: shardInfo.GetUpdatedAt(), ReplicationAckLevel: shardInfo.GetReplicationAckLevel(), TransferAckLevel: shardInfo.GetTransferAckLevel(), TimerAckLevel: shardInfo.GetTimerAckLevel(), ClusterTransferAckLevel: shardInfo.ClusterTransferAckLevel, ClusterTimerAckLevel: timerAckLevel, TransferProcessingQueueStates: transferPQS, TimerProcessingQueueStates: timerPQS, DomainNotificationVersion: shardInfo.GetDomainNotificationVersion(), ClusterReplicationLevel: shardInfo.ClusterReplicationLevel, ReplicationDLQAckLevel: shardInfo.ReplicationDlqAckLevel, PendingFailoverMarkers: pendingFailoverMarkers, QueueStates: shardInfo.GetQueueStates(), }} return resp, nil } func (m *sqlShardStore) UpdateShard( ctx context.Context, request *persistence.InternalUpdateShardRequest, ) error { row, err := shardInfoToShardsRow(*request.ShardInfo, m.parser) if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("UpdateShard operation failed. Error: %v", err), } } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(request.ShardInfo.ShardID, m.db.GetTotalNumDBShards()) return m.txExecute(ctx, dbShardID, "UpdateShard", func(tx sqlplugin.Tx) error { if err := lockShard(ctx, tx, request.ShardInfo.ShardID, request.PreviousRangeID); err != nil { return err } result, err := tx.UpdateShards(ctx, row) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("rowsAffected returned error for shardID %v: %v", request.ShardInfo.ShardID, err) } if rowsAffected != 1 { return fmt.Errorf("rowsAffected returned %v shards instead of one", rowsAffected) } return nil }) } // initiated by the owning shard func lockShard(ctx context.Context, tx sqlplugin.Tx, shardID int, oldRangeID int64) error { rangeID, err := tx.WriteLockShards(ctx, &sqlplugin.ShardsFilter{ShardID: int64(shardID)}) if err != nil { if err == sql.ErrNoRows { return &types.InternalServiceError{ Message: fmt.Sprintf("Failed to lock shard with ID %v that does not exist.", shardID), } } return convertCommonErrors(tx, "lockShard", fmt.Sprintf("Failed to lock shard with ID: %v.", shardID), err) } if int64(rangeID) != oldRangeID { return &persistence.ShardOwnershipLostError{ ShardID: shardID, Msg: fmt.Sprintf("Failed to update shard. Previous range ID: %v; new range ID: %v", oldRangeID, rangeID), } } return nil } // initiated by the owning shard func readLockShard(ctx context.Context, tx sqlplugin.Tx, shardID int, oldRangeID int64) error { rangeID, err := tx.ReadLockShards(ctx, &sqlplugin.ShardsFilter{ShardID: int64(shardID)}) if err != nil { if err == sql.ErrNoRows { return &types.InternalServiceError{ Message: fmt.Sprintf("Failed to lock shard with ID %v that does not exist.", shardID), } } return convertCommonErrors(tx, "readLockShard", fmt.Sprintf("Failed to lock shard with ID: %v.", shardID), err) } if int64(rangeID) != oldRangeID { return &persistence.ShardOwnershipLostError{ ShardID: shardID, Msg: fmt.Sprintf("Failed to lock shard. Previous range ID: %v; new range ID: %v", oldRangeID, rangeID), } } return nil } func shardInfoToShardsRow(s persistence.InternalShardInfo, parser serialization.Parser) (*sqlplugin.ShardsRow, error) { var markerData []byte markerEncoding := string(constants.EncodingTypeEmpty) if s.PendingFailoverMarkers != nil { markerData = s.PendingFailoverMarkers.Data markerEncoding = string(s.PendingFailoverMarkers.Encoding) } var transferPQSData []byte transferPQSEncoding := string(constants.EncodingTypeEmpty) if s.TransferProcessingQueueStates != nil { transferPQSData = s.TransferProcessingQueueStates.Data transferPQSEncoding = string(s.TransferProcessingQueueStates.Encoding) } var timerPQSData []byte timerPQSEncoding := string(constants.EncodingTypeEmpty) if s.TimerProcessingQueueStates != nil { timerPQSData = s.TimerProcessingQueueStates.Data timerPQSEncoding = string(s.TimerProcessingQueueStates.Encoding) } shardInfo := &serialization.ShardInfo{ StolenSinceRenew: int32(s.StolenSinceRenew), UpdatedAt: s.UpdatedAt, ReplicationAckLevel: s.ReplicationAckLevel, TransferAckLevel: s.TransferAckLevel, TimerAckLevel: s.TimerAckLevel, ClusterTransferAckLevel: s.ClusterTransferAckLevel, ClusterTimerAckLevel: s.ClusterTimerAckLevel, TransferProcessingQueueStates: transferPQSData, TransferProcessingQueueStatesEncoding: transferPQSEncoding, TimerProcessingQueueStates: timerPQSData, TimerProcessingQueueStatesEncoding: timerPQSEncoding, DomainNotificationVersion: s.DomainNotificationVersion, Owner: s.Owner, ClusterReplicationLevel: s.ClusterReplicationLevel, ReplicationDlqAckLevel: s.ReplicationDLQAckLevel, PendingFailoverMarkers: markerData, PendingFailoverMarkersEncoding: markerEncoding, QueueStates: s.QueueStates, } blob, err := parser.ShardInfoToBlob(shardInfo) if err != nil { return nil, err } return &sqlplugin.ShardsRow{ ShardID: int64(s.ShardID), RangeID: s.RangeID, Data: blob.Data, DataEncoding: string(blob.Encoding), }, nil } ================================================ FILE: common/persistence/sql/sql_shard_store_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) func TestGetShard(t *testing.T) { testCases := []struct { name string clusterName string req *persistence.InternalGetShardRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) want *persistence.InternalGetShardResponse wantErr bool }{ { name: "Success case", clusterName: "active", req: &persistence.InternalGetShardRequest{ ShardID: 2, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 2}).Return(&sqlplugin.ShardsRow{ ShardID: 2, RangeID: 4, Data: []byte(`aaaa`), DataEncoding: "json", }, nil) mockParser.EXPECT().ShardInfoFromBlob([]byte(`aaaa`), "json").Return(&serialization.ShardInfo{ Owner: "owner", StolenSinceRenew: 1, UpdatedAt: time.Unix(100, 10), ReplicationAckLevel: 1001, TransferAckLevel: 1002, TimerAckLevel: time.Unix(2, 1), TransferProcessingQueueStates: []byte(`transfer`), TransferProcessingQueueStatesEncoding: "transfer", TimerProcessingQueueStates: []byte(`timer`), TimerProcessingQueueStatesEncoding: "timer", DomainNotificationVersion: 99, PendingFailoverMarkers: []byte(`markers`), PendingFailoverMarkersEncoding: "markers", ReplicationDlqAckLevel: map[string]int64{"active": 10}, ClusterReplicationLevel: map[string]int64{"active": 1002}, ClusterTimerAckLevel: map[string]time.Time{"active": time.Unix(2, 1)}, ClusterTransferAckLevel: map[string]int64{"active": 1002}, QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: {}, }, }, }, }, nil) }, want: &persistence.InternalGetShardResponse{ ShardInfo: &persistence.InternalShardInfo{ ShardID: 2, RangeID: 4, Owner: "owner", StolenSinceRenew: 1, UpdatedAt: time.Unix(100, 10), ReplicationAckLevel: 1001, TransferAckLevel: 1002, TimerAckLevel: time.Unix(2, 1), ClusterTransferAckLevel: map[string]int64{"active": 1002}, ClusterTimerAckLevel: map[string]time.Time{"active": time.Unix(2, 1)}, TransferProcessingQueueStates: &persistence.DataBlob{ Encoding: constants.EncodingType("transfer"), Data: []byte(`transfer`), }, TimerProcessingQueueStates: &persistence.DataBlob{ Encoding: constants.EncodingType("timer"), Data: []byte(`timer`), }, DomainNotificationVersion: 99, ClusterReplicationLevel: map[string]int64{"active": 1002}, ReplicationDLQAckLevel: map[string]int64{"active": 10}, PendingFailoverMarkers: &persistence.DataBlob{ Encoding: constants.EncodingType("markers"), Data: []byte(`markers`), }, QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: {}, }, }, }, }, }, wantErr: false, }, { name: "Error case - failed to get shard", clusterName: "active", req: &persistence.InternalGetShardRequest{ ShardID: 2, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { err := errors.New("some error") mockDB.EXPECT().SelectFromShards(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to decode data", clusterName: "active", req: &persistence.InternalGetShardRequest{ ShardID: 2, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 2}).Return(&sqlplugin.ShardsRow{ ShardID: 2, RangeID: 4, Data: []byte(`aaaa`), DataEncoding: "json", }, nil) err := errors.New("some error") mockParser.EXPECT().ShardInfoFromBlob(gomock.Any(), gomock.Any()).Return(nil, err) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockParser(ctrl) store, err := NewShardPersistence(mockDB, tc.clusterName, nil, mockParser) require.NoError(t, err, "Failed to create sql shard store") tc.mockSetup(mockDB, mockParser) got, err := store.GetShard(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestCreateShard(t *testing.T) { testCases := []struct { name string clusterName string req *persistence.InternalCreateShardRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", clusterName: "active", req: &persistence.InternalCreateShardRequest{ ShardInfo: &persistence.InternalShardInfo{ ShardID: 2, Owner: "owner", RangeID: 4, StolenSinceRenew: 1, UpdatedAt: time.Unix(1, 2), ReplicationAckLevel: 9, TransferAckLevel: 99, TimerAckLevel: time.Unix(9, 9), ClusterTransferAckLevel: map[string]int64{"a": 8}, ClusterTimerAckLevel: map[string]time.Time{"b": time.Unix(8, 8)}, TransferProcessingQueueStates: &persistence.DataBlob{ Data: []byte(`transfer`), Encoding: constants.EncodingType("transfer"), }, TimerProcessingQueueStates: &persistence.DataBlob{ Data: []byte(`timer`), Encoding: constants.EncodingType("timer"), }, DomainNotificationVersion: 101, ClusterReplicationLevel: map[string]int64{"z": 199}, ReplicationDLQAckLevel: map[string]int64{"y": 1111}, PendingFailoverMarkers: &persistence.DataBlob{ Data: []byte(`markers`), Encoding: constants.EncodingType("markers"), }, QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: {}, }, }, }, }, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromShards(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) mockParser.EXPECT().ShardInfoToBlob(&serialization.ShardInfo{ StolenSinceRenew: 1, UpdatedAt: time.Unix(1, 2), ReplicationAckLevel: 9, TransferAckLevel: 99, TimerAckLevel: time.Unix(9, 9), ClusterTransferAckLevel: map[string]int64{"a": 8}, ClusterTimerAckLevel: map[string]time.Time{"b": time.Unix(8, 8)}, TransferProcessingQueueStates: []byte(`transfer`), TransferProcessingQueueStatesEncoding: "transfer", TimerProcessingQueueStates: []byte(`timer`), TimerProcessingQueueStatesEncoding: "timer", DomainNotificationVersion: 101, Owner: "owner", ClusterReplicationLevel: map[string]int64{"z": 199}, ReplicationDlqAckLevel: map[string]int64{"y": 1111}, PendingFailoverMarkers: []byte(`markers`), PendingFailoverMarkersEncoding: "markers", QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: {}, }, }, }, }).Return(persistence.DataBlob{ Encoding: constants.EncodingType("shard"), Data: []byte(`shard`), }, nil) mockDB.EXPECT().InsertIntoShards(gomock.Any(), &sqlplugin.ShardsRow{ ShardID: 2, RangeID: 4, Data: []byte(`shard`), DataEncoding: "shard", }).Return(nil, nil) }, wantErr: false, }, { name: "Error case - already exists", clusterName: "active", req: &persistence.InternalCreateShardRequest{ ShardInfo: &persistence.InternalShardInfo{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromShards(gomock.Any(), gomock.Any()).Return(&sqlplugin.ShardsRow{}, nil) mockParser.EXPECT().ShardInfoFromBlob(gomock.Any(), gomock.Any()).Return(&serialization.ShardInfo{}, nil) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *persistence.ShardAlreadyExistError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be ShardAlreadyExistError") }, }, { name: "Error case - failed to encode", clusterName: "active", req: &persistence.InternalCreateShardRequest{ ShardInfo: &persistence.InternalShardInfo{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromShards(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, errors.New("some error")) }, wantErr: true, }, { name: "Error case - failed to insert", clusterName: "active", req: &persistence.InternalCreateShardRequest{ ShardInfo: &persistence.InternalShardInfo{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromShards(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: constants.EncodingType("shard"), Data: []byte(`shard`), }, nil) err := errors.New("some error") mockDB.EXPECT().InsertIntoShards(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockParser(ctrl) store, err := NewShardPersistence(mockDB, tc.clusterName, nil, mockParser) require.NoError(t, err, "Failed to create sql shard store") tc.mockSetup(mockDB, mockParser) err = store.CreateShard(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestUpdateShard(t *testing.T) { testCases := []struct { name string clusterName string req *persistence.InternalUpdateShardRequest mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx, *serialization.MockParser) wantErr bool }{ { name: "Success case", clusterName: "active", req: &persistence.InternalUpdateShardRequest{ PreviousRangeID: 1, ShardInfo: &persistence.InternalShardInfo{ ShardID: 2, Owner: "owner", RangeID: 4, StolenSinceRenew: 1, UpdatedAt: time.Unix(1, 2), ReplicationAckLevel: 9, TransferAckLevel: 99, TimerAckLevel: time.Unix(9, 9), ClusterTransferAckLevel: map[string]int64{"a": 8}, ClusterTimerAckLevel: map[string]time.Time{"b": time.Unix(8, 8)}, TransferProcessingQueueStates: &persistence.DataBlob{ Data: []byte(`transfer`), Encoding: constants.EncodingType("transfer"), }, TimerProcessingQueueStates: &persistence.DataBlob{ Data: []byte(`timer`), Encoding: constants.EncodingType("timer"), }, DomainNotificationVersion: 101, ClusterReplicationLevel: map[string]int64{"z": 199}, ReplicationDLQAckLevel: map[string]int64{"y": 1111}, PendingFailoverMarkers: &persistence.DataBlob{ Data: []byte(`markers`), Encoding: constants.EncodingType("markers"), }, QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: {}, }, }, }, }, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().ShardInfoToBlob(&serialization.ShardInfo{ StolenSinceRenew: 1, UpdatedAt: time.Unix(1, 2), ReplicationAckLevel: 9, TransferAckLevel: 99, TimerAckLevel: time.Unix(9, 9), ClusterTransferAckLevel: map[string]int64{"a": 8}, ClusterTimerAckLevel: map[string]time.Time{"b": time.Unix(8, 8)}, TransferProcessingQueueStates: []byte(`transfer`), TransferProcessingQueueStatesEncoding: "transfer", TimerProcessingQueueStates: []byte(`timer`), TimerProcessingQueueStatesEncoding: "timer", DomainNotificationVersion: 101, Owner: "owner", ClusterReplicationLevel: map[string]int64{"z": 199}, ReplicationDlqAckLevel: map[string]int64{"y": 1111}, PendingFailoverMarkers: []byte(`markers`), PendingFailoverMarkersEncoding: "markers", QueueStates: map[int32]*types.QueueState{ 0: &types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: {}, }, }, }, }).Return(persistence.DataBlob{ Encoding: constants.EncodingType("shard"), Data: []byte(`shard`), }, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().WriteLockShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 2}).Return(1, nil) mockTx.EXPECT().UpdateShards(gomock.Any(), &sqlplugin.ShardsRow{ ShardID: 2, RangeID: 4, Data: []byte(`shard`), DataEncoding: "shard", }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) }, wantErr: false, }, { name: "Error case - failed to encode", clusterName: "active", req: &persistence.InternalUpdateShardRequest{ ShardInfo: &persistence.InternalShardInfo{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{}, errors.New("some error")) }, wantErr: true, }, { name: "Error case - failed to lock", clusterName: "active", req: &persistence.InternalUpdateShardRequest{ ShardInfo: &persistence.InternalShardInfo{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: constants.EncodingType("shard"), Data: []byte(`shard`), }, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().WriteLockShards(gomock.Any(), gomock.Any()).Return(0, err) mockTx.EXPECT().IsNotFoundError(gomock.Any()).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, }, { name: "Error case - failed to update", clusterName: "active", req: &persistence.InternalUpdateShardRequest{ ShardInfo: &persistence.InternalShardInfo{}, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockParser.EXPECT().ShardInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Encoding: constants.EncodingType("shard"), Data: []byte(`shard`), }, nil) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().WriteLockShards(gomock.Any(), gomock.Any()).Return(0, nil) err := errors.New("some error") mockTx.EXPECT().UpdateShards(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().Rollback().Return(nil) mockDB.EXPECT().IsNotFoundError(gomock.Any()).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) store, err := NewShardPersistence(mockDB, tc.clusterName, nil, mockParser) require.NoError(t, err, "Failed to create sql shard store") tc.mockSetup(mockDB, mockTx, mockParser) err = store.UpdateShard(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestLockShard(t *testing.T) { shardID := 1 oldRangeID := int64(99) testCases := []struct { name string mockSetup func(*sqlplugin.MockTx) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().WriteLockShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 1}).Return(99, nil) }, wantErr: false, }, { name: "Error case - not found", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().WriteLockShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 1}).Return(0, sql.ErrNoRows) }, wantErr: true, }, { name: "Error case - database failure", mockSetup: func(mockTx *sqlplugin.MockTx) { err := errors.New("some error") mockTx.EXPECT().WriteLockShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 1}).Return(0, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - rangeID mismatch", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().WriteLockShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 1}).Return(98, nil) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *persistence.ShardOwnershipLostError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be ShardOwnershipLostError") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) tc.mockSetup(mockTx) err := lockShard(context.Background(), mockTx, shardID, oldRangeID) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestReadLockShard(t *testing.T) { shardID := 1 oldRangeID := int64(99) testCases := []struct { name string mockSetup func(*sqlplugin.MockTx) wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().ReadLockShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 1}).Return(99, nil) }, wantErr: false, }, { name: "Error case - not found", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().ReadLockShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 1}).Return(0, sql.ErrNoRows) }, wantErr: true, }, { name: "Error case - database failure", mockSetup: func(mockTx *sqlplugin.MockTx) { err := errors.New("some error") mockTx.EXPECT().ReadLockShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 1}).Return(0, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - rangeID mismatch", mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().ReadLockShards(gomock.Any(), &sqlplugin.ShardsFilter{ShardID: 1}).Return(98, nil) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *persistence.ShardOwnershipLostError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be ShardOwnershipLostError") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTx := sqlplugin.NewMockTx(ctrl) tc.mockSetup(mockTx) err := readLockShard(context.Background(), mockTx, shardID, oldRangeID) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } ================================================ FILE: common/persistence/sql/sql_task_store.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "fmt" "math" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) type sqlTaskStore struct { sqlStore nShards int } var ( taskListTTL = time.Hour * 24 ) // newTaskPersistence creates a new instance of TaskManager func newTaskPersistence( db sqlplugin.DB, nShards int, log log.Logger, parser serialization.Parser, ) (persistence.TaskStore, error) { return &sqlTaskStore{ sqlStore: sqlStore{ db: db, logger: log, parser: parser, }, nShards: nShards, }, nil } func (m *sqlTaskStore) GetTaskListSize(ctx context.Context, request *persistence.GetTaskListSizeRequest) (*persistence.GetTaskListSizeResponse, error) { dbShardID := sqlplugin.GetDBShardIDFromDomainIDAndTasklist(request.DomainID, request.TaskListName, m.db.GetTotalNumDBShards()) domainID := serialization.MustParseUUID(request.DomainID) size, err := m.db.GetTasksCount(ctx, &sqlplugin.TasksFilter{ ShardID: dbShardID, DomainID: domainID, TaskListName: request.TaskListName, TaskType: int64(request.TaskListType), MinTaskID: &request.AckLevel, }) if err != nil { return nil, convertCommonErrors(m.db, "GetTaskListSize", "", err) } return &persistence.GetTaskListSizeResponse{Size: size}, nil } func (m *sqlTaskStore) LeaseTaskList( ctx context.Context, request *persistence.LeaseTaskListRequest, ) (*persistence.LeaseTaskListResponse, error) { var rangeID int64 var ackLevel int64 dbShardID := sqlplugin.GetDBShardIDFromDomainIDAndTasklist(request.DomainID, request.TaskList, m.db.GetTotalNumDBShards()) domainID := serialization.MustParseUUID(request.DomainID) rows, err := m.db.SelectFromTaskLists(ctx, &sqlplugin.TaskListsFilter{ ShardID: dbShardID, DomainID: &domainID, Name: &request.TaskList, TaskType: common.Int64Ptr(int64(request.TaskType))}) if err != nil { if err == sql.ErrNoRows { tlInfo := &serialization.TaskListInfo{ AckLevel: ackLevel, Kind: int16(request.TaskListKind), ExpiryTimestamp: time.Unix(0, 0), LastUpdated: request.CurrentTimeStamp, } blob, err := m.parser.TaskListInfoToBlob(tlInfo) if err != nil { return nil, err } row := sqlplugin.TaskListsRow{ ShardID: dbShardID, DomainID: domainID, Name: request.TaskList, TaskType: int64(request.TaskType), Data: blob.Data, DataEncoding: string(blob.Encoding), } rows = []sqlplugin.TaskListsRow{row} if m.db.SupportsTTL() && persistence.TaskListKindHasTTL(request.TaskListKind) { rowWithTTL := sqlplugin.TaskListsRowWithTTL{ TaskListsRow: row, TTL: taskListTTL, } if _, err := m.db.InsertIntoTaskListsWithTTL(ctx, &rowWithTTL); err != nil { return nil, convertCommonErrors(m.db, "LeaseTaskListWithTTL", fmt.Sprintf("Failed to make task list %v of type %v.", request.TaskList, request.TaskType), err) } } else { if _, err := m.db.InsertIntoTaskLists(ctx, &row); err != nil { return nil, convertCommonErrors(m.db, "LeaseTaskList", fmt.Sprintf("Failed to make task list %v of type %v.", request.TaskList, request.TaskType), err) } } } else { return nil, convertCommonErrors(m.db, "LeaseTaskList", "Failed to check if task list existed.", err) } } row := rows[0] if request.RangeID > 0 && request.RangeID != row.RangeID { return nil, &persistence.ConditionFailedError{ Msg: fmt.Sprintf("leaseTaskList:renew failed:taskList:%v, taskListType:%v, haveRangeID:%v, gotRangeID:%v", request.TaskList, request.TaskType, rangeID, row.RangeID), } } tlInfo, err := m.parser.TaskListInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } var resp *persistence.LeaseTaskListResponse err = m.txExecute(ctx, dbShardID, "LeaseTaskList", func(tx sqlplugin.Tx) error { rangeID = row.RangeID ackLevel = tlInfo.GetAckLevel() // We need to separately check the condition and do the // update because we want to throw different error codes. // Since we need to do things separately (in a transaction), we need to take a lock. err1 := lockTaskList(ctx, tx, dbShardID, domainID, request.TaskList, request.TaskType, rangeID) if err1 != nil { return err1 } now := request.CurrentTimeStamp tlInfo.LastUpdated = now blob, err1 := m.parser.TaskListInfoToBlob(tlInfo) if err1 != nil { return err1 } row := &sqlplugin.TaskListsRow{ ShardID: dbShardID, DomainID: row.DomainID, RangeID: row.RangeID + 1, Name: row.Name, TaskType: row.TaskType, Data: blob.Data, DataEncoding: string(blob.Encoding), } var result sql.Result if m.db.SupportsTTL() && persistence.TaskListKindHasTTL(int(tlInfo.GetKind())) { result, err1 = tx.UpdateTaskListsWithTTL(ctx, &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: *row, TTL: taskListTTL, }) } else { result, err1 = tx.UpdateTaskLists(ctx, row) } if err1 != nil { return err1 } rowsAffected, err1 := result.RowsAffected() if err1 != nil { return fmt.Errorf("rowsAffected error: %v", err1) } if rowsAffected == 0 { return fmt.Errorf("%v rows affected instead of 1", rowsAffected) } resp = &persistence.LeaseTaskListResponse{TaskListInfo: &persistence.TaskListInfo{ DomainID: request.DomainID, Name: request.TaskList, TaskType: request.TaskType, RangeID: rangeID + 1, AckLevel: ackLevel, Kind: request.TaskListKind, LastUpdated: now, AdaptivePartitionConfig: fromSerializationTaskListPartitionConfig(tlInfo.AdaptivePartitionConfig), }} return nil }) return resp, err } func (m *sqlTaskStore) GetTaskList( ctx context.Context, request *persistence.GetTaskListRequest, ) (*persistence.GetTaskListResponse, error) { dbShardID := sqlplugin.GetDBShardIDFromDomainIDAndTasklist(request.DomainID, request.TaskList, m.db.GetTotalNumDBShards()) domainID := serialization.MustParseUUID(request.DomainID) rows, err := m.db.SelectFromTaskLists(ctx, &sqlplugin.TaskListsFilter{ ShardID: dbShardID, DomainID: &domainID, Name: &request.TaskList, TaskType: common.Int64Ptr(int64(request.TaskType))}) if err != nil { return nil, convertCommonErrors(m.db, "GetTaskList", "", err) } row := rows[0] tlInfo, err := m.parser.TaskListInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } return &persistence.GetTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ DomainID: request.DomainID, Name: request.TaskList, TaskType: request.TaskType, RangeID: row.RangeID, AckLevel: tlInfo.AckLevel, Kind: int(tlInfo.Kind), Expiry: tlInfo.ExpiryTimestamp, LastUpdated: tlInfo.LastUpdated, AdaptivePartitionConfig: fromSerializationTaskListPartitionConfig(tlInfo.AdaptivePartitionConfig), }, }, nil } func (m *sqlTaskStore) UpdateTaskList( ctx context.Context, request *persistence.UpdateTaskListRequest, ) (*persistence.UpdateTaskListResponse, error) { dbShardID := sqlplugin.GetDBShardIDFromDomainIDAndTasklist(request.TaskListInfo.DomainID, request.TaskListInfo.Name, m.db.GetTotalNumDBShards()) domainID := serialization.MustParseUUID(request.TaskListInfo.DomainID) now := request.CurrentTimeStamp tlInfo := &serialization.TaskListInfo{ AckLevel: request.TaskListInfo.AckLevel, Kind: int16(request.TaskListInfo.Kind), ExpiryTimestamp: time.Unix(0, 0), LastUpdated: now, AdaptivePartitionConfig: toSerializationTaskListPartitionConfig(request.TaskListInfo.AdaptivePartitionConfig), } if persistence.TaskListKindHasTTL(request.TaskListInfo.Kind) { tlInfo.ExpiryTimestamp = now.Add(taskListTTL) } var resp *persistence.UpdateTaskListResponse blob, err := m.parser.TaskListInfoToBlob(tlInfo) if err != nil { return nil, err } err = m.txExecute(ctx, dbShardID, "UpdateTaskList", func(tx sqlplugin.Tx) error { err1 := lockTaskList( ctx, tx, dbShardID, domainID, request.TaskListInfo.Name, request.TaskListInfo.TaskType, request.TaskListInfo.RangeID) if err1 != nil { return err1 } var result sql.Result row := &sqlplugin.TaskListsRow{ ShardID: dbShardID, DomainID: domainID, RangeID: request.TaskListInfo.RangeID, Name: request.TaskListInfo.Name, TaskType: int64(request.TaskListInfo.TaskType), Data: blob.Data, DataEncoding: string(blob.Encoding), } if m.db.SupportsTTL() && persistence.TaskListKindHasTTL(request.TaskListInfo.Kind) { result, err1 = tx.UpdateTaskListsWithTTL(ctx, &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: *row, TTL: taskListTTL, }) } else { result, err1 = tx.UpdateTaskLists(ctx, row) } if err1 != nil { return err1 } rowsAffected, err1 := result.RowsAffected() if err1 != nil { return err1 } if rowsAffected != 1 { return fmt.Errorf("%v rows were affected instead of 1", rowsAffected) } resp = &persistence.UpdateTaskListResponse{} return nil }) return resp, err } type taskListPageToken struct { ShardID int DomainID serialization.UUID Name string TaskType int64 } // ListTaskList lists tasklist from DB // DomainID translates into byte array in SQL. The minUUID is not the minimum byte array. func (m *sqlTaskStore) ListTaskList( ctx context.Context, request *persistence.ListTaskListRequest, ) (*persistence.ListTaskListResponse, error) { pageToken := taskListPageToken{DomainID: serialization.UUID{}} if len(request.PageToken) > 0 { if err := gobDeserialize(request.PageToken, &pageToken); err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("error deserializing page token: %v", err)} } } else { pageToken = taskListPageToken{TaskType: math.MinInt16, DomainID: serialization.UUID{}} } var err error var rows []sqlplugin.TaskListsRow for pageToken.ShardID < m.nShards { rows, err = m.db.SelectFromTaskLists(ctx, &sqlplugin.TaskListsFilter{ ShardID: pageToken.ShardID, DomainIDGreaterThan: &pageToken.DomainID, NameGreaterThan: &pageToken.Name, TaskTypeGreaterThan: &pageToken.TaskType, PageSize: &request.PageSize, }) if err != nil { return nil, convertCommonErrors(m.db, "ListTaskList", "", err) } if len(rows) > 0 { break } pageToken = taskListPageToken{ShardID: pageToken.ShardID + 1, TaskType: math.MinInt16, DomainID: serialization.UUID{}} } var nextPageToken []byte switch { case len(rows) >= request.PageSize: lastRow := &rows[request.PageSize-1] nextPageToken, err = gobSerialize(&taskListPageToken{ ShardID: pageToken.ShardID, DomainID: lastRow.DomainID, Name: lastRow.Name, TaskType: lastRow.TaskType, }) case pageToken.ShardID+1 < m.nShards: nextPageToken, err = gobSerialize(&taskListPageToken{ShardID: pageToken.ShardID + 1, TaskType: math.MinInt16, DomainID: serialization.UUID{}}) } if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("error serializing nextPageToken:%v", err)} } resp := &persistence.ListTaskListResponse{ Items: make([]persistence.TaskListInfo, len(rows)), NextPageToken: nextPageToken, } for i := range rows { info, err := m.parser.TaskListInfoFromBlob(rows[i].Data, rows[i].DataEncoding) if err != nil { return nil, err } resp.Items[i].DomainID = rows[i].DomainID.String() resp.Items[i].Name = rows[i].Name resp.Items[i].TaskType = int(rows[i].TaskType) resp.Items[i].RangeID = rows[i].RangeID resp.Items[i].Kind = int(info.GetKind()) resp.Items[i].AckLevel = info.GetAckLevel() resp.Items[i].Expiry = info.GetExpiryTimestamp() resp.Items[i].LastUpdated = info.GetLastUpdated() } return resp, nil } func (m *sqlTaskStore) DeleteTaskList( ctx context.Context, request *persistence.DeleteTaskListRequest, ) error { shardID := sqlplugin.GetDBShardIDFromDomainIDAndTasklist(request.DomainID, request.TaskListName, m.db.GetTotalNumDBShards()) domainID := serialization.MustParseUUID(request.DomainID) result, err := m.db.DeleteFromTaskLists(ctx, &sqlplugin.TaskListsFilter{ ShardID: shardID, DomainID: &domainID, Name: &request.TaskListName, TaskType: common.Int64Ptr(int64(request.TaskListType)), RangeID: &request.RangeID, }) if err != nil { return convertCommonErrors(m.db, "DeleteTaskList", "", err) } nRows, err := result.RowsAffected() if err != nil { return &types.InternalServiceError{Message: fmt.Sprintf("rowsAffected returned error:%v", err)} } if nRows != 1 { return &types.InternalServiceError{Message: fmt.Sprintf("delete failed: %v rows affected instead of 1", nRows)} } return nil } func (m *sqlTaskStore) CreateTasks( ctx context.Context, request *persistence.CreateTasksRequest, ) (*persistence.CreateTasksResponse, error) { var tasksRows []sqlplugin.TasksRow var tasksRowsWithTTL []sqlplugin.TasksRowWithTTL if m.db.SupportsTTL() { tasksRowsWithTTL = make([]sqlplugin.TasksRowWithTTL, len(request.Tasks)) } else { tasksRows = make([]sqlplugin.TasksRow, len(request.Tasks)) } dbShardID := sqlplugin.GetDBShardIDFromDomainIDAndTasklist(request.TaskListInfo.DomainID, request.TaskListInfo.Name, m.db.GetTotalNumDBShards()) for i, v := range request.Tasks { var expiryTime time.Time var ttl time.Duration if v.Data.ScheduleToStartTimeoutSeconds > 0 { ttl = time.Duration(v.Data.ScheduleToStartTimeoutSeconds) * time.Second if m.db.SupportsTTL() { maxAllowedTTL, err := m.db.MaxAllowedTTL() if err != nil { return nil, err } if ttl > *maxAllowedTTL { ttl = *maxAllowedTTL } } expiryTime = request.CurrentTimeStamp.Add(ttl) } blob, err := m.parser.TaskInfoToBlob(&serialization.TaskInfo{ WorkflowID: v.Data.WorkflowID, RunID: serialization.MustParseUUID(v.Data.RunID), ScheduleID: v.Data.ScheduleID, ExpiryTimestamp: expiryTime, CreatedTimestamp: request.CurrentTimeStamp, PartitionConfig: v.Data.PartitionConfig, }) if err != nil { return nil, err } currTasksRow := sqlplugin.TasksRow{ ShardID: dbShardID, DomainID: serialization.MustParseUUID(v.Data.DomainID), TaskListName: request.TaskListInfo.Name, TaskType: int64(request.TaskListInfo.TaskType), TaskID: v.TaskID, Data: blob.Data, DataEncoding: string(blob.Encoding), } if m.db.SupportsTTL() { currTasksRowWithTTL := sqlplugin.TasksRowWithTTL{ TasksRow: currTasksRow, } if ttl > 0 { currTasksRowWithTTL.TTL = &ttl } tasksRowsWithTTL[i] = currTasksRowWithTTL } else { tasksRows[i] = currTasksRow } } var resp *persistence.CreateTasksResponse err := m.txExecute(ctx, dbShardID, "CreateTasks", func(tx sqlplugin.Tx) error { if m.db.SupportsTTL() { if _, err := tx.InsertIntoTasksWithTTL(ctx, tasksRowsWithTTL); err != nil { return err } } else { if _, err := tx.InsertIntoTasks(ctx, tasksRows); err != nil { return err } } // Lock task list before committing. err1 := lockTaskList(ctx, tx, dbShardID, serialization.MustParseUUID(request.TaskListInfo.DomainID), request.TaskListInfo.Name, request.TaskListInfo.TaskType, request.TaskListInfo.RangeID) if err1 != nil { return err1 } resp = &persistence.CreateTasksResponse{} return nil }) return resp, err } func (m *sqlTaskStore) GetTasks( ctx context.Context, request *persistence.GetTasksRequest, ) (*persistence.GetTasksResponse, error) { shardID := sqlplugin.GetDBShardIDFromDomainIDAndTasklist(request.DomainID, request.TaskList, m.db.GetTotalNumDBShards()) rows, err := m.db.SelectFromTasks(ctx, &sqlplugin.TasksFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID(request.DomainID), TaskListName: request.TaskList, TaskType: int64(request.TaskType), MinTaskID: &request.ReadLevel, MaxTaskID: request.MaxReadLevel, PageSize: &request.BatchSize, }) if err != nil { return nil, convertCommonErrors(m.db, "GetTasks", "", err) } var tasks = make([]*persistence.TaskInfo, len(rows)) for i, v := range rows { info, err := m.parser.TaskInfoFromBlob(v.Data, v.DataEncoding) if err != nil { return nil, err } tasks[i] = &persistence.TaskInfo{ DomainID: request.DomainID, WorkflowID: info.GetWorkflowID(), RunID: info.RunID.String(), TaskID: v.TaskID, ScheduleID: info.GetScheduleID(), Expiry: info.GetExpiryTimestamp(), CreatedTime: info.GetCreatedTimestamp(), PartitionConfig: info.GetPartitionConfig(), } } return &persistence.GetTasksResponse{Tasks: tasks}, nil } func (m *sqlTaskStore) CompleteTask( ctx context.Context, request *persistence.CompleteTaskRequest, ) error { taskID := request.TaskID taskList := request.TaskList shardID := sqlplugin.GetDBShardIDFromDomainIDAndTasklist(taskList.DomainID, taskList.Name, m.db.GetTotalNumDBShards()) _, err := m.db.DeleteFromTasks(ctx, &sqlplugin.TasksFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID(taskList.DomainID), TaskListName: taskList.Name, TaskType: int64(taskList.TaskType), TaskID: &taskID}) if err != nil { return convertCommonErrors(m.db, "CompleteTask", "", err) } return nil } func (m *sqlTaskStore) CompleteTasksLessThan( ctx context.Context, request *persistence.CompleteTasksLessThanRequest, ) (*persistence.CompleteTasksLessThanResponse, error) { shardID := sqlplugin.GetDBShardIDFromDomainIDAndTasklist(request.DomainID, request.TaskListName, m.db.GetTotalNumDBShards()) result, err := m.db.DeleteFromTasks(ctx, &sqlplugin.TasksFilter{ ShardID: shardID, DomainID: serialization.MustParseUUID(request.DomainID), TaskListName: request.TaskListName, TaskType: int64(request.TaskType), TaskIDLessThanEquals: &request.TaskID, Limit: &request.Limit, }) if err != nil { return nil, convertCommonErrors(m.db, "CompleteTasksLessThan", "", err) } nRows, err := result.RowsAffected() if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("rowsAffected returned error: %v", err), } } return &persistence.CompleteTasksLessThanResponse{TasksCompleted: int(nRows)}, nil } // GetOrphanTasks gets tasks from the tasks table that belong to a task_list no longer present // in the task_lists table. // TODO: Limit this query to a specific shard at a time. See https://github.com/uber/cadence/issues/4064 func (m *sqlTaskStore) GetOrphanTasks(ctx context.Context, request *persistence.GetOrphanTasksRequest) (*persistence.GetOrphanTasksResponse, error) { rows, err := m.db.GetOrphanTasks(ctx, &sqlplugin.OrphanTasksFilter{ Limit: &request.Limit, }) if err != nil { return nil, convertCommonErrors(m.db, "GetOrphanTasks", "", err) } var tasks = make([]*persistence.TaskKey, len(rows)) for i, v := range rows { tasks[i] = &persistence.TaskKey{ DomainID: v.DomainID.String(), TaskListName: v.TaskListName, TaskType: int(v.TaskType), TaskID: v.TaskID, } } return &persistence.GetOrphanTasksResponse{Tasks: tasks}, nil } func lockTaskList(ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, name string, taskListType int, oldRangeID int64) error { rangeID, err := tx.LockTaskLists(ctx, &sqlplugin.TaskListsFilter{ ShardID: shardID, DomainID: &domainID, Name: &name, TaskType: common.Int64Ptr(int64(taskListType))}) switch err { case nil: if rangeID != oldRangeID { return &persistence.ConditionFailedError{ Msg: fmt.Sprintf("Task list range ID was %v when it was should have been %v", rangeID, oldRangeID), } } return nil case sql.ErrNoRows: return &persistence.ConditionFailedError{ Msg: "Task list does not exist.", } default: return convertCommonErrors(tx, "lockTaskList", "", err) } } func toSerializationTaskListPartitionConfig(c *persistence.TaskListPartitionConfig) *serialization.TaskListPartitionConfig { if c == nil { return nil } return &serialization.TaskListPartitionConfig{ Version: c.Version, NumReadPartitions: int32(len(c.ReadPartitions)), NumWritePartitions: int32(len(c.WritePartitions)), ReadPartitions: toSerializationTaskListPartitionMap(c.ReadPartitions), WritePartitions: toSerializationTaskListPartitionMap(c.WritePartitions), } } func toSerializationTaskListPartitionMap(m map[int]*persistence.TaskListPartition) map[int32]*serialization.TaskListPartition { if m == nil { return nil } result := make(map[int32]*serialization.TaskListPartition, len(m)) for id, p := range m { result[int32(id)] = &serialization.TaskListPartition{IsolationGroups: p.IsolationGroups} } return result } func fromSerializationTaskListPartitionConfig(c *serialization.TaskListPartitionConfig) *persistence.TaskListPartitionConfig { if c == nil { return nil } var read map[int]*persistence.TaskListPartition if int32(len(c.ReadPartitions)) == c.NumReadPartitions { read = fromSerializationTaskListPartitionMap(c.ReadPartitions) } else { read = createDefaultPartitions(c.NumReadPartitions) } var write map[int]*persistence.TaskListPartition if int32(len(c.WritePartitions)) == c.NumWritePartitions { write = fromSerializationTaskListPartitionMap(c.WritePartitions) } else { write = createDefaultPartitions(c.NumWritePartitions) } return &persistence.TaskListPartitionConfig{ Version: c.Version, ReadPartitions: read, WritePartitions: write, } } func createDefaultPartitions(len int32) map[int]*persistence.TaskListPartition { partitions := make(map[int]*persistence.TaskListPartition, len) for i := 0; i < int(len); i++ { partitions[i] = &persistence.TaskListPartition{} } return partitions } func fromSerializationTaskListPartitionMap(m map[int32]*serialization.TaskListPartition) map[int]*persistence.TaskListPartition { if m == nil { return nil } result := make(map[int]*persistence.TaskListPartition, len(m)) for id, p := range m { result[int(id)] = &persistence.TaskListPartition{IsolationGroups: p.IsolationGroups} } return result } ================================================ FILE: common/persistence/sql/sql_task_store_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "encoding/base64" "errors" "fmt" "math" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) func TestGetTaskListSize(t *testing.T) { testCases := []struct { name string req *persistence.GetTaskListSizeRequest mockSetup func(*sqlplugin.MockDB) want *persistence.GetTaskListSizeResponse wantErr bool }{ { name: "Success case", req: &persistence.GetTaskListSizeRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskListName: "tl", TaskListType: 0, AckLevel: 10, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().GetTasksCount(gomock.Any(), &sqlplugin.TasksFilter{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), TaskListName: "tl", TaskType: 0, MinTaskID: common.Int64Ptr(10), }).Return(int64(1), nil) }, want: &persistence.GetTaskListSizeResponse{ Size: 1, }, wantErr: false, }, { name: "Error case", req: &persistence.GetTaskListSizeRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskListName: "tl", TaskListType: 0, AckLevel: 10, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) err := errors.New("some error") mockDB.EXPECT().GetTasksCount(gomock.Any(), gomock.Any()).Return(int64(0), err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store := &sqlTaskStore{ sqlStore: sqlStore{db: mockDB}, } tc.mockSetup(mockDB) got, err := store.GetTaskListSize(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestLeaseTaskList(t *testing.T) { now := time.Now() testCases := []struct { name string req *persistence.LeaseTaskListRequest mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx, *serialization.MockParser) want *persistence.LeaseTaskListResponse wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case - first lease", req: &persistence.LeaseTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, TaskListKind: persistence.TaskListKindSticky, RangeID: 0, CurrentTimeStamp: now, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(nil, sql.ErrNoRows) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).DoAndReturn(func(info *serialization.TaskListInfo) (persistence.DataBlob, error) { assert.Equal(t, int16(persistence.TaskListKindSticky), info.Kind) assert.Equal(t, int64(0), info.AckLevel) assert.WithinDuration(t, now, info.LastUpdated, time.Second) return persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil }) mockDB.EXPECT().SupportsTTL().Return(true) mockDB.EXPECT().InsertIntoTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(nil, nil) mockParser.EXPECT().TaskListInfoFromBlob([]byte(`tl`), "tl").Return(&serialization.TaskListInfo{ AckLevel: 0, Kind: int16(persistence.TaskListKindSticky), ExpiryTimestamp: time.Unix(0, 0), LastUpdated: time.Unix(0, 1), }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), 0).Return(mockTx, nil) mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(int64(0), nil) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().SupportsTTL().Return(true) mockTx.EXPECT().UpdateTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, RangeID: 1, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) }, want: &persistence.LeaseTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 0, RangeID: 1, AckLevel: 0, Kind: persistence.TaskListKindSticky, }, }, wantErr: false, }, { name: "Success case - first lease - ephemeral", req: &persistence.LeaseTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, TaskListKind: persistence.TaskListKindEphemeral, RangeID: 0, CurrentTimeStamp: now, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(nil, sql.ErrNoRows) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).DoAndReturn(func(info *serialization.TaskListInfo) (persistence.DataBlob, error) { assert.Equal(t, int16(persistence.TaskListKindEphemeral), info.Kind) assert.Equal(t, int64(0), info.AckLevel) assert.WithinDuration(t, now, info.LastUpdated, time.Second) return persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil }) mockDB.EXPECT().SupportsTTL().Return(true) mockDB.EXPECT().InsertIntoTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(nil, nil) mockParser.EXPECT().TaskListInfoFromBlob([]byte(`tl`), "tl").Return(&serialization.TaskListInfo{ AckLevel: 0, Kind: int16(persistence.TaskListKindEphemeral), ExpiryTimestamp: time.Unix(0, 0), LastUpdated: time.Unix(0, 1), }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), 0).Return(mockTx, nil) mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(int64(0), nil) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().SupportsTTL().Return(true) mockTx.EXPECT().UpdateTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, RangeID: 1, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) }, want: &persistence.LeaseTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 0, RangeID: 1, AckLevel: 0, Kind: persistence.TaskListKindEphemeral, }, }, wantErr: false, }, { name: "Success case - first lease - normal tasklist", req: &persistence.LeaseTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, TaskListKind: persistence.TaskListKindNormal, RangeID: 0, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(nil, sql.ErrNoRows) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).DoAndReturn(func(info *serialization.TaskListInfo) (persistence.DataBlob, error) { assert.Equal(t, int16(persistence.TaskListKindNormal), info.Kind) assert.Equal(t, int64(0), info.AckLevel) return persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil }) mockDB.EXPECT().SupportsTTL().Return(true) mockDB.EXPECT().InsertIntoTaskLists(gomock.Any(), &sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, Data: []byte(`tl`), DataEncoding: "tl", }, ).Return(nil, nil) mockParser.EXPECT().TaskListInfoFromBlob([]byte(`tl`), "tl").Return(&serialization.TaskListInfo{ AckLevel: 0, Kind: int16(persistence.TaskListKindSticky), ExpiryTimestamp: time.Unix(0, 0), LastUpdated: time.Unix(0, 1), AdaptivePartitionConfig: &serialization.TaskListPartitionConfig{ Version: 0, NumReadPartitions: 1, ReadPartitions: map[int32]*serialization.TaskListPartition{0: {}}, NumWritePartitions: 1, WritePartitions: map[int32]*serialization.TaskListPartition{0: {}}, }, }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), 0).Return(mockTx, nil) mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(int64(0), nil) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().SupportsTTL().Return(true) mockTx.EXPECT().UpdateTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, RangeID: 1, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) }, want: &persistence.LeaseTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 0, RangeID: 1, AckLevel: 0, Kind: persistence.TaskListKindNormal, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 0, ReadPartitions: map[int]*persistence.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*persistence.TaskListPartition{ 0: {}, }, }, }, }, wantErr: false, }, { name: "Error case - failed to get tasklist", req: &persistence.LeaseTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, TaskListKind: persistence.TaskListKindSticky, RangeID: 0, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) err := errors.New("some error") mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to insert tasklist", req: &persistence.LeaseTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, TaskListKind: persistence.TaskListKindSticky, RangeID: 0, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().SupportsTTL().Return(true) err := errors.New("some error") mockDB.EXPECT().InsertIntoTaskListsWithTTL(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - condition failed", req: &persistence.LeaseTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, TaskListKind: persistence.TaskListKindSticky, RangeID: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(nil, sql.ErrNoRows) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().SupportsTTL().Return(true) mockDB.EXPECT().InsertIntoTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(nil, nil) }, wantErr: true, assertErr: func(t *testing.T, err error) { var expectedErr *persistence.ConditionFailedError assert.True(t, errors.As(err, &expectedErr), "Expected the error to be ConditionFailedError") }, }, { name: "Error case - failed to lock tasklist", req: &persistence.LeaseTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, TaskListKind: persistence.TaskListKindSticky, RangeID: 0, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(nil, sql.ErrNoRows) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().SupportsTTL().Return(true) mockDB.EXPECT().InsertIntoTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(nil, nil) mockParser.EXPECT().TaskListInfoFromBlob([]byte(`tl`), "tl").Return(&serialization.TaskListInfo{ AckLevel: 0, Kind: int16(persistence.TaskListKindSticky), ExpiryTimestamp: time.Unix(0, 0), LastUpdated: time.Unix(0, 1), }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), 0).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(int64(0), err) mockTx.EXPECT().IsNotFoundError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, }, { name: "Error case - failed to update tasklists", req: &persistence.LeaseTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, TaskListKind: persistence.TaskListKindSticky, RangeID: 0, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(nil, sql.ErrNoRows) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().SupportsTTL().Return(true) mockDB.EXPECT().InsertIntoTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(nil, nil) mockParser.EXPECT().TaskListInfoFromBlob([]byte(`tl`), "tl").Return(&serialization.TaskListInfo{ AckLevel: 0, Kind: int16(persistence.TaskListKindSticky), ExpiryTimestamp: time.Unix(0, 0), LastUpdated: time.Unix(0, 1), }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), 0).Return(mockTx, nil) mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(int64(0), nil) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().SupportsTTL().Return(true) err := errors.New("some error") mockTx.EXPECT().UpdateTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, RangeID: 1, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) store := &sqlTaskStore{ sqlStore: sqlStore{db: mockDB, parser: mockParser}, } tc.mockSetup(mockDB, mockTx, mockParser) got, err := store.LeaseTaskList(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err, "Did not expect an error for test case") got.TaskListInfo.LastUpdated = tc.want.TaskListInfo.LastUpdated assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestGetTaskList(t *testing.T) { testCases := []struct { name string req *persistence.GetTaskListRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) want *persistence.GetTaskListResponse wantErr bool }{ { name: "Success case", req: &persistence.GetTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, filter *sqlplugin.TaskListsFilter) ([]sqlplugin.TaskListsRow, error) { assert.Equal(t, serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), *filter.DomainID) assert.Equal(t, "tl", *filter.Name) assert.Equal(t, int64(1), *filter.TaskType) return []sqlplugin.TaskListsRow{ { ShardID: 11, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 1, RangeID: 123, Data: []byte(`tl`), DataEncoding: "tl", }, }, nil }) mockParser.EXPECT().TaskListInfoFromBlob([]byte(`tl`), "tl").Return(&serialization.TaskListInfo{ Kind: 1, AckLevel: 2, ExpiryTimestamp: time.Unix(1, 4), LastUpdated: time.Unix(10, 0), AdaptivePartitionConfig: &serialization.TaskListPartitionConfig{ Version: 0, NumReadPartitions: 1, NumWritePartitions: 1, ReadPartitions: map[int32]*serialization.TaskListPartition{ 0: { IsolationGroups: []string{"foo"}, }, }, WritePartitions: map[int32]*serialization.TaskListPartition{ 0: { IsolationGroups: []string{"bar"}, }, }, }, }, nil) }, want: &persistence.GetTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 1, RangeID: 123, Kind: 1, AckLevel: 2, Expiry: time.Unix(1, 4), LastUpdated: time.Unix(10, 0), AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 0, ReadPartitions: map[int]*persistence.TaskListPartition{ 0: { IsolationGroups: []string{"foo"}, }, }, WritePartitions: map[int]*persistence.TaskListPartition{ 0: { IsolationGroups: []string{"bar"}, }, }, }, }, }, wantErr: false, }, { name: "Partition counts instead of data", req: &persistence.GetTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, filter *sqlplugin.TaskListsFilter) ([]sqlplugin.TaskListsRow, error) { assert.Equal(t, serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), *filter.DomainID) assert.Equal(t, "tl", *filter.Name) assert.Equal(t, int64(1), *filter.TaskType) return []sqlplugin.TaskListsRow{ { ShardID: 11, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 1, RangeID: 123, Data: []byte(`tl`), DataEncoding: "tl", }, }, nil }) mockParser.EXPECT().TaskListInfoFromBlob([]byte(`tl`), "tl").Return(&serialization.TaskListInfo{ Kind: 1, AckLevel: 2, ExpiryTimestamp: time.Unix(1, 4), LastUpdated: time.Unix(10, 0), AdaptivePartitionConfig: &serialization.TaskListPartitionConfig{ Version: 0, NumReadPartitions: 2, NumWritePartitions: 2, }, }, nil) }, want: &persistence.GetTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 1, RangeID: 123, Kind: 1, AckLevel: 2, Expiry: time.Unix(1, 4), LastUpdated: time.Unix(10, 0), AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 0, ReadPartitions: map[int]*persistence.TaskListPartition{ 0: {}, 1: {}, }, WritePartitions: map[int]*persistence.TaskListPartition{ 0: {}, 1: {}, }, }, }, }, wantErr: false, }, { name: "Error case", req: &persistence.GetTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { err := errors.New("some error") mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockParser(ctrl) store := &sqlTaskStore{ sqlStore: sqlStore{db: mockDB, parser: mockParser}, nShards: 1000, } tc.mockSetup(mockDB, mockParser) got, err := store.GetTaskList(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestUpdateTaskList(t *testing.T) { now := time.Now() testCases := []struct { name string req *persistence.UpdateTaskListRequest mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx, *serialization.MockParser) want *persistence.UpdateTaskListResponse wantErr bool }{ { name: "Success case", req: &persistence.UpdateTaskListRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 0, RangeID: 1, AckLevel: 0, Kind: persistence.TaskListKindSticky, }, CurrentTimeStamp: now, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).DoAndReturn(func(info *serialization.TaskListInfo) (persistence.DataBlob, error) { assert.WithinDuration(t, now, info.LastUpdated, time.Second) return persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil }) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(int64(1), nil) mockDB.EXPECT().SupportsTTL().Return(true) mockTx.EXPECT().UpdateTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, RangeID: 1, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) }, want: &persistence.UpdateTaskListResponse{}, wantErr: false, }, { name: "Success case - ephemeral", req: &persistence.UpdateTaskListRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 0, RangeID: 1, AckLevel: 0, Kind: persistence.TaskListKindEphemeral, }, CurrentTimeStamp: now, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(int64(1), nil) mockDB.EXPECT().SupportsTTL().Return(true) mockTx.EXPECT().UpdateTaskListsWithTTL(gomock.Any(), &sqlplugin.TaskListsRowWithTTL{ TaskListsRow: sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, RangeID: 1, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: taskListTTL, }).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) }, want: &persistence.UpdateTaskListResponse{}, wantErr: false, }, { name: "Success case - normal tasklist", req: &persistence.UpdateTaskListRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 0, RangeID: 1, AckLevel: 0, Kind: persistence.TaskListKindNormal, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 0, ReadPartitions: map[int]*persistence.TaskListPartition{ 0: { IsolationGroups: []string{"foo"}, }, }, WritePartitions: map[int]*persistence.TaskListPartition{ 0: { IsolationGroups: []string{"bar"}, }, }, }, }, CurrentTimeStamp: now, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).DoAndReturn(func(info *serialization.TaskListInfo) (persistence.DataBlob, error) { assert.Equal(t, int16(persistence.TaskListKindNormal), info.Kind) assert.Equal(t, int64(0), info.AckLevel) assert.WithinDuration(t, now, info.LastUpdated, time.Second) assert.Equal(t, int64(0), info.AdaptivePartitionConfig.Version) assert.Equal(t, int32(1), info.AdaptivePartitionConfig.NumReadPartitions) assert.Equal(t, int32(1), info.AdaptivePartitionConfig.NumWritePartitions) assert.Equal(t, "foo", info.AdaptivePartitionConfig.ReadPartitions[0].IsolationGroups[0]) assert.Equal(t, "bar", info.AdaptivePartitionConfig.WritePartitions[0].IsolationGroups[0]) return persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil }) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(int64(1), nil) mockDB.EXPECT().SupportsTTL().Return(true) mockTx.EXPECT().UpdateTaskLists(gomock.Any(), &sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, RangeID: 1, Data: []byte(`tl`), DataEncoding: "tl", }, ).Return(&sqlResult{rowsAffected: 1}, nil) mockTx.EXPECT().Commit().Return(nil) }, want: &persistence.UpdateTaskListResponse{}, wantErr: false, }, { name: "Error case - failed to lock task list", req: &persistence.UpdateTaskListRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 0, RangeID: 1, AckLevel: 0, Kind: persistence.TaskListKindSticky, }, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(int64(1), err) mockTx.EXPECT().IsNotFoundError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, want: &persistence.UpdateTaskListResponse{}, wantErr: true, }, { name: "Error case - failed to update task list", req: &persistence.UpdateTaskListRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 0, RangeID: 1, AckLevel: 0, Kind: persistence.TaskListKindSticky, }, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockParser.EXPECT().TaskListInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), }).Return(int64(1), nil) err := errors.New("some error") mockDB.EXPECT().SupportsTTL().Return(false) mockTx.EXPECT().UpdateTaskLists(gomock.Any(), &sqlplugin.TaskListsRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, RangeID: 1, Data: []byte(`tl`), DataEncoding: "tl", }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, want: &persistence.UpdateTaskListResponse{}, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) store := &sqlTaskStore{ sqlStore: sqlStore{db: mockDB, parser: mockParser}, } tc.mockSetup(mockDB, mockTx, mockParser) got, err := store.UpdateTaskList(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestListTaskList(t *testing.T) { pageSize := 1 testCases := []struct { name string pageToken *taskListPageToken mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) want *persistence.ListTaskListResponse wantToken *taskListPageToken wantErr bool }{ { name: "Success case", pageToken: &taskListPageToken{ ShardID: 10, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, filter *sqlplugin.TaskListsFilter) ([]sqlplugin.TaskListsRow, error) { assert.Equal(t, 10, filter.ShardID) assert.Equal(t, serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), *filter.DomainIDGreaterThan) assert.Equal(t, "tl", *filter.NameGreaterThan) assert.Equal(t, int64(0), *filter.TaskTypeGreaterThan) assert.Equal(t, 1, *filter.PageSize) return []sqlplugin.TaskListsRow{}, nil }) mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, filter *sqlplugin.TaskListsFilter) ([]sqlplugin.TaskListsRow, error) { assert.Equal(t, 11, filter.ShardID) assert.Equal(t, serialization.UUID{}, *filter.DomainIDGreaterThan) assert.Equal(t, "", *filter.NameGreaterThan) assert.Equal(t, int64(math.MinInt16), *filter.TaskTypeGreaterThan) assert.Equal(t, 1, *filter.PageSize) return []sqlplugin.TaskListsRow{ { ShardID: 11, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 1, RangeID: 123, Data: []byte(`tl`), DataEncoding: "tl", }, }, nil }) mockParser.EXPECT().TaskListInfoFromBlob([]byte(`tl`), "tl").Return(&serialization.TaskListInfo{ Kind: 1, AckLevel: 2, ExpiryTimestamp: time.Unix(1, 4), LastUpdated: time.Unix(10, 0), }, nil) }, want: &persistence.ListTaskListResponse{ Items: []persistence.TaskListInfo{ { DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 1, RangeID: 123, Kind: 1, AckLevel: 2, Expiry: time.Unix(1, 4), LastUpdated: time.Unix(10, 0), }, }, NextPageToken: func() []byte { token, err := gobSerialize(&taskListPageToken{ ShardID: 11, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 1, }) require.NoError(t, err, "failed to serialize page token") return token }(), }, wantErr: false, }, { name: "Error case", pageToken: &taskListPageToken{ ShardID: 10, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), Name: "tl", TaskType: 0, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { err := errors.New("some error") mockDB.EXPECT().SelectFromTaskLists(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockParser(ctrl) store := &sqlTaskStore{ sqlStore: sqlStore{db: mockDB, parser: mockParser}, nShards: 1000, } tc.mockSetup(mockDB, mockParser) pageToken, err := gobSerialize(tc.pageToken) require.NoError(t, err, "invalid pageToken") req := &persistence.ListTaskListRequest{ PageSize: pageSize, PageToken: pageToken, } got, err := store.ListTaskList(context.Background(), req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestDeleteTaskList(t *testing.T) { testCases := []struct { name string req *persistence.DeleteTaskListRequest mockSetup func(*sqlplugin.MockDB) wantErr bool }{ { name: "Success case", req: &persistence.DeleteTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskListName: "tl", TaskListType: 0, RangeID: 10, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().DeleteFromTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(0), RangeID: common.Int64Ptr(10), }).Return(&sqlResult{rowsAffected: 1}, nil) }, wantErr: false, }, { name: "Error case", req: &persistence.DeleteTaskListRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskListName: "tl", TaskListType: 0, RangeID: 10, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) err := errors.New("some error") mockDB.EXPECT().DeleteFromTaskLists(gomock.Any(), gomock.Any()).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store := &sqlTaskStore{ sqlStore: sqlStore{db: mockDB}, } tc.mockSetup(mockDB) err := store.DeleteTaskList(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestCreateTasks(t *testing.T) { testCases := []struct { name string req *persistence.CreateTasksRequest mockSetup func(*sqlplugin.MockDB, *sqlplugin.MockTx, *serialization.MockParser) want *persistence.CreateTasksResponse wantErr bool }{ { name: "Success case", req: &persistence.CreateTasksRequest{ TaskListInfo: &persistence.TaskListInfo{ Name: "tl", TaskType: 1, RangeID: 9, DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", }, Tasks: []*persistence.CreateTaskInfo{ { Data: &persistence.TaskInfo{ ScheduleToStartTimeoutSeconds: 1, DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskID: 999, }, TaskID: 999, }, }, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SupportsTTL().Return(true).Times(4) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().MaxAllowedTTL().Return(common.DurationPtr(time.Hour), nil) mockParser.EXPECT().TaskInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().InsertIntoTasksWithTTL(gomock.Any(), []sqlplugin.TasksRowWithTTL{ { TasksRow: sqlplugin.TasksRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), TaskListName: "tl", TaskType: 1, TaskID: 999, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: common.DurationPtr(time.Second), }, }).Return(nil, nil) mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(1), }).Return(int64(9), nil) mockTx.EXPECT().Commit().Return(nil) }, want: &persistence.CreateTasksResponse{}, wantErr: false, }, { name: "Error case - failed to insert tasks", req: &persistence.CreateTasksRequest{ TaskListInfo: &persistence.TaskListInfo{ Name: "tl", TaskType: 1, RangeID: 9, DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", }, Tasks: []*persistence.CreateTaskInfo{ { Data: &persistence.TaskInfo{ ScheduleToStartTimeoutSeconds: 1, DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskID: 999, }, TaskID: 999, }, }, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SupportsTTL().Return(true).Times(4) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().MaxAllowedTTL().Return(common.DurationPtr(time.Hour), nil) mockParser.EXPECT().TaskInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) err := errors.New("some error") mockTx.EXPECT().InsertIntoTasksWithTTL(gomock.Any(), []sqlplugin.TasksRowWithTTL{ { TasksRow: sqlplugin.TasksRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), TaskListName: "tl", TaskType: 1, TaskID: 999, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: common.DurationPtr(time.Second), }, }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, }, { name: "Error case - failed to lock tasklist", req: &persistence.CreateTasksRequest{ TaskListInfo: &persistence.TaskListInfo{ Name: "tl", TaskType: 1, RangeID: 9, DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", }, Tasks: []*persistence.CreateTaskInfo{ { Data: &persistence.TaskInfo{ ScheduleToStartTimeoutSeconds: 1, DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskID: 999, }, TaskID: 999, }, }, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockTx *sqlplugin.MockTx, mockParser *serialization.MockParser) { mockDB.EXPECT().SupportsTTL().Return(true).Times(4) mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().MaxAllowedTTL().Return(common.DurationPtr(time.Hour), nil) mockParser.EXPECT().TaskInfoToBlob(gomock.Any()).Return(persistence.DataBlob{ Data: []byte(`tl`), Encoding: constants.EncodingType("tl"), }, nil) mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) mockTx.EXPECT().InsertIntoTasksWithTTL(gomock.Any(), []sqlplugin.TasksRowWithTTL{ { TasksRow: sqlplugin.TasksRow{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), TaskListName: "tl", TaskType: 1, TaskID: 999, Data: []byte(`tl`), DataEncoding: "tl", }, TTL: common.DurationPtr(time.Second), }, }).Return(nil, nil) err := errors.New("some error") mockTx.EXPECT().LockTaskLists(gomock.Any(), &sqlplugin.TaskListsFilter{ ShardID: 0, DomainID: serialization.UUIDPtr(serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0")), Name: common.StringPtr("tl"), TaskType: common.Int64Ptr(1), }).Return(int64(0), err) mockTx.EXPECT().IsNotFoundError(err).Return(true) mockTx.EXPECT().Rollback().Return(nil) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockTx := sqlplugin.NewMockTx(ctrl) mockParser := serialization.NewMockParser(ctrl) store := &sqlTaskStore{ sqlStore: sqlStore{db: mockDB, parser: mockParser}, } tc.mockSetup(mockDB, mockTx, mockParser) got, err := store.CreateTasks(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestGetTasks(t *testing.T) { testCases := []struct { name string req *persistence.GetTasksRequest mockSetup func(*sqlplugin.MockDB, *serialization.MockParser) want *persistence.GetTasksResponse wantErr bool }{ { name: "Success case", req: &persistence.GetTasksRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, ReadLevel: 10, MaxReadLevel: common.Int64Ptr(9999), BatchSize: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().SelectFromTasks(gomock.Any(), &sqlplugin.TasksFilter{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), TaskListName: "tl", TaskType: 0, MinTaskID: common.Int64Ptr(10), MaxTaskID: common.Int64Ptr(9999), PageSize: common.IntPtr(1), }).Return([]sqlplugin.TasksRow{ { TaskID: 888, Data: []byte(`task`), DataEncoding: "task", }, }, nil) mockParser.EXPECT().TaskInfoFromBlob([]byte(`task`), "task").Return(&serialization.TaskInfo{ WorkflowID: "test", RunID: serialization.MustParseUUID("b9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), ScheduleID: 7, ExpiryTimestamp: time.Unix(9, 0), CreatedTimestamp: time.Unix(8, 7), PartitionConfig: map[string]string{"a": "b"}, }, nil) }, want: &persistence.GetTasksResponse{ Tasks: []*persistence.TaskInfo{ { DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", WorkflowID: "test", RunID: "b9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskID: 888, ScheduleID: 7, Expiry: time.Unix(9, 0), CreatedTime: time.Unix(8, 7), PartitionConfig: map[string]string{"a": "b"}, }, }, }, wantErr: false, }, { name: "Error case", req: &persistence.GetTasksRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskList: "tl", TaskType: 0, ReadLevel: 10, MaxReadLevel: common.Int64Ptr(9999), BatchSize: 1, }, mockSetup: func(mockDB *sqlplugin.MockDB, mockParser *serialization.MockParser) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) err := errors.New("some error") mockDB.EXPECT().SelectFromTasks(gomock.Any(), &sqlplugin.TasksFilter{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), TaskListName: "tl", TaskType: 0, MinTaskID: common.Int64Ptr(10), MaxTaskID: common.Int64Ptr(9999), PageSize: common.IntPtr(1), }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) mockParser := serialization.NewMockParser(ctrl) store := &sqlTaskStore{ sqlStore: sqlStore{db: mockDB, parser: mockParser}, } tc.mockSetup(mockDB, mockParser) got, err := store.GetTasks(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestCompleteTask(t *testing.T) { testCases := []struct { name string req *persistence.CompleteTaskRequest mockSetup func(*sqlplugin.MockDB) wantErr bool }{ { name: "Success case", req: &persistence.CompleteTaskRequest{ TaskList: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 0, }, TaskID: 1001, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().DeleteFromTasks(gomock.Any(), &sqlplugin.TasksFilter{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), TaskListName: "tl", TaskType: 0, TaskID: common.Int64Ptr(1001), }).Return(&sqlResult{rowsAffected: 1}, nil) }, wantErr: false, }, { name: "Error case", req: &persistence.CompleteTaskRequest{ TaskList: &persistence.TaskListInfo{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", Name: "tl", TaskType: 0, }, TaskID: 1001, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) err := errors.New("some error") mockDB.EXPECT().DeleteFromTasks(gomock.Any(), &sqlplugin.TasksFilter{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), TaskListName: "tl", TaskType: 0, TaskID: common.Int64Ptr(1001), }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store := &sqlTaskStore{ sqlStore: sqlStore{db: mockDB}, } tc.mockSetup(mockDB) err := store.CompleteTask(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestCompleteTaskLessThan(t *testing.T) { testCases := []struct { name string req *persistence.CompleteTasksLessThanRequest mockSetup func(*sqlplugin.MockDB) want *persistence.CompleteTasksLessThanResponse wantErr bool }{ { name: "Success case", req: &persistence.CompleteTasksLessThanRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskListName: "tl", TaskType: 0, TaskID: 1001, Limit: 100, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) mockDB.EXPECT().DeleteFromTasks(gomock.Any(), &sqlplugin.TasksFilter{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), TaskListName: "tl", TaskType: 0, TaskIDLessThanEquals: common.Int64Ptr(1001), Limit: common.IntPtr(100), }).Return(&sqlResult{rowsAffected: 100}, nil) }, want: &persistence.CompleteTasksLessThanResponse{TasksCompleted: 100}, wantErr: false, }, { name: "Error case", req: &persistence.CompleteTasksLessThanRequest{ DomainID: "c9488dc7-20b2-44c3-b2e4-bfea5af62ac0", TaskListName: "tl", TaskType: 0, TaskID: 1001, Limit: 100, }, mockSetup: func(mockDB *sqlplugin.MockDB) { mockDB.EXPECT().GetTotalNumDBShards().Return(1) err := errors.New("some error") mockDB.EXPECT().DeleteFromTasks(gomock.Any(), &sqlplugin.TasksFilter{ ShardID: 0, DomainID: serialization.MustParseUUID("c9488dc7-20b2-44c3-b2e4-bfea5af62ac0"), TaskListName: "tl", TaskType: 0, TaskIDLessThanEquals: common.Int64Ptr(1001), Limit: common.IntPtr(100), }).Return(nil, err) mockDB.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDB := sqlplugin.NewMockDB(ctrl) store := &sqlTaskStore{ sqlStore: sqlStore{db: mockDB}, } tc.mockSetup(mockDB) got, err := store.CompleteTasksLessThan(context.Background(), tc.req) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.want, got, "Unexpected result for test case") } }) } } func TestHelp(t *testing.T) { shard := sqlplugin.GetDBShardIDFromDomainIDAndTasklist("c4b5cb22-c213-4812-bb4a-fc1ade5405ef", "pgtasklist", 16384) println("shard: ", shard) // BgAKAAAKAAwAAAAABQ+kWAoADgAAAAAAAAAACgAQGB6uFsU9cvEMABIKAAoAAAAAAAAAAQgADAAAAAAIAA4AAAAAAAA= dc := &persistence.DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), } parser, err := serialization.NewParser(dc) require.NoError(t, err) data, err := base64.StdEncoding.DecodeString("BgAKAAAKAAwAAAAABGGFYAoADgAAAAAAAAAACgAQGB6uGPaVqOUMABIKAAoAAAAAAAAAAQgADAAAAAIIAA4AAAACAAA=") require.NoError(t, err) info, err := parser.TaskListInfoFromBlob(data, "thriftrw") require.NoError(t, err) fmt.Printf("info: %v", info) } ================================================ FILE: common/persistence/sql/sql_test_utils.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "errors" "fmt" "io/ioutil" "log" "os" "strings" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence/persistence-tests/testcluster" "github.com/uber/cadence/environment" ) // testCluster allows executing cassandra operations in testing. type testCluster struct { dbName string schemaDir string cfg config.SQL } var _ testcluster.PersistenceTestCluster = (*testCluster)(nil) // NewTestCluster returns a new SQL test cluster func NewTestCluster(pluginName, dbName, username, password, host string, port int, schemaDir string) (testcluster.PersistenceTestCluster, error) { var result testCluster var err error if port == 0 { port, err = environment.GetMySQLPort() if err != nil { return nil, err } } if schemaDir == "" { return nil, errors.New("schemaDir is empty") } result.dbName = dbName result.schemaDir = schemaDir result.cfg = config.SQL{ User: username, Password: password, ConnectAddr: fmt.Sprintf("%v:%v", host, port), ConnectProtocol: "tcp", PluginName: pluginName, DatabaseName: dbName, NumShards: 4, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, } return &result, nil } // DatabaseName from PersistenceTestCluster interface func (s *testCluster) DatabaseName() string { return s.dbName } // SetupTestDatabase from PersistenceTestCluster interface func (s *testCluster) SetupTestDatabase() { s.createDatabase() schemaDir := s.schemaDir + "/" if !strings.HasPrefix(schemaDir, "/") && !strings.HasPrefix(schemaDir, "../") { cadencePackageDir, err := getCadencePackageDir() if err != nil { log.Fatal(err) } schemaDir = cadencePackageDir + schemaDir } s.loadSchema([]string{"schema.sql"}, schemaDir) s.loadVisibilitySchema([]string{"schema.sql"}, schemaDir) } // Config returns the persistence config for connecting to this test cluster func (s *testCluster) Config() config.Persistence { cfg := s.cfg return config.Persistence{ DefaultStore: "test", VisibilityStore: "test", DataStores: map[string]config.DataStore{ "test": {SQL: &cfg}, }, TransactionSizeLimit: dynamicproperties.GetIntPropertyFn(constants.DefaultTransactionSizeLimit), ErrorInjectionRate: dynamicproperties.GetFloatPropertyFn(0), NumHistoryShards: s.cfg.NumShards, } } // TearDownTestDatabase from PersistenceTestCluster interface func (s *testCluster) TearDownTestDatabase() { s.dropDatabase() } // createDatabase from PersistenceTestCluster interface func (s *testCluster) createDatabase() { if s.cfg.PluginName == "sqlite" { // sqlite doesn't support creating database return } cfg2 := s.cfg // NOTE need to connect with empty name to create new database cfg2.DatabaseName = "" db, err := NewSQLAdminDB(&cfg2) if err != nil { panic(err) } defer func() { err := db.Close() if err != nil { panic(err) } }() err = db.CreateDatabase(s.cfg.DatabaseName) if err != nil { panic(err) } } // dropDatabase from PersistenceTestCluster interface func (s *testCluster) dropDatabase() { if s.cfg.PluginName == "sqlite" { // sqlite doesn't support dropping database return } cfg2 := s.cfg // NOTE need to connect with empty name to drop the database cfg2.DatabaseName = "" db, err := NewSQLAdminDB(&cfg2) if err != nil { panic(err) } defer func() { err := db.Close() if err != nil { panic(err) } }() err = db.DropDatabase(s.cfg.DatabaseName) if err != nil { panic(err) } } // loadSchema from PersistenceTestCluster interface func (s *testCluster) loadSchema(fileNames []string, schemaDir string) { workflowSchemaDir := schemaDir + "/cadence" err := s.loadDatabaseSchema(workflowSchemaDir, fileNames, true) if err != nil { log.Fatal(err) } } // loadVisibilitySchema from PersistenceTestCluster interface func (s *testCluster) loadVisibilitySchema(fileNames []string, schemaDir string) { workflowSchemaDir := schemaDir + "/visibility" err := s.loadDatabaseSchema(workflowSchemaDir, fileNames, true) if err != nil { log.Fatal(err) } } func getCadencePackageDir() (string, error) { cadencePackageDir, err := os.Getwd() if err != nil { panic(err) } cadenceIndex := strings.LastIndex(cadencePackageDir, "cadence/") cadencePackageDir = cadencePackageDir[:cadenceIndex+len("cadence/")] return cadencePackageDir, err } // loadDatabaseSchema loads the schema from the given .sql files on this database func (s *testCluster) loadDatabaseSchema(dir string, fileNames []string, override bool) (err error) { db, err := NewSQLAdminDB(&s.cfg) if err != nil { panic(err) } defer func() { err := db.Close() if err != nil { panic(err) } }() for _, file := range fileNames { // This is only used in tests. Excluding it from security scanners // #nosec content, err := ioutil.ReadFile(dir + "/" + file) if err != nil { return fmt.Errorf("error reading contents of file %v:%v", file, err.Error()) } err = db.ExecSchemaOperationQuery(context.Background(), string(content)) if err != nil { return fmt.Errorf("error loading schema from %v: %v", file, err.Error()) } } return nil } ================================================ FILE: common/persistence/sql/sql_visibility_store.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "encoding/json" "fmt" "time" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) type ( sqlVisibilityStore struct { sqlStore } visibilityPageToken struct { Time time.Time RunID string } ) // NewSQLVisibilityStore creates an instance of ExecutionStore func NewSQLVisibilityStore(cfg config.SQL, logger log.Logger) (p.VisibilityStore, error) { db, err := NewSQLDB(&cfg) if err != nil { return nil, err } return &sqlVisibilityStore{ sqlStore: sqlStore{ db: db, logger: logger, }, }, nil } func (s *sqlVisibilityStore) RecordWorkflowExecutionStarted( ctx context.Context, request *p.InternalRecordWorkflowExecutionStartedRequest, ) error { _, err := s.db.InsertIntoVisibility(ctx, &sqlplugin.VisibilityRow{ DomainID: request.DomainUUID, WorkflowID: request.WorkflowID, RunID: request.RunID, StartTime: request.StartTimestamp, ExecutionTime: request.ExecutionTimestamp, WorkflowTypeName: request.WorkflowTypeName, Memo: request.Memo.Data, Encoding: string(request.Memo.GetEncoding()), IsCron: request.IsCron, CronSchedule: request.CronSchedule, NumClusters: request.NumClusters, UpdateTime: request.UpdateTimestamp, ShardID: request.ShardID, ExecutionStatus: int32(request.ExecutionStatus), ScheduledExecutionTime: request.ScheduledExecutionTime, }) if err != nil { return convertCommonErrors(s.db, "RecordWorkflowExecutionStarted", "", err) } return nil } func (s *sqlVisibilityStore) RecordWorkflowExecutionClosed( ctx context.Context, request *p.InternalRecordWorkflowExecutionClosedRequest, ) error { closeTime := request.CloseTimestamp // Map CloseStatus to ExecutionStatus executionStatus := types.WorkflowExecutionStatusCompleted // default switch request.Status { case types.WorkflowExecutionCloseStatusCompleted: executionStatus = types.WorkflowExecutionStatusCompleted case types.WorkflowExecutionCloseStatusFailed: executionStatus = types.WorkflowExecutionStatusFailed case types.WorkflowExecutionCloseStatusCanceled: executionStatus = types.WorkflowExecutionStatusCanceled case types.WorkflowExecutionCloseStatusTerminated: executionStatus = types.WorkflowExecutionStatusTerminated case types.WorkflowExecutionCloseStatusContinuedAsNew: executionStatus = types.WorkflowExecutionStatusContinuedAsNew case types.WorkflowExecutionCloseStatusTimedOut: executionStatus = types.WorkflowExecutionStatusTimedOut } result, err := s.db.ReplaceIntoVisibility(ctx, &sqlplugin.VisibilityRow{ DomainID: request.DomainUUID, WorkflowID: request.WorkflowID, RunID: request.RunID, StartTime: request.StartTimestamp, ExecutionTime: request.ExecutionTimestamp, WorkflowTypeName: request.WorkflowTypeName, CloseTime: &closeTime, CloseStatus: common.Int32Ptr(int32(*thrift.FromWorkflowExecutionCloseStatus(&request.Status))), HistoryLength: &request.HistoryLength, Memo: request.Memo.Data, Encoding: string(request.Memo.GetEncoding()), IsCron: request.IsCron, CronSchedule: request.CronSchedule, NumClusters: request.NumClusters, UpdateTime: request.UpdateTimestamp, ShardID: request.ShardID, ExecutionStatus: int32(executionStatus), ScheduledExecutionTime: request.ScheduledExecutionTime, }) if err != nil { return convertCommonErrors(s.db, "RecordWorkflowExecutionClosed", "", err) } noRowsAffected, err := result.RowsAffected() if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("RecordWorkflowExecutionClosed rowsAffected error: %v", err), } } if noRowsAffected > 2 { // either adds a new row or deletes old row and adds new row return &types.InternalServiceError{ Message: fmt.Sprintf("RecordWorkflowExecutionClosed unexpected numRows (%v) updated", noRowsAffected), } } return nil } func (s *sqlVisibilityStore) RecordWorkflowExecutionUninitialized( ctx context.Context, request *p.InternalRecordWorkflowExecutionUninitializedRequest, ) error { // temporary: not implemented, only implemented for ES return nil } func (s *sqlVisibilityStore) UpsertWorkflowExecution( _ context.Context, request *p.InternalUpsertWorkflowExecutionRequest, ) error { if p.IsNopUpsertWorkflowRequest(request) { return nil } return p.ErrVisibilityOperationNotSupported } func (s *sqlVisibilityStore) ListOpenWorkflowExecutions( ctx context.Context, request *p.InternalListWorkflowExecutionsRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { return s.listWorkflowExecutions("ListOpenWorkflowExecutions", request.NextPageToken, request.EarliestTime, request.LatestTime, func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) { return s.db.SelectFromVisibility(ctx, &sqlplugin.VisibilityFilter{ DomainID: request.DomainUUID, MinStartTime: &request.EarliestTime, MaxStartTime: &readLevel.Time, RunID: &readLevel.RunID, PageSize: &request.PageSize, }) }) } func (s *sqlVisibilityStore) ListClosedWorkflowExecutions( ctx context.Context, request *p.InternalListWorkflowExecutionsRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { return s.listWorkflowExecutions("ListClosedWorkflowExecutions", request.NextPageToken, request.EarliestTime, request.LatestTime, func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) { return s.db.SelectFromVisibility(ctx, &sqlplugin.VisibilityFilter{ DomainID: request.DomainUUID, MinStartTime: &request.EarliestTime, MaxStartTime: &readLevel.Time, Closed: true, RunID: &readLevel.RunID, PageSize: &request.PageSize, }) }) } func (s *sqlVisibilityStore) ListOpenWorkflowExecutionsByType( ctx context.Context, request *p.InternalListWorkflowExecutionsByTypeRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { return s.listWorkflowExecutions("ListOpenWorkflowExecutionsByType", request.NextPageToken, request.EarliestTime, request.LatestTime, func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) { return s.db.SelectFromVisibility(ctx, &sqlplugin.VisibilityFilter{ DomainID: request.DomainUUID, MinStartTime: &request.EarliestTime, MaxStartTime: &readLevel.Time, RunID: &readLevel.RunID, WorkflowTypeName: &request.WorkflowTypeName, PageSize: &request.PageSize, }) }) } func (s *sqlVisibilityStore) ListClosedWorkflowExecutionsByType( ctx context.Context, request *p.InternalListWorkflowExecutionsByTypeRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { return s.listWorkflowExecutions("ListClosedWorkflowExecutionsByType", request.NextPageToken, request.EarliestTime, request.LatestTime, func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) { return s.db.SelectFromVisibility(ctx, &sqlplugin.VisibilityFilter{ DomainID: request.DomainUUID, MinStartTime: &request.EarliestTime, MaxStartTime: &readLevel.Time, Closed: true, RunID: &readLevel.RunID, WorkflowTypeName: &request.WorkflowTypeName, PageSize: &request.PageSize, }) }) } func (s *sqlVisibilityStore) ListOpenWorkflowExecutionsByWorkflowID( ctx context.Context, request *p.InternalListWorkflowExecutionsByWorkflowIDRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { return s.listWorkflowExecutions("ListOpenWorkflowExecutionsByWorkflowID", request.NextPageToken, request.EarliestTime, request.LatestTime, func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) { return s.db.SelectFromVisibility(ctx, &sqlplugin.VisibilityFilter{ DomainID: request.DomainUUID, MinStartTime: &request.EarliestTime, MaxStartTime: &readLevel.Time, RunID: &readLevel.RunID, WorkflowID: &request.WorkflowID, PageSize: &request.PageSize, }) }) } func (s *sqlVisibilityStore) ListClosedWorkflowExecutionsByWorkflowID( ctx context.Context, request *p.InternalListWorkflowExecutionsByWorkflowIDRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { return s.listWorkflowExecutions("ListClosedWorkflowExecutionsByWorkflowID", request.NextPageToken, request.EarliestTime, request.LatestTime, func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) { return s.db.SelectFromVisibility(ctx, &sqlplugin.VisibilityFilter{ DomainID: request.DomainUUID, MinStartTime: &request.EarliestTime, MaxStartTime: &readLevel.Time, Closed: true, RunID: &readLevel.RunID, WorkflowID: &request.WorkflowID, PageSize: &request.PageSize, }) }) } func (s *sqlVisibilityStore) ListClosedWorkflowExecutionsByStatus( ctx context.Context, request *p.InternalListClosedWorkflowExecutionsByStatusRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { return s.listWorkflowExecutions("ListClosedWorkflowExecutionsByStatus", request.NextPageToken, request.EarliestTime, request.LatestTime, func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) { return s.db.SelectFromVisibility(ctx, &sqlplugin.VisibilityFilter{ DomainID: request.DomainUUID, MinStartTime: &request.EarliestTime, MaxStartTime: &readLevel.Time, Closed: true, RunID: &readLevel.RunID, CloseStatus: common.Int32Ptr(int32(*thrift.FromWorkflowExecutionCloseStatus(&request.Status))), PageSize: &request.PageSize, }) }) } func (s *sqlVisibilityStore) GetClosedWorkflowExecution( ctx context.Context, request *p.InternalGetClosedWorkflowExecutionRequest, ) (*p.InternalGetClosedWorkflowExecutionResponse, error) { execution := request.Execution rows, err := s.db.SelectFromVisibility(ctx, &sqlplugin.VisibilityFilter{ DomainID: request.DomainUUID, Closed: true, RunID: &execution.RunID, }) if err != nil { if err == sql.ErrNoRows { return nil, &types.EntityNotExistsError{ Message: fmt.Sprintf("Workflow execution not found. WorkflowId: %v, RunId: %v", execution.GetWorkflowID(), execution.GetRunID()), } } return nil, convertCommonErrors(s.db, "GetClosedWorkflowExecution", "", err) } rows[0].DomainID = request.DomainUUID rows[0].RunID = execution.GetRunID() rows[0].WorkflowID = execution.GetWorkflowID() return &p.InternalGetClosedWorkflowExecutionResponse{Execution: s.rowToInfo(&rows[0])}, nil } func (s *sqlVisibilityStore) DeleteWorkflowExecution( ctx context.Context, request *p.VisibilityDeleteWorkflowExecutionRequest, ) error { _, err := s.db.DeleteFromVisibility(ctx, &sqlplugin.VisibilityFilter{ DomainID: request.DomainID, RunID: &request.RunID, }) if err != nil { return convertCommonErrors(s.db, "DeleteWorkflowExecution", "", err) } return nil } func (s *sqlVisibilityStore) DeleteUninitializedWorkflowExecution( ctx context.Context, request *p.VisibilityDeleteWorkflowExecutionRequest, ) error { // temporary: not implemented, only implemented for ES return nil } func (s *sqlVisibilityStore) ListWorkflowExecutions( _ context.Context, _ *p.ListWorkflowExecutionsByQueryRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { return nil, p.ErrVisibilityOperationNotSupported } func (s *sqlVisibilityStore) ScanWorkflowExecutions( _ context.Context, _ *p.ListWorkflowExecutionsByQueryRequest, ) (*p.InternalListWorkflowExecutionsResponse, error) { return nil, p.ErrVisibilityOperationNotSupported } func (s *sqlVisibilityStore) CountWorkflowExecutions( _ context.Context, _ *p.CountWorkflowExecutionsRequest, ) (*p.CountWorkflowExecutionsResponse, error) { return nil, p.ErrVisibilityOperationNotSupported } func (s *sqlVisibilityStore) rowToInfo(row *sqlplugin.VisibilityRow) *p.InternalVisibilityWorkflowExecutionInfo { if row.ExecutionTime.UnixNano() == 0 { row.ExecutionTime = row.StartTime } info := &p.InternalVisibilityWorkflowExecutionInfo{ WorkflowID: row.WorkflowID, RunID: row.RunID, TypeName: row.WorkflowTypeName, StartTime: row.StartTime, ExecutionTime: row.ExecutionTime, IsCron: row.IsCron, CronSchedule: row.CronSchedule, NumClusters: row.NumClusters, Memo: p.NewDataBlob(row.Memo, constants.EncodingType(row.Encoding)), UpdateTime: row.UpdateTime, ShardID: row.ShardID, ExecutionStatus: types.WorkflowExecutionStatus(row.ExecutionStatus), ScheduledExecutionTime: row.ScheduledExecutionTime, } if row.CloseStatus != nil { status := workflow.WorkflowExecutionCloseStatus(*row.CloseStatus) info.Status = thrift.ToWorkflowExecutionCloseStatus(&status) info.CloseTime = *row.CloseTime info.HistoryLength = *row.HistoryLength } return info } func (s *sqlVisibilityStore) listWorkflowExecutions(opName string, pageToken []byte, earliestTime time.Time, latestTime time.Time, selectOp func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error)) (*p.InternalListWorkflowExecutionsResponse, error) { var readLevel *visibilityPageToken var err error if len(pageToken) > 0 { readLevel, err = s.deserializePageToken(pageToken) if err != nil { return nil, err } } else { readLevel = &visibilityPageToken{Time: latestTime, RunID: ""} } rows, err := selectOp(readLevel) if err != nil { return nil, convertCommonErrors(s.db, opName, "", err) } if len(rows) == 0 { return &p.InternalListWorkflowExecutionsResponse{}, nil } var infos = make([]*p.InternalVisibilityWorkflowExecutionInfo, len(rows)) for i, row := range rows { infos[i] = s.rowToInfo(&row) } var nextPageToken []byte lastRow := rows[len(rows)-1] lastStartTime := lastRow.StartTime if lastStartTime.Sub(earliestTime).Nanoseconds() > 0 { nextPageToken, err = s.serializePageToken(&visibilityPageToken{ Time: lastStartTime, RunID: lastRow.RunID, }) if err != nil { return nil, err } } return &p.InternalListWorkflowExecutionsResponse{ Executions: infos, NextPageToken: nextPageToken, }, nil } func (s *sqlVisibilityStore) deserializePageToken(data []byte) (*visibilityPageToken, error) { var token visibilityPageToken err := json.Unmarshal(data, &token) return &token, err } func (s *sqlVisibilityStore) serializePageToken(token *visibilityPageToken) ([]byte, error) { data, err := json.Marshal(token) return data, err } ================================================ FILE: common/persistence/sql/sqldriver/connections.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sqldriver import ( "fmt" "github.com/jmoiron/sqlx" "github.com/uber/cadence/common/config" ) type CreateSingleDBConn func(cfg *config.SQL) (*sqlx.DB, error) // CreateDBConnections returns references to logical connections to the underlying SQL databases. // By default when UseMultipleDatabases == false, the returned object is to tied to a single // SQL database and the object can be used to perform CRUD operations on the tables in the database. // If UseMultipleDatabases == true then return connections to all the databases func CreateDBConnections(cfg *config.SQL, createConnFunc CreateSingleDBConn) ([]*sqlx.DB, error) { if !cfg.UseMultipleDatabases { xdb, err := createConnFunc(cfg) if err != nil { return nil, err } return []*sqlx.DB{xdb}, nil } if cfg.NumShards <= 1 || len(cfg.MultipleDatabasesConfig) != cfg.NumShards { return nil, fmt.Errorf("invalid SQL config. NumShards should be > 1 and equal to the length of MultipleDatabasesConfig") } // recover from the original at the end defer func() { cfg.User = "" cfg.Password = "" cfg.DatabaseName = "" cfg.ConnectAddr = "" }() xdbs := make([]*sqlx.DB, cfg.NumShards) for idx, entry := range cfg.MultipleDatabasesConfig { cfg.User = entry.User cfg.Password = entry.Password cfg.DatabaseName = entry.DatabaseName cfg.ConnectAddr = entry.ConnectAddr xdb, err := createConnFunc(cfg) if err != nil { return nil, fmt.Errorf("got error of %v to connect to %v database with config %v", err, idx, cfg) } xdbs[idx] = xdb } return xdbs, nil } ================================================ FILE: common/persistence/sql/sqldriver/driver.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sqldriver import ( "fmt" "github.com/jmoiron/sqlx" ) // NewDriver returns a driver to SQL, either using singleton Driver or sharded Driver func NewDriver(xdbs []*sqlx.DB, tx *sqlx.Tx, dbShardID int) (Driver, error) { if len(xdbs) == 1 { return newSingletonSQLDriver(xdbs[0], tx, dbShardID), nil } if len(xdbs) <= 1 { return nil, fmt.Errorf("invalid number of connection for sharded SQL driver") } // this is the case of multiple database with sharding return newShardedSQLDriver(xdbs, tx, dbShardID), nil } ================================================ FILE: common/persistence/sql/sqldriver/interface.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/common/persistence/sql/sqldriver package sqldriver import ( "context" "database/sql" "github.com/jmoiron/sqlx" ) type ( // Driver interface is an abstraction to query SQL. // The layer is added so that we can have a adapter to support multiple SQL databases behind a single Cadence cluster Driver interface { // shared methods are for both non-transactional (using sqlx.DB) and transactional (using sqlx.Tx) operation -- // if a transaction is started(using BeginTxx), then query are executed in the transaction mode. Otherwise executed in normal mode. commonOfDbAndTx // BeginTxx starts a new transaction in the shard of dbShardID BeginTxx(ctx context.Context, dbShardID int, opts *sql.TxOptions) (*sqlx.Tx, error) // Commit commits the current transaction(started by BeginTxx) Commit() error // Rollback rollbacks the current transaction(started by BeginTxx) Rollback() error // Close closes this driver(and underlying connections) Close() error // ExecDDL executes a DDL query ExecDDL(ctx context.Context, dbShardID int, query string, args ...interface{}) (sql.Result, error) // SelectForSchemaQuery executes a select query for schema(returning multiple rows). SelectForSchemaQuery(dbShardID int, dest interface{}, query string, args ...interface{}) error // GetForSchemaQuery executes a get query for schema(returning single row). GetForSchemaQuery(dbShardID int, dest interface{}, query string, args ...interface{}) error } // the methods can be executed from either a started or transaction(then need to call Commit/Rollback), or without a transaction commonOfDbAndTx interface { ExecContext(ctx context.Context, dbShardID int, query string, args ...interface{}) (sql.Result, error) NamedExecContext(ctx context.Context, dbShardID int, query string, arg interface{}) (sql.Result, error) GetContext(ctx context.Context, dbShardID int, dest interface{}, query string, args ...interface{}) error SelectContext(ctx context.Context, dbShardID int, dest interface{}, query string, args ...interface{}) error } ) ================================================ FILE: common/persistence/sql/sqldriver/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package sqldriver -source interface.go -destination interface_mock.go -self_package github.com/uber/cadence/common/persistence/sql/sqldriver // // Package sqldriver is a generated GoMock package. package sqldriver import ( context "context" sql "database/sql" reflect "reflect" sqlx "github.com/jmoiron/sqlx" gomock "go.uber.org/mock/gomock" ) // MockDriver is a mock of Driver interface. type MockDriver struct { ctrl *gomock.Controller recorder *MockDriverMockRecorder isgomock struct{} } // MockDriverMockRecorder is the mock recorder for MockDriver. type MockDriverMockRecorder struct { mock *MockDriver } // NewMockDriver creates a new mock instance. func NewMockDriver(ctrl *gomock.Controller) *MockDriver { mock := &MockDriver{ctrl: ctrl} mock.recorder = &MockDriverMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDriver) EXPECT() *MockDriverMockRecorder { return m.recorder } // BeginTxx mocks base method. func (m *MockDriver) BeginTxx(ctx context.Context, dbShardID int, opts *sql.TxOptions) (*sqlx.Tx, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BeginTxx", ctx, dbShardID, opts) ret0, _ := ret[0].(*sqlx.Tx) ret1, _ := ret[1].(error) return ret0, ret1 } // BeginTxx indicates an expected call of BeginTxx. func (mr *MockDriverMockRecorder) BeginTxx(ctx, dbShardID, opts any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTxx", reflect.TypeOf((*MockDriver)(nil).BeginTxx), ctx, dbShardID, opts) } // Close mocks base method. func (m *MockDriver) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockDriverMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDriver)(nil).Close)) } // Commit mocks base method. func (m *MockDriver) Commit() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Commit") ret0, _ := ret[0].(error) return ret0 } // Commit indicates an expected call of Commit. func (mr *MockDriverMockRecorder) Commit() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockDriver)(nil).Commit)) } // ExecContext mocks base method. func (m *MockDriver) ExecContext(ctx context.Context, dbShardID int, query string, args ...any) (sql.Result, error) { m.ctrl.T.Helper() varargs := []any{ctx, dbShardID, query} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ExecContext", varargs...) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ExecContext indicates an expected call of ExecContext. func (mr *MockDriverMockRecorder) ExecContext(ctx, dbShardID, query any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, dbShardID, query}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecContext", reflect.TypeOf((*MockDriver)(nil).ExecContext), varargs...) } // ExecDDL mocks base method. func (m *MockDriver) ExecDDL(ctx context.Context, dbShardID int, query string, args ...any) (sql.Result, error) { m.ctrl.T.Helper() varargs := []any{ctx, dbShardID, query} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ExecDDL", varargs...) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ExecDDL indicates an expected call of ExecDDL. func (mr *MockDriverMockRecorder) ExecDDL(ctx, dbShardID, query any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, dbShardID, query}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecDDL", reflect.TypeOf((*MockDriver)(nil).ExecDDL), varargs...) } // GetContext mocks base method. func (m *MockDriver) GetContext(ctx context.Context, dbShardID int, dest any, query string, args ...any) error { m.ctrl.T.Helper() varargs := []any{ctx, dbShardID, dest, query} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetContext", varargs...) ret0, _ := ret[0].(error) return ret0 } // GetContext indicates an expected call of GetContext. func (mr *MockDriverMockRecorder) GetContext(ctx, dbShardID, dest, query any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, dbShardID, dest, query}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContext", reflect.TypeOf((*MockDriver)(nil).GetContext), varargs...) } // GetForSchemaQuery mocks base method. func (m *MockDriver) GetForSchemaQuery(dbShardID int, dest any, query string, args ...any) error { m.ctrl.T.Helper() varargs := []any{dbShardID, dest, query} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetForSchemaQuery", varargs...) ret0, _ := ret[0].(error) return ret0 } // GetForSchemaQuery indicates an expected call of GetForSchemaQuery. func (mr *MockDriverMockRecorder) GetForSchemaQuery(dbShardID, dest, query any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{dbShardID, dest, query}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetForSchemaQuery", reflect.TypeOf((*MockDriver)(nil).GetForSchemaQuery), varargs...) } // NamedExecContext mocks base method. func (m *MockDriver) NamedExecContext(ctx context.Context, dbShardID int, query string, arg any) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NamedExecContext", ctx, dbShardID, query, arg) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // NamedExecContext indicates an expected call of NamedExecContext. func (mr *MockDriverMockRecorder) NamedExecContext(ctx, dbShardID, query, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NamedExecContext", reflect.TypeOf((*MockDriver)(nil).NamedExecContext), ctx, dbShardID, query, arg) } // Rollback mocks base method. func (m *MockDriver) Rollback() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Rollback") ret0, _ := ret[0].(error) return ret0 } // Rollback indicates an expected call of Rollback. func (mr *MockDriverMockRecorder) Rollback() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rollback", reflect.TypeOf((*MockDriver)(nil).Rollback)) } // SelectContext mocks base method. func (m *MockDriver) SelectContext(ctx context.Context, dbShardID int, dest any, query string, args ...any) error { m.ctrl.T.Helper() varargs := []any{ctx, dbShardID, dest, query} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SelectContext", varargs...) ret0, _ := ret[0].(error) return ret0 } // SelectContext indicates an expected call of SelectContext. func (mr *MockDriverMockRecorder) SelectContext(ctx, dbShardID, dest, query any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, dbShardID, dest, query}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectContext", reflect.TypeOf((*MockDriver)(nil).SelectContext), varargs...) } // SelectForSchemaQuery mocks base method. func (m *MockDriver) SelectForSchemaQuery(dbShardID int, dest any, query string, args ...any) error { m.ctrl.T.Helper() varargs := []any{dbShardID, dest, query} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SelectForSchemaQuery", varargs...) ret0, _ := ret[0].(error) return ret0 } // SelectForSchemaQuery indicates an expected call of SelectForSchemaQuery. func (mr *MockDriverMockRecorder) SelectForSchemaQuery(dbShardID, dest, query any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{dbShardID, dest, query}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectForSchemaQuery", reflect.TypeOf((*MockDriver)(nil).SelectForSchemaQuery), varargs...) } // MockcommonOfDbAndTx is a mock of commonOfDbAndTx interface. type MockcommonOfDbAndTx struct { ctrl *gomock.Controller recorder *MockcommonOfDbAndTxMockRecorder isgomock struct{} } // MockcommonOfDbAndTxMockRecorder is the mock recorder for MockcommonOfDbAndTx. type MockcommonOfDbAndTxMockRecorder struct { mock *MockcommonOfDbAndTx } // NewMockcommonOfDbAndTx creates a new mock instance. func NewMockcommonOfDbAndTx(ctrl *gomock.Controller) *MockcommonOfDbAndTx { mock := &MockcommonOfDbAndTx{ctrl: ctrl} mock.recorder = &MockcommonOfDbAndTxMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockcommonOfDbAndTx) EXPECT() *MockcommonOfDbAndTxMockRecorder { return m.recorder } // ExecContext mocks base method. func (m *MockcommonOfDbAndTx) ExecContext(ctx context.Context, dbShardID int, query string, args ...any) (sql.Result, error) { m.ctrl.T.Helper() varargs := []any{ctx, dbShardID, query} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ExecContext", varargs...) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ExecContext indicates an expected call of ExecContext. func (mr *MockcommonOfDbAndTxMockRecorder) ExecContext(ctx, dbShardID, query any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, dbShardID, query}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecContext", reflect.TypeOf((*MockcommonOfDbAndTx)(nil).ExecContext), varargs...) } // GetContext mocks base method. func (m *MockcommonOfDbAndTx) GetContext(ctx context.Context, dbShardID int, dest any, query string, args ...any) error { m.ctrl.T.Helper() varargs := []any{ctx, dbShardID, dest, query} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetContext", varargs...) ret0, _ := ret[0].(error) return ret0 } // GetContext indicates an expected call of GetContext. func (mr *MockcommonOfDbAndTxMockRecorder) GetContext(ctx, dbShardID, dest, query any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, dbShardID, dest, query}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContext", reflect.TypeOf((*MockcommonOfDbAndTx)(nil).GetContext), varargs...) } // NamedExecContext mocks base method. func (m *MockcommonOfDbAndTx) NamedExecContext(ctx context.Context, dbShardID int, query string, arg any) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NamedExecContext", ctx, dbShardID, query, arg) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // NamedExecContext indicates an expected call of NamedExecContext. func (mr *MockcommonOfDbAndTxMockRecorder) NamedExecContext(ctx, dbShardID, query, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NamedExecContext", reflect.TypeOf((*MockcommonOfDbAndTx)(nil).NamedExecContext), ctx, dbShardID, query, arg) } // SelectContext mocks base method. func (m *MockcommonOfDbAndTx) SelectContext(ctx context.Context, dbShardID int, dest any, query string, args ...any) error { m.ctrl.T.Helper() varargs := []any{ctx, dbShardID, dest, query} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SelectContext", varargs...) ret0, _ := ret[0].(error) return ret0 } // SelectContext indicates an expected call of SelectContext. func (mr *MockcommonOfDbAndTxMockRecorder) SelectContext(ctx, dbShardID, dest, query any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, dbShardID, dest, query}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectContext", reflect.TypeOf((*MockcommonOfDbAndTx)(nil).SelectContext), varargs...) } ================================================ FILE: common/persistence/sql/sqldriver/sharded.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sqldriver import ( "context" "database/sql" "fmt" "github.com/jmoiron/sqlx" "go.uber.org/multierr" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) type ( // sharded is the driver querying a group of SQL databases as sharded solution sharded struct { dbs []*sqlx.DB // this is for starting a transaction, or executing any non transaction query tx *sqlx.Tx // this is a reference of a started transaction useTx bool // if tx is not nil, the methods from commonOfDbAndTx should use tx currTxShardID int // which shard is current tx started from } ) // newShardedSQLDriver returns a driver querying a group of SQL databases as sharded solution. // xdbs is the list of connections to the sql instances. The length of the list of the list is the totalNumShards // dbShardID is needed when tx is not nil. It means a started transaction in the shard. func newShardedSQLDriver(xdbs []*sqlx.DB, xtx *sqlx.Tx, dbShardID int) Driver { driver := &sharded{ dbs: xdbs, tx: xtx, } if xtx != nil { driver.useTx = true driver.currTxShardID = dbShardID } return driver } // below are shared by transactional and non-transactional, if s.tx is not nil then use s.tx, otherwise use s.db func (s *sharded) ExecContext(ctx context.Context, dbShardID int, query string, args ...interface{}) (sql.Result, error) { if dbShardID == sqlplugin.DbShardUndefined || dbShardID == sqlplugin.DbAllShards { return nil, fmt.Errorf("invalid dbShardID %v shouldn't be used to ExecContext, there must be a bug", dbShardID) } if s.useTx { if s.currTxShardID != dbShardID { return nil, getUnmatchedTxnError(dbShardID, s.currTxShardID) } return s.tx.ExecContext(ctx, query, args...) } return s.dbs[dbShardID].ExecContext(ctx, query, args...) } func (s *sharded) NamedExecContext(ctx context.Context, dbShardID int, query string, arg interface{}) (sql.Result, error) { if dbShardID == sqlplugin.DbShardUndefined || dbShardID == sqlplugin.DbAllShards { return nil, fmt.Errorf("invalid dbShardID %v shouldn't be used to NamedExecContext, there must be a bug", dbShardID) } if s.useTx { if s.currTxShardID != dbShardID { return nil, getUnmatchedTxnError(dbShardID, s.currTxShardID) } return s.tx.NamedExecContext(ctx, query, arg) } return s.dbs[dbShardID].NamedExecContext(ctx, query, arg) } func (s *sharded) GetContext(ctx context.Context, dbShardID int, dest interface{}, query string, args ...interface{}) error { if dbShardID == sqlplugin.DbShardUndefined || dbShardID == sqlplugin.DbAllShards { return fmt.Errorf("invalid dbShardID %v shouldn't be used to GetContext, there must be a bug", dbShardID) } if s.useTx { if s.currTxShardID != dbShardID { return getUnmatchedTxnError(dbShardID, s.currTxShardID) } return s.tx.GetContext(ctx, dest, query, args...) } return s.dbs[dbShardID].GetContext(ctx, dest, query, args...) } func (s *sharded) SelectContext(ctx context.Context, dbShardID int, dest interface{}, query string, args ...interface{}) error { if dbShardID == sqlplugin.DbShardUndefined || dbShardID == sqlplugin.DbAllShards { return fmt.Errorf("invalid dbShardID %v shouldn't be used to SelectContext, there must be a bug", dbShardID) } if s.useTx { if s.currTxShardID != dbShardID { return getUnmatchedTxnError(dbShardID, s.currTxShardID) } return s.tx.SelectContext(ctx, dest, query, args...) } return s.dbs[dbShardID].SelectContext(ctx, dest, query, args...) } // below are non-transactional methods only func (s *sharded) ExecDDL(ctx context.Context, dbShardID int, query string, args ...interface{}) (sql.Result, error) { // sharded SQL driver doesn't implement any schema operation as it's hard to guarantee the correctness. // schema operation across shards is implemented by application layer return nil, fmt.Errorf("sharded SQL driver shouldn't be used to ExecDDL, there must be a bug") } func (s *sharded) SelectForSchemaQuery(dbShardID int, dest interface{}, query string, args ...interface{}) error { // sharded SQL driver doesn't implement any schema operation as it's hard to guarantee the correctness. // schema operation across shards is implemented by application layer return fmt.Errorf("sharded SQL driver shouldn't be used to SelectForSchemaQuery, there must be a bug") } func (s *sharded) GetForSchemaQuery(dbShardID int, dest interface{}, query string, args ...interface{}) error { // sharded SQL driver doesn't implement any schema operation as it's hard to guarantee the correctness. // schema operation across shards is implemented by application layer return fmt.Errorf("sharded SQL driver shouldn't be used to GetForSchemaQuery, there must be a bug") } func (s *sharded) BeginTxx(ctx context.Context, dbShardID int, opts *sql.TxOptions) (*sqlx.Tx, error) { if dbShardID == sqlplugin.DbShardUndefined || dbShardID == sqlplugin.DbAllShards { return nil, fmt.Errorf("invalid dbShardID %v shouldn't be used to BeginTxx, there must be a bug", dbShardID) } return s.dbs[dbShardID].BeginTxx(ctx, opts) } func (s *sharded) Close() error { var errs []error for _, db := range s.dbs { err := db.Close() if err != nil { errs = append(errs, err) } } if len(errs) > 0 { return multierr.Combine(errs...) } return nil } // below are transactional methods only func (s *sharded) Commit() error { return s.tx.Commit() } func (s *sharded) Rollback() error { return s.tx.Rollback() } func getUnmatchedTxnError(requestShardID, startedShardID int) error { return fmt.Errorf("requested dbShardID %v doesn't match with started transaction shardID %v, must be a bug", requestShardID, startedShardID) } ================================================ FILE: common/persistence/sql/sqldriver/singleton.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sqldriver import ( "context" "database/sql" "github.com/jmoiron/sqlx" ) type ( // singleton is the driver querying a single SQL database, which is the default driver singleton struct { db *sqlx.DB // this is for starting a transaction, or executing any non transaction query tx *sqlx.Tx // this is a reference of a started transaction useTx bool // if tx is not nil, the methods from commonOfDbAndTx should use tx } ) // newSingletonSQLDriver returns a driver querying a single SQL database, which is the default driver // typically dbShardID is needed when tx is not nil, because it means a started transaction in a shard. // But this singleton doesn't have sharding so omitting it. func newSingletonSQLDriver(xdb *sqlx.DB, xtx *sqlx.Tx, _ int) Driver { driver := &singleton{ db: xdb, tx: xtx, } if xtx != nil { driver.useTx = true } return driver } // below are shared by transactional and non-transactional, if s.tx is not nil then use s.tx, otherwise use s.db func (s *singleton) ExecContext(ctx context.Context, _ int, query string, args ...interface{}) (sql.Result, error) { if s.useTx { return s.tx.ExecContext(ctx, query, args...) } return s.db.ExecContext(ctx, query, args...) } func (s *singleton) NamedExecContext(ctx context.Context, _ int, query string, arg interface{}) (sql.Result, error) { if s.useTx { return s.tx.NamedExecContext(ctx, query, arg) } return s.db.NamedExecContext(ctx, query, arg) } func (s *singleton) GetContext(ctx context.Context, _ int, dest interface{}, query string, args ...interface{}) error { if s.useTx { return s.tx.GetContext(ctx, dest, query, args...) } return s.db.GetContext(ctx, dest, query, args...) } func (s *singleton) SelectContext(ctx context.Context, _ int, dest interface{}, query string, args ...interface{}) error { if s.useTx { return s.tx.SelectContext(ctx, dest, query, args...) } return s.db.SelectContext(ctx, dest, query, args...) } // below are non-transactional methods only func (s *singleton) ExecDDL(ctx context.Context, _ int, query string, args ...interface{}) (sql.Result, error) { return s.db.ExecContext(ctx, query, args...) } func (s *singleton) SelectForSchemaQuery(_ int, dest interface{}, query string, args ...interface{}) error { return s.db.Select(dest, query, args...) } func (s *singleton) GetForSchemaQuery(_ int, dest interface{}, query string, args ...interface{}) error { return s.db.Get(dest, query, args...) } func (s *singleton) BeginTxx(ctx context.Context, _ int, opts *sql.TxOptions) (*sqlx.Tx, error) { return s.db.BeginTxx(ctx, opts) } func (s *singleton) Close() error { return s.db.Close() } // below are transactional methods only func (s *singleton) Commit() error { return s.tx.Commit() } func (s *singleton) Rollback() error { return s.tx.Rollback() } ================================================ FILE: common/persistence/sql/sqlplugin/dbSharding.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sqlplugin import ( "github.com/dgryski/go-farm" "github.com/uber/cadence/common/persistence/serialization" ) // This section defines the special dbShardID, they must all below 0 const ( // this means the query need to execute in one shard but the shard should be fixed/static, e.g. for domain, queue storage are single shard DbDefaultShard = 0 // this is should never being used in sharded SQL driver. It is used in admin/schema operation in singleton driver, which ignores all the shardID parameter DbShardUndefined = -1 // this means the query needs to execute in all dbShards in sharded SQL driver (currently not supported) DbAllShards = -2 ) // GetDBShardIDFromHistoryShardID maps historyShardID to a DBShardID func GetDBShardIDFromHistoryShardID(historyShardID int, numDBShards int) int { return historyShardID % numDBShards } // GetDBShardIDFromDomainIDAndTasklist maps to a DBShardID func GetDBShardIDFromDomainIDAndTasklist(domainID, tasklistName string, numDBShards int) int { hash := farm.Hash32([]byte(domainID+"_"+tasklistName)) % uint32(numDBShards) return int(hash) % numDBShards } // GetDBShardIDFromDomainID maps domainID to a DBShardID func GetDBShardIDFromDomainID(domainID string, numDBShards int) int { hash := farm.Hash32([]byte(domainID)) % uint32(numDBShards) return int(hash) % numDBShards } // GetDBShardIDFromTreeID maps treeID to a DBShardID func GetDBShardIDFromTreeID(treeID serialization.UUID, numDBShards int) int { hash := farm.Hash32(treeID) % uint32(numDBShards) return int(hash) % numDBShards } ================================================ FILE: common/persistence/sql/sqlplugin/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go // // Generated by this command: // // mockgen -package sqlplugin -source interfaces.go -destination interface_mock.go -self_package github.com/uber/cadence/common/persistence/sql/sqlplugin // // Package sqlplugin is a generated GoMock package. package sqlplugin import ( context "context" sql "database/sql" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" config "github.com/uber/cadence/common/config" persistence "github.com/uber/cadence/common/persistence" ) // MockPlugin is a mock of Plugin interface. type MockPlugin struct { ctrl *gomock.Controller recorder *MockPluginMockRecorder isgomock struct{} } // MockPluginMockRecorder is the mock recorder for MockPlugin. type MockPluginMockRecorder struct { mock *MockPlugin } // NewMockPlugin creates a new mock instance. func NewMockPlugin(ctrl *gomock.Controller) *MockPlugin { mock := &MockPlugin{ctrl: ctrl} mock.recorder = &MockPluginMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPlugin) EXPECT() *MockPluginMockRecorder { return m.recorder } // CreateAdminDB mocks base method. func (m *MockPlugin) CreateAdminDB(cfg *config.SQL) (AdminDB, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateAdminDB", cfg) ret0, _ := ret[0].(AdminDB) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateAdminDB indicates an expected call of CreateAdminDB. func (mr *MockPluginMockRecorder) CreateAdminDB(cfg any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAdminDB", reflect.TypeOf((*MockPlugin)(nil).CreateAdminDB), cfg) } // CreateDB mocks base method. func (m *MockPlugin) CreateDB(cfg *config.SQL) (DB, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateDB", cfg) ret0, _ := ret[0].(DB) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateDB indicates an expected call of CreateDB. func (mr *MockPluginMockRecorder) CreateDB(cfg any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDB", reflect.TypeOf((*MockPlugin)(nil).CreateDB), cfg) } // MocktableCRUD is a mock of tableCRUD interface. type MocktableCRUD struct { ctrl *gomock.Controller recorder *MocktableCRUDMockRecorder isgomock struct{} } // MocktableCRUDMockRecorder is the mock recorder for MocktableCRUD. type MocktableCRUDMockRecorder struct { mock *MocktableCRUD } // NewMocktableCRUD creates a new mock instance. func NewMocktableCRUD(ctrl *gomock.Controller) *MocktableCRUD { mock := &MocktableCRUD{ctrl: ctrl} mock.recorder = &MocktableCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MocktableCRUD) EXPECT() *MocktableCRUDMockRecorder { return m.recorder } // DeleteFromActivityInfoMaps mocks base method. func (m *MocktableCRUD) DeleteFromActivityInfoMaps(ctx context.Context, filter *ActivityInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromActivityInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromActivityInfoMaps indicates an expected call of DeleteFromActivityInfoMaps. func (mr *MocktableCRUDMockRecorder) DeleteFromActivityInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromActivityInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromActivityInfoMaps), ctx, filter) } // DeleteFromBufferedEvents mocks base method. func (m *MocktableCRUD) DeleteFromBufferedEvents(ctx context.Context, filter *BufferedEventsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromBufferedEvents", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromBufferedEvents indicates an expected call of DeleteFromBufferedEvents. func (mr *MocktableCRUDMockRecorder) DeleteFromBufferedEvents(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromBufferedEvents", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromBufferedEvents), ctx, filter) } // DeleteFromChildExecutionInfoMaps mocks base method. func (m *MocktableCRUD) DeleteFromChildExecutionInfoMaps(ctx context.Context, filter *ChildExecutionInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromChildExecutionInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromChildExecutionInfoMaps indicates an expected call of DeleteFromChildExecutionInfoMaps. func (mr *MocktableCRUDMockRecorder) DeleteFromChildExecutionInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromChildExecutionInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromChildExecutionInfoMaps), ctx, filter) } // DeleteFromCrossClusterTasks mocks base method. func (m *MocktableCRUD) DeleteFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromCrossClusterTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromCrossClusterTasks indicates an expected call of DeleteFromCrossClusterTasks. func (mr *MocktableCRUDMockRecorder) DeleteFromCrossClusterTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromCrossClusterTasks", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromCrossClusterTasks), ctx, filter) } // DeleteFromCurrentExecutions mocks base method. func (m *MocktableCRUD) DeleteFromCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromCurrentExecutions", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromCurrentExecutions indicates an expected call of DeleteFromCurrentExecutions. func (mr *MocktableCRUDMockRecorder) DeleteFromCurrentExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromCurrentExecutions", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromCurrentExecutions), ctx, filter) } // DeleteFromDomain mocks base method. func (m *MocktableCRUD) DeleteFromDomain(ctx context.Context, filter *DomainFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromDomain", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromDomain indicates an expected call of DeleteFromDomain. func (mr *MocktableCRUDMockRecorder) DeleteFromDomain(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromDomain", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromDomain), ctx, filter) } // DeleteFromExecutions mocks base method. func (m *MocktableCRUD) DeleteFromExecutions(ctx context.Context, filter *ExecutionsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromExecutions", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromExecutions indicates an expected call of DeleteFromExecutions. func (mr *MocktableCRUDMockRecorder) DeleteFromExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromExecutions", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromExecutions), ctx, filter) } // DeleteFromHistoryNode mocks base method. func (m *MocktableCRUD) DeleteFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromHistoryNode", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromHistoryNode indicates an expected call of DeleteFromHistoryNode. func (mr *MocktableCRUDMockRecorder) DeleteFromHistoryNode(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromHistoryNode", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromHistoryNode), ctx, filter) } // DeleteFromHistoryTree mocks base method. func (m *MocktableCRUD) DeleteFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromHistoryTree", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromHistoryTree indicates an expected call of DeleteFromHistoryTree. func (mr *MocktableCRUDMockRecorder) DeleteFromHistoryTree(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromHistoryTree", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromHistoryTree), ctx, filter) } // DeleteFromReplicationTasks mocks base method. func (m *MocktableCRUD) DeleteFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromReplicationTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromReplicationTasks indicates an expected call of DeleteFromReplicationTasks. func (mr *MocktableCRUDMockRecorder) DeleteFromReplicationTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromReplicationTasks", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromReplicationTasks), ctx, filter) } // DeleteFromRequestCancelInfoMaps mocks base method. func (m *MocktableCRUD) DeleteFromRequestCancelInfoMaps(ctx context.Context, filter *RequestCancelInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromRequestCancelInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromRequestCancelInfoMaps indicates an expected call of DeleteFromRequestCancelInfoMaps. func (mr *MocktableCRUDMockRecorder) DeleteFromRequestCancelInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromRequestCancelInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromRequestCancelInfoMaps), ctx, filter) } // DeleteFromSignalInfoMaps mocks base method. func (m *MocktableCRUD) DeleteFromSignalInfoMaps(ctx context.Context, filter *SignalInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromSignalInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromSignalInfoMaps indicates an expected call of DeleteFromSignalInfoMaps. func (mr *MocktableCRUDMockRecorder) DeleteFromSignalInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromSignalInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromSignalInfoMaps), ctx, filter) } // DeleteFromSignalsRequestedSets mocks base method. func (m *MocktableCRUD) DeleteFromSignalsRequestedSets(ctx context.Context, filter *SignalsRequestedSetsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromSignalsRequestedSets", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromSignalsRequestedSets indicates an expected call of DeleteFromSignalsRequestedSets. func (mr *MocktableCRUDMockRecorder) DeleteFromSignalsRequestedSets(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromSignalsRequestedSets", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromSignalsRequestedSets), ctx, filter) } // DeleteFromTaskLists mocks base method. func (m *MocktableCRUD) DeleteFromTaskLists(ctx context.Context, filter *TaskListsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTaskLists", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTaskLists indicates an expected call of DeleteFromTaskLists. func (mr *MocktableCRUDMockRecorder) DeleteFromTaskLists(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTaskLists", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromTaskLists), ctx, filter) } // DeleteFromTasks mocks base method. func (m *MocktableCRUD) DeleteFromTasks(ctx context.Context, filter *TasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTasks indicates an expected call of DeleteFromTasks. func (mr *MocktableCRUDMockRecorder) DeleteFromTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTasks", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromTasks), ctx, filter) } // DeleteFromTimerInfoMaps mocks base method. func (m *MocktableCRUD) DeleteFromTimerInfoMaps(ctx context.Context, filter *TimerInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTimerInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTimerInfoMaps indicates an expected call of DeleteFromTimerInfoMaps. func (mr *MocktableCRUDMockRecorder) DeleteFromTimerInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTimerInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromTimerInfoMaps), ctx, filter) } // DeleteFromTimerTasks mocks base method. func (m *MocktableCRUD) DeleteFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTimerTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTimerTasks indicates an expected call of DeleteFromTimerTasks. func (mr *MocktableCRUDMockRecorder) DeleteFromTimerTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTimerTasks", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromTimerTasks), ctx, filter) } // DeleteFromTransferTasks mocks base method. func (m *MocktableCRUD) DeleteFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTransferTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTransferTasks indicates an expected call of DeleteFromTransferTasks. func (mr *MocktableCRUDMockRecorder) DeleteFromTransferTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTransferTasks", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromTransferTasks), ctx, filter) } // DeleteFromVisibility mocks base method. func (m *MocktableCRUD) DeleteFromVisibility(ctx context.Context, filter *VisibilityFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromVisibility", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromVisibility indicates an expected call of DeleteFromVisibility. func (mr *MocktableCRUDMockRecorder) DeleteFromVisibility(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromVisibility", reflect.TypeOf((*MocktableCRUD)(nil).DeleteFromVisibility), ctx, filter) } // DeleteMessage mocks base method. func (m *MocktableCRUD) DeleteMessage(ctx context.Context, queueType persistence.QueueType, messageID int64) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessage", ctx, queueType, messageID) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteMessage indicates an expected call of DeleteMessage. func (mr *MocktableCRUDMockRecorder) DeleteMessage(ctx, queueType, messageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessage", reflect.TypeOf((*MocktableCRUD)(nil).DeleteMessage), ctx, queueType, messageID) } // DeleteMessageFromReplicationTasksDLQ mocks base method. func (m *MocktableCRUD) DeleteMessageFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessageFromReplicationTasksDLQ", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteMessageFromReplicationTasksDLQ indicates an expected call of DeleteMessageFromReplicationTasksDLQ. func (mr *MocktableCRUDMockRecorder) DeleteMessageFromReplicationTasksDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessageFromReplicationTasksDLQ", reflect.TypeOf((*MocktableCRUD)(nil).DeleteMessageFromReplicationTasksDLQ), ctx, filter) } // DeleteMessagesBefore mocks base method. func (m *MocktableCRUD) DeleteMessagesBefore(ctx context.Context, queueType persistence.QueueType, messageID int64) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessagesBefore", ctx, queueType, messageID) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteMessagesBefore indicates an expected call of DeleteMessagesBefore. func (mr *MocktableCRUDMockRecorder) DeleteMessagesBefore(ctx, queueType, messageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessagesBefore", reflect.TypeOf((*MocktableCRUD)(nil).DeleteMessagesBefore), ctx, queueType, messageID) } // GetAckLevels mocks base method. func (m *MocktableCRUD) GetAckLevels(ctx context.Context, queueType persistence.QueueType, forUpdate bool) (map[string]int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAckLevels", ctx, queueType, forUpdate) ret0, _ := ret[0].(map[string]int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAckLevels indicates an expected call of GetAckLevels. func (mr *MocktableCRUDMockRecorder) GetAckLevels(ctx, queueType, forUpdate any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAckLevels", reflect.TypeOf((*MocktableCRUD)(nil).GetAckLevels), ctx, queueType, forUpdate) } // GetAllHistoryTreeBranches mocks base method. func (m *MocktableCRUD) GetAllHistoryTreeBranches(ctx context.Context, filter *HistoryTreeFilter) ([]HistoryTreeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllHistoryTreeBranches", ctx, filter) ret0, _ := ret[0].([]HistoryTreeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAllHistoryTreeBranches indicates an expected call of GetAllHistoryTreeBranches. func (mr *MocktableCRUDMockRecorder) GetAllHistoryTreeBranches(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllHistoryTreeBranches", reflect.TypeOf((*MocktableCRUD)(nil).GetAllHistoryTreeBranches), ctx, filter) } // GetLastEnqueuedMessageIDForUpdate mocks base method. func (m *MocktableCRUD) GetLastEnqueuedMessageIDForUpdate(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLastEnqueuedMessageIDForUpdate", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLastEnqueuedMessageIDForUpdate indicates an expected call of GetLastEnqueuedMessageIDForUpdate. func (mr *MocktableCRUDMockRecorder) GetLastEnqueuedMessageIDForUpdate(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastEnqueuedMessageIDForUpdate", reflect.TypeOf((*MocktableCRUD)(nil).GetLastEnqueuedMessageIDForUpdate), ctx, queueType) } // GetMessagesBetween mocks base method. func (m *MocktableCRUD) GetMessagesBetween(ctx context.Context, queueType persistence.QueueType, firstMessageID, lastMessageID int64, maxRows int) ([]QueueRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMessagesBetween", ctx, queueType, firstMessageID, lastMessageID, maxRows) ret0, _ := ret[0].([]QueueRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMessagesBetween indicates an expected call of GetMessagesBetween. func (mr *MocktableCRUDMockRecorder) GetMessagesBetween(ctx, queueType, firstMessageID, lastMessageID, maxRows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessagesBetween", reflect.TypeOf((*MocktableCRUD)(nil).GetMessagesBetween), ctx, queueType, firstMessageID, lastMessageID, maxRows) } // GetMessagesFromQueue mocks base method. func (m *MocktableCRUD) GetMessagesFromQueue(ctx context.Context, queueType persistence.QueueType, lastMessageID int64, maxRows int) ([]QueueRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMessagesFromQueue", ctx, queueType, lastMessageID, maxRows) ret0, _ := ret[0].([]QueueRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMessagesFromQueue indicates an expected call of GetMessagesFromQueue. func (mr *MocktableCRUDMockRecorder) GetMessagesFromQueue(ctx, queueType, lastMessageID, maxRows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessagesFromQueue", reflect.TypeOf((*MocktableCRUD)(nil).GetMessagesFromQueue), ctx, queueType, lastMessageID, maxRows) } // GetOrphanTasks mocks base method. func (m *MocktableCRUD) GetOrphanTasks(ctx context.Context, filter *OrphanTasksFilter) ([]TaskKeyRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrphanTasks", ctx, filter) ret0, _ := ret[0].([]TaskKeyRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetOrphanTasks indicates an expected call of GetOrphanTasks. func (mr *MocktableCRUDMockRecorder) GetOrphanTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrphanTasks", reflect.TypeOf((*MocktableCRUD)(nil).GetOrphanTasks), ctx, filter) } // GetQueueSize mocks base method. func (m *MocktableCRUD) GetQueueSize(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueSize", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetQueueSize indicates an expected call of GetQueueSize. func (mr *MocktableCRUDMockRecorder) GetQueueSize(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueSize", reflect.TypeOf((*MocktableCRUD)(nil).GetQueueSize), ctx, queueType) } // GetTasksCount mocks base method. func (m *MocktableCRUD) GetTasksCount(ctx context.Context, filter *TasksFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasksCount", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTasksCount indicates an expected call of GetTasksCount. func (mr *MocktableCRUDMockRecorder) GetTasksCount(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasksCount", reflect.TypeOf((*MocktableCRUD)(nil).GetTasksCount), ctx, filter) } // InsertAckLevel mocks base method. func (m *MocktableCRUD) InsertAckLevel(ctx context.Context, queueType persistence.QueueType, messageID int64, clusterName string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertAckLevel", ctx, queueType, messageID, clusterName) ret0, _ := ret[0].(error) return ret0 } // InsertAckLevel indicates an expected call of InsertAckLevel. func (mr *MocktableCRUDMockRecorder) InsertAckLevel(ctx, queueType, messageID, clusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertAckLevel", reflect.TypeOf((*MocktableCRUD)(nil).InsertAckLevel), ctx, queueType, messageID, clusterName) } // InsertConfig mocks base method. func (m *MocktableCRUD) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertConfig", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertConfig indicates an expected call of InsertConfig. func (mr *MocktableCRUDMockRecorder) InsertConfig(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertConfig", reflect.TypeOf((*MocktableCRUD)(nil).InsertConfig), ctx, row) } // InsertIntoBufferedEvents mocks base method. func (m *MocktableCRUD) InsertIntoBufferedEvents(ctx context.Context, rows []BufferedEventsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoBufferedEvents", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoBufferedEvents indicates an expected call of InsertIntoBufferedEvents. func (mr *MocktableCRUDMockRecorder) InsertIntoBufferedEvents(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoBufferedEvents", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoBufferedEvents), ctx, rows) } // InsertIntoCrossClusterTasks mocks base method. func (m *MocktableCRUD) InsertIntoCrossClusterTasks(ctx context.Context, rows []CrossClusterTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoCrossClusterTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoCrossClusterTasks indicates an expected call of InsertIntoCrossClusterTasks. func (mr *MocktableCRUDMockRecorder) InsertIntoCrossClusterTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoCrossClusterTasks", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoCrossClusterTasks), ctx, rows) } // InsertIntoCurrentExecutions mocks base method. func (m *MocktableCRUD) InsertIntoCurrentExecutions(ctx context.Context, row *CurrentExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoCurrentExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoCurrentExecutions indicates an expected call of InsertIntoCurrentExecutions. func (mr *MocktableCRUDMockRecorder) InsertIntoCurrentExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoCurrentExecutions", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoCurrentExecutions), ctx, row) } // InsertIntoDomain mocks base method. func (m *MocktableCRUD) InsertIntoDomain(ctx context.Context, rows *DomainRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoDomain", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoDomain indicates an expected call of InsertIntoDomain. func (mr *MocktableCRUDMockRecorder) InsertIntoDomain(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoDomain", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoDomain), ctx, rows) } // InsertIntoDomainAuditLog mocks base method. func (m *MocktableCRUD) InsertIntoDomainAuditLog(ctx context.Context, row *DomainAuditLogRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoDomainAuditLog", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoDomainAuditLog indicates an expected call of InsertIntoDomainAuditLog. func (mr *MocktableCRUDMockRecorder) InsertIntoDomainAuditLog(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoDomainAuditLog", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoDomainAuditLog), ctx, row) } // InsertIntoExecutions mocks base method. func (m *MocktableCRUD) InsertIntoExecutions(ctx context.Context, row *ExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoExecutions indicates an expected call of InsertIntoExecutions. func (mr *MocktableCRUDMockRecorder) InsertIntoExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoExecutions", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoExecutions), ctx, row) } // InsertIntoHistoryNode mocks base method. func (m *MocktableCRUD) InsertIntoHistoryNode(ctx context.Context, row *HistoryNodeRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoHistoryNode", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoHistoryNode indicates an expected call of InsertIntoHistoryNode. func (mr *MocktableCRUDMockRecorder) InsertIntoHistoryNode(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoHistoryNode", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoHistoryNode), ctx, row) } // InsertIntoHistoryTree mocks base method. func (m *MocktableCRUD) InsertIntoHistoryTree(ctx context.Context, row *HistoryTreeRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoHistoryTree", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoHistoryTree indicates an expected call of InsertIntoHistoryTree. func (mr *MocktableCRUDMockRecorder) InsertIntoHistoryTree(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoHistoryTree", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoHistoryTree), ctx, row) } // InsertIntoQueue mocks base method. func (m *MocktableCRUD) InsertIntoQueue(ctx context.Context, row *QueueRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoQueue", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoQueue indicates an expected call of InsertIntoQueue. func (mr *MocktableCRUDMockRecorder) InsertIntoQueue(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoQueue", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoQueue), ctx, row) } // InsertIntoReplicationTasks mocks base method. func (m *MocktableCRUD) InsertIntoReplicationTasks(ctx context.Context, rows []ReplicationTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoReplicationTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoReplicationTasks indicates an expected call of InsertIntoReplicationTasks. func (mr *MocktableCRUDMockRecorder) InsertIntoReplicationTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoReplicationTasks", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoReplicationTasks), ctx, rows) } // InsertIntoReplicationTasksDLQ mocks base method. func (m *MocktableCRUD) InsertIntoReplicationTasksDLQ(ctx context.Context, row *ReplicationTaskDLQRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoReplicationTasksDLQ", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoReplicationTasksDLQ indicates an expected call of InsertIntoReplicationTasksDLQ. func (mr *MocktableCRUDMockRecorder) InsertIntoReplicationTasksDLQ(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoReplicationTasksDLQ", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoReplicationTasksDLQ), ctx, row) } // InsertIntoShards mocks base method. func (m *MocktableCRUD) InsertIntoShards(ctx context.Context, rows *ShardsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoShards", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoShards indicates an expected call of InsertIntoShards. func (mr *MocktableCRUDMockRecorder) InsertIntoShards(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoShards", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoShards), ctx, rows) } // InsertIntoSignalsRequestedSets mocks base method. func (m *MocktableCRUD) InsertIntoSignalsRequestedSets(ctx context.Context, rows []SignalsRequestedSetsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoSignalsRequestedSets", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoSignalsRequestedSets indicates an expected call of InsertIntoSignalsRequestedSets. func (mr *MocktableCRUDMockRecorder) InsertIntoSignalsRequestedSets(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoSignalsRequestedSets", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoSignalsRequestedSets), ctx, rows) } // InsertIntoTaskLists mocks base method. func (m *MocktableCRUD) InsertIntoTaskLists(ctx context.Context, row *TaskListsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTaskLists", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTaskLists indicates an expected call of InsertIntoTaskLists. func (mr *MocktableCRUDMockRecorder) InsertIntoTaskLists(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTaskLists", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoTaskLists), ctx, row) } // InsertIntoTaskListsWithTTL mocks base method. func (m *MocktableCRUD) InsertIntoTaskListsWithTTL(ctx context.Context, row *TaskListsRowWithTTL) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTaskListsWithTTL", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTaskListsWithTTL indicates an expected call of InsertIntoTaskListsWithTTL. func (mr *MocktableCRUDMockRecorder) InsertIntoTaskListsWithTTL(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTaskListsWithTTL", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoTaskListsWithTTL), ctx, row) } // InsertIntoTasks mocks base method. func (m *MocktableCRUD) InsertIntoTasks(ctx context.Context, rows []TasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTasks indicates an expected call of InsertIntoTasks. func (mr *MocktableCRUDMockRecorder) InsertIntoTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTasks", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoTasks), ctx, rows) } // InsertIntoTasksWithTTL mocks base method. func (m *MocktableCRUD) InsertIntoTasksWithTTL(ctx context.Context, rows []TasksRowWithTTL) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTasksWithTTL", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTasksWithTTL indicates an expected call of InsertIntoTasksWithTTL. func (mr *MocktableCRUDMockRecorder) InsertIntoTasksWithTTL(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTasksWithTTL", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoTasksWithTTL), ctx, rows) } // InsertIntoTimerTasks mocks base method. func (m *MocktableCRUD) InsertIntoTimerTasks(ctx context.Context, rows []TimerTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTimerTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTimerTasks indicates an expected call of InsertIntoTimerTasks. func (mr *MocktableCRUDMockRecorder) InsertIntoTimerTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTimerTasks", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoTimerTasks), ctx, rows) } // InsertIntoTransferTasks mocks base method. func (m *MocktableCRUD) InsertIntoTransferTasks(ctx context.Context, rows []TransferTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTransferTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTransferTasks indicates an expected call of InsertIntoTransferTasks. func (mr *MocktableCRUDMockRecorder) InsertIntoTransferTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTransferTasks", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoTransferTasks), ctx, rows) } // InsertIntoVisibility mocks base method. func (m *MocktableCRUD) InsertIntoVisibility(ctx context.Context, row *VisibilityRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoVisibility", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoVisibility indicates an expected call of InsertIntoVisibility. func (mr *MocktableCRUDMockRecorder) InsertIntoVisibility(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoVisibility", reflect.TypeOf((*MocktableCRUD)(nil).InsertIntoVisibility), ctx, row) } // LockCurrentExecutions mocks base method. func (m *MocktableCRUD) LockCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (*CurrentExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockCurrentExecutions", ctx, filter) ret0, _ := ret[0].(*CurrentExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // LockCurrentExecutions indicates an expected call of LockCurrentExecutions. func (mr *MocktableCRUDMockRecorder) LockCurrentExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockCurrentExecutions", reflect.TypeOf((*MocktableCRUD)(nil).LockCurrentExecutions), ctx, filter) } // LockCurrentExecutionsJoinExecutions mocks base method. func (m *MocktableCRUD) LockCurrentExecutionsJoinExecutions(ctx context.Context, filter *CurrentExecutionsFilter) ([]CurrentExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockCurrentExecutionsJoinExecutions", ctx, filter) ret0, _ := ret[0].([]CurrentExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // LockCurrentExecutionsJoinExecutions indicates an expected call of LockCurrentExecutionsJoinExecutions. func (mr *MocktableCRUDMockRecorder) LockCurrentExecutionsJoinExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockCurrentExecutionsJoinExecutions", reflect.TypeOf((*MocktableCRUD)(nil).LockCurrentExecutionsJoinExecutions), ctx, filter) } // LockDomainMetadata mocks base method. func (m *MocktableCRUD) LockDomainMetadata(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockDomainMetadata", ctx) ret0, _ := ret[0].(error) return ret0 } // LockDomainMetadata indicates an expected call of LockDomainMetadata. func (mr *MocktableCRUDMockRecorder) LockDomainMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockDomainMetadata", reflect.TypeOf((*MocktableCRUD)(nil).LockDomainMetadata), ctx) } // LockTaskLists mocks base method. func (m *MocktableCRUD) LockTaskLists(ctx context.Context, filter *TaskListsFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockTaskLists", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // LockTaskLists indicates an expected call of LockTaskLists. func (mr *MocktableCRUDMockRecorder) LockTaskLists(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockTaskLists", reflect.TypeOf((*MocktableCRUD)(nil).LockTaskLists), ctx, filter) } // MaxAllowedTTL mocks base method. func (m *MocktableCRUD) MaxAllowedTTL() (*time.Duration, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MaxAllowedTTL") ret0, _ := ret[0].(*time.Duration) ret1, _ := ret[1].(error) return ret0, ret1 } // MaxAllowedTTL indicates an expected call of MaxAllowedTTL. func (mr *MocktableCRUDMockRecorder) MaxAllowedTTL() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxAllowedTTL", reflect.TypeOf((*MocktableCRUD)(nil).MaxAllowedTTL)) } // RangeDeleteFromCrossClusterTasks mocks base method. func (m *MocktableCRUD) RangeDeleteFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromCrossClusterTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromCrossClusterTasks indicates an expected call of RangeDeleteFromCrossClusterTasks. func (mr *MocktableCRUDMockRecorder) RangeDeleteFromCrossClusterTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromCrossClusterTasks", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteFromCrossClusterTasks), ctx, filter) } // RangeDeleteFromReplicationTasks mocks base method. func (m *MocktableCRUD) RangeDeleteFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromReplicationTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromReplicationTasks indicates an expected call of RangeDeleteFromReplicationTasks. func (mr *MocktableCRUDMockRecorder) RangeDeleteFromReplicationTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromReplicationTasks", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteFromReplicationTasks), ctx, filter) } // RangeDeleteFromTimerTasks mocks base method. func (m *MocktableCRUD) RangeDeleteFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromTimerTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromTimerTasks indicates an expected call of RangeDeleteFromTimerTasks. func (mr *MocktableCRUDMockRecorder) RangeDeleteFromTimerTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromTimerTasks", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteFromTimerTasks), ctx, filter) } // RangeDeleteFromTransferTasks mocks base method. func (m *MocktableCRUD) RangeDeleteFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromTransferTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromTransferTasks indicates an expected call of RangeDeleteFromTransferTasks. func (mr *MocktableCRUDMockRecorder) RangeDeleteFromTransferTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromTransferTasks", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteFromTransferTasks), ctx, filter) } // RangeDeleteMessageFromReplicationTasksDLQ mocks base method. func (m *MocktableCRUD) RangeDeleteMessageFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteMessageFromReplicationTasksDLQ", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteMessageFromReplicationTasksDLQ indicates an expected call of RangeDeleteMessageFromReplicationTasksDLQ. func (mr *MocktableCRUDMockRecorder) RangeDeleteMessageFromReplicationTasksDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteMessageFromReplicationTasksDLQ", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteMessageFromReplicationTasksDLQ), ctx, filter) } // RangeDeleteMessages mocks base method. func (m *MocktableCRUD) RangeDeleteMessages(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID, inclusiveEndMessageID int64) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteMessages", ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteMessages indicates an expected call of RangeDeleteMessages. func (mr *MocktableCRUDMockRecorder) RangeDeleteMessages(ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteMessages", reflect.TypeOf((*MocktableCRUD)(nil).RangeDeleteMessages), ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) } // ReadLockExecutions mocks base method. func (m *MocktableCRUD) ReadLockExecutions(ctx context.Context, filter *ExecutionsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadLockExecutions", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadLockExecutions indicates an expected call of ReadLockExecutions. func (mr *MocktableCRUDMockRecorder) ReadLockExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadLockExecutions", reflect.TypeOf((*MocktableCRUD)(nil).ReadLockExecutions), ctx, filter) } // ReadLockShards mocks base method. func (m *MocktableCRUD) ReadLockShards(ctx context.Context, filter *ShardsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadLockShards", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadLockShards indicates an expected call of ReadLockShards. func (mr *MocktableCRUDMockRecorder) ReadLockShards(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadLockShards", reflect.TypeOf((*MocktableCRUD)(nil).ReadLockShards), ctx, filter) } // ReplaceIntoActivityInfoMaps mocks base method. func (m *MocktableCRUD) ReplaceIntoActivityInfoMaps(ctx context.Context, rows []ActivityInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoActivityInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoActivityInfoMaps indicates an expected call of ReplaceIntoActivityInfoMaps. func (mr *MocktableCRUDMockRecorder) ReplaceIntoActivityInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoActivityInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).ReplaceIntoActivityInfoMaps), ctx, rows) } // ReplaceIntoChildExecutionInfoMaps mocks base method. func (m *MocktableCRUD) ReplaceIntoChildExecutionInfoMaps(ctx context.Context, rows []ChildExecutionInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoChildExecutionInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoChildExecutionInfoMaps indicates an expected call of ReplaceIntoChildExecutionInfoMaps. func (mr *MocktableCRUDMockRecorder) ReplaceIntoChildExecutionInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoChildExecutionInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).ReplaceIntoChildExecutionInfoMaps), ctx, rows) } // ReplaceIntoRequestCancelInfoMaps mocks base method. func (m *MocktableCRUD) ReplaceIntoRequestCancelInfoMaps(ctx context.Context, rows []RequestCancelInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoRequestCancelInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoRequestCancelInfoMaps indicates an expected call of ReplaceIntoRequestCancelInfoMaps. func (mr *MocktableCRUDMockRecorder) ReplaceIntoRequestCancelInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoRequestCancelInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).ReplaceIntoRequestCancelInfoMaps), ctx, rows) } // ReplaceIntoSignalInfoMaps mocks base method. func (m *MocktableCRUD) ReplaceIntoSignalInfoMaps(ctx context.Context, rows []SignalInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoSignalInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoSignalInfoMaps indicates an expected call of ReplaceIntoSignalInfoMaps. func (mr *MocktableCRUDMockRecorder) ReplaceIntoSignalInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoSignalInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).ReplaceIntoSignalInfoMaps), ctx, rows) } // ReplaceIntoTimerInfoMaps mocks base method. func (m *MocktableCRUD) ReplaceIntoTimerInfoMaps(ctx context.Context, rows []TimerInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoTimerInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoTimerInfoMaps indicates an expected call of ReplaceIntoTimerInfoMaps. func (mr *MocktableCRUDMockRecorder) ReplaceIntoTimerInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoTimerInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).ReplaceIntoTimerInfoMaps), ctx, rows) } // ReplaceIntoVisibility mocks base method. func (m *MocktableCRUD) ReplaceIntoVisibility(ctx context.Context, row *VisibilityRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoVisibility", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoVisibility indicates an expected call of ReplaceIntoVisibility. func (mr *MocktableCRUDMockRecorder) ReplaceIntoVisibility(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoVisibility", reflect.TypeOf((*MocktableCRUD)(nil).ReplaceIntoVisibility), ctx, row) } // SelectFromActivityInfoMaps mocks base method. func (m *MocktableCRUD) SelectFromActivityInfoMaps(ctx context.Context, filter *ActivityInfoMapsFilter) ([]ActivityInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromActivityInfoMaps", ctx, filter) ret0, _ := ret[0].([]ActivityInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromActivityInfoMaps indicates an expected call of SelectFromActivityInfoMaps. func (mr *MocktableCRUDMockRecorder) SelectFromActivityInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromActivityInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromActivityInfoMaps), ctx, filter) } // SelectFromBufferedEvents mocks base method. func (m *MocktableCRUD) SelectFromBufferedEvents(ctx context.Context, filter *BufferedEventsFilter) ([]BufferedEventsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromBufferedEvents", ctx, filter) ret0, _ := ret[0].([]BufferedEventsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromBufferedEvents indicates an expected call of SelectFromBufferedEvents. func (mr *MocktableCRUDMockRecorder) SelectFromBufferedEvents(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromBufferedEvents", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromBufferedEvents), ctx, filter) } // SelectFromChildExecutionInfoMaps mocks base method. func (m *MocktableCRUD) SelectFromChildExecutionInfoMaps(ctx context.Context, filter *ChildExecutionInfoMapsFilter) ([]ChildExecutionInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromChildExecutionInfoMaps", ctx, filter) ret0, _ := ret[0].([]ChildExecutionInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromChildExecutionInfoMaps indicates an expected call of SelectFromChildExecutionInfoMaps. func (mr *MocktableCRUDMockRecorder) SelectFromChildExecutionInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromChildExecutionInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromChildExecutionInfoMaps), ctx, filter) } // SelectFromCrossClusterTasks mocks base method. func (m *MocktableCRUD) SelectFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) ([]CrossClusterTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromCrossClusterTasks", ctx, filter) ret0, _ := ret[0].([]CrossClusterTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromCrossClusterTasks indicates an expected call of SelectFromCrossClusterTasks. func (mr *MocktableCRUDMockRecorder) SelectFromCrossClusterTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromCrossClusterTasks", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromCrossClusterTasks), ctx, filter) } // SelectFromCurrentExecutions mocks base method. func (m *MocktableCRUD) SelectFromCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (*CurrentExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromCurrentExecutions", ctx, filter) ret0, _ := ret[0].(*CurrentExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromCurrentExecutions indicates an expected call of SelectFromCurrentExecutions. func (mr *MocktableCRUDMockRecorder) SelectFromCurrentExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromCurrentExecutions", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromCurrentExecutions), ctx, filter) } // SelectFromDomain mocks base method. func (m *MocktableCRUD) SelectFromDomain(ctx context.Context, filter *DomainFilter) ([]DomainRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromDomain", ctx, filter) ret0, _ := ret[0].([]DomainRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromDomain indicates an expected call of SelectFromDomain. func (mr *MocktableCRUDMockRecorder) SelectFromDomain(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromDomain", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromDomain), ctx, filter) } // SelectFromDomainAuditLogs mocks base method. func (m *MocktableCRUD) SelectFromDomainAuditLogs(ctx context.Context, filter *DomainAuditLogFilter) ([]*DomainAuditLogRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromDomainAuditLogs", ctx, filter) ret0, _ := ret[0].([]*DomainAuditLogRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromDomainAuditLogs indicates an expected call of SelectFromDomainAuditLogs. func (mr *MocktableCRUDMockRecorder) SelectFromDomainAuditLogs(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromDomainAuditLogs", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromDomainAuditLogs), ctx, filter) } // SelectFromDomainMetadata mocks base method. func (m *MocktableCRUD) SelectFromDomainMetadata(ctx context.Context) (*DomainMetadataRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromDomainMetadata", ctx) ret0, _ := ret[0].(*DomainMetadataRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromDomainMetadata indicates an expected call of SelectFromDomainMetadata. func (mr *MocktableCRUDMockRecorder) SelectFromDomainMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromDomainMetadata", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromDomainMetadata), ctx) } // SelectFromExecutions mocks base method. func (m *MocktableCRUD) SelectFromExecutions(ctx context.Context, filter *ExecutionsFilter) ([]ExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromExecutions", ctx, filter) ret0, _ := ret[0].([]ExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromExecutions indicates an expected call of SelectFromExecutions. func (mr *MocktableCRUDMockRecorder) SelectFromExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromExecutions", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromExecutions), ctx, filter) } // SelectFromHistoryNode mocks base method. func (m *MocktableCRUD) SelectFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) ([]HistoryNodeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryNode", ctx, filter) ret0, _ := ret[0].([]HistoryNodeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromHistoryNode indicates an expected call of SelectFromHistoryNode. func (mr *MocktableCRUDMockRecorder) SelectFromHistoryNode(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryNode", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromHistoryNode), ctx, filter) } // SelectFromHistoryTree mocks base method. func (m *MocktableCRUD) SelectFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) ([]HistoryTreeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryTree", ctx, filter) ret0, _ := ret[0].([]HistoryTreeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromHistoryTree indicates an expected call of SelectFromHistoryTree. func (mr *MocktableCRUDMockRecorder) SelectFromHistoryTree(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryTree", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromHistoryTree), ctx, filter) } // SelectFromReplicationDLQ mocks base method. func (m *MocktableCRUD) SelectFromReplicationDLQ(ctx context.Context, filter *ReplicationTaskDLQFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromReplicationDLQ", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromReplicationDLQ indicates an expected call of SelectFromReplicationDLQ. func (mr *MocktableCRUDMockRecorder) SelectFromReplicationDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromReplicationDLQ", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromReplicationDLQ), ctx, filter) } // SelectFromReplicationTasks mocks base method. func (m *MocktableCRUD) SelectFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) ([]ReplicationTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromReplicationTasks", ctx, filter) ret0, _ := ret[0].([]ReplicationTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromReplicationTasks indicates an expected call of SelectFromReplicationTasks. func (mr *MocktableCRUDMockRecorder) SelectFromReplicationTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromReplicationTasks", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromReplicationTasks), ctx, filter) } // SelectFromReplicationTasksDLQ mocks base method. func (m *MocktableCRUD) SelectFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) ([]ReplicationTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromReplicationTasksDLQ", ctx, filter) ret0, _ := ret[0].([]ReplicationTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromReplicationTasksDLQ indicates an expected call of SelectFromReplicationTasksDLQ. func (mr *MocktableCRUDMockRecorder) SelectFromReplicationTasksDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromReplicationTasksDLQ", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromReplicationTasksDLQ), ctx, filter) } // SelectFromRequestCancelInfoMaps mocks base method. func (m *MocktableCRUD) SelectFromRequestCancelInfoMaps(ctx context.Context, filter *RequestCancelInfoMapsFilter) ([]RequestCancelInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromRequestCancelInfoMaps", ctx, filter) ret0, _ := ret[0].([]RequestCancelInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromRequestCancelInfoMaps indicates an expected call of SelectFromRequestCancelInfoMaps. func (mr *MocktableCRUDMockRecorder) SelectFromRequestCancelInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromRequestCancelInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromRequestCancelInfoMaps), ctx, filter) } // SelectFromShards mocks base method. func (m *MocktableCRUD) SelectFromShards(ctx context.Context, filter *ShardsFilter) (*ShardsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromShards", ctx, filter) ret0, _ := ret[0].(*ShardsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromShards indicates an expected call of SelectFromShards. func (mr *MocktableCRUDMockRecorder) SelectFromShards(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromShards", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromShards), ctx, filter) } // SelectFromSignalInfoMaps mocks base method. func (m *MocktableCRUD) SelectFromSignalInfoMaps(ctx context.Context, filter *SignalInfoMapsFilter) ([]SignalInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromSignalInfoMaps", ctx, filter) ret0, _ := ret[0].([]SignalInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromSignalInfoMaps indicates an expected call of SelectFromSignalInfoMaps. func (mr *MocktableCRUDMockRecorder) SelectFromSignalInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromSignalInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromSignalInfoMaps), ctx, filter) } // SelectFromSignalsRequestedSets mocks base method. func (m *MocktableCRUD) SelectFromSignalsRequestedSets(ctx context.Context, filter *SignalsRequestedSetsFilter) ([]SignalsRequestedSetsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromSignalsRequestedSets", ctx, filter) ret0, _ := ret[0].([]SignalsRequestedSetsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromSignalsRequestedSets indicates an expected call of SelectFromSignalsRequestedSets. func (mr *MocktableCRUDMockRecorder) SelectFromSignalsRequestedSets(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromSignalsRequestedSets", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromSignalsRequestedSets), ctx, filter) } // SelectFromTaskLists mocks base method. func (m *MocktableCRUD) SelectFromTaskLists(ctx context.Context, filter *TaskListsFilter) ([]TaskListsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTaskLists", ctx, filter) ret0, _ := ret[0].([]TaskListsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTaskLists indicates an expected call of SelectFromTaskLists. func (mr *MocktableCRUDMockRecorder) SelectFromTaskLists(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTaskLists", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromTaskLists), ctx, filter) } // SelectFromTasks mocks base method. func (m *MocktableCRUD) SelectFromTasks(ctx context.Context, filter *TasksFilter) ([]TasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTasks", ctx, filter) ret0, _ := ret[0].([]TasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTasks indicates an expected call of SelectFromTasks. func (mr *MocktableCRUDMockRecorder) SelectFromTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTasks", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromTasks), ctx, filter) } // SelectFromTimerInfoMaps mocks base method. func (m *MocktableCRUD) SelectFromTimerInfoMaps(ctx context.Context, filter *TimerInfoMapsFilter) ([]TimerInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTimerInfoMaps", ctx, filter) ret0, _ := ret[0].([]TimerInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTimerInfoMaps indicates an expected call of SelectFromTimerInfoMaps. func (mr *MocktableCRUDMockRecorder) SelectFromTimerInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTimerInfoMaps", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromTimerInfoMaps), ctx, filter) } // SelectFromTimerTasks mocks base method. func (m *MocktableCRUD) SelectFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) ([]TimerTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTimerTasks", ctx, filter) ret0, _ := ret[0].([]TimerTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTimerTasks indicates an expected call of SelectFromTimerTasks. func (mr *MocktableCRUDMockRecorder) SelectFromTimerTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTimerTasks", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromTimerTasks), ctx, filter) } // SelectFromTransferTasks mocks base method. func (m *MocktableCRUD) SelectFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) ([]TransferTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTransferTasks", ctx, filter) ret0, _ := ret[0].([]TransferTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTransferTasks indicates an expected call of SelectFromTransferTasks. func (mr *MocktableCRUDMockRecorder) SelectFromTransferTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTransferTasks", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromTransferTasks), ctx, filter) } // SelectFromVisibility mocks base method. func (m *MocktableCRUD) SelectFromVisibility(ctx context.Context, filter *VisibilityFilter) ([]VisibilityRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromVisibility", ctx, filter) ret0, _ := ret[0].([]VisibilityRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromVisibility indicates an expected call of SelectFromVisibility. func (mr *MocktableCRUDMockRecorder) SelectFromVisibility(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromVisibility", reflect.TypeOf((*MocktableCRUD)(nil).SelectFromVisibility), ctx, filter) } // SelectLatestConfig mocks base method. func (m *MocktableCRUD) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectLatestConfig", ctx, rowType) ret0, _ := ret[0].(*persistence.InternalConfigStoreEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectLatestConfig indicates an expected call of SelectLatestConfig. func (mr *MocktableCRUDMockRecorder) SelectLatestConfig(ctx, rowType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectLatestConfig", reflect.TypeOf((*MocktableCRUD)(nil).SelectLatestConfig), ctx, rowType) } // SupportsAsyncTransaction mocks base method. func (m *MocktableCRUD) SupportsAsyncTransaction() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SupportsAsyncTransaction") ret0, _ := ret[0].(bool) return ret0 } // SupportsAsyncTransaction indicates an expected call of SupportsAsyncTransaction. func (mr *MocktableCRUDMockRecorder) SupportsAsyncTransaction() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportsAsyncTransaction", reflect.TypeOf((*MocktableCRUD)(nil).SupportsAsyncTransaction)) } // SupportsTTL mocks base method. func (m *MocktableCRUD) SupportsTTL() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SupportsTTL") ret0, _ := ret[0].(bool) return ret0 } // SupportsTTL indicates an expected call of SupportsTTL. func (mr *MocktableCRUDMockRecorder) SupportsTTL() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportsTTL", reflect.TypeOf((*MocktableCRUD)(nil).SupportsTTL)) } // UpdateAckLevels mocks base method. func (m *MocktableCRUD) UpdateAckLevels(ctx context.Context, queueType persistence.QueueType, clusterAckLevels map[string]int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAckLevels", ctx, queueType, clusterAckLevels) ret0, _ := ret[0].(error) return ret0 } // UpdateAckLevels indicates an expected call of UpdateAckLevels. func (mr *MocktableCRUDMockRecorder) UpdateAckLevels(ctx, queueType, clusterAckLevels any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAckLevels", reflect.TypeOf((*MocktableCRUD)(nil).UpdateAckLevels), ctx, queueType, clusterAckLevels) } // UpdateCurrentExecutions mocks base method. func (m *MocktableCRUD) UpdateCurrentExecutions(ctx context.Context, row *CurrentExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateCurrentExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateCurrentExecutions indicates an expected call of UpdateCurrentExecutions. func (mr *MocktableCRUDMockRecorder) UpdateCurrentExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCurrentExecutions", reflect.TypeOf((*MocktableCRUD)(nil).UpdateCurrentExecutions), ctx, row) } // UpdateDomain mocks base method. func (m *MocktableCRUD) UpdateDomain(ctx context.Context, row *DomainRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomain", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MocktableCRUDMockRecorder) UpdateDomain(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MocktableCRUD)(nil).UpdateDomain), ctx, row) } // UpdateDomainMetadata mocks base method. func (m *MocktableCRUD) UpdateDomainMetadata(ctx context.Context, row *DomainMetadataRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomainMetadata", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomainMetadata indicates an expected call of UpdateDomainMetadata. func (mr *MocktableCRUDMockRecorder) UpdateDomainMetadata(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomainMetadata", reflect.TypeOf((*MocktableCRUD)(nil).UpdateDomainMetadata), ctx, row) } // UpdateExecutions mocks base method. func (m *MocktableCRUD) UpdateExecutions(ctx context.Context, row *ExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateExecutions indicates an expected call of UpdateExecutions. func (mr *MocktableCRUDMockRecorder) UpdateExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateExecutions", reflect.TypeOf((*MocktableCRUD)(nil).UpdateExecutions), ctx, row) } // UpdateShards mocks base method. func (m *MocktableCRUD) UpdateShards(ctx context.Context, row *ShardsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateShards", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateShards indicates an expected call of UpdateShards. func (mr *MocktableCRUDMockRecorder) UpdateShards(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateShards", reflect.TypeOf((*MocktableCRUD)(nil).UpdateShards), ctx, row) } // UpdateTaskLists mocks base method. func (m *MocktableCRUD) UpdateTaskLists(ctx context.Context, row *TaskListsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskLists", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskLists indicates an expected call of UpdateTaskLists. func (mr *MocktableCRUDMockRecorder) UpdateTaskLists(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskLists", reflect.TypeOf((*MocktableCRUD)(nil).UpdateTaskLists), ctx, row) } // UpdateTaskListsWithTTL mocks base method. func (m *MocktableCRUD) UpdateTaskListsWithTTL(ctx context.Context, row *TaskListsRowWithTTL) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskListsWithTTL", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskListsWithTTL indicates an expected call of UpdateTaskListsWithTTL. func (mr *MocktableCRUDMockRecorder) UpdateTaskListsWithTTL(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListsWithTTL", reflect.TypeOf((*MocktableCRUD)(nil).UpdateTaskListsWithTTL), ctx, row) } // WriteLockExecutions mocks base method. func (m *MocktableCRUD) WriteLockExecutions(ctx context.Context, filter *ExecutionsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteLockExecutions", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // WriteLockExecutions indicates an expected call of WriteLockExecutions. func (mr *MocktableCRUDMockRecorder) WriteLockExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteLockExecutions", reflect.TypeOf((*MocktableCRUD)(nil).WriteLockExecutions), ctx, filter) } // WriteLockShards mocks base method. func (m *MocktableCRUD) WriteLockShards(ctx context.Context, filter *ShardsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteLockShards", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // WriteLockShards indicates an expected call of WriteLockShards. func (mr *MocktableCRUDMockRecorder) WriteLockShards(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteLockShards", reflect.TypeOf((*MocktableCRUD)(nil).WriteLockShards), ctx, filter) } // MockadminCRUD is a mock of adminCRUD interface. type MockadminCRUD struct { ctrl *gomock.Controller recorder *MockadminCRUDMockRecorder isgomock struct{} } // MockadminCRUDMockRecorder is the mock recorder for MockadminCRUD. type MockadminCRUDMockRecorder struct { mock *MockadminCRUD } // NewMockadminCRUD creates a new mock instance. func NewMockadminCRUD(ctrl *gomock.Controller) *MockadminCRUD { mock := &MockadminCRUD{ctrl: ctrl} mock.recorder = &MockadminCRUDMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockadminCRUD) EXPECT() *MockadminCRUDMockRecorder { return m.recorder } // CreateDatabase mocks base method. func (m *MockadminCRUD) CreateDatabase(database string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateDatabase", database) ret0, _ := ret[0].(error) return ret0 } // CreateDatabase indicates an expected call of CreateDatabase. func (mr *MockadminCRUDMockRecorder) CreateDatabase(database any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDatabase", reflect.TypeOf((*MockadminCRUD)(nil).CreateDatabase), database) } // CreateSchemaVersionTables mocks base method. func (m *MockadminCRUD) CreateSchemaVersionTables() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateSchemaVersionTables") ret0, _ := ret[0].(error) return ret0 } // CreateSchemaVersionTables indicates an expected call of CreateSchemaVersionTables. func (mr *MockadminCRUDMockRecorder) CreateSchemaVersionTables() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSchemaVersionTables", reflect.TypeOf((*MockadminCRUD)(nil).CreateSchemaVersionTables)) } // DropAllTables mocks base method. func (m *MockadminCRUD) DropAllTables(database string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DropAllTables", database) ret0, _ := ret[0].(error) return ret0 } // DropAllTables indicates an expected call of DropAllTables. func (mr *MockadminCRUDMockRecorder) DropAllTables(database any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropAllTables", reflect.TypeOf((*MockadminCRUD)(nil).DropAllTables), database) } // DropDatabase mocks base method. func (m *MockadminCRUD) DropDatabase(database string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DropDatabase", database) ret0, _ := ret[0].(error) return ret0 } // DropDatabase indicates an expected call of DropDatabase. func (mr *MockadminCRUDMockRecorder) DropDatabase(database any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropDatabase", reflect.TypeOf((*MockadminCRUD)(nil).DropDatabase), database) } // DropTable mocks base method. func (m *MockadminCRUD) DropTable(table string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DropTable", table) ret0, _ := ret[0].(error) return ret0 } // DropTable indicates an expected call of DropTable. func (mr *MockadminCRUDMockRecorder) DropTable(table any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropTable", reflect.TypeOf((*MockadminCRUD)(nil).DropTable), table) } // ExecSchemaOperationQuery mocks base method. func (m *MockadminCRUD) ExecSchemaOperationQuery(ctx context.Context, stmt string, args ...any) error { m.ctrl.T.Helper() varargs := []any{ctx, stmt} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ExecSchemaOperationQuery", varargs...) ret0, _ := ret[0].(error) return ret0 } // ExecSchemaOperationQuery indicates an expected call of ExecSchemaOperationQuery. func (mr *MockadminCRUDMockRecorder) ExecSchemaOperationQuery(ctx, stmt any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, stmt}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecSchemaOperationQuery", reflect.TypeOf((*MockadminCRUD)(nil).ExecSchemaOperationQuery), varargs...) } // ListTables mocks base method. func (m *MockadminCRUD) ListTables(database string) ([]string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTables", database) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTables indicates an expected call of ListTables. func (mr *MockadminCRUDMockRecorder) ListTables(database any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTables", reflect.TypeOf((*MockadminCRUD)(nil).ListTables), database) } // ReadSchemaVersion mocks base method. func (m *MockadminCRUD) ReadSchemaVersion(database string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadSchemaVersion", database) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadSchemaVersion indicates an expected call of ReadSchemaVersion. func (mr *MockadminCRUDMockRecorder) ReadSchemaVersion(database any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadSchemaVersion", reflect.TypeOf((*MockadminCRUD)(nil).ReadSchemaVersion), database) } // UpdateSchemaVersion mocks base method. func (m *MockadminCRUD) UpdateSchemaVersion(database, newVersion, minCompatibleVersion string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateSchemaVersion", database, newVersion, minCompatibleVersion) ret0, _ := ret[0].(error) return ret0 } // UpdateSchemaVersion indicates an expected call of UpdateSchemaVersion. func (mr *MockadminCRUDMockRecorder) UpdateSchemaVersion(database, newVersion, minCompatibleVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSchemaVersion", reflect.TypeOf((*MockadminCRUD)(nil).UpdateSchemaVersion), database, newVersion, minCompatibleVersion) } // WriteSchemaUpdateLog mocks base method. func (m *MockadminCRUD) WriteSchemaUpdateLog(oldVersion, newVersion, manifestMD5, desc string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteSchemaUpdateLog", oldVersion, newVersion, manifestMD5, desc) ret0, _ := ret[0].(error) return ret0 } // WriteSchemaUpdateLog indicates an expected call of WriteSchemaUpdateLog. func (mr *MockadminCRUDMockRecorder) WriteSchemaUpdateLog(oldVersion, newVersion, manifestMD5, desc any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteSchemaUpdateLog", reflect.TypeOf((*MockadminCRUD)(nil).WriteSchemaUpdateLog), oldVersion, newVersion, manifestMD5, desc) } // MockTx is a mock of Tx interface. type MockTx struct { ctrl *gomock.Controller recorder *MockTxMockRecorder isgomock struct{} } // MockTxMockRecorder is the mock recorder for MockTx. type MockTxMockRecorder struct { mock *MockTx } // NewMockTx creates a new mock instance. func NewMockTx(ctrl *gomock.Controller) *MockTx { mock := &MockTx{ctrl: ctrl} mock.recorder = &MockTxMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTx) EXPECT() *MockTxMockRecorder { return m.recorder } // Commit mocks base method. func (m *MockTx) Commit() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Commit") ret0, _ := ret[0].(error) return ret0 } // Commit indicates an expected call of Commit. func (mr *MockTxMockRecorder) Commit() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockTx)(nil).Commit)) } // DeleteFromActivityInfoMaps mocks base method. func (m *MockTx) DeleteFromActivityInfoMaps(ctx context.Context, filter *ActivityInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromActivityInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromActivityInfoMaps indicates an expected call of DeleteFromActivityInfoMaps. func (mr *MockTxMockRecorder) DeleteFromActivityInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromActivityInfoMaps", reflect.TypeOf((*MockTx)(nil).DeleteFromActivityInfoMaps), ctx, filter) } // DeleteFromBufferedEvents mocks base method. func (m *MockTx) DeleteFromBufferedEvents(ctx context.Context, filter *BufferedEventsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromBufferedEvents", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromBufferedEvents indicates an expected call of DeleteFromBufferedEvents. func (mr *MockTxMockRecorder) DeleteFromBufferedEvents(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromBufferedEvents", reflect.TypeOf((*MockTx)(nil).DeleteFromBufferedEvents), ctx, filter) } // DeleteFromChildExecutionInfoMaps mocks base method. func (m *MockTx) DeleteFromChildExecutionInfoMaps(ctx context.Context, filter *ChildExecutionInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromChildExecutionInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromChildExecutionInfoMaps indicates an expected call of DeleteFromChildExecutionInfoMaps. func (mr *MockTxMockRecorder) DeleteFromChildExecutionInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromChildExecutionInfoMaps", reflect.TypeOf((*MockTx)(nil).DeleteFromChildExecutionInfoMaps), ctx, filter) } // DeleteFromCrossClusterTasks mocks base method. func (m *MockTx) DeleteFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromCrossClusterTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromCrossClusterTasks indicates an expected call of DeleteFromCrossClusterTasks. func (mr *MockTxMockRecorder) DeleteFromCrossClusterTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromCrossClusterTasks", reflect.TypeOf((*MockTx)(nil).DeleteFromCrossClusterTasks), ctx, filter) } // DeleteFromCurrentExecutions mocks base method. func (m *MockTx) DeleteFromCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromCurrentExecutions", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromCurrentExecutions indicates an expected call of DeleteFromCurrentExecutions. func (mr *MockTxMockRecorder) DeleteFromCurrentExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromCurrentExecutions", reflect.TypeOf((*MockTx)(nil).DeleteFromCurrentExecutions), ctx, filter) } // DeleteFromDomain mocks base method. func (m *MockTx) DeleteFromDomain(ctx context.Context, filter *DomainFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromDomain", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromDomain indicates an expected call of DeleteFromDomain. func (mr *MockTxMockRecorder) DeleteFromDomain(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromDomain", reflect.TypeOf((*MockTx)(nil).DeleteFromDomain), ctx, filter) } // DeleteFromExecutions mocks base method. func (m *MockTx) DeleteFromExecutions(ctx context.Context, filter *ExecutionsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromExecutions", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromExecutions indicates an expected call of DeleteFromExecutions. func (mr *MockTxMockRecorder) DeleteFromExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromExecutions", reflect.TypeOf((*MockTx)(nil).DeleteFromExecutions), ctx, filter) } // DeleteFromHistoryNode mocks base method. func (m *MockTx) DeleteFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromHistoryNode", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromHistoryNode indicates an expected call of DeleteFromHistoryNode. func (mr *MockTxMockRecorder) DeleteFromHistoryNode(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromHistoryNode", reflect.TypeOf((*MockTx)(nil).DeleteFromHistoryNode), ctx, filter) } // DeleteFromHistoryTree mocks base method. func (m *MockTx) DeleteFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromHistoryTree", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromHistoryTree indicates an expected call of DeleteFromHistoryTree. func (mr *MockTxMockRecorder) DeleteFromHistoryTree(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromHistoryTree", reflect.TypeOf((*MockTx)(nil).DeleteFromHistoryTree), ctx, filter) } // DeleteFromReplicationTasks mocks base method. func (m *MockTx) DeleteFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromReplicationTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromReplicationTasks indicates an expected call of DeleteFromReplicationTasks. func (mr *MockTxMockRecorder) DeleteFromReplicationTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromReplicationTasks", reflect.TypeOf((*MockTx)(nil).DeleteFromReplicationTasks), ctx, filter) } // DeleteFromRequestCancelInfoMaps mocks base method. func (m *MockTx) DeleteFromRequestCancelInfoMaps(ctx context.Context, filter *RequestCancelInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromRequestCancelInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromRequestCancelInfoMaps indicates an expected call of DeleteFromRequestCancelInfoMaps. func (mr *MockTxMockRecorder) DeleteFromRequestCancelInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromRequestCancelInfoMaps", reflect.TypeOf((*MockTx)(nil).DeleteFromRequestCancelInfoMaps), ctx, filter) } // DeleteFromSignalInfoMaps mocks base method. func (m *MockTx) DeleteFromSignalInfoMaps(ctx context.Context, filter *SignalInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromSignalInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromSignalInfoMaps indicates an expected call of DeleteFromSignalInfoMaps. func (mr *MockTxMockRecorder) DeleteFromSignalInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromSignalInfoMaps", reflect.TypeOf((*MockTx)(nil).DeleteFromSignalInfoMaps), ctx, filter) } // DeleteFromSignalsRequestedSets mocks base method. func (m *MockTx) DeleteFromSignalsRequestedSets(ctx context.Context, filter *SignalsRequestedSetsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromSignalsRequestedSets", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromSignalsRequestedSets indicates an expected call of DeleteFromSignalsRequestedSets. func (mr *MockTxMockRecorder) DeleteFromSignalsRequestedSets(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromSignalsRequestedSets", reflect.TypeOf((*MockTx)(nil).DeleteFromSignalsRequestedSets), ctx, filter) } // DeleteFromTaskLists mocks base method. func (m *MockTx) DeleteFromTaskLists(ctx context.Context, filter *TaskListsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTaskLists", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTaskLists indicates an expected call of DeleteFromTaskLists. func (mr *MockTxMockRecorder) DeleteFromTaskLists(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTaskLists", reflect.TypeOf((*MockTx)(nil).DeleteFromTaskLists), ctx, filter) } // DeleteFromTasks mocks base method. func (m *MockTx) DeleteFromTasks(ctx context.Context, filter *TasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTasks indicates an expected call of DeleteFromTasks. func (mr *MockTxMockRecorder) DeleteFromTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTasks", reflect.TypeOf((*MockTx)(nil).DeleteFromTasks), ctx, filter) } // DeleteFromTimerInfoMaps mocks base method. func (m *MockTx) DeleteFromTimerInfoMaps(ctx context.Context, filter *TimerInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTimerInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTimerInfoMaps indicates an expected call of DeleteFromTimerInfoMaps. func (mr *MockTxMockRecorder) DeleteFromTimerInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTimerInfoMaps", reflect.TypeOf((*MockTx)(nil).DeleteFromTimerInfoMaps), ctx, filter) } // DeleteFromTimerTasks mocks base method. func (m *MockTx) DeleteFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTimerTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTimerTasks indicates an expected call of DeleteFromTimerTasks. func (mr *MockTxMockRecorder) DeleteFromTimerTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTimerTasks", reflect.TypeOf((*MockTx)(nil).DeleteFromTimerTasks), ctx, filter) } // DeleteFromTransferTasks mocks base method. func (m *MockTx) DeleteFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTransferTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTransferTasks indicates an expected call of DeleteFromTransferTasks. func (mr *MockTxMockRecorder) DeleteFromTransferTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTransferTasks", reflect.TypeOf((*MockTx)(nil).DeleteFromTransferTasks), ctx, filter) } // DeleteFromVisibility mocks base method. func (m *MockTx) DeleteFromVisibility(ctx context.Context, filter *VisibilityFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromVisibility", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromVisibility indicates an expected call of DeleteFromVisibility. func (mr *MockTxMockRecorder) DeleteFromVisibility(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromVisibility", reflect.TypeOf((*MockTx)(nil).DeleteFromVisibility), ctx, filter) } // DeleteMessage mocks base method. func (m *MockTx) DeleteMessage(ctx context.Context, queueType persistence.QueueType, messageID int64) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessage", ctx, queueType, messageID) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteMessage indicates an expected call of DeleteMessage. func (mr *MockTxMockRecorder) DeleteMessage(ctx, queueType, messageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessage", reflect.TypeOf((*MockTx)(nil).DeleteMessage), ctx, queueType, messageID) } // DeleteMessageFromReplicationTasksDLQ mocks base method. func (m *MockTx) DeleteMessageFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessageFromReplicationTasksDLQ", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteMessageFromReplicationTasksDLQ indicates an expected call of DeleteMessageFromReplicationTasksDLQ. func (mr *MockTxMockRecorder) DeleteMessageFromReplicationTasksDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessageFromReplicationTasksDLQ", reflect.TypeOf((*MockTx)(nil).DeleteMessageFromReplicationTasksDLQ), ctx, filter) } // DeleteMessagesBefore mocks base method. func (m *MockTx) DeleteMessagesBefore(ctx context.Context, queueType persistence.QueueType, messageID int64) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessagesBefore", ctx, queueType, messageID) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteMessagesBefore indicates an expected call of DeleteMessagesBefore. func (mr *MockTxMockRecorder) DeleteMessagesBefore(ctx, queueType, messageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessagesBefore", reflect.TypeOf((*MockTx)(nil).DeleteMessagesBefore), ctx, queueType, messageID) } // GetAckLevels mocks base method. func (m *MockTx) GetAckLevels(ctx context.Context, queueType persistence.QueueType, forUpdate bool) (map[string]int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAckLevels", ctx, queueType, forUpdate) ret0, _ := ret[0].(map[string]int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAckLevels indicates an expected call of GetAckLevels. func (mr *MockTxMockRecorder) GetAckLevels(ctx, queueType, forUpdate any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAckLevels", reflect.TypeOf((*MockTx)(nil).GetAckLevels), ctx, queueType, forUpdate) } // GetAllHistoryTreeBranches mocks base method. func (m *MockTx) GetAllHistoryTreeBranches(ctx context.Context, filter *HistoryTreeFilter) ([]HistoryTreeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllHistoryTreeBranches", ctx, filter) ret0, _ := ret[0].([]HistoryTreeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAllHistoryTreeBranches indicates an expected call of GetAllHistoryTreeBranches. func (mr *MockTxMockRecorder) GetAllHistoryTreeBranches(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllHistoryTreeBranches", reflect.TypeOf((*MockTx)(nil).GetAllHistoryTreeBranches), ctx, filter) } // GetLastEnqueuedMessageIDForUpdate mocks base method. func (m *MockTx) GetLastEnqueuedMessageIDForUpdate(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLastEnqueuedMessageIDForUpdate", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLastEnqueuedMessageIDForUpdate indicates an expected call of GetLastEnqueuedMessageIDForUpdate. func (mr *MockTxMockRecorder) GetLastEnqueuedMessageIDForUpdate(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastEnqueuedMessageIDForUpdate", reflect.TypeOf((*MockTx)(nil).GetLastEnqueuedMessageIDForUpdate), ctx, queueType) } // GetMessagesBetween mocks base method. func (m *MockTx) GetMessagesBetween(ctx context.Context, queueType persistence.QueueType, firstMessageID, lastMessageID int64, maxRows int) ([]QueueRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMessagesBetween", ctx, queueType, firstMessageID, lastMessageID, maxRows) ret0, _ := ret[0].([]QueueRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMessagesBetween indicates an expected call of GetMessagesBetween. func (mr *MockTxMockRecorder) GetMessagesBetween(ctx, queueType, firstMessageID, lastMessageID, maxRows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessagesBetween", reflect.TypeOf((*MockTx)(nil).GetMessagesBetween), ctx, queueType, firstMessageID, lastMessageID, maxRows) } // GetMessagesFromQueue mocks base method. func (m *MockTx) GetMessagesFromQueue(ctx context.Context, queueType persistence.QueueType, lastMessageID int64, maxRows int) ([]QueueRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMessagesFromQueue", ctx, queueType, lastMessageID, maxRows) ret0, _ := ret[0].([]QueueRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMessagesFromQueue indicates an expected call of GetMessagesFromQueue. func (mr *MockTxMockRecorder) GetMessagesFromQueue(ctx, queueType, lastMessageID, maxRows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessagesFromQueue", reflect.TypeOf((*MockTx)(nil).GetMessagesFromQueue), ctx, queueType, lastMessageID, maxRows) } // GetOrphanTasks mocks base method. func (m *MockTx) GetOrphanTasks(ctx context.Context, filter *OrphanTasksFilter) ([]TaskKeyRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrphanTasks", ctx, filter) ret0, _ := ret[0].([]TaskKeyRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetOrphanTasks indicates an expected call of GetOrphanTasks. func (mr *MockTxMockRecorder) GetOrphanTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrphanTasks", reflect.TypeOf((*MockTx)(nil).GetOrphanTasks), ctx, filter) } // GetQueueSize mocks base method. func (m *MockTx) GetQueueSize(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueSize", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetQueueSize indicates an expected call of GetQueueSize. func (mr *MockTxMockRecorder) GetQueueSize(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueSize", reflect.TypeOf((*MockTx)(nil).GetQueueSize), ctx, queueType) } // GetTasksCount mocks base method. func (m *MockTx) GetTasksCount(ctx context.Context, filter *TasksFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasksCount", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTasksCount indicates an expected call of GetTasksCount. func (mr *MockTxMockRecorder) GetTasksCount(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasksCount", reflect.TypeOf((*MockTx)(nil).GetTasksCount), ctx, filter) } // InsertAckLevel mocks base method. func (m *MockTx) InsertAckLevel(ctx context.Context, queueType persistence.QueueType, messageID int64, clusterName string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertAckLevel", ctx, queueType, messageID, clusterName) ret0, _ := ret[0].(error) return ret0 } // InsertAckLevel indicates an expected call of InsertAckLevel. func (mr *MockTxMockRecorder) InsertAckLevel(ctx, queueType, messageID, clusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertAckLevel", reflect.TypeOf((*MockTx)(nil).InsertAckLevel), ctx, queueType, messageID, clusterName) } // InsertConfig mocks base method. func (m *MockTx) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertConfig", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertConfig indicates an expected call of InsertConfig. func (mr *MockTxMockRecorder) InsertConfig(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertConfig", reflect.TypeOf((*MockTx)(nil).InsertConfig), ctx, row) } // InsertIntoBufferedEvents mocks base method. func (m *MockTx) InsertIntoBufferedEvents(ctx context.Context, rows []BufferedEventsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoBufferedEvents", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoBufferedEvents indicates an expected call of InsertIntoBufferedEvents. func (mr *MockTxMockRecorder) InsertIntoBufferedEvents(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoBufferedEvents", reflect.TypeOf((*MockTx)(nil).InsertIntoBufferedEvents), ctx, rows) } // InsertIntoCrossClusterTasks mocks base method. func (m *MockTx) InsertIntoCrossClusterTasks(ctx context.Context, rows []CrossClusterTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoCrossClusterTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoCrossClusterTasks indicates an expected call of InsertIntoCrossClusterTasks. func (mr *MockTxMockRecorder) InsertIntoCrossClusterTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoCrossClusterTasks", reflect.TypeOf((*MockTx)(nil).InsertIntoCrossClusterTasks), ctx, rows) } // InsertIntoCurrentExecutions mocks base method. func (m *MockTx) InsertIntoCurrentExecutions(ctx context.Context, row *CurrentExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoCurrentExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoCurrentExecutions indicates an expected call of InsertIntoCurrentExecutions. func (mr *MockTxMockRecorder) InsertIntoCurrentExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoCurrentExecutions", reflect.TypeOf((*MockTx)(nil).InsertIntoCurrentExecutions), ctx, row) } // InsertIntoDomain mocks base method. func (m *MockTx) InsertIntoDomain(ctx context.Context, rows *DomainRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoDomain", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoDomain indicates an expected call of InsertIntoDomain. func (mr *MockTxMockRecorder) InsertIntoDomain(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoDomain", reflect.TypeOf((*MockTx)(nil).InsertIntoDomain), ctx, rows) } // InsertIntoDomainAuditLog mocks base method. func (m *MockTx) InsertIntoDomainAuditLog(ctx context.Context, row *DomainAuditLogRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoDomainAuditLog", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoDomainAuditLog indicates an expected call of InsertIntoDomainAuditLog. func (mr *MockTxMockRecorder) InsertIntoDomainAuditLog(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoDomainAuditLog", reflect.TypeOf((*MockTx)(nil).InsertIntoDomainAuditLog), ctx, row) } // InsertIntoExecutions mocks base method. func (m *MockTx) InsertIntoExecutions(ctx context.Context, row *ExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoExecutions indicates an expected call of InsertIntoExecutions. func (mr *MockTxMockRecorder) InsertIntoExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoExecutions", reflect.TypeOf((*MockTx)(nil).InsertIntoExecutions), ctx, row) } // InsertIntoHistoryNode mocks base method. func (m *MockTx) InsertIntoHistoryNode(ctx context.Context, row *HistoryNodeRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoHistoryNode", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoHistoryNode indicates an expected call of InsertIntoHistoryNode. func (mr *MockTxMockRecorder) InsertIntoHistoryNode(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoHistoryNode", reflect.TypeOf((*MockTx)(nil).InsertIntoHistoryNode), ctx, row) } // InsertIntoHistoryTree mocks base method. func (m *MockTx) InsertIntoHistoryTree(ctx context.Context, row *HistoryTreeRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoHistoryTree", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoHistoryTree indicates an expected call of InsertIntoHistoryTree. func (mr *MockTxMockRecorder) InsertIntoHistoryTree(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoHistoryTree", reflect.TypeOf((*MockTx)(nil).InsertIntoHistoryTree), ctx, row) } // InsertIntoQueue mocks base method. func (m *MockTx) InsertIntoQueue(ctx context.Context, row *QueueRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoQueue", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoQueue indicates an expected call of InsertIntoQueue. func (mr *MockTxMockRecorder) InsertIntoQueue(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoQueue", reflect.TypeOf((*MockTx)(nil).InsertIntoQueue), ctx, row) } // InsertIntoReplicationTasks mocks base method. func (m *MockTx) InsertIntoReplicationTasks(ctx context.Context, rows []ReplicationTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoReplicationTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoReplicationTasks indicates an expected call of InsertIntoReplicationTasks. func (mr *MockTxMockRecorder) InsertIntoReplicationTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoReplicationTasks", reflect.TypeOf((*MockTx)(nil).InsertIntoReplicationTasks), ctx, rows) } // InsertIntoReplicationTasksDLQ mocks base method. func (m *MockTx) InsertIntoReplicationTasksDLQ(ctx context.Context, row *ReplicationTaskDLQRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoReplicationTasksDLQ", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoReplicationTasksDLQ indicates an expected call of InsertIntoReplicationTasksDLQ. func (mr *MockTxMockRecorder) InsertIntoReplicationTasksDLQ(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoReplicationTasksDLQ", reflect.TypeOf((*MockTx)(nil).InsertIntoReplicationTasksDLQ), ctx, row) } // InsertIntoShards mocks base method. func (m *MockTx) InsertIntoShards(ctx context.Context, rows *ShardsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoShards", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoShards indicates an expected call of InsertIntoShards. func (mr *MockTxMockRecorder) InsertIntoShards(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoShards", reflect.TypeOf((*MockTx)(nil).InsertIntoShards), ctx, rows) } // InsertIntoSignalsRequestedSets mocks base method. func (m *MockTx) InsertIntoSignalsRequestedSets(ctx context.Context, rows []SignalsRequestedSetsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoSignalsRequestedSets", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoSignalsRequestedSets indicates an expected call of InsertIntoSignalsRequestedSets. func (mr *MockTxMockRecorder) InsertIntoSignalsRequestedSets(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoSignalsRequestedSets", reflect.TypeOf((*MockTx)(nil).InsertIntoSignalsRequestedSets), ctx, rows) } // InsertIntoTaskLists mocks base method. func (m *MockTx) InsertIntoTaskLists(ctx context.Context, row *TaskListsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTaskLists", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTaskLists indicates an expected call of InsertIntoTaskLists. func (mr *MockTxMockRecorder) InsertIntoTaskLists(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTaskLists", reflect.TypeOf((*MockTx)(nil).InsertIntoTaskLists), ctx, row) } // InsertIntoTaskListsWithTTL mocks base method. func (m *MockTx) InsertIntoTaskListsWithTTL(ctx context.Context, row *TaskListsRowWithTTL) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTaskListsWithTTL", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTaskListsWithTTL indicates an expected call of InsertIntoTaskListsWithTTL. func (mr *MockTxMockRecorder) InsertIntoTaskListsWithTTL(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTaskListsWithTTL", reflect.TypeOf((*MockTx)(nil).InsertIntoTaskListsWithTTL), ctx, row) } // InsertIntoTasks mocks base method. func (m *MockTx) InsertIntoTasks(ctx context.Context, rows []TasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTasks indicates an expected call of InsertIntoTasks. func (mr *MockTxMockRecorder) InsertIntoTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTasks", reflect.TypeOf((*MockTx)(nil).InsertIntoTasks), ctx, rows) } // InsertIntoTasksWithTTL mocks base method. func (m *MockTx) InsertIntoTasksWithTTL(ctx context.Context, rows []TasksRowWithTTL) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTasksWithTTL", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTasksWithTTL indicates an expected call of InsertIntoTasksWithTTL. func (mr *MockTxMockRecorder) InsertIntoTasksWithTTL(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTasksWithTTL", reflect.TypeOf((*MockTx)(nil).InsertIntoTasksWithTTL), ctx, rows) } // InsertIntoTimerTasks mocks base method. func (m *MockTx) InsertIntoTimerTasks(ctx context.Context, rows []TimerTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTimerTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTimerTasks indicates an expected call of InsertIntoTimerTasks. func (mr *MockTxMockRecorder) InsertIntoTimerTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTimerTasks", reflect.TypeOf((*MockTx)(nil).InsertIntoTimerTasks), ctx, rows) } // InsertIntoTransferTasks mocks base method. func (m *MockTx) InsertIntoTransferTasks(ctx context.Context, rows []TransferTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTransferTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTransferTasks indicates an expected call of InsertIntoTransferTasks. func (mr *MockTxMockRecorder) InsertIntoTransferTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTransferTasks", reflect.TypeOf((*MockTx)(nil).InsertIntoTransferTasks), ctx, rows) } // InsertIntoVisibility mocks base method. func (m *MockTx) InsertIntoVisibility(ctx context.Context, row *VisibilityRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoVisibility", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoVisibility indicates an expected call of InsertIntoVisibility. func (mr *MockTxMockRecorder) InsertIntoVisibility(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoVisibility", reflect.TypeOf((*MockTx)(nil).InsertIntoVisibility), ctx, row) } // IsDupEntryError mocks base method. func (m *MockTx) IsDupEntryError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsDupEntryError", err) ret0, _ := ret[0].(bool) return ret0 } // IsDupEntryError indicates an expected call of IsDupEntryError. func (mr *MockTxMockRecorder) IsDupEntryError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDupEntryError", reflect.TypeOf((*MockTx)(nil).IsDupEntryError), err) } // IsNotFoundError mocks base method. func (m *MockTx) IsNotFoundError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsNotFoundError", err) ret0, _ := ret[0].(bool) return ret0 } // IsNotFoundError indicates an expected call of IsNotFoundError. func (mr *MockTxMockRecorder) IsNotFoundError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNotFoundError", reflect.TypeOf((*MockTx)(nil).IsNotFoundError), err) } // IsThrottlingError mocks base method. func (m *MockTx) IsThrottlingError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsThrottlingError", err) ret0, _ := ret[0].(bool) return ret0 } // IsThrottlingError indicates an expected call of IsThrottlingError. func (mr *MockTxMockRecorder) IsThrottlingError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsThrottlingError", reflect.TypeOf((*MockTx)(nil).IsThrottlingError), err) } // IsTimeoutError mocks base method. func (m *MockTx) IsTimeoutError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsTimeoutError", err) ret0, _ := ret[0].(bool) return ret0 } // IsTimeoutError indicates an expected call of IsTimeoutError. func (mr *MockTxMockRecorder) IsTimeoutError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsTimeoutError", reflect.TypeOf((*MockTx)(nil).IsTimeoutError), err) } // LockCurrentExecutions mocks base method. func (m *MockTx) LockCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (*CurrentExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockCurrentExecutions", ctx, filter) ret0, _ := ret[0].(*CurrentExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // LockCurrentExecutions indicates an expected call of LockCurrentExecutions. func (mr *MockTxMockRecorder) LockCurrentExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockCurrentExecutions", reflect.TypeOf((*MockTx)(nil).LockCurrentExecutions), ctx, filter) } // LockCurrentExecutionsJoinExecutions mocks base method. func (m *MockTx) LockCurrentExecutionsJoinExecutions(ctx context.Context, filter *CurrentExecutionsFilter) ([]CurrentExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockCurrentExecutionsJoinExecutions", ctx, filter) ret0, _ := ret[0].([]CurrentExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // LockCurrentExecutionsJoinExecutions indicates an expected call of LockCurrentExecutionsJoinExecutions. func (mr *MockTxMockRecorder) LockCurrentExecutionsJoinExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockCurrentExecutionsJoinExecutions", reflect.TypeOf((*MockTx)(nil).LockCurrentExecutionsJoinExecutions), ctx, filter) } // LockDomainMetadata mocks base method. func (m *MockTx) LockDomainMetadata(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockDomainMetadata", ctx) ret0, _ := ret[0].(error) return ret0 } // LockDomainMetadata indicates an expected call of LockDomainMetadata. func (mr *MockTxMockRecorder) LockDomainMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockDomainMetadata", reflect.TypeOf((*MockTx)(nil).LockDomainMetadata), ctx) } // LockTaskLists mocks base method. func (m *MockTx) LockTaskLists(ctx context.Context, filter *TaskListsFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockTaskLists", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // LockTaskLists indicates an expected call of LockTaskLists. func (mr *MockTxMockRecorder) LockTaskLists(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockTaskLists", reflect.TypeOf((*MockTx)(nil).LockTaskLists), ctx, filter) } // MaxAllowedTTL mocks base method. func (m *MockTx) MaxAllowedTTL() (*time.Duration, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MaxAllowedTTL") ret0, _ := ret[0].(*time.Duration) ret1, _ := ret[1].(error) return ret0, ret1 } // MaxAllowedTTL indicates an expected call of MaxAllowedTTL. func (mr *MockTxMockRecorder) MaxAllowedTTL() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxAllowedTTL", reflect.TypeOf((*MockTx)(nil).MaxAllowedTTL)) } // RangeDeleteFromCrossClusterTasks mocks base method. func (m *MockTx) RangeDeleteFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromCrossClusterTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromCrossClusterTasks indicates an expected call of RangeDeleteFromCrossClusterTasks. func (mr *MockTxMockRecorder) RangeDeleteFromCrossClusterTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromCrossClusterTasks", reflect.TypeOf((*MockTx)(nil).RangeDeleteFromCrossClusterTasks), ctx, filter) } // RangeDeleteFromReplicationTasks mocks base method. func (m *MockTx) RangeDeleteFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromReplicationTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromReplicationTasks indicates an expected call of RangeDeleteFromReplicationTasks. func (mr *MockTxMockRecorder) RangeDeleteFromReplicationTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromReplicationTasks", reflect.TypeOf((*MockTx)(nil).RangeDeleteFromReplicationTasks), ctx, filter) } // RangeDeleteFromTimerTasks mocks base method. func (m *MockTx) RangeDeleteFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromTimerTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromTimerTasks indicates an expected call of RangeDeleteFromTimerTasks. func (mr *MockTxMockRecorder) RangeDeleteFromTimerTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromTimerTasks", reflect.TypeOf((*MockTx)(nil).RangeDeleteFromTimerTasks), ctx, filter) } // RangeDeleteFromTransferTasks mocks base method. func (m *MockTx) RangeDeleteFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromTransferTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromTransferTasks indicates an expected call of RangeDeleteFromTransferTasks. func (mr *MockTxMockRecorder) RangeDeleteFromTransferTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromTransferTasks", reflect.TypeOf((*MockTx)(nil).RangeDeleteFromTransferTasks), ctx, filter) } // RangeDeleteMessageFromReplicationTasksDLQ mocks base method. func (m *MockTx) RangeDeleteMessageFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteMessageFromReplicationTasksDLQ", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteMessageFromReplicationTasksDLQ indicates an expected call of RangeDeleteMessageFromReplicationTasksDLQ. func (mr *MockTxMockRecorder) RangeDeleteMessageFromReplicationTasksDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteMessageFromReplicationTasksDLQ", reflect.TypeOf((*MockTx)(nil).RangeDeleteMessageFromReplicationTasksDLQ), ctx, filter) } // RangeDeleteMessages mocks base method. func (m *MockTx) RangeDeleteMessages(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID, inclusiveEndMessageID int64) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteMessages", ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteMessages indicates an expected call of RangeDeleteMessages. func (mr *MockTxMockRecorder) RangeDeleteMessages(ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteMessages", reflect.TypeOf((*MockTx)(nil).RangeDeleteMessages), ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) } // ReadLockExecutions mocks base method. func (m *MockTx) ReadLockExecutions(ctx context.Context, filter *ExecutionsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadLockExecutions", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadLockExecutions indicates an expected call of ReadLockExecutions. func (mr *MockTxMockRecorder) ReadLockExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadLockExecutions", reflect.TypeOf((*MockTx)(nil).ReadLockExecutions), ctx, filter) } // ReadLockShards mocks base method. func (m *MockTx) ReadLockShards(ctx context.Context, filter *ShardsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadLockShards", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadLockShards indicates an expected call of ReadLockShards. func (mr *MockTxMockRecorder) ReadLockShards(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadLockShards", reflect.TypeOf((*MockTx)(nil).ReadLockShards), ctx, filter) } // ReplaceIntoActivityInfoMaps mocks base method. func (m *MockTx) ReplaceIntoActivityInfoMaps(ctx context.Context, rows []ActivityInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoActivityInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoActivityInfoMaps indicates an expected call of ReplaceIntoActivityInfoMaps. func (mr *MockTxMockRecorder) ReplaceIntoActivityInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoActivityInfoMaps", reflect.TypeOf((*MockTx)(nil).ReplaceIntoActivityInfoMaps), ctx, rows) } // ReplaceIntoChildExecutionInfoMaps mocks base method. func (m *MockTx) ReplaceIntoChildExecutionInfoMaps(ctx context.Context, rows []ChildExecutionInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoChildExecutionInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoChildExecutionInfoMaps indicates an expected call of ReplaceIntoChildExecutionInfoMaps. func (mr *MockTxMockRecorder) ReplaceIntoChildExecutionInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoChildExecutionInfoMaps", reflect.TypeOf((*MockTx)(nil).ReplaceIntoChildExecutionInfoMaps), ctx, rows) } // ReplaceIntoRequestCancelInfoMaps mocks base method. func (m *MockTx) ReplaceIntoRequestCancelInfoMaps(ctx context.Context, rows []RequestCancelInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoRequestCancelInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoRequestCancelInfoMaps indicates an expected call of ReplaceIntoRequestCancelInfoMaps. func (mr *MockTxMockRecorder) ReplaceIntoRequestCancelInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoRequestCancelInfoMaps", reflect.TypeOf((*MockTx)(nil).ReplaceIntoRequestCancelInfoMaps), ctx, rows) } // ReplaceIntoSignalInfoMaps mocks base method. func (m *MockTx) ReplaceIntoSignalInfoMaps(ctx context.Context, rows []SignalInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoSignalInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoSignalInfoMaps indicates an expected call of ReplaceIntoSignalInfoMaps. func (mr *MockTxMockRecorder) ReplaceIntoSignalInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoSignalInfoMaps", reflect.TypeOf((*MockTx)(nil).ReplaceIntoSignalInfoMaps), ctx, rows) } // ReplaceIntoTimerInfoMaps mocks base method. func (m *MockTx) ReplaceIntoTimerInfoMaps(ctx context.Context, rows []TimerInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoTimerInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoTimerInfoMaps indicates an expected call of ReplaceIntoTimerInfoMaps. func (mr *MockTxMockRecorder) ReplaceIntoTimerInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoTimerInfoMaps", reflect.TypeOf((*MockTx)(nil).ReplaceIntoTimerInfoMaps), ctx, rows) } // ReplaceIntoVisibility mocks base method. func (m *MockTx) ReplaceIntoVisibility(ctx context.Context, row *VisibilityRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoVisibility", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoVisibility indicates an expected call of ReplaceIntoVisibility. func (mr *MockTxMockRecorder) ReplaceIntoVisibility(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoVisibility", reflect.TypeOf((*MockTx)(nil).ReplaceIntoVisibility), ctx, row) } // Rollback mocks base method. func (m *MockTx) Rollback() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Rollback") ret0, _ := ret[0].(error) return ret0 } // Rollback indicates an expected call of Rollback. func (mr *MockTxMockRecorder) Rollback() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rollback", reflect.TypeOf((*MockTx)(nil).Rollback)) } // SelectFromActivityInfoMaps mocks base method. func (m *MockTx) SelectFromActivityInfoMaps(ctx context.Context, filter *ActivityInfoMapsFilter) ([]ActivityInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromActivityInfoMaps", ctx, filter) ret0, _ := ret[0].([]ActivityInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromActivityInfoMaps indicates an expected call of SelectFromActivityInfoMaps. func (mr *MockTxMockRecorder) SelectFromActivityInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromActivityInfoMaps", reflect.TypeOf((*MockTx)(nil).SelectFromActivityInfoMaps), ctx, filter) } // SelectFromBufferedEvents mocks base method. func (m *MockTx) SelectFromBufferedEvents(ctx context.Context, filter *BufferedEventsFilter) ([]BufferedEventsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromBufferedEvents", ctx, filter) ret0, _ := ret[0].([]BufferedEventsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromBufferedEvents indicates an expected call of SelectFromBufferedEvents. func (mr *MockTxMockRecorder) SelectFromBufferedEvents(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromBufferedEvents", reflect.TypeOf((*MockTx)(nil).SelectFromBufferedEvents), ctx, filter) } // SelectFromChildExecutionInfoMaps mocks base method. func (m *MockTx) SelectFromChildExecutionInfoMaps(ctx context.Context, filter *ChildExecutionInfoMapsFilter) ([]ChildExecutionInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromChildExecutionInfoMaps", ctx, filter) ret0, _ := ret[0].([]ChildExecutionInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromChildExecutionInfoMaps indicates an expected call of SelectFromChildExecutionInfoMaps. func (mr *MockTxMockRecorder) SelectFromChildExecutionInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromChildExecutionInfoMaps", reflect.TypeOf((*MockTx)(nil).SelectFromChildExecutionInfoMaps), ctx, filter) } // SelectFromCrossClusterTasks mocks base method. func (m *MockTx) SelectFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) ([]CrossClusterTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromCrossClusterTasks", ctx, filter) ret0, _ := ret[0].([]CrossClusterTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromCrossClusterTasks indicates an expected call of SelectFromCrossClusterTasks. func (mr *MockTxMockRecorder) SelectFromCrossClusterTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromCrossClusterTasks", reflect.TypeOf((*MockTx)(nil).SelectFromCrossClusterTasks), ctx, filter) } // SelectFromCurrentExecutions mocks base method. func (m *MockTx) SelectFromCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (*CurrentExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromCurrentExecutions", ctx, filter) ret0, _ := ret[0].(*CurrentExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromCurrentExecutions indicates an expected call of SelectFromCurrentExecutions. func (mr *MockTxMockRecorder) SelectFromCurrentExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromCurrentExecutions", reflect.TypeOf((*MockTx)(nil).SelectFromCurrentExecutions), ctx, filter) } // SelectFromDomain mocks base method. func (m *MockTx) SelectFromDomain(ctx context.Context, filter *DomainFilter) ([]DomainRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromDomain", ctx, filter) ret0, _ := ret[0].([]DomainRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromDomain indicates an expected call of SelectFromDomain. func (mr *MockTxMockRecorder) SelectFromDomain(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromDomain", reflect.TypeOf((*MockTx)(nil).SelectFromDomain), ctx, filter) } // SelectFromDomainAuditLogs mocks base method. func (m *MockTx) SelectFromDomainAuditLogs(ctx context.Context, filter *DomainAuditLogFilter) ([]*DomainAuditLogRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromDomainAuditLogs", ctx, filter) ret0, _ := ret[0].([]*DomainAuditLogRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromDomainAuditLogs indicates an expected call of SelectFromDomainAuditLogs. func (mr *MockTxMockRecorder) SelectFromDomainAuditLogs(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromDomainAuditLogs", reflect.TypeOf((*MockTx)(nil).SelectFromDomainAuditLogs), ctx, filter) } // SelectFromDomainMetadata mocks base method. func (m *MockTx) SelectFromDomainMetadata(ctx context.Context) (*DomainMetadataRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromDomainMetadata", ctx) ret0, _ := ret[0].(*DomainMetadataRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromDomainMetadata indicates an expected call of SelectFromDomainMetadata. func (mr *MockTxMockRecorder) SelectFromDomainMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromDomainMetadata", reflect.TypeOf((*MockTx)(nil).SelectFromDomainMetadata), ctx) } // SelectFromExecutions mocks base method. func (m *MockTx) SelectFromExecutions(ctx context.Context, filter *ExecutionsFilter) ([]ExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromExecutions", ctx, filter) ret0, _ := ret[0].([]ExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromExecutions indicates an expected call of SelectFromExecutions. func (mr *MockTxMockRecorder) SelectFromExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromExecutions", reflect.TypeOf((*MockTx)(nil).SelectFromExecutions), ctx, filter) } // SelectFromHistoryNode mocks base method. func (m *MockTx) SelectFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) ([]HistoryNodeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryNode", ctx, filter) ret0, _ := ret[0].([]HistoryNodeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromHistoryNode indicates an expected call of SelectFromHistoryNode. func (mr *MockTxMockRecorder) SelectFromHistoryNode(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryNode", reflect.TypeOf((*MockTx)(nil).SelectFromHistoryNode), ctx, filter) } // SelectFromHistoryTree mocks base method. func (m *MockTx) SelectFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) ([]HistoryTreeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryTree", ctx, filter) ret0, _ := ret[0].([]HistoryTreeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromHistoryTree indicates an expected call of SelectFromHistoryTree. func (mr *MockTxMockRecorder) SelectFromHistoryTree(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryTree", reflect.TypeOf((*MockTx)(nil).SelectFromHistoryTree), ctx, filter) } // SelectFromReplicationDLQ mocks base method. func (m *MockTx) SelectFromReplicationDLQ(ctx context.Context, filter *ReplicationTaskDLQFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromReplicationDLQ", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromReplicationDLQ indicates an expected call of SelectFromReplicationDLQ. func (mr *MockTxMockRecorder) SelectFromReplicationDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromReplicationDLQ", reflect.TypeOf((*MockTx)(nil).SelectFromReplicationDLQ), ctx, filter) } // SelectFromReplicationTasks mocks base method. func (m *MockTx) SelectFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) ([]ReplicationTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromReplicationTasks", ctx, filter) ret0, _ := ret[0].([]ReplicationTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromReplicationTasks indicates an expected call of SelectFromReplicationTasks. func (mr *MockTxMockRecorder) SelectFromReplicationTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromReplicationTasks", reflect.TypeOf((*MockTx)(nil).SelectFromReplicationTasks), ctx, filter) } // SelectFromReplicationTasksDLQ mocks base method. func (m *MockTx) SelectFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) ([]ReplicationTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromReplicationTasksDLQ", ctx, filter) ret0, _ := ret[0].([]ReplicationTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromReplicationTasksDLQ indicates an expected call of SelectFromReplicationTasksDLQ. func (mr *MockTxMockRecorder) SelectFromReplicationTasksDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromReplicationTasksDLQ", reflect.TypeOf((*MockTx)(nil).SelectFromReplicationTasksDLQ), ctx, filter) } // SelectFromRequestCancelInfoMaps mocks base method. func (m *MockTx) SelectFromRequestCancelInfoMaps(ctx context.Context, filter *RequestCancelInfoMapsFilter) ([]RequestCancelInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromRequestCancelInfoMaps", ctx, filter) ret0, _ := ret[0].([]RequestCancelInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromRequestCancelInfoMaps indicates an expected call of SelectFromRequestCancelInfoMaps. func (mr *MockTxMockRecorder) SelectFromRequestCancelInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromRequestCancelInfoMaps", reflect.TypeOf((*MockTx)(nil).SelectFromRequestCancelInfoMaps), ctx, filter) } // SelectFromShards mocks base method. func (m *MockTx) SelectFromShards(ctx context.Context, filter *ShardsFilter) (*ShardsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromShards", ctx, filter) ret0, _ := ret[0].(*ShardsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromShards indicates an expected call of SelectFromShards. func (mr *MockTxMockRecorder) SelectFromShards(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromShards", reflect.TypeOf((*MockTx)(nil).SelectFromShards), ctx, filter) } // SelectFromSignalInfoMaps mocks base method. func (m *MockTx) SelectFromSignalInfoMaps(ctx context.Context, filter *SignalInfoMapsFilter) ([]SignalInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromSignalInfoMaps", ctx, filter) ret0, _ := ret[0].([]SignalInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromSignalInfoMaps indicates an expected call of SelectFromSignalInfoMaps. func (mr *MockTxMockRecorder) SelectFromSignalInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromSignalInfoMaps", reflect.TypeOf((*MockTx)(nil).SelectFromSignalInfoMaps), ctx, filter) } // SelectFromSignalsRequestedSets mocks base method. func (m *MockTx) SelectFromSignalsRequestedSets(ctx context.Context, filter *SignalsRequestedSetsFilter) ([]SignalsRequestedSetsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromSignalsRequestedSets", ctx, filter) ret0, _ := ret[0].([]SignalsRequestedSetsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromSignalsRequestedSets indicates an expected call of SelectFromSignalsRequestedSets. func (mr *MockTxMockRecorder) SelectFromSignalsRequestedSets(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromSignalsRequestedSets", reflect.TypeOf((*MockTx)(nil).SelectFromSignalsRequestedSets), ctx, filter) } // SelectFromTaskLists mocks base method. func (m *MockTx) SelectFromTaskLists(ctx context.Context, filter *TaskListsFilter) ([]TaskListsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTaskLists", ctx, filter) ret0, _ := ret[0].([]TaskListsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTaskLists indicates an expected call of SelectFromTaskLists. func (mr *MockTxMockRecorder) SelectFromTaskLists(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTaskLists", reflect.TypeOf((*MockTx)(nil).SelectFromTaskLists), ctx, filter) } // SelectFromTasks mocks base method. func (m *MockTx) SelectFromTasks(ctx context.Context, filter *TasksFilter) ([]TasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTasks", ctx, filter) ret0, _ := ret[0].([]TasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTasks indicates an expected call of SelectFromTasks. func (mr *MockTxMockRecorder) SelectFromTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTasks", reflect.TypeOf((*MockTx)(nil).SelectFromTasks), ctx, filter) } // SelectFromTimerInfoMaps mocks base method. func (m *MockTx) SelectFromTimerInfoMaps(ctx context.Context, filter *TimerInfoMapsFilter) ([]TimerInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTimerInfoMaps", ctx, filter) ret0, _ := ret[0].([]TimerInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTimerInfoMaps indicates an expected call of SelectFromTimerInfoMaps. func (mr *MockTxMockRecorder) SelectFromTimerInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTimerInfoMaps", reflect.TypeOf((*MockTx)(nil).SelectFromTimerInfoMaps), ctx, filter) } // SelectFromTimerTasks mocks base method. func (m *MockTx) SelectFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) ([]TimerTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTimerTasks", ctx, filter) ret0, _ := ret[0].([]TimerTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTimerTasks indicates an expected call of SelectFromTimerTasks. func (mr *MockTxMockRecorder) SelectFromTimerTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTimerTasks", reflect.TypeOf((*MockTx)(nil).SelectFromTimerTasks), ctx, filter) } // SelectFromTransferTasks mocks base method. func (m *MockTx) SelectFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) ([]TransferTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTransferTasks", ctx, filter) ret0, _ := ret[0].([]TransferTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTransferTasks indicates an expected call of SelectFromTransferTasks. func (mr *MockTxMockRecorder) SelectFromTransferTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTransferTasks", reflect.TypeOf((*MockTx)(nil).SelectFromTransferTasks), ctx, filter) } // SelectFromVisibility mocks base method. func (m *MockTx) SelectFromVisibility(ctx context.Context, filter *VisibilityFilter) ([]VisibilityRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromVisibility", ctx, filter) ret0, _ := ret[0].([]VisibilityRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromVisibility indicates an expected call of SelectFromVisibility. func (mr *MockTxMockRecorder) SelectFromVisibility(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromVisibility", reflect.TypeOf((*MockTx)(nil).SelectFromVisibility), ctx, filter) } // SelectLatestConfig mocks base method. func (m *MockTx) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectLatestConfig", ctx, rowType) ret0, _ := ret[0].(*persistence.InternalConfigStoreEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectLatestConfig indicates an expected call of SelectLatestConfig. func (mr *MockTxMockRecorder) SelectLatestConfig(ctx, rowType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectLatestConfig", reflect.TypeOf((*MockTx)(nil).SelectLatestConfig), ctx, rowType) } // SupportsAsyncTransaction mocks base method. func (m *MockTx) SupportsAsyncTransaction() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SupportsAsyncTransaction") ret0, _ := ret[0].(bool) return ret0 } // SupportsAsyncTransaction indicates an expected call of SupportsAsyncTransaction. func (mr *MockTxMockRecorder) SupportsAsyncTransaction() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportsAsyncTransaction", reflect.TypeOf((*MockTx)(nil).SupportsAsyncTransaction)) } // SupportsTTL mocks base method. func (m *MockTx) SupportsTTL() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SupportsTTL") ret0, _ := ret[0].(bool) return ret0 } // SupportsTTL indicates an expected call of SupportsTTL. func (mr *MockTxMockRecorder) SupportsTTL() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportsTTL", reflect.TypeOf((*MockTx)(nil).SupportsTTL)) } // UpdateAckLevels mocks base method. func (m *MockTx) UpdateAckLevels(ctx context.Context, queueType persistence.QueueType, clusterAckLevels map[string]int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAckLevels", ctx, queueType, clusterAckLevels) ret0, _ := ret[0].(error) return ret0 } // UpdateAckLevels indicates an expected call of UpdateAckLevels. func (mr *MockTxMockRecorder) UpdateAckLevels(ctx, queueType, clusterAckLevels any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAckLevels", reflect.TypeOf((*MockTx)(nil).UpdateAckLevels), ctx, queueType, clusterAckLevels) } // UpdateCurrentExecutions mocks base method. func (m *MockTx) UpdateCurrentExecutions(ctx context.Context, row *CurrentExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateCurrentExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateCurrentExecutions indicates an expected call of UpdateCurrentExecutions. func (mr *MockTxMockRecorder) UpdateCurrentExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCurrentExecutions", reflect.TypeOf((*MockTx)(nil).UpdateCurrentExecutions), ctx, row) } // UpdateDomain mocks base method. func (m *MockTx) UpdateDomain(ctx context.Context, row *DomainRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomain", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MockTxMockRecorder) UpdateDomain(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MockTx)(nil).UpdateDomain), ctx, row) } // UpdateDomainMetadata mocks base method. func (m *MockTx) UpdateDomainMetadata(ctx context.Context, row *DomainMetadataRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomainMetadata", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomainMetadata indicates an expected call of UpdateDomainMetadata. func (mr *MockTxMockRecorder) UpdateDomainMetadata(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomainMetadata", reflect.TypeOf((*MockTx)(nil).UpdateDomainMetadata), ctx, row) } // UpdateExecutions mocks base method. func (m *MockTx) UpdateExecutions(ctx context.Context, row *ExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateExecutions indicates an expected call of UpdateExecutions. func (mr *MockTxMockRecorder) UpdateExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateExecutions", reflect.TypeOf((*MockTx)(nil).UpdateExecutions), ctx, row) } // UpdateShards mocks base method. func (m *MockTx) UpdateShards(ctx context.Context, row *ShardsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateShards", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateShards indicates an expected call of UpdateShards. func (mr *MockTxMockRecorder) UpdateShards(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateShards", reflect.TypeOf((*MockTx)(nil).UpdateShards), ctx, row) } // UpdateTaskLists mocks base method. func (m *MockTx) UpdateTaskLists(ctx context.Context, row *TaskListsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskLists", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskLists indicates an expected call of UpdateTaskLists. func (mr *MockTxMockRecorder) UpdateTaskLists(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskLists", reflect.TypeOf((*MockTx)(nil).UpdateTaskLists), ctx, row) } // UpdateTaskListsWithTTL mocks base method. func (m *MockTx) UpdateTaskListsWithTTL(ctx context.Context, row *TaskListsRowWithTTL) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskListsWithTTL", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskListsWithTTL indicates an expected call of UpdateTaskListsWithTTL. func (mr *MockTxMockRecorder) UpdateTaskListsWithTTL(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListsWithTTL", reflect.TypeOf((*MockTx)(nil).UpdateTaskListsWithTTL), ctx, row) } // WriteLockExecutions mocks base method. func (m *MockTx) WriteLockExecutions(ctx context.Context, filter *ExecutionsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteLockExecutions", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // WriteLockExecutions indicates an expected call of WriteLockExecutions. func (mr *MockTxMockRecorder) WriteLockExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteLockExecutions", reflect.TypeOf((*MockTx)(nil).WriteLockExecutions), ctx, filter) } // WriteLockShards mocks base method. func (m *MockTx) WriteLockShards(ctx context.Context, filter *ShardsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteLockShards", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // WriteLockShards indicates an expected call of WriteLockShards. func (mr *MockTxMockRecorder) WriteLockShards(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteLockShards", reflect.TypeOf((*MockTx)(nil).WriteLockShards), ctx, filter) } // MockDB is a mock of DB interface. type MockDB struct { ctrl *gomock.Controller recorder *MockDBMockRecorder isgomock struct{} } // MockDBMockRecorder is the mock recorder for MockDB. type MockDBMockRecorder struct { mock *MockDB } // NewMockDB creates a new mock instance. func NewMockDB(ctrl *gomock.Controller) *MockDB { mock := &MockDB{ctrl: ctrl} mock.recorder = &MockDBMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDB) EXPECT() *MockDBMockRecorder { return m.recorder } // BeginTx mocks base method. func (m *MockDB) BeginTx(ctx context.Context, dbShardID int) (Tx, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BeginTx", ctx, dbShardID) ret0, _ := ret[0].(Tx) ret1, _ := ret[1].(error) return ret0, ret1 } // BeginTx indicates an expected call of BeginTx. func (mr *MockDBMockRecorder) BeginTx(ctx, dbShardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTx", reflect.TypeOf((*MockDB)(nil).BeginTx), ctx, dbShardID) } // Close mocks base method. func (m *MockDB) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockDBMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDB)(nil).Close)) } // DeleteFromActivityInfoMaps mocks base method. func (m *MockDB) DeleteFromActivityInfoMaps(ctx context.Context, filter *ActivityInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromActivityInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromActivityInfoMaps indicates an expected call of DeleteFromActivityInfoMaps. func (mr *MockDBMockRecorder) DeleteFromActivityInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromActivityInfoMaps", reflect.TypeOf((*MockDB)(nil).DeleteFromActivityInfoMaps), ctx, filter) } // DeleteFromBufferedEvents mocks base method. func (m *MockDB) DeleteFromBufferedEvents(ctx context.Context, filter *BufferedEventsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromBufferedEvents", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromBufferedEvents indicates an expected call of DeleteFromBufferedEvents. func (mr *MockDBMockRecorder) DeleteFromBufferedEvents(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromBufferedEvents", reflect.TypeOf((*MockDB)(nil).DeleteFromBufferedEvents), ctx, filter) } // DeleteFromChildExecutionInfoMaps mocks base method. func (m *MockDB) DeleteFromChildExecutionInfoMaps(ctx context.Context, filter *ChildExecutionInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromChildExecutionInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromChildExecutionInfoMaps indicates an expected call of DeleteFromChildExecutionInfoMaps. func (mr *MockDBMockRecorder) DeleteFromChildExecutionInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromChildExecutionInfoMaps", reflect.TypeOf((*MockDB)(nil).DeleteFromChildExecutionInfoMaps), ctx, filter) } // DeleteFromCrossClusterTasks mocks base method. func (m *MockDB) DeleteFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromCrossClusterTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromCrossClusterTasks indicates an expected call of DeleteFromCrossClusterTasks. func (mr *MockDBMockRecorder) DeleteFromCrossClusterTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromCrossClusterTasks", reflect.TypeOf((*MockDB)(nil).DeleteFromCrossClusterTasks), ctx, filter) } // DeleteFromCurrentExecutions mocks base method. func (m *MockDB) DeleteFromCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromCurrentExecutions", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromCurrentExecutions indicates an expected call of DeleteFromCurrentExecutions. func (mr *MockDBMockRecorder) DeleteFromCurrentExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromCurrentExecutions", reflect.TypeOf((*MockDB)(nil).DeleteFromCurrentExecutions), ctx, filter) } // DeleteFromDomain mocks base method. func (m *MockDB) DeleteFromDomain(ctx context.Context, filter *DomainFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromDomain", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromDomain indicates an expected call of DeleteFromDomain. func (mr *MockDBMockRecorder) DeleteFromDomain(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromDomain", reflect.TypeOf((*MockDB)(nil).DeleteFromDomain), ctx, filter) } // DeleteFromExecutions mocks base method. func (m *MockDB) DeleteFromExecutions(ctx context.Context, filter *ExecutionsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromExecutions", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromExecutions indicates an expected call of DeleteFromExecutions. func (mr *MockDBMockRecorder) DeleteFromExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromExecutions", reflect.TypeOf((*MockDB)(nil).DeleteFromExecutions), ctx, filter) } // DeleteFromHistoryNode mocks base method. func (m *MockDB) DeleteFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromHistoryNode", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromHistoryNode indicates an expected call of DeleteFromHistoryNode. func (mr *MockDBMockRecorder) DeleteFromHistoryNode(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromHistoryNode", reflect.TypeOf((*MockDB)(nil).DeleteFromHistoryNode), ctx, filter) } // DeleteFromHistoryTree mocks base method. func (m *MockDB) DeleteFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromHistoryTree", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromHistoryTree indicates an expected call of DeleteFromHistoryTree. func (mr *MockDBMockRecorder) DeleteFromHistoryTree(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromHistoryTree", reflect.TypeOf((*MockDB)(nil).DeleteFromHistoryTree), ctx, filter) } // DeleteFromReplicationTasks mocks base method. func (m *MockDB) DeleteFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromReplicationTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromReplicationTasks indicates an expected call of DeleteFromReplicationTasks. func (mr *MockDBMockRecorder) DeleteFromReplicationTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromReplicationTasks", reflect.TypeOf((*MockDB)(nil).DeleteFromReplicationTasks), ctx, filter) } // DeleteFromRequestCancelInfoMaps mocks base method. func (m *MockDB) DeleteFromRequestCancelInfoMaps(ctx context.Context, filter *RequestCancelInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromRequestCancelInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromRequestCancelInfoMaps indicates an expected call of DeleteFromRequestCancelInfoMaps. func (mr *MockDBMockRecorder) DeleteFromRequestCancelInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromRequestCancelInfoMaps", reflect.TypeOf((*MockDB)(nil).DeleteFromRequestCancelInfoMaps), ctx, filter) } // DeleteFromSignalInfoMaps mocks base method. func (m *MockDB) DeleteFromSignalInfoMaps(ctx context.Context, filter *SignalInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromSignalInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromSignalInfoMaps indicates an expected call of DeleteFromSignalInfoMaps. func (mr *MockDBMockRecorder) DeleteFromSignalInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromSignalInfoMaps", reflect.TypeOf((*MockDB)(nil).DeleteFromSignalInfoMaps), ctx, filter) } // DeleteFromSignalsRequestedSets mocks base method. func (m *MockDB) DeleteFromSignalsRequestedSets(ctx context.Context, filter *SignalsRequestedSetsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromSignalsRequestedSets", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromSignalsRequestedSets indicates an expected call of DeleteFromSignalsRequestedSets. func (mr *MockDBMockRecorder) DeleteFromSignalsRequestedSets(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromSignalsRequestedSets", reflect.TypeOf((*MockDB)(nil).DeleteFromSignalsRequestedSets), ctx, filter) } // DeleteFromTaskLists mocks base method. func (m *MockDB) DeleteFromTaskLists(ctx context.Context, filter *TaskListsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTaskLists", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTaskLists indicates an expected call of DeleteFromTaskLists. func (mr *MockDBMockRecorder) DeleteFromTaskLists(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTaskLists", reflect.TypeOf((*MockDB)(nil).DeleteFromTaskLists), ctx, filter) } // DeleteFromTasks mocks base method. func (m *MockDB) DeleteFromTasks(ctx context.Context, filter *TasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTasks indicates an expected call of DeleteFromTasks. func (mr *MockDBMockRecorder) DeleteFromTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTasks", reflect.TypeOf((*MockDB)(nil).DeleteFromTasks), ctx, filter) } // DeleteFromTimerInfoMaps mocks base method. func (m *MockDB) DeleteFromTimerInfoMaps(ctx context.Context, filter *TimerInfoMapsFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTimerInfoMaps", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTimerInfoMaps indicates an expected call of DeleteFromTimerInfoMaps. func (mr *MockDBMockRecorder) DeleteFromTimerInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTimerInfoMaps", reflect.TypeOf((*MockDB)(nil).DeleteFromTimerInfoMaps), ctx, filter) } // DeleteFromTimerTasks mocks base method. func (m *MockDB) DeleteFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTimerTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTimerTasks indicates an expected call of DeleteFromTimerTasks. func (mr *MockDBMockRecorder) DeleteFromTimerTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTimerTasks", reflect.TypeOf((*MockDB)(nil).DeleteFromTimerTasks), ctx, filter) } // DeleteFromTransferTasks mocks base method. func (m *MockDB) DeleteFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromTransferTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromTransferTasks indicates an expected call of DeleteFromTransferTasks. func (mr *MockDBMockRecorder) DeleteFromTransferTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromTransferTasks", reflect.TypeOf((*MockDB)(nil).DeleteFromTransferTasks), ctx, filter) } // DeleteFromVisibility mocks base method. func (m *MockDB) DeleteFromVisibility(ctx context.Context, filter *VisibilityFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFromVisibility", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteFromVisibility indicates an expected call of DeleteFromVisibility. func (mr *MockDBMockRecorder) DeleteFromVisibility(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFromVisibility", reflect.TypeOf((*MockDB)(nil).DeleteFromVisibility), ctx, filter) } // DeleteMessage mocks base method. func (m *MockDB) DeleteMessage(ctx context.Context, queueType persistence.QueueType, messageID int64) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessage", ctx, queueType, messageID) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteMessage indicates an expected call of DeleteMessage. func (mr *MockDBMockRecorder) DeleteMessage(ctx, queueType, messageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessage", reflect.TypeOf((*MockDB)(nil).DeleteMessage), ctx, queueType, messageID) } // DeleteMessageFromReplicationTasksDLQ mocks base method. func (m *MockDB) DeleteMessageFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessageFromReplicationTasksDLQ", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteMessageFromReplicationTasksDLQ indicates an expected call of DeleteMessageFromReplicationTasksDLQ. func (mr *MockDBMockRecorder) DeleteMessageFromReplicationTasksDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessageFromReplicationTasksDLQ", reflect.TypeOf((*MockDB)(nil).DeleteMessageFromReplicationTasksDLQ), ctx, filter) } // DeleteMessagesBefore mocks base method. func (m *MockDB) DeleteMessagesBefore(ctx context.Context, queueType persistence.QueueType, messageID int64) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteMessagesBefore", ctx, queueType, messageID) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteMessagesBefore indicates an expected call of DeleteMessagesBefore. func (mr *MockDBMockRecorder) DeleteMessagesBefore(ctx, queueType, messageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessagesBefore", reflect.TypeOf((*MockDB)(nil).DeleteMessagesBefore), ctx, queueType, messageID) } // GetAckLevels mocks base method. func (m *MockDB) GetAckLevels(ctx context.Context, queueType persistence.QueueType, forUpdate bool) (map[string]int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAckLevels", ctx, queueType, forUpdate) ret0, _ := ret[0].(map[string]int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAckLevels indicates an expected call of GetAckLevels. func (mr *MockDBMockRecorder) GetAckLevels(ctx, queueType, forUpdate any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAckLevels", reflect.TypeOf((*MockDB)(nil).GetAckLevels), ctx, queueType, forUpdate) } // GetAllHistoryTreeBranches mocks base method. func (m *MockDB) GetAllHistoryTreeBranches(ctx context.Context, filter *HistoryTreeFilter) ([]HistoryTreeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllHistoryTreeBranches", ctx, filter) ret0, _ := ret[0].([]HistoryTreeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAllHistoryTreeBranches indicates an expected call of GetAllHistoryTreeBranches. func (mr *MockDBMockRecorder) GetAllHistoryTreeBranches(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllHistoryTreeBranches", reflect.TypeOf((*MockDB)(nil).GetAllHistoryTreeBranches), ctx, filter) } // GetLastEnqueuedMessageIDForUpdate mocks base method. func (m *MockDB) GetLastEnqueuedMessageIDForUpdate(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLastEnqueuedMessageIDForUpdate", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLastEnqueuedMessageIDForUpdate indicates an expected call of GetLastEnqueuedMessageIDForUpdate. func (mr *MockDBMockRecorder) GetLastEnqueuedMessageIDForUpdate(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastEnqueuedMessageIDForUpdate", reflect.TypeOf((*MockDB)(nil).GetLastEnqueuedMessageIDForUpdate), ctx, queueType) } // GetMessagesBetween mocks base method. func (m *MockDB) GetMessagesBetween(ctx context.Context, queueType persistence.QueueType, firstMessageID, lastMessageID int64, maxRows int) ([]QueueRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMessagesBetween", ctx, queueType, firstMessageID, lastMessageID, maxRows) ret0, _ := ret[0].([]QueueRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMessagesBetween indicates an expected call of GetMessagesBetween. func (mr *MockDBMockRecorder) GetMessagesBetween(ctx, queueType, firstMessageID, lastMessageID, maxRows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessagesBetween", reflect.TypeOf((*MockDB)(nil).GetMessagesBetween), ctx, queueType, firstMessageID, lastMessageID, maxRows) } // GetMessagesFromQueue mocks base method. func (m *MockDB) GetMessagesFromQueue(ctx context.Context, queueType persistence.QueueType, lastMessageID int64, maxRows int) ([]QueueRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMessagesFromQueue", ctx, queueType, lastMessageID, maxRows) ret0, _ := ret[0].([]QueueRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMessagesFromQueue indicates an expected call of GetMessagesFromQueue. func (mr *MockDBMockRecorder) GetMessagesFromQueue(ctx, queueType, lastMessageID, maxRows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessagesFromQueue", reflect.TypeOf((*MockDB)(nil).GetMessagesFromQueue), ctx, queueType, lastMessageID, maxRows) } // GetOrphanTasks mocks base method. func (m *MockDB) GetOrphanTasks(ctx context.Context, filter *OrphanTasksFilter) ([]TaskKeyRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrphanTasks", ctx, filter) ret0, _ := ret[0].([]TaskKeyRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetOrphanTasks indicates an expected call of GetOrphanTasks. func (mr *MockDBMockRecorder) GetOrphanTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrphanTasks", reflect.TypeOf((*MockDB)(nil).GetOrphanTasks), ctx, filter) } // GetQueueSize mocks base method. func (m *MockDB) GetQueueSize(ctx context.Context, queueType persistence.QueueType) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueSize", ctx, queueType) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetQueueSize indicates an expected call of GetQueueSize. func (mr *MockDBMockRecorder) GetQueueSize(ctx, queueType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueSize", reflect.TypeOf((*MockDB)(nil).GetQueueSize), ctx, queueType) } // GetTasksCount mocks base method. func (m *MockDB) GetTasksCount(ctx context.Context, filter *TasksFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasksCount", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTasksCount indicates an expected call of GetTasksCount. func (mr *MockDBMockRecorder) GetTasksCount(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasksCount", reflect.TypeOf((*MockDB)(nil).GetTasksCount), ctx, filter) } // GetTotalNumDBShards mocks base method. func (m *MockDB) GetTotalNumDBShards() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTotalNumDBShards") ret0, _ := ret[0].(int) return ret0 } // GetTotalNumDBShards indicates an expected call of GetTotalNumDBShards. func (mr *MockDBMockRecorder) GetTotalNumDBShards() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTotalNumDBShards", reflect.TypeOf((*MockDB)(nil).GetTotalNumDBShards)) } // InsertAckLevel mocks base method. func (m *MockDB) InsertAckLevel(ctx context.Context, queueType persistence.QueueType, messageID int64, clusterName string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertAckLevel", ctx, queueType, messageID, clusterName) ret0, _ := ret[0].(error) return ret0 } // InsertAckLevel indicates an expected call of InsertAckLevel. func (mr *MockDBMockRecorder) InsertAckLevel(ctx, queueType, messageID, clusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertAckLevel", reflect.TypeOf((*MockDB)(nil).InsertAckLevel), ctx, queueType, messageID, clusterName) } // InsertConfig mocks base method. func (m *MockDB) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertConfig", ctx, row) ret0, _ := ret[0].(error) return ret0 } // InsertConfig indicates an expected call of InsertConfig. func (mr *MockDBMockRecorder) InsertConfig(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertConfig", reflect.TypeOf((*MockDB)(nil).InsertConfig), ctx, row) } // InsertIntoBufferedEvents mocks base method. func (m *MockDB) InsertIntoBufferedEvents(ctx context.Context, rows []BufferedEventsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoBufferedEvents", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoBufferedEvents indicates an expected call of InsertIntoBufferedEvents. func (mr *MockDBMockRecorder) InsertIntoBufferedEvents(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoBufferedEvents", reflect.TypeOf((*MockDB)(nil).InsertIntoBufferedEvents), ctx, rows) } // InsertIntoCrossClusterTasks mocks base method. func (m *MockDB) InsertIntoCrossClusterTasks(ctx context.Context, rows []CrossClusterTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoCrossClusterTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoCrossClusterTasks indicates an expected call of InsertIntoCrossClusterTasks. func (mr *MockDBMockRecorder) InsertIntoCrossClusterTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoCrossClusterTasks", reflect.TypeOf((*MockDB)(nil).InsertIntoCrossClusterTasks), ctx, rows) } // InsertIntoCurrentExecutions mocks base method. func (m *MockDB) InsertIntoCurrentExecutions(ctx context.Context, row *CurrentExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoCurrentExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoCurrentExecutions indicates an expected call of InsertIntoCurrentExecutions. func (mr *MockDBMockRecorder) InsertIntoCurrentExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoCurrentExecutions", reflect.TypeOf((*MockDB)(nil).InsertIntoCurrentExecutions), ctx, row) } // InsertIntoDomain mocks base method. func (m *MockDB) InsertIntoDomain(ctx context.Context, rows *DomainRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoDomain", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoDomain indicates an expected call of InsertIntoDomain. func (mr *MockDBMockRecorder) InsertIntoDomain(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoDomain", reflect.TypeOf((*MockDB)(nil).InsertIntoDomain), ctx, rows) } // InsertIntoDomainAuditLog mocks base method. func (m *MockDB) InsertIntoDomainAuditLog(ctx context.Context, row *DomainAuditLogRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoDomainAuditLog", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoDomainAuditLog indicates an expected call of InsertIntoDomainAuditLog. func (mr *MockDBMockRecorder) InsertIntoDomainAuditLog(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoDomainAuditLog", reflect.TypeOf((*MockDB)(nil).InsertIntoDomainAuditLog), ctx, row) } // InsertIntoExecutions mocks base method. func (m *MockDB) InsertIntoExecutions(ctx context.Context, row *ExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoExecutions indicates an expected call of InsertIntoExecutions. func (mr *MockDBMockRecorder) InsertIntoExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoExecutions", reflect.TypeOf((*MockDB)(nil).InsertIntoExecutions), ctx, row) } // InsertIntoHistoryNode mocks base method. func (m *MockDB) InsertIntoHistoryNode(ctx context.Context, row *HistoryNodeRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoHistoryNode", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoHistoryNode indicates an expected call of InsertIntoHistoryNode. func (mr *MockDBMockRecorder) InsertIntoHistoryNode(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoHistoryNode", reflect.TypeOf((*MockDB)(nil).InsertIntoHistoryNode), ctx, row) } // InsertIntoHistoryTree mocks base method. func (m *MockDB) InsertIntoHistoryTree(ctx context.Context, row *HistoryTreeRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoHistoryTree", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoHistoryTree indicates an expected call of InsertIntoHistoryTree. func (mr *MockDBMockRecorder) InsertIntoHistoryTree(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoHistoryTree", reflect.TypeOf((*MockDB)(nil).InsertIntoHistoryTree), ctx, row) } // InsertIntoQueue mocks base method. func (m *MockDB) InsertIntoQueue(ctx context.Context, row *QueueRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoQueue", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoQueue indicates an expected call of InsertIntoQueue. func (mr *MockDBMockRecorder) InsertIntoQueue(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoQueue", reflect.TypeOf((*MockDB)(nil).InsertIntoQueue), ctx, row) } // InsertIntoReplicationTasks mocks base method. func (m *MockDB) InsertIntoReplicationTasks(ctx context.Context, rows []ReplicationTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoReplicationTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoReplicationTasks indicates an expected call of InsertIntoReplicationTasks. func (mr *MockDBMockRecorder) InsertIntoReplicationTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoReplicationTasks", reflect.TypeOf((*MockDB)(nil).InsertIntoReplicationTasks), ctx, rows) } // InsertIntoReplicationTasksDLQ mocks base method. func (m *MockDB) InsertIntoReplicationTasksDLQ(ctx context.Context, row *ReplicationTaskDLQRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoReplicationTasksDLQ", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoReplicationTasksDLQ indicates an expected call of InsertIntoReplicationTasksDLQ. func (mr *MockDBMockRecorder) InsertIntoReplicationTasksDLQ(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoReplicationTasksDLQ", reflect.TypeOf((*MockDB)(nil).InsertIntoReplicationTasksDLQ), ctx, row) } // InsertIntoShards mocks base method. func (m *MockDB) InsertIntoShards(ctx context.Context, rows *ShardsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoShards", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoShards indicates an expected call of InsertIntoShards. func (mr *MockDBMockRecorder) InsertIntoShards(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoShards", reflect.TypeOf((*MockDB)(nil).InsertIntoShards), ctx, rows) } // InsertIntoSignalsRequestedSets mocks base method. func (m *MockDB) InsertIntoSignalsRequestedSets(ctx context.Context, rows []SignalsRequestedSetsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoSignalsRequestedSets", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoSignalsRequestedSets indicates an expected call of InsertIntoSignalsRequestedSets. func (mr *MockDBMockRecorder) InsertIntoSignalsRequestedSets(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoSignalsRequestedSets", reflect.TypeOf((*MockDB)(nil).InsertIntoSignalsRequestedSets), ctx, rows) } // InsertIntoTaskLists mocks base method. func (m *MockDB) InsertIntoTaskLists(ctx context.Context, row *TaskListsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTaskLists", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTaskLists indicates an expected call of InsertIntoTaskLists. func (mr *MockDBMockRecorder) InsertIntoTaskLists(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTaskLists", reflect.TypeOf((*MockDB)(nil).InsertIntoTaskLists), ctx, row) } // InsertIntoTaskListsWithTTL mocks base method. func (m *MockDB) InsertIntoTaskListsWithTTL(ctx context.Context, row *TaskListsRowWithTTL) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTaskListsWithTTL", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTaskListsWithTTL indicates an expected call of InsertIntoTaskListsWithTTL. func (mr *MockDBMockRecorder) InsertIntoTaskListsWithTTL(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTaskListsWithTTL", reflect.TypeOf((*MockDB)(nil).InsertIntoTaskListsWithTTL), ctx, row) } // InsertIntoTasks mocks base method. func (m *MockDB) InsertIntoTasks(ctx context.Context, rows []TasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTasks indicates an expected call of InsertIntoTasks. func (mr *MockDBMockRecorder) InsertIntoTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTasks", reflect.TypeOf((*MockDB)(nil).InsertIntoTasks), ctx, rows) } // InsertIntoTasksWithTTL mocks base method. func (m *MockDB) InsertIntoTasksWithTTL(ctx context.Context, rows []TasksRowWithTTL) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTasksWithTTL", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTasksWithTTL indicates an expected call of InsertIntoTasksWithTTL. func (mr *MockDBMockRecorder) InsertIntoTasksWithTTL(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTasksWithTTL", reflect.TypeOf((*MockDB)(nil).InsertIntoTasksWithTTL), ctx, rows) } // InsertIntoTimerTasks mocks base method. func (m *MockDB) InsertIntoTimerTasks(ctx context.Context, rows []TimerTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTimerTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTimerTasks indicates an expected call of InsertIntoTimerTasks. func (mr *MockDBMockRecorder) InsertIntoTimerTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTimerTasks", reflect.TypeOf((*MockDB)(nil).InsertIntoTimerTasks), ctx, rows) } // InsertIntoTransferTasks mocks base method. func (m *MockDB) InsertIntoTransferTasks(ctx context.Context, rows []TransferTasksRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoTransferTasks", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoTransferTasks indicates an expected call of InsertIntoTransferTasks. func (mr *MockDBMockRecorder) InsertIntoTransferTasks(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoTransferTasks", reflect.TypeOf((*MockDB)(nil).InsertIntoTransferTasks), ctx, rows) } // InsertIntoVisibility mocks base method. func (m *MockDB) InsertIntoVisibility(ctx context.Context, row *VisibilityRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertIntoVisibility", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // InsertIntoVisibility indicates an expected call of InsertIntoVisibility. func (mr *MockDBMockRecorder) InsertIntoVisibility(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertIntoVisibility", reflect.TypeOf((*MockDB)(nil).InsertIntoVisibility), ctx, row) } // IsDupEntryError mocks base method. func (m *MockDB) IsDupEntryError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsDupEntryError", err) ret0, _ := ret[0].(bool) return ret0 } // IsDupEntryError indicates an expected call of IsDupEntryError. func (mr *MockDBMockRecorder) IsDupEntryError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDupEntryError", reflect.TypeOf((*MockDB)(nil).IsDupEntryError), err) } // IsNotFoundError mocks base method. func (m *MockDB) IsNotFoundError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsNotFoundError", err) ret0, _ := ret[0].(bool) return ret0 } // IsNotFoundError indicates an expected call of IsNotFoundError. func (mr *MockDBMockRecorder) IsNotFoundError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNotFoundError", reflect.TypeOf((*MockDB)(nil).IsNotFoundError), err) } // IsThrottlingError mocks base method. func (m *MockDB) IsThrottlingError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsThrottlingError", err) ret0, _ := ret[0].(bool) return ret0 } // IsThrottlingError indicates an expected call of IsThrottlingError. func (mr *MockDBMockRecorder) IsThrottlingError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsThrottlingError", reflect.TypeOf((*MockDB)(nil).IsThrottlingError), err) } // IsTimeoutError mocks base method. func (m *MockDB) IsTimeoutError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsTimeoutError", err) ret0, _ := ret[0].(bool) return ret0 } // IsTimeoutError indicates an expected call of IsTimeoutError. func (mr *MockDBMockRecorder) IsTimeoutError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsTimeoutError", reflect.TypeOf((*MockDB)(nil).IsTimeoutError), err) } // LockCurrentExecutions mocks base method. func (m *MockDB) LockCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (*CurrentExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockCurrentExecutions", ctx, filter) ret0, _ := ret[0].(*CurrentExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // LockCurrentExecutions indicates an expected call of LockCurrentExecutions. func (mr *MockDBMockRecorder) LockCurrentExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockCurrentExecutions", reflect.TypeOf((*MockDB)(nil).LockCurrentExecutions), ctx, filter) } // LockCurrentExecutionsJoinExecutions mocks base method. func (m *MockDB) LockCurrentExecutionsJoinExecutions(ctx context.Context, filter *CurrentExecutionsFilter) ([]CurrentExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockCurrentExecutionsJoinExecutions", ctx, filter) ret0, _ := ret[0].([]CurrentExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // LockCurrentExecutionsJoinExecutions indicates an expected call of LockCurrentExecutionsJoinExecutions. func (mr *MockDBMockRecorder) LockCurrentExecutionsJoinExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockCurrentExecutionsJoinExecutions", reflect.TypeOf((*MockDB)(nil).LockCurrentExecutionsJoinExecutions), ctx, filter) } // LockDomainMetadata mocks base method. func (m *MockDB) LockDomainMetadata(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockDomainMetadata", ctx) ret0, _ := ret[0].(error) return ret0 } // LockDomainMetadata indicates an expected call of LockDomainMetadata. func (mr *MockDBMockRecorder) LockDomainMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockDomainMetadata", reflect.TypeOf((*MockDB)(nil).LockDomainMetadata), ctx) } // LockTaskLists mocks base method. func (m *MockDB) LockTaskLists(ctx context.Context, filter *TaskListsFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LockTaskLists", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // LockTaskLists indicates an expected call of LockTaskLists. func (mr *MockDBMockRecorder) LockTaskLists(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockTaskLists", reflect.TypeOf((*MockDB)(nil).LockTaskLists), ctx, filter) } // MaxAllowedTTL mocks base method. func (m *MockDB) MaxAllowedTTL() (*time.Duration, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MaxAllowedTTL") ret0, _ := ret[0].(*time.Duration) ret1, _ := ret[1].(error) return ret0, ret1 } // MaxAllowedTTL indicates an expected call of MaxAllowedTTL. func (mr *MockDBMockRecorder) MaxAllowedTTL() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxAllowedTTL", reflect.TypeOf((*MockDB)(nil).MaxAllowedTTL)) } // PluginName mocks base method. func (m *MockDB) PluginName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PluginName") ret0, _ := ret[0].(string) return ret0 } // PluginName indicates an expected call of PluginName. func (mr *MockDBMockRecorder) PluginName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginName", reflect.TypeOf((*MockDB)(nil).PluginName)) } // RangeDeleteFromCrossClusterTasks mocks base method. func (m *MockDB) RangeDeleteFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromCrossClusterTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromCrossClusterTasks indicates an expected call of RangeDeleteFromCrossClusterTasks. func (mr *MockDBMockRecorder) RangeDeleteFromCrossClusterTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromCrossClusterTasks", reflect.TypeOf((*MockDB)(nil).RangeDeleteFromCrossClusterTasks), ctx, filter) } // RangeDeleteFromReplicationTasks mocks base method. func (m *MockDB) RangeDeleteFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromReplicationTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromReplicationTasks indicates an expected call of RangeDeleteFromReplicationTasks. func (mr *MockDBMockRecorder) RangeDeleteFromReplicationTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromReplicationTasks", reflect.TypeOf((*MockDB)(nil).RangeDeleteFromReplicationTasks), ctx, filter) } // RangeDeleteFromTimerTasks mocks base method. func (m *MockDB) RangeDeleteFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromTimerTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromTimerTasks indicates an expected call of RangeDeleteFromTimerTasks. func (mr *MockDBMockRecorder) RangeDeleteFromTimerTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromTimerTasks", reflect.TypeOf((*MockDB)(nil).RangeDeleteFromTimerTasks), ctx, filter) } // RangeDeleteFromTransferTasks mocks base method. func (m *MockDB) RangeDeleteFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteFromTransferTasks", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteFromTransferTasks indicates an expected call of RangeDeleteFromTransferTasks. func (mr *MockDBMockRecorder) RangeDeleteFromTransferTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteFromTransferTasks", reflect.TypeOf((*MockDB)(nil).RangeDeleteFromTransferTasks), ctx, filter) } // RangeDeleteMessageFromReplicationTasksDLQ mocks base method. func (m *MockDB) RangeDeleteMessageFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteMessageFromReplicationTasksDLQ", ctx, filter) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteMessageFromReplicationTasksDLQ indicates an expected call of RangeDeleteMessageFromReplicationTasksDLQ. func (mr *MockDBMockRecorder) RangeDeleteMessageFromReplicationTasksDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteMessageFromReplicationTasksDLQ", reflect.TypeOf((*MockDB)(nil).RangeDeleteMessageFromReplicationTasksDLQ), ctx, filter) } // RangeDeleteMessages mocks base method. func (m *MockDB) RangeDeleteMessages(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID, inclusiveEndMessageID int64) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RangeDeleteMessages", ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // RangeDeleteMessages indicates an expected call of RangeDeleteMessages. func (mr *MockDBMockRecorder) RangeDeleteMessages(ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RangeDeleteMessages", reflect.TypeOf((*MockDB)(nil).RangeDeleteMessages), ctx, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) } // ReadLockExecutions mocks base method. func (m *MockDB) ReadLockExecutions(ctx context.Context, filter *ExecutionsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadLockExecutions", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadLockExecutions indicates an expected call of ReadLockExecutions. func (mr *MockDBMockRecorder) ReadLockExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadLockExecutions", reflect.TypeOf((*MockDB)(nil).ReadLockExecutions), ctx, filter) } // ReadLockShards mocks base method. func (m *MockDB) ReadLockShards(ctx context.Context, filter *ShardsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadLockShards", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadLockShards indicates an expected call of ReadLockShards. func (mr *MockDBMockRecorder) ReadLockShards(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadLockShards", reflect.TypeOf((*MockDB)(nil).ReadLockShards), ctx, filter) } // ReplaceIntoActivityInfoMaps mocks base method. func (m *MockDB) ReplaceIntoActivityInfoMaps(ctx context.Context, rows []ActivityInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoActivityInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoActivityInfoMaps indicates an expected call of ReplaceIntoActivityInfoMaps. func (mr *MockDBMockRecorder) ReplaceIntoActivityInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoActivityInfoMaps", reflect.TypeOf((*MockDB)(nil).ReplaceIntoActivityInfoMaps), ctx, rows) } // ReplaceIntoChildExecutionInfoMaps mocks base method. func (m *MockDB) ReplaceIntoChildExecutionInfoMaps(ctx context.Context, rows []ChildExecutionInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoChildExecutionInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoChildExecutionInfoMaps indicates an expected call of ReplaceIntoChildExecutionInfoMaps. func (mr *MockDBMockRecorder) ReplaceIntoChildExecutionInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoChildExecutionInfoMaps", reflect.TypeOf((*MockDB)(nil).ReplaceIntoChildExecutionInfoMaps), ctx, rows) } // ReplaceIntoRequestCancelInfoMaps mocks base method. func (m *MockDB) ReplaceIntoRequestCancelInfoMaps(ctx context.Context, rows []RequestCancelInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoRequestCancelInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoRequestCancelInfoMaps indicates an expected call of ReplaceIntoRequestCancelInfoMaps. func (mr *MockDBMockRecorder) ReplaceIntoRequestCancelInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoRequestCancelInfoMaps", reflect.TypeOf((*MockDB)(nil).ReplaceIntoRequestCancelInfoMaps), ctx, rows) } // ReplaceIntoSignalInfoMaps mocks base method. func (m *MockDB) ReplaceIntoSignalInfoMaps(ctx context.Context, rows []SignalInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoSignalInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoSignalInfoMaps indicates an expected call of ReplaceIntoSignalInfoMaps. func (mr *MockDBMockRecorder) ReplaceIntoSignalInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoSignalInfoMaps", reflect.TypeOf((*MockDB)(nil).ReplaceIntoSignalInfoMaps), ctx, rows) } // ReplaceIntoTimerInfoMaps mocks base method. func (m *MockDB) ReplaceIntoTimerInfoMaps(ctx context.Context, rows []TimerInfoMapsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoTimerInfoMaps", ctx, rows) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoTimerInfoMaps indicates an expected call of ReplaceIntoTimerInfoMaps. func (mr *MockDBMockRecorder) ReplaceIntoTimerInfoMaps(ctx, rows any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoTimerInfoMaps", reflect.TypeOf((*MockDB)(nil).ReplaceIntoTimerInfoMaps), ctx, rows) } // ReplaceIntoVisibility mocks base method. func (m *MockDB) ReplaceIntoVisibility(ctx context.Context, row *VisibilityRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplaceIntoVisibility", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplaceIntoVisibility indicates an expected call of ReplaceIntoVisibility. func (mr *MockDBMockRecorder) ReplaceIntoVisibility(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceIntoVisibility", reflect.TypeOf((*MockDB)(nil).ReplaceIntoVisibility), ctx, row) } // SelectFromActivityInfoMaps mocks base method. func (m *MockDB) SelectFromActivityInfoMaps(ctx context.Context, filter *ActivityInfoMapsFilter) ([]ActivityInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromActivityInfoMaps", ctx, filter) ret0, _ := ret[0].([]ActivityInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromActivityInfoMaps indicates an expected call of SelectFromActivityInfoMaps. func (mr *MockDBMockRecorder) SelectFromActivityInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromActivityInfoMaps", reflect.TypeOf((*MockDB)(nil).SelectFromActivityInfoMaps), ctx, filter) } // SelectFromBufferedEvents mocks base method. func (m *MockDB) SelectFromBufferedEvents(ctx context.Context, filter *BufferedEventsFilter) ([]BufferedEventsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromBufferedEvents", ctx, filter) ret0, _ := ret[0].([]BufferedEventsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromBufferedEvents indicates an expected call of SelectFromBufferedEvents. func (mr *MockDBMockRecorder) SelectFromBufferedEvents(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromBufferedEvents", reflect.TypeOf((*MockDB)(nil).SelectFromBufferedEvents), ctx, filter) } // SelectFromChildExecutionInfoMaps mocks base method. func (m *MockDB) SelectFromChildExecutionInfoMaps(ctx context.Context, filter *ChildExecutionInfoMapsFilter) ([]ChildExecutionInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromChildExecutionInfoMaps", ctx, filter) ret0, _ := ret[0].([]ChildExecutionInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromChildExecutionInfoMaps indicates an expected call of SelectFromChildExecutionInfoMaps. func (mr *MockDBMockRecorder) SelectFromChildExecutionInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromChildExecutionInfoMaps", reflect.TypeOf((*MockDB)(nil).SelectFromChildExecutionInfoMaps), ctx, filter) } // SelectFromCrossClusterTasks mocks base method. func (m *MockDB) SelectFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) ([]CrossClusterTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromCrossClusterTasks", ctx, filter) ret0, _ := ret[0].([]CrossClusterTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromCrossClusterTasks indicates an expected call of SelectFromCrossClusterTasks. func (mr *MockDBMockRecorder) SelectFromCrossClusterTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromCrossClusterTasks", reflect.TypeOf((*MockDB)(nil).SelectFromCrossClusterTasks), ctx, filter) } // SelectFromCurrentExecutions mocks base method. func (m *MockDB) SelectFromCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (*CurrentExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromCurrentExecutions", ctx, filter) ret0, _ := ret[0].(*CurrentExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromCurrentExecutions indicates an expected call of SelectFromCurrentExecutions. func (mr *MockDBMockRecorder) SelectFromCurrentExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromCurrentExecutions", reflect.TypeOf((*MockDB)(nil).SelectFromCurrentExecutions), ctx, filter) } // SelectFromDomain mocks base method. func (m *MockDB) SelectFromDomain(ctx context.Context, filter *DomainFilter) ([]DomainRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromDomain", ctx, filter) ret0, _ := ret[0].([]DomainRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromDomain indicates an expected call of SelectFromDomain. func (mr *MockDBMockRecorder) SelectFromDomain(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromDomain", reflect.TypeOf((*MockDB)(nil).SelectFromDomain), ctx, filter) } // SelectFromDomainAuditLogs mocks base method. func (m *MockDB) SelectFromDomainAuditLogs(ctx context.Context, filter *DomainAuditLogFilter) ([]*DomainAuditLogRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromDomainAuditLogs", ctx, filter) ret0, _ := ret[0].([]*DomainAuditLogRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromDomainAuditLogs indicates an expected call of SelectFromDomainAuditLogs. func (mr *MockDBMockRecorder) SelectFromDomainAuditLogs(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromDomainAuditLogs", reflect.TypeOf((*MockDB)(nil).SelectFromDomainAuditLogs), ctx, filter) } // SelectFromDomainMetadata mocks base method. func (m *MockDB) SelectFromDomainMetadata(ctx context.Context) (*DomainMetadataRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromDomainMetadata", ctx) ret0, _ := ret[0].(*DomainMetadataRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromDomainMetadata indicates an expected call of SelectFromDomainMetadata. func (mr *MockDBMockRecorder) SelectFromDomainMetadata(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromDomainMetadata", reflect.TypeOf((*MockDB)(nil).SelectFromDomainMetadata), ctx) } // SelectFromExecutions mocks base method. func (m *MockDB) SelectFromExecutions(ctx context.Context, filter *ExecutionsFilter) ([]ExecutionsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromExecutions", ctx, filter) ret0, _ := ret[0].([]ExecutionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromExecutions indicates an expected call of SelectFromExecutions. func (mr *MockDBMockRecorder) SelectFromExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromExecutions", reflect.TypeOf((*MockDB)(nil).SelectFromExecutions), ctx, filter) } // SelectFromHistoryNode mocks base method. func (m *MockDB) SelectFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) ([]HistoryNodeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryNode", ctx, filter) ret0, _ := ret[0].([]HistoryNodeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromHistoryNode indicates an expected call of SelectFromHistoryNode. func (mr *MockDBMockRecorder) SelectFromHistoryNode(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryNode", reflect.TypeOf((*MockDB)(nil).SelectFromHistoryNode), ctx, filter) } // SelectFromHistoryTree mocks base method. func (m *MockDB) SelectFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) ([]HistoryTreeRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromHistoryTree", ctx, filter) ret0, _ := ret[0].([]HistoryTreeRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromHistoryTree indicates an expected call of SelectFromHistoryTree. func (mr *MockDBMockRecorder) SelectFromHistoryTree(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromHistoryTree", reflect.TypeOf((*MockDB)(nil).SelectFromHistoryTree), ctx, filter) } // SelectFromReplicationDLQ mocks base method. func (m *MockDB) SelectFromReplicationDLQ(ctx context.Context, filter *ReplicationTaskDLQFilter) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromReplicationDLQ", ctx, filter) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromReplicationDLQ indicates an expected call of SelectFromReplicationDLQ. func (mr *MockDBMockRecorder) SelectFromReplicationDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromReplicationDLQ", reflect.TypeOf((*MockDB)(nil).SelectFromReplicationDLQ), ctx, filter) } // SelectFromReplicationTasks mocks base method. func (m *MockDB) SelectFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) ([]ReplicationTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromReplicationTasks", ctx, filter) ret0, _ := ret[0].([]ReplicationTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromReplicationTasks indicates an expected call of SelectFromReplicationTasks. func (mr *MockDBMockRecorder) SelectFromReplicationTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromReplicationTasks", reflect.TypeOf((*MockDB)(nil).SelectFromReplicationTasks), ctx, filter) } // SelectFromReplicationTasksDLQ mocks base method. func (m *MockDB) SelectFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) ([]ReplicationTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromReplicationTasksDLQ", ctx, filter) ret0, _ := ret[0].([]ReplicationTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromReplicationTasksDLQ indicates an expected call of SelectFromReplicationTasksDLQ. func (mr *MockDBMockRecorder) SelectFromReplicationTasksDLQ(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromReplicationTasksDLQ", reflect.TypeOf((*MockDB)(nil).SelectFromReplicationTasksDLQ), ctx, filter) } // SelectFromRequestCancelInfoMaps mocks base method. func (m *MockDB) SelectFromRequestCancelInfoMaps(ctx context.Context, filter *RequestCancelInfoMapsFilter) ([]RequestCancelInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromRequestCancelInfoMaps", ctx, filter) ret0, _ := ret[0].([]RequestCancelInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromRequestCancelInfoMaps indicates an expected call of SelectFromRequestCancelInfoMaps. func (mr *MockDBMockRecorder) SelectFromRequestCancelInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromRequestCancelInfoMaps", reflect.TypeOf((*MockDB)(nil).SelectFromRequestCancelInfoMaps), ctx, filter) } // SelectFromShards mocks base method. func (m *MockDB) SelectFromShards(ctx context.Context, filter *ShardsFilter) (*ShardsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromShards", ctx, filter) ret0, _ := ret[0].(*ShardsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromShards indicates an expected call of SelectFromShards. func (mr *MockDBMockRecorder) SelectFromShards(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromShards", reflect.TypeOf((*MockDB)(nil).SelectFromShards), ctx, filter) } // SelectFromSignalInfoMaps mocks base method. func (m *MockDB) SelectFromSignalInfoMaps(ctx context.Context, filter *SignalInfoMapsFilter) ([]SignalInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromSignalInfoMaps", ctx, filter) ret0, _ := ret[0].([]SignalInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromSignalInfoMaps indicates an expected call of SelectFromSignalInfoMaps. func (mr *MockDBMockRecorder) SelectFromSignalInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromSignalInfoMaps", reflect.TypeOf((*MockDB)(nil).SelectFromSignalInfoMaps), ctx, filter) } // SelectFromSignalsRequestedSets mocks base method. func (m *MockDB) SelectFromSignalsRequestedSets(ctx context.Context, filter *SignalsRequestedSetsFilter) ([]SignalsRequestedSetsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromSignalsRequestedSets", ctx, filter) ret0, _ := ret[0].([]SignalsRequestedSetsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromSignalsRequestedSets indicates an expected call of SelectFromSignalsRequestedSets. func (mr *MockDBMockRecorder) SelectFromSignalsRequestedSets(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromSignalsRequestedSets", reflect.TypeOf((*MockDB)(nil).SelectFromSignalsRequestedSets), ctx, filter) } // SelectFromTaskLists mocks base method. func (m *MockDB) SelectFromTaskLists(ctx context.Context, filter *TaskListsFilter) ([]TaskListsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTaskLists", ctx, filter) ret0, _ := ret[0].([]TaskListsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTaskLists indicates an expected call of SelectFromTaskLists. func (mr *MockDBMockRecorder) SelectFromTaskLists(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTaskLists", reflect.TypeOf((*MockDB)(nil).SelectFromTaskLists), ctx, filter) } // SelectFromTasks mocks base method. func (m *MockDB) SelectFromTasks(ctx context.Context, filter *TasksFilter) ([]TasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTasks", ctx, filter) ret0, _ := ret[0].([]TasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTasks indicates an expected call of SelectFromTasks. func (mr *MockDBMockRecorder) SelectFromTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTasks", reflect.TypeOf((*MockDB)(nil).SelectFromTasks), ctx, filter) } // SelectFromTimerInfoMaps mocks base method. func (m *MockDB) SelectFromTimerInfoMaps(ctx context.Context, filter *TimerInfoMapsFilter) ([]TimerInfoMapsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTimerInfoMaps", ctx, filter) ret0, _ := ret[0].([]TimerInfoMapsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTimerInfoMaps indicates an expected call of SelectFromTimerInfoMaps. func (mr *MockDBMockRecorder) SelectFromTimerInfoMaps(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTimerInfoMaps", reflect.TypeOf((*MockDB)(nil).SelectFromTimerInfoMaps), ctx, filter) } // SelectFromTimerTasks mocks base method. func (m *MockDB) SelectFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) ([]TimerTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTimerTasks", ctx, filter) ret0, _ := ret[0].([]TimerTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTimerTasks indicates an expected call of SelectFromTimerTasks. func (mr *MockDBMockRecorder) SelectFromTimerTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTimerTasks", reflect.TypeOf((*MockDB)(nil).SelectFromTimerTasks), ctx, filter) } // SelectFromTransferTasks mocks base method. func (m *MockDB) SelectFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) ([]TransferTasksRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromTransferTasks", ctx, filter) ret0, _ := ret[0].([]TransferTasksRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromTransferTasks indicates an expected call of SelectFromTransferTasks. func (mr *MockDBMockRecorder) SelectFromTransferTasks(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromTransferTasks", reflect.TypeOf((*MockDB)(nil).SelectFromTransferTasks), ctx, filter) } // SelectFromVisibility mocks base method. func (m *MockDB) SelectFromVisibility(ctx context.Context, filter *VisibilityFilter) ([]VisibilityRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectFromVisibility", ctx, filter) ret0, _ := ret[0].([]VisibilityRow) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectFromVisibility indicates an expected call of SelectFromVisibility. func (mr *MockDBMockRecorder) SelectFromVisibility(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectFromVisibility", reflect.TypeOf((*MockDB)(nil).SelectFromVisibility), ctx, filter) } // SelectLatestConfig mocks base method. func (m *MockDB) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SelectLatestConfig", ctx, rowType) ret0, _ := ret[0].(*persistence.InternalConfigStoreEntry) ret1, _ := ret[1].(error) return ret0, ret1 } // SelectLatestConfig indicates an expected call of SelectLatestConfig. func (mr *MockDBMockRecorder) SelectLatestConfig(ctx, rowType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectLatestConfig", reflect.TypeOf((*MockDB)(nil).SelectLatestConfig), ctx, rowType) } // SupportsAsyncTransaction mocks base method. func (m *MockDB) SupportsAsyncTransaction() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SupportsAsyncTransaction") ret0, _ := ret[0].(bool) return ret0 } // SupportsAsyncTransaction indicates an expected call of SupportsAsyncTransaction. func (mr *MockDBMockRecorder) SupportsAsyncTransaction() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportsAsyncTransaction", reflect.TypeOf((*MockDB)(nil).SupportsAsyncTransaction)) } // SupportsTTL mocks base method. func (m *MockDB) SupportsTTL() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SupportsTTL") ret0, _ := ret[0].(bool) return ret0 } // SupportsTTL indicates an expected call of SupportsTTL. func (mr *MockDBMockRecorder) SupportsTTL() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportsTTL", reflect.TypeOf((*MockDB)(nil).SupportsTTL)) } // UpdateAckLevels mocks base method. func (m *MockDB) UpdateAckLevels(ctx context.Context, queueType persistence.QueueType, clusterAckLevels map[string]int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAckLevels", ctx, queueType, clusterAckLevels) ret0, _ := ret[0].(error) return ret0 } // UpdateAckLevels indicates an expected call of UpdateAckLevels. func (mr *MockDBMockRecorder) UpdateAckLevels(ctx, queueType, clusterAckLevels any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAckLevels", reflect.TypeOf((*MockDB)(nil).UpdateAckLevels), ctx, queueType, clusterAckLevels) } // UpdateCurrentExecutions mocks base method. func (m *MockDB) UpdateCurrentExecutions(ctx context.Context, row *CurrentExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateCurrentExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateCurrentExecutions indicates an expected call of UpdateCurrentExecutions. func (mr *MockDBMockRecorder) UpdateCurrentExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCurrentExecutions", reflect.TypeOf((*MockDB)(nil).UpdateCurrentExecutions), ctx, row) } // UpdateDomain mocks base method. func (m *MockDB) UpdateDomain(ctx context.Context, row *DomainRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomain", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MockDBMockRecorder) UpdateDomain(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MockDB)(nil).UpdateDomain), ctx, row) } // UpdateDomainMetadata mocks base method. func (m *MockDB) UpdateDomainMetadata(ctx context.Context, row *DomainMetadataRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomainMetadata", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomainMetadata indicates an expected call of UpdateDomainMetadata. func (mr *MockDBMockRecorder) UpdateDomainMetadata(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomainMetadata", reflect.TypeOf((*MockDB)(nil).UpdateDomainMetadata), ctx, row) } // UpdateExecutions mocks base method. func (m *MockDB) UpdateExecutions(ctx context.Context, row *ExecutionsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateExecutions", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateExecutions indicates an expected call of UpdateExecutions. func (mr *MockDBMockRecorder) UpdateExecutions(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateExecutions", reflect.TypeOf((*MockDB)(nil).UpdateExecutions), ctx, row) } // UpdateShards mocks base method. func (m *MockDB) UpdateShards(ctx context.Context, row *ShardsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateShards", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateShards indicates an expected call of UpdateShards. func (mr *MockDBMockRecorder) UpdateShards(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateShards", reflect.TypeOf((*MockDB)(nil).UpdateShards), ctx, row) } // UpdateTaskLists mocks base method. func (m *MockDB) UpdateTaskLists(ctx context.Context, row *TaskListsRow) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskLists", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskLists indicates an expected call of UpdateTaskLists. func (mr *MockDBMockRecorder) UpdateTaskLists(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskLists", reflect.TypeOf((*MockDB)(nil).UpdateTaskLists), ctx, row) } // UpdateTaskListsWithTTL mocks base method. func (m *MockDB) UpdateTaskListsWithTTL(ctx context.Context, row *TaskListsRowWithTTL) (sql.Result, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskListsWithTTL", ctx, row) ret0, _ := ret[0].(sql.Result) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskListsWithTTL indicates an expected call of UpdateTaskListsWithTTL. func (mr *MockDBMockRecorder) UpdateTaskListsWithTTL(ctx, row any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListsWithTTL", reflect.TypeOf((*MockDB)(nil).UpdateTaskListsWithTTL), ctx, row) } // WriteLockExecutions mocks base method. func (m *MockDB) WriteLockExecutions(ctx context.Context, filter *ExecutionsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteLockExecutions", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // WriteLockExecutions indicates an expected call of WriteLockExecutions. func (mr *MockDBMockRecorder) WriteLockExecutions(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteLockExecutions", reflect.TypeOf((*MockDB)(nil).WriteLockExecutions), ctx, filter) } // WriteLockShards mocks base method. func (m *MockDB) WriteLockShards(ctx context.Context, filter *ShardsFilter) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteLockShards", ctx, filter) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // WriteLockShards indicates an expected call of WriteLockShards. func (mr *MockDBMockRecorder) WriteLockShards(ctx, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteLockShards", reflect.TypeOf((*MockDB)(nil).WriteLockShards), ctx, filter) } // MockAdminDB is a mock of AdminDB interface. type MockAdminDB struct { ctrl *gomock.Controller recorder *MockAdminDBMockRecorder isgomock struct{} } // MockAdminDBMockRecorder is the mock recorder for MockAdminDB. type MockAdminDBMockRecorder struct { mock *MockAdminDB } // NewMockAdminDB creates a new mock instance. func NewMockAdminDB(ctrl *gomock.Controller) *MockAdminDB { mock := &MockAdminDB{ctrl: ctrl} mock.recorder = &MockAdminDBMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockAdminDB) EXPECT() *MockAdminDBMockRecorder { return m.recorder } // Close mocks base method. func (m *MockAdminDB) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockAdminDBMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockAdminDB)(nil).Close)) } // CreateDatabase mocks base method. func (m *MockAdminDB) CreateDatabase(database string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateDatabase", database) ret0, _ := ret[0].(error) return ret0 } // CreateDatabase indicates an expected call of CreateDatabase. func (mr *MockAdminDBMockRecorder) CreateDatabase(database any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDatabase", reflect.TypeOf((*MockAdminDB)(nil).CreateDatabase), database) } // CreateSchemaVersionTables mocks base method. func (m *MockAdminDB) CreateSchemaVersionTables() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateSchemaVersionTables") ret0, _ := ret[0].(error) return ret0 } // CreateSchemaVersionTables indicates an expected call of CreateSchemaVersionTables. func (mr *MockAdminDBMockRecorder) CreateSchemaVersionTables() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSchemaVersionTables", reflect.TypeOf((*MockAdminDB)(nil).CreateSchemaVersionTables)) } // DropAllTables mocks base method. func (m *MockAdminDB) DropAllTables(database string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DropAllTables", database) ret0, _ := ret[0].(error) return ret0 } // DropAllTables indicates an expected call of DropAllTables. func (mr *MockAdminDBMockRecorder) DropAllTables(database any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropAllTables", reflect.TypeOf((*MockAdminDB)(nil).DropAllTables), database) } // DropDatabase mocks base method. func (m *MockAdminDB) DropDatabase(database string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DropDatabase", database) ret0, _ := ret[0].(error) return ret0 } // DropDatabase indicates an expected call of DropDatabase. func (mr *MockAdminDBMockRecorder) DropDatabase(database any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropDatabase", reflect.TypeOf((*MockAdminDB)(nil).DropDatabase), database) } // DropTable mocks base method. func (m *MockAdminDB) DropTable(table string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DropTable", table) ret0, _ := ret[0].(error) return ret0 } // DropTable indicates an expected call of DropTable. func (mr *MockAdminDBMockRecorder) DropTable(table any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropTable", reflect.TypeOf((*MockAdminDB)(nil).DropTable), table) } // ExecSchemaOperationQuery mocks base method. func (m *MockAdminDB) ExecSchemaOperationQuery(ctx context.Context, stmt string, args ...any) error { m.ctrl.T.Helper() varargs := []any{ctx, stmt} for _, a := range args { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ExecSchemaOperationQuery", varargs...) ret0, _ := ret[0].(error) return ret0 } // ExecSchemaOperationQuery indicates an expected call of ExecSchemaOperationQuery. func (mr *MockAdminDBMockRecorder) ExecSchemaOperationQuery(ctx, stmt any, args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, stmt}, args...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecSchemaOperationQuery", reflect.TypeOf((*MockAdminDB)(nil).ExecSchemaOperationQuery), varargs...) } // ListTables mocks base method. func (m *MockAdminDB) ListTables(database string) ([]string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTables", database) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTables indicates an expected call of ListTables. func (mr *MockAdminDBMockRecorder) ListTables(database any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTables", reflect.TypeOf((*MockAdminDB)(nil).ListTables), database) } // PluginName mocks base method. func (m *MockAdminDB) PluginName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PluginName") ret0, _ := ret[0].(string) return ret0 } // PluginName indicates an expected call of PluginName. func (mr *MockAdminDBMockRecorder) PluginName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginName", reflect.TypeOf((*MockAdminDB)(nil).PluginName)) } // ReadSchemaVersion mocks base method. func (m *MockAdminDB) ReadSchemaVersion(database string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadSchemaVersion", database) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadSchemaVersion indicates an expected call of ReadSchemaVersion. func (mr *MockAdminDBMockRecorder) ReadSchemaVersion(database any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadSchemaVersion", reflect.TypeOf((*MockAdminDB)(nil).ReadSchemaVersion), database) } // UpdateSchemaVersion mocks base method. func (m *MockAdminDB) UpdateSchemaVersion(database, newVersion, minCompatibleVersion string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateSchemaVersion", database, newVersion, minCompatibleVersion) ret0, _ := ret[0].(error) return ret0 } // UpdateSchemaVersion indicates an expected call of UpdateSchemaVersion. func (mr *MockAdminDBMockRecorder) UpdateSchemaVersion(database, newVersion, minCompatibleVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSchemaVersion", reflect.TypeOf((*MockAdminDB)(nil).UpdateSchemaVersion), database, newVersion, minCompatibleVersion) } // WriteSchemaUpdateLog mocks base method. func (m *MockAdminDB) WriteSchemaUpdateLog(oldVersion, newVersion, manifestMD5, desc string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteSchemaUpdateLog", oldVersion, newVersion, manifestMD5, desc) ret0, _ := ret[0].(error) return ret0 } // WriteSchemaUpdateLog indicates an expected call of WriteSchemaUpdateLog. func (mr *MockAdminDBMockRecorder) WriteSchemaUpdateLog(oldVersion, newVersion, manifestMD5, desc any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteSchemaUpdateLog", reflect.TypeOf((*MockAdminDB)(nil).WriteSchemaUpdateLog), oldVersion, newVersion, manifestMD5, desc) } // MockErrorChecker is a mock of ErrorChecker interface. type MockErrorChecker struct { ctrl *gomock.Controller recorder *MockErrorCheckerMockRecorder isgomock struct{} } // MockErrorCheckerMockRecorder is the mock recorder for MockErrorChecker. type MockErrorCheckerMockRecorder struct { mock *MockErrorChecker } // NewMockErrorChecker creates a new mock instance. func NewMockErrorChecker(ctrl *gomock.Controller) *MockErrorChecker { mock := &MockErrorChecker{ctrl: ctrl} mock.recorder = &MockErrorCheckerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockErrorChecker) EXPECT() *MockErrorCheckerMockRecorder { return m.recorder } // IsDupEntryError mocks base method. func (m *MockErrorChecker) IsDupEntryError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsDupEntryError", err) ret0, _ := ret[0].(bool) return ret0 } // IsDupEntryError indicates an expected call of IsDupEntryError. func (mr *MockErrorCheckerMockRecorder) IsDupEntryError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDupEntryError", reflect.TypeOf((*MockErrorChecker)(nil).IsDupEntryError), err) } // IsNotFoundError mocks base method. func (m *MockErrorChecker) IsNotFoundError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsNotFoundError", err) ret0, _ := ret[0].(bool) return ret0 } // IsNotFoundError indicates an expected call of IsNotFoundError. func (mr *MockErrorCheckerMockRecorder) IsNotFoundError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNotFoundError", reflect.TypeOf((*MockErrorChecker)(nil).IsNotFoundError), err) } // IsThrottlingError mocks base method. func (m *MockErrorChecker) IsThrottlingError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsThrottlingError", err) ret0, _ := ret[0].(bool) return ret0 } // IsThrottlingError indicates an expected call of IsThrottlingError. func (mr *MockErrorCheckerMockRecorder) IsThrottlingError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsThrottlingError", reflect.TypeOf((*MockErrorChecker)(nil).IsThrottlingError), err) } // IsTimeoutError mocks base method. func (m *MockErrorChecker) IsTimeoutError(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsTimeoutError", err) ret0, _ := ret[0].(bool) return ret0 } // IsTimeoutError indicates an expected call of IsTimeoutError. func (mr *MockErrorCheckerMockRecorder) IsTimeoutError(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsTimeoutError", reflect.TypeOf((*MockErrorChecker)(nil).IsTimeoutError), err) } ================================================ FILE: common/persistence/sql/sqlplugin/interfaces.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/common/persistence/sql/sqlplugin package sqlplugin import ( "context" "database/sql" "errors" "time" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" ) var ( // ErrTTLNotSupported indicates the sql plugin does not support ttl ErrTTLNotSupported = errors.New("plugin implementation does not support ttl") ) type ( // Plugin defines the interface for any SQL database that needs to implement Plugin interface { CreateDB(cfg *config.SQL) (DB, error) CreateAdminDB(cfg *config.SQL) (AdminDB, error) } // DomainRow represents a row in domain table DomainRow struct { ID serialization.UUID Name string Data []byte DataEncoding string IsGlobal bool } // DomainFilter contains the column names within domain table that // can be used to filter results through a WHERE clause. When ID is not // nil, it will be used for WHERE condition. If ID is nil and Name is non-nil, // Name will be used for WHERE condition. When both ID and Name are nil, // no WHERE clause will be used DomainFilter struct { ID *serialization.UUID Name *string GreaterThanID *serialization.UUID PageSize *int } // DomainMetadataRow represents a row in domain_metadata table DomainMetadataRow struct { NotificationVersion int64 } // ShardsRow represents a row in shards table ShardsRow struct { ShardID int64 RangeID int64 Data []byte DataEncoding string } // ShardsFilter contains the column names within shards table that // can be used to filter results through a WHERE clause ShardsFilter struct { ShardID int64 } // TransferTasksRow represents a row in transfer_tasks table TransferTasksRow struct { ShardID int TaskID int64 Data []byte DataEncoding string } // CrossClusterTasksRow represents a row in cross_cluster_tasks table CrossClusterTasksRow struct { TargetCluster string ShardID int TaskID int64 Data []byte DataEncoding string } // TransferTasksFilter contains the column names within transfer_tasks table that // can be used to filter results through a WHERE clause TransferTasksFilter struct { ShardID int TaskID int64 InclusiveMinTaskID int64 ExclusiveMaxTaskID int64 PageSize int } // CrossClusterTasksFilter contains the column names within cross_cluster_tasks table that // can be used to filter results through a WHERE clause CrossClusterTasksFilter struct { TargetCluster string ShardID int TaskID int64 MinTaskID int64 MaxTaskID int64 PageSize int } // ExecutionsRow represents a row in executions table ExecutionsRow struct { ShardID int DomainID serialization.UUID WorkflowID string RunID serialization.UUID NextEventID int64 LastWriteVersion int64 Data []byte DataEncoding string VersionHistories []byte VersionHistoriesEncoding string } // ExecutionsFilter contains the column names within executions table that // can be used to filter results through a WHERE clause // To get single row, it requires ShardID, DomainID, WorkflowID, RunID // To get a list of rows, it requires ShardID, Size. // The WorkflowID and RunID are optional for listing rows. They work as the start boundary for pagination. ExecutionsFilter struct { ShardID int DomainID serialization.UUID WorkflowID string RunID serialization.UUID Size int } // CurrentExecutionsRow represents a row in current_executions table CurrentExecutionsRow struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID CreateRequestID string State int CloseStatus int LastWriteVersion int64 StartVersion int64 } // CurrentExecutionsFilter contains the column names within current_executions table that // can be used to filter results through a WHERE clause CurrentExecutionsFilter struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID } // BufferedEventsRow represents a row in buffered_events table BufferedEventsRow struct { ShardID int DomainID serialization.UUID WorkflowID string RunID serialization.UUID Data []byte DataEncoding string } // BufferedEventsFilter contains the column names within buffered_events table that // can be used to filter results through a WHERE clause BufferedEventsFilter struct { ShardID int DomainID serialization.UUID WorkflowID string RunID serialization.UUID } // TasksRow represents a row in tasks table TasksRow struct { ShardID int // this is DBShardID, not historyShardID (TODO: maybe rename it for clarification) DomainID serialization.UUID TaskType int64 TaskID int64 TaskListName string Data []byte DataEncoding string } // TaskKeyRow represents a result row giving task keys TaskKeyRow struct { DomainID serialization.UUID TaskListName string TaskType int64 TaskID int64 } // TasksRowWithTTL represents a row in tasks table with a ttl TasksRowWithTTL struct { TasksRow TasksRow // TTL is optional because InsertIntoTasksWithTTL operates over a slice of TasksRowWithTTL. // Some items in the slice may have a TTL while others do not. It is the responsibility // of the plugin implementation to handle items with TTL set and items with TTL not set. TTL *time.Duration } // TasksFilter contains the column names within tasks table that // can be used to filter results through a WHERE clause TasksFilter struct { ShardID int // this is DBShardID, not historyShardID (TODO: maybe rename it for clarification) DomainID serialization.UUID TaskListName string TaskType int64 TaskID *int64 MinTaskID *int64 MaxTaskID *int64 TaskIDLessThanEquals *int64 Limit *int PageSize *int } // OrphanTasksFilter contains the parameters controlling orphan deletion OrphanTasksFilter struct { Limit *int } // TaskListsRow represents a row in task_lists table TaskListsRow struct { ShardID int // this is DBShardID, not historyShardID (TODO: maybe rename it for clarification) DomainID serialization.UUID Name string TaskType int64 RangeID int64 Data []byte DataEncoding string } // TaskListsRowWithTTL represents a row in task_lists table with a ttl TaskListsRowWithTTL struct { TaskListsRow TaskListsRow TTL time.Duration } // TaskListsFilter contains the column names within task_lists table that // can be used to filter results through a WHERE clause TaskListsFilter struct { ShardID int // this is DBShardID, not historyShardID (TODO: maybe rename it for clarification) DomainID *serialization.UUID Name *string TaskType *int64 DomainIDGreaterThan *serialization.UUID NameGreaterThan *string TaskTypeGreaterThan *int64 RangeID *int64 PageSize *int } // ReplicationTasksRow represents a row in replication_tasks table ReplicationTasksRow struct { ShardID int TaskID int64 Data []byte DataEncoding string } // ReplicationTaskDLQRow represents a row in replication_tasks_dlq table ReplicationTaskDLQRow struct { SourceClusterName string ShardID int TaskID int64 Data []byte DataEncoding string } // ReplicationTasksFilter contains the column names within replication_tasks table that // can be used to filter results through a WHERE clause ReplicationTasksFilter struct { ShardID int TaskID int64 InclusiveMinTaskID int64 ExclusiveMaxTaskID int64 PageSize int } // ReplicationTasksDLQFilter contains the column names within replication_tasks_dlq table that // can be used to filter results through a WHERE clause ReplicationTasksDLQFilter struct { ReplicationTasksFilter SourceClusterName string } // ReplicationTaskDLQFilter contains the column names within replication_tasks_dlq table that // can be used to filter results through a WHERE clause ReplicationTaskDLQFilter struct { SourceClusterName string ShardID int } // TimerTasksRow represents a row in timer_tasks table TimerTasksRow struct { ShardID int VisibilityTimestamp time.Time TaskID int64 Data []byte DataEncoding string } // TimerTasksFilter contains the column names within timer_tasks table that // can be used to filter results through a WHERE clause TimerTasksFilter struct { ShardID int TaskID int64 VisibilityTimestamp time.Time MinVisibilityTimestamp time.Time MaxVisibilityTimestamp time.Time PageSize int } // EventsRow represents a row in events table EventsRow struct { DomainID serialization.UUID WorkflowID string RunID serialization.UUID FirstEventID int64 BatchVersion int64 RangeID int64 TxID int64 Data []byte DataEncoding string } // EventsFilter contains the column names within events table that // can be used to filter results through a WHERE clause EventsFilter struct { DomainID serialization.UUID WorkflowID string RunID serialization.UUID FirstEventID *int64 NextEventID *int64 PageSize *int } // HistoryNodeRow represents a row in history_node table HistoryNodeRow struct { ShardID int TreeID serialization.UUID BranchID serialization.UUID NodeID int64 // use pointer so that it's easier to multiple by -1 TxnID *int64 Data []byte DataEncoding string } // HistoryNodeFilter contains the column names within history_node table that // can be used to filter results through a WHERE clause HistoryNodeFilter struct { ShardID int TreeID serialization.UUID BranchID serialization.UUID // Inclusive MinNodeID *int64 // Exclusive MaxNodeID *int64 PageSize int } // HistoryTreeRow represents a row in history_tree table HistoryTreeRow struct { ShardID int TreeID serialization.UUID BranchID serialization.UUID Data []byte DataEncoding string } // HistoryTreeFilter contains the column names within history_tree table that // can be used to filter results through a WHERE clause HistoryTreeFilter struct { ShardID int TreeID serialization.UUID BranchID *serialization.UUID PageSize *int } // ActivityInfoMapsRow represents a row in activity_info_maps table ActivityInfoMapsRow struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID ScheduleID int64 Data []byte DataEncoding string LastHeartbeatDetails []byte LastHeartbeatUpdatedTime time.Time } // ActivityInfoMapsFilter contains the column names within activity_info_maps table that // can be used to filter results through a WHERE clause ActivityInfoMapsFilter struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID ScheduleIDs []int64 } // TimerInfoMapsRow represents a row in timer_info_maps table TimerInfoMapsRow struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID TimerID string Data []byte DataEncoding string } // TimerInfoMapsFilter contains the column names within timer_info_maps table that // can be used to filter results through a WHERE clause TimerInfoMapsFilter struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID TimerIDs []string } // ChildExecutionInfoMapsRow represents a row in child_execution_info_maps table ChildExecutionInfoMapsRow struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID InitiatedID int64 Data []byte DataEncoding string } // ChildExecutionInfoMapsFilter contains the column names within child_execution_info_maps table that // can be used to filter results through a WHERE clause ChildExecutionInfoMapsFilter struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID InitiatedIDs []int64 } // RequestCancelInfoMapsRow represents a row in request_cancel_info_maps table RequestCancelInfoMapsRow struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID InitiatedID int64 Data []byte DataEncoding string } // RequestCancelInfoMapsFilter contains the column names within request_cancel_info_maps table that // can be used to filter results through a WHERE clause RequestCancelInfoMapsFilter struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID InitiatedIDs []int64 } // SignalInfoMapsRow represents a row in signal_info_maps table SignalInfoMapsRow struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID InitiatedID int64 Data []byte DataEncoding string } // SignalInfoMapsFilter contains the column names within signal_info_maps table that // can be used to filter results through a WHERE clause SignalInfoMapsFilter struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID InitiatedIDs []int64 } // SignalsRequestedSetsRow represents a row in signals_requested_sets table SignalsRequestedSetsRow struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID SignalID string } // SignalsRequestedSetsFilter contains the column names within signals_requested_sets table that // can be used to filter results through a WHERE clause SignalsRequestedSetsFilter struct { ShardID int64 DomainID serialization.UUID WorkflowID string RunID serialization.UUID SignalIDs []string } // VisibilityRow represents a row in executions_visibility table VisibilityRow struct { DomainID string RunID string WorkflowTypeName string WorkflowID string StartTime time.Time ExecutionTime time.Time CloseStatus *int32 CloseTime *time.Time HistoryLength *int64 Memo []byte Encoding string IsCron bool CronSchedule string NumClusters int16 UpdateTime time.Time ShardID int16 ExecutionStatus int32 ScheduledExecutionTime time.Time } // VisibilityFilter contains the column names within executions_visibility table that // can be used to filter results through a WHERE clause VisibilityFilter struct { DomainID string Closed bool RunID *string WorkflowID *string WorkflowTypeName *string CloseStatus *int32 MinStartTime *time.Time MaxStartTime *time.Time PageSize *int } // QueueRow represents a row in queue table QueueRow struct { QueueType persistence.QueueType MessageID int64 MessagePayload []byte } // QueueMetadataRow represents a row in queue_metadata table QueueMetadataRow struct { QueueType persistence.QueueType Data []byte } // ClusterConfigRow represents a row in cluster_config table ClusterConfigRow struct { RowType int Version int64 Timestamp time.Time Data []byte DataEncoding string } // DomainAuditLogRow represents a row in domain_audit_log table DomainAuditLogRow struct { DomainID string EventID string StateBefore []byte StateBeforeEncoding constants.EncodingType StateAfter []byte StateAfterEncoding constants.EncodingType OperationType persistence.DomainAuditOperationType CreatedTime time.Time LastUpdatedTime time.Time Identity string IdentityType string Comment string } // DomainAuditLogFilter contains the filter criteria for querying domain audit logs DomainAuditLogFilter struct { DomainID string OperationType persistence.DomainAuditOperationType MinCreatedTime *time.Time PageSize int // PageMaxCreatedTime and PageMinEventID are used to paginate Select queries PageMaxCreatedTime *time.Time PageMinEventID *string } // tableCRUD defines the API for interacting with the database tables tableCRUD interface { InsertIntoDomain(ctx context.Context, rows *DomainRow) (sql.Result, error) UpdateDomain(ctx context.Context, row *DomainRow) (sql.Result, error) // SelectFromDomain returns domains that match filter criteria. Either ID or // Name can be specified to filter results. If both are not specified, all rows // will be returned SelectFromDomain(ctx context.Context, filter *DomainFilter) ([]DomainRow, error) // DeleteDomain deletes a single row. One of ID or Name MUST be specified DeleteFromDomain(ctx context.Context, filter *DomainFilter) (sql.Result, error) LockDomainMetadata(ctx context.Context) error UpdateDomainMetadata(ctx context.Context, row *DomainMetadataRow) (sql.Result, error) SelectFromDomainMetadata(ctx context.Context) (*DomainMetadataRow, error) InsertIntoShards(ctx context.Context, rows *ShardsRow) (sql.Result, error) UpdateShards(ctx context.Context, row *ShardsRow) (sql.Result, error) SelectFromShards(ctx context.Context, filter *ShardsFilter) (*ShardsRow, error) ReadLockShards(ctx context.Context, filter *ShardsFilter) (int, error) WriteLockShards(ctx context.Context, filter *ShardsFilter) (int, error) InsertIntoTasks(ctx context.Context, rows []TasksRow) (sql.Result, error) InsertIntoTasksWithTTL(ctx context.Context, rows []TasksRowWithTTL) (sql.Result, error) // SelectFromTasks retrieves one or more rows from the tasks table // Required filter params - {domainID, tasklistName, taskType, minTaskID, maxTaskID, pageSize} SelectFromTasks(ctx context.Context, filter *TasksFilter) ([]TasksRow, error) // DeleteFromTasks deletes a row from tasks table // Required filter params: // to delete single row // - {domainID, tasklistName, taskType, taskID} // to delete multiple rows // - {domainID, tasklistName, taskType, taskIDLessThanEquals, limit } // - this will delete up to limit number of tasks less than or equal to the given task id DeleteFromTasks(ctx context.Context, filter *TasksFilter) (sql.Result, error) GetTasksCount(ctx context.Context, filter *TasksFilter) (int64, error) GetOrphanTasks(ctx context.Context, filter *OrphanTasksFilter) ([]TaskKeyRow, error) InsertIntoTaskLists(ctx context.Context, row *TaskListsRow) (sql.Result, error) InsertIntoTaskListsWithTTL(ctx context.Context, row *TaskListsRowWithTTL) (sql.Result, error) UpdateTaskLists(ctx context.Context, row *TaskListsRow) (sql.Result, error) UpdateTaskListsWithTTL(ctx context.Context, row *TaskListsRowWithTTL) (sql.Result, error) // SelectFromTaskLists returns one or more rows from task_lists table // Required Filter params: // to read a single row: {shardID, domainID, name, taskType} // to range read multiple rows: {shardID, domainIDGreaterThan, nameGreaterThan, taskTypeGreaterThan, pageSize} SelectFromTaskLists(ctx context.Context, filter *TaskListsFilter) ([]TaskListsRow, error) DeleteFromTaskLists(ctx context.Context, filter *TaskListsFilter) (sql.Result, error) LockTaskLists(ctx context.Context, filter *TaskListsFilter) (int64, error) // eventsV2 InsertIntoHistoryNode(ctx context.Context, row *HistoryNodeRow) (sql.Result, error) SelectFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) ([]HistoryNodeRow, error) DeleteFromHistoryNode(ctx context.Context, filter *HistoryNodeFilter) (sql.Result, error) InsertIntoHistoryTree(ctx context.Context, row *HistoryTreeRow) (sql.Result, error) SelectFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) ([]HistoryTreeRow, error) DeleteFromHistoryTree(ctx context.Context, filter *HistoryTreeFilter) (sql.Result, error) GetAllHistoryTreeBranches(ctx context.Context, filter *HistoryTreeFilter) ([]HistoryTreeRow, error) InsertIntoExecutions(ctx context.Context, row *ExecutionsRow) (sql.Result, error) UpdateExecutions(ctx context.Context, row *ExecutionsRow) (sql.Result, error) SelectFromExecutions(ctx context.Context, filter *ExecutionsFilter) ([]ExecutionsRow, error) DeleteFromExecutions(ctx context.Context, filter *ExecutionsFilter) (sql.Result, error) ReadLockExecutions(ctx context.Context, filter *ExecutionsFilter) (int, error) WriteLockExecutions(ctx context.Context, filter *ExecutionsFilter) (int, error) LockCurrentExecutionsJoinExecutions(ctx context.Context, filter *CurrentExecutionsFilter) ([]CurrentExecutionsRow, error) InsertIntoCurrentExecutions(ctx context.Context, row *CurrentExecutionsRow) (sql.Result, error) UpdateCurrentExecutions(ctx context.Context, row *CurrentExecutionsRow) (sql.Result, error) // SelectFromCurrentExecutions returns one or more rows from current_executions table // Required params - {shardID, domainID, workflowID} SelectFromCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (*CurrentExecutionsRow, error) // DeleteFromCurrentExecutions deletes a single row that matches the filter criteria // If a row exist, that row will be deleted and this method will return success // If there is no row matching the filter criteria, this method will still return success // Callers can check the output of Result.RowsAffected() to see if a row was deleted or not // Required params - {shardID, domainID, workflowID, runID} DeleteFromCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (sql.Result, error) LockCurrentExecutions(ctx context.Context, filter *CurrentExecutionsFilter) (*CurrentExecutionsRow, error) InsertIntoTransferTasks(ctx context.Context, rows []TransferTasksRow) (sql.Result, error) // SelectFromTransferTasks returns rows that match filter criteria from transfer_tasks table. // Required filter params - {shardID, minTaskID, maxTaskID} SelectFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) ([]TransferTasksRow, error) // DeleteFromTransferTasks deletes one or more rows from transfer_tasks table. // Required filter params - {shardID, taskID} DeleteFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) (sql.Result, error) // RangeDeleteFromTransferTasks deletes one or more rows from transfer_tasks table. // Required filter params - {shardID, minTaskID, maxTaskID} RangeDeleteFromTransferTasks(ctx context.Context, filter *TransferTasksFilter) (sql.Result, error) // TODO: add cross-cluster tasks methods // InsertIntoCrossClusterTasks adds a new row to the cross_cluster_tasks table InsertIntoCrossClusterTasks(ctx context.Context, rows []CrossClusterTasksRow) (sql.Result, error) // SelectFromCrossClusterTasks returns rows that match filter criteria from cross_cluster_tasks table. // Required filter params - {shardID, minTaskID, maxTaskID} SelectFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) ([]CrossClusterTasksRow, error) // DeleteFromCrossClusterTasks deletes one or more rows from cross_cluster_tasks table. // Required filter params - {shardID, taskID} DeleteFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) (sql.Result, error) // RangeDeleteFromCrossClusterTasks deletes one or more rows from cross_cluster_tasks table. // Required filter params - {shardID, minTaskID, maxTaskID} RangeDeleteFromCrossClusterTasks(ctx context.Context, filter *CrossClusterTasksFilter) (sql.Result, error) InsertIntoTimerTasks(ctx context.Context, rows []TimerTasksRow) (sql.Result, error) // SelectFromTimerTasks returns one or more rows from timer_tasks table // Required filter Params - {shardID, taskID, minVisibilityTimestamp, maxVisibilityTimestamp, pageSize} SelectFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) ([]TimerTasksRow, error) // DeleteFromTimerTasks deletes one or more rows from timer_tasks table // Required filter Params: {shardID, visibilityTimestamp, taskID} DeleteFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) (sql.Result, error) // RangeDeleteFromTimerTasks deletes one or more rows from timer_tasks table // Required filter Params: {shardID, minVisibilityTimestamp, maxVisibilityTimestamp} RangeDeleteFromTimerTasks(ctx context.Context, filter *TimerTasksFilter) (sql.Result, error) InsertIntoBufferedEvents(ctx context.Context, rows []BufferedEventsRow) (sql.Result, error) SelectFromBufferedEvents(ctx context.Context, filter *BufferedEventsFilter) ([]BufferedEventsRow, error) DeleteFromBufferedEvents(ctx context.Context, filter *BufferedEventsFilter) (sql.Result, error) InsertIntoReplicationTasks(ctx context.Context, rows []ReplicationTasksRow) (sql.Result, error) // SelectFromReplicationTasks returns one or more rows from replication_tasks table // Required filter params - {shardID, minTaskID, maxTaskID, pageSize} SelectFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) ([]ReplicationTasksRow, error) // DeleteFromReplicationTasks deletes a row from replication_tasks table // Required filter params - {shardID, taskID} DeleteFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) (sql.Result, error) // DeleteFromReplicationTasks deletes multi rows from replication_tasks table // Required filter params - {shardID, inclusiveEndTaskID} RangeDeleteFromReplicationTasks(ctx context.Context, filter *ReplicationTasksFilter) (sql.Result, error) // InsertIntoReplicationTasksDLQ puts the replication task into DLQ InsertIntoReplicationTasksDLQ(ctx context.Context, row *ReplicationTaskDLQRow) (sql.Result, error) // SelectFromReplicationTasksDLQ returns one or more rows from replication_tasks_dlq table // Required filter params - {sourceClusterName, shardID, minTaskID, pageSize} SelectFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) ([]ReplicationTasksRow, error) // SelectFromReplicationDLQ returns one row from replication_tasks_dlq table // Required filter params - {sourceClusterName} SelectFromReplicationDLQ(ctx context.Context, filter *ReplicationTaskDLQFilter) (int64, error) // DeleteMessageFromReplicationTasksDLQ deletes one row from replication_tasks_dlq table // Required filter params - {sourceClusterName, shardID, taskID} DeleteMessageFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) (sql.Result, error) // RangeDeleteMessageFromReplicationTasksDLQ deletes one or more rows from replication_tasks_dlq table // Required filter params - {sourceClusterName, shardID, taskID, inclusiveTaskID} RangeDeleteMessageFromReplicationTasksDLQ(ctx context.Context, filter *ReplicationTasksDLQFilter) (sql.Result, error) ReplaceIntoActivityInfoMaps(ctx context.Context, rows []ActivityInfoMapsRow) (sql.Result, error) // SelectFromActivityInfoMaps returns one or more rows from activity_info_maps // Required filter params - {shardID, domainID, workflowID, runID} SelectFromActivityInfoMaps(ctx context.Context, filter *ActivityInfoMapsFilter) ([]ActivityInfoMapsRow, error) // DeleteFromActivityInfoMaps deletes a row from activity_info_maps table // Required filter params // - one or multiple rows delete - {shardID, domainID, workflowID, runID, scheduleIDs} // - range delete - {shardID, domainID, workflowID, runID} DeleteFromActivityInfoMaps(ctx context.Context, filter *ActivityInfoMapsFilter) (sql.Result, error) ReplaceIntoTimerInfoMaps(ctx context.Context, rows []TimerInfoMapsRow) (sql.Result, error) // SelectFromTimerInfoMaps returns one or more rows form timer_info_maps table // Required filter params - {shardID, domainID, workflowID, runID} SelectFromTimerInfoMaps(ctx context.Context, filter *TimerInfoMapsFilter) ([]TimerInfoMapsRow, error) // DeleteFromTimerInfoMaps deletes one or more rows from timer_info_maps // Required filter params // - one or multiple rows delete- {shardID, domainID, workflowID, runID, timerIDs} // - range delete - {shardID, domainID, workflowID, runID} DeleteFromTimerInfoMaps(ctx context.Context, filter *TimerInfoMapsFilter) (sql.Result, error) ReplaceIntoChildExecutionInfoMaps(ctx context.Context, rows []ChildExecutionInfoMapsRow) (sql.Result, error) // SelectFromChildExecutionInfoMaps returns one or more rows form child_execution_info_maps table // Required filter params - {shardID, domainID, workflowID, runID} SelectFromChildExecutionInfoMaps(ctx context.Context, filter *ChildExecutionInfoMapsFilter) ([]ChildExecutionInfoMapsRow, error) // DeleteFromChildExecutionInfoMaps deletes one or more rows from child_execution_info_maps // Required filter params // - onne or multiple rows delete - {shardID, domainID, workflowID, runID, initiatedIDs} // - range delete - {shardID, domainID, workflowID, runID} DeleteFromChildExecutionInfoMaps(ctx context.Context, filter *ChildExecutionInfoMapsFilter) (sql.Result, error) ReplaceIntoRequestCancelInfoMaps(ctx context.Context, rows []RequestCancelInfoMapsRow) (sql.Result, error) // SelectFromRequestCancelInfoMaps returns one or more rows form request_cancel_info_maps table // Required filter params - {shardID, domainID, workflowID, runID} SelectFromRequestCancelInfoMaps(ctx context.Context, filter *RequestCancelInfoMapsFilter) ([]RequestCancelInfoMapsRow, error) // DeleteFromRequestCancelInfoMaps deletes one or more rows from request_cancel_info_maps // Required filter params // - one or multiple rows delete - {shardID, domainID, workflowID, runID, initiatedIDs} // - range delete - {shardID, domainID, workflowID, runID} DeleteFromRequestCancelInfoMaps(ctx context.Context, filter *RequestCancelInfoMapsFilter) (sql.Result, error) ReplaceIntoSignalInfoMaps(ctx context.Context, rows []SignalInfoMapsRow) (sql.Result, error) // SelectFromSignalInfoMaps returns one or more rows form signal_info_maps table // Required filter params - {shardID, domainID, workflowID, runID} SelectFromSignalInfoMaps(ctx context.Context, filter *SignalInfoMapsFilter) ([]SignalInfoMapsRow, error) // DeleteFromSignalInfoMaps deletes one or more rows from signal_info_maps table // Required filter params // - one or multiple rows delete - {shardID, domainID, workflowID, runID, initiatedIDs} // - range delete - {shardID, domainID, workflowID, runID} DeleteFromSignalInfoMaps(ctx context.Context, filter *SignalInfoMapsFilter) (sql.Result, error) InsertIntoSignalsRequestedSets(ctx context.Context, rows []SignalsRequestedSetsRow) (sql.Result, error) // SelectFromSignalInfoMaps returns one or more rows form singals_requested_sets table // Required filter params - {shardID, domainID, workflowID, runID} SelectFromSignalsRequestedSets(ctx context.Context, filter *SignalsRequestedSetsFilter) ([]SignalsRequestedSetsRow, error) // DeleteFromSignalsRequestedSets deletes one or more rows from signals_requested_sets // Required filter params // - one or multiple rows delete - {shardID, domainID, workflowID, runID, signalIDs} // - range delete - {shardID, domainID, workflowID, runID} DeleteFromSignalsRequestedSets(ctx context.Context, filter *SignalsRequestedSetsFilter) (sql.Result, error) // InsertIntoVisibility inserts a row into visibility table. If a row already exist, // no changes will be made by this API InsertIntoVisibility(ctx context.Context, row *VisibilityRow) (sql.Result, error) // ReplaceIntoVisibility deletes old row (if it exist) and inserts new row into visibility table ReplaceIntoVisibility(ctx context.Context, row *VisibilityRow) (sql.Result, error) // SelectFromVisibility returns one or more rows from visibility table // Required filter params: // - getClosedWorkflowExecution - retrieves single row - {domainID, runID, closed=true} // - All other queries retrieve multiple rows (range): // - MUST specify following required params: // - domainID, minStartTime, maxStartTime, runID and pageSize where some or all of these may come from previous page token // - OPTIONALLY specify one of following params // - workflowID, workflowTypeName, closeStatus (along with closed=true) SelectFromVisibility(ctx context.Context, filter *VisibilityFilter) ([]VisibilityRow, error) DeleteFromVisibility(ctx context.Context, filter *VisibilityFilter) (sql.Result, error) InsertIntoQueue(ctx context.Context, row *QueueRow) (sql.Result, error) GetLastEnqueuedMessageIDForUpdate(ctx context.Context, queueType persistence.QueueType) (int64, error) GetMessagesFromQueue(ctx context.Context, queueType persistence.QueueType, lastMessageID int64, maxRows int) ([]QueueRow, error) GetMessagesBetween(ctx context.Context, queueType persistence.QueueType, firstMessageID int64, lastMessageID int64, maxRows int) ([]QueueRow, error) DeleteMessagesBefore(ctx context.Context, queueType persistence.QueueType, messageID int64) (sql.Result, error) RangeDeleteMessages(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, inclusiveEndMessageID int64) (sql.Result, error) DeleteMessage(ctx context.Context, queueType persistence.QueueType, messageID int64) (sql.Result, error) InsertAckLevel(ctx context.Context, queueType persistence.QueueType, messageID int64, clusterName string) error UpdateAckLevels(ctx context.Context, queueType persistence.QueueType, clusterAckLevels map[string]int64) error GetAckLevels(ctx context.Context, queueType persistence.QueueType, forUpdate bool) (map[string]int64, error) GetQueueSize(ctx context.Context, queueType persistence.QueueType) (int64, error) // InsertConfig insert a config entry with version. Return nosqlplugin.NewConditionFailure if the same version of the row_type is existing InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error // SelectLatestConfig returns the config entry of the row_type with the largest(latest) version value SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) // InsertDomainAuditLog inserts a new audit log entry for a domain operation. Returns error if there is any failure InsertIntoDomainAuditLog(ctx context.Context, row *DomainAuditLogRow) (sql.Result, error) // SelectFromDomainAuditLogs returns audit log entries for a domain. Returns paginated results ordered by created_time DESC, event_id ASC SelectFromDomainAuditLogs(ctx context.Context, filter *DomainAuditLogFilter) ([]*DomainAuditLogRow, error) // The follow provide information about the underlying sql crud implementation SupportsTTL() bool MaxAllowedTTL() (*time.Duration, error) SupportsAsyncTransaction() bool } // adminCRUD defines admin operations for CLI and test suites adminCRUD interface { CreateSchemaVersionTables() error ReadSchemaVersion(database string) (string, error) UpdateSchemaVersion(database string, newVersion string, minCompatibleVersion string) error WriteSchemaUpdateLog(oldVersion string, newVersion string, manifestMD5 string, desc string) error ListTables(database string) ([]string, error) DropTable(table string) error DropAllTables(database string) error CreateDatabase(database string) error DropDatabase(database string) error // ExecSchemaOperationQuery allows passing in any query, but it must be schema operation (DDL) ExecSchemaOperationQuery(ctx context.Context, stmt string, args ...interface{}) error } // Tx defines the API for a SQL transaction Tx interface { tableCRUD ErrorChecker Commit() error Rollback() error } // DB defines the API for regular SQL operations of a Cadence server DB interface { tableCRUD ErrorChecker GetTotalNumDBShards() int BeginTx(ctx context.Context, dbShardID int) (Tx, error) PluginName() string Close() error } // AdminDB defines the API for admin SQL operations for CLI and testing suites AdminDB interface { adminCRUD PluginName() string Close() error } ErrorChecker interface { IsDupEntryError(err error) bool IsNotFoundError(err error) bool IsTimeoutError(err error) bool IsThrottlingError(err error) bool } ) ================================================ FILE: common/persistence/sql/sqlplugin/mysql/admin.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "fmt" "time" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( readSchemaVersionQuery = `SELECT curr_version from schema_version where db_name=?` writeSchemaVersionQuery = `REPLACE into schema_version(db_name, creation_time, curr_version, min_compatible_version) VALUES (?,?,?,?)` writeSchemaUpdateHistoryQuery = `INSERT into schema_update_history(year, month, update_time, old_version, new_version, manifest_md5, description) VALUES(?,?,?,?,?,?,?)` createSchemaVersionTableQuery = `CREATE TABLE schema_version(db_name VARCHAR(255) not null PRIMARY KEY, ` + `creation_time DATETIME(6), ` + `curr_version VARCHAR(64), ` + `min_compatible_version VARCHAR(64));` createSchemaUpdateHistoryTableQuery = `CREATE TABLE schema_update_history(` + `year int not null, ` + `month int not null, ` + `update_time DATETIME(6) not null, ` + `description VARCHAR(255), ` + `manifest_md5 VARCHAR(64), ` + `new_version VARCHAR(64), ` + `old_version VARCHAR(64), ` + `PRIMARY KEY (year, month, update_time));` // NOTE we have to use %v because somehow mysql doesn't work with ? here createDatabaseQuery = "CREATE database %v CHARACTER SET UTF8" dropDatabaseQuery = "Drop database %v" listTablesQuery = "SHOW TABLES FROM %v" dropTableQuery = "DROP TABLE %v" ) // CreateSchemaVersionTables sets up the schema version tables func (mdb *DB) CreateSchemaVersionTables() error { if err := mdb.ExecSchemaOperationQuery(context.Background(), createSchemaVersionTableQuery); err != nil { return err } return mdb.ExecSchemaOperationQuery(context.Background(), createSchemaUpdateHistoryTableQuery) } // ReadSchemaVersion returns the current schema version for the keyspace func (mdb *DB) ReadSchemaVersion(database string) (string, error) { var version string err := mdb.driver.GetForSchemaQuery(sqlplugin.DbShardUndefined, &version, readSchemaVersionQuery, database) return version, err } // UpdateSchemaVersion updates the schema version for the keyspace func (mdb *DB) UpdateSchemaVersion(database string, newVersion string, minCompatibleVersion string) error { return mdb.ExecSchemaOperationQuery(context.Background(), writeSchemaVersionQuery, database, time.Now(), newVersion, minCompatibleVersion) } // WriteSchemaUpdateLog adds an entry to the schema update history table func (mdb *DB) WriteSchemaUpdateLog(oldVersion string, newVersion string, manifestMD5 string, desc string) error { now := time.Now().UTC() return mdb.ExecSchemaOperationQuery(context.Background(), writeSchemaUpdateHistoryQuery, now.Year(), int(now.Month()), now, oldVersion, newVersion, manifestMD5, desc) } // ExecSchemaOperationQuery executes a sql statement for schema ONLY. DO NOT use it in other cases, otherwise it will not work for multiple SQL database. // For Sharded SQL, it will execute the statement for all shards func (mdb *DB) ExecSchemaOperationQuery(ctx context.Context, stmt string, args ...interface{}) error { _, err := mdb.driver.ExecDDL(ctx, sqlplugin.DbShardUndefined, stmt, args...) return err } // ListTables returns a list of tables in this database func (mdb *DB) ListTables(database string) ([]string, error) { var tables []string err := mdb.driver.SelectForSchemaQuery(sqlplugin.DbShardUndefined, &tables, fmt.Sprintf(listTablesQuery, database)) return tables, err } // DropTable drops a given table from the database func (mdb *DB) DropTable(name string) error { return mdb.ExecSchemaOperationQuery(context.Background(), fmt.Sprintf(dropTableQuery, name)) } // DropAllTables drops all tables from this database func (mdb *DB) DropAllTables(database string) error { tables, err := mdb.ListTables(database) if err != nil { return err } for _, tab := range tables { if err := mdb.DropTable(tab); err != nil { return err } } return nil } // CreateDatabase creates a database if it doesn't exist func (mdb *DB) CreateDatabase(name string) error { return mdb.ExecSchemaOperationQuery(context.Background(), fmt.Sprintf(createDatabaseQuery, name)) } // DropDatabase drops a database func (mdb *DB) DropDatabase(name string) error { return mdb.ExecSchemaOperationQuery(context.Background(), fmt.Sprintf(dropDatabaseQuery, name)) } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/configstore.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package mysql import ( "context" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) func (mdb *DB) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { _, err := mdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, _insertConfigQuery, row.RowType, -1*row.Version, mdb.converter.ToDateTime(row.Timestamp), row.Values.Data, row.Values.Encoding) return err } func (mdb *DB) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { var row sqlplugin.ClusterConfigRow err := mdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row, _selectLatestConfigQuery, rowType) if err != nil { return nil, err } row.Version *= -1 return &persistence.InternalConfigStoreEntry{ RowType: row.RowType, Version: row.Version, Timestamp: mdb.converter.FromDateTime(row.Timestamp), Values: &persistence.DataBlob{ Data: row.Data, Encoding: constants.EncodingType(row.DataEncoding), }, }, nil } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/configstore_sql.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package mysql const ( _selectLatestConfigQuery = "SELECT row_type, version, timestamp, data, data_encoding FROM cluster_config WHERE row_type = ? ORDER BY version LIMIT 1;" _insertConfigQuery = "INSERT INTO cluster_config (row_type, version, timestamp, data, data_encoding) VALUES(?, ?, ?, ?, ?)" ) ================================================ FILE: common/persistence/sql/sqlplugin/mysql/configstore_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package mysql import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqldriver" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) func TestInsertConfig(t *testing.T) { testCases := []struct { name string row *persistence.InternalConfigStoreEntry mockSetup func(*sqldriver.MockDriver) expectError bool }{ { name: "Success case", row: &persistence.InternalConfigStoreEntry{ Values: &persistence.DataBlob{}, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().ExecContext(gomock.Any(), sqlplugin.DbDefaultShard, _insertConfigQuery, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil) }, expectError: false, }, { name: "Error case", row: &persistence.InternalConfigStoreEntry{ Values: &persistence.DataBlob{}, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().ExecContext(gomock.Any(), sqlplugin.DbDefaultShard, _insertConfigQuery, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("some error")) }, expectError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDriver := sqldriver.NewMockDriver(ctrl) mdb := &DB{driver: mockDriver, converter: &converter{}} // Setup mock expectations tc.mockSetup(mockDriver) err := mdb.InsertConfig(context.Background(), tc.row) if tc.expectError { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestSelectLatestConfig(t *testing.T) { now := time.Now() testCases := []struct { name string rowType int setupMock func(*sqldriver.MockDriver) expectError bool expectedRow *persistence.InternalConfigStoreEntry }{ { name: "Success case", rowType: 1, setupMock: func(md *sqldriver.MockDriver) { row := sqlplugin.ClusterConfigRow{ RowType: 1, Version: -2, Timestamp: now, Data: []byte("test data"), DataEncoding: "json", } md.EXPECT().GetContext(gomock.Any(), sqlplugin.DbDefaultShard, gomock.Any(), _selectLatestConfigQuery, 1).DoAndReturn( func(ctx context.Context, shardID int, r *sqlplugin.ClusterConfigRow, query string, args ...interface{}) error { *r = row return nil }, ) }, expectError: false, expectedRow: &persistence.InternalConfigStoreEntry{ RowType: 1, Version: 2, Timestamp: now, Values: &persistence.DataBlob{ Data: []byte("test data"), Encoding: constants.EncodingType("json"), }, }, }, { name: "Error case", rowType: 2, setupMock: func(md *sqldriver.MockDriver) { md.EXPECT().GetContext(gomock.Any(), sqlplugin.DbDefaultShard, gomock.Any(), _selectLatestConfigQuery, 2).Return(errors.New("some error")) }, expectError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDriver := sqldriver.NewMockDriver(ctrl) mdb := &DB{driver: mockDriver, converter: &converter{}} tc.setupMock(mockDriver) row, err := mdb.SelectLatestConfig(context.Background(), tc.rowType) if tc.expectError { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") assert.Equal(t, tc.expectedRow, row, "Expected result to be the same for test case") } }) } } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/db.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "database/sql" "time" "github.com/VividCortex/mysqlerr" "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" "github.com/uber/cadence/common/persistence/sql/sqldriver" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) type ( DB struct { converter DataConverter driver sqldriver.Driver originalDBs []*sqlx.DB numDBShards int } ) // NewDB returns an instance of DB, which is a logical // connection to the underlying mysql database // dbShardID is needed when tx is not nil func NewDB(xdbs []*sqlx.DB, tx *sqlx.Tx, dbShardID int, numDBShards int, converter DataConverter) (*DB, error) { driver, err := sqldriver.NewDriver(xdbs, tx, dbShardID) if err != nil { return nil, err } db := &DB{ converter: converter, originalDBs: xdbs, // this is kept because NewDB will be called again when starting a transaction driver: driver, numDBShards: numDBShards, } return db, nil } // NewDBWithDriver returns an instance of DB with the given driver func NewDBWithDriver(originalDBs []*sqlx.DB, driver sqldriver.Driver, numDBShards int, converter DataConverter) *DB { return &DB{ converter: converter, originalDBs: originalDBs, driver: driver, numDBShards: numDBShards, } } func (mdb *DB) GetTotalNumDBShards() int { return mdb.numDBShards } var _ sqlplugin.AdminDB = (*DB)(nil) var _ sqlplugin.DB = (*DB)(nil) var _ sqlplugin.Tx = (*DB)(nil) func (mdb *DB) IsDupEntryError(err error) bool { sqlErr, ok := err.(*mysql.MySQLError) // ErrDupEntry MySQL Error 1062 indicates a duplicate primary key i.e. the row already exists, // so we don't do the insert and return a ConditionalUpdate error. return ok && sqlErr.Number == mysqlerr.ER_DUP_ENTRY } func (mdb *DB) IsNotFoundError(err error) bool { return err == sql.ErrNoRows } func (mdb *DB) IsTimeoutError(err error) bool { if err == context.DeadlineExceeded { return true } sqlErr, ok := err.(*mysql.MySQLError) if ok { if sqlErr.Number == mysqlerr.ER_NET_READ_INTERRUPTED || sqlErr.Number == mysqlerr.ER_NET_WRITE_INTERRUPTED || sqlErr.Number == mysqlerr.ER_LOCK_WAIT_TIMEOUT || sqlErr.Number == mysqlerr.ER_XA_RBTIMEOUT || sqlErr.Number == mysqlerr.ER_QUERY_TIMEOUT || sqlErr.Number == mysqlerr.ER_LOCKING_SERVICE_TIMEOUT || sqlErr.Number == mysqlerr.ER_REGEXP_TIME_OUT { return true } } return false } func (mdb *DB) IsThrottlingError(err error) bool { sqlErr, ok := err.(*mysql.MySQLError) if ok { if sqlErr.Number == mysqlerr.ER_CON_COUNT_ERROR || sqlErr.Number == mysqlerr.ER_TOO_MANY_USER_CONNECTIONS || sqlErr.Number == mysqlerr.ER_TOO_MANY_CONCURRENT_TRXS || sqlErr.Number == mysqlerr.ER_CLONE_TOO_MANY_CONCURRENT_CLONES { return true } } return false } // BeginTx starts a new transaction and returns a reference to the Tx object func (mdb *DB) BeginTx(ctx context.Context, dbShardID int) (sqlplugin.Tx, error) { xtx, err := mdb.driver.BeginTxx(ctx, dbShardID, nil) if err != nil { return nil, err } return NewDB(mdb.originalDBs, xtx, dbShardID, mdb.numDBShards, mdb.converter) } // Commit commits a previously started transaction func (mdb *DB) Commit() error { return mdb.driver.Commit() } // Rollback triggers rollback of a previously started transaction func (mdb *DB) Rollback() error { return mdb.driver.Rollback() } // Close closes the connection to the mysql db func (mdb *DB) Close() error { return mdb.driver.Close() } // PluginName returns the name of the mysql plugin func (mdb *DB) PluginName() string { return PluginName } // SupportsTTL returns weather MySQL supports TTL func (mdb *DB) SupportsTTL() bool { return false } // MaxAllowedTTL returns the max allowed ttl MySQL supports func (mdb *DB) MaxAllowedTTL() (*time.Duration, error) { return nil, sqlplugin.ErrTTLNotSupported } // SupportsTTL returns weather MySQL supports Asynchronous transaction func (mdb *DB) SupportsAsyncTransaction() bool { return false } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/domain.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "database/sql" "errors" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( createDomainQuery = `INSERT INTO domains (id, name, is_global, data, data_encoding) VALUES(?, ?, ?, ?, ?)` updateDomainQuery = `UPDATE domains SET name = ?, data = ?, data_encoding = ? WHERE shard_id=54321 AND id = ?` getDomainPart = `SELECT id, name, is_global, data, data_encoding FROM domains` getDomainByIDQuery = getDomainPart + ` WHERE shard_id=? AND id = ?` getDomainByNameQuery = getDomainPart + ` WHERE shard_id=? AND name = ?` listDomainsQuery = getDomainPart + ` WHERE shard_id=? ORDER BY id LIMIT ?` listDomainsRangeQuery = getDomainPart + ` WHERE shard_id=? AND id > ? ORDER BY id LIMIT ?` deleteDomainByIDQuery = `DELETE FROM domains WHERE shard_id=? AND id = ?` deleteDomainByNameQuery = `DELETE FROM domains WHERE shard_id=? AND name = ?` getDomainMetadataQuery = `SELECT notification_version FROM domain_metadata` lockDomainMetadataQuery = `SELECT notification_version FROM domain_metadata FOR UPDATE` updateDomainMetadataQuery = `UPDATE domain_metadata SET notification_version = ? WHERE notification_version = ?` ) const ( shardID = 54321 ) var errMissingArgs = errors.New("missing one or more args for API") // InsertIntoDomain inserts a single row into domains table func (mdb *DB) InsertIntoDomain(ctx context.Context, row *sqlplugin.DomainRow) (sql.Result, error) { return mdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, createDomainQuery, row.ID, row.Name, row.IsGlobal, row.Data, row.DataEncoding) } // UpdateDomain updates a single row in domains table func (mdb *DB) UpdateDomain(ctx context.Context, row *sqlplugin.DomainRow) (sql.Result, error) { return mdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, updateDomainQuery, row.Name, row.Data, row.DataEncoding, row.ID) } // SelectFromDomain reads one or more rows from domains table func (mdb *DB) SelectFromDomain(ctx context.Context, filter *sqlplugin.DomainFilter) ([]sqlplugin.DomainRow, error) { switch { case filter.ID != nil || filter.Name != nil: return mdb.selectFromDomain(ctx, filter) case filter.PageSize != nil && *filter.PageSize > 0: return mdb.selectAllFromDomain(ctx, filter) default: return nil, errMissingArgs } } func (mdb *DB) selectFromDomain(ctx context.Context, filter *sqlplugin.DomainFilter) ([]sqlplugin.DomainRow, error) { var err error var row sqlplugin.DomainRow switch { case filter.ID != nil: err = mdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row, getDomainByIDQuery, shardID, *filter.ID) case filter.Name != nil: err = mdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row, getDomainByNameQuery, shardID, *filter.Name) } if err != nil { return nil, err } return []sqlplugin.DomainRow{row}, err } func (mdb *DB) selectAllFromDomain(ctx context.Context, filter *sqlplugin.DomainFilter) ([]sqlplugin.DomainRow, error) { var err error var rows []sqlplugin.DomainRow switch { case filter.GreaterThanID != nil: err = mdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, listDomainsRangeQuery, shardID, *filter.GreaterThanID, *filter.PageSize) default: err = mdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, listDomainsQuery, shardID, filter.PageSize) } return rows, err } // DeleteFromDomain deletes a single row in domains table func (mdb *DB) DeleteFromDomain(ctx context.Context, filter *sqlplugin.DomainFilter) (sql.Result, error) { var err error var result sql.Result switch { case filter.ID != nil: result, err = mdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, deleteDomainByIDQuery, shardID, filter.ID) default: result, err = mdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, deleteDomainByNameQuery, shardID, filter.Name) } return result, err } // LockDomainMetadata acquires a write lock on a single row in domain_metadata table func (mdb *DB) LockDomainMetadata(ctx context.Context) error { var row sqlplugin.DomainMetadataRow err := mdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row.NotificationVersion, lockDomainMetadataQuery) return err } // SelectFromDomainMetadata reads a single row in domain_metadata table func (mdb *DB) SelectFromDomainMetadata(ctx context.Context) (*sqlplugin.DomainMetadataRow, error) { var row sqlplugin.DomainMetadataRow err := mdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row.NotificationVersion, getDomainMetadataQuery) return &row, err } // UpdateDomainMetadata updates a single row in domain_metadata table func (mdb *DB) UpdateDomainMetadata(ctx context.Context, row *sqlplugin.DomainMetadataRow) (sql.Result, error) { return mdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, updateDomainMetadataQuery, row.NotificationVersion+1, row.NotificationVersion) } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/domain_audit_log.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( _insertDomainAuditLogQuery = `INSERT INTO domain_audit_log ( domain_id, event_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` _selectDomainAuditLogsQuery = `SELECT event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment FROM domain_audit_log WHERE domain_id = ? AND operation_type = ? AND created_time >= ? AND (created_time < ? OR (created_time = ? AND event_id > ?)) ORDER BY created_time DESC, event_id ASC LIMIT ?` _selectAllDomainAuditLogsQuery = `SELECT event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment FROM domain_audit_log WHERE domain_id = ? AND operation_type = ? AND created_time >= ? AND (created_time < ? OR (created_time = ? AND event_id > ?)) ORDER BY created_time DESC, event_id ASC` ) // InsertIntoDomainAuditLog inserts a single row into domain_audit_log table func (mdb *DB) InsertIntoDomainAuditLog(ctx context.Context, row *sqlplugin.DomainAuditLogRow) (sql.Result, error) { return mdb.driver.ExecContext( ctx, sqlplugin.DbDefaultShard, _insertDomainAuditLogQuery, row.DomainID, row.EventID, row.StateBefore, row.StateBeforeEncoding, row.StateAfter, row.StateAfterEncoding, row.OperationType, row.CreatedTime, row.LastUpdatedTime, row.Identity, row.IdentityType, row.Comment, ) } // SelectFromDomainAuditLogs returns audit log entries for a domain, operation type, and time range func (mdb *DB) SelectFromDomainAuditLogs( ctx context.Context, filter *sqlplugin.DomainAuditLogFilter, ) ([]*sqlplugin.DomainAuditLogRow, error) { args := []interface{}{ filter.DomainID, filter.OperationType, *filter.MinCreatedTime, *filter.PageMaxCreatedTime, *filter.PageMaxCreatedTime, *filter.PageMinEventID, } var rows []*sqlplugin.DomainAuditLogRow if filter.PageSize > 0 { args = append(args, filter.PageSize) err := mdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, _selectDomainAuditLogsQuery, args...) if err != nil { return nil, err } } else { err := mdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, _selectAllDomainAuditLogsQuery, args...) if err != nil { return nil, err } } return rows, nil } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/domain_audit_log_test.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "errors" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqldriver" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) func TestInsertIntoDomainAuditLog(t *testing.T) { now := time.Now().UTC() tests := []struct { name string row *sqlplugin.DomainAuditLogRow mockSetup func(*sqldriver.MockDriver) wantErr bool }{ { name: "successfully inserted", row: &sqlplugin.DomainAuditLogRow{ DomainID: uuid.New(), EventID: uuid.New(), StateBefore: []byte("state-before"), StateBeforeEncoding: constants.EncodingTypeJSON, StateAfter: []byte("state-after"), StateAfterEncoding: constants.EncodingTypeJSON, OperationType: persistence.DomainAuditOperationTypeFailover, CreatedTime: now, LastUpdatedTime: now, Identity: "test-identity", IdentityType: "user", Comment: "test comment", }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().ExecContext( gomock.Any(), sqlplugin.DbDefaultShard, _insertDomainAuditLogQuery, gomock.Any(), gomock.Any(), []byte("state-before"), constants.EncodingTypeJSON, []byte("state-after"), constants.EncodingTypeJSON, persistence.DomainAuditOperationTypeFailover, now, now, "test-identity", "user", "test comment", ).Return(nil, nil) }, wantErr: false, }, { name: "exec failed", row: &sqlplugin.DomainAuditLogRow{ DomainID: uuid.New(), EventID: uuid.New(), OperationType: persistence.DomainAuditOperationTypeFailover, CreatedTime: now, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().ExecContext( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(nil, errors.New("exec failed")) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDriver := sqldriver.NewMockDriver(ctrl) tc.mockSetup(mockDriver) mdb := &DB{ driver: mockDriver, converter: &converter{}, } _, err := mdb.InsertIntoDomainAuditLog(context.Background(), tc.row) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestSelectFromDomainAuditLogs(t *testing.T) { domainID := "d1111111-1111-1111-1111-111111111111" operationType := persistence.DomainAuditOperationTypeFailover minTime := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) maxTime := time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC) // Create times in descending order createdTime1 := time.Date(2024, 7, 1, 12, 0, 0, 0, time.UTC) createdTime2 := time.Date(2024, 6, 1, 12, 0, 0, 0, time.UTC) createdTime3 := time.Date(2024, 5, 1, 12, 0, 0, 0, time.UTC) createdTime4 := time.Date(2024, 4, 1, 12, 0, 0, 0, time.UTC) eventID1 := "e1111111-1111-1111-1111-111111111111" eventID2 := "e2222222-2222-2222-2222-222222222222" eventID3 := "e3333333-3333-3333-3333-333333333333" eventID4 := "e4444444-4444-4444-4444-444444444444" // Default page cursor: maxCreatedTime with max UUID (no page token) defaultPageMaxCreatedTime := maxTime defaultPageMinEventID := "ffffffff-ffff-ffff-ffff-ffffffffffff" tests := []struct { name string filter *sqlplugin.DomainAuditLogFilter mockSetup func(*sqldriver.MockDriver) wantRows []*sqlplugin.DomainAuditLogRow wantErr bool }{ { name: "pageSize limits number of results; no pageToken", filter: &sqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, PageSize: 2, PageMaxCreatedTime: &defaultPageMaxCreatedTime, PageMinEventID: &defaultPageMinEventID, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().SelectContext( gomock.Any(), sqlplugin.DbDefaultShard, gomock.Any(), _selectDomainAuditLogsQuery, domainID, operationType, minTime, defaultPageMaxCreatedTime, defaultPageMaxCreatedTime, defaultPageMinEventID, 2, ).DoAndReturn(func(ctx context.Context, shardID int, dest interface{}, query string, args ...interface{}) error { rows := dest.(*[]*sqlplugin.DomainAuditLogRow) *rows = []*sqlplugin.DomainAuditLogRow{ { EventID: eventID1, DomainID: domainID, CreatedTime: createdTime1, OperationType: operationType, }, { EventID: eventID2, DomainID: domainID, CreatedTime: createdTime2, OperationType: operationType, }, } return nil }) }, wantRows: []*sqlplugin.DomainAuditLogRow{ { EventID: eventID1, DomainID: domainID, CreatedTime: createdTime1, OperationType: operationType, }, { EventID: eventID2, DomainID: domainID, CreatedTime: createdTime2, OperationType: operationType, }, }, wantErr: false, }, { name: "pageToken filters results by createdTime and eventID; pageSize is 0", filter: &sqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, PageSize: 0, PageMaxCreatedTime: &createdTime2, PageMinEventID: &eventID2, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().SelectContext( gomock.Any(), sqlplugin.DbDefaultShard, gomock.Any(), _selectAllDomainAuditLogsQuery, domainID, operationType, minTime, createdTime2, createdTime2, eventID2, ).DoAndReturn(func(ctx context.Context, shardID int, dest interface{}, query string, args ...interface{}) error { rows := dest.(*[]*sqlplugin.DomainAuditLogRow) *rows = []*sqlplugin.DomainAuditLogRow{ { EventID: eventID3, DomainID: domainID, CreatedTime: createdTime3, OperationType: operationType, }, { EventID: eventID4, DomainID: domainID, CreatedTime: createdTime4, OperationType: operationType, }, } return nil }) }, wantRows: []*sqlplugin.DomainAuditLogRow{ { EventID: eventID3, DomainID: domainID, CreatedTime: createdTime3, OperationType: operationType, }, { EventID: eventID4, DomainID: domainID, CreatedTime: createdTime4, OperationType: operationType, }, }, wantErr: false, }, { name: "success with no results", filter: &sqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, PageMaxCreatedTime: &defaultPageMaxCreatedTime, PageMinEventID: &defaultPageMinEventID, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().SelectContext( gomock.Any(), sqlplugin.DbDefaultShard, gomock.Any(), _selectAllDomainAuditLogsQuery, domainID, operationType, minTime, defaultPageMaxCreatedTime, defaultPageMaxCreatedTime, defaultPageMinEventID, ).DoAndReturn(func(ctx context.Context, shardID int, dest interface{}, query string, args ...interface{}) error { return nil }) }, wantRows: []*sqlplugin.DomainAuditLogRow{}, wantErr: false, }, { name: "error when select fails", filter: &sqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, PageMaxCreatedTime: &defaultPageMaxCreatedTime, PageMinEventID: &defaultPageMinEventID, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().SelectContext( gomock.Any(), sqlplugin.DbDefaultShard, gomock.Any(), _selectAllDomainAuditLogsQuery, domainID, operationType, minTime, defaultPageMaxCreatedTime, defaultPageMaxCreatedTime, defaultPageMinEventID, ).Return(errors.New("select failed")) }, wantRows: nil, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDriver := sqldriver.NewMockDriver(ctrl) tc.mockSetup(mockDriver) mdb := &DB{ driver: mockDriver, converter: &converter{}, } rows, err := mdb.SelectFromDomainAuditLogs(context.Background(), tc.filter) if tc.wantErr { assert.Error(t, err) return } assert.NoError(t, err) assert.Equal(t, len(tc.wantRows), len(rows), "number of rows should match") for i, wantRow := range tc.wantRows { if i < len(rows) { assert.Equal(t, wantRow.EventID, rows[i].EventID, "row %d eventID", i) assert.Equal(t, wantRow.DomainID, rows[i].DomainID, "row %d domainID", i) assert.Equal(t, wantRow.OperationType, rows[i].OperationType, "row %d operationType", i) assert.Equal(t, wantRow.CreatedTime.Unix(), rows[i].CreatedTime.Unix(), "row %d createdTime", i) } } }) } } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/dsn_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "net/url" "strings" "testing" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/config" ) type StoreTestSuite struct { suite.Suite } func TestStoreTestSuite(t *testing.T) { suite.Run(t, new(StoreTestSuite)) } func (s *StoreTestSuite) TestBuildDSN() { testCases := []struct { in config.SQL outURLPath string outIsolationKey string outIsolationVal string }{ { in: config.SQL{ User: "test", Password: "pass", ConnectProtocol: "tcp", ConnectAddr: "192.168.0.1:3306", DatabaseName: "db1", EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, }, outIsolationKey: "transaction_isolation", outIsolationVal: "'READ-COMMITTED'", outURLPath: "test:pass@tcp(192.168.0.1:3306)/db1?", }, { in: config.SQL{ User: "test", Password: "pass", ConnectProtocol: "tcp", ConnectAddr: "192.168.0.1:3306", DatabaseName: "db1", ConnectAttributes: map[string]string{"k1": "v1", "k2": "v2"}, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, }, outIsolationKey: "transaction_isolation", outIsolationVal: "'READ-COMMITTED'", outURLPath: "test:pass@tcp(192.168.0.1:3306)/db1?", }, { in: config.SQL{ User: "test", Password: "pass", ConnectProtocol: "tcp", ConnectAddr: "192.168.0.1:3306", DatabaseName: "db1", ConnectAttributes: map[string]string{"k1": "v1", "k2": "v2", "tx_isolation": "'REPEATABLE-READ'"}, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, }, outIsolationKey: "tx_isolation", outIsolationVal: "'repeatable-read'", outURLPath: "test:pass@tcp(192.168.0.1:3306)/db1?", }, { in: config.SQL{ User: "test", Password: "pass", ConnectProtocol: "tcp", ConnectAddr: "192.168.0.1:3306", DatabaseName: "db1", ConnectAttributes: map[string]string{"k1": "v1", "k2": "v2", "tx_isolation": "REPEATABLE-READ"}, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, }, outIsolationKey: "tx_isolation", outIsolationVal: "'repeatable-read'", outURLPath: "test:pass@tcp(192.168.0.1:3306)/db1?", }, { in: config.SQL{ User: "test", Password: "pass", ConnectProtocol: "tcp", ConnectAddr: "192.168.0.1:3306", DatabaseName: "db1", ConnectAttributes: map[string]string{"k1": "v1", "k2": "v2", "transaction_isolation": "REPEATABLE-READ"}, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, }, outIsolationKey: "transaction_isolation", outIsolationVal: "'repeatable-read'", outURLPath: "test:pass@tcp(192.168.0.1:3306)/db1?", }, } for _, tc := range testCases { out := buildDSN(&tc.in) s.True(strings.HasPrefix(out, tc.outURLPath), "invalid url path") tokens := strings.Split(out, "?") s.Equal(2, len(tokens), "invalid url") qry, err := url.Parse("?" + tokens[1]) s.NoError(err) wantAttrs := buildExpectedURLParams(tc.in.ConnectAttributes, tc.outIsolationKey, tc.outIsolationVal) s.Equal(wantAttrs, qry.Query(), "invalid dsn url params") } } func buildExpectedURLParams(attrs map[string]string, isolationKey string, isolationValue string) url.Values { result := make(map[string][]string, len(dsnAttrOverrides)+len(attrs)+1) for k, v := range attrs { result[k] = []string{v} } result[isolationKey] = []string{isolationValue} for k, v := range dsnAttrOverrides { result[k] = []string{v} } return result } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/events.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( // below are templates for history_node table addHistoryNodesQuery = `INSERT INTO history_node (` + `shard_id, tree_id, branch_id, node_id, txn_id, data, data_encoding) ` + `VALUES (:shard_id, :tree_id, :branch_id, :node_id, :txn_id, :data, :data_encoding) ` getHistoryNodesQuery = `SELECT node_id, txn_id, data, data_encoding FROM history_node ` + `WHERE shard_id = ? AND tree_id = ? AND branch_id = ? AND node_id >= ? and node_id < ? ORDER BY shard_id, tree_id, branch_id, node_id, txn_id LIMIT ? ` deleteHistoryNodesQuery = `DELETE FROM history_node WHERE shard_id = ? AND tree_id = ? AND branch_id = ? AND node_id >= ? ` deleteHistoryNodesByBatchQuery = `DELETE FROM history_node WHERE shard_id = ? AND tree_id = ? AND branch_id = ? AND node_id >= ? ORDER BY shard_id, tree_id, branch_id, node_id, txn_id LIMIT ? ` // below are templates for history_tree table addHistoryTreeQuery = `INSERT INTO history_tree (` + `shard_id, tree_id, branch_id, data, data_encoding) ` + `VALUES (:shard_id, :tree_id, :branch_id, :data, :data_encoding) ` getHistoryTreeQuery = `SELECT branch_id, data, data_encoding FROM history_tree WHERE shard_id = ? AND tree_id = ? ` deleteHistoryTreeQuery = `DELETE FROM history_tree WHERE shard_id = ? AND tree_id = ? AND branch_id = ? ` getAllHistoryTreeQuery = `SELECT shard_id, tree_id, branch_id, data, data_encoding FROM history_tree WHERE (shard_id = ? AND tree_id = ? AND branch_id > ?) OR (shard_id = ? AND tree_id > ?) OR (shard_id > ?) ORDER BY shard_id, tree_id, branch_id LIMIT ?` ) // For history_node table: // InsertIntoHistoryNode inserts a row into history_node table func (mdb *DB) InsertIntoHistoryNode(ctx context.Context, row *sqlplugin.HistoryNodeRow) (sql.Result, error) { // NOTE: Query 5.6 doesn't support clustering order, to workaround, we let txn_id multiple by -1 *row.TxnID *= -1 dbShardID := sqlplugin.GetDBShardIDFromTreeID(row.TreeID, mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, addHistoryNodesQuery, row) } // SelectFromHistoryNode reads one or more rows from history_node table func (mdb *DB) SelectFromHistoryNode(ctx context.Context, filter *sqlplugin.HistoryNodeFilter) ([]sqlplugin.HistoryNodeRow, error) { var rows []sqlplugin.HistoryNodeRow dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getHistoryNodesQuery, filter.ShardID, filter.TreeID, filter.BranchID, *filter.MinNodeID, *filter.MaxNodeID, filter.PageSize) // NOTE: since we let txn_id multiple by -1 when inserting, we have to revert it back here for _, row := range rows { *row.TxnID *= -1 } return rows, err } // DeleteFromHistoryNode deletes one or more rows from history_node table func (mdb *DB) DeleteFromHistoryNode(ctx context.Context, filter *sqlplugin.HistoryNodeFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, mdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return mdb.driver.ExecContext(ctx, dbShardID, deleteHistoryNodesByBatchQuery, filter.ShardID, filter.TreeID, filter.BranchID, *filter.MinNodeID, filter.PageSize) } return mdb.driver.ExecContext(ctx, dbShardID, deleteHistoryNodesQuery, filter.ShardID, filter.TreeID, filter.BranchID, *filter.MinNodeID) } // For history_tree table: // InsertIntoHistoryTree inserts a row into history_tree table func (mdb *DB) InsertIntoHistoryTree(ctx context.Context, row *sqlplugin.HistoryTreeRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(row.TreeID, mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, addHistoryTreeQuery, row) } // SelectFromHistoryTree reads one or more rows from history_tree table func (mdb *DB) SelectFromHistoryTree(ctx context.Context, filter *sqlplugin.HistoryTreeFilter) ([]sqlplugin.HistoryTreeRow, error) { var rows []sqlplugin.HistoryTreeRow dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getHistoryTreeQuery, filter.ShardID, filter.TreeID) return rows, err } // DeleteFromHistoryTree deletes one or more rows from history_tree table func (mdb *DB) DeleteFromHistoryTree(ctx context.Context, filter *sqlplugin.HistoryTreeFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, deleteHistoryTreeQuery, filter.ShardID, filter.TreeID, *filter.BranchID) } func (mdb *DB) GetAllHistoryTreeBranches(ctx context.Context, filter *sqlplugin.HistoryTreeFilter) ([]sqlplugin.HistoryTreeRow, error) { var rows []sqlplugin.HistoryTreeRow dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getAllHistoryTreeQuery, filter.ShardID, filter.TreeID, *filter.BranchID, filter.ShardID, filter.TreeID, filter.ShardID, filter.PageSize) return rows, err } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/execution.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( executionsColumns = `shard_id, domain_id, workflow_id, run_id, next_event_id, last_write_version, data, data_encoding` createExecutionQuery = `INSERT INTO executions(` + executionsColumns + `) VALUES(:shard_id, :domain_id, :workflow_id, :run_id, :next_event_id, :last_write_version, :data, :data_encoding)` updateExecutionQuery = `UPDATE executions SET next_event_id = :next_event_id, last_write_version = :last_write_version, data = :data, data_encoding = :data_encoding WHERE shard_id = :shard_id AND domain_id = :domain_id AND workflow_id = :workflow_id AND run_id = :run_id` getExecutionQuery = `SELECT ` + executionsColumns + ` FROM executions WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ?` listExecutionQuery = `SELECT ` + executionsColumns + ` FROM executions WHERE shard_id = ? AND workflow_id > ? ORDER BY workflow_id LIMIT ?` deleteExecutionQuery = `DELETE FROM executions WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ?` lockExecutionQueryBase = `SELECT next_event_id FROM executions WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ?` writeLockExecutionQuery = lockExecutionQueryBase + ` FOR UPDATE` readLockExecutionQuery = lockExecutionQueryBase + ` LOCK IN SHARE MODE` createCurrentExecutionQuery = `INSERT INTO current_executions (shard_id, domain_id, workflow_id, run_id, create_request_id, state, close_status, start_version, last_write_version) VALUES (:shard_id, :domain_id, :workflow_id, :run_id, :create_request_id, :state, :close_status, :start_version, :last_write_version)` deleteCurrentExecutionQuery = "DELETE FROM current_executions WHERE shard_id=? AND domain_id=? AND workflow_id=? AND run_id=?" getCurrentExecutionQuery = `SELECT shard_id, domain_id, workflow_id, run_id, create_request_id, state, close_status, start_version, last_write_version FROM current_executions WHERE shard_id = ? AND domain_id = ? AND workflow_id = ?` lockCurrentExecutionJoinExecutionsQuery = `SELECT ce.shard_id, ce.domain_id, ce.workflow_id, ce.run_id, ce.create_request_id, ce.state, ce.close_status, ce.start_version, e.last_write_version FROM current_executions ce INNER JOIN executions e ON e.shard_id = ce.shard_id AND e.domain_id = ce.domain_id AND e.workflow_id = ce.workflow_id AND e.run_id = ce.run_id WHERE ce.shard_id = ? AND ce.domain_id = ? AND ce.workflow_id = ? FOR UPDATE` lockCurrentExecutionQuery = getCurrentExecutionQuery + ` FOR UPDATE` updateCurrentExecutionsQuery = `UPDATE current_executions SET run_id = :run_id, create_request_id = :create_request_id, state = :state, close_status = :close_status, start_version = :start_version, last_write_version = :last_write_version WHERE shard_id = :shard_id AND domain_id = :domain_id AND workflow_id = :workflow_id ` getTransferTasksQuery = `SELECT task_id, data, data_encoding FROM transfer_tasks WHERE shard_id = ? AND task_id >= ? AND task_id < ? ORDER BY shard_id, task_id LIMIT ?` createTransferTasksQuery = `INSERT INTO transfer_tasks(shard_id, task_id, data, data_encoding) VALUES(:shard_id, :task_id, :data, :data_encoding)` deleteTransferTaskQuery = `DELETE FROM transfer_tasks WHERE shard_id = ? AND task_id = ?` rangeDeleteTransferTaskQuery = `DELETE FROM transfer_tasks WHERE shard_id = ? AND task_id >= ? AND task_id < ?` rangeDeleteTransferTaskByBatchQuery = rangeDeleteTransferTaskQuery + ` ORDER BY task_id LIMIT ?` getCrossClusterTasksQuery = `SELECT task_id, data, data_encoding FROM cross_cluster_tasks WHERE target_cluster = ? AND shard_id = ? AND task_id > ? AND task_id <= ? ORDER BY task_id LIMIT ?` createCrossClusterTasksQuery = `INSERT INTO cross_cluster_tasks(target_cluster, shard_id, task_id, data, data_encoding) VALUES(:target_cluster, :shard_id, :task_id, :data, :data_encoding)` deleteCrossClusterTaskQuery = `DELETE FROM cross_cluster_tasks WHERE target_cluster = ? AND shard_id = ? AND task_id = ?` rangeDeleteCrossClusterTaskQuery = `DELETE FROM cross_cluster_tasks WHERE target_cluster = ? AND shard_id = ? AND task_id >= ? AND task_id < ?` rangeDeleteCrossClusterTaskByBatchQuery = rangeDeleteCrossClusterTaskQuery + ` ORDER BY task_id LIMIT ?` createTimerTasksQuery = `INSERT INTO timer_tasks (shard_id, visibility_timestamp, task_id, data, data_encoding) VALUES (:shard_id, :visibility_timestamp, :task_id, :data, :data_encoding)` getTimerTasksQuery = `SELECT visibility_timestamp, task_id, data, data_encoding FROM timer_tasks WHERE shard_id = ? AND ((visibility_timestamp >= ? AND task_id >= ?) OR visibility_timestamp > ?) AND visibility_timestamp < ? ORDER BY visibility_timestamp,task_id LIMIT ?` deleteTimerTaskQuery = `DELETE FROM timer_tasks WHERE shard_id = ? AND visibility_timestamp = ? AND task_id = ?` rangeDeleteTimerTaskQuery = `DELETE FROM timer_tasks WHERE shard_id = ? AND visibility_timestamp >= ? AND visibility_timestamp < ?` rangeDeleteTimerTaskByBatchQuery = rangeDeleteTimerTaskQuery + ` ORDER BY visibility_timestamp,task_id LIMIT ?` createReplicationTasksQuery = `INSERT INTO replication_tasks (shard_id, task_id, data, data_encoding) VALUES(:shard_id, :task_id, :data, :data_encoding)` getReplicationTasksQuery = `SELECT task_id, data, data_encoding FROM replication_tasks WHERE shard_id = ? AND task_id >= ? AND task_id < ? ORDER BY task_id LIMIT ?` deleteReplicationTaskQuery = `DELETE FROM replication_tasks WHERE shard_id = ? AND task_id = ?` rangeDeleteReplicationTaskQuery = `DELETE FROM replication_tasks WHERE shard_id = ? AND task_id < ?` rangeDeleteReplicationTaskByBatchQuery = rangeDeleteReplicationTaskQuery + ` ORDER BY task_id LIMIT ?` getReplicationTasksDLQQuery = `SELECT task_id, data, data_encoding FROM replication_tasks_dlq WHERE source_cluster_name = ? AND shard_id = ? AND task_id >= ? AND task_id < ? ORDER BY task_id LIMIT ?` getReplicationTaskDLQQuery = `SELECT count(1) as count FROM replication_tasks_dlq WHERE source_cluster_name = ? AND shard_id = ?` bufferedEventsColumns = `shard_id, domain_id, workflow_id, run_id, data, data_encoding` createBufferedEventsQuery = `INSERT INTO buffered_events(` + bufferedEventsColumns + `) VALUES (:shard_id, :domain_id, :workflow_id, :run_id, :data, :data_encoding)` deleteBufferedEventsQuery = `DELETE FROM buffered_events WHERE shard_id=? AND domain_id=? AND workflow_id=? AND run_id=?` getBufferedEventsQuery = `SELECT data, data_encoding FROM buffered_events WHERE shard_id=? AND domain_id=? AND workflow_id=? AND run_id=?` insertReplicationTaskDLQQuery = ` INSERT INTO replication_tasks_dlq (source_cluster_name, shard_id, task_id, data, data_encoding) VALUES (:source_cluster_name, :shard_id, :task_id, :data, :data_encoding) ` deleteReplicationTaskFromDLQQuery = ` DELETE FROM replication_tasks_dlq WHERE source_cluster_name = ? AND shard_id = ? AND task_id = ?` rangeDeleteReplicationTaskFromDLQQuery = ` DELETE FROM replication_tasks_dlq WHERE source_cluster_name = ? AND shard_id = ? AND task_id >= ? AND task_id < ?` rangeDeleteReplicationTaskFromDLQByBatchQuery = rangeDeleteReplicationTaskFromDLQQuery + ` ORDER BY task_id LIMIT ?` ) // InsertIntoExecutions inserts a row into executions table func (mdb *DB) InsertIntoExecutions(ctx context.Context, row *sqlplugin.ExecutionsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(row.ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, createExecutionQuery, row) } // UpdateExecutions updates a single row in executions table func (mdb *DB) UpdateExecutions(ctx context.Context, row *sqlplugin.ExecutionsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(row.ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, updateExecutionQuery, row) } // SelectFromExecutions reads a single row from executions table // The list execution query result is order by workflow ID only. It may returns duplicate record with pagination. func (mdb *DB) SelectFromExecutions(ctx context.Context, filter *sqlplugin.ExecutionsFilter) ([]sqlplugin.ExecutionsRow, error) { var rows []sqlplugin.ExecutionsRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) var err error if len(filter.DomainID) == 0 && filter.Size > 0 { err = mdb.driver.SelectContext(ctx, dbShardID, &rows, listExecutionQuery, filter.ShardID, filter.WorkflowID, filter.Size) if err != nil { return nil, err } } else { var row sqlplugin.ExecutionsRow err = mdb.driver.GetContext(ctx, dbShardID, &row, getExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) if err != nil { return nil, err } rows = append(rows, row) } return rows, err } // DeleteFromExecutions deletes a single row from executions table func (mdb *DB) DeleteFromExecutions(ctx context.Context, filter *sqlplugin.ExecutionsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, deleteExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } // ReadLockExecutions acquires a write lock on a single row in executions table func (mdb *DB) ReadLockExecutions(ctx context.Context, filter *sqlplugin.ExecutionsFilter) (int, error) { var nextEventID int dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) err := mdb.driver.GetContext(ctx, dbShardID, &nextEventID, readLockExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) return nextEventID, err } // WriteLockExecutions acquires a write lock on a single row in executions table func (mdb *DB) WriteLockExecutions(ctx context.Context, filter *sqlplugin.ExecutionsFilter) (int, error) { var nextEventID int dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) err := mdb.driver.GetContext(ctx, dbShardID, &nextEventID, writeLockExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) return nextEventID, err } // InsertIntoCurrentExecutions inserts a single row into current_executions table func (mdb *DB) InsertIntoCurrentExecutions(ctx context.Context, row *sqlplugin.CurrentExecutionsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, createCurrentExecutionQuery, row) } // UpdateCurrentExecutions updates a single row in current_executions table func (mdb *DB) UpdateCurrentExecutions(ctx context.Context, row *sqlplugin.CurrentExecutionsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, updateCurrentExecutionsQuery, row) } // SelectFromCurrentExecutions reads one or more rows from current_executions table func (mdb *DB) SelectFromCurrentExecutions(ctx context.Context, filter *sqlplugin.CurrentExecutionsFilter) (*sqlplugin.CurrentExecutionsRow, error) { var row sqlplugin.CurrentExecutionsRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) err := mdb.driver.GetContext(ctx, dbShardID, &row, getCurrentExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID) return &row, err } // DeleteFromCurrentExecutions deletes a single row in current_executions table func (mdb *DB) DeleteFromCurrentExecutions(ctx context.Context, filter *sqlplugin.CurrentExecutionsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, deleteCurrentExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } // LockCurrentExecutions acquires a write lock on a single row in current_executions table func (mdb *DB) LockCurrentExecutions(ctx context.Context, filter *sqlplugin.CurrentExecutionsFilter) (*sqlplugin.CurrentExecutionsRow, error) { var row sqlplugin.CurrentExecutionsRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) err := mdb.driver.GetContext(ctx, dbShardID, &row, lockCurrentExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID) return &row, err } // LockCurrentExecutionsJoinExecutions joins a row in current_executions with executions table and acquires a // write lock on the result func (mdb *DB) LockCurrentExecutionsJoinExecutions(ctx context.Context, filter *sqlplugin.CurrentExecutionsFilter) ([]sqlplugin.CurrentExecutionsRow, error) { var rows []sqlplugin.CurrentExecutionsRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, lockCurrentExecutionJoinExecutionsQuery, filter.ShardID, filter.DomainID, filter.WorkflowID) return rows, err } // InsertIntoTransferTasks inserts one or more rows into transfer_tasks table func (mdb *DB) InsertIntoTransferTasks(ctx context.Context, rows []sqlplugin.TransferTasksRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(rows[0].ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, createTransferTasksQuery, rows) } // SelectFromTransferTasks reads one or more rows from transfer_tasks table func (mdb *DB) SelectFromTransferTasks(ctx context.Context, filter *sqlplugin.TransferTasksFilter) ([]sqlplugin.TransferTasksRow, error) { var rows []sqlplugin.TransferTasksRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getTransferTasksQuery, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize) if err != nil { return nil, err } return rows, err } // DeleteFromTransferTasks deletes one row from transfer_tasks table func (mdb *DB) DeleteFromTransferTasks(ctx context.Context, filter *sqlplugin.TransferTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, deleteTransferTaskQuery, filter.ShardID, filter.TaskID) } // RangeDeleteFromTransferTasks deletes multi rows from transfer_tasks table func (mdb *DB) RangeDeleteFromTransferTasks(ctx context.Context, filter *sqlplugin.TransferTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTransferTaskByBatchQuery, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize) } return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTransferTaskQuery, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID) } // InsertIntoCrossClusterTasks inserts one or more rows into cross_cluster_tasks table func (mdb *DB) InsertIntoCrossClusterTasks(ctx context.Context, rows []sqlplugin.CrossClusterTasksRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(rows[0].ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, createCrossClusterTasksQuery, rows) } // SelectFromCrossClusterTasks reads one or more rows from cross_cluster_tasks table func (mdb *DB) SelectFromCrossClusterTasks(ctx context.Context, filter *sqlplugin.CrossClusterTasksFilter) ([]sqlplugin.CrossClusterTasksRow, error) { var rows []sqlplugin.CrossClusterTasksRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getCrossClusterTasksQuery, filter.TargetCluster, filter.ShardID, filter.MinTaskID, filter.MaxTaskID, filter.PageSize) if err != nil { return nil, err } return rows, err } // DeleteFromCrossClusterTasks deletes one row from cross_cluster_tasks table func (mdb *DB) DeleteFromCrossClusterTasks(ctx context.Context, filter *sqlplugin.CrossClusterTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, deleteCrossClusterTaskQuery, filter.TargetCluster, filter.ShardID, filter.TaskID) } // RangeDeleteFromCrossClusterTasks deletes multi rows from cross_cluster_tasks table func (mdb *DB) RangeDeleteFromCrossClusterTasks(ctx context.Context, filter *sqlplugin.CrossClusterTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteCrossClusterTaskByBatchQuery, filter.TargetCluster, filter.ShardID, filter.MinTaskID, filter.MaxTaskID, filter.PageSize) } return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteCrossClusterTaskQuery, filter.TargetCluster, filter.ShardID, filter.MinTaskID, filter.MaxTaskID) } // InsertIntoTimerTasks inserts one or more rows into timer_tasks table func (mdb *DB) InsertIntoTimerTasks(ctx context.Context, rows []sqlplugin.TimerTasksRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(rows[0].ShardID, mdb.GetTotalNumDBShards()) for i := range rows { rows[i].VisibilityTimestamp = mdb.converter.ToDateTime(rows[i].VisibilityTimestamp) } return mdb.driver.NamedExecContext(ctx, dbShardID, createTimerTasksQuery, rows) } // SelectFromTimerTasks reads one or more rows from timer_tasks table func (mdb *DB) SelectFromTimerTasks(ctx context.Context, filter *sqlplugin.TimerTasksFilter) ([]sqlplugin.TimerTasksRow, error) { var rows []sqlplugin.TimerTasksRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) filter.MinVisibilityTimestamp = mdb.converter.ToDateTime(filter.MinVisibilityTimestamp) filter.MaxVisibilityTimestamp = mdb.converter.ToDateTime(filter.MaxVisibilityTimestamp) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getTimerTasksQuery, filter.ShardID, filter.MinVisibilityTimestamp, filter.TaskID, filter.MinVisibilityTimestamp, filter.MaxVisibilityTimestamp, filter.PageSize) if err != nil { return nil, err } for i := range rows { rows[i].VisibilityTimestamp = mdb.converter.FromDateTime(rows[i].VisibilityTimestamp) } return rows, err } // DeleteFromTimerTasks deletes one row from timer_tasks table func (mdb *DB) DeleteFromTimerTasks(ctx context.Context, filter *sqlplugin.TimerTasksFilter) (sql.Result, error) { filter.VisibilityTimestamp = mdb.converter.ToDateTime(filter.VisibilityTimestamp) dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, deleteTimerTaskQuery, filter.ShardID, filter.VisibilityTimestamp, filter.TaskID) } // RangeDeleteFromTimerTasks deletes multi rows from timer_tasks table func (mdb *DB) RangeDeleteFromTimerTasks(ctx context.Context, filter *sqlplugin.TimerTasksFilter) (sql.Result, error) { filter.MinVisibilityTimestamp = mdb.converter.ToDateTime(filter.MinVisibilityTimestamp) filter.MaxVisibilityTimestamp = mdb.converter.ToDateTime(filter.MaxVisibilityTimestamp) dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTimerTaskByBatchQuery, filter.ShardID, filter.MinVisibilityTimestamp, filter.MaxVisibilityTimestamp, filter.PageSize) } return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTimerTaskQuery, filter.ShardID, filter.MinVisibilityTimestamp, filter.MaxVisibilityTimestamp) } // InsertIntoBufferedEvents inserts one or more rows into buffered_events table func (mdb *DB) InsertIntoBufferedEvents(ctx context.Context, rows []sqlplugin.BufferedEventsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(rows[0].ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, createBufferedEventsQuery, rows) } // SelectFromBufferedEvents reads one or more rows from buffered_events table func (mdb *DB) SelectFromBufferedEvents(ctx context.Context, filter *sqlplugin.BufferedEventsFilter) ([]sqlplugin.BufferedEventsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) var rows []sqlplugin.BufferedEventsRow err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getBufferedEventsQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID rows[i].ShardID = filter.ShardID } return rows, err } // DeleteFromBufferedEvents deletes one or more rows from buffered_events table func (mdb *DB) DeleteFromBufferedEvents(ctx context.Context, filter *sqlplugin.BufferedEventsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, deleteBufferedEventsQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } // InsertIntoReplicationTasks inserts one or more rows into replication_tasks table func (mdb *DB) InsertIntoReplicationTasks(ctx context.Context, rows []sqlplugin.ReplicationTasksRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(rows[0].ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, createReplicationTasksQuery, rows) } // SelectFromReplicationTasks reads one or more rows from replication_tasks table func (mdb *DB) SelectFromReplicationTasks(ctx context.Context, filter *sqlplugin.ReplicationTasksFilter) ([]sqlplugin.ReplicationTasksRow, error) { var rows []sqlplugin.ReplicationTasksRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getReplicationTasksQuery, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize) return rows, err } // DeleteFromReplicationTasks deletes one row from replication_tasks table func (mdb *DB) DeleteFromReplicationTasks(ctx context.Context, filter *sqlplugin.ReplicationTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, deleteReplicationTaskQuery, filter.ShardID, filter.TaskID) } // RangeDeleteFromReplicationTasks deletes multi rows from replication_tasks table func (mdb *DB) RangeDeleteFromReplicationTasks(ctx context.Context, filter *sqlplugin.ReplicationTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteReplicationTaskByBatchQuery, filter.ShardID, filter.ExclusiveMaxTaskID, filter.PageSize) } return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteReplicationTaskQuery, filter.ShardID, filter.ExclusiveMaxTaskID) } // InsertIntoReplicationTasksDLQ inserts one or more rows into replication_tasks_dlq table func (mdb *DB) InsertIntoReplicationTasksDLQ(ctx context.Context, row *sqlplugin.ReplicationTaskDLQRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(row.ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, insertReplicationTaskDLQQuery, row) } // SelectFromReplicationTasksDLQ reads one or more rows from replication_tasks_dlq table func (mdb *DB) SelectFromReplicationTasksDLQ(ctx context.Context, filter *sqlplugin.ReplicationTasksDLQFilter) ([]sqlplugin.ReplicationTasksRow, error) { var rows []sqlplugin.ReplicationTasksRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext( ctx, dbShardID, &rows, getReplicationTasksDLQQuery, filter.SourceClusterName, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize) return rows, err } // SelectFromReplicationDLQ reads one row from replication_tasks_dlq table func (mdb *DB) SelectFromReplicationDLQ(ctx context.Context, filter *sqlplugin.ReplicationTaskDLQFilter) (int64, error) { var size []int64 dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) if err := mdb.driver.SelectContext( ctx, dbShardID, &size, getReplicationTaskDLQQuery, filter.SourceClusterName, filter.ShardID, ); err != nil { return 0, err } return size[0], nil } // DeleteMessageFromReplicationTasksDLQ deletes one row from replication_tasks_dlq table func (mdb *DB) DeleteMessageFromReplicationTasksDLQ( ctx context.Context, filter *sqlplugin.ReplicationTasksDLQFilter, ) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext( ctx, dbShardID, deleteReplicationTaskFromDLQQuery, filter.SourceClusterName, filter.ShardID, filter.TaskID, ) } // DeleteMessageFromReplicationTasksDLQ deletes one or more rows from replication_tasks_dlq table func (mdb *DB) RangeDeleteMessageFromReplicationTasksDLQ( ctx context.Context, filter *sqlplugin.ReplicationTasksDLQFilter, ) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return mdb.driver.ExecContext( ctx, dbShardID, rangeDeleteReplicationTaskFromDLQByBatchQuery, filter.SourceClusterName, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize, ) } return mdb.driver.ExecContext( ctx, dbShardID, rangeDeleteReplicationTaskFromDLQQuery, filter.SourceClusterName, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, ) } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/execution_maps.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "database/sql" "fmt" "strings" "github.com/jmoiron/sqlx" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( deleteMapQryTemplate = `DELETE FROM %v WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ?` // %[2]v is the columns of the value struct (i.e. no primary key columns), comma separated // %[3]v should be %[2]v with colons prepended. // i.e. %[3]v = ",".join(":" + s for s in %[2]v) // So that this query can be used with BindNamed // %[4]v should be the name of the key associated with the map // e.g. for ActivityInfo it is "schedule_id" setKeyInMapQryTemplate = `REPLACE INTO %[1]v (shard_id, domain_id, workflow_id, run_id, %[4]v, %[2]v) VALUES (:shard_id, :domain_id, :workflow_id, :run_id, :%[4]v, %[3]v)` // %[2]v is the name of the key deleteKeyInMapQryTemplate = `DELETE FROM %[1]v WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ? AND %[2]v IN ( ? )` // %[1]v is the name of the table // %[2]v is the name of the key // %[3]v is the value columns, separated by commas getMapQryTemplate = `SELECT %[2]v, %[3]v FROM %[1]v WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ?` ) func stringMap(a []string, f func(string) string) []string { b := make([]string, len(a)) for i, v := range a { b[i] = f(v) } return b } func makeDeleteMapQry(tableName string) string { return fmt.Sprintf(deleteMapQryTemplate, tableName) } func makeSetKeyInMapQry(tableName string, nonPrimaryKeyColumns []string, mapKeyName string) string { return fmt.Sprintf(setKeyInMapQryTemplate, tableName, strings.Join(nonPrimaryKeyColumns, ","), strings.Join(stringMap(nonPrimaryKeyColumns, func(x string) string { return ":" + x }), ","), mapKeyName) } func makeDeleteKeyInMapQry(tableName string, mapKeyName string) string { return fmt.Sprintf(deleteKeyInMapQryTemplate, tableName, mapKeyName) } func makeGetMapQryTemplate(tableName string, nonPrimaryKeyColumns []string, mapKeyName string) string { return fmt.Sprintf(getMapQryTemplate, tableName, mapKeyName, strings.Join(nonPrimaryKeyColumns, ",")) } var ( // Omit shard_id, run_id, domain_id, workflow_id, schedule_id since they're in the primary key activityInfoColumns = []string{ "data", "data_encoding", "last_heartbeat_details", "last_heartbeat_updated_time", } activityInfoTableName = "activity_info_maps" activityInfoKey = "schedule_id" deleteActivityInfoMapQry = makeDeleteMapQry(activityInfoTableName) setKeyInActivityInfoMapQry = makeSetKeyInMapQry(activityInfoTableName, activityInfoColumns, activityInfoKey) deleteKeyInActivityInfoMapQry = makeDeleteKeyInMapQry(activityInfoTableName, activityInfoKey) getActivityInfoMapQry = makeGetMapQryTemplate(activityInfoTableName, activityInfoColumns, activityInfoKey) ) // ReplaceIntoActivityInfoMaps replaces one or more rows in activity_info_maps table func (mdb *DB) ReplaceIntoActivityInfoMaps(ctx context.Context, rows []sqlplugin.ActivityInfoMapsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), mdb.GetTotalNumDBShards()) for i := range rows { rows[i].LastHeartbeatUpdatedTime = mdb.converter.ToDateTime(rows[i].LastHeartbeatUpdatedTime) } return mdb.driver.NamedExecContext(ctx, dbShardID, setKeyInActivityInfoMapQry, rows) } // SelectFromActivityInfoMaps reads one or more rows from activity_info_maps table func (mdb *DB) SelectFromActivityInfoMaps(ctx context.Context, filter *sqlplugin.ActivityInfoMapsFilter) ([]sqlplugin.ActivityInfoMapsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) var rows []sqlplugin.ActivityInfoMapsRow err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getActivityInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID rows[i].LastHeartbeatUpdatedTime = mdb.converter.FromDateTime(rows[i].LastHeartbeatUpdatedTime) } return rows, err } // DeleteFromActivityInfoMaps deletes one or more rows from activity_info_maps table func (mdb *DB) DeleteFromActivityInfoMaps(ctx context.Context, filter *sqlplugin.ActivityInfoMapsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) if len(filter.ScheduleIDs) > 0 { query, args, err := sqlx.In(deleteKeyInActivityInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.ScheduleIDs) if err != nil { return nil, err } return mdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return mdb.driver.ExecContext(ctx, dbShardID, deleteActivityInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } var ( timerInfoColumns = []string{ "data", "data_encoding", } timerInfoTableName = "timer_info_maps" timerInfoKey = "timer_id" deleteTimerInfoMapSQLQuery = makeDeleteMapQry(timerInfoTableName) setKeyInTimerInfoMapSQLQuery = makeSetKeyInMapQry(timerInfoTableName, timerInfoColumns, timerInfoKey) deleteKeyInTimerInfoMapSQLQuery = makeDeleteKeyInMapQry(timerInfoTableName, timerInfoKey) getTimerInfoMapSQLQuery = makeGetMapQryTemplate(timerInfoTableName, timerInfoColumns, timerInfoKey) ) // ReplaceIntoTimerInfoMaps replaces one or more rows in timer_info_maps table func (mdb *DB) ReplaceIntoTimerInfoMaps(ctx context.Context, rows []sqlplugin.TimerInfoMapsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, setKeyInTimerInfoMapSQLQuery, rows) } // SelectFromTimerInfoMaps reads one or more rows from timer_info_maps table func (mdb *DB) SelectFromTimerInfoMaps(ctx context.Context, filter *sqlplugin.TimerInfoMapsFilter) ([]sqlplugin.TimerInfoMapsRow, error) { var rows []sqlplugin.TimerInfoMapsRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getTimerInfoMapSQLQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID } return rows, err } // DeleteFromTimerInfoMaps deletes one or more rows from timer_info_maps table func (mdb *DB) DeleteFromTimerInfoMaps(ctx context.Context, filter *sqlplugin.TimerInfoMapsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) if len(filter.TimerIDs) > 0 { query, args, err := sqlx.In(deleteKeyInTimerInfoMapSQLQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.TimerIDs) if err != nil { return nil, err } return mdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return mdb.driver.ExecContext(ctx, dbShardID, deleteTimerInfoMapSQLQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } var ( childExecutionInfoColumns = []string{ "data", "data_encoding", } childExecutionInfoTableName = "child_execution_info_maps" childExecutionInfoKey = "initiated_id" deleteChildExecutionInfoMapQry = makeDeleteMapQry(childExecutionInfoTableName) setKeyInChildExecutionInfoMapQry = makeSetKeyInMapQry(childExecutionInfoTableName, childExecutionInfoColumns, childExecutionInfoKey) deleteKeyInChildExecutionInfoMapQry = makeDeleteKeyInMapQry(childExecutionInfoTableName, childExecutionInfoKey) getChildExecutionInfoMapQry = makeGetMapQryTemplate(childExecutionInfoTableName, childExecutionInfoColumns, childExecutionInfoKey) ) // ReplaceIntoChildExecutionInfoMaps replaces one or more rows in child_execution_info_maps table func (mdb *DB) ReplaceIntoChildExecutionInfoMaps(ctx context.Context, rows []sqlplugin.ChildExecutionInfoMapsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, setKeyInChildExecutionInfoMapQry, rows) } // SelectFromChildExecutionInfoMaps reads one or more rows from child_execution_info_maps table func (mdb *DB) SelectFromChildExecutionInfoMaps(ctx context.Context, filter *sqlplugin.ChildExecutionInfoMapsFilter) ([]sqlplugin.ChildExecutionInfoMapsRow, error) { var rows []sqlplugin.ChildExecutionInfoMapsRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getChildExecutionInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID } return rows, err } // DeleteFromChildExecutionInfoMaps deletes one or more rows from child_execution_info_maps table func (mdb *DB) DeleteFromChildExecutionInfoMaps(ctx context.Context, filter *sqlplugin.ChildExecutionInfoMapsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) if len(filter.InitiatedIDs) > 0 { query, args, err := sqlx.In(deleteKeyInChildExecutionInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.InitiatedIDs) if err != nil { return nil, err } return mdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return mdb.driver.ExecContext(ctx, dbShardID, deleteChildExecutionInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } var ( requestCancelInfoColumns = []string{ "data", "data_encoding", } requestCancelInfoTableName = "request_cancel_info_maps" requestCancelInfoKey = "initiated_id" deleteRequestCancelInfoMapQry = makeDeleteMapQry(requestCancelInfoTableName) setKeyInRequestCancelInfoMapQry = makeSetKeyInMapQry(requestCancelInfoTableName, requestCancelInfoColumns, requestCancelInfoKey) deleteKeyInRequestCancelInfoMapQry = makeDeleteKeyInMapQry(requestCancelInfoTableName, requestCancelInfoKey) getRequestCancelInfoMapQry = makeGetMapQryTemplate(requestCancelInfoTableName, requestCancelInfoColumns, requestCancelInfoKey) ) // ReplaceIntoRequestCancelInfoMaps replaces one or more rows in request_cancel_info_maps table func (mdb *DB) ReplaceIntoRequestCancelInfoMaps(ctx context.Context, rows []sqlplugin.RequestCancelInfoMapsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, setKeyInRequestCancelInfoMapQry, rows) } // SelectFromRequestCancelInfoMaps reads one or more rows from request_cancel_info_maps table func (mdb *DB) SelectFromRequestCancelInfoMaps(ctx context.Context, filter *sqlplugin.RequestCancelInfoMapsFilter) ([]sqlplugin.RequestCancelInfoMapsRow, error) { var rows []sqlplugin.RequestCancelInfoMapsRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getRequestCancelInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID } return rows, err } // DeleteFromRequestCancelInfoMaps deletes one or more rows from request_cancel_info_maps table func (mdb *DB) DeleteFromRequestCancelInfoMaps(ctx context.Context, filter *sqlplugin.RequestCancelInfoMapsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) if len(filter.InitiatedIDs) > 0 { query, args, err := sqlx.In(deleteKeyInRequestCancelInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.InitiatedIDs) if err != nil { return nil, err } return mdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return mdb.driver.ExecContext(ctx, dbShardID, deleteRequestCancelInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } var ( signalInfoColumns = []string{ "data", "data_encoding", } signalInfoTableName = "signal_info_maps" signalInfoKey = "initiated_id" deleteSignalInfoMapQry = makeDeleteMapQry(signalInfoTableName) setKeyInSignalInfoMapQry = makeSetKeyInMapQry(signalInfoTableName, signalInfoColumns, signalInfoKey) deleteKeyInSignalInfoMapQry = makeDeleteKeyInMapQry(signalInfoTableName, signalInfoKey) getSignalInfoMapQry = makeGetMapQryTemplate(signalInfoTableName, signalInfoColumns, signalInfoKey) ) // ReplaceIntoSignalInfoMaps replaces one or more rows in signal_info_maps table func (mdb *DB) ReplaceIntoSignalInfoMaps(ctx context.Context, rows []sqlplugin.SignalInfoMapsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, setKeyInSignalInfoMapQry, rows) } // SelectFromSignalInfoMaps reads one or more rows from signal_info_maps table func (mdb *DB) SelectFromSignalInfoMaps(ctx context.Context, filter *sqlplugin.SignalInfoMapsFilter) ([]sqlplugin.SignalInfoMapsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) var rows []sqlplugin.SignalInfoMapsRow err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getSignalInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID } return rows, err } // DeleteFromSignalInfoMaps deletes one or more rows from signal_info_maps table func (mdb *DB) DeleteFromSignalInfoMaps(ctx context.Context, filter *sqlplugin.SignalInfoMapsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) if len(filter.InitiatedIDs) > 0 { query, args, err := sqlx.In(deleteKeyInSignalInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.InitiatedIDs) if err != nil { return nil, err } return mdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return mdb.driver.ExecContext(ctx, dbShardID, deleteSignalInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } const ( deleteAllSignalsRequestedSetQry = `DELETE FROM signals_requested_sets WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ? ` createSignalsRequestedSetQry = `INSERT IGNORE INTO signals_requested_sets (shard_id, domain_id, workflow_id, run_id, signal_id) VALUES (:shard_id, :domain_id, :workflow_id, :run_id, :signal_id)` deleteSignalsRequestedSetQry = `DELETE FROM signals_requested_sets WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ? AND signal_id IN ( ? )` getSignalsRequestedSetQry = `SELECT signal_id FROM signals_requested_sets WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ?` ) // InsertIntoSignalsRequestedSets inserts one or more rows into signals_requested_sets table func (mdb *DB) InsertIntoSignalsRequestedSets(ctx context.Context, rows []sqlplugin.SignalsRequestedSetsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, createSignalsRequestedSetQry, rows) } // SelectFromSignalsRequestedSets reads one or more rows from signals_requested_sets table func (mdb *DB) SelectFromSignalsRequestedSets(ctx context.Context, filter *sqlplugin.SignalsRequestedSetsFilter) ([]sqlplugin.SignalsRequestedSetsRow, error) { var rows []sqlplugin.SignalsRequestedSetsRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, getSignalsRequestedSetQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID } return rows, err } // DeleteFromSignalsRequestedSets deletes one or more rows from signals_requested_sets table func (mdb *DB) DeleteFromSignalsRequestedSets(ctx context.Context, filter *sqlplugin.SignalsRequestedSetsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) if len(filter.SignalIDs) > 0 { query, args, err := sqlx.In(deleteSignalsRequestedSetQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.SignalIDs) if err != nil { return nil, err } return mdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return mdb.driver.ExecContext(ctx, dbShardID, deleteAllSignalsRequestedSetQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/plugin.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "bytes" "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "net" "net/url" "strings" "github.com/go-sql-driver/mysql" "github.com/iancoleman/strcase" "github.com/jmoiron/sqlx" "github.com/uber/cadence/common/config" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/sql" "github.com/uber/cadence/common/persistence/sql/sqldriver" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/environment" ) const ( // PluginName is the name of the plugin PluginName = "mysql" dsnFmt = "%s:%s@%v(%v)/%s" isolationLevelAttrName = "transaction_isolation" isolationLevelAttrNameLegacy = "tx_isolation" defaultIsolationLevel = "'READ-COMMITTED'" // customTLSName is the name used if a custom tls configuration is created customTLSName = "tls-custom" ) var dsnAttrOverrides = map[string]string{ "parseTime": "true", "clientFoundRows": "true", "multiStatements": "true", } type plugin struct{} var _ sqlplugin.Plugin = (*plugin)(nil) func init() { sql.RegisterPlugin(PluginName, &plugin{}) } // CreateDB initialize the DB object func (p *plugin) CreateDB(cfg *config.SQL) (sqlplugin.DB, error) { return p.createDB(cfg) } // CreateAdminDB initialize the adminDb object func (p *plugin) CreateAdminDB(cfg *config.SQL) (sqlplugin.AdminDB, error) { return p.createDB(cfg) } func (p *plugin) createDB(cfg *config.SQL) (*DB, error) { conns, err := sqldriver.CreateDBConnections(cfg, func(cfg *config.SQL) (*sqlx.DB, error) { return p.createSingleDBConn(cfg) }) if err != nil { return nil, err } return NewDB(conns, nil, sqlplugin.DbShardUndefined, cfg.NumShards, newConverter()) } func (p *plugin) createSingleDBConn(cfg *config.SQL) (*sqlx.DB, error) { err := registerTLSConfig(cfg) if err != nil { return nil, err } db, err := sqlx.Connect(PluginName, buildDSN(cfg)) if err != nil { return nil, err } if cfg.MaxConns > 0 { db.SetMaxOpenConns(cfg.MaxConns) } if cfg.MaxIdleConns > 0 { db.SetMaxIdleConns(cfg.MaxIdleConns) } if cfg.MaxConnLifetime > 0 { db.SetConnMaxLifetime(cfg.MaxConnLifetime) } // Maps struct names in CamelCase to snake without need for DB struct tags. db.MapperFunc(strcase.ToSnake) return db, nil } func registerTLSConfig(cfg *config.SQL) error { if cfg.TLS == nil || !cfg.TLS.Enabled { return nil } host, _, err := net.SplitHostPort(cfg.ConnectAddr) if err != nil { return fmt.Errorf("error in host port from ConnectAddr: %v", err) } // TODO: create a way to set MinVersion and CipherSuites via cfg. tlsConfig := &tls.Config{ ServerName: host, InsecureSkipVerify: !cfg.TLS.EnableHostVerification, } if cfg.TLS.CaFile != "" { rootCertPool := x509.NewCertPool() pem, err := ioutil.ReadFile(cfg.TLS.CaFile) if err != nil { return fmt.Errorf("failed to load CA files: %v", err) } if ok := rootCertPool.AppendCertsFromPEM(pem); !ok { return fmt.Errorf("failed to append CA file") } tlsConfig.RootCAs = rootCertPool } if cfg.TLS.CertFile != "" && cfg.TLS.KeyFile != "" { clientCert := make([]tls.Certificate, 0, 1) certs, err := tls.LoadX509KeyPair( cfg.TLS.CertFile, cfg.TLS.KeyFile, ) if err != nil { return fmt.Errorf("failed to load tls x509 key pair: %v", err) } clientCert = append(clientCert, certs) tlsConfig.Certificates = clientCert } // In order to use the TLS configuration you need to register it. Once registered you use it by specifying // `tls` in the connect attributes. err = mysql.RegisterTLSConfig(customTLSName, tlsConfig) if err != nil { return fmt.Errorf("failed to register tls config: %v", err) } if cfg.ConnectAttributes == nil { cfg.ConnectAttributes = map[string]string{} } // If no `tls` connect attribute is provided then we override it to our newly registered tls config automatically. // This allows users to simply provide a tls config without needing to remember to also set the connect attribute if cfg.ConnectAttributes["tls"] == "" { if cfg.TLS.SSLMode != "" { cfg.ConnectAttributes["tls"] = cfg.TLS.SSLMode } else { cfg.ConnectAttributes["tls"] = customTLSName } } return nil } func buildDSN(cfg *config.SQL) string { attrs := buildDSNAttrs(cfg) dsn := fmt.Sprintf(dsnFmt, cfg.User, cfg.Password, cfg.ConnectProtocol, cfg.ConnectAddr, cfg.DatabaseName) if attrs != "" { dsn = dsn + "?" + attrs } return dsn } func buildDSNAttrs(cfg *config.SQL) string { attrs := make(map[string]string, len(dsnAttrOverrides)+len(cfg.ConnectAttributes)+1) for k, v := range cfg.ConnectAttributes { k1, v1 := sanitizeAttr(k, v) attrs[k1] = v1 } // only override isolation level if not specified if !hasAttr(attrs, isolationLevelAttrName) && !hasAttr(attrs, isolationLevelAttrNameLegacy) { attrs[isolationLevelAttrName] = defaultIsolationLevel } // these attrs are always overriden for k, v := range dsnAttrOverrides { attrs[k] = v } first := true var buf bytes.Buffer for k, v := range attrs { if !first { buf.WriteString("&") } first = false buf.WriteString(k) buf.WriteString("=") buf.WriteString(v) } return url.PathEscape(buf.String()) } func hasAttr(attrs map[string]string, key string) bool { _, ok := attrs[key] return ok } func sanitizeAttr(inkey string, invalue string) (string, string) { key := strings.ToLower(strings.TrimSpace(inkey)) value := strings.ToLower(strings.TrimSpace(invalue)) switch key { case isolationLevelAttrName, isolationLevelAttrNameLegacy: if value[0] != '\'' { // mysql sys variable values must be enclosed in single quotes value = "'" + value + "'" } return key, value default: return inkey, invalue } } const ( testSchemaDir = "schema/mysql/v8" ) // GetTestClusterOption return test options func GetTestClusterOption() (*pt.TestBaseOptions, error) { port, err := environment.GetMySQLPort() if err != nil { return nil, err } return &pt.TestBaseOptions{ DBPluginName: PluginName, DBUsername: environment.GetMySQLUser(), DBPassword: environment.GetMySQLPassword(), DBHost: environment.GetMySQLAddress(), DBPort: port, SchemaDir: testSchemaDir, StoreType: config.StoreTypeSQL, }, nil } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/queue.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "database/sql" "encoding/json" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( templateEnqueueMessageQuery = `INSERT INTO queue (queue_type, message_id, message_payload) VALUES(:queue_type, :message_id, :message_payload)` templateGetLastMessageIDQuery = `SELECT message_id FROM queue WHERE queue_type=? ORDER BY message_id DESC LIMIT 1 FOR UPDATE` templateGetMessagesQuery = `SELECT message_id, message_payload FROM queue WHERE queue_type = ? and message_id > ? ORDER BY message_id ASC LIMIT ?` templateGetMessagesBetweenQuery = `SELECT message_id, message_payload FROM queue WHERE queue_type = ? and message_id > ? and message_id <= ? ORDER BY message_id ASC LIMIT ?` templateDeleteMessagesBeforeQuery = `DELETE FROM queue WHERE queue_type = ? and message_id < ?` templateRangeDeleteMessagesQuery = `DELETE FROM queue WHERE queue_type = ? and message_id > ? and message_id <= ?` templateDeleteMessageQuery = `DELETE FROM queue WHERE queue_type = ? and message_id = ?` templateGetQueueMetadataQuery = `SELECT data from queue_metadata WHERE queue_type = ?` templateGetQueueMetadataForUpdateQuery = templateGetQueueMetadataQuery + ` FOR UPDATE` templateInsertQueueMetadataQuery = `INSERT INTO queue_metadata (queue_type, data) VALUES(:queue_type, :data)` templateUpdateQueueMetadataQuery = `UPDATE queue_metadata SET data = ? WHERE queue_type = ?` templateGetQueueSizeQuery = `SELECT COUNT(1) AS count FROM queue WHERE queue_type=?` ) // InsertIntoQueue inserts a new row into queue table func (mdb *DB) InsertIntoQueue( ctx context.Context, row *sqlplugin.QueueRow, ) (sql.Result, error) { return mdb.driver.NamedExecContext(ctx, sqlplugin.DbDefaultShard, templateEnqueueMessageQuery, row) } // GetLastEnqueuedMessageIDForUpdate returns the last enqueued message ID func (mdb *DB) GetLastEnqueuedMessageIDForUpdate( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { var lastMessageID int64 err := mdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &lastMessageID, templateGetLastMessageIDQuery, queueType) return lastMessageID, err } // GetMessagesFromQueue retrieves messages from the queue func (mdb *DB) GetMessagesFromQueue( ctx context.Context, queueType persistence.QueueType, lastMessageID int64, maxRows int, ) ([]sqlplugin.QueueRow, error) { var rows []sqlplugin.QueueRow err := mdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, templateGetMessagesQuery, queueType, lastMessageID, maxRows) return rows, err } // GetMessagesBetween retrieves messages from the queue func (mdb *DB) GetMessagesBetween( ctx context.Context, queueType persistence.QueueType, firstMessageID int64, lastMessageID int64, maxRows int, ) ([]sqlplugin.QueueRow, error) { var rows []sqlplugin.QueueRow err := mdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, templateGetMessagesBetweenQuery, queueType, firstMessageID, lastMessageID, maxRows) return rows, err } // DeleteMessagesBefore deletes messages before messageID from the queue func (mdb *DB) DeleteMessagesBefore( ctx context.Context, queueType persistence.QueueType, messageID int64, ) (sql.Result, error) { return mdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, templateDeleteMessagesBeforeQuery, queueType, messageID) } // RangeDeleteMessages deletes messages before messageID from the queue func (mdb *DB) RangeDeleteMessages( ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, inclusiveEndMessageID int64, ) (sql.Result, error) { return mdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, templateRangeDeleteMessagesQuery, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) } // DeleteMessage deletes message with a messageID from the queue func (mdb *DB) DeleteMessage( ctx context.Context, queueType persistence.QueueType, messageID int64, ) (sql.Result, error) { return mdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, templateDeleteMessageQuery, queueType, messageID) } // InsertAckLevel inserts ack level func (mdb *DB) InsertAckLevel( ctx context.Context, queueType persistence.QueueType, messageID int64, clusterName string, ) error { clusterAckLevels := map[string]int64{clusterName: messageID} data, err := json.Marshal(clusterAckLevels) if err != nil { return err } _, err = mdb.driver.NamedExecContext(ctx, sqlplugin.DbDefaultShard, templateInsertQueueMetadataQuery, sqlplugin.QueueMetadataRow{QueueType: queueType, Data: data}) return err } // UpdateAckLevels updates cluster ack levels func (mdb *DB) UpdateAckLevels( ctx context.Context, queueType persistence.QueueType, clusterAckLevels map[string]int64, ) error { data, err := json.Marshal(clusterAckLevels) if err != nil { return err } _, err = mdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, templateUpdateQueueMetadataQuery, data, queueType) return err } // GetAckLevels returns ack levels for pulling clusters func (mdb *DB) GetAckLevels( ctx context.Context, queueType persistence.QueueType, forUpdate bool, ) (map[string]int64, error) { queryStr := templateGetQueueMetadataQuery if forUpdate { queryStr = templateGetQueueMetadataForUpdateQuery } var data []byte err := mdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &data, queryStr, queueType) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } var clusterAckLevels map[string]int64 if err := json.Unmarshal(data, &clusterAckLevels); err != nil { return nil, err } return clusterAckLevels, nil } // GetQueueSize returns the queue size func (mdb *DB) GetQueueSize( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { var size []int64 if err := mdb.driver.SelectContext( ctx, sqlplugin.DbDefaultShard, &size, templateGetQueueSizeQuery, queueType, ); err != nil { return 0, err } return size[0], nil } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/shard.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( createShardQry = `INSERT INTO shards (shard_id, range_id, data, data_encoding) VALUES (?, ?, ?, ?)` getShardQry = `SELECT shard_id, range_id, data, data_encoding FROM shards WHERE shard_id = ?` updateShardQry = `UPDATE shards SET range_id = ?, data = ?, data_encoding = ? WHERE shard_id = ?` lockShardQry = `SELECT range_id FROM shards WHERE shard_id = ? FOR UPDATE` readLockShardQry = `SELECT range_id FROM shards WHERE shard_id = ? LOCK IN SHARE MODE` ) // InsertIntoShards inserts one or more rows into shards table func (mdb *DB) InsertIntoShards(ctx context.Context, row *sqlplugin.ShardsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, createShardQry, row.ShardID, row.RangeID, row.Data, row.DataEncoding) } // UpdateShards updates one or more rows into shards table func (mdb *DB) UpdateShards(ctx context.Context, row *sqlplugin.ShardsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, updateShardQry, row.RangeID, row.Data, row.DataEncoding, row.ShardID) } // SelectFromShards reads one or more rows from shards table func (mdb *DB) SelectFromShards(ctx context.Context, filter *sqlplugin.ShardsFilter) (*sqlplugin.ShardsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) var row sqlplugin.ShardsRow err := mdb.driver.GetContext(ctx, dbShardID, &row, getShardQry, filter.ShardID) if err != nil { return nil, err } return &row, err } // ReadLockShards acquires a read lock on a single row in shards table func (mdb *DB) ReadLockShards(ctx context.Context, filter *sqlplugin.ShardsFilter) (int, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) var rangeID int err := mdb.driver.GetContext(ctx, dbShardID, &rangeID, readLockShardQry, filter.ShardID) return rangeID, err } // WriteLockShards acquires a write lock on a single row in shards table func (mdb *DB) WriteLockShards(ctx context.Context, filter *sqlplugin.ShardsFilter) (int, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) var rangeID int err := mdb.driver.GetContext(ctx, dbShardID, &rangeID, lockShardQry, filter.ShardID) return rangeID, err } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/task.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "database/sql" "fmt" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( taskListCreatePart = `INTO task_lists(shard_id, domain_id, name, task_type, range_id, data, data_encoding) ` + `VALUES (:shard_id, :domain_id, :name, :task_type, :range_id, :data, :data_encoding)` // (default range ID: initialRangeID == 1) createTaskListQry = `INSERT ` + taskListCreatePart updateTaskListQry = `UPDATE task_lists SET range_id = :range_id, data = :data, data_encoding = :data_encoding WHERE shard_id = :shard_id AND domain_id = :domain_id AND name = :name AND task_type = :task_type ` // This query uses pagination that is best understood by analogy to simple numbers. // Given a list of numbers // 111 // 113 // 122 // 211 // where the hundreds digit corresponds to domain_id, the tens digit // corresponds to name, and the ones digit corresponds to task_type, // Imagine recurring queries with a limit of 1. // For the second query to skip the first result and return 112, it must allow equal values in hundreds & tens, but it's OK because the ones digit is higher. // For the third query, the ones digit is now lower but that's irrelevant because the tens digit is greater. // For the fourth query, both the tens digit and ones digit are now lower but that's again irrelevant because now the hundreds digit is higher. // This technique is useful since the size of the table can easily change between calls, making SKIP an unreliable method, while other db-specific things like rowids are not portable listTaskListQry = `SELECT domain_id, range_id, name, task_type, data, data_encoding ` + `FROM task_lists ` + `WHERE shard_id = ? AND ((domain_id = ? AND name = ? AND task_type > ?) OR (domain_id=? AND name > ?) OR (domain_id > ?)) ` + `ORDER BY domain_id,name,task_type LIMIT ?` getTaskListQry = `SELECT domain_id, range_id, name, task_type, data, data_encoding ` + `FROM task_lists ` + `WHERE shard_id = ? AND domain_id = ? AND name = ? AND task_type = ?` deleteTaskListQry = `DELETE FROM task_lists WHERE shard_id=? AND domain_id=? AND name=? AND task_type=? AND range_id=?` lockTaskListQry = `SELECT range_id FROM task_lists ` + `WHERE shard_id = ? AND domain_id = ? AND name = ? AND task_type = ? FOR UPDATE` getTaskMinMaxQry = `SELECT task_id, data, data_encoding ` + `FROM tasks ` + `WHERE domain_id = ? AND task_list_name = ? AND task_type = ? AND task_id > ? AND task_id <= ? ` + ` ORDER BY task_id LIMIT ?` getTaskMinQry = `SELECT task_id, data, data_encoding ` + `FROM tasks ` + `WHERE domain_id = ? AND task_list_name = ? AND task_type = ? AND task_id > ? ORDER BY task_id LIMIT ?` getTasksCountQry = `SELECT count(1) as count ` + `FROM tasks ` + `WHERE domain_id = ? AND task_list_name = ? AND task_type = ? AND task_id > ?` createTaskQry = `INSERT INTO ` + `tasks(domain_id, task_list_name, task_type, task_id, data, data_encoding) ` + `VALUES(:domain_id, :task_list_name, :task_type, :task_id, :data, :data_encoding)` deleteTaskQry = `DELETE FROM tasks ` + `WHERE domain_id = ? AND task_list_name = ? AND task_type = ? AND task_id = ?` rangeDeleteTaskQry = `DELETE FROM tasks ` + `WHERE domain_id = ? AND task_list_name = ? AND task_type = ? AND task_id <= ? ` + `ORDER BY domain_id,task_list_name,task_type,task_id LIMIT ?` getOrphanTaskQry = `SELECT task_id, domain_id, task_list_name, task_type FROM tasks AS t ` + `WHERE NOT EXISTS ( ` + ` SELECT domain_id, name, task_type FROM task_lists AS tl ` + ` WHERE t.domain_id=tl.domain_id and t.task_list_name=tl.name and t.task_type=tl.task_type ` + `) LIMIT ?;` ) // InsertIntoTasks inserts one or more rows into tasks table func (mdb *DB) InsertIntoTasks(ctx context.Context, rows []sqlplugin.TasksRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } return mdb.driver.NamedExecContext(ctx, rows[0].ShardID, createTaskQry, rows) } // SelectFromTasks reads one or more rows from tasks table func (mdb *DB) SelectFromTasks(ctx context.Context, filter *sqlplugin.TasksFilter) ([]sqlplugin.TasksRow, error) { var err error var rows []sqlplugin.TasksRow switch { case filter.MaxTaskID != nil: err = mdb.driver.SelectContext(ctx, filter.ShardID, &rows, getTaskMinMaxQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.MinTaskID, *filter.MaxTaskID, *filter.PageSize) default: err = mdb.driver.SelectContext(ctx, filter.ShardID, &rows, getTaskMinQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.MinTaskID, *filter.PageSize) } if err != nil { return nil, err } return rows, err } // DeleteFromTasks deletes one or more rows from tasks table func (mdb *DB) DeleteFromTasks(ctx context.Context, filter *sqlplugin.TasksFilter) (sql.Result, error) { if filter.TaskIDLessThanEquals != nil { if filter.Limit == nil || *filter.Limit == 0 { return nil, fmt.Errorf("missing limit parameter") } return mdb.driver.ExecContext(ctx, filter.ShardID, rangeDeleteTaskQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.TaskIDLessThanEquals, *filter.Limit) } return mdb.driver.ExecContext(ctx, filter.ShardID, deleteTaskQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.TaskID) } func (mdb *DB) GetOrphanTasks(ctx context.Context, filter *sqlplugin.OrphanTasksFilter) ([]sqlplugin.TaskKeyRow, error) { if filter.Limit == nil || *filter.Limit == 0 { return nil, fmt.Errorf("missing limit parameter") } var rows []sqlplugin.TaskKeyRow err := mdb.driver.SelectContext(ctx, sqlplugin.DbAllShards, &rows, getOrphanTaskQry, *filter.Limit) if err != nil { return nil, err } return rows, nil } // InsertIntoTaskLists inserts one or more rows into task_lists table func (mdb *DB) InsertIntoTaskLists(ctx context.Context, row *sqlplugin.TaskListsRow) (sql.Result, error) { return mdb.driver.NamedExecContext(ctx, row.ShardID, createTaskListQry, row) } // UpdateTaskLists updates a row in task_lists table func (mdb *DB) UpdateTaskLists(ctx context.Context, row *sqlplugin.TaskListsRow) (sql.Result, error) { return mdb.driver.NamedExecContext(ctx, row.ShardID, updateTaskListQry, row) } // SelectFromTaskLists reads one or more rows from task_lists table func (mdb *DB) SelectFromTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) ([]sqlplugin.TaskListsRow, error) { switch { case filter.DomainID != nil && filter.Name != nil && filter.TaskType != nil: return mdb.selectFromTaskLists(ctx, filter) case filter.DomainIDGreaterThan != nil && filter.NameGreaterThan != nil && filter.TaskTypeGreaterThan != nil && filter.PageSize != nil: return mdb.rangeSelectFromTaskLists(ctx, filter) default: return nil, fmt.Errorf("invalid set of query filter params") } } func (mdb *DB) selectFromTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) ([]sqlplugin.TaskListsRow, error) { var err error var row sqlplugin.TaskListsRow err = mdb.driver.GetContext(ctx, filter.ShardID, &row, getTaskListQry, filter.ShardID, *filter.DomainID, *filter.Name, *filter.TaskType) if err != nil { return nil, err } return []sqlplugin.TaskListsRow{row}, err } func (mdb *DB) rangeSelectFromTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) ([]sqlplugin.TaskListsRow, error) { var err error var rows []sqlplugin.TaskListsRow err = mdb.driver.SelectContext(ctx, filter.ShardID, &rows, listTaskListQry, filter.ShardID, *filter.DomainIDGreaterThan, *filter.NameGreaterThan, *filter.TaskTypeGreaterThan, *filter.DomainIDGreaterThan, *filter.NameGreaterThan, *filter.DomainIDGreaterThan, *filter.PageSize) if err != nil { return nil, err } for i := range rows { rows[i].ShardID = filter.ShardID } return rows, nil } // DeleteFromTaskLists deletes a row from task_lists table func (mdb *DB) DeleteFromTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) (sql.Result, error) { return mdb.driver.ExecContext(ctx, filter.ShardID, deleteTaskListQry, filter.ShardID, *filter.DomainID, *filter.Name, *filter.TaskType, *filter.RangeID) } // LockTaskLists locks a row in task_lists table func (mdb *DB) LockTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) (int64, error) { var rangeID int64 err := mdb.driver.GetContext(ctx, filter.ShardID, &rangeID, lockTaskListQry, filter.ShardID, *filter.DomainID, *filter.Name, *filter.TaskType) return rangeID, err } func (mdb *DB) GetTasksCount(ctx context.Context, filter *sqlplugin.TasksFilter) (int64, error) { var size []int64 if err := mdb.driver.SelectContext(ctx, filter.ShardID, &size, getTasksCountQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.MinTaskID); err != nil { return 0, err } return size[0], nil } // InsertIntoTasksWithTTL is not supported in MySQL func (mdb *DB) InsertIntoTasksWithTTL(_ context.Context, _ []sqlplugin.TasksRowWithTTL) (sql.Result, error) { return nil, sqlplugin.ErrTTLNotSupported } // InsertIntoTaskListsWithTTL is not supported in MySQL func (mdb *DB) InsertIntoTaskListsWithTTL(_ context.Context, _ *sqlplugin.TaskListsRowWithTTL) (sql.Result, error) { return nil, sqlplugin.ErrTTLNotSupported } // UpdateTaskListsWithTTL is not supported in MySQL func (mdb *DB) UpdateTaskListsWithTTL(_ context.Context, _ *sqlplugin.TaskListsRowWithTTL) (sql.Result, error) { return nil, sqlplugin.ErrTTLNotSupported } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/typeconv.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import "time" // DataConverter defines the API for conversions to/from // go types to mysql datatypes type DataConverter interface { ToDateTime(t time.Time) time.Time FromDateTime(t time.Time) time.Time } type converter struct{} func newConverter() *converter { return &converter{} } var minMySQLDateTime = getMinMySQLDateTime() // ToDateTime converts to time to MySQL datetime func (c *converter) ToDateTime(t time.Time) time.Time { if t.IsZero() { return minMySQLDateTime } return t } // FromDateTime converts mysql datetime and returns go time func (c *converter) FromDateTime(t time.Time) time.Time { if t.Equal(minMySQLDateTime) { return time.Time{} } return t } func getMinMySQLDateTime() time.Time { t, err := time.Parse(time.RFC3339, "1000-01-01T00:00:00Z") if err != nil { return time.Unix(0, 0) } return t } ================================================ FILE: common/persistence/sql/sqlplugin/mysql/visibility.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "context" "database/sql" "errors" "fmt" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( templateCreateWorkflowExecutionStarted = `INSERT IGNORE INTO executions_visibility (` + `domain_id, workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time) ` + `VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` templateCreateWorkflowExecutionClosed = `REPLACE INTO executions_visibility (` + `domain_id, workflow_id, run_id, start_time, execution_time, workflow_type_name, close_time, close_status, history_length, memo, encoding, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time) ` + `VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` // RunID condition is needed for correct pagination templateConditions = ` AND domain_id = ? AND start_time >= ? AND start_time <= ? AND (run_id > ? OR start_time < ?) ORDER BY start_time DESC, run_id LIMIT ?` templateOpenFieldNames = `workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, is_cron, update_time, shard_id` templateOpenSelect = `SELECT ` + templateOpenFieldNames + ` FROM executions_visibility WHERE close_status IS NULL ` templateClosedSelect = `SELECT ` + templateOpenFieldNames + `, close_time, close_status, history_length FROM executions_visibility WHERE close_status IS NOT NULL ` templateGetOpenWorkflowExecutions = templateOpenSelect + templateConditions templateGetClosedWorkflowExecutions = templateClosedSelect + templateConditions templateGetOpenWorkflowExecutionsByType = templateOpenSelect + `AND workflow_type_name = ?` + templateConditions templateGetClosedWorkflowExecutionsByType = templateClosedSelect + `AND workflow_type_name = ?` + templateConditions templateGetOpenWorkflowExecutionsByID = templateOpenSelect + `AND workflow_id = ?` + templateConditions templateGetClosedWorkflowExecutionsByID = templateClosedSelect + `AND workflow_id = ?` + templateConditions templateGetClosedWorkflowExecutionsByStatus = templateClosedSelect + `AND close_status = ?` + templateConditions templateGetClosedWorkflowExecution = `SELECT workflow_id, run_id, start_time, execution_time, memo, encoding, close_time, workflow_type_name, close_status, history_length, is_cron, update_time, shard_id FROM executions_visibility WHERE domain_id = ? AND close_status IS NOT NULL AND run_id = ?` templateDeleteWorkflowExecution = "DELETE FROM executions_visibility WHERE domain_id=? AND run_id=?" ) var errCloseParams = errors.New("missing one of {closeStatus, closeTime, historyLength} params") // InsertIntoVisibility inserts a row into visibility table. If an row already exist, // its left as such and no update will be made func (mdb *DB) InsertIntoVisibility(ctx context.Context, row *sqlplugin.VisibilityRow) (sql.Result, error) { row.StartTime = mdb.converter.ToDateTime(row.StartTime) scheduledExecutionTime := mdb.converter.ToDateTime(row.ScheduledExecutionTime) dbShardID := sqlplugin.GetDBShardIDFromDomainID(row.DomainID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, templateCreateWorkflowExecutionStarted, row.DomainID, row.WorkflowID, row.RunID, row.StartTime, row.ExecutionTime, row.WorkflowTypeName, row.Memo, row.Encoding, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID, row.ExecutionStatus, row.CronSchedule, scheduledExecutionTime) } // ReplaceIntoVisibility replaces an existing row if it exist or creates a new row in visibility table func (mdb *DB) ReplaceIntoVisibility(ctx context.Context, row *sqlplugin.VisibilityRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromDomainID(row.DomainID, mdb.GetTotalNumDBShards()) switch { case row.CloseStatus != nil && row.CloseTime != nil && row.HistoryLength != nil: row.StartTime = mdb.converter.ToDateTime(row.StartTime) closeTime := mdb.converter.ToDateTime(*row.CloseTime) scheduledExecutionTime := mdb.converter.ToDateTime(row.ScheduledExecutionTime) return mdb.driver.ExecContext(ctx, dbShardID, templateCreateWorkflowExecutionClosed, row.DomainID, row.WorkflowID, row.RunID, row.StartTime, row.ExecutionTime, row.WorkflowTypeName, closeTime, *row.CloseStatus, *row.HistoryLength, row.Memo, row.Encoding, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID, row.ExecutionStatus, row.CronSchedule, scheduledExecutionTime) default: return nil, errCloseParams } } // DeleteFromVisibility deletes a row from visibility table if it exist func (mdb *DB) DeleteFromVisibility(ctx context.Context, filter *sqlplugin.VisibilityFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromDomainID(filter.DomainID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, templateDeleteWorkflowExecution, filter.DomainID, filter.RunID) } // SelectFromVisibility reads one or more rows from visibility table func (mdb *DB) SelectFromVisibility(ctx context.Context, filter *sqlplugin.VisibilityFilter) ([]sqlplugin.VisibilityRow, error) { dbShardID := sqlplugin.GetDBShardIDFromDomainID(filter.DomainID, mdb.GetTotalNumDBShards()) var err error var rows []sqlplugin.VisibilityRow if filter.MinStartTime != nil { *filter.MinStartTime = mdb.converter.ToDateTime(*filter.MinStartTime) } if filter.MaxStartTime != nil { *filter.MaxStartTime = mdb.converter.ToDateTime(*filter.MaxStartTime) } switch { case filter.MinStartTime == nil && filter.RunID != nil && filter.Closed: var row sqlplugin.VisibilityRow err = mdb.driver.GetContext(ctx, dbShardID, &row, templateGetClosedWorkflowExecution, filter.DomainID, *filter.RunID) if err == nil { rows = append(rows, row) } case filter.MinStartTime != nil && filter.WorkflowID != nil: qry := templateGetOpenWorkflowExecutionsByID if filter.Closed { qry = templateGetClosedWorkflowExecutionsByID } err = mdb.driver.SelectContext(ctx, dbShardID, &rows, qry, *filter.WorkflowID, filter.DomainID, mdb.converter.ToDateTime(*filter.MinStartTime), mdb.converter.ToDateTime(*filter.MaxStartTime), *filter.RunID, *filter.MinStartTime, *filter.PageSize) case filter.MinStartTime != nil && filter.WorkflowTypeName != nil: qry := templateGetOpenWorkflowExecutionsByType if filter.Closed { qry = templateGetClosedWorkflowExecutionsByType } err = mdb.driver.SelectContext(ctx, dbShardID, &rows, qry, *filter.WorkflowTypeName, filter.DomainID, mdb.converter.ToDateTime(*filter.MinStartTime), mdb.converter.ToDateTime(*filter.MaxStartTime), *filter.RunID, *filter.MaxStartTime, *filter.PageSize) case filter.MinStartTime != nil && filter.CloseStatus != nil: err = mdb.driver.SelectContext(ctx, dbShardID, &rows, templateGetClosedWorkflowExecutionsByStatus, *filter.CloseStatus, filter.DomainID, mdb.converter.ToDateTime(*filter.MinStartTime), mdb.converter.ToDateTime(*filter.MaxStartTime), *filter.RunID, mdb.converter.ToDateTime(*filter.MaxStartTime), *filter.PageSize) case filter.MinStartTime != nil: qry := templateGetOpenWorkflowExecutions if filter.Closed { qry = templateGetClosedWorkflowExecutions } err = mdb.driver.SelectContext(ctx, dbShardID, &rows, qry, filter.DomainID, mdb.converter.ToDateTime(*filter.MinStartTime), mdb.converter.ToDateTime(*filter.MaxStartTime), *filter.RunID, mdb.converter.ToDateTime(*filter.MaxStartTime), *filter.PageSize) default: return nil, fmt.Errorf("invalid query filter") } if err != nil { return nil, err } for i := range rows { rows[i].StartTime = mdb.converter.FromDateTime(rows[i].StartTime) rows[i].ExecutionTime = mdb.converter.FromDateTime(rows[i].ExecutionTime) if rows[i].CloseTime != nil { closeTime := mdb.converter.FromDateTime(*rows[i].CloseTime) rows[i].CloseTime = &closeTime } } return rows, err } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/admin.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "fmt" "time" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( readSchemaVersionQuery = `SELECT curr_version from schema_version where db_name=$1` writeSchemaVersionQuery = `INSERT into schema_version(db_name, creation_time, curr_version, min_compatible_version) VALUES ($1,$2,$3,$4) ON CONFLICT (db_name) DO UPDATE SET creation_time = excluded.creation_time, curr_version = excluded.curr_version, min_compatible_version = excluded.min_compatible_version;` writeSchemaUpdateHistoryQuery = `INSERT into schema_update_history(year, month, update_time, old_version, new_version, manifest_md5, description) VALUES($1,$2,$3,$4,$5,$6,$7)` createSchemaVersionTableQuery = `CREATE TABLE schema_version(db_name VARCHAR(255) not null PRIMARY KEY, ` + `creation_time TIMESTAMP, ` + `curr_version VARCHAR(64), ` + `min_compatible_version VARCHAR(64));` createSchemaUpdateHistoryTableQuery = `CREATE TABLE schema_update_history(` + `year int not null, ` + `month int not null, ` + `update_time TIMESTAMP not null, ` + `description VARCHAR(255), ` + `manifest_md5 VARCHAR(64), ` + `new_version VARCHAR(64), ` + `old_version VARCHAR(64), ` + `PRIMARY KEY (year, month, update_time));` // NOTE we have to use %v because somehow postgres doesn't work with ? here // It's a small bug in sqlx library // TODO https://github.com/uber/cadence/issues/2893 createDatabaseQuery = "CREATE database %v" dropDatabaseQuery = "Drop database %v" listTablesQuery = "select table_name from information_schema.tables where table_schema='public'" dropTableQuery = "DROP TABLE %v" ) // CreateSchemaVersionTables sets up the schema version tables func (pdb *db) CreateSchemaVersionTables() error { if err := pdb.ExecSchemaOperationQuery(context.Background(), createSchemaVersionTableQuery); err != nil { return err } return pdb.ExecSchemaOperationQuery(context.Background(), createSchemaUpdateHistoryTableQuery) } // ReadSchemaVersion returns the current schema version for the keyspace func (pdb *db) ReadSchemaVersion(database string) (string, error) { var version string err := pdb.driver.GetForSchemaQuery(sqlplugin.DbShardUndefined, &version, readSchemaVersionQuery, database) return version, err } // UpdateSchemaVersion updates the schema version for the keyspace func (pdb *db) UpdateSchemaVersion(database string, newVersion string, minCompatibleVersion string) error { return pdb.ExecSchemaOperationQuery(context.Background(), writeSchemaVersionQuery, database, time.Now(), newVersion, minCompatibleVersion) } // WriteSchemaUpdateLog adds an entry to the schema update history table func (pdb *db) WriteSchemaUpdateLog(oldVersion string, newVersion string, manifestMD5 string, desc string) error { now := time.Now().UTC() return pdb.ExecSchemaOperationQuery(context.Background(), writeSchemaUpdateHistoryQuery, now.Year(), int(now.Month()), now, oldVersion, newVersion, manifestMD5, desc) } // ExecSchemaOperationQuery executes a sql statement for schema ONLY. DO NOT use it in other cases, otherwise it will not work for multiple SQL database. // For Sharded SQL, it will execute the statement for all shards func (pdb *db) ExecSchemaOperationQuery(ctx context.Context, stmt string, args ...interface{}) error { _, err := pdb.driver.ExecDDL(ctx, sqlplugin.DbShardUndefined, stmt, args...) return err } // ListTables returns a list of tables in this database func (pdb *db) ListTables(database string) ([]string, error) { var tables []string err := pdb.driver.SelectForSchemaQuery(sqlplugin.DbShardUndefined, &tables, listTablesQuery) return tables, err } // DropTable drops a given table from the database func (pdb *db) DropTable(name string) error { return pdb.ExecSchemaOperationQuery(context.Background(), fmt.Sprintf(dropTableQuery, name)) } // DropAllTables drops all tables from this database func (pdb *db) DropAllTables(database string) error { tables, err := pdb.ListTables(database) if err != nil { return err } for _, tab := range tables { if err := pdb.DropTable(tab); err != nil { return err } } return nil } // CreateDatabase creates a database if it doesn't exist func (pdb *db) CreateDatabase(name string) error { return pdb.ExecSchemaOperationQuery(context.Background(), fmt.Sprintf(createDatabaseQuery, name)) } // DropDatabase drops a database func (pdb *db) DropDatabase(name string) error { return pdb.ExecSchemaOperationQuery(context.Background(), fmt.Sprintf(dropDatabaseQuery, name)) } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/configstore.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package postgres import ( "context" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( _selectLatestConfigQuery = "SELECT row_type, version, timestamp, data, data_encoding FROM cluster_config WHERE row_type = $1 ORDER BY version LIMIT 1;" _insertConfigQuery = "INSERT INTO cluster_config (row_type, version, timestamp, data, data_encoding) VALUES($1, $2, $3, $4, $5)" ) func (pdb *db) InsertConfig(ctx context.Context, row *persistence.InternalConfigStoreEntry) error { _, err := pdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, _insertConfigQuery, row.RowType, -1*row.Version, pdb.converter.ToPostgresDateTime(row.Timestamp), row.Values.Data, row.Values.Encoding) return err } func (pdb *db) SelectLatestConfig(ctx context.Context, rowType int) (*persistence.InternalConfigStoreEntry, error) { var row sqlplugin.ClusterConfigRow err := pdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row, _selectLatestConfigQuery, rowType) if err != nil { return nil, err } row.Version *= -1 return &persistence.InternalConfigStoreEntry{ RowType: row.RowType, Version: row.Version, Timestamp: pdb.converter.FromPostgresDateTime(row.Timestamp), Values: &persistence.DataBlob{ Data: row.Data, Encoding: constants.EncodingType(row.DataEncoding), }, }, nil } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/db.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "database/sql" "time" "github.com/jmoiron/sqlx" "github.com/lib/pq" "github.com/uber/cadence/common/persistence/sql/sqldriver" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) type ( db struct { converter DataConverter driver sqldriver.Driver originalDBs []*sqlx.DB numDBShards int } ) func (pdb *db) GetTotalNumDBShards() int { return pdb.numDBShards } var _ sqlplugin.DB = (*db)(nil) var _ sqlplugin.Tx = (*db)(nil) // ErrDupEntry indicates a duplicate primary key i.e. the row already exists, // check http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html const ErrDupEntry = "23505" const ErrInsufficientResources = "53000" const ErrTooManyConnections = "53300" func (pdb *db) IsDupEntryError(err error) bool { sqlErr, ok := err.(*pq.Error) return ok && sqlErr.Code == ErrDupEntry } func (pdb *db) IsNotFoundError(err error) bool { return err == sql.ErrNoRows } func (pdb *db) IsTimeoutError(err error) bool { return err == context.DeadlineExceeded } func (pdb *db) IsThrottlingError(err error) bool { sqlErr, ok := err.(*pq.Error) if ok { if sqlErr.Code == ErrTooManyConnections || sqlErr.Code == ErrInsufficientResources { return true } } return false } // newDB returns an instance of DB, which is a logical // connection to the underlying postgres database // dbShardID is needed when tx is not nil func newDB(xdbs []*sqlx.DB, tx *sqlx.Tx, dbShardID int, numDBShards int) (*db, error) { driver, err := sqldriver.NewDriver(xdbs, tx, dbShardID) if err != nil { return nil, err } db := &db{ converter: &converter{}, originalDBs: xdbs, // this is kept because newDB will be called again when starting a transaction driver: driver, numDBShards: numDBShards, } return db, nil } // BeginTx starts a new transaction and returns a reference to the Tx object func (pdb *db) BeginTx(ctx context.Context, dbShardID int) (sqlplugin.Tx, error) { xtx, err := pdb.driver.BeginTxx(ctx, dbShardID, nil) if err != nil { return nil, err } return newDB(pdb.originalDBs, xtx, dbShardID, pdb.numDBShards) } // Commit commits a previously started transaction func (pdb *db) Commit() error { return pdb.driver.Commit() } // Rollback triggers rollback of a previously started transaction func (pdb *db) Rollback() error { return pdb.driver.Rollback() } // Close closes the connection to the mysql db func (pdb *db) Close() error { return pdb.driver.Close() } // PluginName returns the name of the mysql plugin func (pdb *db) PluginName() string { return PluginName } // SupportsTTL returns weather Postgres supports TTL func (pdb *db) SupportsTTL() bool { return false } // MaxAllowedTTL returns the max allowed ttl Postgres supports func (pdb *db) MaxAllowedTTL() (*time.Duration, error) { return nil, sqlplugin.ErrTTLNotSupported } // SupportsTTL returns weather Postgre supports Asynchronous transaction func (pdb *db) SupportsAsyncTransaction() bool { return false } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/domain.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "database/sql" "errors" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( createDomainQuery = `INSERT INTO domains (id, name, is_global, data, data_encoding) VALUES($1, $2, $3, $4, $5)` updateDomainQuery = `UPDATE domains SET name = $1, data = $2, data_encoding = $3 WHERE shard_id=54321 AND id = $4` getDomainPart = `SELECT id, name, is_global, data, data_encoding FROM domains` getDomainByIDQuery = getDomainPart + ` WHERE shard_id=$1 AND id = $2` getDomainByNameQuery = getDomainPart + ` WHERE shard_id=$1 AND name = $2` listDomainsQuery = getDomainPart + ` WHERE shard_id=$1 ORDER BY id LIMIT $2` listDomainsRangeQuery = getDomainPart + ` WHERE shard_id=$1 AND id > $2 ORDER BY id LIMIT $3` deleteDomainByIDQuery = `DELETE FROM domains WHERE shard_id=$1 AND id = $2` deleteDomainByNameQuery = `DELETE FROM domains WHERE shard_id=$1 AND name = $2` getDomainMetadataQuery = `SELECT notification_version FROM domain_metadata` lockDomainMetadataQuery = `SELECT notification_version FROM domain_metadata FOR UPDATE` updateDomainMetadataQuery = `UPDATE domain_metadata SET notification_version = $1 WHERE notification_version = $2` ) const ( shardID = 54321 ) var errMissingArgs = errors.New("missing one or more args for API") // InsertIntoDomain inserts a single row into domains table func (pdb *db) InsertIntoDomain(ctx context.Context, row *sqlplugin.DomainRow) (sql.Result, error) { return pdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, createDomainQuery, row.ID, row.Name, row.IsGlobal, row.Data, row.DataEncoding) } // UpdateDomain updates a single row in domains table func (pdb *db) UpdateDomain(ctx context.Context, row *sqlplugin.DomainRow) (sql.Result, error) { return pdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, updateDomainQuery, row.Name, row.Data, row.DataEncoding, row.ID) } // SelectFromDomain reads one or more rows from domains table func (pdb *db) SelectFromDomain(ctx context.Context, filter *sqlplugin.DomainFilter) ([]sqlplugin.DomainRow, error) { switch { case filter.ID != nil || filter.Name != nil: return pdb.selectFromDomain(ctx, filter) case filter.PageSize != nil && *filter.PageSize > 0: return pdb.selectAllFromDomain(ctx, filter) default: return nil, errMissingArgs } } func (pdb *db) selectFromDomain(ctx context.Context, filter *sqlplugin.DomainFilter) ([]sqlplugin.DomainRow, error) { var err error var row sqlplugin.DomainRow switch { case filter.ID != nil: err = pdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row, getDomainByIDQuery, shardID, *filter.ID) case filter.Name != nil: err = pdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row, getDomainByNameQuery, shardID, *filter.Name) } if err != nil { return nil, err } return []sqlplugin.DomainRow{row}, err } func (pdb *db) selectAllFromDomain(ctx context.Context, filter *sqlplugin.DomainFilter) ([]sqlplugin.DomainRow, error) { var err error var rows []sqlplugin.DomainRow switch { case filter.GreaterThanID != nil: err = pdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, listDomainsRangeQuery, shardID, *filter.GreaterThanID, *filter.PageSize) default: err = pdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, listDomainsQuery, shardID, filter.PageSize) } return rows, err } // DeleteFromDomain deletes a single row in domains table func (pdb *db) DeleteFromDomain(ctx context.Context, filter *sqlplugin.DomainFilter) (sql.Result, error) { var err error var result sql.Result switch { case filter.ID != nil: result, err = pdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, deleteDomainByIDQuery, shardID, filter.ID) default: result, err = pdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, deleteDomainByNameQuery, shardID, filter.Name) } return result, err } // LockDomainMetadata acquires a write lock on a single row in domain_metadata table func (pdb *db) LockDomainMetadata(ctx context.Context) error { var row sqlplugin.DomainMetadataRow err := pdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row.NotificationVersion, lockDomainMetadataQuery) return err } // SelectFromDomainMetadata reads a single row in domain_metadata table func (pdb *db) SelectFromDomainMetadata(ctx context.Context) (*sqlplugin.DomainMetadataRow, error) { var row sqlplugin.DomainMetadataRow err := pdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row.NotificationVersion, getDomainMetadataQuery) return &row, err } // UpdateDomainMetadata updates a single row in domain_metadata table func (pdb *db) UpdateDomainMetadata(ctx context.Context, row *sqlplugin.DomainMetadataRow) (sql.Result, error) { return pdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, updateDomainMetadataQuery, row.NotificationVersion+1, row.NotificationVersion) } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/domain_audit_log.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( _insertDomainAuditLogQuery = `INSERT INTO domain_audit_log ( domain_id, event_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)` _selectDomainAuditLogsQuery = `SELECT event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment FROM domain_audit_log WHERE domain_id = $1 AND operation_type = $2 AND created_time >= $3 AND (created_time < $4 OR (created_time = $4 AND event_id > $5)) ORDER BY created_time DESC, event_id ASC LIMIT $6` _selectAllDomainAuditLogsQuery = `SELECT event_id, domain_id, state_before, state_before_encoding, state_after, state_after_encoding, operation_type, created_time, last_updated_time, identity, identity_type, comment FROM domain_audit_log WHERE domain_id = $1 AND operation_type = $2 AND created_time >= $3 AND (created_time < $4 OR (created_time = $4 AND event_id > $5)) ORDER BY created_time DESC, event_id ASC` ) // InsertIntoDomainAuditLog inserts a single row into domain_audit_log table func (pdb *db) InsertIntoDomainAuditLog(ctx context.Context, row *sqlplugin.DomainAuditLogRow) (sql.Result, error) { return pdb.driver.ExecContext( ctx, sqlplugin.DbDefaultShard, _insertDomainAuditLogQuery, row.DomainID, row.EventID, row.StateBefore, row.StateBeforeEncoding, row.StateAfter, row.StateAfterEncoding, row.OperationType, row.CreatedTime, row.LastUpdatedTime, row.Identity, row.IdentityType, row.Comment, ) } // SelectFromDomainAuditLogs returns audit log entries for a domain, operation type, and time range func (pdb *db) SelectFromDomainAuditLogs( ctx context.Context, filter *sqlplugin.DomainAuditLogFilter, ) ([]*sqlplugin.DomainAuditLogRow, error) { args := []interface{}{ filter.DomainID, filter.OperationType, *filter.MinCreatedTime, *filter.PageMaxCreatedTime, *filter.PageMinEventID, } var rows []*sqlplugin.DomainAuditLogRow if filter.PageSize > 0 { args = append(args, filter.PageSize) err := pdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, _selectDomainAuditLogsQuery, args...) if err != nil { return nil, err } } else { err := pdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, _selectAllDomainAuditLogsQuery, args...) if err != nil { return nil, err } } return rows, nil } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/domain_audit_log_test.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "errors" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqldriver" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) func TestInsertIntoDomainAuditLog(t *testing.T) { now := time.Now().UTC() tests := []struct { name string row *sqlplugin.DomainAuditLogRow mockSetup func(*sqldriver.MockDriver) wantErr bool }{ { name: "successfully inserted", row: &sqlplugin.DomainAuditLogRow{ DomainID: uuid.New(), EventID: uuid.New(), StateBefore: []byte("state-before"), StateBeforeEncoding: constants.EncodingTypeJSON, StateAfter: []byte("state-after"), StateAfterEncoding: constants.EncodingTypeJSON, OperationType: persistence.DomainAuditOperationTypeFailover, CreatedTime: now, LastUpdatedTime: now, Identity: "test-identity", IdentityType: "user", Comment: "test comment", }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().ExecContext( gomock.Any(), sqlplugin.DbDefaultShard, _insertDomainAuditLogQuery, gomock.Any(), gomock.Any(), []byte("state-before"), constants.EncodingTypeJSON, []byte("state-after"), constants.EncodingTypeJSON, persistence.DomainAuditOperationTypeFailover, now, now, "test-identity", "user", "test comment", ).Return(nil, nil) }, wantErr: false, }, { name: "exec failed", row: &sqlplugin.DomainAuditLogRow{ DomainID: uuid.New(), EventID: uuid.New(), OperationType: persistence.DomainAuditOperationTypeFailover, CreatedTime: now, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().ExecContext( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(nil, errors.New("exec failed")) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDriver := sqldriver.NewMockDriver(ctrl) tc.mockSetup(mockDriver) pdb := &db{ driver: mockDriver, converter: &converter{}, } _, err := pdb.InsertIntoDomainAuditLog(context.Background(), tc.row) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestSelectFromDomainAuditLogs(t *testing.T) { domainID := "d1111111-1111-1111-1111-111111111111" operationType := persistence.DomainAuditOperationTypeFailover minTime := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) maxTime := time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC) // Create times in descending order createdTime1 := time.Date(2024, 7, 1, 12, 0, 0, 0, time.UTC) createdTime2 := time.Date(2024, 6, 1, 12, 0, 0, 0, time.UTC) createdTime3 := time.Date(2024, 5, 1, 12, 0, 0, 0, time.UTC) createdTime4 := time.Date(2024, 4, 1, 12, 0, 0, 0, time.UTC) eventID1 := "e1111111-1111-1111-1111-111111111111" eventID2 := "e2222222-2222-2222-2222-222222222222" eventID3 := "e3333333-3333-3333-3333-333333333333" eventID4 := "e4444444-4444-4444-4444-444444444444" // Default page cursor: maxCreatedTime with max UUID (no page token) defaultPageMaxCreatedTime := maxTime defaultPageMinEventID := "ffffffff-ffff-ffff-ffff-ffffffffffff" tests := []struct { name string filter *sqlplugin.DomainAuditLogFilter mockSetup func(*sqldriver.MockDriver) wantRows []*sqlplugin.DomainAuditLogRow wantErr bool }{ { name: "pageSize limits number of results; no pageToken", filter: &sqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, PageSize: 2, PageMaxCreatedTime: &defaultPageMaxCreatedTime, PageMinEventID: &defaultPageMinEventID, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().SelectContext( gomock.Any(), sqlplugin.DbDefaultShard, gomock.Any(), _selectDomainAuditLogsQuery, domainID, operationType, minTime, defaultPageMaxCreatedTime, defaultPageMinEventID, 2, ).DoAndReturn(func(ctx context.Context, shardID int, dest interface{}, query string, args ...interface{}) error { rows := dest.(*[]*sqlplugin.DomainAuditLogRow) *rows = []*sqlplugin.DomainAuditLogRow{ { EventID: eventID1, DomainID: domainID, CreatedTime: createdTime1, OperationType: operationType, }, { EventID: eventID2, DomainID: domainID, CreatedTime: createdTime2, OperationType: operationType, }, } return nil }) }, wantRows: []*sqlplugin.DomainAuditLogRow{ { EventID: eventID1, DomainID: domainID, CreatedTime: createdTime1, OperationType: operationType, }, { EventID: eventID2, DomainID: domainID, CreatedTime: createdTime2, OperationType: operationType, }, }, wantErr: false, }, { name: "pageToken filters results by createdTime and eventID; pageSize is 0", filter: &sqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, PageSize: 0, PageMaxCreatedTime: &createdTime2, PageMinEventID: &eventID2, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().SelectContext( gomock.Any(), sqlplugin.DbDefaultShard, gomock.Any(), _selectAllDomainAuditLogsQuery, domainID, operationType, minTime, createdTime2, eventID2, ).DoAndReturn(func(ctx context.Context, shardID int, dest interface{}, query string, args ...interface{}) error { rows := dest.(*[]*sqlplugin.DomainAuditLogRow) *rows = []*sqlplugin.DomainAuditLogRow{ { EventID: eventID3, DomainID: domainID, CreatedTime: createdTime3, OperationType: operationType, }, { EventID: eventID4, DomainID: domainID, CreatedTime: createdTime4, OperationType: operationType, }, } return nil }) }, wantRows: []*sqlplugin.DomainAuditLogRow{ { EventID: eventID3, DomainID: domainID, CreatedTime: createdTime3, OperationType: operationType, }, { EventID: eventID4, DomainID: domainID, CreatedTime: createdTime4, OperationType: operationType, }, }, wantErr: false, }, { name: "success with no results", filter: &sqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, PageMaxCreatedTime: &defaultPageMaxCreatedTime, PageMinEventID: &defaultPageMinEventID, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().SelectContext( gomock.Any(), sqlplugin.DbDefaultShard, gomock.Any(), _selectAllDomainAuditLogsQuery, domainID, operationType, minTime, defaultPageMaxCreatedTime, defaultPageMinEventID, ).DoAndReturn(func(ctx context.Context, shardID int, dest interface{}, query string, args ...interface{}) error { return nil }) }, wantRows: []*sqlplugin.DomainAuditLogRow{}, wantErr: false, }, { name: "error when select fails", filter: &sqlplugin.DomainAuditLogFilter{ DomainID: domainID, OperationType: operationType, MinCreatedTime: &minTime, PageMaxCreatedTime: &defaultPageMaxCreatedTime, PageMinEventID: &defaultPageMinEventID, }, mockSetup: func(mockDriver *sqldriver.MockDriver) { mockDriver.EXPECT().SelectContext( gomock.Any(), sqlplugin.DbDefaultShard, gomock.Any(), _selectAllDomainAuditLogsQuery, domainID, operationType, minTime, defaultPageMaxCreatedTime, defaultPageMinEventID, ).Return(errors.New("select failed")) }, wantRows: nil, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDriver := sqldriver.NewMockDriver(ctrl) tc.mockSetup(mockDriver) pdb := &db{ driver: mockDriver, converter: &converter{}, } rows, err := pdb.SelectFromDomainAuditLogs(context.Background(), tc.filter) if tc.wantErr { assert.Error(t, err) return } assert.NoError(t, err) assert.Equal(t, len(tc.wantRows), len(rows), "number of rows should match") for i, wantRow := range tc.wantRows { if i < len(rows) { assert.Equal(t, wantRow.EventID, rows[i].EventID, "row %d eventID", i) assert.Equal(t, wantRow.DomainID, rows[i].DomainID, "row %d domainID", i) assert.Equal(t, wantRow.OperationType, rows[i].OperationType, "row %d operationType", i) assert.Equal(t, wantRow.CreatedTime.Unix(), rows[i].CreatedTime.Unix(), "row %d createdTime", i) } } }) } } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/events.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( // below are templates for history_node table addHistoryNodesQuery = `INSERT INTO history_node (` + `shard_id, tree_id, branch_id, node_id, txn_id, data, data_encoding) ` + `VALUES (:shard_id, :tree_id, :branch_id, :node_id, :txn_id, :data, :data_encoding) ` getHistoryNodesQuery = `SELECT node_id, txn_id, data, data_encoding FROM history_node ` + `WHERE shard_id = $1 AND tree_id = $2 AND branch_id = $3 AND node_id >= $4 and node_id < $5 ORDER BY shard_id, tree_id, branch_id, node_id, txn_id LIMIT $6 ` deleteHistoryNodesQuery = `DELETE FROM history_node WHERE shard_id = $1 AND tree_id = $2 AND branch_id = $3 AND node_id >= $4` deleteHistoryNodesByBatchQuery = `DELETE FROM history_node WHERE shard_id = $1 AND tree_id = $2 AND branch_id = $3 AND (node_id,txn_id) IN (SELECT node_id,txn_id FROM history_node WHERE shard_id = $1 AND tree_id = $2 AND branch_id = $3 AND node_id >= $4 LIMIT $5)` // below are templates for history_tree table addHistoryTreeQuery = `INSERT INTO history_tree (` + `shard_id, tree_id, branch_id, data, data_encoding) ` + `VALUES (:shard_id, :tree_id, :branch_id, :data, :data_encoding) ` getHistoryTreeQuery = `SELECT branch_id, data, data_encoding FROM history_tree WHERE shard_id = $1 AND tree_id = $2 ` deleteHistoryTreeQuery = `DELETE FROM history_tree WHERE shard_id = $1 AND tree_id = $2 AND branch_id = $3 ` getAllHistoryTreeQuery = `SELECT shard_id, tree_id, branch_id, data, data_encoding FROM history_tree WHERE (shard_id = $1 AND tree_id = $2 AND branch_id > $3) OR (shard_id = $1 AND tree_id > $2) OR (shard_id > $1) ORDER BY shard_id, tree_id, branch_id LIMIT $4` ) // For history_node table: // InsertIntoHistoryNode inserts a row into history_node table func (pdb *db) InsertIntoHistoryNode(ctx context.Context, row *sqlplugin.HistoryNodeRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(row.TreeID, pdb.GetTotalNumDBShards()) // NOTE: Query 5.6 doesn't support clustering order, to workaround, we let txn_id multiple by -1 *row.TxnID *= -1 return pdb.driver.NamedExecContext(ctx, dbShardID, addHistoryNodesQuery, row) } // SelectFromHistoryNode reads one or more rows from history_node table func (pdb *db) SelectFromHistoryNode(ctx context.Context, filter *sqlplugin.HistoryNodeFilter) ([]sqlplugin.HistoryNodeRow, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, pdb.GetTotalNumDBShards()) var rows []sqlplugin.HistoryNodeRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getHistoryNodesQuery, filter.ShardID, filter.TreeID, filter.BranchID, *filter.MinNodeID, *filter.MaxNodeID, filter.PageSize) // NOTE: since we let txn_id multiple by -1 when inserting, we have to revert it back here for _, row := range rows { *row.TxnID *= -1 } return rows, err } // DeleteFromHistoryNode deletes one or more rows from history_node table func (pdb *db) DeleteFromHistoryNode(ctx context.Context, filter *sqlplugin.HistoryNodeFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, pdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return pdb.driver.ExecContext(ctx, dbShardID, deleteHistoryNodesByBatchQuery, filter.ShardID, filter.TreeID, filter.BranchID, *filter.MinNodeID, filter.PageSize) } return pdb.driver.ExecContext(ctx, dbShardID, deleteHistoryNodesQuery, filter.ShardID, filter.TreeID, filter.BranchID, *filter.MinNodeID) } // For history_tree table: // InsertIntoHistoryTree inserts a row into history_tree table func (pdb *db) InsertIntoHistoryTree(ctx context.Context, row *sqlplugin.HistoryTreeRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(row.TreeID, pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, addHistoryTreeQuery, row) } // SelectFromHistoryTree reads one or more rows from history_tree table func (pdb *db) SelectFromHistoryTree(ctx context.Context, filter *sqlplugin.HistoryTreeFilter) ([]sqlplugin.HistoryTreeRow, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, pdb.GetTotalNumDBShards()) var rows []sqlplugin.HistoryTreeRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getHistoryTreeQuery, filter.ShardID, filter.TreeID) return rows, err } // DeleteFromHistoryTree deletes one or more rows from history_tree table func (pdb *db) DeleteFromHistoryTree(ctx context.Context, filter *sqlplugin.HistoryTreeFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext(ctx, dbShardID, deleteHistoryTreeQuery, filter.ShardID, filter.TreeID, *filter.BranchID) } func (pdb *db) GetAllHistoryTreeBranches(ctx context.Context, filter *sqlplugin.HistoryTreeFilter) ([]sqlplugin.HistoryTreeRow, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, pdb.GetTotalNumDBShards()) var rows []sqlplugin.HistoryTreeRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getAllHistoryTreeQuery, filter.ShardID, filter.TreeID, filter.BranchID, filter.PageSize) return rows, err } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/execution.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( executionsColumns = `shard_id, domain_id, workflow_id, run_id, next_event_id, last_write_version, data, data_encoding` createExecutionQuery = `INSERT INTO executions(` + executionsColumns + `) VALUES(:shard_id, :domain_id, :workflow_id, :run_id, :next_event_id, :last_write_version, :data, :data_encoding)` updateExecutionQuery = `UPDATE executions SET next_event_id = :next_event_id, last_write_version = :last_write_version, data = :data, data_encoding = :data_encoding WHERE shard_id = :shard_id AND domain_id = :domain_id AND workflow_id = :workflow_id AND run_id = :run_id` getExecutionQuery = `SELECT ` + executionsColumns + ` FROM executions WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3 AND run_id = $4` listExecutionQuery = `SELECT ` + executionsColumns + ` FROM executions WHERE shard_id = $1 AND workflow_id > $2 ORDER BY workflow_id LIMIT $3` deleteExecutionQuery = `DELETE FROM executions WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3 AND run_id = $4` lockExecutionQueryBase = `SELECT next_event_id FROM executions WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3 AND run_id = $4` writeLockExecutionQuery = lockExecutionQueryBase + ` FOR UPDATE` readLockExecutionQuery = lockExecutionQueryBase + ` FOR SHARE` createCurrentExecutionQuery = `INSERT INTO current_executions (shard_id, domain_id, workflow_id, run_id, create_request_id, state, close_status, start_version, last_write_version) VALUES (:shard_id, :domain_id, :workflow_id, :run_id, :create_request_id, :state, :close_status, :start_version, :last_write_version)` deleteCurrentExecutionQuery = "DELETE FROM current_executions WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3 AND run_id = $4" getCurrentExecutionQuery = `SELECT shard_id, domain_id, workflow_id, run_id, create_request_id, state, close_status, start_version, last_write_version FROM current_executions WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3` lockCurrentExecutionJoinExecutionsQuery = `SELECT ce.shard_id, ce.domain_id, ce.workflow_id, ce.run_id, ce.create_request_id, ce.state, ce.close_status, ce.start_version, e.last_write_version FROM current_executions ce INNER JOIN executions e ON e.shard_id = ce.shard_id AND e.domain_id = ce.domain_id AND e.workflow_id = ce.workflow_id AND e.run_id = ce.run_id WHERE ce.shard_id = $1 AND ce.domain_id = $2 AND ce.workflow_id = $3 FOR UPDATE` lockCurrentExecutionQuery = getCurrentExecutionQuery + ` FOR UPDATE` updateCurrentExecutionsQuery = `UPDATE current_executions SET run_id = :run_id, create_request_id = :create_request_id, state = :state, close_status = :close_status, start_version = :start_version, last_write_version = :last_write_version WHERE shard_id = :shard_id AND domain_id = :domain_id AND workflow_id = :workflow_id ` getTransferTasksQuery = `SELECT task_id, data, data_encoding FROM transfer_tasks WHERE shard_id = $1 AND task_id >= $2 AND task_id < $3 ORDER BY shard_id, task_id LIMIT $4` createTransferTasksQuery = `INSERT INTO transfer_tasks(shard_id, task_id, data, data_encoding) VALUES(:shard_id, :task_id, :data, :data_encoding)` deleteTransferTaskQuery = `DELETE FROM transfer_tasks WHERE shard_id = $1 AND task_id = $2` rangeDeleteTransferTaskQuery = `DELETE FROM transfer_tasks WHERE shard_id = $1 AND task_id > $2 AND task_id <= $3` rangeDeleteTransferTaskByBatchQuery = `DELETE FROM transfer_tasks WHERE shard_id = $1 AND task_id IN (SELECT task_id FROM transfer_tasks WHERE shard_id = $1 AND task_id >= $2 AND task_id < $3 ORDER BY task_id LIMIT $4)` getCrossClusterTasksQuery = `SELECT task_id, data, data_encoding FROM cross_cluster_tasks WHERE target_cluster = $1 AND shard_id = $2 AND task_id > $3 AND task_id <= $4 ORDER BY task_id LIMIT $5` createCrossClusterTasksQuery = `INSERT INTO cross_cluster_tasks(target_cluster, shard_id, task_id, data, data_encoding) VALUES(:target_cluster, :shard_id, :task_id, :data, :data_encoding)` deleteCrossClusterTaskQuery = `DELETE FROM cross_cluster_tasks WHERE target_cluster = $1 AND shard_id = $2 AND task_id = $3` rangeDeleteCrossClusterTaskQuery = `DELETE FROM cross_cluster_tasks WHERE target_cluster = $1 AND shard_id = $2 AND task_id >= $3 AND task_id < $4` rangeDeleteCrossClusterTaskByBatchQuery = `DELETE FROM cross_cluster_tasks WHERE target_cluster = $1 AND shard_id = $2 AND task_id IN (SELECT task_id FROM cross_cluster_tasks WHERE target_cluster = $1 AND shard_id = $2 AND task_id >= $3 AND task_id < $4 ORDER BY task_id LIMIT $5)` createTimerTasksQuery = `INSERT INTO timer_tasks (shard_id, visibility_timestamp, task_id, data, data_encoding) VALUES (:shard_id, :visibility_timestamp, :task_id, :data, :data_encoding)` getTimerTasksQuery = `SELECT visibility_timestamp, task_id, data, data_encoding FROM timer_tasks WHERE shard_id = $1 AND (visibility_timestamp, task_id) >= ($2, $3) AND visibility_timestamp < $4 ORDER BY visibility_timestamp,task_id LIMIT $5` deleteTimerTaskQuery = `DELETE FROM timer_tasks WHERE shard_id = $1 AND visibility_timestamp = $2 AND task_id = $3` rangeDeleteTimerTaskQuery = `DELETE FROM timer_tasks WHERE shard_id = $1 AND visibility_timestamp >= $2 AND visibility_timestamp < $3` rangeDeleteTimerTaskByBatchQuery = `DELETE FROM timer_tasks WHERE shard_id = $1 AND (visibility_timestamp,task_id) IN (SELECT visibility_timestamp,task_id FROM timer_tasks WHERE shard_id = $1 AND visibility_timestamp >= $2 AND visibility_timestamp < $3 ORDER BY visibility_timestamp,task_id LIMIT $4)` createReplicationTasksQuery = `INSERT INTO replication_tasks (shard_id, task_id, data, data_encoding) VALUES(:shard_id, :task_id, :data, :data_encoding)` getReplicationTasksQuery = `SELECT task_id, data, data_encoding FROM replication_tasks WHERE shard_id = $1 AND task_id >= $2 AND task_id < $3 ORDER BY task_id LIMIT $4` deleteReplicationTaskQuery = `DELETE FROM replication_tasks WHERE shard_id = $1 AND task_id = $2` rangeDeleteReplicationTaskQuery = `DELETE FROM replication_tasks WHERE shard_id = $1 AND task_id <= $2` rangeDeleteReplicationTaskByBatchQuery = `DELETE FROM replication_tasks WHERE shard_id = $1 AND task_id IN (SELECT task_id FROM replication_tasks WHERE task_id < $2 ORDER BY task_id LIMIT $3)` getReplicationTasksDLQQuery = `SELECT task_id, data, data_encoding FROM replication_tasks_dlq WHERE source_cluster_name = $1 AND shard_id = $2 AND task_id >= $3 AND task_id < $4 ORDER BY task_id LIMIT $5` getReplicationTaskDLQQuery = `SELECT count(1) as count FROM replication_tasks_dlq WHERE source_cluster_name = $1 AND shard_id = $2` bufferedEventsColumns = `shard_id, domain_id, workflow_id, run_id, data, data_encoding` createBufferedEventsQuery = `INSERT INTO buffered_events(` + bufferedEventsColumns + `) VALUES (:shard_id, :domain_id, :workflow_id, :run_id, :data, :data_encoding)` deleteBufferedEventsQuery = `DELETE FROM buffered_events WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3 AND run_id = $4` getBufferedEventsQuery = `SELECT data, data_encoding FROM buffered_events WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3 AND run_id = $4` insertReplicationTaskDLQQuery = ` INSERT INTO replication_tasks_dlq (source_cluster_name, shard_id, task_id, data, data_encoding) VALUES (:source_cluster_name, :shard_id, :task_id, :data, :data_encoding) ` deleteReplicationTaskFromDLQQuery = ` DELETE FROM replication_tasks_dlq WHERE source_cluster_name = $1 AND shard_id = $2 AND task_id = $3` rangeDeleteReplicationTaskFromDLQQuery = ` DELETE FROM replication_tasks_dlq WHERE source_cluster_name = $1 AND shard_id = $2 AND task_id >= $3 AND task_id < $4` rangeDeleteReplicationTaskFromDLQByBatchQuery = `DELETE FROM replication_tasks_dlq WHERE source_cluster_name = $1 AND shard_id = $2 AND task_id IN (SELECT task_id FROM replication_tasks_dlq WHERE source_cluster_name = $1 AND shard_id = $2 AND task_id >= $3 AND task_id < $4 ORDER BY task_id LIMIT $5)` ) // InsertIntoExecutions inserts a row into executions table func (pdb *db) InsertIntoExecutions(ctx context.Context, row *sqlplugin.ExecutionsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, createExecutionQuery, row) } // UpdateExecutions updates a single row in executions table func (pdb *db) UpdateExecutions(ctx context.Context, row *sqlplugin.ExecutionsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, updateExecutionQuery, row) } // SelectFromExecutions reads a single row from executions table // The list execution query result is order by workflow ID only. It may returns duplicate record with pagination. func (pdb *db) SelectFromExecutions(ctx context.Context, filter *sqlplugin.ExecutionsFilter) ([]sqlplugin.ExecutionsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.ExecutionsRow var err error if len(filter.DomainID) == 0 && filter.Size > 0 { err = pdb.driver.SelectContext(ctx, dbShardID, &rows, listExecutionQuery, filter.ShardID, filter.WorkflowID, filter.Size) if err != nil { return nil, err } } else { var row sqlplugin.ExecutionsRow err = pdb.driver.GetContext(ctx, dbShardID, &row, getExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) if err != nil { return nil, err } rows = append(rows, row) } return rows, err } // DeleteFromExecutions deletes a single row from executions table func (pdb *db) DeleteFromExecutions(ctx context.Context, filter *sqlplugin.ExecutionsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext(ctx, dbShardID, deleteExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } // ReadLockExecutions acquires a write lock on a single row in executions table func (pdb *db) ReadLockExecutions(ctx context.Context, filter *sqlplugin.ExecutionsFilter) (int, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var nextEventID int err := pdb.driver.GetContext(ctx, dbShardID, &nextEventID, readLockExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) return nextEventID, err } // WriteLockExecutions acquires a write lock on a single row in executions table func (pdb *db) WriteLockExecutions(ctx context.Context, filter *sqlplugin.ExecutionsFilter) (int, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var nextEventID int err := pdb.driver.GetContext(ctx, dbShardID, &nextEventID, writeLockExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) return nextEventID, err } // InsertIntoCurrentExecutions inserts a single row into current_executions table func (pdb *db) InsertIntoCurrentExecutions(ctx context.Context, row *sqlplugin.CurrentExecutionsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, createCurrentExecutionQuery, row) } // UpdateCurrentExecutions updates a single row in current_executions table func (pdb *db) UpdateCurrentExecutions(ctx context.Context, row *sqlplugin.CurrentExecutionsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, updateCurrentExecutionsQuery, row) } // SelectFromCurrentExecutions reads one or more rows from current_executions table func (pdb *db) SelectFromCurrentExecutions(ctx context.Context, filter *sqlplugin.CurrentExecutionsFilter) (*sqlplugin.CurrentExecutionsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var row sqlplugin.CurrentExecutionsRow err := pdb.driver.GetContext(ctx, dbShardID, &row, getCurrentExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID) return &row, err } // DeleteFromCurrentExecutions deletes a single row in current_executions table func (pdb *db) DeleteFromCurrentExecutions(ctx context.Context, filter *sqlplugin.CurrentExecutionsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext(ctx, dbShardID, deleteCurrentExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } // LockCurrentExecutions acquires a write lock on a single row in current_executions table func (pdb *db) LockCurrentExecutions(ctx context.Context, filter *sqlplugin.CurrentExecutionsFilter) (*sqlplugin.CurrentExecutionsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var row sqlplugin.CurrentExecutionsRow err := pdb.driver.GetContext(ctx, dbShardID, &row, lockCurrentExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID) return &row, err } // LockCurrentExecutionsJoinExecutions joins a row in current_executions with executions table and acquires a // write lock on the result func (pdb *db) LockCurrentExecutionsJoinExecutions(ctx context.Context, filter *sqlplugin.CurrentExecutionsFilter) ([]sqlplugin.CurrentExecutionsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.CurrentExecutionsRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, lockCurrentExecutionJoinExecutionsQuery, filter.ShardID, filter.DomainID, filter.WorkflowID) return rows, err } // InsertIntoTransferTasks inserts one or more rows into transfer_tasks table func (pdb *db) InsertIntoTransferTasks(ctx context.Context, rows []sqlplugin.TransferTasksRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(rows[0].ShardID, pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, createTransferTasksQuery, rows) } // SelectFromTransferTasks reads one or more rows from transfer_tasks table func (pdb *db) SelectFromTransferTasks(ctx context.Context, filter *sqlplugin.TransferTasksFilter) ([]sqlplugin.TransferTasksRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.TransferTasksRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getTransferTasksQuery, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize) if err != nil { return nil, err } return rows, err } // DeleteFromTransferTasks deletes one or more rows from transfer_tasks table func (pdb *db) DeleteFromTransferTasks(ctx context.Context, filter *sqlplugin.TransferTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext(ctx, dbShardID, deleteTransferTaskQuery, filter.ShardID, filter.TaskID) } // RangeDeleteFromTransferTasks deletes multi rows from transfer_tasks table func (pdb *db) RangeDeleteFromTransferTasks(ctx context.Context, filter *sqlplugin.TransferTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return pdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTransferTaskByBatchQuery, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize) } return pdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTransferTaskQuery, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID) } // InsertIntoCrossClusterTasks inserts one or more rows into cross_cluster_tasks table func (pdb *db) InsertIntoCrossClusterTasks(ctx context.Context, rows []sqlplugin.CrossClusterTasksRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(rows[0].ShardID, pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, createCrossClusterTasksQuery, rows) } // SelectFromCrossClusterTasks reads one or more rows from cross_cluster_tasks table func (pdb *db) SelectFromCrossClusterTasks(ctx context.Context, filter *sqlplugin.CrossClusterTasksFilter) ([]sqlplugin.CrossClusterTasksRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.CrossClusterTasksRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getCrossClusterTasksQuery, filter.TargetCluster, filter.ShardID, filter.MinTaskID, filter.MaxTaskID, filter.PageSize) if err != nil { return nil, err } return rows, err } // DeleteFromCrossClusterTasks deletes one or more rows from cross_cluster_tasks table func (pdb *db) DeleteFromCrossClusterTasks(ctx context.Context, filter *sqlplugin.CrossClusterTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext(ctx, dbShardID, deleteCrossClusterTaskQuery, filter.TargetCluster, filter.ShardID, filter.TaskID) } // RangeDeleteFromCrossClusterTasks deletes multi rows from cross_cluster_tasks table func (pdb *db) RangeDeleteFromCrossClusterTasks(ctx context.Context, filter *sqlplugin.CrossClusterTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return pdb.driver.ExecContext(ctx, dbShardID, rangeDeleteCrossClusterTaskByBatchQuery, filter.TargetCluster, filter.ShardID, filter.MinTaskID, filter.MaxTaskID, filter.PageSize) } return pdb.driver.ExecContext(ctx, dbShardID, rangeDeleteCrossClusterTaskQuery, filter.TargetCluster, filter.ShardID, filter.MinTaskID, filter.MaxTaskID) } // InsertIntoTimerTasks inserts one or more rows into timer_tasks table func (pdb *db) InsertIntoTimerTasks(ctx context.Context, rows []sqlplugin.TimerTasksRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(rows[0].ShardID, pdb.GetTotalNumDBShards()) for i := range rows { rows[i].VisibilityTimestamp = pdb.converter.ToPostgresDateTime(rows[i].VisibilityTimestamp) } return pdb.driver.NamedExecContext(ctx, dbShardID, createTimerTasksQuery, rows) } // SelectFromTimerTasks reads one or more rows from timer_tasks table func (pdb *db) SelectFromTimerTasks(ctx context.Context, filter *sqlplugin.TimerTasksFilter) ([]sqlplugin.TimerTasksRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.TimerTasksRow filter.MinVisibilityTimestamp = pdb.converter.ToPostgresDateTime(filter.MinVisibilityTimestamp) filter.MaxVisibilityTimestamp = pdb.converter.ToPostgresDateTime(filter.MaxVisibilityTimestamp) err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getTimerTasksQuery, filter.ShardID, filter.MinVisibilityTimestamp, filter.TaskID, filter.MaxVisibilityTimestamp, filter.PageSize) if err != nil { return nil, err } for i := range rows { rows[i].VisibilityTimestamp = pdb.converter.FromPostgresDateTime(rows[i].VisibilityTimestamp) } return rows, err } // DeleteFromTimerTasks deletes one or more rows from timer_tasks table func (pdb *db) DeleteFromTimerTasks(ctx context.Context, filter *sqlplugin.TimerTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) filter.VisibilityTimestamp = pdb.converter.ToPostgresDateTime(filter.VisibilityTimestamp) return pdb.driver.ExecContext(ctx, dbShardID, deleteTimerTaskQuery, filter.ShardID, filter.VisibilityTimestamp, filter.TaskID) } // RangeDeleteFromTimerTasks deletes multi rows from timer_tasks table func (pdb *db) RangeDeleteFromTimerTasks(ctx context.Context, filter *sqlplugin.TimerTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) filter.MinVisibilityTimestamp = pdb.converter.ToPostgresDateTime(filter.MinVisibilityTimestamp) filter.MaxVisibilityTimestamp = pdb.converter.ToPostgresDateTime(filter.MaxVisibilityTimestamp) if filter.PageSize > 0 { return pdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTimerTaskByBatchQuery, filter.ShardID, filter.MinVisibilityTimestamp, filter.MaxVisibilityTimestamp, filter.PageSize) } return pdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTimerTaskQuery, filter.ShardID, filter.MinVisibilityTimestamp, filter.MaxVisibilityTimestamp) } // InsertIntoBufferedEvents inserts one or more rows into buffered_events table func (pdb *db) InsertIntoBufferedEvents(ctx context.Context, rows []sqlplugin.BufferedEventsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(rows[0].ShardID, pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, createBufferedEventsQuery, rows) } // SelectFromBufferedEvents reads one or more rows from buffered_events table func (pdb *db) SelectFromBufferedEvents(ctx context.Context, filter *sqlplugin.BufferedEventsFilter) ([]sqlplugin.BufferedEventsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.BufferedEventsRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getBufferedEventsQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID rows[i].ShardID = filter.ShardID } return rows, err } // DeleteFromBufferedEvents deletes one or more rows from buffered_events table func (pdb *db) DeleteFromBufferedEvents(ctx context.Context, filter *sqlplugin.BufferedEventsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext(ctx, dbShardID, deleteBufferedEventsQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } // InsertIntoReplicationTasks inserts one or more rows into replication_tasks table func (pdb *db) InsertIntoReplicationTasks(ctx context.Context, rows []sqlplugin.ReplicationTasksRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(rows[0].ShardID, pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, createReplicationTasksQuery, rows) } // SelectFromReplicationTasks reads one or more rows from replication_tasks table func (pdb *db) SelectFromReplicationTasks(ctx context.Context, filter *sqlplugin.ReplicationTasksFilter) ([]sqlplugin.ReplicationTasksRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.ReplicationTasksRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getReplicationTasksQuery, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize) return rows, err } // DeleteFromReplicationTasks deletes one rows from replication_tasks table func (pdb *db) DeleteFromReplicationTasks(ctx context.Context, filter *sqlplugin.ReplicationTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext(ctx, dbShardID, deleteReplicationTaskQuery, filter.ShardID, filter.TaskID) } // RangeDeleteFromReplicationTasks deletes multi rows from replication_tasks table func (pdb *db) RangeDeleteFromReplicationTasks(ctx context.Context, filter *sqlplugin.ReplicationTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return pdb.driver.ExecContext(ctx, dbShardID, rangeDeleteReplicationTaskByBatchQuery, filter.ShardID, filter.ExclusiveMaxTaskID, filter.PageSize) } return pdb.driver.ExecContext(ctx, dbShardID, rangeDeleteReplicationTaskQuery, filter.ShardID, filter.ExclusiveMaxTaskID) } // InsertIntoReplicationTasksDLQ inserts one or more rows into replication_tasks_dlq table func (pdb *db) InsertIntoReplicationTasksDLQ(ctx context.Context, row *sqlplugin.ReplicationTaskDLQRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, insertReplicationTaskDLQQuery, row) } // SelectFromReplicationTasksDLQ reads one or more rows from replication_tasks_dlq table func (pdb *db) SelectFromReplicationTasksDLQ(ctx context.Context, filter *sqlplugin.ReplicationTasksDLQFilter) ([]sqlplugin.ReplicationTasksRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.ReplicationTasksRow err := pdb.driver.SelectContext( ctx, dbShardID, &rows, getReplicationTasksDLQQuery, filter.SourceClusterName, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize) return rows, err } // SelectFromReplicationDLQ reads one row from replication_tasks_dlq table func (pdb *db) SelectFromReplicationDLQ(ctx context.Context, filter *sqlplugin.ReplicationTaskDLQFilter) (int64, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var size []int64 if err := pdb.driver.SelectContext( ctx, dbShardID, &size, getReplicationTaskDLQQuery, filter.SourceClusterName, filter.ShardID, ); err != nil { return 0, err } return size[0], nil } // DeleteMessageFromReplicationTasksDLQ deletes one row from replication_tasks_dlq table func (pdb *db) DeleteMessageFromReplicationTasksDLQ( ctx context.Context, filter *sqlplugin.ReplicationTasksDLQFilter, ) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext( ctx, dbShardID, deleteReplicationTaskFromDLQQuery, filter.SourceClusterName, filter.ShardID, filter.TaskID, ) } // DeleteMessageFromReplicationTasksDLQ deletes one or more rows from replication_tasks_dlq table func (pdb *db) RangeDeleteMessageFromReplicationTasksDLQ( ctx context.Context, filter *sqlplugin.ReplicationTasksDLQFilter, ) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return pdb.driver.ExecContext( ctx, dbShardID, rangeDeleteReplicationTaskFromDLQByBatchQuery, filter.SourceClusterName, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize, ) } return pdb.driver.ExecContext( ctx, dbShardID, rangeDeleteReplicationTaskFromDLQQuery, filter.SourceClusterName, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, ) } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/execution_maps.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "database/sql" "fmt" "strings" "github.com/jmoiron/sqlx" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( deleteMapQueryTemplate = `DELETE FROM %v WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3 AND run_id = $4` // %[2]v is the columns of the value struct (i.e. no primary key columns), comma separated // %[3]v should be %[2]v with colons prepended. // i.e. %[3]v = ",".join(":" + s for s in %[2]v) // %[5]v should be %[2]v with "excluded." prepended. // i.e. %[5]v = ",".join("excluded." + s for s in %[2]v) // So that this query can be used with BindNamed // %[4]v should be the name of the key associated with the map // e.g. for ActivityInfo it is "schedule_id" setKeyInMapQueryTemplate = `INSERT INTO %[1]v (shard_id, domain_id, workflow_id, run_id, %[4]v, %[2]v) VALUES (:shard_id, :domain_id, :workflow_id, :run_id, :%[4]v, %[3]v) ON CONFLICT (shard_id, domain_id, workflow_id, run_id, %[4]v) DO UPDATE SET (shard_id, domain_id, workflow_id, run_id, %[4]v, %[2]v) = (excluded.shard_id, excluded.domain_id, excluded.workflow_id, excluded.run_id, excluded.%[4]v, %[5]v)` // %[2]v is the name of the key deleteKeyInMapQueryTemplate = `DELETE FROM %[1]v WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ? AND %[2]v IN ( ? )` // %[1]v is the name of the table // %[2]v is the name of the key // %[3]v is the value columns, separated by commas getMapQueryTemplate = `SELECT %[2]v, %[3]v FROM %[1]v WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3 AND run_id = $4` ) const ( deleteAllSignalsRequestedSetQuery = `DELETE FROM signals_requested_sets WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3 AND run_id = $4 ` createSignalsRequestedSetQuery = `INSERT INTO signals_requested_sets (shard_id, domain_id, workflow_id, run_id, signal_id) VALUES (:shard_id, :domain_id, :workflow_id, :run_id, :signal_id) ON CONFLICT (shard_id, domain_id, workflow_id, run_id, signal_id) DO NOTHING` deleteSignalsRequestedSetQuery = `DELETE FROM signals_requested_sets WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ? AND signal_id IN ( ? )` getSignalsRequestedSetQuery = `SELECT signal_id FROM signals_requested_sets WHERE shard_id = $1 AND domain_id = $2 AND workflow_id = $3 AND run_id = $4` ) func stringMap(a []string, f func(string) string) []string { b := make([]string, len(a)) for i, v := range a { b[i] = f(v) } return b } func makeDeleteMapQry(tableName string) string { return fmt.Sprintf(deleteMapQueryTemplate, tableName) } func makeSetKeyInMapQry(tableName string, nonPrimaryKeyColumns []string, mapKeyName string) string { return fmt.Sprintf(setKeyInMapQueryTemplate, tableName, strings.Join(nonPrimaryKeyColumns, ","), strings.Join(stringMap(nonPrimaryKeyColumns, func(x string) string { return ":" + x }), ","), mapKeyName, strings.Join(stringMap(nonPrimaryKeyColumns, func(x string) string { return "excluded." + x }), ",")) } func makeDeleteKeyInMapQry(tableName string, mapKeyName string) string { return fmt.Sprintf(deleteKeyInMapQueryTemplate, tableName, mapKeyName) } func makeGetMapQryTemplate(tableName string, nonPrimaryKeyColumns []string, mapKeyName string) string { return fmt.Sprintf(getMapQueryTemplate, tableName, mapKeyName, strings.Join(nonPrimaryKeyColumns, ",")) } var ( // Omit shard_id, run_id, domain_id, workflow_id, schedule_id since they're in the primary key activityInfoColumns = []string{ "data", "data_encoding", "last_heartbeat_details", "last_heartbeat_updated_time", } activityInfoTableName = "activity_info_maps" activityInfoKey = "schedule_id" deleteActivityInfoMapQry = makeDeleteMapQry(activityInfoTableName) setKeyInActivityInfoMapQry = makeSetKeyInMapQry(activityInfoTableName, activityInfoColumns, activityInfoKey) deleteKeyInActivityInfoMapQry = makeDeleteKeyInMapQry(activityInfoTableName, activityInfoKey) getActivityInfoMapQry = makeGetMapQryTemplate(activityInfoTableName, activityInfoColumns, activityInfoKey) ) // ReplaceIntoActivityInfoMaps replaces one or more rows in activity_info_maps table func (pdb *db) ReplaceIntoActivityInfoMaps(ctx context.Context, rows []sqlplugin.ActivityInfoMapsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), pdb.GetTotalNumDBShards()) for i := range rows { rows[i].LastHeartbeatUpdatedTime = pdb.converter.ToPostgresDateTime(rows[i].LastHeartbeatUpdatedTime) } return pdb.driver.NamedExecContext(ctx, dbShardID, setKeyInActivityInfoMapQry, rows) } // SelectFromActivityInfoMaps reads one or more rows from activity_info_maps table func (pdb *db) SelectFromActivityInfoMaps(ctx context.Context, filter *sqlplugin.ActivityInfoMapsFilter) ([]sqlplugin.ActivityInfoMapsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.ActivityInfoMapsRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getActivityInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID rows[i].LastHeartbeatUpdatedTime = pdb.converter.FromPostgresDateTime(rows[i].LastHeartbeatUpdatedTime) } return rows, err } // DeleteFromActivityInfoMaps deletes one or more rows from activity_info_maps table func (pdb *db) DeleteFromActivityInfoMaps(ctx context.Context, filter *sqlplugin.ActivityInfoMapsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) if len(filter.ScheduleIDs) > 0 { query, args, err := sqlx.In(deleteKeyInActivityInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.ScheduleIDs) if err != nil { return nil, err } return pdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return pdb.driver.ExecContext(ctx, dbShardID, deleteActivityInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } var ( timerInfoColumns = []string{ "data", "data_encoding", } timerInfoTableName = "timer_info_maps" timerInfoKey = "timer_id" deleteTimerInfoMapSQLQuery = makeDeleteMapQry(timerInfoTableName) setKeyInTimerInfoMapSQLQuery = makeSetKeyInMapQry(timerInfoTableName, timerInfoColumns, timerInfoKey) deleteKeyInTimerInfoMapSQLQuery = makeDeleteKeyInMapQry(timerInfoTableName, timerInfoKey) getTimerInfoMapSQLQuery = makeGetMapQryTemplate(timerInfoTableName, timerInfoColumns, timerInfoKey) ) // ReplaceIntoTimerInfoMaps replaces one or more rows in timer_info_maps table func (pdb *db) ReplaceIntoTimerInfoMaps(ctx context.Context, rows []sqlplugin.TimerInfoMapsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, setKeyInTimerInfoMapSQLQuery, rows) } // SelectFromTimerInfoMaps reads one or more rows from timer_info_maps table func (pdb *db) SelectFromTimerInfoMaps(ctx context.Context, filter *sqlplugin.TimerInfoMapsFilter) ([]sqlplugin.TimerInfoMapsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.TimerInfoMapsRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getTimerInfoMapSQLQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID } return rows, err } // DeleteFromTimerInfoMaps deletes one or more rows from timer_info_maps table func (pdb *db) DeleteFromTimerInfoMaps(ctx context.Context, filter *sqlplugin.TimerInfoMapsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) if len(filter.TimerIDs) > 0 { query, args, err := sqlx.In(deleteKeyInTimerInfoMapSQLQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.TimerIDs) if err != nil { return nil, err } return pdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return pdb.driver.ExecContext(ctx, dbShardID, deleteTimerInfoMapSQLQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } var ( childExecutionInfoColumns = []string{ "data", "data_encoding", } childExecutionInfoTableName = "child_execution_info_maps" childExecutionInfoKey = "initiated_id" deleteChildExecutionInfoMapQry = makeDeleteMapQry(childExecutionInfoTableName) setKeyInChildExecutionInfoMapQry = makeSetKeyInMapQry(childExecutionInfoTableName, childExecutionInfoColumns, childExecutionInfoKey) deleteKeyInChildExecutionInfoMapQry = makeDeleteKeyInMapQry(childExecutionInfoTableName, childExecutionInfoKey) getChildExecutionInfoMapQry = makeGetMapQryTemplate(childExecutionInfoTableName, childExecutionInfoColumns, childExecutionInfoKey) ) // ReplaceIntoChildExecutionInfoMaps replaces one or more rows in child_execution_info_maps table func (pdb *db) ReplaceIntoChildExecutionInfoMaps(ctx context.Context, rows []sqlplugin.ChildExecutionInfoMapsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, setKeyInChildExecutionInfoMapQry, rows) } // SelectFromChildExecutionInfoMaps reads one or more rows from child_execution_info_maps table func (pdb *db) SelectFromChildExecutionInfoMaps(ctx context.Context, filter *sqlplugin.ChildExecutionInfoMapsFilter) ([]sqlplugin.ChildExecutionInfoMapsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.ChildExecutionInfoMapsRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getChildExecutionInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID } return rows, err } // DeleteFromChildExecutionInfoMaps deletes one or more rows from child_execution_info_maps table func (pdb *db) DeleteFromChildExecutionInfoMaps(ctx context.Context, filter *sqlplugin.ChildExecutionInfoMapsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) if len(filter.InitiatedIDs) > 0 { query, args, err := sqlx.In(deleteKeyInChildExecutionInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.InitiatedIDs) if err != nil { return nil, err } return pdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return pdb.driver.ExecContext(ctx, dbShardID, deleteChildExecutionInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } var ( requestCancelInfoColumns = []string{ "data", "data_encoding", } requestCancelInfoTableName = "request_cancel_info_maps" requestCancelInfoKey = "initiated_id" deleteRequestCancelInfoMapQry = makeDeleteMapQry(requestCancelInfoTableName) setKeyInRequestCancelInfoMapQry = makeSetKeyInMapQry(requestCancelInfoTableName, requestCancelInfoColumns, requestCancelInfoKey) deleteKeyInRequestCancelInfoMapQry = makeDeleteKeyInMapQry(requestCancelInfoTableName, requestCancelInfoKey) getRequestCancelInfoMapQry = makeGetMapQryTemplate(requestCancelInfoTableName, requestCancelInfoColumns, requestCancelInfoKey) ) // ReplaceIntoRequestCancelInfoMaps replaces one or more rows in request_cancel_info_maps table func (pdb *db) ReplaceIntoRequestCancelInfoMaps(ctx context.Context, rows []sqlplugin.RequestCancelInfoMapsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, setKeyInRequestCancelInfoMapQry, rows) } // SelectFromRequestCancelInfoMaps reads one or more rows from request_cancel_info_maps table func (pdb *db) SelectFromRequestCancelInfoMaps(ctx context.Context, filter *sqlplugin.RequestCancelInfoMapsFilter) ([]sqlplugin.RequestCancelInfoMapsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.RequestCancelInfoMapsRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getRequestCancelInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID } return rows, err } // DeleteFromRequestCancelInfoMaps deletes one or more rows from request_cancel_info_maps table func (pdb *db) DeleteFromRequestCancelInfoMaps(ctx context.Context, filter *sqlplugin.RequestCancelInfoMapsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) if len(filter.InitiatedIDs) > 0 { query, args, err := sqlx.In(deleteKeyInRequestCancelInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.InitiatedIDs) if err != nil { return nil, err } return pdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return pdb.driver.ExecContext(ctx, dbShardID, deleteRequestCancelInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } var ( signalInfoColumns = []string{ "data", "data_encoding", } signalInfoTableName = "signal_info_maps" signalInfoKey = "initiated_id" deleteSignalInfoMapQry = makeDeleteMapQry(signalInfoTableName) setKeyInSignalInfoMapQry = makeSetKeyInMapQry(signalInfoTableName, signalInfoColumns, signalInfoKey) deleteKeyInSignalInfoMapQry = makeDeleteKeyInMapQry(signalInfoTableName, signalInfoKey) getSignalInfoMapQry = makeGetMapQryTemplate(signalInfoTableName, signalInfoColumns, signalInfoKey) ) // ReplaceIntoSignalInfoMaps replaces one or more rows in signal_info_maps table func (pdb *db) ReplaceIntoSignalInfoMaps(ctx context.Context, rows []sqlplugin.SignalInfoMapsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, setKeyInSignalInfoMapQry, rows) } // SelectFromSignalInfoMaps reads one or more rows from signal_info_maps table func (pdb *db) SelectFromSignalInfoMaps(ctx context.Context, filter *sqlplugin.SignalInfoMapsFilter) ([]sqlplugin.SignalInfoMapsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.SignalInfoMapsRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getSignalInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID } return rows, err } // DeleteFromSignalInfoMaps deletes one or more rows from signal_info_maps table func (pdb *db) DeleteFromSignalInfoMaps(ctx context.Context, filter *sqlplugin.SignalInfoMapsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) if len(filter.InitiatedIDs) > 0 { query, args, err := sqlx.In(deleteKeyInSignalInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.InitiatedIDs) if err != nil { return nil, err } return pdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return pdb.driver.ExecContext(ctx, dbShardID, deleteSignalInfoMapQry, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } // InsertIntoSignalsRequestedSets inserts one or more rows into signals_requested_sets table func (pdb *db) InsertIntoSignalsRequestedSets(ctx context.Context, rows []sqlplugin.SignalsRequestedSetsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.NamedExecContext(ctx, dbShardID, createSignalsRequestedSetQuery, rows) } // SelectFromSignalsRequestedSets reads one or more rows from signals_requested_sets table func (pdb *db) SelectFromSignalsRequestedSets(ctx context.Context, filter *sqlplugin.SignalsRequestedSetsFilter) ([]sqlplugin.SignalsRequestedSetsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rows []sqlplugin.SignalsRequestedSetsRow err := pdb.driver.SelectContext(ctx, dbShardID, &rows, getSignalsRequestedSetQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) for i := 0; i < len(rows); i++ { rows[i].ShardID = int64(filter.ShardID) rows[i].DomainID = filter.DomainID rows[i].WorkflowID = filter.WorkflowID rows[i].RunID = filter.RunID } return rows, err } // DeleteFromSignalsRequestedSets deletes one or more rows from signals_requested_sets table func (pdb *db) DeleteFromSignalsRequestedSets(ctx context.Context, filter *sqlplugin.SignalsRequestedSetsFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) if len(filter.SignalIDs) > 0 { query, args, err := sqlx.In(deleteSignalsRequestedSetQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID, filter.SignalIDs) if err != nil { return nil, err } return pdb.driver.ExecContext(ctx, dbShardID, sqlx.Rebind(sqlx.BindType(PluginName), query), args...) } return pdb.driver.ExecContext(ctx, dbShardID, deleteAllSignalsRequestedSetQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/plugin.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "fmt" "net" "net/url" "os" "runtime" "github.com/iancoleman/strcase" "github.com/jmoiron/sqlx" "github.com/uber/cadence/common/config" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/sql" "github.com/uber/cadence/common/persistence/sql/sqldriver" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/environment" ) const ( // PluginName is the name of the plugin PluginName = "postgres" dsnFmt = "postgres://%s@%s:%s/%s" ) type plugin struct{} var _ sqlplugin.Plugin = (*plugin)(nil) func init() { sql.RegisterPlugin(PluginName, &plugin{}) } // CreateDB initialize the db object func (d *plugin) CreateDB(cfg *config.SQL) (sqlplugin.DB, error) { conns, err := sqldriver.CreateDBConnections(cfg, func(cfg *config.SQL) (*sqlx.DB, error) { return d.createSingleDBConn(cfg) }) if err != nil { return nil, err } return newDB(conns, nil, sqlplugin.DbShardUndefined, cfg.NumShards) } // CreateAdminDB initialize the adminDB object func (d *plugin) CreateAdminDB(cfg *config.SQL) (sqlplugin.AdminDB, error) { conns, err := sqldriver.CreateDBConnections(cfg, func(cfg *config.SQL) (*sqlx.DB, error) { return d.createSingleDBConn(cfg) }) if err != nil { return nil, err } return newDB(conns, nil, sqlplugin.DbShardUndefined, cfg.NumShards) } // CreateDBConnection creates a returns a reference to a logical connection to the // underlying SQL database. The returned object is to tied to a single // SQL database and the object can be used to perform CRUD operations on // the tables in the database func (d *plugin) createSingleDBConn(cfg *config.SQL) (*sqlx.DB, error) { params, err := registerTLSConfig(cfg) if err != nil { return nil, err } for k, v := range cfg.ConnectAttributes { params.Set(k, v) } host, port, err := net.SplitHostPort(cfg.ConnectAddr) if err != nil { return nil, fmt.Errorf("invalid connect address, it must be in host:port format, %v, err: %v", cfg.ConnectAddr, err) } db, err := sqlx.Connect(PluginName, buildDSN(cfg, host, port, params)) if err != nil { return nil, err } if cfg.MaxConns > 0 { db.SetMaxOpenConns(cfg.MaxConns) } if cfg.MaxIdleConns > 0 { db.SetMaxIdleConns(cfg.MaxIdleConns) } if cfg.MaxConnLifetime > 0 { db.SetConnMaxLifetime(cfg.MaxConnLifetime) } // Maps struct names in CamelCase to snake without need for db struct tags. db.MapperFunc(strcase.ToSnake) return db, nil } func buildDSN(cfg *config.SQL, host string, port string, params url.Values) string { dbName := cfg.DatabaseName // NOTE: postgres doesn't allow to connect with empty dbName, the admin dbName is "postgres" if dbName == "" { dbName = "postgres" } credentialString := generateCredentialString(cfg.User, cfg.Password) dsn := fmt.Sprintf(dsnFmt, credentialString, host, port, dbName) if attrs := params.Encode(); attrs != "" { dsn += "?" + attrs } return dsn } func generateCredentialString(user string, password string) string { userPass := url.PathEscape(user) if password != "" { userPass += ":" + url.PathEscape(password) } return userPass } func registerTLSConfig(cfg *config.SQL) (sslParams url.Values, err error) { sslParams = url.Values{} if cfg.TLS != nil && cfg.TLS.Enabled { sslMode := cfg.TLS.SSLMode if sslMode == "" { // NOTE: Default to require for backward compatibility for Cadence users. sslMode = "require" } sslParams.Set("sslmode", sslMode) sslParams.Set("sslrootcert", cfg.TLS.CaFile) sslParams.Set("sslkey", cfg.TLS.KeyFile) sslParams.Set("sslcert", cfg.TLS.CertFile) } else { sslParams.Set("sslmode", "disable") } return } const ( testSchemaDir = "schema/postgres" ) // GetTestClusterOption return test options func GetTestClusterOption() (*pt.TestBaseOptions, error) { testUser := "postgres" testPassword := "cadence" if runtime.GOOS == "darwin" { testUser = os.Getenv("USER") testPassword = "" } if os.Getenv("POSTGRES_USER") != "" { testUser = os.Getenv("POSTGRES_USER") } if os.Getenv("POSTGRES_PASSWORD") != "" { testPassword = os.Getenv("POSTGRES_PASSWORD") } dbPort, err := environment.GetPostgresPort() if err != nil { return nil, err } return &pt.TestBaseOptions{ DBPluginName: PluginName, DBUsername: testUser, DBPassword: testPassword, DBHost: environment.GetPostgresAddress(), DBPort: dbPort, SchemaDir: testSchemaDir, }, nil } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/plugin_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package postgres import "testing" var testCases = []struct { name string username string password string want string }{ { name: "default", username: "cadence", password: "cadence", want: "cadence:cadence", }, { name: "with forward slash", username: "cadence", password: "cad/ence", want: "cadence:cad%2Fence", }, { name: "with question mark", username: "cadence", password: "cad?ence", want: "cadence:cad%3Fence", }, } func TestGenerateCredentialString(t *testing.T) { for _, tc := range testCases { if userPass := generateCredentialString(tc.username, tc.password); userPass != tc.want { t.Errorf("%v: got %v, want %v", tc.name, userPass, tc.want) } } } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/queue.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "database/sql" "encoding/json" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( templateEnqueueMessageQuery = `INSERT INTO queue (queue_type, message_id, message_payload) VALUES(:queue_type, :message_id, :message_payload)` templateGetLastMessageIDQuery = `SELECT message_id FROM queue WHERE queue_type=$1 ORDER BY message_id DESC LIMIT 1 FOR UPDATE` templateGetMessagesQuery = `SELECT message_id, message_payload FROM queue WHERE queue_type = $1 and message_id > $2 ORDER BY message_id ASC LIMIT $3` templateGetMessagesBetweenQuery = `SELECT message_id, message_payload FROM queue WHERE queue_type = $1 and message_id > $2 and message_id <= $3 ORDER BY message_id ASC LIMIT $4` templateDeleteMessageQuery = `DELETE FROM queue WHERE queue_type = $1 and message_id = $2` templateDeleteMessagesBeforeQuery = `DELETE FROM queue WHERE queue_type = $1 and message_id < $2` templateRangeDeleteMessagesQuery = `DELETE FROM queue WHERE queue_type = $1 and message_id > $2 and message_id <= $3` templateGetQueueMetadataQuery = `SELECT data from queue_metadata WHERE queue_type = $1` templateGetQueueMetadataForUpdateQuery = templateGetQueueMetadataQuery + ` FOR UPDATE` templateInsertQueueMetadataQuery = `INSERT INTO queue_metadata (queue_type, data) VALUES(:queue_type, :data)` templateUpdateQueueMetadataQuery = `UPDATE queue_metadata SET data = $1 WHERE queue_type = $2` templateGetQueueSizeQuery = `SELECT COUNT(1) AS count FROM queue WHERE queue_type=$1` ) // InsertIntoQueue inserts a new row into queue table func (pdb *db) InsertIntoQueue(ctx context.Context, row *sqlplugin.QueueRow) (sql.Result, error) { return pdb.driver.NamedExecContext(ctx, sqlplugin.DbDefaultShard, templateEnqueueMessageQuery, row) } // GetLastEnqueuedMessageIDForUpdate returns the last enqueued message ID func (pdb *db) GetLastEnqueuedMessageIDForUpdate(ctx context.Context, queueType persistence.QueueType) (int64, error) { var lastMessageID int64 err := pdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &lastMessageID, templateGetLastMessageIDQuery, queueType) return lastMessageID, err } // GetMessagesFromQueue retrieves messages from the queue func (pdb *db) GetMessagesFromQueue(ctx context.Context, queueType persistence.QueueType, lastMessageID int64, maxRows int) ([]sqlplugin.QueueRow, error) { var rows []sqlplugin.QueueRow err := pdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, templateGetMessagesQuery, queueType, lastMessageID, maxRows) return rows, err } // GetMessagesBetween retrieves messages from the queue func (pdb *db) GetMessagesBetween(ctx context.Context, queueType persistence.QueueType, firstMessageID int64, lastMessageID int64, maxRows int) ([]sqlplugin.QueueRow, error) { var rows []sqlplugin.QueueRow err := pdb.driver.SelectContext(ctx, sqlplugin.DbDefaultShard, &rows, templateGetMessagesBetweenQuery, queueType, firstMessageID, lastMessageID, maxRows) return rows, err } // DeleteMessagesBefore deletes messages before messageID from the queue func (pdb *db) DeleteMessagesBefore(ctx context.Context, queueType persistence.QueueType, messageID int64) (sql.Result, error) { return pdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, templateDeleteMessagesBeforeQuery, queueType, messageID) } // RangeDeleteMessages deletes messages before messageID from the queue func (pdb *db) RangeDeleteMessages(ctx context.Context, queueType persistence.QueueType, exclusiveBeginMessageID int64, inclusiveEndMessageID int64) (sql.Result, error) { return pdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, templateRangeDeleteMessagesQuery, queueType, exclusiveBeginMessageID, inclusiveEndMessageID) } // DeleteMessage deletes message with a messageID from the queue func (pdb *db) DeleteMessage(ctx context.Context, queueType persistence.QueueType, messageID int64) (sql.Result, error) { return pdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, templateDeleteMessageQuery, queueType, messageID) } // InsertAckLevel inserts ack level func (pdb *db) InsertAckLevel(ctx context.Context, queueType persistence.QueueType, messageID int64, clusterName string) error { clusterAckLevels := map[string]int64{clusterName: messageID} data, err := json.Marshal(clusterAckLevels) if err != nil { return err } _, err = pdb.driver.NamedExecContext(ctx, sqlplugin.DbDefaultShard, templateInsertQueueMetadataQuery, sqlplugin.QueueMetadataRow{QueueType: queueType, Data: data}) return err } // UpdateAckLevels updates cluster ack levels func (pdb *db) UpdateAckLevels(ctx context.Context, queueType persistence.QueueType, clusterAckLevels map[string]int64) error { data, err := json.Marshal(clusterAckLevels) if err != nil { return err } _, err = pdb.driver.ExecContext(ctx, sqlplugin.DbDefaultShard, templateUpdateQueueMetadataQuery, data, queueType) return err } // GetAckLevels returns ack levels for pulling clusters func (pdb *db) GetAckLevels(ctx context.Context, queueType persistence.QueueType, forUpdate bool) (map[string]int64, error) { queryStr := templateGetQueueMetadataQuery if forUpdate { queryStr = templateGetQueueMetadataForUpdateQuery } var data []byte err := pdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &data, queryStr, queueType) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } var clusterAckLevels map[string]int64 if err := json.Unmarshal(data, &clusterAckLevels); err != nil { return nil, err } return clusterAckLevels, nil } // GetQueueSize returns the queue size func (pdb *db) GetQueueSize( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { var size []int64 if err := pdb.driver.SelectContext( ctx, sqlplugin.DbDefaultShard, &size, templateGetQueueSizeQuery, queueType, ); err != nil { return 0, err } return size[0], nil } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/shard.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( createShardQry = `INSERT INTO shards (shard_id, range_id, data, data_encoding) VALUES ($1, $2, $3, $4)` getShardQry = `SELECT shard_id, range_id, data, data_encoding FROM shards WHERE shard_id = $1` updateShardQry = `UPDATE shards SET range_id = $1, data = $2, data_encoding = $3 WHERE shard_id = $4` lockShardQry = `SELECT range_id FROM shards WHERE shard_id = $1 FOR UPDATE` readLockShardQry = `SELECT range_id FROM shards WHERE shard_id = $1 FOR SHARE` ) // InsertIntoShards inserts one or more rows into shards table func (pdb *db) InsertIntoShards(ctx context.Context, row *sqlplugin.ShardsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext(ctx, dbShardID, createShardQry, row.ShardID, row.RangeID, row.Data, row.DataEncoding) } // UpdateShards updates one or more rows into shards table func (pdb *db) UpdateShards(ctx context.Context, row *sqlplugin.ShardsRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(row.ShardID), pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext(ctx, dbShardID, updateShardQry, row.RangeID, row.Data, row.DataEncoding, row.ShardID) } // SelectFromShards reads one or more rows from shards table func (pdb *db) SelectFromShards(ctx context.Context, filter *sqlplugin.ShardsFilter) (*sqlplugin.ShardsRow, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var row sqlplugin.ShardsRow err := pdb.driver.GetContext(ctx, dbShardID, &row, getShardQry, filter.ShardID) if err != nil { return nil, err } return &row, err } // ReadLockShards acquires a read lock on a single row in shards table func (pdb *db) ReadLockShards(ctx context.Context, filter *sqlplugin.ShardsFilter) (int, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rangeID int err := pdb.driver.GetContext(ctx, dbShardID, &rangeID, readLockShardQry, filter.ShardID) return rangeID, err } // WriteLockShards acquires a write lock on a single row in shards table func (pdb *db) WriteLockShards(ctx context.Context, filter *sqlplugin.ShardsFilter) (int, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), pdb.GetTotalNumDBShards()) var rangeID int err := pdb.driver.GetContext(ctx, dbShardID, &rangeID, lockShardQry, filter.ShardID) return rangeID, err } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/task.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "database/sql" "fmt" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( taskListCreatePart = `INTO task_lists(shard_id, domain_id, name, task_type, range_id, data, data_encoding) ` + `VALUES (:shard_id, :domain_id, :name, :task_type, :range_id, :data, :data_encoding)` // (default range ID: initialRangeID == 1) createTaskListQry = `INSERT ` + taskListCreatePart updateTaskListQry = `UPDATE task_lists SET range_id = :range_id, data = :data, data_encoding = :data_encoding WHERE shard_id = :shard_id AND domain_id = :domain_id AND name = :name AND task_type = :task_type ` // This query uses pagination that is best understood by analogy to simple numbers. // Given a list of numbers // 111 // 112 // 121 // 211 // where the hundreds digit corresponds to domain_id, the tens digit // corresponds to name, and the ones digit corresponds to task_type, // Imagine recurring queries with a limit of 1. // For the second query to skip the first result and return 112, it must allow equal values in hundreds & tens, but it's OK because the ones digit is higher. // For the third query, the ones digit is now lower but that's irrelevant because the tens digit is greater. // For the fourth query, the tens digit is now lower but that's again irrelevant because now the hundreds digit is higher. // This technique is useful since the size of the table can easily change between calls, making SKIP an unreliable method, while other db-specific things like rowids are not portable listTaskListQry = `SELECT domain_id, range_id, name, task_type, data, data_encoding ` + `FROM task_lists ` + `WHERE shard_id = $1 AND ((domain_id = $2 AND name = $3 AND task_type > $4) OR (domain_id=$2 AND name > $3) OR (domain_id > $2)) ` + `ORDER BY domain_id,name,task_type LIMIT $5` getTaskListQry = `SELECT domain_id, range_id, name, task_type, data, data_encoding ` + `FROM task_lists ` + `WHERE shard_id = $1 AND domain_id = $2 AND name = $3 AND task_type = $4` deleteTaskListQry = `DELETE FROM task_lists WHERE shard_id=$1 AND domain_id=$2 AND name=$3 AND task_type=$4 AND range_id=$5` lockTaskListQry = `SELECT range_id FROM task_lists ` + `WHERE shard_id = $1 AND domain_id = $2 AND name = $3 AND task_type = $4 FOR UPDATE` getTaskMinMaxQry = `SELECT task_id, data, data_encoding ` + `FROM tasks ` + `WHERE domain_id = $1 AND task_list_name = $2 AND task_type = $3 AND task_id > $4 AND task_id <= $5 ` + ` ORDER BY task_id LIMIT $6` getTaskMinQry = `SELECT task_id, data, data_encoding ` + `FROM tasks ` + `WHERE domain_id = $1 AND task_list_name = $2 AND task_type = $3 AND task_id > $4 ORDER BY task_id LIMIT $5` getTasksCountQry = `SELECT count(1) as count ` + `FROM tasks ` + `WHERE domain_id = $1 AND task_list_name = $2 AND task_type = $3 AND task_id > $4` createTaskQry = `INSERT INTO ` + `tasks(domain_id, task_list_name, task_type, task_id, data, data_encoding) ` + `VALUES(:domain_id, :task_list_name, :task_type, :task_id, :data, :data_encoding)` deleteTaskQry = `DELETE FROM tasks ` + `WHERE domain_id = $1 AND task_list_name = $2 AND task_type = $3 AND task_id = $4` rangeDeleteTaskQry = `DELETE FROM tasks ` + `WHERE domain_id = $1 AND task_list_name = $2 AND task_type = $3 AND task_id IN (SELECT task_id FROM tasks WHERE domain_id = $1 AND task_list_name = $2 AND task_type = $3 AND task_id <= $4 ` + `ORDER BY domain_id,task_list_name,task_type,task_id LIMIT $5 )` getOrphanTaskQry = `SELECT task_id, domain_id, task_list_name, task_type FROM tasks AS t ` + `WHERE NOT EXISTS ( ` + ` SELECT domain_id, name, task_type FROM task_lists AS tl ` + ` WHERE t.domain_id=tl.domain_id and t.task_list_name=tl.name and t.task_type=tl.task_type ` + `) LIMIT $1;` ) // InsertIntoTasks inserts one or more rows into tasks table func (pdb *db) InsertIntoTasks(ctx context.Context, rows []sqlplugin.TasksRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } return pdb.driver.NamedExecContext(ctx, rows[0].ShardID, createTaskQry, rows) } // SelectFromTasks reads one or more rows from tasks table func (pdb *db) SelectFromTasks(ctx context.Context, filter *sqlplugin.TasksFilter) ([]sqlplugin.TasksRow, error) { var err error var rows []sqlplugin.TasksRow switch { case filter.MaxTaskID != nil: err = pdb.driver.SelectContext(ctx, filter.ShardID, &rows, getTaskMinMaxQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.MinTaskID, *filter.MaxTaskID, *filter.PageSize) default: err = pdb.driver.SelectContext(ctx, filter.ShardID, &rows, getTaskMinQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.MinTaskID, *filter.PageSize) } if err != nil { return nil, err } return rows, err } // DeleteFromTasks deletes one or more rows from tasks table func (pdb *db) DeleteFromTasks(ctx context.Context, filter *sqlplugin.TasksFilter) (sql.Result, error) { if filter.TaskIDLessThanEquals != nil { if filter.Limit == nil || *filter.Limit == 0 { return nil, fmt.Errorf("missing limit parameter") } return pdb.driver.ExecContext(ctx, filter.ShardID, rangeDeleteTaskQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.TaskIDLessThanEquals, *filter.Limit) } return pdb.driver.ExecContext(ctx, filter.ShardID, deleteTaskQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.TaskID) } func (pdb *db) GetTasksCount(ctx context.Context, filter *sqlplugin.TasksFilter) (int64, error) { var size []int64 if err := pdb.driver.SelectContext(ctx, filter.ShardID, &size, getTasksCountQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.MinTaskID); err != nil { return 0, err } return size[0], nil } func (pdb *db) GetOrphanTasks(ctx context.Context, filter *sqlplugin.OrphanTasksFilter) ([]sqlplugin.TaskKeyRow, error) { if filter.Limit == nil || *filter.Limit == 0 { return nil, fmt.Errorf("missing limit parameter") } var rows []sqlplugin.TaskKeyRow err := pdb.driver.SelectContext(ctx, sqlplugin.DbAllShards, &rows, getOrphanTaskQry, *filter.Limit) if err != nil { return nil, err } return rows, nil } // InsertIntoTaskLists inserts one or more rows into task_lists table func (pdb *db) InsertIntoTaskLists(ctx context.Context, row *sqlplugin.TaskListsRow) (sql.Result, error) { return pdb.driver.NamedExecContext(ctx, row.ShardID, createTaskListQry, row) } // UpdateTaskLists updates a row in task_lists table func (pdb *db) UpdateTaskLists(ctx context.Context, row *sqlplugin.TaskListsRow) (sql.Result, error) { return pdb.driver.NamedExecContext(ctx, row.ShardID, updateTaskListQry, row) } // SelectFromTaskLists reads one or more rows from task_lists table func (pdb *db) SelectFromTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) ([]sqlplugin.TaskListsRow, error) { switch { case filter.DomainID != nil && filter.Name != nil && filter.TaskType != nil: return pdb.selectFromTaskLists(ctx, filter) case filter.DomainIDGreaterThan != nil && filter.NameGreaterThan != nil && filter.TaskTypeGreaterThan != nil && filter.PageSize != nil: return pdb.rangeSelectFromTaskLists(ctx, filter) default: return nil, fmt.Errorf("invalid set of query filter params") } } func (pdb *db) selectFromTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) ([]sqlplugin.TaskListsRow, error) { var err error var row sqlplugin.TaskListsRow err = pdb.driver.GetContext(ctx, filter.ShardID, &row, getTaskListQry, filter.ShardID, *filter.DomainID, *filter.Name, *filter.TaskType) if err != nil { return nil, err } return []sqlplugin.TaskListsRow{row}, err } func (pdb *db) rangeSelectFromTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) ([]sqlplugin.TaskListsRow, error) { var err error var rows []sqlplugin.TaskListsRow err = pdb.driver.SelectContext(ctx, filter.ShardID, &rows, listTaskListQry, filter.ShardID, *filter.DomainIDGreaterThan, *filter.NameGreaterThan, *filter.TaskTypeGreaterThan, *filter.PageSize) if err != nil { return nil, err } for i := range rows { rows[i].ShardID = filter.ShardID } return rows, nil } // DeleteFromTaskLists deletes a row from task_lists table func (pdb *db) DeleteFromTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) (sql.Result, error) { return pdb.driver.ExecContext(ctx, filter.ShardID, deleteTaskListQry, filter.ShardID, *filter.DomainID, *filter.Name, *filter.TaskType, *filter.RangeID) } // LockTaskLists locks a row in task_lists table func (pdb *db) LockTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) (int64, error) { var rangeID int64 err := pdb.driver.GetContext(ctx, filter.ShardID, &rangeID, lockTaskListQry, filter.ShardID, *filter.DomainID, *filter.Name, *filter.TaskType) return rangeID, err } // InsertIntoTasksWithTTL is not supported in Postgres func (pdb *db) InsertIntoTasksWithTTL(_ context.Context, _ []sqlplugin.TasksRowWithTTL) (sql.Result, error) { return nil, sqlplugin.ErrTTLNotSupported } // InsertIntoTaskListsWithTTL is not supported in Postgres func (pdb *db) InsertIntoTaskListsWithTTL(_ context.Context, _ *sqlplugin.TaskListsRowWithTTL) (sql.Result, error) { return nil, sqlplugin.ErrTTLNotSupported } // UpdateTaskListsWithTTL is not supported in Postgres func (pdb *db) UpdateTaskListsWithTTL(_ context.Context, _ *sqlplugin.TaskListsRowWithTTL) (sql.Result, error) { return nil, sqlplugin.ErrTTLNotSupported } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/typeconv.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import "time" var localZone, _ = time.Now().Zone() var localOffset = getLocalOffset() type ( // DataConverter defines the API for conversions to/from // go types to postgres datatypes // TODO https://github.com/uber/cadence/issues/2892 // There are some reasons: // r application layer is not consistent with timezone: for example, // in some case we write timestamp with local timezone but when the time.Time // is converted from "JSON"(from paging token), the timezone is missing DataConverter interface { ToPostgresDateTime(t time.Time) time.Time FromPostgresDateTime(t time.Time) time.Time } converter struct{} ) // ToPostgresDateTime converts to time to Postgres datetime func (c *converter) ToPostgresDateTime(t time.Time) time.Time { zn, _ := t.Zone() if zn != localZone { nano := t.UnixNano() t := time.Unix(0, nano) return t } return t } // FromPostgresDateTime converts postgres datetime and returns go time func (c *converter) FromPostgresDateTime(t time.Time) time.Time { return t.Add(-localOffset) } func getLocalOffset() time.Duration { _, offsetSecs := time.Now().Zone() return time.Duration(offsetSecs) * time.Second } ================================================ FILE: common/persistence/sql/sqlplugin/postgres/visibility.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "context" "database/sql" "errors" "fmt" "strings" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( templateCreateWorkflowExecutionStarted = `INSERT INTO executions_visibility (` + `domain_id, workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time) ` + `VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) ON CONFLICT (domain_id, run_id) DO NOTHING` templateCreateWorkflowExecutionClosed = `INSERT INTO executions_visibility (` + `domain_id, workflow_id, run_id, start_time, execution_time, workflow_type_name, close_time, close_status, history_length, memo, encoding, is_cron, num_clusters, update_time, shard_id, execution_status, cron_schedule, scheduled_execution_time) ` + `VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) ON CONFLICT (domain_id, run_id) DO UPDATE SET workflow_id = excluded.workflow_id, start_time = excluded.start_time, execution_time = excluded.execution_time, workflow_type_name = excluded.workflow_type_name, close_time = excluded.close_time, close_status = excluded.close_status, history_length = excluded.history_length, memo = excluded.memo, encoding = excluded.encoding, is_cron = excluded.is_cron, num_clusters = excluded.num_clusters, update_time = excluded.update_time, shard_id = excluded.shard_id, execution_status = excluded.execution_status, cron_schedule = excluded.cron_schedule, scheduled_execution_time = excluded.scheduled_execution_time` // RunID condition is needed for correct pagination templateConditions1 = ` AND domain_id = $1 AND start_time >= $2 AND start_time <= $3 AND (run_id > $4 OR start_time < $5) ORDER BY start_time DESC, run_id LIMIT $6` templateConditions2 = ` AND domain_id = $2 AND start_time >= $3 AND start_time <= $4 AND (run_id > $5 OR start_time < $6) ORDER BY start_time DESC, run_id LIMIT $7` templateOpenFieldNames = `workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, is_cron, update_time, shard_id` templateOpenSelect = `SELECT ` + templateOpenFieldNames + ` FROM executions_visibility WHERE close_status IS NULL ` templateClosedSelect = `SELECT ` + templateOpenFieldNames + `, close_time, close_status, history_length FROM executions_visibility WHERE close_status IS NOT NULL ` templateGetOpenWorkflowExecutions = templateOpenSelect + templateConditions1 templateGetClosedWorkflowExecutions = templateClosedSelect + templateConditions1 templateGetOpenWorkflowExecutionsByType = templateOpenSelect + `AND workflow_type_name = $1` + templateConditions2 templateGetClosedWorkflowExecutionsByType = templateClosedSelect + `AND workflow_type_name = $1` + templateConditions2 templateGetOpenWorkflowExecutionsByID = templateOpenSelect + `AND workflow_id = $1` + templateConditions2 templateGetClosedWorkflowExecutionsByID = templateClosedSelect + `AND workflow_id = $1` + templateConditions2 templateGetClosedWorkflowExecutionsByStatus = templateClosedSelect + `AND close_status = $1` + templateConditions2 templateGetClosedWorkflowExecution = `SELECT workflow_id, run_id, start_time, execution_time, memo, encoding, close_time, workflow_type_name, close_status, history_length, is_cron, update_time, shard_id FROM executions_visibility WHERE domain_id = $1 AND close_status IS NOT NULL AND run_id = $2` templateDeleteWorkflowExecution = "DELETE FROM executions_visibility WHERE domain_id=$1 AND run_id=$2" ) var errCloseParams = errors.New("missing one of {closeStatus, closeTime, historyLength} params") // InsertIntoVisibility inserts a row into visibility table. If an row already exist, // its left as such and no update will be made func (pdb *db) InsertIntoVisibility(ctx context.Context, row *sqlplugin.VisibilityRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromDomainID(row.DomainID, pdb.GetTotalNumDBShards()) row.StartTime = pdb.converter.ToPostgresDateTime(row.StartTime) scheduledExecutionTime := pdb.converter.ToPostgresDateTime(row.ScheduledExecutionTime) return pdb.driver.ExecContext(ctx, dbShardID, templateCreateWorkflowExecutionStarted, row.DomainID, row.WorkflowID, row.RunID, row.StartTime, row.ExecutionTime, row.WorkflowTypeName, row.Memo, row.Encoding, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID, row.ExecutionStatus, row.CronSchedule, scheduledExecutionTime) } // ReplaceIntoVisibility replaces an existing row if it exist or creates a new row in visibility table func (pdb *db) ReplaceIntoVisibility(ctx context.Context, row *sqlplugin.VisibilityRow) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromDomainID(row.DomainID, pdb.GetTotalNumDBShards()) switch { case row.CloseStatus != nil && row.CloseTime != nil && row.HistoryLength != nil: row.StartTime = pdb.converter.ToPostgresDateTime(row.StartTime) closeTime := pdb.converter.ToPostgresDateTime(*row.CloseTime) scheduledExecutionTime := pdb.converter.ToPostgresDateTime(row.ScheduledExecutionTime) return pdb.driver.ExecContext(ctx, dbShardID, templateCreateWorkflowExecutionClosed, row.DomainID, row.WorkflowID, row.RunID, row.StartTime, row.ExecutionTime, row.WorkflowTypeName, closeTime, *row.CloseStatus, *row.HistoryLength, row.Memo, row.Encoding, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID, row.ExecutionStatus, row.CronSchedule, scheduledExecutionTime) default: return nil, errCloseParams } } // DeleteFromVisibility deletes a row from visibility table if it exist func (pdb *db) DeleteFromVisibility(ctx context.Context, filter *sqlplugin.VisibilityFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromDomainID(filter.DomainID, pdb.GetTotalNumDBShards()) return pdb.driver.ExecContext(ctx, dbShardID, templateDeleteWorkflowExecution, filter.DomainID, filter.RunID) } // SelectFromVisibility reads one or more rows from visibility table func (pdb *db) SelectFromVisibility(ctx context.Context, filter *sqlplugin.VisibilityFilter) ([]sqlplugin.VisibilityRow, error) { dbShardID := sqlplugin.GetDBShardIDFromDomainID(filter.DomainID, pdb.GetTotalNumDBShards()) var err error var rows []sqlplugin.VisibilityRow if filter.MinStartTime != nil { *filter.MinStartTime = pdb.converter.ToPostgresDateTime(*filter.MinStartTime) } if filter.MaxStartTime != nil { *filter.MaxStartTime = pdb.converter.ToPostgresDateTime(*filter.MaxStartTime) } switch { case filter.MinStartTime == nil && filter.RunID != nil && filter.Closed: var row sqlplugin.VisibilityRow err = pdb.driver.GetContext(ctx, dbShardID, &row, templateGetClosedWorkflowExecution, filter.DomainID, *filter.RunID) if err == nil { rows = append(rows, row) } case filter.MinStartTime != nil && filter.WorkflowID != nil: qry := templateGetOpenWorkflowExecutionsByID if filter.Closed { qry = templateGetClosedWorkflowExecutionsByID } err = pdb.driver.SelectContext(ctx, dbShardID, &rows, qry, *filter.WorkflowID, filter.DomainID, pdb.converter.ToPostgresDateTime(*filter.MinStartTime), pdb.converter.ToPostgresDateTime(*filter.MaxStartTime), *filter.RunID, *filter.MinStartTime, *filter.PageSize) case filter.MinStartTime != nil && filter.WorkflowTypeName != nil: qry := templateGetOpenWorkflowExecutionsByType if filter.Closed { qry = templateGetClosedWorkflowExecutionsByType } err = pdb.driver.SelectContext(ctx, dbShardID, &rows, qry, *filter.WorkflowTypeName, filter.DomainID, pdb.converter.ToPostgresDateTime(*filter.MinStartTime), pdb.converter.ToPostgresDateTime(*filter.MaxStartTime), *filter.RunID, *filter.MaxStartTime, *filter.PageSize) case filter.MinStartTime != nil && filter.CloseStatus != nil: err = pdb.driver.SelectContext(ctx, dbShardID, &rows, templateGetClosedWorkflowExecutionsByStatus, *filter.CloseStatus, filter.DomainID, pdb.converter.ToPostgresDateTime(*filter.MinStartTime), pdb.converter.ToPostgresDateTime(*filter.MaxStartTime), *filter.RunID, pdb.converter.ToPostgresDateTime(*filter.MaxStartTime), *filter.PageSize) case filter.MinStartTime != nil: qry := templateGetOpenWorkflowExecutions if filter.Closed { qry = templateGetClosedWorkflowExecutions } minSt := pdb.converter.ToPostgresDateTime(*filter.MinStartTime) maxSt := pdb.converter.ToPostgresDateTime(*filter.MaxStartTime) err = pdb.driver.SelectContext(ctx, dbShardID, &rows, qry, filter.DomainID, minSt, maxSt, *filter.RunID, maxSt, *filter.PageSize) default: return nil, fmt.Errorf("invalid query filter") } if err != nil { return nil, err } for i := range rows { rows[i].StartTime = pdb.converter.FromPostgresDateTime(rows[i].StartTime) rows[i].ExecutionTime = pdb.converter.FromPostgresDateTime(rows[i].ExecutionTime) if rows[i].CloseTime != nil { closeTime := pdb.converter.FromPostgresDateTime(*rows[i].CloseTime) rows[i].CloseTime = &closeTime } rows[i].RunID = strings.TrimSpace(rows[i].RunID) rows[i].WorkflowID = strings.TrimSpace(rows[i].WorkflowID) } return rows, err } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/admin.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "errors" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( listTablesQuery = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'" ) // CreateDatabase is not supported by sqlite // each sqlite file is a database func (mdb *DB) CreateDatabase(name string) error { return errors.New("sqlite doesn't support creating database") } // DropDatabase is not supported by sqlite // each sqlite file is a database func (mdb *DB) DropDatabase(name string) error { return errors.New("sqlite doesn't support dropping database") } // ListTables returns a list of tables in this database func (mdb *DB) ListTables(_ string) ([]string, error) { var tables []string err := mdb.driver.SelectForSchemaQuery(sqlplugin.DbShardUndefined, &tables, listTablesQuery) return tables, err } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/db.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "context" "github.com/jmoiron/sqlx" "github.com/uber/cadence/common/persistence/sql/sqldriver" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/persistence/sql/sqlplugin/mysql" // import sqlite driver _ "github.com/ncruces/go-sqlite3/driver" // import embed sqlite db _ "github.com/ncruces/go-sqlite3/embed" ) var ( _ sqlplugin.AdminDB = (*DB)(nil) _ sqlplugin.DB = (*DB)(nil) _ sqlplugin.Tx = (*DB)(nil) ) // DB contains methods for managing objects in a sqlite database // It inherits methods from the mysql.DB to reuse the implementation of the methods // sqlplugin.ErrorChecker is customized for sqlite type DB struct { *mysql.DB converter mysql.DataConverter driver sqldriver.Driver originalDBs []*sqlx.DB numDBShards int databaseName string } // NewDB returns an instance of DB, which contains a new created mysql.DB with sqlite specific methods func NewDB(xdbs []*sqlx.DB, tx *sqlx.Tx, dbShardID int, numDBShards int, dataConverter mysql.DataConverter, databaseName string) (*DB, error) { driver, err := sqldriver.NewDriver(xdbs, tx, dbShardID) if err != nil { return nil, err } return &DB{ DB: mysql.NewDBWithDriver(xdbs, driver, numDBShards, dataConverter), driver: driver, originalDBs: xdbs, numDBShards: numDBShards, converter: dataConverter, databaseName: databaseName, }, nil } // PluginName returns the name of the plugin func (mdb *DB) PluginName() string { return PluginName } // BeginTx starts a new transaction and returns a new Tx func (mdb *DB) BeginTx(ctx context.Context, dbShardID int) (sqlplugin.Tx, error) { xtx, err := mdb.driver.BeginTxx(ctx, dbShardID, nil) if err != nil { return nil, err } return NewDB(mdb.originalDBs, xtx, dbShardID, mdb.numDBShards, mdb.converter, mdb.databaseName) } func (mdb *DB) Close() error { if mdb.databaseName == "" { return mdb.DB.Close() } return closeSharedDBConn(mdb.databaseName, mdb.DB.Close) } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/db_pool.go ================================================ package sqlite import ( "sync" "github.com/jmoiron/sqlx" ) // SQLite database takes exclusive access to the database file during write operations. // If another process attempts to perform a write operation, it receives a "database is locked" error. // To ensure only one database connection is created per database file within the running process, // we use dbPool to track and reuse database connections. // When a new database connection is requested, it increments dbPoolCounter and returns the existing sql.DB // from dbPool. When a connection is requested to be closed, it decrements dbPoolCounter. // Once the counter reaches 0, the database connection is closed as there are no more references to it. // Reference: https://github.com/mattn/go-sqlite3/issues/274#issuecomment-232942571 var ( dbPool = make(map[string]*sqlx.DB) dbPoolCounter = make(map[string]int) dbPoolMx sync.Mutex ) // createSharedDBConn creates a new database connection in the dbPool if it doesn't exist. func createSharedDBConn(databaseName string, createDBConnFn func() (*sqlx.DB, error)) (*sqlx.DB, error) { dbPoolMx.Lock() defer dbPoolMx.Unlock() if db, ok := dbPool[databaseName]; ok { dbPoolCounter[databaseName]++ return db, nil } db, err := createDBConnFn() if err != nil { return nil, err } dbPool[databaseName] = db dbPoolCounter[databaseName]++ return db, nil } // closeSharedDBConn closes the database connection in the dbPool if it exists. func closeSharedDBConn(databaseName string, closeDBConnFn func() error) error { dbPoolMx.Lock() defer dbPoolMx.Unlock() dbPoolCounter[databaseName]-- if dbPoolCounter[databaseName] != 0 { return nil } delete(dbPool, databaseName) delete(dbPoolCounter, databaseName) return closeDBConnFn() } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/domain.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "context" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( lockDomainMetadataQuery = `SELECT notification_version FROM domain_metadata` ) // LockDomainMetadata acquires a write lock on a single row in domain_metadata table func (mdb *DB) LockDomainMetadata(ctx context.Context) error { var row sqlplugin.DomainMetadataRow err := mdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &row.NotificationVersion, lockDomainMetadataQuery) return err } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/dsn.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "bytes" "fmt" "sort" "strings" "github.com/uber/cadence/common/config" ) const ( // if journal mode is not provided, we set it to WAL by default // WAL mode allows readers and writers from different processes // to access the database concurrently by default // https://sqlite.org/pragma.html#pragma_journal_mode // https://www.sqlite.org/wal.html pragmaJournalModeAttrName = "_pragma.journal_mode" pragmaJournalModeDefaultValue = "WAL" // if busy_timeout is not provided, we set it to 60 seconds by default // https://sqlite.org/pragma.html#pragma_busy_timeout pragmaBusyTimeoutAttrName = "_pragma.busy_timeout" pragmaBusyTimeoutDefault = "60000" ) const ( // pragmaKey is the key is used to set pragma arguments in the DSN pragmaKey = "_pragma" // pragmaPrefix is the prefix used to identify pragma arguments in the config pragmaPrefix = "_pragma." ) // buildDSN builds the data source name for sqlite from config.SQL // If DatabaseName is not provided, then sqlite will use in-memory database, otherwise it will use the file as the database // All dsn attributes can be set up in the ConnectAttributes field of the config.SQL // Available attributes can be found here: https://github.com/ncruces/go-sqlite3/blob/main/driver/driver.go // PRAGMA attributes should start with "_pragma." prefix // example: "_pragma.journal_mode":"wal" will be transformed to "_pragma=journal_mode(wal)" // More about PRAGMA attributes: https://sqlite.org/pragma.html // Default PRAGMA values if not provided: // - journal_mode: WAL (file only) - https://sqlite.org/pragma.html#pragma_journal_mode // - busy_timeout: 60 seconds - https://sqlite.org/pragma.html#pragma_busy_timeout func buildDSN(cfg *config.SQL) string { // by default, we use in-memory database if no database name is provided var dsn = "file::memory:" // if database name is provided, then sqlite will use the file as the database if cfg.DatabaseName != "" { dsn = fmt.Sprintf("file:%s", cfg.DatabaseName) } if dsnAttrs := buildDSNAttrs(cfg); dsnAttrs != "" { dsn += "?" + dsnAttrs } return dsn } // buildDSNAttrs builds the data source name attributes for sqlite from config.SQL func buildDSNAttrs(cfg *config.SQL) string { sanitizedAttrs := sanitizeDSNAttrs(cfg.ConnectAttributes) if cfg.DatabaseName != "" { defaultIfEmpty(sanitizedAttrs, pragmaJournalModeAttrName, pragmaJournalModeDefaultValue) } defaultIfEmpty(sanitizedAttrs, pragmaBusyTimeoutAttrName, pragmaBusyTimeoutDefault) return joinDSNAttrs(sanitizedAttrs) } // defaultIfEmpty sets the value to the key if the key is not present in the attributes func defaultIfEmpty(attrs map[string]string, key, value string) { if hasAttr(attrs, key) { return } attrs[key] = value } // hasAttr checks if the attributes map has any of the keys func hasAttr(attrs map[string]string, keys ...string) bool { for key := range attrs { for _, k := range keys { if key == k { return true } } } return false } // sanitizeDSNAttrs sanitizes the attributes by trimming the keys and values func sanitizeDSNAttrs(attrs map[string]string) map[string]string { sanitized := make(map[string]string, len(attrs)) for k, v := range attrs { k, v = sanitizeDSNAttrKey(k), sanitizeDSNAttrValue(v) sanitized[k] = v } return sanitized } // isPragmaKey checks if the key is a pragma key func isPragmaKey(key string) bool { return strings.HasPrefix(key, pragmaPrefix) } // transformPragmaArgument transforms the pragma argument to the format that can be used in the DSN // example: "_pragma.journal_mode":"wal" -> "_pragma=journal_mode(wal)" func transformPragmaArgument(key, value string) (newKey, newValue string) { return pragmaKey, fmt.Sprintf("%s(%s)", strings.TrimPrefix(key, pragmaPrefix), value) } // sanitizeDSNAttrElem trims the value, lowercases it func sanitizeDSNAttrKey(v string) string { return strings.TrimSpace(strings.ToLower(v)) } // sanitizeDSNAttrElem trims the value, lowercases it func sanitizeDSNAttrValue(v string) string { return strings.TrimSpace(v) } // sortedKeys returns the sorted keys of the map func sortMapKeys(m map[string]string) []string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } sort.Strings(keys) return keys } // joinDSNAttrs joins the attributes into a single string // with key=value pairs separated by & and escaped func joinDSNAttrs(attrs map[string]string) string { first := true var buf bytes.Buffer // sort the keys to make the order of the attributes deterministic sortedKeys := sortMapKeys(attrs) for _, k := range sortedKeys { v := attrs[k] // pragma arguments should be transformed to the format that can be used in the DSN if isPragmaKey(k) { k, v = transformPragmaArgument(k, v) } if !first { buf.WriteString("&") } first = false buf.WriteString(k) buf.WriteString("=") buf.WriteString(v) } return buf.String() } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/dsn_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" ) func Test_buildDSN(t *testing.T) { for name, c := range map[string]struct { cfg *config.SQL want string }{ "empty": { cfg: &config.SQL{}, want: "file::memory:?_pragma=busy_timeout(60000)", }, "database name only": { cfg: &config.SQL{ DatabaseName: "cadence.db", }, want: "file:cadence.db?_pragma=busy_timeout(60000)&_pragma=journal_mode(WAL)", }, } { t.Run(name, func(t *testing.T) { dsn := buildDSN(c.cfg) assert.Equal(t, c.want, dsn) }) } } func Test_buildDSN_attrs(t *testing.T) { for name, c := range map[string]struct { cfg *config.SQL want string }{ "only connection attrs": { cfg: &config.SQL{ ConnectAttributes: map[string]string{ "_busy_timeout": "10", "_FK": "true", }, }, want: "file::memory:?_busy_timeout=10&_fk=true&_pragma=busy_timeout(60000)", }, "database name and connection attrs": { cfg: &config.SQL{ DatabaseName: "cadence.db", ConnectAttributes: map[string]string{ "cache1 ": "NONe ", }, }, want: "file:cadence.db?_pragma=busy_timeout(60000)&_pragma=journal_mode(WAL)&cache1=NONe", }, } { t.Run(name, func(t *testing.T) { dsn := buildDSN(c.cfg) assert.Contains(t, c.want, dsn) }) } } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/error_checker.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "context" "database/sql" "errors" "github.com/ncruces/go-sqlite3" ) // IsDupEntryError verify if the error is a duplicate entry error func (mdb *DB) IsDupEntryError(err error) bool { var sqlErr *sqlite3.Error if ok := errors.As(err, &sqlErr); !ok { return false } switch sqlErr.ExtendedCode() { case // https://sqlite.org/rescode.html#constraint_unique sqlite3.CONSTRAINT_UNIQUE, // https://sqlite.org/rescode.html#constraint_primarykey sqlite3.CONSTRAINT_PRIMARYKEY: return true } return false } // IsNotFoundError verify if the error is a not found error func (mdb *DB) IsNotFoundError(err error) bool { return errors.Is(err, sql.ErrNoRows) } // IsTimeoutError verify if the error is a timeout error func (mdb *DB) IsTimeoutError(err error) bool { if errors.Is(err, context.DeadlineExceeded) { return true } var sqlErr *sqlite3.Error if ok := errors.As(err, &sqlErr); !ok { return false } // https://sqlite.org/rescode.html#busy_timeout if sqlErr.Timeout() { return true } // https://sqlite.org/rescode.html#interrupt if sqlErr.Code() == sqlite3.INTERRUPT { return true } return false } // IsThrottlingError verify if the error is a throttling error func (mdb *DB) IsThrottlingError(_ error) bool { return false } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/events.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( deleteHistoryNodesQuery = `WITH events_to_delete AS ( SELECT shard_id, tree_id, branch_id, node_id, txn_id FROM history_node WHERE shard_id = ? AND tree_id = ? AND branch_id = ? AND node_id >= ? ORDER BY shard_id, tree_id, branch_id, node_id, txn_id LIMIT ? ) DELETE FROM history_node WHERE (shard_id, tree_id, branch_id, node_id, txn_id) IN (SELECT shard_id, tree_id, branch_id, node_id, txn_id FROM events_to_delete);` ) // DeleteFromHistoryNode deletes one or more rows from history_node table func (mdb *DB) DeleteFromHistoryNode(ctx context.Context, filter *sqlplugin.HistoryNodeFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromTreeID(filter.TreeID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, deleteHistoryNodesQuery, filter.ShardID, filter.TreeID, filter.BranchID, *filter.MinNodeID, filter.PageSize) } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/execution.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( lockExecutionQueryBase = `SELECT next_event_id FROM executions WHERE shard_id = ? AND domain_id = ? AND workflow_id = ? AND run_id = ?` writeLockExecutionQuery = lockExecutionQueryBase readLockExecutionQuery = lockExecutionQueryBase lockCurrentExecutionJoinExecutionsQuery = `SELECT ce.shard_id, ce.domain_id, ce.workflow_id, ce.run_id, ce.create_request_id, ce.state, ce.close_status, ce.start_version, e.last_write_version FROM current_executions ce INNER JOIN executions e ON e.shard_id = ce.shard_id AND e.domain_id = ce.domain_id AND e.workflow_id = ce.workflow_id AND e.run_id = ce.run_id WHERE ce.shard_id = ? AND ce.domain_id = ? AND ce.workflow_id = ?` getCurrentExecutionQuery = `SELECT shard_id, domain_id, workflow_id, run_id, create_request_id, state, close_status, start_version, last_write_version FROM current_executions WHERE shard_id = ? AND domain_id = ? AND workflow_id = ?` lockCurrentExecutionQuery = getCurrentExecutionQuery rangeDeleteTransferTaskQuery = `DELETE FROM transfer_tasks WHERE shard_id = ? AND task_id >= ? AND task_id < ?` rangeDeleteTransferTaskByBatchQuery = `WITH tasks_to_delete AS ( SELECT shard_id, task_id FROM transfer_tasks WHERE shard_id = ? AND task_id >= ? AND task_id < ? ORDER BY task_id LIMIT ? ) DELETE FROM transfer_tasks WHERE (shard_id, task_id) IN (SELECT shard_id, task_id FROM tasks_to_delete);` rangeDeleteReplicationTaskQuery = `DELETE FROM replication_tasks WHERE shard_id = ? AND task_id < ?` rangeDeleteReplicationTaskByBatchQuery = `WITH tasks_to_delete AS ( SELECT shard_id, task_id FROM replication_tasks WHERE shard_id = ? AND task_id < ? ORDER BY task_id LIMIT ? ) DELETE FROM replication_tasks WHERE (shard_id, task_id) IN (SELECT shard_id, task_id FROM tasks_to_delete);` rangeDeleteTimerTaskQuery = `DELETE FROM timer_tasks WHERE shard_id = ? AND visibility_timestamp >= ? AND visibility_timestamp < ?` rangeDeleteTimerTaskByBatchQuery = `WITH tasks_to_delete AS ( SELECT shard_id, visibility_timestamp, task_id FROM timer_tasks WHERE shard_id = ? AND visibility_timestamp >= ? AND visibility_timestamp < ? ORDER BY visibility_timestamp,task_id LIMIT ? ) DELETE FROM timer_tasks WHERE (shard_id, visibility_timestamp, task_id) IN (SELECT shard_id, visibility_timestamp, task_id FROM tasks_to_delete);` ) // ReadLockExecutions acquires a write lock on a single row in executions table func (mdb *DB) ReadLockExecutions(ctx context.Context, filter *sqlplugin.ExecutionsFilter) (int, error) { var nextEventID int dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) err := mdb.driver.GetContext(ctx, dbShardID, &nextEventID, readLockExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) return nextEventID, err } // WriteLockExecutions acquires a write lock on a single row in executions table func (mdb *DB) WriteLockExecutions(ctx context.Context, filter *sqlplugin.ExecutionsFilter) (int, error) { var nextEventID int dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) err := mdb.driver.GetContext(ctx, dbShardID, &nextEventID, writeLockExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID, filter.RunID) return nextEventID, err } // LockCurrentExecutionsJoinExecutions joins a row in current_executions with executions table and acquires a // write lock on the result func (mdb *DB) LockCurrentExecutionsJoinExecutions(ctx context.Context, filter *sqlplugin.CurrentExecutionsFilter) ([]sqlplugin.CurrentExecutionsRow, error) { var rows []sqlplugin.CurrentExecutionsRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) err := mdb.driver.SelectContext(ctx, dbShardID, &rows, lockCurrentExecutionJoinExecutionsQuery, filter.ShardID, filter.DomainID, filter.WorkflowID) return rows, err } // LockCurrentExecutions acquires a write lock on a single row in current_executions table func (mdb *DB) LockCurrentExecutions(ctx context.Context, filter *sqlplugin.CurrentExecutionsFilter) (*sqlplugin.CurrentExecutionsRow, error) { var row sqlplugin.CurrentExecutionsRow dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) err := mdb.driver.GetContext(ctx, dbShardID, &row, lockCurrentExecutionQuery, filter.ShardID, filter.DomainID, filter.WorkflowID) return &row, err } // RangeDeleteFromTransferTasks deletes multi rows from transfer_tasks table func (mdb *DB) RangeDeleteFromTransferTasks(ctx context.Context, filter *sqlplugin.TransferTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTransferTaskByBatchQuery, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID, filter.PageSize) } return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTransferTaskQuery, filter.ShardID, filter.InclusiveMinTaskID, filter.ExclusiveMaxTaskID) } // RangeDeleteFromReplicationTasks deletes multi rows from replication_tasks table func (mdb *DB) RangeDeleteFromReplicationTasks(ctx context.Context, filter *sqlplugin.ReplicationTasksFilter) (sql.Result, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteReplicationTaskByBatchQuery, filter.ShardID, filter.ExclusiveMaxTaskID, filter.PageSize) } return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteReplicationTaskQuery, filter.ShardID, filter.ExclusiveMaxTaskID) } // RangeDeleteFromTimerTasks deletes multi rows from timer_tasks table func (mdb *DB) RangeDeleteFromTimerTasks(ctx context.Context, filter *sqlplugin.TimerTasksFilter) (sql.Result, error) { filter.MinVisibilityTimestamp = mdb.converter.ToDateTime(filter.MinVisibilityTimestamp) filter.MaxVisibilityTimestamp = mdb.converter.ToDateTime(filter.MaxVisibilityTimestamp) dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(filter.ShardID, mdb.GetTotalNumDBShards()) if filter.PageSize > 0 { return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTimerTaskByBatchQuery, filter.ShardID, filter.MinVisibilityTimestamp, filter.MaxVisibilityTimestamp, filter.PageSize) } return mdb.driver.ExecContext(ctx, dbShardID, rangeDeleteTimerTaskQuery, filter.ShardID, filter.MinVisibilityTimestamp, filter.MaxVisibilityTimestamp) } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/execution_maps.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( createSignalsRequestedSetQry = `INSERT OR IGNORE INTO signals_requested_sets (shard_id, domain_id, workflow_id, run_id, signal_id) VALUES (:shard_id, :domain_id, :workflow_id, :run_id, :signal_id)` ) // InsertIntoSignalsRequestedSets inserts one or more rows into signals_requested_sets table func (mdb *DB) InsertIntoSignalsRequestedSets(ctx context.Context, rows []sqlplugin.SignalsRequestedSetsRow) (sql.Result, error) { if len(rows) == 0 { return nil, nil } dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(rows[0].ShardID), mdb.GetTotalNumDBShards()) return mdb.driver.NamedExecContext(ctx, dbShardID, createSignalsRequestedSetQry, rows) } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/plugin.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "fmt" "os" "path" "github.com/google/uuid" "github.com/iancoleman/strcase" "github.com/jmoiron/sqlx" "github.com/uber/cadence/common/config" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/sql" "github.com/uber/cadence/common/persistence/sql/sqldriver" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( PluginName = "sqlite" ) // SQLite plugin provides an sql persistence storage implementation for sqlite database // Mostly the implementation reuses the mysql implementation // If DatabaseName is not provided, then sqlite will use in-memory database, // otherwise it will use the file as the database type plugin struct{} var _ sqlplugin.Plugin = (*plugin)(nil) func init() { sql.RegisterPlugin(PluginName, &plugin{}) } // CreateDB wraps createDB to return an instance of sqlplugin.DB func (p *plugin) CreateDB(cfg *config.SQL) (sqlplugin.DB, error) { return p.createDB(cfg) } // CreateAdminDB wraps createDB to return an instance of sqlplugin.AdminDB func (p *plugin) CreateAdminDB(cfg *config.SQL) (sqlplugin.AdminDB, error) { return p.createDB(cfg) } // createDB create a new instance of DB func (p *plugin) createDB(cfg *config.SQL) (*DB, error) { conns, err := sqldriver.CreateDBConnections(cfg, p.createSingleDBConn) if err != nil { return nil, err } return NewDB(conns, nil, sqlplugin.DbShardUndefined, cfg.NumShards, newConverter(), cfg.DatabaseName) } // createSingleDBConn creates a single database connection for sqlite // Plugin respects the following arguments MaxConns, MaxIdleConns, MaxConnLifetime // Other arguments are used and described in buildDSN function func (p *plugin) createSingleDBConn(cfg *config.SQL) (*sqlx.DB, error) { if cfg.DatabaseName == "" { return p.createDBConn(cfg) } return createSharedDBConn(cfg.DatabaseName, func() (*sqlx.DB, error) { return p.createDBConn(cfg) }) } func (p *plugin) createDBConn(cfg *config.SQL) (*sqlx.DB, error) { db, err := sqlx.Connect("sqlite3", buildDSN(cfg)) if err != nil { return nil, fmt.Errorf("failed to create database connection: %v", err) } if cfg.MaxConns > 0 { db.SetMaxOpenConns(cfg.MaxConns) } if cfg.MaxIdleConns > 0 { db.SetMaxIdleConns(cfg.MaxIdleConns) } if cfg.MaxConnLifetime > 0 { db.SetConnMaxLifetime(cfg.MaxConnLifetime) } // Maps struct names in CamelCase to snake without need for DB struct tags. db.MapperFunc(strcase.ToSnake) return db, nil } const testSchemaDir = "schema/sqlite" // GetTestClusterOption returns a test cluster option for sqlite plugin // It uses a temporary directory for the database name func GetTestClusterOption() *pt.TestBaseOptions { return &pt.TestBaseOptions{ DBPluginName: PluginName, DBName: path.Join(os.TempDir(), uuid.New().String()), SchemaDir: testSchemaDir, StoreType: config.StoreTypeSQL, } } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/plugin_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "os" "path" "testing" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" ) func TestPlugin_CreateDB(t *testing.T) { for name, cfg := range map[string]*config.SQL{ "in-memory": {}, "temp file": {DatabaseName: path.Join(os.TempDir(), uuid.New().String())}, } { t.Run(name, func(t *testing.T) { p := &plugin{} db, err := p.CreateDB(cfg) assert.NoError(t, err) assert.NotNil(t, db) }) } } func TestPlugin_CreateAdminDB(t *testing.T) { for name, cfg := range map[string]*config.SQL{ "in-memory": {}, "temp file": {DatabaseName: path.Join(os.TempDir(), uuid.New().String())}, } { t.Run(name, func(t *testing.T) { p := &plugin{} db, err := p.CreateAdminDB(cfg) assert.NoError(t, err) assert.NotNil(t, db) }) } } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/queue.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "context" "database/sql" "encoding/json" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( templateGetLastMessageIDQuery = `SELECT message_id FROM queue WHERE queue_type=? ORDER BY message_id DESC LIMIT 1` templateGetQueueMetadataQuery = `SELECT data from queue_metadata WHERE queue_type = ?` ) // GetLastEnqueuedMessageIDForUpdate returns the last enqueued message ID func (mdb *DB) GetLastEnqueuedMessageIDForUpdate( ctx context.Context, queueType persistence.QueueType, ) (int64, error) { var lastMessageID int64 err := mdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &lastMessageID, templateGetLastMessageIDQuery, queueType) return lastMessageID, err } // GetAckLevels returns ack levels for pulling clusters func (mdb *DB) GetAckLevels( ctx context.Context, queueType persistence.QueueType, forUpdate bool, ) (map[string]int64, error) { queryStr := templateGetQueueMetadataQuery if forUpdate { // FOR UPDATE is not supported in sqlite } var data []byte err := mdb.driver.GetContext(ctx, sqlplugin.DbDefaultShard, &data, queryStr, queueType) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } var clusterAckLevels map[string]int64 if err := json.Unmarshal(data, &clusterAckLevels); err != nil { return nil, err } return clusterAckLevels, nil } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/shard.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "context" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( lockShardQry = `SELECT range_id FROM shards WHERE shard_id = ?` readLockShardQry = `SELECT range_id FROM shards WHERE shard_id = ?` ) // WriteLockShards acquires a write lock on a single row in shards table func (mdb *DB) WriteLockShards(ctx context.Context, filter *sqlplugin.ShardsFilter) (int, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) var rangeID int err := mdb.driver.GetContext(ctx, dbShardID, &rangeID, lockShardQry, filter.ShardID) return rangeID, err } // ReadLockShards acquires a read lock on a single row in shards table func (mdb *DB) ReadLockShards(ctx context.Context, filter *sqlplugin.ShardsFilter) (int, error) { dbShardID := sqlplugin.GetDBShardIDFromHistoryShardID(int(filter.ShardID), mdb.GetTotalNumDBShards()) var rangeID int err := mdb.driver.GetContext(ctx, dbShardID, &rangeID, readLockShardQry, filter.ShardID) return rangeID, err } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/sqlite_persistence_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sqlite import ( "testing" "github.com/stretchr/testify/suite" pt "github.com/uber/cadence/common/persistence/persistence-tests" ) func TestSQLiteHistoryV2PersistenceSuite(t *testing.T) { s := new(pt.HistoryV2PersistenceSuite) option := GetTestClusterOption() s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestSQLiteMatchingPersistenceSuite(t *testing.T) { s := new(pt.MatchingPersistenceSuite) option := GetTestClusterOption() s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestSQLiteMetadataPersistenceSuiteV2(t *testing.T) { s := new(pt.MetadataPersistenceSuiteV2) option := GetTestClusterOption() s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestSQLiteShardPersistenceSuite(t *testing.T) { s := new(pt.ShardPersistenceSuite) option := GetTestClusterOption() s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } type ExecutionManagerSuite struct { pt.ExecutionManagerSuite } func (s *ExecutionManagerSuite) TestCreateWorkflowExecutionWithWorkflowRequestsDedup() { s.T().Skip("skip the test until we store workflow_request in sqlite") } func (s *ExecutionManagerSuite) TestUpdateWorkflowExecutionWithWorkflowRequestsDedup() { s.T().Skip("skip the test until we store workflow_request in sqlite") } func TestSQLiteExecutionManagerSuite(t *testing.T) { s := new(ExecutionManagerSuite) option := GetTestClusterOption() s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestSQLiteExecutionManagerWithEventsV2(t *testing.T) { s := new(pt.ExecutionManagerSuiteForEventsV2) option := GetTestClusterOption() s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestSQLiteVisibilityPersistenceSuite(t *testing.T) { s := new(pt.DBVisibilityPersistenceSuite) option := GetTestClusterOption() s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestSQLiteQueuePersistence(t *testing.T) { s := new(pt.QueuePersistenceSuite) option := GetTestClusterOption() s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestSQLiteConfigPersistence(t *testing.T) { s := new(pt.ConfigStorePersistenceSuite) option := GetTestClusterOption() s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestSQLiteDomainAuditPersistence(t *testing.T) { t.Skip("DomainAuditPersistence is only implemented for NoSQL/Cassandra in this PR") } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/task.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "context" "database/sql" "fmt" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( lockTaskListQry = `SELECT range_id FROM task_lists ` + `WHERE shard_id = ? AND domain_id = ? AND name = ? AND task_type = ?` deleteTaskQry = `DELETE FROM tasks ` + `WHERE domain_id = ? AND task_list_name = ? AND task_type = ? AND task_id = ?` rangeDeleteTaskQry = `WITH tasks_to_delete AS ( SELECT domain_id,task_list_name,task_type,task_id FROM tasks WHERE domain_id = ? AND task_list_name = ? AND task_type = ? AND task_id <= ? ORDER BY domain_id,task_list_name,task_type,task_id LIMIT ? ) DELETE FROM tasks WHERE (domain_id,task_list_name,task_type,task_id) IN (SELECT domain_id,task_list_name,task_type,task_id FROM tasks_to_delete);` ) // LockTaskLists locks a row in task_lists table func (mdb *DB) LockTaskLists(ctx context.Context, filter *sqlplugin.TaskListsFilter) (int64, error) { var rangeID int64 err := mdb.driver.GetContext(ctx, filter.ShardID, &rangeID, lockTaskListQry, filter.ShardID, *filter.DomainID, *filter.Name, *filter.TaskType) return rangeID, err } // DeleteFromTasks deletes one or more rows from tasks table func (mdb *DB) DeleteFromTasks(ctx context.Context, filter *sqlplugin.TasksFilter) (sql.Result, error) { if filter.TaskIDLessThanEquals != nil { if filter.Limit == nil || *filter.Limit == 0 { return nil, fmt.Errorf("missing limit parameter") } return mdb.driver.ExecContext(ctx, filter.ShardID, rangeDeleteTaskQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.TaskIDLessThanEquals, *filter.Limit) } return mdb.driver.ExecContext(ctx, filter.ShardID, deleteTaskQry, filter.DomainID, filter.TaskListName, filter.TaskType, *filter.TaskID) } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/typeconv.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "time" ) // converter implements mysql.DataConverter // SQLite does not require any conversion, so this is a no-op implementation type converter struct{} // newConverter returns a new instance of converter func newConverter() *converter { return &converter{} } // ToDateTime returns the same time func (c converter) ToDateTime(t time.Time) time.Time { return t } // FromDateTime returns the same time func (c converter) FromDateTime(t time.Time) time.Time { return t } ================================================ FILE: common/persistence/sql/sqlplugin/sqlite/visibility.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sqlite import ( "context" "database/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) const ( templateCreateWorkflowExecutionStarted = `INSERT OR IGNORE INTO executions_visibility (` + `domain_id, workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, is_cron, num_clusters, update_time, shard_id) ` + `VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` ) // InsertIntoVisibility inserts a row into visibility table. If an row already exist, // its left as such and no update will be made func (mdb *DB) InsertIntoVisibility(ctx context.Context, row *sqlplugin.VisibilityRow) (sql.Result, error) { row.StartTime = mdb.converter.ToDateTime(row.StartTime) dbShardID := sqlplugin.GetDBShardIDFromDomainID(row.DomainID, mdb.GetTotalNumDBShards()) return mdb.driver.ExecContext(ctx, dbShardID, templateCreateWorkflowExecutionStarted, row.DomainID, row.WorkflowID, row.RunID, row.StartTime, row.ExecutionTime, row.WorkflowTypeName, row.Memo, row.Encoding, row.IsCron, row.NumClusters, row.UpdateTime, row.ShardID) } ================================================ FILE: common/persistence/sql/workflow_state_maps.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/types" ) func updateActivityInfos( ctx context.Context, tx sqlplugin.Tx, activityInfos []*persistence.InternalActivityInfo, deleteInfos []int64, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, parser serialization.Parser, ) error { if len(activityInfos) > 0 { rows := make([]sqlplugin.ActivityInfoMapsRow, len(activityInfos)) for i, activityInfo := range activityInfos { scheduledEvent, scheduledEncoding := persistence.FromDataBlob(activityInfo.ScheduledEvent) startEvent, startEncoding := persistence.FromDataBlob(activityInfo.StartedEvent) info := &serialization.ActivityInfo{ Version: activityInfo.Version, ScheduledEventBatchID: activityInfo.ScheduledEventBatchID, ScheduledEvent: scheduledEvent, ScheduledEventEncoding: scheduledEncoding, ScheduledTimestamp: activityInfo.ScheduledTime, StartedID: activityInfo.StartedID, StartedEvent: startEvent, StartedEventEncoding: startEncoding, StartedTimestamp: activityInfo.StartedTime, ActivityID: activityInfo.ActivityID, RequestID: activityInfo.RequestID, ScheduleToStartTimeout: activityInfo.ScheduleToStartTimeout, ScheduleToCloseTimeout: activityInfo.ScheduleToCloseTimeout, StartToCloseTimeout: activityInfo.StartToCloseTimeout, HeartbeatTimeout: activityInfo.HeartbeatTimeout, CancelRequested: activityInfo.CancelRequested, CancelRequestID: activityInfo.CancelRequestID, TimerTaskStatus: activityInfo.TimerTaskStatus, Attempt: activityInfo.Attempt, TaskList: activityInfo.TaskList, TaskListKind: activityInfo.TaskListKind, StartedIdentity: activityInfo.StartedIdentity, HasRetryPolicy: activityInfo.HasRetryPolicy, RetryInitialInterval: activityInfo.InitialInterval, RetryBackoffCoefficient: activityInfo.BackoffCoefficient, RetryMaximumInterval: activityInfo.MaximumInterval, RetryExpirationTimestamp: activityInfo.ExpirationTime, RetryMaximumAttempts: activityInfo.MaximumAttempts, RetryNonRetryableErrors: activityInfo.NonRetriableErrors, RetryLastFailureReason: activityInfo.LastFailureReason, RetryLastWorkerIdentity: activityInfo.LastWorkerIdentity, RetryLastFailureDetails: activityInfo.LastFailureDetails, } blob, err := parser.ActivityInfoToBlob(info) if err != nil { return err } rows[i] = sqlplugin.ActivityInfoMapsRow{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, ScheduleID: activityInfo.ScheduleID, LastHeartbeatUpdatedTime: activityInfo.LastHeartBeatUpdatedTime, LastHeartbeatDetails: activityInfo.Details, Data: blob.Data, DataEncoding: string(blob.Encoding), } } if _, err := tx.ReplaceIntoActivityInfoMaps(ctx, rows); err != nil { return convertCommonErrors(tx, "updateActivityInfos", "Failed to execute update query.", err) } } if len(deleteInfos) > 0 { if _, err := tx.DeleteFromActivityInfoMaps(ctx, &sqlplugin.ActivityInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, ScheduleIDs: deleteInfos, }); err != nil { return convertCommonErrors(tx, "updateActivityInfos", "Failed to execute delete query.", err) } } return nil } func getActivityInfoMap( ctx context.Context, db sqlplugin.DB, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, parser serialization.Parser, ) (map[int64]*persistence.InternalActivityInfo, error) { rows, err := db.SelectFromActivityInfoMaps(ctx, &sqlplugin.ActivityInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }) if err != nil && err != sql.ErrNoRows { return nil, convertCommonErrors(db, "getActivityInfoMap", "", err) } ret := make(map[int64]*persistence.InternalActivityInfo) for _, row := range rows { decoded, err := parser.ActivityInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } info := &persistence.InternalActivityInfo{ DomainID: row.DomainID.String(), ScheduleID: row.ScheduleID, Details: row.LastHeartbeatDetails, LastHeartBeatUpdatedTime: row.LastHeartbeatUpdatedTime, Version: decoded.GetVersion(), ScheduledEventBatchID: decoded.GetScheduledEventBatchID(), ScheduledEvent: persistence.NewDataBlob(decoded.ScheduledEvent, constants.EncodingType(decoded.GetScheduledEventEncoding())), ScheduledTime: decoded.GetScheduledTimestamp(), StartedID: decoded.GetStartedID(), StartedTime: decoded.GetStartedTimestamp(), ActivityID: decoded.GetActivityID(), RequestID: decoded.GetRequestID(), ScheduleToStartTimeout: decoded.GetScheduleToStartTimeout(), ScheduleToCloseTimeout: decoded.GetScheduleToCloseTimeout(), StartToCloseTimeout: decoded.GetStartToCloseTimeout(), HeartbeatTimeout: decoded.GetHeartbeatTimeout(), CancelRequested: decoded.GetCancelRequested(), CancelRequestID: decoded.GetCancelRequestID(), TimerTaskStatus: decoded.GetTimerTaskStatus(), Attempt: decoded.GetAttempt(), StartedIdentity: decoded.GetStartedIdentity(), TaskList: decoded.GetTaskList(), TaskListKind: decoded.GetTaskListKind(), HasRetryPolicy: decoded.GetHasRetryPolicy(), InitialInterval: decoded.GetRetryInitialInterval(), BackoffCoefficient: decoded.GetRetryBackoffCoefficient(), MaximumInterval: decoded.GetRetryMaximumInterval(), ExpirationTime: decoded.GetRetryExpirationTimestamp(), MaximumAttempts: decoded.GetRetryMaximumAttempts(), NonRetriableErrors: decoded.GetRetryNonRetryableErrors(), LastFailureReason: decoded.GetRetryLastFailureReason(), LastWorkerIdentity: decoded.GetRetryLastWorkerIdentity(), LastFailureDetails: decoded.GetRetryLastFailureDetails(), } if decoded.StartedEvent != nil { info.StartedEvent = persistence.NewDataBlob(decoded.StartedEvent, constants.EncodingType(decoded.GetStartedEventEncoding())) } ret[row.ScheduleID] = info } return ret, nil } func deleteActivityInfoMap( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) error { if _, err := tx.DeleteFromActivityInfoMaps(ctx, &sqlplugin.ActivityInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "deleteActivityInfoMap", "", err) } return nil } func updateTimerInfos( ctx context.Context, tx sqlplugin.Tx, timerInfos []*persistence.TimerInfo, deleteInfos []string, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, parser serialization.Parser, ) error { if len(timerInfos) > 0 { rows := make([]sqlplugin.TimerInfoMapsRow, len(timerInfos)) for i, timerInfo := range timerInfos { blob, err := parser.TimerInfoToBlob(&serialization.TimerInfo{ Version: timerInfo.Version, StartedID: timerInfo.StartedID, ExpiryTimestamp: timerInfo.ExpiryTime, // TaskID is a misleading variable, it actually serves // the purpose of indicating whether a timer task is // generated for this timer info TaskID: timerInfo.TaskStatus, }) if err != nil { return err } rows[i] = sqlplugin.TimerInfoMapsRow{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, TimerID: timerInfo.TimerID, Data: blob.Data, DataEncoding: string(blob.Encoding), } } if _, err := tx.ReplaceIntoTimerInfoMaps(ctx, rows); err != nil { return convertCommonErrors(tx, "updateTimerInfos", "Failed to execute update query.", err) } } if len(deleteInfos) > 0 { if _, err := tx.DeleteFromTimerInfoMaps(ctx, &sqlplugin.TimerInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, TimerIDs: deleteInfos, }); err != nil { return convertCommonErrors(tx, "updateTimerInfos", "Failed to execute delete query.", err) } } return nil } func getTimerInfoMap( ctx context.Context, db sqlplugin.DB, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, parser serialization.Parser, ) (map[string]*persistence.TimerInfo, error) { rows, err := db.SelectFromTimerInfoMaps(ctx, &sqlplugin.TimerInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }) if err != nil && err != sql.ErrNoRows { return nil, convertCommonErrors(db, "getTimerInfoMap", "", err) } ret := make(map[string]*persistence.TimerInfo) for _, row := range rows { info, err := parser.TimerInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } ret[row.TimerID] = &persistence.TimerInfo{ TimerID: row.TimerID, Version: info.GetVersion(), StartedID: info.GetStartedID(), ExpiryTime: info.GetExpiryTimestamp(), // TaskID is a misleading variable, it actually serves // the purpose of indicating whether a timer task is // generated for this timer info TaskStatus: info.GetTaskID(), } } return ret, nil } func deleteTimerInfoMap( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) error { if _, err := tx.DeleteFromTimerInfoMaps(ctx, &sqlplugin.TimerInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "deleteTimerInfoMap", "", err) } return nil } func updateChildExecutionInfos( ctx context.Context, tx sqlplugin.Tx, childExecutionInfos []*persistence.InternalChildExecutionInfo, deleteInfos []int64, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, parser serialization.Parser, ) error { if len(childExecutionInfos) > 0 { rows := make([]sqlplugin.ChildExecutionInfoMapsRow, len(childExecutionInfos)) for i, childExecutionInfo := range childExecutionInfos { initiateEvent, initiateEncoding := persistence.FromDataBlob(childExecutionInfo.InitiatedEvent) startEvent, startEncoding := persistence.FromDataBlob(childExecutionInfo.StartedEvent) info := &serialization.ChildExecutionInfo{ Version: childExecutionInfo.Version, InitiatedEventBatchID: childExecutionInfo.InitiatedEventBatchID, InitiatedEvent: initiateEvent, InitiatedEventEncoding: initiateEncoding, StartedEvent: startEvent, StartedEventEncoding: startEncoding, StartedID: childExecutionInfo.StartedID, StartedWorkflowID: childExecutionInfo.StartedWorkflowID, StartedRunID: serialization.MustParseUUID(childExecutionInfo.StartedRunID), CreateRequestID: childExecutionInfo.CreateRequestID, DomainID: childExecutionInfo.DomainID, DomainNameDEPRECATED: childExecutionInfo.DomainNameDEPRECATED, WorkflowTypeName: childExecutionInfo.WorkflowTypeName, ParentClosePolicy: int32(childExecutionInfo.ParentClosePolicy), } blob, err := parser.ChildExecutionInfoToBlob(info) if err != nil { return err } rows[i] = sqlplugin.ChildExecutionInfoMapsRow{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, InitiatedID: childExecutionInfo.InitiatedID, Data: blob.Data, DataEncoding: string(blob.Encoding), } } if _, err := tx.ReplaceIntoChildExecutionInfoMaps(ctx, rows); err != nil { return convertCommonErrors(tx, "updateChildExecutionInfos", "Failed to execute update query.", err) } } if len(deleteInfos) > 0 { if _, err := tx.DeleteFromChildExecutionInfoMaps(ctx, &sqlplugin.ChildExecutionInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, InitiatedIDs: deleteInfos, }); err != nil { return convertCommonErrors(tx, "updateChildExecutionInfos", "Failed to execute delete query.", err) } } return nil } func getChildExecutionInfoMap( ctx context.Context, db sqlplugin.DB, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, parser serialization.Parser, ) (map[int64]*persistence.InternalChildExecutionInfo, error) { rows, err := db.SelectFromChildExecutionInfoMaps(ctx, &sqlplugin.ChildExecutionInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }) if err != nil && err != sql.ErrNoRows { return nil, convertCommonErrors(db, "getChildExecutionInfoMap", "", err) } ret := make(map[int64]*persistence.InternalChildExecutionInfo) for _, row := range rows { rowInfo, err := parser.ChildExecutionInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } info := &persistence.InternalChildExecutionInfo{ InitiatedID: row.InitiatedID, InitiatedEventBatchID: rowInfo.GetInitiatedEventBatchID(), Version: rowInfo.GetVersion(), StartedID: rowInfo.GetStartedID(), StartedWorkflowID: rowInfo.GetStartedWorkflowID(), StartedRunID: serialization.UUID(rowInfo.GetStartedRunID()).String(), CreateRequestID: rowInfo.GetCreateRequestID(), DomainID: rowInfo.GetDomainID(), DomainNameDEPRECATED: rowInfo.GetDomainNameDEPRECATED(), WorkflowTypeName: rowInfo.GetWorkflowTypeName(), ParentClosePolicy: types.ParentClosePolicy(rowInfo.GetParentClosePolicy()), } if rowInfo.InitiatedEvent != nil { info.InitiatedEvent = persistence.NewDataBlob(rowInfo.InitiatedEvent, constants.EncodingType(rowInfo.GetInitiatedEventEncoding())) } if rowInfo.StartedEvent != nil { info.StartedEvent = persistence.NewDataBlob(rowInfo.StartedEvent, constants.EncodingType(rowInfo.GetStartedEventEncoding())) } ret[row.InitiatedID] = info } return ret, nil } func deleteChildExecutionInfoMap( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) error { if _, err := tx.DeleteFromChildExecutionInfoMaps(ctx, &sqlplugin.ChildExecutionInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "deleteChildExecutionInfoMap", "", err) } return nil } func updateRequestCancelInfos( ctx context.Context, tx sqlplugin.Tx, requestCancelInfos []*persistence.RequestCancelInfo, deleteInfos []int64, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, parser serialization.Parser, ) error { if len(requestCancelInfos) > 0 { rows := make([]sqlplugin.RequestCancelInfoMapsRow, len(requestCancelInfos)) for i, requestCancelInfo := range requestCancelInfos { blob, err := parser.RequestCancelInfoToBlob(&serialization.RequestCancelInfo{ Version: requestCancelInfo.Version, InitiatedEventBatchID: requestCancelInfo.InitiatedEventBatchID, CancelRequestID: requestCancelInfo.CancelRequestID, }) if err != nil { return err } rows[i] = sqlplugin.RequestCancelInfoMapsRow{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, InitiatedID: requestCancelInfo.InitiatedID, Data: blob.Data, DataEncoding: string(blob.Encoding), } } if _, err := tx.ReplaceIntoRequestCancelInfoMaps(ctx, rows); err != nil { return convertCommonErrors(tx, "updateRequestCancelInfos", "Failed to execute update query.", err) } } if len(deleteInfos) > 0 { if _, err := tx.DeleteFromRequestCancelInfoMaps(ctx, &sqlplugin.RequestCancelInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, InitiatedIDs: deleteInfos, }); err != nil { return convertCommonErrors(tx, "updateRequestCancelInfos", "Failed to execute delete query.", err) } } return nil } func getRequestCancelInfoMap( ctx context.Context, db sqlplugin.DB, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, parser serialization.Parser, ) (map[int64]*persistence.RequestCancelInfo, error) { rows, err := db.SelectFromRequestCancelInfoMaps(ctx, &sqlplugin.RequestCancelInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }) if err != nil && err != sql.ErrNoRows { return nil, convertCommonErrors(db, "getRequestCancelInfoMap", "", err) } ret := make(map[int64]*persistence.RequestCancelInfo) for _, row := range rows { rowInfo, err := parser.RequestCancelInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } ret[row.InitiatedID] = &persistence.RequestCancelInfo{ Version: rowInfo.GetVersion(), InitiatedID: row.InitiatedID, InitiatedEventBatchID: rowInfo.GetInitiatedEventBatchID(), CancelRequestID: rowInfo.GetCancelRequestID(), } } return ret, nil } func deleteRequestCancelInfoMap( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) error { if _, err := tx.DeleteFromRequestCancelInfoMaps(ctx, &sqlplugin.RequestCancelInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "deleteRequestCancelInfoMap", "", err) } return nil } func updateSignalInfos( ctx context.Context, tx sqlplugin.Tx, signalInfos []*persistence.SignalInfo, deleteInfos []int64, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, parser serialization.Parser, ) error { if len(signalInfos) > 0 { rows := make([]sqlplugin.SignalInfoMapsRow, len(signalInfos)) for i, signalInfo := range signalInfos { blob, err := parser.SignalInfoToBlob(&serialization.SignalInfo{ Version: signalInfo.Version, InitiatedEventBatchID: signalInfo.InitiatedEventBatchID, RequestID: signalInfo.SignalRequestID, Name: signalInfo.SignalName, Input: signalInfo.Input, Control: signalInfo.Control, }) if err != nil { return err } rows[i] = sqlplugin.SignalInfoMapsRow{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, InitiatedID: signalInfo.InitiatedID, Data: blob.Data, DataEncoding: string(blob.Encoding), } } if _, err := tx.ReplaceIntoSignalInfoMaps(ctx, rows); err != nil { return convertCommonErrors(tx, "updateSignalInfos", "Failed to execute update query.", err) } } if len(deleteInfos) > 0 { if _, err := tx.DeleteFromSignalInfoMaps(ctx, &sqlplugin.SignalInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, InitiatedIDs: deleteInfos, }); err != nil { return convertCommonErrors(tx, "updateSignalInfos", "Failed to execute delete query.", err) } } return nil } func getSignalInfoMap( ctx context.Context, db sqlplugin.DB, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, parser serialization.Parser, ) (map[int64]*persistence.SignalInfo, error) { rows, err := db.SelectFromSignalInfoMaps(ctx, &sqlplugin.SignalInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }) if err != nil && err != sql.ErrNoRows { return nil, convertCommonErrors(db, "getSignalInfoMap", "", err) } ret := make(map[int64]*persistence.SignalInfo) for _, row := range rows { rowInfo, err := parser.SignalInfoFromBlob(row.Data, row.DataEncoding) if err != nil { return nil, err } ret[row.InitiatedID] = &persistence.SignalInfo{ Version: rowInfo.GetVersion(), InitiatedID: row.InitiatedID, InitiatedEventBatchID: rowInfo.GetInitiatedEventBatchID(), SignalRequestID: rowInfo.GetRequestID(), SignalName: rowInfo.GetName(), Input: rowInfo.GetInput(), Control: rowInfo.GetControl(), } } return ret, nil } func deleteSignalInfoMap( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) error { if _, err := tx.DeleteFromSignalInfoMaps(ctx, &sqlplugin.SignalInfoMapsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "deleteSignalInfoMap", "", err) } return nil } ================================================ FILE: common/persistence/sql/workflow_state_non_maps.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "database/sql" "github.com/uber/cadence/common/constants" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) func updateSignalsRequested( ctx context.Context, tx sqlplugin.Tx, signalRequestedIDs []string, deleteSignalRequestIDs []string, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) error { if len(signalRequestedIDs) > 0 { rows := make([]sqlplugin.SignalsRequestedSetsRow, len(signalRequestedIDs)) for i, v := range signalRequestedIDs { rows[i] = sqlplugin.SignalsRequestedSetsRow{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, SignalID: v, } } if _, err := tx.InsertIntoSignalsRequestedSets(ctx, rows); err != nil { return convertCommonErrors(tx, "updateSignalsRequested", "Failed to execute update query.", err) } } if len(deleteSignalRequestIDs) > 0 { if _, err := tx.DeleteFromSignalsRequestedSets(ctx, &sqlplugin.SignalsRequestedSetsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, SignalIDs: deleteSignalRequestIDs, }); err != nil { return convertCommonErrors(tx, "updateSignalsRequested", "Failed to execute delete query.", err) } } return nil } func getSignalsRequested( ctx context.Context, db sqlplugin.DB, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) (map[string]struct{}, error) { rows, err := db.SelectFromSignalsRequestedSets(ctx, &sqlplugin.SignalsRequestedSetsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }) if err != nil && err != sql.ErrNoRows { return nil, convertCommonErrors(db, "getSignalsRequested", "", err) } var ret = make(map[string]struct{}) for _, s := range rows { ret[s.SignalID] = struct{}{} } return ret, nil } func deleteSignalsRequestedSet( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) error { if _, err := tx.DeleteFromSignalsRequestedSets(ctx, &sqlplugin.SignalsRequestedSetsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "deleteSignalsRequestedSet", "", err) } return nil } func updateBufferedEvents( ctx context.Context, tx sqlplugin.Tx, batch *p.DataBlob, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) error { if batch == nil { return nil } row := sqlplugin.BufferedEventsRow{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, Data: batch.Data, DataEncoding: string(batch.Encoding), } if _, err := tx.InsertIntoBufferedEvents(ctx, []sqlplugin.BufferedEventsRow{row}); err != nil { return convertCommonErrors(tx, "updateBufferedEvents", "", err) } return nil } func getBufferedEvents( ctx context.Context, db sqlplugin.DB, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) ([]*p.DataBlob, error) { rows, err := db.SelectFromBufferedEvents(ctx, &sqlplugin.BufferedEventsFilter{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, }) if err != nil && err != sql.ErrNoRows { return nil, convertCommonErrors(db, "getBufferedEvents", "", err) } var result []*p.DataBlob for _, row := range rows { result = append(result, p.NewDataBlob(row.Data, constants.EncodingType(row.DataEncoding))) } return result, nil } func deleteBufferedEvents( ctx context.Context, tx sqlplugin.Tx, shardID int, domainID serialization.UUID, workflowID string, runID serialization.UUID, ) error { if _, err := tx.DeleteFromBufferedEvents(ctx, &sqlplugin.BufferedEventsFilter{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, }); err != nil { return convertCommonErrors(tx, "deleteBufferedEvents", "", err) } return nil } ================================================ FILE: common/persistence/sql/workflow_state_non_maps_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/serialization" "github.com/uber/cadence/common/persistence/sql/sqlplugin" ) func TestUpdateSignalsRequested(t *testing.T) { shardID := 1 domainID := serialization.MustParseUUID("bf2360c6-62c7-48a2-bd95-fe6254bca628") workflowID := "test" runID := serialization.MustParseUUID("bf2360c6-a2c7-48a2-bd95-fe6254bca628") testCases := []struct { name string signalRequestedIDs []string deleteSignalRequestIDs []string mockSetup func(*sqlplugin.MockTx) wantErr bool }{ { name: "Success case", signalRequestedIDs: []string{"s1", "s2"}, deleteSignalRequestIDs: []string{"s3", "s4"}, mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().InsertIntoSignalsRequestedSets(gomock.Any(), []sqlplugin.SignalsRequestedSetsRow{ { ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, SignalID: "s1", }, { ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, SignalID: "s2", }, }).Return(nil, nil) mockTx.EXPECT().DeleteFromSignalsRequestedSets(gomock.Any(), &sqlplugin.SignalsRequestedSetsFilter{ ShardID: int64(shardID), DomainID: domainID, WorkflowID: workflowID, RunID: runID, SignalIDs: []string{"s3", "s4"}, }).Return(nil, nil) }, wantErr: false, }, { name: "Error case - failed to insert", signalRequestedIDs: []string{"s1", "s2"}, deleteSignalRequestIDs: []string{"s3", "s4"}, mockSetup: func(mockTx *sqlplugin.MockTx) { err := errors.New("some error") mockTx.EXPECT().InsertIntoSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, { name: "Error case - failed to delete", signalRequestedIDs: []string{"s1", "s2"}, deleteSignalRequestIDs: []string{"s3", "s4"}, mockSetup: func(mockTx *sqlplugin.MockTx) { err := errors.New("some error") mockTx.EXPECT().InsertIntoSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, nil) mockTx.EXPECT().DeleteFromSignalsRequestedSets(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockTx := sqlplugin.NewMockTx(ctrl) tc.mockSetup(mockTx) err := updateSignalsRequested(context.Background(), mockTx, tc.signalRequestedIDs, tc.deleteSignalRequestIDs, shardID, domainID, workflowID, runID) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } func TestUpdateBufferedEvents(t *testing.T) { shardID := 1 domainID := serialization.MustParseUUID("bf2360c6-62c7-48a2-bd95-fe6254bca628") workflowID := "test" runID := serialization.MustParseUUID("bf2360c6-a2c7-48a2-bd95-fe6254bca628") testCases := []struct { name string batch *persistence.DataBlob mockSetup func(*sqlplugin.MockTx) wantErr bool }{ { name: "Success case", batch: &persistence.DataBlob{Data: []byte(`buffer`), Encoding: constants.EncodingType("buffer")}, mockSetup: func(mockTx *sqlplugin.MockTx) { mockTx.EXPECT().InsertIntoBufferedEvents(gomock.Any(), []sqlplugin.BufferedEventsRow{ { ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, Data: []byte(`buffer`), DataEncoding: "buffer", }, }).Return(nil, nil) }, wantErr: false, }, { name: "Error case", batch: &persistence.DataBlob{Data: []byte(`buffer`), Encoding: constants.EncodingType("buffer")}, mockSetup: func(mockTx *sqlplugin.MockTx) { err := errors.New("some error") mockTx.EXPECT().InsertIntoBufferedEvents(gomock.Any(), gomock.Any()).Return(nil, err) mockTx.EXPECT().IsNotFoundError(err).Return(true) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockTx := sqlplugin.NewMockTx(ctrl) tc.mockSetup(mockTx) err := updateBufferedEvents(context.Background(), mockTx, tc.batch, shardID, domainID, workflowID, runID) if tc.wantErr { assert.Error(t, err, "Expected an error for test case") } else { assert.NoError(t, err, "Did not expect an error for test case") } }) } } ================================================ FILE: common/persistence/statsComputer.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence type ( // statsComputer is to computing struct sizes after serialization statsComputer struct{} ) func (sc *statsComputer) computeMutableStateStats(req *InternalGetWorkflowExecutionResponse) *MutableStateStats { executionInfoSize := computeExecutionInfoSize(req.State.ExecutionInfo) activityInfoCount := 0 activityInfoSize := 0 for _, ai := range req.State.ActivityInfos { activityInfoCount++ activityInfoSize += computeActivityInfoSize(ai) } timerInfoCount := 0 timerInfoSize := 0 for _, ti := range req.State.TimerInfos { timerInfoCount++ timerInfoSize += computeTimerInfoSize(ti) } childExecutionInfoCount := 0 childExecutionInfoSize := 0 for _, ci := range req.State.ChildExecutionInfos { childExecutionInfoCount++ childExecutionInfoSize += computeChildInfoSize(ci) } signalInfoCount := 0 signalInfoSize := 0 for _, si := range req.State.SignalInfos { signalInfoCount++ signalInfoSize += computeSignalInfoSize(si) } bufferedEventsCount := 0 bufferedEventsSize := 0 for _, be := range req.State.BufferedEvents { bufferedEventsCount++ bufferedEventsSize += len(be.Data) } requestCancelInfoCount := len(req.State.RequestCancelInfos) totalSize := executionInfoSize totalSize += activityInfoSize totalSize += timerInfoSize totalSize += childExecutionInfoSize totalSize += signalInfoSize totalSize += bufferedEventsSize return &MutableStateStats{ MutableStateSize: totalSize, ExecutionInfoSize: executionInfoSize, ActivityInfoSize: activityInfoSize, TimerInfoSize: timerInfoSize, ChildInfoSize: childExecutionInfoSize, SignalInfoSize: signalInfoSize, BufferedEventsSize: bufferedEventsSize, ActivityInfoCount: activityInfoCount, TimerInfoCount: timerInfoCount, ChildInfoCount: childExecutionInfoCount, SignalInfoCount: signalInfoCount, BufferedEventsCount: bufferedEventsCount, RequestCancelInfoCount: requestCancelInfoCount, } } func (sc *statsComputer) computeMutableStateUpdateStats(req *InternalUpdateWorkflowExecutionRequest) *MutableStateUpdateSessionStats { if req.NewWorkflowSnapshot != nil { return mergeMutableStateUpdateSessionStats(sc.computeWorkflowMutationStats(&req.UpdateWorkflowMutation), sc.computeWorkflowSnapshotStats(req.NewWorkflowSnapshot)) } return sc.computeWorkflowMutationStats(&req.UpdateWorkflowMutation) } func (sc *statsComputer) computeMutableStateCreateStats(req *InternalCreateWorkflowExecutionRequest) *MutableStateUpdateSessionStats { return sc.computeWorkflowSnapshotStats(&req.NewWorkflowSnapshot) } func (sc *statsComputer) computeMutableStateConflictResolveStats(req *InternalConflictResolveWorkflowExecutionRequest) *MutableStateUpdateSessionStats { mss := sc.computeWorkflowSnapshotStats(&req.ResetWorkflowSnapshot) if req.NewWorkflowSnapshot != nil { mss = mergeMutableStateUpdateSessionStats(mss, sc.computeWorkflowSnapshotStats(req.NewWorkflowSnapshot)) } if req.CurrentWorkflowMutation != nil { mss = mergeMutableStateUpdateSessionStats(mss, sc.computeWorkflowMutationStats(req.CurrentWorkflowMutation)) } return mss } func (sc *statsComputer) computeWorkflowMutationStats(req *InternalWorkflowMutation) *MutableStateUpdateSessionStats { executionInfoSize := computeExecutionInfoSize(req.ExecutionInfo) activityInfoCount := 0 activityInfoSize := 0 for _, ai := range req.UpsertActivityInfos { activityInfoCount++ activityInfoSize += computeActivityInfoSize(ai) } timerInfoCount := 0 timerInfoSize := 0 for _, ti := range req.UpsertTimerInfos { timerInfoCount++ timerInfoSize += computeTimerInfoSize(ti) } childExecutionInfoCount := 0 childExecutionInfoSize := 0 for _, ci := range req.UpsertChildExecutionInfos { childExecutionInfoCount++ childExecutionInfoSize += computeChildInfoSize(ci) } signalInfoCount := 0 signalInfoSize := 0 for _, si := range req.UpsertSignalInfos { signalInfoCount++ signalInfoSize += computeSignalInfoSize(si) } bufferedEventsSize := 0 if req.NewBufferedEvents != nil { bufferedEventsSize = len(req.NewBufferedEvents.Data) } requestCancelInfoCount := len(req.UpsertRequestCancelInfos) deleteActivityInfoCount := len(req.DeleteActivityInfos) deleteTimerInfoCount := len(req.DeleteTimerInfos) deleteChildInfoCount := len(req.DeleteChildExecutionInfos) deleteSignalInfoCount := len(req.DeleteSignalInfos) deleteRequestCancelInfoCount := len(req.DeleteRequestCancelInfos) taskCountByCategory := computeTaskCountByCategory(req.TasksByCategory) totalSize := executionInfoSize totalSize += activityInfoSize totalSize += timerInfoSize totalSize += childExecutionInfoSize totalSize += signalInfoSize totalSize += bufferedEventsSize return &MutableStateUpdateSessionStats{ MutableStateSize: totalSize, ExecutionInfoSize: executionInfoSize, ActivityInfoSize: activityInfoSize, TimerInfoSize: timerInfoSize, ChildInfoSize: childExecutionInfoSize, SignalInfoSize: signalInfoSize, BufferedEventsSize: bufferedEventsSize, ActivityInfoCount: activityInfoCount, TimerInfoCount: timerInfoCount, ChildInfoCount: childExecutionInfoCount, SignalInfoCount: signalInfoCount, RequestCancelInfoCount: requestCancelInfoCount, DeleteActivityInfoCount: deleteActivityInfoCount, DeleteTimerInfoCount: deleteTimerInfoCount, DeleteChildInfoCount: deleteChildInfoCount, DeleteSignalInfoCount: deleteSignalInfoCount, DeleteRequestCancelInfoCount: deleteRequestCancelInfoCount, TaskCountByCategory: taskCountByCategory, } } func (sc *statsComputer) computeWorkflowSnapshotStats(req *InternalWorkflowSnapshot) *MutableStateUpdateSessionStats { executionInfoSize := computeExecutionInfoSize(req.ExecutionInfo) activityInfoCount := 0 activityInfoSize := 0 for _, ai := range req.ActivityInfos { activityInfoCount++ activityInfoSize += computeActivityInfoSize(ai) } timerInfoCount := 0 timerInfoSize := 0 for _, ti := range req.TimerInfos { timerInfoCount++ timerInfoSize += computeTimerInfoSize(ti) } childExecutionInfoCount := 0 childExecutionInfoSize := 0 for _, ci := range req.ChildExecutionInfos { childExecutionInfoCount++ childExecutionInfoSize += computeChildInfoSize(ci) } signalInfoCount := 0 signalInfoSize := 0 for _, si := range req.SignalInfos { signalInfoCount++ signalInfoSize += computeSignalInfoSize(si) } requestCancelInfoCount := len(req.RequestCancelInfos) taskCountByCategory := computeTaskCountByCategory(req.TasksByCategory) totalSize := executionInfoSize totalSize += activityInfoSize totalSize += timerInfoSize totalSize += childExecutionInfoSize totalSize += signalInfoSize return &MutableStateUpdateSessionStats{ MutableStateSize: totalSize, ExecutionInfoSize: executionInfoSize, ActivityInfoSize: activityInfoSize, TimerInfoSize: timerInfoSize, ChildInfoSize: childExecutionInfoSize, SignalInfoSize: signalInfoSize, ActivityInfoCount: activityInfoCount, TimerInfoCount: timerInfoCount, ChildInfoCount: childExecutionInfoCount, SignalInfoCount: signalInfoCount, RequestCancelInfoCount: requestCancelInfoCount, TaskCountByCategory: taskCountByCategory, } } func mergeMutableStateUpdateSessionStats(stats ...*MutableStateUpdateSessionStats) *MutableStateUpdateSessionStats { result := &MutableStateUpdateSessionStats{ TaskCountByCategory: make(map[HistoryTaskCategory]int), } for _, s := range stats { result.MutableStateSize += s.MutableStateSize result.ExecutionInfoSize += s.ExecutionInfoSize result.ActivityInfoSize += s.ActivityInfoSize result.TimerInfoSize += s.TimerInfoSize result.ChildInfoSize += s.ChildInfoSize result.SignalInfoSize += s.SignalInfoSize result.BufferedEventsSize += s.BufferedEventsSize result.ActivityInfoCount += s.ActivityInfoCount result.TimerInfoCount += s.TimerInfoCount result.ChildInfoCount += s.ChildInfoCount result.SignalInfoCount += s.SignalInfoCount result.RequestCancelInfoCount += s.RequestCancelInfoCount result.DeleteActivityInfoCount += s.DeleteActivityInfoCount result.DeleteTimerInfoCount += s.DeleteTimerInfoCount result.DeleteChildInfoCount += s.DeleteChildInfoCount result.DeleteSignalInfoCount += s.DeleteSignalInfoCount result.DeleteRequestCancelInfoCount += s.DeleteRequestCancelInfoCount for k, v := range s.TaskCountByCategory { result.TaskCountByCategory[k] += v } } return result } func computeExecutionInfoSize(executionInfo *InternalWorkflowExecutionInfo) int { size := len(executionInfo.WorkflowID) size += len(executionInfo.TaskList) size += len(executionInfo.WorkflowTypeName) size += len(executionInfo.ParentWorkflowID) return size } func computeActivityInfoSize(ai *InternalActivityInfo) int { size := len(ai.ActivityID) if ai.ScheduledEvent != nil { size += len(ai.ScheduledEvent.Data) } if ai.StartedEvent != nil { size += len(ai.StartedEvent.Data) } size += len(ai.Details) return size } func computeTimerInfoSize(ti *TimerInfo) int { size := len(ti.TimerID) return size } func computeChildInfoSize(ci *InternalChildExecutionInfo) int { size := 0 if ci.InitiatedEvent != nil { size += len(ci.InitiatedEvent.Data) } if ci.StartedEvent != nil { size += len(ci.StartedEvent.Data) } return size } func computeSignalInfoSize(si *SignalInfo) int { size := len(si.SignalName) size += len(si.Input) size += len(si.Control) return size } func computeTaskCountByCategory(tasks map[HistoryTaskCategory][]Task) map[HistoryTaskCategory]int { taskCountByCategory := make(map[HistoryTaskCategory]int, len(tasks)) for k, v := range tasks { taskCountByCategory[k] = len(v) } return taskCountByCategory } ================================================ FILE: common/persistence/statsComputer_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" ) type ( statsComputerSuite struct { sc *statsComputer suite.Suite // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions } ) func TestStatsComputerSuite(t *testing.T) { s := new(statsComputerSuite) suite.Run(t, s) } // TODO need to add more tests func (s *statsComputerSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) s.sc = &statsComputer{} } func (s *statsComputerSuite) createRequest() *InternalUpdateWorkflowExecutionRequest { return &InternalUpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: InternalWorkflowMutation{ ExecutionInfo: &InternalWorkflowExecutionInfo{}, }, } } func (s *statsComputerSuite) TestStatsWithStartedEvent() { ms := s.createRequest() domainID := "A" execution := workflow.WorkflowExecution{ WorkflowId: common.StringPtr("test-workflow-id"), RunId: common.StringPtr("run_id"), } workflowType := &workflow.WorkflowType{ Name: common.StringPtr("test-workflow-type-name"), } taskList := &workflow.TaskList{ Name: common.StringPtr("test-tasklist"), } ms.UpdateWorkflowMutation.ExecutionInfo.DomainID = domainID ms.UpdateWorkflowMutation.ExecutionInfo.WorkflowID = execution.GetWorkflowId() ms.UpdateWorkflowMutation.ExecutionInfo.RunID = execution.GetRunId() ms.UpdateWorkflowMutation.ExecutionInfo.WorkflowTypeName = workflowType.GetName() ms.UpdateWorkflowMutation.ExecutionInfo.TaskList = taskList.GetName() expectedSize := len(execution.GetWorkflowId()) + len(workflowType.GetName()) + len(taskList.GetName()) stats := s.sc.computeMutableStateUpdateStats(ms) s.Equal(stats.ExecutionInfoSize, expectedSize) } func (s *statsComputerSuite) TestComputeWorkflowMutationStats() { a1 := &InternalActivityInfo{} t1 := &TimerInfo{} c1 := &InternalChildExecutionInfo{} s1 := &SignalInfo{} r1 := &RequestCancelInfo{} ms := &InternalWorkflowMutation{ ExecutionInfo: &InternalWorkflowExecutionInfo{}, UpsertActivityInfos: []*InternalActivityInfo{a1, a1}, UpsertTimerInfos: []*TimerInfo{t1, t1, t1}, UpsertChildExecutionInfos: []*InternalChildExecutionInfo{c1, c1, c1, c1}, UpsertSignalInfos: []*SignalInfo{s1}, UpsertRequestCancelInfos: []*RequestCancelInfo{r1}, NewBufferedEvents: &DataBlob{Data: []byte("asdfsaf")}, DeleteActivityInfos: []int64{1, 2, 3, 4}, DeleteTimerInfos: []string{"asdfa"}, DeleteChildExecutionInfos: []int64{0}, DeleteSignalInfos: nil, DeleteRequestCancelInfos: []int64{}, TasksByCategory: map[HistoryTaskCategory][]Task{ HistoryTaskCategoryTransfer: []Task{ &ActivityTask{}, &DecisionTask{}, }, HistoryTaskCategoryTimer: []Task{ &UserTimerTask{}, }, }, } stats := s.sc.computeWorkflowMutationStats(ms) s.Equal(computeExecutionInfoSize(ms.ExecutionInfo), stats.ExecutionInfoSize) s.Equal(computeActivityInfoSize(a1)*len(ms.UpsertActivityInfos), stats.ActivityInfoSize) s.Equal(len(ms.UpsertActivityInfos), stats.ActivityInfoCount) s.Equal(computeTimerInfoSize(t1)*len(ms.UpsertTimerInfos), stats.TimerInfoSize) s.Equal(len(ms.UpsertTimerInfos), stats.TimerInfoCount) s.Equal(computeChildInfoSize(c1)*len(ms.UpsertChildExecutionInfos), stats.ChildInfoSize) s.Equal(len(ms.UpsertChildExecutionInfos), stats.ChildInfoCount) s.Equal(computeSignalInfoSize(s1)*len(ms.UpsertSignalInfos), stats.SignalInfoSize) s.Equal(len(ms.UpsertSignalInfos), stats.SignalInfoCount) s.Equal(len(ms.UpsertRequestCancelInfos), stats.RequestCancelInfoCount) s.Equal(len(ms.NewBufferedEvents.Data), stats.BufferedEventsSize) s.Equal(len(ms.DeleteActivityInfos), stats.DeleteActivityInfoCount) s.Equal(len(ms.DeleteTimerInfos), stats.DeleteTimerInfoCount) s.Equal(len(ms.DeleteChildExecutionInfos), stats.DeleteChildInfoCount) s.Equal(len(ms.DeleteSignalInfos), stats.DeleteSignalInfoCount) s.Equal(len(ms.DeleteRequestCancelInfos), stats.DeleteRequestCancelInfoCount) s.Equal(stats.ExecutionInfoSize+stats.ActivityInfoSize+stats.TimerInfoSize+stats.ChildInfoSize+stats.SignalInfoSize+stats.BufferedEventsSize, stats.MutableStateSize) for c, tasks := range ms.TasksByCategory { s.Equal(len(tasks), stats.TaskCountByCategory[c]) } } func (s *statsComputerSuite) TestComputeWorkflowSnapshotStats() { a1 := &InternalActivityInfo{} t1 := &TimerInfo{} c1 := &InternalChildExecutionInfo{} s1 := &SignalInfo{} r1 := &RequestCancelInfo{} ms := &InternalWorkflowSnapshot{ ExecutionInfo: &InternalWorkflowExecutionInfo{}, ActivityInfos: []*InternalActivityInfo{a1, a1}, TimerInfos: []*TimerInfo{t1, t1, t1}, ChildExecutionInfos: []*InternalChildExecutionInfo{c1, c1, c1, c1}, SignalInfos: []*SignalInfo{s1}, RequestCancelInfos: []*RequestCancelInfo{r1}, TasksByCategory: map[HistoryTaskCategory][]Task{ HistoryTaskCategoryTransfer: []Task{ &ActivityTask{}, &DecisionTask{}, }, HistoryTaskCategoryTimer: []Task{ &UserTimerTask{}, }, }, } stats := s.sc.computeWorkflowSnapshotStats(ms) s.Equal(computeExecutionInfoSize(ms.ExecutionInfo), stats.ExecutionInfoSize) s.Equal(computeActivityInfoSize(a1)*len(ms.ActivityInfos), stats.ActivityInfoSize) s.Equal(len(ms.ActivityInfos), stats.ActivityInfoCount) s.Equal(computeTimerInfoSize(t1)*len(ms.TimerInfos), stats.TimerInfoSize) s.Equal(len(ms.TimerInfos), stats.TimerInfoCount) s.Equal(computeChildInfoSize(c1)*len(ms.ChildExecutionInfos), stats.ChildInfoSize) s.Equal(len(ms.ChildExecutionInfos), stats.ChildInfoCount) s.Equal(computeSignalInfoSize(s1)*len(ms.SignalInfos), stats.SignalInfoSize) s.Equal(len(ms.SignalInfos), stats.SignalInfoCount) s.Equal(len(ms.RequestCancelInfos), stats.RequestCancelInfoCount) s.Equal(stats.ExecutionInfoSize+stats.ActivityInfoSize+stats.TimerInfoSize+stats.ChildInfoSize+stats.SignalInfoSize+stats.BufferedEventsSize, stats.MutableStateSize) for c, tasks := range ms.TasksByCategory { s.Equal(len(tasks), stats.TaskCountByCategory[c]) } } ================================================ FILE: common/persistence/task_manager.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "github.com/uber/cadence/common/clock" ) type ( taskManager struct { persistence TaskStore timeSrc clock.TimeSource } ) var _ TaskManager = (*taskManager)(nil) // NewTaskManager returns a new TaskManager func NewTaskManager( persistence TaskStore, ) TaskManager { return &taskManager{ persistence: persistence, timeSrc: clock.NewRealTimeSource(), } } func (t *taskManager) GetName() string { return t.persistence.GetName() } func (t *taskManager) Close() { t.persistence.Close() } func (t *taskManager) LeaseTaskList(ctx context.Context, request *LeaseTaskListRequest) (*LeaseTaskListResponse, error) { request.CurrentTimeStamp = t.timeSrc.Now() return t.persistence.LeaseTaskList(ctx, request) } func (t *taskManager) GetTaskList(ctx context.Context, request *GetTaskListRequest) (*GetTaskListResponse, error) { return t.persistence.GetTaskList(ctx, request) } func (t *taskManager) UpdateTaskList(ctx context.Context, request *UpdateTaskListRequest) (*UpdateTaskListResponse, error) { request.CurrentTimeStamp = t.timeSrc.Now() return t.persistence.UpdateTaskList(ctx, request) } func (t *taskManager) ListTaskList(ctx context.Context, request *ListTaskListRequest) (*ListTaskListResponse, error) { return t.persistence.ListTaskList(ctx, request) } func (t *taskManager) DeleteTaskList(ctx context.Context, request *DeleteTaskListRequest) error { return t.persistence.DeleteTaskList(ctx, request) } func (t *taskManager) GetTaskListSize(ctx context.Context, request *GetTaskListSizeRequest) (*GetTaskListSizeResponse, error) { return t.persistence.GetTaskListSize(ctx, request) } func (t *taskManager) CreateTasks(ctx context.Context, request *CreateTasksRequest) (*CreateTasksResponse, error) { request.CurrentTimeStamp = t.timeSrc.Now() return t.persistence.CreateTasks(ctx, request) } func (t *taskManager) GetTasks(ctx context.Context, request *GetTasksRequest) (*GetTasksResponse, error) { return t.persistence.GetTasks(ctx, request) } func (t *taskManager) CompleteTask(ctx context.Context, request *CompleteTaskRequest) error { return t.persistence.CompleteTask(ctx, request) } func (t *taskManager) CompleteTasksLessThan(ctx context.Context, request *CompleteTasksLessThanRequest) (*CompleteTasksLessThanResponse, error) { return t.persistence.CompleteTasksLessThan(ctx, request) } func (t *taskManager) GetOrphanTasks(ctx context.Context, request *GetOrphanTasksRequest) (*GetOrphanTasksResponse, error) { return t.persistence.GetOrphanTasks(ctx, request) } ================================================ FILE: common/persistence/task_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) func setUpMocksForTaskManager(t *testing.T) (*taskManager, *MockTaskStore) { ctrl := gomock.NewController(t) mockTaskStore := NewMockTaskStore(ctrl) taskManager := NewTaskManager(mockTaskStore).(*taskManager) return taskManager, mockTaskStore } func TestTaskManager_GetName(t *testing.T) { t.Run("get name", func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) // Set up expectation mockTaskStore.EXPECT().GetName().Return("mock-task-store").Times(1) // Call the method name := taskManager.GetName() // Validate the result assert.Equal(t, "mock-task-store", name) }) } func TestTaskManager_Close(t *testing.T) { t.Run("close task store", func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) // Set up expectation mockTaskStore.EXPECT().Close().Times(1) // Call the method taskManager.Close() // No need to assert, just ensuring method is called }) } func TestTaskManager_LeaseTaskList(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *LeaseTaskListRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). LeaseTaskList(gomock.Any(), gomock.Any()). Return(&LeaseTaskListResponse{}, nil).Times(1) }, request: &LeaseTaskListRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). LeaseTaskList(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &LeaseTaskListRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method _, err := taskManager.LeaseTaskList(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestTaskManager_GetTaskList(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *GetTaskListRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). GetTaskList(gomock.Any(), gomock.Any()). Return(&GetTaskListResponse{}, nil).Times(1) }, request: &GetTaskListRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). GetTaskList(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &GetTaskListRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method _, err := taskManager.GetTaskList(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestTaskManager_UpdateTaskList(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *UpdateTaskListRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). UpdateTaskList(gomock.Any(), gomock.Any()). Return(&UpdateTaskListResponse{}, nil).Times(1) }, request: &UpdateTaskListRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). UpdateTaskList(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &UpdateTaskListRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method _, err := taskManager.UpdateTaskList(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestTaskManager_ListTaskList(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *ListTaskListRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). ListTaskList(gomock.Any(), gomock.Any()). Return(&ListTaskListResponse{}, nil).Times(1) }, request: &ListTaskListRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). ListTaskList(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &ListTaskListRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method _, err := taskManager.ListTaskList(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestTaskManager_DeleteTaskList(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *DeleteTaskListRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). DeleteTaskList(gomock.Any(), gomock.Any()). Return(nil).Times(1) }, request: &DeleteTaskListRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). DeleteTaskList(gomock.Any(), gomock.Any()). Return(errors.New("persistence error")).Times(1) }, request: &DeleteTaskListRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method err := taskManager.DeleteTaskList(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestTaskManager_GetTaskListSize(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *GetTaskListSizeRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). GetTaskListSize(gomock.Any(), gomock.Any()). Return(&GetTaskListSizeResponse{}, nil).Times(1) }, request: &GetTaskListSizeRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). GetTaskListSize(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &GetTaskListSizeRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method _, err := taskManager.GetTaskListSize(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestTaskManager_CreateTasks(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *CreateTasksRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). CreateTasks(gomock.Any(), gomock.Any()). Return(&CreateTasksResponse{}, nil).Times(1) }, request: &CreateTasksRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). CreateTasks(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &CreateTasksRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method _, err := taskManager.CreateTasks(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestTaskManager_GetTasks(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *GetTasksRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). GetTasks(gomock.Any(), gomock.Any()). Return(&GetTasksResponse{}, nil).Times(1) }, request: &GetTasksRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). GetTasks(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &GetTasksRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method _, err := taskManager.GetTasks(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestTaskManager_CompleteTask(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *CompleteTaskRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). CompleteTask(gomock.Any(), gomock.Any()). Return(nil).Times(1) }, request: &CompleteTaskRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). CompleteTask(gomock.Any(), gomock.Any()). Return(errors.New("persistence error")).Times(1) }, request: &CompleteTaskRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method err := taskManager.CompleteTask(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestTaskManager_CompleteTasksLessThan(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *CompleteTasksLessThanRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). CompleteTasksLessThan(gomock.Any(), gomock.Any()). Return(&CompleteTasksLessThanResponse{}, nil).Times(1) }, request: &CompleteTasksLessThanRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). CompleteTasksLessThan(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &CompleteTasksLessThanRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method _, err := taskManager.CompleteTasksLessThan(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestTaskManager_GetOrphanTasks(t *testing.T) { testCases := []struct { name string setupMock func(*MockTaskStore) request *GetOrphanTasksRequest expectError bool expectedError string }{ { name: "success", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). GetOrphanTasks(gomock.Any(), gomock.Any()). Return(&GetOrphanTasksResponse{}, nil).Times(1) }, request: &GetOrphanTasksRequest{}, expectError: false, }, { name: "persistence error", setupMock: func(mockTaskStore *MockTaskStore) { mockTaskStore.EXPECT(). GetOrphanTasks(gomock.Any(), gomock.Any()). Return(nil, errors.New("persistence error")).Times(1) }, request: &GetOrphanTasksRequest{}, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskManager, mockTaskStore := setUpMocksForTaskManager(t) tc.setupMock(mockTaskStore) // Call the method _, err := taskManager.GetOrphanTasks(context.Background(), tc.request) // Validate the result if tc.expectError { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: common/persistence/tasks.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "encoding/json" "fmt" "math" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) // Task is the generic interface for workflow tasks type Task interface { GetTaskCategory() HistoryTaskCategory GetTaskKey() HistoryTaskKey GetTaskType() int GetDomainID() string GetWorkflowID() string GetRunID() string // GetTaskList returns the name of the task list the task is currently // associated with. This may differ from the original task list if the // task is a sticky decision task. GetTaskList() string // GetOriginalTaskList returns the task list on which the task was initially // scheduled. It is used to enforce rate limits and ensure fair scheduling // across task lists. GetOriginalTaskList() string GetOriginalTaskListKind() types.TaskListKind GetVersion() int64 SetVersion(version int64) GetTaskID() int64 SetTaskID(id int64) GetVisibilityTimestamp() time.Time SetVisibilityTimestamp(timestamp time.Time) ByteSize() uint64 ToTransferTaskInfo() (*TransferTaskInfo, error) ToTimerTaskInfo() (*TimerTaskInfo, error) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) } var ( MaximumHistoryTaskKey = HistoryTaskKey{ scheduledTime: time.Unix(0, math.MaxInt64), taskID: math.MaxInt64, } ) type ( HistoryTaskKey struct { scheduledTime time.Time taskID int64 } WorkflowIdentifier struct { DomainID string WorkflowID string RunID string } // TaskData is common attributes for all tasks. TaskData struct { Version int64 TaskID int64 VisibilityTimestamp time.Time } // ActivityTask identifies a transfer task for activity ActivityTask struct { WorkflowIdentifier TaskData TargetDomainID string TaskList string ScheduleID int64 } // DecisionTask identifies a transfer task for decision DecisionTask struct { WorkflowIdentifier TaskData TargetDomainID string TaskList string ScheduleID int64 OriginalTaskList string OriginalTaskListKind types.TaskListKind } // RecordWorkflowStartedTask identifites a transfer task for writing visibility open execution record RecordWorkflowStartedTask struct { WorkflowIdentifier TaskData TaskList string } // ResetWorkflowTask identifites a transfer task to reset workflow ResetWorkflowTask struct { WorkflowIdentifier TaskData TaskList string } // CloseExecutionTask identifies a transfer task for deletion of execution CloseExecutionTask struct { WorkflowIdentifier TaskData TaskList string } // DeleteHistoryEventTask identifies a timer task for deletion of history events of completed execution. DeleteHistoryEventTask struct { WorkflowIdentifier TaskData TaskList string } // DecisionTimeoutTask identifies a timeout task. DecisionTimeoutTask struct { WorkflowIdentifier TaskData EventID int64 ScheduleAttempt int64 TimeoutType int TaskList string } // WorkflowTimeoutTask identifies a timeout task. WorkflowTimeoutTask struct { WorkflowIdentifier TaskData TaskList string } // CancelExecutionTask identifies a transfer task for cancel of execution CancelExecutionTask struct { WorkflowIdentifier TaskData TargetDomainID string TargetWorkflowID string TargetRunID string TargetChildWorkflowOnly bool InitiatedID int64 TaskList string } // SignalExecutionTask identifies a transfer task for signal execution SignalExecutionTask struct { WorkflowIdentifier TaskData TargetDomainID string TargetWorkflowID string TargetRunID string TargetChildWorkflowOnly bool InitiatedID int64 TaskList string } // UpsertWorkflowSearchAttributesTask identifies a transfer task for upsert search attributes UpsertWorkflowSearchAttributesTask struct { WorkflowIdentifier TaskData TaskList string } // StartChildExecutionTask identifies a transfer task for starting child execution StartChildExecutionTask struct { WorkflowIdentifier TaskData TargetDomainID string TargetWorkflowID string InitiatedID int64 TaskList string } // RecordWorkflowClosedTask identifies a transfer task for writing visibility close execution record RecordWorkflowClosedTask struct { WorkflowIdentifier TaskData TaskList string } // RecordChildExecutionCompletedTask identifies a task for recording the competion of a child workflow RecordChildExecutionCompletedTask struct { WorkflowIdentifier TaskData TargetDomainID string TargetWorkflowID string TargetRunID string TaskList string } // ActivityTimeoutTask identifies a timeout task. ActivityTimeoutTask struct { WorkflowIdentifier TaskData TimeoutType int EventID int64 Attempt int64 TaskList string } // UserTimerTask identifies a timeout task. UserTimerTask struct { WorkflowIdentifier TaskData EventID int64 TaskList string } // ActivityRetryTimerTask to schedule a retry task for activity ActivityRetryTimerTask struct { WorkflowIdentifier TaskData EventID int64 Attempt int64 TaskList string } // WorkflowBackoffTimerTask to schedule first decision task for retried workflow WorkflowBackoffTimerTask struct { WorkflowIdentifier TaskData TimeoutType int // 0 for retry, 1 for cron. TaskList string } // HistoryReplicationTask is the replication task created for shipping history replication events to other clusters HistoryReplicationTask struct { WorkflowIdentifier TaskData FirstEventID int64 NextEventID int64 BranchToken []byte NewRunBranchToken []byte } // SyncActivityTask is the replication task created for shipping activity info to other clusters SyncActivityTask struct { WorkflowIdentifier TaskData ScheduledID int64 } // FailoverMarkerTask is the marker for graceful failover FailoverMarkerTask struct { TaskData DomainID string } ) // assert all task types implements Task interface var ( _ Task = (*ActivityTask)(nil) _ Task = (*DecisionTask)(nil) _ Task = (*RecordWorkflowStartedTask)(nil) _ Task = (*ResetWorkflowTask)(nil) _ Task = (*CloseExecutionTask)(nil) _ Task = (*DeleteHistoryEventTask)(nil) _ Task = (*DecisionTimeoutTask)(nil) _ Task = (*WorkflowTimeoutTask)(nil) _ Task = (*CancelExecutionTask)(nil) _ Task = (*SignalExecutionTask)(nil) _ Task = (*RecordChildExecutionCompletedTask)(nil) _ Task = (*UpsertWorkflowSearchAttributesTask)(nil) _ Task = (*StartChildExecutionTask)(nil) _ Task = (*RecordWorkflowClosedTask)(nil) _ Task = (*ActivityTimeoutTask)(nil) _ Task = (*UserTimerTask)(nil) _ Task = (*ActivityRetryTimerTask)(nil) _ Task = (*WorkflowBackoffTimerTask)(nil) _ Task = (*HistoryReplicationTask)(nil) _ Task = (*SyncActivityTask)(nil) _ Task = (*FailoverMarkerTask)(nil) immediateTaskKeyScheduleTime = time.Unix(0, 0).UTC() ) func IsTaskCorrupted(task Task) bool { switch task.(type) { case *FailoverMarkerTask: return task.GetDomainID() == "" default: return task.GetDomainID() == "" || task.GetWorkflowID() == "" || task.GetRunID() == "" } } func NewImmediateTaskKey(taskID int64) HistoryTaskKey { return HistoryTaskKey{ scheduledTime: immediateTaskKeyScheduleTime, taskID: taskID, } } func NewHistoryTaskKey(scheduledTime time.Time, taskID int64) HistoryTaskKey { return HistoryTaskKey{ scheduledTime: scheduledTime, taskID: taskID, } } func (a HistoryTaskKey) GetTaskID() int64 { return a.taskID } func (a HistoryTaskKey) GetScheduledTime() time.Time { return a.scheduledTime } func (a HistoryTaskKey) Compare(b HistoryTaskKey) int { if a.scheduledTime.Before(b.scheduledTime) { return -1 } else if a.scheduledTime.After(b.scheduledTime) { return 1 } if a.taskID < b.taskID { return -1 } else if a.taskID > b.taskID { return 1 } return 0 } func (a HistoryTaskKey) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]interface{}{ "scheduledTime": a.scheduledTime.UTC(), "taskID": a.taskID, }) } func (a HistoryTaskKey) Next() HistoryTaskKey { if a.taskID == math.MaxInt64 { return HistoryTaskKey{ scheduledTime: a.scheduledTime.Add(time.Nanosecond), taskID: 0, } } return HistoryTaskKey{ scheduledTime: a.scheduledTime, taskID: a.taskID + 1, } } func MinHistoryTaskKey(a, b HistoryTaskKey) HistoryTaskKey { if a.Compare(b) < 0 { return a } return b } func MaxHistoryTaskKey(a, b HistoryTaskKey) HistoryTaskKey { if a.Compare(b) > 0 { return a } return b } func (a *WorkflowIdentifier) GetDomainID() string { return a.DomainID } func (a *WorkflowIdentifier) GetWorkflowID() string { return a.WorkflowID } func (a *WorkflowIdentifier) GetRunID() string { return a.RunID } func (a *WorkflowIdentifier) ByteSize() uint64 { return uint64(len(a.DomainID) + len(a.WorkflowID) + len(a.RunID)) } // GetVersion returns the version of the task func (a *TaskData) GetVersion() int64 { return a.Version } // SetVersion sets the version of the task func (a *TaskData) SetVersion(version int64) { a.Version = version } // GetTaskID returns the sequence ID of the task func (a *TaskData) GetTaskID() int64 { return a.TaskID } // SetTaskID sets the sequence ID of the task func (a *TaskData) SetTaskID(id int64) { a.TaskID = id } // GetVisibilityTimestamp get the visibility timestamp func (a *TaskData) GetVisibilityTimestamp() time.Time { return a.VisibilityTimestamp } // SetVisibilityTimestamp set the visibility timestamp func (a *TaskData) SetVisibilityTimestamp(timestamp time.Time) { a.VisibilityTimestamp = timestamp } func (a *TaskData) ByteSize() uint64 { return uint64(8 + 8 + 24) // time.Time is 24 bytes } // GetType returns the type of the activity task func (a *ActivityTask) GetTaskType() int { return TransferTaskTypeActivityTask } func (a *ActivityTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (a *ActivityTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(a.TaskID) } func (a *ActivityTask) GetTaskList() string { return a.TaskList } func (a *ActivityTask) GetOriginalTaskList() string { return a.TaskList } func (a *ActivityTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (a *ActivityTask) ByteSize() uint64 { return a.WorkflowIdentifier.ByteSize() + a.TaskData.ByteSize() + uint64(len(a.TargetDomainID)) + uint64(len(a.TaskList)) + 8 } func (a *ActivityTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return &TransferTaskInfo{ TaskType: TransferTaskTypeActivityTask, DomainID: a.DomainID, WorkflowID: a.WorkflowID, RunID: a.RunID, TaskID: a.TaskID, VisibilityTimestamp: a.VisibilityTimestamp, Version: a.Version, TargetDomainID: a.TargetDomainID, TaskList: a.TaskList, ScheduleID: a.ScheduleID, TargetWorkflowID: TransferTaskTransferTargetWorkflowID, TargetRunID: TransferTaskTransferTargetRunID, }, nil } func (a *ActivityTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("activity task is not timer task") } func (a *ActivityTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("activity task is not replication task") } // GetType returns the type of the decision task func (d *DecisionTask) GetTaskType() int { return TransferTaskTypeDecisionTask } func (d *DecisionTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (d *DecisionTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(d.TaskID) } func (d *DecisionTask) GetTaskList() string { return d.TaskList } func (d *DecisionTask) GetOriginalTaskList() string { return d.OriginalTaskList } func (d *DecisionTask) GetOriginalTaskListKind() types.TaskListKind { return d.OriginalTaskListKind } func (d *DecisionTask) ByteSize() uint64 { return d.WorkflowIdentifier.ByteSize() + d.TaskData.ByteSize() + uint64(len(d.TargetDomainID)) + uint64(len(d.TaskList)) + uint64(len(d.OriginalTaskList)) + 16 } func (d *DecisionTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return &TransferTaskInfo{ TaskType: TransferTaskTypeDecisionTask, DomainID: d.DomainID, WorkflowID: d.WorkflowID, RunID: d.RunID, TaskID: d.TaskID, VisibilityTimestamp: d.VisibilityTimestamp, Version: d.Version, TargetDomainID: d.TargetDomainID, TaskList: d.TaskList, ScheduleID: d.ScheduleID, OriginalTaskList: d.OriginalTaskList, OriginalTaskListKind: d.OriginalTaskListKind, TargetWorkflowID: TransferTaskTransferTargetWorkflowID, TargetRunID: TransferTaskTransferTargetRunID, }, nil } func (d *DecisionTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("decision task is not timer task") } func (d *DecisionTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("decision task is not replication task") } // GetType returns the type of the record workflow started task func (a *RecordWorkflowStartedTask) GetTaskType() int { return TransferTaskTypeRecordWorkflowStarted } func (a *RecordWorkflowStartedTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (a *RecordWorkflowStartedTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(a.TaskID) } func (a *RecordWorkflowStartedTask) GetTaskList() string { return a.TaskList } func (a *RecordWorkflowStartedTask) GetOriginalTaskList() string { return a.TaskList } func (a *RecordWorkflowStartedTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (a *RecordWorkflowStartedTask) ByteSize() uint64 { return a.WorkflowIdentifier.ByteSize() + a.TaskData.ByteSize() + uint64(len(a.TaskList)) } func (a *RecordWorkflowStartedTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return &TransferTaskInfo{ TaskType: TransferTaskTypeRecordWorkflowStarted, DomainID: a.DomainID, WorkflowID: a.WorkflowID, RunID: a.RunID, TaskID: a.TaskID, VisibilityTimestamp: a.VisibilityTimestamp, Version: a.Version, TaskList: a.TaskList, TargetDomainID: a.DomainID, TargetWorkflowID: TransferTaskTransferTargetWorkflowID, TargetRunID: TransferTaskTransferTargetRunID, }, nil } func (a *RecordWorkflowStartedTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("record workflow started task is not timer task") } func (a *RecordWorkflowStartedTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("record workflow started task is not replication task") } // GetType returns the type of the ResetWorkflowTask func (a *ResetWorkflowTask) GetTaskType() int { return TransferTaskTypeResetWorkflow } func (a *ResetWorkflowTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (a *ResetWorkflowTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(a.TaskID) } func (a *ResetWorkflowTask) GetTaskList() string { return a.TaskList } func (a *ResetWorkflowTask) GetOriginalTaskList() string { return a.TaskList } func (a *ResetWorkflowTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (a *ResetWorkflowTask) ByteSize() uint64 { return a.WorkflowIdentifier.ByteSize() + a.TaskData.ByteSize() + uint64(len(a.TaskList)) } func (a *ResetWorkflowTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return &TransferTaskInfo{ TaskType: TransferTaskTypeResetWorkflow, DomainID: a.DomainID, WorkflowID: a.WorkflowID, RunID: a.RunID, TaskID: a.TaskID, VisibilityTimestamp: a.VisibilityTimestamp, Version: a.Version, TaskList: a.TaskList, TargetDomainID: a.DomainID, TargetWorkflowID: TransferTaskTransferTargetWorkflowID, TargetRunID: TransferTaskTransferTargetRunID, }, nil } func (a *ResetWorkflowTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("reset workflow task is not timer task") } func (a *ResetWorkflowTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("reset workflow task is not replication task") } // GetType returns the type of the close execution task func (a *CloseExecutionTask) GetTaskType() int { return TransferTaskTypeCloseExecution } func (a *CloseExecutionTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (a *CloseExecutionTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(a.TaskID) } func (a *CloseExecutionTask) GetTaskList() string { return a.TaskList } func (a *CloseExecutionTask) GetOriginalTaskList() string { return a.TaskList } func (a *CloseExecutionTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (a *CloseExecutionTask) ByteSize() uint64 { return a.WorkflowIdentifier.ByteSize() + a.TaskData.ByteSize() + uint64(len(a.TaskList)) } func (a *CloseExecutionTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return &TransferTaskInfo{ TaskType: TransferTaskTypeCloseExecution, DomainID: a.DomainID, WorkflowID: a.WorkflowID, RunID: a.RunID, TaskID: a.TaskID, VisibilityTimestamp: a.VisibilityTimestamp, Version: a.Version, TaskList: a.TaskList, TargetDomainID: a.DomainID, TargetWorkflowID: TransferTaskTransferTargetWorkflowID, TargetRunID: TransferTaskTransferTargetRunID, }, nil } func (a *CloseExecutionTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("close execution task is not timer task") } func (a *CloseExecutionTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("close execution task is not replication task") } // GetType returns the type of the delete execution task func (a *DeleteHistoryEventTask) GetTaskType() int { return TaskTypeDeleteHistoryEvent } func (a *DeleteHistoryEventTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTimer } func (a *DeleteHistoryEventTask) GetTaskKey() HistoryTaskKey { return NewHistoryTaskKey(a.VisibilityTimestamp, a.TaskID) } func (a *DeleteHistoryEventTask) GetTaskList() string { return a.TaskList } func (a *DeleteHistoryEventTask) GetOriginalTaskList() string { return a.TaskList } func (a *DeleteHistoryEventTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (a *DeleteHistoryEventTask) ByteSize() uint64 { return a.WorkflowIdentifier.ByteSize() + a.TaskData.ByteSize() + uint64(len(a.TaskList)) } func (a *DeleteHistoryEventTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return nil, fmt.Errorf("delete history event task is not transfer task") } func (a *DeleteHistoryEventTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return &TimerTaskInfo{ TaskType: TaskTypeDeleteHistoryEvent, DomainID: a.DomainID, WorkflowID: a.WorkflowID, RunID: a.RunID, TaskID: a.TaskID, VisibilityTimestamp: a.VisibilityTimestamp, Version: a.Version, TaskList: a.TaskList, }, nil } func (a *DeleteHistoryEventTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("delete history event task is not replication task") } // GetType returns the type of the timer task func (d *DecisionTimeoutTask) GetTaskType() int { return TaskTypeDecisionTimeout } func (d *DecisionTimeoutTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTimer } func (d *DecisionTimeoutTask) GetTaskKey() HistoryTaskKey { return NewHistoryTaskKey(d.VisibilityTimestamp, d.TaskID) } func (d *DecisionTimeoutTask) GetTaskList() string { return d.TaskList } func (d *DecisionTimeoutTask) GetOriginalTaskList() string { return d.TaskList } func (d *DecisionTimeoutTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (d *DecisionTimeoutTask) ByteSize() uint64 { return d.WorkflowIdentifier.ByteSize() + d.TaskData.ByteSize() + 8 + 8 + 8 + uint64(len(d.TaskList)) } func (d *DecisionTimeoutTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return nil, fmt.Errorf("decision timeout task is not transfer task") } func (d *DecisionTimeoutTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return &TimerTaskInfo{ TaskType: TaskTypeDecisionTimeout, DomainID: d.DomainID, WorkflowID: d.WorkflowID, RunID: d.RunID, TaskID: d.TaskID, VisibilityTimestamp: d.VisibilityTimestamp, Version: d.Version, EventID: d.EventID, ScheduleAttempt: d.ScheduleAttempt, TimeoutType: d.TimeoutType, TaskList: d.TaskList, }, nil } func (d *DecisionTimeoutTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("decision timeout task is not replication task") } // GetType returns the type of the timer task func (a *ActivityTimeoutTask) GetTaskType() int { return TaskTypeActivityTimeout } func (a *ActivityTimeoutTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTimer } func (a *ActivityTimeoutTask) GetTaskKey() HistoryTaskKey { return NewHistoryTaskKey(a.VisibilityTimestamp, a.TaskID) } func (a *ActivityTimeoutTask) GetTaskList() string { return a.TaskList } func (a *ActivityTimeoutTask) GetOriginalTaskList() string { return a.TaskList } func (a *ActivityTimeoutTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (a *ActivityTimeoutTask) ByteSize() uint64 { return a.WorkflowIdentifier.ByteSize() + a.TaskData.ByteSize() + 8 + 8 + 8 + uint64(len(a.TaskList)) } func (a *ActivityTimeoutTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return nil, fmt.Errorf("activity timeout task is not transfer task") } func (a *ActivityTimeoutTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return &TimerTaskInfo{ TaskType: TaskTypeActivityTimeout, DomainID: a.DomainID, WorkflowID: a.WorkflowID, RunID: a.RunID, TaskID: a.TaskID, VisibilityTimestamp: a.VisibilityTimestamp, Version: a.Version, EventID: a.EventID, ScheduleAttempt: a.Attempt, TimeoutType: a.TimeoutType, TaskList: a.TaskList, }, nil } func (a *ActivityTimeoutTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("activity timeout task is not replication task") } // GetType returns the type of the timer task func (u *UserTimerTask) GetTaskType() int { return TaskTypeUserTimer } func (u *UserTimerTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTimer } func (u *UserTimerTask) GetTaskKey() HistoryTaskKey { return NewHistoryTaskKey(u.VisibilityTimestamp, u.TaskID) } func (u *UserTimerTask) GetTaskList() string { return u.TaskList } func (u *UserTimerTask) GetOriginalTaskList() string { return u.TaskList } func (u *UserTimerTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (u *UserTimerTask) ByteSize() uint64 { return u.WorkflowIdentifier.ByteSize() + u.TaskData.ByteSize() + 8 + uint64(len(u.TaskList)) } func (u *UserTimerTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return nil, fmt.Errorf("user timer task is not transfer task") } func (u *UserTimerTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return &TimerTaskInfo{ TaskType: TaskTypeUserTimer, DomainID: u.DomainID, WorkflowID: u.WorkflowID, RunID: u.RunID, TaskID: u.TaskID, VisibilityTimestamp: u.VisibilityTimestamp, Version: u.Version, EventID: u.EventID, TaskList: u.TaskList, }, nil } func (u *UserTimerTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("user timer task is not replication task") } // GetType returns the type of the retry timer task func (r *ActivityRetryTimerTask) GetTaskType() int { return TaskTypeActivityRetryTimer } func (r *ActivityRetryTimerTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTimer } func (r *ActivityRetryTimerTask) GetTaskKey() HistoryTaskKey { return NewHistoryTaskKey(r.VisibilityTimestamp, r.TaskID) } func (r *ActivityRetryTimerTask) GetTaskList() string { return r.TaskList } func (r *ActivityRetryTimerTask) GetOriginalTaskList() string { return r.TaskList } func (r *ActivityRetryTimerTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (r *ActivityRetryTimerTask) ByteSize() uint64 { return r.WorkflowIdentifier.ByteSize() + r.TaskData.ByteSize() + 8 + 8 + uint64(len(r.TaskList)) } func (r *ActivityRetryTimerTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return nil, fmt.Errorf("activity retry timer task is not transfer task") } func (r *ActivityRetryTimerTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return &TimerTaskInfo{ TaskType: TaskTypeActivityRetryTimer, DomainID: r.DomainID, WorkflowID: r.WorkflowID, RunID: r.RunID, TaskID: r.TaskID, VisibilityTimestamp: r.VisibilityTimestamp, Version: r.Version, EventID: r.EventID, ScheduleAttempt: r.Attempt, TaskList: r.TaskList, }, nil } func (r *ActivityRetryTimerTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("activity retry timer task is not replication task") } // GetType returns the type of the retry timer task func (r *WorkflowBackoffTimerTask) GetTaskType() int { return TaskTypeWorkflowBackoffTimer } func (r *WorkflowBackoffTimerTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTimer } func (r *WorkflowBackoffTimerTask) GetTaskKey() HistoryTaskKey { return NewHistoryTaskKey(r.VisibilityTimestamp, r.TaskID) } func (r *WorkflowBackoffTimerTask) GetTaskList() string { return r.TaskList } func (r *WorkflowBackoffTimerTask) GetOriginalTaskList() string { return r.TaskList } func (r *WorkflowBackoffTimerTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (r *WorkflowBackoffTimerTask) ByteSize() uint64 { return r.WorkflowIdentifier.ByteSize() + r.TaskData.ByteSize() + 8 + uint64(len(r.TaskList)) } func (r *WorkflowBackoffTimerTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return nil, fmt.Errorf("workflow backoff timer task is not transfer task") } func (r *WorkflowBackoffTimerTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return &TimerTaskInfo{ TaskType: TaskTypeWorkflowBackoffTimer, DomainID: r.DomainID, WorkflowID: r.WorkflowID, RunID: r.RunID, TaskID: r.TaskID, VisibilityTimestamp: r.VisibilityTimestamp, Version: r.Version, TimeoutType: r.TimeoutType, TaskList: r.TaskList, }, nil } func (r *WorkflowBackoffTimerTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("workflow backoff timer task is not replication task") } // GetType returns the type of the timeout task. func (u *WorkflowTimeoutTask) GetTaskType() int { return TaskTypeWorkflowTimeout } func (u *WorkflowTimeoutTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTimer } func (u *WorkflowTimeoutTask) GetTaskKey() HistoryTaskKey { return NewHistoryTaskKey(u.VisibilityTimestamp, u.TaskID) } func (u *WorkflowTimeoutTask) GetTaskList() string { return u.TaskList } func (u *WorkflowTimeoutTask) GetOriginalTaskList() string { return u.TaskList } func (u *WorkflowTimeoutTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (u *WorkflowTimeoutTask) ByteSize() uint64 { return u.WorkflowIdentifier.ByteSize() + u.TaskData.ByteSize() + uint64(len(u.TaskList)) } func (u *WorkflowTimeoutTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return nil, fmt.Errorf("workflow timeout task is not transfer task") } func (u *WorkflowTimeoutTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return &TimerTaskInfo{ TaskType: TaskTypeWorkflowTimeout, DomainID: u.DomainID, WorkflowID: u.WorkflowID, RunID: u.RunID, TaskID: u.TaskID, VisibilityTimestamp: u.VisibilityTimestamp, Version: u.Version, TaskList: u.TaskList, }, nil } func (u *WorkflowTimeoutTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("workflow timeout task is not replication task") } // GetType returns the type of the cancel transfer task func (u *CancelExecutionTask) GetTaskType() int { return TransferTaskTypeCancelExecution } func (u *CancelExecutionTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (u *CancelExecutionTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(u.TaskID) } func (u *CancelExecutionTask) GetTaskList() string { return u.TaskList } func (u *CancelExecutionTask) GetOriginalTaskList() string { return u.TaskList } func (u *CancelExecutionTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (u *CancelExecutionTask) ByteSize() uint64 { return u.WorkflowIdentifier.ByteSize() + u.TaskData.ByteSize() + uint64(len(u.TargetDomainID)) + uint64(len(u.TargetWorkflowID)) + uint64(len(u.TargetRunID)) + 8 + 1 + uint64(len(u.TaskList)) } func (u *CancelExecutionTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { targetRunID := u.TargetRunID if u.TargetRunID == "" { targetRunID = TransferTaskTransferTargetRunID } return &TransferTaskInfo{ TaskType: TransferTaskTypeCancelExecution, DomainID: u.DomainID, WorkflowID: u.WorkflowID, RunID: u.RunID, TaskID: u.TaskID, VisibilityTimestamp: u.VisibilityTimestamp, Version: u.Version, TargetDomainID: u.TargetDomainID, TargetWorkflowID: u.TargetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: u.TargetChildWorkflowOnly, ScheduleID: u.InitiatedID, TaskList: u.TaskList, }, nil } func (u *CancelExecutionTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("cancel execution task is not timer task") } func (u *CancelExecutionTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("cancel execution task is not replication task") } // GetType returns the type of the signal transfer task func (u *SignalExecutionTask) GetTaskType() int { return TransferTaskTypeSignalExecution } func (u *SignalExecutionTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (u *SignalExecutionTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(u.TaskID) } func (u *SignalExecutionTask) GetTaskList() string { return u.TaskList } func (u *SignalExecutionTask) GetOriginalTaskList() string { return u.TaskList } func (u *SignalExecutionTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (u *SignalExecutionTask) ByteSize() uint64 { return u.WorkflowIdentifier.ByteSize() + u.TaskData.ByteSize() + uint64(len(u.TargetDomainID)) + uint64(len(u.TargetWorkflowID)) + uint64(len(u.TargetRunID)) + 8 + 1 + uint64(len(u.TaskList)) } func (u *SignalExecutionTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { targetRunID := u.TargetRunID if u.TargetRunID == "" { targetRunID = TransferTaskTransferTargetRunID } return &TransferTaskInfo{ TaskType: TransferTaskTypeSignalExecution, DomainID: u.DomainID, WorkflowID: u.WorkflowID, RunID: u.RunID, TaskID: u.TaskID, VisibilityTimestamp: u.VisibilityTimestamp, Version: u.Version, TargetDomainID: u.TargetDomainID, TargetWorkflowID: u.TargetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: u.TargetChildWorkflowOnly, ScheduleID: u.InitiatedID, TaskList: u.TaskList, }, nil } func (u *SignalExecutionTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("signal execution task is not timer task") } func (u *SignalExecutionTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("signal execution task is not replication task") } // GetType returns the type of the record child execution completed task func (u *RecordChildExecutionCompletedTask) GetTaskType() int { return TransferTaskTypeRecordChildExecutionCompleted } func (u *RecordChildExecutionCompletedTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (u *RecordChildExecutionCompletedTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(u.TaskID) } func (u *RecordChildExecutionCompletedTask) GetTaskList() string { return u.TaskList } func (u *RecordChildExecutionCompletedTask) GetOriginalTaskList() string { return u.TaskList } func (u *RecordChildExecutionCompletedTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (u *RecordChildExecutionCompletedTask) ByteSize() uint64 { return u.WorkflowIdentifier.ByteSize() + u.TaskData.ByteSize() + uint64(len(u.TargetDomainID)) + uint64(len(u.TargetWorkflowID)) + uint64(len(u.TargetRunID)) + uint64(len(u.TaskList)) } func (u *RecordChildExecutionCompletedTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { targetRunID := u.TargetRunID if u.TargetRunID == "" { targetRunID = TransferTaskTransferTargetRunID } return &TransferTaskInfo{ TaskType: TransferTaskTypeRecordChildExecutionCompleted, DomainID: u.DomainID, WorkflowID: u.WorkflowID, RunID: u.RunID, TaskID: u.TaskID, VisibilityTimestamp: u.VisibilityTimestamp, Version: u.Version, TargetDomainID: u.TargetDomainID, TargetWorkflowID: u.TargetWorkflowID, TargetRunID: targetRunID, TaskList: u.TaskList, }, nil } func (u *RecordChildExecutionCompletedTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("record child execution completed task is not timer task") } func (u *RecordChildExecutionCompletedTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("record child execution completed task is not replication task") } // GetType returns the type of the upsert search attributes transfer task func (u *UpsertWorkflowSearchAttributesTask) GetTaskType() int { return TransferTaskTypeUpsertWorkflowSearchAttributes } func (u *UpsertWorkflowSearchAttributesTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(u.TaskID) } func (u *UpsertWorkflowSearchAttributesTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (u *UpsertWorkflowSearchAttributesTask) GetTaskList() string { return u.TaskList } func (u *UpsertWorkflowSearchAttributesTask) GetOriginalTaskList() string { return u.TaskList } func (u *UpsertWorkflowSearchAttributesTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (u *UpsertWorkflowSearchAttributesTask) ByteSize() uint64 { return u.WorkflowIdentifier.ByteSize() + u.TaskData.ByteSize() + uint64(len(u.TaskList)) } func (u *UpsertWorkflowSearchAttributesTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return &TransferTaskInfo{ TaskType: TransferTaskTypeUpsertWorkflowSearchAttributes, DomainID: u.DomainID, WorkflowID: u.WorkflowID, RunID: u.RunID, TaskID: u.TaskID, VisibilityTimestamp: u.VisibilityTimestamp, Version: u.Version, TaskList: u.TaskList, TargetDomainID: u.DomainID, TargetWorkflowID: TransferTaskTransferTargetWorkflowID, TargetRunID: TransferTaskTransferTargetRunID, }, nil } func (u *UpsertWorkflowSearchAttributesTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("upsert workflow search attributes task is not timer task") } func (u *UpsertWorkflowSearchAttributesTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("upsert workflow search attributes task is not replication task") } // GetType returns the type of the start child transfer task func (u *StartChildExecutionTask) GetTaskType() int { return TransferTaskTypeStartChildExecution } func (u *StartChildExecutionTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (u *StartChildExecutionTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(u.TaskID) } func (u *StartChildExecutionTask) GetTaskList() string { return u.TaskList } func (u *StartChildExecutionTask) GetOriginalTaskList() string { return u.TaskList } func (u *StartChildExecutionTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (u *StartChildExecutionTask) ByteSize() uint64 { return u.WorkflowIdentifier.ByteSize() + u.TaskData.ByteSize() + uint64(len(u.TargetDomainID)) + uint64(len(u.TargetWorkflowID)) + 8 + uint64(len(u.TaskList)) } func (u *StartChildExecutionTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return &TransferTaskInfo{ TaskType: TransferTaskTypeStartChildExecution, DomainID: u.DomainID, WorkflowID: u.WorkflowID, RunID: u.RunID, TaskID: u.TaskID, VisibilityTimestamp: u.VisibilityTimestamp, Version: u.Version, TargetDomainID: u.TargetDomainID, TargetWorkflowID: u.TargetWorkflowID, ScheduleID: u.InitiatedID, TaskList: u.TaskList, TargetRunID: TransferTaskTransferTargetRunID, }, nil } func (u *StartChildExecutionTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("start child execution task is not timer task") } func (u *StartChildExecutionTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("start child execution task is not replication task") } // GetType returns the type of the record workflow closed task func (u *RecordWorkflowClosedTask) GetTaskType() int { return TransferTaskTypeRecordWorkflowClosed } func (u *RecordWorkflowClosedTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryTransfer } func (u *RecordWorkflowClosedTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(u.TaskID) } func (u *RecordWorkflowClosedTask) GetTaskList() string { return u.TaskList } func (u *RecordWorkflowClosedTask) GetOriginalTaskList() string { return u.TaskList } func (u *RecordWorkflowClosedTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (u *RecordWorkflowClosedTask) ByteSize() uint64 { return u.WorkflowIdentifier.ByteSize() + u.TaskData.ByteSize() + uint64(len(u.TaskList)) } func (u *RecordWorkflowClosedTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return &TransferTaskInfo{ TaskType: TransferTaskTypeRecordWorkflowClosed, DomainID: u.DomainID, WorkflowID: u.WorkflowID, RunID: u.RunID, TaskID: u.TaskID, VisibilityTimestamp: u.VisibilityTimestamp, Version: u.Version, TaskList: u.TaskList, TargetDomainID: u.DomainID, TargetWorkflowID: TransferTaskTransferTargetWorkflowID, TargetRunID: TransferTaskTransferTargetRunID, }, nil } func (u *RecordWorkflowClosedTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("record workflow closed task is not timer task") } func (u *RecordWorkflowClosedTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return nil, fmt.Errorf("record workflow closed task is not replication task") } // GetType returns the type of the history replication task func (a *HistoryReplicationTask) GetTaskType() int { return ReplicationTaskTypeHistory } func (a *HistoryReplicationTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryReplication } func (a *HistoryReplicationTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(a.TaskID) } func (a *HistoryReplicationTask) GetTaskList() string { return "" } func (a *HistoryReplicationTask) GetOriginalTaskList() string { return "" } func (a *HistoryReplicationTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (a *HistoryReplicationTask) ByteSize() uint64 { return a.WorkflowIdentifier.ByteSize() + a.TaskData.ByteSize() + 8 + 8 + uint64(len(a.BranchToken)) + uint64(len(a.NewRunBranchToken)) } func (a *HistoryReplicationTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return nil, fmt.Errorf("history replication task is not transfer task") } func (a *HistoryReplicationTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("history replication task is not timer task") } func (a *HistoryReplicationTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return &types.ReplicationTaskInfo{ DomainID: a.DomainID, WorkflowID: a.WorkflowID, RunID: a.RunID, TaskType: ReplicationTaskTypeHistory, TaskID: a.TaskID, Version: a.Version, FirstEventID: a.FirstEventID, NextEventID: a.NextEventID, ScheduledID: constants.EmptyEventID, }, nil } // GetType returns the type of the sync activity task func (a *SyncActivityTask) GetTaskType() int { return ReplicationTaskTypeSyncActivity } func (a *SyncActivityTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryReplication } func (a *SyncActivityTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(a.TaskID) } func (a *SyncActivityTask) GetTaskList() string { return "" } func (a *SyncActivityTask) GetOriginalTaskList() string { return "" } func (a *SyncActivityTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (a *SyncActivityTask) ByteSize() uint64 { return a.WorkflowIdentifier.ByteSize() + a.TaskData.ByteSize() + 8 } func (a *SyncActivityTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return &types.ReplicationTaskInfo{ DomainID: a.DomainID, WorkflowID: a.WorkflowID, RunID: a.RunID, TaskType: ReplicationTaskTypeSyncActivity, TaskID: a.TaskID, Version: a.Version, FirstEventID: constants.EmptyEventID, NextEventID: constants.EmptyEventID, ScheduledID: a.ScheduledID, }, nil } func (a *SyncActivityTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return nil, fmt.Errorf("sync activity task is not transfer task") } func (a *SyncActivityTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("sync activity task is not timer task") } // GetType returns the type of the history replication task func (a *FailoverMarkerTask) GetTaskType() int { return ReplicationTaskTypeFailoverMarker } func (a *FailoverMarkerTask) GetTaskCategory() HistoryTaskCategory { return HistoryTaskCategoryReplication } func (a *FailoverMarkerTask) GetTaskKey() HistoryTaskKey { return NewImmediateTaskKey(a.TaskID) } func (a *FailoverMarkerTask) GetTaskList() string { return "" } func (a *FailoverMarkerTask) GetOriginalTaskList() string { return "" } func (a *FailoverMarkerTask) GetOriginalTaskListKind() types.TaskListKind { return types.TaskListKindNormal } func (a *FailoverMarkerTask) ByteSize() uint64 { return uint64(len(a.DomainID)) + a.TaskData.ByteSize() } func (a *FailoverMarkerTask) ToTransferTaskInfo() (*TransferTaskInfo, error) { return nil, fmt.Errorf("failover marker task is not transfer task") } func (a *FailoverMarkerTask) ToTimerTaskInfo() (*TimerTaskInfo, error) { return nil, fmt.Errorf("failover marker task is not timer task") } func (a *FailoverMarkerTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { return &types.ReplicationTaskInfo{ DomainID: a.DomainID, TaskType: ReplicationTaskTypeFailoverMarker, TaskID: a.TaskID, Version: a.Version, FirstEventID: constants.EmptyEventID, NextEventID: constants.EmptyEventID, ScheduledID: constants.EmptyEventID, }, nil } func (a *FailoverMarkerTask) GetDomainID() string { return a.DomainID } func (a *FailoverMarkerTask) GetWorkflowID() string { return "" } func (a *FailoverMarkerTask) GetRunID() string { return "" } ================================================ FILE: common/persistence/tasks_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "testing" "time" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" ) func TestTaskCommonMethods(t *testing.T) { timeNow := time.Now() tasks := []Task{ &ActivityTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &DecisionTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &RecordWorkflowStartedTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &ResetWorkflowTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &CloseExecutionTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &DeleteHistoryEventTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &DecisionTimeoutTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &ActivityTimeoutTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &UserTimerTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &ActivityRetryTimerTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &WorkflowBackoffTimerTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &WorkflowTimeoutTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &CancelExecutionTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &SignalExecutionTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &RecordChildExecutionCompletedTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &UpsertWorkflowSearchAttributesTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &StartChildExecutionTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &RecordWorkflowClosedTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &HistoryReplicationTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &SyncActivityTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &FailoverMarkerTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, } for _, task := range tasks { switch ty := task.(type) { case *ActivityTask: assert.Equal(t, TransferTaskTypeActivityTask, ty.GetTaskType()) case *DecisionTask: assert.Equal(t, TransferTaskTypeDecisionTask, ty.GetTaskType()) case *RecordWorkflowStartedTask: assert.Equal(t, TransferTaskTypeRecordWorkflowStarted, ty.GetTaskType()) case *ResetWorkflowTask: assert.Equal(t, TransferTaskTypeResetWorkflow, ty.GetTaskType()) case *CloseExecutionTask: assert.Equal(t, TransferTaskTypeCloseExecution, ty.GetTaskType()) case *DeleteHistoryEventTask: assert.Equal(t, TaskTypeDeleteHistoryEvent, ty.GetTaskType()) case *DecisionTimeoutTask: assert.Equal(t, TaskTypeDecisionTimeout, ty.GetTaskType()) case *ActivityTimeoutTask: assert.Equal(t, TaskTypeActivityTimeout, ty.GetTaskType()) case *UserTimerTask: assert.Equal(t, TaskTypeUserTimer, ty.GetTaskType()) case *ActivityRetryTimerTask: assert.Equal(t, TaskTypeActivityRetryTimer, ty.GetTaskType()) case *WorkflowBackoffTimerTask: assert.Equal(t, TaskTypeWorkflowBackoffTimer, ty.GetTaskType()) case *WorkflowTimeoutTask: assert.Equal(t, TaskTypeWorkflowTimeout, ty.GetTaskType()) case *CancelExecutionTask: assert.Equal(t, TransferTaskTypeCancelExecution, ty.GetTaskType()) case *SignalExecutionTask: assert.Equal(t, TransferTaskTypeSignalExecution, ty.GetTaskType()) case *RecordChildExecutionCompletedTask: assert.Equal(t, TransferTaskTypeRecordChildExecutionCompleted, ty.GetTaskType()) case *UpsertWorkflowSearchAttributesTask: assert.Equal(t, TransferTaskTypeUpsertWorkflowSearchAttributes, ty.GetTaskType()) case *StartChildExecutionTask: assert.Equal(t, TransferTaskTypeStartChildExecution, ty.GetTaskType()) case *RecordWorkflowClosedTask: assert.Equal(t, TransferTaskTypeRecordWorkflowClosed, ty.GetTaskType()) case *HistoryReplicationTask: assert.Equal(t, ReplicationTaskTypeHistory, ty.GetTaskType()) case *SyncActivityTask: assert.Equal(t, ReplicationTaskTypeSyncActivity, ty.GetTaskType()) case *FailoverMarkerTask: assert.Equal(t, ReplicationTaskTypeFailoverMarker, ty.GetTaskType()) default: t.Fatalf("Unhandled task type: %T", t) } // Test version methods assert.Equal(t, int64(1), task.GetVersion()) task.SetVersion(2) assert.Equal(t, int64(2), task.GetVersion()) // Test TaskID methods assert.Equal(t, int64(1), task.GetTaskID()) task.SetTaskID(2) assert.Equal(t, int64(2), task.GetTaskID()) // Test VisibilityTimestamp methods assert.Equal(t, timeNow, task.GetVisibilityTimestamp()) newTime := timeNow.Add(time.Second) task.SetVisibilityTimestamp(newTime) assert.Equal(t, newTime, task.GetVisibilityTimestamp()) if task.GetTaskCategory().Type() == HistoryTaskCategoryTypeImmediate { assert.Equal(t, NewImmediateTaskKey(task.GetTaskID()), task.GetTaskKey()) } else { assert.Equal(t, NewHistoryTaskKey(task.GetVisibilityTimestamp(), task.GetTaskID()), task.GetTaskKey()) } } } func TestTransferTaskMapping(t *testing.T) { f := fuzz.New().NilChance(0.0) tasks := []Task{ &ActivityTask{}, &DecisionTask{}, &RecordWorkflowStartedTask{}, &ResetWorkflowTask{}, &CloseExecutionTask{}, &CancelExecutionTask{}, &SignalExecutionTask{}, &RecordChildExecutionCompletedTask{}, &UpsertWorkflowSearchAttributesTask{}, &StartChildExecutionTask{}, &RecordWorkflowClosedTask{}, } for i := 0; i < 1000; i++ { for _, task := range tasks { f.Fuzz(task) transfer, err := task.ToTransferTaskInfo() assert.NoError(t, err) t.Log(transfer) task2, err := transfer.ToTask() assert.NoError(t, err) assert.Equal(t, task, task2) } } } func TestTimerTaskMapping(t *testing.T) { f := fuzz.New().NilChance(0.0) tasks := []Task{ &DecisionTimeoutTask{}, &ActivityTimeoutTask{}, &DeleteHistoryEventTask{}, &WorkflowTimeoutTask{}, &UserTimerTask{}, &ActivityRetryTimerTask{}, &WorkflowBackoffTimerTask{}, } for i := 0; i < 1000; i++ { for _, task := range tasks { f.Fuzz(task) timer, err := task.ToTimerTaskInfo() assert.NoError(t, err) task2, err := timer.ToTask() assert.NoError(t, err) assert.Equal(t, task, task2) } } } func TestHistoryTaskKeyComparison(t *testing.T) { now := time.Now() key1 := NewHistoryTaskKey(now, 1) key2 := NewHistoryTaskKey(now, 2) key3 := NewHistoryTaskKey(now.Add(time.Second), 1) assert.Equal(t, 0, key1.Compare(key1)) assert.Equal(t, -1, key1.Compare(key2)) assert.Equal(t, 1, key2.Compare(key1)) assert.Equal(t, -1, key2.Compare(key3)) assert.Equal(t, 1, key3.Compare(key2)) } func TestIsTaskCorrupted(t *testing.T) { timeNow := time.Now() tests := []struct { name string task Task expected bool }{ { name: "Valid ActivityTask", task: &ActivityTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "test-workflow", RunID: "test-run", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, }, expected: false, }, { name: "Valid DecisionTask", task: &DecisionTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "test-workflow", RunID: "test-run", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, }, expected: false, }, { name: "Valid TimerTask", task: &UserTimerTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "test-workflow", RunID: "test-run", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, EventID: 123, }, expected: false, }, { name: "Valid ReplicationTask", task: &HistoryReplicationTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "test-workflow", RunID: "test-run", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, FirstEventID: 1, NextEventID: 10, }, expected: false, }, { name: "Task with empty DomainID", task: &ActivityTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "", WorkflowID: "test-workflow", RunID: "test-run", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, }, expected: true, }, { name: "Task with empty WorkflowID", task: &DecisionTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "", RunID: "test-run", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, }, expected: true, }, { name: "Task with empty RunID", task: &UserTimerTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "test-workflow", RunID: "", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, EventID: 123, }, expected: true, }, { name: "Task with all empty identifiers", task: &ActivityTimeoutTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "", WorkflowID: "", RunID: "", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, TimeoutType: 1, EventID: 123, Attempt: 1, }, expected: true, }, { name: "Task with whitespace-only identifiers", task: &WorkflowTimeoutTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: " ", WorkflowID: "test-workflow", RunID: "test-run", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, }, expected: false, // Whitespace is not empty string }, { name: "FailoverMarkerTask with empty DomainID", task: &FailoverMarkerTask{ TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, DomainID: "", }, expected: true, }, { name: "FailoverMarkerTask with valid DomainID", task: &FailoverMarkerTask{ TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, DomainID: "test-domain", }, expected: false, // FailoverMarkerTask only has DomainID, WorkflowID and RunID are empty by design }, { name: "Task with mixed empty and non-empty identifiers", task: &CancelExecutionTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "", RunID: "test-run", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, TargetDomainID: "target-domain", TargetWorkflowID: "target-workflow", TargetRunID: "target-run", TargetChildWorkflowOnly: false, InitiatedID: 123, }, expected: true, }, { name: "Task with very long identifiers", task: &SignalExecutionTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "very-long-domain-id-that-exceeds-normal-length", WorkflowID: "very-long-workflow-id-that-exceeds-normal-length", RunID: "very-long-run-id-that-exceeds-normal-length", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, TargetDomainID: "target-domain", TargetWorkflowID: "target-workflow", TargetRunID: "target-run", TargetChildWorkflowOnly: false, InitiatedID: 123, }, expected: false, }, { name: "Task with special characters in identifiers", task: &StartChildExecutionTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "domain-with-special-chars-!@#$%^&*()", WorkflowID: "workflow-with-special-chars-!@#$%^&*()", RunID: "run-with-special-chars-!@#$%^&*()", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, TargetDomainID: "target-domain", TargetWorkflowID: "target-workflow", InitiatedID: 123, }, expected: false, }, { name: "Task with unicode characters in identifiers", task: &RecordWorkflowStartedTask{ WorkflowIdentifier: WorkflowIdentifier{ DomainID: "domain-with-unicode-测试", WorkflowID: "workflow-with-unicode-测试", RunID: "run-with-unicode-测试", }, TaskData: TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: timeNow, }, }, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := IsTaskCorrupted(tt.task) assert.Equal(t, tt.expected, result, "IsTaskCorrupted() = %v, want %v", result, tt.expected) }) } } func TestIsTaskCorruptedWithAllTaskTypes(t *testing.T) { timeNow := time.Now() validIdentifier := WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "test-workflow", RunID: "test-run", } emptyIdentifier := WorkflowIdentifier{ DomainID: "", WorkflowID: "", RunID: "", } // Test all task types with valid identifiers validTasks := []Task{ &ActivityTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &DecisionTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &RecordWorkflowStartedTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &ResetWorkflowTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &CloseExecutionTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &DeleteHistoryEventTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &DecisionTimeoutTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &WorkflowTimeoutTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &CancelExecutionTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &SignalExecutionTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &RecordChildExecutionCompletedTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &UpsertWorkflowSearchAttributesTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &StartChildExecutionTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &RecordWorkflowClosedTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &ActivityTimeoutTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &UserTimerTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &ActivityRetryTimerTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &WorkflowBackoffTimerTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &HistoryReplicationTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &SyncActivityTask{WorkflowIdentifier: validIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &FailoverMarkerTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}, DomainID: "test-domain"}, } // Test all task types with empty identifiers corruptedTasks := []Task{ &ActivityTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &DecisionTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &RecordWorkflowStartedTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &ResetWorkflowTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &CloseExecutionTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &DeleteHistoryEventTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &DecisionTimeoutTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &WorkflowTimeoutTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &CancelExecutionTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &SignalExecutionTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &RecordChildExecutionCompletedTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &UpsertWorkflowSearchAttributesTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &StartChildExecutionTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &RecordWorkflowClosedTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &ActivityTimeoutTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &UserTimerTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &ActivityRetryTimerTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &WorkflowBackoffTimerTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &HistoryReplicationTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &SyncActivityTask{WorkflowIdentifier: emptyIdentifier, TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}}, &FailoverMarkerTask{TaskData: TaskData{Version: 1, TaskID: 1, VisibilityTimestamp: timeNow}, DomainID: ""}, } t.Run("All task types with valid identifiers should not be corrupted", func(t *testing.T) { for i, task := range validTasks { result := IsTaskCorrupted(task) assert.False(t, result, "Task type %T at index %d should not be corrupted", task, i) } }) t.Run("All task types with empty identifiers should be corrupted", func(t *testing.T) { for i, task := range corruptedTasks { result := IsTaskCorrupted(task) assert.True(t, result, "Task type %T at index %d should be corrupted", task, i) } }) } ================================================ FILE: common/persistence/versionHistory.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "bytes" "fmt" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) // NewVersionHistoryItem create a new version history item func NewVersionHistoryItem( inputEventID int64, inputVersion int64, ) *VersionHistoryItem { if inputEventID < 0 || (inputVersion < 0 && inputVersion != constants.EmptyVersion) { panic(fmt.Sprintf( "invalid version history item event ID: %v, version: %v", inputEventID, inputVersion, )) } return &VersionHistoryItem{EventID: inputEventID, Version: inputVersion} } // NewVersionHistoryItemFromInternalType create a new version history item from internal type object func NewVersionHistoryItemFromInternalType( input *types.VersionHistoryItem, ) *VersionHistoryItem { if input == nil { return nil } return NewVersionHistoryItem(input.EventID, input.Version) } // Duplicate duplicate VersionHistoryItem func (item *VersionHistoryItem) Duplicate() *VersionHistoryItem { return NewVersionHistoryItem(item.EventID, item.Version) } // ToInternalType return internal format of version history item func (item *VersionHistoryItem) ToInternalType() *types.VersionHistoryItem { if item == nil { return nil } return &types.VersionHistoryItem{ EventID: item.EventID, Version: item.Version, } } // Equals test if this version history item and input version history item are the same func (item *VersionHistoryItem) Equals(input *VersionHistoryItem) bool { return item.Version == input.Version && item.EventID == input.EventID } // NewVersionHistory create a new version history func NewVersionHistory( inputToken []byte, inputItems []*VersionHistoryItem, ) *VersionHistory { token := make([]byte, len(inputToken)) copy(token, inputToken) versionHistory := &VersionHistory{ BranchToken: token, Items: nil, } for _, item := range inputItems { if err := versionHistory.AddOrUpdateItem(item.Duplicate()); err != nil { panic(fmt.Sprintf("unable to initialize version history: %v", err)) } } return versionHistory } // NewVersionHistoryFromInternalType create a new version history from internal type object func NewVersionHistoryFromInternalType( input *types.VersionHistory, ) *VersionHistory { if input == nil { return nil } items := make([]*VersionHistoryItem, 0, len(input.Items)) for _, item := range input.Items { items = append(items, NewVersionHistoryItemFromInternalType(item)) } return NewVersionHistory(input.BranchToken, items) } // Duplicate duplicate VersionHistory func (v *VersionHistory) Duplicate() *VersionHistory { return NewVersionHistory(v.BranchToken, v.Items) } // ToInternalType return internal format of version history func (v *VersionHistory) ToInternalType() *types.VersionHistory { if v == nil { return nil } token := make([]byte, len(v.BranchToken)) copy(token, v.BranchToken) items := []*types.VersionHistoryItem{} for _, item := range v.Items { items = append(items, item.ToInternalType()) } tHistory := &types.VersionHistory{ BranchToken: token, Items: items, } return tHistory } // DuplicateUntilLCAItem duplicate the version history up until LCA item func (v *VersionHistory) DuplicateUntilLCAItem( lcaItem *VersionHistoryItem, ) (*VersionHistory, error) { versionHistory := NewVersionHistory(nil, nil) notFoundErr := &types.BadRequestError{ Message: "version history does not contains the LCA item.", } for _, item := range v.Items { if item.Version < lcaItem.Version { if err := versionHistory.AddOrUpdateItem(item); err != nil { return nil, err } } else if item.Version == lcaItem.Version { if lcaItem.EventID > item.EventID { return nil, notFoundErr } if err := versionHistory.AddOrUpdateItem(lcaItem); err != nil { return nil, err } return versionHistory, nil } else { return nil, notFoundErr } } return nil, notFoundErr } // SetBranchToken the overwrite the branch token func (v *VersionHistory) SetBranchToken( inputToken []byte, ) error { token := make([]byte, len(inputToken)) copy(token, inputToken) v.BranchToken = token return nil } // GetBranchToken return the branch token func (v *VersionHistory) GetBranchToken() []byte { token := make([]byte, len(v.BranchToken)) copy(token, v.BranchToken) return token } // AddOrUpdateItem updates the versionHistory slice func (v *VersionHistory) AddOrUpdateItem( item *VersionHistoryItem, ) error { if len(v.Items) == 0 { v.Items = []*VersionHistoryItem{item.Duplicate()} return nil } lastItem := v.Items[len(v.Items)-1] if item.Version < lastItem.Version { return &types.BadRequestError{Message: fmt.Sprintf( "cannot update version history with a lower version %v. Last version: %v", item.Version, lastItem.Version, )} } if item.EventID <= lastItem.EventID { return &types.BadRequestError{Message: fmt.Sprintf( "cannot add version history with a lower event id %v. Last event id: %v", item.EventID, lastItem.EventID, )} } if item.Version > lastItem.Version { // Add a new history v.Items = append(v.Items, item.Duplicate()) } else { // item.Version == lastItem.Version && item.EventID > lastItem.EventID // Update event ID lastItem.EventID = item.EventID } return nil } // ContainsItem check whether given version history item is included func (v *VersionHistory) ContainsItem( item *VersionHistoryItem, ) bool { prevEventID := constants.FirstEventID - 1 for _, currentItem := range v.Items { if item.Version == currentItem.Version { if prevEventID < item.EventID && item.EventID <= currentItem.EventID { return true } } else if item.Version < currentItem.Version { return false } prevEventID = currentItem.EventID } return false } // FindLCAItem returns the lowest common ancestor version history item func (v *VersionHistory) FindLCAItem( remote *VersionHistory, ) (*VersionHistoryItem, error) { localIndex := len(v.Items) - 1 remoteIndex := len(remote.Items) - 1 for localIndex >= 0 && remoteIndex >= 0 { localVersionItem := v.Items[localIndex] remoteVersionItem := remote.Items[remoteIndex] if localVersionItem.Version == remoteVersionItem.Version { if localVersionItem.EventID > remoteVersionItem.EventID { return remoteVersionItem.Duplicate(), nil } return localVersionItem.Duplicate(), nil } else if localVersionItem.Version > remoteVersionItem.Version { localIndex-- } else { // localVersionItem.Version < remoteVersionItem.Version remoteIndex-- } } return nil, &types.BadRequestError{ Message: "version history is malformed. No joint point found.", } } // IsLCAAppendable checks if a LCA version history item is appendable func (v *VersionHistory) IsLCAAppendable( item *VersionHistoryItem, ) bool { if len(v.Items) == 0 { panic("version history not initialized") } if item == nil { panic("version history item is null") } return *v.Items[len(v.Items)-1] == *item } // GetFirstItem return the first version history item func (v *VersionHistory) GetFirstItem() (*VersionHistoryItem, error) { if len(v.Items) == 0 { return nil, &types.BadRequestError{Message: "version history is empty."} } return v.Items[0].Duplicate(), nil } // GetLastItem return the last version history item func (v *VersionHistory) GetLastItem() (*VersionHistoryItem, error) { if len(v.Items) == 0 { return nil, &types.BadRequestError{Message: "version history is empty"} } return v.Items[len(v.Items)-1].Duplicate(), nil } // GetEventVersion return the corresponding event version of an event ID func (v *VersionHistory) GetEventVersion( eventID int64, ) (int64, error) { lastItem, err := v.GetLastItem() if err != nil { return 0, err } if eventID < constants.FirstEventID || eventID > lastItem.EventID { return 0, &types.BadRequestError{Message: "input event ID is not in range."} } // items are sorted by eventID & version // so the fist item with item event ID >= input event ID // the item version is the result for _, currentItem := range v.Items { if eventID <= currentItem.EventID { return currentItem.Version, nil } } return 0, &types.BadRequestError{Message: "input event ID is not in range."} } // IsEmpty indicate whether version history is empty func (v *VersionHistory) IsEmpty() bool { return len(v.Items) == 0 } // Equals test if this version history and input version history are the same func (v *VersionHistory) Equals( input *VersionHistory, ) bool { if !bytes.Equal(v.BranchToken, input.BranchToken) { return false } if len(v.Items) != len(input.Items) { return false } for index, localItem := range v.Items { incomingItem := input.Items[index] if !localItem.Equals(incomingItem) { return false } } return true } // NewVersionHistories create a new version histories func NewVersionHistories( versionHistory *VersionHistory, ) *VersionHistories { if versionHistory == nil { return nil } return &VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*VersionHistory{versionHistory}, } } // NewVersionHistoriesFromInternalType create a new version histories from internal type object func NewVersionHistoriesFromInternalType( input *types.VersionHistories, ) *VersionHistories { if input == nil { return nil } if len(input.Histories) == 0 { panic("version histories cannot have empty") } currentVersionHistoryIndex := int(input.GetCurrentVersionHistoryIndex()) versionHistories := NewVersionHistories(NewVersionHistoryFromInternalType(input.Histories[0])) for i := 1; i < len(input.Histories); i++ { _, _, err := versionHistories.AddVersionHistory(NewVersionHistoryFromInternalType(input.Histories[i])) if err != nil { panic(fmt.Sprintf("unable to initialize version histories: %v", err)) } } if currentVersionHistoryIndex != versionHistories.CurrentVersionHistoryIndex { panic("unable to initialize version histories: current index mismatch") } return versionHistories } // Duplicate duplicate VersionHistories func (h *VersionHistories) Duplicate() *VersionHistories { if h == nil { return nil } currentVersionHistoryIndex := h.CurrentVersionHistoryIndex histories := []*VersionHistory{} for _, history := range h.Histories { histories = append(histories, history.Duplicate()) } return &VersionHistories{ CurrentVersionHistoryIndex: currentVersionHistoryIndex, Histories: histories, } } // ToInternalType return internal format of version histories func (h *VersionHistories) ToInternalType() *types.VersionHistories { currentVersionHistoryIndex := h.CurrentVersionHistoryIndex histories := []*types.VersionHistory{} for _, history := range h.Histories { histories = append(histories, history.ToInternalType()) } return &types.VersionHistories{ CurrentVersionHistoryIndex: int32(currentVersionHistoryIndex), Histories: histories, } } // GetVersionHistory get the version history according to index provided func (h *VersionHistories) GetVersionHistory( branchIndex int, ) (*VersionHistory, error) { if branchIndex < 0 || branchIndex >= len(h.Histories) { return nil, &types.BadRequestError{Message: fmt.Sprintf("getting branch index: %d, available branch count: %d", branchIndex, len(h.Histories))} } return h.Histories[branchIndex], nil } // AddVersionHistory add a version history and return the whether current branch is changed func (h *VersionHistories) AddVersionHistory( v *VersionHistory, ) (bool, int, error) { if v == nil { return false, 0, &types.BadRequestError{Message: "version histories is null."} } // assuming existing version histories inside are valid incomingFirstItem, err := v.GetFirstItem() if err != nil { return false, 0, err } currentVersionHistory, err := h.GetVersionHistory(h.CurrentVersionHistoryIndex) if err != nil { return false, 0, err } currentFirstItem, err := currentVersionHistory.GetFirstItem() if err != nil { return false, 0, err } if incomingFirstItem.Version != currentFirstItem.Version { return false, 0, &types.BadRequestError{Message: "version history first item does not match."} } // TODO maybe we need more strict validation newVersionHistory := v.Duplicate() h.Histories = append(h.Histories, newVersionHistory) newVersionHistoryIndex := len(h.Histories) - 1 // check if need to switch current branch newLastItem, err := newVersionHistory.GetLastItem() if err != nil { return false, 0, err } currentLastItem, err := currentVersionHistory.GetLastItem() if err != nil { return false, 0, err } currentBranchChanged := false if newLastItem.Version > currentLastItem.Version { currentBranchChanged = true h.CurrentVersionHistoryIndex = newVersionHistoryIndex } return currentBranchChanged, newVersionHistoryIndex, nil } // FindLCAVersionHistoryIndexAndItem finds the lowest common ancestor version history index // along with corresponding item func (h *VersionHistories) FindLCAVersionHistoryIndexAndItem( incomingHistory *VersionHistory, ) (int, *VersionHistoryItem, error) { var versionHistoryIndex int var versionHistoryLength int var versionHistoryItem *VersionHistoryItem for index, localHistory := range h.Histories { item, err := localHistory.FindLCAItem(incomingHistory) if err != nil { return 0, nil, err } // if not set if versionHistoryItem == nil || // if seeing LCA item with higher event ID item.EventID > versionHistoryItem.EventID || // if seeing LCA item with equal event ID but shorter history (item.EventID == versionHistoryItem.EventID && len(localHistory.Items) < versionHistoryLength) { versionHistoryIndex = index versionHistoryLength = len(localHistory.Items) versionHistoryItem = item } } return versionHistoryIndex, versionHistoryItem, nil } // FindFirstVersionHistoryByItem find the first version history index and history which // contains the given version history item func (h *VersionHistories) FindFirstVersionHistoryByItem( item *VersionHistoryItem, ) (index int, history *VersionHistory, err error) { for index, localHistory := range h.Histories { if localHistory.ContainsItem(item) { return index, localHistory, nil } } return 0, nil, &types.BadRequestError{Message: "version histories does not contains given item."} } // IsRebuilt returns true if the current branch index's last write version is not the largest // among all branches' last write version func (h *VersionHistories) IsRebuilt() (bool, error) { currentVersionHistory, err := h.GetCurrentVersionHistory() if err != nil { return false, err } currentLastItem, err := currentVersionHistory.GetLastItem() if err != nil { return false, err } for _, versionHistory := range h.Histories { lastItem, err := versionHistory.GetLastItem() if err != nil { return false, err } if lastItem.Version > currentLastItem.Version { return true, nil } } return false, nil } // SetCurrentVersionHistoryIndex set the current branch index func (h *VersionHistories) SetCurrentVersionHistoryIndex( index int, ) error { if index < 0 || index >= len(h.Histories) { return &types.BadRequestError{Message: "invalid current branch index."} } h.CurrentVersionHistoryIndex = index return nil } // GetCurrentVersionHistoryIndex get the current branch index func (h *VersionHistories) GetCurrentVersionHistoryIndex() int { return h.CurrentVersionHistoryIndex } // GetCurrentVersionHistory get the current version history func (h *VersionHistories) GetCurrentVersionHistory() (*VersionHistory, error) { return h.GetVersionHistory(h.GetCurrentVersionHistoryIndex()) } ================================================ FILE: common/persistence/versionHistory_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) type ( versionHistorySuite struct { suite.Suite } versionHistoriesSuite struct { suite.Suite } ) func TestVersionHistorySuite(t *testing.T) { s := new(versionHistorySuite) suite.Run(t, s) } func TestVersionHistoriesSuite(t *testing.T) { s := new(versionHistoriesSuite) suite.Run(t, s) } func (s *versionHistorySuite) TestConversion() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) s.Equal(&VersionHistory{ BranchToken: BranchToken, Items: Items, }, history) s.Equal(history, NewVersionHistoryFromInternalType(history.ToInternalType())) } func (s *versionHistorySuite) TestDuplicateUntilLCAItem_Success() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) newHistory, err := history.DuplicateUntilLCAItem(NewVersionHistoryItem(2, 0)) s.NoError(err) newBranchToken := []byte("other random branch token") err = newHistory.SetBranchToken(newBranchToken) s.NoError(err) s.Equal(newBranchToken, newHistory.GetBranchToken()) s.Equal(NewVersionHistory( newBranchToken, []*VersionHistoryItem{{EventID: 2, Version: 0}}, ), newHistory) newHistory, err = history.DuplicateUntilLCAItem(NewVersionHistoryItem(5, 4)) s.NoError(err) newBranchToken = []byte("another random branch token") err = newHistory.SetBranchToken(newBranchToken) s.NoError(err) s.Equal(newBranchToken, newHistory.GetBranchToken()) s.Equal(NewVersionHistory( newBranchToken, []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, }, ), newHistory) newHistory, err = history.DuplicateUntilLCAItem(NewVersionHistoryItem(6, 4)) s.NoError(err) newBranchToken = []byte("yet another random branch token") err = newHistory.SetBranchToken(newBranchToken) s.NoError(err) s.Equal(newBranchToken, newHistory.GetBranchToken()) s.Equal(NewVersionHistory( newBranchToken, []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, }, ), newHistory) } func (s *versionHistorySuite) TestDuplicateUntilLCAItem_Failure() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) _, err := history.DuplicateUntilLCAItem(NewVersionHistoryItem(4, 0)) s.IsType(&types.BadRequestError{}, err) _, err = history.DuplicateUntilLCAItem(NewVersionHistoryItem(2, 1)) s.IsType(&types.BadRequestError{}, err) _, err = history.DuplicateUntilLCAItem(NewVersionHistoryItem(5, 3)) s.IsType(&types.BadRequestError{}, err) _, err = history.DuplicateUntilLCAItem(NewVersionHistoryItem(7, 5)) s.IsType(&types.BadRequestError{}, err) _, err = history.DuplicateUntilLCAItem(NewVersionHistoryItem(4, 0)) s.IsType(&types.BadRequestError{}, err) _, err = history.DuplicateUntilLCAItem(NewVersionHistoryItem(7, 4)) s.IsType(&types.BadRequestError{}, err) } func (s *versionHistorySuite) TestSetBranchToken() { Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(nil, Items) err := history.SetBranchToken([]byte("some random branch token")) s.NoError(err) } func (s *versionHistorySuite) TestAddOrUpdateItem_VersionIncrease() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) item := &VersionHistoryItem{ EventID: 8, Version: 5, } err := history.AddOrUpdateItem(item) s.NoError(err) s.Equal(NewVersionHistory( BranchToken, []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, {EventID: 8, Version: 5}, }, ), history) } func (s *versionHistorySuite) TestAddOrUpdateItem_EventIDIncrease() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) item := &VersionHistoryItem{ EventID: 8, Version: 4, } err := history.AddOrUpdateItem(item) s.NoError(err) s.Equal(NewVersionHistory( BranchToken, []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 8, Version: 4}, }, ), history) } func (s *versionHistorySuite) TestAddOrUpdateItem_Failed_LowerVersion() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) err := history.AddOrUpdateItem(NewVersionHistoryItem(8, 3)) s.Error(err) } func (s *versionHistorySuite) TestAddOrUpdateItem_Failed_SameVersion_EventIDNotIncreasing() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) err := history.AddOrUpdateItem(NewVersionHistoryItem(5, 4)) s.Error(err) err = history.AddOrUpdateItem(NewVersionHistoryItem(6, 4)) s.Error(err) } func (s *versionHistorySuite) TestAddOrUpdateItem_Failed_VersionNoIncreasing() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) err := history.AddOrUpdateItem(NewVersionHistoryItem(6, 3)) s.Error(err) err = history.AddOrUpdateItem(NewVersionHistoryItem(2, 3)) s.Error(err) err = history.AddOrUpdateItem(NewVersionHistoryItem(7, 3)) s.Error(err) } func (s *versionHistoriesSuite) TestContainsItem_True() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) prevEventID := constants.FirstEventID - 1 for _, item := range Items { for EventID := prevEventID + 1; EventID <= item.EventID; EventID++ { s.True(history.ContainsItem(NewVersionHistoryItem(EventID, item.Version))) } prevEventID = item.EventID } } func (s *versionHistoriesSuite) TestContainsItem_False() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) s.False(history.ContainsItem(NewVersionHistoryItem(4, 0))) s.False(history.ContainsItem(NewVersionHistoryItem(3, 1))) s.False(history.ContainsItem(NewVersionHistoryItem(7, 4))) s.False(history.ContainsItem(NewVersionHistoryItem(6, 5))) } func (s *versionHistorySuite) TestIsLCAAppendable_True() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) ret := history.IsLCAAppendable(NewVersionHistoryItem(6, 4)) s.True(ret) } func (s *versionHistorySuite) TestIsLCAAppendable_False_VersionNotMatch() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) ret := history.IsLCAAppendable(NewVersionHistoryItem(6, 7)) s.False(ret) } func (s *versionHistorySuite) TestIsLCAAppendable_False_EventIDNotMatch() { BranchToken := []byte("some random branch token") Items := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 6, Version: 4}, } history := NewVersionHistory(BranchToken, Items) ret := history.IsLCAAppendable(NewVersionHistoryItem(7, 4)) s.False(ret) } func (s *versionHistorySuite) TestFindLCAItem_ReturnLocal() { localBranchToken := []byte("local branch token") localItems := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, {EventID: 9, Version: 10}, } remoteBranchToken := []byte("remote branch token") remoteItems := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 7, Version: 4}, {EventID: 8, Version: 8}, {EventID: 11, Version: 12}, } localVersionHistory := NewVersionHistory(localBranchToken, localItems) remoteVersionHistory := NewVersionHistory(remoteBranchToken, remoteItems) item, err := localVersionHistory.FindLCAItem(remoteVersionHistory) s.NoError(err) s.Equal(NewVersionHistoryItem(5, 4), item) } func (s *versionHistorySuite) TestFindLCAItem_ReturnRemote() { localBranchToken := []byte("local branch token") localItems := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, {EventID: 9, Version: 10}, } remoteBranchToken := []byte("remote branch token") remoteItems := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 6, Version: 6}, {EventID: 11, Version: 12}, } localVersionHistory := NewVersionHistory(localBranchToken, localItems) remoteVersionHistory := NewVersionHistory(remoteBranchToken, remoteItems) item, err := localVersionHistory.FindLCAItem(remoteVersionHistory) s.NoError(err) s.Equal(NewVersionHistoryItem(6, 6), item) } func (s *versionHistorySuite) TestFindLCAItem_Error_NoLCA() { localBranchToken := []byte("local branch token") localItems := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, {EventID: 9, Version: 10}, } remoteBranchToken := []byte("remote branch token") remoteItems := []*VersionHistoryItem{ {EventID: 3, Version: 1}, {EventID: 7, Version: 2}, {EventID: 8, Version: 3}, } localVersionHistory := NewVersionHistory(localBranchToken, localItems) remoteVersionHistory := NewVersionHistory(remoteBranchToken, remoteItems) _, err := localVersionHistory.FindLCAItem(remoteVersionHistory) s.Error(err) } func (s *versionHistorySuite) TestGetFirstItem_Success() { BranchToken := []byte("some random branch token") item := NewVersionHistoryItem(3, 0) history := NewVersionHistory(BranchToken, []*VersionHistoryItem{item}) firstItem, err := history.GetFirstItem() s.NoError(err) s.Equal(item, firstItem) item = NewVersionHistoryItem(4, 0) err = history.AddOrUpdateItem(item) s.NoError(err) firstItem, err = history.GetFirstItem() s.NoError(err) s.Equal(item, firstItem) err = history.AddOrUpdateItem(NewVersionHistoryItem(7, 1)) s.NoError(err) firstItem, err = history.GetFirstItem() s.NoError(err) s.Equal(item, firstItem) } func (s *versionHistorySuite) TestGetFirstItem_Failure() { BranchToken := []byte("some random branch token") history := NewVersionHistory(BranchToken, []*VersionHistoryItem{}) _, err := history.GetFirstItem() s.IsType(&types.BadRequestError{}, err) } func (s *versionHistorySuite) TestGetLastItem_Success() { BranchToken := []byte("some random branch token") item := NewVersionHistoryItem(3, 0) history := NewVersionHistory(BranchToken, []*VersionHistoryItem{item}) lastItem, err := history.GetLastItem() s.NoError(err) s.Equal(item, lastItem) item = NewVersionHistoryItem(4, 0) err = history.AddOrUpdateItem(item) s.NoError(err) lastItem, err = history.GetLastItem() s.NoError(err) s.Equal(item, lastItem) item = NewVersionHistoryItem(7, 1) err = history.AddOrUpdateItem(item) s.NoError(err) lastItem, err = history.GetLastItem() s.NoError(err) s.Equal(item, lastItem) } func (s *versionHistorySuite) TestGetLastItem_Failure() { BranchToken := []byte("some random branch token") history := NewVersionHistory(BranchToken, []*VersionHistoryItem{}) _, err := history.GetLastItem() s.IsType(&types.BadRequestError{}, err) } func (s *versionHistoriesSuite) TestGetVersion_Success() { BranchToken := []byte("some random branch token") item1 := NewVersionHistoryItem(3, 0) item2 := NewVersionHistoryItem(6, 8) item3 := NewVersionHistoryItem(8, 12) history := NewVersionHistory(BranchToken, []*VersionHistoryItem{item1, item2, item3}) Version, err := history.GetEventVersion(1) s.NoError(err) s.Equal(item1.Version, Version) Version, err = history.GetEventVersion(2) s.NoError(err) s.Equal(item1.Version, Version) Version, err = history.GetEventVersion(3) s.NoError(err) s.Equal(item1.Version, Version) Version, err = history.GetEventVersion(4) s.NoError(err) s.Equal(item2.Version, Version) Version, err = history.GetEventVersion(5) s.NoError(err) s.Equal(item2.Version, Version) Version, err = history.GetEventVersion(6) s.NoError(err) s.Equal(item2.Version, Version) Version, err = history.GetEventVersion(7) s.NoError(err) s.Equal(item3.Version, Version) Version, err = history.GetEventVersion(8) s.NoError(err) s.Equal(item3.Version, Version) } func (s *versionHistoriesSuite) TestGetVersion_Failure() { BranchToken := []byte("some random branch token") item1 := NewVersionHistoryItem(3, 0) item2 := NewVersionHistoryItem(6, 8) item3 := NewVersionHistoryItem(8, 12) history := NewVersionHistory(BranchToken, []*VersionHistoryItem{item1, item2, item3}) _, err := history.GetEventVersion(0) s.Error(err) _, err = history.GetEventVersion(9) s.Error(err) } func (s *versionHistorySuite) TestEquals() { localBranchToken := []byte("local branch token") localItems := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, {EventID: 9, Version: 10}, } remoteBranchToken := []byte("remote branch token") remoteItems := []*VersionHistoryItem{ {EventID: 3, Version: 1}, {EventID: 7, Version: 2}, {EventID: 8, Version: 3}, } localVersionHistory := NewVersionHistory(localBranchToken, localItems) remoteVersionHistory := NewVersionHistory(remoteBranchToken, remoteItems) s.False(localVersionHistory.Equals(remoteVersionHistory)) s.True(localVersionHistory.Equals(localVersionHistory.Duplicate())) s.True(remoteVersionHistory.Equals(remoteVersionHistory.Duplicate())) } func (s *versionHistoriesSuite) TestConversion() { BranchToken := []byte("some random branch token") localItems := []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, {EventID: 9, Version: 10}, } versionHistory := NewVersionHistory(BranchToken, localItems) histories := NewVersionHistories(versionHistory) s.Equal(&VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*VersionHistory{versionHistory}, }, histories) s.Equal(histories, NewVersionHistoriesFromInternalType(histories.ToInternalType())) } func (s *versionHistoriesSuite) TestAddGetVersionHistory() { versionHistory1 := NewVersionHistory([]byte("branch token 1"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, {EventID: 9, Version: 10}, }) versionHistory2 := NewVersionHistory([]byte("branch token 2"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 6, Version: 6}, {EventID: 11, Version: 12}, }) histories := NewVersionHistories(versionHistory1) s.Equal(0, histories.CurrentVersionHistoryIndex) currentBranchChanged, newVersionHistoryIndex, err := histories.AddVersionHistory(versionHistory2) s.Nil(err) s.True(currentBranchChanged) s.Equal(1, newVersionHistoryIndex) s.Equal(1, histories.CurrentVersionHistoryIndex) resultVersionHistory1, err := histories.GetVersionHistory(0) s.Nil(err) s.Equal(versionHistory1, resultVersionHistory1) resultVersionHistory2, err := histories.GetVersionHistory(1) s.Nil(err) s.Equal(versionHistory2, resultVersionHistory2) } func (s *versionHistoriesSuite) TestFindLCAVersionHistoryIndexAndItem_LargerEventIDWins() { versionHistory1 := NewVersionHistory([]byte("branch token 1"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, {EventID: 9, Version: 10}, }) versionHistory2 := NewVersionHistory([]byte("branch token 2"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 6, Version: 6}, {EventID: 11, Version: 12}, }) histories := NewVersionHistories(versionHistory1) _, _, err := histories.AddVersionHistory(versionHistory2) s.Nil(err) versionHistoryIncoming := NewVersionHistory([]byte("branch token incoming"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 8, Version: 6}, {EventID: 11, Version: 100}, }) index, item, err := histories.FindLCAVersionHistoryIndexAndItem(versionHistoryIncoming) s.Nil(err) s.Equal(0, index) s.Equal(NewVersionHistoryItem(7, 6), item) } func (s *versionHistoriesSuite) TestFindLCAVersionHistoryIndexAndItem_SameEventIDShorterLengthWins() { versionHistory1 := NewVersionHistory([]byte("branch token 1"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, {EventID: 9, Version: 10}, }) versionHistory2 := NewVersionHistory([]byte("branch token 2"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, }) histories := NewVersionHistories(versionHistory1) _, _, err := histories.AddVersionHistory(versionHistory2) s.Nil(err) versionHistoryIncoming := NewVersionHistory([]byte("branch token incoming"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 8, Version: 6}, {EventID: 11, Version: 100}, }) index, item, err := histories.FindLCAVersionHistoryIndexAndItem(versionHistoryIncoming) s.Nil(err) s.Equal(1, index) s.Equal(NewVersionHistoryItem(7, 6), item) } func (s *versionHistoriesSuite) TestFindFirstVersionHistoryByItem() { versionHistory1 := NewVersionHistory([]byte("branch token 1"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, }) versionHistory2 := NewVersionHistory([]byte("branch token 2"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, {EventID: 9, Version: 10}, }) histories := NewVersionHistories(versionHistory1) _, _, err := histories.AddVersionHistory(versionHistory2) s.Nil(err) index, history, err := histories.FindFirstVersionHistoryByItem(NewVersionHistoryItem(8, 10)) s.NoError(err) s.Equal(1, index) s.Equal(versionHistory2, history) index, history, err = histories.FindFirstVersionHistoryByItem(NewVersionHistoryItem(4, 4)) s.NoError(err) s.Equal(0, index) s.Equal(versionHistory1, history) _, _, err = histories.FindFirstVersionHistoryByItem(NewVersionHistoryItem(41, 4)) s.Error(err) } func (s *versionHistoriesSuite) TestCurrentVersionHistoryIndexIsInReplay() { versionHistory1 := NewVersionHistory([]byte("branch token 1"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 7, Version: 6}, {EventID: 9, Version: 10}, }) versionHistory2 := NewVersionHistory([]byte("branch token 2"), []*VersionHistoryItem{ {EventID: 3, Version: 0}, {EventID: 5, Version: 4}, {EventID: 6, Version: 6}, {EventID: 11, Version: 12}, }) histories := NewVersionHistories(versionHistory1) s.Equal(0, histories.CurrentVersionHistoryIndex) currentBranchChanged, newVersionHistoryIndex, err := histories.AddVersionHistory(versionHistory2) s.Nil(err) s.True(currentBranchChanged) s.Equal(1, newVersionHistoryIndex) s.Equal(1, histories.CurrentVersionHistoryIndex) isInReplay, err := histories.IsRebuilt() s.NoError(err) s.False(isInReplay) err = histories.SetCurrentVersionHistoryIndex(0) s.NoError(err) isInReplay, err = histories.IsRebuilt() s.NoError(err) s.True(isInReplay) err = histories.SetCurrentVersionHistoryIndex(1) s.NoError(err) isInReplay, err = histories.IsRebuilt() s.NoError(err) s.False(isInReplay) } func TestNilHandling(t *testing.T) { assert.Nil(t, NewVersionHistoriesFromInternalType(nil)) assert.Nil(t, NewVersionHistories(nil)) assert.Nil(t, NewVersionHistoryItemFromInternalType(nil)) assert.Nil(t, NewVersionHistoryFromInternalType(nil)) var vh *VersionHistory assert.Nil(t, vh.ToInternalType()) } ================================================ FILE: common/persistence/visibility_hybrid_manager.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "context" "fmt" "math/rand" "strings" "time" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) type ( visibilityHybridManager struct { logger log.Logger visibilityMgrs map[string]VisibilityManager readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter writeVisibilityStoreName dynamicproperties.StringPropertyFn logCustomerQueryParameter dynamicproperties.BoolPropertyFnWithDomainFilter name string } ) const ( ContextKey = ResponseComparatorContextKey("visibility-override") dbVisStoreName = "db" advancedWriteModeOff = "off" ) // ResponseComparatorContextKey is for Pinot/ES response comparator. This struct will be passed into ctx as a key. type ResponseComparatorContextKey string type OperationType string var Operation = struct { LIST OperationType COUNT OperationType }{ LIST: "list", COUNT: "count", } var _ VisibilityManager = (*visibilityHybridManager)(nil) // NewVisibilityTripleManager create a visibility manager that operate on DB or advanced visibility based on dynamic config. // For Pinot migration, Pinot is the destination visibility manager, ES is the source visibility manager, and DB is the fallback. // For OpenSearch migration, OS is the destination visibility manager, ES is the source visibility manager, and DB is the fallback. func NewVisibilityHybridManager( visibilityMgrs map[string]VisibilityManager, readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter, writeVisibilityStoreName dynamicproperties.StringPropertyFn, logCustomerQueryParameter dynamicproperties.BoolPropertyFnWithDomainFilter, name string, logger log.Logger, ) VisibilityManager { if len(visibilityMgrs) == 0 { logger.Fatal("No visibility managers provided. At least one visibility manager is required.") return nil } if logCustomerQueryParameter == nil { logCustomerQueryParameter = dynamicproperties.GetBoolPropertyFnFilteredByDomain(false) } return &visibilityHybridManager{ visibilityMgrs: visibilityMgrs, readVisibilityStoreName: readVisibilityStoreName, writeVisibilityStoreName: writeVisibilityStoreName, logger: logger, logCustomerQueryParameter: logCustomerQueryParameter, name: name, } } func (v *visibilityHybridManager) Close() { for _, mgr := range v.visibilityMgrs { if mgr != nil { mgr.Close() } } } func (v *visibilityHybridManager) GetName() string { return v.name } func (v *visibilityHybridManager) RecordWorkflowExecutionStarted( ctx context.Context, request *RecordWorkflowExecutionStartedRequest, ) error { return v.chooseVisibilityManagerForWrite( ctx, func(storeName string) error { mgr, ok := v.visibilityMgrs[storeName] if !ok || mgr == nil { return fmt.Errorf("Visibility store manager with name %s not found", storeName) } return mgr.RecordWorkflowExecutionStarted(ctx, request) }, ) } func (v *visibilityHybridManager) RecordWorkflowExecutionClosed( ctx context.Context, request *RecordWorkflowExecutionClosedRequest, ) error { return v.chooseVisibilityManagerForWrite( ctx, func(storeName string) error { mgr, ok := v.visibilityMgrs[storeName] if !ok || mgr == nil { return fmt.Errorf("Visibility store manager with name %s not found", storeName) } return mgr.RecordWorkflowExecutionClosed(ctx, request) }, ) } func (v *visibilityHybridManager) RecordWorkflowExecutionUninitialized( ctx context.Context, request *RecordWorkflowExecutionUninitializedRequest, ) error { return v.chooseVisibilityManagerForWrite( ctx, func(storeName string) error { mgr, ok := v.visibilityMgrs[storeName] if !ok || mgr == nil { return fmt.Errorf("Visibility store manager with name %s not found", storeName) } return mgr.RecordWorkflowExecutionUninitialized(ctx, request) }, ) } func (v *visibilityHybridManager) DeleteWorkflowExecution( ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest, ) error { return v.chooseVisibilityManagerForWrite( ctx, func(storeName string) error { mgr, ok := v.visibilityMgrs[storeName] if !ok || mgr == nil { return fmt.Errorf("Visibility store manager with name %s not found", storeName) } return mgr.DeleteWorkflowExecution(ctx, request) }, ) } func (v *visibilityHybridManager) DeleteUninitializedWorkflowExecution( ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest, ) error { return v.chooseVisibilityManagerForWrite( ctx, func(storeName string) error { mgr, ok := v.visibilityMgrs[storeName] if !ok || mgr == nil { return fmt.Errorf("Visibility store manager with name %s not found", storeName) } return mgr.DeleteUninitializedWorkflowExecution(ctx, request) }, ) } func (v *visibilityHybridManager) UpsertWorkflowExecution( ctx context.Context, request *UpsertWorkflowExecutionRequest, ) error { return v.chooseVisibilityManagerForWrite( ctx, func(storeName string) error { mgr, ok := v.visibilityMgrs[storeName] if !ok || mgr == nil { return fmt.Errorf("Visibility store manager with name %s not found", storeName) } return mgr.UpsertWorkflowExecution(ctx, request) }, ) } func (v *visibilityHybridManager) chooseVisibilityModeForAdmin() string { var modes []string for storeName, mgr := range v.visibilityMgrs { if mgr != nil { modes = append(modes, storeName) } } if len(modes) == 0 { return "INVALID_ADMIN_MODE" } return strings.Join(modes, ",") } func (v *visibilityHybridManager) chooseVisibilityManagerForWrite(ctx context.Context, visFunc func(string) error) error { var writeMode string if v.writeVisibilityStoreName != nil { writeMode = v.writeVisibilityStoreName() } else { key := VisibilityAdminDeletionKey("visibilityAdminDelete") if value := ctx.Value(key); value != nil && value.(bool) { writeMode = v.chooseVisibilityModeForAdmin() } } modes := strings.Split(writeMode, ",") for i := range modes { modes[i] = strings.ToLower(strings.TrimSpace(modes[i])) } var errors []string for _, mode := range modes { if mode == advancedWriteModeOff { mode = dbVisStoreName } if mgr, ok := v.visibilityMgrs[mode]; ok && mgr != nil { if err := visFunc(mode); err != nil { errors = append(errors, err.Error()) } } else if mode != dbVisStoreName && !strings.Contains(writeMode, dbVisStoreName) { // If requested mode is not available and it's not already "db", fall back to "db" // when write mode already includes db, skip this step since it will perform the write in another loop v.logger.Warn("requested visibility mode is not available, falling back to db", tag.Value(mode)) if err := visFunc(dbVisStoreName); err != nil { errors = append(errors, err.Error()) } } else { // If the mode is "db" but not available, this is an error // This is the else case - when mode is "db" but the manager is not available errors = append(errors, fmt.Sprintf("DB visibility mode is not available: %s", mode)) } } if len(errors) > 0 { return &types.InternalServiceError{ Message: fmt.Sprintf("Error writing to visibility: %v", strings.Join(errors, "; ")), } } return nil } // For Pinot Migration uses. It will be a temporary usage type userParameters struct { operation string domainName string workflowType string workflowID string closeStatus int // if it is -1, then will have --open flag in comparator workflow customQuery string earliestTime int64 latestTime int64 } // For Visibility Migration uses. It will be a temporary usage // logUserQueryParameters will log user queries' parameters so that a comparator workflow can consume func (v *visibilityHybridManager) logUserQueryParameters(userParam userParameters, domain string, override bool) { // Don't log if it is not enabled // don't log if it is a call from Pinot Response Comparator workflow if !v.logCustomerQueryParameter(domain) || override { return } randNum := rand.Intn(10) if randNum != 5 { // Intentionally to have 1/10 chance to log custom query parameters return } v.logger.Info("Logging user query parameters for visibility migration response comparator...", tag.OperationName(userParam.operation), tag.WorkflowDomainName(userParam.domainName), tag.WorkflowType(userParam.workflowType), tag.WorkflowID(userParam.workflowID), tag.WorkflowCloseStatus(userParam.closeStatus), tag.VisibilityQuery(filterAttrPrefix(userParam.customQuery)), tag.EarliestTime(userParam.earliestTime), tag.LatestTime(userParam.latestTime)) } // This is for only logUserQueryParameters (for Pinot Response comparator) usage. // Be careful because there's a low possibility that there'll be false positive cases (shown in unit tests) func filterAttrPrefix(str string) string { str = strings.Replace(str, "`Attr.", "", -1) return strings.Replace(str, "`", "", -1) } func (v *visibilityHybridManager) ListOpenWorkflowExecutions( ctx context.Context, request *ListWorkflowExecutionsRequest, ) (*ListWorkflowExecutionsResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.ListOpenWorkflowExecutions, request, v.logger) } // return result from primary return manager.ListOpenWorkflowExecutions(ctx, request) } func (v *visibilityHybridManager) ListClosedWorkflowExecutions( ctx context.Context, request *ListWorkflowExecutionsRequest, ) (*ListWorkflowExecutionsResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.ListClosedWorkflowExecutions, request, v.logger) } return manager.ListClosedWorkflowExecutions(ctx, request) } func (v *visibilityHybridManager) ListOpenWorkflowExecutionsByType( ctx context.Context, request *ListWorkflowExecutionsByTypeRequest, ) (*ListWorkflowExecutionsResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.ListOpenWorkflowExecutionsByType, request, v.logger) } return manager.ListOpenWorkflowExecutionsByType(ctx, request) } func (v *visibilityHybridManager) ListClosedWorkflowExecutionsByType( ctx context.Context, request *ListWorkflowExecutionsByTypeRequest, ) (*ListWorkflowExecutionsResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.ListClosedWorkflowExecutionsByType, request, v.logger) } return manager.ListClosedWorkflowExecutionsByType(ctx, request) } func (v *visibilityHybridManager) ListOpenWorkflowExecutionsByWorkflowID( ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest, ) (*ListWorkflowExecutionsResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.ListOpenWorkflowExecutionsByWorkflowID, request, v.logger) } return manager.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) } func (v *visibilityHybridManager) ListClosedWorkflowExecutionsByWorkflowID( ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest, ) (*ListWorkflowExecutionsResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.ListClosedWorkflowExecutionsByWorkflowID, request, v.logger) } return manager.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) } func (v *visibilityHybridManager) ListClosedWorkflowExecutionsByStatus( ctx context.Context, request *ListClosedWorkflowExecutionsByStatusRequest, ) (*ListWorkflowExecutionsResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.ListClosedWorkflowExecutionsByStatus, request, v.logger) } return manager.ListClosedWorkflowExecutionsByStatus(ctx, request) } func (v *visibilityHybridManager) GetClosedWorkflowExecution( ctx context.Context, request *GetClosedWorkflowExecutionRequest, ) (*GetClosedWorkflowExecutionResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.GetClosedWorkflowExecution, request, v.logger) } return manager.GetClosedWorkflowExecution(ctx, request) } func (v *visibilityHybridManager) ListWorkflowExecutions( ctx context.Context, request *ListWorkflowExecutionsByQueryRequest, ) (*ListWorkflowExecutionsResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.ListWorkflowExecutions, request, v.logger) } return manager.ListWorkflowExecutions(ctx, request) } func (v *visibilityHybridManager) ScanWorkflowExecutions( ctx context.Context, request *ListWorkflowExecutionsByQueryRequest, ) (*ListWorkflowExecutionsResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.ScanWorkflowExecutions, request, v.logger) } return manager.ScanWorkflowExecutions(ctx, request) } func (v *visibilityHybridManager) CountWorkflowExecutions( ctx context.Context, request *CountWorkflowExecutionsRequest, ) (*CountWorkflowExecutionsResponse, error) { manager, shadowMgr := v.chooseVisibilityManagerForRead(ctx, request.Domain) if shadowMgr != nil { go shadow(shadowMgr.CountWorkflowExecutions, request, v.logger) } return manager.CountWorkflowExecutions(ctx, request) } func (v *visibilityHybridManager) chooseVisibilityManagerForRead(ctx context.Context, domain string) (VisibilityManager, VisibilityManager) { var visibilityMgr, shadowMgr VisibilityManager stores := strings.Split(v.readVisibilityStoreName(domain), ",") for i := range stores { stores[i] = strings.ToLower(strings.TrimSpace(stores[i])) } readStore := stores[0] // if read stores have more than 1, the others will go shadow read if v.visibilityMgrs[readStore] != nil { visibilityMgr = v.visibilityMgrs[readStore] } else { v.logger.Warn("domain is configured to read from advanced visibility but it's not available, fall back to basic visibility", tag.WorkflowDomainName(domain)) visibilityMgr = v.visibilityMgrs[dbVisStoreName] //db will always be available } if len(stores) > 1 { shadowMgr = v.visibilityMgrs[stores[1]] } return visibilityMgr, shadowMgr } func shadow[ReqT any, ResT any](f func(ctx context.Context, request ReqT) (ResT, error), request ReqT, logger log.Logger) { ctxNew, cancel := context.WithTimeout(context.Background(), 30*time.Second) // don't want f to run too long defer cancel() defer func() { if r := recover(); r != nil { logger.Info(fmt.Sprintf("Recovered in Shadow function in double read: %v", r)) } }() _, err := f(ctxNew, request) if err != nil { logger.Error(fmt.Sprintf("Error in Shadow function in double read: %s", err.Error())) } } ================================================ FILE: common/persistence/visibility_hybrid_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "fmt" "sync" "testing" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" ) const ( dualStoreName = "es,pinot" // test dual read for es and pinot, with es as primary dualStorePinotPrimary = "pinot,es" // test dual read for es and pinot, with pinot as primary tripleStoreName = "es,pinot,db" // test triple read for es, pinot and db dualReadStoreName = "es,db" // test dual read for es and db dualReadStoreDBPrimary = "db,es" // test dual read for es and db with db as primary esStoreName = "es" pinotStoreName = "pinot" testStoreName = "test" ) func TestNewVisibilityHybridManager(t *testing.T) { // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager }{ "Case1: nil case": { mockDBVisibilityManager: nil, mockESVisibilityManager: nil, mockPinotVisibilityManager: nil, }, "Case2: success case": { mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { assert.NotPanics(t, func() { visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } NewVisibilityHybridManager(visibilityMgrs, nil, dynamicproperties.GetStringPropertyFn(esStoreName), nil, testStoreName, log.NewNoop()) }) }) } } func TestNewVisibilityHybridManager_EmptyVisibilityMgr(t *testing.T) { // put this outside because need to use it as an input of the table tests assert.NotPanics(t, func() { visibilityMgrs := map[string]VisibilityManager{} NewVisibilityHybridManager(visibilityMgrs, nil, nil, nil, testStoreName, log.NewNoop()) }) } func TestVisibilityHybridManagerClose(t *testing.T) { // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { mockDBVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(mockESVisibilityManager *MockVisibilityManager) }{ "Case1-1: success case with DB visibility is not nil": { mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().Close().Return().Times(1) }, }, "Case1-2: success case with ES visibility is not nil": { mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().Close().Return().Times(1) }, }, "Case1-3: success case with pinot visibility is not nil": { mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().Close().Return().Times(1) }, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, nil, dynamicproperties.GetStringPropertyFn(esStoreName), nil, testStoreName, log.NewNoop()) assert.NotPanics(t, func() { visibilityManager.Close() }) }) } } func TestVisibilityHybridManagerGetName(t *testing.T) { visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: NewMockVisibilityManager(gomock.NewController(t)), } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, nil, dynamicproperties.GetStringPropertyFn(dbVisStoreName), nil, testStoreName, log.NewNoop()) assert.Equal(t, testStoreName, visibilityManager.GetName()) } func TestVisibilityHybridRecordWorkflowExecutionStarted(t *testing.T) { request := &RecordWorkflowExecutionStartedRequest{} // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *RecordWorkflowExecutionStartedRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(mockESVisibilityManager *MockVisibilityManager) writeVisibilityStoreName dynamicproperties.StringPropertyFn expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(advancedWriteModeOff), expectedError: nil, }, "Case1-2: success case with ES visibility is not nil": { request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(esStoreName), expectedError: nil, }, "Case1-3: success case with pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(pinotStoreName), }, "Case1-4: success case with ES visibility is nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(esStoreName), expectedError: fmt.Errorf("error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, nil, test.writeVisibilityStoreName, nil, testStoreName, log.NewNoop()) err := visibilityManager.RecordWorkflowExecutionStarted(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestVisibilityHybridRecordWorkflowExecutionClosed(t *testing.T) { request := &RecordWorkflowExecutionClosedRequest{} // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { context context.Context request *RecordWorkflowExecutionClosedRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(mockESVisibilityManager *MockVisibilityManager) writeVisibilityStoreName dynamicproperties.StringPropertyFn expectedError error }{ "Case0-1: success case with writeVisibilityStoreName is nil - should fall back to db": { context: context.Background(), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, expectedError: nil, // Should succeed by falling back to db }, "Case0-2: error case with ES has errors in dual mode": { context: context.Background(), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).AnyTimes() }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).AnyTimes() }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(tripleStoreName), expectedError: fmt.Errorf("error"), }, "Case0-3: error case with ES has errors in On mode with Pinot is not nil": { context: context.Background(), request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).AnyTimes() }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).AnyTimes() }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(dualStoreName), expectedError: fmt.Errorf("error"), }, "Case0-4: error case with Pinot has errors in On mode": { context: context.Background(), request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).AnyTimes() }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(dualStoreName), expectedError: fmt.Errorf("error"), }, "Case0-5: error case with Pinot has errors in Dual mode": { context: context.Background(), request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).AnyTimes() }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(dualStoreName), expectedError: fmt.Errorf("error"), }, "Case0-6: error case with ES has errors in Dual mode": { context: context.Background(), request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).AnyTimes() }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(dualStoreName), expectedError: fmt.Errorf("error"), }, "Case1-1: success case with DB visibility is not nil": { context: context.Background(), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(advancedWriteModeOff), expectedError: nil, }, "Case1-2: success case with ES visibility is not nil": { context: context.Background(), request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(esStoreName), }, "Case1-3: success case with pinot visibility is not nil": { context: context.Background(), request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(pinotStoreName), }, "Case1-4: success case with dual manager": { context: context.Background(), request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(dualStoreName), }, "Case1-5: success case with triple manager when ES and Pinot are not nil": { context: context.Background(), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(tripleStoreName), }, "Case2-1: choose both when ES is nil, fall back to db": { context: context.Background(), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(dualStoreName), expectedError: nil, }, "Case2-2: choose both when Pinot is nil": { context: context.Background(), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(dualStoreName), expectedError: nil, }, "Case3-1: chooseVisibilityModeForAdmin when ES is nil": { context: context.WithValue(context.Background(), VisibilityAdminDeletionKey("visibilityAdminDelete"), true), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, }, "Case3-2: chooseVisibilityModeForAdmin when DB is nil": { context: context.WithValue(context.Background(), VisibilityAdminDeletionKey("visibilityAdminDelete"), true), request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, }, "Case3-3: chooseVisibilityModeForAdmin when both are not nil": { context: context.WithValue(context.Background(), VisibilityAdminDeletionKey("visibilityAdminDelete"), true), request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, }, "Case3-3: chooseVisibilityModeForAdmin when triple are not nil": { context: context.WithValue(context.Background(), VisibilityAdminDeletionKey("visibilityAdminDelete"), true), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() }, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, nil, test.writeVisibilityStoreName, nil, testStoreName, log.NewNoop()) err := visibilityManager.RecordWorkflowExecutionClosed(test.context, test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } // test an edge case func TestVisibilityHybridChooseVisibilityModeForAdmin(t *testing.T) { ctrl := gomock.NewController(t) dbManager := NewMockVisibilityManager(ctrl) esManager := NewMockVisibilityManager(ctrl) pntManager := NewMockVisibilityManager(ctrl) visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: dbManager, esStoreName: esManager, pinotStoreName: pntManager, } mgr := NewVisibilityHybridManager(visibilityMgrs, nil, nil, nil, testStoreName, log.NewNoop()) tripleManager := mgr.(*visibilityHybridManager) tripleManager.visibilityMgrs[dbVisStoreName] = nil tripleManager.visibilityMgrs[esStoreName] = nil tripleManager.visibilityMgrs[pinotStoreName] = nil assert.Equal(t, "INVALID_ADMIN_MODE", tripleManager.chooseVisibilityModeForAdmin()) } func TestVisibilityHybridRecordWorkflowExecutionUninitialized(t *testing.T) { request := &RecordWorkflowExecutionUninitializedRequest{} // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *RecordWorkflowExecutionUninitializedRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(mockESVisibilityManager *MockVisibilityManager) writeVisibilityStoreName dynamicproperties.StringPropertyFn expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().RecordWorkflowExecutionUninitialized(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(advancedWriteModeOff), expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionUninitialized(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionUninitialized(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(dualStoreName), expectedError: nil, }, "Case1-3: success case with ES visibility is not nil": { request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().RecordWorkflowExecutionUninitialized(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().RecordWorkflowExecutionUninitialized(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(dualStoreName), expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, nil, test.writeVisibilityStoreName, nil, testStoreName, log.NewNoop()) err := visibilityManager.RecordWorkflowExecutionUninitialized(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestVisibilityHybridUpsertWorkflowExecution(t *testing.T) { request := &UpsertWorkflowExecutionRequest{} // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *UpsertWorkflowExecutionRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(mockESVisibilityManager *MockVisibilityManager) writeVisibilityStoreName dynamicproperties.StringPropertyFn expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().UpsertWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(advancedWriteModeOff), expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().UpsertWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(pinotStoreName), expectedError: nil, }, "Case1-3: success case with ES visibility is not nil": { request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().UpsertWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(esStoreName), expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, nil, test.writeVisibilityStoreName, nil, testStoreName, log.NewNoop()) err := visibilityManager.UpsertWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestVisibilityHybridDeleteWorkflowExecution(t *testing.T) { request := &VisibilityDeleteWorkflowExecutionRequest{} // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *VisibilityDeleteWorkflowExecutionRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(mockESVisibilityManager *MockVisibilityManager) writeVisibilityStoreName dynamicproperties.StringPropertyFn expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(advancedWriteModeOff), expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(pinotStoreName), expectedError: nil, }, "Case1-3: success case with ES visibility is not nil": { request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(esStoreName), expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, nil, test.writeVisibilityStoreName, nil, testStoreName, log.NewNoop()) err := visibilityManager.DeleteWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestVisibilityHybridDeleteUninitializedWorkflowExecution(t *testing.T) { request := &VisibilityDeleteWorkflowExecutionRequest{} // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *VisibilityDeleteWorkflowExecutionRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(mockESVisibilityManager *MockVisibilityManager) writeVisibilityStoreName dynamicproperties.StringPropertyFn expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().DeleteUninitializedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(advancedWriteModeOff), expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().DeleteUninitializedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(pinotStoreName), expectedError: nil, }, "Case1-3: success case with ES visibility is not nil": { request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().DeleteUninitializedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(esStoreName), expectedError: nil, }, "Case1-4: success case with both are not nil": { request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().DeleteUninitializedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().DeleteUninitializedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, writeVisibilityStoreName: dynamicproperties.GetStringPropertyFn(dualStoreName), expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, nil, test.writeVisibilityStoreName, nil, testStoreName, log.NewNoop()) err := visibilityManager.DeleteUninitializedWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestFilterAttrPrefix(t *testing.T) { tests := map[string]struct { expectedInput string expectedOutput string }{ "Case1: empty input": { expectedInput: "", expectedOutput: "", }, "Case2: filtered input": { expectedInput: "`Attr.CustomIntField` = 12", expectedOutput: "CustomIntField = 12", }, "Case3: complex input": { expectedInput: "WorkflowID = 'test-wf' and (`Attr.CustomIntField` = 12 or `Attr.CustomStringField` = 'a-b-c' and WorkflowType = 'wf-type')", expectedOutput: "WorkflowID = 'test-wf' and (CustomIntField = 12 or CustomStringField = 'a-b-c' and WorkflowType = 'wf-type')", }, "Case4: false positive case": { expectedInput: "`Attr.CustomStringField` = '`Attr.ABCtesting'", expectedOutput: "CustomStringField = 'ABCtesting'", // this is supposed to be CustomStringField = '`Attr.ABCtesting' }, } for name, test := range tests { t.Run(name, func(t *testing.T) { assert.NotPanics(t, func() { actualOutput := filterAttrPrefix(test.expectedInput) assert.Equal(t, test.expectedOutput, actualOutput) }) }) } } func TestVisibilityHybridListOpenWorkflowExecutions(t *testing.T) { request := &ListWorkflowExecutionsRequest{ Domain: "test-domain", } // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *ListWorkflowExecutionsRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(esStoreName), wgCount: 0, expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, "Case2-2: Pinot nil case with double read": { request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 0, expectedError: nil, }, "Case2-3: Read mode is from ES with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, "Case2-4: double read with an error": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()). Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, "Case2-5: double read with panic": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() panic("test panic") }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()). Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.ListOpenWorkflowExecutions(context.Background(), test.request) wg.Wait() if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestVisibilityHybridListClosedWorkflowExecutions(t *testing.T) { request := &ListWorkflowExecutionsRequest{ Domain: "test-domain", } // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { context context.Context request *ListWorkflowExecutionsRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { context: context.Background(), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dbVisStoreName), wgCount: 0, expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { context: context.Background(), request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case1-3: success case with ES visibility is not nil": { context: context.Background(), request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(esStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with DB visibility is not nil and read Pinot is true": { context: context.Background(), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-2: success case with DB visibility is not nil and read modes are false": { context: context.Background(), request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain("db"), wgCount: 0, expectedError: nil, }, "Case3-1: read from ES with context key": { context: context.Background(), request: request, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(esStoreName), wgCount: 0, }, "Case3-2: read from Pinot with context key": { context: context.Background(), request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, }, "Case4-1: success case with double read": { context: context.Background(), request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, "Case4-2: double read with an error": { context: context.Background(), request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()). DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.ListClosedWorkflowExecutions(test.context, test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } wg.Wait() }) } } func TestVisibilityHybridListOpenWorkflowExecutionsByType(t *testing.T) { request := &ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: ListWorkflowExecutionsRequest{ Domain: "test-domain", }, } // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *ListWorkflowExecutionsByTypeRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(esStoreName), expectedError: nil, wgCount: 0, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByTypeRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, "Case2-2: double read with an error": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()). DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByTypeRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.ListOpenWorkflowExecutionsByType(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } wg.Wait() }) } } func TestVisibilityHybridListClosedWorkflowExecutionsByType(t *testing.T) { request := &ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: ListWorkflowExecutionsRequest{ Domain: "test-domain", }, } // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *ListWorkflowExecutionsByTypeRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dbVisStoreName), wgCount: 0, expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByTypeRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, "Case2-2: double read with an error": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()). Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByTypeRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.ListClosedWorkflowExecutionsByType(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } wg.Wait() }) } } func TestVisibilityHybridListOpenWorkflowExecutionsByWorkflowID(t *testing.T) { request := &ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: ListWorkflowExecutionsRequest{ Domain: "test-domain", }, } // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *ListWorkflowExecutionsByWorkflowIDRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dbVisStoreName), wgCount: 0, expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, "Case2-2: double read with an error": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()). DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()). Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.ListOpenWorkflowExecutionsByWorkflowID(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } wg.Wait() }) } } func TestVisibilityHybridListClosedWorkflowExecutionsByWorkflowID(t *testing.T) { request := &ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: ListWorkflowExecutionsRequest{ Domain: "test-domain", }, } // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *ListWorkflowExecutionsByWorkflowIDRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dbVisStoreName), wgCount: 0, expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, "Case2-2: double read with an error": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()). DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.ListClosedWorkflowExecutionsByWorkflowID(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } wg.Wait() }) } } func TestVisibilityHybridListClosedWorkflowExecutionsByStatus(t *testing.T) { defer goleak.VerifyNone(t) request := &ListClosedWorkflowExecutionsByStatusRequest{ ListWorkflowExecutionsRequest: ListWorkflowExecutionsRequest{ Domain: "test-domain", }, } // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *ListClosedWorkflowExecutionsByStatusRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dbVisStoreName), wgCount: 0, expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListClosedWorkflowExecutionsByStatusRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, "Case2-2: double read with an error": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()). DoAndReturn(func( ctx context.Context, request *ListClosedWorkflowExecutionsByStatusRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.ListClosedWorkflowExecutionsByStatus(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } wg.Wait() }) } } func TestVisibilityHybridGetClosedWorkflowExecution(t *testing.T) { request := &GetClosedWorkflowExecutionRequest{ Domain: "test-domain", } // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *GetClosedWorkflowExecutionRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dbVisStoreName), wgCount: 0, expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *GetClosedWorkflowExecutionRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, "Case2-2: double read with an error": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func( ctx context.Context, request *GetClosedWorkflowExecutionRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.GetClosedWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } wg.Wait() }) } } func TestVisibilityHybridListWorkflowExecutions(t *testing.T) { request := &ListWorkflowExecutionsByQueryRequest{ Domain: "test-domain", } // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *ListWorkflowExecutionsByQueryRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dbVisStoreName), wgCount: 0, expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, "Case2-2: double read with an error": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()). DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.ListWorkflowExecutions(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } wg.Wait() }) } } func TestVisibilityHybridScanWorkflowExecutions(t *testing.T) { request := &ListWorkflowExecutionsByQueryRequest{ Domain: "test-domain", } // put this outside because need to use it as an input of the table tests ctrl := gomock.NewController(t) tests := map[string]struct { request *ListWorkflowExecutionsByQueryRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dbVisStoreName), wgCount: 0, expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, "Case2-2: double read with an error": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()). DoAndReturn(func( ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()). Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.ScanWorkflowExecutions(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } wg.Wait() }) } } func TestVisibilityHybridCountWorkflowExecutions(t *testing.T) { request := &CountWorkflowExecutionsRequest{ Domain: "test-domain", } ctrl := gomock.NewController(t) tests := map[string]struct { request *CountWorkflowExecutionsRequest mockDBVisibilityManager VisibilityManager mockESVisibilityManager VisibilityManager mockPinotVisibilityManager VisibilityManager mockDBVisibilityManagerAffordance func(mockDBVisibilityManager *MockVisibilityManager) mockPinotVisibilityManagerAffordance func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) mockESVisibilityManagerAffordance func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) readVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter wgCount int expectedError error }{ "Case1-1: success case with DB visibility is not nil": { request: request, mockDBVisibilityManager: NewMockVisibilityManager(ctrl), mockDBVisibilityManagerAffordance: func(mockDBVisibilityManager *MockVisibilityManager) { mockDBVisibilityManager.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dbVisStoreName), wgCount: 0, expectedError: nil, }, "Case1-2: success case with Pinot visibility is not nil": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(pinotStoreName), wgCount: 0, expectedError: nil, }, "Case2-1: success case with double read": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).DoAndReturn(func( ctx context.Context, request *CountWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, nil }).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStorePinotPrimary), wgCount: 1, expectedError: nil, }, "Case2-2: double read with an error": { request: request, mockPinotVisibilityManager: NewMockVisibilityManager(ctrl), mockPinotVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockPinotVisibilityManager *MockVisibilityManager) { mockPinotVisibilityManager.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()). DoAndReturn(func( ctx context.Context, request *CountWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) { wg.Done() return nil, fmt.Errorf("test error") }).Times(1) }, mockESVisibilityManager: NewMockVisibilityManager(ctrl), mockESVisibilityManagerAffordance: func(wg *sync.WaitGroup, mockESVisibilityManager *MockVisibilityManager) { mockESVisibilityManager.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, readVisibilityStoreName: dynamicproperties.GetStringPropertyFnFilteredByDomain(dualStoreName), wgCount: 1, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { wg := sync.WaitGroup{} wg.Add(test.wgCount) if test.mockDBVisibilityManager != nil { test.mockDBVisibilityManagerAffordance(test.mockDBVisibilityManager.(*MockVisibilityManager)) } if test.mockPinotVisibilityManager != nil { test.mockPinotVisibilityManagerAffordance(&wg, test.mockPinotVisibilityManager.(*MockVisibilityManager)) } if test.mockESVisibilityManager != nil { test.mockESVisibilityManagerAffordance(&wg, test.mockESVisibilityManager.(*MockVisibilityManager)) } visibilityMgrs := map[string]VisibilityManager{ dbVisStoreName: test.mockDBVisibilityManager, esStoreName: test.mockESVisibilityManager, pinotStoreName: test.mockPinotVisibilityManager, } visibilityManager := NewVisibilityHybridManager(visibilityMgrs, test.readVisibilityStoreName, nil, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), testStoreName, log.NewNoop()) _, err := visibilityManager.CountWorkflowExecutions(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } wg.Wait() }) } } ================================================ FILE: common/persistence/visibility_manager_interfaces.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination visibility_manager_interfaces_mock.go -self_package github.com/uber/cadence/common/persistence github.com/uber/cadence/common/persistence VisibilityManager // Generate rate limiter wrapper. //go:generate gowrap gen -g -p . -i VisibilityManager -t ./wrappers/templates/ratelimited.tmpl -o wrappers/ratelimited/visibility_generated.go // Generate error injection wrapper. //go:generate gowrap gen -g -p . -i VisibilityManager -t ./wrappers/templates/errorinjector.tmpl -o wrappers/errorinjectors/visibility_generated.go // Generate metered wrapper. //go:generate gowrap gen -g -p . -i VisibilityManager -t ./wrappers/templates/metered.tmpl -o wrappers/metered/visibility_generated.go package persistence import ( "context" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/types" ) // Interfaces for the Visibility Store. // This is a secondary store that is eventually consistent with the main // executions store, and stores workflow execution records for visibility // purposes. // ErrVisibilityOperationNotSupported is an error which indicates that operation is not supported in selected persistence var ErrVisibilityOperationNotSupported = &types.BadRequestError{Message: "Operation is not supported"} type ( // RecordWorkflowExecutionStartedRequest is used to add a record of a newly // started execution RecordWorkflowExecutionStartedRequest struct { DomainUUID string Domain string // not persisted, used as config filter key Execution types.WorkflowExecution WorkflowTypeName string StartTimestamp int64 ExecutionTimestamp int64 WorkflowTimeout int64 // not persisted, used for cassandra ttl TaskID int64 // not persisted, used as condition update version for ES Memo *types.Memo TaskList string IsCron bool NumClusters int16 ClusterAttributeScope string ClusterAttributeName string UpdateTimestamp int64 // unit is unix nano, consistent with start/execution timestamp, same in other requests SearchAttributes map[string][]byte ShardID int16 ExecutionStatus types.WorkflowExecutionStatus CronSchedule string ScheduledExecutionTimestamp int64 // unit is unix nano, used to record the actual execution timestamp if it's a cron workflow } // RecordWorkflowExecutionClosedRequest is used to add a record of a newly // closed execution RecordWorkflowExecutionClosedRequest struct { DomainUUID string Domain string // not persisted, used as config filter key Execution types.WorkflowExecution WorkflowTypeName string StartTimestamp int64 ExecutionTimestamp int64 CloseTimestamp int64 Status types.WorkflowExecutionCloseStatus HistoryLength int64 RetentionSeconds int64 TaskID int64 // not persisted, used as condition update version for ES Memo *types.Memo TaskList string IsCron bool CronSchedule string NumClusters int16 ClusterAttributeScope string ClusterAttributeName string UpdateTimestamp int64 SearchAttributes map[string][]byte ShardID int16 ExecutionStatus types.WorkflowExecutionStatus ScheduledExecutionTimestamp int64 } // RecordWorkflowExecutionUninitializedRequest is used to add a record of a newly uninitialized execution RecordWorkflowExecutionUninitializedRequest struct { DomainUUID string Domain string Execution types.WorkflowExecution WorkflowTypeName string UpdateTimestamp int64 ShardID int64 } // UpsertWorkflowExecutionRequest is used to upsert workflow execution UpsertWorkflowExecutionRequest struct { DomainUUID string Domain string // not persisted, used as config filter key Execution types.WorkflowExecution WorkflowTypeName string StartTimestamp int64 ExecutionTimestamp int64 WorkflowTimeout int64 // not persisted, used for cassandra ttl TaskID int64 // not persisted, used as condition update version for ES Memo *types.Memo TaskList string IsCron bool NumClusters int16 ClusterAttributeScope string ClusterAttributeName string UpdateTimestamp int64 SearchAttributes map[string][]byte ShardID int64 ExecutionStatus types.WorkflowExecutionStatus CronSchedule string ScheduledExecutionTimestamp int64 // unit is unix nano, used to record the actual execution timestamp if it's a cron workflow } // ListWorkflowExecutionsRequest is used to list executions in a domain ListWorkflowExecutionsRequest struct { DomainUUID string Domain string // domain name is not persisted, but used as config filter key // The earliest end of the time range EarliestTime int64 // The latest end of the time range LatestTime int64 // Maximum number of workflow executions per page PageSize int // Token to continue reading next page of workflow executions. // Pass in empty slice for first page. NextPageToken []byte } // ListWorkflowExecutionsByQueryRequest is used to list executions in a domain ListWorkflowExecutionsByQueryRequest struct { DomainUUID string Domain string // domain name is not persisted, but used as config filter key PageSize int // Maximum number of workflow executions per page // Token to continue reading next page of workflow executions. // Pass in empty slice for first page. NextPageToken []byte Query string } // ListWorkflowExecutionsResponse is the response to ListWorkflowExecutionsRequest ListWorkflowExecutionsResponse struct { Executions []*types.WorkflowExecutionInfo // Token to read next page if there are more workflow executions beyond page size. // Use this to set NextPageToken on ListWorkflowExecutionsRequest to read the next page. NextPageToken []byte } // CountWorkflowExecutionsRequest is request from CountWorkflowExecutions CountWorkflowExecutionsRequest struct { DomainUUID string Domain string // domain name is not persisted, but used as config filter key Query string } // CountWorkflowExecutionsResponse is response to CountWorkflowExecutions CountWorkflowExecutionsResponse struct { Count int64 } // ListWorkflowExecutionsByTypeRequest is used to list executions of // a specific type in a domain ListWorkflowExecutionsByTypeRequest struct { ListWorkflowExecutionsRequest WorkflowTypeName string } // ListWorkflowExecutionsByWorkflowIDRequest is used to list executions that // have specific WorkflowID in a domain ListWorkflowExecutionsByWorkflowIDRequest struct { ListWorkflowExecutionsRequest WorkflowID string } // ListClosedWorkflowExecutionsByStatusRequest is used to list executions that // have specific close status ListClosedWorkflowExecutionsByStatusRequest struct { ListWorkflowExecutionsRequest Status types.WorkflowExecutionCloseStatus } // GetClosedWorkflowExecutionRequest is used retrieve the record for a specific execution GetClosedWorkflowExecutionRequest struct { DomainUUID string Domain string // domain name is not persisted, but used as config filter key Execution types.WorkflowExecution } // GetClosedWorkflowExecutionResponse is the response to GetClosedWorkflowExecutionRequest GetClosedWorkflowExecutionResponse struct { Execution *types.WorkflowExecutionInfo } // VisibilityDeleteWorkflowExecutionRequest contains the request params for DeleteWorkflowExecution call VisibilityDeleteWorkflowExecutionRequest struct { DomainID string Domain string RunID string WorkflowID string TaskID int64 } VisibilityAdminDeletionKey string // VisibilityManager is used to manage the visibility store VisibilityManager interface { Closeable GetName() string RecordWorkflowExecutionStarted(ctx context.Context, request *RecordWorkflowExecutionStartedRequest) error RecordWorkflowExecutionClosed(ctx context.Context, request *RecordWorkflowExecutionClosedRequest) error RecordWorkflowExecutionUninitialized(ctx context.Context, request *RecordWorkflowExecutionUninitializedRequest) error UpsertWorkflowExecution(ctx context.Context, request *UpsertWorkflowExecutionRequest) error ListOpenWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) ListClosedWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) ListOpenWorkflowExecutionsByType(ctx context.Context, request *ListWorkflowExecutionsByTypeRequest) (*ListWorkflowExecutionsResponse, error) ListClosedWorkflowExecutionsByType(ctx context.Context, request *ListWorkflowExecutionsByTypeRequest) (*ListWorkflowExecutionsResponse, error) ListOpenWorkflowExecutionsByWorkflowID(ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest) (*ListWorkflowExecutionsResponse, error) ListClosedWorkflowExecutionsByWorkflowID(ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest) (*ListWorkflowExecutionsResponse, error) ListClosedWorkflowExecutionsByStatus(ctx context.Context, request *ListClosedWorkflowExecutionsByStatusRequest) (*ListWorkflowExecutionsResponse, error) DeleteWorkflowExecution(ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest) error ListWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*ListWorkflowExecutionsResponse, error) ScanWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*ListWorkflowExecutionsResponse, error) CountWorkflowExecutions(ctx context.Context, request *CountWorkflowExecutionsRequest) (*CountWorkflowExecutionsResponse, error) // NOTE: GetClosedWorkflowExecution is only for persistence testing, currently no index is supported for filtering by RunID GetClosedWorkflowExecution(ctx context.Context, request *GetClosedWorkflowExecutionRequest) (*GetClosedWorkflowExecutionResponse, error) DeleteUninitializedWorkflowExecution(ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest) error } ) // IsNopUpsertWorkflowRequest return whether upsert request should be no-op func IsNopUpsertWorkflowRequest(request *InternalUpsertWorkflowExecutionRequest) bool { _, exist := request.SearchAttributes[definition.CadenceChangeVersion] return exist } ================================================ FILE: common/persistence/visibility_manager_interfaces_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/persistence (interfaces: VisibilityManager) // // Generated by this command: // // mockgen -package persistence -destination visibility_manager_interfaces_mock.go -self_package github.com/uber/cadence/common/persistence github.com/uber/cadence/common/persistence VisibilityManager // // Package persistence is a generated GoMock package. package persistence import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockVisibilityManager is a mock of VisibilityManager interface. type MockVisibilityManager struct { ctrl *gomock.Controller recorder *MockVisibilityManagerMockRecorder isgomock struct{} } // MockVisibilityManagerMockRecorder is the mock recorder for MockVisibilityManager. type MockVisibilityManagerMockRecorder struct { mock *MockVisibilityManager } // NewMockVisibilityManager creates a new mock instance. func NewMockVisibilityManager(ctrl *gomock.Controller) *MockVisibilityManager { mock := &MockVisibilityManager{ctrl: ctrl} mock.recorder = &MockVisibilityManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockVisibilityManager) EXPECT() *MockVisibilityManagerMockRecorder { return m.recorder } // Close mocks base method. func (m *MockVisibilityManager) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockVisibilityManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockVisibilityManager)(nil).Close)) } // CountWorkflowExecutions mocks base method. func (m *MockVisibilityManager) CountWorkflowExecutions(ctx context.Context, request *CountWorkflowExecutionsRequest) (*CountWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CountWorkflowExecutions", ctx, request) ret0, _ := ret[0].(*CountWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CountWorkflowExecutions indicates an expected call of CountWorkflowExecutions. func (mr *MockVisibilityManagerMockRecorder) CountWorkflowExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountWorkflowExecutions", reflect.TypeOf((*MockVisibilityManager)(nil).CountWorkflowExecutions), ctx, request) } // DeleteUninitializedWorkflowExecution mocks base method. func (m *MockVisibilityManager) DeleteUninitializedWorkflowExecution(ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteUninitializedWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteUninitializedWorkflowExecution indicates an expected call of DeleteUninitializedWorkflowExecution. func (mr *MockVisibilityManagerMockRecorder) DeleteUninitializedWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUninitializedWorkflowExecution", reflect.TypeOf((*MockVisibilityManager)(nil).DeleteUninitializedWorkflowExecution), ctx, request) } // DeleteWorkflowExecution mocks base method. func (m *MockVisibilityManager) DeleteWorkflowExecution(ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteWorkflowExecution indicates an expected call of DeleteWorkflowExecution. func (mr *MockVisibilityManagerMockRecorder) DeleteWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWorkflowExecution", reflect.TypeOf((*MockVisibilityManager)(nil).DeleteWorkflowExecution), ctx, request) } // GetClosedWorkflowExecution mocks base method. func (m *MockVisibilityManager) GetClosedWorkflowExecution(ctx context.Context, request *GetClosedWorkflowExecutionRequest) (*GetClosedWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetClosedWorkflowExecution", ctx, request) ret0, _ := ret[0].(*GetClosedWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetClosedWorkflowExecution indicates an expected call of GetClosedWorkflowExecution. func (mr *MockVisibilityManagerMockRecorder) GetClosedWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClosedWorkflowExecution", reflect.TypeOf((*MockVisibilityManager)(nil).GetClosedWorkflowExecution), ctx, request) } // GetName mocks base method. func (m *MockVisibilityManager) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockVisibilityManagerMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockVisibilityManager)(nil).GetName)) } // ListClosedWorkflowExecutions mocks base method. func (m *MockVisibilityManager) ListClosedWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListClosedWorkflowExecutions", ctx, request) ret0, _ := ret[0].(*ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListClosedWorkflowExecutions indicates an expected call of ListClosedWorkflowExecutions. func (mr *MockVisibilityManagerMockRecorder) ListClosedWorkflowExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClosedWorkflowExecutions", reflect.TypeOf((*MockVisibilityManager)(nil).ListClosedWorkflowExecutions), ctx, request) } // ListClosedWorkflowExecutionsByStatus mocks base method. func (m *MockVisibilityManager) ListClosedWorkflowExecutionsByStatus(ctx context.Context, request *ListClosedWorkflowExecutionsByStatusRequest) (*ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListClosedWorkflowExecutionsByStatus", ctx, request) ret0, _ := ret[0].(*ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListClosedWorkflowExecutionsByStatus indicates an expected call of ListClosedWorkflowExecutionsByStatus. func (mr *MockVisibilityManagerMockRecorder) ListClosedWorkflowExecutionsByStatus(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClosedWorkflowExecutionsByStatus", reflect.TypeOf((*MockVisibilityManager)(nil).ListClosedWorkflowExecutionsByStatus), ctx, request) } // ListClosedWorkflowExecutionsByType mocks base method. func (m *MockVisibilityManager) ListClosedWorkflowExecutionsByType(ctx context.Context, request *ListWorkflowExecutionsByTypeRequest) (*ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListClosedWorkflowExecutionsByType", ctx, request) ret0, _ := ret[0].(*ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListClosedWorkflowExecutionsByType indicates an expected call of ListClosedWorkflowExecutionsByType. func (mr *MockVisibilityManagerMockRecorder) ListClosedWorkflowExecutionsByType(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClosedWorkflowExecutionsByType", reflect.TypeOf((*MockVisibilityManager)(nil).ListClosedWorkflowExecutionsByType), ctx, request) } // ListClosedWorkflowExecutionsByWorkflowID mocks base method. func (m *MockVisibilityManager) ListClosedWorkflowExecutionsByWorkflowID(ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest) (*ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListClosedWorkflowExecutionsByWorkflowID", ctx, request) ret0, _ := ret[0].(*ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListClosedWorkflowExecutionsByWorkflowID indicates an expected call of ListClosedWorkflowExecutionsByWorkflowID. func (mr *MockVisibilityManagerMockRecorder) ListClosedWorkflowExecutionsByWorkflowID(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClosedWorkflowExecutionsByWorkflowID", reflect.TypeOf((*MockVisibilityManager)(nil).ListClosedWorkflowExecutionsByWorkflowID), ctx, request) } // ListOpenWorkflowExecutions mocks base method. func (m *MockVisibilityManager) ListOpenWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsRequest) (*ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListOpenWorkflowExecutions", ctx, request) ret0, _ := ret[0].(*ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListOpenWorkflowExecutions indicates an expected call of ListOpenWorkflowExecutions. func (mr *MockVisibilityManagerMockRecorder) ListOpenWorkflowExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOpenWorkflowExecutions", reflect.TypeOf((*MockVisibilityManager)(nil).ListOpenWorkflowExecutions), ctx, request) } // ListOpenWorkflowExecutionsByType mocks base method. func (m *MockVisibilityManager) ListOpenWorkflowExecutionsByType(ctx context.Context, request *ListWorkflowExecutionsByTypeRequest) (*ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListOpenWorkflowExecutionsByType", ctx, request) ret0, _ := ret[0].(*ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListOpenWorkflowExecutionsByType indicates an expected call of ListOpenWorkflowExecutionsByType. func (mr *MockVisibilityManagerMockRecorder) ListOpenWorkflowExecutionsByType(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOpenWorkflowExecutionsByType", reflect.TypeOf((*MockVisibilityManager)(nil).ListOpenWorkflowExecutionsByType), ctx, request) } // ListOpenWorkflowExecutionsByWorkflowID mocks base method. func (m *MockVisibilityManager) ListOpenWorkflowExecutionsByWorkflowID(ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest) (*ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListOpenWorkflowExecutionsByWorkflowID", ctx, request) ret0, _ := ret[0].(*ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListOpenWorkflowExecutionsByWorkflowID indicates an expected call of ListOpenWorkflowExecutionsByWorkflowID. func (mr *MockVisibilityManagerMockRecorder) ListOpenWorkflowExecutionsByWorkflowID(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOpenWorkflowExecutionsByWorkflowID", reflect.TypeOf((*MockVisibilityManager)(nil).ListOpenWorkflowExecutionsByWorkflowID), ctx, request) } // ListWorkflowExecutions mocks base method. func (m *MockVisibilityManager) ListWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListWorkflowExecutions", ctx, request) ret0, _ := ret[0].(*ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListWorkflowExecutions indicates an expected call of ListWorkflowExecutions. func (mr *MockVisibilityManagerMockRecorder) ListWorkflowExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListWorkflowExecutions", reflect.TypeOf((*MockVisibilityManager)(nil).ListWorkflowExecutions), ctx, request) } // RecordWorkflowExecutionClosed mocks base method. func (m *MockVisibilityManager) RecordWorkflowExecutionClosed(ctx context.Context, request *RecordWorkflowExecutionClosedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordWorkflowExecutionClosed", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RecordWorkflowExecutionClosed indicates an expected call of RecordWorkflowExecutionClosed. func (mr *MockVisibilityManagerMockRecorder) RecordWorkflowExecutionClosed(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWorkflowExecutionClosed", reflect.TypeOf((*MockVisibilityManager)(nil).RecordWorkflowExecutionClosed), ctx, request) } // RecordWorkflowExecutionStarted mocks base method. func (m *MockVisibilityManager) RecordWorkflowExecutionStarted(ctx context.Context, request *RecordWorkflowExecutionStartedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordWorkflowExecutionStarted", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RecordWorkflowExecutionStarted indicates an expected call of RecordWorkflowExecutionStarted. func (mr *MockVisibilityManagerMockRecorder) RecordWorkflowExecutionStarted(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWorkflowExecutionStarted", reflect.TypeOf((*MockVisibilityManager)(nil).RecordWorkflowExecutionStarted), ctx, request) } // RecordWorkflowExecutionUninitialized mocks base method. func (m *MockVisibilityManager) RecordWorkflowExecutionUninitialized(ctx context.Context, request *RecordWorkflowExecutionUninitializedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordWorkflowExecutionUninitialized", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RecordWorkflowExecutionUninitialized indicates an expected call of RecordWorkflowExecutionUninitialized. func (mr *MockVisibilityManagerMockRecorder) RecordWorkflowExecutionUninitialized(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWorkflowExecutionUninitialized", reflect.TypeOf((*MockVisibilityManager)(nil).RecordWorkflowExecutionUninitialized), ctx, request) } // ScanWorkflowExecutions mocks base method. func (m *MockVisibilityManager) ScanWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ScanWorkflowExecutions", ctx, request) ret0, _ := ret[0].(*ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ScanWorkflowExecutions indicates an expected call of ScanWorkflowExecutions. func (mr *MockVisibilityManagerMockRecorder) ScanWorkflowExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScanWorkflowExecutions", reflect.TypeOf((*MockVisibilityManager)(nil).ScanWorkflowExecutions), ctx, request) } // UpsertWorkflowExecution mocks base method. func (m *MockVisibilityManager) UpsertWorkflowExecution(ctx context.Context, request *UpsertWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpsertWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // UpsertWorkflowExecution indicates an expected call of UpsertWorkflowExecution. func (mr *MockVisibilityManagerMockRecorder) UpsertWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertWorkflowExecution", reflect.TypeOf((*MockVisibilityManager)(nil).UpsertWorkflowExecution), ctx, request) } ================================================ FILE: common/persistence/visibility_single_manager.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "context" "encoding/json" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) type ( visibilityManagerImpl struct { serializer PayloadSerializer persistence VisibilityStore logger log.Logger dc *DynamicConfiguration } ) var _ VisibilityManager = (*visibilityManagerImpl)(nil) // NewVisibilityManagerImpl returns new VisibilityManager via a VisibilityStore func NewVisibilityManagerImpl(persistence VisibilityStore, logger log.Logger, dc *DynamicConfiguration) VisibilityManager { return &visibilityManagerImpl{ serializer: NewPayloadSerializer(), persistence: persistence, logger: logger, dc: dc, } } func (v *visibilityManagerImpl) Close() { v.persistence.Close() } func (v *visibilityManagerImpl) GetName() string { return v.persistence.GetName() } func (v *visibilityManagerImpl) RecordWorkflowExecutionStarted( ctx context.Context, request *RecordWorkflowExecutionStartedRequest, ) error { req := &InternalRecordWorkflowExecutionStartedRequest{ DomainUUID: request.DomainUUID, WorkflowID: request.Execution.GetWorkflowID(), RunID: request.Execution.GetRunID(), WorkflowTypeName: request.WorkflowTypeName, StartTimestamp: time.Unix(0, request.StartTimestamp), ExecutionTimestamp: time.Unix(0, request.ExecutionTimestamp), WorkflowTimeout: common.SecondsToDuration(request.WorkflowTimeout), TaskID: request.TaskID, TaskList: request.TaskList, IsCron: request.IsCron, CronSchedule: request.CronSchedule, NumClusters: request.NumClusters, ClusterAttributeScope: request.ClusterAttributeScope, ClusterAttributeName: request.ClusterAttributeName, Memo: v.serializeMemo(request.Memo, request.DomainUUID, request.Execution.GetWorkflowID(), request.Execution.GetRunID()), UpdateTimestamp: time.Unix(0, request.UpdateTimestamp), SearchAttributes: request.SearchAttributes, ShardID: request.ShardID, ExecutionStatus: request.ExecutionStatus, ScheduledExecutionTime: time.Unix(0, request.ScheduledExecutionTimestamp), } return v.persistence.RecordWorkflowExecutionStarted(ctx, req) } func (v *visibilityManagerImpl) RecordWorkflowExecutionClosed( ctx context.Context, request *RecordWorkflowExecutionClosedRequest, ) error { req := &InternalRecordWorkflowExecutionClosedRequest{ DomainUUID: request.DomainUUID, WorkflowID: request.Execution.GetWorkflowID(), RunID: request.Execution.GetRunID(), WorkflowTypeName: request.WorkflowTypeName, StartTimestamp: time.Unix(0, request.StartTimestamp), ExecutionTimestamp: time.Unix(0, request.ExecutionTimestamp), TaskID: request.TaskID, Memo: v.serializeMemo(request.Memo, request.DomainUUID, request.Execution.GetWorkflowID(), request.Execution.GetRunID()), TaskList: request.TaskList, ClusterAttributeScope: request.ClusterAttributeScope, ClusterAttributeName: request.ClusterAttributeName, SearchAttributes: request.SearchAttributes, CloseTimestamp: time.Unix(0, request.CloseTimestamp), Status: request.Status, HistoryLength: request.HistoryLength, RetentionPeriod: common.SecondsToDuration(request.RetentionSeconds), IsCron: request.IsCron, CronSchedule: request.CronSchedule, NumClusters: request.NumClusters, UpdateTimestamp: time.Unix(0, request.UpdateTimestamp), ShardID: request.ShardID, ExecutionStatus: request.ExecutionStatus, ScheduledExecutionTime: time.Unix(0, request.ScheduledExecutionTimestamp), } return v.persistence.RecordWorkflowExecutionClosed(ctx, req) } func (v *visibilityManagerImpl) RecordWorkflowExecutionUninitialized( ctx context.Context, request *RecordWorkflowExecutionUninitializedRequest, ) error { req := &InternalRecordWorkflowExecutionUninitializedRequest{ DomainUUID: request.DomainUUID, WorkflowID: request.Execution.GetWorkflowID(), RunID: request.Execution.GetRunID(), WorkflowTypeName: request.WorkflowTypeName, UpdateTimestamp: time.Unix(0, request.UpdateTimestamp), ShardID: request.ShardID, } return v.persistence.RecordWorkflowExecutionUninitialized(ctx, req) } func (v *visibilityManagerImpl) UpsertWorkflowExecution( ctx context.Context, request *UpsertWorkflowExecutionRequest, ) error { req := &InternalUpsertWorkflowExecutionRequest{ DomainUUID: request.DomainUUID, WorkflowID: request.Execution.GetWorkflowID(), RunID: request.Execution.GetRunID(), WorkflowTypeName: request.WorkflowTypeName, StartTimestamp: time.Unix(0, request.StartTimestamp), ExecutionTimestamp: time.Unix(0, request.ExecutionTimestamp), TaskID: request.TaskID, Memo: v.serializeMemo(request.Memo, request.DomainUUID, request.Execution.GetWorkflowID(), request.Execution.GetRunID()), TaskList: request.TaskList, IsCron: request.IsCron, CronSchedule: request.CronSchedule, NumClusters: request.NumClusters, ClusterAttributeScope: request.ClusterAttributeScope, ClusterAttributeName: request.ClusterAttributeName, UpdateTimestamp: time.Unix(0, request.UpdateTimestamp), SearchAttributes: request.SearchAttributes, ShardID: request.ShardID, ExecutionStatus: request.ExecutionStatus, ScheduledExecutionTimestamp: request.ScheduledExecutionTimestamp, } return v.persistence.UpsertWorkflowExecution(ctx, req) } func (v *visibilityManagerImpl) ListOpenWorkflowExecutions( ctx context.Context, request *ListWorkflowExecutionsRequest, ) (*ListWorkflowExecutionsResponse, error) { internalResp, err := v.persistence.ListOpenWorkflowExecutions(ctx, v.toInternalListWorkflowExecutionsRequest(request)) if err != nil { return nil, err } return v.convertInternalListResponse(internalResp), nil } func (v *visibilityManagerImpl) ListClosedWorkflowExecutions( ctx context.Context, request *ListWorkflowExecutionsRequest, ) (*ListWorkflowExecutionsResponse, error) { internalResp, err := v.persistence.ListClosedWorkflowExecutions(ctx, v.toInternalListWorkflowExecutionsRequest(request)) if err != nil { return nil, err } return v.convertInternalListResponse(internalResp), nil } func (v *visibilityManagerImpl) ListOpenWorkflowExecutionsByType( ctx context.Context, request *ListWorkflowExecutionsByTypeRequest, ) (*ListWorkflowExecutionsResponse, error) { internalListRequest := v.toInternalListWorkflowExecutionsRequest(&request.ListWorkflowExecutionsRequest) internalRequest := &InternalListWorkflowExecutionsByTypeRequest{ WorkflowTypeName: request.WorkflowTypeName, } if internalListRequest != nil { internalRequest.InternalListWorkflowExecutionsRequest = *internalListRequest } internalResp, err := v.persistence.ListOpenWorkflowExecutionsByType(ctx, internalRequest) if err != nil { return nil, err } return v.convertInternalListResponse(internalResp), nil } func (v *visibilityManagerImpl) ListClosedWorkflowExecutionsByType( ctx context.Context, request *ListWorkflowExecutionsByTypeRequest, ) (*ListWorkflowExecutionsResponse, error) { internalListRequest := v.toInternalListWorkflowExecutionsRequest(&request.ListWorkflowExecutionsRequest) internalRequest := &InternalListWorkflowExecutionsByTypeRequest{ WorkflowTypeName: request.WorkflowTypeName, } if internalListRequest != nil { internalRequest.InternalListWorkflowExecutionsRequest = *internalListRequest } internalResp, err := v.persistence.ListClosedWorkflowExecutionsByType(ctx, internalRequest) if err != nil { return nil, err } return v.convertInternalListResponse(internalResp), nil } func (v *visibilityManagerImpl) ListOpenWorkflowExecutionsByWorkflowID( ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest, ) (*ListWorkflowExecutionsResponse, error) { internalListRequest := v.toInternalListWorkflowExecutionsRequest(&request.ListWorkflowExecutionsRequest) internalRequest := &InternalListWorkflowExecutionsByWorkflowIDRequest{ WorkflowID: request.WorkflowID, } if internalListRequest != nil { internalRequest.InternalListWorkflowExecutionsRequest = *internalListRequest } internalResp, err := v.persistence.ListOpenWorkflowExecutionsByWorkflowID(ctx, internalRequest) if err != nil { return nil, err } return v.convertInternalListResponse(internalResp), nil } func (v *visibilityManagerImpl) ListClosedWorkflowExecutionsByWorkflowID( ctx context.Context, request *ListWorkflowExecutionsByWorkflowIDRequest, ) (*ListWorkflowExecutionsResponse, error) { internalListRequest := v.toInternalListWorkflowExecutionsRequest(&request.ListWorkflowExecutionsRequest) internalRequest := &InternalListWorkflowExecutionsByWorkflowIDRequest{ WorkflowID: request.WorkflowID, } if internalListRequest != nil { internalRequest.InternalListWorkflowExecutionsRequest = *internalListRequest } internalResp, err := v.persistence.ListClosedWorkflowExecutionsByWorkflowID(ctx, internalRequest) if err != nil { return nil, err } return v.convertInternalListResponse(internalResp), nil } func (v *visibilityManagerImpl) ListClosedWorkflowExecutionsByStatus( ctx context.Context, request *ListClosedWorkflowExecutionsByStatusRequest, ) (*ListWorkflowExecutionsResponse, error) { internalListRequest := v.toInternalListWorkflowExecutionsRequest(&request.ListWorkflowExecutionsRequest) internalRequest := &InternalListClosedWorkflowExecutionsByStatusRequest{ Status: request.Status, } if internalListRequest != nil { internalRequest.InternalListWorkflowExecutionsRequest = *internalListRequest } internalResp, err := v.persistence.ListClosedWorkflowExecutionsByStatus(ctx, internalRequest) if err != nil { return nil, err } return v.convertInternalListResponse(internalResp), nil } func (v *visibilityManagerImpl) GetClosedWorkflowExecution( ctx context.Context, request *GetClosedWorkflowExecutionRequest, ) (*GetClosedWorkflowExecutionResponse, error) { internalReq := &InternalGetClosedWorkflowExecutionRequest{ DomainUUID: request.DomainUUID, Domain: request.Domain, Execution: request.Execution, } internalResp, err := v.persistence.GetClosedWorkflowExecution(ctx, internalReq) if err != nil { return nil, err } return v.convertInternalGetResponse(internalResp), nil } func (v *visibilityManagerImpl) DeleteWorkflowExecution( ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest, ) error { return v.persistence.DeleteWorkflowExecution(ctx, request) } func (v *visibilityManagerImpl) DeleteUninitializedWorkflowExecution( ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest, ) error { return v.persistence.DeleteUninitializedWorkflowExecution(ctx, request) } func (v *visibilityManagerImpl) ListWorkflowExecutions( ctx context.Context, request *ListWorkflowExecutionsByQueryRequest, ) (*ListWorkflowExecutionsResponse, error) { internalResp, err := v.persistence.ListWorkflowExecutions(ctx, request) if err != nil { return nil, err } return v.convertInternalListResponse(internalResp), nil } func (v *visibilityManagerImpl) ScanWorkflowExecutions( ctx context.Context, request *ListWorkflowExecutionsByQueryRequest, ) (*ListWorkflowExecutionsResponse, error) { internalResp, err := v.persistence.ScanWorkflowExecutions(ctx, request) if err != nil { return nil, err } return v.convertInternalListResponse(internalResp), nil } func (v *visibilityManagerImpl) CountWorkflowExecutions( ctx context.Context, request *CountWorkflowExecutionsRequest, ) (*CountWorkflowExecutionsResponse, error) { return v.persistence.CountWorkflowExecutions(ctx, request) } func (v *visibilityManagerImpl) convertInternalGetResponse(internalResp *InternalGetClosedWorkflowExecutionResponse) *GetClosedWorkflowExecutionResponse { if internalResp == nil { return nil } resp := &GetClosedWorkflowExecutionResponse{} resp.Execution = v.convertVisibilityWorkflowExecutionInfo(internalResp.Execution) return resp } func (v *visibilityManagerImpl) convertInternalListResponse(internalResp *InternalListWorkflowExecutionsResponse) *ListWorkflowExecutionsResponse { if internalResp == nil { return nil } resp := &ListWorkflowExecutionsResponse{} resp.Executions = make([]*types.WorkflowExecutionInfo, len(internalResp.Executions)) for i, execution := range internalResp.Executions { resp.Executions[i] = v.convertVisibilityWorkflowExecutionInfo(execution) } resp.NextPageToken = internalResp.NextPageToken return resp } func (v *visibilityManagerImpl) getSearchAttributes(attr map[string]interface{}) (*types.SearchAttributes, error) { indexedFields := make(map[string][]byte) var err error var valBytes []byte for k, val := range attr { valBytes, err = json.Marshal(val) if err != nil { v.logger.Error("error when encode search attributes", tag.Value(val)) continue } indexedFields[k] = valBytes } if err != nil { return nil, err } return &types.SearchAttributes{ IndexedFields: indexedFields, }, nil } func (v *visibilityManagerImpl) convertVisibilityWorkflowExecutionInfo(execution *InternalVisibilityWorkflowExecutionInfo) *types.WorkflowExecutionInfo { // special handling of ExecutionTime for cron or retry if execution.ExecutionTime.UnixNano() == 0 { execution.ExecutionTime = execution.StartTime } memo, err := v.serializer.DeserializeVisibilityMemo(execution.Memo) if err != nil { v.logger.Error("failed to deserialize memo", tag.WorkflowID(execution.WorkflowID), tag.WorkflowRunID(execution.RunID), tag.Error(err)) } searchAttributes, err := v.getSearchAttributes(execution.SearchAttributes) if err != nil { v.logger.Error("failed to convert search attributes", tag.WorkflowID(execution.WorkflowID), tag.WorkflowRunID(execution.RunID), tag.Error(err)) } convertedExecution := &types.WorkflowExecutionInfo{ Execution: &types.WorkflowExecution{ WorkflowID: execution.WorkflowID, RunID: execution.RunID, }, Type: &types.WorkflowType{ Name: execution.TypeName, }, StartTime: common.Int64Ptr(execution.StartTime.UnixNano()), ExecutionTime: common.Int64Ptr(execution.ExecutionTime.UnixNano()), Memo: memo, SearchAttributes: searchAttributes, TaskList: &types.TaskList{Name: execution.TaskList}, IsCron: execution.IsCron, CronSchedule: common.StringPtr(execution.CronSchedule), ExecutionStatus: &execution.ExecutionStatus, ScheduledExecutionTime: common.Int64Ptr(execution.ScheduledExecutionTime.UnixNano()), } // for close records if execution.Status != nil { convertedExecution.CloseTime = common.Int64Ptr(execution.CloseTime.UnixNano()) convertedExecution.CloseStatus = execution.Status convertedExecution.HistoryLength = execution.HistoryLength } return convertedExecution } func (v *visibilityManagerImpl) toInternalListWorkflowExecutionsRequest(req *ListWorkflowExecutionsRequest) *InternalListWorkflowExecutionsRequest { if req == nil { return nil } return &InternalListWorkflowExecutionsRequest{ DomainUUID: req.DomainUUID, Domain: req.Domain, EarliestTime: time.Unix(0, req.EarliestTime), LatestTime: time.Unix(0, req.LatestTime), PageSize: req.PageSize, NextPageToken: req.NextPageToken, } } func (v *visibilityManagerImpl) serializeMemo(visibilityMemo *types.Memo, domainID, wID, rID string) *DataBlob { memo, err := v.serializer.SerializeVisibilityMemo(visibilityMemo, constants.EncodingType(v.dc.SerializationEncoding())) if err != nil { v.logger.WithTags( tag.WorkflowDomainID(domainID), tag.WorkflowID(wID), tag.WorkflowRunID(rID), tag.Error(err)). Error("Unable to encode visibility memo") } if memo == nil { return &DataBlob{} } return memo } ================================================ FILE: common/persistence/visibility_single_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) func TestNewVisibilityManagerImpl(t *testing.T) { tests := map[string]struct { name string }{ "Case1: success case": { name: "TestNewVisibilityManagerImpl", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) assert.NotPanics(t, func() { NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) }) }) } } func TestClose(t *testing.T) { tests := map[string]struct { name string }{ "Case1: success case": { name: "TestNewVisibilityManagerImpl", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) mockVisibilityStore.EXPECT().Close().Return().Times(1) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) assert.NotPanics(t, func() { visibilityManager.Close() }) }) } } func TestGetName(t *testing.T) { tests := map[string]struct { name string }{ "Case1: success case": { name: "TestNewVisibilityManagerImpl", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) mockVisibilityStore.EXPECT().GetName().Return(testTableName).Times(1) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) assert.NotPanics(t, func() { visibilityManager.GetName() }) }) } } func TestRecordWorkflowExecutionStarted(t *testing.T) { errorRequest := &RecordWorkflowExecutionStartedRequest{ Execution: types.WorkflowExecution{ WorkflowID: "err-wid", RunID: "err-rid", }, Memo: &types.Memo{ Fields: map[string][]byte{ "Service": []byte("servername1"), }, }, SearchAttributes: map[string][]byte{ "CustomStringField": []byte("test string"), "CustomTimeField": []byte("2020-01-01T00:00:00Z"), }, } request := &RecordWorkflowExecutionStartedRequest{ Execution: types.WorkflowExecution{ WorkflowID: "wid", RunID: "rid", }, Memo: &types.Memo{}, SearchAttributes: map[string][]byte{ "CustomStringField": []byte("test string"), "CustomTimeField": []byte("2020-01-01T00:00:00Z"), }, } tests := map[string]struct { request *RecordWorkflowExecutionStartedRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) err := visibilityManager.RecordWorkflowExecutionStarted(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestRecordWorkflowExecutionClosed(t *testing.T) { errorRequest := &RecordWorkflowExecutionClosedRequest{ Execution: types.WorkflowExecution{ WorkflowID: "err-wid", RunID: "err-rid", }, Memo: &types.Memo{}, SearchAttributes: map[string][]byte{ "CustomStringField": []byte("test string"), "CustomTimeField": []byte("2020-01-01T00:00:00Z"), }, } request := &RecordWorkflowExecutionClosedRequest{ Execution: types.WorkflowExecution{ WorkflowID: "wid", RunID: "rid", }, Memo: &types.Memo{}, SearchAttributes: map[string][]byte{ "CustomStringField": []byte("test string"), "CustomTimeField": []byte("2020-01-01T00:00:00Z"), }, } tests := map[string]struct { request *RecordWorkflowExecutionClosedRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) err := visibilityManager.RecordWorkflowExecutionClosed(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestRecordWorkflowExecutionUninitialized(t *testing.T) { errorRequest := &RecordWorkflowExecutionUninitializedRequest{ Execution: types.WorkflowExecution{ WorkflowID: "err-wid", RunID: "err-rid", }, } request := &RecordWorkflowExecutionUninitializedRequest{ Execution: types.WorkflowExecution{ WorkflowID: "wid", RunID: "rid", }, } tests := map[string]struct { request *RecordWorkflowExecutionUninitializedRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().RecordWorkflowExecutionUninitialized(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().RecordWorkflowExecutionUninitialized(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) err := visibilityManager.RecordWorkflowExecutionUninitialized(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestUpsertWorkflowExecution(t *testing.T) { errorRequest := &UpsertWorkflowExecutionRequest{ Execution: types.WorkflowExecution{ WorkflowID: "err-wid", RunID: "err-rid", }, Memo: &types.Memo{}, SearchAttributes: map[string][]byte{ "CustomStringField": []byte("test string"), "CustomTimeField": []byte("2020-01-01T00:00:00Z"), }, } request := &UpsertWorkflowExecutionRequest{ Execution: types.WorkflowExecution{ WorkflowID: "wid", RunID: "rid", }, Memo: &types.Memo{}, SearchAttributes: map[string][]byte{ "CustomStringField": []byte("test string"), "CustomTimeField": []byte("2020-01-01T00:00:00Z"), }, } tests := map[string]struct { request *UpsertWorkflowExecutionRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().UpsertWorkflowExecution(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().UpsertWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) err := visibilityManager.UpsertWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestDeleteWorkflowExecution(t *testing.T) { errorRequest := &VisibilityDeleteWorkflowExecutionRequest{} request := &VisibilityDeleteWorkflowExecutionRequest{} tests := map[string]struct { request *VisibilityDeleteWorkflowExecutionRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) err := visibilityManager.DeleteWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestDeleteUninitializedWorkflowExecution(t *testing.T) { errorRequest := &VisibilityDeleteWorkflowExecutionRequest{} request := &VisibilityDeleteWorkflowExecutionRequest{} tests := map[string]struct { request *VisibilityDeleteWorkflowExecutionRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().DeleteUninitializedWorkflowExecution(gomock.Any(), gomock.Any()).Return(fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().DeleteUninitializedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) err := visibilityManager.DeleteUninitializedWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListOpenWorkflowExecutions(t *testing.T) { errorRequest := &ListWorkflowExecutionsRequest{} request := &ListWorkflowExecutionsRequest{} tests := map[string]struct { request *ListWorkflowExecutionsRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.ListOpenWorkflowExecutions(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListClosedWorkflowExecutions(t *testing.T) { errorRequest := &ListWorkflowExecutionsRequest{} request := &ListWorkflowExecutionsRequest{} tests := map[string]struct { request *ListWorkflowExecutionsRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.ListClosedWorkflowExecutions(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListOpenWorkflowExecutionsByType(t *testing.T) { errorRequest := &ListWorkflowExecutionsByTypeRequest{} request := &ListWorkflowExecutionsByTypeRequest{} tests := map[string]struct { request *ListWorkflowExecutionsByTypeRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.ListOpenWorkflowExecutionsByType(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestTestListClosedWorkflowExecutionsByType(t *testing.T) { errorRequest := &ListWorkflowExecutionsByTypeRequest{} request := &ListWorkflowExecutionsByTypeRequest{} tests := map[string]struct { request *ListWorkflowExecutionsByTypeRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.ListClosedWorkflowExecutionsByType(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListOpenWorkflowExecutionsByWorkflowID(t *testing.T) { errorRequest := &ListWorkflowExecutionsByWorkflowIDRequest{} request := &ListWorkflowExecutionsByWorkflowIDRequest{} tests := map[string]struct { request *ListWorkflowExecutionsByWorkflowIDRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.ListOpenWorkflowExecutionsByWorkflowID(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListClosedWorkflowExecutionsByWorkflowID(t *testing.T) { errorRequest := &ListWorkflowExecutionsByWorkflowIDRequest{} request := &ListWorkflowExecutionsByWorkflowIDRequest{} tests := map[string]struct { request *ListWorkflowExecutionsByWorkflowIDRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.ListClosedWorkflowExecutionsByWorkflowID(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListClosedWorkflowExecutionsByStatus(t *testing.T) { errorRequest := &ListClosedWorkflowExecutionsByStatusRequest{} request := &ListClosedWorkflowExecutionsByStatusRequest{} tests := map[string]struct { request *ListClosedWorkflowExecutionsByStatusRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.ListClosedWorkflowExecutionsByStatus(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestGetClosedWorkflowExecution(t *testing.T) { errorRequest := &GetClosedWorkflowExecutionRequest{} request := &GetClosedWorkflowExecutionRequest{} tests := map[string]struct { request *GetClosedWorkflowExecutionRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()).Return( &InternalGetClosedWorkflowExecutionResponse{ Execution: &InternalVisibilityWorkflowExecutionInfo{ Memo: &DataBlob{ Data: []byte("test string"), }, }, }, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.GetClosedWorkflowExecution(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListWorkflowExecutions(t *testing.T) { errorRequest := &ListWorkflowExecutionsByQueryRequest{} request := &ListWorkflowExecutionsByQueryRequest{} testStatus := types.WorkflowExecutionCloseStatus(2) tests := map[string]struct { request *ListWorkflowExecutionsByQueryRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()).Return( &InternalListWorkflowExecutionsResponse{ Executions: []*InternalVisibilityWorkflowExecutionInfo{ { Status: &testStatus, Memo: &DataBlob{ Data: []byte("test string"), }, SearchAttributes: map[string]interface{}{ "CustomStringField": []byte("test string"), "CustomTimeField": []byte("2020-01-01T00:00:00Z"), }, }, }}, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.ListWorkflowExecutions(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestScanWorkflowExecutions(t *testing.T) { errorRequest := &ListWorkflowExecutionsByQueryRequest{} request := &ListWorkflowExecutionsByQueryRequest{} tests := map[string]struct { request *ListWorkflowExecutionsByQueryRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.ScanWorkflowExecutions(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestCountWorkflowExecutions(t *testing.T) { errorRequest := &CountWorkflowExecutionsRequest{} request := &CountWorkflowExecutionsRequest{} tests := map[string]struct { request *CountWorkflowExecutionsRequest visibilityStoreAffordance func(mockVisibilityStore *MockVisibilityStore) expectedError error }{ "Case1: error case": { request: errorRequest, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { request: request, visibilityStoreAffordance: func(mockVisibilityStore *MockVisibilityStore) { mockVisibilityStore.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) test.visibilityStoreAffordance(mockVisibilityStore) _, err := visibilityManager.CountWorkflowExecutions(context.Background(), test.request) if test.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestGetSearchAttributes(t *testing.T) { tests := map[string]struct { input *map[string]interface{} expectedOutput *types.SearchAttributes expectedError error }{ "Case1: error case": { input: &map[string]interface{}{ "CustomStringField": make(chan int), }, expectedError: fmt.Errorf("error"), }, "Case2: normal case": { input: &map[string]interface{}{}, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityManagerImpl := visibilityManager.(*visibilityManagerImpl) actualOutput, actualErr := visibilityManagerImpl.getSearchAttributes(*test.input) if test.expectedError != nil { assert.Error(t, actualErr) } else { assert.NoError(t, actualErr) assert.NotNil(t, actualOutput) } }) } } func TestConvertVisibilityWorkflowExecutionInfo(t *testing.T) { testExecutionTime := int64(0) testStartTime := time.Now() testResultTime := testStartTime.UnixNano() tests := map[string]struct { input *InternalVisibilityWorkflowExecutionInfo expectedOutput *types.WorkflowExecutionInfo }{ "Case1: error case": { input: &InternalVisibilityWorkflowExecutionInfo{ SearchAttributes: map[string]interface{}{ "CustomStringField": make(chan int), }, ExecutionTime: time.UnixMilli(int64(testExecutionTime)), StartTime: testStartTime, }, expectedOutput: &types.WorkflowExecutionInfo{ ExecutionTime: &testResultTime, }, }, "Case2: normal case with Execution Time is 0": { input: &InternalVisibilityWorkflowExecutionInfo{ ExecutionTime: time.UnixMilli(int64(testExecutionTime)), StartTime: testStartTime, }, expectedOutput: &types.WorkflowExecutionInfo{ ExecutionTime: &testResultTime, }, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityManagerImpl := visibilityManager.(*visibilityManagerImpl) actualOutput := visibilityManagerImpl.convertVisibilityWorkflowExecutionInfo(test.input) assert.Equal(t, *test.expectedOutput.ExecutionTime, *actualOutput.ExecutionTime) }) } } func TestToInternalListWorkflowExecutionsRequest(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityManagerImpl := visibilityManager.(*visibilityManagerImpl) assert.Nil(t, visibilityManagerImpl.toInternalListWorkflowExecutionsRequest(nil)) } func TestSerializeMemo(t *testing.T) { ctrl := gomock.NewController(t) mockVisibilityStore := NewMockVisibilityStore(ctrl) mockPayloadSerializer := NewMockPayloadSerializer(ctrl) mockPayloadSerializer.EXPECT().SerializeVisibilityMemo(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) visibilityManager := NewVisibilityManagerImpl(mockVisibilityStore, log.NewNoop(), &DynamicConfiguration{ SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), }) visibilityManagerImpl := visibilityManager.(*visibilityManagerImpl) visibilityManagerImpl.serializer = mockPayloadSerializer assert.NotPanics(t, func() { visibilityManagerImpl.serializeMemo(nil, "testDomain", "testWorkflowID", "testRunID") }) } ================================================ FILE: common/persistence/visibility_store_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/persistence (interfaces: VisibilityStore) // // Generated by this command: // // mockgen -package persistence -destination visibility_store_mock.go -self_package github.com/uber/cadence/common/persistence github.com/uber/cadence/common/persistence VisibilityStore // // Package persistence is a generated GoMock package. package persistence import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockVisibilityStore is a mock of VisibilityStore interface. type MockVisibilityStore struct { ctrl *gomock.Controller recorder *MockVisibilityStoreMockRecorder isgomock struct{} } // MockVisibilityStoreMockRecorder is the mock recorder for MockVisibilityStore. type MockVisibilityStoreMockRecorder struct { mock *MockVisibilityStore } // NewMockVisibilityStore creates a new mock instance. func NewMockVisibilityStore(ctrl *gomock.Controller) *MockVisibilityStore { mock := &MockVisibilityStore{ctrl: ctrl} mock.recorder = &MockVisibilityStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockVisibilityStore) EXPECT() *MockVisibilityStoreMockRecorder { return m.recorder } // Close mocks base method. func (m *MockVisibilityStore) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockVisibilityStoreMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockVisibilityStore)(nil).Close)) } // CountWorkflowExecutions mocks base method. func (m *MockVisibilityStore) CountWorkflowExecutions(ctx context.Context, request *CountWorkflowExecutionsRequest) (*CountWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CountWorkflowExecutions", ctx, request) ret0, _ := ret[0].(*CountWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CountWorkflowExecutions indicates an expected call of CountWorkflowExecutions. func (mr *MockVisibilityStoreMockRecorder) CountWorkflowExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountWorkflowExecutions", reflect.TypeOf((*MockVisibilityStore)(nil).CountWorkflowExecutions), ctx, request) } // DeleteUninitializedWorkflowExecution mocks base method. func (m *MockVisibilityStore) DeleteUninitializedWorkflowExecution(ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteUninitializedWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteUninitializedWorkflowExecution indicates an expected call of DeleteUninitializedWorkflowExecution. func (mr *MockVisibilityStoreMockRecorder) DeleteUninitializedWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUninitializedWorkflowExecution", reflect.TypeOf((*MockVisibilityStore)(nil).DeleteUninitializedWorkflowExecution), ctx, request) } // DeleteWorkflowExecution mocks base method. func (m *MockVisibilityStore) DeleteWorkflowExecution(ctx context.Context, request *VisibilityDeleteWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // DeleteWorkflowExecution indicates an expected call of DeleteWorkflowExecution. func (mr *MockVisibilityStoreMockRecorder) DeleteWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWorkflowExecution", reflect.TypeOf((*MockVisibilityStore)(nil).DeleteWorkflowExecution), ctx, request) } // GetClosedWorkflowExecution mocks base method. func (m *MockVisibilityStore) GetClosedWorkflowExecution(ctx context.Context, request *InternalGetClosedWorkflowExecutionRequest) (*InternalGetClosedWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetClosedWorkflowExecution", ctx, request) ret0, _ := ret[0].(*InternalGetClosedWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetClosedWorkflowExecution indicates an expected call of GetClosedWorkflowExecution. func (mr *MockVisibilityStoreMockRecorder) GetClosedWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClosedWorkflowExecution", reflect.TypeOf((*MockVisibilityStore)(nil).GetClosedWorkflowExecution), ctx, request) } // GetName mocks base method. func (m *MockVisibilityStore) GetName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetName") ret0, _ := ret[0].(string) return ret0 } // GetName indicates an expected call of GetName. func (mr *MockVisibilityStoreMockRecorder) GetName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockVisibilityStore)(nil).GetName)) } // ListClosedWorkflowExecutions mocks base method. func (m *MockVisibilityStore) ListClosedWorkflowExecutions(ctx context.Context, request *InternalListWorkflowExecutionsRequest) (*InternalListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListClosedWorkflowExecutions", ctx, request) ret0, _ := ret[0].(*InternalListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListClosedWorkflowExecutions indicates an expected call of ListClosedWorkflowExecutions. func (mr *MockVisibilityStoreMockRecorder) ListClosedWorkflowExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClosedWorkflowExecutions", reflect.TypeOf((*MockVisibilityStore)(nil).ListClosedWorkflowExecutions), ctx, request) } // ListClosedWorkflowExecutionsByStatus mocks base method. func (m *MockVisibilityStore) ListClosedWorkflowExecutionsByStatus(ctx context.Context, request *InternalListClosedWorkflowExecutionsByStatusRequest) (*InternalListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListClosedWorkflowExecutionsByStatus", ctx, request) ret0, _ := ret[0].(*InternalListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListClosedWorkflowExecutionsByStatus indicates an expected call of ListClosedWorkflowExecutionsByStatus. func (mr *MockVisibilityStoreMockRecorder) ListClosedWorkflowExecutionsByStatus(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClosedWorkflowExecutionsByStatus", reflect.TypeOf((*MockVisibilityStore)(nil).ListClosedWorkflowExecutionsByStatus), ctx, request) } // ListClosedWorkflowExecutionsByType mocks base method. func (m *MockVisibilityStore) ListClosedWorkflowExecutionsByType(ctx context.Context, request *InternalListWorkflowExecutionsByTypeRequest) (*InternalListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListClosedWorkflowExecutionsByType", ctx, request) ret0, _ := ret[0].(*InternalListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListClosedWorkflowExecutionsByType indicates an expected call of ListClosedWorkflowExecutionsByType. func (mr *MockVisibilityStoreMockRecorder) ListClosedWorkflowExecutionsByType(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClosedWorkflowExecutionsByType", reflect.TypeOf((*MockVisibilityStore)(nil).ListClosedWorkflowExecutionsByType), ctx, request) } // ListClosedWorkflowExecutionsByWorkflowID mocks base method. func (m *MockVisibilityStore) ListClosedWorkflowExecutionsByWorkflowID(ctx context.Context, request *InternalListWorkflowExecutionsByWorkflowIDRequest) (*InternalListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListClosedWorkflowExecutionsByWorkflowID", ctx, request) ret0, _ := ret[0].(*InternalListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListClosedWorkflowExecutionsByWorkflowID indicates an expected call of ListClosedWorkflowExecutionsByWorkflowID. func (mr *MockVisibilityStoreMockRecorder) ListClosedWorkflowExecutionsByWorkflowID(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClosedWorkflowExecutionsByWorkflowID", reflect.TypeOf((*MockVisibilityStore)(nil).ListClosedWorkflowExecutionsByWorkflowID), ctx, request) } // ListOpenWorkflowExecutions mocks base method. func (m *MockVisibilityStore) ListOpenWorkflowExecutions(ctx context.Context, request *InternalListWorkflowExecutionsRequest) (*InternalListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListOpenWorkflowExecutions", ctx, request) ret0, _ := ret[0].(*InternalListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListOpenWorkflowExecutions indicates an expected call of ListOpenWorkflowExecutions. func (mr *MockVisibilityStoreMockRecorder) ListOpenWorkflowExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOpenWorkflowExecutions", reflect.TypeOf((*MockVisibilityStore)(nil).ListOpenWorkflowExecutions), ctx, request) } // ListOpenWorkflowExecutionsByType mocks base method. func (m *MockVisibilityStore) ListOpenWorkflowExecutionsByType(ctx context.Context, request *InternalListWorkflowExecutionsByTypeRequest) (*InternalListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListOpenWorkflowExecutionsByType", ctx, request) ret0, _ := ret[0].(*InternalListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListOpenWorkflowExecutionsByType indicates an expected call of ListOpenWorkflowExecutionsByType. func (mr *MockVisibilityStoreMockRecorder) ListOpenWorkflowExecutionsByType(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOpenWorkflowExecutionsByType", reflect.TypeOf((*MockVisibilityStore)(nil).ListOpenWorkflowExecutionsByType), ctx, request) } // ListOpenWorkflowExecutionsByWorkflowID mocks base method. func (m *MockVisibilityStore) ListOpenWorkflowExecutionsByWorkflowID(ctx context.Context, request *InternalListWorkflowExecutionsByWorkflowIDRequest) (*InternalListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListOpenWorkflowExecutionsByWorkflowID", ctx, request) ret0, _ := ret[0].(*InternalListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListOpenWorkflowExecutionsByWorkflowID indicates an expected call of ListOpenWorkflowExecutionsByWorkflowID. func (mr *MockVisibilityStoreMockRecorder) ListOpenWorkflowExecutionsByWorkflowID(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOpenWorkflowExecutionsByWorkflowID", reflect.TypeOf((*MockVisibilityStore)(nil).ListOpenWorkflowExecutionsByWorkflowID), ctx, request) } // ListWorkflowExecutions mocks base method. func (m *MockVisibilityStore) ListWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*InternalListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListWorkflowExecutions", ctx, request) ret0, _ := ret[0].(*InternalListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListWorkflowExecutions indicates an expected call of ListWorkflowExecutions. func (mr *MockVisibilityStoreMockRecorder) ListWorkflowExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListWorkflowExecutions", reflect.TypeOf((*MockVisibilityStore)(nil).ListWorkflowExecutions), ctx, request) } // RecordWorkflowExecutionClosed mocks base method. func (m *MockVisibilityStore) RecordWorkflowExecutionClosed(ctx context.Context, request *InternalRecordWorkflowExecutionClosedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordWorkflowExecutionClosed", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RecordWorkflowExecutionClosed indicates an expected call of RecordWorkflowExecutionClosed. func (mr *MockVisibilityStoreMockRecorder) RecordWorkflowExecutionClosed(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWorkflowExecutionClosed", reflect.TypeOf((*MockVisibilityStore)(nil).RecordWorkflowExecutionClosed), ctx, request) } // RecordWorkflowExecutionStarted mocks base method. func (m *MockVisibilityStore) RecordWorkflowExecutionStarted(ctx context.Context, request *InternalRecordWorkflowExecutionStartedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordWorkflowExecutionStarted", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RecordWorkflowExecutionStarted indicates an expected call of RecordWorkflowExecutionStarted. func (mr *MockVisibilityStoreMockRecorder) RecordWorkflowExecutionStarted(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWorkflowExecutionStarted", reflect.TypeOf((*MockVisibilityStore)(nil).RecordWorkflowExecutionStarted), ctx, request) } // RecordWorkflowExecutionUninitialized mocks base method. func (m *MockVisibilityStore) RecordWorkflowExecutionUninitialized(ctx context.Context, request *InternalRecordWorkflowExecutionUninitializedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordWorkflowExecutionUninitialized", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RecordWorkflowExecutionUninitialized indicates an expected call of RecordWorkflowExecutionUninitialized. func (mr *MockVisibilityStoreMockRecorder) RecordWorkflowExecutionUninitialized(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWorkflowExecutionUninitialized", reflect.TypeOf((*MockVisibilityStore)(nil).RecordWorkflowExecutionUninitialized), ctx, request) } // ScanWorkflowExecutions mocks base method. func (m *MockVisibilityStore) ScanWorkflowExecutions(ctx context.Context, request *ListWorkflowExecutionsByQueryRequest) (*InternalListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ScanWorkflowExecutions", ctx, request) ret0, _ := ret[0].(*InternalListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ScanWorkflowExecutions indicates an expected call of ScanWorkflowExecutions. func (mr *MockVisibilityStoreMockRecorder) ScanWorkflowExecutions(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScanWorkflowExecutions", reflect.TypeOf((*MockVisibilityStore)(nil).ScanWorkflowExecutions), ctx, request) } // UpsertWorkflowExecution mocks base method. func (m *MockVisibilityStore) UpsertWorkflowExecution(ctx context.Context, request *InternalUpsertWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpsertWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // UpsertWorkflowExecution indicates an expected call of UpsertWorkflowExecution. func (mr *MockVisibilityStoreMockRecorder) UpsertWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertWorkflowExecution", reflect.TypeOf((*MockVisibilityStore)(nil).UpsertWorkflowExecution), ctx, request) } ================================================ FILE: common/persistence/workflowStateCloseStatusValidator.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "fmt" "github.com/uber/cadence/common/types" ) var ( validWorkflowStates = map[int]struct{}{ WorkflowStateCreated: {}, WorkflowStateRunning: {}, WorkflowStateCompleted: {}, WorkflowStateZombie: {}, WorkflowStateCorrupted: {}, } validWorkflowCloseStatuses = map[int]struct{}{ WorkflowCloseStatusNone: {}, WorkflowCloseStatusCompleted: {}, WorkflowCloseStatusFailed: {}, WorkflowCloseStatusCanceled: {}, WorkflowCloseStatusTerminated: {}, WorkflowCloseStatusContinuedAsNew: {}, WorkflowCloseStatusTimedOut: {}, } ) // ValidateCreateWorkflowStateCloseStatus validate workflow state and close status func ValidateCreateWorkflowStateCloseStatus( state int, closeStatus int, ) error { if err := validateWorkflowState(state); err != nil { return err } if err := validateWorkflowCloseStatus(closeStatus); err != nil { return err } // validate workflow state & close status if state == WorkflowStateCompleted || closeStatus != WorkflowCloseStatusNone { return &types.InternalServiceError{ Message: fmt.Sprintf("Create workflow with invalid state: %v or close status: %v", state, closeStatus), } } return nil } // ValidateUpdateWorkflowStateCloseStatus validate workflow state and close status func ValidateUpdateWorkflowStateCloseStatus( state int, closeStatus int, ) error { if err := validateWorkflowState(state); err != nil { return err } if err := validateWorkflowCloseStatus(closeStatus); err != nil { return err } // validate workflow state & close status if closeStatus == WorkflowCloseStatusNone { if state == WorkflowStateCompleted { return &types.InternalServiceError{ Message: fmt.Sprintf("Update workflow with invalid state: %v or close status: %v", state, closeStatus), } } } else { // WorkflowCloseStatusCompleted // WorkflowCloseStatusFailed // WorkflowCloseStatusCanceled // WorkflowCloseStatusTerminated // WorkflowCloseStatusContinuedAsNew // WorkflowCloseStatusTimedOut if state != WorkflowStateCompleted { return &types.InternalServiceError{ Message: fmt.Sprintf("Update workflow with invalid state: %v or close status: %v", state, closeStatus), } } } return nil } // validateWorkflowState validate workflow state func validateWorkflowState( state int, ) error { if _, ok := validWorkflowStates[state]; !ok { return &types.InternalServiceError{ Message: fmt.Sprintf("Invalid workflow state: %v", state), } } return nil } // validateWorkflowCloseStatus validate workflow close status func validateWorkflowCloseStatus( closeStatus int, ) error { if _, ok := validWorkflowCloseStatuses[closeStatus]; !ok { return &types.InternalServiceError{ Message: fmt.Sprintf("Invalid workflow close status: %v", closeStatus), } } return nil } // ToInternalWorkflowExecutionCloseStatus convert persistence representation of close status to internal representation func ToInternalWorkflowExecutionCloseStatus( closeStatus int, ) *types.WorkflowExecutionCloseStatus { switch closeStatus { case WorkflowCloseStatusNone: return nil case WorkflowCloseStatusCompleted: return types.WorkflowExecutionCloseStatusCompleted.Ptr() case WorkflowCloseStatusFailed: return types.WorkflowExecutionCloseStatusFailed.Ptr() case WorkflowCloseStatusCanceled: return types.WorkflowExecutionCloseStatusCanceled.Ptr() case WorkflowCloseStatusTerminated: return types.WorkflowExecutionCloseStatusTerminated.Ptr() case WorkflowCloseStatusContinuedAsNew: return types.WorkflowExecutionCloseStatusContinuedAsNew.Ptr() case WorkflowCloseStatusTimedOut: return types.WorkflowExecutionCloseStatusTimedOut.Ptr() default: panic("Invalid value for enum WorkflowExecutionCloseStatus") } } // FromInternalWorkflowExecutionCloseStatus convert internal representation of close status to persistence representation func FromInternalWorkflowExecutionCloseStatus( closeStatus *types.WorkflowExecutionCloseStatus, ) int { if closeStatus == nil { return WorkflowCloseStatusNone } switch *closeStatus { case types.WorkflowExecutionCloseStatusCompleted: return WorkflowCloseStatusCompleted case types.WorkflowExecutionCloseStatusFailed: return WorkflowCloseStatusFailed case types.WorkflowExecutionCloseStatusCanceled: return WorkflowCloseStatusCanceled case types.WorkflowExecutionCloseStatusTerminated: return WorkflowCloseStatusTerminated case types.WorkflowExecutionCloseStatusContinuedAsNew: return WorkflowCloseStatusContinuedAsNew case types.WorkflowExecutionCloseStatusTimedOut: return WorkflowCloseStatusTimedOut default: panic("Invalid value for enum WorkflowExecutionCloseStatus") } } ================================================ FILE: common/persistence/workflowStateCloseStatusValidator_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "testing" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/types" ) type ( workflowStateCloseStatusSuite struct { suite.Suite } ) func TestWorkflowStateCloseStatusSuite(t *testing.T) { s := new(workflowStateCloseStatusSuite) suite.Run(t, s) } func (s *workflowStateCloseStatusSuite) SetupSuite() { } func (s *workflowStateCloseStatusSuite) TearDownSuite() { } func (s *workflowStateCloseStatusSuite) SetupTest() { } func (s *workflowStateCloseStatusSuite) TearDownTest() { } func (s *workflowStateCloseStatusSuite) TestCreateWorkflowStateCloseStatus_WorkflowStateCreated() { closeStatuses := []int{ WorkflowCloseStatusCompleted, WorkflowCloseStatusFailed, WorkflowCloseStatusCanceled, WorkflowCloseStatusTerminated, WorkflowCloseStatusContinuedAsNew, WorkflowCloseStatusTimedOut, } s.Nil(ValidateCreateWorkflowStateCloseStatus(WorkflowStateCreated, WorkflowCloseStatusNone)) for _, closeStatus := range closeStatuses { s.NotNil(ValidateCreateWorkflowStateCloseStatus(WorkflowStateCreated, closeStatus)) } } func (s *workflowStateCloseStatusSuite) TestCreateWorkflowStateCloseStatus_WorkflowStateRunning() { closeStatuses := []int{ WorkflowCloseStatusCompleted, WorkflowCloseStatusFailed, WorkflowCloseStatusCanceled, WorkflowCloseStatusTerminated, WorkflowCloseStatusContinuedAsNew, WorkflowCloseStatusTimedOut, } s.Nil(ValidateCreateWorkflowStateCloseStatus(WorkflowStateRunning, WorkflowCloseStatusNone)) for _, closeStatus := range closeStatuses { s.NotNil(ValidateCreateWorkflowStateCloseStatus(WorkflowStateRunning, closeStatus)) } } func (s *workflowStateCloseStatusSuite) TestCreateWorkflowStateCloseStatus_WorkflowStateCompleted() { closeStatuses := []int{ WorkflowCloseStatusNone, WorkflowCloseStatusCompleted, WorkflowCloseStatusFailed, WorkflowCloseStatusCanceled, WorkflowCloseStatusTerminated, WorkflowCloseStatusContinuedAsNew, WorkflowCloseStatusTimedOut, } for _, closeStatus := range closeStatuses { s.NotNil(ValidateCreateWorkflowStateCloseStatus(WorkflowStateCompleted, closeStatus)) } } func (s *workflowStateCloseStatusSuite) TestCreateWorkflowStateCloseStatus_WorkflowStateZombie() { closeStatuses := []int{ WorkflowCloseStatusCompleted, WorkflowCloseStatusFailed, WorkflowCloseStatusCanceled, WorkflowCloseStatusTerminated, WorkflowCloseStatusContinuedAsNew, WorkflowCloseStatusTimedOut, } s.Nil(ValidateCreateWorkflowStateCloseStatus(WorkflowStateZombie, WorkflowCloseStatusNone)) for _, closeStatus := range closeStatuses { s.NotNil(ValidateCreateWorkflowStateCloseStatus(WorkflowStateZombie, closeStatus)) } } // TODO func (s *workflowStateCloseStatusSuite) TestUpdateWorkflowStateCloseStatus_WorkflowStateCreated() { closeStatuses := []int{ WorkflowCloseStatusCompleted, WorkflowCloseStatusFailed, WorkflowCloseStatusCanceled, WorkflowCloseStatusTerminated, WorkflowCloseStatusContinuedAsNew, WorkflowCloseStatusTimedOut, } s.Nil(ValidateUpdateWorkflowStateCloseStatus(WorkflowStateCreated, WorkflowCloseStatusNone)) for _, closeStatus := range closeStatuses { s.NotNil(ValidateUpdateWorkflowStateCloseStatus(WorkflowStateCreated, closeStatus)) } } func (s *workflowStateCloseStatusSuite) TestUpdateWorkflowStateCloseStatus_WorkflowStateRunning() { closeStatuses := []int{ WorkflowCloseStatusCompleted, WorkflowCloseStatusFailed, WorkflowCloseStatusCanceled, WorkflowCloseStatusTerminated, WorkflowCloseStatusContinuedAsNew, WorkflowCloseStatusTimedOut, } s.Nil(ValidateUpdateWorkflowStateCloseStatus(WorkflowStateRunning, WorkflowCloseStatusNone)) for _, closeStatus := range closeStatuses { s.NotNil(ValidateUpdateWorkflowStateCloseStatus(WorkflowStateRunning, closeStatus)) } } func (s *workflowStateCloseStatusSuite) TestUpdateWorkflowStateCloseStatus_WorkflowStateCompleted() { closeStatuses := []int{ WorkflowCloseStatusCompleted, WorkflowCloseStatusFailed, WorkflowCloseStatusCanceled, WorkflowCloseStatusTerminated, WorkflowCloseStatusContinuedAsNew, WorkflowCloseStatusTimedOut, } s.NotNil(ValidateUpdateWorkflowStateCloseStatus(WorkflowStateCompleted, WorkflowCloseStatusNone)) for _, closeStatus := range closeStatuses { s.Nil(ValidateUpdateWorkflowStateCloseStatus(WorkflowStateCompleted, closeStatus)) } } func (s *workflowStateCloseStatusSuite) TestUpdateWorkflowStateCloseStatus_WorkflowStateZombie() { closeStatuses := []int{ WorkflowCloseStatusCompleted, WorkflowCloseStatusFailed, WorkflowCloseStatusCanceled, WorkflowCloseStatusTerminated, WorkflowCloseStatusContinuedAsNew, WorkflowCloseStatusTimedOut, } s.Nil(ValidateUpdateWorkflowStateCloseStatus(WorkflowStateZombie, WorkflowCloseStatusNone)) for _, closeStatus := range closeStatuses { s.NotNil(ValidateUpdateWorkflowStateCloseStatus(WorkflowStateZombie, closeStatus)) } } func (s *workflowStateCloseStatusSuite) TestInternalMapping() { s.Nil(ToInternalWorkflowExecutionCloseStatus(WorkflowCloseStatusNone)) s.Equal(types.WorkflowExecutionCloseStatusCompleted.Ptr(), ToInternalWorkflowExecutionCloseStatus(WorkflowCloseStatusCompleted)) s.Equal(types.WorkflowExecutionCloseStatusFailed.Ptr(), ToInternalWorkflowExecutionCloseStatus(WorkflowCloseStatusFailed)) s.Equal(types.WorkflowExecutionCloseStatusCanceled.Ptr(), ToInternalWorkflowExecutionCloseStatus(WorkflowCloseStatusCanceled)) s.Equal(types.WorkflowExecutionCloseStatusTerminated.Ptr(), ToInternalWorkflowExecutionCloseStatus(WorkflowCloseStatusTerminated)) s.Equal(types.WorkflowExecutionCloseStatusContinuedAsNew.Ptr(), ToInternalWorkflowExecutionCloseStatus(WorkflowCloseStatusContinuedAsNew)) s.Equal(types.WorkflowExecutionCloseStatusTimedOut.Ptr(), ToInternalWorkflowExecutionCloseStatus(WorkflowCloseStatusTimedOut)) s.Equal(WorkflowCloseStatusNone, FromInternalWorkflowExecutionCloseStatus(nil)) s.Equal(WorkflowCloseStatusCompleted, FromInternalWorkflowExecutionCloseStatus(types.WorkflowExecutionCloseStatusCompleted.Ptr())) s.Equal(WorkflowCloseStatusFailed, FromInternalWorkflowExecutionCloseStatus(types.WorkflowExecutionCloseStatusFailed.Ptr())) s.Equal(WorkflowCloseStatusCanceled, FromInternalWorkflowExecutionCloseStatus(types.WorkflowExecutionCloseStatusCanceled.Ptr())) s.Equal(WorkflowCloseStatusTerminated, FromInternalWorkflowExecutionCloseStatus(types.WorkflowExecutionCloseStatusTerminated.Ptr())) s.Equal(WorkflowCloseStatusContinuedAsNew, FromInternalWorkflowExecutionCloseStatus(types.WorkflowExecutionCloseStatusContinuedAsNew.Ptr())) s.Equal(WorkflowCloseStatusTimedOut, FromInternalWorkflowExecutionCloseStatus(types.WorkflowExecutionCloseStatusTimedOut.Ptr())) } ================================================ FILE: common/persistence/workflow_execution_info.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package persistence import ( "fmt" "github.com/uber/cadence/common/types" ) // SetNextEventID sets the nextEventID func (e *WorkflowExecutionInfo) SetNextEventID(id int64) { e.NextEventID = id } // IncreaseNextEventID increase the nextEventID by 1 func (e *WorkflowExecutionInfo) IncreaseNextEventID() { e.NextEventID++ } // SetLastFirstEventID set the LastFirstEventID func (e *WorkflowExecutionInfo) SetLastFirstEventID(id int64) { e.LastFirstEventID = id } // UpdateWorkflowStateCloseStatus update the workflow state func (e *WorkflowExecutionInfo) UpdateWorkflowStateCloseStatus( state int, closeStatus int, ) error { switch e.State { case WorkflowStateVoid: // no validation case WorkflowStateCreated: switch state { case WorkflowStateCreated: if closeStatus != WorkflowCloseStatusNone { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } case WorkflowStateRunning: if closeStatus != WorkflowCloseStatusNone { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } case WorkflowStateCompleted: if closeStatus != WorkflowCloseStatusTerminated && closeStatus != WorkflowCloseStatusTimedOut && closeStatus != WorkflowCloseStatusContinuedAsNew { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } case WorkflowStateZombie: if closeStatus != WorkflowCloseStatusNone { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown workflow state: %v", state), } } case WorkflowStateRunning: switch state { case WorkflowStateCreated: return e.createInvalidStateTransitionErr(e.State, state, closeStatus) case WorkflowStateRunning: if closeStatus != WorkflowCloseStatusNone { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } case WorkflowStateCompleted: if closeStatus == WorkflowCloseStatusNone { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } case WorkflowStateZombie: if closeStatus != WorkflowCloseStatusNone { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown workflow state: %v", state), } } case WorkflowStateCompleted: switch state { case WorkflowStateCreated: return e.createInvalidStateTransitionErr(e.State, state, closeStatus) case WorkflowStateRunning: return e.createInvalidStateTransitionErr(e.State, state, closeStatus) case WorkflowStateCompleted: if closeStatus != e.CloseStatus { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } case WorkflowStateZombie: return e.createInvalidStateTransitionErr(e.State, state, closeStatus) default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown workflow state: %v", state), } } case WorkflowStateZombie: switch state { case WorkflowStateCreated: if closeStatus != WorkflowCloseStatusNone { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } case WorkflowStateRunning: if closeStatus != WorkflowCloseStatusNone { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } case WorkflowStateCompleted: if closeStatus == WorkflowCloseStatusNone { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } case WorkflowStateZombie: if closeStatus == WorkflowCloseStatusNone { return e.createInvalidStateTransitionErr(e.State, state, closeStatus) } default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown workflow state: %v", state), } } default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown workflow state: %v", state), } } e.State = state e.CloseStatus = closeStatus return nil } func (e *WorkflowExecutionInfo) IsRunning() bool { switch e.State { case WorkflowStateCreated: return true case WorkflowStateRunning: return true case WorkflowStateCompleted: return false case WorkflowStateZombie: return false case WorkflowStateCorrupted: return false default: panic(fmt.Sprintf("unknown workflow state: %v", e.State)) } } // UpdateWorkflowStateCloseStatus update the workflow state func (e *WorkflowExecutionInfo) createInvalidStateTransitionErr( currentState int, targetState int, targetCloseStatus int, ) error { return &types.InternalServiceError{ Message: fmt.Sprintf(invalidStateTransitionMsg, currentState, targetState, targetCloseStatus), } } ================================================ FILE: common/persistence/workflow_execution_info_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package persistence import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) var ( workflowStates = map[string]int{ "Void": WorkflowStateVoid, "Created": WorkflowStateCreated, "Running": WorkflowStateRunning, "Completed": WorkflowStateCompleted, "Zombie": WorkflowStateZombie, } workflowCloseStatuses = map[string]int{ "None": WorkflowCloseStatusNone, "Completed": WorkflowCloseStatusCompleted, "Failed": WorkflowCloseStatusFailed, "Canceled": WorkflowCloseStatusCanceled, "Terminated": WorkflowCloseStatusTerminated, "ContinuedAsNew": WorkflowCloseStatusContinuedAsNew, "TimedOut": WorkflowCloseStatusTimedOut, } ) func TestWorkflowExecutionInfoSetNextEventID(t *testing.T) { info := &WorkflowExecutionInfo{NextEventID: 1} info.SetNextEventID(2) assert.Equal(t, int64(2), info.NextEventID) } func TestWorkflowExecutionInfoIncreaseNextEventID(t *testing.T) { info := &WorkflowExecutionInfo{NextEventID: 1} info.IncreaseNextEventID() assert.Equal(t, int64(2), info.NextEventID) } func TestWorkflowExecutionInfoSetLastFirstEventID(t *testing.T) { info := &WorkflowExecutionInfo{LastFirstEventID: 1} info.SetLastFirstEventID(2) assert.Equal(t, int64(2), info.LastFirstEventID) } func TestWorkflowExecutionInfoIsRunning(t *testing.T) { tests := map[string]struct { state int expected bool }{ "Created": { WorkflowStateCreated, true, }, "Running": { WorkflowStateRunning, true, }, "Completed": { WorkflowStateCompleted, false, }, "Zombie": { WorkflowStateZombie, false, }, "Corrupted": { WorkflowStateCorrupted, false, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { info := &WorkflowExecutionInfo{ State: test.state, } assert.Equal(t, test.expected, info.IsRunning()) }) } } type transitionChecker func(executionInfo *WorkflowExecutionInfo, toState, closeStatus int) bool type stateChecker func(executionInfo *WorkflowExecutionInfo, closeStatus int) bool type testCase struct { name string fromState int fromStatus int toState int toStatus int } func TestWorkflowExecutionInfoUpdateWorkflowStateCloseStatus(t *testing.T) { expectedTransitions := map[int]transitionChecker{ WorkflowStateVoid: toAnyState(), WorkflowStateCreated: toStates(map[int]stateChecker{ WorkflowStateCreated: withCloseStatusNone(), WorkflowStateRunning: withCloseStatusNone(), WorkflowStateCompleted: withCloseStatus(WorkflowCloseStatusTerminated, WorkflowCloseStatusTimedOut, WorkflowCloseStatusContinuedAsNew), WorkflowStateZombie: withCloseStatusNone(), }), WorkflowStateRunning: toStates(map[int]stateChecker{ WorkflowStateRunning: withCloseStatusNone(), WorkflowStateCompleted: withAnyCloseStatusExcept(WorkflowCloseStatusNone), WorkflowStateZombie: withCloseStatusNone(), }), WorkflowStateCompleted: toStates(map[int]stateChecker{ WorkflowStateCompleted: func(executionInfo *WorkflowExecutionInfo, closeStatus int) bool { return executionInfo.CloseStatus == closeStatus }, }), WorkflowStateZombie: toStates(map[int]stateChecker{ WorkflowStateCreated: withCloseStatusNone(), WorkflowStateRunning: withCloseStatusNone(), WorkflowStateCompleted: withAnyCloseStatusExcept(WorkflowCloseStatusNone), WorkflowStateZombie: withAnyCloseStatusExcept(WorkflowCloseStatusNone), }), } var testCases []*testCase // From every state with a CloseStatus of None to every state and close status for fromStateName, fromState := range workflowStates { for toStateName, toState := range workflowStates { for closeStatusName, closeStatus := range workflowCloseStatuses { testCases = append(testCases, &testCase{ name: fmt.Sprintf("(%s,None)->(%s,%s)", fromStateName, toStateName, closeStatusName), fromState: fromState, fromStatus: WorkflowCloseStatusNone, toState: toState, toStatus: closeStatus, }) } } } // Ensure Completed doesn't allow for changing the Status testCases = append(testCases, &testCase{ name: "(Completed,Failed)->(Completed, Completed)", fromState: WorkflowStateCompleted, fromStatus: WorkflowCloseStatusFailed, toState: WorkflowStateCompleted, toStatus: WorkflowCloseStatusCompleted, }) testCases = append(testCases, &testCase{ name: "(Completed,Completed)->(Completed,Completed)", fromState: WorkflowStateCompleted, fromStatus: WorkflowCloseStatusCompleted, toState: WorkflowStateCompleted, toStatus: WorkflowCloseStatusCompleted, }) // Invalid states testCases = append(testCases, &testCase{ name: "(Completed,Completed)->(?,Completed)", fromState: WorkflowStateCompleted, fromStatus: WorkflowCloseStatusCompleted, toState: 100000, toStatus: WorkflowCloseStatusCompleted, }) testCases = append(testCases, &testCase{ name: "(?,Completed)->(Completed,Completed)", fromState: 100000, fromStatus: WorkflowCloseStatusCompleted, toState: WorkflowStateCompleted, toStatus: WorkflowCloseStatusCompleted, }) for _, test := range testCases { t.Run(test.name, func(t *testing.T) { executionInfo := &WorkflowExecutionInfo{ State: test.fromState, CloseStatus: test.fromStatus, } var noErr bool if isValid, ok := expectedTransitions[test.fromState]; ok { noErr = isValid(executionInfo, test.toState, test.toStatus) } else { noErr = false } err := executionInfo.UpdateWorkflowStateCloseStatus(test.toState, test.toStatus) if noErr { assert.NoError(t, err) assert.Equal(t, test.toState, executionInfo.State) assert.Equal(t, test.toStatus, executionInfo.CloseStatus) } else { assert.Error(t, err) } }) } } func toAnyState() transitionChecker { return func(workflowExecutionInfo *WorkflowExecutionInfo, toState, closeStatus int) bool { return true } } func toStates(validStates map[int]stateChecker) transitionChecker { return func(workflowExecutionInfo *WorkflowExecutionInfo, toState, closeStatus int) bool { if checker, ok := validStates[toState]; ok { return checker(workflowExecutionInfo, closeStatus) } return false } } func withCloseStatusNone() stateChecker { return withCloseStatus(WorkflowCloseStatusNone) } func withCloseStatus(closeStatuses ...int) stateChecker { return func(workflowExecutionInfo *WorkflowExecutionInfo, closeStatus int) bool { return contains(closeStatuses, closeStatus) } } func withAnyCloseStatusExcept(closeStatuses ...int) stateChecker { return func(workflowExecutionInfo *WorkflowExecutionInfo, closeStatus int) bool { for _, v := range closeStatuses { if v == closeStatus { return false } } return true } } func contains(slice []int, value int) bool { for _, v := range slice { if v == value { return true } } return false } ================================================ FILE: common/persistence/wrappers/errorinjectors/configstore_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../templates/errorinjector.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" ) // injectorConfigStoreManager implements persistence.ConfigStoreManager interface instrumented with error injection. type injectorConfigStoreManager struct { wrapped persistence.ConfigStoreManager starttime time.Time errorRate float64 logger log.Logger } // NewConfigStoreManager creates a new instance of ConfigStoreManager with error injection. func NewConfigStoreManager( wrapped persistence.ConfigStoreManager, errorRate float64, logger log.Logger, starttime time.Time, ) persistence.ConfigStoreManager { return &injectorConfigStoreManager{ wrapped: wrapped, starttime: starttime, errorRate: errorRate, logger: logger, } } func (c *injectorConfigStoreManager) Close() { c.wrapped.Close() return } func (c *injectorConfigStoreManager) FetchDynamicConfig(ctx context.Context, cfgType persistence.ConfigType) (fp1 *persistence.FetchDynamicConfigResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { fp1, err = c.wrapped.FetchDynamicConfig(ctx, cfgType) } if fakeErr != nil { logErr(c.logger, "ConfigStoreManager.FetchDynamicConfig", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorConfigStoreManager) UpdateDynamicConfig(ctx context.Context, request *persistence.UpdateDynamicConfigRequest, cfgType persistence.ConfigType) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.UpdateDynamicConfig(ctx, request, cfgType) } if fakeErr != nil { logErr(c.logger, "ConfigStoreManager.UpdateDynamicConfig", fakeErr, forwardCall, err) err = fakeErr return } return } ================================================ FILE: common/persistence/wrappers/errorinjectors/domain_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../templates/errorinjector.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" ) // injectorDomainManager implements persistence.DomainManager interface instrumented with error injection. type injectorDomainManager struct { wrapped persistence.DomainManager starttime time.Time errorRate float64 logger log.Logger } // NewDomainManager creates a new instance of DomainManager with error injection. func NewDomainManager( wrapped persistence.DomainManager, errorRate float64, logger log.Logger, starttime time.Time, ) persistence.DomainManager { return &injectorDomainManager{ wrapped: wrapped, starttime: starttime, errorRate: errorRate, logger: logger, } } func (c *injectorDomainManager) Close() { c.wrapped.Close() return } func (c *injectorDomainManager) CreateDomain(ctx context.Context, request *persistence.CreateDomainRequest) (cp1 *persistence.CreateDomainResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { cp1, err = c.wrapped.CreateDomain(ctx, request) } if fakeErr != nil { logErr(c.logger, "DomainManager.CreateDomain", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorDomainManager) DeleteDomain(ctx context.Context, request *persistence.DeleteDomainRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteDomain(ctx, request) } if fakeErr != nil { logErr(c.logger, "DomainManager.DeleteDomain", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorDomainManager) DeleteDomainByName(ctx context.Context, request *persistence.DeleteDomainByNameRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteDomainByName(ctx, request) } if fakeErr != nil { logErr(c.logger, "DomainManager.DeleteDomainByName", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorDomainManager) GetDomain(ctx context.Context, request *persistence.GetDomainRequest) (gp1 *persistence.GetDomainResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetDomain(ctx, request) } if fakeErr != nil { logErr(c.logger, "DomainManager.GetDomain", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorDomainManager) GetMetadata(ctx context.Context) (gp1 *persistence.GetMetadataResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetMetadata(ctx) } if fakeErr != nil { logErr(c.logger, "DomainManager.GetMetadata", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorDomainManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *injectorDomainManager) ListDomains(ctx context.Context, request *persistence.ListDomainsRequest) (lp1 *persistence.ListDomainsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListDomains(ctx, request) } if fakeErr != nil { logErr(c.logger, "DomainManager.ListDomains", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorDomainManager) UpdateDomain(ctx context.Context, request *persistence.UpdateDomainRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.UpdateDomain(ctx, request) } if fakeErr != nil { logErr(c.logger, "DomainManager.UpdateDomain", fakeErr, forwardCall, err) err = fakeErr return } return } ================================================ FILE: common/persistence/wrappers/errorinjectors/execution_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../templates/errorinjector.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // injectorExecutionManager implements persistence.ExecutionManager interface instrumented with error injection. type injectorExecutionManager struct { wrapped persistence.ExecutionManager starttime time.Time errorRate float64 logger log.Logger } // NewExecutionManager creates a new instance of ExecutionManager with error injection. func NewExecutionManager( wrapped persistence.ExecutionManager, errorRate float64, logger log.Logger, starttime time.Time, ) persistence.ExecutionManager { return &injectorExecutionManager{ wrapped: wrapped, starttime: starttime, errorRate: errorRate, logger: logger, } } func (c *injectorExecutionManager) Close() { c.wrapped.Close() return } func (c *injectorExecutionManager) CompleteHistoryTask(ctx context.Context, request *persistence.CompleteHistoryTaskRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.CompleteHistoryTask(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.CompleteHistoryTask", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) ConflictResolveWorkflowExecution(ctx context.Context, request *persistence.ConflictResolveWorkflowExecutionRequest) (cp1 *persistence.ConflictResolveWorkflowExecutionResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { cp1, err = c.wrapped.ConflictResolveWorkflowExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.ConflictResolveWorkflowExecution", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) CreateFailoverMarkerTasks(ctx context.Context, request *persistence.CreateFailoverMarkersRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.CreateFailoverMarkerTasks(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.CreateFailoverMarkerTasks", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) CreateWorkflowExecution(ctx context.Context, request *persistence.CreateWorkflowExecutionRequest) (cp1 *persistence.CreateWorkflowExecutionResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { cp1, err = c.wrapped.CreateWorkflowExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.CreateWorkflowExecution", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) DeleteActiveClusterSelectionPolicy(ctx context.Context, domainID string, workflowID string, runID string) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.DeleteActiveClusterSelectionPolicy", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) DeleteCurrentWorkflowExecution(ctx context.Context, request *persistence.DeleteCurrentWorkflowExecutionRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteCurrentWorkflowExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.DeleteCurrentWorkflowExecution", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) DeleteReplicationTaskFromDLQ(ctx context.Context, request *persistence.DeleteReplicationTaskFromDLQRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteReplicationTaskFromDLQ(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.DeleteReplicationTaskFromDLQ", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) DeleteWorkflowExecution(ctx context.Context, request *persistence.DeleteWorkflowExecutionRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteWorkflowExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.DeleteWorkflowExecution", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) GetActiveClusterSelectionPolicy(ctx context.Context, domainID string, wfID string, rID string) (ap1 *types.ActiveClusterSelectionPolicy, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { ap1, err = c.wrapped.GetActiveClusterSelectionPolicy(ctx, domainID, wfID, rID) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.GetActiveClusterSelectionPolicy", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) GetCurrentExecution(ctx context.Context, request *persistence.GetCurrentExecutionRequest) (gp1 *persistence.GetCurrentExecutionResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetCurrentExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.GetCurrentExecution", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) GetHistoryTasks(ctx context.Context, request *persistence.GetHistoryTasksRequest) (gp1 *persistence.GetHistoryTasksResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetHistoryTasks(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.GetHistoryTasks", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *injectorExecutionManager) GetReplicationDLQSize(ctx context.Context, request *persistence.GetReplicationDLQSizeRequest) (gp1 *persistence.GetReplicationDLQSizeResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetReplicationDLQSize(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.GetReplicationDLQSize", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) GetReplicationTasksFromDLQ(ctx context.Context, request *persistence.GetReplicationTasksFromDLQRequest) (gp1 *persistence.GetHistoryTasksResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetReplicationTasksFromDLQ(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.GetReplicationTasksFromDLQ", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) GetShardID() (i1 int) { return c.wrapped.GetShardID() } func (c *injectorExecutionManager) GetWorkflowExecution(ctx context.Context, request *persistence.GetWorkflowExecutionRequest) (gp1 *persistence.GetWorkflowExecutionResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetWorkflowExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.GetWorkflowExecution", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) IsWorkflowExecutionExists(ctx context.Context, request *persistence.IsWorkflowExecutionExistsRequest) (ip1 *persistence.IsWorkflowExecutionExistsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { ip1, err = c.wrapped.IsWorkflowExecutionExists(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.IsWorkflowExecutionExists", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) ListConcreteExecutions(ctx context.Context, request *persistence.ListConcreteExecutionsRequest) (lp1 *persistence.ListConcreteExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListConcreteExecutions(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.ListConcreteExecutions", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) ListCurrentExecutions(ctx context.Context, request *persistence.ListCurrentExecutionsRequest) (lp1 *persistence.ListCurrentExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListCurrentExecutions(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.ListCurrentExecutions", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) PutReplicationTaskToDLQ(ctx context.Context, request *persistence.PutReplicationTaskToDLQRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.PutReplicationTaskToDLQ(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.PutReplicationTaskToDLQ", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) RangeCompleteHistoryTask(ctx context.Context, request *persistence.RangeCompleteHistoryTaskRequest) (rp1 *persistence.RangeCompleteHistoryTaskResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { rp1, err = c.wrapped.RangeCompleteHistoryTask(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.RangeCompleteHistoryTask", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) RangeDeleteReplicationTaskFromDLQ(ctx context.Context, request *persistence.RangeDeleteReplicationTaskFromDLQRequest) (rp1 *persistence.RangeDeleteReplicationTaskFromDLQResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { rp1, err = c.wrapped.RangeDeleteReplicationTaskFromDLQ(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.RangeDeleteReplicationTaskFromDLQ", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorExecutionManager) UpdateWorkflowExecution(ctx context.Context, request *persistence.UpdateWorkflowExecutionRequest) (up1 *persistence.UpdateWorkflowExecutionResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { up1, err = c.wrapped.UpdateWorkflowExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "ExecutionManager.UpdateWorkflowExecution", fakeErr, forwardCall, err) err = fakeErr return } return } ================================================ FILE: common/persistence/wrappers/errorinjectors/history_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../templates/errorinjector.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" ) // injectorHistoryManager implements persistence.HistoryManager interface instrumented with error injection. type injectorHistoryManager struct { wrapped persistence.HistoryManager starttime time.Time errorRate float64 logger log.Logger } // NewHistoryManager creates a new instance of HistoryManager with error injection. func NewHistoryManager( wrapped persistence.HistoryManager, errorRate float64, logger log.Logger, starttime time.Time, ) persistence.HistoryManager { return &injectorHistoryManager{ wrapped: wrapped, starttime: starttime, errorRate: errorRate, logger: logger, } } func (c *injectorHistoryManager) AppendHistoryNodes(ctx context.Context, request *persistence.AppendHistoryNodesRequest) (ap1 *persistence.AppendHistoryNodesResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { ap1, err = c.wrapped.AppendHistoryNodes(ctx, request) } if fakeErr != nil { logErr(c.logger, "HistoryManager.AppendHistoryNodes", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorHistoryManager) Close() { c.wrapped.Close() return } func (c *injectorHistoryManager) DeleteHistoryBranch(ctx context.Context, request *persistence.DeleteHistoryBranchRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteHistoryBranch(ctx, request) } if fakeErr != nil { logErr(c.logger, "HistoryManager.DeleteHistoryBranch", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorHistoryManager) ForkHistoryBranch(ctx context.Context, request *persistence.ForkHistoryBranchRequest) (fp1 *persistence.ForkHistoryBranchResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { fp1, err = c.wrapped.ForkHistoryBranch(ctx, request) } if fakeErr != nil { logErr(c.logger, "HistoryManager.ForkHistoryBranch", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorHistoryManager) GetAllHistoryTreeBranches(ctx context.Context, request *persistence.GetAllHistoryTreeBranchesRequest) (gp1 *persistence.GetAllHistoryTreeBranchesResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetAllHistoryTreeBranches(ctx, request) } if fakeErr != nil { logErr(c.logger, "HistoryManager.GetAllHistoryTreeBranches", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorHistoryManager) GetHistoryTree(ctx context.Context, request *persistence.GetHistoryTreeRequest) (gp1 *persistence.GetHistoryTreeResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetHistoryTree(ctx, request) } if fakeErr != nil { logErr(c.logger, "HistoryManager.GetHistoryTree", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorHistoryManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *injectorHistoryManager) ReadHistoryBranch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (rp1 *persistence.ReadHistoryBranchResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { rp1, err = c.wrapped.ReadHistoryBranch(ctx, request) } if fakeErr != nil { logErr(c.logger, "HistoryManager.ReadHistoryBranch", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorHistoryManager) ReadHistoryBranchByBatch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (rp1 *persistence.ReadHistoryBranchByBatchResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { rp1, err = c.wrapped.ReadHistoryBranchByBatch(ctx, request) } if fakeErr != nil { logErr(c.logger, "HistoryManager.ReadHistoryBranchByBatch", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorHistoryManager) ReadRawHistoryBranch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (rp1 *persistence.ReadRawHistoryBranchResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { rp1, err = c.wrapped.ReadRawHistoryBranch(ctx, request) } if fakeErr != nil { logErr(c.logger, "HistoryManager.ReadRawHistoryBranch", fakeErr, forwardCall, err) err = fakeErr return } return } ================================================ FILE: common/persistence/wrappers/errorinjectors/injectors_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package errorinjectors import ( "context" "fmt" "reflect" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) var _staticMethods = map[string]bool{ "Close": true, "GetName": true, "GetShardID": true, } var wrappers = []any{ &injectorConfigStoreManager{}, &injectorDomainManager{}, &injectorHistoryManager{}, &injectorQueueManager{}, &injectorShardManager{}, &injectorTaskManager{}, &injectorVisibilityManager{}, &injectorExecutionManager{}, } func TestInjectorsWithoutErrors(t *testing.T) { for _, injector := range wrappers { name := reflect.TypeOf(injector).String() t.Run(name, func(t *testing.T) { object := builderForPassThrough(t, injector, 0, testlogger.New(t), true, nil) v := reflect.ValueOf(object) infoT := reflect.TypeOf(v.Interface()) for i := 0; i < infoT.NumMethod(); i++ { method := infoT.Method(i) if _staticMethods[method.Name] { // Skip methods that do not use error injection. continue } t.Run(method.Name, func(t *testing.T) { vals := make([]reflect.Value, 0, method.Type.NumIn()-1) // First argument is always context.Context vals = append(vals, reflect.ValueOf(context.Background())) for i := 2; i < method.Type.NumIn(); i++ { vals = append(vals, reflect.Zero(method.Type.In(i))) } callRes := v.MethodByName(method.Name).Call(vals) resultErr := callRes[len(callRes)-1].Interface() assert.Nil(t, resultErr, "method %v returned error %v", method.Name, resultErr) }) } }) } } func TestInjectorsWith100ErrorRate(t *testing.T) { oldRandomStubFunc := _randomStubFunc _randomStubFunc = func() bool { return false } defer func() { _randomStubFunc = oldRandomStubFunc }() for _, injector := range wrappers { name := reflect.TypeOf(injector).String() t.Run(name, func(t *testing.T) { // We cannot use test logger here, since logger.Error will fail the test. object := builderForPassThrough(t, injector, 1, log.NewNoop(), false, nil) v := reflect.ValueOf(object) infoT := reflect.TypeOf(v.Interface()) for i := 0; i < infoT.NumMethod(); i++ { method := infoT.Method(i) if _staticMethods[method.Name] { // Skip methods that do not use error injection. continue } t.Run(method.Name, func(t *testing.T) { vals := make([]reflect.Value, 0, method.Type.NumIn()-1) // First argument is always context.Context vals = append(vals, reflect.ValueOf(context.Background())) for i := 2; i < method.Type.NumIn(); i++ { vals = append(vals, reflect.Zero(method.Type.In(i))) } var callRes []reflect.Value assert.NotPanicsf(t, func() { callRes = v.MethodByName(method.Name).Call(vals) }, "method does not have tag defined") if len(callRes) == 0 { // Empty result means that method panicked. return } resultErr := callRes[len(callRes)-1].Interface() err, ok := resultErr.(error) assert.True(t, ok, "method %v must return error") assert.True(t, isFakeError(err), "method %v returned not faked error, got %v", method.Name, err) }) } }) } } func TestInjectorsWithUnderlyingErrors(t *testing.T) { for _, injector := range wrappers { name := reflect.TypeOf(injector).String() t.Run(name, func(t *testing.T) { expectedMethodErr := fmt.Errorf("%s: injected error", name) object := builderForPassThrough(t, injector, 0, testlogger.New(t), true, expectedMethodErr) v := reflect.ValueOf(object) infoT := reflect.TypeOf(v.Interface()) for i := 0; i < infoT.NumMethod(); i++ { method := infoT.Method(i) if _staticMethods[method.Name] { // Skip methods that do not use error injection. continue } t.Run(method.Name, func(t *testing.T) { vals := make([]reflect.Value, 0, method.Type.NumIn()-1) // First argument is always context.Context vals = append(vals, reflect.ValueOf(context.Background())) for i := 2; i < method.Type.NumIn(); i++ { vals = append(vals, reflect.Zero(method.Type.In(i))) } callRes := v.MethodByName(method.Name).Call(vals) resultErr := callRes[len(callRes)-1].Interface() err, ok := resultErr.(error) require.True(t, ok, "method %v must return error") assert.Equal(t, expectedMethodErr, err, "method %v returned different error, expected %v, got %v", method.Name, expectedMethodErr, err) }) } }) } } func builderForPassThrough(t *testing.T, injector any, errorRate float64, logger log.Logger, expectCalls bool, expectedErr error) (object any) { ctrl := gomock.NewController(t) // For error injection tests (errorRate > 0), use a starttime far enough in the past // to bypass the 30-second warmup period in generateFakeError starttime := time.Now() if errorRate > 0 { starttime = time.Now().Add(-time.Minute) // 60 seconds ago, past the 30s warmup } switch injector.(type) { case *injectorConfigStoreManager: mocked := persistence.NewMockConfigStoreManager(ctrl) object = NewConfigStoreManager(mocked, errorRate, logger, starttime) if expectCalls { mocked.EXPECT().UpdateDynamicConfig(gomock.Any(), gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().FetchDynamicConfig(gomock.Any(), gomock.Any()).Return(&persistence.FetchDynamicConfigResponse{}, expectedErr) } case *injectorDomainManager: mocked := persistence.NewMockDomainManager(ctrl) object = NewDomainManager(mocked, errorRate, logger, starttime) if expectCalls { mocked.EXPECT().CreateDomain(gomock.Any(), gomock.Any()).Return(&persistence.CreateDomainResponse{}, expectedErr) mocked.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(&persistence.GetDomainResponse{}, expectedErr) mocked.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteDomain(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteDomainByName(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(&persistence.ListDomainsResponse{}, expectedErr) mocked.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{}, expectedErr) } case *injectorHistoryManager: mocked := persistence.NewMockHistoryManager(ctrl) object = NewHistoryManager(mocked, errorRate, logger, starttime) if expectCalls { mocked.EXPECT().AppendHistoryNodes(gomock.Any(), gomock.Any()).Return(&persistence.AppendHistoryNodesResponse{}, expectedErr) mocked.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchResponse{}, expectedErr) mocked.EXPECT().ReadHistoryBranchByBatch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchByBatchResponse{}, expectedErr) mocked.EXPECT().ReadRawHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadRawHistoryBranchResponse{}, expectedErr) mocked.EXPECT().ForkHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ForkHistoryBranchResponse{}, expectedErr) mocked.EXPECT().DeleteHistoryBranch(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetHistoryTree(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTreeResponse{}, expectedErr) mocked.EXPECT().GetAllHistoryTreeBranches(gomock.Any(), gomock.Any()).Return(&persistence.GetAllHistoryTreeBranchesResponse{}, expectedErr) } case *injectorQueueManager: mocked := persistence.NewMockQueueManager(ctrl) object = NewQueueManager(mocked, errorRate, logger, starttime) if expectCalls { mocked.EXPECT().EnqueueMessage(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().ReadMessages(gomock.Any(), gomock.Any()).Return(&persistence.ReadMessagesResponse{Messages: []*persistence.QueueMessage{}}, expectedErr) mocked.EXPECT().UpdateAckLevel(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetAckLevels(gomock.Any(), gomock.Any()).Return(&persistence.GetAckLevelsResponse{AckLevels: map[string]int64{}}, expectedErr) mocked.EXPECT().DeleteMessagesBefore(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteMessageFromDLQ(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().EnqueueMessageToDLQ(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetDLQAckLevels(gomock.Any(), gomock.Any()).Return(&persistence.GetDLQAckLevelsResponse{AckLevels: map[string]int64{}}, expectedErr) mocked.EXPECT().GetDLQSize(gomock.Any(), gomock.Any()).Return(&persistence.GetDLQSizeResponse{Size: 0}, expectedErr) mocked.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().ReadMessagesFromDLQ(gomock.Any(), gomock.Any()).Return(&persistence.ReadMessagesFromDLQResponse{Messages: []*persistence.QueueMessage{}}, expectedErr) mocked.EXPECT().UpdateDLQAckLevel(gomock.Any(), gomock.Any()).Return(expectedErr) } case *injectorShardManager: mocked := persistence.NewMockShardManager(ctrl) object = NewShardManager(mocked, errorRate, logger, starttime) if expectCalls { mocked.EXPECT().GetShard(gomock.Any(), gomock.Any()).Return(&persistence.GetShardResponse{}, expectedErr) mocked.EXPECT().UpdateShard(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().CreateShard(gomock.Any(), gomock.Any()).Return(expectedErr) } case *injectorTaskManager: mocked := persistence.NewMockTaskManager(ctrl) object = NewTaskManager(mocked, errorRate, logger, starttime) if expectCalls { mocked.EXPECT().CompleteTasksLessThan(gomock.Any(), gomock.Any()).Return(&persistence.CompleteTasksLessThanResponse{}, expectedErr) mocked.EXPECT().CompleteTask(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().CreateTasks(gomock.Any(), gomock.Any()).Return(&persistence.CreateTasksResponse{}, expectedErr) mocked.EXPECT().DeleteTaskList(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetTasksResponse{}, expectedErr) mocked.EXPECT().GetOrphanTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetOrphanTasksResponse{}, expectedErr) mocked.EXPECT().GetTaskListSize(gomock.Any(), gomock.Any()).Return(&persistence.GetTaskListSizeResponse{}, expectedErr) mocked.EXPECT().LeaseTaskList(gomock.Any(), gomock.Any()).Return(&persistence.LeaseTaskListResponse{}, expectedErr) mocked.EXPECT().GetTaskList(gomock.Any(), gomock.Any()).Return(&persistence.GetTaskListResponse{}, expectedErr) mocked.EXPECT().ListTaskList(gomock.Any(), gomock.Any()).Return(&persistence.ListTaskListResponse{}, expectedErr) mocked.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any()).Return(&persistence.UpdateTaskListResponse{}, expectedErr) } case *injectorVisibilityManager: mocked := persistence.NewMockVisibilityManager(ctrl) object = NewVisibilityManager(mocked, errorRate, logger, starttime) if expectCalls { mocked.EXPECT().DeleteUninitializedWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.CountWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.GetClosedWorkflowExecutionResponse{}, expectedErr) mocked.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().RecordWorkflowExecutionUninitialized(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().UpsertWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) } case *injectorExecutionManager: mocked := persistence.NewMockExecutionManager(ctrl) object = NewExecutionManager(mocked, errorRate, logger, starttime) if expectCalls { mocked.EXPECT().CreateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.CreateWorkflowExecutionResponse{}, expectedErr) mocked.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.GetWorkflowExecutionResponse{}, expectedErr) mocked.EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.UpdateWorkflowExecutionResponse{}, expectedErr) mocked.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteCurrentWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetCurrentExecution(gomock.Any(), gomock.Any()).Return(&persistence.GetCurrentExecutionResponse{}, expectedErr) mocked.EXPECT().ConflictResolveWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.ConflictResolveWorkflowExecutionResponse{}, expectedErr) mocked.EXPECT().CreateFailoverMarkerTasks(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteReplicationTaskFromDLQ(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetReplicationDLQSize(gomock.Any(), gomock.Any()).Return(&persistence.GetReplicationDLQSizeResponse{}, expectedErr) mocked.EXPECT().GetReplicationTasksFromDLQ(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTasksResponse{}, expectedErr) mocked.EXPECT().IsWorkflowExecutionExists(gomock.Any(), gomock.Any()).Return(&persistence.IsWorkflowExecutionExistsResponse{}, expectedErr) mocked.EXPECT().ListConcreteExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListConcreteExecutionsResponse{}, expectedErr) mocked.EXPECT().ListCurrentExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListCurrentExecutionsResponse{}, expectedErr) mocked.EXPECT().PutReplicationTaskToDLQ(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTasksResponse{}, expectedErr) mocked.EXPECT().CompleteHistoryTask(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().RangeCompleteHistoryTask(gomock.Any(), gomock.Any()).Return(&persistence.RangeCompleteHistoryTaskResponse{}, expectedErr) mocked.EXPECT().RangeDeleteReplicationTaskFromDLQ(gomock.Any(), gomock.Any()).Return(&persistence.RangeDeleteReplicationTaskFromDLQResponse{}, expectedErr) mocked.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterSelectionPolicy{}, expectedErr) mocked.EXPECT().DeleteActiveClusterSelectionPolicy(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(expectedErr) } default: t.Errorf("unsupported type %v", reflect.TypeOf(injector)) t.FailNow() } return } func TestGenerateFakeErrorWarmupPeriod(t *testing.T) { tests := []struct { name string starttime time.Time errorRate float64 expectNoErrors bool }{ { name: "no errors within 30 second warmup period", starttime: time.Now(), // just started errorRate: 1.0, // 100% error rate expectNoErrors: true, // but no errors during warmup }, { name: "no errors at 29 seconds", starttime: time.Now().Add(-29 * time.Second), errorRate: 1.0, expectNoErrors: true, }, { name: "errors generated after 30 seconds", starttime: time.Now().Add(-31 * time.Second), errorRate: 1.0, expectNoErrors: false, }, { name: "no errors when error rate is 0 even after warmup", starttime: time.Now().Add(-time.Minute), errorRate: 0.0, expectNoErrors: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { // Call multiple times to account for randomness const iterations = 100 var errorCount int for i := 0; i < iterations; i++ { err := generateFakeError(tc.errorRate, tc.starttime) if err != nil { errorCount++ } } if tc.expectNoErrors { assert.Equal(t, 0, errorCount, "expected no errors during warmup period, but got %d errors in %d iterations", errorCount, iterations) } else { // With 100% error rate and past warmup, we expect errors assert.Greater(t, errorCount, 0, "expected errors after warmup period with 100%% error rate, but got none in %d iterations", iterations) } }) } } ================================================ FILE: common/persistence/wrappers/errorinjectors/queue_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../templates/errorinjector.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" ) // injectorQueueManager implements persistence.QueueManager interface instrumented with error injection. type injectorQueueManager struct { wrapped persistence.QueueManager starttime time.Time errorRate float64 logger log.Logger } // NewQueueManager creates a new instance of QueueManager with error injection. func NewQueueManager( wrapped persistence.QueueManager, errorRate float64, logger log.Logger, starttime time.Time, ) persistence.QueueManager { return &injectorQueueManager{ wrapped: wrapped, starttime: starttime, errorRate: errorRate, logger: logger, } } func (c *injectorQueueManager) Close() { c.wrapped.Close() return } func (c *injectorQueueManager) DeleteMessageFromDLQ(ctx context.Context, request *persistence.DeleteMessageFromDLQRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteMessageFromDLQ(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.DeleteMessageFromDLQ", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) DeleteMessagesBefore(ctx context.Context, request *persistence.DeleteMessagesBeforeRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteMessagesBefore(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.DeleteMessagesBefore", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) EnqueueMessage(ctx context.Context, request *persistence.EnqueueMessageRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.EnqueueMessage(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.EnqueueMessage", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) EnqueueMessageToDLQ(ctx context.Context, request *persistence.EnqueueMessageToDLQRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.EnqueueMessageToDLQ(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.EnqueueMessageToDLQ", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) GetAckLevels(ctx context.Context, request *persistence.GetAckLevelsRequest) (gp1 *persistence.GetAckLevelsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetAckLevels(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.GetAckLevels", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) GetDLQAckLevels(ctx context.Context, request *persistence.GetDLQAckLevelsRequest) (gp1 *persistence.GetDLQAckLevelsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetDLQAckLevels(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.GetDLQAckLevels", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) GetDLQSize(ctx context.Context, request *persistence.GetDLQSizeRequest) (gp1 *persistence.GetDLQSizeResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetDLQSize(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.GetDLQSize", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) RangeDeleteMessagesFromDLQ(ctx context.Context, request *persistence.RangeDeleteMessagesFromDLQRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.RangeDeleteMessagesFromDLQ(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.RangeDeleteMessagesFromDLQ", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) ReadMessages(ctx context.Context, request *persistence.ReadMessagesRequest) (rp1 *persistence.ReadMessagesResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { rp1, err = c.wrapped.ReadMessages(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.ReadMessages", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) ReadMessagesFromDLQ(ctx context.Context, request *persistence.ReadMessagesFromDLQRequest) (rp1 *persistence.ReadMessagesFromDLQResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { rp1, err = c.wrapped.ReadMessagesFromDLQ(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.ReadMessagesFromDLQ", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) UpdateAckLevel(ctx context.Context, request *persistence.UpdateAckLevelRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.UpdateAckLevel(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.UpdateAckLevel", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorQueueManager) UpdateDLQAckLevel(ctx context.Context, request *persistence.UpdateDLQAckLevelRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.UpdateDLQAckLevel(ctx, request) } if fakeErr != nil { logErr(c.logger, "QueueManager.UpdateDLQAckLevel", fakeErr, forwardCall, err) err = fakeErr return } return } ================================================ FILE: common/persistence/wrappers/errorinjectors/shard_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../templates/errorinjector.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" ) // injectorShardManager implements persistence.ShardManager interface instrumented with error injection. type injectorShardManager struct { wrapped persistence.ShardManager starttime time.Time errorRate float64 logger log.Logger } // NewShardManager creates a new instance of ShardManager with error injection. func NewShardManager( wrapped persistence.ShardManager, errorRate float64, logger log.Logger, starttime time.Time, ) persistence.ShardManager { return &injectorShardManager{ wrapped: wrapped, starttime: starttime, errorRate: errorRate, logger: logger, } } func (c *injectorShardManager) Close() { c.wrapped.Close() return } func (c *injectorShardManager) CreateShard(ctx context.Context, request *persistence.CreateShardRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.CreateShard(ctx, request) } if fakeErr != nil { logErr(c.logger, "ShardManager.CreateShard", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorShardManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *injectorShardManager) GetShard(ctx context.Context, request *persistence.GetShardRequest) (gp1 *persistence.GetShardResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetShard(ctx, request) } if fakeErr != nil { logErr(c.logger, "ShardManager.GetShard", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorShardManager) UpdateShard(ctx context.Context, request *persistence.UpdateShardRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.UpdateShard(ctx, request) } if fakeErr != nil { logErr(c.logger, "ShardManager.UpdateShard", fakeErr, forwardCall, err) err = fakeErr return } return } ================================================ FILE: common/persistence/wrappers/errorinjectors/task_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../templates/errorinjector.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" ) // injectorTaskManager implements persistence.TaskManager interface instrumented with error injection. type injectorTaskManager struct { wrapped persistence.TaskManager starttime time.Time errorRate float64 logger log.Logger } // NewTaskManager creates a new instance of TaskManager with error injection. func NewTaskManager( wrapped persistence.TaskManager, errorRate float64, logger log.Logger, starttime time.Time, ) persistence.TaskManager { return &injectorTaskManager{ wrapped: wrapped, starttime: starttime, errorRate: errorRate, logger: logger, } } func (c *injectorTaskManager) Close() { c.wrapped.Close() return } func (c *injectorTaskManager) CompleteTask(ctx context.Context, request *persistence.CompleteTaskRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.CompleteTask(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.CompleteTask", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorTaskManager) CompleteTasksLessThan(ctx context.Context, request *persistence.CompleteTasksLessThanRequest) (cp1 *persistence.CompleteTasksLessThanResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { cp1, err = c.wrapped.CompleteTasksLessThan(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.CompleteTasksLessThan", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorTaskManager) CreateTasks(ctx context.Context, request *persistence.CreateTasksRequest) (cp1 *persistence.CreateTasksResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { cp1, err = c.wrapped.CreateTasks(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.CreateTasks", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorTaskManager) DeleteTaskList(ctx context.Context, request *persistence.DeleteTaskListRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteTaskList(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.DeleteTaskList", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorTaskManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *injectorTaskManager) GetOrphanTasks(ctx context.Context, request *persistence.GetOrphanTasksRequest) (gp1 *persistence.GetOrphanTasksResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetOrphanTasks(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.GetOrphanTasks", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorTaskManager) GetTaskList(ctx context.Context, request *persistence.GetTaskListRequest) (gp1 *persistence.GetTaskListResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetTaskList(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.GetTaskList", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorTaskManager) GetTaskListSize(ctx context.Context, request *persistence.GetTaskListSizeRequest) (gp1 *persistence.GetTaskListSizeResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetTaskListSize(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.GetTaskListSize", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorTaskManager) GetTasks(ctx context.Context, request *persistence.GetTasksRequest) (gp1 *persistence.GetTasksResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetTasks(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.GetTasks", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorTaskManager) LeaseTaskList(ctx context.Context, request *persistence.LeaseTaskListRequest) (lp1 *persistence.LeaseTaskListResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.LeaseTaskList(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.LeaseTaskList", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorTaskManager) ListTaskList(ctx context.Context, request *persistence.ListTaskListRequest) (lp1 *persistence.ListTaskListResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListTaskList(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.ListTaskList", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorTaskManager) UpdateTaskList(ctx context.Context, request *persistence.UpdateTaskListRequest) (up1 *persistence.UpdateTaskListResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { up1, err = c.wrapped.UpdateTaskList(ctx, request) } if fakeErr != nil { logErr(c.logger, "TaskManager.UpdateTaskList", fakeErr, forwardCall, err) err = fakeErr return } return } ================================================ FILE: common/persistence/wrappers/errorinjectors/utils.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package errorinjectors import ( "fmt" "math/rand" "strings" "time" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" ) // _randomStubFunc is a stub randomized function that could be overriden in tests. // It introduces randomness to timeout and unhandled errors, to mimic retriable db isues. var _randomStubFunc = func() bool { // forward the call with 50% chance return rand.Intn(2) == 0 } func shouldForwardCallToPersistence( err error, ) bool { if err == nil { return true } if err == ErrFakeTimeout || err == errors.ErrFakeUnhandled { return _randomStubFunc() } return false } func generateFakeError( errorRate float64, starttime time.Time, ) error { // don't inject errors before 30 seconds of startup to avoid overwhelming the system // on startup if time.Since(starttime) < 30*time.Second { return nil } randFl := rand.Float64() if randFl < errorRate { return fakeErrors[rand.Intn(len(fakeErrors))] } return nil } var ( // ErrFakeTimeout is a fake persistence timeout error. ErrFakeTimeout = &persistence.TimeoutError{Msg: "Fake Persistence Timeout Error."} ) var ( fakeErrors = []error{ errors.ErrFakeServiceBusy, errors.ErrFakeInternalService, ErrFakeTimeout, errors.ErrFakeUnhandled, } ) func isFakeError(err error) bool { for _, fakeErr := range fakeErrors { if err == fakeErr { return true } } return false } const ( msgInjectedFakeErr = "Injected fake persistence error" ) func logErr(logger log.Logger, objectMethod string, fakeErr error, forwardCall bool, err error) { logger.Error(msgInjectedFakeErr, getOperationFromMethodName(objectMethod), tag.Error(fakeErr), tag.Bool(forwardCall), tag.StoreError(err), ) } func getOperationFromMethodName(op string) tag.Tag { var t *tag.Tag switch { case strings.HasPrefix(op, "ConfigStoreManager"): t = configManagerTags(op) case strings.HasPrefix(op, "DomainManager"): t = domainManagerTags(op) case strings.HasPrefix(op, "HistoryManager"): t = historyManagerTags(op) case strings.HasPrefix(op, "ShardManager"): t = shardManagerTags(op) case strings.HasPrefix(op, "ExecutionManager"): t = executionManagerTags(op) case strings.HasPrefix(op, "VisibilityManager"): t = visibilityManagerTags(op) case strings.HasPrefix(op, "QueueManager"): t = queueManagerTags(op) case strings.HasPrefix(op, "TaskManager"): t = taskManagerTags(op) } if t == nil { panic(fmt.Sprintf("No tag defined for operation %s", op)) } return *t } func configManagerTags(op string) *tag.Tag { switch op { case "ConfigStoreManager.FetchDynamicConfig": return &tag.StoreOperationFetchDynamicConfig case "ConfigStoreManager.UpdateDynamicConfig": return &tag.StoreOperationUpdateDynamicConfig } return nil } func domainManagerTags(op string) *tag.Tag { switch op { case "DomainManager.CreateDomain": return &tag.StoreOperationCreateDomain case "DomainManager.GetDomain": return &tag.StoreOperationGetDomain case "DomainManager.DeleteDomain": return &tag.StoreOperationDeleteDomain case "DomainManager.DeleteDomainByName": return &tag.StoreOperationDeleteDomainByName case "DomainManager.ListDomains": return &tag.StoreOperationListDomains case "DomainManager.GetMetadata": return &tag.StoreOperationGetMetadata case "DomainManager.UpdateDomain": return &tag.StoreOperationUpdateDomain } return nil } func historyManagerTags(op string) *tag.Tag { switch op { case "HistoryManager.AppendHistoryNodes": return &tag.StoreOperationAppendHistoryNodes case "HistoryManager.DeleteHistoryBranch": return &tag.StoreOperationDeleteHistoryBranch case "HistoryManager.ForkHistoryBranch": return &tag.StoreOperationForkHistoryBranch case "HistoryManager.GetHistoryTree": return &tag.StoreOperationGetHistoryTree case "HistoryManager.ReadHistoryBranch": return &tag.StoreOperationReadHistoryBranch case "HistoryManager.ReadHistoryBranchByBatch": return &tag.StoreOperationReadHistoryBranchByBatch case "HistoryManager.ReadRawHistoryBranch": return &tag.StoreOperationReadRawHistoryBranch case "HistoryManager.GetAllHistoryTreeBranches": return &tag.StoreOperationGetAllHistoryTreeBranches } return nil } func shardManagerTags(op string) *tag.Tag { switch op { case "ShardManager.CreateShard": return &tag.StoreOperationCreateShard case "ShardManager.GetShard": return &tag.StoreOperationGetShard case "ShardManager.UpdateShard": return &tag.StoreOperationUpdateShard } return nil } func executionManagerTags(op string) *tag.Tag { switch op { case "ExecutionManager.CreateWorkflowExecution": return &tag.StoreOperationCreateWorkflowExecution case "ExecutionManager.GetWorkflowExecution": return &tag.StoreOperationGetWorkflowExecution case "ExecutionManager.UpdateWorkflowExecution": return &tag.StoreOperationUpdateWorkflowExecution case "ExecutionManager.ResetWorkflowExecution": return &tag.StoreOperationResetWorkflowExecution case "ExecutionManager.DeleteWorkflowExecution": return &tag.StoreOperationDeleteWorkflowExecution case "ExecutionManager.GetCurrentExecution": return &tag.StoreOperationGetCurrentExecution case "ExecutionManager.ConflictResolveWorkflowExecution": return &tag.StoreOperationConflictResolveWorkflowExecution case "ExecutionManager.DeleteCurrentWorkflowExecution": return &tag.StoreOperationDeleteCurrentWorkflowExecution case "ExecutionManager.ListCurrentExecutions": return &tag.StoreOperationListCurrentExecution case "ExecutionManager.IsWorkflowExecutionExists": return &tag.StoreOperationIsWorkflowExecutionExists case "ExecutionManager.ListConcreteExecutions": return &tag.StoreOperationListConcreteExecution case "ExecutionManager.GetTransferTasks": return &tag.StoreOperationGetTransferTasks case "ExecutionManager.GetCrossClusterTasks": return &tag.StoreOperationGetCrossClusterTasks case "ExecutionManager.GetReplicationTasks": return &tag.StoreOperationGetReplicationTasks case "ExecutionManager.CompleteTransferTask": return &tag.StoreOperationCompleteTransferTask case "ExecutionManager.GetHistoryTasks": return &tag.StoreOperationGetHistoryTasks case "ExecutionManager.CompleteHistoryTask": return &tag.StoreOperationCompleteHistoryTask case "ExecutionManager.RangeCompleteHistoryTask": return &tag.StoreOperationRangeCompleteHistoryTask case "ExecutionManager.CompleteCrossClusterTask": return &tag.StoreOperationCompleteCrossClusterTask case "ExecutionManager.CompleteReplicationTask": return &tag.StoreOperationCompleteReplicationTask case "ExecutionManager.PutReplicationTaskToDLQ": return &tag.StoreOperationPutReplicationTaskToDLQ case "ExecutionManager.GetReplicationTasksFromDLQ": return &tag.StoreOperationGetReplicationTasksFromDLQ case "ExecutionManager.GetReplicationDLQSize": return &tag.StoreOperationGetReplicationDLQSize case "ExecutionManager.DeleteReplicationTaskFromDLQ": return &tag.StoreOperationDeleteReplicationTaskFromDLQ case "ExecutionManager.RangeDeleteReplicationTaskFromDLQ": return &tag.StoreOperationRangeDeleteReplicationTaskFromDLQ case "ExecutionManager.GetTimerIndexTasks": return &tag.StoreOperationGetTimerIndexTasks case "ExecutionManager.CompleteTimerTask": return &tag.StoreOperationCompleteTimerTask case "ExecutionManager.CreateFailoverMarkerTasks": return &tag.StoreOperationCreateFailoverMarkerTasks case "ExecutionManager.GetActiveClusterSelectionPolicy": return &tag.StoreOperationGetActiveClusterSelectionPolicy case "ExecutionManager.DeleteActiveClusterSelectionPolicy": return &tag.StoreOperationDeleteActiveClusterSelectionPolicy } return nil } func visibilityManagerTags(op string) *tag.Tag { switch op { case "VisibilityManager.RecordWorkflowExecutionStarted": return &tag.StoreOperationRecordWorkflowExecutionStarted case "VisibilityManager.RecordWorkflowExecutionClosed": return &tag.StoreOperationRecordWorkflowExecutionClosed case "VisibilityManager.UpsertWorkflowExecution": return &tag.StoreOperationUpsertWorkflowExecution case "VisibilityManager.ListOpenWorkflowExecutions": return &tag.StoreOperationListOpenWorkflowExecutions case "VisibilityManager.ListClosedWorkflowExecutions": return &tag.StoreOperationListClosedWorkflowExecutions case "VisibilityManager.ListOpenWorkflowExecutionsByType": return &tag.StoreOperationListOpenWorkflowExecutionsByType case "VisibilityManager.ListClosedWorkflowExecutionsByType": return &tag.StoreOperationListClosedWorkflowExecutionsByType case "VisibilityManager.ListOpenWorkflowExecutionsByWorkflowID": return &tag.StoreOperationListOpenWorkflowExecutionsByWorkflowID case "VisibilityManager.ListClosedWorkflowExecutionsByWorkflowID": return &tag.StoreOperationListClosedWorkflowExecutionsByWorkflowID case "VisibilityManager.ListClosedWorkflowExecutionsByStatus": return &tag.StoreOperationListClosedWorkflowExecutionsByStatus case "VisibilityManager.GetClosedWorkflowExecution": return &tag.StoreOperationGetClosedWorkflowExecution case "VisibilityManager.DeleteWorkflowExecution": return &tag.StoreOperationDeleteWorkflowExecution case "VisibilityManager.ListWorkflowExecutions": return &tag.StoreOperationListWorkflowExecutions case "VisibilityManager.ScanWorkflowExecutions": return &tag.StoreOperationScanWorkflowExecutions case "VisibilityManager.CountWorkflowExecutions": return &tag.StoreOperationCountWorkflowExecutions case "VisibilityManager.DeleteUninitializedWorkflowExecution": return &tag.StoreOperationDeleteUninitializedWorkflowExecution case "VisibilityManager.RecordWorkflowExecutionUninitialized": return &tag.StoreOperationRecordWorkflowExecutionUninitialized } return nil } func taskManagerTags(op string) *tag.Tag { switch op { case "TaskManager.LeaseTaskList": return &tag.StoreOperationLeaseTaskList case "TaskManager.GetTaskList": return &tag.StoreOperationGetTaskList case "TaskManager.UpdateTaskList": return &tag.StoreOperationUpdateTaskList case "TaskManager.CreateTasks": return &tag.StoreOperationCreateTasks case "TaskManager.GetTasks": return &tag.StoreOperationGetTasks case "TaskManager.CompleteTask": return &tag.StoreOperationCompleteTask case "TaskManager.CompleteTasksLessThan": return &tag.StoreOperationCompleteTasksLessThan case "TaskManager.DeleteTaskList": return &tag.StoreOperationDeleteTaskList case "TaskManager.GetOrphanTasks": return &tag.StoreOperationGetOrphanTasks case "TaskManager.GetTaskListSize": return &tag.StoreOperationGetTaskListSize case "TaskManager.ListTaskList": return &tag.StoreOperationListTaskList } return nil } func queueManagerTags(op string) *tag.Tag { switch op { case "QueueManager.EnqueueMessage": return &tag.StoreOperationEnqueueMessage case "QueueManager.EnqueueMessageToDLQ": return &tag.StoreOperationEnqueueMessageToDLQ case "QueueManager.DeleteMessageFromDLQ": return &tag.StoreOperationDeleteMessageFromDLQ case "QueueManager.RangeDeleteMessagesFromDLQ": return &tag.StoreOperationRangeDeleteMessagesFromDLQ case "QueueManager.UpdateAckLevel": return &tag.StoreOperationUpdateAckLevel case "QueueManager.GetAckLevels": return &tag.StoreOperationGetAckLevels case "QueueManager.UpdateDLQAckLevel": return &tag.StoreOperationUpdateDLQAckLevel case "QueueManager.GetDLQAckLevels": return &tag.StoreOperationGetDLQAckLevels case "QueueManager.GetDLQSize": return &tag.StoreOperationGetDLQSize case "QueueManager.DeleteMessagesBefore": return &tag.StoreOperationDeleteMessagesBefore case "QueueManager.ReadMessages": return &tag.StoreOperationReadMessages case "QueueManager.ReadMessagesFromDLQ": return &tag.StoreOperationReadMessagesFromDLQ } return nil } ================================================ FILE: common/persistence/wrappers/errorinjectors/visibility_generated.go ================================================ package errorinjectors // Code generated by gowrap. DO NOT EDIT. // template: ../templates/errorinjector.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" ) // injectorVisibilityManager implements persistence.VisibilityManager interface instrumented with error injection. type injectorVisibilityManager struct { wrapped persistence.VisibilityManager starttime time.Time errorRate float64 logger log.Logger } // NewVisibilityManager creates a new instance of VisibilityManager with error injection. func NewVisibilityManager( wrapped persistence.VisibilityManager, errorRate float64, logger log.Logger, starttime time.Time, ) persistence.VisibilityManager { return &injectorVisibilityManager{ wrapped: wrapped, starttime: starttime, errorRate: errorRate, logger: logger, } } func (c *injectorVisibilityManager) Close() { c.wrapped.Close() return } func (c *injectorVisibilityManager) CountWorkflowExecutions(ctx context.Context, request *persistence.CountWorkflowExecutionsRequest) (cp1 *persistence.CountWorkflowExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { cp1, err = c.wrapped.CountWorkflowExecutions(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.CountWorkflowExecutions", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) DeleteUninitializedWorkflowExecution(ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteUninitializedWorkflowExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.DeleteUninitializedWorkflowExecution", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) DeleteWorkflowExecution(ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.DeleteWorkflowExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.DeleteWorkflowExecution", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) GetClosedWorkflowExecution(ctx context.Context, request *persistence.GetClosedWorkflowExecutionRequest) (gp1 *persistence.GetClosedWorkflowExecutionResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { gp1, err = c.wrapped.GetClosedWorkflowExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.GetClosedWorkflowExecution", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *injectorVisibilityManager) ListClosedWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListClosedWorkflowExecutions(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.ListClosedWorkflowExecutions", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) ListClosedWorkflowExecutionsByStatus(ctx context.Context, request *persistence.ListClosedWorkflowExecutionsByStatusRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListClosedWorkflowExecutionsByStatus(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.ListClosedWorkflowExecutionsByStatus", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) ListClosedWorkflowExecutionsByType(ctx context.Context, request *persistence.ListWorkflowExecutionsByTypeRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListClosedWorkflowExecutionsByType(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.ListClosedWorkflowExecutionsByType", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) ListClosedWorkflowExecutionsByWorkflowID(ctx context.Context, request *persistence.ListWorkflowExecutionsByWorkflowIDRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.ListClosedWorkflowExecutionsByWorkflowID", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) ListOpenWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListOpenWorkflowExecutions(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.ListOpenWorkflowExecutions", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) ListOpenWorkflowExecutionsByType(ctx context.Context, request *persistence.ListWorkflowExecutionsByTypeRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListOpenWorkflowExecutionsByType(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.ListOpenWorkflowExecutionsByType", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) ListOpenWorkflowExecutionsByWorkflowID(ctx context.Context, request *persistence.ListWorkflowExecutionsByWorkflowIDRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.ListOpenWorkflowExecutionsByWorkflowID", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) ListWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsByQueryRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ListWorkflowExecutions(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.ListWorkflowExecutions", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) RecordWorkflowExecutionClosed(ctx context.Context, request *persistence.RecordWorkflowExecutionClosedRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.RecordWorkflowExecutionClosed(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.RecordWorkflowExecutionClosed", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) RecordWorkflowExecutionStarted(ctx context.Context, request *persistence.RecordWorkflowExecutionStartedRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.RecordWorkflowExecutionStarted(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.RecordWorkflowExecutionStarted", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) RecordWorkflowExecutionUninitialized(ctx context.Context, request *persistence.RecordWorkflowExecutionUninitializedRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.RecordWorkflowExecutionUninitialized(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.RecordWorkflowExecutionUninitialized", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) ScanWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsByQueryRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { lp1, err = c.wrapped.ScanWorkflowExecutions(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.ScanWorkflowExecutions", fakeErr, forwardCall, err) err = fakeErr return } return } func (c *injectorVisibilityManager) UpsertWorkflowExecution(ctx context.Context, request *persistence.UpsertWorkflowExecutionRequest) (err error) { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { err = c.wrapped.UpsertWorkflowExecution(ctx, request) } if fakeErr != nil { logErr(c.logger, "VisibilityManager.UpsertWorkflowExecution", fakeErr, forwardCall, err) err = fakeErr return } return } ================================================ FILE: common/persistence/wrappers/metered/base.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metered import ( "context" "errors" "time" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type retryCountKeyType string const retryCountKey = retryCountKeyType("retryCount") type base struct { metricClient metrics.Client logger log.Logger enableLatencyHistogramMetrics bool sampleLoggingRate dynamicproperties.IntPropertyFn enableShardIDMetrics dynamicproperties.BoolPropertyFn } func (p *base) updateErrorMetricPerDomain(scope metrics.ScopeIdx, err error, scopeWithDomainTag metrics.Scope, logger log.Logger) { logger = logger.Helper() switch { case errors.As(err, new(*types.DomainAlreadyExistsError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrDomainAlreadyExistsCounterPerDomain) case errors.As(err, new(*types.BadRequestError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrBadRequestCounterPerDomain) case errors.As(err, new(*persistence.WorkflowExecutionAlreadyStartedError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrExecutionAlreadyStartedCounterPerDomain) case errors.As(err, new(*persistence.ConditionFailedError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrConditionFailedCounterPerDomain) case errors.As(err, new(*persistence.CurrentWorkflowConditionFailedError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrCurrentWorkflowConditionFailedCounterPerDomain) case errors.As(err, new(*persistence.ShardAlreadyExistError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrShardExistsCounterPerDomain) case errors.As(err, new(*persistence.ShardOwnershipLostError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrShardOwnershipLostCounterPerDomain) case errors.As(err, new(*types.EntityNotExistsError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrEntityNotExistsCounterPerDomain) case errors.As(err, new(*persistence.DuplicateRequestError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrDuplicateRequestCounterPerDomain) case errors.As(err, new(*persistence.TimeoutError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrTimeoutCounterPerDomain) scopeWithDomainTag.IncCounter(metrics.PersistenceFailuresPerDomain) case errors.As(err, new(*types.ServiceBusyError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrBusyCounterPerDomain) scopeWithDomainTag.IncCounter(metrics.PersistenceFailuresPerDomain) case errors.As(err, new(*persistence.DBUnavailableError)): scopeWithDomainTag.IncCounter(metrics.PersistenceErrDBUnavailableCounterPerDomain) scopeWithDomainTag.IncCounter(metrics.PersistenceFailuresPerDomain) logger.Error("DBUnavailable Error:", tag.Error(err), tag.MetricScope(int(scope))) default: logger.Error("Operation failed with internal error.", tag.Error(err), tag.MetricScope(int(scope))) scopeWithDomainTag.IncCounter(metrics.PersistenceFailuresPerDomain) } } func (p *base) updateErrorMetric(scope metrics.ScopeIdx, err error, metricsScope metrics.Scope, logger log.Logger) { logger = logger.Helper() switch { case errors.As(err, new(*types.DomainAlreadyExistsError)): metricsScope.IncCounter(metrics.PersistenceErrDomainAlreadyExistsCounter) case errors.As(err, new(*types.BadRequestError)): metricsScope.IncCounter(metrics.PersistenceErrBadRequestCounter) case errors.As(err, new(*persistence.WorkflowExecutionAlreadyStartedError)): metricsScope.IncCounter(metrics.PersistenceErrExecutionAlreadyStartedCounter) case errors.As(err, new(*persistence.ConditionFailedError)): metricsScope.IncCounter(metrics.PersistenceErrConditionFailedCounter) case errors.As(err, new(*persistence.CurrentWorkflowConditionFailedError)): metricsScope.IncCounter(metrics.PersistenceErrCurrentWorkflowConditionFailedCounter) case errors.As(err, new(*persistence.ShardAlreadyExistError)): metricsScope.IncCounter(metrics.PersistenceErrShardExistsCounter) case errors.As(err, new(*persistence.ShardOwnershipLostError)): metricsScope.IncCounter(metrics.PersistenceErrShardOwnershipLostCounter) case errors.As(err, new(*types.EntityNotExistsError)): metricsScope.IncCounter(metrics.PersistenceErrEntityNotExistsCounter) case errors.As(err, new(*persistence.DuplicateRequestError)): metricsScope.IncCounter(metrics.PersistenceErrDuplicateRequestCounter) case errors.As(err, new(*persistence.TimeoutError)): metricsScope.IncCounter(metrics.PersistenceErrTimeoutCounter) metricsScope.IncCounter(metrics.PersistenceFailures) case errors.As(err, new(*types.ServiceBusyError)): metricsScope.IncCounter(metrics.PersistenceErrBusyCounter) metricsScope.IncCounter(metrics.PersistenceFailures) case errors.As(err, new(*persistence.DBUnavailableError)): metricsScope.IncCounter(metrics.PersistenceErrDBUnavailableCounter) metricsScope.IncCounter(metrics.PersistenceFailures) logger.Error("DBUnavailable Error:", tag.Error(err), tag.MetricScope(int(scope))) default: logger.Error("Operation failed with internal error.", tag.Error(err), tag.MetricScope(int(scope))) metricsScope.IncCounter(metrics.PersistenceFailures) } } func (p *base) call(scope metrics.ScopeIdx, op func() error, tags ...metrics.Tag) error { metricsScope := p.metricClient.Scope(scope, tags...) if len(tags) > 0 { metricsScope.IncCounter(metrics.PersistenceRequestsPerDomain) } else { metricsScope.IncCounter(metrics.PersistenceRequests) } before := time.Now() err := op() duration := time.Since(before) if len(tags) > 0 { metricsScope.RecordTimer(metrics.PersistenceLatencyPerDomain, duration) } else { metricsScope.RecordTimer(metrics.PersistenceLatency, duration) } if p.enableLatencyHistogramMetrics { metricsScope.RecordHistogramDuration(metrics.PersistenceLatencyHistogram, duration) } logger := p.logger.Helper() if err != nil { if len(tags) > 0 { p.updateErrorMetricPerDomain(scope, err, metricsScope, logger) } else { p.updateErrorMetric(scope, err, metricsScope, logger) } } return err } func (p *base) callWithoutDomainTag(scope metrics.ScopeIdx, op func() error, tags ...metrics.Tag) error { metricsScope := p.metricClient.Scope(scope, tags...) metricsScope.IncCounter(metrics.PersistenceRequests) before := time.Now() err := op() duration := time.Since(before) metricsScope.RecordTimer(metrics.PersistenceLatency, duration) if p.enableLatencyHistogramMetrics { metricsScope.RecordHistogramDuration(metrics.PersistenceLatencyHistogram, duration) } if err != nil { p.updateErrorMetric(scope, err, metricsScope, p.logger.Helper()) } return err } func (p *base) callWithDomainAndShardScope(scope metrics.ScopeIdx, op func() error, domainTag metrics.Tag, shardIDTag metrics.Tag, additionalTags ...metrics.Tag) error { domainMetricsScope := p.metricClient.Scope(scope, append([]metrics.Tag{domainTag}, additionalTags...)...) shardOperationsMetricsScope := p.metricClient.Scope(scope, append([]metrics.Tag{shardIDTag}, additionalTags...)...) shardOverallMetricsScope := p.metricClient.Scope(metrics.PersistenceShardRequestCountScope, shardIDTag) domainMetricsScope.IncCounter(metrics.PersistenceRequestsPerDomain) shardOperationsMetricsScope.IncCounter(metrics.PersistenceRequestsPerShard) shardOverallMetricsScope.IncCounter(metrics.PersistenceRequestsPerShard) before := time.Now() err := op() duration := time.Since(before) domainMetricsScope.RecordTimer(metrics.PersistenceLatencyPerDomain, duration) shardOperationsMetricsScope.RecordTimer(metrics.PersistenceLatencyPerShard, duration) shardOverallMetricsScope.RecordTimer(metrics.PersistenceLatencyPerShard, duration) if p.enableLatencyHistogramMetrics { domainMetricsScope.RecordHistogramDuration(metrics.PersistenceLatencyHistogram, duration) } if err != nil { p.updateErrorMetricPerDomain(scope, err, domainMetricsScope, p.logger.Helper()) } return err } type lengther interface { Len() int } type sizer interface { ByteSize() uint64 } type taggedRequest interface { MetricTags() []metrics.Tag } type extraLogRequest interface { GetExtraLogTags() []tag.Tag } func (p *base) emitRowCountMetrics(methodName string, req any, res any) { scope, ok := emptyCountedMethods[methodName] if !ok { // Method is not counted as empty. return } resLen, ok := res.(lengther) if !ok { return } metricScope := p.metricClient.Scope(scope.scope, getCustomMetricTags(req)...) if resLen.Len() == 0 { metricScope.IncCounter(metrics.PersistenceEmptyResponseCounter) } else { metricScope.RecordHistogramValue(metrics.PersistenceResponseRowSize, float64(resLen.Len())) } } func (p *base) emitPayloadSizeMetrics(methodName string, req any, res any) { scope, ok := payloadSizeEmittingMethods[methodName] if !ok { return } resSize, ok := res.(sizer) if !ok { return } metricScope := p.metricClient.Scope(scope.scope, getCustomMetricTags(req)...) metricScope.RecordHistogramValue(metrics.PersistenceResponsePayloadSize, float64(resSize.ByteSize())) } func (p *base) emptyMetric(methodName string, req any, res any, err error) { if err != nil { return } p.emitRowCountMetrics(methodName, req, res) p.emitPayloadSizeMetrics(methodName, req, res) } var emptyCountedMethods = map[string]struct { scope metrics.ScopeIdx }{ "ExecutionManager.ListCurrentExecutions": { scope: metrics.PersistenceListCurrentExecutionsScope, }, "ExecutionManager.GetReplicationTasksFromDLQ": { scope: metrics.PersistenceGetReplicationTasksFromDLQScope, }, "ExecutionManager.GetHistoryTasks": { scope: metrics.PersistenceGetHistoryTasksScope, }, "TaskManager.GetTasks": { scope: metrics.PersistenceGetTasksScope, }, "DomainManager.ListDomains": { scope: metrics.PersistenceListDomainsScope, }, "HistoryManager.ReadHistoryBranch": { scope: metrics.PersistenceReadHistoryBranchScope, }, "HistoryManager.GetAllHistoryTreeBranches": { scope: metrics.PersistenceGetAllHistoryTreeBranchesScope, }, "QueueManager.ReadMessages": { scope: metrics.PersistenceReadMessagesScope, }, } var payloadSizeEmittingMethods = map[string]struct { scope metrics.ScopeIdx }{ "ExecutionManager.ListCurrentExecutions": { scope: metrics.PersistenceListCurrentExecutionsScope, }, "ExecutionManager.GetReplicationTasksFromDLQ": { scope: metrics.PersistenceGetReplicationTasksFromDLQScope, }, "ExecutionManager.GetHistoryTasks": { scope: metrics.PersistenceGetHistoryTasksScope, }, "TaskManager.GetTasks": { scope: metrics.PersistenceGetTasksScope, }, "DomainManager.ListDomains": { scope: metrics.PersistenceListDomainsScope, }, "HistoryManager.ReadRawHistoryBranch": { scope: metrics.PersistenceReadRawHistoryBranchScope, }, "HistoryManager.GetAllHistoryTreeBranches": { scope: metrics.PersistenceGetAllHistoryTreeBranchesScope, }, "QueueManager.ReadMessages": { scope: metrics.PersistenceReadMessagesScope, }, } type domainTaggedRequest interface { GetDomainName() string } func getDomainNameFromRequest(req any) (res string, check bool) { d, check := req.(domainTaggedRequest) if check { res = d.GetDomainName() } return res, check } func getCustomLogTags(req any) (res []tag.Tag) { d, check := req.(extraLogRequest) if check { res = d.GetExtraLogTags() } return res } func getCustomMetricTags(req any) (res []metrics.Tag) { d, check := req.(taggedRequest) if check { res = d.MetricTags() } return res } func getRetryCountFromContext(ctx context.Context) int { if retryCount, ok := ctx.Value(retryCountKey).(int); ok { return retryCount } return 0 } ================================================ FILE: common/persistence/wrappers/metered/configstore_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) // meteredConfigStoreManager implements persistence.ConfigStoreManager interface instrumented with rate limiter. type meteredConfigStoreManager struct { base wrapped persistence.ConfigStoreManager } // NewConfigStoreManager creates a new instance of ConfigStoreManager with ratelimiter. func NewConfigStoreManager( wrapped persistence.ConfigStoreManager, metricClient metrics.Client, logger log.Logger, cfg *config.Persistence, ) persistence.ConfigStoreManager { return &meteredConfigStoreManager{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger, enableLatencyHistogramMetrics: cfg.EnablePersistenceLatencyHistogramMetrics, }, } } func (c *meteredConfigStoreManager) Close() { c.wrapped.Close() return } func (c *meteredConfigStoreManager) FetchDynamicConfig(ctx context.Context, cfgType persistence.ConfigType) (fp1 *persistence.FetchDynamicConfigResponse, err error) { op := func() error { fp1, err = c.wrapped.FetchDynamicConfig(ctx, cfgType) c.emptyMetric("ConfigStoreManager.FetchDynamicConfig", cfgType, fp1, err) return err } err = c.call(metrics.PersistenceFetchDynamicConfigScope, op, getCustomMetricTags(cfgType)...) return } func (c *meteredConfigStoreManager) UpdateDynamicConfig(ctx context.Context, request *persistence.UpdateDynamicConfigRequest, cfgType persistence.ConfigType) (err error) { op := func() error { err = c.wrapped.UpdateDynamicConfig(ctx, request, cfgType) c.emptyMetric("ConfigStoreManager.UpdateDynamicConfig", request, err, err) return err } err = c.call(metrics.PersistenceUpdateDynamicConfigScope, op, getCustomMetricTags(request)...) return } ================================================ FILE: common/persistence/wrappers/metered/domain_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) // meteredDomainManager implements persistence.DomainManager interface instrumented with rate limiter. type meteredDomainManager struct { base wrapped persistence.DomainManager } // NewDomainManager creates a new instance of DomainManager with ratelimiter. func NewDomainManager( wrapped persistence.DomainManager, metricClient metrics.Client, logger log.Logger, cfg *config.Persistence, ) persistence.DomainManager { return &meteredDomainManager{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger, enableLatencyHistogramMetrics: cfg.EnablePersistenceLatencyHistogramMetrics, }, } } func (c *meteredDomainManager) Close() { c.wrapped.Close() return } func (c *meteredDomainManager) CreateDomain(ctx context.Context, request *persistence.CreateDomainRequest) (cp1 *persistence.CreateDomainResponse, err error) { op := func() error { cp1, err = c.wrapped.CreateDomain(ctx, request) c.emptyMetric("DomainManager.CreateDomain", request, cp1, err) return err } err = c.call(metrics.PersistenceCreateDomainScope, op, getCustomMetricTags(request)...) return } func (c *meteredDomainManager) DeleteDomain(ctx context.Context, request *persistence.DeleteDomainRequest) (err error) { op := func() error { err = c.wrapped.DeleteDomain(ctx, request) c.emptyMetric("DomainManager.DeleteDomain", request, err, err) return err } err = c.call(metrics.PersistenceDeleteDomainScope, op, getCustomMetricTags(request)...) return } func (c *meteredDomainManager) DeleteDomainByName(ctx context.Context, request *persistence.DeleteDomainByNameRequest) (err error) { op := func() error { err = c.wrapped.DeleteDomainByName(ctx, request) c.emptyMetric("DomainManager.DeleteDomainByName", request, err, err) return err } err = c.call(metrics.PersistenceDeleteDomainByNameScope, op, getCustomMetricTags(request)...) return } func (c *meteredDomainManager) GetDomain(ctx context.Context, request *persistence.GetDomainRequest) (gp1 *persistence.GetDomainResponse, err error) { op := func() error { gp1, err = c.wrapped.GetDomain(ctx, request) c.emptyMetric("DomainManager.GetDomain", request, gp1, err) return err } err = c.call(metrics.PersistenceGetDomainScope, op, getCustomMetricTags(request)...) return } func (c *meteredDomainManager) GetMetadata(ctx context.Context) (gp1 *persistence.GetMetadataResponse, err error) { op := func() error { gp1, err = c.wrapped.GetMetadata(ctx) return err } err = c.call(metrics.PersistenceGetMetadataScope, op) return } func (c *meteredDomainManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *meteredDomainManager) ListDomains(ctx context.Context, request *persistence.ListDomainsRequest) (lp1 *persistence.ListDomainsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListDomains(ctx, request) c.emptyMetric("DomainManager.ListDomains", request, lp1, err) return err } err = c.call(metrics.PersistenceListDomainsScope, op, getCustomMetricTags(request)...) return } func (c *meteredDomainManager) UpdateDomain(ctx context.Context, request *persistence.UpdateDomainRequest) (err error) { op := func() error { err = c.wrapped.UpdateDomain(ctx, request) c.emptyMetric("DomainManager.UpdateDomain", request, err, err) return err } err = c.call(metrics.PersistenceUpdateDomainScope, op, getCustomMetricTags(request)...) return } ================================================ FILE: common/persistence/wrappers/metered/execution_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../templates/metered_execution.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // meteredExecutionManager implements persistence.ExecutionManager interface instrumented with rate limiter. type meteredExecutionManager struct { base wrapped persistence.ExecutionManager } // NewExecutionManager creates a new instance of ExecutionManager with ratelimiter. func NewExecutionManager( wrapped persistence.ExecutionManager, metricClient metrics.Client, logger log.Logger, cfg *config.Persistence, sampleLoggingRate dynamicproperties.IntPropertyFn, enableShardIDMetrics dynamicproperties.BoolPropertyFn, ) persistence.ExecutionManager { return &meteredExecutionManager{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger.WithTags(tag.ShardID(wrapped.GetShardID())), enableLatencyHistogramMetrics: cfg.EnablePersistenceLatencyHistogramMetrics, sampleLoggingRate: sampleLoggingRate, enableShardIDMetrics: enableShardIDMetrics, }, } } func (c *meteredExecutionManager) Close() { c.wrapped.Close() return } func (c *meteredExecutionManager) CompleteHistoryTask(ctx context.Context, request *persistence.CompleteHistoryTaskRequest) (err error) { op := func() error { err = c.wrapped.CompleteHistoryTask(ctx, request) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence CompleteHistoryTask called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceCompleteHistoryTaskScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceCompleteHistoryTaskScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceCompleteHistoryTaskScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) ConflictResolveWorkflowExecution(ctx context.Context, request *persistence.ConflictResolveWorkflowExecutionRequest) (cp1 *persistence.ConflictResolveWorkflowExecutionResponse, err error) { op := func() error { cp1, err = c.wrapped.ConflictResolveWorkflowExecution(ctx, request) c.emptyMetric("ExecutionManager.ConflictResolveWorkflowExecution", request, cp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence ConflictResolveWorkflowExecution called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceConflictResolveWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceConflictResolveWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceConflictResolveWorkflowExecutionScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) CreateFailoverMarkerTasks(ctx context.Context, request *persistence.CreateFailoverMarkersRequest) (err error) { op := func() error { err = c.wrapped.CreateFailoverMarkerTasks(ctx, request) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence CreateFailoverMarkerTasks called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceCreateFailoverMarkerTasksScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceCreateFailoverMarkerTasksScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceCreateFailoverMarkerTasksScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) CreateWorkflowExecution(ctx context.Context, request *persistence.CreateWorkflowExecutionRequest) (cp1 *persistence.CreateWorkflowExecutionResponse, err error) { op := func() error { cp1, err = c.wrapped.CreateWorkflowExecution(ctx, request) c.emptyMetric("ExecutionManager.CreateWorkflowExecution", request, cp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence CreateWorkflowExecution called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceCreateWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceCreateWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceCreateWorkflowExecutionScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) DeleteActiveClusterSelectionPolicy(ctx context.Context, domainID string, workflowID string, runID string) (err error) { op := func() error { err = c.wrapped.DeleteActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(domainID); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(domainID)...) c.logger.SampleInfo("Persistence DeleteActiveClusterSelectionPolicy called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceDeleteActiveClusterSelectionPolicyScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceDeleteActiveClusterSelectionPolicyScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceDeleteActiveClusterSelectionPolicyScope, op, append(getCustomMetricTags(domainID), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) DeleteCurrentWorkflowExecution(ctx context.Context, request *persistence.DeleteCurrentWorkflowExecutionRequest) (err error) { op := func() error { err = c.wrapped.DeleteCurrentWorkflowExecution(ctx, request) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence DeleteCurrentWorkflowExecution called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceDeleteCurrentWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceDeleteCurrentWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceDeleteCurrentWorkflowExecutionScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) DeleteReplicationTaskFromDLQ(ctx context.Context, request *persistence.DeleteReplicationTaskFromDLQRequest) (err error) { op := func() error { err = c.wrapped.DeleteReplicationTaskFromDLQ(ctx, request) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence DeleteReplicationTaskFromDLQ called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceDeleteReplicationTaskFromDLQScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceDeleteReplicationTaskFromDLQScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceDeleteReplicationTaskFromDLQScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) DeleteWorkflowExecution(ctx context.Context, request *persistence.DeleteWorkflowExecutionRequest) (err error) { op := func() error { err = c.wrapped.DeleteWorkflowExecution(ctx, request) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence DeleteWorkflowExecution called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceDeleteWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceDeleteWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceDeleteWorkflowExecutionScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) GetActiveClusterSelectionPolicy(ctx context.Context, domainID string, wfID string, rID string) (ap1 *types.ActiveClusterSelectionPolicy, err error) { op := func() error { ap1, err = c.wrapped.GetActiveClusterSelectionPolicy(ctx, domainID, wfID, rID) c.emptyMetric("ExecutionManager.GetActiveClusterSelectionPolicy", domainID, ap1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(domainID); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(domainID)...) c.logger.SampleInfo("Persistence GetActiveClusterSelectionPolicy called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceGetActiveClusterSelectionPolicyScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceGetActiveClusterSelectionPolicyScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceGetActiveClusterSelectionPolicyScope, op, append(getCustomMetricTags(domainID), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) GetCurrentExecution(ctx context.Context, request *persistence.GetCurrentExecutionRequest) (gp1 *persistence.GetCurrentExecutionResponse, err error) { op := func() error { gp1, err = c.wrapped.GetCurrentExecution(ctx, request) c.emptyMetric("ExecutionManager.GetCurrentExecution", request, gp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence GetCurrentExecution called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceGetCurrentExecutionScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceGetCurrentExecutionScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceGetCurrentExecutionScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) GetHistoryTasks(ctx context.Context, request *persistence.GetHistoryTasksRequest) (gp1 *persistence.GetHistoryTasksResponse, err error) { op := func() error { gp1, err = c.wrapped.GetHistoryTasks(ctx, request) c.emptyMetric("ExecutionManager.GetHistoryTasks", request, gp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence GetHistoryTasks called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceGetHistoryTasksScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceGetHistoryTasksScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceGetHistoryTasksScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *meteredExecutionManager) GetReplicationDLQSize(ctx context.Context, request *persistence.GetReplicationDLQSizeRequest) (gp1 *persistence.GetReplicationDLQSizeResponse, err error) { op := func() error { gp1, err = c.wrapped.GetReplicationDLQSize(ctx, request) c.emptyMetric("ExecutionManager.GetReplicationDLQSize", request, gp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence GetReplicationDLQSize called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceGetReplicationDLQSizeScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceGetReplicationDLQSizeScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceGetReplicationDLQSizeScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) GetReplicationTasksFromDLQ(ctx context.Context, request *persistence.GetReplicationTasksFromDLQRequest) (gp1 *persistence.GetHistoryTasksResponse, err error) { op := func() error { gp1, err = c.wrapped.GetReplicationTasksFromDLQ(ctx, request) c.emptyMetric("ExecutionManager.GetReplicationTasksFromDLQ", request, gp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence GetReplicationTasksFromDLQ called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceGetReplicationTasksFromDLQScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceGetReplicationTasksFromDLQScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceGetReplicationTasksFromDLQScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) GetShardID() (i1 int) { return c.wrapped.GetShardID() } func (c *meteredExecutionManager) GetWorkflowExecution(ctx context.Context, request *persistence.GetWorkflowExecutionRequest) (gp1 *persistence.GetWorkflowExecutionResponse, err error) { op := func() error { gp1, err = c.wrapped.GetWorkflowExecution(ctx, request) c.emptyMetric("ExecutionManager.GetWorkflowExecution", request, gp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence GetWorkflowExecution called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceGetWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceGetWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceGetWorkflowExecutionScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) IsWorkflowExecutionExists(ctx context.Context, request *persistence.IsWorkflowExecutionExistsRequest) (ip1 *persistence.IsWorkflowExecutionExistsResponse, err error) { op := func() error { ip1, err = c.wrapped.IsWorkflowExecutionExists(ctx, request) c.emptyMetric("ExecutionManager.IsWorkflowExecutionExists", request, ip1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence IsWorkflowExecutionExists called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceIsWorkflowExecutionExistsScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceIsWorkflowExecutionExistsScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceIsWorkflowExecutionExistsScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) ListConcreteExecutions(ctx context.Context, request *persistence.ListConcreteExecutionsRequest) (lp1 *persistence.ListConcreteExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListConcreteExecutions(ctx, request) c.emptyMetric("ExecutionManager.ListConcreteExecutions", request, lp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence ListConcreteExecutions called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceListConcreteExecutionsScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceListConcreteExecutionsScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceListConcreteExecutionsScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) ListCurrentExecutions(ctx context.Context, request *persistence.ListCurrentExecutionsRequest) (lp1 *persistence.ListCurrentExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListCurrentExecutions(ctx, request) c.emptyMetric("ExecutionManager.ListCurrentExecutions", request, lp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence ListCurrentExecutions called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceListCurrentExecutionsScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceListCurrentExecutionsScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceListCurrentExecutionsScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) PutReplicationTaskToDLQ(ctx context.Context, request *persistence.PutReplicationTaskToDLQRequest) (err error) { op := func() error { err = c.wrapped.PutReplicationTaskToDLQ(ctx, request) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence PutReplicationTaskToDLQ called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistencePutReplicationTaskToDLQScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistencePutReplicationTaskToDLQScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistencePutReplicationTaskToDLQScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) RangeCompleteHistoryTask(ctx context.Context, request *persistence.RangeCompleteHistoryTaskRequest) (rp1 *persistence.RangeCompleteHistoryTaskResponse, err error) { op := func() error { rp1, err = c.wrapped.RangeCompleteHistoryTask(ctx, request) c.emptyMetric("ExecutionManager.RangeCompleteHistoryTask", request, rp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence RangeCompleteHistoryTask called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceRangeCompleteHistoryTaskScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceRangeCompleteHistoryTaskScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceRangeCompleteHistoryTaskScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) RangeDeleteReplicationTaskFromDLQ(ctx context.Context, request *persistence.RangeDeleteReplicationTaskFromDLQRequest) (rp1 *persistence.RangeDeleteReplicationTaskFromDLQResponse, err error) { op := func() error { rp1, err = c.wrapped.RangeDeleteReplicationTaskFromDLQ(ctx, request) c.emptyMetric("ExecutionManager.RangeDeleteReplicationTaskFromDLQ", request, rp1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence RangeDeleteReplicationTaskFromDLQ called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceRangeDeleteReplicationTaskFromDLQScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceRangeDeleteReplicationTaskFromDLQScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceRangeDeleteReplicationTaskFromDLQScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } func (c *meteredExecutionManager) UpdateWorkflowExecution(ctx context.Context, request *persistence.UpdateWorkflowExecutionRequest) (up1 *persistence.UpdateWorkflowExecutionResponse, err error) { op := func() error { up1, err = c.wrapped.UpdateWorkflowExecution(ctx, request) c.emptyMetric("ExecutionManager.UpdateWorkflowExecution", request, up1, err) return err } retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest(request); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags(request)...) c.logger.SampleInfo("Persistence UpdateWorkflowExecution called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope(metrics.PersistenceUpdateWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call(metrics.PersistenceUpdateWorkflowExecutionScope, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } err = c.callWithoutDomainTag(metrics.PersistenceUpdateWorkflowExecutionScope, op, append(getCustomMetricTags(request), metrics.IsRetryTag(retryCount > 0))...) return } ================================================ FILE: common/persistence/wrappers/metered/history_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) // meteredHistoryManager implements persistence.HistoryManager interface instrumented with rate limiter. type meteredHistoryManager struct { base wrapped persistence.HistoryManager } // NewHistoryManager creates a new instance of HistoryManager with ratelimiter. func NewHistoryManager( wrapped persistence.HistoryManager, metricClient metrics.Client, logger log.Logger, cfg *config.Persistence, ) persistence.HistoryManager { return &meteredHistoryManager{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger, enableLatencyHistogramMetrics: cfg.EnablePersistenceLatencyHistogramMetrics, }, } } func (c *meteredHistoryManager) AppendHistoryNodes(ctx context.Context, request *persistence.AppendHistoryNodesRequest) (ap1 *persistence.AppendHistoryNodesResponse, err error) { op := func() error { ap1, err = c.wrapped.AppendHistoryNodes(ctx, request) c.emptyMetric("HistoryManager.AppendHistoryNodes", request, ap1, err) return err } err = c.call(metrics.PersistenceAppendHistoryNodesScope, op, getCustomMetricTags(request)...) return } func (c *meteredHistoryManager) Close() { c.wrapped.Close() return } func (c *meteredHistoryManager) DeleteHistoryBranch(ctx context.Context, request *persistence.DeleteHistoryBranchRequest) (err error) { op := func() error { err = c.wrapped.DeleteHistoryBranch(ctx, request) c.emptyMetric("HistoryManager.DeleteHistoryBranch", request, err, err) return err } err = c.call(metrics.PersistenceDeleteHistoryBranchScope, op, getCustomMetricTags(request)...) return } func (c *meteredHistoryManager) ForkHistoryBranch(ctx context.Context, request *persistence.ForkHistoryBranchRequest) (fp1 *persistence.ForkHistoryBranchResponse, err error) { op := func() error { fp1, err = c.wrapped.ForkHistoryBranch(ctx, request) c.emptyMetric("HistoryManager.ForkHistoryBranch", request, fp1, err) return err } err = c.call(metrics.PersistenceForkHistoryBranchScope, op, getCustomMetricTags(request)...) return } func (c *meteredHistoryManager) GetAllHistoryTreeBranches(ctx context.Context, request *persistence.GetAllHistoryTreeBranchesRequest) (gp1 *persistence.GetAllHistoryTreeBranchesResponse, err error) { op := func() error { gp1, err = c.wrapped.GetAllHistoryTreeBranches(ctx, request) c.emptyMetric("HistoryManager.GetAllHistoryTreeBranches", request, gp1, err) return err } err = c.call(metrics.PersistenceGetAllHistoryTreeBranchesScope, op, getCustomMetricTags(request)...) return } func (c *meteredHistoryManager) GetHistoryTree(ctx context.Context, request *persistence.GetHistoryTreeRequest) (gp1 *persistence.GetHistoryTreeResponse, err error) { op := func() error { gp1, err = c.wrapped.GetHistoryTree(ctx, request) c.emptyMetric("HistoryManager.GetHistoryTree", request, gp1, err) return err } err = c.call(metrics.PersistenceGetHistoryTreeScope, op, getCustomMetricTags(request)...) return } func (c *meteredHistoryManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *meteredHistoryManager) ReadHistoryBranch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (rp1 *persistence.ReadHistoryBranchResponse, err error) { op := func() error { rp1, err = c.wrapped.ReadHistoryBranch(ctx, request) c.emptyMetric("HistoryManager.ReadHistoryBranch", request, rp1, err) return err } err = c.call(metrics.PersistenceReadHistoryBranchScope, op, getCustomMetricTags(request)...) return } func (c *meteredHistoryManager) ReadHistoryBranchByBatch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (rp1 *persistence.ReadHistoryBranchByBatchResponse, err error) { op := func() error { rp1, err = c.wrapped.ReadHistoryBranchByBatch(ctx, request) c.emptyMetric("HistoryManager.ReadHistoryBranchByBatch", request, rp1, err) return err } err = c.call(metrics.PersistenceReadHistoryBranchByBatchScope, op, getCustomMetricTags(request)...) return } func (c *meteredHistoryManager) ReadRawHistoryBranch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (rp1 *persistence.ReadRawHistoryBranchResponse, err error) { op := func() error { rp1, err = c.wrapped.ReadRawHistoryBranch(ctx, request) c.emptyMetric("HistoryManager.ReadRawHistoryBranch", request, rp1, err) return err } err = c.call(metrics.PersistenceReadRawHistoryBranchScope, op, getCustomMetricTags(request)...) return } ================================================ FILE: common/persistence/wrappers/metered/metered_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metered import ( "context" "errors" "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) var _staticMethods = map[string]bool{ "Close": true, "GetName": true, "GetShardID": true, } func TestWrappersAgainstPreviousImplementation(t *testing.T) { for _, tc := range []struct { name string prepareMock func(t *testing.T, ctrl *gomock.Controller, newMetricsClient metrics.Client, newLogger log.Logger) (newManager any, mocked any) }{ { name: "ConfigStoreManager", prepareMock: func(t *testing.T, ctrl *gomock.Controller, newMetricsClient metrics.Client, newLogger log.Logger) (newManager any, mocked any) { wrapped := persistence.NewMockConfigStoreManager(ctrl) newObj := NewConfigStoreManager(wrapped, newMetricsClient, newLogger, &config.Persistence{EnablePersistenceLatencyHistogramMetrics: true}) return newObj, wrapped }, }, { name: "DomainManager", prepareMock: func(t *testing.T, ctrl *gomock.Controller, newMetricsClient metrics.Client, newLogger log.Logger) (newManager any, mocked any) { wrapped := persistence.NewMockDomainManager(ctrl) newObj := NewDomainManager(wrapped, newMetricsClient, newLogger, &config.Persistence{EnablePersistenceLatencyHistogramMetrics: true}) return newObj, wrapped }, }, { name: "HistoryManager", prepareMock: func(t *testing.T, ctrl *gomock.Controller, newMetricsClient metrics.Client, newLogger log.Logger) (newManager any, mocked any) { wrapped := persistence.NewMockHistoryManager(ctrl) newObj := NewHistoryManager(wrapped, newMetricsClient, newLogger, &config.Persistence{EnablePersistenceLatencyHistogramMetrics: true}) return newObj, wrapped }, }, { name: "QueueManager", prepareMock: func(t *testing.T, ctrl *gomock.Controller, newMetricsClient metrics.Client, newLogger log.Logger) (newManager any, mocked any) { wrapped := persistence.NewMockQueueManager(ctrl) newObj := NewQueueManager(wrapped, newMetricsClient, newLogger, &config.Persistence{EnablePersistenceLatencyHistogramMetrics: true}) return newObj, wrapped }, }, { name: "ShardManager", prepareMock: func(t *testing.T, ctrl *gomock.Controller, newMetricsClient metrics.Client, newLogger log.Logger) (newManager any, mocked any) { wrapped := persistence.NewMockShardManager(ctrl) newObj := NewShardManager(wrapped, newMetricsClient, newLogger, &config.Persistence{EnablePersistenceLatencyHistogramMetrics: true}) return newObj, wrapped }, }, { name: "TaskManager", prepareMock: func(t *testing.T, ctrl *gomock.Controller, newMetricsClient metrics.Client, newLogger log.Logger) (newManager any, mocked any) { wrapped := persistence.NewMockTaskManager(ctrl) newObj := NewTaskManager(wrapped, newMetricsClient, newLogger, &config.Persistence{EnablePersistenceLatencyHistogramMetrics: true}) return newObj, wrapped }, }, { name: "VisibilityManager", prepareMock: func(t *testing.T, ctrl *gomock.Controller, newMetricsClient metrics.Client, newLogger log.Logger) (newManager any, mocked any) { wrapped := persistence.NewMockVisibilityManager(ctrl) newObj := NewVisibilityManager(wrapped, newMetricsClient, newLogger, &config.Persistence{EnablePersistenceLatencyHistogramMetrics: true}) return newObj, wrapped }, }, { name: "ExecutionManager", prepareMock: func(t *testing.T, ctrl *gomock.Controller, newMetricsClient metrics.Client, newLogger log.Logger) (newManager any, mocked any) { wrapped := persistence.NewMockExecutionManager(ctrl) wrapped.EXPECT().GetShardID().Return(0).AnyTimes() newObj := NewExecutionManager(wrapped, newMetricsClient, newLogger, &config.Persistence{EnablePersistenceLatencyHistogramMetrics: true}, dynamicproperties.GetIntPropertyFn(1), dynamicproperties.GetBoolPropertyFn(true)) return newObj, wrapped }, }, } { t.Run(tc.name, func(t *testing.T) { t.Run("without error", func(t *testing.T) { ctrl := gomock.NewController(t) zapLogger, logs := setupLogsCapture() metricScope := tally.NewTestScope("", nil) metricsClient := metrics.NewClient(metricScope, metrics.ServiceIdx(0), metrics.HistogramMigration{}) logger := log.NewLogger(zapLogger) wrapper, mocked := tc.prepareMock(t, ctrl, metricsClient, logger) prepareMockForTest(t, mocked, nil) runScenario(t, wrapper, logs, metricScope) }) t.Run("with error", func(t *testing.T) { for _, errorType := range []error{ &types.DomainAlreadyExistsError{}, &types.BadRequestError{}, &types.EntityNotExistsError{}, &types.ServiceBusyError{}, &persistence.WorkflowExecutionAlreadyStartedError{}, &persistence.ConditionFailedError{}, &persistence.CurrentWorkflowConditionFailedError{}, &persistence.ShardAlreadyExistError{}, &persistence.ShardOwnershipLostError{}, &persistence.DuplicateRequestError{}, &persistence.TimeoutError{}, &persistence.DBUnavailableError{}, errors.New("persistence error"), } { t.Run(reflect.TypeOf(errorType).String(), func(t *testing.T) { ctrl := gomock.NewController(t) zapLogger, logs := setupLogsCapture() metricScope := tally.NewTestScope("", nil) metricsClient := metrics.NewClient(metricScope, metrics.ServiceIdx(0), metrics.HistogramMigration{}) logger := log.NewLogger(zapLogger) newObj, mocked := tc.prepareMock(t, ctrl, metricsClient, logger) prepareMockForTest(t, mocked, errorType) runScenario(t, newObj, logs, metricScope) }) } }) }) } } func prepareMockForTest(t *testing.T, input interface{}, expectedErr error) { switch mocked := input.(type) { case *persistence.MockConfigStoreManager: mocked.EXPECT().UpdateDynamicConfig(gomock.Any(), gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().FetchDynamicConfig(gomock.Any(), gomock.Any()).Return(&persistence.FetchDynamicConfigResponse{}, expectedErr).Times(1) case *persistence.MockDomainManager: mocked.EXPECT().CreateDomain(gomock.Any(), gomock.Any()).Return(&persistence.CreateDomainResponse{}, expectedErr).Times(1) mocked.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(&persistence.GetDomainResponse{}, expectedErr).Times(1) mocked.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().DeleteDomain(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().DeleteDomainByName(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(&persistence.ListDomainsResponse{}, expectedErr).Times(1) mocked.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{}, expectedErr).Times(1) case *persistence.MockHistoryManager: mocked.EXPECT().AppendHistoryNodes(gomock.Any(), gomock.Any()).Return(&persistence.AppendHistoryNodesResponse{}, expectedErr).Times(1) mocked.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchResponse{}, expectedErr).Times(1) mocked.EXPECT().ReadHistoryBranchByBatch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchByBatchResponse{}, expectedErr).Times(1) mocked.EXPECT().ReadRawHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadRawHistoryBranchResponse{}, expectedErr).Times(1) mocked.EXPECT().ForkHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ForkHistoryBranchResponse{}, expectedErr).Times(1) mocked.EXPECT().DeleteHistoryBranch(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().GetHistoryTree(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTreeResponse{}, expectedErr).Times(1) mocked.EXPECT().GetAllHistoryTreeBranches(gomock.Any(), gomock.Any()).Return(&persistence.GetAllHistoryTreeBranchesResponse{}, expectedErr).Times(1) case *persistence.MockQueueManager: mocked.EXPECT().EnqueueMessage(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().ReadMessages(gomock.Any(), gomock.Any()).Return(&persistence.ReadMessagesResponse{Messages: []*persistence.QueueMessage{}}, expectedErr).Times(1) mocked.EXPECT().UpdateAckLevel(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().GetAckLevels(gomock.Any(), gomock.Any()).Return(&persistence.GetAckLevelsResponse{AckLevels: map[string]int64{}}, expectedErr).Times(1) mocked.EXPECT().DeleteMessagesBefore(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().DeleteMessageFromDLQ(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().EnqueueMessageToDLQ(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().GetDLQAckLevels(gomock.Any(), gomock.Any()).Return(&persistence.GetDLQAckLevelsResponse{AckLevels: map[string]int64{}}, expectedErr).Times(1) mocked.EXPECT().GetDLQSize(gomock.Any(), gomock.Any()).Return(&persistence.GetDLQSizeResponse{Size: 0}, expectedErr).Times(1) mocked.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().ReadMessagesFromDLQ(gomock.Any(), gomock.Any()).Return(&persistence.ReadMessagesFromDLQResponse{Messages: []*persistence.QueueMessage{}}, expectedErr).Times(1) mocked.EXPECT().UpdateDLQAckLevel(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) case *persistence.MockShardManager: mocked.EXPECT().GetShard(gomock.Any(), gomock.Any()).Return(&persistence.GetShardResponse{}, expectedErr).Times(1) mocked.EXPECT().UpdateShard(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().CreateShard(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) case *persistence.MockTaskManager: mocked.EXPECT().CompleteTasksLessThan(gomock.Any(), gomock.Any()).Return(&persistence.CompleteTasksLessThanResponse{}, expectedErr).Times(1) mocked.EXPECT().CompleteTask(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().CreateTasks(gomock.Any(), gomock.Any()).Return(&persistence.CreateTasksResponse{}, expectedErr).Times(1) mocked.EXPECT().DeleteTaskList(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().GetTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetTasksResponse{}, expectedErr).Times(1) mocked.EXPECT().GetOrphanTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetOrphanTasksResponse{}, expectedErr).Times(1) mocked.EXPECT().GetTaskListSize(gomock.Any(), gomock.Any()).Return(&persistence.GetTaskListSizeResponse{}, expectedErr).Times(1) mocked.EXPECT().LeaseTaskList(gomock.Any(), gomock.Any()).Return(&persistence.LeaseTaskListResponse{}, expectedErr).Times(1) mocked.EXPECT().GetTaskList(gomock.Any(), gomock.Any()).Return(&persistence.GetTaskListResponse{}, expectedErr).Times(1) mocked.EXPECT().ListTaskList(gomock.Any(), gomock.Any()).Return(&persistence.ListTaskListResponse{}, expectedErr).Times(1) mocked.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any()).Return(&persistence.UpdateTaskListResponse{}, expectedErr).Times(1) case *persistence.MockVisibilityManager: mocked.EXPECT().DeleteUninitializedWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.CountWorkflowExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.GetClosedWorkflowExecutionResponse{}, expectedErr).Times(1) mocked.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().RecordWorkflowExecutionUninitialized(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().UpsertWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr).Times(1) case *persistence.MockExecutionManager: mocked.EXPECT().CreateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.CreateWorkflowExecutionResponse{}, expectedErr).Times(1) mocked.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.GetWorkflowExecutionResponse{}, expectedErr).Times(1) mocked.EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.UpdateWorkflowExecutionResponse{}, expectedErr).Times(1) mocked.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().DeleteCurrentWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().GetCurrentExecution(gomock.Any(), gomock.Any()).Return(&persistence.GetCurrentExecutionResponse{}, expectedErr).Times(1) mocked.EXPECT().ConflictResolveWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.ConflictResolveWorkflowExecutionResponse{}, expectedErr).Times(1) mocked.EXPECT().CreateFailoverMarkerTasks(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().DeleteReplicationTaskFromDLQ(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().GetReplicationDLQSize(gomock.Any(), gomock.Any()).Return(&persistence.GetReplicationDLQSizeResponse{}, expectedErr).Times(1) mocked.EXPECT().GetReplicationTasksFromDLQ(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTasksResponse{}, expectedErr).Times(1) mocked.EXPECT().IsWorkflowExecutionExists(gomock.Any(), gomock.Any()).Return(&persistence.IsWorkflowExecutionExistsResponse{}, expectedErr).Times(1) mocked.EXPECT().ListConcreteExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListConcreteExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().ListCurrentExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListCurrentExecutionsResponse{}, expectedErr).Times(1) mocked.EXPECT().PutReplicationTaskToDLQ(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTasksResponse{}, expectedErr).Times(1) mocked.EXPECT().CompleteHistoryTask(gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) mocked.EXPECT().RangeCompleteHistoryTask(gomock.Any(), gomock.Any()).Return(&persistence.RangeCompleteHistoryTaskResponse{}, expectedErr).Times(1) mocked.EXPECT().RangeDeleteReplicationTaskFromDLQ(gomock.Any(), gomock.Any()).Return(&persistence.RangeDeleteReplicationTaskFromDLQResponse{}, expectedErr).Times(1) mocked.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterSelectionPolicy{}, expectedErr).Times(1) mocked.EXPECT().DeleteActiveClusterSelectionPolicy(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(expectedErr).Times(1) default: t.Errorf("unsupported type %v", reflect.TypeOf(input)) t.FailNow() } return } func setupLogsCapture() (*zap.Logger, *observer.ObservedLogs) { core, logs := observer.New(zap.InfoLevel) return zap.New(core), logs } func runScenario(t *testing.T, newObj any, newLogs *observer.ObservedLogs, newMetrics tally.TestScope) { newV := reflect.ValueOf(newObj) infoT := reflect.TypeOf(newV.Interface()) for i := 0; i < infoT.NumMethod(); i++ { method := infoT.Method(i) if _staticMethods[method.Name] { // Skip methods that do not use error injection. continue } t.Run(method.Name, func(t *testing.T) { vals := make([]reflect.Value, 0, method.Type.NumIn()-1) // First argument is always context.Context vals = append(vals, reflect.ValueOf(context.Background())) for i := 2; i < method.Type.NumIn(); i++ { if method.Type.In(i).Kind() == reflect.Ptr { vals = append(vals, reflect.New(method.Type.In(i).Elem())) continue } vals = append(vals, reflect.Zero(method.Type.In(i))) } var newRes []reflect.Value assert.NotPanicsf(t, func() { newRes = newV.MethodByName(method.Name).Call(vals) }, "method does not have tag defined") if len(newRes) == 0 { // Empty result means that method panicked. return } }) } } ================================================ FILE: common/persistence/wrappers/metered/queue_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) // meteredQueueManager implements persistence.QueueManager interface instrumented with rate limiter. type meteredQueueManager struct { base wrapped persistence.QueueManager } // NewQueueManager creates a new instance of QueueManager with ratelimiter. func NewQueueManager( wrapped persistence.QueueManager, metricClient metrics.Client, logger log.Logger, cfg *config.Persistence, ) persistence.QueueManager { return &meteredQueueManager{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger, enableLatencyHistogramMetrics: cfg.EnablePersistenceLatencyHistogramMetrics, }, } } func (c *meteredQueueManager) Close() { c.wrapped.Close() return } func (c *meteredQueueManager) DeleteMessageFromDLQ(ctx context.Context, request *persistence.DeleteMessageFromDLQRequest) (err error) { op := func() error { err = c.wrapped.DeleteMessageFromDLQ(ctx, request) c.emptyMetric("QueueManager.DeleteMessageFromDLQ", request, err, err) return err } err = c.call(metrics.PersistenceDeleteMessageFromDLQScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) DeleteMessagesBefore(ctx context.Context, request *persistence.DeleteMessagesBeforeRequest) (err error) { op := func() error { err = c.wrapped.DeleteMessagesBefore(ctx, request) c.emptyMetric("QueueManager.DeleteMessagesBefore", request, err, err) return err } err = c.call(metrics.PersistenceDeleteMessagesBeforeScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) EnqueueMessage(ctx context.Context, request *persistence.EnqueueMessageRequest) (err error) { op := func() error { err = c.wrapped.EnqueueMessage(ctx, request) c.emptyMetric("QueueManager.EnqueueMessage", request, err, err) return err } err = c.call(metrics.PersistenceEnqueueMessageScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) EnqueueMessageToDLQ(ctx context.Context, request *persistence.EnqueueMessageToDLQRequest) (err error) { op := func() error { err = c.wrapped.EnqueueMessageToDLQ(ctx, request) c.emptyMetric("QueueManager.EnqueueMessageToDLQ", request, err, err) return err } err = c.call(metrics.PersistenceEnqueueMessageToDLQScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) GetAckLevels(ctx context.Context, request *persistence.GetAckLevelsRequest) (gp1 *persistence.GetAckLevelsResponse, err error) { op := func() error { gp1, err = c.wrapped.GetAckLevels(ctx, request) c.emptyMetric("QueueManager.GetAckLevels", request, gp1, err) return err } err = c.call(metrics.PersistenceGetAckLevelsScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) GetDLQAckLevels(ctx context.Context, request *persistence.GetDLQAckLevelsRequest) (gp1 *persistence.GetDLQAckLevelsResponse, err error) { op := func() error { gp1, err = c.wrapped.GetDLQAckLevels(ctx, request) c.emptyMetric("QueueManager.GetDLQAckLevels", request, gp1, err) return err } err = c.call(metrics.PersistenceGetDLQAckLevelsScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) GetDLQSize(ctx context.Context, request *persistence.GetDLQSizeRequest) (gp1 *persistence.GetDLQSizeResponse, err error) { op := func() error { gp1, err = c.wrapped.GetDLQSize(ctx, request) c.emptyMetric("QueueManager.GetDLQSize", request, gp1, err) return err } err = c.call(metrics.PersistenceGetDLQSizeScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) RangeDeleteMessagesFromDLQ(ctx context.Context, request *persistence.RangeDeleteMessagesFromDLQRequest) (err error) { op := func() error { err = c.wrapped.RangeDeleteMessagesFromDLQ(ctx, request) c.emptyMetric("QueueManager.RangeDeleteMessagesFromDLQ", request, err, err) return err } err = c.call(metrics.PersistenceRangeDeleteMessagesFromDLQScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) ReadMessages(ctx context.Context, request *persistence.ReadMessagesRequest) (rp1 *persistence.ReadMessagesResponse, err error) { op := func() error { rp1, err = c.wrapped.ReadMessages(ctx, request) c.emptyMetric("QueueManager.ReadMessages", request, rp1, err) return err } err = c.call(metrics.PersistenceReadMessagesScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) ReadMessagesFromDLQ(ctx context.Context, request *persistence.ReadMessagesFromDLQRequest) (rp1 *persistence.ReadMessagesFromDLQResponse, err error) { op := func() error { rp1, err = c.wrapped.ReadMessagesFromDLQ(ctx, request) c.emptyMetric("QueueManager.ReadMessagesFromDLQ", request, rp1, err) return err } err = c.call(metrics.PersistenceReadMessagesFromDLQScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) UpdateAckLevel(ctx context.Context, request *persistence.UpdateAckLevelRequest) (err error) { op := func() error { err = c.wrapped.UpdateAckLevel(ctx, request) c.emptyMetric("QueueManager.UpdateAckLevel", request, err, err) return err } err = c.call(metrics.PersistenceUpdateAckLevelScope, op, getCustomMetricTags(request)...) return } func (c *meteredQueueManager) UpdateDLQAckLevel(ctx context.Context, request *persistence.UpdateDLQAckLevelRequest) (err error) { op := func() error { err = c.wrapped.UpdateDLQAckLevel(ctx, request) c.emptyMetric("QueueManager.UpdateDLQAckLevel", request, err, err) return err } err = c.call(metrics.PersistenceUpdateDLQAckLevelScope, op, getCustomMetricTags(request)...) return } ================================================ FILE: common/persistence/wrappers/metered/shard_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) // meteredShardManager implements persistence.ShardManager interface instrumented with rate limiter. type meteredShardManager struct { base wrapped persistence.ShardManager } // NewShardManager creates a new instance of ShardManager with ratelimiter. func NewShardManager( wrapped persistence.ShardManager, metricClient metrics.Client, logger log.Logger, cfg *config.Persistence, ) persistence.ShardManager { return &meteredShardManager{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger, enableLatencyHistogramMetrics: cfg.EnablePersistenceLatencyHistogramMetrics, }, } } func (c *meteredShardManager) Close() { c.wrapped.Close() return } func (c *meteredShardManager) CreateShard(ctx context.Context, request *persistence.CreateShardRequest) (err error) { op := func() error { err = c.wrapped.CreateShard(ctx, request) c.emptyMetric("ShardManager.CreateShard", request, err, err) return err } err = c.call(metrics.PersistenceCreateShardScope, op, getCustomMetricTags(request)...) return } func (c *meteredShardManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *meteredShardManager) GetShard(ctx context.Context, request *persistence.GetShardRequest) (gp1 *persistence.GetShardResponse, err error) { op := func() error { gp1, err = c.wrapped.GetShard(ctx, request) c.emptyMetric("ShardManager.GetShard", request, gp1, err) return err } err = c.call(metrics.PersistenceGetShardScope, op, getCustomMetricTags(request)...) return } func (c *meteredShardManager) UpdateShard(ctx context.Context, request *persistence.UpdateShardRequest) (err error) { op := func() error { err = c.wrapped.UpdateShard(ctx, request) c.emptyMetric("ShardManager.UpdateShard", request, err, err) return err } err = c.call(metrics.PersistenceUpdateShardScope, op, getCustomMetricTags(request)...) return } ================================================ FILE: common/persistence/wrappers/metered/task_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) // meteredTaskManager implements persistence.TaskManager interface instrumented with rate limiter. type meteredTaskManager struct { base wrapped persistence.TaskManager } // NewTaskManager creates a new instance of TaskManager with ratelimiter. func NewTaskManager( wrapped persistence.TaskManager, metricClient metrics.Client, logger log.Logger, cfg *config.Persistence, ) persistence.TaskManager { return &meteredTaskManager{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger, enableLatencyHistogramMetrics: cfg.EnablePersistenceLatencyHistogramMetrics, }, } } func (c *meteredTaskManager) Close() { c.wrapped.Close() return } func (c *meteredTaskManager) CompleteTask(ctx context.Context, request *persistence.CompleteTaskRequest) (err error) { op := func() error { err = c.wrapped.CompleteTask(ctx, request) c.emptyMetric("TaskManager.CompleteTask", request, err, err) return err } err = c.call(metrics.PersistenceCompleteTaskScope, op, getCustomMetricTags(request)...) return } func (c *meteredTaskManager) CompleteTasksLessThan(ctx context.Context, request *persistence.CompleteTasksLessThanRequest) (cp1 *persistence.CompleteTasksLessThanResponse, err error) { op := func() error { cp1, err = c.wrapped.CompleteTasksLessThan(ctx, request) c.emptyMetric("TaskManager.CompleteTasksLessThan", request, cp1, err) return err } err = c.call(metrics.PersistenceCompleteTasksLessThanScope, op, getCustomMetricTags(request)...) return } func (c *meteredTaskManager) CreateTasks(ctx context.Context, request *persistence.CreateTasksRequest) (cp1 *persistence.CreateTasksResponse, err error) { op := func() error { cp1, err = c.wrapped.CreateTasks(ctx, request) c.emptyMetric("TaskManager.CreateTasks", request, cp1, err) return err } err = c.call(metrics.PersistenceCreateTasksScope, op, getCustomMetricTags(request)...) return } func (c *meteredTaskManager) DeleteTaskList(ctx context.Context, request *persistence.DeleteTaskListRequest) (err error) { op := func() error { err = c.wrapped.DeleteTaskList(ctx, request) c.emptyMetric("TaskManager.DeleteTaskList", request, err, err) return err } err = c.call(metrics.PersistenceDeleteTaskListScope, op, getCustomMetricTags(request)...) return } func (c *meteredTaskManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *meteredTaskManager) GetOrphanTasks(ctx context.Context, request *persistence.GetOrphanTasksRequest) (gp1 *persistence.GetOrphanTasksResponse, err error) { op := func() error { gp1, err = c.wrapped.GetOrphanTasks(ctx, request) c.emptyMetric("TaskManager.GetOrphanTasks", request, gp1, err) return err } err = c.call(metrics.PersistenceGetOrphanTasksScope, op, getCustomMetricTags(request)...) return } func (c *meteredTaskManager) GetTaskList(ctx context.Context, request *persistence.GetTaskListRequest) (gp1 *persistence.GetTaskListResponse, err error) { op := func() error { gp1, err = c.wrapped.GetTaskList(ctx, request) c.emptyMetric("TaskManager.GetTaskList", request, gp1, err) return err } err = c.call(metrics.PersistenceGetTaskListScope, op, getCustomMetricTags(request)...) return } func (c *meteredTaskManager) GetTaskListSize(ctx context.Context, request *persistence.GetTaskListSizeRequest) (gp1 *persistence.GetTaskListSizeResponse, err error) { op := func() error { gp1, err = c.wrapped.GetTaskListSize(ctx, request) c.emptyMetric("TaskManager.GetTaskListSize", request, gp1, err) return err } err = c.call(metrics.PersistenceGetTaskListSizeScope, op, getCustomMetricTags(request)...) return } func (c *meteredTaskManager) GetTasks(ctx context.Context, request *persistence.GetTasksRequest) (gp1 *persistence.GetTasksResponse, err error) { op := func() error { gp1, err = c.wrapped.GetTasks(ctx, request) c.emptyMetric("TaskManager.GetTasks", request, gp1, err) return err } err = c.call(metrics.PersistenceGetTasksScope, op, getCustomMetricTags(request)...) return } func (c *meteredTaskManager) LeaseTaskList(ctx context.Context, request *persistence.LeaseTaskListRequest) (lp1 *persistence.LeaseTaskListResponse, err error) { op := func() error { lp1, err = c.wrapped.LeaseTaskList(ctx, request) c.emptyMetric("TaskManager.LeaseTaskList", request, lp1, err) return err } err = c.call(metrics.PersistenceLeaseTaskListScope, op, getCustomMetricTags(request)...) return } func (c *meteredTaskManager) ListTaskList(ctx context.Context, request *persistence.ListTaskListRequest) (lp1 *persistence.ListTaskListResponse, err error) { op := func() error { lp1, err = c.wrapped.ListTaskList(ctx, request) c.emptyMetric("TaskManager.ListTaskList", request, lp1, err) return err } err = c.call(metrics.PersistenceListTaskListScope, op, getCustomMetricTags(request)...) return } func (c *meteredTaskManager) UpdateTaskList(ctx context.Context, request *persistence.UpdateTaskListRequest) (up1 *persistence.UpdateTaskListResponse, err error) { op := func() error { up1, err = c.wrapped.UpdateTaskList(ctx, request) c.emptyMetric("TaskManager.UpdateTaskList", request, up1, err) return err } err = c.call(metrics.PersistenceUpdateTaskListScope, op, getCustomMetricTags(request)...) return } ================================================ FILE: common/persistence/wrappers/metered/visibility_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) // meteredVisibilityManager implements persistence.VisibilityManager interface instrumented with rate limiter. type meteredVisibilityManager struct { base wrapped persistence.VisibilityManager } // NewVisibilityManager creates a new instance of VisibilityManager with ratelimiter. func NewVisibilityManager( wrapped persistence.VisibilityManager, metricClient metrics.Client, logger log.Logger, cfg *config.Persistence, ) persistence.VisibilityManager { return &meteredVisibilityManager{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger, enableLatencyHistogramMetrics: cfg.EnablePersistenceLatencyHistogramMetrics, }, } } func (c *meteredVisibilityManager) Close() { c.wrapped.Close() return } func (c *meteredVisibilityManager) CountWorkflowExecutions(ctx context.Context, request *persistence.CountWorkflowExecutionsRequest) (cp1 *persistence.CountWorkflowExecutionsResponse, err error) { op := func() error { cp1, err = c.wrapped.CountWorkflowExecutions(ctx, request) c.emptyMetric("VisibilityManager.CountWorkflowExecutions", request, cp1, err) return err } err = c.call(metrics.PersistenceCountWorkflowExecutionsScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) DeleteUninitializedWorkflowExecution(ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest) (err error) { op := func() error { err = c.wrapped.DeleteUninitializedWorkflowExecution(ctx, request) c.emptyMetric("VisibilityManager.DeleteUninitializedWorkflowExecution", request, err, err) return err } err = c.call(metrics.PersistenceDeleteUninitializedWorkflowExecutionScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) DeleteWorkflowExecution(ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest) (err error) { op := func() error { err = c.wrapped.DeleteWorkflowExecution(ctx, request) c.emptyMetric("VisibilityManager.DeleteWorkflowExecution", request, err, err) return err } err = c.call(metrics.PersistenceVisibilityDeleteWorkflowExecutionScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) GetClosedWorkflowExecution(ctx context.Context, request *persistence.GetClosedWorkflowExecutionRequest) (gp1 *persistence.GetClosedWorkflowExecutionResponse, err error) { op := func() error { gp1, err = c.wrapped.GetClosedWorkflowExecution(ctx, request) c.emptyMetric("VisibilityManager.GetClosedWorkflowExecution", request, gp1, err) return err } err = c.call(metrics.PersistenceGetClosedWorkflowExecutionScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *meteredVisibilityManager) ListClosedWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListClosedWorkflowExecutions(ctx, request) c.emptyMetric("VisibilityManager.ListClosedWorkflowExecutions", request, lp1, err) return err } err = c.call(metrics.PersistenceListClosedWorkflowExecutionsScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) ListClosedWorkflowExecutionsByStatus(ctx context.Context, request *persistence.ListClosedWorkflowExecutionsByStatusRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListClosedWorkflowExecutionsByStatus(ctx, request) c.emptyMetric("VisibilityManager.ListClosedWorkflowExecutionsByStatus", request, lp1, err) return err } err = c.call(metrics.PersistenceListClosedWorkflowExecutionsByStatusScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) ListClosedWorkflowExecutionsByType(ctx context.Context, request *persistence.ListWorkflowExecutionsByTypeRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListClosedWorkflowExecutionsByType(ctx, request) c.emptyMetric("VisibilityManager.ListClosedWorkflowExecutionsByType", request, lp1, err) return err } err = c.call(metrics.PersistenceListClosedWorkflowExecutionsByTypeScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) ListClosedWorkflowExecutionsByWorkflowID(ctx context.Context, request *persistence.ListWorkflowExecutionsByWorkflowIDRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) c.emptyMetric("VisibilityManager.ListClosedWorkflowExecutionsByWorkflowID", request, lp1, err) return err } err = c.call(metrics.PersistenceListClosedWorkflowExecutionsByWorkflowIDScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) ListOpenWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListOpenWorkflowExecutions(ctx, request) c.emptyMetric("VisibilityManager.ListOpenWorkflowExecutions", request, lp1, err) return err } err = c.call(metrics.PersistenceListOpenWorkflowExecutionsScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) ListOpenWorkflowExecutionsByType(ctx context.Context, request *persistence.ListWorkflowExecutionsByTypeRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListOpenWorkflowExecutionsByType(ctx, request) c.emptyMetric("VisibilityManager.ListOpenWorkflowExecutionsByType", request, lp1, err) return err } err = c.call(metrics.PersistenceListOpenWorkflowExecutionsByTypeScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) ListOpenWorkflowExecutionsByWorkflowID(ctx context.Context, request *persistence.ListWorkflowExecutionsByWorkflowIDRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) c.emptyMetric("VisibilityManager.ListOpenWorkflowExecutionsByWorkflowID", request, lp1, err) return err } err = c.call(metrics.PersistenceListOpenWorkflowExecutionsByWorkflowIDScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) ListWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsByQueryRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ListWorkflowExecutions(ctx, request) c.emptyMetric("VisibilityManager.ListWorkflowExecutions", request, lp1, err) return err } err = c.call(metrics.PersistenceListWorkflowExecutionsScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) RecordWorkflowExecutionClosed(ctx context.Context, request *persistence.RecordWorkflowExecutionClosedRequest) (err error) { op := func() error { err = c.wrapped.RecordWorkflowExecutionClosed(ctx, request) c.emptyMetric("VisibilityManager.RecordWorkflowExecutionClosed", request, err, err) return err } err = c.call(metrics.PersistenceRecordWorkflowExecutionClosedScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) RecordWorkflowExecutionStarted(ctx context.Context, request *persistence.RecordWorkflowExecutionStartedRequest) (err error) { op := func() error { err = c.wrapped.RecordWorkflowExecutionStarted(ctx, request) c.emptyMetric("VisibilityManager.RecordWorkflowExecutionStarted", request, err, err) return err } err = c.call(metrics.PersistenceRecordWorkflowExecutionStartedScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) RecordWorkflowExecutionUninitialized(ctx context.Context, request *persistence.RecordWorkflowExecutionUninitializedRequest) (err error) { op := func() error { err = c.wrapped.RecordWorkflowExecutionUninitialized(ctx, request) c.emptyMetric("VisibilityManager.RecordWorkflowExecutionUninitialized", request, err, err) return err } err = c.call(metrics.PersistenceRecordWorkflowExecutionUninitializedScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) ScanWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsByQueryRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { op := func() error { lp1, err = c.wrapped.ScanWorkflowExecutions(ctx, request) c.emptyMetric("VisibilityManager.ScanWorkflowExecutions", request, lp1, err) return err } err = c.call(metrics.PersistenceScanWorkflowExecutionsScope, op, getCustomMetricTags(request)...) return } func (c *meteredVisibilityManager) UpsertWorkflowExecution(ctx context.Context, request *persistence.UpsertWorkflowExecutionRequest) (err error) { op := func() error { err = c.wrapped.UpsertWorkflowExecution(ctx, request) c.emptyMetric("VisibilityManager.UpsertWorkflowExecution", request, err, err) return err } err = c.call(metrics.PersistenceUpsertWorkflowExecutionScope, op, getCustomMetricTags(request)...) return } ================================================ FILE: common/persistence/wrappers/ratelimited/configstore_generated.go ================================================ package ratelimited // Code generated by gowrap. DO NOT EDIT. // template: ../templates/ratelimited.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" ) // ratelimitedConfigStoreManager implements persistence.ConfigStoreManager interface instrumented with rate limiter. type ratelimitedConfigStoreManager struct { wrapped persistence.ConfigStoreManager rateLimiter quotas.Limiter callerBypass quotas.CallerBypass } // NewConfigStoreManager creates a new instance of ConfigStoreManager with ratelimiter. func NewConfigStoreManager( wrapped persistence.ConfigStoreManager, rateLimiter quotas.Limiter, callerBypass quotas.CallerBypass, ) persistence.ConfigStoreManager { return &ratelimitedConfigStoreManager{ wrapped: wrapped, rateLimiter: rateLimiter, callerBypass: callerBypass, } } func (c *ratelimitedConfigStoreManager) Close() { c.wrapped.Close() return } func (c *ratelimitedConfigStoreManager) FetchDynamicConfig(ctx context.Context, cfgType persistence.ConfigType) (fp1 *persistence.FetchDynamicConfigResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.FetchDynamicConfig(ctx, cfgType) } func (c *ratelimitedConfigStoreManager) UpdateDynamicConfig(ctx context.Context, request *persistence.UpdateDynamicConfigRequest, cfgType persistence.ConfigType) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.UpdateDynamicConfig(ctx, request, cfgType) } ================================================ FILE: common/persistence/wrappers/ratelimited/domain_generated.go ================================================ package ratelimited // Code generated by gowrap. DO NOT EDIT. // template: ../templates/ratelimited.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" ) // ratelimitedDomainManager implements persistence.DomainManager interface instrumented with rate limiter. type ratelimitedDomainManager struct { wrapped persistence.DomainManager rateLimiter quotas.Limiter callerBypass quotas.CallerBypass } // NewDomainManager creates a new instance of DomainManager with ratelimiter. func NewDomainManager( wrapped persistence.DomainManager, rateLimiter quotas.Limiter, callerBypass quotas.CallerBypass, ) persistence.DomainManager { return &ratelimitedDomainManager{ wrapped: wrapped, rateLimiter: rateLimiter, callerBypass: callerBypass, } } func (c *ratelimitedDomainManager) Close() { c.wrapped.Close() return } func (c *ratelimitedDomainManager) CreateDomain(ctx context.Context, request *persistence.CreateDomainRequest) (cp1 *persistence.CreateDomainResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.CreateDomain(ctx, request) } func (c *ratelimitedDomainManager) DeleteDomain(ctx context.Context, request *persistence.DeleteDomainRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteDomain(ctx, request) } func (c *ratelimitedDomainManager) DeleteDomainByName(ctx context.Context, request *persistence.DeleteDomainByNameRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteDomainByName(ctx, request) } func (c *ratelimitedDomainManager) GetDomain(ctx context.Context, request *persistence.GetDomainRequest) (gp1 *persistence.GetDomainResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetDomain(ctx, request) } func (c *ratelimitedDomainManager) GetMetadata(ctx context.Context) (gp1 *persistence.GetMetadataResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetMetadata(ctx) } func (c *ratelimitedDomainManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *ratelimitedDomainManager) ListDomains(ctx context.Context, request *persistence.ListDomainsRequest) (lp1 *persistence.ListDomainsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListDomains(ctx, request) } func (c *ratelimitedDomainManager) UpdateDomain(ctx context.Context, request *persistence.UpdateDomainRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.UpdateDomain(ctx, request) } ================================================ FILE: common/persistence/wrappers/ratelimited/errors.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ratelimited import ( "github.com/uber/cadence/common/types" ) var ( // ErrPersistenceLimitExceeded is the error indicating QPS limit reached. ErrPersistenceLimitExceeded = &types.ServiceBusyError{Message: "Persistence Max QPS Reached."} ) ================================================ FILE: common/persistence/wrappers/ratelimited/execution_generated.go ================================================ package ratelimited // Code generated by gowrap. DO NOT EDIT. // template: ../templates/ratelimited.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" ) // ratelimitedExecutionManager implements persistence.ExecutionManager interface instrumented with rate limiter. type ratelimitedExecutionManager struct { wrapped persistence.ExecutionManager rateLimiter quotas.Limiter callerBypass quotas.CallerBypass } // NewExecutionManager creates a new instance of ExecutionManager with ratelimiter. func NewExecutionManager( wrapped persistence.ExecutionManager, rateLimiter quotas.Limiter, callerBypass quotas.CallerBypass, ) persistence.ExecutionManager { return &ratelimitedExecutionManager{ wrapped: wrapped, rateLimiter: rateLimiter, callerBypass: callerBypass, } } func (c *ratelimitedExecutionManager) Close() { c.wrapped.Close() return } func (c *ratelimitedExecutionManager) CompleteHistoryTask(ctx context.Context, request *persistence.CompleteHistoryTaskRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.CompleteHistoryTask(ctx, request) } func (c *ratelimitedExecutionManager) ConflictResolveWorkflowExecution(ctx context.Context, request *persistence.ConflictResolveWorkflowExecutionRequest) (cp1 *persistence.ConflictResolveWorkflowExecutionResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ConflictResolveWorkflowExecution(ctx, request) } func (c *ratelimitedExecutionManager) CreateFailoverMarkerTasks(ctx context.Context, request *persistence.CreateFailoverMarkersRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.CreateFailoverMarkerTasks(ctx, request) } func (c *ratelimitedExecutionManager) CreateWorkflowExecution(ctx context.Context, request *persistence.CreateWorkflowExecutionRequest) (cp1 *persistence.CreateWorkflowExecutionResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.CreateWorkflowExecution(ctx, request) } func (c *ratelimitedExecutionManager) DeleteActiveClusterSelectionPolicy(ctx context.Context, domainID string, workflowID string, runID string) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteActiveClusterSelectionPolicy(ctx, domainID, workflowID, runID) } func (c *ratelimitedExecutionManager) DeleteCurrentWorkflowExecution(ctx context.Context, request *persistence.DeleteCurrentWorkflowExecutionRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteCurrentWorkflowExecution(ctx, request) } func (c *ratelimitedExecutionManager) DeleteReplicationTaskFromDLQ(ctx context.Context, request *persistence.DeleteReplicationTaskFromDLQRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteReplicationTaskFromDLQ(ctx, request) } func (c *ratelimitedExecutionManager) DeleteWorkflowExecution(ctx context.Context, request *persistence.DeleteWorkflowExecutionRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteWorkflowExecution(ctx, request) } func (c *ratelimitedExecutionManager) GetActiveClusterSelectionPolicy(ctx context.Context, domainID string, wfID string, rID string) (ap1 *types.ActiveClusterSelectionPolicy, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetActiveClusterSelectionPolicy(ctx, domainID, wfID, rID) } func (c *ratelimitedExecutionManager) GetCurrentExecution(ctx context.Context, request *persistence.GetCurrentExecutionRequest) (gp1 *persistence.GetCurrentExecutionResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetCurrentExecution(ctx, request) } func (c *ratelimitedExecutionManager) GetHistoryTasks(ctx context.Context, request *persistence.GetHistoryTasksRequest) (gp1 *persistence.GetHistoryTasksResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetHistoryTasks(ctx, request) } func (c *ratelimitedExecutionManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *ratelimitedExecutionManager) GetReplicationDLQSize(ctx context.Context, request *persistence.GetReplicationDLQSizeRequest) (gp1 *persistence.GetReplicationDLQSizeResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetReplicationDLQSize(ctx, request) } func (c *ratelimitedExecutionManager) GetReplicationTasksFromDLQ(ctx context.Context, request *persistence.GetReplicationTasksFromDLQRequest) (gp1 *persistence.GetHistoryTasksResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetReplicationTasksFromDLQ(ctx, request) } func (c *ratelimitedExecutionManager) GetShardID() (i1 int) { return c.wrapped.GetShardID() } func (c *ratelimitedExecutionManager) GetWorkflowExecution(ctx context.Context, request *persistence.GetWorkflowExecutionRequest) (gp1 *persistence.GetWorkflowExecutionResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetWorkflowExecution(ctx, request) } func (c *ratelimitedExecutionManager) IsWorkflowExecutionExists(ctx context.Context, request *persistence.IsWorkflowExecutionExistsRequest) (ip1 *persistence.IsWorkflowExecutionExistsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.IsWorkflowExecutionExists(ctx, request) } func (c *ratelimitedExecutionManager) ListConcreteExecutions(ctx context.Context, request *persistence.ListConcreteExecutionsRequest) (lp1 *persistence.ListConcreteExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListConcreteExecutions(ctx, request) } func (c *ratelimitedExecutionManager) ListCurrentExecutions(ctx context.Context, request *persistence.ListCurrentExecutionsRequest) (lp1 *persistence.ListCurrentExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListCurrentExecutions(ctx, request) } func (c *ratelimitedExecutionManager) PutReplicationTaskToDLQ(ctx context.Context, request *persistence.PutReplicationTaskToDLQRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.PutReplicationTaskToDLQ(ctx, request) } func (c *ratelimitedExecutionManager) RangeCompleteHistoryTask(ctx context.Context, request *persistence.RangeCompleteHistoryTaskRequest) (rp1 *persistence.RangeCompleteHistoryTaskResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.RangeCompleteHistoryTask(ctx, request) } func (c *ratelimitedExecutionManager) RangeDeleteReplicationTaskFromDLQ(ctx context.Context, request *persistence.RangeDeleteReplicationTaskFromDLQRequest) (rp1 *persistence.RangeDeleteReplicationTaskFromDLQResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.RangeDeleteReplicationTaskFromDLQ(ctx, request) } func (c *ratelimitedExecutionManager) UpdateWorkflowExecution(ctx context.Context, request *persistence.UpdateWorkflowExecutionRequest) (up1 *persistence.UpdateWorkflowExecutionResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.UpdateWorkflowExecution(ctx, request) } ================================================ FILE: common/persistence/wrappers/ratelimited/history_generated.go ================================================ package ratelimited // Code generated by gowrap. DO NOT EDIT. // template: ../templates/ratelimited.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" ) // ratelimitedHistoryManager implements persistence.HistoryManager interface instrumented with rate limiter. type ratelimitedHistoryManager struct { wrapped persistence.HistoryManager rateLimiter quotas.Limiter callerBypass quotas.CallerBypass } // NewHistoryManager creates a new instance of HistoryManager with ratelimiter. func NewHistoryManager( wrapped persistence.HistoryManager, rateLimiter quotas.Limiter, callerBypass quotas.CallerBypass, ) persistence.HistoryManager { return &ratelimitedHistoryManager{ wrapped: wrapped, rateLimiter: rateLimiter, callerBypass: callerBypass, } } func (c *ratelimitedHistoryManager) AppendHistoryNodes(ctx context.Context, request *persistence.AppendHistoryNodesRequest) (ap1 *persistence.AppendHistoryNodesResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.AppendHistoryNodes(ctx, request) } func (c *ratelimitedHistoryManager) Close() { c.wrapped.Close() return } func (c *ratelimitedHistoryManager) DeleteHistoryBranch(ctx context.Context, request *persistence.DeleteHistoryBranchRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteHistoryBranch(ctx, request) } func (c *ratelimitedHistoryManager) ForkHistoryBranch(ctx context.Context, request *persistence.ForkHistoryBranchRequest) (fp1 *persistence.ForkHistoryBranchResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ForkHistoryBranch(ctx, request) } func (c *ratelimitedHistoryManager) GetAllHistoryTreeBranches(ctx context.Context, request *persistence.GetAllHistoryTreeBranchesRequest) (gp1 *persistence.GetAllHistoryTreeBranchesResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetAllHistoryTreeBranches(ctx, request) } func (c *ratelimitedHistoryManager) GetHistoryTree(ctx context.Context, request *persistence.GetHistoryTreeRequest) (gp1 *persistence.GetHistoryTreeResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetHistoryTree(ctx, request) } func (c *ratelimitedHistoryManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *ratelimitedHistoryManager) ReadHistoryBranch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (rp1 *persistence.ReadHistoryBranchResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ReadHistoryBranch(ctx, request) } func (c *ratelimitedHistoryManager) ReadHistoryBranchByBatch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (rp1 *persistence.ReadHistoryBranchByBatchResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ReadHistoryBranchByBatch(ctx, request) } func (c *ratelimitedHistoryManager) ReadRawHistoryBranch(ctx context.Context, request *persistence.ReadHistoryBranchRequest) (rp1 *persistence.ReadRawHistoryBranchResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ReadRawHistoryBranch(ctx, request) } ================================================ FILE: common/persistence/wrappers/ratelimited/queue_generated.go ================================================ package ratelimited // Code generated by gowrap. DO NOT EDIT. // template: ../templates/ratelimited.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" ) // ratelimitedQueueManager implements persistence.QueueManager interface instrumented with rate limiter. type ratelimitedQueueManager struct { wrapped persistence.QueueManager rateLimiter quotas.Limiter callerBypass quotas.CallerBypass } // NewQueueManager creates a new instance of QueueManager with ratelimiter. func NewQueueManager( wrapped persistence.QueueManager, rateLimiter quotas.Limiter, callerBypass quotas.CallerBypass, ) persistence.QueueManager { return &ratelimitedQueueManager{ wrapped: wrapped, rateLimiter: rateLimiter, callerBypass: callerBypass, } } func (c *ratelimitedQueueManager) Close() { c.wrapped.Close() return } func (c *ratelimitedQueueManager) DeleteMessageFromDLQ(ctx context.Context, request *persistence.DeleteMessageFromDLQRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteMessageFromDLQ(ctx, request) } func (c *ratelimitedQueueManager) DeleteMessagesBefore(ctx context.Context, request *persistence.DeleteMessagesBeforeRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteMessagesBefore(ctx, request) } func (c *ratelimitedQueueManager) EnqueueMessage(ctx context.Context, request *persistence.EnqueueMessageRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.EnqueueMessage(ctx, request) } func (c *ratelimitedQueueManager) EnqueueMessageToDLQ(ctx context.Context, request *persistence.EnqueueMessageToDLQRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.EnqueueMessageToDLQ(ctx, request) } func (c *ratelimitedQueueManager) GetAckLevels(ctx context.Context, request *persistence.GetAckLevelsRequest) (gp1 *persistence.GetAckLevelsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetAckLevels(ctx, request) } func (c *ratelimitedQueueManager) GetDLQAckLevels(ctx context.Context, request *persistence.GetDLQAckLevelsRequest) (gp1 *persistence.GetDLQAckLevelsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetDLQAckLevels(ctx, request) } func (c *ratelimitedQueueManager) GetDLQSize(ctx context.Context, request *persistence.GetDLQSizeRequest) (gp1 *persistence.GetDLQSizeResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetDLQSize(ctx, request) } func (c *ratelimitedQueueManager) RangeDeleteMessagesFromDLQ(ctx context.Context, request *persistence.RangeDeleteMessagesFromDLQRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.RangeDeleteMessagesFromDLQ(ctx, request) } func (c *ratelimitedQueueManager) ReadMessages(ctx context.Context, request *persistence.ReadMessagesRequest) (rp1 *persistence.ReadMessagesResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ReadMessages(ctx, request) } func (c *ratelimitedQueueManager) ReadMessagesFromDLQ(ctx context.Context, request *persistence.ReadMessagesFromDLQRequest) (rp1 *persistence.ReadMessagesFromDLQResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ReadMessagesFromDLQ(ctx, request) } func (c *ratelimitedQueueManager) UpdateAckLevel(ctx context.Context, request *persistence.UpdateAckLevelRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.UpdateAckLevel(ctx, request) } func (c *ratelimitedQueueManager) UpdateDLQAckLevel(ctx context.Context, request *persistence.UpdateDLQAckLevelRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.UpdateDLQAckLevel(ctx, request) } ================================================ FILE: common/persistence/wrappers/ratelimited/shard_generated.go ================================================ package ratelimited // Code generated by gowrap. DO NOT EDIT. // template: ../templates/ratelimited.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" ) // ratelimitedShardManager implements persistence.ShardManager interface instrumented with rate limiter. type ratelimitedShardManager struct { wrapped persistence.ShardManager rateLimiter quotas.Limiter callerBypass quotas.CallerBypass } // NewShardManager creates a new instance of ShardManager with ratelimiter. func NewShardManager( wrapped persistence.ShardManager, rateLimiter quotas.Limiter, callerBypass quotas.CallerBypass, ) persistence.ShardManager { return &ratelimitedShardManager{ wrapped: wrapped, rateLimiter: rateLimiter, callerBypass: callerBypass, } } func (c *ratelimitedShardManager) Close() { c.wrapped.Close() return } func (c *ratelimitedShardManager) CreateShard(ctx context.Context, request *persistence.CreateShardRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.CreateShard(ctx, request) } func (c *ratelimitedShardManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *ratelimitedShardManager) GetShard(ctx context.Context, request *persistence.GetShardRequest) (gp1 *persistence.GetShardResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetShard(ctx, request) } func (c *ratelimitedShardManager) UpdateShard(ctx context.Context, request *persistence.UpdateShardRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.UpdateShard(ctx, request) } ================================================ FILE: common/persistence/wrappers/ratelimited/task_generated.go ================================================ package ratelimited // Code generated by gowrap. DO NOT EDIT. // template: ../templates/ratelimited.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" ) // ratelimitedTaskManager implements persistence.TaskManager interface instrumented with rate limiter. type ratelimitedTaskManager struct { wrapped persistence.TaskManager rateLimiter quotas.Limiter callerBypass quotas.CallerBypass } // NewTaskManager creates a new instance of TaskManager with ratelimiter. func NewTaskManager( wrapped persistence.TaskManager, rateLimiter quotas.Limiter, callerBypass quotas.CallerBypass, ) persistence.TaskManager { return &ratelimitedTaskManager{ wrapped: wrapped, rateLimiter: rateLimiter, callerBypass: callerBypass, } } func (c *ratelimitedTaskManager) Close() { c.wrapped.Close() return } func (c *ratelimitedTaskManager) CompleteTask(ctx context.Context, request *persistence.CompleteTaskRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.CompleteTask(ctx, request) } func (c *ratelimitedTaskManager) CompleteTasksLessThan(ctx context.Context, request *persistence.CompleteTasksLessThanRequest) (cp1 *persistence.CompleteTasksLessThanResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.CompleteTasksLessThan(ctx, request) } func (c *ratelimitedTaskManager) CreateTasks(ctx context.Context, request *persistence.CreateTasksRequest) (cp1 *persistence.CreateTasksResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.CreateTasks(ctx, request) } func (c *ratelimitedTaskManager) DeleteTaskList(ctx context.Context, request *persistence.DeleteTaskListRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteTaskList(ctx, request) } func (c *ratelimitedTaskManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *ratelimitedTaskManager) GetOrphanTasks(ctx context.Context, request *persistence.GetOrphanTasksRequest) (gp1 *persistence.GetOrphanTasksResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetOrphanTasks(ctx, request) } func (c *ratelimitedTaskManager) GetTaskList(ctx context.Context, request *persistence.GetTaskListRequest) (gp1 *persistence.GetTaskListResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetTaskList(ctx, request) } func (c *ratelimitedTaskManager) GetTaskListSize(ctx context.Context, request *persistence.GetTaskListSizeRequest) (gp1 *persistence.GetTaskListSizeResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetTaskListSize(ctx, request) } func (c *ratelimitedTaskManager) GetTasks(ctx context.Context, request *persistence.GetTasksRequest) (gp1 *persistence.GetTasksResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetTasks(ctx, request) } func (c *ratelimitedTaskManager) LeaseTaskList(ctx context.Context, request *persistence.LeaseTaskListRequest) (lp1 *persistence.LeaseTaskListResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.LeaseTaskList(ctx, request) } func (c *ratelimitedTaskManager) ListTaskList(ctx context.Context, request *persistence.ListTaskListRequest) (lp1 *persistence.ListTaskListResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListTaskList(ctx, request) } func (c *ratelimitedTaskManager) UpdateTaskList(ctx context.Context, request *persistence.UpdateTaskListRequest) (up1 *persistence.UpdateTaskListResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.UpdateTaskList(ctx, request) } ================================================ FILE: common/persistence/wrappers/ratelimited/utils_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ratelimited import ( "context" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/quotas" ) type limiterAlwaysAllow struct{} func (l limiterAlwaysAllow) Allow() bool { return true } func (l limiterAlwaysAllow) Wait(ctx context.Context) error { return nil } func (l limiterAlwaysAllow) Reserve() clock.Reservation { return &reservationAlwaysAllow{} } func (l limiterAlwaysAllow) Limit() rate.Limit { return rate.Inf } type limiterNeverAllow struct{} func (l limiterNeverAllow) Allow() bool { return false } func (l limiterNeverAllow) Wait(ctx context.Context) error { <-ctx.Done() return ctx.Err() } func (l limiterNeverAllow) Reserve() clock.Reservation { return &reservationNeverAllow{} } func (l limiterNeverAllow) Limit() rate.Limit { return 0 } type reservationAlwaysAllow struct{} type reservationNeverAllow struct{} func (r reservationAlwaysAllow) Allow() bool { return true } func (r reservationAlwaysAllow) Used(bool) {} func (r reservationNeverAllow) Allow() bool { return false } func (r reservationNeverAllow) Used(bool) {} var _ quotas.Limiter = (*limiterAlwaysAllow)(nil) var _ quotas.Limiter = (*limiterNeverAllow)(nil) var _ clock.Reservation = (*reservationAlwaysAllow)(nil) var _ clock.Reservation = (*reservationNeverAllow)(nil) ================================================ FILE: common/persistence/wrappers/ratelimited/visibility_generated.go ================================================ package ratelimited // Code generated by gowrap. DO NOT EDIT. // template: ../templates/ratelimited.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" ) // ratelimitedVisibilityManager implements persistence.VisibilityManager interface instrumented with rate limiter. type ratelimitedVisibilityManager struct { wrapped persistence.VisibilityManager rateLimiter quotas.Limiter callerBypass quotas.CallerBypass } // NewVisibilityManager creates a new instance of VisibilityManager with ratelimiter. func NewVisibilityManager( wrapped persistence.VisibilityManager, rateLimiter quotas.Limiter, callerBypass quotas.CallerBypass, ) persistence.VisibilityManager { return &ratelimitedVisibilityManager{ wrapped: wrapped, rateLimiter: rateLimiter, callerBypass: callerBypass, } } func (c *ratelimitedVisibilityManager) Close() { c.wrapped.Close() return } func (c *ratelimitedVisibilityManager) CountWorkflowExecutions(ctx context.Context, request *persistence.CountWorkflowExecutionsRequest) (cp1 *persistence.CountWorkflowExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.CountWorkflowExecutions(ctx, request) } func (c *ratelimitedVisibilityManager) DeleteUninitializedWorkflowExecution(ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteUninitializedWorkflowExecution(ctx, request) } func (c *ratelimitedVisibilityManager) DeleteWorkflowExecution(ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.DeleteWorkflowExecution(ctx, request) } func (c *ratelimitedVisibilityManager) GetClosedWorkflowExecution(ctx context.Context, request *persistence.GetClosedWorkflowExecutionRequest) (gp1 *persistence.GetClosedWorkflowExecutionResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.GetClosedWorkflowExecution(ctx, request) } func (c *ratelimitedVisibilityManager) GetName() (s1 string) { return c.wrapped.GetName() } func (c *ratelimitedVisibilityManager) ListClosedWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListClosedWorkflowExecutions(ctx, request) } func (c *ratelimitedVisibilityManager) ListClosedWorkflowExecutionsByStatus(ctx context.Context, request *persistence.ListClosedWorkflowExecutionsByStatusRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListClosedWorkflowExecutionsByStatus(ctx, request) } func (c *ratelimitedVisibilityManager) ListClosedWorkflowExecutionsByType(ctx context.Context, request *persistence.ListWorkflowExecutionsByTypeRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListClosedWorkflowExecutionsByType(ctx, request) } func (c *ratelimitedVisibilityManager) ListClosedWorkflowExecutionsByWorkflowID(ctx context.Context, request *persistence.ListWorkflowExecutionsByWorkflowIDRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) } func (c *ratelimitedVisibilityManager) ListOpenWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListOpenWorkflowExecutions(ctx, request) } func (c *ratelimitedVisibilityManager) ListOpenWorkflowExecutionsByType(ctx context.Context, request *persistence.ListWorkflowExecutionsByTypeRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListOpenWorkflowExecutionsByType(ctx, request) } func (c *ratelimitedVisibilityManager) ListOpenWorkflowExecutionsByWorkflowID(ctx context.Context, request *persistence.ListWorkflowExecutionsByWorkflowIDRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) } func (c *ratelimitedVisibilityManager) ListWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsByQueryRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ListWorkflowExecutions(ctx, request) } func (c *ratelimitedVisibilityManager) RecordWorkflowExecutionClosed(ctx context.Context, request *persistence.RecordWorkflowExecutionClosedRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.RecordWorkflowExecutionClosed(ctx, request) } func (c *ratelimitedVisibilityManager) RecordWorkflowExecutionStarted(ctx context.Context, request *persistence.RecordWorkflowExecutionStartedRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.RecordWorkflowExecutionStarted(ctx, request) } func (c *ratelimitedVisibilityManager) RecordWorkflowExecutionUninitialized(ctx context.Context, request *persistence.RecordWorkflowExecutionUninitializedRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.RecordWorkflowExecutionUninitialized(ctx, request) } func (c *ratelimitedVisibilityManager) ScanWorkflowExecutions(ctx context.Context, request *persistence.ListWorkflowExecutionsByQueryRequest) (lp1 *persistence.ListWorkflowExecutionsResponse, err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.ScanWorkflowExecutions(ctx, request) } func (c *ratelimitedVisibilityManager) UpsertWorkflowExecution(ctx context.Context, request *persistence.UpsertWorkflowExecutionRequest) (err error) { if !c.callerBypass.AllowLimiter(ctx, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } return c.wrapped.UpsertWorkflowExecution(ctx, request) } ================================================ FILE: common/persistence/wrappers/ratelimited/wrappers_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ratelimited import ( "context" "errors" "fmt" "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" ) var _staticMethods = map[string]bool{ "Close": true, "GetName": true, "GetShardID": true, } var wrappers = []any{ &ratelimitedConfigStoreManager{}, &ratelimitedDomainManager{}, &ratelimitedHistoryManager{}, &ratelimitedQueueManager{}, &ratelimitedShardManager{}, &ratelimitedTaskManager{}, &ratelimitedVisibilityManager{}, &ratelimitedExecutionManager{}, } func TestClientsRateLimitAlwaysAllow(t *testing.T) { for _, injector := range wrappers { name := reflect.TypeOf(injector).String() t.Run(name, func(t *testing.T) { object := builderForPassThrough(t, injector, &limiterAlwaysAllow{}, true, quotas.CallerBypass{}, nil) v := reflect.ValueOf(object) infoT := reflect.TypeOf(v.Interface()) for i := 0; i < infoT.NumMethod(); i++ { method := infoT.Method(i) if _staticMethods[method.Name] { // Skip methods that do not use error injection. continue } t.Run(method.Name, func(t *testing.T) { vals := make([]reflect.Value, 0, method.Type.NumIn()-1) // First argument is always context.Context vals = append(vals, reflect.ValueOf(context.Background())) for i := 2; i < method.Type.NumIn(); i++ { vals = append(vals, reflect.Zero(method.Type.In(i))) } callRes := v.MethodByName(method.Name).Call(vals) resultErr := callRes[len(callRes)-1].Interface() assert.Nil(t, resultErr, "method %v returned error %v", method.Name, resultErr) }) } }) } } func TestClientsAlwaysRateLimited(t *testing.T) { for _, injector := range wrappers { name := reflect.TypeOf(injector).String() t.Run(name, func(t *testing.T) { object := builderForPassThrough(t, injector, &limiterNeverAllow{}, false, quotas.CallerBypass{}, nil) v := reflect.ValueOf(object) infoT := reflect.TypeOf(v.Interface()) for i := 0; i < infoT.NumMethod(); i++ { method := infoT.Method(i) if _staticMethods[method.Name] { // Skip methods that do not use error injection. continue } t.Run(method.Name, func(t *testing.T) { vals := make([]reflect.Value, 0, method.Type.NumIn()-1) // First argument is always context.Context vals = append(vals, reflect.ValueOf(context.Background())) for i := 2; i < method.Type.NumIn(); i++ { vals = append(vals, reflect.Zero(method.Type.In(i))) } callRes := v.MethodByName(method.Name).Call(vals) resultErr := callRes[len(callRes)-1].Interface() err, ok := resultErr.(error) require.True(t, ok, "method %v must return error") var expectedErr *types.ServiceBusyError assert.True(t, errors.As(err, &expectedErr), "method %v must return error of type *types.ServiceBusyError", method.Name) assert.Equal(t, ErrPersistenceLimitExceeded.Message, expectedErr.Message, "method %v returned different error, expected %v, got %v", method.Name, ErrPersistenceLimitExceeded.Message, expectedErr.Message) }) } }) } } func TestInjectorsWithUnderlyingErrors(t *testing.T) { for _, injector := range wrappers { name := reflect.TypeOf(injector).String() t.Run(name, func(t *testing.T) { expectedMethodErr := fmt.Errorf("%s: injected error", name) object := builderForPassThrough(t, injector, &limiterAlwaysAllow{}, true, quotas.CallerBypass{}, expectedMethodErr) v := reflect.ValueOf(object) infoT := reflect.TypeOf(v.Interface()) for i := 0; i < infoT.NumMethod(); i++ { method := infoT.Method(i) if _staticMethods[method.Name] { // Skip methods that do not use error injection. continue } t.Run(method.Name, func(t *testing.T) { vals := make([]reflect.Value, 0, method.Type.NumIn()-1) // First argument is always context.Context vals = append(vals, reflect.ValueOf(context.Background())) for i := 2; i < method.Type.NumIn(); i++ { vals = append(vals, reflect.Zero(method.Type.In(i))) } callRes := v.MethodByName(method.Name).Call(vals) resultErr := callRes[len(callRes)-1].Interface() err, ok := resultErr.(error) require.True(t, ok, "method %v must return error") assert.Equal(t, expectedMethodErr, err, "method %v returned different error, expected %v, got %v", method.Name, expectedMethodErr, err) }) } }) } } func builderForPassThrough(t *testing.T, injector any, limiter quotas.Limiter, expectCalls bool, callerBypass quotas.CallerBypass, expectedErr error) (object any) { ctrl := gomock.NewController(t) switch injector.(type) { case *ratelimitedConfigStoreManager: mocked := persistence.NewMockConfigStoreManager(ctrl) object = NewConfigStoreManager(mocked, limiter, callerBypass) if expectCalls { mocked.EXPECT().UpdateDynamicConfig(gomock.Any(), gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().FetchDynamicConfig(gomock.Any(), gomock.Any()).Return(&persistence.FetchDynamicConfigResponse{}, expectedErr) } case *ratelimitedDomainManager: mocked := persistence.NewMockDomainManager(ctrl) object = NewDomainManager(mocked, limiter, callerBypass) if expectCalls { mocked.EXPECT().CreateDomain(gomock.Any(), gomock.Any()).Return(&persistence.CreateDomainResponse{}, expectedErr) mocked.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(&persistence.GetDomainResponse{}, expectedErr) mocked.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteDomain(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteDomainByName(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(&persistence.ListDomainsResponse{}, expectedErr) mocked.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{}, expectedErr) } case *ratelimitedHistoryManager: mocked := persistence.NewMockHistoryManager(ctrl) object = NewHistoryManager(mocked, limiter, callerBypass) if expectCalls { mocked.EXPECT().AppendHistoryNodes(gomock.Any(), gomock.Any()).Return(&persistence.AppendHistoryNodesResponse{}, expectedErr) mocked.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchResponse{}, expectedErr) mocked.EXPECT().ReadHistoryBranchByBatch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchByBatchResponse{}, expectedErr) mocked.EXPECT().ReadRawHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadRawHistoryBranchResponse{}, expectedErr) mocked.EXPECT().ForkHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ForkHistoryBranchResponse{}, expectedErr) mocked.EXPECT().DeleteHistoryBranch(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetHistoryTree(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTreeResponse{}, expectedErr) mocked.EXPECT().GetAllHistoryTreeBranches(gomock.Any(), gomock.Any()).Return(&persistence.GetAllHistoryTreeBranchesResponse{}, expectedErr) } case *ratelimitedQueueManager: mocked := persistence.NewMockQueueManager(ctrl) object = NewQueueManager(mocked, limiter, callerBypass) if expectCalls { mocked.EXPECT().EnqueueMessage(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().ReadMessages(gomock.Any(), gomock.Any()).Return(&persistence.ReadMessagesResponse{Messages: []*persistence.QueueMessage{}}, expectedErr) mocked.EXPECT().UpdateAckLevel(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetAckLevels(gomock.Any(), gomock.Any()).Return(&persistence.GetAckLevelsResponse{AckLevels: map[string]int64{}}, expectedErr) mocked.EXPECT().DeleteMessagesBefore(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteMessageFromDLQ(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().EnqueueMessageToDLQ(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetDLQAckLevels(gomock.Any(), gomock.Any()).Return(&persistence.GetDLQAckLevelsResponse{AckLevels: map[string]int64{}}, expectedErr) mocked.EXPECT().GetDLQSize(gomock.Any(), gomock.Any()).Return(&persistence.GetDLQSizeResponse{Size: 0}, expectedErr) mocked.EXPECT().RangeDeleteMessagesFromDLQ(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().ReadMessagesFromDLQ(gomock.Any(), gomock.Any()).Return(&persistence.ReadMessagesFromDLQResponse{Messages: []*persistence.QueueMessage{}}, expectedErr) mocked.EXPECT().UpdateDLQAckLevel(gomock.Any(), gomock.Any()).Return(expectedErr) } case *ratelimitedShardManager: mocked := persistence.NewMockShardManager(ctrl) object = NewShardManager(mocked, limiter, callerBypass) if expectCalls { mocked.EXPECT().GetShard(gomock.Any(), gomock.Any()).Return(&persistence.GetShardResponse{}, expectedErr) mocked.EXPECT().UpdateShard(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().CreateShard(gomock.Any(), gomock.Any()).Return(expectedErr) } case *ratelimitedTaskManager: mocked := persistence.NewMockTaskManager(ctrl) object = NewTaskManager(mocked, limiter, callerBypass) if expectCalls { mocked.EXPECT().CompleteTasksLessThan(gomock.Any(), gomock.Any()).Return(&persistence.CompleteTasksLessThanResponse{}, expectedErr) mocked.EXPECT().CompleteTask(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().CreateTasks(gomock.Any(), gomock.Any()).Return(&persistence.CreateTasksResponse{}, expectedErr) mocked.EXPECT().DeleteTaskList(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetTasksResponse{}, expectedErr) mocked.EXPECT().GetOrphanTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetOrphanTasksResponse{}, expectedErr) mocked.EXPECT().GetTaskListSize(gomock.Any(), gomock.Any()).Return(&persistence.GetTaskListSizeResponse{}, expectedErr) mocked.EXPECT().LeaseTaskList(gomock.Any(), gomock.Any()).Return(&persistence.LeaseTaskListResponse{}, expectedErr) mocked.EXPECT().GetTaskList(gomock.Any(), gomock.Any()).Return(&persistence.GetTaskListResponse{}, expectedErr) mocked.EXPECT().ListTaskList(gomock.Any(), gomock.Any()).Return(&persistence.ListTaskListResponse{}, expectedErr) mocked.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any()).Return(&persistence.UpdateTaskListResponse{}, expectedErr) } case *ratelimitedVisibilityManager: mocked := persistence.NewMockVisibilityManager(ctrl) object = NewVisibilityManager(mocked, limiter, callerBypass) if expectCalls { mocked.EXPECT().DeleteUninitializedWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.CountWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().GetClosedWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.GetClosedWorkflowExecutionResponse{}, expectedErr) mocked.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListClosedWorkflowExecutionsByStatus(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) mocked.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().RecordWorkflowExecutionUninitialized(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().UpsertWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, expectedErr) } case *ratelimitedExecutionManager: mocked := persistence.NewMockExecutionManager(ctrl) object = NewExecutionManager(mocked, limiter, callerBypass) if expectCalls { mocked.EXPECT().CreateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.CreateWorkflowExecutionResponse{}, expectedErr) mocked.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.GetWorkflowExecutionResponse{}, expectedErr) mocked.EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.UpdateWorkflowExecutionResponse{}, expectedErr) mocked.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteCurrentWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetCurrentExecution(gomock.Any(), gomock.Any()).Return(&persistence.GetCurrentExecutionResponse{}, expectedErr) mocked.EXPECT().ConflictResolveWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.ConflictResolveWorkflowExecutionResponse{}, expectedErr) mocked.EXPECT().CreateFailoverMarkerTasks(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().DeleteReplicationTaskFromDLQ(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetReplicationDLQSize(gomock.Any(), gomock.Any()).Return(&persistence.GetReplicationDLQSizeResponse{}, expectedErr) mocked.EXPECT().GetReplicationTasksFromDLQ(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTasksResponse{}, expectedErr) mocked.EXPECT().IsWorkflowExecutionExists(gomock.Any(), gomock.Any()).Return(&persistence.IsWorkflowExecutionExistsResponse{}, expectedErr) mocked.EXPECT().ListConcreteExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListConcreteExecutionsResponse{}, expectedErr) mocked.EXPECT().ListCurrentExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListCurrentExecutionsResponse{}, expectedErr) mocked.EXPECT().PutReplicationTaskToDLQ(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTasksResponse{}, expectedErr) mocked.EXPECT().CompleteHistoryTask(gomock.Any(), gomock.Any()).Return(expectedErr) mocked.EXPECT().RangeCompleteHistoryTask(gomock.Any(), gomock.Any()).Return(&persistence.RangeCompleteHistoryTaskResponse{}, expectedErr) mocked.EXPECT().RangeDeleteReplicationTaskFromDLQ(gomock.Any(), gomock.Any()).Return(&persistence.RangeDeleteReplicationTaskFromDLQResponse{}, expectedErr) mocked.EXPECT().GetActiveClusterSelectionPolicy(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterSelectionPolicy{}, expectedErr) mocked.EXPECT().DeleteActiveClusterSelectionPolicy(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(expectedErr) } default: t.Errorf("unsupported type %v", reflect.TypeOf(injector)) t.FailNow() } return } func TestVisibilityManagerBypassRateLimitForCallerTypes(t *testing.T) { tests := []struct { name string callerType types.CallerType bypassCallerTypes []interface{} shouldBypass bool }{ { name: "CLI bypasses when configured", callerType: types.CallerTypeCLI, bypassCallerTypes: []interface{}{"cli"}, shouldBypass: true, }, { name: "UI bypasses when configured", callerType: types.CallerTypeUI, bypassCallerTypes: []interface{}{"ui"}, shouldBypass: true, }, { name: "SDK bypasses when configured", callerType: types.CallerTypeSDK, bypassCallerTypes: []interface{}{"sdk"}, shouldBypass: true, }, { name: "Internal bypasses when configured", callerType: types.CallerTypeInternal, bypassCallerTypes: []interface{}{"internal"}, shouldBypass: true, }, { name: "Multiple types can bypass", callerType: types.CallerTypeCLI, bypassCallerTypes: []interface{}{"cli", "internal"}, shouldBypass: true, }, { name: "Caller type not in bypass list is rate limited", callerType: types.CallerTypeSDK, bypassCallerTypes: []interface{}{"cli", "internal"}, shouldBypass: false, }, { name: "Unknown does not bypass", callerType: types.CallerTypeUnknown, bypassCallerTypes: []interface{}{"cli", "ui", "sdk", "internal"}, shouldBypass: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mocked := persistence.NewMockVisibilityManager(ctrl) configClient := dynamicconfig.NewInMemoryClient() _ = configClient.UpdateValue(dynamicproperties.RateLimiterBypassCallerTypes, tt.bypassCallerTypes) dc := dynamicconfig.NewCollection( configClient, testlogger.New(t), ) callerBypass := quotas.NewCallerBypass(dc.GetListProperty(dynamicproperties.RateLimiterBypassCallerTypes)) vm := NewVisibilityManager(mocked, &limiterNeverAllow{}, callerBypass) ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(tt.callerType)) if tt.shouldBypass { mocked.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(nil) err := vm.RecordWorkflowExecutionStarted(ctx, &persistence.RecordWorkflowExecutionStartedRequest{}) assert.NoError(t, err) } else { err := vm.RecordWorkflowExecutionStarted(ctx, &persistence.RecordWorkflowExecutionStartedRequest{}) var expectedErr *types.ServiceBusyError assert.True(t, errors.As(err, &expectedErr)) assert.Equal(t, ErrPersistenceLimitExceeded.Message, expectedErr.Message) } }) } } func TestVisibilityManagerBypassRateLimitWithDynamicConfig(t *testing.T) { ctrl := gomock.NewController(t) mocked := persistence.NewMockVisibilityManager(ctrl) configClient := dynamicconfig.NewInMemoryClient() configClient.UpdateValue(dynamicproperties.RateLimiterBypassCallerTypes, []interface{}{"cli", "internal"}) dc := dynamicconfig.NewCollection( configClient, testlogger.New(t), ) callerBypass := quotas.NewCallerBypass(dc.GetListProperty(dynamicproperties.RateLimiterBypassCallerTypes)) vm := NewVisibilityManager(mocked, &limiterNeverAllow{}, callerBypass) t.Run("CLI bypasses rate limit", func(t *testing.T) { ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(types.CallerTypeCLI)) mocked.EXPECT().RecordWorkflowExecutionStarted(ctx, gomock.Any()).Return(nil) err := vm.RecordWorkflowExecutionStarted(ctx, &persistence.RecordWorkflowExecutionStartedRequest{}) assert.NoError(t, err) }) t.Run("Internal bypasses rate limit", func(t *testing.T) { ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(types.CallerTypeInternal)) mocked.EXPECT().RecordWorkflowExecutionClosed(ctx, gomock.Any()).Return(nil) err := vm.RecordWorkflowExecutionClosed(ctx, &persistence.RecordWorkflowExecutionClosedRequest{}) assert.NoError(t, err) }) t.Run("SDK does not bypass rate limit", func(t *testing.T) { ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(types.CallerTypeSDK)) err := vm.UpsertWorkflowExecution(ctx, &persistence.UpsertWorkflowExecutionRequest{}) var expectedErr *types.ServiceBusyError assert.True(t, errors.As(err, &expectedErr)) assert.Equal(t, ErrPersistenceLimitExceeded.Message, expectedErr.Message) }) t.Run("No CallerInfo does not bypass rate limit", func(t *testing.T) { ctx := context.Background() err := vm.DeleteWorkflowExecution(ctx, &persistence.VisibilityDeleteWorkflowExecutionRequest{}) var expectedErr *types.ServiceBusyError assert.True(t, errors.As(err, &expectedErr)) assert.Equal(t, ErrPersistenceLimitExceeded.Message, expectedErr.Message) }) } func TestVisibilityManagerNoBypassWithoutDynamicConfig(t *testing.T) { ctrl := gomock.NewController(t) mocked := persistence.NewMockVisibilityManager(ctrl) vm := NewVisibilityManager(mocked, &limiterNeverAllow{}, quotas.CallerBypass{}) ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(types.CallerTypeCLI)) err := vm.RecordWorkflowExecutionStarted(ctx, &persistence.RecordWorkflowExecutionStartedRequest{}) var expectedErr *types.ServiceBusyError assert.True(t, errors.As(err, &expectedErr)) assert.Equal(t, ErrPersistenceLimitExceeded.Message, expectedErr.Message) } func TestShardManagerBypassRateLimitForCallerTypes(t *testing.T) { tests := []struct { name string callerType types.CallerType bypassCallerTypes []interface{} shouldBypass bool }{ { name: "CLI bypasses when configured", callerType: types.CallerTypeCLI, bypassCallerTypes: []interface{}{"cli"}, shouldBypass: true, }, { name: "Internal bypasses when configured", callerType: types.CallerTypeInternal, bypassCallerTypes: []interface{}{"internal"}, shouldBypass: true, }, { name: "Multiple types can bypass", callerType: types.CallerTypeCLI, bypassCallerTypes: []interface{}{"cli", "internal"}, shouldBypass: true, }, { name: "Caller type not in bypass list is rate limited", callerType: types.CallerTypeSDK, bypassCallerTypes: []interface{}{"cli", "internal"}, shouldBypass: false, }, { name: "Unknown does not bypass", callerType: types.CallerTypeUnknown, bypassCallerTypes: []interface{}{"cli", "ui", "sdk", "internal"}, shouldBypass: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mocked := persistence.NewMockShardManager(ctrl) configClient := dynamicconfig.NewInMemoryClient() _ = configClient.UpdateValue(dynamicproperties.RateLimiterBypassCallerTypes, tt.bypassCallerTypes) dc := dynamicconfig.NewCollection( configClient, testlogger.New(t), ) callerBypass := quotas.NewCallerBypass(dc.GetListProperty(dynamicproperties.RateLimiterBypassCallerTypes)) sm := NewShardManager(mocked, &limiterNeverAllow{}, callerBypass) ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(tt.callerType)) if tt.shouldBypass { mocked.EXPECT().GetShard(gomock.Any(), gomock.Any()).Return(&persistence.GetShardResponse{}, nil) _, err := sm.GetShard(ctx, &persistence.GetShardRequest{}) assert.NoError(t, err) } else { _, err := sm.GetShard(ctx, &persistence.GetShardRequest{}) var expectedErr *types.ServiceBusyError assert.True(t, errors.As(err, &expectedErr)) assert.Equal(t, ErrPersistenceLimitExceeded.Message, expectedErr.Message) } }) } } func TestHistoryManagerBypassRateLimitForCallerTypes(t *testing.T) { tests := []struct { name string callerType types.CallerType bypassCallerTypes []interface{} shouldBypass bool }{ { name: "CLI bypasses when configured", callerType: types.CallerTypeCLI, bypassCallerTypes: []interface{}{"cli"}, shouldBypass: true, }, { name: "Internal bypasses when configured", callerType: types.CallerTypeInternal, bypassCallerTypes: []interface{}{"internal"}, shouldBypass: true, }, { name: "Multiple types can bypass", callerType: types.CallerTypeCLI, bypassCallerTypes: []interface{}{"cli", "internal"}, shouldBypass: true, }, { name: "Caller type not in bypass list is rate limited", callerType: types.CallerTypeSDK, bypassCallerTypes: []interface{}{"cli", "internal"}, shouldBypass: false, }, { name: "Unknown does not bypass", callerType: types.CallerTypeUnknown, bypassCallerTypes: []interface{}{"cli", "ui", "sdk", "internal"}, shouldBypass: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mocked := persistence.NewMockHistoryManager(ctrl) configClient := dynamicconfig.NewInMemoryClient() _ = configClient.UpdateValue(dynamicproperties.RateLimiterBypassCallerTypes, tt.bypassCallerTypes) dc := dynamicconfig.NewCollection( configClient, testlogger.New(t), ) callerBypass := quotas.NewCallerBypass(dc.GetListProperty(dynamicproperties.RateLimiterBypassCallerTypes)) hm := NewHistoryManager(mocked, &limiterNeverAllow{}, callerBypass) ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(tt.callerType)) if tt.shouldBypass { mocked.EXPECT().AppendHistoryNodes(gomock.Any(), gomock.Any()).Return(&persistence.AppendHistoryNodesResponse{}, nil) _, err := hm.AppendHistoryNodes(ctx, &persistence.AppendHistoryNodesRequest{}) assert.NoError(t, err) } else { _, err := hm.AppendHistoryNodes(ctx, &persistence.AppendHistoryNodesRequest{}) var expectedErr *types.ServiceBusyError assert.True(t, errors.As(err, &expectedErr)) assert.Equal(t, ErrPersistenceLimitExceeded.Message, expectedErr.Message) } }) } } ================================================ FILE: common/persistence/wrappers/sampled/tokenbucketfactory.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sampled import ( "sync" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/tokenbucket" ) type RateLimiterFactoryFunc func(timeSource clock.TimeSource, numOfPriority int, qpsConfig dynamicproperties.IntPropertyFnWithDomainFilter) RateLimiterFactory type RateLimiterFactory interface { GetRateLimiter(domain string) tokenbucket.PriorityTokenBucket } type domainToBucketMap struct { sync.RWMutex timeSource clock.TimeSource qpsConfig dynamicproperties.IntPropertyFnWithDomainFilter numOfPriority int mappings map[string]tokenbucket.PriorityTokenBucket } // NewDomainToBucketMap returns a rate limiter factory. func NewDomainToBucketMap(timeSource clock.TimeSource, numOfPriority int, qpsConfig dynamicproperties.IntPropertyFnWithDomainFilter) RateLimiterFactory { return &domainToBucketMap{ timeSource: timeSource, qpsConfig: qpsConfig, numOfPriority: numOfPriority, mappings: make(map[string]tokenbucket.PriorityTokenBucket), } } func (m *domainToBucketMap) GetRateLimiter(domain string) tokenbucket.PriorityTokenBucket { m.RLock() rateLimiter, exist := m.mappings[domain] m.RUnlock() if exist { return rateLimiter } m.Lock() if rateLimiter, ok := m.mappings[domain]; ok { // read again to ensure no duplicate create m.Unlock() return rateLimiter } rateLimiter = tokenbucket.NewFullPriorityTokenBucket(m.numOfPriority, m.qpsConfig(domain), m.timeSource) m.mappings[domain] = rateLimiter m.Unlock() return rateLimiter } ================================================ FILE: common/persistence/wrappers/sampled/tokenbucketfactory_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sampled import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) func TestDomainToBucketMap(t *testing.T) { mockedTime := clock.NewMockedTimeSource() factory := NewDomainToBucketMap(mockedTime, 1, dynamicproperties.GetIntPropertyFilteredByDomain(1)) // Test that the factory returns the same bucket for the same domain bucket1 := factory.GetRateLimiter("domain1") bucket2 := factory.GetRateLimiter("domain1") assert.Equal(t, bucket1, bucket2, "domain bucket should return the same bucket for the same domain") } ================================================ FILE: common/persistence/wrappers/sampled/visibility_manager.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sampled import ( "context" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const ( // To sample visibility request, open has only 1 bucket, closed has 2 numOfPriorityForOpen = 1 numOfPriorityForClosed = 2 numOfPriorityForList = 1 ) // errPersistenceLimitExceededForList is the error indicating QPS limit reached for list visibility. var errPersistenceLimitExceededForList = &types.ServiceBusyError{Message: "Persistence Max QPS Reached for List Operations."} type visibilityManager struct { rateLimitersForOpen RateLimiterFactory rateLimitersForClosed RateLimiterFactory rateLimitersForList RateLimiterFactory persistence persistence.VisibilityManager metricClient metrics.Client logger log.Logger } type ( // Config is config for visibility Config struct { VisibilityOpenMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter `yaml:"-" json:"-"` // VisibilityClosedMaxQPS max QPS for record closed workflows VisibilityClosedMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter `yaml:"-" json:"-"` // VisibilityListMaxQPS max QPS for list workflow VisibilityListMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter `yaml:"-" json:"-"` } ) type Params struct { Config *Config MetricClient metrics.Client Logger log.Logger TimeSource clock.TimeSource RateLimiterFactoryFunc RateLimiterFactoryFunc } // NewVisibilityManager creates a client to manage visibility with sampling // For write requests, it will do sampling which will lose some records // For read requests, it will do sampling which will return service busy errors. // Note that this is different from NewVisibilityPersistenceRateLimitedClient which is overlapping with the read processing. func NewVisibilityManager(persistence persistence.VisibilityManager, p Params) persistence.VisibilityManager { return &visibilityManager{ persistence: persistence, rateLimitersForOpen: p.RateLimiterFactoryFunc(p.TimeSource, numOfPriorityForOpen, p.Config.VisibilityOpenMaxQPS), rateLimitersForClosed: p.RateLimiterFactoryFunc(p.TimeSource, numOfPriorityForClosed, p.Config.VisibilityClosedMaxQPS), rateLimitersForList: p.RateLimiterFactoryFunc(p.TimeSource, numOfPriorityForList, p.Config.VisibilityListMaxQPS), metricClient: p.MetricClient, logger: p.Logger, } } func (p *visibilityManager) RecordWorkflowExecutionStarted( ctx context.Context, request *persistence.RecordWorkflowExecutionStartedRequest, ) error { domain := request.Domain domainID := request.DomainUUID rateLimiter := p.rateLimitersForOpen.GetRateLimiter(domain) if ok, _ := rateLimiter.GetToken(0, 1); ok { return p.persistence.RecordWorkflowExecutionStarted(ctx, request) } p.logger.Info("Request for open workflow is sampled", tag.WorkflowDomainID(domainID), tag.WorkflowDomainName(domain), tag.WorkflowType(request.WorkflowTypeName), tag.WorkflowID(request.Execution.GetWorkflowID()), tag.WorkflowRunID(request.Execution.GetRunID()), ) p.metricClient.IncCounter(metrics.PersistenceRecordWorkflowExecutionStartedScope, metrics.PersistenceSampledCounter) return nil } func (p *visibilityManager) RecordWorkflowExecutionClosed( ctx context.Context, request *persistence.RecordWorkflowExecutionClosedRequest, ) error { domain := request.Domain domainID := request.DomainUUID priority := getRequestPriority(request) rateLimiter := p.rateLimitersForClosed.GetRateLimiter(domain) if ok, _ := rateLimiter.GetToken(priority, 1); ok { return p.persistence.RecordWorkflowExecutionClosed(ctx, request) } p.logger.Info("Request for closed workflow is sampled", tag.WorkflowDomainID(domainID), tag.WorkflowDomainName(domain), tag.WorkflowType(request.WorkflowTypeName), tag.WorkflowID(request.Execution.GetWorkflowID()), tag.WorkflowRunID(request.Execution.GetRunID()), ) p.metricClient.IncCounter(metrics.PersistenceRecordWorkflowExecutionClosedScope, metrics.PersistenceSampledCounter) return nil } func (p *visibilityManager) UpsertWorkflowExecution( ctx context.Context, request *persistence.UpsertWorkflowExecutionRequest, ) error { domain := request.Domain domainID := request.DomainUUID rateLimiter := p.rateLimitersForClosed.GetRateLimiter(domain) if ok, _ := rateLimiter.GetToken(0, 1); ok { return p.persistence.UpsertWorkflowExecution(ctx, request) } p.logger.Info("Request for upsert workflow is sampled", tag.WorkflowDomainID(domainID), tag.WorkflowDomainName(domain), tag.WorkflowType(request.WorkflowTypeName), tag.WorkflowID(request.Execution.GetWorkflowID()), tag.WorkflowRunID(request.Execution.GetRunID()), ) p.metricClient.IncCounter(metrics.PersistenceUpsertWorkflowExecutionScope, metrics.PersistenceSampledCounter) return nil } func (p *visibilityManager) ListOpenWorkflowExecutions( ctx context.Context, request *persistence.ListWorkflowExecutionsRequest, ) (*persistence.ListWorkflowExecutionsResponse, error) { if err := p.tryConsumeListToken(request.Domain, "ListOpenWorkflowExecutions"); err != nil { return nil, err } return p.persistence.ListOpenWorkflowExecutions(ctx, request) } func (p *visibilityManager) ListClosedWorkflowExecutions( ctx context.Context, request *persistence.ListWorkflowExecutionsRequest, ) (*persistence.ListWorkflowExecutionsResponse, error) { if err := p.tryConsumeListToken(request.Domain, "ListClosedWorkflowExecutions"); err != nil { return nil, err } return p.persistence.ListClosedWorkflowExecutions(ctx, request) } func (p *visibilityManager) ListOpenWorkflowExecutionsByType( ctx context.Context, request *persistence.ListWorkflowExecutionsByTypeRequest, ) (*persistence.ListWorkflowExecutionsResponse, error) { if err := p.tryConsumeListToken(request.Domain, "ListOpenWorkflowExecutionsByType"); err != nil { return nil, err } return p.persistence.ListOpenWorkflowExecutionsByType(ctx, request) } func (p *visibilityManager) ListClosedWorkflowExecutionsByType( ctx context.Context, request *persistence.ListWorkflowExecutionsByTypeRequest, ) (*persistence.ListWorkflowExecutionsResponse, error) { if err := p.tryConsumeListToken(request.Domain, "ListClosedWorkflowExecutionsByType"); err != nil { return nil, err } return p.persistence.ListClosedWorkflowExecutionsByType(ctx, request) } func (p *visibilityManager) ListOpenWorkflowExecutionsByWorkflowID( ctx context.Context, request *persistence.ListWorkflowExecutionsByWorkflowIDRequest, ) (*persistence.ListWorkflowExecutionsResponse, error) { if err := p.tryConsumeListToken(request.Domain, "ListOpenWorkflowExecutionsByWorkflowID"); err != nil { return nil, err } return p.persistence.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) } func (p *visibilityManager) ListClosedWorkflowExecutionsByWorkflowID( ctx context.Context, request *persistence.ListWorkflowExecutionsByWorkflowIDRequest, ) (*persistence.ListWorkflowExecutionsResponse, error) { if err := p.tryConsumeListToken(request.Domain, "ListClosedWorkflowExecutionsByWorkflowID"); err != nil { return nil, err } return p.persistence.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) } func (p *visibilityManager) ListClosedWorkflowExecutionsByStatus( ctx context.Context, request *persistence.ListClosedWorkflowExecutionsByStatusRequest, ) (*persistence.ListWorkflowExecutionsResponse, error) { if err := p.tryConsumeListToken(request.Domain, "ListClosedWorkflowExecutionsByStatus"); err != nil { return nil, err } return p.persistence.ListClosedWorkflowExecutionsByStatus(ctx, request) } func (p *visibilityManager) RecordWorkflowExecutionUninitialized( ctx context.Context, request *persistence.RecordWorkflowExecutionUninitializedRequest, ) error { return p.persistence.RecordWorkflowExecutionUninitialized(ctx, request) } func (p *visibilityManager) GetClosedWorkflowExecution( ctx context.Context, request *persistence.GetClosedWorkflowExecutionRequest, ) (*persistence.GetClosedWorkflowExecutionResponse, error) { return p.persistence.GetClosedWorkflowExecution(ctx, request) } func (p *visibilityManager) DeleteWorkflowExecution( ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest, ) error { return p.persistence.DeleteWorkflowExecution(ctx, request) } func (p *visibilityManager) DeleteUninitializedWorkflowExecution( ctx context.Context, request *persistence.VisibilityDeleteWorkflowExecutionRequest, ) error { return p.persistence.DeleteUninitializedWorkflowExecution(ctx, request) } func (p *visibilityManager) ListWorkflowExecutions( ctx context.Context, request *persistence.ListWorkflowExecutionsByQueryRequest, ) (*persistence.ListWorkflowExecutionsResponse, error) { return p.persistence.ListWorkflowExecutions(ctx, request) } func (p *visibilityManager) ScanWorkflowExecutions( ctx context.Context, request *persistence.ListWorkflowExecutionsByQueryRequest, ) (*persistence.ListWorkflowExecutionsResponse, error) { return p.persistence.ScanWorkflowExecutions(ctx, request) } func (p *visibilityManager) CountWorkflowExecutions( ctx context.Context, request *persistence.CountWorkflowExecutionsRequest, ) (*persistence.CountWorkflowExecutionsResponse, error) { return p.persistence.CountWorkflowExecutions(ctx, request) } func (p *visibilityManager) Close() { p.persistence.Close() } func (p *visibilityManager) GetName() string { return p.persistence.GetName() } func getRequestPriority(request *persistence.RecordWorkflowExecutionClosedRequest) int { priority := 0 if request.Status == types.WorkflowExecutionCloseStatusCompleted { priority = 1 // low priority for completed workflows } return priority } func (p *visibilityManager) tryConsumeListToken(domain, method string) error { rateLimiter := p.rateLimitersForList.GetRateLimiter(domain) ok, _ := rateLimiter.GetToken(0, 1) if ok { p.logger.Debug("List API request consumed QPS token", tag.WorkflowDomainName(domain), tag.Name(method)) return nil } p.logger.Debug("List API request is being sampled", tag.WorkflowDomainName(domain), tag.Name(method)) return errPersistenceLimitExceededForList } ================================================ FILE: common/persistence/wrappers/sampled/visibility_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sampled import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/tokenbucket" "github.com/uber/cadence/common/types" ) func TestVisibilityManagerSampledCalls(t *testing.T) { for _, tc := range []struct { name string priority int prepareMock func(*persistence.MockVisibilityManager) operation func(context.Context, string, persistence.VisibilityManager) error }{ { name: "RecordWorkflowExecutionStarted", prepareMock: func(mock *persistence.MockVisibilityManager) { mock.EXPECT().RecordWorkflowExecutionStarted(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, operation: func(ctx context.Context, domain string, m persistence.VisibilityManager) error { return m.RecordWorkflowExecutionStarted(ctx, &persistence.RecordWorkflowExecutionStartedRequest{ Domain: domain, }) }, }, { name: "RecordWorkflowExecutionClosed", prepareMock: func(mock *persistence.MockVisibilityManager) { mock.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, operation: func(ctx context.Context, domain string, m persistence.VisibilityManager) error { return m.RecordWorkflowExecutionClosed(ctx, &persistence.RecordWorkflowExecutionClosedRequest{ Domain: domain, Status: types.WorkflowExecutionCloseStatusCanceled, }) }, }, { name: "RecordWorkflowExecutionClosed_Completed", priority: 1, prepareMock: func(mock *persistence.MockVisibilityManager) { mock.EXPECT().RecordWorkflowExecutionClosed(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, operation: func(ctx context.Context, domain string, m persistence.VisibilityManager) error { return m.RecordWorkflowExecutionClosed(ctx, &persistence.RecordWorkflowExecutionClosedRequest{ Domain: domain, Status: types.WorkflowExecutionCloseStatusCompleted, }) }, }, { name: "UpsertWorkflowExecution", prepareMock: func(mock *persistence.MockVisibilityManager) { mock.EXPECT().UpsertWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, operation: func(ctx context.Context, domain string, m persistence.VisibilityManager) error { return m.UpsertWorkflowExecution(ctx, &persistence.UpsertWorkflowExecutionRequest{ Domain: domain, }) }, }, } { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockedManager := persistence.NewMockVisibilityManager(ctrl) testDomain := "domain1" m := NewVisibilityManager(mockedManager, Params{ Config: &Config{}, MetricClient: metrics.NewNoopMetricsClient(), Logger: testlogger.New(t), TimeSource: clock.NewMockedTimeSource(), RateLimiterFactoryFunc: rateLimiterStubFunc(map[string]tokenbucket.PriorityTokenBucket{ testDomain: &tokenBucketFactoryStub{tokens: map[int]int{tc.priority: 1}}, }), }) tc.prepareMock(mockedManager) err := tc.operation(context.Background(), testDomain, m) assert.NoError(t, err, "first call should succeed") err = tc.operation(context.Background(), testDomain, m) assert.NoError(t, err, "second call should not fail, but underlying call should be blocked by rate limiter") }) } } func TestVisibilityManagerListOperations(t *testing.T) { for _, tc := range []struct { name string priority int prepareMock func(*persistence.MockVisibilityManager) operation func(context.Context, string, persistence.VisibilityManager) error }{ { name: "ListOpenWorkflowExecutions", prepareMock: func(mock *persistence.MockVisibilityManager) { mock.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, nil).Times(1) }, operation: func(ctx context.Context, domain string, m persistence.VisibilityManager) error { _, err := m.ListOpenWorkflowExecutions(ctx, &persistence.ListWorkflowExecutionsRequest{ Domain: domain, }) return err }, }, { name: "ListClosedWorkflowExecutions", prepareMock: func(mock *persistence.MockVisibilityManager) { mock.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, nil).Times(1) }, operation: func(ctx context.Context, domain string, m persistence.VisibilityManager) error { _, err := m.ListClosedWorkflowExecutions(ctx, &persistence.ListWorkflowExecutionsRequest{ Domain: domain, }) return err }, }, { name: "ListOpenWorkflowExecutionsByType", prepareMock: func(mock *persistence.MockVisibilityManager) { mock.EXPECT().ListOpenWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, nil).Times(1) }, operation: func(ctx context.Context, domain string, m persistence.VisibilityManager) error { _, err := m.ListOpenWorkflowExecutionsByType(ctx, &persistence.ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: persistence.ListWorkflowExecutionsRequest{ Domain: domain, }, }) return err }, }, { name: "ListClosedWorkflowExecutionsByType", prepareMock: func(mock *persistence.MockVisibilityManager) { mock.EXPECT().ListClosedWorkflowExecutionsByType(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, nil).Times(1) }, operation: func(ctx context.Context, domain string, m persistence.VisibilityManager) error { _, err := m.ListClosedWorkflowExecutionsByType(ctx, &persistence.ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: persistence.ListWorkflowExecutionsRequest{ Domain: domain, }, }) return err }, }, { name: "ListOpenWorkflowExecutionsByWorkflowID", prepareMock: func(mock *persistence.MockVisibilityManager) { mock.EXPECT().ListOpenWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, nil).Times(1) }, operation: func(ctx context.Context, domain string, m persistence.VisibilityManager) error { _, err := m.ListOpenWorkflowExecutionsByWorkflowID(ctx, &persistence.ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: persistence.ListWorkflowExecutionsRequest{ Domain: domain, }, }) return err }, }, { name: "ListClosedWorkflowExecutionsByWorkflowID", prepareMock: func(mock *persistence.MockVisibilityManager) { mock.EXPECT().ListClosedWorkflowExecutionsByWorkflowID(gomock.Any(), gomock.Any()).Return(&persistence.ListWorkflowExecutionsResponse{}, nil).Times(1) }, operation: func(ctx context.Context, domain string, m persistence.VisibilityManager) error { _, err := m.ListClosedWorkflowExecutionsByWorkflowID(ctx, &persistence.ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: persistence.ListWorkflowExecutionsRequest{ Domain: domain, }, }) return err }, }, } { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockedManager := persistence.NewMockVisibilityManager(ctrl) testDomain := "domain1" m := NewVisibilityManager(mockedManager, Params{ Config: &Config{}, MetricClient: metrics.NewNoopMetricsClient(), Logger: testlogger.New(t), TimeSource: clock.NewMockedTimeSource(), RateLimiterFactoryFunc: rateLimiterStubFunc(map[string]tokenbucket.PriorityTokenBucket{ testDomain: &tokenBucketFactoryStub{tokens: map[int]int{tc.priority: 1}}, }), }) tc.prepareMock(mockedManager) err := tc.operation(context.Background(), testDomain, m) assert.NoError(t, err, "first call should succeed") err = tc.operation(context.Background(), testDomain, m) assert.Error(t, err, "second call should fail since underlying call should be blocked by rate limiter") }) } } func rateLimiterStubFunc(domainData map[string]tokenbucket.PriorityTokenBucket) RateLimiterFactoryFunc { return func(timeSource clock.TimeSource, numOfPriority int, qpsConfig dynamicproperties.IntPropertyFnWithDomainFilter) RateLimiterFactory { return rateLimiterStub{domainData} } } type rateLimiterStub struct { data map[string]tokenbucket.PriorityTokenBucket } func (r rateLimiterStub) GetRateLimiter(domain string) tokenbucket.PriorityTokenBucket { return r.data[domain] } type tokenBucketFactoryStub struct { tokens map[int]int } func (t *tokenBucketFactoryStub) GetToken(priority, count int) (bool, time.Duration) { val := t.tokens[priority] if count > val { return false, time.Duration(0) } val -= count t.tokens[priority] = val return true, time.Duration(0) } ================================================ FILE: common/persistence/wrappers/templates/errorinjector.tmpl ================================================ import ( "context" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" ) {{ $decorator := (printf "injector%s" .Interface.Name) }} {{ $interfaceName := .Interface.Name }} // {{$decorator}} implements {{.Interface.Type}} interface instrumented with error injection. type {{$decorator}} struct { wrapped {{.Interface.Type}} starttime time.Time errorRate float64 logger log.Logger } // New{{.Interface.Name}} creates a new instance of {{.Interface.Name}} with error injection. func New{{.Interface.Name}}( wrapped persistence.{{.Interface.Name}}, errorRate float64, logger log.Logger, starttime time.Time, ) persistence.{{.Interface.Name}} { return &{{$decorator}}{ wrapped: wrapped, starttime: starttime, errorRate: errorRate, logger: logger, } } {{range $methodName, $method := .Interface.Methods}} {{- if (and $method.AcceptsContext $method.ReturnsError)}} func (c *{{$decorator}}) {{$method.Declaration}} { fakeErr := generateFakeError(c.errorRate, c.starttime) var forwardCall bool if forwardCall = shouldForwardCallToPersistence(fakeErr); forwardCall { {{$method.ResultsNames}} = c.wrapped.{{$method.Call}} } if fakeErr != nil { logErr(c.logger, "{{$interfaceName}}.{{$methodName}}", fakeErr, forwardCall, err) err = fakeErr return } return } {{else}} func (c *{{$decorator}}) {{$method.Declaration}} { {{ $method.Pass "c.wrapped." }} } {{end}} {{end}} ================================================ FILE: common/persistence/wrappers/templates/metered.tmpl ================================================ import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) {{ $decorator := (printf "metered%s" .Interface.Name) }} {{ $interfaceName := .Interface.Name }} // {{$decorator}} implements {{.Interface.Type}} interface instrumented with rate limiter. type {{$decorator}} struct { base wrapped {{.Interface.Type}} } // New{{.Interface.Name}} creates a new instance of {{.Interface.Name}} with ratelimiter. func New{{.Interface.Name}}( wrapped persistence.{{.Interface.Name}}, metricClient metrics.Client, logger log.Logger, cfg *config.Persistence, ) persistence.{{.Interface.Name}} { return &{{$decorator}}{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger, enableLatencyHistogramMetrics: cfg.EnablePersistenceLatencyHistogramMetrics, }, } } {{range $methodName, $method := .Interface.Methods}} {{- if (and $method.AcceptsContext $method.ReturnsError)}} func (c *{{$decorator}}) {{$method.Declaration}} { op := func() error { {{$method.ResultsNames}} = c.wrapped.{{$method.Call}} {{ if and (gt (len $method.Params) 1) (gt (len $method.Results) 0) -}} c.emptyMetric("{{$interfaceName}}.{{$methodName}}", {{(index $method.Params 1).Name}}, {{(index $method.Results 0).Name}}, err) {{ end -}} return err } {{$scopeName := printf "metrics.Persistence%sScope" $methodName}} {{ if and (eq $interfaceName "VisibilityManager") (eq $methodName "DeleteWorkflowExecution") -}} {{ $scopeName = "metrics.PersistenceVisibilityDeleteWorkflowExecutionScope" }} {{ end -}} {{$extraTags := ""}} {{ if gt (len $method.Params) 1 -}} {{ $reqName := (index $method.Params 1).Name }} {{ $extraTags = printf ", getCustomMetricTags(%s)..." $reqName }} {{ end -}} err = c.call({{$scopeName}}, op{{$extraTags}}) return } {{else}} func (c *{{$decorator}}) {{$method.Declaration}} { {{ $method.Pass "c.wrapped." }} } {{end}} {{end}} ================================================ FILE: common/persistence/wrappers/templates/metered_execution.tmpl ================================================ import ( "context" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) {{ $decorator := (printf "metered%s" .Interface.Name) }} {{ $interfaceName := .Interface.Name }} // {{$decorator}} implements {{.Interface.Type}} interface instrumented with rate limiter. type {{$decorator}} struct { base wrapped {{.Interface.Type}} } // New{{.Interface.Name}} creates a new instance of {{.Interface.Name}} with ratelimiter. func New{{.Interface.Name}}( wrapped persistence.{{.Interface.Name}}, metricClient metrics.Client, logger log.Logger, cfg *config.Persistence, sampleLoggingRate dynamicproperties.IntPropertyFn, enableShardIDMetrics dynamicproperties.BoolPropertyFn, ) persistence.{{.Interface.Name}} { return &{{$decorator}}{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger.WithTags(tag.ShardID(wrapped.GetShardID())), enableLatencyHistogramMetrics: cfg.EnablePersistenceLatencyHistogramMetrics, sampleLoggingRate: sampleLoggingRate, enableShardIDMetrics: enableShardIDMetrics, }, } } {{range $methodName, $method := .Interface.Methods}} {{- if (and $method.AcceptsContext $method.ReturnsError)}} func (c *{{$decorator}}) {{$method.Declaration}} { op := func() error { {{$method.ResultsNames}} = c.wrapped.{{$method.Call}} {{ if gt (len $method.Results) 1 -}} c.emptyMetric("{{$interfaceName}}.{{$methodName}}", {{(index $method.Params 1).Name}}, {{(index $method.Results 0).Name}}, err) {{ end -}} return err } {{$scopeName := printf "metrics.Persistence%sScope" $methodName}} {{ if gt (len $method.Params) 1 -}} {{ $reqName := (index $method.Params 1).Name }} retryCount := getRetryCountFromContext(ctx) if domainName, hasDomainName := getDomainNameFromRequest({{$reqName}}); hasDomainName { logTags := append([]tag.Tag{tag.WorkflowDomainName(domainName)}, getCustomLogTags({{$reqName}})...) c.logger.SampleInfo("Persistence {{$methodName}} called", c.sampleLoggingRate(), logTags...) if c.enableShardIDMetrics() { err = c.callWithDomainAndShardScope({{$scopeName}}, op, metrics.DomainTag(domainName), metrics.ShardIDTag(c.GetShardID()), metrics.IsRetryTag(retryCount > 0)) } else { err = c.call({{$scopeName}}, op, metrics.DomainTag(domainName), metrics.IsRetryTag(retryCount > 0)) } return } {{ end -}} {{$extraTags := ""}} {{ if gt (len $method.Params) 1 -}} {{ $reqName := (index $method.Params 1).Name }} {{ $extraTags = printf ", append(getCustomMetricTags(%s), metrics.IsRetryTag(retryCount > 0))..." $reqName }} {{ end -}} err = c.callWithoutDomainTag({{$scopeName}}, op{{$extraTags}}) return } {{else}} func (c *{{$decorator}}) {{$method.Declaration}} { {{ $method.Pass "c.wrapped." }} } {{end}} {{end}} ================================================ FILE: common/persistence/wrappers/templates/ratelimited.tmpl ================================================ import ( "context" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" ) {{ $decorator := (printf "ratelimited%s" .Interface.Name) }} // {{$decorator}} implements {{.Interface.Type}} interface instrumented with rate limiter. type {{$decorator}} struct { wrapped {{.Interface.Type}} rateLimiter quotas.Limiter callerBypass quotas.CallerBypass } // New{{.Interface.Name}} creates a new instance of {{.Interface.Name}} with ratelimiter. func New{{.Interface.Name}}( wrapped persistence.{{.Interface.Name}}, rateLimiter quotas.Limiter, callerBypass quotas.CallerBypass, ) persistence.{{.Interface.Name}} { return &{{$decorator}}{ wrapped: wrapped, rateLimiter: rateLimiter, callerBypass: callerBypass, } } {{range $method := .Interface.Methods}} {{- if (and $method.AcceptsContext $method.ReturnsError)}} func (c *{{$decorator}}) {{$method.Declaration}} { if !c.callerBypass.AllowLimiter({{(index $method.Params 0).Name}}, c.rateLimiter) { err = ErrPersistenceLimitExceeded return } {{ $method.Pass "c.wrapped." }} } {{else}} func (c *{{$decorator}}) {{$method.Declaration}} { {{ $method.Pass "c.wrapped." }} } {{end}} {{end}} ================================================ FILE: common/pinot/generic_client_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go // // Generated by this command: // // mockgen -package pinot -source interfaces.go -destination generic_client_mock.go -self_package github.com/uber/cadence/common/pinot // // Package pinot is a generated GoMock package. package pinot import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockGenericClient is a mock of GenericClient interface. type MockGenericClient struct { ctrl *gomock.Controller recorder *MockGenericClientMockRecorder isgomock struct{} } // MockGenericClientMockRecorder is the mock recorder for MockGenericClient. type MockGenericClientMockRecorder struct { mock *MockGenericClient } // NewMockGenericClient creates a new mock instance. func NewMockGenericClient(ctrl *gomock.Controller) *MockGenericClient { mock := &MockGenericClient{ctrl: ctrl} mock.recorder = &MockGenericClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockGenericClient) EXPECT() *MockGenericClientMockRecorder { return m.recorder } // CountByQuery mocks base method. func (m *MockGenericClient) CountByQuery(query string) (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CountByQuery", query) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // CountByQuery indicates an expected call of CountByQuery. func (mr *MockGenericClientMockRecorder) CountByQuery(query any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountByQuery", reflect.TypeOf((*MockGenericClient)(nil).CountByQuery), query) } // GetTableName mocks base method. func (m *MockGenericClient) GetTableName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTableName") ret0, _ := ret[0].(string) return ret0 } // GetTableName indicates an expected call of GetTableName. func (mr *MockGenericClientMockRecorder) GetTableName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTableName", reflect.TypeOf((*MockGenericClient)(nil).GetTableName)) } // Search mocks base method. func (m *MockGenericClient) Search(request *SearchRequest) (*SearchResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Search", request) ret0, _ := ret[0].(*SearchResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Search indicates an expected call of Search. func (mr *MockGenericClientMockRecorder) Search(request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockGenericClient)(nil).Search), request) } // SearchAggr mocks base method. func (m *MockGenericClient) SearchAggr(request *SearchRequest) (AggrResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SearchAggr", request) ret0, _ := ret[0].(AggrResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SearchAggr indicates an expected call of SearchAggr. func (mr *MockGenericClientMockRecorder) SearchAggr(request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchAggr", reflect.TypeOf((*MockGenericClient)(nil).SearchAggr), request) } ================================================ FILE: common/pinot/interfaces.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination generic_client_mock.go -self_package github.com/uber/cadence/common/pinot package pinot import p "github.com/uber/cadence/common/persistence" type ( // GenericClient is a generic interface for all versions of Pinot clients GenericClient interface { // Search API is only for supporting various List[Open/Closed]WorkflowExecutions(ByXyz). // Use SearchByQuery or ScanByQuery for generic purpose searching. Search(request *SearchRequest) (*SearchResponse, error) SearchAggr(request *SearchRequest) (AggrResponse, error) // CountByQuery is for returning the count of workflow executions that match the query CountByQuery(query string) (int64, error) GetTableName() string } // IsRecordValidFilter is a function to filter visibility records IsRecordValidFilter func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool // SearchRequest is request for Search SearchRequest struct { Query string IsOpen bool Filter IsRecordValidFilter MaxResultWindow int ListRequest *p.InternalListWorkflowExecutionsRequest } // SearchResponse is a response to Search, SearchByQuery and ScanByQuery SearchResponse = p.InternalListWorkflowExecutionsResponse AggrResponse [][]interface{} ) ================================================ FILE: common/pinot/page_token.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package pinot import ( "bytes" "encoding/json" "fmt" "github.com/uber/cadence/common/types" ) type ( // PinotVisibilityPageToken holds the paging token for Pinot PinotVisibilityPageToken struct { From int } ) // DeserializePageToken return the structural token func DeserializePageToken(data []byte) (*PinotVisibilityPageToken, error) { var token PinotVisibilityPageToken dec := json.NewDecoder(bytes.NewReader(data)) dec.UseNumber() err := dec.Decode(&token) if err != nil { return nil, &types.BadRequestError{ Message: fmt.Sprintf("unable to deserialize page token. err: %v", err), } } return &token, nil } // SerializePageToken return the token blob func SerializePageToken(token *PinotVisibilityPageToken) ([]byte, error) { data, err := json.Marshal(token) if err != nil { return nil, &types.BadRequestError{ Message: fmt.Sprintf("unable to serialize page token. err: %v", err), } } return data, nil } // GetNextPageToken returns the structural token with nil handling func GetNextPageToken(token []byte) (*PinotVisibilityPageToken, error) { var result *PinotVisibilityPageToken var err error if len(token) > 0 { result, err = DeserializePageToken(token) if err != nil { return nil, err } } else { result = &PinotVisibilityPageToken{} } return result, nil } ================================================ FILE: common/pinot/page_token_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pinot import ( "testing" "github.com/stretchr/testify/assert" ) func TestSerializePageToken(t *testing.T) { token := &PinotVisibilityPageToken{ From: 1, } tests := map[string]struct { token *PinotVisibilityPageToken expectedOutput []byte expectedError error }{ "Case2: normal case with nil response": { token: token, expectedOutput: []byte(`{"From":1}`), expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { actualOutput, err := SerializePageToken(test.token) assert.Equal(t, test.expectedOutput, actualOutput) assert.Nil(t, err) }) } } ================================================ FILE: common/pinot/pinotQueryValidator.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pinot import ( "errors" "fmt" "strconv" "strings" "time" "github.com/xwb1989/sqlparser" "github.com/uber/cadence/common" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" ) // VisibilityQueryValidator for sql query validation type VisibilityQueryValidator struct { validSearchAttributes dynamicproperties.MapPropertyFn pinotOptimizedQueryColumns dynamicproperties.MapPropertyFn } var timeSystemKeys = map[string]bool{ "StartTime": true, "CloseTime": true, "ExecutionTime": true, "UpdateTime": true, } // NewPinotQueryValidator create VisibilityQueryValidator func NewPinotQueryValidator(validSearchAttributes dynamicproperties.MapPropertyFn, pinotOptimizedQueryColumns dynamicproperties.MapPropertyFn) *VisibilityQueryValidator { return &VisibilityQueryValidator{ validSearchAttributes: validSearchAttributes, pinotOptimizedQueryColumns: pinotOptimizedQueryColumns, } } // ValidateQuery validates that search attributes in the query and returns modified query. func (qv *VisibilityQueryValidator) ValidateQuery(whereClause string) (string, error) { if len(whereClause) != 0 { // Build a placeholder query that allows us to easily parse the contents of the where clause. // IMPORTANT: This query is never executed, it is just used to parse and validate whereClause var placeholderQuery string whereClause := strings.TrimSpace(whereClause) if common.IsJustOrderByClause(whereClause) { // just order by placeholderQuery = fmt.Sprintf("SELECT * FROM dummy %s", whereClause) } else { placeholderQuery = fmt.Sprintf("SELECT * FROM dummy WHERE %s", whereClause) } stmt, err := sqlparser.Parse(placeholderQuery) if err != nil { return "", &types.BadRequestError{Message: "Invalid query: " + err.Error()} } sel, ok := stmt.(*sqlparser.Select) if !ok { return "", &types.BadRequestError{Message: "Invalid select query."} } buf := sqlparser.NewTrackedBuffer(nil) res := "" // validate where expr if sel.Where != nil { res, err = qv.validateWhereExpr(sel.Where.Expr) if err != nil { return "", &types.BadRequestError{Message: err.Error()} } } sel.OrderBy.Format(buf) res += buf.String() return res, nil } return whereClause, nil } func (qv *VisibilityQueryValidator) validateWhereExpr(expr sqlparser.Expr) (string, error) { if expr == nil { return "", nil } switch expr := expr.(type) { case *sqlparser.AndExpr, *sqlparser.OrExpr: return qv.validateAndOrExpr(expr) case *sqlparser.ComparisonExpr: return qv.validateComparisonExpr(expr) case *sqlparser.RangeCond: return qv.validateRangeExpr(expr) case *sqlparser.ParenExpr: return qv.validateWhereExpr(expr.Expr) default: return "", errors.New("invalid where clause") } } // for "between...and..." only // <, >, >=, <= are included in validateComparisonExpr() func (qv *VisibilityQueryValidator) validateRangeExpr(expr sqlparser.Expr) (string, error) { buf := sqlparser.NewTrackedBuffer(nil) rangeCond := expr.(*sqlparser.RangeCond) colName, ok := rangeCond.Left.(*sqlparser.ColName) if !ok { return "", errors.New("invalid range expression: fail to get colname") } colNameStr := colName.Name.String() if !qv.IsValidSearchAttributes(colNameStr) { return "", fmt.Errorf("invalid search attribute %q", colNameStr) } if definition.IsSystemIndexedKey(colNameStr) { if _, ok = timeSystemKeys[colNameStr]; ok { if lowerBound, ok := rangeCond.From.(*sqlparser.SQLVal); ok { trimmed, err := trimTimeFieldValueFromNanoToMilliSeconds(lowerBound) if err != nil { return "", fmt.Errorf("trim time field %s got error: %w", colNameStr, err) } rangeCond.From = trimmed } if upperBound, ok := rangeCond.To.(*sqlparser.SQLVal); ok { trimmed, err := trimTimeFieldValueFromNanoToMilliSeconds(upperBound) if err != nil { return "", fmt.Errorf("trim time field %s got error: %w", colNameStr, err) } rangeCond.To = trimmed } } expr.Format(buf) return buf.String(), nil } // lowerBound, ok := rangeCond.From.(*sqlparser.ColName) lowerBound, ok := rangeCond.From.(*sqlparser.SQLVal) if !ok { return "", errors.New("invalid range expression: fail to get lowerbound") } lowerBoundString := string(lowerBound.Val) upperBound, ok := rangeCond.To.(*sqlparser.SQLVal) if !ok { return "", errors.New("invalid range expression: fail to get upperbound") } upperBoundString := string(upperBound.Val) return fmt.Sprintf("(JSON_MATCH(Attr, '\"$.%s\" is not null') "+ "AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.%s') AS INT) >= %s "+ "AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.%s') AS INT) <= %s)", colNameStr, colNameStr, lowerBoundString, colNameStr, upperBoundString), nil } func (qv *VisibilityQueryValidator) validateAndOrExpr(expr sqlparser.Expr) (string, error) { var leftExpr sqlparser.Expr var rightExpr sqlparser.Expr isAnd := false switch expr := expr.(type) { case *sqlparser.AndExpr: leftExpr = expr.Left rightExpr = expr.Right isAnd = true case *sqlparser.OrExpr: leftExpr = expr.Left rightExpr = expr.Right } leftRes, err := qv.validateWhereExpr(leftExpr) if err != nil { return "", err } rightRes, err := qv.validateWhereExpr(rightExpr) if err != nil { return "", err } if isAnd { return fmt.Sprintf("%s and %s", leftRes, rightRes), nil } return fmt.Sprintf("(%s or %s)", leftRes, rightRes), nil } func (qv *VisibilityQueryValidator) validateComparisonExpr(expr sqlparser.Expr) (string, error) { comparisonExpr := expr.(*sqlparser.ComparisonExpr) colName, ok := comparisonExpr.Left.(*sqlparser.ColName) if !ok { return "", errors.New("invalid comparison expression, left") } colNameStr := colName.Name.String() if !qv.IsValidSearchAttributes(colNameStr) { return "", fmt.Errorf("invalid search attribute %q", colNameStr) } // Case1: it is system key // this means that we don't need to change the structure of the query, // just need to check if a value == "missing" if definition.IsSystemIndexedKey(colNameStr) { return qv.processSystemKey(expr) } // Case2: when a value is not system key // This means, the value is from Attr so that we need to change the query to be a Json index format return qv.processCustomKey(expr) } // IsValidSearchAttributes return true if key is registered func (qv *VisibilityQueryValidator) IsValidSearchAttributes(key string) bool { validAttr := qv.validSearchAttributes() _, isValidKey := validAttr[key] return isValidKey } // IsOptimizedQueryColumn return true if colNameStr is in the optimized query columns func (qv *VisibilityQueryValidator) IsOptimizedQueryColumn(colNameStr string) bool { if qv.pinotOptimizedQueryColumns() != nil { optimizedQueryColumns := qv.pinotOptimizedQueryColumns() _, isOptimizedQueryColumn := optimizedQueryColumns[colNameStr] return isOptimizedQueryColumn } return false } func (qv *VisibilityQueryValidator) processSystemBoolKey(colNameStr string, comparisonExpr sqlparser.ComparisonExpr) (string, error) { // case1: isCron = false colVal, ok := comparisonExpr.Right.(sqlparser.BoolVal) if !ok { // case2: isCron = "false" or isCron = 'false' sqlVal, ok := comparisonExpr.Right.(*sqlparser.SQLVal) if !ok { return "", fmt.Errorf("failed to process a bool key to SQLVal: %v", comparisonExpr.Right) } colValStr := string(sqlVal.Val) if strings.ToLower(colValStr) != "false" && strings.ToLower(colValStr) != "true" { return "", fmt.Errorf("invalid bool value in pinot_query_validator: %s", colValStr) } return fmt.Sprintf("%s = %s", colNameStr, colValStr), nil } return fmt.Sprintf("%s = %v", colNameStr, colVal), nil } func (qv *VisibilityQueryValidator) processSystemKey(expr sqlparser.Expr) (string, error) { comparisonExpr := expr.(*sqlparser.ComparisonExpr) buf := sqlparser.NewTrackedBuffer(nil) colName, ok := comparisonExpr.Left.(*sqlparser.ColName) if !ok { return "", fmt.Errorf("left comparison is invalid: %v", comparisonExpr.Left) } colNameStr := colName.Name.String() // handle system bool key if definition.IsSystemBoolKey(colNameStr) { return qv.processSystemBoolKey(colNameStr, *comparisonExpr) } if comparisonExpr.Operator == sqlparser.LikeStr { colVal, ok := comparisonExpr.Right.(*sqlparser.SQLVal) if !ok { return "", fmt.Errorf("right comparison is invalid: %v", comparisonExpr.Right) } colValStr := string(colVal.Val) return fmt.Sprintf("TEXT_MATCH(%s, '/.*%s.*/')", colNameStr, colValStr), nil } if comparisonExpr.Operator != sqlparser.EqualStr && comparisonExpr.Operator != sqlparser.NotEqualStr { if _, ok := timeSystemKeys[colNameStr]; ok { sqlVal, ok := comparisonExpr.Right.(*sqlparser.SQLVal) if !ok { return "", fmt.Errorf("right comparison is invalid: %v", comparisonExpr.Right) } trimmed, err := trimTimeFieldValueFromNanoToMilliSeconds(sqlVal) if err != nil { return "", fmt.Errorf("trim time field %s got error: %w", colNameStr, err) } comparisonExpr.Right = trimmed } expr.Format(buf) return buf.String(), nil } // need to deal with missing value e.g. CloseTime = missing // Question: why is the right side is sometimes a type of "colName", and sometimes a type of "SQLVal"? // Answer: for any value, sqlParser will treat any string that doesn't surrounded by single quote as ColName; // any string that surrounded by single quote as SQLVal _, ok = comparisonExpr.Right.(*sqlparser.SQLVal) if !ok { // this means, the value is a string, and not surrounded by single qoute, which means, val = missing colVal, ok := comparisonExpr.Right.(*sqlparser.ColName) if !ok { return "", fmt.Errorf("right comparison is invalid: %v", comparisonExpr.Right) } colValStr := colVal.Name.String() // double check if val is not missing if colValStr != "missing" { return "", fmt.Errorf("right comparison is invalid string value: %s", colValStr) } var newColVal string if strings.ToLower(colNameStr) == "historylength" { newColVal = "0" } else { newColVal = "-1" // -1 is the default value for all Closed workflows related fields } comparisonExpr.Right = &sqlparser.SQLVal{ Type: sqlparser.IntVal, // or sqlparser.StrVal if you need to assign a string Val: []byte(newColVal), } } else { if _, ok := timeSystemKeys[colNameStr]; ok { sqlVal, ok := comparisonExpr.Right.(*sqlparser.SQLVal) if !ok { return "", fmt.Errorf("right comparison is invalid/missing. key %s, right expr %v", colNameStr, comparisonExpr.Right) } trimmed, err := trimTimeFieldValueFromNanoToMilliSeconds(sqlVal) if err != nil { return "", fmt.Errorf("trim time field %s got error: %w", colNameStr, err) } comparisonExpr.Right = trimmed } else if colNameStr == "CloseStatus" { sqlVal, ok := comparisonExpr.Right.(*sqlparser.SQLVal) if !ok { return "", fmt.Errorf("right comparison is invalid: %v", comparisonExpr.Right) } closeStatus, err := parseCloseStatus(sqlVal) if err != nil { return "", fmt.Errorf("parse CloseStatus field got error: %w", err) } comparisonExpr.Right = closeStatus } } // For this branch, we still have a sqlExpr type. So need to use a buf to return the string comparisonExpr.Format(buf) return buf.String(), nil } func (qv *VisibilityQueryValidator) processInClause(expr sqlparser.Expr) (string, error) { comparisonExpr, ok := expr.(*sqlparser.ComparisonExpr) if !ok { return "", errors.New("invalid IN expression") } colName, ok := comparisonExpr.Left.(*sqlparser.ColName) if !ok { return "", errors.New("invalid IN expression, left") } colNameStr := colName.Name.String() valTuple, ok := comparisonExpr.Right.(sqlparser.ValTuple) if !ok { return "", errors.New("invalid IN expression, right") } values := make([]string, len(valTuple)) for i, val := range valTuple { sqlVal, ok := val.(*sqlparser.SQLVal) if !ok { return "", errors.New("invalid IN expression, value") } values[i] = "''" + string(sqlVal.Val) + "''" } return fmt.Sprintf("JSON_MATCH(Attr, '\"$.%s\" IN (%s)') or JSON_MATCH(Attr, '\"$.%s[*]\" IN (%s)')", colNameStr, strings.Join(values, ","), colNameStr, strings.Join(values, ",")), nil } func (qv *VisibilityQueryValidator) processCustomKey(expr sqlparser.Expr) (string, error) { comparisonExpr := expr.(*sqlparser.ComparisonExpr) colName, ok := comparisonExpr.Left.(*sqlparser.ColName) if !ok { return "", errors.New("invalid comparison expression, left") } colNameStr := colName.Name.String() // check type: if is IndexedValueTypeString, change to like statement for partial match valType, ok := qv.validSearchAttributes()[colNameStr] if !ok { return "", fmt.Errorf("invalid search attribute") } useOptimizedQuery := qv.IsOptimizedQueryColumn(colNameStr) // process IN clause in json indexed col: Attr operator := strings.ToLower(comparisonExpr.Operator) if operator == sqlparser.InStr { return qv.processInClause(expr) } // get the column value colVal, ok := comparisonExpr.Right.(*sqlparser.SQLVal) if !ok { return "", errors.New("invalid comparison expression, right") } // get the value type indexValType := common.ConvertIndexedValueTypeToInternalType(valType, log.NewNoop()) colValStr := string(colVal.Val) switch indexValType { case types.IndexedValueTypeString: return processCustomString(operator, colNameStr, colValStr, useOptimizedQuery), nil case types.IndexedValueTypeKeyword: return processCustomKeyword(operator, colNameStr, colValStr, useOptimizedQuery), nil case types.IndexedValueTypeDatetime: var err error colVal, err = trimTimeFieldValueFromNanoToMilliSeconds(colVal) if err != nil { return "", fmt.Errorf("trim time field %s got error: %w", colNameStr, err) } colValStr := string(colVal.Val) return processCustomNum(operator, colNameStr, colValStr, "BIGINT"), nil case types.IndexedValueTypeDouble: return processCustomNum(operator, colNameStr, colValStr, "DOUBLE"), nil case types.IndexedValueTypeInt: return processCustomNum(operator, colNameStr, colValStr, "INT"), nil default: return processEqual(colNameStr, colValStr), nil } } func processCustomNum(operator string, colNameStr string, colValStr string, valType string) string { if operator == sqlparser.EqualStr { return processEqual(colNameStr, colValStr) } return fmt.Sprintf("(JSON_MATCH(Attr, '\"$.%s\" is not null') "+ "AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.%s') AS %s) %s %s)", colNameStr, colNameStr, valType, operator, colValStr) } func processEqual(colNameStr string, colValStr string) string { return fmt.Sprintf("JSON_MATCH(Attr, '\"$.%s\"=''%s''')", colNameStr, colValStr) } func processCustomKeyword(operator string, colNameStr string, colValStr string, useOptimizedQuery bool) string { if useOptimizedQuery { // replace "-" with "" for optimized query so Pinot can tokenize the whole UUID filteredColValStr := strings.ReplaceAll(colValStr, "-", "") return fmt.Sprintf("%s %s '%s'", colNameStr, operator, filteredColValStr) } if colValStr == "" { // partial match for an empty string (still it will only match empty string) // so it equals to exact match for an empty string return processCustomString(operator, colNameStr, colValStr, useOptimizedQuery) } connector := "or" if operator == "!=" { connector = "and" } return fmt.Sprintf("(JSON_MATCH(Attr, '\"$.%s\"%s''%s''') %s JSON_MATCH(Attr, '\"$.%s[*]\"%s''%s'''))", colNameStr, operator, colValStr, connector, colNameStr, operator, colValStr) } func processCustomString(operator string, colNameStr string, colValStr string, useOptimizedQuery bool) string { if useOptimizedQuery { // replace "-" with "" for optimized query so Pinot can tokenize the whole UUID filteredColValStr := strings.ReplaceAll(colValStr, "-", "") return fmt.Sprintf("%s %s '%s'", colNameStr, operator, filteredColValStr) } notEqual := "" if operator == "!=" { notEqual = "NOT " } // handle edge case if colValStr == "" { return fmt.Sprintf("JSON_MATCH(Attr, '\"$.%s\" is not null') "+ "AND %sJSON_MATCH(Attr, 'REGEXP_LIKE(\"$.%s\", ''^$'')')", colNameStr, notEqual, colNameStr) } return fmt.Sprintf("JSON_MATCH(Attr, '\"$.%s\" is not null') "+ "AND %sJSON_MATCH(Attr, 'REGEXP_LIKE(\"$.%s\", ''.*%s.*'')')", colNameStr, notEqual, colNameStr, colValStr) } func trimTimeFieldValueFromNanoToMilliSeconds(original *sqlparser.SQLVal) (*sqlparser.SQLVal, error) { // Convert the SQLVal to a string valStr := string(original.Val) newVal, err := parseTime(valStr) if err != nil { return original, fmt.Errorf("error: failed to parse int from SQLVal %s", valStr) } // Convert the new value back to SQLVal return &sqlparser.SQLVal{ Type: sqlparser.IntVal, Val: []byte(strconv.FormatInt(newVal, 10)), }, nil } func parseTime(timeStr string) (int64, error) { if len(timeStr) == 0 { return 0, errors.New("invalid time string") } // try to parse parsedTime, err := time.Parse(time.RFC3339, timeStr) if err == nil { return parsedTime.UnixMilli(), nil } // treat as raw time valInt, err := strconv.ParseInt(timeStr, 10, 64) if err == nil { var newVal int64 if valInt < 0 { // exclude open workflow which time field will be -1 newVal = valInt } else if len(timeStr) > 13 { // Assuming nanoseconds if more than 13 digits newVal = valInt / 1000000 // Convert time to milliseconds } else { newVal = valInt } return newVal, nil } return 0, errors.New("invalid time string") } func parseCloseStatus(original *sqlparser.SQLVal) (*sqlparser.SQLVal, error) { statusStr := string(original.Val) // first check if already in int64 format if status, err := strconv.ParseInt(statusStr, 10, 64); err == nil { // Instead of returning the original value, return a new SQLVal that holds the integer value // Or it will fail the case CloseStatus = '1' return &sqlparser.SQLVal{ Type: sqlparser.IntVal, Val: []byte(strconv.FormatInt(status, 10)), }, nil } // try to parse close status string var parsedStatus types.WorkflowExecutionCloseStatus err := parsedStatus.UnmarshalText([]byte(statusStr)) if err != nil { return nil, err } return &sqlparser.SQLVal{ Type: sqlparser.IntVal, Val: []byte(strconv.FormatInt(int64(parsedStatus), 10)), }, nil } ================================================ FILE: common/pinot/pinotQueryValidator_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pinot import ( "testing" "github.com/stretchr/testify/assert" "github.com/xwb1989/sqlparser" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) func TestValidateQuery(t *testing.T) { tests := map[string]struct { query string validated string err string }{ "Case1: empty query": { query: "", validated: "", }, "Case2-1: simple query": { query: "WorkflowID = 'wid'", validated: "WorkflowID = 'wid'", }, "Case2-2: simple query with partial match": { query: "WorkflowID like 'wid'", validated: "TEXT_MATCH(WorkflowID, '/.*wid.*/')", }, "Case2-3: invalid simple query with partial match": { query: "WorkflowID like wid", err: "right comparison is invalid: &{ wid { }}"}, "Case3-1: query with custom field": { query: "CustomStringField = 'custom'", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*custom.*'')')`, }, "Case3-2: query with custom field not equal": { query: "CustomStringField != 'custom'", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*custom.*'')')`, }, "Case3-3: query with custom field value is empty": { query: "CustomStringField = ''", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''^$'')')`, }, "Case3-4: query with custom field not equal to empty": { query: "CustomStringField != ''", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''^$'')')`, }, "Case4: custom field query with or in string": { query: "CustomStringField='Or'", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*Or.*'')')`, }, "Case5: custom keyword field query": { query: "CustomKeywordField = 'custom'", validated: `(JSON_MATCH(Attr, '"$.CustomKeywordField"=''custom''') or JSON_MATCH(Attr, '"$.CustomKeywordField[*]"=''custom'''))`, }, "Case6-1: complex query I: with parenthesis": { query: "(CustomStringField = 'custom and custom2 or custom3 order by') or CustomIntField between 1 and 10", validated: `(JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*custom and custom2 or custom3 order by.*'')') or (JSON_MATCH(Attr, '"$.CustomIntField" is not null') AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomIntField') AS INT) >= 1 AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomIntField') AS INT) <= 10))`, }, "Case6-2: complex query II: with only system keys": { query: "DomainID = 'd-id' and (RunID = 'run-id' or WorkflowID = 'wid')", validated: "DomainID = 'd-id' and (RunID = 'run-id' or WorkflowID = 'wid')", }, "Case6-3: complex query III: operation priorities": { query: "DomainID = 'd-id' or RunID = 'run-id' and WorkflowID = 'wid'", validated: "(DomainID = 'd-id' or RunID = 'run-id' and WorkflowID = 'wid')", }, "Case6-4: complex query IV": { query: "WorkflowID = 'wid' and (CustomStringField = 'custom and custom2 or custom3 order by' or CustomIntField between 1 and 10)", validated: `WorkflowID = 'wid' and (JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*custom and custom2 or custom3 order by.*'')') or (JSON_MATCH(Attr, '"$.CustomIntField" is not null') AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomIntField') AS INT) >= 1 AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomIntField') AS INT) <= 10))`, }, "Case6-5: complex query with partial match": { query: "RunID like '123' or WorkflowID like '123'", validated: "(TEXT_MATCH(RunID, '/.*123.*/') or TEXT_MATCH(WorkflowID, '/.*123.*/'))", }, "Case7: invalid sql query": { query: "Invalid SQL", err: "Invalid query: syntax error at position 38 near 'sql'", }, "Case8-1: query with missing val": { query: "CloseTime = missing", validated: "CloseTime = -1", }, "Case8-2: query with not missing case": { query: "CloseTime != missing", validated: "CloseTime != -1", }, "Case8-3: query with custom attr": { query: "CustomKeywordField = missing", err: "invalid comparison expression, right", }, "Case8-4: query with custom keyword field not equal": { query: "CustomKeywordField != 0", validated: `(JSON_MATCH(Attr, '"$.CustomKeywordField"!=''0''') and JSON_MATCH(Attr, '"$.CustomKeywordField[*]"!=''0'''))`, }, "Case9: invalid where expression": { query: "InvalidWhereExpr", err: "invalid where clause", }, "Case10: invalid search attribute": { query: "Invalid = 'a' and 1 < 2", err: `invalid search attribute "Invalid"`, }, "Case11-1: order by clause": { query: "order by CloseTime desc", validated: " order by CloseTime desc", }, "Case11-2: only order by clause with custom field": { query: "order by CustomIntField desc", validated: " order by CustomIntField desc", }, "Case11-3: order by clause with custom field": { query: "WorkflowID = 'wid' order by CloseTime desc", validated: "WorkflowID = 'wid' order by CloseTime desc", }, "Case12-1: security SQL injection - with another statement": { query: "WorkflowID = 'wid'; SELECT * FROM important_table;", err: "Invalid query: syntax error at position 53 near 'select'", }, "Case12-2: security SQL injection - with union": { query: "WorkflowID = 'wid' union select * from dummy", err: "Invalid select query.", }, "Case13: or clause": { query: "CustomIntField = 1 or CustomIntField = 2", validated: `(JSON_MATCH(Attr, '"$.CustomIntField"=''1''') or JSON_MATCH(Attr, '"$.CustomIntField"=''2'''))`, }, "Case14-1: range query: custom filed": { query: "CustomIntField BETWEEN 1 AND 2", validated: `(JSON_MATCH(Attr, '"$.CustomIntField" is not null') AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomIntField') AS INT) >= 1 AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomIntField') AS INT) <= 2)`, }, "Case14-2: range query: system filed": { query: "NumClusters BETWEEN 1 AND 2", validated: "NumClusters between 1 and 2", }, "Case15-1: custom date attribute less than": { query: "CustomDatetimeField < 1697754674", validated: `(JSON_MATCH(Attr, '"$.CustomDatetimeField" is not null') AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomDatetimeField') AS BIGINT) < 1697754674)`, }, "Case15-2: custom date attribute greater than or equal to": { query: "CustomDatetimeField >= 1697754674", validated: `(JSON_MATCH(Attr, '"$.CustomDatetimeField" is not null') AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomDatetimeField') AS BIGINT) >= 1697754674)`, }, "Case15-3: system date attribute greater than or equal to": { query: "StartTime >= 1697754674", validated: "StartTime >= 1697754674", }, "Case15-4: unix nano converts to milli seconds for equal statements": { query: "StartTime = 1707319950934000128", validated: "StartTime = 1707319950934", }, "Case15-5: unix nano converts to milli seconds for unequal statements query": { query: "StartTime > 1707319950934000128", validated: "StartTime > 1707319950934", }, "Case15-6: open workflows": { query: "CloseTime = -1", validated: "CloseTime = -1", }, "Case15-7: startTime for range query": { query: "StartTime BETWEEN 1707319950934000128 AND 1707319950935000128", validated: "StartTime between 1707319950934 and 1707319950935", }, "Case15-8: invalid string for trim": { query: "CloseTime = abc", validated: "", err: "right comparison is invalid string value: abc", }, "Case15-9: invalid value for trim": { query: "CloseTime = 123.45", validated: "", err: "trim time field CloseTime got error: error: failed to parse int from SQLVal 123.45", }, "Case15-10: invalid from time for range query": { query: "StartTime BETWEEN 17.50 AND 1707319950935000128", validated: "", err: "trim time field StartTime got error: error: failed to parse int from SQLVal 17.50", }, "Case15-11: invalid to time for range query": { query: "StartTime BETWEEN 1707319950934000128 AND 1707319950935000128.1", validated: "", err: "trim time field StartTime got error: error: failed to parse int from SQLVal 1707319950935000128.1", }, "Case15-12: value already in milliseconds": { query: "StartTime = 170731995093", validated: "StartTime = 170731995093", }, "Case15-13: value in raw string for equal statement": { query: "StartTime = '2024-02-07T15:32:30Z'", validated: "StartTime = 1707319950000", }, "Case15-14: value in raw string for not equal statement": { query: "StartTime > '2024-02-07T15:32:30Z'", validated: "StartTime > 1707319950000", }, "Case15-15: value in raw string for range statement": { query: "StartTime between '2024-02-07T15:32:30Z' and '2024-02-07T15:33:30Z'", validated: "StartTime between 1707319950000 and 1707320010000", }, "Case15-16: combined time and missing case": { query: "CloseTime != missing and StartTime >= 1707662555754408145", validated: "CloseTime != -1 and StartTime >= 1707662555754", }, "Case15-17: CustomDatetimeField with big int type case": { query: "CustomDatetimeField = 1707319950000", validated: `JSON_MATCH(Attr, '"$.CustomDatetimeField"=''1707319950000''')`, }, "Case15-18: CustomDatetimeField with time.Time() type case": { query: "CustomDatetimeField = '2024-02-07T15:32:30Z'", validated: `JSON_MATCH(Attr, '"$.CustomDatetimeField"=''1707319950000''')`, }, "Case15-19: CustomDatetimeField with error case": { query: "CustomDatetimeField = 'test'", validated: "", err: "trim time field CustomDatetimeField got error: error: failed to parse int from SQLVal test", }, "Case16-1: custom int attribute greater than or equal to": { query: "CustomIntField >= 0", validated: `(JSON_MATCH(Attr, '"$.CustomIntField" is not null') AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomIntField') AS INT) >= 0)`, }, "Case16-2: custom double attribute greater than or equal to": { query: "CustomDoubleField >= 0", validated: `(JSON_MATCH(Attr, '"$.CustomDoubleField" is not null') AND CAST(JSON_EXTRACT_SCALAR(Attr, '$.CustomDoubleField') AS DOUBLE) >= 0)`, }, "Case17: custom keyword attribute greater than or equal to. Will return error run time": { query: "CustomKeywordField < 0", validated: `(JSON_MATCH(Attr, '"$.CustomKeywordField"<''0''') or JSON_MATCH(Attr, '"$.CustomKeywordField[*]"<''0'''))`, }, "Case18: custom int order by. Will have errors at run time. Doesn't support for now": { query: "CustomIntField = 0 order by CustomIntField desc", validated: `JSON_MATCH(Attr, '"$.CustomIntField"=''0''') order by CustomIntField desc`, }, "case19-1: close status parse string": { query: "CloseStatus = 'CONTINUED_AS_NEW'", validated: "CloseStatus = 4", }, "case19-2: close status parse number": { query: "CloseStatus = '1'", validated: "CloseStatus = 1", }, "case19-3: close status parse normal case": { query: "CloseStatus = 1", validated: "CloseStatus = 1", }, "case20-1: in clause in Attr": { query: "CustomKeywordField in (123)", validated: `JSON_MATCH(Attr, '"$.CustomKeywordField" IN (''123'')') or JSON_MATCH(Attr, '"$.CustomKeywordField[*]" IN (''123'')')`, }, "case20-2: in clause in Attr with multiple values": { query: "CustomKeywordField in (123, 456)", validated: `JSON_MATCH(Attr, '"$.CustomKeywordField" IN (''123'',''456'')') or JSON_MATCH(Attr, '"$.CustomKeywordField[*]" IN (''123'',''456'')')`, }, "case20-3-1: in clause in Attr with a string value, double quote": { query: `CustomKeywordField in ("abc")`, validated: `JSON_MATCH(Attr, '"$.CustomKeywordField" IN (''abc'')') or JSON_MATCH(Attr, '"$.CustomKeywordField[*]" IN (''abc'')')`, }, "case20-3-2: in clause in Attr with a string value, single quote": { query: "CustomKeywordField in ('abc')", validated: `JSON_MATCH(Attr, '"$.CustomKeywordField" IN (''abc'')') or JSON_MATCH(Attr, '"$.CustomKeywordField[*]" IN (''abc'')')`, }, "case20-4: in clause in Attr with invalid IN expression, value": { query: "CustomKeywordField in (abc)", validated: "", err: "invalid IN expression, value", }, "case21-1: test bool value- system key- no quotes": { query: "IsCron = true", validated: "IsCron = true", }, "case21-2: test bool value- system key- single quotes": { query: "IsCron = 'true'", validated: "IsCron = true", }, "case21-3: test bool value- system key- double quotes": { query: `IsCron = "true"`, validated: "IsCron = true", }, "case21-4: test bool value- system key- invalid value": { query: "IsCron = 1", validated: "", err: "invalid bool value in pinot_query_validator: 1", }, "case21-5: test bool value- when it is not SQLBool and SQLVAl": { query: "IsCron = abc", validated: "", err: "failed to process a bool key to SQLVal: &{ abc { }}", }, "case22-1: test not equal to a string field": { query: "CustomStringField != 'abc'", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*abc.*'')')`, }, "case22-2: test not equal to an empty string": { query: "CustomStringField != ''", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''^$'')')`, }, // ES also doesn't support this kind of query "case22-3: custom string is missing": { query: "CustomStringField is missing", err: "Invalid query: syntax error at position 55 near 'missing'", }, // ES also doesn't support this kind of query "case22-4: custom string is not missing": { query: "CustomStringField is not missing", err: "Invalid query: syntax error at position 59 near 'missing'", }, "case22-5: 2 custom string not equal with and clause": { query: "CustomStringField != 'abc' AND CustomStringField != 'def'", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*abc.*'')') and JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*def.*'')')`, }, "case22-6: 2 custom string, equal and not equal with and clause": { query: "CustomStringField = 'abc' AND CustomStringField != 'def'", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*abc.*'')') and JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*def.*'')')`, }, "case22-7: 2 custom string, not equal and equal with and clause": { query: "CustomStringField != 'abc' AND CustomStringField = 'def'", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*abc.*'')') and JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*def.*'')')`, }, "case22-8: 2 custom string equal with and clause": { query: "CustomStringField = 'abc' AND CustomStringField = 'def'", validated: `JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*abc.*'')') and JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*def.*'')')`, }, "case22-9: 2 custom string not equal with or clause": { query: "CustomStringField != 'abc' OR CustomStringField != 'def'", validated: `(JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*abc.*'')') or JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*def.*'')'))`, }, "case22-10: 2 custom string, equal and not equal with or clause": { query: "CustomStringField = 'abc' OR CustomStringField != 'def'", validated: `(JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*abc.*'')') or JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*def.*'')'))`, }, "case22-11: 2 custom string, not equal and equal with or clause": { query: "CustomStringField != 'abc' OR CustomStringField = 'def'", validated: `(JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*abc.*'')') or JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*def.*'')'))`, }, "case22-12: 2 custom string equal with or clause": { query: "CustomStringField = 'abc' OR CustomStringField = 'def'", validated: `(JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*abc.*'')') or JSON_MATCH(Attr, '"$.CustomStringField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomStringField", ''.*def.*'')'))`, }, "case23-1: custom keyword field is empty case": { query: "CustomKeywordField = ''", validated: `JSON_MATCH(Attr, '"$.CustomKeywordField" is not null') AND JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomKeywordField", ''^$'')')`, }, "case23-2: custom keyword field is not empty case": { query: "CustomKeywordField != ''", validated: `JSON_MATCH(Attr, '"$.CustomKeywordField" is not null') AND NOT JSON_MATCH(Attr, 'REGEXP_LIKE("$.CustomKeywordField", ''^$'')')`, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { validSearchAttr := dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()) pinotOptimizedQueryColumns := dynamicproperties.GetMapPropertyFn(map[string]interface{}{ "CustomTestField": "test", }) qv := NewPinotQueryValidator(validSearchAttr, pinotOptimizedQueryColumns) validated, err := qv.ValidateQuery(test.query) if err != nil { assert.Equal(t, test.err, err.Error()) } else { assert.Equal(t, test.validated, validated) } }) } } func TestProcessInClause_FailedInputExprCases(t *testing.T) { // Define test cases tests := map[string]struct { inputExpr sqlparser.Expr expectedError string }{ "case1: 'In' clause in Attr with invalid expr": { inputExpr: &sqlparser.SQLVal{Type: sqlparser.StrVal, Val: []byte("invalid")}, expectedError: "invalid IN expression", }, "case2: 'In' clause in Attr with invalid expr, left": { inputExpr: &sqlparser.ComparisonExpr{Operator: sqlparser.InStr}, expectedError: "invalid IN expression, left", }, "case3: 'In' clause in Attr with invalid expr, right": { inputExpr: &sqlparser.ComparisonExpr{Operator: sqlparser.InStr, Left: &sqlparser.ColName{Name: sqlparser.NewColIdent("CustomKeywordField")}}, expectedError: "invalid IN expression, right", }, } // Create a new VisibilityQueryValidator validSearchAttr := dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()) pinotOptimizedQueryColumns := dynamicproperties.GetMapPropertyFn(map[string]interface{}{ "CustomTestField": "test", }) qv := NewPinotQueryValidator(validSearchAttr, pinotOptimizedQueryColumns) for name, test := range tests { t.Run(name, func(t *testing.T) { // Call processInClause with the input expression _, err := qv.processInClause(test.inputExpr) // Check that an error was returned and that the error message matches the expected error message if assert.Error(t, err) { assert.Contains(t, err.Error(), test.expectedError) } }) } } func TestParseTime(t *testing.T) { var tests = []struct { name string timeStr string expected int64 hasErr bool }{ {"empty string", "", 0, true}, {"valid RFC3339", "2024-02-07T15:32:30Z", 1707319950000, false}, {"valid unix milli string", "1707319950000", 1707319950000, false}, {"valid unix nano string", "1707319950000000000", 1707319950000, false}, {"invalid string", "invalid", 0, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { parsed, err := parseTime(tt.timeStr) assert.Equal(t, parsed, tt.expected) if tt.hasErr { assert.Error(t, err) } }) } } func TestParseCloseStatus(t *testing.T) { tests := []struct { input string expected *sqlparser.SQLVal expectedErr bool }{ { input: "4", expected: &sqlparser.SQLVal{Type: sqlparser.IntVal, Val: []byte("4")}, expectedErr: false, }, { input: "CANCELED", expected: &sqlparser.SQLVal{Type: sqlparser.IntVal, Val: []byte("2")}, expectedErr: false, }, { input: "invalid", expected: nil, expectedErr: true, // expected error, }, } for _, test := range tests { t.Run(test.input, func(t *testing.T) { original := &sqlparser.SQLVal{Type: sqlparser.IntVal, Val: []byte(test.input)} result, err := parseCloseStatus(original) assert.Equal(t, test.expected, result) if test.expectedErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestIsOptimizedQueryColumn(t *testing.T) { tests := map[string]struct { query string validated string err string }{ "case1-1: customzied search attribute field in pinotOptimizedQueryColumns": { query: "CustomTestKeywordField = 'Test-UUID-1234-5678'", validated: `CustomTestKeywordField = 'TestUUID12345678'`, }, "case1-2: customzied search attribute field in pinotOptimizedQueryColumns": { query: "CustomTestStringField = 'Test Test-UUID-1234-5678'", validated: `CustomTestStringField = 'Test TestUUID12345678'`, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { searchAttr := definition.GetDefaultIndexedKeys() searchAttr["CustomTestKeywordField"] = types.IndexedValueTypeKeyword searchAttr["CustomTestStringField"] = types.IndexedValueTypeString validSearchAttr := dynamicproperties.GetMapPropertyFn(searchAttr) pinotOptimizedQueryColumns := dynamicproperties.GetMapPropertyFn(map[string]interface{}{ "CustomTestKeywordField": "test", "CustomTestStringField": "test", }) qv := NewPinotQueryValidator(validSearchAttr, pinotOptimizedQueryColumns) validated, err := qv.ValidateQuery(test.query) if err != nil { assert.Equal(t, test.err, err.Error()) } else { assert.Equal(t, test.validated, validated) } }) } } ================================================ FILE: common/pinot/pinot_client.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pinot import ( "encoding/json" "fmt" "github.com/startreedata/pinot-client-go/pinot" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type PinotClient struct { client *pinot.Connection logger log.Logger tableName string serviceName string } func NewPinotClient(client *pinot.Connection, logger log.Logger, pinotConfig *config.PinotVisibilityConfig) GenericClient { return &PinotClient{ client: client, logger: logger, tableName: pinotConfig.Table, serviceName: pinotConfig.ServiceName, } } func (c *PinotClient) Search(request *SearchRequest) (*SearchResponse, error) { resp, err := c.client.ExecuteSQL(c.tableName, request.Query) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("Pinot Search failed, %v", err), } } token, err := GetNextPageToken(request.ListRequest.NextPageToken) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("Get NextPage token failed, %v", err), } } return c.getInternalListWorkflowExecutionsResponse(resp, request.Filter, token, request.ListRequest.PageSize, request.MaxResultWindow) } func (c *PinotClient) SearchAggr(request *SearchRequest) (AggrResponse, error) { resp, err := c.client.ExecuteSQL(c.tableName, request.Query) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("Pinot SearchAggr failed, %v", err), } } return resp.ResultTable.Rows, nil } func (c *PinotClient) CountByQuery(query string) (int64, error) { resp, err := c.client.ExecuteSQL(c.tableName, query) if err != nil { return 0, &types.InternalServiceError{ Message: fmt.Sprintf("CountWorkflowExecutions ExecuteSQL failed, %v", err), } } count, err := resp.ResultTable.Rows[0][0].(json.Number).Int64() if err == nil { return count, nil } return -1, &types.InternalServiceError{ Message: fmt.Sprintf("can't convert result to integer!, query = %s, query result = %v, err = %v", query, resp.ResultTable.Rows[0][0], err), } } func (c *PinotClient) GetTableName() string { return c.tableName } // Pinot Response Translator // We flattened the search attributes into columns in Pinot table // This function converts the search result back to VisibilityRecord func (c *PinotClient) getInternalListWorkflowExecutionsResponse( resp *pinot.BrokerResponse, isRecordValid func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool, token *PinotVisibilityPageToken, pageSize int, maxResultWindow int, ) (*p.InternalListWorkflowExecutionsResponse, error) { response := &p.InternalListWorkflowExecutionsResponse{} if resp == nil || resp.ResultTable == nil || resp.ResultTable.GetRowCount() == 0 { return response, nil } schema := resp.ResultTable.DataSchema // get the schema to map results columnNames := schema.ColumnNames actualHits := resp.ResultTable.Rows numOfActualHits := resp.ResultTable.GetRowCount() response.Executions = make([]*p.InternalVisibilityWorkflowExecutionInfo, 0) for i := 0; i < numOfActualHits; i++ { workflowExecutionInfo, err := ConvertSearchResultToVisibilityRecord(actualHits[i], columnNames) if err != nil { return nil, err } if isRecordValid == nil || isRecordValid(workflowExecutionInfo) { response.Executions = append(response.Executions, workflowExecutionInfo) } } if numOfActualHits == pageSize { // this means the response is not the last page var nextPageToken []byte var err error // ES Search API support pagination using From and PageSize, but has limit that From+PageSize cannot exceed a threshold // In pinot we just skip (previous pages * page limit) items and take the next (number of page limit) items nextPageToken, err = SerializePageToken(&PinotVisibilityPageToken{From: token.From + numOfActualHits}) if err != nil { return nil, err } response.NextPageToken = make([]byte, len(nextPageToken)) copy(response.NextPageToken, nextPageToken) } return response, nil } func (c *PinotClient) getInternalGetClosedWorkflowExecutionResponse(resp *pinot.BrokerResponse) ( *p.InternalGetClosedWorkflowExecutionResponse, error, ) { if resp == nil { return nil, nil } response := &p.InternalGetClosedWorkflowExecutionResponse{} schema := resp.ResultTable.DataSchema // get the schema to map results columnNames := schema.ColumnNames actualHits := resp.ResultTable.Rows var err error response.Execution, err = ConvertSearchResultToVisibilityRecord(actualHits[0], columnNames) if err != nil { return nil, err } return response, nil } ================================================ FILE: common/pinot/pinot_client_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pinot import ( "fmt" "net/http" "net/http/httptest" "strings" "testing" "time" "github.com/startreedata/pinot-client-go/pinot" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) var ( testIndex = "test-index" testDomain = "test-domain" testDomainID = "bfd5c907-f899-4baf-a7b2-2ab85e623ebd" testPageSize = 10 testEarliestTime = int64(1547596872371000000) testLatestTime = int64(2547596872371000000) testWorkflowType = "test-wf-type" testWorkflowID = "test-wid" testCloseStatus = int32(1) client = PinotClient{ client: nil, logger: nil, } ) func TestSearch(t *testing.T) { query := "select teamID, count(*) as cnt, sum(homeRuns) as sum_homeRuns from baseballStats group by teamID limit 10" request := &SearchRequest{ Query: query, ListRequest: &p.InternalListWorkflowExecutionsRequest{ NextPageToken: nil, }, } errorRequest := &SearchRequest{ Query: "error", ListRequest: &p.InternalListWorkflowExecutionsRequest{ NextPageToken: []byte("ha-ha"), }, } tests := map[string]struct { inputRequest *SearchRequest expectedError error server *httptest.Server }{ "Case1-1: error internal server case": { inputRequest: errorRequest, expectedError: &types.InternalServiceError{ Message: fmt.Sprintf("Pinot Search failed, caught http exception when querying Pinot: 400 Bad Request"), }, server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) })), }, "Case1-2: error json conversion case": { inputRequest: errorRequest, expectedError: &types.InternalServiceError{ Message: fmt.Sprintf("Get NextPage token failed, unable to deserialize page token. err: invalid character 'h' looking for beginning of value"), }, server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) assert.Equal(t, "POST", r.Method) assert.True(t, strings.HasSuffix(r.RequestURI, "/query/sql")) fmt.Fprintln(w, "{\"resultTable\":{\"dataSchema\":{\"columnDataTypes\":[\"LONG\"],\"columnNames\":[\"cnt\"]},\"rows\":[[97.889]]},\"exceptions\":[],\"numServersQueried\":1,\"numServersResponded\":1,\"numSegmentsQueried\":1,\"numSegmentsProcessed\":1,\"numSegmentsMatched\":1,\"numConsumingSegmentsQueried\":0,\"numDocsScanned\":97889,\"numEntriesScannedInFilter\":0,\"numEntriesScannedPostFilter\":0,\"numGroupsLimitReached\":false,\"totalDocs\":97889,\"timeUsedMs\":5,\"segmentStatistics\":[],\"traceInfo\":{},\"minConsumingFreshnessTimeMs\":0}") })), }, "Case2: normal case": { inputRequest: request, expectedError: nil, server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) assert.Equal(t, "POST", r.Method) assert.True(t, strings.HasSuffix(r.RequestURI, "/query/sql")) fmt.Fprintln(w, "{\"resultTable\":{\"dataSchema\":{\"columnDataTypes\":[\"LONG\"],\"columnNames\":[\"cnt\"]},\"rows\":[[97889]]},\"exceptions\":[],\"numServersQueried\":1,\"numServersResponded\":1,\"numSegmentsQueried\":1,\"numSegmentsProcessed\":1,\"numSegmentsMatched\":1,\"numConsumingSegmentsQueried\":0,\"numDocsScanned\":97889,\"numEntriesScannedInFilter\":0,\"numEntriesScannedPostFilter\":0,\"numGroupsLimitReached\":false,\"totalDocs\":97889,\"timeUsedMs\":5,\"segmentStatistics\":[],\"traceInfo\":{},\"minConsumingFreshnessTimeMs\":0}") })), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ts := test.server defer ts.Close() pinotConnection, err := pinot.NewFromBrokerList([]string{ts.URL}) assert.NotNil(t, pinotConnection) assert.Nil(t, err) pinotClient := NewPinotClient(pinotConnection, testlogger.New(t), &config.PinotVisibilityConfig{ Table: "", ServiceName: "", }) actualOutput, err := pinotClient.Search(test.inputRequest) if test.expectedError != nil { assert.Nil(t, actualOutput) assert.Equal(t, test.expectedError.Error(), err.Error()) } else { assert.NotNil(t, actualOutput) assert.Nil(t, err) } }) } } func TestCountByQuery(t *testing.T) { errorQuery := "error" query := "select teamID, count(*) as cnt, sum(homeRuns) as sum_homeRuns from baseballStats group by teamID limit 10" tests := map[string]struct { inputQuery string expectedOutput int64 expectedError error server *httptest.Server }{ "Case1-1: error internal server case": { inputQuery: errorQuery, expectedOutput: 0, expectedError: &types.InternalServiceError{ Message: fmt.Sprintf("CountWorkflowExecutions ExecuteSQL failed, caught http exception when querying Pinot: 400 Bad Request"), }, server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) })), }, "Case1-2: error json conversion case": { inputQuery: errorQuery, expectedOutput: -1, expectedError: &types.InternalServiceError{ Message: fmt.Sprintf("can't convert result to integer!, query = error, query result = 97.889, err = strconv.ParseInt: parsing \"97.889\": invalid syntax"), }, server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) assert.Equal(t, "POST", r.Method) assert.True(t, strings.HasSuffix(r.RequestURI, "/query/sql")) fmt.Fprintln(w, "{\"resultTable\":{\"dataSchema\":{\"columnDataTypes\":[\"LONG\"],\"columnNames\":[\"cnt\"]},\"rows\":[[97.889]]},\"exceptions\":[],\"numServersQueried\":1,\"numServersResponded\":1,\"numSegmentsQueried\":1,\"numSegmentsProcessed\":1,\"numSegmentsMatched\":1,\"numConsumingSegmentsQueried\":0,\"numDocsScanned\":97889,\"numEntriesScannedInFilter\":0,\"numEntriesScannedPostFilter\":0,\"numGroupsLimitReached\":false,\"totalDocs\":97889,\"timeUsedMs\":5,\"segmentStatistics\":[],\"traceInfo\":{},\"minConsumingFreshnessTimeMs\":0}") })), }, "Case2: normal case": { inputQuery: query, expectedOutput: 97889, expectedError: nil, server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) assert.Equal(t, "POST", r.Method) assert.True(t, strings.HasSuffix(r.RequestURI, "/query/sql")) fmt.Fprintln(w, "{\"resultTable\":{\"dataSchema\":{\"columnDataTypes\":[\"LONG\"],\"columnNames\":[\"cnt\"]},\"rows\":[[97889]]},\"exceptions\":[],\"numServersQueried\":1,\"numServersResponded\":1,\"numSegmentsQueried\":1,\"numSegmentsProcessed\":1,\"numSegmentsMatched\":1,\"numConsumingSegmentsQueried\":0,\"numDocsScanned\":97889,\"numEntriesScannedInFilter\":0,\"numEntriesScannedPostFilter\":0,\"numGroupsLimitReached\":false,\"totalDocs\":97889,\"timeUsedMs\":5,\"segmentStatistics\":[],\"traceInfo\":{},\"minConsumingFreshnessTimeMs\":0}") })), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ts := test.server defer ts.Close() pinotConnection, err := pinot.NewFromBrokerList([]string{ts.URL}) assert.NotNil(t, pinotConnection) assert.Nil(t, err) pinotClient := NewPinotClient(pinotConnection, testlogger.New(t), &config.PinotVisibilityConfig{ Table: "", ServiceName: "", }) actualOutput, err := pinotClient.CountByQuery(test.inputQuery) assert.Equal(t, test.expectedOutput, actualOutput) if test.expectedError != nil { assert.Equal(t, test.expectedError.Error(), err.Error()) } else { assert.Nil(t, err) } }) } } func TestSearchAggr(t *testing.T) { query := "select teamID, count(*) as cnt, sum(homeRuns) as sum_homeRuns from baseballStats group by teamID limit 10" request := &SearchRequest{ Query: query, ListRequest: &p.InternalListWorkflowExecutionsRequest{ NextPageToken: nil, }, } errorRequest := &SearchRequest{ Query: "error", ListRequest: &p.InternalListWorkflowExecutionsRequest{ NextPageToken: []byte("ha-ha"), }, } tests := map[string]struct { inputRequest *SearchRequest expectedError error server *httptest.Server }{ "Case1-1: error internal server case": { inputRequest: errorRequest, expectedError: &types.InternalServiceError{ Message: fmt.Sprintf("Pinot SearchAggr failed, caught http exception when querying Pinot: 400 Bad Request"), }, server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) })), }, "Case2: normal case": { inputRequest: request, expectedError: nil, server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) assert.Equal(t, "POST", r.Method) assert.True(t, strings.HasSuffix(r.RequestURI, "/query/sql")) fmt.Fprintln(w, "{\"resultTable\":{\"dataSchema\":{\"columnDataTypes\":[\"LONG\"],\"columnNames\":[\"cnt\"]},\"rows\":[[\"test-domain\", 10]]},\"exceptions\":[],\"numServersQueried\":1,\"numServersResponded\":1,\"numSegmentsQueried\":1,\"numSegmentsProcessed\":1,\"numSegmentsMatched\":1,\"numConsumingSegmentsQueried\":0,\"numDocsScanned\":97889,\"numEntriesScannedInFilter\":0,\"numEntriesScannedPostFilter\":0,\"numGroupsLimitReached\":false,\"totalDocs\":97889,\"timeUsedMs\":5,\"segmentStatistics\":[],\"traceInfo\":{},\"minConsumingFreshnessTimeMs\":0}") })), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ts := test.server defer ts.Close() pinotConnection, err := pinot.NewFromBrokerList([]string{ts.URL}) assert.NotNil(t, pinotConnection) assert.Nil(t, err) pinotClient := NewPinotClient(pinotConnection, testlogger.New(t), &config.PinotVisibilityConfig{ Table: "", ServiceName: "", }) actualOutput, err := pinotClient.SearchAggr(test.inputRequest) if test.expectedError != nil { assert.Nil(t, actualOutput) assert.Equal(t, test.expectedError.Error(), err.Error()) } else { assert.NotNil(t, actualOutput) assert.Nil(t, err) } }) } } func TestGetTableName(t *testing.T) { assert.Equal(t, "", client.GetTableName()) } func TestBuildMap(t *testing.T) { columnName := []string{"WorkflowID", "RunID", "WorkflowType", "DomainID", "StartTime", "ExecutionTime", "CloseTime", "CloseStatus", "HistoryLength", "TaskList", "IsCron", "NumClusters", "UpdateTime", "CustomIntField", "CustomStringField"} hit := []interface{}{"wfid", "rid", "wftype", "domainid", testEarliestTime, testEarliestTime, testLatestTime, 1, 1, "tsklst", true, 1, testEarliestTime, 1, "some string"} tests := map[string]struct { inputColumnNames []string inputHit []interface{} expectedMap map[string]interface{} }{ "Case1: with everything": { inputColumnNames: columnName, inputHit: hit, expectedMap: map[string]interface{}{"CloseStatus": 1, "CloseTime": int64(2547596872371000000), "CustomIntField": 1, "CustomStringField": "some string", "DomainID": "domainid", "ExecutionTime": int64(1547596872371000000), "HistoryLength": 1, "IsCron": true, "NumClusters": 1, "RunID": "rid", "StartTime": int64(1547596872371000000), "TaskList": "tsklst", "UpdateTime": int64(1547596872371000000), "WorkflowID": "wfid", "WorkflowType": "wftype"}, }, "Case2: nil result": { inputColumnNames: nil, inputHit: nil, expectedMap: map[string]interface{}{}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { resMap := buildMap(test.inputHit, test.inputColumnNames) assert.Equal(t, test.expectedMap, resMap) }) } } func TestGetInternalListWorkflowExecutionsResponse(t *testing.T) { columnName := []string{"WorkflowID", "RunID", "WorkflowType", "DomainID", "StartTime", "ExecutionTime", "CloseTime", "CloseStatus", "HistoryLength", "Encoding", "TaskList", "IsCron", "NumClusters", "UpdateTime", "Attr"} hit1 := []interface{}{"wfid1", "rid1", "wftype1", "domainid1", testEarliestTime, testEarliestTime, testLatestTime, 1, 1, "encode1", "tsklst1", true, 1, testEarliestTime, "null"} hit2 := []interface{}{"wfid2", "rid2", "wftype2", "domainid2", testEarliestTime, testEarliestTime, testLatestTime, 1, 1, "encode2", "tsklst2", false, 1, testEarliestTime, "null"} hit3 := []interface{}{"wfid3", "rid3", "wftype3", "domainid3", testEarliestTime, testEarliestTime, testLatestTime, 1, 1, "encode3", "tsklst3", false, 1, testEarliestTime, "null"} hit4 := []interface{}{"wfid4", "rid4", "wftype4", "domainid4", testEarliestTime, testEarliestTime, testLatestTime, 1, 1, "encode4", "tsklst4", false, 1, testEarliestTime, "null"} hit5 := []interface{}{"wfid5", "rid5", "wftype5", "domainid5", testEarliestTime, testEarliestTime, testLatestTime, 1, 1, "encode5", "tsklst5", false, 1, testEarliestTime, "null"} brokerResponse := &pinot.BrokerResponse{ AggregationResults: nil, SelectionResults: nil, ResultTable: &pinot.ResultTable{ DataSchema: pinot.RespSchema{ ColumnDataTypes: nil, ColumnNames: columnName, }, Rows: [][]interface{}{ hit1, hit2, hit3, hit4, hit5, }, }, Exceptions: nil, TraceInfo: nil, NumServersQueried: 1, NumServersResponded: 1, NumSegmentsQueried: 1, NumSegmentsProcessed: 1, NumSegmentsMatched: 1, NumConsumingSegmentsQueried: 1, NumDocsScanned: 10, NumEntriesScannedInFilter: 1, NumEntriesScannedPostFilter: 1, NumGroupsLimitReached: false, TotalDocs: 1, TimeUsedMs: 1, MinConsumingFreshnessTimeMs: 1, } token := &PinotVisibilityPageToken{ From: 0, } // Cannot use a table test, because they are not checking the same fields result, err := client.getInternalListWorkflowExecutionsResponse(brokerResponse, nil, token, 5, 33) assert.Equal(t, "wfid1", result.Executions[0].WorkflowID) assert.Equal(t, "rid1", result.Executions[0].RunID) assert.Equal(t, "wftype1", result.Executions[0].WorkflowType) assert.Equal(t, "domainid1", result.Executions[0].DomainID) assert.Equal(t, time.UnixMilli(testEarliestTime), result.Executions[0].StartTime) assert.Equal(t, time.UnixMilli(testEarliestTime), result.Executions[0].ExecutionTime) assert.Equal(t, time.UnixMilli(testLatestTime), result.Executions[0].CloseTime) assert.Equal(t, types.WorkflowExecutionCloseStatus(1), *result.Executions[0].Status) assert.Equal(t, int64(1), result.Executions[0].HistoryLength) assert.Equal(t, "tsklst1", result.Executions[0].TaskList) assert.Equal(t, true, result.Executions[0].IsCron) assert.Equal(t, int16(1), result.Executions[0].NumClusters) assert.Equal(t, time.UnixMilli(testEarliestTime), result.Executions[0].UpdateTime) assert.Equal(t, "wfid2", result.Executions[1].WorkflowID) assert.Equal(t, "rid2", result.Executions[1].RunID) assert.Equal(t, "wftype2", result.Executions[1].WorkflowType) assert.Equal(t, "domainid2", result.Executions[1].DomainID) assert.Equal(t, time.UnixMilli(testEarliestTime), result.Executions[1].StartTime) assert.Equal(t, time.UnixMilli(testEarliestTime), result.Executions[1].ExecutionTime) assert.Equal(t, time.UnixMilli(testLatestTime), result.Executions[1].CloseTime) assert.Equal(t, types.WorkflowExecutionCloseStatus(1), *result.Executions[1].Status) assert.Equal(t, int64(1), result.Executions[1].HistoryLength) assert.Equal(t, "tsklst2", result.Executions[1].TaskList) assert.Equal(t, false, result.Executions[1].IsCron) assert.Equal(t, int16(1), result.Executions[1].NumClusters) assert.Equal(t, time.UnixMilli(testEarliestTime), result.Executions[1].UpdateTime) assert.Nil(t, err) responseToken := result.NextPageToken unmarshalResponseToken, err := GetNextPageToken(responseToken) if err != nil { panic(fmt.Sprintf("Unmarshal error in PinotClient test %s", err)) } assert.Equal(t, 5, unmarshalResponseToken.From) // check if record is not valid isRecordValid := func(rec *p.InternalVisibilityWorkflowExecutionInfo) bool { return false } emptyResult, err := client.getInternalListWorkflowExecutionsResponse(brokerResponse, isRecordValid, nil, 10, 33) assert.Equal(t, 0, len(emptyResult.Executions)) assert.Nil(t, err) // check nil input nilResult, err := client.getInternalListWorkflowExecutionsResponse(nil, isRecordValid, nil, 10, 33) assert.Equal(t, &p.InternalListWorkflowExecutionsResponse{}, nilResult) assert.Nil(t, err) } func TestGetInternalGetClosedWorkflowExecutionResponse(t *testing.T) { columnName := []string{"WorkflowID", "RunID", "WorkflowType", "DomainID", "StartTime", "ExecutionTime", "CloseTime", "CloseStatus", "HistoryLength", "Encoding", "TaskList", "IsCron", "NumClusters", "UpdateTime", "Attr"} hit1 := []interface{}{"wfid1", "rid1", "wftype1", "domainid1", testEarliestTime, testEarliestTime, testLatestTime, 1, 1, "encode1", "tsklst1", true, 1, testEarliestTime, "null"} brokerResponse := &pinot.BrokerResponse{ AggregationResults: nil, SelectionResults: nil, ResultTable: &pinot.ResultTable{ DataSchema: pinot.RespSchema{ ColumnDataTypes: nil, ColumnNames: columnName, }, Rows: [][]interface{}{ hit1, }, }, Exceptions: nil, TraceInfo: nil, NumServersQueried: 1, NumServersResponded: 1, NumSegmentsQueried: 1, NumSegmentsProcessed: 1, NumSegmentsMatched: 1, NumConsumingSegmentsQueried: 1, NumDocsScanned: 1, NumEntriesScannedInFilter: 1, NumEntriesScannedPostFilter: 1, NumGroupsLimitReached: false, TotalDocs: 1, TimeUsedMs: 1, MinConsumingFreshnessTimeMs: 1, } tests := map[string]struct { input *pinot.BrokerResponse isInputEmpty bool expectedError error }{ "Case1: empty case": { input: nil, isInputEmpty: true, expectedError: nil, }, "Case2: with everything": { input: brokerResponse, isInputEmpty: false, expectedError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { actualOutput, actualError := client.getInternalGetClosedWorkflowExecutionResponse(test.input) if test.isInputEmpty { assert.Nil(t, actualOutput) assert.Nil(t, actualError) } else { assert.Equal(t, "wfid1", actualOutput.Execution.WorkflowID) assert.Equal(t, "rid1", actualOutput.Execution.RunID) assert.Equal(t, "wftype1", actualOutput.Execution.WorkflowType) assert.Equal(t, "domainid1", actualOutput.Execution.DomainID) assert.Equal(t, time.UnixMilli(testEarliestTime), actualOutput.Execution.StartTime) assert.Equal(t, time.UnixMilli(testEarliestTime), actualOutput.Execution.ExecutionTime) assert.Equal(t, time.UnixMilli(testLatestTime), actualOutput.Execution.CloseTime) assert.Equal(t, types.WorkflowExecutionCloseStatus(1), *actualOutput.Execution.Status) assert.Equal(t, int64(1), actualOutput.Execution.HistoryLength) assert.Equal(t, "tsklst1", actualOutput.Execution.TaskList) assert.Equal(t, true, actualOutput.Execution.IsCron) assert.Equal(t, int16(1), actualOutput.Execution.NumClusters) assert.Equal(t, time.UnixMilli(testEarliestTime), actualOutput.Execution.UpdateTime) assert.Nil(t, actualError) } }) } } ================================================ FILE: common/pinot/response_utility.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pinot import ( "encoding/json" "fmt" "time" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const ( Memo = "Memo" Attr = "Attr" ) func buildMap(hit []interface{}, columnNames []string) map[string]interface{} { systemKeyMap := make(map[string]interface{}) for i := 0; i < len(columnNames); i++ { key := columnNames[i] systemKeyMap[key] = hit[i] } return systemKeyMap } // VisibilityRecord is a struct of doc for deserialization // this is different from InternalVisibilityWorkflowExecutionInfo // use this to deserialize the systemKeyMap from Pinot response type VisibilityRecord struct { WorkflowID string RunID string WorkflowType string DomainID string StartTime int64 ExecutionTime int64 CloseTime int64 CloseStatus int HistoryLength int64 TaskList string IsCron bool NumClusters int16 ClusterAttributeScope string ClusterAttributeName string UpdateTime int64 ShardID int16 } func ConvertSearchResultToVisibilityRecord(hit []interface{}, columnNames []string) (*p.InternalVisibilityWorkflowExecutionInfo, error) { if len(hit) != len(columnNames) { return nil, fmt.Errorf("length of hit (%v) is not equal with length of columnNames(%v)", len(hit), len(columnNames)) } systemKeyMap := buildMap(hit, columnNames) jsonSystemKeyMap, err := json.Marshal(systemKeyMap) if err != nil { return nil, fmt.Errorf("unable to marshal systemKeyMap") } attributeMap := make(map[string]interface{}) var memo *p.DataBlob if systemKeyMap[Attr] != nil { attrMapStr, ok := systemKeyMap[Attr].(string) if !ok { return nil, fmt.Errorf(`assertion error. Can't convert systemKeyMap[Attr] to string. Found %T`, systemKeyMap[Attr]) } err = json.Unmarshal([]byte(attrMapStr), &attributeMap) if err != nil { return nil, fmt.Errorf("unable to Unmarshal searchAttribute map: %s", err.Error()) } if attributeMap[Memo] != nil { memo, err = convertMemo(attributeMap[Memo]) if err != nil { return nil, fmt.Errorf("unable to convert memo: %s", err.Error()) } } delete(attributeMap, Memo) // cleanup after we get memo from search attribute } // if memo is empty, set it to nil if memo != nil && len(memo.GetData()) == 0 { memo = nil } var source *VisibilityRecord err = json.Unmarshal(jsonSystemKeyMap, &source) if err != nil { return nil, fmt.Errorf("unable to Unmarshal systemKeyMap: %s", err.Error()) } record := &p.InternalVisibilityWorkflowExecutionInfo{ DomainID: source.DomainID, WorkflowType: source.WorkflowType, WorkflowID: source.WorkflowID, RunID: source.RunID, TypeName: source.WorkflowType, StartTime: time.UnixMilli(source.StartTime), // be careful: source.StartTime is in milliseconds ExecutionTime: time.UnixMilli(source.ExecutionTime), TaskList: source.TaskList, IsCron: source.IsCron, NumClusters: source.NumClusters, ClusterAttributeScope: source.ClusterAttributeScope, ClusterAttributeName: source.ClusterAttributeName, ShardID: source.ShardID, SearchAttributes: attributeMap, Memo: memo, } if source.UpdateTime > 0 { record.UpdateTime = time.UnixMilli(source.UpdateTime) } if source.CloseTime > 0 { record.CloseTime = time.UnixMilli(source.CloseTime) record.Status = toWorkflowExecutionCloseStatus(source.CloseStatus) record.HistoryLength = source.HistoryLength } return record, nil } func convertMemo(memoRaw interface{}) (*p.DataBlob, error) { db := p.DataBlob{} memoRawStr, ok := memoRaw.(string) if !ok { return nil, fmt.Errorf("memoRaw is not a String: %T", memoRaw) } err := json.Unmarshal([]byte(memoRawStr), &db) if err != nil { return nil, fmt.Errorf("unable to unmarshal memoRawStr: %s", err.Error()) } return &db, nil } func toWorkflowExecutionCloseStatus(status int) *types.WorkflowExecutionCloseStatus { if status < 0 { return nil } closeStatus := types.WorkflowExecutionCloseStatus(status) return &closeStatus } ================================================ FILE: common/pinot/response_utility_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package pinot import ( "encoding/json" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/constants" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestConvertSearchResultToVisibilityRecord(t *testing.T) { columnName := []string{"WorkflowID", "RunID", "WorkflowType", "DomainID", "StartTime", "ExecutionTime", "CloseTime", "CloseStatus", "HistoryLength", "TaskList", "IsCron", "NumClusters", "UpdateTime", "Attr", "ClusterAttributeScope", "ClusterAttributeName"} closeStatus := types.WorkflowExecutionCloseStatusFailed sampleRawMemo := &types.Memo{ Fields: map[string][]byte{ `"Service"`: []byte(`"serverName1"`), }, } serializer := p.NewPayloadSerializer() sampleEncodedMemo, err := serializer.SerializeVisibilityMemo(sampleRawMemo, constants.EncodingTypeThriftRW) assert.NoError(t, err) errorMapRaw1 := map[string]interface{}{"Memo": 123} errorMap1, err := json.Marshal(errorMapRaw1) errorMapRaw2 := map[string]interface{}{"Memo": "123"} errorMap2, err := json.Marshal(errorMapRaw2) tests := map[string]struct { inputColumnNames []string inputHit []interface{} expectedVisibilityRecord *p.InternalVisibilityWorkflowExecutionInfo memoCheck bool expectErr error }{ "Case1: nil result": { inputColumnNames: nil, inputHit: []interface{}{"wfid", "rid", "wftype", "domainid", testEarliestTime, testEarliestTime, testLatestTime, -1, 1, "tsklst", true, 1, testEarliestTime, "{}", "region", "us-east"}, expectedVisibilityRecord: nil, expectErr: fmt.Errorf("length of hit (16) is not equal with length of columnNames(0)"), }, "Case2-1: marshal system key error case": { inputColumnNames: columnName, inputHit: []interface{}{"wfid", "rid", "wftype", "domainid", testEarliestTime, testEarliestTime, testLatestTime, 1, 1, "tsklst", true, 1, testEarliestTime, make(chan int), "region", "us-east"}, expectedVisibilityRecord: nil, expectErr: fmt.Errorf("unable to marshal systemKeyMap"), }, "Case2-2: unmarshal system key error case": { inputColumnNames: columnName, inputHit: []interface{}{"wfid", "rid", "wftype", "domainid", testEarliestTime, testEarliestTime, testLatestTime, 1, "1", "tsklst", true, 1, testEarliestTime, `{"CustomStringField": "customA and customB or customC", "CustomDoubleField": 3.14}`, "region", "us-east"}, expectedVisibilityRecord: nil, expectErr: fmt.Errorf("unable to Unmarshal systemKeyMap: json: cannot unmarshal string into Go struct field VisibilityRecord.HistoryLength of type int64"), }, "Case2-3: Attr to string error case": { inputColumnNames: []string{"Attr"}, inputHit: []interface{}{123}, expectedVisibilityRecord: nil, expectErr: fmt.Errorf(`assertion error. Can't convert systemKeyMap[Attr] to string. Found int`), }, "Case2-4: Attr unmarshal to map error case": { inputColumnNames: []string{"Attr"}, inputHit: []interface{}{"123"}, expectedVisibilityRecord: nil, expectErr: fmt.Errorf(`unable to Unmarshal searchAttribute map: json: cannot unmarshal number into Go value of type map[string]interface {}`), }, "Case2-4: Memo to string error case": { inputColumnNames: []string{"Attr"}, inputHit: []interface{}{string(errorMap1)}, expectedVisibilityRecord: nil, expectErr: fmt.Errorf(`unable to convert memo: memoRaw is not a String: float64`), }, "Case2-5: Memo unmarshal error case": { inputColumnNames: []string{"Attr"}, inputHit: []interface{}{string(errorMap2)}, expectedVisibilityRecord: nil, expectErr: fmt.Errorf(`unable to convert memo: unable to unmarshal memoRawStr: json: cannot unmarshal number into Go value of type persistence.DataBlob`), }, "Case3-1: closed wf with everything except for an empty Attr": { inputColumnNames: columnName, inputHit: []interface{}{"wfid", "rid", "wftype", "domainid", testEarliestTime, testEarliestTime, testLatestTime, -1, 1, "tsklst", true, 1, testEarliestTime, "{}", "region", "us-east"}, expectedVisibilityRecord: &p.InternalVisibilityWorkflowExecutionInfo{ DomainID: "domainid", WorkflowType: "wftype", WorkflowID: "wfid", RunID: "rid", TypeName: "wftype", StartTime: time.UnixMilli(testEarliestTime), ExecutionTime: time.UnixMilli(testEarliestTime), CloseTime: time.UnixMilli(testLatestTime), Status: nil, HistoryLength: 1, Memo: nil, TaskList: "tsklst", IsCron: true, NumClusters: 1, ClusterAttributeScope: "region", ClusterAttributeName: "us-east", UpdateTime: time.UnixMilli(testEarliestTime), SearchAttributes: map[string]interface{}{}, ShardID: 0, }, }, "Case3-2: closed wf with everything": { inputColumnNames: columnName, inputHit: []interface{}{"wfid", "rid", "wftype", "domainid", testEarliestTime, testEarliestTime, testLatestTime, 1, 1, "tsklst", true, 1, testEarliestTime, `{"CustomStringField": "customA and customB or customC", "CustomDoubleField": 3.14}`, "region", "us-east"}, expectedVisibilityRecord: &p.InternalVisibilityWorkflowExecutionInfo{ DomainID: "domainid", WorkflowType: "wftype", WorkflowID: "wfid", RunID: "rid", TypeName: "wftype", StartTime: time.UnixMilli(testEarliestTime), ExecutionTime: time.UnixMilli(testEarliestTime), CloseTime: time.UnixMilli(testLatestTime), Status: &closeStatus, HistoryLength: 1, Memo: nil, TaskList: "tsklst", IsCron: true, NumClusters: 1, ClusterAttributeScope: "region", ClusterAttributeName: "us-east", UpdateTime: time.UnixMilli(testEarliestTime), SearchAttributes: map[string]interface{}{"CustomStringField": "customA and customB or customC", "CustomDoubleField": 3.14}, ShardID: 0, }, }, "Case4: open wf with everything": { inputColumnNames: columnName, inputHit: []interface{}{"wfid", "rid", "wftype", "domainid", testEarliestTime, testEarliestTime, -1, -1, -1, "tsklst", true, 1, testEarliestTime, `{"CustomStringField": "customA and customB or customC", "CustomDoubleField": 3.14}`, "region", "us-east"}, expectedVisibilityRecord: &p.InternalVisibilityWorkflowExecutionInfo{ DomainID: "domainid", WorkflowType: "wftype", WorkflowID: "wfid", RunID: "rid", TypeName: "wftype", StartTime: time.UnixMilli(testEarliestTime), ExecutionTime: time.UnixMilli(testEarliestTime), Memo: nil, TaskList: "tsklst", IsCron: true, NumClusters: 1, ClusterAttributeScope: "region", ClusterAttributeName: "us-east", UpdateTime: time.UnixMilli(testEarliestTime), SearchAttributes: map[string]interface{}{"CustomStringField": "customA and customB or customC", "CustomDoubleField": 3.14}, ShardID: 0, }, memoCheck: true, }, "Case5-1: open wf with memo": { inputColumnNames: columnName, inputHit: []interface{}{"wfid", "rid", "wftype", "domainid", testEarliestTime, testEarliestTime, -1, -1, -1, "tsklst", true, 1, testEarliestTime, `{"Memo":"{\"Encoding\":\"thriftrw\",\"Data\":\"WQ0ACgsLAAAAAQAAAAkiU2VydmljZSIAAAANInNlcnZlck5hbWUxIgA=\"}"}`, "region", "us-east"}, expectedVisibilityRecord: &p.InternalVisibilityWorkflowExecutionInfo{ DomainID: "domainid", WorkflowType: "wftype", WorkflowID: "wfid", RunID: "rid", TypeName: "wftype", StartTime: time.UnixMilli(testEarliestTime), ExecutionTime: time.UnixMilli(testEarliestTime), Memo: sampleEncodedMemo, TaskList: "tsklst", IsCron: true, NumClusters: 1, ClusterAttributeScope: "region", ClusterAttributeName: "us-east", UpdateTime: time.UnixMilli(testEarliestTime), SearchAttributes: map[string]interface{}{"CustomStringField": "customA and customB or customC", "CustomDoubleField": 3.14}, ShardID: 0, }, memoCheck: true, }, "Case5-2: open wf with empty memo": { inputColumnNames: columnName, inputHit: []interface{}{"wfid", "rid", "wftype", "domainid", testEarliestTime, testEarliestTime, -1, -1, -1, "tsklst", true, 1, testEarliestTime, `{"Memo":"{\"Encoding\":\"\",\"Data\":\"\"}"}`, "region", "us-east"}, expectedVisibilityRecord: &p.InternalVisibilityWorkflowExecutionInfo{ DomainID: "domainid", WorkflowType: "wftype", WorkflowID: "wfid", RunID: "rid", TypeName: "wftype", StartTime: time.UnixMilli(testEarliestTime), ExecutionTime: time.UnixMilli(testEarliestTime), Memo: nil, TaskList: "tsklst", IsCron: true, NumClusters: 1, ClusterAttributeScope: "region", ClusterAttributeName: "us-east", UpdateTime: time.UnixMilli(testEarliestTime), SearchAttributes: map[string]interface{}{"CustomStringField": "customA and customB or customC", "CustomDoubleField": 3.14}, ShardID: 0, }, memoCheck: true, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { assert.NotPanics(t, func() { visibilityRecord, err := ConvertSearchResultToVisibilityRecord(test.inputHit, test.inputColumnNames) if !test.memoCheck { assert.Equal(t, test.expectedVisibilityRecord, visibilityRecord) assert.Equal(t, test.expectErr, err) } else { assert.Equal(t, test.expectedVisibilityRecord.Memo.GetData(), visibilityRecord.Memo.GetData()) assert.Equal(t, test.expectedVisibilityRecord.Memo.GetEncoding(), visibilityRecord.Memo.GetEncoding()) } }) }) } } // This is the process of figuring out how to encode/decode memo for Pinot func TestDeserializeMemoMockingE2E(t *testing.T) { sampleRawMemo := &types.Memo{ Fields: map[string][]byte{ "Service": []byte("serverName1"), }, } serializer := p.NewPayloadSerializer() sampleEncodedMemo, err := serializer.SerializeVisibilityMemo(sampleRawMemo, constants.EncodingTypeThriftRW) assert.NoError(t, err) // not a human-readable string assert.Equal(t, "Y\r\x00\n\v\v\x00\x00\x00\x01\x00\x00\x00\aService\x00\x00\x00\vserverName1\x00", string(sampleEncodedMemo.GetData())) marshaledMemo, err := json.Marshal(sampleEncodedMemo) assert.NoError(t, err) // after marshal, data becomes a human-readable char array assert.Equal(t, `{"Encoding":"thriftrw","Data":"WQ0ACgsLAAAAAQAAAAdTZXJ2aWNlAAAAC3NlcnZlck5hbWUxAA=="}`, string(marshaledMemo)) // must-do step, to give it a type, or we can't convert it to a string in the reading side. marshaledMemoStr := string(marshaledMemo) // mock the reading side unmarshaledRawData := p.DataBlob{} // marshaledMemoStr still knows that it is a DataBlob type err = json.Unmarshal([]byte(marshaledMemoStr), &unmarshaledRawData) assert.NoError(t, err) sampleDecodedMemo, err := serializer.DeserializeVisibilityMemo(&unmarshaledRawData) assert.NoError(t, err) assert.Equal(t, "serverName1", string(sampleDecodedMemo.Fields["Service"])) } ================================================ FILE: common/pprof.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package common //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination pprof_mock.go -package common github.com/uber/cadence/common PProfInitializer type ( // PProfInitializer initialize the pprof based on config PProfInitializer interface { Start() error } ) ================================================ FILE: common/pprof_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: pprof.go // // Generated by this command: // // mockgen -package common -source pprof.go -destination pprof_mock.go -package common github.com/uber/cadence/common PProfInitializer // // Package common is a generated GoMock package. package common import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockPProfInitializer is a mock of PProfInitializer interface. type MockPProfInitializer struct { ctrl *gomock.Controller recorder *MockPProfInitializerMockRecorder isgomock struct{} } // MockPProfInitializerMockRecorder is the mock recorder for MockPProfInitializer. type MockPProfInitializerMockRecorder struct { mock *MockPProfInitializer } // NewMockPProfInitializer creates a new mock instance. func NewMockPProfInitializer(ctrl *gomock.Controller) *MockPProfInitializer { mock := &MockPProfInitializer{ctrl: ctrl} mock.recorder = &MockPProfInitializerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPProfInitializer) EXPECT() *MockPProfInitializerMockRecorder { return m.recorder } // Start mocks base method. func (m *MockPProfInitializer) Start() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start") ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockPProfInitializerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockPProfInitializer)(nil).Start)) } ================================================ FILE: common/quotas/caller_bypass.go ================================================ // Copyright (c) 2025 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package quotas import ( "context" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) // CallerBypass encapsulates the logic for bypassing rate limits based on caller type type CallerBypass struct { bypassCallerTypes dynamicproperties.ListPropertyFn } // NewCallerBypass creates a new CallerBypass with the given bypass caller types configuration func NewCallerBypass(bypassCallerTypes dynamicproperties.ListPropertyFn) CallerBypass { return CallerBypass{ bypassCallerTypes: bypassCallerTypes, } } // AllowLimiter checks if a request should be allowed through a Limiter. // It first checks the limiter's Allow() method, and if that returns false, // it checks if the caller type should bypass rate limiting. func (c CallerBypass) AllowLimiter(ctx context.Context, limiter Limiter) bool { if limiter.Allow() { return true } return c.ShouldBypass(ctx) } // AllowPolicy checks if a request should be allowed through a Policy. // It first checks the policy's Allow() method, and if that returns false, // it checks if the caller type should bypass rate limiting. func (c CallerBypass) AllowPolicy(ctx context.Context, policy Policy, info Info) bool { if policy.Allow(info) { return true } return c.ShouldBypass(ctx) } // ShouldBypass checks if the caller type from the context should bypass rate limiting // based on the configured bypass caller types. func (c CallerBypass) ShouldBypass(ctx context.Context) bool { if c.bypassCallerTypes == nil { return false } callerInfo := types.GetCallerInfoFromContext(ctx) bypassCallerTypes := c.bypassCallerTypes() for _, bypassType := range bypassCallerTypes { if bypassTypeStr, ok := bypassType.(string); ok { if types.ParseCallerType(bypassTypeStr) == callerInfo.GetCallerType() { return true } } } return false } ================================================ FILE: common/quotas/caller_bypass_test.go ================================================ // Copyright (c) 2025 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package quotas import ( "context" "testing" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) func TestCallerBypass_AllowLimiter(t *testing.T) { tests := []struct { name string limiterAllows bool bypassCallerTypes []interface{} callerType types.CallerType expected bool }{ { name: "Limiter allows - bypass not checked", limiterAllows: true, bypassCallerTypes: []interface{}{"cli"}, callerType: types.CallerTypeSDK, expected: true, }, { name: "Limiter blocks but caller bypasses", limiterAllows: false, bypassCallerTypes: []interface{}{"cli"}, callerType: types.CallerTypeCLI, expected: true, }, { name: "Limiter blocks and caller doesn't bypass", limiterAllows: false, bypassCallerTypes: []interface{}{"cli"}, callerType: types.CallerTypeSDK, expected: false, }, { name: "Empty bypass list blocks", limiterAllows: false, bypassCallerTypes: []interface{}{}, callerType: types.CallerTypeCLI, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockLimiter := NewMockLimiter(ctrl) mockLimiter.EXPECT().Allow().Return(tt.limiterAllows) bypassFn := func(...dynamicproperties.FilterOption) []interface{} { return tt.bypassCallerTypes } callerBypass := NewCallerBypass(bypassFn) ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(tt.callerType)) result := callerBypass.AllowLimiter(ctx, mockLimiter) if result != tt.expected { t.Errorf("expected %v, got %v", tt.expected, result) } }) } } func TestCallerBypass_AllowPolicy(t *testing.T) { tests := []struct { name string policyAllows bool bypassCallerTypes []interface{} callerType types.CallerType expected bool }{ { name: "Policy allows - bypass not checked", policyAllows: true, bypassCallerTypes: []interface{}{"cli"}, callerType: types.CallerTypeSDK, expected: true, }, { name: "Policy blocks but caller bypasses", policyAllows: false, bypassCallerTypes: []interface{}{"ui", "cli"}, callerType: types.CallerTypeUI, expected: true, }, { name: "Policy blocks and caller doesn't bypass", policyAllows: false, bypassCallerTypes: []interface{}{"internal"}, callerType: types.CallerTypeSDK, expected: false, }, { name: "Empty bypass list blocks", policyAllows: false, bypassCallerTypes: []interface{}{}, callerType: types.CallerTypeCLI, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockPolicy := NewMockPolicy(ctrl) mockPolicy.EXPECT().Allow(Info{Domain: "test"}).Return(tt.policyAllows) bypassFn := func(...dynamicproperties.FilterOption) []interface{} { return tt.bypassCallerTypes } callerBypass := NewCallerBypass(bypassFn) ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(tt.callerType)) result := callerBypass.AllowPolicy(ctx, mockPolicy, Info{Domain: "test"}) if result != tt.expected { t.Errorf("expected %v, got %v", tt.expected, result) } }) } } func TestCallerBypass_ShouldBypass(t *testing.T) { tests := []struct { name string bypassCallerTypes []interface{} callerType types.CallerType expected bool }{ { name: "CLI bypasses when configured", bypassCallerTypes: []interface{}{"cli"}, callerType: types.CallerTypeCLI, expected: true, }, { name: "Multiple types in list", bypassCallerTypes: []interface{}{"cli", "ui", "internal"}, callerType: types.CallerTypeInternal, expected: true, }, { name: "Type not in list doesn't bypass", bypassCallerTypes: []interface{}{"cli"}, callerType: types.CallerTypeSDK, expected: false, }, { name: "Unknown type doesn't bypass", bypassCallerTypes: []interface{}{"cli", "ui"}, callerType: types.CallerTypeUnknown, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { bypassFn := func(...dynamicproperties.FilterOption) []interface{} { return tt.bypassCallerTypes } callerBypass := NewCallerBypass(bypassFn) ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(tt.callerType)) result := callerBypass.ShouldBypass(ctx) if result != tt.expected { t.Errorf("expected %v, got %v", tt.expected, result) } }) } } func TestCallerBypass_NilBypassFunction(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() callerBypass := NewCallerBypass(nil) ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(types.CallerTypeCLI)) if callerBypass.ShouldBypass(ctx) { t.Error("expected ShouldBypass to return false with nil bypass function") } mockLimiter := NewMockLimiter(ctrl) mockLimiter.EXPECT().Allow().Return(false) if callerBypass.AllowLimiter(ctx, mockLimiter) { t.Error("expected AllowLimiter to return false when limiter blocks and bypass function is nil") } } ================================================ FILE: common/quotas/collection.go ================================================ // Copyright (c) 2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package=$GOPACKAGE -destination=collection_mock.go github.com/uber/cadence/common/quotas ICollection //go:generate mockgen -package=$GOPACKAGE -destination=limiterfactory_mock.go github.com/uber/cadence/common/quotas LimiterFactory package quotas import ( "sync" ) // LimiterFactory is used to create a Limiter for a given domain type LimiterFactory interface { // GetLimiter returns a new Limiter for the given domain GetLimiter(domain string) Limiter } // Collection stores a map of limiters by key type Collection struct { mu sync.RWMutex factory LimiterFactory limiters map[string]Limiter } type ICollection interface { For(key string) Limiter } var _ ICollection = (*Collection)(nil) // NewCollection create a new limiter collection. // Given factory is called to create new individual limiter. func NewCollection(factory LimiterFactory) *Collection { return &Collection{ factory: factory, limiters: make(map[string]Limiter), } } // For retrieves limiter by a given key. // If limiter for such key does not exists, it creates new one with via factory. func (c *Collection) For(key string) Limiter { c.mu.RLock() limiter, ok := c.limiters[key] c.mu.RUnlock() if !ok { // create a new limiter newLimiter := c.factory.GetLimiter(key) // verify that it is needed and add to map c.mu.Lock() limiter, ok = c.limiters[key] if !ok { c.limiters[key] = newLimiter limiter = newLimiter } c.mu.Unlock() } return limiter } ================================================ FILE: common/quotas/collection_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/quotas (interfaces: ICollection) // // Generated by this command: // // mockgen -package=quotas -destination=collection_mock.go github.com/uber/cadence/common/quotas ICollection // // Package quotas is a generated GoMock package. package quotas import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockICollection is a mock of ICollection interface. type MockICollection struct { ctrl *gomock.Controller recorder *MockICollectionMockRecorder isgomock struct{} } // MockICollectionMockRecorder is the mock recorder for MockICollection. type MockICollectionMockRecorder struct { mock *MockICollection } // NewMockICollection creates a new mock instance. func NewMockICollection(ctrl *gomock.Controller) *MockICollection { mock := &MockICollection{ctrl: ctrl} mock.recorder = &MockICollectionMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockICollection) EXPECT() *MockICollectionMockRecorder { return m.recorder } // For mocks base method. func (m *MockICollection) For(key string) Limiter { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "For", key) ret0, _ := ret[0].(Limiter) return ret0 } // For indicates an expected call of For. func (mr *MockICollectionMockRecorder) For(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "For", reflect.TypeOf((*MockICollection)(nil).For), key) } ================================================ FILE: common/quotas/dynamicratelimiter.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package quotas import ( "context" "math" "sync/atomic" "time" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" ) const _ttl = time.Second * 5 const _minBurst = 1 // DynamicRateLimiter implements a dynamic config wrapper around the rate limiter, // checks for updates to the dynamic config and updates the rate limiter accordingly type DynamicRateLimiter struct { rps RPSFunc rl clock.Ratelimiter timeSource clock.TimeSource ttl time.Duration lastUpdateTime atomic.Pointer[time.Time] minBurst int } type DynamicRateLimiterOpts struct { TTL time.Duration MinBurst int TimeSource clock.TimeSource } var _defaultOpts = DynamicRateLimiterOpts{ TTL: _ttl, MinBurst: _minBurst, TimeSource: clock.NewRealTimeSource(), } // NewDynamicRateLimiter returns a rate limiter which handles dynamic config func NewDynamicRateLimiter(rps RPSFunc) Limiter { return NewDynamicRateLimiterWithOpts(rps, _defaultOpts) } func NewDynamicRateLimiterWithOpts(rps RPSFunc, opts DynamicRateLimiterOpts) Limiter { ts := opts.TimeSource if ts == nil { ts = _defaultOpts.TimeSource } res := &DynamicRateLimiter{ rps: rps, timeSource: ts, ttl: opts.TTL, minBurst: opts.MinBurst, } now := res.timeSource.Now() res.lastUpdateTime.Store(&now) lim, burst := res.getLimitAndBurst() res.rl = clock.NewRateLimiterWithTimeSource(res.timeSource, lim, burst) return res } // Allow immediately returns with true or false indicating if a rate limit // token is available or not func (d *DynamicRateLimiter) Allow() bool { d.maybeRefreshRps() return d.rl.Allow() } // Wait waits up till deadline for a rate limit token func (d *DynamicRateLimiter) Wait(ctx context.Context) error { d.maybeRefreshRps() return d.rl.Wait(ctx) } // Reserve reserves a rate limit token func (d *DynamicRateLimiter) Reserve() clock.Reservation { d.maybeRefreshRps() return d.rl.Reserve() } func (d *DynamicRateLimiter) Limit() rate.Limit { d.maybeRefreshRps() return d.rl.Limit() } func (d *DynamicRateLimiter) maybeRefreshRps() { now := d.timeSource.Now() lastUpdated := d.lastUpdateTime.Load() if now.After(lastUpdated.Add(d.ttl-1)) && d.lastUpdateTime.CompareAndSwap(lastUpdated, &now) { d.rl.SetLimitAndBurst(d.getLimitAndBurst()) } } func (d *DynamicRateLimiter) getLimitAndBurst() (rate.Limit, int) { rps := d.rps() burst := max(int(math.Ceil(rps)), d.minBurst) // If we have 0 rps we have to zero out the burst to immediately cut off new permits if rps == 0 { burst = 0 } return rate.Limit(rps), burst } ================================================ FILE: common/quotas/global/algorithm/requestweighted.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. /* Package algorithm contains a running-weighted-average calculator for ratelimits, and some associated types to prevent accidental confusion between the various floats/ints/etc involved. This package is intentionally unaware of RPC or any desired RPS limits, it just tracks the observed rates of requests and returns proportions, so other code can determine RPS values. This is both to simplify testing and to keep its internals as fast as possible, as it is relatively CPU heavy and needs to be quick to prevent a snowballing backlog of concurrent updates and requests for aggregated data. # How this fits in the global ratelimiting system Going back to the [github.com/uber/cadence/common/quotas/global] package diagram: each aggregating host will have one or more instances of this weight-calculator, and it will receive a shard worth of request data. Though this package has no direct connection to RPC structures, the request data is expected to closely match the argument to RequestWeighted.Update, and the response data like the return value of RequestWeighted.HostWeights. This makes aggregating hosts intentionally very simple outside of this package: they essentially just forward the request and response, multiplying by dynamicconfig intended-ratelimit values (which occurs outside the mutex, to minimize contention). # Expected use Limiting hosts collect metrics and submit it to an aggregating host via RequestWeighted.Update, through [some kind of RPC setup]. Once updated, the aggregating host can get the RequestWeighted.HostWeights for that host's ratelimits (== the updated limits), multiply those 0..1 weights by the dynamicconfig configured RPS, and return them to the limiting host that triggered the request. If there are unused RPS remaining, the aggregating host *may* increase the RPS it returns by [some amount], to allow the limiting host to pre-emptively allow more requests than is "fair" before the next update. Even if it does not, an increase in attempted usage will increase that limiter's weight on the next update cycle, so this is mostly intended as a tool for reducing incorrectly-rejected requests when a ratelimit's usage is well below its allowed limit. # Dealing with expired data As user calls change, or the aggregating-host ring changes, Limit keys may become effectively unused in an instance. During normal use, any accessed Limit will clean up "expired" data if it is found, and there is essentially no ongoing "upkeep" cost for an un-accessed Limit (aside from memory). Hopefully this will be sufficient to keep memory use and performance reasonable. If it is not, a trivial goroutine to periodically call RequestWeighted.GC will clear *all* old data. Every minute or so should be more than sufficient. This method returns some simple metrics about how much data exists / was removed, so it can be reported to help us estimate how necessary it is in practice. # Dealing with excessive contention In large clusters, there will be likely be many update-requests, many Limit keys, and more data to process to return responses (more hosts per limit). At some point this could become a bottleneck, preventing timely updates. On even our largest internal clusters, I do not believe we will run that risk. At least, not with the planned frontend-only limits. `pprof` should be able to easily validate this and let us better estimate headroom for adding more in the future. If too much contention does occur, there are 3 relatively simple mitigations: 1. turn it completely off, go back to host-local limits 2. slow down the update frequency 3. use N instances and shard keys across them 1 is pretty obvious and has clear behavior, though it means degrading behavior for our imbalanced-load clusters. 2 is essentially the only short-term and dynamically-apply-able option that retains any of the behavior we want. This will impact how quickly the algorithm converges, so you may also want to adjust the new-data weight to be higher, though "how much" depends on what kind of behavior you want / can tolerate. 3 offers a linear contention improvement and should be basically trivial to build: create N instances instead of 1, and shard the Limit keys to each instance. Since this is mostly CPU bound and each one would be fully independent, making `GOMAXPROCS` instances is an obvious first choice, and this does not need to be dynamically reconfigured at runtime so there should be no need to build a "smart" re-balancing / re-sharding system of any kind. Tests contain a single-threaded benchmark and laptop-local results for estimating contention, and for judging if changes will help or hinder, but real-world use and different CPUs will of course be somewhat different. Personally I expect "few mutexes, GOMAXPROCS instances" is roughly ideal for CPU throughput with the current setup, but an entirely different internal structure might exceed it. */ package algorithm import ( "errors" "fmt" "math" "sync" "time" "go.uber.org/multierr" "golang.org/x/exp/constraints" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas/global/shared" ) type ( // requests holds the running per-second running average request data for a single key from a single host, // and the last time it was updated. requests struct { // TODO: add "first update" time, don't return to callers until it has passed 1(+?) update cycle as data is incomplete. lastUpdate time.Time // only so we know if elapsed times are exceeded, not used to compute per-second rates accepted, rejected PerSecond // requests received, per second (conceptually divided by update rate) } // Identity is an arbitrary (stable) identifier for a "limiting" host. Identity string // Limit is the key being used to Limit requests. Limit string // PerSecond represents already-scaled-to-per-second RPS values PerSecond float64 // HostWeight is a float between 0 and 1, showing how much of a ratelimit should be allocated to a host. HostWeight float64 impl struct { // intentionally value-typed so caller cannot mutate the fields. // manually copy data if this changes. cfg Config scope metrics.Scope logger log.Logger // mut protects usage, as it is the only mutable data mut sync.Mutex // usage data for ratelimits, per host. // // data is first keyed on the limit and then on the host, as it's assumed that there will be // many times more limit keys than limiting hosts, and this will reduce cardinality more rapidly. usage map[Limit]map[Identity]requests clock clock.TimeSource } // Metrics reports overall counts discovered as part of garbage collection. // // This is not necessarily worth maintaining verbatim, but while it's easy to // collect it might give us some insight into overall behavior. Metrics struct { // HostLimits is the number of per-host limits that remain after cleanup HostLimits int // Limits is the number of cross-host limits that remain after cleanup Limits int // RemovedHostLimits is the number of per-host limits that were removed as part of this GC pass RemovedHostLimits int // RemovedLimits is the number of cross-host limits that were removed as part of this GC pass RemovedLimits int } // Requests contains accepted/rejected request counts for a ratelimit, as part of an update operation. // Only intended to be used in [RequestWeighted.Update]. Requests struct { Accepted, Rejected int } // RequestWeighted returns aggregated RPS and distribution data for its Limit keys, // based on how many requests each Identity has received. RequestWeighted interface { // Update load-data for this host's requests, given a known elapsed time spent accumulating this load info. // // Elapsed time must be non-zero, but is not otherwise constrained. Update(params UpdateParams) error // HostUsage returns the per-[Limit] weights for all requested + known keys for this Identity, // as well as the Limit's overall used RPS (to decide RPS to allow for new hosts). HostUsage(host Identity, limits []Limit) (weights map[Limit]HostUsage, err error) // GC can be called periodically to pre-emptively prune old ratelimits. // // Limit keys that are accessed normally will automatically garbage-collect themselves and old host data, // and an unused limit only costs memory, so this may prove to be unnecessary. GC() (Metrics, error) } Config struct { // How much each update should be weighted vs prior data. // Must be between 0 and 1, recommend starting with 0.5 (4 updates until data has <10% influence) NewDataWeight dynamicproperties.FloatPropertyFn // Expected time between updates. Should match the cluster's config for how often limiters check in, // i.e. this should probably be the same dynamic config value, updated at / near the same time. UpdateInterval dynamicproperties.DurationPropertyFn // How long to wait before considering a host-limit's RPS usage "probably inactive", rather than // simply delayed. // // Should always be larger than UpdateInterval, as less is meaningless. Values are reduced based on // missed UpdateInterval multiples, not DecayAfter. // Unsure about a good default (try 2x UpdateInterval?), but larger numbers mean smoother behavior // but longer delays on adjusting to hosts that have disappeared or stopped receiving requests. DecayAfter dynamicproperties.DurationPropertyFn // How much time can pass without receiving any update before completely deleting data. // // Due to ever-reducing weight after DecayAfter, this is intended to reduce calculation costs, // not influence behavior / weights returned. Even extremely-low-weighted hosts will still be retained // as long as they keep reporting "in use" (e.g. 1 rps used out of >100,000 is fine and will be tracked). // // "Good" values depend on a lot of details, but >=10*UpdateInterval seems reasonably safe for a // NewDataWeight of 0.5, as the latest data will be reduced to only 0.1% and may not be worth keeping. GcAfter dynamicproperties.DurationPropertyFn } // UpdateParams contains args for calling Update. UpdateParams struct { ID Identity Load map[Limit]Requests Elapsed time.Duration } HostUsage struct { Weight HostWeight Used PerSecond } // configSnapshot holds a non-changing snapshot of the dynamic config values, // and also provides a validate() method to make sure they're sane. configSnapshot struct { now time.Time weight float64 rate time.Duration decayAfter time.Duration gcAfter time.Duration } ) // these could be configurable, but it's not expected to be a noticeable performance concern. const ( guessNumKeys = 1024 // guesstimate at num of ratelimit keys in a cluster guessHostCount = 32 // guesstimate at num of frontend hosts in a cluster that receive traffic for each key // Some event-RPSes exceed 100k, but none get close to 1m. // So, to add some buffer, 10m per second is considered "impossible". // This is quite vague and can be changed, it essentially just serves as the logging threshold. guessImpossibleRps = 10_000_000 ) func (p UpdateParams) Validate() error { if len(p.ID) == 0 { return fmt.Errorf("empty caller ID") } if p.Elapsed <= 0 { return fmt.Errorf("elapsed time must be positive, got %v", p.Elapsed) } return nil } func (c configSnapshot) validate() error { // errors are untyped because they should not generally be "handled", only returned. // in principle, they're all "bad server config" / 5XX and if sustained will eventually lead to // the limiting hosts using their fallback limiters. var err error if c.weight < 0 { // nonsensical err = multierr.Append(err, fmt.Errorf("new data weight cannot be negative: %f", c.weight)) } if c.rate <= 0 { // rate is used in division, absolutely must not be zero. // and negatives would just be weird enough to consider an error too. err = multierr.Append(err, fmt.Errorf("update rate must be positive: %v", c.rate)) } // decayAfter and gcAfter should always be larger than rate, but currently no logic misbehaves // if this is not true, so it is not checked. this allows e.g. temporary weird mid-rollout combinations // without breaking. // negative values cannot ever be correct though, so block them. if c.decayAfter < 0 { err = multierr.Append(err, fmt.Errorf("decay-after cannot be negative: %v", c.decayAfter)) } if c.gcAfter < 0 { err = multierr.Append(err, fmt.Errorf("gc-after cannot be negative: %v", c.decayAfter)) } if err != nil { return multierr.Append(errors.New("bad ratelimiter config"), err) } return nil } // shouldGC returns true if data should be garbage collected func (c configSnapshot) shouldGC(dataAge time.Duration) bool { return dataAge >= c.gcAfter } // missedUpdateScalar returns an amount to multiply old RPS data by, to account for missed updates outside SLA. func (c configSnapshot) missedUpdateScalar(dataAge time.Duration) PerSecond { if dataAge < c.decayAfter { // new enough to not decay old data return 1 } // fast check as exponents are slow and computing past this is unnecessary. // normally, a `c.shouldGC(dataAge)` check should make this unused. if dataAge >= c.gcAfter { // old enough to treat old data as nonexistent return 0 } // somewhere in the middle: account for missed updates by simulating 0-value updates. // this intentionally floors rather than allowing fractions because: // - precision isn't important // - tests are a bit easier (more stable / less crazy-looking values) // - as a bonus freebie: int division and int exponents are typically faster to compute missed := dataAge / c.rate if missed < 1 { // note: this path should never be used, it's effectively error handling. // // this can only be true if cfg.decayAfter is smaller than cfg.rate, which // should not occur in practice as that would be a nonsensical config combination. // // the types cannot *prevent* it though, so it must be handled, and there's a // reasonable response if it does occur: // don't reduce old data. return 1 } // missed at least one full update period, calculate an exponential decay multiplier for the old values. return PerSecond(math.Pow(1-c.weight, math.Floor(float64(missed)))) } // New returns a concurrency-safe host-weight aggregator. // // This instance is effectively single-threaded, but a small sharding wrapper should allow better concurrent // throughput if needed (bound by CPU cores, as it's moderately CPU-costly). func New(met metrics.Client, logger log.Logger, cfg Config) (RequestWeighted, error) { i := &impl{ cfg: cfg, scope: met.Scope(metrics.GlobalRatelimiterAggregator), logger: logger.WithTags(tag.ComponentGlobalRatelimiter), usage: make(map[Limit]map[Identity]requests, guessNumKeys), // start out relatively large clock: clock.NewRealTimeSource(), } _, err := i.snapshot() // validate config by just taking a snapshot if err != nil { return nil, fmt.Errorf("invalid config: %w", err) } return i, nil } // Update performs a weighted update to the running RPS for this host's per-key request data func (a *impl) Update(p UpdateParams) error { if err := p.Validate(); err != nil { return fmt.Errorf("bad args to update: %w", err) } a.mut.Lock() once := newOnce() defer once.Do(a.mut.Unlock) var initialized, reinitialized, updated, decayed int64 snap, err := a.snapshot() if err != nil { return err } for key, req := range p.Load { ih := a.usage[key] if ih == nil { ih = make(map[Identity]requests, guessHostCount) } var next requests prev := ih[p.ID] // sanity check: elapsed time should not be less than 1s, so just cap it. // in practice this should always be safe with a >=1s configured rate, as // the caller should not send *more* frequently than every 1s (monotonic time). // // but this is rather easy to trigger in tests / fuzzing, // and extreme values lead to irrational math either way. elapsed := math.Max(float64(p.Elapsed), float64(time.Second)) aps := shared.SanityLogFloat(0, PerSecond(float64(req.Accepted)/(elapsed/float64(time.Second))), guessImpossibleRps, "accepted rps", a.logger) rps := shared.SanityLogFloat(0, PerSecond(float64(req.Rejected)/(elapsed/float64(time.Second))), guessImpossibleRps, "rejected rps", a.logger) // zeros are not worth recording, and this also simplifies math elsewhere // for two major reasons: // - it prevents some divide-by-zero scenarios by simply not having actual zeros // - it prevents weights from perpetually lowering if zeros are repeatedly sent, where they may eventually reach zero // // these keys will eventually gc, just leave them alone until that happens. // currently this gc relies on the assumption that HostUsage will be called with the same set of keys "soon", // but that is fairly easy to fix if needed. if rps+aps == 0 { continue } if prev.lastUpdate.IsZero() { initialized++ next = requests{ lastUpdate: snap.now, accepted: aps, // no requests == 100% weight rejected: rps, // no requests == 100% weight } } else { age := snap.now.Sub(prev.lastUpdate) if snap.shouldGC(age) { reinitialized++ // would have GC'd if we had seen it earlier, so it's the same as the zero state next = requests{ lastUpdate: snap.now, accepted: aps, // no requests == 100% weight rejected: rps, // no requests == 100% weight } } else { updated++ // compute the next rolling average step (`*reduce` simulates skipped updates) reduce := snap.missedUpdateScalar(age) if reduce < 1 { decayed++ } next = requests{ lastUpdate: snap.now, // TODO: max(1, actual) so this does not lead to <1 rps allowances? or maybe just 1+actual and then reduce in used-responses? // otherwise currently this may lead to rare callers getting 0.0001 rps, // and never recovering, despite steady and fair usage. accepted: shared.SanityLogFloat(0, weighted(aps, prev.accepted*reduce, snap.weight), guessImpossibleRps, "weighted accepted rps", a.logger), rejected: shared.SanityLogFloat(0, weighted(rps, prev.rejected*reduce, snap.weight), guessImpossibleRps, "weighted rejected rps", a.logger), } } } ih[p.ID] = next a.usage[key] = ih } once.Do(a.mut.Unlock) // don't hold the lock while emitting metrics a.scope.RecordHistogramValue(metrics.GlobalRatelimiterInitialized, float64(initialized)) a.scope.RecordHistogramValue(metrics.GlobalRatelimiterReinitialized, float64(reinitialized)) a.scope.RecordHistogramValue(metrics.GlobalRatelimiterUpdated, float64(updated)) a.scope.RecordHistogramValue(metrics.GlobalRatelimiterDecayed, float64(decayed)) return nil } // getWeightsLocked returns the weights of observed hosts (based on ALL requests), and the total number of requests accepted per second. func (a *impl) getWeightsLocked(key Limit, snap configSnapshot) (weights map[Identity]HostWeight, usedRPS PerSecond, met Metrics) { ir := a.usage[key] if len(ir) == 0 { return nil, 0, met } weights = make(map[Identity]HostWeight, len(ir)) total := HostWeight(0.0) for id, reqs := range ir { // account for missed updates age := snap.now.Sub(reqs.lastUpdate) if snap.shouldGC(age) { // old, clean up delete(ir, id) met.RemovedHostLimits++ continue } // should never be zero, `shouldGC` takes care of that. reduce := shared.SanityLogFloat(0, snap.missedUpdateScalar(age), 1, "missed update", a.logger) // similarly: should never be zero, accepted + rejected must be nonzero or they are not inserted. // this may be reduced to very low values, but still far from == 0. actual := HostWeight((reqs.accepted + reqs.rejected) * reduce) weights[id] = actual // populate with the reduced values so it doesn't have to be calculated again total += actual // keep a running total to scale all values when done usedRPS += reqs.accepted * reduce met.HostLimits++ } if len(ir) == 0 { // completely empty Limit, gc it as well delete(a.usage, key) met.RemovedLimits++ return nil, 0, met } // zeros anywhere here should not be possible - they are prevented from being inserted, // and anything simply "losing weight" will only become "rather low", not zero, // before enough passes have occurred to garbage collect it. // // specifically, 1rps -> 0rps takes over 1,000 halving cycles, and a single 1rps event // during that will immediately re-set it above 0.5 and will need 1,000+ more cycles. // // if gc period / weight amount is set extreme enough this is "possible", // but we are unlikely to ever cause it. for id := range ir { // normalize by the total. // this also ensures all values are between 0 and 1 (inclusive), // though zero should be impossible. weights[id] = shared.SanityLogFloat(0, weights[id]/total, 1, "normalized weight", a.logger) } met.Limits = 1 return weights, usedRPS, met } func (a *impl) HostUsage(host Identity, limits []Limit) (usage map[Limit]HostUsage, err error) { a.mut.Lock() once := newOnce() defer once.Do(a.mut.Unlock) snap, err := a.snapshot() if err != nil { return nil, err } var cumulative Metrics usage = make(map[Limit]HostUsage, len(limits)) for _, lim := range limits { hosts, used, met := a.getWeightsLocked(lim, snap) cumulative.Limits += met.Limits // always 1 or 0 cumulative.HostLimits += met.HostLimits cumulative.RemovedLimits += met.RemovedLimits // always 0 or 1 (opposite Limits) cumulative.RemovedHostLimits += met.RemovedHostLimits if len(hosts) > 0 { usage[lim] = HostUsage{ // limit is known, has some usage on at least one host. // usage has an "upper limit" because it is only the accepted RPS, not all requests received. Used: shared.SanityLogFloat(0, used, guessImpossibleRps, "used rps", a.logger), // either known weight if there is info for this host, or zero if not. // zeros are interpreted as "unknown", the same as "not present". Weight: shared.SanityLogFloat(0, hosts[host], 1, "computed weight", a.logger), } } } once.Do(a.mut.Unlock) // don't hold the lock while emitting metrics a.scope.RecordHistogramValue(metrics.GlobalRatelimiterLimitsQueried, float64(cumulative.Limits)) a.scope.RecordHistogramValue(metrics.GlobalRatelimiterHostLimitsQueried, float64(cumulative.HostLimits)) a.scope.RecordHistogramValue(metrics.GlobalRatelimiterRemovedLimits, float64(cumulative.RemovedLimits)) a.scope.RecordHistogramValue(metrics.GlobalRatelimiterRemovedHostLimits, float64(cumulative.RemovedHostLimits)) return usage, nil } func (a *impl) GC() (Metrics, error) { a.mut.Lock() defer a.mut.Unlock() m := Metrics{} snap, err := a.snapshot() if err != nil { return Metrics{}, err } // TODO: too costly? can check the first-N% each time and it'll eventually visit all keys, demonstrated in tests. for lim, dat := range a.usage { for host, reqs := range dat { age := snap.now.Sub(reqs.lastUpdate) if snap.shouldGC(age) { // clean up stale host data within limits delete(dat, host) m.RemovedHostLimits++ } else { m.HostLimits++ } } // clean up stale limits if len(dat) == 0 { delete(a.usage, lim) m.RemovedLimits++ } else { m.Limits++ } } return m, nil } // returns a snapshot of config and "now" for easier chaining through calls, and reducing calls to // non-trivial field types like dynamic config. func (a *impl) snapshot() (configSnapshot, error) { snap := configSnapshot{ now: a.clock.Now(), weight: a.cfg.NewDataWeight(), rate: a.cfg.UpdateInterval(), decayAfter: a.cfg.DecayAfter(), gcAfter: a.cfg.GcAfter(), } return snap, snap.validate() } func weighted[T numeric](newer, older T, weight float64) T { return T((float64(newer) * weight) + (float64(older) * (1 - weight))) } type numeric interface { constraints.Integer | constraints.Float } // non-sync version of sync.Once, for easier unlocking type doOnce bool func newOnce() *doOnce { value := doOnce(false) return &value } func (o *doOnce) Do(cb func()) { if *o == false { *o = true cb() } } ================================================ FILE: common/quotas/global/algorithm/requestweighted_fuzz_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package algorithm import ( "encoding/binary" "math" "testing" "time" "golang.org/x/exp/maps" "golang.org/x/exp/slices" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas/global/shared" ) // Calls Update and HostWeights as many times as there is sufficient data, // to make sure multiple updates do not lead to Infs or NaNs, because floating point is hard. // // This is best to let run for a very long time after making changes, as odd sequences may be necessary. // E.g. it took nearly 30 minutes to hit a case where `elapsed` had an `INT_MIN` value from the data bytes, // which failed earlier attempts to math.Abs it with `-val` because `-INT_MIN == INT_MIN`. // // Your *local* fuzz corpus will help re-running to find more interesting things more quickly, // but a cleared cache will have to start over and may take a long time. func FuzzMultiUpdate(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { accept, reject, elapsed, data, msg := ints(data) if msg != "" { t.Skip(msg) } var host, key string if len(data) > 1 { // single-char strings are probably best, some collision is a good thing. host, key = string(data[0]), string(data[1]) // data = data[2:] // leaving the data in there is fine, it'll just become part of the accept/reject ints. } else { t.Skip("not enough data for keys") } l, obs := testlogger.NewObserved(t) // failed sanity checks will fail the fuzz test, as they use .Error i, err := New(metrics.NewNoopMetricsClient(), l, Config{ // TODO: fuzz test with different weights too? though only some human-friendly values are likely, like 0.25..0.75 // extremely high or low values are kinda-intentionally allowed to misbehave since they're really not rational to set, // but it might be a good exercise to make sure the math is reasonable even in those edge cases... NewDataWeight: func(opts ...dynamicproperties.FilterOption) float64 { return 0.5 }, UpdateInterval: func(opts ...dynamicproperties.FilterOption) time.Duration { return time.Second }, DecayAfter: func(opts ...dynamicproperties.FilterOption) time.Duration { return time.Hour }, GcAfter: func(opts ...dynamicproperties.FilterOption) time.Duration { return time.Hour }, }) if err != nil { t.Fatal(err) } // if it takes more than a couple seconds, fuzz considers it stuck. // 1000 seems too long, 100 is pretty quick, probably just lower if necessary. for iter := 0; iter < 100; iter++ { // send as many updates as we have data for t.Logf("accept=%d, reject=%d, elapsed=%d, host=%q, key=%q", accept, reject, elapsed, host, key) err = i.Update(UpdateParams{ ID: Identity(host), Load: map[Limit]Requests{ Limit(key): { Accepted: int(accept), Rejected: int(reject), }, }, Elapsed: time.Duration(int(elapsed)), }) if err != nil { t.Fatal(err) } u := i.(*impl).usage // collect and sort the layers of map keys, // as determinism helps the fuzzing system choose branches better. keySet := make(map[Limit]struct{}) identSet := make(map[Identity]struct{}) for limitKey, hostUsage := range u { keySet[limitKey] = struct{}{} for ident := range hostUsage { identSet[ident] = struct{}{} } } if accept+reject > 0 && len(keySet) == 0 { t.Error("no identities") } if accept+reject > 0 && len(identSet) == 0 { t.Error("no keys") } keys := maps.Keys(keySet) idents := maps.Keys(identSet) slices.Sort(keys) slices.Sort(idents) // scan for NaNs and Infs in internal data for _, k := range keys { hu := u[k] for _, ident := range idents { us := hu[ident] if bad := fsane(us.accepted); bad != "" { t.Errorf("%v for accepted rps:%q, key:%q", bad, k, ident) } if bad := fsane(us.rejected); bad != "" { t.Errorf("%v for rejected rps:%q, key:%q", bad, k, ident) } } } // and get all hosts and check the weight calculations for all keys too for _, ident := range idents { res, err := i.HostUsage(ident, keys) if err != nil { t.Fatal(err) } if len(res) == 0 { t.Error("no results") } for _, k := range keys { r, ok := res[k] if !ok { // currently all requested keys are expected to be returned t.Errorf("key not found: %q", k) } if bad := fsane(r.Used); bad != "" { t.Error(bad, "usage") } if r.Used < 0 { t.Error("negative usage") } if bad := fsane(r.Weight); bad != "" { t.Error(bad, "weight") } if r.Weight < 0 { t.Error("negative weight") } else if r.Weight > 1 { t.Error("too much weight") } } } // check for error logs, as this would imply sanity check violations shared.AssertNoSanityCheckFailures(t, obs.TakeAll()) // refresh for the next round accept, reject, elapsed, data, msg = ints(data) if msg != "" { break // not enough data for another round } if len(data) > 1 { host, key = string(data[0]), string(data[1]) // data = data[2:] // leaving the data in there is fine } else { break // not enough data for another round } } }) } // float sanity check because it's a lot to write out every time func fsane[T ~float64](t T) string { if math.IsNaN(float64(t)) { return "NaN" } if math.IsInf(float64(t), 0) { return "Inf" } return "" } // helper to pull varints out of a pile of bytes. // returns the unused portion of data, // and returns a non-empty string if there was a problem func ints(inData []byte) (accept, reject, elapsed int64, data []byte, err string) { data = inData accept, data, msg := getPositiveInt64(data) if msg != "" { return 0, 0, 0, nil, "not enough data" } reject, data, msg = getPositiveInt64(data) if msg != "" { return 0, 0, 0, nil, "not enough data" } elapsed, data, msg = getPositiveInt64(data) if msg != "" { return 0, 0, 0, nil, "not enough data" } if elapsed == 0 { return 0, 0, 0, nil, "zero elapsed time, cannot use" } return accept, reject, elapsed, data, "" } // helpers need helpers sometimes func getPositiveInt64(data []byte) (int64, []byte, string) { val, read := binary.Varint(data) if read == 0 { return 0, nil, "not enough data" } if read > 0 { data = data[read:] // successfully read an int } if read < 0 { data = data[-read:] // overflowed and stopped reading part way } if val < 0 { val = -val } if val < 0 { // -INT_MIN == INT_MIN, so the above check may have done nothing. // so special case INT_MIN by rolling it over to INT_MAX. val-- } // and last but not least: make sure it's below our "impossible" value when accept+reject are combined. // partly this ensures random fuzzed rps doesn't exceed it (which is common), // and partly it just asserts "we do not test irrational rates". val = val % ((guessImpossibleRps - 1) / 2) return val, data, "" } // not covered by the larger fuzz test because it pins some "reasonable" values. func FuzzMissedUpdate(f *testing.F) { f.Fuzz(func(t *testing.T, decay, gc, age, rate int, weight float64) { if decay <= 0 || gc <= 0 || age <= 0 || rate <= 0 { t.Skip() } scalar := configSnapshot{ weight: weight - math.Floor(weight), // 0..1, idk about negatives but it doesn't seem to break either rate: time.Duration(rate), decayAfter: time.Duration(decay), gcAfter: time.Duration(gc), }.missedUpdateScalar(time.Duration(age)) if scalar < 0 || scalar > 1 { t.Fail() } if bad := fsane(scalar); bad != "" { t.Error(bad, "scalar") } }) } ================================================ FILE: common/quotas/global/algorithm/requestweighted_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package algorithm import ( "fmt" "math" "math/rand" "strings" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/multierr" "golang.org/x/exp/maps" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas/global/shared" ) // just simplifies newForTest usage as most tests only care about rate func defaultConfig(rate time.Duration) configSnapshot { return configSnapshot{ // now: , ignored // intentionally avoiding 0.5 because it cannot tell if the code uses // `weight` or `1-weight`, which is usually relevant. // // 0.1 is relatively human-math-friendly for a single step, // but is otherwise arbitrary. weight: 0.1, rate: rate, decayAfter: 2 * rate, gcAfter: 10 * rate, } } func newValid(t testlogger.TestingT, snap configSnapshot) (*impl, clock.MockedTimeSource) { return newForTest(t, snap, true) } func newForTest(t testlogger.TestingT, snap configSnapshot, validate bool) (*impl, clock.MockedTimeSource) { cfg := Config{ NewDataWeight: func(_ ...dynamicproperties.FilterOption) float64 { return snap.weight }, UpdateInterval: func(_ ...dynamicproperties.FilterOption) time.Duration { return snap.rate }, DecayAfter: func(_ ...dynamicproperties.FilterOption) time.Duration { return snap.decayAfter }, GcAfter: func(_ ...dynamicproperties.FilterOption) time.Duration { return snap.gcAfter }, } var agg *impl if validate { l, obs := testlogger.NewObserved(t) t.Cleanup(func() { shared.AssertNoSanityCheckFailures(t, obs.TakeAll()) }) i, err := New(metrics.NewNoopMetricsClient(), l, cfg) require.NoError(t, err) agg = i.(*impl) } else { // need to build by hand, New returns nil on err agg = &impl{ cfg: cfg, scope: metrics.NewNoopMetricsClient().Scope(metrics.GlobalRatelimiterAggregator), usage: make(map[Limit]map[Identity]requests), clock: nil, } } underlying := agg tick := clock.NewMockedTimeSource() underlying.clock = tick // adjust time to get rid of sub-second output, it's just harder to read. // doesn't matter if this goes forward or backward. tick.Advance(tick.Now().Sub(tick.Now().Round(time.Second))) return underlying, tick } func TestEmitsMetrics(t *testing.T) { t.Cleanup(func() { if t.Failed() { t.Log("This test is sensitive about bucket sizes, but they aren't actually important.") t.Log("If bucket sizes have changed, just update the test to create enough data / use the new values, so each step is unique") } }) assertHistogramContents := func(name string, snap tally.HistogramSnapshot, expected map[float64]int64) { t.Helper() for bucket, val := range snap.Values() { // {bucket_boundary: value} assert.Equal(t, expected[bucket], val, "bucket %v has unexpected value for histogram name %v", bucket, name) } } assertAllHistogramContents := func(snap tally.Snapshot, contents map[string]map[float64]int64) { t.Helper() for _, data := range snap.Histograms() { // {"full.name+tags":{tags, values, etc}} name := strings.TrimPrefix(data.Name(), "test.global_ratelimiter_") // common prefix for this test exp, ok := contents[name] if !ok { // keys remain between snapshots, so they're a bit of a pain. // values are zeroed by each snapshot though. for bucket, val := range data.Values() { assert.Zerof(t, val, "ignored key %v (trimmed: %v) in bucket %v has non-zero value, cannot be ignored", data.Name(), name, bucket) } } else { assertHistogramContents(name, data, exp) } } } agg, _ := newValid(t, defaultConfig(time.Second)) ts := tally.NewTestScope("test", nil) agg.scope = metrics.NewClient(ts, metrics.History, metrics.HistogramMigration{}).Scope(metrics.GlobalRatelimiterAggregator) h1, h2 := Identity("host 1"), Identity("host 2") key := Limit("key") err := agg.Update(UpdateParams{ ID: h1, Load: map[Limit]Requests{key: {1, 1}}, Elapsed: time.Second, }) require.NoError(t, err) snap := ts.Snapshot() assertAllHistogramContents(snap, map[string]map[float64]int64{ "initialized": {1: 1}, // one key was created "reinitialized": {0: 1}, "updated": {0: 1}, "decayed": {0: 1}, }) err = agg.Update(UpdateParams{ ID: h2, Load: map[Limit]Requests{key: {1, 1}}, Elapsed: time.Second, }) require.NoError(t, err) snap = ts.Snapshot() assertAllHistogramContents(snap, map[string]map[float64]int64{ "initialized": {1: 1}, // keys are disjoint, so another key was created "reinitialized": {0: 1}, "updated": {0: 1}, "decayed": {0: 1}, }) err = agg.Update(UpdateParams{ ID: h1, Load: map[Limit]Requests{key: {1, 1}}, Elapsed: time.Second, }) require.NoError(t, err) snap = ts.Snapshot() assertAllHistogramContents(snap, map[string]map[float64]int64{ "initialized": {0: 1}, "reinitialized": {0: 1}, "updated": {1: 1}, // h1 was updated "decayed": {0: 1}, }) _, err = agg.HostUsage(h1, []Limit{key}) require.NoError(t, err) snap = ts.Snapshot() assertAllHistogramContents(snap, map[string]map[float64]int64{ "limits_queried": {1: 1}, // one limit exists "host_limits_queried": {2: 1}, // two hosts have data for that limit "removed_limits": {0: 1}, // none removed "removed_host_limits": {0: 1}, // none removed }) } func TestMissedUpdateHandling(t *testing.T) { agg, tick := newValid(t, configSnapshot{ weight: 0.1, rate: time.Second, decayAfter: 2 * time.Second, gcAfter: 10 * time.Second, }) h1, h2 := Identity("host 1"), Identity("host 2") key := Limit("key") err := agg.Update(UpdateParams{ ID: h1, Load: map[Limit]Requests{key: {1, 1}}, Elapsed: time.Second, }) require.NoError(t, err) err = agg.Update(UpdateParams{ ID: h2, Load: map[Limit]Requests{key: {1, 1}}, Elapsed: time.Second, }) require.NoError(t, err) // sanity-check the initial values usage, err := agg.HostUsage(h1, []Limit{key}) require.NoError(t, err) assert.Len(t, usage, 1) // 1 key known, should always be true assert.Equal(t, PerSecond(2), usage[key].Used) // only 2 accepted requests // move to 1 second later == expected update rate. // should still match the original values as it's not excessively delayed. tick.Advance(time.Second) usage, err = agg.HostUsage(h1, []Limit{key}) assert.Len(t, usage, 1) assert.Equal(t, PerSecond(2), usage[key].Used) // still 2 allowed // advance to 1.5 seconds: over the expected rate but not beyond decay-after, // which should still not change anything. tick.Advance(time.Second / 2) usage, err = agg.HostUsage(h1, []Limit{key}) assert.Len(t, usage, 1) assert.Equal(t, PerSecond(2), usage[key].Used) // still 2 allowed // advance another second, to 2.5s total. // exactly 2s and 3s are being avoided because nanosecond-equal times are highly unlikely, // and the behavior at that time doesn't actually matter, so it doesn't matter if it changes. // // 2.5s is beyond decayAfter (2s) so it should retroactively count missed updates, // immediately freeing up RPS as if it was tracking 0s all along, because we're assuming // it has been inactive for some reason. // // 2 full updates have been missed, so: 2 => 1.8 => 1.62 tick.Advance(time.Second) usage, err = agg.HostUsage(h1, []Limit{key}) assert.Len(t, usage, 1) assert.Equal(t, PerSecond(1.62), usage[key].Used) // reduced from 2 // advance to 3.5 seconds, for 3 total missed updates: // 2 => 1.8 => 1.62 => 1.4580000000000002 tick.Advance(time.Second) usage, err = agg.HostUsage(h1, []Limit{key}) assert.Len(t, usage, 1) // still tracking the key / not GC'd assert.InDelta(t, float64(usage[key].Used), 1.458, 0.0001) // further reduced } func TestGC(t *testing.T) { h1, h2 := Identity("host 1"), Identity("host 2") key := Limit("key") // creates an aggregator and advances time 9 seconds, ensuring that data still exists. // advance 1 more second to trigger garbage collection. // this moves slightly beyond 9s to avoid testing the precise boundary time, as it's not relevant. setup := func(t *testing.T) (*impl, clock.MockedTimeSource) { agg, tick := newValid(t, configSnapshot{ rate: time.Second, gcAfter: 10 * time.Second, // irrelevant for these tests but must be non-zero: weight: 0.1, decayAfter: 2 * time.Second, }) err := agg.Update(UpdateParams{ID: h1, Load: map[Limit]Requests{key: {1, 1}}, Elapsed: time.Second}) require.NoError(t, err) err = agg.Update(UpdateParams{ID: h2, Load: map[Limit]Requests{key: {1, 1}}, Elapsed: time.Second}) require.NoError(t, err) usage, err := agg.HostUsage(h1, []Limit{key}) require.NoError(t, err) // sanity check that we have data require.Len(t, usage, 1, "sanity check: should have inserted limit's data") require.Equal(t, usage[key].Used, PerSecond(2), "sanity check: should have inserted usage data") // partially advance, sanity check. tick.Advance(9*time.Second + (time.Second / 10)) usage, err = agg.HostUsage(h1, []Limit{key}) require.Len(t, usage, 1, "sanity check: should have inserted limit's data after 9s") require.Equal(t, usage[key].Used, PerSecond(2*math.Pow(0.9, 9)), "sanity check: should have inserted usage data, reduced after 9s") return agg, tick } t.Run("no cleanup before expiration", func(t *testing.T) { agg, _ := setup(t) met, err := agg.GC() require.NoError(t, err) require.Equal(t, Metrics{ HostLimits: 2, Limits: 1, RemovedHostLimits: 0, RemovedLimits: 0, }, met) }) t.Run("cleans up during read", func(t *testing.T) { agg, tick := setup(t) tick.Advance(time.Second) // advance to 10th second // read it out, should detect out-of-date data and clean it up usage, err := agg.HostUsage(h1, []Limit{key}) require.NoError(t, err) require.Len(t, usage, 0, "should be no data for h1") // internals should also be empty require.Len(t, agg.usage, 0) // also frees memory }) t.Run("retains recent data while cleaning", func(t *testing.T) { agg, tick := setup(t) // refresh data for one host err := agg.Update(UpdateParams{ID: h1, Load: map[Limit]Requests{key: {1, 1}}, Elapsed: time.Second}) require.NoError(t, err) tick.Advance(time.Second) // advance to 10th second // read both hosts. h1 should exist, h2 should not usage, err := agg.HostUsage(h1, []Limit{key}) require.NoError(t, err) require.NotZero(t, usage[key].Weight, "h1 was refreshed and should remain") require.NotZero(t, usage[key].Used, "h1 was refreshed and usage data should remain") usage, err = agg.HostUsage(h2, []Limit{key}) require.Zero(t, usage[key].Weight, "h2 should have no weight at all") require.NotZero(t, usage[key].Used, "h1 was refreshed and usage data should remain") // internals should also be partly emptied require.Len(t, agg.usage, 1, "limit should remain") }) t.Run("cleans up by explicit gc", func(t *testing.T) { agg, tick := setup(t) tick.Advance(time.Second) met, err := agg.GC() require.NoError(t, err) require.Equal(t, Metrics{ HostLimits: 0, // none remain Limits: 0, // none remain RemovedHostLimits: 2, // h1 and h2 for the single key, both removed RemovedLimits: 1, // single key removed }, met) // internals should also be empty require.Len(t, agg.usage, 0) }) } func TestMinorCoverage(t *testing.T) { // not overly useful tests, but coverage++ t.Run("gc", func(t *testing.T) { // invalid config agg, _ := newForTest(t, configSnapshot{}, false) m, err := agg.GC() assert.Zero(t, m) assert.ErrorContains(t, err, "bad ratelimiter config") }) t.Run("update", func(t *testing.T) { // invalid config agg, _ := newForTest(t, configSnapshot{}, false) err := agg.Update(UpdateParams{ID: "ignored", Load: nil, Elapsed: time.Second}) assert.ErrorContains(t, err, "bad ratelimiter config") }) t.Run("get-weights", func(t *testing.T) { // invalid config agg, _ := newForTest(t, configSnapshot{}, false) usage, err := agg.HostUsage("ignored", nil) assert.Zero(t, usage) assert.ErrorContains(t, err, "bad ratelimiter config") }) // a bit more useful t.Run("config validation", func(t *testing.T) { err := configSnapshot{ weight: -1, rate: time.Duration(0), decayAfter: -time.Second, gcAfter: -time.Second, now: time.Time{}, // ignored }.validate() // should have the shared error string assert.ErrorContains(t, err, "bad ratelimiter config") // should have each sub-error assert.ErrorContains(t, err, "weight cannot be negative") assert.ErrorContains(t, err, "rate must be positive") assert.ErrorContains(t, err, "decay-after cannot be negative") assert.ErrorContains(t, err, "gc-after cannot be negative") assert.Len(t, multierr.Errors(err), 5, "should have 5 errors, 4 details and one general") }) t.Run("fast scalar path", func(t *testing.T) { cfg := configSnapshot{ gcAfter: time.Second, } assert.Zero(t, cfg.missedUpdateScalar(2*time.Second), "should multiply old data by exactly zero when beyond gc age") }) // weird config coverage t.Run("irrational decayAfter", func(t *testing.T) { // specifically: exercises the "less than one missed update" branch now := time.Now().Round(time.Second) cfg := configSnapshot{ now: now, rate: time.Second, decayAfter: time.Second / 4, // irrational but allowed: decay faster than expected update rate // effectively ignored weight: 0.1, gcAfter: time.Second * 2, } scale := cfg.missedUpdateScalar(time.Second / 2) // between update and decay periods assert.Equal(t, PerSecond(1), scale, "should not have decayed yet") }) } func TestRapidlyCoalesces(t *testing.T) { // This test ensures that, regardless of starting weights, the algorithm // "rapidly" achieves near-actual weight distribution after a small number of rounds. // // Otherwise, the exact numbers here don't really matter, it's just handy to show the // behavior in semi-extreme scenarios. Logs show a quick adjustment which is what we want. // If you're making changes, check with like 10k rounds to make sure it's stable. // // Time is also not advanced because it doesn't actually need to be advanced. // An update is an update, and the caller's elapsed time is assumed to be correct. agg, _ := newValid(t, configSnapshot{ // Using 0.5 weight because that's what we expect to use IRL, and this test is // ensuring that weight is good enough for the behavior we want. // Weight-math-correctness is ensured by other tests. weight: 0.5, // irrelevant / ignored rate: time.Second, decayAfter: 2 * time.Second, gcAfter: 10 * time.Second, }) snapshot := func() configSnapshot { snap, err := agg.snapshot() require.NoError(t, err) return snap } key := Limit("start workflow") h1, h2, h3 := Identity("one"), Identity("two"), Identity("three") weights, used, met := agg.getWeightsLocked(key, snapshot()) assert.Zero(t, weights, "should have no weights") assert.Zero(t, used, "should have no used RPS") assert.Zero(t, met, "should have processed no data while calculating") push := func(host Identity, accept, reject int) { err := agg.Update(UpdateParams{ ID: host, Load: map[Limit]Requests{ key: { Accepted: accept, Rejected: reject, }, }, Elapsed: time.Second, // 1s just to make rps in == rps out }) require.NoError(t, err) } // init with anything <~1000, too large and even a small fraction of the original value can be too big. push(h1, rand.Intn(1000), rand.Intn(1000)) push(h2, rand.Intn(1000), rand.Intn(1000)) push(h3, rand.Intn(1000), rand.Intn(1000)) // now update multiple times and make sure it gets to 90% within 4 steps == 12s (normally). // // 4 steps with 0.5 weight should mean only 0.5^4 => 6.25% of the original influence remains, // which feels pretty reasonable: after ~10 seconds (3s updates), the oldest data only has ~10% weight. const target = 10 + 200 + 999 for i := 0; i < 4; i++ { weights, used, met = agg.getWeightsLocked(key, snapshot()) t.Log("used:", used, "of actual:", target) t.Log("weights so far:", weights) t.Log("calculation metrics:", met) push(h1, 10, 10) push(h2, 200, 200) push(h3, 999, 999) } weights, used, met = agg.getWeightsLocked(key, snapshot()) t.Log("used:", used, "of actual:", target) t.Log("weights so far:", weights) t.Log("calculation metrics:", met) // aggregated allowed-request values should be less than 10% off assert.InDeltaf(t, target, float64(used), target*0.1, "should have allowed >90%% of target rps by the 5th round") // actually ~94% // also check weights, they should be within 10% assert.InDeltaMapValues(t, map[Identity]float64{ h1: 10 / 1209.0, // 0.07698229407 h2: 200 / 1209.0, // 0.1539645881 h3: 999 / 1209.0, // 0.7690531178 }, floaty(weights), 0.1, "should be close to true load balance") } // converts for testify/assert as it requires same types, not just same underlying type func floaty[K comparable, V numeric](m map[K]V) map[K]float64 { out := make(map[K]float64, len(m)) for k, v := range m { out[k] = float64(v) } return out } func TestConcurrent(t *testing.T) { // essentially a fuzz-test for race purposes. // values aren't checked, but it shouldn't panic / shouldn't race / etc. // low timeout + real time clock to also have wall-clock changes race with logic. // // this test frequently reaches 100% coverage all on its own (currently), // though it's not guaranteed and that is not the intent. // other tests should cover sufficiently even if this test is skipped: // t.Skip("skipped to check coverage") const ( updateRate = time.Millisecond // fairly arbitrary, max gap between updates targetDuration = 100 * updateRate // also minimum number of updates numHosts = 10 numUpdaters = 10 // fairly arbitrary updatesPerBatch = numHosts / 3 // intentionally below len(hosts) to allow some to gc normally ) agg, _ := newValid(t, configSnapshot{ rate: updateRate, decayAfter: 2 * updateRate, gcAfter: 3 * updateRate, // relatively low to trigger implicit gc, check coverage if changing the values weight: 0.1, // irrelevant but must be non-zero }) agg.clock = clock.NewRealTimeSource() var hosts []Identity for i := 0; i < numHosts; i++ { hosts = append(hosts, Identity(fmt.Sprintf("host %d", i))) } var keys []Limit for i := 'a'; i <= 'z'; i++ { keys = append(keys, Limit(i)) } do := make(chan struct{}, numUpdaters) var wg sync.WaitGroup // run some goroutines to update/read in batches for i := 0; i < numUpdaters; i++ { wg.Add(1) go func() { defer wg.Done() for { _, ok := <-do if !ok { // chan's closed, stop return } num := rand.Intn(len(keys)) updates := make(map[Limit]Requests, num) for i := 0; i < num; i++ { updates[keys[rand.Intn(len(keys))]] = Requests{ Accepted: rand.Intn(100), Rejected: rand.Intn(100), } } host := hosts[rand.Intn(len(hosts))] // randomly update or read if rand.Intn(2) == 0 { err := agg.Update(UpdateParams{ID: host, Load: updates, Elapsed: updateRate}) require.NoError(t, err) } else { _, err := agg.HostUsage(host, maps.Keys(updates)) require.NoError(t, err) } } }() } // run a "trigger some work occasionally" goroutine wg.Add(1) start := time.Now() go func() { defer wg.Done() for { if time.Since(start) > targetDuration { close(do) return } // sleep a random portion of the update rate time.Sleep(time.Duration(rand.Intn(int(updateRate)))) // allow a random number of updates. // should be non-blocking to further encourage racing, when possible for i := rand.Intn(updatesPerBatch); i > 0; i-- { do <- struct{}{} } } }() assert.True(t, common.AwaitWaitGroup(&wg, 5*targetDuration), "blocked test? still waiting after %v", 5*targetDuration) // non-racy even if waiting failed m, err := agg.GC() require.NoError(t, err) t.Logf("%#v", m) // should (usually) not be "full" + should (usually) remove some data } func TestSimulate(t *testing.T) { // Semi-fuzzy simulated sequence with fully computed values, exercising most behaviors. // // Everything about this test is sensitive to changes in behavior, // so if that occurs just update the values after ensuring they're reasonable. // Exact matches after changes are not at all important. updateRate := 3 * time.Second // both expected and duration fed to update agg, tick := newValid(t, configSnapshot{ // now: , ignored weight: 0.75, // fairly fast adjustment, and semi-human-friendly math rate: updateRate, decayAfter: 2 * updateRate, gcAfter: 10 * updateRate, }) // keeping var == string simplifies copy/paste as we cannot log the var name start, query := Limit("start"), Limit("query") all := []Limit{start, query} h1, h2, h3 := Identity("one"), Identity("two"), Identity("three") snap, err := agg.snapshot() require.NoError(t, err) weights, used, met := agg.getWeightsLocked(start, snap) assert.Zero(t, weights, "should have no weights") assert.Zero(t, used, "should have no used RPS") assert.Zero(t, met, "should have processed no data while calculating") // just simplifies arg-construction push := func(host Identity, key Limit, accept, reject int) { err := agg.Update(UpdateParams{ ID: host, Load: map[Limit]Requests{ key: { Accepted: accept, Rejected: reject, }, }, Elapsed: time.Second, }) require.NoError(t, err) } // these tests intentionally share data and run sequentially, // the sub-testing is mostly to help group semantics. // init with Some Numbers. updates to different keys with the same timestamp // can be grouped or separate, it doesn't matter - only same-key changes behavior. push(h1, query, 5, 5) push(h1, start, 5, 5) tick.Advance(time.Second) push(h2, query, 1, 1) push(h2, start, 1, 1) tick.Advance(time.Second) push(h3, query, 0, 0) push(h3, start, 1, 1) tick.Advance(time.Second) // changing new-data-weight does not affect this test because // zero -> nonzero keeps 100% to jump-start the initial state, // rather than gradually growing from zero (which would be biased // towards lower values during movement - not bad, but not intended). t.Run("initial weights at 3s", func(t *testing.T) { // 3s elapsed, all fresh. h1 is "old" but within decayAfter so it's assumed still valid usage, err := agg.HostUsage(h1, all) require.NoError(t, err) // h1 has most of the weight. expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.83, Used: 6}, start: {Weight: 0.71, Used: 7}, // h3 uses more start than query, so h1 has less weight for start }, usage) usage, err = agg.HostUsage(h2, all) expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.17, Used: 6}, // only h1 and h2 called this, so (0.83 + 0.17)==1 start: {Weight: 0.14, Used: 7}, // h3 also had a small use, so less than query }, usage) usage, err = agg.HostUsage(h3, all) expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0}, // no query at all start: {Weight: 0.14}, // same num of starts as h2 }, usage) }) if t.Failed() { return // later tests likely invalid, verify each step before moving to the next } // advance to second h2 update, skip the others. // no data has expired yet, but h1 is now at 5s old (closing in on decayAfter) tick.Advance(2 * time.Second) push(h2, query, 1, 2) push(h2, start, 3, 3) t.Run("increased h2 weight at 5s", func(t *testing.T) { usage, err := agg.HostUsage(h1, all) require.NoError(t, err) // h1's weight reduces due to increased h2 usage expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.78, Used: 6}, // accepted query requests did not change (1 before, 1 after}) start: {Weight: 0.59, Used: 8.5}, // but start calls from h2 increased by 2 -> 0.75 weight -> +1.5 total }, usage) usage, err = agg.HostUsage(h2, all) require.NoError(t, err) expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.22, Used: 6}, // h3 has no query, so h1+h2=1.0 but the balance has shifted a bit towards h2 start: {Weight: 0.29, Used: 8.5}, // increased over last round due to more calls // plus a sanity check: same rps as h1 saw }, usage) usage, err = agg.HostUsage(h3, all) require.NoError(t, err) // h3 is almost idle but 0.1 weight changes slowly expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0, Used: 6}, // never sent any query requests start: {Weight: 0.12, Used: 8.5}, // decreased since last round, as relative usage is lower // plus a sanity check: same rps as h1 saw }, usage) }) if t.Failed() { return // later tests likely invalid, verify each step before moving to the next } // advance to 10s. // this puts h1's original data beyond decayAfter, so it will act as if it // had received 0-valued updates, to reduce its weight. tick.Advance(5 * time.Second) push(h2, query, 5, 5) push(h2, start, 5, 5) push(h3, query, 5, 5) push(h3, start, 5, 5) t.Run("h1 decayed at 10s", func(t *testing.T) { usage, err := agg.HostUsage(h1, all) require.NoError(t, err) expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.01, Used: 7.83}, // 10 accepted in last round, but only 0.75 weight, 6.0 -> 7.8 due to older data start: {Weight: 0.01, Used: 8.22}, // similar }, usage) usage, err = agg.HostUsage(h2, all) require.NoError(t, err) // h2 has slightly over half weight due to greater historical use expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.45}, start: {Weight: 0.53}, // still more starts than queries }, usage) usage, err = agg.HostUsage(h3, all) require.NoError(t, err) // h2 slightly less, due to low historical + some used by h1 expectSimilarUsage(t, map[Limit]HostUsage{ // this looks odd, but it's higher due to lower historical *total* queries, // leading to a somewhat counter-intuitive: // - smaller numerator (lower calls by this host) // - smaller denominator (lower total calls) // - higher final value (smaller denominator has greater influence) query: {Weight: 0.54}, start: {Weight: 0.46}, }, usage) }) if t.Failed() { return // later tests likely invalid, verify each step before moving to the next } // update everything, should flatten compared to previous round tick.Advance(1 * time.Second) push(h1, query, 5, 5) push(h1, start, 5, 5) push(h2, query, 5, 5) push(h2, start, 5, 5) push(h3, query, 5, 5) push(h3, start, 5, 5) t.Run("all equal at 11s is relatively flatter than before", func(t *testing.T) { usage, err := agg.HostUsage(h1, all) require.NoError(t, err) // historically lower weight = current lower weight, but a big jump from 0.01 before. // // this is likely a faster shift than we want in practice, as it'll make allowed-request // behavior quite jumpy, which is why the initial weight is likely to be around 0.5. expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.28, Used: 13.21}, // used is adjusting towards 15 start: {Weight: 0.28, Used: 13.52}, }, usage) // and weights are flattening towards 0.33 usage, err = agg.HostUsage(h2, all) require.NoError(t, err) expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.36}, start: {Weight: 0.36}, }, usage) usage, err = agg.HostUsage(h3, all) require.NoError(t, err) expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.37}, start: {Weight: 0.35}, }, usage) }) if t.Failed() { return // later tests likely invalid, verify each step before moving to the next } // do that again, should flatten further tick.Advance(1 * time.Second) push(h1, query, 5, 5) push(h1, start, 5, 5) push(h2, query, 5, 5) push(h2, start, 5, 5) push(h3, query, 5, 5) push(h3, start, 5, 5) t.Run("all equal at 12s is even flatter", func(t *testing.T) { usage, err := agg.HostUsage(h1, all) require.NoError(t, err) // still slightly below the others but it hardly matters now expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.32, Used: 14.55}, // Used RPS now quite close to 15 start: {Weight: 0.32, Used: 14.58}, }, usage) // weights flattening towards 0.33 usage, err = agg.HostUsage(h2, all) require.NoError(t, err) expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.34}, start: {Weight: 0.34}, }, usage) usage, err = agg.HostUsage(h3, all) require.NoError(t, err) expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.34}, start: {Weight: 0.34}, }, usage) }) if t.Failed() { return // later tests likely invalid, verify each step before moving to the next } // make everything fully expired so the "from expired data" branch is exercised too. // specifically this just needs to be beyond gcAfter for all data. tick.Advance(time.Hour) // push anything (need all keys to get same HostWeight map keys for `expectSimilarUsage` push(h1, query, 5, 5) push(h1, start, 5, 5) push(h2, query, 5, 5) push(h2, start, 5, 5) push(h3, query, 5, 5) push(h3, start, 5, 5) t.Run("updating expired data acts like deleted data", func(t *testing.T) { // should leap to exact values, not weighted-from-zero. usage, err := agg.HostUsage(h1, all) require.NoError(t, err) // still slightly below the others but it hardly matters now expectSimilarUsage(t, map[Limit]HostUsage{ // note: used RPS is exactly 15. if weighting from or very near zero, rather than tossing // old data entirely, this would be: (0*0.25 + 15*0.75) == 11.25 query: {Weight: 0.333, Used: 15}, start: {Weight: 0.333, Used: 15}, }, usage) // weights flattening towards 0.33 usage, err = agg.HostUsage(h2, all) require.NoError(t, err) expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.333}, start: {Weight: 0.333}, }, usage) usage, err = agg.HostUsage(h3, all) require.NoError(t, err) expectSimilarUsage(t, map[Limit]HostUsage{ query: {Weight: 0.333}, start: {Weight: 0.333}, }, usage) }) } func expectSimilarUsage( t *testing.T, expected map[Limit]HostUsage, actual map[Limit]HostUsage) { t.Helper() // report caller's line as the logger, not this expect-er // check that host weights are similar eWeight := make(map[Limit]float64, len(expected)) aWeight := make(map[Limit]float64, len(expected)) for k, v := range expected { eWeight[k] = float64(v.Weight) } for k, v := range actual { aWeight[k] = float64(v.Weight) } // check host weights are similar if !assert.InDeltaMapValues(t, eWeight, aWeight, 0.01) { t.Logf("weight(s) differ by more than 0.01:\n"+ "expected: %v\n"+ "actual: %v", eWeight, aWeight) t.Logf("verify the weights by hand, and if it's correct just update them. " + "values do not need to stay precise / there is no perfect value, " + "they just need to behave the way we want and should not change unexpectedly.") } // check that the used RPS is identical too, if non-zero eRps := make(map[Limit]float64, len(expected)) aRps := make(map[Limit]float64, len(expected)) for k, v := range expected { if v.Used != 0 { eRps[k] = float64(v.Used) } } for k, v := range actual { if v, ok := expected[k]; ok && v.Used == 0 { continue // ignore zeros in the expected map, as long as the key exists } aRps[k] = float64(v.Used) } if !assert.InDeltaMapValues(t, eWeight, aWeight, 0.01) { t.Logf("RPS(s) differ by more than 0.1:\n"+ "expected: %v\n"+ "actual: %v", eRps, aRps) t.Logf("verify the RPS values by hand, and if it's correct just update them. " + "zero-RPS ignores the value, and can be used where we don't particularly care.") } } // fairly fuzzy but somewhat representative of expected use: // benchmark "update a bunch of keys and get my load" requests, and accumulate data across all iterations. // this is roughly what a server update operation will look like. // there are intentionally overlaps in both hosts and keys to get multiple hosts per key and updates to existing data. // // while this obviously has major caveats and fake costs, the benchmark regularly runs several thousand iterations // before it settles, filling a moderate amount of data along the way (e.g. consistently all 10k keys, and >200k records in memory). // // general results imply: // - host-weight reading takes a few times longer than updating, but on my laptop the // bench takes about 3-4ms per loop for the current 100/1000/10000 values, without mutexes. // - this is relatively CPU costly, but even if our largest cluster checks in every 3s to a *single* // host, this should work out with some room to spare... and it will be far more spread // out in practice due to the number of history hosts involved. // - an attempt to cache weight calculations per key until invalidated saved about 1/2 the cpu // with a perfect cache. // - ^ the cache seems not worth the complexity, but it'd be fairly easy to add if we change our minds // // since there is only a coarse mutex in the implementation itself, the whole aggregator forces itself // to be processed serially. if this turns out to be too costly, the easy fix is probably // to shard the keys to e.g. 8 separate aggregators that can be processed concurrently. // // mutexes could be added to each Limit, but that could end up costing more in memory delays // than the fine-grained locking gains us in concurrency flexibility. benchmark first! func BenchmarkNormalUse(b *testing.B) { updateRate := 3 * time.Second agg, _ := newValid(b, defaultConfig(updateRate)) // intentionally using real clock source, time-gathering cost is relevant to benchmark agg.clock = clock.NewRealTimeSource() // aiming for vaguely realistic-ish values const ( hosts = 100 // num of hosts in pool, if rounds > hosts there will be duplicates (intentional) keysPerHost = 1000 // more domains than hosts, multiple limits per domain. globalKeys = 10000 // each host gets ~10% of requests (not consistent across calls tho) requests = 1000 // accept/reject counts ) // rand and string-formatting is a non-trivial amount of load, // so this is prepared up-front so it can be excluded. type round struct { host Identity load map[Limit]Requests keys []Limit elapsed time.Duration } rounds := make([]round, 0, b.N) for i := 0; i < b.N; i++ { keys := rand.Intn(keysPerHost) reqs := make(map[Limit]Requests, keys) for len(reqs) < keys { key := Limit(fmt.Sprintf("key %d", rand.Intn(globalKeys))) if _, ok := reqs[key]; ok { // duplicate, just try again. // dups wouldn't really invalidate the benchmark, // but it's easy to avoid and should make more stable results. continue } reqs[key] = Requests{ // values don't really matter but there's no need for them all to use the same value Accepted: rand.Intn(requests), Rejected: rand.Intn(requests), } } rounds = append(rounds, round{ host: Identity(fmt.Sprintf("host %d", rand.Intn(hosts))), load: reqs, keys: maps.Keys(reqs), elapsed: time.Duration(rand.Int63n(updateRate.Nanoseconds())), }) } b.ResetTimer() b.ReportAllocs() sawnonzero := 0 for _, r := range rounds { // == b.N times err := agg.Update(UpdateParams{ID: r.host, Load: r.load, Elapsed: r.elapsed}) require.NoError(b, err) var unused map[Limit]PerSecond // ensure non-error second return for test safety usage, err := agg.HostUsage(r.host, r.keys) require.NoError(b, err) _ = unused // ignore unused rps if len(usage) > 0 { // wrote data and later read it out, benchmark is likely functional sawnonzero++ } } b.StopTimer() // not benchmarking gc, just using it to show sanity-check metrics b.Log("N was:", b.N) m, err := agg.GC() require.NoError(b, err) b.Log("gc metrics:", m) // shows how many keys / hosts / etc we stored for manual validation // sanity check, as "all zero" should mean something like "always looking at nonexistent keys" / bad benchmark code. b.Log("nonzero results:", sawnonzero) assert.True(b, b.N < 500 || sawnonzero > 0, "no non-zero result found on a large enough benchmark, likely not benchmarking anything useful") } func TestHashmapIterationIsRandom(t *testing.T) { // is partial hashmap iteration reliably random each time so we can statistically ensure coverage, // or is it relatively fixed per map? // language spec is somewhat vague on the details here, so let's check. // // verdict: yes! looks random each time. // so we can abuse it for an amortized / interruptible cleanup tool. const keys = 100 m := map[int]struct{}{} for i := 0; i < keys; i++ { m[i] = struct{}{} } allObserved := make(map[int]struct{}, len(m)) var orderObserved [][]int singlePass := func() { for i := 0; i < keys; i++ { observed := make([]int, 0, keys/10) for k := range m { allObserved[k] = struct{}{} observed = append(observed, k) if len(observed) == cap(observed) { break // interrupt part way through } } orderObserved = append(orderObserved, observed) } } // keep trying up to 10x to make it sufficiently-unlikely that randomness will fail. // with only a single round it fails like 5% of the time, which is reasonable behavior // but too much noise to allow to fail the test suite. for i := 0; i < 10 && len(allObserved) < keys; i++ { if i > 0 { t.Logf("insufficient keys observed (%d), trying again in round %d", len(allObserved), i+1) } singlePass() } // complain if it still hasn't observed all keys if !assert.Len(t, allObserved, keys) { // super noisy when successful, so only log when failing for idx, pass := range orderObserved { t.Log("Pass", idx) for _, keys := range pass { t.Log("\t", keys) } } } } ================================================ FILE: common/quotas/global/algorithm/testdata/fuzz/FuzzMissedUpdate/264c784f7bafbf5f ================================================ go test fuzz v1 int(0) int(60) int(0) int(0) float64(0) ================================================ FILE: common/quotas/global/algorithm/testdata/fuzz/FuzzMultiUpdate/00649674e28cdc32 ================================================ go test fuzz v1 []byte("00\x0000") ================================================ FILE: common/quotas/global/algorithm/testdata/fuzz/FuzzMultiUpdate/0bb6b094a7f63d70 ================================================ go test fuzz v1 []byte("00000\x13\x89\x8c>\bەlw)4\xca|A##X\x1ca\xf6O-\xf3\x9a:\xfeS+\xe5\xafW\x1cn\xcbŴ\xe3<\xaf\xc6\xec\xb4$R\xb3\xd6Q\"\xb3\x82\x10g\xed$\xac\xdd\xc9\xfa\x88\xb7\xfe\xf3z&vY\n\x16\xfdT\x16\xcf\x12T\xbdv\x12\xf9\xb5~\xc3Y4@\x84\x16\xfc9$\xbd\xf1z\xff\x7f\xff\xff\x04\x04\xbe\xdfP\xee\xde[\xbb)\x86\v\xcd_\xd7\xca%-zO{\xccl\xbe`\x1d\xd2\xe8\x99o\xbbO\f\x10\r\x88\xaa\x1eG\x17\x19\x83}\xcfUR\xa4\x18<\xa4\xd6zhk\x8bV(\x98h\xee\xa2\xe4ns.\xb7}-u6\x83\xfa\x0f\a\x17\xd8ؑ\xd2 9ȗ(x\xa7\xfe-C\xe9{[\x01\x13\xbcE\x845C!)\x19\xf6\x1d۾\t\xd92\xfe<\xf3\xcaD2:]\x03\xfc\x16Q\x15\xc0\xd8(\x1f\x9bBg\xc1\xe8ev\xe4\xe7\xe7\xf4\x8c/\x18\x1dڱ\xfc\xb1\xfc,S<\"\x16\xfcN\x91\xc2g\xc63\x81\x87\xf8\x84\x02\x0e\xeb\xedբ\x99ˎ\x1a*\x9d\xff<)\xdb \xf0i\xa9L\x9c\xf5\x9f\xaf犨\xa1\xbc\xc4\xd4\xef\x8e>Z\t\x83?\xa0i\xdeFB\x83\x0e\x05\x05\xb2\xe0%~\xb0x\x96\xe2(FM:\xc8J\x98\xd1\xfd\x1e\x10\xef\x1b\x14\x92\x18\xab\x89\tU\xcc\x04\xe3\xe3C\x010z\x99TK\xf6N\b\u0601\xa5}t\x1dyQvem\x9c\x19]kM\xa8=\xb9:\xfd\x8e\x11\xe8\x81D\x1a%M)\xfa\xa6~k@˽\x94 U\x8d\xfbF\x17O\x905\xcc\xf2\xffMi\xa3\xdeeV\x9b\xbb\xec6\xa9\x939E\xc3\xfa\f\xbd\x92\b#,1Q\x89\xe8\x15u9\xb3{\xed\x10z\xc9!\xdfs\xe7s\x18O0\x1a\xe9\x8d\xf3\x95:\"X\x86ڰ\x19\xcb-\xfc\x0f\xa1EK`\x1fRew;/\x80\x7fP\xe1G&R\xc76\xdc,\x1a?\xe8\xcf\xc9\xeaR\xf5l\x86\xa6\xd3\tL\x14>\x87.B\x1a\xbd?Tڧ8\xa6S\xf7\xfb\x84\xf6*\xe3\xb8\xf7\x13\"c85\x93Б$\xc4{\xb9\x85J\xc0\xa9\xaam\xc5\xfc\b\xe6K\x90v\x15'J\x83\x0fK!k\xcaᚲJgw\xee\xffGYt\b\x94\xbd\x9f\xa9vG\xf0\x98\xe37\x1c\xde\x1a\xeb)n7\x8b(3H\xd1\v\xfc\x9c\xd0K\xacCd\"\xe2\xcd^i\xab\xffA\xd8[\xad\xdda\xba6\xf4\xa6\xd5\xd8`̓\xaa\x04}?%:\xe1\x9b6r2|b\xaf\x82AP\xdc\xc2\x1ct\xdc\x0e") ================================================ FILE: common/quotas/global/algorithm/testdata/fuzz/FuzzMultiUpdate/356e28f5914a0f16 ================================================ go test fuzz v1 []byte("00000000000000000000000000000000") ================================================ FILE: common/quotas/global/algorithm/testdata/fuzz/FuzzMultiUpdate/449388c309f148fd ================================================ go test fuzz v1 []byte("\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e0\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e0000") ================================================ FILE: common/quotas/global/algorithm/testdata/fuzz/FuzzMultiUpdate/582528ddfad69eb5 ================================================ go test fuzz v1 []byte("0") ================================================ FILE: common/quotas/global/algorithm/testdata/fuzz/FuzzMultiUpdate/754d7f941db60f1d ================================================ go test fuzz v1 []byte("0\xc8\xc8\xc80000") ================================================ FILE: common/quotas/global/algorithm/testdata/fuzz/FuzzMultiUpdate/84c7bfae679f54a7 ================================================ go test fuzz v1 []byte("00000000000000000000000000000001") ================================================ FILE: common/quotas/global/algorithm/testdata/fuzz/FuzzMultiUpdate/9abdd061069970e6 ================================================ go test fuzz v1 []byte("00100") ================================================ FILE: common/quotas/global/algorithm/testdata/fuzz/FuzzMultiUpdate/c50ed3d1a22fe00d ================================================ go test fuzz v1 []byte("00000000000000000000000000000000000000000000000000000000000000\bPG\x12\r\x14<\x9e2\x8d\xb2\f\xdc:\xe4\xd1\xf0G\x93z\xb6O\xbe\xec\xc41\xf9\x9eq|\x1c\f\x03\x83D\x90\x18\xfd\x96\xb6Z\x1a\xf0\xc4\xc9\"\x14\x15\xca\xcaw\x04\xd7\xda\xce%\x92&\x04#\x8d_e\xc1\xa0\xc4\x1e\x82\x0e\xe05\x1a҃4\xfe1-\x00}\xb4\xb1e\\~Ġd\x83\x8b\x93\x9fl@25\xeam\x19\xb4\xd5\x1d\x9c\x1b6\x7f\x96\xc0\x94\xa4\xd2\xdf\xfc\x00\x0e\xb2AOCm\xe6L\xdc~\x1e\xeb\x9a}\x1co\xa1\xc5u/29\x80JL9\xf2\xe8+\"\xe6_\x99'\xaajJt\x99\x12\x04\xfb\xff&p\xac3\x10g<\x1f\xfeX\x87P\xd8\x17\x13ڃ\xa9&\x8akQ\xf3\x13=!\xa5\x8d\xa4)\r\bC^E\x83'ʔ\x00\xac\x10w\xc3\xd9ӧ\x1e[A\xffuEQn\x8c*ar\x8b49\xc3\xce\x03u\xf3\nD\xc5A\xa8\x98\x14\xba.\x98A\x8b\x1f=<\xa2\x9a\b\\\x00(>h\xbc\x1d\xbd+/Y\x88\xb2h\x15\xf5\xd6z\x8e`\xd2[x Ϣ\xacP3h\xba\x9bWs\a\xf4'-\xb3\x97\x12g}\xcf\xea\xcarD\x12=\xc5\x16\xd9\xedâ[s\x9f\x17uX\xd5\xfa\x9ee\x85\xd0\xe0X\xe6\x1eS\xf1\x80E\xd79\xfaQ=\xc5\xe22\x85\xb1j\x7fy[+eK{\xb0\xaa'\xe8fA\x7f\x1a\xae\x8dMDŽ\xa3\x93\x13\x10͗{Qcs\xfe\x8c\xe6n\xc9\xed\xe9!|\f/W-k>}$\xed\x9c[\x990\x18\bE4|\t;\x0e\xba\x03\xa9J\xf5\x15u\xc6=\xb4\xc6WCI\xb4\xfb\xab!\x8eϽ\xb1\xad\x8e\x13\xd1\xecω\xcej\xd4\"\x8d2z\x86t4on\x189%`\xb8\xad\xbc\xa0\xae$\x81ѾU\x1fh\xec\xe4n\a\x10䖸\x883\xa9}\x94\xe6\xac:\xa7\xfbBy^\x1b\xa2\"'(e\xfcK\xa4\x01!D\xb2\xe4)\x98Y/{\x8d\x82\x97\xef\v\xe6X\xe3\x04տ\xb4\x95\xee\x8bY\x9f\x93a \xbfIǜ\xb0\x17]\xc1\xd8T\xbb\xd3\x05\xb9\x8eP\xee?\xa5Gx\x80G.&\x84'n\x93, \xdd\x13b\xf1\xb72V/8\xe0Fb\x10a\xe7\x94\xdb\x03k\x82i\xf46f=+\xce\x13E\xd2.b\xb1\x84ʴ\x0f\xf9\xac\xf8ʑ\xef+J\xe0\x7f&\xe4\xa02\xa2\x04\xea\x17k\xb3T\xb9\x8c\xd1І_o\xbby|\xa9\x96L\xc3F\xc7c\x03\xf7\xf6\x9d\x86\xe0'\x1e\\\x1aXZ\a\x9d\x9d\x01\xf3\xee\x1bf:W\x8c\x83\xacX\xb7\fs@{r\xa1\x94\xb7\xb9gwX/\x1c\x05\xb4np\xbfM\x1bL\x91\x89\xa9\xf7\x8bP\x8e\x99BA\xf3ԞU>\rZ\xb4S\xa6\xf2d\xf2\xdbo\xca\x1a%\"7\xf7\xf6\xb0\xc8\xcb\x11\x1b\xcb\x06Cq\x85\x7fW\xe2\xc1\xe99\xf7\xc6? gcAfterIdle || c.shouldDeleteKey(mode, true) { c.logger.Debug( "deleting local ratelimiter", tag.GlobalRatelimiterKey(string(k)), tag.GlobalRatelimiterIdleCount(counts.Idle), ) c.local.Delete(k) return true // continue iterating, possibly delete others too } c.sendMetrics(k, true, mode, counts) return true }) }() capacity := c.global.Len() capacity += capacity / 10 // size may grow while we range, try to avoid reallocating in that case usage := make(map[shared.GlobalKey]rpc.Calls, capacity) startups, failings, globals := 0, 0, 0 c.global.Range(func(k shared.LocalKey, v *internal.FallbackLimiter) bool { gkey := c.km.LocalToGlobal(k) counts, startup, failing := v.Collect() mode := c.keyMode(gkey) if counts.Idle > gcAfterIdle || c.shouldDeleteKey(mode, false) { c.logger.Debug( "deleting global ratelimiter", tag.GlobalRatelimiterKey(string(k)), tag.GlobalRatelimiterIdleCount(counts.Idle), ) c.global.Delete(k) return true // continue iterating, possibly delete others too } if startup { startups++ } if failing { failings++ } if !(startup || failing) { globals++ } usage[gkey] = rpc.Calls{ Allowed: counts.Allowed, Rejected: counts.Rejected, } c.sendMetrics(k, false, mode, counts) return true }) // track how often we're using fallbacks vs non-fallbacks. // with per-host metrics this can tell us if a host is abnormal // compared to the rest of the cluster, e.g. persistent failing values. c.scope.RecordHistogramValue(metrics.GlobalRatelimiterStartupUsageHistogram, float64(startups)) c.scope.RecordHistogramValue(metrics.GlobalRatelimiterFailingUsageHistogram, float64(failings)) c.scope.RecordHistogramValue(metrics.GlobalRatelimiterGlobalUsageHistogram, float64(globals)) if len(usage) > 0 { sw := c.scope.StartTimer(metrics.GlobalRatelimiterUpdateLatency) c.doUpdate(now.Sub(lastGatherTime), usage) sw.Stop() } <-localMetricsDone // should be much faster than doUpdate, unless it's no-opped lastGatherTime = now } } } func (c *Collection) sendMetrics(lkey shared.LocalKey, isLocalLimiter bool, mode keyMode, usage internal.UsageMetrics) { // emit quota information to make monitoring easier. // regrettably this will only be emitted when the key is (recently) in use, but // for active users this is probably sufficient. other cases will probably need // a continual "emit all quotas" loop somewhere. c.scope. Tagged(metrics.GlobalRatelimiterKeyTag(string(lkey))). UpdateGauge(metrics.GlobalRatelimiterQuota, float64(c.targetRPS(lkey))) limitType := "global" if isLocalLimiter { limitType = "local" } limitTypeIsPrimary := isLocalLimiter && mode.isLocalPrimary() || !isLocalLimiter && mode.isGlobalPrimary() scope := c.scope.Tagged( metrics.GlobalRatelimiterKeyTag(string(lkey)), metrics.GlobalRatelimiterTypeTag(limitType), // useful for being able to tell when a key is "in use" or not, e.g. for monitoring purposes metrics.GlobalRatelimiterIsPrimary(limitTypeIsPrimary), ) scope.AddCounter(metrics.GlobalRatelimiterAllowedRequestsCount, int64(usage.Allowed)) scope.AddCounter(metrics.GlobalRatelimiterRejectedRequestsCount, int64(usage.Rejected)) } // doUpdate pushes usage data to aggregators. mutates `usage` as it runs. func (c *Collection) doUpdate(since time.Duration, usage map[shared.GlobalKey]rpc.Calls) { ctx, cancel := context.WithTimeout(c.ctx, 10*time.Second) // TODO: configurable? possibly even worth cutting off after 1s. defer cancel() res := c.aggs.Update(ctx, since, usage) if res.Err != nil { // should not happen outside pretty major errors, but may recover next time. // if sustained, e.g. due to a bug, this should eventually cause the affected // keys to use their fallback behavior, which is easily alerted on. c.logger.Error("aggregator update error", tag.Error(res.Err)) } // either way, process all weights we did successfully retrieve. for gkey, info := range res.Weights { delete(usage, gkey) // clean up the list so we know what was missed lkey, err := c.km.GlobalToLocal(gkey) if err != nil { // should not happen unless agg returns keys that were not asked for, // and are not for this collection c.logger.Error("bad global key structure returned", tag.Error(err)) continue } // < 0, nan, and inf in `info` are prevented by Update and do not need to be handled here, // though the math below could create new irrational values. target := rate.Limit(c.targetRPS(lkey)) limiter := c.global.Load(lkey) fallbackTarget := limiter.FallbackLimit() boosted := boostRPS(target, fallbackTarget, info.Weight, info.UsedRPS) limiter.Update(boosted) } // mark all non-returned limits as failures. // this also handles request errors: all requested values are failed // because none of them have been deleted above. // // aside from request errors this should be rare and might represent a race // in the aggregator, but the semantics for handling it are pretty clear // either way. for globalkey := range usage { localKey, err := c.km.GlobalToLocal(globalkey) if err != nil { // should not happen, would require local mapping to be wrong c.logger.Error("bad global key structure in local-only path", tag.Error(err)) continue } // requested but not returned, bump the fallback fuse c.global.Load(localKey).FailedUpdate() } } func boostRPS(target, fallback rate.Limit, weight float64, usedRPS float64) rate.Limit { baseline := target * rate.Limit(weight) // low weights lead to low per-host overage allowed, and this can lead to // restricting low-RPS-slightly-bursty requests quite a lot more than intended, // despite more than enough unused quota remaining at all times. // // as a partial mitigation, "boost" low-weight values, allowing them to use // more of the unused RPS than their weight would normally imply, up to the // fallback's limit. // as overall usage increases, this "allowed overage" will shrink, helping // ensure it keeps converging towards the global target RPS. if baseline < fallback { // unused should not go below zero, e.g. if target was lowered, // so this cannot reduce below the fair baseline. unused := math.Max(0, float64(target)-usedRPS) boosted := math.Min( // with many bursty low-weight hosts, this may allow too much. // currently this isn't really a concern, but this could be adjusted // by num-of-low-hosts or something if needed. float64(baseline)+unused, // can't exceed the local fallback value though. // this is also what would be allowed if this limit was garbage collected, // so it's already established as a "safe enough" value. float64(fallback), ) return rate.Limit(boosted) } // any host with a weighted target higher than the fallback will already be // allowing a relatively large "growth room" on top of its actual usage, so // they don't need this boost. return baseline } ================================================ FILE: common/quotas/global/collection/collection_fuzz_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package collection import ( "math" "testing" "golang.org/x/time/rate" ) func FuzzBoostRPS(f *testing.F) { f.Fuzz(func(t *testing.T, target, fallback, weight, used float64) { target = math.Abs(target) fallback = math.Abs(fallback) used = math.Abs(used) weight = weight - math.Floor(weight) // trim to 0..1 if anyInvalid(target, fallback, used, weight) { t.Skip("bad numbers") } if target < fallback { // fallback is always equal or below target, as it's `target / num hosts`. target, fallback = fallback, target } boosted := boostRPS(rate.Limit(target), rate.Limit(fallback), weight, used) if boosted > rate.Limit(target) { // should never exceed whole-cluster target t.Error("boosted beyond configured limit") } if boosted < 0 { // should never become negative. // // the ratelimiter treats negatives as zero, so this is "fine", // but it's likely a sign of flawed logic. t.Error("boosted is negative") } if math.IsNaN(float64(boosted)) { t.Error("boosted is NaN") } if math.IsInf(float64(boosted), 0) { t.Error("boosted is inf") } }) } func anyInvalid(f ...float64) bool { for _, v := range f { if math.IsNaN(v) { return true } if math.IsInf(v, 0) { return true } } return false } ================================================ FILE: common/quotas/global/collection/collection_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package collection import ( "context" "reflect" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/atomic" "go.uber.org/goleak" "go.uber.org/mock/gomock" "go.uber.org/zap/zapcore" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" dynamicquotas "github.com/uber/cadence/common/dynamicconfig/quotas" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/quotas/global/collection/internal" "github.com/uber/cadence/common/quotas/global/rpc" "github.com/uber/cadence/common/quotas/global/shared" ) func TestLifecycleBasics(t *testing.T) { defer goleak.VerifyNone(t) // must shut down cleanly ctrl := gomock.NewController(t) logger, logs := testlogger.NewObserved(t) c, err := New( "test", quotas.NewCollection(quotas.NewMockLimiterFactory(ctrl)), quotas.NewCollection(quotas.NewMockLimiterFactory(ctrl)), func(opts ...dynamicproperties.FilterOption) time.Duration { return time.Second }, nil, // not used func(globalRatelimitKey string) string { return string(modeGlobal) }, nil, // no rpc expected as there are no metrics to submit logger, metrics.NewNoopMetricsClient(), ) require.NoError(t, err) mts := clock.NewMockedTimeSource() c.TestOverrides(t, &mts, nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() require.NoError(t, c.OnStart(ctx)) mts.BlockUntil(1) // created timer in background updater mts.Advance(time.Second) // trigger the update // clockwork does not provide a way to wait for "a ticker or timer event was consumed", // so just sleep for a bit to allow that branch of the select statement to be followed. // // it seems *possible* for clockwork to provide this, maybe we should build it? time.Sleep(time.Millisecond) require.NoError(t, c.OnStop(ctx)) // will fail if the tick did not begin processing, e.g. no tick or it raced with shutdown. // without a brief sleep after advancing time, this extremely rarely fails (try 10k tests to see) assert.Len(t, logs.FilterMessage("update tick").All(), 1, "no update logs, did it process the tick? all: %#v", logs.All()) } func TestCollectionLimitersCollectMetrics(t *testing.T) { tests := map[string]struct { mode keyMode local internal.UsageMetrics global internal.UsageMetrics }{ "disabled": { mode: modeDisabled, }, "local": { mode: modeLocal, local: internal.UsageMetrics{Allowed: 1}, }, "global": { mode: modeGlobal, global: internal.UsageMetrics{Allowed: 1}, }, "local-shadow": { mode: modeLocalShadowGlobal, local: internal.UsageMetrics{Allowed: 1}, global: internal.UsageMetrics{Allowed: 1}, }, "global-shadow": { mode: modeGlobalShadowLocal, local: internal.UsageMetrics{Allowed: 1}, global: internal.UsageMetrics{Allowed: 1}, }, } for name, test := range tests { test := test t.Run(name, func(t *testing.T) { t.Parallel() // anything non-zero localLimiters := dynamicquotas.NewSimpleDynamicRateLimiterFactory(func(domain string) int { return 1 }) globalLimiters := dynamicquotas.NewSimpleDynamicRateLimiterFactory(func(domain string) int { return 10 }) c, err := New( "test", quotas.NewCollection(localLimiters), quotas.NewCollection(globalLimiters), func(opts ...dynamicproperties.FilterOption) time.Duration { return time.Second }, func(domain string) int { return 5 }, func(globalRatelimitKey string) string { return string(test.mode) }, nil, testlogger.New(t), metrics.NewNoopMetricsClient(), ) require.NoError(t, err) // not starting, in principle it could Collect() in the background and break this test. // perform one call _ = c.For("test").Allow() // check counts on limits globalEvents := make(map[shared.LocalKey]internal.UsageMetrics) c.global.Range(func(k shared.LocalKey, v *internal.FallbackLimiter) bool { usage, _, _ := v.Collect() _, ok := globalEvents[k] require.False(t, ok, "data key should not already exist: %v", k) globalEvents[k] = internal.UsageMetrics{ Allowed: usage.Allowed, Rejected: usage.Rejected, } return true }) localEvents := make(map[shared.LocalKey]internal.UsageMetrics) c.local.Range(func(k shared.LocalKey, v internal.CountedLimiter) bool { _, ok := localEvents[k] require.False(t, ok, "data key should not already exist: %v", k) localEvents[k] = v.Collect() return true }) if reflect.ValueOf(test.global).IsZero() { assert.Empty(t, globalEvents, "unexpected global events, should be none") } else { assert.Equal(t, map[shared.LocalKey]internal.UsageMetrics{"test": test.global}, globalEvents, "incorrect global metrics") } if reflect.ValueOf(test.local).IsZero() { assert.Empty(t, localEvents, "unexpected local events, should be none") } else { assert.Equal(t, map[shared.LocalKey]internal.UsageMetrics{"test": test.local}, localEvents, "incorrect local metrics") } }) } } func TestCollectionSubmitsDataAndUpdates(t *testing.T) { defer goleak.VerifyNone(t) // must shut down cleanly ctrl := gomock.NewController(t) // anything non-zero limiters := dynamicquotas.NewSimpleDynamicRateLimiterFactory(func(domain string) int { return 1 }) logger, observed := testlogger.NewObserved(t) aggs := rpc.NewMockClient(ctrl) c, err := New( "test", quotas.NewCollection(limiters), quotas.NewCollection(limiters), func(opts ...dynamicproperties.FilterOption) time.Duration { return time.Second }, func(domain string) int { return 10 }, // target 10 rps / one per 100ms func(globalRatelimitKey string) string { return string(modeGlobal) }, aggs, logger, metrics.NewNoopMetricsClient(), ) require.NoError(t, err) mts := clock.NewMockedTimeSource() c.TestOverrides(t, &mts, nil) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() require.NoError(t, c.OnStart(ctx)) // generate some data. // these start with the collections' limits, i.e. 1 token, so only one request is allowed. someLimiter := c.For("something") res := someLimiter.Reserve() assert.True(t, res.Allow(), "first request should have been allowed") res.Used(true) assert.False(t, someLimiter.Allow(), "second request on the same domain should have been rejected") assert.NoError(t, c.For("other").Wait(ctx), "request on a different domain should be allowed") // all limiters are now drained, and will take ~1s to recover normally. // prep for the calls called := make(chan struct{}, 1) aggs.EXPECT().Update(gomock.Any(), gomock.Any(), map[shared.GlobalKey]rpc.Calls{ "test:other": { Allowed: 1, Rejected: 0, }, "test:something": { Allowed: 1, Rejected: 1, }, }).DoAndReturn(func(ctx context.Context, period time.Duration, load map[shared.GlobalKey]rpc.Calls) rpc.UpdateResult { called <- struct{}{} return rpc.UpdateResult{ Weights: map[shared.GlobalKey]rpc.UpdateEntry{ "test:something": {Weight: 1, UsedRPS: 2}, // should recover a token in 100ms // "test:other": // not returned, should not change weight/rps and stay at 1s }, Err: nil, } }) mts.BlockUntil(1) // need to have created timer in background updater mts.Advance(time.Second) // trigger the update. also fills all ratelimiters. // wait until the calls occur select { case <-called: case <-time.After(time.Second / 2): // keep total wait shorter than 1s to avoid refilling the slow token, just in case t.Fatal("did not make an rpc call after 1/2s") } // crash if more calls occur close(called) // wait for the updates to be sent to the ratelimiters, and for at least one "something"'s 100ms token to recover time.Sleep(150 * time.Millisecond) // and make sure updates occurred assert.False(t, c.For("other").Allow(), "should be no recovered tokens yet on the slow limit") // less than 1 second == no tokens assert.True(t, c.For("something").Allow(), "should have allowed one request on the fast limit") // over 100ms (got updated rate) == at least one token // specifically: because this is the first update to this limiter, it should now allow 10 requests, because the token bucket should be full. // just check once though, no need to be precise here. assert.True(t, c.For("something").Allow(), "after the initial update, the fast limiter should have extra tokens available") assert.NoError(t, c.OnStop(ctx)) // and make sure no non-fatal errors were logged for _, log := range observed.All() { t.Logf("observed log: %v", log.Message) assert.Lessf(t, log.Level, zapcore.ErrorLevel, "should not be any error logs, got: %v", log.Message) } } func TestTogglingMode(t *testing.T) { defer goleak.VerifyNone(t) aggs := rpc.NewMockClient(gomock.NewController(t)) var mode atomic.Value mode.Store(modeDisabled) c, err := New( "test", quotas.NewCollection(dynamicquotas.NewSimpleDynamicRateLimiterFactory(func(domain string) int { return 1 })), quotas.NewCollection(dynamicquotas.NewSimpleDynamicRateLimiterFactory(func(domain string) int { return 1 })), func(opts ...dynamicproperties.FilterOption) time.Duration { return time.Second }, // update every second func(domain string) int { return 1 }, func(globalRatelimitKey string) string { return string(mode.Load().(keyMode)) }, aggs, testlogger.New(t), metrics.NewNoopMetricsClient(), ) require.NoError(t, err) ts := clock.NewMockedTimeSource() c.TestOverrides(t, &ts, nil) require.NoError(t, c.OnStart(context.Background()), "failed to start") defer func() { // reduces goroutine-noise on failure require.NoError(t, c.OnStop(context.Background()), "failed to stop") }() ts.BlockUntil(1) // background ticker created // must tick "incrementally" or updates may be lost unrealistically tick := func(n int) { for i := 0; i < n; i++ { ts.Advance(time.Second) time.Sleep(10 * time.Millisecond) // allow code time to run to get back to waiting } } // a bit of shenaniganry: // use the same mock for all the sub-tests, but don't make any assertions on the number of calls. // if you do, it "blames" the parent test rather than the sub-test, which is hard to debug. // // this could be resolved by rerunning all steps upper { upper = vint } return true }) assert.LessOrEqual(t, int64(loops), upper, // regrettably not guaranteed due to deletions, but I have yet to see it. // if this becomes an issue, probably just delete it. "did not observe a value at least as high as the number of loops. "+ "not technically impossible, just very unlikely", ) t.Logf( "Metrics for cpu %v:\n"+ "\tKey == value (1=>1-1): %v\n"+ "\tValue higher (5=>5-100): %v\n"+ "\tValue lower (100=>100-5): %v\n"+ "\tNumber of iterations: %v\n"+ "\tHighest saved create: %v\n"+ // same or higher than iterations "\tTotal num of creates: %v", // same or higher than saved runtime.GOMAXPROCS(0), same, higher, lower, loops, upper, creates.Load(), ) } ================================================ FILE: common/quotas/global/collection/internal/counted.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package internal import ( "context" "go.uber.org/atomic" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/quotas" ) type ( CountedLimiter struct { wrapped quotas.Limiter usage *AtomicUsage } countedReservation struct { wrapped clock.Reservation usage *AtomicUsage // reference to the reservation's limiter's usage } AtomicUsage struct { allowed, rejected, idle atomic.Int64 } UsageMetrics struct { Allowed, Rejected, Idle int } ) var _ quotas.Limiter = CountedLimiter{} var _ clock.Reservation = countedReservation{} func NewCountedLimiter(limiter quotas.Limiter) CountedLimiter { return CountedLimiter{ wrapped: limiter, usage: &AtomicUsage{}, } } func (c CountedLimiter) Allow() bool { allowed := c.wrapped.Allow() c.usage.Count(allowed) return allowed } func (c CountedLimiter) Wait(ctx context.Context) error { err := c.wrapped.Wait(ctx) c.usage.Count(err == nil) return err } func (c CountedLimiter) Reserve() clock.Reservation { return countedReservation{ wrapped: c.wrapped.Reserve(), usage: c.usage, } } func (c CountedLimiter) Limit() rate.Limit { return c.wrapped.Limit() } func (c CountedLimiter) Collect() UsageMetrics { return c.usage.Collect() } func (c countedReservation) Allow() bool { return c.wrapped.Allow() } func (c countedReservation) Used(wasUsed bool) { c.wrapped.Used(wasUsed) c.usage.idle.Store(0) if c.Allow() { if wasUsed { // only counts as allowed if used, else it is hopefully rolled back. // this may or may not restore the token, but it does imply "this limiter did not limit the event". c.usage.Count(true) } // else it was canceled, and not "used". // // currently these are not tracked because some other rejection will occur // and be emitted in all our current uses, but with bad enough luck or // latency before canceling it could lead to misleading metrics. } else { // these reservations cannot be waited on so they cannot become allowed, // and they cannot be returned, so they are always rejected. // // specifically: it is likely that `wasUsed == Allow()`, so false cannot be // trusted to mean "will not use for some other reason", and the underlying // rate.Limiter did not change state anyway because it returned the // pending-token before becoming a clock.Reservation. c.usage.Count(false) } } func (a *AtomicUsage) Count(allowed bool) { if allowed { a.allowed.Add(1) } else { a.rejected.Add(1) } a.idle.Store(0) } func (a *AtomicUsage) Collect() UsageMetrics { m := UsageMetrics{ Allowed: int(a.allowed.Swap(0)), Rejected: int(a.rejected.Swap(0)), } if m.Allowed+m.Rejected == 0 { m.Idle = int(a.idle.Add(1)) } // else 0 return m } ================================================ FILE: common/quotas/global/collection/internal/counted_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package internal import ( "context" "math/rand" "testing" "time" "github.com/stretchr/testify/assert" "golang.org/x/sync/errgroup" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/quotas" ) func TestUsage(t *testing.T) { t.Run("tracks allow", func(t *testing.T) { ts := clock.NewMockedTimeSource() counted := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, 1, 1)) assert.True(t, counted.Allow(), "should match wrapped limiter") assert.Equal(t, UsageMetrics{1, 0, 0}, counted.Collect()) assert.False(t, counted.Allow(), "should match wrapped limiter") assert.Equal(t, UsageMetrics{0, 1, 0}, counted.Collect(), "previous collect should have reset counts, and should now have just a reject") }) t.Run("tracks wait", func(t *testing.T) { ts := clock.NewMockedTimeSource() counted := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, 1, 1)) // consume the available token requireQuickly(t, 100*time.Millisecond, func() { assert.NoError(t, counted.Wait(context.Background()), "should match wrapped limiter") assert.Equal(t, UsageMetrics{1, 0, 0}, counted.Collect()) }) // give up before the next token arrives requireQuickly(t, 100*time.Millisecond, func() { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) defer cancel() assert.Error(t, counted.Wait(ctx), "should match wrapped limiter") assert.Equal(t, UsageMetrics{0, 1, 0}, counted.Collect(), "previous collect should have reset counts, and should now have just a reject") }) // wait for the next token to arrive requireQuickly(t, 100*time.Millisecond, func() { var g errgroup.Group g.Go(func() error { // waits for token to arrive assert.NoError(t, counted.Wait(context.Background()), "should match wrapped limiter") assert.Equal(t, UsageMetrics{1, 0, 0}, counted.Collect()) return nil }) g.Go(func() error { time.Sleep(time.Millisecond) ts.Advance(time.Second) // recover one token return nil }) assert.NoError(t, g.Wait()) }) }) t.Run("tracks reserve", func(t *testing.T) { ts := clock.NewMockedTimeSource() lim := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, 1, 1)) r := lim.Reserve() assert.True(t, r.Allow(), "should have used the available burst") assert.Equal(t, UsageMetrics{0, 0, 1}, lim.Collect(), "allowed tokens should not be counted until they're used") r.Used(true) assert.Equal(t, UsageMetrics{1, 0, 0}, lim.Collect(), "using the token should reset idle and count allowed") r = lim.Reserve() assert.False(t, r.Allow(), "should not have a token available") r.Used(false) assert.Equal(t, UsageMetrics{0, 1, 0}, lim.Collect(), "not-allowed reservations count as rejection") }) // largely for coverage t.Run("supports Limit", func(t *testing.T) { rps := rate.Limit(1) lim := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(clock.NewMockedTimeSource(), rps, 1)) assert.Equal(t, rps, lim.Limit()) }) } func TestRegression_ReserveCountsCorrectly(t *testing.T) { run := func(t *testing.T, lim quotas.Limiter, advance func(time.Duration), collect func() UsageMetrics) { allowed, returned, rejected := 0, 0, 0 for i := 0; ; i++ { if rejected > 3 { // normal exit: some rejects occurred. break // just to get more than 1 to be more interesting } if i > 1_000 { // infinite loop guard because it's a real mess to debug t.Error("too many attempts, test is not sane. allowed:", allowed, "rejected:", rejected, "returned:", returned) break } r := lim.Reserve() if rand.Intn(2) == 0 { // time advancing before canceling should not affect this test because it is not concurrent, // so only do it sometimes to make sure that's true advance(time.Millisecond) } if r.Allow() { if i%2 == 0 { allowed++ r.Used(true) } else { returned++ r.Used(false) } } else { rejected++ // try with both true and false. // expected use is to call with false on all rejects, but it should not be required r.Used(i%2 == 0) } } usage := collect() t.Logf("usage: %#v", usage) assert.NotZero(t, allowed, "should have allowed some requests") assert.Equal(t, allowed, usage.Allowed, "wrong num of requests allowed") assert.Equal(t, rejected, usage.Rejected, "wrong num of requests rejected") assert.Equal(t, 0, usage.Idle, "limiter should never be idle in this test") } t.Run("counted", func(t *testing.T) { // "base" counting-limiter should count correctly ts := clock.NewMockedTimeSource() wrapped := clock.NewRateLimiterWithTimeSource(ts, 1, 100) lim := NewCountedLimiter(wrapped) run(t, lim, ts.Advance, lim.Collect) }) t.Run("shadowed", func(t *testing.T) { // "shadowed" should call the primary correctly at the very least ts := clock.NewMockedTimeSource() wrapped := clock.NewRateLimiterWithTimeSource(ts, 1, 100) counted := NewCountedLimiter(wrapped) lim := NewShadowedLimiter(counted, allowlimiter{}) run(t, lim, ts.Advance, counted.Collect) }) t.Run("fallback", func(t *testing.T) { // "fallback" uses a different implementation, but it should count exactly the same. // TODO: ideally it would actually be the same code, but that's a bit awkward due to needing different interfaces. ts := clock.NewMockedTimeSource() wrapped := clock.NewRateLimiterWithTimeSource(ts, 1, 100) l := NewFallbackLimiter(allowlimiter{}) l.Update(1) // allows using primary, else it calls the fallback l.primary = wrapped // cheat, just swap it out run(t, l, ts.Advance, func() UsageMetrics { u, _, _ := l.Collect() return u }) }) } // Wait-based tests can block forever if there's an issue, better to fail fast. func requireQuickly(t *testing.T, timeout time.Duration, cb func()) { done := make(chan struct{}) go func() { defer close(done) cb() }() wait := time.NewTimer(timeout) defer wait.Stop() select { case <-done: case <-wait.C: // should be far faster t.Fatal("timed out waiting for callback to return") } } ================================================ FILE: common/quotas/global/collection/internal/fallback.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Package internal protects these types' concurrency primitives and other // internals from accidental misuse. package internal import ( "context" "math" "sync/atomic" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/quotas" ) type ( // FallbackLimiter wraps a "primary" [rate.Limiter] with a "fallback" Limiter (i.e. a [github.com/uber/cadence/common/quotas.Limiter]) // to use before the primary is fully ready, or after too many attempts to update the "primary" limit have failed. // // Intended use is: // - "primary" is the global-load-balanced ratelimiter, with weights updating every few seconds // - "fallback" is the "rps / num-of-hosts" ratelimiters we use many other places, which do not need to exchange data to work // - When "primary" is not yet initialized (no updates yet at startup) or the global-load-balancing system is broken (too many failures), // this limiter internally switches to the "fallback" limiter until it recovers. Otherwise, "primary" is preferred. // - both limiters are called all the time to keep their used-tokens roughly in sync (via shadowedLimiter), so they // can be switched between without allowing bursts due to the "other" limit's token bucket filling up. // // The owning global/collection.Collection uses this as follows: // - every couple seconds, Collect() usage information from all limiters // - submit this to aggregator hosts, which return how much weight to allow this host per limiter // - per limiter: // - if a weight was returned, Update(rps) it so the new value is used in the future // - else, call FailedUpdate() to shorten the "use fallback" fuse, possibly switching to the fallback by doing so // // During this sequence, a requested limit may not be returned by an aggregator for two major reasons, // and will result in a FailedUpdate() call to shorten a "use fallback logic" fuse: // 1. this limit is legitimately unused (or idle too long) and no data exists for it // 2. ring re-sharding led to losing track of the limit, and it is now owned by a host with insufficient data // // To mitigate the impact of the second case, missing data is temporarily ignored, and the previously-configured // Update rate is used without any changes. This gives the aggregating host time to fill in its data, and a later // cycle should return "real" values that match actual usage, Update-ing this limiter. // // If no data has been returned for a sufficiently long time, the "primary" ratelimit will be ignored, and // the fallback limit will be used exclusively. This is intended as a safety fallback, e.g. during initial // rollout/rollback and outage scenarios, normal use is not expected to rely on it. // // ----- // // Implementation notes: // - atomics are used as values instead of pointers simply for data locality, which requires FallbackLimiter // to be used as a pointer. `go vet -copylocks` should be enough to ensure this is done correctly, which is // enforced by `make lint`. // - the variety of atomics here is almost certainly NOT necessary for performance, // it was just possible and simple enough to use, and it makes deadlocks trivially impossible. // but rate.Limiter already use mutexes internally, so adding another to track counts / fallback // decisions / etc should be entirely fine, if it becomes needed. FallbackLimiter struct { // number of failed updates, for deciding when to use fallback failedUpdates atomic.Int64 // counts of what was allowed vs not. // this is not collected by a wrapper because it would hide the limit-setting methods, // but it is otherwise identical to a CountedLimiter around this FallbackLimiter. usage AtomicUsage // primary / desired limiter, based on load-balanced information. // // note that use and modification is NOT synchronized externally, // so updates and deciding when to use the fallback must be done carefully // to avoid undesired combinations when they interleave. // // if needed or desired, just switch to locks, they should be more than // fast enough because the limiter already uses locking internally. primary clock.Ratelimiter // fallback used when failedUpdates exceeds maxFailedUpdates, // or prior to the first Update call with globally-adjusted values. fallback quotas.Limiter } ) const ( // when failed updates exceeds this value, use the fallback maxFailedUpdates = 9 // at startup / new limits in use, use the fallback logic. // that's expected / normal behavior as we have no data yet. // // positive values risk being interpreted as a "failure" though, so start deeply // negative, so it can be identified as "still starting up". // min-int64 has more than enough room to "count" failed updates for eons // without becoming positive, so it should not risk being misinterpreted. initialFailedUpdates = math.MinInt64 ) // NewFallbackLimiter returns a quotas.Limiter that uses a simpler fallback when necessary, // and attempts to keep both the fallback and the "real" limiter "warm" by mirroring calls // between the two regardless of which is being used. func NewFallbackLimiter(fallback quotas.Limiter) *FallbackLimiter { l := &FallbackLimiter{ // start from 0 as a default, the limiter is unused until it is updated. // // caution: it's important to not call any time-advancing methods on this until // after the first update, so the token bucket will fill properly. // // this (partially) mimics how new ratelimiters start with a full token bucket, // but there does not seem to be any way to perfectly mimic it without using locks, // and hopefully precision is not needed. primary: clock.NewRatelimiter(0, 0), fallback: fallback, } l.failedUpdates.Store(initialFailedUpdates) return l } // Collect returns the current allowed/rejected values and resets them to zero (like CountedLimiter). // it also returns info about if this limiter is currently starting up or in a "use fallback due to failures" mode. // "starting" is expected any time a limit is new (or not recently used), "failing" may happen rarely // but should not ever be a steady event unless something is broken. func (b *FallbackLimiter) Collect() (usage UsageMetrics, starting, failing bool) { usage = b.usage.Collect() starting, failing = b.mode() return usage, starting, failing } func (b *FallbackLimiter) mode() (startingUp, tooManyFailures bool) { failed := b.failedUpdates.Load() startingUp = failed < 0 tooManyFailures = failed > maxFailedUpdates return startingUp, tooManyFailures } func (b *FallbackLimiter) useFallback() bool { starting, fallback := b.mode() return starting || fallback } // Update adjusts the underlying "primary" ratelimit, and resets the fallback fuse. // This implies switching to the "primary" limiter - if that is not desired, call Reset() immediately after. func (b *FallbackLimiter) Update(lim rate.Limit) { // caution: order here matters, to prevent potentially-old limiter values from being used // before they are updated. // // this is probably not going to be noticeable, but some users are sensitive to ANY // requests being ratelimited. updating the fallback fuse last should be more reliable // in preventing that from happening when they would not otherwise be limited, e.g. with // the initial value of 0 burst, or if this rps was very low previously and is now high // (because the token bucket will be empty). defer func() { // reset the use-fallback fuse, which may also (re)enable using the "primary" limiter b.failedUpdates.Store(0) }() if b.primary.Limit() == lim { return } b.primary.SetLimit(lim) b.primary.SetBurst(max(1, int(lim))) // 0 burst blocks all requests, so allow at least 1 and rely on rps to fill sanely } // FailedUpdate should be called when a limit fails to update from an aggregator, // possibly implying some kind of problem, which may be unique to this limit. // // After crossing a threshold of failures (currently 10), the fallback will be switched to. func (b *FallbackLimiter) FailedUpdate() (failures int) { failures = int(b.failedUpdates.Add(1)) // always increment the count for monitoring purposes return failures } // Reset defers to the fallback limiter until an update is received. // // This is intended to be used when the current limit is no longer trustworthy for some reason, // determined externally rather than from too many FailedUpdate calls. func (b *FallbackLimiter) Reset() { b.failedUpdates.Store(initialFailedUpdates) } func (b *FallbackLimiter) Allow() bool { allowed := b.both().Allow() b.usage.Count(allowed) return allowed } func (b *FallbackLimiter) Wait(ctx context.Context) error { err := b.both().Wait(ctx) b.usage.Count(err == nil) return err } func (b *FallbackLimiter) Reserve() clock.Reservation { return countedReservation{ wrapped: b.both().Reserve(), usage: &b.usage, } } func (b *FallbackLimiter) Limit() rate.Limit { if b.useFallback() { return b.fallback.Limit() } return b.primary.Limit() } func (b *FallbackLimiter) FallbackLimit() rate.Limit { return b.fallback.Limit() } func (b *FallbackLimiter) both() quotas.Limiter { starting, failing := b.mode() if starting { // don't touch the primary until an update occurs, // to allow the token bucket to fill properly. return b.fallback } if failing { // keep shadowing calls, so the token buckets are similar. // this prevents allowing a full burst when recovering, which seems // reasonable as things were apparently unhealthy. return NewShadowedLimiter(b.fallback, b.primary) } return NewShadowedLimiter(b.primary, b.fallback) } ================================================ FILE: common/quotas/global/collection/internal/fallback_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package internal import ( "context" "math/rand" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "golang.org/x/sync/errgroup" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/quotas" ) func TestFallbackRegression(t *testing.T) { t.Run("primary should start with fallback limit", func(t *testing.T) { // checks for an issue in earlier versions, where a newly-enabled primary limit would start with an empty token bucket, // unfairly rejecting requests that other limiters would allow (as they are created with a full bucket at their target rate). // // this can be spotted by doing this sequence: // - create a new limiter, fallback of N per second // - this starts the "primary" limiter with limit=0, burst=0. // - Allow() a request // - the limiters here may disagree. this is fine, primary is not used yet. // - however: this advanced the primary's internal "now" to now. (this now happens only after update) // - update the limit to match the fallback // - primary now has limit==N, burst==N, but tokens=0 and now=now. // - Allow() a request // - fallback allows due to having N-1 tokens // - primary rejects due to 0 tokens // ^ this is the problem. // ^ this could also happen if calling `ratelimiter.Limit()` sets "now" in the future, though this seems unlikely. // // this uses real time because sleeping is not necessary, and it ensures // that any time-advancing calls that occur too early will lead to ~zero tokens. limit := rate.Limit(10) // enough to accept all requests rl := NewFallbackLimiter(clock.NewRatelimiter(limit, int(limit))) // sanity check: this may be fine to change, but it will mean the test needs to be rewritten because the token bucket may not be empty. orig := rl.primary assert.Zero(t, orig.Burst(), "sanity check: default primary ratelimiter's burst is not zero, this test likely needs a rewrite") // simulate: call while still starting up, it should not touch the primary limiter. allowed := rl.Allow() before, starting, failing := rl.Collect() assert.True(t, allowed, "first request should have been allowed, on the fallback limiter") assert.True(t, starting, "should be true == in starting mode") assert.False(t, failing, "should be false == not failing") assert.Equal(t, UsageMetrics{ Allowed: 1, Rejected: 0, Idle: 0, }, before) // update: this should set limits, and either fill the bucket or cause it to be filled on the next request rl.Update(limit) // call again: should be allowed, as this is the first time-touching request since it was created, // and the token bucket should have filled to match the first-update value. allowed = rl.Allow() after, starting, failing := rl.Collect() assert.True(t, allowed, "second request should have been allowed, on the primary limiter") assert.False(t, starting, "should be false == not in starting mode (using global)") assert.False(t, failing, "should be false == not failing") assert.Equal(t, UsageMetrics{ Allowed: 1, Rejected: 0, Idle: 0, }, after) assert.InDeltaf(t, // Tokens() advances time, so this will not be precise. rl.primary.Tokens(), int(limit)-1, 0.1, "should have ~%v tokens: %v from the initial fill, minus 1 for the allow call", int(limit)-1, int(limit), ) }) } func TestLimiter(t *testing.T) { t.Run("uses fallback initially", func(t *testing.T) { m := quotas.NewMockLimiter(gomock.NewController(t)) m.EXPECT().Allow().Times(1).Return(true) m.EXPECT().Allow().Times(2).Return(false) lim := NewFallbackLimiter(m) assert.True(t, lim.Allow(), "should return fallback's first response") assert.False(t, lim.Allow(), "should return fallback's second response") assert.False(t, lim.Allow(), "should return fallback's third response") usage, starting, failing := lim.Collect() assert.Equal(t, UsageMetrics{1, 2, 0}, usage, "usage metrics should match returned values") assert.True(t, starting, "should still be starting up") assert.False(t, failing, "should not be failing, still starting up") }) t.Run("uses primary after update", func(t *testing.T) { lim := NewFallbackLimiter(allowlimiter{}) lim.Update(1_000_000) // large enough to allow millisecond sleeps to refill time.Sleep(time.Millisecond) // allow some tokens to fill assert.True(t, lim.Allow(), "limiter allows after enough time has passed") assert.True(t, lim.Allow(), "limiter allows burst too") usage, startup, failing := lim.Collect() assert.False(t, failing, "should not use fallback limiter after update") assert.False(t, startup, "should not be starting up, has had an update") assert.Equal(t, UsageMetrics{2, 0, 0}, usage, "usage should match behavior") }) t.Run("collecting usage data resets counts", func(t *testing.T) { lim := NewFallbackLimiter(allowlimiter{}) lim.Update(1) lim.Allow() limit, _, _ := lim.Collect() assert.Equal(t, 1, limit.Allowed+limit.Rejected, "should count one request") limit, _, _ = lim.Collect() assert.Zero(t, limit.Allowed+limit.Rejected, "collect should have cleared the counts") }) t.Run("use-fallback fuse", func(t *testing.T) { // duplicate to allow this test to be external, keep in sync by hand const maxFailedUpdates = 9 t.Cleanup(func() { if t.Failed() { // notices sub-test failures t.Logf("maxFailedUpdates may be out of sync (%v), check hardcoded values", maxFailedUpdates) } }) t.Run("falls back after too many failures", func(t *testing.T) { lim := NewFallbackLimiter(allowlimiter{}) // fallback behavior is ignored lim.Update(1) _, startup, failing := lim.Collect() require.False(t, failing, "should not be using fallback") require.False(t, startup, "should not be starting up, has had an update") // the bucket will fill from time 0 on the first update, ensuring the first request is allowed require.True(t, lim.Allow(), "rate.Limiter should start with a full bucket") // fail enough times to trigger a fallback for i := 0; i < maxFailedUpdates; i++ { // build up to the edge... lim.FailedUpdate() _, _, failing = lim.Collect() require.False(t, failing, "should not be using fallback after %n failed updates", i+1) } lim.FailedUpdate() // ... and push it over _, _, failing = lim.Collect() require.True(t, failing, "%vth update should switch to fallback", maxFailedUpdates+1) assert.True(t, lim.Allow(), "should return fallback's allowed request") }) t.Run("failing many times does not accidentally switch away from startup mode", func(t *testing.T) { lim := NewFallbackLimiter(nil) for i := 0; i < maxFailedUpdates*10; i++ { lim.FailedUpdate() _, startup, failing := lim.Collect() require.True(t, startup, "should still be starting up %v failed updates", i+1) require.False(t, failing, "failing can only happen after startup finishess") } }) }) t.Run("coverage", func(t *testing.T) { // easy line to cover to bring to 100% lim := NewFallbackLimiter(nil) lim.Update(1) lim.Update(1) // should go down "no changes needed, return early" path }) } func TestLimiterNotRacy(t *testing.T) { lim := NewFallbackLimiter(allowlimiter{}) var g errgroup.Group const loops = 1000 for i := 0; i < loops; i++ { // clear ~10% of the time if rand.Intn(10) == 0 { g.Go(func() error { lim.Reset() return nil }) } // update ~10% of the time, fail the rest. // this should randomly clear occasionally via failures. if rand.Intn(10) == 0 { g.Go(func() error { lim.Update(rate.Limit(1 / rand.Float64())) // essentially never exercises "same value, do nothing" logic return nil }) } else { g.Go(func() error { lim.FailedUpdate() return nil }) } // collect occasionally if rand.Intn(10) == 0 { g.Go(func() error { lim.Collect() return nil }) } g.Go(func() error { lim.Allow() return nil }) g.Go(func() error { lim.Reserve().Used(rand.Int()%2 == 0) return nil }) g.Go(func() error { lim.Limit() return nil }) g.Go(func() error { lim.FallbackLimit() return nil }) g.Go(func() error { ctx, cancel := context.WithTimeout(context.Background(), time.Microsecond) defer cancel() _ = lim.Wait(ctx) return nil }) } } var _ quotas.Limiter = allowlimiter{} var _ clock.Reservation = allowres{} type allowlimiter struct{} type allowres struct{} func (allowlimiter) Allow() bool { return true } func (a allowlimiter) Wait(context.Context) error { return nil } func (a allowlimiter) Reserve() clock.Reservation { return allowres{} } func (a allowlimiter) Limit() rate.Limit { return rate.Inf } func (a allowres) Allow() bool { return true } func (a allowres) Used(bool) {} ================================================ FILE: common/quotas/global/collection/internal/shadowed.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package internal import ( "context" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/quotas" ) type ( shadowedLimiter struct { primary quotas.Limiter shadow quotas.Limiter } shadowedReservation struct { primary clock.Reservation shadow clock.Reservation } ) var _ quotas.Limiter = shadowedLimiter{} var _ clock.Reservation = shadowedReservation{} // NewShadowedLimiter mirrors all quotas.Limiter to its two wrapped limiters, // but only returns results from / waits as long as the "primary" one (first arg). // // This is intended for when you want to use one limit but also run a second // limit at the same time for comparison (to decide before switching) or // to warm data (to reduce spikes when switching). func NewShadowedLimiter(primary, shadow quotas.Limiter) quotas.Limiter { return shadowedLimiter{ primary: primary, shadow: shadow, } } func (s shadowedLimiter) Allow() bool { _ = s.shadow.Allow() return s.primary.Allow() } func (s shadowedLimiter) Wait(ctx context.Context) (err error) { ctx, cancel := context.WithCancel(ctx) defer cancel() go func() { _ = s.shadow.Wait(ctx) // not waited for because it should end ~immediately on its own }() return s.primary.Wait(ctx) } func (s shadowedLimiter) Reserve() clock.Reservation { return shadowedReservation{ primary: s.primary.Reserve(), shadow: s.shadow.Reserve(), } } func (s shadowedLimiter) Limit() rate.Limit { return s.primary.Limit() } func (s shadowedReservation) Allow() bool { _ = s.shadow.Allow() return s.primary.Allow() } func (s shadowedReservation) Used(wasUsed bool) { s.primary.Used(wasUsed) // Shadow reservations are more complicated than they seem. // // Mirroring the *exact* call here will not ever let the shadow allow more RPS // than the primary, because the primary will reject and not-use requests exceeding its limit. // // This means you'll see fewer rejects (because shadow-cancels are not rejects), but: // - You can't assume each reject would have been an allow because it returned // the shadow-token, allowing ~infinite not-counted-reject-RPS on shadows. // - You can't tell what the shadow's peak RPS actually is, because it will // always be limited to match the primary (or lower). // // This doesn't really tell you anything useful about the shadow's behavior. // To improve this, we need to try to differentiate between "was this // rejected directly by the primary" vs "was this rejected by something else, // and that's transitively canceling this reservation". // // That's pretty easy: we can tell by the primary's Allow state. if s.primary.Allow() { // If wasUsed: this request was allowed, so the shadow should also try to consume any token // it received (pass `true`). // // If !wasUsed: this request was rejected by something else, e.g. a second-stage limiter. // this second-stage limiter would also have rejected the request if shadow was primary, // so try to return the shadow's token (pass `false`). // This will cap the shadow's allow-RPS to match the later-stage limiter, but that would // still be true if the shadow limiter became the primary, so it's accurate behavior. // // Tn other words: just forward `wasUsed` to shadow. s.shadow.Used(wasUsed) } else { // No transitive rejection is possible (we do not currently try to get two limiters separately before checking, // so this must be due to the primary rejecting the request), so we must assume that any token available would // have been used. // // This will be misleading about the behavior of later-stage limiters (shadow may allow more RPS than if it was // primary because a later-stage limiter could lead to it canceling reservations, effectively capping its RPS // below its configured RPS), but there isn't much we can do to know about that here. // Metrics on that second-stage limiter should show why a multi-stage operation is being rejected, and we could // start reporting "allowed but canceled transitively" metrics to show when these transitive limits are affecting // a limiter, and that's about as good as we can get. // // But regardless, for this shadowed reservation: always try to consume the token. // - This allows shadow-RPS to go above primary-RPS, which is not possible if `false` was forwarded here. // - Shadow-RPS can still go below primary-RPS because it can reject additional requests. // it'll essentially just be "primary-rejects + difference-in-allows" which isn't all that useful, but it's relatively accurate. s.shadow.Used(true) } } ================================================ FILE: common/quotas/global/collection/internal/shadowed_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package internal import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/quotas" ) func TestShadowed(t *testing.T) { t.Run("allow", func(t *testing.T) { tests := map[string]struct { primary bool shadow bool allowed bool pUsage UsageMetrics sUsage UsageMetrics }{ "only primary allows": { primary: true, shadow: false, allowed: true, pUsage: UsageMetrics{Allowed: 1}, sUsage: UsageMetrics{Rejected: 1}, }, "only shadow allows": { primary: false, shadow: true, allowed: false, pUsage: UsageMetrics{Rejected: 1}, sUsage: UsageMetrics{Allowed: 1}, }, "both allow": { primary: true, shadow: true, allowed: true, pUsage: UsageMetrics{Allowed: 1}, sUsage: UsageMetrics{Allowed: 1}, }, "both reject": { primary: false, shadow: false, allowed: false, pUsage: UsageMetrics{Rejected: 1}, sUsage: UsageMetrics{Rejected: 1}, }, } for name, test := range tests { test := test t.Run("allow-"+name, func(t *testing.T) { ts := clock.NewMockedTimeSource() primaryBurst, shadowBurst := 0, 0 if test.primary { primaryBurst = 1 } if test.shadow { shadowBurst = 1 } primary := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, rate.Every(time.Second), primaryBurst)) shadow := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, rate.Every(time.Second), shadowBurst)) s := NewShadowedLimiter(primary, shadow) assert.Equalf(t, test.allowed, s.Allow(), "should match primary behavior: %v", test.allowed) assert.Equal(t, test.pUsage, primary.Collect(), "should have called primary") assert.Equal(t, test.sUsage, shadow.Collect(), "should have called shadow") }) t.Run("reserve-"+name, func(t *testing.T) { ts := clock.NewMockedTimeSource() primaryBurst, shadowBurst := 0, 0 if test.primary { primaryBurst = 1 } if test.shadow { shadowBurst = 1 } primary := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, rate.Every(time.Second), primaryBurst)) shadow := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, rate.Every(time.Second), shadowBurst)) s := NewShadowedLimiter(primary, shadow) res := s.Reserve() assert.Equalf(t, test.allowed, res.Allow(), "should match primary behavior: %v", test.allowed) res.Used(true) assert.Equal(t, test.pUsage, primary.Collect(), "should have called primary") assert.Equal(t, test.sUsage, shadow.Collect(), "should have called shadow") }) t.Run("wait-"+name, func(t *testing.T) { // the Wait operation is inherently racy in how it calls both limiters, // so this test delays the primary's response a bit to stabilize behavior. ctrl := gomock.NewController(t) primary := quotas.NewMockLimiter(ctrl) primary.EXPECT().Wait(gomock.Any()).DoAndReturn(func(ctx context.Context) error { select { case <-ctx.Done(): // canceled return ctx.Err() case <-time.After(10 * time.Millisecond): // give the shadow some time to run if test.primary { return nil } return context.Canceled } }) cPrimary := NewCountedLimiter(primary) shadow := quotas.NewMockLimiter(ctrl) shadow.EXPECT().Wait(gomock.Any()).DoAndReturn(func(ctx context.Context) error { // shadow returns immediately to keep the test's inherent race // more stable. if test.shadow { return nil } return context.Canceled }) cShadow := NewCountedLimiter(shadow) s := NewShadowedLimiter(cPrimary, cShadow) requireQuickly(t, 100*time.Millisecond, func() { ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(50 * time.Millisecond) cancel() }() err := s.Wait(ctx) if test.allowed { assert.NoError(t, err, "should have waited until allowed") } else { assert.Error(t, err, "should have been canceled") } }) assert.Equal(t, test.pUsage, cPrimary.Collect(), "should have called primary") assert.Equal(t, test.sUsage, cShadow.Collect(), "should have called shadow") }) } }) t.Run("reserve", func(t *testing.T) { t.Run("only returns primary behavior", func(t *testing.T) { t.Run("allowed", func(t *testing.T) { ts := clock.NewMockedTimeSource() primary := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, 1, 1)) // allows an event shadow := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, 0, 0)) // always rejects s := NewShadowedLimiter(primary, shadow) res := s.Reserve() res.Used(res.Allow()) assert.True(t, res.Allow(), "should match primary behavior") assert.Equal(t, UsageMetrics{1, 0, 0}, primary.Collect(), "should have called primary") assert.Equal(t, UsageMetrics{0, 1, 0}, shadow.Collect(), "should have called shadow") }) t.Run("rejected", func(t *testing.T) { ts := clock.NewMockedTimeSource() primary := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, 0, 0)) // always rejects shadow := NewCountedLimiter(clock.NewRateLimiterWithTimeSource(ts, 1, 1)) // allow an event s := NewShadowedLimiter(primary, shadow) res := s.Reserve() res.Used(res.Allow()) assert.False(t, res.Allow(), "should match primary behavior") assert.Equal(t, UsageMetrics{0, 1, 0}, primary.Collect(), "should have called primary") // this also checks "shadow can allow higher than primary". // this was not true with the original too-simple implementation. assert.Equal(t, UsageMetrics{1, 0, 0}, shadow.Collect(), "should have called shadow, and shadow should have allowed despite primary rejecting") }) }) }) t.Run("limit", func(t *testing.T) { l := NewShadowedLimiter(&allowlimiter{}, clock.NewRatelimiter(rate.Limit(0), 1)) assert.Equal(t, rate.Inf, l.Limit(), "should return the primary limit, not shadowed") }) } ================================================ FILE: common/quotas/global/collection/testdata/fuzz/FuzzBoostRPS/1b0805e3169f0ae7 ================================================ go test fuzz v1 float64(-95) float64(-158) float64(-52.111111111111114) float64(0) ================================================ FILE: common/quotas/global/collection/testdata/fuzz/FuzzBoostRPS/216cd14a71215fe2 ================================================ go test fuzz v1 float64(-95) float64(0) float64(0) float64(0) ================================================ FILE: common/quotas/global/doc.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. /* Package global contains a global-load-balance-aware ratelimiter (when complete). # High level overview At a very high level, this will: - collect usage metrics in-memory in "limiting" hosts (which use an in-memory ratelimiter) - asynchronously submit these usage metrics to "aggregating" hosts, which will return per-key RPS data - "limiting" hosts use this returned value to update their in-memory ratelimiters "Aggregating" hosts will internally: - receive data for key X from all "limiting" hosts - compute request-weight per host+key in the cluster - return the "limiting"-host's weighted portion of each ratelimit to allow The exact logic that the "aggregating" hosts use is intentionally hidden from the "limiting" hosts, so it can be changed without changing how they enforce limits. The weight-calculation algorithm will (eventually) be controlled by a dynamicconfig value, to allow shadowing and experimenting with different algorithms at runtime, or disabling the global logic altogether. See sub-packages for implementation details. # Data flow in the system The overall planned system's data-flow is driven entirely by limiting hosts repeatedly calling an "update(request, data)" API on aggregating hosts, and using the return value. Aggregating hosts make no outbound requests at all: lim: a host limiting e.g. user RPS for domain ASDF agg: a host aggregating this data, to compute fair RPS allowances ┌───┐ ┌────┐┌────┐┌────┐ ┌────┐ │lim│ │ring││agg1││agg2│ │lim2│ └─┬─┘ └─┬──┘└─┬──┘└─┬──┘ └─┬──┘ │ │ │ │ │ │ shard(a,b,c) │ │ │ update(a1,d37) │ │────────────────>┌┴┐ ┌┴┐<───────────────────────│ │ [[a], [b,c]] │ │ └┬┘───────────────────────>│ │<────────────────└┬┘ │ │ ... │ │ │ │ │ update(a1) │ │ │ got 1 request on "a" limit │──────────────────────>┌┴┐ │ │ │ │ │ │ │ update(b1,c2) │ │ │ │ 1 req on "b", 2 on "c" │────────────────────────────>┌┴┐ │ │ │ │ │ │ │ [b4/s, c9/s] │ │ │ │ │ allow "b" 4 RPS, "c" gets 9 │<────────────────────────────└┬┘ │ │ │ │ │ │ [a2/s] │ │ │ │ 2 RPS for "a" │<──────────────────────└┬┘ │ │ │ │ │ ┌─┴─┐ ┌─┴──┐┌─┴──┐┌─┴──┐ │lim│ │ring││agg1││agg2│ └───┘ └────┘└────┘└────┘ This package as a whole is only concerned with the high level request pattern in use: - limiters enforce limits and collect data - limiters asynchronously submit that data to all relevant aggregators, sharded by key - aggregators decide per-limiter-and-key limits based on global data, and return it to limiters Currently: - limiters are Frontends, but can trivially be any Cadence service as they only send outbound requests - aggregators are arbitrarily in History service because it already has a ring implemented (Frontend does not) - neither of these are fundamentally required, and they may change or be in multiple services later The actual contents of the requests and responses and how limits are decided is intentionally flexible to allow creating custom algorithms / data / etc externally without requiring internal RPC spec definitions. To achieve this, we have a custom [github.com/uber/cadence/common/types.Any] type that can be used with either Thrift or Protobuf. This type is intentionally not google.protobuf.Any because there is no reason to require exchanging Protobuf data (particularly if using Thrift), and because that tends to bind to a specific code generator, but it serves essentially the same purpose. A built-in version of all this will be coming in later commits, and possibly more in the future if we end up needing major changes that are reusable. # Planned use The first use of this is intentionally focused on addressing imbalanced usage of per-domain user/worker/ES ratelimits across different frontend hosts, e.g. due to unfair load balancing or per-datacenter affinity or other occasionally-changing-else-mostly-stable routing decisions. This will essentially imply 3 keys per domain, and will look something like `domain-user-request` / `domain-worker-request` / etc - they just have to be unique per conceptual thing-with-a-configured-limit. These frontend limits are generally configured up-front and are used to protect the cluster (especially the database) from noisy neighbors and un-planned major usage changes. True precision is not necessary (accepting 110 instead of 100 will not collapse the cluster, but allowing *unlimited* could), but users plan around the numbers we have given them, and significant or sustained differences in reality can cause problems for them. Particularly if it significantly decreases due to a change in how our network decides to distribute their requests. By detecting this imbalanced load we can fairly allow "hot" hosts more RPS than our "limit / N hosts" limiters allow (essentially stealing unused quota from low-usage hosts), and ultimately allow request rates closer to the intended limits as the balance changes over time. It's better for our users (they get what we told them they get), and better for us (no need to artificially raise the limit to work around the load imbalance, nor keep changing it as load changes). Weight-data loss is expected due to frontend or agg process losses, and allowed-limits naturally trail real-time use due to the asynchronous nature - this is not *ideal* but is expected to be acceptable, as it will adjust / self-repair "soon" as normal updates occur. More concretely, our planned target is something like: - frontends (the limiting hosts) submit usage data every 3s - history's aggregating algorithms "quickly" account for new load, and return allowed limits to frontends - within about 10s of a change, we want things to be ~90% accurate or better compared to the "ground truth" (this implies ~0.5 weight per 3s update) - with stable traffic, >99% accuracy at allowing the intended RPS of a limit key, regardless of how requests are distributed - briefly going over or under the intended limit during changes or partial data is allowed, but generally requests should be incorrectly rejected over incorrectly allowed Future uses (with minor changes) may include things like: - limiting cluster-wide database RPS across all hosts (one key, 50k+ RPS) despite significantly-different load depending on server role and shard distribution - different load-deciding algorithms, e.g. TCP Vegas or some other more sophisticated weight-deciding algorithm (this first one is intentionally simple, and might prove good enough) - closed-source pluggable algorithms that share the same intent and request-flow but little else (i.e. extendable by operators without new RPC definitions. routing and usage patterns are often unique, no one approach can be ideal everywhere) # Out of scope Uses explicitly out of scope for this whole structure includes things like: - perfect accuracy on RPS targets (requires synchronous decisions) - rapid and significant usage changes, like a domain sending all requests to one frontend host one second, then a different the next, etc. - brief spikes (e.g. sub-10-seconds. these are broadly undesirable and can behave worse than RPS/hosts would allow) - guaranteed limiting below intended RPS (partial data may lead to split-brain-like behavior, leading to over-allowing) - fully automated target-RPS adjusting (e.g. system-load detecting, not pre-configured RPS. this may be *possible* to implement in this system, but it is not *planned* and may require significant changes) */ package global ================================================ FILE: common/quotas/global/rpc/client.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Package rpc contains a concurrent RPC client, and handles mapping to/from the // Any-typed request details so other packages do not have to concern themselves // with those details. package rpc //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination client_mock.go -self_package github.com/uber/cadence/common/quotas/global/rpc Client import ( "context" "errors" "fmt" "math" "sync" "time" "github.com/google/uuid" "golang.org/x/sync/errgroup" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas/global/shared" "github.com/uber/cadence/common/types" ) type ( Calls struct { Allowed int Rejected int } ) type ( // UpdateResult holds all successes and errors encountered, to be distinct // from fatal errors that imply unusable results (i.e. a normal error return). UpdateResult struct { // all successfully-returned weights. // this may be populated and is safe to use even if Errors is present, // as some shards may have succeeded. Weights map[shared.GlobalKey]UpdateEntry // Any unexpected errors encountered, or nil if none. // Weights is valid even if this is present, though it may be empty. // // This should always be nil aside from major programming or rollout // errors, e.g. failures to serialize or deserialize data (coding errors // or incompatible server versions). Err error } UpdateEntry struct { Weight float64 UsedRPS float64 } Client interface { // Update performs concurrent calls to all aggregating peers to send load info // and retrieve new per-key weights from the rest of the cluster. // This is intended to be called periodically in the background per process, // not synchronously or concurrently (per set of keys anyway), as it is fairly // high cost to shard keys and send so many requests. // // Currently, there are no truly fatal errors so this API does not return `error`. // Even in the presence of errors, some successful data may have been loaded, // and that will be part of the UpdateResult struct. // // As part of the contract of this call, UpdateResult will not ever contain // NaN, Inf, or negative values, as these imply fatal calculation problems. // It checks internally and will return an error rather than any value. Update(ctx context.Context, period time.Duration, load map[shared.GlobalKey]Calls) UpdateResult } client struct { history history.Client resolver history.PeerResolver thisHost string logger log.Logger scope metrics.Scope } ) var _ Client = (*client)(nil) func New( historyClient history.Client, resolver history.PeerResolver, logger log.Logger, met metrics.Client, ) Client { return &client{ history: historyClient, resolver: resolver, thisHost: uuid.NewString(), // TODO: would descriptive be better? but it works, unique ensures correctness. logger: logger, scope: met.Scope(metrics.GlobalRatelimiter), } } func (c *client) Update(ctx context.Context, period time.Duration, load map[shared.GlobalKey]Calls) UpdateResult { keys := make([]string, 0, len(load)) for k := range load { keys = append(keys, string(k)) } peers, err := c.resolver.GlobalRatelimitPeers(keys) if err != nil { // should only happen if peers are unavailable, individual requests are handled other ways return UpdateResult{ Err: fmt.Errorf("unable to shard ratelimit update data: %w", err), } } var mut sync.Mutex weights := make(map[shared.GlobalKey]UpdateEntry, len(load)) // should get back most or all keys requested var g errgroup.Group // could limit max concurrency easily with `g.SetLimit(n)` if desired, // but as each goes to a different host this seems fine to just blast out all at once, // and it makes timeouts easy because we don't need to reserve room for queued calls. for peer, peerKeys := range peers { peer, peerKeys := peer, peerKeys // for closure g.Go(func() (err error) { defer func() { log.CapturePanic(recover(), c.logger, &err) }() result, err := c.updateSinglePeer(ctx, peer, period, filterKeys(peerKeys, load)) if err != nil { return err // does not stop other calls, nor should it } mut.Lock() defer mut.Unlock() for k, v := range result { if _, ok := weights[k]; ok { // should not happen as resolver.GlobalRatelimiterPeers ensures disjoint keys // are requested, and only the requested keys are returned. return fmt.Errorf("received duplicate key %q from peer %q", k, peer) } weights[k] = v } return nil }) } err = g.Wait() if err != nil { // wrap it so it's relatively severe-looking, as these should not happen err = fmt.Errorf("potentially fatal error during ratelimit update requests: %w", err) } return UpdateResult{ Weights: weights, Err: err, } } func (c *client) updateSinglePeer(ctx context.Context, peer history.Peer, period time.Duration, load map[shared.GlobalKey]Calls) (map[shared.GlobalKey]UpdateEntry, error) { anyValue, err := updateToAny(c.thisHost, period, load) if err != nil { // serialization errors should never happen return nil, &SerializationError{err} } result, err := c.history.RatelimitUpdate( ctx, &types.RatelimitUpdateRequest{ Any: anyValue, }, peer.ToYarpcShardKey(), ) if err != nil { // client metrics are fine for monitoring, but it does not log errors. // TODO: possibly filter out or aggregate "peer lost" logs? they're expected during deploys, since calls are not implicitly retried. c.logger.Warn( "request failure when updating ratelimits", tag.Error(&RPCError{err}), tag.GlobalRatelimiterPeer(string(peer)), ) return nil, nil // rpc errors are essentially expected, and not an "error" } resp, err := anyToWeights(result.Any) if err != nil { // deserialization errors should never happen return nil, &SerializationError{err} } // and check the values. // - weights must be 0..1 inclusive // - used RPS must be non-negative // - NaNs and Infs are always rejected // // any failure is fatal to the whole request, that host cannot be trusted, // and any affected limiters should switch to their fallbacks if the issue // persists long enough. for key, entry := range resp { if msg := shared.SanityCheckFloat(0, entry.Weight, 1, "weight"); msg != "" { return nil, fmt.Errorf("bad value for key %q: from host: %q: %w", key, peer, errors.New(msg)) } // no upper bound, but true infs are not allowed if msg := shared.SanityCheckFloat(0, entry.UsedRPS, math.Inf(1), "used rps"); msg != "" { return nil, fmt.Errorf("bad value for key %q: from host: %q %w", key, peer, errors.New(msg)) } } return resp, nil } func filterKeys(keys []string, load map[shared.GlobalKey]Calls) map[shared.GlobalKey]Calls { result := make(map[shared.GlobalKey]Calls, len(keys)) for _, k := range keys { result[shared.GlobalKey(k)] = load[shared.GlobalKey(k)] } return result } ================================================ FILE: common/quotas/global/rpc/client_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: client.go // // Generated by this command: // // mockgen -package rpc -source client.go -destination client_mock.go -self_package github.com/uber/cadence/common/quotas/global/rpc Client // // Package rpc is a generated GoMock package. package rpc import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" shared "github.com/uber/cadence/common/quotas/global/shared" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // Update mocks base method. func (m *MockClient) Update(ctx context.Context, period time.Duration, load map[shared.GlobalKey]Calls) UpdateResult { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Update", ctx, period, load) ret0, _ := ret[0].(UpdateResult) return ret0 } // Update indicates an expected call of Update. func (mr *MockClientMockRecorder) Update(ctx, period, load any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockClient)(nil).Update), ctx, period, load) } ================================================ FILE: common/quotas/global/rpc/client_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package rpc import ( "context" "errors" "fmt" "math" "reflect" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "golang.org/x/exp/maps" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas/global/algorithm" "github.com/uber/cadence/common/quotas/global/shared" "github.com/uber/cadence/common/types" ) func TestClient(t *testing.T) { setup := func(t *testing.T) (c *client, hc *history.MockClient, pr *history.MockPeerResolver) { ctrl := gomock.NewController(t) hc = history.NewMockClient(ctrl) pr = history.NewMockPeerResolver(ctrl) impl := New(hc, pr, testlogger.New(t), metrics.NewNoopMetricsClient()) return impl.(*client), hc, pr } encode := func(t *testing.T, w map[algorithm.Limit]algorithm.HostUsage) (*types.RatelimitUpdateResponse, error) { a, err := AggregatorWeightsToAny(w) assert.NoError(t, err, "should be impossible: could not encode aggregator response to any") return &types.RatelimitUpdateResponse{ Any: a, }, nil } t.Run("valid request", func(t *testing.T) { c, hc, pr := setup(t) data := map[shared.GlobalKey]Calls{ "a": {10, 20}, "b": {3, 5}, "c": {78, 9}, } // make a realistic used-RPS value (assuming 1 second elapsed) used := float64(0) for _, calls := range data { used += float64(calls.Allowed + calls.Rejected) } response := map[shared.GlobalKey]UpdateEntry{ "a": {Weight: 0.1, UsedRPS: used}, "b": {Weight: 0.2, UsedRPS: used}, "c": {Weight: 0.3, UsedRPS: used}, } stringKeys := make([]string, 0, len(data)) for k := range data { stringKeys = append(stringKeys, string(k)) } pr.EXPECT().GlobalRatelimitPeers(gomock.InAnyOrder(stringKeys)).Return(map[history.Peer][]string{ "agg-1": {"a", "c"}, "agg-2": {"b"}, }, nil) hc.EXPECT(). RatelimitUpdate(gomock.Any(), matchrequest{t, []string{"a", "c"}}, matchyarpc{t, yarpc.WithShardKey("agg-1")}). Return(encode(t, map[algorithm.Limit]algorithm.HostUsage{"a": {Weight: 0.1, Used: algorithm.PerSecond(used)}, "c": {Weight: 0.3, Used: algorithm.PerSecond(used)}})) hc.EXPECT(). RatelimitUpdate(gomock.Any(), matchrequest{t, []string{"b"}}, matchyarpc{t, yarpc.WithShardKey("agg-2")}). Return(encode(t, map[algorithm.Limit]algorithm.HostUsage{"b": {Weight: 0.2, Used: algorithm.PerSecond(used)}})) result := c.Update(context.Background(), time.Second, data) assert.NoError(t, result.Err) assert.Equal(t, response, result.Weights) }) t.Run("bad peers", func(t *testing.T) { c, _, pr := setup(t) err := errors.New("peer issue") pr.EXPECT().GlobalRatelimitPeers(gomock.Any()).Return(nil, err) res := c.Update(context.Background(), time.Second, nil) assert.ErrorContains(t, res.Err, "unable to shard") assert.ErrorIs(t, res.Err, err) // should contain the cause }) t.Run("bad math", func(t *testing.T) { // the client should validate responses to make sure they do not contain // known-to-be-invalid data. c, hc, pr := setup(t) pr.EXPECT().GlobalRatelimitPeers([]string{"a"}). Return(map[history.Peer][]string{"agg-1": {"a"}}, nil) hc.EXPECT().RatelimitUpdate(gomock.Any(), gomock.Any(), gomock.Any()). Return(encode(t, map[algorithm.Limit]algorithm.HostUsage{"a": { Weight: algorithm.HostWeight(math.NaN()), // invalid value from agg, detected after deserializing Used: 1, }})) result := c.Update(context.Background(), time.Second, map[shared.GlobalKey]Calls{"a": {10, 20}}) assert.Empty(t, result.Weights, "bad data should lead to no usable response") assert.ErrorContains(t, result.Err, `bad value for key "a"`, "failure should mention the key") assert.ErrorContains(t, result.Err, "is NaN", "failure should mention the bad value type") assert.ErrorContains(t, result.Err, "potentially fatal error during ratelimit update requests", "failure should look alarming") }) } func stringy[T ~string](in []T) []string { out := make([]string, len(in)) for i := range in { out[i] = string(in[i]) } return out } // matches a single yarpc option type matchyarpc struct { t *testing.T opt yarpc.CallOption } var _ gomock.Matcher = matchyarpc{} func (m matchyarpc) Matches(x interface{}) bool { opts, ok := x.([]yarpc.CallOption) if !ok { m.t.Logf("not a []yarpc.CallOption: %T", x) return false } m.t.Logf("opts: %#v, should be: %#v", opts, m.opt) return len(opts) == 1 && reflect.DeepEqual(opts[0], m.opt) } func (m matchyarpc) String() string { return fmt.Sprintf("%#v", m.opt) } // matches load-keys in an update request type matchrequest struct { t *testing.T loadKeys []string } var _ gomock.Matcher = matchrequest{} func (a matchrequest) Matches(x interface{}) bool { v, ok := x.(*types.RatelimitUpdateRequest) if !ok { a.t.Logf("not a *types.RatelimitUpdateRequest: %T", x) return false } up, err := AnyToAggregatorUpdate(v.Any) if err != nil { a.t.Logf("failed to decode to agg update: %v", err) return false } keys := map[string]struct{}{} for _, k := range a.loadKeys { keys[k] = struct{}{} } gotKeys := map[string]struct{}{} for _, k := range stringy(maps.Keys(up.Load)) { gotKeys[k] = struct{}{} } a.t.Logf("want keys: %v, got keys: %v", maps.Keys(keys), maps.Keys(gotKeys)) return reflect.DeepEqual(keys, gotKeys) } func (a matchrequest) String() string { return fmt.Sprintf("request with load keys: %v", a.loadKeys) } ================================================ FILE: common/quotas/global/rpc/error.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package rpc // RPCError wraps errors to mark them as coming from RPC, i.e. basically expected. type RPCError struct { cause error } // SerializationError wraps errors to mark them as coming from (de)serialization. // // In practice this should not ever occur, as it should imply one of: // - incorrect Any Value/ValueType pairing, which should not pass tests // - non-backwards-compatible type changes deployed in an unsafe way // - incompatible Thrift-binary changes type SerializationError struct { cause error } type errwrapper interface { error Unwrap() error } var _ errwrapper = (*RPCError)(nil) var _ errwrapper = (*SerializationError)(nil) func (e *RPCError) Error() string { return e.cause.Error() } func (e *RPCError) Unwrap() error { return e.cause } func (e *SerializationError) Error() string { return e.cause.Error() } func (e *SerializationError) Unwrap() error { return e.cause } ================================================ FILE: common/quotas/global/rpc/mapping.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package rpc import ( "fmt" "math" "testing" "time" "github.com/uber/cadence/.gen/go/history" "github.com/uber/cadence/common/quotas/global/algorithm" "github.com/uber/cadence/common/quotas/global/shared" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" ) // funcs are ordered to match how an Update request flows: // limiter -> Update on history -> aggregator, and return. // // 1. updateToAny: limiter-load is gathered and converted to an Any // 2. AnyToAggregatorUpdate: the Any is received by a history host and decoded for an aggregator // 3. AggregatorWeightsToAny: the aggregator responds, and is converted to another Any // 4. anyToWeights: the response is decoded to get the weights to use // // to make the rpc API generic for other kinds of data / multiple algorithms, just inject // these mappers so the request sharding can encode data per peer as needed. // e.g. use a mapper with a `peer_callback(keys for peer) Any` so it does not need to // know what kind of data is being converted. func updateToAny(host string, elapsed time.Duration, load map[shared.GlobalKey]Calls) (*types.Any, error) { calls := make(map[string]*history.WeightedRatelimitCalls, len(load)) for k, v := range load { calls[string(k)] = &history.WeightedRatelimitCalls{ Allowed: saturatingInt32(v.Allowed), Rejected: saturatingInt32(v.Rejected), } } req := &history.WeightedRatelimitUsage{ Caller: host, ElapsedMS: saturatingInt32(elapsed / time.Millisecond), Calls: calls, } bytes, err := thrift.EncodeToBytes(req) if err != nil { // should be impossible return nil, &SerializationError{err} } return &types.Any{ ValueType: history.WeightedRatelimitUsageAnyType, Value: bytes, }, nil } // AnyToAggregatorUpdate converts an in-bound ratelimiter's Any-typed data to the // structure that [algorithm.RequestWeighted.Update] expects. func AnyToAggregatorUpdate(request *types.Any) (algorithm.UpdateParams, error) { if request.ValueType != history.WeightedRatelimitUsageAnyType { return algorithm.UpdateParams{}, fmt.Errorf("unrecognized Any type: %q", request.ValueType) } var out history.WeightedRatelimitUsage err := thrift.DecodeStructFromBytes(request.Value, &out) if err != nil { return algorithm.UpdateParams{}, &SerializationError{err} } load := make(map[algorithm.Limit]algorithm.Requests, len(out.Calls)) for k, v := range out.Calls { load[algorithm.Limit(k)] = algorithm.Requests{ Accepted: int(v.Allowed), Rejected: int(v.Rejected), } } par := algorithm.UpdateParams{ ID: algorithm.Identity(out.Caller), Elapsed: time.Duration(out.ElapsedMS) * time.Millisecond, Load: load, } return par, par.Validate() } // AggregatorWeightsToAny converts the [algorithm.RequestWeighted.HostWeights] response // (for an in-bound Update request) to an Any-type compatible with RPC. func AggregatorWeightsToAny(response map[algorithm.Limit]algorithm.HostUsage) (*types.Any, error) { quotas := make(map[string]*history.WeightedRatelimitUsageQuotaEntry, len(response)) for k, v := range response { quotas[string(k)] = &history.WeightedRatelimitUsageQuotaEntry{ Weight: float64(v.Weight), Used: float64(v.Used), } } wrapper := &history.WeightedRatelimitUsageQuotas{ Quotas: quotas, } data, err := thrift.EncodeToBytes(wrapper) if err != nil { // should be impossible return nil, &SerializationError{err} } return &types.Any{ ValueType: history.WeightedRatelimitUsageQuotasAnyType, Value: data, }, nil } func anyToWeights(response *types.Any) (map[shared.GlobalKey]UpdateEntry, error) { if response.ValueType != history.WeightedRatelimitUsageQuotasAnyType { return nil, fmt.Errorf("unrecognized Any type: %q", response.ValueType) } var out history.WeightedRatelimitUsageQuotas err := thrift.DecodeStructFromBytes(response.Value, &out) if err != nil { return nil, &SerializationError{err} } result := make(map[shared.GlobalKey]UpdateEntry, len(out.Quotas)) for k, v := range out.Quotas { result[shared.GlobalKey(k)] = UpdateEntry{ Weight: v.Weight, UsedRPS: v.Used, } } return result, nil } type numeric interface { int | time.Duration } func saturatingInt32[T numeric](i T) int32 { if i > math.MaxInt32 { return math.MaxInt32 } return int32(i) } // exposed only for testing purposes // TestUpdateToAny is exposed for handler tests, use updateToAny in internal code instead. func TestUpdateToAny(t *testing.T, host string, elapsed time.Duration, load map[shared.GlobalKey]Calls) (*types.Any, error) { t.Helper() return updateToAny(host, elapsed, load) } // TestAnyToWeights is exposed for handler tests, use anyToWeights in internal code instead func TestAnyToWeights(t *testing.T, response *types.Any) (map[shared.GlobalKey]UpdateEntry, error) { t.Helper() return anyToWeights(response) } ================================================ FILE: common/quotas/global/rpc/mapping_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package rpc import ( "testing" "time" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/quotas/global/algorithm" "github.com/uber/cadence/common/quotas/global/shared" "github.com/uber/cadence/common/types" ) func TestMapping(t *testing.T) { t.Run("request", func(t *testing.T) { host := uuid.NewString() elapsed := 3 * time.Second inLoad := map[shared.GlobalKey]Calls{ "domain-x-user": {1, 2}, "domain-y-worker": {3, 4}, } // same contents but different types outLoad := map[algorithm.Limit]algorithm.Requests{ "domain-x-user": {1, 2}, "domain-y-worker": {3, 4}, } intermediate, err := updateToAny(host, elapsed, inLoad) require.NoError(t, err) params, err := AnyToAggregatorUpdate(intermediate) require.NoError(t, err) assert.EqualValues(t, host, params.ID) assert.Equal(t, elapsed, params.Elapsed) assert.Equal(t, outLoad, params.Load) }) t.Run("response", func(t *testing.T) { inResponse := map[algorithm.Limit]algorithm.HostUsage{ "domain-x-user": {Weight: 0.5, Used: 10}, "domain-y-worker": {Weight: 0.3, Used: 20}, } // same contents but different types outResponse := map[shared.GlobalKey]UpdateEntry{ "domain-x-user": {Weight: 0.5, UsedRPS: 10}, "domain-y-worker": {Weight: 0.3, UsedRPS: 20}, } intermediate, err := AggregatorWeightsToAny(inResponse) require.NoError(t, err) weights, err := anyToWeights(intermediate) require.NoError(t, err) assert.Equal(t, outResponse, weights) }) t.Run("bad data", func(t *testing.T) { validUpdate, err := updateToAny(uuid.NewString(), time.Second, map[shared.GlobalKey]Calls{"domain-x-user": {1, 2}}) require.NoError(t, err) validResponse, err := AggregatorWeightsToAny(map[algorithm.Limit]algorithm.HostUsage{"domain-x-user": {Weight: 0.7}}) require.NoError(t, err) _, err = AnyToAggregatorUpdate(validUpdate) require.NoError(t, err, "sanity check failed, starting data should be valid") _, err = anyToWeights(validResponse) require.NoError(t, err, "sanity check failed, starting data should be valid") withType := func(valueType string, in *types.Any) *types.Any { dup := *in dup.ValueType = valueType return &dup } withData := func(data []byte, in *types.Any) *types.Any { dup := *in dup.Value = data return &dup } toUpdate := func(data *types.Any) error { _, err := AnyToAggregatorUpdate(data) return err } toWeights := func(data *types.Any) error { _, err := anyToWeights(data) return err } tests := map[string]struct { value *types.Any errContains string do func(data *types.Any) error }{ "wrong value type update": { value: withType("other", validUpdate), do: toUpdate, errContains: "other", // should reference the wrong value }, "wrong value type weights": { value: withType("other", validResponse), do: toWeights, errContains: "other", // should reference the wrong value }, "bad data update": { value: withData([]byte("bad"), validUpdate), do: toUpdate, errContains: "could not decode", }, "bad data weights": { value: withData([]byte("bad"), validResponse), do: toWeights, errContains: "could not decode", }, "empty data update": { value: withData(nil, validUpdate), do: toUpdate, errContains: "could not decode", }, "empty data weights": { value: withData(nil, validResponse), do: toWeights, errContains: "could not decode", }, "swapped types update": { value: validUpdate, do: toWeights, errContains: "unrecognized Any type", }, "swapped types weights": { value: validResponse, do: toUpdate, errContains: "unrecognized Any type", }, } for name, test := range tests { test := test t.Run(name, func(t *testing.T) { require.ErrorContains(t, test.do(test.value), test.errContains) }) } }) } ================================================ FILE: common/quotas/global/shared/keymapper.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Package shared holds some types that are used between multiple global-ratelimiter // packages, and need to be broken out to prevent import cycles. package shared import ( "fmt" "strings" ) type ( // KeyMapper is used to ensure that all keys that get communicated to // aggregators are uniquely identifiable, when multiple collections are used. // // A unique prefix / pattern per collection should be sufficient. KeyMapper interface { LocalToGlobal(key LocalKey) GlobalKey // GlobalToLocal does the reverse of LocalToGlobal. // // An error will be returned if the global key does not seem to have // come from this mapper. GlobalToLocal(key GlobalKey) (LocalKey, error) } LocalKey string // LocalKey represents a "local" / unique to this collection ratelimit key GlobalKey string // GlobalKey represents a "global" / globally unique ratelimit key (i.e. namespaced by collection) simplekm struct { prefix string } ) var _ KeyMapper = simplekm{} func (s simplekm) LocalToGlobal(key LocalKey) GlobalKey { return GlobalKey(s.prefix + string(key)) } func (s simplekm) GlobalToLocal(key GlobalKey) (LocalKey, error) { k := string(key) if !strings.HasPrefix(k, s.prefix) { return "", fmt.Errorf("missing prefix %q in global key: %q", s.prefix, key) } return LocalKey(strings.TrimPrefix(k, s.prefix)), nil } // PrefixKey builds a KeyMapper that simply adds (or removes) a fixed prefix to all keys. // If the global key does not have this prefix, an error will be returned when mapping to local. func PrefixKey(prefix string) KeyMapper { return simplekm{ prefix: prefix, } } ================================================ FILE: common/quotas/global/shared/sanity.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shared import ( "fmt" "math" "strings" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) // SanityCheckFloat checks bounds and numerically-valid-ness, and returns an error string if any check fails. // // This is the "lower level" of SanityLogFloat, when invalid means behavior needs to change, // and invalid data is an unavoidable possibility. // // If invalid data should *always* be avoided by earlier code, use SanityLogFloat instead to // do a paranoid log of violations without affecting behavior. func SanityCheckFloat[T ~float64](lower, actual, upper T, what string) (msg string) { if math.IsNaN(float64(actual)) { return what + " is NaN, should be impossible" } else if math.IsInf(float64(actual), 0) { return what + " is Inf, should be impossible" } else if actual < lower { return what + " is below lower bound, should be impossible" } else if upper < actual { return what + " is above upper bound, should be impossible" } return "" } // SanityLogFloat ensures values are within sane bounds (inclusive) and not NaN or Inf, // and logs an error if these expectations are violated. // This should only be used when a value is believed to *always* be valid, not // when it needs to be clamped to a reasonable range. // // This largely exists because [math.Max] and similar retain NaN/Inf instead of // treating them as error states, and that (and other issues) caused problems. // There are a lot of potential gotchas with floats, and without good fuzzing // you're kinda unlikely to trigger them all, so this helps us be as paranoid as // we like with small enough cost to ignore (while also documenting expectations). // // The "actual" value is returned no matter what, to make this easier to use in // a logic chain without temp vars. // // Violations are intentionally NOT fatal, nor is a "reasonable" value returned, // as they are not expected to occur and code paths involved are not built with // that assumption. Consider it "undefined behavior", this log exists only to // help troubleshoot the cause later if it happens. // // --- // // In tests, always assert that this is not ever triggered, by using the following: // // logger, obs := logtest.NewObserved(t) // t.Cleanup(func() { AssertNoSanityCheckFailures(t, obs) }) // // your test func SanityLogFloat[T ~float64](lower, actual, upper T, what string, logger log.Logger) T { msg := SanityCheckFloat(lower, actual, upper, what) if msg != "" { logger.Error(msg, tag.Dynamic("lower", lower), tag.Dynamic("actual", actual), tag.Dynamic("upper", upper), tag.Dynamic("actual_type", fmt.Sprintf("%T", actual))) } return actual } // small and easier to mock in tests, as no other methods are needed type testingt interface { Errorf(string, ...any) } func AssertNoSanityCheckFailures(t testingt, entries []observer.LoggedEntry) { logged := false for _, entry := range entries { if entry.Level.Enabled(zap.ErrorLevel) && strings.Contains(entry.Message, "impossible") { t.Errorf("sanity check log detected: %v: %v", entry.Message, entry.Context) if logged { t.Errorf("ignoring any remaining sanity check failures, comment this out to get all logs") // produces 2 logs and then gives up, as large or fuzz tests can trigger thousands and that's very slow break } logged = true } } } ================================================ FILE: common/quotas/global/shared/sanity_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shared import ( "fmt" "math" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/log/testlogger" ) func TestSanityChecks(t *testing.T) { tests := map[string]struct { lower, actual, upper float64 expected string }{ "nan": {lower: 0, actual: math.NaN(), upper: 0, expected: "is NaN"}, "pos inf": {lower: 0, actual: math.Inf(1), upper: 0, expected: "is Inf"}, "neg inf": {lower: 0, actual: math.Inf(-1), upper: 0, expected: "is Inf"}, "too low": {lower: 5, actual: 1, upper: 10, expected: "below lower bound"}, "too high": {lower: 0, actual: 10, upper: 5, expected: "above upper bound"}, "inclusive lower": {lower: 0, actual: 0, upper: 10, expected: ""}, "valid": {lower: 0, actual: 5, upper: 10, expected: ""}, "inclusive upper": {lower: 0, actual: 10, upper: 10, expected: ""}, } for name, test := range tests { name, test := name, test t.Run(name, func(t *testing.T) { msg := SanityCheckFloat(test.lower, test.actual, test.upper, "test") if test.expected == "" { assert.Empty(t, msg, "should not have failed validation") } else { assert.Containsf(t, msg, test.expected, "wrong or missing error case") } }) } t.Run("logs", func(t *testing.T) { l, obs := testlogger.NewObserved(t) _ = SanityLogFloat[float64](5, 1, 10, "test", l) logs := obs.TakeAll() require.Len(t, logs, 1, "should have made an error log") assert.Contains(t, logs[0].Message, "below lower bound", "should log the issue") assert.Contains(t, logs[0].Message, "test", "should mention the 'what'") assert.NotZero(t, logs[0].ContextMap()["actual"], "should log the actual value") }) t.Run("assert helper", func(t *testing.T) { l, obs := testlogger.NewObserved(t) _ = SanityLogFloat[float64](5, 1, 10, "test", l) logs := obs.TakeAll() require.Len(t, logs, 1, "should have made an error log") errobs := &errorfobserver{} AssertNoSanityCheckFailures(errobs, logs) require.Len(t, errobs.logs, 1, "should Errorf that there was a log") assert.Contains(t, errobs.logs[0], "sanity check log detected") assert.Contains(t, errobs.logs[0], "below lower bound", "should contain the original message") errobs = &errorfobserver{} // clear the logs tripleLog := append(logs, logs[0], logs[0]) // fake having three logs, details don't matter AssertNoSanityCheckFailures(errobs, tripleLog) require.Len(t, errobs.logs, 3, "should have two Errorfs and one giving up") assert.Contains(t, errobs.logs[0], "sanity check log detected") assert.Contains(t, errobs.logs[1], "sanity check log detected") assert.Contains(t, errobs.logs[2], "ignoring any remaining sanity check failures") }) } type errorfobserver struct { logs []string } func (o *errorfobserver) Errorf(format string, args ...interface{}) { o.logs = append(o.logs, fmt.Sprintf(format, args...)) } ================================================ FILE: common/quotas/interfaces.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package=$GOPACKAGE -destination=limiter_mock.go github.com/uber/cadence/common/quotas Limiter //go:generate mockgen -package=$GOPACKAGE -destination=policy_mock.go github.com/uber/cadence/common/quotas Policy package quotas import ( "context" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" ) // RPSFunc returns a float64 as the RPS type RPSFunc func() float64 // RPSKeyFunc returns a float64 as the RPS for the given key type RPSKeyFunc func(key string) float64 // Info corresponds to information required to determine rate limits type Info struct { Domain string } // Limiter corresponds to basic rate limiting functionality. // // TODO: This can likely be replaced with clock.Ratelimiter, now that it exists, // but it is being left as a read-only mirror for now as only these methods are // currently needed in areas that currently use this Limiter. type Limiter interface { // Allow attempts to allow a request to go through. The method returns // immediately with a true or false indicating if the request can make // progress Allow() bool // Wait waits till the deadline for a rate limit token to allow the request // to go through. Wait(ctx context.Context) error // Reserve reserves a rate limit token Reserve() clock.Reservation // Limit returns the current configured ratelimit. // // If this Limiter wraps multiple values, this is generally the "most relevant" one, // i.e. the one that is most likely to apply to the next request Limit() rate.Limit } // Policy corresponds to a quota policy. A policy allows implementing layered // and more complex rate limiting functionality. type Policy interface { // Allow attempts to allow a request to go through. The method returns // immediately with a true or false indicating if the request can make // progress Allow(info Info) bool // Wait waits up till the context deadline for a rate limit token to allow // the request to go through. Returns nil if request is allowed. Wait(ctx context.Context, info Info) error } ================================================ FILE: common/quotas/limiter_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/quotas (interfaces: Limiter) // // Generated by this command: // // mockgen -package=quotas -destination=limiter_mock.go github.com/uber/cadence/common/quotas Limiter // // Package quotas is a generated GoMock package. package quotas import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" rate "golang.org/x/time/rate" clock "github.com/uber/cadence/common/clock" ) // MockLimiter is a mock of Limiter interface. type MockLimiter struct { ctrl *gomock.Controller recorder *MockLimiterMockRecorder isgomock struct{} } // MockLimiterMockRecorder is the mock recorder for MockLimiter. type MockLimiterMockRecorder struct { mock *MockLimiter } // NewMockLimiter creates a new mock instance. func NewMockLimiter(ctrl *gomock.Controller) *MockLimiter { mock := &MockLimiter{ctrl: ctrl} mock.recorder = &MockLimiterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockLimiter) EXPECT() *MockLimiterMockRecorder { return m.recorder } // Allow mocks base method. func (m *MockLimiter) Allow() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Allow") ret0, _ := ret[0].(bool) return ret0 } // Allow indicates an expected call of Allow. func (mr *MockLimiterMockRecorder) Allow() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Allow", reflect.TypeOf((*MockLimiter)(nil).Allow)) } // Limit mocks base method. func (m *MockLimiter) Limit() rate.Limit { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Limit") ret0, _ := ret[0].(rate.Limit) return ret0 } // Limit indicates an expected call of Limit. func (mr *MockLimiterMockRecorder) Limit() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Limit", reflect.TypeOf((*MockLimiter)(nil).Limit)) } // Reserve mocks base method. func (m *MockLimiter) Reserve() clock.Reservation { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Reserve") ret0, _ := ret[0].(clock.Reservation) return ret0 } // Reserve indicates an expected call of Reserve. func (mr *MockLimiterMockRecorder) Reserve() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reserve", reflect.TypeOf((*MockLimiter)(nil).Reserve)) } // Wait mocks base method. func (m *MockLimiter) Wait(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Wait", ctx) ret0, _ := ret[0].(error) return ret0 } // Wait indicates an expected call of Wait. func (mr *MockLimiterMockRecorder) Wait(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Wait", reflect.TypeOf((*MockLimiter)(nil).Wait), ctx) } ================================================ FILE: common/quotas/limiter_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package quotas import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" ) const ( defaultRps = 2000 defaultDomain = "test" ) func TestMultiStageRateLimiterBlockedByDomainRps(t *testing.T) { t.Parallel() policy := newFixedRpsMultiStageRateLimiter(t, 2, 1) check := func(suffix string) { assert.True(t, policy.Allow(Info{Domain: defaultDomain}), "first should work"+suffix) assert.False(t, policy.Allow(Info{Domain: defaultDomain}), "second should be limited"+suffix) // smaller local limit applies assert.False(t, policy.Allow(Info{Domain: defaultDomain}), "third should be limited"+suffix) } check("") // allow bucket to refill time.Sleep(time.Second) check(" after refresh") } func TestMultiStageRateLimiterBlockedByGlobalRps(t *testing.T) { t.Parallel() policy := newFixedRpsMultiStageRateLimiter(t, 1, 2) check := func(suffix string) { assert.True(t, policy.Allow(Info{Domain: defaultDomain}), "first should work"+suffix) assert.False(t, policy.Allow(Info{Domain: defaultDomain}), "second should be limited"+suffix) // smaller global limit applies assert.False(t, policy.Allow(Info{Domain: defaultDomain}), "third should be limited"+suffix) } check("") // allow bucket to refill time.Sleep(time.Second) check(" after refill") } func TestMultiStageRateLimitingMultipleDomains(t *testing.T) { t.Parallel() policy := newFixedRpsMultiStageRateLimiter(t, 2, 1) // should allow 1/s per domain, 2/s total check := func(suffix string) { assert.True(t, policy.Allow(Info{Domain: "one"}), "1:1 should work"+suffix) assert.False(t, policy.Allow(Info{Domain: "one"}), "1:2 should be limited"+suffix) // per domain limited assert.True(t, policy.Allow(Info{Domain: "two"}), "2:1 should work"+suffix) assert.False(t, policy.Allow(Info{Domain: "two"}), "2:2 should be limited"+suffix) // per domain limited + global limited // third domain should be entirely cut off by global and cannot perform any requests assert.False(t, policy.Allow(Info{Domain: "three"}), "3:1 should be limited"+suffix) // allowed by domain, but limited by global } check("") // allow bucket to refill time.Sleep(time.Second) check(" after refill") } func TestMultiStageRateLimiterWait(t *testing.T) { t.Parallel() ctx := context.Background() cases := []struct { name string policy Policy info Info expectedErr error }{ { name: "both allow", policy: newFixedRpsMultiStageRateLimiter(t, 2, 1), info: Info{Domain: defaultDomain}, expectedErr: nil, }, { name: "global blocks", policy: newFixedRpsMultiStageRateLimiter(t, 0, 1), info: Info{Domain: defaultDomain}, expectedErr: clock.ErrCannotWait, }, { name: "domain blocks", policy: newFixedRpsMultiStageRateLimiter(t, 2, 0), info: Info{Domain: defaultDomain}, expectedErr: clock.ErrCannotWait, }, { name: "both block", policy: newFixedRpsMultiStageRateLimiter(t, 0, 0), info: Info{Domain: defaultDomain}, expectedErr: clock.ErrCannotWait, }, { name: "empty domain uses global only - allow", policy: newFixedRpsMultiStageRateLimiter(t, 1, 0), info: Info{Domain: ""}, expectedErr: nil, }, { name: "empty domain uses global only - block", policy: newFixedRpsMultiStageRateLimiter(t, 0, 0), info: Info{Domain: ""}, expectedErr: clock.ErrCannotWait, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { policy := tc.policy err := policy.Wait(ctx, tc.info) if tc.expectedErr != nil { assert.ErrorIs(t, err, tc.expectedErr) } else { assert.NoError(t, err) } }) } } func BenchmarkMultiStageRateLimiter(b *testing.B) { policy := newFixedRpsMultiStageRateLimiter(b, defaultRps, defaultRps) for n := 0; n < b.N; n++ { policy.Allow(Info{Domain: defaultDomain}) } } func BenchmarkMultiStageRateLimiter20Domains(b *testing.B) { numDomains := 20 policy := newFixedRpsMultiStageRateLimiter(b, defaultRps, defaultRps) domains := getDomains(numDomains) for n := 0; n < b.N; n++ { policy.Allow(Info{Domain: domains[n%numDomains]}) } } func BenchmarkMultiStageRateLimiter100Domains(b *testing.B) { numDomains := 100 policy := newFixedRpsMultiStageRateLimiter(b, defaultRps, defaultRps) domains := getDomains(numDomains) for n := 0; n < b.N; n++ { policy.Allow(Info{Domain: domains[n%numDomains]}) } } func BenchmarkMultiStageRateLimiter1000Domains(b *testing.B) { numDomains := 1000 policy := newFixedRpsMultiStageRateLimiter(b, defaultRps, defaultRps) domains := getDomains(numDomains) for n := 0; n < b.N; n++ { policy.Allow(Info{Domain: domains[n%numDomains]}) } } func TestDynamicRateLimiter_RepeatedCalls(t *testing.T) { ttl := time.Second mockTime := clock.NewMockedTimeSource() // Use the number of times the function has been called as a counter and ensure that we make no extra calls rps := 0 rpsFunc := func() float64 { res := rps rps = rps + 1 return float64(res) } limiter := NewDynamicRateLimiterWithOpts(rpsFunc, DynamicRateLimiterOpts{ TTL: time.Second, MinBurst: 0, TimeSource: mockTime, }) assert.Equal(t, limiter.Limit(), rate.Limit(0)) assert.Equal(t, limiter.Limit(), rate.Limit(0)) mockTime.Advance(ttl - 1) assert.Equal(t, limiter.Limit(), rate.Limit(0)) mockTime.Advance(time.Nanosecond) assert.Equal(t, limiter.Limit(), rate.Limit(1)) assert.Equal(t, limiter.Limit(), rate.Limit(1)) // Offset the time from the TTL and ensure it's still respected mockTime.Advance(1500 * time.Millisecond) assert.Equal(t, limiter.Limit(), rate.Limit(2)) mockTime.Advance(time.Second - 1) assert.Equal(t, limiter.Limit(), rate.Limit(2)) mockTime.Advance(time.Nanosecond) assert.Equal(t, limiter.Limit(), rate.Limit(3)) } func TestDynamicRateLimiter_Allow(t *testing.T) { mockTime := clock.NewMockedTimeSource() rps := 0 limiter := NewDynamicRateLimiterWithOpts(func() float64 { return float64(rps) }, DynamicRateLimiterOpts{ TTL: time.Second, MinBurst: 1, TimeSource: mockTime, }) assert.Equal(t, false, limiter.Allow()) rps = 1 assert.Equal(t, false, limiter.Allow()) mockTime.Advance(time.Second) assert.Equal(t, true, limiter.Allow()) } func TestDynamicRateLimiter_Limit(t *testing.T) { mockTime := clock.NewMockedTimeSource() rps := 0 limiter := NewDynamicRateLimiterWithOpts(func() float64 { return float64(rps) }, DynamicRateLimiterOpts{ TTL: time.Second, MinBurst: 1, TimeSource: mockTime, }) assert.Equal(t, rate.Limit(0), limiter.Limit()) rps = 1 assert.Equal(t, rate.Limit(0), limiter.Limit()) mockTime.Advance(time.Second) assert.Equal(t, rate.Limit(1), limiter.Limit()) } func TestDynamicRateLimiter_Reserve(t *testing.T) { mockTime := clock.NewMockedTimeSource() rps := 0 limiter := NewDynamicRateLimiterWithOpts(func() float64 { return float64(rps) }, DynamicRateLimiterOpts{ TTL: time.Second, MinBurst: 1, TimeSource: mockTime, }) res := limiter.Reserve() assert.Equal(t, false, res.Allow()) res.Used(false) rps = 1 res = limiter.Reserve() assert.Equal(t, false, res.Allow()) res.Used(false) mockTime.Advance(time.Second) res = limiter.Reserve() assert.Equal(t, true, res.Allow()) res.Used(true) } func TestDynamicRateLimiter_Wait(t *testing.T) { ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second) t.Cleanup(cancelFunc) mockTime := clock.NewMockedTimeSource() rps := 0 limiter := NewDynamicRateLimiterWithOpts(func() float64 { return float64(rps) }, DynamicRateLimiterOpts{ TTL: time.Second, MinBurst: 1, TimeSource: mockTime, }) err := limiter.Wait(ctx) assert.ErrorIs(t, err, clock.ErrCannotWait) rps = 1 err = limiter.Wait(ctx) assert.ErrorIs(t, err, clock.ErrCannotWait) mockTime.Advance(time.Second) err = limiter.Wait(ctx) assert.NoError(t, err) } func newFixedRpsMultiStageRateLimiter(t testing.TB, globalRps float64, domainRps int) Policy { return NewMultiStageRateLimiter( NewDynamicRateLimiter(func() float64 { return globalRps }), NewCollection(newStubFactory(t, domainRps)), ) } func getDomains(n int) []string { domains := make([]string, 0, n) for i := 0; i < n; i++ { domains = append(domains, fmt.Sprintf("domains%v", i)) } return domains } func newStubFactory(t testing.TB, rps int) *stubLimiterFactory { return &stubLimiterFactory{ t: t, rps: rps, } } type stubLimiterFactory struct { t testing.TB rps int } func (s *stubLimiterFactory) GetLimiter(domain string) Limiter { return clock.NewRatelimiter(rate.Limit(s.rps), s.rps) } ================================================ FILE: common/quotas/limiterfactory_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/quotas (interfaces: LimiterFactory) // // Generated by this command: // // mockgen -package=quotas -destination=limiterfactory_mock.go github.com/uber/cadence/common/quotas LimiterFactory // // Package quotas is a generated GoMock package. package quotas import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockLimiterFactory is a mock of LimiterFactory interface. type MockLimiterFactory struct { ctrl *gomock.Controller recorder *MockLimiterFactoryMockRecorder isgomock struct{} } // MockLimiterFactoryMockRecorder is the mock recorder for MockLimiterFactory. type MockLimiterFactoryMockRecorder struct { mock *MockLimiterFactory } // NewMockLimiterFactory creates a new mock instance. func NewMockLimiterFactory(ctrl *gomock.Controller) *MockLimiterFactory { mock := &MockLimiterFactory{ctrl: ctrl} mock.recorder = &MockLimiterFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockLimiterFactory) EXPECT() *MockLimiterFactoryMockRecorder { return m.recorder } // GetLimiter mocks base method. func (m *MockLimiterFactory) GetLimiter(domain string) Limiter { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLimiter", domain) ret0, _ := ret[0].(Limiter) return ret0 } // GetLimiter indicates an expected call of GetLimiter. func (mr *MockLimiterFactoryMockRecorder) GetLimiter(domain any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLimiter", reflect.TypeOf((*MockLimiterFactory)(nil).GetLimiter), domain) } ================================================ FILE: common/quotas/multistageratelimiter.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package quotas import "context" // MultiStageRateLimiter indicates a domain specific rate limit policy type MultiStageRateLimiter struct { domainLimiters ICollection globalLimiter Limiter } // NewMultiStageRateLimiter returns a new domain quota rate limiter. This is about // an order of magnitude slower than func NewMultiStageRateLimiter(global Limiter, domainLimiters ICollection) *MultiStageRateLimiter { return &MultiStageRateLimiter{ domainLimiters: domainLimiters, globalLimiter: global, } } // Allow attempts to allow a request to go through. The method returns // immediately with a true or false indicating if the request can make // progress func (d *MultiStageRateLimiter) Allow(info Info) (allowed bool) { domain := info.Domain if len(domain) == 0 { return d.globalLimiter.Allow() } // take a reservation with the domain limiter first rsv := d.domainLimiters.For(domain).Reserve() defer func() { rsv.Used(allowed) // returns the token if allowed but not used }() if !rsv.Allow() { return false } // ensure that the reservation does not break the global rate limit, if it // does, cancel the reservation and do not allow to proceed. if !d.globalLimiter.Allow() { return false } return true } // Wait waits up till the context deadline for a rate limit token to allow the request // to go through. This waits on the per-domain limiter, then waits on the global limiter. func (d *MultiStageRateLimiter) Wait(ctx context.Context, info Info) error { domain := info.Domain if len(domain) == 0 { return d.globalLimiter.Wait(ctx) } err := d.domainLimiters.For(domain).Wait(ctx) if err != nil { return err } // A limitation in this implementation is that when domain limiter // allows but global limiter does not, we have already consumed a token // from the domain limiter and cannot return it, because Wait() doesn't // return a reservation. This should not affect throughput since // global limiter is the bottleneck in this situation. return d.globalLimiter.Wait(ctx) } ================================================ FILE: common/quotas/permember/permember.go ================================================ // Copyright (c) 2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package permember import ( "math" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/quotas" ) // PerMember allows creating per instance RPS based on globalRPS averaged by member count for a given service. // If member count can not be retrieved or globalRPS is not provided it falls back to instanceRPS. func PerMember(service string, globalRPS, instanceRPS float64, resolver membership.Resolver) float64 { if globalRPS <= 0 { return instanceRPS } memberCount, err := resolver.MemberCount(service) if err != nil || memberCount < 1 { return instanceRPS } avgQuota := math.Max(globalRPS/float64(memberCount), 1) return math.Min(avgQuota, instanceRPS) } // NewPerMemberDynamicRateLimiterFactory creates a new LimiterFactory which creates // a new DynamicRateLimiter for each domain, the RPS for the DynamicRateLimiter is given // by the globalRPS and averaged by member count for a given service. // instanceRPS is used as a fallback if globalRPS is not provided. func NewPerMemberDynamicRateLimiterFactory( service string, globalRPS dynamicproperties.IntPropertyFnWithDomainFilter, instanceRPS dynamicproperties.IntPropertyFnWithDomainFilter, resolver membership.Resolver, ) quotas.LimiterFactory { return perMemberFactory{ service: service, globalRPS: globalRPS, instanceRPS: instanceRPS, resolver: resolver, } } type perMemberFactory struct { service string globalRPS dynamicproperties.IntPropertyFnWithDomainFilter instanceRPS dynamicproperties.IntPropertyFnWithDomainFilter resolver membership.Resolver } func (f perMemberFactory) GetLimiter(domain string) quotas.Limiter { return quotas.NewDynamicRateLimiter(func() float64 { return PerMember( f.service, float64(f.globalRPS(domain)), float64(f.instanceRPS(domain)), f.resolver, ) }) } ================================================ FILE: common/quotas/permember/permember_test.go ================================================ // Copyright (c) 2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package permember import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/membership" ) func Test_PerMember(t *testing.T) { ctrl := gomock.NewController(t) resolver := membership.NewMockResolver(ctrl) resolver.EXPECT().MemberCount("A").Return(10, nil).MinTimes(1) resolver.EXPECT().MemberCount("X").Return(0, assert.AnError).MinTimes(1) resolver.EXPECT().MemberCount("Y").Return(0, nil).MinTimes(1) // Invalid service - fallback to instanceRPS assert.Equal(t, 3.0, PerMember("X", 20.0, 3.0, resolver)) // Invalid member count - fallback to instanceRPS assert.Equal(t, 3.0, PerMember("Y", 20.0, 3.0, resolver)) // GlobalRPS not provided - fallback to instanceRPS assert.Equal(t, 3.0, PerMember("A", 0, 3.0, resolver)) // Calculate average per member RPS (prefer averaged global - lower) assert.Equal(t, 2.0, PerMember("A", 20.0, 3.0, resolver)) // Calculate average per member RPS (prefer instanceRPS - lower) assert.Equal(t, 3.0, PerMember("A", 100.0, 3.0, resolver)) } func Test_PerMemberFactory(t *testing.T) { ctrl := gomock.NewController(t) resolver := membership.NewMockResolver(ctrl) resolver.EXPECT().MemberCount("A").Return(10, nil).MinTimes(1) factory := NewPerMemberDynamicRateLimiterFactory( "A", func(string) int { return 20 }, func(string) int { return 3 }, resolver, ) limiter := factory.GetLimiter("TestDomainName") // The limit is 20 and there are 10 instances, so the per member limit is 2 assert.Equal(t, true, limiter.Allow()) assert.Equal(t, true, limiter.Allow()) assert.Equal(t, false, limiter.Allow()) } ================================================ FILE: common/quotas/policy_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/common/quotas (interfaces: Policy) // // Generated by this command: // // mockgen -package=quotas -destination=policy_mock.go github.com/uber/cadence/common/quotas Policy // // Package quotas is a generated GoMock package. package quotas import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockPolicy is a mock of Policy interface. type MockPolicy struct { ctrl *gomock.Controller recorder *MockPolicyMockRecorder isgomock struct{} } // MockPolicyMockRecorder is the mock recorder for MockPolicy. type MockPolicyMockRecorder struct { mock *MockPolicy } // NewMockPolicy creates a new mock instance. func NewMockPolicy(ctrl *gomock.Controller) *MockPolicy { mock := &MockPolicy{ctrl: ctrl} mock.recorder = &MockPolicyMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPolicy) EXPECT() *MockPolicyMockRecorder { return m.recorder } // Allow mocks base method. func (m *MockPolicy) Allow(info Info) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Allow", info) ret0, _ := ret[0].(bool) return ret0 } // Allow indicates an expected call of Allow. func (mr *MockPolicyMockRecorder) Allow(info any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Allow", reflect.TypeOf((*MockPolicy)(nil).Allow), info) } // Wait mocks base method. func (m *MockPolicy) Wait(ctx context.Context, info Info) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Wait", ctx, info) ret0, _ := ret[0].(error) return ret0 } // Wait indicates an expected call of Wait. func (mr *MockPolicyMockRecorder) Wait(ctx, info any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Wait", reflect.TypeOf((*MockPolicy)(nil).Wait), ctx, info) } ================================================ FILE: common/rangeiter/dynamic_config_linear_iterator.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package rangeiter import ( "fmt" "sync" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) // DynamicConfigLinearIterator is a LinearIterator that can be reconfigured dynamically. // It is safe for concurrent use. Every operation checks the values of min, max, and stepCount in dynamic config // and refreshes the iterator if they have changed. type DynamicConfigLinearIterator[T Integer] struct { mx sync.Mutex logger log.Logger iter *LinearIterator[T] curSpec *linearIteratorSpec[T] minFn func() T maxFn func() T stepCountFn func() int } // NewDynamicConfigLinearIterator creates a new DynamicConfigLinearIterator. func NewDynamicConfigLinearIterator[T Integer](min, max func() T, stepCount func() int, logger log.Logger) *DynamicConfigLinearIterator[T] { iter := &DynamicConfigLinearIterator[T]{ minFn: min, maxFn: max, stepCountFn: stepCount, logger: logger, } iter.curSpec = iter.newLinearIteratorSpec() iter.iter = iter.curSpec.newIterator() iter.logger.Info("Dynamic config linear iterator initialized", tag.DynamicConfigLinearIteratorSpec(iter.curSpec), ) return iter } // refreshIterator checks if the spec has changed and refreshes the iterator if it has. func (d *DynamicConfigLinearIterator[T]) refreshIterator() { newSpec := d.newLinearIteratorSpec() if newSpec.equal(d.curSpec) { return } d.logger.Info("Dynamic config linear iterator spec has changed, refreshing iterator, applying new spec", tag.DynamicConfigLinearIteratorSpec(newSpec), ) d.curSpec = newSpec d.iter = newSpec.newIterator() } // newLinearIteratorSpec creates a new linearIteratorSpec from the current dynamic config. func (d *DynamicConfigLinearIterator[T]) newLinearIteratorSpec() *linearIteratorSpec[T] { return &linearIteratorSpec[T]{ min: d.minFn(), max: d.maxFn(), stepCount: d.stepCountFn(), } } // Next returns the next value in the iterator. func (d *DynamicConfigLinearIterator[T]) Next() T { d.mx.Lock() defer d.mx.Unlock() d.refreshIterator() return d.iter.Next() } // Previous returns the previous value in the iterator. func (d *DynamicConfigLinearIterator[T]) Previous() T { d.mx.Lock() defer d.mx.Unlock() d.refreshIterator() return d.iter.Previous() } // Value returns the current value in the iterator. func (d *DynamicConfigLinearIterator[T]) Value() T { d.mx.Lock() defer d.mx.Unlock() d.refreshIterator() return d.iter.Value() } // Reset resets the iterator to the beginning. func (d *DynamicConfigLinearIterator[T]) Reset() { d.mx.Lock() defer d.mx.Unlock() d.refreshIterator() d.iter.Reset() } // linearIteratorSpec is a specification for a LinearIterator type linearIteratorSpec[T Integer] struct { min, max T stepCount int } // String returns a string representation of the spec. func (s *linearIteratorSpec[T]) String() string { return fmt.Sprintf("min: %v, max: %v, stepCount: %v", s.min, s.max, s.stepCount) } // newIterator creates a new LinearIterator from the spec. func (s *linearIteratorSpec[T]) newIterator() *LinearIterator[T] { return NewLinearIterator(s.min, s.max, s.stepCount) } // equal returns true if the spec is equal to another spec. func (s *linearIteratorSpec[T]) equal(b *linearIteratorSpec[T]) bool { return s.min == b.min && s.max == b.max && s.stepCount == b.stepCount } ================================================ FILE: common/rangeiter/dynamic_config_linear_iterator_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package rangeiter import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/log/testlogger" ) func TestDynamicConfigLinearIterator_Next(t *testing.T) { var ( min = 10 max = 20 stepCount = 3 // range [10, 15, 20] minFn = func() int { return min } maxFn = func() int { return max } stepCountFn = func() int { return stepCount } ) iter := NewDynamicConfigLinearIterator(minFn, maxFn, stepCountFn, testlogger.New(t)) t.Run("initialized values", func(t *testing.T) { // Start value - 15, next values - 20, 20 assert.Equal(t, 20, iter.Next()) assert.Equal(t, 20, iter.Next()) }) t.Run("changed max value", func(t *testing.T) { max = 30 // range [10, 20, 30] // Start value - 20, next value - 30 assert.Equal(t, 30, iter.Next()) }) t.Run("changed step count value", func(t *testing.T) { stepCount = 6 // range [10, 14, 18, 22, 26, 30] // Start value - 18 assert.Equal(t, 22, iter.Next()) assert.Equal(t, 26, iter.Next()) assert.Equal(t, 30, iter.Next()) }) } func TestDynamicConfigLinearIterator_Previous(t *testing.T) { var ( min = 10 max = 20 stepCount = 3 // range [10, 15, 20] minFn = func() int { return min } maxFn = func() int { return max } stepCountFn = func() int { return stepCount } ) iter := NewDynamicConfigLinearIterator(minFn, maxFn, stepCountFn, testlogger.New(t)) t.Run("initialized values", func(t *testing.T) { // Start value - 15, previous values - 10, 10 assert.Equal(t, 10, iter.Previous()) assert.Equal(t, 10, iter.Previous()) }) t.Run("changed max value", func(t *testing.T) { max = 30 // range [10, 20, 30] // Start value - 20, previous value - 10 assert.Equal(t, 10, iter.Previous()) }) t.Run("changed step count value", func(t *testing.T) { stepCount = 6 // range [10, 14, 18, 22, 26, 30] // Start value - 18 assert.Equal(t, 14, iter.Previous()) assert.Equal(t, 10, iter.Previous()) assert.Equal(t, 10, iter.Previous()) }) } func TestDynamicConfigLinearIterator_Value(t *testing.T) { var ( min = 10 max = 20 stepCount = 3 // range [10, 15, 20] minFn = func() int { return min } maxFn = func() int { return max } stepCountFn = func() int { return stepCount } ) iter := NewDynamicConfigLinearIterator(minFn, maxFn, stepCountFn, testlogger.New(t)) t.Run("initialized values", func(t *testing.T) { // range [10, 15, 20] assert.Equal(t, 15, iter.Value()) }) t.Run("changed max value", func(t *testing.T) { max = 30 // range [10, 20, 30] assert.Equal(t, 20, iter.Value()) }) t.Run("changed step count value", func(t *testing.T) { stepCount = 6 // range [10, 14, 18, 22, 26, 30] assert.Equal(t, 18, iter.Value()) }) } func TestDynamicConfigLinearIterator_Reset(t *testing.T) { var ( min = 10 max = 20 stepCount = 3 // range [10, 15, 20] minFn = func() int { return min } maxFn = func() int { return max } stepCountFn = func() int { return stepCount } ) iter := NewDynamicConfigLinearIterator(minFn, maxFn, stepCountFn, testlogger.New(t)) t.Run("initialized values", func(t *testing.T) { // range [10, 15, 20] assert.Equal(t, 15, iter.Value()) iter.Reset() assert.Equal(t, 15, iter.Value()) }) t.Run("changed max value", func(t *testing.T) { max = 30 // range [10, 20, 30] assert.Equal(t, 20, iter.Value()) iter.Reset() assert.Equal(t, 20, iter.Value()) }) t.Run("changed step count value", func(t *testing.T) { stepCount = 6 // range [10, 14, 18, 22, 26, 30] assert.Equal(t, 18, iter.Value()) iter.Reset() assert.Equal(t, 18, iter.Value()) }) t.Run("reached max and config not changed", func(t *testing.T) { // range [10, 14, 18, 22, 26, 30] assert.Equal(t, 22, iter.Next()) assert.Equal(t, 26, iter.Next()) assert.Equal(t, 30, iter.Next()) iter.Reset() assert.Equal(t, 18, iter.Value()) }) t.Run("reached min and config not changed", func(t *testing.T) { // range [10, 14, 18, 22, 26, 30] assert.Equal(t, 14, iter.Previous()) assert.Equal(t, 10, iter.Previous()) iter.Reset() assert.Equal(t, 18, iter.Value()) }) } ================================================ FILE: common/rangeiter/iterator.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package rangeiter import "golang.org/x/exp/constraints" // Iterator is an interface for iterating through a range between two values. // The range is a range of integers from a minimum to a maximum value (inclusive). type Iterator[T Integer] interface { // Next returns the next value closer to the max value in the range // If the current value is the max value, Next will return the max value Next() T // Previous returns the previous value closer to the min value in the range // If the current value is the min value, Previous will return the min value Previous() T // Value returns the current value in the range Value() T // Reset resets the Iterator to its initial state Reset() } // Integer is a type constraint for the Iterator interface to ensure that only integer types are used. type Integer = constraints.Integer ================================================ FILE: common/rangeiter/linear_iterator.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package rangeiter // LinearIterator is an Iterator that steps through a range linearly with a fixed step size. // The fixed step size is calculated based on the range and the number of steps provided. type LinearIterator[T Integer] struct { cur int steps []T } // NewLinearIterator returns a new LinearIterator. // The range is a range of integers from min to max. The stepCount is the number of steps between min and max (both inclusive). // After creating the Iterator, the current value will be the middle value of the range. // If stepCount is less or equal than 2 or the difference between min and max is less than 1, the Iterator will only have min and max. // - range [0, 10], stepCount 1 -> [0, 10] // - range [10, 10], stepCount 2 -> [10, 10] // // If the difference between min and max is less than stepCount, the Iterator will have all the values between min and max. // - range [0, 5], stepCount 10 -> [0, 1, 2, 3, 4, 5] // - range [0, 10], stepCount 11 -> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] func NewLinearIterator[T Integer](min, max T, stepCount int) *LinearIterator[T] { // Ensure min <= max if min > max { min, max = max, min } totalRange := int(max - min) // If stepCount is less than 2 or the difference between min and max is less than 1, // return a Iterator with only min and max if stepCount <= 2 || totalRange <= 1 { return &LinearIterator[T]{ cur: 0, steps: []T{min, max}, } } // if the total range is less than the step count, return a Iterator with all the values if totalRange < stepCount { steps := make([]T, totalRange+1) steps[0], steps[totalRange] = min, max for i := 1; i < totalRange; i++ { steps[i] = min + T(i) } return &LinearIterator[T]{ cur: (len(steps) - 1) / 2, steps: steps, } } // Calculate step size as a float to ensure even distribution stepSize := float64(totalRange / (stepCount - 1)) // Generate steps steps := make([]T, stepCount) for i := 0; i < stepCount; i++ { steps[i] = min + T(float64(i)*stepSize) } // Ensure the last value is exactly max (to avoid floating-point errors) steps[stepCount-1] = max return &LinearIterator[T]{ cur: (len(steps) - 1) / 2, steps: steps, } } // Next returns the next value in the range. func (s *LinearIterator[T]) Next() T { if s.cur < len(s.steps)-1 { s.cur++ } return s.steps[s.cur] } // Previous returns the previous value in the range. func (s *LinearIterator[T]) Previous() T { if s.cur > 0 { s.cur-- } return s.steps[s.cur] } // Value returns the current value in the range. func (s *LinearIterator[T]) Value() T { return s.steps[s.cur] } // Reset resets the Iterator to the beginning of the range. func (s *LinearIterator[T]) Reset() { s.cur = (len(s.steps) - 1) / 2 } ================================================ FILE: common/rangeiter/linear_iterator_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package rangeiter import ( "testing" "github.com/stretchr/testify/assert" ) func TestNewLinearIterator(t *testing.T) { for name, c := range map[string]struct { min, max int stepCount int want *LinearIterator[int] }{ "min greater max, stepCount greater range": { min: 10, max: 1, stepCount: 10, // range: 10 - 1 = 9 want: &LinearIterator[int]{ cur: 4, steps: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, }, }, "max greater min, stepCount is one": { min: 0, max: 10, stepCount: 1, want: &LinearIterator[int]{ cur: 0, steps: []int{0, 10}, }, }, "max greater min, stepCount is two": { min: 0, max: 10, stepCount: 2, want: &LinearIterator[int]{ cur: 0, steps: []int{0, 10}, }, }, "max greater min, stepCount is three": { min: 0, max: 10, stepCount: 3, want: &LinearIterator[int]{ cur: 1, steps: []int{0, 5, 10}, }, }, "max greater min, stepCount is four": { min: 0, max: 10, stepCount: 4, want: &LinearIterator[int]{ cur: 1, steps: []int{0, 3, 6, 10}, }, }, "max greater min, stepCount is six": { min: 0, max: 10, stepCount: 6, want: &LinearIterator[int]{ cur: 2, steps: []int{0, 2, 4, 6, 8, 10}, }, }, "max greater min than 1, stepCount is greater range": { min: 9, max: 10, stepCount: 10, want: &LinearIterator[int]{ cur: 0, steps: []int{9, 10}, }, }, "max equal mim, stepCount is greater range": { min: 10, max: 10, stepCount: 10, want: &LinearIterator[int]{ cur: 0, steps: []int{10, 10}, }, }, "max greater min, range is less than stepCount": { min: 0, max: 10, stepCount: 20, // range: 10 - 0 = 10 want: &LinearIterator[int]{ cur: 5, steps: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, }, }, "max greater min, range is equal to stepCount": { min: 0, max: 10, stepCount: 10, // range: 10 - 0 = 10 want: &LinearIterator[int]{ cur: 4, steps: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 10}, }, }, "negative max and min, stepCount is two": { min: -1000 * 1000, max: -1000, stepCount: 35, want: &LinearIterator[int]{ cur: 17, steps: []int{-1000000, -970618, -941236, -911854, -882472, -853090, -823708, -794326, -764944, -735562, -706180, -676798, -647416, -618034, -588652, -559270, -529888, -500506, -471124, -441742, -412360, -382978, -353596, -324214, -294832, -265450, -236068, -206686, -177304, -147922, -118540, -89158, -59776, -30394, -1000}, }, }, } { t.Run(name, func(t *testing.T) { got := NewLinearIterator(c.min, c.max, c.stepCount) assert.Equal(t, c.want, got) }) } } func TestLinearIterator_Next(t *testing.T) { for name, c := range map[string]struct { cur int steps []int wantValue int wantCur int }{ "cur equals to min": { cur: 0, steps: []int{0, 1, 2, 3, 4, 5}, wantValue: 1, wantCur: 1, }, "cur equals to middle": { cur: 3, // value - 3 steps: []int{0, 1, 2, 3, 4, 5}, wantValue: 4, wantCur: 4, }, "cur equals to max": { cur: 5, steps: []int{0, 1, 2, 3, 4, 5}, wantValue: 5, wantCur: 5, }, } { t.Run(name, func(t *testing.T) { s := &LinearIterator[int]{ cur: c.cur, steps: c.steps, } assert.Equal(t, c.wantValue, s.Next()) assert.Equal(t, c.wantCur, s.cur) }) } } func TestLinearIterator_Previous(t *testing.T) { for name, c := range map[string]struct { cur int steps []int wantValue int wantCur int }{ "cur equals to min": { cur: 0, steps: []int{0, 1, 2, 3, 4, 5}, wantValue: 0, wantCur: 0, }, "cur equals to middle": { cur: 3, // value - 3 steps: []int{0, 1, 2, 3, 4, 5}, wantValue: 2, wantCur: 2, }, "cur equals to max": { cur: 6, steps: []int{0, 1, 2, 3, 4, 5}, wantValue: 5, wantCur: 5, }, } { t.Run(name, func(t *testing.T) { s := &LinearIterator[int]{ cur: c.cur, steps: c.steps, } assert.Equal(t, c.wantValue, s.Previous()) assert.Equal(t, c.wantCur, s.cur) }) } } func TestLinearIterator_Reset(t *testing.T) { for name, c := range map[string]struct { cur int steps []int wantValue int wantCur int }{ "cur equals to min": { cur: 0, steps: []int{0, 1, 2, 3, 4, 5}, wantValue: 2, wantCur: 2, }, "cur equals to middle": { cur: 3, // value - 3 steps: []int{0, 1, 2, 3, 4, 5}, wantValue: 2, wantCur: 2, }, "cur equals to max": { cur: 6, steps: []int{0, 1, 2, 3, 4, 5}, wantValue: 2, wantCur: 2, }, } { t.Run(name, func(t *testing.T) { s := &LinearIterator[int]{ cur: c.cur, steps: c.steps, } s.Reset() assert.Equal(t, c.wantValue, s.Value()) assert.Equal(t, c.wantCur, s.cur) }) } } func TestLinearIterator_Value(t *testing.T) { Iterator := &LinearIterator[int]{ cur: 3, steps: []int{0, 1, 2, 3, 4, 5}, } assert.Equal(t, 3, Iterator.Value()) } ================================================ FILE: common/reconciliation/constants.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package reconciliation // Execution fixer workflow relates const ( CheckDataCorruptionWorkflowType = "check-data-corruption-workflow" CheckDataCorruptionWorkflowTaskList = "check-data-corruption-workflow-tl" CheckDataCorruptionWorkflowSignalName = "check-data-corruption-workflow-signal" CheckDataCorruptionWorkflowID = "check-data-corruption-workflow-id" CheckDataCorruptionWorkflowTimeoutInSeconds = 24 * 60 * 60 CheckDataCorruptionWorkflowTaskTimeoutInSeconds = 60 ) ================================================ FILE: common/reconciliation/entity/types.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package entity import ( "errors" "fmt" "time" "github.com/uber/cadence/common/persistence" ) type ( // Execution is a base type for executions which should be checked or fixed. Execution struct { ShardID int DomainID string WorkflowID string RunID string State int } // ConcreteExecution is a concrete execution. ConcreteExecution struct { BranchToken []byte TreeID string BranchID string Execution } // CurrentExecution is a current execution. CurrentExecution struct { CurrentRunID string Execution } // Timer is a timer scheduled to be fired Timer struct { ShardID int WorkflowID string DomainID string RunID string VisibilityTimestamp time.Time TaskID int64 TaskType int TimeoutType int EventID int64 ScheduleAttempt int64 Version int64 } ) func (t *Timer) Validate() error { if t.ShardID < 0 { return fmt.Errorf("invalid ShardID: %v", t.ShardID) } if len(t.DomainID) == 0 { return errors.New("empty DomainID") } if len(t.WorkflowID) == 0 { return errors.New("empty WorkflowID") } if len(t.RunID) == 0 { return errors.New("empty RunID") } return nil } func (t *Timer) Clone() Entity { return &Timer{} } func (t *Timer) GetShardID() int { return t.ShardID } func (t *Timer) GetDomainID() string { return t.DomainID } // ValidateExecution returns an error if Execution is not valid, nil otherwise. func validateExecution(execution *Execution) error { if execution.ShardID < 0 { return fmt.Errorf("invalid ShardID: %v", execution.ShardID) } if len(execution.DomainID) == 0 { return errors.New("empty DomainID") } if len(execution.WorkflowID) == 0 { return errors.New("empty WorkflowID") } if len(execution.RunID) == 0 { return errors.New("empty RunID") } if execution.State < persistence.WorkflowStateCreated || execution.State > persistence.WorkflowStateCorrupted { return fmt.Errorf("unknown workflow state: %v", execution.State) } return nil } // Validate returns an error if ConcreteExecution is not valid, nil otherwise. func (ce *ConcreteExecution) Validate() error { err := validateExecution(&ce.Execution) if err != nil { return err } if len(ce.BranchToken) == 0 { return errors.New("empty BranchToken") } if len(ce.TreeID) == 0 { return errors.New("empty TreeID") } if len(ce.BranchID) == 0 { return errors.New("empty BranchID") } return nil } // Validate returns an error if CurrentExecution is not valid, nil otherwise. func (curre *CurrentExecution) Validate() error { err := validateExecution(&curre.Execution) if err != nil { return err } if len(curre.CurrentRunID) == 0 { return errors.New("empty CurrentRunID") } return nil } // Clone will return a new copy of ConcreteExecution func (ConcreteExecution) Clone() Entity { return &ConcreteExecution{} } // Clone will return a new copy of CurrentExecution func (CurrentExecution) Clone() Entity { return &CurrentExecution{} } // GetShardID returns shard id func (ce *ConcreteExecution) GetShardID() int { return ce.Execution.ShardID } // GetShardID returns shard id func (curre *CurrentExecution) GetShardID() int { return curre.Execution.ShardID } // GetDomainID returns the domain id func (ce *ConcreteExecution) GetDomainID() string { return ce.DomainID } // GetDomainID returns the domain id func (curre *CurrentExecution) GetDomainID() string { return curre.DomainID } // Entity allows to deserialize and validate different type of executions type Entity interface { Validate() error Clone() Entity GetShardID() int GetDomainID() string } ================================================ FILE: common/reconciliation/entity/types_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package entity import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/persistence" ) const ( domainID = "test-domain-id" workflowID = "test-workflow-id" runID = "test-run-id" treeID = "test-tree-id" branchID = "test-branch-id" ) func TestUtilSuite(t *testing.T) { suite.Run(t, new(TypeSuite)) } type TypeSuite struct { *require.Assertions suite.Suite } func (t *TypeSuite) SetupTest() { t.Assertions = require.New(t.T()) } func (t *TypeSuite) TestValidateExecution() { testCases := []struct { execution *ConcreteExecution expectError bool }{ { execution: &ConcreteExecution{}, expectError: true, }, { execution: &ConcreteExecution{ Execution: Execution{ ShardID: -1, }, }, expectError: true, }, { execution: &ConcreteExecution{ Execution: Execution{ ShardID: 0, }, }, expectError: true, }, { execution: &ConcreteExecution{ Execution: Execution{ ShardID: 0, DomainID: domainID, }, }, expectError: true, }, { execution: &ConcreteExecution{ Execution: Execution{ ShardID: 0, DomainID: domainID, WorkflowID: workflowID, }, }, expectError: true, }, { execution: &ConcreteExecution{ Execution: Execution{ ShardID: 0, DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, }, expectError: true, }, { execution: &ConcreteExecution{ Execution: Execution{ ShardID: 0, DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, BranchToken: []byte{1, 2, 3}, }, expectError: true, }, { execution: &ConcreteExecution{ Execution: Execution{ ShardID: 0, DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, BranchToken: []byte{1, 2, 3}, TreeID: treeID, }, expectError: true, }, { execution: &ConcreteExecution{ Execution: Execution{ ShardID: 0, DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: persistence.WorkflowStateCreated - 1, }, BranchToken: []byte{1, 2, 3}, TreeID: treeID, BranchID: branchID, }, expectError: true, }, { execution: &ConcreteExecution{ Execution: Execution{ ShardID: 0, DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: persistence.WorkflowStateCorrupted + 1, }, BranchToken: []byte{1, 2, 3}, TreeID: treeID, BranchID: branchID, }, expectError: true, }, { execution: &ConcreteExecution{ Execution: Execution{ ShardID: 0, DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: persistence.WorkflowStateCreated, }, BranchToken: []byte{1, 2, 3}, TreeID: treeID, BranchID: branchID, }, expectError: false, }, } for _, tc := range testCases { err := tc.execution.Validate() if tc.expectError { t.Error(err) } else { t.NoError(err) } } } ================================================ FILE: common/reconciliation/fetcher/concrete.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package fetcher import ( "context" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) // ConcreteExecutionIterator is used to retrieve Concrete executions. func ConcreteExecutionIterator( ctx context.Context, retryer persistence.Retryer, pageSize int, ) pagination.Iterator { return pagination.NewIterator(ctx, nil, getConcreteExecutions(retryer, pageSize, codec.NewThriftRWEncoder())) } // ConcreteExecution returns a single ConcreteExecution from persistence func ConcreteExecution( ctx context.Context, retryer persistence.Retryer, request ExecutionRequest, ) (entity.Entity, error) { req := persistence.GetWorkflowExecutionRequest{ DomainID: request.DomainID, Execution: types.WorkflowExecution{ WorkflowID: request.WorkflowID, RunID: request.RunID, }, DomainName: request.DomainName, } e, err := retryer.GetWorkflowExecution(ctx, &req) if err != nil { return nil, err } branchToken, branch, err := getBranchToken(e.State.ExecutionInfo.BranchToken, e.State.VersionHistories, codec.NewThriftRWEncoder()) if err != nil { return nil, err } return &entity.ConcreteExecution{ BranchToken: branchToken, TreeID: branch.GetTreeID(), BranchID: branch.GetBranchID(), Execution: entity.Execution{ ShardID: retryer.GetShardID(), DomainID: e.State.ExecutionInfo.DomainID, WorkflowID: e.State.ExecutionInfo.WorkflowID, RunID: e.State.ExecutionInfo.RunID, State: e.State.ExecutionInfo.State, }, }, nil } func getConcreteExecutions( pr persistence.Retryer, pageSize int, encoder *codec.ThriftRWEncoder, ) pagination.FetchFn { return func(ctx context.Context, token pagination.PageToken) (pagination.Page, error) { req := &persistence.ListConcreteExecutionsRequest{ PageSize: pageSize, } if token != nil { req.PageToken = token.([]byte) } resp, err := pr.ListConcreteExecutions(ctx, req) if err != nil { return pagination.Page{}, err } executions := make([]pagination.Entity, len(resp.Executions)) for i, e := range resp.Executions { branchToken, branch, err := getBranchToken(e.ExecutionInfo.BranchToken, e.VersionHistories, encoder) if err != nil { return pagination.Page{}, err } concreteExec := &entity.ConcreteExecution{ BranchToken: branchToken, TreeID: branch.GetTreeID(), BranchID: branch.GetBranchID(), Execution: entity.Execution{ ShardID: pr.GetShardID(), DomainID: e.ExecutionInfo.DomainID, WorkflowID: e.ExecutionInfo.WorkflowID, RunID: e.ExecutionInfo.RunID, State: e.ExecutionInfo.State, }, } if err := concreteExec.Validate(); err != nil { return pagination.Page{}, err } executions[i] = concreteExec } var nextToken interface{} = resp.PageToken if len(resp.PageToken) == 0 { nextToken = nil } page := pagination.Page{ CurrentToken: token, NextToken: nextToken, Entities: executions, } return page, nil } } // getBranchToken returns the branchToken and historyBranch, error on failure. func getBranchToken( branchToken []byte, histories *persistence.VersionHistories, decoder *codec.ThriftRWEncoder, ) ([]byte, shared.HistoryBranch, error) { var branch shared.HistoryBranch bt := branchToken if histories != nil { versionHistory, err := histories.GetCurrentVersionHistory() if err != nil { return nil, branch, err } bt = versionHistory.GetBranchToken() } if err := decoder.Decode(bt, &branch); err != nil { return nil, branch, err } return bt, branch, nil } ================================================ FILE: common/reconciliation/fetcher/concrete_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package fetcher import ( "context" "fmt" "testing" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" ) func TestConcreteExecutionIterator(t *testing.T) { ctrl := gomock.NewController(t) retryer := persistence.NewMockRetryer(ctrl) retryer.EXPECT().ListConcreteExecutions(gomock.Any(), gomock.Any()). Return(&persistence.ListConcreteExecutionsResponse{}, nil). Times(1) iterator := ConcreteExecutionIterator( context.Background(), retryer, 10, ) require.NotNil(t, iterator) } func TestConcreteExecution(t *testing.T) { encoder := codec.NewThriftRWEncoder() tests := []struct { desc string req ExecutionRequest mockFn func(retryer *persistence.MockRetryer) wantEntity entity.Entity wantErr bool }{ { desc: "success", req: ExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", DomainName: "test-domain-name", }, mockFn: func(retryer *persistence.MockRetryer) { retryer.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()). Return( &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ BranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id", "test-branch-id"), State: persistence.WorkflowStateRunning, DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, }, nil, ).Times(1) retryer.EXPECT().GetShardID().Return(355).Times(1) }, wantEntity: &entity.ConcreteExecution{ BranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id", "test-branch-id"), TreeID: "test-tree-id", BranchID: "test-branch-id", Execution: entity.Execution{ ShardID: 355, DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", State: persistence.WorkflowStateRunning, }, }, }, { desc: "GetWorkflowExecution failed", req: ExecutionRequest{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", DomainName: "test-domain-name", }, mockFn: func(retryer *persistence.MockRetryer) { retryer.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()). Return(nil, fmt.Errorf("failed")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) retryer := persistence.NewMockRetryer(ctrl) tc.mockFn(retryer) gotEntity, err := ConcreteExecution(context.Background(), retryer, tc.req) if (err != nil) != tc.wantErr { t.Fatalf("ConcreteExecution() err: %v, wantErr %v", err, tc.wantErr) } if diff := cmp.Diff(tc.wantEntity, gotEntity); diff != "" { t.Errorf("ConcreteExecution() mismatch (-want +got):\n%s", diff) } }) } } func TestGetConcreteExecutions(t *testing.T) { encoder := codec.NewThriftRWEncoder() testExecutions := []*persistence.ListConcreteExecutionsEntity{ { ExecutionInfo: &persistence.WorkflowExecutionInfo{ BranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id-1", "test-branch-id-1"), State: persistence.WorkflowStateRunning, DomainID: "test-domain-id-1", WorkflowID: "test-workflow-id-1", RunID: "test-run-id-1", }, }, { ExecutionInfo: &persistence.WorkflowExecutionInfo{ BranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id-2", "test-branch-id-2"), State: persistence.WorkflowStateCompleted, DomainID: "test-domain-id-2", WorkflowID: "test-workflow-id-2", RunID: "test-run-id-2", }, }, } tests := []struct { desc string pageSize int pageToken pagination.PageToken mockFn func(*testing.T, *persistence.MockRetryer) wantPage pagination.Page wantErr bool }{ { desc: "success", pageSize: 2, pageToken: []byte("test-page-token"), mockFn: func(t *testing.T, retryer *persistence.MockRetryer) { retryer.EXPECT().ListConcreteExecutions(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *persistence.ListConcreteExecutionsRequest) (*persistence.ListConcreteExecutionsResponse, error) { wantReq := &persistence.ListConcreteExecutionsRequest{ PageSize: 2, PageToken: []byte("test-page-token"), } if diff := cmp.Diff(wantReq, req); diff != "" { t.Errorf("Request mismatch (-want +got):\n%s", diff) } return &persistence.ListConcreteExecutionsResponse{ PageToken: []byte("test-next-page-token"), Executions: testExecutions, }, nil }).Times(1) // will be called for each execution in the response retryer.EXPECT().GetShardID().Return(355).Times(2) }, wantPage: pagination.Page{ CurrentToken: []byte("test-page-token"), NextToken: []byte("test-next-page-token"), Entities: concreteExecutionsToEntities(testExecutions, 355, encoder), }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) retryer := persistence.NewMockRetryer(ctrl) tc.mockFn(t, retryer) fetchFn := getConcreteExecutions(retryer, tc.pageSize, encoder) gotPage, err := fetchFn(context.Background(), tc.pageToken) if (err != nil) != tc.wantErr { t.Fatalf("ConcreteExecution() err: %v, wantErr %v", err, tc.wantErr) } if diff := cmp.Diff(tc.wantPage, gotPage); diff != "" { t.Errorf("ConcreteExecution() mismatch (-want +got):\n%s", diff) } }) } } func TestGetBranchToken(t *testing.T) { encoder := codec.NewThriftRWEncoder() testCases := []struct { name string entity *persistence.ListConcreteExecutionsEntity wantErr bool wantBranchToken []byte wantHistoryBranch shared.HistoryBranch }{ { name: "valid branch token - no version histories", entity: &persistence.ListConcreteExecutionsEntity{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ BranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id", "test-branch-id"), }, }, wantBranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id", "test-branch-id"), wantHistoryBranch: shared.HistoryBranch{ TreeID: common.StringPtr("test-tree-id"), BranchID: common.StringPtr("test-branch-id"), }, }, { name: "valid branch token - with version histories", entity: &persistence.ListConcreteExecutionsEntity{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ BranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id", "test-branch-id"), }, VersionHistories: &persistence.VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: []*persistence.VersionHistory{ {}, // this will be ignored because index is 1 { BranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id-from-versionhistory", "test-branch-id-from-versionhistory"), }, }, }, }, wantBranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id-from-versionhistory", "test-branch-id-from-versionhistory"), wantHistoryBranch: shared.HistoryBranch{ TreeID: common.StringPtr("test-tree-id-from-versionhistory"), BranchID: common.StringPtr("test-branch-id-from-versionhistory"), }, }, { name: "version history index out of bound", entity: &persistence.ListConcreteExecutionsEntity{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ BranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id", "test-branch-id"), }, VersionHistories: &persistence.VersionHistories{ CurrentVersionHistoryIndex: 2, Histories: []*persistence.VersionHistory{ {}, { BranchToken: mustGetValidBranchToken(t, encoder, "test-tree-id-from-versionhistory", "test-branch-id-from-versionhistory"), }, }, }, }, wantErr: true, }, { name: "invalid branch token", entity: &persistence.ListConcreteExecutionsEntity{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ BranchToken: []byte("invalid"), }, }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { branchToken, branch, err := getBranchToken( tc.entity.ExecutionInfo.BranchToken, tc.entity.VersionHistories, encoder, ) if tc.wantErr { require.Error(t, err) require.Nil(t, branchToken) require.Empty(t, branch.GetTreeID()) require.Empty(t, branch.GetBranchID()) } else { if diff := cmp.Diff(tc.wantHistoryBranch, branch); diff != "" { t.Fatalf("HistoryBranch mismatch (-want +got):\n%s", diff) } require.Equal(t, tc.wantBranchToken, branchToken) } }) } } func mustGetValidBranchToken(t *testing.T, encoder *codec.ThriftRWEncoder, treeID, branchID string) []byte { hb := &shared.HistoryBranch{ TreeID: common.StringPtr(treeID), BranchID: common.StringPtr(branchID), } bytes, err := encoder.Encode(hb) if err != nil { t.Fatalf("failed to encode branch token: %v", err) } return bytes } func concreteExecutionsToEntities(execs []*persistence.ListConcreteExecutionsEntity, shardID int, encoder *codec.ThriftRWEncoder) []pagination.Entity { entities := make([]pagination.Entity, len(execs)) for i, e := range execs { branchToken, branch, err := getBranchToken(e.ExecutionInfo.BranchToken, e.VersionHistories, encoder) if err != nil { return nil } concreteExec := &entity.ConcreteExecution{ BranchToken: branchToken, TreeID: branch.GetTreeID(), BranchID: branch.GetBranchID(), Execution: entity.Execution{ ShardID: shardID, DomainID: e.ExecutionInfo.DomainID, WorkflowID: e.ExecutionInfo.WorkflowID, RunID: e.ExecutionInfo.RunID, State: e.ExecutionInfo.State, }, } entities[i] = concreteExec } return entities } ================================================ FILE: common/reconciliation/fetcher/current.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package fetcher import ( "context" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" ) // CurrentExecutionIterator is used to retrieve Concrete executions. func CurrentExecutionIterator( ctx context.Context, retryer persistence.Retryer, pageSize int, ) pagination.Iterator { return pagination.NewIterator(ctx, nil, getCurrentExecution(retryer, pageSize)) } // CurrentExecution returns a single execution func CurrentExecution( ctx context.Context, retryer persistence.Retryer, request ExecutionRequest, ) (entity.Entity, error) { req := persistence.GetCurrentExecutionRequest{ DomainID: request.DomainID, WorkflowID: request.WorkflowID, DomainName: request.DomainName, } e, err := retryer.GetCurrentExecution(ctx, &req) if err != nil { return nil, err } return &entity.CurrentExecution{ CurrentRunID: e.RunID, Execution: entity.Execution{ ShardID: retryer.GetShardID(), DomainID: request.DomainID, WorkflowID: request.WorkflowID, RunID: e.RunID, State: e.State, }, }, nil } func getCurrentExecution( pr persistence.Retryer, pageSize int, ) pagination.FetchFn { return func(ctx context.Context, token pagination.PageToken) (pagination.Page, error) { req := &persistence.ListCurrentExecutionsRequest{ PageSize: pageSize, } if token != nil { req.PageToken = token.([]byte) } resp, err := pr.ListCurrentExecutions(ctx, req) if err != nil { return pagination.Page{}, err } executions := make([]pagination.Entity, len(resp.Executions)) for i, e := range resp.Executions { currentExec := &entity.CurrentExecution{ CurrentRunID: e.CurrentRunID, Execution: entity.Execution{ ShardID: pr.GetShardID(), DomainID: e.DomainID, WorkflowID: e.WorkflowID, RunID: e.RunID, State: e.State, }, } if err := currentExec.Validate(); err != nil { return pagination.Page{}, err } executions[i] = currentExec } var nextToken interface{} = resp.PageToken if len(resp.PageToken) == 0 { nextToken = nil } page := pagination.Page{ CurrentToken: token, NextToken: nextToken, Entities: executions, } return page, nil } } ================================================ FILE: common/reconciliation/fetcher/current_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package fetcher import ( "context" "fmt" "testing" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" ) func TestCurrentExecutionIterator(t *testing.T) { ctrl := gomock.NewController(t) retryer := persistence.NewMockRetryer(ctrl) retryer.EXPECT().ListCurrentExecutions(gomock.Any(), gomock.Any()). Return(&persistence.ListCurrentExecutionsResponse{}, nil). Times(1) iterator := CurrentExecutionIterator( context.Background(), retryer, 10, ) require.NotNil(t, iterator) } func TestCurrentExecution(t *testing.T) { ctx := context.Background() testCases := []struct { name string setupMock func(*persistence.MockRetryer) request ExecutionRequest wantEntity entity.Entity wantErr bool }{ { name: "success", request: ExecutionRequest{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", DomainName: "testDomainName", }, setupMock: func(mockRetryer *persistence.MockRetryer) { mockRetryer.EXPECT().GetCurrentExecution(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", DomainName: "testDomainName", }).Return(&persistence.GetCurrentExecutionResponse{ RunID: "testRunID", State: persistence.WorkflowStateRunning, }, nil).Times(1) mockRetryer.EXPECT().GetShardID().Return(123).Times(1) }, wantEntity: &entity.CurrentExecution{ CurrentRunID: "testRunID", Execution: entity.Execution{ ShardID: 123, DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", State: persistence.WorkflowStateRunning, }, }, }, { name: "GetCurrentExecution failed", request: ExecutionRequest{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", DomainName: "testDomainName", }, setupMock: func(mockRetryer *persistence.MockRetryer) { mockRetryer.EXPECT().GetCurrentExecution(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", DomainName: "testDomainName", }).Return(nil, fmt.Errorf("failed")).Times(1) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockRetryer := persistence.NewMockRetryer(ctrl) tc.setupMock(mockRetryer) gotEntity, err := CurrentExecution(ctx, mockRetryer, tc.request) if (err != nil) != tc.wantErr { t.Errorf("CurrentExecution() error = %v, wantErr %v", err, tc.wantErr) } require.Equal(t, tc.wantEntity, gotEntity) }) } } func TestGetCurrentExecution(t *testing.T) { ctx := context.Background() pageSize := 10 testCases := []struct { name string setupMock func(*persistence.MockRetryer) wantPage pagination.Page wantErr bool }{ { name: "success", setupMock: func(mockRetryer *persistence.MockRetryer) { executions := []*persistence.CurrentWorkflowExecution{ { DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", State: persistence.WorkflowStateRunning, CurrentRunID: "testCurrentRunID", // Setting CurrentRunID }, } mockRetryer.EXPECT(). ListCurrentExecutions(ctx, &persistence.ListCurrentExecutionsRequest{ PageSize: pageSize, }). Return(&persistence.ListCurrentExecutionsResponse{ Executions: executions, PageToken: nil, }, nil).Times(1) mockRetryer.EXPECT().GetShardID().Return(123).Times(1) }, wantPage: pagination.Page{ Entities: []pagination.Entity{ &entity.CurrentExecution{ CurrentRunID: "testCurrentRunID", // This should match with the mocked data Execution: entity.Execution{ ShardID: 123, DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", State: persistence.WorkflowStateRunning, }, }, }, }, wantErr: false, }, { name: "ListCurrentExecutions failed", setupMock: func(mockRetryer *persistence.MockRetryer) { mockRetryer.EXPECT(). ListCurrentExecutions(ctx, &persistence.ListCurrentExecutionsRequest{ PageSize: pageSize, }). Return(nil, fmt.Errorf("failed")). Times(1) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockRetryer := persistence.NewMockRetryer(ctrl) tc.setupMock(mockRetryer) fetchFn := getCurrentExecution(mockRetryer, pageSize) page, err := fetchFn(ctx, nil) if (err != nil) != tc.wantErr { t.Errorf("getCurrentExecution() error = %v, wantErr %v", err, tc.wantErr) } require.Equal(t, tc.wantPage, page) }) } } ================================================ FILE: common/reconciliation/fetcher/timer.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package fetcher import ( "context" "time" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" ) // TimerIterator is used to retrieve Concrete executions. func TimerIterator( ctx context.Context, retryer persistence.Retryer, minTimestamp time.Time, maxTimestamp time.Time, pageSize int, ) pagination.Iterator { return pagination.NewIterator(ctx, nil, getUserTimers(retryer, minTimestamp, maxTimestamp, pageSize)) } func getUserTimers( pr persistence.Retryer, minTimestamp time.Time, maxTimestamp time.Time, pageSize int, ) pagination.FetchFn { return func(ctx context.Context, token pagination.PageToken) (pagination.Page, error) { req := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(minTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxTimestamp, 0), PageSize: pageSize, } if token != nil { req.NextPageToken = token.([]byte) } resp, err := pr.GetHistoryTasks(ctx, req) if err != nil { return pagination.Page{}, err } var timers []pagination.Entity for _, t := range resp.Tasks { if t.GetTaskType() != persistence.TaskTypeUserTimer { continue } timer := &entity.Timer{ ShardID: pr.GetShardID(), DomainID: t.GetDomainID(), WorkflowID: t.GetWorkflowID(), RunID: t.GetRunID(), TaskType: t.GetTaskType(), VisibilityTimestamp: t.GetVisibilityTimestamp(), } if err := timer.Validate(); err != nil { return pagination.Page{}, err } timers = append(timers, timer) } var nextToken interface{} = resp.NextPageToken if len(resp.NextPageToken) == 0 { nextToken = nil } page := pagination.Page{ CurrentToken: token, NextToken: nextToken, Entities: timers, } return page, nil } } ================================================ FILE: common/reconciliation/fetcher/timer_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package fetcher import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" ) func TestTimerIterator(t *testing.T) { ctrl := gomock.NewController(t) retryer := persistence.NewMockRetryer(ctrl) retryer.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Any()). Return(&persistence.GetHistoryTasksResponse{}, nil). Times(1) iterator := TimerIterator( context.Background(), retryer, time.Now(), time.Now(), 10, ) require.NotNil(t, iterator) } func TestGetUserTimers(t *testing.T) { fixedTimestamp, err := time.Parse(time.RFC3339, "2023-12-12T22:08:41Z") if err != nil { t.Fatalf("Failed to parse timestamp: %v", err) } pageSize := 10 minTimestamp := fixedTimestamp.Add(-time.Hour) maxTimestamp := fixedTimestamp nonNilToken := []byte("non-nil-token") testCases := []struct { name string setupMock func(ctrl *gomock.Controller) *persistence.MockRetryer token pagination.PageToken expectedPage pagination.Page expectedError error }{ { name: "Success", setupMock: func(ctrl *gomock.Controller) *persistence.MockRetryer { mockRetryer := persistence.NewMockRetryer(ctrl) timerTasks := []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ VisibilityTimestamp: fixedTimestamp, }, }, } mockRetryer.EXPECT(). GetHistoryTasks(gomock.Any(), &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(minTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxTimestamp, 0), PageSize: pageSize, NextPageToken: nil, }). Return(&persistence.GetHistoryTasksResponse{ Tasks: timerTasks, NextPageToken: nil, }, nil) mockRetryer.EXPECT().GetShardID().Return(123) return mockRetryer }, token: nil, expectedPage: pagination.Page{ Entities: []pagination.Entity{ &entity.Timer{ ShardID: 123, DomainID: "testDomainID", WorkflowID: "testWorkflowID", RunID: "testRunID", TaskType: persistence.TaskTypeUserTimer, VisibilityTimestamp: fixedTimestamp, }, }, }, expectedError: nil, }, { name: "Non-nil Pagination Token Provided", setupMock: func(ctrl *gomock.Controller) *persistence.MockRetryer { mockRetryer := persistence.NewMockRetryer(ctrl) mockRetryer.EXPECT(). GetHistoryTasks(gomock.Any(), &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(minTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxTimestamp, 0), PageSize: pageSize, NextPageToken: nonNilToken, }). Return(&persistence.GetHistoryTasksResponse{ Tasks: nil, NextPageToken: nonNilToken, }, nil) return mockRetryer }, token: nonNilToken, expectedPage: pagination.Page{ Entities: nil, CurrentToken: nonNilToken, NextToken: nonNilToken, }, expectedError: nil, }, { name: "Invalid Timer Causes Error", setupMock: func(ctrl *gomock.Controller) *persistence.MockRetryer { mockRetryer := persistence.NewMockRetryer(ctrl) invalidTimer := &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "", // Invalid as it's empty WorkflowID: "testWorkflowID", RunID: "testRunID", }, TaskData: persistence.TaskData{ VisibilityTimestamp: fixedTimestamp, }, } mockRetryer.EXPECT(). GetHistoryTasks(gomock.Any(), &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(minTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxTimestamp, 0), PageSize: pageSize, NextPageToken: nil, }). Return(&persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{invalidTimer}, NextPageToken: nil, }, nil) mockRetryer.EXPECT().GetShardID().Return(123) return mockRetryer }, token: nil, expectedPage: pagination.Page{}, expectedError: fmt.Errorf("empty DomainID"), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockRetryer := tc.setupMock(ctrl) fetchFn := getUserTimers(mockRetryer, minTimestamp, maxTimestamp, pageSize) page, err := fetchFn(context.Background(), tc.token) if tc.expectedError != nil { require.Error(t, err) require.EqualError(t, err, tc.expectedError.Error(), "Error should match") } else { require.NoError(t, err, "No error is expected") } require.Equal(t, tc.expectedPage, page, "Page should match") }) } } ================================================ FILE: common/reconciliation/fetcher/types.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package fetcher // ExecutionRequest is used to fetch execution from persistence type ExecutionRequest struct { DomainID string WorkflowID string RunID string DomainName string } ================================================ FILE: common/reconciliation/invariant/collection_enumer_generated.go ================================================ // Code generated by "enumer -type=Collection -output collection_enumer_generated.go"; DO NOT EDIT. package invariant import ( "fmt" "strings" ) const _CollectionName = "CollectionMutableStateCollectionHistoryCollectionDomainCollectionStale" var _CollectionIndex = [...]uint8{0, 22, 39, 55, 70} const _CollectionLowerName = "collectionmutablestatecollectionhistorycollectiondomaincollectionstale" func (i Collection) String() string { if i < 0 || i >= Collection(len(_CollectionIndex)-1) { return fmt.Sprintf("Collection(%d)", i) } return _CollectionName[_CollectionIndex[i]:_CollectionIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _CollectionNoOp() { var x [1]struct{} _ = x[CollectionMutableState-(0)] _ = x[CollectionHistory-(1)] _ = x[CollectionDomain-(2)] _ = x[CollectionStale-(3)] } var _CollectionValues = []Collection{CollectionMutableState, CollectionHistory, CollectionDomain, CollectionStale} var _CollectionNameToValueMap = map[string]Collection{ _CollectionName[0:22]: CollectionMutableState, _CollectionLowerName[0:22]: CollectionMutableState, _CollectionName[22:39]: CollectionHistory, _CollectionLowerName[22:39]: CollectionHistory, _CollectionName[39:55]: CollectionDomain, _CollectionLowerName[39:55]: CollectionDomain, _CollectionName[55:70]: CollectionStale, _CollectionLowerName[55:70]: CollectionStale, } var _CollectionNames = []string{ _CollectionName[0:22], _CollectionName[22:39], _CollectionName[39:55], _CollectionName[55:70], } // CollectionString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func CollectionString(s string) (Collection, error) { if val, ok := _CollectionNameToValueMap[s]; ok { return val, nil } if val, ok := _CollectionNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to Collection values", s) } // CollectionValues returns all values of the enum func CollectionValues() []Collection { return _CollectionValues } // CollectionStrings returns a slice of all String values of the enum func CollectionStrings() []string { strs := make([]string, len(_CollectionNames)) copy(strs, _CollectionNames) return strs } // IsACollection returns "true" if the value is listed in the enum definition. "false" otherwise func (i Collection) IsACollection() bool { for _, v := range _CollectionValues { if i == v { return true } } return false } ================================================ FILE: common/reconciliation/invariant/concrete_execution_exists.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "fmt" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) type concreteExecutionExists struct { pr persistence.Retryer cache cache.DomainCache } // NewConcreteExecutionExists returns a new invariant for checking concrete execution func NewConcreteExecutionExists(pr persistence.Retryer, cache cache.DomainCache) Invariant { return &concreteExecutionExists{ pr: pr, cache: cache, } } func (c *concreteExecutionExists) Check(ctx context.Context, execution interface{}) CheckResult { if checkResult := validateCheckContext(ctx, c.Name()); checkResult != nil { return *checkResult } currentExecution, ok := execution.(*entity.CurrentExecution) if !ok { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: c.Name(), Info: "failed to check: expected current execution", } } if len(currentExecution.CurrentRunID) == 0 { // set the current run id var runIDCheckResult *CheckResult currentExecution, runIDCheckResult = c.validateCurrentRunID(ctx, currentExecution) if runIDCheckResult != nil { return *runIDCheckResult } } domainName, err := c.cache.GetDomainName(currentExecution.DomainID) if err != nil { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: c.Name(), Info: "Failed to fetch Domain Name", InfoDetails: err.Error(), } } concreteExecResp, concreteExecErr := c.pr.IsWorkflowExecutionExists(ctx, &persistence.IsWorkflowExecutionExistsRequest{ DomainID: currentExecution.DomainID, DomainName: domainName, WorkflowID: currentExecution.WorkflowID, RunID: currentExecution.CurrentRunID, }) if concreteExecErr != nil { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: c.Name(), Info: "failed to check if concrete execution exists", InfoDetails: concreteExecErr.Error(), } } if !concreteExecResp.Exists { // verify if the current execution exists _, checkResult := c.validateCurrentRunID(ctx, currentExecution) if checkResult != nil { return *checkResult } return CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: c.Name(), Info: "execution is open without having concrete execution", InfoDetails: fmt.Sprintf("concrete execution not found. WorkflowId: %v, RunId: %v", currentExecution.WorkflowID, currentExecution.CurrentRunID), } } return CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: c.Name(), } } func (c *concreteExecutionExists) Fix(ctx context.Context, execution interface{}) FixResult { if fixResult := validateFixContext(ctx, c.Name()); fixResult != nil { return *fixResult } currentExecution, ok := execution.(*entity.CurrentExecution) if !ok { return FixResult{ FixResultType: FixResultTypeFailed, InvariantName: c.Name(), Info: "failed to fix: expected current execution", } } var runIDCheckResult *CheckResult if len(currentExecution.CurrentRunID) == 0 { // this is to set the current run ID prior to the check and fix operations currentExecution, runIDCheckResult = c.validateCurrentRunID(ctx, currentExecution) if runIDCheckResult != nil { return FixResult{ FixResultType: FixResultTypeSkipped, CheckResult: *runIDCheckResult, InvariantName: c.Name(), } } } fixResult, checkResult := checkBeforeFix(ctx, c, currentExecution) if fixResult != nil { return *fixResult } domainName, errorDomain := c.cache.GetDomainName(currentExecution.DomainID) if errorDomain != nil { return FixResult{ FixResultType: FixResultTypeFailed, InvariantName: c.Name(), Info: "failed to fetch Domain Name", InfoDetails: errorDomain.Error(), } } if err := c.pr.DeleteCurrentWorkflowExecution(ctx, &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: currentExecution.DomainID, WorkflowID: currentExecution.WorkflowID, RunID: currentExecution.CurrentRunID, DomainName: domainName, }); err != nil { return FixResult{ FixResultType: FixResultTypeFailed, InvariantName: c.Name(), Info: "failed to delete current workflow execution", InfoDetails: err.Error(), } } return FixResult{ FixResultType: FixResultTypeFixed, CheckResult: *checkResult, InvariantName: c.Name(), } } func (c *concreteExecutionExists) Name() Name { return ConcreteExecutionExists } func (c *concreteExecutionExists) validateCurrentRunID( ctx context.Context, currentExecution *entity.CurrentExecution, ) (*entity.CurrentExecution, *CheckResult) { domainName, err := c.cache.GetDomainName(currentExecution.DomainID) if err != nil { return nil, &CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: c.Name(), Info: "failed to fetch domainName", InfoDetails: err.Error(), } } resp, err := c.pr.GetCurrentExecution(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: currentExecution.DomainID, WorkflowID: currentExecution.WorkflowID, DomainName: domainName, }) if err != nil { switch err.(type) { case *types.EntityNotExistsError: return nil, &CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: c.Name(), Info: "current execution does not exist.", InfoDetails: err.Error(), } default: return nil, &CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: c.Name(), Info: "failed to get current execution.", InfoDetails: err.Error(), } } } if len(currentExecution.CurrentRunID) == 0 { currentExecution.CurrentRunID = resp.RunID } if currentExecution.CurrentRunID != resp.RunID { return nil, &CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: c.Name(), } } return currentExecution, nil } ================================================ FILE: common/reconciliation/invariant/concrete_execution_exists_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "errors" "fmt" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" c "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) func TestConcreteExecutionCheckAndFix(t *testing.T) { notExistsError := types.EntityNotExistsError{} unknownError := types.BadRequestError{} testCases := []struct { desc string execution any ctx context.Context getConcreteResp *persistence.IsWorkflowExecutionExistsResponse getConcreteErr error getCurrentResp *persistence.GetCurrentExecutionResponse getCurrentErr error getDomainNameErr error wantCheckResult CheckResult wantFixResult FixResult }{ { desc: "closed execution with concrete execution", execution: getClosedCurrentExecution(), getConcreteResp: &persistence.IsWorkflowExecutionExistsResponse{Exists: true}, getCurrentResp: &persistence.GetCurrentExecutionResponse{ RunID: getClosedCurrentExecution().CurrentRunID, }, wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: ConcreteExecutionExists, }, wantFixResult: FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: ConcreteExecutionExists, Info: "skipped fix because execution was healthy", }, }, { desc: "failed to get concrete execution", execution: getOpenCurrentExecution(), getConcreteErr: errors.New("error getting concrete execution"), getCurrentResp: &persistence.GetCurrentExecutionResponse{ RunID: getOpenCurrentExecution().CurrentRunID, }, wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: ConcreteExecutionExists, Info: "failed to check if concrete execution exists", InfoDetails: "error getting concrete execution", }, wantFixResult: FixResult{ FixResultType: FixResultTypeFailed, InvariantName: ConcreteExecutionExists, Info: "failed fix because check failed", }, }, { desc: "open execution without concrete execution", execution: getOpenCurrentExecution(), getConcreteResp: &persistence.IsWorkflowExecutionExistsResponse{Exists: false}, getCurrentResp: &persistence.GetCurrentExecutionResponse{ RunID: getOpenCurrentExecution().CurrentRunID, }, wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: ConcreteExecutionExists, Info: "execution is open without having concrete execution", InfoDetails: fmt.Sprintf("concrete execution not found. WorkflowId: %v, RunId: %v", workflowID, currentRunID), }, wantFixResult: FixResult{ FixResultType: FixResultTypeFixed, InvariantName: ConcreteExecutionExists, }, }, { desc: "mismatching current runid and concrete execution doesn't exist", execution: getOpenCurrentExecution(), getConcreteResp: &persistence.IsWorkflowExecutionExistsResponse{Exists: false}, getCurrentResp: &persistence.GetCurrentExecutionResponse{ RunID: uuid.New(), }, wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: ConcreteExecutionExists, }, wantFixResult: FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: ConcreteExecutionExists, Info: "skipped fix because execution was healthy", }, }, { desc: "open execution with concrete execution", execution: getOpenCurrentExecution(), getConcreteErr: nil, getConcreteResp: &persistence.IsWorkflowExecutionExistsResponse{Exists: true}, getCurrentResp: &persistence.GetCurrentExecutionResponse{ RunID: getOpenCurrentExecution().CurrentRunID, }, wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: ConcreteExecutionExists, }, wantFixResult: FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: ConcreteExecutionExists, Info: "skipped fix because execution was healthy", }, }, { desc: "open execution that is not current", execution: getOpenCurrentExecution(), getConcreteErr: nil, getConcreteResp: &persistence.IsWorkflowExecutionExistsResponse{Exists: true}, getCurrentResp: &persistence.GetCurrentExecutionResponse{ RunID: uuid.New(), }, wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: ConcreteExecutionExists, }, wantFixResult: FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: ConcreteExecutionExists, Info: "skipped fix because execution was healthy", }, }, { desc: "concrete exists but current doesn't", execution: getOpenCurrentExecution(), getConcreteErr: nil, getConcreteResp: &persistence.IsWorkflowExecutionExistsResponse{Exists: true}, getCurrentResp: nil, getCurrentErr: ¬ExistsError, wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: ConcreteExecutionExists, }, wantFixResult: FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: ConcreteExecutionExists, Info: "skipped fix because execution was healthy", }, }, { desc: "concrete exists but failed to get current", execution: getOpenCurrentExecution(), getConcreteErr: nil, getConcreteResp: &persistence.IsWorkflowExecutionExistsResponse{Exists: false}, getCurrentResp: nil, getCurrentErr: &unknownError, wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: ConcreteExecutionExists, Info: "failed to get current execution.", InfoDetails: unknownError.Error(), }, wantFixResult: FixResult{ FixResultType: FixResultTypeFailed, InvariantName: ConcreteExecutionExists, Info: "failed fix because check failed", }, }, { desc: "canceled context", ctx: canceledCtx(), wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: ConcreteExecutionExists, Info: "failed to check: context expired or cancelled", InfoDetails: "context canceled", }, wantFixResult: FixResult{ FixResultType: FixResultTypeFailed, InvariantName: ConcreteExecutionExists, Info: "failed to check: context expired or cancelled", InfoDetails: "context canceled", }, }, { desc: "invalid execution object", execution: &entity.ConcreteExecution{}, // invalid type wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: ConcreteExecutionExists, Info: "failed to check: expected current execution", }, wantFixResult: FixResult{ FixResultType: FixResultTypeFailed, InvariantName: ConcreteExecutionExists, Info: "failed to fix: expected current execution", }, }, { desc: "empty run id - found after current lookup", execution: func() *entity.CurrentExecution { e := getOpenCurrentExecution() e.CurrentRunID = "" return e }(), getConcreteResp: &persistence.IsWorkflowExecutionExistsResponse{Exists: true}, getCurrentResp: &persistence.GetCurrentExecutionResponse{ RunID: currentRunID, }, wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: ConcreteExecutionExists, }, wantFixResult: FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: ConcreteExecutionExists, Info: "skipped fix because execution was healthy", }, }, { desc: "empty run id - current lookup returns not found", execution: func() *entity.CurrentExecution { e := getOpenCurrentExecution() e.CurrentRunID = "" return e }(), getCurrentErr: ¬ExistsError, wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: ConcreteExecutionExists, Info: "current execution does not exist.", }, wantFixResult: FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: ConcreteExecutionExists, }, }, { desc: "empty run id - domain name lookup failed", execution: func() *entity.CurrentExecution { e := getOpenCurrentExecution() e.CurrentRunID = "" return e }(), getDomainNameErr: errors.New("error getting domain name"), wantCheckResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: ConcreteExecutionExists, Info: "failed to fetch domainName", InfoDetails: "error getting domain name", }, wantFixResult: FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: ConcreteExecutionExists, }, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(ctrl) execManager := &mocks.ExecutionManager{} execManager.On("IsWorkflowExecutionExists", mock.Anything, mock.Anything).Return(tc.getConcreteResp, tc.getConcreteErr) execManager.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(tc.getCurrentResp, tc.getCurrentErr) execManager.On("DeleteCurrentWorkflowExecution", mock.Anything, mock.Anything).Return(nil) mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, tc.getDomainNameErr).AnyTimes() o := NewConcreteExecutionExists(persistence.NewPersistenceRetryer(execManager, nil, c.CreatePersistenceRetryPolicy()), mockDomainCache) ctx := tc.ctx if ctx == nil { ctx = context.Background() } require.Equal(t, tc.wantCheckResult, o.Check(ctx, tc.execution)) gotFixResult := o.Fix(ctx, tc.execution) gotFixResult.CheckResult = CheckResult{} require.Equal(t, tc.wantFixResult, gotFixResult) }) } } func canceledCtx() context.Context { ctx, cancel := context.WithCancel(context.Background()) cancel() return ctx } ================================================ FILE: common/reconciliation/invariant/history_exists.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" c "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) const ( historyPageSize = 1 ) type ( historyExists struct { pr persistence.Retryer dc cache.DomainCache } ) func NewHistoryExists( pr persistence.Retryer, dc cache.DomainCache, ) Invariant { return &historyExists{ pr: pr, dc: dc, } } func (h *historyExists) Check( ctx context.Context, execution interface{}, ) CheckResult { if checkResult := validateCheckContext(ctx, h.Name()); checkResult != nil { return *checkResult } concreteExecution, ok := execution.(*entity.ConcreteExecution) if !ok { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: h.Name(), Info: "failed to check: expected concrete execution", } } domainID := concreteExecution.GetDomainID() domainName, errorDomainName := h.dc.GetDomainName(domainID) if errorDomainName != nil { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: h.Name(), Info: "failed to check: expected DomainName", InfoDetails: errorDomainName.Error(), } } readHistoryBranchReq := &persistence.ReadHistoryBranchRequest{ BranchToken: concreteExecution.BranchToken, MinEventID: constants.FirstEventID, MaxEventID: constants.FirstEventID + 1, PageSize: historyPageSize, NextPageToken: nil, ShardID: c.IntPtr(concreteExecution.ShardID), DomainName: domainName, } readHistoryBranchResp, readHistoryBranchErr := h.pr.ReadHistoryBranch(ctx, readHistoryBranchReq) stillExists, existsCheckError := ExecutionStillExists(ctx, &concreteExecution.Execution, h.pr, h.dc) if existsCheckError != nil { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: h.Name(), Info: "failed to check if concrete execution still exists", InfoDetails: existsCheckError.Error(), } } if !stillExists { return CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: h.Name(), Info: "determined execution was healthy because concrete execution no longer exists", } } if readHistoryBranchErr != nil { switch readHistoryBranchErr.(type) { case *types.EntityNotExistsError: return CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: h.Name(), Info: "concrete execution exists but history does not exist", InfoDetails: readHistoryBranchErr.Error(), } default: return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: h.Name(), Info: "failed to verify if history exists", InfoDetails: readHistoryBranchErr.Error(), } } } if readHistoryBranchResp == nil || len(readHistoryBranchResp.HistoryEvents) == 0 { return CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: h.Name(), Info: "concrete execution exists but got empty history", } } return CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: h.Name(), } } func (h *historyExists) Fix( ctx context.Context, execution interface{}, ) FixResult { if fixResult := validateFixContext(ctx, h.Name()); fixResult != nil { return *fixResult } fixResult, checkResult := checkBeforeFix(ctx, h, execution) if fixResult != nil { return *fixResult } fixResult = DeleteExecution(ctx, execution, h.pr, h.dc) fixResult.CheckResult = *checkResult fixResult.InvariantName = h.Name() return *fixResult } func (h *historyExists) Name() Name { return HistoryExists } // ExecutionStillExists returns true if execution still exists in persistence, false otherwise. // Returns error on failure to confirm. func ExecutionStillExists( ctx context.Context, exec *entity.Execution, pr persistence.Retryer, dc cache.DomainCache, ) (bool, error) { domainName, errorDomainName := dc.GetDomainName(exec.DomainID) if errorDomainName != nil { return false, errorDomainName } req := &persistence.GetWorkflowExecutionRequest{ DomainID: exec.DomainID, Execution: types.WorkflowExecution{ WorkflowID: exec.WorkflowID, RunID: exec.RunID, }, DomainName: domainName, } _, err := pr.GetWorkflowExecution(ctx, req) if err == nil { return true, nil } switch err.(type) { case *types.EntityNotExistsError: return false, nil default: return false, err } } ================================================ FILE: common/reconciliation/invariant/history_exists_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "errors" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" c2 "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type HistoryExistsSuite struct { *require.Assertions suite.Suite } func TestHistoryExistsSuite(t *testing.T) { suite.Run(t, new(HistoryExistsSuite)) } func (s *HistoryExistsSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *HistoryExistsSuite) TestCheck() { testCases := []struct { getExecErr error getExecResp *persistence.GetWorkflowExecutionResponse getHistoryErr error getHistoryResp *persistence.ReadHistoryBranchResponse expectedResult CheckResult expectedResourcePopulated bool }{ { getExecErr: errors.New("got error checking workflow exists"), getHistoryResp: &persistence.ReadHistoryBranchResponse{}, expectedResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: HistoryExists, Info: "failed to check if concrete execution still exists", InfoDetails: "got error checking workflow exists", }, expectedResourcePopulated: false, }, { getExecErr: &types.EntityNotExistsError{}, getHistoryResp: &persistence.ReadHistoryBranchResponse{}, expectedResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: HistoryExists, Info: "determined execution was healthy because concrete execution no longer exists", }, expectedResourcePopulated: false, }, { getExecResp: &persistence.GetWorkflowExecutionResponse{}, getHistoryResp: nil, getHistoryErr: &types.EntityNotExistsError{Message: "got entity not exists error"}, expectedResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: HistoryExists, Info: "concrete execution exists but history does not exist", InfoDetails: "got entity not exists error", }, expectedResourcePopulated: false, }, { getExecResp: &persistence.GetWorkflowExecutionResponse{}, getHistoryResp: nil, getHistoryErr: errors.New("error fetching history"), expectedResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: HistoryExists, Info: "failed to verify if history exists", InfoDetails: "error fetching history", }, expectedResourcePopulated: false, }, { getExecResp: &persistence.GetWorkflowExecutionResponse{}, getHistoryResp: nil, expectedResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: HistoryExists, Info: "concrete execution exists but got empty history", }, expectedResourcePopulated: false, }, { getExecResp: &persistence.GetWorkflowExecutionResponse{}, getHistoryResp: &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ {}, }, }, expectedResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: HistoryExists, }, expectedResourcePopulated: true, }, } ctrl := gomock.NewController(s.T()) domainCache := cache.NewMockDomainCache(ctrl) for _, tc := range testCases { execManager := &mocks.ExecutionManager{} historyManager := &mocks.HistoryV2Manager{} execManager.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(tc.getExecResp, tc.getExecErr) historyManager.On("ReadHistoryBranch", mock.Anything, mock.Anything).Return(tc.getHistoryResp, tc.getHistoryErr) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-name", nil).AnyTimes() i := NewHistoryExists(persistence.NewPersistenceRetryer(execManager, historyManager, c2.CreatePersistenceRetryPolicy()), domainCache) result := i.Check(context.Background(), getOpenConcreteExecution()) s.Equal(tc.expectedResult, result) } } ================================================ FILE: common/reconciliation/invariant/inactive_domain_exists.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" ) type ( inactiveDomainExists struct { pr persistence.Retryer dc cache.DomainCache } ) func NewInactiveDomainExists( pr persistence.Retryer, dc cache.DomainCache, ) Invariant { return &inactiveDomainExists{ pr: pr, dc: dc, } } func (idc *inactiveDomainExists) Check( ctx context.Context, execution interface{}, ) CheckResult { if checkResult := validateCheckContext(ctx, idc.Name()); checkResult != nil { return *checkResult } concreteExecution, ok := execution.(*entity.ConcreteExecution) if !ok { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: idc.Name(), Info: "failed to check: expected concrete execution", } } domainID := concreteExecution.GetDomainID() domain, err := idc.dc.GetDomainByID(domainID) if err != nil { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: idc.Name(), Info: "failed to check: expected Domain", InfoDetails: err.Error(), } } if domain.GetInfo().Status == persistence.DomainStatusDeprecated || domain.GetInfo().Status == persistence.DomainStatusDeleted { return CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: idc.Name(), Info: "failed check: domain is not active", InfoDetails: "The domain has been deprecated or deleted", } } return CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: idc.Name(), Info: "workflow's domain is active", InfoDetails: "workflow's domain is active", } } func (idc *inactiveDomainExists) Fix( ctx context.Context, execution interface{}, ) FixResult { if fixResult := validateFixContext(ctx, idc.Name()); fixResult != nil { return *fixResult } fixResult, checkResult := checkBeforeFix(ctx, idc, execution) if fixResult != nil { return *fixResult } fixResult = DeleteExecution(ctx, execution, idc.pr, idc.dc) fixResult.CheckResult = *checkResult fixResult.InvariantName = idc.Name() return *fixResult } func (idc *inactiveDomainExists) Name() Name { return InactiveDomainExists } ================================================ FILE: common/reconciliation/invariant/inactive_domain_exists_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" c2 "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" ) type InactiveInactiveDomainExistsSuite struct { *require.Assertions suite.Suite } func TestInactiveInactiveDomainExistsSuite(t *testing.T) { suite.Run(t, new(InactiveInactiveDomainExistsSuite)) } func (s *InactiveInactiveDomainExistsSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *InactiveInactiveDomainExistsSuite) TestCheck() { testCases := []struct { getExecErr error getExecResp *persistence.GetWorkflowExecutionResponse getDomainErr error expectedResult CheckResult }{ { getExecErr: &types.EntityNotExistsError{}, expectedResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: InactiveDomainExists, Info: "workflow's domain is active", InfoDetails: "workflow's domain is active", }, }, } ctrl := gomock.NewController(s.T()) domainCache := cache.NewMockDomainCache(ctrl) for _, tc := range testCases { execManager := &mocks.ExecutionManager{} execManager.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(tc.getExecResp, tc.getExecErr) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-name", nil).AnyTimes() domainCache.EXPECT().GetDomainByID(gomock.Any()).Return(constants.TestGlobalDomainEntry, nil).AnyTimes() i := NewInactiveDomainExists(persistence.NewPersistenceRetryer(execManager, nil, c2.CreatePersistenceRetryPolicy()), domainCache) result := i.Check(context.Background(), getOpenConcreteExecution()) s.Equal(tc.expectedResult, result) } } ================================================ FILE: common/reconciliation/invariant/invariant_manager.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import "context" type ( invariantManager struct { invariants []Invariant } ) // NewInvariantManager handles running a collection of invariants according to the invariant collection provided. func NewInvariantManager( invariants []Invariant, ) Manager { return &invariantManager{ invariants: invariants, } } // RunChecks runs all enabled checks. func (i *invariantManager) RunChecks( ctx context.Context, execution interface{}, ) ManagerCheckResult { result := ManagerCheckResult{ CheckResultType: CheckResultTypeHealthy, DeterminingInvariantType: nil, CheckResults: nil, } for _, iv := range i.invariants { checkResult := iv.Check(ctx, execution) result.CheckResults = append(result.CheckResults, checkResult) checkResultType, updated := i.nextCheckResultType(result.CheckResultType, checkResult.CheckResultType) result.CheckResultType = checkResultType if updated { result.DeterminingInvariantType = &checkResult.InvariantName } } return result } // RunFixes runs all enabled fixes. func (i *invariantManager) RunFixes( ctx context.Context, execution interface{}) ManagerFixResult { result := ManagerFixResult{ FixResultType: FixResultTypeSkipped, DeterminingInvariantName: nil, FixResults: nil, } for _, iv := range i.invariants { fixResult := iv.Fix(ctx, execution) result.FixResults = append(result.FixResults, fixResult) fixResultType, updated := i.nextFixResultType(result.FixResultType, fixResult.FixResultType) result.FixResultType = fixResultType if updated { result.DeterminingInvariantName = &fixResult.InvariantName } } return result } func (i *invariantManager) nextFixResultType( currentState FixResultType, event FixResultType, ) (FixResultType, bool) { switch currentState { case FixResultTypeSkipped: return event, event != FixResultTypeSkipped case FixResultTypeFixed: if event == FixResultTypeFailed { return event, true } return currentState, false case FixResultTypeFailed: return currentState, false default: panic("unknown FixResultType") } } func (i *invariantManager) nextCheckResultType( currentState CheckResultType, event CheckResultType, ) (CheckResultType, bool) { switch currentState { case CheckResultTypeHealthy: return event, event != CheckResultTypeHealthy case CheckResultTypeCorrupted: if event == CheckResultTypeFailed { return event, true } return currentState, false case CheckResultTypeFailed: return currentState, false default: panic("unknown CheckResultType") } } ================================================ FILE: common/reconciliation/invariant/invariant_manager_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/reconciliation/entity" ) type InvariantManagerSuite struct { *require.Assertions suite.Suite controller *gomock.Controller } func TestInvariantManagerSuite(t *testing.T) { suite.Run(t, new(InvariantManagerSuite)) } func (s *InvariantManagerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) } func (s *InvariantManagerSuite) TearDownTest() { s.controller.Finish() } func (s *InvariantManagerSuite) TestRunChecks() { testCases := []struct { checkResults []CheckResult expected ManagerCheckResult }{ { checkResults: nil, expected: ManagerCheckResult{ CheckResultType: CheckResultTypeHealthy, CheckResults: nil, }, }, { checkResults: []CheckResult{ { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("first"), Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { CheckResultType: CheckResultTypeFailed, InvariantName: Name("second"), Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, }, expected: ManagerCheckResult{ CheckResultType: CheckResultTypeFailed, DeterminingInvariantType: NamePtr("second"), CheckResults: []CheckResult{ { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("first"), Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { CheckResultType: CheckResultTypeFailed, InvariantName: Name("second"), Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, }, }, }, { checkResults: []CheckResult{ { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("first"), Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { CheckResultType: CheckResultTypeCorrupted, InvariantName: Name("second"), Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, }, expected: ManagerCheckResult{ CheckResultType: CheckResultTypeCorrupted, DeterminingInvariantType: NamePtr("second"), CheckResults: []CheckResult{ { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("first"), Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { CheckResultType: CheckResultTypeCorrupted, InvariantName: Name("second"), Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, }, }, }, { checkResults: []CheckResult{ { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("first"), Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("second"), Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, }, expected: ManagerCheckResult{ CheckResultType: CheckResultTypeHealthy, DeterminingInvariantType: nil, CheckResults: []CheckResult{ { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("first"), Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("second"), Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, }, }, }, { checkResults: []CheckResult{ { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("first"), Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { CheckResultType: CheckResultTypeCorrupted, InvariantName: Name("second"), Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, { CheckResultType: CheckResultTypeFailed, InvariantName: Name("third"), Info: "invariant 3 info", InfoDetails: "invariant 3 info details", }, { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("forth"), Info: "invariant 4 info", InfoDetails: "invariant 4 info details", }, }, expected: ManagerCheckResult{ CheckResultType: CheckResultTypeFailed, DeterminingInvariantType: NamePtr("third"), CheckResults: []CheckResult{ { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("first"), Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { CheckResultType: CheckResultTypeCorrupted, InvariantName: Name("second"), Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, { CheckResultType: CheckResultTypeFailed, InvariantName: Name("third"), Info: "invariant 3 info", InfoDetails: "invariant 3 info details", }, { CheckResultType: CheckResultTypeHealthy, InvariantName: Name("forth"), Info: "invariant 4 info", InfoDetails: "invariant 4 info details", }, }, }, }, } for _, tc := range testCases { invariants := make([]Invariant, len(tc.checkResults)) for i := 0; i < len(tc.checkResults); i++ { mockInvariant := NewMockInvariant(s.controller) mockInvariant.EXPECT().Check(gomock.Any(), gomock.Any()).Return(tc.checkResults[i]) invariants[i] = mockInvariant } manager := &invariantManager{ invariants: invariants, } s.Equal(tc.expected, manager.RunChecks(context.Background(), entity.Execution{})) } } func (s *InvariantManagerSuite) TestRunFixes() { testCases := []struct { fixResults []FixResult expected ManagerFixResult }{ { fixResults: nil, expected: ManagerFixResult{ FixResultType: FixResultTypeSkipped, DeterminingInvariantName: nil, FixResults: nil, }, }, { fixResults: []FixResult{ { FixResultType: FixResultTypeFixed, InvariantName: Name("first"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 1 check info", InfoDetails: "invariant 1 check info details", }, Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { FixResultType: FixResultTypeFailed, InvariantName: Name("second"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 2 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, }, expected: ManagerFixResult{ FixResultType: FixResultTypeFailed, DeterminingInvariantName: NamePtr("second"), FixResults: []FixResult{ { FixResultType: FixResultTypeFixed, InvariantName: Name("first"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 1 check info", InfoDetails: "invariant 1 check info details", }, Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { FixResultType: FixResultTypeFailed, InvariantName: Name("second"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 2 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, }, }, }, { fixResults: []FixResult{ { FixResultType: FixResultTypeSkipped, InvariantName: Name("first"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 1 check info", InfoDetails: "invariant 1 check info details", }, Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { FixResultType: FixResultTypeSkipped, InvariantName: Name("second"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 2 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, }, expected: ManagerFixResult{ FixResultType: FixResultTypeSkipped, DeterminingInvariantName: nil, FixResults: []FixResult{ { FixResultType: FixResultTypeSkipped, InvariantName: Name("first"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 1 check info", InfoDetails: "invariant 1 check info details", }, Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { FixResultType: FixResultTypeSkipped, InvariantName: Name("second"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 2 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, }, }, }, { fixResults: []FixResult{ { FixResultType: FixResultTypeSkipped, InvariantName: Name("first"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 1 check info", InfoDetails: "invariant 1 check info details", }, Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { FixResultType: FixResultTypeFixed, InvariantName: Name("second"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 2 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, { FixResultType: FixResultTypeSkipped, InvariantName: Name("third"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 3 check info", InfoDetails: "invariant 3 check info details", }, Info: "invariant 3 info", InfoDetails: "invariant 3 info details", }, }, expected: ManagerFixResult{ FixResultType: FixResultTypeFixed, DeterminingInvariantName: NamePtr("second"), FixResults: []FixResult{ { FixResultType: FixResultTypeSkipped, InvariantName: Name("first"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 1 check info", InfoDetails: "invariant 1 check info details", }, Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { FixResultType: FixResultTypeFixed, InvariantName: Name("second"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 2 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, { FixResultType: FixResultTypeSkipped, InvariantName: Name("third"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 3 check info", InfoDetails: "invariant 3 check info details", }, Info: "invariant 3 info", InfoDetails: "invariant 3 info details", }, }, }, }, { fixResults: []FixResult{ { FixResultType: FixResultTypeSkipped, InvariantName: Name("first"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 1 check info", InfoDetails: "invariant 1 check info details", }, Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { FixResultType: FixResultTypeFixed, InvariantName: Name("second"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 2 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, { FixResultType: FixResultTypeSkipped, InvariantName: Name("third"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 3 check info", InfoDetails: "invariant 3 check info details", }, Info: "invariant 3 info", InfoDetails: "invariant 3 info details", }, { FixResultType: FixResultTypeFailed, InvariantName: Name("forth"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 4 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 4 info", InfoDetails: "invariant 4 info details", }, }, expected: ManagerFixResult{ FixResultType: FixResultTypeFailed, DeterminingInvariantName: NamePtr("forth"), FixResults: []FixResult{ { FixResultType: FixResultTypeSkipped, InvariantName: Name("first"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 1 check info", InfoDetails: "invariant 1 check info details", }, Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { FixResultType: FixResultTypeFixed, InvariantName: Name("second"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 2 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, { FixResultType: FixResultTypeSkipped, InvariantName: Name("third"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 3 check info", InfoDetails: "invariant 3 check info details", }, Info: "invariant 3 info", InfoDetails: "invariant 3 info details", }, { FixResultType: FixResultTypeFailed, InvariantName: Name("forth"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 4 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 4 info", InfoDetails: "invariant 4 info details", }, }, }, }, { fixResults: []FixResult{ { FixResultType: FixResultTypeSkipped, InvariantName: Name("first"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 1 check info", InfoDetails: "invariant 1 check info details", }, Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { FixResultType: FixResultTypeFailed, InvariantName: Name("second"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 4 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 4 info", InfoDetails: "invariant 4 info details", }, { FixResultType: FixResultTypeFixed, InvariantName: Name("third"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 2 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, { FixResultType: FixResultTypeSkipped, InvariantName: Name("forth"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 3 check info", InfoDetails: "invariant 3 check info details", }, Info: "invariant 3 info", InfoDetails: "invariant 3 info details", }, }, expected: ManagerFixResult{ FixResultType: FixResultTypeFailed, DeterminingInvariantName: NamePtr("second"), FixResults: []FixResult{ { FixResultType: FixResultTypeSkipped, InvariantName: Name("first"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 1 check info", InfoDetails: "invariant 1 check info details", }, Info: "invariant 1 info", InfoDetails: "invariant 1 info details", }, { FixResultType: FixResultTypeFailed, InvariantName: Name("second"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 4 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 4 info", InfoDetails: "invariant 4 info details", }, { FixResultType: FixResultTypeFixed, InvariantName: Name("third"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, Info: "invariant 2 check info", InfoDetails: "invariant 2 check info details", }, Info: "invariant 2 info", InfoDetails: "invariant 2 info details", }, { FixResultType: FixResultTypeSkipped, InvariantName: Name("forth"), CheckResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, Info: "invariant 3 check info", InfoDetails: "invariant 3 check info details", }, Info: "invariant 3 info", InfoDetails: "invariant 3 info details", }, }, }, }, } for _, tc := range testCases { invariants := make([]Invariant, len(tc.fixResults)) for i := 0; i < len(tc.fixResults); i++ { mockInvariant := NewMockInvariant(s.controller) mockInvariant.EXPECT().Fix(gomock.Any(), gomock.Any()).Return(tc.fixResults[i]) invariants[i] = mockInvariant } manager := &invariantManager{ invariants: invariants, } s.Equal(tc.expected, manager.RunFixes(context.Background(), entity.Execution{})) } } ================================================ FILE: common/reconciliation/invariant/invariant_test_utils.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" ) const ( domainID = "test-domain-id" domainName = "test-domain-name" workflowID = "test-workflow-id" runID = "test-run-id" shardID = 0 treeID = "test-tree-id" branchID = "test-branch-id" openState = persistence.WorkflowStateCreated closedState = persistence.WorkflowStateCompleted currentRunID = "test-current-run-id" ) var ( branchToken = []byte{1, 2, 3} ) func getOpenConcreteExecution() *entity.ConcreteExecution { return &entity.ConcreteExecution{ Execution: entity.Execution{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: openState, }, BranchToken: branchToken, TreeID: treeID, BranchID: branchID, } } func getClosedConcreteExecution() *entity.ConcreteExecution { return &entity.ConcreteExecution{ Execution: entity.Execution{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: closedState, }, BranchToken: branchToken, TreeID: treeID, BranchID: branchID, } } func getOpenCurrentExecution() *entity.CurrentExecution { return &entity.CurrentExecution{ Execution: entity.Execution{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: openState, }, CurrentRunID: currentRunID, } } func getClosedCurrentExecution() *entity.CurrentExecution { return &entity.CurrentExecution{ Execution: entity.Execution{ ShardID: shardID, DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: closedState, }, CurrentRunID: currentRunID, } } ================================================ FILE: common/reconciliation/invariant/mocks.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: types.go // // Generated by this command: // // mockgen -package invariant -source types.go -destination mocks.go -self_package github.com/uber/cadence/common/reconciliation/invariant // // Package invariant is a generated GoMock package. package invariant import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockInvariant is a mock of Invariant interface. type MockInvariant struct { ctrl *gomock.Controller recorder *MockInvariantMockRecorder isgomock struct{} } // MockInvariantMockRecorder is the mock recorder for MockInvariant. type MockInvariantMockRecorder struct { mock *MockInvariant } // NewMockInvariant creates a new mock instance. func NewMockInvariant(ctrl *gomock.Controller) *MockInvariant { mock := &MockInvariant{ctrl: ctrl} mock.recorder = &MockInvariantMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockInvariant) EXPECT() *MockInvariantMockRecorder { return m.recorder } // Check mocks base method. func (m *MockInvariant) Check(arg0 context.Context, arg1 any) CheckResult { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Check", arg0, arg1) ret0, _ := ret[0].(CheckResult) return ret0 } // Check indicates an expected call of Check. func (mr *MockInvariantMockRecorder) Check(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Check", reflect.TypeOf((*MockInvariant)(nil).Check), arg0, arg1) } // Fix mocks base method. func (m *MockInvariant) Fix(arg0 context.Context, arg1 any) FixResult { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Fix", arg0, arg1) ret0, _ := ret[0].(FixResult) return ret0 } // Fix indicates an expected call of Fix. func (mr *MockInvariantMockRecorder) Fix(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fix", reflect.TypeOf((*MockInvariant)(nil).Fix), arg0, arg1) } // Name mocks base method. func (m *MockInvariant) Name() Name { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Name") ret0, _ := ret[0].(Name) return ret0 } // Name indicates an expected call of Name. func (mr *MockInvariantMockRecorder) Name() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockInvariant)(nil).Name)) } // MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller recorder *MockManagerMockRecorder isgomock struct{} } // MockManagerMockRecorder is the mock recorder for MockManager. type MockManagerMockRecorder struct { mock *MockManager } // NewMockManager creates a new mock instance. func NewMockManager(ctrl *gomock.Controller) *MockManager { mock := &MockManager{ctrl: ctrl} mock.recorder = &MockManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockManager) EXPECT() *MockManagerMockRecorder { return m.recorder } // RunChecks mocks base method. func (m *MockManager) RunChecks(arg0 context.Context, arg1 any) ManagerCheckResult { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RunChecks", arg0, arg1) ret0, _ := ret[0].(ManagerCheckResult) return ret0 } // RunChecks indicates an expected call of RunChecks. func (mr *MockManagerMockRecorder) RunChecks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunChecks", reflect.TypeOf((*MockManager)(nil).RunChecks), arg0, arg1) } // RunFixes mocks base method. func (m *MockManager) RunFixes(arg0 context.Context, arg1 any) ManagerFixResult { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RunFixes", arg0, arg1) ret0, _ := ret[0].(ManagerFixResult) return ret0 } // RunFixes indicates an expected call of RunFixes. func (mr *MockManagerMockRecorder) RunFixes(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunFixes", reflect.TypeOf((*MockManager)(nil).RunFixes), arg0, arg1) } ================================================ FILE: common/reconciliation/invariant/open_current_execution.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "fmt" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) type ( openCurrentExecution struct { pr persistence.Retryer dc cache.DomainCache } ) // NewOpenCurrentExecution returns a new invariant for checking open current execution func NewOpenCurrentExecution( pr persistence.Retryer, dc cache.DomainCache, ) Invariant { return &openCurrentExecution{ pr: pr, dc: dc, } } func (o *openCurrentExecution) Check( ctx context.Context, execution interface{}, ) CheckResult { if checkResult := validateCheckContext(ctx, o.Name()); checkResult != nil { return *checkResult } concreteExecution, ok := execution.(*entity.ConcreteExecution) if !ok { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: o.Name(), Info: "failed to check: expected concrete execution", } } if !Open(concreteExecution.State) { return CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: o.Name(), } } domainName, err := o.dc.GetDomainName(concreteExecution.DomainID) if err != nil { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: o.Name(), Info: "failed to fetch Domain Name", InfoDetails: err.Error(), } } currentExecResp, currentExecErr := o.pr.GetCurrentExecution(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: concreteExecution.DomainID, WorkflowID: concreteExecution.WorkflowID, DomainName: domainName, }) stillOpen, stillOpenErr := ExecutionStillOpen(ctx, &concreteExecution.Execution, o.pr, o.dc) if stillOpenErr != nil { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: o.Name(), Info: "failed to check if concrete execution is still open", InfoDetails: stillOpenErr.Error(), } } if !stillOpen { return CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: o.Name(), } } if currentExecErr != nil { switch currentExecErr.(type) { case *types.EntityNotExistsError: return CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: o.Name(), Info: "execution is open without having current execution", InfoDetails: currentExecErr.Error(), } default: return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: o.Name(), Info: "failed to check if current execution exists", InfoDetails: currentExecErr.Error(), } } } if currentExecResp.RunID != concreteExecution.RunID { return CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: o.Name(), Info: "execution is open but current points at a different execution", InfoDetails: fmt.Sprintf("current points at %v", currentExecResp.RunID), } } return CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: o.Name(), } } func (o *openCurrentExecution) Fix( ctx context.Context, execution interface{}, ) FixResult { if fixResult := validateFixContext(ctx, o.Name()); fixResult != nil { return *fixResult } fixResult, checkResult := checkBeforeFix(ctx, o, execution) if fixResult != nil { return *fixResult } fixResult = DeleteExecution(ctx, execution, o.pr, o.dc) fixResult.CheckResult = *checkResult fixResult.InvariantName = o.Name() return *fixResult } func (o *openCurrentExecution) Name() Name { return OpenCurrentExecution } // ExecutionStillOpen returns true if execution in persistence exists and is open, false otherwise. // Returns error on failure to confirm. func ExecutionStillOpen( ctx context.Context, exec *entity.Execution, pr persistence.Retryer, dc cache.DomainCache, ) (bool, error) { domainName, err := dc.GetDomainName(exec.DomainID) if err != nil { return false, nil } req := &persistence.GetWorkflowExecutionRequest{ DomainID: exec.DomainID, Execution: types.WorkflowExecution{ WorkflowID: exec.WorkflowID, RunID: exec.RunID, }, DomainName: domainName, } resp, err := pr.GetWorkflowExecution(ctx, req) if err != nil { switch err.(type) { case *types.EntityNotExistsError: return false, nil default: return false, err } } return Open(resp.State.ExecutionInfo.State), nil } ================================================ FILE: common/reconciliation/invariant/open_current_execution_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "errors" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" c2 "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) type OpenCurrentExecutionSuite struct { *require.Assertions suite.Suite } func TestOpenCurrentExecutionSuite(t *testing.T) { suite.Run(t, new(OpenCurrentExecutionSuite)) } func (s *OpenCurrentExecutionSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *OpenCurrentExecutionSuite) TestCheck() { testCases := []struct { execution *entity.ConcreteExecution getCurrentResp *persistence.GetCurrentExecutionResponse getCurrentErr error getConcreteResp *persistence.GetWorkflowExecutionResponse getConcreteErr error expectedResult CheckResult }{ { execution: getClosedConcreteExecution(), expectedResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: OpenCurrentExecution, }, }, { execution: getOpenConcreteExecution(), getConcreteErr: errors.New("got error checking if concrete is open"), expectedResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: OpenCurrentExecution, Info: "failed to check if concrete execution is still open", InfoDetails: "got error checking if concrete is open", }, }, { execution: getOpenConcreteExecution(), getConcreteResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: closedState, }, }, }, getConcreteErr: nil, expectedResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: OpenCurrentExecution, }, }, { execution: getOpenConcreteExecution(), getConcreteResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: openState, }, }, }, getConcreteErr: nil, getCurrentErr: &types.EntityNotExistsError{}, expectedResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: OpenCurrentExecution, Info: "execution is open without having current execution", InfoDetails: "", }, }, { execution: getOpenConcreteExecution(), getConcreteResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: openState, }, }, }, getConcreteErr: nil, getCurrentErr: errors.New("error getting current execution"), expectedResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: OpenCurrentExecution, Info: "failed to check if current execution exists", InfoDetails: "error getting current execution", }, }, { execution: getOpenConcreteExecution(), getConcreteResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: openState, }, }, }, getConcreteErr: nil, getCurrentErr: nil, getCurrentResp: &persistence.GetCurrentExecutionResponse{ RunID: "not-equal", }, expectedResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: OpenCurrentExecution, Info: "execution is open but current points at a different execution", InfoDetails: "current points at not-equal", }, }, { execution: getOpenConcreteExecution(), getConcreteResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: openState, }, }, }, getConcreteErr: nil, getCurrentErr: nil, getCurrentResp: &persistence.GetCurrentExecutionResponse{ RunID: runID, }, expectedResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: OpenCurrentExecution, }, }, } ctrl := gomock.NewController(s.T()) domainCache := cache.NewMockDomainCache(ctrl) for _, tc := range testCases { execManager := &mocks.ExecutionManager{} execManager.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(tc.getConcreteResp, tc.getConcreteErr) execManager.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(tc.getCurrentResp, tc.getCurrentErr) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-name", nil).AnyTimes() o := NewOpenCurrentExecution(persistence.NewPersistenceRetryer(execManager, nil, c2.CreatePersistenceRetryPolicy()), domainCache) s.Equal(tc.expectedResult, o.Check(context.Background(), tc.execution)) } } ================================================ FILE: common/reconciliation/invariant/stale_workflow.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "fmt" "reflect" "strings" "time" "go.uber.org/zap" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) const ( // how long to allow things to be beyond when they should have been fully cleaned up, for safety purposes retentionSafetyMargin = time.Hour * 24 * 10 // time.DateOnly, introduced in go1.20 // // deprecated: use time.DateOnly when able dateOnly = "2006-01-02" ) // sane time ranges for workflows. // this is largely to protect against accidental conversions from zero values, or incorrect second/nanosecond/etc scales. var ( // too old to be real. e.g. prior to Cadence existing. // // anything "reasonable" is likely fine, but this should be substantially newer than 1970 to catch zero times. impossiblyOld = time.Date(2015, 0, 0, 0, 0, 0, 0, time.UTC) // too far in the future to be real. // // crazy values are technically possible if someone sets a 100-year timeout on a workflow or something, // but we should probably block those anyway. hopefully nonexistent or rare enough to handle manually (and correct). impossiblyFuture = time.Date(2100, 0, 0, 0, 0, 0, 0, time.UTC) // our internal limits are: // - practically: 30 days // - for monthly-job people: 40 days // - rare exceptions we are trying to eliminate: 90+ days // // this should be far beyond that and should imply bad data, not merely an exceptional domain. // // note that this is not used to *force* deletion. workflows in domains beyond this value will fail and be skipped. maxRetentionDays = 200 ) type staleWorkflowCheck struct { pr persistence.Retryer dc cache.DomainCache log *zap.Logger } // NewStaleWorkflow checks to see if a workflow has out-lived its retention window. // This primarily asserts that that now < (min(start + execution timeout, min) + (domain retention*2)). // If a workflow fails this check, its data is still lingering well beyond when it should have been cleaned up. func NewStaleWorkflow( pr persistence.Retryer, dc cache.DomainCache, log *zap.Logger, ) Invariant { return &staleWorkflowCheck{ pr: pr, dc: dc, log: log, } } func (c *staleWorkflowCheck) Check( ctx context.Context, execution interface{}, ) CheckResult { _, result := c.check(ctx, execution) return result } func (c *staleWorkflowCheck) check( ctx context.Context, execution interface{}, ) (deleteConcrete bool, result CheckResult) { concreteExecution, ok := execution.(*entity.ConcreteExecution) if !ok { return false, c.failed("expected concrete execution", "") } if concreteExecution.WorkflowID == "" || concreteExecution.RunID == "" || concreteExecution.DomainID == "" { return false, c.failed("missing critical data", "") } domainID := concreteExecution.GetDomainID() concreteWorkflow, err := c.pr.GetWorkflowExecution(ctx, &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: concreteExecution.WorkflowID, RunID: concreteExecution.RunID, }, }) if err != nil { return false, c.failed("failed to get concrete execution record", err.Error()) } pastExpiration, checkresult := c.CheckAge(concreteWorkflow) if pastExpiration { if checkresult.CheckResultType == CheckResultTypeCorrupted { return true, checkresult // delete the concrete execution, it's out of retention } // else check the current record, as it may be causing issues for the concrete } return false, checkresult // TODO: ^ similar check for current record? Bad-current can sometimes block a different concrete. } func (c *staleWorkflowCheck) Fix(ctx context.Context, execution interface{}) FixResult { // essentially checkBeforeFix deleteConcrete, checkResult := c.check(ctx, execution) if checkResult.CheckResultType == CheckResultTypeHealthy { return FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: c.Name(), Info: fmt.Sprintf("no need to fix: %s", checkResult.Info), InfoDetails: checkResult.InfoDetails, } } if checkResult.CheckResultType != CheckResultTypeCorrupted { return FixResult{ FixResultType: FixResultTypeFailed, InvariantName: c.Name(), Info: fmt.Sprintf("unable to determine if beyond expiration: %s", checkResult.Info), InfoDetails: checkResult.InfoDetails, } } if deleteConcrete { fixResult := DeleteExecution(ctx, execution, c.pr, c.dc) fixResult.CheckResult = checkResult fixResult.InvariantName = c.Name() return *fixResult } // TODO: handle current deletion? // probably not possible to reach currently because current records are not handled in check. return FixResult{ FixResultType: FixResultTypeFailed, InvariantName: c.Name(), Info: fmt.Sprintf("current record deletion not yet built: %v", checkResult.Info), InfoDetails: checkResult.InfoDetails, } } func (c *staleWorkflowCheck) Name() Name { return StaleWorkflow } func (c *staleWorkflowCheck) CheckAge(workflow *persistence.GetWorkflowExecutionResponse) (pastExpiration bool, result CheckResult) { info := workflow.State.ExecutionInfo retentionNum, domainName, err := c.getDomainInfo(info) if err != nil { return false, c.failed( "unable to get domain retention", "domain: %v, name: %v, err: %v", info.DomainID, domainName, err) } if retentionNum <= 0 { return false, c.failed( "non-positive retention days in domain", "domain: %v, name: %v", info.DomainID, domainName) } if retentionNum > int32(maxRetentionDays) { return false, c.failed( "irrationally-large retention days in domain", "domain: %v, name: %v, days: %v, max: %v", info.DomainID, domainName, retentionNum, maxRetentionDays) } retention := time.Hour * 24 * time.Duration(retentionNum) // add a safety buffer of 10 days safety := time.Hour * 24 * 10 maxLifespan := retention + safety logStaleWorkflow := func(state string, expected time.Time) { c.log.Info("scanner found stale "+state+" workflow", zap.String("wid", info.WorkflowID), zap.String("rid", info.RunID), zap.String("domain_name", domainName), zap.String("domain_id", info.DomainID), zap.String("started_at", info.StartTimestamp.Format(dateOnly)), zap.String("expected_cleanup_at", expected.Format(dateOnly)), zap.String("state", state), // may simplify log processing ) } // treat "running" (has had decision tasks) and "created" (no decisions, e.g. cron / delay / lost initial tasks) // as essentially the same thing. either way there's no close event to check. if info.State == persistence.WorkflowStateRunning || info.State == persistence.WorkflowStateCreated { stale, expected, result := c.checkRunningAge(workflow, maxLifespan, domainName) if stale { // we know these exist, but we don't expect them to be numerous "recently". // log for verification. logStaleWorkflow("running", expected) } return stale, result } else if info.State == persistence.WorkflowStateZombie { // TODO: what exactly is zombie? seems like probably "current but no concrete" but I'm surprised this is in the state. // // https://github.com/uber/cadence/issues/1800 // "The ability to create workflow with zombie status allows replication stack to backfill finished workflow execution sent by remote, while not affecting local running workflow" // so these are just replication of completed workflows? why is this a special state? stale, expected, result := c.checkZombieAge(workflow, maxLifespan, domainName) if stale { // we know these exist, but we don't have a good feel for the distribution logStaleWorkflow("zombie", expected) } return stale, result } else if info.State == persistence.WorkflowStateCompleted { stale, expected, result := c.checkClosedAge(workflow, maxLifespan, domainName) if stale { // we know these exist, but we don't have a good feel for the distribution logStaleWorkflow("closed", expected) } return stale, result } // currently one of: corrupted, created, void // but if more are added in the future, this is still the safe option. return false, c.failed( "unhandled workflow state", "info.State value: %d", info.State) } func (c *staleWorkflowCheck) checkRunningAge(workflow *persistence.GetWorkflowExecutionResponse, maxLifespan time.Duration, domainName string) (pastExpiration bool, expected time.Time, result CheckResult) { // workflow-expiration timer is calculated in GenerateWorkflowStartTasks, mimic it here for safety: https://github.com/uber/cadence/blob/master/service/history/execution/mutable_state_task_generator.go#L140-L163 /* // running workflows might contain critical information in their first history record, so it must be retrieved. info := workflow.State.ExecutionInfo duration := time.Duration(info.WorkflowTimeout) * time.Second // First-decision-backoff is only in first event. It must be included because the expiration time does not include it. // I really wish it wasn't built this way, this makes it much much harder to scan in bulk. timeout := info.StartTimestamp.Add(duration + firstdecisionbackoff) // ensure that the first attempt does not time out early based on retry policy timeout if attr.Attempt > 0 && !executionInfo.ExpirationTime.IsZero() && workflowTimeoutTimestamp.After(executionInfo.ExpirationTime) { workflowTimeoutTimestamp = executionInfo.ExpirationTime } */ first, ok, result := c.firstEvent(workflow, domainName) if !ok { // completely missing history should be caught by the history-exists check, no need to handle here return false, expected, result } startedAt := workflow.State.ExecutionInfo.StartTimestamp // nonzero timeout := time.Second * time.Duration(workflow.State.ExecutionInfo.WorkflowTimeout) // nonzero backoff := time.Second * time.Duration(first.GetFirstDecisionTaskBackoffSeconds()) // may be zero // comment in GenerateWorkflowStartTasks seems backwards, it clamps to the minimum of timeout and retry-expiration, // which means we should be able to ignore it and be *safe* if perhaps not *optimal*. // // completed workflows seem to have this set, but perhaps not running. // which can happen here since this method is reused for closed-but-corrupt workflows. // since I'm not confident on what this field's value is / if it's trustworthy, ignore for now. // // expiration := workflow.State.ExecutionInfo.ExpirationTime // if !expiration.IsZero() { // fmt.Println("non-zero expiration time, not sure what this means") // } timesOutAt := startedAt.Add(timeout).Add(backoff) cleansUpAt := timesOutAt.Add(maxLifespan) // sanity checks on calculated times if ok, result := c.checkTimeInSaneRange(timesOutAt, "workflow timeout"); !ok { return false, expected, result } if ok, result := c.checkTimeInSaneRange(cleansUpAt, "workflow cleanup time"); !ok { return false, expected, result } if cleansUpAt.Before(time.Now()) { return true, cleansUpAt, CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: c.Name(), Info: "stale running workflow", InfoDetails: fmt.Sprintf("running workflow should have timed out by %v and been cleaned up by %v, but it still exists", timesOutAt.Format(dateOnly), cleansUpAt.Format(dateOnly)), } } return false, cleansUpAt, CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: c.Name(), Info: "running workflow is within expiration window", } } // intended as defensive programming, in case values are in some wrong scale or some other surprise occurs. // // these should not trigger in practice, and are intended to catch bugs or logical flaws while developing or changing // this fixer, or flawed/missing knowledge - if they occur, that's likely a scenario we should be handling explicitly. func (c *staleWorkflowCheck) checkTimeInSaneRange(t time.Time, kind string) (ok bool, result CheckResult) { if t.IsZero() { // will also be before-impossibly-old, but separated for clarity purposes return false, c.failed(fmt.Sprintf("calculated %v is zero, failing", kind), "") } if t.Before(impossiblyOld) || t.After(impossiblyFuture) { // something screwed up, time is outside sane bounds return false, c.failed( fmt.Sprintf("calculated %v seems insane, failing", kind), fmt.Sprintf("expected %v to be within range: %q < actual %q < %q", kind, impossiblyOld, t, impossiblyFuture)) } return true, result } func (c *staleWorkflowCheck) checkClosedAge(workflow *persistence.GetWorkflowExecutionResponse, maxLifespan time.Duration, domainName string) (pastExpiration bool, expected time.Time, result CheckResult) { closed, ok, result := c.closeEventTime(workflow, domainName) if !ok { // error of some kind, see if we can consider it stale from just the start info. // this is somewhat common if we are missing the final event in a workflow, e.g. due to broken replication or partial (un)deletion. // running retention will never be shorter than closed retention, so this is always safe. runningStale, expected, runningResult := c.checkRunningAge(workflow, maxLifespan, domainName) // rewrite to mention closed, but interpreted as running. // the reason for failure is still relevant, but this way at least we know it was from the fallback. return runningStale, expected, CheckResult{ CheckResultType: runningResult.CheckResultType, InvariantName: runningResult.InvariantName, Info: strings.Replace(runningResult.Info, "running workflow", "completed workflow (err, treating as running)", 1) + ". closed info: " + result.Info, InfoDetails: strings.Replace(runningResult.InfoDetails, "running workflow", "completed workflow (err, treating as running)", 1) + ". closed info details: " + result.InfoDetails, } } if ok, result := c.checkTimeInSaneRange(closed, "workflow close"); !ok { // bad data, zero value, or perhaps a number-scale issue. either way untrustworthy. return false, expected, result } expected = closed.Add(maxLifespan) if expected.Before(time.Now()) { // >10 days beyond retention, should be deleted return true, expected, CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: c.Name(), Info: "completed workflow exists beyond retention window", InfoDetails: fmt.Sprintf("completed workflow exists beyond retention window, should have disappeared by %v", closed.Add(maxLifespan).Format(dateOnly)), } } // still in retention, it's fine return false, expected, CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: c.Name(), Info: "completed workflow still within retention + 10-day buffer", InfoDetails: fmt.Sprintf("completed workflow still within retention + 10-day buffer, closed %v and allowed to exist until %v", closed, closed.Add(maxLifespan).Format(dateOnly)), } } func (c *staleWorkflowCheck) checkZombieAge(workflow *persistence.GetWorkflowExecutionResponse, maxLifespan time.Duration, domainName string) (pastExpiration bool, expected time.Time, result CheckResult) { // zombies may or may not have history, and may or may not be "running" depending on if enough history has replicated. // they are not ever "current" though apparently. despite there being a current execution record pointing to them. wut. /* func (e *mutableStateBuilder) IsCurrentWorkflowGuaranteed() bool { // stateInDB is used like a bloom filter: // // 1. stateInDB being created / running meaning that this workflow must be the current // workflow (assuming there is no rebuild of mutable state). // 2. stateInDB being completed does not guarantee this workflow being the current workflow // 3. stateInDB being zombie guarantees this workflow not being the current workflow // 4. stateInDB cannot be void, void is only possible when mutable state is just initialized */ // AFAICT zombie workflows are both not running and not completely replicated, i.e. there is no completion info or final completion event. // so just check them as running, at least until we learn otherwise. // // if there are completed ones, just check completed-result if running-age fails. // the cleanup time is just the minimum of the two (should always be completed if it exists) cleanup, expected, result := c.checkRunningAge(workflow, maxLifespan, domainName) // just reword it result.Info = strings.Replace(result.Info, "running workflow", "zombie workflow", 1) result.InfoDetails = strings.Replace(result.InfoDetails, "running workflow", "zombie workflow", 1) return cleanup, expected, result } // it's just quite verbose func (c *staleWorkflowCheck) failed(info string, details string, args ...interface{}) CheckResult { if details != "" { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: c.Name(), Info: info, InfoDetails: fmt.Sprintf(details, args...), } } return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: c.Name(), Info: info, } } func (c *staleWorkflowCheck) firstEvent(workflow *persistence.GetWorkflowExecutionResponse, domainName string) (attrs types.WorkflowExecutionStartedEventAttributes, ok bool, result CheckResult) { // see: func (e *mutableStateBuilder) GetStartEvent( /* currentBranchToken, err := e.GetCurrentBranchToken() if err != nil { return nil, err } startEvent, err := e.eventsCache.GetEvent( ctx, e.shard.GetShardID(), e.executionInfo.DomainID, e.executionInfo.WorkflowID, e.executionInfo.RunID, common.FirstEventID, common.FirstEventID, currentBranchToken, ) */ branchToken, ok, result := c.getBranchToken(workflow) if !ok { return attrs, false, result } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() shard := c.pr.GetShardID() history, err := c.pr.ReadHistoryBranch(ctx, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: constants.FirstEventID, MaxEventID: constants.FirstEventID + 1, // exclusive bound ShardID: &shard, PageSize: 1, // just 1 necessary DomainName: domainName, }) if err != nil { return attrs, false, c.failed( "could not read first event history", "read branch error for shard: %v, token: %s, err: %v", c.pr.GetShardID(), branchToken, err) } if len(history.HistoryEvents) < 1 { return attrs, false, c.failed( "incomplete history, missing first event?", "got: %v", history.HistoryEvents) } first := history.HistoryEvents[0] if first.WorkflowExecutionStartedEventAttributes == nil { return attrs, false, c.failed("missing start event attributes", "got: %v", first) } return *first.WorkflowExecutionStartedEventAttributes, true, result } func (c *staleWorkflowCheck) getBranchToken(workflow *persistence.GetWorkflowExecutionResponse) (token []byte, ok bool, result CheckResult) { /* func (e *mutableStateBuilder) GetCurrentBranchToken() ([]byte, error) { if e.versionHistories != nil { currentVersionHistory, err := e.versionHistories.GetCurrentVersionHistory() if err != nil { return nil, err } return currentVersionHistory.GetBranchToken(), nil } return e.executionInfo.BranchToken, nil } */ if workflow.State.VersionHistories == nil { // should imply no history data, should be impossible? return nil, false, c.failed("no version histories", "") } currentVersionHistory, err := workflow.State.VersionHistories.GetCurrentVersionHistory() if err != nil { // should be impossible? return nil, false, c.failed("unable to get current version history", "err: %v", err) } currentBranchToken := currentVersionHistory.GetBranchToken() if len(currentBranchToken) == 0 { // should be impossible? return nil, false, c.failed("no current branch token", "") } return currentBranchToken, true, result } func (c *staleWorkflowCheck) closeEventTime(workflow *persistence.GetWorkflowExecutionResponse, domainName string) (when time.Time, ok bool, result CheckResult) { // completion event is (sometimes?) nil, pull it from history instead. // strangely, close-event-time is not part of the workflow execution response. // pull the final event in history to figure it out. branchToken, ok, result := c.getBranchToken(workflow) if !ok { return when, false, result } last, err := c.getLastEvent(branchToken, domainName) if err != nil { return when, false, c.failed("failed to get last event", err.Error()) } if last == nil { return when, false, c.failed("empty last event, should be impossible", "") // successful totally-empty history seems impossible } // make extra sure it's a completion event. // all history events have a timestamp, but it needs to be a final-state event, not a random one. if !anyPresent( last.WorkflowExecutionCanceledEventAttributes, last.WorkflowExecutionCompletedEventAttributes, last.WorkflowExecutionContinuedAsNewEventAttributes, last.WorkflowExecutionFailedEventAttributes, last.WorkflowExecutionTerminatedEventAttributes, last.WorkflowExecutionTimedOutEventAttributes, ) { // completion event may simply not exist due to incomplete replication. // this may be handled by history_exists or broken-history invariants, and we don't need to handle it here. return when, false, c.failed("last event is not a completion-type", "missing data? got: %v", last) } if last.Timestamp == nil { return when, false, c.failed("last event has a nil timestamp", "bad data? got: %v", last) } // sanity check, so far I have not seen any completion events in the top-level state. // I suspect it's only an old / cache field, but bail if it disagrees, just in case. if workflow.State.ExecutionInfo.CompletionEvent != nil && workflow.State.ExecutionInfo.CompletionEvent.Timestamp != nil && *last.Timestamp != *workflow.State.ExecutionInfo.CompletionEvent.Timestamp { return when, false, c.failed( "workflow execution info and final event disagree", "bad data? got execution-completion-event timestamp: %v and final history event timestamp: %v", time.Unix(0, *last.Timestamp), time.Unix(0, *workflow.State.ExecutionInfo.CompletionEvent.Timestamp)) } ts := time.Unix(0, *last.Timestamp) if ok, result := c.checkTimeInSaneRange(ts, "last event timestamp"); !ok { return when, false, result } return ts, true, result } func anyPresent(items ...interface{}) bool { for _, i := range items { // cannot use `i == nil` because typed nil pointers are boxed into non-nil interface{} due to having a type if !reflect.ValueOf(i).IsNil() { return true } } return false } func (c *staleWorkflowCheck) getLastEvent(branchToken []byte, domainName string) (*types.HistoryEvent, error) { const ( maxHistoryLen = 250000 // our internal limits are much smaller, but it would be fine to raise this pageSize = 1000 // multiple 1000/page limits elsewhere, also fine to change batchTimeout = 5 * time.Second ) shard := c.pr.GetShardID() iter := 0 var nextPageToken []byte var lastEvent *types.HistoryEvent for ; iter < (maxHistoryLen / pageSize); iter++ { // loose sanity check ctx, cancel := context.WithTimeout(context.Background(), batchTimeout) defer cancel() // revive:disable-line:defer clean up on panics, dups are just noops history, err := c.pr.ReadHistoryBranch(ctx, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, // only interested in the last event, but we have to read in order to get there. MinEventID: constants.FirstEventID, MaxEventID: constants.EndEventID, ShardID: &shard, PageSize: pageSize, DomainName: domainName, NextPageToken: nextPageToken, }) cancel() // handle non-panics if err != nil { return nil, err } if len(history.HistoryEvents) > 0 { // should only be false if it didn't notice end-of-history in the previous batch (not sure if this happens or not) lastEvent = history.HistoryEvents[len(history.HistoryEvents)-1] } if len(history.NextPageToken) == 0 { // received last batch return lastEvent, nil } nextPageToken = history.NextPageToken } // should be unreachable, assuming our limits work return nil, fmt.Errorf("exceeded max history requests (%v), failing for branch token: %s", iter, branchToken) } func (c *staleWorkflowCheck) getDomainInfo(info *persistence.WorkflowExecutionInfo) (retention int32, name string, err error) { // domain cache entries have private fields, hence this testable method is necessary to avoid using them domain, err := c.dc.GetDomainByID(info.DomainID) if err != nil { return -1, "", err } retentionNum := domain.GetRetentionDays(info.WorkflowID) // takes retention-sampling into account return retentionNum, domain.GetInfo().Name, nil } ================================================ FILE: common/reconciliation/invariant/stale_workflow_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "fmt" "math/rand" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) func setup(t *testing.T, retentionDays int) (*persistence.MockRetryer, *staleWorkflowCheck) { ctrl := gomock.NewController(t) pr := persistence.NewMockRetryer(ctrl) dc := cache.NewMockDomainCache(ctrl) domainEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: int32(retentionDays)}, true, nil, 0, nil, 0, 0, 0) dc.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil).AnyTimes() dc.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() invariant := NewStaleWorkflow(pr, dc, testlogger.NewZap(t)) impl, ok := invariant.(*staleWorkflowCheck) if !ok { t.Fatalf("NewStaleWorkflowVersion returned invariant of type %T but want *staleWorkflowCheck", invariant) } return pr, impl } // some acceptable defaults for tests that don't care about precise windows const ( testRetentionDays = 5 testRetentionAfterSafetyMargin = (time.Duration(testRetentionDays) * 24 * time.Hour) + retentionSafetyMargin oneDay = 24 * time.Hour ) // This test case validates the Check method for stale workflow invariant is doing the correct thing at high level. // Rest of the tests in this file covers the internals of CheckAge which is called by Check. func TestCheck(t *testing.T) { tests := []struct { desc string execution any mockFn func(*persistence.MockRetryer) wantResult CheckResult }{ { desc: "corrupted workflow past expiration", execution: &entity.ConcreteExecution{ Execution: entity.Execution{ WorkflowID: workflowID, RunID: runID, DomainID: domainID, }, }, mockFn: func(pr *persistence.MockRetryer) { pr.EXPECT().GetWorkflowExecution(gomock.Any(), &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, }).Return(execution(domainID, running, time.Now().Add(-testRetentionAfterSafetyMargin-2*time.Hour), time.Hour, nil), nil).Times(1) pr.EXPECT().GetShardID().Return(123).Times(1) pr.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()). Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, }, }, nil).Times(1) }, wantResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: "stale_workflow", Info: "stale running workflow", }, }, { desc: "normal healthy workflow", execution: &entity.ConcreteExecution{ Execution: entity.Execution{ WorkflowID: workflowID, RunID: runID, DomainID: domainID, }, }, mockFn: func(pr *persistence.MockRetryer) { pr.EXPECT().GetWorkflowExecution(gomock.Any(), &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, }).Return(execution(domainID, running, time.Now().Add(-2*time.Hour), time.Hour, nil), nil).Times(1) pr.EXPECT().GetShardID().Return(123).Times(1) pr.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()). Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, }, }, nil).Times(1) }, wantResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: "stale_workflow", Info: "running workflow is within expiration window", }, }, { desc: "zombie healthy workflow", execution: &entity.ConcreteExecution{ Execution: entity.Execution{ WorkflowID: workflowID, RunID: runID, DomainID: domainID, }, }, mockFn: func(pr *persistence.MockRetryer) { pr.EXPECT().GetWorkflowExecution(gomock.Any(), &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, }).Return(execution(domainID, zombie, time.Now().Add(-2*time.Hour), time.Hour, nil), nil).Times(1) pr.EXPECT().GetShardID().Return(123).Times(1) pr.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()). Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, }, }, nil).Times(1) }, wantResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: "stale_workflow", Info: "zombie workflow is within expiration window", }, }, { desc: "invalid execution object type", execution: &entity.Timer{}, // invalid object type mockFn: func(pr *persistence.MockRetryer) {}, wantResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: "stale_workflow", Info: "expected concrete execution", }, }, { desc: "missing workflow id", execution: &entity.ConcreteExecution{ Execution: entity.Execution{ WorkflowID: "", // missing workflow id RunID: runID, DomainID: domainID, }, }, mockFn: func(pr *persistence.MockRetryer) {}, wantResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: "stale_workflow", Info: "missing critical data", }, }, { desc: "get workflow execution failed", execution: &entity.ConcreteExecution{ Execution: entity.Execution{ WorkflowID: workflowID, RunID: runID, DomainID: domainID, }, }, mockFn: func(pr *persistence.MockRetryer) { pr.EXPECT().GetWorkflowExecution(gomock.Any(), &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, }).Return(nil, fmt.Errorf("failed")).Times(1) }, wantResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: "stale_workflow", Info: "failed to get concrete execution record", }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { pr, impl := setup(t, testRetentionDays) tc.mockFn(pr) gotResult := impl.Check(context.Background(), tc.execution) gotResult.InfoDetails = "" // ignore details for now assert.Equal(t, tc.wantResult, gotResult) }) } } func TestFix(t *testing.T) { tests := []struct { desc string execution any mockFn func(*persistence.MockRetryer) wantResult FixResult }{ { desc: "healthy workflow skipped", execution: &entity.ConcreteExecution{ Execution: entity.Execution{ WorkflowID: workflowID, RunID: runID, DomainID: domainID, }, }, mockFn: func(pr *persistence.MockRetryer) { pr.EXPECT().GetWorkflowExecution(gomock.Any(), &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, }).Return(execution(domainID, running, time.Now().Add(-2*time.Hour), time.Hour, nil), nil).Times(1) pr.EXPECT().GetShardID().Return(123).Times(1) pr.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()). Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, }, }, nil).Times(1) }, wantResult: FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: "stale_workflow", Info: "no need to fix: running workflow is within expiration window", }, }, { desc: "corrupted workflow deleted", execution: &entity.ConcreteExecution{ Execution: entity.Execution{ WorkflowID: workflowID, RunID: runID, DomainID: domainID, }, }, mockFn: func(pr *persistence.MockRetryer) { pr.EXPECT().GetWorkflowExecution(gomock.Any(), &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, }).Return(execution(domainID, running, time.Now().Add(-testRetentionAfterSafetyMargin-2*time.Hour), time.Hour, nil), nil).Times(1) pr.EXPECT().GetShardID().Return(123).Times(1) pr.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()). Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, }, }, nil).Times(1) pr.EXPECT().DeleteWorkflowExecution(gomock.Any(), &persistence.DeleteWorkflowExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, DomainName: domainName, }).Return(nil).Times(1) pr.EXPECT().DeleteCurrentWorkflowExecution(gomock.Any(), &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, DomainName: domainName, }).Return(nil).Times(1) }, wantResult: FixResult{ FixResultType: FixResultTypeFixed, InvariantName: "stale_workflow", CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: "stale_workflow", Info: "stale running workflow", }, }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { pr, impl := setup(t, testRetentionDays) tc.mockFn(pr) gotResult := impl.Fix(context.Background(), tc.execution) gotResult.CheckResult.InfoDetails = "" // ignore details for now gotResult.InfoDetails = "" // ignore details for now assert.Equal(t, tc.wantResult, gotResult) }) } } func TestStillRunning(t *testing.T) { // lots of similarity in these tests, abstract it a bit. run := func(created time.Time, timeout, backoff time.Duration) (pastExpiration bool, result CheckResult) { // sanity checks if timeout <= 0 { panic("cannot have non-positive timeouts") } if backoff < 0 { panic("cannot have negative backoff") } if time.Now().Before(created) { panic("created cannot be in the future") } pr, impl := setup(t, testRetentionDays) withOpenHistory( created, backoff, pr, ) wf := execution( domainID, running, created, timeout, nil, ) return impl.CheckAge(wf) } assertHealthy := func(t *testing.T, result CheckResult, isPastExpiration bool) { t.Logf("result: %#v", result) assert.Equal(t, CheckResultTypeHealthy, result.CheckResultType, "result should be healthy") assert.False(t, isPastExpiration, "workflow should not be past expiration") } t.Run("healthy", func(t *testing.T) { pastExpiration, result := run( time.Now().Add(-time.Hour), 2*time.Hour, 0, ) // still running, still within retention window, "healthy" assertHealthy(t, result, pastExpiration) }) t.Run("healthy-long-workflow", func(t *testing.T) { // specifically: this workflow runs longer than the retention window, to make sure it's not prematurely removed pastExpiration, result := run( time.Now().Add(-testRetentionAfterSafetyMargin).Add(-oneDay), // create 1 day before safety margin testRetentionAfterSafetyMargin+(2*oneDay), // expires tomorrow 0, ) // still running, still within retention window, "healthy" assertHealthy(t, result, pastExpiration) }) t.Run("healthy-short-workflow-long-backoff", func(t *testing.T) { // create+timeout alone should have cleaned this up by ~1 day, but backoff means it has not yet done anything pastExpiration, result := run( time.Now().Add(-testRetentionAfterSafetyMargin).Add(-oneDay), time.Minute, // with only create date, should have already been cleaned up testRetentionAfterSafetyMargin+(2*oneDay), // but backoff means it has not actually "started" yet ) assertHealthy(t, result, pastExpiration) }) t.Run("bad-running-but-retained", func(t *testing.T) { pastExpiration, result := run( time.Now().Add(-time.Hour), time.Minute, 0, ) // still within the retention window, so it's treated as "healthy" for the purposes of this check, // even though it's "corrupt" because it should not still be running. assertHealthy(t, result, pastExpiration) }) t.Run("bad-running-and-corrupt", func(t *testing.T) { // created a day before retention margin created := time.Now().Add(-testRetentionAfterSafetyMargin).Add(-oneDay) pastExpiration, result := run( created, // created a day before retention margin time.Minute, // should have been deleted 23h59m ago at the latest 0, ) // should be cleaned up because it's simply too old to reasonably exist t.Logf("result: %#v", result) assert.Equal(t, CheckResultTypeCorrupted, result.CheckResultType, "result should be corrupted, as it has expired") assert.True(t, pastExpiration, "workflow should be past expiration, created: %v, retention days: %v + %v safety, now: %v", created.Format(dateOnly), testRetentionDays, retentionSafetyMargin, time.Now().Format(dateOnly)) }) t.Run("bad-running-and-backoff-and-corrupt", func(t *testing.T) { // created 2 days before retention margin created := time.Now().Add(-testRetentionAfterSafetyMargin).Add(-(2 * oneDay)) pastExpiration, result := run( created, time.Minute, oneDay, // with one day backoff + timeout, should have been cleaned 23h59m ago ) // should be cleaned up because it's simply too old to reasonably exist t.Logf("result: %#v", result) assert.Equal(t, CheckResultTypeCorrupted, result.CheckResultType, "result should be corrupted, as it has expired") assert.True(t, pastExpiration, "workflow should be past expiration, created: %v, backoff: %v, retention days: %v + %v safety, now: %v", created.Format(dateOnly), oneDay, testRetentionDays, retentionSafetyMargin, time.Now().Format(dateOnly)) }) } func TestPresent(t *testing.T) { // paranoia: nil pointers and interfaces combine in surprising ways in Go. var s *string var i *int assert.False(t, anyPresent(s, i), "null pointers should not be present") present := "non null" assert.True(t, anyPresent(s, i, &present), "fully populated values should be present") var empty string assert.True(t, anyPresent(s, i, &empty), "non-null empty values should still be present") } func TestComplete(t *testing.T) { // lots of similarity in these tests, abstract it a bit. run := func(t *testing.T, created time.Time, timeout time.Duration, completed time.Time) (pastExpiration bool, result CheckResult) { // sanity checks if timeout <= 0 { panic("cannot have non-positive timeouts") } if completed.Before(created) { panic("cannot complete before being created") } if time.Now().Before(created) { panic("created cannot be in the future") } if created.Add(timeout).Before(completed) { panic("timeout would have already completed this workflow") } pr, impl := setup(t, testRetentionDays) withClosedHistory(&completed, pr) // randomize completion time vs nil, as both seem important. log which for reproduction purposes. var maybeCompleted *time.Time if rand.Intn(2) == 1 { t.Log("including completed time") maybeCompleted = &completed } else { t.Log("leaving completed time as nil") } wf := execution(domainID, closed, created, timeout, maybeCompleted) return impl.CheckAge(wf) } assertHealthy := func(t *testing.T, result CheckResult, isPastExpiration bool) { t.Logf("result: %#v", result) assert.Equal(t, CheckResultTypeHealthy, result.CheckResultType, "result should be healthy") assert.False(t, isPastExpiration, "workflow should not be past expiration") } assertCorrupt := func(t *testing.T, result CheckResult, isPastExpiration bool) { t.Logf("result: %#v", result) assert.Equal(t, CheckResultTypeCorrupted, result.CheckResultType, "result should be corrupted, as it has expired") assert.True(t, isPastExpiration, "workflow should be past expiration") } t.Run("recently-complete", func(t *testing.T) { startTime := time.Now().Add(-time.Hour) timeout := time.Hour timeoutTime := startTime.Add(timeout) pastExpiration, result := run(t, startTime, timeout, timeoutTime) // started and completed well within retention assertHealthy(t, result, pastExpiration) }) t.Run("long-ago-complete", func(t *testing.T) { startTime := time.Now().Add(-testRetentionAfterSafetyMargin).Add(-2 * oneDay) timeout := time.Hour timeoutTime := startTime.Add(timeout) pastExpiration, result := run(t, startTime, timeout, timeoutTime) // completed long ago, still around assertCorrupt(t, result, pastExpiration) }) t.Run("long-ago-complete-but-not-yet-timeout", func(t *testing.T) { startTime := time.Now().Add(-testRetentionAfterSafetyMargin).Add(-2 * oneDay) // same as above timeout := time.Since(startTime) + oneDay // workflow timeout would fire tomorrow timeoutTime := startTime.Add(time.Hour) // same as above pastExpiration, result := run(t, startTime, timeout, timeoutTime) // completed long ago, still around. timeout would keep it present if it were considered. assertCorrupt(t, result, pastExpiration) }) t.Run("long-ago-started-but-recently-complete", func(t *testing.T) { startTime := time.Now().Add(-testRetentionAfterSafetyMargin).Add(-2 * oneDay) // same as above timeout := time.Since(startTime) - oneDay // workflow timeout occurred yesterday timeoutTime := startTime.Add(timeout) // and it timed out pastExpiration, result := run(t, startTime, timeout, timeoutTime) // complete, ran longer than retention, still present assertHealthy(t, result, pastExpiration) }) t.Run("long-long-ago-started-and-long-complete", func(t *testing.T) { startTime := time.Now().Add(-(2 * testRetentionAfterSafetyMargin)).Add(-2 * oneDay) // ~2x older timeout := 10 * testRetentionAfterSafetyMargin // workflow timeout far in the future timeoutTime := startTime.Add(testRetentionAfterSafetyMargin).Add(oneDay) // completed retention+oneDay ago pastExpiration, result := run(t, startTime, timeout, timeoutTime) // ran longer than retention, completed long than retention ago, should be gone assertCorrupt(t, result, pastExpiration) }) } func TestCompleteWithRunningFallback(t *testing.T) { pr, impl := setup(t, testRetentionDays) startTime := time.Now().Add(-testRetentionAfterSafetyMargin).Add(-2 * oneDay) // started long ago withOpenHistoryFallback(startTime, 0, pr) wf := execution( domainID, closed, startTime, time.Hour, // would have timed out 1 hour after starting, outside retention nil, // no completed time value ) pastExpiration, result := impl.CheckAge(wf) t.Logf("result: %#v", result) assert.Equal(t, CheckResultTypeCorrupted, result.CheckResultType, "result should be corrupted, as it has expired") assert.True(t, pastExpiration, "workflow should be past expiration") assert.Contains(t, result.InfoDetails, "completed workflow", "should have recognized it's completed") assert.Contains(t, result.InfoDetails, "treating as running", "should have mentioned that it has fallen back to running") } func withClosedHistory(finish *time.Time, pr *persistence.MockRetryer) { pr.EXPECT().GetShardID().Return(0).MinTimes(1) firstPage := &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { Timestamp: nil, // unsure, probably should be start time, but currently unused WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, { DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, }, }, NextPageToken: []byte("not nil"), } var ts *int64 // always present from samples I've seen, and asserted if finish != nil { nano := finish.UnixNano() ts = &nano } final := &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { Timestamp: ts, // always present from samples I've seen, and asserted WorkflowExecutionCompletedEventAttributes: &types.WorkflowExecutionCompletedEventAttributes{ DecisionTaskCompletedEventID: 3, }, }, }, } pr.EXPECT().ReadHistoryBranch( gomock.Any(), requestMaxEvent{constants.EndEventID, ""}, // not a first-event-only request ).Return(firstPage, nil).Times(1) pr.EXPECT().ReadHistoryBranch( gomock.Any(), requestMaxEvent{constants.EndEventID, "not nil"}, // token from first page ).Return(final, nil).Times(1) } func withOpenHistory(start time.Time, backoff time.Duration, pr *persistence.MockRetryer) { pr.EXPECT().GetShardID().Return(0).MinTimes(1) seconds := int32(backoff.Seconds()) res := &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { Timestamp: nil, // unsure, probably should be start time, but currently unused WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: &seconds, }, }, }, } pr.EXPECT().ReadHistoryBranch( gomock.Any(), requestMaxEvent{2, ""}, // does not match a "read all history" / "get last event" request ).Return(res, nil).Times(1) } // essentially incomplete-closed-then-open, but it's annoying to break them apart reusably so it's duplicated here func withOpenHistoryFallback(start time.Time, backoff time.Duration, pr *persistence.MockRetryer) { closedHistory := &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { Timestamp: nil, // unsure, probably should be start time, but currently unused WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, { DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, }, }, NextPageToken: nil, // premature end of history } pr.EXPECT().ReadHistoryBranch( gomock.Any(), requestMaxEvent{constants.EndEventID, ""}, // not a first-event-only request ).Return(closedHistory, nil).Times(1) // and then it's just another open request, no need to customize withOpenHistory(start, backoff, pr) } type requestMaxEvent struct { max int64 token string } var _ gomock.Matcher = requestMaxEvent{} func (m requestMaxEvent) Matches(x interface{}) bool { req, ok := x.(*persistence.ReadHistoryBranchRequest) if !ok { return false } if req.MaxEventID > m.max { return false // wrong max id } // nil and empty-array are both empty string, which is close enough return m.token == string(req.NextPageToken) } func (m requestMaxEvent) String() string { return fmt.Sprintf("max event ID must be <= %v and next page token must be %q", m.max, m.token) } type workflowstate int // minor type-safety hack, we should change these iotas const ( running workflowstate = persistence.WorkflowStateRunning closed workflowstate = persistence.WorkflowStateCompleted zombie workflowstate = persistence.WorkflowStateZombie ) func execution(domainid string, state workflowstate, created time.Time, timeout time.Duration, completed *time.Time) *persistence.GetWorkflowExecutionResponse { res := &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ // needed for many things ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: domainid, State: int(state), /* untyped iota, persistence.WorkflowState* */ StartTimestamp: created, WorkflowTimeout: int32(timeout.Seconds()), }, // needed for branch token VersionHistories: &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("fake-branch-token"), }, }, }, }, } if completed != nil { // not yet seen IRL, but asserted to match the last event timestamp in full history nano := completed.UnixNano() res.State.ExecutionInfo.CompletionEvent = &types.HistoryEvent{ Timestamp: &nano, } } return res } ================================================ FILE: common/reconciliation/invariant/timer_invalid.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) const TimerInvalidName = "TimerInvalid" type TimerInvalid struct { pr persistence.Retryer cache cache.DomainCache } // NewTimerInvalid returns a new history exists invariant func NewTimerInvalid( pr persistence.Retryer, cache cache.DomainCache, ) Invariant { return &TimerInvalid{ pr: pr, cache: cache, } } // Check checks if timer is scheduled for open execution func (h *TimerInvalid) Check( ctx context.Context, e interface{}, ) CheckResult { if checkResult := validateCheckContext(ctx, h.Name()); checkResult != nil { return *checkResult } timer, ok := e.(*entity.Timer) if !ok { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: h.Name(), Info: "failed to check: expected timer entity", } } domainID := timer.DomainID domainName, err := h.cache.GetDomainName(timer.DomainID) if err != nil { return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: h.Name(), Info: "failed to check: expected Domain Name", } } req := &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: timer.WorkflowID, RunID: timer.RunID, }, DomainName: domainName, } resp, err := h.pr.GetWorkflowExecution(ctx, req) if err != nil { switch err.(type) { case *types.EntityNotExistsError: return CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: h.Name(), Info: "timer scheduled for non existing workflow", } default: return CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: h.Name(), Info: "failed to get workflow for timer", } } } if !Open(resp.State.ExecutionInfo.State) { return CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: h.Name(), Info: "timer scheduled for closed workflow", } } return CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: h.Name(), } } // Fix will delete invalid timer func (h *TimerInvalid) Fix( ctx context.Context, e interface{}, ) FixResult { if fixResult := validateFixContext(ctx, h.Name()); fixResult != nil { return *fixResult } fixResult, checkResult := checkBeforeFix(ctx, h, e) if fixResult != nil { return *fixResult } timer, _ := e.(*entity.Timer) if timer.TaskType != persistence.TaskTypeUserTimer { return FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: h.Name(), Info: "timer is not a TaskTypeUserTimer", } } req := persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, TaskKey: persistence.NewHistoryTaskKey( timer.VisibilityTimestamp, timer.TaskID, ), } if err := h.pr.CompleteHistoryTask(ctx, &req); err != nil { return FixResult{ FixResultType: FixResultTypeFailed, InvariantName: h.Name(), Info: err.Error(), } } return FixResult{ FixResultType: FixResultTypeFixed, InvariantName: h.Name(), CheckResult: *checkResult, } } func (h *TimerInvalid) Name() Name { return TimerInvalidName } ================================================ FILE: common/reconciliation/invariant/timer_invalid_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) type TimerInvalidTest struct { suite.Suite } func TestTimerInvalidSuite(t *testing.T) { suite.Run(t, new(TimerInvalidTest)) } func (ts *TimerInvalidTest) TestCheck() { testCases := []struct { name string ctxExpired bool getExecResp *persistence.GetWorkflowExecutionResponse getExecErr error expectedResult CheckResult entity interface{} }{ { name: "Context expired", ctxExpired: true, expectedResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: "TimerInvalid", Info: "failed to check: context expired or cancelled", InfoDetails: "context deadline exceeded", }, getExecResp: &persistence.GetWorkflowExecutionResponse{}, getExecErr: errors.New("random error"), entity: &entity.Timer{}, }, { name: "Check if entity is a timer", expectedResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: "TimerInvalid", Info: "failed to check: expected timer entity", InfoDetails: "", }, }, { name: "Check for persistence error", expectedResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: "TimerInvalid", Info: "failed to get workflow for timer", InfoDetails: "", }, getExecResp: nil, getExecErr: errors.New("random error"), entity: &entity.Timer{}, }, { name: "Workflow not found in persistence", expectedResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: "TimerInvalid", Info: "timer scheduled for non existing workflow", InfoDetails: "", }, getExecResp: nil, getExecErr: &types.EntityNotExistsError{}, entity: &entity.Timer{}, }, { name: "Workflow is closed", expectedResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: "TimerInvalid", Info: "timer scheduled for closed workflow", InfoDetails: "", }, getExecResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: closedState, }, }, }, getExecErr: nil, entity: &entity.Timer{}, }, { name: "Check passed", expectedResult: CheckResult{ CheckResultType: CheckResultTypeHealthy, InvariantName: "TimerInvalid", }, getExecResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: openState, }, }, }, getExecErr: nil, entity: &entity.Timer{}, }, } ctrl := gomock.NewController(ts.T()) mockDomainCache := cache.NewMockDomainCache(ctrl) for _, tc := range testCases { mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-name", nil).AnyTimes() ts.Run(tc.name, func() { execManager := &mocks.ExecutionManager{} execManager.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(tc.getExecResp, tc.getExecErr) i := NewTimerInvalid( persistence.NewPersistenceRetryer( execManager, nil, common.CreatePersistenceRetryPolicy(), ), mockDomainCache, ) ctx := context.Background() if tc.ctxExpired { ctx, _ = context.WithDeadline(ctx, time.Now()) } result := i.Check(ctx, tc.entity) ts.Equal(tc.expectedResult, result) }) } } func (ts *TimerInvalidTest) TestFix() { testCases := []struct { name string ctxExpired bool getExecResp *persistence.GetWorkflowExecutionResponse getExecErr error expectedResult FixResult entity interface{} ttComplete error }{ { name: "context expired", ctxExpired: true, expectedResult: FixResult{ FixResultType: FixResultTypeFailed, InvariantName: "TimerInvalid", Info: "failed to check: context expired or cancelled", InfoDetails: "context deadline exceeded", }, getExecResp: &persistence.GetWorkflowExecutionResponse{}, getExecErr: errors.New("random error"), entity: &entity.Timer{}, }, { name: "check before fix fails bc it's not a timer", expectedResult: FixResult{ FixResultType: FixResultTypeFailed, CheckResult: CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: "TimerInvalid", Info: "failed to check: expected timer entity", }, InvariantName: "TimerInvalid", Info: "failed fix because check failed", InfoDetails: "", }, }, { name: "timer type is not a user timer", getExecResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: closedState, }, }, }, expectedResult: FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: "TimerInvalid", Info: "timer is not a TaskTypeUserTimer", InfoDetails: "", }, entity: &entity.Timer{ TaskType: persistence.TaskTypeActivityRetryTimer, }, }, { name: "timer deletion fails on persistence", getExecResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: closedState, }, }, }, expectedResult: FixResult{ FixResultType: FixResultTypeFailed, InvariantName: "TimerInvalid", Info: "error from persistence", InfoDetails: "", }, entity: &entity.Timer{ TaskType: persistence.TaskTypeUserTimer, }, ttComplete: errors.New("error from persistence"), }, { name: "timer deleted", getExecResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: closedState, }, }, }, expectedResult: FixResult{ FixResultType: FixResultTypeFixed, InvariantName: "TimerInvalid", Info: "", InfoDetails: "", CheckResult: CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: "TimerInvalid", Info: "timer scheduled for closed workflow", }, }, entity: &entity.Timer{ TaskType: persistence.TaskTypeUserTimer, }, ttComplete: nil, }, } ctrl := gomock.NewController(ts.T()) mockDomainCache := cache.NewMockDomainCache(ctrl) for _, tc := range testCases { mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-name", nil).AnyTimes() ts.Run(tc.name, func() { execManager := &mocks.ExecutionManager{} execManager.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(tc.getExecResp, tc.getExecErr) execManager.On("CompleteHistoryTask", mock.Anything, mock.Anything).Return(tc.ttComplete) i := NewTimerInvalid( persistence.NewPersistenceRetryer( execManager, nil, common.CreatePersistenceRetryPolicy(), ), mockDomainCache, ) ctx := context.Background() if tc.ctxExpired { ctx, _ = context.WithDeadline(ctx, time.Now()) } result := i.Fix(ctx, tc.entity) ts.Equal(tc.expectedResult, result) }) } } ================================================ FILE: common/reconciliation/invariant/types.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination mocks.go -self_package github.com/uber/cadence/common/reconciliation/invariant //go:generate enumer -type=Collection -output collection_enumer_generated.go package invariant import "context" const ( // CheckResultTypeFailed indicates a failure occurred while attempting to run check CheckResultTypeFailed CheckResultType = "failed" // CheckResultTypeCorrupted indicates check successfully ran and detected a corruption CheckResultTypeCorrupted CheckResultType = "corrupted" // CheckResultTypeHealthy indicates check successfully ran and detected no corruption CheckResultTypeHealthy CheckResultType = "healthy" // FixResultTypeSkipped indicates that fix skipped execution FixResultTypeSkipped FixResultType = "skipped" // FixResultTypeFixed indicates that fix successfully fixed an execution FixResultTypeFixed FixResultType = "fixed" // FixResultTypeFailed indicates that fix attempted to fix an execution but failed to do so FixResultTypeFailed FixResultType = "failed" // HistoryExists asserts that history must exist if concrete execution exists HistoryExists Name = "history_exists" // InactiveDomainExists asserts that if domain status is not registered that it's inactive InactiveDomainExists Name = "inactive_domain_exists" // OpenCurrentExecution asserts that an open concrete execution must have a valid current execution OpenCurrentExecution Name = "open_current_execution" // ConcreteExecutionExists asserts that an open current execution must have a valid concrete execution ConcreteExecutionExists Name = "concrete_execution_exists" // StaleWorkflow checks for workflows that exist beyond their retention window, // implying a failed cleanup / lost timers / etc of some kind. StaleWorkflow Name = "stale_workflow" // CollectionMutableState is the collection of invariants relating to mutable state CollectionMutableState Collection = 0 // CollectionHistory is the collection of invariants relating to history CollectionHistory Collection = 1 // CollectionDomain is the collection of invariants relating to domain status CollectionDomain Collection = 2 // CollectionStale contains the stale workflow scanner CollectionStale Collection = 3 ) type ( // Name is the name of an invariant Name string // Collection is a type which indicates a collection of invariants Collection int // CheckResultType is the result type of running an invariant check CheckResultType string // FixResultType is the result type of running an invariant fix FixResultType string ) // Invariant represents an invariant of a single execution. // It can be used to check that the execution satisfies the invariant. // It can also be used to fix the invariant for an execution. type Invariant interface { Check(context.Context, interface{}) CheckResult Fix(context.Context, interface{}) FixResult Name() Name } // Manager represents a manager of several invariants. // It can be used to run a group of invariant checks or fixes. type Manager interface { RunChecks(context.Context, interface{}) ManagerCheckResult RunFixes(context.Context, interface{}) ManagerFixResult } // ManagerCheckResult is the result of running a list of checks type ManagerCheckResult struct { CheckResultType CheckResultType DeterminingInvariantType *Name CheckResults []CheckResult } // ManagerFixResult is the result of running a list of fixes type ManagerFixResult struct { FixResultType FixResultType DeterminingInvariantName *Name FixResults []FixResult } // CheckResult is the result of running Check. type CheckResult struct { CheckResultType CheckResultType InvariantName Name Info string InfoDetails string } // FixResult is the result of running Fix. type FixResult struct { FixResultType FixResultType InvariantName Name CheckResult CheckResult Info string InfoDetails string } // NamePtr returns a pointer to Name func NamePtr(t Name) *Name { return &t } ================================================ FILE: common/reconciliation/invariant/util.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" ) func checkBeforeFix( ctx context.Context, invariant Invariant, execution interface{}, ) (*FixResult, *CheckResult) { checkResult := invariant.Check(ctx, execution) if checkResult.CheckResultType == CheckResultTypeHealthy { return &FixResult{ FixResultType: FixResultTypeSkipped, InvariantName: invariant.Name(), CheckResult: checkResult, Info: "skipped fix because execution was healthy", }, nil } if checkResult.CheckResultType == CheckResultTypeFailed { return &FixResult{ FixResultType: FixResultTypeFailed, InvariantName: invariant.Name(), CheckResult: checkResult, Info: "failed fix because check failed", }, nil } return nil, &checkResult } // Open returns true if workflow state is open false if workflow is closed func Open(state int) bool { return state == persistence.WorkflowStateCreated || state == persistence.WorkflowStateRunning } // ExecutionOpen returns true if execution state is open false if workflow is closed func ExecutionOpen(execution interface{}) bool { return Open(getExecution(execution).State) } // getExecution returns base Execution func getExecution(execution interface{}) *entity.Execution { switch e := execution.(type) { case *entity.CurrentExecution: return &e.Execution case *entity.ConcreteExecution: return &e.Execution default: panic("unexpected execution type") } } // DeleteExecution deletes concrete execution and // current execution conditionally on matching runID. func DeleteExecution( ctx context.Context, exec interface{}, pr persistence.Retryer, dc cache.DomainCache, ) *FixResult { execution := getExecution(exec) domainName, errorDomainName := dc.GetDomainName(execution.DomainID) if errorDomainName != nil { return &FixResult{ FixResultType: FixResultTypeFailed, Info: "failed to fetch domainName", InfoDetails: errorDomainName.Error(), } } if err := pr.DeleteWorkflowExecution(ctx, &persistence.DeleteWorkflowExecutionRequest{ DomainID: execution.DomainID, WorkflowID: execution.WorkflowID, RunID: execution.RunID, DomainName: domainName, }); err != nil { return &FixResult{ FixResultType: FixResultTypeFailed, Info: "failed to delete concrete workflow execution", InfoDetails: err.Error(), } } if err := pr.DeleteCurrentWorkflowExecution(ctx, &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: execution.DomainID, WorkflowID: execution.WorkflowID, RunID: execution.RunID, DomainName: domainName, }); err != nil { return &FixResult{ FixResultType: FixResultTypeFailed, Info: "failed to delete current workflow execution", InfoDetails: err.Error(), } } return &FixResult{ FixResultType: FixResultTypeFixed, } } func validateCheckContext( ctx context.Context, invariantName Name, ) *CheckResult { if ctxErr := ctx.Err(); ctxErr != nil { return &CheckResult{ CheckResultType: CheckResultTypeFailed, InvariantName: invariantName, Info: "failed to check: context expired or cancelled", InfoDetails: ctxErr.Error(), } } return nil } func validateFixContext( ctx context.Context, invariantName Name, ) *FixResult { if ctxErr := ctx.Err(); ctxErr != nil { return &FixResult{ FixResultType: FixResultTypeFailed, InvariantName: invariantName, Info: "failed to check: context expired or cancelled", InfoDetails: ctxErr.Error(), } } return nil } ================================================ FILE: common/reconciliation/invariant/util_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "errors" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" ) type UtilSuite struct { *require.Assertions suite.Suite } func TestUtilSuite(t *testing.T) { suite.Run(t, new(UtilSuite)) } func (s *UtilSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *UtilSuite) TestDeleteExecution() { testCases := []struct { deleteConcreteErr error deleteCurrentErr error expectedFixResult *FixResult }{ { deleteConcreteErr: errors.New("error deleting concrete execution"), expectedFixResult: &FixResult{ FixResultType: FixResultTypeFailed, Info: "failed to delete concrete workflow execution", InfoDetails: "error deleting concrete execution", }, }, { deleteCurrentErr: errors.New("error deleting current execution"), expectedFixResult: &FixResult{ FixResultType: FixResultTypeFailed, Info: "failed to delete current workflow execution", InfoDetails: "error deleting current execution", }, }, { expectedFixResult: &FixResult{ FixResultType: FixResultTypeFixed, }, }, } ctrl := gomock.NewController(s.T()) mockDomainCache := cache.NewMockDomainCache(ctrl) for _, tc := range testCases { execManager := &mocks.ExecutionManager{} execManager.On("DeleteWorkflowExecution", mock.Anything, mock.Anything, mock.Anything).Return(tc.deleteConcreteErr).Once() if tc.deleteConcreteErr == nil { execManager.On("DeleteCurrentWorkflowExecution", mock.Anything, mock.Anything).Return(tc.deleteCurrentErr).Once() } pr := persistence.NewPersistenceRetryer(execManager, nil, common.CreatePersistenceRetryPolicy()) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-name", nil).AnyTimes() result := DeleteExecution(context.Background(), &entity.ConcreteExecution{}, pr, mockDomainCache) s.Equal(tc.expectedFixResult, result) } } func (s *UtilSuite) TestExecutionStillOpen() { testCases := []struct { getExecResp *persistence.GetWorkflowExecutionResponse getExecErr error expectError bool expectOpen bool }{ { getExecResp: nil, getExecErr: &types.EntityNotExistsError{}, expectError: false, expectOpen: false, }, { getExecResp: nil, getExecErr: errors.New("got error"), expectError: true, expectOpen: false, }, { getExecResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, }, }, getExecErr: nil, expectError: false, expectOpen: false, }, { getExecResp: &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, }, getExecErr: nil, expectError: false, expectOpen: true, }, } ctrl := gomock.NewController(s.T()) mockDomainCache := cache.NewMockDomainCache(ctrl) for _, tc := range testCases { execManager := &mocks.ExecutionManager{} execManager.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(tc.getExecResp, tc.getExecErr) pr := persistence.NewPersistenceRetryer(execManager, nil, common.CreatePersistenceRetryPolicy()) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-name", nil) open, err := ExecutionStillOpen(context.Background(), &entity.Execution{}, pr, mockDomainCache) if tc.expectError { s.Error(err) } else { s.NoError(err) } if tc.expectOpen { s.True(open) } else { s.False(open) } } } func (s *UtilSuite) TestExecutionStillExists() { testCases := []struct { getExecResp *persistence.GetWorkflowExecutionResponse getExecErr error expectError bool expectExists bool }{ { getExecResp: &persistence.GetWorkflowExecutionResponse{}, getExecErr: nil, expectError: false, expectExists: true, }, { getExecResp: nil, getExecErr: &types.EntityNotExistsError{}, expectError: false, expectExists: false, }, { getExecResp: nil, getExecErr: errors.New("got error"), expectError: true, expectExists: false, }, } ctrl := gomock.NewController(s.T()) mockDomainCache := cache.NewMockDomainCache(ctrl) for _, tc := range testCases { execManager := &mocks.ExecutionManager{} execManager.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(tc.getExecResp, tc.getExecErr) pr := persistence.NewPersistenceRetryer(execManager, nil, common.CreatePersistenceRetryPolicy()) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-name", nil) exists, err := ExecutionStillExists(context.Background(), &entity.Execution{}, pr, mockDomainCache) if tc.expectError { s.Error(err) } else { s.NoError(err) } if tc.expectExists { s.True(exists) } else { s.False(exists) } } } ================================================ FILE: common/reconciliation/store/blobstoreIterator.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package store import ( "bytes" "context" "encoding/json" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/reconciliation/entity" ) type ( blobstoreIterator struct { itr pagination.Iterator } ) // NewBlobstoreIterator constructs a new iterator backed by blobstore. func NewBlobstoreIterator( ctx context.Context, client blobstore.Client, keys Keys, entity entity.Entity, ) ScanOutputIterator { return &blobstoreIterator{ itr: pagination.NewIterator(ctx, keys.MinPage, getBlobstoreFetchPageFn(client, keys, entity)), } } // Next returns the next ScanOutputEntity func (i *blobstoreIterator) Next() (*ScanOutputEntity, error) { exec, err := i.itr.Next() if exec != nil { return exec.(*ScanOutputEntity), err } return nil, err } // HasNext returns true if there is a next ScanOutputEntity false otherwise func (i *blobstoreIterator) HasNext() bool { return i.itr.HasNext() } func getBlobstoreFetchPageFn( client blobstore.Client, keys Keys, entity entity.Entity, ) pagination.FetchFn { return func(ctx context.Context, token pagination.PageToken) (pagination.Page, error) { index := token.(int) key := pageNumberToKey(keys.UUID, keys.Extension, index) req := &blobstore.GetRequest{ Key: key, } resp, err := client.Get(ctx, req) if err != nil { return pagination.Page{}, err } parts := bytes.Split(resp.Blob.Body, SeparatorToken) var executions []pagination.Entity for _, p := range parts { if len(p) == 0 { continue } soe, err := deserialize(p, entity) if err != nil { return pagination.Page{}, err } executions = append(executions, soe) } var nextPageToken interface{} = index + 1 if nextPageToken.(int) > keys.MaxPage { nextPageToken = nil } return pagination.Page{ CurrentToken: token, NextToken: nextPageToken, Entities: executions, }, nil } } func deserialize(data []byte, blob entity.Entity) (*ScanOutputEntity, error) { soe := &ScanOutputEntity{ Execution: blob.Clone(), } if err := json.Unmarshal(data, soe); err != nil { return nil, err } if err := soe.Execution.(entity.Entity).Validate(); err != nil { return nil, err } return soe, nil } ================================================ FILE: common/reconciliation/store/blobstoreWriter.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package store import ( "bytes" "context" "encoding/json" "fmt" "time" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/pagination" ) const ( maxRetries = 3 initialRetryDelay = 2 * time.Second // Initial delay between retries maxRetryDelay = 30 * time.Second // Maximum delay between retries ) type ( blobstoreWriter struct { writer pagination.Writer uuid string extension Extension } ) // NewBlobstoreWriter constructs a new blobstore writer func NewBlobstoreWriter( uuid string, extension Extension, client blobstore.Client, flushThreshold int, ) ExecutionWriter { // Set a longer expiration interval than timeout for the entire retry process totalRetryDuration := 2 * Timeout retryPolicy := backoff.NewExponentialRetryPolicy(initialRetryDelay) retryPolicy.SetMaximumInterval(maxRetryDelay) retryPolicy.SetExpirationInterval(totalRetryDuration) // Setting the attempts to 3 as a precaution. If we don't see any significant latency we can remove this config. retryPolicy.SetMaximumAttempts(maxRetries) throttlePolicy := backoff.NewExponentialRetryPolicy(initialRetryDelay) throttlePolicy.SetMaximumInterval(maxRetryDelay) throttlePolicy.SetExpirationInterval(totalRetryDuration) return &blobstoreWriter{ writer: pagination.NewWriter( getBlobstoreWriteFn(uuid, extension, client, retryPolicy, throttlePolicy), getBlobstoreShouldFlushFn(flushThreshold), 0), uuid: uuid, extension: extension, } } // Add adds an entity to blobstore writer func (bw *blobstoreWriter) Add(e interface{}) error { return bw.writer.Add(e) } // Flush flushes contents of writer to blobstore. // Only triggers flush if page contains some contents. func (bw *blobstoreWriter) Flush() error { return bw.writer.FlushIfNotEmpty() } // FlushedKeys returns the keys that have been successfully flushed. // Returns nil if no keys have been flushed. func (bw *blobstoreWriter) FlushedKeys() *Keys { if len(bw.writer.FlushedPages()) == 0 { return nil } return &Keys{ UUID: bw.uuid, MinPage: bw.writer.FirstFlushedPage().(int), MaxPage: bw.writer.LastFlushedPage().(int), Extension: bw.extension, } } func getBlobstoreWriteFn( uuid string, extension Extension, client blobstore.Client, retryPolicy backoff.RetryPolicy, throttlePolicy backoff.RetryPolicy, ) pagination.WriteFn { return func(page pagination.Page) (pagination.PageToken, error) { blobIndex := page.CurrentToken.(int) key := pageNumberToKey(uuid, extension, blobIndex) buffer := &bytes.Buffer{} for _, e := range page.Entities { data, err := json.Marshal(e) if err != nil { return nil, err } buffer.Write(data) buffer.Write(SeparatorToken) } req := &blobstore.PutRequest{ Key: key, Blob: blobstore.Blob{ Body: buffer.Bytes(), }, } operation := func(ctx context.Context) error { ctx, cancel := context.WithTimeout(ctx, Timeout) defer cancel() _, err := client.Put(ctx, req) return err } // Using the ThrottleRetry struct and its Do method to implement the retry logic in the getBlobstoreWriteFn. // This struct offers a way to retry operations with a specified policy and also to throttle retries if necessary. throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithThrottlePolicy(throttlePolicy), backoff.WithRetryableError(func(err error) bool { return true // assuming all errors are retryable }), ) // The Do method of throttleRetry is used to execute the operation with retries according to the policy. err := throttleRetry.Do(context.Background(), operation) if err != nil { return nil, err } return blobIndex + 1, nil } } func getBlobstoreShouldFlushFn( flushThreshold int, ) pagination.ShouldFlushFn { return func(page pagination.Page) bool { return len(page.Entities) > flushThreshold } } func pageNumberToKey(uuid string, extension Extension, pageNum int) string { return fmt.Sprintf("%v_%v.%v", uuid, pageNum, extension) } ================================================ FILE: common/reconciliation/store/blobstorewriter_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package store import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/blobstore/filestore" "github.com/uber/cadence/common/config" ) func TestBlobstoreWriter(t *testing.T) { type testCase struct { name string input string input2 string expectedErr bool } testCases := []testCase{ { name: "Normal case", input: "one", input2: "two", expectedErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { uuid := "test-uuid" extension := Extension("test") outputDir := t.TempDir() cfg := &config.FileBlobstore{ OutputDirectory: outputDir, } // Reusing the FilestoreClient from the other sister test in the same package. blobstoreClient, err := filestore.NewFilestoreClient(cfg) require.NoError(t, err) blobstoreWriter := NewBlobstoreWriter(uuid, extension, blobstoreClient, 10).(*blobstoreWriter) // Add data to the writer err = blobstoreWriter.Add(tc.input) err = blobstoreWriter.Add(tc.input2) if tc.expectedErr { assert.Error(t, err) return } assert.NoError(t, err) // Flush the writer to write data to the blobstore err = blobstoreWriter.Flush() assert.NoError(t, err) // Retrieve the keys of flushed data flushedKeys := blobstoreWriter.FlushedKeys() if flushedKeys == nil { t.Fatal("Expected flushedKeys to be not nil") } // Read back the data from the blobstore key := pageNumberToKey(uuid, extension, flushedKeys.MinPage) req := &blobstore.GetRequest{Key: key} ctx := context.Background() resp, err := blobstoreClient.Get(ctx, req) assert.NoError(t, err) // Verify the contents assert.Equal(t, string(resp.Blob.Body), "\"one\"\r\n\"two\"\r\n") }) } } ================================================ FILE: common/reconciliation/store/mocks.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: types.go // // Generated by this command: // // mockgen -package store -source types.go -destination mocks.go -self_package github.com/uber/cadence/common/reconciliation/store // // Package store is a generated GoMock package. package store import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockScanOutputIterator is a mock of ScanOutputIterator interface. type MockScanOutputIterator struct { ctrl *gomock.Controller recorder *MockScanOutputIteratorMockRecorder isgomock struct{} } // MockScanOutputIteratorMockRecorder is the mock recorder for MockScanOutputIterator. type MockScanOutputIteratorMockRecorder struct { mock *MockScanOutputIterator } // NewMockScanOutputIterator creates a new mock instance. func NewMockScanOutputIterator(ctrl *gomock.Controller) *MockScanOutputIterator { mock := &MockScanOutputIterator{ctrl: ctrl} mock.recorder = &MockScanOutputIteratorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockScanOutputIterator) EXPECT() *MockScanOutputIteratorMockRecorder { return m.recorder } // HasNext mocks base method. func (m *MockScanOutputIterator) HasNext() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasNext") ret0, _ := ret[0].(bool) return ret0 } // HasNext indicates an expected call of HasNext. func (mr *MockScanOutputIteratorMockRecorder) HasNext() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasNext", reflect.TypeOf((*MockScanOutputIterator)(nil).HasNext)) } // Next mocks base method. func (m *MockScanOutputIterator) Next() (*ScanOutputEntity, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Next") ret0, _ := ret[0].(*ScanOutputEntity) ret1, _ := ret[1].(error) return ret0, ret1 } // Next indicates an expected call of Next. func (mr *MockScanOutputIteratorMockRecorder) Next() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Next", reflect.TypeOf((*MockScanOutputIterator)(nil).Next)) } // MockExecutionWriter is a mock of ExecutionWriter interface. type MockExecutionWriter struct { ctrl *gomock.Controller recorder *MockExecutionWriterMockRecorder isgomock struct{} } // MockExecutionWriterMockRecorder is the mock recorder for MockExecutionWriter. type MockExecutionWriterMockRecorder struct { mock *MockExecutionWriter } // NewMockExecutionWriter creates a new mock instance. func NewMockExecutionWriter(ctrl *gomock.Controller) *MockExecutionWriter { mock := &MockExecutionWriter{ctrl: ctrl} mock.recorder = &MockExecutionWriterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockExecutionWriter) EXPECT() *MockExecutionWriterMockRecorder { return m.recorder } // Add mocks base method. func (m *MockExecutionWriter) Add(arg0 any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Add", arg0) ret0, _ := ret[0].(error) return ret0 } // Add indicates an expected call of Add. func (mr *MockExecutionWriterMockRecorder) Add(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockExecutionWriter)(nil).Add), arg0) } // Flush mocks base method. func (m *MockExecutionWriter) Flush() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Flush") ret0, _ := ret[0].(error) return ret0 } // Flush indicates an expected call of Flush. func (mr *MockExecutionWriterMockRecorder) Flush() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Flush", reflect.TypeOf((*MockExecutionWriter)(nil).Flush)) } // FlushedKeys mocks base method. func (m *MockExecutionWriter) FlushedKeys() *Keys { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FlushedKeys") ret0, _ := ret[0].(*Keys) return ret0 } // FlushedKeys indicates an expected call of FlushedKeys. func (mr *MockExecutionWriterMockRecorder) FlushedKeys() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushedKeys", reflect.TypeOf((*MockExecutionWriter)(nil).FlushedKeys)) } ================================================ FILE: common/reconciliation/store/types.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination mocks.go -self_package github.com/uber/cadence/common/reconciliation/store package store import ( "time" "github.com/uber/cadence/common/reconciliation/invariant" ) const ( // SkippedExtension is the extension for files which contain skips SkippedExtension Extension = "skipped" // FailedExtension is the extension for files which contain failures FailedExtension Extension = "failed" // FixedExtension is the extension for files which contain fixes FixedExtension Extension = "fixed" // CorruptedExtension is the extension for files which contain corruptions CorruptedExtension Extension = "corrupted" ) var ( // SeparatorToken is used to separate entries written to blobstore SeparatorToken = []byte("\r\n") // Timeout is the timeout used for blobstore requests Timeout = time.Second * 10 ) type ( // Extension is the type which indicates the file extension type Extension string // Keys indicate the keys which were uploaded during a scan or fix. // Keys are constructed as uuid_page.extension. MinPage and MaxPage are // both inclusive and pages are sequential, meaning from this struct all pages can be deterministically constructed. Keys struct { UUID string MinPage int MaxPage int Extension Extension } ) // The following are serializable types which get output by Scan and Fix to durable sinks. type ( // ScanOutputEntity represents a single execution that should be durably recorded by Scan. ScanOutputEntity struct { Execution interface{} Result invariant.ManagerCheckResult } // FixOutputEntity represents a single execution that should be durably recorded by fix. // It contains the ScanOutputEntity that was given as input to fix. FixOutputEntity struct { Execution interface{} Input ScanOutputEntity Result invariant.ManagerFixResult } ) type ( // ScanOutputIterator gets ScanOutputEntities from underlying store ScanOutputIterator interface { // Next returns the next ScanOutputEntity found. Any error reading from underlying store // or converting store entry to ScanOutputEntity will result in an error after which iterator cannot be used. Next() (*ScanOutputEntity, error) // HasNext indicates if the iterator has a next element. If HasNext is true it is // guaranteed that Next will return a nil error and non-nil ScanOutputEntity. HasNext() bool } // ExecutionWriter is used to write entities (FixOutputEntity or ScanOutputEntity) to blobstore ExecutionWriter interface { Add(interface{}) error Flush() error FlushedKeys() *Keys } ) ================================================ FILE: common/reconciliation/store/writerIterator_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package store import ( "context" "fmt" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/uber/cadence/common" "github.com/uber/cadence/common/blobstore/filestore" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/fetcher" ) var ( validBranchToken = []byte{89, 11, 0, 10, 0, 0, 0, 12, 116, 101, 115, 116, 45, 116, 114, 101, 101, 45, 105, 100, 11, 0, 20, 0, 0, 0, 14, 116, 101, 115, 116, 45, 98, 114, 97, 110, 99, 104, 45, 105, 100, 0} executionPageSize = 10 testShardID = 1 ) func TestWriterIterator(t *testing.T) { testCases := []struct { name string pages int countPerPage int expectedCount int // Add this field }{ {"StandardCase", 10, 10, 100}, {"FewPages", 3, 15, 45}, // Set expectedCount as pages * countPerPage {"SingleLargePage", 1, 100, 100}, {"ManySmallPages", 20, 2, 40}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { assertions := require.New(t) pr := persistence.NewPersistenceRetryer(getMockExecutionManager(tc.pages, tc.countPerPage), nil, common.CreatePersistenceRetryPolicy()) pItr := fetcher.ConcreteExecutionIterator(context.Background(), pr, executionPageSize) uuid := "uuid" extension := Extension("test") outputDir := t.TempDir() cfg := &config.FileBlobstore{ OutputDirectory: outputDir, } blobstore, err := filestore.NewFilestoreClient(cfg) assertions.NoError(err) blobstoreWriter := NewBlobstoreWriter(uuid, extension, blobstore, 10) var outputs []*ScanOutputEntity for pItr.HasNext() { exec, err := pItr.Next() assertions.NoError(err) soe := &ScanOutputEntity{ Execution: exec, } outputs = append(outputs, soe) assertions.NoError(blobstoreWriter.Add(soe)) } assertions.NoError(blobstoreWriter.Flush()) assertions.Len(outputs, tc.pages*tc.countPerPage) assertions.False(pItr.HasNext()) _, err = pItr.Next() assertions.Equal(pagination.ErrIteratorFinished, err) flushedKeys := blobstoreWriter.FlushedKeys() assertions.Equal(uuid, flushedKeys.UUID) assertions.Equal(0, flushedKeys.MinPage) if tc.expectedCount > 0 { assertions.Equal((tc.expectedCount-1)/10, flushedKeys.MaxPage) } else { assertions.Equal(0, flushedKeys.MaxPage) } assertions.Equal(Extension("test"), flushedKeys.Extension) blobstoreItr := NewBlobstoreIterator(context.Background(), blobstore, *flushedKeys, &entity.ConcreteExecution{}) i := 0 for blobstoreItr.HasNext() { scanOutputEntity, err := blobstoreItr.Next() if err != nil { assertions.Fail(fmt.Sprintf("Error iterating blobstore: %v", err)) break } if scanOutputEntity == nil { break // No more items } if i < len(outputs) { // Compare the Execution field of ScanOutputEntity with the expected ConcreteExecution assertions.Equal(outputs[i].Execution, scanOutputEntity.Execution) } i++ } assertions.Equal(tc.expectedCount, i, "Number of items from blobstore iterator mismatch") }) } } func getMockExecutionManager(pages int, countPerPage int) persistence.ExecutionManager { execManager := &mocks.ExecutionManager{} for i := 0; i < pages; i++ { req := &persistence.ListConcreteExecutionsRequest{ PageToken: []byte(fmt.Sprintf("token_%v", i)), PageSize: executionPageSize, } if i == 0 { req.PageToken = nil } resp := &persistence.ListConcreteExecutionsResponse{ Executions: getExecutions(countPerPage), PageToken: []byte(fmt.Sprintf("token_%v", i+1)), } if i == pages-1 { resp.PageToken = nil } execManager.On("ListConcreteExecutions", mock.Anything, req).Return(resp, nil) execManager.On("GetShardID").Return(testShardID) } return execManager } func getExecutions(count int) []*persistence.ListConcreteExecutionsEntity { var result []*persistence.ListConcreteExecutionsEntity for i := 0; i < count; i++ { execution := &persistence.ListConcreteExecutionsEntity{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), BranchToken: validBranchToken, State: 0, }, } if i%2 == 0 { execution.ExecutionInfo.BranchToken = nil execution.VersionHistories = &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: validBranchToken, }, }, } } result = append(result, execution) } return result } ================================================ FILE: common/resource/params.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package resource import ( "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/authorization" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/configstore" es "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" persistenceClient "github.com/uber/cadence/common/persistence/client" "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" "github.com/uber/cadence/service/worker/diagnostics/invariant" ) type ( // Params holds the set of parameters needed to initialize common service resources Params struct { Name string InstanceID string Logger log.Logger ThrottledLogger log.Logger HostName string GetIsolationGroups func() []string MetricScope tally.Scope MembershipResolver membership.Resolver HashRings map[string]membership.SingleProvider RPCFactory rpc.Factory PProfInitializer common.PProfInitializer PersistenceConfig config.Persistence ClusterMetadata cluster.Metadata ReplicatorConfig config.Replicator MetricsClient metrics.Client MessagingClient messaging.Client BlobstoreClient blobstore.Client ESClient es.GenericClient ESConfig *config.ElasticSearchConfig // RPC configuration RPCConfig config.RPC DynamicConfig dynamicconfig.Client ClusterRedirectionPolicy *config.ClusterRedirectionPolicy PublicClient workflowserviceclient.Interface ArchivalMetadata archiver.ArchivalMetadata ArchiverProvider provider.ArchiverProvider Authorizer authorization.Authorizer // NOTE: this can be nil. If nil, AccessControlledHandlerImpl will initiate one with config.Authorization AuthorizationConfig config.Authorization // NOTE: empty(default) struct will get a authorization.NoopAuthorizer IsolationGroupStore configstore.Client // This can be nil, the default config store will be created if so IsolationGroupState isolationgroup.State // This can be nil, the default state store will be chosen if so PinotConfig *config.PinotVisibilityConfig KafkaConfig config.KafkaConfig PinotClient pinot.GenericClient OSClient es.GenericClient OSConfig *config.ElasticSearchConfig AsyncWorkflowQueueProvider queue.Provider TimeSource clock.TimeSource // HistoryClientFn is used by integration tests to mock a history client HistoryClientFn func() history.Client // NewPersistenceBeanFn can be used to override the default persistence bean creation in unit tests to avoid DB setup NewPersistenceBeanFn func(persistenceClient.Factory, *persistenceClient.Params, *service.Config) (persistenceClient.Bean, error) DiagnosticsInvariants []invariant.Invariant // ShardDistributorMatchingConfig is the config for shard distributor executor client in matching service ShardDistributorMatchingConfig clientcommon.Config // DrainObserver is an optional observer that signals when this instance is // drained from service discovery. // It is used by shard-distributor executor clients to // gracefully stop processing during drains. DrainObserver clientcommon.DrainSignalObserver } ) ================================================ FILE: common/resource/resource_impl.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package resource import ( "math/rand" "sync/atomic" "time" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/yarpc" "github.com/uber/cadence/client" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/client/wrappers/retryable" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/configstore" csc "github.com/uber/cadence/common/dynamicconfig/configstore/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/isolationgroup/defaultisolationgroupstate" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" persistenceClient "github.com/uber/cadence/common/persistence/client" qrpc "github.com/uber/cadence/common/quotas/global/rpc" "github.com/uber/cadence/common/quotas/permember" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) func NewResourceFactory() ResourceFactory { return &resourceImplFactory{} } type resourceImplFactory struct{} func (*resourceImplFactory) NewResource( params *Params, serviceName string, serviceConfig *service.Config, ) (resource Resource, err error) { return New(params, serviceName, serviceConfig) } // Impl contains all common resources shared across frontend / matching / history / worker type Impl struct { status int32 // static infos numShards int serviceName string hostInfo membership.HostInfo metricsScope tally.Scope clusterMetadata cluster.Metadata // other common resources domainCache cache.DomainCache domainMetricsScopeCache cache.DomainMetricsScopeCache activeClusterMgr activecluster.Manager timeSource clock.TimeSource payloadSerializer persistence.PayloadSerializer metricsClient metrics.Client messagingClient messaging.Client blobstoreClient blobstore.Client archivalMetadata archiver.ArchivalMetadata archiverProvider provider.ArchiverProvider domainReplicationQueue domain.ReplicationQueue // membership infos membershipResolver membership.Resolver hashRings map[string]membership.Ring // internal services clients sdkClient workflowserviceclient.Interface frontendRawClient frontend.Client frontendClient frontend.Client matchingRawClient matching.Client matchingClient matching.Client historyRawClient history.Client historyClient history.Client shardDistributorRawClient sharddistributor.Client shardDistributorClient sharddistributor.Client shardDistributorExecutorRawClient executorclient.Client shardDistributorExecutorClient executorclient.Client clientBean client.Bean // persistence clients persistenceBean persistenceClient.Bean // hostName hostName string // loggers logger log.Logger throttledLogger log.Logger // for registering handlers dispatcher *yarpc.Dispatcher // internal vars pprofInitializer common.PProfInitializer runtimeMetricsReporter *metrics.RuntimeMetricsReporter rpcFactory rpc.Factory isolationGroups isolationgroup.State isolationGroupConfigStore configstore.Client asyncWorkflowQueueProvider queue.Provider ratelimiterAggregatorClient qrpc.Client } var _ Resource = (*Impl)(nil) // New create a new resource containing common dependencies func New( params *Params, serviceName string, serviceConfig *service.Config, ) (impl *Impl, retError error) { hostname := params.HostName logger := params.Logger throttledLogger := log.NewThrottledLogger(logger, serviceConfig.ThrottledLoggerMaxRPS) numShards := params.PersistenceConfig.NumHistoryShards dispatcher := params.RPCFactory.GetDispatcher() membershipResolver := params.MembershipResolver ensureGetAllIsolationGroupsFnIsSet(params) dynamicCollection := dynamicconfig.NewCollection( params.DynamicConfig, logger, dynamicproperties.ClusterNameFilter(params.ClusterMetadata.GetCurrentClusterName()), ) clientBean, err := client.NewClientBean( client.NewRPCClientFactory( params.RPCFactory, membershipResolver, params.MetricsClient, dynamicCollection, numShards, params.GetIsolationGroups, logger, ), params.RPCFactory.GetDispatcher(), params.ClusterMetadata, ) if err != nil { return nil, err } newPersistenceBeanFn := persistenceClient.NewBeanFromFactory if params.NewPersistenceBeanFn != nil { newPersistenceBeanFn = params.NewPersistenceBeanFn } persistenceBean, err := newPersistenceBeanFn(persistenceClient.NewFactory( ¶ms.PersistenceConfig, func() float64 { return permember.PerMember( serviceName, float64(serviceConfig.PersistenceGlobalMaxQPS()), float64(serviceConfig.PersistenceMaxQPS()), membershipResolver, ) }, params.ClusterMetadata.GetCurrentClusterName(), params.MetricsClient, logger, persistence.NewDynamicConfiguration(dynamicCollection), ), &persistenceClient.Params{ PersistenceConfig: params.PersistenceConfig, MetricsClient: params.MetricsClient, MessagingClient: params.MessagingClient, ESClient: params.ESClient, ESConfig: params.ESConfig, PinotConfig: params.PinotConfig, PinotClient: params.PinotClient, OSClient: params.OSClient, OSConfig: params.OSConfig, }, serviceConfig) if err != nil { return nil, err } domainCache := cache.NewDomainCache( persistenceBean.GetDomainManager(), params.ClusterMetadata, params.MetricsClient, logger, cache.WithTimeSource(params.TimeSource), ) activeClusterMgr, err := activecluster.NewManager( domainCache.GetDomainByID, params.MetricsClient, logger, persistenceBean, numShards, ) if err != nil { return nil, err } domainMetricsScopeCache := cache.NewDomainMetricsScopeCache() domainReplicationQueue := domain.NewReplicationQueue( persistenceBean.GetDomainReplicationQueueManager(), params.ClusterMetadata.GetCurrentClusterName(), params.MetricsClient, logger, ) frontendRawClient := clientBean.GetFrontendClient() frontendClient := retryable.NewFrontendClient( frontendRawClient, common.CreateFrontendServiceRetryPolicy(), serviceConfig.IsErrorRetryableFunction, ) matchingRawClient, err := clientBean.GetMatchingClient(domainCache.GetDomainName) if err != nil { return nil, err } matchingClient := retryable.NewMatchingClient( matchingRawClient, common.CreateMatchingServiceRetryPolicy(), serviceConfig.IsErrorRetryableFunction, ) shardDistributorRawClient := clientBean.GetShardDistributorClient() // If the raw client is nil, then the client bean is not configured to provide a shard distributor client, so we // do not wrap and provide a retryable client var shardDistributorClient sharddistributor.Client if shardDistributorRawClient == nil { shardDistributorClient = nil } else { shardDistributorClient = retryable.NewShardDistributorClient( shardDistributorRawClient, common.CreateShardDistributorServiceRetryPolicy(), serviceConfig.IsErrorRetryableFunction, ) } shardDistributorExecutorRawClient := clientBean.GetShardDistributorExecutorClient() var shardDistributorExecutorClient executorclient.Client if shardDistributorExecutorRawClient == nil { shardDistributorExecutorClient = nil } else { shardDistributorExecutorClient = retryable.NewShardDistributorExecutorClient( shardDistributorExecutorRawClient, common.CreateShardDistributorServiceRetryPolicy(), serviceConfig.IsErrorRetryableFunction, ) } var historyRawClient history.Client if params.HistoryClientFn != nil { logger.Debug("Using history client from HistoryClientFn") historyRawClient = params.HistoryClientFn() } else { logger.Debug("Using history client from bean") historyRawClient = clientBean.GetHistoryClient() } historyClient := retryable.NewHistoryClient( historyRawClient, common.CreateHistoryServiceRetryPolicy(), serviceConfig.IsErrorRetryableFunction, ) historyArchiverBootstrapContainer := &archiver.HistoryBootstrapContainer{ HistoryV2Manager: persistenceBean.GetHistoryManager(), Logger: logger, MetricsClient: params.MetricsClient, ClusterMetadata: params.ClusterMetadata, DomainCache: domainCache, } visibilityArchiverBootstrapContainer := &archiver.VisibilityBootstrapContainer{ Logger: logger, MetricsClient: params.MetricsClient, ClusterMetadata: params.ClusterMetadata, DomainCache: domainCache, } if err := params.ArchiverProvider.RegisterBootstrapContainer( serviceName, historyArchiverBootstrapContainer, visibilityArchiverBootstrapContainer, ); err != nil { return nil, err } isolationGroupStore := createConfigStoreOrDefault(params, dynamicCollection) isolationGroupState, err := ensureIsolationGroupStateHandlerOrDefault( params, dynamicCollection, domainCache, isolationGroupStore, ) if err != nil { return nil, err } ratelimiterAggs := qrpc.New( historyRawClient, // no retries, will retry internally if needed clientBean.GetHistoryPeers(), logger, params.MetricsClient, ) impl = &Impl{ status: common.DaemonStatusInitialized, // static infos numShards: numShards, serviceName: params.Name, metricsScope: params.MetricScope, clusterMetadata: params.ClusterMetadata, // other common resources domainCache: domainCache, domainMetricsScopeCache: domainMetricsScopeCache, activeClusterMgr: activeClusterMgr, timeSource: clock.NewRealTimeSource(), payloadSerializer: persistence.NewPayloadSerializer(), metricsClient: params.MetricsClient, messagingClient: params.MessagingClient, blobstoreClient: params.BlobstoreClient, archivalMetadata: params.ArchivalMetadata, archiverProvider: params.ArchiverProvider, domainReplicationQueue: domainReplicationQueue, // membership infos membershipResolver: membershipResolver, // internal services clients sdkClient: params.PublicClient, frontendRawClient: frontendRawClient, frontendClient: frontendClient, matchingRawClient: matchingRawClient, matchingClient: matchingClient, historyRawClient: historyRawClient, historyClient: historyClient, shardDistributorRawClient: shardDistributorRawClient, shardDistributorClient: shardDistributorClient, shardDistributorExecutorRawClient: shardDistributorExecutorRawClient, shardDistributorExecutorClient: shardDistributorExecutorClient, clientBean: clientBean, // persistence clients persistenceBean: persistenceBean, // hostname hostName: hostname, // loggers logger: logger, throttledLogger: throttledLogger, // for registering handlers dispatcher: dispatcher, // internal vars pprofInitializer: params.PProfInitializer, runtimeMetricsReporter: metrics.NewRuntimeMetricsReporter( params.MetricScope, time.Minute, logger, params.InstanceID, ), rpcFactory: params.RPCFactory, isolationGroups: isolationGroupState, isolationGroupConfigStore: isolationGroupStore, // can be nil where persistence is not available asyncWorkflowQueueProvider: params.AsyncWorkflowQueueProvider, ratelimiterAggregatorClient: ratelimiterAggs, } return impl, nil } // Start all resources func (h *Impl) Start() { if !atomic.CompareAndSwapInt32( &h.status, common.DaemonStatusInitialized, common.DaemonStatusStarted, ) { return } h.metricsScope.Counter(metrics.RestartCount).Inc(1) h.runtimeMetricsReporter.Start() if err := h.pprofInitializer.Start(); err != nil { h.logger.WithTags(tag.Error(err)).Fatal("fail to start PProf") } if err := h.rpcFactory.Start(h.membershipResolver); err != nil { h.logger.WithTags(tag.Error(err)).Fatal("fail to start RPC factory") } if err := h.dispatcher.Start(); err != nil { h.logger.WithTags(tag.Error(err)).Fatal("fail to start dispatcher") } h.membershipResolver.Start() h.domainCache.Start() h.domainMetricsScopeCache.Start() hostInfo, err := h.membershipResolver.WhoAmI() if err != nil { h.logger.WithTags(tag.Error(err)).Fatal("fail to get host info from membership monitor") } h.hostInfo = hostInfo if h.isolationGroupConfigStore != nil { h.isolationGroupConfigStore.Start() } // The service is now started up h.logger.Info("service started") // seed the random generator once for this service rand.Seed(time.Now().UTC().UnixNano()) } // Stop stops all resources func (h *Impl) Stop() { if !atomic.CompareAndSwapInt32( &h.status, common.DaemonStatusStarted, common.DaemonStatusStopped, ) { return } h.domainCache.Stop() h.domainMetricsScopeCache.Stop() h.membershipResolver.Stop() if err := h.dispatcher.Stop(); err != nil { h.logger.WithTags(tag.Error(err)).Error("failed to stop dispatcher") } h.rpcFactory.Stop() h.runtimeMetricsReporter.Stop() h.persistenceBean.Close() if h.isolationGroupConfigStore != nil { h.isolationGroupConfigStore.Stop() } h.isolationGroups.Stop() } // GetServiceName return service name func (h *Impl) GetServiceName() string { return h.serviceName } // GetHostInfo return host info func (h *Impl) GetHostInfo() membership.HostInfo { return h.hostInfo } // GetClusterMetadata return cluster metadata func (h *Impl) GetClusterMetadata() cluster.Metadata { return h.clusterMetadata } // other common resources // GetDomainCache return domain cache func (h *Impl) GetDomainCache() cache.DomainCache { return h.domainCache } // GetDomainMetricsScopeCache return domainMetricsScope cache func (h *Impl) GetDomainMetricsScopeCache() cache.DomainMetricsScopeCache { return h.domainMetricsScopeCache } // GetActiveClusterManager return active cluster manager func (h *Impl) GetActiveClusterManager() activecluster.Manager { return h.activeClusterMgr } // GetTimeSource return time source func (h *Impl) GetTimeSource() clock.TimeSource { return h.timeSource } // GetPayloadSerializer return binary payload serializer func (h *Impl) GetPayloadSerializer() persistence.PayloadSerializer { return h.payloadSerializer } // GetMetricsClient return metrics client func (h *Impl) GetMetricsClient() metrics.Client { return h.metricsClient } // GetMessagingClient return messaging client func (h *Impl) GetMessagingClient() messaging.Client { return h.messagingClient } // GetBlobstoreClient returns blobstore client func (h *Impl) GetBlobstoreClient() blobstore.Client { return h.blobstoreClient } // GetArchivalMetadata return archival metadata func (h *Impl) GetArchivalMetadata() archiver.ArchivalMetadata { return h.archivalMetadata } // GetArchiverProvider return archival provider func (h *Impl) GetArchiverProvider() provider.ArchiverProvider { return h.archiverProvider } // GetDomainReplicationQueue return domain replication queue func (h *Impl) GetDomainReplicationQueue() domain.ReplicationQueue { return h.domainReplicationQueue } // GetMembershipResolver return the membership resolver func (h *Impl) GetMembershipResolver() membership.Resolver { return h.membershipResolver } // internal services clients // GetSDKClient return sdk client func (h *Impl) GetSDKClient() workflowserviceclient.Interface { return h.sdkClient } // GetFrontendRawClient return frontend client without retry policy func (h *Impl) GetFrontendRawClient() frontend.Client { return h.frontendRawClient } // GetFrontendClient return frontend client with retry policy func (h *Impl) GetFrontendClient() frontend.Client { return h.frontendClient } // GetMatchingRawClient return matching client without retry policy func (h *Impl) GetMatchingRawClient() matching.Client { return h.matchingRawClient } // GetMatchingClient return matching client with retry policy func (h *Impl) GetMatchingClient() matching.Client { return h.matchingClient } // GetHistoryRawClient return history client without retry policy func (h *Impl) GetHistoryRawClient() history.Client { return h.historyRawClient } // GetHistoryClient return history client with retry policy func (h *Impl) GetHistoryClient() history.Client { return h.historyClient } // GetShardDistributorExecutorRawClient return client for sharddistributor executor func (h *Impl) GetShardDistributorExecutorRawClient() executorclient.Client { return h.shardDistributorExecutorRawClient } // GetShardDistributorExecutorClient return client for sharddistributor executor func (h *Impl) GetShardDistributorExecutorClient() executorclient.Client { return h.shardDistributorExecutorRawClient } func (h *Impl) GetRatelimiterAggregatorsClient() qrpc.Client { return h.ratelimiterAggregatorClient } // GetRemoteAdminClient return remote admin client for given cluster name func (h *Impl) GetRemoteAdminClient( cluster string, ) (admin.Client, error) { return h.clientBean.GetRemoteAdminClient(cluster) } // GetRemoteFrontendClient return remote frontend client for given cluster name func (h *Impl) GetRemoteFrontendClient( cluster string, ) (frontend.Client, error) { return h.clientBean.GetRemoteFrontendClient(cluster) } // GetClientBean return RPC client bean func (h *Impl) GetClientBean() client.Bean { return h.clientBean } // persistence clients // GetMetadataManager return metadata manager func (h *Impl) GetDomainManager() persistence.DomainManager { return h.persistenceBean.GetDomainManager() } // GetDomainAuditManager return domain audit manager func (h *Impl) GetDomainAuditManager() persistence.DomainAuditManager { return h.persistenceBean.GetDomainAuditManager() } // GetTaskManager return task manager func (h *Impl) GetTaskManager() persistence.TaskManager { return h.persistenceBean.GetTaskManager() } // GetVisibilityManager return visibility manager func (h *Impl) GetVisibilityManager() persistence.VisibilityManager { return h.persistenceBean.GetVisibilityManager() } // GetShardManager return shard manager func (h *Impl) GetShardManager() persistence.ShardManager { return h.persistenceBean.GetShardManager() } // GetHistoryManager return history manager func (h *Impl) GetHistoryManager() persistence.HistoryManager { return h.persistenceBean.GetHistoryManager() } // GetExecutionManager return execution manager for given shard ID func (h *Impl) GetExecutionManager(shardID int) (persistence.ExecutionManager, error) { return h.persistenceBean.GetExecutionManager(shardID) } // GetPersistenceBean return persistence bean func (h *Impl) GetPersistenceBean() persistenceClient.Bean { return h.persistenceBean } func (h *Impl) GetHostName() string { return h.hostName } // loggers // GetLogger return logger func (h *Impl) GetLogger() log.Logger { return h.logger } // GetThrottledLogger return throttled logger func (h *Impl) GetThrottledLogger() log.Logger { return h.throttledLogger } // GetDispatcher return YARPC dispatcher, used for registering handlers func (h *Impl) GetDispatcher() *yarpc.Dispatcher { return h.dispatcher } // GetIsolationGroupState returns the isolationGroupState func (h *Impl) GetIsolationGroupState() isolationgroup.State { return h.isolationGroups } // GetIsolationGroupStore returns the isolation group configuration store or nil func (h *Impl) GetIsolationGroupStore() configstore.Client { return h.isolationGroupConfigStore } // GetAsyncWorkflowQueueProvider returns the async workflow queue provider func (h *Impl) GetAsyncWorkflowQueueProvider() queue.Provider { return h.asyncWorkflowQueueProvider } // GetMetricsScope returns the tally scope for metrics reporting func (h *Impl) GetMetricsScope() tally.Scope { return h.metricsScope } // due to the config store being only available for some // persistence layers, *both* the configStoreClient and IsolationGroupState // will be optionally available func createConfigStoreOrDefault( params *Params, dc *dynamicconfig.Collection, ) configstore.Client { if params.IsolationGroupStore != nil { return params.IsolationGroupStore } cscConfig := &csc.ClientConfig{ PollInterval: dc.GetDurationProperty(dynamicproperties.IsolationGroupStateRefreshInterval)(), UpdateRetryAttempts: dc.GetIntProperty(dynamicproperties.IsolationGroupStateUpdateRetryAttempts)(), FetchTimeout: dc.GetDurationProperty(dynamicproperties.IsolationGroupStateFetchTimeout)(), UpdateTimeout: dc.GetDurationProperty(dynamicproperties.IsolationGroupStateUpdateTimeout)(), } cfgStoreClient, err := configstore.NewConfigStoreClient(cscConfig, ¶ms.PersistenceConfig, params.Logger, params.MetricsClient, persistence.GlobalIsolationGroupConfig) if err != nil { // not possible to create the client under some persistence configurations, so this is expected params.Logger.Warn("not instantiating Isolation group config store, this feature will not be enabled", tag.Error(err)) return nil } return cfgStoreClient } // Use the provided IsolationGroupStateHandler or the default one // due to the config store being only available for some // persistence layers, *both* the configStoreClient and IsolationGroupState // will be optionally available func ensureIsolationGroupStateHandlerOrDefault( params *Params, dc *dynamicconfig.Collection, domainCache cache.DomainCache, isolationGroupStore dynamicconfig.Client, ) (isolationgroup.State, error) { if params.IsolationGroupState != nil { return params.IsolationGroupState, nil } return defaultisolationgroupstate.NewDefaultIsolationGroupStateWatcherWithConfigStoreClient( params.Logger, dc, domainCache, isolationGroupStore, params.MetricsClient, params.GetIsolationGroups, ) } func ensureGetAllIsolationGroupsFnIsSet(params *Params) { if params.GetIsolationGroups == nil { params.GetIsolationGroups = func() []string { return []string{} } } } ================================================ FILE: common/resource/resource_impl_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package resource import ( "runtime/debug" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" persistenceClient "github.com/uber/cadence/common/persistence/client" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/common/service" ) func TestStartStop(t *testing.T) { defer func() { if r := recover(); r != nil { t.Fatalf("Panic: %v, stacktrace: %s", r, debug.Stack()) } }() ctrl := gomock.NewController(t) serviceName := "test-service" hostName := "test-host" metricsCl := metrics.NewNoopMetricsClient() logger := testlogger.New(t) dc := dynamicconfig.NewInMemoryClient() // membership resolver mocks memberRes := membership.NewMockResolver(ctrl) memberRes.EXPECT().MemberCount(gomock.Any()).Return(4, nil).AnyTimes() memberRes.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() memberRes.EXPECT().Unsubscribe(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() memberRes.EXPECT().Start().AnyTimes() memberRes.EXPECT().Stop().AnyTimes() selfHostInfo := membership.NewHostInfo("localhost:0") memberRes.EXPECT().WhoAmI().Return(selfHostInfo, nil).AnyTimes() // pprof mocks pprof := common.NewMockPProfInitializer(ctrl) pprof.EXPECT().Start().Return(nil).Times(1) // rpc mocks clusterMetadata := cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 1, PrimaryClusterName: "primary-cluster", CurrentClusterName: "primary-cluster", ClusterGroup: map[string]config.ClusterInformation{ "primary-cluster": {InitialFailoverVersion: 1, Enabled: true, RPCTransport: "tchannel", RPCAddress: "localhost:0"}, "secondary-cluster": {InitialFailoverVersion: 1, Enabled: true, RPCTransport: "tchannel", RPCAddress: "localhost:0"}, }, }, func(d string) bool { return false }, metricsCl, logger, ) directOutboundPCF := rpc.NewDirectPeerChooserFactory(serviceName, logger, metricsCl) directConnRetainFn := func(opts ...dynamicproperties.FilterOption) bool { return false } pcf := rpc.NewMockPeerChooserFactory(ctrl) peerChooser := rpc.NewMockPeerChooser(ctrl) peerChooser.EXPECT().Start().Return(nil).AnyTimes() peerChooser.EXPECT().Stop().Return(nil).AnyTimes() pcf.EXPECT().CreatePeerChooser(gomock.Any(), gomock.Any()).Return(peerChooser, nil).AnyTimes() ob := rpc.CombineOutbounds( rpc.NewCrossDCOutbounds(clusterMetadata.GetAllClusterInfo(), pcf), rpc.NewDirectOutboundBuilder(service.History, true, nil, directOutboundPCF, directConnRetainFn), rpc.NewDirectOutboundBuilder(service.Matching, true, nil, directOutboundPCF, directConnRetainFn), ) rpcFac := rpc.NewFactory(logger, rpc.Params{ ServiceName: serviceName, TChannelAddress: "localhost:0", GRPCAddress: "localhost:0", OutboundsBuilder: ob, }) // persistence mocks persistenceClientBean := persistenceClient.NewMockBean(ctrl) domainMgr := persistence.NewMockDomainManager(ctrl) domainMgr.EXPECT().GetMetadata(gomock.Any()).Return(&persistence.GetMetadataResponse{ NotificationVersion: 2, }, nil).AnyTimes() domain := &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ ID: "test-domain-id", Name: "test-domain-name", Data: map[string]string{"k1": "v1"}, }, Config: &persistence.DomainConfig{}, ReplicationConfig: &persistence.DomainReplicationConfig{}, NotificationVersion: 1, // should be less than notification version for this domain to be loaded by cache. } domainMgr.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(&persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{domain}, }, nil).AnyTimes() domainMgr.EXPECT().GetDomain(gomock.Any(), gomock.Any()).Return(domain, nil).AnyTimes() domainReplMgr := persistence.NewMockQueueManager(ctrl) historyMgr := persistence.NewMockHistoryManager(ctrl) taskMgr := persistence.NewMockTaskManager(ctrl) visMgr := persistence.NewMockVisibilityManager(ctrl) shardMgr := persistence.NewMockShardManager(ctrl) execMgr := persistence.NewMockExecutionManager(ctrl) persistenceClientBean.EXPECT().GetDomainManager().Return(domainMgr).AnyTimes() persistenceClientBean.EXPECT().GetTaskManager().Return(taskMgr).AnyTimes() persistenceClientBean.EXPECT().GetVisibilityManager().Return(visMgr).AnyTimes() persistenceClientBean.EXPECT().GetShardManager().Return(shardMgr).AnyTimes() persistenceClientBean.EXPECT().GetExecutionManager(gomock.Any()).Return(execMgr, nil).AnyTimes() persistenceClientBean.EXPECT().GetDomainReplicationQueueManager().Return(domainReplMgr).AnyTimes() persistenceClientBean.EXPECT().GetHistoryManager().Return(historyMgr).AnyTimes() persistenceClientBean.EXPECT().Close().Times(1) // archiver provider mocks archiveProvider := provider.NewMockArchiverProvider(ctrl) archiveProvider.EXPECT().RegisterBootstrapContainer(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) // params params := &Params{ Name: serviceName, HostName: hostName, PersistenceConfig: config.Persistence{ NumHistoryShards: 1024, DefaultStore: "nosql-store", DataStores: map[string]config.DataStore{ "nosql-store": { NoSQL: &config.NoSQL{}, }, }, }, ClusterMetadata: clusterMetadata, Logger: logger, MetricScope: tally.NoopScope, MetricsClient: metricsCl, RPCFactory: rpcFac, MembershipResolver: memberRes, DynamicConfig: dc, TimeSource: clock.NewRealTimeSource(), PProfInitializer: pprof, NewPersistenceBeanFn: func(persistenceClient.Factory, *persistenceClient.Params, *service.Config) (persistenceClient.Bean, error) { return persistenceClientBean, nil }, ArchiverProvider: archiveProvider, AsyncWorkflowQueueProvider: queue.NewMockProvider(ctrl), } // bare minimum service config svcCfg := &service.Config{ ThrottledLoggerMaxRPS: func(opts ...dynamicproperties.FilterOption) int { return 100 }, PersistenceGlobalMaxQPS: func(opts ...dynamicproperties.FilterOption) int { return 100 }, PersistenceMaxQPS: func(opts ...dynamicproperties.FilterOption) int { return 100 }, } i, err := New(params, serviceName, svcCfg) if err != nil { t.Fatal(err) } i.Start() i.Start() // should be no-op defer func() { i.Stop() i.Stop() // should be no-op goleak.VerifyNone(t) }() time.Sleep(100 * time.Millisecond) // wait for go routines to start // validations assert.Equal(t, serviceName, i.GetServiceName()) assert.Equal(t, selfHostInfo, i.GetHostInfo()) gotDomain, err := i.GetDomainCache().GetDomainByID("test-domain-id") assert.NoError(t, err) assert.Equal(t, domain.Info, gotDomain.GetInfo()) assert.NotNil(t, i.GetDomainMetricsScopeCache()) assert.NotNil(t, i.GetTimeSource()) assert.NotNil(t, i.GetPayloadSerializer()) assert.Equal(t, metricsCl, i.GetMetricsClient()) assert.Equal(t, params.MessagingClient, i.GetMessagingClient()) assert.Equal(t, params.BlobstoreClient, i.GetBlobstoreClient()) assert.Equal(t, params.ArchivalMetadata, i.GetArchivalMetadata()) assert.Equal(t, archiveProvider, i.GetArchiverProvider()) assert.NotNil(t, i.GetDomainReplicationQueue()) assert.Equal(t, memberRes, i.GetMembershipResolver()) assert.Equal(t, params.PublicClient, i.GetSDKClient()) assert.NotNil(t, i.GetFrontendRawClient()) assert.NotNil(t, i.GetFrontendClient()) assert.NotNil(t, i.GetMatchingRawClient()) assert.NotNil(t, i.GetMatchingClient()) assert.NotNil(t, i.GetHistoryRawClient()) assert.NotNil(t, i.GetHistoryClient()) assert.NotNil(t, i.GetRatelimiterAggregatorsClient()) adminClient, err := i.GetRemoteAdminClient("secondary-cluster") assert.NoError(t, err) assert.NotNil(t, adminClient) frontendClient, err := i.GetRemoteFrontendClient("secondary-cluster") assert.NoError(t, err) assert.NotNil(t, frontendClient) assert.NotNil(t, i.GetClientBean()) assert.Equal(t, domainMgr, i.GetDomainManager()) assert.Equal(t, taskMgr, i.GetTaskManager()) assert.Equal(t, visMgr, i.GetVisibilityManager()) assert.Equal(t, shardMgr, i.GetShardManager()) assert.Equal(t, historyMgr, i.GetHistoryManager()) em, err := i.GetExecutionManager(3) assert.NoError(t, err) assert.Equal(t, execMgr, em) assert.Equal(t, persistenceClientBean, i.GetPersistenceBean()) assert.Equal(t, hostName, i.GetHostName()) assert.NotNil(t, i.GetLogger()) assert.NotNil(t, i.GetThrottledLogger()) assert.NotNil(t, i.GetDispatcher()) assert.NotNil(t, i.GetIsolationGroupState()) assert.Nil(t, i.GetIsolationGroupStore()) assert.Equal(t, params.AsyncWorkflowQueueProvider, i.GetAsyncWorkflowQueueProvider()) } ================================================ FILE: common/resource/resource_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: types.go // // Generated by this command: // // mockgen -package resource -source types.go -destination resource_mock.go -self_package github.com/uber/cadence/common/resource // // Package resource is a generated GoMock package. package resource import ( reflect "reflect" tally "github.com/uber-go/tally" workflowserviceclient "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" client "github.com/uber/cadence/client" admin "github.com/uber/cadence/client/admin" frontend "github.com/uber/cadence/client/frontend" history "github.com/uber/cadence/client/history" matching "github.com/uber/cadence/client/matching" activecluster "github.com/uber/cadence/common/activecluster" archiver "github.com/uber/cadence/common/archiver" provider "github.com/uber/cadence/common/archiver/provider" queue "github.com/uber/cadence/common/asyncworkflow/queue" blobstore "github.com/uber/cadence/common/blobstore" cache "github.com/uber/cadence/common/cache" clock "github.com/uber/cadence/common/clock" cluster "github.com/uber/cadence/common/cluster" domain "github.com/uber/cadence/common/domain" configstore "github.com/uber/cadence/common/dynamicconfig/configstore" isolationgroup "github.com/uber/cadence/common/isolationgroup" log "github.com/uber/cadence/common/log" membership "github.com/uber/cadence/common/membership" messaging "github.com/uber/cadence/common/messaging" metrics "github.com/uber/cadence/common/metrics" persistence "github.com/uber/cadence/common/persistence" client0 "github.com/uber/cadence/common/persistence/client" rpc "github.com/uber/cadence/common/quotas/global/rpc" service "github.com/uber/cadence/common/service" executorclient "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) // MockResourceFactory is a mock of ResourceFactory interface. type MockResourceFactory struct { ctrl *gomock.Controller recorder *MockResourceFactoryMockRecorder isgomock struct{} } // MockResourceFactoryMockRecorder is the mock recorder for MockResourceFactory. type MockResourceFactoryMockRecorder struct { mock *MockResourceFactory } // NewMockResourceFactory creates a new mock instance. func NewMockResourceFactory(ctrl *gomock.Controller) *MockResourceFactory { mock := &MockResourceFactory{ctrl: ctrl} mock.recorder = &MockResourceFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockResourceFactory) EXPECT() *MockResourceFactoryMockRecorder { return m.recorder } // NewResource mocks base method. func (m *MockResourceFactory) NewResource(params *Params, serviceName string, serviceConfig *service.Config) (Resource, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewResource", params, serviceName, serviceConfig) ret0, _ := ret[0].(Resource) ret1, _ := ret[1].(error) return ret0, ret1 } // NewResource indicates an expected call of NewResource. func (mr *MockResourceFactoryMockRecorder) NewResource(params, serviceName, serviceConfig any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewResource", reflect.TypeOf((*MockResourceFactory)(nil).NewResource), params, serviceName, serviceConfig) } // MockResource is a mock of Resource interface. type MockResource struct { ctrl *gomock.Controller recorder *MockResourceMockRecorder isgomock struct{} } // MockResourceMockRecorder is the mock recorder for MockResource. type MockResourceMockRecorder struct { mock *MockResource } // NewMockResource creates a new mock instance. func NewMockResource(ctrl *gomock.Controller) *MockResource { mock := &MockResource{ctrl: ctrl} mock.recorder = &MockResourceMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockResource) EXPECT() *MockResourceMockRecorder { return m.recorder } // GetActiveClusterManager mocks base method. func (m *MockResource) GetActiveClusterManager() activecluster.Manager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveClusterManager") ret0, _ := ret[0].(activecluster.Manager) return ret0 } // GetActiveClusterManager indicates an expected call of GetActiveClusterManager. func (mr *MockResourceMockRecorder) GetActiveClusterManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterManager", reflect.TypeOf((*MockResource)(nil).GetActiveClusterManager)) } // GetArchivalMetadata mocks base method. func (m *MockResource) GetArchivalMetadata() archiver.ArchivalMetadata { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetArchivalMetadata") ret0, _ := ret[0].(archiver.ArchivalMetadata) return ret0 } // GetArchivalMetadata indicates an expected call of GetArchivalMetadata. func (mr *MockResourceMockRecorder) GetArchivalMetadata() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetArchivalMetadata", reflect.TypeOf((*MockResource)(nil).GetArchivalMetadata)) } // GetArchiverProvider mocks base method. func (m *MockResource) GetArchiverProvider() provider.ArchiverProvider { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetArchiverProvider") ret0, _ := ret[0].(provider.ArchiverProvider) return ret0 } // GetArchiverProvider indicates an expected call of GetArchiverProvider. func (mr *MockResourceMockRecorder) GetArchiverProvider() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetArchiverProvider", reflect.TypeOf((*MockResource)(nil).GetArchiverProvider)) } // GetAsyncWorkflowQueueProvider mocks base method. func (m *MockResource) GetAsyncWorkflowQueueProvider() queue.Provider { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAsyncWorkflowQueueProvider") ret0, _ := ret[0].(queue.Provider) return ret0 } // GetAsyncWorkflowQueueProvider indicates an expected call of GetAsyncWorkflowQueueProvider. func (mr *MockResourceMockRecorder) GetAsyncWorkflowQueueProvider() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAsyncWorkflowQueueProvider", reflect.TypeOf((*MockResource)(nil).GetAsyncWorkflowQueueProvider)) } // GetBlobstoreClient mocks base method. func (m *MockResource) GetBlobstoreClient() blobstore.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBlobstoreClient") ret0, _ := ret[0].(blobstore.Client) return ret0 } // GetBlobstoreClient indicates an expected call of GetBlobstoreClient. func (mr *MockResourceMockRecorder) GetBlobstoreClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlobstoreClient", reflect.TypeOf((*MockResource)(nil).GetBlobstoreClient)) } // GetClientBean mocks base method. func (m *MockResource) GetClientBean() client.Bean { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetClientBean") ret0, _ := ret[0].(client.Bean) return ret0 } // GetClientBean indicates an expected call of GetClientBean. func (mr *MockResourceMockRecorder) GetClientBean() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClientBean", reflect.TypeOf((*MockResource)(nil).GetClientBean)) } // GetClusterMetadata mocks base method. func (m *MockResource) GetClusterMetadata() cluster.Metadata { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetClusterMetadata") ret0, _ := ret[0].(cluster.Metadata) return ret0 } // GetClusterMetadata indicates an expected call of GetClusterMetadata. func (mr *MockResourceMockRecorder) GetClusterMetadata() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterMetadata", reflect.TypeOf((*MockResource)(nil).GetClusterMetadata)) } // GetDispatcher mocks base method. func (m *MockResource) GetDispatcher() *yarpc.Dispatcher { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDispatcher") ret0, _ := ret[0].(*yarpc.Dispatcher) return ret0 } // GetDispatcher indicates an expected call of GetDispatcher. func (mr *MockResourceMockRecorder) GetDispatcher() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDispatcher", reflect.TypeOf((*MockResource)(nil).GetDispatcher)) } // GetDomainAuditManager mocks base method. func (m *MockResource) GetDomainAuditManager() persistence.DomainAuditManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainAuditManager") ret0, _ := ret[0].(persistence.DomainAuditManager) return ret0 } // GetDomainAuditManager indicates an expected call of GetDomainAuditManager. func (mr *MockResourceMockRecorder) GetDomainAuditManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainAuditManager", reflect.TypeOf((*MockResource)(nil).GetDomainAuditManager)) } // GetDomainCache mocks base method. func (m *MockResource) GetDomainCache() cache.DomainCache { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainCache") ret0, _ := ret[0].(cache.DomainCache) return ret0 } // GetDomainCache indicates an expected call of GetDomainCache. func (mr *MockResourceMockRecorder) GetDomainCache() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainCache", reflect.TypeOf((*MockResource)(nil).GetDomainCache)) } // GetDomainManager mocks base method. func (m *MockResource) GetDomainManager() persistence.DomainManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainManager") ret0, _ := ret[0].(persistence.DomainManager) return ret0 } // GetDomainManager indicates an expected call of GetDomainManager. func (mr *MockResourceMockRecorder) GetDomainManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainManager", reflect.TypeOf((*MockResource)(nil).GetDomainManager)) } // GetDomainMetricsScopeCache mocks base method. func (m *MockResource) GetDomainMetricsScopeCache() cache.DomainMetricsScopeCache { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainMetricsScopeCache") ret0, _ := ret[0].(cache.DomainMetricsScopeCache) return ret0 } // GetDomainMetricsScopeCache indicates an expected call of GetDomainMetricsScopeCache. func (mr *MockResourceMockRecorder) GetDomainMetricsScopeCache() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainMetricsScopeCache", reflect.TypeOf((*MockResource)(nil).GetDomainMetricsScopeCache)) } // GetDomainReplicationQueue mocks base method. func (m *MockResource) GetDomainReplicationQueue() domain.ReplicationQueue { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainReplicationQueue") ret0, _ := ret[0].(domain.ReplicationQueue) return ret0 } // GetDomainReplicationQueue indicates an expected call of GetDomainReplicationQueue. func (mr *MockResourceMockRecorder) GetDomainReplicationQueue() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainReplicationQueue", reflect.TypeOf((*MockResource)(nil).GetDomainReplicationQueue)) } // GetExecutionManager mocks base method. func (m *MockResource) GetExecutionManager(arg0 int) (persistence.ExecutionManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetExecutionManager", arg0) ret0, _ := ret[0].(persistence.ExecutionManager) ret1, _ := ret[1].(error) return ret0, ret1 } // GetExecutionManager indicates an expected call of GetExecutionManager. func (mr *MockResourceMockRecorder) GetExecutionManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExecutionManager", reflect.TypeOf((*MockResource)(nil).GetExecutionManager), arg0) } // GetFrontendClient mocks base method. func (m *MockResource) GetFrontendClient() frontend.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFrontendClient") ret0, _ := ret[0].(frontend.Client) return ret0 } // GetFrontendClient indicates an expected call of GetFrontendClient. func (mr *MockResourceMockRecorder) GetFrontendClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFrontendClient", reflect.TypeOf((*MockResource)(nil).GetFrontendClient)) } // GetFrontendRawClient mocks base method. func (m *MockResource) GetFrontendRawClient() frontend.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFrontendRawClient") ret0, _ := ret[0].(frontend.Client) return ret0 } // GetFrontendRawClient indicates an expected call of GetFrontendRawClient. func (mr *MockResourceMockRecorder) GetFrontendRawClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFrontendRawClient", reflect.TypeOf((*MockResource)(nil).GetFrontendRawClient)) } // GetHistoryClient mocks base method. func (m *MockResource) GetHistoryClient() history.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryClient") ret0, _ := ret[0].(history.Client) return ret0 } // GetHistoryClient indicates an expected call of GetHistoryClient. func (mr *MockResourceMockRecorder) GetHistoryClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryClient", reflect.TypeOf((*MockResource)(nil).GetHistoryClient)) } // GetHistoryManager mocks base method. func (m *MockResource) GetHistoryManager() persistence.HistoryManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryManager") ret0, _ := ret[0].(persistence.HistoryManager) return ret0 } // GetHistoryManager indicates an expected call of GetHistoryManager. func (mr *MockResourceMockRecorder) GetHistoryManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryManager", reflect.TypeOf((*MockResource)(nil).GetHistoryManager)) } // GetHistoryRawClient mocks base method. func (m *MockResource) GetHistoryRawClient() history.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryRawClient") ret0, _ := ret[0].(history.Client) return ret0 } // GetHistoryRawClient indicates an expected call of GetHistoryRawClient. func (mr *MockResourceMockRecorder) GetHistoryRawClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryRawClient", reflect.TypeOf((*MockResource)(nil).GetHistoryRawClient)) } // GetHostInfo mocks base method. func (m *MockResource) GetHostInfo() membership.HostInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHostInfo") ret0, _ := ret[0].(membership.HostInfo) return ret0 } // GetHostInfo indicates an expected call of GetHostInfo. func (mr *MockResourceMockRecorder) GetHostInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostInfo", reflect.TypeOf((*MockResource)(nil).GetHostInfo)) } // GetHostName mocks base method. func (m *MockResource) GetHostName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHostName") ret0, _ := ret[0].(string) return ret0 } // GetHostName indicates an expected call of GetHostName. func (mr *MockResourceMockRecorder) GetHostName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostName", reflect.TypeOf((*MockResource)(nil).GetHostName)) } // GetIsolationGroupState mocks base method. func (m *MockResource) GetIsolationGroupState() isolationgroup.State { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetIsolationGroupState") ret0, _ := ret[0].(isolationgroup.State) return ret0 } // GetIsolationGroupState indicates an expected call of GetIsolationGroupState. func (mr *MockResourceMockRecorder) GetIsolationGroupState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIsolationGroupState", reflect.TypeOf((*MockResource)(nil).GetIsolationGroupState)) } // GetIsolationGroupStore mocks base method. func (m *MockResource) GetIsolationGroupStore() configstore.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetIsolationGroupStore") ret0, _ := ret[0].(configstore.Client) return ret0 } // GetIsolationGroupStore indicates an expected call of GetIsolationGroupStore. func (mr *MockResourceMockRecorder) GetIsolationGroupStore() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIsolationGroupStore", reflect.TypeOf((*MockResource)(nil).GetIsolationGroupStore)) } // GetLogger mocks base method. func (m *MockResource) GetLogger() log.Logger { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLogger") ret0, _ := ret[0].(log.Logger) return ret0 } // GetLogger indicates an expected call of GetLogger. func (mr *MockResourceMockRecorder) GetLogger() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogger", reflect.TypeOf((*MockResource)(nil).GetLogger)) } // GetMatchingClient mocks base method. func (m *MockResource) GetMatchingClient() matching.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMatchingClient") ret0, _ := ret[0].(matching.Client) return ret0 } // GetMatchingClient indicates an expected call of GetMatchingClient. func (mr *MockResourceMockRecorder) GetMatchingClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMatchingClient", reflect.TypeOf((*MockResource)(nil).GetMatchingClient)) } // GetMatchingRawClient mocks base method. func (m *MockResource) GetMatchingRawClient() matching.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMatchingRawClient") ret0, _ := ret[0].(matching.Client) return ret0 } // GetMatchingRawClient indicates an expected call of GetMatchingRawClient. func (mr *MockResourceMockRecorder) GetMatchingRawClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMatchingRawClient", reflect.TypeOf((*MockResource)(nil).GetMatchingRawClient)) } // GetMembershipResolver mocks base method. func (m *MockResource) GetMembershipResolver() membership.Resolver { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMembershipResolver") ret0, _ := ret[0].(membership.Resolver) return ret0 } // GetMembershipResolver indicates an expected call of GetMembershipResolver. func (mr *MockResourceMockRecorder) GetMembershipResolver() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMembershipResolver", reflect.TypeOf((*MockResource)(nil).GetMembershipResolver)) } // GetMessagingClient mocks base method. func (m *MockResource) GetMessagingClient() messaging.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMessagingClient") ret0, _ := ret[0].(messaging.Client) return ret0 } // GetMessagingClient indicates an expected call of GetMessagingClient. func (mr *MockResourceMockRecorder) GetMessagingClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessagingClient", reflect.TypeOf((*MockResource)(nil).GetMessagingClient)) } // GetMetricsClient mocks base method. func (m *MockResource) GetMetricsClient() metrics.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMetricsClient") ret0, _ := ret[0].(metrics.Client) return ret0 } // GetMetricsClient indicates an expected call of GetMetricsClient. func (mr *MockResourceMockRecorder) GetMetricsClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetricsClient", reflect.TypeOf((*MockResource)(nil).GetMetricsClient)) } // GetMetricsScope mocks base method. func (m *MockResource) GetMetricsScope() tally.Scope { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMetricsScope") ret0, _ := ret[0].(tally.Scope) return ret0 } // GetMetricsScope indicates an expected call of GetMetricsScope. func (mr *MockResourceMockRecorder) GetMetricsScope() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetricsScope", reflect.TypeOf((*MockResource)(nil).GetMetricsScope)) } // GetPayloadSerializer mocks base method. func (m *MockResource) GetPayloadSerializer() persistence.PayloadSerializer { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPayloadSerializer") ret0, _ := ret[0].(persistence.PayloadSerializer) return ret0 } // GetPayloadSerializer indicates an expected call of GetPayloadSerializer. func (mr *MockResourceMockRecorder) GetPayloadSerializer() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPayloadSerializer", reflect.TypeOf((*MockResource)(nil).GetPayloadSerializer)) } // GetPersistenceBean mocks base method. func (m *MockResource) GetPersistenceBean() client0.Bean { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPersistenceBean") ret0, _ := ret[0].(client0.Bean) return ret0 } // GetPersistenceBean indicates an expected call of GetPersistenceBean. func (mr *MockResourceMockRecorder) GetPersistenceBean() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPersistenceBean", reflect.TypeOf((*MockResource)(nil).GetPersistenceBean)) } // GetRatelimiterAggregatorsClient mocks base method. func (m *MockResource) GetRatelimiterAggregatorsClient() rpc.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRatelimiterAggregatorsClient") ret0, _ := ret[0].(rpc.Client) return ret0 } // GetRatelimiterAggregatorsClient indicates an expected call of GetRatelimiterAggregatorsClient. func (mr *MockResourceMockRecorder) GetRatelimiterAggregatorsClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRatelimiterAggregatorsClient", reflect.TypeOf((*MockResource)(nil).GetRatelimiterAggregatorsClient)) } // GetRemoteAdminClient mocks base method. func (m *MockResource) GetRemoteAdminClient(cluster string) (admin.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRemoteAdminClient", cluster) ret0, _ := ret[0].(admin.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRemoteAdminClient indicates an expected call of GetRemoteAdminClient. func (mr *MockResourceMockRecorder) GetRemoteAdminClient(cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemoteAdminClient", reflect.TypeOf((*MockResource)(nil).GetRemoteAdminClient), cluster) } // GetRemoteFrontendClient mocks base method. func (m *MockResource) GetRemoteFrontendClient(cluster string) (frontend.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRemoteFrontendClient", cluster) ret0, _ := ret[0].(frontend.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRemoteFrontendClient indicates an expected call of GetRemoteFrontendClient. func (mr *MockResourceMockRecorder) GetRemoteFrontendClient(cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemoteFrontendClient", reflect.TypeOf((*MockResource)(nil).GetRemoteFrontendClient), cluster) } // GetSDKClient mocks base method. func (m *MockResource) GetSDKClient() workflowserviceclient.Interface { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSDKClient") ret0, _ := ret[0].(workflowserviceclient.Interface) return ret0 } // GetSDKClient indicates an expected call of GetSDKClient. func (mr *MockResourceMockRecorder) GetSDKClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSDKClient", reflect.TypeOf((*MockResource)(nil).GetSDKClient)) } // GetServiceName mocks base method. func (m *MockResource) GetServiceName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetServiceName") ret0, _ := ret[0].(string) return ret0 } // GetServiceName indicates an expected call of GetServiceName. func (mr *MockResourceMockRecorder) GetServiceName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceName", reflect.TypeOf((*MockResource)(nil).GetServiceName)) } // GetShardDistributorExecutorClient mocks base method. func (m *MockResource) GetShardDistributorExecutorClient() executorclient.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardDistributorExecutorClient") ret0, _ := ret[0].(executorclient.Client) return ret0 } // GetShardDistributorExecutorClient indicates an expected call of GetShardDistributorExecutorClient. func (mr *MockResourceMockRecorder) GetShardDistributorExecutorClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardDistributorExecutorClient", reflect.TypeOf((*MockResource)(nil).GetShardDistributorExecutorClient)) } // GetShardManager mocks base method. func (m *MockResource) GetShardManager() persistence.ShardManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardManager") ret0, _ := ret[0].(persistence.ShardManager) return ret0 } // GetShardManager indicates an expected call of GetShardManager. func (mr *MockResourceMockRecorder) GetShardManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardManager", reflect.TypeOf((*MockResource)(nil).GetShardManager)) } // GetTaskManager mocks base method. func (m *MockResource) GetTaskManager() persistence.TaskManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskManager") ret0, _ := ret[0].(persistence.TaskManager) return ret0 } // GetTaskManager indicates an expected call of GetTaskManager. func (mr *MockResourceMockRecorder) GetTaskManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskManager", reflect.TypeOf((*MockResource)(nil).GetTaskManager)) } // GetThrottledLogger mocks base method. func (m *MockResource) GetThrottledLogger() log.Logger { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetThrottledLogger") ret0, _ := ret[0].(log.Logger) return ret0 } // GetThrottledLogger indicates an expected call of GetThrottledLogger. func (mr *MockResourceMockRecorder) GetThrottledLogger() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetThrottledLogger", reflect.TypeOf((*MockResource)(nil).GetThrottledLogger)) } // GetTimeSource mocks base method. func (m *MockResource) GetTimeSource() clock.TimeSource { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTimeSource") ret0, _ := ret[0].(clock.TimeSource) return ret0 } // GetTimeSource indicates an expected call of GetTimeSource. func (mr *MockResourceMockRecorder) GetTimeSource() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeSource", reflect.TypeOf((*MockResource)(nil).GetTimeSource)) } // GetVisibilityManager mocks base method. func (m *MockResource) GetVisibilityManager() persistence.VisibilityManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVisibilityManager") ret0, _ := ret[0].(persistence.VisibilityManager) return ret0 } // GetVisibilityManager indicates an expected call of GetVisibilityManager. func (mr *MockResourceMockRecorder) GetVisibilityManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVisibilityManager", reflect.TypeOf((*MockResource)(nil).GetVisibilityManager)) } // Start mocks base method. func (m *MockResource) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockResourceMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockResource)(nil).Start)) } // Stop mocks base method. func (m *MockResource) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockResourceMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockResource)(nil).Stop)) } ================================================ FILE: common/resource/resource_test_utils.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package resource import ( "testing" oldgomock "github.com/golang/mock/gomock" // client library cannot change from the old gomock "github.com/stretchr/testify/mock" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" publicservicetest "go.uber.org/cadence/.gen/go/cadence/workflowservicetest" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/client" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/client/sharddistributorexecutor" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig/configstore" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" persistenceClient "github.com/uber/cadence/common/persistence/client" "github.com/uber/cadence/common/quotas/global/rpc" "github.com/uber/cadence/common/taskvalidator" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) type ( // Test is the test implementation used for testing Test struct { MetricsScope tally.TestScope ClusterMetadata cluster.Metadata // other common resources DomainCache *cache.MockDomainCache DomainMetricsScopeCache cache.DomainMetricsScopeCache ActiveClusterMgr *activecluster.MockManager DomainReplicationQueue *domain.MockReplicationQueue TimeSource clock.TimeSource PayloadSerializer persistence.PayloadSerializer MetricsClient metrics.Client ArchivalMetadata *archiver.MockArchivalMetadata ArchiverProvider *provider.MockArchiverProvider BlobstoreClient *blobstore.MockClient MockPayloadSerializer *persistence.MockPayloadSerializer // membership infos MembershipResolver *membership.MockResolver // internal services clients SDKClient *publicservicetest.MockClient FrontendClient *frontend.MockClient MatchingClient *matching.MockClient HistoryClient *history.MockClient ShardDistributorExecutorClient *sharddistributorexecutor.MockClient RemoteAdminClient *admin.MockClient RemoteFrontendClient *frontend.MockClient ClientBean *client.MockBean // persistence clients MetadataMgr *mocks.MetadataManager DomainAuditMgr *persistence.MockDomainAuditManager TaskMgr *mocks.TaskManager VisibilityMgr *mocks.VisibilityManager ShardMgr *mocks.ShardManager HistoryMgr *mocks.HistoryV2Manager ExecutionMgr *mocks.ExecutionManager PersistenceBean *persistenceClient.MockBean IsolationGroups *isolationgroup.MockState IsolationGroupStore *configstore.MockClient HostName string Logger log.Logger taskvalidator taskvalidator.Checker AsyncWorkflowQueueProvider *queue.MockProvider RatelimiterAggregatorClient rpc.Client } ) var _ Resource = (*Test)(nil) const ( testHostName = "test_host" ) var ( testHostInfo = membership.NewHostInfo(testHostName) ) // NewTest returns a new test resource instance func NewTest( t *testing.T, controller *gomock.Controller, serviceMetricsIndex metrics.ServiceIdx, ) *Test { logger := testlogger.New(t) frontendClient := frontend.NewMockClient(controller) matchingClient := matching.NewMockClient(controller) historyClient := history.NewMockClient(controller) remoteAdminClient := admin.NewMockClient(controller) remoteFrontendClient := frontend.NewMockClient(controller) clientBean := client.NewMockBean(controller) clientBean.EXPECT().GetFrontendClient().Return(frontendClient).AnyTimes() clientBean.EXPECT().GetMatchingClient(gomock.Any()).Return(matchingClient, nil).AnyTimes() clientBean.EXPECT().GetHistoryClient().Return(historyClient).AnyTimes() clientBean.EXPECT().GetRemoteAdminClient(gomock.Any()).Return(remoteAdminClient, nil).AnyTimes() clientBean.EXPECT().GetRemoteFrontendClient(gomock.Any()).Return(remoteFrontendClient, nil).AnyTimes() shardDistributorExecutorClient := sharddistributorexecutor.NewMockClient(controller) metadataMgr := &mocks.MetadataManager{} domainAuditMgr := persistence.NewMockDomainAuditManager(controller) taskMgr := &mocks.TaskManager{} visibilityMgr := &mocks.VisibilityManager{} shardMgr := &mocks.ShardManager{} historyMgr := &mocks.HistoryV2Manager{} executionMgr := &mocks.ExecutionManager{} domainReplicationQueue := domain.NewMockReplicationQueue(controller) domainReplicationQueue.EXPECT().Start().AnyTimes() domainReplicationQueue.EXPECT().Stop().AnyTimes() persistenceBean := persistenceClient.NewMockBean(controller) persistenceBean.EXPECT().GetDomainManager().Return(metadataMgr).AnyTimes() persistenceBean.EXPECT().GetDomainAuditManager().Return(domainAuditMgr).AnyTimes() persistenceBean.EXPECT().GetTaskManager().Return(taskMgr).AnyTimes() persistenceBean.EXPECT().GetVisibilityManager().Return(visibilityMgr).AnyTimes() persistenceBean.EXPECT().GetHistoryManager().Return(historyMgr).AnyTimes() persistenceBean.EXPECT().GetShardManager().Return(shardMgr).AnyTimes() persistenceBean.EXPECT().GetExecutionManager(gomock.Any()).Return(executionMgr, nil).AnyTimes() isolationGroupMock := isolationgroup.NewMockState(controller) isolationGroupMock.EXPECT().Stop().AnyTimes() scope := tally.NewTestScope("test", nil) asyncWorkflowQueueProvider := queue.NewMockProvider(controller) activeClusterMgr := activecluster.NewMockManager(controller) return &Test{ MetricsScope: scope, // By default tests will run on active cluster unless overridden otherwise ClusterMetadata: cluster.TestActiveClusterMetadata, // other common resources DomainCache: cache.NewMockDomainCache(controller), DomainMetricsScopeCache: cache.NewDomainMetricsScopeCache(), DomainReplicationQueue: domainReplicationQueue, ActiveClusterMgr: activeClusterMgr, TimeSource: clock.NewRealTimeSource(), PayloadSerializer: persistence.NewPayloadSerializer(), MetricsClient: metrics.NewClient(scope, serviceMetricsIndex, metrics.HistogramMigration{}), ArchivalMetadata: &archiver.MockArchivalMetadata{}, ArchiverProvider: provider.NewMockArchiverProvider(controller), BlobstoreClient: blobstore.NewMockClient(controller), MockPayloadSerializer: persistence.NewMockPayloadSerializer(controller), // membership infos MembershipResolver: membership.NewMockResolver(controller), // internal services clients SDKClient: publicservicetest.NewMockClient(oldgomock.NewController(t)), FrontendClient: frontendClient, MatchingClient: matchingClient, HistoryClient: historyClient, RemoteAdminClient: remoteAdminClient, RemoteFrontendClient: remoteFrontendClient, ClientBean: clientBean, ShardDistributorExecutorClient: shardDistributorExecutorClient, // persistence clients MetadataMgr: metadataMgr, DomainAuditMgr: domainAuditMgr, TaskMgr: taskMgr, VisibilityMgr: visibilityMgr, ShardMgr: shardMgr, HistoryMgr: historyMgr, ExecutionMgr: executionMgr, PersistenceBean: persistenceBean, IsolationGroups: isolationGroupMock, // logger Logger: logger, AsyncWorkflowQueueProvider: asyncWorkflowQueueProvider, RatelimiterAggregatorClient: nil, // TODO: not currently used } } // Start for testing func (s *Test) Start() { } // Stop for testing func (s *Test) Stop() { } // static infos // GetServiceName for testing func (s *Test) GetServiceName() string { panic("user should implement this method for test") } // GetHostInfo for testing func (s *Test) GetHostInfo() membership.HostInfo { return testHostInfo } // GetClusterMetadata for testing func (s *Test) GetClusterMetadata() cluster.Metadata { return s.ClusterMetadata } // other common resources // GetDomainCache for testing func (s *Test) GetDomainCache() cache.DomainCache { return s.DomainCache } // GetDomainMetricsScopeCache for testing func (s *Test) GetDomainMetricsScopeCache() cache.DomainMetricsScopeCache { return s.DomainMetricsScopeCache } // GetDomainReplicationQueue for testing func (s *Test) GetDomainReplicationQueue() domain.ReplicationQueue { // user should implement this method for test return s.DomainReplicationQueue } func (s *Test) GetActiveClusterManager() activecluster.Manager { return s.ActiveClusterMgr } // GetTimeSource for testing func (s *Test) GetTimeSource() clock.TimeSource { return s.TimeSource } // GetPayloadSerializer for testing func (s *Test) GetPayloadSerializer() persistence.PayloadSerializer { return s.PayloadSerializer } // GetPayloadSerializer for testing func (s *Test) GetTaskValidator() taskvalidator.Checker { return s.taskvalidator } // GetMetricsClient for testing func (s *Test) GetMetricsClient() metrics.Client { return s.MetricsClient } // GetMetricsScope for testing func (s *Test) GetMetricsScope() tally.Scope { return s.MetricsScope } // GetMessagingClient for testing func (s *Test) GetMessagingClient() messaging.Client { panic("user should implement this method for test") } // GetBlobstoreClient for testing func (s *Test) GetBlobstoreClient() blobstore.Client { return s.BlobstoreClient } // GetArchivalMetadata for testing func (s *Test) GetArchivalMetadata() archiver.ArchivalMetadata { return s.ArchivalMetadata } // GetArchiverProvider for testing func (s *Test) GetArchiverProvider() provider.ArchiverProvider { return s.ArchiverProvider } // GetMembershipResolver for testing func (s *Test) GetMembershipResolver() membership.Resolver { return s.MembershipResolver } // internal services clients // GetSDKClient for testing func (s *Test) GetSDKClient() workflowserviceclient.Interface { return s.SDKClient } // GetFrontendRawClient for testing func (s *Test) GetFrontendRawClient() frontend.Client { return s.FrontendClient } // GetFrontendClient for testing func (s *Test) GetFrontendClient() frontend.Client { return s.FrontendClient } // GetMatchingRawClient for testing func (s *Test) GetMatchingRawClient() matching.Client { return s.MatchingClient } // GetMatchingClient for testing func (s *Test) GetMatchingClient() matching.Client { return s.MatchingClient } // GetHistoryRawClient for testing func (s *Test) GetHistoryRawClient() history.Client { return s.HistoryClient } // GetHistoryClient for testing func (s *Test) GetHistoryClient() history.Client { return s.HistoryClient } func (s *Test) GetShardDistributorExecutorClient() executorclient.Client { return s.ShardDistributorExecutorClient } // GetRemoteAdminClient for testing func (s *Test) GetRemoteAdminClient( cluster string, ) (admin.Client, error) { return s.RemoteAdminClient, nil } // GetRemoteFrontendClient for testing func (s *Test) GetRemoteFrontendClient( cluster string, ) (frontend.Client, error) { return s.RemoteFrontendClient, nil } // GetClientBean for testing func (s *Test) GetClientBean() client.Bean { return s.ClientBean } // persistence clients // GetMetadataManager for testing func (s *Test) GetDomainManager() persistence.DomainManager { return s.MetadataMgr } // GetDomainAuditManager for testing func (s *Test) GetDomainAuditManager() persistence.DomainAuditManager { return s.DomainAuditMgr } // GetTaskManager for testing func (s *Test) GetTaskManager() persistence.TaskManager { return s.TaskMgr } // GetVisibilityManager for testing func (s *Test) GetVisibilityManager() persistence.VisibilityManager { return s.VisibilityMgr } // GetShardManager for testing func (s *Test) GetShardManager() persistence.ShardManager { return s.ShardMgr } // GetHistoryManager for testing func (s *Test) GetHistoryManager() persistence.HistoryManager { return s.HistoryMgr } // GetExecutionManager for testing func (s *Test) GetExecutionManager( shardID int, ) (persistence.ExecutionManager, error) { return s.ExecutionMgr, nil } // GetPersistenceBean for testing func (s *Test) GetPersistenceBean() persistenceClient.Bean { return s.PersistenceBean } // GetHostName for testing func (s *Test) GetHostName() string { return s.HostName } // loggers // GetLogger for testing func (s *Test) GetLogger() log.Logger { return s.Logger } // GetThrottledLogger for testing func (s *Test) GetThrottledLogger() log.Logger { return s.Logger } // GetDispatcher for testing func (s *Test) GetDispatcher() *yarpc.Dispatcher { panic("user should implement this method for test") } // GetIsolationGroupState returns the isolationGroupState for testing func (s *Test) GetIsolationGroupState() isolationgroup.State { return s.IsolationGroups } // GetIsolationGroupStore returns the config store for their // isolation-group stores func (s *Test) GetIsolationGroupStore() configstore.Client { return s.IsolationGroupStore } func (s *Test) GetAsyncWorkflowQueueProvider() queue.Provider { return s.AsyncWorkflowQueueProvider } func (s *Test) GetRatelimiterAggregatorsClient() rpc.Client { return s.RatelimiterAggregatorClient } // Finish checks whether expectations are met func (s *Test) Finish( t mock.TestingT, ) { s.ArchivalMetadata.AssertExpectations(t) s.MetadataMgr.AssertExpectations(t) s.TaskMgr.AssertExpectations(t) s.VisibilityMgr.AssertExpectations(t) s.ShardMgr.AssertExpectations(t) s.HistoryMgr.AssertExpectations(t) s.ExecutionMgr.AssertExpectations(t) } ================================================ FILE: common/resource/types.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination resource_mock.go -self_package github.com/uber/cadence/common/resource package resource import ( "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/yarpc" "github.com/uber/cadence/client" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig/configstore" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" persistenceClient "github.com/uber/cadence/common/persistence/client" qrpc "github.com/uber/cadence/common/quotas/global/rpc" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) type ResourceFactory interface { NewResource(params *Params, serviceName string, serviceConfig *service.Config, ) (resource Resource, err error) } // Resource is the interface which expose common resources type Resource interface { common.Daemon // static infos GetServiceName() string GetHostInfo() membership.HostInfo GetArchivalMetadata() archiver.ArchivalMetadata GetClusterMetadata() cluster.Metadata // other common resources GetDomainCache() cache.DomainCache GetDomainMetricsScopeCache() cache.DomainMetricsScopeCache GetActiveClusterManager() activecluster.Manager GetTimeSource() clock.TimeSource GetPayloadSerializer() persistence.PayloadSerializer GetMetricsClient() metrics.Client GetArchiverProvider() provider.ArchiverProvider GetMessagingClient() messaging.Client GetBlobstoreClient() blobstore.Client GetDomainReplicationQueue() domain.ReplicationQueue // membership infos GetMembershipResolver() membership.Resolver // internal services clients GetSDKClient() workflowserviceclient.Interface GetFrontendRawClient() frontend.Client GetFrontendClient() frontend.Client GetMatchingRawClient() matching.Client GetMatchingClient() matching.Client GetHistoryRawClient() history.Client GetHistoryClient() history.Client GetRatelimiterAggregatorsClient() qrpc.Client GetRemoteAdminClient(cluster string) (admin.Client, error) GetRemoteFrontendClient(cluster string) (frontend.Client, error) GetClientBean() client.Bean GetShardDistributorExecutorClient() executorclient.Client // persistence clients GetDomainManager() persistence.DomainManager GetDomainAuditManager() persistence.DomainAuditManager GetTaskManager() persistence.TaskManager GetVisibilityManager() persistence.VisibilityManager GetShardManager() persistence.ShardManager GetHistoryManager() persistence.HistoryManager GetExecutionManager(int) (persistence.ExecutionManager, error) GetPersistenceBean() persistenceClient.Bean // GetHostName get host name GetHostName() string // loggers GetLogger() log.Logger GetThrottledLogger() log.Logger // for registering handlers GetDispatcher() *yarpc.Dispatcher // GetIsolationGroupState returns the isolationGroupState GetIsolationGroupState() isolationgroup.State GetIsolationGroupStore() configstore.Client GetAsyncWorkflowQueueProvider() queue.Provider // GetMetricsScope returns the tally scope for metrics reporting GetMetricsScope() tally.Scope } ================================================ FILE: common/rpc/direct_peer_chooser.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "context" "fmt" "runtime/debug" "sync" "sync/atomic" "go.uber.org/yarpc/api/peer" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/peer/direct" "go.uber.org/yarpc/peer/hostport" "go.uber.org/yarpc/yarpcerrors" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" ) var ( noOpSubscriberInstance = &noOpSubscriber{} ) // directPeerChooser is a peer.Chooser that chooses a peer based on the shard key. // Peers are managed by the peerList and peers are reused across multiple requests. type directPeerChooser struct { status int32 serviceName string logger log.Logger scope metrics.Scope t peer.Transport enableConnRetainMode dynamicproperties.BoolPropertyFn legacyChooser peer.Chooser legacyChooserErr error mu sync.RWMutex peers map[string]peer.Peer } func newDirectChooser( serviceName string, t peer.Transport, logger log.Logger, metricsCl metrics.Client, enableConnRetainMode dynamicproperties.BoolPropertyFn, ) *directPeerChooser { dpc := &directPeerChooser{ serviceName: serviceName, logger: logger.WithTags(tag.DestService(serviceName)), scope: metricsCl.Scope(metrics.P2PRPCPeerChooserScope).Tagged(metrics.DestServiceTag(serviceName)), t: t, enableConnRetainMode: enableConnRetainMode, peers: make(map[string]peer.Peer), } if dpc.enableConnRetainMode == nil { dpc.enableConnRetainMode = func(opts ...dynamicproperties.FilterOption) bool { return false } } return dpc } // Start statisfies the peer.Chooser interface. func (g *directPeerChooser) Start() (err error) { if !atomic.CompareAndSwapInt32(&g.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return nil } defer func() { if err != nil { g.logger.Error("direct peer chooser failed to start", tag.Error(err)) return } g.logger.Info("direct peer chooser started") }() if !g.enableConnRetainMode() { c, ok := g.getLegacyChooser() if ok { return c.Start() } return fmt.Errorf("failed to start direct peer chooser because direct peer chooser initialization failed, err: %v", g.legacyChooserErr) } return nil } // Stop statisfies the peer.Chooser interface. func (g *directPeerChooser) Stop() error { if !atomic.CompareAndSwapInt32(&g.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return nil } var err error // Stop legacy chooser if it was initialized if g.legacyChooser != nil { err = g.legacyChooser.Stop() } // Release all peers if there's any g.updatePeersInternal(nil) g.logger.Info("direct peer chooser stopped", tag.Error(err)) return err } // IsRunning statisfies the peer.Chooser interface. func (g *directPeerChooser) IsRunning() bool { if atomic.LoadInt32(&g.status) != common.DaemonStatusStarted { return false } if !g.enableConnRetainMode() { c, ok := g.getLegacyChooser() if ok { return c.IsRunning() } return false } return true // no-op } // Choose returns an existing peer for the shard key. // ShardKey is {host}:{port} of the peer. It could be tchannel or grpc address. func (g *directPeerChooser) Choose(ctx context.Context, req *transport.Request) (peer peer.Peer, onFinish func(error), err error) { if !g.enableConnRetainMode() { return g.chooseFromLegacyDirectPeerChooser(ctx, req) } if req.ShardKey == "" { return nil, nil, yarpcerrors.InvalidArgumentErrorf("chooser requires ShardKey to be non-empty") } g.mu.RLock() p, ok := g.peers[req.ShardKey] if ok { g.mu.RUnlock() return p, func(error) {}, nil } g.mu.RUnlock() // peer is not cached, add new peer p, err = g.addPeer(req.ShardKey) if err != nil { return nil, nil, yarpcerrors.InternalErrorf("failed to add peer for shard key %v, err: %v", req.ShardKey, err) } return p, func(error) {}, nil } // UpdatePeers removes peers that are not in the members list. // Do not create actual yarpc peers for the members. They are created lazily when a request comes in (Choose is called). func (g *directPeerChooser) UpdatePeers(serviceName string, members []membership.HostInfo) { if g.serviceName != serviceName { g.logger.Debug("This is not the service chooser is created for. Ignore such updates.", tag.Dynamic("members-service", serviceName)) return } g.logger.Debug("direct peer chooser got a membership update", tag.Counter(len(members))) // If the chooser is not started, do not act on membership changes. // If membership updates arrive after chooser is stopped, ignore them. if atomic.LoadInt32(&g.status) != common.DaemonStatusStarted { return } g.updatePeersInternal(members) } func (g *directPeerChooser) updatePeersInternal(members []membership.HostInfo) { // Create a map of valid peer addresses given members list. validPeerAddresses := make(map[string]bool) for _, member := range members { for _, portName := range []string{membership.PortTchannel, membership.PortGRPC} { addr, err := member.GetNamedAddress(portName) if err != nil { g.logger.Error(fmt.Sprintf("failed to get %s address of member", portName), tag.Error(err), tag.Address(member.GetAddress())) continue } validPeerAddresses[addr] = true } } // Take a copy of the current peers to avoid keeping write lock while removing all peers. peers := make(map[string]bool) g.mu.RLock() for addr := range g.peers { peers[addr] = true } g.mu.RUnlock() g.logger.Debugf("valid peers: %v, current peers: %v", validPeerAddresses, peers) for addr := range peers { if !validPeerAddresses[addr] { g.removePeer(addr) } } } func (g *directPeerChooser) removePeer(addr string) { g.mu.RLock() peer, exists := g.peers[addr] if exists && peer != nil { if err := g.t.ReleasePeer(peer, noOpSubscriberInstance); err != nil { g.logger.Error("failed to release peer", tag.Error(err), tag.Address(addr)) } } else { if peer == nil { g.logger.Error("peer is nil", tag.Address(addr), tag.Dynamic("stack", string(debug.Stack()))) } else { g.logger.Error("peer is not in the peers map", tag.Address(addr), tag.Dynamic("stack", string(debug.Stack()))) } } g.mu.RUnlock() g.mu.Lock() defer g.mu.Unlock() delete(g.peers, addr) g.logger.Info("removed peer from direct peer chooser", tag.Address(addr)) g.scope.IncCounter(metrics.P2PPeerRemoved) g.scope.UpdateGauge(metrics.P2PPeersCount, float64(len(g.peers))) } func (g *directPeerChooser) addPeer(addr string) (peer.Peer, error) { g.mu.Lock() defer g.mu.Unlock() if p, ok := g.peers[addr]; ok { return p, nil } p, err := g.t.RetainPeer(hostport.Identify(addr), noOpSubscriberInstance) if err != nil { return nil, err } g.peers[addr] = p g.logger.Info("added peer to direct peer chooser", tag.Address(addr)) g.scope.IncCounter(metrics.P2PPeerAdded) g.scope.UpdateGauge(metrics.P2PPeersCount, float64(len(g.peers))) return p, nil } func (g *directPeerChooser) chooseFromLegacyDirectPeerChooser(ctx context.Context, req *transport.Request) (peer.Peer, func(error), error) { c, ok := g.getLegacyChooser() if !ok { return nil, nil, yarpcerrors.InternalErrorf("failed to get legacy direct peer chooser, err: %v", g.legacyChooserErr) } return c.Choose(ctx, req) } func (g *directPeerChooser) getLegacyChooser() (peer.Chooser, bool) { g.mu.RLock() if g.legacyChooser != nil { // Legacy chooser already created, return it g.mu.RUnlock() return g.legacyChooser, true } if g.legacyChooserErr != nil { // There was an error creating the legacy chooser, return false g.mu.RUnlock() return nil, false } g.mu.RUnlock() g.mu.Lock() g.legacyChooser, g.legacyChooserErr = direct.New(direct.Configuration{}, g.t) g.mu.Unlock() if g.legacyChooserErr != nil { g.logger.Error("failed to create legacy direct peer chooser", tag.Error(g.legacyChooserErr)) return nil, false } if atomic.LoadInt32(&g.status) == common.DaemonStatusStarted { // Start the legacy chooser if the current chooser is already started if err := g.legacyChooser.Start(); err != nil { g.logger.Error("failed to start legacy direct peer chooser", tag.Error(err)) return nil, false } } return g.legacyChooser, true } // noOpSubscriber is a no-op implementation of peer.Subscriber type noOpSubscriber struct{} func (*noOpSubscriber) NotifyStatusChanged(peer.Identifier) {} ================================================ FILE: common/rpc/direct_peer_chooser_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "context" "testing" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/transport/grpc" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" ) func TestDirectChooser_PeerUpdates(t *testing.T) { logger := testlogger.New(t) metricCl := metrics.NewNoopMetricsClient() serviceName := "service" directConnRetainFn := func(opts ...dynamicproperties.FilterOption) bool { return true } grpcTransport := grpc.NewTransport() chooser := newDirectChooser(serviceName, grpcTransport, logger, metricCl, directConnRetainFn) choosePeers := func(peers ...string) { t.Helper() // Calling Choose() will create peers and they will be cached for _, p := range peers { _, onFinish, err := chooser.Choose(context.Background(), &transport.Request{ Caller: "caller", Service: "service", ShardKey: p, }) assert.NoError(t, err, "Choose() failed") onFinish(nil) } } currentPeersMap := func() map[string]bool { t.Helper() chooser.mu.RLock() defer chooser.mu.RUnlock() peers := make(map[string]bool, len(chooser.peers)) for p := range chooser.peers { peers[p] = true } return peers } newHost := func(peer string) membership.HostInfo { return membership.NewDetailedHostInfo(peer+":80", peer, membership.PortMap{ membership.PortGRPC: 80, }) } t.Run("chooser not started so should discard membership updates", func(t *testing.T) { choosePeers("peer1:80", "peer2:80") chooser.UpdatePeers(serviceName, nil) wantPeers := map[string]bool{"peer1:80": true, "peer2:80": true} gotPeers := currentPeersMap() if diff := cmp.Diff(wantPeers, gotPeers); diff != "" { t.Fatalf("Peers mismatch (-want +got):\n%s", diff) } }) // Start chooser and do more validations if err := chooser.Start(); err != nil { t.Fatalf("failed to start direct peer chooser: %v", err) } defer chooser.Stop() t.Run("peer1 and peer2 are chosen, peer2 is removed from members list", func(t *testing.T) { choosePeers("peer1:80", "peer2:80") chooser.UpdatePeers(serviceName, []membership.HostInfo{ newHost("peer1"), }) wantPeers := map[string]bool{"peer1:80": true} gotPeers := currentPeersMap() if diff := cmp.Diff(wantPeers, gotPeers); diff != "" { t.Fatalf("Peers mismatch (-want +got):\n%s", diff) } }) t.Run("peer3 and peer4 are also chosen, membership list has peer1 and peer4", func(t *testing.T) { choosePeers("peer3:80", "peer4:80") chooser.UpdatePeers(serviceName, []membership.HostInfo{ newHost("peer1"), newHost("peer4"), }) wantPeers := map[string]bool{"peer1:80": true, "peer4:80": true} gotPeers := currentPeersMap() if diff := cmp.Diff(wantPeers, gotPeers); diff != "" { t.Fatalf("Peers mismatch (-want +got):\n%s", diff) } }) t.Run("membership list update for another service is ignored, should still keep peer1 and peer4", func(t *testing.T) { chooser.UpdatePeers("another-service", []membership.HostInfo{ newHost("peer50"), }) wantPeers := map[string]bool{"peer1:80": true, "peer4:80": true} gotPeers := currentPeersMap() if diff := cmp.Diff(wantPeers, gotPeers); diff != "" { t.Fatalf("Peers mismatch (-want +got):\n%s", diff) } }) } func TestDirectChooser_StartStop(t *testing.T) { newReq := func(shardKey string) *transport.Request { return &transport.Request{ Caller: "caller", Service: "service", ShardKey: shardKey, } } tests := []struct { desc string retainConn bool req *transport.Request multipleChoose bool wantChooseErr bool }{ { desc: "legacy chooser", retainConn: false, req: newReq("key"), }, { desc: "legacy chooser - empty shard key", retainConn: false, req: newReq(""), wantChooseErr: true, }, { desc: "connection retain mode", retainConn: true, req: newReq("key"), }, { desc: "connection retain mode - empty shard key", retainConn: true, req: newReq(""), wantChooseErr: true, }, { desc: "connection retain mode - multiple choose should return chooser from cache", retainConn: true, req: newReq("key"), multipleChoose: true, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { defer goleak.VerifyNone(t) logger := testlogger.New(t) metricCl := metrics.NewNoopMetricsClient() serviceName := "service" directConnRetainFn := func(opts ...dynamicproperties.FilterOption) bool { return tc.retainConn } grpcTransport := grpc.NewTransport() chooser := newDirectChooser(serviceName, grpcTransport, logger, metricCl, directConnRetainFn) assert.False(t, chooser.IsRunning(), "expected IsRunning()=false before Start()") if err := chooser.Start(); err != nil { t.Fatalf("failed to start direct peer chooser: %v", err) } assert.NoError(t, chooser.Start(), "starting again should be no-op") assert.True(t, chooser.IsRunning()) peer, onFinish, err := chooser.Choose(context.Background(), tc.req) if tc.wantChooseErr != (err != nil) { t.Fatalf("Choose() err = %v, wantChooseErr = %v", err, tc.wantChooseErr) } if err == nil { assert.NotNil(t, peer) assert.NotNil(t, onFinish) // call onFinish will release the peer for legacy chooser onFinish(nil) } if tc.multipleChoose { peer2, onFinish2, err2 := chooser.Choose(context.Background(), tc.req) assert.NoError(t, err2) assert.NotNil(t, onFinish2) assert.Equal(t, peer, peer2) onFinish2(nil) } if err := chooser.Stop(); err != nil { t.Fatalf("failed to stop direct peer chooser: %v", err) } assert.NoError(t, chooser.Stop(), "stopping again should be no-op") }) } } ================================================ FILE: common/rpc/dns_updater.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "context" "fmt" "net" "strings" "sync" "time" "go.uber.org/yarpc/api/peer" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) type ( dnsUpdater struct { resolver dnsHostResolver interval time.Duration dnsAddress string port string currentPeers map[string]struct{} list peer.List logger log.Logger wg sync.WaitGroup ctx context.Context cancel context.CancelFunc } dnsRefreshResult struct { updates peer.ListUpdates newPeers map[string]struct{} changed bool } aPeer struct { addrPort string } ) type dnsHostResolver interface { LookupHost(ctx context.Context, host string) (addrs []string, err error) } func newDNSUpdater(list peer.List, dnsPort string, interval time.Duration, logger log.Logger) (*dnsUpdater, error) { ss := strings.Split(dnsPort, ":") if len(ss) != 2 { return nil, fmt.Errorf("incorrect DNS:Port format") } ctx, cancel := context.WithCancel(context.Background()) return &dnsUpdater{ resolver: net.DefaultResolver, interval: interval, logger: logger, list: list, dnsAddress: ss[0], port: ss[1], currentPeers: make(map[string]struct{}), ctx: ctx, cancel: cancel, }, nil } func (d *dnsUpdater) Start() { d.wg.Add(1) go func() { defer d.wg.Done() for { now := time.Now() res, err := d.refresh() if err != nil { d.logger.Error("Failed to update DNS", tag.Error(err), tag.Address(d.dnsAddress)) } if res != nil && res.changed { if len(res.updates.Additions) > 0 { d.logger.Info("Add new peers by DNS lookup", tag.Address(d.dnsAddress), tag.Addresses(identifiersToStringList(res.updates.Additions))) } if len(res.updates.Removals) > 0 { d.logger.Info("Remove stale peers by DNS lookup", tag.Address(d.dnsAddress), tag.Addresses(identifiersToStringList(res.updates.Removals))) } err := d.list.Update(res.updates) if err != nil { d.logger.Error("Failed to update peerList", tag.Error(err), tag.Address(d.dnsAddress)) } else { d.currentPeers = res.newPeers } } sleepDu := now.Add(d.interval).Sub(now) t := time.NewTimer(sleepDu) select { case <-d.ctx.Done(): t.Stop() d.logger.Info("DNS updater is stopping so returning from dns update loop", tag.Address(d.dnsAddress)) return case <-t.C: continue } } }() } func (d *dnsUpdater) Stop() { d.logger.Info("DNS updater is stopping", tag.Address(d.dnsAddress)) d.cancel() d.wg.Wait() d.logger.Info("DNS updater stopped", tag.Address(d.dnsAddress)) } func (d *dnsUpdater) refresh() (*dnsRefreshResult, error) { ips, err := d.resolver.LookupHost(d.ctx, d.dnsAddress) if err != nil { return nil, err } newPeers := map[string]struct{}{} for _, ip := range ips { adr := fmt.Sprintf("%v:%v", ip, d.port) newPeers[adr] = struct{}{} } updates := peer.ListUpdates{ Additions: make([]peer.Identifier, 0), Removals: make([]peer.Identifier, 0), } changed := false // remove if it doesn't exist anymore for addr := range d.currentPeers { if _, ok := newPeers[addr]; !ok { changed = true updates.Removals = append( updates.Removals, aPeer{addrPort: addr}, ) } } // add if it doesn't exist before for addr := range newPeers { if _, ok := d.currentPeers[addr]; !ok { changed = true updates.Additions = append( updates.Additions, aPeer{addrPort: addr}, ) } } return &dnsRefreshResult{ updates: updates, newPeers: newPeers, changed: changed, }, nil } func (a aPeer) Identifier() string { return a.addrPort } func identifiersToStringList(ids []peer.Identifier) []string { ss := make([]string, 0, len(ids)) for _, id := range ids { ss = append(ss, id.Identifier()) } return ss } ================================================ FILE: common/rpc/dns_updater_test.go ================================================ package rpc import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/yarpc/api/peer" "github.com/uber/cadence/common/log" ) type mockDNSHostResolver struct { addrsToReturn []string errToReturn error } func (m *mockDNSHostResolver) LookupHost(ctx context.Context, host string) ([]string, error) { return m.addrsToReturn, m.errToReturn } type dummyPeerList struct { updates []peer.ListUpdates errToReturn error } func (d *dummyPeerList) Update(updates peer.ListUpdates) error { d.updates = append(d.updates, updates) return d.errToReturn } func TestDNSUpdater_Refresh(t *testing.T) { tests := []struct { name string initialPeers []string newPeers []string currentPeers map[string]struct{} wantChanged bool wantNewPeers map[string]struct{} wantAdditions []string wantRemovals []string wantDNSFailure bool }{ { name: "Initial state: two peers", initialPeers: nil, newPeers: []string{"10.0.0.1", "10.0.0.2"}, currentPeers: nil, wantChanged: true, wantNewPeers: map[string]struct{}{ "10.0.0.1:1234": {}, "10.0.0.2:1234": {}, }, wantAdditions: []string{"10.0.0.1:1234", "10.0.0.2:1234"}, wantRemovals: nil, }, { name: "Change DNS, one removed, one added, one persistent", initialPeers: []string{"10.0.0.1", "10.0.0.2"}, newPeers: []string{"10.0.0.2", "10.0.0.3"}, currentPeers: map[string]struct{}{ "10.0.0.1:1234": {}, "10.0.0.2:1234": {}, }, wantChanged: true, wantNewPeers: map[string]struct{}{ "10.0.0.2:1234": {}, "10.0.0.3:1234": {}, }, wantAdditions: []string{"10.0.0.3:1234"}, wantRemovals: []string{"10.0.0.1:1234"}, }, { name: "No changes", initialPeers: []string{"10.0.0.2", "10.0.0.3"}, newPeers: []string{"10.0.0.2", "10.0.0.3"}, currentPeers: map[string]struct{}{ "10.0.0.2:1234": {}, "10.0.0.3:1234": {}, }, wantChanged: false, wantNewPeers: map[string]struct{}{ "10.0.0.2:1234": {}, "10.0.0.3:1234": {}, }, wantAdditions: nil, wantRemovals: nil, }, { name: "DNS failure", initialPeers: []string{"10.0.0.1", "10.0.0.2"}, newPeers: nil, currentPeers: map[string]struct{}{"10.0.0.1:1234": {}, "10.0.0.2:1234": {}}, wantChanged: false, wantNewPeers: nil, wantAdditions: nil, wantRemovals: nil, wantDNSFailure: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockResolver := &mockDNSHostResolver{} dnsAddr := "test.service.com:1234" interval := 1 * time.Second logger := log.NewNoop() testList := &dummyPeerList{} updater, err := newDNSUpdater(testList, dnsAddr, interval, logger) require.NoError(t, err, "should create dnsUpdater") updater.resolver = mockResolver // Optionally set initial currentPeers if tt.currentPeers != nil { updater.currentPeers = tt.currentPeers } if tt.wantDNSFailure { mockResolver.errToReturn = errors.New("mock DNS error") } else { mockResolver.errToReturn = nil mockResolver.addrsToReturn = tt.newPeers } res, err := updater.refresh() if tt.wantDNSFailure { require.Error(t, err) assert.Nil(t, res) } else { require.NoError(t, err) assert.Equal(t, tt.wantChanged, res.changed) assert.Equal(t, tt.wantNewPeers, res.newPeers) actualAdditions := identifiersToStringList(res.updates.Additions) actualRemovals := identifiersToStringList(res.updates.Removals) assert.ElementsMatch(t, tt.wantAdditions, actualAdditions) assert.ElementsMatch(t, tt.wantRemovals, actualRemovals) } }) } } func TestDNSUpdater_UpdaterFailure(t *testing.T) { mockResolver := &mockDNSHostResolver{ addrsToReturn: []string{"10.0.0.3", "10.0.0.4"}, } logger := log.NewNoop() testList := &dummyPeerList{errToReturn: errors.New("mock updater error")} updater, err := newDNSUpdater(testList, "host.test:1234", 100*time.Millisecond, logger) require.NoError(t, err) updater.resolver = mockResolver updater.currentPeers = map[string]struct{}{ "10.0.0.1:1234": {}, "10.0.0.2:1234": {}, } updater.Start() defer updater.Stop() time.Sleep(1 * time.Second) assert.Equal(t, map[string]struct{}{"10.0.0.1:1234": {}, "10.0.0.2:1234": {}}, updater.currentPeers) } ================================================ FILE: common/rpc/factory.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "context" "crypto/tls" "fmt" "net" nethttp "net/http" "sync" "go.uber.org/yarpc" "go.uber.org/yarpc/transport/grpc" yarpchttp "go.uber.org/yarpc/transport/http" "go.uber.org/yarpc/transport/tchannel" "google.golang.org/grpc/credentials" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" ) const ( defaultGRPCSizeLimit = 4 * 1024 * 1024 factoryComponentName = "rpc-factory" ) var ( // P2P outbounds are only needed for history and matching services servicesToTalkP2P = []string{service.History, service.Matching} ) // Factory is an implementation of rpc.Factory interface type FactoryImpl struct { startOnce sync.Once stopOnce sync.Once maxMessageSize int channel tchannel.Channel dispatcher *yarpc.Dispatcher outbounds *Outbounds logger log.Logger serviceName string wg sync.WaitGroup ctx context.Context cancelFn context.CancelFunc peerLister PeerLister } // NewFactory builds a new rpc.Factory func NewFactory(logger log.Logger, p Params) Factory { logger = logger.WithTags(tag.ComponentRPCFactory) inbounds := yarpc.Inbounds{} // Create TChannel transport // This is here only because ringpop expects tchannel.ChannelTransport, // everywhere else we use regular tchannel.Transport. ch, err := tchannel.NewChannelTransport( tchannel.ServiceName(p.ServiceName), tchannel.ListenAddr(p.TChannelAddress)) if err != nil { logger.Fatal("Failed to create transport channel", tag.Error(err)) } tchannel, err := tchannel.NewTransport(tchannel.ServiceName(p.ServiceName)) if err != nil { logger.Fatal("Failed to create tchannel transport", tag.Error(err)) } inbounds = append(inbounds, ch.NewInbound()) logger.Info("Listening for TChannel requests", tag.Address(p.TChannelAddress)) // Create gRPC transport var options []grpc.TransportOption if p.GRPCMaxMsgSize > 0 { options = append(options, grpc.ServerMaxRecvMsgSize(p.GRPCMaxMsgSize)) options = append(options, grpc.ClientMaxRecvMsgSize(p.GRPCMaxMsgSize)) } grpcTransport := grpc.NewTransport(options...) if len(p.GRPCAddress) > 0 { listener, err := net.Listen("tcp", p.GRPCAddress) if err != nil { logger.Fatal("Failed to listen on GRPC port", tag.Error(err)) } var inboundOptions []grpc.InboundOption if p.InboundTLS != nil { inboundOptions = append(inboundOptions, grpc.InboundCredentials(credentials.NewTLS(p.InboundTLS))) } inbounds = append(inbounds, grpcTransport.NewInbound(listener, inboundOptions...)) logger.Info("Listening for GRPC requests", tag.Address(p.GRPCAddress)) } // Create http inbound if configured if p.HTTP != nil { interceptor := func(handler nethttp.Handler) nethttp.Handler { return nethttp.HandlerFunc(func(w nethttp.ResponseWriter, r *nethttp.Request) { procedure := r.Header.Get(yarpchttp.ProcedureHeader) if _, found := p.HTTP.Procedures[procedure]; found { handler.ServeHTTP(w, r) return } nethttp.NotFound(w, r) }) } inboundOptions := []yarpchttp.InboundOption{yarpchttp.Interceptor(interceptor)} if p.HTTP.TLS != nil { inboundOptions = append(inboundOptions, yarpchttp.InboundTLSConfiguration(p.HTTP.TLS), yarpchttp.InboundTLSMode(p.HTTP.Mode)) logger.Info(fmt.Sprintf("Enabling HTTP TLS with Mode %q", p.HTTP.Mode)) } httpinbound := yarpchttp.NewTransport().NewInbound(p.HTTP.Address, inboundOptions...) inbounds = append(inbounds, httpinbound) logger.Info("Listening for HTTP requests", tag.Address(p.HTTP.Address)) } // Create outbounds outbounds := &Outbounds{} if p.OutboundsBuilder != nil { outbounds, err = p.OutboundsBuilder.Build(grpcTransport, tchannel) if err != nil { logger.Fatal("Failed to create outbounds", tag.Error(err)) } } dispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: p.ServiceName, Inbounds: inbounds, Outbounds: outbounds.Outbounds, InboundMiddleware: p.InboundMiddleware, OutboundMiddleware: p.OutboundMiddleware, }) ctx, cancel := context.WithCancel(context.Background()) return &FactoryImpl{ maxMessageSize: p.GRPCMaxMsgSize, dispatcher: dispatcher, channel: ch.Channel(), outbounds: outbounds, serviceName: p.ServiceName, logger: logger, ctx: ctx, cancelFn: cancel, } } // GetDispatcher return a cached dispatcher func (d *FactoryImpl) GetDispatcher() *yarpc.Dispatcher { return d.dispatcher } // GetTChannel GetChannel returns Tchannel Channel used by Ringpop func (d *FactoryImpl) GetTChannel() tchannel.Channel { return d.channel } func (d *FactoryImpl) GetMaxMessageSize() int { if d.maxMessageSize == 0 { return defaultGRPCSizeLimit } return d.maxMessageSize } func (d *FactoryImpl) Start(peerLister PeerLister) error { var err error d.startOnce.Do(func() { d.peerLister = peerLister // subscribe to membership changes for history and matching. This is needed to update the peers for rpc for _, svc := range servicesToTalkP2P { ch := make(chan *membership.ChangedEvent, 1) if err = d.peerLister.Subscribe(svc, factoryComponentName, ch); err != nil { err = fmt.Errorf("rpc factory failed to subscribe to membership updates for svc: %v, err: %w", svc, err) return } d.wg.Add(1) go d.listenMembershipChanges(svc, ch) } }) return err } func (d *FactoryImpl) Stop() error { d.stopOnce.Do(func() { d.logger.Info("stopping rpc factory") for _, svc := range servicesToTalkP2P { if err := d.peerLister.Unsubscribe(svc, factoryComponentName); err != nil { d.logger.Error("rpc factory failed to unsubscribe from membership updates", tag.Error(err), tag.Service(svc)) } } d.cancelFn() d.wg.Wait() d.logger.Info("stopped rpc factory") }) return nil } func (d *FactoryImpl) listenMembershipChanges(svc string, ch chan *membership.ChangedEvent) { defer d.wg.Done() for { select { case <-ch: d.logger.Debug("rpc factory received membership changed event", tag.Service(svc)) members, err := d.peerLister.Members(svc) if err != nil { d.logger.Error("rpc factory failed to get members from membership resolver", tag.Error(err), tag.Service(svc)) continue } d.outbounds.UpdatePeers(svc, members) case <-d.ctx.Done(): d.logger.Info("rpc factory stopped so listenMembershipChanges returning", tag.Service(svc)) return } } } func createDialer(transport *grpc.Transport, tlsConfig *tls.Config) *grpc.Dialer { var dialOptions []grpc.DialOption if tlsConfig != nil { dialOptions = append(dialOptions, grpc.DialerCredentials(credentials.NewTLS(tlsConfig))) } return transport.NewDialer(dialOptions...) } ================================================ FILE: common/rpc/factory_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: types.go // // Generated by this command: // // mockgen -package rpc -source types.go -destination factory_mock.go -self_package github.com/uber/cadence/common/rpc // // Package rpc is a generated GoMock package. package rpc import ( reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" tchannel "go.uber.org/yarpc/transport/tchannel" membership "github.com/uber/cadence/common/membership" ) // MockFactory is a mock of Factory interface. type MockFactory struct { ctrl *gomock.Controller recorder *MockFactoryMockRecorder isgomock struct{} } // MockFactoryMockRecorder is the mock recorder for MockFactory. type MockFactoryMockRecorder struct { mock *MockFactory } // NewMockFactory creates a new mock instance. func NewMockFactory(ctrl *gomock.Controller) *MockFactory { mock := &MockFactory{ctrl: ctrl} mock.recorder = &MockFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFactory) EXPECT() *MockFactoryMockRecorder { return m.recorder } // GetDispatcher mocks base method. func (m *MockFactory) GetDispatcher() *yarpc.Dispatcher { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDispatcher") ret0, _ := ret[0].(*yarpc.Dispatcher) return ret0 } // GetDispatcher indicates an expected call of GetDispatcher. func (mr *MockFactoryMockRecorder) GetDispatcher() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDispatcher", reflect.TypeOf((*MockFactory)(nil).GetDispatcher)) } // GetMaxMessageSize mocks base method. func (m *MockFactory) GetMaxMessageSize() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMaxMessageSize") ret0, _ := ret[0].(int) return ret0 } // GetMaxMessageSize indicates an expected call of GetMaxMessageSize. func (mr *MockFactoryMockRecorder) GetMaxMessageSize() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMaxMessageSize", reflect.TypeOf((*MockFactory)(nil).GetMaxMessageSize)) } // GetTChannel mocks base method. func (m *MockFactory) GetTChannel() tchannel.Channel { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTChannel") ret0, _ := ret[0].(tchannel.Channel) return ret0 } // GetTChannel indicates an expected call of GetTChannel. func (mr *MockFactoryMockRecorder) GetTChannel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTChannel", reflect.TypeOf((*MockFactory)(nil).GetTChannel)) } // Start mocks base method. func (m *MockFactory) Start(arg0 PeerLister) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start", arg0) ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockFactoryMockRecorder) Start(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockFactory)(nil).Start), arg0) } // Stop mocks base method. func (m *MockFactory) Stop() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Stop") ret0, _ := ret[0].(error) return ret0 } // Stop indicates an expected call of Stop. func (mr *MockFactoryMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockFactory)(nil).Stop)) } // MockPeerLister is a mock of PeerLister interface. type MockPeerLister struct { ctrl *gomock.Controller recorder *MockPeerListerMockRecorder isgomock struct{} } // MockPeerListerMockRecorder is the mock recorder for MockPeerLister. type MockPeerListerMockRecorder struct { mock *MockPeerLister } // NewMockPeerLister creates a new mock instance. func NewMockPeerLister(ctrl *gomock.Controller) *MockPeerLister { mock := &MockPeerLister{ctrl: ctrl} mock.recorder = &MockPeerListerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPeerLister) EXPECT() *MockPeerListerMockRecorder { return m.recorder } // Members mocks base method. func (m *MockPeerLister) Members(service string) ([]membership.HostInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Members", service) ret0, _ := ret[0].([]membership.HostInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // Members indicates an expected call of Members. func (mr *MockPeerListerMockRecorder) Members(service any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Members", reflect.TypeOf((*MockPeerLister)(nil).Members), service) } // Subscribe mocks base method. func (m *MockPeerLister) Subscribe(service, name string, notifyChannel chan<- *membership.ChangedEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Subscribe", service, name, notifyChannel) ret0, _ := ret[0].(error) return ret0 } // Subscribe indicates an expected call of Subscribe. func (mr *MockPeerListerMockRecorder) Subscribe(service, name, notifyChannel any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockPeerLister)(nil).Subscribe), service, name, notifyChannel) } // Unsubscribe mocks base method. func (m *MockPeerLister) Unsubscribe(service, name string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Unsubscribe", service, name) ret0, _ := ret[0].(error) return ret0 } // Unsubscribe indicates an expected call of Unsubscribe. func (mr *MockPeerListerMockRecorder) Unsubscribe(service, name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockPeerLister)(nil).Unsubscribe), service, name) } ================================================ FILE: common/rpc/factory_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "errors" "sync" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" ) func TestNewFactory(t *testing.T) { ctrl := gomock.NewController(t) logger := testlogger.New(t) serviceName := "service" ob := NewMockOutboundsBuilder(ctrl) ob.EXPECT().Build(gomock.Any(), gomock.Any()).Return(&Outbounds{}, nil).Times(1) grpcMsgSize := 4 * 1024 * 1024 f := NewFactory(logger, Params{ ServiceName: serviceName, TChannelAddress: "localhost:0", GRPCMaxMsgSize: grpcMsgSize, GRPCAddress: "localhost:0", HTTP: &httpParams{ Address: "localhost:0", }, OutboundsBuilder: ob, }) if f == nil { t.Fatal("NewFactory returned nil") } assert.NotNil(t, f.GetDispatcher(), "GetDispatcher returned nil") assert.NotNil(t, f.GetTChannel(), "GetTChannel returned nil") assert.Equal(t, grpcMsgSize, f.GetMaxMessageSize(), "GetMaxMessageSize returned wrong value") } func TestStartStop(t *testing.T) { membersBySvc := map[string][]membership.HostInfo{ service.Matching: { membership.NewHostInfo("localhost:9191"), membership.NewHostInfo("localhost:9192"), }, service.History: { membership.NewHostInfo("localhost:8585"), }, } tests := []struct { desc string wantMembersBySvc map[string][]membership.HostInfo mockFn func(*membership.MockResolver) wantStartErr bool }{ { desc: "success", wantMembersBySvc: membersBySvc, mockFn: func(peerLister *membership.MockResolver) { for _, svc := range servicesToTalkP2P { peerLister.EXPECT().Subscribe(svc, factoryComponentName, gomock.Any()). DoAndReturn(func(service, name string, notifyChannel chan<- *membership.ChangedEvent) error { // Notify the channel once to validate listening logic is working notifyChannel <- &membership.ChangedEvent{} return nil }).Times(1) peerLister.EXPECT().Members(svc).Return(membersBySvc[svc], nil).Times(1) peerLister.EXPECT().Unsubscribe(svc, factoryComponentName).Return(nil).Times(1) } }, }, { desc: "subscription to membership updates fail", wantStartErr: true, mockFn: func(peerLister *membership.MockResolver) { for i, svc := range servicesToTalkP2P { if i == 0 { // subscribe will only be called for the first service and after failing, it should not be called for the rest peerLister.EXPECT().Subscribe(svc, factoryComponentName, gomock.Any()).Return(errors.New("failed")).Times(1) } // subscribe will be called for all services during stop peerLister.EXPECT().Unsubscribe(svc, factoryComponentName).Return(nil).Times(1) } }, }, { desc: "unsubscirption from membership updates fail", wantMembersBySvc: membersBySvc, mockFn: func(peerLister *membership.MockResolver) { for _, svc := range servicesToTalkP2P { peerLister.EXPECT().Subscribe(svc, factoryComponentName, gomock.Any()). DoAndReturn(func(service, name string, notifyChannel chan<- *membership.ChangedEvent) error { // Notify the channel once to validate listening logic is working notifyChannel <- &membership.ChangedEvent{} return nil }).Times(1) peerLister.EXPECT().Members(svc).Return(membersBySvc[svc], nil).Times(1) peerLister.EXPECT().Unsubscribe(svc, factoryComponentName).Return(errors.New("failed")).Times(1) } }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) logger := testlogger.New(t) serviceName := "service" ob := NewMockOutboundsBuilder(ctrl) var mu sync.Mutex gotMembers := make(map[string][]membership.HostInfo) outbounds := &Outbounds{ onUpdatePeers: func(svc string, members []membership.HostInfo) { mu.Lock() defer mu.Unlock() gotMembers[svc] = members }, } ob.EXPECT().Build(gomock.Any(), gomock.Any()).Return(outbounds, nil).Times(1) grpcMsgSize := 4 * 1024 * 1024 f := NewFactory(logger, Params{ ServiceName: serviceName, TChannelAddress: "localhost:0", GRPCMaxMsgSize: grpcMsgSize, GRPCAddress: "localhost:0", HTTP: &httpParams{ Address: "localhost:0", }, OutboundsBuilder: ob, }) peerLister := membership.NewMockResolver(ctrl) tc.mockFn(peerLister) if err := f.Start(peerLister); err != nil { if !tc.wantStartErr { t.Fatalf("Factory.Start() returned error: %v", err) } // start failed expectedly. do not proceed with rest of the validations f.Stop() return } // Wait for membership changes to be processed time.Sleep(100 * time.Millisecond) mu.Lock() assert.Equal(t, tc.wantMembersBySvc, gotMembers, "UpdatePeers not called with expected members") mu.Unlock() if err := f.Stop(); err != nil { t.Fatalf("Factory.Stop() returned error: %v", err) } goleak.VerifyNone(t) }) } } ================================================ FILE: common/rpc/localip.go ================================================ // Copyright (c) 2015 Uber Technologies, Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc // ** This code is copied from tchannel, we would like to not take dependency on tchannel code ** import ( "errors" "net" ) // scoreAddr scores how likely the given addr is to be a remote address and returns the // IP to use when listening. Any address which receives a negative score should not be used. // Scores are calculated as: // -1 for any unknown IP addreseses. // +300 for IPv4 addresses // +100 for non-local addresses, extra +100 for "up" interaces. func scoreAddr(iface net.Interface, addr net.Addr) (int, net.IP) { var ip net.IP if netAddr, ok := addr.(*net.IPNet); ok { ip = netAddr.IP } else if netIP, ok := addr.(*net.IPAddr); ok { ip = netIP.IP } else { return -1, nil } var score int if ip.To4() != nil { score += 300 } if iface.Flags&net.FlagLoopback == 0 && !ip.IsLoopback() { score += 100 if iface.Flags&net.FlagUp != 0 { score += 100 } } return score, ip } // ListenIP returns the IP to bind to in Listen. It tries to find an IP that can be used // by other machines to reach this machine. func ListenIP() (net.IP, error) { interfaces, err := net.Interfaces() if err != nil { return nil, err } bestScore := -1 var bestIP net.IP // Select the highest scoring IP as the best IP. for _, iface := range interfaces { addrs, err := iface.Addrs() if err != nil { // Skip this interface if there is an error. continue } for _, addr := range addrs { score, ip := scoreAddr(iface, addr) if score > bestScore { bestScore = score bestIP = ip } } } if bestScore == -1 { return nil, errors.New("no addresses to listen on") } return bestIP, nil } func mustParseMAC(s string) net.HardwareAddr { addr, err := net.ParseMAC(s) if err != nil { panic(err) } return addr } ================================================ FILE: common/rpc/localip_test.go ================================================ // Copyright (c) 2015 Uber Technologies, Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc // ** This code is copied from tchannel, we would like to not take dependency on tchannel code ** import ( "net" "testing" "github.com/stretchr/testify/assert" ) func TestScoreAddr(t *testing.T) { ipv4 := net.ParseIP("10.0.1.2") ipv6 := net.ParseIP("2001:db8:a0b:12f0::1") tests := []struct { msg string iface net.Interface addr net.Addr want int wantIP net.IP }{ { msg: "non-local up ipv4 IPNet address", iface: net.Interface{Flags: net.FlagUp}, addr: &net.IPNet{IP: ipv4}, want: 500, wantIP: ipv4, }, { msg: "non-local up ipv4 IPAddr address", iface: net.Interface{Flags: net.FlagUp}, addr: &net.IPAddr{IP: ipv4}, want: 500, wantIP: ipv4, }, { msg: "non-local up ipv4 IPAddr address, docker interface", iface: net.Interface{ Flags: net.FlagUp, HardwareAddr: mustParseMAC("02:42:ac:11:56:af"), }, addr: &net.IPNet{IP: ipv4}, want: 500, wantIP: ipv4, }, { msg: "non-local up ipv4 address, local MAC address", iface: net.Interface{ Flags: net.FlagUp, HardwareAddr: mustParseMAC("02:42:9c:52:fc:86"), }, addr: &net.IPNet{IP: ipv4}, want: 500, wantIP: ipv4, }, { msg: "non-local down ipv4 address", iface: net.Interface{}, addr: &net.IPNet{IP: ipv4}, want: 400, wantIP: ipv4, }, { msg: "non-local down ipv6 address", iface: net.Interface{}, addr: &net.IPAddr{IP: ipv6}, want: 100, wantIP: ipv6, }, { msg: "unknown address type", iface: net.Interface{}, addr: &net.UnixAddr{Name: "/tmp/socket"}, want: -1, }, } for _, tt := range tests { gotScore, gotIP := scoreAddr(tt.iface, tt.addr) assert.Equal(t, tt.want, gotScore, tt.msg) assert.Equal(t, tt.wantIP, gotIP, tt.msg) } } ================================================ FILE: common/rpc/middleware.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "context" "encoding/json" "io" "go.uber.org/cadence/worker" "go.uber.org/yarpc" "go.uber.org/yarpc/api/transport" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type authOutboundMiddleware struct { authProvider worker.AuthorizationProvider } func (m *authOutboundMiddleware) Call(ctx context.Context, request *transport.Request, out transport.UnaryOutbound) (*transport.Response, error) { if m.authProvider == nil { return out.Call(ctx, request) } token, err := m.authProvider.GetAuthToken() if err != nil { return nil, err } request.Headers = request.Headers. With(common.AuthorizationTokenHeaderName, string(token)) return out.Call(ctx, request) } type contextKey string const _responseInfoContextKey = contextKey("response-info") // ContextWithResponseInfo will create a child context that has ResponseInfo set as value. // This value will get filled after the call is made and can be used later to retrieve some info of interest. func ContextWithResponseInfo(parent context.Context) (context.Context, *ResponseInfo) { responseInfo := &ResponseInfo{} return context.WithValue(parent, _responseInfoContextKey, responseInfo), responseInfo } // ResponseInfo structure is filled with data after the RPC call. // It can be obtained with rpc.ContextWithResponseInfo function. type ResponseInfo struct { Size int } type countingReadCloser struct { reader io.ReadCloser bytesRead *int } func (r *countingReadCloser) Read(p []byte) (n int, err error) { n, err = r.reader.Read(p) *r.bytesRead += n return n, err } func (r *countingReadCloser) Close() (err error) { return r.reader.Close() } // ResponseInfoMiddleware populates context with ResponseInfo structure which contains info about response that was received. // In particular, it counts the size of the response in bytes. Such information can be useful down the line, where payload are deserialized and no longer have their size. type ResponseInfoMiddleware struct{} func (m *ResponseInfoMiddleware) Call(ctx context.Context, request *transport.Request, out transport.UnaryOutbound) (*transport.Response, error) { response, err := out.Call(ctx, request) if value := ctx.Value(_responseInfoContextKey); value != nil { if responseInfo, ok := value.(*ResponseInfo); ok && response != nil { // We can not use response.BodySize here, because it is not set on all transports. // Instead wrap body reader with counter, that increments responseInfo.Size as it is read. response.Body = &countingReadCloser{reader: response.Body, bytesRead: &responseInfo.Size} } } return response, err } // InboundMetricsMiddleware tags context with additional metric tags from incoming request. type InboundMetricsMiddleware struct{} func (m *InboundMetricsMiddleware) Handle(ctx context.Context, req *transport.Request, resw transport.ResponseWriter, h transport.UnaryHandler) error { ctx = metrics.TagContext(ctx, metrics.CallerTag(req.Caller), metrics.TransportTag(req.Transport), ) return h.Handle(ctx, req, resw) } // CallerInfoMiddleware extracts caller information from headers and adds it to the context. type CallerInfoMiddleware struct{} func (m *CallerInfoMiddleware) Handle(ctx context.Context, req *transport.Request, resw transport.ResponseWriter, h transport.UnaryHandler) error { ctx = types.GetContextWithCallerInfoFromHeaders(ctx, req.Headers) return h.Handle(ctx, req, resw) } // CallerInfoOutboundMiddleware sets the caller type header on outbound calls. // If the caller type is not already set in headers (by HeaderForwardingMiddleware), // it sets to "internal" only if the call originated from internal service logic. // External requests without the header are left unset and will be extracted as "unknown" by the receiving service. type CallerInfoOutboundMiddleware struct{} func (m *CallerInfoOutboundMiddleware) Call(ctx context.Context, request *transport.Request, out transport.UnaryOutbound) (*transport.Response, error) { if _, ok := request.Headers.Get(types.CallerTypeHeaderName); !ok { if yarpc.CallFromContext(ctx) == nil { request.Headers = request.Headers.With(types.CallerTypeHeaderName, string(types.CallerTypeInternal)) } } return out.Call(ctx, request) } type overrideCallerMiddleware struct { caller string } func (m *overrideCallerMiddleware) Call(ctx context.Context, request *transport.Request, out transport.UnaryOutbound) (*transport.Response, error) { request.Caller = m.caller return out.Call(ctx, request) } // HeaderForwardingMiddleware forwards headers from current inbound RPC call that is being handled to new outbound calls being made. // As this does NOT differentiate between transports or purposes, it generally assumes we are not acting as a true proxy, // so things like content lengths and encodings should not be forwarded - they will be provided by the outbound RPC library as needed. // // Duplicated headers retain the first value only, matching how browsers and Go (afaict) generally behave. // // This uses overly-simplified rules for choosing which headers are forwarded and which are not, intended to be lightly configurable. // For a more in-depth logic review if it becomes needed, check: // - How Go's ReverseProxy deals with headers, e.g. per-protocol and a list of exclusions: https://cs.opensource.google/go/go/+/refs/tags/go1.20.1:src/net/http/httputil/reverseproxy.go;l=332 // - HTTP's spec for headers, namely how duplicates and Connection work: https://www.rfc-editor.org/rfc/rfc9110.html#name-header-fields // - Many browsers prefer first-value-wins for unexpected duplicates: https://bugzilla.mozilla.org/show_bug.cgi?id=376756 // - But there are MANY map-like implementations that choose last-value wins, and this mismatch is a source of frequent security problems. // - YARPC's `With` only retains the last call's value: https://github.com/yarpc/yarpc-go/blob/8ccd79a2ca696150213faac1d35011c5be52e5fb/api/transport/header.go#L69-L77 // - Go's MIMEHeader's Get (used by YARPC) only returns the first value, and does not join duplicates: https://pkg.go.dev/net/textproto#MIMEHeader.Get // // There is likely no correct choice, as it depends on the recipients' behavior. // If we need to support more complex logic, it's likely worth jumping to a fully-controllable thing. // Middle-grounds will probably just need to be changed again later. type HeaderForwardingMiddleware struct { // Rules are applied in order to add or remove headers by regex. // // There are no default rules, so by default no headers are copied. // To include headers by default, Add with a permissive regex and then remove specific ones. Rules []config.HeaderRule } func (m *HeaderForwardingMiddleware) Call(ctx context.Context, request *transport.Request, out transport.UnaryOutbound) (*transport.Response, error) { if inboundCall := yarpc.CallFromContext(ctx); inboundCall != nil { outboundHeaders := request.Headers pending := make(map[string][]string, len(inboundCall.HeaderNames())) names := inboundCall.HeaderNames() for _, rule := range m.Rules { for _, key := range names { if !rule.Match.MatchString(key) { continue } if _, ok := outboundHeaders.Get(key); ok { continue // do not overwrite existing headers } if rule.Add { pending[key] = append(pending[key], inboundCall.Header(key)) } else { delete(pending, key) } } } for k, vs := range pending { // yarpc's Headers.With keeps the LAST value, but we (and browsers) prefer the FIRST, // and we do not canonicalize duplicates. request.Headers = request.Headers.With(k, vs[0]) } } return out.Call(ctx, request) } // ForwardPartitionConfigMiddleware forwards the partition config to remote cluster // The middleware should always be applied after any other middleware that inject partition config into the context // so that it can overwrites the partition config into the context // The purpose of this middleware is to make sure the partition config doesn't change when a request is forwarded from // passive cluster to the active cluster type ForwardPartitionConfigMiddleware struct{} func (m *ForwardPartitionConfigMiddleware) Handle(ctx context.Context, req *transport.Request, resw transport.ResponseWriter, h transport.UnaryHandler) error { if _, ok := req.Headers.Get(common.AutoforwardingClusterHeaderName); ok { var partitionConfig map[string]string if blob, ok := req.Headers.Get(common.PartitionConfigHeaderName); ok && len(blob) > 0 { if err := json.Unmarshal([]byte(blob), &partitionConfig); err != nil { return err } } ctx = isolationgroup.ContextWithConfig(ctx, partitionConfig) isolationGroup, _ := req.Headers.Get(common.IsolationGroupHeaderName) ctx = isolationgroup.ContextWithIsolationGroup(ctx, isolationGroup) } return h.Handle(ctx, req, resw) } func (m *ForwardPartitionConfigMiddleware) Call(ctx context.Context, request *transport.Request, out transport.UnaryOutbound) (*transport.Response, error) { if _, ok := request.Headers.Get(common.AutoforwardingClusterHeaderName); ok { partitionConfig := isolationgroup.ConfigFromContext(ctx) if len(partitionConfig) > 0 { blob, err := json.Marshal(partitionConfig) if err != nil { return nil, err } request.Headers = request.Headers.With(common.PartitionConfigHeaderName, string(blob)) } else { request.Headers.Del(common.PartitionConfigHeaderName) } isolationGroup := isolationgroup.IsolationGroupFromContext(ctx) if isolationGroup != "" { request.Headers = request.Headers.With(common.IsolationGroupHeaderName, isolationGroup) } else { request.Headers.Del(common.IsolationGroupHeaderName) } } return out.Call(ctx, request) } // ClientPartitionConfigMiddleware stores the partition config and isolation group of the request into the context // It reads a header from client request and uses it as the isolation group type ClientPartitionConfigMiddleware struct{} func (m *ClientPartitionConfigMiddleware) Handle(ctx context.Context, req *transport.Request, resw transport.ResponseWriter, h transport.UnaryHandler) error { zone, _ := req.Headers.Get(common.ClientIsolationGroupHeaderName) if zone != "" { ctx = isolationgroup.ContextWithConfig(ctx, map[string]string{ isolationgroup.GroupKey: zone, }) ctx = isolationgroup.ContextWithIsolationGroup(ctx, zone) } return h.Handle(ctx, req, resw) } ================================================ FILE: common/rpc/middleware_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "bytes" "context" "encoding/json" "fmt" "io/ioutil" "regexp" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/yarpctest" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) func TestAuthOubboundMiddleware(t *testing.T) { m := authOutboundMiddleware{} _, err := m.Call(context.Background(), &transport.Request{}, &fakeOutbound{verify: func(request *transport.Request) { assert.Empty(t, request.Headers) }}) assert.NoError(t, err) m = authOutboundMiddleware{fakeAuthProvider{err: assert.AnError}} _, err = m.Call(context.Background(), &transport.Request{}, &fakeOutbound{}) assert.Error(t, err) m = authOutboundMiddleware{fakeAuthProvider{token: []byte("token")}} _, err = m.Call(context.Background(), &transport.Request{}, &fakeOutbound{verify: func(request *transport.Request) { assert.Equal(t, "token", request.Headers.Items()[common.AuthorizationTokenHeaderName]) }}) assert.NoError(t, err) } func TestResponseInfoMiddleware(t *testing.T) { m := ResponseInfoMiddleware{} ctx, responseInfo := ContextWithResponseInfo(context.Background()) body := ioutil.NopCloser(bytes.NewReader([]byte{1, 2, 3, 4, 5})) response, err := m.Call(ctx, &transport.Request{}, &fakeOutbound{response: &transport.Response{Body: body}}) assert.NoError(t, err) ioutil.ReadAll(response.Body) assert.Equal(t, 5, responseInfo.Size) } func TestResponseInfoMiddleware_Error(t *testing.T) { m := ResponseInfoMiddleware{} ctx, responseInfo := ContextWithResponseInfo(context.Background()) _, err := m.Call(ctx, &transport.Request{}, &fakeOutbound{err: fmt.Errorf("test")}) assert.Error(t, err) assert.Equal(t, 0, responseInfo.Size) } func TestInboundMetricsMiddleware(t *testing.T) { m := InboundMetricsMiddleware{} h := &fakeHandler{} err := m.Handle(context.Background(), &transport.Request{Transport: "grpc", Caller: "x-caller"}, nil, h) assert.NoError(t, err) assert.ElementsMatch(t, metrics.GetContextTags(h.ctx), []metrics.Tag{ metrics.TransportTag("grpc"), metrics.CallerTag("x-caller"), }) } func TestOverrideCallerMiddleware(t *testing.T) { m := overrideCallerMiddleware{"x-caller"} _, err := m.Call(context.Background(), &transport.Request{Caller: "service"}, &fakeOutbound{verify: func(r *transport.Request) { assert.Equal(t, "x-caller", r.Caller) }}) assert.NoError(t, err) } func TestHeaderForwardingMiddleware(t *testing.T) { inboundHeaders := map[string]string{ "key-a": "inbound-value-a", "key-b": "inbound-value-b", "key-x": "inbound-value-x", } outboundHeaders := map[string]string{ "key-b": "outbound-value-b", "key-c": "outbound-value-c", } combinedHeaders := map[string]string{ "key-a": "inbound-value-a", "key-b": "outbound-value-b", "key-c": "outbound-value-c", "key-x": "inbound-value-x", } combinedHeadersWithoutX := map[string]string{ "key-a": "inbound-value-a", "key-b": "outbound-value-b", "key-c": "outbound-value-c", } ctxWithInbound := yarpctest.ContextWithCall(context.Background(), &yarpctest.Call{Headers: inboundHeaders}) makeRequest := func() *transport.Request { // request and headers are mutated by Call, so we must not share data return &transport.Request{Headers: transport.HeadersFromMap(outboundHeaders)} } t.Run("default rules", func(t *testing.T) { t.Parallel() m := HeaderForwardingMiddleware{ Rules: []config.HeaderRule{ // default { Add: true, Match: regexp.MustCompile(""), }, }, } t.Run("no inbound makes no changes", func(t *testing.T) { t.Parallel() // No ongoing inbound call -> keep existing outbound headers _, err := m.Call(context.Background(), makeRequest(), &fakeOutbound{verify: func(r *transport.Request) { assert.Equal(t, outboundHeaders, r.Headers.Items()) }}) assert.NoError(t, err) }) t.Run("inbound headers merge missing", func(t *testing.T) { t.Parallel() // With ongoing inbound call -> forward inbound headers not present in the request _, err := m.Call(ctxWithInbound, makeRequest(), &fakeOutbound{verify: func(r *transport.Request) { assert.Equal(t, combinedHeaders, r.Headers.Items()) }}) assert.NoError(t, err) }) }) t.Run("can exclude inbound headers", func(t *testing.T) { t.Parallel() m := HeaderForwardingMiddleware{ Rules: []config.HeaderRule{ // add by default, earlier tests ensure this works { Add: true, Match: regexp.MustCompile(""), }, // remove X { Add: false, Match: regexp.MustCompile("(?i)Key-x"), }, }, } _, err := m.Call(ctxWithInbound, makeRequest(), &fakeOutbound{verify: func(r *transport.Request) { assert.Equal(t, combinedHeadersWithoutX, r.Headers.Items()) }}) assert.NoError(t, err) }) } func TestForwardPartitionConfigMiddleware(t *testing.T) { t.Run("inbound middleware", func(t *testing.T) { partitionConfig := map[string]string{"isolation-group": "xyz"} blob, err := json.Marshal(partitionConfig) require.NoError(t, err) testCases := []struct { message string headers transport.Headers ctx context.Context expectedPartitionConfig map[string]string expectedIsolationGroup string }{ { message: "it injects partition config into context", headers: transport.NewHeaders(). With(common.PartitionConfigHeaderName, string(blob)). With(common.IsolationGroupHeaderName, "abc"). With(common.AutoforwardingClusterHeaderName, "cluster0"), ctx: context.Background(), expectedPartitionConfig: partitionConfig, expectedIsolationGroup: "abc", }, { message: "it overwrites the existing partition config in the context", headers: transport.NewHeaders(). With(common.PartitionConfigHeaderName, string(blob)). With(common.IsolationGroupHeaderName, "abc"). With(common.AutoforwardingClusterHeaderName, "cluster0"), ctx: isolationgroup.ContextWithIsolationGroup(isolationgroup.ContextWithConfig(context.Background(), map[string]string{"z": "x"}), "fff"), expectedPartitionConfig: partitionConfig, expectedIsolationGroup: "abc", }, { message: "it overwrites the existing partition config in the context with nil config", headers: transport.NewHeaders(). With(common.AutoforwardingClusterHeaderName, "cluster0"), ctx: isolationgroup.ContextWithIsolationGroup(isolationgroup.ContextWithConfig(context.Background(), map[string]string{"z": "x"}), "fff"), expectedPartitionConfig: nil, expectedIsolationGroup: "", }, { message: "it injects partition config into context only if the request is an auto-fowarding request", headers: transport.NewHeaders(). With(common.PartitionConfigHeaderName, string(blob)). With(common.IsolationGroupHeaderName, "abc"), ctx: context.Background(), expectedPartitionConfig: nil, expectedIsolationGroup: "", }, } for _, tt := range testCases { t.Run(tt.message, func(t *testing.T) { m := &ForwardPartitionConfigMiddleware{} h := &fakeHandler{} err := m.Handle(tt.ctx, &transport.Request{Headers: tt.headers}, nil, h) assert.NoError(t, err) assert.Equal(t, tt.expectedPartitionConfig, isolationgroup.ConfigFromContext(h.ctx)) assert.Equal(t, tt.expectedIsolationGroup, isolationgroup.IsolationGroupFromContext(h.ctx)) }) } }) t.Run("outbound middleware", func(t *testing.T) { partitionConfig := map[string]string{"isolation-group": "xyz"} blob, err := json.Marshal(partitionConfig) require.NoError(t, err) testCases := []struct { message string headers transport.Headers ctx context.Context expectedSerializedPartitionConfig string expectedIsolationGroup string }{ { message: "it retrieves partition config from the context and sets it in the request header", headers: transport.NewHeaders(). With(common.AutoforwardingClusterHeaderName, "cluster0"), ctx: isolationgroup.ContextWithIsolationGroup(isolationgroup.ContextWithConfig(context.Background(), partitionConfig), "abc"), expectedSerializedPartitionConfig: string(blob), expectedIsolationGroup: "abc", }, { message: "it retrieves partition config from the context and overwrites the existing request header", headers: transport.NewHeaders(). With(common.IsolationGroupHeaderName, "lll"). With(common.PartitionConfigHeaderName, "asdfasf"). With(common.AutoforwardingClusterHeaderName, "cluster0"), ctx: isolationgroup.ContextWithIsolationGroup(isolationgroup.ContextWithConfig(context.Background(), partitionConfig), "abc"), expectedSerializedPartitionConfig: string(blob), expectedIsolationGroup: "abc", }, { message: "it deletes the existing request header if we cannot retrieve partition config from the context", headers: transport.NewHeaders(). With(common.IsolationGroupHeaderName, "lll"). With(common.PartitionConfigHeaderName, "asdfasf"). With(common.AutoforwardingClusterHeaderName, "cluster0"), ctx: context.Background(), expectedSerializedPartitionConfig: "", expectedIsolationGroup: "", }, { message: "it retrieves partition config from the context and sets it in the request header only if the request is an auto-forwarding request", headers: transport.NewHeaders(). With(common.IsolationGroupHeaderName, "lll"). With(common.PartitionConfigHeaderName, "asdfasf"), ctx: isolationgroup.ContextWithIsolationGroup(isolationgroup.ContextWithConfig(context.Background(), partitionConfig), "abc"), expectedSerializedPartitionConfig: "asdfasf", expectedIsolationGroup: "lll", }, } for _, tt := range testCases { t.Run(tt.message, func(t *testing.T) { m := &ForwardPartitionConfigMiddleware{} o := &fakeOutbound{ verify: func(r *transport.Request) { assert.Equal(t, tt.expectedIsolationGroup, r.Headers.Items()[common.IsolationGroupHeaderName]) assert.Equal(t, tt.expectedSerializedPartitionConfig, r.Headers.Items()[common.PartitionConfigHeaderName]) }, } _, err := m.Call(tt.ctx, &transport.Request{Headers: tt.headers}, o) assert.NoError(t, err) }) } }) } func TestClientPartitionConfigMiddleware(t *testing.T) { t.Run("it sets the partition config", func(t *testing.T) { m := &ClientPartitionConfigMiddleware{} h := &fakeHandler{} headers := transport.NewHeaders(). With(common.ClientIsolationGroupHeaderName, "dca1") err := m.Handle(context.Background(), &transport.Request{Headers: headers}, nil, h) assert.NoError(t, err) assert.Equal(t, map[string]string{isolationgroup.GroupKey: "dca1"}, isolationgroup.ConfigFromContext(h.ctx)) assert.Equal(t, "dca1", isolationgroup.IsolationGroupFromContext(h.ctx)) }) t.Run("noop when header is empty", func(t *testing.T) { m := &ClientPartitionConfigMiddleware{} h := &fakeHandler{} headers := transport.NewHeaders() ctx := context.Background() err := m.Handle(ctx, &transport.Request{Headers: headers}, nil, h) assert.NoError(t, err) assert.Nil(t, isolationgroup.ConfigFromContext(h.ctx)) assert.Equal(t, "", isolationgroup.IsolationGroupFromContext(h.ctx)) assert.Equal(t, ctx, h.ctx) }) } func TestCallerInfoMiddleware(t *testing.T) { t.Run("extracts caller type from header", func(t *testing.T) { m := &CallerInfoMiddleware{} h := &fakeHandler{} headers := transport.NewHeaders().With(types.CallerTypeHeaderName, "cli") err := m.Handle(context.Background(), &transport.Request{Headers: headers}, nil, h) assert.NoError(t, err) callerInfo := types.GetCallerInfoFromContext(h.ctx) assert.Equal(t, types.CallerTypeCLI, callerInfo.GetCallerType()) }) t.Run("sets unknown caller type when header is missing", func(t *testing.T) { m := &CallerInfoMiddleware{} h := &fakeHandler{} headers := transport.NewHeaders() err := m.Handle(context.Background(), &transport.Request{Headers: headers}, nil, h) assert.NoError(t, err) callerInfo := types.GetCallerInfoFromContext(h.ctx) assert.Equal(t, types.CallerTypeUnknown, callerInfo.GetCallerType()) }) t.Run("extracts different caller types", func(t *testing.T) { tests := []struct { name string headerValue string expectedCaller types.CallerType }{ {"CLI", "cli", types.CallerTypeCLI}, {"UI", "ui", types.CallerTypeUI}, {"SDK", "sdk", types.CallerTypeSDK}, {"Internal", "internal", types.CallerTypeInternal}, {"Empty", "", types.CallerTypeUnknown}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &CallerInfoMiddleware{} h := &fakeHandler{} headers := transport.NewHeaders().With(types.CallerTypeHeaderName, tt.headerValue) err := m.Handle(context.Background(), &transport.Request{Headers: headers}, nil, h) assert.NoError(t, err) callerInfo := types.GetCallerInfoFromContext(h.ctx) assert.Equal(t, tt.expectedCaller, callerInfo.GetCallerType()) }) } }) } func TestCallerInfoOutboundMiddleware(t *testing.T) { t.Run("sets internal caller type when no inbound call and header is missing", func(t *testing.T) { m := &CallerInfoOutboundMiddleware{} _, err := m.Call(context.Background(), &transport.Request{}, &fakeOutbound{verify: func(r *transport.Request) { callerType, ok := r.Headers.Get(types.CallerTypeHeaderName) assert.True(t, ok) assert.Equal(t, string(types.CallerTypeInternal), callerType) }}) assert.NoError(t, err) }) t.Run("does not set header when inbound call exists without header", func(t *testing.T) { m := &CallerInfoOutboundMiddleware{} ctxWithInbound := yarpctest.ContextWithCall(context.Background(), &yarpctest.Call{}) _, err := m.Call(ctxWithInbound, &transport.Request{}, &fakeOutbound{verify: func(r *transport.Request) { _, ok := r.Headers.Get(types.CallerTypeHeaderName) assert.False(t, ok, "header should not be set for external requests without header") }}) assert.NoError(t, err) }) t.Run("does not override existing caller type header", func(t *testing.T) { m := &CallerInfoOutboundMiddleware{} headers := transport.NewHeaders().With(types.CallerTypeHeaderName, "cli") _, err := m.Call(context.Background(), &transport.Request{Headers: headers}, &fakeOutbound{verify: func(r *transport.Request) { callerType, ok := r.Headers.Get(types.CallerTypeHeaderName) assert.True(t, ok) assert.Equal(t, "cli", callerType) }}) assert.NoError(t, err) }) t.Run("does not override header forwarded from inbound call", func(t *testing.T) { m := &CallerInfoOutboundMiddleware{} ctxWithInbound := yarpctest.ContextWithCall(context.Background(), &yarpctest.Call{ Headers: map[string]string{types.CallerTypeHeaderName: "ui"}, }) headers := transport.NewHeaders().With(types.CallerTypeHeaderName, "ui") _, err := m.Call(ctxWithInbound, &transport.Request{Headers: headers}, &fakeOutbound{verify: func(r *transport.Request) { callerType, ok := r.Headers.Get(types.CallerTypeHeaderName) assert.True(t, ok) assert.Equal(t, "ui", callerType, "should preserve forwarded header") }}) assert.NoError(t, err) }) } type fakeHandler struct { ctx context.Context } func (h *fakeHandler) Handle(ctx context.Context, req *transport.Request, resw transport.ResponseWriter) error { h.ctx = ctx return nil } type fakeOutbound struct { verify func(*transport.Request) response *transport.Response err error } func (o fakeOutbound) Call(ctx context.Context, request *transport.Request) (*transport.Response, error) { if o.verify != nil { o.verify(request) } return o.response, o.err } func (o fakeOutbound) Start() error { return nil } func (o fakeOutbound) Stop() error { return nil } func (o fakeOutbound) IsRunning() bool { return true } func (o fakeOutbound) Transports() []transport.Transport { return nil } type fakeAuthProvider struct { token []byte err error } func (p fakeAuthProvider) GetAuthToken() ([]byte, error) { return p.token, p.err } ================================================ FILE: common/rpc/outbounds.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination outbounds_mock.go -self_package github.com/uber/cadence/common/rpc package rpc import ( "crypto/tls" "fmt" "go.uber.org/multierr" "go.uber.org/yarpc" "go.uber.org/yarpc/api/middleware" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/transport/grpc" "go.uber.org/yarpc/transport/tchannel" "github.com/uber/cadence/common/authorization" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" ) const ( // OutboundPublicClient is the name of configured public client outbound OutboundPublicClient = "public-client" crossDCCaller = "cadence-xdc-client" ) // OutboundsBuilder allows defining outbounds for the dispatcher type OutboundsBuilder interface { // Build creates yarpc outbounds given transport instances for either gRPC and TChannel based on the configuration Build(*grpc.Transport, *tchannel.Transport) (*Outbounds, error) } type Outbounds struct { yarpc.Outbounds onUpdatePeers func(serviceName string, members []membership.HostInfo) } func (o *Outbounds) UpdatePeers(serviceName string, peers []membership.HostInfo) { if o.onUpdatePeers != nil { o.onUpdatePeers(serviceName, peers) } } type multiOutboundsBuilder struct { builders []OutboundsBuilder } // CombineOutbounds takes multiple outbound builders and combines them func CombineOutbounds(builders ...OutboundsBuilder) OutboundsBuilder { return multiOutboundsBuilder{builders} } func (b multiOutboundsBuilder) Build(grpc *grpc.Transport, tchannel *tchannel.Transport) (*Outbounds, error) { outbounds := yarpc.Outbounds{} var errs error var callbacks []func(string, []membership.HostInfo) for _, builder := range b.builders { builderOutbounds, err := builder.Build(grpc, tchannel) if err != nil { errs = multierr.Append(errs, err) continue } if builderOutbounds.onUpdatePeers != nil { callbacks = append(callbacks, builderOutbounds.onUpdatePeers) } for name, outbound := range builderOutbounds.Outbounds { if _, exists := outbounds[name]; exists { errs = multierr.Append(errs, fmt.Errorf("outbound %q already configured", name)) break } outbounds[name] = outbound } } return &Outbounds{ Outbounds: outbounds, onUpdatePeers: func(serviceName string, members []membership.HostInfo) { for _, callback := range callbacks { callback(serviceName, members) } }, }, errs } type publicClientOutbound struct { address string isGRPC bool authMiddleware middleware.UnaryOutbound } func newPublicClientOutbound(config *config.Config) (publicClientOutbound, error) { if len(config.PublicClient.HostPort) == 0 { return publicClientOutbound{}, fmt.Errorf("need to provide an endpoint config for PublicClient") } var authMiddleware middleware.UnaryOutbound if config.Authorization.OAuthAuthorizer.Enable { clusterName := config.ClusterGroupMetadata.CurrentClusterName clusterInfo := config.ClusterGroupMetadata.ClusterGroup[clusterName] authProvider, err := authorization.GetAuthProviderClient(clusterInfo.AuthorizationProvider.PrivateKey) if err != nil { return publicClientOutbound{}, fmt.Errorf("create AuthProvider: %v", err) } authMiddleware = &authOutboundMiddleware{authProvider} } isGrpc := config.PublicClient.Transport == grpc.TransportName return publicClientOutbound{config.PublicClient.HostPort, isGrpc, authMiddleware}, nil } func (b publicClientOutbound) Build(grpc *grpc.Transport, tchannel *tchannel.Transport) (*Outbounds, error) { var outbound transport.UnaryOutbound if b.isGRPC { outbound = grpc.NewSingleOutbound(b.address) } else { outbound = tchannel.NewSingleOutbound(b.address) } return &Outbounds{ Outbounds: yarpc.Outbounds{ OutboundPublicClient: { ServiceName: service.Frontend, Unary: middleware.ApplyUnaryOutbound(outbound, b.authMiddleware), }, }, }, nil } type crossDCOutbounds struct { clusterGroup map[string]config.ClusterInformation pcf PeerChooserFactory } func NewCrossDCOutbounds(clusterGroup map[string]config.ClusterInformation, pcf PeerChooserFactory) OutboundsBuilder { return crossDCOutbounds{clusterGroup, pcf} } func (b crossDCOutbounds) Build(grpcTransport *grpc.Transport, tchannelTransport *tchannel.Transport) (*Outbounds, error) { outbounds := yarpc.Outbounds{} for clusterName, clusterInfo := range b.clusterGroup { if !clusterInfo.Enabled { continue } var outbound transport.UnaryOutbound switch clusterInfo.RPCTransport { case tchannel.TransportName: peerChooser, err := b.pcf.CreatePeerChooser(tchannelTransport, PeerChooserOptions{Address: clusterInfo.RPCAddress}) if err != nil { return nil, err } outbound = tchannelTransport.NewOutbound(peerChooser) case grpc.TransportName: tlsConfig, err := clusterInfo.TLS.ToTLSConfig() if err != nil { return nil, err } peerChooser, err := b.pcf.CreatePeerChooser(createDialer(grpcTransport, tlsConfig), PeerChooserOptions{Address: clusterInfo.RPCAddress}) if err != nil { return nil, err } outbound = grpcTransport.NewOutbound(peerChooser) default: return nil, fmt.Errorf("unknown cross DC transport type: %s", clusterInfo.RPCTransport) } var authMiddleware middleware.UnaryOutbound if clusterInfo.AuthorizationProvider.Enable { authProvider, err := authorization.GetAuthProviderClient(clusterInfo.AuthorizationProvider.PrivateKey) if err != nil { return nil, fmt.Errorf("create AuthProvider: %v", err) } authMiddleware = &authOutboundMiddleware{authProvider} } outbounds[clusterName] = transport.Outbounds{ ServiceName: clusterInfo.RPCName, Unary: middleware.ApplyUnaryOutbound(outbound, yarpc.UnaryOutboundMiddleware( authMiddleware, &CallerInfoOutboundMiddleware{}, &overrideCallerMiddleware{crossDCCaller}, )), } } return &Outbounds{Outbounds: outbounds}, nil } type directOutbound struct { serviceName string grpcEnabled bool tlsConfig *tls.Config pcf PeerChooserFactory enableConnRetainMode dynamicproperties.BoolPropertyFn } func NewDirectOutboundBuilder(serviceName string, grpcEnabled bool, tlsConfig *tls.Config, pcf PeerChooserFactory, enableConnRetainMode dynamicproperties.BoolPropertyFn) OutboundsBuilder { return directOutbound{serviceName, grpcEnabled, tlsConfig, pcf, enableConnRetainMode} } func (o directOutbound) Build(grpc *grpc.Transport, tchannel *tchannel.Transport) (*Outbounds, error) { var outbound transport.UnaryOutbound var streamOutbound transport.StreamOutbound opts := PeerChooserOptions{ EnableConnectionRetainingDirectChooser: o.enableConnRetainMode, ServiceName: o.serviceName, } var err error var directChooser PeerChooser if o.grpcEnabled { directChooser, err = o.pcf.CreatePeerChooser(createDialer(grpc, o.tlsConfig), opts) if err != nil { return nil, err } outbound = grpc.NewOutbound(directChooser) // Shard manager needs stream outbound, it only supports GRPC, so we don't need to create a tchannel stream outbound streamOutbound = grpc.NewOutbound(directChooser) } else { directChooser, err = o.pcf.CreatePeerChooser(tchannel, opts) if err != nil { return nil, err } outbound = tchannel.NewOutbound(directChooser) } return &Outbounds{ Outbounds: yarpc.Outbounds{ o.serviceName: { ServiceName: o.serviceName, Unary: middleware.ApplyUnaryOutbound(outbound, yarpc.UnaryOutboundMiddleware(&CallerInfoOutboundMiddleware{}, &ResponseInfoMiddleware{})), Stream: streamOutbound, }, }, onUpdatePeers: directChooser.UpdatePeers, }, nil } func IsGRPCOutbound(config transport.ClientConfig) bool { namer, ok := config.GetUnaryOutbound().(transport.Namer) if !ok { // This should not happen, unless yarpc older than v1.43.0 is used panic("Outbound does not implement transport.Namer") } return namer.TransportName() == grpc.TransportName } func NewSingleGRPCOutboundBuilder(outboundName string, serviceName string, address string) OutboundsBuilder { return singleGRPCOutbound{outboundName, serviceName, address} } type singleGRPCOutbound struct { outboundName string serviceName string address string } func (b singleGRPCOutbound) Build(grpc *grpc.Transport, _ *tchannel.Transport) (*Outbounds, error) { return &Outbounds{ Outbounds: yarpc.Outbounds{ b.outboundName: { ServiceName: b.serviceName, Unary: grpc.NewSingleOutbound(b.address), Stream: grpc.NewSingleOutbound(b.address), }, }, }, nil } ================================================ FILE: common/rpc/outbounds_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: outbounds.go // // Generated by this command: // // mockgen -package rpc -source outbounds.go -destination outbounds_mock.go -self_package github.com/uber/cadence/common/rpc // // Package rpc is a generated GoMock package. package rpc import ( reflect "reflect" gomock "go.uber.org/mock/gomock" grpc "go.uber.org/yarpc/transport/grpc" tchannel "go.uber.org/yarpc/transport/tchannel" ) // MockOutboundsBuilder is a mock of OutboundsBuilder interface. type MockOutboundsBuilder struct { ctrl *gomock.Controller recorder *MockOutboundsBuilderMockRecorder isgomock struct{} } // MockOutboundsBuilderMockRecorder is the mock recorder for MockOutboundsBuilder. type MockOutboundsBuilderMockRecorder struct { mock *MockOutboundsBuilder } // NewMockOutboundsBuilder creates a new mock instance. func NewMockOutboundsBuilder(ctrl *gomock.Controller) *MockOutboundsBuilder { mock := &MockOutboundsBuilder{ctrl: ctrl} mock.recorder = &MockOutboundsBuilderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockOutboundsBuilder) EXPECT() *MockOutboundsBuilderMockRecorder { return m.recorder } // Build mocks base method. func (m *MockOutboundsBuilder) Build(arg0 *grpc.Transport, arg1 *tchannel.Transport) (*Outbounds, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Build", arg0, arg1) ret0, _ := ret[0].(*Outbounds) ret1, _ := ret[1].(error) return ret0, ret1 } // Build indicates an expected call of Build. func (mr *MockOutboundsBuilderMockRecorder) Build(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Build", reflect.TypeOf((*MockOutboundsBuilder)(nil).Build), arg0, arg1) } ================================================ FILE: common/rpc/outbounds_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "errors" "io/ioutil" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/yarpc" "go.uber.org/yarpc/api/peer" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/peer/direct" "go.uber.org/yarpc/transport/grpc" "go.uber.org/yarpc/transport/tchannel" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/service" ) func TestCombineOutbounds(t *testing.T) { grpc := &grpc.Transport{} tchannel := &tchannel.Transport{} combined := CombineOutbounds() outbounds, err := combined.Build(grpc, tchannel) assert.NoError(t, err) assert.Empty(t, outbounds.Outbounds) combined = CombineOutbounds(fakeOutboundBuilder{err: errors.New("err-A")}) _, err = combined.Build(grpc, tchannel) assert.EqualError(t, err, "err-A") combined = CombineOutbounds( fakeOutboundBuilder{outbounds: yarpc.Outbounds{"A": {}}}, fakeOutboundBuilder{outbounds: yarpc.Outbounds{"A": {}}}, ) _, err = combined.Build(grpc, tchannel) assert.EqualError(t, err, "outbound \"A\" already configured") combined = CombineOutbounds( fakeOutboundBuilder{err: errors.New("err-A")}, fakeOutboundBuilder{err: errors.New("err-B")}, ) _, err = combined.Build(grpc, tchannel) assert.EqualError(t, err, "err-A; err-B") combined = CombineOutbounds( fakeOutboundBuilder{outbounds: yarpc.Outbounds{"A": {}}}, fakeOutboundBuilder{outbounds: yarpc.Outbounds{"B": {}, "C": {}}}, ) outbounds, err = combined.Build(grpc, tchannel) assert.NoError(t, err) assert.Equal(t, yarpc.Outbounds{ "A": {}, "B": {}, "C": {}, }, outbounds.Outbounds) } func TestPublicClientOutbound(t *testing.T) { makeConfig := func(hostPort string, transport string, enableAuth bool, keyPath string) *config.Config { return &config.Config{ PublicClient: config.PublicClient{HostPort: hostPort, Transport: transport}, Authorization: config.Authorization{OAuthAuthorizer: config.OAuthAuthorizer{Enable: enableAuth}}, ClusterGroupMetadata: &config.ClusterGroupMetadata{ CurrentClusterName: "cluster-A", ClusterGroup: map[string]config.ClusterInformation{ "cluster-A": { AuthorizationProvider: config.AuthorizationProvider{ PrivateKey: keyPath, }, }, }, }, } } _, err := newPublicClientOutbound(&config.Config{}) require.EqualError(t, err, "need to provide an endpoint config for PublicClient") builder, err := newPublicClientOutbound(makeConfig("localhost:1234", "tchannel", false, "")) require.NoError(t, err) require.NotNil(t, builder) require.Equal(t, "localhost:1234", builder.address) require.Equal(t, nil, builder.authMiddleware) require.False(t, builder.isGRPC) builder, err = newPublicClientOutbound(makeConfig("localhost:1234", "tchannel", true, "invalid")) require.EqualError(t, err, "create AuthProvider: invalid private key path invalid") require.False(t, builder.isGRPC) builder, err = newPublicClientOutbound(makeConfig("localhost:1234", "grpc", true, tempFile(t, "private-key"))) require.NoError(t, err) require.NotNil(t, builder) require.Equal(t, "localhost:1234", builder.address) require.NotNil(t, builder.authMiddleware) require.True(t, builder.isGRPC) grpc := &grpc.Transport{} tchannel := &tchannel.Transport{} o, err := builder.Build(grpc, tchannel) require.NoError(t, err) outbounds := o.Outbounds assert.Equal(t, outbounds[OutboundPublicClient].ServiceName, service.Frontend) assert.NotNil(t, outbounds[OutboundPublicClient].Unary) } func TestCrossDCOutbounds(t *testing.T) { grpc := &grpc.Transport{} tchannel := &tchannel.Transport{} clusterGroup := map[string]config.ClusterInformation{ "cluster-A": {Enabled: true, RPCName: "cadence-frontend", RPCTransport: "invalid"}, } _, err := NewCrossDCOutbounds(clusterGroup, &fakePeerChooserFactory{}).Build(grpc, tchannel) assert.EqualError(t, err, "unknown cross DC transport type: invalid") clusterGroup = map[string]config.ClusterInformation{ "cluster-A": {Enabled: true, RPCName: "cadence-frontend", RPCTransport: "grpc", AuthorizationProvider: config.AuthorizationProvider{Enable: true, PrivateKey: "invalid path"}}, } _, err = NewCrossDCOutbounds(clusterGroup, &fakePeerChooserFactory{}).Build(grpc, tchannel) assert.EqualError(t, err, "create AuthProvider: invalid private key path invalid path") clusterGroup = map[string]config.ClusterInformation{ "cluster-A": {Enabled: true, RPCName: "cadence-frontend", RPCAddress: "address-A", RPCTransport: "grpc", AuthorizationProvider: config.AuthorizationProvider{Enable: true, PrivateKey: tempFile(t, "key")}}, "cluster-B": {Enabled: true, RPCName: "cadence-frontend", RPCAddress: "address-B", RPCTransport: "tchannel"}, "cluster-C": {Enabled: false}, } o, err := NewCrossDCOutbounds(clusterGroup, &fakePeerChooserFactory{}).Build(grpc, tchannel) assert.NoError(t, err) outbounds := o.Outbounds assert.Equal(t, 2, len(outbounds)) assert.Equal(t, "cadence-frontend", outbounds["cluster-A"].ServiceName) assert.Equal(t, "cadence-frontend", outbounds["cluster-B"].ServiceName) assert.NotNil(t, outbounds["cluster-A"].Unary) assert.NotNil(t, outbounds["cluster-B"].Unary) } func TestDirectOutbound(t *testing.T) { grpc := &grpc.Transport{} tchannel := &tchannel.Transport{} logger := testlogger.New(t) metricCl := metrics.NewNoopMetricsClient() falseFn := func(opts ...dynamicproperties.FilterOption) bool { return false } o, err := NewDirectOutboundBuilder("cadence-history", false, nil, NewDirectPeerChooserFactory("cadence-history", logger, metricCl), falseFn).Build(grpc, tchannel) assert.NoError(t, err) outbounds := o.Outbounds assert.Equal(t, "cadence-history", outbounds["cadence-history"].ServiceName) assert.NotNil(t, outbounds["cadence-history"].Unary) o, err = NewDirectOutboundBuilder("cadence-history", true, nil, NewDirectPeerChooserFactory("cadence-history", logger, metricCl), falseFn).Build(grpc, tchannel) assert.NoError(t, err) outbounds = o.Outbounds assert.Equal(t, "cadence-history", outbounds["cadence-history"].ServiceName) assert.NotNil(t, outbounds["cadence-history"].Unary) } func TestSingleGRPCOutbound(t *testing.T) { grpc := &grpc.Transport{} tchannel := &tchannel.Transport{} builder := NewSingleGRPCOutboundBuilder("grpc-only-out", "grpc-service-name", "http://example.com:1234") outBound, err := builder.Build(grpc, tchannel) assert.NoError(t, err) assert.Equal(t, "grpc-service-name", outBound.Outbounds["grpc-only-out"].ServiceName) assert.NotNil(t, outBound.Outbounds["grpc-only-out"].Unary) } func TestIsGRPCOutboud(t *testing.T) { assert.True(t, IsGRPCOutbound(&transport.OutboundConfig{Outbounds: transport.Outbounds{Unary: (&grpc.Transport{}).NewSingleOutbound("localhost:1234")}})) assert.False(t, IsGRPCOutbound(&transport.OutboundConfig{Outbounds: transport.Outbounds{Unary: (&tchannel.Transport{}).NewSingleOutbound("localhost:1234")}})) assert.Panics(t, func() { IsGRPCOutbound(&transport.OutboundConfig{Outbounds: transport.Outbounds{Unary: fakeOutbound{}}}) }) } func tempFile(t *testing.T, content string) string { f, err := ioutil.TempFile("", "") require.NoError(t, err) f.Write([]byte(content)) require.NoError(t, err) err = f.Close() require.NoError(t, err) return f.Name() } type fakeOutboundBuilder struct { outbounds yarpc.Outbounds err error } func (b fakeOutboundBuilder) Build(*grpc.Transport, *tchannel.Transport) (*Outbounds, error) { return &Outbounds{Outbounds: b.outbounds}, b.err } type fakePeerChooserFactory struct{} func (f fakePeerChooserFactory) CreatePeerChooser(transport peer.Transport, opts PeerChooserOptions) (PeerChooser, error) { chooser, err := direct.New(direct.Configuration{}, transport) if err != nil { return nil, err } return &defaultPeerChooser{actual: chooser}, nil } ================================================ FILE: common/rpc/params.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "crypto/tls" "errors" "fmt" "net" "regexp" "strconv" "go.uber.org/yarpc" yarpctls "go.uber.org/yarpc/api/transport/tls" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/service" ) // Params allows to configure rpc.Factory type Params struct { ServiceName string TChannelAddress string GRPCAddress string GRPCMaxMsgSize int HTTP *httpParams InboundTLS *tls.Config OutboundTLS map[string]*tls.Config InboundMiddleware yarpc.InboundMiddleware OutboundMiddleware yarpc.OutboundMiddleware OutboundsBuilder OutboundsBuilder } type httpParams struct { Address string Procedures map[string]struct{} TLS *tls.Config Mode yarpctls.Mode } // NewParams creates parameters for rpc.Factory from the given config func NewParams(serviceName string, config *config.Config, dc *dynamicconfig.Collection, logger log.Logger, metricsCl metrics.Client) (Params, error) { serviceConfig, err := config.GetServiceConfig(serviceName) if err != nil { return Params{}, err } listenIP, err := GetListenIP(serviceConfig.RPC) if err != nil { return Params{}, fmt.Errorf("get listen IP: %v", err) } inboundTLS, err := serviceConfig.RPC.TLS.ToTLSConfig() if err != nil { return Params{}, fmt.Errorf("inbound TLS config: %v", err) } outboundTLS := map[string]*tls.Config{} for _, outboundServiceName := range service.List { outboundServiceConfig, err := config.GetServiceConfig(outboundServiceName) if err != nil { continue } outboundTLS[outboundServiceName], err = outboundServiceConfig.RPC.TLS.ToTLSConfig() if err != nil { return Params{}, fmt.Errorf("outbound %s TLS config: %v", outboundServiceName, err) } } enableGRPCOutbound := dc.GetBoolProperty(dynamicproperties.EnableGRPCOutbound)() publicClientOutbound, err := newPublicClientOutbound(config) if err != nil { return Params{}, fmt.Errorf("public client outbound: %v", err) } forwardingRules, err := getForwardingRules(dc) if err != nil { return Params{}, err } if len(forwardingRules) == 0 { // not set, load from static config forwardingRules = config.HeaderForwardingRules } var http *httpParams if serviceConfig.RPC.HTTP != nil { if serviceConfig.RPC.HTTP.Port <= 0 { return Params{}, errors.New("HTTP port is not set") } procedureMap := map[string]struct{}{} for _, v := range serviceConfig.RPC.HTTP.Procedures { procedureMap[v] = struct{}{} } http = &httpParams{ Address: net.JoinHostPort(listenIP.String(), strconv.Itoa(int(serviceConfig.RPC.HTTP.Port))), Procedures: procedureMap, } if serviceConfig.RPC.HTTP.TLS.Enabled { httptls, err := serviceConfig.RPC.HTTP.TLS.ToTLSConfig() if err != nil { return Params{}, fmt.Errorf("creating TLS config for HTTP: %w", err) } http.TLS = httptls http.Mode = serviceConfig.RPC.HTTP.TLSMode } } outboundsBuilders := []OutboundsBuilder{ NewDirectOutboundBuilder( service.History, enableGRPCOutbound, outboundTLS[service.History], NewDirectPeerChooserFactory(service.History, logger, metricsCl), dc.GetBoolProperty(dynamicproperties.EnableConnectionRetainingDirectChooser), ), NewDirectOutboundBuilder( service.Matching, enableGRPCOutbound, outboundTLS[service.Matching], NewDirectPeerChooserFactory(service.Matching, logger, metricsCl), dc.GetBoolProperty(dynamicproperties.EnableConnectionRetainingDirectChooser), ), publicClientOutbound, } if config.ShardDistributorClient.HostPort != "" { outboundsBuilders = append(outboundsBuilders, NewSingleGRPCOutboundBuilder( service.ShardDistributor, service.ShardDistributor, config.ShardDistributorClient.HostPort, )) } return Params{ ServiceName: serviceName, HTTP: http, TChannelAddress: net.JoinHostPort(listenIP.String(), strconv.Itoa(int(serviceConfig.RPC.Port))), GRPCAddress: net.JoinHostPort(listenIP.String(), strconv.Itoa(int(serviceConfig.RPC.GRPCPort))), GRPCMaxMsgSize: serviceConfig.RPC.GRPCMaxMsgSize, OutboundsBuilder: CombineOutbounds(outboundsBuilders...), InboundTLS: inboundTLS, OutboundTLS: outboundTLS, InboundMiddleware: yarpc.InboundMiddleware{ // order matters: ForwardPartitionConfigMiddleware must be applied after ClientPartitionConfigMiddleware Unary: yarpc.UnaryInboundMiddleware(&InboundMetricsMiddleware{}, &CallerInfoMiddleware{}, &ClientPartitionConfigMiddleware{}, &ForwardPartitionConfigMiddleware{}), }, OutboundMiddleware: yarpc.OutboundMiddleware{ Unary: yarpc.UnaryOutboundMiddleware(&HeaderForwardingMiddleware{ Rules: forwardingRules, }, &ForwardPartitionConfigMiddleware{}), }, }, nil } func getForwardingRules(dc *dynamicconfig.Collection) ([]config.HeaderRule, error) { var forwardingRules []config.HeaderRule dynForwarding := dc.GetListProperty(dynamicproperties.HeaderForwardingRules)() if len(dynForwarding) > 0 { for _, f := range dynForwarding { switch v := f.(type) { case config.HeaderRule: // default or correctly typed value forwardingRules = append(forwardingRules, v) case map[string]interface{}: // loaded from generic deserialization, compatible with encoding/json add, aok := v["Add"].(bool) m, mok := v["Match"].(string) if !aok || !mok { return nil, fmt.Errorf("invalid generic types for header forwarding rule: %#v", v) } r, err := regexp.Compile(m) if err != nil { return nil, fmt.Errorf("invalid match regex in header forwarding rule: %q, err: %w", m, err) } forwardingRules = append(forwardingRules, config.HeaderRule{ Add: add, Match: r, }) default: // unknown return nil, fmt.Errorf("unrecognized dynamic header forwarding type: %T", v) } } } return forwardingRules, nil } // GetListenIP returns the IP address to bind/listen on based on RPC config // It respects bindOnLocalHost, bindOnIP, or falls back to auto-detection func GetListenIP(config config.RPC) (net.IP, error) { if config.BindOnLocalHost && len(config.BindOnIP) > 0 { return nil, fmt.Errorf("bindOnLocalHost and bindOnIP are mutually exclusive") } if config.BindOnLocalHost { return net.IPv4(127, 0, 0, 1), nil } if len(config.BindOnIP) > 0 { ip := net.ParseIP(config.BindOnIP) if ip != nil && ip.To4() != nil { return ip.To4(), nil } if ip != nil && ip.To16() != nil { return ip.To16(), nil } return nil, fmt.Errorf("unable to parse bindOnIP value or it is not an IPv4 or IPv6 address: %s", config.BindOnIP) } return ListenIP() } ================================================ FILE: common/rpc/params_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "net" "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/service" ) func TestNewParams(t *testing.T) { serviceName := service.Frontend dc := dynamicconfig.NewNopCollection() makeConfig := func(svc config.Service) *config.Config { return &config.Config{ PublicClient: config.PublicClient{HostPort: "localhost:9999"}, ShardDistributorClient: config.ShardDistributorClient{HostPort: "localhost:9998"}, Services: map[string]config.Service{"frontend": svc}} } logger := testlogger.New(t) metricsCl := metrics.NewNoopMetricsClient() _, err := NewParams(serviceName, &config.Config{}, dc, logger, metricsCl) assert.EqualError(t, err, "no config section for service: frontend") _, err = NewParams(serviceName, makeConfig(config.Service{RPC: config.RPC{BindOnLocalHost: true, BindOnIP: "1.2.3.4"}}), dc, logger, metricsCl) assert.EqualError(t, err, "get listen IP: bindOnLocalHost and bindOnIP are mutually exclusive") _, err = NewParams(serviceName, makeConfig(config.Service{RPC: config.RPC{BindOnIP: "invalidIP"}}), dc, logger, metricsCl) assert.EqualError(t, err, "get listen IP: unable to parse bindOnIP value or it is not an IPv4 or IPv6 address: invalidIP") _, err = NewParams(serviceName, &config.Config{Services: map[string]config.Service{"frontend": {}}}, dc, logger, metricsCl) assert.EqualError(t, err, "public client outbound: need to provide an endpoint config for PublicClient") cfg := makeConfig(config.Service{RPC: config.RPC{BindOnLocalHost: true, TLS: config.TLS{Enabled: true, CertFile: "invalid", KeyFile: "invalid"}}}) _, err = NewParams(serviceName, cfg, dc, logger, metricsCl) assert.EqualError(t, err, "inbound TLS config: open invalid: no such file or directory") cfg = &config.Config{Services: map[string]config.Service{ "frontend": {RPC: config.RPC{BindOnLocalHost: true}}, "history": {RPC: config.RPC{TLS: config.TLS{Enabled: true, CaFile: "invalid"}}}, }} _, err = NewParams(serviceName, cfg, dc, logger, metricsCl) assert.EqualError(t, err, "outbound cadence-history TLS config: open invalid: no such file or directory") cfg = makeConfig(config.Service{RPC: config.RPC{BindOnLocalHost: true, Port: 1111, GRPCPort: 2222, GRPCMaxMsgSize: 3333}}) params, err := NewParams(serviceName, cfg, dc, logger, metricsCl) assert.NoError(t, err) assert.Equal(t, "127.0.0.1:1111", params.TChannelAddress) assert.Equal(t, "127.0.0.1:2222", params.GRPCAddress) assert.Equal(t, 3333, params.GRPCMaxMsgSize) assert.Nil(t, params.InboundTLS) cfg = makeConfig(config.Service{RPC: config.RPC{BindOnLocalHost: true, HTTP: &config.HTTP{Port: 8800}}}) params, err = NewParams(serviceName, cfg, dc, logger, metricsCl) assert.NoError(t, err) assert.Equal(t, "127.0.0.1:8800", params.HTTP.Address) cfg = makeConfig(config.Service{RPC: config.RPC{BindOnLocalHost: true, HTTP: &config.HTTP{}}}) params, err = NewParams(serviceName, cfg, dc, logger, metricsCl) assert.Error(t, err) cfg = makeConfig(config.Service{RPC: config.RPC{BindOnIP: "1.2.3.4", GRPCPort: 2222}}) params, err = NewParams(serviceName, cfg, dc, logger, metricsCl) assert.NoError(t, err) assert.Equal(t, "1.2.3.4:2222", params.GRPCAddress) cfg = makeConfig(config.Service{RPC: config.RPC{GRPCPort: 2222, TLS: config.TLS{Enabled: true}}}) params, err = NewParams(serviceName, cfg, dc, logger, metricsCl) assert.NoError(t, err) ip, port, err := net.SplitHostPort(params.GRPCAddress) assert.NoError(t, err) assert.Equal(t, "2222", port) assert.NotNil(t, net.ParseIP(ip)) assert.NotNil(t, params.InboundTLS) } ================================================ FILE: common/rpc/peer_chooser.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination peer_chooser_mock.go -self_package github.com/uber/cadence/common/rpc package rpc import ( "context" "time" "go.uber.org/yarpc/api/peer" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/peer/roundrobin" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" ) const defaultDNSRefreshInterval = time.Second * 10 type ( PeerChooserOptions struct { // Address is the target dns address. Used by dns peer chooser. Address string // ServiceName is the name of service. Used by direct peer chooser. ServiceName string // EnableConnectionRetainingDirectChooser is used by direct peer chooser. // If false, yarpc's own default direct peer chooser will be used which doesn't retain connections. // If true, cadence's own direct peer chooser will be used which retains connections. EnableConnectionRetainingDirectChooser dynamicproperties.BoolPropertyFn } PeerChooserFactory interface { CreatePeerChooser(transport peer.Transport, opts PeerChooserOptions) (PeerChooser, error) } PeerChooser interface { peer.Chooser // UpdatePeers updates the list of peers if needed. UpdatePeers(serviceName string, members []membership.HostInfo) } dnsPeerChooserFactory struct { interval time.Duration logger log.Logger } directPeerChooserFactory struct { serviceName string logger log.Logger metricsCl metrics.Client choosers []*directPeerChooser } ) type defaultPeerChooser struct { actual peer.Chooser onStop func() } // UpdatePeers is a no-op for defaultPeerChooser. It is added to satisfy the PeerChooser interface. func (d *defaultPeerChooser) UpdatePeers(string, []membership.HostInfo) {} // Choose a Peer for the next call, block until a peer is available (or timeout) func (d *defaultPeerChooser) Choose(ctx context.Context, req *transport.Request) (peer peer.Peer, onFinish func(error), err error) { return d.actual.Choose(ctx, req) } func (d *defaultPeerChooser) Start() error { return d.actual.Start() } func (d *defaultPeerChooser) Stop() error { if d.onStop != nil { d.onStop() } return d.actual.Stop() } func (d *defaultPeerChooser) IsRunning() bool { return d.actual.IsRunning() } func NewDNSPeerChooserFactory(interval time.Duration, logger log.Logger) PeerChooserFactory { if interval <= 0 { interval = defaultDNSRefreshInterval } return &dnsPeerChooserFactory{ interval: interval, logger: logger, } } func (f *dnsPeerChooserFactory) CreatePeerChooser(transport peer.Transport, opts PeerChooserOptions) (PeerChooser, error) { peerList := roundrobin.New(transport) peerListUpdater, err := newDNSUpdater(peerList, opts.Address, f.interval, f.logger) if err != nil { return nil, err } peerListUpdater.Start() return &defaultPeerChooser{ actual: peerList, onStop: peerListUpdater.Stop, }, nil } func NewDirectPeerChooserFactory(serviceName string, logger log.Logger, metricsCl metrics.Client) PeerChooserFactory { return &directPeerChooserFactory{ serviceName: serviceName, logger: logger, metricsCl: metricsCl, } } func (f *directPeerChooserFactory) CreatePeerChooser(transport peer.Transport, opts PeerChooserOptions) (PeerChooser, error) { c := newDirectChooser(f.serviceName, transport, f.logger, f.metricsCl, opts.EnableConnectionRetainingDirectChooser) f.choosers = append(f.choosers, c) return c, nil } ================================================ FILE: common/rpc/peer_chooser_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: peer_chooser.go // // Generated by this command: // // mockgen -package rpc -source peer_chooser.go -destination peer_chooser_mock.go -self_package github.com/uber/cadence/common/rpc // // Package rpc is a generated GoMock package. package rpc import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" peer "go.uber.org/yarpc/api/peer" transport "go.uber.org/yarpc/api/transport" membership "github.com/uber/cadence/common/membership" ) // MockPeerChooserFactory is a mock of PeerChooserFactory interface. type MockPeerChooserFactory struct { ctrl *gomock.Controller recorder *MockPeerChooserFactoryMockRecorder isgomock struct{} } // MockPeerChooserFactoryMockRecorder is the mock recorder for MockPeerChooserFactory. type MockPeerChooserFactoryMockRecorder struct { mock *MockPeerChooserFactory } // NewMockPeerChooserFactory creates a new mock instance. func NewMockPeerChooserFactory(ctrl *gomock.Controller) *MockPeerChooserFactory { mock := &MockPeerChooserFactory{ctrl: ctrl} mock.recorder = &MockPeerChooserFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPeerChooserFactory) EXPECT() *MockPeerChooserFactoryMockRecorder { return m.recorder } // CreatePeerChooser mocks base method. func (m *MockPeerChooserFactory) CreatePeerChooser(transport peer.Transport, opts PeerChooserOptions) (PeerChooser, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreatePeerChooser", transport, opts) ret0, _ := ret[0].(PeerChooser) ret1, _ := ret[1].(error) return ret0, ret1 } // CreatePeerChooser indicates an expected call of CreatePeerChooser. func (mr *MockPeerChooserFactoryMockRecorder) CreatePeerChooser(transport, opts any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePeerChooser", reflect.TypeOf((*MockPeerChooserFactory)(nil).CreatePeerChooser), transport, opts) } // MockPeerChooser is a mock of PeerChooser interface. type MockPeerChooser struct { ctrl *gomock.Controller recorder *MockPeerChooserMockRecorder isgomock struct{} } // MockPeerChooserMockRecorder is the mock recorder for MockPeerChooser. type MockPeerChooserMockRecorder struct { mock *MockPeerChooser } // NewMockPeerChooser creates a new mock instance. func NewMockPeerChooser(ctrl *gomock.Controller) *MockPeerChooser { mock := &MockPeerChooser{ctrl: ctrl} mock.recorder = &MockPeerChooserMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPeerChooser) EXPECT() *MockPeerChooserMockRecorder { return m.recorder } // Choose mocks base method. func (m *MockPeerChooser) Choose(arg0 context.Context, arg1 *transport.Request) (peer.Peer, func(error), error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Choose", arg0, arg1) ret0, _ := ret[0].(peer.Peer) ret1, _ := ret[1].(func(error)) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // Choose indicates an expected call of Choose. func (mr *MockPeerChooserMockRecorder) Choose(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Choose", reflect.TypeOf((*MockPeerChooser)(nil).Choose), arg0, arg1) } // IsRunning mocks base method. func (m *MockPeerChooser) IsRunning() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsRunning") ret0, _ := ret[0].(bool) return ret0 } // IsRunning indicates an expected call of IsRunning. func (mr *MockPeerChooserMockRecorder) IsRunning() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsRunning", reflect.TypeOf((*MockPeerChooser)(nil).IsRunning)) } // Start mocks base method. func (m *MockPeerChooser) Start() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start") ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockPeerChooserMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockPeerChooser)(nil).Start)) } // Stop mocks base method. func (m *MockPeerChooser) Stop() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Stop") ret0, _ := ret[0].(error) return ret0 } // Stop indicates an expected call of Stop. func (mr *MockPeerChooserMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockPeerChooser)(nil).Stop)) } // UpdatePeers mocks base method. func (m *MockPeerChooser) UpdatePeers(serviceName string, members []membership.HostInfo) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdatePeers", serviceName, members) } // UpdatePeers indicates an expected call of UpdatePeers. func (mr *MockPeerChooserMockRecorder) UpdatePeers(serviceName, members any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePeers", reflect.TypeOf((*MockPeerChooser)(nil).UpdatePeers), serviceName, members) } ================================================ FILE: common/rpc/peer_chooser_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package rpc import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/yarpc/api/peer" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/transport/grpc" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) type ( fakePeerTransport struct{} fakePeer struct{} ) func (t *fakePeerTransport) RetainPeer(peer.Identifier, peer.Subscriber) (peer.Peer, error) { return &fakePeer{}, nil } func (t *fakePeerTransport) ReleasePeer(peer.Identifier, peer.Subscriber) error { return nil } func (p *fakePeer) Identifier() string { return "fakePeer" } func (p *fakePeer) Status() peer.Status { return peer.Status{ConnectionStatus: peer.Available} } func (p *fakePeer) StartRequest() {} func (p *fakePeer) EndRequest() {} func TestDNSPeerChooserFactory(t *testing.T) { defer goleak.VerifyNone(t) logger := log.NewNoop() ctx := context.Background() interval := 10 * time.Millisecond factory := NewDNSPeerChooserFactory(interval, logger) peerTransport := &fakePeerTransport{} // Ensure invalid address returns error _, err := factory.CreatePeerChooser(peerTransport, PeerChooserOptions{Address: "invalid address"}) assert.EqualError(t, err, "incorrect DNS:Port format") chooser, err := factory.CreatePeerChooser(peerTransport, PeerChooserOptions{Address: "localhost:1234"}) require.NoError(t, err) require.NoError(t, chooser.Start()) defer chooser.Stop() require.True(t, chooser.IsRunning()) // Wait for refresh time.Sleep(interval + 50*time.Millisecond) peer, _, err := chooser.Choose(ctx, &transport.Request{}) require.NoError(t, err) require.NotNil(t, peer) assert.Equal(t, "fakePeer", peer.Identifier()) } func TestDirectPeerChooserFactory(t *testing.T) { logger := testlogger.New(t) metricCl := metrics.NewNoopMetricsClient() serviceName := "service" pcf := NewDirectPeerChooserFactory(serviceName, logger, metricCl) directConnRetainFn := func(opts ...dynamicproperties.FilterOption) bool { return false } grpcTransport := grpc.NewTransport() chooser, err := pcf.CreatePeerChooser(grpcTransport, PeerChooserOptions{ ServiceName: serviceName, EnableConnectionRetainingDirectChooser: directConnRetainFn, }) if err != nil { t.Fatalf("Failed to create direct peer chooser: %v", err) } if chooser == nil { t.Fatal("Failed to create direct peer chooser: nil") } if _, dc := chooser.(*directPeerChooser); !dc { t.Fatalf("Want chooser be of type (*directPeerChooser), got %d", chooser) } } ================================================ FILE: common/rpc/rpcfx/rpcfx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package rpcfx import ( "fmt" "go.uber.org/fx" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/rpc" ) // Module provides rpc.Params and rpc.Factory for fx application. var Module = fx.Module("rpcfx", fx.Provide(paramsBuilder), fx.Provide(buildFactory), ) type paramsBuilderParams struct { fx.In ServiceFullName string `name:"service-full-name"` Cfg config.Config Logger log.Logger DynamicCollection *dynamicconfig.Collection MetricsClient metrics.Client } func paramsBuilder(p paramsBuilderParams) (rpc.Params, error) { res, err := rpc.NewParams(p.ServiceFullName, &p.Cfg, p.DynamicCollection, p.Logger, p.MetricsClient) if err != nil { return rpc.Params{}, fmt.Errorf("create rpc params: %w", err) } return res, nil } type factoryParams struct { fx.In Logger log.Logger RPCParams rpc.Params Lifecycle fx.Lifecycle } func buildFactory(p factoryParams) rpc.Factory { res := rpc.NewFactory(p.Logger, p.RPCParams) p.Lifecycle.Append(fx.StartStopHook(startDispatcher(res), rpcStopper(res))) return res } func startDispatcher(f rpc.Factory) func() error { return func() error { return f.GetDispatcher().Start() } } func rpcStopper(factory rpc.Factory) func() error { return func() error { err := factory.GetDispatcher().Stop() if err != nil { return fmt.Errorf("dispatcher stop: %w", err) } return nil } } ================================================ FILE: common/rpc/types.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination factory_mock.go -self_package github.com/uber/cadence/common/rpc package rpc import ( "go.uber.org/yarpc" "go.uber.org/yarpc/transport/tchannel" "github.com/uber/cadence/common/membership" ) // Factory Creates a dispatcher that knows how to transport requests. type Factory interface { GetDispatcher() *yarpc.Dispatcher GetMaxMessageSize() int Start(PeerLister) error GetTChannel() tchannel.Channel Stop() error } type PeerLister interface { Subscribe(service, name string, notifyChannel chan<- *membership.ChangedEvent) error Unsubscribe(service, name string) error Members(service string) ([]membership.HostInfo, error) } ================================================ FILE: common/rsa.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package common import ( "crypto/rsa" "crypto/x509" "encoding/pem" "fmt" "os" "strings" ) type KeyType string const ( KeyTypePrivate KeyType = "private key" KeyTypePublic KeyType = "public key" ) func loadRSAKey(path string, keyType KeyType) (interface{}, error) { keyString, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("invalid %s path %s", keyType, path) } block, _ := pem.Decode(keyString) if block == nil || strings.ToLower(block.Type) != strings.ToLower(string(keyType)) { return nil, fmt.Errorf("failed to parse PEM block containing the %s", keyType) } switch keyType { case KeyTypePrivate: key, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return nil, fmt.Errorf("failed to parse DER encoded %s: %s", keyType, err.Error()) } return key, nil case KeyTypePublic: key, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, fmt.Errorf("failed to parse DER encoded %s: %s", keyType, err.Error()) } return key, nil default: return nil, fmt.Errorf("invalid Key Type") } } func LoadRSAPublicKey(path string) (*rsa.PublicKey, error) { key, err := loadRSAKey(path, KeyTypePublic) if err != nil { return nil, err } return key.(*rsa.PublicKey), err } func LoadRSAPrivateKey(path string) (*rsa.PrivateKey, error) { key, err := loadRSAKey(path, KeyTypePrivate) if err != nil { return nil, err } return key.(*rsa.PrivateKey), err } ================================================ FILE: common/scripting/exec.go ================================================ package scripting import ( "bytes" "context" "errors" "io" "os" "os/exec" ) // Executor is a helper for running scripting type Executor interface { BashExec(ctx context.Context, in string) (stdout string, stderr string, exitErr *exec.ExitError) Exec(ctx context.Context, bin string, args ...string) (stdout string, stderr string, exitErr *exec.ExitError) QuietBashExec(ctx context.Context, in string) (stdout string, stderr string, exitErr *exec.ExitError) QuietExec(ctx context.Context, bin string, args ...string) (stdout string, stderr string, exitErr *exec.ExitError) } // New returns a new bash executor func New() Executor { return &execImpl{} } type execImpl struct{} // BashExec is a helper function for ensuring the user // can read a legible streaming from a subprocess, // as well as allowing programmatic access to stdout, stderr and exit codes // it's highly unsafe for any kind of untrusted inputs as it's explicitly bypassing // go's exec safety args, so it *must not* come into contact with anything untrusted func (execImpl) BashExec(ctx context.Context, in string) (stdout string, stderr string, exitErr *exec.ExitError) { cmd := exec.CommandContext(ctx, "bash", "-c", in) var stdBuffer bytes.Buffer var stdErrBuffer bytes.Buffer mw := io.MultiWriter(os.Stdout, &stdBuffer) mwErr := io.MultiWriter(os.Stderr, &stdErrBuffer) cmd.Stdout = mw cmd.Stderr = mwErr err := cmd.Run() var e *exec.ExitError if errors.As(err, &e) && e.ExitCode() != 0 { return stdBuffer.String(), stdErrBuffer.String(), e } else if err != nil { panic(err) } return stdBuffer.String(), stdErrBuffer.String(), nil } // Exec is a wrapper around exec.Command which adds some convenience // functionality to both capture standout/err as well as tee it to the user's UI in real time // meaning that the user doesn't need to wait for the command to complete. // It's value is fairly marginal and if it presents any problems the user should consider just // using exec.Command directly func (execImpl) Exec(ctx context.Context, bin string, args ...string) (stdout string, stderr string, exitErr *exec.ExitError) { cmd := exec.CommandContext(ctx, bin, args...) var stdBuffer bytes.Buffer var stdErrBuffer bytes.Buffer mw := io.MultiWriter(os.Stdout, &stdBuffer) mwErr := io.MultiWriter(os.Stderr, &stdErrBuffer) cmd.Stdout = mw cmd.Stderr = mwErr err := cmd.Run() var e *exec.ExitError if errors.As(err, &e) && e.ExitCode() != 0 { return stdBuffer.String(), stdErrBuffer.String(), e } else if err != nil { panic(err) } return stdBuffer.String(), stdErrBuffer.String(), nil } // QuietBashExec ... func (execImpl) QuietBashExec(ctx context.Context, in string) (stdout string, stderr string, exitErr *exec.ExitError) { cmd := exec.CommandContext(ctx, "bash", "-c", in) var stdBuffer bytes.Buffer var stdErrBuffer bytes.Buffer cmd.Stdout = &stdBuffer cmd.Stderr = &stdErrBuffer err := cmd.Run() var e *exec.ExitError if errors.As(err, &e) && e.ExitCode() != 0 { return stdBuffer.String(), stdErrBuffer.String(), e } else if err != nil { panic(err) } return stdBuffer.String(), stdErrBuffer.String(), nil } // QuietExec ... func (execImpl) QuietExec(ctx context.Context, bin string, args ...string) (stdout string, stderr string, exitErr *exec.ExitError) { cmd := exec.CommandContext(ctx, bin, args...) var stdBuffer bytes.Buffer var stdErrBuffer bytes.Buffer cmd.Stdout = &stdBuffer cmd.Stderr = &stdErrBuffer err := cmd.Run() var e *exec.ExitError if errors.As(err, &e) && e.ExitCode() != 0 { return stdBuffer.String(), stdErrBuffer.String(), e } else if err != nil { panic(err) } return stdBuffer.String(), stdErrBuffer.String(), nil } ================================================ FILE: common/scripting/exec_test.go ================================================ package scripting import ( "context" "testing" "github.com/stretchr/testify/assert" ) func TestBashExec(t *testing.T) { script := New() stdout, stderr, err := script.BashExec(context.Background(), "echo test") assert.Equal(t, "test\n", stdout) assert.Equal(t, "", stderr) assert.Nil(t, err) stdout, stderr, err = script.BashExec(context.Background(), "echo test 1>&2") assert.Equal(t, "test\n", stderr) assert.Equal(t, "", stdout) assert.Nil(t, err) stdout, stderr, err = script.BashExec(context.Background(), "false") assert.Equal(t, "", stderr) assert.Equal(t, "", stdout) assert.Error(t, err) tests := map[string]struct { args string expectedStdout string expectedSterr string expectedErr bool }{ "stdout": { args: "echo test", expectedStdout: "test\n", expectedSterr: "", expectedErr: false, }, "stderr": { args: "echo test 1>&2", expectedStdout: "", expectedSterr: "test\n", expectedErr: false, }, "error": { args: "false", expectedStdout: "", expectedSterr: "", expectedErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { stdout, stderr, err := script.BashExec(context.Background(), td.args) assert.Equal(t, td.expectedStdout, stdout) assert.Equal(t, td.expectedSterr, stderr) if td.expectedErr { assert.Error(t, err) } stdout, stderr, err = script.QuietBashExec(context.Background(), td.args) assert.Equal(t, td.expectedStdout, stdout) assert.Equal(t, td.expectedSterr, stderr) if td.expectedErr { assert.Error(t, err) } }) } } func TestExec(t *testing.T) { script := New() tests := map[string]struct { bin string args []string expectedStdout string expectedSterr string expectedErr bool }{ "stdout": { bin: "echo", args: []string{"test"}, expectedStdout: "test\n", expectedSterr: "", expectedErr: false, }, "error": { bin: "false", args: []string{""}, expectedStdout: "", expectedSterr: "", expectedErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { stdout, stderr, err := script.Exec(context.Background(), td.bin, td.args...) assert.Equal(t, td.expectedStdout, stdout) assert.Equal(t, td.expectedSterr, stderr) if td.expectedErr { assert.Error(t, err) } stdout, stderr, err = script.QuietExec(context.Background(), td.bin, td.args...) assert.Equal(t, td.expectedStdout, stdout) assert.Equal(t, td.expectedSterr, stderr) if td.expectedErr { assert.Error(t, err) } }) } } ================================================ FILE: common/service/config.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package service import ( "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) type ( // Config is a subset of the service dynamic config for single service Config struct { PersistenceMaxQPS dynamicproperties.IntPropertyFn PersistenceGlobalMaxQPS dynamicproperties.IntPropertyFn ThrottledLoggerMaxRPS dynamicproperties.IntPropertyFn // WriteVisibilityStoreName is the write mode of visibility WriteVisibilityStoreName dynamicproperties.StringPropertyFn // EnableLogCustomerQueryParameter is to enable log customer parameters EnableLogCustomerQueryParameter dynamicproperties.BoolPropertyFnWithDomainFilter // ReadVisibilityStoreName is the read store for visibility ReadVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter // configs for db visibility EnableDBVisibilitySampling dynamicproperties.BoolPropertyFn `yaml:"-" json:"-"` EnableReadDBVisibilityFromClosedExecutionV2 dynamicproperties.BoolPropertyFn `yaml:"-" json:"-"` WriteDBVisibilityOpenMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter `yaml:"-" json:"-"` WriteDBVisibilityClosedMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter `yaml:"-" json:"-"` DBVisibilityListMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter `yaml:"-" json:"-"` // configs for es visibility ESIndexMaxResultWindow dynamicproperties.IntPropertyFn `yaml:"-" json:"-"` ValidSearchAttributes dynamicproperties.MapPropertyFn `yaml:"-" json:"-"` PinotOptimizedQueryColumns dynamicproperties.MapPropertyFn `yaml:"-" json:"-"` SearchAttributesHiddenValueKeys dynamicproperties.MapPropertyFn `yaml:"-" json:"-"` // deprecated: never read from, all ES reads and writes erroneously use PersistenceMaxQPS ESVisibilityListMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter `yaml:"-" json:"-"` IsErrorRetryableFunction backoff.IsRetryable } ) ================================================ FILE: common/service/metrics.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package service import ( "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" ) // GetMetricsServiceIdx returns the metrics name func GetMetricsServiceIdx(serviceName string, logger log.Logger) metrics.ServiceIdx { switch serviceName { case Frontend: return metrics.Frontend case History: return metrics.History case Matching: return metrics.Matching case Worker: return metrics.Worker case ShardDistributor: return metrics.ShardDistributor default: logger.Fatal("Unknown service name for metrics!") } // this should never happen! return metrics.NumServices } ================================================ FILE: common/service/name.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package service import "strings" const ( _servicePrefix = "cadence-" // Frontend is the name of the frontend service Frontend = "cadence-frontend" // History is the name of the history service History = "cadence-history" // Matching is the name of the matching service Matching = "cadence-matching" // Worker is the name of the worker service Worker = "cadence-worker" // ShardDistributor is the name of the shard distributor service ShardDistributor = "cadence-shard-distributor" ) // ListWithRing contains the list of all cadence services that has a hash ring var ListWithRing = []string{Frontend, History, Matching, Worker} // List contains the list of all cadence services var List = []string{Frontend, History, Matching, Worker, ShardDistributor} // ShortName returns cadence service name without "cadence-" prefix func ShortName(name string) string { return strings.TrimPrefix(name, _servicePrefix) } // FullName returns cadence service name with "cadence-" prefix func FullName(name string) string { if strings.HasPrefix(name, _servicePrefix) { return name } return _servicePrefix + name } func ShortNames(names []string) []string { result := make([]string, len(names)) for i := range names { result[i] = ShortName(names[i]) } return result } ================================================ FILE: common/service/name_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package service import ( "testing" "github.com/stretchr/testify/assert" ) func TestServiceNames(t *testing.T) { shortName := "frontend" fullName := "cadence-frontend" assert.Equal(t, shortName, ShortName(shortName)) assert.Equal(t, shortName, ShortName(fullName)) assert.Equal(t, fullName, FullName(shortName)) assert.Equal(t, fullName, FullName(fullName)) assert.Equal(t, []string{"cadence-frontend", "cadence-history", "cadence-matching", "cadence-worker", "cadence-shard-distributor"}, List) assert.Equal(t, []string{"frontend", "history", "matching", "worker", "shard-distributor"}, ShortNames(List)) } ================================================ FILE: common/stats/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go // // Generated by this command: // // mockgen -package stats -source interfaces.go -destination interface_mock.go -self_package github.com/uber/cadence/common/stats QPSTracker // // Package stats is a generated GoMock package. package stats import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockQPSTracker is a mock of QPSTracker interface. type MockQPSTracker struct { ctrl *gomock.Controller recorder *MockQPSTrackerMockRecorder isgomock struct{} } // MockQPSTrackerMockRecorder is the mock recorder for MockQPSTracker. type MockQPSTrackerMockRecorder struct { mock *MockQPSTracker } // NewMockQPSTracker creates a new mock instance. func NewMockQPSTracker(ctrl *gomock.Controller) *MockQPSTracker { mock := &MockQPSTracker{ctrl: ctrl} mock.recorder = &MockQPSTrackerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQPSTracker) EXPECT() *MockQPSTrackerMockRecorder { return m.recorder } // QPS mocks base method. func (m *MockQPSTracker) QPS() float64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QPS") ret0, _ := ret[0].(float64) return ret0 } // QPS indicates an expected call of QPS. func (mr *MockQPSTrackerMockRecorder) QPS() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QPS", reflect.TypeOf((*MockQPSTracker)(nil).QPS)) } // ReportCounter mocks base method. func (m *MockQPSTracker) ReportCounter(arg0 int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReportCounter", arg0) } // ReportCounter indicates an expected call of ReportCounter. func (mr *MockQPSTrackerMockRecorder) ReportCounter(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportCounter", reflect.TypeOf((*MockQPSTracker)(nil).ReportCounter), arg0) } // Start mocks base method. func (m *MockQPSTracker) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockQPSTrackerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockQPSTracker)(nil).Start)) } // Stop mocks base method. func (m *MockQPSTracker) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockQPSTrackerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockQPSTracker)(nil).Stop)) } // MockQPSTrackerGroup is a mock of QPSTrackerGroup interface. type MockQPSTrackerGroup struct { ctrl *gomock.Controller recorder *MockQPSTrackerGroupMockRecorder isgomock struct{} } // MockQPSTrackerGroupMockRecorder is the mock recorder for MockQPSTrackerGroup. type MockQPSTrackerGroupMockRecorder struct { mock *MockQPSTrackerGroup } // NewMockQPSTrackerGroup creates a new mock instance. func NewMockQPSTrackerGroup(ctrl *gomock.Controller) *MockQPSTrackerGroup { mock := &MockQPSTrackerGroup{ctrl: ctrl} mock.recorder = &MockQPSTrackerGroupMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQPSTrackerGroup) EXPECT() *MockQPSTrackerGroupMockRecorder { return m.recorder } // GroupQPS mocks base method. func (m *MockQPSTrackerGroup) GroupQPS(group string) float64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GroupQPS", group) ret0, _ := ret[0].(float64) return ret0 } // GroupQPS indicates an expected call of GroupQPS. func (mr *MockQPSTrackerGroupMockRecorder) GroupQPS(group any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupQPS", reflect.TypeOf((*MockQPSTrackerGroup)(nil).GroupQPS), group) } // QPS mocks base method. func (m *MockQPSTrackerGroup) QPS() float64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QPS") ret0, _ := ret[0].(float64) return ret0 } // QPS indicates an expected call of QPS. func (mr *MockQPSTrackerGroupMockRecorder) QPS() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QPS", reflect.TypeOf((*MockQPSTrackerGroup)(nil).QPS)) } // ReportCounter mocks base method. func (m *MockQPSTrackerGroup) ReportCounter(arg0 int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReportCounter", arg0) } // ReportCounter indicates an expected call of ReportCounter. func (mr *MockQPSTrackerGroupMockRecorder) ReportCounter(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportCounter", reflect.TypeOf((*MockQPSTrackerGroup)(nil).ReportCounter), arg0) } // ReportGroup mocks base method. func (m *MockQPSTrackerGroup) ReportGroup(group string, amount int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReportGroup", group, amount) } // ReportGroup indicates an expected call of ReportGroup. func (mr *MockQPSTrackerGroupMockRecorder) ReportGroup(group, amount any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportGroup", reflect.TypeOf((*MockQPSTrackerGroup)(nil).ReportGroup), group, amount) } // Start mocks base method. func (m *MockQPSTrackerGroup) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockQPSTrackerGroupMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockQPSTrackerGroup)(nil).Start)) } // Stop mocks base method. func (m *MockQPSTrackerGroup) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockQPSTrackerGroupMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockQPSTrackerGroup)(nil).Stop)) } ================================================ FILE: common/stats/interfaces.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/common/stats QPSTracker package stats import ( "github.com/uber/cadence/common" ) // QPSTracker is an interface for reporting statistics related to quotas. type QPSTracker interface { common.Daemon // ReportCounter reports the value of a counter. ReportCounter(int64) // QPS returns the current queries per second (QPS) value. QPS() float64 } // QPSTrackerGroup allows for estimating QPS metrics with an additional dimension type QPSTrackerGroup interface { QPSTracker ReportGroup(group string, amount int64) GroupQPS(group string) float64 } ================================================ FILE: common/stats/stats.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package stats import ( "sync" "time" "go.uber.org/atomic" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/service/matching/event" ) type ( // emaFixedWindowQPSTracker is a QPSTracker that uses a fixed time period to calculate QPS and an exponential moving average algorithm to estimate QPS. emaFixedWindowQPSTracker struct { groups sync.Map root *emaFixedWindowState timeSource clock.TimeSource exp float64 bucketInterval time.Duration wg sync.WaitGroup done chan struct{} status *atomic.Int32 baseEvent event.E } emaFixedWindowState struct { exp float64 bucketIntervalSeconds float64 firstBucket bool baseEvent event.E qps *atomic.Float64 counter *atomic.Int64 } ) func NewEmaFixedWindowQPSTracker(timeSource clock.TimeSource, exp float64, bucketInterval time.Duration, baseEvent event.E) QPSTrackerGroup { return &emaFixedWindowQPSTracker{ root: newEmaFixedWindowState(exp, bucketInterval, baseEvent), timeSource: timeSource, exp: exp, bucketInterval: bucketInterval, done: make(chan struct{}), status: atomic.NewInt32(common.DaemonStatusInitialized), } } func (r *emaFixedWindowQPSTracker) Start() { if !r.status.CompareAndSwap(common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } r.wg.Add(1) go r.reportLoop() } func (r *emaFixedWindowQPSTracker) reportLoop() { defer r.wg.Done() ticker := r.timeSource.NewTicker(r.bucketInterval) defer ticker.Stop() for { select { case <-ticker.Chan(): r.report() case <-r.done: return } } } func (r *emaFixedWindowQPSTracker) report() { r.root.report() r.groups.Range(func(key, value any) bool { state := value.(*emaFixedWindowState) state.report() return true }) } func (r *emaFixedWindowQPSTracker) Stop() { if !r.status.CompareAndSwap(common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(r.done) r.wg.Wait() } func (r *emaFixedWindowQPSTracker) ReportCounter(amount int64) { r.root.add(amount) } func (r *emaFixedWindowQPSTracker) ReportGroup(group string, amount int64) { r.root.add(amount) r.getOrCreate(group).add(amount) } func (r *emaFixedWindowQPSTracker) GroupQPS(group string) float64 { state, ok := r.get(group) if !ok { return 0 } return state.getQPS() } func (r *emaFixedWindowQPSTracker) QPS() float64 { return r.root.getQPS() } func (r *emaFixedWindowQPSTracker) get(group string) (*emaFixedWindowState, bool) { res, ok := r.groups.Load(group) if !ok { return nil, false } return res.(*emaFixedWindowState), true } func (r *emaFixedWindowQPSTracker) getOrCreate(group string) *emaFixedWindowState { res, ok := r.groups.Load(group) if !ok { e := r.baseEvent e.Payload = map[string]any{ "group": group, } res, _ = r.groups.LoadOrStore(group, newEmaFixedWindowState(r.exp, r.bucketInterval, e)) } return res.(*emaFixedWindowState) } func newEmaFixedWindowState(exp float64, bucketInterval time.Duration, baseEvent event.E) *emaFixedWindowState { return &emaFixedWindowState{ exp: exp, bucketIntervalSeconds: bucketInterval.Seconds(), firstBucket: true, baseEvent: baseEvent, qps: atomic.NewFloat64(0), counter: atomic.NewInt64(0), } } func (s *emaFixedWindowState) report() { if s.firstBucket { counter := s.counter.Swap(0) s.store(float64(counter) / s.bucketIntervalSeconds) s.firstBucket = false return } counter := s.counter.Swap(0) qps := s.qps.Load() s.store(qps*(1-s.exp) + float64(counter)*s.exp/s.bucketIntervalSeconds) } func (s *emaFixedWindowState) store(qps float64) { s.qps.Store(qps) e := s.baseEvent e.EventName = "QPSTrackerUpdate" payload := make(map[string]any, len(e.Payload)+1) for k, v := range e.Payload { payload[k] = v } payload["QPS"] = qps e.Payload = payload event.Log(e) } func (s *emaFixedWindowState) add(delta int64) { s.counter.Add(delta) } func (s *emaFixedWindowState) getQPS() float64 { return s.qps.Load() } type ( bucket struct { counter int64 timeIndex int } rollingWindowQPSTracker struct { sync.RWMutex timeSource clock.TimeSource bucketInterval time.Duration buckets []bucket counter int64 timeIndex int } ) func NewRollingWindowQPSTracker(timeSource clock.TimeSource, bucketInterval time.Duration, numBuckets int) QPSTracker { return &rollingWindowQPSTracker{ timeSource: timeSource, bucketInterval: bucketInterval, buckets: make([]bucket, numBuckets), } } func (r *rollingWindowQPSTracker) Start() { } func (r *rollingWindowQPSTracker) Stop() { } func (r *rollingWindowQPSTracker) getCurrentTimeIndex() int { now := r.timeSource.Now() return int(now.UnixNano() / int64(r.bucketInterval)) } func (r *rollingWindowQPSTracker) ReportCounter(delta int64) { r.Lock() defer r.Unlock() currentIndex := r.getCurrentTimeIndex() if currentIndex == r.timeIndex { r.counter += delta return } r.buckets[r.timeIndex%len(r.buckets)] = bucket{ counter: r.counter, timeIndex: r.timeIndex, } r.timeIndex = currentIndex r.counter = delta } func (r *rollingWindowQPSTracker) QPS() float64 { r.RLock() defer r.RUnlock() currentIndex := r.getCurrentTimeIndex() totalCounter := int64(0) for _, b := range r.buckets { if currentIndex-b.timeIndex <= len(r.buckets) { totalCounter += b.counter } } if currentIndex != r.timeIndex && currentIndex-r.timeIndex <= len(r.buckets) { totalCounter += r.counter } return float64(totalCounter) / float64(r.bucketInterval) / float64(len(r.buckets)) * float64(time.Second) } ================================================ FILE: common/stats/stats_benchmark_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package stats import ( "sync" "testing" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/service/matching/event" ) // Benchmark the ReportCounter function to see how it handles frequent updates func BenchmarkReportCounter(b *testing.B) { timeSource := clock.NewRealTimeSource() // Initialize the QPS reporter with a smoothing factor and a 1 second bucket interval reporter := NewEmaFixedWindowQPSTracker(timeSource, 0.5, time.Second, event.E{}) reporter.Start() // Run the benchmark for b.N iterations b.ResetTimer() for i := 0; i < b.N; i++ { reporter.ReportCounter(1) } // Stop the reporter after the benchmark b.StopTimer() reporter.Stop() } // Benchmark the QPS calculation function under high load func BenchmarkQPS(b *testing.B) { timeSource := clock.NewRealTimeSource() // Initialize the QPS reporter reporter := NewEmaFixedWindowQPSTracker(timeSource, 0.5, time.Second, event.E{}) reporter.Start() // Simulate a number of report updates before calling QPS for i := 0; i < 1000; i++ { reporter.ReportCounter(1) } // Benchmark QPS retrieval b.ResetTimer() for i := 0; i < b.N; i++ { _ = reporter.QPS() } // Stop the reporter b.StopTimer() reporter.Stop() } // Benchmark the full reporting loop, simulating a real-time system. func BenchmarkFullReport(b *testing.B) { timeSource := clock.NewRealTimeSource() // Initialize the QPS reporter reporter := NewEmaFixedWindowQPSTracker(timeSource, 0.5, time.Millisecond*100, event.E{}) // 100ms bucket interval reporter.Start() var wg sync.WaitGroup // Number of goroutines for each task numReporters := 10 numQPSQueries := 10 b.ResetTimer() for i := 0; i < numReporters; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < b.N; j++ { // Report random counter value (simulate workload) reporter.ReportCounter(1) } }() } for i := 0; i < numQPSQueries; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < b.N; j++ { // Query QPS value (simulate workload) _ = reporter.QPS() } }() } wg.Wait() // Stop the reporter after the benchmark b.StopTimer() reporter.Stop() } func BenchmarkReportCounterRollingWindow(b *testing.B) { timeSource := clock.NewRealTimeSource() // Initialize the QPS reporter with a smoothing factor and a 1 second bucket interval reporter := NewRollingWindowQPSTracker(timeSource, time.Second, 10) reporter.Start() // Run the benchmark for b.N iterations b.ResetTimer() for i := 0; i < b.N; i++ { reporter.ReportCounter(1) } // Stop the reporter after the benchmark b.StopTimer() reporter.Stop() } func BenchmarkQPSRollingWindow(b *testing.B) { timeSource := clock.NewRealTimeSource() // Initialize the QPS reporter reporter := NewRollingWindowQPSTracker(timeSource, time.Second, 10) reporter.Start() // Simulate a number of report updates before calling QPS for i := 0; i < 1000; i++ { reporter.ReportCounter(1) } // Benchmark QPS retrieval b.ResetTimer() for i := 0; i < b.N; i++ { _ = reporter.QPS() } // Stop the reporter b.StopTimer() reporter.Stop() } // Benchmark the full reporting loop, simulating a real-time system. func BenchmarkFullReportRollingWindow(b *testing.B) { timeSource := clock.NewRealTimeSource() // Initialize the QPS reporter reporter := NewRollingWindowQPSTracker(timeSource, time.Millisecond*100, 10) // 100ms bucket interval reporter.Start() var wg sync.WaitGroup // Number of goroutines for each task numReporters := 10 numQPSQueries := 10 b.ResetTimer() for i := 0; i < numReporters; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < b.N; j++ { // Report random counter value (simulate workload) reporter.ReportCounter(1) } }() } for i := 0; i < numQPSQueries; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < b.N; j++ { // Query QPS value (simulate workload) _ = reporter.QPS() } }() } wg.Wait() // Stop the reporter after the benchmark b.StopTimer() reporter.Stop() } ================================================ FILE: common/stats/stats_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package stats import ( "math" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/service/matching/event" ) const floatResolution = 1e-6 func TestEmaFixedWindowQPSTracker(t *testing.T) { timeSource := clock.NewMockedTimeSourceAt(time.Now()) exp := 0.4 bucketInterval := time.Second r := NewEmaFixedWindowQPSTracker(timeSource, exp, bucketInterval, event.E{}) r.Start() defer r.Stop() // Test ReportCounter r.ReportCounter(10) r.ReportCounter(20) qps := r.QPS() assert.Equal(t, float64(0), qps) timeSource.BlockUntil(1) timeSource.Advance(bucketInterval) time.Sleep(10 * time.Millisecond) // Test QPS qps = r.QPS() expectedQPS := float64(30) / (float64(bucketInterval) / float64(time.Second)) assert.InDelta(t, expectedQPS, qps, floatResolution) r.ReportCounter(10) timeSource.BlockUntil(1) timeSource.Advance(bucketInterval) time.Sleep(10 * time.Millisecond) // Test QPS qps = r.QPS() expectedQPS = float64(22) / (float64(bucketInterval) / float64(time.Second)) assert.InDelta(t, expectedQPS, qps, floatResolution) } func TestEmaFixedWindowQPSTracker_Groups(t *testing.T) { timeSource := clock.NewMockedTimeSourceAt(time.Now()) exp := 0.5 bucketInterval := time.Second r := NewEmaFixedWindowQPSTracker(timeSource, exp, bucketInterval, event.E{}).(*emaFixedWindowQPSTracker) r.ReportGroup("foo", 10) r.ReportGroup("bar", 20) r.report() // Test QPS expectedQPS := float64(30) expectedFoo := float64(10) expectedBar := float64(20) assert.InDelta(t, expectedQPS, r.QPS(), floatResolution) assert.InDelta(t, expectedFoo, r.GroupQPS("foo"), floatResolution) assert.InDelta(t, expectedBar, r.GroupQPS("bar"), floatResolution) assert.Equal(t, float64(0), r.GroupQPS("unknown")) } func TestEmaFixedWindowQPSTracker_MultipleReportCycles(t *testing.T) { timeSource := clock.NewMockedTimeSourceAt(time.Now()) exp := 0.5 bucketInterval := time.Second r := NewEmaFixedWindowQPSTracker(timeSource, exp, bucketInterval, event.E{}).(*emaFixedWindowQPSTracker) r.ReportGroup("foo", 10) r.ReportGroup("bar", 20) r.report() r.ReportGroup("foo", 20) r.ReportGroup("bar", 40) r.report() // Test QPS expectedQPS := float64(45) expectedFoo := float64(15) expectedBar := float64(30) assert.InDelta(t, expectedQPS, r.QPS(), floatResolution) assert.InDelta(t, expectedFoo, r.GroupQPS("foo"), floatResolution) assert.InDelta(t, expectedBar, r.GroupQPS("bar"), floatResolution) assert.Equal(t, float64(0), r.GroupQPS("unknown")) } func TestRollingWindowQPSTracker(t *testing.T) { timeSource := clock.NewMockedTimeSourceAt(time.Now()) bucketInterval := time.Second r := NewRollingWindowQPSTracker(timeSource, bucketInterval, 10) r.Start() defer r.Stop() r.ReportCounter(10) qps := r.QPS() if qps != 0 { t.Errorf("QPS mismatch, expected: 0, got: %v", qps) } timeSource.Advance(bucketInterval) qps = r.QPS() expectedQPS := float64(10) / (float64(bucketInterval) * 10 / float64(time.Second)) if math.Abs(qps-expectedQPS) > floatResolution { t.Errorf("QPS mismatch, expected: %v, got: %v", expectedQPS, qps) } r.ReportCounter(20) qps = r.QPS() if qps != expectedQPS { t.Errorf("QPS mismatch, expected: %v, got: %v", expectedQPS, qps) } timeSource.Advance(bucketInterval) qps = r.QPS() expectedQPS = float64(30) / (float64(bucketInterval) * 10 / float64(time.Second)) if math.Abs(qps-expectedQPS) > floatResolution { t.Errorf("QPS mismatch, expected: %v, got: %v", expectedQPS, qps) } r.ReportCounter(100) timeSource.Advance(8 * bucketInterval) qps = r.QPS() expectedQPS = float64(130) / (float64(bucketInterval) * 10 / float64(time.Second)) if math.Abs(qps-expectedQPS) > floatResolution { t.Errorf("QPS mismatch, expected: %v, got: %v", expectedQPS, qps) } timeSource.Advance(bucketInterval) qps = r.QPS() expectedQPS = float64(120) / (float64(bucketInterval) * 10 / float64(time.Second)) if math.Abs(qps-expectedQPS) > floatResolution { t.Errorf("QPS mismatch, expected: %v, got: %v", expectedQPS, qps) } timeSource.Advance(bucketInterval) qps = r.QPS() expectedQPS = float64(100) / (float64(bucketInterval) * 10 / float64(time.Second)) if math.Abs(qps-expectedQPS) > floatResolution { t.Errorf("QPS mismatch, expected: %v, got: %v", expectedQPS, qps) } } ================================================ FILE: common/syncmap/syncmap.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package syncmap import "sync" // syncmap is a very simple type-safe locked map, with semantics similar to sync.Map, // but only supports inserting once and getting. type ( syncmap[K comparable, V any] struct { mut sync.Mutex data map[K]V } SyncMap[K comparable, V any] interface { Get(key K) (value V, ok bool) Put(key K, value V) (inserted bool) } ) func New[K comparable, V any]() SyncMap[K, V] { return &syncmap[K, V]{ data: make(map[K]V), } } func (m *syncmap[K, V]) Get(key K) (value V, ok bool) { m.mut.Lock() defer m.mut.Unlock() value, ok = m.data[key] return value, ok } func (m *syncmap[K, V]) Put(key K, value V) (inserted bool) { m.mut.Lock() defer m.mut.Unlock() if _, ok := m.data[key]; ok { return false } m.data[key] = value return true } ================================================ FILE: common/syncmap/syncmap_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package syncmap import ( "strconv" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // exclusively tests for races and crashes func TestRace(t *testing.T) { t.Parallel() const parallel = 100 var finish sync.WaitGroup var start sync.WaitGroup finish.Add(parallel) start.Add(parallel + 1) m := New[string, int]() for i := 0; i < parallel; i++ { i := i go func() { defer finish.Done() start.Done() start.Wait() // maximize contention key := strconv.Itoa(i % 3) if i%2 == 0 { _ = m.Put(key, i) _, _ = m.Get(key) } else { // reverse order to thrash a bit harder _, _ = m.Get(key) _ = m.Put(key, i) } }() } start.Done() finish.Wait() } // checks get/put behavior. it's somewhat different and not *expected* to change, // but should be totally fine to change to a general map API as long as existing uses are adapted. func TestBasics(t *testing.T) { t.Parallel() const key = "key" const orig = 5 const replacement = 10 require.NotEqual(t, orig, replacement) m := New[string, int]() val, ok := m.Get(key) assert.False(t, ok, "should not get until a value is inserted") assert.Zero(t, val, "should get zero value if nothing inserted") inserted := m.Put(key, orig) assert.True(t, inserted, "should have inserted into empty map") val, ok = m.Get(key) assert.True(t, ok, "should successfully get after insert") assert.Equal(t, orig, val, "should get what was put") replaced := m.Put(key, replacement) assert.False(t, replaced, "should not replace existing values") val, ok = m.Get(key) assert.True(t, ok, "should still get") assert.Equal(t, orig, val, "should get original value, not replaced") } ================================================ FILE: common/task/fifo_task_scheduler.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type fifoTaskSchedulerImpl[T Task] struct { status int32 logger log.Logger metricsScope metrics.Scope options *FIFOTaskSchedulerOptions dispatcherWG sync.WaitGroup taskCh chan T ctx context.Context cancel context.CancelFunc processor Processor } // NewFIFOTaskScheduler creates a new FIFO task scheduler // it's an no-op implementation as it simply copy tasks from // one task channel to another task channel. // This scheduler is only for development purpose. func NewFIFOTaskScheduler[T Task]( logger log.Logger, metricsClient metrics.Client, options *FIFOTaskSchedulerOptions, ) Scheduler[T] { ctx, cancel := context.WithCancel(context.Background()) return &fifoTaskSchedulerImpl[T]{ status: common.DaemonStatusInitialized, logger: logger, metricsScope: metricsClient.Scope(metrics.TaskSchedulerScope), options: options, taskCh: make(chan T, options.QueueSize), ctx: ctx, cancel: cancel, processor: NewParallelTaskProcessor( logger, metricsClient, &ParallelTaskProcessorOptions{ QueueSize: options.QueueSize, WorkerCount: options.WorkerCount, RetryPolicy: options.RetryPolicy, }, ), } } func (f *fifoTaskSchedulerImpl[T]) Start() { if !atomic.CompareAndSwapInt32(&f.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } f.processor.Start() f.dispatcherWG.Add(f.options.DispatcherCount) for i := 0; i != f.options.DispatcherCount; i++ { go f.dispatcher() } f.logger.Info("FIFO task scheduler started.") } func (f *fifoTaskSchedulerImpl[T]) Stop() { if !atomic.CompareAndSwapInt32(&f.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } f.cancel() f.processor.Stop() f.drainAndNackTasks() if success := common.AwaitWaitGroup(&f.dispatcherWG, time.Minute); !success { f.logger.Warn("FIFO task scheduler timedout on shutdown.") } f.logger.Info("FIFO task scheduler shutdown.") } func (f *fifoTaskSchedulerImpl[T]) Submit(task T) error { f.metricsScope.IncCounter(metrics.ParallelTaskSubmitRequest) sw := f.metricsScope.StartTimer(metrics.ParallelTaskSubmitLatency) defer sw.Stop() if f.isStopped() { return ErrTaskSchedulerClosed } select { case f.taskCh <- task: if f.isStopped() { f.drainAndNackTasks() } return nil case <-f.ctx.Done(): return ErrTaskSchedulerClosed } } func (f *fifoTaskSchedulerImpl[T]) TrySubmit(task T) (bool, error) { if f.isStopped() { return false, ErrTaskSchedulerClosed } select { case f.taskCh <- task: f.metricsScope.IncCounter(metrics.ParallelTaskSubmitRequest) if f.isStopped() { f.drainAndNackTasks() } return true, nil case <-f.ctx.Done(): return false, ErrTaskSchedulerClosed default: return false, nil } } func (f *fifoTaskSchedulerImpl[T]) dispatcher() { defer f.dispatcherWG.Done() for { select { case task := <-f.taskCh: if err := f.processor.Submit(task); err != nil { f.logger.Error("failed to submit task to processor", tag.Error(err)) task.Nack(err) } case <-f.ctx.Done(): return } } } func (f *fifoTaskSchedulerImpl[T]) isStopped() bool { return atomic.LoadInt32(&f.status) == common.DaemonStatusStopped } func (f *fifoTaskSchedulerImpl[T]) drainAndNackTasks() { for { select { case task := <-f.taskCh: task.Nack(nil) default: return } } } ================================================ FILE: common/task/fifo_task_scheduler_options.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "fmt" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) // FIFOTaskSchedulerOptions configs FIFO task scheduler type FIFOTaskSchedulerOptions struct { QueueSize int WorkerCount dynamicproperties.IntPropertyFn DispatcherCount int RetryPolicy backoff.RetryPolicy } func (o *FIFOTaskSchedulerOptions) String() string { return fmt.Sprintf("{QueueSize: %v, WorkerCount: %v, DispatcherCount: %v}", o.QueueSize, o.WorkerCount(), o.DispatcherCount) } ================================================ FILE: common/task/fifo_task_scheduler_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "sync" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) type ( fifoTaskSchedulerSuite struct { *require.Assertions suite.Suite controller *gomock.Controller mockProcessor *MockProcessor queueSize int scheduler *fifoTaskSchedulerImpl[PriorityTask] } ) func TestFIFOTaskSchedulerSuite(t *testing.T) { s := new(fifoTaskSchedulerSuite) suite.Run(t, s) } func (s *fifoTaskSchedulerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockProcessor = NewMockProcessor(s.controller) s.queueSize = 2 s.scheduler = NewFIFOTaskScheduler[PriorityTask]( testlogger.New(s.Suite.T()), metrics.NewClient(tally.NoopScope, metrics.Common, metrics.HistogramMigration{}), &FIFOTaskSchedulerOptions{ QueueSize: s.queueSize, WorkerCount: dynamicproperties.GetIntPropertyFn(1), DispatcherCount: 1, RetryPolicy: backoff.NewExponentialRetryPolicy(time.Millisecond), }, ).(*fifoTaskSchedulerImpl[PriorityTask]) } func (s *fifoTaskSchedulerSuite) TearDownTest() { s.controller.Finish() } func (s *fifoTaskSchedulerSuite) TestFIFO() { numTasks := 5 tasks := []PriorityTask{} var taskWG sync.WaitGroup calls := []any{ s.mockProcessor.EXPECT().Start(), } mockFn := func(_ Task) error { taskWG.Done() return nil } for i := 0; i != numTasks; i++ { mockTask := NewMockPriorityTask(s.controller) tasks = append(tasks, mockTask) taskWG.Add(1) calls = append(calls, s.mockProcessor.EXPECT().Submit(newMockPriorityTaskMatcher(mockTask)).DoAndReturn(mockFn)) } calls = append(calls, s.mockProcessor.EXPECT().Stop()) gomock.InOrder(calls...) s.scheduler.processor = s.mockProcessor s.scheduler.Start() for _, task := range tasks { s.NoError(s.scheduler.Submit(task)) } taskWG.Wait() s.scheduler.Stop() } func (s *fifoTaskSchedulerSuite) TestTrySubmit() { for i := 0; i != s.queueSize; i++ { mockTask := NewMockPriorityTask(s.controller) submitted, err := s.scheduler.TrySubmit(mockTask) s.NoError(err) s.True(submitted) } // now the queue is full, submit one more task, should be non-blocking mockTask := NewMockPriorityTask(s.controller) submitted, err := s.scheduler.TrySubmit(mockTask) s.NoError(err) s.False(submitted) } func (s *fifoTaskSchedulerSuite) TestSchedulerContract() { testSchedulerContract(s.Assertions, s.controller, s.scheduler, nil) } ================================================ FILE: common/task/hierarchical_weighted_round_robin_task_pool.go ================================================ package task import ( "context" "errors" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" ) var ( errWeightedKeyWeightMustBeGreaterThanZero = errors.New("weight must be greater than 0") ) type HierarchicalWeightedRoundRobinTaskPoolOptions[K comparable, T Task] struct { BufferSize int TaskToWeightedKeysFn func(T) []WeightedKey[K] } type hierarchicalWeightedRoundRobinTaskPoolImpl[K comparable, T Task] struct { sync.Mutex status int32 root *iwrrNode[K, T] bufferSize int ctx context.Context cancel context.CancelFunc options *HierarchicalWeightedRoundRobinTaskPoolOptions[K, T] logger log.Logger timeSource clock.TimeSource wg sync.WaitGroup doCleanupFn func(now time.Time, ttl time.Duration) } // newHierarchicalWeightedRoundRobinTaskPool creates a new hierarchical WRR task pool func newHierarchicalWeightedRoundRobinTaskPool[K comparable, T Task]( logger log.Logger, metricsClient metrics.Client, timeSource clock.TimeSource, options *HierarchicalWeightedRoundRobinTaskPoolOptions[K, T], ) *hierarchicalWeightedRoundRobinTaskPoolImpl[K, T] { ctx, cancel := context.WithCancel(context.Background()) pool := &hierarchicalWeightedRoundRobinTaskPoolImpl[K, T]{ status: common.DaemonStatusInitialized, root: newiwrrNode[K, T](options.BufferSize), bufferSize: options.BufferSize, ctx: ctx, cancel: cancel, options: options, logger: logger, timeSource: timeSource, } pool.doCleanupFn = pool.doCleanup return pool } func (p *hierarchicalWeightedRoundRobinTaskPoolImpl[K, T]) Start() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } p.wg.Add(1) go p.cleanupLoop() p.logger.Info("Hierarchical weighted round robin task pool started.") } func (p *hierarchicalWeightedRoundRobinTaskPoolImpl[K, T]) Stop() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } p.cancel() p.wg.Wait() p.logger.Info("Hierarchical weighted round robin task pool stopped.") } func (p *hierarchicalWeightedRoundRobinTaskPoolImpl[K, T]) cleanupLoop() { defer p.wg.Done() ticker := p.timeSource.NewTicker(time.Duration((defaultIdleChannelTTLInSeconds / 2)) * time.Second) defer ticker.Stop() for { select { case <-ticker.Chan(): now := p.timeSource.Now() ttl := time.Duration(defaultIdleChannelTTLInSeconds) * time.Second p.doCleanupFn(now, ttl) case <-p.ctx.Done(): return } } } func (p *hierarchicalWeightedRoundRobinTaskPoolImpl[K, T]) doCleanup(now time.Time, ttl time.Duration) { p.root.cleanup(now, ttl) } func (p *hierarchicalWeightedRoundRobinTaskPoolImpl[K, T]) Enqueue(task T) error { weightedKeys := p.options.TaskToWeightedKeysFn(task) if err := verifyWeightedKeys(weightedKeys); err != nil { return err } var err error p.root.c.IncRef() defer p.root.c.DecRef() p.root.executeAtPath(weightedKeys, p.bufferSize, func(c *TTLChannel[T]) int64 { select { case c.Chan() <- task: c.UpdateLastWriteTime(p.timeSource.Now()) return 1 case <-p.ctx.Done(): err = p.ctx.Err() return 0 } }) return err } func (p *hierarchicalWeightedRoundRobinTaskPoolImpl[K, T]) TryEnqueue(task T) (bool, error) { weightedKeys := p.options.TaskToWeightedKeysFn(task) if err := verifyWeightedKeys(weightedKeys); err != nil { return false, err } var err error p.root.c.IncRef() defer p.root.c.DecRef() delta := p.root.executeAtPath(weightedKeys, p.bufferSize, func(c *TTLChannel[T]) int64 { select { case c.Chan() <- task: c.UpdateLastWriteTime(p.timeSource.Now()) return 1 case <-p.ctx.Done(): err = p.ctx.Err() return 0 default: return 0 } }) return delta > 0, err } func (p *hierarchicalWeightedRoundRobinTaskPoolImpl[K, T]) TryDequeue() (T, bool) { var zero T item, ok := p.root.tryGetNextItem() if !ok { return zero, false } return item, true } func verifyWeightedKeys[K comparable](weightedKeys []WeightedKey[K]) error { for _, weightedKey := range weightedKeys { if weightedKey.Weight <= 0 { return errWeightedKeyWeightMustBeGreaterThanZero } } return nil } ================================================ FILE: common/task/hierarchical_weighted_round_robin_task_pool_test.go ================================================ package task import ( "math/rand" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) // testPriorityTask is a simple test implementation of PriorityTask type testPriorityTask struct { domain string tasklist string tenant string state State } func (t *testPriorityTask) Execute() error { return nil } func (t *testPriorityTask) HandleErr(err error) error { return err } func (t *testPriorityTask) RetryErr(err error) bool { return false } func (t *testPriorityTask) Ack() { t.state = TaskStateAcked } func (t *testPriorityTask) Nack(err error) { t.state = TaskStatePending } func (t *testPriorityTask) Cancel() { t.state = TaskStateCanceled } func (t *testPriorityTask) State() State { return t.state } func (t *testPriorityTask) Priority() int { return 0 } func (t *testPriorityTask) SetPriority(p int) {} func TestHierarchicalWRRTaskPool_SingleLevel_IWRROrdering(t *testing.T) { // Define domain to weight mapping domainWeights := map[string]int{ "domain0": 5, "domain1": 3, "domain2": 1, } pool := newHierarchicalWeightedRoundRobinTaskPool[string, *testPriorityTask]( testlogger.New(t), metrics.NoopClient, clock.NewMockedTimeSource(), &HierarchicalWeightedRoundRobinTaskPoolOptions[string, *testPriorityTask]{ BufferSize: 20, TaskToWeightedKeysFn: func(task *testPriorityTask) []WeightedKey[string] { // Single-level hierarchy: tasks grouped by domain with different weights return []WeightedKey[string]{ {Key: task.domain, Weight: domainWeights[task.domain]}, } }, }, ) pool.Start() defer pool.Stop() // Create tasks with weights [5, 3, 1] // Domain0 (weight 5): 5 tasks // Domain1 (weight 3): 3 tasks // Domain2 (weight 1): 1 task var tasks []*testPriorityTask for i := 0; i < 5; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain0"}) } for i := 0; i < 3; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain1"}) } tasks = append(tasks, &testPriorityTask{domain: "domain2"}) // Enqueue tasks in parallel using randomly chosen Enqueue or TryEnqueue var wg sync.WaitGroup for _, task := range tasks { wg.Add(1) go func(t *testPriorityTask) { defer wg.Done() // Randomly choose between Enqueue and TryEnqueue if rand.Intn(2) == 0 { _ = pool.Enqueue(t) } else { _, _ = pool.TryEnqueue(t) } }(task) } wg.Wait() // IWRR pattern for weights [5, 3, 1]: domain0, domain0, domain0, domain1, domain0, domain1, domain0, domain1, domain2 // Expected domain sequence: [0, 0, 0, 1, 0, 1, 0, 1, 2] expectedDomainPattern := []string{"domain0", "domain0", "domain0", "domain1", "domain0", "domain1", "domain0", "domain1", "domain2"} var actualDomainSequence []string for { task, ok := pool.TryDequeue() if !ok { break } actualDomainSequence = append(actualDomainSequence, task.domain) } assert.Equal(t, expectedDomainPattern, actualDomainSequence) } func TestHierarchicalWRRTaskPool_TwoLevel_IWRROrdering(t *testing.T) { // Define domain to weight mapping - different weights domainWeights := map[string]int{ "domain1": 3, "domain2": 2, "domain3": 1, } // Define tasklist to weight mapping tasklistWeights := map[string]int{ "tasklist1A": 2, "tasklist1B": 1, "tasklist2A": 3, "tasklist2B": 1, "tasklist3A": 2, "tasklist3B": 1, } pool := newHierarchicalWeightedRoundRobinTaskPool[string, *testPriorityTask]( testlogger.New(t), metrics.NoopClient, clock.NewMockedTimeSource(), &HierarchicalWeightedRoundRobinTaskPoolOptions[string, *testPriorityTask]{ BufferSize: 20, TaskToWeightedKeysFn: func(task *testPriorityTask) []WeightedKey[string] { // Two-level hierarchy: domain (level 1) -> tasklist (level 2) return []WeightedKey[string]{ {Key: task.domain, Weight: domainWeights[task.domain]}, {Key: task.tasklist, Weight: tasklistWeights[task.tasklist]}, } }, }, ) pool.Start() defer pool.Stop() // Create enough tasks for each domain to complete at least one full tasklist IWRR cycle // Domain1 tasklist IWRR [2, 1]: [tasklist1A, tasklist1A, tasklist1B] = 3 tasks // Domain2 tasklist IWRR [3, 1]: [tasklist2A, tasklist2A, tasklist2A, tasklist2B] = 4 tasks // Domain3 tasklist IWRR [2, 1]: [tasklist3A, tasklist3A, tasklist3B] = 3 tasks tasks := []*testPriorityTask{ // Domain1 tasks (3 tasks for one complete tasklist IWRR) &testPriorityTask{domain: "domain1", tasklist: "tasklist1A"}, &testPriorityTask{domain: "domain1", tasklist: "tasklist1A"}, &testPriorityTask{domain: "domain1", tasklist: "tasklist1B"}, // Domain2 tasks (4 tasks for one complete tasklist IWRR) &testPriorityTask{domain: "domain2", tasklist: "tasklist2A"}, &testPriorityTask{domain: "domain2", tasklist: "tasklist2A"}, &testPriorityTask{domain: "domain2", tasklist: "tasklist2A"}, &testPriorityTask{domain: "domain2", tasklist: "tasklist2B"}, // Domain3 tasks (3 tasks for one complete tasklist IWRR) &testPriorityTask{domain: "domain3", tasklist: "tasklist3A"}, &testPriorityTask{domain: "domain3", tasklist: "tasklist3A"}, &testPriorityTask{domain: "domain3", tasklist: "tasklist3B"}, } // Enqueue all tasks in parallel using randomly chosen Enqueue or TryEnqueue var wg sync.WaitGroup for _, task := range tasks { wg.Add(1) go func(t *testPriorityTask) { defer wg.Done() // Randomly choose between Enqueue and TryEnqueue if rand.Intn(2) == 0 { _ = pool.Enqueue(t) } else { _, _ = pool.TryEnqueue(t) } }(task) } wg.Wait() // Domain IWRR pattern for weights [3, 2, 1]: [domain1, domain1, domain2, domain1, domain2, domain3] // When we dequeue, we follow this domain pattern, and within each domain we follow its tasklist IWRR // // Expected dequeue pattern: // 1. domain1 (1st visit) -> tasklist1A (position 0 in tasklist IWRR) // 2. domain1 (2nd visit) -> tasklist1A (position 1 in tasklist IWRR) // 3. domain2 (1st visit) -> tasklist2A (position 0 in tasklist IWRR) // 4. domain1 (3rd visit) -> tasklist1B (position 2 in tasklist IWRR) // 5. domain2 (2nd visit) -> tasklist2A (position 1 in tasklist IWRR) // 6. domain3 (1st visit) -> tasklist3A (position 0 in tasklist IWRR) // Now we loop back to domain1, but it's exhausted, so we skip it // 7. domain2 (3rd visit) -> tasklist2A (position 2 in tasklist IWRR) // 8. domain2 (4th visit) -> tasklist2B (position 3 in tasklist IWRR) // 9. domain3 (2nd visit) -> tasklist3A (position 1 in tasklist IWRR) // 10. domain3 (3rd visit) -> tasklist3B (position 2 in tasklist IWRR) type expectedTask struct { domain string tasklist string } expectedPattern := []expectedTask{ {"domain1", "tasklist1A"}, // domain IWRR pos 0, tasklist IWRR pos 0 {"domain1", "tasklist1A"}, // domain IWRR pos 1, tasklist IWRR pos 1 {"domain2", "tasklist2A"}, // domain IWRR pos 2, tasklist IWRR pos 0 {"domain1", "tasklist1B"}, // domain IWRR pos 3, tasklist IWRR pos 2 {"domain2", "tasklist2A"}, // domain IWRR pos 4, tasklist IWRR pos 1 {"domain3", "tasklist3A"}, // domain IWRR pos 5, tasklist IWRR pos 0 {"domain2", "tasklist2A"}, // domain1 exhausted, skip to domain2, tasklist IWRR pos 2 {"domain2", "tasklist2B"}, // continue domain2, tasklist IWRR pos 3 {"domain3", "tasklist3A"}, // domain3, tasklist IWRR pos 1 {"domain3", "tasklist3B"}, // domain3, tasklist IWRR pos 2 } // Dequeue all tasks and verify the order var actualPattern []expectedTask for { task, ok := pool.TryDequeue() if !ok { break } actualPattern = append(actualPattern, expectedTask{ domain: task.domain, tasklist: task.tasklist, }) } // Verify exact ordering require.Equal(t, len(expectedPattern), len(actualPattern), "should dequeue all tasks") for i, expected := range expectedPattern { assert.Equal(t, expected.domain, actualPattern[i].domain, "task %d domain mismatch", i) assert.Equal(t, expected.tasklist, actualPattern[i].tasklist, "task %d tasklist mismatch", i) } } func TestHierarchicalWRRTaskPool_TwoLevel_LargeScale_IWRROrdering(t *testing.T) { // Define domain to weight mapping domainWeights := map[string]int{ "domain1": 5, "domain2": 3, "domain3": 2, } // Define tasklist to weight mapping tasklistWeights := map[string]int{ "tasklist1A": 3, "tasklist1B": 2, "tasklist1C": 1, "tasklist2A": 4, "tasklist2B": 2, "tasklist3A": 3, "tasklist3B": 1, } pool := newHierarchicalWeightedRoundRobinTaskPool[string, *testPriorityTask]( testlogger.New(t), metrics.NoopClient, clock.NewMockedTimeSource(), &HierarchicalWeightedRoundRobinTaskPoolOptions[string, *testPriorityTask]{ BufferSize: 100, TaskToWeightedKeysFn: func(task *testPriorityTask) []WeightedKey[string] { return []WeightedKey[string]{ {Key: task.domain, Weight: domainWeights[task.domain]}, {Key: task.tasklist, Weight: tasklistWeights[task.tasklist]}, } }, }, ) pool.Start() defer pool.Stop() // Create a large number of tasks // Domain1: 30 tasks (5 cycles of tasklist IWRR: [3,2,1] = 6 tasks per cycle) // Domain2: 30 tasks (5 cycles of tasklist IWRR: [4,2] = 6 tasks per cycle) // Domain3: 20 tasks (5 cycles of tasklist IWRR: [3,1] = 4 tasks per cycle) var tasks []*testPriorityTask // Domain1: 5 cycles of [tasklist1A(3), tasklist1B(2), tasklist1C(1)] for cycle := 0; cycle < 5; cycle++ { for i := 0; i < 3; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain1", tasklist: "tasklist1A"}) } for i := 0; i < 2; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain1", tasklist: "tasklist1B"}) } tasks = append(tasks, &testPriorityTask{domain: "domain1", tasklist: "tasklist1C"}) } // Domain2: 5 cycles of [tasklist2A(4), tasklist2B(2)] for cycle := 0; cycle < 5; cycle++ { for i := 0; i < 4; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain2", tasklist: "tasklist2A"}) } for i := 0; i < 2; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain2", tasklist: "tasklist2B"}) } } // Domain3: 5 cycles of [tasklist3A(3), tasklist3B(1)] for cycle := 0; cycle < 5; cycle++ { for i := 0; i < 3; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain3", tasklist: "tasklist3A"}) } tasks = append(tasks, &testPriorityTask{domain: "domain3", tasklist: "tasklist3B"}) } // Enqueue all tasks in parallel using randomly chosen Enqueue or TryEnqueue var wg sync.WaitGroup for _, task := range tasks { wg.Add(1) go func(t *testPriorityTask) { defer wg.Done() if rand.Intn(2) == 0 { _ = pool.Enqueue(t) } else { _, _ = pool.TryEnqueue(t) } }(task) } wg.Wait() // Generate expected patterns by level // Level 1 (domain): IWRR for weights [5, 3, 2] // Round 4: domain1 (weight 5 > 4) // Round 3: domain1 (weight 5 > 3) // Round 2: domain1, domain2 (weights 5,3 > 2) // Round 1: domain1, domain2, domain3 (weights 5,3,2 > 1) // Round 0: domain1, domain2, domain3 (weights 5,3,2 > 0) // Pattern: [domain1, domain1, domain1, domain2, domain1, domain2, domain3, domain1, domain2, domain3] expectedDomainPattern := []string{ "domain1", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain2", "domain3", } // Level 2 (tasklist per domain): // domain1 with weights [3, 2, 1]: [tasklist1A, tasklist1A, tasklist1B, tasklist1A, tasklist1B, tasklist1C] // domain2 with weights [4, 2]: [tasklist2A, tasklist2A, tasklist2A, tasklist2B, tasklist2A, tasklist2B] // domain3 with weights [3, 1]: [tasklist3A, tasklist3A, tasklist3A, tasklist3B] expectedTasklistPatterns := map[string][]string{ "domain1": {"tasklist1A", "tasklist1A", "tasklist1B", "tasklist1A", "tasklist1B", "tasklist1C"}, "domain2": {"tasklist2A", "tasklist2A", "tasklist2A", "tasklist2B", "tasklist2A", "tasklist2B"}, "domain3": {"tasklist3A", "tasklist3A", "tasklist3A", "tasklist3B"}, } // Dequeue all tasks var dequeuedDomains []string dequeuedTasklistsByDomain := make(map[string][]string) for { task, ok := pool.TryDequeue() if !ok { break } dequeuedDomains = append(dequeuedDomains, task.domain) dequeuedTasklistsByDomain[task.domain] = append( dequeuedTasklistsByDomain[task.domain], task.tasklist, ) } // Verify we got all 80 tasks require.Equal(t, 80, len(dequeuedDomains), "should dequeue all 80 tasks") // Verify level 1 (domain) pattern for the first cycle (first 10 tasks) // After that, domains may exhaust at different rates, so we only verify the first full cycle for i := 0; i < len(expectedDomainPattern) && i < len(dequeuedDomains); i++ { assert.Equal(t, expectedDomainPattern[i], dequeuedDomains[i], "domain mismatch at position %d in first cycle", i) } // Verify level 2 (tasklist) pattern for each domain // Each domain should follow its own tasklist IWRR pattern consistently for domain, dequeuedTasklists := range dequeuedTasklistsByDomain { expectedPattern := expectedTasklistPatterns[domain] for i := 0; i < len(dequeuedTasklists); i++ { expectedTasklist := expectedPattern[i%len(expectedPattern)] assert.Equal(t, expectedTasklist, dequeuedTasklists[i], "tasklist mismatch for domain %s at position %d", domain, i) } } } func TestHierarchicalWRRTaskPool_ThreeLevel_LargeScale_IWRROrdering(t *testing.T) { // Define domain to weight mapping domainWeights := map[string]int{ "domain1": 3, "domain2": 2, "domain3": 1, } // Define tasklist to weight mapping tasklistWeights := map[string]int{ "tasklist1A": 3, "tasklist1B": 1, "tasklist2A": 2, "tasklist2B": 1, "tasklist3A": 2, "tasklist3B": 1, } // Define tenant to weight mapping tenantWeights := map[string]int{ "tenant1": 3, "tenant2": 2, "tenant3": 1, } pool := newHierarchicalWeightedRoundRobinTaskPool[string, *testPriorityTask]( testlogger.New(t), metrics.NoopClient, clock.NewMockedTimeSource(), &HierarchicalWeightedRoundRobinTaskPoolOptions[string, *testPriorityTask]{ BufferSize: 200, TaskToWeightedKeysFn: func(task *testPriorityTask) []WeightedKey[string] { return []WeightedKey[string]{ {Key: task.domain, Weight: domainWeights[task.domain]}, {Key: task.tasklist, Weight: tasklistWeights[task.tasklist]}, {Key: task.tenant, Weight: tenantWeights[task.tenant]}, } }, }, ) pool.Start() defer pool.Stop() // Create a large number of tasks across 3 levels // Each (domain, tasklist) combination gets multiple tenant cycles // Tenant IWRR for weights [3, 2, 1]: 6 tasks per cycle var tasks []*testPriorityTask // Domain1 - tasklist1A (weight 3) - 3 tenant cycles = 18 tasks for cycle := 0; cycle < 3; cycle++ { for i := 0; i < 3; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain1", tasklist: "tasklist1A", tenant: "tenant1"}) } for i := 0; i < 2; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain1", tasklist: "tasklist1A", tenant: "tenant2"}) } tasks = append(tasks, &testPriorityTask{domain: "domain1", tasklist: "tasklist1A", tenant: "tenant3"}) } // Domain1 - tasklist1B (weight 1) - 3 tenant cycles = 18 tasks for cycle := 0; cycle < 3; cycle++ { for i := 0; i < 3; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain1", tasklist: "tasklist1B", tenant: "tenant1"}) } for i := 0; i < 2; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain1", tasklist: "tasklist1B", tenant: "tenant2"}) } tasks = append(tasks, &testPriorityTask{domain: "domain1", tasklist: "tasklist1B", tenant: "tenant3"}) } // Domain2 - tasklist2A (weight 2) - 2 tenant cycles = 12 tasks for cycle := 0; cycle < 2; cycle++ { for i := 0; i < 3; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain2", tasklist: "tasklist2A", tenant: "tenant1"}) } for i := 0; i < 2; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain2", tasklist: "tasklist2A", tenant: "tenant2"}) } tasks = append(tasks, &testPriorityTask{domain: "domain2", tasklist: "tasklist2A", tenant: "tenant3"}) } // Domain2 - tasklist2B (weight 1) - 2 tenant cycles = 12 tasks for cycle := 0; cycle < 2; cycle++ { for i := 0; i < 3; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain2", tasklist: "tasklist2B", tenant: "tenant1"}) } for i := 0; i < 2; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain2", tasklist: "tasklist2B", tenant: "tenant2"}) } tasks = append(tasks, &testPriorityTask{domain: "domain2", tasklist: "tasklist2B", tenant: "tenant3"}) } // Domain3 - tasklist3A (weight 2) - 2 tenant cycles = 12 tasks for cycle := 0; cycle < 2; cycle++ { for i := 0; i < 3; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain3", tasklist: "tasklist3A", tenant: "tenant1"}) } for i := 0; i < 2; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain3", tasklist: "tasklist3A", tenant: "tenant2"}) } tasks = append(tasks, &testPriorityTask{domain: "domain3", tasklist: "tasklist3A", tenant: "tenant3"}) } // Domain3 - tasklist3B (weight 1) - 2 tenant cycles = 12 tasks for cycle := 0; cycle < 2; cycle++ { for i := 0; i < 3; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain3", tasklist: "tasklist3B", tenant: "tenant1"}) } for i := 0; i < 2; i++ { tasks = append(tasks, &testPriorityTask{domain: "domain3", tasklist: "tasklist3B", tenant: "tenant2"}) } tasks = append(tasks, &testPriorityTask{domain: "domain3", tasklist: "tasklist3B", tenant: "tenant3"}) } // Total: 18+18+12+12+12+12 = 84 tasks // Enqueue all tasks in parallel using randomly chosen Enqueue or TryEnqueue var wg sync.WaitGroup for _, task := range tasks { wg.Add(1) go func(t *testPriorityTask) { defer wg.Done() if rand.Intn(2) == 0 { _ = pool.Enqueue(t) } else { _, _ = pool.TryEnqueue(t) } }(task) } wg.Wait() // Dequeue all tasks type taskKey struct { domain string tasklist string } var dequeuedDomains []string dequeuedTasklistsByDomain := make(map[string][]string) dequeuedTenantsByTasklist := make(map[taskKey][]string) for { task, ok := pool.TryDequeue() if !ok { break } dequeuedDomains = append(dequeuedDomains, task.domain) dequeuedTasklistsByDomain[task.domain] = append( dequeuedTasklistsByDomain[task.domain], task.tasklist, ) key := taskKey{domain: task.domain, tasklist: task.tasklist} dequeuedTenantsByTasklist[key] = append( dequeuedTenantsByTasklist[key], task.tenant, ) } // Verify we got all 84 tasks require.Equal(t, 84, len(dequeuedDomains), "should dequeue all 84 tasks") // Build expected domain sequence manually with exhaustion tracking // Domain task counts: domain1=36, domain2=24, domain3=24 // Pattern: [domain1, domain1, domain2, domain1, domain2, domain3] repeats 12 times (72 tasks) // After 12 cycles: domain1=36 (exhausted), domain2=24 (exhausted), domain3=12 // Then domain3 continues for 12 more tasks expectedDomainSequence := []string{ // 12 full cycles "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", "domain1", "domain1", "domain2", "domain1", "domain2", "domain3", // domain1 and domain2 exhausted, domain3 continues for 12 more "domain3", "domain3", "domain3", "domain3", "domain3", "domain3", "domain3", "domain3", "domain3", "domain3", "domain3", "domain3", } // Verify full domain sequence require.Equal(t, 84, len(expectedDomainSequence), "expected domain sequence length") require.Equal(t, len(expectedDomainSequence), len(dequeuedDomains), "domain sequence length mismatch") for i := 0; i < len(expectedDomainSequence); i++ { assert.Equal(t, expectedDomainSequence[i], dequeuedDomains[i], "domain mismatch at position %d", i) } // Build expected tasklist sequences manually for each domain expectedTasklistSequences := map[string][]string{ // Domain1: tasklist1A=18, tasklist1B=18 // Pattern: [tasklist1A, tasklist1A, tasklist1A, tasklist1B] repeats 6 times (24 tasks) // After 6 cycles: tasklist1A=18 (exhausted), tasklist1B=6 // Then tasklist1B continues for 12 more tasks "domain1": { // 6 full cycles "tasklist1A", "tasklist1A", "tasklist1A", "tasklist1B", "tasklist1A", "tasklist1A", "tasklist1A", "tasklist1B", "tasklist1A", "tasklist1A", "tasklist1A", "tasklist1B", "tasklist1A", "tasklist1A", "tasklist1A", "tasklist1B", "tasklist1A", "tasklist1A", "tasklist1A", "tasklist1B", "tasklist1A", "tasklist1A", "tasklist1A", "tasklist1B", // tasklist1A exhausted, tasklist1B continues for 12 more "tasklist1B", "tasklist1B", "tasklist1B", "tasklist1B", "tasklist1B", "tasklist1B", "tasklist1B", "tasklist1B", "tasklist1B", "tasklist1B", "tasklist1B", "tasklist1B", }, // Domain2: tasklist2A=12, tasklist2B=12 // Pattern: [tasklist2A, tasklist2A, tasklist2B] repeats 6 times (18 tasks) // After 6 cycles: tasklist2A=12 (exhausted), tasklist2B=6 // Then tasklist2B continues for 6 more tasks "domain2": { // 6 full cycles "tasklist2A", "tasklist2A", "tasklist2B", "tasklist2A", "tasklist2A", "tasklist2B", "tasklist2A", "tasklist2A", "tasklist2B", "tasklist2A", "tasklist2A", "tasklist2B", "tasklist2A", "tasklist2A", "tasklist2B", "tasklist2A", "tasklist2A", "tasklist2B", // tasklist2A exhausted, tasklist2B continues for 6 more "tasklist2B", "tasklist2B", "tasklist2B", "tasklist2B", "tasklist2B", "tasklist2B", }, // Domain3: tasklist3A=12, tasklist3B=12 // Pattern: [tasklist3A, tasklist3A, tasklist3B] repeats 6 times (18 tasks) // After 6 cycles: tasklist3A=12 (exhausted), tasklist3B=6 // Then tasklist3B continues for 6 more tasks "domain3": { // 6 full cycles "tasklist3A", "tasklist3A", "tasklist3B", "tasklist3A", "tasklist3A", "tasklist3B", "tasklist3A", "tasklist3A", "tasklist3B", "tasklist3A", "tasklist3A", "tasklist3B", "tasklist3A", "tasklist3A", "tasklist3B", "tasklist3A", "tasklist3A", "tasklist3B", // tasklist3A exhausted, tasklist3B continues for 6 more "tasklist3B", "tasklist3B", "tasklist3B", "tasklist3B", "tasklist3B", "tasklist3B", }, } // Verify full tasklist sequence for each domain for domain, expectedSequence := range expectedTasklistSequences { dequeuedTasklists := dequeuedTasklistsByDomain[domain] require.Equal(t, len(expectedSequence), len(dequeuedTasklists), "tasklist sequence length mismatch for domain %s", domain) for i := 0; i < len(expectedSequence); i++ { assert.Equal(t, expectedSequence[i], dequeuedTasklists[i], "tasklist mismatch for domain %s at position %d", domain, i) } } // Verify level 3 (tenant) pattern for each (domain, tasklist) combination // Build expected tenant sequences manually // Pattern: [tenant1, tenant1, tenant2, tenant1, tenant2, tenant3] - 6 tasks per cycle expectedTenantSequences := map[taskKey][]string{ // Domain1 tasklists: 3 cycles each = 18 tasks {domain: "domain1", tasklist: "tasklist1A"}: { "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", }, {domain: "domain1", tasklist: "tasklist1B"}: { "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", }, // Domain2 tasklists: 2 cycles each = 12 tasks {domain: "domain2", tasklist: "tasklist2A"}: { "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", }, {domain: "domain2", tasklist: "tasklist2B"}: { "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", }, // Domain3 tasklists: 2 cycles each = 12 tasks {domain: "domain3", tasklist: "tasklist3A"}: { "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", }, {domain: "domain3", tasklist: "tasklist3B"}: { "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", "tenant1", "tenant1", "tenant2", "tenant1", "tenant2", "tenant3", }, } for key, expectedSequence := range expectedTenantSequences { dequeuedTenants := dequeuedTenantsByTasklist[key] require.Equal(t, len(expectedSequence), len(dequeuedTenants), "tenant sequence length mismatch for domain=%s, tasklist=%s", key.domain, key.tasklist) for i := 0; i < len(expectedSequence); i++ { assert.Equal(t, expectedSequence[i], dequeuedTenants[i], "tenant mismatch for domain=%s, tasklist=%s at position %d", key.domain, key.tasklist, i) } } } func TestHierarchicalWRRTaskPool_TryDequeue_Empty(t *testing.T) { domainWeights := map[string]int{ "domain1": 3, } pool := newHierarchicalWeightedRoundRobinTaskPool[string]( testlogger.New(t), metrics.NoopClient, clock.NewMockedTimeSource(), &HierarchicalWeightedRoundRobinTaskPoolOptions[string, *testPriorityTask]{ BufferSize: 10, TaskToWeightedKeysFn: func(task *testPriorityTask) []WeightedKey[string] { return []WeightedKey[string]{ {Key: task.domain, Weight: domainWeights[task.domain]}, } }, }, ) pool.Start() defer pool.Stop() // Try to dequeue from empty pool task, ok := pool.TryDequeue() assert.False(t, ok, "TryDequeue should return false on empty pool") assert.Nil(t, task, "task should be nil when dequeue fails") // Enqueue one task and dequeue it testTask := &testPriorityTask{domain: "domain1"} err := pool.Enqueue(testTask) require.NoError(t, err) task, ok = pool.TryDequeue() assert.True(t, ok, "TryDequeue should return true when task exists") assert.NotNil(t, task, "task should not be nil") // Try to dequeue again from now-empty pool task, ok = pool.TryDequeue() assert.False(t, ok, "TryDequeue should return false after pool is emptied") assert.Nil(t, task, "task should be nil when dequeue fails") } func TestHierarchicalWRRTaskPool_TryEnqueue_BufferFull(t *testing.T) { domainWeights := map[string]int{ "domain1": 3, } // Create pool with small buffer size pool := newHierarchicalWeightedRoundRobinTaskPool[string, *testPriorityTask]( testlogger.New(t), metrics.NoopClient, clock.NewMockedTimeSource(), &HierarchicalWeightedRoundRobinTaskPoolOptions[string, *testPriorityTask]{ BufferSize: 2, // Small buffer to easily fill it TaskToWeightedKeysFn: func(task *testPriorityTask) []WeightedKey[string] { return []WeightedKey[string]{ {Key: task.domain, Weight: domainWeights[task.domain]}, } }, }, ) pool.Start() defer pool.Stop() // Fill the buffer completely success1, err1 := pool.TryEnqueue(&testPriorityTask{domain: "domain1"}) assert.True(t, success1, "first TryEnqueue should succeed") assert.NoError(t, err1) success2, err2 := pool.TryEnqueue(&testPriorityTask{domain: "domain1"}) assert.True(t, success2, "second TryEnqueue should succeed") assert.NoError(t, err2) // Try to enqueue when buffer is full success3, err3 := pool.TryEnqueue(&testPriorityTask{domain: "domain1"}) assert.False(t, success3, "TryEnqueue should return false when buffer is full") assert.NoError(t, err3, "TryEnqueue should not return error even when buffer is full") // Dequeue one task to free space task, ok := pool.TryDequeue() assert.True(t, ok, "TryDequeue should succeed") assert.NotNil(t, task) // Now TryEnqueue should succeed again success4, err4 := pool.TryEnqueue(&testPriorityTask{domain: "domain1"}) assert.True(t, success4, "TryEnqueue should succeed after freeing space") assert.NoError(t, err4) } func TestHierarchicalWRRTaskPool_Enqueue_ContextCancellation(t *testing.T) { domainWeights := map[string]int{ "domain1": 3, } // Create pool with small buffer size pool := newHierarchicalWeightedRoundRobinTaskPool[string, *testPriorityTask]( testlogger.New(t), metrics.NoopClient, clock.NewMockedTimeSource(), &HierarchicalWeightedRoundRobinTaskPoolOptions[string, *testPriorityTask]{ BufferSize: 1, // Very small buffer TaskToWeightedKeysFn: func(task *testPriorityTask) []WeightedKey[string] { return []WeightedKey[string]{ {Key: task.domain, Weight: domainWeights[task.domain]}, } }, }, ) pool.Start() // Fill the buffer err := pool.Enqueue(&testPriorityTask{domain: "domain1"}) require.NoError(t, err) // Start a goroutine that will block on Enqueue enqueueDone := make(chan error, 1) go func() { err := pool.Enqueue(&testPriorityTask{domain: "domain1"}) enqueueDone <- err }() // Stop the pool, which cancels the context pool.Stop() // The blocked Enqueue should return with context error select { case err := <-enqueueDone: assert.Error(t, err, "Enqueue should return error when context is cancelled") assert.Equal(t, pool.ctx.Err(), err, "error should be context cancellation error") case <-time.After(1 * time.Second): t.Fatal("Enqueue did not unblock after context cancellation") } } func TestHierarchicalWRRTaskPool_CleanupLoop(t *testing.T) { domainWeights := map[string]int{ "domain1": 3, } timeSource := clock.NewMockedTimeSource() pool := newHierarchicalWeightedRoundRobinTaskPool[string, *testPriorityTask]( testlogger.New(t), metrics.NoopClient, timeSource, &HierarchicalWeightedRoundRobinTaskPoolOptions[string, *testPriorityTask]{ BufferSize: 10, TaskToWeightedKeysFn: func(task *testPriorityTask) []WeightedKey[string] { return []WeightedKey[string]{ {Key: task.domain, Weight: domainWeights[task.domain]}, } }, }, ) // Replace doCleanupFn with a fake that tracks calls cleanupCalls := make(chan struct { now time.Time ttl time.Duration }, 10) pool.doCleanupFn = func(now time.Time, ttl time.Duration) { cleanupCalls <- struct { now time.Time ttl time.Duration }{now: now, ttl: ttl} } pool.Start() // Advance time by cleanup interval (1800 seconds = TTL/2) timeSource.BlockUntil(1) timeSource.Advance(1800 * time.Second) // Wait for first cleanup call call1 := <-cleanupCalls expectedTTL := 3600 * time.Second require.Equal(t, expectedTTL, call1.ttl, "TTL should be 3600 seconds") require.NotZero(t, call1.now, "now should be set") // Advance time by another interval timeSource.BlockUntil(1) timeSource.Advance(1800 * time.Second) call2 := <-cleanupCalls require.Equal(t, expectedTTL, call2.ttl, "TTL should be 3600 seconds") require.True(t, call2.now.After(call1.now), "second call should have later timestamp") // Stop the pool pool.Stop() // Advance time - should NOT trigger cleanup because loop is stopped timeSource.Advance(1800 * time.Second) select { case <-cleanupCalls: t.Fatal("cleanup should not be called after Stop()") default: // Expected - no cleanup call } } ================================================ FILE: common/task/hierarchical_weighted_round_robin_task_scheduler.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type hierarchicalWeightedRoundRobinTaskSchedulerImpl[K comparable, T Task] struct { sync.RWMutex status int32 pool *hierarchicalWeightedRoundRobinTaskPoolImpl[K, T] ctx context.Context cancel context.CancelFunc notifyCh chan struct{} dispatcherWG sync.WaitGroup logger log.Logger metricsScope metrics.Scope options *HierarchicalWeightedRoundRobinTaskPoolOptions[K, T] processor Processor } // NewHierarchicalWeightedRoundRobinTaskScheduler creates a new hierarchical WRR task scheduler func NewHierarchicalWeightedRoundRobinTaskScheduler[K comparable, T Task]( logger log.Logger, metricsClient metrics.Client, timeSource clock.TimeSource, processor Processor, options *HierarchicalWeightedRoundRobinTaskPoolOptions[K, T], ) (Scheduler[T], error) { metricsScope := metricsClient.Scope(metrics.TaskSchedulerScope) ctx, cancel := context.WithCancel(context.Background()) scheduler := &hierarchicalWeightedRoundRobinTaskSchedulerImpl[K, T]{ status: common.DaemonStatusInitialized, pool: newHierarchicalWeightedRoundRobinTaskPool[K]( logger, metricsClient, timeSource, options, ), ctx: ctx, cancel: cancel, notifyCh: make(chan struct{}, 1), logger: logger, metricsScope: metricsScope, options: options, processor: processor, } return scheduler, nil } func (w *hierarchicalWeightedRoundRobinTaskSchedulerImpl[K, T]) Start() { if !atomic.CompareAndSwapInt32(&w.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } w.pool.Start() w.dispatcherWG.Add(1) go w.dispatcher() w.logger.Info("Hierarchical weighted round robin task scheduler started.") } func (w *hierarchicalWeightedRoundRobinTaskSchedulerImpl[K, T]) Stop() { if !atomic.CompareAndSwapInt32(&w.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } w.cancel() w.pool.Stop() if success := common.AwaitWaitGroup(&w.dispatcherWG, time.Minute); !success { w.logger.Warn("Hierarchical weighted round robin task scheduler timedout on shutdown.") } w.drainAndNackTasks() w.logger.Info("Hierarchical weighted round robin task scheduler stopped.") } func (w *hierarchicalWeightedRoundRobinTaskSchedulerImpl[K, T]) Submit(task T) error { w.metricsScope.IncCounter(metrics.PriorityTaskSubmitRequest) sw := w.metricsScope.StartTimer(metrics.PriorityTaskSubmitLatency) defer sw.Stop() if w.isStopped() { return ErrTaskSchedulerClosed } if err := w.pool.Enqueue(task); err != nil { return err } w.notifyDispatcher() return nil } func (w *hierarchicalWeightedRoundRobinTaskSchedulerImpl[K, T]) TrySubmit( task T, ) (bool, error) { w.metricsScope.IncCounter(metrics.PriorityTaskSubmitRequest) sw := w.metricsScope.StartTimer(metrics.PriorityTaskSubmitLatency) defer sw.Stop() if w.isStopped() { return false, ErrTaskSchedulerClosed } ok, err := w.pool.TryEnqueue(task) if ok { w.notifyDispatcher() } return ok, err } func (w *hierarchicalWeightedRoundRobinTaskSchedulerImpl[K, T]) dispatcher() { defer w.dispatcherWG.Done() for { select { case <-w.notifyCh: w.dispatchTasks() case <-w.ctx.Done(): return } } } func (w *hierarchicalWeightedRoundRobinTaskSchedulerImpl[K, T]) dispatchTasks() { for { if w.isStopped() { return } task, ok := w.pool.TryDequeue() if !ok { return } if err := w.processor.Submit(task); err != nil { w.logger.Error("fail to submit task to processor", tag.Error(err)) task.Nack(err) } } } func (w *hierarchicalWeightedRoundRobinTaskSchedulerImpl[K, T]) notifyDispatcher() { select { case w.notifyCh <- struct{}{}: // sent a notification to the dispatcher default: // do not block if there's already a notification } } func (w *hierarchicalWeightedRoundRobinTaskSchedulerImpl[K, T]) isStopped() bool { return atomic.LoadInt32(&w.status) == common.DaemonStatusStopped } func (w *hierarchicalWeightedRoundRobinTaskSchedulerImpl[K, T]) drainAndNackTasks() { for { task, ok := w.pool.TryDequeue() if !ok { return } task.Nack(nil) } } ================================================ FILE: common/task/hierarchical_weighted_round_robin_task_scheduler_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "testing" "time" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) func TestHierarchicalWeightedRoundRobinTaskScheduler_SchedulerContract(t *testing.T) { controller := gomock.NewController(t) realProcessor := NewParallelTaskProcessor( testlogger.New(t), metrics.NewClient(tally.NoopScope, metrics.Common, metrics.HistogramMigration{}), &ParallelTaskProcessorOptions{ QueueSize: 1, WorkerCount: dynamicproperties.GetIntPropertyFn(1), RetryPolicy: backoff.NewExponentialRetryPolicy(time.Millisecond), }, ) // Create hierarchical scheduler with string keys based on priority scheduler, err := NewHierarchicalWeightedRoundRobinTaskScheduler( testlogger.New(t), metrics.NewClient(tally.NoopScope, metrics.Common, metrics.HistogramMigration{}), clock.NewMockedTimeSource(), realProcessor, &HierarchicalWeightedRoundRobinTaskPoolOptions[string, PriorityTask]{ BufferSize: 1000, TaskToWeightedKeysFn: func(task PriorityTask) []WeightedKey[string] { priority := task.Priority() // Create a simple hierarchy: group -> priority // Groups based on priority ranges with different weights var group string var groupWeight int if priority == 0 { group = "group0" groupWeight = 3 } else if priority == 1 { group = "group1" groupWeight = 2 } else { group = "group2" groupWeight = 1 } // Second level: individual priority priorityKey := string(rune('0' + priority)) priorityWeight := 3 - priority if priorityWeight < 1 { priorityWeight = 1 } return []WeightedKey[string]{ {Key: group, Weight: groupWeight}, {Key: priorityKey, Weight: priorityWeight}, } }, }, ) require.NoError(t, err) // Reuse the existing testSchedulerContract function testSchedulerContract(require.New(t), controller, scheduler, realProcessor) } ================================================ FILE: common/task/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/common/task package task import "github.com/uber/cadence/common" type ( // Processor is the generic coroutine pool interface // which process tasks Processor interface { common.Daemon Submit(task Task) error } // Scheduler is the generic interface for scheduling tasks with priority // and processing them Scheduler[T Task] interface { common.Daemon Submit(task T) error TrySubmit(task T) (bool, error) } // SchedulerType respresents the type of the task scheduler implementation SchedulerType int // State represents the current state of a task State int // Task is the interface for tasks Task interface { // Execute process this task Execute() error // HandleErr handle the error returned by Execute HandleErr(err error) error // RetryErr check whether to retry after HandleErr(Execute()) RetryErr(err error) bool // Ack marks the task as successful completed Ack() // Nack marks the task as unsuccessful completed Nack(err error) // Cancel marks the task as canceled Cancel() // State returns the current task state State() State } // PriorityTask is the interface for tasks which have and can be assigned a priority PriorityTask interface { Task // Priority returns the priority of the task, or noPriority if no priority was previously assigned Priority() int // SetPriority sets the priority of the task SetPriority(int) } // SequentialTaskQueueFactory is the function which generate a new SequentialTaskQueue // for a give SequentialTask SequentialTaskQueueFactory func(task Task) SequentialTaskQueue // SequentialTaskQueue is the generic task queue interface which group // sequential tasks to be executed one by one SequentialTaskQueue interface { // QueueID return the ID of the queue, as well as the tasks inside (same) QueueID() interface{} // Add push an task to the task set Add(task Task) // Remove pop an task from the task set Remove() Task // IsEmpty indicate if the task set is empty IsEmpty() bool // Len return the size of the queue Len() int } // Schedule represents a stateless schedule definition Schedule[V any] interface { // NewIterator creates a new stateful iterator for this schedule NewIterator() Iterator[V] // Len returns the length of the schedule Len() int } // Iterator represents a stateful iteration through a schedule Iterator[V any] interface { // Next returns the next value in the iteration // Returns (value, true) if available, (zero value, false) if exhausted TryNext() (V, bool) } ) const ( // SchedulerTypeFIFO is the scheduler type for FIFO scheduler implementation SchedulerTypeFIFO SchedulerType = iota + 1 // SchedulerTypeWRR is the scheduler type for weighted round robin scheduler implementation SchedulerTypeWRR ) const ( // TaskStatePending is the state for a task when it's waiting to be processed or currently being processed TaskStatePending State = iota + 1 // TaskStateAcked is the state for a task if it has been successfully completed TaskStateAcked // TaskStateCanceled is the state for a task if it has been canceled TaskStateCanceled ) ================================================ FILE: common/task/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package task -source interface.go -destination interface_mock.go -self_package github.com/uber/cadence/common/task // // Package task is a generated GoMock package. package task import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockProcessor is a mock of Processor interface. type MockProcessor struct { ctrl *gomock.Controller recorder *MockProcessorMockRecorder isgomock struct{} } // MockProcessorMockRecorder is the mock recorder for MockProcessor. type MockProcessorMockRecorder struct { mock *MockProcessor } // NewMockProcessor creates a new mock instance. func NewMockProcessor(ctrl *gomock.Controller) *MockProcessor { mock := &MockProcessor{ctrl: ctrl} mock.recorder = &MockProcessorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProcessor) EXPECT() *MockProcessorMockRecorder { return m.recorder } // Start mocks base method. func (m *MockProcessor) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockProcessorMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockProcessor)(nil).Start)) } // Stop mocks base method. func (m *MockProcessor) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockProcessorMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockProcessor)(nil).Stop)) } // Submit mocks base method. func (m *MockProcessor) Submit(task Task) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Submit", task) ret0, _ := ret[0].(error) return ret0 } // Submit indicates an expected call of Submit. func (mr *MockProcessorMockRecorder) Submit(task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Submit", reflect.TypeOf((*MockProcessor)(nil).Submit), task) } // MockScheduler is a mock of Scheduler interface. type MockScheduler[T Task] struct { ctrl *gomock.Controller recorder *MockSchedulerMockRecorder[T] isgomock struct{} } // MockSchedulerMockRecorder is the mock recorder for MockScheduler. type MockSchedulerMockRecorder[T Task] struct { mock *MockScheduler[T] } // NewMockScheduler creates a new mock instance. func NewMockScheduler[T Task](ctrl *gomock.Controller) *MockScheduler[T] { mock := &MockScheduler[T]{ctrl: ctrl} mock.recorder = &MockSchedulerMockRecorder[T]{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockScheduler[T]) EXPECT() *MockSchedulerMockRecorder[T] { return m.recorder } // Start mocks base method. func (m *MockScheduler[T]) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockSchedulerMockRecorder[T]) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockScheduler[T])(nil).Start)) } // Stop mocks base method. func (m *MockScheduler[T]) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockSchedulerMockRecorder[T]) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockScheduler[T])(nil).Stop)) } // Submit mocks base method. func (m *MockScheduler[T]) Submit(task T) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Submit", task) ret0, _ := ret[0].(error) return ret0 } // Submit indicates an expected call of Submit. func (mr *MockSchedulerMockRecorder[T]) Submit(task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Submit", reflect.TypeOf((*MockScheduler[T])(nil).Submit), task) } // TrySubmit mocks base method. func (m *MockScheduler[T]) TrySubmit(task T) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TrySubmit", task) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // TrySubmit indicates an expected call of TrySubmit. func (mr *MockSchedulerMockRecorder[T]) TrySubmit(task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TrySubmit", reflect.TypeOf((*MockScheduler[T])(nil).TrySubmit), task) } // MockTask is a mock of Task interface. type MockTask struct { ctrl *gomock.Controller recorder *MockTaskMockRecorder isgomock struct{} } // MockTaskMockRecorder is the mock recorder for MockTask. type MockTaskMockRecorder struct { mock *MockTask } // NewMockTask creates a new mock instance. func NewMockTask(ctrl *gomock.Controller) *MockTask { mock := &MockTask{ctrl: ctrl} mock.recorder = &MockTaskMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTask) EXPECT() *MockTaskMockRecorder { return m.recorder } // Ack mocks base method. func (m *MockTask) Ack() { m.ctrl.T.Helper() m.ctrl.Call(m, "Ack") } // Ack indicates an expected call of Ack. func (mr *MockTaskMockRecorder) Ack() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ack", reflect.TypeOf((*MockTask)(nil).Ack)) } // Cancel mocks base method. func (m *MockTask) Cancel() { m.ctrl.T.Helper() m.ctrl.Call(m, "Cancel") } // Cancel indicates an expected call of Cancel. func (mr *MockTaskMockRecorder) Cancel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cancel", reflect.TypeOf((*MockTask)(nil).Cancel)) } // Execute mocks base method. func (m *MockTask) Execute() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Execute") ret0, _ := ret[0].(error) return ret0 } // Execute indicates an expected call of Execute. func (mr *MockTaskMockRecorder) Execute() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockTask)(nil).Execute)) } // HandleErr mocks base method. func (m *MockTask) HandleErr(err error) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleErr", err) ret0, _ := ret[0].(error) return ret0 } // HandleErr indicates an expected call of HandleErr. func (mr *MockTaskMockRecorder) HandleErr(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleErr", reflect.TypeOf((*MockTask)(nil).HandleErr), err) } // Nack mocks base method. func (m *MockTask) Nack(err error) { m.ctrl.T.Helper() m.ctrl.Call(m, "Nack", err) } // Nack indicates an expected call of Nack. func (mr *MockTaskMockRecorder) Nack(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Nack", reflect.TypeOf((*MockTask)(nil).Nack), err) } // RetryErr mocks base method. func (m *MockTask) RetryErr(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RetryErr", err) ret0, _ := ret[0].(bool) return ret0 } // RetryErr indicates an expected call of RetryErr. func (mr *MockTaskMockRecorder) RetryErr(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetryErr", reflect.TypeOf((*MockTask)(nil).RetryErr), err) } // State mocks base method. func (m *MockTask) State() State { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "State") ret0, _ := ret[0].(State) return ret0 } // State indicates an expected call of State. func (mr *MockTaskMockRecorder) State() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "State", reflect.TypeOf((*MockTask)(nil).State)) } // MockPriorityTask is a mock of PriorityTask interface. type MockPriorityTask struct { ctrl *gomock.Controller recorder *MockPriorityTaskMockRecorder isgomock struct{} } // MockPriorityTaskMockRecorder is the mock recorder for MockPriorityTask. type MockPriorityTaskMockRecorder struct { mock *MockPriorityTask } // NewMockPriorityTask creates a new mock instance. func NewMockPriorityTask(ctrl *gomock.Controller) *MockPriorityTask { mock := &MockPriorityTask{ctrl: ctrl} mock.recorder = &MockPriorityTaskMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPriorityTask) EXPECT() *MockPriorityTaskMockRecorder { return m.recorder } // Ack mocks base method. func (m *MockPriorityTask) Ack() { m.ctrl.T.Helper() m.ctrl.Call(m, "Ack") } // Ack indicates an expected call of Ack. func (mr *MockPriorityTaskMockRecorder) Ack() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ack", reflect.TypeOf((*MockPriorityTask)(nil).Ack)) } // Cancel mocks base method. func (m *MockPriorityTask) Cancel() { m.ctrl.T.Helper() m.ctrl.Call(m, "Cancel") } // Cancel indicates an expected call of Cancel. func (mr *MockPriorityTaskMockRecorder) Cancel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cancel", reflect.TypeOf((*MockPriorityTask)(nil).Cancel)) } // Execute mocks base method. func (m *MockPriorityTask) Execute() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Execute") ret0, _ := ret[0].(error) return ret0 } // Execute indicates an expected call of Execute. func (mr *MockPriorityTaskMockRecorder) Execute() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockPriorityTask)(nil).Execute)) } // HandleErr mocks base method. func (m *MockPriorityTask) HandleErr(err error) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleErr", err) ret0, _ := ret[0].(error) return ret0 } // HandleErr indicates an expected call of HandleErr. func (mr *MockPriorityTaskMockRecorder) HandleErr(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleErr", reflect.TypeOf((*MockPriorityTask)(nil).HandleErr), err) } // Nack mocks base method. func (m *MockPriorityTask) Nack(err error) { m.ctrl.T.Helper() m.ctrl.Call(m, "Nack", err) } // Nack indicates an expected call of Nack. func (mr *MockPriorityTaskMockRecorder) Nack(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Nack", reflect.TypeOf((*MockPriorityTask)(nil).Nack), err) } // Priority mocks base method. func (m *MockPriorityTask) Priority() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Priority") ret0, _ := ret[0].(int) return ret0 } // Priority indicates an expected call of Priority. func (mr *MockPriorityTaskMockRecorder) Priority() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Priority", reflect.TypeOf((*MockPriorityTask)(nil).Priority)) } // RetryErr mocks base method. func (m *MockPriorityTask) RetryErr(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RetryErr", err) ret0, _ := ret[0].(bool) return ret0 } // RetryErr indicates an expected call of RetryErr. func (mr *MockPriorityTaskMockRecorder) RetryErr(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetryErr", reflect.TypeOf((*MockPriorityTask)(nil).RetryErr), err) } // SetPriority mocks base method. func (m *MockPriorityTask) SetPriority(arg0 int) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetPriority", arg0) } // SetPriority indicates an expected call of SetPriority. func (mr *MockPriorityTaskMockRecorder) SetPriority(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPriority", reflect.TypeOf((*MockPriorityTask)(nil).SetPriority), arg0) } // State mocks base method. func (m *MockPriorityTask) State() State { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "State") ret0, _ := ret[0].(State) return ret0 } // State indicates an expected call of State. func (mr *MockPriorityTaskMockRecorder) State() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "State", reflect.TypeOf((*MockPriorityTask)(nil).State)) } // MockSequentialTaskQueue is a mock of SequentialTaskQueue interface. type MockSequentialTaskQueue struct { ctrl *gomock.Controller recorder *MockSequentialTaskQueueMockRecorder isgomock struct{} } // MockSequentialTaskQueueMockRecorder is the mock recorder for MockSequentialTaskQueue. type MockSequentialTaskQueueMockRecorder struct { mock *MockSequentialTaskQueue } // NewMockSequentialTaskQueue creates a new mock instance. func NewMockSequentialTaskQueue(ctrl *gomock.Controller) *MockSequentialTaskQueue { mock := &MockSequentialTaskQueue{ctrl: ctrl} mock.recorder = &MockSequentialTaskQueueMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSequentialTaskQueue) EXPECT() *MockSequentialTaskQueueMockRecorder { return m.recorder } // Add mocks base method. func (m *MockSequentialTaskQueue) Add(task Task) { m.ctrl.T.Helper() m.ctrl.Call(m, "Add", task) } // Add indicates an expected call of Add. func (mr *MockSequentialTaskQueueMockRecorder) Add(task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockSequentialTaskQueue)(nil).Add), task) } // IsEmpty mocks base method. func (m *MockSequentialTaskQueue) IsEmpty() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsEmpty") ret0, _ := ret[0].(bool) return ret0 } // IsEmpty indicates an expected call of IsEmpty. func (mr *MockSequentialTaskQueueMockRecorder) IsEmpty() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsEmpty", reflect.TypeOf((*MockSequentialTaskQueue)(nil).IsEmpty)) } // Len mocks base method. func (m *MockSequentialTaskQueue) Len() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Len") ret0, _ := ret[0].(int) return ret0 } // Len indicates an expected call of Len. func (mr *MockSequentialTaskQueueMockRecorder) Len() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Len", reflect.TypeOf((*MockSequentialTaskQueue)(nil).Len)) } // QueueID mocks base method. func (m *MockSequentialTaskQueue) QueueID() any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueueID") ret0, _ := ret[0].(any) return ret0 } // QueueID indicates an expected call of QueueID. func (mr *MockSequentialTaskQueueMockRecorder) QueueID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueID", reflect.TypeOf((*MockSequentialTaskQueue)(nil).QueueID)) } // Remove mocks base method. func (m *MockSequentialTaskQueue) Remove() Task { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Remove") ret0, _ := ret[0].(Task) return ret0 } // Remove indicates an expected call of Remove. func (mr *MockSequentialTaskQueueMockRecorder) Remove() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockSequentialTaskQueue)(nil).Remove)) } // MockSchedule is a mock of Schedule interface. type MockSchedule[V any] struct { ctrl *gomock.Controller recorder *MockScheduleMockRecorder[V] isgomock struct{} } // MockScheduleMockRecorder is the mock recorder for MockSchedule. type MockScheduleMockRecorder[V any] struct { mock *MockSchedule[V] } // NewMockSchedule creates a new mock instance. func NewMockSchedule[V any](ctrl *gomock.Controller) *MockSchedule[V] { mock := &MockSchedule[V]{ctrl: ctrl} mock.recorder = &MockScheduleMockRecorder[V]{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSchedule[V]) EXPECT() *MockScheduleMockRecorder[V] { return m.recorder } // Len mocks base method. func (m *MockSchedule[V]) Len() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Len") ret0, _ := ret[0].(int) return ret0 } // Len indicates an expected call of Len. func (mr *MockScheduleMockRecorder[V]) Len() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Len", reflect.TypeOf((*MockSchedule[V])(nil).Len)) } // NewIterator mocks base method. func (m *MockSchedule[V]) NewIterator() Iterator[V] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewIterator") ret0, _ := ret[0].(Iterator[V]) return ret0 } // NewIterator indicates an expected call of NewIterator. func (mr *MockScheduleMockRecorder[V]) NewIterator() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewIterator", reflect.TypeOf((*MockSchedule[V])(nil).NewIterator)) } // MockIterator is a mock of Iterator interface. type MockIterator[V any] struct { ctrl *gomock.Controller recorder *MockIteratorMockRecorder[V] isgomock struct{} } // MockIteratorMockRecorder is the mock recorder for MockIterator. type MockIteratorMockRecorder[V any] struct { mock *MockIterator[V] } // NewMockIterator creates a new mock instance. func NewMockIterator[V any](ctrl *gomock.Controller) *MockIterator[V] { mock := &MockIterator[V]{ctrl: ctrl} mock.recorder = &MockIteratorMockRecorder[V]{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockIterator[V]) EXPECT() *MockIteratorMockRecorder[V] { return m.recorder } // TryNext mocks base method. func (m *MockIterator[V]) TryNext() (V, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TryNext") ret0, _ := ret[0].(V) ret1, _ := ret[1].(bool) return ret0, ret1 } // TryNext indicates an expected call of TryNext. func (mr *MockIteratorMockRecorder[V]) TryNext() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryNext", reflect.TypeOf((*MockIterator[V])(nil).TryNext)) } ================================================ FILE: common/task/iwrr_node.go ================================================ package task import ( "sync" "sync/atomic" "time" ) type ( // WeightedKey represents a key-weight pair used in hierarchical IWRR scheduling. // The key identifies a node at a specific level in the hierarchy, and the weight // determines the relative priority of that node in the IWRR schedule. WeightedKey[K comparable] struct { Key K // the identifier for this level in the hierarchy Weight int // the relative weight/priority for IWRR scheduling (higher weight = more frequent selection) } // iwrrNode represents a node in the hierarchical IWRR tree structure. // Each node can contain its own TTL channel for items and/or children nodes that form a subtree. // The tree structure allows for hierarchical weighted round-robin scheduling where items are // distributed across multiple levels based on their key paths and weights. iwrrNode[K comparable, V any] struct { sync.RWMutex // The following fields are protected by the node's RWMutex children map[K]weightedContainer[*iwrrNode[K, V]] // child nodes with their associated weights // The following fields are immutable after construction c *TTLChannel[V] // TTL channel for storing items at this level // The following fields are concurrency-safe atomic fields childrenItemCount atomic.Int64 // total number of items in all children's channels (across entire subtree) drainSelfFirst atomic.Bool // when true, this node's channel is drained before children; when false, children are checked first iwrrSchedule atomic.Pointer[iwrrSchedule[*iwrrNode[K, V]]] // atomic snapshot of all direct children for IWRR scheduling // The following fields are NOT concurrency-safe and must only be accessed by the single consumer goroutine iter Iterator[*iwrrNode[K, V]] // stateful iterator for traversing the IWRR schedule } ) // newiwrrNode creates a new iwrrNode with the specified buffer size for its TTL channel. // The node is initialized with an empty children map and an empty IWRR schedule. func newiwrrNode[K comparable, V any](bufferSize int) *iwrrNode[K, V] { node := &iwrrNode[K, V]{ c: NewTTLChannel[V](bufferSize), children: make(map[K]weightedContainer[*iwrrNode[K, V]]), } schedule := newIWRRSchedule[K, *iwrrNode[K, V]](nil) node.iwrrSchedule.Store(schedule) node.iter = schedule.NewIterator() return node } // executeAtPath recursively navigates or creates nodes along the hierarchical key path // and executes the callback function at the target (leaf) node. // // The method traverses the tree following the path, creating intermediate nodes as needed. // When a child node is created or its weight changes, the parent's IWRR schedule is updated. // After executing the callback, the drainSelfFirst flag is set to indicate children should // be drained first, and the childrenItemCount is updated with the delta from the callback. // // Note: The parent node is responsible for incrementing the reference count of the child node's channel, // so that the subtree is not cleaned up while the parent is trying to change the subtree structure. // // Parameters: // - path: slice of WeightedKey representing the hierarchical path to the target node // - bufferSize: buffer size for any newly created nodes // - callback: function to execute on the target node's channel, returns the change in item count // // Returns: the delta in item count from the callback execution // // Concurrency: This method is safe for concurrent access from multiple producer goroutines. func (n *iwrrNode[K, V]) executeAtPath(path []WeightedKey[K], bufferSize int, callback func(c *TTLChannel[V]) int64) int64 { if len(path) == 0 { n.drainSelfFirst.Store(false) delta := callback(n.c) return delta } key := path[0].Key weight := path[0].Weight needsUpdate := false n.RLock() if container, exists := n.children[key]; exists && container.weight == weight { // Increment reference count to prevent cleanup while we're using this child container.item.c.IncRef() defer container.item.c.DecRef() n.RUnlock() delta := container.item.executeAtPath(path[1:], bufferSize, callback) n.drainSelfFirst.Store(true) n.childrenItemCount.Add(delta) return delta } n.RUnlock() n.Lock() container, exists := n.children[key] if !exists { child := newiwrrNode[K, V](bufferSize) n.children[key] = weightedContainer[*iwrrNode[K, V]]{ item: child, weight: weight, } container = n.children[key] needsUpdate = true } else if container.weight != weight { n.children[key] = weightedContainer[*iwrrNode[K, V]]{ item: container.item, weight: weight, } container = n.children[key] needsUpdate = true } // Update this node's schedule on the way back if needed if needsUpdate { n.updateScheduleLocked() } // Increment reference count to prevent cleanup while we're using this child container.item.c.IncRef() defer container.item.c.DecRef() n.Unlock() // Recurse to execute at the leaf node delta := container.item.executeAtPath(path[1:], bufferSize, callback) n.drainSelfFirst.Store(true) n.childrenItemCount.Add(delta) return delta } // updateScheduleLocked updates the IWRR schedule for this node based on its current direct children. // The new schedule is atomically stored, allowing the consumer goroutine to pick it up when the // current iterator naturally exhausts and resets. // // Concurrency: Must be called with write lock held on this node. func (n *iwrrNode[K, V]) updateScheduleLocked() { schedule := newIWRRSchedule[K, *iwrrNode[K, V]](n.children) n.iwrrSchedule.Store(schedule) // The iterator will pick up the new schedule when it naturally exhausts and resets } // tryGetNextItem attempts to retrieve the next item from this subtree using IWRR scheduling. // The method respects the drainSelfFirst flag to determine priority: // - If drainSelfFirst is true: tries own channel first, then children // - If drainSelfFirst is false: tries children first, then own channel // // When traversing children, uses the IWRR iterator to select children according to their weights. // // Returns: the item and true if an item was successfully retrieved, zero value and false otherwise // // Concurrency: NOT safe for concurrent access. Must only be called by a single consumer goroutine. func (n *iwrrNode[K, V]) tryGetNextItem() (V, bool) { var zero V // Read drainSelfFirst flag drainSelf := n.drainSelfFirst.Load() // Check drainSelfFirst flag to determine order if drainSelf { if item, ok := n.tryOwnChannel(); ok { return item, true } } // Try children using IWRR iterator if item, ok := n.tryChildren(); ok { return item, true } // Try own channel if we haven't already if !drainSelf { if item, ok := n.tryOwnChannel(); ok { return item, true } } return zero, false } // tryOwnChannel attempts to retrieve an item from this node's own TTL channel. // Uses a non-blocking select to avoid waiting if the channel is empty. // // Returns: the item and true if an item was available, zero value and false if the channel is empty func (n *iwrrNode[K, V]) tryOwnChannel() (V, bool) { var zero V select { case item := <-n.c.Chan(): return item, true default: return zero, false } } // tryChildren attempts to retrieve an item from one of the child nodes using the IWRR iterator. // The method loops while childrenItemCount indicates items are available in the subtree. // If the iterator is exhausted but items remain, it resets the iterator with a fresh schedule snapshot. // When a child returns an item, the childrenItemCount is decremented to reflect the removal. // // This approach ensures: // - Children are selected according to their IWRR weights // - Schedule updates from producers are eventually picked up // - The method doesn't block indefinitely on empty children // // Returns: the item and true if an item was retrieved from a child, zero value and false otherwise func (n *iwrrNode[K, V]) tryChildren() (V, bool) { var zero V for n.childrenItemCount.Load() > 0 { child, ok := n.iter.TryNext() if !ok { // Iterator exhausted - check if any children have items if n.childrenItemCount.Load() == 0 { // No children have items, give up return zero, false } // Some child has items, reset iterator and try another round schedule := n.iwrrSchedule.Load() n.iter = schedule.NewIterator() continue } // Recursively try to get item from child item, ok := child.tryGetNextItem() if ok { // Got an item from child, decrement children count n.childrenItemCount.Add(-1) return item, true } // Child had no item, try next child } return zero, false } // cleanup recursively removes idle nodes from the tree based on the TTL. // The method performs a depth-first traversal, cleaning up children before checking itself. // // Process: // 1. Take a snapshot of children with read lock // 2. Release lock and recursively cleanup all children (allows concurrent executeAtPath) // 3. If any children should be removed, acquire write lock and check if the child still qualifies for deletion // - Child still exists and is the same instance (not replaced) // - Child has no children (no descendants were added since cleanup decision) // - Child's TTL channel still qualifies for cleanup (no tasks in own channel) // 4. Update the IWRR schedule if any children were removed // 5. If this node is now a leaf, check if its own channel should be cleaned up // // A leaf node is eligible for removal if its TTL channel has been idle beyond the TTL duration. // // Performance optimization: The parent lock is released during recursive cleanup of children, // reducing lock contention and allowing concurrent operations on the parent node. // // Returns: true if this node should be removed by its parent, false otherwise // // Concurrency: Safe for concurrent access. Uses read lock for snapshot, write lock only for modifications. func (n *iwrrNode[K, V]) cleanup(now time.Time, ttl time.Duration) bool { // Step 1: Take snapshot of children with read lock n.RLock() if len(n.children) == 0 { n.RUnlock() // Fast path: leaf node, check if should be cleaned up shouldRemove := n.c.ShouldCleanup(now, ttl) return shouldRemove } childrenSnapshot := make(map[K]*iwrrNode[K, V], len(n.children)) for key, container := range n.children { childrenSnapshot[key] = container.item } n.RUnlock() // Step 2: Cleanup children WITHOUT holding parent lock // This allows concurrent executeAtPath operations on the parent keysToRemove := make([]K, 0) for key, child := range childrenSnapshot { if child.cleanup(now, ttl) { keysToRemove = append(keysToRemove, key) } } // Step 3: Fast path if no children need removal if len(keysToRemove) == 0 { return false } // Step 4: Acquire write lock only when we need to modify n.Lock() defer n.Unlock() needsUpdate := false for _, key := range keysToRemove { // Verify the child still qualifies for deletion: // 1. Child still exists and is the same instance (not replaced) // 2. Child's TTL channel still qualifies for cleanup (no tasks in own channel, RefCount == 0) // If RefCount == 0, executeAtPath has fully returned, meaning any grandchildren it // created are already committed to child.children — making the hasNoChildren check // below accurate. // 3. Child has no children (no descendants were added since cleanup decision) // If no descendants exist, childrenItemCount must be 0 by definition // This protects against the race where executeAtPath adds tasks to descendants // between the cleanup decision and the actual deletion. container, exists := n.children[key] if !exists || container.item != childrenSnapshot[key] { continue } // Check ShouldCleanup first: if RefCount > 0, an executeAtPath is still in-flight // for this child and may be creating grandchildren, so skip deletion. if !container.item.c.ShouldCleanup(now, ttl) { continue } // ShouldCleanup passed (RefCount==0, Len==0, TTL expired). Take the read lock only to // guard against a concurrent cleanup goroutine removing grandchildren from this child // and confirm there are truly no descendants before deleting. container.item.RLock() hasNoChildren := len(container.item.children) == 0 container.item.RUnlock() if hasNoChildren { delete(n.children, key) needsUpdate = true } } // Update schedule if children were removed if needsUpdate { n.updateScheduleLocked() } // If the node became a leaf node, check if it should be removed if len(n.children) == 0 { return n.c.ShouldCleanup(now, ttl) } return false } ================================================ FILE: common/task/iwrr_node_test.go ================================================ package task import ( "fmt" "sync" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestIwrrNode_ExecuteAtPath(t *testing.T) { tests := []struct { name string setupFn func() (*iwrrNode[string, int], *iwrrNode[string, int]) // returns (root, expectedChild) path []WeightedKey[string] bufferSize int callbackValue int64 expectedDelta int64 verifyFn func(t *testing.T, root *iwrrNode[string, int], expectedChild *iwrrNode[string, int]) expectedChildCount int64 }{ { name: "empty_path_executes_on_current_node", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { return newiwrrNode[string, int](10), nil }, path: []WeightedKey[string]{}, bufferSize: 10, callbackValue: 1, expectedDelta: 1, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { assert.False(t, root.drainSelfFirst.Load(), "drainSelfFirst should be false after execution on self") }, expectedChildCount: 0, }, { name: "single_element_path_creates_child", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { return newiwrrNode[string, int](10), nil }, path: []WeightedKey[string]{ {Key: "child1", Weight: 3}, }, bufferSize: 10, callbackValue: 1, expectedDelta: 1, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { container := root.children["child1"] require.NotNil(t, container.item) assert.Equal(t, 3, container.weight) assert.True(t, root.drainSelfFirst.Load(), "root should have drainSelfFirst set") assert.False(t, container.item.drainSelfFirst.Load(), "target should not have drainSelfFirst set") }, expectedChildCount: 1, }, { name: "multi_element_path_creates_nested_children", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { return newiwrrNode[string, int](10), nil }, path: []WeightedKey[string]{ {Key: "level1", Weight: 5}, {Key: "level2", Weight: 3}, {Key: "level3", Weight: 2}, }, bufferSize: 10, callbackValue: 1, expectedDelta: 1, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { level1 := root.children["level1"] require.NotNil(t, level1.item) assert.Equal(t, 5, level1.weight) level2 := level1.item.children["level2"] require.NotNil(t, level2.item) assert.Equal(t, 3, level2.weight) level3 := level2.item.children["level3"] require.NotNil(t, level3.item) assert.Equal(t, 2, level3.weight) // Check childrenItemCount at each level assert.Equal(t, int64(1), root.childrenItemCount.Load()) assert.Equal(t, int64(1), level1.item.childrenItemCount.Load()) assert.Equal(t, int64(1), level2.item.childrenItemCount.Load()) }, expectedChildCount: 1, }, { name: "existing_path_with_same_weight_reuses_nodes", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { root := newiwrrNode[string, int](10) // Pre-create a 3-level path root.Lock() level1 := newiwrrNode[string, int](10) root.children["level1"] = weightedContainer[*iwrrNode[string, int]]{ item: level1, weight: 5, } root.updateScheduleLocked() root.Unlock() level1.Lock() level2 := newiwrrNode[string, int](10) level1.children["level2"] = weightedContainer[*iwrrNode[string, int]]{ item: level2, weight: 3, } level1.updateScheduleLocked() level1.Unlock() level2.Lock() level3 := newiwrrNode[string, int](10) level2.children["level3"] = weightedContainer[*iwrrNode[string, int]]{ item: level3, weight: 2, } level2.updateScheduleLocked() level2.Unlock() // Store the expected nodes by writing unique IDs to their channels level1.c.Chan() <- 111 level2.c.Chan() <- 222 level3.c.Chan() <- 333 return root, nil }, path: []WeightedKey[string]{ {Key: "level1", Weight: 5}, {Key: "level2", Weight: 3}, {Key: "level3", Weight: 2}, }, bufferSize: 10, callbackValue: 1, expectedDelta: 1, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { // Verify all three levels exist and have correct weights level1Container := root.children["level1"] require.NotNil(t, level1Container.item) assert.Equal(t, 5, level1Container.weight) // Check it's the same instance by reading the unique ID we stored id1, ok := <-level1Container.item.c.Chan() require.True(t, ok) assert.Equal(t, 111, id1, "level1 should be reused (same instance)") level2Container := level1Container.item.children["level2"] require.NotNil(t, level2Container.item) assert.Equal(t, 3, level2Container.weight) id2, ok := <-level2Container.item.c.Chan() require.True(t, ok) assert.Equal(t, 222, id2, "level2 should be reused (same instance)") level3Container := level2Container.item.children["level3"] require.NotNil(t, level3Container.item) assert.Equal(t, 2, level3Container.weight) id3, ok := <-level3Container.item.c.Chan() require.True(t, ok) assert.Equal(t, 333, id3, "level3 should be reused (same instance)") // Verify childrenItemCount at each level assert.Equal(t, int64(1), root.childrenItemCount.Load()) assert.Equal(t, int64(1), level1Container.item.childrenItemCount.Load()) assert.Equal(t, int64(1), level2Container.item.childrenItemCount.Load()) }, expectedChildCount: 1, }, { name: "existing_multi_level_path_with_weight_changes_reuses_nodes", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { root := newiwrrNode[string, int](10) // Pre-create a 3-level path with initial weights [5, 3, 2] root.Lock() level1 := newiwrrNode[string, int](10) root.children["level1"] = weightedContainer[*iwrrNode[string, int]]{ item: level1, weight: 5, } root.updateScheduleLocked() root.Unlock() level1.Lock() level2 := newiwrrNode[string, int](10) level1.children["level2"] = weightedContainer[*iwrrNode[string, int]]{ item: level2, weight: 3, } level1.updateScheduleLocked() level1.Unlock() level2.Lock() level3 := newiwrrNode[string, int](10) level2.children["level3"] = weightedContainer[*iwrrNode[string, int]]{ item: level3, weight: 2, } level2.updateScheduleLocked() level2.Unlock() // Store unique IDs to verify same instances level1.c.Chan() <- 111 level2.c.Chan() <- 222 level3.c.Chan() <- 333 return root, nil }, path: []WeightedKey[string]{ {Key: "level1", Weight: 10}, // Changed from 5 to 10 {Key: "level2", Weight: 7}, // Changed from 3 to 7 {Key: "level3", Weight: 4}, // Changed from 2 to 4 }, bufferSize: 10, callbackValue: 1, expectedDelta: 1, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { // Verify all three levels exist with UPDATED weights level1Container := root.children["level1"] require.NotNil(t, level1Container.item) assert.Equal(t, 10, level1Container.weight, "level1 weight should be updated to 10") // Check it's the same instance by reading the unique ID we stored id1, ok := <-level1Container.item.c.Chan() require.True(t, ok) assert.Equal(t, 111, id1, "level1 should be reused (same instance)") level2Container := level1Container.item.children["level2"] require.NotNil(t, level2Container.item) assert.Equal(t, 7, level2Container.weight, "level2 weight should be updated to 7") id2, ok := <-level2Container.item.c.Chan() require.True(t, ok) assert.Equal(t, 222, id2, "level2 should be reused (same instance)") level3Container := level2Container.item.children["level3"] require.NotNil(t, level3Container.item) assert.Equal(t, 4, level3Container.weight, "level3 weight should be updated to 4") id3, ok := <-level3Container.item.c.Chan() require.True(t, ok) assert.Equal(t, 333, id3, "level3 should be reused (same instance)") // Verify childrenItemCount at each level assert.Equal(t, int64(1), root.childrenItemCount.Load()) assert.Equal(t, int64(1), level1Container.item.childrenItemCount.Load()) assert.Equal(t, int64(1), level2Container.item.childrenItemCount.Load()) }, expectedChildCount: 1, }, { name: "weight_change_triggers_schedule_update", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { root := newiwrrNode[string, int](10) // Pre-create a child with weight 5 root.Lock() child := newiwrrNode[string, int](10) root.children["child1"] = weightedContainer[*iwrrNode[string, int]]{ item: child, weight: 5, } root.updateScheduleLocked() root.Unlock() return root, child }, path: []WeightedKey[string]{ {Key: "child1", Weight: 10}, // Different weight }, bufferSize: 10, callbackValue: 1, expectedDelta: 1, verifyFn: func(t *testing.T, root *iwrrNode[string, int], expectedChild *iwrrNode[string, int]) { container := root.children["child1"] require.NotNil(t, container.item) assert.Equal(t, 10, container.weight, "weight should be updated") // Should reuse the same child node even though weight changed assert.Same(t, expectedChild, container.item, "should reuse the same node instance") // Verify schedule was updated with new weight schedule := root.iwrrSchedule.Load() require.NotNil(t, schedule) // Schedule length should reflect the updated weight (10, not 5) assert.Equal(t, 10, schedule.Len(), "schedule length should reflect updated weight") }, expectedChildCount: 1, }, { name: "partial_path_exists_reuses_top_creates_bottom", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { root := newiwrrNode[string, int](10) // Only create level1, but not level2 or level3 root.Lock() level1 := newiwrrNode[string, int](10) root.children["level1"] = weightedContainer[*iwrrNode[string, int]]{ item: level1, weight: 5, } root.updateScheduleLocked() root.Unlock() // Store unique ID in level1 to verify it's reused level1.c.Chan() <- 111 return root, nil }, path: []WeightedKey[string]{ {Key: "level1", Weight: 5}, // Exists - should reuse {Key: "level2", Weight: 3}, // Doesn't exist - should create {Key: "level3", Weight: 2}, // Doesn't exist - should create }, bufferSize: 10, callbackValue: 1, expectedDelta: 1, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { // Verify level1 was reused level1Container := root.children["level1"] require.NotNil(t, level1Container.item) assert.Equal(t, 5, level1Container.weight) id1, ok := <-level1Container.item.c.Chan() require.True(t, ok) assert.Equal(t, 111, id1, "level1 should be reused (same instance)") // Verify level2 was created level2Container := level1Container.item.children["level2"] require.NotNil(t, level2Container.item) assert.Equal(t, 3, level2Container.weight) // Verify level3 was created level3Container := level2Container.item.children["level3"] require.NotNil(t, level3Container.item) assert.Equal(t, 2, level3Container.weight) // Verify childrenItemCount propagated correctly assert.Equal(t, int64(1), root.childrenItemCount.Load()) assert.Equal(t, int64(1), level1Container.item.childrenItemCount.Load()) assert.Equal(t, int64(1), level2Container.item.childrenItemCount.Load()) }, expectedChildCount: 1, }, { name: "partial_path_exists_with_weight_change", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { root := newiwrrNode[string, int](10) // Create level1 and level2, but not level3 root.Lock() level1 := newiwrrNode[string, int](10) root.children["level1"] = weightedContainer[*iwrrNode[string, int]]{ item: level1, weight: 5, } root.updateScheduleLocked() root.Unlock() level1.Lock() level2 := newiwrrNode[string, int](10) level1.children["level2"] = weightedContainer[*iwrrNode[string, int]]{ item: level2, weight: 3, } level1.updateScheduleLocked() level1.Unlock() // Store unique IDs to verify reuse level1.c.Chan() <- 111 level2.c.Chan() <- 222 return root, nil }, path: []WeightedKey[string]{ {Key: "level1", Weight: 10}, // Exists - reuse with updated weight {Key: "level2", Weight: 3}, // Exists - reuse with same weight {Key: "level3", Weight: 2}, // Doesn't exist - create }, bufferSize: 10, callbackValue: 1, expectedDelta: 1, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { // Verify level1 was reused with updated weight level1Container := root.children["level1"] require.NotNil(t, level1Container.item) assert.Equal(t, 10, level1Container.weight, "level1 weight should be updated") id1, ok := <-level1Container.item.c.Chan() require.True(t, ok) assert.Equal(t, 111, id1, "level1 should be reused (same instance)") // Verify level2 was reused with same weight level2Container := level1Container.item.children["level2"] require.NotNil(t, level2Container.item) assert.Equal(t, 3, level2Container.weight) id2, ok := <-level2Container.item.c.Chan() require.True(t, ok) assert.Equal(t, 222, id2, "level2 should be reused (same instance)") // Verify level3 was newly created level3Container := level2Container.item.children["level3"] require.NotNil(t, level3Container.item) assert.Equal(t, 2, level3Container.weight) // Verify childrenItemCount propagated correctly assert.Equal(t, int64(1), root.childrenItemCount.Load()) assert.Equal(t, int64(1), level1Container.item.childrenItemCount.Load()) assert.Equal(t, int64(1), level2Container.item.childrenItemCount.Load()) }, expectedChildCount: 1, }, { name: "multiple_children_at_same_level", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { return newiwrrNode[string, int](10), nil }, path: []WeightedKey[string]{ {Key: "child1", Weight: 3}, }, bufferSize: 10, callbackValue: 1, expectedDelta: 1, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { // Execute on another child to verify multiple children work delta2 := root.executeAtPath([]WeightedKey[string]{ {Key: "child2", Weight: 7}, }, 10, func(c *TTLChannel[int]) int64 { return 2 }) assert.Equal(t, int64(2), delta2) assert.Len(t, root.children, 2) assert.Equal(t, 3, root.children["child1"].weight) assert.Equal(t, 7, root.children["child2"].weight) assert.Equal(t, int64(3), root.childrenItemCount.Load(), "should have sum of deltas") }, expectedChildCount: 1, }, { name: "callback_delta_propagates_up", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { return newiwrrNode[string, int](10), nil }, path: []WeightedKey[string]{ {Key: "level1", Weight: 5}, {Key: "level2", Weight: 3}, }, bufferSize: 10, callbackValue: 42, expectedDelta: 42, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { assert.Equal(t, int64(42), root.childrenItemCount.Load()) assert.Equal(t, int64(42), root.children["level1"].item.childrenItemCount.Load()) }, expectedChildCount: 42, }, { name: "zero_delta_from_callback", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { return newiwrrNode[string, int](10), nil }, path: []WeightedKey[string]{ {Key: "child1", Weight: 3}, }, bufferSize: 10, callbackValue: 0, expectedDelta: 0, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { assert.Equal(t, int64(0), root.childrenItemCount.Load()) }, expectedChildCount: 0, }, { name: "negative_delta_from_callback", setupFn: func() (*iwrrNode[string, int], *iwrrNode[string, int]) { root := newiwrrNode[string, int](10) // Pre-populate some count root.childrenItemCount.Store(10) return root, nil }, path: []WeightedKey[string]{ {Key: "child1", Weight: 3}, }, bufferSize: 10, callbackValue: -5, expectedDelta: -5, verifyFn: func(t *testing.T, root *iwrrNode[string, int], _ *iwrrNode[string, int]) { assert.Equal(t, int64(5), root.childrenItemCount.Load(), "should subtract from existing count") }, expectedChildCount: 5, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { root, expectedChild := tt.setupFn() // Create callback that returns the expected value delta := root.executeAtPath(tt.path, tt.bufferSize, func(c *TTLChannel[int]) int64 { return tt.callbackValue }) assert.Equal(t, tt.expectedDelta, delta, "delta mismatch") assert.Equal(t, tt.expectedChildCount, root.childrenItemCount.Load(), "childrenItemCount mismatch") if tt.verifyFn != nil { tt.verifyFn(t, root, expectedChild) } }) } } func TestIwrrNode_ExecuteAtPath_ConcurrentAccess(t *testing.T) { root := newiwrrNode[int, int](100) // Concurrently execute on multi-level paths done := make(chan bool) for i := 0; i < 10; i++ { go func(id int) { for j := 0; j < 100; j++ { // Create 3-level paths with varying structures path := []WeightedKey[int]{ {Key: id % 3, Weight: (id % 3) + 1}, // level1: 3 different keys {Key: id % 5, Weight: (id % 5) + 1}, // level2: 5 different keys {Key: id, Weight: id + 1}, // level3: 10 different keys } root.executeAtPath(path, 10, func(c *TTLChannel[int]) int64 { // Just return delta without writing to channel // to avoid blocking when channel is full return 1 }) } done <- true }(i) } // Wait for all goroutines for i := 0; i < 10; i++ { <-done } // Verify tree structure level1Count := len(root.children) assert.LessOrEqual(t, level1Count, 3, "should have at most 3 level1 children") assert.GreaterOrEqual(t, level1Count, 1, "should have at least 1 level1 child") // Verify level2 and level3 exist for key1, container1 := range root.children { require.NotNil(t, container1.item, "level1 child %d should not be nil", key1) level2Count := len(container1.item.children) assert.LessOrEqual(t, level2Count, 5, "level1[%d] should have at most 5 level2 children", key1) assert.GreaterOrEqual(t, level2Count, 1, "level1[%d] should have at least 1 level2 child", key1) for key2, container2 := range container1.item.children { require.NotNil(t, container2.item, "level2 child %d->%d should not be nil", key1, key2) level3Count := len(container2.item.children) assert.GreaterOrEqual(t, level3Count, 1, "level2[%d][%d] should have at least 1 level3 child", key1, key2) } } // Total items should be 10 * 100 = 1000 assert.Equal(t, int64(1000), root.childrenItemCount.Load()) } func TestIwrrNode_Cleanup(t *testing.T) { baseTime := time.Unix(1000, 0) ttl := 10 * time.Second tests := []struct { name string setupFn func() *iwrrNode[string, int] now time.Time ttl time.Duration expectedReturn bool verifyFn func(t *testing.T, root *iwrrNode[string, int]) }{ { name: "leaf_node_should_be_cleaned_up", setupFn: func() *iwrrNode[string, int] { node := newiwrrNode[string, int](10) // Set last write time to old time node.c.UpdateLastWriteTime(baseTime) // Ensure refCount is 0 and channel is empty return node }, now: baseTime.Add(ttl + time.Second), // Past TTL ttl: ttl, expectedReturn: true, verifyFn: func(t *testing.T, root *iwrrNode[string, int]) { assert.Empty(t, root.children, "should have no children") }, }, { name: "leaf_node_should_not_be_cleaned_up_not_expired", setupFn: func() *iwrrNode[string, int] { node := newiwrrNode[string, int](10) node.c.UpdateLastWriteTime(baseTime) return node }, now: baseTime.Add(ttl - time.Second), // Before TTL ttl: ttl, expectedReturn: false, verifyFn: func(t *testing.T, root *iwrrNode[string, int]) { assert.Empty(t, root.children, "should have no children") }, }, { name: "leaf_node_should_not_be_cleaned_up_has_refs", setupFn: func() *iwrrNode[string, int] { node := newiwrrNode[string, int](10) node.c.UpdateLastWriteTime(baseTime) node.c.IncRef() // Add reference return node }, now: baseTime.Add(ttl + time.Second), // Past TTL ttl: ttl, expectedReturn: false, verifyFn: func(t *testing.T, root *iwrrNode[string, int]) { assert.Equal(t, int32(1), root.c.RefCount()) }, }, { name: "leaf_node_should_not_be_cleaned_up_has_data", setupFn: func() *iwrrNode[string, int] { node := newiwrrNode[string, int](10) node.c.UpdateLastWriteTime(baseTime) node.c.Chan() <- 42 // Add data to channel return node }, now: baseTime.Add(ttl + time.Second), // Past TTL ttl: ttl, expectedReturn: false, verifyFn: func(t *testing.T, root *iwrrNode[string, int]) { assert.Equal(t, 1, root.c.Len()) }, }, { name: "internal_node_with_active_children_should_not_be_cleaned_up", setupFn: func() *iwrrNode[string, int] { root := newiwrrNode[string, int](10) root.Lock() child := newiwrrNode[string, int](10) child.c.UpdateLastWriteTime(baseTime) // Recent write root.children["child1"] = weightedContainer[*iwrrNode[string, int]]{ item: child, weight: 5, } root.updateScheduleLocked() root.Unlock() return root }, now: baseTime.Add(ttl - time.Second), // Before TTL ttl: ttl, expectedReturn: false, verifyFn: func(t *testing.T, root *iwrrNode[string, int]) { assert.Len(t, root.children, 1, "child should not be removed") _, exists := root.children["child1"] assert.True(t, exists, "child should remain") }, }, { name: "internal_node_removes_expired_children", setupFn: func() *iwrrNode[string, int] { root := newiwrrNode[string, int](10) root.Lock() // Add two children: one expired, one active expiredChild := newiwrrNode[string, int](10) expiredChild.c.UpdateLastWriteTime(baseTime.Add(-2 * ttl)) // Way past TTL root.children["expired"] = weightedContainer[*iwrrNode[string, int]]{ item: expiredChild, weight: 3, } activeChild := newiwrrNode[string, int](10) activeChild.c.UpdateLastWriteTime(baseTime.Add(ttl + time.Second)) // Before TTL root.children["active"] = weightedContainer[*iwrrNode[string, int]]{ item: activeChild, weight: 5, } root.updateScheduleLocked() root.Unlock() return root }, now: baseTime.Add(ttl + time.Second), ttl: ttl, expectedReturn: false, verifyFn: func(t *testing.T, root *iwrrNode[string, int]) { assert.Len(t, root.children, 1, "should have 1 child remaining") _, exists := root.children["active"] assert.True(t, exists, "active child should remain") _, exists = root.children["expired"] assert.False(t, exists, "expired child should be removed") }, }, { name: "internal_node_removes_all_expired_children", setupFn: func() *iwrrNode[string, int] { root := newiwrrNode[string, int](10) root.Lock() // Add multiple expired children for i := 1; i <= 3; i++ { child := newiwrrNode[string, int](10) child.c.UpdateLastWriteTime(baseTime.Add(-ttl - time.Second)) root.children[string(rune('a'+i-1))] = weightedContainer[*iwrrNode[string, int]]{ item: child, weight: i, } } root.updateScheduleLocked() root.Unlock() return root }, now: baseTime.Add(ttl + time.Second), ttl: ttl, expectedReturn: true, // All children removed, so node should be removed verifyFn: func(t *testing.T, root *iwrrNode[string, int]) { assert.Empty(t, root.children, "all children should be removed") }, }, { name: "schedule_updated_when_children_removed", setupFn: func() *iwrrNode[string, int] { root := newiwrrNode[string, int](10) root.Lock() // Add two children with different weights child1 := newiwrrNode[string, int](10) child1.c.UpdateLastWriteTime(baseTime.Add(-ttl - time.Second)) // Expired root.children["expired"] = weightedContainer[*iwrrNode[string, int]]{ item: child1, weight: 5, } child2 := newiwrrNode[string, int](10) child2.c.UpdateLastWriteTime(baseTime.Add(ttl + time.Second)) // Active root.children["active"] = weightedContainer[*iwrrNode[string, int]]{ item: child2, weight: 3, } root.updateScheduleLocked() root.Unlock() return root }, now: baseTime.Add(ttl + time.Second), ttl: ttl, expectedReturn: false, verifyFn: func(t *testing.T, root *iwrrNode[string, int]) { assert.Len(t, root.children, 1) _, exists := root.children["expired"] assert.False(t, exists, "expired child should be removed") _, exists = root.children["active"] assert.True(t, exists, "active child should remain") // New schedule should only have weight 3 newSchedule := root.iwrrSchedule.Load() assert.Equal(t, 3, newSchedule.Len(), "updated schedule should only have weight 3") }, }, { name: "multi_level_recursive_cleanup", setupFn: func() *iwrrNode[string, int] { root := newiwrrNode[string, int](10) // Create 3-level tree: root -> level1 -> level2 -> level3 root.Lock() level1 := newiwrrNode[string, int](10) level1.c.UpdateLastWriteTime(baseTime.Add(-ttl - time.Second)) // Expired root.children["expired"] = weightedContainer[*iwrrNode[string, int]]{ item: level1, weight: 5, } root.updateScheduleLocked() root.Unlock() level1.Lock() level2 := newiwrrNode[string, int](10) level2.c.UpdateLastWriteTime(baseTime.Add(-ttl - time.Second)) // Expired level1.children["expired"] = weightedContainer[*iwrrNode[string, int]]{ item: level2, weight: 3, } level1.updateScheduleLocked() level1.Unlock() level2.Lock() level3 := newiwrrNode[string, int](10) level3.c.UpdateLastWriteTime(baseTime.Add(-ttl - time.Second)) // Expired level2.children["level3"] = weightedContainer[*iwrrNode[string, int]]{ item: level3, weight: 2, } level2.updateScheduleLocked() level2.Unlock() return root }, now: baseTime.Add(ttl + time.Second), ttl: ttl, expectedReturn: true, // All should cascade up and be removed verifyFn: func(t *testing.T, root *iwrrNode[string, int]) { assert.Empty(t, root.children, "all children should be recursively removed") }, }, { name: "multi_level_partial_cleanup", setupFn: func() *iwrrNode[string, int] { root := newiwrrNode[string, int](10) // Create tree where one branch is expired, another is active root.Lock() expiredBranch := newiwrrNode[string, int](10) expiredBranch.c.UpdateLastWriteTime(baseTime.Add(-2 * ttl)) // Way past TTL root.children["expired"] = weightedContainer[*iwrrNode[string, int]]{ item: expiredBranch, weight: 3, } activeBranch := newiwrrNode[string, int](10) root.children["active"] = weightedContainer[*iwrrNode[string, int]]{ item: activeBranch, weight: 5, } root.updateScheduleLocked() root.Unlock() // Add active child to active branch activeBranch.Lock() activeLeaf := newiwrrNode[string, int](10) activeLeaf.c.UpdateLastWriteTime(baseTime) // Recent write activeLeaf.c.IncRef() // Keep it active activeBranch.children["leaf"] = weightedContainer[*iwrrNode[string, int]]{ item: activeLeaf, weight: 2, } activeBranch.updateScheduleLocked() activeBranch.Unlock() return root }, now: baseTime.Add(ttl + time.Second), ttl: ttl, expectedReturn: false, verifyFn: func(t *testing.T, root *iwrrNode[string, int]) { assert.Len(t, root.children, 1, "only active branch should remain") _, exists := root.children["active"] assert.True(t, exists, "active branch should remain") activeBranch := root.children["active"].item assert.Len(t, activeBranch.children, 1, "active leaf should remain") }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { root := tt.setupFn() shouldRemove := root.cleanup(tt.now, tt.ttl) assert.Equal(t, tt.expectedReturn, shouldRemove, "cleanup return value mismatch") if tt.verifyFn != nil { tt.verifyFn(t, root) } }) } } func TestIwrrNode_Cleanup_ConcurrentCleanup(t *testing.T) { // Test that multiple concurrent cleanup calls don't cause race conditions root := newiwrrNode[string, int](10) // Create a tree with multiple levels and old timestamps // root -> level1a -> level2a // -> level1b -> level2b // -> level1c -> level2c oldTime := time.Now().Add(-2 * time.Hour) // Old enough to be cleaned up for i := 0; i < 3; i++ { path := []WeightedKey[string]{ {Key: fmt.Sprintf("level1_%d", i), Weight: 1}, {Key: fmt.Sprintf("level2_%d", i), Weight: 1}, } root.executeAtPath(path, 10, func(c *TTLChannel[int]) int64 { c.UpdateLastWriteTime(oldTime) // Set old timestamp return 0 // Don't add items, just create the tree structure }) } // Verify initial state require.Equal(t, 3, len(root.children)) // Run multiple concurrent cleanup operations now := time.Now() ttl := time.Hour var wg sync.WaitGroup numGoroutines := 10 for i := 0; i < numGoroutines; i++ { wg.Add(1) go func() { defer wg.Done() root.cleanup(now, ttl) }() } wg.Wait() // Verify tree was cleaned up correctly // All nodes should be removed since they're idle assert.Equal(t, 0, len(root.children)) } func TestIwrrNode_Cleanup_ConcurrentWithExecuteAtPath(t *testing.T) { // Test that cleanup and executeAtPath can run concurrently without deadlock or corruption // Uses multi-level hierarchy with multiple goroutines for both operations root := newiwrrNode[string, int](100) var wg sync.WaitGroup stopCh := make(chan struct{}) var tasksAdded atomic.Int64 // Multiple goroutines running executeAtPath with multi-level paths numExecuteGoroutines := 10 for g := 0; g < numExecuteGoroutines; g++ { wg.Add(1) go func(goroutineID int) { defer wg.Done() counter := 0 for { select { case <-stopCh: return default: // Create multi-level paths: domain -> tasklist -> tenant domainKey := fmt.Sprintf("domain_%d", (goroutineID+counter)%3) tasklistKey := fmt.Sprintf("tasklist_%d", (goroutineID+counter)%5) tenantKey := fmt.Sprintf("tenant_%d", (goroutineID+counter)%7) path := []WeightedKey[string]{ {Key: domainKey, Weight: ((goroutineID + counter) % 3) + 1}, {Key: tasklistKey, Weight: ((goroutineID + counter) % 5) + 1}, {Key: tenantKey, Weight: ((goroutineID + counter) % 7) + 1}, } taskValue := goroutineID*10000 + counter root.executeAtPath(path, 10, func(c *TTLChannel[int]) int64 { select { case c.Chan() <- taskValue: c.UpdateLastWriteTime(time.Now()) tasksAdded.Add(1) return 1 default: return 0 } }) counter++ } } }(g) } // Multiple goroutines running cleanup numCleanupGoroutines := 5 for g := 0; g < numCleanupGoroutines; g++ { wg.Add(1) go func() { defer wg.Done() for { select { case <-stopCh: return default: // Cleanup with a TTL that will remove idle nodes now := time.Now() ttl := 50 * time.Millisecond root.cleanup(now, ttl) time.Sleep(time.Millisecond) } } }() } // Let them run concurrently for a short time time.Sleep(50 * time.Millisecond) close(stopCh) wg.Wait() // Drain all tasks and verify count drainedCount := 0 for { if _, ok := root.tryGetNextItem(); ok { drainedCount++ } else { break } } // All added items should be drained assert.Equal(t, int(tasksAdded.Load()), drainedCount, "All enqueued items should be dequeuable") } func TestIwrrNode_Cleanup_PointerEqualityCheck(t *testing.T) { // Test that cleanup only removes the child it decided to remove, // not a newly created child with the same key root := newiwrrNode[string, int](10) // Add an initial child that will be cleaned up path := []WeightedKey[string]{ {Key: "child", Weight: 1}, } root.executeAtPath(path, 10, func(c *TTLChannel[int]) int64 { c.UpdateLastWriteTime(time.Now().Add(-2 * time.Hour)) // Old timestamp return 0 }) // Start cleanup in background (it will snapshot, then recurse, then try to delete) cleanupDone := make(chan bool) go func() { // Add a small delay to ensure executeAtPath runs during the recursive phase now := time.Now() ttl := time.Hour root.cleanup(now, ttl) cleanupDone <- true }() // While cleanup is running, add a new child with the same key // This simulates the race condition the pointer equality check protects against time.Sleep(time.Millisecond) // Give cleanup time to snapshot path2 := []WeightedKey[string]{ {Key: "child", Weight: 2}, // Same key, different weight } root.executeAtPath(path2, 10, func(c *TTLChannel[int]) int64 { c.UpdateLastWriteTime(time.Now()) return 0 }) <-cleanupDone // Verify: the new child should still exist root.RLock() container, exists := root.children["child"] root.RUnlock() // The child should exist (either old or new depending on timing) // If it's the new child, weight should be 2 // If it's the old child, weight should be 1 // Either way, the tree should be in a consistent state if exists { // If child exists, verify it's a valid node assert.NotNil(t, container.item) assert.Contains(t, []int{1, 2}, container.weight) } } func TestIwrrNode_Cleanup_DeepHierarchyConcurrency(t *testing.T) { // Test cleanup performance with deep hierarchy and concurrent operations // Verifies all tasks can be drained even with concurrent cleanup root := newiwrrNode[string, int](10) // Track tasks added var tasksAdded atomic.Int64 // Create a deep hierarchy (5 levels, 3 children per level = 243 leaf paths) var createPaths func(depth int, prefix string) [][]WeightedKey[string] createPaths = func(depth int, prefix string) [][]WeightedKey[string] { if depth == 0 { return [][]WeightedKey[string]{{}} } var paths [][]WeightedKey[string] for i := 0; i < 3; i++ { key := fmt.Sprintf("%s_L%d_C%d", prefix, 5-depth, i) subPaths := createPaths(depth-1, prefix) for _, subPath := range subPaths { path := append([]WeightedKey[string]{{Key: key, Weight: i + 1}}, subPath...) paths = append(paths, path) } } return paths } paths := createPaths(5, "node") for i, path := range paths { root.executeAtPath(path, 10, func(c *TTLChannel[int]) int64 { select { case c.Chan() <- i: c.UpdateLastWriteTime(time.Now()) tasksAdded.Add(1) return 1 default: return 0 } }) } // Verify tree was created require.Equal(t, 3, len(root.children)) var wg sync.WaitGroup // Start multiple cleanup operations concurrently for i := 0; i < 5; i++ { wg.Add(1) go func() { defer wg.Done() now := time.Now().Add(2 * time.Hour) ttl := time.Hour root.cleanup(now, ttl) }() } // Also start some executeAtPath operations to add new nodes/tasks for i := 0; i < 5; i++ { wg.Add(1) go func(idx int) { defer wg.Done() path := []WeightedKey[string]{ {Key: fmt.Sprintf("new_L0_C%d", idx), Weight: 1}, } root.executeAtPath(path, 10, func(c *TTLChannel[int]) int64 { select { case c.Chan() <- idx + 1000: c.UpdateLastWriteTime(time.Now()) tasksAdded.Add(1) return 1 default: return 0 } }) }(i) } wg.Wait() // Drain all remaining tasks to verify nothing was lost drainedCount := 0 for { if _, ok := root.tryGetNextItem(); ok { drainedCount++ } else { break } } totalAdded := int(tasksAdded.Load()) assert.Equal(t, totalAdded, drainedCount, "All added tasks should be dequeuable") } ================================================ FILE: common/task/iwrr_schedule.go ================================================ package task import "slices" var _ Schedule[any] = &iwrrSchedule[any]{} // weightedContainer is a container for an item with a weight type weightedContainer[V any] struct { weight int item V } // iwrrSchedule implements Schedule using an efficient interleaved weighted round-robin algorithm // ref: https://en.wikipedia.org/wiki/Weighted_round_robin#Interleaved_WRR // It is stateless and creates iterators on demand type iwrrSchedule[V any] struct { // Snapshot of items sorted by weight (ascending) items []weightedContainer[V] // Maximum weight among all items maxWeight int // Total virtual length (sum of all weights) totalLen int } // iwrrIterator is a stateful iterator that tracks position in the schedule type iwrrIterator[V any] struct { schedule *iwrrSchedule[V] currentRound int // Current round (maxWeight-1 down to 0) currentIndex int // Index within current round's qualifying items } // newIWRRSchedule creates a new IWRR schedule from a snapshot of weighted containers // Items with weight <= 0 are ignored func newIWRRSchedule[K comparable, V any](items map[K]weightedContainer[V]) *iwrrSchedule[V] { if len(items) == 0 { return &iwrrSchedule[V]{} } // Filter out items with weight <= 0 and copy to slice itemsCopy := make([]weightedContainer[V], 0, len(items)) totalLen := 0 for _, container := range items { if container.weight > 0 { itemsCopy = append(itemsCopy, container) totalLen += container.weight } } // Return empty schedule if no valid items if len(itemsCopy) == 0 { return &iwrrSchedule[V]{} } // Sort by weight (ascending) slices.SortFunc(itemsCopy, func(a, b weightedContainer[V]) int { return a.weight - b.weight }) maxWeight := itemsCopy[len(itemsCopy)-1].weight return &iwrrSchedule[V]{ items: itemsCopy, maxWeight: maxWeight, totalLen: totalLen, } } // NewIterator creates a new stateful iterator for this schedule func (s *iwrrSchedule[V]) NewIterator() Iterator[V] { if len(s.items) == 0 { return &iwrrIterator[V]{schedule: s} } return &iwrrIterator[V]{ schedule: s, currentRound: s.maxWeight - 1, currentIndex: len(s.items) - 1, } } // Len returns the total virtual length of the schedule func (s *iwrrSchedule[V]) Len() int { return s.totalLen } // TryNext returns the next item in the IWRR iteration // The algorithm processes rounds from maxWeight-1 down to 0 // In each round r, items with weight > r are included // Returns false when the iteration is exhausted (all rounds completed) func (it *iwrrIterator[V]) TryNext() (zero V, ok bool) { if it.schedule == nil || len(it.schedule.items) == 0 { return } // Find the next qualifying item for it.currentRound >= 0 { // Find items that qualify for current round (weight > round) // We iterate from highest weight to lowest for it.currentIndex >= 0 && it.schedule.items[it.currentIndex].weight > it.currentRound { item := it.schedule.items[it.currentIndex].item it.currentIndex-- return item, true } // Move to next round it.currentRound-- it.currentIndex = len(it.schedule.items) - 1 } return } ================================================ FILE: common/task/iwrr_schedule_test.go ================================================ package task import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // testWeightedItem is a simple implementation for testing type testWeightedItem struct { id int } // toWeightedMap converts a map of items with weights to a map of weightedContainers func toWeightedMap(items map[int]*testWeightedItem, weights map[int]int) map[int]weightedContainer[*testWeightedItem] { result := make(map[int]weightedContainer[*testWeightedItem]) for k, item := range items { result[k] = weightedContainer[*testWeightedItem]{ item: item, weight: weights[k], } } return result } func TestIWRRSchedule_Empty(t *testing.T) { schedule := newIWRRSchedule[int, *testWeightedItem](nil) assert.Equal(t, 0, schedule.Len()) iter := schedule.NewIterator() item, ok := iter.TryNext() assert.False(t, ok) assert.Nil(t, item) } func TestIWRRSchedule_SingleChannel(t *testing.T) { items := map[int]*testWeightedItem{ 0: {id: 0}, } weights := map[int]int{ 0: 3, } schedule := newIWRRSchedule[int, *testWeightedItem](toWeightedMap(items, weights)) // Total length should be the weight assert.Equal(t, 3, schedule.Len()) iter := schedule.NewIterator() // Should return the item 3 times for i := 0; i < 3; i++ { item, ok := iter.TryNext() assert.True(t, ok, "iteration %d should succeed", i) assert.Equal(t, items[0], item) } // Fourth call should return false (exhausted) item, ok := iter.TryNext() assert.False(t, ok) assert.Nil(t, item) } func TestIWRRSchedule_MultipleChannels_EqualWeights(t *testing.T) { items := map[int]*testWeightedItem{ 0: {id: 0}, 1: {id: 1}, 2: {id: 2}, } weights := map[int]int{ 0: 2, 1: 2, 2: 2, } schedule := newIWRRSchedule[int, *testWeightedItem](toWeightedMap(items, weights)) assert.Equal(t, 6, schedule.Len()) iter := schedule.NewIterator() // With equal weights, each item should appear equal number of times // Note: the exact order depends on map iteration order counts := make(map[*testWeightedItem]int) for { item, ok := iter.TryNext() if !ok { break } counts[item]++ } assert.Equal(t, 2, counts[items[0]], "item 0 should appear 2 times") assert.Equal(t, 2, counts[items[1]], "item 1 should appear 2 times") assert.Equal(t, 2, counts[items[2]], "item 2 should appear 2 times") } func TestIWRRSchedule_MultipleChannels_DifferentWeights(t *testing.T) { // Create items with weights [1, 2, 3] items := map[int]*testWeightedItem{ 0: {id: 0}, 1: {id: 1}, 2: {id: 2}, } weights := map[int]int{ 0: 1, 1: 2, 2: 3, } schedule := newIWRRSchedule[int, *testWeightedItem](toWeightedMap(items, weights)) assert.Equal(t, 6, schedule.Len()) iter := schedule.NewIterator() // IWRR with weights [1, 2, 3] processes rounds from 2 down to 0: // Round 2: items with weight > 2 → item 2 (weight 3) // Round 1: items with weight > 1 → items 2, 1 (weights 3, 2) // Round 0: items with weight > 0 → items 2, 1, 0 (weights 3, 2, 1) // Result: [2, 2, 1, 2, 1, 0] expectedSequence := []*testWeightedItem{ items[2], // round 2: weight 3 items[2], // round 1: weight 3 items[1], // round 1: weight 2 items[2], // round 0: weight 3 items[1], // round 0: weight 2 items[0], // round 0: weight 1 } for i, expected := range expectedSequence { item, ok := iter.TryNext() require.True(t, ok, "iteration %d should succeed", i) assert.Equal(t, expected, item, "iteration %d", i) } // Should be exhausted item, ok := iter.TryNext() assert.False(t, ok) assert.Nil(t, item) } func TestIWRRSchedule_LargeWeights(t *testing.T) { items := map[int]*testWeightedItem{ 0: {id: 0}, 1: {id: 1}, 2: {id: 2}, } weights := map[int]int{ 0: 100, 1: 50, 2: 25, } schedule := newIWRRSchedule[int, *testWeightedItem](toWeightedMap(items, weights)) assert.Equal(t, 175, schedule.Len()) iter := schedule.NewIterator() // IWRR pattern for [100, 50, 25]: // Rounds 99-50 (50 rounds): only item 0 (weight 100 > round) // Rounds 49-25 (25 rounds): items 0, 1 (weights 100, 50 > round) // Rounds 24-0 (25 rounds): items 0, 1, 2 (all weights > round) var expectedSequence []*testWeightedItem // First 50 rounds: only item 0 for i := 0; i < 50; i++ { expectedSequence = append(expectedSequence, items[0]) } // Next 25 rounds: items 0, 1 for i := 0; i < 25; i++ { expectedSequence = append(expectedSequence, items[0], items[1]) } // Last 25 rounds: items 0, 1, 2 for i := 0; i < 25; i++ { expectedSequence = append(expectedSequence, items[0], items[1], items[2]) } // Verify the sequence for i, expected := range expectedSequence { item, ok := iter.TryNext() require.True(t, ok, "iteration %d should succeed", i) assert.Equal(t, expected, item, "iteration %d", i) } // Should be exhausted item, ok := iter.TryNext() assert.False(t, ok) assert.Nil(t, item) } func TestIWRRSchedule_ChannelWithZeroWeight(t *testing.T) { items := map[int]*testWeightedItem{ 0: {id: 0}, 1: {id: 1}, } weights := map[int]int{ 0: 0, 1: 3, } schedule := newIWRRSchedule[int, *testWeightedItem](toWeightedMap(items, weights)) // Total length should only count non-zero weights assert.Equal(t, 3, schedule.Len()) iter := schedule.NewIterator() // Should only return item with weight 3 for i := 0; i < 3; i++ { item, ok := iter.TryNext() require.True(t, ok, "iteration %d", i) assert.Equal(t, items[1], item) } // Should be exhausted item, ok := iter.TryNext() assert.False(t, ok) assert.Nil(t, item) } func TestIWRRSchedule_WeightedChannelFields(t *testing.T) { // Verify that the returned item is correct testItem := &testWeightedItem{ id: 0, } items := map[int]*testWeightedItem{ 0: testItem, } weights := map[int]int{ 0: 10, } schedule := newIWRRSchedule[int, *testWeightedItem](toWeightedMap(items, weights)) iter := schedule.NewIterator() item, ok := iter.TryNext() require.True(t, ok) assert.Equal(t, testItem, item) } func TestIWRRSchedule_ExhaustedSchedule_MultipleCallsReturnFalse(t *testing.T) { items := map[int]*testWeightedItem{ 0: {id: 0}, } weights := map[int]int{ 0: 1, } schedule := newIWRRSchedule[int, *testWeightedItem](toWeightedMap(items, weights)) iter := schedule.NewIterator() // Exhaust the iterator item, ok := iter.TryNext() assert.True(t, ok) assert.NotNil(t, item) // Multiple calls after exhaustion should all return false for i := 0; i < 5; i++ { item, ok := iter.TryNext() assert.False(t, ok, "call %d after exhaustion", i) assert.Nil(t, item, "call %d after exhaustion", i) } } func TestIWRRSchedule_Ordering_Weights_5_3_1(t *testing.T) { // Test case from task pool tests: weights [5, 3, 1] items := map[int]*testWeightedItem{ 0: {id: 0}, 1: {id: 1}, 2: {id: 2}, } weights := map[int]int{ 0: 5, 1: 3, 2: 1, } schedule := newIWRRSchedule[int, *testWeightedItem](toWeightedMap(items, weights)) assert.Equal(t, 9, schedule.Len()) iter := schedule.NewIterator() // IWRR pattern for [5, 3, 1]: // Round 4: [0] (weight 5 > 4) // Round 3: [0] (weight 5 > 3) // Round 2: [0, 1] (weights 5,3 > 2) // Round 1: [0, 1] (weights 5,3 > 1) // Round 0: [0, 1, 2] (weights 5,3,1 > 0) // Result: [0, 0, 0, 1, 0, 1, 0, 1, 2] expectedPattern := []int{0, 0, 0, 1, 0, 1, 0, 1, 2} for i, expectedIdx := range expectedPattern { item, ok := iter.TryNext() require.True(t, ok, "iteration %d", i) assert.Equal(t, items[expectedIdx], item, "iteration %d", i) } // Exhausted item, ok := iter.TryNext() assert.False(t, ok) assert.Nil(t, item) } func TestIWRRSchedule_StatelessSchedule_MultipleIterators(t *testing.T) { // Test that the schedule is stateless and can create multiple independent iterators item1 := &testWeightedItem{id: 0} item2 := &testWeightedItem{id: 1} items := map[int]*testWeightedItem{ 0: item1, 1: item2, } weights := map[int]int{ 0: 2, 1: 1, } schedule := newIWRRSchedule[int, *testWeightedItem](toWeightedMap(items, weights)) // IWRR for weights [2, 1]: [item1, item1, item2] // Create first iterator and consume partially iter1 := schedule.NewIterator() i1, ok1 := iter1.TryNext() require.True(t, ok1) assert.Equal(t, item1, i1) // Create second iterator - should start from the beginning iter2 := schedule.NewIterator() i2, ok2 := iter2.TryNext() require.True(t, ok2) assert.Equal(t, item1, i2, "second iterator should start from beginning") // First iterator should continue from where it left off i1, ok1 = iter1.TryNext() require.True(t, ok1) assert.Equal(t, item1, i1) // Second iterator should be independent and continue its own iteration i2, ok2 = iter2.TryNext() require.True(t, ok2) assert.Equal(t, item1, i2) } ================================================ FILE: common/task/parallel_task_processor.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "errors" "fmt" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type ( // ParallelTaskProcessorOptions configs PriorityTaskProcessor ParallelTaskProcessorOptions struct { QueueSize int WorkerCount dynamicproperties.IntPropertyFn RetryPolicy backoff.RetryPolicy } parallelTaskProcessorImpl struct { status int32 tasksCh chan Task shutdownCh chan struct{} workerShutdownCh []chan struct{} shutdownWG sync.WaitGroup logger log.Logger metricsScope metrics.Scope options *ParallelTaskProcessorOptions } ) const ( defaultMonitorTickerDuration = 5 * time.Second ) var ( // ErrTaskProcessorClosed is the error returned when submiting task to a stopped processor ErrTaskProcessorClosed = errors.New("task processor has already shutdown") ) // NewParallelTaskProcessor creates a new PriorityTaskProcessor func NewParallelTaskProcessor( logger log.Logger, metricsClient metrics.Client, options *ParallelTaskProcessorOptions, ) Processor { return ¶llelTaskProcessorImpl{ status: common.DaemonStatusInitialized, tasksCh: make(chan Task, options.QueueSize), shutdownCh: make(chan struct{}), workerShutdownCh: make([]chan struct{}, 0, options.WorkerCount()), logger: logger, metricsScope: metricsClient.Scope(metrics.ParallelTaskProcessingScope), options: options, } } func (p *parallelTaskProcessorImpl) Start() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } initialWorkerCount := p.options.WorkerCount() p.shutdownWG.Add(initialWorkerCount) for i := 0; i < initialWorkerCount; i++ { shutdownCh := make(chan struct{}) p.workerShutdownCh = append(p.workerShutdownCh, shutdownCh) go p.taskWorker(shutdownCh) } p.shutdownWG.Add(1) go p.workerMonitor(defaultMonitorTickerDuration) p.logger.Info("Parallel task processor started.") } func (p *parallelTaskProcessorImpl) Stop() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(p.shutdownCh) p.drainAndNackTasks() if success := common.AwaitWaitGroup(&p.shutdownWG, time.Minute); !success { p.logger.Warn("Parallel task processor timedout on shutdown.") } p.logger.Info("Parallel task processor shutdown.") } func (p *parallelTaskProcessorImpl) Submit(task Task) error { p.metricsScope.IncCounter(metrics.ParallelTaskSubmitRequest) sw := p.metricsScope.StartTimer(metrics.ParallelTaskSubmitLatency) defer sw.Stop() if p.isStopped() { return ErrTaskProcessorClosed } select { case p.tasksCh <- task: if p.isStopped() { p.drainAndNackTasks() } return nil case <-p.shutdownCh: return ErrTaskProcessorClosed } } func (p *parallelTaskProcessorImpl) taskWorker(shutdownCh chan struct{}) { defer p.shutdownWG.Done() for { select { case <-shutdownCh: return case task := <-p.tasksCh: p.executeTask(task, shutdownCh) } } } func (p *parallelTaskProcessorImpl) executeTask(task Task, shutdownCh chan struct{}) { sw := p.metricsScope.StartTimer(metrics.ParallelTaskTaskProcessingLatency) defer sw.Stop() defer func() { if r := recover(); r != nil { p.logger.Error("recovered panic in task execution", tag.Dynamic("recovered-panic", r)) task.HandleErr(fmt.Errorf("recovered panic: %v", r)) task.Nack(nil) } }() op := func(ctx context.Context) error { if err := task.Execute(); err != nil { return task.HandleErr(err) } return nil } isRetryable := func(err error) bool { select { case <-shutdownCh: return false default: } return task.RetryErr(err) } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(p.options.RetryPolicy), backoff.WithRetryableError(isRetryable), ) if err := throttleRetry.Do(context.Background(), op); err != nil { // non-retryable error or exhausted all retries or worker shutdown task.Nack(err) return } // no error task.Ack() } func (p *parallelTaskProcessorImpl) workerMonitor(tickerDuration time.Duration) { defer p.shutdownWG.Done() ticker := time.NewTicker(tickerDuration) for { select { case <-p.shutdownCh: ticker.Stop() p.removeWorker(len(p.workerShutdownCh)) return case <-ticker.C: targetWorkerCount := p.options.WorkerCount() currentWorkerCount := len(p.workerShutdownCh) p.addWorker(targetWorkerCount - currentWorkerCount) p.removeWorker(currentWorkerCount - targetWorkerCount) } } } func (p *parallelTaskProcessorImpl) addWorker(count int) { for i := 0; i < count; i++ { shutdownCh := make(chan struct{}) p.workerShutdownCh = append(p.workerShutdownCh, shutdownCh) p.shutdownWG.Add(1) go p.taskWorker(shutdownCh) } } func (p *parallelTaskProcessorImpl) removeWorker(count int) { if count <= 0 { return } currentWorkerCount := len(p.workerShutdownCh) if count > currentWorkerCount { count = currentWorkerCount } shutdownChToClose := p.workerShutdownCh[currentWorkerCount-count:] p.workerShutdownCh = p.workerShutdownCh[:currentWorkerCount-count] for _, shutdownCh := range shutdownChToClose { close(shutdownCh) } } func (p *parallelTaskProcessorImpl) drainAndNackTasks() { for { select { case task := <-p.tasksCh: task.Nack(nil) default: return } } } func (p *parallelTaskProcessorImpl) isStopped() bool { return atomic.LoadInt32(&p.status) == common.DaemonStatusStopped } ================================================ FILE: common/task/parallel_task_processor_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "errors" "math/rand" "sync" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) type ( parallelTaskProcessorSuite struct { *require.Assertions suite.Suite controller *gomock.Controller processor *parallelTaskProcessorImpl } ) var ( errRetryable = errors.New("retryable error") errNonRetryable = errors.New("non-retryable error") ) func TestParallelTaskProcessorSuite(t *testing.T) { s := new(parallelTaskProcessorSuite) suite.Run(t, s) } func (s *parallelTaskProcessorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.processor = NewParallelTaskProcessor( testlogger.New(s.Suite.T()), metrics.NewClient(tally.NoopScope, metrics.Common, metrics.HistogramMigration{}), &ParallelTaskProcessorOptions{ QueueSize: 0, WorkerCount: dynamicproperties.GetIntPropertyFn(1), RetryPolicy: backoff.NewExponentialRetryPolicy(time.Millisecond), }, ).(*parallelTaskProcessorImpl) } func (s *parallelTaskProcessorSuite) TearDownTest() { s.controller.Finish() } func (s *parallelTaskProcessorSuite) TestSubmit_Success() { mockTask := NewMockTask(s.controller) mockTask.EXPECT().Execute().Return(nil).MaxTimes(1) mockTask.EXPECT().Ack().MaxTimes(1) s.processor.Start() err := s.processor.Submit(mockTask) s.NoError(err) s.processor.Stop() } func (s *parallelTaskProcessorSuite) TestSubmit_Fail() { mockTask := NewMockTask(s.controller) s.processor.Start() s.processor.Stop() err := s.processor.Submit(mockTask) s.Equal(ErrTaskProcessorClosed, err) } func (s *parallelTaskProcessorSuite) TestTaskWorker() { numTasks := 5 done := make(chan struct{}) s.processor.shutdownWG.Add(1) workerShutdownCh := make(chan struct{}) s.processor.workerShutdownCh = append(s.processor.workerShutdownCh, workerShutdownCh) go func() { for i := 0; i != numTasks; i++ { mockTask := NewMockTask(s.controller) mockTask.EXPECT().Execute().Return(nil).Times(1) mockTask.EXPECT().Ack().Times(1) err := s.processor.Submit(mockTask) s.NoError(err) } close(workerShutdownCh) close(done) }() s.processor.taskWorker(workerShutdownCh) <-done } func (s *parallelTaskProcessorSuite) TestExecuteTask_RetryableError() { mockTask := NewMockTask(s.controller) gomock.InOrder( mockTask.EXPECT().Execute().Return(errRetryable), mockTask.EXPECT().HandleErr(errRetryable).Return(errRetryable), mockTask.EXPECT().RetryErr(errRetryable).Return(true), mockTask.EXPECT().Execute().Return(errRetryable), mockTask.EXPECT().HandleErr(errRetryable).Return(errRetryable), mockTask.EXPECT().RetryErr(errRetryable).Return(true), mockTask.EXPECT().Execute().Return(nil), mockTask.EXPECT().Ack(), ) s.processor.executeTask(mockTask, make(chan struct{})) } func (s *parallelTaskProcessorSuite) TestExecuteTask_NonRetryableError() { mockTask := NewMockTask(s.controller) gomock.InOrder( mockTask.EXPECT().Execute().Return(errNonRetryable), mockTask.EXPECT().HandleErr(errNonRetryable).Return(errNonRetryable), mockTask.EXPECT().RetryErr(errNonRetryable).Return(false).AnyTimes(), mockTask.EXPECT().Nack(gomock.Any()), ) s.processor.executeTask(mockTask, make(chan struct{})) } func (s *parallelTaskProcessorSuite) TestExecuteTask_WorkerStopped() { mockTask := NewMockTask(s.controller) mockTask.EXPECT().Execute().Return(errRetryable).AnyTimes() mockTask.EXPECT().HandleErr(errRetryable).Return(errRetryable).AnyTimes() mockTask.EXPECT().RetryErr(errRetryable).Return(true).AnyTimes() mockTask.EXPECT().Nack(gomock.Any()).Times(1) done := make(chan struct{}) workerShutdownCh := make(chan struct{}) go func() { s.processor.executeTask(mockTask, workerShutdownCh) close(done) }() close(workerShutdownCh) <-done } func (s *parallelTaskProcessorSuite) TestAddWorker() { newWorkerCount := 10 s.processor.addWorker(newWorkerCount) s.Len(s.processor.workerShutdownCh, newWorkerCount) s.processor.addWorker(-1) s.Len(s.processor.workerShutdownCh, newWorkerCount) for _, shutdownCh := range s.processor.workerShutdownCh { close(shutdownCh) } s.processor.shutdownWG.Wait() } func (s *parallelTaskProcessorSuite) TestRemoveWorker() { currentWorkerCount := 10 removeWorkerCount := 4 s.processor.addWorker(currentWorkerCount) s.processor.removeWorker(removeWorkerCount) s.Len(s.processor.workerShutdownCh, currentWorkerCount-removeWorkerCount) s.processor.removeWorker(-1) s.Len(s.processor.workerShutdownCh, currentWorkerCount-removeWorkerCount) for _, shutdownCh := range s.processor.workerShutdownCh { close(shutdownCh) } s.processor.shutdownWG.Wait() } func (s *parallelTaskProcessorSuite) TestMonitor() { workerCount := 5 s.processor.shutdownWG.Add(1) // for monitor dcClient := dynamicconfig.NewInMemoryClient() err := dcClient.UpdateValue(dynamicproperties.TaskSchedulerWorkerCount, workerCount) s.NoError(err) dcCollection := dynamicconfig.NewCollection(dcClient, s.processor.logger) s.processor.options.WorkerCount = dcCollection.GetIntProperty(dynamicproperties.TaskSchedulerWorkerCount) testMonitorTickerDuration := 100 * time.Millisecond go s.processor.workerMonitor(testMonitorTickerDuration) time.Sleep(2 * testMonitorTickerDuration) // note we can't check the length of the workerShutdownCh directly // as that will lead to race condition. Instead we check the current // size of shutdownWG chan, which should be workerCount + 1 for i := 0; i != workerCount+1; i++ { s.processor.shutdownWG.Done() } s.processor.shutdownWG.Wait() s.processor.shutdownWG.Add(workerCount + 1) newWorkerCount := 3 err = dcClient.UpdateValue(dynamicproperties.TaskSchedulerWorkerCount, newWorkerCount) s.NoError(err) time.Sleep(2 * testMonitorTickerDuration) for i := 0; i != newWorkerCount+1; i++ { s.processor.shutdownWG.Done() } s.processor.shutdownWG.Wait() s.processor.shutdownWG.Add(newWorkerCount + 1) close(s.processor.shutdownCh) time.Sleep(2 * testMonitorTickerDuration) s.processor.shutdownWG.Wait() } func (s *parallelTaskProcessorSuite) TestProcessorContract() { numTasks := 10000 var taskWG sync.WaitGroup tasks := []Task{} taskStatusLock := &sync.Mutex{} taskStatus := make(map[Task]State) for i := 0; i != numTasks; i++ { mockTask := NewMockTask(s.controller) taskStatus[mockTask] = TaskStatePending mockTask.EXPECT().Execute().Return(nil).MaxTimes(1) mockTask.EXPECT().Ack().Do(func() { taskStatusLock.Lock() defer taskStatusLock.Unlock() s.Equal(TaskStatePending, taskStatus[mockTask]) taskStatus[mockTask] = TaskStateAcked taskWG.Done() }).MaxTimes(1) mockTask.EXPECT().Nack(gomock.Any()).Do(func(err error) { taskStatusLock.Lock() defer taskStatusLock.Unlock() s.Equal(TaskStatePending, taskStatus[mockTask]) taskStatus[mockTask] = State(-1) // set it to whatever state that is not TaskStatePending taskWG.Done() }).MaxTimes(1) tasks = append(tasks, mockTask) taskWG.Add(1) } processor := NewParallelTaskProcessor( testlogger.New(s.Suite.T()), metrics.NewClient(tally.NoopScope, metrics.Common, metrics.HistogramMigration{}), &ParallelTaskProcessorOptions{ QueueSize: 100, WorkerCount: dynamicproperties.GetIntPropertyFn(10), RetryPolicy: backoff.NewExponentialRetryPolicy(time.Millisecond), }, ).(*parallelTaskProcessorImpl) processor.Start() go func() { time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond) processor.Stop() }() for _, task := range tasks { if err := processor.Submit(task); err != nil { taskWG.Done() taskStatusLock.Lock() delete(taskStatus, task) taskStatusLock.Unlock() } } s.True(common.AwaitWaitGroup(&taskWG, 10*time.Second)) <-processor.shutdownCh for _, status := range taskStatus { s.NotEqual(TaskStatePending, status) } } func (s *parallelTaskProcessorSuite) TestExecuteTask_PanicHandling() { mockTask := NewMockTask(s.controller) mockTask.EXPECT().Execute().Do(func() { panic("A panic occurred") }) mockTask.EXPECT().HandleErr(gomock.Any()).Return(errRetryable).AnyTimes() mockTask.EXPECT().Nack(gomock.Any()).Times(1) done := make(chan struct{}) workerShutdownCh := make(chan struct{}) go func() { s.processor.executeTask(mockTask, workerShutdownCh) close(done) }() close(workerShutdownCh) <-done } ================================================ FILE: common/task/scheduler_options.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "fmt" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) type SchedulerOptions[K comparable, T Task] struct { SchedulerType SchedulerType FIFOSchedulerOptions *FIFOTaskSchedulerOptions WRRSchedulerOptions *WeightedRoundRobinTaskSchedulerOptions[K, T] } func NewSchedulerOptions[K comparable, T Task]( schedulerType int, queueSize int, workerCount dynamicproperties.IntPropertyFn, dispatcherCount int, taskToChannelKeyFn func(T) K, channelKeyToWeightFn func(K) int, ) (*SchedulerOptions[K, T], error) { options := &SchedulerOptions[K, T]{ SchedulerType: SchedulerType(schedulerType), } switch options.SchedulerType { case SchedulerTypeFIFO: options.FIFOSchedulerOptions = &FIFOTaskSchedulerOptions{ QueueSize: queueSize, WorkerCount: workerCount, DispatcherCount: dispatcherCount, RetryPolicy: common.CreateTaskProcessingRetryPolicy(), } case SchedulerTypeWRR: options.WRRSchedulerOptions = &WeightedRoundRobinTaskSchedulerOptions[K, T]{ QueueSize: queueSize, DispatcherCount: dispatcherCount, TaskToChannelKeyFn: taskToChannelKeyFn, ChannelKeyToWeightFn: channelKeyToWeightFn, } default: return nil, fmt.Errorf("unknown task scheduler type: %v", schedulerType) } return options, nil } func (o *SchedulerOptions[K, T]) String() string { return fmt.Sprintf("{schedulerType:%v, fifoSchedulerOptions:%s, wrrSchedulerOptions:%s}", o.SchedulerType, o.FIFOSchedulerOptions, o.WRRSchedulerOptions) } ================================================ FILE: common/task/scheduler_options_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "testing" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) func TestSchedulerOptionsString(t *testing.T) { tests := []struct { desc string schedulerType int queueSize int workerCount dynamicproperties.IntPropertyFn dispatcherCount int wantErr bool want string }{ { desc: "FIFO", schedulerType: int(SchedulerTypeFIFO), queueSize: 1, workerCount: dynamicproperties.GetIntPropertyFn(3), dispatcherCount: 1, want: "{schedulerType:1, fifoSchedulerOptions:{QueueSize: 1, WorkerCount: 3, DispatcherCount: 1}, wrrSchedulerOptions:}", }, { desc: "WRR", schedulerType: int(SchedulerTypeWRR), queueSize: 3, workerCount: dynamicproperties.GetIntPropertyFn(4), dispatcherCount: 5, want: "{schedulerType:2, fifoSchedulerOptions:, wrrSchedulerOptions:{QueueSize: 3, DispatcherCount: 5}}", }, { desc: "InvalidSchedulerType", schedulerType: 3, wantErr: true, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { o, err := NewSchedulerOptions[int, PriorityTask](tc.schedulerType, tc.queueSize, tc.workerCount, tc.dispatcherCount, nil, nil) if (err != nil) != tc.wantErr { t.Errorf("Got error: %v, wantErr: %v", err, tc.wantErr) } if err != nil { return } if got := o.String(); got != tc.want { t.Errorf("Got: %v, want: %v", got, tc.want) } }) } } ================================================ FILE: common/task/sequential_task_processor.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type ( sequentialTaskProcessorImpl struct { status int32 shutdownChan chan struct{} waitGroup sync.WaitGroup coroutineSize int taskqueues collection.ConcurrentTxMap taskQueueFactory SequentialTaskQueueFactory taskqueueChan chan SequentialTaskQueue metricsScope metrics.Scope logger log.Logger } ) // NewSequentialTaskProcessor create a new sequential tasks processor func NewSequentialTaskProcessor( coroutineSize int, taskQueueHashFn collection.HashFunc, taskQueueFactory SequentialTaskQueueFactory, metricsClient metrics.Client, logger log.Logger, ) Processor { return &sequentialTaskProcessorImpl{ status: common.DaemonStatusInitialized, shutdownChan: make(chan struct{}), coroutineSize: coroutineSize, taskqueues: collection.NewShardedConcurrentTxMap(1024, taskQueueHashFn), taskQueueFactory: taskQueueFactory, taskqueueChan: make(chan SequentialTaskQueue, coroutineSize), metricsScope: metricsClient.Scope(metrics.SequentialTaskProcessingScope), logger: logger, } } func (t *sequentialTaskProcessorImpl) Start() { if !atomic.CompareAndSwapInt32(&t.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } t.waitGroup.Add(t.coroutineSize) for i := 0; i < t.coroutineSize; i++ { go t.pollAndProcessTaskQueue() } t.logger.Info("Task processor started.") } func (t *sequentialTaskProcessorImpl) Stop() { if !atomic.CompareAndSwapInt32(&t.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(t.shutdownChan) if success := common.AwaitWaitGroup(&t.waitGroup, time.Minute); !success { t.logger.Warn("Task processor timeout trying to stop.") } t.logger.Info("Task processor stopped.") } func (t *sequentialTaskProcessorImpl) Submit(task Task) error { t.metricsScope.IncCounter(metrics.SequentialTaskSubmitRequest) metricsTimer := t.metricsScope.StartTimer(metrics.SequentialTaskSubmitLatency) defer metricsTimer.Stop() taskqueue := t.taskQueueFactory(task) taskqueue.Add(task) _, fnEvaluated, err := t.taskqueues.PutOrDo( taskqueue.QueueID(), taskqueue, func(key interface{}, value interface{}) error { value.(SequentialTaskQueue).Add(task) return nil }, ) if err != nil { return err } // if function evaluated, meaning that the task set is // already dispatched if fnEvaluated { t.metricsScope.IncCounter(metrics.SequentialTaskSubmitRequestTaskQueueExist) return nil } // need to dispatch this task set t.metricsScope.IncCounter(metrics.SequentialTaskSubmitRequestTaskQueueMissing) select { case <-t.shutdownChan: case t.taskqueueChan <- taskqueue: } return nil } func (t *sequentialTaskProcessorImpl) pollAndProcessTaskQueue() { defer t.waitGroup.Done() for { select { case <-t.shutdownChan: return case taskqueue := <-t.taskqueueChan: metricsTimer := t.metricsScope.StartTimer(metrics.SequentialTaskQueueProcessingLatency) t.processTaskQueue(taskqueue) metricsTimer.Stop() } } } func (t *sequentialTaskProcessorImpl) processTaskQueue(taskqueue SequentialTaskQueue) { for { select { case <-t.shutdownChan: return default: queueSize := taskqueue.Len() t.metricsScope.RecordTimer(metrics.SequentialTaskQueueSize, time.Duration(queueSize)) if queueSize > 0 { t.processTaskOnce(taskqueue) } if taskqueue.IsEmpty() { deleted := t.taskqueues.RemoveIf(taskqueue.QueueID(), func(key interface{}, value interface{}) bool { return value.(SequentialTaskQueue).IsEmpty() }) if deleted { return } // if deletion failed, meaning that task queue is offered with new task // continue execution } } } } func (t *sequentialTaskProcessorImpl) processTaskOnce(taskqueue SequentialTaskQueue) { metricsTimer := t.metricsScope.StartTimer(metrics.SequentialTaskTaskProcessingLatency) defer metricsTimer.Stop() task := taskqueue.Remove() err := task.Execute() err = task.HandleErr(err) if err != nil { if task.RetryErr(err) { taskqueue.Add(task) } else { t.logger.Error("Unable to process task", tag.Error(err)) task.Nack(err) } } else { task.Ack() } } ================================================ FILE: common/task/sequential_task_processor_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "errors" "math/rand" "sync" "testing" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) type ( SequentialTaskProcessorSuite struct { suite.Suite processor Processor } testSequentialTaskQueueImpl struct { id uint32 taskQueue collection.Queue[Task] } testSequentialTaskImpl struct { waitgroup *sync.WaitGroup queueID uint32 taskID uint32 lock sync.Mutex acked int nacked int } ) func TestSequentialTaskProcessorSuite(t *testing.T) { suite.Run(t, new(SequentialTaskProcessorSuite)) } func (s *SequentialTaskProcessorSuite) SetupTest() { logger := testlogger.New(s.T()) s.processor = NewSequentialTaskProcessor( 20, func(key interface{}) uint32 { return key.(uint32) }, func(task Task) SequentialTaskQueue { taskQueue := collection.NewConcurrentPriorityQueue(func(this Task, other Task) bool { return this.(*testSequentialTaskImpl).taskID < other.(*testSequentialTaskImpl).taskID }) return &testSequentialTaskQueueImpl{ id: task.(*testSequentialTaskImpl).queueID, taskQueue: taskQueue, } }, metrics.NewClient(tally.NoopScope, metrics.Common, metrics.HistogramMigration{}), logger, ) } func (s *SequentialTaskProcessorSuite) TestSubmit_NoPriorTask() { waitgroup := &sync.WaitGroup{} waitgroup.Add(1) task := newTestSequentialTaskImpl(waitgroup, 4, uint32(1)) // do not start the processor s.Nil(s.processor.Submit(task)) sequentialTaskQueue := <-s.processor.(*sequentialTaskProcessorImpl).taskqueueChan sequentialTask := sequentialTaskQueue.Remove() s.True(sequentialTaskQueue.IsEmpty()) s.Equal(task, sequentialTask) } func (s *SequentialTaskProcessorSuite) TestSubmit_HasPriorTask() { waitgroup := &sync.WaitGroup{} task1 := newTestSequentialTaskImpl(waitgroup, 4, uint32(1)) task2 := newTestSequentialTaskImpl(waitgroup, 4, uint32(2)) // do not start the processor s.Nil(s.processor.Submit(task1)) s.Nil(s.processor.Submit(task2)) sequentialTaskQueue := <-s.processor.(*sequentialTaskProcessorImpl).taskqueueChan sequentialTask1 := sequentialTaskQueue.Remove() sequentialTask2 := sequentialTaskQueue.Remove() s.True(sequentialTaskQueue.IsEmpty()) s.Equal(task1, sequentialTask1) s.Equal(task2, sequentialTask2) } func (s *SequentialTaskProcessorSuite) TestProcessTaskQueue_ShutDown() { waitgroup := &sync.WaitGroup{} waitgroup.Add(2) task1 := newTestSequentialTaskImpl(waitgroup, 4, uint32(1)) task2 := newTestSequentialTaskImpl(waitgroup, 4, uint32(2)) // do not start the processor s.Nil(s.processor.Submit(task1)) s.Nil(s.processor.Submit(task2)) sequentialTaskQueue := <-s.processor.(*sequentialTaskProcessorImpl).taskqueueChan s.processor.Start() s.processor.Stop() s.processor.(*sequentialTaskProcessorImpl).processTaskQueue(sequentialTaskQueue) s.Equal(0, task1.NumAcked()) s.Equal(0, task1.NumNcked()) s.Equal(0, task2.NumAcked()) s.Equal(0, task2.NumNcked()) s.Equal(1, s.processor.(*sequentialTaskProcessorImpl).taskqueues.Len()) s.Equal(2, sequentialTaskQueue.Len()) } func (s *SequentialTaskProcessorSuite) TestProcessTaskQueue() { waitgroup := &sync.WaitGroup{} waitgroup.Add(2) task1 := newTestSequentialTaskImpl(waitgroup, 4, uint32(1)) task2 := newTestSequentialTaskImpl(waitgroup, 4, uint32(2)) // do not start the processor s.Nil(s.processor.Submit(task1)) s.Nil(s.processor.Submit(task2)) sequentialTaskQueue := <-s.processor.(*sequentialTaskProcessorImpl).taskqueueChan s.processor.(*sequentialTaskProcessorImpl).processTaskQueue(sequentialTaskQueue) waitgroup.Wait() s.Equal(1, task1.NumAcked()) s.Equal(0, task1.NumNcked()) s.Equal(1, task2.NumAcked()) s.Equal(0, task2.NumNcked()) s.Equal(0, s.processor.(*sequentialTaskProcessorImpl).taskqueues.Len()) s.Equal(0, sequentialTaskQueue.Len()) } func (s *SequentialTaskProcessorSuite) TestSequentialTaskProcessing() { numTasks := 100 waitgroup := &sync.WaitGroup{} waitgroup.Add(numTasks) tasks := []*testSequentialTaskImpl{} for i := 0; i < numTasks; i++ { tasks = append(tasks, newTestSequentialTaskImpl(waitgroup, 4, uint32(i))) } s.processor.Start() for _, task := range tasks { s.Nil(s.processor.Submit(task)) } waitgroup.Wait() s.processor.Stop() for _, task := range tasks { s.Equal(1, task.NumAcked()) s.Equal(0, task.NumNcked()) } s.Equal(0, s.processor.(*sequentialTaskProcessorImpl).taskqueues.Len()) } func (s *SequentialTaskProcessorSuite) TestRandomizedTaskProcessing() { numQueues := 100 numTasks := 1000 waitgroup := &sync.WaitGroup{} waitgroup.Add(numQueues * numTasks) tasks := make([][]*testSequentialTaskImpl, numQueues) for i := 0; i < numQueues; i++ { tasks[i] = make([]*testSequentialTaskImpl, numTasks) for j := 0; j < numTasks; j++ { tasks[i][j] = newTestSequentialTaskImpl(waitgroup, uint32(i), uint32(j)) } randomize(tasks[i]) } s.processor.Start() startChan := make(chan struct{}) for i := 0; i < numQueues; i++ { go func(i int) { <-startChan for j := 0; j < numTasks; j++ { s.Nil(s.processor.Submit(tasks[i][j])) } }(i) } close(startChan) waitgroup.Wait() s.processor.Stop() for i := 0; i < numQueues; i++ { for j := 0; j < numTasks; j++ { task := tasks[i][j] s.Equal(1, task.NumAcked()) s.Equal(0, task.NumNcked()) } } s.Equal(0, s.processor.(*sequentialTaskProcessorImpl).taskqueues.Len()) } func randomize(array []*testSequentialTaskImpl) { for i := 0; i < len(array); i++ { index := rand.Int31n(int32(i) + 1) array[i], array[index] = array[index], array[i] } } func newTestSequentialTaskImpl(waitgroup *sync.WaitGroup, queueID uint32, taskID uint32) *testSequentialTaskImpl { return &testSequentialTaskImpl{ waitgroup: waitgroup, queueID: queueID, taskID: taskID, } } func (t *testSequentialTaskImpl) Execute() error { if rand.Float64() < 0.5 { return nil } return errors.New("some random error") } func (t *testSequentialTaskImpl) HandleErr(err error) error { return err } func (t *testSequentialTaskImpl) RetryErr(err error) bool { return true } func (t *testSequentialTaskImpl) State() State { if t.acked > 0 { return TaskStateAcked } return TaskStatePending } func (t *testSequentialTaskImpl) Ack() { t.lock.Lock() defer t.lock.Unlock() t.acked++ t.waitgroup.Done() } func (t *testSequentialTaskImpl) NumAcked() int { t.lock.Lock() defer t.lock.Unlock() return t.acked } func (t *testSequentialTaskImpl) Nack(err error) { t.lock.Lock() defer t.lock.Unlock() t.nacked++ t.waitgroup.Done() } func (t *testSequentialTaskImpl) Cancel() { } func (t *testSequentialTaskImpl) NumNcked() int { t.lock.Lock() defer t.lock.Unlock() return t.nacked } func (t *testSequentialTaskQueueImpl) QueueID() interface{} { return t.id } func (t *testSequentialTaskQueueImpl) Add(task Task) { t.taskQueue.Add(task) } func (t *testSequentialTaskQueueImpl) Remove() Task { item, _ := t.taskQueue.Remove() return item.(Task) } func (t *testSequentialTaskQueueImpl) IsEmpty() bool { return t.taskQueue.IsEmpty() } func (t *testSequentialTaskQueueImpl) Len() int { return t.taskQueue.Len() } ================================================ FILE: common/task/ttl_channel.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package task import ( "sync/atomic" "time" ) // TTLChannel is a channel that can expire if it is not written to for a given amount of time. type TTLChannel[V any] struct { c chan V lastWriteTime atomic.Int64 refCount atomic.Int32 } func NewTTLChannel[V any](bufferSize int) *TTLChannel[V] { return &TTLChannel[V]{ c: make(chan V, bufferSize), } } func (c *TTLChannel[V]) IncRef() { c.refCount.Add(1) } func (c *TTLChannel[V]) DecRef() { c.refCount.Add(-1) } func (c *TTLChannel[V]) RefCount() int32 { return c.refCount.Load() } func (c *TTLChannel[V]) LastWriteTime() time.Time { return time.Unix(c.lastWriteTime.Load(), 0) } func (c *TTLChannel[V]) UpdateLastWriteTime(now time.Time) { c.lastWriteTime.Store(now.Unix()) } func (c *TTLChannel[V]) Chan() chan V { return c.c } func (c *TTLChannel[V]) Len() int { return len(c.c) } func (c *TTLChannel[V]) Cap() int { return cap(c.c) } func (c *TTLChannel[V]) ShouldCleanup(now time.Time, ttl time.Duration) bool { return now.Sub(c.LastWriteTime()) > ttl && c.Len() == 0 && c.RefCount() == 0 } ================================================ FILE: common/task/ttl_channel_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package task import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestNewTTLChannel(t *testing.T) { tests := []struct { name string bufferSize int }{ { name: "unbuffered channel", bufferSize: 0, }, { name: "small buffer", bufferSize: 10, }, { name: "large buffer", bufferSize: 1000, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ch := NewTTLChannel[int](tt.bufferSize) require.NotNil(t, ch) assert.NotNil(t, ch.Chan()) assert.Equal(t, tt.bufferSize, ch.Cap()) assert.Equal(t, 0, ch.Len()) assert.Equal(t, int32(0), ch.RefCount()) }) } } func TestTTLChannel_RefCount(t *testing.T) { ch := NewTTLChannel[int](10) // Initial ref count should be 0 assert.Equal(t, int32(0), ch.RefCount()) // Add refs ch.IncRef() assert.Equal(t, int32(1), ch.RefCount()) ch.IncRef() assert.Equal(t, int32(2), ch.RefCount()) ch.IncRef() assert.Equal(t, int32(3), ch.RefCount()) // Decrement refs ch.DecRef() assert.Equal(t, int32(2), ch.RefCount()) ch.DecRef() assert.Equal(t, int32(1), ch.RefCount()) ch.DecRef() assert.Equal(t, int32(0), ch.RefCount()) } func TestTTLChannel_RefCount_MultipleAddDec(t *testing.T) { ch := NewTTLChannel[string](5) // Add multiple refs for i := 0; i < 100; i++ { ch.IncRef() } assert.Equal(t, int32(100), ch.RefCount()) // Decrement all refs for i := 0; i < 100; i++ { ch.DecRef() } assert.Equal(t, int32(0), ch.RefCount()) } func TestTTLChannel_LastWriteTime(t *testing.T) { ch := NewTTLChannel[int](10) // Initial last write time should be zero (Unix epoch) assert.Equal(t, time.Unix(0, 0), ch.LastWriteTime()) // Update last write time now := time.Now() ch.UpdateLastWriteTime(now) // Should return the updated time (truncated to seconds) expected := time.Unix(now.Unix(), 0) assert.Equal(t, expected, ch.LastWriteTime()) // Update with a different time later := now.Add(1 * time.Hour) ch.UpdateLastWriteTime(later) expected = time.Unix(later.Unix(), 0) assert.Equal(t, expected, ch.LastWriteTime()) } func TestTTLChannel_Chan(t *testing.T) { ch := NewTTLChannel[int](10) // Should return the underlying channel c := ch.Chan() require.NotNil(t, c) // Should be able to send and receive c <- 42 val := <-c assert.Equal(t, 42, val) } func TestTTLChannel_Len(t *testing.T) { ch := NewTTLChannel[int](10) // Initially empty assert.Equal(t, 0, ch.Len()) // Add items ch.Chan() <- 1 assert.Equal(t, 1, ch.Len()) ch.Chan() <- 2 assert.Equal(t, 2, ch.Len()) ch.Chan() <- 3 assert.Equal(t, 3, ch.Len()) // Remove items <-ch.Chan() assert.Equal(t, 2, ch.Len()) <-ch.Chan() assert.Equal(t, 1, ch.Len()) <-ch.Chan() assert.Equal(t, 0, ch.Len()) } func TestTTLChannel_Cap(t *testing.T) { tests := []struct { name string bufferSize int }{ { name: "unbuffered", bufferSize: 0, }, { name: "buffered 1", bufferSize: 1, }, { name: "buffered 100", bufferSize: 100, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ch := NewTTLChannel[int](tt.bufferSize) assert.Equal(t, tt.bufferSize, ch.Cap()) }) } } func TestTTLChannel_ShouldCleanup(t *testing.T) { tests := []struct { name string refCount int32 channelLen int timeSinceWrite time.Duration ttl time.Duration expected bool }{ { name: "should cleanup - all conditions met", refCount: 0, channelLen: 0, timeSinceWrite: 2 * time.Hour, ttl: 1 * time.Hour, expected: true, }, { name: "should not cleanup - has references", refCount: 1, channelLen: 0, timeSinceWrite: 2 * time.Hour, ttl: 1 * time.Hour, expected: false, }, { name: "should not cleanup - channel not empty", refCount: 0, channelLen: 1, timeSinceWrite: 2 * time.Hour, ttl: 1 * time.Hour, expected: false, }, { name: "should not cleanup - not expired", refCount: 0, channelLen: 0, timeSinceWrite: 30 * time.Minute, ttl: 1 * time.Hour, expected: false, }, { name: "should not cleanup - just before TTL boundary", refCount: 0, channelLen: 0, timeSinceWrite: 1*time.Hour - 1*time.Second, ttl: 1 * time.Hour, expected: false, }, { name: "should cleanup - slightly past TTL", refCount: 0, channelLen: 0, timeSinceWrite: 1*time.Hour + 1*time.Second, ttl: 1 * time.Hour, expected: true, }, { name: "should not cleanup - multiple issues", refCount: 2, channelLen: 3, timeSinceWrite: 30 * time.Minute, ttl: 1 * time.Hour, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ch := NewTTLChannel[int](10) // Set up ref count for i := int32(0); i < tt.refCount; i++ { ch.IncRef() } // Set up channel length for i := 0; i < tt.channelLen; i++ { ch.Chan() <- i } // Set up last write time lastWriteTime := time.Now().Add(-tt.timeSinceWrite) ch.UpdateLastWriteTime(lastWriteTime) // Check should cleanup now := time.Now() result := ch.ShouldCleanup(now, tt.ttl) assert.Equal(t, tt.expected, result) }) } } func TestTTLChannel_ShouldCleanup_EdgeCases(t *testing.T) { t.Run("zero TTL", func(t *testing.T) { ch := NewTTLChannel[int](10) // Use time truncated to seconds to match what gets stored now := time.Unix(time.Now().Unix(), 0) ch.UpdateLastWriteTime(now) // Even with zero TTL, should cleanup only if time has passed assert.False(t, ch.ShouldCleanup(now, 0)) assert.True(t, ch.ShouldCleanup(now.Add(1*time.Second), 0)) }) t.Run("never written", func(t *testing.T) { ch := NewTTLChannel[int](10) now := time.Now() ttl := 1 * time.Hour // Channel was never written to (lastWriteTime is Unix epoch) // Should cleanup since time.Now() - Unix(0) > ttl assert.True(t, ch.ShouldCleanup(now, ttl)) }) t.Run("negative ref count", func(t *testing.T) { ch := NewTTLChannel[int](10) now := time.Now() ch.UpdateLastWriteTime(now.Add(-2 * time.Hour)) // Manually set negative ref count (shouldn't happen in practice) ch.DecRef() assert.Equal(t, int32(-1), ch.RefCount()) // Should not cleanup with negative ref count // (even though it's an error state, better safe than sorry) assert.False(t, ch.ShouldCleanup(now, 1*time.Hour)) }) } func TestTTLChannel_ConcurrentRefCount(t *testing.T) { ch := NewTTLChannel[int](10) iterations := 1000 // Concurrently add refs done := make(chan bool) for i := 0; i < 10; i++ { go func() { for j := 0; j < iterations; j++ { ch.IncRef() } done <- true }() } // Wait for all goroutines for i := 0; i < 10; i++ { <-done } // Should have 10 * iterations refs expected := int32(10 * iterations) assert.Equal(t, expected, ch.RefCount()) // Concurrently decrement refs for i := 0; i < 10; i++ { go func() { for j := 0; j < iterations; j++ { ch.DecRef() } done <- true }() } // Wait for all goroutines for i := 0; i < 10; i++ { <-done } // Should be back to 0 assert.Equal(t, int32(0), ch.RefCount()) } func TestTTLChannel_ConcurrentTimeUpdate(t *testing.T) { ch := NewTTLChannel[int](10) iterations := 100 // Concurrently update time done := make(chan bool) baseTime := time.Now() for i := 0; i < 10; i++ { go func(offset int) { for j := 0; j < iterations; j++ { ch.UpdateLastWriteTime(baseTime.Add(time.Duration(offset+j) * time.Second)) } done <- true }(i * iterations) } // Wait for all goroutines for i := 0; i < 10; i++ { <-done } // Should have some valid time (exact value depends on interleaving) lastTime := ch.LastWriteTime() assert.True(t, !lastTime.Before(baseTime), "last write time should not be before base time") } ================================================ FILE: common/task/weighted_channel_pool.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package task import ( "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) const defaultIdleChannelTTLInSeconds = 3600 type ( WeightedRoundRobinChannelPoolOptions struct { BufferSize int IdleChannelTTLInSeconds int64 } WeightedRoundRobinChannelPool[K comparable, V any] struct { sync.RWMutex status int32 shutdownCh chan struct{} shutdownWG sync.WaitGroup bufferSize int idleChannelTTLInSeconds int64 logger log.Logger metricsScope metrics.Scope timeSource clock.TimeSource channelMap map[K]weightedContainer[*TTLChannel[V]] // a snapshot of the channels to be used for the IWRR schedule iwrrSchedule atomic.Pointer[iwrrSchedule[*TTLChannel[V]]] } ) func NewWeightedRoundRobinChannelPool[K comparable, V any]( logger log.Logger, metricsScope metrics.Scope, timeSource clock.TimeSource, options WeightedRoundRobinChannelPoolOptions, ) *WeightedRoundRobinChannelPool[K, V] { wrr := &WeightedRoundRobinChannelPool[K, V]{ bufferSize: options.BufferSize, idleChannelTTLInSeconds: options.IdleChannelTTLInSeconds, logger: logger, metricsScope: metricsScope, timeSource: timeSource, channelMap: make(map[K]weightedContainer[*TTLChannel[V]]), shutdownCh: make(chan struct{}), } // Initialize with empty schedule schedule := newIWRRSchedule[K, *TTLChannel[V]](nil) wrr.iwrrSchedule.Store(schedule) return wrr } func (p *WeightedRoundRobinChannelPool[K, V]) Start() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } p.shutdownWG.Add(1) go p.cleanupLoop() p.logger.Info("Weighted round robin channel pool started.") } func (p *WeightedRoundRobinChannelPool[K, V]) Stop() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(p.shutdownCh) p.shutdownWG.Wait() p.logger.Info("Weighted round robin channel pool stopped.") } func (p *WeightedRoundRobinChannelPool[K, V]) cleanupLoop() { defer p.shutdownWG.Done() ticker := p.timeSource.NewTicker(time.Duration((p.idleChannelTTLInSeconds / 2)) * time.Second) defer ticker.Stop() for { select { case <-ticker.Chan(): p.doCleanup() case <-p.shutdownCh: return } } } func (p *WeightedRoundRobinChannelPool[K, V]) doCleanup() { p.Lock() defer p.Unlock() var channelsToCleanup []K now := p.timeSource.Now() ttl := time.Duration(p.idleChannelTTLInSeconds) * time.Second for k, container := range p.channelMap { if container.item.ShouldCleanup(now, ttl) { channelsToCleanup = append(channelsToCleanup, k) } } for _, k := range channelsToCleanup { delete(p.channelMap, k) } if len(channelsToCleanup) > 0 { p.logger.Info("clean up idle channels", tag.Dynamic("channels", channelsToCleanup)) p.updateScheduleLocked() } } func (p *WeightedRoundRobinChannelPool[K, V]) GetOrCreateChannel(key K, weight int) (chan V, func()) { p.RLock() if container, exists := p.channelMap[key]; exists && container.weight == weight { container.item.IncRef() container.item.UpdateLastWriteTime(p.timeSource.Now()) p.RUnlock() return container.item.Chan(), container.item.DecRef } p.RUnlock() p.Lock() defer p.Unlock() if container, exists := p.channelMap[key]; exists { container.item.IncRef() container.item.UpdateLastWriteTime(p.timeSource.Now()) if container.weight != weight { p.channelMap[key] = weightedContainer[*TTLChannel[V]]{ item: container.item, weight: weight, } p.updateScheduleLocked() } return container.item.Chan(), container.item.DecRef } c := NewTTLChannel[V](p.bufferSize) p.channelMap[key] = weightedContainer[*TTLChannel[V]]{ item: c, weight: weight, } c.IncRef() c.UpdateLastWriteTime(p.timeSource.Now()) p.updateScheduleLocked() return c.Chan(), c.DecRef } func (p *WeightedRoundRobinChannelPool[K, V]) GetAllChannels() []chan V { p.RLock() defer p.RUnlock() allChannels := make([]chan V, 0, len(p.channelMap)) for _, container := range p.channelMap { allChannels = append(allChannels, container.item.Chan()) } return allChannels } func (p *WeightedRoundRobinChannelPool[K, V]) GetSchedule() Schedule[*TTLChannel[V]] { return p.iwrrSchedule.Load() } func (p *WeightedRoundRobinChannelPool[K, V]) updateScheduleLocked() { p.iwrrSchedule.Store(newIWRRSchedule(p.channelMap)) // Update memory gauge - now only stores channel references once, not weight times memoryBytes := len(p.channelMap) * 16 // channel map entries p.metricsScope.UpdateGauge(metrics.WeightedChannelPoolSizeGauge, float64(memoryBytes)) } ================================================ FILE: common/task/weighted_channel_pool_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package task import ( "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) func TestGetOrCreateChannel(t *testing.T) { timeSource := clock.NewMockedTimeSource() pool := NewWeightedRoundRobinChannelPool[string, int]( testlogger.New(t), metrics.NoopScope, timeSource, WeightedRoundRobinChannelPoolOptions{ BufferSize: 1000, IdleChannelTTLInSeconds: 10, }, ) // First, verify that the method returns the same channel if the key and weight are the same c1, releaseFn1 := pool.GetOrCreateChannel("k1", 1) defer releaseFn1() c2, releaseFn2 := pool.GetOrCreateChannel("k1", 1) defer releaseFn2() assert.Equal(t, c1, c2) // Next, verify that the methods returns the same channel if the key is the same but weight is different c3, releaseFn3 := pool.GetOrCreateChannel("k1", 2) defer releaseFn3() assert.Equal(t, c1, c3) } func TestGetOrCreateChannelConcurrent(t *testing.T) { timeSource := clock.NewMockedTimeSource() pool := NewWeightedRoundRobinChannelPool[string, int]( testlogger.New(t), metrics.NoopScope, timeSource, WeightedRoundRobinChannelPoolOptions{ BufferSize: 1000, IdleChannelTTLInSeconds: 10, }, ) var wg sync.WaitGroup var chMap sync.Map wg.Add(15) for i := 0; i < 5; i++ { go func(i int) { defer wg.Done() c, releaseFn := pool.GetOrCreateChannel("k1", i+1) defer releaseFn() chMap.Store("k1", c) }(i) go func(i int) { defer wg.Done() c, releaseFn := pool.GetOrCreateChannel("k2", i+1) defer releaseFn() chMap.Store("k2", c) }(i) go func(i int) { defer wg.Done() c, releaseFn := pool.GetOrCreateChannel("k3", i+1) defer releaseFn() chMap.Store("k3", c) }(i) } wg.Wait() chs := pool.GetAllChannels() assert.Len(t, chs, 3) ch1, _ := chMap.Load("k1") ch2, _ := chMap.Load("k2") ch3, _ := chMap.Load("k3") expectedChs := []chan int{ch1.(chan int), ch2.(chan int), ch3.(chan int)} assert.ElementsMatch(t, expectedChs, chs) } func TestGetSchedule(t *testing.T) { timeSource := clock.NewMockedTimeSource() pool := NewWeightedRoundRobinChannelPool[string, int]( testlogger.New(t), metrics.NoopScope, timeSource, WeightedRoundRobinChannelPoolOptions{ BufferSize: 1000, IdleChannelTTLInSeconds: 10, }, ) c1, releaseFn1 := pool.GetOrCreateChannel("k1", 1) defer releaseFn1() c2, releaseFn2 := pool.GetOrCreateChannel("k2", 2) defer releaseFn2() c3, releaseFn3 := pool.GetOrCreateChannel("k3", 3) defer releaseFn3() schedule := pool.GetSchedule() assert.Equal(t, 6, schedule.Len()) iter1 := schedule.NewIterator() // Verify IWRR sequence: [c3, c3, c2, c3, c2, c1] expectedSequence1 := []chan int{c3, c3, c2, c3, c2, c1} for _, expected := range expectedSequence1 { ch, ok := iter1.TryNext() assert.True(t, ok) assert.Equal(t, expected, ch.Chan()) } c4, releaseFn4 := pool.GetOrCreateChannel("k2", 4) defer releaseFn4() assert.Equal(t, c2, c4) schedule = pool.GetSchedule() assert.Equal(t, 8, schedule.Len()) iter2 := schedule.NewIterator() // Verify IWRR sequence after weight change: [c2, c2, c3, c2, c3, c2, c3, c1] expectedSequence2 := []chan int{c2, c2, c3, c2, c3, c2, c3, c1} for _, expected := range expectedSequence2 { ch, ok := iter2.TryNext() assert.True(t, ok) assert.Equal(t, expected, ch.Chan()) } } func TestCleanup(t *testing.T) { timeSource := clock.NewRealTimeSource() pool := NewWeightedRoundRobinChannelPool[string, int]( testlogger.New(t), metrics.NoopScope, timeSource, WeightedRoundRobinChannelPoolOptions{ BufferSize: 1000, IdleChannelTTLInSeconds: 2, }, ) pool.Start() defer pool.Stop() // First, verify that the method returns the same channel if the key and weight are the same _, releaseFn1 := pool.GetOrCreateChannel("k1", 1) ch2, releaseFn2 := pool.GetOrCreateChannel("k2", 1) ch3, releaseFn3 := pool.GetOrCreateChannel("k3", 1) ch3 <- 1 assert.Len(t, pool.GetAllChannels(), 3) releaseFn1() releaseFn3() time.Sleep(time.Second * 4) // only c1 is deleted chs := pool.GetAllChannels() assert.ElementsMatch(t, chs, []chan int{ch2, ch3}) releaseFn2() } ================================================ FILE: common/task/weighted_round_robin_task_scheduler.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "errors" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type weightedRoundRobinTaskSchedulerImpl[K comparable, T Task] struct { sync.RWMutex status int32 pool *WeightedRoundRobinChannelPool[K, T] ctx context.Context cancel context.CancelFunc notifyCh chan struct{} dispatcherWG sync.WaitGroup logger log.Logger metricsScope metrics.Scope options *WeightedRoundRobinTaskSchedulerOptions[K, T] processor Processor } const ( wRRTaskProcessorQueueSize = 1 defaultUpdateWeightsInterval = 5 * time.Second ) var ( // ErrTaskSchedulerClosed is the error returned when submitting task to a stopped scheduler ErrTaskSchedulerClosed = errors.New("task scheduler has already shutdown") ) // NewWeightedRoundRobinTaskScheduler creates a new WRR task scheduler func NewWeightedRoundRobinTaskScheduler[K comparable, T Task]( logger log.Logger, metricsClient metrics.Client, timeSource clock.TimeSource, processor Processor, options *WeightedRoundRobinTaskSchedulerOptions[K, T], ) (Scheduler[T], error) { metricsScope := metricsClient.Scope(metrics.TaskSchedulerScope) ctx, cancel := context.WithCancel(context.Background()) scheduler := &weightedRoundRobinTaskSchedulerImpl[K, T]{ status: common.DaemonStatusInitialized, pool: NewWeightedRoundRobinChannelPool[K, T]( logger, metricsScope, timeSource, WeightedRoundRobinChannelPoolOptions{ BufferSize: options.QueueSize, IdleChannelTTLInSeconds: defaultIdleChannelTTLInSeconds, }), ctx: ctx, cancel: cancel, notifyCh: make(chan struct{}, 1), logger: logger, metricsScope: metricsScope, options: options, processor: processor, } return scheduler, nil } func (w *weightedRoundRobinTaskSchedulerImpl[K, T]) Start() { if !atomic.CompareAndSwapInt32(&w.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } w.dispatcherWG.Add(w.options.DispatcherCount) for i := 0; i != w.options.DispatcherCount; i++ { go w.dispatcher() } w.logger.Info("Weighted round robin task scheduler started.") } func (w *weightedRoundRobinTaskSchedulerImpl[K, T]) Stop() { if !atomic.CompareAndSwapInt32(&w.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } w.cancel() taskChs := w.pool.GetAllChannels() for _, taskCh := range taskChs { drainAndNackPriorityTask(taskCh) } if success := common.AwaitWaitGroup(&w.dispatcherWG, time.Minute); !success { w.logger.Warn("Weighted round robin task scheduler timedout on shutdown.") } w.logger.Info("Weighted round robin task scheduler shutdown.") } func (w *weightedRoundRobinTaskSchedulerImpl[K, T]) Submit(task T) error { w.metricsScope.IncCounter(metrics.PriorityTaskSubmitRequest) sw := w.metricsScope.StartTimer(metrics.PriorityTaskSubmitLatency) defer sw.Stop() if w.isStopped() { return ErrTaskSchedulerClosed } key := w.options.TaskToChannelKeyFn(task) weight := w.options.ChannelKeyToWeightFn(key) taskCh, releaseFn := w.pool.GetOrCreateChannel(key, weight) defer releaseFn() select { case taskCh <- task: w.notifyDispatcher() if w.isStopped() { drainAndNackPriorityTask(taskCh) } return nil case <-w.ctx.Done(): return ErrTaskSchedulerClosed } } func (w *weightedRoundRobinTaskSchedulerImpl[K, T]) TrySubmit( task T, ) (bool, error) { if w.isStopped() { return false, ErrTaskSchedulerClosed } key := w.options.TaskToChannelKeyFn(task) weight := w.options.ChannelKeyToWeightFn(key) taskCh, releaseFn := w.pool.GetOrCreateChannel(key, weight) defer releaseFn() select { case taskCh <- task: w.metricsScope.IncCounter(metrics.PriorityTaskSubmitRequest) if w.isStopped() { drainAndNackPriorityTask(taskCh) } else { w.notifyDispatcher() } return true, nil case <-w.ctx.Done(): return false, ErrTaskSchedulerClosed default: return false, nil } } func (w *weightedRoundRobinTaskSchedulerImpl[K, T]) dispatcher() { defer w.dispatcherWG.Done() for { select { case <-w.notifyCh: w.dispatchTasks() case <-w.ctx.Done(): return } } } func (w *weightedRoundRobinTaskSchedulerImpl[K, T]) dispatchTasks() { hasTask := true for hasTask { hasTask = false schedule := w.pool.GetSchedule() // Create a new iterator for this dispatch cycle iter := schedule.NewIterator() // Iterate through the schedule using the iterator for ch, ok := iter.TryNext(); ok; ch, ok = iter.TryNext() { select { case task := <-ch.c: hasTask = true if err := w.processor.Submit(task); err != nil { w.logger.Error("fail to submit task to processor", tag.Error(err)) task.Nack(err) } case <-w.ctx.Done(): return default: } } } } func (w *weightedRoundRobinTaskSchedulerImpl[K, T]) notifyDispatcher() { select { case w.notifyCh <- struct{}{}: // sent a notification to the dispatcher default: // do not block if there's already a notification } } func (w *weightedRoundRobinTaskSchedulerImpl[K, T]) isStopped() bool { return atomic.LoadInt32(&w.status) == common.DaemonStatusStopped } func drainAndNackPriorityTask[T Task](taskCh <-chan T) { for { select { case task := <-taskCh: task.Nack(nil) default: return } } } ================================================ FILE: common/task/weighted_round_robin_task_scheduler_options.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "fmt" ) // WeightedRoundRobinTaskSchedulerOptions configs WRR task scheduler type WeightedRoundRobinTaskSchedulerOptions[K comparable, T Task] struct { QueueSize int DispatcherCount int TaskToChannelKeyFn func(T) K ChannelKeyToWeightFn func(K) int } func (o *WeightedRoundRobinTaskSchedulerOptions[K, T]) String() string { return fmt.Sprintf("{QueueSize: %v, DispatcherCount: %v}", o.QueueSize, o.DispatcherCount) } ================================================ FILE: common/task/weighted_round_robin_task_scheduler_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "errors" "fmt" "math/rand" "sync" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ) type ( weightedRoundRobinTaskSchedulerSuite struct { *require.Assertions suite.Suite controller *gomock.Controller mockProcessor *MockProcessor realProcessor Processor queueSize int scheduler *weightedRoundRobinTaskSchedulerImpl[int, PriorityTask] } mockPriorityTaskMatcher struct { task *MockPriorityTask } ) var ( testSchedulerWeights = dynamicproperties.GetMapPropertyFn( map[string]interface{}{ "0": 3, "1": 2, "2": 1, }, ) ) func TestWeightedRoundRobinTaskSchedulerSuite(t *testing.T) { s := new(weightedRoundRobinTaskSchedulerSuite) suite.Run(t, s) } func (s *weightedRoundRobinTaskSchedulerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockProcessor = NewMockProcessor(s.controller) s.queueSize = 1000 s.realProcessor = NewParallelTaskProcessor( testlogger.New(s.Suite.T()), metrics.NewClient(tally.NoopScope, metrics.Common, metrics.HistogramMigration{}), &ParallelTaskProcessorOptions{ QueueSize: 1, WorkerCount: dynamicproperties.GetIntPropertyFn(1), RetryPolicy: backoff.NewExponentialRetryPolicy(time.Millisecond), }, ) s.scheduler = s.newTestWeightedRoundRobinTaskScheduler( &WeightedRoundRobinTaskSchedulerOptions[int, PriorityTask]{ QueueSize: s.queueSize, DispatcherCount: 3, TaskToChannelKeyFn: func(task PriorityTask) int { return task.Priority() }, ChannelKeyToWeightFn: func(key int) int { return testSchedulerWeights()[fmt.Sprintf("%v", key)].(int) }, }, ) } func (s *weightedRoundRobinTaskSchedulerSuite) TearDownTest() { s.controller.Finish() } func (s *weightedRoundRobinTaskSchedulerSuite) TestSubmit_Success() { taskPriority := 1 mockTask := NewMockPriorityTask(s.controller) mockTask.EXPECT().Priority().Return(taskPriority) err := s.scheduler.Submit(mockTask) s.NoError(err) weight := s.scheduler.options.ChannelKeyToWeightFn(taskPriority) taskCh, releaseFn := s.scheduler.pool.GetOrCreateChannel(taskPriority, weight) defer releaseFn() task := <-taskCh s.Equal(mockTask, task) taskChs := s.scheduler.pool.GetAllChannels() for _, taskCh := range taskChs { s.Empty(taskCh) } } func (s *weightedRoundRobinTaskSchedulerSuite) TestSubmit_Fail_SchedulerShutDown() { // create a new scheduler here with queue size 0, otherwise test is non-deterministic scheduler := s.newTestWeightedRoundRobinTaskScheduler( &WeightedRoundRobinTaskSchedulerOptions[int, PriorityTask]{ QueueSize: 0, DispatcherCount: 3, }, ) mockTask := NewMockPriorityTask(s.controller) scheduler.Start() scheduler.Stop() err := scheduler.Submit(mockTask) s.Equal(ErrTaskSchedulerClosed, err) } func (s *weightedRoundRobinTaskSchedulerSuite) TestTrySubmit() { taskPriority := 1 for i := 0; i != s.queueSize; i++ { mockTask := NewMockPriorityTask(s.controller) mockTask.EXPECT().Priority().Return(taskPriority) submitted, err := s.scheduler.TrySubmit(mockTask) s.NoError(err) s.True(submitted) } // now the queue is full, submit one more task, should be non-blocking mockTask := NewMockPriorityTask(s.controller) mockTask.EXPECT().Priority().Return(taskPriority) submitted, err := s.scheduler.TrySubmit(mockTask) s.NoError(err) s.False(submitted) } func (s *weightedRoundRobinTaskSchedulerSuite) TestDispatcher_SubmitWithNoError() { weights, err := dynamicproperties.ConvertDynamicConfigMapPropertyToIntMap(testSchedulerWeights()) s.NoError(err) numPriorities := len(weights) tasks := [][]*MockPriorityTask{} var taskWG sync.WaitGroup for i := 0; i != numPriorities; i++ { tasks = append(tasks, []*MockPriorityTask{}) } taskPerPriority := 5 numSubmittedTask := 0 tasksPerRound := []int{6, 5, 2, 1, 1} round := 0 mockFn := func(_ Task) error { numSubmittedTask++ if numSubmittedTask == tasksPerRound[round] { round++ numSubmittedTask = 0 for priority, weight := range weights { expectedRemainingTasksNum := taskPerPriority - round*weight if expectedRemainingTasksNum < 0 { expectedRemainingTasksNum = 0 } taskCh, releaseFn := s.scheduler.pool.GetOrCreateChannel(priority, weight) s.Equal(expectedRemainingTasksNum, len(taskCh)) releaseFn() } } taskWG.Done() return nil } for priority := range weights { for i := 0; i != taskPerPriority; i++ { mockTask := NewMockPriorityTask(s.controller) mockTask.EXPECT().Priority().Return(priority).AnyTimes() s.scheduler.Submit(mockTask) tasks[priority] = append(tasks[priority], mockTask) taskWG.Add(1) s.mockProcessor.EXPECT().Submit(newMockPriorityTaskMatcher(mockTask)).DoAndReturn(mockFn) } } s.scheduler.processor = s.mockProcessor doneCh := make(chan struct{}) go func() { s.scheduler.dispatcherWG.Add(1) s.scheduler.dispatcher() close(doneCh) }() taskWG.Wait() s.scheduler.cancel() <-doneCh } func (s *weightedRoundRobinTaskSchedulerSuite) TestDispatcher_FailToSubmit() { mockTask := NewMockPriorityTask(s.controller) mockTask.EXPECT().Priority().Return(0) mockTask.EXPECT().Nack(gomock.Any()) var taskWG sync.WaitGroup s.scheduler.Submit(mockTask) taskWG.Add(1) mockFn := func(_ Task) error { taskWG.Done() return errors.New("some random error") } s.mockProcessor.EXPECT().Submit(newMockPriorityTaskMatcher(mockTask)).DoAndReturn(mockFn) s.scheduler.processor = s.mockProcessor doneCh := make(chan struct{}) go func() { s.scheduler.dispatcherWG.Add(1) s.scheduler.dispatcher() close(doneCh) }() taskWG.Wait() s.scheduler.cancel() <-doneCh } func (s *weightedRoundRobinTaskSchedulerSuite) TestWRR() { numTasks := 1000 var taskWG sync.WaitGroup tasks := []PriorityTask{} mockFn := func(_ Task) error { taskWG.Done() return nil } for i := 0; i != numTasks; i++ { mockTask := NewMockPriorityTask(s.controller) mockTask.EXPECT().Priority().Return(rand.Intn(len(testSchedulerWeights()))).Times(1) tasks = append(tasks, mockTask) taskWG.Add(1) s.mockProcessor.EXPECT().Submit(newMockPriorityTaskMatcher(mockTask)).DoAndReturn(mockFn) } s.scheduler.processor = s.mockProcessor s.scheduler.Start() for _, task := range tasks { if rand.Intn(2) == 0 { s.NoError(s.scheduler.Submit(task)) } else { submitted, err := s.scheduler.TrySubmit(task) s.NoError(err) s.True(submitted) } } taskWG.Wait() s.scheduler.Stop() } func (s *weightedRoundRobinTaskSchedulerSuite) TestSchedulerContract() { testSchedulerContract(s.Assertions, s.controller, s.scheduler, s.realProcessor) } func (s *weightedRoundRobinTaskSchedulerSuite) newTestWeightedRoundRobinTaskScheduler( options *WeightedRoundRobinTaskSchedulerOptions[int, PriorityTask], ) *weightedRoundRobinTaskSchedulerImpl[int, PriorityTask] { scheduler, err := NewWeightedRoundRobinTaskScheduler( testlogger.New(s.Suite.T()), metrics.NewClient(tally.NoopScope, metrics.Common, metrics.HistogramMigration{}), clock.NewMockedTimeSource(), s.realProcessor, options, ) s.NoError(err) return scheduler.(*weightedRoundRobinTaskSchedulerImpl[int, PriorityTask]) } func testSchedulerContract( s *require.Assertions, controller *gomock.Controller, scheduler Scheduler[PriorityTask], processor Processor, ) { numTasks := 10000 var taskWG sync.WaitGroup tasks := []PriorityTask{} taskStatusLock := &sync.Mutex{} taskStatus := make(map[PriorityTask]State) for i := 0; i != numTasks; i++ { mockTask := NewMockPriorityTask(controller) taskStatus[mockTask] = TaskStatePending mockTask.EXPECT().Priority().Return(rand.Intn(len(testSchedulerWeights()))).MaxTimes(1) mockTask.EXPECT().Execute().Return(nil).MaxTimes(1) mockTask.EXPECT().Ack().Do(func() { taskStatusLock.Lock() defer taskStatusLock.Unlock() s.Equal(TaskStatePending, taskStatus[mockTask]) taskStatus[mockTask] = TaskStateAcked taskWG.Done() }).MaxTimes(1) mockTask.EXPECT().Nack(gomock.Any()).Do(func(err error) { taskStatusLock.Lock() defer taskStatusLock.Unlock() s.Equal(TaskStatePending, taskStatus[mockTask]) taskStatus[mockTask] = State(-1) // set it to whatever state that is not TaskStatePending taskWG.Done() }).MaxTimes(1) tasks = append(tasks, mockTask) taskWG.Add(1) } if processor != nil { processor.Start() } scheduler.Start() go func() { time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond) scheduler.Stop() if processor != nil { processor.Stop() } }() for _, task := range tasks { if rand.Intn(2) == 0 { if err := scheduler.Submit(task); err != nil { taskWG.Done() taskStatusLock.Lock() delete(taskStatus, task) taskStatusLock.Unlock() } } else { if submitted, _ := scheduler.TrySubmit(task); !submitted { taskWG.Done() taskStatusLock.Lock() delete(taskStatus, task) taskStatusLock.Unlock() } } } s.True(common.AwaitWaitGroup(&taskWG, 10*time.Second)) switch schedulerImpl := scheduler.(type) { case *fifoTaskSchedulerImpl[PriorityTask]: <-schedulerImpl.ctx.Done() case *weightedRoundRobinTaskSchedulerImpl[int, PriorityTask]: <-schedulerImpl.ctx.Done() case *hierarchicalWeightedRoundRobinTaskSchedulerImpl[string, PriorityTask]: <-schedulerImpl.ctx.Done() default: s.Fail("unknown task scheduler type") } for _, status := range taskStatus { s.NotEqual(TaskStatePending, status) } } func newMockPriorityTaskMatcher(mockTask *MockPriorityTask) gomock.Matcher { return &mockPriorityTaskMatcher{ task: mockTask, } } func (m *mockPriorityTaskMatcher) Matches(x interface{}) bool { taskPtr, ok := x.(*MockPriorityTask) if !ok { return false } return taskPtr == m.task } func (m *mockPriorityTaskMatcher) String() string { return fmt.Sprintf("is equal to %v", m.task) } ================================================ FILE: common/taskTokenSerializerInterfaces.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination taskTokenSerializerInterfaces_mock.go -self_package github.com/uber/cadence/common package common type ( // TaskTokenSerializer serializes task tokens TaskTokenSerializer interface { Serialize(token *TaskToken) ([]byte, error) Deserialize(data []byte) (*TaskToken, error) SerializeQueryTaskToken(token *QueryTaskToken) ([]byte, error) DeserializeQueryTaskToken(data []byte) (*QueryTaskToken, error) } // TaskToken identifies a task TaskToken struct { DomainID string `json:"domainId"` WorkflowID string `json:"workflowId"` WorkflowType string `json:"workflowType"` RunID string `json:"runId"` ScheduleID int64 `json:"scheduleId"` ScheduleAttempt int64 `json:"scheduleAttempt"` ActivityID string `json:"activityId"` ActivityType string `json:"activityType"` } // QueryTaskToken identifies a query task QueryTaskToken struct { DomainID string `json:"domainId"` WorkflowID string `json:"workflowId"` RunID string `json:"runId"` TaskList string `json:"taskList"` TaskID string `json:"taskId"` } ) func (t TaskToken) GetDomainID() string { return t.DomainID } func (t QueryTaskToken) GetDomainID() string { return t.DomainID } ================================================ FILE: common/taskTokenSerializerInterfaces_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: taskTokenSerializerInterfaces.go // // Generated by this command: // // mockgen -package common -source taskTokenSerializerInterfaces.go -destination taskTokenSerializerInterfaces_mock.go -self_package github.com/uber/cadence/common // // Package common is a generated GoMock package. package common import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockTaskTokenSerializer is a mock of TaskTokenSerializer interface. type MockTaskTokenSerializer struct { ctrl *gomock.Controller recorder *MockTaskTokenSerializerMockRecorder isgomock struct{} } // MockTaskTokenSerializerMockRecorder is the mock recorder for MockTaskTokenSerializer. type MockTaskTokenSerializerMockRecorder struct { mock *MockTaskTokenSerializer } // NewMockTaskTokenSerializer creates a new mock instance. func NewMockTaskTokenSerializer(ctrl *gomock.Controller) *MockTaskTokenSerializer { mock := &MockTaskTokenSerializer{ctrl: ctrl} mock.recorder = &MockTaskTokenSerializerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskTokenSerializer) EXPECT() *MockTaskTokenSerializerMockRecorder { return m.recorder } // Deserialize mocks base method. func (m *MockTaskTokenSerializer) Deserialize(data []byte) (*TaskToken, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Deserialize", data) ret0, _ := ret[0].(*TaskToken) ret1, _ := ret[1].(error) return ret0, ret1 } // Deserialize indicates an expected call of Deserialize. func (mr *MockTaskTokenSerializerMockRecorder) Deserialize(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deserialize", reflect.TypeOf((*MockTaskTokenSerializer)(nil).Deserialize), data) } // DeserializeQueryTaskToken mocks base method. func (m *MockTaskTokenSerializer) DeserializeQueryTaskToken(data []byte) (*QueryTaskToken, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeserializeQueryTaskToken", data) ret0, _ := ret[0].(*QueryTaskToken) ret1, _ := ret[1].(error) return ret0, ret1 } // DeserializeQueryTaskToken indicates an expected call of DeserializeQueryTaskToken. func (mr *MockTaskTokenSerializerMockRecorder) DeserializeQueryTaskToken(data any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeserializeQueryTaskToken", reflect.TypeOf((*MockTaskTokenSerializer)(nil).DeserializeQueryTaskToken), data) } // Serialize mocks base method. func (m *MockTaskTokenSerializer) Serialize(token *TaskToken) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Serialize", token) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // Serialize indicates an expected call of Serialize. func (mr *MockTaskTokenSerializerMockRecorder) Serialize(token any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Serialize", reflect.TypeOf((*MockTaskTokenSerializer)(nil).Serialize), token) } // SerializeQueryTaskToken mocks base method. func (m *MockTaskTokenSerializer) SerializeQueryTaskToken(token *QueryTaskToken) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SerializeQueryTaskToken", token) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // SerializeQueryTaskToken indicates an expected call of SerializeQueryTaskToken. func (mr *MockTaskTokenSerializerMockRecorder) SerializeQueryTaskToken(token any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SerializeQueryTaskToken", reflect.TypeOf((*MockTaskTokenSerializer)(nil).SerializeQueryTaskToken), token) } ================================================ FILE: common/taskvalidator/validateworkflow.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Package taskvalidator provides a Work in Progress service for workflow validations. package taskvalidator import ( "context" "errors" "time" "go.uber.org/zap" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/types" ) // Checker interface for initiating the validation process. type Checker interface { WorkflowCheckforValidation(ctx context.Context, workflowID string, domainID string, domainName string, runID string) error } // staleChecker interface definition. type staleChecker interface { CheckAge(response *persistence.GetWorkflowExecutionResponse) (bool, error) } // checkerImpl is the implementation of the Checker interface. type checkerImpl struct { logger *zap.Logger metricsClient metrics.Client dc cache.DomainCache pr persistence.Retryer staleCheck staleChecker } // NewWfChecker creates a new instance of a workflow validation checker. // It requires a logger, metrics client, domain cache, persistence retryer, // and a stale checker implementation to function. func NewWfChecker(logger *zap.Logger, metrics metrics.Client, domainCache cache.DomainCache, executionManager persistence.ExecutionManager, historymanager persistence.HistoryManager) (Checker, error) { // Create the persistence retryer retryPolicy := backoff.NewExponentialRetryPolicy(100 * time.Millisecond) // Adjust as needed pr := persistence.NewPersistenceRetryer(executionManager, historymanager, retryPolicy) // Create the stale check instance staleCheckInstance := invariant.NewStaleWorkflow(pr, domainCache, logger) staleCheck, _ := staleCheckInstance.(staleChecker) // Return the checker implementation return &checkerImpl{ logger: logger, metricsClient: metrics, dc: domainCache, pr: pr, staleCheck: staleCheck, }, nil } // WorkflowCheckforValidation performs workflow validation. func (w *checkerImpl) WorkflowCheckforValidation(ctx context.Context, workflowID string, domainID string, domainName string, runID string) error { w.logger.Info("WorkflowCheckforValidation", zap.String("WorkflowID", workflowID), zap.String("RunID", runID), zap.String("DomainID", domainID), zap.String("DomainName", domainName)) workflowResp, err := w.pr.GetWorkflowExecution(ctx, &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, DomainName: domainName, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, }) if err != nil { w.logger.Error("Error getting workflow execution", zap.Error(err)) return err } // Create an instance of ConcreteExecution concreteExecution := &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: workflowResp.State.ExecutionInfo.DomainID, WorkflowID: workflowResp.State.ExecutionInfo.WorkflowID, RunID: workflowResp.State.ExecutionInfo.RunID, State: workflowResp.State.ExecutionInfo.State, }, } if w.isDomainDeprecated(domainID) { invariant.DeleteExecution(ctx, concreteExecution, w.pr, w.dc) return nil } if w.staleCheck != nil { stale, err := w.staleWorkflowCheck(workflowResp) if err != nil { w.logger.Error("Error checking if the execution is stale", zap.Error(err)) return err } if stale { invariant.DeleteExecution(ctx, concreteExecution, w.pr, w.dc) return nil } } w.metricsClient.Scope(metrics.TaskValidatorScope, metrics.DomainTag(domainName)).IncCounter(metrics.ValidatedWorkflowCount) return nil } // isDomainDeprecated checks if a domain is deprecated. func (w *checkerImpl) isDomainDeprecated(domainID string) bool { domain, err := w.dc.GetDomainByID(domainID) if err != nil { w.logger.Error("Error getting domain by ID", zap.Error(err)) return false } return domain.IsDeprecatedOrDeleted() } // staleWorkflowCheck checks if a workflow is stale and returns any errors encountered. func (w *checkerImpl) staleWorkflowCheck(workflowResp *persistence.GetWorkflowExecutionResponse) (bool, error) { if workflowResp == nil || workflowResp.State == nil || workflowResp.State.ExecutionInfo == nil { w.logger.Error("Invalid workflow execution response received") return false, errors.New("invalid workflow execution response") } pastExpiration, err := w.staleCheck.CheckAge(workflowResp) if err != nil { w.logger.Error("Error during stale workflow check", zap.Error(err)) return false, err } return pastExpiration, nil } ================================================ FILE: common/taskvalidator/validateworkflow_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package taskvalidator import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "go.uber.org/zap" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/constants" ) type mockStaleChecker struct { CheckAgeFunc func(response *persistence.GetWorkflowExecutionResponse) (bool, error) } func (m *mockStaleChecker) CheckAge(response *persistence.GetWorkflowExecutionResponse) (bool, error) { return m.CheckAgeFunc(response) } func TestWorkflowCheckforValidation(t *testing.T) { testCases := []struct { name string workflowID string domainID string domainName string runID string isStale bool simulateError bool }{ {"NonStaleWorkflow", "workflow-1", "domain-1", "domain-name-1", "run-1", false, false}, {"StaleWorkflow", "workflow-2", "domain-2", "domain-name-2", "run-2", true, false}, {"ErrorInGetWorkflowExecution", "workflow-3", "domain-3", "domain-name-3", "run-3", false, true}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockLogger := zap.NewNop() mockMetricsClient := metrics.NewNoopMetricsClient() mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockExecutionManager := persistence.NewMockExecutionManager(mockCtrl) mockHistoryManager := persistence.NewMockHistoryManager(mockCtrl) checker, err := NewWfChecker(mockLogger, mockMetricsClient, mockDomainCache, mockExecutionManager, mockHistoryManager) assert.NoError(t, err, "Failed to create checker") mockDomainCache.EXPECT().GetDomainByID(tc.domainID).Return(constants.TestGlobalDomainEntry, nil).AnyTimes() mockDomainCache.EXPECT().GetDomainName(tc.domainID).Return(tc.domainName, nil).AnyTimes() if tc.isStale { mockExecutionManager.EXPECT().DeleteWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockExecutionManager.EXPECT().DeleteCurrentWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() } mockExecutionManager.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, request *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) { if tc.simulateError { return nil, errors.New("database error") } return &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, }, }, }, nil }).AnyTimes() ctx := context.Background() err = checker.WorkflowCheckforValidation(ctx, tc.workflowID, tc.domainID, tc.domainName, tc.runID) if tc.simulateError { assert.Error(t, err, "Expected error when GetWorkflowExecution fails") } else { assert.NoError(t, err, "Expected no error for valid workflow execution") } }) } } ================================================ FILE: common/testing/allisset.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testing import ( "reflect" "testing" "github.com/stretchr/testify/assert" ) func allIsSet(t *testing.T, err error) { // All the errors are pointers, so we get the value with .Elem errValue := reflect.ValueOf(err).Elem() for i := 0; i < errValue.NumField(); i++ { field := errValue.Field(i) // IsZero checks if the value is the default value (e.g. nil, "", 0 etc) assert.True(t, !field.IsZero(), "Field %s is not set for error %s", errValue.Type().Field(i).Name, errValue.Type()) } } ================================================ FILE: common/testing/allisset_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testing import ( "reflect" "testing" "github.com/uber/cadence/common/types/testdata" ) func TestAllFieldsSetInTestErrors(t *testing.T) { for _, err := range testdata.Errors { name := reflect.TypeOf(err).Elem().Name() t.Run(name, func(t *testing.T) { // Test all fields are set in the error allIsSet(t, err) }) } } ================================================ FILE: common/testing/event_generator.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package testing import "math/rand" const ( emptyCandidateIndex = -1 defaultVersion = int64(100) ) var ( defaultBatchFunc = func(batch []Vertex, history []Vertex) bool { return len(batch) == 0 } ) type ( // HistoryEventVertex represents one type of history event HistoryEventVertex struct { name string isStrictOnNextVertex bool maxNextGeneration int dataFunc func(...interface{}) interface{} data interface{} } // HistoryEventModel is a graph represents relationships among history event types HistoryEventModel struct { edges []Edge } // EventGenerator is a history event generator // The event generator will generate next history event // based on the history event transition graph defined in the model EventGenerator struct { connections map[string][]Edge previousVertices []Vertex leafVertices []Vertex entryVertices []Vertex exitVertices map[string]bool randomEntryVertices []Vertex dice *rand.Rand seed int64 canDoBatch func([]Vertex, []Vertex) bool version int64 } // HistoryEventEdge is the directional edge of two history events HistoryEventEdge struct { startVertex Vertex endVertex Vertex condition func(...interface{}) bool action func() } ) // NewEventGenerator initials the event generator func NewEventGenerator( seed int64, ) Generator { return &EventGenerator{ connections: make(map[string][]Edge), previousVertices: make([]Vertex, 0), leafVertices: make([]Vertex, 0), entryVertices: make([]Vertex, 0), exitVertices: make(map[string]bool), randomEntryVertices: make([]Vertex, 0), dice: rand.New(rand.NewSource(seed)), seed: seed, canDoBatch: defaultBatchFunc, version: defaultVersion, } } // AddInitialEntryVertex adds the initial history event vertices // Generator will only start from one of the entry vertex func (g *EventGenerator) AddInitialEntryVertex( entry ...Vertex, ) { g.entryVertices = append( g.entryVertices, entry...) } // AddExitVertex adds the terminate history event vertex func (g *EventGenerator) AddExitVertex( exit ...Vertex, ) { for _, v := range exit { g.exitVertices[v.GetName()] = true } } // AddRandomEntryVertex adds the random history event vertex func (g *EventGenerator) AddRandomEntryVertex( exit ...Vertex, ) { g.randomEntryVertices = append(g.randomEntryVertices, exit...) } // AddModel adds a history event model func (g *EventGenerator) AddModel( model Model, ) { for _, e := range model.ListEdges() { if _, ok := g.connections[e.GetStartVertex().GetName()]; !ok { g.connections[e.GetStartVertex().GetName()] = make([]Edge, 0) } g.connections[e.GetStartVertex().GetName()] = append(g.connections[e.GetStartVertex().GetName()], e) } } // ListGeneratedVertices returns all the generated history events func (g EventGenerator) ListGeneratedVertices() []Vertex { return g.previousVertices } // HasNextVertex checks if there is accessible history event vertex func (g *EventGenerator) HasNextVertex() bool { for _, prev := range g.previousVertices { if _, ok := g.exitVertices[prev.GetName()]; ok { return false } } return len(g.leafVertices) > 0 || (len(g.previousVertices) == 0 && len(g.entryVertices) > 0) } // GetNextVertices generates a batch of history events happened in the same transaction func (g *EventGenerator) GetNextVertices() []Vertex { if !g.HasNextVertex() { panic("Generator reached to a terminate state.") } batch := make([]Vertex, 0) for g.HasNextVertex() && g.canDoBatch(batch, g.previousVertices) { res := g.generateNextEventBatch() g.updateContext(res) batch = append(batch, res...) } return batch } // Reset reset the generator to the initial state func (g *EventGenerator) Reset() { g.leafVertices = make([]Vertex, 0) g.previousVertices = make([]Vertex, 0) g.version = defaultVersion } // DeepCopy copy a new instance of generator func (g *EventGenerator) DeepCopy() Generator { return &EventGenerator{ connections: copyConnections(g.connections), previousVertices: copyVertex(g.previousVertices), leafVertices: copyVertex(g.leafVertices), entryVertices: copyVertex(g.entryVertices), exitVertices: copyExitVertices(g.exitVertices), randomEntryVertices: copyVertex(g.randomEntryVertices), dice: rand.New(rand.NewSource(g.seed)), seed: g.seed, canDoBatch: g.canDoBatch, version: g.version, } } // SetBatchGenerationRule sets a function to determine next generated batch of history events func (g *EventGenerator) SetBatchGenerationRule( canDoBatchFunc func([]Vertex, []Vertex) bool, ) { g.canDoBatch = canDoBatchFunc } // SetVersion sets the event version func (g *EventGenerator) SetVersion(version int64) { g.version = version } // GetVersion returns event version func (g *EventGenerator) GetVersion() int64 { return g.version } func (g *EventGenerator) generateNextEventBatch() []Vertex { batch := make([]Vertex, 0) switch { case len(g.previousVertices) == 0: // Generate for the first time, get the event candidates from entry vertex group batch = append(batch, g.getEntryVertex()) case len(g.randomEntryVertices) > 0 && g.dice.Intn(len(g.connections)) == 0: // Get the event candidate from random vertex group batch = append(batch, g.getRandomVertex()) default: // Get the event candidates based on context idx := g.getVertexCandidate() batch = append(batch, g.randomNextVertex(idx)...) g.leafVertices = append(g.leafVertices[:idx], g.leafVertices[idx+1:]...) } return batch } func (g *EventGenerator) updateContext( batch []Vertex, ) { g.leafVertices = append(g.leafVertices, batch...) g.previousVertices = append(g.previousVertices, batch...) } func (g *EventGenerator) getEntryVertex() Vertex { if len(g.entryVertices) == 0 { panic("No possible start vertex to go to next step") } nextRange := len(g.entryVertices) nextIdx := g.dice.Intn(nextRange) vertex := g.entryVertices[nextIdx].DeepCopy() vertex.GenerateData(nil) return vertex } func (g *EventGenerator) getRandomVertex() Vertex { if len(g.randomEntryVertices) == 0 { panic("No possible vertex to go to next step") } nextRange := len(g.randomEntryVertices) nextIdx := g.dice.Intn(nextRange) vertex := g.randomEntryVertices[nextIdx].DeepCopy() parentEvent := g.previousVertices[len(g.previousVertices)-1] vertex.GenerateData(parentEvent.GetData(), parentEvent.GetData(), g.version) return vertex } func (g *EventGenerator) getVertexCandidate() int { if len(g.leafVertices) == 0 { panic("No possible vertex to go to next step") } nextRange := len(g.leafVertices) notAvailable := make(map[int]bool) var nextVertexIdx int for len(notAvailable) < nextRange { nextVertexIdx = g.dice.Intn(nextRange) // If the vertex is not accessible at this state, skip it if _, ok := notAvailable[nextVertexIdx]; ok { continue } isAccessible, nextVertexIdx := g.findAccessibleVertex(nextVertexIdx) if isAccessible { return nextVertexIdx } notAvailable[nextVertexIdx] = true } // If all history event cannot be accessible, which means the model is incorrect panic("cannot find available history event to proceed. please check your model") } func (g *EventGenerator) findAccessibleVertex( vertexIndex int, ) (bool, int) { candidate := g.leafVertices[vertexIndex] if g.leafVertices[len(g.leafVertices)-1].IsStrictOnNextVertex() { vertexIndex = len(g.leafVertices) - 1 candidate = g.leafVertices[vertexIndex] } neighbors := g.connections[candidate.GetName()] for _, nextV := range neighbors { if nextV.GetCondition() == nil || nextV.GetCondition()(g.previousVertices) { return true, vertexIndex } } return false, emptyCandidateIndex } func (g *EventGenerator) randomNextVertex( nextVertexIdx int, ) []Vertex { nextVertex := g.leafVertices[nextVertexIdx] count := g.dice.Intn(nextVertex.GetMaxNextVertex()) + 1 res := make([]Vertex, 0) latestVertex := g.previousVertices[len(g.previousVertices)-1] for i := 0; i < count; i++ { endVertex := g.pickRandomVertex(nextVertex) endVertex.GenerateData(nextVertex.GetData(), latestVertex.GetData(), g.version) latestVertex = endVertex res = append(res, endVertex) if _, ok := g.exitVertices[endVertex.GetName()]; ok { res = []Vertex{endVertex} return res } } return res } func (g *EventGenerator) pickRandomVertex( nextVertex Vertex, ) Vertex { neighbors := g.connections[nextVertex.GetName()] neighborsRange := len(neighbors) nextIdx := g.dice.Intn(neighborsRange) for neighbors[nextIdx].GetCondition() != nil && !neighbors[nextIdx].GetCondition()(g.previousVertices) { nextIdx = g.dice.Intn(neighborsRange) } newConnection := neighbors[nextIdx] endVertex := newConnection.GetEndVertex() if newConnection.GetAction() != nil { newConnection.GetAction()() } return endVertex.DeepCopy() } // NewHistoryEventEdge initials a new edge between two HistoryEventVertexes func NewHistoryEventEdge( start Vertex, end Vertex, ) Edge { return &HistoryEventEdge{ startVertex: start, endVertex: end, } } // SetStartVertex sets the start vertex func (c *HistoryEventEdge) SetStartVertex( start Vertex, ) { c.startVertex = start } // GetStartVertex returns the start vertex func (c HistoryEventEdge) GetStartVertex() Vertex { return c.startVertex } // SetEndVertex sets the end vertex func (c *HistoryEventEdge) SetEndVertex(end Vertex) { c.endVertex = end } // GetEndVertex returns the end vertex func (c HistoryEventEdge) GetEndVertex() Vertex { return c.endVertex } // SetCondition sets the condition to access this edge func (c *HistoryEventEdge) SetCondition( condition func(...interface{}) bool, ) { c.condition = condition } // GetCondition returns the condition func (c HistoryEventEdge) GetCondition() func(...interface{}) bool { return c.condition } // SetAction sets an action to perform when the end vertex hits func (c *HistoryEventEdge) SetAction(action func()) { c.action = action } // GetAction returns the action func (c HistoryEventEdge) GetAction() func() { return c.action } // DeepCopy copies a new edge func (c *HistoryEventEdge) DeepCopy() Edge { return &HistoryEventEdge{ startVertex: c.startVertex.DeepCopy(), endVertex: c.endVertex.DeepCopy(), condition: c.condition, action: c.action, } } // NewHistoryEventVertex initials a history event vertex func NewHistoryEventVertex( name string, ) Vertex { return &HistoryEventVertex{ name: name, isStrictOnNextVertex: false, maxNextGeneration: 1, } } // GetName returns the name func (he HistoryEventVertex) GetName() string { return he.name } // SetName sets the name func (he *HistoryEventVertex) SetName( name string, ) { he.name = name } // Equals compares two vertex // func (he *HistoryEventVertex) Equals( // v Vertex, // ) bool { // // return strings.EqualFold(he.name, v.GetName()) && he.data == v.GetData() // } // SetIsStrictOnNextVertex sets if a vertex can be added between the current vertex and its child Vertices func (he *HistoryEventVertex) SetIsStrictOnNextVertex( isStrict bool, ) { he.isStrictOnNextVertex = isStrict } // IsStrictOnNextVertex returns the isStrict flag func (he HistoryEventVertex) IsStrictOnNextVertex() bool { return he.isStrictOnNextVertex } // SetMaxNextVertex sets the max concurrent path can be generated from this vertex func (he *HistoryEventVertex) SetMaxNextVertex( maxNextGeneration int, ) { if maxNextGeneration < 1 { panic("max next vertex number cannot less than 1") } he.maxNextGeneration = maxNextGeneration } // GetMaxNextVertex returns the max concurrent path func (he HistoryEventVertex) GetMaxNextVertex() int { return he.maxNextGeneration } // SetDataFunc sets the data generation function func (he *HistoryEventVertex) SetDataFunc( dataFunc func(...interface{}) interface{}, ) { he.dataFunc = dataFunc } // GetDataFunc returns the data generation function func (he HistoryEventVertex) GetDataFunc() func(...interface{}) interface{} { return he.dataFunc } // GenerateData generates the data and return func (he *HistoryEventVertex) GenerateData( input ...interface{}, ) interface{} { if he.dataFunc == nil { return nil } he.data = he.dataFunc(input...) return he.data } // GetData returns the vertex data func (he HistoryEventVertex) GetData() interface{} { return he.data } // DeepCopy returns the a deep copy of vertex func (he HistoryEventVertex) DeepCopy() Vertex { return &HistoryEventVertex{ name: he.GetName(), isStrictOnNextVertex: he.IsStrictOnNextVertex(), maxNextGeneration: he.GetMaxNextVertex(), dataFunc: he.GetDataFunc(), data: he.GetData(), } } // NewHistoryEventModel initials new history event model func NewHistoryEventModel() Model { return &HistoryEventModel{ edges: make([]Edge, 0), } } // AddEdge adds an edge to the model func (m *HistoryEventModel) AddEdge( edge ...Edge, ) { m.edges = append(m.edges, edge...) } // ListEdges returns all added edges func (m HistoryEventModel) ListEdges() []Edge { return m.edges } ================================================ FILE: common/testing/generator_interface.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package testing type ( // Model represents a state transition graph that contains all the relationships of Vertex Model interface { AddEdge(...Edge) ListEdges() []Edge } // Generator generates a sequence of vertices based on the defined models // It must define InitialEntryVertex and ExitVertex // To use a generator: // for generator.HasNextVertex { // generator.GetNextVertices // } Generator interface { // InitialEntryVertex is the beginning vertices of the graph // Only one vertex will be picked as the entry AddInitialEntryVertex(...Vertex) // ExitVertex is the terminate vertices of the graph AddExitVertex(...Vertex) // RandomEntryVertex is a random entry point which can be access at any state of the generator AddRandomEntryVertex(...Vertex) // AddModel loads model into the generator // AddModel can load multiple models and models will be joint if there is common vertices AddModel(Model) // HasNextVertex determines if there is more vertex to generate HasNextVertex() bool // GetNextVertices generates next vertex batch GetNextVertices() []Vertex // ListGeneratedVertices lists the pasted generated vertices ListGeneratedVertices() []Vertex // Reset cleans up all the internal states and reset to a brand new generator Reset() // DeepCopy copy a new instance of generator DeepCopy() Generator // SetBatchGenerationRule sets a function that used in GetNextVertex to return batch result SetBatchGenerationRule(func([]Vertex, []Vertex) bool) // SetVersion sets the event version SetVersion(int64) // GetVersion gets the event version GetVersion() int64 } // Vertex represents a state in the model. A state represents a type of an Cadence event Vertex interface { // The name of the vertex. Usually, this will be the Cadence event type SetName(string) GetName() string // Equals(Vertex) bool // IsStrictOnNextVertex means if the vertex must be followed by its children // When IsStrictOnNextVertex set to true, it means this event can only follow by its neighbors SetIsStrictOnNextVertex(bool) IsStrictOnNextVertex() bool // MaxNextVertex means the max neighbors can branch out from this vertex SetMaxNextVertex(int) GetMaxNextVertex() int // SetVertexDataFunc sets a function to generate end vertex data SetDataFunc(func(...interface{}) interface{}) GetDataFunc() func(...interface{}) interface{} GenerateData(...interface{}) interface{} GetData() interface{} DeepCopy() Vertex } // Edge is the connection between two vertices Edge interface { // StartVertex is the head of the connection SetStartVertex(Vertex) GetStartVertex() Vertex // EndVertex is the end of the connection SetEndVertex(Vertex) GetEndVertex() Vertex // Condition defines a function to determine if this connection is accessible SetCondition(func(...interface{}) bool) GetCondition() func(...interface{}) bool // Action defines function to perform when the end vertex reached SetAction(func()) GetAction() func() DeepCopy() Edge } ) ================================================ FILE: common/testing/history_event_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package testing import ( "fmt" "testing" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/types" ) type ( historyEventTestSuit struct { suite.Suite generator Generator } ) func TestHistoryEventTestSuite(t *testing.T) { suite.Run(t, new(historyEventTestSuit)) } func (s *historyEventTestSuit) SetupSuite() { s.generator = InitializeHistoryEventGenerator("domain", 1) } func (s *historyEventTestSuit) SetupTest() { s.generator.Reset() } // This is a sample about how to use the generator func (s *historyEventTestSuit) Test_HistoryEvent_Generator() { maxEventID := int64(0) maxVersion := int64(1) maxTaskID := int64(1) for i := 0; i < 10 && s.generator.HasNextVertex(); i++ { events := s.generator.GetNextVertices() fmt.Println("########################") for _, e := range events { event := e.GetData().(*types.HistoryEvent) if maxEventID != event.ID-1 { s.Fail("event id sequence is incorrect") } maxEventID = event.ID if maxVersion > event.Version { s.Fail("event version is incorrect") } maxVersion = event.Version if maxTaskID > event.TaskID { s.Fail("event task id is incorrect") } maxTaskID = event.TaskID fmt.Println(e.GetName()) fmt.Println(event.ID) } } s.NotEmpty(s.generator.ListGeneratedVertices()) fmt.Println("==========================") branchGenerator1 := s.generator.DeepCopy() for i := 0; i < 10 && branchGenerator1.HasNextVertex(); i++ { events := branchGenerator1.GetNextVertices() fmt.Println("########################") for _, e := range events { event := e.GetData().(*types.HistoryEvent) if maxEventID != event.ID-1 { s.Fail("event id sequence is incorrect") } maxEventID = event.ID if maxVersion > event.Version { s.Fail("event version is incorrect") } maxVersion = event.Version if maxTaskID > event.TaskID { s.Fail("event task id is incorrect") } maxTaskID = event.TaskID fmt.Println(e.GetName()) fmt.Println(event.ID) } } fmt.Println("==========================") history := s.generator.ListGeneratedVertices() maxEventID = history[len(history)-1].GetData().(*types.HistoryEvent).ID for i := 0; i < 10 && s.generator.HasNextVertex(); i++ { events := s.generator.GetNextVertices() fmt.Println("########################") for _, e := range events { event := e.GetData().(*types.HistoryEvent) if maxEventID != event.ID-1 { s.Fail("event id sequence is incorrect") } maxEventID = event.ID if maxVersion > event.Version { s.Fail("event version is incorrect") } maxVersion = event.Version if maxTaskID > event.TaskID { s.Fail("event task id is incorrect") } maxTaskID = event.TaskID fmt.Println(e.GetName()) fmt.Println(event.ID) } } } ================================================ FILE: common/testing/history_event_util.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package testing import ( "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) const ( timeout = int32(10000) signal = "NDC signal" checksum = "NDC checksum" childWorkflowPrefix = "child-" reason = "NDC reason" workflowType = "test-workflow-type" taskList = "taskList" identity = "identity" decisionTaskAttempts = 0 childWorkflowID = "child-WorkflowID" externalWorkflowID = "external-WorkflowID" ) var ( globalTaskID int64 = 1 ) // InitializeHistoryEventGenerator initializes the history event generator func InitializeHistoryEventGenerator( domain string, defaultVersion int64, ) Generator { generator := NewEventGenerator(time.Now().UnixNano()) generator.SetVersion(defaultVersion) // Functions notPendingDecisionTask := func(input ...interface{}) bool { count := 0 history := input[0].([]Vertex) for _, e := range history { switch e.GetName() { case types.EventTypeDecisionTaskScheduled.String(): count++ case types.EventTypeDecisionTaskCompleted.String(), types.EventTypeDecisionTaskFailed.String(), types.EventTypeDecisionTaskTimedOut.String(): count-- } } return count <= 0 } containActivityComplete := func(input ...interface{}) bool { history := input[0].([]Vertex) for _, e := range history { if e.GetName() == types.EventTypeActivityTaskCompleted.String() { return true } } return false } hasPendingActivity := func(input ...interface{}) bool { count := 0 history := input[0].([]Vertex) for _, e := range history { switch e.GetName() { case types.EventTypeActivityTaskScheduled.String(): count++ case types.EventTypeActivityTaskCanceled.String(), types.EventTypeActivityTaskFailed.String(), types.EventTypeActivityTaskTimedOut.String(), types.EventTypeActivityTaskCompleted.String(): count-- } } return count > 0 } canDoBatch := func(currentBatch []Vertex, history []Vertex) bool { if len(currentBatch) == 0 { return true } hasPendingDecisionTask := false for _, event := range history { switch event.GetName() { case types.EventTypeDecisionTaskScheduled.String(): hasPendingDecisionTask = true case types.EventTypeDecisionTaskCompleted.String(), types.EventTypeDecisionTaskFailed.String(), types.EventTypeDecisionTaskTimedOut.String(): hasPendingDecisionTask = false } } if hasPendingDecisionTask { return false } if currentBatch[len(currentBatch)-1].GetName() == types.EventTypeDecisionTaskScheduled.String() { return false } if currentBatch[0].GetName() == types.EventTypeDecisionTaskCompleted.String() { return len(currentBatch) == 1 } return true } // Setup decision task model decisionModel := NewHistoryEventModel() decisionSchedule := NewHistoryEventVertex(types.EventTypeDecisionTaskScheduled.String()) decisionSchedule.SetDataFunc(func(input ...interface{}) interface{} { lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeDecisionTaskScheduled.Ptr() historyEvent.DecisionTaskScheduledEventAttributes = &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{ Name: taskList, Kind: types.TaskListKindNormal.Ptr(), }, StartToCloseTimeoutSeconds: common.Int32Ptr(timeout), Attempt: decisionTaskAttempts, } return historyEvent }) decisionStart := NewHistoryEventVertex(types.EventTypeDecisionTaskStarted.String()) decisionStart.SetIsStrictOnNextVertex(true) decisionStart.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeDecisionTaskStarted.Ptr() historyEvent.DecisionTaskStartedEventAttributes = &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: lastEvent.ID, Identity: identity, RequestID: uuid.New(), } return historyEvent }) decisionFail := NewHistoryEventVertex(types.EventTypeDecisionTaskFailed.String()) decisionFail.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeDecisionTaskFailed.Ptr() historyEvent.DecisionTaskFailedEventAttributes = &types.DecisionTaskFailedEventAttributes{ ScheduledEventID: lastEvent.GetDecisionTaskStartedEventAttributes().ScheduledEventID, StartedEventID: lastEvent.ID, Cause: types.DecisionTaskFailedCauseUnhandledDecision.Ptr(), Identity: identity, ForkEventVersion: version, } return historyEvent }) decisionTimedOut := NewHistoryEventVertex(types.EventTypeDecisionTaskTimedOut.String()) decisionTimedOut.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeDecisionTaskTimedOut.Ptr() historyEvent.DecisionTaskTimedOutEventAttributes = &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: lastEvent.GetDecisionTaskStartedEventAttributes().ScheduledEventID, StartedEventID: lastEvent.ID, TimeoutType: types.TimeoutTypeScheduleToStart.Ptr(), } return historyEvent }) decisionComplete := NewHistoryEventVertex(types.EventTypeDecisionTaskCompleted.String()) decisionComplete.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeDecisionTaskCompleted.Ptr() historyEvent.DecisionTaskCompletedEventAttributes = &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: lastEvent.GetDecisionTaskStartedEventAttributes().ScheduledEventID, StartedEventID: lastEvent.ID, Identity: identity, BinaryChecksum: checksum, } return historyEvent }) decisionComplete.SetIsStrictOnNextVertex(true) decisionComplete.SetMaxNextVertex(2) decisionScheduleToStart := NewHistoryEventEdge(decisionSchedule, decisionStart) decisionStartToComplete := NewHistoryEventEdge(decisionStart, decisionComplete) decisionStartToFail := NewHistoryEventEdge(decisionStart, decisionFail) decisionStartToTimedOut := NewHistoryEventEdge(decisionStart, decisionTimedOut) decisionFailToSchedule := NewHistoryEventEdge(decisionFail, decisionSchedule) decisionFailToSchedule.SetCondition(notPendingDecisionTask) decisionTimedOutToSchedule := NewHistoryEventEdge(decisionTimedOut, decisionSchedule) decisionTimedOutToSchedule.SetCondition(notPendingDecisionTask) decisionModel.AddEdge(decisionScheduleToStart, decisionStartToComplete, decisionStartToFail, decisionStartToTimedOut, decisionFailToSchedule, decisionTimedOutToSchedule) // Setup workflow model workflowModel := NewHistoryEventModel() workflowStart := NewHistoryEventVertex(types.EventTypeWorkflowExecutionStarted.String()) workflowStart.SetDataFunc(func(input ...interface{}) interface{} { historyEvent := getDefaultHistoryEvent(1, defaultVersion) historyEvent.EventType = types.EventTypeWorkflowExecutionStarted.Ptr() historyEvent.WorkflowExecutionStartedEventAttributes = &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{ Name: workflowType, }, TaskList: &types.TaskList{ Name: taskList, Kind: types.TaskListKindNormal.Ptr(), }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(timeout), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(timeout), Identity: identity, FirstExecutionRunID: uuid.New(), } return historyEvent }) workflowSignal := NewHistoryEventVertex(types.EventTypeWorkflowExecutionSignaled.String()) workflowSignal.SetDataFunc(func(input ...interface{}) interface{} { lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeWorkflowExecutionSignaled.Ptr() historyEvent.WorkflowExecutionSignaledEventAttributes = &types.WorkflowExecutionSignaledEventAttributes{ SignalName: signal, Identity: identity, } return historyEvent }) workflowComplete := NewHistoryEventVertex(types.EventTypeWorkflowExecutionCompleted.String()) workflowComplete.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) EventID := lastEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeWorkflowExecutionCompleted.Ptr() historyEvent.WorkflowExecutionCompletedEventAttributes = &types.WorkflowExecutionCompletedEventAttributes{ DecisionTaskCompletedEventID: lastEvent.ID, } return historyEvent }) continueAsNew := NewHistoryEventVertex(types.EventTypeWorkflowExecutionContinuedAsNew.String()) continueAsNew.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) EventID := lastEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeWorkflowExecutionContinuedAsNew.Ptr() historyEvent.WorkflowExecutionContinuedAsNewEventAttributes = &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: uuid.New(), WorkflowType: &types.WorkflowType{ Name: workflowType, }, TaskList: &types.TaskList{ Name: taskList, Kind: types.TaskListKindNormal.Ptr(), }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(timeout), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(timeout), DecisionTaskCompletedEventID: EventID - 1, Initiator: types.ContinueAsNewInitiatorDecider.Ptr(), } return historyEvent }) workflowFail := NewHistoryEventVertex(types.EventTypeWorkflowExecutionFailed.String()) workflowFail.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) EventID := lastEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeWorkflowExecutionFailed.Ptr() historyEvent.WorkflowExecutionFailedEventAttributes = &types.WorkflowExecutionFailedEventAttributes{ DecisionTaskCompletedEventID: lastEvent.ID, } return historyEvent }) workflowCancel := NewHistoryEventVertex(types.EventTypeWorkflowExecutionCanceled.String()) workflowCancel.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeWorkflowExecutionCanceled.Ptr() historyEvent.WorkflowExecutionCanceledEventAttributes = &types.WorkflowExecutionCanceledEventAttributes{ DecisionTaskCompletedEventID: lastEvent.ID, } return historyEvent }) workflowCancelRequest := NewHistoryEventVertex(types.EventTypeWorkflowExecutionCancelRequested.String()) workflowCancelRequest.SetDataFunc(func(input ...interface{}) interface{} { lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeWorkflowExecutionCancelRequested.Ptr() historyEvent.WorkflowExecutionCancelRequestedEventAttributes = &types.WorkflowExecutionCancelRequestedEventAttributes{ Cause: "", ExternalInitiatedEventID: common.Int64Ptr(1), ExternalWorkflowExecution: &types.WorkflowExecution{ WorkflowID: externalWorkflowID, RunID: uuid.New(), }, Identity: identity, } return historyEvent }) workflowTerminate := NewHistoryEventVertex(types.EventTypeWorkflowExecutionTerminated.String()) workflowTerminate.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) EventID := lastEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeWorkflowExecutionTerminated.Ptr() historyEvent.WorkflowExecutionTerminatedEventAttributes = &types.WorkflowExecutionTerminatedEventAttributes{ Identity: identity, Reason: reason, } return historyEvent }) workflowTimedOut := NewHistoryEventVertex(types.EventTypeWorkflowExecutionTimedOut.String()) workflowTimedOut.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) EventID := lastEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeWorkflowExecutionTimedOut.Ptr() historyEvent.WorkflowExecutionTimedOutEventAttributes = &types.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: types.TimeoutTypeStartToClose.Ptr(), } return historyEvent }) workflowStartToSignal := NewHistoryEventEdge(workflowStart, workflowSignal) workflowStartToDecisionSchedule := NewHistoryEventEdge(workflowStart, decisionSchedule) workflowStartToDecisionSchedule.SetCondition(notPendingDecisionTask) workflowSignalToDecisionSchedule := NewHistoryEventEdge(workflowSignal, decisionSchedule) workflowSignalToDecisionSchedule.SetCondition(notPendingDecisionTask) decisionCompleteToWorkflowComplete := NewHistoryEventEdge(decisionComplete, workflowComplete) decisionCompleteToWorkflowComplete.SetCondition(containActivityComplete) decisionCompleteToWorkflowFailed := NewHistoryEventEdge(decisionComplete, workflowFail) decisionCompleteToWorkflowFailed.SetCondition(containActivityComplete) decisionCompleteToCAN := NewHistoryEventEdge(decisionComplete, continueAsNew) decisionCompleteToCAN.SetCondition(containActivityComplete) workflowCancelRequestToCancel := NewHistoryEventEdge(workflowCancelRequest, workflowCancel) workflowModel.AddEdge(workflowStartToSignal, workflowStartToDecisionSchedule, workflowSignalToDecisionSchedule, decisionCompleteToCAN, decisionCompleteToWorkflowComplete, decisionCompleteToWorkflowFailed, workflowCancelRequestToCancel) // Setup activity model activityModel := NewHistoryEventModel() activitySchedule := NewHistoryEventVertex(types.EventTypeActivityTaskScheduled.String()) activitySchedule.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeActivityTaskScheduled.Ptr() historyEvent.ActivityTaskScheduledEventAttributes = &types.ActivityTaskScheduledEventAttributes{ ActivityID: uuid.New(), ActivityType: &types.ActivityType{ Name: "activity", }, Domain: common.StringPtr(domain), TaskList: &types.TaskList{ Name: taskList, Kind: types.TaskListKindNormal.Ptr(), }, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(timeout), ScheduleToStartTimeoutSeconds: common.Int32Ptr(timeout), StartToCloseTimeoutSeconds: common.Int32Ptr(timeout), DecisionTaskCompletedEventID: lastEvent.ID, } return historyEvent }) activityStart := NewHistoryEventVertex(types.EventTypeActivityTaskStarted.String()) activityStart.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeActivityTaskStarted.Ptr() historyEvent.ActivityTaskStartedEventAttributes = &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: lastEvent.ID, Identity: identity, RequestID: uuid.New(), Attempt: 0, } return historyEvent }) activityComplete := NewHistoryEventVertex(types.EventTypeActivityTaskCompleted.String()) activityComplete.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeActivityTaskCompleted.Ptr() historyEvent.ActivityTaskCompletedEventAttributes = &types.ActivityTaskCompletedEventAttributes{ ScheduledEventID: lastEvent.GetActivityTaskStartedEventAttributes().ScheduledEventID, StartedEventID: lastEvent.ID, Identity: identity, } return historyEvent }) activityFail := NewHistoryEventVertex(types.EventTypeActivityTaskFailed.String()) activityFail.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeActivityTaskFailed.Ptr() historyEvent.ActivityTaskFailedEventAttributes = &types.ActivityTaskFailedEventAttributes{ ScheduledEventID: lastEvent.GetActivityTaskStartedEventAttributes().ScheduledEventID, StartedEventID: lastEvent.ID, Identity: identity, Reason: common.StringPtr(reason), } return historyEvent }) activityTimedOut := NewHistoryEventVertex(types.EventTypeActivityTaskTimedOut.String()) activityTimedOut.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeActivityTaskTimedOut.Ptr() historyEvent.ActivityTaskTimedOutEventAttributes = &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: lastEvent.GetActivityTaskStartedEventAttributes().ScheduledEventID, StartedEventID: lastEvent.ID, TimeoutType: types.TimeoutTypeScheduleToClose.Ptr(), } return historyEvent }) activityCancelRequest := NewHistoryEventVertex(types.EventTypeActivityTaskCancelRequested.String()) activityCancelRequest.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeActivityTaskCancelRequested.Ptr() historyEvent.ActivityTaskCancelRequestedEventAttributes = &types.ActivityTaskCancelRequestedEventAttributes{ DecisionTaskCompletedEventID: lastEvent.GetActivityTaskScheduledEventAttributes().DecisionTaskCompletedEventID, ActivityID: lastEvent.GetActivityTaskScheduledEventAttributes().ActivityID, } return historyEvent }) activityCancel := NewHistoryEventVertex(types.EventTypeActivityTaskCanceled.String()) activityCancel.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeActivityTaskCanceled.Ptr() historyEvent.ActivityTaskCanceledEventAttributes = &types.ActivityTaskCanceledEventAttributes{ LatestCancelRequestedEventID: lastEvent.ID, ScheduledEventID: lastEvent.ID, StartedEventID: lastEvent.ID, Identity: identity, } return historyEvent }) activityCancelRequestFail := NewHistoryEventVertex(types.EventTypeRequestCancelActivityTaskFailed.String()) activityCancelRequestFail.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 versionBump := input[2].(int64) subVersion := input[3].(int64) version := lastGeneratedEvent.Version + versionBump + subVersion historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeRequestCancelActivityTaskFailed.Ptr() historyEvent.RequestCancelActivityTaskFailedEventAttributes = &types.RequestCancelActivityTaskFailedEventAttributes{ ActivityID: uuid.New(), DecisionTaskCompletedEventID: lastEvent.GetActivityTaskCancelRequestedEventAttributes().DecisionTaskCompletedEventID, } return historyEvent }) decisionCompleteToATSchedule := NewHistoryEventEdge(decisionComplete, activitySchedule) activityScheduleToStart := NewHistoryEventEdge(activitySchedule, activityStart) activityScheduleToStart.SetCondition(hasPendingActivity) activityStartToComplete := NewHistoryEventEdge(activityStart, activityComplete) activityStartToComplete.SetCondition(hasPendingActivity) activityStartToFail := NewHistoryEventEdge(activityStart, activityFail) activityStartToFail.SetCondition(hasPendingActivity) activityStartToTimedOut := NewHistoryEventEdge(activityStart, activityTimedOut) activityStartToTimedOut.SetCondition(hasPendingActivity) activityCompleteToDecisionSchedule := NewHistoryEventEdge(activityComplete, decisionSchedule) activityCompleteToDecisionSchedule.SetCondition(notPendingDecisionTask) activityFailToDecisionSchedule := NewHistoryEventEdge(activityFail, decisionSchedule) activityFailToDecisionSchedule.SetCondition(notPendingDecisionTask) activityTimedOutToDecisionSchedule := NewHistoryEventEdge(activityTimedOut, decisionSchedule) activityTimedOutToDecisionSchedule.SetCondition(notPendingDecisionTask) activityCancelToDecisionSchedule := NewHistoryEventEdge(activityCancel, decisionSchedule) activityCancelToDecisionSchedule.SetCondition(notPendingDecisionTask) // TODO: bypass activity cancel request event. Support this event later. // activityScheduleToActivityCancelRequest := NewHistoryEventEdge(activitySchedule, activityCancelRequest) // activityScheduleToActivityCancelRequest.SetCondition(hasPendingActivity) activityCancelReqToCancel := NewHistoryEventEdge(activityCancelRequest, activityCancel) activityCancelReqToCancel.SetCondition(hasPendingActivity) activityCancelReqToCancelFail := NewHistoryEventEdge(activityCancelRequest, activityCancelRequestFail) activityCancelRequestFailToDecisionSchedule := NewHistoryEventEdge(activityCancelRequestFail, decisionSchedule) activityCancelRequestFailToDecisionSchedule.SetCondition(notPendingDecisionTask) activityModel.AddEdge(decisionCompleteToATSchedule, activityScheduleToStart, activityStartToComplete, activityStartToFail, activityStartToTimedOut, decisionCompleteToATSchedule, activityCompleteToDecisionSchedule, activityFailToDecisionSchedule, activityTimedOutToDecisionSchedule, activityCancelReqToCancel, activityCancelReqToCancelFail, activityCancelToDecisionSchedule, activityCancelRequestFailToDecisionSchedule) // Setup timer model timerModel := NewHistoryEventModel() timerStart := NewHistoryEventVertex(types.EventTypeTimerStarted.String()) timerStart.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeTimerStarted.Ptr() historyEvent.TimerStartedEventAttributes = &types.TimerStartedEventAttributes{ TimerID: uuid.New(), StartToFireTimeoutSeconds: common.Int64Ptr(10), DecisionTaskCompletedEventID: lastEvent.ID, } return historyEvent }) timerFired := NewHistoryEventVertex(types.EventTypeTimerFired.String()) timerFired.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeTimerFired.Ptr() historyEvent.TimerFiredEventAttributes = &types.TimerFiredEventAttributes{ TimerID: lastEvent.GetTimerStartedEventAttributes().TimerID, StartedEventID: lastEvent.ID, } return historyEvent }) timerCancel := NewHistoryEventVertex(types.EventTypeTimerCanceled.String()) timerCancel.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeTimerCanceled.Ptr() historyEvent.TimerCanceledEventAttributes = &types.TimerCanceledEventAttributes{ TimerID: lastEvent.GetTimerStartedEventAttributes().TimerID, StartedEventID: lastEvent.ID, DecisionTaskCompletedEventID: lastEvent.GetTimerStartedEventAttributes().DecisionTaskCompletedEventID, Identity: identity, } return historyEvent }) timerStartToFire := NewHistoryEventEdge(timerStart, timerFired) timerStartToCancel := NewHistoryEventEdge(timerStart, timerCancel) decisionCompleteToTimerStart := NewHistoryEventEdge(decisionComplete, timerStart) timerFiredToDecisionSchedule := NewHistoryEventEdge(timerFired, decisionSchedule) timerFiredToDecisionSchedule.SetCondition(notPendingDecisionTask) timerCancelToDecisionSchedule := NewHistoryEventEdge(timerCancel, decisionSchedule) timerCancelToDecisionSchedule.SetCondition(notPendingDecisionTask) timerModel.AddEdge(timerStartToFire, timerStartToCancel, decisionCompleteToTimerStart, timerFiredToDecisionSchedule, timerCancelToDecisionSchedule) // Setup child workflow model childWorkflowModel := NewHistoryEventModel() childWorkflowInitial := NewHistoryEventVertex(types.EventTypeStartChildWorkflowExecutionInitiated.String()) childWorkflowInitial.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeStartChildWorkflowExecutionInitiated.Ptr() historyEvent.StartChildWorkflowExecutionInitiatedEventAttributes = &types.StartChildWorkflowExecutionInitiatedEventAttributes{ Domain: domain, WorkflowID: childWorkflowID, WorkflowType: &types.WorkflowType{ Name: childWorkflowPrefix + workflowType, }, TaskList: &types.TaskList{ Name: taskList, Kind: types.TaskListKindNormal.Ptr(), }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(timeout), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(timeout), DecisionTaskCompletedEventID: lastEvent.ID, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyRejectDuplicate.Ptr(), } return historyEvent }) childWorkflowInitialFail := NewHistoryEventVertex(types.EventTypeStartChildWorkflowExecutionFailed.String()) childWorkflowInitialFail.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeStartChildWorkflowExecutionFailed.Ptr() historyEvent.StartChildWorkflowExecutionFailedEventAttributes = &types.StartChildWorkflowExecutionFailedEventAttributes{ Domain: domain, WorkflowID: childWorkflowID, WorkflowType: &types.WorkflowType{ Name: childWorkflowPrefix + workflowType, }, Cause: types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning.Ptr(), InitiatedEventID: lastEvent.ID, DecisionTaskCompletedEventID: lastEvent.GetStartChildWorkflowExecutionInitiatedEventAttributes().DecisionTaskCompletedEventID, } return historyEvent }) childWorkflowStart := NewHistoryEventVertex(types.EventTypeChildWorkflowExecutionStarted.String()) childWorkflowStart.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeChildWorkflowExecutionStarted.Ptr() historyEvent.ChildWorkflowExecutionStartedEventAttributes = &types.ChildWorkflowExecutionStartedEventAttributes{ Domain: domain, WorkflowType: &types.WorkflowType{ Name: childWorkflowPrefix + workflowType, }, InitiatedEventID: lastEvent.ID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childWorkflowID, RunID: uuid.New(), }, } return historyEvent }) childWorkflowCancel := NewHistoryEventVertex(types.EventTypeChildWorkflowExecutionCanceled.String()) childWorkflowCancel.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeChildWorkflowExecutionCanceled.Ptr() historyEvent.ChildWorkflowExecutionCanceledEventAttributes = &types.ChildWorkflowExecutionCanceledEventAttributes{ Domain: domain, WorkflowType: &types.WorkflowType{ Name: childWorkflowPrefix + workflowType, }, InitiatedEventID: lastEvent.GetChildWorkflowExecutionStartedEventAttributes().InitiatedEventID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childWorkflowID, RunID: lastEvent.GetChildWorkflowExecutionStartedEventAttributes().GetWorkflowExecution().RunID, }, StartedEventID: lastEvent.ID, } return historyEvent }) childWorkflowComplete := NewHistoryEventVertex(types.EventTypeChildWorkflowExecutionCompleted.String()) childWorkflowComplete.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeChildWorkflowExecutionCompleted.Ptr() historyEvent.ChildWorkflowExecutionCompletedEventAttributes = &types.ChildWorkflowExecutionCompletedEventAttributes{ Domain: domain, WorkflowType: &types.WorkflowType{ Name: childWorkflowPrefix + workflowType, }, InitiatedEventID: lastEvent.GetChildWorkflowExecutionStartedEventAttributes().InitiatedEventID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childWorkflowID, RunID: lastEvent.GetChildWorkflowExecutionStartedEventAttributes().GetWorkflowExecution().RunID, }, StartedEventID: lastEvent.ID, } return historyEvent }) childWorkflowFail := NewHistoryEventVertex(types.EventTypeChildWorkflowExecutionFailed.String()) childWorkflowFail.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeChildWorkflowExecutionFailed.Ptr() historyEvent.ChildWorkflowExecutionFailedEventAttributes = &types.ChildWorkflowExecutionFailedEventAttributes{ Domain: domain, WorkflowType: &types.WorkflowType{ Name: childWorkflowPrefix + workflowType, }, InitiatedEventID: lastEvent.GetChildWorkflowExecutionStartedEventAttributes().InitiatedEventID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childWorkflowID, RunID: lastEvent.GetChildWorkflowExecutionStartedEventAttributes().GetWorkflowExecution().RunID, }, StartedEventID: lastEvent.ID, } return historyEvent }) childWorkflowTerminate := NewHistoryEventVertex(types.EventTypeChildWorkflowExecutionTerminated.String()) childWorkflowTerminate.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeChildWorkflowExecutionTerminated.Ptr() historyEvent.ChildWorkflowExecutionTerminatedEventAttributes = &types.ChildWorkflowExecutionTerminatedEventAttributes{ Domain: domain, WorkflowType: &types.WorkflowType{ Name: childWorkflowPrefix + workflowType, }, InitiatedEventID: lastEvent.GetChildWorkflowExecutionStartedEventAttributes().InitiatedEventID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childWorkflowID, RunID: lastEvent.GetChildWorkflowExecutionStartedEventAttributes().GetWorkflowExecution().RunID, }, StartedEventID: lastEvent.ID, } return historyEvent }) childWorkflowTimedOut := NewHistoryEventVertex(types.EventTypeChildWorkflowExecutionTimedOut.String()) childWorkflowTimedOut.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeChildWorkflowExecutionTimedOut.Ptr() historyEvent.ChildWorkflowExecutionTimedOutEventAttributes = &types.ChildWorkflowExecutionTimedOutEventAttributes{ Domain: domain, WorkflowType: &types.WorkflowType{ Name: childWorkflowPrefix + workflowType, }, InitiatedEventID: lastEvent.GetChildWorkflowExecutionStartedEventAttributes().InitiatedEventID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childWorkflowID, RunID: lastEvent.GetChildWorkflowExecutionStartedEventAttributes().GetWorkflowExecution().RunID, }, StartedEventID: lastEvent.ID, TimeoutType: types.TimeoutTypeScheduleToClose.Ptr(), } return historyEvent }) decisionCompleteToChildWorkflowInitial := NewHistoryEventEdge(decisionComplete, childWorkflowInitial) childWorkflowInitialToFail := NewHistoryEventEdge(childWorkflowInitial, childWorkflowInitialFail) childWorkflowInitialToStart := NewHistoryEventEdge(childWorkflowInitial, childWorkflowStart) childWorkflowStartToCancel := NewHistoryEventEdge(childWorkflowStart, childWorkflowCancel) childWorkflowStartToFail := NewHistoryEventEdge(childWorkflowStart, childWorkflowFail) childWorkflowStartToComplete := NewHistoryEventEdge(childWorkflowStart, childWorkflowComplete) childWorkflowStartToTerminate := NewHistoryEventEdge(childWorkflowStart, childWorkflowTerminate) childWorkflowStartToTimedOut := NewHistoryEventEdge(childWorkflowStart, childWorkflowTimedOut) childWorkflowCancelToDecisionSchedule := NewHistoryEventEdge(childWorkflowCancel, decisionSchedule) childWorkflowCancelToDecisionSchedule.SetCondition(notPendingDecisionTask) childWorkflowFailToDecisionSchedule := NewHistoryEventEdge(childWorkflowFail, decisionSchedule) childWorkflowFailToDecisionSchedule.SetCondition(notPendingDecisionTask) childWorkflowCompleteToDecisionSchedule := NewHistoryEventEdge(childWorkflowComplete, decisionSchedule) childWorkflowCompleteToDecisionSchedule.SetCondition(notPendingDecisionTask) childWorkflowTerminateToDecisionSchedule := NewHistoryEventEdge(childWorkflowTerminate, decisionSchedule) childWorkflowTerminateToDecisionSchedule.SetCondition(notPendingDecisionTask) childWorkflowTimedOutToDecisionSchedule := NewHistoryEventEdge(childWorkflowTimedOut, decisionSchedule) childWorkflowTimedOutToDecisionSchedule.SetCondition(notPendingDecisionTask) childWorkflowInitialFailToDecisionSchedule := NewHistoryEventEdge(childWorkflowInitialFail, decisionSchedule) childWorkflowInitialFailToDecisionSchedule.SetCondition(notPendingDecisionTask) childWorkflowModel.AddEdge(decisionCompleteToChildWorkflowInitial, childWorkflowInitialToFail, childWorkflowInitialToStart, childWorkflowStartToCancel, childWorkflowStartToFail, childWorkflowStartToComplete, childWorkflowStartToTerminate, childWorkflowStartToTimedOut, childWorkflowCancelToDecisionSchedule, childWorkflowFailToDecisionSchedule, childWorkflowCompleteToDecisionSchedule, childWorkflowTerminateToDecisionSchedule, childWorkflowTimedOutToDecisionSchedule, childWorkflowInitialFailToDecisionSchedule) // Setup external workflow model externalWorkflowModel := NewHistoryEventModel() externalWorkflowSignal := NewHistoryEventVertex(types.EventTypeSignalExternalWorkflowExecutionInitiated.String()) externalWorkflowSignal.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeSignalExternalWorkflowExecutionInitiated.Ptr() historyEvent.SignalExternalWorkflowExecutionInitiatedEventAttributes = &types.SignalExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventID: lastEvent.ID, Domain: domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: externalWorkflowID, RunID: uuid.New(), }, SignalName: "signal", ChildWorkflowOnly: false, } return historyEvent }) externalWorkflowSignalFailed := NewHistoryEventVertex(types.EventTypeSignalExternalWorkflowExecutionFailed.String()) externalWorkflowSignalFailed.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeSignalExternalWorkflowExecutionFailed.Ptr() historyEvent.SignalExternalWorkflowExecutionFailedEventAttributes = &types.SignalExternalWorkflowExecutionFailedEventAttributes{ Cause: types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr(), DecisionTaskCompletedEventID: lastEvent.GetSignalExternalWorkflowExecutionInitiatedEventAttributes().DecisionTaskCompletedEventID, Domain: domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: lastEvent.GetSignalExternalWorkflowExecutionInitiatedEventAttributes().GetWorkflowExecution().WorkflowID, RunID: lastEvent.GetSignalExternalWorkflowExecutionInitiatedEventAttributes().GetWorkflowExecution().RunID, }, InitiatedEventID: lastEvent.ID, } return historyEvent }) externalWorkflowSignaled := NewHistoryEventVertex(types.EventTypeExternalWorkflowExecutionSignaled.String()) externalWorkflowSignaled.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeExternalWorkflowExecutionSignaled.Ptr() historyEvent.ExternalWorkflowExecutionSignaledEventAttributes = &types.ExternalWorkflowExecutionSignaledEventAttributes{ InitiatedEventID: lastEvent.ID, Domain: domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: lastEvent.GetSignalExternalWorkflowExecutionInitiatedEventAttributes().GetWorkflowExecution().WorkflowID, RunID: lastEvent.GetSignalExternalWorkflowExecutionInitiatedEventAttributes().GetWorkflowExecution().RunID, }, } return historyEvent }) externalWorkflowCancel := NewHistoryEventVertex(types.EventTypeRequestCancelExternalWorkflowExecutionInitiated.String()) externalWorkflowCancel.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeRequestCancelExternalWorkflowExecutionInitiated.Ptr() historyEvent.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes = &types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventID: lastEvent.ID, Domain: domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: externalWorkflowID, RunID: uuid.New(), }, ChildWorkflowOnly: false, } return historyEvent }) externalWorkflowCancelFail := NewHistoryEventVertex(types.EventTypeRequestCancelExternalWorkflowExecutionFailed.String()) externalWorkflowCancelFail.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeRequestCancelExternalWorkflowExecutionFailed.Ptr() historyEvent.RequestCancelExternalWorkflowExecutionFailedEventAttributes = &types.RequestCancelExternalWorkflowExecutionFailedEventAttributes{ Cause: types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr(), DecisionTaskCompletedEventID: lastEvent.GetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes().DecisionTaskCompletedEventID, Domain: domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: lastEvent.GetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes().GetWorkflowExecution().WorkflowID, RunID: lastEvent.GetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes().GetWorkflowExecution().RunID, }, InitiatedEventID: lastEvent.ID, } return historyEvent }) externalWorkflowCanceled := NewHistoryEventVertex(types.EventTypeExternalWorkflowExecutionCancelRequested.String()) externalWorkflowCanceled.SetDataFunc(func(input ...interface{}) interface{} { lastEvent := input[0].(*types.HistoryEvent) lastGeneratedEvent := input[1].(*types.HistoryEvent) EventID := lastGeneratedEvent.ID + 1 version := input[2].(int64) historyEvent := getDefaultHistoryEvent(EventID, version) historyEvent.EventType = types.EventTypeExternalWorkflowExecutionCancelRequested.Ptr() historyEvent.ExternalWorkflowExecutionCancelRequestedEventAttributes = &types.ExternalWorkflowExecutionCancelRequestedEventAttributes{ InitiatedEventID: lastEvent.ID, Domain: domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: lastEvent.GetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes().GetWorkflowExecution().WorkflowID, RunID: lastEvent.GetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes().GetWorkflowExecution().RunID, }, } return historyEvent }) decisionCompleteToExternalWorkflowSignal := NewHistoryEventEdge(decisionComplete, externalWorkflowSignal) decisionCompleteToExternalWorkflowCancel := NewHistoryEventEdge(decisionComplete, externalWorkflowCancel) externalWorkflowSignalToFail := NewHistoryEventEdge(externalWorkflowSignal, externalWorkflowSignalFailed) externalWorkflowSignalToSignaled := NewHistoryEventEdge(externalWorkflowSignal, externalWorkflowSignaled) externalWorkflowCancelToFail := NewHistoryEventEdge(externalWorkflowCancel, externalWorkflowCancelFail) externalWorkflowCancelToCanceled := NewHistoryEventEdge(externalWorkflowCancel, externalWorkflowCanceled) externalWorkflowSignaledToDecisionSchedule := NewHistoryEventEdge(externalWorkflowSignaled, decisionSchedule) externalWorkflowSignaledToDecisionSchedule.SetCondition(notPendingDecisionTask) externalWorkflowSignalFailedToDecisionSchedule := NewHistoryEventEdge(externalWorkflowSignalFailed, decisionSchedule) externalWorkflowSignalFailedToDecisionSchedule.SetCondition(notPendingDecisionTask) externalWorkflowCanceledToDecisionSchedule := NewHistoryEventEdge(externalWorkflowCanceled, decisionSchedule) externalWorkflowCanceledToDecisionSchedule.SetCondition(notPendingDecisionTask) externalWorkflowCancelFailToDecisionSchedule := NewHistoryEventEdge(externalWorkflowCancelFail, decisionSchedule) externalWorkflowCancelFailToDecisionSchedule.SetCondition(notPendingDecisionTask) externalWorkflowModel.AddEdge(decisionCompleteToExternalWorkflowSignal, decisionCompleteToExternalWorkflowCancel, externalWorkflowSignalToFail, externalWorkflowSignalToSignaled, externalWorkflowCancelToFail, externalWorkflowCancelToCanceled, externalWorkflowSignaledToDecisionSchedule, externalWorkflowSignalFailedToDecisionSchedule, externalWorkflowCanceledToDecisionSchedule, externalWorkflowCancelFailToDecisionSchedule) // Config event generator generator.SetBatchGenerationRule(canDoBatch) generator.AddInitialEntryVertex(workflowStart) generator.AddExitVertex(workflowComplete, workflowFail, workflowTerminate, workflowTimedOut, continueAsNew) // generator.AddRandomEntryVertex(workflowSignal, workflowTerminate, workflowTimedOut) generator.AddModel(decisionModel) generator.AddModel(workflowModel) generator.AddModel(activityModel) generator.AddModel(timerModel) generator.AddModel(childWorkflowModel) generator.AddModel(externalWorkflowModel) return generator } func getDefaultHistoryEvent( EventID int64, version int64, ) *types.HistoryEvent { globalTaskID++ return &types.HistoryEvent{ ID: EventID, Timestamp: common.Int64Ptr(time.Now().UnixNano()), TaskID: globalTaskID, Version: version, } } func copyConnections( originalMap map[string][]Edge, ) map[string][]Edge { newMap := make(map[string][]Edge) for key, value := range originalMap { newMap[key] = copyEdges(value) } return newMap } func copyExitVertices( originalMap map[string]bool, ) map[string]bool { newMap := make(map[string]bool) for key, value := range originalMap { newMap[key] = value } return newMap } func copyVertex(vertex []Vertex) []Vertex { newVertex := make([]Vertex, len(vertex)) for idx, v := range vertex { newVertex[idx] = v.DeepCopy() } return newVertex } func copyEdges(edges []Edge) []Edge { newEdges := make([]Edge, len(edges)) for idx, e := range edges { newEdges[idx] = e.DeepCopy() } return newEdges } ================================================ FILE: common/testing/testdatagen/fuzzer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdatagen import ( "testing" "time" fuzz "github.com/google/gofuzz" ) // NewFuzzer creates a new fuzzer, notes down the deterministic seed func New(t *testing.T, generatorFuncs ...interface{}) *fuzz.Fuzzer { return NewWithNilChance(t, time.Now().UnixNano(), 0.2, generatorFuncs...) } // NewFuzzer creates a new fuzzer, notes down the deterministic seed func NewWithNilChance(t *testing.T, seed int64, nilchance float32, generatorFuncs ...interface{}) *fuzz.Fuzzer { t.Log("Fuzz Seed:", seed) return fuzz.NewWithSeed(seed).Funcs(generatorFuncs...).NilChance(float64(nilchance)) } ================================================ FILE: common/testing/testdatagen/idlfuzzedtestdata/history.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package idlfuzzedtestdata import ( "testing" fuzz "github.com/google/gofuzz" "github.com/uber/cadence/common/testing/testdatagen" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" ) // NewFuzzerWithIDLTypes creates a new fuzzer, notes down the deterministic seed // this particular invocation is preconfigured to be able to handle idl structs // correctly without generating completely invalid data (which, while good to test for // in the context of an application is too wide a search to be useful) func NewFuzzerWithIDLTypes(t *testing.T) *fuzz.Fuzzer { return testdatagen.New(t, // USE THESE VERY SPARINGLY, ONLY WHEN YOU MUST! // // The goal of providing these generators for specific types should be // to use them as little as possible, as they are fixed test data // which will not evolve with the idl or functions, therefore // the main benefit of fuzzing - evolving tests to handle all new fields in place - // will be defeated. // // for example, for mappers, if you add a new field that needs to be // mapped from protobuf to a native-go type (from the types folder) // and the testdata is fixed here *and not updated*, then the issue // will not be caught by any roundtrip tests. GenHistoryEvent, ) } // GenHistoryEvent is a function to use with gofuzz which // skips the majority of difficult to generate values // for the sake of simplicity in testing. Use it with the fuzz.Funcs(...) generation function func GenHistoryEvent(o *types.HistoryEvent, c fuzz.Continue) { // todo (david.porter) setup an assertion to ensure this list is exhaustive i := c.Rand.Intn(len(testdata.HistoryEventArray) - 1) o = testdata.HistoryEventArray[i] return } ================================================ FILE: common/tokenbucket/tb.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tokenbucket import ( "sync" "sync/atomic" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) type ( // TokenBucket is the interface for any implementation of a token bucket rate limiter TokenBucket interface { // TryConsume attempts to take count tokens from the // bucket. Returns true on success, false // otherwise along with the duration for the next refill TryConsume(count int) (bool, time.Duration) // Consume waits up to timeout duration to take count // tokens from the bucket. Returns true if count // tokens were acquired before timeout, false // otherwise Consume(count int, timeout time.Duration) bool } // PriorityTokenBucket is the interface for rate limiter with priority PriorityTokenBucket interface { // GetToken attempts to take count tokens from the // bucket with that priority. Priority 0 is highest. // Returns true on success, false // otherwise along with the duration for the next refill GetToken(priority, count int) (bool, time.Duration) } tokenBucketImpl struct { sync.Mutex tokens int fillRate int // amount of tokens to add every interval fillInterval time.Duration // time between refills // Because we divide the per-second quota equally // every 100 millis, there could be a remainder when // the desired rate is not a multiple 10 (1second/100Millis) // To overcome this, we keep track of left over remainder // and distribute this evenly during every fillInterval overflowRps int overflowTokens int nextRefillTime time.Time nextOverflowRefillTime time.Time clock clock.TimeSource } dynamicTokenBucketImpl struct { tb *tokenBucketImpl currentRPS int32 rps dynamicproperties.IntPropertyFn } priorityTokenBucketImpl struct { sync.Mutex tokens []int fillRate int nextRefillTime time.Time // Because we divide the per-second quota equally // every 100 millis, there could be a remainder when // the desired rate is not a multiple 10 (1second/100Millis) // To overcome this, we keep track of left over remainder // and distribute this evenly during every fillInterval overflowRps int overflowTokens int nextOverflowRefillTime time.Time timeSource clock.TimeSource } ) const ( millisPerSecond = 1000 backoffInterval = 10 * time.Millisecond refillRate = 100 * time.Millisecond ) // New creates and returns a // new token bucket rate limiter that // replenishes the bucket every 100 // milliseconds. Thread safe. // // @param rps // // Desired rate per second // // Golang.org has an alternative implementation // of the rate limiter. On benchmarking, golang's // implementation was order of magnitude slower. // In addition, it does a lot more than what we // need. These are the benchmarks under different // scenarios // // BenchmarkTokenBucketParallel 50000000 40.7 ns/op // BenchmarkGolangRateParallel 10000000 150 ns/op // BenchmarkTokenBucketParallel-8 20000000 124 ns/op // BenchmarkGolangRateParallel-8 10000000 208 ns/op // BenchmarkTokenBucketParallel 50000000 37.8 ns/op // BenchmarkGolangRateParallel 10000000 153 ns/op // BenchmarkTokenBucketParallel-8 10000000 129 ns/op // BenchmarkGolangRateParallel-8 10000000 208 ns/op func New(rps int, timeSource clock.TimeSource) TokenBucket { return newTokenBucket(rps, timeSource) } func newTokenBucket(rps int, timeSource clock.TimeSource) *tokenBucketImpl { tb := new(tokenBucketImpl) tb.clock = timeSource tb.reset(rps) return tb } func (tb *tokenBucketImpl) TryConsume(count int) (bool, time.Duration) { now := tb.clock.Now() tb.Lock() tb.refill(now) nextRefillTime := tb.nextRefillTime.Sub(now) if tb.tokens < count { tb.Unlock() return false, nextRefillTime } tb.tokens -= count tb.Unlock() return true, nextRefillTime } func (tb *tokenBucketImpl) Consume(count int, timeout time.Duration) bool { var remTime = timeout var expiryTime = tb.clock.Now().Add(timeout) for { if ok, _ := tb.TryConsume(count); ok { return true } if remTime < backoffInterval { tb.clock.Sleep(remTime) } else { tb.clock.Sleep(backoffInterval) } now := tb.clock.Now() if now.Compare(expiryTime) >= 0 { return false } remTime = expiryTime.Sub(now) } } func (tb *tokenBucketImpl) reset(rps int) { tb.Lock() tb.fillInterval = refillRate tb.fillRate = (rps * 100) / millisPerSecond tb.overflowRps = rps - (10 * tb.fillRate) tb.nextOverflowRefillTime = time.Time{} tb.Unlock() } func (tb *tokenBucketImpl) refill(now time.Time) { tb.refillOverFlow(now) if tb.isRefillDue(now) { tb.tokens = tb.fillRate if tb.overflowTokens > 0 { tb.tokens++ tb.overflowTokens-- } tb.nextRefillTime = now.Add(tb.fillInterval) } } func (tb *tokenBucketImpl) refillOverFlow(now time.Time) { if tb.overflowRps < 1 { return } if tb.isOverflowRefillDue(now) { tb.overflowTokens = tb.overflowRps tb.nextOverflowRefillTime = now.Add(time.Second) } } func (tb *tokenBucketImpl) isRefillDue(now time.Time) bool { return now.Compare(tb.nextRefillTime) >= 0 } func (tb *tokenBucketImpl) isOverflowRefillDue(now time.Time) bool { return now.Compare(tb.nextOverflowRefillTime) >= 0 } // NewDynamicTokenBucket creates and returns a token bucket // rate limiter that supports dynamic change of RPS. Thread safe. // @param rps // // Dynamic config function for rate per second func NewDynamicTokenBucket(rps dynamicproperties.IntPropertyFn, timeSource clock.TimeSource) TokenBucket { initialRPS := rps() return &dynamicTokenBucketImpl{ rps: rps, currentRPS: int32(initialRPS), tb: newTokenBucket(initialRPS, timeSource), } } func (dtb *dynamicTokenBucketImpl) TryConsume(count int) (bool, time.Duration) { dtb.resetRateIfChanged(dtb.rps()) return dtb.tb.TryConsume(count) } func (dtb *dynamicTokenBucketImpl) Consume(count int, timeout time.Duration) bool { dtb.resetRateIfChanged(dtb.rps()) return dtb.tb.Consume(count, timeout) } // resetLimitIfChanged resets the underlying token bucket if the // current rps quota is different from the actual rps quota obtained // from dynamic config func (dtb *dynamicTokenBucketImpl) resetRateIfChanged(newRPS int) { currentRPS := atomic.LoadInt32(&dtb.currentRPS) if int(currentRPS) == newRPS { return } if atomic.CompareAndSwapInt32(&dtb.currentRPS, currentRPS, int32(newRPS)) { dtb.tb.reset(newRPS) } } // NewPriorityTokenBucket creates and returns a // new token bucket rate limiter support priority. // There are n buckets for n priorities. It // replenishes the top priority bucket every 100 // milliseconds, unused tokens flows to next bucket. // The idea comes from Dual Token Bucket Algorithms. // Thread safe. // // @param numOfPriority // // Number of priorities // // @param rps // // Desired rate per second func NewPriorityTokenBucket(numOfPriority, rps int, timeSource clock.TimeSource) PriorityTokenBucket { tb := new(priorityTokenBucketImpl) tb.tokens = make([]int, numOfPriority) tb.timeSource = timeSource tb.fillRate = (rps * 100) / millisPerSecond tb.overflowRps = rps - (10 * tb.fillRate) tb.refill(tb.timeSource.Now()) return tb } // NewFullPriorityTokenBucket creates and returns a new priority token bucket with all bucket init with full tokens. // With all buckets full, get tokens from low priority buckets won't be missed initially, but may caused bursts. func NewFullPriorityTokenBucket(numOfPriority, rps int, timeSource clock.TimeSource) PriorityTokenBucket { tb := new(priorityTokenBucketImpl) tb.tokens = make([]int, numOfPriority) tb.timeSource = timeSource tb.fillRate = (rps * 100) / millisPerSecond tb.overflowRps = rps - (10 * tb.fillRate) tb.refill(tb.timeSource.Now()) for i := 1; i < numOfPriority; i++ { tb.nextRefillTime = time.Time{} tb.refill(tb.timeSource.Now()) } return tb } func (tb *priorityTokenBucketImpl) GetToken(priority, count int) (bool, time.Duration) { now := tb.timeSource.Now() tb.Lock() tb.refill(now) nextRefillTime := tb.nextRefillTime.Sub(now) if tb.tokens[priority] < count { tb.Unlock() return false, nextRefillTime } tb.tokens[priority] -= count tb.Unlock() return true, nextRefillTime } func (tb *priorityTokenBucketImpl) refill(now time.Time) { tb.refillOverFlow(now) if tb.isRefillDue(now) { more := tb.fillRate for i := 0; i < len(tb.tokens); i++ { tb.tokens[i] += more if tb.tokens[i] > tb.fillRate { more = tb.tokens[i] - tb.fillRate tb.tokens[i] = tb.fillRate } else { break } } if tb.overflowTokens > 0 { tb.tokens[0]++ tb.overflowTokens-- } tb.nextRefillTime = now.Add(refillRate) } } func (tb *priorityTokenBucketImpl) refillOverFlow(now time.Time) { if tb.overflowRps < 1 { return } if tb.isOverflowRefillDue(now) { tb.overflowTokens = tb.overflowRps tb.nextOverflowRefillTime = now.Add(time.Second) } } func (tb *priorityTokenBucketImpl) isRefillDue(now time.Time) bool { return now.Compare(tb.nextRefillTime) >= 0 } func (tb *priorityTokenBucketImpl) isOverflowRefillDue(now time.Time) bool { return now.Compare(tb.nextOverflowRefillTime) >= 0 } ================================================ FILE: common/tokenbucket/tb_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tokenbucket import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) func TestRpsEnforced(t *testing.T) { ts := clock.NewMockedTimeSource() tb := New(99, ts) for i := 0; i < 2; i++ { total, attempts := testRpsEnforcedHelper(tb, ts, 11, 90, 10) assert.Equal(t, 90, total, "Token bucket failed to enforce limit") assert.Equal(t, 9, attempts, "Token bucket gave out tokens too quickly") ts.Advance(time.Millisecond * 101) ok, _ := tb.TryConsume(9) assert.True(t, ok, "Token bucket failed to enforce limit") ok, _ = tb.TryConsume(1) assert.False(t, ok, "Token bucket failed to enforce limit") ts.Advance(time.Second) } } func TestLowRpsEnforced(t *testing.T) { ts := clock.NewMockedTimeSource() tb := New(3, ts) total, attempts := testRpsEnforcedHelper(tb, ts, 10, 3, 1) assert.Equal(t, 3, total, "Token bucket failed to enforce limit") assert.Equal(t, 3, attempts, "Token bucket gave out tokens too quickly") } func TestDynamicRpsEnforced(t *testing.T) { rpsConfigFn, rpsPtr := getTestRPSConfigFn(99) ts := clock.NewMockedTimeSource() dtb := NewDynamicTokenBucket(rpsConfigFn, ts) total, attempts := testRpsEnforcedHelper(dtb, ts, 11, 90, 10) assert.Equal(t, 90, total, "Token bucket failed to enforce limit") assert.Equal(t, 9, attempts, "Token bucket gave out tokens too quickly") ts.Advance(time.Second) *rpsPtr = 3 total, attempts = testRpsEnforcedHelper(dtb, ts, 10, 3, 1) assert.Equal(t, 3, total, "Token bucket failed to enforce limit") assert.Equal(t, 3, attempts, "Token bucket gave out tokens too quickly") } func testRpsEnforcedHelper(tb TokenBucket, ts clock.MockedTimeSource, maxAttempts, tokenNeeded, consumeRate int) (total, attempts int) { total = 0 attempts = 1 for ; attempts < maxAttempts+1; attempts++ { for c := 0; c < 2; c++ { if ok, _ := tb.TryConsume(consumeRate); ok { total += consumeRate } } if total >= tokenNeeded { break } ts.Advance(time.Millisecond * 101) } return } func getTestRPSConfigFn(defaultValue int) (dynamicproperties.IntPropertyFn, *int) { rps := defaultValue return func(_ ...dynamicproperties.FilterOption) int { return rps }, &rps } func TestPriorityRpsEnforced(t *testing.T) { ts := clock.NewMockedTimeSource() tb := NewPriorityTokenBucket(1, 99, ts) // behavior same to tokenBucketImpl for i := 0; i < 2; i++ { total := 0 attempts := 1 for ; attempts < 11; attempts++ { for c := 0; c < 2; c++ { if ok, _ := tb.GetToken(0, 10); ok { total += 10 } } if total >= 90 { break } ts.Advance(time.Millisecond * 101) } assert.Equal(t, 90, total, "Token bucket failed to enforce limit") assert.Equal(t, 9, attempts, "Token bucket gave out tokens too quickly") ts.Advance(time.Millisecond * 101) ok, _ := tb.GetToken(0, 9) assert.True(t, ok, "Token bucket failed to enforce limit") ok, _ = tb.GetToken(0, 1) assert.False(t, ok, "Token bucket failed to enforce limit") ts.Advance(time.Second) } } func TestPriorityLowRpsEnforced(t *testing.T) { ts := clock.NewMockedTimeSource() tb := NewPriorityTokenBucket(1, 3, ts) // behavior same to tokenBucketImpl total := 0 attempts := 1 for ; attempts < 10; attempts++ { for c := 0; c < 2; c++ { if ok, _ := tb.GetToken(0, 1); ok { total++ } } if total >= 3 { break } ts.Advance(time.Millisecond * 101) } assert.Equal(t, 3, total, "Token bucket failed to enforce limit") assert.Equal(t, 3, attempts, "Token bucket gave out tokens too quickly") } func TestPriorityTokenBucket(t *testing.T) { ts := clock.NewMockedTimeSource() tb := NewPriorityTokenBucket(2, 100, ts) for i := 0; i < 2; i++ { ok2, _ := tb.GetToken(1, 1) assert.False(t, ok2) ok, _ := tb.GetToken(0, 10) assert.True(t, ok) ts.Advance(time.Millisecond * 101) } for i := 0; i < 2; i++ { ok, _ := tb.GetToken(0, 9) assert.True(t, ok) // 1 token remaining in 1st bucket, 0 in 2nd ok2, _ := tb.GetToken(1, 1) assert.False(t, ok2) ts.Advance(time.Millisecond * 101) ok2, _ = tb.GetToken(1, 2) assert.False(t, ok2) ok2, _ = tb.GetToken(1, 1) assert.True(t, ok2) } } func TestFullPriorityTokenBucket(t *testing.T) { ts := clock.NewMockedTimeSource() tb := NewFullPriorityTokenBucket(2, 100, ts) ok2, _ := tb.GetToken(1, 10) assert.True(t, ok2) for i := 0; i < 2; i++ { ok2, _ := tb.GetToken(1, 1) assert.False(t, ok2) ok, _ := tb.GetToken(0, 10) assert.True(t, ok) ts.Advance(time.Millisecond * 101) } ok2, _ = tb.GetToken(1, 1) assert.False(t, ok2) ts.Advance(time.Millisecond * 101) ok2, _ = tb.GetToken(1, 5) assert.True(t, ok2) ts.Advance(time.Millisecond * 101) ok2, _ = tb.GetToken(1, 15) assert.False(t, ok2) ok2, _ = tb.GetToken(1, 10) assert.True(t, ok2) ok, _ := tb.GetToken(0, 10) assert.True(t, ok) } func TestTokenBucketConsume(t *testing.T) { t.Run("consume_wait", func(t *testing.T) { // make sure we don't deadlock inside the test. go panicOnTimeout(time.Minute)() ts := clock.NewMockedTimeSource() // I provide 10 rps, which means I can consume 10 tokens per second. // tokenBucketImpl will fill 1 token and then refill it every 100 milliseconds. tb := New(10, ts) // I consume 1 token, so the next Consume call will block until refill. ok, _ := tb.TryConsume(1) assert.True(t, ok) consumeFinished := make(chan struct{}) go func() { success := tb.Consume(1, 2*refillRate) assert.True(t, success, "Consume must acquire token successfully") close(consumeFinished) }() // I need to make sure that goroutine with Consume call is blocked on time.Sleep. // BlockUntil awaits for at least 1 goroutine to be blocked on time.Sleep/timer/ticker. ts.BlockUntil(1) // Checking that consume is still blocked. select { case <-consumeFinished: assert.Fail(t, "Consume returned before refill") default: } // tb has internal retries to acquire tokens which happens at most backoffInterval (10 * time.Millisecond). // I advance time once, so the next iteration is still blocked. ts.Advance(backoffInterval + 1) // wait until Consume did one iteration and stopped on time.Sleep ts.BlockUntil(1) // Checking that consume is still blocked. select { case <-consumeFinished: assert.Fail(t, "Consume returned before refill") default: } // Advance time to the next refill time. ts.Advance(refillRate - backoffInterval + 1) // Advance should unblock Consume and it should return afterwards. // I don't provide a timeout, since I have a goroutine to panic on timeout. <-consumeFinished }) t.Run("timeout", func(t *testing.T) { // make sure we don't deadlock inside the test. go panicOnTimeout(time.Minute)() ts := clock.NewMockedTimeSource() // I provide 10 rps, which means I can consume 10 tokens per second. // tokenBucketImpl will fill 1 token and then refill it every 100 milliseconds. tb := New(10, ts) // I consume 1 token, so the next Consume call will block until refill. ok, _ := tb.TryConsume(1) assert.True(t, ok) // This time in consume I provide a small timeout of backoffInterval/2. // So Consume will do only one internal iteration and then return before refill. consumeFinished := make(chan struct{}) go func() { success := tb.Consume(1, backoffInterval/2) assert.False(t, success, "Consume must fail to acquire token") close(consumeFinished) }() // I need to make sure that goroutine with Consume call is blocked on time.Sleep. // BlockUntil awaits for at least 1 goroutine to be blocked on time.Sleep/timer/ticker. ts.BlockUntil(1) select { case <-consumeFinished: assert.Fail(t, "Consume returned before refill") default: } // advance time to wake Consume up and make it return before refill. ts.Advance(backoffInterval/2 + 1) <-consumeFinished }) } func panicOnTimeout(d time.Duration) (cancel func()) { stop := make(chan struct{}) go func() { select { case <-time.After(d): panic("timeout") case <-stop: // noop } }() return func() { close(stop) } } ================================================ FILE: common/types/admin.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "sort" "unsafe" ) // AddSearchAttributeRequest is an internal type (TBD...) type AddSearchAttributeRequest struct { SearchAttribute map[string]IndexedValueType `json:"searchAttribute,omitempty"` SecurityToken string `json:"securityToken,omitempty"` } // GetSearchAttribute is an internal getter (TBD...) func (v *AddSearchAttributeRequest) GetSearchAttribute() (o map[string]IndexedValueType) { if v != nil && v.SearchAttribute != nil { return v.SearchAttribute } return } // DescribeClusterResponse is an internal type (TBD...) type DescribeClusterResponse struct { SupportedClientVersions *SupportedClientVersions `json:"supportedClientVersions,omitempty"` MembershipInfo *MembershipInfo `json:"membershipInfo,omitempty"` PersistenceInfo map[string]*PersistenceInfo `json:"persistenceInfo,omitempty"` } // AdminDescribeWorkflowExecutionRequest is an internal type (TBD...) type AdminDescribeWorkflowExecutionRequest struct { Domain string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *AdminDescribeWorkflowExecutionRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // AdminDescribeWorkflowExecutionResponse is an internal type (TBD...) type AdminDescribeWorkflowExecutionResponse struct { ShardID string `json:"shardId,omitempty"` HistoryAddr string `json:"historyAddr,omitempty"` MutableStateInCache string `json:"mutableStateInCache,omitempty"` MutableStateInDatabase string `json:"mutableStateInDatabase,omitempty"` } // GetShardID is an internal getter (TBD...) func (v *AdminDescribeWorkflowExecutionResponse) GetShardID() (o string) { if v != nil { return v.ShardID } return } // GetMutableStateInDatabase is an internal getter (TBD...) func (v *AdminDescribeWorkflowExecutionResponse) GetMutableStateInDatabase() (o string) { if v != nil { return v.MutableStateInDatabase } return } // GetWorkflowExecutionRawHistoryV2Request is an internal type (TBD...) type GetWorkflowExecutionRawHistoryV2Request struct { Domain string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` StartEventID *int64 `json:"startEventId,omitempty"` StartEventVersion *int64 `json:"startEventVersion,omitempty"` EndEventID *int64 `json:"endEventId,omitempty"` EndEventVersion *int64 `json:"endEventVersion,omitempty"` MaximumPageSize int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *GetWorkflowExecutionRawHistoryV2Request) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetStartEventID is an internal getter (TBD...) func (v *GetWorkflowExecutionRawHistoryV2Request) GetStartEventID() (o int64) { if v != nil && v.StartEventID != nil { return *v.StartEventID } return } // GetStartEventVersion is an internal getter (TBD...) func (v *GetWorkflowExecutionRawHistoryV2Request) GetStartEventVersion() (o int64) { if v != nil && v.StartEventVersion != nil { return *v.StartEventVersion } return } // GetEndEventID is an internal getter (TBD...) func (v *GetWorkflowExecutionRawHistoryV2Request) GetEndEventID() (o int64) { if v != nil && v.EndEventID != nil { return *v.EndEventID } return } // GetEndEventVersion is an internal getter (TBD...) func (v *GetWorkflowExecutionRawHistoryV2Request) GetEndEventVersion() (o int64) { if v != nil && v.EndEventVersion != nil { return *v.EndEventVersion } return } // GetMaximumPageSize is an internal getter (TBD...) func (v *GetWorkflowExecutionRawHistoryV2Request) GetMaximumPageSize() (o int32) { if v != nil { return v.MaximumPageSize } return } // GetWorkflowExecutionRawHistoryV2Response is an internal type (TBD...) type GetWorkflowExecutionRawHistoryV2Response struct { NextPageToken []byte `json:"nextPageToken,omitempty"` HistoryBatches []*DataBlob `json:"historyBatches,omitempty"` VersionHistory *VersionHistory `json:"versionHistory,omitempty"` } // GetHistoryBatches is an internal getter (TBD...) func (v *GetWorkflowExecutionRawHistoryV2Response) GetHistoryBatches() (o []*DataBlob) { if v != nil && v.HistoryBatches != nil { return v.HistoryBatches } return } // GetVersionHistory is an internal getter (TBD...) func (v *GetWorkflowExecutionRawHistoryV2Response) GetVersionHistory() (o *VersionHistory) { if v != nil && v.VersionHistory != nil { return v.VersionHistory } return } // HostInfo is an internal type (TBD...) type HostInfo struct { Identity string `json:"Identity,omitempty"` } // MembershipInfo is an internal type (TBD...) type MembershipInfo struct { CurrentHost *HostInfo `json:"currentHost,omitempty"` ReachableMembers []string `json:"reachableMembers,omitempty"` Rings []*RingInfo `json:"rings,omitempty"` } // PersistenceSetting is used to expose persistence engine settings type PersistenceSetting struct { Key string `json:"key"` Value string `json:"value"` } // PersistenceFeature is used to expose store specific feature. // Feature can be cadence or store specific. type PersistenceFeature struct { Key string `json:"key"` Enabled bool `json:"enabled"` } // PersistenceInfo is used to expose store configuration type PersistenceInfo struct { Backend string `json:"backend"` Settings []*PersistenceSetting `json:"settings,omitempty"` Features []*PersistenceFeature `json:"features,omitempty"` } // ResendReplicationTasksRequest is an internal type (TBD...) type ResendReplicationTasksRequest struct { DomainID string `json:"domainID,omitempty"` WorkflowID string `json:"workflowID,omitempty"` RunID string `json:"runID,omitempty"` RemoteCluster string `json:"remoteCluster,omitempty"` StartEventID *int64 `json:"startEventID,omitempty"` StartVersion *int64 `json:"startVersion,omitempty"` EndEventID *int64 `json:"endEventID,omitempty"` EndVersion *int64 `json:"endVersion,omitempty"` } // GetWorkflowID is an internal getter (TBD...) func (v *ResendReplicationTasksRequest) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *ResendReplicationTasksRequest) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetRemoteCluster is an internal getter (TBD...) func (v *ResendReplicationTasksRequest) GetRemoteCluster() (o string) { if v != nil { return v.RemoteCluster } return } // RingInfo is an internal type (TBD...) type RingInfo struct { Role string `json:"role,omitempty"` MemberCount int32 `json:"memberCount,omitempty"` Members []*HostInfo `json:"members,omitempty"` } type GetDynamicConfigRequest struct { ConfigName string `json:"configName,omitempty"` Filters []*DynamicConfigFilter `json:"filters,omitempty"` } type GetDynamicConfigResponse struct { Value *DataBlob `json:"value,omitempty"` } type UpdateDynamicConfigRequest struct { ConfigName string `json:"configName,omitempty"` ConfigValues []*DynamicConfigValue `json:"configValues,omitempty"` } type RestoreDynamicConfigRequest struct { ConfigName string `json:"configName,omitempty"` Filters []*DynamicConfigFilter `json:"filters,omitempty"` } // AdminDeleteWorkflowRequest is an internal type (TBD...) type AdminDeleteWorkflowRequest struct { Domain string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` SkipErrors bool `json:"skipErrors,omitempty"` } func (v *AdminDeleteWorkflowRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetExecution is an internal getter (TBD...) func (v *AdminDeleteWorkflowRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } func (v *AdminDeleteWorkflowRequest) GetSkipErrors() (o bool) { if v != nil { return v.SkipErrors } return } type AdminDeleteWorkflowResponse struct { HistoryDeleted bool `json:"historyDeleted,omitempty"` ExecutionsDeleted bool `json:"executionsDeleted,omitempty"` VisibilityDeleted bool `json:"visibilityDeleted,omitempty"` } type AdminMaintainWorkflowRequest = AdminDeleteWorkflowRequest type AdminMaintainWorkflowResponse = AdminDeleteWorkflowResponse type ListDynamicConfigRequest struct { ConfigName string `json:"configName,omitempty"` } type ListDynamicConfigResponse struct { Entries []*DynamicConfigEntry `json:"entries,omitempty"` } type IsolationGroupState int const ( IsolationGroupStateInvalid IsolationGroupState = iota IsolationGroupStateHealthy IsolationGroupStateDrained ) type IsolationGroupPartition struct { Name string State IsolationGroupState } // ByteSize returns an approximate size of the object in bytes func (i IsolationGroupPartition) ByteSize() uint64 { var size uint64 size += uint64(unsafe.Sizeof(i)) size += uint64(len(i.Name)) return size } // IsolationGroupConfiguration is an internal representation of a set of // isolation-groups as a mapping and may refer to either globally or per-domain (or both) configurations. // and their statuses. It's redundantly indexed by IsolationGroup name to simplify lookups. // // For example: This might be a global configuration persisted // in the config store and look like this: // // IsolationGroupConfiguration{ // "isolationGroup1234": {Name: "isolationGroup1234", Status: IsolationGroupStatusDrained}, // } // // Indicating that task processing isn't to occur within this isolationGroup anymore, but all others are ok. type IsolationGroupConfiguration map[string]IsolationGroupPartition // ToPartitionList Renders the isolation group to the less complicated and confusing simple list of isolation groups func (i IsolationGroupConfiguration) ToPartitionList() []IsolationGroupPartition { out := []IsolationGroupPartition{} for _, v := range i { out = append(out, v) } // ensure determinitism in list ordering for convenience sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name }) return out } func (i IsolationGroupConfiguration) DeepCopy() IsolationGroupConfiguration { if i == nil { return nil } out := IsolationGroupConfiguration{} for k, v := range i { out[k] = v } return out } // ByteSize returns an approximate size of the object in bytes func (i *IsolationGroupConfiguration) ByteSize() uint64 { if i == nil { return 0 } size := uint64(unsafe.Sizeof(*i)) for k, v := range *i { size += uint64(len(k)) + uint64(len(v.Name)) } return size } // FromIsolationGroupPartitionList maps a list of isolation to the internal IsolationGroup configuration type // whose map keys tend to be used more for set operations func FromIsolationGroupPartitionList(in []IsolationGroupPartition) IsolationGroupConfiguration { if len(in) == 0 { return IsolationGroupConfiguration{} } out := IsolationGroupConfiguration{} for _, v := range in { out[v.Name] = v } return out } type GetGlobalIsolationGroupsRequest struct{} type GetGlobalIsolationGroupsResponse struct { IsolationGroups IsolationGroupConfiguration } type UpdateGlobalIsolationGroupsRequest struct { IsolationGroups IsolationGroupConfiguration } type UpdateGlobalIsolationGroupsResponse struct{} type GetDomainIsolationGroupsRequest struct { Domain string } type GetDomainIsolationGroupsResponse struct { IsolationGroups IsolationGroupConfiguration } type UpdateDomainIsolationGroupsRequest struct { Domain string IsolationGroups IsolationGroupConfiguration } type UpdateDomainIsolationGroupsResponse struct{} type GetDomainAsyncWorkflowConfiguratonRequest struct { Domain string } type GetDomainAsyncWorkflowConfiguratonResponse struct { Configuration *AsyncWorkflowConfiguration } type AsyncWorkflowConfiguration struct { Enabled bool PredefinedQueueName string QueueType string QueueConfig *DataBlob } func (c AsyncWorkflowConfiguration) DeepCopy() AsyncWorkflowConfiguration { res := AsyncWorkflowConfiguration{ Enabled: c.Enabled, PredefinedQueueName: c.PredefinedQueueName, QueueType: c.QueueType, QueueConfig: c.QueueConfig.DeepCopy(), } return res } // ByteSize returns an approximate size of the object in bytes func (c *AsyncWorkflowConfiguration) ByteSize() uint64 { if c == nil { return 0 } size := uint64(unsafe.Sizeof(*c)) size += uint64(len(c.PredefinedQueueName)) size += uint64(len(c.QueueType)) size += c.QueueConfig.ByteSize() return size } type UpdateDomainAsyncWorkflowConfiguratonRequest struct { Domain string Configuration *AsyncWorkflowConfiguration } type UpdateDomainAsyncWorkflowConfiguratonResponse struct { } type UpdateTaskListPartitionConfigRequest struct { Domain string TaskList *TaskList TaskListType *TaskListType PartitionConfig *TaskListPartitionConfig } type UpdateTaskListPartitionConfigResponse struct{} ================================================ FILE: common/types/admin_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "testing" "github.com/stretchr/testify/assert" ) func TestIsolationGroupConfiguration_ToPartitionList(t *testing.T) { tests := map[string]struct { in *IsolationGroupConfiguration expectedOut []IsolationGroupPartition }{ "valid value": { in: &IsolationGroupConfiguration{ "zone-1": { Name: "zone-1", State: IsolationGroupStateDrained, }, "zone-2": { Name: "zone-2", State: IsolationGroupStateHealthy, }, "zone-3": { Name: "zone-3", State: IsolationGroupStateDrained, }, }, expectedOut: []IsolationGroupPartition{ { Name: "zone-1", State: IsolationGroupStateDrained, }, { Name: "zone-2", State: IsolationGroupStateHealthy, }, { Name: "zone-3", State: IsolationGroupStateDrained, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expectedOut, td.in.ToPartitionList()) }) } } func TestIsolationGroupConfigurationDeepCopy(t *testing.T) { tests := []struct { name string input IsolationGroupConfiguration }{ { name: "nil", input: nil, }, { name: "empty", input: IsolationGroupConfiguration{}, }, { name: "single group", input: IsolationGroupConfiguration{ "zone-1": { Name: "zone-1", State: IsolationGroupStateDrained, }, }, }, { name: "multiple groups", input: IsolationGroupConfiguration{ "zone-1": { Name: "zone-1", State: IsolationGroupStateDrained, }, "zone-2": { Name: "zone-2", State: IsolationGroupStateHealthy, }, "zone-3": { Name: "zone-3", State: IsolationGroupStateDrained, }, }, }, } for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { got := tc.input.DeepCopy() assert.Equal(t, tc.input, got) if tc.input == nil { return } tc.input["new"] = IsolationGroupPartition{Name: "new", State: IsolationGroupStateHealthy} assert.NotEqual(t, tc.input, got) }) } } func TestAsyncWorkflowConfigurationDeepCopy(t *testing.T) { tests := []struct { name string input AsyncWorkflowConfiguration }{ { name: "empty", input: AsyncWorkflowConfiguration{}, }, { name: "predefined queue", input: AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-async-wf-queue", }, }, { name: "custom queue", input: AsyncWorkflowConfiguration{ Enabled: true, QueueType: "custom", QueueConfig: &DataBlob{ EncodingType: EncodingTypeThriftRW.Ptr(), Data: []byte("test-async-wf-queue"), }, }, }, } for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { got := tc.input.DeepCopy() assert.Equal(t, tc.input, got) if tc.input.QueueConfig != nil { // assert that queue configs look the same but underlying slice is different assert.Equal(t, tc.input.QueueConfig, got.QueueConfig) if tc.input.QueueConfig.Data != nil && identicalByteArray(tc.input.QueueConfig.Data, got.QueueConfig.Data) { t.Error("expected DeepCopy to return a new QueueConfig.Data") } } }) } } func TestAddSearchAttributeRequest_GetSearchAttribute(t *testing.T) { tests := []struct { name string request *AddSearchAttributeRequest want map[string]IndexedValueType }{ { name: "Nil request", request: nil, want: nil, }, { name: "Nil SearchAttribute", request: &AddSearchAttributeRequest{ SearchAttribute: nil, }, want: nil, }, { name: "With SearchAttribute", request: &AddSearchAttributeRequest{ SearchAttribute: map[string]IndexedValueType{ "attr1": 1, "attr2": 2, }, }, want: map[string]IndexedValueType{ "attr1": 1, "attr2": 2, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetSearchAttribute() assert.Equal(t, tt.want, got, "GetSearchAttribute() result mismatch") }) } } func TestAdminDescribeWorkflowExecutionRequest_GetDomain(t *testing.T) { tests := []struct { name string request *AdminDescribeWorkflowExecutionRequest want string }{ { name: "Nil request", request: nil, want: "", }, { name: "Empty Domain", request: &AdminDescribeWorkflowExecutionRequest{}, want: "", }, { name: "With Domain", request: &AdminDescribeWorkflowExecutionRequest{ Domain: "test-domain", }, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetDomain() assert.Equal(t, tt.want, got, "GetDomain() result mismatch") }) } } func TestAdminDescribeWorkflowExecutionResponse_GetShardID(t *testing.T) { tests := []struct { name string response *AdminDescribeWorkflowExecutionResponse want string }{ { name: "Nil response", response: nil, want: "", }, { name: "Empty ShardID", response: &AdminDescribeWorkflowExecutionResponse{}, want: "", }, { name: "With ShardID", response: &AdminDescribeWorkflowExecutionResponse{ ShardID: "shard-123", }, want: "shard-123", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.response.GetShardID() assert.Equal(t, tt.want, got, "GetShardID() result mismatch") }) } } func TestAdminDescribeWorkflowExecutionResponse_GetMutableStateInDatabase(t *testing.T) { tests := []struct { name string response *AdminDescribeWorkflowExecutionResponse want string }{ { name: "Nil response", response: nil, want: "", }, { name: "Empty MutableStateInDatabase", response: &AdminDescribeWorkflowExecutionResponse{}, want: "", }, { name: "With MutableStateInDatabase", response: &AdminDescribeWorkflowExecutionResponse{ MutableStateInDatabase: "state-data", }, want: "state-data", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.response.GetMutableStateInDatabase() assert.Equal(t, tt.want, got, "GetMutableStateInDatabase() result mismatch") }) } } func TestGetWorkflowExecutionRawHistoryV2Request_GetDomain(t *testing.T) { tests := []struct { name string request *GetWorkflowExecutionRawHistoryV2Request want string }{ { name: "Nil request", request: nil, want: "", }, { name: "Empty Domain", request: &GetWorkflowExecutionRawHistoryV2Request{}, want: "", }, { name: "With Domain", request: &GetWorkflowExecutionRawHistoryV2Request{ Domain: "test-domain", }, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetDomain() assert.Equal(t, tt.want, got, "GetDomain() result mismatch") }) } } func TestGetWorkflowExecutionRawHistoryV2Request_GetStartEventID(t *testing.T) { tests := []struct { name string request *GetWorkflowExecutionRawHistoryV2Request want int64 }{ { name: "Nil request", request: nil, want: 0, }, { name: "Nil StartEventID", request: &GetWorkflowExecutionRawHistoryV2Request{}, want: 0, }, { name: "With StartEventID", request: &GetWorkflowExecutionRawHistoryV2Request{ StartEventID: ptrInt64(100), }, want: 100, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetStartEventID() assert.Equal(t, tt.want, got, "GetStartEventID() result mismatch") }) } } func TestGetWorkflowExecutionRawHistoryV2Request_GetMaximumPageSize(t *testing.T) { tests := []struct { name string request *GetWorkflowExecutionRawHistoryV2Request want int32 }{ { name: "Nil request", request: nil, want: 0, }, { name: "Default MaximumPageSize", request: &GetWorkflowExecutionRawHistoryV2Request{}, want: 0, }, { name: "With MaximumPageSize", request: &GetWorkflowExecutionRawHistoryV2Request{ MaximumPageSize: 100, }, want: 100, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetMaximumPageSize() assert.Equal(t, tt.want, got, "GetMaximumPageSize() result mismatch") }) } } func TestGetWorkflowExecutionRawHistoryV2Request_GetStartEventVersion(t *testing.T) { tests := []struct { name string request *GetWorkflowExecutionRawHistoryV2Request want int64 }{ { name: "Nil request", request: nil, want: 0, }, { name: "Nil StartEventVersion", request: &GetWorkflowExecutionRawHistoryV2Request{}, want: 0, }, { name: "With StartEventVersion", request: &GetWorkflowExecutionRawHistoryV2Request{ StartEventVersion: ptrInt64(123), }, want: 123, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetStartEventVersion() assert.Equal(t, tt.want, got, "GetStartEventVersion() result mismatch") }) } } func TestGetWorkflowExecutionRawHistoryV2Request_GetEndEventID(t *testing.T) { tests := []struct { name string request *GetWorkflowExecutionRawHistoryV2Request want int64 }{ { name: "Nil request", request: nil, want: 0, }, { name: "Nil EndEventID", request: &GetWorkflowExecutionRawHistoryV2Request{}, want: 0, }, { name: "With EndEventID", request: &GetWorkflowExecutionRawHistoryV2Request{ EndEventID: ptrInt64(200), }, want: 200, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetEndEventID() assert.Equal(t, tt.want, got, "GetEndEventID() result mismatch") }) } } func TestGetWorkflowExecutionRawHistoryV2Request_GetEndEventVersion(t *testing.T) { tests := []struct { name string request *GetWorkflowExecutionRawHistoryV2Request want int64 }{ { name: "Nil request", request: nil, want: 0, }, { name: "Nil EndEventVersion", request: &GetWorkflowExecutionRawHistoryV2Request{}, want: 0, }, { name: "With EndEventVersion", request: &GetWorkflowExecutionRawHistoryV2Request{ EndEventVersion: ptrInt64(456), }, want: 456, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetEndEventVersion() assert.Equal(t, tt.want, got, "GetEndEventVersion() result mismatch") }) } } func TestGetWorkflowExecutionRawHistoryV2Response_GetHistoryBatches(t *testing.T) { tests := []struct { name string response *GetWorkflowExecutionRawHistoryV2Response want []*DataBlob }{ { name: "Nil response", response: nil, want: nil, }, { name: "Empty HistoryBatches", response: &GetWorkflowExecutionRawHistoryV2Response{}, want: nil, }, { name: "With HistoryBatches", response: &GetWorkflowExecutionRawHistoryV2Response{ HistoryBatches: []*DataBlob{ { EncodingType: EncodingTypeJSON.Ptr(), Data: []byte("data1"), }, { EncodingType: EncodingTypeThriftRW.Ptr(), Data: []byte("data2"), }, }, }, want: []*DataBlob{ { EncodingType: EncodingTypeJSON.Ptr(), Data: []byte("data1"), }, { EncodingType: EncodingTypeThriftRW.Ptr(), Data: []byte("data2"), }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.response.GetHistoryBatches() assert.Equal(t, tt.want, got, "GetHistoryBatches() result mismatch") }) } } func TestGetWorkflowExecutionRawHistoryV2Response_GetVersionHistory(t *testing.T) { tests := []struct { name string response *GetWorkflowExecutionRawHistoryV2Response want *VersionHistory }{ { name: "Nil response", response: nil, want: nil, }, { name: "Empty VersionHistory", response: &GetWorkflowExecutionRawHistoryV2Response{}, want: nil, }, { name: "With VersionHistory", response: &GetWorkflowExecutionRawHistoryV2Response{ VersionHistory: &VersionHistory{ Items: []*VersionHistoryItem{ { EventID: 1, Version: 1, }, { EventID: 2, Version: 2, }, }, }, }, want: &VersionHistory{ Items: []*VersionHistoryItem{ { EventID: 1, Version: 1, }, { EventID: 2, Version: 2, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.response.GetVersionHistory() assert.Equal(t, tt.want, got, "GetVersionHistory() result mismatch") }) } } func TestResendReplicationTasksRequest_GetWorkflowID(t *testing.T) { tests := []struct { name string request *ResendReplicationTasksRequest expected string }{ { name: "Nil request", request: nil, expected: "", }, { name: "Empty WorkflowID", request: &ResendReplicationTasksRequest{}, expected: "", }, { name: "With WorkflowID", request: &ResendReplicationTasksRequest{ WorkflowID: "test-workflow-id", }, expected: "test-workflow-id", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetWorkflowID() assert.Equal(t, tt.expected, got) }) } } func TestResendReplicationTasksRequest_GetRunID(t *testing.T) { tests := []struct { name string request *ResendReplicationTasksRequest expected string }{ { name: "Nil request", request: nil, expected: "", }, { name: "Empty RunID", request: &ResendReplicationTasksRequest{}, expected: "", }, { name: "With RunID", request: &ResendReplicationTasksRequest{ RunID: "test-run-id", }, expected: "test-run-id", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetRunID() assert.Equal(t, tt.expected, got) }) } } func TestResendReplicationTasksRequest_GetRemoteCluster(t *testing.T) { tests := []struct { name string request *ResendReplicationTasksRequest expected string }{ { name: "Nil request", request: nil, expected: "", }, { name: "Empty RemoteCluster", request: &ResendReplicationTasksRequest{}, expected: "", }, { name: "With RemoteCluster", request: &ResendReplicationTasksRequest{ RemoteCluster: "test-remote-cluster", }, expected: "test-remote-cluster", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetRemoteCluster() assert.Equal(t, tt.expected, got) }) } } func TestAdminDeleteWorkflowRequest_GetDomain(t *testing.T) { tests := []struct { name string request *AdminDeleteWorkflowRequest expected string }{ { name: "Nil request", request: nil, expected: "", }, { name: "Empty Domain", request: &AdminDeleteWorkflowRequest{}, expected: "", }, { name: "With Domain", request: &AdminDeleteWorkflowRequest{ Domain: "test-domain", }, expected: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetDomain() assert.Equal(t, tt.expected, got) }) } } func TestAdminDeleteWorkflowRequest_GetExecution(t *testing.T) { tests := []struct { name string request *AdminDeleteWorkflowRequest expected *WorkflowExecution }{ { name: "Nil request", request: nil, expected: nil, }, { name: "Nil Execution", request: &AdminDeleteWorkflowRequest{}, expected: nil, }, { name: "With Execution", request: &AdminDeleteWorkflowRequest{ Execution: &WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, expected: &WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetExecution() assert.Equal(t, tt.expected, got) }) } } func TestAdminDeleteWorkflowRequest_GetSkipErrors(t *testing.T) { tests := []struct { name string request *AdminDeleteWorkflowRequest expected bool }{ { name: "Nil request", request: nil, expected: false, }, { name: "SkipErrors is false", request: &AdminDeleteWorkflowRequest{SkipErrors: false}, expected: false, }, { name: "SkipErrors is true", request: &AdminDeleteWorkflowRequest{SkipErrors: true}, expected: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.request.GetSkipErrors() assert.Equal(t, tt.expected, got) }) } } func TestFromIsolationGroupPartitionList(t *testing.T) { tests := []struct { name string in []IsolationGroupPartition want IsolationGroupConfiguration }{ { name: "empty list", in: []IsolationGroupPartition{}, want: IsolationGroupConfiguration{}, }, { name: "single group", in: []IsolationGroupPartition{ {Name: "zone-1", State: IsolationGroupStateHealthy}, }, want: IsolationGroupConfiguration{ "zone-1": {Name: "zone-1", State: IsolationGroupStateHealthy}, }, }, { name: "multiple groups", in: []IsolationGroupPartition{ {Name: "zone-1", State: IsolationGroupStateHealthy}, {Name: "zone-2", State: IsolationGroupStateDrained}, }, want: IsolationGroupConfiguration{ "zone-1": {Name: "zone-1", State: IsolationGroupStateHealthy}, "zone-2": {Name: "zone-2", State: IsolationGroupStateDrained}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := FromIsolationGroupPartitionList(tt.in) assert.Equal(t, tt.want, got) }) } } func ptrInt64(i int64) *int64 { return &i } ================================================ FILE: common/types/caller.go ================================================ // Copyright (c) 2025 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package types import ( "context" ) const ( CallerTypeHeaderName = "cadence-caller-type" // need to define it here due to circular dependency with common ) type CallerType string const ( CallerTypeUnknown CallerType = "unknown" CallerTypeCLI CallerType = "cli" CallerTypeUI CallerType = "ui" CallerTypeSDK CallerType = "sdk" CallerTypeInternal CallerType = "internal" ) // CallerInfo captures request source information for observability and resource management. // // Intent: // - Track the source/origin/actor of API requests (CLI, UI, SDK, internal service calls, etc.) // - Enable client-specific behavior and resource allocation decisions // - Support future extensibility for additional caller metadata (e.g., identity, version) // // Consumers: // - Logging and audit systems for request attribution // - Metrics and monitoring for client-specific observability // - Rate limiting and resource management based on caller information // // Lifecycle: // - Should be set early in request processing, typically after authentication // - Expected for external API calls (CLI, UI, SDK) // - May be absent for internal service-to-service calls or unauthenticated endpoints // - Set by authentication/authorization middleware or API gateway components type CallerInfo struct { callerType CallerType } // NewCallerInfo creates a new CallerInfo func NewCallerInfo(callerType CallerType) CallerInfo { return CallerInfo{callerType: callerType} } // GetCallerType returns the CallerType func (c CallerInfo) GetCallerType() CallerType { return c.callerType } type callerInfoContextKey string const callerInfoKey = callerInfoContextKey("caller-info") func (c CallerType) String() string { return string(c) } // ParseCallerType converts a string to CallerType // Returns CallerTypeUnknown if s is empty func ParseCallerType(s string) CallerType { if s == "" { return CallerTypeUnknown } return CallerType(s) } // ContextWithCallerInfo adds CallerInfo to context func ContextWithCallerInfo(ctx context.Context, callerInfo CallerInfo) context.Context { return context.WithValue(ctx, callerInfoKey, callerInfo) } // GetCallerInfoFromContext retrieves CallerInfo from context // Returns CallerInfo with CallerTypeUnknown if not set in context func GetCallerInfoFromContext(ctx context.Context) CallerInfo { if ctx == nil { return NewCallerInfo(CallerTypeUnknown) } if callerInfo, ok := ctx.Value(callerInfoKey).(CallerInfo); ok { return callerInfo } return NewCallerInfo(CallerTypeUnknown) } // NewCallerInfoFromTransportHeaders extracts CallerInfo from transport headers // This is used by middleware to extract caller information from incoming requests func NewCallerInfoFromTransportHeaders(headers interface{ Get(string) (string, bool) }) CallerInfo { callerTypeStr, _ := headers.Get(CallerTypeHeaderName) // Future: add more header extractions here // version, _ := headers.Get("cadence-client-version") // identity, _ := headers.Get("cadence-client-identity") return NewCallerInfo(ParseCallerType(callerTypeStr)) } // GetContextWithCallerInfoFromHeaders extracts CallerInfo from transport headers and adds it to the context func GetContextWithCallerInfoFromHeaders(ctx context.Context, headers interface{ Get(string) (string, bool) }) context.Context { return ContextWithCallerInfo(ctx, NewCallerInfoFromTransportHeaders(headers)) } ================================================ FILE: common/types/caller_test.go ================================================ // Copyright (c) 2025 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package types import ( "context" "testing" "github.com/stretchr/testify/assert" ) func TestCallerType_String(t *testing.T) { tests := []struct { name string callerType CallerType want string }{ {"CLI", CallerTypeCLI, "cli"}, {"UI", CallerTypeUI, "ui"}, {"SDK", CallerTypeSDK, "sdk"}, {"Internal", CallerTypeInternal, "internal"}, {"Unknown", CallerTypeUnknown, "unknown"}, {"Empty string", CallerType(""), ""}, {"Invalid", CallerType("invalid"), "invalid"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, tt.callerType.String()) }) } } func TestParseCallerType(t *testing.T) { tests := []struct { name string input string want CallerType }{ {"cli", "cli", CallerTypeCLI}, {"ui", "ui", CallerTypeUI}, {"sdk", "sdk", CallerTypeSDK}, {"internal", "internal", CallerTypeInternal}, {"unknown", "unknown", CallerTypeUnknown}, {"empty", "", CallerTypeUnknown}, {"custom value", "my-custom-tool", CallerType("my-custom-tool")}, {"uppercase", "CLI", CallerType("CLI")}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, ParseCallerType(tt.input)) }) } } func TestCallerTypeRoundTrip(t *testing.T) { tests := []CallerType{ CallerTypeCLI, CallerTypeUI, CallerTypeSDK, CallerTypeInternal, CallerTypeUnknown, } for _, ct := range tests { t.Run(ct.String(), func(t *testing.T) { str := ct.String() parsed := ParseCallerType(str) assert.Equal(t, ct, parsed) }) } } func TestNewCallerInfo(t *testing.T) { tests := []struct { name string callerType CallerType want CallerType }{ { name: "CLI", callerType: CallerTypeCLI, want: CallerTypeCLI, }, { name: "SDK", callerType: CallerTypeSDK, want: CallerTypeSDK, }, { name: "Unknown", callerType: CallerTypeUnknown, want: CallerTypeUnknown, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { info := NewCallerInfo(tt.callerType) assert.Equal(t, tt.want, info.GetCallerType()) }) } } func TestCallerInfo_GetCallerType(t *testing.T) { tests := []struct { name string info CallerInfo want CallerType }{ { name: "CLI CallerInfo", info: NewCallerInfo(CallerTypeCLI), want: CallerTypeCLI, }, { name: "SDK CallerInfo", info: NewCallerInfo(CallerTypeSDK), want: CallerTypeSDK, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, tt.info.GetCallerType()) }) } } func TestContextWithCallerInfo(t *testing.T) { tests := []struct { name string callerType CallerType want CallerType }{ { name: "CLI caller info", callerType: CallerTypeCLI, want: CallerTypeCLI, }, { name: "SDK caller info", callerType: CallerTypeSDK, want: CallerTypeSDK, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() ctx = ContextWithCallerInfo(ctx, NewCallerInfo(tt.callerType)) got := GetCallerInfoFromContext(ctx) assert.Equal(t, tt.want, got.GetCallerType()) }) } } func TestGetCallerInfoFromContext(t *testing.T) { tests := []struct { name string ctx context.Context wantCaller CallerType }{ { name: "nil context", ctx: nil, wantCaller: CallerTypeUnknown, }, { name: "context without caller info", ctx: context.Background(), wantCaller: CallerTypeUnknown, }, { name: "context with CLI caller info", ctx: ContextWithCallerInfo(context.Background(), NewCallerInfo(CallerTypeCLI)), wantCaller: CallerTypeCLI, }, { name: "context with SDK caller info", ctx: ContextWithCallerInfo(context.Background(), NewCallerInfo(CallerTypeSDK)), wantCaller: CallerTypeSDK, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := GetCallerInfoFromContext(tt.ctx) assert.Equal(t, tt.wantCaller, got.GetCallerType()) }) } } type mockHeaders map[string]string func (m mockHeaders) Get(key string) (string, bool) { val, ok := m[key] return val, ok } func TestNewCallerInfoFromTransportHeaders(t *testing.T) { tests := []struct { name string headers mockHeaders wantCaller CallerType }{ { name: "CLI caller-type header", headers: mockHeaders{CallerTypeHeaderName: "cli"}, wantCaller: CallerTypeCLI, }, { name: "UI caller-type header", headers: mockHeaders{CallerTypeHeaderName: "ui"}, wantCaller: CallerTypeUI, }, { name: "SDK caller-type header", headers: mockHeaders{CallerTypeHeaderName: "sdk"}, wantCaller: CallerTypeSDK, }, { name: "Internal caller-type header", headers: mockHeaders{CallerTypeHeaderName: "internal"}, wantCaller: CallerTypeInternal, }, { name: "empty caller-type header", headers: mockHeaders{CallerTypeHeaderName: ""}, wantCaller: CallerTypeUnknown, }, { name: "missing caller-type header", headers: mockHeaders{}, wantCaller: CallerTypeUnknown, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := NewCallerInfoFromTransportHeaders(tt.headers) assert.Equal(t, tt.wantCaller, got.GetCallerType()) }) } } func TestGetContextWithCallerInfoFromHeaders(t *testing.T) { tests := []struct { name string headers mockHeaders wantCaller CallerType }{ { name: "CLI caller-type header", headers: mockHeaders{CallerTypeHeaderName: "cli"}, wantCaller: CallerTypeCLI, }, { name: "SDK caller-type header", headers: mockHeaders{CallerTypeHeaderName: "sdk"}, wantCaller: CallerTypeSDK, }, { name: "empty caller-type header", headers: mockHeaders{CallerTypeHeaderName: ""}, wantCaller: CallerTypeUnknown, }, { name: "missing caller-type header", headers: mockHeaders{}, wantCaller: CallerTypeUnknown, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { resultCtx := GetContextWithCallerInfoFromHeaders(context.Background(), tt.headers) callerInfo := GetCallerInfoFromContext(resultCtx) assert.Equal(t, tt.wantCaller, callerInfo.GetCallerType()) }) } } ================================================ FILE: common/types/configStore.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types type DynamicConfigBlob struct { SchemaVersion int64 `json:"schemaVersion,omitempty"` Entries []*DynamicConfigEntry `json:"entries,omitempty"` } type DynamicConfigEntry struct { Name string `json:"name,omitempty"` Values []*DynamicConfigValue `json:"values,omitempty"` } type DynamicConfigValue struct { Value *DataBlob `json:"value,omitempty"` Filters []*DynamicConfigFilter `json:"filters,omitempty"` } type DynamicConfigFilter struct { Name string `json:"name,omitempty"` Value *DataBlob `json:"value,omitempty"` } func (dcf *DynamicConfigFilter) Copy() *DynamicConfigFilter { if dcf == nil { return nil } return &DynamicConfigFilter{ Name: dcf.Name, Value: dcf.Value.DeepCopy(), } } func (dcv *DynamicConfigValue) Copy() *DynamicConfigValue { if dcv == nil { return nil } var newFilters []*DynamicConfigFilter if dcv.Filters != nil { newFilters = make([]*DynamicConfigFilter, 0, len(dcv.Filters)) for _, filter := range dcv.Filters { newFilters = append(newFilters, filter.Copy()) } } return &DynamicConfigValue{ Value: dcv.Value.DeepCopy(), Filters: newFilters, } } func (dce *DynamicConfigEntry) Copy() *DynamicConfigEntry { if dce == nil { return nil } var newValues []*DynamicConfigValue if dce.Values != nil { newValues = make([]*DynamicConfigValue, 0, len(dce.Values)) for _, value := range dce.Values { newValues = append(newValues, value.Copy()) } } return &DynamicConfigEntry{ Name: dce.Name, Values: newValues, } } ================================================ FILE: common/types/configStore_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "testing" "github.com/stretchr/testify/assert" ) func TestDynamicConfigEntryCopy(t *testing.T) { tests := []struct { name string input *DynamicConfigEntry }{ { name: "nil", input: nil, }, { name: "empty", input: &DynamicConfigEntry{}, }, { name: "nil values", input: &DynamicConfigEntry{ Name: "test-2", Values: nil, }, }, { name: "non-nil values", input: &DynamicConfigEntry{ Name: "test-2", Values: []*DynamicConfigValue{ &DynamicConfigValue{ Value: &DataBlob{Data: []byte("data")}, Filters: nil, }, }, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { valCopy := tc.input.Copy() assert.Equal(t, tc.input, valCopy) // check if modifying the copy does not modify the original if valCopy != nil && valCopy.Values != nil { valCopy.Values[0].Value.Data = []byte("modified") assert.NotEqual(t, tc.input, valCopy) } assert.Equal(t, tc.input, tc.input.Copy()) }) } } func TestDynamicConfigFilterCopy(t *testing.T) { tests := []struct { name string input *DynamicConfigFilter }{ { name: "nil", input: nil, }, { name: "empty", input: &DynamicConfigFilter{}, }, { name: "nil value", input: &DynamicConfigFilter{ Name: "test-2", Value: nil, }, }, { name: "non-nil values", input: &DynamicConfigFilter{ Name: "test-2", Value: &DataBlob{Data: []byte("data")}, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { valCopy := tc.input.Copy() assert.Equal(t, tc.input, valCopy) // check if modifying the copy does not modify the original if valCopy != nil { valCopy.Name = "modified" assert.NotEqual(t, tc.input, valCopy) } }) } } func TestDynamicConfigValueCopy(t *testing.T) { tests := []struct { name string input *DynamicConfigValue }{ { name: "nil", input: nil, }, { name: "empty", input: &DynamicConfigValue{}, }, { name: "non-nil values", input: &DynamicConfigValue{ Value: &DataBlob{Data: []byte("data")}, Filters: []*DynamicConfigFilter{ &DynamicConfigFilter{ Name: "filter-1", Value: &DataBlob{Data: []byte("data")}, }}, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { // check if modifying the copy does not modify the original valCopy := tc.input.Copy() assert.Equal(t, tc.input, valCopy) if valCopy != nil && valCopy.Value != nil { valCopy.Value.Data = []byte("modified") assert.NotEqual(t, tc.input, valCopy) } }) } } ================================================ FILE: common/types/enums.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types // EventTypeValues returns all recognized values of EventType. func EventTypeValues() []EventType { return []EventType{ EventTypeWorkflowExecutionStarted, EventTypeWorkflowExecutionCompleted, EventTypeWorkflowExecutionFailed, EventTypeWorkflowExecutionTimedOut, EventTypeDecisionTaskScheduled, EventTypeDecisionTaskStarted, EventTypeDecisionTaskCompleted, EventTypeDecisionTaskTimedOut, EventTypeDecisionTaskFailed, EventTypeActivityTaskScheduled, EventTypeActivityTaskStarted, EventTypeActivityTaskCompleted, EventTypeActivityTaskFailed, EventTypeActivityTaskTimedOut, EventTypeActivityTaskCancelRequested, EventTypeRequestCancelActivityTaskFailed, EventTypeActivityTaskCanceled, EventTypeTimerStarted, EventTypeTimerFired, EventTypeCancelTimerFailed, EventTypeTimerCanceled, EventTypeWorkflowExecutionCancelRequested, EventTypeWorkflowExecutionCanceled, EventTypeRequestCancelExternalWorkflowExecutionInitiated, EventTypeRequestCancelExternalWorkflowExecutionFailed, EventTypeExternalWorkflowExecutionCancelRequested, EventTypeMarkerRecorded, EventTypeWorkflowExecutionSignaled, EventTypeWorkflowExecutionTerminated, EventTypeWorkflowExecutionContinuedAsNew, EventTypeStartChildWorkflowExecutionInitiated, EventTypeStartChildWorkflowExecutionFailed, EventTypeChildWorkflowExecutionStarted, EventTypeChildWorkflowExecutionCompleted, EventTypeChildWorkflowExecutionFailed, EventTypeChildWorkflowExecutionCanceled, EventTypeChildWorkflowExecutionTimedOut, EventTypeChildWorkflowExecutionTerminated, EventTypeSignalExternalWorkflowExecutionInitiated, EventTypeSignalExternalWorkflowExecutionFailed, EventTypeExternalWorkflowExecutionSignaled, EventTypeUpsertWorkflowSearchAttributes, } } // DecisionTypeValues returns all recognized values of DecisionType. func DecisionTypeValues() []DecisionType { return []DecisionType{ DecisionTypeScheduleActivityTask, DecisionTypeRequestCancelActivityTask, DecisionTypeStartTimer, DecisionTypeCompleteWorkflowExecution, DecisionTypeFailWorkflowExecution, DecisionTypeCancelTimer, DecisionTypeCancelWorkflowExecution, DecisionTypeRequestCancelExternalWorkflowExecution, DecisionTypeRecordMarker, DecisionTypeContinueAsNewWorkflowExecution, DecisionTypeStartChildWorkflowExecution, DecisionTypeSignalExternalWorkflowExecution, DecisionTypeUpsertWorkflowSearchAttributes, } } ================================================ FILE: common/types/enums_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "testing" "github.com/stretchr/testify/require" ) func Test_EventTypeValues(t *testing.T) { result := EventTypeValues() require.Equal(t, 42, len(result)) } func Test_DecisionTypeValues(t *testing.T) { result := DecisionTypeValues() require.Equal(t, 13, len(result)) } ================================================ FILE: common/types/errors.go ================================================ // Copyright (c) 2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "go.uber.org/zap/zapcore" ) // EventAlreadyStartedError is an internal type (TBD...) type EventAlreadyStartedError struct { Message string `json:"message,required"` } func (err AccessDeniedError) Error() string { return err.Message } func (err BadRequestError) Error() string { return err.Message } func (err CancellationAlreadyRequestedError) Error() string { return err.Message } func (err ClientVersionNotSupportedError) Error() string { return "client version not supported" } func (err ClientVersionNotSupportedError) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("feature-version", err.FeatureVersion) enc.AddString("client-implementation", err.ClientImpl) enc.AddString("supported-versions", err.SupportedVersions) return nil } func (err FeatureNotEnabledError) Error() string { return "feature not enabled" } func (err FeatureNotEnabledError) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("feature-flag", err.FeatureFlag) return nil } func (err CurrentBranchChangedError) Error() string { return err.Message } func (err CurrentBranchChangedError) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("wf-branch-token", string(err.CurrentBranchToken)) return nil } func (err DomainAlreadyExistsError) Error() string { return err.Message } func (err DomainNotActiveError) Error() string { return err.Message } func (err DomainNotActiveError) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("domain-name", err.DomainName) enc.AddString("current-cluster", err.CurrentCluster) enc.AddString("active-cluster", err.ActiveCluster) return nil } func (err EntityNotExistsError) Error() string { return err.Message } func (err WorkflowExecutionAlreadyCompletedError) Error() string { return err.Message } func (err InternalDataInconsistencyError) Error() string { return err.Message } func (err InternalServiceError) Error() string { return err.Message } func (err LimitExceededError) Error() string { return err.Message } func (err QueryFailedError) Error() string { return err.Message } func (err RemoteSyncMatchedError) Error() string { return err.Message } func (err RetryTaskV2Error) Error() string { return err.Message } func (err RetryTaskV2Error) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("domain-id", err.DomainID) enc.AddString("workflow-id", err.WorkflowID) enc.AddString("run-id", err.RunID) if err.StartEventID != nil { enc.AddInt64("start-event-id", *err.StartEventID) } if err.StartEventVersion != nil { enc.AddInt64("start-event-version", *err.StartEventVersion) } if err.EndEventID != nil { enc.AddInt64("end-event-id", *err.EndEventID) } if err.EndEventVersion != nil { enc.AddInt64("end-event-version", *err.EndEventVersion) } return nil } func (err ServiceBusyError) Error() string { return err.Message } func (err WorkflowExecutionAlreadyStartedError) Error() string { return err.Message } func (err WorkflowExecutionAlreadyStartedError) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("start-request-id", err.StartRequestID) enc.AddString("run-id", err.RunID) return nil } func (err ShardOwnershipLostError) Error() string { return err.Message } func (err ShardOwnershipLostError) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("shard-owner", err.Owner) return nil } func (err EventAlreadyStartedError) Error() string { return err.Message } func (err StickyWorkerUnavailableError) Error() string { return err.Message } func (err ReadOnlyPartitionError) Error() string { return err.Message } ================================================ FILE: common/types/errors_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "reflect" "testing" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" ) func Test_Error(t *testing.T) { errMessage := "test" tests := []struct { name string err error }{ { err: AccessDeniedError{ Message: errMessage, }, }, { err: BadRequestError{ Message: errMessage, }, }, { err: CancellationAlreadyRequestedError{ Message: errMessage, }, }, { err: DomainAlreadyExistsError{ Message: errMessage, }, }, { err: EntityNotExistsError{ Message: errMessage, }, }, { err: InternalDataInconsistencyError{ Message: errMessage, }, }, { err: WorkflowExecutionAlreadyCompletedError{ Message: errMessage, }, }, { err: LimitExceededError{ Message: errMessage, }, }, { err: QueryFailedError{ Message: errMessage, }, }, { err: RemoteSyncMatchedError{ Message: errMessage, }, }, { err: ServiceBusyError{ Message: errMessage, }, }, { err: EventAlreadyStartedError{ Message: errMessage, }, }, { err: StickyWorkerUnavailableError{ Message: errMessage, }, }, { err: ReadOnlyPartitionError{ Message: errMessage, }, }, { err: InternalServiceError{ Message: errMessage, }, }, } for _, tt := range tests { t.Run(reflect.TypeOf(tt.err).String(), func(t *testing.T) { require.Equal(t, errMessage, tt.err.Error()) }) } } func Test_ClientVersionNotSupportedError(t *testing.T) { err := ClientVersionNotSupportedError{ FeatureVersion: "1.0", ClientImpl: "1.0", SupportedVersions: "1.2", } require.Equal(t, "client version not supported", err.Error()) require.NoError(t, err.MarshalLogObject(zapcore.NewMapObjectEncoder())) } func Test_FeatureNotEnabledError(t *testing.T) { err := FeatureNotEnabledError{FeatureFlag: "test"} require.Equal(t, "feature not enabled", err.Error()) require.NoError(t, err.MarshalLogObject(zapcore.NewMapObjectEncoder())) } func Test_CurrentBranchChangedError(t *testing.T) { err := CurrentBranchChangedError{Message: "test", CurrentBranchToken: []byte{}} require.Equal(t, "test", err.Error()) require.NoError(t, err.MarshalLogObject(zapcore.NewMapObjectEncoder())) } func Test_DomainNotActiveError(t *testing.T) { err := DomainNotActiveError{Message: "test", DomainName: "test-domain"} require.Equal(t, "test", err.Error()) require.NoError(t, err.MarshalLogObject(zapcore.NewMapObjectEncoder())) } func Test_RetryTaskV2Error(t *testing.T) { testID := int64(1) testVersion := int64(1.0) err := RetryTaskV2Error{ Message: "test", DomainID: "test-domain-id", WorkflowID: "wid", RunID: "rid", StartEventID: &testID, StartEventVersion: &testVersion, EndEventID: &testID, EndEventVersion: &testVersion, } require.Equal(t, "test", err.Error()) require.NoError(t, err.MarshalLogObject(zapcore.NewMapObjectEncoder())) } func Test_WorkflowExecutionAlreadyStartedError(t *testing.T) { err := WorkflowExecutionAlreadyStartedError{Message: "test"} require.Equal(t, "test", err.Error()) require.NoError(t, err.MarshalLogObject(zapcore.NewMapObjectEncoder())) } func Test_ShardOwnershipLostError(t *testing.T) { err := ShardOwnershipLostError{Message: "test"} require.Equal(t, "test", err.Error()) require.NoError(t, err.MarshalLogObject(zapcore.NewMapObjectEncoder())) } ================================================ FILE: common/types/health.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types // HealthStatus is an internal type (TBD...) type HealthStatus struct { Ok bool `json:"ok,required"` Msg string `json:"msg,omitempty"` } ================================================ FILE: common/types/history.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types // DescribeMutableStateRequest is an internal type (TBD...) type DescribeMutableStateRequest struct { DomainUUID string `json:"domainUUID,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *DescribeMutableStateRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // DescribeMutableStateResponse is an internal type (TBD...) type DescribeMutableStateResponse struct { MutableStateInCache string `json:"mutableStateInCache,omitempty"` MutableStateInDatabase string `json:"mutableStateInDatabase,omitempty"` } // HistoryDescribeWorkflowExecutionRequest is an internal type (TBD...) type HistoryDescribeWorkflowExecutionRequest struct { DomainUUID string `json:"domainUUID,omitempty"` Request *DescribeWorkflowExecutionRequest `json:"request,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryDescribeWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // DomainFilter is an internal type (TBD...) type DomainFilter struct { DomainIDs []string `json:"domainIDs,omitempty"` ReverseMatch bool `json:"reverseMatch,omitempty"` } // GetDomainIDs is an internal getter (TBD...) func (v *DomainFilter) GetDomainIDs() (o []string) { if v != nil && v.DomainIDs != nil { return v.DomainIDs } return } // GetReverseMatch is an internal getter (TBD...) func (v *DomainFilter) GetReverseMatch() (o bool) { if v != nil { return v.ReverseMatch } return } // FailoverMarkerToken is an internal type (TBD...) type FailoverMarkerToken struct { ShardIDs []int32 `json:"shardIDs,omitempty"` FailoverMarker *FailoverMarkerAttributes `json:"failoverMarker,omitempty"` } // GetShardIDs is an internal getter (TBD...) func (v *FailoverMarkerToken) GetShardIDs() (o []int32) { if v != nil && v.ShardIDs != nil { return v.ShardIDs } return } // GetFailoverMarker is an internal getter (TBD...) func (v *FailoverMarkerToken) GetFailoverMarker() (o *FailoverMarkerAttributes) { if v != nil && v.FailoverMarker != nil { return v.FailoverMarker } return } // GetMutableStateRequest is an internal type (TBD...) type GetMutableStateRequest struct { DomainUUID string `json:"domainUUID,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` ExpectedNextEventID int64 `json:"expectedNextEventId,omitempty"` CurrentBranchToken []byte `json:"currentBranchToken,omitempty"` VersionHistoryItem *VersionHistoryItem `json:"versionHistoryItem,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *GetMutableStateRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetExpectedNextEventID is an internal getter (TBD...) func (v *GetMutableStateRequest) GetExpectedNextEventID() (o int64) { if v != nil { return v.ExpectedNextEventID } return } // GetMutableStateResponse is an internal type (TBD...) type GetMutableStateResponse struct { Execution *WorkflowExecution `json:"execution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` NextEventID int64 `json:"NextEventId,omitempty"` PreviousStartedEventID *int64 `json:"PreviousStartedEventId,omitempty"` LastFirstEventID int64 `json:"LastFirstEventId,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` StickyTaskList *TaskList `json:"stickyTaskList,omitempty"` ClientLibraryVersion string `json:"clientLibraryVersion,omitempty"` ClientFeatureVersion string `json:"clientFeatureVersion,omitempty"` ClientImpl string `json:"clientImpl,omitempty"` IsWorkflowRunning bool `json:"isWorkflowRunning,omitempty"` StickyTaskListScheduleToStartTimeout *int32 `json:"stickyTaskListScheduleToStartTimeout,omitempty"` EventStoreVersion int32 `json:"eventStoreVersion,omitempty"` CurrentBranchToken []byte `json:"currentBranchToken,omitempty"` WorkflowState *int32 `json:"workflowState,omitempty"` WorkflowCloseState *int32 `json:"workflowCloseState,omitempty"` VersionHistories *VersionHistories `json:"versionHistories,omitempty"` IsStickyTaskListEnabled bool `json:"isStickyTaskListEnabled,omitempty"` HistorySize int64 `json:"historySize,omitempty"` } // GetNextEventID is an internal getter (TBD...) func (v *GetMutableStateResponse) GetNextEventID() (o int64) { if v != nil { return v.NextEventID } return } // GetPreviousStartedEventID is an internal getter (TBD...) func (v *GetMutableStateResponse) GetPreviousStartedEventID() (o int64) { if v != nil && v.PreviousStartedEventID != nil { return *v.PreviousStartedEventID } return } // GetStickyTaskList is an internal getter (TBD...) func (v *GetMutableStateResponse) GetStickyTaskList() (o *TaskList) { if v != nil && v.StickyTaskList != nil { return v.StickyTaskList } return } // GetClientFeatureVersion is an internal getter (TBD...) func (v *GetMutableStateResponse) GetClientFeatureVersion() (o string) { if v != nil { return v.ClientFeatureVersion } return } // GetClientImpl is an internal getter (TBD...) func (v *GetMutableStateResponse) GetClientImpl() (o string) { if v != nil { return v.ClientImpl } return } // GetIsWorkflowRunning is an internal getter (TBD...) func (v *GetMutableStateResponse) GetIsWorkflowRunning() (o bool) { if v != nil { return v.IsWorkflowRunning } return } // GetStickyTaskListScheduleToStartTimeout is an internal getter (TBD...) func (v *GetMutableStateResponse) GetStickyTaskListScheduleToStartTimeout() (o int32) { if v != nil && v.StickyTaskListScheduleToStartTimeout != nil { return *v.StickyTaskListScheduleToStartTimeout } return } // GetCurrentBranchToken is an internal getter (TBD...) func (v *GetMutableStateResponse) GetCurrentBranchToken() (o []byte) { if v != nil && v.CurrentBranchToken != nil { return v.CurrentBranchToken } return } // GetWorkflowCloseState is an internal getter (TBD...) func (v *GetMutableStateResponse) GetWorkflowCloseState() (o int32) { if v != nil && v.WorkflowCloseState != nil { return *v.WorkflowCloseState } return } // GetVersionHistories is an internal getter (TBD...) func (v *GetMutableStateResponse) GetVersionHistories() (o *VersionHistories) { if v != nil && v.VersionHistories != nil { return v.VersionHistories } return } // GetIsStickyTaskListEnabled is an internal getter (TBD...) func (v *GetMutableStateResponse) GetIsStickyTaskListEnabled() (o bool) { if v != nil { return v.IsStickyTaskListEnabled } return } // NotifyFailoverMarkersRequest is an internal type (TBD...) type NotifyFailoverMarkersRequest struct { FailoverMarkerTokens []*FailoverMarkerToken `json:"failoverMarkerTokens,omitempty"` } // GetFailoverMarkerTokens is an internal getter (TBD...) func (v *NotifyFailoverMarkersRequest) GetFailoverMarkerTokens() (o []*FailoverMarkerToken) { if v != nil && v.FailoverMarkerTokens != nil { return v.FailoverMarkerTokens } return } // ParentExecutionInfo is an internal type (TBD...) type ParentExecutionInfo struct { DomainUUID string `json:"domainUUID,omitempty"` Domain string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` InitiatedID int64 `json:"initiatedId,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *ParentExecutionInfo) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetExecution is an internal getter (TBD...) func (v *ParentExecutionInfo) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // PollMutableStateRequest is an internal type (TBD...) type PollMutableStateRequest struct { DomainUUID string `json:"domainUUID,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` ExpectedNextEventID int64 `json:"expectedNextEventId,omitempty"` CurrentBranchToken []byte `json:"currentBranchToken,omitempty"` VersionHistoryItem *VersionHistoryItem `json:"versionHistoryItem,omitempty"` } func (p *PollMutableStateRequest) GetVersionHistoryItem() *VersionHistoryItem { if p.VersionHistoryItem == nil { return nil } return p.VersionHistoryItem } // GetDomainUUID is an internal getter (TBD...) func (p *PollMutableStateRequest) GetDomainUUID() (o string) { if p != nil { return p.DomainUUID } return } // PollMutableStateResponse is an internal type (TBD...) type PollMutableStateResponse struct { Execution *WorkflowExecution `json:"execution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` NextEventID int64 `json:"NextEventId,omitempty"` PreviousStartedEventID *int64 `json:"PreviousStartedEventId,omitempty"` LastFirstEventID int64 `json:"LastFirstEventId,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` StickyTaskList *TaskList `json:"stickyTaskList,omitempty"` ClientLibraryVersion string `json:"clientLibraryVersion,omitempty"` ClientFeatureVersion string `json:"clientFeatureVersion,omitempty"` ClientImpl string `json:"clientImpl,omitempty"` StickyTaskListScheduleToStartTimeout *int32 `json:"stickyTaskListScheduleToStartTimeout,omitempty"` CurrentBranchToken []byte `json:"currentBranchToken,omitempty"` VersionHistories *VersionHistories `json:"versionHistories,omitempty"` WorkflowState *int32 `json:"workflowState,omitempty"` WorkflowCloseState *int32 `json:"workflowCloseState,omitempty"` } // GetNextEventID is an internal getter (TBD...) func (v *PollMutableStateResponse) GetNextEventID() (o int64) { if v != nil { return v.NextEventID } return } // GetLastFirstEventID is an internal getter (TBD...) func (v *PollMutableStateResponse) GetLastFirstEventID() (o int64) { if v != nil { return v.LastFirstEventID } return } // GetWorkflowCloseState is an internal getter (TBD...) func (v *PollMutableStateResponse) GetWorkflowCloseState() (o int32) { if v != nil && v.WorkflowCloseState != nil { return *v.WorkflowCloseState } return } type QueueState struct { VirtualQueueStates map[int64]*VirtualQueueState ExclusiveMaxReadLevel *TaskKey } func (q *QueueState) Copy() (o *QueueState) { if q == nil { return } var virtualQueueStates map[int64]*VirtualQueueState if q.VirtualQueueStates != nil { virtualQueueStates = make(map[int64]*VirtualQueueState) for key, value := range q.VirtualQueueStates { virtualQueueStates[key] = value.Copy() } } o = &QueueState{ VirtualQueueStates: virtualQueueStates, ExclusiveMaxReadLevel: q.ExclusiveMaxReadLevel.Copy(), } return } type VirtualQueueState struct { VirtualSliceStates []*VirtualSliceState } func (v *VirtualQueueState) Copy() (o *VirtualQueueState) { if v == nil { return } var virtualSliceStates []*VirtualSliceState if v.VirtualSliceStates != nil { virtualSliceStates = make([]*VirtualSliceState, len(v.VirtualSliceStates)) for i, slice := range v.VirtualSliceStates { virtualSliceStates[i] = slice.Copy() } } o = &VirtualQueueState{ VirtualSliceStates: virtualSliceStates, } return } type VirtualSliceState struct { TaskRange *TaskRange Predicate *Predicate } func (v *VirtualSliceState) Copy() (o *VirtualSliceState) { if v == nil { return } o = &VirtualSliceState{ TaskRange: v.TaskRange.Copy(), Predicate: v.Predicate.Copy(), } return } type TaskRange struct { InclusiveMin *TaskKey ExclusiveMax *TaskKey } func (t *TaskRange) Copy() (o *TaskRange) { if t == nil { return } o = &TaskRange{ InclusiveMin: t.InclusiveMin.Copy(), ExclusiveMax: t.ExclusiveMax.Copy(), } return } type TaskKey struct { ScheduledTimeNano int64 TaskID int64 } func (t *TaskKey) Copy() (o *TaskKey) { if t == nil { return } o = &TaskKey{ ScheduledTimeNano: t.ScheduledTimeNano, TaskID: t.TaskID, } return } // ProcessingQueueState is an internal type (TBD...) type ProcessingQueueState struct { Level *int32 `json:"level,omitempty"` AckLevel *int64 `json:"ackLevel,omitempty"` MaxLevel *int64 `json:"maxLevel,omitempty"` DomainFilter *DomainFilter `json:"domainFilter,omitempty"` } // GetLevel is an internal getter (TBD...) func (v *ProcessingQueueState) GetLevel() (o int32) { if v != nil && v.Level != nil { return *v.Level } return } // GetAckLevel is an internal getter (TBD...) func (v *ProcessingQueueState) GetAckLevel() (o int64) { if v != nil && v.AckLevel != nil { return *v.AckLevel } return } // GetMaxLevel is an internal getter (TBD...) func (v *ProcessingQueueState) GetMaxLevel() (o int64) { if v != nil && v.MaxLevel != nil { return *v.MaxLevel } return } // GetDomainFilter is an internal getter (TBD...) func (v *ProcessingQueueState) GetDomainFilter() (o *DomainFilter) { if v != nil && v.DomainFilter != nil { return v.DomainFilter } return } // ProcessingQueueStates is an internal type (TBD...) type ProcessingQueueStates struct { StatesByCluster map[string][]*ProcessingQueueState `json:"statesByCluster,omitempty"` } // HistoryQueryWorkflowRequest is an internal type (TBD...) type HistoryQueryWorkflowRequest struct { DomainUUID string `json:"domainUUID,omitempty"` Request *QueryWorkflowRequest `json:"request,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryQueryWorkflowRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetRequest is an internal getter (TBD...) func (v *HistoryQueryWorkflowRequest) GetRequest() (o *QueryWorkflowRequest) { if v != nil && v.Request != nil { return v.Request } return } // HistoryQueryWorkflowResponse is an internal type (TBD...) type HistoryQueryWorkflowResponse struct { Response *QueryWorkflowResponse `json:"response,omitempty"` } // GetResponse is an internal getter (TBD...) func (v *HistoryQueryWorkflowResponse) GetResponse() (o *QueryWorkflowResponse) { if v != nil && v.Response != nil { return v.Response } return } // HistoryReapplyEventsRequest is an internal type (TBD...) type HistoryReapplyEventsRequest struct { DomainUUID string `json:"domainUUID,omitempty"` Request *ReapplyEventsRequest `json:"request,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryReapplyEventsRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetRequest is an internal getter (TBD...) func (v *HistoryReapplyEventsRequest) GetRequest() (o *ReapplyEventsRequest) { if v != nil && v.Request != nil { return v.Request } return } // HistoryRecordActivityTaskHeartbeatRequest is an internal type (TBD...) type HistoryRecordActivityTaskHeartbeatRequest struct { DomainUUID string `json:"domainUUID,omitempty"` HeartbeatRequest *RecordActivityTaskHeartbeatRequest `json:"heartbeatRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryRecordActivityTaskHeartbeatRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // RecordActivityTaskStartedRequest is an internal type (TBD...) type RecordActivityTaskStartedRequest struct { DomainUUID string `json:"domainUUID,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` ScheduleID int64 `json:"scheduleId,omitempty"` TaskID int64 `json:"taskId,omitempty"` RequestID string `json:"requestId,omitempty"` PollRequest *PollForActivityTaskRequest `json:"pollRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *RecordActivityTaskStartedRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetScheduleID is an internal getter (TBD...) func (v *RecordActivityTaskStartedRequest) GetScheduleID() (o int64) { if v != nil { return v.ScheduleID } return } // GetTaskID is an internal getter (TBD...) func (v *RecordActivityTaskStartedRequest) GetTaskID() (o int64) { if v != nil { return v.TaskID } return } // GetRequestID is an internal getter (TBD...) func (v *RecordActivityTaskStartedRequest) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // RecordActivityTaskStartedResponse is an internal type (TBD...) type RecordActivityTaskStartedResponse struct { ScheduledEvent *HistoryEvent `json:"scheduledEvent,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Attempt int64 `json:"attempt,omitempty"` ScheduledTimestampOfThisAttempt *int64 `json:"scheduledTimestampOfThisAttempt,omitempty"` HeartbeatDetails []byte `json:"heartbeatDetails,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` WorkflowDomain string `json:"workflowDomain,omitempty"` } // GetAttempt is an internal getter (TBD...) func (v *RecordActivityTaskStartedResponse) GetAttempt() (o int64) { if v != nil { return v.Attempt } return } // GetScheduledTimestampOfThisAttempt is an internal getter (TBD...) func (v *RecordActivityTaskStartedResponse) GetScheduledTimestampOfThisAttempt() (o int64) { if v != nil && v.ScheduledTimestampOfThisAttempt != nil { return *v.ScheduledTimestampOfThisAttempt } return } // RecordChildExecutionCompletedRequest is an internal type (TBD...) type RecordChildExecutionCompletedRequest struct { DomainUUID string `json:"domainUUID,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` InitiatedID int64 `json:"initiatedId,omitempty"` CompletedExecution *WorkflowExecution `json:"completedExecution,omitempty"` CompletionEvent *HistoryEvent `json:"completionEvent,omitempty"` StartedID int64 `json:"startedId,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *RecordChildExecutionCompletedRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // RecordDecisionTaskStartedRequest is an internal type (TBD...) type RecordDecisionTaskStartedRequest struct { DomainUUID string `json:"domainUUID,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` ScheduleID int64 `json:"scheduleId,omitempty"` TaskID int64 `json:"taskId,omitempty"` RequestID string `json:"requestId,omitempty"` PollRequest *PollForDecisionTaskRequest `json:"pollRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *RecordDecisionTaskStartedRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetScheduleID is an internal getter (TBD...) func (v *RecordDecisionTaskStartedRequest) GetScheduleID() (o int64) { if v != nil { return v.ScheduleID } return } // GetRequestID is an internal getter (TBD...) func (v *RecordDecisionTaskStartedRequest) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // RecordDecisionTaskStartedResponse is an internal type (TBD...) type RecordDecisionTaskStartedResponse struct { WorkflowType *WorkflowType `json:"workflowType,omitempty"` PreviousStartedEventID *int64 `json:"previousStartedEventId,omitempty"` ScheduledEventID int64 `json:"scheduledEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` NextEventID int64 `json:"nextEventId,omitempty"` Attempt int64 `json:"attempt,omitempty"` StickyExecutionEnabled bool `json:"stickyExecutionEnabled,omitempty"` DecisionInfo *TransientDecisionInfo `json:"decisionInfo,omitempty"` WorkflowExecutionTaskList *TaskList `json:"WorkflowExecutionTaskList,omitempty"` EventStoreVersion int32 `json:"eventStoreVersion,omitempty"` BranchToken []byte `json:"branchToken,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Queries map[string]*WorkflowQuery `json:"queries,omitempty"` HistorySize int64 `json:"historySize,omitempty"` } // GetPreviousStartedEventID is an internal getter (TBD...) func (v *RecordDecisionTaskStartedResponse) GetPreviousStartedEventID() (o int64) { if v != nil && v.PreviousStartedEventID != nil { return *v.PreviousStartedEventID } return } // GetScheduledEventID is an internal getter (TBD...) func (v *RecordDecisionTaskStartedResponse) GetScheduledEventID() (o int64) { if v != nil { return v.ScheduledEventID } return } // GetAttempt is an internal getter (TBD...) func (v *RecordDecisionTaskStartedResponse) GetAttempt() (o int64) { if v != nil { return v.Attempt } return } // HistoryRefreshWorkflowTasksRequest is an internal type (TBD...) type HistoryRefreshWorkflowTasksRequest struct { DomainUIID string `json:"domainUIID,omitempty"` Request *RefreshWorkflowTasksRequest `json:"request,omitempty"` } // GetRequest is an internal getter (TBD...) func (v *HistoryRefreshWorkflowTasksRequest) GetRequest() (o *RefreshWorkflowTasksRequest) { if v != nil && v.Request != nil { return v.Request } return } // RemoveSignalMutableStateRequest is an internal type (TBD...) type RemoveSignalMutableStateRequest struct { DomainUUID string `json:"domainUUID,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` RequestID string `json:"requestId,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *RemoveSignalMutableStateRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetRequestID is an internal getter (TBD...) func (v *RemoveSignalMutableStateRequest) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // ReplicateEventsV2Request is an internal type (TBD...) type ReplicateEventsV2Request struct { DomainUUID string `json:"domainUUID,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` VersionHistoryItems []*VersionHistoryItem `json:"versionHistoryItems,omitempty"` Events *DataBlob `json:"events,omitempty"` NewRunEvents *DataBlob `json:"newRunEvents,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *ReplicateEventsV2Request) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // HistoryRequestCancelWorkflowExecutionRequest is an internal type (TBD...) type HistoryRequestCancelWorkflowExecutionRequest struct { DomainUUID string `json:"domainUUID,omitempty"` CancelRequest *RequestCancelWorkflowExecutionRequest `json:"cancelRequest,omitempty"` ExternalInitiatedEventID *int64 `json:"externalInitiatedEventId,omitempty"` ExternalWorkflowExecution *WorkflowExecution `json:"externalWorkflowExecution,omitempty"` ChildWorkflowOnly bool `json:"childWorkflowOnly,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryRequestCancelWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetCancelRequest is an internal getter (TBD...) func (v *HistoryRequestCancelWorkflowExecutionRequest) GetCancelRequest() (o *RequestCancelWorkflowExecutionRequest) { if v != nil && v.CancelRequest != nil { return v.CancelRequest } return } // GetExternalWorkflowExecution is an internal getter (TBD...) func (v *HistoryRequestCancelWorkflowExecutionRequest) GetExternalWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.ExternalWorkflowExecution != nil { return v.ExternalWorkflowExecution } return } // GetChildWorkflowOnly is an internal getter (TBD...) func (v *HistoryRequestCancelWorkflowExecutionRequest) GetChildWorkflowOnly() (o bool) { if v != nil { return v.ChildWorkflowOnly } return } // HistoryResetStickyTaskListRequest is an internal type (TBD...) type HistoryResetStickyTaskListRequest struct { DomainUUID string `json:"domainUUID,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryResetStickyTaskListRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // HistoryResetStickyTaskListResponse is an internal type (TBD...) type HistoryResetStickyTaskListResponse struct { } // HistoryResetWorkflowExecutionRequest is an internal type (TBD...) type HistoryResetWorkflowExecutionRequest struct { DomainUUID string `json:"domainUUID,omitempty"` ResetRequest *ResetWorkflowExecutionRequest `json:"resetRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryResetWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // HistoryRespondActivityTaskCanceledRequest is an internal type (TBD...) type HistoryRespondActivityTaskCanceledRequest struct { DomainUUID string `json:"domainUUID,omitempty"` CancelRequest *RespondActivityTaskCanceledRequest `json:"cancelRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryRespondActivityTaskCanceledRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // HistoryRespondActivityTaskCompletedRequest is an internal type (TBD...) type HistoryRespondActivityTaskCompletedRequest struct { DomainUUID string `json:"domainUUID,omitempty"` CompleteRequest *RespondActivityTaskCompletedRequest `json:"completeRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryRespondActivityTaskCompletedRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // HistoryRespondActivityTaskFailedRequest is an internal type (TBD...) type HistoryRespondActivityTaskFailedRequest struct { DomainUUID string `json:"domainUUID,omitempty"` FailedRequest *RespondActivityTaskFailedRequest `json:"failedRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryRespondActivityTaskFailedRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // HistoryRespondDecisionTaskCompletedRequest is an internal type (TBD...) type HistoryRespondDecisionTaskCompletedRequest struct { DomainUUID string `json:"domainUUID,omitempty"` CompleteRequest *RespondDecisionTaskCompletedRequest `json:"completeRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryRespondDecisionTaskCompletedRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetCompleteRequest is an internal getter (TBD...) func (v *HistoryRespondDecisionTaskCompletedRequest) GetCompleteRequest() (o *RespondDecisionTaskCompletedRequest) { if v != nil && v.CompleteRequest != nil { return v.CompleteRequest } return } // HistoryRespondDecisionTaskCompletedResponse is an internal type (TBD...) type HistoryRespondDecisionTaskCompletedResponse struct { StartedResponse *RecordDecisionTaskStartedResponse `json:"startedResponse,omitempty"` ActivitiesToDispatchLocally map[string]*ActivityLocalDispatchInfo `json:"activitiesToDispatchLocally,omitempty"` } // HistoryRespondDecisionTaskFailedRequest is an internal type (TBD...) type HistoryRespondDecisionTaskFailedRequest struct { DomainUUID string `json:"domainUUID,omitempty"` FailedRequest *RespondDecisionTaskFailedRequest `json:"failedRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryRespondDecisionTaskFailedRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // ScheduleDecisionTaskRequest is an internal type (TBD...) type ScheduleDecisionTaskRequest struct { DomainUUID string `json:"domainUUID,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` IsFirstDecision bool `json:"isFirstDecision,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *ScheduleDecisionTaskRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // ShardOwnershipLostError is an internal type (TBD...) type ShardOwnershipLostError struct { Message string `json:"message,omitempty"` Owner string `json:"owner,omitempty"` } // GetOwner is an internal getter (TBD...) func (v *ShardOwnershipLostError) GetOwner() (o string) { if v != nil { return v.Owner } return } // HistorySignalWithStartWorkflowExecutionRequest is an internal type (TBD...) type HistorySignalWithStartWorkflowExecutionRequest struct { DomainUUID string `json:"domainUUID,omitempty"` SignalWithStartRequest *SignalWithStartWorkflowExecutionRequest `json:"signalWithStartRequest,omitempty"` PartitionConfig map[string]string } // GetDomainUUID is an internal getter (TBD...) func (v *HistorySignalWithStartWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetPartitionConfig is an internal getter (TBD...) func (v *HistorySignalWithStartWorkflowExecutionRequest) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // HistorySignalWorkflowExecutionRequest is an internal type (TBD...) type HistorySignalWorkflowExecutionRequest struct { DomainUUID string `json:"domainUUID,omitempty"` SignalRequest *SignalWorkflowExecutionRequest `json:"signalRequest,omitempty"` ExternalWorkflowExecution *WorkflowExecution `json:"externalWorkflowExecution,omitempty"` ChildWorkflowOnly bool `json:"childWorkflowOnly,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistorySignalWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetChildWorkflowOnly is an internal getter (TBD...) func (v *HistorySignalWorkflowExecutionRequest) GetChildWorkflowOnly() (o bool) { if v != nil { return v.ChildWorkflowOnly } return } // HistoryStartWorkflowExecutionRequest is an internal type (TBD...) type HistoryStartWorkflowExecutionRequest struct { DomainUUID string `json:"domainUUID,omitempty"` StartRequest *StartWorkflowExecutionRequest `json:"startRequest,omitempty"` ParentExecutionInfo *ParentExecutionInfo `json:"parentExecutionInfo,omitempty"` Attempt int32 `json:"attempt,omitempty"` ExpirationTimestamp *int64 `json:"expirationTimestamp,omitempty"` ContinueAsNewInitiator *ContinueAsNewInitiator `json:"continueAsNewInitiator,omitempty"` ContinuedFailureReason *string `json:"continuedFailureReason,omitempty"` ContinuedFailureDetails []byte `json:"continuedFailureDetails,omitempty"` LastCompletionResult []byte `json:"lastCompletionResult,omitempty"` FirstDecisionTaskBackoffSeconds *int32 `json:"firstDecisionTaskBackoffSeconds,omitempty"` PartitionConfig map[string]string } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryStartWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetAttempt is an internal getter (TBD...) func (v *HistoryStartWorkflowExecutionRequest) GetAttempt() (o int32) { if v != nil { return v.Attempt } return } // GetExpirationTimestamp is an internal getter (TBD...) func (v *HistoryStartWorkflowExecutionRequest) GetExpirationTimestamp() (o int64) { if v != nil && v.ExpirationTimestamp != nil { return *v.ExpirationTimestamp } return } // GetFirstDecisionTaskBackoffSeconds is an internal getter (TBD...) func (v *HistoryStartWorkflowExecutionRequest) GetFirstDecisionTaskBackoffSeconds() (o int32) { if v != nil && v.FirstDecisionTaskBackoffSeconds != nil { return *v.FirstDecisionTaskBackoffSeconds } return } // GetPartitionConfig is an internal getter (TBD...) func (v *HistoryStartWorkflowExecutionRequest) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // SyncActivityRequest is an internal type (TBD...) type SyncActivityRequest struct { DomainID string `json:"domainId,omitempty"` WorkflowID string `json:"workflowId,omitempty"` RunID string `json:"runId,omitempty"` Version int64 `json:"version,omitempty"` ScheduledID int64 `json:"scheduledId,omitempty"` ScheduledTime *int64 `json:"scheduledTime,omitempty"` StartedID int64 `json:"startedId,omitempty"` StartedTime *int64 `json:"startedTime,omitempty"` LastHeartbeatTime *int64 `json:"lastHeartbeatTime,omitempty"` Details []byte `json:"details,omitempty"` Attempt int32 `json:"attempt,omitempty"` LastFailureReason *string `json:"lastFailureReason,omitempty"` LastWorkerIdentity string `json:"lastWorkerIdentity,omitempty"` LastFailureDetails []byte `json:"lastFailureDetails,omitempty"` VersionHistory *VersionHistory `json:"versionHistory,omitempty"` } // GetDomainID is an internal getter (TBD...) func (v *SyncActivityRequest) GetDomainID() (o string) { if v != nil { return v.DomainID } return } // GetWorkflowID is an internal getter (TBD...) func (v *SyncActivityRequest) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *SyncActivityRequest) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetVersion is an internal getter (TBD...) func (v *SyncActivityRequest) GetVersion() (o int64) { if v != nil { return v.Version } return } // GetScheduledID is an internal getter (TBD...) func (v *SyncActivityRequest) GetScheduledID() (o int64) { if v != nil { return v.ScheduledID } return } // GetScheduledTime is an internal getter (TBD...) func (v *SyncActivityRequest) GetScheduledTime() (o int64) { if v != nil && v.ScheduledTime != nil { return *v.ScheduledTime } return } // GetStartedID is an internal getter (TBD...) func (v *SyncActivityRequest) GetStartedID() (o int64) { if v != nil { return v.StartedID } return } // GetStartedTime is an internal getter (TBD...) func (v *SyncActivityRequest) GetStartedTime() (o int64) { if v != nil && v.StartedTime != nil { return *v.StartedTime } return } // GetLastHeartbeatTime is an internal getter (TBD...) func (v *SyncActivityRequest) GetLastHeartbeatTime() (o int64) { if v != nil && v.LastHeartbeatTime != nil { return *v.LastHeartbeatTime } return } // GetDetails is an internal getter (TBD...) func (v *SyncActivityRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // GetAttempt is an internal getter (TBD...) func (v *SyncActivityRequest) GetAttempt() (o int32) { if v != nil { return v.Attempt } return } // GetLastFailureReason is an internal getter (TBD...) func (v *SyncActivityRequest) GetLastFailureReason() (o string) { if v != nil && v.LastFailureReason != nil { return *v.LastFailureReason } return } // GetLastWorkerIdentity is an internal getter (TBD...) func (v *SyncActivityRequest) GetLastWorkerIdentity() (o string) { if v != nil { return v.LastWorkerIdentity } return } // GetLastFailureDetails is an internal getter (TBD...) func (v *SyncActivityRequest) GetLastFailureDetails() (o []byte) { if v != nil && v.LastFailureDetails != nil { return v.LastFailureDetails } return } // GetVersionHistory is an internal getter (TBD...) func (v *SyncActivityRequest) GetVersionHistory() (o *VersionHistory) { if v != nil && v.VersionHistory != nil { return v.VersionHistory } return } // SyncShardStatusRequest is an internal type (TBD...) type SyncShardStatusRequest struct { SourceCluster string `json:"sourceCluster,omitempty"` ShardID int64 `json:"shardId,omitempty"` Timestamp *int64 `json:"timestamp,omitempty"` } // GetSourceCluster is an internal getter (TBD...) func (v *SyncShardStatusRequest) GetSourceCluster() (o string) { if v != nil { return v.SourceCluster } return } // GetShardID is an internal getter (TBD...) func (v *SyncShardStatusRequest) GetShardID() (o int64) { if v != nil { return v.ShardID } return } // GetTimestamp is an internal getter (TBD...) func (v *SyncShardStatusRequest) GetTimestamp() (o int64) { if v != nil && v.Timestamp != nil { return *v.Timestamp } return } // HistoryTerminateWorkflowExecutionRequest is an internal type (TBD...) type HistoryTerminateWorkflowExecutionRequest struct { DomainUUID string `json:"domainUUID,omitempty"` TerminateRequest *TerminateWorkflowExecutionRequest `json:"terminateRequest,omitempty"` ExternalWorkflowExecution *WorkflowExecution `json:"externalWorkflowExecution,omitempty"` ChildWorkflowOnly bool `json:"childWorkflowOnly,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *HistoryTerminateWorkflowExecutionRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetTerminateRequest is an internal getter (TBD...) func (v *HistoryTerminateWorkflowExecutionRequest) GetTerminateRequest() (o *TerminateWorkflowExecutionRequest) { if v != nil && v.TerminateRequest != nil { return v.TerminateRequest } return } // GetExternalWorkflowExecution is an internal getter (TBD...) func (v *HistoryTerminateWorkflowExecutionRequest) GetExternalWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.ExternalWorkflowExecution != nil { return v.ExternalWorkflowExecution } return } // GetChildWorkflowOnly is an internal getter (TBD...) func (v *HistoryTerminateWorkflowExecutionRequest) GetChildWorkflowOnly() (o bool) { if v != nil { return v.ChildWorkflowOnly } return } // GetFailoverInfoRequest is an internal type (TBD...) type GetFailoverInfoRequest struct { DomainID string `json:"domainID,omitempty"` } // GetDomainID is an internal getter (TBD...) func (v *GetFailoverInfoRequest) GetDomainID() (o string) { if v != nil { return v.DomainID } return } // GetFailoverInfoResponse is an internal type (TBD...) type GetFailoverInfoResponse struct { CompletedShardCount int32 `json:"completedShardCount,omitempty"` PendingShards []int32 `json:"pendingShards,omitempty"` } // GetCompletedShardCount is an internal getter (TBD...) func (v *GetFailoverInfoResponse) GetCompletedShardCount() (o int32) { if v != nil { return v.CompletedShardCount } return } // GetPendingShards is an internal getter (TBD...) func (v *GetFailoverInfoResponse) GetPendingShards() (o []int32) { if v != nil { return v.PendingShards } return } type RatelimitUpdateRequest struct { Any *Any `json:"any"` } type RatelimitUpdateResponse struct { Any *Any `json:"any"` } ================================================ FILE: common/types/history_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "testing" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" ) var ( domainUUID = "test-domainUUID" mutableStateInCache = "test-cache" mutableStateInDatabase = "test-database" domainIDs = []string{"id1", "id2", "id3"} shardIDs = []int32{1, 2, 3} expectedNextEventID = int64(123) nextEventID = int64(456) previousStartedEventID = int64(789) stickyTaskListTimeout = int32(100) workflowCloseState = int32(2) clientFeatureVersion = "v1.0" clientImpl = "test-client" currentBranchToken = []byte("branch-token") requestID = "test-requestID" scheduleID = int64(100) taskID = int64(200) startedTimestamp = int64(123456789) attempt = int64(3) attempt32 = int32(3) expirationTimestamp = int64(1234567890) firstDecisionBackoff = int32(60) partitionConfig = map[string]string{"partition1": "value1"} scheduledTime = int64(1617181920) startedTime = int64(1617182920) lastHeartbeatTime = int64(1617183920) lastFailureReason = "test-failure" lastWorkerIdentity = "test-worker" details = []byte("test-details") lastFailureDetails = []byte("failure-details") versionHistory = &VersionHistory{} sourceCluster = "source-cluster" shardID = int64(1000) terminateRequest = &TerminateWorkflowExecutionRequest{} externalWorkflowExecution = &WorkflowExecution{} childWorkflowOnly = true pendingShards = []int32{1, 2, 3} completedShardCount = int32(5) workflowID = "test-workflow-id" runID = "test-run-id" scheduledID = int64(202) startedID = int64(303) version = int64(10) ) func TestDescribeMutableStateRequest(t *testing.T) { testStruct := DescribeMutableStateRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *DescribeMutableStateRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) // Should return empty string when struct is nil } func TestDescribeMutableStateResponse(t *testing.T) { testStruct := DescribeMutableStateResponse{ MutableStateInCache: mutableStateInCache, MutableStateInDatabase: mutableStateInDatabase, } assert.Equal(t, mutableStateInCache, testStruct.MutableStateInCache) assert.Equal(t, mutableStateInDatabase, testStruct.MutableStateInDatabase) } func TestHistoryDescribeWorkflowExecutionRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryDescribeWorkflowExecutionRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *HistoryDescribeWorkflowExecutionRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) // Should return empty string when struct is nil } func TestDomainFilter_GetDomainIDs(t *testing.T) { testStruct := DomainFilter{ DomainIDs: domainIDs, } // Non-nil struct test res := testStruct.GetDomainIDs() assert.Equal(t, domainIDs, res) // Nil struct test var nilStruct *DomainFilter res = nilStruct.GetDomainIDs() assert.Empty(t, res) // Should return empty slice when struct is nil } func TestDomainFilter_GetReverseMatch(t *testing.T) { testStruct := DomainFilter{ ReverseMatch: true, } // Non-nil struct test res := testStruct.GetReverseMatch() assert.True(t, res) // Nil struct test var nilStruct *DomainFilter res = nilStruct.GetReverseMatch() assert.False(t, res) // Should return false when struct is nil } func TestFailoverMarkerToken_GetShardIDs(t *testing.T) { testStruct := FailoverMarkerToken{ ShardIDs: shardIDs, } // Non-nil struct test res := testStruct.GetShardIDs() assert.Equal(t, shardIDs, res) // Nil struct test var nilStruct *FailoverMarkerToken res = nilStruct.GetShardIDs() assert.Empty(t, res) // Should return empty slice when struct is nil } func TestFailoverMarkerToken_GetFailoverMarker(t *testing.T) { failoverMarker := &FailoverMarkerAttributes{} testStruct := FailoverMarkerToken{ FailoverMarker: failoverMarker, } // Non-nil struct test res := testStruct.GetFailoverMarker() assert.Equal(t, failoverMarker, res) // Nil struct test var nilStruct *FailoverMarkerToken res = nilStruct.GetFailoverMarker() assert.Nil(t, res) // Should return nil when struct is nil } func TestGetMutableStateRequest_GetDomainUUID(t *testing.T) { testStruct := GetMutableStateRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *GetMutableStateRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestGetMutableStateRequest_GetExpectedNextEventID(t *testing.T) { testStruct := GetMutableStateRequest{ ExpectedNextEventID: expectedNextEventID, } // Non-nil struct test res := testStruct.GetExpectedNextEventID() assert.Equal(t, expectedNextEventID, res) // Nil struct test var nilStruct *GetMutableStateRequest res = nilStruct.GetExpectedNextEventID() assert.Equal(t, int64(0), res) } func TestGetMutableStateResponse_GetNextEventID(t *testing.T) { testStruct := GetMutableStateResponse{ NextEventID: nextEventID, } // Non-nil struct test res := testStruct.GetNextEventID() assert.Equal(t, nextEventID, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetNextEventID() assert.Equal(t, int64(0), res) } func TestGetMutableStateResponse_GetPreviousStartedEventID(t *testing.T) { testStruct := GetMutableStateResponse{ PreviousStartedEventID: &previousStartedEventID, } // Non-nil struct test res := testStruct.GetPreviousStartedEventID() assert.Equal(t, previousStartedEventID, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetPreviousStartedEventID() assert.Equal(t, int64(0), res) } func TestGetMutableStateResponse_GetStickyTaskList(t *testing.T) { stickyTaskList := &TaskList{} testStruct := GetMutableStateResponse{ StickyTaskList: stickyTaskList, } // Non-nil struct test res := testStruct.GetStickyTaskList() assert.Equal(t, stickyTaskList, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetStickyTaskList() assert.Nil(t, res) } func TestGetMutableStateResponse_GetClientFeatureVersion(t *testing.T) { testStruct := GetMutableStateResponse{ ClientFeatureVersion: clientFeatureVersion, } // Non-nil struct test res := testStruct.GetClientFeatureVersion() assert.Equal(t, clientFeatureVersion, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetClientFeatureVersion() assert.Equal(t, "", res) } func TestGetMutableStateResponse_GetClientImpl(t *testing.T) { testStruct := GetMutableStateResponse{ ClientImpl: clientImpl, } // Non-nil struct test res := testStruct.GetClientImpl() assert.Equal(t, clientImpl, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetClientImpl() assert.Equal(t, "", res) } func TestGetMutableStateResponse_GetIsWorkflowRunning(t *testing.T) { testStruct := GetMutableStateResponse{ IsWorkflowRunning: true, } // Non-nil struct test res := testStruct.GetIsWorkflowRunning() assert.True(t, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetIsWorkflowRunning() assert.False(t, res) } func TestGetMutableStateResponse_GetStickyTaskListScheduleToStartTimeout(t *testing.T) { testStruct := GetMutableStateResponse{ StickyTaskListScheduleToStartTimeout: &stickyTaskListTimeout, } // Non-nil struct test res := testStruct.GetStickyTaskListScheduleToStartTimeout() assert.Equal(t, stickyTaskListTimeout, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetStickyTaskListScheduleToStartTimeout() assert.Equal(t, int32(0), res) } func TestGetMutableStateResponse_GetCurrentBranchToken(t *testing.T) { testStruct := GetMutableStateResponse{ CurrentBranchToken: currentBranchToken, } // Non-nil struct test res := testStruct.GetCurrentBranchToken() assert.Equal(t, currentBranchToken, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetCurrentBranchToken() assert.Nil(t, res) } func TestGetMutableStateResponse_GetWorkflowCloseState(t *testing.T) { testStruct := GetMutableStateResponse{ WorkflowCloseState: &workflowCloseState, } // Non-nil struct test res := testStruct.GetWorkflowCloseState() assert.Equal(t, workflowCloseState, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetWorkflowCloseState() assert.Equal(t, int32(0), res) } func TestGetMutableStateResponse_GetVersionHistories(t *testing.T) { versionHistories := &VersionHistories{} testStruct := GetMutableStateResponse{ VersionHistories: versionHistories, } // Non-nil struct test res := testStruct.GetVersionHistories() assert.Equal(t, versionHistories, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetVersionHistories() assert.Nil(t, res) } func TestGetMutableStateResponse_GetIsStickyTaskListEnabled(t *testing.T) { testStruct := GetMutableStateResponse{ IsStickyTaskListEnabled: true, } // Non-nil struct test res := testStruct.GetIsStickyTaskListEnabled() assert.True(t, res) // Nil struct test var nilStruct *GetMutableStateResponse res = nilStruct.GetIsStickyTaskListEnabled() assert.False(t, res) } func TestNotifyFailoverMarkersRequest_GetFailoverMarkerTokens(t *testing.T) { failoverMarkerTokens := []*FailoverMarkerToken{} testStruct := NotifyFailoverMarkersRequest{ FailoverMarkerTokens: failoverMarkerTokens, } // Non-nil struct test res := testStruct.GetFailoverMarkerTokens() assert.Equal(t, failoverMarkerTokens, res) // Nil struct test var nilStruct *NotifyFailoverMarkersRequest res = nilStruct.GetFailoverMarkerTokens() assert.Nil(t, res) } func TestParentExecutionInfo_GetDomain(t *testing.T) { testStruct := ParentExecutionInfo{ Domain: "test-domain", } // Non-nil struct test res := testStruct.GetDomain() assert.Equal(t, "test-domain", res) // Nil struct test var nilStruct *ParentExecutionInfo res = nilStruct.GetDomain() assert.Equal(t, "", res) } func TestParentExecutionInfo_GetExecution(t *testing.T) { execution := &WorkflowExecution{} testStruct := ParentExecutionInfo{ Execution: execution, } // Non-nil struct test res := testStruct.GetExecution() assert.Equal(t, execution, res) // Nil struct test var nilStruct *ParentExecutionInfo res = nilStruct.GetExecution() assert.Nil(t, res) } func TestPollMutableStateRequest_GetDomainUUID(t *testing.T) { testStruct := PollMutableStateRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *PollMutableStateRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestPollMutableStateResponse_GetNextEventID(t *testing.T) { testStruct := PollMutableStateResponse{ NextEventID: nextEventID, } // Non-nil struct test res := testStruct.GetNextEventID() assert.Equal(t, nextEventID, res) // Nil struct test var nilStruct *PollMutableStateResponse res = nilStruct.GetNextEventID() assert.Equal(t, int64(0), res) } func TestPollMutableStateResponse_GetLastFirstEventID(t *testing.T) { testStruct := PollMutableStateResponse{ LastFirstEventID: nextEventID, } // Non-nil struct test res := testStruct.GetLastFirstEventID() assert.Equal(t, nextEventID, res) // Nil struct test var nilStruct *PollMutableStateResponse res = nilStruct.GetLastFirstEventID() assert.Equal(t, int64(0), res) } func TestPollMutableStateResponse_GetWorkflowCloseState(t *testing.T) { testStruct := PollMutableStateResponse{ WorkflowCloseState: &workflowCloseState, } // Non-nil struct test res := testStruct.GetWorkflowCloseState() assert.Equal(t, workflowCloseState, res) // Nil struct test var nilStruct *PollMutableStateResponse res = nilStruct.GetWorkflowCloseState() assert.Equal(t, int32(0), res) } func TestProcessingQueueState_GetLevel(t *testing.T) { level := int32(2) testStruct := ProcessingQueueState{ Level: &level, } // Non-nil struct test res := testStruct.GetLevel() assert.Equal(t, level, res) // Nil struct test var nilStruct *ProcessingQueueState res = nilStruct.GetLevel() assert.Equal(t, int32(0), res) } func TestProcessingQueueState_GetAckLevel(t *testing.T) { ackLevel := int64(1111) testStruct := ProcessingQueueState{ AckLevel: &ackLevel, } // Non-nil struct test res := testStruct.GetAckLevel() assert.Equal(t, ackLevel, res) // Nil struct test var nilStruct *ProcessingQueueState res = nilStruct.GetAckLevel() assert.Equal(t, int64(0), res) } func TestProcessingQueueState_GetMaxLevel(t *testing.T) { maxLevel := int64(2222) testStruct := ProcessingQueueState{ MaxLevel: &maxLevel, } // Non-nil struct test res := testStruct.GetMaxLevel() assert.Equal(t, maxLevel, res) // Nil struct test var nilStruct *ProcessingQueueState res = nilStruct.GetMaxLevel() assert.Equal(t, int64(0), res) } func TestProcessingQueueState_GetDomainFilter(t *testing.T) { domainFilter := &DomainFilter{} testStruct := ProcessingQueueState{ DomainFilter: domainFilter, } // Non-nil struct test res := testStruct.GetDomainFilter() assert.Equal(t, domainFilter, res) // Nil struct test var nilStruct *ProcessingQueueState res = nilStruct.GetDomainFilter() assert.Nil(t, res) } func TestHistoryQueryWorkflowRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryQueryWorkflowRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *HistoryQueryWorkflowRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryQueryWorkflowRequest_GetRequest(t *testing.T) { request := &QueryWorkflowRequest{} testStruct := HistoryQueryWorkflowRequest{ Request: request, } // Non-nil struct test res := testStruct.GetRequest() assert.Equal(t, request, res) // Nil struct test var nilStruct *HistoryQueryWorkflowRequest res = nilStruct.GetRequest() assert.Nil(t, res) } func TestHistoryQueryWorkflowResponse_GetResponse(t *testing.T) { response := &QueryWorkflowResponse{} testStruct := HistoryQueryWorkflowResponse{ Response: response, } // Non-nil struct test res := testStruct.GetResponse() assert.Equal(t, response, res) // Nil struct test var nilStruct *HistoryQueryWorkflowResponse res = nilStruct.GetResponse() assert.Nil(t, res) } func TestHistoryReapplyEventsRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryReapplyEventsRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *HistoryReapplyEventsRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryReapplyEventsRequest_GetRequest(t *testing.T) { request := &ReapplyEventsRequest{} testStruct := HistoryReapplyEventsRequest{ Request: request, } // Non-nil struct test res := testStruct.GetRequest() assert.Equal(t, request, res) // Nil struct test var nilStruct *HistoryReapplyEventsRequest res = nilStruct.GetRequest() assert.Nil(t, res) } func TestHistoryRecordActivityTaskHeartbeatRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *HistoryRecordActivityTaskHeartbeatRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestRecordActivityTaskStartedRequest_GetDomainUUID(t *testing.T) { testStruct := RecordActivityTaskStartedRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *RecordActivityTaskStartedRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestRecordActivityTaskStartedRequest_GetScheduleID(t *testing.T) { testStruct := RecordActivityTaskStartedRequest{ ScheduleID: scheduleID, } // Non-nil struct test res := testStruct.GetScheduleID() assert.Equal(t, scheduleID, res) // Nil struct test var nilStruct *RecordActivityTaskStartedRequest res = nilStruct.GetScheduleID() assert.Equal(t, int64(0), res) } func TestRecordActivityTaskStartedRequest_GetTaskID(t *testing.T) { testStruct := RecordActivityTaskStartedRequest{ TaskID: taskID, } // Non-nil struct test res := testStruct.GetTaskID() assert.Equal(t, taskID, res) // Nil struct test var nilStruct *RecordActivityTaskStartedRequest res = nilStruct.GetTaskID() assert.Equal(t, int64(0), res) } func TestRecordActivityTaskStartedRequest_GetRequestID(t *testing.T) { testStruct := RecordActivityTaskStartedRequest{ RequestID: requestID, } // Non-nil struct test res := testStruct.GetRequestID() assert.Equal(t, requestID, res) // Nil struct test var nilStruct *RecordActivityTaskStartedRequest res = nilStruct.GetRequestID() assert.Equal(t, "", res) } func TestRecordActivityTaskStartedResponse_GetAttempt(t *testing.T) { testStruct := RecordActivityTaskStartedResponse{ Attempt: attempt, } // Non-nil struct test res := testStruct.GetAttempt() assert.Equal(t, attempt, res) // Nil struct test var nilStruct *RecordActivityTaskStartedResponse res = nilStruct.GetAttempt() assert.Equal(t, int64(0), res) } func TestRecordActivityTaskStartedResponse_GetScheduledTimestampOfThisAttempt(t *testing.T) { testStruct := RecordActivityTaskStartedResponse{ ScheduledTimestampOfThisAttempt: &startedTimestamp, } // Non-nil struct test res := testStruct.GetScheduledTimestampOfThisAttempt() assert.Equal(t, startedTimestamp, res) // Nil struct test var nilStruct *RecordActivityTaskStartedResponse res = nilStruct.GetScheduledTimestampOfThisAttempt() assert.Equal(t, int64(0), res) } func TestRecordChildExecutionCompletedRequest_GetDomainUUID(t *testing.T) { testStruct := RecordChildExecutionCompletedRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *RecordChildExecutionCompletedRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestRecordDecisionTaskStartedRequest_GetDomainUUID(t *testing.T) { testStruct := RecordDecisionTaskStartedRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *RecordDecisionTaskStartedRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestRecordDecisionTaskStartedRequest_GetScheduleID(t *testing.T) { testStruct := RecordDecisionTaskStartedRequest{ ScheduleID: scheduleID, } // Non-nil struct test res := testStruct.GetScheduleID() assert.Equal(t, scheduleID, res) // Nil struct test var nilStruct *RecordDecisionTaskStartedRequest res = nilStruct.GetScheduleID() assert.Equal(t, int64(0), res) } func TestRecordDecisionTaskStartedRequest_GetRequestID(t *testing.T) { testStruct := RecordDecisionTaskStartedRequest{ RequestID: requestID, } // Non-nil struct test res := testStruct.GetRequestID() assert.Equal(t, requestID, res) // Nil struct test var nilStruct *RecordDecisionTaskStartedRequest res = nilStruct.GetRequestID() assert.Equal(t, "", res) } func TestRecordDecisionTaskStartedResponse_GetPreviousStartedEventID(t *testing.T) { testStruct := RecordDecisionTaskStartedResponse{ PreviousStartedEventID: &previousStartedEventID, } // Non-nil struct test res := testStruct.GetPreviousStartedEventID() assert.Equal(t, previousStartedEventID, res) // Nil struct test var nilStruct *RecordDecisionTaskStartedResponse res = nilStruct.GetPreviousStartedEventID() assert.Equal(t, int64(0), res) } func TestRecordDecisionTaskStartedResponse_GetScheduledEventID(t *testing.T) { scheduledEventID := int64(1001) testStruct := RecordDecisionTaskStartedResponse{ ScheduledEventID: scheduledEventID, } // Non-nil struct test res := testStruct.GetScheduledEventID() assert.Equal(t, scheduledEventID, res) // Nil struct test var nilStruct *RecordDecisionTaskStartedResponse res = nilStruct.GetScheduledEventID() assert.Equal(t, int64(0), res) } func TestRecordDecisionTaskStartedResponse_GetAttempt(t *testing.T) { testStruct := RecordDecisionTaskStartedResponse{ Attempt: attempt, } // Non-nil struct test res := testStruct.GetAttempt() assert.Equal(t, attempt, res) // Nil struct test var nilStruct *RecordDecisionTaskStartedResponse res = nilStruct.GetAttempt() assert.Equal(t, int64(0), res) } func TestRemoveSignalMutableStateRequest_GetDomainUUID(t *testing.T) { testStruct := RemoveSignalMutableStateRequest{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *RemoveSignalMutableStateRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestRemoveSignalMutableStateRequest_GetRequestID(t *testing.T) { testStruct := RemoveSignalMutableStateRequest{ RequestID: requestID, } // Non-nil struct test res := testStruct.GetRequestID() assert.Equal(t, requestID, res) // Nil struct test var nilStruct *RemoveSignalMutableStateRequest res = nilStruct.GetRequestID() assert.Equal(t, "", res) } func TestReplicateEventsV2Request_GetDomainUUID(t *testing.T) { testStruct := ReplicateEventsV2Request{ DomainUUID: domainUUID, } // Non-nil struct test res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) // Nil struct test var nilStruct *ReplicateEventsV2Request res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryRefreshWorkflowTasksRequest_GetRequest(t *testing.T) { // Define a sample RefreshWorkflowTasksRequest sampleRequest := &RefreshWorkflowTasksRequest{} // Test when the struct is non-nil and Request is non-nil testStruct := HistoryRefreshWorkflowTasksRequest{ Request: sampleRequest, } res := testStruct.GetRequest() assert.Equal(t, sampleRequest, res) // Test when the struct is non-nil but Request is nil testStructWithNilRequest := HistoryRefreshWorkflowTasksRequest{ Request: nil, } res = testStructWithNilRequest.GetRequest() assert.Nil(t, res) // Test when the struct itself is nil var nilStruct *HistoryRefreshWorkflowTasksRequest res = nilStruct.GetRequest() assert.Nil(t, res) } func TestHistoryRequestCancelWorkflowExecutionRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistoryRequestCancelWorkflowExecutionRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryRequestCancelWorkflowExecutionRequest_GetCancelRequest(t *testing.T) { cancelRequest := &RequestCancelWorkflowExecutionRequest{} testStruct := HistoryRequestCancelWorkflowExecutionRequest{ CancelRequest: cancelRequest, } res := testStruct.GetCancelRequest() assert.Equal(t, cancelRequest, res) var nilStruct *HistoryRequestCancelWorkflowExecutionRequest res = nilStruct.GetCancelRequest() assert.Nil(t, res) } func TestHistoryRequestCancelWorkflowExecutionRequest_GetExternalWorkflowExecution(t *testing.T) { externalWorkflowExecution := &WorkflowExecution{} testStruct := HistoryRequestCancelWorkflowExecutionRequest{ ExternalWorkflowExecution: externalWorkflowExecution, } res := testStruct.GetExternalWorkflowExecution() assert.Equal(t, externalWorkflowExecution, res) var nilStruct *HistoryRequestCancelWorkflowExecutionRequest res = nilStruct.GetExternalWorkflowExecution() assert.Nil(t, res) } func TestHistoryRequestCancelWorkflowExecutionRequest_GetChildWorkflowOnly(t *testing.T) { testStruct := HistoryRequestCancelWorkflowExecutionRequest{ ChildWorkflowOnly: true, } res := testStruct.GetChildWorkflowOnly() assert.True(t, res) var nilStruct *HistoryRequestCancelWorkflowExecutionRequest res = nilStruct.GetChildWorkflowOnly() assert.False(t, res) } func TestHistoryResetStickyTaskListRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryResetStickyTaskListRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistoryResetStickyTaskListRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryResetWorkflowExecutionRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryResetWorkflowExecutionRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistoryResetWorkflowExecutionRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryRespondActivityTaskCanceledRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryRespondActivityTaskCanceledRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistoryRespondActivityTaskCanceledRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryRespondActivityTaskCompletedRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryRespondActivityTaskCompletedRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistoryRespondActivityTaskCompletedRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryRespondActivityTaskFailedRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryRespondActivityTaskFailedRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistoryRespondActivityTaskFailedRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryRespondDecisionTaskCompletedRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistoryRespondDecisionTaskCompletedRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryRespondDecisionTaskCompletedRequest_GetCompleteRequest(t *testing.T) { completeRequest := &RespondDecisionTaskCompletedRequest{} testStruct := HistoryRespondDecisionTaskCompletedRequest{ CompleteRequest: completeRequest, } res := testStruct.GetCompleteRequest() assert.Equal(t, completeRequest, res) var nilStruct *HistoryRespondDecisionTaskCompletedRequest res = nilStruct.GetCompleteRequest() assert.Nil(t, res) } func TestHistoryRespondDecisionTaskFailedRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryRespondDecisionTaskFailedRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistoryRespondDecisionTaskFailedRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestScheduleDecisionTaskRequest_GetDomainUUID(t *testing.T) { testStruct := ScheduleDecisionTaskRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *ScheduleDecisionTaskRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestShardOwnershipLostError_GetOwner(t *testing.T) { owner := "test-owner" testStruct := ShardOwnershipLostError{ Owner: owner, } res := testStruct.GetOwner() assert.Equal(t, owner, res) var nilStruct *ShardOwnershipLostError res = nilStruct.GetOwner() assert.Equal(t, "", res) } func TestHistorySignalWithStartWorkflowExecutionRequest_GetDomainUUID(t *testing.T) { testStruct := HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistorySignalWithStartWorkflowExecutionRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistorySignalWithStartWorkflowExecutionRequest_GetPartitionConfig(t *testing.T) { partitionConfig := map[string]string{"partition1": "value1"} testStruct := HistorySignalWithStartWorkflowExecutionRequest{ PartitionConfig: partitionConfig, } res := testStruct.GetPartitionConfig() assert.Equal(t, partitionConfig, res) var nilStruct *HistorySignalWithStartWorkflowExecutionRequest res = nilStruct.GetPartitionConfig() assert.Nil(t, res) } func TestHistorySignalWorkflowExecutionRequest_GetDomainUUID(t *testing.T) { testStruct := HistorySignalWorkflowExecutionRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistorySignalWorkflowExecutionRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistorySignalWorkflowExecutionRequest_GetChildWorkflowOnly(t *testing.T) { testStruct := HistorySignalWorkflowExecutionRequest{ ChildWorkflowOnly: true, } res := testStruct.GetChildWorkflowOnly() assert.True(t, res) var nilStruct *HistorySignalWorkflowExecutionRequest res = nilStruct.GetChildWorkflowOnly() assert.False(t, res) } func TestHistoryStartWorkflowExecutionRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryStartWorkflowExecutionRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistoryStartWorkflowExecutionRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryStartWorkflowExecutionRequest_GetAttempt(t *testing.T) { testStruct := HistoryStartWorkflowExecutionRequest{ Attempt: attempt32, } res := testStruct.GetAttempt() assert.Equal(t, attempt32, res) var nilStruct *HistoryStartWorkflowExecutionRequest res = nilStruct.GetAttempt() assert.Equal(t, int32(0), res) } func TestHistoryStartWorkflowExecutionRequest_GetExpirationTimestamp(t *testing.T) { testStruct := HistoryStartWorkflowExecutionRequest{ ExpirationTimestamp: &expirationTimestamp, } res := testStruct.GetExpirationTimestamp() assert.Equal(t, expirationTimestamp, res) var nilStruct *HistoryStartWorkflowExecutionRequest res = nilStruct.GetExpirationTimestamp() assert.Equal(t, int64(0), res) } func TestHistoryStartWorkflowExecutionRequest_GetFirstDecisionTaskBackoffSeconds(t *testing.T) { testStruct := HistoryStartWorkflowExecutionRequest{ FirstDecisionTaskBackoffSeconds: &firstDecisionBackoff, } res := testStruct.GetFirstDecisionTaskBackoffSeconds() assert.Equal(t, firstDecisionBackoff, res) var nilStruct *HistoryStartWorkflowExecutionRequest res = nilStruct.GetFirstDecisionTaskBackoffSeconds() assert.Equal(t, int32(0), res) } func TestHistoryStartWorkflowExecutionRequest_GetPartitionConfig(t *testing.T) { testStruct := HistoryStartWorkflowExecutionRequest{ PartitionConfig: partitionConfig, } res := testStruct.GetPartitionConfig() assert.Equal(t, partitionConfig, res) var nilStruct *HistoryStartWorkflowExecutionRequest res = nilStruct.GetPartitionConfig() assert.Nil(t, res) } func TestSyncActivityRequest_GetDomainID(t *testing.T) { testStruct := SyncActivityRequest{ DomainID: domainUUID, } res := testStruct.GetDomainID() assert.Equal(t, domainUUID, res) var nilStruct *SyncActivityRequest res = nilStruct.GetDomainID() assert.Equal(t, "", res) } func TestSyncActivityRequest_GetScheduledTime(t *testing.T) { testStruct := SyncActivityRequest{ ScheduledTime: &scheduledTime, } res := testStruct.GetScheduledTime() assert.Equal(t, scheduledTime, res) var nilStruct *SyncActivityRequest res = nilStruct.GetScheduledTime() assert.Equal(t, int64(0), res) } func TestSyncActivityRequest_GetStartedTime(t *testing.T) { testStruct := SyncActivityRequest{ StartedTime: &startedTime, } res := testStruct.GetStartedTime() assert.Equal(t, startedTime, res) var nilStruct *SyncActivityRequest res = nilStruct.GetStartedTime() assert.Equal(t, int64(0), res) } func TestSyncActivityRequest_GetLastHeartbeatTime(t *testing.T) { testStruct := SyncActivityRequest{ LastHeartbeatTime: &lastHeartbeatTime, } res := testStruct.GetLastHeartbeatTime() assert.Equal(t, lastHeartbeatTime, res) var nilStruct *SyncActivityRequest res = nilStruct.GetLastHeartbeatTime() assert.Equal(t, int64(0), res) } func TestSyncActivityRequest_GetDetails(t *testing.T) { testStruct := SyncActivityRequest{ Details: details, } res := testStruct.GetDetails() assert.Equal(t, details, res) var nilStruct *SyncActivityRequest res = nilStruct.GetDetails() assert.Nil(t, res) } func TestSyncActivityRequest_GetLastFailureReason(t *testing.T) { testStruct := SyncActivityRequest{ LastFailureReason: &lastFailureReason, } res := testStruct.GetLastFailureReason() assert.Equal(t, lastFailureReason, res) var nilStruct *SyncActivityRequest res = nilStruct.GetLastFailureReason() assert.Equal(t, "", res) } func TestSyncActivityRequest_GetLastWorkerIdentity(t *testing.T) { testStruct := SyncActivityRequest{ LastWorkerIdentity: lastWorkerIdentity, } res := testStruct.GetLastWorkerIdentity() assert.Equal(t, lastWorkerIdentity, res) var nilStruct *SyncActivityRequest res = nilStruct.GetLastWorkerIdentity() assert.Equal(t, "", res) } func TestSyncActivityRequest_GetLastFailureDetails(t *testing.T) { testStruct := SyncActivityRequest{ LastFailureDetails: lastFailureDetails, } res := testStruct.GetLastFailureDetails() assert.Equal(t, lastFailureDetails, res) var nilStruct *SyncActivityRequest res = nilStruct.GetLastFailureDetails() assert.Nil(t, res) } func TestSyncActivityRequest_GetAttempt(t *testing.T) { testStruct := SyncActivityRequest{ Attempt: attempt32, } res := testStruct.GetAttempt() assert.Equal(t, attempt32, res) var nilStruct *SyncActivityRequest res = nilStruct.GetAttempt() assert.Equal(t, int32(0), res) } func TestSyncActivityRequest_GetWorkflowID(t *testing.T) { testStruct := SyncActivityRequest{ WorkflowID: workflowID, } res := testStruct.GetWorkflowID() assert.Equal(t, workflowID, res) var nilStruct *SyncActivityRequest res = nilStruct.GetWorkflowID() assert.Equal(t, "", res) } func TestSyncActivityRequest_GetRunID(t *testing.T) { testStruct := SyncActivityRequest{ RunID: runID, } res := testStruct.GetRunID() assert.Equal(t, runID, res) var nilStruct *SyncActivityRequest res = nilStruct.GetRunID() assert.Equal(t, "", res) } func TestSyncActivityRequest_GetVersion(t *testing.T) { testStruct := SyncActivityRequest{ Version: version, } res := testStruct.GetVersion() assert.Equal(t, version, res) var nilStruct *SyncActivityRequest res = nilStruct.GetVersion() assert.Equal(t, int64(0), res) } func TestSyncActivityRequest_GetScheduledID(t *testing.T) { testStruct := SyncActivityRequest{ ScheduledID: scheduledID, } res := testStruct.GetScheduledID() assert.Equal(t, scheduledID, res) var nilStruct *SyncActivityRequest res = nilStruct.GetScheduledID() assert.Equal(t, int64(0), res) } func TestSyncActivityRequest_GetStartedID(t *testing.T) { testStruct := SyncActivityRequest{ StartedID: startedID, } res := testStruct.GetStartedID() assert.Equal(t, startedID, res) var nilStruct *SyncActivityRequest res = nilStruct.GetStartedID() assert.Equal(t, int64(0), res) } func TestSyncActivityRequest_GetVersionHistory(t *testing.T) { testStruct := SyncActivityRequest{ VersionHistory: versionHistory, } res := testStruct.GetVersionHistory() assert.Equal(t, versionHistory, res) var nilStruct *SyncActivityRequest res = nilStruct.GetVersionHistory() assert.Nil(t, res) } func TestSyncShardStatusRequest_GetSourceCluster(t *testing.T) { testStruct := SyncShardStatusRequest{ SourceCluster: sourceCluster, } res := testStruct.GetSourceCluster() assert.Equal(t, sourceCluster, res) var nilStruct *SyncShardStatusRequest res = nilStruct.GetSourceCluster() assert.Equal(t, "", res) } func TestSyncShardStatusRequest_GetShardID(t *testing.T) { testStruct := SyncShardStatusRequest{ ShardID: shardID, } res := testStruct.GetShardID() assert.Equal(t, shardID, res) var nilStruct *SyncShardStatusRequest res = nilStruct.GetShardID() assert.Equal(t, int64(0), res) } func TestSyncShardStatusRequest_GetTimestamp(t *testing.T) { testStruct := SyncShardStatusRequest{ Timestamp: &startedTimestamp, } res := testStruct.GetTimestamp() assert.Equal(t, startedTimestamp, res) var nilStruct *SyncShardStatusRequest res = nilStruct.GetTimestamp() assert.Equal(t, int64(0), res) } func TestHistoryTerminateWorkflowExecutionRequest_GetDomainUUID(t *testing.T) { testStruct := HistoryTerminateWorkflowExecutionRequest{ DomainUUID: domainUUID, } res := testStruct.GetDomainUUID() assert.Equal(t, domainUUID, res) var nilStruct *HistoryTerminateWorkflowExecutionRequest res = nilStruct.GetDomainUUID() assert.Equal(t, "", res) } func TestHistoryTerminateWorkflowExecutionRequest_GetTerminateRequest(t *testing.T) { testStruct := HistoryTerminateWorkflowExecutionRequest{ TerminateRequest: terminateRequest, } res := testStruct.GetTerminateRequest() assert.Equal(t, terminateRequest, res) var nilStruct *HistoryTerminateWorkflowExecutionRequest res = nilStruct.GetTerminateRequest() assert.Nil(t, res) } func TestHistoryTerminateWorkflowExecutionRequest_GetExternalWorkflowExecution(t *testing.T) { testStruct := HistoryTerminateWorkflowExecutionRequest{ ExternalWorkflowExecution: externalWorkflowExecution, } res := testStruct.GetExternalWorkflowExecution() assert.Equal(t, externalWorkflowExecution, res) var nilStruct *HistoryTerminateWorkflowExecutionRequest res = nilStruct.GetExternalWorkflowExecution() assert.Nil(t, res) } func TestHistoryTerminateWorkflowExecutionRequest_GetChildWorkflowOnly(t *testing.T) { testStruct := HistoryTerminateWorkflowExecutionRequest{ ChildWorkflowOnly: childWorkflowOnly, } res := testStruct.GetChildWorkflowOnly() assert.Equal(t, childWorkflowOnly, res) var nilStruct *HistoryTerminateWorkflowExecutionRequest res = nilStruct.GetChildWorkflowOnly() assert.False(t, res) } func TestGetFailoverInfoRequest_GetDomainID(t *testing.T) { testStruct := GetFailoverInfoRequest{ DomainID: domainUUID, } res := testStruct.GetDomainID() assert.Equal(t, domainUUID, res) var nilStruct *GetFailoverInfoRequest res = nilStruct.GetDomainID() assert.Equal(t, "", res) } func TestGetFailoverInfoResponse_GetCompletedShardCount(t *testing.T) { testStruct := GetFailoverInfoResponse{ CompletedShardCount: completedShardCount, } res := testStruct.GetCompletedShardCount() assert.Equal(t, completedShardCount, res) var nilStruct *GetFailoverInfoResponse res = nilStruct.GetCompletedShardCount() assert.Equal(t, int32(0), res) } func TestGetFailoverInfoResponse_GetPendingShards(t *testing.T) { testStruct := GetFailoverInfoResponse{ PendingShards: pendingShards, } res := testStruct.GetPendingShards() assert.Equal(t, pendingShards, res) var nilStruct *GetFailoverInfoResponse res = nilStruct.GetPendingShards() assert.Nil(t, res) } func TestQueueState_Copy(t *testing.T) { f := fuzz.New().NilChance(0.1) info := &QueueState{} for i := 0; i < 1000; i++ { f.Fuzz(info) infoCopy := info.Copy() assert.Equal(t, info, infoCopy) } } ================================================ FILE: common/types/mapper/errorutils/convert.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package errorutils import "errors" // ConvertError checks if an error is of type T and if so, converts it using f. func ConvertError[T, V error](err error, fn func(T) V) (bool, V) { var ( e T res V ) if !errors.As(err, &e) { return false, res } return true, fn(e) } ================================================ FILE: common/types/mapper/errorutils/convert_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package errorutils import ( "testing" "github.com/stretchr/testify/assert" ) func TestConvertError(t *testing.T) { t.Run("sample error", func(t *testing.T) { err := &sampleError{ message: "test", } isError, converted := ConvertError(err, sampleErrorConvertor) assert.True(t, isError, "is error") assert.Error(t, converted, "converted error") assert.Equal(t, err.message, converted.message) }) t.Run("nil error is propagated as nil", func(t *testing.T) { isError, converted := ConvertError(nil, sampleErrorConvertor) assert.False(t, isError, "is error") assert.Nil(t, converted, "converted error") }) } type sampleError struct { message string } func (s *sampleError) Error() string { return "sample error" } type convertedError struct { message string } func (c *convertedError) Error() string { return "converted error" } func sampleErrorConvertor(e *sampleError) *convertedError { return &convertedError{ message: e.message, } } ================================================ FILE: common/types/mapper/proto/admin.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "sort" adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "github.com/uber/cadence/common/types" ) func FromAdminAddSearchAttributeRequest(t *types.AddSearchAttributeRequest) *adminv1.AddSearchAttributeRequest { if t == nil { return nil } return &adminv1.AddSearchAttributeRequest{ SearchAttribute: FromIndexedValueTypeMap(t.SearchAttribute), SecurityToken: t.SecurityToken, } } func ToAdminAddSearchAttributeRequest(t *adminv1.AddSearchAttributeRequest) *types.AddSearchAttributeRequest { if t == nil { return nil } return &types.AddSearchAttributeRequest{ SearchAttribute: ToIndexedValueTypeMap(t.SearchAttribute), SecurityToken: t.SecurityToken, } } func FromAdminCloseShardRequest(t *types.CloseShardRequest) *adminv1.CloseShardRequest { if t == nil { return nil } return &adminv1.CloseShardRequest{ ShardId: t.ShardID, } } func ToAdminCloseShardRequest(t *adminv1.CloseShardRequest) *types.CloseShardRequest { if t == nil { return nil } return &types.CloseShardRequest{ ShardID: t.ShardId, } } func FromAdminDescribeClusterResponse(t *types.DescribeClusterResponse) *adminv1.DescribeClusterResponse { if t == nil { return nil } return &adminv1.DescribeClusterResponse{ SupportedClientVersions: FromSupportedClientVersions(t.SupportedClientVersions), MembershipInfo: FromMembershipInfo(t.MembershipInfo), PersistenceInfo: FromPersistenceInfoMap(t.PersistenceInfo), } } func ToAdminDescribeClusterResponse(t *adminv1.DescribeClusterResponse) *types.DescribeClusterResponse { if t == nil { return nil } return &types.DescribeClusterResponse{ SupportedClientVersions: ToSupportedClientVersions(t.SupportedClientVersions), MembershipInfo: ToMembershipInfo(t.MembershipInfo), PersistenceInfo: ToPersistenceInfoMap(t.PersistenceInfo), } } func FromAdminDescribeShardDistributionRequest(t *types.DescribeShardDistributionRequest) *adminv1.DescribeShardDistributionRequest { if t == nil { return nil } return &adminv1.DescribeShardDistributionRequest{ PageSize: t.PageSize, PageId: t.PageID, } } func FromAdminDescribeHistoryHostRequest(t *types.DescribeHistoryHostRequest) *adminv1.DescribeHistoryHostRequest { if t == nil { return nil } if t.HostAddress != nil { return &adminv1.DescribeHistoryHostRequest{ DescribeBy: &adminv1.DescribeHistoryHostRequest_HostAddress{HostAddress: *t.HostAddress}, } } if t.ShardIDForHost != nil { return &adminv1.DescribeHistoryHostRequest{ DescribeBy: &adminv1.DescribeHistoryHostRequest_ShardId{ShardId: *t.ShardIDForHost}, } } if t.ExecutionForHost != nil { return &adminv1.DescribeHistoryHostRequest{ DescribeBy: &adminv1.DescribeHistoryHostRequest_WorkflowExecution{WorkflowExecution: FromWorkflowExecution(t.ExecutionForHost)}, } } panic("neither oneof field is set for DescribeHistoryHostRequest") } func ToAdminDescribeShardDistributionRequest(t *adminv1.DescribeShardDistributionRequest) *types.DescribeShardDistributionRequest { if t == nil { return nil } return &types.DescribeShardDistributionRequest{ PageSize: t.PageSize, PageID: t.PageId, } } func ToAdminDescribeHistoryHostRequest(t *adminv1.DescribeHistoryHostRequest) *types.DescribeHistoryHostRequest { if t == nil { return nil } switch describeBy := t.DescribeBy.(type) { case *adminv1.DescribeHistoryHostRequest_HostAddress: return &types.DescribeHistoryHostRequest{HostAddress: &describeBy.HostAddress} case *adminv1.DescribeHistoryHostRequest_ShardId: return &types.DescribeHistoryHostRequest{ShardIDForHost: &describeBy.ShardId} case *adminv1.DescribeHistoryHostRequest_WorkflowExecution: return &types.DescribeHistoryHostRequest{ExecutionForHost: ToWorkflowExecution(describeBy.WorkflowExecution)} } panic("neither oneof field is set for DescribeHistoryHostRequest") } func FromAdminDescribeShardDistributionResponse(t *types.DescribeShardDistributionResponse) *adminv1.DescribeShardDistributionResponse { if t == nil { return nil } return &adminv1.DescribeShardDistributionResponse{ NumberOfShards: t.NumberOfShards, Shards: t.Shards, } } func FromAdminDescribeHistoryHostResponse(t *types.DescribeHistoryHostResponse) *adminv1.DescribeHistoryHostResponse { if t == nil { return nil } return &adminv1.DescribeHistoryHostResponse{ NumberOfShards: t.NumberOfShards, ShardIds: t.ShardIDs, DomainCache: FromDomainCacheInfo(t.DomainCache), ShardControllerStatus: t.ShardControllerStatus, Address: t.Address, } } func ToAdminDescribeShardDistributionResponse(t *adminv1.DescribeShardDistributionResponse) *types.DescribeShardDistributionResponse { if t == nil { return nil } return &types.DescribeShardDistributionResponse{ NumberOfShards: t.NumberOfShards, Shards: t.Shards, } } func ToAdminDescribeHistoryHostResponse(t *adminv1.DescribeHistoryHostResponse) *types.DescribeHistoryHostResponse { if t == nil { return nil } return &types.DescribeHistoryHostResponse{ NumberOfShards: t.NumberOfShards, ShardIDs: t.ShardIds, DomainCache: ToDomainCacheInfo(t.DomainCache), ShardControllerStatus: t.ShardControllerStatus, Address: t.Address, } } func FromAdminDescribeQueueRequest(t *types.DescribeQueueRequest) *adminv1.DescribeQueueRequest { if t == nil { return nil } return &adminv1.DescribeQueueRequest{ ShardId: t.ShardID, ClusterName: t.ClusterName, TaskType: FromTaskType(t.Type), } } func ToAdminDescribeQueueRequest(t *adminv1.DescribeQueueRequest) *types.DescribeQueueRequest { if t == nil { return nil } return &types.DescribeQueueRequest{ ShardID: t.ShardId, ClusterName: t.ClusterName, Type: ToTaskType(t.TaskType), } } func FromAdminDescribeQueueResponse(t *types.DescribeQueueResponse) *adminv1.DescribeQueueResponse { if t == nil { return nil } return &adminv1.DescribeQueueResponse{ ProcessingQueueStates: t.ProcessingQueueStates, } } func ToAdminDescribeQueueResponse(t *adminv1.DescribeQueueResponse) *types.DescribeQueueResponse { if t == nil { return nil } return &types.DescribeQueueResponse{ ProcessingQueueStates: t.ProcessingQueueStates, } } func FromAdminDescribeWorkflowExecutionRequest(t *types.AdminDescribeWorkflowExecutionRequest) *adminv1.DescribeWorkflowExecutionRequest { if t == nil { return nil } return &adminv1.DescribeWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), } } func ToAdminDescribeWorkflowExecutionRequest(t *adminv1.DescribeWorkflowExecutionRequest) *types.AdminDescribeWorkflowExecutionRequest { if t == nil { return nil } return &types.AdminDescribeWorkflowExecutionRequest{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), } } func FromAdminDescribeWorkflowExecutionResponse(t *types.AdminDescribeWorkflowExecutionResponse) *adminv1.DescribeWorkflowExecutionResponse { if t == nil { return nil } return &adminv1.DescribeWorkflowExecutionResponse{ ShardId: stringToInt32(t.ShardID), HistoryAddr: t.HistoryAddr, MutableStateInCache: t.MutableStateInCache, MutableStateInDatabase: t.MutableStateInDatabase, } } func ToAdminDescribeWorkflowExecutionResponse(t *adminv1.DescribeWorkflowExecutionResponse) *types.AdminDescribeWorkflowExecutionResponse { if t == nil { return nil } return &types.AdminDescribeWorkflowExecutionResponse{ ShardID: int32ToString(t.ShardId), HistoryAddr: t.HistoryAddr, MutableStateInCache: t.MutableStateInCache, MutableStateInDatabase: t.MutableStateInDatabase, } } func FromAdminGetDLQReplicationMessagesRequest(t *types.GetDLQReplicationMessagesRequest) *adminv1.GetDLQReplicationMessagesRequest { if t == nil { return nil } return &adminv1.GetDLQReplicationMessagesRequest{ TaskInfos: FromReplicationTaskInfoArray(t.TaskInfos), } } func ToAdminGetDLQReplicationMessagesRequest(t *adminv1.GetDLQReplicationMessagesRequest) *types.GetDLQReplicationMessagesRequest { if t == nil { return nil } return &types.GetDLQReplicationMessagesRequest{ TaskInfos: ToReplicationTaskInfoArray(t.TaskInfos), } } func FromAdminGetDLQReplicationMessagesResponse(t *types.GetDLQReplicationMessagesResponse) *adminv1.GetDLQReplicationMessagesResponse { if t == nil { return nil } return &adminv1.GetDLQReplicationMessagesResponse{ ReplicationTasks: FromReplicationTaskArray(t.ReplicationTasks), } } func ToAdminGetDLQReplicationMessagesResponse(t *adminv1.GetDLQReplicationMessagesResponse) *types.GetDLQReplicationMessagesResponse { if t == nil { return nil } return &types.GetDLQReplicationMessagesResponse{ ReplicationTasks: ToReplicationTaskArray(t.ReplicationTasks), } } func FromAdminGetDomainReplicationMessagesRequest(t *types.GetDomainReplicationMessagesRequest) *adminv1.GetDomainReplicationMessagesRequest { if t == nil { return nil } return &adminv1.GetDomainReplicationMessagesRequest{ LastRetrievedMessageId: fromInt64Value(t.LastRetrievedMessageID), LastProcessedMessageId: fromInt64Value(t.LastProcessedMessageID), ClusterName: t.ClusterName, } } func ToAdminGetDomainReplicationMessagesRequest(t *adminv1.GetDomainReplicationMessagesRequest) *types.GetDomainReplicationMessagesRequest { if t == nil { return nil } return &types.GetDomainReplicationMessagesRequest{ LastRetrievedMessageID: toInt64Value(t.LastRetrievedMessageId), LastProcessedMessageID: toInt64Value(t.LastProcessedMessageId), ClusterName: t.ClusterName, } } func FromAdminGetDomainReplicationMessagesResponse(t *types.GetDomainReplicationMessagesResponse) *adminv1.GetDomainReplicationMessagesResponse { if t == nil { return nil } return &adminv1.GetDomainReplicationMessagesResponse{ Messages: FromReplicationMessages(t.Messages), } } func ToAdminGetDomainReplicationMessagesResponse(t *adminv1.GetDomainReplicationMessagesResponse) *types.GetDomainReplicationMessagesResponse { if t == nil { return nil } return &types.GetDomainReplicationMessagesResponse{ Messages: ToReplicationMessages(t.Messages), } } func FromAdminGetReplicationMessagesRequest(t *types.GetReplicationMessagesRequest) *adminv1.GetReplicationMessagesRequest { if t == nil { return nil } return &adminv1.GetReplicationMessagesRequest{ Tokens: FromReplicationTokenArray(t.Tokens), ClusterName: t.ClusterName, } } func ToAdminGetReplicationMessagesRequest(t *adminv1.GetReplicationMessagesRequest) *types.GetReplicationMessagesRequest { if t == nil { return nil } return &types.GetReplicationMessagesRequest{ Tokens: ToReplicationTokenArray(t.Tokens), ClusterName: t.ClusterName, } } func FromAdminGetReplicationMessagesResponse(t *types.GetReplicationMessagesResponse) *adminv1.GetReplicationMessagesResponse { if t == nil { return nil } return &adminv1.GetReplicationMessagesResponse{ ShardMessages: FromReplicationMessagesMap(t.MessagesByShard), } } func ToAdminGetReplicationMessagesResponse(t *adminv1.GetReplicationMessagesResponse) *types.GetReplicationMessagesResponse { if t == nil { return nil } return &types.GetReplicationMessagesResponse{ MessagesByShard: ToReplicationMessagesMap(t.ShardMessages), } } func FromAdminGetWorkflowExecutionRawHistoryV2Request(t *types.GetWorkflowExecutionRawHistoryV2Request) *adminv1.GetWorkflowExecutionRawHistoryV2Request { if t == nil { return nil } return &adminv1.GetWorkflowExecutionRawHistoryV2Request{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), StartEvent: FromEventIDVersionPair(t.StartEventID, t.StartEventVersion), EndEvent: FromEventIDVersionPair(t.EndEventID, t.EndEventVersion), PageSize: t.MaximumPageSize, NextPageToken: t.NextPageToken, } } func ToAdminGetWorkflowExecutionRawHistoryV2Request(t *adminv1.GetWorkflowExecutionRawHistoryV2Request) *types.GetWorkflowExecutionRawHistoryV2Request { if t == nil { return nil } return &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), StartEventID: ToEventID(t.StartEvent), StartEventVersion: ToEventVersion(t.StartEvent), EndEventID: ToEventID(t.EndEvent), EndEventVersion: ToEventVersion(t.EndEvent), MaximumPageSize: t.PageSize, NextPageToken: t.NextPageToken, } } func FromAdminGetWorkflowExecutionRawHistoryV2Response(t *types.GetWorkflowExecutionRawHistoryV2Response) *adminv1.GetWorkflowExecutionRawHistoryV2Response { if t == nil { return nil } return &adminv1.GetWorkflowExecutionRawHistoryV2Response{ NextPageToken: t.NextPageToken, HistoryBatches: FromDataBlobArray(t.HistoryBatches), VersionHistory: FromVersionHistory(t.VersionHistory), } } func ToAdminGetWorkflowExecutionRawHistoryV2Response(t *adminv1.GetWorkflowExecutionRawHistoryV2Response) *types.GetWorkflowExecutionRawHistoryV2Response { if t == nil { return nil } return &types.GetWorkflowExecutionRawHistoryV2Response{ NextPageToken: t.NextPageToken, HistoryBatches: ToDataBlobArray(t.HistoryBatches), VersionHistory: ToVersionHistory(t.VersionHistory), } } func FromAdminCountDLQMessagesRequest(t *types.CountDLQMessagesRequest) *adminv1.CountDLQMessagesRequest { if t == nil { return nil } return &adminv1.CountDLQMessagesRequest{ ForceFetch: t.ForceFetch, } } func ToAdminCountDLQMessagesRequest(t *adminv1.CountDLQMessagesRequest) *types.CountDLQMessagesRequest { if t == nil { return nil } return &types.CountDLQMessagesRequest{ ForceFetch: t.ForceFetch, } } func FromAdminCountDLQMessagesResponse(t *types.CountDLQMessagesResponse) *adminv1.CountDLQMessagesResponse { if t == nil { return nil } return &adminv1.CountDLQMessagesResponse{ History: FromHistoryDLQCountEntryMap(t.History), Domain: t.Domain, } } func ToAdminCountDLQMessagesResponse(t *adminv1.CountDLQMessagesResponse) *types.CountDLQMessagesResponse { if t == nil { return nil } return &types.CountDLQMessagesResponse{ History: ToHistoryDLQCountEntryMap(t.History), Domain: t.Domain, } } func FromAdminMergeDLQMessagesRequest(t *types.MergeDLQMessagesRequest) *adminv1.MergeDLQMessagesRequest { if t == nil { return nil } return &adminv1.MergeDLQMessagesRequest{ Type: FromDLQType(t.Type), ShardId: t.ShardID, SourceCluster: t.SourceCluster, InclusiveEndMessageId: fromInt64Value(t.InclusiveEndMessageID), PageSize: t.MaximumPageSize, NextPageToken: t.NextPageToken, } } func ToAdminMergeDLQMessagesRequest(t *adminv1.MergeDLQMessagesRequest) *types.MergeDLQMessagesRequest { if t == nil { return nil } return &types.MergeDLQMessagesRequest{ Type: ToDLQType(t.Type), ShardID: t.ShardId, SourceCluster: t.SourceCluster, InclusiveEndMessageID: toInt64Value(t.InclusiveEndMessageId), MaximumPageSize: t.PageSize, NextPageToken: t.NextPageToken, } } func FromAdminMergeDLQMessagesResponse(t *types.MergeDLQMessagesResponse) *adminv1.MergeDLQMessagesResponse { if t == nil { return nil } return &adminv1.MergeDLQMessagesResponse{ NextPageToken: t.NextPageToken, } } func ToAdminMergeDLQMessagesResponse(t *adminv1.MergeDLQMessagesResponse) *types.MergeDLQMessagesResponse { if t == nil { return nil } return &types.MergeDLQMessagesResponse{ NextPageToken: t.NextPageToken, } } func FromAdminPurgeDLQMessagesRequest(t *types.PurgeDLQMessagesRequest) *adminv1.PurgeDLQMessagesRequest { if t == nil { return nil } return &adminv1.PurgeDLQMessagesRequest{ Type: FromDLQType(t.Type), ShardId: t.ShardID, SourceCluster: t.SourceCluster, InclusiveEndMessageId: fromInt64Value(t.InclusiveEndMessageID), } } func ToAdminPurgeDLQMessagesRequest(t *adminv1.PurgeDLQMessagesRequest) *types.PurgeDLQMessagesRequest { if t == nil { return nil } return &types.PurgeDLQMessagesRequest{ Type: ToDLQType(t.Type), ShardID: t.ShardId, SourceCluster: t.SourceCluster, InclusiveEndMessageID: toInt64Value(t.InclusiveEndMessageId), } } func FromAdminReadDLQMessagesRequest(t *types.ReadDLQMessagesRequest) *adminv1.ReadDLQMessagesRequest { if t == nil { return nil } return &adminv1.ReadDLQMessagesRequest{ Type: FromDLQType(t.Type), ShardId: t.ShardID, SourceCluster: t.SourceCluster, InclusiveEndMessageId: fromInt64Value(t.InclusiveEndMessageID), PageSize: t.MaximumPageSize, NextPageToken: t.NextPageToken, } } func ToAdminReadDLQMessagesRequest(t *adminv1.ReadDLQMessagesRequest) *types.ReadDLQMessagesRequest { if t == nil { return nil } return &types.ReadDLQMessagesRequest{ Type: ToDLQType(t.Type), ShardID: t.ShardId, SourceCluster: t.SourceCluster, InclusiveEndMessageID: toInt64Value(t.InclusiveEndMessageId), MaximumPageSize: t.PageSize, NextPageToken: t.NextPageToken, } } func FromAdminReadDLQMessagesResponse(t *types.ReadDLQMessagesResponse) *adminv1.ReadDLQMessagesResponse { if t == nil { return nil } return &adminv1.ReadDLQMessagesResponse{ Type: FromDLQType(t.Type), ReplicationTasks: FromReplicationTaskArray(t.ReplicationTasks), ReplicationTasksInfo: FromReplicationTaskInfoArray(t.ReplicationTasksInfo), NextPageToken: t.NextPageToken, } } func ToAdminReadDLQMessagesResponse(t *adminv1.ReadDLQMessagesResponse) *types.ReadDLQMessagesResponse { if t == nil { return nil } return &types.ReadDLQMessagesResponse{ Type: ToDLQType(t.Type), ReplicationTasks: ToReplicationTaskArray(t.ReplicationTasks), ReplicationTasksInfo: ToReplicationTaskInfoArray(t.ReplicationTasksInfo), NextPageToken: t.NextPageToken, } } func FromAdminReapplyEventsRequest(t *types.ReapplyEventsRequest) *adminv1.ReapplyEventsRequest { if t == nil { return nil } return &adminv1.ReapplyEventsRequest{ Domain: t.DomainName, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Events: FromDataBlob(t.Events), } } func ToAdminReapplyEventsRequest(t *adminv1.ReapplyEventsRequest) *types.ReapplyEventsRequest { if t == nil { return nil } return &types.ReapplyEventsRequest{ DomainName: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Events: ToDataBlob(t.Events), } } func FromAdminRefreshWorkflowTasksRequest(t *types.RefreshWorkflowTasksRequest) *adminv1.RefreshWorkflowTasksRequest { if t == nil { return nil } return &adminv1.RefreshWorkflowTasksRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), } } func ToAdminRefreshWorkflowTasksRequest(t *adminv1.RefreshWorkflowTasksRequest) *types.RefreshWorkflowTasksRequest { if t == nil { return nil } return &types.RefreshWorkflowTasksRequest{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), } } func FromAdminRemoveTaskRequest(t *types.RemoveTaskRequest) *adminv1.RemoveTaskRequest { if t == nil { return nil } return &adminv1.RemoveTaskRequest{ ShardId: t.ShardID, TaskType: FromTaskType(t.Type), TaskId: t.TaskID, VisibilityTime: unixNanoToTime(t.VisibilityTimestamp), ClusterName: t.ClusterName, } } func ToAdminRemoveTaskRequest(t *adminv1.RemoveTaskRequest) *types.RemoveTaskRequest { if t == nil { return nil } return &types.RemoveTaskRequest{ ShardID: t.ShardId, Type: ToTaskType(t.TaskType), TaskID: t.TaskId, VisibilityTimestamp: timeToUnixNano(t.VisibilityTime), ClusterName: t.ClusterName, } } func FromAdminResendReplicationTasksRequest(t *types.ResendReplicationTasksRequest) *adminv1.ResendReplicationTasksRequest { if t == nil { return nil } return &adminv1.ResendReplicationTasksRequest{ DomainId: t.DomainID, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), RemoteCluster: t.RemoteCluster, StartEvent: FromEventIDVersionPair(t.StartEventID, t.StartVersion), EndEvent: FromEventIDVersionPair(t.EndEventID, t.EndVersion), } } func ToAdminResendReplicationTasksRequest(t *adminv1.ResendReplicationTasksRequest) *types.ResendReplicationTasksRequest { if t == nil { return nil } return &types.ResendReplicationTasksRequest{ DomainID: t.DomainId, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), RemoteCluster: t.RemoteCluster, StartEventID: ToEventID(t.StartEvent), StartVersion: ToEventVersion(t.StartEvent), EndEventID: ToEventID(t.EndEvent), EndVersion: ToEventVersion(t.EndEvent), } } func FromAdminResetQueueRequest(t *types.ResetQueueRequest) *adminv1.ResetQueueRequest { if t == nil { return nil } return &adminv1.ResetQueueRequest{ ShardId: t.ShardID, ClusterName: t.ClusterName, TaskType: FromTaskType(t.Type), } } func ToAdminResetQueueRequest(t *adminv1.ResetQueueRequest) *types.ResetQueueRequest { if t == nil { return nil } return &types.ResetQueueRequest{ ShardID: t.ShardId, ClusterName: t.ClusterName, Type: ToTaskType(t.TaskType), } } // FromAdminGetCrossClusterTasksRequest converts internal GetCrossClusterTasksRequest type to proto func FromAdminGetCrossClusterTasksRequest(t *types.GetCrossClusterTasksRequest) *adminv1.GetCrossClusterTasksRequest { if t == nil { return nil } return &adminv1.GetCrossClusterTasksRequest{ ShardIds: t.ShardIDs, TargetCluster: t.TargetCluster, } } // ToAdminGetCrossClusterTasksRequest converts proto GetCrossClusterTasksRequest type to internal func ToAdminGetCrossClusterTasksRequest(t *adminv1.GetCrossClusterTasksRequest) *types.GetCrossClusterTasksRequest { if t == nil { return nil } return &types.GetCrossClusterTasksRequest{ ShardIDs: t.ShardIds, TargetCluster: t.TargetCluster, } } // FromAdminGetCrossClusterTasksResponse converts internal GetCrossClusterTasksResponse type to proto func FromAdminGetCrossClusterTasksResponse(t *types.GetCrossClusterTasksResponse) *adminv1.GetCrossClusterTasksResponse { if t == nil { return nil } return &adminv1.GetCrossClusterTasksResponse{ TasksByShard: FromCrossClusterTaskRequestMap(t.TasksByShard), FailedCauseByShard: FromGetTaskFailedCauseMap(t.FailedCauseByShard), } } // ToAdminGetCrossClusterTasksResponse converts proto GetCrossClusterTasksResponse type to internal func ToAdminGetCrossClusterTasksResponse(t *adminv1.GetCrossClusterTasksResponse) *types.GetCrossClusterTasksResponse { if t == nil { return nil } return &types.GetCrossClusterTasksResponse{ TasksByShard: ToCrossClusterTaskRequestMap(t.TasksByShard), FailedCauseByShard: ToGetTaskFailedCauseMap(t.FailedCauseByShard), } } // FromAdminRespondCrossClusterTasksCompletedRequest converts internal RespondCrossClusterTasksCompletedRequest type to thrift func FromAdminRespondCrossClusterTasksCompletedRequest(t *types.RespondCrossClusterTasksCompletedRequest) *adminv1.RespondCrossClusterTasksCompletedRequest { if t == nil { return nil } return &adminv1.RespondCrossClusterTasksCompletedRequest{ ShardId: t.ShardID, TargetCluster: t.TargetCluster, TaskResponses: FromCrossClusterTaskResponseArray(t.TaskResponses), FetchNewTasks: t.FetchNewTasks, } } // ToAdminRespondCrossClusterTasksCompletedRequest converts thrift RespondCrossClusterTasksCompletedRequest type to internal func ToAdminRespondCrossClusterTasksCompletedRequest(t *adminv1.RespondCrossClusterTasksCompletedRequest) *types.RespondCrossClusterTasksCompletedRequest { if t == nil { return nil } return &types.RespondCrossClusterTasksCompletedRequest{ ShardID: t.ShardId, TargetCluster: t.TargetCluster, TaskResponses: ToCrossClusterTaskResponseArray(t.TaskResponses), FetchNewTasks: t.FetchNewTasks, } } // FromAdminRespondCrossClusterTasksCompletedResponse converts internal RespondCrossClusterTasksCompletedResponse type to thrift func FromAdminRespondCrossClusterTasksCompletedResponse(t *types.RespondCrossClusterTasksCompletedResponse) *adminv1.RespondCrossClusterTasksCompletedResponse { if t == nil { return nil } return &adminv1.RespondCrossClusterTasksCompletedResponse{ Tasks: FromCrossClusterTaskRequestArray(t.Tasks), } } // ToAdminRespondCrossClusterTasksCompletedResponse converts thrift RespondCrossClusterTasksCompletedResponse type to internal func ToAdminRespondCrossClusterTasksCompletedResponse(t *adminv1.RespondCrossClusterTasksCompletedResponse) *types.RespondCrossClusterTasksCompletedResponse { if t == nil { return nil } return &types.RespondCrossClusterTasksCompletedResponse{ Tasks: ToCrossClusterTaskRequestArray(t.Tasks), } } // FromAdminGetDynamicConfigRequest converts internal GetDynamicConfigRequest type to proto func FromAdminGetDynamicConfigRequest(t *types.GetDynamicConfigRequest) *adminv1.GetDynamicConfigRequest { if t == nil { return nil } return &adminv1.GetDynamicConfigRequest{ ConfigName: t.ConfigName, Filters: FromDynamicConfigFilterArray(t.Filters), } } // ToAdminGetDynamicConfigRequest converts proto GetDynamicConfigRequest type to internal func ToAdminGetDynamicConfigRequest(t *adminv1.GetDynamicConfigRequest) *types.GetDynamicConfigRequest { if t == nil { return nil } return &types.GetDynamicConfigRequest{ ConfigName: t.ConfigName, Filters: ToDynamicConfigFilterArray(t.Filters), } } // FromAdminGetDynamicConfigResponse converts internal GetDynamicConfigResponse type to proto func FromAdminGetDynamicConfigResponse(t *types.GetDynamicConfigResponse) *adminv1.GetDynamicConfigResponse { if t == nil { return nil } return &adminv1.GetDynamicConfigResponse{ Value: FromDataBlob(t.Value), } } // ToAdminGetDynamicConfigResponse converts proto GetDynamicConfigResponse type to internal func ToAdminGetDynamicConfigResponse(t *adminv1.GetDynamicConfigResponse) *types.GetDynamicConfigResponse { if t == nil { return nil } return &types.GetDynamicConfigResponse{ Value: ToDataBlob(t.Value), } } // FromAdminUpdateDynamicConfigRequest converts internal UpdateDynamicConfigRequest type to proto func FromAdminUpdateDynamicConfigRequest(t *types.UpdateDynamicConfigRequest) *adminv1.UpdateDynamicConfigRequest { if t == nil { return nil } return &adminv1.UpdateDynamicConfigRequest{ ConfigName: t.ConfigName, ConfigValues: FromDynamicConfigValueArray(t.ConfigValues), } } // ToAdminUpdateDynamicConfigRequest converts proto UpdateDynamicConfigRequest type to internal func ToAdminUpdateDynamicConfigRequest(t *adminv1.UpdateDynamicConfigRequest) *types.UpdateDynamicConfigRequest { if t == nil { return nil } return &types.UpdateDynamicConfigRequest{ ConfigName: t.ConfigName, ConfigValues: ToDynamicConfigValueArray(t.ConfigValues), } } // FromAdminRestoreDynamicConfigRequest converts internal RestoreDynamicConfigRequest type to proto func FromAdminRestoreDynamicConfigRequest(t *types.RestoreDynamicConfigRequest) *adminv1.RestoreDynamicConfigRequest { if t == nil { return nil } return &adminv1.RestoreDynamicConfigRequest{ ConfigName: t.ConfigName, Filters: FromDynamicConfigFilterArray(t.Filters), } } // ToAdminRestoreDynamicConfigRequest converts proto RestoreDynamicConfigRequest type to internal func ToAdminRestoreDynamicConfigRequest(t *adminv1.RestoreDynamicConfigRequest) *types.RestoreDynamicConfigRequest { if t == nil { return nil } return &types.RestoreDynamicConfigRequest{ ConfigName: t.ConfigName, Filters: ToDynamicConfigFilterArray(t.Filters), } } // FromAdminDeleteWorkflowRequest converts internal AdminDeleteWorkflowRequest type to proto func FromAdminDeleteWorkflowRequest(t *types.AdminDeleteWorkflowRequest) *adminv1.DeleteWorkflowRequest { if t == nil { return nil } return &adminv1.DeleteWorkflowRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), } } // ToAdminDeleteWorkflowRequest converts proto AdminDeleteWorkflowRequest type to internal func ToAdminDeleteWorkflowRequest(t *adminv1.DeleteWorkflowRequest) *types.AdminDeleteWorkflowRequest { if t == nil { return nil } return &types.AdminDeleteWorkflowRequest{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), } } // FromAdminDeleteWorkflowResponse converts internal AdminDeleteWorkflowRequest type to proto func FromAdminDeleteWorkflowResponse(t *types.AdminDeleteWorkflowResponse) *adminv1.DeleteWorkflowResponse { if t == nil { return nil } return &adminv1.DeleteWorkflowResponse{ HistoryDeleted: t.HistoryDeleted, ExecutionsDeleted: t.ExecutionsDeleted, VisibilityDeleted: t.VisibilityDeleted, } } // ToAdminDeleteWorkflowResponse converts proto AdminDeleteWorkflowResponse type to internal func ToAdminDeleteWorkflowResponse(t *adminv1.DeleteWorkflowResponse) *types.AdminDeleteWorkflowResponse { if t == nil { return nil } return &types.AdminDeleteWorkflowResponse{ HistoryDeleted: t.HistoryDeleted, ExecutionsDeleted: t.ExecutionsDeleted, VisibilityDeleted: t.VisibilityDeleted, } } // FromAdminMaintainCorruptWorkflowRequest converts internal AdminMaintainWorkflowRequest type to proto func FromAdminMaintainCorruptWorkflowRequest(t *types.AdminMaintainWorkflowRequest) *adminv1.MaintainCorruptWorkflowRequest { if t == nil { return nil } return &adminv1.MaintainCorruptWorkflowRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), } } // ToAdminMaintainCorruptWorkflowRequest converts proto AdminMaintainWorkflowRequest type to internal func ToAdminMaintainCorruptWorkflowRequest(t *adminv1.MaintainCorruptWorkflowRequest) *types.AdminMaintainWorkflowRequest { if t == nil { return nil } return &types.AdminMaintainWorkflowRequest{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), } } // FromAdminMaintainCorruptWorkflowResponse converts internal AdminMaintainWorkflowResponse type to proto func FromAdminMaintainCorruptWorkflowResponse(t *types.AdminMaintainWorkflowResponse) *adminv1.MaintainCorruptWorkflowResponse { if t == nil { return nil } return &adminv1.MaintainCorruptWorkflowResponse{ HistoryDeleted: t.HistoryDeleted, ExecutionsDeleted: t.ExecutionsDeleted, VisibilityDeleted: t.VisibilityDeleted, } } // ToAdminMaintainCorruptWorkflowResponse converts proto AdminMaintainWorkflowResponse type to internal func ToAdminMaintainCorruptWorkflowResponse(t *adminv1.MaintainCorruptWorkflowResponse) *types.AdminMaintainWorkflowResponse { if t == nil { return nil } return &types.AdminMaintainWorkflowResponse{ HistoryDeleted: t.HistoryDeleted, ExecutionsDeleted: t.ExecutionsDeleted, VisibilityDeleted: t.VisibilityDeleted, } } // FromAdminListDynamicConfigRequest converts internal ListDynamicConfigRequest type to proto func FromAdminListDynamicConfigRequest(t *types.ListDynamicConfigRequest) *adminv1.ListDynamicConfigRequest { if t == nil { return nil } return &adminv1.ListDynamicConfigRequest{ ConfigName: t.ConfigName, } } // ToAdminListDynamicConfigRequest converts proto ListDynamicConfigRequest type to internal func ToAdminListDynamicConfigRequest(t *adminv1.ListDynamicConfigRequest) *types.ListDynamicConfigRequest { if t == nil { return nil } return &types.ListDynamicConfigRequest{ ConfigName: t.ConfigName, } } // FromAdminListDynamicConfigResponse converts internal ListDynamicConfigResponse type to proto func FromAdminListDynamicConfigResponse(t *types.ListDynamicConfigResponse) *adminv1.ListDynamicConfigResponse { if t == nil { return nil } return &adminv1.ListDynamicConfigResponse{ Entries: FromDynamicConfigEntryArray(t.Entries), } } // ToAdminListDynamicConfigResponse converts proto ListDynamicConfigResponse type to internal func ToAdminListDynamicConfigResponse(t *adminv1.ListDynamicConfigResponse) *types.ListDynamicConfigResponse { if t == nil { return nil } return &types.ListDynamicConfigResponse{ Entries: ToDynamicConfigEntryArray(t.Entries), } } // FromDynamicConfigEntryArray converts internal DynamicConfigEntry array type to proto func FromDynamicConfigEntryArray(t []*types.DynamicConfigEntry) []*adminv1.DynamicConfigEntry { if t == nil { return nil } v := make([]*adminv1.DynamicConfigEntry, len(t)) for i := range t { v[i] = FromDynamicConfigEntry(t[i]) } return v } // ToDynamicConfigEntryArray converts proto DynamicConfigEntry array type to internal func ToDynamicConfigEntryArray(t []*adminv1.DynamicConfigEntry) []*types.DynamicConfigEntry { if t == nil { return nil } v := make([]*types.DynamicConfigEntry, len(t)) for i := range t { v[i] = ToDynamicConfigEntry(t[i]) } return v } // FromDynamicConfigEntry converts internal DynamicConfigEntry type to proto func FromDynamicConfigEntry(t *types.DynamicConfigEntry) *adminv1.DynamicConfigEntry { if t == nil { return nil } return &adminv1.DynamicConfigEntry{ Name: t.Name, Values: FromDynamicConfigValueArray(t.Values), } } // ToDynamicConfigEntry converts proto DynamicConfigEntry type to internal func ToDynamicConfigEntry(t *adminv1.DynamicConfigEntry) *types.DynamicConfigEntry { if t == nil { return nil } return &types.DynamicConfigEntry{ Name: t.Name, Values: ToDynamicConfigValueArray(t.Values), } } // FromDynamicConfigValueArray converts internal DynamicConfigValue array type to proto func FromDynamicConfigValueArray(t []*types.DynamicConfigValue) []*adminv1.DynamicConfigValue { if t == nil { return nil } v := make([]*adminv1.DynamicConfigValue, len(t)) for i := range t { v[i] = FromDynamicConfigValue(t[i]) } return v } // ToDynamicConfigValueArray converts proto DynamicConfigValue array type to internal func ToDynamicConfigValueArray(t []*adminv1.DynamicConfigValue) []*types.DynamicConfigValue { if t == nil { return nil } v := make([]*types.DynamicConfigValue, len(t)) for i := range t { v[i] = ToDynamicConfigValue(t[i]) } return v } // FromDynamicConfigValue converts internal DynamicConfigValue type to proto func FromDynamicConfigValue(t *types.DynamicConfigValue) *adminv1.DynamicConfigValue { if t == nil { return nil } return &adminv1.DynamicConfigValue{ Value: FromDataBlob(t.Value), Filters: FromDynamicConfigFilterArray(t.Filters), } } // ToDynamicConfigValue converts proto DynamicConfigValue type to internal func ToDynamicConfigValue(t *adminv1.DynamicConfigValue) *types.DynamicConfigValue { if t == nil { return nil } return &types.DynamicConfigValue{ Value: ToDataBlob(t.Value), Filters: ToDynamicConfigFilterArray(t.Filters), } } // FromDynamicConfigFilterArray converts internal DynamicConfigFilter array type to proto func FromDynamicConfigFilterArray(t []*types.DynamicConfigFilter) []*adminv1.DynamicConfigFilter { if t == nil { return nil } v := make([]*adminv1.DynamicConfigFilter, len(t)) for i := range t { v[i] = FromDynamicConfigFilter(t[i]) } return v } // ToDynamicConfigFilterArray converts proto DynamicConfigFilter array type to internal func ToDynamicConfigFilterArray(t []*adminv1.DynamicConfigFilter) []*types.DynamicConfigFilter { if t == nil { return nil } v := make([]*types.DynamicConfigFilter, len(t)) for i := range t { v[i] = ToDynamicConfigFilter(t[i]) } return v } // FromDynamicConfigFilter converts internal DynamicConfigFilter type to proto func FromDynamicConfigFilter(t *types.DynamicConfigFilter) *adminv1.DynamicConfigFilter { if t == nil { return nil } return &adminv1.DynamicConfigFilter{ Name: t.Name, Value: FromDataBlob(t.Value), } } // ToDynamicConfigFilter converts thrift DynamicConfigFilter type to internal func ToDynamicConfigFilter(t *adminv1.DynamicConfigFilter) *types.DynamicConfigFilter { if t == nil { return nil } return &types.DynamicConfigFilter{ Name: t.Name, Value: ToDataBlob(t.Value), } } func FromAdminGetGlobalIsolationGroupsResponse(t *types.GetGlobalIsolationGroupsResponse) *adminv1.GetGlobalIsolationGroupsResponse { if t == nil { return nil } return &adminv1.GetGlobalIsolationGroupsResponse{ IsolationGroups: FromIsolationGroupConfig(&t.IsolationGroups), } } func FromAdminUpdateGlobalIsolationGroupsRequest(t *types.UpdateGlobalIsolationGroupsRequest) *adminv1.UpdateGlobalIsolationGroupsRequest { if t == nil { return nil } return &adminv1.UpdateGlobalIsolationGroupsRequest{ IsolationGroups: FromIsolationGroupConfig(&t.IsolationGroups), } } func FromAdminUpdateDomainIsolationGroupsRequest(t *types.UpdateDomainIsolationGroupsRequest) *adminv1.UpdateDomainIsolationGroupsRequest { if t == nil { return nil } return &adminv1.UpdateDomainIsolationGroupsRequest{ Domain: t.Domain, IsolationGroups: FromIsolationGroupConfig(&t.IsolationGroups), } } func FromAdminGetGlobalIsolationGroupsRequest(t *types.GetGlobalIsolationGroupsRequest) *adminv1.GetGlobalIsolationGroupsRequest { if t == nil { return nil } return &adminv1.GetGlobalIsolationGroupsRequest{} } func FromAdminGetDomainIsolationGroupsRequest(t *types.GetDomainIsolationGroupsRequest) *adminv1.GetDomainIsolationGroupsRequest { if t == nil { return nil } return &adminv1.GetDomainIsolationGroupsRequest{ Domain: t.Domain, } } func ToAdminGetGlobalIsolationGroupsRequest(t *adminv1.GetGlobalIsolationGroupsRequest) *types.GetGlobalIsolationGroupsRequest { if t == nil { return nil } return &types.GetGlobalIsolationGroupsRequest{} } func ToAdminGetGlobalIsolationGroupsResponse(t *adminv1.GetGlobalIsolationGroupsResponse) *types.GetGlobalIsolationGroupsResponse { if t == nil { return nil } ig := ToIsolationGroupConfig(t.IsolationGroups) if ig == nil { return &types.GetGlobalIsolationGroupsResponse{} } return &types.GetGlobalIsolationGroupsResponse{ IsolationGroups: *ig, } } func ToAdminGetDomainIsolationGroupsResponse(t *adminv1.GetDomainIsolationGroupsResponse) *types.GetDomainIsolationGroupsResponse { if t == nil { return nil } ig := ToIsolationGroupConfig(t.IsolationGroups) if ig == nil { return &types.GetDomainIsolationGroupsResponse{} } return &types.GetDomainIsolationGroupsResponse{ IsolationGroups: *ig, } } func FromAdminGetDomainIsolationGroupsResponse(t *types.GetDomainIsolationGroupsResponse) *adminv1.GetDomainIsolationGroupsResponse { if t == nil { return nil } cfg := FromIsolationGroupConfig(&t.IsolationGroups) return &adminv1.GetDomainIsolationGroupsResponse{ IsolationGroups: cfg, } } func ToAdminGetDomainIsolationGroupsRequest(t *adminv1.GetDomainIsolationGroupsRequest) *types.GetDomainIsolationGroupsRequest { if t == nil { return nil } return &types.GetDomainIsolationGroupsRequest{Domain: t.Domain} } func FromAdminUpdateGlobalIsolationGroupsResponse(t *types.UpdateGlobalIsolationGroupsResponse) *adminv1.UpdateGlobalIsolationGroupsResponse { if t == nil { return nil } return &adminv1.UpdateGlobalIsolationGroupsResponse{} } func ToAdminUpdateGlobalIsolationGroupsRequest(t *adminv1.UpdateGlobalIsolationGroupsRequest) *types.UpdateGlobalIsolationGroupsRequest { if t == nil { return nil } cfg := ToIsolationGroupConfig(t.IsolationGroups) if cfg == nil { return &types.UpdateGlobalIsolationGroupsRequest{} } return &types.UpdateGlobalIsolationGroupsRequest{ IsolationGroups: *cfg, } } func ToAdminUpdateGlobalIsolationGroupsResponse(t *adminv1.UpdateGlobalIsolationGroupsResponse) *types.UpdateGlobalIsolationGroupsResponse { if t == nil { return nil } return &types.UpdateGlobalIsolationGroupsResponse{} } func ToAdminUpdateDomainIsolationGroupsResponse(t *adminv1.UpdateDomainIsolationGroupsResponse) *types.UpdateDomainIsolationGroupsResponse { if t == nil { return nil } return &types.UpdateDomainIsolationGroupsResponse{} } func FromAdminUpdateDomainIsolationGroupsResponse(t *types.UpdateDomainIsolationGroupsResponse) *adminv1.UpdateDomainIsolationGroupsResponse { if t == nil { return nil } return &adminv1.UpdateDomainIsolationGroupsResponse{} } func ToAdminUpdateDomainIsolationGroupsRequest(t *adminv1.UpdateDomainIsolationGroupsRequest) *types.UpdateDomainIsolationGroupsRequest { if t == nil { return nil } cfg := ToIsolationGroupConfig(t.IsolationGroups) if cfg == nil { return &types.UpdateDomainIsolationGroupsRequest{ Domain: t.Domain, } } return &types.UpdateDomainIsolationGroupsRequest{ Domain: t.Domain, IsolationGroups: *cfg, } } func FromIsolationGroupConfig(in *types.IsolationGroupConfiguration) *apiv1.IsolationGroupConfiguration { if in == nil || *in == nil { return nil } var out []*apiv1.IsolationGroupPartition for _, v := range *in { out = append(out, &apiv1.IsolationGroupPartition{ Name: v.Name, State: apiv1.IsolationGroupState(v.State), }) } sort.Slice(out, func(i, j int) bool { if out[i] == nil || out[j] == nil { return false } return out[i].Name < out[j].Name }) return &apiv1.IsolationGroupConfiguration{ IsolationGroups: out, } } func ToIsolationGroupConfig(in *apiv1.IsolationGroupConfiguration) *types.IsolationGroupConfiguration { if in == nil { return nil } out := make(types.IsolationGroupConfiguration) for v := range in.IsolationGroups { out[in.IsolationGroups[v].Name] = types.IsolationGroupPartition{ Name: in.IsolationGroups[v].Name, State: types.IsolationGroupState(in.IsolationGroups[v].State), } } return &out } func ToAdminGetDomainAsyncWorkflowConfiguratonRequest(in *adminv1.GetDomainAsyncWorkflowConfiguratonRequest) *types.GetDomainAsyncWorkflowConfiguratonRequest { if in == nil { return nil } return &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: in.Domain, } } func FromAdminGetDomainAsyncWorkflowConfiguratonResponse(in *types.GetDomainAsyncWorkflowConfiguratonResponse) *adminv1.GetDomainAsyncWorkflowConfiguratonResponse { if in == nil { return nil } return &adminv1.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: FromDomainAsyncWorkflowConfiguraton(in.Configuration), } } func FromDomainAsyncWorkflowConfiguraton(in *types.AsyncWorkflowConfiguration) *apiv1.AsyncWorkflowConfiguration { if in == nil { return nil } return &apiv1.AsyncWorkflowConfiguration{ Enabled: in.Enabled, PredefinedQueueName: in.PredefinedQueueName, QueueType: in.QueueType, QueueConfig: FromDataBlob(in.QueueConfig), } } func ToAdminUpdateDomainAsyncWorkflowConfiguratonRequest(in *adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest) *types.UpdateDomainAsyncWorkflowConfiguratonRequest { if in == nil { return nil } return &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: in.Domain, Configuration: ToDomainAsyncWorkflowConfiguraton(in.Configuration), } } func ToDomainAsyncWorkflowConfiguraton(in *apiv1.AsyncWorkflowConfiguration) *types.AsyncWorkflowConfiguration { if in == nil { return nil } return &types.AsyncWorkflowConfiguration{ Enabled: in.Enabled, PredefinedQueueName: in.PredefinedQueueName, QueueType: in.QueueType, QueueConfig: ToDataBlob(in.QueueConfig), } } func FromAdminUpdateDomainAsyncWorkflowConfiguratonResponse(in *types.UpdateDomainAsyncWorkflowConfiguratonResponse) *adminv1.UpdateDomainAsyncWorkflowConfiguratonResponse { if in == nil { return nil } return &adminv1.UpdateDomainAsyncWorkflowConfiguratonResponse{} } func FromAdminGetDomainAsyncWorkflowConfiguratonRequest(in *types.GetDomainAsyncWorkflowConfiguratonRequest) *adminv1.GetDomainAsyncWorkflowConfiguratonRequest { if in == nil { return nil } return &adminv1.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: in.Domain, } } func ToAdminGetDomainAsyncWorkflowConfiguratonResponse(in *adminv1.GetDomainAsyncWorkflowConfiguratonResponse) *types.GetDomainAsyncWorkflowConfiguratonResponse { if in == nil { return nil } return &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: ToDomainAsyncWorkflowConfiguraton(in.Configuration), } } func FromAdminUpdateDomainAsyncWorkflowConfiguratonRequest(in *types.UpdateDomainAsyncWorkflowConfiguratonRequest) *adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest { if in == nil { return nil } return &adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: in.Domain, Configuration: FromDomainAsyncWorkflowConfiguraton(in.Configuration), } } func ToAdminUpdateDomainAsyncWorkflowConfiguratonResponse(in *adminv1.UpdateDomainAsyncWorkflowConfiguratonResponse) *types.UpdateDomainAsyncWorkflowConfiguratonResponse { if in == nil { return nil } return &types.UpdateDomainAsyncWorkflowConfiguratonResponse{} } func FromAdminUpdateTaskListPartitionConfigRequest(in *types.UpdateTaskListPartitionConfigRequest) *adminv1.UpdateTaskListPartitionConfigRequest { if in == nil { return nil } return &adminv1.UpdateTaskListPartitionConfigRequest{ Domain: in.Domain, TaskList: FromTaskList(in.TaskList), TaskListType: FromTaskListType(in.TaskListType), PartitionConfig: FromAPITaskListPartitionConfig(in.PartitionConfig), } } func ToAdminUpdateTaskListPartitionConfigRequest(in *adminv1.UpdateTaskListPartitionConfigRequest) *types.UpdateTaskListPartitionConfigRequest { if in == nil { return nil } return &types.UpdateTaskListPartitionConfigRequest{ Domain: in.Domain, TaskList: ToTaskList(in.TaskList), TaskListType: ToTaskListType(in.TaskListType), PartitionConfig: ToAPITaskListPartitionConfig(in.PartitionConfig), } } func FromAdminUpdateTaskListPartitionConfigResponse(t *types.UpdateTaskListPartitionConfigResponse) *adminv1.UpdateTaskListPartitionConfigResponse { if t == nil { return nil } return &adminv1.UpdateTaskListPartitionConfigResponse{} } func ToAdminUpdateTaskListPartitionConfigResponse(t *adminv1.UpdateTaskListPartitionConfigResponse) *types.UpdateTaskListPartitionConfigResponse { if t == nil { return nil } return &types.UpdateTaskListPartitionConfigResponse{} } ================================================ FILE: common/types/mapper/proto/admin_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "fmt" "sort" "testing" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" v1 "github.com/uber/cadence-idl/go/proto/api/v1" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/testutils" "github.com/uber/cadence/common/types/testdata" ) func TestAdminAddSearchAttributeRequest(t *testing.T) { for _, item := range []*types.AddSearchAttributeRequest{nil, {}, &testdata.AdminAddSearchAttributeRequest} { assert.Equal(t, item, ToAdminAddSearchAttributeRequest(FromAdminAddSearchAttributeRequest(item))) } } func TestAdminCloseShardRequest(t *testing.T) { for _, item := range []*types.CloseShardRequest{nil, {}, &testdata.AdminCloseShardRequest} { assert.Equal(t, item, ToAdminCloseShardRequest(FromAdminCloseShardRequest(item))) } } func TestAdminDeleteWorkflowRequest(t *testing.T) { for _, item := range []*types.AdminDeleteWorkflowRequest{nil, {}, &testdata.AdminDeleteWorkflowRequest} { assert.Equal(t, item, ToAdminDeleteWorkflowRequest(FromAdminDeleteWorkflowRequest(item))) } } func TestAdminDeleteWorkflowResponse(t *testing.T) { for _, item := range []*types.AdminDeleteWorkflowResponse{nil, {}, &testdata.AdminDeleteWorkflowResponse} { assert.Equal(t, item, ToAdminDeleteWorkflowResponse(FromAdminDeleteWorkflowResponse(item))) } } func TestAdminDescribeClusterResponse(t *testing.T) { for _, item := range []*types.DescribeClusterResponse{nil, {}, &testdata.AdminDescribeClusterResponse} { assert.Equal(t, item, ToAdminDescribeClusterResponse(FromAdminDescribeClusterResponse(item))) } } func TestAdminDescribeHistoryHostRequest(t *testing.T) { for _, item := range []*types.DescribeHistoryHostRequest{ nil, &testdata.AdminDescribeHistoryHostRequest_ByHost, &testdata.AdminDescribeHistoryHostRequest_ByShard, &testdata.AdminDescribeHistoryHostRequest_ByExecution, } { assert.Equal(t, item, ToAdminDescribeHistoryHostRequest(FromAdminDescribeHistoryHostRequest(item))) } assert.Panics(t, func() { ToAdminDescribeHistoryHostRequest(&adminv1.DescribeHistoryHostRequest{}) }) assert.Panics(t, func() { FromAdminDescribeHistoryHostRequest(&types.DescribeHistoryHostRequest{}) }) } func TestAdminDescribeHistoryHostResponse(t *testing.T) { for _, item := range []*types.DescribeHistoryHostResponse{nil, {}, &testdata.AdminDescribeHistoryHostResponse} { assert.Equal(t, item, ToAdminDescribeHistoryHostResponse(FromAdminDescribeHistoryHostResponse(item))) } } func TestAdminDescribeQueueRequest(t *testing.T) { for _, item := range []*types.DescribeQueueRequest{nil, {}, &testdata.AdminDescribeQueueRequest} { assert.Equal(t, item, ToAdminDescribeQueueRequest(FromAdminDescribeQueueRequest(item))) } } func TestAdminDescribeQueueResponse(t *testing.T) { for _, item := range []*types.DescribeQueueResponse{nil, {}, &testdata.AdminDescribeQueueResponse} { assert.Equal(t, item, ToAdminDescribeQueueResponse(FromAdminDescribeQueueResponse(item))) } } func TestAdminDescribeShardDistributionRequest(t *testing.T) { for _, item := range []*types.DescribeShardDistributionRequest{nil, {}, &testdata.AdminDescribeShardDistributionRequest} { assert.Equal(t, item, ToAdminDescribeShardDistributionRequest(FromAdminDescribeShardDistributionRequest(item))) } } func TestAdminDescribeShardDistributionResponse(t *testing.T) { for _, item := range []*types.DescribeShardDistributionResponse{nil, {}, &testdata.AdminDescribeShardDistributionResponse} { assert.Equal(t, item, ToAdminDescribeShardDistributionResponse(FromAdminDescribeShardDistributionResponse(item))) } } func TestAdminDescribeWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.AdminDescribeWorkflowExecutionRequest{nil, {}, &testdata.AdminDescribeWorkflowExecutionRequest} { assert.Equal(t, item, ToAdminDescribeWorkflowExecutionRequest(FromAdminDescribeWorkflowExecutionRequest(item))) } } func TestAdminDescribeWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.AdminDescribeWorkflowExecutionResponse{nil, {ShardID: "0"}, &testdata.AdminDescribeWorkflowExecutionResponse} { assert.Equal(t, item, ToAdminDescribeWorkflowExecutionResponse(FromAdminDescribeWorkflowExecutionResponse(item))) } } func TestAdminGetDLQReplicationMessagesRequest(t *testing.T) { for _, item := range []*types.GetDLQReplicationMessagesRequest{nil, {}, &testdata.AdminGetDLQReplicationMessagesRequest} { assert.Equal(t, item, ToAdminGetDLQReplicationMessagesRequest(FromAdminGetDLQReplicationMessagesRequest(item))) } } func TestAdminGetDLQReplicationMessagesResponse(t *testing.T) { for _, item := range []*types.GetDLQReplicationMessagesResponse{nil, {}, &testdata.AdminGetDLQReplicationMessagesResponse} { assert.Equal(t, item, ToAdminGetDLQReplicationMessagesResponse(FromAdminGetDLQReplicationMessagesResponse(item))) } } func TestAdminGetDomainIsolationGroupsRequest(t *testing.T) { for _, item := range []*types.GetDomainIsolationGroupsRequest{nil, {}, &testdata.AdminGetDomainIsolationGroupsRequest} { assert.Equal(t, item, ToAdminGetDomainIsolationGroupsRequest(FromAdminGetDomainIsolationGroupsRequest(item))) } } func TestAdminGetDomainIsolationGroupsResponse(t *testing.T) { for _, item := range []*types.GetDomainIsolationGroupsResponse{nil, {}, &testdata.AdminGetDomainIsolationGroupsResponse} { assert.Equal(t, item, ToAdminGetDomainIsolationGroupsResponse(FromAdminGetDomainIsolationGroupsResponse(item))) } } func TestAdminGetDomainReplicationMessagesRequest(t *testing.T) { for _, item := range []*types.GetDomainReplicationMessagesRequest{nil, {}, &testdata.AdminGetDomainReplicationMessagesRequest} { assert.Equal(t, item, ToAdminGetDomainReplicationMessagesRequest(FromAdminGetDomainReplicationMessagesRequest(item))) } } func TestAdminGetDomainReplicationMessagesResponse(t *testing.T) { for _, item := range []*types.GetDomainReplicationMessagesResponse{nil, {}, &testdata.AdminGetDomainReplicationMessagesResponse} { assert.Equal(t, item, ToAdminGetDomainReplicationMessagesResponse(FromAdminGetDomainReplicationMessagesResponse(item))) } } func TestAdminGetDynamicConfigRequest(t *testing.T) { for _, item := range []*types.GetDynamicConfigRequest{nil, {}, &testdata.AdminGetDynamicConfigRequest} { assert.Equal(t, item, ToAdminGetDynamicConfigRequest(FromAdminGetDynamicConfigRequest(item))) } } func TestAdminGetDynamicConfigResponse(t *testing.T) { for _, item := range []*types.GetDynamicConfigResponse{nil, {}, &testdata.AdminGetDynamicConfigResponse} { assert.Equal(t, item, ToAdminGetDynamicConfigResponse(FromAdminGetDynamicConfigResponse(item))) } } func TestAdminGetGlobalIsolationGroupsRequest(t *testing.T) { for _, item := range []*types.GetGlobalIsolationGroupsRequest{nil, {}, &testdata.AdminGetGlobalIsolationGroupsRequest} { assert.Equal(t, item, ToAdminGetGlobalIsolationGroupsRequest(FromAdminGetGlobalIsolationGroupsRequest(item))) } } func TestAdminGetReplicationMessagesRequest(t *testing.T) { for _, item := range []*types.GetReplicationMessagesRequest{nil, {}, &testdata.AdminGetReplicationMessagesRequest} { assert.Equal(t, item, ToAdminGetReplicationMessagesRequest(FromAdminGetReplicationMessagesRequest(item))) } } func TestAdminGetReplicationMessagesResponse(t *testing.T) { for _, item := range []*types.GetReplicationMessagesResponse{nil, {}, &testdata.AdminGetReplicationMessagesResponse} { assert.Equal(t, item, ToAdminGetReplicationMessagesResponse(FromAdminGetReplicationMessagesResponse(item))) } } func TestAdminGetWorkflowExecutionRawHistoryV2Request(t *testing.T) { for _, item := range []*types.GetWorkflowExecutionRawHistoryV2Request{nil, {}, &testdata.AdminGetWorkflowExecutionRawHistoryV2Request} { assert.Equal(t, item, ToAdminGetWorkflowExecutionRawHistoryV2Request(FromAdminGetWorkflowExecutionRawHistoryV2Request(item))) } } func TestAdminGetWorkflowExecutionRawHistoryV2Response(t *testing.T) { for _, item := range []*types.GetWorkflowExecutionRawHistoryV2Response{nil, {}, &testdata.AdminGetWorkflowExecutionRawHistoryV2Response} { assert.Equal(t, item, ToAdminGetWorkflowExecutionRawHistoryV2Response(FromAdminGetWorkflowExecutionRawHistoryV2Response(item))) } } func TestAdminCountDLQMessagesRequest(t *testing.T) { for _, item := range []*types.CountDLQMessagesRequest{nil, {}, &testdata.AdminCountDLQMessagesRequest} { assert.Equal(t, item, ToAdminCountDLQMessagesRequest(FromAdminCountDLQMessagesRequest(item))) } } func TestAdminCountDLQMessagesResponse(t *testing.T) { for _, item := range []*types.CountDLQMessagesResponse{nil, {}, &testdata.AdminCountDLQMessagesResponse} { assert.Equal(t, item, ToAdminCountDLQMessagesResponse(FromAdminCountDLQMessagesResponse(item))) } } func TestAdminListDynamicConfigRequest(t *testing.T) { for _, item := range []*types.ListDynamicConfigRequest{nil, {}, &testdata.AdminListDynamicConfigRequest} { assert.Equal(t, item, ToAdminListDynamicConfigRequest(FromAdminListDynamicConfigRequest(item))) } } func TestAdminListDynamicConfigResponse(t *testing.T) { for _, item := range []*types.ListDynamicConfigResponse{nil, {}, &testdata.AdminListDynamicConfigResponse} { assert.Equal(t, item, ToAdminListDynamicConfigResponse(FromAdminListDynamicConfigResponse(item))) } } func TestAdminMaintainCorruptWorkflowRequest(t *testing.T) { for _, item := range []*types.AdminMaintainWorkflowRequest{nil, {}, &testdata.AdminMaintainCorruptWorkflowRequest} { assert.Equal(t, item, ToAdminMaintainCorruptWorkflowRequest(FromAdminMaintainCorruptWorkflowRequest(item))) } } func TestAdminMaintainCorruptWorkflowResponse(t *testing.T) { for _, item := range []*types.AdminMaintainWorkflowResponse{nil, {}, &testdata.AdminMaintainCorruptWorkflowResponse} { assert.Equal(t, item, ToAdminMaintainCorruptWorkflowResponse(FromAdminMaintainCorruptWorkflowResponse(item))) } } func TestAdminMergeDLQMessagesRequest(t *testing.T) { for _, item := range []*types.MergeDLQMessagesRequest{nil, {}, &testdata.AdminMergeDLQMessagesRequest} { assert.Equal(t, item, ToAdminMergeDLQMessagesRequest(FromAdminMergeDLQMessagesRequest(item))) } } func TestAdminMergeDLQMessagesResponse(t *testing.T) { for _, item := range []*types.MergeDLQMessagesResponse{nil, {}, &testdata.AdminMergeDLQMessagesResponse} { assert.Equal(t, item, ToAdminMergeDLQMessagesResponse(FromAdminMergeDLQMessagesResponse(item))) } } func TestAdminPurgeDLQMessagesRequest(t *testing.T) { for _, item := range []*types.PurgeDLQMessagesRequest{nil, {}, &testdata.AdminPurgeDLQMessagesRequest} { assert.Equal(t, item, ToAdminPurgeDLQMessagesRequest(FromAdminPurgeDLQMessagesRequest(item))) } } func TestAdminReadDLQMessagesRequest(t *testing.T) { for _, item := range []*types.ReadDLQMessagesRequest{nil, {}, &testdata.AdminReadDLQMessagesRequest} { assert.Equal(t, item, ToAdminReadDLQMessagesRequest(FromAdminReadDLQMessagesRequest(item))) } } func TestAdminReadDLQMessagesResponse(t *testing.T) { for _, item := range []*types.ReadDLQMessagesResponse{nil, {}, &testdata.AdminReadDLQMessagesResponse} { assert.Equal(t, item, ToAdminReadDLQMessagesResponse(FromAdminReadDLQMessagesResponse(item))) } } func TestAdminReapplyEventsRequest(t *testing.T) { for _, item := range []*types.ReapplyEventsRequest{nil, {}, &testdata.AdminReapplyEventsRequest} { assert.Equal(t, item, ToAdminReapplyEventsRequest(FromAdminReapplyEventsRequest(item))) } } func TestAdminRefreshWorkflowTasksRequest(t *testing.T) { for _, item := range []*types.RefreshWorkflowTasksRequest{nil, {}, &testdata.AdminRefreshWorkflowTasksRequest} { assert.Equal(t, item, ToAdminRefreshWorkflowTasksRequest(FromAdminRefreshWorkflowTasksRequest(item))) } } func TestAdminRemoveTaskRequest(t *testing.T) { for _, item := range []*types.RemoveTaskRequest{nil, {}, &testdata.AdminRemoveTaskRequest} { assert.Equal(t, item, ToAdminRemoveTaskRequest(FromAdminRemoveTaskRequest(item))) } } func TestAdminResendReplicationTasksRequest(t *testing.T) { for _, item := range []*types.ResendReplicationTasksRequest{nil, {}, &testdata.AdminResendReplicationTasksRequest} { assert.Equal(t, item, ToAdminResendReplicationTasksRequest(FromAdminResendReplicationTasksRequest(item))) } } func TestAdminResetQueueRequest(t *testing.T) { for _, item := range []*types.ResetQueueRequest{nil, {}, &testdata.AdminResetQueueRequest} { assert.Equal(t, item, ToAdminResetQueueRequest(FromAdminResetQueueRequest(item))) } } func TestAdminGetCrossClusterTasksRequest(t *testing.T) { for _, item := range []*types.GetCrossClusterTasksRequest{nil, {}, &testdata.AdminGetCrossClusterTasksRequest} { assert.Equal(t, item, ToAdminGetCrossClusterTasksRequest(FromAdminGetCrossClusterTasksRequest(item))) } } func TestAdminGetCrossClusterTasksResponse(t *testing.T) { for _, item := range []*types.GetCrossClusterTasksResponse{nil, {}, &testdata.AdminGetCrossClusterTasksResponse} { assert.Equal(t, item, ToAdminGetCrossClusterTasksResponse(FromAdminGetCrossClusterTasksResponse(item))) } } func TestAdminRespondCrossClusterTasksCompletedRequest(t *testing.T) { for _, item := range []*types.RespondCrossClusterTasksCompletedRequest{nil, {}, &testdata.AdminRespondCrossClusterTasksCompletedRequest} { assert.Equal(t, item, ToAdminRespondCrossClusterTasksCompletedRequest(FromAdminRespondCrossClusterTasksCompletedRequest(item))) } } func TestAdminRespondCrossClusterTasksCompletedResponse(t *testing.T) { for _, item := range []*types.RespondCrossClusterTasksCompletedResponse{nil, {}, &testdata.AdminRespondCrossClusterTasksCompletedResponse} { assert.Equal(t, item, ToAdminRespondCrossClusterTasksCompletedResponse(FromAdminRespondCrossClusterTasksCompletedResponse(item))) } } func TestAdminUpdateDomainIsolationGroupsRequest(t *testing.T) { for _, item := range []*types.UpdateDomainIsolationGroupsRequest{nil, {}, &testdata.AdminUpdateDomainIsolationGroupsRequest} { assert.Equal(t, item, ToAdminUpdateDomainIsolationGroupsRequest(FromAdminUpdateDomainIsolationGroupsRequest(item))) } } func TestAdminUpdateDomainIsolationGroupsResponse(t *testing.T) { for _, item := range []*types.UpdateDomainIsolationGroupsResponse{nil, {}, &testdata.AdminUpdateDomainIsolationGroupsResponse} { assert.Equal(t, item, ToAdminUpdateDomainIsolationGroupsResponse(FromAdminUpdateDomainIsolationGroupsResponse(item))) } } func TestAdminRestoreDynamicConfigRequest(t *testing.T) { for _, item := range []*types.RestoreDynamicConfigRequest{nil, {}, &testdata.AdminRestoreDynamicConfigRequest} { assert.Equal(t, item, ToAdminRestoreDynamicConfigRequest(FromAdminRestoreDynamicConfigRequest(item))) } } func TestAdminUpdateDynamicConfigRequest(t *testing.T) { for _, item := range []*types.UpdateDynamicConfigRequest{nil, {}, &testdata.AdminUpdateDynamicConfigRequest} { assert.Equal(t, item, ToAdminUpdateDynamicConfigRequest(FromAdminUpdateDynamicConfigRequest(item))) } } func TestAdminUpdateGlobalIsolationGroupsResponse(t *testing.T) { for _, item := range []*types.UpdateGlobalIsolationGroupsResponse{nil, {}, &testdata.AdminUpdateGlobalIsolationGroupsResponse} { assert.Equal(t, item, ToAdminUpdateGlobalIsolationGroupsResponse(FromAdminUpdateGlobalIsolationGroupsResponse(item))) } } func TestFromAdminGetGlobalIsolationGroupsResponse(t *testing.T) { tests := map[string]struct { in *types.GetGlobalIsolationGroupsResponse expected *adminv1.GetGlobalIsolationGroupsResponse }{ "Valid mapping": { in: &types.GetGlobalIsolationGroupsResponse{ IsolationGroups: types.IsolationGroupConfiguration{ "zone 0": { Name: "zone 0", State: types.IsolationGroupStateHealthy, }, "zone 1": { Name: "zone 1", State: types.IsolationGroupStateDrained, }, }, }, expected: &adminv1.GetGlobalIsolationGroupsResponse{ IsolationGroups: &v1.IsolationGroupConfiguration{ IsolationGroups: []*v1.IsolationGroupPartition{ { Name: "zone 0", State: v1.IsolationGroupState_ISOLATION_GROUP_STATE_HEALTHY, }, { Name: "zone 1", State: v1.IsolationGroupState_ISOLATION_GROUP_STATE_DRAINED, }, }, }, }, }, "nil - 1": { in: &types.GetGlobalIsolationGroupsResponse{ IsolationGroups: types.IsolationGroupConfiguration{}, }, expected: &adminv1.GetGlobalIsolationGroupsResponse{ IsolationGroups: &v1.IsolationGroupConfiguration{}, }, }, "nil - 2": { expected: nil, }, "nil - 3": { in: &types.GetGlobalIsolationGroupsResponse{ IsolationGroups: nil, }, expected: &adminv1.GetGlobalIsolationGroupsResponse{ IsolationGroups: nil, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { res := FromAdminGetGlobalIsolationGroupsResponse(td.in) assert.Equal(t, td.expected, res, "mapping") roundTrip := ToAdminGetGlobalIsolationGroupsResponse(res) if td.in != nil { assert.Equal(t, td.in, roundTrip, "roundtrip") } }) } } func TestToAdminGetGlobalIsolationGroupsRequest(t *testing.T) { tests := map[string]struct { in *adminv1.GetGlobalIsolationGroupsRequest expected *types.GetGlobalIsolationGroupsRequest }{ "Valid mapping": { in: &adminv1.GetGlobalIsolationGroupsRequest{}, expected: &types.GetGlobalIsolationGroupsRequest{}, }, "nil - 2": { in: &adminv1.GetGlobalIsolationGroupsRequest{}, expected: &types.GetGlobalIsolationGroupsRequest{}, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminGetGlobalIsolationGroupsRequest(td.in)) }) } } func TestFromAdminGetDomainIsolationGroupsResponse(t *testing.T) { tests := map[string]struct { in *types.GetDomainIsolationGroupsResponse expected *adminv1.GetDomainIsolationGroupsResponse }{ "Valid mapping": { in: &types.GetDomainIsolationGroupsResponse{ IsolationGroups: types.IsolationGroupConfiguration{ "zone 0": { Name: "zone 0", State: types.IsolationGroupStateHealthy, }, "zone 1": { Name: "zone 1", State: types.IsolationGroupStateDrained, }, }, }, expected: &adminv1.GetDomainIsolationGroupsResponse{ IsolationGroups: &v1.IsolationGroupConfiguration{ IsolationGroups: []*v1.IsolationGroupPartition{ { Name: "zone 0", State: v1.IsolationGroupState_ISOLATION_GROUP_STATE_HEALTHY, }, { Name: "zone 1", State: v1.IsolationGroupState_ISOLATION_GROUP_STATE_DRAINED, }, }, }, }, }, "empty": { in: &types.GetDomainIsolationGroupsResponse{ IsolationGroups: types.IsolationGroupConfiguration{}, }, expected: &adminv1.GetDomainIsolationGroupsResponse{ IsolationGroups: &v1.IsolationGroupConfiguration{ IsolationGroups: []*v1.IsolationGroupPartition{}, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { res := FromAdminGetDomainIsolationGroupsResponse(td.in) // map iteration is nondeterministic sort.Slice(res.IsolationGroups.IsolationGroups, func(i int, j int) bool { return res.IsolationGroups.IsolationGroups[i].Name > res.IsolationGroups.IsolationGroups[j].Name }) }) } } func TestToAdminGetDomainIsolationGroupsRequest(t *testing.T) { tests := map[string]struct { in *adminv1.GetDomainIsolationGroupsRequest expected *types.GetDomainIsolationGroupsRequest }{ "Valid mapping": { in: &adminv1.GetDomainIsolationGroupsRequest{ Domain: "domain123", }, expected: &types.GetDomainIsolationGroupsRequest{ Domain: "domain123", }, }, "empty": { in: &adminv1.GetDomainIsolationGroupsRequest{}, expected: &types.GetDomainIsolationGroupsRequest{}, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminGetDomainIsolationGroupsRequest(td.in)) }) } } func TestFromAdminUpdateGlobalIsolationGroupsResponse(t *testing.T) { tests := map[string]struct { in *types.UpdateGlobalIsolationGroupsResponse expected *adminv1.UpdateGlobalIsolationGroupsResponse }{ "Valid mapping": {}, "empty": { in: &types.UpdateGlobalIsolationGroupsResponse{}, expected: &adminv1.UpdateGlobalIsolationGroupsResponse{}, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, FromAdminUpdateGlobalIsolationGroupsResponse(td.in)) }) } } func TestToAdminUpdateGlobalIsolationGroupsRequest(t *testing.T) { tests := map[string]struct { in *adminv1.UpdateGlobalIsolationGroupsRequest expected *types.UpdateGlobalIsolationGroupsRequest }{ "Valid mapping": { in: &adminv1.UpdateGlobalIsolationGroupsRequest{ IsolationGroups: &v1.IsolationGroupConfiguration{ IsolationGroups: []*v1.IsolationGroupPartition{ { Name: "zone 1", State: v1.IsolationGroupState_ISOLATION_GROUP_STATE_HEALTHY, }, { Name: "zone 2", State: v1.IsolationGroupState_ISOLATION_GROUP_STATE_DRAINED, }, }, }, }, expected: &types.UpdateGlobalIsolationGroupsRequest{ IsolationGroups: types.IsolationGroupConfiguration{ "zone 1": types.IsolationGroupPartition{ Name: "zone 1", State: types.IsolationGroupStateHealthy, }, "zone 2": types.IsolationGroupPartition{ Name: "zone 2", State: types.IsolationGroupStateDrained, }, }, }, }, "empty": { in: &adminv1.UpdateGlobalIsolationGroupsRequest{}, expected: &types.UpdateGlobalIsolationGroupsRequest{}, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { res := ToAdminUpdateGlobalIsolationGroupsRequest(td.in) assert.Equal(t, td.expected, res, "conversion") roundTrip := FromAdminUpdateGlobalIsolationGroupsRequest(res) if td.in != nil { assert.Equal(t, td.in, roundTrip, "roundtrip") } }) } } func TestFromAdminUpdateDomainIsolationGroupsResponse(t *testing.T) { tests := map[string]struct { in *types.UpdateDomainIsolationGroupsResponse expected *adminv1.UpdateDomainIsolationGroupsResponse }{ "empty": { in: &types.UpdateDomainIsolationGroupsResponse{}, expected: &adminv1.UpdateDomainIsolationGroupsResponse{}, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, FromAdminUpdateDomainIsolationGroupsResponse(td.in)) }) } } func TestToUpdateDomainIsolationGroupsRequest(t *testing.T) { tests := map[string]struct { in *adminv1.UpdateDomainIsolationGroupsRequest expected *types.UpdateDomainIsolationGroupsRequest }{ "valid": { in: &adminv1.UpdateDomainIsolationGroupsRequest{ Domain: "test-domain", IsolationGroups: &v1.IsolationGroupConfiguration{ IsolationGroups: []*v1.IsolationGroupPartition{ { Name: "zone-1", State: v1.IsolationGroupState_ISOLATION_GROUP_STATE_HEALTHY, }, { Name: "zone-2", State: v1.IsolationGroupState_ISOLATION_GROUP_STATE_DRAINED, }, }, }, }, expected: &types.UpdateDomainIsolationGroupsRequest{ Domain: "test-domain", IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": types.IsolationGroupPartition{ Name: "zone-1", State: types.IsolationGroupStateHealthy, }, "zone-2": types.IsolationGroupPartition{ Name: "zone-2", State: types.IsolationGroupStateDrained, }, }, }, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminUpdateDomainIsolationGroupsRequest(td.in)) }) } } func TestToAdminGetDomainAsyncWorkflowConfiguratonRequest(t *testing.T) { tests := map[string]struct { in *adminv1.GetDomainAsyncWorkflowConfiguratonRequest expected *types.GetDomainAsyncWorkflowConfiguratonRequest }{ "nil": { in: nil, expected: nil, }, "empty": { in: &adminv1.GetDomainAsyncWorkflowConfiguratonRequest{}, expected: &types.GetDomainAsyncWorkflowConfiguratonRequest{}, }, "valid": { in: &adminv1.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, expected: &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminGetDomainAsyncWorkflowConfiguratonRequest(td.in)) }) } } func TestFromAdminGetDomainAsyncWorkflowConfiguratonResponse(t *testing.T) { tests := map[string]struct { in *types.GetDomainAsyncWorkflowConfiguratonResponse expected *adminv1.GetDomainAsyncWorkflowConfiguratonResponse }{ "nil": { in: nil, expected: nil, }, "empty": { in: &types.GetDomainAsyncWorkflowConfiguratonResponse{}, expected: &adminv1.GetDomainAsyncWorkflowConfiguratonResponse{}, }, "predefined queue": { in: &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-queue", }, }, expected: &adminv1.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &v1.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-queue", }, }, }, "inline queue": { in: &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"topic":"test-topic","dlq_topic":"test-dlq-topic","consumer_group":"test-consumer-group","brokers":["test-broker-1","test-broker-2"],"properties":{"test-key-1":"test-value-1"}}`), }, }, }, expected: &adminv1.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &v1.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &v1.DataBlob{ EncodingType: v1.EncodingType_ENCODING_TYPE_JSON, Data: []byte(`{"topic":"test-topic","dlq_topic":"test-dlq-topic","consumer_group":"test-consumer-group","brokers":["test-broker-1","test-broker-2"],"properties":{"test-key-1":"test-value-1"}}`), }, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, FromAdminGetDomainAsyncWorkflowConfiguratonResponse(td.in)) }) } } func TestToAdminUpdateDomainAsyncWorkflowConfiguratonRequest(t *testing.T) { tests := map[string]struct { in *adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest expected *types.UpdateDomainAsyncWorkflowConfiguratonRequest }{ "nil": { in: nil, expected: nil, }, "empty": { in: &adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest{}, expected: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{}, }, "predefined queue": { in: &adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &v1.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-queue", }, }, expected: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-queue", }, }, }, "inline queue": { in: &adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &v1.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &v1.DataBlob{ EncodingType: v1.EncodingType_ENCODING_TYPE_JSON, Data: []byte(`{"topic":"test-topic","dlq_topic":"test-dlq-topic","consumer_group":"test-consumer-group","brokers":["test-broker-1","test-broker-2"],"properties":{"test-key-1":"test-value-1"}}`), }, }, }, expected: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"topic":"test-topic","dlq_topic":"test-dlq-topic","consumer_group":"test-consumer-group","brokers":["test-broker-1","test-broker-2"],"properties":{"test-key-1":"test-value-1"}}`), }, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminUpdateDomainAsyncWorkflowConfiguratonRequest(td.in)) }) } } func TestFromAdminUpdateDomainAsyncWorkflowConfiguratonResponse(t *testing.T) { tests := map[string]struct { in *types.UpdateDomainAsyncWorkflowConfiguratonResponse expected *adminv1.UpdateDomainAsyncWorkflowConfiguratonResponse }{ "nil": { in: nil, expected: nil, }, "empty": { in: &types.UpdateDomainAsyncWorkflowConfiguratonResponse{}, expected: &adminv1.UpdateDomainAsyncWorkflowConfiguratonResponse{}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, FromAdminUpdateDomainAsyncWorkflowConfiguratonResponse(td.in)) }) } } func TestFromAdminGetDomainAsyncWorkflowConfiguratonRequest(t *testing.T) { tests := map[string]struct { in *types.GetDomainAsyncWorkflowConfiguratonRequest expected *adminv1.GetDomainAsyncWorkflowConfiguratonRequest }{ "nil": { in: nil, expected: nil, }, "empty": { in: &types.GetDomainAsyncWorkflowConfiguratonRequest{}, expected: &adminv1.GetDomainAsyncWorkflowConfiguratonRequest{}, }, "valid": { in: &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, expected: &adminv1.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, FromAdminGetDomainAsyncWorkflowConfiguratonRequest(td.in)) }) } } func TestToAdminGetDomainAsyncWorkflowConfiguratonResponse(t *testing.T) { tests := map[string]struct { in *adminv1.GetDomainAsyncWorkflowConfiguratonResponse expected *types.GetDomainAsyncWorkflowConfiguratonResponse }{ "nil": { in: nil, expected: nil, }, "empty": { in: &adminv1.GetDomainAsyncWorkflowConfiguratonResponse{}, expected: &types.GetDomainAsyncWorkflowConfiguratonResponse{}, }, "predefined queue": { in: &adminv1.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &v1.AsyncWorkflowConfiguration{ PredefinedQueueName: "test-queue", }, }, expected: &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &types.AsyncWorkflowConfiguration{ PredefinedQueueName: "test-queue", }, }, }, "inline queue": { in: &adminv1.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &v1.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &v1.DataBlob{ EncodingType: v1.EncodingType_ENCODING_TYPE_JSON, Data: []byte(`{"topic":"test-topic","dlq_topic":"test-dlq-topic","consumer_group":"test-consumer-group","brokers":["test-broker-1","test-broker-2"],"properties":{"test-key-1":"test-value-1"}}`), }, }, }, expected: &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"topic":"test-topic","dlq_topic":"test-dlq-topic","consumer_group":"test-consumer-group","brokers":["test-broker-1","test-broker-2"],"properties":{"test-key-1":"test-value-1"}}`), }, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminGetDomainAsyncWorkflowConfiguratonResponse(td.in)) }) } } func TestFromAdminUpdateDomainAsyncWorkflowConfiguratonRequest(t *testing.T) { tests := map[string]struct { in *types.UpdateDomainAsyncWorkflowConfiguratonRequest expected *adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest }{ "nil": { in: nil, expected: nil, }, "empty": { in: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{}, expected: &adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest{}, }, "predefined queue": { in: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &types.AsyncWorkflowConfiguration{ PredefinedQueueName: "test-queue", }, }, expected: &adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &v1.AsyncWorkflowConfiguration{ PredefinedQueueName: "test-queue", }, }, }, "kafka inline queue": { in: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"topic":"test-topic","dlq_topic":"test-dlq-topic","consumer_group":"test-consumer-group","brokers":["test-broker-1","test-broker-2"],"properties":{"test-key-1":"test-value-1"}}`), }, }, }, expected: &adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &v1.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &v1.DataBlob{ EncodingType: v1.EncodingType_ENCODING_TYPE_JSON, Data: []byte(`{"topic":"test-topic","dlq_topic":"test-dlq-topic","consumer_group":"test-consumer-group","brokers":["test-broker-1","test-broker-2"],"properties":{"test-key-1":"test-value-1"}}`), }, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, FromAdminUpdateDomainAsyncWorkflowConfiguratonRequest(td.in)) }) } } func TestToAdminUpdateDomainAsyncWorkflowConfiguratonResponse(t *testing.T) { tests := map[string]struct { in *adminv1.UpdateDomainAsyncWorkflowConfiguratonResponse expected *types.UpdateDomainAsyncWorkflowConfiguratonResponse }{ "nil": { in: nil, expected: nil, }, "empty": { in: &adminv1.UpdateDomainAsyncWorkflowConfiguratonResponse{}, expected: &types.UpdateDomainAsyncWorkflowConfiguratonResponse{}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminUpdateDomainAsyncWorkflowConfiguratonResponse(td.in)) }) } } func TestAdminUpdateTaskListPartitionConfigRequest(t *testing.T) { for _, item := range []*types.UpdateTaskListPartitionConfigRequest{nil, {}, &testdata.AdminUpdateTaskListPartitionConfigRequest} { assert.Equal(t, item, ToAdminUpdateTaskListPartitionConfigRequest(FromAdminUpdateTaskListPartitionConfigRequest(item))) } } func TestAdminUpdateTaskListPartitionConfigResponse(t *testing.T) { for _, item := range []*types.UpdateTaskListPartitionConfigResponse{nil, {}} { assert.Equal(t, item, ToAdminUpdateTaskListPartitionConfigResponse(FromAdminUpdateTaskListPartitionConfigResponse(item))) } } func TestAdminCloseShardRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminCloseShardRequest, ToAdminCloseShardRequest) } func TestAdminResetQueueRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminResetQueueRequest, ToAdminResetQueueRequest, testutils.WithCustomFuncs(func(e *types.ResetQueueRequest, c fuzz.Continue) { // This function needs to exist because we can't independently fuzz the type field // as both shardID and type have the same type signature. c.Fuzz(e) testutils.TaskTypeFuzzer(e.Type, c) }), ) } func TestAdminUpdateDynamicConfigRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminUpdateDynamicConfigRequest, ToAdminUpdateDynamicConfigRequest, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestAdminUpdateTaskListPartitionConfigRequestFuzz(t *testing.T) { // ReadPartitions and WritePartitions are tested in api_test.go testutils.RunMapperFuzzTest(t, FromAdminUpdateTaskListPartitionConfigRequest, ToAdminUpdateTaskListPartitionConfigRequest, testutils.WithExcludedFields("ReadPartitions", "WritePartitions"), ) } func TestAdminGetDLQReplicationMessagesRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetDLQReplicationMessagesRequest, ToAdminGetDLQReplicationMessagesRequest) } func TestAdminMergeDLQMessagesResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminMergeDLQMessagesResponse, ToAdminMergeDLQMessagesResponse) } func TestAdminDescribeWorkflowExecutionRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminDescribeWorkflowExecutionRequest, ToAdminDescribeWorkflowExecutionRequest) } func TestAdminCountDLQMessagesResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminCountDLQMessagesResponse, ToAdminCountDLQMessagesResponse) } func TestAdminReapplyEventsRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminReapplyEventsRequest, ToAdminReapplyEventsRequest, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestAdminRespondCrossClusterTasksCompletedRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminRespondCrossClusterTasksCompletedRequest, ToAdminRespondCrossClusterTasksCompletedRequest, testutils.WithExcludedFields( "TaskResponses", // Excluded and tested in TestFromCrossClusterTaskResponseFuzz in shared ), ) } func TestAdminGetDomainReplicationMessagesRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetDomainReplicationMessagesRequest, ToAdminGetDomainReplicationMessagesRequest) } func TestAdminUpdateTaskListPartitionConfigResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminUpdateTaskListPartitionConfigResponse, ToAdminUpdateTaskListPartitionConfigResponse) } func TestAdminRefreshWorkflowTasksRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminRefreshWorkflowTasksRequest, ToAdminRefreshWorkflowTasksRequest) } func TestAdminDescribeHistoryHostRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminDescribeHistoryHostRequest, ToAdminDescribeHistoryHostRequest, testutils.WithCustomFuncs(DescribeHistoryHostRequestFuzzer), ) } func TestAdminGetWorkflowExecutionRawHistoryV2ResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetWorkflowExecutionRawHistoryV2Response, ToAdminGetWorkflowExecutionRawHistoryV2Response, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestAdminRemoveTaskRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminRemoveTaskRequest, ToAdminRemoveTaskRequest, testutils.WithCustomFuncs(func(e *types.RemoveTaskRequest, c fuzz.Continue) { c.Fuzz(e) testutils.TaskTypeFuzzer(e.Type, c) }), ) } func TestAdminResendReplicationTasksRequestFuzz(t *testing.T) { // [BUG] When ID or Version is nil for a given pair round-trip serialization fails. This is a mapper bug - should handle errors gracefully. testutils.RunMapperFuzzTest(t, FromAdminResendReplicationTasksRequest, ToAdminResendReplicationTasksRequest, testutils.WithCustomFuncs(func(r *types.ResendReplicationTasksRequest, c fuzz.Continue) { c.FuzzNoCustom(r) r.StartEventID, r.StartVersion = EventIDVersionPairFuzzer(c) r.EndEventID, r.EndVersion = EventIDVersionPairFuzzer(c) }), ) } func TestAdminAddSearchAttributeRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminAddSearchAttributeRequest, ToAdminAddSearchAttributeRequest) } func TestAdminDescribeClusterResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminDescribeClusterResponse, ToAdminDescribeClusterResponse) } func TestAdminGetDynamicConfigResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetDynamicConfigResponse, ToAdminGetDynamicConfigResponse, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestAdminMaintainCorruptWorkflowRequestFuzz(t *testing.T) { // [BUG] SkipErrors field is missing from the mapper - not included in proto conversion testutils.RunMapperFuzzTest(t, FromAdminMaintainCorruptWorkflowRequest, ToAdminMaintainCorruptWorkflowRequest, testutils.WithExcludedFields("SkipErrors"), ) } func TestDynamicConfigFilterArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDynamicConfigFilterArray, ToDynamicConfigFilterArray, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer)) } func TestAdminGetDomainAsyncWorkflowConfiguratonResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetDomainAsyncWorkflowConfiguratonResponse, ToAdminGetDomainAsyncWorkflowConfiguratonResponse, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestAdminDescribeWorkflowExecutionResponseFuzz(t *testing.T) { // [BUG] ShardID is a string that gets converted to int32 using stringToInt32() // which panics on invalid strings. This is a mapper bug - should handle errors gracefully. // Using custom fuzzer to ensure ShardID is a valid int32 string or empty. testutils.RunMapperFuzzTest(t, FromAdminDescribeWorkflowExecutionResponse, ToAdminDescribeWorkflowExecutionResponse, testutils.WithCustomFuncs( func(r *types.AdminDescribeWorkflowExecutionResponse, c fuzz.Continue) { c.FuzzNoCustom(r) // Always generate a valid ShardID (int32 as string) to avoid stringToInt32 panics shardID := c.Int31() r.ShardID = fmt.Sprintf("%d", shardID) }, testutils.EncodingTypeFuzzer), ) } func TestAdminDescribeShardDistributionResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminDescribeShardDistributionResponse, ToAdminDescribeShardDistributionResponse) } func TestAdminMergeDLQMessagesRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminMergeDLQMessagesRequest, ToAdminMergeDLQMessagesRequest, testutils.WithCustomFuncs(testutils.DLQTypeFuzzer), ) } func TestAdminGetReplicationMessagesRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetReplicationMessagesRequest, ToAdminGetReplicationMessagesRequest) } func TestAdminListDynamicConfigResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminListDynamicConfigResponse, ToAdminListDynamicConfigResponse, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestAdminGetGlobalIsolationGroupsRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetGlobalIsolationGroupsRequest, ToAdminGetGlobalIsolationGroupsRequest) } func TestAdminUpdateDomainAsyncWorkflowConfiguratonResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminUpdateDomainAsyncWorkflowConfiguratonResponse, ToAdminUpdateDomainAsyncWorkflowConfiguratonResponse) } func TestDynamicConfigFilterFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDynamicConfigFilter, ToDynamicConfigFilter, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer)) } func TestAdminDescribeHistoryHostResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminDescribeHistoryHostResponse, ToAdminDescribeHistoryHostResponse) } func TestAdminGetWorkflowExecutionRawHistoryV2RequestFuzz(t *testing.T) { // [BUG] When ID or Version is nil for a given pair round-trip serialization fails. This is a mapper bug - should handle errors gracefully. testutils.RunMapperFuzzTest(t, FromAdminGetWorkflowExecutionRawHistoryV2Request, ToAdminGetWorkflowExecutionRawHistoryV2Request, testutils.WithCustomFuncs(func(r *types.GetWorkflowExecutionRawHistoryV2Request, c fuzz.Continue) { c.FuzzNoCustom(r) r.StartEventID, r.StartEventVersion = EventIDVersionPairFuzzer(c) r.EndEventID, r.EndEventVersion = EventIDVersionPairFuzzer(c) }), ) } func TestAdminRestoreDynamicConfigRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminRestoreDynamicConfigRequest, ToAdminRestoreDynamicConfigRequest, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestAdminDeleteWorkflowRequestFuzz(t *testing.T) { // [BUG] SkipErrors is not mapped testutils.RunMapperFuzzTest(t, FromAdminDeleteWorkflowRequest, ToAdminDeleteWorkflowRequest, testutils.WithExcludedFields("SkipErrors"), ) } func TestDynamicConfigEntryFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDynamicConfigEntry, ToDynamicConfigEntry, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer)) } func TestDynamicConfigValueArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDynamicConfigValueArray, ToDynamicConfigValueArray, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer)) } func TestAdminDescribeQueueResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminDescribeQueueResponse, ToAdminDescribeQueueResponse) } func TestAdminReadDLQMessagesResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminReadDLQMessagesResponse, ToAdminReadDLQMessagesResponse, testutils.WithExcludedFields("ReplicationTasks"), // TODO(c-warren): Test ReplicationTasks in shared_test.go testutils.WithCustomFuncs(testutils.DLQTypeFuzzer), ) } func TestAdminGetDynamicConfigRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetDynamicConfigRequest, ToAdminGetDynamicConfigRequest, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestDynamicConfigValueFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDynamicConfigValue, ToDynamicConfigValue, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer)) } func TestAdminCountDLQMessagesRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminCountDLQMessagesRequest, ToAdminCountDLQMessagesRequest) } func TestAdminUpdateDomainAsyncWorkflowConfiguratonRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminUpdateDomainAsyncWorkflowConfiguratonRequest, ToAdminUpdateDomainAsyncWorkflowConfiguratonRequest, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestAdminDescribeQueueRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminDescribeQueueRequest, ToAdminDescribeQueueRequest, testutils.WithCustomFuncs(func(r *types.DescribeQueueRequest, c fuzz.Continue) { c.FuzzNoCustom(r) testutils.TaskTypeFuzzer(r.Type, c) }), ) } func TestAdminMaintainCorruptWorkflowResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminMaintainCorruptWorkflowResponse, ToAdminMaintainCorruptWorkflowResponse) } func TestAdminUpdateGlobalIsolationGroupsResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminUpdateGlobalIsolationGroupsResponse, ToAdminUpdateGlobalIsolationGroupsResponse) } func TestDomainAsyncWorkflowConfiguratonFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDomainAsyncWorkflowConfiguraton, ToDomainAsyncWorkflowConfiguraton, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer)) } func TestAdminDescribeShardDistributionRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminDescribeShardDistributionRequest, ToAdminDescribeShardDistributionRequest) } func TestAdminPurgeDLQMessagesRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminPurgeDLQMessagesRequest, ToAdminPurgeDLQMessagesRequest, testutils.WithCustomFuncs(testutils.DLQTypeFuzzer), ) } func TestAdminReadDLQMessagesRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminReadDLQMessagesRequest, ToAdminReadDLQMessagesRequest, testutils.WithCustomFuncs(testutils.DLQTypeFuzzer), ) } func TestAdminGetCrossClusterTasksRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetCrossClusterTasksRequest, ToAdminGetCrossClusterTasksRequest) } func TestDynamicConfigEntryArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDynamicConfigEntryArray, ToDynamicConfigEntryArray, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer)) } func TestAdminUpdateDomainIsolationGroupsResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminUpdateDomainIsolationGroupsResponse, ToAdminUpdateDomainIsolationGroupsResponse) } func TestAdminUpdateDomainIsolationGroupsRequestFuzz(t *testing.T) { // [BUG] IsolationGroups map keys are normalized during round-trip causing map size changes // Excluding IsolationGroups field to avoid non-deterministic map key handling testutils.RunMapperFuzzTest(t, FromAdminUpdateDomainIsolationGroupsRequest, ToAdminUpdateDomainIsolationGroupsRequest, testutils.WithExcludedFields("IsolationGroups"), // IsolationGroups rely on testdata testing ) } func TestAdminGetDomainIsolationGroupsResponseFuzz(t *testing.T) { // [BUG] IsolationGroups map keys are normalized during round-trip causing map size changes // Excluding IsolationGroups field to avoid non-deterministic map key handling testutils.RunMapperFuzzTest(t, FromAdminGetDomainIsolationGroupsResponse, ToAdminGetDomainIsolationGroupsResponse, testutils.WithExcludedFields("IsolationGroups"), // IsolationGroups rely on testdata testing ) } func TestAdminGetDomainAsyncWorkflowConfiguratonRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetDomainAsyncWorkflowConfiguratonRequest, ToAdminGetDomainAsyncWorkflowConfiguratonRequest) } func TestAdminDeleteWorkflowResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminDeleteWorkflowResponse, ToAdminDeleteWorkflowResponse) } func TestAdminListDynamicConfigRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminListDynamicConfigRequest, ToAdminListDynamicConfigRequest) } func TestAdminGetGlobalIsolationGroupsResponseFuzz(t *testing.T) { // [BUG] IsolationGroups map keys are normalized during round-trip causing map size changes // Excluding IsolationGroups field to avoid non-deterministic map key handling testutils.RunMapperFuzzTest(t, FromAdminGetGlobalIsolationGroupsResponse, ToAdminGetGlobalIsolationGroupsResponse, testutils.WithExcludedFields("IsolationGroups"), ) } func TestAdminGetDomainIsolationGroupsRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAdminGetDomainIsolationGroupsRequest, ToAdminGetDomainIsolationGroupsRequest) } // DescribeHistoryHostRequestFuzzer ensures only one of the oneof fields is set func DescribeHistoryHostRequestFuzzer(r *types.DescribeHistoryHostRequest, c fuzz.Continue) { choice := c.Intn(3) switch choice { case 0: // Set HostAddress only var addr string c.Fuzz(&addr) r.HostAddress = &addr r.ShardIDForHost = nil r.ExecutionForHost = nil case 1: // Set ShardIDForHost only var shardID int32 c.Fuzz(&shardID) r.ShardIDForHost = &shardID r.HostAddress = nil r.ExecutionForHost = nil case 2: // Set ExecutionForHost only var exec types.WorkflowExecution c.Fuzz(&exec) r.ExecutionForHost = &exec r.HostAddress = nil r.ShardIDForHost = nil } } func EventIDVersionPairFuzzer(c fuzz.Continue) (*int64, *int64) { id := c.Int63() ver := c.Int63() return &id, &ver } ================================================ FILE: common/types/mapper/proto/api.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) func FromActivityLocalDispatchInfo(t *types.ActivityLocalDispatchInfo) *apiv1.ActivityLocalDispatchInfo { if t == nil { return nil } return &apiv1.ActivityLocalDispatchInfo{ ActivityId: t.ActivityID, ScheduledTime: unixNanoToTime(t.ScheduledTimestamp), StartedTime: unixNanoToTime(t.StartedTimestamp), ScheduledTimeOfThisAttempt: unixNanoToTime(t.ScheduledTimestampOfThisAttempt), TaskToken: t.TaskToken, } } func ToActivityLocalDispatchInfo(t *apiv1.ActivityLocalDispatchInfo) *types.ActivityLocalDispatchInfo { if t == nil { return nil } return &types.ActivityLocalDispatchInfo{ ActivityID: t.ActivityId, ScheduledTimestamp: timeToUnixNano(t.ScheduledTime), StartedTimestamp: timeToUnixNano(t.StartedTime), ScheduledTimestampOfThisAttempt: timeToUnixNano(t.ScheduledTimeOfThisAttempt), TaskToken: t.TaskToken, } } func FromActivityTaskCancelRequestedEventAttributes(t *types.ActivityTaskCancelRequestedEventAttributes) *apiv1.ActivityTaskCancelRequestedEventAttributes { if t == nil { return nil } return &apiv1.ActivityTaskCancelRequestedEventAttributes{ ActivityId: t.ActivityID, DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, } } func ToActivityTaskCancelRequestedEventAttributes(t *apiv1.ActivityTaskCancelRequestedEventAttributes) *types.ActivityTaskCancelRequestedEventAttributes { if t == nil { return nil } return &types.ActivityTaskCancelRequestedEventAttributes{ ActivityID: t.ActivityId, DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, } } func FromActivityTaskCanceledEventAttributes(t *types.ActivityTaskCanceledEventAttributes) *apiv1.ActivityTaskCanceledEventAttributes { if t == nil { return nil } return &apiv1.ActivityTaskCanceledEventAttributes{ Details: FromPayload(t.Details), LatestCancelRequestedEventId: t.LatestCancelRequestedEventID, ScheduledEventId: t.ScheduledEventID, StartedEventId: t.StartedEventID, Identity: t.Identity, } } func ToActivityTaskCanceledEventAttributes(t *apiv1.ActivityTaskCanceledEventAttributes) *types.ActivityTaskCanceledEventAttributes { if t == nil { return nil } return &types.ActivityTaskCanceledEventAttributes{ Details: ToPayload(t.Details), LatestCancelRequestedEventID: t.LatestCancelRequestedEventId, ScheduledEventID: t.ScheduledEventId, StartedEventID: t.StartedEventId, Identity: t.Identity, } } func FromActivityTaskCompletedEventAttributes(t *types.ActivityTaskCompletedEventAttributes) *apiv1.ActivityTaskCompletedEventAttributes { if t == nil { return nil } return &apiv1.ActivityTaskCompletedEventAttributes{ Result: FromPayload(t.Result), ScheduledEventId: t.ScheduledEventID, StartedEventId: t.StartedEventID, Identity: t.Identity, } } func ToActivityTaskCompletedEventAttributes(t *apiv1.ActivityTaskCompletedEventAttributes) *types.ActivityTaskCompletedEventAttributes { if t == nil { return nil } return &types.ActivityTaskCompletedEventAttributes{ Result: ToPayload(t.Result), ScheduledEventID: t.ScheduledEventId, StartedEventID: t.StartedEventId, Identity: t.Identity, } } func FromActivityTaskFailedEventAttributes(t *types.ActivityTaskFailedEventAttributes) *apiv1.ActivityTaskFailedEventAttributes { if t == nil { return nil } return &apiv1.ActivityTaskFailedEventAttributes{ Failure: FromFailure(t.Reason, t.Details), ScheduledEventId: t.ScheduledEventID, StartedEventId: t.StartedEventID, Identity: t.Identity, } } func ToActivityTaskFailedEventAttributes(t *apiv1.ActivityTaskFailedEventAttributes) *types.ActivityTaskFailedEventAttributes { if t == nil { return nil } return &types.ActivityTaskFailedEventAttributes{ Reason: ToFailureReason(t.Failure), Details: ToFailureDetails(t.Failure), ScheduledEventID: t.ScheduledEventId, StartedEventID: t.StartedEventId, Identity: t.Identity, } } func FromActivityTaskScheduledEventAttributes(t *types.ActivityTaskScheduledEventAttributes) *apiv1.ActivityTaskScheduledEventAttributes { if t == nil { return nil } return &apiv1.ActivityTaskScheduledEventAttributes{ ActivityId: t.ActivityID, ActivityType: FromActivityType(t.ActivityType), Domain: t.GetDomain(), TaskList: FromTaskList(t.TaskList), Input: FromPayload(t.Input), ScheduleToCloseTimeout: secondsToDuration(t.ScheduleToCloseTimeoutSeconds), ScheduleToStartTimeout: secondsToDuration(t.ScheduleToStartTimeoutSeconds), StartToCloseTimeout: secondsToDuration(t.StartToCloseTimeoutSeconds), HeartbeatTimeout: secondsToDuration(t.HeartbeatTimeoutSeconds), DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, RetryPolicy: FromRetryPolicy(t.RetryPolicy), Header: FromHeader(t.Header), } } func ToActivityTaskScheduledEventAttributes(t *apiv1.ActivityTaskScheduledEventAttributes) *types.ActivityTaskScheduledEventAttributes { if t == nil { return nil } return &types.ActivityTaskScheduledEventAttributes{ ActivityID: t.ActivityId, ActivityType: ToActivityType(t.ActivityType), Domain: &t.Domain, TaskList: ToTaskList(t.TaskList), Input: ToPayload(t.Input), ScheduleToCloseTimeoutSeconds: durationToSeconds(t.ScheduleToCloseTimeout), ScheduleToStartTimeoutSeconds: durationToSeconds(t.ScheduleToStartTimeout), StartToCloseTimeoutSeconds: durationToSeconds(t.StartToCloseTimeout), HeartbeatTimeoutSeconds: durationToSeconds(t.HeartbeatTimeout), DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, RetryPolicy: ToRetryPolicy(t.RetryPolicy), Header: ToHeader(t.Header), } } func FromActivityTaskStartedEventAttributes(t *types.ActivityTaskStartedEventAttributes) *apiv1.ActivityTaskStartedEventAttributes { if t == nil { return nil } return &apiv1.ActivityTaskStartedEventAttributes{ ScheduledEventId: t.ScheduledEventID, Identity: t.Identity, RequestId: t.RequestID, Attempt: t.Attempt, LastFailure: FromFailure(t.LastFailureReason, t.LastFailureDetails), } } func ToActivityTaskStartedEventAttributes(t *apiv1.ActivityTaskStartedEventAttributes) *types.ActivityTaskStartedEventAttributes { if t == nil { return nil } return &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: t.ScheduledEventId, Identity: t.Identity, RequestID: t.RequestId, Attempt: t.Attempt, LastFailureReason: ToFailureReason(t.LastFailure), LastFailureDetails: ToFailureDetails(t.LastFailure), } } func FromActivityTaskTimedOutEventAttributes(t *types.ActivityTaskTimedOutEventAttributes) *apiv1.ActivityTaskTimedOutEventAttributes { if t == nil { return nil } return &apiv1.ActivityTaskTimedOutEventAttributes{ Details: FromPayload(t.Details), ScheduledEventId: t.ScheduledEventID, StartedEventId: t.StartedEventID, TimeoutType: FromTimeoutType(t.TimeoutType), LastFailure: FromFailure(t.LastFailureReason, t.LastFailureDetails), } } func ToActivityTaskTimedOutEventAttributes(t *apiv1.ActivityTaskTimedOutEventAttributes) *types.ActivityTaskTimedOutEventAttributes { if t == nil { return nil } return &types.ActivityTaskTimedOutEventAttributes{ Details: ToPayload(t.Details), ScheduledEventID: t.ScheduledEventId, StartedEventID: t.StartedEventId, TimeoutType: ToTimeoutType(t.TimeoutType), LastFailureReason: ToFailureReason(t.LastFailure), LastFailureDetails: ToFailureDetails(t.LastFailure), } } func FromActivityType(t *types.ActivityType) *apiv1.ActivityType { if t == nil { return nil } return &apiv1.ActivityType{ Name: t.Name, } } func ToActivityType(t *apiv1.ActivityType) *types.ActivityType { if t == nil { return nil } return &types.ActivityType{ Name: t.Name, } } func FromArchivalStatus(t *types.ArchivalStatus) apiv1.ArchivalStatus { if t == nil { return apiv1.ArchivalStatus_ARCHIVAL_STATUS_INVALID } switch *t { case types.ArchivalStatusDisabled: return apiv1.ArchivalStatus_ARCHIVAL_STATUS_DISABLED case types.ArchivalStatusEnabled: return apiv1.ArchivalStatus_ARCHIVAL_STATUS_ENABLED } return apiv1.ArchivalStatus_ARCHIVAL_STATUS_INVALID } func ToArchivalStatus(t apiv1.ArchivalStatus) *types.ArchivalStatus { switch t { case apiv1.ArchivalStatus_ARCHIVAL_STATUS_INVALID: return nil case apiv1.ArchivalStatus_ARCHIVAL_STATUS_DISABLED: return types.ArchivalStatusDisabled.Ptr() case apiv1.ArchivalStatus_ARCHIVAL_STATUS_ENABLED: return types.ArchivalStatusEnabled.Ptr() } return nil } func FromBadBinaries(t *types.BadBinaries) *apiv1.BadBinaries { if t == nil { return nil } return &apiv1.BadBinaries{ Binaries: FromBadBinaryInfoMap(t.Binaries), } } func ToBadBinaries(t *apiv1.BadBinaries) *types.BadBinaries { if t == nil { return nil } return &types.BadBinaries{ Binaries: ToBadBinaryInfoMap(t.Binaries), } } func FromBadBinaryInfo(t *types.BadBinaryInfo) *apiv1.BadBinaryInfo { if t == nil { return nil } return &apiv1.BadBinaryInfo{ Reason: t.Reason, Operator: t.Operator, CreatedTime: unixNanoToTime(t.CreatedTimeNano), } } func ToBadBinaryInfo(t *apiv1.BadBinaryInfo) *types.BadBinaryInfo { if t == nil { return nil } return &types.BadBinaryInfo{ Reason: t.Reason, Operator: t.Operator, CreatedTimeNano: timeToUnixNano(t.CreatedTime), } } func FromCancelExternalWorkflowExecutionFailedCause(t *types.CancelExternalWorkflowExecutionFailedCause) apiv1.CancelExternalWorkflowExecutionFailedCause { if t == nil { return apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID } switch *t { case types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution: return apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION case types.CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted: return apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_COMPLETED } return apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID } func ToCancelExternalWorkflowExecutionFailedCause(t apiv1.CancelExternalWorkflowExecutionFailedCause) *types.CancelExternalWorkflowExecutionFailedCause { switch t { case apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID: return nil case apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION: return types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr() case apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_COMPLETED: return types.CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted.Ptr() } return nil } func FromCancelTimerDecisionAttributes(t *types.CancelTimerDecisionAttributes) *apiv1.CancelTimerDecisionAttributes { if t == nil { return nil } return &apiv1.CancelTimerDecisionAttributes{ TimerId: t.TimerID, } } func ToCancelTimerDecisionAttributes(t *apiv1.CancelTimerDecisionAttributes) *types.CancelTimerDecisionAttributes { if t == nil { return nil } return &types.CancelTimerDecisionAttributes{ TimerID: t.TimerId, } } func FromCancelTimerFailedEventAttributes(t *types.CancelTimerFailedEventAttributes) *apiv1.CancelTimerFailedEventAttributes { if t == nil { return nil } return &apiv1.CancelTimerFailedEventAttributes{ TimerId: t.TimerID, Cause: t.Cause, DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, Identity: t.Identity, } } func ToCancelTimerFailedEventAttributes(t *apiv1.CancelTimerFailedEventAttributes) *types.CancelTimerFailedEventAttributes { if t == nil { return nil } return &types.CancelTimerFailedEventAttributes{ TimerID: t.TimerId, Cause: t.Cause, DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, Identity: t.Identity, } } func FromCancelWorkflowExecutionDecisionAttributes(t *types.CancelWorkflowExecutionDecisionAttributes) *apiv1.CancelWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &apiv1.CancelWorkflowExecutionDecisionAttributes{ Details: FromPayload(t.Details), } } func ToCancelWorkflowExecutionDecisionAttributes(t *apiv1.CancelWorkflowExecutionDecisionAttributes) *types.CancelWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.CancelWorkflowExecutionDecisionAttributes{ Details: ToPayload(t.Details), } } func FromChildWorkflowExecutionCanceledEventAttributes(t *types.ChildWorkflowExecutionCanceledEventAttributes) *apiv1.ChildWorkflowExecutionCanceledEventAttributes { if t == nil { return nil } return &apiv1.ChildWorkflowExecutionCanceledEventAttributes{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: t.InitiatedEventID, StartedEventId: t.StartedEventID, Details: FromPayload(t.Details), } } func ToChildWorkflowExecutionCanceledEventAttributes(t *apiv1.ChildWorkflowExecutionCanceledEventAttributes) *types.ChildWorkflowExecutionCanceledEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionCanceledEventAttributes{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.InitiatedEventId, StartedEventID: t.StartedEventId, Details: ToPayload(t.Details), } } func FromChildWorkflowExecutionCompletedEventAttributes(t *types.ChildWorkflowExecutionCompletedEventAttributes) *apiv1.ChildWorkflowExecutionCompletedEventAttributes { if t == nil { return nil } return &apiv1.ChildWorkflowExecutionCompletedEventAttributes{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: t.InitiatedEventID, StartedEventId: t.StartedEventID, Result: FromPayload(t.Result), } } func ToChildWorkflowExecutionCompletedEventAttributes(t *apiv1.ChildWorkflowExecutionCompletedEventAttributes) *types.ChildWorkflowExecutionCompletedEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionCompletedEventAttributes{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.InitiatedEventId, StartedEventID: t.StartedEventId, Result: ToPayload(t.Result), } } func FromChildWorkflowExecutionFailedCause(t *types.ChildWorkflowExecutionFailedCause) apiv1.ChildWorkflowExecutionFailedCause { if t == nil { return apiv1.ChildWorkflowExecutionFailedCause_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID } switch *t { case types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning: return apiv1.ChildWorkflowExecutionFailedCause_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_RUNNING } return apiv1.ChildWorkflowExecutionFailedCause_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID } func ToChildWorkflowExecutionFailedCause(t apiv1.ChildWorkflowExecutionFailedCause) *types.ChildWorkflowExecutionFailedCause { switch t { case apiv1.ChildWorkflowExecutionFailedCause_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID: return nil case apiv1.ChildWorkflowExecutionFailedCause_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_RUNNING: return types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning.Ptr() } return nil } func FromChildWorkflowExecutionFailedEventAttributes(t *types.ChildWorkflowExecutionFailedEventAttributes) *apiv1.ChildWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &apiv1.ChildWorkflowExecutionFailedEventAttributes{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: t.InitiatedEventID, StartedEventId: t.StartedEventID, Failure: FromFailure(t.Reason, t.Details), } } func ToChildWorkflowExecutionFailedEventAttributes(t *apiv1.ChildWorkflowExecutionFailedEventAttributes) *types.ChildWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionFailedEventAttributes{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.InitiatedEventId, StartedEventID: t.StartedEventId, Reason: ToFailureReason(t.Failure), Details: ToFailureDetails(t.Failure), } } func FromChildWorkflowExecutionStartedEventAttributes(t *types.ChildWorkflowExecutionStartedEventAttributes) *apiv1.ChildWorkflowExecutionStartedEventAttributes { if t == nil { return nil } return &apiv1.ChildWorkflowExecutionStartedEventAttributes{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: t.InitiatedEventID, Header: FromHeader(t.Header), } } func ToChildWorkflowExecutionStartedEventAttributes(t *apiv1.ChildWorkflowExecutionStartedEventAttributes) *types.ChildWorkflowExecutionStartedEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionStartedEventAttributes{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.InitiatedEventId, Header: ToHeader(t.Header), } } func FromChildWorkflowExecutionTerminatedEventAttributes(t *types.ChildWorkflowExecutionTerminatedEventAttributes) *apiv1.ChildWorkflowExecutionTerminatedEventAttributes { if t == nil { return nil } return &apiv1.ChildWorkflowExecutionTerminatedEventAttributes{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: t.InitiatedEventID, StartedEventId: t.StartedEventID, } } func ToChildWorkflowExecutionTerminatedEventAttributes(t *apiv1.ChildWorkflowExecutionTerminatedEventAttributes) *types.ChildWorkflowExecutionTerminatedEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionTerminatedEventAttributes{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.InitiatedEventId, StartedEventID: t.StartedEventId, } } func FromChildWorkflowExecutionTimedOutEventAttributes(t *types.ChildWorkflowExecutionTimedOutEventAttributes) *apiv1.ChildWorkflowExecutionTimedOutEventAttributes { if t == nil { return nil } return &apiv1.ChildWorkflowExecutionTimedOutEventAttributes{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: t.InitiatedEventID, StartedEventId: t.StartedEventID, TimeoutType: FromTimeoutType(t.TimeoutType), } } func ToChildWorkflowExecutionTimedOutEventAttributes(t *apiv1.ChildWorkflowExecutionTimedOutEventAttributes) *types.ChildWorkflowExecutionTimedOutEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionTimedOutEventAttributes{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.InitiatedEventId, StartedEventID: t.StartedEventId, TimeoutType: ToTimeoutType(t.TimeoutType), } } func FromClusterReplicationConfiguration(t *types.ClusterReplicationConfiguration) *apiv1.ClusterReplicationConfiguration { if t == nil { return nil } return &apiv1.ClusterReplicationConfiguration{ ClusterName: t.ClusterName, } } func ToClusterReplicationConfiguration(t *apiv1.ClusterReplicationConfiguration) *types.ClusterReplicationConfiguration { if t == nil { return nil } return &types.ClusterReplicationConfiguration{ ClusterName: t.ClusterName, } } func FromCompleteWorkflowExecutionDecisionAttributes(t *types.CompleteWorkflowExecutionDecisionAttributes) *apiv1.CompleteWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &apiv1.CompleteWorkflowExecutionDecisionAttributes{ Result: FromPayload(t.Result), } } func ToCompleteWorkflowExecutionDecisionAttributes(t *apiv1.CompleteWorkflowExecutionDecisionAttributes) *types.CompleteWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.CompleteWorkflowExecutionDecisionAttributes{ Result: ToPayload(t.Result), } } func FromContinueAsNewInitiator(t *types.ContinueAsNewInitiator) apiv1.ContinueAsNewInitiator { if t == nil { return apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_INVALID } switch *t { case types.ContinueAsNewInitiatorDecider: return apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_DECIDER case types.ContinueAsNewInitiatorRetryPolicy: return apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_RETRY_POLICY case types.ContinueAsNewInitiatorCronSchedule: return apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_CRON_SCHEDULE } return apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_INVALID } func ToContinueAsNewInitiator(t apiv1.ContinueAsNewInitiator) *types.ContinueAsNewInitiator { switch t { case apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_INVALID: return nil case apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_DECIDER: return types.ContinueAsNewInitiatorDecider.Ptr() case apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_RETRY_POLICY: return types.ContinueAsNewInitiatorRetryPolicy.Ptr() case apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_CRON_SCHEDULE: return types.ContinueAsNewInitiatorCronSchedule.Ptr() } return nil } func FromContinueAsNewWorkflowExecutionDecisionAttributes(t *types.ContinueAsNewWorkflowExecutionDecisionAttributes) *apiv1.ContinueAsNewWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &apiv1.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: FromPayload(t.Input), ExecutionStartToCloseTimeout: secondsToDuration(t.ExecutionStartToCloseTimeoutSeconds), TaskStartToCloseTimeout: secondsToDuration(t.TaskStartToCloseTimeoutSeconds), BackoffStartInterval: secondsToDuration(t.BackoffStartIntervalInSeconds), RetryPolicy: FromRetryPolicy(t.RetryPolicy), Initiator: FromContinueAsNewInitiator(t.Initiator), Failure: FromFailure(t.FailureReason, t.FailureDetails), LastCompletionResult: FromPayload(t.LastCompletionResult), CronSchedule: t.CronSchedule, Header: FromHeader(t.Header), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), JitterStart: secondsToDuration(t.JitterStartSeconds), CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } func ToContinueAsNewWorkflowExecutionDecisionAttributes(t *apiv1.ContinueAsNewWorkflowExecutionDecisionAttributes) *types.ContinueAsNewWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: ToPayload(t.Input), ExecutionStartToCloseTimeoutSeconds: durationToSeconds(t.ExecutionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: durationToSeconds(t.TaskStartToCloseTimeout), BackoffStartIntervalInSeconds: durationToSeconds(t.BackoffStartInterval), RetryPolicy: ToRetryPolicy(t.RetryPolicy), Initiator: ToContinueAsNewInitiator(t.Initiator), FailureReason: ToFailureReason(t.Failure), FailureDetails: ToFailureDetails(t.Failure), LastCompletionResult: ToPayload(t.LastCompletionResult), CronSchedule: t.CronSchedule, Header: ToHeader(t.Header), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), JitterStartSeconds: durationToSeconds(t.JitterStart), CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } func FromCountWorkflowExecutionsRequest(t *types.CountWorkflowExecutionsRequest) *apiv1.CountWorkflowExecutionsRequest { if t == nil { return nil } return &apiv1.CountWorkflowExecutionsRequest{ Domain: t.Domain, Query: t.Query, } } func ToCountWorkflowExecutionsRequest(t *apiv1.CountWorkflowExecutionsRequest) *types.CountWorkflowExecutionsRequest { if t == nil { return nil } return &types.CountWorkflowExecutionsRequest{ Domain: t.Domain, Query: t.Query, } } func FromCountWorkflowExecutionsResponse(t *types.CountWorkflowExecutionsResponse) *apiv1.CountWorkflowExecutionsResponse { if t == nil { return nil } return &apiv1.CountWorkflowExecutionsResponse{ Count: t.Count, } } func ToCountWorkflowExecutionsResponse(t *apiv1.CountWorkflowExecutionsResponse) *types.CountWorkflowExecutionsResponse { if t == nil { return nil } return &types.CountWorkflowExecutionsResponse{ Count: t.Count, } } func FromDataBlob(t *types.DataBlob) *apiv1.DataBlob { if t == nil { return nil } return &apiv1.DataBlob{ EncodingType: FromEncodingType(t.EncodingType), Data: t.Data, } } func ToDataBlob(t *apiv1.DataBlob) *types.DataBlob { if t == nil { return nil } return &types.DataBlob{ EncodingType: ToEncodingType(t.EncodingType), Data: t.Data, } } func FromDecisionTaskCompletedEventAttributes(t *types.DecisionTaskCompletedEventAttributes) *apiv1.DecisionTaskCompletedEventAttributes { if t == nil { return nil } return &apiv1.DecisionTaskCompletedEventAttributes{ ScheduledEventId: t.ScheduledEventID, StartedEventId: t.StartedEventID, Identity: t.Identity, BinaryChecksum: t.BinaryChecksum, ExecutionContext: t.ExecutionContext, } } func ToDecisionTaskCompletedEventAttributes(t *apiv1.DecisionTaskCompletedEventAttributes) *types.DecisionTaskCompletedEventAttributes { if t == nil { return nil } return &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: t.ScheduledEventId, StartedEventID: t.StartedEventId, Identity: t.Identity, BinaryChecksum: t.BinaryChecksum, ExecutionContext: t.ExecutionContext, } } func FromDecisionTaskFailedCause(t *types.DecisionTaskFailedCause) apiv1.DecisionTaskFailedCause { if t == nil { return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_INVALID } switch *t { case types.DecisionTaskFailedCauseUnhandledDecision: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_UNHANDLED_DECISION case types.DecisionTaskFailedCauseBadScheduleActivityAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_SCHEDULE_ACTIVITY_ATTRIBUTES case types.DecisionTaskFailedCauseBadRequestCancelActivityAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES case types.DecisionTaskFailedCauseBadStartTimerAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_START_TIMER_ATTRIBUTES case types.DecisionTaskFailedCauseBadCancelTimerAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_CANCEL_TIMER_ATTRIBUTES case types.DecisionTaskFailedCauseBadRecordMarkerAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_RECORD_MARKER_ATTRIBUTES case types.DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES case types.DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES case types.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES case types.DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES case types.DecisionTaskFailedCauseBadContinueAsNewAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_CONTINUE_AS_NEW_ATTRIBUTES case types.DecisionTaskFailedCauseStartTimerDuplicateID: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_START_TIMER_DUPLICATE_ID case types.DecisionTaskFailedCauseResetStickyTasklist: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_RESET_STICKY_TASK_LIST case types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_WORKFLOW_WORKER_UNHANDLED_FAILURE case types.DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES case types.DecisionTaskFailedCauseBadStartChildExecutionAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_START_CHILD_EXECUTION_ATTRIBUTES case types.DecisionTaskFailedCauseForceCloseDecision: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_FORCE_CLOSE_DECISION case types.DecisionTaskFailedCauseFailoverCloseDecision: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_FAILOVER_CLOSE_DECISION case types.DecisionTaskFailedCauseBadSignalInputSize: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_SIGNAL_INPUT_SIZE case types.DecisionTaskFailedCauseResetWorkflow: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_RESET_WORKFLOW case types.DecisionTaskFailedCauseBadBinary: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_BINARY case types.DecisionTaskFailedCauseScheduleActivityDuplicateID: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_SCHEDULE_ACTIVITY_DUPLICATE_ID case types.DecisionTaskFailedCauseBadSearchAttributes: return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_SEARCH_ATTRIBUTES } return apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_INVALID } func ToDecisionTaskFailedCause(t apiv1.DecisionTaskFailedCause) *types.DecisionTaskFailedCause { switch t { case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_INVALID: return nil case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_UNHANDLED_DECISION: return types.DecisionTaskFailedCauseUnhandledDecision.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_SCHEDULE_ACTIVITY_ATTRIBUTES: return types.DecisionTaskFailedCauseBadScheduleActivityAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES: return types.DecisionTaskFailedCauseBadRequestCancelActivityAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_START_TIMER_ATTRIBUTES: return types.DecisionTaskFailedCauseBadStartTimerAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_CANCEL_TIMER_ATTRIBUTES: return types.DecisionTaskFailedCauseBadCancelTimerAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_RECORD_MARKER_ATTRIBUTES: return types.DecisionTaskFailedCauseBadRecordMarkerAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES: return types.DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES: return types.DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES: return types.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES: return types.DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_CONTINUE_AS_NEW_ATTRIBUTES: return types.DecisionTaskFailedCauseBadContinueAsNewAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_START_TIMER_DUPLICATE_ID: return types.DecisionTaskFailedCauseStartTimerDuplicateID.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_RESET_STICKY_TASK_LIST: return types.DecisionTaskFailedCauseResetStickyTasklist.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_WORKFLOW_WORKER_UNHANDLED_FAILURE: return types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES: return types.DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_START_CHILD_EXECUTION_ATTRIBUTES: return types.DecisionTaskFailedCauseBadStartChildExecutionAttributes.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_FORCE_CLOSE_DECISION: return types.DecisionTaskFailedCauseForceCloseDecision.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_FAILOVER_CLOSE_DECISION: return types.DecisionTaskFailedCauseFailoverCloseDecision.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_SIGNAL_INPUT_SIZE: return types.DecisionTaskFailedCauseBadSignalInputSize.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_RESET_WORKFLOW: return types.DecisionTaskFailedCauseResetWorkflow.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_BINARY: return types.DecisionTaskFailedCauseBadBinary.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_SCHEDULE_ACTIVITY_DUPLICATE_ID: return types.DecisionTaskFailedCauseScheduleActivityDuplicateID.Ptr() case apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_BAD_SEARCH_ATTRIBUTES: return types.DecisionTaskFailedCauseBadSearchAttributes.Ptr() } return nil } func FromDecisionTaskFailedEventAttributes(t *types.DecisionTaskFailedEventAttributes) *apiv1.DecisionTaskFailedEventAttributes { if t == nil { return nil } return &apiv1.DecisionTaskFailedEventAttributes{ ScheduledEventId: t.ScheduledEventID, StartedEventId: t.StartedEventID, Cause: FromDecisionTaskFailedCause(t.Cause), Failure: FromFailure(t.Reason, t.Details), Identity: t.Identity, BaseRunId: t.BaseRunID, NewRunId: t.NewRunID, ForkEventVersion: t.ForkEventVersion, BinaryChecksum: t.BinaryChecksum, RequestId: t.RequestID, } } func ToDecisionTaskFailedEventAttributes(t *apiv1.DecisionTaskFailedEventAttributes) *types.DecisionTaskFailedEventAttributes { if t == nil { return nil } return &types.DecisionTaskFailedEventAttributes{ ScheduledEventID: t.ScheduledEventId, StartedEventID: t.StartedEventId, Cause: ToDecisionTaskFailedCause(t.Cause), Reason: ToFailureReason(t.Failure), Details: ToFailureDetails(t.Failure), Identity: t.Identity, BaseRunID: t.BaseRunId, NewRunID: t.NewRunId, ForkEventVersion: t.ForkEventVersion, BinaryChecksum: t.BinaryChecksum, RequestID: t.RequestId, } } func FromDecisionTaskScheduledEventAttributes(t *types.DecisionTaskScheduledEventAttributes) *apiv1.DecisionTaskScheduledEventAttributes { if t == nil { return nil } return &apiv1.DecisionTaskScheduledEventAttributes{ TaskList: FromTaskList(t.TaskList), StartToCloseTimeout: secondsToDuration(t.StartToCloseTimeoutSeconds), Attempt: int32(t.Attempt), } } func ToDecisionTaskScheduledEventAttributes(t *apiv1.DecisionTaskScheduledEventAttributes) *types.DecisionTaskScheduledEventAttributes { if t == nil { return nil } return &types.DecisionTaskScheduledEventAttributes{ TaskList: ToTaskList(t.TaskList), StartToCloseTimeoutSeconds: durationToSeconds(t.StartToCloseTimeout), Attempt: int64(t.Attempt), } } func FromDecisionTaskStartedEventAttributes(t *types.DecisionTaskStartedEventAttributes) *apiv1.DecisionTaskStartedEventAttributes { if t == nil { return nil } return &apiv1.DecisionTaskStartedEventAttributes{ ScheduledEventId: t.ScheduledEventID, Identity: t.Identity, RequestId: t.RequestID, } } func ToDecisionTaskStartedEventAttributes(t *apiv1.DecisionTaskStartedEventAttributes) *types.DecisionTaskStartedEventAttributes { if t == nil { return nil } return &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: t.ScheduledEventId, Identity: t.Identity, RequestID: t.RequestId, } } func FromDecisionTaskTimedOutEventAttributes(t *types.DecisionTaskTimedOutEventAttributes) *apiv1.DecisionTaskTimedOutEventAttributes { if t == nil { return nil } return &apiv1.DecisionTaskTimedOutEventAttributes{ ScheduledEventId: t.ScheduledEventID, StartedEventId: t.StartedEventID, TimeoutType: FromTimeoutType(t.TimeoutType), BaseRunId: t.BaseRunID, NewRunId: t.NewRunID, ForkEventVersion: t.ForkEventVersion, Reason: t.Reason, Cause: FromDecisionTaskTimedOutCause(t.Cause), RequestId: t.RequestID, } } func ToDecisionTaskTimedOutEventAttributes(t *apiv1.DecisionTaskTimedOutEventAttributes) *types.DecisionTaskTimedOutEventAttributes { if t == nil { return nil } return &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: t.ScheduledEventId, StartedEventID: t.StartedEventId, TimeoutType: ToTimeoutType(t.TimeoutType), BaseRunID: t.BaseRunId, NewRunID: t.NewRunId, ForkEventVersion: t.ForkEventVersion, Reason: t.Reason, Cause: ToDecisionTaskTimedOutCause(t.Cause), RequestID: t.RequestId, } } func FromDeleteDomainRequest(t *types.DeleteDomainRequest) *apiv1.DeleteDomainRequest { if t == nil { return nil } return &apiv1.DeleteDomainRequest{ Name: t.Name, SecurityToken: t.SecurityToken, } } func ToDeleteDomainRequest(t *apiv1.DeleteDomainRequest) *types.DeleteDomainRequest { if t == nil { return nil } return &types.DeleteDomainRequest{ Name: t.Name, SecurityToken: t.SecurityToken, } } func FromDeprecateDomainRequest(t *types.DeprecateDomainRequest) *apiv1.DeprecateDomainRequest { if t == nil { return nil } return &apiv1.DeprecateDomainRequest{ Name: t.Name, SecurityToken: t.SecurityToken, } } func ToDeprecateDomainRequest(t *apiv1.DeprecateDomainRequest) *types.DeprecateDomainRequest { if t == nil { return nil } return &types.DeprecateDomainRequest{ Name: t.Name, SecurityToken: t.SecurityToken, } } func FromDescribeDomainRequest(t *types.DescribeDomainRequest) *apiv1.DescribeDomainRequest { if t == nil { return nil } if t.UUID != nil { return &apiv1.DescribeDomainRequest{DescribeBy: &apiv1.DescribeDomainRequest_Id{Id: *t.UUID}} } if t.Name != nil { return &apiv1.DescribeDomainRequest{DescribeBy: &apiv1.DescribeDomainRequest_Name{Name: *t.Name}} } // TODO: Remove this panic and decide on an error behaviour panic("neither oneof field is set for DescribeDomainRequest") } func ToDescribeDomainRequest(t *apiv1.DescribeDomainRequest) *types.DescribeDomainRequest { if t == nil { return nil } switch describeBy := t.DescribeBy.(type) { case *apiv1.DescribeDomainRequest_Id: return &types.DescribeDomainRequest{UUID: common.StringPtr(describeBy.Id)} case *apiv1.DescribeDomainRequest_Name: return &types.DescribeDomainRequest{Name: common.StringPtr(describeBy.Name)} } // TODO: Remove this panic and decide on an error behaviour panic("neither oneof field is set for DescribeDomainRequest") } func FromDescribeDomainResponseDomain(t *types.DescribeDomainResponse) *apiv1.Domain { if t == nil { return nil } domain := apiv1.Domain{ FailoverVersion: t.FailoverVersion, IsGlobalDomain: t.IsGlobalDomain, } if info := t.DomainInfo; info != nil { domain.Id = info.UUID domain.Name = info.Name domain.Status = FromDomainStatus(info.Status) domain.Description = info.Description domain.OwnerEmail = info.OwnerEmail domain.Data = info.Data } if config := t.Configuration; config != nil { domain.IsolationGroups = FromIsolationGroupConfig(config.IsolationGroups) domain.WorkflowExecutionRetentionPeriod = daysToDuration(&config.WorkflowExecutionRetentionPeriodInDays) domain.BadBinaries = FromBadBinaries(config.BadBinaries) domain.HistoryArchivalStatus = FromArchivalStatus(config.HistoryArchivalStatus) domain.HistoryArchivalUri = config.HistoryArchivalURI domain.VisibilityArchivalStatus = FromArchivalStatus(config.VisibilityArchivalStatus) domain.VisibilityArchivalUri = config.VisibilityArchivalURI domain.AsyncWorkflowConfig = FromDomainAsyncWorkflowConfiguraton(config.AsyncWorkflowConfig) } if repl := t.ReplicationConfiguration; repl != nil { domain.ActiveClusterName = repl.ActiveClusterName domain.Clusters = FromClusterReplicationConfigurationArray(repl.Clusters) domain.ActiveClusters = FromActiveClusters(repl.ActiveClusters) } if info := t.GetFailoverInfo(); info != nil { domain.FailoverInfo = FromFailoverInfo(t.GetFailoverInfo()) } return &domain } func FromDescribeDomainResponse(t *types.DescribeDomainResponse) *apiv1.DescribeDomainResponse { if t == nil { return nil } return &apiv1.DescribeDomainResponse{ Domain: FromDescribeDomainResponseDomain(t), } } func ToDescribeDomainResponseDomain(t *apiv1.Domain) *types.DescribeDomainResponse { if t == nil { return nil } return &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: t.Name, Status: ToDomainStatus(t.Status), Description: t.Description, OwnerEmail: t.OwnerEmail, Data: t.Data, UUID: t.Id, }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: common.Int32Default(durationToDays(t.WorkflowExecutionRetentionPeriod)), EmitMetric: true, BadBinaries: ToBadBinaries(t.BadBinaries), HistoryArchivalStatus: ToArchivalStatus(t.HistoryArchivalStatus), HistoryArchivalURI: t.HistoryArchivalUri, VisibilityArchivalStatus: ToArchivalStatus(t.VisibilityArchivalStatus), VisibilityArchivalURI: t.VisibilityArchivalUri, IsolationGroups: ToIsolationGroupConfig(t.IsolationGroups), AsyncWorkflowConfig: ToDomainAsyncWorkflowConfiguraton(t.AsyncWorkflowConfig), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: t.ActiveClusterName, Clusters: ToClusterReplicationConfigurationArray(t.Clusters), ActiveClusters: ToActiveClusters(t.ActiveClusters), }, FailoverVersion: t.FailoverVersion, IsGlobalDomain: t.IsGlobalDomain, FailoverInfo: ToFailoverInfo(t.FailoverInfo), } } func ToDescribeDomainResponse(t *apiv1.DescribeDomainResponse) *types.DescribeDomainResponse { if t == nil { return nil } response := ToDescribeDomainResponseDomain(t.Domain) return response } func FromFailoverInfo(t *types.FailoverInfo) *apiv1.FailoverInfo { if t == nil { return nil } return &apiv1.FailoverInfo{ FailoverVersion: t.GetFailoverVersion(), FailoverStartTimestamp: unixNanoToTime(&t.FailoverStartTimestamp), FailoverExpireTimestamp: unixNanoToTime(&t.FailoverExpireTimestamp), CompletedShardCount: t.GetCompletedShardCount(), PendingShards: t.GetPendingShards(), } } func ToFailoverInfo(t *apiv1.FailoverInfo) *types.FailoverInfo { if t == nil { return nil } startTs := timeToUnixNano(t.GetFailoverStartTimestamp()) expireTs := timeToUnixNano(t.GetFailoverExpireTimestamp()) if startTs == nil || expireTs == nil { return nil } return &types.FailoverInfo{ FailoverVersion: t.GetFailoverVersion(), FailoverStartTimestamp: *startTs, FailoverExpireTimestamp: *expireTs, CompletedShardCount: t.GetCompletedShardCount(), PendingShards: t.GetPendingShards(), } } func FromDescribeTaskListRequest(t *types.DescribeTaskListRequest) *apiv1.DescribeTaskListRequest { if t == nil { return nil } return &apiv1.DescribeTaskListRequest{ Domain: t.Domain, TaskList: FromTaskList(t.TaskList), TaskListType: FromTaskListType(t.TaskListType), IncludeTaskListStatus: t.IncludeTaskListStatus, } } func ToDescribeTaskListRequest(t *apiv1.DescribeTaskListRequest) *types.DescribeTaskListRequest { if t == nil { return nil } return &types.DescribeTaskListRequest{ Domain: t.Domain, TaskList: ToTaskList(t.TaskList), TaskListType: ToTaskListType(t.TaskListType), IncludeTaskListStatus: t.IncludeTaskListStatus, } } func FromDescribeTaskListResponse(t *types.DescribeTaskListResponse) *apiv1.DescribeTaskListResponse { if t == nil { return nil } return &apiv1.DescribeTaskListResponse{ Pollers: FromPollerInfoArray(t.Pollers), TaskListStatus: FromTaskListStatus(t.TaskListStatus), PartitionConfig: FromAPITaskListPartitionConfig(t.PartitionConfig), TaskList: FromTaskList(t.TaskList), } } func ToDescribeTaskListResponse(t *apiv1.DescribeTaskListResponse) *types.DescribeTaskListResponse { if t == nil { return nil } return &types.DescribeTaskListResponse{ Pollers: ToPollerInfoArray(t.Pollers), TaskListStatus: ToTaskListStatus(t.TaskListStatus), PartitionConfig: ToAPITaskListPartitionConfig(t.PartitionConfig), TaskList: ToTaskList(t.TaskList), } } func FromDescribeWorkflowExecutionRequest(t *types.DescribeWorkflowExecutionRequest) *apiv1.DescribeWorkflowExecutionRequest { if t == nil { return nil } return &apiv1.DescribeWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), QueryConsistencyLevel: FromQueryConsistencyLevel(t.QueryConsistencyLevel), } } func ToDescribeWorkflowExecutionRequest(t *apiv1.DescribeWorkflowExecutionRequest) *types.DescribeWorkflowExecutionRequest { if t == nil { return nil } return &types.DescribeWorkflowExecutionRequest{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), QueryConsistencyLevel: ToQueryConsistencyLevel(t.QueryConsistencyLevel), } } func FromDescribeWorkflowExecutionResponse(t *types.DescribeWorkflowExecutionResponse) *apiv1.DescribeWorkflowExecutionResponse { if t == nil { return nil } return &apiv1.DescribeWorkflowExecutionResponse{ ExecutionConfiguration: FromWorkflowExecutionConfiguration(t.ExecutionConfiguration), WorkflowExecutionInfo: FromWorkflowExecutionInfo(t.WorkflowExecutionInfo), PendingActivities: FromPendingActivityInfoArray(t.PendingActivities), PendingChildren: FromPendingChildExecutionInfoArray(t.PendingChildren), PendingDecision: FromPendingDecisionInfo(t.PendingDecision), } } func ToDescribeWorkflowExecutionResponse(t *apiv1.DescribeWorkflowExecutionResponse) *types.DescribeWorkflowExecutionResponse { if t == nil { return nil } return &types.DescribeWorkflowExecutionResponse{ ExecutionConfiguration: ToWorkflowExecutionConfiguration(t.ExecutionConfiguration), WorkflowExecutionInfo: ToWorkflowExecutionInfo(t.WorkflowExecutionInfo), PendingActivities: ToPendingActivityInfoArray(t.PendingActivities), PendingChildren: ToPendingChildExecutionInfoArray(t.PendingChildren), PendingDecision: ToPendingDecisionInfo(t.PendingDecision), } } func FromDiagnoseWorkflowExecutionRequest(t *types.DiagnoseWorkflowExecutionRequest) *apiv1.DiagnoseWorkflowExecutionRequest { if t == nil { return nil } return &apiv1.DiagnoseWorkflowExecutionRequest{ Domain: t.GetDomain(), WorkflowExecution: FromWorkflowExecution(t.GetWorkflowExecution()), Identity: t.GetIdentity(), } } func ToDiagnoseWorkflowExecutionRequest(t *apiv1.DiagnoseWorkflowExecutionRequest) *types.DiagnoseWorkflowExecutionRequest { if t == nil { return nil } return &types.DiagnoseWorkflowExecutionRequest{ Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.GetWorkflowExecution()), Identity: t.GetIdentity(), } } func FromDiagnoseWorkflowExecutionResponse(t *types.DiagnoseWorkflowExecutionResponse) *apiv1.DiagnoseWorkflowExecutionResponse { if t == nil { return nil } return &apiv1.DiagnoseWorkflowExecutionResponse{ Domain: t.GetDomain(), DiagnosticWorkflowExecution: FromWorkflowExecution(t.GetDiagnosticWorkflowExecution()), } } func ToDiagnoseWorkflowExecutionResponse(t *apiv1.DiagnoseWorkflowExecutionResponse) *types.DiagnoseWorkflowExecutionResponse { if t == nil { return nil } return &types.DiagnoseWorkflowExecutionResponse{ Domain: t.GetDomain(), DiagnosticWorkflowExecution: ToWorkflowExecution(t.GetDiagnosticWorkflowExecution()), } } func FromDomainStatus(t *types.DomainStatus) apiv1.DomainStatus { if t == nil { return apiv1.DomainStatus_DOMAIN_STATUS_INVALID } switch *t { case types.DomainStatusRegistered: return apiv1.DomainStatus_DOMAIN_STATUS_REGISTERED case types.DomainStatusDeprecated: return apiv1.DomainStatus_DOMAIN_STATUS_DEPRECATED case types.DomainStatusDeleted: return apiv1.DomainStatus_DOMAIN_STATUS_DELETED } return apiv1.DomainStatus_DOMAIN_STATUS_INVALID } func ToDomainStatus(t apiv1.DomainStatus) *types.DomainStatus { switch t { case apiv1.DomainStatus_DOMAIN_STATUS_INVALID: return nil case apiv1.DomainStatus_DOMAIN_STATUS_REGISTERED: return types.DomainStatusRegistered.Ptr() case apiv1.DomainStatus_DOMAIN_STATUS_DEPRECATED: return types.DomainStatusDeprecated.Ptr() case apiv1.DomainStatus_DOMAIN_STATUS_DELETED: return types.DomainStatusDeleted.Ptr() } return nil } func FromEncodingType(t *types.EncodingType) apiv1.EncodingType { if t == nil { return apiv1.EncodingType_ENCODING_TYPE_INVALID } switch *t { case types.EncodingTypeThriftRW: return apiv1.EncodingType_ENCODING_TYPE_THRIFTRW case types.EncodingTypeJSON: return apiv1.EncodingType_ENCODING_TYPE_JSON } return apiv1.EncodingType_ENCODING_TYPE_INVALID } func ToEncodingType(t apiv1.EncodingType) *types.EncodingType { switch t { case apiv1.EncodingType_ENCODING_TYPE_INVALID: return nil case apiv1.EncodingType_ENCODING_TYPE_THRIFTRW: return types.EncodingTypeThriftRW.Ptr() case apiv1.EncodingType_ENCODING_TYPE_JSON: return types.EncodingTypeJSON.Ptr() case apiv1.EncodingType_ENCODING_TYPE_PROTO3: return nil } return nil } func FromEventFilterType(t *types.HistoryEventFilterType) apiv1.EventFilterType { if t == nil { return apiv1.EventFilterType_EVENT_FILTER_TYPE_INVALID } switch *t { case types.HistoryEventFilterTypeAllEvent: return apiv1.EventFilterType_EVENT_FILTER_TYPE_ALL_EVENT case types.HistoryEventFilterTypeCloseEvent: return apiv1.EventFilterType_EVENT_FILTER_TYPE_CLOSE_EVENT } return apiv1.EventFilterType_EVENT_FILTER_TYPE_INVALID } func ToEventFilterType(t apiv1.EventFilterType) *types.HistoryEventFilterType { switch t { case apiv1.EventFilterType_EVENT_FILTER_TYPE_INVALID: return nil case apiv1.EventFilterType_EVENT_FILTER_TYPE_ALL_EVENT: return types.HistoryEventFilterTypeAllEvent.Ptr() case apiv1.EventFilterType_EVENT_FILTER_TYPE_CLOSE_EVENT: return types.HistoryEventFilterTypeCloseEvent.Ptr() } return nil } func FromExternalWorkflowExecutionCancelRequestedEventAttributes(t *types.ExternalWorkflowExecutionCancelRequestedEventAttributes) *apiv1.ExternalWorkflowExecutionCancelRequestedEventAttributes { if t == nil { return nil } return &apiv1.ExternalWorkflowExecutionCancelRequestedEventAttributes{ InitiatedEventId: t.InitiatedEventID, Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), } } func ToExternalWorkflowExecutionCancelRequestedEventAttributes(t *apiv1.ExternalWorkflowExecutionCancelRequestedEventAttributes) *types.ExternalWorkflowExecutionCancelRequestedEventAttributes { if t == nil { return nil } return &types.ExternalWorkflowExecutionCancelRequestedEventAttributes{ InitiatedEventID: t.InitiatedEventId, Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), } } func FromExternalWorkflowExecutionSignaledEventAttributes(t *types.ExternalWorkflowExecutionSignaledEventAttributes) *apiv1.ExternalWorkflowExecutionSignaledEventAttributes { if t == nil { return nil } return &apiv1.ExternalWorkflowExecutionSignaledEventAttributes{ InitiatedEventId: t.InitiatedEventID, Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Control: t.Control, } } func ToExternalWorkflowExecutionSignaledEventAttributes(t *apiv1.ExternalWorkflowExecutionSignaledEventAttributes) *types.ExternalWorkflowExecutionSignaledEventAttributes { if t == nil { return nil } return &types.ExternalWorkflowExecutionSignaledEventAttributes{ InitiatedEventID: t.InitiatedEventId, Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Control: t.Control, } } func FromFailWorkflowExecutionDecisionAttributes(t *types.FailWorkflowExecutionDecisionAttributes) *apiv1.FailWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &apiv1.FailWorkflowExecutionDecisionAttributes{ Failure: FromFailure(t.Reason, t.Details), } } func ToFailWorkflowExecutionDecisionAttributes(t *apiv1.FailWorkflowExecutionDecisionAttributes) *types.FailWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.FailWorkflowExecutionDecisionAttributes{ Reason: ToFailureReason(t.Failure), Details: ToFailureDetails(t.Failure), } } func FromGetClusterInfoResponse(t *types.ClusterInfo) *apiv1.GetClusterInfoResponse { if t == nil { return nil } return &apiv1.GetClusterInfoResponse{ SupportedClientVersions: FromSupportedClientVersions(t.SupportedClientVersions), } } func ToGetClusterInfoResponse(t *apiv1.GetClusterInfoResponse) *types.ClusterInfo { if t == nil { return nil } return &types.ClusterInfo{ SupportedClientVersions: ToSupportedClientVersions(t.SupportedClientVersions), } } func FromGetSearchAttributesResponse(t *types.GetSearchAttributesResponse) *apiv1.GetSearchAttributesResponse { if t == nil { return nil } return &apiv1.GetSearchAttributesResponse{ Keys: FromIndexedValueTypeMap(t.Keys), } } func ToGetSearchAttributesResponse(t *apiv1.GetSearchAttributesResponse) *types.GetSearchAttributesResponse { if t == nil { return nil } return &types.GetSearchAttributesResponse{ Keys: ToIndexedValueTypeMap(t.Keys), } } func FromGetWorkflowExecutionHistoryRequest(t *types.GetWorkflowExecutionHistoryRequest) *apiv1.GetWorkflowExecutionHistoryRequest { if t == nil { return nil } return &apiv1.GetWorkflowExecutionHistoryRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), PageSize: t.MaximumPageSize, NextPageToken: t.NextPageToken, WaitForNewEvent: t.WaitForNewEvent, HistoryEventFilterType: FromEventFilterType(t.HistoryEventFilterType), SkipArchival: t.SkipArchival, QueryConsistencyLevel: FromQueryConsistencyLevel(t.QueryConsistencyLevel), } } func ToGetWorkflowExecutionHistoryRequest(t *apiv1.GetWorkflowExecutionHistoryRequest) *types.GetWorkflowExecutionHistoryRequest { if t == nil { return nil } return &types.GetWorkflowExecutionHistoryRequest{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), MaximumPageSize: t.PageSize, NextPageToken: t.NextPageToken, WaitForNewEvent: t.WaitForNewEvent, HistoryEventFilterType: ToEventFilterType(t.HistoryEventFilterType), SkipArchival: t.SkipArchival, QueryConsistencyLevel: ToQueryConsistencyLevel(t.QueryConsistencyLevel), } } func FromGetWorkflowExecutionHistoryResponse(t *types.GetWorkflowExecutionHistoryResponse) *apiv1.GetWorkflowExecutionHistoryResponse { if t == nil { return nil } return &apiv1.GetWorkflowExecutionHistoryResponse{ History: FromHistory(t.History), RawHistory: FromDataBlobArray(t.RawHistory), NextPageToken: t.NextPageToken, Archived: t.Archived, } } func ToGetWorkflowExecutionHistoryResponse(t *apiv1.GetWorkflowExecutionHistoryResponse) *types.GetWorkflowExecutionHistoryResponse { if t == nil { return nil } return &types.GetWorkflowExecutionHistoryResponse{ History: ToHistory(t.History), RawHistory: ToDataBlobArray(t.RawHistory), NextPageToken: t.NextPageToken, Archived: t.Archived, } } func FromHeader(t *types.Header) *apiv1.Header { if t == nil { return nil } return &apiv1.Header{ Fields: FromPayloadMap(t.Fields), } } func ToHeader(t *apiv1.Header) *types.Header { if t == nil { return nil } return &types.Header{ Fields: ToPayloadMap(t.Fields), } } func FromHealthResponse(t *types.HealthStatus) *apiv1.HealthResponse { if t == nil { return nil } return &apiv1.HealthResponse{ Ok: t.Ok, Message: t.Msg, } } func ToHealthResponse(t *apiv1.HealthResponse) *types.HealthStatus { if t == nil { return nil } return &types.HealthStatus{ Ok: t.Ok, Msg: t.Message, } } func FromHistory(t *types.History) *apiv1.History { if t == nil { return nil } return &apiv1.History{ Events: FromHistoryEventArray(t.Events), } } func ToHistory(t *apiv1.History) *types.History { if t == nil { return nil } return &types.History{ Events: ToHistoryEventArray(t.Events), } } func FromIndexedValueType(t types.IndexedValueType) apiv1.IndexedValueType { switch t { case types.IndexedValueTypeString: return apiv1.IndexedValueType_INDEXED_VALUE_TYPE_STRING case types.IndexedValueTypeKeyword: return apiv1.IndexedValueType_INDEXED_VALUE_TYPE_KEYWORD case types.IndexedValueTypeInt: return apiv1.IndexedValueType_INDEXED_VALUE_TYPE_INT case types.IndexedValueTypeDouble: return apiv1.IndexedValueType_INDEXED_VALUE_TYPE_DOUBLE case types.IndexedValueTypeBool: return apiv1.IndexedValueType_INDEXED_VALUE_TYPE_BOOL case types.IndexedValueTypeDatetime: return apiv1.IndexedValueType_INDEXED_VALUE_TYPE_DATETIME } return apiv1.IndexedValueType_INDEXED_VALUE_TYPE_INVALID } func ToIndexedValueType(t apiv1.IndexedValueType) types.IndexedValueType { switch t { case apiv1.IndexedValueType_INDEXED_VALUE_TYPE_INVALID: return types.IndexedValueTypeString case apiv1.IndexedValueType_INDEXED_VALUE_TYPE_STRING: return types.IndexedValueTypeString case apiv1.IndexedValueType_INDEXED_VALUE_TYPE_KEYWORD: return types.IndexedValueTypeKeyword case apiv1.IndexedValueType_INDEXED_VALUE_TYPE_INT: return types.IndexedValueTypeInt case apiv1.IndexedValueType_INDEXED_VALUE_TYPE_DOUBLE: return types.IndexedValueTypeDouble case apiv1.IndexedValueType_INDEXED_VALUE_TYPE_BOOL: return types.IndexedValueTypeBool case apiv1.IndexedValueType_INDEXED_VALUE_TYPE_DATETIME: return types.IndexedValueTypeDatetime } return types.IndexedValueTypeString } func FromListArchivedWorkflowExecutionsRequest(t *types.ListArchivedWorkflowExecutionsRequest) *apiv1.ListArchivedWorkflowExecutionsRequest { if t == nil { return nil } return &apiv1.ListArchivedWorkflowExecutionsRequest{ Domain: t.Domain, PageSize: t.PageSize, NextPageToken: t.NextPageToken, Query: t.Query, } } func ToListArchivedWorkflowExecutionsRequest(t *apiv1.ListArchivedWorkflowExecutionsRequest) *types.ListArchivedWorkflowExecutionsRequest { if t == nil { return nil } return &types.ListArchivedWorkflowExecutionsRequest{ Domain: t.Domain, PageSize: t.PageSize, NextPageToken: t.NextPageToken, Query: t.Query, } } func FromListArchivedWorkflowExecutionsResponse(t *types.ListArchivedWorkflowExecutionsResponse) *apiv1.ListArchivedWorkflowExecutionsResponse { if t == nil { return nil } return &apiv1.ListArchivedWorkflowExecutionsResponse{ Executions: FromWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } func ToListArchivedWorkflowExecutionsResponse(t *apiv1.ListArchivedWorkflowExecutionsResponse) *types.ListArchivedWorkflowExecutionsResponse { if t == nil { return nil } return &types.ListArchivedWorkflowExecutionsResponse{ Executions: ToWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } func FromListClosedWorkflowExecutionsResponse(t *types.ListClosedWorkflowExecutionsResponse) *apiv1.ListClosedWorkflowExecutionsResponse { if t == nil { return nil } return &apiv1.ListClosedWorkflowExecutionsResponse{ Executions: FromWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } func ToListClosedWorkflowExecutionsResponse(t *apiv1.ListClosedWorkflowExecutionsResponse) *types.ListClosedWorkflowExecutionsResponse { if t == nil { return nil } return &types.ListClosedWorkflowExecutionsResponse{ Executions: ToWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } func FromListDomainsRequest(t *types.ListDomainsRequest) *apiv1.ListDomainsRequest { if t == nil { return nil } return &apiv1.ListDomainsRequest{ PageSize: t.PageSize, NextPageToken: t.NextPageToken, } } func ToListDomainsRequest(t *apiv1.ListDomainsRequest) *types.ListDomainsRequest { if t == nil { return nil } return &types.ListDomainsRequest{ PageSize: t.PageSize, NextPageToken: t.NextPageToken, } } func FromListDomainsResponse(t *types.ListDomainsResponse) *apiv1.ListDomainsResponse { if t == nil { return nil } return &apiv1.ListDomainsResponse{ Domains: FromDescribeDomainResponseArray(t.Domains), NextPageToken: t.NextPageToken, } } func ToListDomainsResponse(t *apiv1.ListDomainsResponse) *types.ListDomainsResponse { if t == nil { return nil } return &types.ListDomainsResponse{ Domains: ToDescribeDomainResponseArray(t.Domains), NextPageToken: t.NextPageToken, } } func FromListOpenWorkflowExecutionsResponse(t *types.ListOpenWorkflowExecutionsResponse) *apiv1.ListOpenWorkflowExecutionsResponse { if t == nil { return nil } return &apiv1.ListOpenWorkflowExecutionsResponse{ Executions: FromWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } func ToListOpenWorkflowExecutionsResponse(t *apiv1.ListOpenWorkflowExecutionsResponse) *types.ListOpenWorkflowExecutionsResponse { if t == nil { return nil } return &types.ListOpenWorkflowExecutionsResponse{ Executions: ToWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } func FromListTaskListPartitionsRequest(t *types.ListTaskListPartitionsRequest) *apiv1.ListTaskListPartitionsRequest { if t == nil { return nil } return &apiv1.ListTaskListPartitionsRequest{ Domain: t.Domain, TaskList: FromTaskList(t.TaskList), } } func ToListTaskListPartitionsRequest(t *apiv1.ListTaskListPartitionsRequest) *types.ListTaskListPartitionsRequest { if t == nil { return nil } return &types.ListTaskListPartitionsRequest{ Domain: t.Domain, TaskList: ToTaskList(t.TaskList), } } func FromListTaskListPartitionsResponse(t *types.ListTaskListPartitionsResponse) *apiv1.ListTaskListPartitionsResponse { if t == nil { return nil } return &apiv1.ListTaskListPartitionsResponse{ ActivityTaskListPartitions: FromTaskListPartitionMetadataArray(t.ActivityTaskListPartitions), DecisionTaskListPartitions: FromTaskListPartitionMetadataArray(t.DecisionTaskListPartitions), } } func ToListTaskListPartitionsResponse(t *apiv1.ListTaskListPartitionsResponse) *types.ListTaskListPartitionsResponse { if t == nil { return nil } return &types.ListTaskListPartitionsResponse{ ActivityTaskListPartitions: ToTaskListPartitionMetadataArray(t.ActivityTaskListPartitions), DecisionTaskListPartitions: ToTaskListPartitionMetadataArray(t.DecisionTaskListPartitions), } } func FromGetTaskListsByDomainRequest(t *types.GetTaskListsByDomainRequest) *apiv1.GetTaskListsByDomainRequest { if t == nil { return nil } return &apiv1.GetTaskListsByDomainRequest{ Domain: t.Domain, } } func ToGetTaskListsByDomainRequest(t *apiv1.GetTaskListsByDomainRequest) *types.GetTaskListsByDomainRequest { if t == nil { return nil } return &types.GetTaskListsByDomainRequest{ Domain: t.Domain, } } func FromGetTaskListsByDomainResponse(t *types.GetTaskListsByDomainResponse) *apiv1.GetTaskListsByDomainResponse { if t == nil { return nil } return &apiv1.GetTaskListsByDomainResponse{ DecisionTaskListMap: FromDescribeTaskListResponseMap(t.GetDecisionTaskListMap()), ActivityTaskListMap: FromDescribeTaskListResponseMap(t.GetActivityTaskListMap()), } } func ToGetTaskListsByDomainResponse(t *apiv1.GetTaskListsByDomainResponse) *types.GetTaskListsByDomainResponse { if t == nil { return nil } return &types.GetTaskListsByDomainResponse{ DecisionTaskListMap: ToDescribeTaskListResponseMap(t.GetDecisionTaskListMap()), ActivityTaskListMap: ToDescribeTaskListResponseMap(t.GetActivityTaskListMap()), } } func FromDescribeTaskListResponseMap(t map[string]*types.DescribeTaskListResponse) map[string]*apiv1.DescribeTaskListResponse { if t == nil { return nil } taskListMap := make(map[string]*apiv1.DescribeTaskListResponse, len(t)) for key, value := range t { taskListMap[key] = FromDescribeTaskListResponse(value) } return taskListMap } func ToDescribeTaskListResponseMap(t map[string]*apiv1.DescribeTaskListResponse) map[string]*types.DescribeTaskListResponse { if t == nil { return nil } taskListMap := make(map[string]*types.DescribeTaskListResponse, len(t)) for key, value := range t { taskListMap[key] = ToDescribeTaskListResponse(value) } return taskListMap } func FromListWorkflowExecutionsRequest(t *types.ListWorkflowExecutionsRequest) *apiv1.ListWorkflowExecutionsRequest { if t == nil { return nil } return &apiv1.ListWorkflowExecutionsRequest{ Domain: t.Domain, PageSize: t.PageSize, NextPageToken: t.NextPageToken, Query: t.Query, } } func ToListWorkflowExecutionsRequest(t *apiv1.ListWorkflowExecutionsRequest) *types.ListWorkflowExecutionsRequest { if t == nil { return nil } return &types.ListWorkflowExecutionsRequest{ Domain: t.Domain, PageSize: t.PageSize, NextPageToken: t.NextPageToken, Query: t.Query, } } func FromListWorkflowExecutionsResponse(t *types.ListWorkflowExecutionsResponse) *apiv1.ListWorkflowExecutionsResponse { if t == nil { return nil } return &apiv1.ListWorkflowExecutionsResponse{ Executions: FromWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } func ToListWorkflowExecutionsResponse(t *apiv1.ListWorkflowExecutionsResponse) *types.ListWorkflowExecutionsResponse { if t == nil { return nil } return &types.ListWorkflowExecutionsResponse{ Executions: ToWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } func FromMarkerRecordedEventAttributes(t *types.MarkerRecordedEventAttributes) *apiv1.MarkerRecordedEventAttributes { if t == nil { return nil } return &apiv1.MarkerRecordedEventAttributes{ MarkerName: t.MarkerName, Details: FromPayload(t.Details), DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, Header: FromHeader(t.Header), } } func ToMarkerRecordedEventAttributes(t *apiv1.MarkerRecordedEventAttributes) *types.MarkerRecordedEventAttributes { if t == nil { return nil } return &types.MarkerRecordedEventAttributes{ MarkerName: t.MarkerName, Details: ToPayload(t.Details), DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, Header: ToHeader(t.Header), } } func FromMemo(t *types.Memo) *apiv1.Memo { if t == nil { return nil } return &apiv1.Memo{ Fields: FromPayloadMap(t.Fields), } } func ToMemo(t *apiv1.Memo) *types.Memo { if t == nil { return nil } return &types.Memo{ Fields: ToPayloadMap(t.Fields), } } func FromParentClosePolicy(t *types.ParentClosePolicy) apiv1.ParentClosePolicy { if t == nil { return apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_INVALID } switch *t { case types.ParentClosePolicyAbandon: return apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_ABANDON case types.ParentClosePolicyRequestCancel: return apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_REQUEST_CANCEL case types.ParentClosePolicyTerminate: return apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_TERMINATE } return apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_INVALID } func ToParentClosePolicy(t apiv1.ParentClosePolicy) *types.ParentClosePolicy { switch t { case apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_INVALID: return nil case apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_ABANDON: return types.ParentClosePolicyAbandon.Ptr() case apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_REQUEST_CANCEL: return types.ParentClosePolicyRequestCancel.Ptr() case apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_TERMINATE: return types.ParentClosePolicyTerminate.Ptr() } return nil } func FromPendingActivityInfo(t *types.PendingActivityInfo) *apiv1.PendingActivityInfo { if t == nil { return nil } return &apiv1.PendingActivityInfo{ ActivityId: t.ActivityID, ActivityType: FromActivityType(t.ActivityType), State: FromPendingActivityState(t.State), HeartbeatDetails: FromPayload(t.HeartbeatDetails), LastHeartbeatTime: unixNanoToTime(t.LastHeartbeatTimestamp), LastStartedTime: unixNanoToTime(t.LastStartedTimestamp), Attempt: t.Attempt, MaximumAttempts: t.MaximumAttempts, ScheduledTime: unixNanoToTime(t.ScheduledTimestamp), ExpirationTime: unixNanoToTime(t.ExpirationTimestamp), LastFailure: FromFailure(t.LastFailureReason, t.LastFailureDetails), LastWorkerIdentity: t.LastWorkerIdentity, StartedWorkerIdentity: t.StartedWorkerIdentity, ScheduleId: t.ScheduleID, } } func ToPendingActivityInfo(t *apiv1.PendingActivityInfo) *types.PendingActivityInfo { if t == nil { return nil } return &types.PendingActivityInfo{ ActivityID: t.ActivityId, ActivityType: ToActivityType(t.ActivityType), State: ToPendingActivityState(t.State), HeartbeatDetails: ToPayload(t.HeartbeatDetails), LastHeartbeatTimestamp: timeToUnixNano(t.LastHeartbeatTime), LastStartedTimestamp: timeToUnixNano(t.LastStartedTime), Attempt: t.Attempt, MaximumAttempts: t.MaximumAttempts, ScheduledTimestamp: timeToUnixNano(t.ScheduledTime), ExpirationTimestamp: timeToUnixNano(t.ExpirationTime), LastFailureReason: ToFailureReason(t.LastFailure), LastFailureDetails: ToFailureDetails(t.LastFailure), LastWorkerIdentity: t.LastWorkerIdentity, StartedWorkerIdentity: t.StartedWorkerIdentity, ScheduleID: t.ScheduleId, } } func FromPendingActivityState(t *types.PendingActivityState) apiv1.PendingActivityState { if t == nil { return apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_INVALID } switch *t { case types.PendingActivityStateScheduled: return apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_SCHEDULED case types.PendingActivityStateStarted: return apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_STARTED case types.PendingActivityStateCancelRequested: return apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_CANCEL_REQUESTED } return apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_INVALID } func ToPendingActivityState(t apiv1.PendingActivityState) *types.PendingActivityState { switch t { case apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_INVALID: return nil case apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_SCHEDULED: return types.PendingActivityStateScheduled.Ptr() case apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_STARTED: return types.PendingActivityStateStarted.Ptr() case apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_CANCEL_REQUESTED: return types.PendingActivityStateCancelRequested.Ptr() } return nil } func FromPendingChildExecutionInfo(t *types.PendingChildExecutionInfo) *apiv1.PendingChildExecutionInfo { if t == nil { return nil } return &apiv1.PendingChildExecutionInfo{ Domain: t.Domain, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), WorkflowTypeName: t.WorkflowTypeName, InitiatedId: t.InitiatedID, ParentClosePolicy: FromParentClosePolicy(t.ParentClosePolicy), } } func ToPendingChildExecutionInfo(t *apiv1.PendingChildExecutionInfo) *types.PendingChildExecutionInfo { if t == nil { return nil } return &types.PendingChildExecutionInfo{ Domain: t.Domain, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), WorkflowTypeName: t.WorkflowTypeName, InitiatedID: t.InitiatedId, ParentClosePolicy: ToParentClosePolicy(t.ParentClosePolicy), } } func FromPendingDecisionInfo(t *types.PendingDecisionInfo) *apiv1.PendingDecisionInfo { if t == nil { return nil } return &apiv1.PendingDecisionInfo{ State: FromPendingDecisionState(t.State), ScheduledTime: unixNanoToTime(t.ScheduledTimestamp), StartedTime: unixNanoToTime(t.StartedTimestamp), Attempt: int32(t.Attempt), OriginalScheduledTime: unixNanoToTime(t.OriginalScheduledTimestamp), ScheduleId: t.ScheduleID, } } func ToPendingDecisionInfo(t *apiv1.PendingDecisionInfo) *types.PendingDecisionInfo { if t == nil { return nil } return &types.PendingDecisionInfo{ State: ToPendingDecisionState(t.State), ScheduledTimestamp: timeToUnixNano(t.ScheduledTime), StartedTimestamp: timeToUnixNano(t.StartedTime), Attempt: int64(t.Attempt), OriginalScheduledTimestamp: timeToUnixNano(t.OriginalScheduledTime), ScheduleID: t.ScheduleId, } } func FromPendingDecisionState(t *types.PendingDecisionState) apiv1.PendingDecisionState { if t == nil { return apiv1.PendingDecisionState_PENDING_DECISION_STATE_INVALID } switch *t { case types.PendingDecisionStateScheduled: return apiv1.PendingDecisionState_PENDING_DECISION_STATE_SCHEDULED case types.PendingDecisionStateStarted: return apiv1.PendingDecisionState_PENDING_DECISION_STATE_STARTED } return apiv1.PendingDecisionState_PENDING_DECISION_STATE_INVALID } func ToPendingDecisionState(t apiv1.PendingDecisionState) *types.PendingDecisionState { switch t { case apiv1.PendingDecisionState_PENDING_DECISION_STATE_INVALID: return nil case apiv1.PendingDecisionState_PENDING_DECISION_STATE_SCHEDULED: return types.PendingDecisionStateScheduled.Ptr() case apiv1.PendingDecisionState_PENDING_DECISION_STATE_STARTED: return types.PendingDecisionStateStarted.Ptr() } return nil } func FromPollForActivityTaskRequest(t *types.PollForActivityTaskRequest) *apiv1.PollForActivityTaskRequest { if t == nil { return nil } return &apiv1.PollForActivityTaskRequest{ Domain: t.Domain, TaskList: FromTaskList(t.TaskList), Identity: t.Identity, TaskListMetadata: FromTaskListMetadata(t.TaskListMetadata), } } func ToPollForActivityTaskRequest(t *apiv1.PollForActivityTaskRequest) *types.PollForActivityTaskRequest { if t == nil { return nil } return &types.PollForActivityTaskRequest{ Domain: t.Domain, TaskList: ToTaskList(t.TaskList), Identity: t.Identity, TaskListMetadata: ToTaskListMetadata(t.TaskListMetadata), } } func FromPollForActivityTaskResponse(t *types.PollForActivityTaskResponse) *apiv1.PollForActivityTaskResponse { if t == nil { return nil } return &apiv1.PollForActivityTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), ActivityId: t.ActivityID, ActivityType: FromActivityType(t.ActivityType), Input: FromPayload(t.Input), ScheduledTime: unixNanoToTime(t.ScheduledTimestamp), StartedTime: unixNanoToTime(t.StartedTimestamp), ScheduleToCloseTimeout: secondsToDuration(t.ScheduleToCloseTimeoutSeconds), StartToCloseTimeout: secondsToDuration(t.StartToCloseTimeoutSeconds), HeartbeatTimeout: secondsToDuration(t.HeartbeatTimeoutSeconds), Attempt: t.Attempt, ScheduledTimeOfThisAttempt: unixNanoToTime(t.ScheduledTimestampOfThisAttempt), HeartbeatDetails: FromPayload(t.HeartbeatDetails), WorkflowType: FromWorkflowType(t.WorkflowType), WorkflowDomain: t.WorkflowDomain, Header: FromHeader(t.Header), AutoConfigHint: FromAutoConfigHint(t.AutoConfigHint), } } func ToPollForActivityTaskResponse(t *apiv1.PollForActivityTaskResponse) *types.PollForActivityTaskResponse { if t == nil { return nil } return &types.PollForActivityTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), ActivityID: t.ActivityId, ActivityType: ToActivityType(t.ActivityType), Input: ToPayload(t.Input), ScheduledTimestamp: timeToUnixNano(t.ScheduledTime), StartedTimestamp: timeToUnixNano(t.StartedTime), ScheduleToCloseTimeoutSeconds: durationToSeconds(t.ScheduleToCloseTimeout), StartToCloseTimeoutSeconds: durationToSeconds(t.StartToCloseTimeout), HeartbeatTimeoutSeconds: durationToSeconds(t.HeartbeatTimeout), Attempt: t.Attempt, ScheduledTimestampOfThisAttempt: timeToUnixNano(t.ScheduledTimeOfThisAttempt), HeartbeatDetails: ToPayload(t.HeartbeatDetails), WorkflowType: ToWorkflowType(t.WorkflowType), WorkflowDomain: t.WorkflowDomain, Header: ToHeader(t.Header), AutoConfigHint: ToAutoConfigHint(t.AutoConfigHint), } } func FromPollForDecisionTaskRequest(t *types.PollForDecisionTaskRequest) *apiv1.PollForDecisionTaskRequest { if t == nil { return nil } return &apiv1.PollForDecisionTaskRequest{ Domain: t.Domain, TaskList: FromTaskList(t.TaskList), Identity: t.Identity, BinaryChecksum: t.BinaryChecksum, } } func ToPollForDecisionTaskRequest(t *apiv1.PollForDecisionTaskRequest) *types.PollForDecisionTaskRequest { if t == nil { return nil } return &types.PollForDecisionTaskRequest{ Domain: t.Domain, TaskList: ToTaskList(t.TaskList), Identity: t.Identity, BinaryChecksum: t.BinaryChecksum, } } func FromPollForDecisionTaskResponse(t *types.PollForDecisionTaskResponse) *apiv1.PollForDecisionTaskResponse { if t == nil { return nil } return &apiv1.PollForDecisionTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), PreviousStartedEventId: fromInt64Value(t.PreviousStartedEventID), StartedEventId: t.StartedEventID, Attempt: t.Attempt, BacklogCountHint: t.BacklogCountHint, History: FromHistory(t.History), NextPageToken: t.NextPageToken, Query: FromWorkflowQuery(t.Query), WorkflowExecutionTaskList: FromTaskList(t.WorkflowExecutionTaskList), ScheduledTime: unixNanoToTime(t.ScheduledTimestamp), StartedTime: unixNanoToTime(t.StartedTimestamp), Queries: FromWorkflowQueryMap(t.Queries), NextEventId: t.NextEventID, TotalHistoryBytes: t.TotalHistoryBytes, AutoConfigHint: FromAutoConfigHint(t.AutoConfigHint), } } func ToPollForDecisionTaskResponse(t *apiv1.PollForDecisionTaskResponse) *types.PollForDecisionTaskResponse { if t == nil { return nil } return &types.PollForDecisionTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), PreviousStartedEventID: toInt64Value(t.PreviousStartedEventId), StartedEventID: t.StartedEventId, Attempt: t.Attempt, BacklogCountHint: t.BacklogCountHint, History: ToHistory(t.History), NextPageToken: t.NextPageToken, Query: ToWorkflowQuery(t.Query), WorkflowExecutionTaskList: ToTaskList(t.WorkflowExecutionTaskList), ScheduledTimestamp: timeToUnixNano(t.ScheduledTime), StartedTimestamp: timeToUnixNano(t.StartedTime), Queries: ToWorkflowQueryMap(t.Queries), NextEventID: t.NextEventId, TotalHistoryBytes: t.TotalHistoryBytes, AutoConfigHint: ToAutoConfigHint(t.AutoConfigHint), } } func FromPollerInfo(t *types.PollerInfo) *apiv1.PollerInfo { if t == nil { return nil } return &apiv1.PollerInfo{ LastAccessTime: unixNanoToTime(t.LastAccessTime), Identity: t.Identity, RatePerSecond: t.RatePerSecond, } } func ToPollerInfo(t *apiv1.PollerInfo) *types.PollerInfo { if t == nil { return nil } return &types.PollerInfo{ LastAccessTime: timeToUnixNano(t.LastAccessTime), Identity: t.Identity, RatePerSecond: t.RatePerSecond, } } func FromQueryConsistencyLevel(t *types.QueryConsistencyLevel) apiv1.QueryConsistencyLevel { if t == nil { return apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_INVALID } switch *t { case types.QueryConsistencyLevelEventual: return apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_EVENTUAL case types.QueryConsistencyLevelStrong: return apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_STRONG } return apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_INVALID } func ToQueryConsistencyLevel(t apiv1.QueryConsistencyLevel) *types.QueryConsistencyLevel { switch t { case apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_INVALID: return nil case apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_EVENTUAL: return types.QueryConsistencyLevelEventual.Ptr() case apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_STRONG: return types.QueryConsistencyLevelStrong.Ptr() } return nil } func FromQueryRejectCondition(t *types.QueryRejectCondition) apiv1.QueryRejectCondition { if t == nil { return apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_INVALID } switch *t { case types.QueryRejectConditionNotOpen: return apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_NOT_OPEN case types.QueryRejectConditionNotCompletedCleanly: return apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_NOT_COMPLETED_CLEANLY } return apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_INVALID } func ToQueryRejectCondition(t apiv1.QueryRejectCondition) *types.QueryRejectCondition { switch t { case apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_INVALID: return nil case apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_NOT_OPEN: return types.QueryRejectConditionNotOpen.Ptr() case apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_NOT_COMPLETED_CLEANLY: return types.QueryRejectConditionNotCompletedCleanly.Ptr() } return nil } func FromQueryRejected(t *types.QueryRejected) *apiv1.QueryRejected { if t == nil { return nil } return &apiv1.QueryRejected{ CloseStatus: FromWorkflowExecutionCloseStatus(t.CloseStatus), } } func ToQueryRejected(t *apiv1.QueryRejected) *types.QueryRejected { if t == nil { return nil } return &types.QueryRejected{ CloseStatus: ToWorkflowExecutionCloseStatus(t.CloseStatus), } } func FromQueryResultType(t *types.QueryResultType) apiv1.QueryResultType { if t == nil { return apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID } switch *t { case types.QueryResultTypeAnswered: return apiv1.QueryResultType_QUERY_RESULT_TYPE_ANSWERED case types.QueryResultTypeFailed: return apiv1.QueryResultType_QUERY_RESULT_TYPE_FAILED } return apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID } func ToQueryResultType(t apiv1.QueryResultType) *types.QueryResultType { switch t { case apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID: return nil case apiv1.QueryResultType_QUERY_RESULT_TYPE_ANSWERED: return types.QueryResultTypeAnswered.Ptr() case apiv1.QueryResultType_QUERY_RESULT_TYPE_FAILED: return types.QueryResultTypeFailed.Ptr() } return nil } func FromQueryWorkflowRequest(t *types.QueryWorkflowRequest) *apiv1.QueryWorkflowRequest { if t == nil { return nil } return &apiv1.QueryWorkflowRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), Query: FromWorkflowQuery(t.Query), QueryRejectCondition: FromQueryRejectCondition(t.QueryRejectCondition), QueryConsistencyLevel: FromQueryConsistencyLevel(t.QueryConsistencyLevel), } } func ToQueryWorkflowRequest(t *apiv1.QueryWorkflowRequest) *types.QueryWorkflowRequest { if t == nil { return nil } return &types.QueryWorkflowRequest{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), Query: ToWorkflowQuery(t.Query), QueryRejectCondition: ToQueryRejectCondition(t.QueryRejectCondition), QueryConsistencyLevel: ToQueryConsistencyLevel(t.QueryConsistencyLevel), } } func FromQueryWorkflowResponse(t *types.QueryWorkflowResponse) *apiv1.QueryWorkflowResponse { if t == nil { return nil } return &apiv1.QueryWorkflowResponse{ QueryResult: FromPayload(t.QueryResult), QueryRejected: FromQueryRejected(t.QueryRejected), } } func ToQueryWorkflowResponse(t *apiv1.QueryWorkflowResponse) *types.QueryWorkflowResponse { if t == nil { return nil } return &types.QueryWorkflowResponse{ QueryResult: ToPayload(t.QueryResult), QueryRejected: ToQueryRejected(t.QueryRejected), } } func FromRecordActivityTaskHeartbeatByIDRequest(t *types.RecordActivityTaskHeartbeatByIDRequest) *apiv1.RecordActivityTaskHeartbeatByIDRequest { if t == nil { return nil } return &apiv1.RecordActivityTaskHeartbeatByIDRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), ActivityId: t.ActivityID, Details: FromPayload(t.Details), Identity: t.Identity, } } func ToRecordActivityTaskHeartbeatByIDRequest(t *apiv1.RecordActivityTaskHeartbeatByIDRequest) *types.RecordActivityTaskHeartbeatByIDRequest { if t == nil { return nil } return &types.RecordActivityTaskHeartbeatByIDRequest{ Domain: t.Domain, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), ActivityID: t.ActivityId, Details: ToPayload(t.Details), Identity: t.Identity, } } func FromRecordActivityTaskHeartbeatByIDResponse(t *types.RecordActivityTaskHeartbeatResponse) *apiv1.RecordActivityTaskHeartbeatByIDResponse { if t == nil { return nil } return &apiv1.RecordActivityTaskHeartbeatByIDResponse{ CancelRequested: t.CancelRequested, } } func ToRecordActivityTaskHeartbeatByIDResponse(t *apiv1.RecordActivityTaskHeartbeatByIDResponse) *types.RecordActivityTaskHeartbeatResponse { if t == nil { return nil } return &types.RecordActivityTaskHeartbeatResponse{ CancelRequested: t.CancelRequested, } } func FromRecordActivityTaskHeartbeatRequest(t *types.RecordActivityTaskHeartbeatRequest) *apiv1.RecordActivityTaskHeartbeatRequest { if t == nil { return nil } return &apiv1.RecordActivityTaskHeartbeatRequest{ TaskToken: t.TaskToken, Details: FromPayload(t.Details), Identity: t.Identity, } } func ToRecordActivityTaskHeartbeatRequest(t *apiv1.RecordActivityTaskHeartbeatRequest) *types.RecordActivityTaskHeartbeatRequest { if t == nil { return nil } return &types.RecordActivityTaskHeartbeatRequest{ TaskToken: t.TaskToken, Details: ToPayload(t.Details), Identity: t.Identity, } } func FromRecordActivityTaskHeartbeatResponse(t *types.RecordActivityTaskHeartbeatResponse) *apiv1.RecordActivityTaskHeartbeatResponse { if t == nil { return nil } return &apiv1.RecordActivityTaskHeartbeatResponse{ CancelRequested: t.CancelRequested, } } func ToRecordActivityTaskHeartbeatResponse(t *apiv1.RecordActivityTaskHeartbeatResponse) *types.RecordActivityTaskHeartbeatResponse { if t == nil { return nil } return &types.RecordActivityTaskHeartbeatResponse{ CancelRequested: t.CancelRequested, } } func FromRecordMarkerDecisionAttributes(t *types.RecordMarkerDecisionAttributes) *apiv1.RecordMarkerDecisionAttributes { if t == nil { return nil } return &apiv1.RecordMarkerDecisionAttributes{ MarkerName: t.MarkerName, Details: FromPayload(t.Details), Header: FromHeader(t.Header), } } func ToRecordMarkerDecisionAttributes(t *apiv1.RecordMarkerDecisionAttributes) *types.RecordMarkerDecisionAttributes { if t == nil { return nil } return &types.RecordMarkerDecisionAttributes{ MarkerName: t.MarkerName, Details: ToPayload(t.Details), Header: ToHeader(t.Header), } } func FromRegisterDomainRequest(t *types.RegisterDomainRequest) *apiv1.RegisterDomainRequest { if t == nil { return nil } return &apiv1.RegisterDomainRequest{ Name: t.Name, Description: t.Description, OwnerEmail: t.OwnerEmail, WorkflowExecutionRetentionPeriod: daysToDuration(&t.WorkflowExecutionRetentionPeriodInDays), Clusters: FromClusterReplicationConfigurationArray(t.Clusters), ActiveClusterName: t.ActiveClusterName, ActiveClusters: FromActiveClusters(t.ActiveClusters), Data: t.Data, SecurityToken: t.SecurityToken, IsGlobalDomain: t.IsGlobalDomain, HistoryArchivalStatus: FromArchivalStatus(t.HistoryArchivalStatus), HistoryArchivalUri: t.HistoryArchivalURI, VisibilityArchivalStatus: FromArchivalStatus(t.VisibilityArchivalStatus), VisibilityArchivalUri: t.VisibilityArchivalURI, } } func ToRegisterDomainRequest(t *apiv1.RegisterDomainRequest) *types.RegisterDomainRequest { if t == nil { return nil } days := durationToDays(t.WorkflowExecutionRetentionPeriod) if days == nil { days = common.Int32Ptr(3) // default to 3 days if not set } return &types.RegisterDomainRequest{ Name: t.Name, Description: t.Description, OwnerEmail: t.OwnerEmail, WorkflowExecutionRetentionPeriodInDays: *days, EmitMetric: common.BoolPtr(true), // this is a legacy field that doesn't exist in proto and probably can be removed Clusters: ToClusterReplicationConfigurationArray(t.Clusters), ActiveClusterName: t.ActiveClusterName, ActiveClusters: ToActiveClusters(t.ActiveClusters), Data: t.Data, SecurityToken: t.SecurityToken, IsGlobalDomain: t.IsGlobalDomain, HistoryArchivalStatus: ToArchivalStatus(t.HistoryArchivalStatus), HistoryArchivalURI: t.HistoryArchivalUri, VisibilityArchivalStatus: ToArchivalStatus(t.VisibilityArchivalStatus), VisibilityArchivalURI: t.VisibilityArchivalUri, } } func FromRequestCancelActivityTaskDecisionAttributes(t *types.RequestCancelActivityTaskDecisionAttributes) *apiv1.RequestCancelActivityTaskDecisionAttributes { if t == nil { return nil } return &apiv1.RequestCancelActivityTaskDecisionAttributes{ ActivityId: t.ActivityID, } } func ToRequestCancelActivityTaskDecisionAttributes(t *apiv1.RequestCancelActivityTaskDecisionAttributes) *types.RequestCancelActivityTaskDecisionAttributes { if t == nil { return nil } return &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: t.ActivityId, } } func FromRequestCancelActivityTaskFailedEventAttributes(t *types.RequestCancelActivityTaskFailedEventAttributes) *apiv1.RequestCancelActivityTaskFailedEventAttributes { if t == nil { return nil } return &apiv1.RequestCancelActivityTaskFailedEventAttributes{ ActivityId: t.ActivityID, Cause: t.Cause, DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, } } func ToRequestCancelActivityTaskFailedEventAttributes(t *apiv1.RequestCancelActivityTaskFailedEventAttributes) *types.RequestCancelActivityTaskFailedEventAttributes { if t == nil { return nil } return &types.RequestCancelActivityTaskFailedEventAttributes{ ActivityID: t.ActivityId, Cause: t.Cause, DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, } } func FromRequestCancelExternalWorkflowExecutionDecisionAttributes(t *types.RequestCancelExternalWorkflowExecutionDecisionAttributes) *apiv1.RequestCancelExternalWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &apiv1.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: t.Domain, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), Control: t.Control, ChildWorkflowOnly: t.ChildWorkflowOnly, } } func ToRequestCancelExternalWorkflowExecutionDecisionAttributes(t *apiv1.RequestCancelExternalWorkflowExecutionDecisionAttributes) *types.RequestCancelExternalWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: t.Domain, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), Control: t.Control, ChildWorkflowOnly: t.ChildWorkflowOnly, } } func FromRequestCancelExternalWorkflowExecutionFailedEventAttributes(t *types.RequestCancelExternalWorkflowExecutionFailedEventAttributes) *apiv1.RequestCancelExternalWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &apiv1.RequestCancelExternalWorkflowExecutionFailedEventAttributes{ Cause: FromCancelExternalWorkflowExecutionFailedCause(t.Cause), DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), InitiatedEventId: t.InitiatedEventID, Control: t.Control, } } func ToRequestCancelExternalWorkflowExecutionFailedEventAttributes(t *apiv1.RequestCancelExternalWorkflowExecutionFailedEventAttributes) *types.RequestCancelExternalWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &types.RequestCancelExternalWorkflowExecutionFailedEventAttributes{ Cause: ToCancelExternalWorkflowExecutionFailedCause(t.Cause), DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), InitiatedEventID: t.InitiatedEventId, Control: t.Control, } } func FromRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(t *types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) *apiv1.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &apiv1.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Control: t.Control, ChildWorkflowOnly: t.ChildWorkflowOnly, } } func ToRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(t *apiv1.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) *types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Control: t.Control, ChildWorkflowOnly: t.ChildWorkflowOnly, } } func FromRequestCancelWorkflowExecutionRequest(t *types.RequestCancelWorkflowExecutionRequest) *apiv1.RequestCancelWorkflowExecutionRequest { if t == nil { return nil } return &apiv1.RequestCancelWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Identity: t.Identity, RequestId: t.RequestID, Cause: t.Cause, FirstExecutionRunId: t.FirstExecutionRunID, } } func ToRequestCancelWorkflowExecutionRequest(t *apiv1.RequestCancelWorkflowExecutionRequest) *types.RequestCancelWorkflowExecutionRequest { if t == nil { return nil } return &types.RequestCancelWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Identity: t.Identity, RequestID: t.RequestId, Cause: t.Cause, FirstExecutionRunID: t.FirstExecutionRunId, } } func FromResetPointInfo(t *types.ResetPointInfo) *apiv1.ResetPointInfo { if t == nil { return nil } return &apiv1.ResetPointInfo{ BinaryChecksum: t.BinaryChecksum, RunId: t.RunID, FirstDecisionCompletedId: t.FirstDecisionCompletedID, CreatedTime: unixNanoToTime(t.CreatedTimeNano), ExpiringTime: unixNanoToTime(t.ExpiringTimeNano), Resettable: t.Resettable, } } func ToResetPointInfo(t *apiv1.ResetPointInfo) *types.ResetPointInfo { if t == nil { return nil } return &types.ResetPointInfo{ BinaryChecksum: t.BinaryChecksum, RunID: t.RunId, FirstDecisionCompletedID: t.FirstDecisionCompletedId, CreatedTimeNano: timeToUnixNano(t.CreatedTime), ExpiringTimeNano: timeToUnixNano(t.ExpiringTime), Resettable: t.Resettable, } } func FromResetPoints(t *types.ResetPoints) *apiv1.ResetPoints { if t == nil { return nil } return &apiv1.ResetPoints{ Points: FromResetPointInfoArray(t.Points), } } func ToResetPoints(t *apiv1.ResetPoints) *types.ResetPoints { if t == nil { return nil } return &types.ResetPoints{ Points: ToResetPointInfoArray(t.Points), } } func FromResetStickyTaskListRequest(t *types.ResetStickyTaskListRequest) *apiv1.ResetStickyTaskListRequest { if t == nil { return nil } return &apiv1.ResetStickyTaskListRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), } } func ToResetStickyTaskListRequest(t *apiv1.ResetStickyTaskListRequest) *types.ResetStickyTaskListRequest { if t == nil { return nil } return &types.ResetStickyTaskListRequest{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), } } func FromResetWorkflowExecutionRequest(t *types.ResetWorkflowExecutionRequest) *apiv1.ResetWorkflowExecutionRequest { if t == nil { return nil } return &apiv1.ResetWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Reason: t.Reason, DecisionFinishEventId: t.DecisionFinishEventID, RequestId: t.RequestID, SkipSignalReapply: t.SkipSignalReapply, } } func ToResetWorkflowExecutionRequest(t *apiv1.ResetWorkflowExecutionRequest) *types.ResetWorkflowExecutionRequest { if t == nil { return nil } return &types.ResetWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Reason: t.Reason, DecisionFinishEventID: t.DecisionFinishEventId, RequestID: t.RequestId, SkipSignalReapply: t.SkipSignalReapply, } } func FromResetWorkflowExecutionResponse(t *types.ResetWorkflowExecutionResponse) *apiv1.ResetWorkflowExecutionResponse { if t == nil { return nil } return &apiv1.ResetWorkflowExecutionResponse{ RunId: t.RunID, } } func ToResetWorkflowExecutionResponse(t *apiv1.ResetWorkflowExecutionResponse) *types.ResetWorkflowExecutionResponse { if t == nil { return nil } return &types.ResetWorkflowExecutionResponse{ RunID: t.RunId, } } func ToRefreshWorkflowTasksRequest(t *apiv1.RefreshWorkflowTasksRequest) *types.RefreshWorkflowTasksRequest { if t == nil { return nil } return &types.RefreshWorkflowTasksRequest{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), } } func FromRefreshWorkflowTasksRequest(t *types.RefreshWorkflowTasksRequest) *apiv1.RefreshWorkflowTasksRequest { if t == nil { return nil } return &apiv1.RefreshWorkflowTasksRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), } } func FromRespondActivityTaskCanceledByIDRequest(t *types.RespondActivityTaskCanceledByIDRequest) *apiv1.RespondActivityTaskCanceledByIDRequest { if t == nil { return nil } return &apiv1.RespondActivityTaskCanceledByIDRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), ActivityId: t.ActivityID, Details: FromPayload(t.Details), Identity: t.Identity, } } func ToRespondActivityTaskCanceledByIDRequest(t *apiv1.RespondActivityTaskCanceledByIDRequest) *types.RespondActivityTaskCanceledByIDRequest { if t == nil { return nil } return &types.RespondActivityTaskCanceledByIDRequest{ Domain: t.Domain, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), ActivityID: t.ActivityId, Details: ToPayload(t.Details), Identity: t.Identity, } } func FromRespondActivityTaskCanceledRequest(t *types.RespondActivityTaskCanceledRequest) *apiv1.RespondActivityTaskCanceledRequest { if t == nil { return nil } return &apiv1.RespondActivityTaskCanceledRequest{ TaskToken: t.TaskToken, Details: FromPayload(t.Details), Identity: t.Identity, } } func ToRespondActivityTaskCanceledRequest(t *apiv1.RespondActivityTaskCanceledRequest) *types.RespondActivityTaskCanceledRequest { if t == nil { return nil } return &types.RespondActivityTaskCanceledRequest{ TaskToken: t.TaskToken, Details: ToPayload(t.Details), Identity: t.Identity, } } func FromRespondActivityTaskCompletedByIDRequest(t *types.RespondActivityTaskCompletedByIDRequest) *apiv1.RespondActivityTaskCompletedByIDRequest { if t == nil { return nil } return &apiv1.RespondActivityTaskCompletedByIDRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), ActivityId: t.ActivityID, Result: FromPayload(t.Result), Identity: t.Identity, } } func ToRespondActivityTaskCompletedByIDRequest(t *apiv1.RespondActivityTaskCompletedByIDRequest) *types.RespondActivityTaskCompletedByIDRequest { if t == nil { return nil } return &types.RespondActivityTaskCompletedByIDRequest{ Domain: t.Domain, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), ActivityID: t.ActivityId, Result: ToPayload(t.Result), Identity: t.Identity, } } func FromRespondActivityTaskCompletedRequest(t *types.RespondActivityTaskCompletedRequest) *apiv1.RespondActivityTaskCompletedRequest { if t == nil { return nil } return &apiv1.RespondActivityTaskCompletedRequest{ TaskToken: t.TaskToken, Result: FromPayload(t.Result), Identity: t.Identity, } } func ToRespondActivityTaskCompletedRequest(t *apiv1.RespondActivityTaskCompletedRequest) *types.RespondActivityTaskCompletedRequest { if t == nil { return nil } return &types.RespondActivityTaskCompletedRequest{ TaskToken: t.TaskToken, Result: ToPayload(t.Result), Identity: t.Identity, } } func FromRespondActivityTaskFailedByIDRequest(t *types.RespondActivityTaskFailedByIDRequest) *apiv1.RespondActivityTaskFailedByIDRequest { if t == nil { return nil } return &apiv1.RespondActivityTaskFailedByIDRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), ActivityId: t.ActivityID, Failure: FromFailure(t.Reason, t.Details), Identity: t.Identity, } } func ToRespondActivityTaskFailedByIDRequest(t *apiv1.RespondActivityTaskFailedByIDRequest) *types.RespondActivityTaskFailedByIDRequest { if t == nil { return nil } return &types.RespondActivityTaskFailedByIDRequest{ Domain: t.Domain, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), ActivityID: t.ActivityId, Reason: ToFailureReason(t.Failure), Details: ToFailureDetails(t.Failure), Identity: t.Identity, } } func FromRespondActivityTaskFailedRequest(t *types.RespondActivityTaskFailedRequest) *apiv1.RespondActivityTaskFailedRequest { if t == nil { return nil } return &apiv1.RespondActivityTaskFailedRequest{ TaskToken: t.TaskToken, Failure: FromFailure(t.Reason, t.Details), Identity: t.Identity, } } func ToRespondActivityTaskFailedRequest(t *apiv1.RespondActivityTaskFailedRequest) *types.RespondActivityTaskFailedRequest { if t == nil { return nil } return &types.RespondActivityTaskFailedRequest{ TaskToken: t.TaskToken, Reason: ToFailureReason(t.Failure), Details: ToFailureDetails(t.Failure), Identity: t.Identity, } } func FromRespondDecisionTaskCompletedRequest(t *types.RespondDecisionTaskCompletedRequest) *apiv1.RespondDecisionTaskCompletedRequest { if t == nil { return nil } return &apiv1.RespondDecisionTaskCompletedRequest{ TaskToken: t.TaskToken, Decisions: FromDecisionArray(t.Decisions), ExecutionContext: t.ExecutionContext, Identity: t.Identity, StickyAttributes: FromStickyExecutionAttributes(t.StickyAttributes), ReturnNewDecisionTask: t.ReturnNewDecisionTask, ForceCreateNewDecisionTask: t.ForceCreateNewDecisionTask, BinaryChecksum: t.BinaryChecksum, QueryResults: FromWorkflowQueryResultMap(t.QueryResults), } } func ToRespondDecisionTaskCompletedRequest(t *apiv1.RespondDecisionTaskCompletedRequest) *types.RespondDecisionTaskCompletedRequest { if t == nil { return nil } return &types.RespondDecisionTaskCompletedRequest{ TaskToken: t.TaskToken, Decisions: ToDecisionArray(t.Decisions), ExecutionContext: t.ExecutionContext, Identity: t.Identity, StickyAttributes: ToStickyExecutionAttributes(t.StickyAttributes), ReturnNewDecisionTask: t.ReturnNewDecisionTask, ForceCreateNewDecisionTask: t.ForceCreateNewDecisionTask, BinaryChecksum: t.BinaryChecksum, QueryResults: ToWorkflowQueryResultMap(t.QueryResults), } } func FromRespondDecisionTaskCompletedResponse(t *types.RespondDecisionTaskCompletedResponse) *apiv1.RespondDecisionTaskCompletedResponse { if t == nil { return nil } return &apiv1.RespondDecisionTaskCompletedResponse{ DecisionTask: FromPollForDecisionTaskResponse(t.DecisionTask), ActivitiesToDispatchLocally: FromActivityLocalDispatchInfoMap(t.ActivitiesToDispatchLocally), } } func ToRespondDecisionTaskCompletedResponse(t *apiv1.RespondDecisionTaskCompletedResponse) *types.RespondDecisionTaskCompletedResponse { if t == nil { return nil } return &types.RespondDecisionTaskCompletedResponse{ DecisionTask: ToPollForDecisionTaskResponse(t.DecisionTask), ActivitiesToDispatchLocally: ToActivityLocalDispatchInfoMap(t.ActivitiesToDispatchLocally), } } func FromRespondDecisionTaskFailedRequest(t *types.RespondDecisionTaskFailedRequest) *apiv1.RespondDecisionTaskFailedRequest { if t == nil { return nil } return &apiv1.RespondDecisionTaskFailedRequest{ TaskToken: t.TaskToken, Cause: FromDecisionTaskFailedCause(t.Cause), Details: FromPayload(t.Details), Identity: t.Identity, BinaryChecksum: t.BinaryChecksum, } } func ToRespondDecisionTaskFailedRequest(t *apiv1.RespondDecisionTaskFailedRequest) *types.RespondDecisionTaskFailedRequest { if t == nil { return nil } return &types.RespondDecisionTaskFailedRequest{ TaskToken: t.TaskToken, Cause: ToDecisionTaskFailedCause(t.Cause), Details: ToPayload(t.Details), Identity: t.Identity, BinaryChecksum: t.BinaryChecksum, } } func FromRespondQueryTaskCompletedRequest(t *types.RespondQueryTaskCompletedRequest) *apiv1.RespondQueryTaskCompletedRequest { if t == nil { return nil } return &apiv1.RespondQueryTaskCompletedRequest{ TaskToken: t.TaskToken, Result: &apiv1.WorkflowQueryResult{ ResultType: FromQueryTaskCompletedType(t.CompletedType), Answer: FromPayload(t.QueryResult), ErrorMessage: t.ErrorMessage, }, WorkerVersionInfo: FromWorkerVersionInfo(t.WorkerVersionInfo), } } func ToRespondQueryTaskCompletedRequest(t *apiv1.RespondQueryTaskCompletedRequest) *types.RespondQueryTaskCompletedRequest { if t == nil { return nil } return &types.RespondQueryTaskCompletedRequest{ TaskToken: t.TaskToken, CompletedType: ToQueryTaskCompletedType(t.Result.ResultType), QueryResult: ToPayload(t.Result.Answer), ErrorMessage: t.Result.ErrorMessage, WorkerVersionInfo: ToWorkerVersionInfo(t.WorkerVersionInfo), } } func FromQueryTaskCompletedType(t *types.QueryTaskCompletedType) apiv1.QueryResultType { if t == nil { return apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID } switch *t { case types.QueryTaskCompletedTypeCompleted: return apiv1.QueryResultType_QUERY_RESULT_TYPE_ANSWERED case types.QueryTaskCompletedTypeFailed: return apiv1.QueryResultType_QUERY_RESULT_TYPE_FAILED } return apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID } func ToQueryTaskCompletedType(t apiv1.QueryResultType) *types.QueryTaskCompletedType { switch t { case apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID: return nil case apiv1.QueryResultType_QUERY_RESULT_TYPE_ANSWERED: return types.QueryTaskCompletedTypeCompleted.Ptr() case apiv1.QueryResultType_QUERY_RESULT_TYPE_FAILED: return types.QueryTaskCompletedTypeFailed.Ptr() } return nil } func FromRetryPolicy(t *types.RetryPolicy) *apiv1.RetryPolicy { if t == nil { return nil } return &apiv1.RetryPolicy{ InitialInterval: secondsToDuration(common.Int32Ptr(t.InitialIntervalInSeconds)), BackoffCoefficient: t.BackoffCoefficient, MaximumInterval: secondsToDuration(common.Int32Ptr(t.MaximumIntervalInSeconds)), MaximumAttempts: t.MaximumAttempts, NonRetryableErrorReasons: t.NonRetriableErrorReasons, ExpirationInterval: secondsToDuration(common.Int32Ptr(t.ExpirationIntervalInSeconds)), } } func ToRetryPolicy(t *apiv1.RetryPolicy) *types.RetryPolicy { if t == nil { return nil } return &types.RetryPolicy{ InitialIntervalInSeconds: common.Int32Default(durationToSeconds(t.InitialInterval)), BackoffCoefficient: t.BackoffCoefficient, MaximumIntervalInSeconds: common.Int32Default(durationToSeconds(t.MaximumInterval)), MaximumAttempts: t.MaximumAttempts, NonRetriableErrorReasons: t.NonRetryableErrorReasons, ExpirationIntervalInSeconds: common.Int32Default(durationToSeconds(t.ExpirationInterval)), } } func FromRestartWorkflowExecutionResponse(t *types.RestartWorkflowExecutionResponse) *apiv1.RestartWorkflowExecutionResponse { if t == nil { return nil } return &apiv1.RestartWorkflowExecutionResponse{ RunId: t.RunID, } } func ToRestartWorkflowExecutionRequest(t *apiv1.RestartWorkflowExecutionRequest) *types.RestartWorkflowExecutionRequest { if t == nil { return nil } return &types.RestartWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Identity: t.Identity, } } func FromScanWorkflowExecutionsRequest(t *types.ListWorkflowExecutionsRequest) *apiv1.ScanWorkflowExecutionsRequest { if t == nil { return nil } return &apiv1.ScanWorkflowExecutionsRequest{ Domain: t.Domain, PageSize: t.PageSize, NextPageToken: t.NextPageToken, Query: t.Query, } } func ToScanWorkflowExecutionsRequest(t *apiv1.ScanWorkflowExecutionsRequest) *types.ListWorkflowExecutionsRequest { if t == nil { return nil } return &types.ListWorkflowExecutionsRequest{ Domain: t.Domain, PageSize: t.PageSize, NextPageToken: t.NextPageToken, Query: t.Query, } } func FromScanWorkflowExecutionsResponse(t *types.ListWorkflowExecutionsResponse) *apiv1.ScanWorkflowExecutionsResponse { if t == nil { return nil } return &apiv1.ScanWorkflowExecutionsResponse{ Executions: FromWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } func ToScanWorkflowExecutionsResponse(t *apiv1.ScanWorkflowExecutionsResponse) *types.ListWorkflowExecutionsResponse { if t == nil { return nil } return &types.ListWorkflowExecutionsResponse{ Executions: ToWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } func FromScheduleActivityTaskDecisionAttributes(t *types.ScheduleActivityTaskDecisionAttributes) *apiv1.ScheduleActivityTaskDecisionAttributes { if t == nil { return nil } return &apiv1.ScheduleActivityTaskDecisionAttributes{ ActivityId: t.ActivityID, ActivityType: FromActivityType(t.ActivityType), Domain: t.Domain, TaskList: FromTaskList(t.TaskList), Input: FromPayload(t.Input), ScheduleToCloseTimeout: secondsToDuration(t.ScheduleToCloseTimeoutSeconds), ScheduleToStartTimeout: secondsToDuration(t.ScheduleToStartTimeoutSeconds), StartToCloseTimeout: secondsToDuration(t.StartToCloseTimeoutSeconds), HeartbeatTimeout: secondsToDuration(t.HeartbeatTimeoutSeconds), RetryPolicy: FromRetryPolicy(t.RetryPolicy), Header: FromHeader(t.Header), RequestLocalDispatch: t.RequestLocalDispatch, } } func ToScheduleActivityTaskDecisionAttributes(t *apiv1.ScheduleActivityTaskDecisionAttributes) *types.ScheduleActivityTaskDecisionAttributes { if t == nil { return nil } return &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: t.ActivityId, ActivityType: ToActivityType(t.ActivityType), Domain: t.Domain, TaskList: ToTaskList(t.TaskList), Input: ToPayload(t.Input), ScheduleToCloseTimeoutSeconds: durationToSeconds(t.ScheduleToCloseTimeout), ScheduleToStartTimeoutSeconds: durationToSeconds(t.ScheduleToStartTimeout), StartToCloseTimeoutSeconds: durationToSeconds(t.StartToCloseTimeout), HeartbeatTimeoutSeconds: durationToSeconds(t.HeartbeatTimeout), RetryPolicy: ToRetryPolicy(t.RetryPolicy), Header: ToHeader(t.Header), RequestLocalDispatch: t.RequestLocalDispatch, } } func FromSearchAttributes(t *types.SearchAttributes) *apiv1.SearchAttributes { if t == nil { return nil } return &apiv1.SearchAttributes{ IndexedFields: FromPayloadMap(t.IndexedFields), } } func ToSearchAttributes(t *apiv1.SearchAttributes) *types.SearchAttributes { if t == nil { return nil } return &types.SearchAttributes{ IndexedFields: ToPayloadMap(t.IndexedFields), } } func FromSignalExternalWorkflowExecutionDecisionAttributes(t *types.SignalExternalWorkflowExecutionDecisionAttributes) *apiv1.SignalExternalWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &apiv1.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), SignalName: t.SignalName, Input: FromPayload(t.Input), Control: t.Control, ChildWorkflowOnly: t.ChildWorkflowOnly, } } func ToSignalExternalWorkflowExecutionDecisionAttributes(t *apiv1.SignalExternalWorkflowExecutionDecisionAttributes) *types.SignalExternalWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), SignalName: t.SignalName, Input: ToPayload(t.Input), Control: t.Control, ChildWorkflowOnly: t.ChildWorkflowOnly, } } func FromSignalExternalWorkflowExecutionFailedCause(t *types.SignalExternalWorkflowExecutionFailedCause) apiv1.SignalExternalWorkflowExecutionFailedCause { if t == nil { return apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID } switch *t { case types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution: return apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION case types.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted: return apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_COMPLETED } return apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID } func ToSignalExternalWorkflowExecutionFailedCause(t apiv1.SignalExternalWorkflowExecutionFailedCause) *types.SignalExternalWorkflowExecutionFailedCause { switch t { case apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID: return nil case apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION: return types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr() case apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_COMPLETED: return types.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted.Ptr() } return nil } func FromSignalExternalWorkflowExecutionFailedEventAttributes(t *types.SignalExternalWorkflowExecutionFailedEventAttributes) *apiv1.SignalExternalWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &apiv1.SignalExternalWorkflowExecutionFailedEventAttributes{ Cause: FromSignalExternalWorkflowExecutionFailedCause(t.Cause), DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), InitiatedEventId: t.InitiatedEventID, Control: t.Control, } } func ToSignalExternalWorkflowExecutionFailedEventAttributes(t *apiv1.SignalExternalWorkflowExecutionFailedEventAttributes) *types.SignalExternalWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &types.SignalExternalWorkflowExecutionFailedEventAttributes{ Cause: ToSignalExternalWorkflowExecutionFailedCause(t.Cause), DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), InitiatedEventID: t.InitiatedEventId, Control: t.Control, } } func FromSignalExternalWorkflowExecutionInitiatedEventAttributes(t *types.SignalExternalWorkflowExecutionInitiatedEventAttributes) *apiv1.SignalExternalWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &apiv1.SignalExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), SignalName: t.SignalName, Input: FromPayload(t.Input), Control: t.Control, ChildWorkflowOnly: t.ChildWorkflowOnly, } } func ToSignalExternalWorkflowExecutionInitiatedEventAttributes(t *apiv1.SignalExternalWorkflowExecutionInitiatedEventAttributes) *types.SignalExternalWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &types.SignalExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), SignalName: t.SignalName, Input: ToPayload(t.Input), Control: t.Control, ChildWorkflowOnly: t.ChildWorkflowOnly, } } func FromSignalWithStartWorkflowExecutionRequest(t *types.SignalWithStartWorkflowExecutionRequest) *apiv1.SignalWithStartWorkflowExecutionRequest { if t == nil { return nil } return &apiv1.SignalWithStartWorkflowExecutionRequest{ StartRequest: &apiv1.StartWorkflowExecutionRequest{ Domain: t.Domain, WorkflowId: t.WorkflowID, WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: FromPayload(t.Input), ExecutionStartToCloseTimeout: secondsToDuration(t.ExecutionStartToCloseTimeoutSeconds), TaskStartToCloseTimeout: secondsToDuration(t.TaskStartToCloseTimeoutSeconds), Identity: t.Identity, RequestId: t.RequestID, WorkflowIdReusePolicy: FromWorkflowIDReusePolicy(t.WorkflowIDReusePolicy), RetryPolicy: FromRetryPolicy(t.RetryPolicy), CronSchedule: t.CronSchedule, Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), Header: FromHeader(t.Header), DelayStart: secondsToDuration(t.DelayStartSeconds), JitterStart: secondsToDuration(t.JitterStartSeconds), FirstRunAt: unixNanoToTime(t.FirstRunAtTimestamp), CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), }, SignalName: t.SignalName, SignalInput: FromPayload(t.SignalInput), Control: t.Control, } } func ToSignalWithStartWorkflowExecutionRequest(t *apiv1.SignalWithStartWorkflowExecutionRequest) *types.SignalWithStartWorkflowExecutionRequest { if t == nil { return nil } return &types.SignalWithStartWorkflowExecutionRequest{ Domain: t.StartRequest.Domain, WorkflowID: t.StartRequest.WorkflowId, WorkflowType: ToWorkflowType(t.StartRequest.WorkflowType), TaskList: ToTaskList(t.StartRequest.TaskList), Input: ToPayload(t.StartRequest.Input), ExecutionStartToCloseTimeoutSeconds: durationToSeconds(t.StartRequest.ExecutionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: durationToSeconds(t.StartRequest.TaskStartToCloseTimeout), Identity: t.StartRequest.Identity, RequestID: t.StartRequest.RequestId, WorkflowIDReusePolicy: ToWorkflowIDReusePolicy(t.StartRequest.WorkflowIdReusePolicy), SignalName: t.SignalName, SignalInput: ToPayload(t.SignalInput), Control: t.Control, RetryPolicy: ToRetryPolicy(t.StartRequest.RetryPolicy), CronSchedule: t.StartRequest.CronSchedule, Memo: ToMemo(t.StartRequest.Memo), SearchAttributes: ToSearchAttributes(t.StartRequest.SearchAttributes), Header: ToHeader(t.StartRequest.Header), DelayStartSeconds: durationToSeconds(t.StartRequest.DelayStart), JitterStartSeconds: durationToSeconds(t.StartRequest.JitterStart), FirstRunAtTimestamp: timeToUnixNano(t.StartRequest.FirstRunAt), CronOverlapPolicy: ToCronOverlapPolicy(t.StartRequest.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.StartRequest.ActiveClusterSelectionPolicy), } } func FromSignalWithStartWorkflowExecutionResponse(t *types.StartWorkflowExecutionResponse) *apiv1.SignalWithStartWorkflowExecutionResponse { if t == nil { return nil } return &apiv1.SignalWithStartWorkflowExecutionResponse{ RunId: t.RunID, } } func ToSignalWithStartWorkflowExecutionResponse(t *apiv1.SignalWithStartWorkflowExecutionResponse) *types.StartWorkflowExecutionResponse { if t == nil { return nil } return &types.StartWorkflowExecutionResponse{ RunID: t.RunId, } } func FromSignalWorkflowExecutionRequest(t *types.SignalWorkflowExecutionRequest) *apiv1.SignalWorkflowExecutionRequest { if t == nil { return nil } return &apiv1.SignalWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), SignalName: t.SignalName, SignalInput: FromPayload(t.Input), Identity: t.Identity, RequestId: t.RequestID, Control: t.Control, } } func ToSignalWorkflowExecutionRequest(t *apiv1.SignalWorkflowExecutionRequest) *types.SignalWorkflowExecutionRequest { if t == nil { return nil } return &types.SignalWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), SignalName: t.SignalName, Input: ToPayload(t.SignalInput), Identity: t.Identity, RequestID: t.RequestId, Control: t.Control, } } func FromStartChildWorkflowExecutionDecisionAttributes(t *types.StartChildWorkflowExecutionDecisionAttributes) *apiv1.StartChildWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &apiv1.StartChildWorkflowExecutionDecisionAttributes{ Domain: t.Domain, WorkflowId: t.WorkflowID, WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: FromPayload(t.Input), ExecutionStartToCloseTimeout: secondsToDuration(t.ExecutionStartToCloseTimeoutSeconds), TaskStartToCloseTimeout: secondsToDuration(t.TaskStartToCloseTimeoutSeconds), ParentClosePolicy: FromParentClosePolicy(t.ParentClosePolicy), Control: t.Control, WorkflowIdReusePolicy: FromWorkflowIDReusePolicy(t.WorkflowIDReusePolicy), RetryPolicy: FromRetryPolicy(t.RetryPolicy), CronSchedule: t.CronSchedule, Header: FromHeader(t.Header), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } func ToStartChildWorkflowExecutionDecisionAttributes(t *apiv1.StartChildWorkflowExecutionDecisionAttributes) *types.StartChildWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: t.Domain, WorkflowID: t.WorkflowId, WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: ToPayload(t.Input), ExecutionStartToCloseTimeoutSeconds: durationToSeconds(t.ExecutionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: durationToSeconds(t.TaskStartToCloseTimeout), ParentClosePolicy: ToParentClosePolicy(t.ParentClosePolicy), Control: t.Control, WorkflowIDReusePolicy: ToWorkflowIDReusePolicy(t.WorkflowIdReusePolicy), RetryPolicy: ToRetryPolicy(t.RetryPolicy), CronSchedule: t.CronSchedule, Header: ToHeader(t.Header), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } func FromStartChildWorkflowExecutionFailedEventAttributes(t *types.StartChildWorkflowExecutionFailedEventAttributes) *apiv1.StartChildWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &apiv1.StartChildWorkflowExecutionFailedEventAttributes{ Domain: t.Domain, WorkflowId: t.WorkflowID, WorkflowType: FromWorkflowType(t.WorkflowType), Cause: FromChildWorkflowExecutionFailedCause(t.Cause), Control: t.Control, InitiatedEventId: t.InitiatedEventID, DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, } } func ToStartChildWorkflowExecutionFailedEventAttributes(t *apiv1.StartChildWorkflowExecutionFailedEventAttributes) *types.StartChildWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &types.StartChildWorkflowExecutionFailedEventAttributes{ Domain: t.Domain, WorkflowID: t.WorkflowId, WorkflowType: ToWorkflowType(t.WorkflowType), Cause: ToChildWorkflowExecutionFailedCause(t.Cause), Control: t.Control, InitiatedEventID: t.InitiatedEventId, DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, } } func FromStartChildWorkflowExecutionInitiatedEventAttributes(t *types.StartChildWorkflowExecutionInitiatedEventAttributes) *apiv1.StartChildWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &apiv1.StartChildWorkflowExecutionInitiatedEventAttributes{ Domain: t.Domain, WorkflowId: t.WorkflowID, WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: FromPayload(t.Input), ExecutionStartToCloseTimeout: secondsToDuration(t.ExecutionStartToCloseTimeoutSeconds), TaskStartToCloseTimeout: secondsToDuration(t.TaskStartToCloseTimeoutSeconds), ParentClosePolicy: FromParentClosePolicy(t.ParentClosePolicy), Control: t.Control, DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, WorkflowIdReusePolicy: FromWorkflowIDReusePolicy(t.WorkflowIDReusePolicy), RetryPolicy: FromRetryPolicy(t.RetryPolicy), CronSchedule: t.CronSchedule, Header: FromHeader(t.Header), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), DelayStart: secondsToDuration(t.DelayStartSeconds), JitterStart: secondsToDuration(t.JitterStartSeconds), FirstRunAt: unixNanoToTime(t.FirstRunAtTimestamp), CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } func ToStartChildWorkflowExecutionInitiatedEventAttributes(t *apiv1.StartChildWorkflowExecutionInitiatedEventAttributes) *types.StartChildWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &types.StartChildWorkflowExecutionInitiatedEventAttributes{ Domain: t.Domain, WorkflowID: t.WorkflowId, WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: ToPayload(t.Input), ExecutionStartToCloseTimeoutSeconds: durationToSeconds(t.ExecutionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: durationToSeconds(t.TaskStartToCloseTimeout), ParentClosePolicy: ToParentClosePolicy(t.ParentClosePolicy), Control: t.Control, DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, WorkflowIDReusePolicy: ToWorkflowIDReusePolicy(t.WorkflowIdReusePolicy), RetryPolicy: ToRetryPolicy(t.RetryPolicy), CronSchedule: t.CronSchedule, Header: ToHeader(t.Header), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), DelayStartSeconds: durationToSeconds(t.DelayStart), JitterStartSeconds: durationToSeconds(t.JitterStart), FirstRunAtTimestamp: timeToUnixNano(t.FirstRunAt), CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } func FromStartTimeFilter(t *types.StartTimeFilter) *apiv1.StartTimeFilter { if t == nil { return nil } return &apiv1.StartTimeFilter{ EarliestTime: unixNanoToTime(t.EarliestTime), LatestTime: unixNanoToTime(t.LatestTime), } } func ToStartTimeFilter(t *apiv1.StartTimeFilter) *types.StartTimeFilter { if t == nil { return nil } return &types.StartTimeFilter{ EarliestTime: timeToUnixNano(t.EarliestTime), LatestTime: timeToUnixNano(t.LatestTime), } } func FromStartTimerDecisionAttributes(t *types.StartTimerDecisionAttributes) *apiv1.StartTimerDecisionAttributes { if t == nil { return nil } return &apiv1.StartTimerDecisionAttributes{ TimerId: t.TimerID, StartToFireTimeout: secondsToDuration(int64To32(t.StartToFireTimeoutSeconds)), } } func ToStartTimerDecisionAttributes(t *apiv1.StartTimerDecisionAttributes) *types.StartTimerDecisionAttributes { if t == nil { return nil } return &types.StartTimerDecisionAttributes{ TimerID: t.TimerId, StartToFireTimeoutSeconds: int32To64(durationToSeconds(t.StartToFireTimeout)), } } func FromRestartWorkflowExecutionRequest(t *types.RestartWorkflowExecutionRequest) *apiv1.RestartWorkflowExecutionRequest { if t == nil { return nil } return &apiv1.RestartWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Identity: t.Identity, } } func ToRestartWorkflowExecutionResponse(t *apiv1.RestartWorkflowExecutionResponse) *types.RestartWorkflowExecutionResponse { if t == nil { return nil } return &types.RestartWorkflowExecutionResponse{ RunID: t.RunId, } } func FromSignalWithStartWorkflowExecutionAsyncRequest(t *types.SignalWithStartWorkflowExecutionAsyncRequest) *apiv1.SignalWithStartWorkflowExecutionAsyncRequest { if t == nil { return nil } return &apiv1.SignalWithStartWorkflowExecutionAsyncRequest{ Request: FromSignalWithStartWorkflowExecutionRequest(t.SignalWithStartWorkflowExecutionRequest), } } func ToSignalWithStartWorkflowExecutionAsyncRequest(t *apiv1.SignalWithStartWorkflowExecutionAsyncRequest) *types.SignalWithStartWorkflowExecutionAsyncRequest { if t == nil { return nil } return &types.SignalWithStartWorkflowExecutionAsyncRequest{ SignalWithStartWorkflowExecutionRequest: ToSignalWithStartWorkflowExecutionRequest(t.Request), } } func FromSignalWithStartWorkflowExecutionAsyncResponse(t *types.SignalWithStartWorkflowExecutionAsyncResponse) *apiv1.SignalWithStartWorkflowExecutionAsyncResponse { if t == nil { return nil } return &apiv1.SignalWithStartWorkflowExecutionAsyncResponse{} } func ToSignalWithStartWorkflowExecutionAsyncResponse(t *apiv1.SignalWithStartWorkflowExecutionAsyncResponse) *types.SignalWithStartWorkflowExecutionAsyncResponse { if t == nil { return nil } return &types.SignalWithStartWorkflowExecutionAsyncResponse{} } func FromStartWorkflowExecutionAsyncRequest(t *types.StartWorkflowExecutionAsyncRequest) *apiv1.StartWorkflowExecutionAsyncRequest { if t == nil { return nil } return &apiv1.StartWorkflowExecutionAsyncRequest{ Request: FromStartWorkflowExecutionRequest(t.StartWorkflowExecutionRequest), } } func ToStartWorkflowExecutionAsyncRequest(t *apiv1.StartWorkflowExecutionAsyncRequest) *types.StartWorkflowExecutionAsyncRequest { if t == nil { return nil } return &types.StartWorkflowExecutionAsyncRequest{ StartWorkflowExecutionRequest: ToStartWorkflowExecutionRequest(t.Request), } } func FromStartWorkflowExecutionAsyncResponse(t *types.StartWorkflowExecutionAsyncResponse) *apiv1.StartWorkflowExecutionAsyncResponse { if t == nil { return nil } return &apiv1.StartWorkflowExecutionAsyncResponse{} } func ToStartWorkflowExecutionAsyncResponse(t *apiv1.StartWorkflowExecutionAsyncResponse) *types.StartWorkflowExecutionAsyncResponse { if t == nil { return nil } return &types.StartWorkflowExecutionAsyncResponse{} } func FromStartWorkflowExecutionRequest(t *types.StartWorkflowExecutionRequest) *apiv1.StartWorkflowExecutionRequest { if t == nil { return nil } return &apiv1.StartWorkflowExecutionRequest{ Domain: t.Domain, WorkflowId: t.WorkflowID, WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: FromPayload(t.Input), ExecutionStartToCloseTimeout: secondsToDuration(t.ExecutionStartToCloseTimeoutSeconds), TaskStartToCloseTimeout: secondsToDuration(t.TaskStartToCloseTimeoutSeconds), Identity: t.Identity, RequestId: t.RequestID, WorkflowIdReusePolicy: FromWorkflowIDReusePolicy(t.WorkflowIDReusePolicy), RetryPolicy: FromRetryPolicy(t.RetryPolicy), CronSchedule: t.CronSchedule, Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), Header: FromHeader(t.Header), DelayStart: secondsToDuration(t.DelayStartSeconds), JitterStart: secondsToDuration(t.JitterStartSeconds), FirstRunAt: unixNanoToTime(t.FirstRunAtTimeStamp), CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } func ToStartWorkflowExecutionRequest(t *apiv1.StartWorkflowExecutionRequest) *types.StartWorkflowExecutionRequest { if t == nil { return nil } return &types.StartWorkflowExecutionRequest{ Domain: t.Domain, WorkflowID: t.WorkflowId, WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: ToPayload(t.Input), ExecutionStartToCloseTimeoutSeconds: durationToSeconds(t.ExecutionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: durationToSeconds(t.TaskStartToCloseTimeout), Identity: t.Identity, RequestID: t.RequestId, WorkflowIDReusePolicy: ToWorkflowIDReusePolicy(t.WorkflowIdReusePolicy), RetryPolicy: ToRetryPolicy(t.RetryPolicy), CronSchedule: t.CronSchedule, Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), Header: ToHeader(t.Header), DelayStartSeconds: durationToSeconds(t.DelayStart), JitterStartSeconds: durationToSeconds(t.JitterStart), FirstRunAtTimeStamp: timeToUnixNano(t.FirstRunAt), CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } func FromStartWorkflowExecutionResponse(t *types.StartWorkflowExecutionResponse) *apiv1.StartWorkflowExecutionResponse { if t == nil { return nil } return &apiv1.StartWorkflowExecutionResponse{ RunId: t.RunID, } } func ToStartWorkflowExecutionResponse(t *apiv1.StartWorkflowExecutionResponse) *types.StartWorkflowExecutionResponse { if t == nil { return nil } return &types.StartWorkflowExecutionResponse{ RunID: t.RunId, } } func FromStatusFilter(t *types.WorkflowExecutionCloseStatus) *apiv1.StatusFilter { if t == nil { return nil } return &apiv1.StatusFilter{ Status: FromWorkflowExecutionCloseStatus(t), } } func ToStatusFilter(t *apiv1.StatusFilter) *types.WorkflowExecutionCloseStatus { if t == nil { return nil } return ToWorkflowExecutionCloseStatus(t.Status) } func FromStickyExecutionAttributes(t *types.StickyExecutionAttributes) *apiv1.StickyExecutionAttributes { if t == nil { return nil } return &apiv1.StickyExecutionAttributes{ WorkerTaskList: FromTaskList(t.WorkerTaskList), ScheduleToStartTimeout: secondsToDuration(t.ScheduleToStartTimeoutSeconds), } } func ToStickyExecutionAttributes(t *apiv1.StickyExecutionAttributes) *types.StickyExecutionAttributes { if t == nil { return nil } return &types.StickyExecutionAttributes{ WorkerTaskList: ToTaskList(t.WorkerTaskList), ScheduleToStartTimeoutSeconds: durationToSeconds(t.ScheduleToStartTimeout), } } func FromSupportedClientVersions(t *types.SupportedClientVersions) *apiv1.SupportedClientVersions { if t == nil { return nil } return &apiv1.SupportedClientVersions{ GoSdk: t.GoSdk, JavaSdk: t.JavaSdk, } } func ToSupportedClientVersions(t *apiv1.SupportedClientVersions) *types.SupportedClientVersions { if t == nil { return nil } return &types.SupportedClientVersions{ GoSdk: t.GoSdk, JavaSdk: t.JavaSdk, } } func FromTaskIDBlock(t *types.TaskIDBlock) *apiv1.TaskIDBlock { if t == nil { return nil } return &apiv1.TaskIDBlock{ StartId: t.StartID, EndId: t.EndID, } } func ToTaskIDBlock(t *apiv1.TaskIDBlock) *types.TaskIDBlock { if t == nil { return nil } return &types.TaskIDBlock{ StartID: t.StartId, EndID: t.EndId, } } func MigrateTaskList(name string, t *apiv1.TaskList) *types.TaskList { if t == nil && name != "" { return &types.TaskList{Name: name, Kind: types.TaskListKindNormal.Ptr()} } return ToTaskList(t) } func FromTaskList(t *types.TaskList) *apiv1.TaskList { if t == nil { return nil } return &apiv1.TaskList{ Name: t.Name, Kind: FromTaskListKind(t.Kind), } } func ToTaskList(t *apiv1.TaskList) *types.TaskList { if t == nil { return nil } return &types.TaskList{ Name: t.Name, Kind: ToTaskListKind(t.Kind), } } func FromTaskListKind(t *types.TaskListKind) apiv1.TaskListKind { if t == nil { return apiv1.TaskListKind_TASK_LIST_KIND_INVALID } switch *t { case types.TaskListKindNormal: return apiv1.TaskListKind_TASK_LIST_KIND_NORMAL case types.TaskListKindSticky: return apiv1.TaskListKind_TASK_LIST_KIND_STICKY case types.TaskListKindEphemeral: return apiv1.TaskListKind_TASK_LIST_KIND_EPHEMERAL } return apiv1.TaskListKind_TASK_LIST_KIND_INVALID } func ToTaskListKind(t apiv1.TaskListKind) *types.TaskListKind { switch t { case apiv1.TaskListKind_TASK_LIST_KIND_INVALID: return nil case apiv1.TaskListKind_TASK_LIST_KIND_NORMAL: return types.TaskListKindNormal.Ptr() case apiv1.TaskListKind_TASK_LIST_KIND_STICKY: return types.TaskListKindSticky.Ptr() case apiv1.TaskListKind_TASK_LIST_KIND_EPHEMERAL: return types.TaskListKindEphemeral.Ptr() } return nil } func FromTaskListMetadata(t *types.TaskListMetadata) *apiv1.TaskListMetadata { if t == nil { return nil } return &apiv1.TaskListMetadata{ MaxTasksPerSecond: fromDoubleValue(t.MaxTasksPerSecond), } } func ToTaskListMetadata(t *apiv1.TaskListMetadata) *types.TaskListMetadata { if t == nil { return nil } return &types.TaskListMetadata{ MaxTasksPerSecond: toDoubleValue(t.MaxTasksPerSecond), } } func FromTaskListPartitionMetadata(t *types.TaskListPartitionMetadata) *apiv1.TaskListPartitionMetadata { if t == nil { return nil } return &apiv1.TaskListPartitionMetadata{ Key: t.Key, OwnerHostName: t.OwnerHostName, } } func ToTaskListPartitionMetadata(t *apiv1.TaskListPartitionMetadata) *types.TaskListPartitionMetadata { if t == nil { return nil } return &types.TaskListPartitionMetadata{ Key: t.Key, OwnerHostName: t.OwnerHostName, } } func FromTaskListStatus(t *types.TaskListStatus) *apiv1.TaskListStatus { if t == nil { return nil } return &apiv1.TaskListStatus{ BacklogCountHint: t.BacklogCountHint, ReadLevel: t.ReadLevel, AckLevel: t.AckLevel, RatePerSecond: t.RatePerSecond, TaskIdBlock: FromTaskIDBlock(t.TaskIDBlock), IsolationGroupMetrics: FromIsolationGroupMetricsMap(t.IsolationGroupMetrics), NewTasksPerSecond: t.NewTasksPerSecond, Empty: t.Empty, } } func ToTaskListStatus(t *apiv1.TaskListStatus) *types.TaskListStatus { if t == nil { return nil } return &types.TaskListStatus{ BacklogCountHint: t.BacklogCountHint, ReadLevel: t.ReadLevel, AckLevel: t.AckLevel, RatePerSecond: t.RatePerSecond, TaskIDBlock: ToTaskIDBlock(t.TaskIdBlock), IsolationGroupMetrics: ToIsolationGroupMetricsMap(t.IsolationGroupMetrics), NewTasksPerSecond: t.NewTasksPerSecond, Empty: t.Empty, } } func FromIsolationGroupMetrics(t *types.IsolationGroupMetrics) *apiv1.IsolationGroupMetrics { if t == nil { return nil } return &apiv1.IsolationGroupMetrics{ NewTasksPerSecond: t.NewTasksPerSecond, PollerCount: t.PollerCount, } } func ToIsolationGroupMetrics(t *apiv1.IsolationGroupMetrics) *types.IsolationGroupMetrics { if t == nil { return nil } return &types.IsolationGroupMetrics{ NewTasksPerSecond: t.NewTasksPerSecond, PollerCount: t.PollerCount, } } func FromTaskListType(t *types.TaskListType) apiv1.TaskListType { if t == nil { return apiv1.TaskListType_TASK_LIST_TYPE_INVALID } switch *t { case types.TaskListTypeDecision: return apiv1.TaskListType_TASK_LIST_TYPE_DECISION case types.TaskListTypeActivity: return apiv1.TaskListType_TASK_LIST_TYPE_ACTIVITY } return apiv1.TaskListType_TASK_LIST_TYPE_INVALID } func ToTaskListType(t apiv1.TaskListType) *types.TaskListType { switch t { case apiv1.TaskListType_TASK_LIST_TYPE_INVALID: return nil case apiv1.TaskListType_TASK_LIST_TYPE_DECISION: return types.TaskListTypeDecision.Ptr() case apiv1.TaskListType_TASK_LIST_TYPE_ACTIVITY: return types.TaskListTypeActivity.Ptr() } return nil } func FromTerminateWorkflowExecutionRequest(t *types.TerminateWorkflowExecutionRequest) *apiv1.TerminateWorkflowExecutionRequest { if t == nil { return nil } return &apiv1.TerminateWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Reason: t.Reason, Details: FromPayload(t.Details), Identity: t.Identity, FirstExecutionRunId: t.FirstExecutionRunID, } } func ToTerminateWorkflowExecutionRequest(t *apiv1.TerminateWorkflowExecutionRequest) *types.TerminateWorkflowExecutionRequest { if t == nil { return nil } return &types.TerminateWorkflowExecutionRequest{ Domain: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Reason: t.Reason, Details: ToPayload(t.Details), Identity: t.Identity, FirstExecutionRunID: t.FirstExecutionRunId, } } func FromTimeoutType(t *types.TimeoutType) apiv1.TimeoutType { if t == nil { return apiv1.TimeoutType_TIMEOUT_TYPE_INVALID } switch *t { case types.TimeoutTypeStartToClose: return apiv1.TimeoutType_TIMEOUT_TYPE_START_TO_CLOSE case types.TimeoutTypeScheduleToStart: return apiv1.TimeoutType_TIMEOUT_TYPE_SCHEDULE_TO_START case types.TimeoutTypeScheduleToClose: return apiv1.TimeoutType_TIMEOUT_TYPE_SCHEDULE_TO_CLOSE case types.TimeoutTypeHeartbeat: return apiv1.TimeoutType_TIMEOUT_TYPE_HEARTBEAT } return apiv1.TimeoutType_TIMEOUT_TYPE_INVALID } func ToTimeoutType(t apiv1.TimeoutType) *types.TimeoutType { switch t { case apiv1.TimeoutType_TIMEOUT_TYPE_INVALID: return nil case apiv1.TimeoutType_TIMEOUT_TYPE_START_TO_CLOSE: return types.TimeoutTypeStartToClose.Ptr() case apiv1.TimeoutType_TIMEOUT_TYPE_SCHEDULE_TO_START: return types.TimeoutTypeScheduleToStart.Ptr() case apiv1.TimeoutType_TIMEOUT_TYPE_SCHEDULE_TO_CLOSE: return types.TimeoutTypeScheduleToClose.Ptr() case apiv1.TimeoutType_TIMEOUT_TYPE_HEARTBEAT: return types.TimeoutTypeHeartbeat.Ptr() } return nil } func FromDecisionTaskTimedOutCause(t *types.DecisionTaskTimedOutCause) apiv1.DecisionTaskTimedOutCause { if t == nil { return apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_INVALID } switch *t { case types.DecisionTaskTimedOutCauseTimeout: return apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_TIMEOUT case types.DecisionTaskTimedOutCauseReset: return apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_RESET } return apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_INVALID } func ToDecisionTaskTimedOutCause(t apiv1.DecisionTaskTimedOutCause) *types.DecisionTaskTimedOutCause { switch t { case apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_INVALID: return nil case apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_TIMEOUT: return types.DecisionTaskTimedOutCauseTimeout.Ptr() case apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_RESET: return types.DecisionTaskTimedOutCauseReset.Ptr() } return nil } func FromTimerCanceledEventAttributes(t *types.TimerCanceledEventAttributes) *apiv1.TimerCanceledEventAttributes { if t == nil { return nil } return &apiv1.TimerCanceledEventAttributes{ TimerId: t.TimerID, StartedEventId: t.StartedEventID, DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, Identity: t.Identity, } } func ToTimerCanceledEventAttributes(t *apiv1.TimerCanceledEventAttributes) *types.TimerCanceledEventAttributes { if t == nil { return nil } return &types.TimerCanceledEventAttributes{ TimerID: t.TimerId, StartedEventID: t.StartedEventId, DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, Identity: t.Identity, } } func FromTimerFiredEventAttributes(t *types.TimerFiredEventAttributes) *apiv1.TimerFiredEventAttributes { if t == nil { return nil } return &apiv1.TimerFiredEventAttributes{ TimerId: t.TimerID, StartedEventId: t.StartedEventID, } } func ToTimerFiredEventAttributes(t *apiv1.TimerFiredEventAttributes) *types.TimerFiredEventAttributes { if t == nil { return nil } return &types.TimerFiredEventAttributes{ TimerID: t.TimerId, StartedEventID: t.StartedEventId, } } func FromTimerStartedEventAttributes(t *types.TimerStartedEventAttributes) *apiv1.TimerStartedEventAttributes { if t == nil { return nil } return &apiv1.TimerStartedEventAttributes{ TimerId: t.TimerID, StartToFireTimeout: secondsToDuration(int64To32(t.StartToFireTimeoutSeconds)), DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, } } func ToTimerStartedEventAttributes(t *apiv1.TimerStartedEventAttributes) *types.TimerStartedEventAttributes { if t == nil { return nil } return &types.TimerStartedEventAttributes{ TimerID: t.TimerId, StartToFireTimeoutSeconds: int32To64(durationToSeconds(t.StartToFireTimeout)), DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, } } const ( DomainUpdateDescriptionField = "description" DomainUpdateOwnerEmailField = "owner_email" DomainUpdateDataField = "data" DomainUpdateRetentionPeriodField = "workflow_execution_retention_period" DomainUpdateBadBinariesField = "bad_binaries" DomainUpdateHistoryArchivalStatusField = "history_archival_status" DomainUpdateHistoryArchivalURIField = "history_archival_uri" DomainUpdateVisibilityArchivalStatusField = "visibility_archival_status" DomainUpdateVisibilityArchivalURIField = "visibility_archival_uri" DomainUpdateActiveClusterNameField = "active_cluster_name" DomainUpdateActiveClustersField = "active_clusters" DomainUpdateClustersField = "clusters" DomainUpdateDeleteBadBinaryField = "delete_bad_binary" DomainUpdateFailoverTimeoutField = "failover_timeout" ) func FromUpdateDomainRequest(t *types.UpdateDomainRequest) *apiv1.UpdateDomainRequest { if t == nil { return nil } request := apiv1.UpdateDomainRequest{ Name: t.Name, SecurityToken: t.SecurityToken, } fields := []string{} if t.Description != nil { request.Description = *t.Description fields = append(fields, DomainUpdateDescriptionField) } if t.OwnerEmail != nil { request.OwnerEmail = *t.OwnerEmail fields = append(fields, DomainUpdateOwnerEmailField) } if t.Data != nil { request.Data = t.Data fields = append(fields, DomainUpdateDataField) } if t.WorkflowExecutionRetentionPeriodInDays != nil { request.WorkflowExecutionRetentionPeriod = daysToDuration(t.WorkflowExecutionRetentionPeriodInDays) fields = append(fields, DomainUpdateRetentionPeriodField) } // if t.EmitMetric != nil {} - DEPRECATED if t.BadBinaries != nil { request.BadBinaries = FromBadBinaries(t.BadBinaries) fields = append(fields, DomainUpdateBadBinariesField) } if t.HistoryArchivalStatus != nil { request.HistoryArchivalStatus = FromArchivalStatus(t.HistoryArchivalStatus) fields = append(fields, DomainUpdateHistoryArchivalStatusField) } if t.HistoryArchivalURI != nil { request.HistoryArchivalUri = *t.HistoryArchivalURI fields = append(fields, DomainUpdateHistoryArchivalURIField) } if t.VisibilityArchivalStatus != nil { request.VisibilityArchivalStatus = FromArchivalStatus(t.VisibilityArchivalStatus) fields = append(fields, DomainUpdateVisibilityArchivalStatusField) } if t.VisibilityArchivalURI != nil { request.VisibilityArchivalUri = *t.VisibilityArchivalURI fields = append(fields, DomainUpdateVisibilityArchivalURIField) } if t.ActiveClusterName != nil { request.ActiveClusterName = *t.ActiveClusterName fields = append(fields, DomainUpdateActiveClusterNameField) } if t.ActiveClusters != nil { request.ActiveClusters = FromActiveClusters(t.ActiveClusters) fields = append(fields, DomainUpdateActiveClustersField) } if t.Clusters != nil { request.Clusters = FromClusterReplicationConfigurationArray(t.Clusters) fields = append(fields, DomainUpdateClustersField) } if t.DeleteBadBinary != nil { request.DeleteBadBinary = *t.DeleteBadBinary fields = append(fields, DomainUpdateDeleteBadBinaryField) } if t.FailoverTimeoutInSeconds != nil { request.FailoverTimeout = secondsToDuration(t.FailoverTimeoutInSeconds) fields = append(fields, DomainUpdateFailoverTimeoutField) } request.UpdateMask = newFieldMask(fields) return &request } func ToUpdateDomainRequest(t *apiv1.UpdateDomainRequest) *types.UpdateDomainRequest { if t == nil { return nil } request := types.UpdateDomainRequest{ Name: t.Name, SecurityToken: t.SecurityToken, } fs := newFieldSet(t.UpdateMask) if fs.isSet(DomainUpdateDescriptionField) { request.Description = common.StringPtr(t.Description) } if fs.isSet(DomainUpdateOwnerEmailField) { request.OwnerEmail = common.StringPtr(t.OwnerEmail) } if fs.isSet(DomainUpdateDataField) { request.Data = t.Data } if fs.isSet(DomainUpdateRetentionPeriodField) { request.WorkflowExecutionRetentionPeriodInDays = durationToDays(t.WorkflowExecutionRetentionPeriod) } if fs.isSet(DomainUpdateBadBinariesField) { request.BadBinaries = ToBadBinaries(t.BadBinaries) } if fs.isSet(DomainUpdateHistoryArchivalStatusField) { request.HistoryArchivalStatus = ToArchivalStatus(t.HistoryArchivalStatus) } if fs.isSet(DomainUpdateHistoryArchivalURIField) { request.HistoryArchivalURI = common.StringPtr(t.HistoryArchivalUri) } if fs.isSet(DomainUpdateVisibilityArchivalStatusField) { request.VisibilityArchivalStatus = ToArchivalStatus(t.VisibilityArchivalStatus) } if fs.isSet(DomainUpdateVisibilityArchivalURIField) { request.VisibilityArchivalURI = common.StringPtr(t.VisibilityArchivalUri) } if fs.isSet(DomainUpdateActiveClusterNameField) { request.ActiveClusterName = common.StringPtr(t.ActiveClusterName) } if fs.isSet(DomainUpdateActiveClustersField) { request.ActiveClusters = ToActiveClusters(t.ActiveClusters) } if fs.isSet(DomainUpdateClustersField) { request.Clusters = ToClusterReplicationConfigurationArray(t.Clusters) } if fs.isSet(DomainUpdateDeleteBadBinaryField) { request.DeleteBadBinary = common.StringPtr(t.DeleteBadBinary) } if fs.isSet(DomainUpdateFailoverTimeoutField) { request.FailoverTimeoutInSeconds = durationToSeconds(t.FailoverTimeout) } return &request } func FromUpdateDomainResponse(t *types.UpdateDomainResponse) *apiv1.UpdateDomainResponse { if t == nil { return nil } domain := &apiv1.Domain{ FailoverVersion: t.FailoverVersion, IsGlobalDomain: t.IsGlobalDomain, } if info := t.DomainInfo; info != nil { domain.Id = info.UUID domain.Name = info.Name domain.Status = FromDomainStatus(info.Status) domain.Description = info.Description domain.OwnerEmail = info.OwnerEmail domain.Data = info.Data } if config := t.Configuration; config != nil { domain.IsolationGroups = FromIsolationGroupConfig(config.IsolationGroups) domain.WorkflowExecutionRetentionPeriod = daysToDuration(&config.WorkflowExecutionRetentionPeriodInDays) domain.BadBinaries = FromBadBinaries(config.BadBinaries) domain.HistoryArchivalStatus = FromArchivalStatus(config.HistoryArchivalStatus) domain.HistoryArchivalUri = config.HistoryArchivalURI domain.VisibilityArchivalStatus = FromArchivalStatus(config.VisibilityArchivalStatus) domain.VisibilityArchivalUri = config.VisibilityArchivalURI domain.AsyncWorkflowConfig = FromDomainAsyncWorkflowConfiguraton(config.AsyncWorkflowConfig) } if repl := t.ReplicationConfiguration; repl != nil { domain.ActiveClusterName = repl.ActiveClusterName domain.Clusters = FromClusterReplicationConfigurationArray(repl.Clusters) domain.ActiveClusters = FromActiveClusters(repl.ActiveClusters) } return &apiv1.UpdateDomainResponse{ Domain: domain, } } func ToUpdateDomainResponse(t *apiv1.UpdateDomainResponse) *types.UpdateDomainResponse { if t == nil || t.Domain == nil { return nil } return &types.UpdateDomainResponse{ DomainInfo: &types.DomainInfo{ Name: t.Domain.Name, Status: ToDomainStatus(t.Domain.Status), Description: t.Domain.Description, OwnerEmail: t.Domain.OwnerEmail, Data: t.Domain.Data, UUID: t.Domain.Id, }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: common.Int32Default(durationToDays(t.Domain.WorkflowExecutionRetentionPeriod)), EmitMetric: true, BadBinaries: ToBadBinaries(t.Domain.BadBinaries), HistoryArchivalStatus: ToArchivalStatus(t.Domain.HistoryArchivalStatus), HistoryArchivalURI: t.Domain.HistoryArchivalUri, VisibilityArchivalStatus: ToArchivalStatus(t.Domain.VisibilityArchivalStatus), VisibilityArchivalURI: t.Domain.VisibilityArchivalUri, IsolationGroups: ToIsolationGroupConfig(t.Domain.IsolationGroups), AsyncWorkflowConfig: ToDomainAsyncWorkflowConfiguraton(t.Domain.AsyncWorkflowConfig), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: t.Domain.ActiveClusterName, Clusters: ToClusterReplicationConfigurationArray(t.Domain.Clusters), ActiveClusters: ToActiveClusters(t.Domain.ActiveClusters), }, FailoverVersion: t.Domain.FailoverVersion, IsGlobalDomain: t.Domain.IsGlobalDomain, } } func FromFailoverDomainRequest(t *types.FailoverDomainRequest) *apiv1.FailoverDomainRequest { if t == nil { return nil } return &apiv1.FailoverDomainRequest{ DomainName: t.DomainName, DomainActiveClusterName: t.GetDomainActiveClusterName(), ActiveClusters: FromActiveClusters(t.ActiveClusters), } } func ToFailoverDomainRequest(t *apiv1.FailoverDomainRequest) *types.FailoverDomainRequest { if t == nil { return nil } var domainActiveClusterName *string if t.DomainActiveClusterName != "" { domainActiveClusterName = common.StringPtr(t.DomainActiveClusterName) } return &types.FailoverDomainRequest{ DomainName: t.DomainName, DomainActiveClusterName: domainActiveClusterName, ActiveClusters: ToActiveClusters(t.ActiveClusters), } } func FromFailoverDomainResponse(t *types.FailoverDomainResponse) *apiv1.FailoverDomainResponse { if t == nil { return nil } domain := &apiv1.Domain{ FailoverVersion: t.FailoverVersion, IsGlobalDomain: t.IsGlobalDomain, } if info := t.DomainInfo; info != nil { domain.Id = info.UUID domain.Name = info.Name domain.Status = FromDomainStatus(info.Status) domain.Description = info.Description domain.OwnerEmail = info.OwnerEmail domain.Data = info.Data } if config := t.Configuration; config != nil { domain.IsolationGroups = FromIsolationGroupConfig(config.IsolationGroups) domain.WorkflowExecutionRetentionPeriod = daysToDuration(&config.WorkflowExecutionRetentionPeriodInDays) domain.BadBinaries = FromBadBinaries(config.BadBinaries) domain.HistoryArchivalStatus = FromArchivalStatus(config.HistoryArchivalStatus) domain.HistoryArchivalUri = config.HistoryArchivalURI domain.VisibilityArchivalStatus = FromArchivalStatus(config.VisibilityArchivalStatus) domain.VisibilityArchivalUri = config.VisibilityArchivalURI domain.AsyncWorkflowConfig = FromDomainAsyncWorkflowConfiguraton(config.AsyncWorkflowConfig) } if repl := t.ReplicationConfiguration; repl != nil { domain.ActiveClusterName = repl.ActiveClusterName domain.Clusters = FromClusterReplicationConfigurationArray(repl.Clusters) domain.ActiveClusters = FromActiveClusters(repl.ActiveClusters) } return &apiv1.FailoverDomainResponse{ Domain: domain, } } func ToFailoverDomainResponse(t *apiv1.FailoverDomainResponse) *types.FailoverDomainResponse { if t == nil || t.Domain == nil { return nil } return &types.FailoverDomainResponse{ DomainInfo: &types.DomainInfo{ Name: t.Domain.Name, Status: ToDomainStatus(t.Domain.Status), Description: t.Domain.Description, OwnerEmail: t.Domain.OwnerEmail, Data: t.Domain.Data, UUID: t.Domain.Id, }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: common.Int32Default(durationToDays(t.Domain.WorkflowExecutionRetentionPeriod)), EmitMetric: true, BadBinaries: ToBadBinaries(t.Domain.BadBinaries), HistoryArchivalStatus: ToArchivalStatus(t.Domain.HistoryArchivalStatus), HistoryArchivalURI: t.Domain.HistoryArchivalUri, VisibilityArchivalStatus: ToArchivalStatus(t.Domain.VisibilityArchivalStatus), VisibilityArchivalURI: t.Domain.VisibilityArchivalUri, IsolationGroups: ToIsolationGroupConfig(t.Domain.IsolationGroups), AsyncWorkflowConfig: ToDomainAsyncWorkflowConfiguraton(t.Domain.AsyncWorkflowConfig), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: t.Domain.ActiveClusterName, Clusters: ToClusterReplicationConfigurationArray(t.Domain.Clusters), ActiveClusters: ToActiveClusters(t.Domain.ActiveClusters), }, FailoverVersion: t.Domain.FailoverVersion, IsGlobalDomain: t.Domain.IsGlobalDomain, } } func FromListFailoverHistoryRequest(t *types.ListFailoverHistoryRequest) *apiv1.ListFailoverHistoryRequest { if t == nil { return nil } return &apiv1.ListFailoverHistoryRequest{ Filters: FromListFailoverHistoryRequestFilters(t.Filters), Pagination: FromPaginationOptions(t.Pagination), } } func ToListFailoverHistoryRequest(t *apiv1.ListFailoverHistoryRequest) *types.ListFailoverHistoryRequest { if t == nil { return nil } return &types.ListFailoverHistoryRequest{ Filters: ToListFailoverHistoryRequestFilters(t.Filters), Pagination: ToPaginationOptions(t.Pagination), } } func ToListFailoverHistoryResponse(t *apiv1.ListFailoverHistoryResponse) *types.ListFailoverHistoryResponse { if t == nil { return nil } return &types.ListFailoverHistoryResponse{ FailoverEvents: ToFailoverEventArray(t.FailoverEvents), NextPageToken: t.NextPageToken, } } func FromListFailoverHistoryResponse(t *types.ListFailoverHistoryResponse) *apiv1.ListFailoverHistoryResponse { if t == nil { return nil } return &apiv1.ListFailoverHistoryResponse{ FailoverEvents: FromFailoverEventArray(t.FailoverEvents), NextPageToken: t.NextPageToken, } } func FromListFailoverHistoryRequestFilters(t *types.ListFailoverHistoryRequestFilters) *apiv1.ListFailoverHistoryRequestFilters { if t == nil { return nil } return &apiv1.ListFailoverHistoryRequestFilters{ DomainId: t.DomainID, } } func ToListFailoverHistoryRequestFilters(t *apiv1.ListFailoverHistoryRequestFilters) *types.ListFailoverHistoryRequestFilters { if t == nil { return nil } return &types.ListFailoverHistoryRequestFilters{ DomainID: t.DomainId, } } func FromPaginationOptions(t *types.PaginationOptions) *apiv1.PaginationOptions { if t == nil { return nil } pageSize := int32(0) if t.PageSize != nil { pageSize = *t.PageSize } return &apiv1.PaginationOptions{ PageSize: pageSize, NextPageToken: t.NextPageToken, } } func ToPaginationOptions(t *apiv1.PaginationOptions) *types.PaginationOptions { if t == nil { return nil } var pageSize *int32 if t.PageSize != 0 { pageSize = common.Int32Ptr(t.PageSize) } return &types.PaginationOptions{ PageSize: pageSize, NextPageToken: t.NextPageToken, } } func FromFailoverEvent(t *types.FailoverEvent) *apiv1.FailoverEvent { if t == nil { return nil } return &apiv1.FailoverEvent{ Id: t.GetID(), CreatedTime: unixNanoToTime(t.CreatedTime), FailoverType: FromFailoverType(t.FailoverType), ClusterFailovers: FromClusterFailoverArray(t.ClusterFailovers), } } func ToFailoverEvent(t *apiv1.FailoverEvent) *types.FailoverEvent { if t == nil { return nil } var id *string if t.Id != "" { id = common.StringPtr(t.Id) } return &types.FailoverEvent{ ID: id, CreatedTime: timeToUnixNano(t.CreatedTime), FailoverType: ToFailoverType(t.FailoverType), ClusterFailovers: ToClusterFailoverArray(t.ClusterFailovers), } } func FromFailoverEventArray(t []*types.FailoverEvent) []*apiv1.FailoverEvent { if t == nil { return nil } v := make([]*apiv1.FailoverEvent, len(t)) for i := range t { v[i] = FromFailoverEvent(t[i]) } return v } func ToFailoverEventArray(t []*apiv1.FailoverEvent) []*types.FailoverEvent { if t == nil { return nil } v := make([]*types.FailoverEvent, len(t)) for i := range t { v[i] = ToFailoverEvent(t[i]) } return v } func FromClusterFailover(t *types.ClusterFailover) *apiv1.ClusterFailover { if t == nil { return nil } return &apiv1.ClusterFailover{ FromCluster: FromActiveClusterInfo(t.FromCluster), ToCluster: FromActiveClusterInfo(t.ToCluster), ClusterAttribute: FromClusterAttribute(t.ClusterAttribute), } } func ToClusterFailover(t *apiv1.ClusterFailover) *types.ClusterFailover { if t == nil { return nil } return &types.ClusterFailover{ FromCluster: ToActiveClusterInfo(t.FromCluster), ToCluster: ToActiveClusterInfo(t.ToCluster), ClusterAttribute: ToClusterAttribute(t.ClusterAttribute), } } func FromClusterFailoverArray(t []*types.ClusterFailover) []*apiv1.ClusterFailover { if t == nil { return nil } v := make([]*apiv1.ClusterFailover, len(t)) for i := range t { v[i] = FromClusterFailover(t[i]) } return v } func ToClusterFailoverArray(t []*apiv1.ClusterFailover) []*types.ClusterFailover { if t == nil { return nil } v := make([]*types.ClusterFailover, len(t)) for i := range t { v[i] = ToClusterFailover(t[i]) } return v } func FromActiveClusterInfo(t *types.ActiveClusterInfo) *apiv1.ActiveClusterInfo { if t == nil { return nil } return &apiv1.ActiveClusterInfo{ ActiveClusterName: t.ActiveClusterName, FailoverVersion: t.FailoverVersion, } } func ToActiveClusterInfo(t *apiv1.ActiveClusterInfo) *types.ActiveClusterInfo { if t == nil { return nil } return &types.ActiveClusterInfo{ ActiveClusterName: t.ActiveClusterName, FailoverVersion: t.FailoverVersion, } } func FromFailoverType(t *types.FailoverType) apiv1.FailoverType { if t == nil { return apiv1.FailoverType_FAILOVER_TYPE_INVALID } switch *t { case types.FailoverTypeForce: return apiv1.FailoverType_FAILOVER_TYPE_FORCE case types.FailoverTypeGraceful: return apiv1.FailoverType_FAILOVER_TYPE_GRACEFUL } return apiv1.FailoverType_FAILOVER_TYPE_INVALID } func ToFailoverType(t apiv1.FailoverType) *types.FailoverType { switch t { case apiv1.FailoverType_FAILOVER_TYPE_FORCE: return types.FailoverTypeForce.Ptr() case apiv1.FailoverType_FAILOVER_TYPE_GRACEFUL: return types.FailoverTypeGraceful.Ptr() default: // For FAILOVER_TYPE_INVALID and unknown values, return nil return nil } } func FromUpsertWorkflowSearchAttributesDecisionAttributes(t *types.UpsertWorkflowSearchAttributesDecisionAttributes) *apiv1.UpsertWorkflowSearchAttributesDecisionAttributes { if t == nil { return nil } return &apiv1.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: FromSearchAttributes(t.SearchAttributes), } } func ToUpsertWorkflowSearchAttributesDecisionAttributes(t *apiv1.UpsertWorkflowSearchAttributesDecisionAttributes) *types.UpsertWorkflowSearchAttributesDecisionAttributes { if t == nil { return nil } return &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: ToSearchAttributes(t.SearchAttributes), } } func FromUpsertWorkflowSearchAttributesEventAttributes(t *types.UpsertWorkflowSearchAttributesEventAttributes) *apiv1.UpsertWorkflowSearchAttributesEventAttributes { if t == nil { return nil } return &apiv1.UpsertWorkflowSearchAttributesEventAttributes{ DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, SearchAttributes: FromSearchAttributes(t.SearchAttributes), } } func ToUpsertWorkflowSearchAttributesEventAttributes(t *apiv1.UpsertWorkflowSearchAttributesEventAttributes) *types.UpsertWorkflowSearchAttributesEventAttributes { if t == nil { return nil } return &types.UpsertWorkflowSearchAttributesEventAttributes{ DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, SearchAttributes: ToSearchAttributes(t.SearchAttributes), } } func FromWorkerVersionInfo(t *types.WorkerVersionInfo) *apiv1.WorkerVersionInfo { if t == nil { return nil } return &apiv1.WorkerVersionInfo{ Impl: t.Impl, FeatureVersion: t.FeatureVersion, } } func ToWorkerVersionInfo(t *apiv1.WorkerVersionInfo) *types.WorkerVersionInfo { if t == nil { return nil } return &types.WorkerVersionInfo{ Impl: t.Impl, FeatureVersion: t.FeatureVersion, } } func FromWorkflowRunPair(workflowID, runID string) *apiv1.WorkflowExecution { return &apiv1.WorkflowExecution{ WorkflowId: workflowID, RunId: runID, } } func ToWorkflowID(t *apiv1.WorkflowExecution) string { if t == nil { return "" } return t.WorkflowId } func ToRunID(t *apiv1.WorkflowExecution) string { if t == nil { return "" } return t.RunId } func FromWorkflowExecution(t *types.WorkflowExecution) *apiv1.WorkflowExecution { if t == nil { return nil } return &apiv1.WorkflowExecution{ WorkflowId: t.WorkflowID, RunId: t.RunID, } } func ToWorkflowExecution(t *apiv1.WorkflowExecution) *types.WorkflowExecution { if t == nil { return nil } return &types.WorkflowExecution{ WorkflowID: t.WorkflowId, RunID: t.RunId, } } func FromExternalExecutionInfoFields(we *types.WorkflowExecution, initiatedID *int64) *apiv1.ExternalExecutionInfo { if we == nil && initiatedID == nil { return nil } return &apiv1.ExternalExecutionInfo{ WorkflowExecution: FromWorkflowExecution(we), InitiatedId: common.Int64Default(initiatedID), // TODO: we need to figure out whetherrr this field is needed or not } } func ToExternalWorkflowExecution(t *apiv1.ExternalExecutionInfo) *types.WorkflowExecution { if t == nil { return nil } return ToWorkflowExecution(t.WorkflowExecution) } func ToExternalInitiatedID(t *apiv1.ExternalExecutionInfo) *int64 { if t == nil { return nil } return &t.InitiatedId } func FromWorkflowExecutionCancelRequestedEventAttributes(t *types.WorkflowExecutionCancelRequestedEventAttributes) *apiv1.WorkflowExecutionCancelRequestedEventAttributes { if t == nil { return nil } return &apiv1.WorkflowExecutionCancelRequestedEventAttributes{ Cause: t.Cause, ExternalExecutionInfo: FromExternalExecutionInfoFields(t.ExternalWorkflowExecution, t.ExternalInitiatedEventID), Identity: t.Identity, RequestId: t.RequestID, } } func ToWorkflowExecutionCancelRequestedEventAttributes(t *apiv1.WorkflowExecutionCancelRequestedEventAttributes) *types.WorkflowExecutionCancelRequestedEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionCancelRequestedEventAttributes{ Cause: t.Cause, ExternalInitiatedEventID: ToExternalInitiatedID(t.ExternalExecutionInfo), ExternalWorkflowExecution: ToExternalWorkflowExecution(t.ExternalExecutionInfo), Identity: t.Identity, RequestID: t.RequestId, } } func FromWorkflowExecutionCanceledEventAttributes(t *types.WorkflowExecutionCanceledEventAttributes) *apiv1.WorkflowExecutionCanceledEventAttributes { if t == nil { return nil } return &apiv1.WorkflowExecutionCanceledEventAttributes{ DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, Details: FromPayload(t.Details), } } func ToWorkflowExecutionCanceledEventAttributes(t *apiv1.WorkflowExecutionCanceledEventAttributes) *types.WorkflowExecutionCanceledEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionCanceledEventAttributes{ DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, Details: ToPayload(t.Details), } } func FromWorkflowExecutionCloseStatus(t *types.WorkflowExecutionCloseStatus) apiv1.WorkflowExecutionCloseStatus { if t == nil { return apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID } switch *t { case types.WorkflowExecutionCloseStatusCompleted: return apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_COMPLETED case types.WorkflowExecutionCloseStatusFailed: return apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_FAILED case types.WorkflowExecutionCloseStatusCanceled: return apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_CANCELED case types.WorkflowExecutionCloseStatusTerminated: return apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_TERMINATED case types.WorkflowExecutionCloseStatusContinuedAsNew: return apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_CONTINUED_AS_NEW case types.WorkflowExecutionCloseStatusTimedOut: return apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_TIMED_OUT } return apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID } func ToWorkflowExecutionCloseStatus(t apiv1.WorkflowExecutionCloseStatus) *types.WorkflowExecutionCloseStatus { switch t { case apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID: return nil case apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_COMPLETED: return types.WorkflowExecutionCloseStatusCompleted.Ptr() case apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_FAILED: return types.WorkflowExecutionCloseStatusFailed.Ptr() case apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_CANCELED: return types.WorkflowExecutionCloseStatusCanceled.Ptr() case apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_TERMINATED: return types.WorkflowExecutionCloseStatusTerminated.Ptr() case apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_CONTINUED_AS_NEW: return types.WorkflowExecutionCloseStatusContinuedAsNew.Ptr() case apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_TIMED_OUT: return types.WorkflowExecutionCloseStatusTimedOut.Ptr() } return nil } func FromWorkflowExecutionStatus(t *types.WorkflowExecutionStatus) apiv1.WorkflowExecutionStatus { if t == nil { return apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_INVALID } switch *t { case types.WorkflowExecutionStatusPending: return apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_PENDING case types.WorkflowExecutionStatusStarted: return apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_STARTED case types.WorkflowExecutionStatusCompleted: return apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_COMPLETED case types.WorkflowExecutionStatusFailed: return apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_FAILED case types.WorkflowExecutionStatusCanceled: return apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_CANCELED case types.WorkflowExecutionStatusTerminated: return apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_TERMINATED case types.WorkflowExecutionStatusContinuedAsNew: return apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW case types.WorkflowExecutionStatusTimedOut: return apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_TIMED_OUT } return apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_INVALID } func ToWorkflowExecutionStatus(t apiv1.WorkflowExecutionStatus) *types.WorkflowExecutionStatus { switch t { case apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_INVALID: return nil case apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_PENDING: return types.WorkflowExecutionStatusPending.Ptr() case apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_STARTED: return types.WorkflowExecutionStatusStarted.Ptr() case apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_COMPLETED: return types.WorkflowExecutionStatusCompleted.Ptr() case apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_FAILED: return types.WorkflowExecutionStatusFailed.Ptr() case apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_CANCELED: return types.WorkflowExecutionStatusCanceled.Ptr() case apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_TERMINATED: return types.WorkflowExecutionStatusTerminated.Ptr() case apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW: return types.WorkflowExecutionStatusContinuedAsNew.Ptr() case apiv1.WorkflowExecutionStatus_WORKFLOW_EXECUTION_STATUS_TIMED_OUT: return types.WorkflowExecutionStatusTimedOut.Ptr() } return nil } func FromWorkflowExecutionCompletedEventAttributes(t *types.WorkflowExecutionCompletedEventAttributes) *apiv1.WorkflowExecutionCompletedEventAttributes { if t == nil { return nil } return &apiv1.WorkflowExecutionCompletedEventAttributes{ Result: FromPayload(t.Result), DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, } } func ToWorkflowExecutionCompletedEventAttributes(t *apiv1.WorkflowExecutionCompletedEventAttributes) *types.WorkflowExecutionCompletedEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionCompletedEventAttributes{ Result: ToPayload(t.Result), DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, } } func FromWorkflowExecutionConfiguration(t *types.WorkflowExecutionConfiguration) *apiv1.WorkflowExecutionConfiguration { if t == nil { return nil } return &apiv1.WorkflowExecutionConfiguration{ TaskList: FromTaskList(t.TaskList), ExecutionStartToCloseTimeout: secondsToDuration(t.ExecutionStartToCloseTimeoutSeconds), TaskStartToCloseTimeout: secondsToDuration(t.TaskStartToCloseTimeoutSeconds), } } func ToWorkflowExecutionConfiguration(t *apiv1.WorkflowExecutionConfiguration) *types.WorkflowExecutionConfiguration { if t == nil { return nil } return &types.WorkflowExecutionConfiguration{ TaskList: ToTaskList(t.TaskList), ExecutionStartToCloseTimeoutSeconds: durationToSeconds(t.ExecutionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: durationToSeconds(t.TaskStartToCloseTimeout), } } func FromWorkflowExecutionContinuedAsNewEventAttributes(t *types.WorkflowExecutionContinuedAsNewEventAttributes) *apiv1.WorkflowExecutionContinuedAsNewEventAttributes { if t == nil { return nil } return &apiv1.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunId: t.NewExecutionRunID, WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: FromPayload(t.Input), ExecutionStartToCloseTimeout: secondsToDuration(t.ExecutionStartToCloseTimeoutSeconds), TaskStartToCloseTimeout: secondsToDuration(t.TaskStartToCloseTimeoutSeconds), DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, BackoffStartInterval: secondsToDuration(t.BackoffStartIntervalInSeconds), Initiator: FromContinueAsNewInitiator(t.Initiator), Failure: FromFailure(t.FailureReason, t.FailureDetails), LastCompletionResult: FromPayload(t.LastCompletionResult), Header: FromHeader(t.Header), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), } } func ToWorkflowExecutionContinuedAsNewEventAttributes(t *apiv1.WorkflowExecutionContinuedAsNewEventAttributes) *types.WorkflowExecutionContinuedAsNewEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: t.NewExecutionRunId, WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: ToPayload(t.Input), ExecutionStartToCloseTimeoutSeconds: durationToSeconds(t.ExecutionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: durationToSeconds(t.TaskStartToCloseTimeout), DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, BackoffStartIntervalInSeconds: durationToSeconds(t.BackoffStartInterval), Initiator: ToContinueAsNewInitiator(t.Initiator), FailureReason: ToFailureReason(t.Failure), FailureDetails: ToFailureDetails(t.Failure), LastCompletionResult: ToPayload(t.LastCompletionResult), Header: ToHeader(t.Header), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), } } func FromWorkflowExecutionFailedEventAttributes(t *types.WorkflowExecutionFailedEventAttributes) *apiv1.WorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &apiv1.WorkflowExecutionFailedEventAttributes{ Failure: FromFailure(t.Reason, t.Details), DecisionTaskCompletedEventId: t.DecisionTaskCompletedEventID, } } func ToWorkflowExecutionFailedEventAttributes(t *apiv1.WorkflowExecutionFailedEventAttributes) *types.WorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionFailedEventAttributes{ Reason: ToFailureReason(t.Failure), Details: ToFailureDetails(t.Failure), DecisionTaskCompletedEventID: t.DecisionTaskCompletedEventId, } } func FromWorkflowExecutionFilter(t *types.WorkflowExecutionFilter) *apiv1.WorkflowExecutionFilter { if t == nil { return nil } return &apiv1.WorkflowExecutionFilter{ WorkflowId: t.WorkflowID, RunId: t.RunID, } } func ToWorkflowExecutionFilter(t *apiv1.WorkflowExecutionFilter) *types.WorkflowExecutionFilter { if t == nil { return nil } return &types.WorkflowExecutionFilter{ WorkflowID: t.WorkflowId, RunID: t.RunId, } } func FromParentExecutionInfo(t *types.ParentExecutionInfo) *apiv1.ParentExecutionInfo { if t == nil { return nil } return &apiv1.ParentExecutionInfo{ DomainId: t.DomainUUID, DomainName: t.Domain, WorkflowExecution: FromWorkflowExecution(t.Execution), InitiatedId: t.InitiatedID, } } func ToParentExecutionInfo(t *apiv1.ParentExecutionInfo) *types.ParentExecutionInfo { if t == nil { return nil } return &types.ParentExecutionInfo{ DomainUUID: t.DomainId, Domain: t.DomainName, Execution: ToWorkflowExecution(t.WorkflowExecution), InitiatedID: t.InitiatedId, } } func FromParentExecutionInfoFields(domainID, domainName *string, we *types.WorkflowExecution, initiatedID *int64) *apiv1.ParentExecutionInfo { if domainID == nil && domainName == nil && we == nil && initiatedID == nil { return nil } // ParentExecutionInfo wrapper was added to unify parent related fields. // However some fields may not be present: // - on older histories // - if conversion involves thrift data types // Fallback to zero values in those cases return &apiv1.ParentExecutionInfo{ DomainId: common.StringDefault(domainID), DomainName: common.StringDefault(domainName), WorkflowExecution: FromWorkflowExecution(we), InitiatedId: common.Int64Default(initiatedID), } } func ToParentDomainID(pei *apiv1.ParentExecutionInfo) *string { if pei == nil { return nil } return &pei.DomainId } func ToParentDomainName(pei *apiv1.ParentExecutionInfo) *string { if pei == nil { return nil } return &pei.DomainName } func ToParentWorkflowExecution(pei *apiv1.ParentExecutionInfo) *types.WorkflowExecution { if pei == nil { return nil } return ToWorkflowExecution(pei.WorkflowExecution) } func ToParentInitiatedID(pei *apiv1.ParentExecutionInfo) *int64 { if pei == nil { return nil } return &pei.InitiatedId } func FromWorkflowExecutionInfo(t *types.WorkflowExecutionInfo) *apiv1.WorkflowExecutionInfo { if t == nil { return nil } tlName := "" if t.TaskList != nil { tlName = t.TaskList.Name } cronSchedule := "" if t.CronSchedule != nil { cronSchedule = *t.CronSchedule } return &apiv1.WorkflowExecutionInfo{ WorkflowExecution: FromWorkflowExecution(t.Execution), Type: FromWorkflowType(t.Type), StartTime: unixNanoToTime(t.StartTime), CloseTime: unixNanoToTime(t.CloseTime), CloseStatus: FromWorkflowExecutionCloseStatus(t.CloseStatus), HistoryLength: t.HistoryLength, ParentExecutionInfo: FromParentExecutionInfoFields(t.ParentDomainID, t.ParentDomain, t.ParentExecution, t.ParentInitiatedID), ExecutionTime: unixNanoToTime(t.ExecutionTime), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), AutoResetPoints: FromResetPoints(t.AutoResetPoints), TaskList: tlName, TaskListInfo: FromTaskList(t.TaskList), PartitionConfig: t.PartitionConfig, IsCron: t.IsCron, CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronSchedule: cronSchedule, ExecutionStatus: FromWorkflowExecutionStatus(t.ExecutionStatus), ScheduledExecutionTime: unixNanoToTime(t.ScheduledExecutionTime), } } func ToWorkflowExecutionInfo(t *apiv1.WorkflowExecutionInfo) *types.WorkflowExecutionInfo { if t == nil { return nil } var cronSchedule *string if t.CronSchedule != "" { cronSchedule = &t.CronSchedule } return &types.WorkflowExecutionInfo{ Execution: ToWorkflowExecution(t.WorkflowExecution), Type: ToWorkflowType(t.Type), StartTime: timeToUnixNano(t.StartTime), CloseTime: timeToUnixNano(t.CloseTime), CloseStatus: ToWorkflowExecutionCloseStatus(t.CloseStatus), HistoryLength: t.HistoryLength, ParentDomainID: ToParentDomainID(t.ParentExecutionInfo), ParentDomain: ToParentDomainName(t.ParentExecutionInfo), ParentExecution: ToParentWorkflowExecution(t.ParentExecutionInfo), ParentInitiatedID: ToParentInitiatedID(t.ParentExecutionInfo), ExecutionTime: timeToUnixNano(t.ExecutionTime), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), AutoResetPoints: ToResetPoints(t.AutoResetPoints), TaskList: MigrateTaskList(t.TaskList, t.TaskListInfo), PartitionConfig: t.PartitionConfig, IsCron: t.IsCron, CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronSchedule: cronSchedule, ExecutionStatus: ToWorkflowExecutionStatus(t.ExecutionStatus), ScheduledExecutionTime: timeToUnixNano(t.ScheduledExecutionTime), } } func FromWorkflowExecutionSignaledEventAttributes(t *types.WorkflowExecutionSignaledEventAttributes) *apiv1.WorkflowExecutionSignaledEventAttributes { if t == nil { return nil } return &apiv1.WorkflowExecutionSignaledEventAttributes{ SignalName: t.SignalName, Input: FromPayload(t.Input), Identity: t.Identity, RequestId: t.RequestID, } } func ToWorkflowExecutionSignaledEventAttributes(t *apiv1.WorkflowExecutionSignaledEventAttributes) *types.WorkflowExecutionSignaledEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionSignaledEventAttributes{ SignalName: t.SignalName, Input: ToPayload(t.Input), Identity: t.Identity, RequestID: t.RequestId, } } func FromWorkflowExecutionStartedEventAttributes(t *types.WorkflowExecutionStartedEventAttributes) *apiv1.WorkflowExecutionStartedEventAttributes { if t == nil { return nil } return &apiv1.WorkflowExecutionStartedEventAttributes{ WorkflowType: FromWorkflowType(t.WorkflowType), ParentExecutionInfo: FromParentExecutionInfoFields(t.ParentWorkflowDomainID, t.ParentWorkflowDomain, t.ParentWorkflowExecution, t.ParentInitiatedEventID), TaskList: FromTaskList(t.TaskList), Input: FromPayload(t.Input), ExecutionStartToCloseTimeout: secondsToDuration(t.ExecutionStartToCloseTimeoutSeconds), TaskStartToCloseTimeout: secondsToDuration(t.TaskStartToCloseTimeoutSeconds), ContinuedExecutionRunId: t.ContinuedExecutionRunID, Initiator: FromContinueAsNewInitiator(t.Initiator), ContinuedFailure: FromFailure(t.ContinuedFailureReason, t.ContinuedFailureDetails), LastCompletionResult: FromPayload(t.LastCompletionResult), OriginalExecutionRunId: t.OriginalExecutionRunID, Identity: t.Identity, FirstExecutionRunId: t.FirstExecutionRunID, FirstScheduledTime: timeToTimestamp(t.FirstScheduleTime), RetryPolicy: FromRetryPolicy(t.RetryPolicy), Attempt: t.Attempt, ExpirationTime: unixNanoToTime(t.ExpirationTimestamp), CronSchedule: t.CronSchedule, FirstDecisionTaskBackoff: secondsToDuration(t.FirstDecisionTaskBackoffSeconds), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), PrevAutoResetPoints: FromResetPoints(t.PrevAutoResetPoints), Header: FromHeader(t.Header), PartitionConfig: t.PartitionConfig, RequestId: t.RequestID, CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } func ToWorkflowExecutionStartedEventAttributes(t *apiv1.WorkflowExecutionStartedEventAttributes) *types.WorkflowExecutionStartedEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: ToWorkflowType(t.WorkflowType), ParentWorkflowDomainID: ToParentDomainID(t.ParentExecutionInfo), ParentWorkflowDomain: ToParentDomainName(t.ParentExecutionInfo), ParentWorkflowExecution: ToParentWorkflowExecution(t.ParentExecutionInfo), ParentInitiatedEventID: ToParentInitiatedID(t.ParentExecutionInfo), TaskList: ToTaskList(t.TaskList), Input: ToPayload(t.Input), ExecutionStartToCloseTimeoutSeconds: durationToSeconds(t.ExecutionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: durationToSeconds(t.TaskStartToCloseTimeout), ContinuedExecutionRunID: t.ContinuedExecutionRunId, Initiator: ToContinueAsNewInitiator(t.Initiator), ContinuedFailureReason: ToFailureReason(t.ContinuedFailure), ContinuedFailureDetails: ToFailureDetails(t.ContinuedFailure), LastCompletionResult: ToPayload(t.LastCompletionResult), OriginalExecutionRunID: t.OriginalExecutionRunId, Identity: t.Identity, FirstExecutionRunID: t.FirstExecutionRunId, FirstScheduleTime: timestampToTime(t.FirstScheduledTime), RetryPolicy: ToRetryPolicy(t.RetryPolicy), Attempt: t.Attempt, ExpirationTimestamp: timeToUnixNano(t.ExpirationTime), CronSchedule: t.CronSchedule, FirstDecisionTaskBackoffSeconds: durationToSeconds(t.FirstDecisionTaskBackoff), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), PrevAutoResetPoints: ToResetPoints(t.PrevAutoResetPoints), Header: ToHeader(t.Header), PartitionConfig: t.PartitionConfig, RequestID: t.RequestId, CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } func FromWorkflowExecutionTerminatedEventAttributes(t *types.WorkflowExecutionTerminatedEventAttributes) *apiv1.WorkflowExecutionTerminatedEventAttributes { if t == nil { return nil } return &apiv1.WorkflowExecutionTerminatedEventAttributes{ Reason: t.Reason, Details: FromPayload(t.Details), Identity: t.Identity, } } func ToWorkflowExecutionTerminatedEventAttributes(t *apiv1.WorkflowExecutionTerminatedEventAttributes) *types.WorkflowExecutionTerminatedEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionTerminatedEventAttributes{ Reason: t.Reason, Details: ToPayload(t.Details), Identity: t.Identity, } } func FromWorkflowExecutionTimedOutEventAttributes(t *types.WorkflowExecutionTimedOutEventAttributes) *apiv1.WorkflowExecutionTimedOutEventAttributes { if t == nil { return nil } return &apiv1.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: FromTimeoutType(t.TimeoutType), } } func ToWorkflowExecutionTimedOutEventAttributes(t *apiv1.WorkflowExecutionTimedOutEventAttributes) *types.WorkflowExecutionTimedOutEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: ToTimeoutType(t.TimeoutType), } } func FromWorkflowIDReusePolicy(t *types.WorkflowIDReusePolicy) apiv1.WorkflowIdReusePolicy { if t == nil { return apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_INVALID } switch *t { case types.WorkflowIDReusePolicyAllowDuplicateFailedOnly: return apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY case types.WorkflowIDReusePolicyAllowDuplicate: return apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE case types.WorkflowIDReusePolicyRejectDuplicate: return apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE case types.WorkflowIDReusePolicyTerminateIfRunning: return apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_TERMINATE_IF_RUNNING } return apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_INVALID } func ToWorkflowIDReusePolicy(t apiv1.WorkflowIdReusePolicy) *types.WorkflowIDReusePolicy { switch t { case apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_INVALID: return nil case apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY: return types.WorkflowIDReusePolicyAllowDuplicateFailedOnly.Ptr() case apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE: return types.WorkflowIDReusePolicyAllowDuplicate.Ptr() case apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE: return types.WorkflowIDReusePolicyRejectDuplicate.Ptr() case apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_TERMINATE_IF_RUNNING: return types.WorkflowIDReusePolicyTerminateIfRunning.Ptr() } return nil } func FromWorkflowQuery(t *types.WorkflowQuery) *apiv1.WorkflowQuery { if t == nil { return nil } return &apiv1.WorkflowQuery{ QueryType: t.QueryType, QueryArgs: FromPayload(t.QueryArgs), } } func ToWorkflowQuery(t *apiv1.WorkflowQuery) *types.WorkflowQuery { if t == nil { return nil } return &types.WorkflowQuery{ QueryType: t.QueryType, QueryArgs: ToPayload(t.QueryArgs), } } func FromWorkflowQueryResult(t *types.WorkflowQueryResult) *apiv1.WorkflowQueryResult { if t == nil { return nil } return &apiv1.WorkflowQueryResult{ ResultType: FromQueryResultType(t.ResultType), Answer: FromPayload(t.Answer), ErrorMessage: t.ErrorMessage, } } func ToWorkflowQueryResult(t *apiv1.WorkflowQueryResult) *types.WorkflowQueryResult { if t == nil { return nil } return &types.WorkflowQueryResult{ ResultType: ToQueryResultType(t.ResultType), Answer: ToPayload(t.Answer), ErrorMessage: t.ErrorMessage, } } func FromWorkflowType(t *types.WorkflowType) *apiv1.WorkflowType { if t == nil { return nil } return &apiv1.WorkflowType{ Name: t.Name, } } func ToWorkflowType(t *apiv1.WorkflowType) *types.WorkflowType { if t == nil { return nil } return &types.WorkflowType{ Name: t.Name, } } func FromWorkflowTypeFilter(t *types.WorkflowTypeFilter) *apiv1.WorkflowTypeFilter { if t == nil { return nil } return &apiv1.WorkflowTypeFilter{ Name: t.Name, } } func ToWorkflowTypeFilter(t *apiv1.WorkflowTypeFilter) *types.WorkflowTypeFilter { if t == nil { return nil } return &types.WorkflowTypeFilter{ Name: t.Name, } } func FromDataBlobArray(t []*types.DataBlob) []*apiv1.DataBlob { if t == nil { return nil } v := make([]*apiv1.DataBlob, len(t)) for i := range t { v[i] = FromDataBlob(t[i]) } return v } func ToDataBlobArray(t []*apiv1.DataBlob) []*types.DataBlob { if t == nil { return nil } v := make([]*types.DataBlob, len(t)) for i := range t { v[i] = ToDataBlob(t[i]) } return v } func FromHistoryEventArray(t []*types.HistoryEvent) []*apiv1.HistoryEvent { if t == nil { return nil } v := make([]*apiv1.HistoryEvent, len(t)) for i := range t { v[i] = FromHistoryEvent(t[i]) } return v } func ToHistoryEventArray(t []*apiv1.HistoryEvent) []*types.HistoryEvent { if t == nil { return []*types.HistoryEvent{} } v := make([]*types.HistoryEvent, len(t)) for i := range t { v[i] = ToHistoryEvent(t[i]) } return v } func FromTaskListPartitionMetadataArray(t []*types.TaskListPartitionMetadata) []*apiv1.TaskListPartitionMetadata { if t == nil { return nil } v := make([]*apiv1.TaskListPartitionMetadata, len(t)) for i := range t { v[i] = FromTaskListPartitionMetadata(t[i]) } return v } func ToTaskListPartitionMetadataArray(t []*apiv1.TaskListPartitionMetadata) []*types.TaskListPartitionMetadata { if t == nil { return nil } v := make([]*types.TaskListPartitionMetadata, len(t)) for i := range t { v[i] = ToTaskListPartitionMetadata(t[i]) } return v } func FromDecisionArray(t []*types.Decision) []*apiv1.Decision { if t == nil { return nil } v := make([]*apiv1.Decision, len(t)) for i := range t { v[i] = FromDecision(t[i]) } return v } func ToDecisionArray(t []*apiv1.Decision) []*types.Decision { if t == nil { return nil } v := make([]*types.Decision, len(t)) for i := range t { v[i] = ToDecision(t[i]) } return v } func FromPollerInfoArray(t []*types.PollerInfo) []*apiv1.PollerInfo { if t == nil { return nil } v := make([]*apiv1.PollerInfo, len(t)) for i := range t { v[i] = FromPollerInfo(t[i]) } return v } func ToPollerInfoArray(t []*apiv1.PollerInfo) []*types.PollerInfo { if t == nil { return nil } v := make([]*types.PollerInfo, len(t)) for i := range t { v[i] = ToPollerInfo(t[i]) } return v } func FromPendingChildExecutionInfoArray(t []*types.PendingChildExecutionInfo) []*apiv1.PendingChildExecutionInfo { if t == nil { return nil } v := make([]*apiv1.PendingChildExecutionInfo, len(t)) for i := range t { v[i] = FromPendingChildExecutionInfo(t[i]) } return v } func ToPendingChildExecutionInfoArray(t []*apiv1.PendingChildExecutionInfo) []*types.PendingChildExecutionInfo { if t == nil { return nil } v := make([]*types.PendingChildExecutionInfo, len(t)) for i := range t { v[i] = ToPendingChildExecutionInfo(t[i]) } return v } func FromWorkflowExecutionInfoArray(t []*types.WorkflowExecutionInfo) []*apiv1.WorkflowExecutionInfo { if t == nil { return nil } v := make([]*apiv1.WorkflowExecutionInfo, len(t)) for i := range t { v[i] = FromWorkflowExecutionInfo(t[i]) } return v } func ToWorkflowExecutionInfoArray(t []*apiv1.WorkflowExecutionInfo) []*types.WorkflowExecutionInfo { if t == nil { return nil } v := make([]*types.WorkflowExecutionInfo, len(t)) for i := range t { v[i] = ToWorkflowExecutionInfo(t[i]) } return v } func FromDescribeDomainResponseArray(t []*types.DescribeDomainResponse) []*apiv1.Domain { if t == nil { return nil } v := make([]*apiv1.Domain, len(t)) for i := range t { v[i] = FromDescribeDomainResponseDomain(t[i]) } return v } func ToDescribeDomainResponseArray(t []*apiv1.Domain) []*types.DescribeDomainResponse { if t == nil { return nil } v := make([]*types.DescribeDomainResponse, len(t)) for i := range t { v[i] = ToDescribeDomainResponseDomain(t[i]) } return v } func FromResetPointInfoArray(t []*types.ResetPointInfo) []*apiv1.ResetPointInfo { if t == nil { return nil } v := make([]*apiv1.ResetPointInfo, len(t)) for i := range t { v[i] = FromResetPointInfo(t[i]) } return v } func ToResetPointInfoArray(t []*apiv1.ResetPointInfo) []*types.ResetPointInfo { if t == nil { return nil } v := make([]*types.ResetPointInfo, len(t)) for i := range t { v[i] = ToResetPointInfo(t[i]) } return v } func FromPendingActivityInfoArray(t []*types.PendingActivityInfo) []*apiv1.PendingActivityInfo { if t == nil { return nil } v := make([]*apiv1.PendingActivityInfo, len(t)) for i := range t { v[i] = FromPendingActivityInfo(t[i]) } return v } func ToPendingActivityInfoArray(t []*apiv1.PendingActivityInfo) []*types.PendingActivityInfo { if t == nil { return nil } v := make([]*types.PendingActivityInfo, len(t)) for i := range t { v[i] = ToPendingActivityInfo(t[i]) } return v } func FromClusterReplicationConfigurationArray(t []*types.ClusterReplicationConfiguration) []*apiv1.ClusterReplicationConfiguration { if t == nil { return nil } v := make([]*apiv1.ClusterReplicationConfiguration, len(t)) for i := range t { v[i] = FromClusterReplicationConfiguration(t[i]) } return v } func ToClusterReplicationConfigurationArray(t []*apiv1.ClusterReplicationConfiguration) []*types.ClusterReplicationConfiguration { if t == nil { return nil } v := make([]*types.ClusterReplicationConfiguration, len(t)) for i := range t { v[i] = ToClusterReplicationConfiguration(t[i]) } return v } func FromActiveClusters(t *types.ActiveClusters) *apiv1.ActiveClusters { if t == nil { return nil } var activeClustersByClusterAttribute map[string]*apiv1.ClusterAttributeScope if t.AttributeScopes != nil { activeClustersByClusterAttribute = make(map[string]*apiv1.ClusterAttributeScope) for scopeType, scope := range t.AttributeScopes { activeClustersByClusterAttribute[scopeType] = FromClusterAttributeScope(&scope) } } return &apiv1.ActiveClusters{ ActiveClustersByClusterAttribute: activeClustersByClusterAttribute, } } func ToActiveClusters(t *apiv1.ActiveClusters) *types.ActiveClusters { if t == nil { return nil } var attributeScopes map[string]types.ClusterAttributeScope if t.ActiveClustersByClusterAttribute != nil { attributeScopes = make(map[string]types.ClusterAttributeScope) for scopeType, scope := range t.ActiveClustersByClusterAttribute { if converted := ToClusterAttributeScope(scope); converted != nil { attributeScopes[scopeType] = *converted } } } return &types.ActiveClusters{ AttributeScopes: attributeScopes, } } func FromClusterAttributeScope(t *types.ClusterAttributeScope) *apiv1.ClusterAttributeScope { if t == nil { return nil } var clusterAttributes map[string]*apiv1.ActiveClusterInfo if len(t.ClusterAttributes) > 0 { clusterAttributes = make(map[string]*apiv1.ActiveClusterInfo) for name, clusterInfo := range t.ClusterAttributes { clusterAttributes[name] = &apiv1.ActiveClusterInfo{ ActiveClusterName: clusterInfo.ActiveClusterName, FailoverVersion: clusterInfo.FailoverVersion, } } } return &apiv1.ClusterAttributeScope{ ClusterAttributes: clusterAttributes, } } func ToClusterAttributeScope(t *apiv1.ClusterAttributeScope) *types.ClusterAttributeScope { if t == nil { return nil } var clusterAttributes map[string]types.ActiveClusterInfo if len(t.ClusterAttributes) > 0 { clusterAttributes = make(map[string]types.ActiveClusterInfo) for name, clusterInfo := range t.ClusterAttributes { if clusterInfo != nil { clusterAttributes[name] = types.ActiveClusterInfo{ ActiveClusterName: clusterInfo.ActiveClusterName, FailoverVersion: clusterInfo.FailoverVersion, } } } } return &types.ClusterAttributeScope{ ClusterAttributes: clusterAttributes, } } func FromActivityLocalDispatchInfoMap(t map[string]*types.ActivityLocalDispatchInfo) map[string]*apiv1.ActivityLocalDispatchInfo { if t == nil { return nil } v := make(map[string]*apiv1.ActivityLocalDispatchInfo, len(t)) for key := range t { v[key] = FromActivityLocalDispatchInfo(t[key]) } return v } func ToActivityLocalDispatchInfoMap(t map[string]*apiv1.ActivityLocalDispatchInfo) map[string]*types.ActivityLocalDispatchInfo { if t == nil { return nil } v := make(map[string]*types.ActivityLocalDispatchInfo, len(t)) for key := range t { v[key] = ToActivityLocalDispatchInfo(t[key]) } return v } func FromBadBinaryInfoMap(t map[string]*types.BadBinaryInfo) map[string]*apiv1.BadBinaryInfo { if t == nil { return nil } v := make(map[string]*apiv1.BadBinaryInfo, len(t)) for key := range t { v[key] = FromBadBinaryInfo(t[key]) } return v } func ToBadBinaryInfoMap(t map[string]*apiv1.BadBinaryInfo) map[string]*types.BadBinaryInfo { if t == nil { return nil } v := make(map[string]*types.BadBinaryInfo, len(t)) for key := range t { v[key] = ToBadBinaryInfo(t[key]) } return v } func FromIndexedValueTypeMap(t map[string]types.IndexedValueType) map[string]apiv1.IndexedValueType { if t == nil { return nil } v := make(map[string]apiv1.IndexedValueType, len(t)) for key := range t { v[key] = FromIndexedValueType(t[key]) } return v } func ToIndexedValueTypeMap(t map[string]apiv1.IndexedValueType) map[string]types.IndexedValueType { if t == nil { return nil } v := make(map[string]types.IndexedValueType, len(t)) for key := range t { v[key] = ToIndexedValueType(t[key]) } return v } func FromWorkflowQueryMap(t map[string]*types.WorkflowQuery) map[string]*apiv1.WorkflowQuery { if t == nil { return nil } v := make(map[string]*apiv1.WorkflowQuery, len(t)) for key := range t { v[key] = FromWorkflowQuery(t[key]) } return v } func ToWorkflowQueryMap(t map[string]*apiv1.WorkflowQuery) map[string]*types.WorkflowQuery { if t == nil { return nil } v := make(map[string]*types.WorkflowQuery, len(t)) for key := range t { v[key] = ToWorkflowQuery(t[key]) } return v } func FromWorkflowQueryResultMap(t map[string]*types.WorkflowQueryResult) map[string]*apiv1.WorkflowQueryResult { if t == nil { return nil } v := make(map[string]*apiv1.WorkflowQueryResult, len(t)) for key := range t { v[key] = FromWorkflowQueryResult(t[key]) } return v } func ToWorkflowQueryResultMap(t map[string]*apiv1.WorkflowQueryResult) map[string]*types.WorkflowQueryResult { if t == nil { return nil } v := make(map[string]*types.WorkflowQueryResult, len(t)) for key := range t { v[key] = ToWorkflowQueryResult(t[key]) } return v } func FromIsolationGroupMetricsMap(t map[string]*types.IsolationGroupMetrics) map[string]*apiv1.IsolationGroupMetrics { if t == nil { return nil } v := make(map[string]*apiv1.IsolationGroupMetrics, len(t)) for key := range t { v[key] = FromIsolationGroupMetrics(t[key]) } return v } func ToIsolationGroupMetricsMap(t map[string]*apiv1.IsolationGroupMetrics) map[string]*types.IsolationGroupMetrics { if t == nil { return nil } v := make(map[string]*types.IsolationGroupMetrics, len(t)) for key := range t { v[key] = ToIsolationGroupMetrics(t[key]) } return v } func FromPayload(data []byte) *apiv1.Payload { if data == nil { return nil } return &apiv1.Payload{ Data: data, } } func ToPayload(p *apiv1.Payload) []byte { if p == nil { return nil } if p.Data == nil { // FromPayload will not generate this case // however, Data field will be dropped by the encoding if it's empty // and receiver side will see nil for the Data field // since we already know p is not nil, Data field must be an empty byte array return []byte{} } return p.Data } func FromPayloadMap(t map[string][]byte) map[string]*apiv1.Payload { if t == nil { return nil } v := make(map[string]*apiv1.Payload, len(t)) for key := range t { v[key] = FromPayload(t[key]) } return v } func ToPayloadMap(t map[string]*apiv1.Payload) map[string][]byte { if t == nil { return nil } v := make(map[string][]byte, len(t)) for key := range t { v[key] = ToPayload(t[key]) } return v } func FromFailure(reason *string, details []byte) *apiv1.Failure { if reason == nil { return nil } return &apiv1.Failure{ Reason: *reason, Details: details, } } func ToFailureReason(failure *apiv1.Failure) *string { if failure == nil { return nil } return &failure.Reason } func ToFailureDetails(failure *apiv1.Failure) []byte { if failure == nil { return nil } return failure.Details } func FromHistoryEvent(e *types.HistoryEvent) *apiv1.HistoryEvent { if e == nil { return nil } event := apiv1.HistoryEvent{ EventId: e.ID, EventTime: unixNanoToTime(e.Timestamp), Version: e.Version, TaskId: e.TaskID, } switch *e.EventType { case types.EventTypeWorkflowExecutionStarted: event.Attributes = &apiv1.HistoryEvent_WorkflowExecutionStartedEventAttributes{ WorkflowExecutionStartedEventAttributes: FromWorkflowExecutionStartedEventAttributes(e.WorkflowExecutionStartedEventAttributes), } case types.EventTypeWorkflowExecutionCompleted: event.Attributes = &apiv1.HistoryEvent_WorkflowExecutionCompletedEventAttributes{ WorkflowExecutionCompletedEventAttributes: FromWorkflowExecutionCompletedEventAttributes(e.WorkflowExecutionCompletedEventAttributes), } case types.EventTypeWorkflowExecutionFailed: event.Attributes = &apiv1.HistoryEvent_WorkflowExecutionFailedEventAttributes{ WorkflowExecutionFailedEventAttributes: FromWorkflowExecutionFailedEventAttributes(e.WorkflowExecutionFailedEventAttributes), } case types.EventTypeWorkflowExecutionTimedOut: event.Attributes = &apiv1.HistoryEvent_WorkflowExecutionTimedOutEventAttributes{ WorkflowExecutionTimedOutEventAttributes: FromWorkflowExecutionTimedOutEventAttributes(e.WorkflowExecutionTimedOutEventAttributes), } case types.EventTypeDecisionTaskScheduled: event.Attributes = &apiv1.HistoryEvent_DecisionTaskScheduledEventAttributes{ DecisionTaskScheduledEventAttributes: FromDecisionTaskScheduledEventAttributes(e.DecisionTaskScheduledEventAttributes), } case types.EventTypeDecisionTaskStarted: event.Attributes = &apiv1.HistoryEvent_DecisionTaskStartedEventAttributes{ DecisionTaskStartedEventAttributes: FromDecisionTaskStartedEventAttributes(e.DecisionTaskStartedEventAttributes), } case types.EventTypeDecisionTaskCompleted: event.Attributes = &apiv1.HistoryEvent_DecisionTaskCompletedEventAttributes{ DecisionTaskCompletedEventAttributes: FromDecisionTaskCompletedEventAttributes(e.DecisionTaskCompletedEventAttributes), } case types.EventTypeDecisionTaskTimedOut: event.Attributes = &apiv1.HistoryEvent_DecisionTaskTimedOutEventAttributes{ DecisionTaskTimedOutEventAttributes: FromDecisionTaskTimedOutEventAttributes(e.DecisionTaskTimedOutEventAttributes), } case types.EventTypeDecisionTaskFailed: event.Attributes = &apiv1.HistoryEvent_DecisionTaskFailedEventAttributes{ DecisionTaskFailedEventAttributes: FromDecisionTaskFailedEventAttributes(e.DecisionTaskFailedEventAttributes), } case types.EventTypeActivityTaskScheduled: event.Attributes = &apiv1.HistoryEvent_ActivityTaskScheduledEventAttributes{ ActivityTaskScheduledEventAttributes: FromActivityTaskScheduledEventAttributes(e.ActivityTaskScheduledEventAttributes), } case types.EventTypeActivityTaskStarted: event.Attributes = &apiv1.HistoryEvent_ActivityTaskStartedEventAttributes{ ActivityTaskStartedEventAttributes: FromActivityTaskStartedEventAttributes(e.ActivityTaskStartedEventAttributes), } case types.EventTypeActivityTaskCompleted: event.Attributes = &apiv1.HistoryEvent_ActivityTaskCompletedEventAttributes{ ActivityTaskCompletedEventAttributes: FromActivityTaskCompletedEventAttributes(e.ActivityTaskCompletedEventAttributes), } case types.EventTypeActivityTaskFailed: event.Attributes = &apiv1.HistoryEvent_ActivityTaskFailedEventAttributes{ ActivityTaskFailedEventAttributes: FromActivityTaskFailedEventAttributes(e.ActivityTaskFailedEventAttributes), } case types.EventTypeActivityTaskTimedOut: event.Attributes = &apiv1.HistoryEvent_ActivityTaskTimedOutEventAttributes{ ActivityTaskTimedOutEventAttributes: FromActivityTaskTimedOutEventAttributes(e.ActivityTaskTimedOutEventAttributes), } case types.EventTypeActivityTaskCancelRequested: event.Attributes = &apiv1.HistoryEvent_ActivityTaskCancelRequestedEventAttributes{ ActivityTaskCancelRequestedEventAttributes: FromActivityTaskCancelRequestedEventAttributes(e.ActivityTaskCancelRequestedEventAttributes), } case types.EventTypeRequestCancelActivityTaskFailed: event.Attributes = &apiv1.HistoryEvent_RequestCancelActivityTaskFailedEventAttributes{ RequestCancelActivityTaskFailedEventAttributes: FromRequestCancelActivityTaskFailedEventAttributes(e.RequestCancelActivityTaskFailedEventAttributes), } case types.EventTypeActivityTaskCanceled: event.Attributes = &apiv1.HistoryEvent_ActivityTaskCanceledEventAttributes{ ActivityTaskCanceledEventAttributes: FromActivityTaskCanceledEventAttributes(e.ActivityTaskCanceledEventAttributes), } case types.EventTypeTimerStarted: event.Attributes = &apiv1.HistoryEvent_TimerStartedEventAttributes{ TimerStartedEventAttributes: FromTimerStartedEventAttributes(e.TimerStartedEventAttributes), } case types.EventTypeTimerFired: event.Attributes = &apiv1.HistoryEvent_TimerFiredEventAttributes{ TimerFiredEventAttributes: FromTimerFiredEventAttributes(e.TimerFiredEventAttributes), } case types.EventTypeTimerCanceled: event.Attributes = &apiv1.HistoryEvent_TimerCanceledEventAttributes{ TimerCanceledEventAttributes: FromTimerCanceledEventAttributes(e.TimerCanceledEventAttributes), } case types.EventTypeCancelTimerFailed: event.Attributes = &apiv1.HistoryEvent_CancelTimerFailedEventAttributes{ CancelTimerFailedEventAttributes: FromCancelTimerFailedEventAttributes(e.CancelTimerFailedEventAttributes), } case types.EventTypeWorkflowExecutionCancelRequested: event.Attributes = &apiv1.HistoryEvent_WorkflowExecutionCancelRequestedEventAttributes{ WorkflowExecutionCancelRequestedEventAttributes: FromWorkflowExecutionCancelRequestedEventAttributes(e.WorkflowExecutionCancelRequestedEventAttributes), } case types.EventTypeWorkflowExecutionCanceled: event.Attributes = &apiv1.HistoryEvent_WorkflowExecutionCanceledEventAttributes{ WorkflowExecutionCanceledEventAttributes: FromWorkflowExecutionCanceledEventAttributes(e.WorkflowExecutionCanceledEventAttributes), } case types.EventTypeRequestCancelExternalWorkflowExecutionInitiated: event.Attributes = &apiv1.HistoryEvent_RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ RequestCancelExternalWorkflowExecutionInitiatedEventAttributes: FromRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(e.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes), } case types.EventTypeRequestCancelExternalWorkflowExecutionFailed: event.Attributes = &apiv1.HistoryEvent_RequestCancelExternalWorkflowExecutionFailedEventAttributes{ RequestCancelExternalWorkflowExecutionFailedEventAttributes: FromRequestCancelExternalWorkflowExecutionFailedEventAttributes(e.RequestCancelExternalWorkflowExecutionFailedEventAttributes), } case types.EventTypeExternalWorkflowExecutionCancelRequested: event.Attributes = &apiv1.HistoryEvent_ExternalWorkflowExecutionCancelRequestedEventAttributes{ ExternalWorkflowExecutionCancelRequestedEventAttributes: FromExternalWorkflowExecutionCancelRequestedEventAttributes(e.ExternalWorkflowExecutionCancelRequestedEventAttributes), } case types.EventTypeMarkerRecorded: event.Attributes = &apiv1.HistoryEvent_MarkerRecordedEventAttributes{ MarkerRecordedEventAttributes: FromMarkerRecordedEventAttributes(e.MarkerRecordedEventAttributes), } case types.EventTypeWorkflowExecutionSignaled: event.Attributes = &apiv1.HistoryEvent_WorkflowExecutionSignaledEventAttributes{ WorkflowExecutionSignaledEventAttributes: FromWorkflowExecutionSignaledEventAttributes(e.WorkflowExecutionSignaledEventAttributes), } case types.EventTypeWorkflowExecutionTerminated: event.Attributes = &apiv1.HistoryEvent_WorkflowExecutionTerminatedEventAttributes{ WorkflowExecutionTerminatedEventAttributes: FromWorkflowExecutionTerminatedEventAttributes(e.WorkflowExecutionTerminatedEventAttributes), } case types.EventTypeWorkflowExecutionContinuedAsNew: event.Attributes = &apiv1.HistoryEvent_WorkflowExecutionContinuedAsNewEventAttributes{ WorkflowExecutionContinuedAsNewEventAttributes: FromWorkflowExecutionContinuedAsNewEventAttributes(e.WorkflowExecutionContinuedAsNewEventAttributes), } case types.EventTypeStartChildWorkflowExecutionInitiated: event.Attributes = &apiv1.HistoryEvent_StartChildWorkflowExecutionInitiatedEventAttributes{ StartChildWorkflowExecutionInitiatedEventAttributes: FromStartChildWorkflowExecutionInitiatedEventAttributes(e.StartChildWorkflowExecutionInitiatedEventAttributes), } case types.EventTypeStartChildWorkflowExecutionFailed: event.Attributes = &apiv1.HistoryEvent_StartChildWorkflowExecutionFailedEventAttributes{ StartChildWorkflowExecutionFailedEventAttributes: FromStartChildWorkflowExecutionFailedEventAttributes(e.StartChildWorkflowExecutionFailedEventAttributes), } case types.EventTypeChildWorkflowExecutionStarted: event.Attributes = &apiv1.HistoryEvent_ChildWorkflowExecutionStartedEventAttributes{ ChildWorkflowExecutionStartedEventAttributes: FromChildWorkflowExecutionStartedEventAttributes(e.ChildWorkflowExecutionStartedEventAttributes), } case types.EventTypeChildWorkflowExecutionCompleted: event.Attributes = &apiv1.HistoryEvent_ChildWorkflowExecutionCompletedEventAttributes{ ChildWorkflowExecutionCompletedEventAttributes: FromChildWorkflowExecutionCompletedEventAttributes(e.ChildWorkflowExecutionCompletedEventAttributes), } case types.EventTypeChildWorkflowExecutionFailed: event.Attributes = &apiv1.HistoryEvent_ChildWorkflowExecutionFailedEventAttributes{ ChildWorkflowExecutionFailedEventAttributes: FromChildWorkflowExecutionFailedEventAttributes(e.ChildWorkflowExecutionFailedEventAttributes), } case types.EventTypeChildWorkflowExecutionCanceled: event.Attributes = &apiv1.HistoryEvent_ChildWorkflowExecutionCanceledEventAttributes{ ChildWorkflowExecutionCanceledEventAttributes: FromChildWorkflowExecutionCanceledEventAttributes(e.ChildWorkflowExecutionCanceledEventAttributes), } case types.EventTypeChildWorkflowExecutionTimedOut: event.Attributes = &apiv1.HistoryEvent_ChildWorkflowExecutionTimedOutEventAttributes{ ChildWorkflowExecutionTimedOutEventAttributes: FromChildWorkflowExecutionTimedOutEventAttributes(e.ChildWorkflowExecutionTimedOutEventAttributes), } case types.EventTypeChildWorkflowExecutionTerminated: event.Attributes = &apiv1.HistoryEvent_ChildWorkflowExecutionTerminatedEventAttributes{ ChildWorkflowExecutionTerminatedEventAttributes: FromChildWorkflowExecutionTerminatedEventAttributes(e.ChildWorkflowExecutionTerminatedEventAttributes), } case types.EventTypeSignalExternalWorkflowExecutionInitiated: event.Attributes = &apiv1.HistoryEvent_SignalExternalWorkflowExecutionInitiatedEventAttributes{ SignalExternalWorkflowExecutionInitiatedEventAttributes: FromSignalExternalWorkflowExecutionInitiatedEventAttributes(e.SignalExternalWorkflowExecutionInitiatedEventAttributes), } case types.EventTypeSignalExternalWorkflowExecutionFailed: event.Attributes = &apiv1.HistoryEvent_SignalExternalWorkflowExecutionFailedEventAttributes{ SignalExternalWorkflowExecutionFailedEventAttributes: FromSignalExternalWorkflowExecutionFailedEventAttributes(e.SignalExternalWorkflowExecutionFailedEventAttributes), } case types.EventTypeExternalWorkflowExecutionSignaled: event.Attributes = &apiv1.HistoryEvent_ExternalWorkflowExecutionSignaledEventAttributes{ ExternalWorkflowExecutionSignaledEventAttributes: FromExternalWorkflowExecutionSignaledEventAttributes(e.ExternalWorkflowExecutionSignaledEventAttributes), } case types.EventTypeUpsertWorkflowSearchAttributes: event.Attributes = &apiv1.HistoryEvent_UpsertWorkflowSearchAttributesEventAttributes{ UpsertWorkflowSearchAttributesEventAttributes: FromUpsertWorkflowSearchAttributesEventAttributes(e.UpsertWorkflowSearchAttributesEventAttributes), } } return &event } func ToHistoryEvent(e *apiv1.HistoryEvent) *types.HistoryEvent { if e == nil { return nil } event := types.HistoryEvent{ ID: e.EventId, Timestamp: timeToUnixNano(e.EventTime), Version: e.Version, TaskID: e.TaskId, } switch attr := e.Attributes.(type) { case *apiv1.HistoryEvent_WorkflowExecutionStartedEventAttributes: event.EventType = types.EventTypeWorkflowExecutionStarted.Ptr() event.WorkflowExecutionStartedEventAttributes = ToWorkflowExecutionStartedEventAttributes(attr.WorkflowExecutionStartedEventAttributes) case *apiv1.HistoryEvent_WorkflowExecutionCompletedEventAttributes: event.EventType = types.EventTypeWorkflowExecutionCompleted.Ptr() event.WorkflowExecutionCompletedEventAttributes = ToWorkflowExecutionCompletedEventAttributes(attr.WorkflowExecutionCompletedEventAttributes) case *apiv1.HistoryEvent_WorkflowExecutionFailedEventAttributes: event.EventType = types.EventTypeWorkflowExecutionFailed.Ptr() event.WorkflowExecutionFailedEventAttributes = ToWorkflowExecutionFailedEventAttributes(attr.WorkflowExecutionFailedEventAttributes) case *apiv1.HistoryEvent_WorkflowExecutionTimedOutEventAttributes: event.EventType = types.EventTypeWorkflowExecutionTimedOut.Ptr() event.WorkflowExecutionTimedOutEventAttributes = ToWorkflowExecutionTimedOutEventAttributes(attr.WorkflowExecutionTimedOutEventAttributes) case *apiv1.HistoryEvent_DecisionTaskScheduledEventAttributes: event.EventType = types.EventTypeDecisionTaskScheduled.Ptr() event.DecisionTaskScheduledEventAttributes = ToDecisionTaskScheduledEventAttributes(attr.DecisionTaskScheduledEventAttributes) case *apiv1.HistoryEvent_DecisionTaskStartedEventAttributes: event.EventType = types.EventTypeDecisionTaskStarted.Ptr() event.DecisionTaskStartedEventAttributes = ToDecisionTaskStartedEventAttributes(attr.DecisionTaskStartedEventAttributes) case *apiv1.HistoryEvent_DecisionTaskCompletedEventAttributes: event.EventType = types.EventTypeDecisionTaskCompleted.Ptr() event.DecisionTaskCompletedEventAttributes = ToDecisionTaskCompletedEventAttributes(attr.DecisionTaskCompletedEventAttributes) case *apiv1.HistoryEvent_DecisionTaskTimedOutEventAttributes: event.EventType = types.EventTypeDecisionTaskTimedOut.Ptr() event.DecisionTaskTimedOutEventAttributes = ToDecisionTaskTimedOutEventAttributes(attr.DecisionTaskTimedOutEventAttributes) case *apiv1.HistoryEvent_DecisionTaskFailedEventAttributes: event.EventType = types.EventTypeDecisionTaskFailed.Ptr() event.DecisionTaskFailedEventAttributes = ToDecisionTaskFailedEventAttributes(attr.DecisionTaskFailedEventAttributes) case *apiv1.HistoryEvent_ActivityTaskScheduledEventAttributes: event.EventType = types.EventTypeActivityTaskScheduled.Ptr() event.ActivityTaskScheduledEventAttributes = ToActivityTaskScheduledEventAttributes(attr.ActivityTaskScheduledEventAttributes) case *apiv1.HistoryEvent_ActivityTaskStartedEventAttributes: event.EventType = types.EventTypeActivityTaskStarted.Ptr() event.ActivityTaskStartedEventAttributes = ToActivityTaskStartedEventAttributes(attr.ActivityTaskStartedEventAttributes) case *apiv1.HistoryEvent_ActivityTaskCompletedEventAttributes: event.EventType = types.EventTypeActivityTaskCompleted.Ptr() event.ActivityTaskCompletedEventAttributes = ToActivityTaskCompletedEventAttributes(attr.ActivityTaskCompletedEventAttributes) case *apiv1.HistoryEvent_ActivityTaskFailedEventAttributes: event.EventType = types.EventTypeActivityTaskFailed.Ptr() event.ActivityTaskFailedEventAttributes = ToActivityTaskFailedEventAttributes(attr.ActivityTaskFailedEventAttributes) case *apiv1.HistoryEvent_ActivityTaskTimedOutEventAttributes: event.EventType = types.EventTypeActivityTaskTimedOut.Ptr() event.ActivityTaskTimedOutEventAttributes = ToActivityTaskTimedOutEventAttributes(attr.ActivityTaskTimedOutEventAttributes) case *apiv1.HistoryEvent_TimerStartedEventAttributes: event.EventType = types.EventTypeTimerStarted.Ptr() event.TimerStartedEventAttributes = ToTimerStartedEventAttributes(attr.TimerStartedEventAttributes) case *apiv1.HistoryEvent_TimerFiredEventAttributes: event.EventType = types.EventTypeTimerFired.Ptr() event.TimerFiredEventAttributes = ToTimerFiredEventAttributes(attr.TimerFiredEventAttributes) case *apiv1.HistoryEvent_ActivityTaskCancelRequestedEventAttributes: event.EventType = types.EventTypeActivityTaskCancelRequested.Ptr() event.ActivityTaskCancelRequestedEventAttributes = ToActivityTaskCancelRequestedEventAttributes(attr.ActivityTaskCancelRequestedEventAttributes) case *apiv1.HistoryEvent_RequestCancelActivityTaskFailedEventAttributes: event.EventType = types.EventTypeRequestCancelActivityTaskFailed.Ptr() event.RequestCancelActivityTaskFailedEventAttributes = ToRequestCancelActivityTaskFailedEventAttributes(attr.RequestCancelActivityTaskFailedEventAttributes) case *apiv1.HistoryEvent_ActivityTaskCanceledEventAttributes: event.EventType = types.EventTypeActivityTaskCanceled.Ptr() event.ActivityTaskCanceledEventAttributes = ToActivityTaskCanceledEventAttributes(attr.ActivityTaskCanceledEventAttributes) case *apiv1.HistoryEvent_TimerCanceledEventAttributes: event.EventType = types.EventTypeTimerCanceled.Ptr() event.TimerCanceledEventAttributes = ToTimerCanceledEventAttributes(attr.TimerCanceledEventAttributes) case *apiv1.HistoryEvent_CancelTimerFailedEventAttributes: event.EventType = types.EventTypeCancelTimerFailed.Ptr() event.CancelTimerFailedEventAttributes = ToCancelTimerFailedEventAttributes(attr.CancelTimerFailedEventAttributes) case *apiv1.HistoryEvent_MarkerRecordedEventAttributes: event.EventType = types.EventTypeMarkerRecorded.Ptr() event.MarkerRecordedEventAttributes = ToMarkerRecordedEventAttributes(attr.MarkerRecordedEventAttributes) case *apiv1.HistoryEvent_WorkflowExecutionSignaledEventAttributes: event.EventType = types.EventTypeWorkflowExecutionSignaled.Ptr() event.WorkflowExecutionSignaledEventAttributes = ToWorkflowExecutionSignaledEventAttributes(attr.WorkflowExecutionSignaledEventAttributes) case *apiv1.HistoryEvent_WorkflowExecutionTerminatedEventAttributes: event.EventType = types.EventTypeWorkflowExecutionTerminated.Ptr() event.WorkflowExecutionTerminatedEventAttributes = ToWorkflowExecutionTerminatedEventAttributes(attr.WorkflowExecutionTerminatedEventAttributes) case *apiv1.HistoryEvent_WorkflowExecutionCancelRequestedEventAttributes: event.EventType = types.EventTypeWorkflowExecutionCancelRequested.Ptr() event.WorkflowExecutionCancelRequestedEventAttributes = ToWorkflowExecutionCancelRequestedEventAttributes(attr.WorkflowExecutionCancelRequestedEventAttributes) case *apiv1.HistoryEvent_WorkflowExecutionCanceledEventAttributes: event.EventType = types.EventTypeWorkflowExecutionCanceled.Ptr() event.WorkflowExecutionCanceledEventAttributes = ToWorkflowExecutionCanceledEventAttributes(attr.WorkflowExecutionCanceledEventAttributes) case *apiv1.HistoryEvent_RequestCancelExternalWorkflowExecutionInitiatedEventAttributes: event.EventType = types.EventTypeRequestCancelExternalWorkflowExecutionInitiated.Ptr() event.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes = ToRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(attr.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) case *apiv1.HistoryEvent_RequestCancelExternalWorkflowExecutionFailedEventAttributes: event.EventType = types.EventTypeRequestCancelExternalWorkflowExecutionFailed.Ptr() event.RequestCancelExternalWorkflowExecutionFailedEventAttributes = ToRequestCancelExternalWorkflowExecutionFailedEventAttributes(attr.RequestCancelExternalWorkflowExecutionFailedEventAttributes) case *apiv1.HistoryEvent_ExternalWorkflowExecutionCancelRequestedEventAttributes: event.EventType = types.EventTypeExternalWorkflowExecutionCancelRequested.Ptr() event.ExternalWorkflowExecutionCancelRequestedEventAttributes = ToExternalWorkflowExecutionCancelRequestedEventAttributes(attr.ExternalWorkflowExecutionCancelRequestedEventAttributes) case *apiv1.HistoryEvent_WorkflowExecutionContinuedAsNewEventAttributes: event.EventType = types.EventTypeWorkflowExecutionContinuedAsNew.Ptr() event.WorkflowExecutionContinuedAsNewEventAttributes = ToWorkflowExecutionContinuedAsNewEventAttributes(attr.WorkflowExecutionContinuedAsNewEventAttributes) case *apiv1.HistoryEvent_StartChildWorkflowExecutionInitiatedEventAttributes: event.EventType = types.EventTypeStartChildWorkflowExecutionInitiated.Ptr() event.StartChildWorkflowExecutionInitiatedEventAttributes = ToStartChildWorkflowExecutionInitiatedEventAttributes(attr.StartChildWorkflowExecutionInitiatedEventAttributes) case *apiv1.HistoryEvent_StartChildWorkflowExecutionFailedEventAttributes: event.EventType = types.EventTypeStartChildWorkflowExecutionFailed.Ptr() event.StartChildWorkflowExecutionFailedEventAttributes = ToStartChildWorkflowExecutionFailedEventAttributes(attr.StartChildWorkflowExecutionFailedEventAttributes) case *apiv1.HistoryEvent_ChildWorkflowExecutionStartedEventAttributes: event.EventType = types.EventTypeChildWorkflowExecutionStarted.Ptr() event.ChildWorkflowExecutionStartedEventAttributes = ToChildWorkflowExecutionStartedEventAttributes(attr.ChildWorkflowExecutionStartedEventAttributes) case *apiv1.HistoryEvent_ChildWorkflowExecutionCompletedEventAttributes: event.EventType = types.EventTypeChildWorkflowExecutionCompleted.Ptr() event.ChildWorkflowExecutionCompletedEventAttributes = ToChildWorkflowExecutionCompletedEventAttributes(attr.ChildWorkflowExecutionCompletedEventAttributes) case *apiv1.HistoryEvent_ChildWorkflowExecutionFailedEventAttributes: event.EventType = types.EventTypeChildWorkflowExecutionFailed.Ptr() event.ChildWorkflowExecutionFailedEventAttributes = ToChildWorkflowExecutionFailedEventAttributes(attr.ChildWorkflowExecutionFailedEventAttributes) case *apiv1.HistoryEvent_ChildWorkflowExecutionCanceledEventAttributes: event.EventType = types.EventTypeChildWorkflowExecutionCanceled.Ptr() event.ChildWorkflowExecutionCanceledEventAttributes = ToChildWorkflowExecutionCanceledEventAttributes(attr.ChildWorkflowExecutionCanceledEventAttributes) case *apiv1.HistoryEvent_ChildWorkflowExecutionTimedOutEventAttributes: event.EventType = types.EventTypeChildWorkflowExecutionTimedOut.Ptr() event.ChildWorkflowExecutionTimedOutEventAttributes = ToChildWorkflowExecutionTimedOutEventAttributes(attr.ChildWorkflowExecutionTimedOutEventAttributes) case *apiv1.HistoryEvent_ChildWorkflowExecutionTerminatedEventAttributes: event.EventType = types.EventTypeChildWorkflowExecutionTerminated.Ptr() event.ChildWorkflowExecutionTerminatedEventAttributes = ToChildWorkflowExecutionTerminatedEventAttributes(attr.ChildWorkflowExecutionTerminatedEventAttributes) case *apiv1.HistoryEvent_SignalExternalWorkflowExecutionInitiatedEventAttributes: event.EventType = types.EventTypeSignalExternalWorkflowExecutionInitiated.Ptr() event.SignalExternalWorkflowExecutionInitiatedEventAttributes = ToSignalExternalWorkflowExecutionInitiatedEventAttributes(attr.SignalExternalWorkflowExecutionInitiatedEventAttributes) case *apiv1.HistoryEvent_SignalExternalWorkflowExecutionFailedEventAttributes: event.EventType = types.EventTypeSignalExternalWorkflowExecutionFailed.Ptr() event.SignalExternalWorkflowExecutionFailedEventAttributes = ToSignalExternalWorkflowExecutionFailedEventAttributes(attr.SignalExternalWorkflowExecutionFailedEventAttributes) case *apiv1.HistoryEvent_ExternalWorkflowExecutionSignaledEventAttributes: event.EventType = types.EventTypeExternalWorkflowExecutionSignaled.Ptr() event.ExternalWorkflowExecutionSignaledEventAttributes = ToExternalWorkflowExecutionSignaledEventAttributes(attr.ExternalWorkflowExecutionSignaledEventAttributes) case *apiv1.HistoryEvent_UpsertWorkflowSearchAttributesEventAttributes: event.EventType = types.EventTypeUpsertWorkflowSearchAttributes.Ptr() event.UpsertWorkflowSearchAttributesEventAttributes = ToUpsertWorkflowSearchAttributesEventAttributes(attr.UpsertWorkflowSearchAttributesEventAttributes) } return &event } func FromDecision(d *types.Decision) *apiv1.Decision { if d == nil { return nil } decision := apiv1.Decision{} switch *d.DecisionType { case types.DecisionTypeScheduleActivityTask: decision.Attributes = &apiv1.Decision_ScheduleActivityTaskDecisionAttributes{ ScheduleActivityTaskDecisionAttributes: FromScheduleActivityTaskDecisionAttributes(d.ScheduleActivityTaskDecisionAttributes), } case types.DecisionTypeRequestCancelActivityTask: decision.Attributes = &apiv1.Decision_RequestCancelActivityTaskDecisionAttributes{ RequestCancelActivityTaskDecisionAttributes: FromRequestCancelActivityTaskDecisionAttributes(d.RequestCancelActivityTaskDecisionAttributes), } case types.DecisionTypeStartTimer: decision.Attributes = &apiv1.Decision_StartTimerDecisionAttributes{ StartTimerDecisionAttributes: FromStartTimerDecisionAttributes(d.StartTimerDecisionAttributes), } case types.DecisionTypeCompleteWorkflowExecution: decision.Attributes = &apiv1.Decision_CompleteWorkflowExecutionDecisionAttributes{ CompleteWorkflowExecutionDecisionAttributes: FromCompleteWorkflowExecutionDecisionAttributes(d.CompleteWorkflowExecutionDecisionAttributes), } case types.DecisionTypeFailWorkflowExecution: decision.Attributes = &apiv1.Decision_FailWorkflowExecutionDecisionAttributes{ FailWorkflowExecutionDecisionAttributes: FromFailWorkflowExecutionDecisionAttributes(d.FailWorkflowExecutionDecisionAttributes), } case types.DecisionTypeCancelTimer: decision.Attributes = &apiv1.Decision_CancelTimerDecisionAttributes{ CancelTimerDecisionAttributes: FromCancelTimerDecisionAttributes(d.CancelTimerDecisionAttributes), } case types.DecisionTypeCancelWorkflowExecution: decision.Attributes = &apiv1.Decision_CancelWorkflowExecutionDecisionAttributes{ CancelWorkflowExecutionDecisionAttributes: FromCancelWorkflowExecutionDecisionAttributes(d.CancelWorkflowExecutionDecisionAttributes), } case types.DecisionTypeRequestCancelExternalWorkflowExecution: decision.Attributes = &apiv1.Decision_RequestCancelExternalWorkflowExecutionDecisionAttributes{ RequestCancelExternalWorkflowExecutionDecisionAttributes: FromRequestCancelExternalWorkflowExecutionDecisionAttributes(d.RequestCancelExternalWorkflowExecutionDecisionAttributes), } case types.DecisionTypeRecordMarker: decision.Attributes = &apiv1.Decision_RecordMarkerDecisionAttributes{ RecordMarkerDecisionAttributes: FromRecordMarkerDecisionAttributes(d.RecordMarkerDecisionAttributes), } case types.DecisionTypeContinueAsNewWorkflowExecution: decision.Attributes = &apiv1.Decision_ContinueAsNewWorkflowExecutionDecisionAttributes{ ContinueAsNewWorkflowExecutionDecisionAttributes: FromContinueAsNewWorkflowExecutionDecisionAttributes(d.ContinueAsNewWorkflowExecutionDecisionAttributes), } case types.DecisionTypeStartChildWorkflowExecution: decision.Attributes = &apiv1.Decision_StartChildWorkflowExecutionDecisionAttributes{ StartChildWorkflowExecutionDecisionAttributes: FromStartChildWorkflowExecutionDecisionAttributes(d.StartChildWorkflowExecutionDecisionAttributes), } case types.DecisionTypeSignalExternalWorkflowExecution: decision.Attributes = &apiv1.Decision_SignalExternalWorkflowExecutionDecisionAttributes{ SignalExternalWorkflowExecutionDecisionAttributes: FromSignalExternalWorkflowExecutionDecisionAttributes(d.SignalExternalWorkflowExecutionDecisionAttributes), } case types.DecisionTypeUpsertWorkflowSearchAttributes: decision.Attributes = &apiv1.Decision_UpsertWorkflowSearchAttributesDecisionAttributes{ UpsertWorkflowSearchAttributesDecisionAttributes: FromUpsertWorkflowSearchAttributesDecisionAttributes(d.UpsertWorkflowSearchAttributesDecisionAttributes), } } return &decision } func ToDecision(d *apiv1.Decision) *types.Decision { if d == nil { return nil } decision := types.Decision{} switch attr := d.Attributes.(type) { case *apiv1.Decision_ScheduleActivityTaskDecisionAttributes: decision.DecisionType = types.DecisionTypeScheduleActivityTask.Ptr() decision.ScheduleActivityTaskDecisionAttributes = ToScheduleActivityTaskDecisionAttributes(attr.ScheduleActivityTaskDecisionAttributes) case *apiv1.Decision_StartTimerDecisionAttributes: decision.DecisionType = types.DecisionTypeStartTimer.Ptr() decision.StartTimerDecisionAttributes = ToStartTimerDecisionAttributes(attr.StartTimerDecisionAttributes) case *apiv1.Decision_CompleteWorkflowExecutionDecisionAttributes: decision.DecisionType = types.DecisionTypeCompleteWorkflowExecution.Ptr() decision.CompleteWorkflowExecutionDecisionAttributes = ToCompleteWorkflowExecutionDecisionAttributes(attr.CompleteWorkflowExecutionDecisionAttributes) case *apiv1.Decision_FailWorkflowExecutionDecisionAttributes: decision.DecisionType = types.DecisionTypeFailWorkflowExecution.Ptr() decision.FailWorkflowExecutionDecisionAttributes = ToFailWorkflowExecutionDecisionAttributes(attr.FailWorkflowExecutionDecisionAttributes) case *apiv1.Decision_RequestCancelActivityTaskDecisionAttributes: decision.DecisionType = types.DecisionTypeRequestCancelActivityTask.Ptr() decision.RequestCancelActivityTaskDecisionAttributes = ToRequestCancelActivityTaskDecisionAttributes(attr.RequestCancelActivityTaskDecisionAttributes) case *apiv1.Decision_CancelTimerDecisionAttributes: decision.DecisionType = types.DecisionTypeCancelTimer.Ptr() decision.CancelTimerDecisionAttributes = ToCancelTimerDecisionAttributes(attr.CancelTimerDecisionAttributes) case *apiv1.Decision_CancelWorkflowExecutionDecisionAttributes: decision.DecisionType = types.DecisionTypeCancelWorkflowExecution.Ptr() decision.CancelWorkflowExecutionDecisionAttributes = ToCancelWorkflowExecutionDecisionAttributes(attr.CancelWorkflowExecutionDecisionAttributes) case *apiv1.Decision_RequestCancelExternalWorkflowExecutionDecisionAttributes: decision.DecisionType = types.DecisionTypeRequestCancelExternalWorkflowExecution.Ptr() decision.RequestCancelExternalWorkflowExecutionDecisionAttributes = ToRequestCancelExternalWorkflowExecutionDecisionAttributes(attr.RequestCancelExternalWorkflowExecutionDecisionAttributes) case *apiv1.Decision_RecordMarkerDecisionAttributes: decision.DecisionType = types.DecisionTypeRecordMarker.Ptr() decision.RecordMarkerDecisionAttributes = ToRecordMarkerDecisionAttributes(attr.RecordMarkerDecisionAttributes) case *apiv1.Decision_ContinueAsNewWorkflowExecutionDecisionAttributes: decision.DecisionType = types.DecisionTypeContinueAsNewWorkflowExecution.Ptr() decision.ContinueAsNewWorkflowExecutionDecisionAttributes = ToContinueAsNewWorkflowExecutionDecisionAttributes(attr.ContinueAsNewWorkflowExecutionDecisionAttributes) case *apiv1.Decision_StartChildWorkflowExecutionDecisionAttributes: decision.DecisionType = types.DecisionTypeStartChildWorkflowExecution.Ptr() decision.StartChildWorkflowExecutionDecisionAttributes = ToStartChildWorkflowExecutionDecisionAttributes(attr.StartChildWorkflowExecutionDecisionAttributes) case *apiv1.Decision_SignalExternalWorkflowExecutionDecisionAttributes: decision.DecisionType = types.DecisionTypeSignalExternalWorkflowExecution.Ptr() decision.SignalExternalWorkflowExecutionDecisionAttributes = ToSignalExternalWorkflowExecutionDecisionAttributes(attr.SignalExternalWorkflowExecutionDecisionAttributes) case *apiv1.Decision_UpsertWorkflowSearchAttributesDecisionAttributes: decision.DecisionType = types.DecisionTypeUpsertWorkflowSearchAttributes.Ptr() decision.UpsertWorkflowSearchAttributesDecisionAttributes = ToUpsertWorkflowSearchAttributesDecisionAttributes(attr.UpsertWorkflowSearchAttributesDecisionAttributes) } return &decision } func FromListClosedWorkflowExecutionsRequest(r *types.ListClosedWorkflowExecutionsRequest) *apiv1.ListClosedWorkflowExecutionsRequest { if r == nil { return nil } request := apiv1.ListClosedWorkflowExecutionsRequest{ Domain: r.Domain, PageSize: r.MaximumPageSize, NextPageToken: r.NextPageToken, StartTimeFilter: FromStartTimeFilter(r.StartTimeFilter), } if r.ExecutionFilter != nil { request.Filters = &apiv1.ListClosedWorkflowExecutionsRequest_ExecutionFilter{ ExecutionFilter: FromWorkflowExecutionFilter(r.ExecutionFilter), } } if r.TypeFilter != nil { request.Filters = &apiv1.ListClosedWorkflowExecutionsRequest_TypeFilter{ TypeFilter: FromWorkflowTypeFilter(r.TypeFilter), } } if r.StatusFilter != nil { request.Filters = &apiv1.ListClosedWorkflowExecutionsRequest_StatusFilter{ StatusFilter: FromStatusFilter(r.StatusFilter), } } return &request } func ToListClosedWorkflowExecutionsRequest(r *apiv1.ListClosedWorkflowExecutionsRequest) *types.ListClosedWorkflowExecutionsRequest { if r == nil { return nil } request := types.ListClosedWorkflowExecutionsRequest{ Domain: r.Domain, MaximumPageSize: r.PageSize, NextPageToken: r.NextPageToken, StartTimeFilter: ToStartTimeFilter(r.StartTimeFilter), } switch filters := r.Filters.(type) { case *apiv1.ListClosedWorkflowExecutionsRequest_ExecutionFilter: request.ExecutionFilter = ToWorkflowExecutionFilter(filters.ExecutionFilter) case *apiv1.ListClosedWorkflowExecutionsRequest_TypeFilter: request.TypeFilter = ToWorkflowTypeFilter(filters.TypeFilter) case *apiv1.ListClosedWorkflowExecutionsRequest_StatusFilter: request.StatusFilter = ToStatusFilter(filters.StatusFilter) } return &request } func FromListOpenWorkflowExecutionsRequest(r *types.ListOpenWorkflowExecutionsRequest) *apiv1.ListOpenWorkflowExecutionsRequest { if r == nil { return nil } request := apiv1.ListOpenWorkflowExecutionsRequest{ Domain: r.Domain, PageSize: r.MaximumPageSize, NextPageToken: r.NextPageToken, StartTimeFilter: FromStartTimeFilter(r.StartTimeFilter), } if r.ExecutionFilter != nil { request.Filters = &apiv1.ListOpenWorkflowExecutionsRequest_ExecutionFilter{ ExecutionFilter: FromWorkflowExecutionFilter(r.ExecutionFilter), } } if r.TypeFilter != nil { request.Filters = &apiv1.ListOpenWorkflowExecutionsRequest_TypeFilter{ TypeFilter: FromWorkflowTypeFilter(r.TypeFilter), } } return &request } func ToListOpenWorkflowExecutionsRequest(r *apiv1.ListOpenWorkflowExecutionsRequest) *types.ListOpenWorkflowExecutionsRequest { if r == nil { return nil } request := types.ListOpenWorkflowExecutionsRequest{ Domain: r.Domain, MaximumPageSize: r.PageSize, NextPageToken: r.NextPageToken, StartTimeFilter: ToStartTimeFilter(r.StartTimeFilter), } switch filters := r.Filters.(type) { case *apiv1.ListOpenWorkflowExecutionsRequest_ExecutionFilter: request.ExecutionFilter = ToWorkflowExecutionFilter(filters.ExecutionFilter) case *apiv1.ListOpenWorkflowExecutionsRequest_TypeFilter: request.TypeFilter = ToWorkflowTypeFilter(filters.TypeFilter) } return &request } func FromResetStickyTaskListResponse(t *types.ResetStickyTaskListResponse) *apiv1.ResetStickyTaskListResponse { if t == nil { return nil } return &apiv1.ResetStickyTaskListResponse{} } func ToResetStickyTaskListResponse(t *apiv1.ResetStickyTaskListResponse) *types.ResetStickyTaskListResponse { if t == nil { return nil } return &types.ResetStickyTaskListResponse{} } func FromAPITaskListPartitionConfig(t *types.TaskListPartitionConfig) *apiv1.TaskListPartitionConfig { if t == nil { return nil } return &apiv1.TaskListPartitionConfig{ Version: t.Version, NumReadPartitions: int32(len(t.ReadPartitions)), NumWritePartitions: int32(len(t.WritePartitions)), ReadPartitions: FromAPITaskListPartitionsMap(t.ReadPartitions), WritePartitions: FromAPITaskListPartitionsMap(t.WritePartitions), } } func ToAPITaskListPartitionConfig(t *apiv1.TaskListPartitionConfig) *types.TaskListPartitionConfig { if t == nil { return nil } return &types.TaskListPartitionConfig{ Version: t.Version, ReadPartitions: ToAPITaskListPartitionsMap(t.NumReadPartitions, t.ReadPartitions), WritePartitions: ToAPITaskListPartitionsMap(t.NumWritePartitions, t.WritePartitions), } } func FromAPITaskListPartition(t *types.TaskListPartition) *apiv1.TaskListPartition { if t == nil { return nil } return &apiv1.TaskListPartition{ IsolationGroups: t.IsolationGroups, } } func ToAPITaskListPartition(t *apiv1.TaskListPartition) *types.TaskListPartition { if t == nil { return nil } return &types.TaskListPartition{ IsolationGroups: t.IsolationGroups, } } func FromAPITaskListPartitionsMap(m map[int]*types.TaskListPartition) map[int32]*apiv1.TaskListPartition { if m == nil { return nil } result := make(map[int32]*apiv1.TaskListPartition, len(m)) for id, p := range m { result[int32(id)] = FromAPITaskListPartition(p) } return result } func ToAPITaskListPartitionsMap(numPartitions int32, m map[int32]*apiv1.TaskListPartition) map[int]*types.TaskListPartition { if m == nil && numPartitions == 0 { return nil } result := make(map[int]*types.TaskListPartition, len(m)) if numPartitions != int32(len(m)) { for i := int32(0); i < numPartitions; i++ { result[int(i)] = &types.TaskListPartition{} } } else { for id, p := range m { result[int(id)] = ToAPITaskListPartition(p) } } return result } func FromAutoConfigHint(t *types.AutoConfigHint) *apiv1.AutoConfigHint { if t == nil { return nil } return &apiv1.AutoConfigHint{ PollerWaitTimeInMs: t.PollerWaitTimeInMs, EnableAutoConfig: t.EnableAutoConfig, } } func ToAutoConfigHint(t *apiv1.AutoConfigHint) *types.AutoConfigHint { if t == nil { return nil } return &types.AutoConfigHint{ PollerWaitTimeInMs: t.PollerWaitTimeInMs, EnableAutoConfig: t.EnableAutoConfig, } } func FromCronOverlapPolicy(p *types.CronOverlapPolicy) apiv1.CronOverlapPolicy { if p == nil { return apiv1.CronOverlapPolicy_CRON_OVERLAP_POLICY_INVALID } switch *p { case types.CronOverlapPolicyBufferOne: return apiv1.CronOverlapPolicy_CRON_OVERLAP_POLICY_BUFFER_ONE case types.CronOverlapPolicySkipped: return apiv1.CronOverlapPolicy_CRON_OVERLAP_POLICY_SKIPPED } return apiv1.CronOverlapPolicy_CRON_OVERLAP_POLICY_INVALID } func ToCronOverlapPolicy(p apiv1.CronOverlapPolicy) *types.CronOverlapPolicy { switch p { case apiv1.CronOverlapPolicy_CRON_OVERLAP_POLICY_BUFFER_ONE: return types.CronOverlapPolicyBufferOne.Ptr() case apiv1.CronOverlapPolicy_CRON_OVERLAP_POLICY_SKIPPED: return types.CronOverlapPolicySkipped.Ptr() case apiv1.CronOverlapPolicy_CRON_OVERLAP_POLICY_INVALID: return nil } return nil } func FromClusterAttribute(c *types.ClusterAttribute) *apiv1.ClusterAttribute { if c == nil { return nil } return &apiv1.ClusterAttribute{ Scope: c.Scope, Name: c.Name, } } func ToClusterAttribute(c *apiv1.ClusterAttribute) *types.ClusterAttribute { if c == nil { return nil } return &types.ClusterAttribute{ Scope: c.Scope, Name: c.Name, } } func FromActiveClusterSelectionPolicy(p *types.ActiveClusterSelectionPolicy) *apiv1.ActiveClusterSelectionPolicy { if p == nil { return nil } // TODO(active-active): Remove the switch statement once the strategy is removed if p.ActiveClusterSelectionStrategy == nil { return &apiv1.ActiveClusterSelectionPolicy{ ClusterAttribute: FromClusterAttribute(p.ClusterAttribute), } } switch *p.ActiveClusterSelectionStrategy { case types.ActiveClusterSelectionStrategyRegionSticky: return &apiv1.ActiveClusterSelectionPolicy{ Strategy: apiv1.ActiveClusterSelectionStrategy_ACTIVE_CLUSTER_SELECTION_STRATEGY_REGION_STICKY, StrategyConfig: &apiv1.ActiveClusterSelectionPolicy_ActiveClusterStickyRegionConfig{ ActiveClusterStickyRegionConfig: &apiv1.ActiveClusterStickyRegionConfig{ StickyRegion: p.StickyRegion, }, }, ClusterAttribute: FromClusterAttribute(p.ClusterAttribute), } case types.ActiveClusterSelectionStrategyExternalEntity: return &apiv1.ActiveClusterSelectionPolicy{ Strategy: apiv1.ActiveClusterSelectionStrategy_ACTIVE_CLUSTER_SELECTION_STRATEGY_EXTERNAL_ENTITY, StrategyConfig: &apiv1.ActiveClusterSelectionPolicy_ActiveClusterExternalEntityConfig{ ActiveClusterExternalEntityConfig: &apiv1.ActiveClusterExternalEntityConfig{ ExternalEntityType: p.ExternalEntityType, ExternalEntityKey: p.ExternalEntityKey, }, }, ClusterAttribute: FromClusterAttribute(p.ClusterAttribute), } } return &apiv1.ActiveClusterSelectionPolicy{ ClusterAttribute: FromClusterAttribute(p.ClusterAttribute), } } func ToActiveClusterSelectionPolicy(p *apiv1.ActiveClusterSelectionPolicy) *types.ActiveClusterSelectionPolicy { if p == nil { return nil } // TODO(active-active): Remove the switch statement once the strategy is removed switch p.Strategy { case apiv1.ActiveClusterSelectionStrategy_ACTIVE_CLUSTER_SELECTION_STRATEGY_REGION_STICKY: return &types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyRegionSticky.Ptr(), StickyRegion: p.StrategyConfig.(*apiv1.ActiveClusterSelectionPolicy_ActiveClusterStickyRegionConfig).ActiveClusterStickyRegionConfig.StickyRegion, ClusterAttribute: ToClusterAttribute(p.ClusterAttribute), } case apiv1.ActiveClusterSelectionStrategy_ACTIVE_CLUSTER_SELECTION_STRATEGY_EXTERNAL_ENTITY: return &types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyExternalEntity.Ptr(), ExternalEntityType: p.StrategyConfig.(*apiv1.ActiveClusterSelectionPolicy_ActiveClusterExternalEntityConfig).ActiveClusterExternalEntityConfig.ExternalEntityType, ExternalEntityKey: p.StrategyConfig.(*apiv1.ActiveClusterSelectionPolicy_ActiveClusterExternalEntityConfig).ActiveClusterExternalEntityConfig.ExternalEntityKey, ClusterAttribute: ToClusterAttribute(p.ClusterAttribute), } } return &types.ActiveClusterSelectionPolicy{ ClusterAttribute: ToClusterAttribute(p.ClusterAttribute), } } ================================================ FILE: common/types/mapper/proto/api_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "testing" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "github.com/uber/cadence/common" "github.com/uber/cadence/common/testing/testdatagen" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/testutils" "github.com/uber/cadence/common/types/testdata" ) func TestActivityLocalDispatchInfo(t *testing.T) { for _, item := range []*types.ActivityLocalDispatchInfo{nil, {}, &testdata.ActivityLocalDispatchInfo} { assert.Equal(t, item, ToActivityLocalDispatchInfo(FromActivityLocalDispatchInfo(item))) } } func TestActivityTaskCancelRequestedEventAttributes(t *testing.T) { for _, item := range []*types.ActivityTaskCancelRequestedEventAttributes{nil, {}, &testdata.ActivityTaskCancelRequestedEventAttributes} { assert.Equal(t, item, ToActivityTaskCancelRequestedEventAttributes(FromActivityTaskCancelRequestedEventAttributes(item))) } } func TestActivityTaskCanceledEventAttributes(t *testing.T) { for _, item := range []*types.ActivityTaskCanceledEventAttributes{nil, {}, &testdata.ActivityTaskCanceledEventAttributes} { assert.Equal(t, item, ToActivityTaskCanceledEventAttributes(FromActivityTaskCanceledEventAttributes(item))) } } func TestActivityTaskCompletedEventAttributes(t *testing.T) { for _, item := range []*types.ActivityTaskCompletedEventAttributes{nil, {}, &testdata.ActivityTaskCompletedEventAttributes} { assert.Equal(t, item, ToActivityTaskCompletedEventAttributes(FromActivityTaskCompletedEventAttributes(item))) } } func TestActivityTaskFailedEventAttributes(t *testing.T) { for _, item := range []*types.ActivityTaskFailedEventAttributes{nil, {}, &testdata.ActivityTaskFailedEventAttributes} { assert.Equal(t, item, ToActivityTaskFailedEventAttributes(FromActivityTaskFailedEventAttributes(item))) } } func TestActivityTaskScheduledEventAttributes(t *testing.T) { // since proto definition for Domain field doesn't have pointer, To(From(item)) won't be equal to item when item's Domain is a nil pointer // this is fine as the code using this field will check both if the field is a nil pointer and if it's a pointer to an empty string. for _, item := range []*types.ActivityTaskScheduledEventAttributes{nil, {Domain: common.StringPtr("")}, &testdata.ActivityTaskScheduledEventAttributes} { assert.Equal(t, item, ToActivityTaskScheduledEventAttributes(FromActivityTaskScheduledEventAttributes(item))) } } func TestActivityTaskStartedEventAttributes(t *testing.T) { for _, item := range []*types.ActivityTaskStartedEventAttributes{nil, {}, &testdata.ActivityTaskStartedEventAttributes} { assert.Equal(t, item, ToActivityTaskStartedEventAttributes(FromActivityTaskStartedEventAttributes(item))) } } func TestActivityTaskTimedOutEventAttributes(t *testing.T) { for _, item := range []*types.ActivityTaskTimedOutEventAttributes{nil, {}, &testdata.ActivityTaskTimedOutEventAttributes} { assert.Equal(t, item, ToActivityTaskTimedOutEventAttributes(FromActivityTaskTimedOutEventAttributes(item))) } } func TestActivityType(t *testing.T) { for _, item := range []*types.ActivityType{nil, {}, &testdata.ActivityType} { assert.Equal(t, item, ToActivityType(FromActivityType(item))) } } func TestBadBinaries(t *testing.T) { for _, item := range []*types.BadBinaries{nil, {}, &testdata.BadBinaries} { assert.Equal(t, item, ToBadBinaries(FromBadBinaries(item))) } } func TestBadBinaryInfo(t *testing.T) { for _, item := range []*types.BadBinaryInfo{nil, {}, &testdata.BadBinaryInfo} { assert.Equal(t, item, ToBadBinaryInfo(FromBadBinaryInfo(item))) } } func TestCancelTimerDecisionAttributes(t *testing.T) { for _, item := range []*types.CancelTimerDecisionAttributes{nil, {}, &testdata.CancelTimerDecisionAttributes} { assert.Equal(t, item, ToCancelTimerDecisionAttributes(FromCancelTimerDecisionAttributes(item))) } } func TestCancelTimerFailedEventAttributes(t *testing.T) { for _, item := range []*types.CancelTimerFailedEventAttributes{nil, {}, &testdata.CancelTimerFailedEventAttributes} { assert.Equal(t, item, ToCancelTimerFailedEventAttributes(FromCancelTimerFailedEventAttributes(item))) } } func TestCancelWorkflowExecutionDecisionAttributes(t *testing.T) { for _, item := range []*types.CancelWorkflowExecutionDecisionAttributes{nil, {}, &testdata.CancelWorkflowExecutionDecisionAttributes} { assert.Equal(t, item, ToCancelWorkflowExecutionDecisionAttributes(FromCancelWorkflowExecutionDecisionAttributes(item))) } } func TestChildWorkflowExecutionCanceledEventAttributes(t *testing.T) { for _, item := range []*types.ChildWorkflowExecutionCanceledEventAttributes{nil, {}, &testdata.ChildWorkflowExecutionCanceledEventAttributes} { assert.Equal(t, item, ToChildWorkflowExecutionCanceledEventAttributes(FromChildWorkflowExecutionCanceledEventAttributes(item))) } } func TestChildWorkflowExecutionCompletedEventAttributes(t *testing.T) { for _, item := range []*types.ChildWorkflowExecutionCompletedEventAttributes{nil, {}, &testdata.ChildWorkflowExecutionCompletedEventAttributes} { assert.Equal(t, item, ToChildWorkflowExecutionCompletedEventAttributes(FromChildWorkflowExecutionCompletedEventAttributes(item))) } } func TestChildWorkflowExecutionFailedEventAttributes(t *testing.T) { for _, item := range []*types.ChildWorkflowExecutionFailedEventAttributes{nil, {}, &testdata.ChildWorkflowExecutionFailedEventAttributes} { assert.Equal(t, item, ToChildWorkflowExecutionFailedEventAttributes(FromChildWorkflowExecutionFailedEventAttributes(item))) } } func TestChildWorkflowExecutionStartedEventAttributes(t *testing.T) { for _, item := range []*types.ChildWorkflowExecutionStartedEventAttributes{nil, {}, &testdata.ChildWorkflowExecutionStartedEventAttributes} { assert.Equal(t, item, ToChildWorkflowExecutionStartedEventAttributes(FromChildWorkflowExecutionStartedEventAttributes(item))) } } func TestChildWorkflowExecutionTerminatedEventAttributes(t *testing.T) { for _, item := range []*types.ChildWorkflowExecutionTerminatedEventAttributes{nil, {}, &testdata.ChildWorkflowExecutionTerminatedEventAttributes} { assert.Equal(t, item, ToChildWorkflowExecutionTerminatedEventAttributes(FromChildWorkflowExecutionTerminatedEventAttributes(item))) } } func TestChildWorkflowExecutionTimedOutEventAttributes(t *testing.T) { for _, item := range []*types.ChildWorkflowExecutionTimedOutEventAttributes{nil, {}, &testdata.ChildWorkflowExecutionTimedOutEventAttributes} { assert.Equal(t, item, ToChildWorkflowExecutionTimedOutEventAttributes(FromChildWorkflowExecutionTimedOutEventAttributes(item))) } } func TestClusterReplicationConfiguration(t *testing.T) { for _, item := range []*types.ClusterReplicationConfiguration{nil, {}, &testdata.ClusterReplicationConfiguration} { assert.Equal(t, item, ToClusterReplicationConfiguration(FromClusterReplicationConfiguration(item))) } } func TestCompleteWorkflowExecutionDecisionAttributes(t *testing.T) { for _, item := range []*types.CompleteWorkflowExecutionDecisionAttributes{nil, {}, &testdata.CompleteWorkflowExecutionDecisionAttributes} { assert.Equal(t, item, ToCompleteWorkflowExecutionDecisionAttributes(FromCompleteWorkflowExecutionDecisionAttributes(item))) } } func TestContinueAsNewWorkflowExecutionDecisionAttributes(t *testing.T) { for _, item := range []*types.ContinueAsNewWorkflowExecutionDecisionAttributes{nil, {}, &testdata.ContinueAsNewWorkflowExecutionDecisionAttributes} { assert.Equal(t, item, ToContinueAsNewWorkflowExecutionDecisionAttributes(FromContinueAsNewWorkflowExecutionDecisionAttributes(item))) } } func TestCountWorkflowExecutionsRequest(t *testing.T) { for _, item := range []*types.CountWorkflowExecutionsRequest{nil, {}, &testdata.CountWorkflowExecutionsRequest} { assert.Equal(t, item, ToCountWorkflowExecutionsRequest(FromCountWorkflowExecutionsRequest(item))) } } func TestCountWorkflowExecutionsResponse(t *testing.T) { for _, item := range []*types.CountWorkflowExecutionsResponse{nil, {}, &testdata.CountWorkflowExecutionsResponse} { assert.Equal(t, item, ToCountWorkflowExecutionsResponse(FromCountWorkflowExecutionsResponse(item))) } } func TestDataBlob(t *testing.T) { for _, item := range []*types.DataBlob{nil, {}, &testdata.DataBlob} { assert.Equal(t, item, ToDataBlob(FromDataBlob(item))) } } func TestDecisionTaskCompletedEventAttributes(t *testing.T) { for _, item := range []*types.DecisionTaskCompletedEventAttributes{nil, {}, &testdata.DecisionTaskCompletedEventAttributes} { assert.Equal(t, item, ToDecisionTaskCompletedEventAttributes(FromDecisionTaskCompletedEventAttributes(item))) } } func TestDecisionTaskFailedEventAttributes(t *testing.T) { for _, item := range []*types.DecisionTaskFailedEventAttributes{nil, {}, &testdata.DecisionTaskFailedEventAttributes} { assert.Equal(t, item, ToDecisionTaskFailedEventAttributes(FromDecisionTaskFailedEventAttributes(item))) } } func TestDecisionTaskScheduledEventAttributes(t *testing.T) { for _, item := range []*types.DecisionTaskScheduledEventAttributes{nil, {}, &testdata.DecisionTaskScheduledEventAttributes} { assert.Equal(t, item, ToDecisionTaskScheduledEventAttributes(FromDecisionTaskScheduledEventAttributes(item))) } } func TestDecisionTaskStartedEventAttributes(t *testing.T) { for _, item := range []*types.DecisionTaskStartedEventAttributes{nil, {}, &testdata.DecisionTaskStartedEventAttributes} { assert.Equal(t, item, ToDecisionTaskStartedEventAttributes(FromDecisionTaskStartedEventAttributes(item))) } } func TestDecisionTaskTimedOutEventAttributes(t *testing.T) { for _, item := range []*types.DecisionTaskTimedOutEventAttributes{nil, {}, &testdata.DecisionTaskTimedOutEventAttributes} { assert.Equal(t, item, ToDecisionTaskTimedOutEventAttributes(FromDecisionTaskTimedOutEventAttributes(item))) } } func TestDeleteDomainRequest(t *testing.T) { for _, item := range []*types.DeleteDomainRequest{nil, {}, &testdata.DeleteDomainRequest} { assert.Equal(t, item, ToDeleteDomainRequest(FromDeleteDomainRequest(item))) } } func TestDeprecateDomainRequest(t *testing.T) { for _, item := range []*types.DeprecateDomainRequest{nil, {}, &testdata.DeprecateDomainRequest} { assert.Equal(t, item, ToDeprecateDomainRequest(FromDeprecateDomainRequest(item))) } } func TestDescribeDomainRequest(t *testing.T) { for _, item := range []*types.DescribeDomainRequest{ &testdata.DescribeDomainRequest_ID, &testdata.DescribeDomainRequest_Name, } { assert.Equal(t, item, ToDescribeDomainRequest(FromDescribeDomainRequest(item))) } assert.Nil(t, ToDescribeDomainRequest(nil)) assert.Nil(t, FromDescribeDomainRequest(nil)) assert.Panics(t, func() { ToDescribeDomainRequest(&apiv1.DescribeDomainRequest{}) }) assert.Panics(t, func() { FromDescribeDomainRequest(&types.DescribeDomainRequest{}) }) } func TestDescribeDomainResponse_Domain(t *testing.T) { for _, item := range []*types.DescribeDomainResponse{nil, &testdata.DescribeDomainResponse} { assert.Equal(t, item, ToDescribeDomainResponseDomain(FromDescribeDomainResponseDomain(item))) } } func TestDescribeDomainResponse(t *testing.T) { for _, item := range []*types.DescribeDomainResponse{nil, &testdata.DescribeDomainResponse} { assert.Equal(t, item, ToDescribeDomainResponse(FromDescribeDomainResponse(item))) } } func TestDescribeTaskListRequest(t *testing.T) { for _, item := range []*types.DescribeTaskListRequest{nil, {}, &testdata.DescribeTaskListRequest} { assert.Equal(t, item, ToDescribeTaskListRequest(FromDescribeTaskListRequest(item))) } } func TestDescribeTaskListResponse(t *testing.T) { for _, item := range []*types.DescribeTaskListResponse{nil, {}, &testdata.DescribeTaskListResponse} { assert.Equal(t, item, ToDescribeTaskListResponse(FromDescribeTaskListResponse(item))) } } func TestDescribeWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.DescribeWorkflowExecutionRequest{nil, {}, &testdata.DescribeWorkflowExecutionRequest} { assert.Equal(t, item, ToDescribeWorkflowExecutionRequest(FromDescribeWorkflowExecutionRequest(item))) } } func TestDescribeWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.DescribeWorkflowExecutionResponse{nil, {}, &testdata.DescribeWorkflowExecutionResponse} { assert.Equal(t, item, ToDescribeWorkflowExecutionResponse(FromDescribeWorkflowExecutionResponse(item))) } } func TestDiagnoseWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.DiagnoseWorkflowExecutionRequest{nil, {}, &testdata.DiagnoseWorkflowExecutionRequest} { assert.Equal(t, item, ToDiagnoseWorkflowExecutionRequest(FromDiagnoseWorkflowExecutionRequest(item))) } } func TestDiagnoseWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.DiagnoseWorkflowExecutionResponse{nil, {}, &testdata.DiagnoseWorkflowExecutionResponse} { assert.Equal(t, item, ToDiagnoseWorkflowExecutionResponse(FromDiagnoseWorkflowExecutionResponse(item))) } } func TestExternalWorkflowExecutionCancelRequestedEventAttributes(t *testing.T) { for _, item := range []*types.ExternalWorkflowExecutionCancelRequestedEventAttributes{nil, {}, &testdata.ExternalWorkflowExecutionCancelRequestedEventAttributes} { assert.Equal(t, item, ToExternalWorkflowExecutionCancelRequestedEventAttributes(FromExternalWorkflowExecutionCancelRequestedEventAttributes(item))) } } func TestExternalWorkflowExecutionSignaledEventAttributes(t *testing.T) { for _, item := range []*types.ExternalWorkflowExecutionSignaledEventAttributes{nil, {}, &testdata.ExternalWorkflowExecutionSignaledEventAttributes} { assert.Equal(t, item, ToExternalWorkflowExecutionSignaledEventAttributes(FromExternalWorkflowExecutionSignaledEventAttributes(item))) } } func TestFailWorkflowExecutionDecisionAttributes(t *testing.T) { for _, item := range []*types.FailWorkflowExecutionDecisionAttributes{nil, {}, &testdata.FailWorkflowExecutionDecisionAttributes} { assert.Equal(t, item, ToFailWorkflowExecutionDecisionAttributes(FromFailWorkflowExecutionDecisionAttributes(item))) } } func TestGetClusterInfoResponse(t *testing.T) { for _, item := range []*types.ClusterInfo{nil, {}, &testdata.ClusterInfo} { assert.Equal(t, item, ToGetClusterInfoResponse(FromGetClusterInfoResponse(item))) } } func TestGetSearchAttributesResponse(t *testing.T) { for _, item := range []*types.GetSearchAttributesResponse{nil, {}, &testdata.GetSearchAttributesResponse} { assert.Equal(t, item, ToGetSearchAttributesResponse(FromGetSearchAttributesResponse(item))) } } func TestGetWorkflowExecutionHistoryRequest(t *testing.T) { for _, item := range []*types.GetWorkflowExecutionHistoryRequest{nil, {}, &testdata.GetWorkflowExecutionHistoryRequest} { assert.Equal(t, item, ToGetWorkflowExecutionHistoryRequest(FromGetWorkflowExecutionHistoryRequest(item))) } } func TestGetWorkflowExecutionHistoryResponse(t *testing.T) { for _, item := range []*types.GetWorkflowExecutionHistoryResponse{nil, {}, &testdata.GetWorkflowExecutionHistoryResponse} { assert.Equal(t, item, ToGetWorkflowExecutionHistoryResponse(FromGetWorkflowExecutionHistoryResponse(item))) } } func TestHeader(t *testing.T) { for _, item := range []*types.Header{nil, {}, &testdata.Header} { assert.Equal(t, item, ToHeader(FromHeader(item))) } } func TestHealthResponse(t *testing.T) { for _, item := range []*types.HealthStatus{nil, {}, &testdata.HealthStatus} { assert.Equal(t, item, ToHealthResponse(FromHealthResponse(item))) } } func TestHistory(t *testing.T) { for _, item := range []*types.History{nil, &testdata.History} { assert.Equal(t, item, ToHistory(FromHistory(item))) } } func TestListArchivedWorkflowExecutionsRequest(t *testing.T) { for _, item := range []*types.ListArchivedWorkflowExecutionsRequest{nil, {}, &testdata.ListArchivedWorkflowExecutionsRequest} { assert.Equal(t, item, ToListArchivedWorkflowExecutionsRequest(FromListArchivedWorkflowExecutionsRequest(item))) } } func TestListArchivedWorkflowExecutionsResponse(t *testing.T) { for _, item := range []*types.ListArchivedWorkflowExecutionsResponse{nil, {}, &testdata.ListArchivedWorkflowExecutionsResponse} { assert.Equal(t, item, ToListArchivedWorkflowExecutionsResponse(FromListArchivedWorkflowExecutionsResponse(item))) } } func TestListClosedWorkflowExecutionsResponse(t *testing.T) { for _, item := range []*types.ListClosedWorkflowExecutionsResponse{nil, {}, &testdata.ListClosedWorkflowExecutionsResponse} { assert.Equal(t, item, ToListClosedWorkflowExecutionsResponse(FromListClosedWorkflowExecutionsResponse(item))) } } func TestListDomainsRequest(t *testing.T) { for _, item := range []*types.ListDomainsRequest{nil, {}, &testdata.ListDomainsRequest} { assert.Equal(t, item, ToListDomainsRequest(FromListDomainsRequest(item))) } } func TestListDomainsResponse(t *testing.T) { for _, item := range []*types.ListDomainsResponse{nil, {}, &testdata.ListDomainsResponse} { assert.Equal(t, item, ToListDomainsResponse(FromListDomainsResponse(item))) } } func TestListOpenWorkflowExecutionsResponse(t *testing.T) { for _, item := range []*types.ListOpenWorkflowExecutionsResponse{nil, {}, &testdata.ListOpenWorkflowExecutionsResponse} { assert.Equal(t, item, ToListOpenWorkflowExecutionsResponse(FromListOpenWorkflowExecutionsResponse(item))) } } func TestListTaskListPartitionsRequest(t *testing.T) { for _, item := range []*types.ListTaskListPartitionsRequest{nil, {}, &testdata.ListTaskListPartitionsRequest} { assert.Equal(t, item, ToListTaskListPartitionsRequest(FromListTaskListPartitionsRequest(item))) } } func TestListTaskListPartitionsResponse(t *testing.T) { for _, item := range []*types.ListTaskListPartitionsResponse{nil, {}, &testdata.ListTaskListPartitionsResponse} { assert.Equal(t, item, ToListTaskListPartitionsResponse(FromListTaskListPartitionsResponse(item))) } } func TestListWorkflowExecutionsRequest(t *testing.T) { for _, item := range []*types.ListWorkflowExecutionsRequest{nil, {}, &testdata.ListWorkflowExecutionsRequest} { assert.Equal(t, item, ToListWorkflowExecutionsRequest(FromListWorkflowExecutionsRequest(item))) } } func TestListWorkflowExecutionsResponse(t *testing.T) { for _, item := range []*types.ListWorkflowExecutionsResponse{nil, {}, &testdata.ListWorkflowExecutionsResponse} { assert.Equal(t, item, ToListWorkflowExecutionsResponse(FromListWorkflowExecutionsResponse(item))) } } func TestMarkerRecordedEventAttributes(t *testing.T) { for _, item := range []*types.MarkerRecordedEventAttributes{nil, {}, &testdata.MarkerRecordedEventAttributes} { assert.Equal(t, item, ToMarkerRecordedEventAttributes(FromMarkerRecordedEventAttributes(item))) } } func TestMemo(t *testing.T) { for _, item := range []*types.Memo{nil, {}, &testdata.Memo} { assert.Equal(t, item, ToMemo(FromMemo(item))) } } func TestPendingActivityInfo(t *testing.T) { for _, item := range []*types.PendingActivityInfo{nil, {}, &testdata.PendingActivityInfo} { assert.Equal(t, item, ToPendingActivityInfo(FromPendingActivityInfo(item))) } } func TestPendingChildExecutionInfo(t *testing.T) { for _, item := range []*types.PendingChildExecutionInfo{nil, {}, &testdata.PendingChildExecutionInfo} { assert.Equal(t, item, ToPendingChildExecutionInfo(FromPendingChildExecutionInfo(item))) } } func TestPendingDecisionInfo(t *testing.T) { for _, item := range []*types.PendingDecisionInfo{nil, {}, &testdata.PendingDecisionInfo} { assert.Equal(t, item, ToPendingDecisionInfo(FromPendingDecisionInfo(item))) } } func TestPollForActivityTaskRequest(t *testing.T) { for _, item := range []*types.PollForActivityTaskRequest{nil, {}, &testdata.PollForActivityTaskRequest} { assert.Equal(t, item, ToPollForActivityTaskRequest(FromPollForActivityTaskRequest(item))) } } func TestPollForActivityTaskResponse(t *testing.T) { for _, item := range []*types.PollForActivityTaskResponse{nil, {}, &testdata.PollForActivityTaskResponse} { assert.Equal(t, item, ToPollForActivityTaskResponse(FromPollForActivityTaskResponse(item))) } } func TestPollForDecisionTaskRequest(t *testing.T) { for _, item := range []*types.PollForDecisionTaskRequest{nil, {}, &testdata.PollForDecisionTaskRequest} { assert.Equal(t, item, ToPollForDecisionTaskRequest(FromPollForDecisionTaskRequest(item))) } } func TestPollForDecisionTaskResponse(t *testing.T) { for _, item := range []*types.PollForDecisionTaskResponse{nil, {}, &testdata.PollForDecisionTaskResponse} { assert.Equal(t, item, ToPollForDecisionTaskResponse(FromPollForDecisionTaskResponse(item))) } } func TestPollerInfo(t *testing.T) { for _, item := range []*types.PollerInfo{nil, {}, &testdata.PollerInfo} { assert.Equal(t, item, ToPollerInfo(FromPollerInfo(item))) } } func TestQueryRejected(t *testing.T) { for _, item := range []*types.QueryRejected{nil, {}, &testdata.QueryRejected} { assert.Equal(t, item, ToQueryRejected(FromQueryRejected(item))) } } func TestQueryWorkflowRequest(t *testing.T) { for _, item := range []*types.QueryWorkflowRequest{nil, {}, &testdata.QueryWorkflowRequest} { assert.Equal(t, item, ToQueryWorkflowRequest(FromQueryWorkflowRequest(item))) } } func TestQueryWorkflowResponse(t *testing.T) { for _, item := range []*types.QueryWorkflowResponse{nil, {}, &testdata.QueryWorkflowResponse} { assert.Equal(t, item, ToQueryWorkflowResponse(FromQueryWorkflowResponse(item))) } } func TestRecordActivityTaskHeartbeatByIDRequest(t *testing.T) { for _, item := range []*types.RecordActivityTaskHeartbeatByIDRequest{nil, {}, &testdata.RecordActivityTaskHeartbeatByIDRequest} { assert.Equal(t, item, ToRecordActivityTaskHeartbeatByIDRequest(FromRecordActivityTaskHeartbeatByIDRequest(item))) } } func TestRecordActivityTaskHeartbeatByIDResponse(t *testing.T) { for _, item := range []*types.RecordActivityTaskHeartbeatResponse{nil, {}, &testdata.RecordActivityTaskHeartbeatResponse} { assert.Equal(t, item, ToRecordActivityTaskHeartbeatByIDResponse(FromRecordActivityTaskHeartbeatByIDResponse(item))) } } func TestRecordActivityTaskHeartbeatRequest(t *testing.T) { for _, item := range []*types.RecordActivityTaskHeartbeatRequest{nil, {}, &testdata.RecordActivityTaskHeartbeatRequest} { assert.Equal(t, item, ToRecordActivityTaskHeartbeatRequest(FromRecordActivityTaskHeartbeatRequest(item))) } } func TestRecordActivityTaskHeartbeatResponse(t *testing.T) { for _, item := range []*types.RecordActivityTaskHeartbeatResponse{nil, {}, &testdata.RecordActivityTaskHeartbeatResponse} { assert.Equal(t, item, ToRecordActivityTaskHeartbeatResponse(FromRecordActivityTaskHeartbeatResponse(item))) } } func TestRecordMarkerDecisionAttributes(t *testing.T) { for _, item := range []*types.RecordMarkerDecisionAttributes{nil, {}, &testdata.RecordMarkerDecisionAttributes} { assert.Equal(t, item, ToRecordMarkerDecisionAttributes(FromRecordMarkerDecisionAttributes(item))) } } func TestRegisterDomainRequestFuzz(t *testing.T) { t.Run("round trip from internal", func(t *testing.T) { testutils.EnsureFuzzCoverage(t, []string{ "nil", "empty", "filled", }, func(t *testing.T, f *fuzz.Fuzzer) string { // Configure fuzzer to generate valid enum values and reasonable day ranges fuzzer := f.Funcs( func(e *types.ArchivalStatus, c fuzz.Continue) { *e = types.ArchivalStatus(c.Intn(2)) // 0-1 are valid values (Disabled=0, Enabled=1) }, func(days *int32, c fuzz.Continue) { // Generate reasonable retention period values to avoid precision loss in conversion *days = int32(c.Intn(10000)) // 0-9999 days is reasonable range }, ).NilChance(0.3) var orig *types.RegisterDomainRequest fuzzer.Fuzz(&orig) out := ToRegisterDomainRequest(FromRegisterDomainRequest(orig)) // Proto RegisterDomainRequest doesn't support EmitMetric field, it's always fixed on if orig != nil { expected := *orig // Copy the struct expected.EmitMetric = common.BoolPtr(true) // this is a legacy field which is always true. It's probably safe to remove assert.Equal(t, &expected, out, "RegisterDomainRequest did not survive round-tripping") } else { assert.Equal(t, orig, out, "RegisterDomainRequest did not survive round-tripping") } if orig == nil { return "nil" } if orig.Name == "" && orig.ActiveClusterName == "" && orig.ActiveClusters == nil { return "empty" } return "filled" }) }) } func TestRequestCancelActivityTaskDecisionAttributes(t *testing.T) { for _, item := range []*types.RequestCancelActivityTaskDecisionAttributes{nil, {}, &testdata.RequestCancelActivityTaskDecisionAttributes} { assert.Equal(t, item, ToRequestCancelActivityTaskDecisionAttributes(FromRequestCancelActivityTaskDecisionAttributes(item))) } } func TestRequestCancelActivityTaskFailedEventAttributes(t *testing.T) { for _, item := range []*types.RequestCancelActivityTaskFailedEventAttributes{nil, {}, &testdata.RequestCancelActivityTaskFailedEventAttributes} { assert.Equal(t, item, ToRequestCancelActivityTaskFailedEventAttributes(FromRequestCancelActivityTaskFailedEventAttributes(item))) } } func TestRequestCancelExternalWorkflowExecutionDecisionAttributes(t *testing.T) { for _, item := range []*types.RequestCancelExternalWorkflowExecutionDecisionAttributes{nil, {}, &testdata.RequestCancelExternalWorkflowExecutionDecisionAttributes} { assert.Equal(t, item, ToRequestCancelExternalWorkflowExecutionDecisionAttributes(FromRequestCancelExternalWorkflowExecutionDecisionAttributes(item))) } } func TestRequestCancelExternalWorkflowExecutionFailedEventAttributes(t *testing.T) { for _, item := range []*types.RequestCancelExternalWorkflowExecutionFailedEventAttributes{nil, {}, &testdata.RequestCancelExternalWorkflowExecutionFailedEventAttributes} { assert.Equal(t, item, ToRequestCancelExternalWorkflowExecutionFailedEventAttributes(FromRequestCancelExternalWorkflowExecutionFailedEventAttributes(item))) } } func TestRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(t *testing.T) { for _, item := range []*types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{nil, {}, &testdata.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes} { assert.Equal(t, item, ToRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(FromRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(item))) } } func TestRequestCancelWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.RequestCancelWorkflowExecutionRequest{nil, {}, &testdata.RequestCancelWorkflowExecutionRequest} { assert.Equal(t, item, ToRequestCancelWorkflowExecutionRequest(FromRequestCancelWorkflowExecutionRequest(item))) } } func TestResetPointInfo(t *testing.T) { for _, item := range []*types.ResetPointInfo{nil, {}, &testdata.ResetPointInfo} { assert.Equal(t, item, ToResetPointInfo(FromResetPointInfo(item))) } } func TestResetPoints(t *testing.T) { for _, item := range []*types.ResetPoints{nil, {}, &testdata.ResetPoints} { assert.Equal(t, item, ToResetPoints(FromResetPoints(item))) } } func TestResetStickyTaskListRequest(t *testing.T) { for _, item := range []*types.ResetStickyTaskListRequest{nil, {}, &testdata.ResetStickyTaskListRequest} { assert.Equal(t, item, ToResetStickyTaskListRequest(FromResetStickyTaskListRequest(item))) } } func TestResetWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.ResetWorkflowExecutionRequest{nil, {}, &testdata.ResetWorkflowExecutionRequest} { assert.Equal(t, item, ToResetWorkflowExecutionRequest(FromResetWorkflowExecutionRequest(item))) } } func TestResetWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.ResetWorkflowExecutionResponse{nil, {}, &testdata.ResetWorkflowExecutionResponse} { assert.Equal(t, item, ToResetWorkflowExecutionResponse(FromResetWorkflowExecutionResponse(item))) } } func TestRespondActivityTaskCanceledByIDRequest(t *testing.T) { for _, item := range []*types.RespondActivityTaskCanceledByIDRequest{nil, {}, &testdata.RespondActivityTaskCanceledByIDRequest} { assert.Equal(t, item, ToRespondActivityTaskCanceledByIDRequest(FromRespondActivityTaskCanceledByIDRequest(item))) } } func TestRespondActivityTaskCanceledRequest(t *testing.T) { for _, item := range []*types.RespondActivityTaskCanceledRequest{nil, {}, &testdata.RespondActivityTaskCanceledRequest} { assert.Equal(t, item, ToRespondActivityTaskCanceledRequest(FromRespondActivityTaskCanceledRequest(item))) } } func TestRespondActivityTaskCompletedByIDRequest(t *testing.T) { for _, item := range []*types.RespondActivityTaskCompletedByIDRequest{nil, {}, &testdata.RespondActivityTaskCompletedByIDRequest} { assert.Equal(t, item, ToRespondActivityTaskCompletedByIDRequest(FromRespondActivityTaskCompletedByIDRequest(item))) } } func TestRespondActivityTaskCompletedRequest(t *testing.T) { for _, item := range []*types.RespondActivityTaskCompletedRequest{nil, {}, &testdata.RespondActivityTaskCompletedRequest} { assert.Equal(t, item, ToRespondActivityTaskCompletedRequest(FromRespondActivityTaskCompletedRequest(item))) } } func TestRespondActivityTaskFailedByIDRequest(t *testing.T) { for _, item := range []*types.RespondActivityTaskFailedByIDRequest{nil, {}, &testdata.RespondActivityTaskFailedByIDRequest} { assert.Equal(t, item, ToRespondActivityTaskFailedByIDRequest(FromRespondActivityTaskFailedByIDRequest(item))) } } func TestRespondActivityTaskFailedRequest(t *testing.T) { for _, item := range []*types.RespondActivityTaskFailedRequest{nil, {}, &testdata.RespondActivityTaskFailedRequest} { assert.Equal(t, item, ToRespondActivityTaskFailedRequest(FromRespondActivityTaskFailedRequest(item))) } } func TestRespondDecisionTaskCompletedRequest(t *testing.T) { for _, item := range []*types.RespondDecisionTaskCompletedRequest{nil, {}, &testdata.RespondDecisionTaskCompletedRequest} { assert.Equal(t, item, ToRespondDecisionTaskCompletedRequest(FromRespondDecisionTaskCompletedRequest(item))) } } func TestRespondDecisionTaskCompletedResponse(t *testing.T) { for _, item := range []*types.RespondDecisionTaskCompletedResponse{nil, {}, &testdata.RespondDecisionTaskCompletedResponse} { assert.Equal(t, item, ToRespondDecisionTaskCompletedResponse(FromRespondDecisionTaskCompletedResponse(item))) } } func TestRespondDecisionTaskFailedRequest(t *testing.T) { for _, item := range []*types.RespondDecisionTaskFailedRequest{nil, {}, &testdata.RespondDecisionTaskFailedRequest} { assert.Equal(t, item, ToRespondDecisionTaskFailedRequest(FromRespondDecisionTaskFailedRequest(item))) } } func TestRespondQueryTaskCompletedRequest(t *testing.T) { for _, item := range []*types.RespondQueryTaskCompletedRequest{nil, {}, &testdata.RespondQueryTaskCompletedRequest} { assert.Equal(t, item, ToRespondQueryTaskCompletedRequest(FromRespondQueryTaskCompletedRequest(item))) } } func TestRetryPolicy(t *testing.T) { for _, item := range []*types.RetryPolicy{nil, {}, &testdata.RetryPolicy} { assert.Equal(t, item, ToRetryPolicy(FromRetryPolicy(item))) } } func TestScanWorkflowExecutionsRequest(t *testing.T) { for _, item := range []*types.ListWorkflowExecutionsRequest{nil, {}, &testdata.ListWorkflowExecutionsRequest} { assert.Equal(t, item, ToScanWorkflowExecutionsRequest(FromScanWorkflowExecutionsRequest(item))) } } func TestScanWorkflowExecutionsResponse(t *testing.T) { for _, item := range []*types.ListWorkflowExecutionsResponse{nil, {}, &testdata.ListWorkflowExecutionsResponse} { assert.Equal(t, item, ToScanWorkflowExecutionsResponse(FromScanWorkflowExecutionsResponse(item))) } } func TestScheduleActivityTaskDecisionAttributes(t *testing.T) { for _, item := range []*types.ScheduleActivityTaskDecisionAttributes{nil, {}, &testdata.ScheduleActivityTaskDecisionAttributes} { assert.Equal(t, item, ToScheduleActivityTaskDecisionAttributes(FromScheduleActivityTaskDecisionAttributes(item))) } } func TestSearchAttributes(t *testing.T) { for _, item := range []*types.SearchAttributes{nil, {}, &testdata.SearchAttributes} { assert.Equal(t, item, ToSearchAttributes(FromSearchAttributes(item))) } } func TestSignalExternalWorkflowExecutionDecisionAttributes(t *testing.T) { for _, item := range []*types.SignalExternalWorkflowExecutionDecisionAttributes{nil, {}, &testdata.SignalExternalWorkflowExecutionDecisionAttributes} { assert.Equal(t, item, ToSignalExternalWorkflowExecutionDecisionAttributes(FromSignalExternalWorkflowExecutionDecisionAttributes(item))) } } func TestSignalExternalWorkflowExecutionFailedEventAttributes(t *testing.T) { for _, item := range []*types.SignalExternalWorkflowExecutionFailedEventAttributes{nil, {}, &testdata.SignalExternalWorkflowExecutionFailedEventAttributes} { assert.Equal(t, item, ToSignalExternalWorkflowExecutionFailedEventAttributes(FromSignalExternalWorkflowExecutionFailedEventAttributes(item))) } } func TestSignalExternalWorkflowExecutionInitiatedEventAttributes(t *testing.T) { for _, item := range []*types.SignalExternalWorkflowExecutionInitiatedEventAttributes{nil, {}, &testdata.SignalExternalWorkflowExecutionInitiatedEventAttributes} { assert.Equal(t, item, ToSignalExternalWorkflowExecutionInitiatedEventAttributes(FromSignalExternalWorkflowExecutionInitiatedEventAttributes(item))) } } func TestSignalWithStartWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.SignalWithStartWorkflowExecutionRequest{nil, {}, &testdata.SignalWithStartWorkflowExecutionRequest} { assert.Equal(t, item, ToSignalWithStartWorkflowExecutionRequest(FromSignalWithStartWorkflowExecutionRequest(item))) } } func TestSignalWithStartWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.StartWorkflowExecutionResponse{nil, {}, &testdata.StartWorkflowExecutionResponse} { assert.Equal(t, item, ToSignalWithStartWorkflowExecutionResponse(FromSignalWithStartWorkflowExecutionResponse(item))) } } func TestSignalWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.SignalWorkflowExecutionRequest{nil, {}, &testdata.SignalWorkflowExecutionRequest} { assert.Equal(t, item, ToSignalWorkflowExecutionRequest(FromSignalWorkflowExecutionRequest(item))) } } func TestStartChildWorkflowExecutionDecisionAttributes(t *testing.T) { for _, item := range []*types.StartChildWorkflowExecutionDecisionAttributes{nil, {}, &testdata.StartChildWorkflowExecutionDecisionAttributes} { assert.Equal(t, item, ToStartChildWorkflowExecutionDecisionAttributes(FromStartChildWorkflowExecutionDecisionAttributes(item))) } } func TestStartChildWorkflowExecutionFailedEventAttributes(t *testing.T) { for _, item := range []*types.StartChildWorkflowExecutionFailedEventAttributes{nil, {}, &testdata.StartChildWorkflowExecutionFailedEventAttributes} { assert.Equal(t, item, ToStartChildWorkflowExecutionFailedEventAttributes(FromStartChildWorkflowExecutionFailedEventAttributes(item))) } } func TestStartChildWorkflowExecutionInitiatedEventAttributes(t *testing.T) { for _, item := range []*types.StartChildWorkflowExecutionInitiatedEventAttributes{nil, {}, &testdata.StartChildWorkflowExecutionInitiatedEventAttributes} { assert.Equal(t, item, ToStartChildWorkflowExecutionInitiatedEventAttributes(FromStartChildWorkflowExecutionInitiatedEventAttributes(item))) } } func TestStartTimeFilter(t *testing.T) { for _, item := range []*types.StartTimeFilter{nil, {}, &testdata.StartTimeFilter} { assert.Equal(t, item, ToStartTimeFilter(FromStartTimeFilter(item))) } } func TestStartTimerDecisionAttributes(t *testing.T) { for _, item := range []*types.StartTimerDecisionAttributes{nil, {}, &testdata.StartTimerDecisionAttributes} { assert.Equal(t, item, ToStartTimerDecisionAttributes(FromStartTimerDecisionAttributes(item))) } } func TestStartWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.StartWorkflowExecutionRequest{nil, {}, &testdata.StartWorkflowExecutionRequest} { assert.Equal(t, item, ToStartWorkflowExecutionRequest(FromStartWorkflowExecutionRequest(item))) } } func TestStartWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.StartWorkflowExecutionResponse{nil, {}, &testdata.StartWorkflowExecutionResponse} { assert.Equal(t, item, ToStartWorkflowExecutionResponse(FromStartWorkflowExecutionResponse(item))) } } func TestStartWorkflowExecutionAsyncRequest(t *testing.T) { for _, item := range []*types.StartWorkflowExecutionAsyncRequest{nil, {}, &testdata.StartWorkflowExecutionAsyncRequest} { assert.Equal(t, item, ToStartWorkflowExecutionAsyncRequest(FromStartWorkflowExecutionAsyncRequest(item))) } } func TestStartWorkflowExecutionAsyncResponse(t *testing.T) { for _, item := range []*types.StartWorkflowExecutionAsyncResponse{nil, {}, &testdata.StartWorkflowExecutionAsyncResponse} { assert.Equal(t, item, ToStartWorkflowExecutionAsyncResponse(FromStartWorkflowExecutionAsyncResponse(item))) } } func TestSignalWithStartWorkflowExecutionAsyncRequest(t *testing.T) { for _, item := range []*types.SignalWithStartWorkflowExecutionAsyncRequest{nil, {}, &testdata.SignalWithStartWorkflowExecutionAsyncRequest} { assert.Equal(t, item, ToSignalWithStartWorkflowExecutionAsyncRequest(FromSignalWithStartWorkflowExecutionAsyncRequest(item))) } } func TestSignalWithStartWorkflowExecutionAsyncResponse(t *testing.T) { for _, item := range []*types.SignalWithStartWorkflowExecutionAsyncResponse{nil, {}, &testdata.SignalWithStartWorkflowExecutionAsyncResponse} { assert.Equal(t, item, ToSignalWithStartWorkflowExecutionAsyncResponse(FromSignalWithStartWorkflowExecutionAsyncResponse(item))) } } func TestStatusFilter(t *testing.T) { for _, item := range []*types.WorkflowExecutionCloseStatus{nil, &testdata.WorkflowExecutionCloseStatus} { assert.Equal(t, item, ToStatusFilter(FromStatusFilter(item))) } } func TestStickyExecutionAttributes(t *testing.T) { for _, item := range []*types.StickyExecutionAttributes{nil, {}, &testdata.StickyExecutionAttributes} { assert.Equal(t, item, ToStickyExecutionAttributes(FromStickyExecutionAttributes(item))) } } func TestSupportedClientVersions(t *testing.T) { for _, item := range []*types.SupportedClientVersions{nil, {}, &testdata.SupportedClientVersions} { assert.Equal(t, item, ToSupportedClientVersions(FromSupportedClientVersions(item))) } } func TestTaskIDBlock(t *testing.T) { for _, item := range []*types.TaskIDBlock{nil, {}, &testdata.TaskIDBlock} { assert.Equal(t, item, ToTaskIDBlock(FromTaskIDBlock(item))) } } func TestTaskList(t *testing.T) { for _, item := range []*types.TaskList{nil, {}, &testdata.TaskList} { assert.Equal(t, item, ToTaskList(FromTaskList(item))) } } func TestTaskListMetadata(t *testing.T) { for _, item := range []*types.TaskListMetadata{nil, {}, &testdata.TaskListMetadata} { assert.Equal(t, item, ToTaskListMetadata(FromTaskListMetadata(item))) } } func TestTaskListPartitionMetadata(t *testing.T) { for _, item := range []*types.TaskListPartitionMetadata{nil, {}, &testdata.TaskListPartitionMetadata} { assert.Equal(t, item, ToTaskListPartitionMetadata(FromTaskListPartitionMetadata(item))) } } func TestTaskListStatus(t *testing.T) { for _, item := range []*types.TaskListStatus{nil, {}, &testdata.TaskListStatus} { assert.Equal(t, item, ToTaskListStatus(FromTaskListStatus(item))) } } func TestTerminateWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.TerminateWorkflowExecutionRequest{nil, {}, &testdata.TerminateWorkflowExecutionRequest} { assert.Equal(t, item, ToTerminateWorkflowExecutionRequest(FromTerminateWorkflowExecutionRequest(item))) } } func TestTimerCanceledEventAttributes(t *testing.T) { for _, item := range []*types.TimerCanceledEventAttributes{nil, {}, &testdata.TimerCanceledEventAttributes} { assert.Equal(t, item, ToTimerCanceledEventAttributes(FromTimerCanceledEventAttributes(item))) } } func TestTimerFiredEventAttributes(t *testing.T) { for _, item := range []*types.TimerFiredEventAttributes{nil, {}, &testdata.TimerFiredEventAttributes} { assert.Equal(t, item, ToTimerFiredEventAttributes(FromTimerFiredEventAttributes(item))) } } func TestTimerStartedEventAttributes(t *testing.T) { for _, item := range []*types.TimerStartedEventAttributes{nil, {}, &testdata.TimerStartedEventAttributes} { assert.Equal(t, item, ToTimerStartedEventAttributes(FromTimerStartedEventAttributes(item))) } } func TestUpdateDomainRequest(t *testing.T) { for _, item := range []*types.UpdateDomainRequest{nil, {}, &testdata.UpdateDomainRequest} { assert.Equal(t, item, ToUpdateDomainRequest(FromUpdateDomainRequest(item))) } } func TestFailoverDomainRequest(t *testing.T) { // Test round-trip conversion for standard testdata for _, item := range []*types.FailoverDomainRequest{nil, {}, &testdata.FailoverDomainRequest, &testdata.FailoverDomainRequest_OnlyActiveClusters} { assert.Equal(t, item, ToFailoverDomainRequest(FromFailoverDomainRequest(item))) } // Test specific edge cases for proto3 empty string handling t.Run("empty DomainActiveClusterName should map to nil pointer", func(t *testing.T) { input := &apiv1.FailoverDomainRequest{ DomainName: "test-domain", DomainActiveClusterName: "", ActiveClusters: nil, } expected := &types.FailoverDomainRequest{ DomainName: "test-domain", DomainActiveClusterName: nil, ActiveClusters: nil, } result := ToFailoverDomainRequest(input) assert.Equal(t, expected, result) assert.Nil(t, result.DomainActiveClusterName, "DomainActiveClusterName should be nil when proto field is empty string, not pointer to empty string") }) t.Run("non-empty DomainActiveClusterName should map to pointer", func(t *testing.T) { input := &apiv1.FailoverDomainRequest{ DomainName: "test-domain", DomainActiveClusterName: "cluster1", ActiveClusters: nil, } expected := &types.FailoverDomainRequest{ DomainName: "test-domain", DomainActiveClusterName: common.StringPtr("cluster1"), ActiveClusters: nil, } assert.Equal(t, expected, ToFailoverDomainRequest(input)) }) t.Run("with ActiveClusters and empty DomainActiveClusterName", func(t *testing.T) { input := &apiv1.FailoverDomainRequest{ DomainName: "test-domain", DomainActiveClusterName: "", ActiveClusters: &apiv1.ActiveClusters{ ActiveClustersByClusterAttribute: map[string]*apiv1.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]*apiv1.ActiveClusterInfo{ "london": { ActiveClusterName: "cluster0", FailoverVersion: 1, }, }, }, }, }, } expected := &types.FailoverDomainRequest{ DomainName: "test-domain", DomainActiveClusterName: nil, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "location": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "london": { ActiveClusterName: "cluster0", FailoverVersion: 1, }, }, }, }, }, } result := ToFailoverDomainRequest(input) assert.Equal(t, expected, result) assert.Nil(t, result.DomainActiveClusterName, "DomainActiveClusterName should be nil when proto field is empty string") }) } func TestUpdateDomainResponse(t *testing.T) { for _, item := range []*types.UpdateDomainResponse{nil, &testdata.UpdateDomainResponse} { assert.Equal(t, item, ToUpdateDomainResponse(FromUpdateDomainResponse(item))) } } func TestUpsertWorkflowSearchAttributesDecisionAttributes(t *testing.T) { for _, item := range []*types.UpsertWorkflowSearchAttributesDecisionAttributes{nil, {}, &testdata.UpsertWorkflowSearchAttributesDecisionAttributes} { assert.Equal(t, item, ToUpsertWorkflowSearchAttributesDecisionAttributes(FromUpsertWorkflowSearchAttributesDecisionAttributes(item))) } } func TestUpsertWorkflowSearchAttributesEventAttributes(t *testing.T) { for _, item := range []*types.UpsertWorkflowSearchAttributesEventAttributes{nil, {}, &testdata.UpsertWorkflowSearchAttributesEventAttributes} { assert.Equal(t, item, ToUpsertWorkflowSearchAttributesEventAttributes(FromUpsertWorkflowSearchAttributesEventAttributes(item))) } } func TestWorkerVersionInfo(t *testing.T) { for _, item := range []*types.WorkerVersionInfo{nil, {}, &testdata.WorkerVersionInfo} { assert.Equal(t, item, ToWorkerVersionInfo(FromWorkerVersionInfo(item))) } } func TestWorkflowExecution(t *testing.T) { for _, item := range []*types.WorkflowExecution{nil, {}, &testdata.WorkflowExecution} { assert.Equal(t, item, ToWorkflowExecution(FromWorkflowExecution(item))) } assert.Empty(t, ToWorkflowID(nil)) assert.Empty(t, ToRunID(nil)) } func TestExternalExecutionInfo(t *testing.T) { assert.Nil(t, FromExternalExecutionInfoFields(nil, nil)) assert.Nil(t, ToExternalWorkflowExecution(nil)) assert.Nil(t, ToExternalInitiatedID(nil)) info := FromExternalExecutionInfoFields(nil, common.Int64Ptr(testdata.EventID1)) assert.Nil(t, ToExternalWorkflowExecution(nil)) assert.Equal(t, testdata.EventID1, *ToExternalInitiatedID(info)) info = FromExternalExecutionInfoFields(&testdata.WorkflowExecution, nil) assert.Equal(t, testdata.WorkflowExecution, *ToExternalWorkflowExecution(info)) assert.Equal(t, int64(0), *ToExternalInitiatedID(info)) info = FromExternalExecutionInfoFields(&testdata.WorkflowExecution, common.Int64Ptr(testdata.EventID1)) assert.Equal(t, testdata.WorkflowExecution, *ToExternalWorkflowExecution(info)) assert.Equal(t, testdata.EventID1, *ToExternalInitiatedID(info)) } func TestWorkflowExecutionCancelRequestedEventAttributes(t *testing.T) { for _, item := range []*types.WorkflowExecutionCancelRequestedEventAttributes{nil, {}, &testdata.WorkflowExecutionCancelRequestedEventAttributes} { assert.Equal(t, item, ToWorkflowExecutionCancelRequestedEventAttributes(FromWorkflowExecutionCancelRequestedEventAttributes(item))) } } func TestWorkflowExecutionCanceledEventAttributes(t *testing.T) { for _, item := range []*types.WorkflowExecutionCanceledEventAttributes{nil, {}, &testdata.WorkflowExecutionCanceledEventAttributes} { assert.Equal(t, item, ToWorkflowExecutionCanceledEventAttributes(FromWorkflowExecutionCanceledEventAttributes(item))) } } func TestWorkflowExecutionCompletedEventAttributes(t *testing.T) { for _, item := range []*types.WorkflowExecutionCompletedEventAttributes{nil, {}, &testdata.WorkflowExecutionCompletedEventAttributes} { assert.Equal(t, item, ToWorkflowExecutionCompletedEventAttributes(FromWorkflowExecutionCompletedEventAttributes(item))) } } func TestWorkflowExecutionConfiguration(t *testing.T) { for _, item := range []*types.WorkflowExecutionConfiguration{nil, {}, &testdata.WorkflowExecutionConfiguration} { assert.Equal(t, item, ToWorkflowExecutionConfiguration(FromWorkflowExecutionConfiguration(item))) } } func TestWorkflowExecutionContinuedAsNewEventAttributes(t *testing.T) { for _, item := range []*types.WorkflowExecutionContinuedAsNewEventAttributes{nil, {}, &testdata.WorkflowExecutionContinuedAsNewEventAttributes} { assert.Equal(t, item, ToWorkflowExecutionContinuedAsNewEventAttributes(FromWorkflowExecutionContinuedAsNewEventAttributes(item))) } } func TestWorkflowExecutionFailedEventAttributes(t *testing.T) { for _, item := range []*types.WorkflowExecutionFailedEventAttributes{nil, {}, &testdata.WorkflowExecutionFailedEventAttributes} { assert.Equal(t, item, ToWorkflowExecutionFailedEventAttributes(FromWorkflowExecutionFailedEventAttributes(item))) } } func TestWorkflowExecutionFilter(t *testing.T) { for _, item := range []*types.WorkflowExecutionFilter{nil, {}, &testdata.WorkflowExecutionFilter} { assert.Equal(t, item, ToWorkflowExecutionFilter(FromWorkflowExecutionFilter(item))) } } func TestParentExecutionInfo(t *testing.T) { for _, item := range []*types.ParentExecutionInfo{nil, {}, &testdata.ParentExecutionInfo} { assert.Equal(t, item, ToParentExecutionInfo(FromParentExecutionInfo(item))) } } func TestParentExecutionInfoFields(t *testing.T) { assert.Nil(t, FromParentExecutionInfoFields(nil, nil, nil, nil)) info := FromParentExecutionInfoFields(nil, nil, testdata.ParentExecutionInfo.Execution, nil) assert.Equal(t, "", *ToParentDomainID(info)) assert.Equal(t, "", *ToParentDomainName(info)) assert.Equal(t, testdata.ParentExecutionInfo.Execution, ToParentWorkflowExecution(info)) assert.Equal(t, int64(0), *ToParentInitiatedID(info)) info = FromParentExecutionInfoFields( &testdata.ParentExecutionInfo.DomainUUID, &testdata.ParentExecutionInfo.Domain, testdata.ParentExecutionInfo.Execution, &testdata.ParentExecutionInfo.InitiatedID) assert.Equal(t, testdata.ParentExecutionInfo.DomainUUID, *ToParentDomainID(info)) assert.Equal(t, testdata.ParentExecutionInfo.Domain, *ToParentDomainName(info)) assert.Equal(t, testdata.ParentExecutionInfo.Execution, ToParentWorkflowExecution(info)) assert.Equal(t, testdata.ParentExecutionInfo.InitiatedID, *ToParentInitiatedID(info)) } func TestWorkflowExecutionInfo(t *testing.T) { for _, item := range []*types.WorkflowExecutionInfo{nil, {}, &testdata.WorkflowExecutionInfo, &testdata.CronWorkflowExecutionInfo, &testdata.WorkflowExecutionInfoEphemeral} { assert.Equal(t, item, ToWorkflowExecutionInfo(FromWorkflowExecutionInfo(item))) } } func TestWorkflowExecutionInfo_MigrateTaskList(t *testing.T) { tlName := "foo" otherName := "bar" cases := []struct { name string in *apiv1.WorkflowExecutionInfo out *types.WorkflowExecutionInfo }{ { name: "nil", in: &apiv1.WorkflowExecutionInfo{}, out: &types.WorkflowExecutionInfo{ TaskList: nil, }, }, { name: "name only", in: &apiv1.WorkflowExecutionInfo{ TaskList: tlName, }, out: &types.WorkflowExecutionInfo{ TaskList: &types.TaskList{Name: tlName, Kind: types.TaskListKindNormal.Ptr()}, }, }, { name: "tl only", in: &apiv1.WorkflowExecutionInfo{ TaskListInfo: &apiv1.TaskList{Name: tlName, Kind: apiv1.TaskListKind_TASK_LIST_KIND_NORMAL}, }, out: &types.WorkflowExecutionInfo{ TaskList: &types.TaskList{Name: tlName, Kind: types.TaskListKindNormal.Ptr()}, }, }, { name: "both", in: &apiv1.WorkflowExecutionInfo{ TaskList: otherName, TaskListInfo: &apiv1.TaskList{Name: tlName, Kind: apiv1.TaskListKind_TASK_LIST_KIND_NORMAL}, }, out: &types.WorkflowExecutionInfo{ TaskList: &types.TaskList{Name: tlName, Kind: types.TaskListKindNormal.Ptr()}, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.out, ToWorkflowExecutionInfo(tc.in)) }) } } func TestWorkflowExecutionSignaledEventAttributes(t *testing.T) { for _, item := range []*types.WorkflowExecutionSignaledEventAttributes{nil, {}, &testdata.WorkflowExecutionSignaledEventAttributes} { assert.Equal(t, item, ToWorkflowExecutionSignaledEventAttributes(FromWorkflowExecutionSignaledEventAttributes(item))) } } func TestWorkflowExecutionStartedEventAttributes(t *testing.T) { for _, item := range []*types.WorkflowExecutionStartedEventAttributes{nil, {}, &testdata.WorkflowExecutionStartedEventAttributes} { assert.Equal(t, item, ToWorkflowExecutionStartedEventAttributes(FromWorkflowExecutionStartedEventAttributes(item))) } } func TestWorkflowExecutionTerminatedEventAttributes(t *testing.T) { for _, item := range []*types.WorkflowExecutionTerminatedEventAttributes{nil, {}, &testdata.WorkflowExecutionTerminatedEventAttributes} { assert.Equal(t, item, ToWorkflowExecutionTerminatedEventAttributes(FromWorkflowExecutionTerminatedEventAttributes(item))) } } func TestWorkflowExecutionTimedOutEventAttributes(t *testing.T) { for _, item := range []*types.WorkflowExecutionTimedOutEventAttributes{nil, {}, &testdata.WorkflowExecutionTimedOutEventAttributes} { assert.Equal(t, item, ToWorkflowExecutionTimedOutEventAttributes(FromWorkflowExecutionTimedOutEventAttributes(item))) } } func TestWorkflowQuery(t *testing.T) { for _, item := range []*types.WorkflowQuery{nil, {}, &testdata.WorkflowQuery} { assert.Equal(t, item, ToWorkflowQuery(FromWorkflowQuery(item))) } } func TestWorkflowQueryResult(t *testing.T) { for _, item := range []*types.WorkflowQueryResult{nil, {}, &testdata.WorkflowQueryResult} { assert.Equal(t, item, ToWorkflowQueryResult(FromWorkflowQueryResult(item))) } } func TestWorkflowType(t *testing.T) { for _, item := range []*types.WorkflowType{nil, {}, &testdata.WorkflowType} { assert.Equal(t, item, ToWorkflowType(FromWorkflowType(item))) } } func TestWorkflowTypeFilter(t *testing.T) { for _, item := range []*types.WorkflowTypeFilter{nil, {}, &testdata.WorkflowTypeFilter} { assert.Equal(t, item, ToWorkflowTypeFilter(FromWorkflowTypeFilter(item))) } } func TestDataBlobArray(t *testing.T) { for _, item := range [][]*types.DataBlob{nil, {}, testdata.DataBlobArray} { assert.Equal(t, item, ToDataBlobArray(FromDataBlobArray(item))) } } func TestHistoryEventArray(t *testing.T) { for _, item := range [][]*types.HistoryEvent{{}, testdata.HistoryEventArray} { assert.Equal(t, item, ToHistoryEventArray(FromHistoryEventArray(item))) } } func TestTaskListPartitionMetadataArray(t *testing.T) { for _, item := range [][]*types.TaskListPartitionMetadata{nil, {}, testdata.TaskListPartitionMetadataArray} { assert.Equal(t, item, ToTaskListPartitionMetadataArray(FromTaskListPartitionMetadataArray(item))) } } func TestDecisionArray(t *testing.T) { for _, item := range [][]*types.Decision{nil, {}, testdata.DecisionArray} { assert.Equal(t, item, ToDecisionArray(FromDecisionArray(item))) } } func TestPollerInfoArray(t *testing.T) { for _, item := range [][]*types.PollerInfo{nil, {}, testdata.PollerInfoArray} { assert.Equal(t, item, ToPollerInfoArray(FromPollerInfoArray(item))) } } func TestPendingChildExecutionInfoArray(t *testing.T) { for _, item := range [][]*types.PendingChildExecutionInfo{nil, {}, testdata.PendingChildExecutionInfoArray} { assert.Equal(t, item, ToPendingChildExecutionInfoArray(FromPendingChildExecutionInfoArray(item))) } } func TestWorkflowExecutionInfoArray(t *testing.T) { for _, item := range [][]*types.WorkflowExecutionInfo{nil, {}, testdata.WorkflowExecutionInfoArray} { assert.Equal(t, item, ToWorkflowExecutionInfoArray(FromWorkflowExecutionInfoArray(item))) } } func TestDescribeDomainResponseArray(t *testing.T) { for _, item := range [][]*types.DescribeDomainResponse{nil, {}, testdata.DescribeDomainResponseArray} { assert.Equal(t, item, ToDescribeDomainResponseArray(FromDescribeDomainResponseArray(item))) } } func TestResetPointInfoArray(t *testing.T) { for _, item := range [][]*types.ResetPointInfo{nil, {}, testdata.ResetPointInfoArray} { assert.Equal(t, item, ToResetPointInfoArray(FromResetPointInfoArray(item))) } } func TestPendingActivityInfoArray(t *testing.T) { for _, item := range [][]*types.PendingActivityInfo{nil, {}, testdata.PendingActivityInfoArray} { assert.Equal(t, item, ToPendingActivityInfoArray(FromPendingActivityInfoArray(item))) } } func TestClusterReplicationConfigurationArray(t *testing.T) { for _, item := range [][]*types.ClusterReplicationConfiguration{nil, {}, testdata.ClusterReplicationConfigurationArray} { assert.Equal(t, item, ToClusterReplicationConfigurationArray(FromClusterReplicationConfigurationArray(item))) } } func TestActivityLocalDispatchInfoMap(t *testing.T) { for _, item := range []map[string]*types.ActivityLocalDispatchInfo{nil, {}, testdata.ActivityLocalDispatchInfoMap} { assert.Equal(t, item, ToActivityLocalDispatchInfoMap(FromActivityLocalDispatchInfoMap(item))) } } func TestBadBinaryInfoMap(t *testing.T) { for _, item := range []map[string]*types.BadBinaryInfo{nil, {}, testdata.BadBinaryInfoMap} { assert.Equal(t, item, ToBadBinaryInfoMap(FromBadBinaryInfoMap(item))) } } func TestIndexedValueTypeMap(t *testing.T) { for _, item := range []map[string]types.IndexedValueType{nil, {}, testdata.IndexedValueTypeMap} { assert.Equal(t, item, ToIndexedValueTypeMap(FromIndexedValueTypeMap(item))) } } func TestWorkflowQueryMap(t *testing.T) { for _, item := range []map[string]*types.WorkflowQuery{nil, {}, testdata.WorkflowQueryMap} { assert.Equal(t, item, ToWorkflowQueryMap(FromWorkflowQueryMap(item))) } } func TestWorkflowQueryResultMap(t *testing.T) { for _, item := range []map[string]*types.WorkflowQueryResult{nil, {}, testdata.WorkflowQueryResultMap} { assert.Equal(t, item, ToWorkflowQueryResultMap(FromWorkflowQueryResultMap(item))) } } func TestPayload(t *testing.T) { for _, item := range [][]byte{nil, {}, testdata.Payload1} { assert.Equal(t, item, ToPayload(FromPayload(item))) } assert.Equal(t, []byte{}, ToPayload(&apiv1.Payload{ Data: nil, })) } func TestPayloadMap(t *testing.T) { for _, item := range []map[string][]byte{nil, {}, testdata.PayloadMap} { assert.Equal(t, item, ToPayloadMap(FromPayloadMap(item))) } } func TestFailure(t *testing.T) { assert.Nil(t, FromFailure(nil, nil)) assert.Nil(t, ToFailureReason(nil)) assert.Nil(t, ToFailureDetails(nil)) failure := FromFailure(&testdata.FailureReason, testdata.FailureDetails) assert.Equal(t, testdata.FailureReason, *ToFailureReason(failure)) assert.Equal(t, testdata.FailureDetails, ToFailureDetails(failure)) } func TestHistoryEvent(t *testing.T) { for _, item := range []*types.HistoryEvent{ nil, &testdata.HistoryEvent_WorkflowExecutionStarted, &testdata.HistoryEvent_WorkflowExecutionCompleted, &testdata.HistoryEvent_WorkflowExecutionFailed, &testdata.HistoryEvent_WorkflowExecutionTimedOut, &testdata.HistoryEvent_DecisionTaskScheduled, &testdata.HistoryEvent_DecisionTaskStarted, &testdata.HistoryEvent_DecisionTaskCompleted, &testdata.HistoryEvent_DecisionTaskTimedOut, &testdata.HistoryEvent_DecisionTaskFailed, &testdata.HistoryEvent_ActivityTaskScheduled, &testdata.HistoryEvent_ActivityTaskStarted, &testdata.HistoryEvent_ActivityTaskCompleted, &testdata.HistoryEvent_ActivityTaskFailed, &testdata.HistoryEvent_ActivityTaskTimedOut, &testdata.HistoryEvent_ActivityTaskCancelRequested, &testdata.HistoryEvent_RequestCancelActivityTaskFailed, &testdata.HistoryEvent_ActivityTaskCanceled, &testdata.HistoryEvent_TimerStarted, &testdata.HistoryEvent_TimerFired, &testdata.HistoryEvent_CancelTimerFailed, &testdata.HistoryEvent_TimerCanceled, &testdata.HistoryEvent_WorkflowExecutionCancelRequested, &testdata.HistoryEvent_WorkflowExecutionCanceled, &testdata.HistoryEvent_RequestCancelExternalWorkflowExecutionInitiated, &testdata.HistoryEvent_RequestCancelExternalWorkflowExecutionFailed, &testdata.HistoryEvent_ExternalWorkflowExecutionCancelRequested, &testdata.HistoryEvent_MarkerRecorded, &testdata.HistoryEvent_WorkflowExecutionSignaled, &testdata.HistoryEvent_WorkflowExecutionTerminated, &testdata.HistoryEvent_WorkflowExecutionContinuedAsNew, &testdata.HistoryEvent_StartChildWorkflowExecutionInitiated, &testdata.HistoryEvent_StartChildWorkflowExecutionFailed, &testdata.HistoryEvent_ChildWorkflowExecutionStarted, &testdata.HistoryEvent_ChildWorkflowExecutionCompleted, &testdata.HistoryEvent_ChildWorkflowExecutionFailed, &testdata.HistoryEvent_ChildWorkflowExecutionCanceled, &testdata.HistoryEvent_ChildWorkflowExecutionTimedOut, &testdata.HistoryEvent_ChildWorkflowExecutionTerminated, &testdata.HistoryEvent_SignalExternalWorkflowExecutionInitiated, &testdata.HistoryEvent_SignalExternalWorkflowExecutionFailed, &testdata.HistoryEvent_ExternalWorkflowExecutionSignaled, &testdata.HistoryEvent_UpsertWorkflowSearchAttributes, } { assert.Equal(t, item, ToHistoryEvent(FromHistoryEvent(item))) } assert.Panics(t, func() { FromHistoryEvent(&types.HistoryEvent{}) }) } func TestDecision(t *testing.T) { for _, item := range []*types.Decision{ nil, &testdata.Decision_CancelTimer, &testdata.Decision_CancelWorkflowExecution, &testdata.Decision_CompleteWorkflowExecution, &testdata.Decision_ContinueAsNewWorkflowExecution, &testdata.Decision_FailWorkflowExecution, &testdata.Decision_RecordMarker, &testdata.Decision_RequestCancelActivityTask, &testdata.Decision_RequestCancelExternalWorkflowExecution, &testdata.Decision_ScheduleActivityTask, &testdata.Decision_SignalExternalWorkflowExecution, &testdata.Decision_StartChildWorkflowExecution, &testdata.Decision_StartTimer, &testdata.Decision_UpsertWorkflowSearchAttributes, } { assert.Equal(t, item, ToDecision(FromDecision(item))) } assert.Panics(t, func() { FromDecision(&types.Decision{}) }) } func TestListClosedWorkflowExecutionsRequest(t *testing.T) { for _, item := range []*types.ListClosedWorkflowExecutionsRequest{ nil, {}, &testdata.ListClosedWorkflowExecutionsRequest_ExecutionFilter, &testdata.ListClosedWorkflowExecutionsRequest_StatusFilter, &testdata.ListClosedWorkflowExecutionsRequest_TypeFilter, } { assert.Equal(t, item, ToListClosedWorkflowExecutionsRequest(FromListClosedWorkflowExecutionsRequest(item))) } } func TestListOpenWorkflowExecutionsRequest(t *testing.T) { for _, item := range []*types.ListOpenWorkflowExecutionsRequest{ nil, {}, &testdata.ListOpenWorkflowExecutionsRequest_ExecutionFilter, &testdata.ListOpenWorkflowExecutionsRequest_TypeFilter, } { assert.Equal(t, item, ToListOpenWorkflowExecutionsRequest(FromListOpenWorkflowExecutionsRequest(item))) } } func TestGetTaskListsByDomainResponse(t *testing.T) { for _, item := range []*types.GetTaskListsByDomainResponse{nil, {}, &testdata.GetTaskListsByDomainResponse} { assert.Equal(t, item, ToMatchingGetTaskListsByDomainResponse(FromMatchingGetTaskListsByDomainResponse(item))) } } func TestFailoverInfo(t *testing.T) { for _, item := range []*types.FailoverInfo{ nil, {}, &testdata.FailoverInfo, } { assert.Equal(t, item, ToFailoverInfo(FromFailoverInfo(item))) } } func TestDescribeTaskListResponseMap(t *testing.T) { for _, item := range []map[string]*types.DescribeTaskListResponse{nil, {}, testdata.DescribeTaskListResponseMap} { assert.Equal(t, item, ToDescribeTaskListResponseMap(FromDescribeTaskListResponseMap(item))) } } func TestAPITaskListPartitionConfig(t *testing.T) { for _, item := range []*types.TaskListPartitionConfig{nil, {}, &testdata.TaskListPartitionConfig} { assert.Equal(t, item, ToAPITaskListPartitionConfig(FromAPITaskListPartitionConfig(item))) } } func TestToAPITaskListPartitionConfig(t *testing.T) { cases := []struct { name string config *apiv1.TaskListPartitionConfig expected *types.TaskListPartitionConfig }{ { name: "happy path", config: &apiv1.TaskListPartitionConfig{ Version: 1, NumReadPartitions: 2, NumWritePartitions: 2, ReadPartitions: map[int32]*apiv1.TaskListPartition{ 0: {IsolationGroups: []string{"foo"}}, 1: {IsolationGroups: []string{"bar"}}, }, WritePartitions: map[int32]*apiv1.TaskListPartition{ 0: {IsolationGroups: []string{"baz"}}, 1: {IsolationGroups: []string{"bar"}}, }, }, expected: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"foo"}}, 1: {IsolationGroups: []string{"bar"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"baz"}}, 1: {IsolationGroups: []string{"bar"}}, }, }, }, { name: "numbers only", config: &apiv1.TaskListPartitionConfig{ Version: 1, NumReadPartitions: 2, NumWritePartitions: 2, }, expected: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, }, }, { name: "number mismatch", config: &apiv1.TaskListPartitionConfig{ Version: 1, NumReadPartitions: 2, NumWritePartitions: 1, ReadPartitions: map[int32]*apiv1.TaskListPartition{ 0: {IsolationGroups: []string{"foo"}}, 1: {IsolationGroups: []string{"bar"}}, }, WritePartitions: map[int32]*apiv1.TaskListPartition{ 0: {IsolationGroups: []string{"baz"}}, 1: {IsolationGroups: []string{"bar"}}, }, }, expected: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"foo"}}, 1: {IsolationGroups: []string{"bar"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { actual := ToAPITaskListPartitionConfig(tc.config) assert.Equal(t, tc.expected, actual) }) } } func TestActiveClustersConversion(t *testing.T) { testCases := []*types.ActiveClusters{ nil, {}, { AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster1", FailoverVersion: 1, }, "us-east-1": { ActiveClusterName: "cluster2", FailoverVersion: 2, }, }, }, }, }, { AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster1", FailoverVersion: 1, }, "us-east-1": { ActiveClusterName: "cluster2", FailoverVersion: 2, }, }, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: "cluster1", FailoverVersion: 10, }, }, }, }, }, { AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster1", FailoverVersion: 1, }, }, }, }, }, } for _, original := range testCases { protoObj := FromActiveClusters(original) roundTripObj := ToActiveClusters(protoObj) assert.Equal(t, original, roundTripObj) } } func TestClusterAttribute(t *testing.T) { for _, item := range []*types.ClusterAttribute{nil, {}, &testdata.ClusterAttribute} { assert.Equal(t, item, ToClusterAttribute(FromClusterAttribute(item))) } } func TestActiveClusterSelectionPolicy(t *testing.T) { for _, item := range []*types.ActiveClusterSelectionPolicy{ nil, {}, &testdata.ActiveClusterSelectionPolicyWithClusterAttribute, &testdata.ActiveClusterSelectionPolicyRegionSticky, &testdata.ActiveClusterSelectionPolicyExternalEntity, } { assert.Equal(t, item, ToActiveClusterSelectionPolicy(FromActiveClusterSelectionPolicy(item))) } } func TestClusterAttributeScopeConversion(t *testing.T) { testCases := []*types.ClusterAttributeScope{ nil, {}, { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster1", FailoverVersion: 1, }, "us-east-1": { ActiveClusterName: "cluster2", FailoverVersion: 2, }, }, }, } for _, original := range testCases { protoObj := FromClusterAttributeScope(original) roundTripObj := ToClusterAttributeScope(protoObj) assert.Equal(t, original, roundTripObj) } } func TestPaginationOptions(t *testing.T) { for _, item := range []*types.PaginationOptions{nil, {}, &testdata.PaginationOptions} { assert.Equal(t, item, ToPaginationOptions(FromPaginationOptions(item))) } } func TestListFailoverHistoryRequestFilters(t *testing.T) { for _, item := range []*types.ListFailoverHistoryRequestFilters{nil, {}, &testdata.ListFailoverHistoryRequestFilters} { assert.Equal(t, item, ToListFailoverHistoryRequestFilters(FromListFailoverHistoryRequestFilters(item))) } } func TestListFailoverHistoryRequest(t *testing.T) { for _, item := range []*types.ListFailoverHistoryRequest{nil, {}, &testdata.ListFailoverHistoryRequest} { assert.Equal(t, item, ToListFailoverHistoryRequest(FromListFailoverHistoryRequest(item))) } } func TestListFailoverHistoryResponse(t *testing.T) { for _, item := range []*types.ListFailoverHistoryResponse{nil, {}, &testdata.ListFailoverHistoryResponse} { assert.Equal(t, item, ToListFailoverHistoryResponse(FromListFailoverHistoryResponse(item))) } } func TestFailoverEvent(t *testing.T) { for _, item := range []*types.FailoverEvent{nil, {}, &testdata.FailoverEvent} { assert.Equal(t, item, ToFailoverEvent(FromFailoverEvent(item))) } } func TestFailoverEventArray(t *testing.T) { testCases := [][]*types.FailoverEvent{ nil, {}, {nil}, {&testdata.FailoverEvent}, {&testdata.FailoverEvent, nil, &testdata.FailoverEvent}, } for _, item := range testCases { assert.Equal(t, item, ToFailoverEventArray(FromFailoverEventArray(item))) } } func TestListFailoverHistoryResponseMapping(t *testing.T) { fuzzer := testdatagen.New(t, func(v *types.FailoverEvent, c fuzz.Continue) { c.Fuzz(v) // Don't allow empty strings for ID - use nil or a non-empty string if v.ID != nil && *v.ID == "" { v.ID = nil } }) for i := 0; i < 100; i++ { var response types.ListFailoverHistoryResponse fuzzer.Fuzz(&response) protoResponse := FromListFailoverHistoryResponse(&response) assert.Equal(t, &response, ToListFailoverHistoryResponse(protoResponse)) } } func TestClusterFailover(t *testing.T) { for _, item := range []*types.ClusterFailover{nil, {}, &testdata.ClusterFailover} { assert.Equal(t, item, ToClusterFailover(FromClusterFailover(item))) } } func TestClusterFailoverArray(t *testing.T) { testCases := [][]*types.ClusterFailover{ nil, {}, {nil}, {&testdata.ClusterFailover}, {&testdata.ClusterFailover, nil, &testdata.ClusterFailover}, } for _, item := range testCases { assert.Equal(t, item, ToClusterFailoverArray(FromClusterFailoverArray(item))) } } func TestActiveClusterInfo(t *testing.T) { for _, item := range []*types.ActiveClusterInfo{nil, {}, &testdata.ActiveClusterInfo1, &testdata.ActiveClusterInfo2} { assert.Equal(t, item, ToActiveClusterInfo(FromActiveClusterInfo(item))) } } func TestFailoverType(t *testing.T) { testCases := []struct { name string input *types.FailoverType expected apiv1.FailoverType }{ { name: "nil", input: nil, expected: apiv1.FailoverType_FAILOVER_TYPE_INVALID, }, { name: "force", input: types.FailoverTypeForce.Ptr(), expected: apiv1.FailoverType_FAILOVER_TYPE_FORCE, }, { name: "graceful", input: types.FailoverTypeGraceful.Ptr(), expected: apiv1.FailoverType_FAILOVER_TYPE_GRACEFUL, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := FromFailoverType(tc.input) assert.Equal(t, tc.expected, result) // Test round-trip if tc.input != nil { roundTrip := ToFailoverType(result) assert.Equal(t, tc.input, roundTrip) } }) } // Test ToFailoverType for all enum values assert.Equal(t, types.FailoverTypeForce.Ptr(), ToFailoverType(apiv1.FailoverType_FAILOVER_TYPE_FORCE)) assert.Equal(t, types.FailoverTypeGraceful.Ptr(), ToFailoverType(apiv1.FailoverType_FAILOVER_TYPE_GRACEFUL)) assert.Nil(t, ToFailoverType(apiv1.FailoverType_FAILOVER_TYPE_INVALID)) assert.Nil(t, ToFailoverType(apiv1.FailoverType(999))) // Unknown value } func QueryConsistencyLevelFuzzer(e *types.QueryConsistencyLevel, c fuzz.Continue) { *e = types.QueryConsistencyLevel(c.Intn(2)) // 0-1: Eventual, Strong } func SignalExternalWorkflowExecutionFailedCauseFuzzer(e *types.SignalExternalWorkflowExecutionFailedCause, c fuzz.Continue) { *e = types.SignalExternalWorkflowExecutionFailedCause(c.Intn(2)) // 0-1: UnknownExternalWorkflowExecution, WorkflowAlreadyCompleted } func WorkflowIDReusePolicyFuzzer(e *types.WorkflowIDReusePolicy, c fuzz.Continue) { *e = types.WorkflowIDReusePolicy(c.Intn(4)) // 0-3: AllowDuplicateFailedOnly, AllowDuplicate, RejectDuplicate, TerminateIfRunning } func ActiveClusterSelectionStrategyFuzzer(e *types.ActiveClusterSelectionStrategy, c fuzz.Continue) { *e = types.ActiveClusterSelectionStrategy(c.Intn(2)) // 0-1: RegionSticky, ExternalEntity } func CronOverlapPolicyFuzzer(e *types.CronOverlapPolicy, c fuzz.Continue) { *e = types.CronOverlapPolicy(c.Intn(2)) // 0-1: Skipped, BufferOne } func DecisionTypeFuzzer(e *types.DecisionType, c fuzz.Continue) { *e = types.DecisionType(c.Intn(13)) } func EventTypeFuzzer(e *types.EventType, c fuzz.Continue) { *e = types.EventType(c.Intn(42)) } func TimeoutTypeFuzzer(e *types.TimeoutType, c fuzz.Continue) { *e = types.TimeoutType(c.Intn(4)) // 0-3: StartToClose, ScheduleToStart, ScheduleToClose, Heartbeat } func TaskListKindFuzzer(e *types.TaskListKind, c fuzz.Continue) { *e = types.TaskListKind(c.Intn(3)) // 0-2: Normal, Sticky, Ephemeral } func FailoverTypeFuzzer(e *types.FailoverType, c fuzz.Continue) { *e = types.FailoverType(c.Intn(2) + 1) // 1-2: Force, Graceful (skip 0=Invalid which maps to nil) } func ArchivalStatusFuzzer(e *types.ArchivalStatus, c fuzz.Continue) { *e = types.ArchivalStatus(c.Intn(2)) // 0-1: Disabled, Enabled } func ContinueAsNewInitiatorFuzzer(e *types.ContinueAsNewInitiator, c fuzz.Continue) { *e = types.ContinueAsNewInitiator(c.Intn(3)) // 0-2: Decider, RetryPolicy, CronSchedule } func CancelExternalWorkflowExecutionFailedCauseFuzzer(e *types.CancelExternalWorkflowExecutionFailedCause, c fuzz.Continue) { *e = types.CancelExternalWorkflowExecutionFailedCause(c.Intn(2)) // 0-1: UnknownExternalWorkflowExecution, WorkflowAlreadyCompleted } func ChildWorkflowExecutionFailedCauseFuzzer(e *types.ChildWorkflowExecutionFailedCause, c fuzz.Continue) { *e = types.ChildWorkflowExecutionFailedCause(c.Intn(2)) // 0-1: WorkflowAlreadyRunning, DeprecatedDomain } func WorkflowExecutionCloseStatusFuzzer(e *types.WorkflowExecutionCloseStatus, c fuzz.Continue) { *e = types.WorkflowExecutionCloseStatus(c.Intn(6)) // 0-5: Completed, Failed, Canceled, Terminated, ContinuedAsNew, TimedOut } func ParentClosePolicyFuzzer(e *types.ParentClosePolicy, c fuzz.Continue) { *e = types.ParentClosePolicy(c.Intn(3)) // 0-2: Abandon, RequestCancel, Terminate } func DecisionTaskTimedOutCauseFuzzer(e *types.DecisionTaskTimedOutCause, c fuzz.Continue) { *e = types.DecisionTaskTimedOutCause(c.Intn(2)) // 0-1: Timeout, Reset } func DecisionTaskFailedCauseFuzzer(e *types.DecisionTaskFailedCause, c fuzz.Continue) { *e = types.DecisionTaskFailedCause(c.Intn(23)) // 0-22: All DecisionTaskFailedCause values } func QueryRejectConditionFuzzer(e *types.QueryRejectCondition, c fuzz.Continue) { *e = types.QueryRejectCondition(c.Intn(2)) // 0-1: NotOpen, NotCompletedCleanly } func PendingActivityStateFuzzer(e *types.PendingActivityState, c fuzz.Continue) { *e = types.PendingActivityState(c.Intn(3)) // 0-2: Scheduled, Started, CancelRequested } func HistoryEventFilterTypeFuzzer(e *types.HistoryEventFilterType, c fuzz.Continue) { *e = types.HistoryEventFilterType(c.Intn(2)) // 0-1: AllEvent, CloseEvent } func IndexedValueTypeFuzzer(e *types.IndexedValueType, c fuzz.Continue) { *e = types.IndexedValueType(c.Intn(6)) // 0-5: String, Keyword, Int, Double, Bool, Datetime } func CompletedTypeFuzzer(e *types.QueryTaskCompletedType, c fuzz.Continue) { *e = types.QueryTaskCompletedType(c.Intn(2)) // 0-1: Completed, Failed } func ActiveClusterSelectionPolicyFuzzerClearAttribute(p *types.ActiveClusterSelectionPolicy, c fuzz.Continue) { // ActiveClusterSelectionPolicy requires string fields to match strategy // When strategy is nil, all string fields must be empty (mapper uses ClusterAttribute) // When strategy is set, only the relevant string fields should be set c.Fuzz(&p.ActiveClusterSelectionStrategy) if p.ActiveClusterSelectionStrategy == nil { p.StickyRegion = "" p.ExternalEntityType = "" p.ExternalEntityKey = "" } else { switch *p.ActiveClusterSelectionStrategy { case types.ActiveClusterSelectionStrategyRegionSticky: c.Fuzz(&p.StickyRegion) p.ExternalEntityType = "" p.ExternalEntityKey = "" case types.ActiveClusterSelectionStrategyExternalEntity: c.Fuzz(&p.ExternalEntityType) c.Fuzz(&p.ExternalEntityKey) p.StickyRegion = "" } } // ClusterAttribute is always cleared (mapper uses strategy+strings) p.ClusterAttribute = nil } func ActiveClusterSelectionPolicyFuzzerWithAttribute(p *types.ActiveClusterSelectionPolicy, c fuzz.Continue) { // ActiveClusterSelectionPolicy requires string fields to match strategy // When strategy is nil, all string fields must be empty (mapper uses ClusterAttribute) // When strategy is set, only the relevant string fields should be set c.Fuzz(&p.ActiveClusterSelectionStrategy) if p.ActiveClusterSelectionStrategy == nil { p.StickyRegion = "" p.ExternalEntityType = "" p.ExternalEntityKey = "" } else { switch *p.ActiveClusterSelectionStrategy { case types.ActiveClusterSelectionStrategyRegionSticky: c.Fuzz(&p.StickyRegion) p.ExternalEntityType = "" p.ExternalEntityKey = "" case types.ActiveClusterSelectionStrategyExternalEntity: p.StickyRegion = "" c.Fuzz(&p.ExternalEntityType) c.Fuzz(&p.ExternalEntityKey) } } c.Fuzz(&p.ClusterAttribute) } func ActiveClusterSelectionPolicyFuzzerNoCustom(p *types.ActiveClusterSelectionPolicy, c fuzz.Continue) { // Fuzz all fields first without custom fuzzers c.FuzzNoCustom(p) // Then clear mutually exclusive fields based on strategy if p.ActiveClusterSelectionStrategy != nil { switch *p.ActiveClusterSelectionStrategy { case types.ActiveClusterSelectionStrategyRegionSticky: p.ExternalEntityType = "" p.ExternalEntityKey = "" case types.ActiveClusterSelectionStrategyExternalEntity: p.StickyRegion = "" } } else { p.StickyRegion = "" p.ExternalEntityType = "" p.ExternalEntityKey = "" } } func TestDataBlobArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDataBlobArray, ToDataBlobArray, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestPendingActivityInfoArrayFuzz(t *testing.T) { // LastFailureReason and LastFailureDetails have asymmetric mapping: // FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped // Excluding both fields from comparison to handle this asymmetry testutils.RunMapperFuzzTest(t, FromPendingActivityInfoArray, ToPendingActivityInfoArray, testutils.WithExcludedFields("LastFailureReason", "LastFailureDetails"), ) } func TestDeleteDomainRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDeleteDomainRequest, ToDeleteDomainRequest) } func TestDescribeWorkflowExecutionRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDescribeWorkflowExecutionRequest, ToDescribeWorkflowExecutionRequest, testutils.WithCustomFuncs(QueryConsistencyLevelFuzzer), ) } func TestRequestCancelWorkflowExecutionRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRequestCancelWorkflowExecutionRequest, ToRequestCancelWorkflowExecutionRequest) } func TestRespondActivityTaskFailedByIDRequestFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped testutils.RunMapperFuzzTest(t, FromRespondActivityTaskFailedByIDRequest, ToRespondActivityTaskFailedByIDRequest, testutils.WithExcludedFields("Details"), ) } func TestSignalExternalWorkflowExecutionFailedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSignalExternalWorkflowExecutionFailedEventAttributes, ToSignalExternalWorkflowExecutionFailedEventAttributes, testutils.WithCustomFuncs( SignalExternalWorkflowExecutionFailedCauseFuzzer, ), ) } func TestClusterReplicationConfigurationArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromClusterReplicationConfigurationArray, ToClusterReplicationConfigurationArray) } func TestIndexedValueTypeMapFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromIndexedValueTypeMap, ToIndexedValueTypeMap, testutils.WithCustomFuncs( IndexedValueTypeFuzzer, ), ) } func TestPollForActivityTaskRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPollForActivityTaskRequest, ToPollForActivityTaskRequest) } func TestResetPointInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromResetPointInfo, ToResetPointInfo) } func TestCancelTimerDecisionAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCancelTimerDecisionAttributes, ToCancelTimerDecisionAttributes) } func TestListDomainsRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListDomainsRequest, ToListDomainsRequest) } func TestPollForActivityTaskResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPollForActivityTaskResponse, ToPollForActivityTaskResponse) } func TestTaskListMetadataFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromTaskListMetadata, ToTaskListMetadata) } func TestListFailoverHistoryResponseFuzz(t *testing.T) { // FailoverEvent.ID: empty string is normalized to nil during conversion (ToFailoverEvent only sets ID if non-empty) testutils.RunMapperFuzzTest(t, FromListFailoverHistoryResponse, ToListFailoverHistoryResponse, testutils.WithCustomFuncs( FailoverTypeFuzzer, ), testutils.WithExcludedFields("ID"), ) } func TestDescribeTaskListResponseFuzz(t *testing.T) { // TaskListPartitionConfig has map[int] fields that get truncated to map[int32] in proto // From and To are non-invertable operations, so we rely on testdata to verify the mapping is correct testutils.RunMapperFuzzTest(t, FromDescribeTaskListResponse, ToDescribeTaskListResponse, testutils.WithExcludedFields("ReadPartitions", "WritePartitions"), ) } func TestStartWorkflowExecutionAsyncRequestFuzz(t *testing.T) { // ActiveClusterSelectionPolicy: string fields must match strategy testutils.RunMapperFuzzTest(t, FromStartWorkflowExecutionAsyncRequest, ToStartWorkflowExecutionAsyncRequest, testutils.WithCustomFuncs( WorkflowIDReusePolicyFuzzer, CronOverlapPolicyFuzzer, ActiveClusterSelectionStrategyFuzzer, ActiveClusterSelectionPolicyFuzzerClearAttribute, ), ) } func TestStartWorkflowExecutionResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromStartWorkflowExecutionResponse, ToStartWorkflowExecutionResponse) } func TestListFailoverHistoryRequestFiltersFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListFailoverHistoryRequestFilters, ToListFailoverHistoryRequestFilters) } func TestChildWorkflowExecutionTerminatedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromChildWorkflowExecutionTerminatedEventAttributes, ToChildWorkflowExecutionTerminatedEventAttributes) } func TestCountWorkflowExecutionsRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCountWorkflowExecutionsRequest, ToCountWorkflowExecutionsRequest) } func TestRequestCancelExternalWorkflowExecutionFailedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRequestCancelExternalWorkflowExecutionFailedEventAttributes, ToRequestCancelExternalWorkflowExecutionFailedEventAttributes, testutils.WithCustomFuncs( CancelExternalWorkflowExecutionFailedCauseFuzzer, ), ) } func TestRequestCancelExternalWorkflowExecutionInitiatedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRequestCancelExternalWorkflowExecutionInitiatedEventAttributes, ToRequestCancelExternalWorkflowExecutionInitiatedEventAttributes) } func TestRespondDecisionTaskCompletedRequestFuzz(t *testing.T) { // Excluding both complex fields - tested separately in TestDecisionArrayFuzz and TestWorkflowQueryResultMapFuzz testutils.RunMapperFuzzTest(t, FromRespondDecisionTaskCompletedRequest, ToRespondDecisionTaskCompletedRequest, testutils.WithExcludedFields("Decisions", "QueryResults"), ) } func TestWorkflowQueryResultMapFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowQueryResultMap, ToWorkflowQueryResultMap) } func TestDecisionTaskStartedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDecisionTaskStartedEventAttributes, ToDecisionTaskStartedEventAttributes) } func TestResetWorkflowExecutionResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromResetWorkflowExecutionResponse, ToResetWorkflowExecutionResponse) } func TestRespondActivityTaskCompletedByIDRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRespondActivityTaskCompletedByIDRequest, ToRespondActivityTaskCompletedByIDRequest) } func TestIsolationGroupMetricsFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromIsolationGroupMetrics, ToIsolationGroupMetrics) } func TestFailoverDomainResponseFuzz(t *testing.T) { // [BUG?] EmitMetric is always set to true in the To* mapper, not set in From mapper // WorkflowExecutionRetentionPeriodInDays loses precision in days→duration→days conversion testutils.RunMapperFuzzTest(t, FromFailoverDomainResponse, ToFailoverDomainResponse, testutils.WithCustomFuncs( func(r *types.FailoverDomainResponse, c fuzz.Continue) { c.Fuzz(r) // Mapper always creates these nested structs, never nil if r.DomainInfo == nil { r.DomainInfo = &types.DomainInfo{} } if r.Configuration == nil { r.Configuration = &types.DomainConfiguration{} } if r.ReplicationConfiguration == nil { r.ReplicationConfiguration = &types.DomainReplicationConfiguration{} } }, testutils.EncodingTypeFuzzer, testutils.IsolationGroupStateFuzzer, testutils.DomainStatusFuzzer, ArchivalStatusFuzzer, ), testutils.WithExcludedFields("EmitMetric", "WorkflowExecutionRetentionPeriodInDays"), ) } func TestFailoverEventArrayFuzz(t *testing.T) { // [BUG] Non-symmetric mapping: An empty string ID becomes nil, but the return trip translates it back to nil testutils.RunMapperFuzzTest(t, FromFailoverEventArray, ToFailoverEventArray, testutils.WithCustomFuncs( FailoverTypeFuzzer, func(e *types.FailoverEvent, c fuzz.Continue) { c.Fuzz(e) // Empty string for ID should be nil if e.ID != nil && *e.ID == "" { e.ID = nil } }, ), ) } func TestWorkflowQueryMapFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowQueryMap, ToWorkflowQueryMap) } func TestActivityTaskCompletedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromActivityTaskCompletedEventAttributes, ToActivityTaskCompletedEventAttributes) } func TestHealthResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromHealthResponse, ToHealthResponse) } func TestListArchivedWorkflowExecutionsResponseFuzz(t *testing.T) { // Executions is tested in FromWorkflowExecutionInfoFuzz test testutils.RunMapperFuzzTest(t, FromListArchivedWorkflowExecutionsResponse, ToListArchivedWorkflowExecutionsResponse, testutils.WithExcludedFields("Executions"), ) } func TestListOpenWorkflowExecutionsResponseFuzz(t *testing.T) { // Executions is tested in FromWorkflowExecutionInfoFuzz test testutils.RunMapperFuzzTest(t, FromListOpenWorkflowExecutionsResponse, ToListOpenWorkflowExecutionsResponse, testutils.WithExcludedFields("Executions"), ) } func TestRespondDecisionTaskFailedRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRespondDecisionTaskFailedRequest, ToRespondDecisionTaskFailedRequest, testutils.WithCustomFuncs( DecisionTaskFailedCauseFuzzer, ), ) } func TestDescribeWorkflowExecutionResponseFuzz(t *testing.T) { t.Skip("All subfields are tested in dedicated fuzz tests") } func TestExternalWorkflowExecutionCancelRequestedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromExternalWorkflowExecutionCancelRequestedEventAttributes, ToExternalWorkflowExecutionCancelRequestedEventAttributes) } func TestSignalExternalWorkflowExecutionInitiatedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSignalExternalWorkflowExecutionInitiatedEventAttributes, ToSignalExternalWorkflowExecutionInitiatedEventAttributes) } func TestStartChildWorkflowExecutionInitiatedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromStartChildWorkflowExecutionInitiatedEventAttributes, ToStartChildWorkflowExecutionInitiatedEventAttributes, testutils.WithCustomFuncs( WorkflowIDReusePolicyFuzzer, CronOverlapPolicyFuzzer, ActiveClusterSelectionStrategyFuzzer, ActiveClusterSelectionPolicyFuzzerWithAttribute, ), ) } func TestWorkflowExecutionTerminatedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowExecutionTerminatedEventAttributes, ToWorkflowExecutionTerminatedEventAttributes) } func TestWorkerVersionInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkerVersionInfo, ToWorkerVersionInfo) } func TestListOpenWorkflowExecutionsRequestFuzz(t *testing.T) { // ExecutionFilter and TypeFilter map to a proto oneof - only one can be set // If both are set, TypeFilter wins and ExecutionFilter is lost testutils.RunMapperFuzzTest(t, FromListOpenWorkflowExecutionsRequest, ToListOpenWorkflowExecutionsRequest, testutils.WithCustomFuncs( func(r *types.ListOpenWorkflowExecutionsRequest, c fuzz.Continue) { c.Fuzz(r) // If both filters are set, clear ExecutionFilter (TypeFilter takes precedence in mapper) if r.ExecutionFilter != nil && r.TypeFilter != nil { r.ExecutionFilter = nil } }, ), ) } func TestGetTaskListsByDomainResponseFuzz(t *testing.T) { // SKIPPED: PartitionConfig is nested inside map values (map[string]*DescribeTaskListResponse) // clearFieldsIf cannot modify fields inside map values due to Go reflection limitations // TODO(c-warren): Implement map rebuilding in clearFieldsIf to support this pattern t.Skip("Map value field exclusion not yet supported") testutils.RunMapperFuzzTest(t, FromGetTaskListsByDomainResponse, ToGetTaskListsByDomainResponse) } func TestRefreshWorkflowTasksRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRefreshWorkflowTasksRequest, ToRefreshWorkflowTasksRequest) } func TestRestartWorkflowExecutionResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRestartWorkflowExecutionResponse, ToRestartWorkflowExecutionResponse) } func TestScanWorkflowExecutionsRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScanWorkflowExecutionsRequest, ToScanWorkflowExecutionsRequest) } func TestUpsertWorkflowSearchAttributesDecisionAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromUpsertWorkflowSearchAttributesDecisionAttributes, ToUpsertWorkflowSearchAttributesDecisionAttributes) } func TestWorkflowExecutionCompletedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowExecutionCompletedEventAttributes, ToWorkflowExecutionCompletedEventAttributes) } func TestWorkflowQueryFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowQuery, ToWorkflowQuery) } func TestHistoryEventFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromHistoryEvent, ToHistoryEvent, testutils.WithCustomFuncs( EventTypeFuzzer, DecisionTypeFuzzer, TimeoutTypeFuzzer, TaskListKindFuzzer, ContinueAsNewInitiatorFuzzer, WorkflowIDReusePolicyFuzzer, CronOverlapPolicyFuzzer, ActiveClusterSelectionStrategyFuzzer, ActiveClusterSelectionPolicyFuzzerWithAttribute, func(h *types.HistoryEvent, c fuzz.Continue) { // Fuzz all fields first c.Fuzz(h) // Ensure EventType is always non-nil (required by mapper switch statement) if h.EventType == nil { eventType := types.EventType(c.Intn(42)) // 0-41: All EventType values h.EventType = &eventType } }, ), ) } func TestActivityTaskFailedEventAttributesFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped testutils.RunMapperFuzzTest(t, FromActivityTaskFailedEventAttributes, ToActivityTaskFailedEventAttributes, testutils.WithExcludedFields("Details"), ) } func TestHistoryFuzz(t *testing.T) { t.Skip("HistoryEvent array testing covered by individual event attribute fuzz tests") } func TestResetWorkflowExecutionRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromResetWorkflowExecutionRequest, ToResetWorkflowExecutionRequest) } func TestRespondActivityTaskCanceledByIDRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRespondActivityTaskCanceledByIDRequest, ToRespondActivityTaskCanceledByIDRequest) } func TestTimerCanceledEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromTimerCanceledEventAttributes, ToTimerCanceledEventAttributes) } func TestDiagnoseWorkflowExecutionResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDiagnoseWorkflowExecutionResponse, ToDiagnoseWorkflowExecutionResponse) } func TestListArchivedWorkflowExecutionsRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListArchivedWorkflowExecutionsRequest, ToListArchivedWorkflowExecutionsRequest) } func TestChildWorkflowExecutionCanceledEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromChildWorkflowExecutionCanceledEventAttributes, ToChildWorkflowExecutionCanceledEventAttributes) } func TestRequestCancelExternalWorkflowExecutionDecisionAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRequestCancelExternalWorkflowExecutionDecisionAttributes, ToRequestCancelExternalWorkflowExecutionDecisionAttributes) } func TestDecisionFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDecision, ToDecision, testutils.WithCustomFuncs( func(d *types.Decision, c fuzz.Continue) { // Fuzz all fields first c.Fuzz(d) // Ensure DecisionType is always non-nil (required by mapper switch statement) if d.DecisionType == nil { decisionType := types.DecisionType(c.Intn(13)) // 0-12: All DecisionType values d.DecisionType = &decisionType } }, DecisionTypeFuzzer, WorkflowIDReusePolicyFuzzer, CronOverlapPolicyFuzzer, ActiveClusterSelectionStrategyFuzzer, ActiveClusterSelectionPolicyFuzzerWithAttribute, ), ) } func TestDeprecateDomainRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDeprecateDomainRequest, ToDeprecateDomainRequest) } func TestFailoverDomainRequestFuzz(t *testing.T) { // [BUG] Non-symmetric mapping: An empty string DomainActiveClusterName becomes nil, but the return trip translates it back to nil // [Missing] Reason is not yet implemented in the mapper testutils.RunMapperFuzzTest(t, FromFailoverDomainRequest, ToFailoverDomainRequest, testutils.WithCustomFuncs( FailoverTypeFuzzer, ), testutils.WithExcludedFields("DomainActiveClusterName", "Reason"), ) } func TestChildWorkflowExecutionStartedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromChildWorkflowExecutionStartedEventAttributes, ToChildWorkflowExecutionStartedEventAttributes) } func TestPollForDecisionTaskResponseFuzz(t *testing.T) { // History.Events: nil vs empty slice (mapper creates empty when nil) // Contains HistoryEvent array which needs comprehensive enum fuzzers testutils.RunMapperFuzzTest(t, FromPollForDecisionTaskResponse, ToPollForDecisionTaskResponse, testutils.WithCustomFuncs( func(h *types.History, c fuzz.Continue) { c.Fuzz(h) // Ensure Events is never nil to avoid nil vs empty slice mismatch if h.Events == nil { h.Events = []*types.HistoryEvent{} } }, func(h *types.HistoryEvent, c fuzz.Continue) { // Fuzz all fields first c.Fuzz(h) // Ensure EventType is always non-nil (required by mapper switch statement) if h.EventType == nil { eventType := types.EventType(c.Intn(42)) // 0-41: All EventType values h.EventType = &eventType } }, EventTypeFuzzer, DecisionTypeFuzzer, TimeoutTypeFuzzer, TaskListKindFuzzer, ContinueAsNewInitiatorFuzzer, WorkflowIDReusePolicyFuzzer, CronOverlapPolicyFuzzer, ActiveClusterSelectionStrategyFuzzer, ActiveClusterSelectionPolicyFuzzerClearAttribute, SignalExternalWorkflowExecutionFailedCauseFuzzer, CancelExternalWorkflowExecutionFailedCauseFuzzer, ChildWorkflowExecutionFailedCauseFuzzer, WorkflowExecutionCloseStatusFuzzer, ParentClosePolicyFuzzer, ), ) } func TestDecisionArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDecisionArray, ToDecisionArray, testutils.WithCustomFuncs( func(d *types.Decision, c fuzz.Continue) { // Fuzz all fields first c.Fuzz(d) // Ensure DecisionType is always non-nil (required by mapper switch statement) if d.DecisionType == nil { decisionType := types.DecisionType(c.Intn(13)) // 0-12: All DecisionType values d.DecisionType = &decisionType } }, DecisionTypeFuzzer, WorkflowIDReusePolicyFuzzer, CronOverlapPolicyFuzzer, ActiveClusterSelectionStrategyFuzzer, ActiveClusterSelectionPolicyFuzzerWithAttribute, ), ) } func TestClusterAttributeFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromClusterAttribute, ToClusterAttribute) } func TestDecisionTaskTimedOutEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDecisionTaskTimedOutEventAttributes, ToDecisionTaskTimedOutEventAttributes, testutils.WithCustomFuncs( DecisionTaskTimedOutCauseFuzzer, ), ) } func TestIndexedValueTypeFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, func(v types.IndexedValueType) apiv1.IndexedValueType { return FromIndexedValueType(v) }, func(v apiv1.IndexedValueType) types.IndexedValueType { return ToIndexedValueType(v) }, ) } func TestDescribeTaskListRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDescribeTaskListRequest, ToDescribeTaskListRequest) } func TestPollerInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPollerInfo, ToPollerInfo) } func TestTerminateWorkflowExecutionRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromTerminateWorkflowExecutionRequest, ToTerminateWorkflowExecutionRequest) } func TestListFailoverHistoryRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListFailoverHistoryRequest, ToListFailoverHistoryRequest) } func TestListClosedWorkflowExecutionsRequestFuzz(t *testing.T) { // ExecutionFilter, TypeFilter, and StatusFilter map to a proto oneof - only one can be set // If multiple are set, StatusFilter wins (checked last), then TypeFilter, then ExecutionFilter testutils.RunMapperFuzzTest(t, FromListClosedWorkflowExecutionsRequest, ToListClosedWorkflowExecutionsRequest, testutils.WithCustomFuncs( func(r *types.ListClosedWorkflowExecutionsRequest, c fuzz.Continue) { c.Fuzz(r) // Clear filters that would be lost in the oneof mapping if r.StatusFilter != nil { // StatusFilter wins - clear the others r.ExecutionFilter = nil r.TypeFilter = nil } else if r.TypeFilter != nil { // TypeFilter wins over ExecutionFilter r.ExecutionFilter = nil } }, ), ) } func TestChildWorkflowExecutionFailedEventAttributesFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped testutils.RunMapperFuzzTest(t, FromChildWorkflowExecutionFailedEventAttributes, ToChildWorkflowExecutionFailedEventAttributes, testutils.WithExcludedFields("Details"), ) } func TestListClosedWorkflowExecutionsResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListClosedWorkflowExecutionsResponse, ToListClosedWorkflowExecutionsResponse, testutils.WithExcludedFields("Executions"), // Executions is tested in FromWorkflowExecutionInfoFuzz test ) } func TestRestartWorkflowExecutionRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRestartWorkflowExecutionRequest, ToRestartWorkflowExecutionRequest) } func TestActivityTaskCanceledEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromActivityTaskCanceledEventAttributes, ToActivityTaskCanceledEventAttributes) } func TestListDomainsResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListDomainsResponse, ToListDomainsResponse, testutils.WithExcludedFields("Domains"), // Domains is tested in TestDescribeDomainResponseDomainFuzz test ) } func TestMarkerRecordedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromMarkerRecordedEventAttributes, ToMarkerRecordedEventAttributes) } func TestTimerFiredEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromTimerFiredEventAttributes, ToTimerFiredEventAttributes) } func TestUpdateDomainRequestFuzz(t *testing.T) { // [BUG?/DEPRECATED] EmitMetric is always set to true in the To* mapper, not set in From mapper // [Missing] FailoverReason is not yet implemented in the mapper // [BUG] WorkflowExecutionRetentionPeriodInDays loses precision in days→duration→days conversion // HistoryArchivalStatus, VisibilityArchivalStatus: only included in field mask if corresponding URI is non-nil testutils.RunMapperFuzzTest(t, FromUpdateDomainRequest, ToUpdateDomainRequest, testutils.WithCustomFuncs( testutils.EncodingTypeFuzzer, testutils.IsolationGroupStateFuzzer, testutils.DomainStatusFuzzer, ArchivalStatusFuzzer, ), testutils.WithExcludedFields("EmitMetric", "FailoverReason", "WorkflowExecutionRetentionPeriodInDays", "HistoryArchivalStatus", "VisibilityArchivalStatus"), ) } func TestActivityTaskTimedOutEventAttributesFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped testutils.RunMapperFuzzTest(t, FromActivityTaskTimedOutEventAttributes, ToActivityTaskTimedOutEventAttributes, testutils.WithExcludedFields("LastFailureDetails"), ) } func TestChildWorkflowExecutionTimedOutEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromChildWorkflowExecutionTimedOutEventAttributes, ToChildWorkflowExecutionTimedOutEventAttributes) } func TestDecisionTaskCompletedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDecisionTaskCompletedEventAttributes, ToDecisionTaskCompletedEventAttributes) } func TestGetWorkflowExecutionHistoryResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromGetWorkflowExecutionHistoryResponse, ToGetWorkflowExecutionHistoryResponse, testutils.WithExcludedFields("History"), // History is tested in TestHistoryEventFuzz test testutils.WithCustomFuncs( testutils.EncodingTypeFuzzer, ), ) } func TestQueryRejectedFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromQueryRejected, ToQueryRejected) } func TestResetPointsFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromResetPoints, ToResetPoints) } func TestSignalWithStartWorkflowExecutionRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSignalWithStartWorkflowExecutionRequest, ToSignalWithStartWorkflowExecutionRequest, testutils.WithCustomFuncs( WorkflowIDReusePolicyFuzzer, CronOverlapPolicyFuzzer, ActiveClusterSelectionStrategyFuzzer, ActiveClusterSelectionPolicyFuzzerWithAttribute, ), ) } func TestWorkflowExecutionFailedEventAttributesFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped testutils.RunMapperFuzzTest(t, FromWorkflowExecutionFailedEventAttributes, ToWorkflowExecutionFailedEventAttributes, testutils.WithExcludedFields("Details"), ) } func TestPendingDecisionInfoFuzz(t *testing.T) { // [BUG] Attempt field is truncated from int64 to int32 in proto mapper testutils.RunMapperFuzzTest(t, FromPendingDecisionInfo, ToPendingDecisionInfo, testutils.WithExcludedFields("Attempt"), ) } func TestStickyExecutionAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromStickyExecutionAttributes, ToStickyExecutionAttributes) } func TestWorkflowExecutionFilterFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowExecutionFilter, ToWorkflowExecutionFilter) } func TestActivityTaskStartedEventAttributesFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped testutils.RunMapperFuzzTest(t, FromActivityTaskStartedEventAttributes, ToActivityTaskStartedEventAttributes, testutils.WithExcludedFields("LastFailureDetails"), ) } func TestDecisionTaskScheduledEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDecisionTaskScheduledEventAttributes, ToDecisionTaskScheduledEventAttributes, testutils.WithCustomFuncs( func(v *types.DecisionTaskScheduledEventAttributes, c fuzz.Continue) { v.TaskList = &types.TaskList{} c.Fuzz(v.TaskList) if c.RandBool() { timeout := c.Int31() v.StartToCloseTimeoutSeconds = &timeout } // Attempt is int64 but proto is int32, so limit to int32 range v.Attempt = int64(c.Int31()) }, ), ) } func TestQueryWorkflowResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromQueryWorkflowResponse, ToQueryWorkflowResponse) } func TestSignalWorkflowExecutionRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSignalWorkflowExecutionRequest, ToSignalWorkflowExecutionRequest) } func TestPendingChildExecutionInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPendingChildExecutionInfo, ToPendingChildExecutionInfo) } func TestPollForDecisionTaskRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPollForDecisionTaskRequest, ToPollForDecisionTaskRequest) } func TestRecordMarkerDecisionAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRecordMarkerDecisionAttributes, ToRecordMarkerDecisionAttributes) } func TestWorkflowQueryResultFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowQueryResult, ToWorkflowQueryResult) } func TestStartChildWorkflowExecutionFailedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromStartChildWorkflowExecutionFailedEventAttributes, ToStartChildWorkflowExecutionFailedEventAttributes, testutils.WithCustomFuncs( func(e *types.ChildWorkflowExecutionFailedCause, c fuzz.Continue) { *e = types.ChildWorkflowExecutionFailedCause(0) // 0: WorkflowAlreadyRunning }, ), ) } func TestTaskListStatusFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromTaskListStatus, ToTaskListStatus) } func TestTaskListPartitionMetadataArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromTaskListPartitionMetadataArray, ToTaskListPartitionMetadataArray) } func TestDecisionTaskFailedEventAttributesFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if Reason is non-nil, so Details without Reason are dropped testutils.RunMapperFuzzTest(t, FromDecisionTaskFailedEventAttributes, ToDecisionTaskFailedEventAttributes, testutils.WithCustomFuncs( DecisionTaskFailedCauseFuzzer, ), testutils.WithExcludedFields("Details"), ) } func TestDescribeDomainRequestFuzz(t *testing.T) { // [BUG] Non-symmetric mapping: An empty string ID becomes nil, but the return trip translates it back to nil testutils.RunMapperFuzzTest(t, FromDescribeDomainRequest, ToDescribeDomainRequest, testutils.WithCustomFuncs( func(r *types.DescribeDomainRequest, c fuzz.Continue) { c.Fuzz(r) // Must have at least one of Name or UUID set (oneof field) if r.Name == nil && r.UUID == nil { if c.RandBool() { r.Name = new(string) *r.Name = c.RandString() } else { r.UUID = new(string) *r.UUID = c.RandString() } } }, ), testutils.WithExcludedFields("ID"), ) } func TestResetStickyTaskListRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromResetStickyTaskListRequest, ToResetStickyTaskListRequest) } func TestActivityTaskCancelRequestedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromActivityTaskCancelRequestedEventAttributes, ToActivityTaskCancelRequestedEventAttributes) } func TestBadBinaryInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromBadBinaryInfo, ToBadBinaryInfo) } func TestCancelTimerFailedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCancelTimerFailedEventAttributes, ToCancelTimerFailedEventAttributes) } func TestRespondActivityTaskCompletedRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRespondActivityTaskCompletedRequest, ToRespondActivityTaskCompletedRequest) } func TestPaginationOptionsFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPaginationOptions, ToPaginationOptions) } func TestCancelWorkflowExecutionDecisionAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCancelWorkflowExecutionDecisionAttributes, ToCancelWorkflowExecutionDecisionAttributes) } func TestFailoverInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromFailoverInfo, ToFailoverInfo) } func TestRespondActivityTaskFailedRequestFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped testutils.RunMapperFuzzTest(t, FromRespondActivityTaskFailedRequest, ToRespondActivityTaskFailedRequest, testutils.WithExcludedFields("Details"), ) } func TestScanWorkflowExecutionsResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScanWorkflowExecutionsResponse, ToScanWorkflowExecutionsResponse, testutils.WithExcludedFields("Executions"), // Executions is tested in FromWorkflowExecutionInfoFuzz test ) } func TestScheduleActivityTaskDecisionAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScheduleActivityTaskDecisionAttributes, ToScheduleActivityTaskDecisionAttributes) } func TestTaskListFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromTaskList, ToTaskList) } func TestMemoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromMemo, ToMemo) } func TestResetPointInfoArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromResetPointInfoArray, ToResetPointInfoArray) } func TestSearchAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSearchAttributes, ToSearchAttributes) } func TestTaskListPartitionMetadataFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromTaskListPartitionMetadata, ToTaskListPartitionMetadata) } func TestWorkflowExecutionSignaledEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowExecutionSignaledEventAttributes, ToWorkflowExecutionSignaledEventAttributes) } func TestHistoryEventArrayFuzz(t *testing.T) { t.Skip("Tested in TestHistoryEventFuzz test") } func TestActivityLocalDispatchInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromActivityLocalDispatchInfo, ToActivityLocalDispatchInfo) } func TestStartWorkflowExecutionRequestFuzz(t *testing.T) { // ActiveClusterSelectionPolicy has mutually exclusive fields testutils.RunMapperFuzzTest(t, FromStartWorkflowExecutionRequest, ToStartWorkflowExecutionRequest, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, WorkflowIDReusePolicyFuzzer, ), ) } func TestUpsertWorkflowSearchAttributesEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromUpsertWorkflowSearchAttributesEventAttributes, ToUpsertWorkflowSearchAttributesEventAttributes) } func TestExternalWorkflowExecutionSignaledEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromExternalWorkflowExecutionSignaledEventAttributes, ToExternalWorkflowExecutionSignaledEventAttributes) } func TestWorkflowTypeFilterFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowTypeFilter, ToWorkflowTypeFilter) } func TestDescribeTaskListResponseMapFuzz(t *testing.T) { // Map[int] with int64 keys don't roundtrip correctly through proto int32 // Use custom fuzzer to nil out ReadPartitions/WritePartitions in all map values testutils.RunMapperFuzzTest(t, FromDescribeTaskListResponseMap, ToDescribeTaskListResponseMap, testutils.WithCustomFuncs( func(m *map[string]*types.DescribeTaskListResponse, c fuzz.Continue) { c.Fuzz(m) // Exclude map[int] fields that don't roundtrip through proto for _, resp := range *m { if resp != nil && resp.PartitionConfig != nil { resp.PartitionConfig.ReadPartitions = nil resp.PartitionConfig.WritePartitions = nil } } }, ), ) } func TestTimerStartedEventAttributesFuzz(t *testing.T) { // StartToFireTimeoutSeconds is int64 but proto converts through int32, so limit to int32 range testutils.RunMapperFuzzTest(t, FromTimerStartedEventAttributes, ToTimerStartedEventAttributes, testutils.WithCustomFuncs( func(v *types.TimerStartedEventAttributes, c fuzz.Continue) { c.Fuzz(v) if v.StartToFireTimeoutSeconds != nil { *v.StartToFireTimeoutSeconds = int64(c.Int31()) } }, ), ) } func TestActivityTypeFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromActivityType, ToActivityType) } func TestWorkflowExecutionFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowExecution, ToWorkflowExecution) } func TestCompleteWorkflowExecutionDecisionAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCompleteWorkflowExecutionDecisionAttributes, ToCompleteWorkflowExecutionDecisionAttributes) } func TestListTaskListPartitionsRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListTaskListPartitionsRequest, ToListTaskListPartitionsRequest) } func TestCountWorkflowExecutionsResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCountWorkflowExecutionsResponse, ToCountWorkflowExecutionsResponse) } func TestDataBlobFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDataBlob, ToDataBlob, testutils.WithCustomFuncs( testutils.EncodingTypeFuzzer, ), ) } func TestActiveClusterInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromActiveClusterInfo, ToActiveClusterInfo) } func TestActiveClusterSelectionPolicyFuzz(t *testing.T) { // ActiveClusterSelectionPolicy has mutually exclusive fields based on Strategy testutils.RunMapperFuzzTest(t, FromActiveClusterSelectionPolicy, ToActiveClusterSelectionPolicy, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, ), ) } func TestRespondActivityTaskCanceledRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRespondActivityTaskCanceledRequest, ToRespondActivityTaskCanceledRequest) } func TestPendingChildExecutionInfoArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPendingChildExecutionInfoArray, ToPendingChildExecutionInfoArray) } func TestRecordActivityTaskHeartbeatResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRecordActivityTaskHeartbeatResponse, ToRecordActivityTaskHeartbeatResponse) } func TestStartTimerDecisionAttributesFuzz(t *testing.T) { // StartToFireTimeoutSeconds is int64 but proto converts through int32 testutils.RunMapperFuzzTest(t, FromStartTimerDecisionAttributes, ToStartTimerDecisionAttributes, testutils.WithCustomFuncs( func(v *types.StartTimerDecisionAttributes, c fuzz.Continue) { c.Fuzz(v) if v.StartToFireTimeoutSeconds != nil { val := int64(c.Int31()) v.StartToFireTimeoutSeconds = &val } }, ), ) } func TestSupportedClientVersionsFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSupportedClientVersions, ToSupportedClientVersions) } func TestClusterFailoverArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromClusterFailoverArray, ToClusterFailoverArray) } func TestActiveClustersFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromActiveClusters, ToActiveClusters) } func TestChildWorkflowExecutionCompletedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromChildWorkflowExecutionCompletedEventAttributes, ToChildWorkflowExecutionCompletedEventAttributes) } func TestClusterReplicationConfigurationFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromClusterReplicationConfiguration, ToClusterReplicationConfiguration) } func TestWorkflowExecutionInfoFuzz(t *testing.T) { // [BUG] UpdateTime missing from mapper (not sent over proto) // [Intended] ParentDomainID, ParentDomain: converted to empty string instead of nil when ParentExecutionInfo exists but field is empty // [Intended] ParentInitiatedID: converted to 0 instead of nil when ParentExecutionInfo exists but field is 0 // [BUG] CronSchedule is not round trip safe with an empty string testutils.RunMapperFuzzTest(t, FromWorkflowExecutionInfo, ToWorkflowExecutionInfo, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, // ActiveClusterSelectionPolicy has mutually exclusive fields based on Strategy ), testutils.WithExcludedFields("UpdateTime", "ParentDomainID", "ParentDomain", "ParentInitiatedID", "CronSchedule"), ) } func TestIsolationGroupMetricsMapFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromIsolationGroupMetricsMap, ToIsolationGroupMetricsMap) } func TestSignalExternalWorkflowExecutionDecisionAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSignalExternalWorkflowExecutionDecisionAttributes, ToSignalExternalWorkflowExecutionDecisionAttributes) } func TestSignalWithStartWorkflowExecutionAsyncRequestFuzz(t *testing.T) { // ActiveClusterSelectionPolicy has mutually exclusive fields, WorkflowIDReusePolicy enum testutils.RunMapperFuzzTest(t, FromSignalWithStartWorkflowExecutionAsyncRequest, ToSignalWithStartWorkflowExecutionAsyncRequest, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, WorkflowIDReusePolicyFuzzer, ), ) } func TestPollerInfoArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPollerInfoArray, ToPollerInfoArray) } func TestResetStickyTaskListResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromResetStickyTaskListResponse, ToResetStickyTaskListResponse) } func TestAPITaskListPartitionFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAPITaskListPartition, ToAPITaskListPartition) } func TestListWorkflowExecutionsResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListWorkflowExecutionsResponse, ToListWorkflowExecutionsResponse, testutils.WithExcludedFields("Executions"), // Executions is tested in FromWorkflowExecutionInfoFuzz test ) } func TestParentExecutionInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromParentExecutionInfo, ToParentExecutionInfo) } func TestFailWorkflowExecutionDecisionAttributesFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped testutils.RunMapperFuzzTest(t, FromFailWorkflowExecutionDecisionAttributes, ToFailWorkflowExecutionDecisionAttributes, testutils.WithExcludedFields("Details"), ) } func TestGetClusterInfoResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromGetClusterInfoResponse, ToGetClusterInfoResponse) } func TestSignalWithStartWorkflowExecutionAsyncResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSignalWithStartWorkflowExecutionAsyncResponse, ToSignalWithStartWorkflowExecutionAsyncResponse) } func TestDescribeDomainResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDescribeDomainResponse, ToDescribeDomainResponse, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, testutils.DomainStatusFuzzer, ArchivalStatusFuzzer, func(r *types.DescribeDomainResponse, c fuzz.Continue) { c.Fuzz(r) // [BUG] On a round-trip, if DomainInfo, Configuration, or ReplicationConfiguration are nil, they become non-nil if r.DomainInfo == nil { r.DomainInfo = &types.DomainInfo{} } if r.Configuration == nil { r.Configuration = &types.DomainConfiguration{} } if r.ReplicationConfiguration == nil { r.ReplicationConfiguration = &types.DomainReplicationConfiguration{} } }, ), testutils.WithExcludedFields("EmitMetric"), // EmitMetric is deprecated and permanently set to true ) } func TestListWorkflowExecutionsRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListWorkflowExecutionsRequest, ToListWorkflowExecutionsRequest) } func TestRequestCancelActivityTaskFailedEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRequestCancelActivityTaskFailedEventAttributes, ToRequestCancelActivityTaskFailedEventAttributes) } func TestStatusFilterFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromStatusFilter, ToStatusFilter) } func TestPayloadFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPayload, ToPayload) } func TestRespondDecisionTaskCompletedResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRespondDecisionTaskCompletedResponse, ToRespondDecisionTaskCompletedResponse, testutils.WithCustomFuncs( func(h *types.History, c fuzz.Continue) { c.Fuzz(h) // nil Events slice becomes empty after proto roundtrip if h.Events == nil { h.Events = []*types.HistoryEvent{} } }, ), ) } func TestStartTimeFilterFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromStartTimeFilter, ToStartTimeFilter) } func TestWorkflowExecutionContinuedAsNewEventAttributesFuzz(t *testing.T) { // ActiveClusterSelectionPolicy has mutually exclusive fields // JitterStartSeconds don't roundtrip correctly // [BUG] FailureDetails requires FailureReason to be set testutils.RunMapperFuzzTest(t, FromWorkflowExecutionContinuedAsNewEventAttributes, ToWorkflowExecutionContinuedAsNewEventAttributes, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, ContinueAsNewInitiatorFuzzer, ), testutils.WithExcludedFields("JitterStartSeconds", "FailureDetails"), ) } func TestGetSearchAttributesResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromGetSearchAttributesResponse, ToGetSearchAttributesResponse) } func TestRecordActivityTaskHeartbeatByIDRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRecordActivityTaskHeartbeatByIDRequest, ToRecordActivityTaskHeartbeatByIDRequest) } func TestTaskIDBlockFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromTaskIDBlock, ToTaskIDBlock) } func TestActivityLocalDispatchInfoMapFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromActivityLocalDispatchInfoMap, ToActivityLocalDispatchInfoMap) } func TestHeaderFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromHeader, ToHeader) } func TestQueryWorkflowRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromQueryWorkflowRequest, ToQueryWorkflowRequest, testutils.WithCustomFuncs( QueryRejectConditionFuzzer, QueryConsistencyLevelFuzzer, ), ) } func TestRecordActivityTaskHeartbeatByIDResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRecordActivityTaskHeartbeatByIDResponse, ToRecordActivityTaskHeartbeatByIDResponse) } func TestWorkflowExecutionConfigurationFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowExecutionConfiguration, ToWorkflowExecutionConfiguration) } func TestPendingActivityInfoFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped testutils.RunMapperFuzzTest(t, FromPendingActivityInfo, ToPendingActivityInfo, testutils.WithCustomFuncs( PendingActivityStateFuzzer, ), testutils.WithExcludedFields("LastFailureDetails"), ) } func TestDiagnoseWorkflowExecutionRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDiagnoseWorkflowExecutionRequest, ToDiagnoseWorkflowExecutionRequest) } func TestWorkflowExecutionStartedEventAttributesFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped // ActiveClusterSelectionPolicy has mutually exclusive fields // JitterStartSeconds don't roundtrip // Empty string fields become nil testutils.RunMapperFuzzTest(t, FromWorkflowExecutionStartedEventAttributes, ToWorkflowExecutionStartedEventAttributes, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, func(e *types.WorkflowExecutionStartedEventAttributes, c fuzz.Continue) { c.Fuzz(e) // Empty strings become nil after proto roundtrip if e.ParentWorkflowDomain != nil && *e.ParentWorkflowDomain == "" { e.ParentWorkflowDomain = nil } if e.ParentWorkflowDomainID != nil && *e.ParentWorkflowDomainID == "" { e.ParentWorkflowDomainID = nil } }, ), testutils.WithExcludedFields("JitterStartSeconds"), ) } func TestGetWorkflowExecutionHistoryRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromGetWorkflowExecutionHistoryRequest, ToGetWorkflowExecutionHistoryRequest, testutils.WithCustomFuncs( HistoryEventFilterTypeFuzzer, QueryConsistencyLevelFuzzer, ), ) } func TestRequestCancelActivityTaskDecisionAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRequestCancelActivityTaskDecisionAttributes, ToRequestCancelActivityTaskDecisionAttributes) } func TestClusterFailoverFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromClusterFailover, ToClusterFailover) } func TestDescribeDomainResponseDomainFuzz(t *testing.T) { // ActiveClusterSelectionPolicy has mutually exclusive fields based on Strategy // WorkflowExecutionRetentionPeriodInDays: nil→0 conversion // [BUG] DomainInfo, Configuration, ReplicationConfiguration must be non-nil testutils.RunMapperFuzzTest(t, FromDescribeDomainResponseDomain, ToDescribeDomainResponseDomain, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, testutils.DomainStatusFuzzer, ArchivalStatusFuzzer, func(r *types.DescribeDomainResponse, c fuzz.Continue) { c.Fuzz(r) // Proto mapper requires these to be non-nil if r.DomainInfo == nil { r.DomainInfo = &types.DomainInfo{} } if r.Configuration == nil { r.Configuration = &types.DomainConfiguration{} } if r.ReplicationConfiguration == nil { r.ReplicationConfiguration = &types.DomainReplicationConfiguration{} } }, ), testutils.WithExcludedFields("WorkflowExecutionRetentionPeriodInDays", "EmitMetric"), ) } func TestRetryPolicyFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRetryPolicy, ToRetryPolicy) } func TestStartWorkflowExecutionAsyncResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromStartWorkflowExecutionAsyncResponse, ToStartWorkflowExecutionAsyncResponse) } func TestAPITaskListPartitionConfigFuzz(t *testing.T) { // From and To are non-invertable operations, so we rely on testdata to verify the mapping is correct testutils.RunMapperFuzzTest(t, FromAPITaskListPartitionConfig, ToAPITaskListPartitionConfig, testutils.WithExcludedFields("ReadPartitions", "WritePartitions"), ) } func TestRespondQueryTaskCompletedRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRespondQueryTaskCompletedRequest, ToRespondQueryTaskCompletedRequest, testutils.WithCustomFuncs( CompletedTypeFuzzer, ), ) } func TestListTaskListPartitionsResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListTaskListPartitionsResponse, ToListTaskListPartitionsResponse) } func TestWorkflowTypeFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowType, ToWorkflowType) } func TestWorkflowExecutionInfoArrayFuzz(t *testing.T) { t.Skip("Tested in FromWorkflowExecutionInfoFuzz test") } func TestRecordActivityTaskHeartbeatRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRecordActivityTaskHeartbeatRequest, ToRecordActivityTaskHeartbeatRequest) } func TestWorkflowExecutionCancelRequestedEventAttributesFuzz(t *testing.T) { // [BUG] Non-symmetric mapping: An external initiated event ID of 0 becomes nil, but the return trip translates it back to nil testutils.RunMapperFuzzTest(t, FromWorkflowExecutionCancelRequestedEventAttributes, ToWorkflowExecutionCancelRequestedEventAttributes, testutils.WithExcludedFields("ExternalInitiatedEventID"), ) } func TestUpdateDomainResponseFuzz(t *testing.T) { // ActiveClusterSelectionPolicy has mutually exclusive fields based on Strategy // WorkflowExecutionRetentionPeriodInDays: nil→0 conversion // DomainInfo, Configuration, ReplicationConfiguration must be non-nil testutils.RunMapperFuzzTest(t, FromUpdateDomainResponse, ToUpdateDomainResponse, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, testutils.DomainStatusFuzzer, ArchivalStatusFuzzer, func(r *types.UpdateDomainResponse, c fuzz.Continue) { c.Fuzz(r) // Proto mapper requires these to be non-nil if r.DomainInfo == nil { r.DomainInfo = &types.DomainInfo{} } if r.Configuration == nil { r.Configuration = &types.DomainConfiguration{} } if r.ReplicationConfiguration == nil { r.ReplicationConfiguration = &types.DomainReplicationConfiguration{} } }, ), testutils.WithExcludedFields("WorkflowExecutionRetentionPeriodInDays", "EmitMetric"), ) } func TestWorkflowExecutionCanceledEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowExecutionCanceledEventAttributes, ToWorkflowExecutionCanceledEventAttributes) } func TestWorkflowExecutionTimedOutEventAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromWorkflowExecutionTimedOutEventAttributes, ToWorkflowExecutionTimedOutEventAttributes) } func TestPayloadMapFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPayloadMap, ToPayloadMap) } func TestAutoConfigHintFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromAutoConfigHint, ToAutoConfigHint) } func TestBadBinariesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromBadBinaries, ToBadBinaries) } func TestSignalWithStartWorkflowExecutionResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSignalWithStartWorkflowExecutionResponse, ToSignalWithStartWorkflowExecutionResponse) } func TestStartChildWorkflowExecutionDecisionAttributesFuzz(t *testing.T) { // ActiveClusterSelectionPolicy has mutually exclusive fields, WorkflowIDReusePolicy enum testutils.RunMapperFuzzTest(t, FromStartChildWorkflowExecutionDecisionAttributes, ToStartChildWorkflowExecutionDecisionAttributes, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, WorkflowIDReusePolicyFuzzer, ), ) } func TestActivityTaskScheduledEventAttributesFuzz(t *testing.T) { // [BUG] Non-symmetric mapping: An empty string domain becomes nil, but the return trip translates it back to nil - not empty string testutils.RunMapperFuzzTest(t, FromActivityTaskScheduledEventAttributes, ToActivityTaskScheduledEventAttributes, testutils.WithExcludedFields("Domain"), ) } func TestGetTaskListsByDomainRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromGetTaskListsByDomainRequest, ToGetTaskListsByDomainRequest) } func TestFailoverEventFuzz(t *testing.T) { // [BUG] Non-symmetric mapping: An empty string ID becomes nil, but the return trip translates it back to nil testutils.RunMapperFuzzTest(t, FromFailoverEvent, ToFailoverEvent, testutils.WithCustomFuncs( FailoverTypeFuzzer, func(e *types.FailoverEvent, c fuzz.Continue) { c.Fuzz(e) // Empty string ID becomes nil if e.ID != nil && *e.ID == "" { e.ID = nil } }, ), ) } func TestContinueAsNewWorkflowExecutionDecisionAttributesFuzz(t *testing.T) { // [BUG] FromFailure only creates a Failure object if reason is non-nil, so details without reason are dropped // ActiveClusterSelectionPolicy has mutually exclusive fields // JitterStartSeconds doesn't roundtrip correctly testutils.RunMapperFuzzTest(t, FromContinueAsNewWorkflowExecutionDecisionAttributes, ToContinueAsNewWorkflowExecutionDecisionAttributes, testutils.WithCustomFuncs( ActiveClusterSelectionPolicyFuzzerNoCustom, ContinueAsNewInitiatorFuzzer, ), testutils.WithExcludedFields("JitterStartSeconds", "FailureDetails"), ) } func TestClusterAttributeScopeFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromClusterAttributeScope, ToClusterAttributeScope) } func TestBadBinaryInfoMapFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromBadBinaryInfoMap, ToBadBinaryInfoMap) } ================================================ FILE: common/types/mapper/proto/enum_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "testing" "github.com/stretchr/testify/assert" adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" sharedv1 "github.com/uber/cadence/.gen/proto/shared/v1" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const UnknownValue = 9999 func TestTaskSource(t *testing.T) { for _, item := range []*types.TaskSource{ nil, types.TaskSourceHistory.Ptr(), types.TaskSourceDbBacklog.Ptr(), } { assert.Equal(t, item, ToTaskSource(FromTaskSource(item))) } } func TestToTaskSource(t *testing.T) { cases := []struct { name string input sharedv1.TaskSource expected *types.TaskSource }{ { name: "when input is invalid it should return nil", input: sharedv1.TaskSource_TASK_SOURCE_INVALID, }, { name: "when input is out of range it should return nil", input: UnknownValue, }, { name: "when input is valid it should return the correctly mapped value", input: sharedv1.TaskSource_TASK_SOURCE_HISTORY, expected: types.TaskSourceHistory.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToTaskSource(tc.input)) }) } } func TestFromTaskSource(t *testing.T) { cases := []struct { name string input *types.TaskSource expected sharedv1.TaskSource }{ { name: "when input is nil it should return INVALID", input: nil, }, { name: "when input is valid it should return the correctly mapped value", input: types.TaskSourceHistory.Ptr(), expected: sharedv1.TaskSource_TASK_SOURCE_HISTORY, }, { name: "when input is out of range it should return INVALID", input: types.TaskSource(UnknownValue).Ptr(), expected: sharedv1.TaskSource_TASK_SOURCE_INVALID, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromTaskSource(tc.input)) }) } } func TestDLQType(t *testing.T) { for _, item := range []*types.DLQType{ nil, types.DLQTypeReplication.Ptr(), types.DLQTypeDomain.Ptr(), } { assert.Equal(t, item, ToDLQType(FromDLQType(item))) } } func TestToDLQType(t *testing.T) { cases := []struct { name string input adminv1.DLQType expected *types.DLQType }{ { name: "when input is invalid it should return nil", input: adminv1.DLQType_DLQ_TYPE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: UnknownValue, expected: nil, }, { name: "when input is valid it should return the correctly mapped value", input: adminv1.DLQType_DLQ_TYPE_REPLICATION, expected: types.DLQTypeReplication.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToDLQType(tc.input)) }) } } func TestFromDLQType(t *testing.T) { cases := []struct { name string input *types.DLQType expected adminv1.DLQType }{ { name: "when input is nil it should return INVALID", input: nil, expected: adminv1.DLQType_DLQ_TYPE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.DLQType(UnknownValue).Ptr(), expected: adminv1.DLQType_DLQ_TYPE_INVALID, }, { name: "when input is valid it should return the correctly mapped value", input: types.DLQTypeReplication.Ptr(), expected: adminv1.DLQType_DLQ_TYPE_REPLICATION, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromDLQType(tc.input)) }) } } func TestDomainOperation(t *testing.T) { for _, item := range []*types.DomainOperation{ nil, types.DomainOperationCreate.Ptr(), types.DomainOperationUpdate.Ptr(), types.DomainOperationDelete.Ptr(), } { assert.Equal(t, item, ToDomainOperation(FromDomainOperation(item))) } } func TestToDomainOperation(t *testing.T) { cases := []struct { name string input adminv1.DomainOperation expected *types.DomainOperation }{ { name: "when input is invalid it should return nil", input: adminv1.DomainOperation_DOMAIN_OPERATION_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: UnknownValue, expected: nil, }, { name: "when input is valid it should return the correctly mapped value", input: adminv1.DomainOperation_DOMAIN_OPERATION_CREATE, expected: types.DomainOperationCreate.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToDomainOperation(tc.input)) }) } } func TestFromDomainOperation(t *testing.T) { cases := []struct { name string input *types.DomainOperation expected adminv1.DomainOperation }{ { name: "when input is nil it should return INVALID", input: nil, expected: adminv1.DomainOperation_DOMAIN_OPERATION_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.DomainOperation(UnknownValue).Ptr(), expected: adminv1.DomainOperation_DOMAIN_OPERATION_INVALID, }, { name: "when input is valid it should return the correctly mapped value", input: types.DomainOperationCreate.Ptr(), expected: adminv1.DomainOperation_DOMAIN_OPERATION_CREATE, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromDomainOperation(tc.input)) }) } } func TestReplicationTaskType(t *testing.T) { for _, item := range []*types.ReplicationTaskType{ nil, types.ReplicationTaskTypeDomain.Ptr(), types.ReplicationTaskTypeHistory.Ptr(), types.ReplicationTaskTypeSyncShardStatus.Ptr(), types.ReplicationTaskTypeSyncActivity.Ptr(), types.ReplicationTaskTypeHistoryMetadata.Ptr(), types.ReplicationTaskTypeHistoryV2.Ptr(), types.ReplicationTaskTypeFailoverMarker.Ptr(), } { assert.Equal(t, item, ToReplicationTaskType(FromReplicationTaskType(item))) } } func TestToReplicationTaskType(t *testing.T) { cases := []struct { name string input adminv1.ReplicationTaskType expected *types.ReplicationTaskType }{ { name: "when input is invalid it should return nil", input: adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: adminv1.ReplicationTaskType(UnknownValue), expected: nil, }, { name: "when input is valid it should return the correctly mapped value", input: adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_DOMAIN, expected: types.ReplicationTaskTypeDomain.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToReplicationTaskType(tc.input)) }) } } func TestFromReplicationTaskType(t *testing.T) { cases := []struct { name string input *types.ReplicationTaskType expected adminv1.ReplicationTaskType }{ { name: "when input is nil it should return INVALID", input: nil, expected: adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.ReplicationTaskType(UnknownValue).Ptr(), expected: adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_INVALID, }, { name: "when input is valid it should return the correctly mapped value", input: types.ReplicationTaskTypeDomain.Ptr(), expected: adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_DOMAIN, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromReplicationTaskType(tc.input)) }) } } func TestArchivalStatus(t *testing.T) { for _, item := range []*types.ArchivalStatus{ nil, types.ArchivalStatusDisabled.Ptr(), types.ArchivalStatusEnabled.Ptr(), } { assert.Equal(t, item, ToArchivalStatus(FromArchivalStatus(item))) } } func TestToArchivalStatus(t *testing.T) { cases := []struct { name string input apiv1.ArchivalStatus expected *types.ArchivalStatus }{ { name: "when input is invalid it should return nil", input: apiv1.ArchivalStatus_ARCHIVAL_STATUS_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.ArchivalStatus(UnknownValue), expected: nil, }, { name: "when input is disabled it should return the correctly mapped value", input: apiv1.ArchivalStatus_ARCHIVAL_STATUS_DISABLED, expected: types.ArchivalStatusDisabled.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToArchivalStatus(tc.input)) }) } } func TestFromArchivalStatus(t *testing.T) { cases := []struct { name string input *types.ArchivalStatus expected apiv1.ArchivalStatus }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.ArchivalStatus_ARCHIVAL_STATUS_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.ArchivalStatus(UnknownValue).Ptr(), expected: apiv1.ArchivalStatus_ARCHIVAL_STATUS_INVALID, }, { name: "when input is disabled it should return the correctly mapped value", input: types.ArchivalStatusDisabled.Ptr(), expected: apiv1.ArchivalStatus_ARCHIVAL_STATUS_DISABLED, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromArchivalStatus(tc.input)) }) } } func TestCancelExternalWorkflowExecutionFailedCause(t *testing.T) { for _, item := range []*types.CancelExternalWorkflowExecutionFailedCause{ nil, types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr(), types.CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted.Ptr(), } { assert.Equal(t, item, ToCancelExternalWorkflowExecutionFailedCause(FromCancelExternalWorkflowExecutionFailedCause(item))) } } func TestToCancelExternalWorkflowExecutionFailedCause(t *testing.T) { cases := []struct { name string input apiv1.CancelExternalWorkflowExecutionFailedCause expected *types.CancelExternalWorkflowExecutionFailedCause }{ { name: "when input is invalid it should return nil", input: apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.CancelExternalWorkflowExecutionFailedCause(UnknownValue), expected: nil, }, { name: "when input is unknown external workflow execution it should return the correctly mapped value", input: apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION, expected: types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToCancelExternalWorkflowExecutionFailedCause(tc.input)) }) } } func TestFromCancelExternalWorkflowExecutionFailedCause(t *testing.T) { cases := []struct { name string input *types.CancelExternalWorkflowExecutionFailedCause expected apiv1.CancelExternalWorkflowExecutionFailedCause }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.CancelExternalWorkflowExecutionFailedCause(UnknownValue).Ptr(), expected: apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID, }, { name: "when input is unknown external workflow execution it should return the correctly mapped value", input: types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr(), expected: apiv1.CancelExternalWorkflowExecutionFailedCause_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromCancelExternalWorkflowExecutionFailedCause(tc.input)) }) } } func TestChildWorkflowExecutionFailedCause(t *testing.T) { for _, item := range []*types.ChildWorkflowExecutionFailedCause{ nil, types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning.Ptr(), } { assert.Equal(t, item, ToChildWorkflowExecutionFailedCause(FromChildWorkflowExecutionFailedCause(item))) } } func TestToChildWorkflowExecutionFailedCause(t *testing.T) { cases := []struct { name string input apiv1.ChildWorkflowExecutionFailedCause expected *types.ChildWorkflowExecutionFailedCause }{ { name: "when input is invalid it should return nil", input: apiv1.ChildWorkflowExecutionFailedCause_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.ChildWorkflowExecutionFailedCause(UnknownValue), expected: nil, }, { name: "when input is workflow already running it should return the correctly mapped value", input: apiv1.ChildWorkflowExecutionFailedCause_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_RUNNING, expected: types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToChildWorkflowExecutionFailedCause(tc.input)) }) } } func TestFromChildWorkflowExecutionFailedCause(t *testing.T) { cases := []struct { name string input *types.ChildWorkflowExecutionFailedCause expected apiv1.ChildWorkflowExecutionFailedCause }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.ChildWorkflowExecutionFailedCause_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.ChildWorkflowExecutionFailedCause(UnknownValue).Ptr(), expected: apiv1.ChildWorkflowExecutionFailedCause_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID, }, { name: "when input is workflow already running it should return the correctly mapped value", input: types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning.Ptr(), expected: apiv1.ChildWorkflowExecutionFailedCause_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_RUNNING, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromChildWorkflowExecutionFailedCause(tc.input)) }) } } func TestContinueAsNewInitiator(t *testing.T) { for _, item := range []*types.ContinueAsNewInitiator{ nil, types.ContinueAsNewInitiatorDecider.Ptr(), types.ContinueAsNewInitiatorRetryPolicy.Ptr(), types.ContinueAsNewInitiatorCronSchedule.Ptr(), } { assert.Equal(t, item, ToContinueAsNewInitiator(FromContinueAsNewInitiator(item))) } } func TestToContinueAsNewInitiator(t *testing.T) { cases := []struct { name string input apiv1.ContinueAsNewInitiator expected *types.ContinueAsNewInitiator }{ { name: "when input is invalid it should return nil", input: apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.ContinueAsNewInitiator(UnknownValue), expected: nil, }, { name: "when input is decider it should return the correctly mapped value", input: apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_DECIDER, expected: types.ContinueAsNewInitiatorDecider.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToContinueAsNewInitiator(tc.input)) }) } } func TestFromContinueAsNewInitiator(t *testing.T) { cases := []struct { name string input *types.ContinueAsNewInitiator expected apiv1.ContinueAsNewInitiator }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.ContinueAsNewInitiator(UnknownValue).Ptr(), expected: apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_INVALID, }, { name: "when input is decider it should return the correctly mapped value", input: types.ContinueAsNewInitiatorDecider.Ptr(), expected: apiv1.ContinueAsNewInitiator_CONTINUE_AS_NEW_INITIATOR_DECIDER, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromContinueAsNewInitiator(tc.input)) }) } } func TestCrossClusterTaskFailedCause(t *testing.T) { for _, item := range []*types.CrossClusterTaskFailedCause{ nil, types.CrossClusterTaskFailedCauseDomainNotActive.Ptr(), types.CrossClusterTaskFailedCauseDomainNotExists.Ptr(), types.CrossClusterTaskFailedCauseWorkflowAlreadyRunning.Ptr(), types.CrossClusterTaskFailedCauseWorkflowNotExists.Ptr(), types.CrossClusterTaskFailedCauseWorkflowAlreadyCompleted.Ptr(), types.CrossClusterTaskFailedCauseUncategorized.Ptr(), } { assert.Equal(t, item, ToCrossClusterTaskFailedCause(FromCrossClusterTaskFailedCause(item))) } } func TestToCrossClusterTaskFailedCause(t *testing.T) { cases := []struct { name string input adminv1.CrossClusterTaskFailedCause expected *types.CrossClusterTaskFailedCause }{ { name: "when input is invalid it should return nil", input: adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: adminv1.CrossClusterTaskFailedCause(UnknownValue), expected: nil, }, { name: "when input is domain not active it should return the correctly mapped value", input: adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_DOMAIN_NOT_ACTIVE, expected: types.CrossClusterTaskFailedCauseDomainNotActive.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToCrossClusterTaskFailedCause(tc.input)) }) } } func TestFromCrossClusterTaskFailedCause(t *testing.T) { cases := []struct { name string input *types.CrossClusterTaskFailedCause expected adminv1.CrossClusterTaskFailedCause }{ { name: "when input is nil it should return INVALID", input: nil, expected: adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.CrossClusterTaskFailedCause(UnknownValue).Ptr(), expected: adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_INVALID, }, { name: "when input is domain not active it should return the correctly mapped value", input: types.CrossClusterTaskFailedCauseDomainNotActive.Ptr(), expected: adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_DOMAIN_NOT_ACTIVE, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromCrossClusterTaskFailedCause(tc.input)) }) } } func TestDecisionTaskFailedCause(t *testing.T) { for _, item := range []*types.DecisionTaskFailedCause{ nil, types.DecisionTaskFailedCauseUnhandledDecision.Ptr(), types.DecisionTaskFailedCauseBadScheduleActivityAttributes.Ptr(), types.DecisionTaskFailedCauseBadRequestCancelActivityAttributes.Ptr(), types.DecisionTaskFailedCauseBadStartTimerAttributes.Ptr(), types.DecisionTaskFailedCauseBadCancelTimerAttributes.Ptr(), types.DecisionTaskFailedCauseBadRecordMarkerAttributes.Ptr(), types.DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseBadContinueAsNewAttributes.Ptr(), types.DecisionTaskFailedCauseStartTimerDuplicateID.Ptr(), types.DecisionTaskFailedCauseResetStickyTasklist.Ptr(), types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure.Ptr(), types.DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseBadStartChildExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseForceCloseDecision.Ptr(), types.DecisionTaskFailedCauseFailoverCloseDecision.Ptr(), types.DecisionTaskFailedCauseBadSignalInputSize.Ptr(), types.DecisionTaskFailedCauseResetWorkflow.Ptr(), types.DecisionTaskFailedCauseBadBinary.Ptr(), types.DecisionTaskFailedCauseScheduleActivityDuplicateID.Ptr(), types.DecisionTaskFailedCauseBadSearchAttributes.Ptr(), } { assert.Equal(t, item, ToDecisionTaskFailedCause(FromDecisionTaskFailedCause(item))) } } func TestToDecisionTaskFailedCause(t *testing.T) { cases := []struct { name string input apiv1.DecisionTaskFailedCause expected *types.DecisionTaskFailedCause }{ { name: "when input is invalid it should return nil", input: apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.DecisionTaskFailedCause(UnknownValue), expected: nil, }, { name: "when input is unhandled decision it should return the correctly mapped value", input: apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_UNHANDLED_DECISION, expected: types.DecisionTaskFailedCauseUnhandledDecision.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToDecisionTaskFailedCause(tc.input)) }) } } func TestFromDecisionTaskFailedCause(t *testing.T) { cases := []struct { name string input *types.DecisionTaskFailedCause expected apiv1.DecisionTaskFailedCause }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.DecisionTaskFailedCause(UnknownValue).Ptr(), expected: apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_INVALID, }, { name: "when input is unhandled decision it should return the correctly mapped value", input: types.DecisionTaskFailedCauseUnhandledDecision.Ptr(), expected: apiv1.DecisionTaskFailedCause_DECISION_TASK_FAILED_CAUSE_UNHANDLED_DECISION, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromDecisionTaskFailedCause(tc.input)) }) } } func TestDomainStatus(t *testing.T) { for _, item := range []*types.DomainStatus{ nil, types.DomainStatusRegistered.Ptr(), types.DomainStatusDeprecated.Ptr(), types.DomainStatusDeleted.Ptr(), } { assert.Equal(t, item, ToDomainStatus(FromDomainStatus(item))) } } func TestToDomainStatus(t *testing.T) { cases := []struct { name string input apiv1.DomainStatus expected *types.DomainStatus }{ { name: "when input is invalid it should return nil", input: apiv1.DomainStatus_DOMAIN_STATUS_INVALID, }, { name: "when input is out of range it should return nil", input: apiv1.DomainStatus(UnknownValue), }, { name: "when input is valid it should return the correctly mapped value", input: apiv1.DomainStatus_DOMAIN_STATUS_REGISTERED, expected: types.DomainStatusRegistered.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToDomainStatus(tc.input)) }) } } func TestFromDomainStatus(t *testing.T) { cases := []struct { name string input *types.DomainStatus expected apiv1.DomainStatus }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.DomainStatus_DOMAIN_STATUS_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.DomainStatus(UnknownValue).Ptr(), expected: apiv1.DomainStatus_DOMAIN_STATUS_INVALID, }, { name: "when input is valid it should return the correctly mapped value", input: types.DomainStatusRegistered.Ptr(), expected: apiv1.DomainStatus_DOMAIN_STATUS_REGISTERED, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromDomainStatus(tc.input)) }) } } func TestEncodingType(t *testing.T) { for _, item := range []*types.EncodingType{ nil, types.EncodingTypeThriftRW.Ptr(), types.EncodingTypeJSON.Ptr(), } { assert.Equal(t, item, ToEncodingType(FromEncodingType(item))) } } func TestToEncodingType(t *testing.T) { cases := []struct { name string input apiv1.EncodingType expected *types.EncodingType }{ { name: "when input is invalid it should return nil", input: apiv1.EncodingType_ENCODING_TYPE_INVALID, }, { name: "when input is out of range it should return nil", input: apiv1.EncodingType(UnknownValue), }, { name: "when input is valid it should return the correctly mapped value", input: apiv1.EncodingType_ENCODING_TYPE_THRIFTRW, expected: types.EncodingTypeThriftRW.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToEncodingType(tc.input)) }) } } func TestFromEncodingType(t *testing.T) { cases := []struct { name string input *types.EncodingType expected apiv1.EncodingType }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.EncodingType_ENCODING_TYPE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.EncodingType(UnknownValue).Ptr(), expected: apiv1.EncodingType_ENCODING_TYPE_INVALID, }, { name: "when input is valid it should return the correctly mapped value", input: types.EncodingTypeThriftRW.Ptr(), expected: apiv1.EncodingType_ENCODING_TYPE_THRIFTRW, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromEncodingType(tc.input)) }) } } func TestEventFilterType(t *testing.T) { for _, item := range []*types.HistoryEventFilterType{ nil, types.HistoryEventFilterTypeAllEvent.Ptr(), types.HistoryEventFilterTypeCloseEvent.Ptr(), } { assert.Equal(t, item, ToEventFilterType(FromEventFilterType(item))) } } func TestToEventFilterType(t *testing.T) { cases := []struct { name string input apiv1.EventFilterType expected *types.HistoryEventFilterType }{ { name: "when input is invalid it should return nil", input: apiv1.EventFilterType_EVENT_FILTER_TYPE_INVALID, }, { name: "when input is out of range it should return nil", input: apiv1.EventFilterType(UnknownValue), }, { name: "when input is valid it should return the correctly mapped value", input: apiv1.EventFilterType_EVENT_FILTER_TYPE_ALL_EVENT, expected: types.HistoryEventFilterTypeAllEvent.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToEventFilterType(tc.input)) }) } } func TestFromEventFilterType(t *testing.T) { cases := []struct { name string input *types.HistoryEventFilterType expected apiv1.EventFilterType }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.EventFilterType_EVENT_FILTER_TYPE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.HistoryEventFilterType(UnknownValue).Ptr(), expected: apiv1.EventFilterType_EVENT_FILTER_TYPE_INVALID, }, { name: "when input is valid it should return the correctly mapped value", input: types.HistoryEventFilterTypeAllEvent.Ptr(), expected: apiv1.EventFilterType_EVENT_FILTER_TYPE_ALL_EVENT, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromEventFilterType(tc.input)) }) } } func TestIndexedValueType(t *testing.T) { for _, item := range []types.IndexedValueType{ types.IndexedValueTypeString, types.IndexedValueTypeKeyword, types.IndexedValueTypeInt, types.IndexedValueTypeDouble, types.IndexedValueTypeBool, types.IndexedValueTypeDatetime, } { assert.Equal(t, item, ToIndexedValueType(FromIndexedValueType(item))) } } func TestToIndexedValueType(t *testing.T) { cases := []struct { name string input apiv1.IndexedValueType expected types.IndexedValueType }{ { name: "when input is invalid it should return string", input: apiv1.IndexedValueType_INDEXED_VALUE_TYPE_INVALID, expected: types.IndexedValueTypeString, }, { name: "when input is out of range it should return string", input: apiv1.IndexedValueType(UnknownValue), expected: types.IndexedValueTypeString, }, { name: "when input is valid it should return the correctly mapped value", input: apiv1.IndexedValueType_INDEXED_VALUE_TYPE_STRING, expected: types.IndexedValueTypeString, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToIndexedValueType(tc.input)) }) } } func TestFromIndexedValueType(t *testing.T) { cases := []struct { name string input types.IndexedValueType expected apiv1.IndexedValueType }{ { name: "when input is out of range it should return INVALID", input: types.IndexedValueType(UnknownValue), expected: apiv1.IndexedValueType_INDEXED_VALUE_TYPE_INVALID, }, { name: "when input is valid it should return the correctly mapped value", input: types.IndexedValueTypeString, expected: apiv1.IndexedValueType_INDEXED_VALUE_TYPE_STRING, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromIndexedValueType(tc.input)) }) } } func TestParentClosePolicy(t *testing.T) { for _, item := range []*types.ParentClosePolicy{ nil, types.ParentClosePolicyAbandon.Ptr(), types.ParentClosePolicyRequestCancel.Ptr(), types.ParentClosePolicyTerminate.Ptr(), } { assert.Equal(t, item, ToParentClosePolicy(FromParentClosePolicy(item))) } } func TestToParentClosePolicy(t *testing.T) { cases := []struct { name string input apiv1.ParentClosePolicy expected *types.ParentClosePolicy }{ { name: "when input is invalid it should return nil", input: apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.ParentClosePolicy(UnknownValue), expected: nil, }, { name: "when input is abandon it should return the correctly mapped value", input: apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_ABANDON, expected: types.ParentClosePolicyAbandon.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToParentClosePolicy(tc.input)) }) } } func TestFromParentClosePolicy(t *testing.T) { cases := []struct { name string input *types.ParentClosePolicy expected apiv1.ParentClosePolicy }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.ParentClosePolicy(UnknownValue).Ptr(), expected: apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_INVALID, }, { name: "when input is abandon it should return the correctly mapped value", input: types.ParentClosePolicyAbandon.Ptr(), expected: apiv1.ParentClosePolicy_PARENT_CLOSE_POLICY_ABANDON, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromParentClosePolicy(tc.input)) }) } } func TestPendingActivityState(t *testing.T) { for _, item := range []*types.PendingActivityState{ nil, types.PendingActivityStateScheduled.Ptr(), types.PendingActivityStateStarted.Ptr(), types.PendingActivityStateCancelRequested.Ptr(), } { assert.Equal(t, item, ToPendingActivityState(FromPendingActivityState(item))) } } func TestToPendingActivityState(t *testing.T) { cases := []struct { name string input apiv1.PendingActivityState expected *types.PendingActivityState }{ { name: "when input is invalid it should return nil", input: apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.PendingActivityState(UnknownValue), expected: nil, }, { name: "when input is scheduled it should return the correctly mapped value", input: apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_SCHEDULED, expected: types.PendingActivityStateScheduled.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToPendingActivityState(tc.input)) }) } } func TestFromPendingActivityState(t *testing.T) { cases := []struct { name string input *types.PendingActivityState expected apiv1.PendingActivityState }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.PendingActivityState(UnknownValue).Ptr(), expected: apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_INVALID, }, { name: "when input is scheduled it should return the correctly mapped value", input: types.PendingActivityStateScheduled.Ptr(), expected: apiv1.PendingActivityState_PENDING_ACTIVITY_STATE_SCHEDULED, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromPendingActivityState(tc.input)) }) } } func TestPendingDecisionState(t *testing.T) { for _, item := range []*types.PendingDecisionState{ nil, types.PendingDecisionStateScheduled.Ptr(), types.PendingDecisionStateStarted.Ptr(), } { assert.Equal(t, item, ToPendingDecisionState(FromPendingDecisionState(item))) } } func TestToPendingDecisionState(t *testing.T) { cases := []struct { name string input apiv1.PendingDecisionState expected *types.PendingDecisionState }{ { name: "when input is invalid it should return nil", input: apiv1.PendingDecisionState_PENDING_DECISION_STATE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.PendingDecisionState(UnknownValue), expected: nil, }, { name: "when input is scheduled it should return the correctly mapped value", input: apiv1.PendingDecisionState_PENDING_DECISION_STATE_SCHEDULED, expected: types.PendingDecisionStateScheduled.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToPendingDecisionState(tc.input)) }) } } func TestFromPendingDecisionState(t *testing.T) { cases := []struct { name string input *types.PendingDecisionState expected apiv1.PendingDecisionState }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.PendingDecisionState_PENDING_DECISION_STATE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.PendingDecisionState(UnknownValue).Ptr(), expected: apiv1.PendingDecisionState_PENDING_DECISION_STATE_INVALID, }, { name: "when input is scheduled it should return the correctly mapped value", input: types.PendingDecisionStateScheduled.Ptr(), expected: apiv1.PendingDecisionState_PENDING_DECISION_STATE_SCHEDULED, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromPendingDecisionState(tc.input)) }) } } func TestQueryConsistencyLevel(t *testing.T) { for _, item := range []*types.QueryConsistencyLevel{ nil, types.QueryConsistencyLevelEventual.Ptr(), types.QueryConsistencyLevelStrong.Ptr(), } { assert.Equal(t, item, ToQueryConsistencyLevel(FromQueryConsistencyLevel(item))) } } func TestToQueryConsistencyLevel(t *testing.T) { cases := []struct { name string input apiv1.QueryConsistencyLevel expected *types.QueryConsistencyLevel }{ { name: "when input is invalid it should return nil", input: apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.QueryConsistencyLevel(UnknownValue), expected: nil, }, { name: "when input is eventual it should return the correctly mapped value", input: apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_EVENTUAL, expected: types.QueryConsistencyLevelEventual.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToQueryConsistencyLevel(tc.input)) }) } } func TestFromQueryConsistencyLevel(t *testing.T) { cases := []struct { name string input *types.QueryConsistencyLevel expected apiv1.QueryConsistencyLevel }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.QueryConsistencyLevel(UnknownValue).Ptr(), expected: apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_INVALID, }, { name: "when input is eventual it should return the correctly mapped value", input: types.QueryConsistencyLevelEventual.Ptr(), expected: apiv1.QueryConsistencyLevel_QUERY_CONSISTENCY_LEVEL_EVENTUAL, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromQueryConsistencyLevel(tc.input)) }) } } func TestQueryRejectCondition(t *testing.T) { for _, item := range []*types.QueryRejectCondition{ nil, types.QueryRejectConditionNotOpen.Ptr(), types.QueryRejectConditionNotCompletedCleanly.Ptr(), } { assert.Equal(t, item, ToQueryRejectCondition(FromQueryRejectCondition(item))) } } func TestToQueryRejectCondition(t *testing.T) { cases := []struct { name string input apiv1.QueryRejectCondition expected *types.QueryRejectCondition }{ { name: "when input is invalid it should return nil", input: apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.QueryRejectCondition(UnknownValue), expected: nil, }, { name: "when input is not open it should return the correctly mapped value", input: apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_NOT_OPEN, expected: types.QueryRejectConditionNotOpen.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToQueryRejectCondition(tc.input)) }) } } func TestFromQueryRejectCondition(t *testing.T) { cases := []struct { name string input *types.QueryRejectCondition expected apiv1.QueryRejectCondition }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.QueryRejectCondition(UnknownValue).Ptr(), expected: apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_INVALID, }, { name: "when input is not open it should return the correctly mapped value", input: types.QueryRejectConditionNotOpen.Ptr(), expected: apiv1.QueryRejectCondition_QUERY_REJECT_CONDITION_NOT_OPEN, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromQueryRejectCondition(tc.input)) }) } } func TestQueryResultType(t *testing.T) { for _, item := range []*types.QueryResultType{ nil, types.QueryResultTypeAnswered.Ptr(), types.QueryResultTypeFailed.Ptr(), } { assert.Equal(t, item, ToQueryResultType(FromQueryResultType(item))) } } func TestToQueryResultType(t *testing.T) { cases := []struct { name string input apiv1.QueryResultType expected *types.QueryResultType }{ { name: "when input is invalid it should return nil", input: apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.QueryResultType(UnknownValue), expected: nil, }, { name: "when input is answered it should return the correctly mapped value", input: apiv1.QueryResultType_QUERY_RESULT_TYPE_ANSWERED, expected: types.QueryResultTypeAnswered.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToQueryResultType(tc.input)) }) } } func TestFromQueryResultType(t *testing.T) { cases := []struct { name string input *types.QueryResultType expected apiv1.QueryResultType }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.QueryResultType(UnknownValue).Ptr(), expected: apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID, }, { name: "when input is answered it should return the correctly mapped value", input: types.QueryResultTypeAnswered.Ptr(), expected: apiv1.QueryResultType_QUERY_RESULT_TYPE_ANSWERED, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromQueryResultType(tc.input)) }) } } func TestQueryTaskCompletedType(t *testing.T) { for _, item := range []*types.QueryTaskCompletedType{ nil, types.QueryTaskCompletedTypeCompleted.Ptr(), types.QueryTaskCompletedTypeFailed.Ptr(), } { assert.Equal(t, item, ToQueryTaskCompletedType(FromQueryTaskCompletedType(item))) } } func TestToQueryTaskCompletedType(t *testing.T) { cases := []struct { name string input apiv1.QueryResultType expected *types.QueryTaskCompletedType }{ { name: "when input is invalid it should return nil", input: apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.QueryResultType(UnknownValue), expected: nil, }, { name: "when input is completed it should return the correctly mapped value", input: apiv1.QueryResultType_QUERY_RESULT_TYPE_ANSWERED, expected: types.QueryTaskCompletedTypeCompleted.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToQueryTaskCompletedType(tc.input)) }) } } func TestFromQueryTaskCompletedType(t *testing.T) { cases := []struct { name string input *types.QueryTaskCompletedType expected apiv1.QueryResultType }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.QueryTaskCompletedType(UnknownValue).Ptr(), expected: apiv1.QueryResultType_QUERY_RESULT_TYPE_INVALID, }, { name: "when input is completed it should return the correctly mapped value", input: types.QueryTaskCompletedTypeCompleted.Ptr(), expected: apiv1.QueryResultType_QUERY_RESULT_TYPE_ANSWERED, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromQueryTaskCompletedType(tc.input)) }) } } func TestSignalExternalWorkflowExecutionFailedCause(t *testing.T) { for _, item := range []*types.SignalExternalWorkflowExecutionFailedCause{ nil, types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr(), types.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted.Ptr(), } { assert.Equal(t, item, ToSignalExternalWorkflowExecutionFailedCause(FromSignalExternalWorkflowExecutionFailedCause(item))) } } func TestToSignalExternalWorkflowExecutionFailedCause(t *testing.T) { cases := []struct { name string input apiv1.SignalExternalWorkflowExecutionFailedCause expected *types.SignalExternalWorkflowExecutionFailedCause }{ { name: "when input is invalid it should return nil", input: apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.SignalExternalWorkflowExecutionFailedCause(UnknownValue), expected: nil, }, { name: "when input is unknown external workflow execution it should return the correctly mapped value", input: apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION, expected: types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToSignalExternalWorkflowExecutionFailedCause(tc.input)) }) } } func TestFromSignalExternalWorkflowExecutionFailedCause(t *testing.T) { cases := []struct { name string input *types.SignalExternalWorkflowExecutionFailedCause expected apiv1.SignalExternalWorkflowExecutionFailedCause }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.SignalExternalWorkflowExecutionFailedCause(UnknownValue).Ptr(), expected: apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_INVALID, }, { name: "when input is unknown external workflow execution it should return the correctly mapped value", input: types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr(), expected: apiv1.SignalExternalWorkflowExecutionFailedCause_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromSignalExternalWorkflowExecutionFailedCause(tc.input)) }) } } func TestTaskListKind(t *testing.T) { for _, item := range []*types.TaskListKind{ nil, types.TaskListKindNormal.Ptr(), types.TaskListKindSticky.Ptr(), types.TaskListKindEphemeral.Ptr(), } { assert.Equal(t, item, ToTaskListKind(FromTaskListKind(item))) } } func TestToTaskListKind(t *testing.T) { cases := []struct { name string input apiv1.TaskListKind expected *types.TaskListKind }{ { name: "when input is invalid it should return nil", input: apiv1.TaskListKind_TASK_LIST_KIND_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.TaskListKind(UnknownValue), expected: nil, }, { name: "when input is normal it should return the correctly mapped value", input: apiv1.TaskListKind_TASK_LIST_KIND_NORMAL, expected: types.TaskListKindNormal.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToTaskListKind(tc.input)) }) } } func TestFromTaskListKind(t *testing.T) { cases := []struct { name string input *types.TaskListKind expected apiv1.TaskListKind }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.TaskListKind_TASK_LIST_KIND_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.TaskListKind(UnknownValue).Ptr(), expected: apiv1.TaskListKind_TASK_LIST_KIND_INVALID, }, { name: "when input is normal it should return the correctly mapped value", input: types.TaskListKindNormal.Ptr(), expected: apiv1.TaskListKind_TASK_LIST_KIND_NORMAL, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromTaskListKind(tc.input)) }) } } func TestTaskListType(t *testing.T) { for _, item := range []*types.TaskListType{ nil, types.TaskListTypeDecision.Ptr(), types.TaskListTypeActivity.Ptr(), } { assert.Equal(t, item, ToTaskListType(FromTaskListType(item))) } } func TestToTaskListType(t *testing.T) { cases := []struct { name string input apiv1.TaskListType expected *types.TaskListType }{ { name: "when input is invalid it should return nil", input: apiv1.TaskListType_TASK_LIST_TYPE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.TaskListType(UnknownValue), expected: nil, }, { name: "when input is decision it should return the correctly mapped value", input: apiv1.TaskListType_TASK_LIST_TYPE_DECISION, expected: types.TaskListTypeDecision.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToTaskListType(tc.input)) }) } } func TestFromTaskListType(t *testing.T) { cases := []struct { name string input *types.TaskListType expected apiv1.TaskListType }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.TaskListType_TASK_LIST_TYPE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.TaskListType(UnknownValue).Ptr(), expected: apiv1.TaskListType_TASK_LIST_TYPE_INVALID, }, { name: "when input is decision it should return the correctly mapped value", input: types.TaskListTypeDecision.Ptr(), expected: apiv1.TaskListType_TASK_LIST_TYPE_DECISION, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromTaskListType(tc.input)) }) } } func TestTimeoutType(t *testing.T) { for _, item := range []*types.TimeoutType{ nil, types.TimeoutTypeStartToClose.Ptr(), types.TimeoutTypeScheduleToStart.Ptr(), types.TimeoutTypeScheduleToClose.Ptr(), types.TimeoutTypeHeartbeat.Ptr(), } { assert.Equal(t, item, ToTimeoutType(FromTimeoutType(item))) } } func TestToTimeoutType(t *testing.T) { cases := []struct { name string input apiv1.TimeoutType expected *types.TimeoutType }{ { name: "when input is invalid it should return nil", input: apiv1.TimeoutType_TIMEOUT_TYPE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.TimeoutType(UnknownValue), expected: nil, }, { name: "when input is start to close it should return the correctly mapped value", input: apiv1.TimeoutType_TIMEOUT_TYPE_START_TO_CLOSE, expected: types.TimeoutTypeStartToClose.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToTimeoutType(tc.input)) }) } } func TestFromTimeoutType(t *testing.T) { cases := []struct { name string input *types.TimeoutType expected apiv1.TimeoutType }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.TimeoutType_TIMEOUT_TYPE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.TimeoutType(UnknownValue).Ptr(), expected: apiv1.TimeoutType_TIMEOUT_TYPE_INVALID, }, { name: "when input is start to close it should return the correctly mapped value", input: types.TimeoutTypeStartToClose.Ptr(), expected: apiv1.TimeoutType_TIMEOUT_TYPE_START_TO_CLOSE, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromTimeoutType(tc.input)) }) } } func TestDecisionTaskTimedOutCause(t *testing.T) { for _, item := range []*types.DecisionTaskTimedOutCause{ nil, types.DecisionTaskTimedOutCauseTimeout.Ptr(), types.DecisionTaskTimedOutCauseReset.Ptr(), } { assert.Equal(t, item, ToDecisionTaskTimedOutCause(FromDecisionTaskTimedOutCause(item))) } } func TestToDecisionTaskTimedOutCause(t *testing.T) { cases := []struct { name string input apiv1.DecisionTaskTimedOutCause expected *types.DecisionTaskTimedOutCause }{ { name: "when input is invalid it should return nil", input: apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.DecisionTaskTimedOutCause(UnknownValue), expected: nil, }, { name: "when input is timeout it should return the correctly mapped value", input: apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_TIMEOUT, expected: types.DecisionTaskTimedOutCauseTimeout.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToDecisionTaskTimedOutCause(tc.input)) }) } } func TestFromDecisionTaskTimedOutCause(t *testing.T) { cases := []struct { name string input *types.DecisionTaskTimedOutCause expected apiv1.DecisionTaskTimedOutCause }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.DecisionTaskTimedOutCause(UnknownValue).Ptr(), expected: apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_INVALID, }, { name: "when input is timeout it should return the correctly mapped value", input: types.DecisionTaskTimedOutCauseTimeout.Ptr(), expected: apiv1.DecisionTaskTimedOutCause_DECISION_TASK_TIMED_OUT_CAUSE_TIMEOUT, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromDecisionTaskTimedOutCause(tc.input)) }) } } func TestWorkflowExecutionCloseStatus(t *testing.T) { for _, item := range []*types.WorkflowExecutionCloseStatus{ nil, types.WorkflowExecutionCloseStatusCompleted.Ptr(), types.WorkflowExecutionCloseStatusFailed.Ptr(), types.WorkflowExecutionCloseStatusCanceled.Ptr(), types.WorkflowExecutionCloseStatusTerminated.Ptr(), types.WorkflowExecutionCloseStatusContinuedAsNew.Ptr(), types.WorkflowExecutionCloseStatusTimedOut.Ptr(), } { assert.Equal(t, item, ToWorkflowExecutionCloseStatus(FromWorkflowExecutionCloseStatus(item))) } } func TestToWorkflowExecutionCloseStatus(t *testing.T) { cases := []struct { name string input apiv1.WorkflowExecutionCloseStatus expected *types.WorkflowExecutionCloseStatus }{ { name: "when input is invalid it should return nil", input: apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.WorkflowExecutionCloseStatus(UnknownValue), expected: nil, }, { name: "when input is completed it should return the correctly mapped value", input: apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_COMPLETED, expected: types.WorkflowExecutionCloseStatusCompleted.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToWorkflowExecutionCloseStatus(tc.input)) }) } } func TestFromWorkflowExecutionCloseStatus(t *testing.T) { cases := []struct { name string input *types.WorkflowExecutionCloseStatus expected apiv1.WorkflowExecutionCloseStatus }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.WorkflowExecutionCloseStatus(UnknownValue).Ptr(), expected: apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID, }, { name: "when input is completed it should return the correctly mapped value", input: types.WorkflowExecutionCloseStatusCompleted.Ptr(), expected: apiv1.WorkflowExecutionCloseStatus_WORKFLOW_EXECUTION_CLOSE_STATUS_COMPLETED, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromWorkflowExecutionCloseStatus(tc.input)) }) } } func TestWorkflowIDReusePolicy(t *testing.T) { for _, item := range []*types.WorkflowIDReusePolicy{ nil, types.WorkflowIDReusePolicyAllowDuplicateFailedOnly.Ptr(), types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), types.WorkflowIDReusePolicyRejectDuplicate.Ptr(), types.WorkflowIDReusePolicyTerminateIfRunning.Ptr(), } { assert.Equal(t, item, ToWorkflowIDReusePolicy(FromWorkflowIDReusePolicy(item))) } } func TestToWorkflowIDReusePolicy(t *testing.T) { cases := []struct { name string input apiv1.WorkflowIdReusePolicy expected *types.WorkflowIDReusePolicy }{ { name: "when input is invalid it should return nil", input: apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: apiv1.WorkflowIdReusePolicy(UnknownValue), expected: nil, }, { name: "when input is allow duplicate failed only it should return the correctly mapped value", input: apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY, expected: types.WorkflowIDReusePolicyAllowDuplicateFailedOnly.Ptr(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToWorkflowIDReusePolicy(tc.input)) }) } } func TestFromWorkflowIDReusePolicy(t *testing.T) { cases := []struct { name string input *types.WorkflowIDReusePolicy expected apiv1.WorkflowIdReusePolicy }{ { name: "when input is nil it should return INVALID", input: nil, expected: apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_INVALID, }, { name: "when input is out of range it should return INVALID", input: types.WorkflowIDReusePolicy(UnknownValue).Ptr(), expected: apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_INVALID, }, { name: "when input is allow duplicate failed only it should return the correctly mapped value", input: types.WorkflowIDReusePolicyAllowDuplicateFailedOnly.Ptr(), expected: apiv1.WorkflowIdReusePolicy_WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromWorkflowIDReusePolicy(tc.input)) }) } } func TestWorkflowState(t *testing.T) { for _, item := range []*int32{ nil, common.Int32Ptr(persistence.WorkflowStateCreated), common.Int32Ptr(persistence.WorkflowStateRunning), common.Int32Ptr(persistence.WorkflowStateCompleted), common.Int32Ptr(persistence.WorkflowStateZombie), common.Int32Ptr(persistence.WorkflowStateVoid), common.Int32Ptr(persistence.WorkflowStateCorrupted), } { assert.Equal(t, item, ToWorkflowState(FromWorkflowState(item))) } } func TestToWorkflowState(t *testing.T) { cases := []struct { name string input sharedv1.WorkflowState expected *int32 }{ { name: "when input is invalid it should return nil", input: sharedv1.WorkflowState_WORKFLOW_STATE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: sharedv1.WorkflowState(UnknownValue), expected: nil, }, { name: "when input is created it should return the correctly mapped value", input: sharedv1.WorkflowState_WORKFLOW_STATE_CREATED, expected: common.Int32Ptr(persistence.WorkflowStateCreated), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToWorkflowState(tc.input)) }) } } func TestFromWorkflowState(t *testing.T) { cases := []struct { name string input *int32 expected sharedv1.WorkflowState }{ { name: "when input is nil it should return INVALID", input: nil, expected: sharedv1.WorkflowState_WORKFLOW_STATE_INVALID, }, { name: "when input is out of range it should return INVALID", input: common.Int32Ptr(UnknownValue), expected: sharedv1.WorkflowState_WORKFLOW_STATE_INVALID, }, { name: "when input is created it should return the correctly mapped value", input: common.Int32Ptr(persistence.WorkflowStateCreated), expected: sharedv1.WorkflowState_WORKFLOW_STATE_CREATED, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromWorkflowState(tc.input)) }) } } func TestTaskType(t *testing.T) { for _, item := range []*int32{ nil, common.Int32Ptr(int32(constants.TaskTypeTransfer)), common.Int32Ptr(int32(constants.TaskTypeTimer)), common.Int32Ptr(int32(constants.TaskTypeReplication)), common.Int32Ptr(int32(constants.TaskTypeCrossCluster)), } { assert.Equal(t, item, ToTaskType(FromTaskType(item))) } } func TestToTaskType(t *testing.T) { cases := []struct { name string input adminv1.TaskType expected *int32 }{ { name: "when input is invalid it should return nil", input: adminv1.TaskType_TASK_TYPE_INVALID, expected: nil, }, { name: "when input is out of range it should return nil", input: adminv1.TaskType(UnknownValue), expected: nil, }, { name: "when input is transfer it should return the correctly mapped value", input: adminv1.TaskType_TASK_TYPE_TRANSFER, expected: common.Int32Ptr(int32(constants.TaskTypeTransfer)), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, ToTaskType(tc.input)) }) } } func TestFromTaskType(t *testing.T) { cases := []struct { name string input *int32 expected adminv1.TaskType }{ { name: "when input is nil it should return INVALID", input: nil, expected: adminv1.TaskType_TASK_TYPE_INVALID, }, { name: "when input is out of range it should return INVALID", input: common.Int32Ptr(UnknownValue), expected: adminv1.TaskType_TASK_TYPE_INVALID, }, { name: "when input is transfer it should return the correctly mapped value", input: common.Int32Ptr(int32(constants.TaskTypeTransfer)), expected: adminv1.TaskType_TASK_TYPE_TRANSFER, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, FromTaskType(tc.input)) }) } } ================================================ FILE: common/types/mapper/proto/errors.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc/encoding/protobuf" "go.uber.org/yarpc/yarpcerrors" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" sharedv1 "github.com/uber/cadence/.gen/proto/shared/v1" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/errorutils" ) func FromError(err error) error { if err == nil { return protobuf.NewError(yarpcerrors.CodeOK, "") } var ( ok bool typedErr error ) if ok, typedErr = errorutils.ConvertError(err, fromAccessDeniedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromInternalServiceError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromEntityNotExistsError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromWorkflowExecutionAlreadyCompletedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromBadRequestError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromQueryFailedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromShardOwnershipLostError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromTaskListNotOwnedByHostError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromCurrentBranchChangedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromRetryTaskV2Error); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromCancellationAlreadyRequestedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromDomainAlreadyExistsError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromEventAlreadyStartedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromWorkflowExecutionAlreadyStartedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromClientVersionNotSupportedErr); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromFeatureNotEnabledErr); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromDomainNotActive); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromInternalDataInconsistencyErr); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromLimitExceededErr); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromServiceBusyErr); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromRemoteSyncMatchedErr); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromStickyWorkerUnavailableErr); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromReadOnlyPartitionErr); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromNamespaceNotFoundErr); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, fromShardNotFoundErr); ok { return typedErr } return protobuf.NewError(yarpcerrors.CodeUnknown, err.Error()) } func ToError(err error) error { status := yarpcerrors.FromError(err) if status == nil || status.Code() == yarpcerrors.CodeOK { return nil } switch status.Code() { case yarpcerrors.CodePermissionDenied: return &types.AccessDeniedError{ Message: status.Message(), } case yarpcerrors.CodeInternal: return &types.InternalServiceError{ Message: status.Message(), } case yarpcerrors.CodeNotFound: switch details := getErrorDetails(err).(type) { case *apiv1.EntityNotExistsError: if details != nil { return &types.EntityNotExistsError{ Message: status.Message(), CurrentCluster: details.CurrentCluster, ActiveCluster: details.ActiveCluster, ActiveClusters: details.ActiveClusters, } } return &types.EntityNotExistsError{ Message: status.Message(), } case *apiv1.WorkflowExecutionAlreadyCompletedError: return &types.WorkflowExecutionAlreadyCompletedError{ Message: status.Message(), } case *sharddistributorv1.NamespaceNotFoundError: if details != nil { return &types.NamespaceNotFoundError{ Namespace: details.Namespace, } } case *sharddistributorv1.ShardNotFoundError: if details != nil { return &types.ShardNotFoundError{ Namespace: details.Namespace, ShardKey: details.ShardKey, } } } case yarpcerrors.CodeInvalidArgument: switch getErrorDetails(err).(type) { case nil: return &types.BadRequestError{ Message: status.Message(), } case *apiv1.QueryFailedError: return &types.QueryFailedError{ Message: status.Message(), } } case yarpcerrors.CodeAborted: switch details := getErrorDetails(err).(type) { case *sharedv1.ShardOwnershipLostError: if details != nil { return &types.ShardOwnershipLostError{ Message: status.Message(), Owner: details.Owner, } } case *sharedv1.TaskListNotOwnedByHostError: if details != nil { return &cadence_errors.TaskListNotOwnedByHostError{ OwnedByIdentity: details.OwnedByIdentity, MyIdentity: details.MyIdentity, TasklistName: details.TaskListName, } } case *sharedv1.CurrentBranchChangedError: if details != nil { return &types.CurrentBranchChangedError{ Message: status.Message(), CurrentBranchToken: details.CurrentBranchToken, } } case *sharedv1.RetryTaskV2Error: if details != nil { return &types.RetryTaskV2Error{ Message: status.Message(), DomainID: details.DomainId, WorkflowID: ToWorkflowID(details.WorkflowExecution), RunID: ToRunID(details.WorkflowExecution), StartEventID: ToEventID(details.StartEvent), StartEventVersion: ToEventVersion(details.StartEvent), EndEventID: ToEventID(details.EndEvent), EndEventVersion: ToEventVersion(details.EndEvent), } } case *apiv1.ReadOnlyPartitionError: return &types.ReadOnlyPartitionError{ Message: status.Message(), } } case yarpcerrors.CodeAlreadyExists: switch details := getErrorDetails(err).(type) { case *apiv1.CancellationAlreadyRequestedError: return &types.CancellationAlreadyRequestedError{ Message: status.Message(), } case *apiv1.DomainAlreadyExistsError: return &types.DomainAlreadyExistsError{ Message: status.Message(), } case *sharedv1.EventAlreadyStartedError: return &types.EventAlreadyStartedError{ Message: status.Message(), } case *apiv1.WorkflowExecutionAlreadyStartedError: if details != nil { return &types.WorkflowExecutionAlreadyStartedError{ Message: status.Message(), StartRequestID: details.StartRequestId, RunID: details.RunId, } } } case yarpcerrors.CodeDataLoss: return &types.InternalDataInconsistencyError{ Message: status.Message(), } case yarpcerrors.CodeFailedPrecondition: switch details := getErrorDetails(err).(type) { case *apiv1.ClientVersionNotSupportedError: if details != nil { return &types.ClientVersionNotSupportedError{ FeatureVersion: details.FeatureVersion, ClientImpl: details.ClientImpl, SupportedVersions: details.SupportedVersions, } } case *apiv1.FeatureNotEnabledError: if details != nil { return &types.FeatureNotEnabledError{ FeatureFlag: details.FeatureFlag, } } case *apiv1.DomainNotActiveError: if details != nil { return &types.DomainNotActiveError{ Message: status.Message(), DomainName: details.Domain, CurrentCluster: details.CurrentCluster, ActiveCluster: details.ActiveCluster, ActiveClusters: details.ActiveClusters, } } } case yarpcerrors.CodeResourceExhausted: switch details := getErrorDetails(err).(type) { case *apiv1.LimitExceededError: return &types.LimitExceededError{ Message: status.Message(), } case *apiv1.ServiceBusyError: if details != nil { return &types.ServiceBusyError{ Message: status.Message(), Reason: details.Reason, } } } case yarpcerrors.CodeUnavailable: switch getErrorDetails(err).(type) { case *sharedv1.RemoteSyncMatchedError: return &types.RemoteSyncMatchedError{ Message: status.Message(), } case *apiv1.StickyWorkerUnavailableError: return &types.StickyWorkerUnavailableError{ Message: status.Message(), } } } // If error does not match anything, return raw yarpc status error // There are some code that casts error to yarpc status to check for deadline exceeded status return status } func getErrorDetails(err error) interface{} { details := protobuf.GetErrorDetails(err) if len(details) > 0 { return details[0] } return nil } func fromAccessDeniedError(e *types.AccessDeniedError) error { return protobuf.NewError(yarpcerrors.CodePermissionDenied, e.Message) } func fromInternalServiceError(e *types.InternalServiceError) error { return protobuf.NewError(yarpcerrors.CodeInternal, e.Message) } func fromEntityNotExistsError(e *types.EntityNotExistsError) error { return protobuf.NewError(yarpcerrors.CodeNotFound, e.Message, protobuf.WithErrorDetails(&apiv1.EntityNotExistsError{ CurrentCluster: e.CurrentCluster, ActiveCluster: e.ActiveCluster, ActiveClusters: e.ActiveClusters, })) } func fromWorkflowExecutionAlreadyCompletedError(e *types.WorkflowExecutionAlreadyCompletedError) error { return protobuf.NewError(yarpcerrors.CodeNotFound, e.Message, protobuf.WithErrorDetails(&apiv1.WorkflowExecutionAlreadyCompletedError{})) } func fromBadRequestError(e *types.BadRequestError) error { return protobuf.NewError(yarpcerrors.CodeInvalidArgument, e.Message) } func fromQueryFailedError(e *types.QueryFailedError) error { return protobuf.NewError(yarpcerrors.CodeInvalidArgument, e.Message, protobuf.WithErrorDetails(&apiv1.QueryFailedError{})) } func fromShardOwnershipLostError(e *types.ShardOwnershipLostError) error { return protobuf.NewError(yarpcerrors.CodeAborted, e.Message, protobuf.WithErrorDetails(&sharedv1.ShardOwnershipLostError{ Owner: e.Owner, })) } func fromTaskListNotOwnedByHostError(e *cadence_errors.TaskListNotOwnedByHostError) error { return protobuf.NewError(yarpcerrors.CodeAborted, e.Error(), protobuf.WithErrorDetails(&sharedv1.TaskListNotOwnedByHostError{ OwnedByIdentity: e.OwnedByIdentity, MyIdentity: e.MyIdentity, TaskListName: e.TasklistName, })) } func fromCurrentBranchChangedError(e *types.CurrentBranchChangedError) error { return protobuf.NewError(yarpcerrors.CodeAborted, e.Message, protobuf.WithErrorDetails(&sharedv1.CurrentBranchChangedError{ CurrentBranchToken: e.GetCurrentBranchToken(), })) } func fromRetryTaskV2Error(e *types.RetryTaskV2Error) error { return protobuf.NewError(yarpcerrors.CodeAborted, e.Message, protobuf.WithErrorDetails(&sharedv1.RetryTaskV2Error{ DomainId: e.DomainID, WorkflowExecution: FromWorkflowRunPair(e.WorkflowID, e.RunID), StartEvent: FromEventIDVersionPair(e.StartEventID, e.StartEventVersion), EndEvent: FromEventIDVersionPair(e.EndEventID, e.EndEventVersion), })) } func fromCancellationAlreadyRequestedError(e *types.CancellationAlreadyRequestedError) error { return protobuf.NewError(yarpcerrors.CodeAlreadyExists, e.Message, protobuf.WithErrorDetails(&apiv1.CancellationAlreadyRequestedError{})) } func fromDomainAlreadyExistsError(e *types.DomainAlreadyExistsError) error { return protobuf.NewError(yarpcerrors.CodeAlreadyExists, e.Message, protobuf.WithErrorDetails(&apiv1.DomainAlreadyExistsError{})) } func fromEventAlreadyStartedError(e *types.EventAlreadyStartedError) error { return protobuf.NewError(yarpcerrors.CodeAlreadyExists, e.Message, protobuf.WithErrorDetails(&sharedv1.EventAlreadyStartedError{})) } func fromWorkflowExecutionAlreadyStartedError(e *types.WorkflowExecutionAlreadyStartedError) error { return protobuf.NewError(yarpcerrors.CodeAlreadyExists, e.Message, protobuf.WithErrorDetails(&apiv1.WorkflowExecutionAlreadyStartedError{ StartRequestId: e.StartRequestID, RunId: e.RunID, })) } func fromClientVersionNotSupportedErr(e *types.ClientVersionNotSupportedError) error { return protobuf.NewError(yarpcerrors.CodeFailedPrecondition, "Client version not supported", protobuf.WithErrorDetails(&apiv1.ClientVersionNotSupportedError{ FeatureVersion: e.FeatureVersion, ClientImpl: e.ClientImpl, SupportedVersions: e.SupportedVersions, })) } func fromFeatureNotEnabledErr(e *types.FeatureNotEnabledError) error { return protobuf.NewError(yarpcerrors.CodeFailedPrecondition, "Feature flag not enabled", protobuf.WithErrorDetails(&apiv1.FeatureNotEnabledError{ FeatureFlag: e.FeatureFlag, })) } func fromDomainNotActive(e *types.DomainNotActiveError) error { return protobuf.NewError(yarpcerrors.CodeFailedPrecondition, e.Message, protobuf.WithErrorDetails(&apiv1.DomainNotActiveError{ Domain: e.DomainName, CurrentCluster: e.CurrentCluster, ActiveCluster: e.ActiveCluster, ActiveClusters: e.ActiveClusters, })) } func fromInternalDataInconsistencyErr(e *types.InternalDataInconsistencyError) error { return protobuf.NewError(yarpcerrors.CodeDataLoss, e.Message, protobuf.WithErrorDetails(&sharedv1.InternalDataInconsistencyError{})) } func fromLimitExceededErr(e *types.LimitExceededError) error { return protobuf.NewError(yarpcerrors.CodeResourceExhausted, e.Message, protobuf.WithErrorDetails(&apiv1.LimitExceededError{})) } func fromServiceBusyErr(e *types.ServiceBusyError) error { return protobuf.NewError(yarpcerrors.CodeResourceExhausted, e.Message, protobuf.WithErrorDetails(&apiv1.ServiceBusyError{ Reason: e.Reason, })) } func fromRemoteSyncMatchedErr(e *types.RemoteSyncMatchedError) error { return protobuf.NewError(yarpcerrors.CodeUnavailable, e.Message, protobuf.WithErrorDetails(&sharedv1.RemoteSyncMatchedError{})) } func fromStickyWorkerUnavailableErr(e *types.StickyWorkerUnavailableError) error { return protobuf.NewError(yarpcerrors.CodeUnavailable, e.Message, protobuf.WithErrorDetails(&apiv1.StickyWorkerUnavailableError{})) } func fromReadOnlyPartitionErr(e *types.ReadOnlyPartitionError) error { return protobuf.NewError(yarpcerrors.CodeAborted, e.Message, protobuf.WithErrorDetails(&apiv1.ReadOnlyPartitionError{})) } func fromNamespaceNotFoundErr(e *types.NamespaceNotFoundError) error { return protobuf.NewError(yarpcerrors.CodeNotFound, e.Error(), protobuf.WithErrorDetails(&sharddistributorv1.NamespaceNotFoundError{ Namespace: e.Namespace, })) } func fromShardNotFoundErr(e *types.ShardNotFoundError) error { return protobuf.NewError(yarpcerrors.CodeNotFound, e.Error(), protobuf.WithErrorDetails(&sharddistributorv1.ShardNotFoundError{ Namespace: e.Namespace, ShardKey: e.ShardKey, })) } ================================================ FILE: common/types/mapper/proto/errors_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "errors" "reflect" "testing" "github.com/stretchr/testify/assert" "go.uber.org/yarpc/yarpcerrors" "github.com/uber/cadence/common/types/testdata" ) func TestErrors(t *testing.T) { for _, err := range testdata.Errors { name := reflect.TypeOf(err).Elem().Name() t.Run(name, func(t *testing.T) { // Test that the mappings does not lose information assert.Equal(t, err, ToError(FromError(err))) }) } } func TestNilMapsToOK(t *testing.T) { protoNoError := FromError(nil) assert.Equal(t, yarpcerrors.CodeOK, yarpcerrors.FromError(protoNoError).Code()) assert.Nil(t, ToError(protoNoError)) } func TestFromUnknownErrorMapsToUnknownError(t *testing.T) { err := errors.New("unknown error") protobufErr := FromError(err) assert.True(t, yarpcerrors.IsUnknown(protobufErr)) clientErr := ToError(protobufErr) assert.True(t, yarpcerrors.IsUnknown(clientErr)) assert.ErrorContains(t, clientErr, err.Error()) } func TestToDeadlineExceededMapsToItself(t *testing.T) { timeout := yarpcerrors.DeadlineExceededErrorf("timeout") assert.Equal(t, timeout, ToError(timeout)) } ================================================ FILE: common/types/mapper/proto/helpers.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "strconv" "time" gogo "github.com/gogo/protobuf/types" "github.com/uber/cadence/common" ) func fromDoubleValue(v *float64) *gogo.DoubleValue { if v == nil { return nil } return &gogo.DoubleValue{Value: *v} } func toDoubleValue(v *gogo.DoubleValue) *float64 { if v == nil { return nil } return common.Float64Ptr(v.Value) } func fromInt64Value(v *int64) *gogo.Int64Value { if v == nil { return nil } return &gogo.Int64Value{Value: *v} } func toInt64Value(v *gogo.Int64Value) *int64 { if v == nil { return nil } return common.Int64Ptr(v.Value) } func unixNanoToTime(t *int64) *gogo.Timestamp { if t == nil { return nil } time, err := gogo.TimestampProto(time.Unix(0, *t)) if err != nil { panic(err) } return time } func timeToUnixNano(t *gogo.Timestamp) *int64 { if t == nil { return nil } timestamp, err := gogo.TimestampFromProto(t) if err != nil { panic(err) } return common.Int64Ptr(timestamp.UnixNano()) } func timeToTimestamp(t *time.Time) *gogo.Timestamp { if t == nil || t.IsZero() { return nil } nanos := t.UnixNano() return unixNanoToTime(&nanos) } func timestampToTime(t *gogo.Timestamp) *time.Time { if t == nil { return nil } time := time.Unix(t.GetSeconds(), int64(t.GetNanos())) return &time } func timestampToTimeVal(t *gogo.Timestamp) time.Time { if t == nil { return time.Time{} } return time.Unix(t.GetSeconds(), int64(t.GetNanos())).UTC() } func durationToDurationProto(d time.Duration) *gogo.Duration { if d == 0 { return nil } return gogo.DurationProto(d) } func durationProtoToDuration(d *gogo.Duration) time.Duration { if d == nil { return 0 } dur, err := gogo.DurationFromProto(d) if err != nil { panic(err) } return dur } func daysToDuration(d *int32) *gogo.Duration { if d == nil { return nil } return gogo.DurationProto(common.DaysToDuration(*d)) } func durationToDays(d *gogo.Duration) *int32 { if d == nil { return nil } duration, err := gogo.DurationFromProto(d) if err != nil { panic(err) } return common.Int32Ptr(common.DurationToDays(duration)) } func secondsToDuration(d *int32) *gogo.Duration { if d == nil { return nil } return gogo.DurationProto(common.SecondsToDuration(int64(*d))) } func durationToSeconds(d *gogo.Duration) *int32 { if d == nil { return nil } duration, err := gogo.DurationFromProto(d) if err != nil { panic(err) } return common.Int32Ptr(int32(common.DurationToSeconds(duration))) } func int32To64(v *int32) *int64 { if v == nil { return nil } return common.Int64Ptr(int64(*v)) } func int64To32(v *int64) *int32 { if v == nil { return nil } return common.Int32Ptr(int32(*v)) } func stringToInt32(s string) int32 { i, err := strconv.Atoi(s) if err != nil { panic(err) } return int32(i) } func int32ToString(i int32) string { s := strconv.Itoa(int(i)) return s } type fieldSet map[string]struct{} func newFieldSet(mask *gogo.FieldMask) fieldSet { if mask == nil { return nil } fs := map[string]struct{}{} for _, field := range mask.Paths { fs[field] = struct{}{} } return fs } func (fs fieldSet) isSet(field string) bool { if fs == nil { return true } _, ok := fs[field] return ok } func newFieldMask(fields []string) *gogo.FieldMask { return &gogo.FieldMask{Paths: fields} } ================================================ FILE: common/types/mapper/proto/helpers_test.go ================================================ // Copyright (c) 2022 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "testing" "time" gogo "github.com/gogo/protobuf/types" "github.com/stretchr/testify/assert" ) func TestTimeToTimestamp(t *testing.T) { testTime := time.Unix(10, 10) timestamp := timeToTimestamp(&testTime) assert.Equal(t, gogo.Timestamp{Seconds: 10, Nanos: 10}, *timestamp) } func TestTimeToTimestampNil(t *testing.T) { result := timeToTimestamp(nil) assert.Nil(t, result) } ================================================ FILE: common/types/mapper/proto/history.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" historyv1 "github.com/uber/cadence/.gen/proto/history/v1" sharedv1 "github.com/uber/cadence/.gen/proto/shared/v1" "github.com/uber/cadence/common" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func FromHistoryCloseShardRequest(t *types.CloseShardRequest) *historyv1.CloseShardRequest { if t == nil { return nil } return &historyv1.CloseShardRequest{ ShardId: t.ShardID, } } func ToHistoryCloseShardRequest(t *historyv1.CloseShardRequest) *types.CloseShardRequest { if t == nil { return nil } return &types.CloseShardRequest{ ShardID: t.ShardId, } } func FromHistoryDescribeHistoryHostRequest(t *types.DescribeHistoryHostRequest) *historyv1.DescribeHistoryHostRequest { if t == nil { return nil } return &historyv1.DescribeHistoryHostRequest{} } func ToHistoryDescribeHistoryHostRequest(t *historyv1.DescribeHistoryHostRequest) *types.DescribeHistoryHostRequest { if t == nil { return nil } return &types.DescribeHistoryHostRequest{} } func FromHistoryDescribeHistoryHostResponse(t *types.DescribeHistoryHostResponse) *historyv1.DescribeHistoryHostResponse { if t == nil { return nil } return &historyv1.DescribeHistoryHostResponse{ NumberOfShards: t.NumberOfShards, ShardIds: t.ShardIDs, DomainCache: FromDomainCacheInfo(t.DomainCache), ShardControllerStatus: t.ShardControllerStatus, Address: t.Address, } } func ToHistoryDescribeHistoryHostResponse(t *historyv1.DescribeHistoryHostResponse) *types.DescribeHistoryHostResponse { if t == nil { return nil } return &types.DescribeHistoryHostResponse{ NumberOfShards: t.NumberOfShards, ShardIDs: t.ShardIds, DomainCache: ToDomainCacheInfo(t.DomainCache), ShardControllerStatus: t.ShardControllerStatus, Address: t.Address, } } func FromHistoryDescribeMutableStateRequest(t *types.DescribeMutableStateRequest) *historyv1.DescribeMutableStateRequest { if t == nil { return nil } return &historyv1.DescribeMutableStateRequest{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.Execution), } } func ToHistoryDescribeMutableStateRequest(t *historyv1.DescribeMutableStateRequest) *types.DescribeMutableStateRequest { if t == nil { return nil } return &types.DescribeMutableStateRequest{ DomainUUID: t.DomainId, Execution: ToWorkflowExecution(t.WorkflowExecution), } } func FromHistoryDescribeMutableStateResponse(t *types.DescribeMutableStateResponse) *historyv1.DescribeMutableStateResponse { if t == nil { return nil } return &historyv1.DescribeMutableStateResponse{ MutableStateInCache: t.MutableStateInCache, MutableStateInDatabase: t.MutableStateInDatabase, } } func ToHistoryDescribeMutableStateResponse(t *historyv1.DescribeMutableStateResponse) *types.DescribeMutableStateResponse { if t == nil { return nil } return &types.DescribeMutableStateResponse{ MutableStateInCache: t.MutableStateInCache, MutableStateInDatabase: t.MutableStateInDatabase, } } func FromHistoryDescribeQueueRequest(t *types.DescribeQueueRequest) *historyv1.DescribeQueueRequest { if t == nil { return nil } return &historyv1.DescribeQueueRequest{ ShardId: t.ShardID, ClusterName: t.ClusterName, TaskType: FromTaskType(t.Type), } } func ToHistoryDescribeQueueRequest(t *historyv1.DescribeQueueRequest) *types.DescribeQueueRequest { if t == nil { return nil } return &types.DescribeQueueRequest{ ShardID: t.ShardId, ClusterName: t.ClusterName, Type: ToTaskType(t.TaskType), } } func FromHistoryDescribeQueueResponse(t *types.DescribeQueueResponse) *historyv1.DescribeQueueResponse { if t == nil { return nil } return &historyv1.DescribeQueueResponse{ ProcessingQueueStates: t.ProcessingQueueStates, } } func ToHistoryDescribeQueueResponse(t *historyv1.DescribeQueueResponse) *types.DescribeQueueResponse { if t == nil { return nil } return &types.DescribeQueueResponse{ ProcessingQueueStates: t.ProcessingQueueStates, } } func FromHistoryDescribeWorkflowExecutionRequest(t *types.HistoryDescribeWorkflowExecutionRequest) *historyv1.DescribeWorkflowExecutionRequest { if t == nil { return nil } return &historyv1.DescribeWorkflowExecutionRequest{ Request: FromDescribeWorkflowExecutionRequest(t.Request), DomainId: t.DomainUUID, } } func ToHistoryDescribeWorkflowExecutionRequest(t *historyv1.DescribeWorkflowExecutionRequest) *types.HistoryDescribeWorkflowExecutionRequest { if t == nil { return nil } return &types.HistoryDescribeWorkflowExecutionRequest{ Request: ToDescribeWorkflowExecutionRequest(t.Request), DomainUUID: t.DomainId, } } func FromHistoryDescribeWorkflowExecutionResponse(t *types.DescribeWorkflowExecutionResponse) *historyv1.DescribeWorkflowExecutionResponse { if t == nil { return nil } return &historyv1.DescribeWorkflowExecutionResponse{ ExecutionConfiguration: FromWorkflowExecutionConfiguration(t.ExecutionConfiguration), WorkflowExecutionInfo: FromWorkflowExecutionInfo(t.WorkflowExecutionInfo), PendingActivities: FromPendingActivityInfoArray(t.PendingActivities), PendingChildren: FromPendingChildExecutionInfoArray(t.PendingChildren), PendingDecision: FromPendingDecisionInfo(t.PendingDecision), } } func ToHistoryDescribeWorkflowExecutionResponse(t *historyv1.DescribeWorkflowExecutionResponse) *types.DescribeWorkflowExecutionResponse { if t == nil { return nil } return &types.DescribeWorkflowExecutionResponse{ ExecutionConfiguration: ToWorkflowExecutionConfiguration(t.ExecutionConfiguration), WorkflowExecutionInfo: ToWorkflowExecutionInfo(t.WorkflowExecutionInfo), PendingActivities: ToPendingActivityInfoArray(t.PendingActivities), PendingChildren: ToPendingChildExecutionInfoArray(t.PendingChildren), PendingDecision: ToPendingDecisionInfo(t.PendingDecision), } } func FromHistoryGetDLQReplicationMessagesRequest(t *types.GetDLQReplicationMessagesRequest) *historyv1.GetDLQReplicationMessagesRequest { if t == nil { return nil } return &historyv1.GetDLQReplicationMessagesRequest{ TaskInfos: FromReplicationTaskInfoArray(t.TaskInfos), } } func ToHistoryGetDLQReplicationMessagesRequest(t *historyv1.GetDLQReplicationMessagesRequest) *types.GetDLQReplicationMessagesRequest { if t == nil { return nil } return &types.GetDLQReplicationMessagesRequest{ TaskInfos: ToReplicationTaskInfoArray(t.TaskInfos), } } func FromHistoryGetDLQReplicationMessagesResponse(t *types.GetDLQReplicationMessagesResponse) *historyv1.GetDLQReplicationMessagesResponse { if t == nil { return nil } return &historyv1.GetDLQReplicationMessagesResponse{ ReplicationTasks: FromReplicationTaskArray(t.ReplicationTasks), } } func ToHistoryGetDLQReplicationMessagesResponse(t *historyv1.GetDLQReplicationMessagesResponse) *types.GetDLQReplicationMessagesResponse { if t == nil { return nil } return &types.GetDLQReplicationMessagesResponse{ ReplicationTasks: ToReplicationTaskArray(t.ReplicationTasks), } } func FromHistoryGetFailoverInfoRequest(t *types.GetFailoverInfoRequest) *historyv1.GetFailoverInfoRequest { if t == nil { return nil } return &historyv1.GetFailoverInfoRequest{ DomainId: t.GetDomainID(), } } func ToHistoryGetFailoverInfoRequest(t *historyv1.GetFailoverInfoRequest) *types.GetFailoverInfoRequest { if t == nil { return nil } return &types.GetFailoverInfoRequest{ DomainID: t.GetDomainId(), } } func FromHistoryGetFailoverInfoResponse(t *types.GetFailoverInfoResponse) *historyv1.GetFailoverInfoResponse { if t == nil { return nil } return &historyv1.GetFailoverInfoResponse{ CompletedShardCount: t.GetCompletedShardCount(), PendingShards: t.GetPendingShards(), } } func ToHistoryGetFailoverInfoResponse(t *historyv1.GetFailoverInfoResponse) *types.GetFailoverInfoResponse { if t == nil { return nil } return &types.GetFailoverInfoResponse{ CompletedShardCount: t.GetCompletedShardCount(), PendingShards: t.GetPendingShards(), } } func FromHistoryGetMutableStateRequest(t *types.GetMutableStateRequest) *historyv1.GetMutableStateRequest { if t == nil { return nil } return &historyv1.GetMutableStateRequest{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.Execution), ExpectedNextEventId: t.ExpectedNextEventID, CurrentBranchToken: t.CurrentBranchToken, VersionHistoryItem: FromVersionHistoryItem(t.VersionHistoryItem), } } func ToHistoryGetMutableStateRequest(t *historyv1.GetMutableStateRequest) *types.GetMutableStateRequest { if t == nil { return nil } return &types.GetMutableStateRequest{ DomainUUID: t.DomainId, Execution: ToWorkflowExecution(t.WorkflowExecution), ExpectedNextEventID: t.ExpectedNextEventId, CurrentBranchToken: t.CurrentBranchToken, VersionHistoryItem: ToVersionHistoryItem(t.VersionHistoryItem), } } func FromHistoryGetMutableStateResponse(t *types.GetMutableStateResponse) *historyv1.GetMutableStateResponse { if t == nil { return nil } var workflowCloseState *types.WorkflowExecutionCloseStatus if t.WorkflowCloseState != nil { workflowCloseState = persistence.ToInternalWorkflowExecutionCloseStatus(int(*t.WorkflowCloseState)) } return &historyv1.GetMutableStateResponse{ WorkflowExecution: FromWorkflowExecution(t.Execution), WorkflowType: FromWorkflowType(t.WorkflowType), NextEventId: t.NextEventID, PreviousStartedEventId: fromInt64Value(t.PreviousStartedEventID), LastFirstEventId: t.LastFirstEventID, TaskList: FromTaskList(t.TaskList), StickyTaskList: FromTaskList(t.StickyTaskList), ClientLibraryVersion: t.ClientLibraryVersion, ClientFeatureVersion: t.ClientFeatureVersion, ClientImpl: t.ClientImpl, StickyTaskListScheduleToStartTimeout: secondsToDuration(t.StickyTaskListScheduleToStartTimeout), EventStoreVersion: t.EventStoreVersion, CurrentBranchToken: t.CurrentBranchToken, WorkflowState: FromWorkflowState(t.WorkflowState), WorkflowCloseState: FromWorkflowExecutionCloseStatus(workflowCloseState), VersionHistories: FromVersionHistories(t.VersionHistories), IsStickyTaskListEnabled: t.IsStickyTaskListEnabled, HistorySize: t.HistorySize, } } func ToHistoryGetMutableStateResponse(t *historyv1.GetMutableStateResponse) *types.GetMutableStateResponse { if t == nil { return nil } return &types.GetMutableStateResponse{ Execution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), NextEventID: t.NextEventId, PreviousStartedEventID: toInt64Value(t.PreviousStartedEventId), LastFirstEventID: t.LastFirstEventId, TaskList: ToTaskList(t.TaskList), StickyTaskList: ToTaskList(t.StickyTaskList), ClientLibraryVersion: t.ClientLibraryVersion, ClientFeatureVersion: t.ClientFeatureVersion, ClientImpl: t.ClientImpl, StickyTaskListScheduleToStartTimeout: durationToSeconds(t.StickyTaskListScheduleToStartTimeout), EventStoreVersion: t.EventStoreVersion, CurrentBranchToken: t.CurrentBranchToken, WorkflowState: ToWorkflowState(t.WorkflowState), WorkflowCloseState: common.Int32Ptr(int32(persistence.FromInternalWorkflowExecutionCloseStatus(ToWorkflowExecutionCloseStatus(t.WorkflowCloseState)))), VersionHistories: ToVersionHistories(t.VersionHistories), IsStickyTaskListEnabled: t.IsStickyTaskListEnabled, IsWorkflowRunning: t.WorkflowState == sharedv1.WorkflowState_WORKFLOW_STATE_RUNNING, HistorySize: t.HistorySize, } } func FromHistoryGetReplicationMessagesRequest(t *types.GetReplicationMessagesRequest) *historyv1.GetReplicationMessagesRequest { if t == nil { return nil } return &historyv1.GetReplicationMessagesRequest{ Tokens: FromReplicationTokenArray(t.Tokens), ClusterName: t.ClusterName, } } func ToHistoryGetReplicationMessagesRequest(t *historyv1.GetReplicationMessagesRequest) *types.GetReplicationMessagesRequest { if t == nil { return nil } return &types.GetReplicationMessagesRequest{ Tokens: ToReplicationTokenArray(t.Tokens), ClusterName: t.ClusterName, } } func FromHistoryGetReplicationMessagesResponse(t *types.GetReplicationMessagesResponse) *historyv1.GetReplicationMessagesResponse { if t == nil { return nil } return &historyv1.GetReplicationMessagesResponse{ ShardMessages: FromReplicationMessagesMap(t.MessagesByShard), } } func ToHistoryGetReplicationMessagesResponse(t *historyv1.GetReplicationMessagesResponse) *types.GetReplicationMessagesResponse { if t == nil { return nil } return &types.GetReplicationMessagesResponse{ MessagesByShard: ToReplicationMessagesMap(t.ShardMessages), } } func FromHistoryCountDLQMessagesRequest(t *types.CountDLQMessagesRequest) *historyv1.CountDLQMessagesRequest { if t == nil { return nil } return &historyv1.CountDLQMessagesRequest{ ForceFetch: t.ForceFetch, } } func ToHistoryCountDLQMessagesRequest(t *historyv1.CountDLQMessagesRequest) *types.CountDLQMessagesRequest { if t == nil { return nil } return &types.CountDLQMessagesRequest{ ForceFetch: t.ForceFetch, } } func FromHistoryCountDLQMessagesResponse(t *types.HistoryCountDLQMessagesResponse) *historyv1.CountDLQMessagesResponse { if t == nil { return nil } return &historyv1.CountDLQMessagesResponse{ Entries: FromHistoryDLQCountEntryMap(t.Entries), } } func ToHistoryCountDLQMessagesResponse(t *historyv1.CountDLQMessagesResponse) *types.HistoryCountDLQMessagesResponse { if t == nil { return nil } return &types.HistoryCountDLQMessagesResponse{ Entries: ToHistoryDLQCountEntryMap(t.Entries), } } func FromHistoryMergeDLQMessagesRequest(t *types.MergeDLQMessagesRequest) *historyv1.MergeDLQMessagesRequest { if t == nil { return nil } return &historyv1.MergeDLQMessagesRequest{ Type: FromDLQType(t.Type), ShardId: t.ShardID, SourceCluster: t.SourceCluster, InclusiveEndMessageId: fromInt64Value(t.InclusiveEndMessageID), PageSize: t.MaximumPageSize, NextPageToken: t.NextPageToken, } } func ToHistoryMergeDLQMessagesRequest(t *historyv1.MergeDLQMessagesRequest) *types.MergeDLQMessagesRequest { if t == nil { return nil } return &types.MergeDLQMessagesRequest{ Type: ToDLQType(t.Type), ShardID: t.ShardId, SourceCluster: t.SourceCluster, InclusiveEndMessageID: toInt64Value(t.InclusiveEndMessageId), MaximumPageSize: t.PageSize, NextPageToken: t.NextPageToken, } } func FromHistoryMergeDLQMessagesResponse(t *types.MergeDLQMessagesResponse) *historyv1.MergeDLQMessagesResponse { if t == nil { return nil } return &historyv1.MergeDLQMessagesResponse{ NextPageToken: t.NextPageToken, } } func ToHistoryMergeDLQMessagesResponse(t *historyv1.MergeDLQMessagesResponse) *types.MergeDLQMessagesResponse { if t == nil { return nil } return &types.MergeDLQMessagesResponse{ NextPageToken: t.NextPageToken, } } func FromHistoryNotifyFailoverMarkersRequest(t *types.NotifyFailoverMarkersRequest) *historyv1.NotifyFailoverMarkersRequest { if t == nil { return nil } return &historyv1.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: FromFailoverMarkerTokenArray(t.FailoverMarkerTokens), } } func ToHistoryNotifyFailoverMarkersRequest(t *historyv1.NotifyFailoverMarkersRequest) *types.NotifyFailoverMarkersRequest { if t == nil { return nil } return &types.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: ToFailoverMarkerTokenArray(t.FailoverMarkerTokens), } } func FromHistoryPollMutableStateRequest(t *types.PollMutableStateRequest) *historyv1.PollMutableStateRequest { if t == nil { return nil } return &historyv1.PollMutableStateRequest{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.Execution), ExpectedNextEventId: t.ExpectedNextEventID, CurrentBranchToken: t.CurrentBranchToken, } } func ToHistoryPollMutableStateRequest(t *historyv1.PollMutableStateRequest) *types.PollMutableStateRequest { if t == nil { return nil } return &types.PollMutableStateRequest{ DomainUUID: t.DomainId, Execution: ToWorkflowExecution(t.WorkflowExecution), ExpectedNextEventID: t.ExpectedNextEventId, CurrentBranchToken: t.CurrentBranchToken, } } func FromHistoryPollMutableStateResponse(t *types.PollMutableStateResponse) *historyv1.PollMutableStateResponse { if t == nil { return nil } var workflowCloseState *types.WorkflowExecutionCloseStatus if t.WorkflowCloseState != nil { workflowCloseState = persistence.ToInternalWorkflowExecutionCloseStatus(int(*t.WorkflowCloseState)) } return &historyv1.PollMutableStateResponse{ WorkflowExecution: FromWorkflowExecution(t.Execution), WorkflowType: FromWorkflowType(t.WorkflowType), NextEventId: t.NextEventID, PreviousStartedEventId: fromInt64Value(t.PreviousStartedEventID), LastFirstEventId: t.LastFirstEventID, TaskList: FromTaskList(t.TaskList), StickyTaskList: FromTaskList(t.StickyTaskList), ClientLibraryVersion: t.ClientLibraryVersion, ClientFeatureVersion: t.ClientFeatureVersion, ClientImpl: t.ClientImpl, StickyTaskListScheduleToStartTimeout: secondsToDuration(t.StickyTaskListScheduleToStartTimeout), CurrentBranchToken: t.CurrentBranchToken, VersionHistories: FromVersionHistories(t.VersionHistories), WorkflowState: FromWorkflowState(t.WorkflowState), WorkflowCloseState: FromWorkflowExecutionCloseStatus(workflowCloseState), } } func ToHistoryPollMutableStateResponse(t *historyv1.PollMutableStateResponse) *types.PollMutableStateResponse { if t == nil { return nil } return &types.PollMutableStateResponse{ Execution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), NextEventID: t.NextEventId, PreviousStartedEventID: toInt64Value(t.PreviousStartedEventId), LastFirstEventID: t.LastFirstEventId, TaskList: ToTaskList(t.TaskList), StickyTaskList: ToTaskList(t.StickyTaskList), ClientLibraryVersion: t.ClientLibraryVersion, ClientFeatureVersion: t.ClientFeatureVersion, ClientImpl: t.ClientImpl, StickyTaskListScheduleToStartTimeout: durationToSeconds(t.StickyTaskListScheduleToStartTimeout), CurrentBranchToken: t.CurrentBranchToken, VersionHistories: ToVersionHistories(t.VersionHistories), WorkflowState: ToWorkflowState(t.WorkflowState), WorkflowCloseState: common.Int32Ptr(int32(persistence.FromInternalWorkflowExecutionCloseStatus(ToWorkflowExecutionCloseStatus(t.WorkflowCloseState)))), } } func FromHistoryPurgeDLQMessagesRequest(t *types.PurgeDLQMessagesRequest) *historyv1.PurgeDLQMessagesRequest { if t == nil { return nil } return &historyv1.PurgeDLQMessagesRequest{ Type: FromDLQType(t.Type), ShardId: t.ShardID, SourceCluster: t.SourceCluster, InclusiveEndMessageId: fromInt64Value(t.InclusiveEndMessageID), } } func ToHistoryPurgeDLQMessagesRequest(t *historyv1.PurgeDLQMessagesRequest) *types.PurgeDLQMessagesRequest { if t == nil { return nil } return &types.PurgeDLQMessagesRequest{ Type: ToDLQType(t.Type), ShardID: t.ShardId, SourceCluster: t.SourceCluster, InclusiveEndMessageID: toInt64Value(t.InclusiveEndMessageId), } } func FromHistoryQueryWorkflowRequest(t *types.HistoryQueryWorkflowRequest) *historyv1.QueryWorkflowRequest { if t == nil { return nil } return &historyv1.QueryWorkflowRequest{ Request: FromQueryWorkflowRequest(t.Request), DomainId: t.DomainUUID, } } func ToHistoryQueryWorkflowRequest(t *historyv1.QueryWorkflowRequest) *types.HistoryQueryWorkflowRequest { if t == nil { return nil } return &types.HistoryQueryWorkflowRequest{ Request: ToQueryWorkflowRequest(t.Request), DomainUUID: t.DomainId, } } func FromHistoryQueryWorkflowResponse(t *types.HistoryQueryWorkflowResponse) *historyv1.QueryWorkflowResponse { if t == nil || t.Response == nil { return nil } return &historyv1.QueryWorkflowResponse{ QueryResult: FromPayload(t.Response.QueryResult), QueryRejected: FromQueryRejected(t.Response.QueryRejected), } } func ToHistoryQueryWorkflowResponse(t *historyv1.QueryWorkflowResponse) *types.HistoryQueryWorkflowResponse { if t == nil { return nil } return &types.HistoryQueryWorkflowResponse{ Response: &types.QueryWorkflowResponse{ QueryResult: ToPayload(t.QueryResult), QueryRejected: ToQueryRejected(t.QueryRejected), }, } } func FromHistoryReadDLQMessagesRequest(t *types.ReadDLQMessagesRequest) *historyv1.ReadDLQMessagesRequest { if t == nil { return nil } return &historyv1.ReadDLQMessagesRequest{ Type: FromDLQType(t.Type), ShardId: t.ShardID, SourceCluster: t.SourceCluster, InclusiveEndMessageId: fromInt64Value(t.InclusiveEndMessageID), PageSize: t.MaximumPageSize, NextPageToken: t.NextPageToken, } } func ToHistoryReadDLQMessagesRequest(t *historyv1.ReadDLQMessagesRequest) *types.ReadDLQMessagesRequest { if t == nil { return nil } return &types.ReadDLQMessagesRequest{ Type: ToDLQType(t.Type), ShardID: t.ShardId, SourceCluster: t.SourceCluster, InclusiveEndMessageID: toInt64Value(t.InclusiveEndMessageId), MaximumPageSize: t.PageSize, NextPageToken: t.NextPageToken, } } func FromHistoryReadDLQMessagesResponse(t *types.ReadDLQMessagesResponse) *historyv1.ReadDLQMessagesResponse { if t == nil { return nil } return &historyv1.ReadDLQMessagesResponse{ Type: FromDLQType(t.Type), ReplicationTasks: FromReplicationTaskArray(t.ReplicationTasks), ReplicationTasksInfo: FromReplicationTaskInfoArray(t.ReplicationTasksInfo), NextPageToken: t.NextPageToken, } } func ToHistoryReadDLQMessagesResponse(t *historyv1.ReadDLQMessagesResponse) *types.ReadDLQMessagesResponse { if t == nil { return nil } return &types.ReadDLQMessagesResponse{ Type: ToDLQType(t.Type), ReplicationTasks: ToReplicationTaskArray(t.ReplicationTasks), ReplicationTasksInfo: ToReplicationTaskInfoArray(t.ReplicationTasksInfo), NextPageToken: t.NextPageToken, } } func FromHistoryReapplyEventsRequest(t *types.HistoryReapplyEventsRequest) *historyv1.ReapplyEventsRequest { if t == nil || t.Request == nil { return nil } return &historyv1.ReapplyEventsRequest{ Domain: t.Request.DomainName, DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.Request.WorkflowExecution), Events: FromDataBlob(t.Request.Events), } } func ToHistoryReapplyEventsRequest(t *historyv1.ReapplyEventsRequest) *types.HistoryReapplyEventsRequest { if t == nil { return nil } return &types.HistoryReapplyEventsRequest{ DomainUUID: t.DomainId, Request: &types.ReapplyEventsRequest{ DomainName: t.Domain, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Events: ToDataBlob(t.Events), }, } } func FromHistoryRecordActivityTaskHeartbeatRequest(t *types.HistoryRecordActivityTaskHeartbeatRequest) *historyv1.RecordActivityTaskHeartbeatRequest { if t == nil { return nil } return &historyv1.RecordActivityTaskHeartbeatRequest{ DomainId: t.DomainUUID, Request: FromRecordActivityTaskHeartbeatRequest(t.HeartbeatRequest), } } func ToHistoryRecordActivityTaskHeartbeatRequest(t *historyv1.RecordActivityTaskHeartbeatRequest) *types.HistoryRecordActivityTaskHeartbeatRequest { if t == nil { return nil } return &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: t.DomainId, HeartbeatRequest: ToRecordActivityTaskHeartbeatRequest(t.Request), } } func FromHistoryRecordActivityTaskHeartbeatResponse(t *types.RecordActivityTaskHeartbeatResponse) *historyv1.RecordActivityTaskHeartbeatResponse { if t == nil { return nil } return &historyv1.RecordActivityTaskHeartbeatResponse{ CancelRequested: t.CancelRequested, } } func ToHistoryRecordActivityTaskHeartbeatResponse(t *historyv1.RecordActivityTaskHeartbeatResponse) *types.RecordActivityTaskHeartbeatResponse { if t == nil { return nil } return &types.RecordActivityTaskHeartbeatResponse{ CancelRequested: t.CancelRequested, } } func FromHistoryRecordActivityTaskStartedRequest(t *types.RecordActivityTaskStartedRequest) *historyv1.RecordActivityTaskStartedRequest { if t == nil { return nil } return &historyv1.RecordActivityTaskStartedRequest{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), ScheduleId: t.ScheduleID, TaskId: t.TaskID, RequestId: t.RequestID, PollRequest: FromPollForActivityTaskRequest(t.PollRequest), } } func ToHistoryRecordActivityTaskStartedRequest(t *historyv1.RecordActivityTaskStartedRequest) *types.RecordActivityTaskStartedRequest { if t == nil { return nil } return &types.RecordActivityTaskStartedRequest{ DomainUUID: t.DomainId, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), ScheduleID: t.ScheduleId, TaskID: t.TaskId, RequestID: t.RequestId, PollRequest: ToPollForActivityTaskRequest(t.PollRequest), } } func FromHistoryRecordActivityTaskStartedResponse(t *types.RecordActivityTaskStartedResponse) *historyv1.RecordActivityTaskStartedResponse { if t == nil { return nil } return &historyv1.RecordActivityTaskStartedResponse{ ScheduledEvent: FromHistoryEvent(t.ScheduledEvent), StartedTime: unixNanoToTime(t.StartedTimestamp), Attempt: int32(t.Attempt), ScheduledTimeOfThisAttempt: unixNanoToTime(t.ScheduledTimestampOfThisAttempt), HeartbeatDetails: FromPayload(t.HeartbeatDetails), WorkflowType: FromWorkflowType(t.WorkflowType), WorkflowDomain: t.WorkflowDomain, } } func ToHistoryRecordActivityTaskStartedResponse(t *historyv1.RecordActivityTaskStartedResponse) *types.RecordActivityTaskStartedResponse { if t == nil { return nil } return &types.RecordActivityTaskStartedResponse{ ScheduledEvent: ToHistoryEvent(t.ScheduledEvent), StartedTimestamp: timeToUnixNano(t.StartedTime), Attempt: int64(t.Attempt), ScheduledTimestampOfThisAttempt: timeToUnixNano(t.ScheduledTimeOfThisAttempt), HeartbeatDetails: ToPayload(t.HeartbeatDetails), WorkflowType: ToWorkflowType(t.WorkflowType), WorkflowDomain: t.WorkflowDomain, } } func FromHistoryRecordChildExecutionCompletedRequest(t *types.RecordChildExecutionCompletedRequest) *historyv1.RecordChildExecutionCompletedRequest { if t == nil { return nil } return &historyv1.RecordChildExecutionCompletedRequest{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), InitiatedId: t.InitiatedID, CompletedExecution: FromWorkflowExecution(t.CompletedExecution), CompletionEvent: FromHistoryEvent(t.CompletionEvent), StartedId: t.StartedID, } } func ToHistoryRecordChildExecutionCompletedRequest(t *historyv1.RecordChildExecutionCompletedRequest) *types.RecordChildExecutionCompletedRequest { if t == nil { return nil } return &types.RecordChildExecutionCompletedRequest{ DomainUUID: t.DomainId, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), InitiatedID: t.InitiatedId, CompletedExecution: ToWorkflowExecution(t.CompletedExecution), CompletionEvent: ToHistoryEvent(t.CompletionEvent), StartedID: t.StartedId, } } func FromHistoryRecordDecisionTaskStartedRequest(t *types.RecordDecisionTaskStartedRequest) *historyv1.RecordDecisionTaskStartedRequest { if t == nil { return nil } return &historyv1.RecordDecisionTaskStartedRequest{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), ScheduleId: t.ScheduleID, TaskId: t.TaskID, RequestId: t.RequestID, PollRequest: FromPollForDecisionTaskRequest(t.PollRequest), } } func ToHistoryRecordDecisionTaskStartedRequest(t *historyv1.RecordDecisionTaskStartedRequest) *types.RecordDecisionTaskStartedRequest { if t == nil { return nil } return &types.RecordDecisionTaskStartedRequest{ DomainUUID: t.DomainId, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), ScheduleID: t.ScheduleId, TaskID: t.TaskId, RequestID: t.RequestId, PollRequest: ToPollForDecisionTaskRequest(t.PollRequest), } } func FromHistoryRecordDecisionTaskStartedResponse(t *types.RecordDecisionTaskStartedResponse) *historyv1.RecordDecisionTaskStartedResponse { if t == nil { return nil } return &historyv1.RecordDecisionTaskStartedResponse{ WorkflowType: FromWorkflowType(t.WorkflowType), PreviousStartedEventId: fromInt64Value(t.PreviousStartedEventID), ScheduledEventId: t.ScheduledEventID, StartedEventId: t.StartedEventID, NextEventId: t.NextEventID, Attempt: int32(t.Attempt), StickyExecutionEnabled: t.StickyExecutionEnabled, DecisionInfo: FromTransientDecisionInfo(t.DecisionInfo), WorkflowExecutionTaskList: FromTaskList(t.WorkflowExecutionTaskList), EventStoreVersion: t.EventStoreVersion, BranchToken: t.BranchToken, ScheduledTime: unixNanoToTime(t.ScheduledTimestamp), StartedTime: unixNanoToTime(t.StartedTimestamp), Queries: FromWorkflowQueryMap(t.Queries), HistorySize: t.HistorySize, } } func ToHistoryRecordDecisionTaskStartedResponse(t *historyv1.RecordDecisionTaskStartedResponse) *types.RecordDecisionTaskStartedResponse { if t == nil { return nil } return &types.RecordDecisionTaskStartedResponse{ WorkflowType: ToWorkflowType(t.WorkflowType), PreviousStartedEventID: toInt64Value(t.PreviousStartedEventId), ScheduledEventID: t.ScheduledEventId, StartedEventID: t.StartedEventId, NextEventID: t.NextEventId, Attempt: int64(t.Attempt), StickyExecutionEnabled: t.StickyExecutionEnabled, DecisionInfo: ToTransientDecisionInfo(t.DecisionInfo), WorkflowExecutionTaskList: ToTaskList(t.WorkflowExecutionTaskList), EventStoreVersion: t.EventStoreVersion, BranchToken: t.BranchToken, ScheduledTimestamp: timeToUnixNano(t.ScheduledTime), StartedTimestamp: timeToUnixNano(t.StartedTime), Queries: ToWorkflowQueryMap(t.Queries), HistorySize: t.HistorySize, } } func FromHistoryRefreshWorkflowTasksRequest(t *types.HistoryRefreshWorkflowTasksRequest) *historyv1.RefreshWorkflowTasksRequest { if t == nil || t.Request == nil { return nil } return &historyv1.RefreshWorkflowTasksRequest{ DomainId: t.DomainUIID, Domain: t.Request.Domain, WorkflowExecution: FromWorkflowExecution(t.Request.Execution), } } func ToHistoryRefreshWorkflowTasksRequest(t *historyv1.RefreshWorkflowTasksRequest) *types.HistoryRefreshWorkflowTasksRequest { if t == nil { return nil } return &types.HistoryRefreshWorkflowTasksRequest{ Request: &types.RefreshWorkflowTasksRequest{ Domain: t.Domain, Execution: ToWorkflowExecution(t.WorkflowExecution), }, DomainUIID: t.DomainId, } } func FromHistoryRemoveSignalMutableStateRequest(t *types.RemoveSignalMutableStateRequest) *historyv1.RemoveSignalMutableStateRequest { if t == nil { return nil } return &historyv1.RemoveSignalMutableStateRequest{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), RequestId: t.RequestID, } } func ToHistoryRemoveSignalMutableStateRequest(t *historyv1.RemoveSignalMutableStateRequest) *types.RemoveSignalMutableStateRequest { if t == nil { return nil } return &types.RemoveSignalMutableStateRequest{ DomainUUID: t.DomainId, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), RequestID: t.RequestId, } } func FromHistoryRemoveTaskRequest(t *types.RemoveTaskRequest) *historyv1.RemoveTaskRequest { if t == nil { return nil } return &historyv1.RemoveTaskRequest{ ShardId: t.ShardID, TaskType: FromTaskType(t.Type), TaskId: t.TaskID, VisibilityTime: unixNanoToTime(t.VisibilityTimestamp), ClusterName: t.ClusterName, } } func ToHistoryRemoveTaskRequest(t *historyv1.RemoveTaskRequest) *types.RemoveTaskRequest { if t == nil { return nil } return &types.RemoveTaskRequest{ ShardID: t.ShardId, Type: ToTaskType(t.TaskType), TaskID: t.TaskId, VisibilityTimestamp: timeToUnixNano(t.VisibilityTime), ClusterName: t.ClusterName, } } func FromHistoryReplicateEventsV2Request(t *types.ReplicateEventsV2Request) *historyv1.ReplicateEventsV2Request { if t == nil { return nil } return &historyv1.ReplicateEventsV2Request{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), VersionHistoryItems: FromVersionHistoryItemArray(t.VersionHistoryItems), Events: FromDataBlob(t.Events), NewRunEvents: FromDataBlob(t.NewRunEvents), } } func ToHistoryReplicateEventsV2Request(t *historyv1.ReplicateEventsV2Request) *types.ReplicateEventsV2Request { if t == nil { return nil } return &types.ReplicateEventsV2Request{ DomainUUID: t.DomainId, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), VersionHistoryItems: ToVersionHistoryItemArray(t.VersionHistoryItems), Events: ToDataBlob(t.Events), NewRunEvents: ToDataBlob(t.NewRunEvents), } } func FromHistoryRequestCancelWorkflowExecutionRequest(t *types.HistoryRequestCancelWorkflowExecutionRequest) *historyv1.RequestCancelWorkflowExecutionRequest { if t == nil { return nil } return &historyv1.RequestCancelWorkflowExecutionRequest{ DomainId: t.DomainUUID, CancelRequest: FromRequestCancelWorkflowExecutionRequest(t.CancelRequest), ExternalExecutionInfo: FromExternalExecutionInfoFields(t.ExternalWorkflowExecution, t.ExternalInitiatedEventID), ChildWorkflowOnly: t.ChildWorkflowOnly, } } func ToHistoryRequestCancelWorkflowExecutionRequest(t *historyv1.RequestCancelWorkflowExecutionRequest) *types.HistoryRequestCancelWorkflowExecutionRequest { if t == nil { return nil } return &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: t.DomainId, CancelRequest: ToRequestCancelWorkflowExecutionRequest(t.CancelRequest), ExternalInitiatedEventID: ToExternalInitiatedID(t.ExternalExecutionInfo), ExternalWorkflowExecution: ToExternalWorkflowExecution(t.ExternalExecutionInfo), ChildWorkflowOnly: t.ChildWorkflowOnly, } } func FromHistoryResetQueueRequest(t *types.ResetQueueRequest) *historyv1.ResetQueueRequest { if t == nil { return nil } return &historyv1.ResetQueueRequest{ ShardId: t.ShardID, ClusterName: t.ClusterName, TaskType: FromTaskType(t.Type), } } func ToHistoryResetQueueRequest(t *historyv1.ResetQueueRequest) *types.ResetQueueRequest { if t == nil { return nil } return &types.ResetQueueRequest{ ShardID: t.ShardId, ClusterName: t.ClusterName, Type: ToTaskType(t.TaskType), } } func FromHistoryResetStickyTaskListRequest(t *types.HistoryResetStickyTaskListRequest) *historyv1.ResetStickyTaskListRequest { if t == nil { return nil } return &historyv1.ResetStickyTaskListRequest{ Request: &apiv1.ResetStickyTaskListRequest{ Domain: "", WorkflowExecution: FromWorkflowExecution(t.Execution), }, DomainId: t.DomainUUID, } } func ToHistoryResetStickyTaskListRequest(t *historyv1.ResetStickyTaskListRequest) *types.HistoryResetStickyTaskListRequest { if t == nil || t.Request == nil { return nil } return &types.HistoryResetStickyTaskListRequest{ DomainUUID: t.DomainId, Execution: ToWorkflowExecution(t.Request.WorkflowExecution), } } func FromHistoryResetWorkflowExecutionRequest(t *types.HistoryResetWorkflowExecutionRequest) *historyv1.ResetWorkflowExecutionRequest { if t == nil { return nil } return &historyv1.ResetWorkflowExecutionRequest{ Request: FromResetWorkflowExecutionRequest(t.ResetRequest), DomainId: t.DomainUUID, } } func ToHistoryResetWorkflowExecutionRequest(t *historyv1.ResetWorkflowExecutionRequest) *types.HistoryResetWorkflowExecutionRequest { if t == nil { return nil } return &types.HistoryResetWorkflowExecutionRequest{ ResetRequest: ToResetWorkflowExecutionRequest(t.Request), DomainUUID: t.DomainId, } } func FromHistoryResetWorkflowExecutionResponse(t *types.ResetWorkflowExecutionResponse) *historyv1.ResetWorkflowExecutionResponse { if t == nil { return nil } return &historyv1.ResetWorkflowExecutionResponse{ RunId: t.RunID, } } func ToHistoryResetWorkflowExecutionResponse(t *historyv1.ResetWorkflowExecutionResponse) *types.ResetWorkflowExecutionResponse { if t == nil { return nil } return &types.ResetWorkflowExecutionResponse{ RunID: t.RunId, } } func FromHistoryRespondActivityTaskCanceledRequest(t *types.HistoryRespondActivityTaskCanceledRequest) *historyv1.RespondActivityTaskCanceledRequest { if t == nil { return nil } return &historyv1.RespondActivityTaskCanceledRequest{ DomainId: t.DomainUUID, Request: FromRespondActivityTaskCanceledRequest(t.CancelRequest), } } func ToHistoryRespondActivityTaskCanceledRequest(t *historyv1.RespondActivityTaskCanceledRequest) *types.HistoryRespondActivityTaskCanceledRequest { if t == nil { return nil } return &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: t.DomainId, CancelRequest: ToRespondActivityTaskCanceledRequest(t.Request), } } func FromHistoryRespondActivityTaskCompletedRequest(t *types.HistoryRespondActivityTaskCompletedRequest) *historyv1.RespondActivityTaskCompletedRequest { if t == nil { return nil } return &historyv1.RespondActivityTaskCompletedRequest{ DomainId: t.DomainUUID, Request: FromRespondActivityTaskCompletedRequest(t.CompleteRequest), } } func ToHistoryRespondActivityTaskCompletedRequest(t *historyv1.RespondActivityTaskCompletedRequest) *types.HistoryRespondActivityTaskCompletedRequest { if t == nil { return nil } return &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: t.DomainId, CompleteRequest: ToRespondActivityTaskCompletedRequest(t.Request), } } func FromHistoryRespondActivityTaskFailedRequest(t *types.HistoryRespondActivityTaskFailedRequest) *historyv1.RespondActivityTaskFailedRequest { if t == nil { return nil } return &historyv1.RespondActivityTaskFailedRequest{ DomainId: t.DomainUUID, Request: FromRespondActivityTaskFailedRequest(t.FailedRequest), } } func ToHistoryRespondActivityTaskFailedRequest(t *historyv1.RespondActivityTaskFailedRequest) *types.HistoryRespondActivityTaskFailedRequest { if t == nil { return nil } return &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: t.DomainId, FailedRequest: ToRespondActivityTaskFailedRequest(t.Request), } } func FromHistoryRespondDecisionTaskCompletedRequest(t *types.HistoryRespondDecisionTaskCompletedRequest) *historyv1.RespondDecisionTaskCompletedRequest { if t == nil { return nil } return &historyv1.RespondDecisionTaskCompletedRequest{ DomainId: t.DomainUUID, Request: FromRespondDecisionTaskCompletedRequest(t.CompleteRequest), } } func ToHistoryRespondDecisionTaskCompletedRequest(t *historyv1.RespondDecisionTaskCompletedRequest) *types.HistoryRespondDecisionTaskCompletedRequest { if t == nil { return nil } return &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: t.DomainId, CompleteRequest: ToRespondDecisionTaskCompletedRequest(t.Request), } } func FromHistoryRespondDecisionTaskCompletedResponse(t *types.HistoryRespondDecisionTaskCompletedResponse) *historyv1.RespondDecisionTaskCompletedResponse { if t == nil { return nil } return &historyv1.RespondDecisionTaskCompletedResponse{ StartedResponse: FromHistoryRecordDecisionTaskStartedResponse(t.StartedResponse), ActivitiesToDispatchLocally: FromActivityLocalDispatchInfoMap(t.ActivitiesToDispatchLocally), } } func ToHistoryRespondDecisionTaskCompletedResponse(t *historyv1.RespondDecisionTaskCompletedResponse) *types.HistoryRespondDecisionTaskCompletedResponse { if t == nil { return nil } return &types.HistoryRespondDecisionTaskCompletedResponse{ StartedResponse: ToHistoryRecordDecisionTaskStartedResponse(t.StartedResponse), ActivitiesToDispatchLocally: ToActivityLocalDispatchInfoMap(t.ActivitiesToDispatchLocally), } } func FromHistoryRespondDecisionTaskFailedRequest(t *types.HistoryRespondDecisionTaskFailedRequest) *historyv1.RespondDecisionTaskFailedRequest { if t == nil { return nil } return &historyv1.RespondDecisionTaskFailedRequest{ DomainId: t.DomainUUID, Request: FromRespondDecisionTaskFailedRequest(t.FailedRequest), } } func ToHistoryRespondDecisionTaskFailedRequest(t *historyv1.RespondDecisionTaskFailedRequest) *types.HistoryRespondDecisionTaskFailedRequest { if t == nil { return nil } return &types.HistoryRespondDecisionTaskFailedRequest{ DomainUUID: t.DomainId, FailedRequest: ToRespondDecisionTaskFailedRequest(t.Request), } } func FromHistoryScheduleDecisionTaskRequest(t *types.ScheduleDecisionTaskRequest) *historyv1.ScheduleDecisionTaskRequest { if t == nil { return nil } return &historyv1.ScheduleDecisionTaskRequest{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), IsFirstDecision: t.IsFirstDecision, } } func ToHistoryScheduleDecisionTaskRequest(t *historyv1.ScheduleDecisionTaskRequest) *types.ScheduleDecisionTaskRequest { if t == nil { return nil } return &types.ScheduleDecisionTaskRequest{ DomainUUID: t.DomainId, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), IsFirstDecision: t.IsFirstDecision, } } func FromHistorySignalWithStartWorkflowExecutionRequest(t *types.HistorySignalWithStartWorkflowExecutionRequest) *historyv1.SignalWithStartWorkflowExecutionRequest { if t == nil { return nil } return &historyv1.SignalWithStartWorkflowExecutionRequest{ Request: FromSignalWithStartWorkflowExecutionRequest(t.SignalWithStartRequest), DomainId: t.DomainUUID, PartitionConfig: t.PartitionConfig, } } func ToHistorySignalWithStartWorkflowExecutionRequest(t *historyv1.SignalWithStartWorkflowExecutionRequest) *types.HistorySignalWithStartWorkflowExecutionRequest { if t == nil { return nil } return &types.HistorySignalWithStartWorkflowExecutionRequest{ SignalWithStartRequest: ToSignalWithStartWorkflowExecutionRequest(t.Request), DomainUUID: t.DomainId, PartitionConfig: t.PartitionConfig, } } func FromHistorySignalWithStartWorkflowExecutionResponse(t *types.StartWorkflowExecutionResponse) *historyv1.SignalWithStartWorkflowExecutionResponse { if t == nil { return nil } return &historyv1.SignalWithStartWorkflowExecutionResponse{ RunId: t.RunID, } } func ToHistorySignalWithStartWorkflowExecutionResponse(t *historyv1.SignalWithStartWorkflowExecutionResponse) *types.StartWorkflowExecutionResponse { if t == nil { return nil } return &types.StartWorkflowExecutionResponse{ RunID: t.RunId, } } func FromHistorySignalWorkflowExecutionRequest(t *types.HistorySignalWorkflowExecutionRequest) *historyv1.SignalWorkflowExecutionRequest { if t == nil { return nil } return &historyv1.SignalWorkflowExecutionRequest{ Request: FromSignalWorkflowExecutionRequest(t.SignalRequest), DomainId: t.DomainUUID, ExternalWorkflowExecution: FromWorkflowExecution(t.ExternalWorkflowExecution), ChildWorkflowOnly: t.ChildWorkflowOnly, } } func ToHistorySignalWorkflowExecutionRequest(t *historyv1.SignalWorkflowExecutionRequest) *types.HistorySignalWorkflowExecutionRequest { if t == nil { return nil } return &types.HistorySignalWorkflowExecutionRequest{ SignalRequest: ToSignalWorkflowExecutionRequest(t.Request), DomainUUID: t.DomainId, ExternalWorkflowExecution: ToWorkflowExecution(t.ExternalWorkflowExecution), ChildWorkflowOnly: t.ChildWorkflowOnly, } } func FromHistoryStartWorkflowExecutionRequest(t *types.HistoryStartWorkflowExecutionRequest) *historyv1.StartWorkflowExecutionRequest { if t == nil { return nil } return &historyv1.StartWorkflowExecutionRequest{ Request: FromStartWorkflowExecutionRequest(t.StartRequest), DomainId: t.DomainUUID, ParentExecutionInfo: FromParentExecutionInfo(t.ParentExecutionInfo), Attempt: t.Attempt, ExpirationTime: unixNanoToTime(t.ExpirationTimestamp), ContinueAsNewInitiator: FromContinueAsNewInitiator(t.ContinueAsNewInitiator), ContinuedFailure: FromFailure(t.ContinuedFailureReason, t.ContinuedFailureDetails), LastCompletionResult: FromPayload(t.LastCompletionResult), FirstDecisionTaskBackoff: secondsToDuration(t.FirstDecisionTaskBackoffSeconds), PartitionConfig: t.PartitionConfig, } } func ToHistoryStartWorkflowExecutionRequest(t *historyv1.StartWorkflowExecutionRequest) *types.HistoryStartWorkflowExecutionRequest { if t == nil { return nil } return &types.HistoryStartWorkflowExecutionRequest{ StartRequest: ToStartWorkflowExecutionRequest(t.Request), DomainUUID: t.DomainId, ParentExecutionInfo: ToParentExecutionInfo(t.ParentExecutionInfo), Attempt: t.Attempt, ExpirationTimestamp: timeToUnixNano(t.ExpirationTime), ContinueAsNewInitiator: ToContinueAsNewInitiator(t.ContinueAsNewInitiator), ContinuedFailureReason: ToFailureReason(t.ContinuedFailure), ContinuedFailureDetails: ToFailureDetails(t.ContinuedFailure), LastCompletionResult: ToPayload(t.LastCompletionResult), FirstDecisionTaskBackoffSeconds: durationToSeconds(t.FirstDecisionTaskBackoff), PartitionConfig: t.PartitionConfig, } } func FromHistoryStartWorkflowExecutionResponse(t *types.StartWorkflowExecutionResponse) *historyv1.StartWorkflowExecutionResponse { if t == nil { return nil } return &historyv1.StartWorkflowExecutionResponse{ RunId: t.RunID, } } func ToHistoryStartWorkflowExecutionResponse(t *historyv1.StartWorkflowExecutionResponse) *types.StartWorkflowExecutionResponse { if t == nil { return nil } return &types.StartWorkflowExecutionResponse{ RunID: t.RunId, } } func FromHistorySyncActivityRequest(t *types.SyncActivityRequest) *historyv1.SyncActivityRequest { if t == nil { return nil } return &historyv1.SyncActivityRequest{ DomainId: t.DomainID, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), Version: t.Version, ScheduledId: t.ScheduledID, ScheduledTime: unixNanoToTime(t.ScheduledTime), StartedId: t.StartedID, StartedTime: unixNanoToTime(t.StartedTime), LastHeartbeatTime: unixNanoToTime(t.LastHeartbeatTime), Details: FromPayload(t.Details), Attempt: t.Attempt, LastFailure: FromFailure(t.LastFailureReason, t.LastFailureDetails), LastWorkerIdentity: t.LastWorkerIdentity, VersionHistory: FromVersionHistory(t.VersionHistory), } } func ToHistorySyncActivityRequest(t *historyv1.SyncActivityRequest) *types.SyncActivityRequest { if t == nil { return nil } return &types.SyncActivityRequest{ DomainID: t.DomainId, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), Version: t.Version, ScheduledID: t.ScheduledId, ScheduledTime: timeToUnixNano(t.ScheduledTime), StartedID: t.StartedId, StartedTime: timeToUnixNano(t.StartedTime), LastHeartbeatTime: timeToUnixNano(t.LastHeartbeatTime), Details: ToPayload(t.Details), Attempt: t.Attempt, LastFailureReason: ToFailureReason(t.LastFailure), LastFailureDetails: ToFailureDetails(t.LastFailure), LastWorkerIdentity: t.LastWorkerIdentity, VersionHistory: ToVersionHistory(t.VersionHistory), } } func FromHistorySyncShardStatusRequest(t *types.SyncShardStatusRequest) *historyv1.SyncShardStatusRequest { if t == nil { return nil } return &historyv1.SyncShardStatusRequest{ SourceCluster: t.SourceCluster, ShardId: int32(t.ShardID), Time: unixNanoToTime(t.Timestamp), } } func ToHistorySyncShardStatusRequest(t *historyv1.SyncShardStatusRequest) *types.SyncShardStatusRequest { if t == nil { return nil } return &types.SyncShardStatusRequest{ SourceCluster: t.SourceCluster, ShardID: int64(t.ShardId), Timestamp: timeToUnixNano(t.Time), } } func FromHistoryTerminateWorkflowExecutionRequest(t *types.HistoryTerminateWorkflowExecutionRequest) *historyv1.TerminateWorkflowExecutionRequest { if t == nil { return nil } return &historyv1.TerminateWorkflowExecutionRequest{ Request: FromTerminateWorkflowExecutionRequest(t.TerminateRequest), DomainId: t.DomainUUID, ExternalWorkflowExecution: FromWorkflowExecution(t.ExternalWorkflowExecution), ChildWorkflowOnly: t.ChildWorkflowOnly, } } func ToHistoryTerminateWorkflowExecutionRequest(t *historyv1.TerminateWorkflowExecutionRequest) *types.HistoryTerminateWorkflowExecutionRequest { if t == nil { return nil } return &types.HistoryTerminateWorkflowExecutionRequest{ TerminateRequest: ToTerminateWorkflowExecutionRequest(t.Request), DomainUUID: t.DomainId, ExternalWorkflowExecution: ToWorkflowExecution(t.ExternalWorkflowExecution), ChildWorkflowOnly: t.ChildWorkflowOnly, } } // FromHistoryGetCrossClusterTasksRequest converts internal GetCrossClusterTasksRequest type to proto func FromHistoryGetCrossClusterTasksRequest(t *types.GetCrossClusterTasksRequest) *historyv1.GetCrossClusterTasksRequest { if t == nil { return nil } return &historyv1.GetCrossClusterTasksRequest{ ShardIds: t.ShardIDs, TargetCluster: t.TargetCluster, } } // ToHistoryGetCrossClusterTasksRequest converts proto GetCrossClusterTasksRequest type to internal func ToHistoryGetCrossClusterTasksRequest(t *historyv1.GetCrossClusterTasksRequest) *types.GetCrossClusterTasksRequest { if t == nil { return nil } return &types.GetCrossClusterTasksRequest{ ShardIDs: t.ShardIds, TargetCluster: t.TargetCluster, } } // FromHistoryGetCrossClusterTasksResponse converts internal GetCrossClusterTasksResponse type to proto func FromHistoryGetCrossClusterTasksResponse(t *types.GetCrossClusterTasksResponse) *historyv1.GetCrossClusterTasksResponse { if t == nil { return nil } return &historyv1.GetCrossClusterTasksResponse{ TasksByShard: FromCrossClusterTaskRequestMap(t.TasksByShard), FailedCauseByShard: FromGetTaskFailedCauseMap(t.FailedCauseByShard), } } // ToHistoryGetCrossClusterTasksResponse converts proto GetCrossClusterTasksResponse type to internal func ToHistoryGetCrossClusterTasksResponse(t *historyv1.GetCrossClusterTasksResponse) *types.GetCrossClusterTasksResponse { if t == nil { return nil } return &types.GetCrossClusterTasksResponse{ TasksByShard: ToCrossClusterTaskRequestMap(t.TasksByShard), FailedCauseByShard: ToGetTaskFailedCauseMap(t.FailedCauseByShard), } } // FromHistoryRespondCrossClusterTasksCompletedRequest converts internal RespondCrossClusterTasksCompletedRequest type to thrift func FromHistoryRespondCrossClusterTasksCompletedRequest(t *types.RespondCrossClusterTasksCompletedRequest) *historyv1.RespondCrossClusterTasksCompletedRequest { if t == nil { return nil } return &historyv1.RespondCrossClusterTasksCompletedRequest{ ShardId: t.ShardID, TargetCluster: t.TargetCluster, TaskResponses: FromCrossClusterTaskResponseArray(t.TaskResponses), FetchNewTasks: t.FetchNewTasks, } } // ToHistoryRespondCrossClusterTasksCompletedRequest converts thrift RespondCrossClusterTasksCompletedRequest type to internal func ToHistoryRespondCrossClusterTasksCompletedRequest(t *historyv1.RespondCrossClusterTasksCompletedRequest) *types.RespondCrossClusterTasksCompletedRequest { if t == nil { return nil } return &types.RespondCrossClusterTasksCompletedRequest{ ShardID: t.ShardId, TargetCluster: t.TargetCluster, TaskResponses: ToCrossClusterTaskResponseArray(t.TaskResponses), FetchNewTasks: t.FetchNewTasks, } } // FromHistoryRespondCrossClusterTasksCompletedResponse converts internal RespondCrossClusterTasksCompletedResponse type to thrift func FromHistoryRespondCrossClusterTasksCompletedResponse(t *types.RespondCrossClusterTasksCompletedResponse) *historyv1.RespondCrossClusterTasksCompletedResponse { if t == nil { return nil } return &historyv1.RespondCrossClusterTasksCompletedResponse{ Tasks: FromCrossClusterTaskRequestArray(t.Tasks), } } // ToHistoryRespondCrossClusterTasksCompletedResponse converts thrift RespondCrossClusterTasksCompletedResponse type to internal func ToHistoryRespondCrossClusterTasksCompletedResponse(t *historyv1.RespondCrossClusterTasksCompletedResponse) *types.RespondCrossClusterTasksCompletedResponse { if t == nil { return nil } return &types.RespondCrossClusterTasksCompletedResponse{ Tasks: ToCrossClusterTaskRequestArray(t.Tasks), } } func FromHistoryResetStickyTaskListResponse(t *types.HistoryResetStickyTaskListResponse) *historyv1.ResetStickyTaskListResponse { if t == nil { return nil } return &historyv1.ResetStickyTaskListResponse{} } func ToHistoryResetStickyTaskListResponse(t *historyv1.ResetStickyTaskListResponse) *types.HistoryResetStickyTaskListResponse { if t == nil { return nil } return &types.HistoryResetStickyTaskListResponse{} } func FromHistoryRatelimitUpdateRequest(t *types.RatelimitUpdateRequest) *historyv1.RatelimitUpdateRequest { if t == nil { return nil } return &historyv1.RatelimitUpdateRequest{ Data: FromAny(t.Any), } } func ToHistoryRatelimitUpdateRequest(t *historyv1.RatelimitUpdateRequest) *types.RatelimitUpdateRequest { if t == nil { return nil } return &types.RatelimitUpdateRequest{ Any: ToAny(t.Data), } } func FromHistoryRatelimitUpdateResponse(t *types.RatelimitUpdateResponse) *historyv1.RatelimitUpdateResponse { if t == nil { return nil } return &historyv1.RatelimitUpdateResponse{ Data: FromAny(t.Any), } } func ToHistoryRatelimitUpdateResponse(t *historyv1.RatelimitUpdateResponse) *types.RatelimitUpdateResponse { if t == nil { return nil } return &types.RatelimitUpdateResponse{ Any: ToAny(t.Data), } } ================================================ FILE: common/types/mapper/proto/history_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "testing" "github.com/stretchr/testify/assert" historyv1 "github.com/uber/cadence/.gen/proto/history/v1" sharedv1 "github.com/uber/cadence/.gen/proto/shared/v1" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" ) func TestHistoryCloseShardRequest(t *testing.T) { for _, item := range []*types.CloseShardRequest{nil, {}, &testdata.HistoryCloseShardRequest} { assert.Equal(t, item, ToHistoryCloseShardRequest(FromHistoryCloseShardRequest(item))) } } func TestHistoryDescribeHistoryHostRequest(t *testing.T) { for _, item := range []*types.DescribeHistoryHostRequest{nil, {}, &testdata.HistoryDescribeHistoryHostRequest} { assert.Equal(t, item, ToHistoryDescribeHistoryHostRequest(FromHistoryDescribeHistoryHostRequest(item))) } } func TestHistoryDescribeHistoryHostResponse(t *testing.T) { for _, item := range []*types.DescribeHistoryHostResponse{nil, {}, &testdata.HistoryDescribeHistoryHostResponse} { assert.Equal(t, item, ToHistoryDescribeHistoryHostResponse(FromHistoryDescribeHistoryHostResponse(item))) } } func TestHistoryDescribeMutableStateRequest(t *testing.T) { for _, item := range []*types.DescribeMutableStateRequest{nil, {}, &testdata.HistoryDescribeMutableStateRequest} { assert.Equal(t, item, ToHistoryDescribeMutableStateRequest(FromHistoryDescribeMutableStateRequest(item))) } } func TestHistoryDescribeMutableStateResponse(t *testing.T) { for _, item := range []*types.DescribeMutableStateResponse{nil, {}, &testdata.HistoryDescribeMutableStateResponse} { assert.Equal(t, item, ToHistoryDescribeMutableStateResponse(FromHistoryDescribeMutableStateResponse(item))) } } func TestHistoryDescribeQueueRequest(t *testing.T) { for _, item := range []*types.DescribeQueueRequest{nil, {}, &testdata.HistoryDescribeQueueRequest} { assert.Equal(t, item, ToHistoryDescribeQueueRequest(FromHistoryDescribeQueueRequest(item))) } } func TestHistoryDescribeQueueResponse(t *testing.T) { for _, item := range []*types.DescribeQueueResponse{nil, {}, &testdata.HistoryDescribeQueueResponse} { assert.Equal(t, item, ToHistoryDescribeQueueResponse(FromHistoryDescribeQueueResponse(item))) } } func TestHistoryDescribeWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.HistoryDescribeWorkflowExecutionRequest{nil, {}, &testdata.HistoryDescribeWorkflowExecutionRequest} { assert.Equal(t, item, ToHistoryDescribeWorkflowExecutionRequest(FromHistoryDescribeWorkflowExecutionRequest(item))) } } func TestHistoryDescribeWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.DescribeWorkflowExecutionResponse{nil, {}, &testdata.HistoryDescribeWorkflowExecutionResponse} { assert.Equal(t, item, ToHistoryDescribeWorkflowExecutionResponse(FromHistoryDescribeWorkflowExecutionResponse(item))) } } func TestHistoryGetDLQReplicationMessagesRequest(t *testing.T) { for _, item := range []*types.GetDLQReplicationMessagesRequest{nil, {}, &testdata.HistoryGetDLQReplicationMessagesRequest} { assert.Equal(t, item, ToHistoryGetDLQReplicationMessagesRequest(FromHistoryGetDLQReplicationMessagesRequest(item))) } } func TestHistoryGetDLQReplicationMessagesResponse(t *testing.T) { for _, item := range []*types.GetDLQReplicationMessagesResponse{nil, {}, &testdata.HistoryGetDLQReplicationMessagesResponse} { assert.Equal(t, item, ToHistoryGetDLQReplicationMessagesResponse(FromHistoryGetDLQReplicationMessagesResponse(item))) } } func TestHistoryGetMutableStateRequest(t *testing.T) { for _, item := range []*types.GetMutableStateRequest{nil, {}, &testdata.HistoryGetMutableStateRequest} { assert.Equal(t, item, ToHistoryGetMutableStateRequest(FromHistoryGetMutableStateRequest(item))) } } func TestHistoryGetMutableStateResponse(t *testing.T) { for _, item := range []*types.GetMutableStateResponse{nil, &testdata.HistoryGetMutableStateResponse} { assert.Equal(t, item, ToHistoryGetMutableStateResponse(FromHistoryGetMutableStateResponse(item))) } } func TestHistoryGetReplicationMessagesRequest(t *testing.T) { for _, item := range []*types.GetReplicationMessagesRequest{nil, {}, &testdata.HistoryGetReplicationMessagesRequest} { assert.Equal(t, item, ToHistoryGetReplicationMessagesRequest(FromHistoryGetReplicationMessagesRequest(item))) } } func TestHistoryGetReplicationMessagesResponse(t *testing.T) { for _, item := range []*types.GetReplicationMessagesResponse{nil, {}, &testdata.HistoryGetReplicationMessagesResponse} { assert.Equal(t, item, ToHistoryGetReplicationMessagesResponse(FromHistoryGetReplicationMessagesResponse(item))) } } func TestHistoryCountDLQMessagesRequest(t *testing.T) { for _, item := range []*types.CountDLQMessagesRequest{nil, {}, &testdata.HistoryCountDLQMessagesRequest} { assert.Equal(t, item, ToHistoryCountDLQMessagesRequest(FromHistoryCountDLQMessagesRequest(item))) } } func TestHistoryCountDLQMessagesResponse(t *testing.T) { for _, item := range []*types.HistoryCountDLQMessagesResponse{nil, {}, &testdata.HistoryCountDLQMessagesResponse} { assert.Equal(t, item, ToHistoryCountDLQMessagesResponse(FromHistoryCountDLQMessagesResponse(item))) } } func TestHistoryMergeDLQMessagesRequest(t *testing.T) { for _, item := range []*types.MergeDLQMessagesRequest{nil, {}, &testdata.HistoryMergeDLQMessagesRequest} { assert.Equal(t, item, ToHistoryMergeDLQMessagesRequest(FromHistoryMergeDLQMessagesRequest(item))) } } func TestHistoryMergeDLQMessagesResponse(t *testing.T) { for _, item := range []*types.MergeDLQMessagesResponse{nil, {}, &testdata.HistoryMergeDLQMessagesResponse} { assert.Equal(t, item, ToHistoryMergeDLQMessagesResponse(FromHistoryMergeDLQMessagesResponse(item))) } } func TestHistoryNotifyFailoverMarkersRequest(t *testing.T) { for _, item := range []*types.NotifyFailoverMarkersRequest{nil, {}, &testdata.HistoryNotifyFailoverMarkersRequest} { assert.Equal(t, item, ToHistoryNotifyFailoverMarkersRequest(FromHistoryNotifyFailoverMarkersRequest(item))) } } func TestHistoryPollMutableStateRequest(t *testing.T) { for _, item := range []*types.PollMutableStateRequest{nil, {}, &testdata.HistoryPollMutableStateRequest} { assert.Equal(t, item, ToHistoryPollMutableStateRequest(FromHistoryPollMutableStateRequest(item))) } } func TestHistoryPollMutableStateResponse(t *testing.T) { for _, item := range []*types.PollMutableStateResponse{nil, &testdata.HistoryPollMutableStateResponse} { assert.Equal(t, item, ToHistoryPollMutableStateResponse(FromHistoryPollMutableStateResponse(item))) } } func TestHistoryPurgeDLQMessagesRequest(t *testing.T) { for _, item := range []*types.PurgeDLQMessagesRequest{nil, {}, &testdata.HistoryPurgeDLQMessagesRequest} { assert.Equal(t, item, ToHistoryPurgeDLQMessagesRequest(FromHistoryPurgeDLQMessagesRequest(item))) } } func TestHistoryQueryWorkflowRequest(t *testing.T) { for _, item := range []*types.HistoryQueryWorkflowRequest{nil, {}, &testdata.HistoryQueryWorkflowRequest} { assert.Equal(t, item, ToHistoryQueryWorkflowRequest(FromHistoryQueryWorkflowRequest(item))) } } func TestHistoryQueryWorkflowResponse(t *testing.T) { for _, item := range []*types.HistoryQueryWorkflowResponse{nil, &testdata.HistoryQueryWorkflowResponse} { assert.Equal(t, item, ToHistoryQueryWorkflowResponse(FromHistoryQueryWorkflowResponse(item))) } assert.Nil(t, FromHistoryQueryWorkflowResponse(&types.HistoryQueryWorkflowResponse{})) } func TestHistoryReadDLQMessagesRequest(t *testing.T) { for _, item := range []*types.ReadDLQMessagesRequest{nil, {}, &testdata.HistoryReadDLQMessagesRequest} { assert.Equal(t, item, ToHistoryReadDLQMessagesRequest(FromHistoryReadDLQMessagesRequest(item))) } } func TestHistoryReadDLQMessagesResponse(t *testing.T) { for _, item := range []*types.ReadDLQMessagesResponse{nil, {}, &testdata.HistoryReadDLQMessagesResponse} { assert.Equal(t, item, ToHistoryReadDLQMessagesResponse(FromHistoryReadDLQMessagesResponse(item))) } } func TestHistoryReapplyEventsRequest(t *testing.T) { for _, item := range []*types.HistoryReapplyEventsRequest{nil, &testdata.HistoryReapplyEventsRequest} { assert.Equal(t, item, ToHistoryReapplyEventsRequest(FromHistoryReapplyEventsRequest(item))) } assert.Nil(t, FromHistoryReapplyEventsRequest(&types.HistoryReapplyEventsRequest{})) } func TestHistoryRecordActivityTaskHeartbeatRequest(t *testing.T) { for _, item := range []*types.HistoryRecordActivityTaskHeartbeatRequest{nil, {}, &testdata.HistoryRecordActivityTaskHeartbeatRequest} { assert.Equal(t, item, ToHistoryRecordActivityTaskHeartbeatRequest(FromHistoryRecordActivityTaskHeartbeatRequest(item))) } } func TestHistoryRecordActivityTaskHeartbeatResponse(t *testing.T) { for _, item := range []*types.RecordActivityTaskHeartbeatResponse{nil, {}, &testdata.HistoryRecordActivityTaskHeartbeatResponse} { assert.Equal(t, item, ToHistoryRecordActivityTaskHeartbeatResponse(FromHistoryRecordActivityTaskHeartbeatResponse(item))) } } func TestHistoryRecordActivityTaskStartedRequest(t *testing.T) { for _, item := range []*types.RecordActivityTaskStartedRequest{nil, {}, &testdata.HistoryRecordActivityTaskStartedRequest} { assert.Equal(t, item, ToHistoryRecordActivityTaskStartedRequest(FromHistoryRecordActivityTaskStartedRequest(item))) } } func TestHistoryRecordActivityTaskStartedResponse(t *testing.T) { for _, item := range []*types.RecordActivityTaskStartedResponse{nil, {}, &testdata.HistoryRecordActivityTaskStartedResponse} { assert.Equal(t, item, ToHistoryRecordActivityTaskStartedResponse(FromHistoryRecordActivityTaskStartedResponse(item))) } } func TestHistoryRecordChildExecutionCompletedRequest(t *testing.T) { for _, item := range []*types.RecordChildExecutionCompletedRequest{nil, {}, &testdata.HistoryRecordChildExecutionCompletedRequest} { assert.Equal(t, item, ToHistoryRecordChildExecutionCompletedRequest(FromHistoryRecordChildExecutionCompletedRequest(item))) } } func TestHistoryRecordDecisionTaskStartedRequest(t *testing.T) { for _, item := range []*types.RecordDecisionTaskStartedRequest{nil, {}, &testdata.HistoryRecordDecisionTaskStartedRequest} { assert.Equal(t, item, ToHistoryRecordDecisionTaskStartedRequest(FromHistoryRecordDecisionTaskStartedRequest(item))) } } func TestHistoryRecordDecisionTaskStartedResponse(t *testing.T) { for _, item := range []*types.RecordDecisionTaskStartedResponse{nil, {}, &testdata.HistoryRecordDecisionTaskStartedResponse} { assert.Equal(t, item, ToHistoryRecordDecisionTaskStartedResponse(FromHistoryRecordDecisionTaskStartedResponse(item))) } } func TestHistoryRefreshWorkflowTasksRequest(t *testing.T) { for _, item := range []*types.HistoryRefreshWorkflowTasksRequest{nil, &testdata.HistoryRefreshWorkflowTasksRequest} { assert.Equal(t, item, ToHistoryRefreshWorkflowTasksRequest(FromHistoryRefreshWorkflowTasksRequest(item))) } assert.Nil(t, FromHistoryRefreshWorkflowTasksRequest(&types.HistoryRefreshWorkflowTasksRequest{})) } func TestHistoryRemoveSignalMutableStateRequest(t *testing.T) { for _, item := range []*types.RemoveSignalMutableStateRequest{nil, {}, &testdata.HistoryRemoveSignalMutableStateRequest} { assert.Equal(t, item, ToHistoryRemoveSignalMutableStateRequest(FromHistoryRemoveSignalMutableStateRequest(item))) } } func TestHistoryRemoveTaskRequest(t *testing.T) { for _, item := range []*types.RemoveTaskRequest{nil, {}, &testdata.HistoryRemoveTaskRequest} { assert.Equal(t, item, ToHistoryRemoveTaskRequest(FromHistoryRemoveTaskRequest(item))) } } func TestHistoryReplicateEventsV2Request(t *testing.T) { for _, item := range []*types.ReplicateEventsV2Request{nil, {}, &testdata.HistoryReplicateEventsV2Request} { assert.Equal(t, item, ToHistoryReplicateEventsV2Request(FromHistoryReplicateEventsV2Request(item))) } } func TestHistoryRequestCancelWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.HistoryRequestCancelWorkflowExecutionRequest{nil, {}, &testdata.HistoryRequestCancelWorkflowExecutionRequest} { assert.Equal(t, item, ToHistoryRequestCancelWorkflowExecutionRequest(FromHistoryRequestCancelWorkflowExecutionRequest(item))) } } func TestHistoryResetQueueRequest(t *testing.T) { for _, item := range []*types.ResetQueueRequest{nil, {}, &testdata.HistoryResetQueueRequest} { assert.Equal(t, item, ToHistoryResetQueueRequest(FromHistoryResetQueueRequest(item))) } } func TestHistoryResetStickyTaskListRequest(t *testing.T) { for _, item := range []*types.HistoryResetStickyTaskListRequest{nil, {}, &testdata.HistoryResetStickyTaskListRequest} { assert.Equal(t, item, ToHistoryResetStickyTaskListRequest(FromHistoryResetStickyTaskListRequest(item))) } } func TestHistoryResetWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.HistoryResetWorkflowExecutionRequest{nil, {}, &testdata.HistoryResetWorkflowExecutionRequest} { assert.Equal(t, item, ToHistoryResetWorkflowExecutionRequest(FromHistoryResetWorkflowExecutionRequest(item))) } } func TestHistoryResetWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.ResetWorkflowExecutionResponse{nil, {}, &testdata.HistoryResetWorkflowExecutionResponse} { assert.Equal(t, item, ToHistoryResetWorkflowExecutionResponse(FromHistoryResetWorkflowExecutionResponse(item))) } } func TestHistoryRespondActivityTaskCanceledRequest(t *testing.T) { for _, item := range []*types.HistoryRespondActivityTaskCanceledRequest{nil, {}, &testdata.HistoryRespondActivityTaskCanceledRequest} { assert.Equal(t, item, ToHistoryRespondActivityTaskCanceledRequest(FromHistoryRespondActivityTaskCanceledRequest(item))) } } func TestHistoryRespondActivityTaskCompletedRequest(t *testing.T) { for _, item := range []*types.HistoryRespondActivityTaskCompletedRequest{nil, {}, &testdata.HistoryRespondActivityTaskCompletedRequest} { assert.Equal(t, item, ToHistoryRespondActivityTaskCompletedRequest(FromHistoryRespondActivityTaskCompletedRequest(item))) } } func TestHistoryRespondActivityTaskFailedRequest(t *testing.T) { for _, item := range []*types.HistoryRespondActivityTaskFailedRequest{nil, {}, &testdata.HistoryRespondActivityTaskFailedRequest} { assert.Equal(t, item, ToHistoryRespondActivityTaskFailedRequest(FromHistoryRespondActivityTaskFailedRequest(item))) } } func TestHistoryRespondDecisionTaskCompletedRequest(t *testing.T) { for _, item := range []*types.HistoryRespondDecisionTaskCompletedRequest{nil, {}, &testdata.HistoryRespondDecisionTaskCompletedRequest} { assert.Equal(t, item, ToHistoryRespondDecisionTaskCompletedRequest(FromHistoryRespondDecisionTaskCompletedRequest(item))) } } func TestHistoryRespondDecisionTaskCompletedResponse(t *testing.T) { for _, item := range []*types.HistoryRespondDecisionTaskCompletedResponse{nil, {}, &testdata.HistoryRespondDecisionTaskCompletedResponse} { assert.Equal(t, item, ToHistoryRespondDecisionTaskCompletedResponse(FromHistoryRespondDecisionTaskCompletedResponse(item))) } } func TestHistoryRespondDecisionTaskFailedRequest(t *testing.T) { for _, item := range []*types.HistoryRespondDecisionTaskFailedRequest{nil, {}, &testdata.HistoryRespondDecisionTaskFailedRequest} { assert.Equal(t, item, ToHistoryRespondDecisionTaskFailedRequest(FromHistoryRespondDecisionTaskFailedRequest(item))) } } func TestHistoryScheduleDecisionTaskRequest(t *testing.T) { for _, item := range []*types.ScheduleDecisionTaskRequest{nil, {}, &testdata.HistoryScheduleDecisionTaskRequest} { assert.Equal(t, item, ToHistoryScheduleDecisionTaskRequest(FromHistoryScheduleDecisionTaskRequest(item))) } } func TestHistorySignalWithStartWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.HistorySignalWithStartWorkflowExecutionRequest{nil, {}, &testdata.HistorySignalWithStartWorkflowExecutionRequest} { assert.Equal(t, item, ToHistorySignalWithStartWorkflowExecutionRequest(FromHistorySignalWithStartWorkflowExecutionRequest(item))) } } func TestHistorySignalWithStartWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.StartWorkflowExecutionResponse{nil, {}, &testdata.HistorySignalWithStartWorkflowExecutionResponse} { assert.Equal(t, item, ToHistorySignalWithStartWorkflowExecutionResponse(FromHistorySignalWithStartWorkflowExecutionResponse(item))) } } func TestHistorySignalWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.HistorySignalWorkflowExecutionRequest{nil, {}, &testdata.HistorySignalWorkflowExecutionRequest} { assert.Equal(t, item, ToHistorySignalWorkflowExecutionRequest(FromHistorySignalWorkflowExecutionRequest(item))) } } func TestHistoryStartWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.HistoryStartWorkflowExecutionRequest{nil, {}, &testdata.HistoryStartWorkflowExecutionRequest} { assert.Equal(t, item, ToHistoryStartWorkflowExecutionRequest(FromHistoryStartWorkflowExecutionRequest(item))) } } func TestHistoryStartWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.StartWorkflowExecutionResponse{nil, {}, &testdata.HistoryStartWorkflowExecutionResponse} { assert.Equal(t, item, ToHistoryStartWorkflowExecutionResponse(FromHistoryStartWorkflowExecutionResponse(item))) } } func TestHistorySyncActivityRequest(t *testing.T) { for _, item := range []*types.SyncActivityRequest{nil, {}, &testdata.HistorySyncActivityRequest} { assert.Equal(t, item, ToHistorySyncActivityRequest(FromHistorySyncActivityRequest(item))) } } func TestHistorySyncShardStatusRequest(t *testing.T) { for _, item := range []*types.SyncShardStatusRequest{nil, {}, &testdata.HistorySyncShardStatusRequest} { assert.Equal(t, item, ToHistorySyncShardStatusRequest(FromHistorySyncShardStatusRequest(item))) } } func TestHistoryTerminateWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.HistoryTerminateWorkflowExecutionRequest{nil, {}, &testdata.HistoryTerminateWorkflowExecutionRequest} { assert.Equal(t, item, ToHistoryTerminateWorkflowExecutionRequest(FromHistoryTerminateWorkflowExecutionRequest(item))) } } func TestHistoryGetCrossClusterTasksRequest(t *testing.T) { for _, item := range []*types.GetCrossClusterTasksRequest{nil, {}, &testdata.HistoryGetCrossClusterTasksRequest} { assert.Equal(t, item, ToHistoryGetCrossClusterTasksRequest(FromHistoryGetCrossClusterTasksRequest(item))) } } func TestHistoryGetCrossClusterTasksResponse(t *testing.T) { for _, item := range []*types.GetCrossClusterTasksResponse{nil, {}, &testdata.HistoryGetCrossClusterTasksResponse} { assert.Equal(t, item, ToHistoryGetCrossClusterTasksResponse(FromHistoryGetCrossClusterTasksResponse(item))) } } func TestHistoryRespondCrossClusterTasksCompletedRequest(t *testing.T) { for _, item := range []*types.RespondCrossClusterTasksCompletedRequest{nil, {}, &testdata.HistoryRespondCrossClusterTasksCompletedRequest} { assert.Equal(t, item, ToHistoryRespondCrossClusterTasksCompletedRequest(FromHistoryRespondCrossClusterTasksCompletedRequest(item))) } } func TestHistoryRespondCrossClusterTasksCompletedResponse(t *testing.T) { for _, item := range []*types.RespondCrossClusterTasksCompletedResponse{nil, {}, &testdata.HistoryRespondCrossClusterTasksCompletedResponse} { assert.Equal(t, item, ToHistoryRespondCrossClusterTasksCompletedResponse(FromHistoryRespondCrossClusterTasksCompletedResponse(item))) } } func TestHistoryGetTaskInfoRequest(t *testing.T) { for _, item := range []*types.GetFailoverInfoRequest{nil, {}, &testdata.GetFailoverInfoRequest} { assert.Equal(t, item, ToHistoryGetFailoverInfoRequest(FromHistoryGetFailoverInfoRequest(item))) } } func TestHistoryGetTaskInfoResponse(t *testing.T) { for _, item := range []*types.GetFailoverInfoResponse{nil, {}, &testdata.GetFailoverInfoResponse} { assert.Equal(t, item, ToHistoryGetFailoverInfoResponse(FromHistoryGetFailoverInfoResponse(item))) } } func TestRatelimitUpdate(t *testing.T) { t.Run("round trip", func(t *testing.T) { for _, item := range []*types.RatelimitUpdateResponse{nil, {}, &testdata.RatelimitUpdateResponse} { assert.Equal(t, item, ToHistoryRatelimitUpdateResponse(FromHistoryRatelimitUpdateResponse(item))) } for _, item := range []*types.RatelimitUpdateRequest{nil, {}, &testdata.RatelimitUpdateRequest} { assert.Equal(t, item, ToHistoryRatelimitUpdateRequest(FromHistoryRatelimitUpdateRequest(item))) } }) t.Run("nil", func(t *testing.T) { assert.Nil(t, ToHistoryRatelimitUpdateRequest(nil), "request to internal") assert.Nil(t, FromHistoryRatelimitUpdateResponse(nil), "response from internal") }) t.Run("nil Any contents", func(t *testing.T) { assert.Equal(t, &types.RatelimitUpdateRequest{Any: nil}, ToHistoryRatelimitUpdateRequest(&historyv1.RatelimitUpdateRequest{Data: nil}), "request to internal") assert.Equal(t, &historyv1.RatelimitUpdateResponse{Data: nil}, FromHistoryRatelimitUpdateResponse(&types.RatelimitUpdateResponse{Any: nil}), "response from internal") }) t.Run("with Any contents", func(t *testing.T) { internal := &types.Any{ValueType: "test", Value: []byte(`test data`)} proto := &sharedv1.Any{ValueType: "test", Value: []byte(`test data`)} assert.Equal(t, &types.RatelimitUpdateRequest{Any: internal}, ToHistoryRatelimitUpdateRequest(&historyv1.RatelimitUpdateRequest{Data: proto}), "request to internal") assert.Equal(t, &historyv1.RatelimitUpdateResponse{Data: proto}, FromHistoryRatelimitUpdateResponse(&types.RatelimitUpdateResponse{Any: internal}), "response from internal") }) } ================================================ FILE: common/types/mapper/proto/matching.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( matchingv1 "github.com/uber/cadence/.gen/proto/matching/v1" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) func FromMatchingAddActivityTaskRequest(t *types.AddActivityTaskRequest) *matchingv1.AddActivityTaskRequest { if t == nil { return nil } return &matchingv1.AddActivityTaskRequest{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.Execution), SourceDomainId: t.SourceDomainUUID, TaskList: FromTaskList(t.TaskList), ScheduleId: t.ScheduleID, ScheduleToStartTimeout: secondsToDuration(t.ScheduleToStartTimeoutSeconds), Source: FromTaskSource(t.Source), ForwardedFrom: t.ForwardedFrom, ActivityTaskDispatchInfo: FromActivityTaskDispatchInfo(t.ActivityTaskDispatchInfo), PartitionConfig: t.PartitionConfig, } } func ToMatchingAddActivityTaskRequest(t *matchingv1.AddActivityTaskRequest) *types.AddActivityTaskRequest { if t == nil { return nil } return &types.AddActivityTaskRequest{ DomainUUID: t.DomainId, Execution: ToWorkflowExecution(t.WorkflowExecution), SourceDomainUUID: t.SourceDomainId, TaskList: ToTaskList(t.TaskList), ScheduleID: t.ScheduleId, ScheduleToStartTimeoutSeconds: durationToSeconds(t.ScheduleToStartTimeout), Source: ToTaskSource(t.Source), ForwardedFrom: t.ForwardedFrom, ActivityTaskDispatchInfo: ToActivityTaskDispatchInfo(t.ActivityTaskDispatchInfo), PartitionConfig: t.PartitionConfig, } } func FromMatchingAddDecisionTaskRequest(t *types.AddDecisionTaskRequest) *matchingv1.AddDecisionTaskRequest { if t == nil { return nil } return &matchingv1.AddDecisionTaskRequest{ DomainId: t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.Execution), TaskList: FromTaskList(t.TaskList), ScheduleId: t.ScheduleID, ScheduleToStartTimeout: secondsToDuration(t.ScheduleToStartTimeoutSeconds), Source: FromTaskSource(t.Source), ForwardedFrom: t.ForwardedFrom, PartitionConfig: t.PartitionConfig, } } func ToMatchingAddDecisionTaskRequest(t *matchingv1.AddDecisionTaskRequest) *types.AddDecisionTaskRequest { if t == nil { return nil } return &types.AddDecisionTaskRequest{ DomainUUID: t.DomainId, Execution: ToWorkflowExecution(t.WorkflowExecution), TaskList: ToTaskList(t.TaskList), ScheduleID: t.ScheduleId, ScheduleToStartTimeoutSeconds: durationToSeconds(t.ScheduleToStartTimeout), Source: ToTaskSource(t.Source), ForwardedFrom: t.ForwardedFrom, PartitionConfig: t.PartitionConfig, } } func FromMatchingAddActivityTaskResponse(t *types.AddActivityTaskResponse) *matchingv1.AddActivityTaskResponse { if t == nil { return nil } return &matchingv1.AddActivityTaskResponse{ PartitionConfig: FromTaskListPartitionConfig(t.PartitionConfig), } } func ToMatchingAddActivityTaskResponse(t *matchingv1.AddActivityTaskResponse) *types.AddActivityTaskResponse { if t == nil { return nil } return &types.AddActivityTaskResponse{ PartitionConfig: ToTaskListPartitionConfig(t.PartitionConfig), } } func FromMatchingAddDecisionTaskResponse(t *types.AddDecisionTaskResponse) *matchingv1.AddDecisionTaskResponse { if t == nil { return nil } return &matchingv1.AddDecisionTaskResponse{ PartitionConfig: FromTaskListPartitionConfig(t.PartitionConfig), } } func ToMatchingAddDecisionTaskResponse(t *matchingv1.AddDecisionTaskResponse) *types.AddDecisionTaskResponse { if t == nil { return nil } return &types.AddDecisionTaskResponse{ PartitionConfig: ToTaskListPartitionConfig(t.PartitionConfig), } } func FromActivityTaskDispatchInfo(t *types.ActivityTaskDispatchInfo) *matchingv1.ActivityTaskDispatchInfo { if t == nil { return nil } return &matchingv1.ActivityTaskDispatchInfo{ ScheduledEvent: FromHistoryEvent(t.ScheduledEvent), StartedTime: unixNanoToTime(t.StartedTimestamp), Attempt: int32(common.Int64Default(t.Attempt)), ScheduledTimeOfThisAttempt: unixNanoToTime(t.ScheduledTimestampOfThisAttempt), HeartbeatDetails: FromPayload(t.HeartbeatDetails), WorkflowType: FromWorkflowType(t.WorkflowType), WorkflowDomain: t.WorkflowDomain, } } func ToActivityTaskDispatchInfo(t *matchingv1.ActivityTaskDispatchInfo) *types.ActivityTaskDispatchInfo { if t == nil { return nil } return &types.ActivityTaskDispatchInfo{ ScheduledEvent: ToHistoryEvent(t.ScheduledEvent), StartedTimestamp: timeToUnixNano(t.StartedTime), Attempt: common.Int64Ptr(int64(t.Attempt)), ScheduledTimestampOfThisAttempt: timeToUnixNano(t.ScheduledTimeOfThisAttempt), HeartbeatDetails: ToPayload(t.HeartbeatDetails), WorkflowType: ToWorkflowType(t.WorkflowType), WorkflowDomain: t.WorkflowDomain, } } func FromMatchingCancelOutstandingPollRequest(t *types.CancelOutstandingPollRequest) *matchingv1.CancelOutstandingPollRequest { if t == nil { return nil } var taskListType *types.TaskListType if t.TaskListType != nil { taskListType = types.TaskListType(*t.TaskListType).Ptr() } return &matchingv1.CancelOutstandingPollRequest{ DomainId: t.DomainUUID, PollerId: t.PollerID, TaskListType: FromTaskListType(taskListType), TaskList: FromTaskList(t.TaskList), } } func ToMatchingCancelOutstandingPollRequest(t *matchingv1.CancelOutstandingPollRequest) *types.CancelOutstandingPollRequest { if t == nil { return nil } var taskListType *int32 if tlt := ToTaskListType(t.TaskListType); tlt != nil { taskListType = common.Int32Ptr(int32(*tlt)) } return &types.CancelOutstandingPollRequest{ DomainUUID: t.DomainId, PollerID: t.PollerId, TaskListType: taskListType, TaskList: ToTaskList(t.TaskList), } } func FromMatchingDescribeTaskListRequest(t *types.MatchingDescribeTaskListRequest) *matchingv1.DescribeTaskListRequest { if t == nil { return nil } return &matchingv1.DescribeTaskListRequest{ Request: FromDescribeTaskListRequest(t.DescRequest), DomainId: t.DomainUUID, } } func ToMatchingDescribeTaskListRequest(t *matchingv1.DescribeTaskListRequest) *types.MatchingDescribeTaskListRequest { if t == nil { return nil } return &types.MatchingDescribeTaskListRequest{ DescRequest: ToDescribeTaskListRequest(t.Request), DomainUUID: t.DomainId, } } func FromMatchingDescribeTaskListResponse(t *types.DescribeTaskListResponse) *matchingv1.DescribeTaskListResponse { if t == nil { return nil } return &matchingv1.DescribeTaskListResponse{ Pollers: FromPollerInfoArray(t.Pollers), TaskListStatus: FromTaskListStatus(t.TaskListStatus), PartitionConfig: FromAPITaskListPartitionConfig(t.PartitionConfig), TaskList: FromTaskList(t.TaskList), } } func ToMatchingDescribeTaskListResponse(t *matchingv1.DescribeTaskListResponse) *types.DescribeTaskListResponse { if t == nil { return nil } return &types.DescribeTaskListResponse{ Pollers: ToPollerInfoArray(t.Pollers), TaskListStatus: ToTaskListStatus(t.TaskListStatus), PartitionConfig: ToAPITaskListPartitionConfig(t.PartitionConfig), TaskList: ToTaskList(t.TaskList), } } func FromMatchingListTaskListPartitionsRequest(t *types.MatchingListTaskListPartitionsRequest) *matchingv1.ListTaskListPartitionsRequest { if t == nil { return nil } return &matchingv1.ListTaskListPartitionsRequest{ Domain: t.Domain, TaskList: FromTaskList(t.TaskList), } } func ToMatchingListTaskListPartitionsRequest(t *matchingv1.ListTaskListPartitionsRequest) *types.MatchingListTaskListPartitionsRequest { if t == nil { return nil } return &types.MatchingListTaskListPartitionsRequest{ Domain: t.Domain, TaskList: ToTaskList(t.TaskList), } } func FromMatchingListTaskListPartitionsResponse(t *types.ListTaskListPartitionsResponse) *matchingv1.ListTaskListPartitionsResponse { if t == nil { return nil } return &matchingv1.ListTaskListPartitionsResponse{ ActivityTaskListPartitions: FromTaskListPartitionMetadataArray(t.ActivityTaskListPartitions), DecisionTaskListPartitions: FromTaskListPartitionMetadataArray(t.DecisionTaskListPartitions), } } func ToMatchingListTaskListPartitionsResponse(t *matchingv1.ListTaskListPartitionsResponse) *types.ListTaskListPartitionsResponse { if t == nil { return nil } return &types.ListTaskListPartitionsResponse{ ActivityTaskListPartitions: ToTaskListPartitionMetadataArray(t.ActivityTaskListPartitions), DecisionTaskListPartitions: ToTaskListPartitionMetadataArray(t.DecisionTaskListPartitions), } } func FromMatchingGetTaskListsByDomainRequest(t *types.GetTaskListsByDomainRequest) *matchingv1.GetTaskListsByDomainRequest { if t == nil { return nil } return &matchingv1.GetTaskListsByDomainRequest{ Domain: t.Domain, } } func ToMatchingGetTaskListsByDomainRequest(t *matchingv1.GetTaskListsByDomainRequest) *types.GetTaskListsByDomainRequest { if t == nil { return nil } return &types.GetTaskListsByDomainRequest{ Domain: t.Domain, } } func FromMatchingGetTaskListsByDomainResponse(t *types.GetTaskListsByDomainResponse) *matchingv1.GetTaskListsByDomainResponse { if t == nil { return nil } return &matchingv1.GetTaskListsByDomainResponse{ DecisionTaskListMap: FromMatchingDescribeTaskListResponseMap(t.GetDecisionTaskListMap()), ActivityTaskListMap: FromMatchingDescribeTaskListResponseMap(t.GetActivityTaskListMap()), } } func ToMatchingGetTaskListsByDomainResponse(t *matchingv1.GetTaskListsByDomainResponse) *types.GetTaskListsByDomainResponse { if t == nil { return nil } return &types.GetTaskListsByDomainResponse{ DecisionTaskListMap: ToMatchingDescribeTaskListResponseMap(t.GetDecisionTaskListMap()), ActivityTaskListMap: ToMatchingDescribeTaskListResponseMap(t.GetActivityTaskListMap()), } } func FromMatchingDescribeTaskListResponseMap(t map[string]*types.DescribeTaskListResponse) map[string]*matchingv1.DescribeTaskListResponse { if t == nil { return nil } taskListMap := make(map[string]*matchingv1.DescribeTaskListResponse, len(t)) for key, value := range t { taskListMap[key] = FromMatchingDescribeTaskListResponse(value) } return taskListMap } func ToMatchingDescribeTaskListResponseMap(t map[string]*matchingv1.DescribeTaskListResponse) map[string]*types.DescribeTaskListResponse { if t == nil { return nil } taskListMap := make(map[string]*types.DescribeTaskListResponse, len(t)) for key, value := range t { taskListMap[key] = ToMatchingDescribeTaskListResponse(value) } return taskListMap } func FromMatchingPollForActivityTaskRequest(t *types.MatchingPollForActivityTaskRequest) *matchingv1.PollForActivityTaskRequest { if t == nil { return nil } return &matchingv1.PollForActivityTaskRequest{ Request: FromPollForActivityTaskRequest(t.PollRequest), DomainId: t.DomainUUID, PollerId: t.PollerID, ForwardedFrom: t.ForwardedFrom, IsolationGroup: t.IsolationGroup, } } func ToMatchingPollForActivityTaskRequest(t *matchingv1.PollForActivityTaskRequest) *types.MatchingPollForActivityTaskRequest { if t == nil { return nil } return &types.MatchingPollForActivityTaskRequest{ PollRequest: ToPollForActivityTaskRequest(t.Request), DomainUUID: t.DomainId, PollerID: t.PollerId, ForwardedFrom: t.ForwardedFrom, IsolationGroup: t.IsolationGroup, } } func FromTaskListPartitionConfig(t *types.TaskListPartitionConfig) *matchingv1.TaskListPartitionConfig { if t == nil { return nil } return &matchingv1.TaskListPartitionConfig{ Version: t.Version, NumReadPartitions: int32(len(t.ReadPartitions)), NumWritePartitions: int32(len(t.WritePartitions)), ReadPartitions: FromMatchingTaskListPartitionsMap(t.ReadPartitions), WritePartitions: FromMatchingTaskListPartitionsMap(t.WritePartitions), } } func ToTaskListPartitionConfig(t *matchingv1.TaskListPartitionConfig) *types.TaskListPartitionConfig { if t == nil { return nil } return &types.TaskListPartitionConfig{ Version: t.Version, ReadPartitions: ToMatchingTaskListPartitionsMap(t.NumReadPartitions, t.ReadPartitions), WritePartitions: ToMatchingTaskListPartitionsMap(t.NumWritePartitions, t.WritePartitions), } } func FromMatchingTaskListPartition(t *types.TaskListPartition) *matchingv1.TaskListPartition { if t == nil { return nil } return &matchingv1.TaskListPartition{ IsolationGroups: t.IsolationGroups, } } func ToMatchingTaskListPartition(t *matchingv1.TaskListPartition) *types.TaskListPartition { if t == nil { return nil } return &types.TaskListPartition{ IsolationGroups: t.IsolationGroups, } } func FromMatchingTaskListPartitionsMap(m map[int]*types.TaskListPartition) map[int32]*matchingv1.TaskListPartition { if m == nil { return nil } result := make(map[int32]*matchingv1.TaskListPartition, len(m)) for id, p := range m { result[int32(id)] = FromMatchingTaskListPartition(p) } return result } func ToMatchingTaskListPartitionsMap(numPartitions int32, m map[int32]*matchingv1.TaskListPartition) map[int]*types.TaskListPartition { if m == nil && numPartitions == 0 { return nil } result := make(map[int]*types.TaskListPartition, len(m)) if numPartitions != int32(len(m)) { for i := int32(0); i < numPartitions; i++ { result[int(i)] = &types.TaskListPartition{} } } else { for id, p := range m { result[int(id)] = ToMatchingTaskListPartition(p) } } return result } func FromMatchingPollForActivityTaskResponse(t *types.MatchingPollForActivityTaskResponse) *matchingv1.PollForActivityTaskResponse { if t == nil { return nil } return &matchingv1.PollForActivityTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), ActivityId: t.ActivityID, ActivityType: FromActivityType(t.ActivityType), Input: FromPayload(t.Input), ScheduledTime: unixNanoToTime(t.ScheduledTimestamp), StartedTime: unixNanoToTime(t.StartedTimestamp), ScheduleToCloseTimeout: secondsToDuration(t.ScheduleToCloseTimeoutSeconds), StartToCloseTimeout: secondsToDuration(t.StartToCloseTimeoutSeconds), HeartbeatTimeout: secondsToDuration(t.HeartbeatTimeoutSeconds), Attempt: t.Attempt, ScheduledTimeOfThisAttempt: unixNanoToTime(t.ScheduledTimestampOfThisAttempt), HeartbeatDetails: FromPayload(t.HeartbeatDetails), WorkflowType: FromWorkflowType(t.WorkflowType), WorkflowDomain: t.WorkflowDomain, Header: FromHeader(t.Header), PartitionConfig: FromTaskListPartitionConfig(t.PartitionConfig), LoadBalancerHints: FromLoadBalancerHints(t.LoadBalancerHints), AutoConfigHint: FromAutoConfigHint(t.AutoConfigHint), } } func ToMatchingPollForActivityTaskResponse(t *matchingv1.PollForActivityTaskResponse) *types.MatchingPollForActivityTaskResponse { if t == nil { return nil } return &types.MatchingPollForActivityTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), ActivityID: t.ActivityId, ActivityType: ToActivityType(t.ActivityType), Input: ToPayload(t.Input), ScheduledTimestamp: timeToUnixNano(t.ScheduledTime), StartedTimestamp: timeToUnixNano(t.StartedTime), ScheduleToCloseTimeoutSeconds: durationToSeconds(t.ScheduleToCloseTimeout), StartToCloseTimeoutSeconds: durationToSeconds(t.StartToCloseTimeout), HeartbeatTimeoutSeconds: durationToSeconds(t.HeartbeatTimeout), Attempt: t.Attempt, ScheduledTimestampOfThisAttempt: timeToUnixNano(t.ScheduledTimeOfThisAttempt), HeartbeatDetails: ToPayload(t.HeartbeatDetails), WorkflowType: ToWorkflowType(t.WorkflowType), WorkflowDomain: t.WorkflowDomain, Header: ToHeader(t.Header), PartitionConfig: ToTaskListPartitionConfig(t.PartitionConfig), LoadBalancerHints: ToLoadBalancerHints(t.LoadBalancerHints), AutoConfigHint: ToAutoConfigHint(t.AutoConfigHint), } } func FromMatchingPollForDecisionTaskRequest(t *types.MatchingPollForDecisionTaskRequest) *matchingv1.PollForDecisionTaskRequest { if t == nil { return nil } return &matchingv1.PollForDecisionTaskRequest{ Request: FromPollForDecisionTaskRequest(t.PollRequest), DomainId: t.DomainUUID, PollerId: t.PollerID, ForwardedFrom: t.ForwardedFrom, IsolationGroup: t.IsolationGroup, } } func ToMatchingPollForDecisionTaskRequest(t *matchingv1.PollForDecisionTaskRequest) *types.MatchingPollForDecisionTaskRequest { if t == nil { return nil } return &types.MatchingPollForDecisionTaskRequest{ PollRequest: ToPollForDecisionTaskRequest(t.Request), DomainUUID: t.DomainId, PollerID: t.PollerId, ForwardedFrom: t.ForwardedFrom, IsolationGroup: t.IsolationGroup, } } func FromMatchingPollForDecisionTaskResponse(t *types.MatchingPollForDecisionTaskResponse) *matchingv1.PollForDecisionTaskResponse { if t == nil { return nil } return &matchingv1.PollForDecisionTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), PreviousStartedEventId: fromInt64Value(t.PreviousStartedEventID), StartedEventId: t.StartedEventID, Attempt: int32(t.Attempt), NextEventId: t.NextEventID, BacklogCountHint: t.BacklogCountHint, StickyExecutionEnabled: t.StickyExecutionEnabled, Query: FromWorkflowQuery(t.Query), DecisionInfo: FromTransientDecisionInfo(t.DecisionInfo), WorkflowExecutionTaskList: FromTaskList(t.WorkflowExecutionTaskList), EventStoreVersion: t.EventStoreVersion, BranchToken: t.BranchToken, ScheduledTime: unixNanoToTime(t.ScheduledTimestamp), StartedTime: unixNanoToTime(t.StartedTimestamp), Queries: FromWorkflowQueryMap(t.Queries), TotalHistoryBytes: t.TotalHistoryBytes, PartitionConfig: FromTaskListPartitionConfig(t.PartitionConfig), LoadBalancerHints: FromLoadBalancerHints(t.LoadBalancerHints), AutoConfigHint: FromAutoConfigHint(t.AutoConfigHint), } } func ToMatchingPollForDecisionTaskResponse(t *matchingv1.PollForDecisionTaskResponse) *types.MatchingPollForDecisionTaskResponse { if t == nil { return nil } return &types.MatchingPollForDecisionTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), PreviousStartedEventID: toInt64Value(t.PreviousStartedEventId), StartedEventID: t.StartedEventId, Attempt: int64(t.Attempt), NextEventID: t.NextEventId, BacklogCountHint: t.BacklogCountHint, StickyExecutionEnabled: t.StickyExecutionEnabled, Query: ToWorkflowQuery(t.Query), DecisionInfo: ToTransientDecisionInfo(t.DecisionInfo), WorkflowExecutionTaskList: ToTaskList(t.WorkflowExecutionTaskList), EventStoreVersion: t.EventStoreVersion, BranchToken: t.BranchToken, ScheduledTimestamp: timeToUnixNano(t.ScheduledTime), StartedTimestamp: timeToUnixNano(t.StartedTime), Queries: ToWorkflowQueryMap(t.Queries), TotalHistoryBytes: t.TotalHistoryBytes, PartitionConfig: ToTaskListPartitionConfig(t.PartitionConfig), LoadBalancerHints: ToLoadBalancerHints(t.LoadBalancerHints), AutoConfigHint: ToAutoConfigHint(t.AutoConfigHint), } } func FromMatchingQueryWorkflowRequest(t *types.MatchingQueryWorkflowRequest) *matchingv1.QueryWorkflowRequest { if t == nil { return nil } return &matchingv1.QueryWorkflowRequest{ Request: FromQueryWorkflowRequest(t.QueryRequest), DomainId: t.DomainUUID, TaskList: FromTaskList(t.TaskList), ForwardedFrom: t.ForwardedFrom, } } func ToMatchingQueryWorkflowRequest(t *matchingv1.QueryWorkflowRequest) *types.MatchingQueryWorkflowRequest { if t == nil { return nil } return &types.MatchingQueryWorkflowRequest{ QueryRequest: ToQueryWorkflowRequest(t.Request), DomainUUID: t.DomainId, TaskList: ToTaskList(t.TaskList), ForwardedFrom: t.ForwardedFrom, } } func FromMatchingQueryWorkflowResponse(t *types.MatchingQueryWorkflowResponse) *matchingv1.QueryWorkflowResponse { if t == nil { return nil } return &matchingv1.QueryWorkflowResponse{ QueryResult: FromPayload(t.QueryResult), QueryRejected: FromQueryRejected(t.QueryRejected), PartitionConfig: FromAPITaskListPartitionConfig(t.PartitionConfig), } } func ToMatchingQueryWorkflowResponse(t *matchingv1.QueryWorkflowResponse) *types.MatchingQueryWorkflowResponse { if t == nil { return nil } return &types.MatchingQueryWorkflowResponse{ QueryResult: ToPayload(t.QueryResult), QueryRejected: ToQueryRejected(t.QueryRejected), PartitionConfig: ToAPITaskListPartitionConfig(t.PartitionConfig), } } func FromMatchingRespondQueryTaskCompletedRequest(t *types.MatchingRespondQueryTaskCompletedRequest) *matchingv1.RespondQueryTaskCompletedRequest { if t == nil { return nil } return &matchingv1.RespondQueryTaskCompletedRequest{ Request: FromRespondQueryTaskCompletedRequest(t.CompletedRequest), DomainId: t.DomainUUID, TaskList: FromTaskList(t.TaskList), TaskId: t.TaskID, } } func ToMatchingRespondQueryTaskCompletedRequest(t *matchingv1.RespondQueryTaskCompletedRequest) *types.MatchingRespondQueryTaskCompletedRequest { if t == nil { return nil } return &types.MatchingRespondQueryTaskCompletedRequest{ CompletedRequest: ToRespondQueryTaskCompletedRequest(t.Request), DomainUUID: t.DomainId, TaskList: ToTaskList(t.TaskList), TaskID: t.TaskId, } } func FromMatchingUpdateTaskListPartitionConfigRequest(t *types.MatchingUpdateTaskListPartitionConfigRequest) *matchingv1.UpdateTaskListPartitionConfigRequest { if t == nil { return nil } return &matchingv1.UpdateTaskListPartitionConfigRequest{ DomainId: t.DomainUUID, TaskList: FromTaskList(t.TaskList), TaskListType: FromTaskListType(t.TaskListType), PartitionConfig: FromAPITaskListPartitionConfig(t.PartitionConfig), } } func ToMatchingUpdateTaskListPartitionConfigRequest(t *matchingv1.UpdateTaskListPartitionConfigRequest) *types.MatchingUpdateTaskListPartitionConfigRequest { if t == nil { return nil } return &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: t.DomainId, TaskList: ToTaskList(t.TaskList), TaskListType: ToTaskListType(t.TaskListType), PartitionConfig: ToAPITaskListPartitionConfig(t.PartitionConfig), } } func FromMatchingRefreshTaskListPartitionConfigRequest(t *types.MatchingRefreshTaskListPartitionConfigRequest) *matchingv1.RefreshTaskListPartitionConfigRequest { if t == nil { return nil } return &matchingv1.RefreshTaskListPartitionConfigRequest{ DomainId: t.DomainUUID, TaskList: FromTaskList(t.TaskList), TaskListType: FromTaskListType(t.TaskListType), PartitionConfig: FromAPITaskListPartitionConfig(t.PartitionConfig), } } func ToMatchingRefreshTaskListPartitionConfigRequest(t *matchingv1.RefreshTaskListPartitionConfigRequest) *types.MatchingRefreshTaskListPartitionConfigRequest { if t == nil { return nil } return &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: t.DomainId, TaskList: ToTaskList(t.TaskList), TaskListType: ToTaskListType(t.TaskListType), PartitionConfig: ToAPITaskListPartitionConfig(t.PartitionConfig), } } func FromMatchingUpdateTaskListPartitionConfigResponse(t *types.MatchingUpdateTaskListPartitionConfigResponse) *matchingv1.UpdateTaskListPartitionConfigResponse { if t == nil { return nil } return &matchingv1.UpdateTaskListPartitionConfigResponse{} } func ToMatchingUpdateTaskListPartitionConfigResponse(t *matchingv1.UpdateTaskListPartitionConfigResponse) *types.MatchingUpdateTaskListPartitionConfigResponse { if t == nil { return nil } return &types.MatchingUpdateTaskListPartitionConfigResponse{} } func FromMatchingRefreshTaskListPartitionConfigResponse(t *types.MatchingRefreshTaskListPartitionConfigResponse) *matchingv1.RefreshTaskListPartitionConfigResponse { if t == nil { return nil } return &matchingv1.RefreshTaskListPartitionConfigResponse{} } func ToMatchingRefreshTaskListPartitionConfigResponse(t *matchingv1.RefreshTaskListPartitionConfigResponse) *types.MatchingRefreshTaskListPartitionConfigResponse { if t == nil { return nil } return &types.MatchingRefreshTaskListPartitionConfigResponse{} } func FromLoadBalancerHints(t *types.LoadBalancerHints) *matchingv1.LoadBalancerHints { if t == nil { return nil } return &matchingv1.LoadBalancerHints{ BacklogCount: t.BacklogCount, RatePerSecond: t.RatePerSecond, } } func ToLoadBalancerHints(t *matchingv1.LoadBalancerHints) *types.LoadBalancerHints { if t == nil { return nil } return &types.LoadBalancerHints{ BacklogCount: t.BacklogCount, RatePerSecond: t.RatePerSecond, } } ================================================ FILE: common/types/mapper/proto/matching_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "testing" "github.com/stretchr/testify/assert" matchingv1 "github.com/uber/cadence/.gen/proto/matching/v1" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" ) func TestMatchingAddActivityTaskRequest(t *testing.T) { for _, item := range []*types.AddActivityTaskRequest{nil, {}, &testdata.MatchingAddActivityTaskRequest} { assert.Equal(t, item, ToMatchingAddActivityTaskRequest(FromMatchingAddActivityTaskRequest(item))) } } func TestMatchingAddDecisionTaskRequest(t *testing.T) { for _, item := range []*types.AddDecisionTaskRequest{nil, {}, &testdata.MatchingAddDecisionTaskRequest} { assert.Equal(t, item, ToMatchingAddDecisionTaskRequest(FromMatchingAddDecisionTaskRequest(item))) } } func TestMatchingAddActivityTaskResponse(t *testing.T) { for _, item := range []*types.AddActivityTaskResponse{nil, {}, &testdata.MatchingAddActivityTaskResponse} { assert.Equal(t, item, ToMatchingAddActivityTaskResponse(FromMatchingAddActivityTaskResponse(item))) } } func TestMatchingAddDecisionTaskResponse(t *testing.T) { for _, item := range []*types.AddDecisionTaskResponse{nil, {}, &testdata.MatchingAddDecisionTaskResponse} { assert.Equal(t, item, ToMatchingAddDecisionTaskResponse(FromMatchingAddDecisionTaskResponse(item))) } } func TestMatchingCancelOutstandingPollRequest(t *testing.T) { for _, item := range []*types.CancelOutstandingPollRequest{nil, {}, &testdata.MatchingCancelOutstandingPollRequest} { assert.Equal(t, item, ToMatchingCancelOutstandingPollRequest(FromMatchingCancelOutstandingPollRequest(item))) } } func TestMatchingDescribeTaskListRequest(t *testing.T) { for _, item := range []*types.MatchingDescribeTaskListRequest{nil, {}, &testdata.MatchingDescribeTaskListRequest} { assert.Equal(t, item, ToMatchingDescribeTaskListRequest(FromMatchingDescribeTaskListRequest(item))) } } func TestMatchingDescribeTaskListResponse(t *testing.T) { for _, item := range []*types.DescribeTaskListResponse{nil, {}, &testdata.MatchingDescribeTaskListResponse} { assert.Equal(t, item, ToMatchingDescribeTaskListResponse(FromMatchingDescribeTaskListResponse(item))) } } func TestMatchingDescribeTaskListResponseMap(t *testing.T) { for _, item := range []map[string]*types.DescribeTaskListResponse{nil, {}, testdata.DescribeTaskListResponseMap} { assert.Equal(t, item, ToMatchingDescribeTaskListResponseMap(FromMatchingDescribeTaskListResponseMap(item))) } } func TestMatchingListTaskListPartitionsRequest(t *testing.T) { for _, item := range []*types.MatchingListTaskListPartitionsRequest{nil, {}, &testdata.MatchingListTaskListPartitionsRequest} { assert.Equal(t, item, ToMatchingListTaskListPartitionsRequest(FromMatchingListTaskListPartitionsRequest(item))) } } func TestMatchingListTaskListPartitionsResponse(t *testing.T) { for _, item := range []*types.ListTaskListPartitionsResponse{nil, {}, &testdata.MatchingListTaskListPartitionsResponse} { assert.Equal(t, item, ToMatchingListTaskListPartitionsResponse(FromMatchingListTaskListPartitionsResponse(item))) } } func TestMatchingPollForActivityTaskRequest(t *testing.T) { for _, item := range []*types.MatchingPollForActivityTaskRequest{nil, {}, &testdata.MatchingPollForActivityTaskRequest} { assert.Equal(t, item, ToMatchingPollForActivityTaskRequest(FromMatchingPollForActivityTaskRequest(item))) } } func TestMatchingPollForActivityTaskResponse(t *testing.T) { for _, item := range []*types.MatchingPollForActivityTaskResponse{nil, {}, &testdata.MatchingPollForActivityTaskResponse} { assert.Equal(t, item, ToMatchingPollForActivityTaskResponse(FromMatchingPollForActivityTaskResponse(item))) } } func TestTaskListPartitionConfig(t *testing.T) { for _, item := range []*types.TaskListPartitionConfig{nil, {}, &testdata.TaskListPartitionConfig} { assert.Equal(t, item, ToTaskListPartitionConfig(FromTaskListPartitionConfig(item))) } } func TestMatchingPollForDecisionTaskRequest(t *testing.T) { for _, item := range []*types.MatchingPollForDecisionTaskRequest{nil, {}, &testdata.MatchingPollForDecisionTaskRequest} { assert.Equal(t, item, ToMatchingPollForDecisionTaskRequest(FromMatchingPollForDecisionTaskRequest(item))) } } func TestMatchingPollForDecisionTaskResponse(t *testing.T) { for _, item := range []*types.MatchingPollForDecisionTaskResponse{nil, {}, &testdata.MatchingPollForDecisionTaskResponse} { assert.Equal(t, item, ToMatchingPollForDecisionTaskResponse(FromMatchingPollForDecisionTaskResponse(item))) } } func TestMatchingQueryWorkflowRequest(t *testing.T) { for _, item := range []*types.MatchingQueryWorkflowRequest{nil, {}, &testdata.MatchingQueryWorkflowRequest} { assert.Equal(t, item, ToMatchingQueryWorkflowRequest(FromMatchingQueryWorkflowRequest(item))) } } func TestMatchingQueryWorkflowResponse(t *testing.T) { for _, item := range []*types.MatchingQueryWorkflowResponse{nil, {}, &testdata.MatchingQueryWorkflowResponse} { assert.Equal(t, item, ToMatchingQueryWorkflowResponse(FromMatchingQueryWorkflowResponse(item))) } } func TestMatchingRespondQueryTaskCompletedRequest(t *testing.T) { for _, item := range []*types.MatchingRespondQueryTaskCompletedRequest{nil, {}, &testdata.MatchingRespondQueryTaskCompletedRequest} { assert.Equal(t, item, ToMatchingRespondQueryTaskCompletedRequest(FromMatchingRespondQueryTaskCompletedRequest(item))) } } func TestMatchingGetTaskListsByDomainRequest(t *testing.T) { for _, item := range []*types.GetTaskListsByDomainRequest{nil, {}, &testdata.MatchingGetTaskListsByDomainRequest} { assert.Equal(t, item, ToMatchingGetTaskListsByDomainRequest(FromMatchingGetTaskListsByDomainRequest(item))) } } func TestMatchingGetTaskListsByDomainResponse(t *testing.T) { for _, item := range []*types.GetTaskListsByDomainResponse{nil, {}, &testdata.GetTaskListsByDomainResponse} { assert.Equal(t, item, ToMatchingGetTaskListsByDomainResponse(FromMatchingGetTaskListsByDomainResponse(item))) } } func TestMatchingUpdateTaskListPartitionConfigRequest(t *testing.T) { for _, item := range []*types.MatchingUpdateTaskListPartitionConfigRequest{nil, {}, &testdata.MatchingUpdateTaskListPartitionConfigRequest} { assert.Equal(t, item, ToMatchingUpdateTaskListPartitionConfigRequest(FromMatchingUpdateTaskListPartitionConfigRequest(item))) } } func TestMatchingUpdateTaskListPartitionConfigResponse(t *testing.T) { for _, item := range []*types.MatchingUpdateTaskListPartitionConfigResponse{nil, {}} { assert.Equal(t, item, ToMatchingUpdateTaskListPartitionConfigResponse(FromMatchingUpdateTaskListPartitionConfigResponse(item))) } } func TestMatchingRefreshTaskListPartitionConfigRequest(t *testing.T) { for _, item := range []*types.MatchingRefreshTaskListPartitionConfigRequest{nil, {}, &testdata.MatchingRefreshTaskListPartitionConfigRequest} { assert.Equal(t, item, ToMatchingRefreshTaskListPartitionConfigRequest(FromMatchingRefreshTaskListPartitionConfigRequest(item))) } } func TestMatchingRefreshTaskListPartitionConfigResponse(t *testing.T) { for _, item := range []*types.MatchingRefreshTaskListPartitionConfigResponse{nil, {}} { assert.Equal(t, item, ToMatchingRefreshTaskListPartitionConfigResponse(FromMatchingRefreshTaskListPartitionConfigResponse(item))) } } func TestToMatchingTaskListPartitionConfig(t *testing.T) { cases := []struct { name string config *matchingv1.TaskListPartitionConfig expected *types.TaskListPartitionConfig }{ { name: "happy path", config: &matchingv1.TaskListPartitionConfig{ Version: 1, NumReadPartitions: 2, NumWritePartitions: 2, ReadPartitions: map[int32]*matchingv1.TaskListPartition{ 0: {IsolationGroups: []string{"foo"}}, 1: {IsolationGroups: []string{"bar"}}, }, WritePartitions: map[int32]*matchingv1.TaskListPartition{ 0: {IsolationGroups: []string{"baz"}}, 1: {IsolationGroups: []string{"bar"}}, }, }, expected: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"foo"}}, 1: {IsolationGroups: []string{"bar"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"baz"}}, 1: {IsolationGroups: []string{"bar"}}, }, }, }, { name: "numbers only", config: &matchingv1.TaskListPartitionConfig{ Version: 1, NumReadPartitions: 2, NumWritePartitions: 2, }, expected: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, }, }, { name: "number mismatch", config: &matchingv1.TaskListPartitionConfig{ Version: 1, NumReadPartitions: 2, NumWritePartitions: 1, ReadPartitions: map[int32]*matchingv1.TaskListPartition{ 0: {IsolationGroups: []string{"foo"}}, 1: {IsolationGroups: []string{"bar"}}, }, WritePartitions: map[int32]*matchingv1.TaskListPartition{ 0: {IsolationGroups: []string{"baz"}}, 1: {IsolationGroups: []string{"bar"}}, }, }, expected: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"foo"}}, 1: {IsolationGroups: []string{"bar"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { actual := ToTaskListPartitionConfig(tc.config) assert.Equal(t, tc.expected, actual) }) } } ================================================ FILE: common/types/mapper/proto/schedule.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package proto import ( apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "github.com/uber/cadence/common/types" ) // --- Enum mappers --- func FromScheduleOverlapPolicy(p types.ScheduleOverlapPolicy) apiv1.ScheduleOverlapPolicy { switch p { case types.ScheduleOverlapPolicySkipNew: return apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_SKIP_NEW case types.ScheduleOverlapPolicyBuffer: return apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_BUFFER case types.ScheduleOverlapPolicyConcurrent: return apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_CONCURRENT case types.ScheduleOverlapPolicyCancelPrevious: return apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_CANCEL_PREVIOUS case types.ScheduleOverlapPolicyTerminatePrevious: return apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_TERMINATE_PREVIOUS } return apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_INVALID } func ToScheduleOverlapPolicy(p apiv1.ScheduleOverlapPolicy) types.ScheduleOverlapPolicy { switch p { case apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_SKIP_NEW: return types.ScheduleOverlapPolicySkipNew case apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_BUFFER: return types.ScheduleOverlapPolicyBuffer case apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_CONCURRENT: return types.ScheduleOverlapPolicyConcurrent case apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_CANCEL_PREVIOUS: return types.ScheduleOverlapPolicyCancelPrevious case apiv1.ScheduleOverlapPolicy_SCHEDULE_OVERLAP_POLICY_TERMINATE_PREVIOUS: return types.ScheduleOverlapPolicyTerminatePrevious } return types.ScheduleOverlapPolicyInvalid } func FromScheduleCatchUpPolicy(p types.ScheduleCatchUpPolicy) apiv1.ScheduleCatchUpPolicy { switch p { case types.ScheduleCatchUpPolicySkip: return apiv1.ScheduleCatchUpPolicy_SCHEDULE_CATCH_UP_POLICY_SKIP case types.ScheduleCatchUpPolicyOne: return apiv1.ScheduleCatchUpPolicy_SCHEDULE_CATCH_UP_POLICY_ONE case types.ScheduleCatchUpPolicyAll: return apiv1.ScheduleCatchUpPolicy_SCHEDULE_CATCH_UP_POLICY_ALL } return apiv1.ScheduleCatchUpPolicy_SCHEDULE_CATCH_UP_POLICY_INVALID } func ToScheduleCatchUpPolicy(p apiv1.ScheduleCatchUpPolicy) types.ScheduleCatchUpPolicy { switch p { case apiv1.ScheduleCatchUpPolicy_SCHEDULE_CATCH_UP_POLICY_SKIP: return types.ScheduleCatchUpPolicySkip case apiv1.ScheduleCatchUpPolicy_SCHEDULE_CATCH_UP_POLICY_ONE: return types.ScheduleCatchUpPolicyOne case apiv1.ScheduleCatchUpPolicy_SCHEDULE_CATCH_UP_POLICY_ALL: return types.ScheduleCatchUpPolicyAll } return types.ScheduleCatchUpPolicyInvalid } // --- Core type mappers --- func FromScheduleSpec(t *types.ScheduleSpec) *apiv1.ScheduleSpec { if t == nil { return nil } return &apiv1.ScheduleSpec{ CronExpression: t.CronExpression, StartTime: timeToTimestamp(&t.StartTime), EndTime: timeToTimestamp(&t.EndTime), Jitter: durationToDurationProto(t.Jitter), } } func ToScheduleSpec(t *apiv1.ScheduleSpec) *types.ScheduleSpec { if t == nil { return nil } return &types.ScheduleSpec{ CronExpression: t.CronExpression, StartTime: timestampToTimeVal(t.StartTime), EndTime: timestampToTimeVal(t.EndTime), Jitter: durationProtoToDuration(t.Jitter), } } func FromStartWorkflowAction(t *types.StartWorkflowAction) *apiv1.ScheduleAction_StartWorkflowAction { if t == nil { return nil } return &apiv1.ScheduleAction_StartWorkflowAction{ WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: FromPayload(t.Input), WorkflowIdPrefix: t.WorkflowIDPrefix, ExecutionStartToCloseTimeout: secondsToDuration(t.ExecutionStartToCloseTimeoutSeconds), TaskStartToCloseTimeout: secondsToDuration(t.TaskStartToCloseTimeoutSeconds), RetryPolicy: FromRetryPolicy(t.RetryPolicy), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), } } func ToStartWorkflowAction(t *apiv1.ScheduleAction_StartWorkflowAction) *types.StartWorkflowAction { if t == nil { return nil } return &types.StartWorkflowAction{ WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: ToPayload(t.Input), WorkflowIDPrefix: t.WorkflowIdPrefix, ExecutionStartToCloseTimeoutSeconds: durationToSeconds(t.ExecutionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: durationToSeconds(t.TaskStartToCloseTimeout), RetryPolicy: ToRetryPolicy(t.RetryPolicy), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), } } func FromScheduleAction(t *types.ScheduleAction) *apiv1.ScheduleAction { if t == nil { return nil } return &apiv1.ScheduleAction{ StartWorkflow: FromStartWorkflowAction(t.StartWorkflow), } } func ToScheduleAction(t *apiv1.ScheduleAction) *types.ScheduleAction { if t == nil { return nil } return &types.ScheduleAction{ StartWorkflow: ToStartWorkflowAction(t.StartWorkflow), } } func FromSchedulePolicies(t *types.SchedulePolicies) *apiv1.SchedulePolicies { if t == nil { return nil } return &apiv1.SchedulePolicies{ OverlapPolicy: FromScheduleOverlapPolicy(t.OverlapPolicy), CatchUpPolicy: FromScheduleCatchUpPolicy(t.CatchUpPolicy), CatchUpWindow: durationToDurationProto(t.CatchUpWindow), PauseOnFailure: t.PauseOnFailure, BufferLimit: t.BufferLimit, ConcurrencyLimit: t.ConcurrencyLimit, } } func ToSchedulePolicies(t *apiv1.SchedulePolicies) *types.SchedulePolicies { if t == nil { return nil } return &types.SchedulePolicies{ OverlapPolicy: ToScheduleOverlapPolicy(t.OverlapPolicy), CatchUpPolicy: ToScheduleCatchUpPolicy(t.CatchUpPolicy), CatchUpWindow: durationProtoToDuration(t.CatchUpWindow), PauseOnFailure: t.PauseOnFailure, BufferLimit: t.BufferLimit, ConcurrencyLimit: t.ConcurrencyLimit, } } // --- State/info type mappers --- func FromSchedulePauseInfo(t *types.SchedulePauseInfo) *apiv1.SchedulePauseInfo { if t == nil { return nil } return &apiv1.SchedulePauseInfo{ Reason: t.Reason, PausedAt: timeToTimestamp(&t.PausedAt), PausedBy: t.PausedBy, } } func ToSchedulePauseInfo(t *apiv1.SchedulePauseInfo) *types.SchedulePauseInfo { if t == nil { return nil } return &types.SchedulePauseInfo{ Reason: t.Reason, PausedAt: timestampToTimeVal(t.PausedAt), PausedBy: t.PausedBy, } } func FromScheduleState(t *types.ScheduleState) *apiv1.ScheduleState { if t == nil { return nil } return &apiv1.ScheduleState{ Paused: t.Paused, PauseInfo: FromSchedulePauseInfo(t.PauseInfo), } } func ToScheduleState(t *apiv1.ScheduleState) *types.ScheduleState { if t == nil { return nil } return &types.ScheduleState{ Paused: t.Paused, PauseInfo: ToSchedulePauseInfo(t.PauseInfo), } } func FromBackfillInfo(t *types.BackfillInfo) *apiv1.BackfillInfo { if t == nil { return nil } return &apiv1.BackfillInfo{ BackfillId: t.BackfillID, StartTime: timeToTimestamp(&t.StartTime), EndTime: timeToTimestamp(&t.EndTime), RunsCompleted: t.RunsCompleted, RunsTotal: t.RunsTotal, } } func ToBackfillInfo(t *apiv1.BackfillInfo) *types.BackfillInfo { if t == nil { return nil } return &types.BackfillInfo{ BackfillID: t.BackfillId, StartTime: timestampToTimeVal(t.StartTime), EndTime: timestampToTimeVal(t.EndTime), RunsCompleted: t.RunsCompleted, RunsTotal: t.RunsTotal, } } func FromBackfillInfoArray(t []*types.BackfillInfo) []*apiv1.BackfillInfo { if t == nil { return nil } v := make([]*apiv1.BackfillInfo, len(t)) for i := range t { v[i] = FromBackfillInfo(t[i]) } return v } func ToBackfillInfoArray(t []*apiv1.BackfillInfo) []*types.BackfillInfo { if t == nil { return nil } v := make([]*types.BackfillInfo, len(t)) for i := range t { v[i] = ToBackfillInfo(t[i]) } return v } func FromScheduleInfo(t *types.ScheduleInfo) *apiv1.ScheduleInfo { if t == nil { return nil } return &apiv1.ScheduleInfo{ LastRunTime: timeToTimestamp(&t.LastRunTime), NextRunTime: timeToTimestamp(&t.NextRunTime), TotalRuns: t.TotalRuns, CreateTime: timeToTimestamp(&t.CreateTime), LastUpdateTime: timeToTimestamp(&t.LastUpdateTime), OngoingBackfills: FromBackfillInfoArray(t.OngoingBackfills), } } func ToScheduleInfo(t *apiv1.ScheduleInfo) *types.ScheduleInfo { if t == nil { return nil } return &types.ScheduleInfo{ LastRunTime: timestampToTimeVal(t.LastRunTime), NextRunTime: timestampToTimeVal(t.NextRunTime), TotalRuns: t.TotalRuns, CreateTime: timestampToTimeVal(t.CreateTime), LastUpdateTime: timestampToTimeVal(t.LastUpdateTime), OngoingBackfills: ToBackfillInfoArray(t.OngoingBackfills), } } func FromScheduleListEntry(t *types.ScheduleListEntry) *apiv1.ScheduleListEntry { if t == nil { return nil } return &apiv1.ScheduleListEntry{ ScheduleId: t.ScheduleID, WorkflowType: FromWorkflowType(t.WorkflowType), State: FromScheduleState(t.State), CronExpression: t.CronExpression, } } func ToScheduleListEntry(t *apiv1.ScheduleListEntry) *types.ScheduleListEntry { if t == nil { return nil } return &types.ScheduleListEntry{ ScheduleID: t.ScheduleId, WorkflowType: ToWorkflowType(t.WorkflowType), State: ToScheduleState(t.State), CronExpression: t.CronExpression, } } func FromScheduleListEntryArray(t []*types.ScheduleListEntry) []*apiv1.ScheduleListEntry { if t == nil { return nil } v := make([]*apiv1.ScheduleListEntry, len(t)) for i := range t { v[i] = FromScheduleListEntry(t[i]) } return v } func ToScheduleListEntryArray(t []*apiv1.ScheduleListEntry) []*types.ScheduleListEntry { if t == nil { return nil } v := make([]*types.ScheduleListEntry, len(t)) for i := range t { v[i] = ToScheduleListEntry(t[i]) } return v } // --- CRUD request/response mappers --- func FromCreateScheduleRequest(t *types.CreateScheduleRequest) *apiv1.CreateScheduleRequest { if t == nil { return nil } return &apiv1.CreateScheduleRequest{ Domain: t.Domain, ScheduleId: t.ScheduleID, Spec: FromScheduleSpec(t.Spec), Action: FromScheduleAction(t.Action), Policies: FromSchedulePolicies(t.Policies), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), } } func ToCreateScheduleRequest(t *apiv1.CreateScheduleRequest) *types.CreateScheduleRequest { if t == nil { return nil } return &types.CreateScheduleRequest{ Domain: t.Domain, ScheduleID: t.ScheduleId, Spec: ToScheduleSpec(t.Spec), Action: ToScheduleAction(t.Action), Policies: ToSchedulePolicies(t.Policies), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), } } func FromCreateScheduleResponse(t *types.CreateScheduleResponse) *apiv1.CreateScheduleResponse { if t == nil { return nil } return &apiv1.CreateScheduleResponse{} } func ToCreateScheduleResponse(t *apiv1.CreateScheduleResponse) *types.CreateScheduleResponse { if t == nil { return nil } return &types.CreateScheduleResponse{} } func FromDescribeScheduleRequest(t *types.DescribeScheduleRequest) *apiv1.DescribeScheduleRequest { if t == nil { return nil } return &apiv1.DescribeScheduleRequest{ Domain: t.Domain, ScheduleId: t.ScheduleID, } } func ToDescribeScheduleRequest(t *apiv1.DescribeScheduleRequest) *types.DescribeScheduleRequest { if t == nil { return nil } return &types.DescribeScheduleRequest{ Domain: t.Domain, ScheduleID: t.ScheduleId, } } func FromDescribeScheduleResponse(t *types.DescribeScheduleResponse) *apiv1.DescribeScheduleResponse { if t == nil { return nil } return &apiv1.DescribeScheduleResponse{ Spec: FromScheduleSpec(t.Spec), Action: FromScheduleAction(t.Action), Policies: FromSchedulePolicies(t.Policies), State: FromScheduleState(t.State), Info: FromScheduleInfo(t.Info), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), } } func ToDescribeScheduleResponse(t *apiv1.DescribeScheduleResponse) *types.DescribeScheduleResponse { if t == nil { return nil } return &types.DescribeScheduleResponse{ Spec: ToScheduleSpec(t.Spec), Action: ToScheduleAction(t.Action), Policies: ToSchedulePolicies(t.Policies), State: ToScheduleState(t.State), Info: ToScheduleInfo(t.Info), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), } } func FromUpdateScheduleRequest(t *types.UpdateScheduleRequest) *apiv1.UpdateScheduleRequest { if t == nil { return nil } return &apiv1.UpdateScheduleRequest{ Domain: t.Domain, ScheduleId: t.ScheduleID, Spec: FromScheduleSpec(t.Spec), Action: FromScheduleAction(t.Action), Policies: FromSchedulePolicies(t.Policies), SearchAttributes: FromSearchAttributes(t.SearchAttributes), } } func ToUpdateScheduleRequest(t *apiv1.UpdateScheduleRequest) *types.UpdateScheduleRequest { if t == nil { return nil } return &types.UpdateScheduleRequest{ Domain: t.Domain, ScheduleID: t.ScheduleId, Spec: ToScheduleSpec(t.Spec), Action: ToScheduleAction(t.Action), Policies: ToSchedulePolicies(t.Policies), SearchAttributes: ToSearchAttributes(t.SearchAttributes), } } func FromUpdateScheduleResponse(t *types.UpdateScheduleResponse) *apiv1.UpdateScheduleResponse { if t == nil { return nil } return &apiv1.UpdateScheduleResponse{} } func ToUpdateScheduleResponse(t *apiv1.UpdateScheduleResponse) *types.UpdateScheduleResponse { if t == nil { return nil } return &types.UpdateScheduleResponse{} } func FromDeleteScheduleRequest(t *types.DeleteScheduleRequest) *apiv1.DeleteScheduleRequest { if t == nil { return nil } return &apiv1.DeleteScheduleRequest{ Domain: t.Domain, ScheduleId: t.ScheduleID, } } func ToDeleteScheduleRequest(t *apiv1.DeleteScheduleRequest) *types.DeleteScheduleRequest { if t == nil { return nil } return &types.DeleteScheduleRequest{ Domain: t.Domain, ScheduleID: t.ScheduleId, } } func FromDeleteScheduleResponse(t *types.DeleteScheduleResponse) *apiv1.DeleteScheduleResponse { if t == nil { return nil } return &apiv1.DeleteScheduleResponse{} } func ToDeleteScheduleResponse(t *apiv1.DeleteScheduleResponse) *types.DeleteScheduleResponse { if t == nil { return nil } return &types.DeleteScheduleResponse{} } // --- Action request/response mappers --- func FromPauseScheduleRequest(t *types.PauseScheduleRequest) *apiv1.PauseScheduleRequest { if t == nil { return nil } return &apiv1.PauseScheduleRequest{ Domain: t.Domain, ScheduleId: t.ScheduleID, Reason: t.Reason, } } func ToPauseScheduleRequest(t *apiv1.PauseScheduleRequest) *types.PauseScheduleRequest { if t == nil { return nil } return &types.PauseScheduleRequest{ Domain: t.Domain, ScheduleID: t.ScheduleId, Reason: t.Reason, } } func FromPauseScheduleResponse(t *types.PauseScheduleResponse) *apiv1.PauseScheduleResponse { if t == nil { return nil } return &apiv1.PauseScheduleResponse{} } func ToPauseScheduleResponse(t *apiv1.PauseScheduleResponse) *types.PauseScheduleResponse { if t == nil { return nil } return &types.PauseScheduleResponse{} } func FromUnpauseScheduleRequest(t *types.UnpauseScheduleRequest) *apiv1.UnpauseScheduleRequest { if t == nil { return nil } return &apiv1.UnpauseScheduleRequest{ Domain: t.Domain, ScheduleId: t.ScheduleID, Reason: t.Reason, CatchUpPolicy: FromScheduleCatchUpPolicy(t.CatchUpPolicy), } } func ToUnpauseScheduleRequest(t *apiv1.UnpauseScheduleRequest) *types.UnpauseScheduleRequest { if t == nil { return nil } return &types.UnpauseScheduleRequest{ Domain: t.Domain, ScheduleID: t.ScheduleId, Reason: t.Reason, CatchUpPolicy: ToScheduleCatchUpPolicy(t.CatchUpPolicy), } } func FromUnpauseScheduleResponse(t *types.UnpauseScheduleResponse) *apiv1.UnpauseScheduleResponse { if t == nil { return nil } return &apiv1.UnpauseScheduleResponse{} } func ToUnpauseScheduleResponse(t *apiv1.UnpauseScheduleResponse) *types.UnpauseScheduleResponse { if t == nil { return nil } return &types.UnpauseScheduleResponse{} } func FromListSchedulesRequest(t *types.ListSchedulesRequest) *apiv1.ListSchedulesRequest { if t == nil { return nil } return &apiv1.ListSchedulesRequest{ Domain: t.Domain, PageSize: t.PageSize, NextPageToken: t.NextPageToken, } } func ToListSchedulesRequest(t *apiv1.ListSchedulesRequest) *types.ListSchedulesRequest { if t == nil { return nil } return &types.ListSchedulesRequest{ Domain: t.Domain, PageSize: t.PageSize, NextPageToken: t.NextPageToken, } } func FromListSchedulesResponse(t *types.ListSchedulesResponse) *apiv1.ListSchedulesResponse { if t == nil { return nil } return &apiv1.ListSchedulesResponse{ Schedules: FromScheduleListEntryArray(t.Schedules), NextPageToken: t.NextPageToken, } } func ToListSchedulesResponse(t *apiv1.ListSchedulesResponse) *types.ListSchedulesResponse { if t == nil { return nil } return &types.ListSchedulesResponse{ Schedules: ToScheduleListEntryArray(t.Schedules), NextPageToken: t.NextPageToken, } } func FromBackfillScheduleRequest(t *types.BackfillScheduleRequest) *apiv1.BackfillScheduleRequest { if t == nil { return nil } return &apiv1.BackfillScheduleRequest{ Domain: t.Domain, ScheduleId: t.ScheduleID, StartTime: timeToTimestamp(&t.StartTime), EndTime: timeToTimestamp(&t.EndTime), OverlapPolicy: FromScheduleOverlapPolicy(t.OverlapPolicy), BackfillId: t.BackfillID, } } func ToBackfillScheduleRequest(t *apiv1.BackfillScheduleRequest) *types.BackfillScheduleRequest { if t == nil { return nil } return &types.BackfillScheduleRequest{ Domain: t.Domain, ScheduleID: t.ScheduleId, StartTime: timestampToTimeVal(t.StartTime), EndTime: timestampToTimeVal(t.EndTime), OverlapPolicy: ToScheduleOverlapPolicy(t.OverlapPolicy), BackfillID: t.BackfillId, } } func FromBackfillScheduleResponse(t *types.BackfillScheduleResponse) *apiv1.BackfillScheduleResponse { if t == nil { return nil } return &apiv1.BackfillScheduleResponse{} } func ToBackfillScheduleResponse(t *apiv1.BackfillScheduleResponse) *types.BackfillScheduleResponse { if t == nil { return nil } return &types.BackfillScheduleResponse{} } ================================================ FILE: common/types/mapper/proto/schedule_test.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package proto import ( "testing" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/testutils" "github.com/uber/cadence/common/types/testdata" ) func TestScheduleSpec(t *testing.T) { for _, item := range []*types.ScheduleSpec{nil, {}, &testdata.ScheduleSpec} { assert.Equal(t, item, ToScheduleSpec(FromScheduleSpec(item))) } } func TestStartWorkflowAction(t *testing.T) { for _, item := range []*types.StartWorkflowAction{nil, {}, &testdata.ScheduleStartWorkflowAction} { assert.Equal(t, item, ToStartWorkflowAction(FromStartWorkflowAction(item))) } } func TestScheduleAction(t *testing.T) { for _, item := range []*types.ScheduleAction{nil, {}, &testdata.ScheduleAction} { assert.Equal(t, item, ToScheduleAction(FromScheduleAction(item))) } } func TestSchedulePolicies(t *testing.T) { for _, item := range []*types.SchedulePolicies{nil, {}, &testdata.SchedulePolicies} { assert.Equal(t, item, ToSchedulePolicies(FromSchedulePolicies(item))) } } func TestSchedulePauseInfo(t *testing.T) { for _, item := range []*types.SchedulePauseInfo{nil, {}, &testdata.SchedulePauseInfo} { assert.Equal(t, item, ToSchedulePauseInfo(FromSchedulePauseInfo(item))) } } func TestScheduleState(t *testing.T) { for _, item := range []*types.ScheduleState{nil, {}, &testdata.ScheduleState} { assert.Equal(t, item, ToScheduleState(FromScheduleState(item))) } } func TestBackfillInfo(t *testing.T) { for _, item := range []*types.BackfillInfo{nil, {}, &testdata.ScheduleBackfillInfo} { assert.Equal(t, item, ToBackfillInfo(FromBackfillInfo(item))) } } func TestScheduleInfo(t *testing.T) { for _, item := range []*types.ScheduleInfo{nil, {}, &testdata.ScheduleInfo} { assert.Equal(t, item, ToScheduleInfo(FromScheduleInfo(item))) } } func TestScheduleListEntry(t *testing.T) { for _, item := range []*types.ScheduleListEntry{nil, {}, &testdata.ScheduleListEntry} { assert.Equal(t, item, ToScheduleListEntry(FromScheduleListEntry(item))) } } func TestScheduleSpecFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScheduleSpec, ToScheduleSpec, WithScheduleEnumFuzzers(), ) } func TestStartWorkflowActionFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromStartWorkflowAction, ToStartWorkflowAction, WithScheduleEnumFuzzers(), ) } func TestScheduleActionFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScheduleAction, ToScheduleAction, WithScheduleEnumFuzzers(), ) } func TestSchedulePoliciesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSchedulePolicies, ToSchedulePolicies, WithScheduleEnumFuzzers(), ) } func TestSchedulePauseInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSchedulePauseInfo, ToSchedulePauseInfo, WithScheduleEnumFuzzers(), ) } func TestScheduleStateFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScheduleState, ToScheduleState, WithScheduleEnumFuzzers(), ) } func TestBackfillInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromBackfillInfo, ToBackfillInfo, WithScheduleEnumFuzzers(), ) } func TestScheduleInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScheduleInfo, ToScheduleInfo, WithScheduleEnumFuzzers(), ) } func TestScheduleListEntryFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScheduleListEntry, ToScheduleListEntry, WithScheduleEnumFuzzers(), ) } // WithScheduleEnumFuzzers adds fuzzers for Schedule-specific enum types func WithScheduleEnumFuzzers() testutils.FuzzOption { return testutils.WithCustomFuncs( func(e *types.ScheduleOverlapPolicy, c fuzz.Continue) { *e = types.ScheduleOverlapPolicy(c.Intn(6)) // 0-5: Invalid through TerminatePrevious }, func(e *types.ScheduleCatchUpPolicy, c fuzz.Continue) { *e = types.ScheduleCatchUpPolicy(c.Intn(4)) // 0-3: Invalid through All }, ) } // --- CRUD request/response deterministic tests --- func TestCreateScheduleRequest(t *testing.T) { for _, item := range []*types.CreateScheduleRequest{nil, {}, &testdata.CreateScheduleRequest} { assert.Equal(t, item, ToCreateScheduleRequest(FromCreateScheduleRequest(item))) } } func TestCreateScheduleResponse(t *testing.T) { for _, item := range []*types.CreateScheduleResponse{nil, {}, &testdata.CreateScheduleResponse} { assert.Equal(t, item, ToCreateScheduleResponse(FromCreateScheduleResponse(item))) } } func TestDescribeScheduleRequest(t *testing.T) { for _, item := range []*types.DescribeScheduleRequest{nil, {}, &testdata.DescribeScheduleRequest} { assert.Equal(t, item, ToDescribeScheduleRequest(FromDescribeScheduleRequest(item))) } } func TestDescribeScheduleResponse(t *testing.T) { for _, item := range []*types.DescribeScheduleResponse{nil, {}, &testdata.DescribeScheduleResponse} { assert.Equal(t, item, ToDescribeScheduleResponse(FromDescribeScheduleResponse(item))) } } func TestUpdateScheduleRequest(t *testing.T) { for _, item := range []*types.UpdateScheduleRequest{nil, {}, &testdata.UpdateScheduleRequest} { assert.Equal(t, item, ToUpdateScheduleRequest(FromUpdateScheduleRequest(item))) } } func TestUpdateScheduleResponse(t *testing.T) { for _, item := range []*types.UpdateScheduleResponse{nil, {}, &testdata.UpdateScheduleResponse} { assert.Equal(t, item, ToUpdateScheduleResponse(FromUpdateScheduleResponse(item))) } } func TestDeleteScheduleRequest(t *testing.T) { for _, item := range []*types.DeleteScheduleRequest{nil, {}, &testdata.DeleteScheduleRequest} { assert.Equal(t, item, ToDeleteScheduleRequest(FromDeleteScheduleRequest(item))) } } func TestDeleteScheduleResponse(t *testing.T) { for _, item := range []*types.DeleteScheduleResponse{nil, {}, &testdata.DeleteScheduleResponse} { assert.Equal(t, item, ToDeleteScheduleResponse(FromDeleteScheduleResponse(item))) } } // --- CRUD request/response fuzz tests --- func TestCreateScheduleRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCreateScheduleRequest, ToCreateScheduleRequest, WithScheduleEnumFuzzers(), ) } func TestDescribeScheduleRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDescribeScheduleRequest, ToDescribeScheduleRequest, WithScheduleEnumFuzzers(), ) } func TestDescribeScheduleResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDescribeScheduleResponse, ToDescribeScheduleResponse, WithScheduleEnumFuzzers(), ) } func TestUpdateScheduleRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromUpdateScheduleRequest, ToUpdateScheduleRequest, WithScheduleEnumFuzzers(), ) } func TestDeleteScheduleRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDeleteScheduleRequest, ToDeleteScheduleRequest, WithScheduleEnumFuzzers(), ) } // --- Action request/response deterministic tests --- func TestPauseScheduleRequest(t *testing.T) { for _, item := range []*types.PauseScheduleRequest{nil, {}, &testdata.PauseScheduleRequest} { assert.Equal(t, item, ToPauseScheduleRequest(FromPauseScheduleRequest(item))) } } func TestPauseScheduleResponse(t *testing.T) { for _, item := range []*types.PauseScheduleResponse{nil, {}, &testdata.PauseScheduleResponse} { assert.Equal(t, item, ToPauseScheduleResponse(FromPauseScheduleResponse(item))) } } func TestUnpauseScheduleRequest(t *testing.T) { for _, item := range []*types.UnpauseScheduleRequest{nil, {}, &testdata.UnpauseScheduleRequest} { assert.Equal(t, item, ToUnpauseScheduleRequest(FromUnpauseScheduleRequest(item))) } } func TestUnpauseScheduleResponse(t *testing.T) { for _, item := range []*types.UnpauseScheduleResponse{nil, {}, &testdata.UnpauseScheduleResponse} { assert.Equal(t, item, ToUnpauseScheduleResponse(FromUnpauseScheduleResponse(item))) } } func TestListSchedulesRequest(t *testing.T) { for _, item := range []*types.ListSchedulesRequest{nil, {}, &testdata.ListSchedulesRequest} { assert.Equal(t, item, ToListSchedulesRequest(FromListSchedulesRequest(item))) } } func TestListSchedulesResponse(t *testing.T) { for _, item := range []*types.ListSchedulesResponse{nil, {}, &testdata.ListSchedulesResponse} { assert.Equal(t, item, ToListSchedulesResponse(FromListSchedulesResponse(item))) } } func TestBackfillScheduleRequest(t *testing.T) { for _, item := range []*types.BackfillScheduleRequest{nil, {}, &testdata.BackfillScheduleRequest} { assert.Equal(t, item, ToBackfillScheduleRequest(FromBackfillScheduleRequest(item))) } } func TestBackfillScheduleResponse(t *testing.T) { for _, item := range []*types.BackfillScheduleResponse{nil, {}, &testdata.BackfillScheduleResponse} { assert.Equal(t, item, ToBackfillScheduleResponse(FromBackfillScheduleResponse(item))) } } // --- Action request/response fuzz tests --- func TestPauseScheduleRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPauseScheduleRequest, ToPauseScheduleRequest, WithScheduleEnumFuzzers(), ) } func TestUnpauseScheduleRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromUnpauseScheduleRequest, ToUnpauseScheduleRequest, WithScheduleEnumFuzzers(), ) } func TestListSchedulesRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListSchedulesRequest, ToListSchedulesRequest, WithScheduleEnumFuzzers(), ) } func TestListSchedulesResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromListSchedulesResponse, ToListSchedulesResponse, WithScheduleEnumFuzzers(), ) } func TestBackfillScheduleRequestFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromBackfillScheduleRequest, ToBackfillScheduleRequest, WithScheduleEnumFuzzers(), ) } func TestCreateScheduleResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCreateScheduleResponse, ToCreateScheduleResponse, WithScheduleEnumFuzzers(), ) } func TestDeleteScheduleResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDeleteScheduleResponse, ToDeleteScheduleResponse, WithScheduleEnumFuzzers(), ) } func TestScheduleCatchUpPolicyFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScheduleCatchUpPolicy, ToScheduleCatchUpPolicy, WithScheduleEnumFuzzers(), ) } func TestScheduleOverlapPolicyFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScheduleOverlapPolicy, ToScheduleOverlapPolicy, WithScheduleEnumFuzzers(), ) } func TestScheduleListEntryArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromScheduleListEntryArray, ToScheduleListEntryArray, WithScheduleEnumFuzzers(), ) } func TestUpdateScheduleResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromUpdateScheduleResponse, ToUpdateScheduleResponse, WithScheduleEnumFuzzers(), ) } func TestBackfillScheduleResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromBackfillScheduleResponse, ToBackfillScheduleResponse, WithScheduleEnumFuzzers(), ) } func TestPauseScheduleResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPauseScheduleResponse, ToPauseScheduleResponse, WithScheduleEnumFuzzers(), ) } func TestUnpauseScheduleResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromUnpauseScheduleResponse, ToUnpauseScheduleResponse, WithScheduleEnumFuzzers(), ) } func TestBackfillInfoArrayFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromBackfillInfoArray, ToBackfillInfoArray, WithScheduleEnumFuzzers(), ) } ================================================ FILE: common/types/mapper/proto/sharddistributor.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/common/types" ) // FromShardDistributorGetShardOwnerRequest converts a types.GetShardOwnerRequest to a sharddistributor.GetShardOwnerRequest func FromShardDistributorGetShardOwnerRequest(t *types.GetShardOwnerRequest) *sharddistributorv1.GetShardOwnerRequest { if t == nil { return nil } return &sharddistributorv1.GetShardOwnerRequest{ ShardKey: t.GetShardKey(), Namespace: t.GetNamespace(), } } // ToShardDistributorGetShardOwnerRequest converts a sharddistributor.GetShardOwnerRequest to a types.GetShardOwnerRequest func ToShardDistributorGetShardOwnerRequest(t *sharddistributorv1.GetShardOwnerRequest) *types.GetShardOwnerRequest { if t == nil { return nil } return &types.GetShardOwnerRequest{ ShardKey: t.GetShardKey(), Namespace: t.GetNamespace(), } } // FromShardDistributorGetShardOwnerResponse converts a types.GetShardOwnerResponse to a sharddistributor.GetShardOwnerResponse func FromShardDistributorGetShardOwnerResponse(t *types.GetShardOwnerResponse) *sharddistributorv1.GetShardOwnerResponse { if t == nil { return nil } return &sharddistributorv1.GetShardOwnerResponse{ Owner: t.GetOwner(), Namespace: t.GetNamespace(), Metadata: t.GetMetadata(), } } // ToShardDistributorGetShardOwnerResponse converts a sharddistributor.GetShardOwnerResponse to a types.GetShardOwnerResponse func ToShardDistributorGetShardOwnerResponse(t *sharddistributorv1.GetShardOwnerResponse) *types.GetShardOwnerResponse { if t == nil { return nil } return &types.GetShardOwnerResponse{ Owner: t.GetOwner(), Namespace: t.GetNamespace(), Metadata: t.GetMetadata(), } } func FromShardDistributorExecutorHeartbeatRequest(t *types.ExecutorHeartbeatRequest) *sharddistributorv1.HeartbeatRequest { if t == nil { return nil } // Convert the ExecutorStatus enum var status sharddistributorv1.ExecutorStatus switch t.GetStatus() { case types.ExecutorStatusINVALID: status = sharddistributorv1.ExecutorStatus_EXECUTOR_STATUS_INVALID case types.ExecutorStatusACTIVE: status = sharddistributorv1.ExecutorStatus_EXECUTOR_STATUS_ACTIVE case types.ExecutorStatusDRAINING: status = sharddistributorv1.ExecutorStatus_EXECUTOR_STATUS_DRAINING case types.ExecutorStatusDRAINED: status = sharddistributorv1.ExecutorStatus_EXECUTOR_STATUS_DRAINED default: status = sharddistributorv1.ExecutorStatus_EXECUTOR_STATUS_INVALID } // Convert the ShardStatusReports var shardStatusReports map[string]*sharddistributorv1.ShardStatusReport if t.GetShardStatusReports() != nil { shardStatusReports = make(map[string]*sharddistributorv1.ShardStatusReport) for shardKey, shardStatusReport := range t.GetShardStatusReports() { var status sharddistributorv1.ShardStatus switch shardStatusReport.GetStatus() { case types.ShardStatusINVALID: status = sharddistributorv1.ShardStatus_SHARD_STATUS_INVALID case types.ShardStatusREADY: status = sharddistributorv1.ShardStatus_SHARD_STATUS_READY case types.ShardStatusDONE: status = sharddistributorv1.ShardStatus_SHARD_STATUS_DONE default: status = sharddistributorv1.ShardStatus_SHARD_STATUS_INVALID } shardStatusReports[shardKey] = &sharddistributorv1.ShardStatusReport{ Status: status, ShardLoad: shardStatusReport.GetShardLoad(), } } } return &sharddistributorv1.HeartbeatRequest{ Namespace: t.GetNamespace(), ExecutorId: t.GetExecutorID(), Status: status, ShardStatusReports: shardStatusReports, Metadata: t.GetMetadata(), } } func ToShardDistributorExecutorHeartbeatRequest(t *sharddistributorv1.HeartbeatRequest) *types.ExecutorHeartbeatRequest { if t == nil { return nil } // Convert the ExecutorStatus enum var status types.ExecutorStatus switch t.GetStatus() { case sharddistributorv1.ExecutorStatus_EXECUTOR_STATUS_INVALID: status = types.ExecutorStatusINVALID case sharddistributorv1.ExecutorStatus_EXECUTOR_STATUS_ACTIVE: status = types.ExecutorStatusACTIVE case sharddistributorv1.ExecutorStatus_EXECUTOR_STATUS_DRAINING: status = types.ExecutorStatusDRAINING case sharddistributorv1.ExecutorStatus_EXECUTOR_STATUS_DRAINED: status = types.ExecutorStatusDRAINED default: status = types.ExecutorStatusINVALID } // Convert the ShardStatusReports var shardStatusReports map[string]*types.ShardStatusReport if t.GetShardStatusReports() != nil { shardStatusReports = make(map[string]*types.ShardStatusReport) for shardKey, shardStatusReport := range t.GetShardStatusReports() { var status types.ShardStatus switch shardStatusReport.GetStatus() { case sharddistributorv1.ShardStatus_SHARD_STATUS_INVALID: status = types.ShardStatusINVALID case sharddistributorv1.ShardStatus_SHARD_STATUS_READY: status = types.ShardStatusREADY case sharddistributorv1.ShardStatus_SHARD_STATUS_DONE: status = types.ShardStatusDONE } shardStatusReports[shardKey] = &types.ShardStatusReport{ Status: status, ShardLoad: shardStatusReport.GetShardLoad(), } } } return &types.ExecutorHeartbeatRequest{ Namespace: t.GetNamespace(), ExecutorID: t.GetExecutorId(), Status: status, ShardStatusReports: shardStatusReports, Metadata: t.GetMetadata(), } } func FromShardDistributorExecutorHeartbeatResponse(t *types.ExecutorHeartbeatResponse) *sharddistributorv1.HeartbeatResponse { if t == nil { return nil } // Convert the ShardAssignments var shardAssignments map[string]*sharddistributorv1.ShardAssignment migrationMode := toMigrationMode(t.GetMigrationMode()) if t.GetShardAssignments() != nil { shardAssignments = make(map[string]*sharddistributorv1.ShardAssignment) for shardKey, shardAssignment := range t.GetShardAssignments() { var status sharddistributorv1.AssignmentStatus switch shardAssignment.GetStatus() { case types.AssignmentStatusINVALID: status = sharddistributorv1.AssignmentStatus_ASSIGNMENT_STATUS_INVALID case types.AssignmentStatusREADY: status = sharddistributorv1.AssignmentStatus_ASSIGNMENT_STATUS_READY } shardAssignments[shardKey] = &sharddistributorv1.ShardAssignment{ Status: status, } } } return &sharddistributorv1.HeartbeatResponse{ ShardAssignments: shardAssignments, MigrationMode: migrationMode, } } func ToShardDistributorExecutorHeartbeatResponse(t *sharddistributorv1.HeartbeatResponse) *types.ExecutorHeartbeatResponse { if t == nil { return nil } // Convert the ShardAssignments var shardAssignments map[string]*types.ShardAssignment var migrationMode types.MigrationMode if t.GetShardAssignments() != nil { shardAssignments = make(map[string]*types.ShardAssignment) for shardKey, shardAssignment := range t.GetShardAssignments() { var status types.AssignmentStatus switch shardAssignment.GetStatus() { case sharddistributorv1.AssignmentStatus_ASSIGNMENT_STATUS_INVALID: status = types.AssignmentStatusINVALID case sharddistributorv1.AssignmentStatus_ASSIGNMENT_STATUS_READY: status = types.AssignmentStatusREADY } shardAssignments[shardKey] = &types.ShardAssignment{ Status: status, } } } migrationMode = getMigrationModeFromProto(t.GetMigrationMode()) return &types.ExecutorHeartbeatResponse{ ShardAssignments: shardAssignments, MigrationMode: migrationMode, } } func getMigrationModeFromProto(protoMigrationMode sharddistributorv1.MigrationMode) types.MigrationMode { var mode types.MigrationMode switch protoMigrationMode { case sharddistributorv1.MigrationMode_MIGRATION_MODE_LOCAL_PASSTHROUGH: mode = types.MigrationModeLOCALPASSTHROUGH case sharddistributorv1.MigrationMode_MIGRATION_MODE_LOCAL_PASSTHROUGH_SHADOW: mode = types.MigrationModeLOCALPASSTHROUGHSHADOW case sharddistributorv1.MigrationMode_MIGRATION_MODE_DISTRIBUTED_PASSTHROUGH: mode = types.MigrationModeDISTRIBUTEDPASSTHROUGH case sharddistributorv1.MigrationMode_MIGRATION_MODE_ONBOARDED: mode = types.MigrationModeONBOARDED default: mode = types.MigrationModeINVALID } return mode } func toMigrationMode(modeSD types.MigrationMode) sharddistributorv1.MigrationMode { var mode sharddistributorv1.MigrationMode switch modeSD { case types.MigrationModeINVALID: mode = sharddistributorv1.MigrationMode_MIGRATION_MODE_INVALID case types.MigrationModeLOCALPASSTHROUGH: mode = sharddistributorv1.MigrationMode_MIGRATION_MODE_LOCAL_PASSTHROUGH case types.MigrationModeLOCALPASSTHROUGHSHADOW: mode = sharddistributorv1.MigrationMode_MIGRATION_MODE_LOCAL_PASSTHROUGH_SHADOW case types.MigrationModeDISTRIBUTEDPASSTHROUGH: mode = sharddistributorv1.MigrationMode_MIGRATION_MODE_DISTRIBUTED_PASSTHROUGH case types.MigrationModeONBOARDED: mode = sharddistributorv1.MigrationMode_MIGRATION_MODE_ONBOARDED default: mode = sharddistributorv1.MigrationMode_MIGRATION_MODE_INVALID } return mode } // FromShardDistributorWatchNamespaceStateRequest converts a types.WatchNamespaceStateRequest to a sharddistributor.WatchNamespaceStateRequest func FromShardDistributorWatchNamespaceStateRequest(t *types.WatchNamespaceStateRequest) *sharddistributorv1.WatchNamespaceStateRequest { if t == nil { return nil } return &sharddistributorv1.WatchNamespaceStateRequest{ Namespace: t.GetNamespace(), } } // ToShardDistributorWatchNamespaceStateRequest converts a sharddistributor.WatchNamespaceStateRequest to a types.WatchNamespaceStateRequest func ToShardDistributorWatchNamespaceStateRequest(t *sharddistributorv1.WatchNamespaceStateRequest) *types.WatchNamespaceStateRequest { if t == nil { return nil } return &types.WatchNamespaceStateRequest{ Namespace: t.GetNamespace(), } } // FromShardDistributorWatchNamespaceStateResponse converts a types.WatchNamespaceStateResponse to a sharddistributor.WatchNamespaceStateResponse func FromShardDistributorWatchNamespaceStateResponse(t *types.WatchNamespaceStateResponse) *sharddistributorv1.WatchNamespaceStateResponse { if t == nil { return nil } var executors []*sharddistributorv1.ExecutorInfo for _, executor := range t.GetExecutors() { // Convert the Shards shards := make([]*sharddistributorv1.Shard, 0, len(executor.GetAssignedShards())) for _, shard := range executor.GetAssignedShards() { shards = append(shards, &sharddistributorv1.Shard{ ShardKey: shard.GetShardKey(), }) } executors = append(executors, &sharddistributorv1.ExecutorInfo{ ExecutorId: executor.GetExecutorID(), Metadata: executor.GetMetadata(), Shards: shards, }) } return &sharddistributorv1.WatchNamespaceStateResponse{ Executors: executors, } } // ToShardDistributorWatchNamespaceStateResponse converts a sharddistributor.WatchNamespaceStateResponse to a types.WatchNamespaceStateResponse func ToShardDistributorWatchNamespaceStateResponse(t *sharddistributorv1.WatchNamespaceStateResponse) *types.WatchNamespaceStateResponse { if t == nil { return nil } var executors []*types.ExecutorShardAssignment if t.GetExecutors() != nil { executors = make([]*types.ExecutorShardAssignment, 0, len(t.GetExecutors())) for _, executor := range t.GetExecutors() { // Convert the Shards shards := make([]*types.Shard, 0, len(executor.GetShards())) for _, shard := range executor.GetShards() { shards = append(shards, &types.Shard{ ShardKey: shard.GetShardKey(), }) } executors = append(executors, &types.ExecutorShardAssignment{ ExecutorID: executor.GetExecutorId(), Metadata: executor.GetMetadata(), AssignedShards: shards, }) } } return &types.WatchNamespaceStateResponse{ Executors: executors, } } ================================================ FILE: common/types/mapper/proto/sharddistributor_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" ) func TestFromShardDistributorGetShardOwnerRequest(t *testing.T) { for _, item := range []*types.GetShardOwnerRequest{nil, {}, &testdata.ShardDistributorGetShardOwnerRequest} { assert.Equal(t, item, ToShardDistributorGetShardOwnerRequest(FromShardDistributorGetShardOwnerRequest(item))) } } func TestFromShardDistributorGetShardOwnerResponse(t *testing.T) { for _, item := range []*types.GetShardOwnerResponse{nil, {}, &testdata.ShardDistributorGetShardOwnerResponse} { assert.Equal(t, item, ToShardDistributorGetShardOwnerResponse(FromShardDistributorGetShardOwnerResponse(item))) } } func TestFromShardDistributorExecutorHeartbeatRequest(t *testing.T) { for _, item := range []*types.ExecutorHeartbeatRequest{nil, {}, &testdata.ShardDistributorExecutorHeartbeatRequest} { assert.Equal(t, item, ToShardDistributorExecutorHeartbeatRequest(FromShardDistributorExecutorHeartbeatRequest(item))) } } func TestToShardDistributorExecutorHeartbeatResponse(t *testing.T) { for _, item := range []*types.ExecutorHeartbeatResponse{nil, {}, &testdata.ShardDistributorExecutorHeartbeatResponse} { assert.Equal(t, item, ToShardDistributorExecutorHeartbeatResponse(FromShardDistributorExecutorHeartbeatResponse(item))) } } func TestFromShardDistributorWatchNamespaceStateRequest(t *testing.T) { for _, item := range []*types.WatchNamespaceStateRequest{nil, {}, &testdata.ShardDistributorWatchNamespaceStateRequest} { assert.Equal(t, item, ToShardDistributorWatchNamespaceStateRequest(FromShardDistributorWatchNamespaceStateRequest(item))) } } func TestFromShardDistributorWatchNamespaceStateResponse(t *testing.T) { for _, item := range []*types.WatchNamespaceStateResponse{nil, {}, &testdata.ShardDistributorWatchNamespaceStateResponse} { assert.Equal(t, item, ToShardDistributorWatchNamespaceStateResponse(FromShardDistributorWatchNamespaceStateResponse(item))) } } ================================================ FILE: common/types/mapper/proto/shared.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" sharedv1 "github.com/uber/cadence/.gen/proto/shared/v1" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func FromHostInfo(t *types.HostInfo) *adminv1.HostInfo { if t == nil { return nil } return &adminv1.HostInfo{ Identity: t.Identity, } } func ToHostInfo(t *adminv1.HostInfo) *types.HostInfo { if t == nil { return nil } return &types.HostInfo{ Identity: t.Identity, } } func FromMembershipInfo(t *types.MembershipInfo) *adminv1.MembershipInfo { if t == nil { return nil } return &adminv1.MembershipInfo{ CurrentHost: FromHostInfo(t.CurrentHost), ReachableMembers: t.ReachableMembers, Rings: FromRingInfoArray(t.Rings), } } func FromPersistenceSettings(t []*types.PersistenceSetting) []*adminv1.PersistenceSetting { if t == nil { return nil } v := make([]*adminv1.PersistenceSetting, len(t)) for i := range t { v[i] = FromPersistenceSetting(t[i]) } return v } func FromPersistenceSetting(t *types.PersistenceSetting) *adminv1.PersistenceSetting { if t == nil { return nil } return &adminv1.PersistenceSetting{ Key: t.Key, Value: t.Value, } } func FromPersistenceFeatures(t []*types.PersistenceFeature) []*adminv1.PersistenceFeature { if t == nil { return nil } v := make([]*adminv1.PersistenceFeature, len(t)) for i := range t { v[i] = FromPersistenceFeature(t[i]) } return v } func FromPersistenceFeature(t *types.PersistenceFeature) *adminv1.PersistenceFeature { if t == nil { return nil } return &adminv1.PersistenceFeature{ Key: t.Key, Enabled: t.Enabled, } } func FromPersistenceInfoMap(t map[string]*types.PersistenceInfo) map[string]*adminv1.PersistenceInfo { if t == nil { return nil } v := make(map[string]*adminv1.PersistenceInfo, len(t)) for key := range t { v[key] = FromPersistenceInfo(t[key]) } return v } func FromPersistenceInfo(t *types.PersistenceInfo) *adminv1.PersistenceInfo { if t == nil { return nil } return &adminv1.PersistenceInfo{ Backend: t.Backend, Settings: FromPersistenceSettings(t.Settings), Features: FromPersistenceFeatures(t.Features), } } func ToPersistenceSettings(t []*adminv1.PersistenceSetting) []*types.PersistenceSetting { if t == nil { return nil } v := make([]*types.PersistenceSetting, len(t)) for i := range t { v[i] = ToPersistenceSetting(t[i]) } return v } func ToPersistenceSetting(t *adminv1.PersistenceSetting) *types.PersistenceSetting { if t == nil { return nil } return &types.PersistenceSetting{ Key: t.Key, Value: t.Value, } } func ToPersistenceFeatures(t []*adminv1.PersistenceFeature) []*types.PersistenceFeature { if t == nil { return nil } v := make([]*types.PersistenceFeature, len(t)) for i := range t { v[i] = ToPersistenceFeature(t[i]) } return v } func ToPersistenceFeature(t *adminv1.PersistenceFeature) *types.PersistenceFeature { if t == nil { return nil } return &types.PersistenceFeature{ Key: t.Key, Enabled: t.Enabled, } } func ToPersistenceInfoMap(t map[string]*adminv1.PersistenceInfo) map[string]*types.PersistenceInfo { if t == nil { return nil } v := make(map[string]*types.PersistenceInfo, len(t)) for key := range t { v[key] = ToPersistenceInfo(t[key]) } return v } func ToPersistenceInfo(t *adminv1.PersistenceInfo) *types.PersistenceInfo { if t == nil { return nil } return &types.PersistenceInfo{ Backend: t.Backend, Settings: ToPersistenceSettings(t.Settings), Features: ToPersistenceFeatures(t.Features), } } func ToMembershipInfo(t *adminv1.MembershipInfo) *types.MembershipInfo { if t == nil { return nil } return &types.MembershipInfo{ CurrentHost: ToHostInfo(t.CurrentHost), ReachableMembers: t.ReachableMembers, Rings: ToRingInfoArray(t.Rings), } } func FromDomainCacheInfo(t *types.DomainCacheInfo) *adminv1.DomainCacheInfo { if t == nil { return nil } return &adminv1.DomainCacheInfo{ NumOfItemsInCacheById: t.NumOfItemsInCacheByID, NumOfItemsInCacheByName: t.NumOfItemsInCacheByName, } } func ToDomainCacheInfo(t *adminv1.DomainCacheInfo) *types.DomainCacheInfo { if t == nil { return nil } return &types.DomainCacheInfo{ NumOfItemsInCacheByID: t.NumOfItemsInCacheById, NumOfItemsInCacheByName: t.NumOfItemsInCacheByName, } } func FromRingInfo(t *types.RingInfo) *adminv1.RingInfo { if t == nil { return nil } return &adminv1.RingInfo{ Role: t.Role, MemberCount: t.MemberCount, Members: FromHostInfoArray(t.Members), } } func ToRingInfo(t *adminv1.RingInfo) *types.RingInfo { if t == nil { return nil } return &types.RingInfo{ Role: t.Role, MemberCount: t.MemberCount, Members: ToHostInfoArray(t.Members), } } func FromTaskSource(t *types.TaskSource) sharedv1.TaskSource { if t == nil { return sharedv1.TaskSource_TASK_SOURCE_INVALID } switch *t { case types.TaskSourceHistory: return sharedv1.TaskSource_TASK_SOURCE_HISTORY case types.TaskSourceDbBacklog: return sharedv1.TaskSource_TASK_SOURCE_DB_BACKLOG } return sharedv1.TaskSource_TASK_SOURCE_INVALID } func ToTaskSource(t sharedv1.TaskSource) *types.TaskSource { switch t { case sharedv1.TaskSource_TASK_SOURCE_INVALID: return nil case sharedv1.TaskSource_TASK_SOURCE_HISTORY: return types.TaskSourceHistory.Ptr() case sharedv1.TaskSource_TASK_SOURCE_DB_BACKLOG: return types.TaskSourceDbBacklog.Ptr() } return nil } func FromTransientDecisionInfo(t *types.TransientDecisionInfo) *sharedv1.TransientDecisionInfo { if t == nil { return nil } return &sharedv1.TransientDecisionInfo{ ScheduledEvent: FromHistoryEvent(t.ScheduledEvent), StartedEvent: FromHistoryEvent(t.StartedEvent), } } func ToTransientDecisionInfo(t *sharedv1.TransientDecisionInfo) *types.TransientDecisionInfo { if t == nil { return nil } return &types.TransientDecisionInfo{ ScheduledEvent: ToHistoryEvent(t.ScheduledEvent), StartedEvent: ToHistoryEvent(t.StartedEvent), } } func FromVersionHistories(t *types.VersionHistories) *sharedv1.VersionHistories { if t == nil { return nil } return &sharedv1.VersionHistories{ CurrentVersionHistoryIndex: t.CurrentVersionHistoryIndex, Histories: FromVersionHistoryArray(t.Histories), } } func ToVersionHistories(t *sharedv1.VersionHistories) *types.VersionHistories { if t == nil { return nil } return &types.VersionHistories{ CurrentVersionHistoryIndex: t.CurrentVersionHistoryIndex, Histories: ToVersionHistoryArray(t.Histories), } } func FromVersionHistory(t *types.VersionHistory) *adminv1.VersionHistory { if t == nil { return nil } return &adminv1.VersionHistory{ BranchToken: t.BranchToken, Items: FromVersionHistoryItemArray(t.Items), } } func ToVersionHistory(t *adminv1.VersionHistory) *types.VersionHistory { if t == nil { return nil } return &types.VersionHistory{ BranchToken: t.BranchToken, Items: ToVersionHistoryItemArray(t.Items), } } func FromVersionHistoryItem(t *types.VersionHistoryItem) *adminv1.VersionHistoryItem { if t == nil { return nil } return &adminv1.VersionHistoryItem{ EventId: t.EventID, Version: t.Version, } } func ToVersionHistoryItem(t *adminv1.VersionHistoryItem) *types.VersionHistoryItem { if t == nil { return nil } return &types.VersionHistoryItem{ EventID: t.EventId, Version: t.Version, } } func FromWorkflowState(t *int32) sharedv1.WorkflowState { if t == nil { return sharedv1.WorkflowState_WORKFLOW_STATE_INVALID } switch *t { case persistence.WorkflowStateCreated: return sharedv1.WorkflowState_WORKFLOW_STATE_CREATED case persistence.WorkflowStateRunning: return sharedv1.WorkflowState_WORKFLOW_STATE_RUNNING case persistence.WorkflowStateCompleted: return sharedv1.WorkflowState_WORKFLOW_STATE_COMPLETED case persistence.WorkflowStateZombie: return sharedv1.WorkflowState_WORKFLOW_STATE_ZOMBIE case persistence.WorkflowStateVoid: return sharedv1.WorkflowState_WORKFLOW_STATE_VOID case persistence.WorkflowStateCorrupted: return sharedv1.WorkflowState_WORKFLOW_STATE_CORRUPTED } return sharedv1.WorkflowState_WORKFLOW_STATE_INVALID } func ToWorkflowState(t sharedv1.WorkflowState) *int32 { switch t { case sharedv1.WorkflowState_WORKFLOW_STATE_INVALID: return nil case sharedv1.WorkflowState_WORKFLOW_STATE_CREATED: v := int32(persistence.WorkflowStateCreated) return &v case sharedv1.WorkflowState_WORKFLOW_STATE_RUNNING: v := int32(persistence.WorkflowStateRunning) return &v case sharedv1.WorkflowState_WORKFLOW_STATE_COMPLETED: v := int32(persistence.WorkflowStateCompleted) return &v case sharedv1.WorkflowState_WORKFLOW_STATE_ZOMBIE: v := int32(persistence.WorkflowStateZombie) return &v case sharedv1.WorkflowState_WORKFLOW_STATE_VOID: v := int32(persistence.WorkflowStateVoid) return &v case sharedv1.WorkflowState_WORKFLOW_STATE_CORRUPTED: v := int32(persistence.WorkflowStateCorrupted) return &v } return nil } func FromHostInfoArray(t []*types.HostInfo) []*adminv1.HostInfo { if t == nil { return nil } v := make([]*adminv1.HostInfo, len(t)) for i := range t { v[i] = FromHostInfo(t[i]) } return v } func ToHostInfoArray(t []*adminv1.HostInfo) []*types.HostInfo { if t == nil { return nil } v := make([]*types.HostInfo, len(t)) for i := range t { v[i] = ToHostInfo(t[i]) } return v } func FromVersionHistoryArray(t []*types.VersionHistory) []*adminv1.VersionHistory { if t == nil { return nil } v := make([]*adminv1.VersionHistory, len(t)) for i := range t { v[i] = FromVersionHistory(t[i]) } return v } func ToVersionHistoryArray(t []*adminv1.VersionHistory) []*types.VersionHistory { if t == nil { return nil } v := make([]*types.VersionHistory, len(t)) for i := range t { v[i] = ToVersionHistory(t[i]) } return v } func FromRingInfoArray(t []*types.RingInfo) []*adminv1.RingInfo { if t == nil { return nil } v := make([]*adminv1.RingInfo, len(t)) for i := range t { v[i] = FromRingInfo(t[i]) } return v } func ToRingInfoArray(t []*adminv1.RingInfo) []*types.RingInfo { if t == nil { return nil } v := make([]*types.RingInfo, len(t)) for i := range t { v[i] = ToRingInfo(t[i]) } return v } func FromDLQType(t *types.DLQType) adminv1.DLQType { if t == nil { return adminv1.DLQType_DLQ_TYPE_INVALID } switch *t { case types.DLQTypeReplication: return adminv1.DLQType_DLQ_TYPE_REPLICATION case types.DLQTypeDomain: return adminv1.DLQType_DLQ_TYPE_DOMAIN } return adminv1.DLQType_DLQ_TYPE_INVALID } func ToDLQType(t adminv1.DLQType) *types.DLQType { switch t { case adminv1.DLQType_DLQ_TYPE_INVALID: return nil case adminv1.DLQType_DLQ_TYPE_REPLICATION: return types.DLQTypeReplication.Ptr() case adminv1.DLQType_DLQ_TYPE_DOMAIN: return types.DLQTypeDomain.Ptr() } return nil } func FromDomainOperation(t *types.DomainOperation) adminv1.DomainOperation { if t == nil { return adminv1.DomainOperation_DOMAIN_OPERATION_INVALID } switch *t { case types.DomainOperationCreate: return adminv1.DomainOperation_DOMAIN_OPERATION_CREATE case types.DomainOperationUpdate: return adminv1.DomainOperation_DOMAIN_OPERATION_UPDATE case types.DomainOperationDelete: return adminv1.DomainOperation_DOMAIN_OPERATION_DELETE } return adminv1.DomainOperation_DOMAIN_OPERATION_INVALID } func ToDomainOperation(t adminv1.DomainOperation) *types.DomainOperation { switch t { case adminv1.DomainOperation_DOMAIN_OPERATION_INVALID: return nil case adminv1.DomainOperation_DOMAIN_OPERATION_CREATE: return types.DomainOperationCreate.Ptr() case adminv1.DomainOperation_DOMAIN_OPERATION_UPDATE: return types.DomainOperationUpdate.Ptr() case adminv1.DomainOperation_DOMAIN_OPERATION_DELETE: return types.DomainOperationDelete.Ptr() } return nil } func FromDomainTaskAttributes(t *types.DomainTaskAttributes) *adminv1.DomainTaskAttributes { if t == nil { return nil } return &adminv1.DomainTaskAttributes{ DomainOperation: FromDomainOperation(t.DomainOperation), Id: t.ID, Domain: FromDescribeDomainResponseDomain(&types.DescribeDomainResponse{ DomainInfo: t.Info, Configuration: t.Config, ReplicationConfiguration: t.ReplicationConfig, }), ConfigVersion: t.ConfigVersion, FailoverVersion: t.FailoverVersion, PreviousFailoverVersion: t.PreviousFailoverVersion, } } func ToDomainTaskAttributes(t *adminv1.DomainTaskAttributes) *types.DomainTaskAttributes { if t == nil { return nil } domain := ToDescribeDomainResponseDomain(t.Domain) return &types.DomainTaskAttributes{ DomainOperation: ToDomainOperation(t.DomainOperation), ID: t.Id, Info: domain.DomainInfo, Config: domain.Configuration, ReplicationConfig: domain.ReplicationConfiguration, ConfigVersion: t.ConfigVersion, FailoverVersion: t.FailoverVersion, PreviousFailoverVersion: t.PreviousFailoverVersion, } } func FromFailoverMarkerAttributes(t *types.FailoverMarkerAttributes) *adminv1.FailoverMarkerAttributes { if t == nil { return nil } return &adminv1.FailoverMarkerAttributes{ DomainId: t.DomainID, FailoverVersion: t.FailoverVersion, CreationTime: unixNanoToTime(t.CreationTime), } } func ToFailoverMarkerAttributes(t *adminv1.FailoverMarkerAttributes) *types.FailoverMarkerAttributes { if t == nil { return nil } return &types.FailoverMarkerAttributes{ DomainID: t.DomainId, FailoverVersion: t.FailoverVersion, CreationTime: timeToUnixNano(t.CreationTime), } } func FromFailoverMarkerToken(t *types.FailoverMarkerToken) *adminv1.FailoverMarkerToken { if t == nil { return nil } return &adminv1.FailoverMarkerToken{ ShardIds: t.ShardIDs, FailoverMarker: FromFailoverMarkerAttributes(t.FailoverMarker), } } func ToFailoverMarkerToken(t *adminv1.FailoverMarkerToken) *types.FailoverMarkerToken { if t == nil { return nil } return &types.FailoverMarkerToken{ ShardIDs: t.ShardIds, FailoverMarker: ToFailoverMarkerAttributes(t.FailoverMarker), } } func FromHistoryTaskV2Attributes(t *types.HistoryTaskV2Attributes) *adminv1.HistoryTaskV2Attributes { if t == nil { return nil } return &adminv1.HistoryTaskV2Attributes{ DomainId: t.DomainID, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), VersionHistoryItems: FromVersionHistoryItemArray(t.VersionHistoryItems), Events: FromDataBlob(t.Events), NewRunEvents: FromDataBlob(t.NewRunEvents), } } func ToHistoryTaskV2Attributes(t *adminv1.HistoryTaskV2Attributes) *types.HistoryTaskV2Attributes { if t == nil { return nil } return &types.HistoryTaskV2Attributes{ DomainID: t.DomainId, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), VersionHistoryItems: ToVersionHistoryItemArray(t.VersionHistoryItems), Events: ToDataBlob(t.Events), NewRunEvents: ToDataBlob(t.NewRunEvents), } } func FromReplicationMessages(t *types.ReplicationMessages) *adminv1.ReplicationMessages { if t == nil { return nil } return &adminv1.ReplicationMessages{ ReplicationTasks: FromReplicationTaskArray(t.ReplicationTasks), LastRetrievedMessageId: t.LastRetrievedMessageID, HasMore: t.HasMore, SyncShardStatus: FromSyncShardStatus(t.SyncShardStatus), } } func ToReplicationMessages(t *adminv1.ReplicationMessages) *types.ReplicationMessages { if t == nil { return nil } return &types.ReplicationMessages{ ReplicationTasks: ToReplicationTaskArray(t.ReplicationTasks), LastRetrievedMessageID: t.LastRetrievedMessageId, HasMore: t.HasMore, SyncShardStatus: ToSyncShardStatus(t.SyncShardStatus), } } // ReplicationMessagesSize returns the size (in bytes) of the types.ReplicationMessages func ReplicationMessagesSize(t *types.ReplicationMessages) int { if t == nil { return 0 } return FromReplicationMessages(t).Size() } func FromReplicationTaskInfo(t *types.ReplicationTaskInfo) *adminv1.ReplicationTaskInfo { if t == nil { return nil } return &adminv1.ReplicationTaskInfo{ DomainId: t.DomainID, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), TaskType: int32(t.TaskType), TaskId: t.TaskID, Version: t.Version, FirstEventId: t.FirstEventID, NextEventId: t.NextEventID, ScheduledId: t.ScheduledID, } } func ToReplicationTaskInfo(t *adminv1.ReplicationTaskInfo) *types.ReplicationTaskInfo { if t == nil { return nil } return &types.ReplicationTaskInfo{ DomainID: t.DomainId, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), TaskType: int16(t.TaskType), TaskID: t.TaskId, Version: t.Version, FirstEventID: t.FirstEventId, NextEventID: t.NextEventId, ScheduledID: t.ScheduledId, } } func FromReplicationTaskType(t *types.ReplicationTaskType) adminv1.ReplicationTaskType { if t == nil { return adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_INVALID } switch *t { case types.ReplicationTaskTypeDomain: return adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_DOMAIN case types.ReplicationTaskTypeHistory: return adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_HISTORY case types.ReplicationTaskTypeSyncShardStatus: return adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_SYNC_SHARD_STATUS case types.ReplicationTaskTypeSyncActivity: return adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_SYNC_ACTIVITY case types.ReplicationTaskTypeHistoryMetadata: return adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_HISTORY_METADATA case types.ReplicationTaskTypeHistoryV2: return adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_HISTORY_V2 case types.ReplicationTaskTypeFailoverMarker: return adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_FAILOVER_MARKER } return adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_INVALID } func ToReplicationTaskType(t adminv1.ReplicationTaskType) *types.ReplicationTaskType { switch t { case adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_INVALID: return nil case adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_DOMAIN: return types.ReplicationTaskTypeDomain.Ptr() case adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_HISTORY: return types.ReplicationTaskTypeHistory.Ptr() case adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_SYNC_SHARD_STATUS: return types.ReplicationTaskTypeSyncShardStatus.Ptr() case adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_SYNC_ACTIVITY: return types.ReplicationTaskTypeSyncActivity.Ptr() case adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_HISTORY_METADATA: return types.ReplicationTaskTypeHistoryMetadata.Ptr() case adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_HISTORY_V2: return types.ReplicationTaskTypeHistoryV2.Ptr() case adminv1.ReplicationTaskType_REPLICATION_TASK_TYPE_FAILOVER_MARKER: return types.ReplicationTaskTypeFailoverMarker.Ptr() } return nil } func FromReplicationToken(t *types.ReplicationToken) *adminv1.ReplicationToken { if t == nil { return nil } return &adminv1.ReplicationToken{ ShardId: t.ShardID, LastRetrievedMessageId: t.LastRetrievedMessageID, LastProcessedMessageId: t.LastProcessedMessageID, } } func ToReplicationToken(t *adminv1.ReplicationToken) *types.ReplicationToken { if t == nil { return nil } return &types.ReplicationToken{ ShardID: t.ShardId, LastRetrievedMessageID: t.LastRetrievedMessageId, LastProcessedMessageID: t.LastProcessedMessageId, } } func FromSyncActivityTaskAttributes(t *types.SyncActivityTaskAttributes) *adminv1.SyncActivityTaskAttributes { if t == nil { return nil } return &adminv1.SyncActivityTaskAttributes{ DomainId: t.DomainID, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), Version: t.Version, ScheduledId: t.ScheduledID, ScheduledTime: unixNanoToTime(t.ScheduledTime), StartedId: t.StartedID, StartedTime: unixNanoToTime(t.StartedTime), LastHeartbeatTime: unixNanoToTime(t.LastHeartbeatTime), Details: FromPayload(t.Details), Attempt: t.Attempt, LastFailure: FromFailure(t.LastFailureReason, t.LastFailureDetails), LastWorkerIdentity: t.LastWorkerIdentity, VersionHistory: FromVersionHistory(t.VersionHistory), } } func ToSyncActivityTaskAttributes(t *adminv1.SyncActivityTaskAttributes) *types.SyncActivityTaskAttributes { if t == nil { return nil } return &types.SyncActivityTaskAttributes{ DomainID: t.DomainId, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), Version: t.Version, ScheduledID: t.ScheduledId, ScheduledTime: timeToUnixNano(t.ScheduledTime), StartedID: t.StartedId, StartedTime: timeToUnixNano(t.StartedTime), LastHeartbeatTime: timeToUnixNano(t.LastHeartbeatTime), Details: ToPayload(t.Details), Attempt: t.Attempt, LastFailureReason: ToFailureReason(t.LastFailure), LastFailureDetails: ToFailureDetails(t.LastFailure), LastWorkerIdentity: t.LastWorkerIdentity, VersionHistory: ToVersionHistory(t.VersionHistory), } } func FromSyncShardStatus(t *types.SyncShardStatus) *adminv1.SyncShardStatus { if t == nil { return nil } return &adminv1.SyncShardStatus{ Timestamp: unixNanoToTime(t.Timestamp), } } func ToSyncShardStatus(t *adminv1.SyncShardStatus) *types.SyncShardStatus { if t == nil { return nil } return &types.SyncShardStatus{ Timestamp: timeToUnixNano(t.Timestamp), } } func FromSyncShardStatusTaskAttributes(t *types.SyncShardStatusTaskAttributes) *adminv1.SyncShardStatusTaskAttributes { if t == nil { return nil } return &adminv1.SyncShardStatusTaskAttributes{ SourceCluster: t.SourceCluster, ShardId: int32(t.ShardID), Timestamp: unixNanoToTime(t.Timestamp), } } func ToSyncShardStatusTaskAttributes(t *adminv1.SyncShardStatusTaskAttributes) *types.SyncShardStatusTaskAttributes { if t == nil { return nil } return &types.SyncShardStatusTaskAttributes{ SourceCluster: t.SourceCluster, ShardID: int64(t.ShardId), Timestamp: timeToUnixNano(t.Timestamp), } } func FromReplicationTaskInfoArray(t []*types.ReplicationTaskInfo) []*adminv1.ReplicationTaskInfo { if t == nil { return nil } v := make([]*adminv1.ReplicationTaskInfo, len(t)) for i := range t { v[i] = FromReplicationTaskInfo(t[i]) } return v } func ToReplicationTaskInfoArray(t []*adminv1.ReplicationTaskInfo) []*types.ReplicationTaskInfo { if t == nil { return nil } v := make([]*types.ReplicationTaskInfo, len(t)) for i := range t { v[i] = ToReplicationTaskInfo(t[i]) } return v } func FromReplicationTaskArray(t []*types.ReplicationTask) []*adminv1.ReplicationTask { if t == nil { return nil } v := make([]*adminv1.ReplicationTask, len(t)) for i := range t { v[i] = FromReplicationTask(t[i]) } return v } func ToReplicationTaskArray(t []*adminv1.ReplicationTask) []*types.ReplicationTask { if t == nil { return nil } v := make([]*types.ReplicationTask, len(t)) for i := range t { v[i] = ToReplicationTask(t[i]) } return v } func FromReplicationTokenArray(t []*types.ReplicationToken) []*adminv1.ReplicationToken { if t == nil { return nil } v := make([]*adminv1.ReplicationToken, len(t)) for i := range t { v[i] = FromReplicationToken(t[i]) } return v } func ToReplicationTokenArray(t []*adminv1.ReplicationToken) []*types.ReplicationToken { if t == nil { return nil } v := make([]*types.ReplicationToken, len(t)) for i := range t { v[i] = ToReplicationToken(t[i]) } return v } func FromReplicationMessagesMap(t map[int32]*types.ReplicationMessages) map[int32]*adminv1.ReplicationMessages { if t == nil { return nil } v := make(map[int32]*adminv1.ReplicationMessages, len(t)) for key := range t { v[key] = FromReplicationMessages(t[key]) } return v } func ToReplicationMessagesMap(t map[int32]*adminv1.ReplicationMessages) map[int32]*types.ReplicationMessages { if t == nil { return nil } v := make(map[int32]*types.ReplicationMessages, len(t)) for key := range t { v[key] = ToReplicationMessages(t[key]) } return v } func FromReplicationTask(t *types.ReplicationTask) *adminv1.ReplicationTask { if t == nil { return nil } task := adminv1.ReplicationTask{ TaskType: FromReplicationTaskType(t.TaskType), SourceTaskId: t.SourceTaskID, CreationTime: unixNanoToTime(t.CreationTime), } if t.DomainTaskAttributes != nil { task.Attributes = &adminv1.ReplicationTask_DomainTaskAttributes{ DomainTaskAttributes: FromDomainTaskAttributes(t.DomainTaskAttributes), } } if t.SyncShardStatusTaskAttributes != nil { task.Attributes = &adminv1.ReplicationTask_SyncShardStatusTaskAttributes{ SyncShardStatusTaskAttributes: FromSyncShardStatusTaskAttributes(t.SyncShardStatusTaskAttributes), } } if t.SyncActivityTaskAttributes != nil { task.Attributes = &adminv1.ReplicationTask_SyncActivityTaskAttributes{ SyncActivityTaskAttributes: FromSyncActivityTaskAttributes(t.SyncActivityTaskAttributes), } } if t.HistoryTaskV2Attributes != nil { task.Attributes = &adminv1.ReplicationTask_HistoryTaskV2Attributes{ HistoryTaskV2Attributes: FromHistoryTaskV2Attributes(t.HistoryTaskV2Attributes), } } if t.FailoverMarkerAttributes != nil { task.Attributes = &adminv1.ReplicationTask_FailoverMarkerAttributes{ FailoverMarkerAttributes: FromFailoverMarkerAttributes(t.FailoverMarkerAttributes), } } return &task } func ToReplicationTask(t *adminv1.ReplicationTask) *types.ReplicationTask { if t == nil { return nil } task := types.ReplicationTask{ TaskType: ToReplicationTaskType(t.TaskType), SourceTaskID: t.SourceTaskId, CreationTime: timeToUnixNano(t.CreationTime), } switch attr := t.Attributes.(type) { case *adminv1.ReplicationTask_DomainTaskAttributes: task.DomainTaskAttributes = ToDomainTaskAttributes(attr.DomainTaskAttributes) case *adminv1.ReplicationTask_SyncShardStatusTaskAttributes: task.SyncShardStatusTaskAttributes = ToSyncShardStatusTaskAttributes(attr.SyncShardStatusTaskAttributes) case *adminv1.ReplicationTask_SyncActivityTaskAttributes: task.SyncActivityTaskAttributes = ToSyncActivityTaskAttributes(attr.SyncActivityTaskAttributes) case *adminv1.ReplicationTask_HistoryTaskV2Attributes: task.HistoryTaskV2Attributes = ToHistoryTaskV2Attributes(attr.HistoryTaskV2Attributes) case *adminv1.ReplicationTask_FailoverMarkerAttributes: task.FailoverMarkerAttributes = ToFailoverMarkerAttributes(attr.FailoverMarkerAttributes) } return &task } func FromTaskType(t *int32) adminv1.TaskType { if t == nil { return adminv1.TaskType_TASK_TYPE_INVALID } switch constants.TaskType(*t) { case constants.TaskTypeTransfer: return adminv1.TaskType_TASK_TYPE_TRANSFER case constants.TaskTypeTimer: return adminv1.TaskType_TASK_TYPE_TIMER case constants.TaskTypeReplication: return adminv1.TaskType_TASK_TYPE_REPLICATION case constants.TaskTypeCrossCluster: return adminv1.TaskType_TASK_TYPE_CROSS_CLUSTER } return adminv1.TaskType_TASK_TYPE_INVALID } func ToTaskType(t adminv1.TaskType) *int32 { switch t { case adminv1.TaskType_TASK_TYPE_INVALID: return nil case adminv1.TaskType_TASK_TYPE_TRANSFER: return common.Int32Ptr(int32(constants.TaskTypeTransfer)) case adminv1.TaskType_TASK_TYPE_TIMER: return common.Int32Ptr(int32(constants.TaskTypeTimer)) case adminv1.TaskType_TASK_TYPE_REPLICATION: return common.Int32Ptr(int32(constants.TaskTypeReplication)) case adminv1.TaskType_TASK_TYPE_CROSS_CLUSTER: return common.Int32Ptr(int32(constants.TaskTypeCrossCluster)) } return nil } func FromFailoverMarkerTokenArray(t []*types.FailoverMarkerToken) []*adminv1.FailoverMarkerToken { if t == nil { return nil } v := make([]*adminv1.FailoverMarkerToken, len(t)) for i := range t { v[i] = FromFailoverMarkerToken(t[i]) } return v } func ToFailoverMarkerTokenArray(t []*adminv1.FailoverMarkerToken) []*types.FailoverMarkerToken { if t == nil { return nil } v := make([]*types.FailoverMarkerToken, len(t)) for i := range t { v[i] = ToFailoverMarkerToken(t[i]) } return v } func FromVersionHistoryItemArray(t []*types.VersionHistoryItem) []*adminv1.VersionHistoryItem { if t == nil { return nil } v := make([]*adminv1.VersionHistoryItem, len(t)) for i := range t { v[i] = FromVersionHistoryItem(t[i]) } return v } func ToVersionHistoryItemArray(t []*adminv1.VersionHistoryItem) []*types.VersionHistoryItem { if t == nil { return nil } v := make([]*types.VersionHistoryItem, len(t)) for i := range t { v[i] = ToVersionHistoryItem(t[i]) } return v } func FromEventIDVersionPair(id, version *int64) *adminv1.VersionHistoryItem { if id == nil || version == nil { return nil } return &adminv1.VersionHistoryItem{ EventId: *id, Version: *version, } } func ToEventID(item *adminv1.VersionHistoryItem) *int64 { if item == nil { return nil } return common.Int64Ptr(item.EventId) } func ToEventVersion(item *adminv1.VersionHistoryItem) *int64 { if item == nil { return nil } return common.Int64Ptr(item.Version) } // FromCrossClusterTaskType converts internal CrossClusterTaskType type to proto func FromCrossClusterTaskType(t *types.CrossClusterTaskType) adminv1.CrossClusterTaskType { if t == nil { return adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_INVALID } switch *t { case types.CrossClusterTaskTypeStartChildExecution: return adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_START_CHILD_EXECUTION case types.CrossClusterTaskTypeCancelExecution: return adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_CANCEL_EXECUTION case types.CrossClusterTaskTypeSignalExecution: return adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_SIGNAL_EXECUTION case types.CrossClusterTaskTypeRecordChildWorkflowExeuctionComplete: return adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_RECORD_CHILD_WORKKLOW_EXECUTION_COMPLETE case types.CrossClusterTaskTypeApplyParentPolicy: return adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_APPLY_PARENT_CLOSE_POLICY } return adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_INVALID } // ToCrossClusterTaskType converts proto CrossClusterTaskType type to internal func ToCrossClusterTaskType(t adminv1.CrossClusterTaskType) *types.CrossClusterTaskType { switch t { case adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_INVALID: return nil case adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_START_CHILD_EXECUTION: return types.CrossClusterTaskTypeStartChildExecution.Ptr() case adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_CANCEL_EXECUTION: return types.CrossClusterTaskTypeCancelExecution.Ptr() case adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_SIGNAL_EXECUTION: return types.CrossClusterTaskTypeSignalExecution.Ptr() case adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_RECORD_CHILD_WORKKLOW_EXECUTION_COMPLETE: return types.CrossClusterTaskTypeRecordChildWorkflowExeuctionComplete.Ptr() case adminv1.CrossClusterTaskType_CROSS_CLUSTER_TASK_TYPE_APPLY_PARENT_CLOSE_POLICY: return types.CrossClusterTaskTypeApplyParentPolicy.Ptr() } return nil } // FromCrossClusterTaskFailedCause converts internal CrossClusterTaskFailedCause type to proto func FromCrossClusterTaskFailedCause(t *types.CrossClusterTaskFailedCause) adminv1.CrossClusterTaskFailedCause { if t == nil { return adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_INVALID } switch *t { case types.CrossClusterTaskFailedCauseDomainNotActive: return adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_DOMAIN_NOT_ACTIVE case types.CrossClusterTaskFailedCauseDomainNotExists: return adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_DOMAIN_NOT_EXISTS case types.CrossClusterTaskFailedCauseWorkflowAlreadyRunning: return adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_WORKFLOW_ALREADY_RUNNING case types.CrossClusterTaskFailedCauseWorkflowNotExists: return adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_WORKFLOW_NOT_EXISTS case types.CrossClusterTaskFailedCauseWorkflowAlreadyCompleted: return adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_WORKFLOW_ALREADY_COMPLETED case types.CrossClusterTaskFailedCauseUncategorized: return adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_UNCATEGORIZED } return adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_INVALID } // ToCrossClusterTaskFailedCause converts proto CrossClusterTaskFailedCause type to internal func ToCrossClusterTaskFailedCause(t adminv1.CrossClusterTaskFailedCause) *types.CrossClusterTaskFailedCause { switch t { case adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_INVALID: return nil case adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_DOMAIN_NOT_ACTIVE: return types.CrossClusterTaskFailedCauseDomainNotActive.Ptr() case adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_DOMAIN_NOT_EXISTS: return types.CrossClusterTaskFailedCauseDomainNotExists.Ptr() case adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_WORKFLOW_ALREADY_RUNNING: return types.CrossClusterTaskFailedCauseWorkflowAlreadyRunning.Ptr() case adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_WORKFLOW_NOT_EXISTS: return types.CrossClusterTaskFailedCauseWorkflowNotExists.Ptr() case adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_WORKFLOW_ALREADY_COMPLETED: return types.CrossClusterTaskFailedCauseWorkflowAlreadyCompleted.Ptr() case adminv1.CrossClusterTaskFailedCause_CROSS_CLUSTER_TASK_FAILED_CAUSE_UNCATEGORIZED: return types.CrossClusterTaskFailedCauseUncategorized.Ptr() } return nil } // FromGetTaskFailedCause converts internal GetTaskFailedCause type to proto func FromGetTaskFailedCause(t *types.GetTaskFailedCause) adminv1.GetTaskFailedCause { if t == nil { return adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_INVALID } switch *t { case types.GetTaskFailedCauseServiceBusy: return adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_SERVICE_BUSY case types.GetTaskFailedCauseTimeout: return adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_TIMEOUT case types.GetTaskFailedCauseShardOwnershipLost: return adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_SHARD_OWNERSHIP_LOST case types.GetTaskFailedCauseUncategorized: return adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_UNCATEGORIZED } return adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_INVALID } // ToGetTaskFailedCause converts proto GetTaskFailedCause type to internal func ToGetTaskFailedCause(t adminv1.GetTaskFailedCause) *types.GetTaskFailedCause { switch t { case adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_INVALID: return nil case adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_SERVICE_BUSY: return types.GetTaskFailedCauseServiceBusy.Ptr() case adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_TIMEOUT: return types.GetTaskFailedCauseTimeout.Ptr() case adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_SHARD_OWNERSHIP_LOST: return types.GetTaskFailedCauseShardOwnershipLost.Ptr() case adminv1.GetTaskFailedCause_GET_TASK_FAILED_CAUSE_UNCATEGORIZED: return types.GetTaskFailedCauseUncategorized.Ptr() } return nil } // FromCrossClusterTaskInfo converts internal CrossClusterTaskInfo type to proto func FromCrossClusterTaskInfo(t *types.CrossClusterTaskInfo) *adminv1.CrossClusterTaskInfo { if t == nil { return nil } return &adminv1.CrossClusterTaskInfo{ DomainId: t.DomainID, WorkflowExecution: FromWorkflowRunPair(t.WorkflowID, t.RunID), TaskType: FromCrossClusterTaskType(t.TaskType), TaskState: int32(t.TaskState), TaskId: t.TaskID, VisibilityTimestamp: unixNanoToTime(t.VisibilityTimestamp), } } // ToCrossClusterTaskInfo converts proto CrossClusterTaskInfo type to internal func ToCrossClusterTaskInfo(t *adminv1.CrossClusterTaskInfo) *types.CrossClusterTaskInfo { if t == nil { return nil } return &types.CrossClusterTaskInfo{ DomainID: t.DomainId, WorkflowID: ToWorkflowID(t.WorkflowExecution), RunID: ToRunID(t.WorkflowExecution), TaskType: ToCrossClusterTaskType(t.TaskType), TaskState: int16(t.TaskState), TaskID: t.TaskId, VisibilityTimestamp: timeToUnixNano(t.VisibilityTimestamp), } } // FromCrossClusterStartChildExecutionRequestAttributes converts internal CrossClusterStartChildExecutionRequestAttributes type to proto func FromCrossClusterStartChildExecutionRequestAttributes(t *types.CrossClusterStartChildExecutionRequestAttributes) *adminv1.CrossClusterStartChildExecutionRequestAttributes { if t == nil { return nil } return &adminv1.CrossClusterStartChildExecutionRequestAttributes{ TargetDomainId: t.TargetDomainID, RequestId: t.RequestID, InitiatedEventId: t.InitiatedEventID, InitiatedEventAttributes: FromStartChildWorkflowExecutionInitiatedEventAttributes(t.InitiatedEventAttributes), TargetRunId: t.GetTargetRunID(), PartitionConfig: t.PartitionConfig, } } // ToCrossClusterStartChildExecutionRequestAttributes converts proto CrossClusterStartChildExecutionRequestAttributes type to internal func ToCrossClusterStartChildExecutionRequestAttributes(t *adminv1.CrossClusterStartChildExecutionRequestAttributes) *types.CrossClusterStartChildExecutionRequestAttributes { if t == nil { return nil } return &types.CrossClusterStartChildExecutionRequestAttributes{ TargetDomainID: t.TargetDomainId, RequestID: t.RequestId, InitiatedEventID: t.InitiatedEventId, InitiatedEventAttributes: ToStartChildWorkflowExecutionInitiatedEventAttributes(t.InitiatedEventAttributes), TargetRunID: &t.TargetRunId, PartitionConfig: t.PartitionConfig, } } // FromCrossClusterStartChildExecutionResponseAttributes converts internal CrossClusterStartChildExecutionResponseAttributes type to proto func FromCrossClusterStartChildExecutionResponseAttributes(t *types.CrossClusterStartChildExecutionResponseAttributes) *adminv1.CrossClusterStartChildExecutionResponseAttributes { if t == nil { return nil } return &adminv1.CrossClusterStartChildExecutionResponseAttributes{ RunId: t.RunID, } } // ToCrossClusterStartChildExecutionResponseAttributes converts proto CrossClusterStartChildExecutionResponseAttributes type to internal func ToCrossClusterStartChildExecutionResponseAttributes(t *adminv1.CrossClusterStartChildExecutionResponseAttributes) *types.CrossClusterStartChildExecutionResponseAttributes { if t == nil { return nil } return &types.CrossClusterStartChildExecutionResponseAttributes{ RunID: t.RunId, } } // FromCrossClusterCancelExecutionRequestAttributes converts internal CrossClusterCancelExecutionRequestAttributes type to proto func FromCrossClusterCancelExecutionRequestAttributes(t *types.CrossClusterCancelExecutionRequestAttributes) *adminv1.CrossClusterCancelExecutionRequestAttributes { if t == nil { return nil } return &adminv1.CrossClusterCancelExecutionRequestAttributes{ TargetDomainId: t.TargetDomainID, TargetWorkflowExecution: FromWorkflowRunPair(t.TargetWorkflowID, t.TargetRunID), RequestId: t.RequestID, InitiatedEventId: t.InitiatedEventID, ChildWorkflowOnly: t.ChildWorkflowOnly, } } // ToCrossClusterCancelExecutionRequestAttributes converts proto CrossClusterCancelExecutionRequestAttributes type to internal func ToCrossClusterCancelExecutionRequestAttributes(t *adminv1.CrossClusterCancelExecutionRequestAttributes) *types.CrossClusterCancelExecutionRequestAttributes { if t == nil { return nil } return &types.CrossClusterCancelExecutionRequestAttributes{ TargetDomainID: t.TargetDomainId, TargetWorkflowID: ToWorkflowID(t.TargetWorkflowExecution), TargetRunID: ToRunID(t.TargetWorkflowExecution), RequestID: t.RequestId, InitiatedEventID: t.InitiatedEventId, ChildWorkflowOnly: t.ChildWorkflowOnly, } } // FromCrossClusterCancelExecutionResponseAttributes converts internal CrossClusterCancelExecutionResponseAttributes type to proto func FromCrossClusterCancelExecutionResponseAttributes(t *types.CrossClusterCancelExecutionResponseAttributes) *adminv1.CrossClusterCancelExecutionResponseAttributes { if t == nil { return nil } return &adminv1.CrossClusterCancelExecutionResponseAttributes{} } // ToCrossClusterCancelExecutionResponseAttributes converts proto CrossClusterCancelExecutionResponseAttributes type to internal func ToCrossClusterCancelExecutionResponseAttributes(t *adminv1.CrossClusterCancelExecutionResponseAttributes) *types.CrossClusterCancelExecutionResponseAttributes { if t == nil { return nil } return &types.CrossClusterCancelExecutionResponseAttributes{} } // FromCrossClusterSignalExecutionRequestAttributes converts internal CrossClusterSignalExecutionRequestAttributes type to proto func FromCrossClusterSignalExecutionRequestAttributes(t *types.CrossClusterSignalExecutionRequestAttributes) *adminv1.CrossClusterSignalExecutionRequestAttributes { if t == nil { return nil } return &adminv1.CrossClusterSignalExecutionRequestAttributes{ TargetDomainId: t.TargetDomainID, TargetWorkflowExecution: FromWorkflowRunPair(t.TargetWorkflowID, t.TargetRunID), RequestId: t.RequestID, InitiatedEventId: t.InitiatedEventID, ChildWorkflowOnly: t.ChildWorkflowOnly, SignalName: t.SignalName, SignalInput: FromPayload(t.SignalInput), Control: t.Control, } } // ToCrossClusterSignalExecutionRequestAttributes converts proto CrossClusterSignalExecutionRequestAttributes type to internal func ToCrossClusterSignalExecutionRequestAttributes(t *adminv1.CrossClusterSignalExecutionRequestAttributes) *types.CrossClusterSignalExecutionRequestAttributes { if t == nil { return nil } return &types.CrossClusterSignalExecutionRequestAttributes{ TargetDomainID: t.TargetDomainId, TargetWorkflowID: ToWorkflowID(t.TargetWorkflowExecution), TargetRunID: ToRunID(t.TargetWorkflowExecution), RequestID: t.RequestId, InitiatedEventID: t.InitiatedEventId, ChildWorkflowOnly: t.ChildWorkflowOnly, SignalName: t.SignalName, SignalInput: ToPayload(t.SignalInput), Control: t.Control, } } // FromCrossClusterSignalExecutionResponseAttributes converts internal CrossClusterSignalExecutionResponseAttributes type to proto func FromCrossClusterSignalExecutionResponseAttributes(t *types.CrossClusterSignalExecutionResponseAttributes) *adminv1.CrossClusterSignalExecutionResponseAttributes { if t == nil { return nil } return &adminv1.CrossClusterSignalExecutionResponseAttributes{} } // ToCrossClusterSignalExecutionResponseAttributes converts proto CrossClusterSignalExecutionResponseAttributes type to internal func ToCrossClusterSignalExecutionResponseAttributes(t *adminv1.CrossClusterSignalExecutionResponseAttributes) *types.CrossClusterSignalExecutionResponseAttributes { if t == nil { return nil } return &types.CrossClusterSignalExecutionResponseAttributes{} } // FromCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes converts internal CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes type to proto func FromCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes(t *types.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) *adminv1.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes { if t == nil { return nil } return &adminv1.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes{ TargetDomainId: t.TargetDomainID, TargetWorkflowExecution: FromWorkflowRunPair(t.TargetWorkflowID, t.TargetRunID), InitiatedEventId: t.InitiatedEventID, CompletionEvent: FromHistoryEvent(t.CompletionEvent), } } // ToCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes converts proto CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes type to internal func ToCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes(t *adminv1.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes) *types.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes { if t == nil { return nil } return &types.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes{ TargetDomainID: t.TargetDomainId, TargetWorkflowID: ToWorkflowID(t.TargetWorkflowExecution), TargetRunID: ToRunID(t.TargetWorkflowExecution), InitiatedEventID: t.InitiatedEventId, CompletionEvent: ToHistoryEvent(t.CompletionEvent), } } // FromCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes converts internal CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes type to proto func FromCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes(t *types.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) *adminv1.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes { if t == nil { return nil } return &adminv1.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes{} } // ToCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes converts proto CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes type to internal func ToCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes(t *adminv1.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) *types.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes { if t == nil { return nil } return &types.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes{} } // FromApplyParentClosePolicyStatus converts internal ApplyParentClosePolicyStatus type to proto func FromApplyParentClosePolicyStatus(t *types.ApplyParentClosePolicyStatus) *adminv1.ApplyParentClosePolicyStatus { if t == nil { return nil } return &adminv1.ApplyParentClosePolicyStatus{ Completed: t.Completed, FailedCause: FromCrossClusterTaskFailedCause(t.FailedCause), } } // ToApplyParentClosePolicyStatus converts proto ApplyParentClosePolicyStatus type to internal func ToApplyParentClosePolicyStatus(t *adminv1.ApplyParentClosePolicyStatus) *types.ApplyParentClosePolicyStatus { if t == nil { return nil } return &types.ApplyParentClosePolicyStatus{ Completed: t.Completed, FailedCause: ToCrossClusterTaskFailedCause(t.FailedCause), } } // FromApplyParentClosePolicyAttributes converts internal ApplyParentClosePolicyAttributes type to proto func FromApplyParentClosePolicyAttributes(t *types.ApplyParentClosePolicyAttributes) *adminv1.ApplyParentClosePolicyAttributes { if t == nil { return nil } return &adminv1.ApplyParentClosePolicyAttributes{ ChildDomainId: t.ChildDomainID, ChildWorkflowId: t.ChildWorkflowID, ChildRunId: t.ChildRunID, ParentClosePolicy: FromParentClosePolicy(t.ParentClosePolicy), } } // ToApplyParentClosePolicyAttributes converts proto ApplyParentClosePolicyAttributes type to internal func ToApplyParentClosePolicyAttributes(t *adminv1.ApplyParentClosePolicyAttributes) *types.ApplyParentClosePolicyAttributes { if t == nil { return nil } return &types.ApplyParentClosePolicyAttributes{ ChildDomainID: t.ChildDomainId, ChildWorkflowID: t.ChildWorkflowId, ChildRunID: t.ChildRunId, ParentClosePolicy: ToParentClosePolicy(t.ParentClosePolicy), } } // FromApplyParentClosePolicyResult converts proto ApplyParentClosePolicyResult type to internal func FromApplyParentClosePolicyResult(t *types.ApplyParentClosePolicyResult) *adminv1.ApplyParentClosePolicyResult { if t == nil { return nil } return &adminv1.ApplyParentClosePolicyResult{ Child: FromApplyParentClosePolicyAttributes(t.Child), FailedCause: FromCrossClusterTaskFailedCause(t.FailedCause), } } // ToApplyParentClosePolicyResult converts proto ApplyParentClosePolicyResult type to internal func ToApplyParentClosePolicyResult(t *adminv1.ApplyParentClosePolicyResult) *types.ApplyParentClosePolicyResult { if t == nil { return nil } return &types.ApplyParentClosePolicyResult{ Child: ToApplyParentClosePolicyAttributes(t.Child), FailedCause: ToCrossClusterTaskFailedCause(t.FailedCause), } } // FromCrossClusterApplyParentClosePolicyRequestAttributes converts internal CrossClusterApplyParentClosePolicyRequestAttributes type to proto func FromCrossClusterApplyParentClosePolicyRequestAttributes(t *types.CrossClusterApplyParentClosePolicyRequestAttributes) *adminv1.CrossClusterApplyParentClosePolicyRequestAttributes { if t == nil { return nil } requestAttributes := &adminv1.CrossClusterApplyParentClosePolicyRequestAttributes{} for _, execution := range t.Children { requestAttributes.Children = append( requestAttributes.Children, &adminv1.ApplyParentClosePolicyRequest{ Child: FromApplyParentClosePolicyAttributes(execution.Child), Status: FromApplyParentClosePolicyStatus(execution.Status), }, ) } return requestAttributes } // ToCrossClusterApplyParentClosePolicyRequestAttributes converts proto CrossClusterApplyParentClosePolicyRequestAttributes type to internal func ToCrossClusterApplyParentClosePolicyRequestAttributes(t *adminv1.CrossClusterApplyParentClosePolicyRequestAttributes) *types.CrossClusterApplyParentClosePolicyRequestAttributes { if t == nil { return nil } requestAttributes := &types.CrossClusterApplyParentClosePolicyRequestAttributes{} for _, execution := range t.Children { requestAttributes.Children = append( requestAttributes.Children, &types.ApplyParentClosePolicyRequest{ Child: ToApplyParentClosePolicyAttributes(execution.Child), Status: ToApplyParentClosePolicyStatus(execution.Status), }, ) } return requestAttributes } // FromCrossClusterApplyParentClosePolicyResponseAttributes converts internal CrossClusterApplyParentClosePolicyResponseAttributes type to proto func FromCrossClusterApplyParentClosePolicyResponseAttributes(t *types.CrossClusterApplyParentClosePolicyResponseAttributes) *adminv1.CrossClusterApplyParentClosePolicyResponseAttributes { if t == nil { return nil } response := &adminv1.CrossClusterApplyParentClosePolicyResponseAttributes{} for _, childStatus := range t.ChildrenStatus { response.ChildrenStatus = append( response.ChildrenStatus, FromApplyParentClosePolicyResult(childStatus), ) } return response } // ToCrossClusterApplyParentClosePolicyResponseAttributes converts proto CrossClusterApplyParentClosePolicyResponseAttributes type to internal func ToCrossClusterApplyParentClosePolicyResponseAttributes(t *adminv1.CrossClusterApplyParentClosePolicyResponseAttributes) *types.CrossClusterApplyParentClosePolicyResponseAttributes { if t == nil { return nil } response := &types.CrossClusterApplyParentClosePolicyResponseAttributes{} for _, childStatus := range t.ChildrenStatus { response.ChildrenStatus = append( response.ChildrenStatus, ToApplyParentClosePolicyResult(childStatus), ) } return response } // FromCrossClusterTaskRequest converts internal CrossClusterTaskRequest type to proto func FromCrossClusterTaskRequest(t *types.CrossClusterTaskRequest) *adminv1.CrossClusterTaskRequest { if t == nil { return nil } request := adminv1.CrossClusterTaskRequest{ TaskInfo: FromCrossClusterTaskInfo(t.TaskInfo), } if t.StartChildExecutionAttributes != nil { request.Attributes = &adminv1.CrossClusterTaskRequest_StartChildExecutionAttributes{ StartChildExecutionAttributes: FromCrossClusterStartChildExecutionRequestAttributes(t.StartChildExecutionAttributes), } } if t.CancelExecutionAttributes != nil { request.Attributes = &adminv1.CrossClusterTaskRequest_CancelExecutionAttributes{ CancelExecutionAttributes: FromCrossClusterCancelExecutionRequestAttributes(t.CancelExecutionAttributes), } } if t.SignalExecutionAttributes != nil { request.Attributes = &adminv1.CrossClusterTaskRequest_SignalExecutionAttributes{ SignalExecutionAttributes: FromCrossClusterSignalExecutionRequestAttributes(t.SignalExecutionAttributes), } } if t.RecordChildWorkflowExecutionCompleteAttributes != nil { request.Attributes = &adminv1.CrossClusterTaskRequest_RecordChildWorkflowExecutionCompleteRequestAttributes{ RecordChildWorkflowExecutionCompleteRequestAttributes: FromCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes(t.RecordChildWorkflowExecutionCompleteAttributes), } } if t.ApplyParentClosePolicyAttributes != nil { request.Attributes = &adminv1.CrossClusterTaskRequest_ApplyParentClosePolicyRequestAttributes{ ApplyParentClosePolicyRequestAttributes: FromCrossClusterApplyParentClosePolicyRequestAttributes(t.ApplyParentClosePolicyAttributes), } } return &request } // ToCrossClusterTaskRequest converts proto CrossClusterTaskRequest type to internal func ToCrossClusterTaskRequest(t *adminv1.CrossClusterTaskRequest) *types.CrossClusterTaskRequest { if t == nil { return nil } request := types.CrossClusterTaskRequest{ TaskInfo: ToCrossClusterTaskInfo(t.TaskInfo), } switch attr := t.Attributes.(type) { case *adminv1.CrossClusterTaskRequest_StartChildExecutionAttributes: request.StartChildExecutionAttributes = ToCrossClusterStartChildExecutionRequestAttributes(attr.StartChildExecutionAttributes) case *adminv1.CrossClusterTaskRequest_CancelExecutionAttributes: request.CancelExecutionAttributes = ToCrossClusterCancelExecutionRequestAttributes(attr.CancelExecutionAttributes) case *adminv1.CrossClusterTaskRequest_SignalExecutionAttributes: request.SignalExecutionAttributes = ToCrossClusterSignalExecutionRequestAttributes(attr.SignalExecutionAttributes) case *adminv1.CrossClusterTaskRequest_RecordChildWorkflowExecutionCompleteRequestAttributes: request.RecordChildWorkflowExecutionCompleteAttributes = ToCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes(attr.RecordChildWorkflowExecutionCompleteRequestAttributes) case *adminv1.CrossClusterTaskRequest_ApplyParentClosePolicyRequestAttributes: request.ApplyParentClosePolicyAttributes = ToCrossClusterApplyParentClosePolicyRequestAttributes(attr.ApplyParentClosePolicyRequestAttributes) } return &request } // FromCrossClusterTaskResponse converts internal CrossClusterTaskResponse type to proto func FromCrossClusterTaskResponse(t *types.CrossClusterTaskResponse) *adminv1.CrossClusterTaskResponse { if t == nil { return nil } response := adminv1.CrossClusterTaskResponse{ TaskId: t.TaskID, TaskType: FromCrossClusterTaskType(t.TaskType), TaskState: int32(t.TaskState), FailedCause: FromCrossClusterTaskFailedCause(t.FailedCause), } if t.StartChildExecutionAttributes != nil { response.Attributes = &adminv1.CrossClusterTaskResponse_StartChildExecutionAttributes{ StartChildExecutionAttributes: FromCrossClusterStartChildExecutionResponseAttributes(t.StartChildExecutionAttributes), } } if t.CancelExecutionAttributes != nil { response.Attributes = &adminv1.CrossClusterTaskResponse_CancelExecutionAttributes{ CancelExecutionAttributes: FromCrossClusterCancelExecutionResponseAttributes(t.CancelExecutionAttributes), } } if t.SignalExecutionAttributes != nil { response.Attributes = &adminv1.CrossClusterTaskResponse_SignalExecutionAttributes{ SignalExecutionAttributes: FromCrossClusterSignalExecutionResponseAttributes(t.SignalExecutionAttributes), } } if t.RecordChildWorkflowExecutionCompleteAttributes != nil { response.Attributes = &adminv1.CrossClusterTaskResponse_RecordChildWorkflowExecutionCompleteRequestAttributes{ RecordChildWorkflowExecutionCompleteRequestAttributes: FromCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes(t.RecordChildWorkflowExecutionCompleteAttributes), } } if t.ApplyParentClosePolicyAttributes != nil { response.Attributes = &adminv1.CrossClusterTaskResponse_ApplyParentClosePolicyResponseAttributes{ ApplyParentClosePolicyResponseAttributes: FromCrossClusterApplyParentClosePolicyResponseAttributes(t.ApplyParentClosePolicyAttributes), } } return &response } // ToCrossClusterTaskResponse converts proto CrossClusterTaskResponse type to internal func ToCrossClusterTaskResponse(t *adminv1.CrossClusterTaskResponse) *types.CrossClusterTaskResponse { if t == nil { return nil } response := types.CrossClusterTaskResponse{ TaskID: t.TaskId, TaskType: ToCrossClusterTaskType(t.TaskType), TaskState: int16(t.TaskState), FailedCause: ToCrossClusterTaskFailedCause(t.FailedCause), } switch attr := t.Attributes.(type) { case *adminv1.CrossClusterTaskResponse_StartChildExecutionAttributes: response.StartChildExecutionAttributes = ToCrossClusterStartChildExecutionResponseAttributes(attr.StartChildExecutionAttributes) case *adminv1.CrossClusterTaskResponse_CancelExecutionAttributes: response.CancelExecutionAttributes = ToCrossClusterCancelExecutionResponseAttributes(attr.CancelExecutionAttributes) case *adminv1.CrossClusterTaskResponse_SignalExecutionAttributes: response.SignalExecutionAttributes = ToCrossClusterSignalExecutionResponseAttributes(attr.SignalExecutionAttributes) case *adminv1.CrossClusterTaskResponse_RecordChildWorkflowExecutionCompleteRequestAttributes: response.RecordChildWorkflowExecutionCompleteAttributes = ToCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes(attr.RecordChildWorkflowExecutionCompleteRequestAttributes) case *adminv1.CrossClusterTaskResponse_ApplyParentClosePolicyResponseAttributes: response.ApplyParentClosePolicyAttributes = ToCrossClusterApplyParentClosePolicyResponseAttributes(attr.ApplyParentClosePolicyResponseAttributes) } return &response } // FromCrossClusterTaskRequestArray converts internal CrossClusterTaskRequest type array to proto func FromCrossClusterTaskRequestArray(t []*types.CrossClusterTaskRequest) *adminv1.CrossClusterTaskRequests { if t == nil { return nil } v := make([]*adminv1.CrossClusterTaskRequest, len(t)) for i := range t { v[i] = FromCrossClusterTaskRequest(t[i]) } return &adminv1.CrossClusterTaskRequests{ TaskRequests: v, } } // ToCrossClusterTaskRequestArray converts proto CrossClusterTaskRequest type array to internal func ToCrossClusterTaskRequestArray(t *adminv1.CrossClusterTaskRequests) []*types.CrossClusterTaskRequest { if t == nil || t.TaskRequests == nil { return nil } v := make([]*types.CrossClusterTaskRequest, len(t.TaskRequests)) for i := range t.TaskRequests { v[i] = ToCrossClusterTaskRequest(t.TaskRequests[i]) } return v } // FromCrossClusterTaskRequestMap converts internal CrossClusterTaskRequest type map to proto func FromCrossClusterTaskRequestMap(t map[int32][]*types.CrossClusterTaskRequest) map[int32]*adminv1.CrossClusterTaskRequests { if t == nil { return nil } v := make(map[int32]*adminv1.CrossClusterTaskRequests, len(t)) for key := range t { v[key] = FromCrossClusterTaskRequestArray(t[key]) } return v } // ToCrossClusterTaskRequestMap converts proto CrossClusterTaskRequest type map to internal func ToCrossClusterTaskRequestMap(t map[int32]*adminv1.CrossClusterTaskRequests) map[int32][]*types.CrossClusterTaskRequest { if t == nil { return nil } v := make(map[int32][]*types.CrossClusterTaskRequest, len(t)) for key := range t { value := ToCrossClusterTaskRequestArray(t[key]) if value == nil { // grpc can't differentiate between empty array or nil array // our application logic ensure no nil array will be returned // for CrossClusterTaskRequest, so always convert to empty array // we only need the special handling here as this array is used // as a map value in GetCrossClusterTasksResponse, // and if the map value is nil, THRIFT won't be able to encode the value // this may happen when we are using grpc within a cluster but thrift across cluster value = []*types.CrossClusterTaskRequest{} } v[key] = value } return v } // FromGetTaskFailedCauseMap converts internal GetTaskFailedCause type map to proto func FromGetTaskFailedCauseMap(t map[int32]types.GetTaskFailedCause) map[int32]adminv1.GetTaskFailedCause { if t == nil { return nil } v := make(map[int32]adminv1.GetTaskFailedCause, len(t)) for key, value := range t { v[key] = FromGetTaskFailedCause(&value) } return v } // ToGetTaskFailedCauseMap converts proto GetTaskFailedCause type map to internal func ToGetTaskFailedCauseMap(t map[int32]adminv1.GetTaskFailedCause) map[int32]types.GetTaskFailedCause { if t == nil { return nil } v := make(map[int32]types.GetTaskFailedCause, len(t)) for key := range t { if internalValue := ToGetTaskFailedCause(t[key]); internalValue != nil { v[key] = *internalValue } } return v } // FromCrossClusterTaskResponseArray converts internal CrossClusterTaskResponse type array to proto func FromCrossClusterTaskResponseArray(t []*types.CrossClusterTaskResponse) []*adminv1.CrossClusterTaskResponse { if t == nil { return nil } v := make([]*adminv1.CrossClusterTaskResponse, len(t)) for i := range t { v[i] = FromCrossClusterTaskResponse(t[i]) } return v } // ToCrossClusterTaskResponseArray converts proto CrossClusterTaskResponse type array to internal func ToCrossClusterTaskResponseArray(t []*adminv1.CrossClusterTaskResponse) []*types.CrossClusterTaskResponse { if t == nil { return nil } v := make([]*types.CrossClusterTaskResponse, len(t)) for i := range t { v[i] = ToCrossClusterTaskResponse(t[i]) } return v } func FromHistoryDLQCountEntryMap(t map[types.HistoryDLQCountKey]int64) []*adminv1.HistoryDLQCountEntry { if t == nil { return nil } entries := make([]*adminv1.HistoryDLQCountEntry, 0, len(t)) for key, count := range t { entries = append(entries, &adminv1.HistoryDLQCountEntry{ ShardId: key.ShardID, SourceCluster: key.SourceCluster, Count: count, }) } return entries } func ToHistoryDLQCountEntryMap(t []*adminv1.HistoryDLQCountEntry) map[types.HistoryDLQCountKey]int64 { if t == nil { return nil } entries := make(map[types.HistoryDLQCountKey]int64, len(t)) for _, entry := range t { key := types.HistoryDLQCountKey{ShardID: entry.ShardId, SourceCluster: entry.SourceCluster} entries[key] = entry.Count } return entries } // ToAny converts thrift Any type to internal func ToAny(t *sharedv1.Any) *types.Any { if t == nil { return nil } return &types.Any{ ValueType: t.GetValueType(), Value: t.Value, } } // FromAny converts internal Any type to thrift func FromAny(t *types.Any) *sharedv1.Any { if t == nil { return nil } return &sharedv1.Any{ ValueType: t.ValueType, Value: t.Value, } } ================================================ FILE: common/types/mapper/proto/shared_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package proto import ( "reflect" "testing" "github.com/gogo/protobuf/proto" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" sharedv1 "github.com/uber/cadence/.gen/proto/shared/v1" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/testutils" "github.com/uber/cadence/common/types/testdata" ) func TestHostInfo(t *testing.T) { for _, item := range []*types.HostInfo{nil, {}, &testdata.HostInfo} { assert.Equal(t, item, ToHostInfo(FromHostInfo(item))) } } func TestMembershipInfo(t *testing.T) { for _, item := range []*types.MembershipInfo{nil, {}, &testdata.MembershipInfo} { assert.Equal(t, item, ToMembershipInfo(FromMembershipInfo(item))) } } func TestDomainCacheInfo(t *testing.T) { for _, item := range []*types.DomainCacheInfo{nil, {}, &testdata.DomainCacheInfo} { assert.Equal(t, item, ToDomainCacheInfo(FromDomainCacheInfo(item))) } } func TestRingInfo(t *testing.T) { for _, item := range []*types.RingInfo{nil, {}, &testdata.RingInfo} { assert.Equal(t, item, ToRingInfo(FromRingInfo(item))) } } func TestTransientDecisionInfo(t *testing.T) { for _, item := range []*types.TransientDecisionInfo{nil, {}, &testdata.TransientDecisionInfo} { assert.Equal(t, item, ToTransientDecisionInfo(FromTransientDecisionInfo(item))) } } func TestVersionHistories(t *testing.T) { for _, item := range []*types.VersionHistories{nil, {}, &testdata.VersionHistories} { assert.Equal(t, item, ToVersionHistories(FromVersionHistories(item))) } } func TestVersionHistory(t *testing.T) { for _, item := range []*types.VersionHistory{nil, {}, &testdata.VersionHistory} { assert.Equal(t, item, ToVersionHistory(FromVersionHistory(item))) } } func TestVersionHistoryItem(t *testing.T) { for _, item := range []*types.VersionHistoryItem{nil, {}, &testdata.VersionHistoryItem} { assert.Equal(t, item, ToVersionHistoryItem(FromVersionHistoryItem(item))) } } func TestHostInfoArray(t *testing.T) { for _, item := range [][]*types.HostInfo{nil, {}, testdata.HostInfoArray} { assert.Equal(t, item, ToHostInfoArray(FromHostInfoArray(item))) } } func TestVersionHistoryArray(t *testing.T) { for _, item := range [][]*types.VersionHistory{nil, {}, testdata.VersionHistoryArray} { assert.Equal(t, item, ToVersionHistoryArray(FromVersionHistoryArray(item))) } } func TestRingInfoArray(t *testing.T) { for _, item := range [][]*types.RingInfo{nil, {}, testdata.RingInfoArray} { assert.Equal(t, item, ToRingInfoArray(FromRingInfoArray(item))) } } func TestDomainTaskAttributes(t *testing.T) { for _, item := range []*types.DomainTaskAttributes{nil, &testdata.DomainTaskAttributes} { assert.Equal(t, item, ToDomainTaskAttributes(FromDomainTaskAttributes(item))) } } func TestFailoverMarkerAttributes(t *testing.T) { for _, item := range []*types.FailoverMarkerAttributes{nil, {}, &testdata.FailoverMarkerAttributes} { assert.Equal(t, item, ToFailoverMarkerAttributes(FromFailoverMarkerAttributes(item))) } } func TestFailoverMarkerToken(t *testing.T) { for _, item := range []*types.FailoverMarkerToken{nil, {}, &testdata.FailoverMarkerToken} { assert.Equal(t, item, ToFailoverMarkerToken(FromFailoverMarkerToken(item))) } } func TestHistoryTaskV2Attributes(t *testing.T) { for _, item := range []*types.HistoryTaskV2Attributes{nil, {}, &testdata.HistoryTaskV2Attributes} { assert.Equal(t, item, ToHistoryTaskV2Attributes(FromHistoryTaskV2Attributes(item))) } } func TestReplicationMessages(t *testing.T) { for _, item := range []*types.ReplicationMessages{nil, {}, &testdata.ReplicationMessages} { assert.Equal(t, item, ToReplicationMessages(FromReplicationMessages(item))) } } func TestReplicationTaskInfo(t *testing.T) { for _, item := range []*types.ReplicationTaskInfo{nil, {}, &testdata.ReplicationTaskInfo} { assert.Equal(t, item, ToReplicationTaskInfo(FromReplicationTaskInfo(item))) } } func TestReplicationToken(t *testing.T) { for _, item := range []*types.ReplicationToken{nil, {}, &testdata.ReplicationToken} { assert.Equal(t, item, ToReplicationToken(FromReplicationToken(item))) } } func TestSyncActivityTaskAttributes(t *testing.T) { for _, item := range []*types.SyncActivityTaskAttributes{nil, {}, &testdata.SyncActivityTaskAttributes} { assert.Equal(t, item, ToSyncActivityTaskAttributes(FromSyncActivityTaskAttributes(item))) } } func TestSyncShardStatus(t *testing.T) { for _, item := range []*types.SyncShardStatus{nil, {}, &testdata.SyncShardStatus} { assert.Equal(t, item, ToSyncShardStatus(FromSyncShardStatus(item))) } } func TestSyncShardStatusTaskAttributes(t *testing.T) { for _, item := range []*types.SyncShardStatusTaskAttributes{nil, {}, &testdata.SyncShardStatusTaskAttributes} { assert.Equal(t, item, ToSyncShardStatusTaskAttributes(FromSyncShardStatusTaskAttributes(item))) } } func TestReplicationTaskInfoArray(t *testing.T) { for _, item := range [][]*types.ReplicationTaskInfo{nil, {}, testdata.ReplicationTaskInfoArray} { assert.Equal(t, item, ToReplicationTaskInfoArray(FromReplicationTaskInfoArray(item))) } } func TestReplicationTaskArray(t *testing.T) { for _, item := range [][]*types.ReplicationTask{nil, {}, testdata.ReplicationTaskArray} { assert.Equal(t, item, ToReplicationTaskArray(FromReplicationTaskArray(item))) } } func TestReplicationTokenArray(t *testing.T) { for _, item := range [][]*types.ReplicationToken{nil, {}, testdata.ReplicationTokenArray} { assert.Equal(t, item, ToReplicationTokenArray(FromReplicationTokenArray(item))) } } func TestReplicationMessagesMap(t *testing.T) { for _, item := range []map[int32]*types.ReplicationMessages{nil, {}, testdata.ReplicationMessagesMap} { assert.Equal(t, item, ToReplicationMessagesMap(FromReplicationMessagesMap(item))) } } func TestReplicationTask(t *testing.T) { for _, item := range []*types.ReplicationTask{ nil, {}, &testdata.ReplicationTask_Domain, &testdata.ReplicationTask_Failover, &testdata.ReplicationTask_History, &testdata.ReplicationTask_SyncActivity, &testdata.ReplicationTask_SyncShard, } { assert.Equal(t, item, ToReplicationTask(FromReplicationTask(item))) } } func TestFailoverMarkerTokenArray(t *testing.T) { for _, item := range [][]*types.FailoverMarkerToken{nil, {}, testdata.FailoverMarkerTokenArray} { assert.Equal(t, item, ToFailoverMarkerTokenArray(FromFailoverMarkerTokenArray(item))) } } func TestVersionHistoryItemArray(t *testing.T) { for _, item := range [][]*types.VersionHistoryItem{nil, {}, testdata.VersionHistoryItemArray} { assert.Equal(t, item, ToVersionHistoryItemArray(FromVersionHistoryItemArray(item))) } } func TestEventIDVersionPair(t *testing.T) { assert.Nil(t, FromEventIDVersionPair(nil, nil)) assert.Nil(t, ToEventID(nil)) assert.Nil(t, ToEventVersion(nil)) pair := FromEventIDVersionPair(common.Int64Ptr(testdata.EventID1), common.Int64Ptr(testdata.Version1)) assert.Equal(t, testdata.EventID1, *ToEventID(pair)) assert.Equal(t, testdata.Version1, *ToEventVersion(pair)) } func TestCrossClusterTaskInfo(t *testing.T) { for _, item := range []*types.CrossClusterTaskInfo{nil, {}, &testdata.CrossClusterTaskInfo} { assert.Equal(t, item, ToCrossClusterTaskInfo(FromCrossClusterTaskInfo(item))) } } func TestCrossClusterTaskRequest(t *testing.T) { for _, item := range []*types.CrossClusterTaskRequest{ nil, {}, &testdata.CrossClusterTaskRequestStartChildExecution, &testdata.CrossClusterTaskRequestCancelExecution, &testdata.CrossClusterTaskRequestSignalExecution, } { assert.Equal(t, item, ToCrossClusterTaskRequest(FromCrossClusterTaskRequest(item))) } } func TestCrossClusterTaskResponse(t *testing.T) { for _, item := range []*types.CrossClusterTaskResponse{ nil, {}, &testdata.CrossClusterTaskResponseStartChildExecution, &testdata.CrossClusterTaskResponseCancelExecution, &testdata.CrossClusterTaskResponseSignalExecution, } { assert.Equal(t, item, ToCrossClusterTaskResponse(FromCrossClusterTaskResponse(item))) } } func TestCrossClusterTaskRequestArray(t *testing.T) { for _, item := range [][]*types.CrossClusterTaskRequest{nil, {}, testdata.CrossClusterTaskRequestArray} { assert.Equal(t, item, ToCrossClusterTaskRequestArray(FromCrossClusterTaskRequestArray(item))) } } func TestCrossClusterTaskResponseArray(t *testing.T) { for _, item := range [][]*types.CrossClusterTaskResponse{nil, {}, testdata.CrossClusterTaskResponseArray} { assert.Equal(t, item, ToCrossClusterTaskResponseArray(FromCrossClusterTaskResponseArray(item))) } } func TestCrossClusterTaskRequestMap(t *testing.T) { for _, item := range []map[int32][]*types.CrossClusterTaskRequest{nil, {}, testdata.CrossClusterTaskRequestMap} { assert.Equal(t, item, ToCrossClusterTaskRequestMap(FromCrossClusterTaskRequestMap(item))) } assert.Equal( t, map[int32][]*types.CrossClusterTaskRequest{ 0: {}, }, ToCrossClusterTaskRequestMap(FromCrossClusterTaskRequestMap( map[int32][]*types.CrossClusterTaskRequest{ 0: nil, }, )), ) } func TestGetTaskFailedCauseMap(t *testing.T) { for _, item := range []map[int32]types.GetTaskFailedCause{nil, {}, testdata.GetCrossClusterTaskFailedCauseMap} { assert.Equal(t, item, ToGetTaskFailedCauseMap(FromGetTaskFailedCauseMap(item))) } } func TestCrossClusterApplyParentClosePolicyRequestAttributes(t *testing.T) { item := testdata.CrossClusterApplyParentClosePolicyRequestAttributes assert.Equal( t, &item, ToCrossClusterApplyParentClosePolicyRequestAttributes( FromCrossClusterApplyParentClosePolicyRequestAttributes(&item), ), ) } func TestApplyParentClosePolicyAttributes(t *testing.T) { item := testdata.ApplyParentClosePolicyAttributes assert.Equal( t, &item, ToApplyParentClosePolicyAttributes( FromApplyParentClosePolicyAttributes(&item), ), ) } func TestApplyParentClosePolicyResult(t *testing.T) { item := testdata.ApplyParentClosePolicyResult assert.Equal( t, &item, ToApplyParentClosePolicyResult( FromApplyParentClosePolicyResult(&item), ), ) } func TestCrossClusterApplyParentClosePolicyResponse(t *testing.T) { item := testdata.CrossClusterApplyParentClosePolicyResponseWithChildren assert.Equal( t, &item, ToCrossClusterApplyParentClosePolicyResponseAttributes( FromCrossClusterApplyParentClosePolicyResponseAttributes(&item), ), ) } func TestAny(t *testing.T) { t.Run("sanity check", func(t *testing.T) { internal := types.Any{ ValueType: "testing", Value: []byte(`test`), } rpc := sharedv1.Any{ ValueType: "testing", Value: []byte(`test`), } require.Equal(t, &rpc, FromAny(&internal)) require.Equal(t, &internal, ToAny(&rpc)) }) t.Run("round trip nils", func(t *testing.T) { // somewhat annoying in fuzzing and there are few possibilities, so tested separately assert.Nil(t, FromAny(ToAny(nil)), "nil proto -> internal -> proto => should result in nil") assert.Nil(t, ToAny(FromAny(nil)), "nil internal -> proto -> internal => should result in nil") }) t.Run("round trip from internal", func(t *testing.T) { testutils.EnsureFuzzCoverage(t, []string{ "empty data", "filled data", }, func(t *testing.T, f *fuzz.Fuzzer) string { var orig types.Any f.Fuzz(&orig) out := ToAny(FromAny(&orig)) assert.Equal(t, &orig, out, "did not survive round-tripping") // report what branch of behavior was fuzzed occurred if len(orig.Value) == 0 { return "empty data" // ignoring nil vs empty difference } return "filled data" }) }) t.Run("round trip from proto", func(t *testing.T) { testutils.EnsureFuzzCoverage(t, []string{ "empty data", "filled data", }, func(t *testing.T, f *fuzz.Fuzzer) string { // unfortunately: // - gofuzz panics when it encounters interface fields (so this cannot be done on oneof fields) // - not directly relevant for Any, but causes issues for fuzzing other types // - it populates the XXX_ fields and these can be hard to clear // so this is fuzz-filled by hand with specific fields rather than as a whole. var orig sharedv1.Any f.Fuzz(&orig.ValueType) f.Fuzz(&orig.Value) out := FromAny(ToAny(&orig)) assert.Equal(t, &orig, out, "did not survive round-tripping") if len(orig.Value) == 0 { return "empty data" // ignoring nil vs empty difference } return "filled data" }) }) t.Run("can contain proto data", func(t *testing.T) { // pushing thrift or proto data through this type is very much expected, // so this is both evidence that it's possible and an example for how to do so. // // note that there is an equivalent test in mapper/thrift/shared_test.go. // they are structurally the same, but encoding/decoding details vary a bit. // some helpers because it's a bit verbose. // // note that these work for both types in this test: they can encode and decode *any* proto data, // you just have to make sure you pass the same type to both encode and decode via some other // source of info (i.e. the ValueType field). encode := func(thing proto.Marshaler) []byte { data, err := thing.Marshal() require.NoErrorf(t, err, "could not Marshal the target type: %T", thing) return data } decode := func(data []byte, target proto.Unmarshaler) { err := target.Unmarshal(data) require.NoErrorf(t, err, "could not Unmarshal to the target type: %T", target) } // --- create the original data, a proto object, and encode it by hand orig := &apiv1.WorkflowExecution{ WorkflowId: testdata.WorkflowID, RunId: testdata.RunID, } internalBytes := encode(orig) // --- put that data into the custom Any type // proto (unfortunately) maintains a type-registry, which means we can look up the unique name for this type... // // ...BUT this is potentially unsafe in normal code: proto type names can be changed without breaking binary compatibility. // we are unlikely to ever do so, but for this reason, hard-coding is actually a bit safer. var typeName = "uber.cadence.api.v1.WorkflowExecution" // current value of proto.MessageName(orig) anyVal := &types.Any{ ValueType: typeName, Value: internalBytes, // store proto bytes in the Any } // --- convert the whole container to proto (mimics making a call via yarpc) protoAny := FromAny(anyVal) // we map to the rpc type networkBytes := encode(protoAny) // yarpc does this // ^ this is what's sent over the network. // as a side note: // the final data is not double-encoded, so this "encode -> wrap -> encode" process is reasonably efficient. // // Thrift and Proto can efficiently move around binary blobs like this, as it's essentially just a memcpy between // the input and the output, and there's no `\0` to `\\0` escaping or base64 encoding or whatever needed. // // no behavior depends on this, it's just presented here as evidence that this Any-wrapper does not meaningfully // change any RPC design concerns: anything you would do with normal RPC can be done through an Any if you need // loose typing, the change-stability / performance / etc is entirely unaffected. // // compare via a sliding window to find the place it overlaps, to prove that this is true: found := false for i := 0; i <= len(networkBytes)-len(internalBytes); i++ { if reflect.DeepEqual(internalBytes, networkBytes[i:i+len(internalBytes)]) { found = true t.Logf("Found matching bytes at index %v", i) // currently at index 41 } } // *should* be true for efficiency's sake, but is not truly necessary for correct behavior assert.Truef(t, found, "did not find internal bytes within network bytes, might be paying double-encoding costs:\n\tinternal: %v\n\tnetwork: %v", internalBytes, networkBytes) // --- the network pushes the data to a new location --- // --- on the receiving side, we map to internal types like normal var outAny sharedv1.Any decode(networkBytes, &outAny) // yarpc does this outAnyVal := ToAny(&outAny) // we map to internal types // --- and finally decode the any-typed data by hand require.Equal(t, typeName, outAnyVal.ValueType, "type name through RPC should match the original type name") var outOrig apiv1.WorkflowExecution // selected based on the ValueType contents decode(outAnyVal.Value, &outOrig) // do the actual custom decoding assert.NotEmpty(t, outOrig, "sanity check, decoded value should not be empty") assert.Equal(t, orig, &outOrig, "final round-tripped Any-contained data should be identical to the original object") }) } func TestFromCrossClusterTaskResponseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterTaskResponse, ToCrossClusterTaskResponse, testutils.WithCustomFuncs( CrossClusterTaskResponseFuzzer, testutils.CrossClusterTaskTypeFuzzer, testutils.CrossClusterTaskFailedCauseFuzzer, ), ) } // CrossClusterTaskResponseFuzzer ensures only one attribute field is set based on TaskType (oneof constraint) func CrossClusterTaskResponseFuzzer(r *types.CrossClusterTaskResponse, c fuzz.Continue) { c.FuzzNoCustom(r) // Ensure TaskType is set to a valid value (0-4) using the enum fuzzer if r.TaskType == nil { var taskType types.CrossClusterTaskType testutils.CrossClusterTaskTypeFuzzer(&taskType, c) r.TaskType = &taskType } else { testutils.CrossClusterTaskTypeFuzzer(r.TaskType, c) } // Ensure FailedCause is valid if set (0-5) using the enum fuzzer if r.FailedCause != nil { testutils.CrossClusterTaskFailedCauseFuzzer(r.FailedCause, c) } // Based on TaskType, clear all attributes except the matching one switch *r.TaskType { case 0: // StartChildExecution r.CancelExecutionAttributes = nil r.SignalExecutionAttributes = nil r.RecordChildWorkflowExecutionCompleteAttributes = nil r.ApplyParentClosePolicyAttributes = nil case 1: // CancelExecution r.StartChildExecutionAttributes = nil r.SignalExecutionAttributes = nil r.RecordChildWorkflowExecutionCompleteAttributes = nil r.ApplyParentClosePolicyAttributes = nil case 2: // SignalExecution r.StartChildExecutionAttributes = nil r.CancelExecutionAttributes = nil r.RecordChildWorkflowExecutionCompleteAttributes = nil r.ApplyParentClosePolicyAttributes = nil case 3: // RecordChildWorkflowExecutionComplete r.StartChildExecutionAttributes = nil r.CancelExecutionAttributes = nil r.SignalExecutionAttributes = nil r.ApplyParentClosePolicyAttributes = nil case 4: // ApplyParentClosePolicy r.StartChildExecutionAttributes = nil r.CancelExecutionAttributes = nil r.SignalExecutionAttributes = nil r.RecordChildWorkflowExecutionCompleteAttributes = nil } } func TestHostInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromHostInfo, ToHostInfo) } func TestMembershipInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromMembershipInfo, ToMembershipInfo) } func TestDomainCacheInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromDomainCacheInfo, ToDomainCacheInfo) } func TestRingInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromRingInfo, ToRingInfo) } func TestVersionHistoriesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromVersionHistories, ToVersionHistories) } func TestVersionHistoryFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromVersionHistory, ToVersionHistory) } func TestVersionHistoryItemFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromVersionHistoryItem, ToVersionHistoryItem) } func TestPersistenceSettingFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPersistenceSetting, ToPersistenceSetting) } func TestPersistenceFeatureFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPersistenceFeature, ToPersistenceFeature) } func TestPersistenceInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromPersistenceInfo, ToPersistenceInfo) } func TestCrossClusterTaskTypeFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterTaskType, ToCrossClusterTaskType, testutils.WithCustomFuncs(testutils.CrossClusterTaskTypeFuzzer), ) } func TestGetTaskFailedCauseFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromGetTaskFailedCause, ToGetTaskFailedCause, testutils.WithCustomFuncs(testutils.GetTaskFailedCauseFuzzer), ) } func TestApplyParentClosePolicyStatusFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromApplyParentClosePolicyStatus, ToApplyParentClosePolicyStatus, testutils.WithCustomFuncs(testutils.CrossClusterTaskFailedCauseFuzzer), ) } func TestDomainTaskAttributesFuzz(t *testing.T) { // Info, Config, and ReplicationConfig are built into the 'Domain' field. // This mapping is tested in TestDescribeDomainResponseDomainFuzz testutils.RunMapperFuzzTest(t, FromDomainTaskAttributes, ToDomainTaskAttributes, testutils.WithCustomFuncs(testutils.DomainOperationFuzzer, testutils.DomainStatusFuzzer), testutils.WithExcludedFields("Info", "Config", "ReplicationConfig"), ) } func TestFailoverMarkerAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromFailoverMarkerAttributes, ToFailoverMarkerAttributes) } func TestFailoverMarkerTokenFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromFailoverMarkerToken, ToFailoverMarkerToken) } func TestHistoryTaskV2AttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromHistoryTaskV2Attributes, ToHistoryTaskV2Attributes, testutils.WithCustomFuncs(testutils.EncodingTypeFuzzer), ) } func TestSyncActivityTaskAttributesFuzz(t *testing.T) { // [BUG] FailureDetails is not round-trip compatible testutils.RunMapperFuzzTest(t, FromSyncActivityTaskAttributes, ToSyncActivityTaskAttributes, testutils.WithCommonEnumFuzzers(), testutils.WithExcludedFields("LastFailureDetails"), ) } func TestSyncShardStatusFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromSyncShardStatus, ToSyncShardStatus) } func TestSyncShardStatusTaskAttributesFuzz(t *testing.T) { // [BUG] ShardID gets truncated (int64 -> int32 issue) testutils.RunMapperFuzzTest(t, FromSyncShardStatusTaskAttributes, ToSyncShardStatusTaskAttributes, testutils.WithExcludedFields("ShardID"), ) } func TestReplicationMessagesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromReplicationMessages, ToReplicationMessages, testutils.WithCustomFuncs(testutils.ReplicationTaskTypeFuzzer, testutils.DomainOperationFuzzer), testutils.WithExcludedFields("ReplicationTasks"), // Tested separately in TestReplicationTaskFuzz ) } func TestReplicationTaskInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromReplicationTaskInfo, ToReplicationTaskInfo) } func TestReplicationTokenFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromReplicationToken, ToReplicationToken) } func TestCrossClusterTaskInfoFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterTaskInfo, ToCrossClusterTaskInfo, testutils.WithCustomFuncs( testutils.CrossClusterTaskTypeFuzzer, testutils.CrossClusterTaskFailedCauseFuzzer, ), ) } func TestCrossClusterStartChildExecutionRequestAttributesFuzz(t *testing.T) { // [BUG] TargetRunID is not round-trip compatible, when it is set to an empty string, it becomes nil testutils.RunMapperFuzzTest(t, FromCrossClusterStartChildExecutionRequestAttributes, ToCrossClusterStartChildExecutionRequestAttributes, testutils.WithCommonEnumFuzzers(), testutils.WithExcludedFields("InitiatedEventAttributes", "TargetRunID"), // InitiatedEventAttributes is tested in FromStartChildWorkflowExecutionInitiatedEventAttributesFuzz ) } func TestCrossClusterStartChildExecutionResponseAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterStartChildExecutionResponseAttributes, ToCrossClusterStartChildExecutionResponseAttributes) } func TestCrossClusterCancelExecutionRequestAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterCancelExecutionRequestAttributes, ToCrossClusterCancelExecutionRequestAttributes) } func TestCrossClusterCancelExecutionResponseAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterCancelExecutionResponseAttributes, ToCrossClusterCancelExecutionResponseAttributes) } func TestCrossClusterSignalExecutionRequestAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterSignalExecutionRequestAttributes, ToCrossClusterSignalExecutionRequestAttributes) } func TestCrossClusterSignalExecutionResponseAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterSignalExecutionResponseAttributes, ToCrossClusterSignalExecutionResponseAttributes) } func TestCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes, ToCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes, testutils.WithCommonEnumFuzzers(), testutils.WithExcludedFields("CompletionEvent"), // CompletionEvent uses FromHistoryEvent which is tested in TestHistoryEventFuzz ) } func TestCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes, ToCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes) } func TestCrossClusterApplyParentClosePolicyResponseAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromCrossClusterApplyParentClosePolicyResponseAttributes, ToCrossClusterApplyParentClosePolicyResponseAttributes, testutils.WithCustomFuncs(testutils.CrossClusterTaskFailedCauseFuzzer), ) } func TestApplyParentClosePolicyAttributesFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromApplyParentClosePolicyAttributes, ToApplyParentClosePolicyAttributes) } func TestApplyParentClosePolicyResultFuzz(t *testing.T) { testutils.RunMapperFuzzTest(t, FromApplyParentClosePolicyResult, ToApplyParentClosePolicyResult, testutils.WithCustomFuncs(testutils.CrossClusterTaskFailedCauseFuzzer), ) } func TestCrossClusterTaskRequestFuzz(t *testing.T) { // This type has a oneof constraint, which doesn't lend itself well to fuzzing // All child attributes are tested separately, so we exclude them here and test only TaskInfo testutils.RunMapperFuzzTest(t, FromCrossClusterTaskRequest, ToCrossClusterTaskRequest, testutils.WithCustomFuncs( testutils.CrossClusterTaskTypeFuzzer, ), testutils.WithExcludedFields( "StartChildExecutionAttributes", // Tested in TestCrossClusterStartChildExecutionRequestAttributesFuzz "CancelExecutionAttributes", // Tested in TestCrossClusterCancelExecutionRequestAttributesFuzz "SignalExecutionAttributes", // Tested in TestCrossClusterSignalExecutionRequestAttributesFuzz "RecordChildWorkflowExecutionCompleteAttributes", // Tested in TestCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributesFuzz "ApplyParentClosePolicyAttributes", // Tested in TestCrossClusterApplyParentClosePolicyRequestAttributesFuzz ), ) } func TestReplicationTaskFuzz(t *testing.T) { // This type has a oneof constraint, which doesn't lend itself well to fuzzing // All child attributes are tested separately, so we exclude them here and test only TaskInfo testutils.RunMapperFuzzTest(t, FromReplicationTask, ToReplicationTask, testutils.WithCustomFuncs( testutils.ReplicationTaskTypeFuzzer, ), testutils.WithExcludedFields( "DomainTaskAttributes", // Tested in TestDomainTaskAttributesFuzz "SyncShardStatusTaskAttributes", // Tested in TestSyncShardStatusTaskAttributesFuzz "SyncActivityTaskAttributes", // Tested in TestSyncActivityTaskAttributesFuzz "HistoryTaskV2Attributes", // Tested in TestHistoryTaskV2AttributesFuzz "FailoverMarkerAttributes", // Tested in TestFailoverMarkerAttributesFuzz ), ) } ================================================ FILE: common/types/mapper/testutils/common_fuzzers.go ================================================ package testutils import ( fuzz "github.com/google/gofuzz" "github.com/uber/cadence/common/types" ) // WithCommonEnumFuzzers adds fuzzers for common Cadence enum types // to ensure only valid enum values are generated func WithCommonEnumFuzzers() FuzzOption { return WithCustomFuncs( func(e *types.WorkflowExecutionCloseStatus, c fuzz.Continue) { *e = types.WorkflowExecutionCloseStatus(c.Intn(6)) // 0-5 }, func(e *types.TaskListKind, c fuzz.Continue) { *e = types.TaskListKind(c.Intn(3)) // 0-2: Normal, Sticky, Ephemeral }, func(e *types.TaskListType, c fuzz.Continue) { *e = types.TaskListType(c.Intn(2)) // 0-1: Decision, Activity }, func(e *types.TimeoutType, c fuzz.Continue) { *e = types.TimeoutType(c.Intn(4)) // 0-3: StartToClose, ScheduleToStart, ScheduleToClose, Heartbeat }, func(e *types.ParentClosePolicy, c fuzz.Continue) { *e = types.ParentClosePolicy(c.Intn(3)) // 0-2 }, func(e *types.PendingActivityState, c fuzz.Continue) { *e = types.PendingActivityState(c.Intn(3)) // 0-2 }, func(e *types.PendingDecisionState, c fuzz.Continue) { *e = types.PendingDecisionState(c.Intn(2)) // 0-1 }, func(e *types.QueryTaskCompletedType, c fuzz.Continue) { *e = types.QueryTaskCompletedType(c.Intn(3)) // 0-2 }, func(e *types.QueryResultType, c fuzz.Continue) { *e = types.QueryResultType(c.Intn(2)) // 0-1: Answered, Failed }, func(e *types.IndexedValueType, c fuzz.Continue) { *e = types.IndexedValueType(c.Intn(6)) // 0-5: String, Keyword, Int, Double, Bool, Datetime }, func(e *types.CronOverlapPolicy, c fuzz.Continue) { *e = types.CronOverlapPolicy(c.Intn(2)) // 0-1: Skipped, BufferOne }, func(e *types.WorkflowExecutionStatus, c fuzz.Continue) { *e = types.WorkflowExecutionStatus(c.Intn(8)) // 0-7: Pending, Started, Completed, Failed, Canceled, Terminated, ContinuedAsNew, TimedOut }, func(e *types.ActiveClusterSelectionStrategy, c fuzz.Continue) { *e = types.ActiveClusterSelectionStrategy(c.Intn(2)) // 0-1: RegionSticky, ExternalEntity }, ) } func EncodingTypeFuzzer(e *types.EncodingType, c fuzz.Continue) { *e = types.EncodingType(c.Intn(2)) // 0-1: ThriftRW, JSON } func CrossClusterTaskFailedCauseFuzzer(e *types.CrossClusterTaskFailedCause, c fuzz.Continue) { *e = types.CrossClusterTaskFailedCause(c.Intn(6)) // 0-5: DomainNotActive, DomainNotExists, WorkflowAlreadyRunning, WorkflowNotExists, WorkflowAlreadyCompleted, Uncategorized } func CrossClusterTaskTypeFuzzer(e *types.CrossClusterTaskType, c fuzz.Continue) { *e = types.CrossClusterTaskType(c.Intn(5)) // 0-4: StartChildExecution, CancelExecution, SignalExecution, RecordChildWorkflowExecutionComplete, ApplyParentClosePolicy } func DLQTypeFuzzer(e *types.DLQType, c fuzz.Continue) { *e = types.DLQType(c.Intn(2)) // 0-1: Replication, Domain } func DomainOperationFuzzer(e *types.DomainOperation, c fuzz.Continue) { *e = types.DomainOperation(c.Intn(3)) // 0-2: Create, Update, Delete } func ReplicationTaskTypeFuzzer(e *types.ReplicationTaskType, c fuzz.Continue) { *e = types.ReplicationTaskType(c.Intn(7)) // 0-6: Domain, History, SyncShardStatus, SyncActivity, HistoryMetadata, HistoryV2, FailoverMarker } func WorkflowStateFuzzer(e *int32, c fuzz.Continue) { // WorkflowState values: 0-2 (Created, Running, Completed) // Note: This operates on *int32 because the types package uses *int32 for WorkflowState if e != nil { *e = int32(c.Intn(3)) } } func GetTaskFailedCauseFuzzer(e *types.GetTaskFailedCause, c fuzz.Continue) { *e = types.GetTaskFailedCause(c.Intn(4)) // 0-3: ServiceBusy, Timeout, ShardOwnershipLost, Uncategorized } func IsolationGroupStateFuzzer(e *types.IsolationGroupState, c fuzz.Continue) { *e = types.IsolationGroupState(c.Intn(3)) // 0-2: Invalid, Healthy, Drained } func TaskTypeFuzzer(e *int32, c fuzz.Continue) { // TaskType internal constant values (from common/constants/constants.go): // 2: TaskTypeTransfer // 3: TaskTypeTimer // 4: TaskTypeReplication // 6: TaskTypeCrossCluster (deprecated but still valid) // Note: Value 0 maps to nil/INVALID, so we always generate a valid value if e != nil { // Generate one of the valid constant values: 2, 3, 4, or 6 validValues := []int32{2, 3, 4, 6} *e = validValues[c.Intn(len(validValues))] } } func DomainStatusFuzzer(e *types.DomainStatus, c fuzz.Continue) { *e = types.DomainStatus(c.Intn(3)) // 0-2: Registered, Deprecated, Deleted } ================================================ FILE: common/types/mapper/testutils/fuzz_mapper.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testutils import ( "reflect" "strings" "testing" "time" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" ) const ( // DefaultNilChance is the default probability of generating nil values DefaultNilChance = 0.3 // DefaultIterations is the default number of fuzzing iterations DefaultIterations = 100 // MaxSafeTimestampSeconds is the maximum safe Unix timestamp (year 2100) MaxSafeTimestampSeconds = 4102444800 // NanosecondsPerSecond is the number of nanoseconds in a second NanosecondsPerSecond = 1000000000 // MaxDurationSeconds is the maximum duration in seconds (24 hours) MaxDurationSeconds = 86400 ) // FuzzOptions configures the behavior of mapper fuzz tests type FuzzOptions struct { // CustomFuncs are custom fuzzer functions to apply for specific types CustomFuncs []interface{} // ExcludedFields are field names to exclude from comparison (set to zero value) ExcludedFields []string // NilChance is the probability of setting pointer/slice fields to nil (default DefaultNilChance) NilChance float64 // Iterations is the number of fuzzing iterations to run (default DefaultIterations) Iterations int } // FuzzOption is a functional option for configuring FuzzOptions type FuzzOption func(*FuzzOptions) // WithCustomFuncs adds custom fuzzer functions for specific types func WithCustomFuncs(funcs ...interface{}) FuzzOption { return func(opts *FuzzOptions) { opts.CustomFuncs = append(opts.CustomFuncs, funcs...) } } // WithExcludedFields specifies field names to exclude from comparison func WithExcludedFields(fields ...string) FuzzOption { return func(opts *FuzzOptions) { opts.ExcludedFields = append(opts.ExcludedFields, fields...) } } // WithNilChance sets the probability of generating nil values func WithNilChance(chance float64) FuzzOption { return func(opts *FuzzOptions) { opts.NilChance = chance } } // WithIterations sets the number of fuzzing iterations func WithIterations(iterations int) FuzzOption { return func(opts *FuzzOptions) { opts.Iterations = iterations } } // WithTimeFuzzers adds custom fuzzers for time.Time and time.Duration // to ensure safe timestamp bounds and proper UTC normalization func WithTimeFuzzers() FuzzOption { return WithCustomFuncs( // time.Time with safe bounds (Unix epoch to year 2100) and UTC normalization func(t *time.Time, c fuzz.Continue) { if c.Intn(10) < 3 { *t = time.Time{} // Zero time return } *t = time.Unix(c.Int63n(MaxSafeTimestampSeconds), c.Int63n(NanosecondsPerSecond)).UTC() }, // time.Duration with safe bounds (up to 24 hours) func(d *time.Duration, c fuzz.Continue) { if c.Intn(10) < 3 { *d = 0 // Zero duration return } *d = time.Duration(c.Int63n(MaxDurationSeconds * NanosecondsPerSecond)) }, ) } // WithDefaultFuzzers applies the standard set of fuzzers for time and common enums. // This is applied automatically by RunMapperFuzzTest unless overridden. func WithDefaultFuzzers() FuzzOption { return func(opts *FuzzOptions) { WithTimeFuzzers()(opts) WithCommonEnumFuzzers()(opts) } } // RunMapperFuzzTest runs a fuzz test for a mapper pair (From/To functions). // It generates random values, converts them through the mapper pair, and verifies // the round-trip preserves the original value. // // Example usage: // // func TestActivityTypeFuzz(t *testing.T) { // RunMapperFuzzTest(t, FromActivityType, ToActivityType) // } func RunMapperFuzzTest[TInternal any, TExternal any]( t *testing.T, fromFunc func(TInternal) TExternal, toFunc func(TExternal) TInternal, options ...FuzzOption, ) { t.Helper() // Apply default options opts := FuzzOptions{ NilChance: DefaultNilChance, Iterations: DefaultIterations, } // Apply default fuzzers first (time and common enums) WithDefaultFuzzers()(&opts) // Then apply user-provided options (can override defaults) for _, opt := range options { opt(&opts) } // Create fuzzer with seed for reproducibility seed := time.Now().UnixNano() fuzzer := fuzz.NewWithSeed(seed). Funcs(opts.CustomFuncs...). NilChance(opts.NilChance) // Log seed on failure for debugging defer func() { if t.Failed() { t.Logf("fuzz seed: %v", seed) } }() // Run fuzzing iterations for i := 0; i < opts.Iterations; i++ { var orig TInternal fuzzer.Fuzz(&orig) // Clear excluded fields before conversion clearExcludedFields(&orig, opts.ExcludedFields) // Test round trip: Internal -> External -> Internal external := fromFunc(orig) result := toFunc(external) // Clear excluded fields in result for comparison clearExcludedFields(&result, opts.ExcludedFields) assert.Equal(t, orig, result, "Round trip failed at iteration %d", i) } } // clearFieldsIf recursively traverses an object and clears fields that match the predicate function. // This is used to exclude certain fields from round-trip comparison. func clearFieldsIf(obj interface{}, shouldClear func(fieldName string) bool) { if obj == nil { return } v := reflect.ValueOf(obj) // Dereference through multiple pointer levels to handle cases like **types.ScheduleSpec // This happens when TInternal is a pointer type and we pass &orig to clearExcludedFields for v.Kind() == reflect.Ptr { if v.IsNil() { return } v = v.Elem() } // Handle slices at top level (e.g., when TInternal is []*SomeType) if v.Kind() == reflect.Slice { for j := 0; j < v.Len(); j++ { elem := v.Index(j) if elem.CanInterface() { if elem.Kind() == reflect.Struct && elem.CanAddr() { clearFieldsIf(elem.Addr().Interface(), shouldClear) } else { clearFieldsIf(elem.Interface(), shouldClear) } } } return } if v.Kind() != reflect.Struct { return } for i := 0; i < v.NumField(); i++ { field := v.Field(i) fieldName := v.Type().Field(i).Name if shouldClear(fieldName) && field.CanSet() { field.Set(reflect.Zero(field.Type())) } // Recursively clear fields in nested structs and slices if field.CanInterface() { switch field.Kind() { case reflect.Ptr: if !field.IsNil() { clearFieldsIf(field.Interface(), shouldClear) } case reflect.Struct: if field.CanAddr() { clearFieldsIf(field.Addr().Interface(), shouldClear) } case reflect.Slice: for j := 0; j < field.Len(); j++ { elem := field.Index(j) if elem.CanInterface() { // For struct elements, pass the address to ensure modifications work // For pointer elements, pass the interface directly if elem.Kind() == reflect.Struct && elem.CanAddr() { clearFieldsIf(elem.Addr().Interface(), shouldClear) } else { clearFieldsIf(elem.Interface(), shouldClear) } } } case reflect.Map: // Map values cannot be modified through reflection in Go. // Calling clearFieldsIf on map values would operate on copies that are discarded. // To properly handle maps with struct values, the map would need to be rebuilt // with cleared values, which is beyond the scope of this utility. // Since Cadence types primarily use slices and pointers rather than maps of structs, // this limitation has minimal practical impact. } } } } // clearExcludedFields clears both protobuf internal fields and user-specified excluded fields. // Protobuf internal fields (XXX_*, sizeCache, unknownFields) are always cleared to avoid comparison issues. // state is not excluded as it is generic enough it may be used by a mapper in future func clearExcludedFields(obj interface{}, excludedFields []string) { // Create a map for O(1) lookup of excluded fields excludedMap := make(map[string]bool) for _, field := range excludedFields { excludedMap[field] = true } clearFieldsIf(obj, func(fieldName string) bool { // Clear if it's a protobuf internal field OR if it's in the excluded list return strings.HasPrefix(fieldName, "XXX_") || fieldName == "sizeCache" || fieldName == "unknownFields" || excludedMap[fieldName] }) } ================================================ FILE: common/types/mapper/testutils/fuzz_mapper_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testutils import ( "testing" "github.com/stretchr/testify/assert" ) // Test types for clearFieldsIf // Note: Real protobuf fields like sizeCache and unknownFields are typically unexported, // but we use exported versions here to test the clearing logic. The function will check // field names and clear them if they match the pattern, regardless of export status. type testStructWithExcludedFields struct { KeepMe string XXX_ClearMe string // revive:disable-line:var-naming Protobuf internal field pattern (XXX_ prefix) AlsoClearMe string // User-specified excluded field } type testStructWithSlices struct { ValueStructs []testStructWithExcludedFields // Slice of value-type structs PointerSlice []*testStructWithExcludedFields // Slice of pointers XXX_Internal string // revive:disable-line:var-naming Protobuf internal field pattern (XXX_ prefix) } type testStructWithNestedStruct struct { Nested testStructWithExcludedFields // Nested struct XXX_Internal string // revive:disable-line:var-naming Protobuf internal field pattern (XXX_ prefix) } func TestClearFieldsIf_ValueStructSlice(t *testing.T) { // This test verifies the fix for slice struct elements (issue #1 from code review) // Previously, clearFieldsIf would operate on copies for slice struct elements, // causing fields to not actually be cleared. obj := &testStructWithSlices{ ValueStructs: []testStructWithExcludedFields{ {KeepMe: "keep1", XXX_ClearMe: "clear1", AlsoClearMe: "clear1"}, {KeepMe: "keep2", XXX_ClearMe: "clear2", AlsoClearMe: "clear2"}, }, PointerSlice: []*testStructWithExcludedFields{ {KeepMe: "keep3", XXX_ClearMe: "clear3", AlsoClearMe: "clear3"}, }, XXX_Internal: "internal", } clearExcludedFields(obj, []string{"AlsoClearMe"}) // Top-level fields should be cleared assert.Equal(t, "", obj.XXX_Internal, "XXX_ field should be cleared") // Value struct slice elements should have their excluded fields cleared // This is the key test - before the fix, these would NOT be cleared assert.Equal(t, "keep1", obj.ValueStructs[0].KeepMe, "KeepMe should not be cleared") assert.Equal(t, "", obj.ValueStructs[0].XXX_ClearMe, "XXX_ClearMe should be cleared in value struct slice") assert.Equal(t, "", obj.ValueStructs[0].AlsoClearMe, "AlsoClearMe should be cleared in value struct slice") assert.Equal(t, "keep2", obj.ValueStructs[1].KeepMe, "KeepMe should not be cleared") assert.Equal(t, "", obj.ValueStructs[1].XXX_ClearMe, "XXX_ClearMe should be cleared in value struct slice") assert.Equal(t, "", obj.ValueStructs[1].AlsoClearMe, "AlsoClearMe should be cleared in value struct slice") // Pointer slice elements should also have their excluded fields cleared assert.Equal(t, "keep3", obj.PointerSlice[0].KeepMe, "KeepMe should not be cleared") assert.Equal(t, "", obj.PointerSlice[0].XXX_ClearMe, "XXX_ClearMe should be cleared in pointer slice") assert.Equal(t, "", obj.PointerSlice[0].AlsoClearMe, "AlsoClearMe should be cleared in pointer slice") } func TestClearFieldsIf_NestedStruct(t *testing.T) { obj := &testStructWithNestedStruct{ Nested: testStructWithExcludedFields{ KeepMe: "keep", XXX_ClearMe: "clear", AlsoClearMe: "clear", }, XXX_Internal: "internal", } clearExcludedFields(obj, []string{"AlsoClearMe"}) // Top-level excluded field should be cleared assert.Equal(t, "", obj.XXX_Internal, "XXX_Internal should be cleared") // Nested struct fields should be cleared assert.Equal(t, "keep", obj.Nested.KeepMe, "KeepMe should not be cleared") assert.Equal(t, "", obj.Nested.XXX_ClearMe, "XXX_ClearMe should be cleared in nested struct") assert.Equal(t, "", obj.Nested.AlsoClearMe, "AlsoClearMe should be cleared in nested struct") } func TestClearFieldsIf_DoesNotClearLegitimateStateField(t *testing.T) { // This test verifies issue #2 from code review: we don't clear legitimate fields named "state" // The previous implementation cleared ANY field named "state", which was too broad. type legitimateStruct struct { State string // Legitimate business logic field XXX_Internal string // revive:disable-line:var-naming Protobuf internal field pattern (XXX_ prefix) } obj := &legitimateStruct{ State: "active", XXX_Internal: "internal", } clearExcludedFields(obj, nil) // Legitimate "State" field should NOT be cleared (issue #2 fix) assert.Equal(t, "active", obj.State, "Legitimate State field should not be cleared") // But XXX_ field should still be cleared assert.Equal(t, "", obj.XXX_Internal, "XXX_ field should be cleared") } func TestClearFieldsIf_ProtobufFields(t *testing.T) { // Test that common protobuf internal field patterns are cleared // Note: Real protobuf internal fields are often unexported and thus cannot be // cleared via reflection, but we test the logic with exported fields. type protobufLikeStruct struct { BusinessField string XXX_NoUnkeyedLit struct{} // revive:disable-line:var-naming XXX_ prefixed fields should be cleared XXX_unrecognized []byte // revive:disable-line:var-naming XXX_ prefixed fields should be cleared } obj := &protobufLikeStruct{ BusinessField: "keep", XXX_NoUnkeyedLit: struct{}{}, XXX_unrecognized: []byte("data"), } clearExcludedFields(obj, nil) // Business field should be kept assert.Equal(t, "keep", obj.BusinessField, "Business field should not be cleared") // All XXX_ prefixed fields should be cleared assert.Equal(t, struct{}{}, obj.XXX_NoUnkeyedLit, "XXX_NoUnkeyedLit should be cleared") assert.Nil(t, obj.XXX_unrecognized, "XXX_unrecognized should be cleared") } func TestClearFieldsIf_DoublePointer(t *testing.T) { // This test verifies the fix for the double-pointer bug (code review issue #3) // When TInternal is a pointer type (like *types.ScheduleSpec), RunMapperFuzzTest // calls clearExcludedFields(&orig, ...), passing a **types.ScheduleSpec. // The function must dereference through multiple pointer levels. type innerStruct struct { KeepMe string XXX_ClearMe string // revive:disable-line:var-naming Protobuf field AlsoClearMe string } // Simulate the actual usage pattern in RunMapperFuzzTest orig := &innerStruct{ KeepMe: "keep", XXX_ClearMe: "clear", AlsoClearMe: "clear", } // This is what RunMapperFuzzTest does: passes &orig where orig is already a pointer // So we're passing **innerStruct to clearExcludedFields clearExcludedFields(&orig, []string{"AlsoClearMe"}) // Fields should be cleared even through the double pointer assert.Equal(t, "keep", orig.KeepMe, "KeepMe should not be cleared") assert.Equal(t, "", orig.XXX_ClearMe, "XXX_ClearMe should be cleared through double pointer") assert.Equal(t, "", orig.AlsoClearMe, "AlsoClearMe should be cleared through double pointer") } func TestClearFieldsIf_TriplePointer(t *testing.T) { // Edge case: ensure we handle arbitrary pointer depth type innerStruct struct { XXX_ClearMe string // revive:disable-line:var-naming Protobuf field } level1 := &innerStruct{XXX_ClearMe: "clear"} level2 := &level1 level3 := &level2 clearExcludedFields(level3, nil) assert.Equal(t, "", level1.XXX_ClearMe, "Should clear through triple pointer") } func TestClearFieldsIf_WithExcludedFields(t *testing.T) { // This test verifies that WithExcludedFields works correctly with pointer types // Previously, this would silently do nothing due to the double-pointer bug type testStruct struct { KeepMe string ExcludeMe string AlsoExcludeMe string } orig := &testStruct{ KeepMe: "keep", ExcludeMe: "exclude1", AlsoExcludeMe: "exclude2", } // Simulate RunMapperFuzzTest usage pattern clearExcludedFields(&orig, []string{"ExcludeMe", "AlsoExcludeMe"}) assert.Equal(t, "keep", orig.KeepMe, "KeepMe should not be cleared") assert.Equal(t, "", orig.ExcludeMe, "ExcludeMe should be cleared via WithExcludedFields") assert.Equal(t, "", orig.AlsoExcludeMe, "AlsoExcludeMe should be cleared via WithExcludedFields") } ================================================ FILE: common/types/mapper/testutils/fuzz_test_utils.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testutils import ( "testing" "time" fuzz "github.com/google/gofuzz" ) func EnsureFuzzCoverage(t *testing.T, expected []string, cb func(t *testing.T, f *fuzz.Fuzzer) string) { t.Helper() var details []string results := make(map[string]bool, len(expected)) for _, e := range expected { results[e] = false } seed := time.Now().UnixNano() f := fuzz.NewWithSeed(seed) // helps with troubleshooting defer func() { if t.Failed() { // else a bit noisy t.Logf("expected to see: %#v", expected) t.Logf("observed results: %#v", results) t.Logf("detailed results: %#v", details) t.Logf("fuzz seed: %v", seed) } }() for tries := 0; tries < 100; tries++ { // retry a few times if needed for i := 0; i < 100; i++ { // always fuzz a moderate amount, don't stop immediately res := cb(t, f) details = append(details, res) if res == "" { t.Errorf("invalid empty response from fuzzing callback on iteration %v", (tries*100)+i) } if _, ok := results[res]; !ok { t.Errorf("unrecognized response from fuzzing callback on iteration %v: %v", (tries*100)+i, res) } if t.Failed() { return // already failed either internally or in the callback, either way stop trying } results[res] = true } stop := true for _, v := range results { stop = stop && v } if stop { return // covered all expected values, stop retrying } } missing := make([]string, 0, len(results)) for _, v := range expected { if !results[v] { missing = append(missing, v) } } t.Errorf("fuzzy coverage func did not check enough cases after 10k attempts, missing: %#v", missing) } ================================================ FILE: common/types/mapper/thrift/admin.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "sort" "github.com/uber/cadence/.gen/go/admin" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/types" ) var ( FromAdminRefreshWorkflowTasksRequest = FromRefreshWorkflowTasksRequest ToAdminRefreshWorkflowTasksRequest = ToRefreshWorkflowTasksRequest ) // FromAdminAddSearchAttributeRequest converts internal AddSearchAttributeRequest type to thrift func FromAdminAddSearchAttributeRequest(t *types.AddSearchAttributeRequest) *admin.AddSearchAttributeRequest { if t == nil { return nil } return &admin.AddSearchAttributeRequest{ SearchAttribute: FromIndexedValueTypeMap(t.SearchAttribute), SecurityToken: &t.SecurityToken, } } // ToAdminAddSearchAttributeRequest converts thrift AddSearchAttributeRequest type to internal func ToAdminAddSearchAttributeRequest(t *admin.AddSearchAttributeRequest) *types.AddSearchAttributeRequest { if t == nil { return nil } return &types.AddSearchAttributeRequest{ SearchAttribute: ToIndexedValueTypeMap(t.SearchAttribute), SecurityToken: t.GetSecurityToken(), } } // FromAdminDescribeClusterResponse converts internal DescribeClusterResponse type to thrift func FromAdminDescribeClusterResponse(t *types.DescribeClusterResponse) *admin.DescribeClusterResponse { if t == nil { return nil } return &admin.DescribeClusterResponse{ SupportedClientVersions: FromSupportedClientVersions(t.SupportedClientVersions), MembershipInfo: FromMembershipInfo(t.MembershipInfo), PersistenceInfo: FromPersistenceInfoMap(t.PersistenceInfo), } } // ToAdminDescribeClusterResponse converts thrift DescribeClusterResponse type to internal func ToAdminDescribeClusterResponse(t *admin.DescribeClusterResponse) *types.DescribeClusterResponse { if t == nil { return nil } return &types.DescribeClusterResponse{ SupportedClientVersions: ToSupportedClientVersions(t.SupportedClientVersions), MembershipInfo: ToMembershipInfo(t.MembershipInfo), PersistenceInfo: ToPersistenceInfoMap(t.PersistenceInfo), } } // FromAdminDescribeWorkflowExecutionRequest converts internal DescribeWorkflowExecutionRequest type to thrift func FromAdminDescribeWorkflowExecutionRequest(t *types.AdminDescribeWorkflowExecutionRequest) *admin.DescribeWorkflowExecutionRequest { if t == nil { return nil } return &admin.DescribeWorkflowExecutionRequest{ Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), } } // ToAdminDescribeWorkflowExecutionRequest converts thrift DescribeWorkflowExecutionRequest type to internal func ToAdminDescribeWorkflowExecutionRequest(t *admin.DescribeWorkflowExecutionRequest) *types.AdminDescribeWorkflowExecutionRequest { if t == nil { return nil } return &types.AdminDescribeWorkflowExecutionRequest{ Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), } } // FromAdminDescribeWorkflowExecutionResponse converts internal DescribeWorkflowExecutionResponse type to thrift func FromAdminDescribeWorkflowExecutionResponse(t *types.AdminDescribeWorkflowExecutionResponse) *admin.DescribeWorkflowExecutionResponse { if t == nil { return nil } return &admin.DescribeWorkflowExecutionResponse{ ShardId: &t.ShardID, HistoryAddr: &t.HistoryAddr, MutableStateInCache: &t.MutableStateInCache, MutableStateInDatabase: &t.MutableStateInDatabase, } } // ToAdminDescribeWorkflowExecutionResponse converts thrift DescribeWorkflowExecutionResponse type to internal func ToAdminDescribeWorkflowExecutionResponse(t *admin.DescribeWorkflowExecutionResponse) *types.AdminDescribeWorkflowExecutionResponse { if t == nil { return nil } return &types.AdminDescribeWorkflowExecutionResponse{ ShardID: t.GetShardId(), HistoryAddr: t.GetHistoryAddr(), MutableStateInCache: t.GetMutableStateInCache(), MutableStateInDatabase: t.GetMutableStateInDatabase(), } } // FromAdminGetWorkflowExecutionRawHistoryV2Request converts internal GetWorkflowExecutionRawHistoryV2Request type to thrift func FromAdminGetWorkflowExecutionRawHistoryV2Request(t *types.GetWorkflowExecutionRawHistoryV2Request) *admin.GetWorkflowExecutionRawHistoryV2Request { if t == nil { return nil } return &admin.GetWorkflowExecutionRawHistoryV2Request{ Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), StartEventId: t.StartEventID, StartEventVersion: t.StartEventVersion, EndEventId: t.EndEventID, EndEventVersion: t.EndEventVersion, MaximumPageSize: &t.MaximumPageSize, NextPageToken: t.NextPageToken, } } // ToAdminGetWorkflowExecutionRawHistoryV2Request converts thrift GetWorkflowExecutionRawHistoryV2Request type to internal func ToAdminGetWorkflowExecutionRawHistoryV2Request(t *admin.GetWorkflowExecutionRawHistoryV2Request) *types.GetWorkflowExecutionRawHistoryV2Request { if t == nil { return nil } return &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), StartEventID: t.StartEventId, StartEventVersion: t.StartEventVersion, EndEventID: t.EndEventId, EndEventVersion: t.EndEventVersion, MaximumPageSize: t.GetMaximumPageSize(), NextPageToken: t.NextPageToken, } } // FromAdminGetWorkflowExecutionRawHistoryV2Response converts internal GetWorkflowExecutionRawHistoryV2Response type to thrift func FromAdminGetWorkflowExecutionRawHistoryV2Response(t *types.GetWorkflowExecutionRawHistoryV2Response) *admin.GetWorkflowExecutionRawHistoryV2Response { if t == nil { return nil } return &admin.GetWorkflowExecutionRawHistoryV2Response{ NextPageToken: t.NextPageToken, HistoryBatches: FromDataBlobArray(t.HistoryBatches), VersionHistory: FromVersionHistory(t.VersionHistory), } } // ToAdminGetWorkflowExecutionRawHistoryV2Response converts thrift GetWorkflowExecutionRawHistoryV2Response type to internal func ToAdminGetWorkflowExecutionRawHistoryV2Response(t *admin.GetWorkflowExecutionRawHistoryV2Response) *types.GetWorkflowExecutionRawHistoryV2Response { if t == nil { return nil } return &types.GetWorkflowExecutionRawHistoryV2Response{ NextPageToken: t.NextPageToken, HistoryBatches: ToDataBlobArray(t.HistoryBatches), VersionHistory: ToVersionHistory(t.VersionHistory), } } // FromHostInfo converts internal HostInfo type to thrift func FromHostInfo(t *types.HostInfo) *admin.HostInfo { if t == nil { return nil } return &admin.HostInfo{ Identity: &t.Identity, } } // ToHostInfo converts thrift HostInfo type to internal func ToHostInfo(t *admin.HostInfo) *types.HostInfo { if t == nil { return nil } return &types.HostInfo{ Identity: t.GetIdentity(), } } // FromMembershipInfo converts internal MembershipInfo type to thrift func FromMembershipInfo(t *types.MembershipInfo) *admin.MembershipInfo { if t == nil { return nil } return &admin.MembershipInfo{ CurrentHost: FromHostInfo(t.CurrentHost), ReachableMembers: t.ReachableMembers, Rings: FromRingInfoArray(t.Rings), } } // ToMembershipInfo converts thrift MembershipInfo type to internal func ToMembershipInfo(t *admin.MembershipInfo) *types.MembershipInfo { if t == nil { return nil } return &types.MembershipInfo{ CurrentHost: ToHostInfo(t.CurrentHost), ReachableMembers: t.ReachableMembers, Rings: ToRingInfoArray(t.Rings), } } // FromPersistenceInfoMap converts internal map[string]*types.PersistenceInfo type to thrift func FromPersistenceInfoMap(t map[string]*types.PersistenceInfo) map[string]*admin.PersistenceInfo { if t == nil { return nil } v := make(map[string]*admin.PersistenceInfo, len(t)) for key := range t { v[key] = FromPersistenceInfo(t[key]) } return v } // FromPersistenceInfo converts internal PersistenceInfo type to thrift func FromPersistenceInfo(t *types.PersistenceInfo) *admin.PersistenceInfo { if t == nil { return nil } return &admin.PersistenceInfo{ Backend: &t.Backend, Settings: FromPersistenceSettings(t.Settings), Features: FromPersistenceFeatures(t.Features), } } // FromPersistenceSettings converts internal []*types.PersistenceSetting type to thrift func FromPersistenceSettings(t []*types.PersistenceSetting) []*admin.PersistenceSetting { if t == nil { return nil } v := make([]*admin.PersistenceSetting, len(t)) for i := range t { v[i] = FromPersistenceSetting(t[i]) } return v } // FromPersistenceSetting converts internal PersistenceSetting type to thrift func FromPersistenceSetting(t *types.PersistenceSetting) *admin.PersistenceSetting { if t == nil { return nil } return &admin.PersistenceSetting{ Key: &t.Key, Value: &t.Value, } } // FromPersistenceFeatures converts internal []*types.PersistenceFeature type to thrift func FromPersistenceFeatures(t []*types.PersistenceFeature) []*admin.PersistenceFeature { if t == nil { return nil } v := make([]*admin.PersistenceFeature, len(t)) for i := range t { v[i] = FromPersistenceFeature(t[i]) } return v } // FromPersistenceFeature converts internal PersistenceFeature type to thrift func FromPersistenceFeature(t *types.PersistenceFeature) *admin.PersistenceFeature { if t == nil { return nil } return &admin.PersistenceFeature{ Key: &t.Key, Enabled: &t.Enabled, } } // ToPersistenceInfoMap converts thrift to internal map[string]*types.PersistenceInfo type func ToPersistenceInfoMap(t map[string]*admin.PersistenceInfo) map[string]*types.PersistenceInfo { if t == nil { return nil } v := make(map[string]*types.PersistenceInfo, len(t)) for key := range t { v[key] = ToPersistenceInfo(t[key]) } return v } // ToPersistenceInfo converts thrift to internal PersistenceInfo type func ToPersistenceInfo(t *admin.PersistenceInfo) *types.PersistenceInfo { if t == nil { return nil } return &types.PersistenceInfo{ Backend: t.GetBackend(), Settings: ToPersistenceSettings(t.Settings), Features: ToPersistenceFeatures(t.Features), } } // ToPersistenceSettings converts thrift to internal []*types.PersistenceSetting type func ToPersistenceSettings(t []*admin.PersistenceSetting) []*types.PersistenceSetting { if t == nil { return nil } v := make([]*types.PersistenceSetting, len(t)) for i := range t { v[i] = ToPersistenceSetting(t[i]) } return v } // ToPersistenceSetting converts from thrift to internal PersistenceSetting type func ToPersistenceSetting(t *admin.PersistenceSetting) *types.PersistenceSetting { if t == nil { return nil } return &types.PersistenceSetting{ Key: t.GetKey(), Value: t.GetValue(), } } // ToPersistenceFeatures converts from thrift to internal []*types.PersistenceFeature type func ToPersistenceFeatures(t []*admin.PersistenceFeature) []*types.PersistenceFeature { if t == nil { return nil } v := make([]*types.PersistenceFeature, len(t)) for i := range t { v[i] = ToPersistenceFeature(t[i]) } return v } // ToPersistenceFeature converts from thrift to internal PersistenceFeature type func ToPersistenceFeature(t *admin.PersistenceFeature) *types.PersistenceFeature { if t == nil { return nil } return &types.PersistenceFeature{ Key: t.GetKey(), Enabled: t.GetEnabled(), } } // FromAdminResendReplicationTasksRequest converts internal ResendReplicationTasksRequest type to thrift func FromAdminResendReplicationTasksRequest(t *types.ResendReplicationTasksRequest) *admin.ResendReplicationTasksRequest { if t == nil { return nil } return &admin.ResendReplicationTasksRequest{ DomainID: &t.DomainID, WorkflowID: &t.WorkflowID, RunID: &t.RunID, RemoteCluster: &t.RemoteCluster, StartEventID: t.StartEventID, StartVersion: t.StartVersion, EndEventID: t.EndEventID, EndVersion: t.EndVersion, } } // ToAdminResendReplicationTasksRequest converts thrift ResendReplicationTasksRequest type to internal func ToAdminResendReplicationTasksRequest(t *admin.ResendReplicationTasksRequest) *types.ResendReplicationTasksRequest { if t == nil { return nil } return &types.ResendReplicationTasksRequest{ DomainID: t.GetDomainID(), WorkflowID: t.GetWorkflowID(), RunID: t.GetRunID(), RemoteCluster: t.GetRemoteCluster(), StartEventID: t.StartEventID, StartVersion: t.StartVersion, EndEventID: t.EndEventID, EndVersion: t.EndVersion, } } // FromRingInfo converts internal RingInfo type to thrift func FromRingInfo(t *types.RingInfo) *admin.RingInfo { if t == nil { return nil } return &admin.RingInfo{ Role: &t.Role, MemberCount: &t.MemberCount, Members: FromHostInfoArray(t.Members), } } // ToRingInfo converts thrift RingInfo type to internal func ToRingInfo(t *admin.RingInfo) *types.RingInfo { if t == nil { return nil } return &types.RingInfo{ Role: t.GetRole(), MemberCount: t.GetMemberCount(), Members: ToHostInfoArray(t.Members), } } // FromRingInfoArray converts internal RingInfo type array to thrift func FromRingInfoArray(t []*types.RingInfo) []*admin.RingInfo { if t == nil { return nil } v := make([]*admin.RingInfo, len(t)) for i := range t { v[i] = FromRingInfo(t[i]) } return v } // ToRingInfoArray converts thrift RingInfo type array to internal func ToRingInfoArray(t []*admin.RingInfo) []*types.RingInfo { if t == nil { return nil } v := make([]*types.RingInfo, len(t)) for i := range t { v[i] = ToRingInfo(t[i]) } return v } // FromHostInfoArray converts internal HostInfo type array to thrift func FromHostInfoArray(t []*types.HostInfo) []*admin.HostInfo { if t == nil { return nil } v := make([]*admin.HostInfo, len(t)) for i := range t { v[i] = FromHostInfo(t[i]) } return v } // ToHostInfoArray converts thrift HostInfo type array to internal func ToHostInfoArray(t []*admin.HostInfo) []*types.HostInfo { if t == nil { return nil } v := make([]*types.HostInfo, len(t)) for i := range t { v[i] = ToHostInfo(t[i]) } return v } // FromAdminGetDynamicConfigRequest converts internal GetDynamicConfigRequest type to thrift func FromAdminGetDynamicConfigRequest(t *types.GetDynamicConfigRequest) *admin.GetDynamicConfigRequest { if t == nil { return nil } return &admin.GetDynamicConfigRequest{ ConfigName: &t.ConfigName, Filters: FromDynamicConfigFilterArray(t.Filters), } } // ToAdminGetDynamicConfigRequest converts thrift GetDynamicConfigRequest type to internal func ToAdminGetDynamicConfigRequest(t *admin.GetDynamicConfigRequest) *types.GetDynamicConfigRequest { if t == nil { return nil } return &types.GetDynamicConfigRequest{ ConfigName: t.GetConfigName(), Filters: ToDynamicConfigFilterArray(t.Filters), } } // FromAdminGetDynamicConfigResponse converts internal GetDynamicConfigResponse type to thrift func FromAdminGetDynamicConfigResponse(t *types.GetDynamicConfigResponse) *admin.GetDynamicConfigResponse { if t == nil { return nil } return &admin.GetDynamicConfigResponse{ Value: FromDataBlob(t.Value), } } // ToAdminGetDynamicConfigResponse converts thrift GetDynamicConfigResponse type to internal func ToAdminGetDynamicConfigResponse(t *admin.GetDynamicConfigResponse) *types.GetDynamicConfigResponse { if t == nil { return nil } return &types.GetDynamicConfigResponse{ Value: ToDataBlob(t.Value), } } // FromAdminUpdateDynamicConfigRequest converts internal UpdateDynamicConfigRequest type to thrift func FromAdminUpdateDynamicConfigRequest(t *types.UpdateDynamicConfigRequest) *admin.UpdateDynamicConfigRequest { if t == nil { return nil } return &admin.UpdateDynamicConfigRequest{ ConfigName: &t.ConfigName, ConfigValues: FromDynamicConfigValueArray(t.ConfigValues), } } // ToAdminUpdateDynamicConfigRequest converts thrift UpdateDynamicConfigRequest type to internal func ToAdminUpdateDynamicConfigRequest(t *admin.UpdateDynamicConfigRequest) *types.UpdateDynamicConfigRequest { if t == nil { return nil } return &types.UpdateDynamicConfigRequest{ ConfigName: t.GetConfigName(), ConfigValues: ToDynamicConfigValueArray(t.ConfigValues), } } // FromAdminRestoreDynamicConfigRequest converts internal RestoreDynamicConfigRequest type to thrift func FromAdminRestoreDynamicConfigRequest(t *types.RestoreDynamicConfigRequest) *admin.RestoreDynamicConfigRequest { if t == nil { return nil } return &admin.RestoreDynamicConfigRequest{ ConfigName: &t.ConfigName, Filters: FromDynamicConfigFilterArray(t.Filters), } } // ToAdminRestoreDynamicConfigRequest converts thrift RestoreDynamicConfigRequest type to internal func ToAdminRestoreDynamicConfigRequest(t *admin.RestoreDynamicConfigRequest) *types.RestoreDynamicConfigRequest { if t == nil { return nil } return &types.RestoreDynamicConfigRequest{ ConfigName: t.GetConfigName(), Filters: ToDynamicConfigFilterArray(t.Filters), } } // FromAdminDeleteWorkflowRequest converts internal AdminDeleteWorkflowRequest type to thrift func FromAdminDeleteWorkflowRequest(t *types.AdminDeleteWorkflowRequest) *admin.AdminDeleteWorkflowRequest { if t == nil { return nil } return &admin.AdminDeleteWorkflowRequest{ Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), } } // ToAdminDeleteWorkflowRequest converts thrift AdminDeleteWorkflowRequest type to internal func ToAdminDeleteWorkflowRequest(t *admin.AdminDeleteWorkflowRequest) *types.AdminDeleteWorkflowRequest { if t == nil { return nil } return &types.AdminDeleteWorkflowRequest{ Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), } } // FromAdminDeleteWorkflowResponse converts internal AdminDeleteWorkflowResponse type to thrift func FromAdminDeleteWorkflowResponse(t *types.AdminDeleteWorkflowResponse) *admin.AdminDeleteWorkflowResponse { if t == nil { return nil } return &admin.AdminDeleteWorkflowResponse{ HistoryDeleted: &t.HistoryDeleted, ExecutionsDeleted: &t.ExecutionsDeleted, VisibilityDeleted: &t.VisibilityDeleted, } } // ToAdminDeleteWorkflowResponse converts thrift AdminDeleteWorkflowResponse type to internal func ToAdminDeleteWorkflowResponse(t *admin.AdminDeleteWorkflowResponse) *types.AdminDeleteWorkflowResponse { if t == nil { return nil } return &types.AdminDeleteWorkflowResponse{ HistoryDeleted: *t.HistoryDeleted, ExecutionsDeleted: *t.ExecutionsDeleted, VisibilityDeleted: *t.VisibilityDeleted, } } // FromAdminMaintainCorruptWorkflowRequest converts internal AdminMaintainWorkflowRequest type to thrift func FromAdminMaintainCorruptWorkflowRequest(t *types.AdminMaintainWorkflowRequest) *admin.AdminMaintainWorkflowRequest { if t == nil { return nil } return &admin.AdminMaintainWorkflowRequest{ Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), } } // ToAdminMaintainCorruptWorkflowRequest converts thrift AdminMaintainWorkflowRequest type to internal func ToAdminMaintainCorruptWorkflowRequest(t *admin.AdminMaintainWorkflowRequest) *types.AdminMaintainWorkflowRequest { if t == nil { return nil } return &types.AdminMaintainWorkflowRequest{ Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), } } // FromAdminMaintainCorruptWorkflowResponse converts internal AdminMaintainWorkflowResponse type to thrift func FromAdminMaintainCorruptWorkflowResponse(t *types.AdminMaintainWorkflowResponse) *admin.AdminMaintainWorkflowResponse { if t == nil { return nil } return &admin.AdminMaintainWorkflowResponse{ HistoryDeleted: &t.HistoryDeleted, ExecutionsDeleted: &t.ExecutionsDeleted, VisibilityDeleted: &t.VisibilityDeleted, } } // ToAdminMaintainCorruptWorkflowResponse converts thrift AdminMaintainWorkflowResponse type to internal func ToAdminMaintainCorruptWorkflowResponse(t *admin.AdminMaintainWorkflowResponse) *types.AdminMaintainWorkflowResponse { if t == nil { return nil } return &types.AdminMaintainWorkflowResponse{ HistoryDeleted: *t.HistoryDeleted, ExecutionsDeleted: *t.ExecutionsDeleted, VisibilityDeleted: *t.VisibilityDeleted, } } // FromAdminListDynamicConfigResponse converts internal ListDynamicConfigResponse type to thrift func FromAdminListDynamicConfigResponse(t *types.ListDynamicConfigResponse) *admin.ListDynamicConfigResponse { if t == nil { return nil } return &admin.ListDynamicConfigResponse{ Entries: FromDynamicConfigEntryArray(t.Entries), } } // FromAdminListDynamicConfigRequest converts internal ListDynamicConfigRequest type to thrift func FromAdminListDynamicConfigRequest(t *types.ListDynamicConfigRequest) *admin.ListDynamicConfigRequest { if t == nil { return nil } return &admin.ListDynamicConfigRequest{ ConfigName: &t.ConfigName, } } // ToAdminListDynamicConfigRequest converts thrift ListDynamicConfigRequest type to internal func ToAdminListDynamicConfigRequest(t *admin.ListDynamicConfigRequest) *types.ListDynamicConfigRequest { if t == nil { return nil } return &types.ListDynamicConfigRequest{ ConfigName: t.GetConfigName(), } } // ToAdminListDynamicConfigResponse converts thrift ListDynamicConfigResponse type to internal func ToAdminListDynamicConfigResponse(t *admin.ListDynamicConfigResponse) *types.ListDynamicConfigResponse { if t == nil { return nil } return &types.ListDynamicConfigResponse{ Entries: ToDynamicConfigEntryArray(t.Entries), } } func FromAdminGetGlobalIsolationGroupsRequest(t *types.GetGlobalIsolationGroupsRequest) *admin.GetGlobalIsolationGroupsRequest { if t == nil { return nil } return &admin.GetGlobalIsolationGroupsRequest{} } func FromAdminGetDomainIsolationGroupsRequest(t *types.GetDomainIsolationGroupsRequest) *admin.GetDomainIsolationGroupsRequest { if t == nil { return nil } return &admin.GetDomainIsolationGroupsRequest{ Domain: &t.Domain, } } func FromAdminGetGlobalIsolationGroupsResponse(t *types.GetGlobalIsolationGroupsResponse) *admin.GetGlobalIsolationGroupsResponse { if t == nil { return nil } cfg := FromIsolationGroupConfig(&t.IsolationGroups) if cfg == nil || cfg.GetIsolationGroups() == nil { return &admin.GetGlobalIsolationGroupsResponse{} } return &admin.GetGlobalIsolationGroupsResponse{ IsolationGroups: cfg, } } func ToAdminGetGlobalIsolationGroupsResponse(t *admin.GetGlobalIsolationGroupsResponse) *types.GetGlobalIsolationGroupsResponse { if t == nil { return nil } if t.IsolationGroups == nil { return &types.GetGlobalIsolationGroupsResponse{} } ig := ToIsolationGroupConfig(t.IsolationGroups) if ig == nil || len(*ig) == 0 { return &types.GetGlobalIsolationGroupsResponse{} } return &types.GetGlobalIsolationGroupsResponse{ IsolationGroups: *ig, } } func ToAdminGetDomainIsolationGroupsResponse(t *admin.GetDomainIsolationGroupsResponse) *types.GetDomainIsolationGroupsResponse { if t == nil { return nil } if t.IsolationGroups == nil { return &types.GetDomainIsolationGroupsResponse{} } ig := ToIsolationGroupConfig(t.IsolationGroups) if ig == nil || len(*ig) == 0 { return &types.GetDomainIsolationGroupsResponse{} } return &types.GetDomainIsolationGroupsResponse{ IsolationGroups: *ig, } } func ToAdminGetGlobalIsolationGroupsRequest(t *admin.GetGlobalIsolationGroupsRequest) *types.GetGlobalIsolationGroupsRequest { if t == nil { return nil } return &types.GetGlobalIsolationGroupsRequest{} } func FromAdminGetDomainIsolationGroupsResponse(t *types.GetDomainIsolationGroupsResponse) *admin.GetDomainIsolationGroupsResponse { if t == nil { return nil } cfg := FromIsolationGroupConfig(&t.IsolationGroups) return &admin.GetDomainIsolationGroupsResponse{ IsolationGroups: cfg, } } func ToAdminGetDomainIsolationGroupsRequest(t *admin.GetDomainIsolationGroupsRequest) *types.GetDomainIsolationGroupsRequest { if t == nil { return nil } return &types.GetDomainIsolationGroupsRequest{Domain: t.GetDomain()} } func FromAdminUpdateGlobalIsolationGroupsResponse(t *types.UpdateGlobalIsolationGroupsResponse) *admin.UpdateGlobalIsolationGroupsResponse { if t == nil { return nil } return &admin.UpdateGlobalIsolationGroupsResponse{} } func FromAdminUpdateGlobalIsolationGroupsRequest(t *types.UpdateGlobalIsolationGroupsRequest) *admin.UpdateGlobalIsolationGroupsRequest { if t == nil { return nil } if t.IsolationGroups == nil { return &admin.UpdateGlobalIsolationGroupsRequest{} } return &admin.UpdateGlobalIsolationGroupsRequest{ IsolationGroups: FromIsolationGroupConfig(&t.IsolationGroups), } } func FromAdminUpdateDomainIsolationGroupsRequest(t *types.UpdateDomainIsolationGroupsRequest) *admin.UpdateDomainIsolationGroupsRequest { if t == nil { return nil } if t.IsolationGroups == nil { return &admin.UpdateDomainIsolationGroupsRequest{} } return &admin.UpdateDomainIsolationGroupsRequest{ Domain: &t.Domain, IsolationGroups: FromIsolationGroupConfig(&t.IsolationGroups), } } func ToAdminUpdateGlobalIsolationGroupsRequest(t *admin.UpdateGlobalIsolationGroupsRequest) *types.UpdateGlobalIsolationGroupsRequest { if t == nil { return nil } cfg := ToIsolationGroupConfig(t.IsolationGroups) if cfg == nil { return &types.UpdateGlobalIsolationGroupsRequest{} } return &types.UpdateGlobalIsolationGroupsRequest{ IsolationGroups: *cfg, } } func ToAdminUpdateGlobalIsolationGroupsResponse(t *admin.UpdateGlobalIsolationGroupsResponse) *types.UpdateGlobalIsolationGroupsResponse { if t == nil { return nil } return &types.UpdateGlobalIsolationGroupsResponse{} } func ToAdminUpdateDomainIsolationGroupsResponse(t *admin.UpdateDomainIsolationGroupsResponse) *types.UpdateDomainIsolationGroupsResponse { if t == nil { return nil } return &types.UpdateDomainIsolationGroupsResponse{} } func FromAdminUpdateDomainIsolationGroupsResponse(t *types.UpdateDomainIsolationGroupsResponse) *admin.UpdateDomainIsolationGroupsResponse { if t == nil { return nil } return &admin.UpdateDomainIsolationGroupsResponse{} } func ToAdminUpdateDomainIsolationGroupsRequest(t *admin.UpdateDomainIsolationGroupsRequest) *types.UpdateDomainIsolationGroupsRequest { if t == nil { return nil } cfg := ToIsolationGroupConfig(t.IsolationGroups) if cfg == nil { return &types.UpdateDomainIsolationGroupsRequest{ Domain: t.GetDomain(), } } return &types.UpdateDomainIsolationGroupsRequest{ Domain: t.GetDomain(), IsolationGroups: *cfg, } } func FromIsolationGroupConfig(in *types.IsolationGroupConfiguration) *shared.IsolationGroupConfiguration { if in == nil { return nil } var out []*shared.IsolationGroupPartition for _, v := range *in { out = append(out, &shared.IsolationGroupPartition{ Name: strPtr(v.Name), State: igStatePtr(shared.IsolationGroupState(v.State)), }) } sort.Slice(out, func(i, j int) bool { if out[i] == nil || out[j] == nil { return false } return *out[i].Name < *out[j].Name }) return &shared.IsolationGroupConfiguration{ IsolationGroups: out, } } func ToIsolationGroupConfig(in *shared.IsolationGroupConfiguration) *types.IsolationGroupConfiguration { if in == nil { return nil } out := make(types.IsolationGroupConfiguration) for v := range in.IsolationGroups { out[in.IsolationGroups[v].GetName()] = types.IsolationGroupPartition{ Name: in.IsolationGroups[v].GetName(), State: types.IsolationGroupState(in.IsolationGroups[v].GetState()), } } return &out } func ToAdminGetDomainAsyncWorkflowConfiguratonRequest(in *admin.GetDomainAsyncWorkflowConfiguratonRequest) *types.GetDomainAsyncWorkflowConfiguratonRequest { if in == nil { return nil } return &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: in.GetDomain(), } } func FromAdminGetDomainAsyncWorkflowConfiguratonResponse(in *types.GetDomainAsyncWorkflowConfiguratonResponse) *admin.GetDomainAsyncWorkflowConfiguratonResponse { if in == nil { return nil } return &admin.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: FromDomainAsyncWorkflowConfiguraton(in.Configuration), } } func FromDomainAsyncWorkflowConfiguraton(in *types.AsyncWorkflowConfiguration) *shared.AsyncWorkflowConfiguration { if in == nil { return nil } return &shared.AsyncWorkflowConfiguration{ Enabled: &in.Enabled, PredefinedQueueName: strPtr(in.PredefinedQueueName), QueueType: strPtr(in.QueueType), QueueConfig: FromDataBlob(in.QueueConfig), } } func ToAdminUpdateDomainAsyncWorkflowConfiguratonRequest(in *admin.UpdateDomainAsyncWorkflowConfiguratonRequest) *types.UpdateDomainAsyncWorkflowConfiguratonRequest { if in == nil { return nil } return &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: in.GetDomain(), Configuration: ToDomainAsyncWorkflowConfiguraton(in.Configuration), } } func ToDomainAsyncWorkflowConfiguraton(in *shared.AsyncWorkflowConfiguration) *types.AsyncWorkflowConfiguration { if in == nil { return nil } return &types.AsyncWorkflowConfiguration{ Enabled: in.GetEnabled(), PredefinedQueueName: in.GetPredefinedQueueName(), QueueType: in.GetQueueType(), QueueConfig: ToDataBlob(in.GetQueueConfig()), } } func FromAdminUpdateDomainAsyncWorkflowConfiguratonResponse(in *types.UpdateDomainAsyncWorkflowConfiguratonResponse) *admin.UpdateDomainAsyncWorkflowConfiguratonResponse { if in == nil { return nil } return &admin.UpdateDomainAsyncWorkflowConfiguratonResponse{} } func FromAdminGetDomainAsyncWorkflowConfiguratonRequest(in *types.GetDomainAsyncWorkflowConfiguratonRequest) *admin.GetDomainAsyncWorkflowConfiguratonRequest { if in == nil { return nil } return &admin.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: strPtr(in.Domain), } } func ToAdminGetDomainAsyncWorkflowConfiguratonResponse(in *admin.GetDomainAsyncWorkflowConfiguratonResponse) *types.GetDomainAsyncWorkflowConfiguratonResponse { if in == nil { return nil } return &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: ToDomainAsyncWorkflowConfiguraton(in.Configuration), } } func FromAdminUpdateDomainAsyncWorkflowConfiguratonRequest(in *types.UpdateDomainAsyncWorkflowConfiguratonRequest) *admin.UpdateDomainAsyncWorkflowConfiguratonRequest { if in == nil { return nil } return &admin.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: strPtr(in.Domain), Configuration: FromDomainAsyncWorkflowConfiguraton(in.Configuration), } } func ToAdminUpdateDomainAsyncWorkflowConfiguratonResponse(in *admin.UpdateDomainAsyncWorkflowConfiguratonResponse) *types.UpdateDomainAsyncWorkflowConfiguratonResponse { if in == nil { return nil } return &types.UpdateDomainAsyncWorkflowConfiguratonResponse{} } func strPtr(s string) *string { return &s } func igStatePtr(s shared.IsolationGroupState) *shared.IsolationGroupState { return &s } ================================================ FILE: common/types/mapper/thrift/admin_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "sort" "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/.gen/go/admin" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" ) func TestAdminAddSearchAttributeRequest(t *testing.T) { for _, item := range []*types.AddSearchAttributeRequest{nil, {}, &testdata.AdminAddSearchAttributeRequest} { assert.Equal(t, item, ToAdminAddSearchAttributeRequest(FromAdminAddSearchAttributeRequest(item))) } } func TestAdminCloseShardRequest(t *testing.T) { for _, item := range []*types.CloseShardRequest{nil, {}, &testdata.AdminCloseShardRequest} { assert.Equal(t, item, ToAdminCloseShardRequest(FromAdminCloseShardRequest(item))) } } func TestAdminDeleteWorkflowRequest(t *testing.T) { for _, item := range []*types.AdminDeleteWorkflowRequest{nil, {}, &testdata.AdminDeleteWorkflowRequest} { assert.Equal(t, item, ToAdminDeleteWorkflowRequest(FromAdminDeleteWorkflowRequest(item))) } } func TestAdminDeleteWorkflowResponse(t *testing.T) { for _, item := range []*types.AdminDeleteWorkflowResponse{nil, {}, &testdata.AdminDeleteWorkflowResponse} { assert.Equal(t, item, ToAdminDeleteWorkflowResponse(FromAdminDeleteWorkflowResponse(item))) } } func TestAdminDescribeClusterResponse(t *testing.T) { for _, item := range []*types.DescribeClusterResponse{nil, {}, &testdata.AdminDescribeClusterResponse} { assert.Equal(t, item, ToAdminDescribeClusterResponse(FromAdminDescribeClusterResponse(item))) } } func TestAdminDescribeHistoryHostRequest(t *testing.T) { for _, item := range []*types.DescribeHistoryHostRequest{ nil, &testdata.AdminDescribeHistoryHostRequest_ByHost, &testdata.AdminDescribeHistoryHostRequest_ByShard, &testdata.AdminDescribeHistoryHostRequest_ByExecution, } { assert.Equal(t, item, ToAdminDescribeHistoryHostRequest(FromAdminDescribeHistoryHostRequest(item))) } } func TestAdminDescribeHistoryHostResponse(t *testing.T) { for _, item := range []*types.DescribeHistoryHostResponse{nil, {}, &testdata.AdminDescribeHistoryHostResponse} { assert.Equal(t, item, ToAdminDescribeHistoryHostResponse(FromAdminDescribeHistoryHostResponse(item))) } } func TestAdminDescribeQueueRequest(t *testing.T) { for _, item := range []*types.DescribeQueueRequest{nil, {}, &testdata.AdminDescribeQueueRequest} { assert.Equal(t, item, ToAdminDescribeQueueRequest(FromAdminDescribeQueueRequest(item))) } } func TestAdminDescribeQueueResponse(t *testing.T) { for _, item := range []*types.DescribeQueueResponse{nil, {}, &testdata.AdminDescribeQueueResponse} { assert.Equal(t, item, ToAdminDescribeQueueResponse(FromAdminDescribeQueueResponse(item))) } } func TestAdminDescribeShardDistributionRequest(t *testing.T) { for _, item := range []*types.DescribeShardDistributionRequest{nil, {}, &testdata.AdminDescribeShardDistributionRequest} { assert.Equal(t, item, ToAdminDescribeShardDistributionRequest(FromAdminDescribeShardDistributionRequest(item))) } } func TestAdminDescribeShardDistributionResponse(t *testing.T) { for _, item := range []*types.DescribeShardDistributionResponse{nil, {}, &testdata.AdminDescribeShardDistributionResponse} { assert.Equal(t, item, ToAdminDescribeShardDistributionResponse(FromAdminDescribeShardDistributionResponse(item))) } } func TestAdminDescribeWorkflowExecutionRequest(t *testing.T) { for _, item := range []*types.AdminDescribeWorkflowExecutionRequest{nil, {}, &testdata.AdminDescribeWorkflowExecutionRequest} { assert.Equal(t, item, ToAdminDescribeWorkflowExecutionRequest(FromAdminDescribeWorkflowExecutionRequest(item))) } } func TestAdminDescribeWorkflowExecutionResponse(t *testing.T) { for _, item := range []*types.AdminDescribeWorkflowExecutionResponse{nil, {ShardID: "0"}, &testdata.AdminDescribeWorkflowExecutionResponse} { assert.Equal(t, item, ToAdminDescribeWorkflowExecutionResponse(FromAdminDescribeWorkflowExecutionResponse(item))) } } func TestAdminGetDomainIsolationGroupsRequest(t *testing.T) { for _, item := range []*types.GetDomainIsolationGroupsRequest{nil, {}, &testdata.AdminGetDomainIsolationGroupsRequest} { assert.Equal(t, item, ToAdminGetDomainIsolationGroupsRequest(FromAdminGetDomainIsolationGroupsRequest(item))) } } func TestAdminGetDomainIsolationGroupsResponse(t *testing.T) { for _, item := range []*types.GetDomainIsolationGroupsResponse{nil, {}, &testdata.AdminGetDomainIsolationGroupsResponse} { assert.Equal(t, item, ToAdminGetDomainIsolationGroupsResponse(FromAdminGetDomainIsolationGroupsResponse(item))) } } func TestAdminGetDomainReplicationMessagesRequest(t *testing.T) { for _, item := range []*types.GetDomainReplicationMessagesRequest{nil, {}, &testdata.AdminGetDomainReplicationMessagesRequest} { assert.Equal(t, item, ToAdminGetDomainReplicationMessagesRequest(FromAdminGetDomainReplicationMessagesRequest(item))) } } func TestAdminGetDomainReplicationMessagesResponse(t *testing.T) { for _, item := range []*types.GetDomainReplicationMessagesResponse{nil, {}, &testdata.AdminGetDomainReplicationMessagesResponse} { assert.Equal(t, item, ToAdminGetDomainReplicationMessagesResponse(FromAdminGetDomainReplicationMessagesResponse(item))) } } func TestAdminGetDynamicConfigRequest(t *testing.T) { for _, item := range []*types.GetDynamicConfigRequest{nil, {}, &testdata.AdminGetDynamicConfigRequest} { assert.Equal(t, item, ToAdminGetDynamicConfigRequest(FromAdminGetDynamicConfigRequest(item))) } } func TestAdminGetDynamicConfigResponse(t *testing.T) { for _, item := range []*types.GetDynamicConfigResponse{nil, {}, &testdata.AdminGetDynamicConfigResponse} { assert.Equal(t, item, ToAdminGetDynamicConfigResponse(FromAdminGetDynamicConfigResponse(item))) } } func TestAdminGetGlobalIsolationGroupsRequest(t *testing.T) { for _, item := range []*types.GetGlobalIsolationGroupsRequest{nil, {}, &testdata.AdminGetGlobalIsolationGroupsRequest} { assert.Equal(t, item, ToAdminGetGlobalIsolationGroupsRequest(FromAdminGetGlobalIsolationGroupsRequest(item))) } } func TestAdminGetReplicationMessagesRequest(t *testing.T) { for _, item := range []*types.GetReplicationMessagesRequest{nil, {}, &testdata.AdminGetReplicationMessagesRequest} { assert.Equal(t, item, ToAdminGetReplicationMessagesRequest(FromAdminGetReplicationMessagesRequest(item))) } } func TestAdminGetReplicationMessagesResponse(t *testing.T) { for _, item := range []*types.GetReplicationMessagesResponse{nil, {}, &testdata.AdminGetReplicationMessagesResponse} { assert.Equal(t, item, ToAdminGetReplicationMessagesResponse(FromAdminGetReplicationMessagesResponse(item))) } } func TestAdminGetWorkflowExecutionRawHistoryV2Request(t *testing.T) { for _, item := range []*types.GetWorkflowExecutionRawHistoryV2Request{nil, {}, &testdata.AdminGetWorkflowExecutionRawHistoryV2Request} { assert.Equal(t, item, ToAdminGetWorkflowExecutionRawHistoryV2Request(FromAdminGetWorkflowExecutionRawHistoryV2Request(item))) } } func TestAdminGetWorkflowExecutionRawHistoryV2Response(t *testing.T) { for _, item := range []*types.GetWorkflowExecutionRawHistoryV2Response{nil, {}, &testdata.AdminGetWorkflowExecutionRawHistoryV2Response} { assert.Equal(t, item, ToAdminGetWorkflowExecutionRawHistoryV2Response(FromAdminGetWorkflowExecutionRawHistoryV2Response(item))) } } func TestAdminListDynamicConfigRequest(t *testing.T) { for _, item := range []*types.ListDynamicConfigRequest{nil, {}, &testdata.AdminListDynamicConfigRequest} { assert.Equal(t, item, ToAdminListDynamicConfigRequest(FromAdminListDynamicConfigRequest(item))) } } func TestAdminListDynamicConfigResponse(t *testing.T) { for _, item := range []*types.ListDynamicConfigResponse{nil, {}, &testdata.AdminListDynamicConfigResponse} { assert.Equal(t, item, ToAdminListDynamicConfigResponse(FromAdminListDynamicConfigResponse(item))) } } func TestAdminMaintainCorruptWorkflowRequest(t *testing.T) { for _, item := range []*types.AdminMaintainWorkflowRequest{nil, {}, &testdata.AdminMaintainCorruptWorkflowRequest} { assert.Equal(t, item, ToAdminMaintainCorruptWorkflowRequest(FromAdminMaintainCorruptWorkflowRequest(item))) } } func TestAdminMaintainCorruptWorkflowResponse(t *testing.T) { for _, item := range []*types.AdminMaintainWorkflowResponse{nil, {}, &testdata.AdminMaintainCorruptWorkflowResponse} { assert.Equal(t, item, ToAdminMaintainCorruptWorkflowResponse(FromAdminMaintainCorruptWorkflowResponse(item))) } } func TestAdminReapplyEventsRequest(t *testing.T) { for _, item := range []*types.ReapplyEventsRequest{nil, {}, &testdata.AdminReapplyEventsRequest} { assert.Equal(t, item, ToAdminReapplyEventsRequest(FromAdminReapplyEventsRequest(item))) } } func TestAdminRefreshWorkflowTasksRequest(t *testing.T) { for _, item := range []*types.RefreshWorkflowTasksRequest{nil, {}, &testdata.AdminRefreshWorkflowTasksRequest} { assert.Equal(t, item, ToAdminRefreshWorkflowTasksRequest(FromAdminRefreshWorkflowTasksRequest(item))) } } func TestAdminRemoveTaskRequest(t *testing.T) { for _, item := range []*types.RemoveTaskRequest{nil, {}, &testdata.AdminRemoveTaskRequest} { assert.Equal(t, item, ToAdminRemoveTaskRequest(FromAdminRemoveTaskRequest(item))) } } func TestAdminResendReplicationTasksRequest(t *testing.T) { for _, item := range []*types.ResendReplicationTasksRequest{nil, {}, &testdata.AdminResendReplicationTasksRequest} { assert.Equal(t, item, ToAdminResendReplicationTasksRequest(FromAdminResendReplicationTasksRequest(item))) } } func TestAdminResetQueueRequest(t *testing.T) { for _, item := range []*types.ResetQueueRequest{nil, {}, &testdata.AdminResetQueueRequest} { assert.Equal(t, item, ToAdminResetQueueRequest(FromAdminResetQueueRequest(item))) } } func TestAdminGetCrossClusterTasksRequest(t *testing.T) { for _, item := range []*types.GetCrossClusterTasksRequest{nil, {}, &testdata.AdminGetCrossClusterTasksRequest} { assert.Equal(t, item, ToAdminGetCrossClusterTasksRequest(FromAdminGetCrossClusterTasksRequest(item))) } } func TestAdminGetCrossClusterTasksResponse(t *testing.T) { for _, item := range []*types.GetCrossClusterTasksResponse{nil, {}, &testdata.AdminGetCrossClusterTasksResponse} { assert.Equal(t, item, ToAdminGetCrossClusterTasksResponse(FromAdminGetCrossClusterTasksResponse(item))) } } func TestAdminRespondCrossClusterTasksCompletedRequest(t *testing.T) { for _, item := range []*types.RespondCrossClusterTasksCompletedRequest{nil, {}, &testdata.AdminRespondCrossClusterTasksCompletedRequest} { assert.Equal(t, item, ToAdminRespondCrossClusterTasksCompletedRequest(FromAdminRespondCrossClusterTasksCompletedRequest(item))) } } func TestAdminRespondCrossClusterTasksCompletedResponse(t *testing.T) { for _, item := range []*types.RespondCrossClusterTasksCompletedResponse{nil, {}, &testdata.AdminRespondCrossClusterTasksCompletedResponse} { assert.Equal(t, item, ToAdminRespondCrossClusterTasksCompletedResponse(FromAdminRespondCrossClusterTasksCompletedResponse(item))) } } func TestAdminUpdateDomainIsolationGroupsRequest(t *testing.T) { for _, item := range []*types.UpdateDomainIsolationGroupsRequest{nil, {}, &testdata.AdminUpdateDomainIsolationGroupsRequest} { assert.Equal(t, item, ToAdminUpdateDomainIsolationGroupsRequest(FromAdminUpdateDomainIsolationGroupsRequest(item))) } } func TestAdminUpdateDomainIsolationGroupsResponse(t *testing.T) { for _, item := range []*types.UpdateDomainIsolationGroupsResponse{nil, {}, &testdata.AdminUpdateDomainIsolationGroupsResponse} { assert.Equal(t, item, ToAdminUpdateDomainIsolationGroupsResponse(FromAdminUpdateDomainIsolationGroupsResponse(item))) } } func TestAdminRestoreDynamicConfigRequest(t *testing.T) { for _, item := range []*types.RestoreDynamicConfigRequest{nil, {}, &testdata.AdminRestoreDynamicConfigRequest} { assert.Equal(t, item, ToAdminRestoreDynamicConfigRequest(FromAdminRestoreDynamicConfigRequest(item))) } } func TestAdminUpdateDynamicConfigRequest(t *testing.T) { for _, item := range []*types.UpdateDynamicConfigRequest{nil, {}, &testdata.AdminUpdateDynamicConfigRequest} { assert.Equal(t, item, ToAdminUpdateDynamicConfigRequest(FromAdminUpdateDynamicConfigRequest(item))) } } func TestAdminUpdateGlobalIsolationGroupsResponse(t *testing.T) { for _, item := range []*types.UpdateGlobalIsolationGroupsResponse{nil, {}, &testdata.AdminUpdateGlobalIsolationGroupsResponse} { assert.Equal(t, item, ToAdminUpdateGlobalIsolationGroupsResponse(FromAdminUpdateGlobalIsolationGroupsResponse(item))) } } func TestFromGetGlobalIsolationGroupsResponse(t *testing.T) { tests := map[string]struct { in *types.GetGlobalIsolationGroupsResponse expected *admin.GetGlobalIsolationGroupsResponse }{ "Valid mapping": { in: &types.GetGlobalIsolationGroupsResponse{ IsolationGroups: types.IsolationGroupConfiguration{ "zone 0": { Name: "zone 0", State: types.IsolationGroupStateHealthy, }, "zone 1": { Name: "zone 1", State: types.IsolationGroupStateDrained, }, }, }, expected: &admin.GetGlobalIsolationGroupsResponse{ IsolationGroups: &shared.IsolationGroupConfiguration{ IsolationGroups: []*shared.IsolationGroupPartition{ { Name: strPtr("zone 0"), State: igStatePtr(shared.IsolationGroupStateHealthy), }, { Name: strPtr("zone 1"), State: igStatePtr(shared.IsolationGroupStateDrained), }, }, }, }, }, "nil - 1": { in: &types.GetGlobalIsolationGroupsResponse{}, expected: &admin.GetGlobalIsolationGroupsResponse{}, }, "nil - 2": { expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { res := FromAdminGetGlobalIsolationGroupsResponse(td.in) if res != nil && res.IsolationGroups != nil { sort.Slice(res.IsolationGroups.IsolationGroups, func(i int, j int) bool { return *res.IsolationGroups.IsolationGroups[i].Name < *res.IsolationGroups.IsolationGroups[j].Name }) } assert.Equal(t, td.expected, res, "expected value") roundTrip := ToAdminGetGlobalIsolationGroupsResponse(res) assert.Equal(t, td.in, roundTrip, "roundtrip value") }) } } func TestToGetGlobalIsolationGroupsRequest(t *testing.T) { tests := map[string]struct { in *admin.GetGlobalIsolationGroupsRequest expected *types.GetGlobalIsolationGroupsRequest }{ "Valid mapping": { in: &admin.GetGlobalIsolationGroupsRequest{}, expected: &types.GetGlobalIsolationGroupsRequest{}, }, "nil - 2": { in: &admin.GetGlobalIsolationGroupsRequest{}, expected: &types.GetGlobalIsolationGroupsRequest{}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminGetGlobalIsolationGroupsRequest(td.in)) }) } } func TestFromGetDomainIsolationGroupsResponse(t *testing.T) { tests := map[string]struct { in *types.GetDomainIsolationGroupsResponse expected *admin.GetDomainIsolationGroupsResponse }{ "Valid mapping": { in: &types.GetDomainIsolationGroupsResponse{ IsolationGroups: types.IsolationGroupConfiguration{ "zone 0": { Name: "zone 0", State: types.IsolationGroupStateHealthy, }, "zone 1": { Name: "zone 1", State: types.IsolationGroupStateDrained, }, }, }, expected: &admin.GetDomainIsolationGroupsResponse{ IsolationGroups: &shared.IsolationGroupConfiguration{ IsolationGroups: []*shared.IsolationGroupPartition{ { Name: strPtr("zone 0"), State: igStatePtr(shared.IsolationGroupStateHealthy), }, { Name: strPtr("zone 1"), State: igStatePtr(shared.IsolationGroupStateDrained), }, }, }, }, }, "empty": { in: &types.GetDomainIsolationGroupsResponse{ IsolationGroups: types.IsolationGroupConfiguration{}, }, expected: &admin.GetDomainIsolationGroupsResponse{ IsolationGroups: &shared.IsolationGroupConfiguration{ IsolationGroups: []*shared.IsolationGroupPartition{}, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { res := FromAdminGetDomainIsolationGroupsResponse(td.in) // map iteration is nondeterministic sort.Slice(res.IsolationGroups.IsolationGroups, func(i int, j int) bool { return *res.IsolationGroups.IsolationGroups[i].Name > *res.IsolationGroups.IsolationGroups[j].Name }) }) } } func TestToGetDomainIsolationGroupsRequest(t *testing.T) { tests := map[string]struct { in *admin.GetDomainIsolationGroupsRequest expected *types.GetDomainIsolationGroupsRequest }{ "Valid mapping": { in: &admin.GetDomainIsolationGroupsRequest{ Domain: strPtr("domain123"), }, expected: &types.GetDomainIsolationGroupsRequest{ Domain: "domain123", }, }, "empty": { in: &admin.GetDomainIsolationGroupsRequest{}, expected: &types.GetDomainIsolationGroupsRequest{}, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminGetDomainIsolationGroupsRequest(td.in)) }) } } func TestFromUpdateGlobalIsolationGroupsResponse(t *testing.T) { tests := map[string]struct { in *types.UpdateGlobalIsolationGroupsResponse expected *admin.UpdateGlobalIsolationGroupsResponse }{ "Valid mapping": {}, "empty": { in: &types.UpdateGlobalIsolationGroupsResponse{}, expected: &admin.UpdateGlobalIsolationGroupsResponse{}, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, FromAdminUpdateGlobalIsolationGroupsResponse(td.in)) }) } } func TestToUpdateGlobalIsolationGroupsRequest(t *testing.T) { tests := map[string]struct { in *admin.UpdateGlobalIsolationGroupsRequest expected *types.UpdateGlobalIsolationGroupsRequest }{ "Valid mapping": { in: &admin.UpdateGlobalIsolationGroupsRequest{ IsolationGroups: &shared.IsolationGroupConfiguration{ IsolationGroups: []*shared.IsolationGroupPartition{ { Name: strPtr("zone 1"), State: igStatePtr(shared.IsolationGroupStateHealthy), }, { Name: strPtr("zone 2"), State: igStatePtr(shared.IsolationGroupStateDrained), }, }, }, }, expected: &types.UpdateGlobalIsolationGroupsRequest{ IsolationGroups: types.IsolationGroupConfiguration{ "zone 1": types.IsolationGroupPartition{ Name: "zone 1", State: types.IsolationGroupStateHealthy, }, "zone 2": types.IsolationGroupPartition{ Name: "zone 2", State: types.IsolationGroupStateDrained, }, }, }, }, "empty": { in: &admin.UpdateGlobalIsolationGroupsRequest{}, expected: &types.UpdateGlobalIsolationGroupsRequest{}, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { res := ToAdminUpdateGlobalIsolationGroupsRequest(td.in) assert.Equal(t, td.expected, res) roundTrip := FromAdminUpdateGlobalIsolationGroupsRequest(res) if td.in != nil { assert.Equal(t, td.in, roundTrip) } }) } } func TestFromUpdateDomainIsolationGroupsResponse(t *testing.T) { tests := map[string]struct { in *types.UpdateDomainIsolationGroupsResponse expected *admin.UpdateDomainIsolationGroupsResponse }{ "empty": { in: &types.UpdateDomainIsolationGroupsResponse{}, expected: &admin.UpdateDomainIsolationGroupsResponse{}, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, FromAdminUpdateDomainIsolationGroupsResponse(td.in)) }) } } func TestToUpdateDomainIsolationGroupsRequest(t *testing.T) { tests := map[string]struct { in *admin.UpdateDomainIsolationGroupsRequest expected *types.UpdateDomainIsolationGroupsRequest }{ "valid": { in: &admin.UpdateDomainIsolationGroupsRequest{ Domain: strPtr("test-domain"), IsolationGroups: &shared.IsolationGroupConfiguration{ IsolationGroups: []*shared.IsolationGroupPartition{ { Name: strPtr("zone-1"), State: igStatePtr(shared.IsolationGroupStateHealthy), }, { Name: strPtr("zone-2"), State: igStatePtr(shared.IsolationGroupStateDrained), }, }, }, }, expected: &types.UpdateDomainIsolationGroupsRequest{ Domain: "test-domain", IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": types.IsolationGroupPartition{ Name: "zone-1", State: types.IsolationGroupStateHealthy, }, "zone-2": types.IsolationGroupPartition{ Name: "zone-2", State: types.IsolationGroupStateDrained, }, }, }, }, "nil": { in: nil, expected: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminUpdateDomainIsolationGroupsRequest(td.in)) }) } } func TestToGetGlobalIsolationGroupsResponse(t *testing.T) { tests := map[string]struct { in *admin.GetGlobalIsolationGroupsResponse expected *types.GetGlobalIsolationGroupsResponse }{ "valid": { in: &admin.GetGlobalIsolationGroupsResponse{ IsolationGroups: &shared.IsolationGroupConfiguration{ IsolationGroups: []*shared.IsolationGroupPartition{ { Name: strPtr("zone-1"), State: igStatePtr(shared.IsolationGroupStateDrained), }, { Name: strPtr("zone-2"), State: igStatePtr(shared.IsolationGroupStateHealthy), }, }, }, }, expected: &types.GetGlobalIsolationGroupsResponse{ IsolationGroups: map[string]types.IsolationGroupPartition{ "zone-1": { Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": { Name: "zone-2", State: types.IsolationGroupStateHealthy, }, }, }, }, "no groups": { in: &admin.GetGlobalIsolationGroupsResponse{}, expected: &types.GetGlobalIsolationGroupsResponse{}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.expected, ToAdminGetGlobalIsolationGroupsResponse(td.in)) }) } } func TestToAdminUpdateDomainAsyncWorkflowConfiguratonRequest(t *testing.T) { enabled := true tests := map[string]struct { input *admin.UpdateDomainAsyncWorkflowConfiguratonRequest want *types.UpdateDomainAsyncWorkflowConfiguratonRequest }{ "empty": { input: &admin.UpdateDomainAsyncWorkflowConfiguratonRequest{}, want: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{}, }, "nil": { input: nil, want: nil, }, "predefined queue": { input: &admin.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: strPtr("test-domain"), Configuration: &shared.AsyncWorkflowConfiguration{ Enabled: &enabled, PredefinedQueueName: strPtr("test-queue"), }, }, want: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: enabled, PredefinedQueueName: "test-queue", }, }, }, "inline queue": { input: &admin.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: strPtr("test-domain"), Configuration: &shared.AsyncWorkflowConfiguration{ Enabled: &enabled, QueueType: strPtr("kafka"), QueueConfig: &shared.DataBlob{ EncodingType: shared.EncodingTypeJSON.Ptr(), Data: []byte("test-data"), }, }, }, want: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: enabled, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte("test-data"), }, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.want, ToAdminUpdateDomainAsyncWorkflowConfiguratonRequest(td.input)) }) } } func TestFromAdminUpdateDomainAsyncWorkflowConfiguratonResponse(t *testing.T) { tests := map[string]struct { input *types.UpdateDomainAsyncWorkflowConfiguratonResponse want *admin.UpdateDomainAsyncWorkflowConfiguratonResponse }{ "empty": { input: &types.UpdateDomainAsyncWorkflowConfiguratonResponse{}, want: &admin.UpdateDomainAsyncWorkflowConfiguratonResponse{}, }, "nil": { input: nil, want: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.want, FromAdminUpdateDomainAsyncWorkflowConfiguratonResponse(td.input)) }) } } func TestToAdminGetDomainAsyncWorkflowConfiguratonRequest(t *testing.T) { tests := map[string]struct { input *admin.GetDomainAsyncWorkflowConfiguratonRequest want *types.GetDomainAsyncWorkflowConfiguratonRequest }{ "empty": { input: &admin.GetDomainAsyncWorkflowConfiguratonRequest{}, want: &types.GetDomainAsyncWorkflowConfiguratonRequest{}, }, "nil": { input: nil, want: nil, }, "valid": { input: &admin.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: strPtr("test-domain"), }, want: &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.want, ToAdminGetDomainAsyncWorkflowConfiguratonRequest(td.input)) }) } } func TestFromAdminGetDomainAsyncWorkflowConfiguratonResponse(t *testing.T) { enabled := true tests := map[string]struct { input *types.GetDomainAsyncWorkflowConfiguratonResponse want *admin.GetDomainAsyncWorkflowConfiguratonResponse }{ "empty": { input: &types.GetDomainAsyncWorkflowConfiguratonResponse{}, want: &admin.GetDomainAsyncWorkflowConfiguratonResponse{}, }, "nil": { input: nil, want: nil, }, "predefined queue": { input: &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &types.AsyncWorkflowConfiguration{ Enabled: enabled, PredefinedQueueName: "test-queue", }, }, want: &admin.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &shared.AsyncWorkflowConfiguration{ Enabled: &enabled, PredefinedQueueName: strPtr("test-queue"), QueueType: strPtr(""), }, }, }, "inline queue": { input: &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &types.AsyncWorkflowConfiguration{ Enabled: enabled, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte("test-data"), }, }, }, want: &admin.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &shared.AsyncWorkflowConfiguration{ Enabled: &enabled, PredefinedQueueName: strPtr(""), QueueType: strPtr("kafka"), QueueConfig: &shared.DataBlob{ EncodingType: shared.EncodingTypeJSON.Ptr(), Data: []byte("test-data"), }, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.want, FromAdminGetDomainAsyncWorkflowConfiguratonResponse(td.input)) }) } } func TestFromAdminGetDomainAsyncWorkflowConfiguratonRequest(t *testing.T) { tests := map[string]struct { input *types.GetDomainAsyncWorkflowConfiguratonRequest want *admin.GetDomainAsyncWorkflowConfiguratonRequest }{ "empty": { input: &types.GetDomainAsyncWorkflowConfiguratonRequest{}, want: &admin.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: strPtr(""), }, }, "nil": { input: nil, want: nil, }, "valid": { input: &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", }, want: &admin.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: strPtr("test-domain"), }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.want, FromAdminGetDomainAsyncWorkflowConfiguratonRequest(td.input)) }) } } func TestToAdminGetDomainAsyncWorkflowConfiguratonResponse(t *testing.T) { enabled := true tests := map[string]struct { input *admin.GetDomainAsyncWorkflowConfiguratonResponse want *types.GetDomainAsyncWorkflowConfiguratonResponse }{ "empty": { input: &admin.GetDomainAsyncWorkflowConfiguratonResponse{}, want: &types.GetDomainAsyncWorkflowConfiguratonResponse{}, }, "nil": { input: nil, want: nil, }, "predefined queue": { input: &admin.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &shared.AsyncWorkflowConfiguration{ Enabled: &enabled, PredefinedQueueName: strPtr("test-queue"), }, }, want: &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &types.AsyncWorkflowConfiguration{ Enabled: enabled, PredefinedQueueName: "test-queue", }, }, }, "inline queue": { input: &admin.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &shared.AsyncWorkflowConfiguration{ Enabled: &enabled, QueueType: strPtr("kafka"), QueueConfig: &shared.DataBlob{ EncodingType: shared.EncodingTypeJSON.Ptr(), Data: []byte("test-data"), }, }, }, want: &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &types.AsyncWorkflowConfiguration{ Enabled: enabled, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte("test-data"), }, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.want, ToAdminGetDomainAsyncWorkflowConfiguratonResponse(td.input)) }) } } func TestFromAdminUpdateDomainAsyncWorkflowConfiguratonRequest(t *testing.T) { enabled := true tests := map[string]struct { input *types.UpdateDomainAsyncWorkflowConfiguratonRequest want *admin.UpdateDomainAsyncWorkflowConfiguratonRequest }{ "empty": { input: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{}, want: &admin.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: strPtr(""), }, }, "nil": { input: nil, want: nil, }, "predefined queue": { input: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: enabled, PredefinedQueueName: "test-queue", }, }, want: &admin.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: strPtr("test-domain"), Configuration: &shared.AsyncWorkflowConfiguration{ Enabled: &enabled, PredefinedQueueName: strPtr("test-queue"), QueueType: strPtr(""), }, }, }, "inline queue": { input: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: "test-domain", Configuration: &types.AsyncWorkflowConfiguration{ Enabled: enabled, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte("test-data"), }, }, }, want: &admin.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: strPtr("test-domain"), Configuration: &shared.AsyncWorkflowConfiguration{ Enabled: &enabled, PredefinedQueueName: strPtr(""), QueueType: strPtr("kafka"), QueueConfig: &shared.DataBlob{ EncodingType: shared.EncodingTypeJSON.Ptr(), Data: []byte("test-data"), }, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.want, FromAdminUpdateDomainAsyncWorkflowConfiguratonRequest(td.input)) }) } } func TestToAdminUpdateDomainAsyncWorkflowConfiguratonResponse(t *testing.T) { tests := map[string]struct { input *admin.UpdateDomainAsyncWorkflowConfiguratonResponse want *types.UpdateDomainAsyncWorkflowConfiguratonResponse }{ "empty": { input: &admin.UpdateDomainAsyncWorkflowConfiguratonResponse{}, want: &types.UpdateDomainAsyncWorkflowConfiguratonResponse{}, }, "nil": { input: nil, want: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, td.want, ToAdminUpdateDomainAsyncWorkflowConfiguratonResponse(td.input)) }) } } ================================================ FILE: common/types/mapper/thrift/any.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "bytes" "fmt" "go.uber.org/thriftrw/protocol/binary" "go.uber.org/thriftrw/wire" ) // any.go contains Any-helpers for encoding/decoding thrift data // ThriftObject is satisfied by any thrift type, as they all have To/FromWire methods. // go.uber.org/cadence has exactly this in an internal API, it's just copied here for simplicity. type ThriftObject interface { FromWire(w wire.Value) error ToWire() (wire.Value, error) } func EncodeToBytes(obj ThriftObject) ([]byte, error) { // get the intermediate format, ready for byte-encoding wireValue, err := obj.ToWire() if err != nil { return nil, fmt.Errorf("failed to produce intermediate wire.Value from type %T", obj) } // write to binary var writer bytes.Buffer err = binary.Default.Encode(wireValue, &writer) if err != nil { return nil, fmt.Errorf("failed to encode wire.Value from type %T", obj) } // and return the bytes return writer.Bytes(), nil } func DecodeStructFromBytes(data []byte, target ThriftObject) error { // decode to the intermediate format, like encoding. // all top-most types likely *should* be a wire.TStruct, but if Any is at some point used // to contain a map/int/etc, just make sure to use wire.TMap / wire.TI64 / etc as is appropriate. intermediate, err := binary.Default.Decode(bytes.NewReader(data), wire.TStruct) if err != nil { return fmt.Errorf("could not decode thrift bytes to intermediate wire.Value for type %T", target) } // and populate the target with the wire.Value contents err = target.FromWire(intermediate) if err != nil { return fmt.Errorf("could not FromWire to the target type %T", target) } return nil } ================================================ FILE: common/types/mapper/thrift/any_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" ) func TestThriftInAny(t *testing.T) { // pushing thrift or proto data through this type is very much expected, // so this is both evidence that it's possible and an example for how to do so. // // note that there is an equivalent test in mapper/proto/shared_test.go. // they are structurally the same, but encoding/decoding details vary a bit. // --- create the original data, a thrift object, and encode it by hand orig := &shared.WorkflowExecution{ WorkflowId: common.StringPtr(testdata.WorkflowID), RunId: common.StringPtr(testdata.RunID), } internalBytes, err := EncodeToBytes(orig) require.NoError(t, err) // --- put that data into the custom Any type // thrift has no registry like proto has, so there is no canonical name. // no problem, just generate one. var typeName = reflect.TypeOf(orig).PkgPath() + reflect.TypeOf(orig).Name() anyVal := &types.Any{ ValueType: typeName, Value: internalBytes, // store thrift bytes in the Any } // --- convert the whole container to thrift (mimics making a call via yarpc) thriftAny := FromAny(anyVal) // we map to the rpc type networkBytes, err := EncodeToBytes(thriftAny) // yarpc does this require.NoError(t, err) // ^ this is what's sent over the network. // as a side note: // the final data is not double-encoded, so this "encode -> wrap -> encode" process is reasonably efficient. // // Thrift and Proto can efficiently move around binary blobs like this, as it's essentially just a memcpy between // the input and the output, and there's no `\0` to `\\0` escaping or base64 encoding or whatever needed. // // no behavior depends on this, it's just presented here as evidence that this Any-wrapper does not meaningfully // change any RPC design concerns: anything you would do with normal RPC can be done through an Any if you need // loose typing, the change-stability / performance / etc is entirely unaffected. // // compare via a sliding window to find the place it overlaps, to prove that this is true: found := false for i := 0; i <= len(networkBytes)-len(internalBytes); i++ { if reflect.DeepEqual(internalBytes, networkBytes[i:i+len(internalBytes)]) { found = true t.Logf("Found matching bytes at index %v", i) // currently at index 14 } } // *should* be true for efficiency's sake, but is not truly necessary for correct behavior assert.Truef(t, found, "did not find internal bytes within network bytes, might be paying double-encoding costs:\n\tinternal: %v\n\tnetwork: %v", internalBytes, networkBytes) // --- the network pushes the data to a new location --- // --- on the receiving side, we map to internal types like normal var outAny shared.Any err = DecodeStructFromBytes(networkBytes, &outAny) // yarpc does this require.NoError(t, err) outAnyVal := ToAny(&outAny) // we map to internal types // --- and finally decode the any-typed data by hand require.Equal(t, typeName, outAnyVal.ValueType, "type name through RPC should match the original type name") var outOrig shared.WorkflowExecution // selected based on the ValueType contents err = DecodeStructFromBytes(outAnyVal.Value, &outOrig) // do the actual custom decoding require.NoError(t, err) assert.NotEmpty(t, outOrig, "sanity check, decoded value should not be empty") assert.Equal(t, orig, &outOrig, "final round-tripped Any-contained data should be identical to the original object") } ================================================ FILE: common/types/mapper/thrift/configStore.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "github.com/uber/cadence/.gen/go/config" "github.com/uber/cadence/common/types" ) // FromDynamicConfigBlob converts internal DynamicConfigBlob type to thrift func FromDynamicConfigBlob(t *types.DynamicConfigBlob) *config.DynamicConfigBlob { if t == nil { return nil } return &config.DynamicConfigBlob{ SchemaVersion: &t.SchemaVersion, Entries: FromDynamicConfigEntryArray(t.Entries), } } // ToDynamicConfigBlob converts thrift DynamicConfigBlob type to internal func ToDynamicConfigBlob(t *config.DynamicConfigBlob) *types.DynamicConfigBlob { if t == nil { return nil } return &types.DynamicConfigBlob{ SchemaVersion: t.GetSchemaVersion(), Entries: ToDynamicConfigEntryArray(t.Entries), } } // FromDynamicConfigEntryArray converts internal DynamicConfigEntry array type to thrift func FromDynamicConfigEntryArray(t []*types.DynamicConfigEntry) []*config.DynamicConfigEntry { if t == nil { return nil } v := make([]*config.DynamicConfigEntry, len(t)) for i := range t { v[i] = FromDynamicConfigEntry(t[i]) } return v } // ToDynamicConfigEntryArray converts thrift DynamicConfigEntry array type to internal func ToDynamicConfigEntryArray(t []*config.DynamicConfigEntry) []*types.DynamicConfigEntry { if t == nil { return nil } v := make([]*types.DynamicConfigEntry, len(t)) for i := range t { v[i] = ToDynamicConfigEntry(t[i]) } return v } // FromDynamicConfigEntry converts internal DynamicConfigEntry type to thrift func FromDynamicConfigEntry(t *types.DynamicConfigEntry) *config.DynamicConfigEntry { if t == nil { return nil } return &config.DynamicConfigEntry{ Name: &t.Name, Values: FromDynamicConfigValueArray(t.Values), } } // ToDynamicConfigEntry converts thrift DynamicConfigEntry type to internal func ToDynamicConfigEntry(t *config.DynamicConfigEntry) *types.DynamicConfigEntry { if t == nil { return nil } return &types.DynamicConfigEntry{ Name: t.GetName(), Values: ToDynamicConfigValueArray(t.Values), } } // FromDynamicConfigValueArray converts internal DynamicConfigValue array type to thrift func FromDynamicConfigValueArray(t []*types.DynamicConfigValue) []*config.DynamicConfigValue { if t == nil { return nil } v := make([]*config.DynamicConfigValue, len(t)) for i := range t { v[i] = FromDynamicConfigValue(t[i]) } return v } // ToDynamicConfigValueArray converts thrift DynamicConfigValue array type to internal func ToDynamicConfigValueArray(t []*config.DynamicConfigValue) []*types.DynamicConfigValue { if t == nil { return nil } v := make([]*types.DynamicConfigValue, len(t)) for i := range t { v[i] = ToDynamicConfigValue(t[i]) } return v } // FromDynamicConfigValue converts internal DynamicConfigValue type to thrift func FromDynamicConfigValue(t *types.DynamicConfigValue) *config.DynamicConfigValue { if t == nil { return nil } return &config.DynamicConfigValue{ Value: FromDataBlob(t.Value), Filters: FromDynamicConfigFilterArray(t.Filters), } } // ToDynamicConfigValue converts thrift DynamicConfigValue type to internal func ToDynamicConfigValue(t *config.DynamicConfigValue) *types.DynamicConfigValue { if t == nil { return nil } return &types.DynamicConfigValue{ Value: ToDataBlob(t.Value), Filters: ToDynamicConfigFilterArray(t.Filters), } } // FromDynamicConfigFilterArray converts internal DynamicConfigFilter array type to thrift func FromDynamicConfigFilterArray(t []*types.DynamicConfigFilter) []*config.DynamicConfigFilter { if t == nil { return nil } v := make([]*config.DynamicConfigFilter, len(t)) for i := range t { v[i] = FromDynamicConfigFilter(t[i]) } return v } // ToDynamicConfigFilterArray converts thrift DynamicConfigFilter array type to internal func ToDynamicConfigFilterArray(t []*config.DynamicConfigFilter) []*types.DynamicConfigFilter { if t == nil { return nil } v := make([]*types.DynamicConfigFilter, len(t)) for i := range t { v[i] = ToDynamicConfigFilter(t[i]) } return v } // FromDynamicConfigFilter converts internal DynamicConfigFilter type to thrift func FromDynamicConfigFilter(t *types.DynamicConfigFilter) *config.DynamicConfigFilter { if t == nil { return nil } return &config.DynamicConfigFilter{ Name: &t.Name, Value: FromDataBlob(t.Value), } } // ToDynamicConfigFilter converts thrift DynamicConfigFilter type to internal func ToDynamicConfigFilter(t *config.DynamicConfigFilter) *types.DynamicConfigFilter { if t == nil { return nil } return &types.DynamicConfigFilter{ Name: t.GetName(), Value: ToDataBlob(t.Value), } } ================================================ FILE: common/types/mapper/thrift/config_store_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" ) func TestDynamicConfigBlob(t *testing.T) { testCases := []struct { desc string input *types.DynamicConfigBlob }{ { desc: "non-nil input test", input: &testdata.DynamicConfigBlob, }, { desc: "empty input test", input: &types.DynamicConfigBlob{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromDynamicConfigBlob(tc.input) roundTripObj := ToDynamicConfigBlob(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestDynamicConfigEntryArray(t *testing.T) { testCase := []struct { desc string input []*types.DynamicConfigEntry }{ { desc: "non-nil input test", input: []*types.DynamicConfigEntry{&testdata.DynamicConfigEntry}, }, { desc: "empty input test", input: []*types.DynamicConfigEntry{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCase { thriftObj := FromDynamicConfigEntryArray(tc.input) roundTripObj := ToDynamicConfigEntryArray(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestDynamicConfigEntry(t *testing.T) { testCases := []struct { desc string input *types.DynamicConfigEntry }{ { desc: "non-nil input test", input: &testdata.DynamicConfigEntry, }, { desc: "empty input test", input: &types.DynamicConfigEntry{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromDynamicConfigEntry(tc.input) roundTripObj := ToDynamicConfigEntry(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestDynamicConfigValueArray(t *testing.T) { testCases := []struct { desc string input []*types.DynamicConfigValue }{ { desc: "non-nil input test", input: []*types.DynamicConfigValue{&testdata.DynamicConfigValue}, }, { desc: "empty input test", input: []*types.DynamicConfigValue{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromDynamicConfigValueArray(tc.input) roundTripObj := ToDynamicConfigValueArray(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestDynamicConfigValue(t *testing.T) { testCases := []struct { desc string input *types.DynamicConfigValue }{ { desc: "non-nil input test", input: &testdata.DynamicConfigValue, }, { desc: "empty input test", input: &types.DynamicConfigValue{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromDynamicConfigValue(tc.input) roundTripObj := ToDynamicConfigValue(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestDynamicConfigFilterArray(t *testing.T) { testCases := []struct { desc string input []*types.DynamicConfigFilter }{ { desc: "non-nil input test", input: []*types.DynamicConfigFilter{&testdata.DynamicConfigFilter}, }, { desc: "empty input test", input: []*types.DynamicConfigFilter{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromDynamicConfigFilterArray(tc.input) roundTripObj := ToDynamicConfigFilterArray(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestDynamicConfigFilter(t *testing.T) { testCases := []struct { desc string input *types.DynamicConfigFilter }{ { desc: "non-nil input test", input: &testdata.DynamicConfigFilter, }, { desc: "empty input test", input: &types.DynamicConfigFilter{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromDynamicConfigFilter(tc.input) roundTripObj := ToDynamicConfigFilter(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } ================================================ FILE: common/types/mapper/thrift/errors.go ================================================ // Copyright (c) 2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "github.com/uber/cadence/common/types/mapper/errorutils" ) // FromError convert error to Thrift type if it comes as its internal equivalent func FromError(err error) error { if err == nil { return nil } var ( ok bool typedErr error ) if ok, typedErr = errorutils.ConvertError(err, FromAccessDeniedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromBadRequestError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromCancellationAlreadyRequestedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromClientVersionNotSupportedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromFeatureNotEnabledError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromCurrentBranchChangedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromDomainAlreadyExistsError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromDomainNotActiveError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromEntityNotExistsError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromWorkflowExecutionAlreadyCompletedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromInternalDataInconsistencyError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromInternalServiceError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromLimitExceededError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromQueryFailedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromRemoteSyncMatchedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromRetryTaskV2Error); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromServiceBusyError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromWorkflowExecutionAlreadyStartedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromShardOwnershipLostError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromEventAlreadyStartedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromStickyWorkerUnavailableError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, FromTaskListNotOwnedByHostError); ok { return typedErr } return err } // ToError convert error to internal type if it comes as its thrift equivalent func ToError(err error) error { if err == nil { return nil } var ( ok bool typedErr error ) if ok, typedErr = errorutils.ConvertError(err, ToAccessDeniedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToBadRequestError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToCancellationAlreadyRequestedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToClientVersionNotSupportedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToFeatureNotEnabledError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToCurrentBranchChangedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToDomainAlreadyExistsError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToDomainNotActiveError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToEntityNotExistsError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToWorkflowExecutionAlreadyCompletedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToInternalDataInconsistencyError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToInternalServiceError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToLimitExceededError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToQueryFailedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToRemoteSyncMatchedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToRetryTaskV2Error); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToServiceBusyError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToWorkflowExecutionAlreadyStartedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToShardOwnershipLostError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToEventAlreadyStartedError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToStickyWorkerUnavailableError); ok { return typedErr } else if ok, typedErr = errorutils.ConvertError(err, ToTaskListNotOwnedByHostError); ok { return typedErr } return err } ================================================ FILE: common/types/mapper/thrift/errors_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "errors" "reflect" "testing" "github.com/stretchr/testify/assert" "go.uber.org/yarpc/yarpcerrors" "github.com/uber/cadence/common/types/testdata" ) func TestErrors(t *testing.T) { for _, err := range testdata.Errors { name := reflect.TypeOf(err).Elem().Name() t.Run(name, func(t *testing.T) { // Test that the mappings does not lose information assert.Equal(t, err, ToError(FromError(err))) }) } } func TestNilMapsToNil(t *testing.T) { assert.Nil(t, FromError(nil)) assert.Nil(t, ToError(nil)) } func TestFromUnknownErrorMapsToItself(t *testing.T) { err := errors.New("unknown error") assert.Equal(t, err, FromError(err)) } func TestToUnknownErrorMapsToItself(t *testing.T) { err := yarpcerrors.DeadlineExceededErrorf("timeout") assert.Equal(t, err, ToError(err)) } ================================================ FILE: common/types/mapper/thrift/health.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "github.com/uber/cadence/.gen/go/health" "github.com/uber/cadence/common/types" ) // FromHealthStatus converts internal HealthStatus type to thrift func FromHealthStatus(t *types.HealthStatus) *health.HealthStatus { if t == nil { return nil } return &health.HealthStatus{ Ok: t.Ok, Msg: &t.Msg, } } // ToHealthStatus converts thrift HealthStatus type to internal func ToHealthStatus(t *health.HealthStatus) *types.HealthStatus { if t == nil { return nil } return &types.HealthStatus{ Ok: t.Ok, Msg: t.GetMsg(), } } ================================================ FILE: common/types/mapper/thrift/helpers.go ================================================ // Copyright (c) 2017-2022 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "time" "github.com/uber/cadence/common" ) func timeToNano(t *time.Time) *int64 { if t == nil { return nil } return common.Int64Ptr(t.UnixNano()) } func nanoToTime(nanos *int64) *time.Time { if nanos == nil { return nil } result := time.Unix(0, *nanos) return &result } ================================================ FILE: common/types/mapper/thrift/helpers_test.go ================================================ // Copyright (c) 2017-2022 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestTimeToNano(t *testing.T) { unixTime := time.Unix(1, 1) result := timeToNano(&unixTime) assert.Equal(t, int64(1000000001), *result) } func TestTimeToNanoNil(t *testing.T) { result := timeToNano(nil) assert.Nil(t, result) } func TestNanoToTime(t *testing.T) { nanos := int64(1000000001) result := nanoToTime(&nanos) assert.True(t, time.Unix(1, 1).Equal(*result)) } func TestNanoToTimeNil(t *testing.T) { result := nanoToTime(nil) assert.Nil(t, result) } ================================================ FILE: common/types/mapper/thrift/history.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "github.com/uber/cadence/.gen/go/history" "github.com/uber/cadence/common/types" ) var ( FromHistoryDescribeHistoryHostRequest = FromAdminDescribeHistoryHostRequest ToHistoryDescribeHistoryHostRequest = ToAdminDescribeHistoryHostRequest FromHistoryDescribeHistoryHostResponse = FromAdminDescribeHistoryHostResponse ToHistoryDescribeHistoryHostResponse = ToAdminDescribeHistoryHostResponse FromHistoryCloseShardRequest = FromAdminCloseShardRequest ToHistoryCloseShardRequest = ToAdminCloseShardRequest FromHistoryDescribeQueueRequest = FromAdminDescribeQueueRequest ToHistoryDescribeQueueRequest = ToAdminDescribeQueueRequest FromHistoryDescribeQueueResponse = FromAdminDescribeQueueResponse ToHistoryDescribeQueueResponse = ToAdminDescribeQueueResponse FromHistoryDescribeWorkflowExecutionResponse = FromDescribeWorkflowExecutionResponse ToHistoryDescribeWorkflowExecutionResponse = ToDescribeWorkflowExecutionResponse FromHistoryGetCrossClusterTasksRequest = FromAdminGetCrossClusterTasksRequest ToHistoryGetCrossClusterTasksRequest = ToAdminGetCrossClusterTasksRequest FromHistoryGetCrossClusterTasksResponse = FromAdminGetCrossClusterTasksResponse ToHistoryGetCrossClusterTasksResponse = ToAdminGetCrossClusterTasksResponse FromHistoryGetDLQReplicationMessagesRequest = FromAdminGetDLQReplicationMessagesRequest ToHistoryGetDLQReplicationMessagesRequest = ToAdminGetDLQReplicationMessagesRequest FromHistoryGetDLQReplicationMessagesResponse = FromAdminGetDLQReplicationMessagesResponse ToHistoryGetDLQReplicationMessagesResponse = ToAdminGetDLQReplicationMessagesResponse FromHistoryGetFailoverInfoRequest = FromGetFailoverInfoRequest ToHistoryGetFailoverInfoRequest = ToGetFailoverInfoRequest FromHistoryGetFailoverInfoResponse = FromGetFailoverInfoResponse ToHistoryGetFailoverInfoResponse = ToGetFailoverInfoResponse FromHistoryGetReplicationMessagesRequest = FromAdminGetReplicationMessagesRequest ToHistoryGetReplicationMessagesRequest = ToAdminGetReplicationMessagesRequest FromHistoryGetReplicationMessagesResponse = FromAdminGetReplicationMessagesResponse ToHistoryGetReplicationMessagesResponse = ToAdminGetReplicationMessagesResponse FromHistoryMergeDLQMessagesRequest = FromAdminMergeDLQMessagesRequest ToHistoryMergeDLQMessagesRequest = ToAdminMergeDLQMessagesRequest FromHistoryMergeDLQMessagesResponse = FromAdminMergeDLQMessagesResponse ToHistoryMergeDLQMessagesResponse = ToAdminMergeDLQMessagesResponse FromHistoryPurgeDLQMessagesRequest = FromAdminPurgeDLQMessagesRequest ToHistoryPurgeDLQMessagesRequest = ToAdminPurgeDLQMessagesRequest FromHistoryReadDLQMessagesRequest = FromAdminReadDLQMessagesRequest ToHistoryReadDLQMessagesRequest = ToAdminReadDLQMessagesRequest FromHistoryReadDLQMessagesResponse = FromAdminReadDLQMessagesResponse ToHistoryReadDLQMessagesResponse = ToAdminReadDLQMessagesResponse FromHistoryRecordActivityTaskHeartbeatResponse = FromRecordActivityTaskHeartbeatResponse ToHistoryRecordActivityTaskHeartbeatResponse = ToRecordActivityTaskHeartbeatResponse FromHistoryRecordActivityTaskStartedRequest = FromRecordActivityTaskStartedRequest ToHistoryRecordActivityTaskStartedRequest = ToRecordActivityTaskStartedRequest FromHistoryRecordActivityTaskStartedResponse = FromRecordActivityTaskStartedResponse ToHistoryRecordActivityTaskStartedResponse = ToRecordActivityTaskStartedResponse FromHistoryRecordChildExecutionCompletedRequest = FromRecordChildExecutionCompletedRequest ToHistoryRecordChildExecutionCompletedRequest = ToRecordChildExecutionCompletedRequest FromHistoryRecordDecisionTaskStartedRequest = FromRecordDecisionTaskStartedRequest ToHistoryRecordDecisionTaskStartedRequest = ToRecordDecisionTaskStartedRequest FromHistoryRecordDecisionTaskStartedResponse = FromRecordDecisionTaskStartedResponse ToHistoryRecordDecisionTaskStartedResponse = ToRecordDecisionTaskStartedResponse FromHistoryRemoveTaskRequest = FromAdminRemoveTaskRequest ToHistoryRemoveTaskRequest = ToAdminRemoveTaskRequest FromHistoryResetQueueRequest = FromAdminResetQueueRequest ToHistoryResetQueueRequest = ToAdminResetQueueRequest FromHistoryResetWorkflowExecutionResponse = FromResetWorkflowExecutionResponse ToHistoryResetWorkflowExecutionResponse = ToResetWorkflowExecutionResponse FromHistoryRespondCrossClusterTasksCompletedRequest = FromAdminRespondCrossClusterTasksCompletedRequest ToHistoryRespondCrossClusterTasksCompletedRequest = ToAdminRespondCrossClusterTasksCompletedRequest FromHistoryRespondCrossClusterTasksCompletedResponse = FromAdminRespondCrossClusterTasksCompletedResponse ToHistoryRespondCrossClusterTasksCompletedResponse = ToAdminRespondCrossClusterTasksCompletedResponse FromHistorySignalWithStartWorkflowExecutionResponse = FromStartWorkflowExecutionResponse ToHistorySignalWithStartWorkflowExecutionResponse = ToStartWorkflowExecutionResponse FromHistoryStartWorkflowExecutionResponse = FromStartWorkflowExecutionResponse ToHistoryStartWorkflowExecutionResponse = ToStartWorkflowExecutionResponse ) // FromHistoryDescribeMutableStateRequest converts internal DescribeMutableStateRequest type to thrift func FromHistoryDescribeMutableStateRequest(t *types.DescribeMutableStateRequest) *history.DescribeMutableStateRequest { if t == nil { return nil } return &history.DescribeMutableStateRequest{ DomainUUID: &t.DomainUUID, Execution: FromWorkflowExecution(t.Execution), } } // ToHistoryDescribeMutableStateRequest converts thrift DescribeMutableStateRequest type to internal func ToHistoryDescribeMutableStateRequest(t *history.DescribeMutableStateRequest) *types.DescribeMutableStateRequest { if t == nil { return nil } return &types.DescribeMutableStateRequest{ DomainUUID: t.GetDomainUUID(), Execution: ToWorkflowExecution(t.Execution), } } // FromHistoryDescribeMutableStateResponse converts internal DescribeMutableStateResponse type to thrift func FromHistoryDescribeMutableStateResponse(t *types.DescribeMutableStateResponse) *history.DescribeMutableStateResponse { if t == nil { return nil } return &history.DescribeMutableStateResponse{ MutableStateInCache: &t.MutableStateInCache, MutableStateInDatabase: &t.MutableStateInDatabase, } } // ToHistoryDescribeMutableStateResponse converts thrift DescribeMutableStateResponse type to internal func ToHistoryDescribeMutableStateResponse(t *history.DescribeMutableStateResponse) *types.DescribeMutableStateResponse { if t == nil { return nil } return &types.DescribeMutableStateResponse{ MutableStateInCache: t.GetMutableStateInCache(), MutableStateInDatabase: t.GetMutableStateInDatabase(), } } // FromHistoryDescribeWorkflowExecutionRequest converts internal DescribeWorkflowExecutionRequest type to thrift func FromHistoryDescribeWorkflowExecutionRequest(t *types.HistoryDescribeWorkflowExecutionRequest) *history.DescribeWorkflowExecutionRequest { if t == nil { return nil } return &history.DescribeWorkflowExecutionRequest{ DomainUUID: &t.DomainUUID, Request: FromDescribeWorkflowExecutionRequest(t.Request), } } // ToHistoryDescribeWorkflowExecutionRequest converts thrift DescribeWorkflowExecutionRequest type to internal func ToHistoryDescribeWorkflowExecutionRequest(t *history.DescribeWorkflowExecutionRequest) *types.HistoryDescribeWorkflowExecutionRequest { if t == nil { return nil } return &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: t.GetDomainUUID(), Request: ToDescribeWorkflowExecutionRequest(t.Request), } } // FromDomainFilter converts internal DomainFilter type to thrift func FromDomainFilter(t *types.DomainFilter) *history.DomainFilter { if t == nil { return nil } return &history.DomainFilter{ DomainIDs: t.DomainIDs, ReverseMatch: &t.ReverseMatch, } } // ToDomainFilter converts thrift DomainFilter type to internal func ToDomainFilter(t *history.DomainFilter) *types.DomainFilter { if t == nil { return nil } return &types.DomainFilter{ DomainIDs: t.DomainIDs, ReverseMatch: t.GetReverseMatch(), } } // FromEventAlreadyStartedError converts internal EventAlreadyStartedError type to thrift func FromEventAlreadyStartedError(t *types.EventAlreadyStartedError) *history.EventAlreadyStartedError { if t == nil { return nil } return &history.EventAlreadyStartedError{ Message: t.Message, } } // ToEventAlreadyStartedError converts thrift EventAlreadyStartedError type to internal func ToEventAlreadyStartedError(t *history.EventAlreadyStartedError) *types.EventAlreadyStartedError { if t == nil { return nil } return &types.EventAlreadyStartedError{ Message: t.Message, } } // FromFailoverMarkerToken converts internal FailoverMarkerToken type to thrift func FromFailoverMarkerToken(t *types.FailoverMarkerToken) *history.FailoverMarkerToken { if t == nil { return nil } return &history.FailoverMarkerToken{ ShardIDs: t.ShardIDs, FailoverMarker: FromFailoverMarkerAttributes(t.FailoverMarker), } } // ToFailoverMarkerToken converts thrift FailoverMarkerToken type to internal func ToFailoverMarkerToken(t *history.FailoverMarkerToken) *types.FailoverMarkerToken { if t == nil { return nil } return &types.FailoverMarkerToken{ ShardIDs: t.ShardIDs, FailoverMarker: ToFailoverMarkerAttributes(t.FailoverMarker), } } // FromHistoryGetMutableStateRequest converts internal GetMutableStateRequest type to thrift func FromHistoryGetMutableStateRequest(t *types.GetMutableStateRequest) *history.GetMutableStateRequest { if t == nil { return nil } return &history.GetMutableStateRequest{ DomainUUID: &t.DomainUUID, Execution: FromWorkflowExecution(t.Execution), ExpectedNextEventId: &t.ExpectedNextEventID, CurrentBranchToken: t.CurrentBranchToken, VersionHistoryItem: FromVersionHistoryItem(t.VersionHistoryItem), } } // ToHistoryGetMutableStateRequest converts thrift GetMutableStateRequest type to internal func ToHistoryGetMutableStateRequest(t *history.GetMutableStateRequest) *types.GetMutableStateRequest { if t == nil { return nil } return &types.GetMutableStateRequest{ DomainUUID: t.GetDomainUUID(), Execution: ToWorkflowExecution(t.Execution), ExpectedNextEventID: t.GetExpectedNextEventId(), CurrentBranchToken: t.CurrentBranchToken, VersionHistoryItem: ToVersionHistoryItem(t.VersionHistoryItem), } } // FromHistoryGetMutableStateResponse converts internal GetMutableStateResponse type to thrift func FromHistoryGetMutableStateResponse(t *types.GetMutableStateResponse) *history.GetMutableStateResponse { if t == nil { return nil } return &history.GetMutableStateResponse{ Execution: FromWorkflowExecution(t.Execution), WorkflowType: FromWorkflowType(t.WorkflowType), NextEventId: &t.NextEventID, PreviousStartedEventId: t.PreviousStartedEventID, LastFirstEventId: &t.LastFirstEventID, TaskList: FromTaskList(t.TaskList), StickyTaskList: FromTaskList(t.StickyTaskList), ClientLibraryVersion: &t.ClientLibraryVersion, ClientFeatureVersion: &t.ClientFeatureVersion, ClientImpl: &t.ClientImpl, IsWorkflowRunning: &t.IsWorkflowRunning, StickyTaskListScheduleToStartTimeout: t.StickyTaskListScheduleToStartTimeout, EventStoreVersion: &t.EventStoreVersion, CurrentBranchToken: t.CurrentBranchToken, WorkflowState: t.WorkflowState, WorkflowCloseState: t.WorkflowCloseState, VersionHistories: FromVersionHistories(t.VersionHistories), IsStickyTaskListEnabled: &t.IsStickyTaskListEnabled, HistorySize: &t.HistorySize, } } // ToHistoryGetMutableStateResponse converts thrift GetMutableStateResponse type to internal func ToHistoryGetMutableStateResponse(t *history.GetMutableStateResponse) *types.GetMutableStateResponse { if t == nil { return nil } return &types.GetMutableStateResponse{ Execution: ToWorkflowExecution(t.Execution), WorkflowType: ToWorkflowType(t.WorkflowType), NextEventID: t.GetNextEventId(), PreviousStartedEventID: t.PreviousStartedEventId, LastFirstEventID: t.GetLastFirstEventId(), TaskList: ToTaskList(t.TaskList), StickyTaskList: ToTaskList(t.StickyTaskList), ClientLibraryVersion: t.GetClientLibraryVersion(), ClientFeatureVersion: t.GetClientFeatureVersion(), ClientImpl: t.GetClientImpl(), IsWorkflowRunning: t.GetIsWorkflowRunning(), StickyTaskListScheduleToStartTimeout: t.StickyTaskListScheduleToStartTimeout, EventStoreVersion: t.GetEventStoreVersion(), CurrentBranchToken: t.CurrentBranchToken, WorkflowState: t.WorkflowState, WorkflowCloseState: t.WorkflowCloseState, VersionHistories: ToVersionHistories(t.VersionHistories), IsStickyTaskListEnabled: t.GetIsStickyTaskListEnabled(), HistorySize: t.GetHistorySize(), } } // FromHistoryNotifyFailoverMarkersRequest converts internal NotifyFailoverMarkersRequest type to thrift func FromHistoryNotifyFailoverMarkersRequest(t *types.NotifyFailoverMarkersRequest) *history.NotifyFailoverMarkersRequest { if t == nil { return nil } return &history.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: FromFailoverMarkerTokenArray(t.FailoverMarkerTokens), } } // ToHistoryNotifyFailoverMarkersRequest converts thrift NotifyFailoverMarkersRequest type to internal func ToHistoryNotifyFailoverMarkersRequest(t *history.NotifyFailoverMarkersRequest) *types.NotifyFailoverMarkersRequest { if t == nil { return nil } return &types.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: ToFailoverMarkerTokenArray(t.FailoverMarkerTokens), } } // FromHistoryParentExecutionInfo converts internal ParentExecutionInfo type to thrift func FromParentExecutionInfo(t *types.ParentExecutionInfo) *history.ParentExecutionInfo { if t == nil { return nil } return &history.ParentExecutionInfo{ DomainUUID: &t.DomainUUID, Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), InitiatedId: &t.InitiatedID, } } // ToParentExecutionInfo converts thrift ParentExecutionInfo type to internal func ToParentExecutionInfo(t *history.ParentExecutionInfo) *types.ParentExecutionInfo { if t == nil { return nil } return &types.ParentExecutionInfo{ DomainUUID: t.GetDomainUUID(), Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), InitiatedID: t.GetInitiatedId(), } } // FromHistoryPollMutableStateRequest converts internal PollMutableStateRequest type to thrift func FromHistoryPollMutableStateRequest(t *types.PollMutableStateRequest) *history.PollMutableStateRequest { if t == nil { return nil } return &history.PollMutableStateRequest{ DomainUUID: &t.DomainUUID, Execution: FromWorkflowExecution(t.Execution), ExpectedNextEventId: &t.ExpectedNextEventID, CurrentBranchToken: t.CurrentBranchToken, } } // ToHistoryPollMutableStateRequest converts thrift PollMutableStateRequest type to internal func ToHistoryPollMutableStateRequest(t *history.PollMutableStateRequest) *types.PollMutableStateRequest { if t == nil { return nil } return &types.PollMutableStateRequest{ DomainUUID: t.GetDomainUUID(), Execution: ToWorkflowExecution(t.Execution), ExpectedNextEventID: t.GetExpectedNextEventId(), CurrentBranchToken: t.CurrentBranchToken, } } // FromHistoryPollMutableStateResponse converts internal PollMutableStateResponse type to thrift func FromHistoryPollMutableStateResponse(t *types.PollMutableStateResponse) *history.PollMutableStateResponse { if t == nil { return nil } return &history.PollMutableStateResponse{ Execution: FromWorkflowExecution(t.Execution), WorkflowType: FromWorkflowType(t.WorkflowType), NextEventId: &t.NextEventID, PreviousStartedEventId: t.PreviousStartedEventID, LastFirstEventId: &t.LastFirstEventID, TaskList: FromTaskList(t.TaskList), StickyTaskList: FromTaskList(t.StickyTaskList), ClientLibraryVersion: &t.ClientLibraryVersion, ClientFeatureVersion: &t.ClientFeatureVersion, ClientImpl: &t.ClientImpl, StickyTaskListScheduleToStartTimeout: t.StickyTaskListScheduleToStartTimeout, CurrentBranchToken: t.CurrentBranchToken, VersionHistories: FromVersionHistories(t.VersionHistories), WorkflowState: t.WorkflowState, WorkflowCloseState: t.WorkflowCloseState, } } // ToHistoryPollMutableStateResponse converts thrift PollMutableStateResponse type to internal func ToHistoryPollMutableStateResponse(t *history.PollMutableStateResponse) *types.PollMutableStateResponse { if t == nil { return nil } return &types.PollMutableStateResponse{ Execution: ToWorkflowExecution(t.Execution), WorkflowType: ToWorkflowType(t.WorkflowType), NextEventID: t.GetNextEventId(), PreviousStartedEventID: t.PreviousStartedEventId, LastFirstEventID: t.GetLastFirstEventId(), TaskList: ToTaskList(t.TaskList), StickyTaskList: ToTaskList(t.StickyTaskList), ClientLibraryVersion: t.GetClientLibraryVersion(), ClientFeatureVersion: t.GetClientFeatureVersion(), ClientImpl: t.GetClientImpl(), StickyTaskListScheduleToStartTimeout: t.StickyTaskListScheduleToStartTimeout, CurrentBranchToken: t.CurrentBranchToken, VersionHistories: ToVersionHistories(t.VersionHistories), WorkflowState: t.WorkflowState, WorkflowCloseState: t.WorkflowCloseState, } } // FromProcessingQueueState converts internal ProcessingQueueState type to thrift func FromProcessingQueueState(t *types.ProcessingQueueState) *history.ProcessingQueueState { if t == nil { return nil } return &history.ProcessingQueueState{ Level: t.Level, AckLevel: t.AckLevel, MaxLevel: t.MaxLevel, DomainFilter: FromDomainFilter(t.DomainFilter), } } // ToProcessingQueueState converts thrift ProcessingQueueState type to internal func ToProcessingQueueState(t *history.ProcessingQueueState) *types.ProcessingQueueState { if t == nil { return nil } return &types.ProcessingQueueState{ Level: t.Level, AckLevel: t.AckLevel, MaxLevel: t.MaxLevel, DomainFilter: ToDomainFilter(t.DomainFilter), } } // FromProcessingQueueStates converts internal ProcessingQueueStates type to thrift func FromProcessingQueueStates(t *types.ProcessingQueueStates) *history.ProcessingQueueStates { if t == nil { return nil } return &history.ProcessingQueueStates{ StatesByCluster: FromProcessingQueueStateArrayMap(t.StatesByCluster), } } // ToProcessingQueueStates converts thrift ProcessingQueueStates type to internal func ToProcessingQueueStates(t *history.ProcessingQueueStates) *types.ProcessingQueueStates { if t == nil { return nil } return &types.ProcessingQueueStates{ StatesByCluster: ToProcessingQueueStateArrayMap(t.StatesByCluster), } } // FromHistoryQueryWorkflowRequest converts internal QueryWorkflowRequest type to thrift func FromHistoryQueryWorkflowRequest(t *types.HistoryQueryWorkflowRequest) *history.QueryWorkflowRequest { if t == nil { return nil } return &history.QueryWorkflowRequest{ DomainUUID: &t.DomainUUID, Request: FromQueryWorkflowRequest(t.Request), } } // ToHistoryQueryWorkflowRequest converts thrift QueryWorkflowRequest type to internal func ToHistoryQueryWorkflowRequest(t *history.QueryWorkflowRequest) *types.HistoryQueryWorkflowRequest { if t == nil { return nil } return &types.HistoryQueryWorkflowRequest{ DomainUUID: t.GetDomainUUID(), Request: ToQueryWorkflowRequest(t.Request), } } // FromHistoryQueryWorkflowResponse converts internal QueryWorkflowResponse type to thrift func FromHistoryQueryWorkflowResponse(t *types.HistoryQueryWorkflowResponse) *history.QueryWorkflowResponse { if t == nil { return nil } return &history.QueryWorkflowResponse{ Response: FromQueryWorkflowResponse(t.Response), } } // ToHistoryQueryWorkflowResponse converts thrift QueryWorkflowResponse type to internal func ToHistoryQueryWorkflowResponse(t *history.QueryWorkflowResponse) *types.HistoryQueryWorkflowResponse { if t == nil { return nil } return &types.HistoryQueryWorkflowResponse{ Response: ToQueryWorkflowResponse(t.Response), } } // FromHistoryReapplyEventsRequest converts internal ReapplyEventsRequest type to thrift func FromHistoryReapplyEventsRequest(t *types.HistoryReapplyEventsRequest) *history.ReapplyEventsRequest { if t == nil { return nil } return &history.ReapplyEventsRequest{ DomainUUID: &t.DomainUUID, Request: FromAdminReapplyEventsRequest(t.Request), } } // ToHistoryReapplyEventsRequest converts thrift ReapplyEventsRequest type to internal func ToHistoryReapplyEventsRequest(t *history.ReapplyEventsRequest) *types.HistoryReapplyEventsRequest { if t == nil { return nil } return &types.HistoryReapplyEventsRequest{ DomainUUID: t.GetDomainUUID(), Request: ToAdminReapplyEventsRequest(t.Request), } } // FromHistoryRecordActivityTaskHeartbeatRequest converts internal RecordActivityTaskHeartbeatRequest type to thrift func FromHistoryRecordActivityTaskHeartbeatRequest(t *types.HistoryRecordActivityTaskHeartbeatRequest) *history.RecordActivityTaskHeartbeatRequest { if t == nil { return nil } return &history.RecordActivityTaskHeartbeatRequest{ DomainUUID: &t.DomainUUID, HeartbeatRequest: FromRecordActivityTaskHeartbeatRequest(t.HeartbeatRequest), } } // ToHistoryRecordActivityTaskHeartbeatRequest converts thrift RecordActivityTaskHeartbeatRequest type to internal func ToHistoryRecordActivityTaskHeartbeatRequest(t *history.RecordActivityTaskHeartbeatRequest) *types.HistoryRecordActivityTaskHeartbeatRequest { if t == nil { return nil } return &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: t.GetDomainUUID(), HeartbeatRequest: ToRecordActivityTaskHeartbeatRequest(t.HeartbeatRequest), } } // FromRecordActivityTaskStartedRequest converts internal RecordActivityTaskStartedRequest type to thrift func FromRecordActivityTaskStartedRequest(t *types.RecordActivityTaskStartedRequest) *history.RecordActivityTaskStartedRequest { if t == nil { return nil } return &history.RecordActivityTaskStartedRequest{ DomainUUID: &t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), ScheduleId: &t.ScheduleID, TaskId: &t.TaskID, RequestId: &t.RequestID, PollRequest: FromPollForActivityTaskRequest(t.PollRequest), } } // ToRecordActivityTaskStartedRequest converts thrift RecordActivityTaskStartedRequest type to internal func ToRecordActivityTaskStartedRequest(t *history.RecordActivityTaskStartedRequest) *types.RecordActivityTaskStartedRequest { if t == nil { return nil } return &types.RecordActivityTaskStartedRequest{ DomainUUID: t.GetDomainUUID(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), ScheduleID: t.GetScheduleId(), TaskID: t.GetTaskId(), RequestID: t.GetRequestId(), PollRequest: ToPollForActivityTaskRequest(t.PollRequest), } } // FromRecordActivityTaskStartedResponse converts internal RecordActivityTaskStartedResponse type to thrift func FromRecordActivityTaskStartedResponse(t *types.RecordActivityTaskStartedResponse) *history.RecordActivityTaskStartedResponse { if t == nil { return nil } return &history.RecordActivityTaskStartedResponse{ ScheduledEvent: FromHistoryEvent(t.ScheduledEvent), StartedTimestamp: t.StartedTimestamp, Attempt: &t.Attempt, ScheduledTimestampOfThisAttempt: t.ScheduledTimestampOfThisAttempt, HeartbeatDetails: t.HeartbeatDetails, WorkflowType: FromWorkflowType(t.WorkflowType), WorkflowDomain: &t.WorkflowDomain, } } // ToRecordActivityTaskStartedResponse converts thrift RecordActivityTaskStartedResponse type to internal func ToRecordActivityTaskStartedResponse(t *history.RecordActivityTaskStartedResponse) *types.RecordActivityTaskStartedResponse { if t == nil { return nil } return &types.RecordActivityTaskStartedResponse{ ScheduledEvent: ToHistoryEvent(t.ScheduledEvent), StartedTimestamp: t.StartedTimestamp, Attempt: t.GetAttempt(), ScheduledTimestampOfThisAttempt: t.ScheduledTimestampOfThisAttempt, HeartbeatDetails: t.HeartbeatDetails, WorkflowType: ToWorkflowType(t.WorkflowType), WorkflowDomain: t.GetWorkflowDomain(), } } // FromRecordChildExecutionCompletedRequest converts internal RecordChildExecutionCompletedRequest type to thrift func FromRecordChildExecutionCompletedRequest(t *types.RecordChildExecutionCompletedRequest) *history.RecordChildExecutionCompletedRequest { if t == nil { return nil } return &history.RecordChildExecutionCompletedRequest{ DomainUUID: &t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), InitiatedId: &t.InitiatedID, CompletedExecution: FromWorkflowExecution(t.CompletedExecution), CompletionEvent: FromHistoryEvent(t.CompletionEvent), StartedId: &t.StartedID, } } // ToRecordChildExecutionCompletedRequest converts thrift RecordChildExecutionCompletedRequest type to internal func ToRecordChildExecutionCompletedRequest(t *history.RecordChildExecutionCompletedRequest) *types.RecordChildExecutionCompletedRequest { if t == nil { return nil } return &types.RecordChildExecutionCompletedRequest{ DomainUUID: t.GetDomainUUID(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), InitiatedID: t.GetInitiatedId(), CompletedExecution: ToWorkflowExecution(t.CompletedExecution), CompletionEvent: ToHistoryEvent(t.CompletionEvent), StartedID: t.GetStartedId(), } } // FromRecordDecisionTaskStartedRequest converts internal RecordDecisionTaskStartedRequest type to thrift func FromRecordDecisionTaskStartedRequest(t *types.RecordDecisionTaskStartedRequest) *history.RecordDecisionTaskStartedRequest { if t == nil { return nil } return &history.RecordDecisionTaskStartedRequest{ DomainUUID: &t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), ScheduleId: &t.ScheduleID, TaskId: &t.TaskID, RequestId: &t.RequestID, PollRequest: FromPollForDecisionTaskRequest(t.PollRequest), } } // ToRecordDecisionTaskStartedRequest converts thrift RecordDecisionTaskStartedRequest type to internal func ToRecordDecisionTaskStartedRequest(t *history.RecordDecisionTaskStartedRequest) *types.RecordDecisionTaskStartedRequest { if t == nil { return nil } return &types.RecordDecisionTaskStartedRequest{ DomainUUID: t.GetDomainUUID(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), ScheduleID: t.GetScheduleId(), TaskID: t.GetTaskId(), RequestID: t.GetRequestId(), PollRequest: ToPollForDecisionTaskRequest(t.PollRequest), } } // FromRecordDecisionTaskStartedResponse converts internal RecordDecisionTaskStartedResponse type to thrift func FromRecordDecisionTaskStartedResponse(t *types.RecordDecisionTaskStartedResponse) *history.RecordDecisionTaskStartedResponse { if t == nil { return nil } return &history.RecordDecisionTaskStartedResponse{ WorkflowType: FromWorkflowType(t.WorkflowType), PreviousStartedEventId: t.PreviousStartedEventID, ScheduledEventId: &t.ScheduledEventID, StartedEventId: &t.StartedEventID, NextEventId: &t.NextEventID, Attempt: &t.Attempt, StickyExecutionEnabled: &t.StickyExecutionEnabled, DecisionInfo: FromTransientDecisionInfo(t.DecisionInfo), WorkflowExecutionTaskList: FromTaskList(t.WorkflowExecutionTaskList), EventStoreVersion: &t.EventStoreVersion, BranchToken: t.BranchToken, ScheduledTimestamp: t.ScheduledTimestamp, StartedTimestamp: t.StartedTimestamp, Queries: FromWorkflowQueryMap(t.Queries), HistorySize: &t.HistorySize, } } // ToRecordDecisionTaskStartedResponse converts thrift RecordDecisionTaskStartedResponse type to internal func ToRecordDecisionTaskStartedResponse(t *history.RecordDecisionTaskStartedResponse) *types.RecordDecisionTaskStartedResponse { if t == nil { return nil } return &types.RecordDecisionTaskStartedResponse{ WorkflowType: ToWorkflowType(t.WorkflowType), PreviousStartedEventID: t.PreviousStartedEventId, ScheduledEventID: t.GetScheduledEventId(), StartedEventID: t.GetStartedEventId(), NextEventID: t.GetNextEventId(), Attempt: t.GetAttempt(), StickyExecutionEnabled: t.GetStickyExecutionEnabled(), DecisionInfo: ToTransientDecisionInfo(t.DecisionInfo), WorkflowExecutionTaskList: ToTaskList(t.WorkflowExecutionTaskList), EventStoreVersion: t.GetEventStoreVersion(), BranchToken: t.BranchToken, ScheduledTimestamp: t.ScheduledTimestamp, StartedTimestamp: t.StartedTimestamp, Queries: ToWorkflowQueryMap(t.Queries), HistorySize: t.GetHistorySize(), } } // FromHistoryRefreshWorkflowTasksRequest converts internal RefreshWorkflowTasksRequest type to thrift func FromHistoryRefreshWorkflowTasksRequest(t *types.HistoryRefreshWorkflowTasksRequest) *history.RefreshWorkflowTasksRequest { if t == nil { return nil } return &history.RefreshWorkflowTasksRequest{ DomainUIID: &t.DomainUIID, Request: FromAdminRefreshWorkflowTasksRequest(t.Request), } } // ToHistoryRefreshWorkflowTasksRequest converts thrift RefreshWorkflowTasksRequest type to internal func ToHistoryRefreshWorkflowTasksRequest(t *history.RefreshWorkflowTasksRequest) *types.HistoryRefreshWorkflowTasksRequest { if t == nil { return nil } return &types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: t.GetDomainUIID(), Request: ToAdminRefreshWorkflowTasksRequest(t.Request), } } // FromHistoryRemoveSignalMutableStateRequest converts internal RemoveSignalMutableStateRequest type to thrift func FromHistoryRemoveSignalMutableStateRequest(t *types.RemoveSignalMutableStateRequest) *history.RemoveSignalMutableStateRequest { if t == nil { return nil } return &history.RemoveSignalMutableStateRequest{ DomainUUID: &t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), RequestId: &t.RequestID, } } // ToHistoryRemoveSignalMutableStateRequest converts thrift RemoveSignalMutableStateRequest type to internal func ToHistoryRemoveSignalMutableStateRequest(t *history.RemoveSignalMutableStateRequest) *types.RemoveSignalMutableStateRequest { if t == nil { return nil } return &types.RemoveSignalMutableStateRequest{ DomainUUID: t.GetDomainUUID(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), RequestID: t.GetRequestId(), } } // FromHistoryReplicateEventsV2Request converts internal ReplicateEventsV2Request type to thrift func FromHistoryReplicateEventsV2Request(t *types.ReplicateEventsV2Request) *history.ReplicateEventsV2Request { if t == nil { return nil } return &history.ReplicateEventsV2Request{ DomainUUID: &t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), VersionHistoryItems: FromVersionHistoryItemArray(t.VersionHistoryItems), Events: FromDataBlob(t.Events), NewRunEvents: FromDataBlob(t.NewRunEvents), } } // ToHistoryReplicateEventsV2Request converts thrift ReplicateEventsV2Request type to internal func ToHistoryReplicateEventsV2Request(t *history.ReplicateEventsV2Request) *types.ReplicateEventsV2Request { if t == nil { return nil } return &types.ReplicateEventsV2Request{ DomainUUID: t.GetDomainUUID(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), VersionHistoryItems: ToVersionHistoryItemArray(t.VersionHistoryItems), Events: ToDataBlob(t.Events), NewRunEvents: ToDataBlob(t.NewRunEvents), } } // FromHistoryRequestCancelWorkflowExecutionRequest converts internal RequestCancelWorkflowExecutionRequest type to thrift func FromHistoryRequestCancelWorkflowExecutionRequest(t *types.HistoryRequestCancelWorkflowExecutionRequest) *history.RequestCancelWorkflowExecutionRequest { if t == nil { return nil } return &history.RequestCancelWorkflowExecutionRequest{ DomainUUID: &t.DomainUUID, CancelRequest: FromRequestCancelWorkflowExecutionRequest(t.CancelRequest), ExternalInitiatedEventId: t.ExternalInitiatedEventID, ExternalWorkflowExecution: FromWorkflowExecution(t.ExternalWorkflowExecution), ChildWorkflowOnly: &t.ChildWorkflowOnly, } } // ToHistoryRequestCancelWorkflowExecutionRequest converts thrift RequestCancelWorkflowExecutionRequest type to internal func ToHistoryRequestCancelWorkflowExecutionRequest(t *history.RequestCancelWorkflowExecutionRequest) *types.HistoryRequestCancelWorkflowExecutionRequest { if t == nil { return nil } return &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: t.GetDomainUUID(), CancelRequest: ToRequestCancelWorkflowExecutionRequest(t.CancelRequest), ExternalInitiatedEventID: t.ExternalInitiatedEventId, ExternalWorkflowExecution: ToWorkflowExecution(t.ExternalWorkflowExecution), ChildWorkflowOnly: t.GetChildWorkflowOnly(), } } // FromHistoryResetStickyTaskListRequest converts internal ResetStickyTaskListRequest type to thrift func FromHistoryResetStickyTaskListRequest(t *types.HistoryResetStickyTaskListRequest) *history.ResetStickyTaskListRequest { if t == nil { return nil } return &history.ResetStickyTaskListRequest{ DomainUUID: &t.DomainUUID, Execution: FromWorkflowExecution(t.Execution), } } // ToHistoryResetStickyTaskListRequest converts thrift ResetStickyTaskListRequest type to internal func ToHistoryResetStickyTaskListRequest(t *history.ResetStickyTaskListRequest) *types.HistoryResetStickyTaskListRequest { if t == nil { return nil } return &types.HistoryResetStickyTaskListRequest{ DomainUUID: t.GetDomainUUID(), Execution: ToWorkflowExecution(t.Execution), } } // FromHistoryResetStickyTaskListResponse converts internal ResetStickyTaskListResponse type to thrift func FromHistoryResetStickyTaskListResponse(t *types.HistoryResetStickyTaskListResponse) *history.ResetStickyTaskListResponse { if t == nil { return nil } return &history.ResetStickyTaskListResponse{} } // ToHistoryResetStickyTaskListResponse converts thrift ResetStickyTaskListResponse type to internal func ToHistoryResetStickyTaskListResponse(t *history.ResetStickyTaskListResponse) *types.HistoryResetStickyTaskListResponse { if t == nil { return nil } return &types.HistoryResetStickyTaskListResponse{} } // FromHistoryResetWorkflowExecutionRequest converts internal ResetWorkflowExecutionRequest type to thrift func FromHistoryResetWorkflowExecutionRequest(t *types.HistoryResetWorkflowExecutionRequest) *history.ResetWorkflowExecutionRequest { if t == nil { return nil } return &history.ResetWorkflowExecutionRequest{ DomainUUID: &t.DomainUUID, ResetRequest: FromResetWorkflowExecutionRequest(t.ResetRequest), } } // ToHistoryResetWorkflowExecutionRequest converts thrift ResetWorkflowExecutionRequest type to internal func ToHistoryResetWorkflowExecutionRequest(t *history.ResetWorkflowExecutionRequest) *types.HistoryResetWorkflowExecutionRequest { if t == nil { return nil } return &types.HistoryResetWorkflowExecutionRequest{ DomainUUID: t.GetDomainUUID(), ResetRequest: ToResetWorkflowExecutionRequest(t.ResetRequest), } } // FromHistoryRespondActivityTaskCanceledRequest converts internal RespondActivityTaskCanceledRequest type to thrift func FromHistoryRespondActivityTaskCanceledRequest(t *types.HistoryRespondActivityTaskCanceledRequest) *history.RespondActivityTaskCanceledRequest { if t == nil { return nil } return &history.RespondActivityTaskCanceledRequest{ DomainUUID: &t.DomainUUID, CancelRequest: FromRespondActivityTaskCanceledRequest(t.CancelRequest), } } // ToHistoryRespondActivityTaskCanceledRequest converts thrift RespondActivityTaskCanceledRequest type to internal func ToHistoryRespondActivityTaskCanceledRequest(t *history.RespondActivityTaskCanceledRequest) *types.HistoryRespondActivityTaskCanceledRequest { if t == nil { return nil } return &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: t.GetDomainUUID(), CancelRequest: ToRespondActivityTaskCanceledRequest(t.CancelRequest), } } // FromHistoryRespondActivityTaskCompletedRequest converts internal RespondActivityTaskCompletedRequest type to thrift func FromHistoryRespondActivityTaskCompletedRequest(t *types.HistoryRespondActivityTaskCompletedRequest) *history.RespondActivityTaskCompletedRequest { if t == nil { return nil } return &history.RespondActivityTaskCompletedRequest{ DomainUUID: &t.DomainUUID, CompleteRequest: FromRespondActivityTaskCompletedRequest(t.CompleteRequest), } } // ToHistoryRespondActivityTaskCompletedRequest converts thrift RespondActivityTaskCompletedRequest type to internal func ToHistoryRespondActivityTaskCompletedRequest(t *history.RespondActivityTaskCompletedRequest) *types.HistoryRespondActivityTaskCompletedRequest { if t == nil { return nil } return &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: t.GetDomainUUID(), CompleteRequest: ToRespondActivityTaskCompletedRequest(t.CompleteRequest), } } // FromHistoryRespondActivityTaskFailedRequest converts internal RespondActivityTaskFailedRequest type to thrift func FromHistoryRespondActivityTaskFailedRequest(t *types.HistoryRespondActivityTaskFailedRequest) *history.RespondActivityTaskFailedRequest { if t == nil { return nil } return &history.RespondActivityTaskFailedRequest{ DomainUUID: &t.DomainUUID, FailedRequest: FromRespondActivityTaskFailedRequest(t.FailedRequest), } } // ToHistoryRespondActivityTaskFailedRequest converts thrift RespondActivityTaskFailedRequest type to internal func ToHistoryRespondActivityTaskFailedRequest(t *history.RespondActivityTaskFailedRequest) *types.HistoryRespondActivityTaskFailedRequest { if t == nil { return nil } return &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: t.GetDomainUUID(), FailedRequest: ToRespondActivityTaskFailedRequest(t.FailedRequest), } } // FromHistoryRespondDecisionTaskCompletedRequest converts internal RespondDecisionTaskCompletedRequest type to thrift func FromHistoryRespondDecisionTaskCompletedRequest(t *types.HistoryRespondDecisionTaskCompletedRequest) *history.RespondDecisionTaskCompletedRequest { if t == nil { return nil } return &history.RespondDecisionTaskCompletedRequest{ DomainUUID: &t.DomainUUID, CompleteRequest: FromRespondDecisionTaskCompletedRequest(t.CompleteRequest), } } // ToHistoryRespondDecisionTaskCompletedRequest converts thrift RespondDecisionTaskCompletedRequest type to internal func ToHistoryRespondDecisionTaskCompletedRequest(t *history.RespondDecisionTaskCompletedRequest) *types.HistoryRespondDecisionTaskCompletedRequest { if t == nil { return nil } return &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: t.GetDomainUUID(), CompleteRequest: ToRespondDecisionTaskCompletedRequest(t.CompleteRequest), } } // FromHistoryRespondDecisionTaskCompletedResponse converts internal RespondDecisionTaskCompletedResponse type to thrift func FromHistoryRespondDecisionTaskCompletedResponse(t *types.HistoryRespondDecisionTaskCompletedResponse) *history.RespondDecisionTaskCompletedResponse { if t == nil { return nil } return &history.RespondDecisionTaskCompletedResponse{ StartedResponse: FromRecordDecisionTaskStartedResponse(t.StartedResponse), ActivitiesToDispatchLocally: FromActivityLocalDispatchInfoMap(t.ActivitiesToDispatchLocally), } } // ToHistoryRespondDecisionTaskCompletedResponse converts thrift RespondDecisionTaskCompletedResponse type to internal func ToHistoryRespondDecisionTaskCompletedResponse(t *history.RespondDecisionTaskCompletedResponse) *types.HistoryRespondDecisionTaskCompletedResponse { if t == nil { return nil } return &types.HistoryRespondDecisionTaskCompletedResponse{ StartedResponse: ToRecordDecisionTaskStartedResponse(t.StartedResponse), ActivitiesToDispatchLocally: ToActivityLocalDispatchInfoMap(t.ActivitiesToDispatchLocally), } } // FromHistoryRespondDecisionTaskFailedRequest converts internal RespondDecisionTaskFailedRequest type to thrift func FromHistoryRespondDecisionTaskFailedRequest(t *types.HistoryRespondDecisionTaskFailedRequest) *history.RespondDecisionTaskFailedRequest { if t == nil { return nil } return &history.RespondDecisionTaskFailedRequest{ DomainUUID: &t.DomainUUID, FailedRequest: FromRespondDecisionTaskFailedRequest(t.FailedRequest), } } // ToHistoryRespondDecisionTaskFailedRequest converts thrift RespondDecisionTaskFailedRequest type to internal func ToHistoryRespondDecisionTaskFailedRequest(t *history.RespondDecisionTaskFailedRequest) *types.HistoryRespondDecisionTaskFailedRequest { if t == nil { return nil } return &types.HistoryRespondDecisionTaskFailedRequest{ DomainUUID: t.GetDomainUUID(), FailedRequest: ToRespondDecisionTaskFailedRequest(t.FailedRequest), } } // FromHistoryScheduleDecisionTaskRequest converts internal ScheduleDecisionTaskRequest type to thrift func FromHistoryScheduleDecisionTaskRequest(t *types.ScheduleDecisionTaskRequest) *history.ScheduleDecisionTaskRequest { if t == nil { return nil } return &history.ScheduleDecisionTaskRequest{ DomainUUID: &t.DomainUUID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), IsFirstDecision: &t.IsFirstDecision, } } // ToHistoryScheduleDecisionTaskRequest converts thrift ScheduleDecisionTaskRequest type to internal func ToHistoryScheduleDecisionTaskRequest(t *history.ScheduleDecisionTaskRequest) *types.ScheduleDecisionTaskRequest { if t == nil { return nil } return &types.ScheduleDecisionTaskRequest{ DomainUUID: t.GetDomainUUID(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), IsFirstDecision: t.GetIsFirstDecision(), } } // FromShardOwnershipLostError converts internal ShardOwnershipLostError type to thrift func FromShardOwnershipLostError(t *types.ShardOwnershipLostError) *history.ShardOwnershipLostError { if t == nil { return nil } return &history.ShardOwnershipLostError{ Message: &t.Message, Owner: &t.Owner, } } // ToShardOwnershipLostError converts thrift ShardOwnershipLostError type to internal func ToShardOwnershipLostError(t *history.ShardOwnershipLostError) *types.ShardOwnershipLostError { if t == nil { return nil } return &types.ShardOwnershipLostError{ Message: t.GetMessage(), Owner: t.GetOwner(), } } // FromHistorySignalWithStartWorkflowExecutionRequest converts internal SignalWithStartWorkflowExecutionRequest type to thrift func FromHistorySignalWithStartWorkflowExecutionRequest(t *types.HistorySignalWithStartWorkflowExecutionRequest) *history.SignalWithStartWorkflowExecutionRequest { if t == nil { return nil } return &history.SignalWithStartWorkflowExecutionRequest{ DomainUUID: &t.DomainUUID, SignalWithStartRequest: FromSignalWithStartWorkflowExecutionRequest(t.SignalWithStartRequest), PartitionConfig: t.PartitionConfig, } } // ToHistorySignalWithStartWorkflowExecutionRequest converts thrift SignalWithStartWorkflowExecutionRequest type to internal func ToHistorySignalWithStartWorkflowExecutionRequest(t *history.SignalWithStartWorkflowExecutionRequest) *types.HistorySignalWithStartWorkflowExecutionRequest { if t == nil { return nil } return &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: t.GetDomainUUID(), SignalWithStartRequest: ToSignalWithStartWorkflowExecutionRequest(t.SignalWithStartRequest), PartitionConfig: t.PartitionConfig, } } // FromHistorySignalWorkflowExecutionRequest converts internal SignalWorkflowExecutionRequest type to thrift func FromHistorySignalWorkflowExecutionRequest(t *types.HistorySignalWorkflowExecutionRequest) *history.SignalWorkflowExecutionRequest { if t == nil { return nil } return &history.SignalWorkflowExecutionRequest{ DomainUUID: &t.DomainUUID, SignalRequest: FromSignalWorkflowExecutionRequest(t.SignalRequest), ExternalWorkflowExecution: FromWorkflowExecution(t.ExternalWorkflowExecution), ChildWorkflowOnly: &t.ChildWorkflowOnly, } } // ToHistorySignalWorkflowExecutionRequest converts thrift SignalWorkflowExecutionRequest type to internal func ToHistorySignalWorkflowExecutionRequest(t *history.SignalWorkflowExecutionRequest) *types.HistorySignalWorkflowExecutionRequest { if t == nil { return nil } return &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: t.GetDomainUUID(), SignalRequest: ToSignalWorkflowExecutionRequest(t.SignalRequest), ExternalWorkflowExecution: ToWorkflowExecution(t.ExternalWorkflowExecution), ChildWorkflowOnly: t.GetChildWorkflowOnly(), } } // FromHistoryStartWorkflowExecutionRequest converts internal StartWorkflowExecutionRequest type to thrift func FromHistoryStartWorkflowExecutionRequest(t *types.HistoryStartWorkflowExecutionRequest) *history.StartWorkflowExecutionRequest { if t == nil { return nil } return &history.StartWorkflowExecutionRequest{ DomainUUID: &t.DomainUUID, StartRequest: FromStartWorkflowExecutionRequest(t.StartRequest), ParentExecutionInfo: FromParentExecutionInfo(t.ParentExecutionInfo), Attempt: &t.Attempt, ExpirationTimestamp: t.ExpirationTimestamp, ContinueAsNewInitiator: FromContinueAsNewInitiator(t.ContinueAsNewInitiator), ContinuedFailureReason: t.ContinuedFailureReason, ContinuedFailureDetails: t.ContinuedFailureDetails, LastCompletionResult: t.LastCompletionResult, FirstDecisionTaskBackoffSeconds: t.FirstDecisionTaskBackoffSeconds, PartitionConfig: t.PartitionConfig, } } // ToHistoryStartWorkflowExecutionRequest converts thrift StartWorkflowExecutionRequest type to internal func ToHistoryStartWorkflowExecutionRequest(t *history.StartWorkflowExecutionRequest) *types.HistoryStartWorkflowExecutionRequest { if t == nil { return nil } return &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: t.GetDomainUUID(), StartRequest: ToStartWorkflowExecutionRequest(t.StartRequest), ParentExecutionInfo: ToParentExecutionInfo(t.ParentExecutionInfo), Attempt: t.GetAttempt(), ExpirationTimestamp: t.ExpirationTimestamp, ContinueAsNewInitiator: ToContinueAsNewInitiator(t.ContinueAsNewInitiator), ContinuedFailureReason: t.ContinuedFailureReason, ContinuedFailureDetails: t.ContinuedFailureDetails, LastCompletionResult: t.LastCompletionResult, FirstDecisionTaskBackoffSeconds: t.FirstDecisionTaskBackoffSeconds, PartitionConfig: t.PartitionConfig, } } // FromHistorySyncActivityRequest converts internal SyncActivityRequest type to thrift func FromHistorySyncActivityRequest(t *types.SyncActivityRequest) *history.SyncActivityRequest { if t == nil { return nil } return &history.SyncActivityRequest{ DomainId: &t.DomainID, WorkflowId: &t.WorkflowID, RunId: &t.RunID, Version: &t.Version, ScheduledId: &t.ScheduledID, ScheduledTime: t.ScheduledTime, StartedId: &t.StartedID, StartedTime: t.StartedTime, LastHeartbeatTime: t.LastHeartbeatTime, Details: t.Details, Attempt: &t.Attempt, LastFailureReason: t.LastFailureReason, LastWorkerIdentity: &t.LastWorkerIdentity, LastFailureDetails: t.LastFailureDetails, VersionHistory: FromVersionHistory(t.VersionHistory), } } // ToHistorySyncActivityRequest converts thrift SyncActivityRequest type to internal func ToHistorySyncActivityRequest(t *history.SyncActivityRequest) *types.SyncActivityRequest { if t == nil { return nil } return &types.SyncActivityRequest{ DomainID: t.GetDomainId(), WorkflowID: t.GetWorkflowId(), RunID: t.GetRunId(), Version: t.GetVersion(), ScheduledID: t.GetScheduledId(), ScheduledTime: t.ScheduledTime, StartedID: t.GetStartedId(), StartedTime: t.StartedTime, LastHeartbeatTime: t.LastHeartbeatTime, Details: t.Details, Attempt: t.GetAttempt(), LastFailureReason: t.LastFailureReason, LastWorkerIdentity: t.GetLastWorkerIdentity(), LastFailureDetails: t.LastFailureDetails, VersionHistory: ToVersionHistory(t.VersionHistory), } } // FromHistorySyncShardStatusRequest converts internal SyncShardStatusRequest type to thrift func FromHistorySyncShardStatusRequest(t *types.SyncShardStatusRequest) *history.SyncShardStatusRequest { if t == nil { return nil } return &history.SyncShardStatusRequest{ SourceCluster: &t.SourceCluster, ShardId: &t.ShardID, Timestamp: t.Timestamp, } } // ToHistorySyncShardStatusRequest converts thrift SyncShardStatusRequest type to internal func ToHistorySyncShardStatusRequest(t *history.SyncShardStatusRequest) *types.SyncShardStatusRequest { if t == nil { return nil } return &types.SyncShardStatusRequest{ SourceCluster: t.GetSourceCluster(), ShardID: t.GetShardId(), Timestamp: t.Timestamp, } } // FromHistoryTerminateWorkflowExecutionRequest converts internal TerminateWorkflowExecutionRequest type to thrift func FromHistoryTerminateWorkflowExecutionRequest(t *types.HistoryTerminateWorkflowExecutionRequest) *history.TerminateWorkflowExecutionRequest { if t == nil { return nil } return &history.TerminateWorkflowExecutionRequest{ DomainUUID: &t.DomainUUID, TerminateRequest: FromTerminateWorkflowExecutionRequest(t.TerminateRequest), ExternalWorkflowExecution: FromWorkflowExecution(t.ExternalWorkflowExecution), ChildWorkflowOnly: &t.ChildWorkflowOnly, } } // ToHistoryTerminateWorkflowExecutionRequest converts thrift TerminateWorkflowExecutionRequest type to internal func ToHistoryTerminateWorkflowExecutionRequest(t *history.TerminateWorkflowExecutionRequest) *types.HistoryTerminateWorkflowExecutionRequest { if t == nil { return nil } return &types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: t.GetDomainUUID(), TerminateRequest: ToTerminateWorkflowExecutionRequest(t.TerminateRequest), ExternalWorkflowExecution: ToWorkflowExecution(t.ExternalWorkflowExecution), ChildWorkflowOnly: t.GetChildWorkflowOnly(), } } // FromFailoverMarkerTokenArray converts internal FailoverMarkerToken type array to thrift func FromFailoverMarkerTokenArray(t []*types.FailoverMarkerToken) []*history.FailoverMarkerToken { if t == nil { return nil } v := make([]*history.FailoverMarkerToken, len(t)) for i := range t { v[i] = FromFailoverMarkerToken(t[i]) } return v } // ToFailoverMarkerTokenArray converts thrift FailoverMarkerToken type array to internal func ToFailoverMarkerTokenArray(t []*history.FailoverMarkerToken) []*types.FailoverMarkerToken { if t == nil { return nil } v := make([]*types.FailoverMarkerToken, len(t)) for i := range t { v[i] = ToFailoverMarkerToken(t[i]) } return v } // FromProcessingQueueStateArray converts internal ProcessingQueueState type array to thrift func FromProcessingQueueStateArray(t []*types.ProcessingQueueState) []*history.ProcessingQueueState { if t == nil { return nil } v := make([]*history.ProcessingQueueState, len(t)) for i := range t { v[i] = FromProcessingQueueState(t[i]) } return v } // ToProcessingQueueStateArray converts thrift ProcessingQueueState type array to internal func ToProcessingQueueStateArray(t []*history.ProcessingQueueState) []*types.ProcessingQueueState { if t == nil { return nil } v := make([]*types.ProcessingQueueState, len(t)) for i := range t { v[i] = ToProcessingQueueState(t[i]) } return v } // FromProcessingQueueStateArrayMap converts internal ProcessingQueueState array map to thrift func FromProcessingQueueStateArrayMap(t map[string][]*types.ProcessingQueueState) map[string][]*history.ProcessingQueueState { if t == nil { return nil } v := make(map[string][]*history.ProcessingQueueState, len(t)) for key := range t { v[key] = FromProcessingQueueStateArray(t[key]) } return v } // ToProcessingQueueStateArrayMap converts thrift ProcessingQueueState array map to internal func ToProcessingQueueStateArrayMap(t map[string][]*history.ProcessingQueueState) map[string][]*types.ProcessingQueueState { if t == nil { return nil } v := make(map[string][]*types.ProcessingQueueState, len(t)) for key := range t { v[key] = ToProcessingQueueStateArray(t[key]) } return v } // FromGetFailoverInfoRequest converts internal GetFailoverInfoRequest type to thrift func FromGetFailoverInfoRequest(t *types.GetFailoverInfoRequest) *history.GetFailoverInfoRequest { if t == nil { return nil } return &history.GetFailoverInfoRequest{ DomainID: &t.DomainID, } } // ToGetFailoverInfoRequest converts thrift GetFailoverInfoRequest type to internal func ToGetFailoverInfoRequest(t *history.GetFailoverInfoRequest) *types.GetFailoverInfoRequest { if t == nil { return nil } return &types.GetFailoverInfoRequest{ DomainID: t.GetDomainID(), } } // FromGetFailoverInfoResponse converts internal GetFailoverInfoRequest type to thrift func FromGetFailoverInfoResponse(t *types.GetFailoverInfoResponse) *history.GetFailoverInfoResponse { if t == nil { return nil } return &history.GetFailoverInfoResponse{ CompletedShardCount: &t.CompletedShardCount, PendingShards: t.GetPendingShards(), } } // ToGetFailoverInfoResponse converts thrift GetFailoverInfoResponse type to internal func ToGetFailoverInfoResponse(t *history.GetFailoverInfoResponse) *types.GetFailoverInfoResponse { if t == nil { return nil } return &types.GetFailoverInfoResponse{ CompletedShardCount: t.GetCompletedShardCount(), PendingShards: t.GetPendingShards(), } } func FromHistoryRatelimitUpdateRequest(t *types.RatelimitUpdateRequest) *history.RatelimitUpdateRequest { if t == nil { return nil } return &history.RatelimitUpdateRequest{ Data: FromAny(t.Any), } } func ToHistoryRatelimitUpdateRequest(t *history.RatelimitUpdateRequest) *types.RatelimitUpdateRequest { if t == nil { return nil } return &types.RatelimitUpdateRequest{ Any: ToAny(t.Data), } } func FromHistoryRatelimitUpdateResponse(t *types.RatelimitUpdateResponse) *history.RatelimitUpdateResponse { if t == nil { return nil } return &history.RatelimitUpdateResponse{ Data: FromAny(t.Any), } } func ToHistoryRatelimitUpdateResponse(t *history.RatelimitUpdateResponse) *types.RatelimitUpdateResponse { if t == nil { return nil } return &types.RatelimitUpdateResponse{ Any: ToAny(t.Data), } } ================================================ FILE: common/types/mapper/thrift/history_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/uber/cadence/.gen/go/history" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" ) func TestHistoryTerminateWorkflowExecutionRequestConversion(t *testing.T) { for _, item := range []*types.HistoryTerminateWorkflowExecutionRequest{nil, {}, &testdata.HistoryTerminateWorkflowExecutionRequest} { assert.Equal(t, item, ToHistoryTerminateWorkflowExecutionRequest(FromHistoryTerminateWorkflowExecutionRequest(item))) } } func TestDescribeMutableStateRequestConversion(t *testing.T) { testCases := []*types.DescribeMutableStateRequest{ nil, {}, &testdata.HistoryDescribeMutableStateRequest, } for _, original := range testCases { thriftObj := FromHistoryDescribeMutableStateRequest(original) roundTripObj := ToHistoryDescribeMutableStateRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeMutableStateResponseConversion(t *testing.T) { testCases := []*types.DescribeMutableStateResponse{ nil, {}, &testdata.HistoryDescribeMutableStateResponse, } for _, original := range testCases { thriftObj := FromHistoryDescribeMutableStateResponse(original) roundTripObj := ToHistoryDescribeMutableStateResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryDescribeWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.HistoryDescribeWorkflowExecutionRequest{ nil, {}, &testdata.HistoryDescribeWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromHistoryDescribeWorkflowExecutionRequest(original) roundTripObj := ToHistoryDescribeWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDomainFilterConversion(t *testing.T) { testCases := []*types.DomainFilter{ nil, {}, {DomainIDs: []string{"test"}, ReverseMatch: true}, } for _, original := range testCases { thriftObj := FromDomainFilter(original) roundTripObj := ToDomainFilter(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestEventAlreadyStartedErrorConversion(t *testing.T) { testCases := []*types.EventAlreadyStartedError{ nil, {}, &testdata.EventAlreadyStartedError, } for _, original := range testCases { thriftObj := FromEventAlreadyStartedError(original) roundTripObj := ToEventAlreadyStartedError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestFailoverMarkerTokenConversion(t *testing.T) { testCases := []*types.FailoverMarkerToken{ nil, {}, &testdata.FailoverMarkerToken, } for _, original := range testCases { thriftObj := FromFailoverMarkerToken(original) roundTripObj := ToFailoverMarkerToken(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestGetMutableStateRequestConversion(t *testing.T) { testCases := []*types.GetMutableStateRequest{ nil, {}, &testdata.HistoryGetMutableStateRequest, } for _, original := range testCases { thriftObj := FromHistoryGetMutableStateRequest(original) roundTripObj := ToHistoryGetMutableStateRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestGetMutableStateResponseConversion(t *testing.T) { testCases := []*types.GetMutableStateResponse{ nil, {}, &testdata.HistoryGetMutableStateResponse, } for _, original := range testCases { thriftObj := FromHistoryGetMutableStateResponse(original) roundTripObj := ToHistoryGetMutableStateResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestNotifyFailoverMarkersRequestConversion(t *testing.T) { testCases := []*types.NotifyFailoverMarkersRequest{ nil, {}, {FailoverMarkerTokens: []*types.FailoverMarkerToken{&testdata.FailoverMarkerToken}}, } for _, original := range testCases { thriftObj := FromHistoryNotifyFailoverMarkersRequest(original) roundTripObj := ToHistoryNotifyFailoverMarkersRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestParentExecutionInfoConversion(t *testing.T) { testCases := []*types.ParentExecutionInfo{ nil, {}, &testdata.ParentExecutionInfo, } for _, original := range testCases { thriftObj := FromParentExecutionInfo(original) roundTripObj := ToParentExecutionInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPollMutableStateRequestConversion(t *testing.T) { testCases := []*types.PollMutableStateRequest{ nil, {}, &testdata.HistoryPollMutableStateRequest, } for _, original := range testCases { thriftObj := FromHistoryPollMutableStateRequest(original) roundTripObj := ToHistoryPollMutableStateRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPollMutableStateResponseConversion(t *testing.T) { testCases := []*types.PollMutableStateResponse{ nil, {}, &testdata.HistoryPollMutableStateResponse, } for _, original := range testCases { thriftObj := FromHistoryPollMutableStateResponse(original) roundTripObj := ToHistoryPollMutableStateResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestProcessingQueueStateConversion(t *testing.T) { testCases := []*types.ProcessingQueueState{ nil, {}, {Level: common.Int32Ptr(1), AckLevel: common.Int64Ptr(1), MaxLevel: common.Int64Ptr(1), DomainFilter: &types.DomainFilter{DomainIDs: []string{"test"}, ReverseMatch: true}}, } for _, original := range testCases { thriftObj := FromProcessingQueueState(original) roundTripObj := ToProcessingQueueState(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestProcessingQueueStatesConversion(t *testing.T) { testCases := []*types.ProcessingQueueStates{ nil, {}, { StatesByCluster: map[string][]*types.ProcessingQueueState{ "test": { {Level: common.Int32Ptr(1), AckLevel: common.Int64Ptr(1), MaxLevel: common.Int64Ptr(1), DomainFilter: &types.DomainFilter{DomainIDs: []string{"test"}, ReverseMatch: true}}, }, }, }, } for _, original := range testCases { thriftObj := FromProcessingQueueStates(original) roundTripObj := ToProcessingQueueStates(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryQueryWorkflowRequestConversion(t *testing.T) { testCases := []*types.HistoryQueryWorkflowRequest{ nil, {}, &testdata.HistoryQueryWorkflowRequest, } for _, original := range testCases { thriftObj := FromHistoryQueryWorkflowRequest(original) roundTripObj := ToHistoryQueryWorkflowRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryQueryWorkflowResponseConversion(t *testing.T) { testCases := []*types.HistoryQueryWorkflowResponse{ nil, {}, &testdata.HistoryQueryWorkflowResponse, } for _, original := range testCases { thriftObj := FromHistoryQueryWorkflowResponse(original) roundTripObj := ToHistoryQueryWorkflowResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryReapplyEventsRequestConversion(t *testing.T) { testCases := []*types.HistoryReapplyEventsRequest{ nil, {}, &testdata.HistoryReapplyEventsRequest, } for _, original := range testCases { thriftObj := FromHistoryReapplyEventsRequest(original) roundTripObj := ToHistoryReapplyEventsRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryRecordActivityTaskHeartbeatRequestConversion(t *testing.T) { testCases := []*types.HistoryRecordActivityTaskHeartbeatRequest{ nil, {}, &testdata.HistoryRecordActivityTaskHeartbeatRequest, } for _, original := range testCases { thriftObj := FromHistoryRecordActivityTaskHeartbeatRequest(original) roundTripObj := ToHistoryRecordActivityTaskHeartbeatRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRecordActivityTaskStartedRequestConversion(t *testing.T) { testCases := []*types.RecordActivityTaskStartedRequest{ nil, {}, &testdata.HistoryRecordActivityTaskStartedRequest, } for _, original := range testCases { thriftObj := FromRecordActivityTaskStartedRequest(original) roundTripObj := ToRecordActivityTaskStartedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRecordActivityTaskStartedResponseConversion(t *testing.T) { testCases := []*types.RecordActivityTaskStartedResponse{ nil, {}, &testdata.HistoryRecordActivityTaskStartedResponse, } for _, original := range testCases { thriftObj := FromRecordActivityTaskStartedResponse(original) roundTripObj := ToRecordActivityTaskStartedResponse(thriftObj) opt := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestRecordChildExecutionCompletedRequestConversion(t *testing.T) { testCases := []*types.RecordChildExecutionCompletedRequest{ nil, {}, &testdata.HistoryRecordChildExecutionCompletedRequest, } for _, original := range testCases { thriftObj := FromRecordChildExecutionCompletedRequest(original) roundTripObj := ToRecordChildExecutionCompletedRequest(thriftObj) opt := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestRecordDecisionTaskStartedRequestConversion(t *testing.T) { testCases := []*types.RecordDecisionTaskStartedRequest{ nil, {}, &testdata.HistoryRecordDecisionTaskStartedRequest, } for _, original := range testCases { thriftObj := FromRecordDecisionTaskStartedRequest(original) roundTripObj := ToRecordDecisionTaskStartedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRecordDecisionTaskStartedResponseConversion(t *testing.T) { testCases := []*types.RecordDecisionTaskStartedResponse{ nil, {}, &testdata.HistoryRecordDecisionTaskStartedResponse, } for _, original := range testCases { thriftObj := FromRecordDecisionTaskStartedResponse(original) roundTripObj := ToRecordDecisionTaskStartedResponse(thriftObj) opt := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestHistoryRefreshWorkflowTasksRequestConversion(t *testing.T) { testCases := []*types.HistoryRefreshWorkflowTasksRequest{ nil, {}, &testdata.HistoryRefreshWorkflowTasksRequest, } for _, original := range testCases { thriftObj := FromHistoryRefreshWorkflowTasksRequest(original) roundTripObj := ToHistoryRefreshWorkflowTasksRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRemoveSignalMutableStateRequestConversion(t *testing.T) { testCases := []*types.RemoveSignalMutableStateRequest{ nil, {}, {DomainUUID: "test-uuid", WorkflowExecution: &types.WorkflowExecution{WorkflowID: "test", RunID: "test"}, RequestID: "test-req"}, } for _, original := range testCases { thriftObj := FromHistoryRemoveSignalMutableStateRequest(original) roundTripObj := ToHistoryRemoveSignalMutableStateRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestReplicateEventsV2RequestConversion(t *testing.T) { testCases := []*types.ReplicateEventsV2Request{ nil, {}, &testdata.HistoryReplicateEventsV2Request, } for _, original := range testCases { thriftObj := FromHistoryReplicateEventsV2Request(original) roundTripObj := ToHistoryReplicateEventsV2Request(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryRequestCancelWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.HistoryRequestCancelWorkflowExecutionRequest{ nil, {}, &testdata.HistoryRequestCancelWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromHistoryRequestCancelWorkflowExecutionRequest(original) roundTripObj := ToHistoryRequestCancelWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryResetStickyTaskListRequestConversion(t *testing.T) { testCases := []*types.HistoryResetStickyTaskListRequest{ nil, {}, &testdata.HistoryResetStickyTaskListRequest, } for _, original := range testCases { thriftObj := FromHistoryResetStickyTaskListRequest(original) roundTripObj := ToHistoryResetStickyTaskListRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryResetStickyTaskListResponseConversion(t *testing.T) { testCases := []*types.HistoryResetStickyTaskListResponse{ nil, {}, } for _, original := range testCases { thriftObj := FromHistoryResetStickyTaskListResponse(original) roundTripObj := ToHistoryResetStickyTaskListResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryResetWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.HistoryResetWorkflowExecutionRequest{ nil, {}, &testdata.HistoryResetWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromHistoryResetWorkflowExecutionRequest(original) roundTripObj := ToHistoryResetWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryRespondActivityTaskCanceledRequestConversion(t *testing.T) { testCases := []*types.HistoryRespondActivityTaskCanceledRequest{ nil, {}, &testdata.HistoryRespondActivityTaskCanceledRequest, } for _, original := range testCases { thriftObj := FromHistoryRespondActivityTaskCanceledRequest(original) roundTripObj := ToHistoryRespondActivityTaskCanceledRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryRespondActivityTaskCompletedRequestConversion(t *testing.T) { testCases := []*types.HistoryRespondActivityTaskCompletedRequest{ nil, {}, &testdata.HistoryRespondActivityTaskCompletedRequest, } for _, original := range testCases { thriftObj := FromHistoryRespondActivityTaskCompletedRequest(original) roundTripObj := ToHistoryRespondActivityTaskCompletedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryRespondActivityTaskFailedRequestConversion(t *testing.T) { testCases := []*types.HistoryRespondActivityTaskFailedRequest{ nil, {}, &testdata.HistoryRespondActivityTaskFailedRequest, } for _, original := range testCases { thriftObj := FromHistoryRespondActivityTaskFailedRequest(original) roundTripObj := ToHistoryRespondActivityTaskFailedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryRespondDecisionTaskCompletedRequestConversion(t *testing.T) { testCases := []*types.HistoryRespondDecisionTaskCompletedRequest{ nil, {}, &testdata.HistoryRespondDecisionTaskCompletedRequest, } for _, original := range testCases { thriftObj := FromHistoryRespondDecisionTaskCompletedRequest(original) roundTripObj := ToHistoryRespondDecisionTaskCompletedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryRespondDecisionTaskCompletedResponseConversion(t *testing.T) { testCases := []*types.HistoryRespondDecisionTaskCompletedResponse{ nil, {}, &testdata.HistoryRespondDecisionTaskCompletedResponse, } for _, original := range testCases { thriftObj := FromHistoryRespondDecisionTaskCompletedResponse(original) roundTripObj := ToHistoryRespondDecisionTaskCompletedResponse(thriftObj) opt := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestHistoryRespondDecisionTaskFailedRequestConversion(t *testing.T) { testCases := []*types.HistoryRespondDecisionTaskFailedRequest{ nil, {}, &testdata.HistoryRespondDecisionTaskFailedRequest, } for _, original := range testCases { thriftObj := FromHistoryRespondDecisionTaskFailedRequest(original) roundTripObj := ToHistoryRespondDecisionTaskFailedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestScheduleDecisionTaskRequestConversion(t *testing.T) { testCases := []*types.ScheduleDecisionTaskRequest{ nil, {}, &testdata.HistoryScheduleDecisionTaskRequest, } for _, original := range testCases { thriftObj := FromHistoryScheduleDecisionTaskRequest(original) roundTripObj := ToHistoryScheduleDecisionTaskRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestShardOwnershipLostErrorConversion(t *testing.T) { testCases := []*types.ShardOwnershipLostError{ nil, {}, &testdata.ShardOwnershipLostError, } for _, original := range testCases { thriftObj := FromShardOwnershipLostError(original) roundTripObj := ToShardOwnershipLostError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistorySignalWithStartWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.HistorySignalWithStartWorkflowExecutionRequest{ nil, {}, &testdata.HistorySignalWithStartWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromHistorySignalWithStartWorkflowExecutionRequest(original) roundTripObj := ToHistorySignalWithStartWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistorySignalWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.HistorySignalWorkflowExecutionRequest{ nil, {}, &testdata.HistorySignalWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromHistorySignalWorkflowExecutionRequest(original) roundTripObj := ToHistorySignalWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryStartWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.HistoryStartWorkflowExecutionRequest{ nil, {}, &testdata.HistoryStartWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromHistoryStartWorkflowExecutionRequest(original) roundTripObj := ToHistoryStartWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSyncActivityRequestConversion(t *testing.T) { testCases := []*types.SyncActivityRequest{ nil, {}, { DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", Version: 1, ScheduledID: 1, ScheduledTime: common.Int64Ptr(98765), StartedID: 1, StartedTime: common.Int64Ptr(98765), LastHeartbeatTime: common.Int64Ptr(98765), Details: []byte("test-details"), Attempt: 3, LastFailureReason: common.StringPtr("test-last-failure-reason"), LastWorkerIdentity: "test-last-worker-identity", LastFailureDetails: []byte("test-last-failure-details"), VersionHistory: &testdata.VersionHistory, }, } for _, original := range testCases { thriftObj := FromHistorySyncActivityRequest(original) roundTripObj := ToHistorySyncActivityRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSyncShardStatusRequestConversion(t *testing.T) { testCases := []*types.SyncShardStatusRequest{ nil, {}, { SourceCluster: "test-source-cluster", ShardID: 1, Timestamp: common.Int64Ptr(98765), }, } for _, original := range testCases { thriftObj := FromHistorySyncShardStatusRequest(original) roundTripObj := ToHistorySyncShardStatusRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRatelimitUpdate(t *testing.T) { t.Run("nil", func(t *testing.T) { assert.Nil(t, ToHistoryRatelimitUpdateRequest(nil), "request to internal") assert.Nil(t, FromHistoryRatelimitUpdateResponse(nil), "response from internal") }) t.Run("nil Any contents", func(t *testing.T) { assert.Equal(t, &types.RatelimitUpdateRequest{Any: nil}, ToHistoryRatelimitUpdateRequest(&history.RatelimitUpdateRequest{Data: nil}), "request to internal") assert.Equal(t, &history.RatelimitUpdateResponse{Data: nil}, FromHistoryRatelimitUpdateResponse(&types.RatelimitUpdateResponse{Any: nil}), "response from internal") }) t.Run("with Any contents", func(t *testing.T) { internal := &types.Any{ValueType: "test", Value: []byte(`test data`)} thrift := &shared.Any{ValueType: common.StringPtr("test"), Value: []byte(`test data`)} assert.Equal(t, &types.RatelimitUpdateRequest{Any: internal}, ToHistoryRatelimitUpdateRequest(&history.RatelimitUpdateRequest{Data: thrift}), "request to internal") assert.Equal(t, &history.RatelimitUpdateResponse{Data: thrift}, FromHistoryRatelimitUpdateResponse(&types.RatelimitUpdateResponse{Any: internal}), "response from internal") }) } ================================================ FILE: common/types/mapper/thrift/matching.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "github.com/uber/cadence/.gen/go/matching" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) var ( FromMatchingDescribeTaskListResponse = FromDescribeTaskListResponse ToMatchingDescribeTaskListResponse = ToDescribeTaskListResponse FromMatchingGetTaskListsByDomainRequest = FromGetTaskListsByDomainRequest ToMatchingGetTaskListsByDomainRequest = ToGetTaskListsByDomainRequest FromMatchingGetTaskListsByDomainResponse = FromGetTaskListsByDomainResponse ToMatchingGetTaskListsByDomainResponse = ToGetTaskListsByDomainResponse FromMatchingListTaskListPartitionsResponse = FromListTaskListPartitionsResponse ToMatchingListTaskListPartitionsResponse = ToListTaskListPartitionsResponse ) // FromMatchingAddActivityTaskRequest converts internal AddActivityTaskRequest type to thrift func FromMatchingAddActivityTaskRequest(t *types.AddActivityTaskRequest) *matching.AddActivityTaskRequest { if t == nil { return nil } return &matching.AddActivityTaskRequest{ DomainUUID: &t.DomainUUID, Execution: FromWorkflowExecution(t.Execution), SourceDomainUUID: &t.SourceDomainUUID, TaskList: FromTaskList(t.TaskList), ScheduleId: &t.ScheduleID, ScheduleToStartTimeoutSeconds: t.ScheduleToStartTimeoutSeconds, Source: FromTaskSource(t.Source), ForwardedFrom: &t.ForwardedFrom, ActivityTaskDispatchInfo: FromActivityTaskDispatchInfo(t.ActivityTaskDispatchInfo), PartitionConfig: t.PartitionConfig, } } // ToMatchingAddActivityTaskRequest converts thrift AddActivityTaskRequest type to internal func ToMatchingAddActivityTaskRequest(t *matching.AddActivityTaskRequest) *types.AddActivityTaskRequest { if t == nil { return nil } return &types.AddActivityTaskRequest{ DomainUUID: t.GetDomainUUID(), Execution: ToWorkflowExecution(t.Execution), SourceDomainUUID: t.GetSourceDomainUUID(), TaskList: ToTaskList(t.TaskList), ScheduleID: t.GetScheduleId(), ScheduleToStartTimeoutSeconds: t.ScheduleToStartTimeoutSeconds, Source: ToTaskSource(t.Source), ForwardedFrom: t.GetForwardedFrom(), ActivityTaskDispatchInfo: ToActivityTaskDispatchInfo(t.ActivityTaskDispatchInfo), PartitionConfig: t.PartitionConfig, } } func FromActivityTaskDispatchInfo(t *types.ActivityTaskDispatchInfo) *matching.ActivityTaskDispatchInfo { if t == nil { return nil } return &matching.ActivityTaskDispatchInfo{ ScheduledEvent: FromHistoryEvent(t.ScheduledEvent), StartedTimestamp: t.StartedTimestamp, Attempt: t.Attempt, ScheduledTimestampOfThisAttempt: t.ScheduledTimestampOfThisAttempt, HeartbeatDetails: t.HeartbeatDetails, WorkflowType: FromWorkflowType(t.WorkflowType), WorkflowDomain: &t.WorkflowDomain, } } // ToRecordActivityTaskStartedResponse converts thrift RecordActivityTaskStartedResponse type to internal func ToActivityTaskDispatchInfo(t *matching.ActivityTaskDispatchInfo) *types.ActivityTaskDispatchInfo { if t == nil { return nil } return &types.ActivityTaskDispatchInfo{ ScheduledEvent: ToHistoryEvent(t.ScheduledEvent), StartedTimestamp: t.StartedTimestamp, Attempt: t.Attempt, ScheduledTimestampOfThisAttempt: t.ScheduledTimestampOfThisAttempt, HeartbeatDetails: t.HeartbeatDetails, WorkflowType: ToWorkflowType(t.WorkflowType), WorkflowDomain: common.StringDefault(t.WorkflowDomain), } } // FromMatchingAddDecisionTaskRequest converts internal AddDecisionTaskRequest type to thrift func FromMatchingAddDecisionTaskRequest(t *types.AddDecisionTaskRequest) *matching.AddDecisionTaskRequest { if t == nil { return nil } return &matching.AddDecisionTaskRequest{ DomainUUID: &t.DomainUUID, Execution: FromWorkflowExecution(t.Execution), TaskList: FromTaskList(t.TaskList), ScheduleId: &t.ScheduleID, ScheduleToStartTimeoutSeconds: t.ScheduleToStartTimeoutSeconds, Source: FromTaskSource(t.Source), ForwardedFrom: &t.ForwardedFrom, PartitionConfig: t.PartitionConfig, } } // ToMatchingAddDecisionTaskRequest converts thrift AddDecisionTaskRequest type to internal func ToMatchingAddDecisionTaskRequest(t *matching.AddDecisionTaskRequest) *types.AddDecisionTaskRequest { if t == nil { return nil } return &types.AddDecisionTaskRequest{ DomainUUID: t.GetDomainUUID(), Execution: ToWorkflowExecution(t.Execution), TaskList: ToTaskList(t.TaskList), ScheduleID: t.GetScheduleId(), ScheduleToStartTimeoutSeconds: t.ScheduleToStartTimeoutSeconds, Source: ToTaskSource(t.Source), ForwardedFrom: t.GetForwardedFrom(), PartitionConfig: t.PartitionConfig, } } // FromMatchingCancelOutstandingPollRequest converts internal CancelOutstandingPollRequest type to thrift func FromMatchingCancelOutstandingPollRequest(t *types.CancelOutstandingPollRequest) *matching.CancelOutstandingPollRequest { if t == nil { return nil } return &matching.CancelOutstandingPollRequest{ DomainUUID: &t.DomainUUID, TaskListType: t.TaskListType, TaskList: FromTaskList(t.TaskList), PollerID: &t.PollerID, } } // ToMatchingCancelOutstandingPollRequest converts thrift CancelOutstandingPollRequest type to internal func ToMatchingCancelOutstandingPollRequest(t *matching.CancelOutstandingPollRequest) *types.CancelOutstandingPollRequest { if t == nil { return nil } return &types.CancelOutstandingPollRequest{ DomainUUID: t.GetDomainUUID(), TaskListType: t.TaskListType, TaskList: ToTaskList(t.TaskList), PollerID: t.GetPollerID(), } } // FromMatchingDescribeTaskListRequest converts internal DescribeTaskListRequest type to thrift func FromMatchingDescribeTaskListRequest(t *types.MatchingDescribeTaskListRequest) *matching.DescribeTaskListRequest { if t == nil { return nil } return &matching.DescribeTaskListRequest{ DomainUUID: &t.DomainUUID, DescRequest: FromDescribeTaskListRequest(t.DescRequest), } } // ToMatchingDescribeTaskListRequest converts thrift DescribeTaskListRequest type to internal func ToMatchingDescribeTaskListRequest(t *matching.DescribeTaskListRequest) *types.MatchingDescribeTaskListRequest { if t == nil { return nil } return &types.MatchingDescribeTaskListRequest{ DomainUUID: t.GetDomainUUID(), DescRequest: ToDescribeTaskListRequest(t.DescRequest), } } // FromMatchingListTaskListPartitionsRequest converts internal ListTaskListPartitionsRequest type to thrift func FromMatchingListTaskListPartitionsRequest(t *types.MatchingListTaskListPartitionsRequest) *matching.ListTaskListPartitionsRequest { if t == nil { return nil } return &matching.ListTaskListPartitionsRequest{ Domain: &t.Domain, TaskList: FromTaskList(t.TaskList), } } // ToMatchingListTaskListPartitionsRequest converts thrift ListTaskListPartitionsRequest type to internal func ToMatchingListTaskListPartitionsRequest(t *matching.ListTaskListPartitionsRequest) *types.MatchingListTaskListPartitionsRequest { if t == nil { return nil } return &types.MatchingListTaskListPartitionsRequest{ Domain: t.GetDomain(), TaskList: ToTaskList(t.TaskList), } } // FromMatchingPollForActivityTaskRequest converts internal PollForActivityTaskRequest type to thrift func FromMatchingPollForActivityTaskRequest(t *types.MatchingPollForActivityTaskRequest) *matching.PollForActivityTaskRequest { if t == nil { return nil } return &matching.PollForActivityTaskRequest{ DomainUUID: &t.DomainUUID, PollerID: &t.PollerID, PollRequest: FromPollForActivityTaskRequest(t.PollRequest), ForwardedFrom: &t.ForwardedFrom, IsolationGroup: &t.IsolationGroup, } } // ToMatchingPollForActivityTaskRequest converts thrift PollForActivityTaskRequest type to internal func ToMatchingPollForActivityTaskRequest(t *matching.PollForActivityTaskRequest) *types.MatchingPollForActivityTaskRequest { if t == nil { return nil } return &types.MatchingPollForActivityTaskRequest{ DomainUUID: t.GetDomainUUID(), PollerID: t.GetPollerID(), PollRequest: ToPollForActivityTaskRequest(t.PollRequest), ForwardedFrom: t.GetForwardedFrom(), IsolationGroup: t.GetIsolationGroup(), } } // FromMatchingPollForDecisionTaskRequest converts internal PollForDecisionTaskRequest type to thrift func FromMatchingPollForDecisionTaskRequest(t *types.MatchingPollForDecisionTaskRequest) *matching.PollForDecisionTaskRequest { if t == nil { return nil } return &matching.PollForDecisionTaskRequest{ DomainUUID: &t.DomainUUID, PollerID: &t.PollerID, PollRequest: FromPollForDecisionTaskRequest(t.PollRequest), ForwardedFrom: &t.ForwardedFrom, IsolationGroup: &t.IsolationGroup, } } // ToMatchingPollForDecisionTaskRequest converts thrift PollForDecisionTaskRequest type to internal func ToMatchingPollForDecisionTaskRequest(t *matching.PollForDecisionTaskRequest) *types.MatchingPollForDecisionTaskRequest { if t == nil { return nil } return &types.MatchingPollForDecisionTaskRequest{ DomainUUID: t.GetDomainUUID(), PollerID: t.GetPollerID(), PollRequest: ToPollForDecisionTaskRequest(t.PollRequest), ForwardedFrom: t.GetForwardedFrom(), IsolationGroup: t.GetIsolationGroup(), } } // FromMatchingPollForDecisionTaskResponse converts internal PollForDecisionTaskResponse type to thrift func FromMatchingPollForDecisionTaskResponse(t *types.MatchingPollForDecisionTaskResponse) *matching.PollForDecisionTaskResponse { if t == nil { return nil } return &matching.PollForDecisionTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), PreviousStartedEventId: t.PreviousStartedEventID, StartedEventId: &t.StartedEventID, Attempt: &t.Attempt, NextEventId: &t.NextEventID, BacklogCountHint: &t.BacklogCountHint, StickyExecutionEnabled: &t.StickyExecutionEnabled, Query: FromWorkflowQuery(t.Query), DecisionInfo: FromTransientDecisionInfo(t.DecisionInfo), WorkflowExecutionTaskList: FromTaskList(t.WorkflowExecutionTaskList), EventStoreVersion: &t.EventStoreVersion, BranchToken: t.BranchToken, ScheduledTimestamp: t.ScheduledTimestamp, StartedTimestamp: t.StartedTimestamp, Queries: FromWorkflowQueryMap(t.Queries), TotalHistoryBytes: &t.TotalHistoryBytes, AutoConfigHint: FromAutoConfigHint(t.AutoConfigHint), } } // ToMatchingPollForDecisionTaskResponse converts thrift PollForDecisionTaskResponse type to internal func ToMatchingPollForDecisionTaskResponse(t *matching.PollForDecisionTaskResponse) *types.MatchingPollForDecisionTaskResponse { if t == nil { return nil } return &types.MatchingPollForDecisionTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), PreviousStartedEventID: t.PreviousStartedEventId, StartedEventID: t.GetStartedEventId(), Attempt: t.GetAttempt(), NextEventID: t.GetNextEventId(), BacklogCountHint: t.GetBacklogCountHint(), StickyExecutionEnabled: t.GetStickyExecutionEnabled(), Query: ToWorkflowQuery(t.Query), DecisionInfo: ToTransientDecisionInfo(t.DecisionInfo), WorkflowExecutionTaskList: ToTaskList(t.WorkflowExecutionTaskList), EventStoreVersion: t.GetEventStoreVersion(), BranchToken: t.BranchToken, ScheduledTimestamp: t.ScheduledTimestamp, StartedTimestamp: t.StartedTimestamp, Queries: ToWorkflowQueryMap(t.Queries), TotalHistoryBytes: t.GetTotalHistoryBytes(), AutoConfigHint: ToAutoConfigHint(t.AutoConfigHint), } } func FromMatchingPollForActivityTaskResponse(t *types.MatchingPollForActivityTaskResponse) *shared.PollForActivityTaskResponse { if t == nil { return nil } return &shared.PollForActivityTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), ActivityId: &t.ActivityID, ActivityType: FromActivityType(t.ActivityType), Input: t.Input, ScheduledTimestamp: t.ScheduledTimestamp, ScheduleToCloseTimeoutSeconds: t.ScheduleToCloseTimeoutSeconds, StartedTimestamp: t.StartedTimestamp, StartToCloseTimeoutSeconds: t.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: t.HeartbeatTimeoutSeconds, Attempt: &t.Attempt, ScheduledTimestampOfThisAttempt: t.ScheduledTimestampOfThisAttempt, HeartbeatDetails: t.HeartbeatDetails, WorkflowType: FromWorkflowType(t.WorkflowType), WorkflowDomain: &t.WorkflowDomain, Header: FromHeader(t.Header), AutoConfigHint: FromAutoConfigHint(t.AutoConfigHint), } } func ToMatchingPollForActivityTaskResponse(t *shared.PollForActivityTaskResponse) *types.MatchingPollForActivityTaskResponse { if t == nil { return nil } return &types.MatchingPollForActivityTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), ActivityID: t.GetActivityId(), ActivityType: ToActivityType(t.ActivityType), Input: t.Input, ScheduledTimestamp: t.ScheduledTimestamp, ScheduleToCloseTimeoutSeconds: t.ScheduleToCloseTimeoutSeconds, StartedTimestamp: t.StartedTimestamp, StartToCloseTimeoutSeconds: t.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: t.HeartbeatTimeoutSeconds, Attempt: t.GetAttempt(), ScheduledTimestampOfThisAttempt: t.ScheduledTimestampOfThisAttempt, HeartbeatDetails: t.HeartbeatDetails, WorkflowType: ToWorkflowType(t.WorkflowType), WorkflowDomain: t.GetWorkflowDomain(), Header: ToHeader(t.Header), AutoConfigHint: ToAutoConfigHint(t.AutoConfigHint), } } // FromMatchingQueryWorkflowRequest converts internal QueryWorkflowRequest type to thrift func FromMatchingQueryWorkflowRequest(t *types.MatchingQueryWorkflowRequest) *matching.QueryWorkflowRequest { if t == nil { return nil } return &matching.QueryWorkflowRequest{ DomainUUID: &t.DomainUUID, TaskList: FromTaskList(t.TaskList), QueryRequest: FromQueryWorkflowRequest(t.QueryRequest), ForwardedFrom: &t.ForwardedFrom, } } // ToMatchingQueryWorkflowRequest converts thrift QueryWorkflowRequest type to internal func ToMatchingQueryWorkflowRequest(t *matching.QueryWorkflowRequest) *types.MatchingQueryWorkflowRequest { if t == nil { return nil } return &types.MatchingQueryWorkflowRequest{ DomainUUID: t.GetDomainUUID(), TaskList: ToTaskList(t.TaskList), QueryRequest: ToQueryWorkflowRequest(t.QueryRequest), ForwardedFrom: t.GetForwardedFrom(), } } // FromMatchingRespondQueryTaskCompletedRequest converts internal RespondQueryTaskCompletedRequest type to thrift func FromMatchingRespondQueryTaskCompletedRequest(t *types.MatchingRespondQueryTaskCompletedRequest) *matching.RespondQueryTaskCompletedRequest { if t == nil { return nil } return &matching.RespondQueryTaskCompletedRequest{ DomainUUID: &t.DomainUUID, TaskList: FromTaskList(t.TaskList), TaskID: &t.TaskID, CompletedRequest: FromRespondQueryTaskCompletedRequest(t.CompletedRequest), } } // ToMatchingRespondQueryTaskCompletedRequest converts thrift RespondQueryTaskCompletedRequest type to internal func ToMatchingRespondQueryTaskCompletedRequest(t *matching.RespondQueryTaskCompletedRequest) *types.MatchingRespondQueryTaskCompletedRequest { if t == nil { return nil } return &types.MatchingRespondQueryTaskCompletedRequest{ DomainUUID: t.GetDomainUUID(), TaskList: ToTaskList(t.TaskList), TaskID: t.GetTaskID(), CompletedRequest: ToRespondQueryTaskCompletedRequest(t.CompletedRequest), } } // FromTaskSource converts internal TaskSource type to thrift func FromTaskSource(t *types.TaskSource) *matching.TaskSource { if t == nil { return nil } switch *t { case types.TaskSourceHistory: v := matching.TaskSourceHistory return &v case types.TaskSourceDbBacklog: v := matching.TaskSourceDbBacklog return &v } panic("unexpected enum value") } // ToTaskSource converts thrift TaskSource type to internal func ToTaskSource(t *matching.TaskSource) *types.TaskSource { if t == nil { return nil } switch *t { case matching.TaskSourceHistory: v := types.TaskSourceHistory return &v case matching.TaskSourceDbBacklog: v := types.TaskSourceDbBacklog return &v } panic("unexpected enum value") } func FromMatchingQueryWorkflowResponse(t *types.MatchingQueryWorkflowResponse) *shared.QueryWorkflowResponse { if t == nil { return nil } return &shared.QueryWorkflowResponse{ QueryResult: t.QueryResult, QueryRejected: FromQueryRejected(t.QueryRejected), } } func ToMatchingQueryWorkflowResponse(t *shared.QueryWorkflowResponse) *types.MatchingQueryWorkflowResponse { if t == nil { return nil } return &types.MatchingQueryWorkflowResponse{ QueryResult: t.QueryResult, QueryRejected: ToQueryRejected(t.QueryRejected), } } ================================================ FILE: common/types/mapper/thrift/matching_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" ) func TestMatchingAddActivityTaskRequest(t *testing.T) { testCases := []struct { desc string input *types.AddActivityTaskRequest }{ { desc: "non-nil input test", input: &testdata.MatchingAddActivityTaskRequest, }, { desc: "empty input test", input: &types.AddActivityTaskRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingAddActivityTaskRequest(tc.input) roundTripObj := ToMatchingAddActivityTaskRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestActivityTaskDispatchInfo(t *testing.T) { testCases := []struct { desc string input *types.ActivityTaskDispatchInfo }{ { desc: "non-nil input test", input: &testdata.MatchingActivityTaskDispatchInfo, }, { desc: "empty input test", input: &types.ActivityTaskDispatchInfo{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromActivityTaskDispatchInfo(tc.input) roundTripObj := ToActivityTaskDispatchInfo(thriftObj) opts := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") if diff := cmp.Diff(tc.input, roundTripObj, opts); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestMatchingAddDecisionTaskRequest(t *testing.T) { testCases := []struct { desc string input *types.AddDecisionTaskRequest }{ { desc: "non-nil input test", input: &testdata.MatchingAddDecisionTaskRequest, }, { desc: "empty input test", input: &types.AddDecisionTaskRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingAddDecisionTaskRequest(tc.input) roundTripObj := ToMatchingAddDecisionTaskRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestMatchingCancelOutStandingPollRequest(t *testing.T) { testCases := []struct { desc string input *types.CancelOutstandingPollRequest }{ { desc: "non-nil input test", input: &testdata.MatchingCancelOutstandingPollRequest, }, { desc: "empty input test", input: &types.CancelOutstandingPollRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingCancelOutstandingPollRequest(tc.input) roundTripObj := ToMatchingCancelOutstandingPollRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestMatchingDescribeTaskListRequest(t *testing.T) { testCases := []struct { desc string input *types.MatchingDescribeTaskListRequest }{ { desc: "non-nil input test", input: &testdata.MatchingDescribeTaskListRequest, }, { desc: "empty input test", input: &types.MatchingDescribeTaskListRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingDescribeTaskListRequest(tc.input) roundTripObj := ToMatchingDescribeTaskListRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestMatchingListTaskListPartitionsRequest(t *testing.T) { testCases := []struct { desc string input *types.MatchingListTaskListPartitionsRequest }{ { desc: "non-nil input test", input: &testdata.MatchingListTaskListPartitionsRequest, }, { desc: "empty input test", input: &types.MatchingListTaskListPartitionsRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingListTaskListPartitionsRequest(tc.input) roundTripObj := ToMatchingListTaskListPartitionsRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestMatchingPollForActivityRequest(t *testing.T) { testCases := []struct { desc string input *types.MatchingPollForActivityTaskRequest }{ { desc: "non-nil input test", input: &testdata.MatchingPollForActivityTaskRequest, }, { desc: "empty input test", input: &types.MatchingPollForActivityTaskRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingPollForActivityTaskRequest(tc.input) roundTripObj := ToMatchingPollForActivityTaskRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestMatchingPollForDecisionRequest(t *testing.T) { testCases := []struct { desc string input *types.MatchingPollForDecisionTaskRequest }{ { desc: "non-nil input test", input: &testdata.MatchingPollForDecisionTaskRequest, }, { desc: "empty input test", input: &types.MatchingPollForDecisionTaskRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingPollForDecisionTaskRequest(tc.input) roundTripObj := ToMatchingPollForDecisionTaskRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestMatchingPollForDecisionResponse(t *testing.T) { testCases := []struct { desc string input *types.MatchingPollForDecisionTaskResponse }{ { desc: "non-nil input test", input: &testdata.MatchingPollForDecisionTaskResponse, }, { desc: "empty input test", input: &types.MatchingPollForDecisionTaskResponse{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingPollForDecisionTaskResponse(tc.input) roundTripObj := ToMatchingPollForDecisionTaskResponse(thriftObj) opt := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") opt2 := cmpopts.IgnoreFields(types.MatchingPollForDecisionTaskResponse{}, "PartitionConfig") opt3 := cmpopts.IgnoreFields(types.MatchingPollForDecisionTaskResponse{}, "LoadBalancerHints") if diff := cmp.Diff(tc.input, roundTripObj, opt, opt2, opt3); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestMatchingPollForActivityTaskResponse(t *testing.T) { testCases := []struct { desc string input *types.MatchingPollForActivityTaskResponse }{ { desc: "non-nil input test", input: &testdata.MatchingPollForActivityTaskResponse, }, { desc: "empty input test", input: &types.MatchingPollForActivityTaskResponse{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingPollForActivityTaskResponse(tc.input) roundTripObj := ToMatchingPollForActivityTaskResponse(thriftObj) opt := cmpopts.IgnoreFields(types.MatchingPollForActivityTaskResponse{}, "PartitionConfig") opt2 := cmpopts.IgnoreFields(types.MatchingPollForActivityTaskResponse{}, "LoadBalancerHints") if diff := cmp.Diff(tc.input, roundTripObj, opt, opt2); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestMatchingQueryWorkflowRequest(t *testing.T) { testCases := []struct { desc string input *types.MatchingQueryWorkflowRequest }{ { desc: "non-nil input test", input: &testdata.MatchingQueryWorkflowRequest, }, { desc: "empty input test", input: &types.MatchingQueryWorkflowRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingQueryWorkflowRequest(tc.input) roundTripObj := ToMatchingQueryWorkflowRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestMatchingRespondQueryTaskCompletedRequest(t *testing.T) { testCases := []struct { desc string input *types.MatchingRespondQueryTaskCompletedRequest }{ { desc: "non-nil input test", input: &testdata.MatchingRespondQueryTaskCompletedRequest, }, { desc: "empty input test", input: &types.MatchingRespondQueryTaskCompletedRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromMatchingRespondQueryTaskCompletedRequest(tc.input) roundTripObj := ToMatchingRespondQueryTaskCompletedRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } func TestTaskSource(t *testing.T) { testCases := []struct { desc string input *types.TaskSource }{ { desc: "non-nil TaskSourceHistory input test", input: types.TaskSourceHistory.Ptr(), }, { desc: "non-nil TaskSourceDBBacklog input test", input: types.TaskSourceDbBacklog.Ptr(), }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { thriftObj := FromTaskSource(tc.input) roundTripObj := ToTaskSource(thriftObj) assert.Equal(t, tc.input, roundTripObj) } } ================================================ FILE: common/types/mapper/thrift/predicate.go ================================================ package thrift import ( "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/types" ) func FromPredicateType(t types.PredicateType) *shared.PredicateType { v := shared.PredicateTypeUniversal switch t { case types.PredicateTypeUniversal: return &v case types.PredicateTypeEmpty: v = shared.PredicateTypeEmpty return &v case types.PredicateTypeDomainID: v = shared.PredicateTypeDomainID return &v } // default to universal return &v } func ToPredicateType(t *shared.PredicateType) types.PredicateType { if t == nil { return types.PredicateTypeUniversal } switch *t { case shared.PredicateTypeUniversal: return types.PredicateTypeUniversal case shared.PredicateTypeEmpty: return types.PredicateTypeEmpty case shared.PredicateTypeDomainID: return types.PredicateTypeDomainID } // default to universal return types.PredicateTypeUniversal } func FromUniversalPredicateAttributes(a *types.UniversalPredicateAttributes) *shared.UniversalPredicateAttributes { if a == nil { return nil } return &shared.UniversalPredicateAttributes{} } func ToUniversalPredicateAttributes(a *shared.UniversalPredicateAttributes) *types.UniversalPredicateAttributes { if a == nil { return nil } return &types.UniversalPredicateAttributes{} } func FromEmptyPredicateAttributes(a *types.EmptyPredicateAttributes) *shared.EmptyPredicateAttributes { if a == nil { return nil } return &shared.EmptyPredicateAttributes{} } func ToEmptyPredicateAttributes(a *shared.EmptyPredicateAttributes) *types.EmptyPredicateAttributes { if a == nil { return nil } return &types.EmptyPredicateAttributes{} } func FromDomainIDPredicateAttributes(a *types.DomainIDPredicateAttributes) *shared.DomainIDPredicateAttributes { if a == nil { return nil } return &shared.DomainIDPredicateAttributes{ DomainIDs: a.DomainIDs, IsExclusive: a.IsExclusive, } } func ToDomainIDPredicateAttributes(a *shared.DomainIDPredicateAttributes) *types.DomainIDPredicateAttributes { if a == nil { return nil } return &types.DomainIDPredicateAttributes{ DomainIDs: a.DomainIDs, IsExclusive: a.IsExclusive, } } func FromPredicate(p *types.Predicate) *shared.Predicate { if p == nil { return nil } return &shared.Predicate{ PredicateType: FromPredicateType(p.PredicateType), UniversalPredicateAttributes: FromUniversalPredicateAttributes(p.UniversalPredicateAttributes), EmptyPredicateAttributes: FromEmptyPredicateAttributes(p.EmptyPredicateAttributes), DomainIDPredicateAttributes: FromDomainIDPredicateAttributes(p.DomainIDPredicateAttributes), } } func ToPredicate(p *shared.Predicate) *types.Predicate { if p == nil { return nil } return &types.Predicate{ PredicateType: ToPredicateType(p.PredicateType), UniversalPredicateAttributes: ToUniversalPredicateAttributes(p.UniversalPredicateAttributes), EmptyPredicateAttributes: ToEmptyPredicateAttributes(p.EmptyPredicateAttributes), DomainIDPredicateAttributes: ToDomainIDPredicateAttributes(p.DomainIDPredicateAttributes), } } ================================================ FILE: common/types/mapper/thrift/predicate_test.go ================================================ // Copyright (c) 2017-2022 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "testing" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) func predicateTypeFuzzGenerator(t *types.PredicateType, c fuzz.Continue) { switch c.Intn(int(types.NumPredicateTypes)) { case 0: *t = types.PredicateTypeUniversal case 1: *t = types.PredicateTypeEmpty case 2: *t = types.PredicateTypeDomainID default: panic("invalid predicate type") } } func predicateTypeSharedFuzzGenerator(t **shared.PredicateType, c fuzz.Continue) { switch c.Intn(int(types.NumPredicateTypes)) { case 0: *t = common.Ptr(shared.PredicateTypeUniversal) case 1: *t = common.Ptr(shared.PredicateTypeEmpty) case 2: *t = common.Ptr(shared.PredicateTypeDomainID) default: panic("invalid predicate type") } } func predicateFuzzGenerator(t *types.Predicate, c fuzz.Continue) { switch c.Intn(int(types.NumPredicateTypes)) { case 0: t.PredicateType = types.PredicateTypeUniversal c.Fuzz(&t.UniversalPredicateAttributes) case 1: t.PredicateType = types.PredicateTypeEmpty c.Fuzz(&t.EmptyPredicateAttributes) case 2: t.PredicateType = types.PredicateTypeDomainID c.Fuzz(&t.DomainIDPredicateAttributes) default: panic("invalid predicate type") } } func predicateSharedFuzzGenerator(t *shared.Predicate, c fuzz.Continue) { switch c.Intn(int(types.NumPredicateTypes)) { case 0: t.PredicateType = common.Ptr(shared.PredicateTypeUniversal) c.Fuzz(&t.UniversalPredicateAttributes) case 1: t.PredicateType = common.Ptr(shared.PredicateTypeEmpty) c.Fuzz(&t.EmptyPredicateAttributes) case 2: t.PredicateType = common.Ptr(shared.PredicateTypeDomainID) c.Fuzz(&t.DomainIDPredicateAttributes) default: panic("invalid predicate type") } } func TestFuzzPredicateType_Roundtrip_FromTypes(t *testing.T) { f := fuzz.New().Funcs( predicateTypeFuzzGenerator, ) for i := 0; i < 1000; i++ { // Generate only valid enum values var original types.PredicateType f.Fuzz(&original) // types → shared → types shared := FromPredicateType(original) converted := ToPredicateType(shared) assert.Equal(t, original, converted, "PredicateType roundtrip failed: types → shared → types") } } func TestFuzzPredicateType_Roundtrip_FromShared(t *testing.T) { f := fuzz.New().Funcs( predicateTypeSharedFuzzGenerator, ) for i := 0; i < 1000; i++ { // Generate only valid enum values var original *shared.PredicateType f.Fuzz(&original) // shared → types → shared types := ToPredicateType(original) converted := FromPredicateType(types) assert.Equal(t, original, converted, "PredicateType roundtrip failed: shared → types → shared") } } func TestFuzzUniversalPredicateAttributes_Roundtrip_FromTypes(t *testing.T) { f := fuzz.New() for i := 0; i < 1000; i++ { var original types.UniversalPredicateAttributes f.Fuzz(&original) // types → shared → types shared := FromUniversalPredicateAttributes(&original) converted := ToUniversalPredicateAttributes(shared) assert.Equal(t, original, *converted, "UniversalPredicateAttributes roundtrip failed: types → shared → types") } } func TestFuzzUniversalPredicateAttributes_Roundtrip_FromShared(t *testing.T) { f := fuzz.New() for i := 0; i < 1000; i++ { var original shared.UniversalPredicateAttributes f.Fuzz(&original) // shared → types → shared types := ToUniversalPredicateAttributes(&original) converted := FromUniversalPredicateAttributes(types) assert.Equal(t, original, *converted, "UniversalPredicateAttributes roundtrip failed: shared → types → shared") } } func TestFuzzEmptyPredicateAttributes_Roundtrip_FromTypes(t *testing.T) { f := fuzz.New() for i := 0; i < 1000; i++ { var original types.EmptyPredicateAttributes f.Fuzz(&original) // types → shared → types shared := FromEmptyPredicateAttributes(&original) converted := ToEmptyPredicateAttributes(shared) assert.Equal(t, original, *converted, "EmptyPredicateAttributes roundtrip failed: types → shared → types") } } func TestFuzzEmptyPredicateAttributes_Roundtrip_FromShared(t *testing.T) { f := fuzz.New() for i := 0; i < 1000; i++ { var original shared.EmptyPredicateAttributes f.Fuzz(&original) // shared → types → shared types := ToEmptyPredicateAttributes(&original) converted := FromEmptyPredicateAttributes(types) assert.Equal(t, original, *converted, "EmptyPredicateAttributes roundtrip failed: shared → types → shared") } } func TestFuzzDomainIDPredicateAttributes_Roundtrip_FromTypes(t *testing.T) { f := fuzz.New().NilChance(0.1) for i := 0; i < 1000; i++ { var original types.DomainIDPredicateAttributes f.Fuzz(&original) // types → shared → types shared := FromDomainIDPredicateAttributes(&original) converted := ToDomainIDPredicateAttributes(shared) assert.Equal(t, original, *converted, "DomainIDPredicateAttributes roundtrip failed: types → shared → types") } } func TestFuzzDomainIDPredicateAttributes_Roundtrip_FromShared(t *testing.T) { f := fuzz.New().NilChance(0.1) for i := 0; i < 1000; i++ { var original shared.DomainIDPredicateAttributes f.Fuzz(&original) // shared → types → shared types := ToDomainIDPredicateAttributes(&original) converted := FromDomainIDPredicateAttributes(types) assert.Equal(t, original, *converted, "DomainIDPredicateAttributes roundtrip failed: shared → types → shared") } } func TestFuzzPredicate_Roundtrip_FromTypes(t *testing.T) { f := fuzz.New().NilChance(0.2).Funcs( predicateFuzzGenerator, ).MaxDepth(15) // Limit depth to avoid infinite recursion for i := 0; i < 1000; i++ { var original types.Predicate f.Fuzz(&original) // types → shared → types shared := FromPredicate(&original) converted := ToPredicate(shared) assert.Equal(t, original, *converted, "Predicate roundtrip failed: types → shared → types") } } func TestFuzzPredicate_Roundtrip_FromShared(t *testing.T) { f := fuzz.New().NilChance(0.2).Funcs( predicateSharedFuzzGenerator, ).MaxDepth(15) // Limit depth to avoid infinite recursion for i := 0; i < 1000; i++ { var original shared.Predicate f.Fuzz(&original) // shared → types → shared types := ToPredicate(&original) converted := FromPredicate(types) assert.Equal(t, original, *converted, "Predicate roundtrip failed: shared → types → shared") } } func TestFuzzPredicate_NilHandling(t *testing.T) { // Test nil handling assert.Nil(t, FromPredicate(nil)) assert.Nil(t, ToPredicate(nil)) assert.Nil(t, FromDomainIDPredicateAttributes(nil)) assert.Nil(t, ToDomainIDPredicateAttributes(nil)) } func TestPredicateType_InvalidValues(t *testing.T) { assert.Equal(t, shared.PredicateTypeUniversal, *FromPredicateType(types.PredicateType(100))) assert.Equal(t, types.PredicateTypeUniversal, ToPredicateType(nil)) assert.Equal(t, types.PredicateTypeUniversal, ToPredicateType(common.Ptr(shared.PredicateType(100)))) } ================================================ FILE: common/types/mapper/thrift/replicator.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "github.com/uber/cadence/.gen/go/replicator" "github.com/uber/cadence/common/types" ) // FromDLQType converts internal DLQType type to thrift func FromDLQType(t *types.DLQType) *replicator.DLQType { if t == nil { return nil } switch *t { case types.DLQTypeReplication: v := replicator.DLQTypeReplication return &v case types.DLQTypeDomain: v := replicator.DLQTypeDomain return &v } panic("unexpected enum value") } // ToDLQType converts thrift DLQType type to internal func ToDLQType(t *replicator.DLQType) *types.DLQType { if t == nil { return nil } switch *t { case replicator.DLQTypeReplication: v := types.DLQTypeReplication return &v case replicator.DLQTypeDomain: v := types.DLQTypeDomain return &v } panic("unexpected enum value") } // FromDomainOperation converts internal DomainOperation type to thrift func FromDomainOperation(t *types.DomainOperation) *replicator.DomainOperation { if t == nil { return nil } switch *t { case types.DomainOperationCreate: v := replicator.DomainOperationCreate return &v case types.DomainOperationUpdate: v := replicator.DomainOperationUpdate return &v case types.DomainOperationDelete: v := replicator.DomainOperationDelete return &v } panic("unexpected enum value") } // ToDomainOperation converts thrift DomainOperation type to internal func ToDomainOperation(t *replicator.DomainOperation) *types.DomainOperation { if t == nil { return nil } switch *t { case replicator.DomainOperationCreate: v := types.DomainOperationCreate return &v case replicator.DomainOperationUpdate: v := types.DomainOperationUpdate return &v case replicator.DomainOperationDelete: v := types.DomainOperationDelete return &v } panic("unexpected enum value") } // FromDomainTaskAttributes converts internal DomainTaskAttributes type to thrift func FromDomainTaskAttributes(t *types.DomainTaskAttributes) *replicator.DomainTaskAttributes { if t == nil { return nil } return &replicator.DomainTaskAttributes{ DomainOperation: FromDomainOperation(t.DomainOperation), ID: &t.ID, Info: FromDomainInfo(t.Info), Config: FromDomainConfiguration(t.Config), ReplicationConfig: FromDomainReplicationConfiguration(t.ReplicationConfig), ConfigVersion: &t.ConfigVersion, FailoverVersion: &t.FailoverVersion, PreviousFailoverVersion: &t.PreviousFailoverVersion, } } // ToDomainTaskAttributes converts thrift DomainTaskAttributes type to internal func ToDomainTaskAttributes(t *replicator.DomainTaskAttributes) *types.DomainTaskAttributes { if t == nil { return nil } return &types.DomainTaskAttributes{ DomainOperation: ToDomainOperation(t.DomainOperation), ID: t.GetID(), Info: ToDomainInfo(t.Info), Config: ToDomainConfiguration(t.Config), ReplicationConfig: ToDomainReplicationConfiguration(t.ReplicationConfig), ConfigVersion: t.GetConfigVersion(), FailoverVersion: t.GetFailoverVersion(), PreviousFailoverVersion: t.GetPreviousFailoverVersion(), } } // FromFailoverMarkerAttributes converts internal FailoverMarkerAttributes type to thrift func FromFailoverMarkerAttributes(t *types.FailoverMarkerAttributes) *replicator.FailoverMarkerAttributes { if t == nil { return nil } return &replicator.FailoverMarkerAttributes{ DomainID: &t.DomainID, FailoverVersion: &t.FailoverVersion, CreationTime: t.CreationTime, } } // ToFailoverMarkerAttributes converts thrift FailoverMarkerAttributes type to internal func ToFailoverMarkerAttributes(t *replicator.FailoverMarkerAttributes) *types.FailoverMarkerAttributes { if t == nil { return nil } return &types.FailoverMarkerAttributes{ DomainID: t.GetDomainID(), FailoverVersion: t.GetFailoverVersion(), CreationTime: t.CreationTime, } } // FromFailoverMarkers converts internal FailoverMarkers type to thrift func FromFailoverMarkers(t *types.FailoverMarkers) *replicator.FailoverMarkers { if t == nil { return nil } return &replicator.FailoverMarkers{ FailoverMarkers: FromFailoverMarkerAttributesArray(t.FailoverMarkers), } } // ToFailoverMarkers converts thrift FailoverMarkers type to internal func ToFailoverMarkers(t *replicator.FailoverMarkers) *types.FailoverMarkers { if t == nil { return nil } return &types.FailoverMarkers{ FailoverMarkers: ToFailoverMarkerAttributesArray(t.FailoverMarkers), } } // FromAdminGetDLQReplicationMessagesRequest converts internal GetDLQReplicationMessagesRequest type to thrift func FromAdminGetDLQReplicationMessagesRequest(t *types.GetDLQReplicationMessagesRequest) *replicator.GetDLQReplicationMessagesRequest { if t == nil { return nil } return &replicator.GetDLQReplicationMessagesRequest{ TaskInfos: FromReplicationTaskInfoArray(t.TaskInfos), } } // ToAdminGetDLQReplicationMessagesRequest converts thrift GetDLQReplicationMessagesRequest type to internal func ToAdminGetDLQReplicationMessagesRequest(t *replicator.GetDLQReplicationMessagesRequest) *types.GetDLQReplicationMessagesRequest { if t == nil { return nil } return &types.GetDLQReplicationMessagesRequest{ TaskInfos: ToReplicationTaskInfoArray(t.TaskInfos), } } // FromAdminGetDLQReplicationMessagesResponse converts internal GetDLQReplicationMessagesResponse type to thrift func FromAdminGetDLQReplicationMessagesResponse(t *types.GetDLQReplicationMessagesResponse) *replicator.GetDLQReplicationMessagesResponse { if t == nil { return nil } return &replicator.GetDLQReplicationMessagesResponse{ ReplicationTasks: FromReplicationTaskArray(t.ReplicationTasks), } } // ToAdminGetDLQReplicationMessagesResponse converts thrift GetDLQReplicationMessagesResponse type to internal func ToAdminGetDLQReplicationMessagesResponse(t *replicator.GetDLQReplicationMessagesResponse) *types.GetDLQReplicationMessagesResponse { if t == nil { return nil } return &types.GetDLQReplicationMessagesResponse{ ReplicationTasks: ToReplicationTaskArray(t.ReplicationTasks), } } // FromAdminGetDomainReplicationMessagesRequest converts internal GetDomainReplicationMessagesRequest type to thrift func FromAdminGetDomainReplicationMessagesRequest(t *types.GetDomainReplicationMessagesRequest) *replicator.GetDomainReplicationMessagesRequest { if t == nil { return nil } return &replicator.GetDomainReplicationMessagesRequest{ LastRetrievedMessageId: t.LastRetrievedMessageID, LastProcessedMessageId: t.LastProcessedMessageID, ClusterName: &t.ClusterName, } } // ToAdminGetDomainReplicationMessagesRequest converts thrift GetDomainReplicationMessagesRequest type to internal func ToAdminGetDomainReplicationMessagesRequest(t *replicator.GetDomainReplicationMessagesRequest) *types.GetDomainReplicationMessagesRequest { if t == nil { return nil } return &types.GetDomainReplicationMessagesRequest{ LastRetrievedMessageID: t.LastRetrievedMessageId, LastProcessedMessageID: t.LastProcessedMessageId, ClusterName: t.GetClusterName(), } } // FromAdminGetDomainReplicationMessagesResponse converts internal GetDomainReplicationMessagesResponse type to thrift func FromAdminGetDomainReplicationMessagesResponse(t *types.GetDomainReplicationMessagesResponse) *replicator.GetDomainReplicationMessagesResponse { if t == nil { return nil } return &replicator.GetDomainReplicationMessagesResponse{ Messages: FromReplicationMessages(t.Messages), } } // ToAdminGetDomainReplicationMessagesResponse converts thrift GetDomainReplicationMessagesResponse type to internal func ToAdminGetDomainReplicationMessagesResponse(t *replicator.GetDomainReplicationMessagesResponse) *types.GetDomainReplicationMessagesResponse { if t == nil { return nil } return &types.GetDomainReplicationMessagesResponse{ Messages: ToReplicationMessages(t.Messages), } } // FromAdminGetReplicationMessagesRequest converts internal GetReplicationMessagesRequest type to thrift func FromAdminGetReplicationMessagesRequest(t *types.GetReplicationMessagesRequest) *replicator.GetReplicationMessagesRequest { if t == nil { return nil } return &replicator.GetReplicationMessagesRequest{ Tokens: FromReplicationTokenArray(t.Tokens), ClusterName: &t.ClusterName, } } // ToAdminGetReplicationMessagesRequest converts thrift GetReplicationMessagesRequest type to internal func ToAdminGetReplicationMessagesRequest(t *replicator.GetReplicationMessagesRequest) *types.GetReplicationMessagesRequest { if t == nil { return nil } return &types.GetReplicationMessagesRequest{ Tokens: ToReplicationTokenArray(t.Tokens), ClusterName: t.GetClusterName(), } } // FromAdminGetReplicationMessagesResponse converts internal GetReplicationMessagesResponse type to thrift func FromAdminGetReplicationMessagesResponse(t *types.GetReplicationMessagesResponse) *replicator.GetReplicationMessagesResponse { if t == nil { return nil } return &replicator.GetReplicationMessagesResponse{ MessagesByShard: FromReplicationMessagesMap(t.MessagesByShard), } } // ToAdminGetReplicationMessagesResponse converts thrift GetReplicationMessagesResponse type to internal func ToAdminGetReplicationMessagesResponse(t *replicator.GetReplicationMessagesResponse) *types.GetReplicationMessagesResponse { if t == nil { return nil } return &types.GetReplicationMessagesResponse{ MessagesByShard: ToReplicationMessagesMap(t.MessagesByShard), } } // FromHistoryTaskV2Attributes converts internal HistoryTaskV2Attributes type to thrift func FromHistoryTaskV2Attributes(t *types.HistoryTaskV2Attributes) *replicator.HistoryTaskV2Attributes { if t == nil { return nil } return &replicator.HistoryTaskV2Attributes{ DomainId: &t.DomainID, WorkflowId: &t.WorkflowID, RunId: &t.RunID, VersionHistoryItems: FromVersionHistoryItemArray(t.VersionHistoryItems), Events: FromDataBlob(t.Events), NewRunEvents: FromDataBlob(t.NewRunEvents), } } // ToHistoryTaskV2Attributes converts thrift HistoryTaskV2Attributes type to internal func ToHistoryTaskV2Attributes(t *replicator.HistoryTaskV2Attributes) *types.HistoryTaskV2Attributes { if t == nil { return nil } return &types.HistoryTaskV2Attributes{ DomainID: t.GetDomainId(), WorkflowID: t.GetWorkflowId(), RunID: t.GetRunId(), VersionHistoryItems: ToVersionHistoryItemArray(t.VersionHistoryItems), Events: ToDataBlob(t.Events), NewRunEvents: ToDataBlob(t.NewRunEvents), } } // FromAdminMergeDLQMessagesRequest converts internal MergeDLQMessagesRequest type to thrift func FromAdminMergeDLQMessagesRequest(t *types.MergeDLQMessagesRequest) *replicator.MergeDLQMessagesRequest { if t == nil { return nil } return &replicator.MergeDLQMessagesRequest{ Type: FromDLQType(t.Type), ShardID: &t.ShardID, SourceCluster: &t.SourceCluster, InclusiveEndMessageID: t.InclusiveEndMessageID, MaximumPageSize: &t.MaximumPageSize, NextPageToken: t.NextPageToken, } } // ToAdminMergeDLQMessagesRequest converts thrift MergeDLQMessagesRequest type to internal func ToAdminMergeDLQMessagesRequest(t *replicator.MergeDLQMessagesRequest) *types.MergeDLQMessagesRequest { if t == nil { return nil } return &types.MergeDLQMessagesRequest{ Type: ToDLQType(t.Type), ShardID: t.GetShardID(), SourceCluster: t.GetSourceCluster(), InclusiveEndMessageID: t.InclusiveEndMessageID, MaximumPageSize: t.GetMaximumPageSize(), NextPageToken: t.NextPageToken, } } // FromAdminMergeDLQMessagesResponse converts internal MergeDLQMessagesResponse type to thrift func FromAdminMergeDLQMessagesResponse(t *types.MergeDLQMessagesResponse) *replicator.MergeDLQMessagesResponse { if t == nil { return nil } return &replicator.MergeDLQMessagesResponse{ NextPageToken: t.NextPageToken, } } // ToAdminMergeDLQMessagesResponse converts thrift MergeDLQMessagesResponse type to internal func ToAdminMergeDLQMessagesResponse(t *replicator.MergeDLQMessagesResponse) *types.MergeDLQMessagesResponse { if t == nil { return nil } return &types.MergeDLQMessagesResponse{ NextPageToken: t.NextPageToken, } } // FromAdminPurgeDLQMessagesRequest converts internal PurgeDLQMessagesRequest type to thrift func FromAdminPurgeDLQMessagesRequest(t *types.PurgeDLQMessagesRequest) *replicator.PurgeDLQMessagesRequest { if t == nil { return nil } return &replicator.PurgeDLQMessagesRequest{ Type: FromDLQType(t.Type), ShardID: &t.ShardID, SourceCluster: &t.SourceCluster, InclusiveEndMessageID: t.InclusiveEndMessageID, } } // ToAdminPurgeDLQMessagesRequest converts thrift PurgeDLQMessagesRequest type to internal func ToAdminPurgeDLQMessagesRequest(t *replicator.PurgeDLQMessagesRequest) *types.PurgeDLQMessagesRequest { if t == nil { return nil } return &types.PurgeDLQMessagesRequest{ Type: ToDLQType(t.Type), ShardID: t.GetShardID(), SourceCluster: t.GetSourceCluster(), InclusiveEndMessageID: t.InclusiveEndMessageID, } } // FromAdminReadDLQMessagesRequest converts internal ReadDLQMessagesRequest type to thrift func FromAdminReadDLQMessagesRequest(t *types.ReadDLQMessagesRequest) *replicator.ReadDLQMessagesRequest { if t == nil { return nil } return &replicator.ReadDLQMessagesRequest{ Type: FromDLQType(t.Type), ShardID: &t.ShardID, SourceCluster: &t.SourceCluster, InclusiveEndMessageID: t.InclusiveEndMessageID, MaximumPageSize: &t.MaximumPageSize, NextPageToken: t.NextPageToken, } } // ToAdminReadDLQMessagesRequest converts thrift ReadDLQMessagesRequest type to internal func ToAdminReadDLQMessagesRequest(t *replicator.ReadDLQMessagesRequest) *types.ReadDLQMessagesRequest { if t == nil { return nil } return &types.ReadDLQMessagesRequest{ Type: ToDLQType(t.Type), ShardID: t.GetShardID(), SourceCluster: t.GetSourceCluster(), InclusiveEndMessageID: t.InclusiveEndMessageID, MaximumPageSize: t.GetMaximumPageSize(), NextPageToken: t.NextPageToken, } } // FromAdminReadDLQMessagesResponse converts internal ReadDLQMessagesResponse type to thrift func FromAdminReadDLQMessagesResponse(t *types.ReadDLQMessagesResponse) *replicator.ReadDLQMessagesResponse { if t == nil { return nil } return &replicator.ReadDLQMessagesResponse{ Type: FromDLQType(t.Type), ReplicationTasks: FromReplicationTaskArray(t.ReplicationTasks), ReplicationTasksInfo: FromReplicationTaskInfoArray(t.ReplicationTasksInfo), NextPageToken: t.NextPageToken, } } // ToAdminReadDLQMessagesResponse converts thrift ReadDLQMessagesResponse type to internal func ToAdminReadDLQMessagesResponse(t *replicator.ReadDLQMessagesResponse) *types.ReadDLQMessagesResponse { if t == nil { return nil } return &types.ReadDLQMessagesResponse{ Type: ToDLQType(t.Type), ReplicationTasks: ToReplicationTaskArray(t.ReplicationTasks), ReplicationTasksInfo: ToReplicationTaskInfoArray(t.ReplicationTasksInfo), NextPageToken: t.NextPageToken, } } // FromReplicationMessages converts internal ReplicationMessages type to thrift func FromReplicationMessages(t *types.ReplicationMessages) *replicator.ReplicationMessages { if t == nil { return nil } return &replicator.ReplicationMessages{ ReplicationTasks: FromReplicationTaskArray(t.ReplicationTasks), LastRetrievedMessageId: &t.LastRetrievedMessageID, HasMore: &t.HasMore, SyncShardStatus: FromSyncShardStatus(t.SyncShardStatus), } } // ToReplicationMessages converts thrift ReplicationMessages type to internal func ToReplicationMessages(t *replicator.ReplicationMessages) *types.ReplicationMessages { if t == nil { return nil } return &types.ReplicationMessages{ ReplicationTasks: ToReplicationTaskArray(t.ReplicationTasks), LastRetrievedMessageID: t.GetLastRetrievedMessageId(), HasMore: t.GetHasMore(), SyncShardStatus: ToSyncShardStatus(t.SyncShardStatus), } } // FromReplicationTask converts internal ReplicationTask type to thrift func FromReplicationTask(t *types.ReplicationTask) *replicator.ReplicationTask { if t == nil { return nil } return &replicator.ReplicationTask{ TaskType: FromReplicationTaskType(t.TaskType), SourceTaskId: &t.SourceTaskID, DomainTaskAttributes: FromDomainTaskAttributes(t.DomainTaskAttributes), SyncShardStatusTaskAttributes: FromSyncShardStatusTaskAttributes(t.SyncShardStatusTaskAttributes), SyncActivityTaskAttributes: FromSyncActivityTaskAttributes(t.SyncActivityTaskAttributes), HistoryTaskV2Attributes: FromHistoryTaskV2Attributes(t.HistoryTaskV2Attributes), FailoverMarkerAttributes: FromFailoverMarkerAttributes(t.FailoverMarkerAttributes), CreationTime: t.CreationTime, } } // ToReplicationTask converts thrift ReplicationTask type to internal func ToReplicationTask(t *replicator.ReplicationTask) *types.ReplicationTask { if t == nil { return nil } return &types.ReplicationTask{ TaskType: ToReplicationTaskType(t.TaskType), SourceTaskID: t.GetSourceTaskId(), DomainTaskAttributes: ToDomainTaskAttributes(t.DomainTaskAttributes), SyncShardStatusTaskAttributes: ToSyncShardStatusTaskAttributes(t.SyncShardStatusTaskAttributes), SyncActivityTaskAttributes: ToSyncActivityTaskAttributes(t.SyncActivityTaskAttributes), HistoryTaskV2Attributes: ToHistoryTaskV2Attributes(t.HistoryTaskV2Attributes), FailoverMarkerAttributes: ToFailoverMarkerAttributes(t.FailoverMarkerAttributes), CreationTime: t.CreationTime, } } // FromReplicationTaskInfo converts internal ReplicationTaskInfo type to thrift func FromReplicationTaskInfo(t *types.ReplicationTaskInfo) *replicator.ReplicationTaskInfo { if t == nil { return nil } return &replicator.ReplicationTaskInfo{ DomainID: &t.DomainID, WorkflowID: &t.WorkflowID, RunID: &t.RunID, TaskType: &t.TaskType, TaskID: &t.TaskID, Version: &t.Version, FirstEventID: &t.FirstEventID, NextEventID: &t.NextEventID, ScheduledID: &t.ScheduledID, } } // ToReplicationTaskInfo converts thrift ReplicationTaskInfo type to internal func ToReplicationTaskInfo(t *replicator.ReplicationTaskInfo) *types.ReplicationTaskInfo { if t == nil { return nil } return &types.ReplicationTaskInfo{ DomainID: t.GetDomainID(), WorkflowID: t.GetWorkflowID(), RunID: t.GetRunID(), TaskType: t.GetTaskType(), TaskID: t.GetTaskID(), Version: t.GetVersion(), FirstEventID: t.GetFirstEventID(), NextEventID: t.GetNextEventID(), ScheduledID: t.GetScheduledID(), } } // FromReplicationTaskType converts internal ReplicationTaskType type to thrift func FromReplicationTaskType(t *types.ReplicationTaskType) *replicator.ReplicationTaskType { if t == nil { return nil } switch *t { case types.ReplicationTaskTypeDomain: v := replicator.ReplicationTaskTypeDomain return &v case types.ReplicationTaskTypeHistory: v := replicator.ReplicationTaskTypeHistory return &v case types.ReplicationTaskTypeSyncShardStatus: v := replicator.ReplicationTaskTypeSyncShardStatus return &v case types.ReplicationTaskTypeSyncActivity: v := replicator.ReplicationTaskTypeSyncActivity return &v case types.ReplicationTaskTypeHistoryMetadata: v := replicator.ReplicationTaskTypeHistoryMetadata return &v case types.ReplicationTaskTypeHistoryV2: v := replicator.ReplicationTaskTypeHistoryV2 return &v case types.ReplicationTaskTypeFailoverMarker: v := replicator.ReplicationTaskTypeFailoverMarker return &v } panic("unexpected enum value") } // ToReplicationTaskType converts thrift ReplicationTaskType type to internal func ToReplicationTaskType(t *replicator.ReplicationTaskType) *types.ReplicationTaskType { if t == nil { return nil } switch *t { case replicator.ReplicationTaskTypeDomain: v := types.ReplicationTaskTypeDomain return &v case replicator.ReplicationTaskTypeHistory: v := types.ReplicationTaskTypeHistory return &v case replicator.ReplicationTaskTypeSyncShardStatus: v := types.ReplicationTaskTypeSyncShardStatus return &v case replicator.ReplicationTaskTypeSyncActivity: v := types.ReplicationTaskTypeSyncActivity return &v case replicator.ReplicationTaskTypeHistoryMetadata: v := types.ReplicationTaskTypeHistoryMetadata return &v case replicator.ReplicationTaskTypeHistoryV2: v := types.ReplicationTaskTypeHistoryV2 return &v case replicator.ReplicationTaskTypeFailoverMarker: v := types.ReplicationTaskTypeFailoverMarker return &v } panic("unexpected enum value") } // FromReplicationToken converts internal ReplicationToken type to thrift func FromReplicationToken(t *types.ReplicationToken) *replicator.ReplicationToken { if t == nil { return nil } return &replicator.ReplicationToken{ ShardID: &t.ShardID, LastRetrievedMessageId: &t.LastRetrievedMessageID, LastProcessedMessageId: &t.LastProcessedMessageID, } } // ToReplicationToken converts thrift ReplicationToken type to internal func ToReplicationToken(t *replicator.ReplicationToken) *types.ReplicationToken { if t == nil { return nil } return &types.ReplicationToken{ ShardID: t.GetShardID(), LastRetrievedMessageID: t.GetLastRetrievedMessageId(), LastProcessedMessageID: t.GetLastProcessedMessageId(), } } // FromSyncActivityTaskAttributes converts internal SyncActivityTaskAttributes type to thrift func FromSyncActivityTaskAttributes(t *types.SyncActivityTaskAttributes) *replicator.SyncActivityTaskAttributes { if t == nil { return nil } return &replicator.SyncActivityTaskAttributes{ DomainId: &t.DomainID, WorkflowId: &t.WorkflowID, RunId: &t.RunID, Version: &t.Version, ScheduledId: &t.ScheduledID, ScheduledTime: t.ScheduledTime, StartedId: &t.StartedID, StartedTime: t.StartedTime, LastHeartbeatTime: t.LastHeartbeatTime, Details: t.Details, Attempt: &t.Attempt, LastFailureReason: t.LastFailureReason, LastWorkerIdentity: &t.LastWorkerIdentity, LastFailureDetails: t.LastFailureDetails, VersionHistory: FromVersionHistory(t.VersionHistory), } } // ToSyncActivityTaskAttributes converts thrift SyncActivityTaskAttributes type to internal func ToSyncActivityTaskAttributes(t *replicator.SyncActivityTaskAttributes) *types.SyncActivityTaskAttributes { if t == nil { return nil } return &types.SyncActivityTaskAttributes{ DomainID: t.GetDomainId(), WorkflowID: t.GetWorkflowId(), RunID: t.GetRunId(), Version: t.GetVersion(), ScheduledID: t.GetScheduledId(), ScheduledTime: t.ScheduledTime, StartedID: t.GetStartedId(), StartedTime: t.StartedTime, LastHeartbeatTime: t.LastHeartbeatTime, Details: t.Details, Attempt: t.GetAttempt(), LastFailureReason: t.LastFailureReason, LastWorkerIdentity: t.GetLastWorkerIdentity(), LastFailureDetails: t.LastFailureDetails, VersionHistory: ToVersionHistory(t.VersionHistory), } } // FromSyncShardStatus converts internal SyncShardStatus type to thrift func FromSyncShardStatus(t *types.SyncShardStatus) *replicator.SyncShardStatus { if t == nil { return nil } return &replicator.SyncShardStatus{ Timestamp: t.Timestamp, } } // ToSyncShardStatus converts thrift SyncShardStatus type to internal func ToSyncShardStatus(t *replicator.SyncShardStatus) *types.SyncShardStatus { if t == nil { return nil } return &types.SyncShardStatus{ Timestamp: t.Timestamp, } } // FromSyncShardStatusTaskAttributes converts internal SyncShardStatusTaskAttributes type to thrift func FromSyncShardStatusTaskAttributes(t *types.SyncShardStatusTaskAttributes) *replicator.SyncShardStatusTaskAttributes { if t == nil { return nil } return &replicator.SyncShardStatusTaskAttributes{ SourceCluster: &t.SourceCluster, ShardId: &t.ShardID, Timestamp: t.Timestamp, } } // ToSyncShardStatusTaskAttributes converts thrift SyncShardStatusTaskAttributes type to internal func ToSyncShardStatusTaskAttributes(t *replicator.SyncShardStatusTaskAttributes) *types.SyncShardStatusTaskAttributes { if t == nil { return nil } return &types.SyncShardStatusTaskAttributes{ SourceCluster: t.GetSourceCluster(), ShardID: t.GetShardId(), Timestamp: t.Timestamp, } } // FromFailoverMarkerAttributesArray converts internal FailoverMarkerAttributes type array to thrift func FromFailoverMarkerAttributesArray(t []*types.FailoverMarkerAttributes) []*replicator.FailoverMarkerAttributes { if t == nil { return nil } v := make([]*replicator.FailoverMarkerAttributes, len(t)) for i := range t { v[i] = FromFailoverMarkerAttributes(t[i]) } return v } // ToFailoverMarkerAttributesArray converts thrift FailoverMarkerAttributes type array to internal func ToFailoverMarkerAttributesArray(t []*replicator.FailoverMarkerAttributes) []*types.FailoverMarkerAttributes { if t == nil { return nil } v := make([]*types.FailoverMarkerAttributes, len(t)) for i := range t { v[i] = ToFailoverMarkerAttributes(t[i]) } return v } // FromReplicationTaskInfoArray converts internal ReplicationTaskInfo type array to thrift func FromReplicationTaskInfoArray(t []*types.ReplicationTaskInfo) []*replicator.ReplicationTaskInfo { if t == nil { return nil } v := make([]*replicator.ReplicationTaskInfo, len(t)) for i := range t { v[i] = FromReplicationTaskInfo(t[i]) } return v } // ToReplicationTaskInfoArray converts thrift ReplicationTaskInfo type array to internal func ToReplicationTaskInfoArray(t []*replicator.ReplicationTaskInfo) []*types.ReplicationTaskInfo { if t == nil { return nil } v := make([]*types.ReplicationTaskInfo, len(t)) for i := range t { v[i] = ToReplicationTaskInfo(t[i]) } return v } // FromReplicationTaskArray converts internal ReplicationTask type array to thrift func FromReplicationTaskArray(t []*types.ReplicationTask) []*replicator.ReplicationTask { if t == nil { return nil } v := make([]*replicator.ReplicationTask, len(t)) for i := range t { v[i] = FromReplicationTask(t[i]) } return v } // ToReplicationTaskArray converts thrift ReplicationTask type array to internal func ToReplicationTaskArray(t []*replicator.ReplicationTask) []*types.ReplicationTask { if t == nil { return nil } v := make([]*types.ReplicationTask, len(t)) for i := range t { v[i] = ToReplicationTask(t[i]) } return v } // FromReplicationTokenArray converts internal ReplicationToken type array to thrift func FromReplicationTokenArray(t []*types.ReplicationToken) []*replicator.ReplicationToken { if t == nil { return nil } v := make([]*replicator.ReplicationToken, len(t)) for i := range t { v[i] = FromReplicationToken(t[i]) } return v } // ToReplicationTokenArray converts thrift ReplicationToken type array to internal func ToReplicationTokenArray(t []*replicator.ReplicationToken) []*types.ReplicationToken { if t == nil { return nil } v := make([]*types.ReplicationToken, len(t)) for i := range t { v[i] = ToReplicationToken(t[i]) } return v } // FromReplicationMessagesMap converts internal ReplicationMessages type map to thrift func FromReplicationMessagesMap(t map[int32]*types.ReplicationMessages) map[int32]*replicator.ReplicationMessages { if t == nil { return nil } v := make(map[int32]*replicator.ReplicationMessages, len(t)) for key := range t { v[key] = FromReplicationMessages(t[key]) } return v } // ToReplicationMessagesMap converts thrift ReplicationMessages type map to internal func ToReplicationMessagesMap(t map[int32]*replicator.ReplicationMessages) map[int32]*types.ReplicationMessages { if t == nil { return nil } v := make(map[int32]*types.ReplicationMessages, len(t)) for key := range t { v[key] = ToReplicationMessages(t[key]) } return v } ================================================ FILE: common/types/mapper/thrift/replicator_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" ) func TestDLQType(t *testing.T) { testCases := []struct { desc string input *types.DLQType }{ { desc: "non-nil input test", input: types.DLQTypeReplication.Ptr(), }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromDLQType(tc.input) roundTripObj := ToDLQType(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestDomainOperation(t *testing.T) { testCases := []struct { desc string input *types.DomainOperation }{ { desc: "non-nil input test", input: types.DomainOperationCreate.Ptr(), }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromDomainOperation(tc.input) roundTripObj := ToDomainOperation(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestDomainTaskAttributes(t *testing.T) { testCases := []struct { desc string input *types.DomainTaskAttributes }{ { desc: "non-nil input test", input: &testdata.DomainTaskAttributes, }, { desc: "empty input test", input: &types.DomainTaskAttributes{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromDomainTaskAttributes(tc.input) roundTripObj := ToDomainTaskAttributes(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestFailoverMarkerAttributes(t *testing.T) { testCases := []struct { desc string input *types.FailoverMarkerAttributes }{ { desc: "non-nil input test", input: &testdata.FailoverMarkerAttributes, }, { desc: "empty input test", input: &types.FailoverMarkerAttributes{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromFailoverMarkerAttributes(tc.input) roundTripObj := ToFailoverMarkerAttributes(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestFailoverMarkers(t *testing.T) { testCases := []struct { desc string input *types.FailoverMarkers }{ { desc: "non-nil input test", input: &types.FailoverMarkers{FailoverMarkers: testdata.FailoverMarkerAttributesArray}, }, { desc: "empty input test", input: &types.FailoverMarkers{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromFailoverMarkers(tc.input) roundTripObj := ToFailoverMarkers(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminGetDLQReplicationMessagesRequest(t *testing.T) { testCases := []struct { desc string input *types.GetDLQReplicationMessagesRequest }{ { desc: "non-nil input test", input: &types.GetDLQReplicationMessagesRequest{ TaskInfos: testdata.ReplicationTaskInfoArray, }, }, { desc: "empty input test", input: &types.GetDLQReplicationMessagesRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminGetDLQReplicationMessagesRequest(tc.input) roundTripObj := ToAdminGetDLQReplicationMessagesRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminGetDLQReplicationMessagesResponse(t *testing.T) { testCases := []struct { desc string input *types.GetDLQReplicationMessagesResponse }{ { desc: "non-nil input test", input: &types.GetDLQReplicationMessagesResponse{ ReplicationTasks: testdata.ReplicationTaskArray, }, }, { desc: "empty input test", input: &types.GetDLQReplicationMessagesResponse{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminGetDLQReplicationMessagesResponse(tc.input) roundTripObj := ToAdminGetDLQReplicationMessagesResponse(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminGetDomainReplicationMessageRequest(t *testing.T) { testCases := []struct { desc string input *types.GetDomainReplicationMessagesRequest }{ { desc: "non-nil input test", input: &testdata.GetDomainReplicationMessagesRequest, }, { desc: "empty input test", input: &types.GetDomainReplicationMessagesRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminGetDomainReplicationMessagesRequest(tc.input) roundTripObj := ToAdminGetDomainReplicationMessagesRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminGetDomainReplicationMessageResponse(t *testing.T) { testCases := []struct { desc string input *types.GetDomainReplicationMessagesResponse }{ { desc: "non-nil input test", input: &types.GetDomainReplicationMessagesResponse{ Messages: &testdata.ReplicationMessages, }, }, { desc: "empty input test", input: &types.GetDomainReplicationMessagesResponse{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminGetDomainReplicationMessagesResponse(tc.input) roundTripObj := ToAdminGetDomainReplicationMessagesResponse(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminGetReplicationMessageRequest(t *testing.T) { testCases := []struct { desc string input *types.GetReplicationMessagesRequest }{ { desc: "non-nil input test", input: &types.GetReplicationMessagesRequest{ Tokens: testdata.ReplicationTokenArray, ClusterName: testdata.ClusterName1, }, }, { desc: "empty input test", input: &types.GetReplicationMessagesRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminGetReplicationMessagesRequest(tc.input) roundTripObj := ToAdminGetReplicationMessagesRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminGetReplicationMessageResponse(t *testing.T) { testCases := []struct { desc string input *types.GetReplicationMessagesResponse }{ { desc: "non-nil input test", input: &types.GetReplicationMessagesResponse{ MessagesByShard: testdata.ReplicationMessagesMap, }, }, { desc: "empty input test", input: &types.GetReplicationMessagesResponse{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminGetReplicationMessagesResponse(tc.input) roundTripObj := ToAdminGetReplicationMessagesResponse(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestHistoryTaskV2Attributes(t *testing.T) { testCases := []struct { desc string input *types.HistoryTaskV2Attributes }{ { desc: "non-nil input test", input: &testdata.HistoryTaskV2Attributes, }, { desc: "empty input test", input: &types.HistoryTaskV2Attributes{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromHistoryTaskV2Attributes(tc.input) roundTripObj := ToHistoryTaskV2Attributes(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminMergeDLQMessagesRequest(t *testing.T) { testCases := []struct { desc string input *types.MergeDLQMessagesRequest }{ { desc: "non-nil input test", input: &testdata.MergeDLQMessagesRequest, }, { desc: "empty input test", input: &types.MergeDLQMessagesRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminMergeDLQMessagesRequest(tc.input) roundTripObj := ToAdminMergeDLQMessagesRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminMergeDLQMessagesResponse(t *testing.T) { testCases := []struct { desc string input *types.MergeDLQMessagesResponse }{ { desc: "non-nil input test", input: &types.MergeDLQMessagesResponse{ NextPageToken: testdata.Token1, }, }, { desc: "empty input test", input: &types.MergeDLQMessagesResponse{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminMergeDLQMessagesResponse(tc.input) roundTripObj := ToAdminMergeDLQMessagesResponse(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminPurgeDLQMessagesRequest(t *testing.T) { testCases := []struct { desc string input *types.PurgeDLQMessagesRequest }{ { desc: "non-nil input test", input: &testdata.PurgeDLQMessagesRequest, }, { desc: "empty input test", input: &types.PurgeDLQMessagesRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminPurgeDLQMessagesRequest(tc.input) roundTripObj := ToAdminPurgeDLQMessagesRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminReadDLQMessagesRequest(t *testing.T) { testCases := []struct { desc string input *types.ReadDLQMessagesRequest }{ { desc: "non-nil input test", input: &testdata.ReadDLQMessagesRequest, }, { desc: "empty input test", input: &types.ReadDLQMessagesRequest{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminReadDLQMessagesRequest(tc.input) roundTripObj := ToAdminReadDLQMessagesRequest(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestAdminReadDLQMessagesResponse(t *testing.T) { testCases := []struct { desc string input *types.ReadDLQMessagesResponse }{ { desc: "non-nil input test", input: &testdata.ReadDLQMessagesResponse, }, { desc: "empty input test", input: &types.ReadDLQMessagesResponse{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromAdminReadDLQMessagesResponse(tc.input) roundTripObj := ToAdminReadDLQMessagesResponse(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestReplicationMessages(t *testing.T) { testCases := []struct { desc string input *types.ReplicationMessages }{ { desc: "non-nil input test", input: &testdata.ReplicationMessages, }, { desc: "empty input test", input: &types.ReplicationMessages{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromReplicationMessages(tc.input) roundTripObj := ToReplicationMessages(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestReplicationTask(t *testing.T) { testCases := []struct { desc string input *types.ReplicationTask }{ { desc: "non-nil input test", input: &testdata.ReplicationTask_Domain, }, { desc: "empty input test", input: &types.ReplicationTask{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromReplicationTask(tc.input) roundTripObj := ToReplicationTask(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestReplicationTaskInfo(t *testing.T) { testCases := []struct { desc string input *types.ReplicationTaskInfo }{ { desc: "non-nil input test", input: &testdata.ReplicationTaskInfo, }, { desc: "empty input test", input: &types.ReplicationTaskInfo{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromReplicationTaskInfo(tc.input) roundTripObj := ToReplicationTaskInfo(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestReplicationTaskType(t *testing.T) { testCases := []struct { desc string input *types.ReplicationTaskType }{ { desc: "non-nil input test", input: types.ReplicationTaskTypeDomain.Ptr(), }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromReplicationTaskType(tc.input) roundTripObj := ToReplicationTaskType(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestReplicationToken(t *testing.T) { testCases := []struct { desc string input *types.ReplicationToken }{ { desc: "non-nil input test", input: &testdata.ReplicationToken, }, { desc: "empty input test", input: &types.ReplicationToken{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromReplicationToken(tc.input) roundTripObj := ToReplicationToken(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestSyncActivityTaskAttributes(t *testing.T) { testCases := []struct { desc string input *types.SyncActivityTaskAttributes }{ { desc: "non-nil input test", input: &testdata.SyncActivityTaskAttributes, }, { desc: "empty input test", input: &types.SyncActivityTaskAttributes{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromSyncActivityTaskAttributes(tc.input) roundTripObj := ToSyncActivityTaskAttributes(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestSyncShardStatus(t *testing.T) { testCases := []struct { desc string input *types.SyncShardStatus }{ { desc: "non-nil input test", input: &testdata.SyncShardStatus, }, { desc: "empty input test", input: &types.SyncShardStatus{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromSyncShardStatus(tc.input) roundTripObj := ToSyncShardStatus(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestSyncShardStatusTaskAttributes(t *testing.T) { testCases := []struct { desc string input *types.SyncShardStatusTaskAttributes }{ { desc: "non-nil input test", input: &testdata.SyncShardStatusTaskAttributes, }, { desc: "empty input test", input: &types.SyncShardStatusTaskAttributes{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromSyncShardStatusTaskAttributes(tc.input) roundTripObj := ToSyncShardStatusTaskAttributes(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestFailoverMarkerAttributesArray(t *testing.T) { testCases := []struct { desc string input []*types.FailoverMarkerAttributes }{ { desc: "non-nil input test", input: testdata.FailoverMarkerAttributesArray, }, { desc: "empty input test", input: []*types.FailoverMarkerAttributes{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromFailoverMarkerAttributesArray(tc.input) roundTripObj := ToFailoverMarkerAttributesArray(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestReplicationTaskInfoArray(t *testing.T) { testCases := []struct { desc string input []*types.ReplicationTaskInfo }{ { desc: "non-nil input test", input: testdata.ReplicationTaskInfoArray, }, { desc: "empty input test", input: []*types.ReplicationTaskInfo{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromReplicationTaskInfoArray(tc.input) roundTripObj := ToReplicationTaskInfoArray(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestReplicationTaskArray(t *testing.T) { testCases := []struct { desc string input []*types.ReplicationTask }{ { desc: "non-nil input test", input: testdata.ReplicationTaskArray, }, { desc: "empty input test", input: []*types.ReplicationTask{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromReplicationTaskArray(tc.input) roundTripObj := ToReplicationTaskArray(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestReplicationTokenArray(t *testing.T) { testCases := []struct { desc string input []*types.ReplicationToken }{ { desc: "non-nil input test", input: testdata.ReplicationTokenArray, }, { desc: "empty input test", input: []*types.ReplicationToken{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromReplicationTokenArray(tc.input) roundTripObj := ToReplicationTokenArray(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } func TestReplicationMessagesMap(t *testing.T) { testCases := []struct { desc string input map[int32]*types.ReplicationMessages }{ { desc: "non-nil input test", input: testdata.ReplicationMessagesMap, }, { desc: "empty input test", input: map[int32]*types.ReplicationMessages{}, }, { desc: "nil input test", input: nil, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { thriftObj := FromReplicationMessagesMap(tc.input) roundTripObj := ToReplicationMessagesMap(thriftObj) assert.Equal(t, tc.input, roundTripObj) }) } } ================================================ FILE: common/types/mapper/thrift/shared.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/types" ) var ( FromScanWorkflowExecutionsRequest = FromListWorkflowExecutionsRequest ToScanWorkflowExecutionsRequest = ToListWorkflowExecutionsRequest FromRecordActivityTaskHeartbeatByIDResponse = FromRecordActivityTaskHeartbeatResponse ToRecordActivityTaskHeartbeatByIDResponse = ToRecordActivityTaskHeartbeatResponse FromScanWorkflowExecutionsResponse = FromListWorkflowExecutionsResponse ToScanWorkflowExecutionsResponse = ToListWorkflowExecutionsResponse FromSignalWithStartWorkflowExecutionResponse = FromStartWorkflowExecutionResponse ToSignalWithStartWorkflowExecutionResponse = ToStartWorkflowExecutionResponse ) // FromAccessDeniedError converts internal AccessDeniedError type to thrift func FromAccessDeniedError(t *types.AccessDeniedError) *shared.AccessDeniedError { if t == nil { return nil } return &shared.AccessDeniedError{ Message: t.Message, } } // ToAccessDeniedError converts thrift AccessDeniedError type to internal func ToAccessDeniedError(t *shared.AccessDeniedError) *types.AccessDeniedError { if t == nil { return nil } return &types.AccessDeniedError{ Message: t.Message, } } // FromActivityLocalDispatchInfo converts internal ActivityLocalDispatchInfo type to thrift func FromActivityLocalDispatchInfo(t *types.ActivityLocalDispatchInfo) *shared.ActivityLocalDispatchInfo { if t == nil { return nil } return &shared.ActivityLocalDispatchInfo{ ActivityId: &t.ActivityID, ScheduledTimestamp: t.ScheduledTimestamp, StartedTimestamp: t.StartedTimestamp, ScheduledTimestampOfThisAttempt: t.ScheduledTimestampOfThisAttempt, TaskToken: t.TaskToken, } } // ToActivityLocalDispatchInfo converts thrift ActivityLocalDispatchInfo type to internal func ToActivityLocalDispatchInfo(t *shared.ActivityLocalDispatchInfo) *types.ActivityLocalDispatchInfo { if t == nil { return nil } return &types.ActivityLocalDispatchInfo{ ActivityID: t.GetActivityId(), ScheduledTimestamp: t.ScheduledTimestamp, StartedTimestamp: t.StartedTimestamp, ScheduledTimestampOfThisAttempt: t.ScheduledTimestampOfThisAttempt, TaskToken: t.TaskToken, } } // FromActivityTaskCancelRequestedEventAttributes converts internal ActivityTaskCancelRequestedEventAttributes type to thrift func FromActivityTaskCancelRequestedEventAttributes(t *types.ActivityTaskCancelRequestedEventAttributes) *shared.ActivityTaskCancelRequestedEventAttributes { if t == nil { return nil } return &shared.ActivityTaskCancelRequestedEventAttributes{ ActivityId: &t.ActivityID, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, } } // ToActivityTaskCancelRequestedEventAttributes converts thrift ActivityTaskCancelRequestedEventAttributes type to internal func ToActivityTaskCancelRequestedEventAttributes(t *shared.ActivityTaskCancelRequestedEventAttributes) *types.ActivityTaskCancelRequestedEventAttributes { if t == nil { return nil } return &types.ActivityTaskCancelRequestedEventAttributes{ ActivityID: t.GetActivityId(), DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), } } // FromActivityTaskCanceledEventAttributes converts internal ActivityTaskCanceledEventAttributes type to thrift func FromActivityTaskCanceledEventAttributes(t *types.ActivityTaskCanceledEventAttributes) *shared.ActivityTaskCanceledEventAttributes { if t == nil { return nil } return &shared.ActivityTaskCanceledEventAttributes{ Details: t.Details, LatestCancelRequestedEventId: &t.LatestCancelRequestedEventID, ScheduledEventId: &t.ScheduledEventID, StartedEventId: &t.StartedEventID, Identity: &t.Identity, } } // ToActivityTaskCanceledEventAttributes converts thrift ActivityTaskCanceledEventAttributes type to internal func ToActivityTaskCanceledEventAttributes(t *shared.ActivityTaskCanceledEventAttributes) *types.ActivityTaskCanceledEventAttributes { if t == nil { return nil } return &types.ActivityTaskCanceledEventAttributes{ Details: t.Details, LatestCancelRequestedEventID: t.GetLatestCancelRequestedEventId(), ScheduledEventID: t.GetScheduledEventId(), StartedEventID: t.GetStartedEventId(), Identity: t.GetIdentity(), } } // FromActivityTaskCompletedEventAttributes converts internal ActivityTaskCompletedEventAttributes type to thrift func FromActivityTaskCompletedEventAttributes(t *types.ActivityTaskCompletedEventAttributes) *shared.ActivityTaskCompletedEventAttributes { if t == nil { return nil } return &shared.ActivityTaskCompletedEventAttributes{ Result: t.Result, ScheduledEventId: &t.ScheduledEventID, StartedEventId: &t.StartedEventID, Identity: &t.Identity, } } // ToActivityTaskCompletedEventAttributes converts thrift ActivityTaskCompletedEventAttributes type to internal func ToActivityTaskCompletedEventAttributes(t *shared.ActivityTaskCompletedEventAttributes) *types.ActivityTaskCompletedEventAttributes { if t == nil { return nil } return &types.ActivityTaskCompletedEventAttributes{ Result: t.Result, ScheduledEventID: t.GetScheduledEventId(), StartedEventID: t.GetStartedEventId(), Identity: t.GetIdentity(), } } // FromActivityTaskFailedEventAttributes converts internal ActivityTaskFailedEventAttributes type to thrift func FromActivityTaskFailedEventAttributes(t *types.ActivityTaskFailedEventAttributes) *shared.ActivityTaskFailedEventAttributes { if t == nil { return nil } return &shared.ActivityTaskFailedEventAttributes{ Reason: t.Reason, Details: t.Details, ScheduledEventId: &t.ScheduledEventID, StartedEventId: &t.StartedEventID, Identity: &t.Identity, } } // ToActivityTaskFailedEventAttributes converts thrift ActivityTaskFailedEventAttributes type to internal func ToActivityTaskFailedEventAttributes(t *shared.ActivityTaskFailedEventAttributes) *types.ActivityTaskFailedEventAttributes { if t == nil { return nil } return &types.ActivityTaskFailedEventAttributes{ Reason: t.Reason, Details: t.Details, ScheduledEventID: t.GetScheduledEventId(), StartedEventID: t.GetStartedEventId(), Identity: t.GetIdentity(), } } // FromActivityTaskScheduledEventAttributes converts internal ActivityTaskScheduledEventAttributes type to thrift func FromActivityTaskScheduledEventAttributes(t *types.ActivityTaskScheduledEventAttributes) *shared.ActivityTaskScheduledEventAttributes { if t == nil { return nil } return &shared.ActivityTaskScheduledEventAttributes{ ActivityId: &t.ActivityID, ActivityType: FromActivityType(t.ActivityType), Domain: t.Domain, TaskList: FromTaskList(t.TaskList), Input: t.Input, ScheduleToCloseTimeoutSeconds: t.ScheduleToCloseTimeoutSeconds, ScheduleToStartTimeoutSeconds: t.ScheduleToStartTimeoutSeconds, StartToCloseTimeoutSeconds: t.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: t.HeartbeatTimeoutSeconds, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, RetryPolicy: FromRetryPolicy(t.RetryPolicy), Header: FromHeader(t.Header), } } // ToActivityTaskScheduledEventAttributes converts thrift ActivityTaskScheduledEventAttributes type to internal func ToActivityTaskScheduledEventAttributes(t *shared.ActivityTaskScheduledEventAttributes) *types.ActivityTaskScheduledEventAttributes { if t == nil { return nil } return &types.ActivityTaskScheduledEventAttributes{ ActivityID: t.GetActivityId(), ActivityType: ToActivityType(t.ActivityType), Domain: t.Domain, TaskList: ToTaskList(t.TaskList), Input: t.Input, ScheduleToCloseTimeoutSeconds: t.ScheduleToCloseTimeoutSeconds, ScheduleToStartTimeoutSeconds: t.ScheduleToStartTimeoutSeconds, StartToCloseTimeoutSeconds: t.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: t.HeartbeatTimeoutSeconds, DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), RetryPolicy: ToRetryPolicy(t.RetryPolicy), Header: ToHeader(t.Header), } } // FromActivityTaskStartedEventAttributes converts internal ActivityTaskStartedEventAttributes type to thrift func FromActivityTaskStartedEventAttributes(t *types.ActivityTaskStartedEventAttributes) *shared.ActivityTaskStartedEventAttributes { if t == nil { return nil } return &shared.ActivityTaskStartedEventAttributes{ ScheduledEventId: &t.ScheduledEventID, Identity: &t.Identity, RequestId: &t.RequestID, Attempt: &t.Attempt, LastFailureReason: t.LastFailureReason, LastFailureDetails: t.LastFailureDetails, } } // ToActivityTaskStartedEventAttributes converts thrift ActivityTaskStartedEventAttributes type to internal func ToActivityTaskStartedEventAttributes(t *shared.ActivityTaskStartedEventAttributes) *types.ActivityTaskStartedEventAttributes { if t == nil { return nil } return &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: t.GetScheduledEventId(), Identity: t.GetIdentity(), RequestID: t.GetRequestId(), Attempt: t.GetAttempt(), LastFailureReason: t.LastFailureReason, LastFailureDetails: t.LastFailureDetails, } } // FromActivityTaskTimedOutEventAttributes converts internal ActivityTaskTimedOutEventAttributes type to thrift func FromActivityTaskTimedOutEventAttributes(t *types.ActivityTaskTimedOutEventAttributes) *shared.ActivityTaskTimedOutEventAttributes { if t == nil { return nil } return &shared.ActivityTaskTimedOutEventAttributes{ Details: t.Details, ScheduledEventId: &t.ScheduledEventID, StartedEventId: &t.StartedEventID, TimeoutType: FromTimeoutType(t.TimeoutType), LastFailureReason: t.LastFailureReason, LastFailureDetails: t.LastFailureDetails, } } // ToActivityTaskTimedOutEventAttributes converts thrift ActivityTaskTimedOutEventAttributes type to internal func ToActivityTaskTimedOutEventAttributes(t *shared.ActivityTaskTimedOutEventAttributes) *types.ActivityTaskTimedOutEventAttributes { if t == nil { return nil } return &types.ActivityTaskTimedOutEventAttributes{ Details: t.Details, ScheduledEventID: t.GetScheduledEventId(), StartedEventID: t.GetStartedEventId(), TimeoutType: ToTimeoutType(t.TimeoutType), LastFailureReason: t.LastFailureReason, LastFailureDetails: t.LastFailureDetails, } } // FromActivityType converts internal ActivityType type to thrift func FromActivityType(t *types.ActivityType) *shared.ActivityType { if t == nil { return nil } return &shared.ActivityType{ Name: &t.Name, } } // ToActivityType converts thrift ActivityType type to internal func ToActivityType(t *shared.ActivityType) *types.ActivityType { if t == nil { return nil } return &types.ActivityType{ Name: t.GetName(), } } // FromArchivalStatus converts internal ArchivalStatus type to thrift func FromArchivalStatus(t *types.ArchivalStatus) *shared.ArchivalStatus { if t == nil { return nil } switch *t { case types.ArchivalStatusDisabled: v := shared.ArchivalStatusDisabled return &v case types.ArchivalStatusEnabled: v := shared.ArchivalStatusEnabled return &v } panic("unexpected enum value") } // ToArchivalStatus converts thrift ArchivalStatus type to internal func ToArchivalStatus(t *shared.ArchivalStatus) *types.ArchivalStatus { if t == nil { return nil } switch *t { case shared.ArchivalStatusDisabled: v := types.ArchivalStatusDisabled return &v case shared.ArchivalStatusEnabled: v := types.ArchivalStatusEnabled return &v } panic("unexpected enum value") } // FromBadBinaries converts internal BadBinaries type to thrift func FromBadBinaries(t *types.BadBinaries) *shared.BadBinaries { if t == nil { return nil } return &shared.BadBinaries{ Binaries: FromBadBinaryInfoMap(t.Binaries), } } // ToBadBinaries converts thrift BadBinaries type to internal func ToBadBinaries(t *shared.BadBinaries) *types.BadBinaries { if t == nil { return nil } return &types.BadBinaries{ Binaries: ToBadBinaryInfoMap(t.Binaries), } } // FromBadBinaryInfo converts internal BadBinaryInfo type to thrift func FromBadBinaryInfo(t *types.BadBinaryInfo) *shared.BadBinaryInfo { if t == nil { return nil } return &shared.BadBinaryInfo{ Reason: &t.Reason, Operator: &t.Operator, CreatedTimeNano: t.CreatedTimeNano, } } // ToBadBinaryInfo converts thrift BadBinaryInfo type to internal func ToBadBinaryInfo(t *shared.BadBinaryInfo) *types.BadBinaryInfo { if t == nil { return nil } return &types.BadBinaryInfo{ Reason: t.GetReason(), Operator: t.GetOperator(), CreatedTimeNano: t.CreatedTimeNano, } } // FromBadRequestError converts internal BadRequestError type to thrift func FromBadRequestError(t *types.BadRequestError) *shared.BadRequestError { if t == nil { return nil } return &shared.BadRequestError{ Message: t.Message, } } // ToBadRequestError converts thrift BadRequestError type to internal func ToBadRequestError(t *shared.BadRequestError) *types.BadRequestError { if t == nil { return nil } return &types.BadRequestError{ Message: t.Message, } } // FromCancelExternalWorkflowExecutionFailedCause converts internal CancelExternalWorkflowExecutionFailedCause type to thrift func FromCancelExternalWorkflowExecutionFailedCause(t *types.CancelExternalWorkflowExecutionFailedCause) *shared.CancelExternalWorkflowExecutionFailedCause { if t == nil { return nil } switch *t { case types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution: v := shared.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution return &v case types.CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted: v := shared.CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted return &v } panic("unexpected enum value") } // ToCancelExternalWorkflowExecutionFailedCause converts thrift CancelExternalWorkflowExecutionFailedCause type to internal func ToCancelExternalWorkflowExecutionFailedCause(t *shared.CancelExternalWorkflowExecutionFailedCause) *types.CancelExternalWorkflowExecutionFailedCause { if t == nil { return nil } switch *t { case shared.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution: v := types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution return &v case shared.CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted: v := types.CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted return &v } panic("unexpected enum value") } // FromCancelTimerDecisionAttributes converts internal CancelTimerDecisionAttributes type to thrift func FromCancelTimerDecisionAttributes(t *types.CancelTimerDecisionAttributes) *shared.CancelTimerDecisionAttributes { if t == nil { return nil } return &shared.CancelTimerDecisionAttributes{ TimerId: &t.TimerID, } } // ToCancelTimerDecisionAttributes converts thrift CancelTimerDecisionAttributes type to internal func ToCancelTimerDecisionAttributes(t *shared.CancelTimerDecisionAttributes) *types.CancelTimerDecisionAttributes { if t == nil { return nil } return &types.CancelTimerDecisionAttributes{ TimerID: t.GetTimerId(), } } // FromCancelTimerFailedEventAttributes converts internal CancelTimerFailedEventAttributes type to thrift func FromCancelTimerFailedEventAttributes(t *types.CancelTimerFailedEventAttributes) *shared.CancelTimerFailedEventAttributes { if t == nil { return nil } return &shared.CancelTimerFailedEventAttributes{ TimerId: &t.TimerID, Cause: &t.Cause, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, Identity: &t.Identity, } } // ToCancelTimerFailedEventAttributes converts thrift CancelTimerFailedEventAttributes type to internal func ToCancelTimerFailedEventAttributes(t *shared.CancelTimerFailedEventAttributes) *types.CancelTimerFailedEventAttributes { if t == nil { return nil } return &types.CancelTimerFailedEventAttributes{ TimerID: t.GetTimerId(), Cause: t.GetCause(), DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), Identity: t.GetIdentity(), } } // FromCancelWorkflowExecutionDecisionAttributes converts internal CancelWorkflowExecutionDecisionAttributes type to thrift func FromCancelWorkflowExecutionDecisionAttributes(t *types.CancelWorkflowExecutionDecisionAttributes) *shared.CancelWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &shared.CancelWorkflowExecutionDecisionAttributes{ Details: t.Details, } } // ToCancelWorkflowExecutionDecisionAttributes converts thrift CancelWorkflowExecutionDecisionAttributes type to internal func ToCancelWorkflowExecutionDecisionAttributes(t *shared.CancelWorkflowExecutionDecisionAttributes) *types.CancelWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.CancelWorkflowExecutionDecisionAttributes{ Details: t.Details, } } // FromCancellationAlreadyRequestedError converts internal CancellationAlreadyRequestedError type to thrift func FromCancellationAlreadyRequestedError(t *types.CancellationAlreadyRequestedError) *shared.CancellationAlreadyRequestedError { if t == nil { return nil } return &shared.CancellationAlreadyRequestedError{ Message: t.Message, } } // ToCancellationAlreadyRequestedError converts thrift CancellationAlreadyRequestedError type to internal func ToCancellationAlreadyRequestedError(t *shared.CancellationAlreadyRequestedError) *types.CancellationAlreadyRequestedError { if t == nil { return nil } return &types.CancellationAlreadyRequestedError{ Message: t.Message, } } // FromChildWorkflowExecutionCanceledEventAttributes converts internal ChildWorkflowExecutionCanceledEventAttributes type to thrift func FromChildWorkflowExecutionCanceledEventAttributes(t *types.ChildWorkflowExecutionCanceledEventAttributes) *shared.ChildWorkflowExecutionCanceledEventAttributes { if t == nil { return nil } return &shared.ChildWorkflowExecutionCanceledEventAttributes{ Details: t.Details, Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: &t.InitiatedEventID, StartedEventId: &t.StartedEventID, } } // ToChildWorkflowExecutionCanceledEventAttributes converts thrift ChildWorkflowExecutionCanceledEventAttributes type to internal func ToChildWorkflowExecutionCanceledEventAttributes(t *shared.ChildWorkflowExecutionCanceledEventAttributes) *types.ChildWorkflowExecutionCanceledEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionCanceledEventAttributes{ Details: t.Details, Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.GetInitiatedEventId(), StartedEventID: t.GetStartedEventId(), } } // FromChildWorkflowExecutionCompletedEventAttributes converts internal ChildWorkflowExecutionCompletedEventAttributes type to thrift func FromChildWorkflowExecutionCompletedEventAttributes(t *types.ChildWorkflowExecutionCompletedEventAttributes) *shared.ChildWorkflowExecutionCompletedEventAttributes { if t == nil { return nil } return &shared.ChildWorkflowExecutionCompletedEventAttributes{ Result: t.Result, Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: &t.InitiatedEventID, StartedEventId: &t.StartedEventID, } } // ToChildWorkflowExecutionCompletedEventAttributes converts thrift ChildWorkflowExecutionCompletedEventAttributes type to internal func ToChildWorkflowExecutionCompletedEventAttributes(t *shared.ChildWorkflowExecutionCompletedEventAttributes) *types.ChildWorkflowExecutionCompletedEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionCompletedEventAttributes{ Result: t.Result, Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.GetInitiatedEventId(), StartedEventID: t.GetStartedEventId(), } } // FromChildWorkflowExecutionFailedCause converts internal ChildWorkflowExecutionFailedCause type to thrift func FromChildWorkflowExecutionFailedCause(t *types.ChildWorkflowExecutionFailedCause) *shared.ChildWorkflowExecutionFailedCause { if t == nil { return nil } switch *t { case types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning: v := shared.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning return &v } panic("unexpected enum value") } // ToChildWorkflowExecutionFailedCause converts thrift ChildWorkflowExecutionFailedCause type to internal func ToChildWorkflowExecutionFailedCause(t *shared.ChildWorkflowExecutionFailedCause) *types.ChildWorkflowExecutionFailedCause { if t == nil { return nil } switch *t { case shared.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning: v := types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning return &v } panic("unexpected enum value") } // FromChildWorkflowExecutionFailedEventAttributes converts internal ChildWorkflowExecutionFailedEventAttributes type to thrift func FromChildWorkflowExecutionFailedEventAttributes(t *types.ChildWorkflowExecutionFailedEventAttributes) *shared.ChildWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &shared.ChildWorkflowExecutionFailedEventAttributes{ Reason: t.Reason, Details: t.Details, Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: &t.InitiatedEventID, StartedEventId: &t.StartedEventID, } } // ToChildWorkflowExecutionFailedEventAttributes converts thrift ChildWorkflowExecutionFailedEventAttributes type to internal func ToChildWorkflowExecutionFailedEventAttributes(t *shared.ChildWorkflowExecutionFailedEventAttributes) *types.ChildWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionFailedEventAttributes{ Reason: t.Reason, Details: t.Details, Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.GetInitiatedEventId(), StartedEventID: t.GetStartedEventId(), } } // FromChildWorkflowExecutionStartedEventAttributes converts internal ChildWorkflowExecutionStartedEventAttributes type to thrift func FromChildWorkflowExecutionStartedEventAttributes(t *types.ChildWorkflowExecutionStartedEventAttributes) *shared.ChildWorkflowExecutionStartedEventAttributes { if t == nil { return nil } return &shared.ChildWorkflowExecutionStartedEventAttributes{ Domain: &t.Domain, InitiatedEventId: &t.InitiatedEventID, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), Header: FromHeader(t.Header), } } // ToChildWorkflowExecutionStartedEventAttributes converts thrift ChildWorkflowExecutionStartedEventAttributes type to internal func ToChildWorkflowExecutionStartedEventAttributes(t *shared.ChildWorkflowExecutionStartedEventAttributes) *types.ChildWorkflowExecutionStartedEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionStartedEventAttributes{ Domain: t.GetDomain(), InitiatedEventID: t.GetInitiatedEventId(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), Header: ToHeader(t.Header), } } // FromChildWorkflowExecutionTerminatedEventAttributes converts internal ChildWorkflowExecutionTerminatedEventAttributes type to thrift func FromChildWorkflowExecutionTerminatedEventAttributes(t *types.ChildWorkflowExecutionTerminatedEventAttributes) *shared.ChildWorkflowExecutionTerminatedEventAttributes { if t == nil { return nil } return &shared.ChildWorkflowExecutionTerminatedEventAttributes{ Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: &t.InitiatedEventID, StartedEventId: &t.StartedEventID, } } // ToChildWorkflowExecutionTerminatedEventAttributes converts thrift ChildWorkflowExecutionTerminatedEventAttributes type to internal func ToChildWorkflowExecutionTerminatedEventAttributes(t *shared.ChildWorkflowExecutionTerminatedEventAttributes) *types.ChildWorkflowExecutionTerminatedEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionTerminatedEventAttributes{ Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.GetInitiatedEventId(), StartedEventID: t.GetStartedEventId(), } } // FromChildWorkflowExecutionTimedOutEventAttributes converts internal ChildWorkflowExecutionTimedOutEventAttributes type to thrift func FromChildWorkflowExecutionTimedOutEventAttributes(t *types.ChildWorkflowExecutionTimedOutEventAttributes) *shared.ChildWorkflowExecutionTimedOutEventAttributes { if t == nil { return nil } return &shared.ChildWorkflowExecutionTimedOutEventAttributes{ TimeoutType: FromTimeoutType(t.TimeoutType), Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), InitiatedEventId: &t.InitiatedEventID, StartedEventId: &t.StartedEventID, } } // ToChildWorkflowExecutionTimedOutEventAttributes converts thrift ChildWorkflowExecutionTimedOutEventAttributes type to internal func ToChildWorkflowExecutionTimedOutEventAttributes(t *shared.ChildWorkflowExecutionTimedOutEventAttributes) *types.ChildWorkflowExecutionTimedOutEventAttributes { if t == nil { return nil } return &types.ChildWorkflowExecutionTimedOutEventAttributes{ TimeoutType: ToTimeoutType(t.TimeoutType), Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), InitiatedEventID: t.GetInitiatedEventId(), StartedEventID: t.GetStartedEventId(), } } // FromClientVersionNotSupportedError converts internal ClientVersionNotSupportedError type to thrift func FromClientVersionNotSupportedError(t *types.ClientVersionNotSupportedError) *shared.ClientVersionNotSupportedError { if t == nil { return nil } return &shared.ClientVersionNotSupportedError{ FeatureVersion: t.FeatureVersion, ClientImpl: t.ClientImpl, SupportedVersions: t.SupportedVersions, } } // ToClientVersionNotSupportedError converts thrift ClientVersionNotSupportedError type to internal func ToClientVersionNotSupportedError(t *shared.ClientVersionNotSupportedError) *types.ClientVersionNotSupportedError { if t == nil { return nil } return &types.ClientVersionNotSupportedError{ FeatureVersion: t.FeatureVersion, ClientImpl: t.ClientImpl, SupportedVersions: t.SupportedVersions, } } // FromFeatureNotEnabledError converts internal FeatureNotEnabledError type to thrift func FromFeatureNotEnabledError(t *types.FeatureNotEnabledError) *shared.FeatureNotEnabledError { if t == nil { return nil } return &shared.FeatureNotEnabledError{ FeatureFlag: t.FeatureFlag, } } // ToFeatureNotEnabledError converts thrift FeatureNotEnabledError type to internal func ToFeatureNotEnabledError(t *shared.FeatureNotEnabledError) *types.FeatureNotEnabledError { if t == nil { return nil } return &types.FeatureNotEnabledError{ FeatureFlag: t.FeatureFlag, } } // FromAdminCloseShardRequest converts internal CloseShardRequest type to thrift func FromAdminCloseShardRequest(t *types.CloseShardRequest) *shared.CloseShardRequest { if t == nil { return nil } return &shared.CloseShardRequest{ ShardID: &t.ShardID, } } // ToAdminCloseShardRequest converts thrift CloseShardRequest type to internal func ToAdminCloseShardRequest(t *shared.CloseShardRequest) *types.CloseShardRequest { if t == nil { return nil } return &types.CloseShardRequest{ ShardID: t.GetShardID(), } } // FromGetClusterInfoResponse converts internal ClusterInfo type to thrift func FromGetClusterInfoResponse(t *types.ClusterInfo) *shared.ClusterInfo { if t == nil { return nil } return &shared.ClusterInfo{ SupportedClientVersions: FromSupportedClientVersions(t.SupportedClientVersions), } } // ToGetClusterInfoResponse converts thrift ClusterInfo type to internal func ToGetClusterInfoResponse(t *shared.ClusterInfo) *types.ClusterInfo { if t == nil { return nil } return &types.ClusterInfo{ SupportedClientVersions: ToSupportedClientVersions(t.SupportedClientVersions), } } // FromClusterReplicationConfiguration converts internal ClusterReplicationConfiguration type to thrift func FromClusterReplicationConfiguration(t *types.ClusterReplicationConfiguration) *shared.ClusterReplicationConfiguration { if t == nil { return nil } return &shared.ClusterReplicationConfiguration{ ClusterName: &t.ClusterName, } } // ToClusterReplicationConfiguration converts thrift ClusterReplicationConfiguration type to internal func ToClusterReplicationConfiguration(t *shared.ClusterReplicationConfiguration) *types.ClusterReplicationConfiguration { if t == nil { return nil } return &types.ClusterReplicationConfiguration{ ClusterName: t.GetClusterName(), } } // FromCompleteWorkflowExecutionDecisionAttributes converts internal CompleteWorkflowExecutionDecisionAttributes type to thrift func FromCompleteWorkflowExecutionDecisionAttributes(t *types.CompleteWorkflowExecutionDecisionAttributes) *shared.CompleteWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &shared.CompleteWorkflowExecutionDecisionAttributes{ Result: t.Result, } } // ToCompleteWorkflowExecutionDecisionAttributes converts thrift CompleteWorkflowExecutionDecisionAttributes type to internal func ToCompleteWorkflowExecutionDecisionAttributes(t *shared.CompleteWorkflowExecutionDecisionAttributes) *types.CompleteWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.CompleteWorkflowExecutionDecisionAttributes{ Result: t.Result, } } // FromContinueAsNewInitiator converts internal ContinueAsNewInitiator type to thrift func FromContinueAsNewInitiator(t *types.ContinueAsNewInitiator) *shared.ContinueAsNewInitiator { if t == nil { return nil } switch *t { case types.ContinueAsNewInitiatorDecider: v := shared.ContinueAsNewInitiatorDecider return &v case types.ContinueAsNewInitiatorRetryPolicy: v := shared.ContinueAsNewInitiatorRetryPolicy return &v case types.ContinueAsNewInitiatorCronSchedule: v := shared.ContinueAsNewInitiatorCronSchedule return &v } panic("unexpected enum value") } // ToContinueAsNewInitiator converts thrift ContinueAsNewInitiator type to internal func ToContinueAsNewInitiator(t *shared.ContinueAsNewInitiator) *types.ContinueAsNewInitiator { if t == nil { return nil } switch *t { case shared.ContinueAsNewInitiatorDecider: v := types.ContinueAsNewInitiatorDecider return &v case shared.ContinueAsNewInitiatorRetryPolicy: v := types.ContinueAsNewInitiatorRetryPolicy return &v case shared.ContinueAsNewInitiatorCronSchedule: v := types.ContinueAsNewInitiatorCronSchedule return &v } panic("unexpected enum value") } // FromContinueAsNewWorkflowExecutionDecisionAttributes converts internal ContinueAsNewWorkflowExecutionDecisionAttributes type to thrift func FromContinueAsNewWorkflowExecutionDecisionAttributes(t *types.ContinueAsNewWorkflowExecutionDecisionAttributes) *shared.ContinueAsNewWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &shared.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, BackoffStartIntervalInSeconds: t.BackoffStartIntervalInSeconds, RetryPolicy: FromRetryPolicy(t.RetryPolicy), Initiator: FromContinueAsNewInitiator(t.Initiator), FailureReason: t.FailureReason, FailureDetails: t.FailureDetails, LastCompletionResult: t.LastCompletionResult, CronSchedule: &t.CronSchedule, Header: FromHeader(t.Header), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), JitterStartSeconds: t.JitterStartSeconds, ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), } } // ToContinueAsNewWorkflowExecutionDecisionAttributes converts thrift ContinueAsNewWorkflowExecutionDecisionAttributes type to internal func ToContinueAsNewWorkflowExecutionDecisionAttributes(t *shared.ContinueAsNewWorkflowExecutionDecisionAttributes) *types.ContinueAsNewWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, BackoffStartIntervalInSeconds: t.BackoffStartIntervalInSeconds, RetryPolicy: ToRetryPolicy(t.RetryPolicy), Initiator: ToContinueAsNewInitiator(t.Initiator), FailureReason: t.FailureReason, FailureDetails: t.FailureDetails, LastCompletionResult: t.LastCompletionResult, CronSchedule: t.GetCronSchedule(), Header: ToHeader(t.Header), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), JitterStartSeconds: t.JitterStartSeconds, ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), } } // FromCountWorkflowExecutionsRequest converts internal CountWorkflowExecutionsRequest type to thrift func FromCountWorkflowExecutionsRequest(t *types.CountWorkflowExecutionsRequest) *shared.CountWorkflowExecutionsRequest { if t == nil { return nil } return &shared.CountWorkflowExecutionsRequest{ Domain: &t.Domain, Query: &t.Query, } } // ToCountWorkflowExecutionsRequest converts thrift CountWorkflowExecutionsRequest type to internal func ToCountWorkflowExecutionsRequest(t *shared.CountWorkflowExecutionsRequest) *types.CountWorkflowExecutionsRequest { if t == nil { return nil } return &types.CountWorkflowExecutionsRequest{ Domain: t.GetDomain(), Query: t.GetQuery(), } } // FromCountWorkflowExecutionsResponse converts internal CountWorkflowExecutionsResponse type to thrift func FromCountWorkflowExecutionsResponse(t *types.CountWorkflowExecutionsResponse) *shared.CountWorkflowExecutionsResponse { if t == nil { return nil } return &shared.CountWorkflowExecutionsResponse{ Count: &t.Count, } } // ToCountWorkflowExecutionsResponse converts thrift CountWorkflowExecutionsResponse type to internal func ToCountWorkflowExecutionsResponse(t *shared.CountWorkflowExecutionsResponse) *types.CountWorkflowExecutionsResponse { if t == nil { return nil } return &types.CountWorkflowExecutionsResponse{ Count: t.GetCount(), } } // FromCurrentBranchChangedError converts internal CurrentBranchChangedError type to thrift func FromCurrentBranchChangedError(t *types.CurrentBranchChangedError) *shared.CurrentBranchChangedError { if t == nil { return nil } return &shared.CurrentBranchChangedError{ Message: t.Message, CurrentBranchToken: t.CurrentBranchToken, } } // ToCurrentBranchChangedError converts thrift CurrentBranchChangedError type to internal func ToCurrentBranchChangedError(t *shared.CurrentBranchChangedError) *types.CurrentBranchChangedError { if t == nil { return nil } return &types.CurrentBranchChangedError{ Message: t.Message, CurrentBranchToken: t.CurrentBranchToken, } } // FromDataBlob converts internal DataBlob type to thrift func FromDataBlob(t *types.DataBlob) *shared.DataBlob { if t == nil { return nil } return &shared.DataBlob{ EncodingType: FromEncodingType(t.EncodingType), Data: t.Data, } } // ToDataBlob converts thrift DataBlob type to internal func ToDataBlob(t *shared.DataBlob) *types.DataBlob { if t == nil { return nil } return &types.DataBlob{ EncodingType: ToEncodingType(t.EncodingType), Data: t.Data, } } // FromDecision converts internal Decision type to thrift func FromDecision(t *types.Decision) *shared.Decision { if t == nil { return nil } return &shared.Decision{ DecisionType: FromDecisionType(t.DecisionType), ScheduleActivityTaskDecisionAttributes: FromScheduleActivityTaskDecisionAttributes(t.ScheduleActivityTaskDecisionAttributes), StartTimerDecisionAttributes: FromStartTimerDecisionAttributes(t.StartTimerDecisionAttributes), CompleteWorkflowExecutionDecisionAttributes: FromCompleteWorkflowExecutionDecisionAttributes(t.CompleteWorkflowExecutionDecisionAttributes), FailWorkflowExecutionDecisionAttributes: FromFailWorkflowExecutionDecisionAttributes(t.FailWorkflowExecutionDecisionAttributes), RequestCancelActivityTaskDecisionAttributes: FromRequestCancelActivityTaskDecisionAttributes(t.RequestCancelActivityTaskDecisionAttributes), CancelTimerDecisionAttributes: FromCancelTimerDecisionAttributes(t.CancelTimerDecisionAttributes), CancelWorkflowExecutionDecisionAttributes: FromCancelWorkflowExecutionDecisionAttributes(t.CancelWorkflowExecutionDecisionAttributes), RequestCancelExternalWorkflowExecutionDecisionAttributes: FromRequestCancelExternalWorkflowExecutionDecisionAttributes(t.RequestCancelExternalWorkflowExecutionDecisionAttributes), RecordMarkerDecisionAttributes: FromRecordMarkerDecisionAttributes(t.RecordMarkerDecisionAttributes), ContinueAsNewWorkflowExecutionDecisionAttributes: FromContinueAsNewWorkflowExecutionDecisionAttributes(t.ContinueAsNewWorkflowExecutionDecisionAttributes), StartChildWorkflowExecutionDecisionAttributes: FromStartChildWorkflowExecutionDecisionAttributes(t.StartChildWorkflowExecutionDecisionAttributes), SignalExternalWorkflowExecutionDecisionAttributes: FromSignalExternalWorkflowExecutionDecisionAttributes(t.SignalExternalWorkflowExecutionDecisionAttributes), UpsertWorkflowSearchAttributesDecisionAttributes: FromUpsertWorkflowSearchAttributesDecisionAttributes(t.UpsertWorkflowSearchAttributesDecisionAttributes), } } // ToDecision converts thrift Decision type to internal func ToDecision(t *shared.Decision) *types.Decision { if t == nil { return nil } return &types.Decision{ DecisionType: ToDecisionType(t.DecisionType), ScheduleActivityTaskDecisionAttributes: ToScheduleActivityTaskDecisionAttributes(t.ScheduleActivityTaskDecisionAttributes), StartTimerDecisionAttributes: ToStartTimerDecisionAttributes(t.StartTimerDecisionAttributes), CompleteWorkflowExecutionDecisionAttributes: ToCompleteWorkflowExecutionDecisionAttributes(t.CompleteWorkflowExecutionDecisionAttributes), FailWorkflowExecutionDecisionAttributes: ToFailWorkflowExecutionDecisionAttributes(t.FailWorkflowExecutionDecisionAttributes), RequestCancelActivityTaskDecisionAttributes: ToRequestCancelActivityTaskDecisionAttributes(t.RequestCancelActivityTaskDecisionAttributes), CancelTimerDecisionAttributes: ToCancelTimerDecisionAttributes(t.CancelTimerDecisionAttributes), CancelWorkflowExecutionDecisionAttributes: ToCancelWorkflowExecutionDecisionAttributes(t.CancelWorkflowExecutionDecisionAttributes), RequestCancelExternalWorkflowExecutionDecisionAttributes: ToRequestCancelExternalWorkflowExecutionDecisionAttributes(t.RequestCancelExternalWorkflowExecutionDecisionAttributes), RecordMarkerDecisionAttributes: ToRecordMarkerDecisionAttributes(t.RecordMarkerDecisionAttributes), ContinueAsNewWorkflowExecutionDecisionAttributes: ToContinueAsNewWorkflowExecutionDecisionAttributes(t.ContinueAsNewWorkflowExecutionDecisionAttributes), StartChildWorkflowExecutionDecisionAttributes: ToStartChildWorkflowExecutionDecisionAttributes(t.StartChildWorkflowExecutionDecisionAttributes), SignalExternalWorkflowExecutionDecisionAttributes: ToSignalExternalWorkflowExecutionDecisionAttributes(t.SignalExternalWorkflowExecutionDecisionAttributes), UpsertWorkflowSearchAttributesDecisionAttributes: ToUpsertWorkflowSearchAttributesDecisionAttributes(t.UpsertWorkflowSearchAttributesDecisionAttributes), } } // FromDecisionTaskCompletedEventAttributes converts internal DecisionTaskCompletedEventAttributes type to thrift func FromDecisionTaskCompletedEventAttributes(t *types.DecisionTaskCompletedEventAttributes) *shared.DecisionTaskCompletedEventAttributes { if t == nil { return nil } return &shared.DecisionTaskCompletedEventAttributes{ ExecutionContext: t.ExecutionContext, ScheduledEventId: &t.ScheduledEventID, StartedEventId: &t.StartedEventID, Identity: &t.Identity, BinaryChecksum: &t.BinaryChecksum, } } // ToDecisionTaskCompletedEventAttributes converts thrift DecisionTaskCompletedEventAttributes type to internal func ToDecisionTaskCompletedEventAttributes(t *shared.DecisionTaskCompletedEventAttributes) *types.DecisionTaskCompletedEventAttributes { if t == nil { return nil } return &types.DecisionTaskCompletedEventAttributes{ ExecutionContext: t.ExecutionContext, ScheduledEventID: t.GetScheduledEventId(), StartedEventID: t.GetStartedEventId(), Identity: t.GetIdentity(), BinaryChecksum: t.GetBinaryChecksum(), } } // FromDecisionTaskFailedCause converts internal DecisionTaskFailedCause type to thrift func FromDecisionTaskFailedCause(t *types.DecisionTaskFailedCause) *shared.DecisionTaskFailedCause { if t == nil { return nil } switch *t { case types.DecisionTaskFailedCauseUnhandledDecision: v := shared.DecisionTaskFailedCauseUnhandledDecision return &v case types.DecisionTaskFailedCauseBadScheduleActivityAttributes: v := shared.DecisionTaskFailedCauseBadScheduleActivityAttributes return &v case types.DecisionTaskFailedCauseBadRequestCancelActivityAttributes: v := shared.DecisionTaskFailedCauseBadRequestCancelActivityAttributes return &v case types.DecisionTaskFailedCauseBadStartTimerAttributes: v := shared.DecisionTaskFailedCauseBadStartTimerAttributes return &v case types.DecisionTaskFailedCauseBadCancelTimerAttributes: v := shared.DecisionTaskFailedCauseBadCancelTimerAttributes return &v case types.DecisionTaskFailedCauseBadRecordMarkerAttributes: v := shared.DecisionTaskFailedCauseBadRecordMarkerAttributes return &v case types.DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes: v := shared.DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes return &v case types.DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes: v := shared.DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes return &v case types.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes: v := shared.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes return &v case types.DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes: v := shared.DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes return &v case types.DecisionTaskFailedCauseBadContinueAsNewAttributes: v := shared.DecisionTaskFailedCauseBadContinueAsNewAttributes return &v case types.DecisionTaskFailedCauseStartTimerDuplicateID: v := shared.DecisionTaskFailedCauseStartTimerDuplicateID return &v case types.DecisionTaskFailedCauseResetStickyTasklist: v := shared.DecisionTaskFailedCauseResetStickyTasklist return &v case types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure: v := shared.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure return &v case types.DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes: v := shared.DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes return &v case types.DecisionTaskFailedCauseBadStartChildExecutionAttributes: v := shared.DecisionTaskFailedCauseBadStartChildExecutionAttributes return &v case types.DecisionTaskFailedCauseForceCloseDecision: v := shared.DecisionTaskFailedCauseForceCloseDecision return &v case types.DecisionTaskFailedCauseFailoverCloseDecision: v := shared.DecisionTaskFailedCauseFailoverCloseDecision return &v case types.DecisionTaskFailedCauseBadSignalInputSize: v := shared.DecisionTaskFailedCauseBadSignalInputSize return &v case types.DecisionTaskFailedCauseResetWorkflow: v := shared.DecisionTaskFailedCauseResetWorkflow return &v case types.DecisionTaskFailedCauseBadBinary: v := shared.DecisionTaskFailedCauseBadBinary return &v case types.DecisionTaskFailedCauseScheduleActivityDuplicateID: v := shared.DecisionTaskFailedCauseScheduleActivityDuplicateID return &v case types.DecisionTaskFailedCauseBadSearchAttributes: v := shared.DecisionTaskFailedCauseBadSearchAttributes return &v } panic("unexpected enum value") } // ToDecisionTaskFailedCause converts thrift DecisionTaskFailedCause type to internal func ToDecisionTaskFailedCause(t *shared.DecisionTaskFailedCause) *types.DecisionTaskFailedCause { if t == nil { return nil } switch *t { case shared.DecisionTaskFailedCauseUnhandledDecision: v := types.DecisionTaskFailedCauseUnhandledDecision return &v case shared.DecisionTaskFailedCauseBadScheduleActivityAttributes: v := types.DecisionTaskFailedCauseBadScheduleActivityAttributes return &v case shared.DecisionTaskFailedCauseBadRequestCancelActivityAttributes: v := types.DecisionTaskFailedCauseBadRequestCancelActivityAttributes return &v case shared.DecisionTaskFailedCauseBadStartTimerAttributes: v := types.DecisionTaskFailedCauseBadStartTimerAttributes return &v case shared.DecisionTaskFailedCauseBadCancelTimerAttributes: v := types.DecisionTaskFailedCauseBadCancelTimerAttributes return &v case shared.DecisionTaskFailedCauseBadRecordMarkerAttributes: v := types.DecisionTaskFailedCauseBadRecordMarkerAttributes return &v case shared.DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes: v := types.DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes return &v case shared.DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes: v := types.DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes return &v case shared.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes: v := types.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes return &v case shared.DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes: v := types.DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes return &v case shared.DecisionTaskFailedCauseBadContinueAsNewAttributes: v := types.DecisionTaskFailedCauseBadContinueAsNewAttributes return &v case shared.DecisionTaskFailedCauseStartTimerDuplicateID: v := types.DecisionTaskFailedCauseStartTimerDuplicateID return &v case shared.DecisionTaskFailedCauseResetStickyTasklist: v := types.DecisionTaskFailedCauseResetStickyTasklist return &v case shared.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure: v := types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure return &v case shared.DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes: v := types.DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes return &v case shared.DecisionTaskFailedCauseBadStartChildExecutionAttributes: v := types.DecisionTaskFailedCauseBadStartChildExecutionAttributes return &v case shared.DecisionTaskFailedCauseForceCloseDecision: v := types.DecisionTaskFailedCauseForceCloseDecision return &v case shared.DecisionTaskFailedCauseFailoverCloseDecision: v := types.DecisionTaskFailedCauseFailoverCloseDecision return &v case shared.DecisionTaskFailedCauseBadSignalInputSize: v := types.DecisionTaskFailedCauseBadSignalInputSize return &v case shared.DecisionTaskFailedCauseResetWorkflow: v := types.DecisionTaskFailedCauseResetWorkflow return &v case shared.DecisionTaskFailedCauseBadBinary: v := types.DecisionTaskFailedCauseBadBinary return &v case shared.DecisionTaskFailedCauseScheduleActivityDuplicateID: v := types.DecisionTaskFailedCauseScheduleActivityDuplicateID return &v case shared.DecisionTaskFailedCauseBadSearchAttributes: v := types.DecisionTaskFailedCauseBadSearchAttributes return &v } panic("unexpected enum value") } // FromDecisionTaskFailedEventAttributes converts internal DecisionTaskFailedEventAttributes type to thrift func FromDecisionTaskFailedEventAttributes(t *types.DecisionTaskFailedEventAttributes) *shared.DecisionTaskFailedEventAttributes { if t == nil { return nil } return &shared.DecisionTaskFailedEventAttributes{ ScheduledEventId: &t.ScheduledEventID, StartedEventId: &t.StartedEventID, Cause: FromDecisionTaskFailedCause(t.Cause), Details: t.Details, Identity: &t.Identity, Reason: t.Reason, BaseRunId: &t.BaseRunID, NewRunId: &t.NewRunID, ForkEventVersion: &t.ForkEventVersion, BinaryChecksum: &t.BinaryChecksum, RequestId: &t.RequestID, } } // ToDecisionTaskFailedEventAttributes converts thrift DecisionTaskFailedEventAttributes type to internal func ToDecisionTaskFailedEventAttributes(t *shared.DecisionTaskFailedEventAttributes) *types.DecisionTaskFailedEventAttributes { if t == nil { return nil } return &types.DecisionTaskFailedEventAttributes{ ScheduledEventID: t.GetScheduledEventId(), StartedEventID: t.GetStartedEventId(), Cause: ToDecisionTaskFailedCause(t.Cause), Details: t.Details, Identity: t.GetIdentity(), Reason: t.Reason, BaseRunID: t.GetBaseRunId(), NewRunID: t.GetNewRunId(), ForkEventVersion: t.GetForkEventVersion(), BinaryChecksum: t.GetBinaryChecksum(), RequestID: t.GetRequestId(), } } // FromDecisionTaskScheduledEventAttributes converts internal DecisionTaskScheduledEventAttributes type to thrift func FromDecisionTaskScheduledEventAttributes(t *types.DecisionTaskScheduledEventAttributes) *shared.DecisionTaskScheduledEventAttributes { if t == nil { return nil } return &shared.DecisionTaskScheduledEventAttributes{ TaskList: FromTaskList(t.TaskList), StartToCloseTimeoutSeconds: t.StartToCloseTimeoutSeconds, Attempt: &t.Attempt, } } // ToDecisionTaskScheduledEventAttributes converts thrift DecisionTaskScheduledEventAttributes type to internal func ToDecisionTaskScheduledEventAttributes(t *shared.DecisionTaskScheduledEventAttributes) *types.DecisionTaskScheduledEventAttributes { if t == nil { return nil } return &types.DecisionTaskScheduledEventAttributes{ TaskList: ToTaskList(t.TaskList), StartToCloseTimeoutSeconds: t.StartToCloseTimeoutSeconds, Attempt: t.GetAttempt(), } } // FromDecisionTaskStartedEventAttributes converts internal DecisionTaskStartedEventAttributes type to thrift func FromDecisionTaskStartedEventAttributes(t *types.DecisionTaskStartedEventAttributes) *shared.DecisionTaskStartedEventAttributes { if t == nil { return nil } return &shared.DecisionTaskStartedEventAttributes{ ScheduledEventId: &t.ScheduledEventID, Identity: &t.Identity, RequestId: &t.RequestID, } } // ToDecisionTaskStartedEventAttributes converts thrift DecisionTaskStartedEventAttributes type to internal func ToDecisionTaskStartedEventAttributes(t *shared.DecisionTaskStartedEventAttributes) *types.DecisionTaskStartedEventAttributes { if t == nil { return nil } return &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: t.GetScheduledEventId(), Identity: t.GetIdentity(), RequestID: t.GetRequestId(), } } // FromDecisionTaskTimedOutCause converts internal DecisionTaskTimedOutCause type to thrift func FromDecisionTaskTimedOutCause(t *types.DecisionTaskTimedOutCause) *shared.DecisionTaskTimedOutCause { if t == nil { return nil } switch *t { case types.DecisionTaskTimedOutCauseTimeout: v := shared.DecisionTaskTimedOutCauseTimeout return &v case types.DecisionTaskTimedOutCauseReset: v := shared.DecisionTaskTimedOutCauseReset return &v } panic("unexpected enum value") } // ToDecisionTaskTimedOutCause converts thrift DecisionTaskTimedOutCause type to internal func ToDecisionTaskTimedOutCause(t *shared.DecisionTaskTimedOutCause) *types.DecisionTaskTimedOutCause { if t == nil { return nil } switch *t { case shared.DecisionTaskTimedOutCauseTimeout: v := types.DecisionTaskTimedOutCauseTimeout return &v case shared.DecisionTaskTimedOutCauseReset: v := types.DecisionTaskTimedOutCauseReset return &v } panic("unexpected enum value") } // FromDecisionTaskTimedOutEventAttributes converts internal DecisionTaskTimedOutEventAttributes type to thrift func FromDecisionTaskTimedOutEventAttributes(t *types.DecisionTaskTimedOutEventAttributes) *shared.DecisionTaskTimedOutEventAttributes { if t == nil { return nil } return &shared.DecisionTaskTimedOutEventAttributes{ ScheduledEventId: &t.ScheduledEventID, StartedEventId: &t.StartedEventID, TimeoutType: FromTimeoutType(t.TimeoutType), BaseRunId: &t.BaseRunID, NewRunId: &t.NewRunID, ForkEventVersion: &t.ForkEventVersion, Reason: &t.Reason, Cause: FromDecisionTaskTimedOutCause(t.Cause), RequestId: &t.RequestID, } } // ToDecisionTaskTimedOutEventAttributes converts thrift DecisionTaskTimedOutEventAttributes type to internal func ToDecisionTaskTimedOutEventAttributes(t *shared.DecisionTaskTimedOutEventAttributes) *types.DecisionTaskTimedOutEventAttributes { if t == nil { return nil } return &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: t.GetScheduledEventId(), StartedEventID: t.GetStartedEventId(), TimeoutType: ToTimeoutType(t.TimeoutType), BaseRunID: t.GetBaseRunId(), NewRunID: t.GetNewRunId(), ForkEventVersion: t.GetForkEventVersion(), Reason: t.GetReason(), Cause: ToDecisionTaskTimedOutCause(t.Cause), RequestID: t.GetRequestId(), } } // FromDecisionType converts internal DecisionType type to thrift func FromDecisionType(t *types.DecisionType) *shared.DecisionType { if t == nil { return nil } switch *t { case types.DecisionTypeScheduleActivityTask: v := shared.DecisionTypeScheduleActivityTask return &v case types.DecisionTypeRequestCancelActivityTask: v := shared.DecisionTypeRequestCancelActivityTask return &v case types.DecisionTypeStartTimer: v := shared.DecisionTypeStartTimer return &v case types.DecisionTypeCompleteWorkflowExecution: v := shared.DecisionTypeCompleteWorkflowExecution return &v case types.DecisionTypeFailWorkflowExecution: v := shared.DecisionTypeFailWorkflowExecution return &v case types.DecisionTypeCancelTimer: v := shared.DecisionTypeCancelTimer return &v case types.DecisionTypeCancelWorkflowExecution: v := shared.DecisionTypeCancelWorkflowExecution return &v case types.DecisionTypeRequestCancelExternalWorkflowExecution: v := shared.DecisionTypeRequestCancelExternalWorkflowExecution return &v case types.DecisionTypeRecordMarker: v := shared.DecisionTypeRecordMarker return &v case types.DecisionTypeContinueAsNewWorkflowExecution: v := shared.DecisionTypeContinueAsNewWorkflowExecution return &v case types.DecisionTypeStartChildWorkflowExecution: v := shared.DecisionTypeStartChildWorkflowExecution return &v case types.DecisionTypeSignalExternalWorkflowExecution: v := shared.DecisionTypeSignalExternalWorkflowExecution return &v case types.DecisionTypeUpsertWorkflowSearchAttributes: v := shared.DecisionTypeUpsertWorkflowSearchAttributes return &v } panic("unexpected enum value") } // ToDecisionType converts thrift DecisionType type to internal func ToDecisionType(t *shared.DecisionType) *types.DecisionType { if t == nil { return nil } switch *t { case shared.DecisionTypeScheduleActivityTask: v := types.DecisionTypeScheduleActivityTask return &v case shared.DecisionTypeRequestCancelActivityTask: v := types.DecisionTypeRequestCancelActivityTask return &v case shared.DecisionTypeStartTimer: v := types.DecisionTypeStartTimer return &v case shared.DecisionTypeCompleteWorkflowExecution: v := types.DecisionTypeCompleteWorkflowExecution return &v case shared.DecisionTypeFailWorkflowExecution: v := types.DecisionTypeFailWorkflowExecution return &v case shared.DecisionTypeCancelTimer: v := types.DecisionTypeCancelTimer return &v case shared.DecisionTypeCancelWorkflowExecution: v := types.DecisionTypeCancelWorkflowExecution return &v case shared.DecisionTypeRequestCancelExternalWorkflowExecution: v := types.DecisionTypeRequestCancelExternalWorkflowExecution return &v case shared.DecisionTypeRecordMarker: v := types.DecisionTypeRecordMarker return &v case shared.DecisionTypeContinueAsNewWorkflowExecution: v := types.DecisionTypeContinueAsNewWorkflowExecution return &v case shared.DecisionTypeStartChildWorkflowExecution: v := types.DecisionTypeStartChildWorkflowExecution return &v case shared.DecisionTypeSignalExternalWorkflowExecution: v := types.DecisionTypeSignalExternalWorkflowExecution return &v case shared.DecisionTypeUpsertWorkflowSearchAttributes: v := types.DecisionTypeUpsertWorkflowSearchAttributes return &v } panic("unexpected enum value") } // FromDeleteDomainRequest converts internal DeleteDomainRequest type to thrift func FromDeleteDomainRequest(t *types.DeleteDomainRequest) *shared.DeleteDomainRequest { if t == nil { return nil } return &shared.DeleteDomainRequest{ Name: &t.Name, SecurityToken: &t.SecurityToken, } } // ToDeleteDomainRequest converts thrift DeleteDomainRequest type to internal func ToDeleteDomainRequest(t *shared.DeleteDomainRequest) *types.DeleteDomainRequest { if t == nil { return nil } return &types.DeleteDomainRequest{ Name: t.GetName(), SecurityToken: t.GetSecurityToken(), } } // FromDeprecateDomainRequest converts internal DeprecateDomainRequest type to thrift func FromDeprecateDomainRequest(t *types.DeprecateDomainRequest) *shared.DeprecateDomainRequest { if t == nil { return nil } return &shared.DeprecateDomainRequest{ Name: &t.Name, SecurityToken: &t.SecurityToken, } } // ToDeprecateDomainRequest converts thrift DeprecateDomainRequest type to internal func ToDeprecateDomainRequest(t *shared.DeprecateDomainRequest) *types.DeprecateDomainRequest { if t == nil { return nil } return &types.DeprecateDomainRequest{ Name: t.GetName(), SecurityToken: t.GetSecurityToken(), } } // FromDescribeDomainRequest converts internal DescribeDomainRequest type to thrift func FromDescribeDomainRequest(t *types.DescribeDomainRequest) *shared.DescribeDomainRequest { if t == nil { return nil } return &shared.DescribeDomainRequest{ Name: t.Name, UUID: t.UUID, } } // ToDescribeDomainRequest converts thrift DescribeDomainRequest type to internal func ToDescribeDomainRequest(t *shared.DescribeDomainRequest) *types.DescribeDomainRequest { if t == nil { return nil } return &types.DescribeDomainRequest{ Name: t.Name, UUID: t.UUID, } } // FromDescribeDomainResponse converts internal DescribeDomainResponse type to thrift func FromDescribeDomainResponse(t *types.DescribeDomainResponse) *shared.DescribeDomainResponse { if t == nil { return nil } return &shared.DescribeDomainResponse{ DomainInfo: FromDomainInfo(t.DomainInfo), Configuration: FromDomainConfiguration(t.Configuration), ReplicationConfiguration: FromDomainReplicationConfiguration(t.ReplicationConfiguration), FailoverVersion: &t.FailoverVersion, IsGlobalDomain: &t.IsGlobalDomain, FailoverInfo: FromFailoverInfo(t.GetFailoverInfo()), } } // ToDescribeDomainResponse converts thrift DescribeDomainResponse type to internal func ToDescribeDomainResponse(t *shared.DescribeDomainResponse) *types.DescribeDomainResponse { if t == nil { return nil } return &types.DescribeDomainResponse{ DomainInfo: ToDomainInfo(t.DomainInfo), Configuration: ToDomainConfiguration(t.Configuration), ReplicationConfiguration: ToDomainReplicationConfiguration(t.ReplicationConfiguration), FailoverVersion: t.GetFailoverVersion(), IsGlobalDomain: t.GetIsGlobalDomain(), FailoverInfo: ToFailoverInfo(t.FailoverInfo), } } // FromFailoverDomainRequest converts internal FailoverDomainRequest type to thrift func FromFailoverDomainRequest(t *types.FailoverDomainRequest) *shared.FailoverDomainRequest { if t == nil { return nil } return &shared.FailoverDomainRequest{ DomainName: &t.DomainName, DomainActiveClusterName: t.DomainActiveClusterName, ActiveClusters: FromActiveClusters(t.ActiveClusters), } } // ToFailoverDomainRequest converts thrift FailoverDomainRequest type to internal func ToFailoverDomainRequest(t *shared.FailoverDomainRequest) *types.FailoverDomainRequest { if t == nil { return nil } return &types.FailoverDomainRequest{ DomainName: t.GetDomainName(), DomainActiveClusterName: t.DomainActiveClusterName, ActiveClusters: ToActiveClusters(t.ActiveClusters), } } // FromFailoverDomainResponse converts internal FailoverDomainResponse type to thrift func FromFailoverDomainResponse(t *types.FailoverDomainResponse) *shared.FailoverDomainResponse { if t == nil { return nil } return &shared.FailoverDomainResponse{ DomainInfo: FromDomainInfo(t.DomainInfo), Configuration: FromDomainConfiguration(t.Configuration), ReplicationConfiguration: FromDomainReplicationConfiguration(t.ReplicationConfiguration), FailoverVersion: &t.FailoverVersion, IsGlobalDomain: &t.IsGlobalDomain, } } // ToFailoverDomainResponse converts thrift FailoverDomainResponse type to internal func ToFailoverDomainResponse(t *shared.FailoverDomainResponse) *types.FailoverDomainResponse { if t == nil { return nil } return &types.FailoverDomainResponse{ DomainInfo: ToDomainInfo(t.DomainInfo), Configuration: ToDomainConfiguration(t.Configuration), ReplicationConfiguration: ToDomainReplicationConfiguration(t.ReplicationConfiguration), FailoverVersion: t.GetFailoverVersion(), IsGlobalDomain: t.GetIsGlobalDomain(), } } // FromAdminDescribeHistoryHostRequest converts internal DescribeHistoryHostRequest type to thrift func FromAdminDescribeHistoryHostRequest(t *types.DescribeHistoryHostRequest) *shared.DescribeHistoryHostRequest { if t == nil { return nil } return &shared.DescribeHistoryHostRequest{ HostAddress: t.HostAddress, ShardIdForHost: t.ShardIDForHost, ExecutionForHost: FromWorkflowExecution(t.ExecutionForHost), } } // ToAdminDescribeHistoryHostRequest converts thrift DescribeHistoryHostRequest type to internal func ToAdminDescribeHistoryHostRequest(t *shared.DescribeHistoryHostRequest) *types.DescribeHistoryHostRequest { if t == nil { return nil } return &types.DescribeHistoryHostRequest{ HostAddress: t.HostAddress, ShardIDForHost: t.ShardIdForHost, ExecutionForHost: ToWorkflowExecution(t.ExecutionForHost), } } // FromAdminDescribeShardDistributionRequest converts internal DescribeHistoryHostRequest type to thrift func FromAdminDescribeShardDistributionRequest(t *types.DescribeShardDistributionRequest) *shared.DescribeShardDistributionRequest { if t == nil { return nil } return &shared.DescribeShardDistributionRequest{ PageSize: &t.PageSize, PageID: &t.PageID, } } // ToAdminDescribeShardDistributionRequest converts thrift DescribeHistoryHostRequest type to internal func ToAdminDescribeShardDistributionRequest(t *shared.DescribeShardDistributionRequest) *types.DescribeShardDistributionRequest { if t == nil { return nil } return &types.DescribeShardDistributionRequest{ PageSize: t.GetPageSize(), PageID: t.GetPageID(), } } // FromAdminDescribeHistoryHostResponse converts internal DescribeHistoryHostResponse type to thrift func FromAdminDescribeHistoryHostResponse(t *types.DescribeHistoryHostResponse) *shared.DescribeHistoryHostResponse { if t == nil { return nil } return &shared.DescribeHistoryHostResponse{ NumberOfShards: &t.NumberOfShards, ShardIDs: t.ShardIDs, DomainCache: FromDomainCacheInfo(t.DomainCache), ShardControllerStatus: &t.ShardControllerStatus, Address: &t.Address, } } // ToAdminDescribeHistoryHostResponse converts thrift DescribeHistoryHostResponse type to internal func ToAdminDescribeHistoryHostResponse(t *shared.DescribeHistoryHostResponse) *types.DescribeHistoryHostResponse { if t == nil { return nil } return &types.DescribeHistoryHostResponse{ NumberOfShards: t.GetNumberOfShards(), ShardIDs: t.ShardIDs, DomainCache: ToDomainCacheInfo(t.DomainCache), ShardControllerStatus: t.GetShardControllerStatus(), Address: t.GetAddress(), } } // FromAdminDescribeShardDistributionResponse converts internal DescribeHistoryHostResponse type to thrift func FromAdminDescribeShardDistributionResponse(t *types.DescribeShardDistributionResponse) *shared.DescribeShardDistributionResponse { if t == nil { return nil } return &shared.DescribeShardDistributionResponse{ NumberOfShards: &t.NumberOfShards, Shards: t.Shards, } } // ToAdminDescribeShardDistributionResponse converts thrift DescribeHistoryHostResponse type to internal func ToAdminDescribeShardDistributionResponse(t *shared.DescribeShardDistributionResponse) *types.DescribeShardDistributionResponse { if t == nil { return nil } return &types.DescribeShardDistributionResponse{ NumberOfShards: t.GetNumberOfShards(), Shards: t.Shards, } } // FromAdminDescribeQueueRequest converts internal DescribeQueueRequest type to thrift func FromAdminDescribeQueueRequest(t *types.DescribeQueueRequest) *shared.DescribeQueueRequest { if t == nil { return nil } return &shared.DescribeQueueRequest{ ShardID: &t.ShardID, ClusterName: &t.ClusterName, Type: t.Type, } } // ToAdminDescribeQueueRequest converts thrift DescribeQueueRequest type to internal func ToAdminDescribeQueueRequest(t *shared.DescribeQueueRequest) *types.DescribeQueueRequest { if t == nil { return nil } return &types.DescribeQueueRequest{ ShardID: t.GetShardID(), ClusterName: t.GetClusterName(), Type: t.Type, } } // FromAdminDescribeQueueResponse converts internal DescribeQueueResponse type to thrift func FromAdminDescribeQueueResponse(t *types.DescribeQueueResponse) *shared.DescribeQueueResponse { if t == nil { return nil } return &shared.DescribeQueueResponse{ ProcessingQueueStates: t.ProcessingQueueStates, } } // ToAdminDescribeQueueResponse converts thrift DescribeQueueResponse type to internal func ToAdminDescribeQueueResponse(t *shared.DescribeQueueResponse) *types.DescribeQueueResponse { if t == nil { return nil } return &types.DescribeQueueResponse{ ProcessingQueueStates: t.ProcessingQueueStates, } } // FromDescribeTaskListRequest converts internal DescribeTaskListRequest type to thrift func FromDescribeTaskListRequest(t *types.DescribeTaskListRequest) *shared.DescribeTaskListRequest { if t == nil { return nil } return &shared.DescribeTaskListRequest{ Domain: &t.Domain, TaskList: FromTaskList(t.TaskList), TaskListType: FromTaskListType(t.TaskListType), IncludeTaskListStatus: &t.IncludeTaskListStatus, } } // ToDescribeTaskListRequest converts thrift DescribeTaskListRequest type to internal func ToDescribeTaskListRequest(t *shared.DescribeTaskListRequest) *types.DescribeTaskListRequest { if t == nil { return nil } return &types.DescribeTaskListRequest{ Domain: t.GetDomain(), TaskList: ToTaskList(t.TaskList), TaskListType: ToTaskListType(t.TaskListType), IncludeTaskListStatus: t.GetIncludeTaskListStatus(), } } // FromDescribeTaskListResponse converts internal DescribeTaskListResponse type to thrift func FromDescribeTaskListResponse(t *types.DescribeTaskListResponse) *shared.DescribeTaskListResponse { if t == nil { return nil } return &shared.DescribeTaskListResponse{ Pollers: FromPollerInfoArray(t.Pollers), TaskListStatus: FromTaskListStatus(t.TaskListStatus), TaskList: FromTaskList(t.TaskList), } } // ToDescribeTaskListResponse converts thrift DescribeTaskListResponse type to internal func ToDescribeTaskListResponse(t *shared.DescribeTaskListResponse) *types.DescribeTaskListResponse { if t == nil { return nil } return &types.DescribeTaskListResponse{ Pollers: ToPollerInfoArray(t.Pollers), TaskListStatus: ToTaskListStatus(t.TaskListStatus), TaskList: ToTaskList(t.TaskList), } } // FromDescribeWorkflowExecutionRequest converts internal DescribeWorkflowExecutionRequest type to thrift func FromDescribeWorkflowExecutionRequest(t *types.DescribeWorkflowExecutionRequest) *shared.DescribeWorkflowExecutionRequest { if t == nil { return nil } return &shared.DescribeWorkflowExecutionRequest{ Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), QueryConsistencyLevel: FromQueryConsistencyLevel(t.QueryConsistencyLevel), } } // ToDescribeWorkflowExecutionRequest converts thrift DescribeWorkflowExecutionRequest type to internal func ToDescribeWorkflowExecutionRequest(t *shared.DescribeWorkflowExecutionRequest) *types.DescribeWorkflowExecutionRequest { if t == nil { return nil } return &types.DescribeWorkflowExecutionRequest{ Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), QueryConsistencyLevel: ToQueryConsistencyLevel(t.QueryConsistencyLevel), } } // FromDescribeWorkflowExecutionResponse converts internal DescribeWorkflowExecutionResponse type to thrift func FromDescribeWorkflowExecutionResponse(t *types.DescribeWorkflowExecutionResponse) *shared.DescribeWorkflowExecutionResponse { if t == nil { return nil } return &shared.DescribeWorkflowExecutionResponse{ ExecutionConfiguration: FromWorkflowExecutionConfiguration(t.ExecutionConfiguration), WorkflowExecutionInfo: FromWorkflowExecutionInfo(t.WorkflowExecutionInfo), PendingActivities: FromPendingActivityInfoArray(t.PendingActivities), PendingChildren: FromPendingChildExecutionInfoArray(t.PendingChildren), PendingDecision: FromPendingDecisionInfo(t.PendingDecision), } } // ToDescribeWorkflowExecutionResponse converts thrift DescribeWorkflowExecutionResponse type to internal func ToDescribeWorkflowExecutionResponse(t *shared.DescribeWorkflowExecutionResponse) *types.DescribeWorkflowExecutionResponse { if t == nil { return nil } return &types.DescribeWorkflowExecutionResponse{ ExecutionConfiguration: ToWorkflowExecutionConfiguration(t.ExecutionConfiguration), WorkflowExecutionInfo: ToWorkflowExecutionInfo(t.WorkflowExecutionInfo), PendingActivities: ToPendingActivityInfoArray(t.PendingActivities), PendingChildren: ToPendingChildExecutionInfoArray(t.PendingChildren), PendingDecision: ToPendingDecisionInfo(t.PendingDecision), } } // FromDiagnoseWorkflowExecutionRequest converts internal DiagnoseWorkflowExecutionRequest type to thrift func FromDiagnoseWorkflowExecutionRequest(t *types.DiagnoseWorkflowExecutionRequest) *shared.DiagnoseWorkflowExecutionRequest { if t == nil { return nil } return &shared.DiagnoseWorkflowExecutionRequest{ Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.GetWorkflowExecution()), Identity: &t.Identity, } } // ToDiagnoseWorkflowExecutionRequest converts thrift DiagnoseWorkflowExecutionRequest type to internal func ToDiagnoseWorkflowExecutionRequest(t *shared.DiagnoseWorkflowExecutionRequest) *types.DiagnoseWorkflowExecutionRequest { if t == nil { return nil } return &types.DiagnoseWorkflowExecutionRequest{ Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.GetWorkflowExecution()), Identity: t.GetIdentity(), } } // FromDiagnoseWorkflowExecutionResponse converts internal DiagnoseWorkflowExecutionResponse type to thrift func FromDiagnoseWorkflowExecutionResponse(t *types.DiagnoseWorkflowExecutionResponse) *shared.DiagnoseWorkflowExecutionResponse { if t == nil { return nil } return &shared.DiagnoseWorkflowExecutionResponse{ Domain: &t.Domain, DiagnosticWorkflowExecution: FromWorkflowExecution(t.GetDiagnosticWorkflowExecution()), } } // ToDiagnoseWorkflowExecutionResponse converts thrift DiagnoseeWorkflowExecutionResponse type to internal func ToDiagnoseWorkflowExecutionResponse(t *shared.DiagnoseWorkflowExecutionResponse) *types.DiagnoseWorkflowExecutionResponse { if t == nil { return nil } return &types.DiagnoseWorkflowExecutionResponse{ Domain: t.GetDomain(), DiagnosticWorkflowExecution: ToWorkflowExecution(t.GetDiagnosticWorkflowExecution()), } } // FromDomainAlreadyExistsError converts internal DomainAlreadyExistsError type to thrift func FromDomainAlreadyExistsError(t *types.DomainAlreadyExistsError) *shared.DomainAlreadyExistsError { if t == nil { return nil } return &shared.DomainAlreadyExistsError{ Message: t.Message, } } // ToDomainAlreadyExistsError converts thrift DomainAlreadyExistsError type to internal func ToDomainAlreadyExistsError(t *shared.DomainAlreadyExistsError) *types.DomainAlreadyExistsError { if t == nil { return nil } return &types.DomainAlreadyExistsError{ Message: t.Message, } } // FromDomainCacheInfo converts internal DomainCacheInfo type to thrift func FromDomainCacheInfo(t *types.DomainCacheInfo) *shared.DomainCacheInfo { if t == nil { return nil } return &shared.DomainCacheInfo{ NumOfItemsInCacheByID: &t.NumOfItemsInCacheByID, NumOfItemsInCacheByName: &t.NumOfItemsInCacheByName, } } // ToDomainCacheInfo converts thrift DomainCacheInfo type to internal func ToDomainCacheInfo(t *shared.DomainCacheInfo) *types.DomainCacheInfo { if t == nil { return nil } return &types.DomainCacheInfo{ NumOfItemsInCacheByID: t.GetNumOfItemsInCacheByID(), NumOfItemsInCacheByName: t.GetNumOfItemsInCacheByName(), } } // FromDomainConfiguration converts internal DomainConfiguration type to thrift func FromDomainConfiguration(t *types.DomainConfiguration) *shared.DomainConfiguration { if t == nil { return nil } return &shared.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: &t.WorkflowExecutionRetentionPeriodInDays, EmitMetric: &t.EmitMetric, BadBinaries: FromBadBinaries(t.BadBinaries), HistoryArchivalStatus: FromArchivalStatus(t.HistoryArchivalStatus), HistoryArchivalURI: &t.HistoryArchivalURI, VisibilityArchivalStatus: FromArchivalStatus(t.VisibilityArchivalStatus), VisibilityArchivalURI: &t.VisibilityArchivalURI, Isolationgroups: FromIsolationGroupConfig(t.IsolationGroups), AsyncWorkflowConfiguration: FromDomainAsyncWorkflowConfiguraton(t.AsyncWorkflowConfig), } } // ToDomainConfiguration converts thrift DomainConfiguration type to internal func ToDomainConfiguration(t *shared.DomainConfiguration) *types.DomainConfiguration { if t == nil { return nil } return &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: t.GetWorkflowExecutionRetentionPeriodInDays(), EmitMetric: t.GetEmitMetric(), BadBinaries: ToBadBinaries(t.BadBinaries), HistoryArchivalStatus: ToArchivalStatus(t.HistoryArchivalStatus), HistoryArchivalURI: t.GetHistoryArchivalURI(), VisibilityArchivalStatus: ToArchivalStatus(t.VisibilityArchivalStatus), VisibilityArchivalURI: t.GetVisibilityArchivalURI(), IsolationGroups: ToIsolationGroupConfig(t.Isolationgroups), AsyncWorkflowConfig: ToDomainAsyncWorkflowConfiguraton(t.AsyncWorkflowConfiguration), } } // FromDomainInfo converts internal DomainInfo type to thrift func FromDomainInfo(t *types.DomainInfo) *shared.DomainInfo { if t == nil { return nil } return &shared.DomainInfo{ Name: &t.Name, Status: FromDomainStatus(t.Status), Description: &t.Description, OwnerEmail: &t.OwnerEmail, Data: t.Data, UUID: &t.UUID, } } // ToDomainInfo converts thrift DomainInfo type to internal func ToDomainInfo(t *shared.DomainInfo) *types.DomainInfo { if t == nil { return nil } return &types.DomainInfo{ Name: t.GetName(), Status: ToDomainStatus(t.Status), Description: t.GetDescription(), OwnerEmail: t.GetOwnerEmail(), Data: t.Data, UUID: t.GetUUID(), } } // FromFailoverInfo converts internal FailoverInfo type to thrift func FromFailoverInfo(t *types.FailoverInfo) *shared.FailoverInfo { if t == nil { return nil } return &shared.FailoverInfo{ FailoverVersion: &t.FailoverVersion, FailoverStartTimestamp: &t.FailoverStartTimestamp, FailoverExpireTimestamp: &t.FailoverExpireTimestamp, CompletedShardCount: &t.CompletedShardCount, PendingShards: t.GetPendingShards(), } } // ToFailoverInfo converts thrift FailoverInfo type to internal func ToFailoverInfo(t *shared.FailoverInfo) *types.FailoverInfo { if t == nil { return nil } return &types.FailoverInfo{ FailoverVersion: t.GetFailoverVersion(), FailoverStartTimestamp: t.GetFailoverStartTimestamp(), FailoverExpireTimestamp: t.GetFailoverExpireTimestamp(), CompletedShardCount: t.GetCompletedShardCount(), PendingShards: t.GetPendingShards(), } } // FromDomainNotActiveError converts internal DomainNotActiveError type to thrift func FromDomainNotActiveError(t *types.DomainNotActiveError) *shared.DomainNotActiveError { if t == nil { return nil } return &shared.DomainNotActiveError{ Message: t.Message, DomainName: t.DomainName, CurrentCluster: t.CurrentCluster, ActiveCluster: t.ActiveCluster, ActiveClusters: t.ActiveClusters, } } // ToDomainNotActiveError converts thrift DomainNotActiveError type to internal func ToDomainNotActiveError(t *shared.DomainNotActiveError) *types.DomainNotActiveError { if t == nil { return nil } return &types.DomainNotActiveError{ Message: t.Message, DomainName: t.DomainName, CurrentCluster: t.CurrentCluster, ActiveCluster: t.ActiveCluster, ActiveClusters: t.ActiveClusters, } } // FromDomainReplicationConfiguration converts internal DomainReplicationConfiguration type to thrift func FromDomainReplicationConfiguration(t *types.DomainReplicationConfiguration) *shared.DomainReplicationConfiguration { if t == nil { return nil } return &shared.DomainReplicationConfiguration{ ActiveClusterName: &t.ActiveClusterName, Clusters: FromClusterReplicationConfigurationArray(t.Clusters), ActiveClusters: FromActiveClusters(t.ActiveClusters), } } // ToDomainReplicationConfiguration converts thrift DomainReplicationConfiguration type to internal func ToDomainReplicationConfiguration(t *shared.DomainReplicationConfiguration) *types.DomainReplicationConfiguration { if t == nil { return nil } return &types.DomainReplicationConfiguration{ ActiveClusterName: t.GetActiveClusterName(), Clusters: ToClusterReplicationConfigurationArray(t.Clusters), ActiveClusters: ToActiveClusters(t.ActiveClusters), } } // FromActiveClusters converts internal ActiveClusters type to thrift func FromActiveClusters(t *types.ActiveClusters) *shared.ActiveClusters { if t == nil { return nil } var activeClustersByClusterAttribute map[string]*shared.ClusterAttributeScope if t.AttributeScopes != nil { activeClustersByClusterAttribute = make(map[string]*shared.ClusterAttributeScope) for scopeType, scope := range t.AttributeScopes { activeClustersByClusterAttribute[scopeType] = FromClusterAttributeScope(&scope) } } return &shared.ActiveClusters{ ActiveClustersByClusterAttribute: activeClustersByClusterAttribute, } } // ToActiveClusters converts thrift ActiveClusters type to internal func ToActiveClusters(t *shared.ActiveClusters) *types.ActiveClusters { if t == nil { return nil } var attributeScopes map[string]types.ClusterAttributeScope if t.ActiveClustersByClusterAttribute != nil { attributeScopes = make(map[string]types.ClusterAttributeScope) for scopeType, scope := range t.ActiveClustersByClusterAttribute { if converted := ToClusterAttributeScope(scope); converted != nil { attributeScopes[scopeType] = *converted } } } return &types.ActiveClusters{ AttributeScopes: attributeScopes, } } // FromClusterAttributeScope converts internal ClusterAttributeScope type to thrift func FromClusterAttributeScope(t *types.ClusterAttributeScope) *shared.ClusterAttributeScope { if t == nil { return nil } var clusterAttributes map[string]*shared.ActiveClusterInfo if len(t.ClusterAttributes) > 0 { clusterAttributes = make(map[string]*shared.ActiveClusterInfo) for name, clusterInfo := range t.ClusterAttributes { clusterAttributes[name] = &shared.ActiveClusterInfo{ ActiveClusterName: &clusterInfo.ActiveClusterName, FailoverVersion: &clusterInfo.FailoverVersion, } } } return &shared.ClusterAttributeScope{ ClusterAttributes: clusterAttributes, } } // ToClusterAttributeScope converts thrift ClusterAttributeScope type to internal func ToClusterAttributeScope(t *shared.ClusterAttributeScope) *types.ClusterAttributeScope { if t == nil { return nil } var clusterAttributes map[string]types.ActiveClusterInfo if len(t.ClusterAttributes) > 0 { clusterAttributes = make(map[string]types.ActiveClusterInfo) for name, clusterInfo := range t.ClusterAttributes { if clusterInfo != nil { clusterAttributes[name] = types.ActiveClusterInfo{ ActiveClusterName: *clusterInfo.ActiveClusterName, FailoverVersion: *clusterInfo.FailoverVersion, } } } } return &types.ClusterAttributeScope{ ClusterAttributes: clusterAttributes, } } // FromDomainStatus converts internal DomainStatus type to thrift func FromDomainStatus(t *types.DomainStatus) *shared.DomainStatus { if t == nil { return nil } switch *t { case types.DomainStatusRegistered: v := shared.DomainStatusRegistered return &v case types.DomainStatusDeprecated: v := shared.DomainStatusDeprecated return &v case types.DomainStatusDeleted: v := shared.DomainStatusDeleted return &v } panic("unexpected enum value") } // ToDomainStatus converts thrift DomainStatus type to internal func ToDomainStatus(t *shared.DomainStatus) *types.DomainStatus { if t == nil { return nil } switch *t { case shared.DomainStatusRegistered: v := types.DomainStatusRegistered return &v case shared.DomainStatusDeprecated: v := types.DomainStatusDeprecated return &v case shared.DomainStatusDeleted: v := types.DomainStatusDeleted return &v } panic("unexpected enum value") } // FromEncodingType converts internal EncodingType type to thrift func FromEncodingType(t *types.EncodingType) *shared.EncodingType { if t == nil { return nil } switch *t { case types.EncodingTypeThriftRW: v := shared.EncodingTypeThriftRW return &v case types.EncodingTypeJSON: v := shared.EncodingTypeJSON return &v } panic("unexpected enum value") } // ToEncodingType converts thrift EncodingType type to internal func ToEncodingType(t *shared.EncodingType) *types.EncodingType { if t == nil { return nil } switch *t { case shared.EncodingTypeThriftRW: v := types.EncodingTypeThriftRW return &v case shared.EncodingTypeJSON: v := types.EncodingTypeJSON return &v } panic("unexpected enum value") } // FromEntityNotExistsError converts internal EntityNotExistsError type to thrift func FromEntityNotExistsError(t *types.EntityNotExistsError) *shared.EntityNotExistsError { if t == nil { return nil } return &shared.EntityNotExistsError{ Message: t.Message, CurrentCluster: &t.CurrentCluster, ActiveCluster: &t.ActiveCluster, ActiveClusters: t.ActiveClusters, } } // ToEntityNotExistsError converts thrift EntityNotExistsError type to internal func ToEntityNotExistsError(t *shared.EntityNotExistsError) *types.EntityNotExistsError { if t == nil { return nil } return &types.EntityNotExistsError{ Message: t.Message, CurrentCluster: t.GetCurrentCluster(), ActiveCluster: t.GetActiveCluster(), ActiveClusters: t.GetActiveClusters(), } } // FromWorkflowExecutionAlreadyCompletedError converts internal WorkflowExecutionAlreadyCompletedError type to thrift func FromWorkflowExecutionAlreadyCompletedError(t *types.WorkflowExecutionAlreadyCompletedError) *shared.WorkflowExecutionAlreadyCompletedError { if t == nil { return nil } return &shared.WorkflowExecutionAlreadyCompletedError{ Message: t.Message, } } // ToWorkflowExecutionAlreadyCompletedError converts thrift WorkflowExecutionAlreadyCompletedError type to internal func ToWorkflowExecutionAlreadyCompletedError(t *shared.WorkflowExecutionAlreadyCompletedError) *types.WorkflowExecutionAlreadyCompletedError { if t == nil { return nil } return &types.WorkflowExecutionAlreadyCompletedError{ Message: t.Message, } } // FromEventType converts internal EventType type to thrift func FromEventType(t *types.EventType) *shared.EventType { if t == nil { return nil } switch *t { case types.EventTypeWorkflowExecutionStarted: v := shared.EventTypeWorkflowExecutionStarted return &v case types.EventTypeWorkflowExecutionCompleted: v := shared.EventTypeWorkflowExecutionCompleted return &v case types.EventTypeWorkflowExecutionFailed: v := shared.EventTypeWorkflowExecutionFailed return &v case types.EventTypeWorkflowExecutionTimedOut: v := shared.EventTypeWorkflowExecutionTimedOut return &v case types.EventTypeDecisionTaskScheduled: v := shared.EventTypeDecisionTaskScheduled return &v case types.EventTypeDecisionTaskStarted: v := shared.EventTypeDecisionTaskStarted return &v case types.EventTypeDecisionTaskCompleted: v := shared.EventTypeDecisionTaskCompleted return &v case types.EventTypeDecisionTaskTimedOut: v := shared.EventTypeDecisionTaskTimedOut return &v case types.EventTypeDecisionTaskFailed: v := shared.EventTypeDecisionTaskFailed return &v case types.EventTypeActivityTaskScheduled: v := shared.EventTypeActivityTaskScheduled return &v case types.EventTypeActivityTaskStarted: v := shared.EventTypeActivityTaskStarted return &v case types.EventTypeActivityTaskCompleted: v := shared.EventTypeActivityTaskCompleted return &v case types.EventTypeActivityTaskFailed: v := shared.EventTypeActivityTaskFailed return &v case types.EventTypeActivityTaskTimedOut: v := shared.EventTypeActivityTaskTimedOut return &v case types.EventTypeActivityTaskCancelRequested: v := shared.EventTypeActivityTaskCancelRequested return &v case types.EventTypeRequestCancelActivityTaskFailed: v := shared.EventTypeRequestCancelActivityTaskFailed return &v case types.EventTypeActivityTaskCanceled: v := shared.EventTypeActivityTaskCanceled return &v case types.EventTypeTimerStarted: v := shared.EventTypeTimerStarted return &v case types.EventTypeTimerFired: v := shared.EventTypeTimerFired return &v case types.EventTypeCancelTimerFailed: v := shared.EventTypeCancelTimerFailed return &v case types.EventTypeTimerCanceled: v := shared.EventTypeTimerCanceled return &v case types.EventTypeWorkflowExecutionCancelRequested: v := shared.EventTypeWorkflowExecutionCancelRequested return &v case types.EventTypeWorkflowExecutionCanceled: v := shared.EventTypeWorkflowExecutionCanceled return &v case types.EventTypeRequestCancelExternalWorkflowExecutionInitiated: v := shared.EventTypeRequestCancelExternalWorkflowExecutionInitiated return &v case types.EventTypeRequestCancelExternalWorkflowExecutionFailed: v := shared.EventTypeRequestCancelExternalWorkflowExecutionFailed return &v case types.EventTypeExternalWorkflowExecutionCancelRequested: v := shared.EventTypeExternalWorkflowExecutionCancelRequested return &v case types.EventTypeMarkerRecorded: v := shared.EventTypeMarkerRecorded return &v case types.EventTypeWorkflowExecutionSignaled: v := shared.EventTypeWorkflowExecutionSignaled return &v case types.EventTypeWorkflowExecutionTerminated: v := shared.EventTypeWorkflowExecutionTerminated return &v case types.EventTypeWorkflowExecutionContinuedAsNew: v := shared.EventTypeWorkflowExecutionContinuedAsNew return &v case types.EventTypeStartChildWorkflowExecutionInitiated: v := shared.EventTypeStartChildWorkflowExecutionInitiated return &v case types.EventTypeStartChildWorkflowExecutionFailed: v := shared.EventTypeStartChildWorkflowExecutionFailed return &v case types.EventTypeChildWorkflowExecutionStarted: v := shared.EventTypeChildWorkflowExecutionStarted return &v case types.EventTypeChildWorkflowExecutionCompleted: v := shared.EventTypeChildWorkflowExecutionCompleted return &v case types.EventTypeChildWorkflowExecutionFailed: v := shared.EventTypeChildWorkflowExecutionFailed return &v case types.EventTypeChildWorkflowExecutionCanceled: v := shared.EventTypeChildWorkflowExecutionCanceled return &v case types.EventTypeChildWorkflowExecutionTimedOut: v := shared.EventTypeChildWorkflowExecutionTimedOut return &v case types.EventTypeChildWorkflowExecutionTerminated: v := shared.EventTypeChildWorkflowExecutionTerminated return &v case types.EventTypeSignalExternalWorkflowExecutionInitiated: v := shared.EventTypeSignalExternalWorkflowExecutionInitiated return &v case types.EventTypeSignalExternalWorkflowExecutionFailed: v := shared.EventTypeSignalExternalWorkflowExecutionFailed return &v case types.EventTypeExternalWorkflowExecutionSignaled: v := shared.EventTypeExternalWorkflowExecutionSignaled return &v case types.EventTypeUpsertWorkflowSearchAttributes: v := shared.EventTypeUpsertWorkflowSearchAttributes return &v } panic("unexpected enum value") } // ToEventType converts thrift EventType type to internal func ToEventType(t *shared.EventType) *types.EventType { if t == nil { return nil } switch *t { case shared.EventTypeWorkflowExecutionStarted: v := types.EventTypeWorkflowExecutionStarted return &v case shared.EventTypeWorkflowExecutionCompleted: v := types.EventTypeWorkflowExecutionCompleted return &v case shared.EventTypeWorkflowExecutionFailed: v := types.EventTypeWorkflowExecutionFailed return &v case shared.EventTypeWorkflowExecutionTimedOut: v := types.EventTypeWorkflowExecutionTimedOut return &v case shared.EventTypeDecisionTaskScheduled: v := types.EventTypeDecisionTaskScheduled return &v case shared.EventTypeDecisionTaskStarted: v := types.EventTypeDecisionTaskStarted return &v case shared.EventTypeDecisionTaskCompleted: v := types.EventTypeDecisionTaskCompleted return &v case shared.EventTypeDecisionTaskTimedOut: v := types.EventTypeDecisionTaskTimedOut return &v case shared.EventTypeDecisionTaskFailed: v := types.EventTypeDecisionTaskFailed return &v case shared.EventTypeActivityTaskScheduled: v := types.EventTypeActivityTaskScheduled return &v case shared.EventTypeActivityTaskStarted: v := types.EventTypeActivityTaskStarted return &v case shared.EventTypeActivityTaskCompleted: v := types.EventTypeActivityTaskCompleted return &v case shared.EventTypeActivityTaskFailed: v := types.EventTypeActivityTaskFailed return &v case shared.EventTypeActivityTaskTimedOut: v := types.EventTypeActivityTaskTimedOut return &v case shared.EventTypeActivityTaskCancelRequested: v := types.EventTypeActivityTaskCancelRequested return &v case shared.EventTypeRequestCancelActivityTaskFailed: v := types.EventTypeRequestCancelActivityTaskFailed return &v case shared.EventTypeActivityTaskCanceled: v := types.EventTypeActivityTaskCanceled return &v case shared.EventTypeTimerStarted: v := types.EventTypeTimerStarted return &v case shared.EventTypeTimerFired: v := types.EventTypeTimerFired return &v case shared.EventTypeCancelTimerFailed: v := types.EventTypeCancelTimerFailed return &v case shared.EventTypeTimerCanceled: v := types.EventTypeTimerCanceled return &v case shared.EventTypeWorkflowExecutionCancelRequested: v := types.EventTypeWorkflowExecutionCancelRequested return &v case shared.EventTypeWorkflowExecutionCanceled: v := types.EventTypeWorkflowExecutionCanceled return &v case shared.EventTypeRequestCancelExternalWorkflowExecutionInitiated: v := types.EventTypeRequestCancelExternalWorkflowExecutionInitiated return &v case shared.EventTypeRequestCancelExternalWorkflowExecutionFailed: v := types.EventTypeRequestCancelExternalWorkflowExecutionFailed return &v case shared.EventTypeExternalWorkflowExecutionCancelRequested: v := types.EventTypeExternalWorkflowExecutionCancelRequested return &v case shared.EventTypeMarkerRecorded: v := types.EventTypeMarkerRecorded return &v case shared.EventTypeWorkflowExecutionSignaled: v := types.EventTypeWorkflowExecutionSignaled return &v case shared.EventTypeWorkflowExecutionTerminated: v := types.EventTypeWorkflowExecutionTerminated return &v case shared.EventTypeWorkflowExecutionContinuedAsNew: v := types.EventTypeWorkflowExecutionContinuedAsNew return &v case shared.EventTypeStartChildWorkflowExecutionInitiated: v := types.EventTypeStartChildWorkflowExecutionInitiated return &v case shared.EventTypeStartChildWorkflowExecutionFailed: v := types.EventTypeStartChildWorkflowExecutionFailed return &v case shared.EventTypeChildWorkflowExecutionStarted: v := types.EventTypeChildWorkflowExecutionStarted return &v case shared.EventTypeChildWorkflowExecutionCompleted: v := types.EventTypeChildWorkflowExecutionCompleted return &v case shared.EventTypeChildWorkflowExecutionFailed: v := types.EventTypeChildWorkflowExecutionFailed return &v case shared.EventTypeChildWorkflowExecutionCanceled: v := types.EventTypeChildWorkflowExecutionCanceled return &v case shared.EventTypeChildWorkflowExecutionTimedOut: v := types.EventTypeChildWorkflowExecutionTimedOut return &v case shared.EventTypeChildWorkflowExecutionTerminated: v := types.EventTypeChildWorkflowExecutionTerminated return &v case shared.EventTypeSignalExternalWorkflowExecutionInitiated: v := types.EventTypeSignalExternalWorkflowExecutionInitiated return &v case shared.EventTypeSignalExternalWorkflowExecutionFailed: v := types.EventTypeSignalExternalWorkflowExecutionFailed return &v case shared.EventTypeExternalWorkflowExecutionSignaled: v := types.EventTypeExternalWorkflowExecutionSignaled return &v case shared.EventTypeUpsertWorkflowSearchAttributes: v := types.EventTypeUpsertWorkflowSearchAttributes return &v } panic("unexpected enum value") } // FromExternalWorkflowExecutionCancelRequestedEventAttributes converts internal ExternalWorkflowExecutionCancelRequestedEventAttributes type to thrift func FromExternalWorkflowExecutionCancelRequestedEventAttributes(t *types.ExternalWorkflowExecutionCancelRequestedEventAttributes) *shared.ExternalWorkflowExecutionCancelRequestedEventAttributes { if t == nil { return nil } return &shared.ExternalWorkflowExecutionCancelRequestedEventAttributes{ InitiatedEventId: &t.InitiatedEventID, Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), } } // ToExternalWorkflowExecutionCancelRequestedEventAttributes converts thrift ExternalWorkflowExecutionCancelRequestedEventAttributes type to internal func ToExternalWorkflowExecutionCancelRequestedEventAttributes(t *shared.ExternalWorkflowExecutionCancelRequestedEventAttributes) *types.ExternalWorkflowExecutionCancelRequestedEventAttributes { if t == nil { return nil } return &types.ExternalWorkflowExecutionCancelRequestedEventAttributes{ InitiatedEventID: t.GetInitiatedEventId(), Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), } } // FromExternalWorkflowExecutionSignaledEventAttributes converts internal ExternalWorkflowExecutionSignaledEventAttributes type to thrift func FromExternalWorkflowExecutionSignaledEventAttributes(t *types.ExternalWorkflowExecutionSignaledEventAttributes) *shared.ExternalWorkflowExecutionSignaledEventAttributes { if t == nil { return nil } return &shared.ExternalWorkflowExecutionSignaledEventAttributes{ InitiatedEventId: &t.InitiatedEventID, Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Control: t.Control, } } // ToExternalWorkflowExecutionSignaledEventAttributes converts thrift ExternalWorkflowExecutionSignaledEventAttributes type to internal func ToExternalWorkflowExecutionSignaledEventAttributes(t *shared.ExternalWorkflowExecutionSignaledEventAttributes) *types.ExternalWorkflowExecutionSignaledEventAttributes { if t == nil { return nil } return &types.ExternalWorkflowExecutionSignaledEventAttributes{ InitiatedEventID: t.GetInitiatedEventId(), Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Control: t.Control, } } // FromFailWorkflowExecutionDecisionAttributes converts internal FailWorkflowExecutionDecisionAttributes type to thrift func FromFailWorkflowExecutionDecisionAttributes(t *types.FailWorkflowExecutionDecisionAttributes) *shared.FailWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &shared.FailWorkflowExecutionDecisionAttributes{ Reason: t.Reason, Details: t.Details, } } // ToFailWorkflowExecutionDecisionAttributes converts thrift FailWorkflowExecutionDecisionAttributes type to internal func ToFailWorkflowExecutionDecisionAttributes(t *shared.FailWorkflowExecutionDecisionAttributes) *types.FailWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.FailWorkflowExecutionDecisionAttributes{ Reason: t.Reason, Details: t.Details, } } // FromGetSearchAttributesResponse converts internal GetSearchAttributesResponse type to thrift func FromGetSearchAttributesResponse(t *types.GetSearchAttributesResponse) *shared.GetSearchAttributesResponse { if t == nil { return nil } return &shared.GetSearchAttributesResponse{ Keys: FromIndexedValueTypeMap(t.Keys), } } // ToGetSearchAttributesResponse converts thrift GetSearchAttributesResponse type to internal func ToGetSearchAttributesResponse(t *shared.GetSearchAttributesResponse) *types.GetSearchAttributesResponse { if t == nil { return nil } return &types.GetSearchAttributesResponse{ Keys: ToIndexedValueTypeMap(t.Keys), } } // FromGetWorkflowExecutionHistoryRequest converts internal GetWorkflowExecutionHistoryRequest type to thrift func FromGetWorkflowExecutionHistoryRequest(t *types.GetWorkflowExecutionHistoryRequest) *shared.GetWorkflowExecutionHistoryRequest { if t == nil { return nil } return &shared.GetWorkflowExecutionHistoryRequest{ Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), MaximumPageSize: &t.MaximumPageSize, NextPageToken: t.NextPageToken, WaitForNewEvent: &t.WaitForNewEvent, HistoryEventFilterType: FromHistoryEventFilterType(t.HistoryEventFilterType), SkipArchival: &t.SkipArchival, QueryConsistencyLevel: FromQueryConsistencyLevel(t.QueryConsistencyLevel), } } // ToGetWorkflowExecutionHistoryRequest converts thrift GetWorkflowExecutionHistoryRequest type to internal func ToGetWorkflowExecutionHistoryRequest(t *shared.GetWorkflowExecutionHistoryRequest) *types.GetWorkflowExecutionHistoryRequest { if t == nil { return nil } return &types.GetWorkflowExecutionHistoryRequest{ Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), MaximumPageSize: t.GetMaximumPageSize(), NextPageToken: t.NextPageToken, WaitForNewEvent: t.GetWaitForNewEvent(), HistoryEventFilterType: ToHistoryEventFilterType(t.HistoryEventFilterType), SkipArchival: t.GetSkipArchival(), QueryConsistencyLevel: ToQueryConsistencyLevel(t.QueryConsistencyLevel), } } // FromGetWorkflowExecutionHistoryResponse converts internal GetWorkflowExecutionHistoryResponse type to thrift func FromGetWorkflowExecutionHistoryResponse(t *types.GetWorkflowExecutionHistoryResponse) *shared.GetWorkflowExecutionHistoryResponse { if t == nil { return nil } return &shared.GetWorkflowExecutionHistoryResponse{ History: FromHistory(t.History), RawHistory: FromDataBlobArray(t.RawHistory), NextPageToken: t.NextPageToken, Archived: &t.Archived, } } // ToGetWorkflowExecutionHistoryResponse converts thrift GetWorkflowExecutionHistoryResponse type to internal func ToGetWorkflowExecutionHistoryResponse(t *shared.GetWorkflowExecutionHistoryResponse) *types.GetWorkflowExecutionHistoryResponse { if t == nil { return nil } return &types.GetWorkflowExecutionHistoryResponse{ History: ToHistory(t.History), RawHistory: ToDataBlobArray(t.RawHistory), NextPageToken: t.NextPageToken, Archived: t.GetArchived(), } } // FromHeader converts internal Header type to thrift func FromHeader(t *types.Header) *shared.Header { if t == nil { return nil } return &shared.Header{ Fields: t.Fields, } } // ToHeader converts thrift Header type to internal func ToHeader(t *shared.Header) *types.Header { if t == nil { return nil } return &types.Header{ Fields: t.Fields, } } // FromHistory converts internal History type to thrift func FromHistory(t *types.History) *shared.History { if t == nil { return nil } return &shared.History{ Events: FromHistoryEventArray(t.Events), } } // ToHistory converts thrift History type to internal func ToHistory(t *shared.History) *types.History { if t == nil { return nil } return &types.History{ Events: ToHistoryEventArray(t.Events), } } // FromHistoryBranch converts internal HistoryBranch type to thrift func FromHistoryBranch(t *types.HistoryBranch) *shared.HistoryBranch { if t == nil { return nil } return &shared.HistoryBranch{ TreeID: &t.TreeID, BranchID: &t.BranchID, Ancestors: FromHistoryBranchRangeArray(t.Ancestors), } } // ToHistoryBranch converts thrift HistoryBranch type to internal func ToHistoryBranch(t *shared.HistoryBranch) *types.HistoryBranch { if t == nil { return nil } return &types.HistoryBranch{ TreeID: *t.TreeID, BranchID: *t.BranchID, Ancestors: ToHistoryBranchRangeArray(t.Ancestors), } } // FromHistoryBranchRange converts internal HistoryBranchRange type to thrift func FromHistoryBranchRange(t *types.HistoryBranchRange) *shared.HistoryBranchRange { if t == nil { return nil } return &shared.HistoryBranchRange{ BranchID: &t.BranchID, BeginNodeID: &t.BeginNodeID, EndNodeID: &t.EndNodeID, } } // ToHistoryBranchRange converts thrift HistoryBranchRange type to internal func ToHistoryBranchRange(t *shared.HistoryBranchRange) *types.HistoryBranchRange { if t == nil { return nil } return &types.HistoryBranchRange{ BranchID: *t.BranchID, BeginNodeID: *t.BeginNodeID, EndNodeID: *t.EndNodeID, } } // FromHistoryEvent converts internal HistoryEvent type to thrift func FromHistoryEvent(t *types.HistoryEvent) *shared.HistoryEvent { if t == nil { return nil } return &shared.HistoryEvent{ EventId: &t.ID, Timestamp: t.Timestamp, EventType: FromEventType(t.EventType), Version: &t.Version, TaskId: &t.TaskID, WorkflowExecutionStartedEventAttributes: FromWorkflowExecutionStartedEventAttributes(t.WorkflowExecutionStartedEventAttributes), WorkflowExecutionCompletedEventAttributes: FromWorkflowExecutionCompletedEventAttributes(t.WorkflowExecutionCompletedEventAttributes), WorkflowExecutionFailedEventAttributes: FromWorkflowExecutionFailedEventAttributes(t.WorkflowExecutionFailedEventAttributes), WorkflowExecutionTimedOutEventAttributes: FromWorkflowExecutionTimedOutEventAttributes(t.WorkflowExecutionTimedOutEventAttributes), DecisionTaskScheduledEventAttributes: FromDecisionTaskScheduledEventAttributes(t.DecisionTaskScheduledEventAttributes), DecisionTaskStartedEventAttributes: FromDecisionTaskStartedEventAttributes(t.DecisionTaskStartedEventAttributes), DecisionTaskCompletedEventAttributes: FromDecisionTaskCompletedEventAttributes(t.DecisionTaskCompletedEventAttributes), DecisionTaskTimedOutEventAttributes: FromDecisionTaskTimedOutEventAttributes(t.DecisionTaskTimedOutEventAttributes), DecisionTaskFailedEventAttributes: FromDecisionTaskFailedEventAttributes(t.DecisionTaskFailedEventAttributes), ActivityTaskScheduledEventAttributes: FromActivityTaskScheduledEventAttributes(t.ActivityTaskScheduledEventAttributes), ActivityTaskStartedEventAttributes: FromActivityTaskStartedEventAttributes(t.ActivityTaskStartedEventAttributes), ActivityTaskCompletedEventAttributes: FromActivityTaskCompletedEventAttributes(t.ActivityTaskCompletedEventAttributes), ActivityTaskFailedEventAttributes: FromActivityTaskFailedEventAttributes(t.ActivityTaskFailedEventAttributes), ActivityTaskTimedOutEventAttributes: FromActivityTaskTimedOutEventAttributes(t.ActivityTaskTimedOutEventAttributes), TimerStartedEventAttributes: FromTimerStartedEventAttributes(t.TimerStartedEventAttributes), TimerFiredEventAttributes: FromTimerFiredEventAttributes(t.TimerFiredEventAttributes), ActivityTaskCancelRequestedEventAttributes: FromActivityTaskCancelRequestedEventAttributes(t.ActivityTaskCancelRequestedEventAttributes), RequestCancelActivityTaskFailedEventAttributes: FromRequestCancelActivityTaskFailedEventAttributes(t.RequestCancelActivityTaskFailedEventAttributes), ActivityTaskCanceledEventAttributes: FromActivityTaskCanceledEventAttributes(t.ActivityTaskCanceledEventAttributes), TimerCanceledEventAttributes: FromTimerCanceledEventAttributes(t.TimerCanceledEventAttributes), CancelTimerFailedEventAttributes: FromCancelTimerFailedEventAttributes(t.CancelTimerFailedEventAttributes), MarkerRecordedEventAttributes: FromMarkerRecordedEventAttributes(t.MarkerRecordedEventAttributes), WorkflowExecutionSignaledEventAttributes: FromWorkflowExecutionSignaledEventAttributes(t.WorkflowExecutionSignaledEventAttributes), WorkflowExecutionTerminatedEventAttributes: FromWorkflowExecutionTerminatedEventAttributes(t.WorkflowExecutionTerminatedEventAttributes), WorkflowExecutionCancelRequestedEventAttributes: FromWorkflowExecutionCancelRequestedEventAttributes(t.WorkflowExecutionCancelRequestedEventAttributes), WorkflowExecutionCanceledEventAttributes: FromWorkflowExecutionCanceledEventAttributes(t.WorkflowExecutionCanceledEventAttributes), RequestCancelExternalWorkflowExecutionInitiatedEventAttributes: FromRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(t.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes), RequestCancelExternalWorkflowExecutionFailedEventAttributes: FromRequestCancelExternalWorkflowExecutionFailedEventAttributes(t.RequestCancelExternalWorkflowExecutionFailedEventAttributes), ExternalWorkflowExecutionCancelRequestedEventAttributes: FromExternalWorkflowExecutionCancelRequestedEventAttributes(t.ExternalWorkflowExecutionCancelRequestedEventAttributes), WorkflowExecutionContinuedAsNewEventAttributes: FromWorkflowExecutionContinuedAsNewEventAttributes(t.WorkflowExecutionContinuedAsNewEventAttributes), StartChildWorkflowExecutionInitiatedEventAttributes: FromStartChildWorkflowExecutionInitiatedEventAttributes(t.StartChildWorkflowExecutionInitiatedEventAttributes), StartChildWorkflowExecutionFailedEventAttributes: FromStartChildWorkflowExecutionFailedEventAttributes(t.StartChildWorkflowExecutionFailedEventAttributes), ChildWorkflowExecutionStartedEventAttributes: FromChildWorkflowExecutionStartedEventAttributes(t.ChildWorkflowExecutionStartedEventAttributes), ChildWorkflowExecutionCompletedEventAttributes: FromChildWorkflowExecutionCompletedEventAttributes(t.ChildWorkflowExecutionCompletedEventAttributes), ChildWorkflowExecutionFailedEventAttributes: FromChildWorkflowExecutionFailedEventAttributes(t.ChildWorkflowExecutionFailedEventAttributes), ChildWorkflowExecutionCanceledEventAttributes: FromChildWorkflowExecutionCanceledEventAttributes(t.ChildWorkflowExecutionCanceledEventAttributes), ChildWorkflowExecutionTimedOutEventAttributes: FromChildWorkflowExecutionTimedOutEventAttributes(t.ChildWorkflowExecutionTimedOutEventAttributes), ChildWorkflowExecutionTerminatedEventAttributes: FromChildWorkflowExecutionTerminatedEventAttributes(t.ChildWorkflowExecutionTerminatedEventAttributes), SignalExternalWorkflowExecutionInitiatedEventAttributes: FromSignalExternalWorkflowExecutionInitiatedEventAttributes(t.SignalExternalWorkflowExecutionInitiatedEventAttributes), SignalExternalWorkflowExecutionFailedEventAttributes: FromSignalExternalWorkflowExecutionFailedEventAttributes(t.SignalExternalWorkflowExecutionFailedEventAttributes), ExternalWorkflowExecutionSignaledEventAttributes: FromExternalWorkflowExecutionSignaledEventAttributes(t.ExternalWorkflowExecutionSignaledEventAttributes), UpsertWorkflowSearchAttributesEventAttributes: FromUpsertWorkflowSearchAttributesEventAttributes(t.UpsertWorkflowSearchAttributesEventAttributes), } } // ToHistoryEvent converts thrift HistoryEvent type to internal func ToHistoryEvent(t *shared.HistoryEvent) *types.HistoryEvent { if t == nil { return nil } return &types.HistoryEvent{ ID: t.GetEventId(), Timestamp: t.Timestamp, EventType: ToEventType(t.EventType), Version: t.GetVersion(), TaskID: t.GetTaskId(), WorkflowExecutionStartedEventAttributes: ToWorkflowExecutionStartedEventAttributes(t.WorkflowExecutionStartedEventAttributes), WorkflowExecutionCompletedEventAttributes: ToWorkflowExecutionCompletedEventAttributes(t.WorkflowExecutionCompletedEventAttributes), WorkflowExecutionFailedEventAttributes: ToWorkflowExecutionFailedEventAttributes(t.WorkflowExecutionFailedEventAttributes), WorkflowExecutionTimedOutEventAttributes: ToWorkflowExecutionTimedOutEventAttributes(t.WorkflowExecutionTimedOutEventAttributes), DecisionTaskScheduledEventAttributes: ToDecisionTaskScheduledEventAttributes(t.DecisionTaskScheduledEventAttributes), DecisionTaskStartedEventAttributes: ToDecisionTaskStartedEventAttributes(t.DecisionTaskStartedEventAttributes), DecisionTaskCompletedEventAttributes: ToDecisionTaskCompletedEventAttributes(t.DecisionTaskCompletedEventAttributes), DecisionTaskTimedOutEventAttributes: ToDecisionTaskTimedOutEventAttributes(t.DecisionTaskTimedOutEventAttributes), DecisionTaskFailedEventAttributes: ToDecisionTaskFailedEventAttributes(t.DecisionTaskFailedEventAttributes), ActivityTaskScheduledEventAttributes: ToActivityTaskScheduledEventAttributes(t.ActivityTaskScheduledEventAttributes), ActivityTaskStartedEventAttributes: ToActivityTaskStartedEventAttributes(t.ActivityTaskStartedEventAttributes), ActivityTaskCompletedEventAttributes: ToActivityTaskCompletedEventAttributes(t.ActivityTaskCompletedEventAttributes), ActivityTaskFailedEventAttributes: ToActivityTaskFailedEventAttributes(t.ActivityTaskFailedEventAttributes), ActivityTaskTimedOutEventAttributes: ToActivityTaskTimedOutEventAttributes(t.ActivityTaskTimedOutEventAttributes), TimerStartedEventAttributes: ToTimerStartedEventAttributes(t.TimerStartedEventAttributes), TimerFiredEventAttributes: ToTimerFiredEventAttributes(t.TimerFiredEventAttributes), ActivityTaskCancelRequestedEventAttributes: ToActivityTaskCancelRequestedEventAttributes(t.ActivityTaskCancelRequestedEventAttributes), RequestCancelActivityTaskFailedEventAttributes: ToRequestCancelActivityTaskFailedEventAttributes(t.RequestCancelActivityTaskFailedEventAttributes), ActivityTaskCanceledEventAttributes: ToActivityTaskCanceledEventAttributes(t.ActivityTaskCanceledEventAttributes), TimerCanceledEventAttributes: ToTimerCanceledEventAttributes(t.TimerCanceledEventAttributes), CancelTimerFailedEventAttributes: ToCancelTimerFailedEventAttributes(t.CancelTimerFailedEventAttributes), MarkerRecordedEventAttributes: ToMarkerRecordedEventAttributes(t.MarkerRecordedEventAttributes), WorkflowExecutionSignaledEventAttributes: ToWorkflowExecutionSignaledEventAttributes(t.WorkflowExecutionSignaledEventAttributes), WorkflowExecutionTerminatedEventAttributes: ToWorkflowExecutionTerminatedEventAttributes(t.WorkflowExecutionTerminatedEventAttributes), WorkflowExecutionCancelRequestedEventAttributes: ToWorkflowExecutionCancelRequestedEventAttributes(t.WorkflowExecutionCancelRequestedEventAttributes), WorkflowExecutionCanceledEventAttributes: ToWorkflowExecutionCanceledEventAttributes(t.WorkflowExecutionCanceledEventAttributes), RequestCancelExternalWorkflowExecutionInitiatedEventAttributes: ToRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(t.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes), RequestCancelExternalWorkflowExecutionFailedEventAttributes: ToRequestCancelExternalWorkflowExecutionFailedEventAttributes(t.RequestCancelExternalWorkflowExecutionFailedEventAttributes), ExternalWorkflowExecutionCancelRequestedEventAttributes: ToExternalWorkflowExecutionCancelRequestedEventAttributes(t.ExternalWorkflowExecutionCancelRequestedEventAttributes), WorkflowExecutionContinuedAsNewEventAttributes: ToWorkflowExecutionContinuedAsNewEventAttributes(t.WorkflowExecutionContinuedAsNewEventAttributes), StartChildWorkflowExecutionInitiatedEventAttributes: ToStartChildWorkflowExecutionInitiatedEventAttributes(t.StartChildWorkflowExecutionInitiatedEventAttributes), StartChildWorkflowExecutionFailedEventAttributes: ToStartChildWorkflowExecutionFailedEventAttributes(t.StartChildWorkflowExecutionFailedEventAttributes), ChildWorkflowExecutionStartedEventAttributes: ToChildWorkflowExecutionStartedEventAttributes(t.ChildWorkflowExecutionStartedEventAttributes), ChildWorkflowExecutionCompletedEventAttributes: ToChildWorkflowExecutionCompletedEventAttributes(t.ChildWorkflowExecutionCompletedEventAttributes), ChildWorkflowExecutionFailedEventAttributes: ToChildWorkflowExecutionFailedEventAttributes(t.ChildWorkflowExecutionFailedEventAttributes), ChildWorkflowExecutionCanceledEventAttributes: ToChildWorkflowExecutionCanceledEventAttributes(t.ChildWorkflowExecutionCanceledEventAttributes), ChildWorkflowExecutionTimedOutEventAttributes: ToChildWorkflowExecutionTimedOutEventAttributes(t.ChildWorkflowExecutionTimedOutEventAttributes), ChildWorkflowExecutionTerminatedEventAttributes: ToChildWorkflowExecutionTerminatedEventAttributes(t.ChildWorkflowExecutionTerminatedEventAttributes), SignalExternalWorkflowExecutionInitiatedEventAttributes: ToSignalExternalWorkflowExecutionInitiatedEventAttributes(t.SignalExternalWorkflowExecutionInitiatedEventAttributes), SignalExternalWorkflowExecutionFailedEventAttributes: ToSignalExternalWorkflowExecutionFailedEventAttributes(t.SignalExternalWorkflowExecutionFailedEventAttributes), ExternalWorkflowExecutionSignaledEventAttributes: ToExternalWorkflowExecutionSignaledEventAttributes(t.ExternalWorkflowExecutionSignaledEventAttributes), UpsertWorkflowSearchAttributesEventAttributes: ToUpsertWorkflowSearchAttributesEventAttributes(t.UpsertWorkflowSearchAttributesEventAttributes), } } // FromHistoryEventFilterType converts internal HistoryEventFilterType type to thrift func FromHistoryEventFilterType(t *types.HistoryEventFilterType) *shared.HistoryEventFilterType { if t == nil { return nil } switch *t { case types.HistoryEventFilterTypeAllEvent: v := shared.HistoryEventFilterTypeAllEvent return &v case types.HistoryEventFilterTypeCloseEvent: v := shared.HistoryEventFilterTypeCloseEvent return &v } panic("unexpected enum value") } // ToHistoryEventFilterType converts thrift HistoryEventFilterType type to internal func ToHistoryEventFilterType(t *shared.HistoryEventFilterType) *types.HistoryEventFilterType { if t == nil { return nil } switch *t { case shared.HistoryEventFilterTypeAllEvent: v := types.HistoryEventFilterTypeAllEvent return &v case shared.HistoryEventFilterTypeCloseEvent: v := types.HistoryEventFilterTypeCloseEvent return &v } panic("unexpected enum value") } // FromIndexedValueType converts internal IndexedValueType type to thrift func FromIndexedValueType(t types.IndexedValueType) shared.IndexedValueType { switch t { case types.IndexedValueTypeString: return shared.IndexedValueTypeString case types.IndexedValueTypeKeyword: return shared.IndexedValueTypeKeyword case types.IndexedValueTypeInt: return shared.IndexedValueTypeInt case types.IndexedValueTypeDouble: return shared.IndexedValueTypeDouble case types.IndexedValueTypeBool: return shared.IndexedValueTypeBool case types.IndexedValueTypeDatetime: return shared.IndexedValueTypeDatetime } panic("unexpected enum value") } // ToIndexedValueType converts thrift IndexedValueType type to internal func ToIndexedValueType(t shared.IndexedValueType) types.IndexedValueType { switch t { case shared.IndexedValueTypeString: return types.IndexedValueTypeString case shared.IndexedValueTypeKeyword: return types.IndexedValueTypeKeyword case shared.IndexedValueTypeInt: return types.IndexedValueTypeInt case shared.IndexedValueTypeDouble: return types.IndexedValueTypeDouble case shared.IndexedValueTypeBool: return types.IndexedValueTypeBool case shared.IndexedValueTypeDatetime: return types.IndexedValueTypeDatetime } panic("unexpected enum value") } // FromInternalDataInconsistencyError converts internal InternalDataInconsistencyError type to thrift func FromInternalDataInconsistencyError(t *types.InternalDataInconsistencyError) *shared.InternalDataInconsistencyError { if t == nil { return nil } return &shared.InternalDataInconsistencyError{ Message: t.Message, } } // ToInternalDataInconsistencyError converts thrift InternalDataInconsistencyError type to internal func ToInternalDataInconsistencyError(t *shared.InternalDataInconsistencyError) *types.InternalDataInconsistencyError { if t == nil { return nil } return &types.InternalDataInconsistencyError{ Message: t.Message, } } // FromInternalServiceError converts internal InternalServiceError type to thrift func FromInternalServiceError(t *types.InternalServiceError) *shared.InternalServiceError { if t == nil { return nil } return &shared.InternalServiceError{ Message: t.Message, } } // ToInternalServiceError converts thrift InternalServiceError type to internal func ToInternalServiceError(t *shared.InternalServiceError) *types.InternalServiceError { if t == nil { return nil } return &types.InternalServiceError{ Message: t.Message, } } // FromLimitExceededError converts internal LimitExceededError type to thrift func FromLimitExceededError(t *types.LimitExceededError) *shared.LimitExceededError { if t == nil { return nil } return &shared.LimitExceededError{ Message: t.Message, } } // ToLimitExceededError converts thrift LimitExceededError type to internal func ToLimitExceededError(t *shared.LimitExceededError) *types.LimitExceededError { if t == nil { return nil } return &types.LimitExceededError{ Message: t.Message, } } // FromListArchivedWorkflowExecutionsRequest converts internal ListArchivedWorkflowExecutionsRequest type to thrift func FromListArchivedWorkflowExecutionsRequest(t *types.ListArchivedWorkflowExecutionsRequest) *shared.ListArchivedWorkflowExecutionsRequest { if t == nil { return nil } return &shared.ListArchivedWorkflowExecutionsRequest{ Domain: &t.Domain, PageSize: &t.PageSize, NextPageToken: t.NextPageToken, Query: &t.Query, } } // ToListArchivedWorkflowExecutionsRequest converts thrift ListArchivedWorkflowExecutionsRequest type to internal func ToListArchivedWorkflowExecutionsRequest(t *shared.ListArchivedWorkflowExecutionsRequest) *types.ListArchivedWorkflowExecutionsRequest { if t == nil { return nil } return &types.ListArchivedWorkflowExecutionsRequest{ Domain: t.GetDomain(), PageSize: t.GetPageSize(), NextPageToken: t.NextPageToken, Query: t.GetQuery(), } } // FromListArchivedWorkflowExecutionsResponse converts internal ListArchivedWorkflowExecutionsResponse type to thrift func FromListArchivedWorkflowExecutionsResponse(t *types.ListArchivedWorkflowExecutionsResponse) *shared.ListArchivedWorkflowExecutionsResponse { if t == nil { return nil } return &shared.ListArchivedWorkflowExecutionsResponse{ Executions: FromWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } // ToListArchivedWorkflowExecutionsResponse converts thrift ListArchivedWorkflowExecutionsResponse type to internal func ToListArchivedWorkflowExecutionsResponse(t *shared.ListArchivedWorkflowExecutionsResponse) *types.ListArchivedWorkflowExecutionsResponse { if t == nil { return nil } return &types.ListArchivedWorkflowExecutionsResponse{ Executions: ToWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } // FromListClosedWorkflowExecutionsRequest converts internal ListClosedWorkflowExecutionsRequest type to thrift func FromListClosedWorkflowExecutionsRequest(t *types.ListClosedWorkflowExecutionsRequest) *shared.ListClosedWorkflowExecutionsRequest { if t == nil { return nil } return &shared.ListClosedWorkflowExecutionsRequest{ Domain: &t.Domain, MaximumPageSize: &t.MaximumPageSize, NextPageToken: t.NextPageToken, StartTimeFilter: FromStartTimeFilter(t.StartTimeFilter), ExecutionFilter: FromWorkflowExecutionFilter(t.ExecutionFilter), TypeFilter: FromWorkflowTypeFilter(t.TypeFilter), StatusFilter: FromWorkflowExecutionCloseStatus(t.StatusFilter), } } // ToListClosedWorkflowExecutionsRequest converts thrift ListClosedWorkflowExecutionsRequest type to internal func ToListClosedWorkflowExecutionsRequest(t *shared.ListClosedWorkflowExecutionsRequest) *types.ListClosedWorkflowExecutionsRequest { if t == nil { return nil } return &types.ListClosedWorkflowExecutionsRequest{ Domain: t.GetDomain(), MaximumPageSize: t.GetMaximumPageSize(), NextPageToken: t.NextPageToken, StartTimeFilter: ToStartTimeFilter(t.StartTimeFilter), ExecutionFilter: ToWorkflowExecutionFilter(t.ExecutionFilter), TypeFilter: ToWorkflowTypeFilter(t.TypeFilter), StatusFilter: ToWorkflowExecutionCloseStatus(t.StatusFilter), } } // FromListClosedWorkflowExecutionsResponse converts internal ListClosedWorkflowExecutionsResponse type to thrift func FromListClosedWorkflowExecutionsResponse(t *types.ListClosedWorkflowExecutionsResponse) *shared.ListClosedWorkflowExecutionsResponse { if t == nil { return nil } return &shared.ListClosedWorkflowExecutionsResponse{ Executions: FromWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } // ToListClosedWorkflowExecutionsResponse converts thrift ListClosedWorkflowExecutionsResponse type to internal func ToListClosedWorkflowExecutionsResponse(t *shared.ListClosedWorkflowExecutionsResponse) *types.ListClosedWorkflowExecutionsResponse { if t == nil { return nil } return &types.ListClosedWorkflowExecutionsResponse{ Executions: ToWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } // FromListDomainsRequest converts internal ListDomainsRequest type to thrift func FromListDomainsRequest(t *types.ListDomainsRequest) *shared.ListDomainsRequest { if t == nil { return nil } return &shared.ListDomainsRequest{ PageSize: &t.PageSize, NextPageToken: t.NextPageToken, } } // ToListDomainsRequest converts thrift ListDomainsRequest type to internal func ToListDomainsRequest(t *shared.ListDomainsRequest) *types.ListDomainsRequest { if t == nil { return nil } return &types.ListDomainsRequest{ PageSize: t.GetPageSize(), NextPageToken: t.NextPageToken, } } // FromListDomainsResponse converts internal ListDomainsResponse type to thrift func FromListDomainsResponse(t *types.ListDomainsResponse) *shared.ListDomainsResponse { if t == nil { return nil } return &shared.ListDomainsResponse{ Domains: FromDescribeDomainResponseArray(t.Domains), NextPageToken: t.NextPageToken, } } // ToListDomainsResponse converts thrift ListDomainsResponse type to internal func ToListDomainsResponse(t *shared.ListDomainsResponse) *types.ListDomainsResponse { if t == nil { return nil } return &types.ListDomainsResponse{ Domains: ToDescribeDomainResponseArray(t.Domains), NextPageToken: t.NextPageToken, } } // ToListFailoverHistoryRequest converts thrift ListFailoverHistoryRequest type to internal func ToListFailoverHistoryRequest(t *shared.ListFailoverHistoryRequest) *types.ListFailoverHistoryRequest { if t == nil { return nil } return &types.ListFailoverHistoryRequest{ Filters: ToListFailoverHistoryRequestFilters(t.Filters), Pagination: ToPaginationOptions(t.Pagination), } } // FromListFailoverHistoryRequest converts internal ListFailoverHistoryRequest type to thrift func FromListFailoverHistoryRequest(t *types.ListFailoverHistoryRequest) *shared.ListFailoverHistoryRequest { if t == nil { return nil } return &shared.ListFailoverHistoryRequest{ Filters: FromListFailoverHistoryRequestFilters(t.Filters), Pagination: FromPaginationOptions(t.Pagination), } } // FromListFailoverHistoryResponse converts internal ListFailoverHistoryResponse type to thrift func FromListFailoverHistoryResponse(t *types.ListFailoverHistoryResponse) *shared.ListFailoverHistoryResponse { if t == nil { return nil } return &shared.ListFailoverHistoryResponse{ FailoverEvents: FromFailoverEventArray(t.FailoverEvents), NextPageToken: t.NextPageToken, } } // ToListFailoverHistoryResponse converts thrift ListFailoverHistoryResponse type to internal func ToListFailoverHistoryResponse(t *shared.ListFailoverHistoryResponse) *types.ListFailoverHistoryResponse { if t == nil { return nil } return &types.ListFailoverHistoryResponse{ FailoverEvents: ToFailoverEventArray(t.FailoverEvents), NextPageToken: t.NextPageToken, } } // ToListFailoverHistoryRequestFilters converts thrift ListFailoverHistoryRequestFilters type to internal func ToListFailoverHistoryRequestFilters(t *shared.ListFailoverHistoryRequestFilters) *types.ListFailoverHistoryRequestFilters { if t == nil { return nil } return &types.ListFailoverHistoryRequestFilters{ DomainID: t.GetDomainID(), } } // FromListFailoverHistoryRequestFilters converts internal ListFailoverHistoryRequestFilters type to thrift func FromListFailoverHistoryRequestFilters(t *types.ListFailoverHistoryRequestFilters) *shared.ListFailoverHistoryRequestFilters { if t == nil { return nil } return &shared.ListFailoverHistoryRequestFilters{ DomainID: &t.DomainID, } } // ToPaginationOptions converts thrift PaginationOptions type to internal func ToPaginationOptions(t *shared.PaginationOptions) *types.PaginationOptions { if t == nil { return nil } return &types.PaginationOptions{ PageSize: t.PageSize, NextPageToken: t.NextPageToken, } } // FromPaginationOptions converts internal PaginationOptions type to thrift func FromPaginationOptions(t *types.PaginationOptions) *shared.PaginationOptions { if t == nil { return nil } return &shared.PaginationOptions{ PageSize: t.PageSize, NextPageToken: t.NextPageToken, } } // ToFailoverEvent converts thrift FailoverEvent type to internal func ToFailoverEvent(t *shared.FailoverEvent) *types.FailoverEvent { if t == nil { return nil } return &types.FailoverEvent{ ID: t.ID, CreatedTime: t.CreatedTime, FailoverType: ToFailoverType(t.FailoverType), ClusterFailovers: ToClusterFailoverArray(t.ClusterFailovers), } } // FromFailoverEvent converts internal FailoverEvent type to thrift func FromFailoverEvent(t *types.FailoverEvent) *shared.FailoverEvent { if t == nil { return nil } return &shared.FailoverEvent{ ID: t.ID, CreatedTime: t.CreatedTime, FailoverType: FromFailoverType(t.FailoverType), ClusterFailovers: FromClusterFailoverArray(t.ClusterFailovers), } } // ToFailoverEventArray converts thrift FailoverEvent array type to internal func ToFailoverEventArray(t []*shared.FailoverEvent) []*types.FailoverEvent { if t == nil { return nil } v := make([]*types.FailoverEvent, len(t)) for i := range t { v[i] = ToFailoverEvent(t[i]) } return v } // FromFailoverEventArray converts internal FailoverEvent array type to thrift func FromFailoverEventArray(t []*types.FailoverEvent) []*shared.FailoverEvent { if t == nil { return nil } v := make([]*shared.FailoverEvent, len(t)) for i := range t { v[i] = FromFailoverEvent(t[i]) } return v } // ToFailoverType converts thrift FailoverType type to internal func ToFailoverType(t *shared.FailoverType) *types.FailoverType { if t == nil { return nil } v := types.FailoverType(*t) return &v } // FromFailoverType converts internal FailoverType type to thrift func FromFailoverType(t *types.FailoverType) *shared.FailoverType { if t == nil { return nil } v := shared.FailoverType(*t) return &v } // ToClusterFailover converts thrift ClusterFailover type to internal func ToClusterFailover(t *shared.ClusterFailover) *types.ClusterFailover { if t == nil { return nil } return &types.ClusterFailover{ FromCluster: ToActiveClusterInfo(t.FromCluster), ToCluster: ToActiveClusterInfo(t.ToCluster), ClusterAttribute: ToClusterAttribute(t.ClusterAttribute), } } // FromClusterFailover converts internal ClusterFailover type to thrift func FromClusterFailover(t *types.ClusterFailover) *shared.ClusterFailover { if t == nil { return nil } return &shared.ClusterFailover{ FromCluster: FromActiveClusterInfo(t.FromCluster), ToCluster: FromActiveClusterInfo(t.ToCluster), ClusterAttribute: FromClusterAttribute(t.ClusterAttribute), } } // ToClusterFailoverArray converts thrift ClusterFailover array type to internal func ToClusterFailoverArray(t []*shared.ClusterFailover) []*types.ClusterFailover { if t == nil { return nil } v := make([]*types.ClusterFailover, len(t)) for i := range t { v[i] = ToClusterFailover(t[i]) } return v } // FromClusterFailoverArray converts internal ClusterFailover array type to thrift func FromClusterFailoverArray(t []*types.ClusterFailover) []*shared.ClusterFailover { if t == nil { return nil } v := make([]*shared.ClusterFailover, len(t)) for i := range t { v[i] = FromClusterFailover(t[i]) } return v } // ToActiveClusterInfo converts thrift ActiveClusterInfo type to internal func ToActiveClusterInfo(t *shared.ActiveClusterInfo) *types.ActiveClusterInfo { if t == nil { return nil } return &types.ActiveClusterInfo{ ActiveClusterName: t.GetActiveClusterName(), FailoverVersion: t.GetFailoverVersion(), } } // FromActiveClusterInfo converts internal ActiveClusterInfo type to thrift func FromActiveClusterInfo(t *types.ActiveClusterInfo) *shared.ActiveClusterInfo { if t == nil { return nil } return &shared.ActiveClusterInfo{ ActiveClusterName: &t.ActiveClusterName, FailoverVersion: &t.FailoverVersion, } } // FromListOpenWorkflowExecutionsRequest converts internal ListOpenWorkflowExecutionsRequest type to thrift func FromListOpenWorkflowExecutionsRequest(t *types.ListOpenWorkflowExecutionsRequest) *shared.ListOpenWorkflowExecutionsRequest { if t == nil { return nil } return &shared.ListOpenWorkflowExecutionsRequest{ Domain: &t.Domain, MaximumPageSize: &t.MaximumPageSize, NextPageToken: t.NextPageToken, StartTimeFilter: FromStartTimeFilter(t.StartTimeFilter), ExecutionFilter: FromWorkflowExecutionFilter(t.ExecutionFilter), TypeFilter: FromWorkflowTypeFilter(t.TypeFilter), } } // ToListOpenWorkflowExecutionsRequest converts thrift ListOpenWorkflowExecutionsRequest type to internal func ToListOpenWorkflowExecutionsRequest(t *shared.ListOpenWorkflowExecutionsRequest) *types.ListOpenWorkflowExecutionsRequest { if t == nil { return nil } return &types.ListOpenWorkflowExecutionsRequest{ Domain: t.GetDomain(), MaximumPageSize: t.GetMaximumPageSize(), NextPageToken: t.NextPageToken, StartTimeFilter: ToStartTimeFilter(t.StartTimeFilter), ExecutionFilter: ToWorkflowExecutionFilter(t.ExecutionFilter), TypeFilter: ToWorkflowTypeFilter(t.TypeFilter), } } // FromListOpenWorkflowExecutionsResponse converts internal ListOpenWorkflowExecutionsResponse type to thrift func FromListOpenWorkflowExecutionsResponse(t *types.ListOpenWorkflowExecutionsResponse) *shared.ListOpenWorkflowExecutionsResponse { if t == nil { return nil } return &shared.ListOpenWorkflowExecutionsResponse{ Executions: FromWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } // ToListOpenWorkflowExecutionsResponse converts thrift ListOpenWorkflowExecutionsResponse type to internal func ToListOpenWorkflowExecutionsResponse(t *shared.ListOpenWorkflowExecutionsResponse) *types.ListOpenWorkflowExecutionsResponse { if t == nil { return nil } return &types.ListOpenWorkflowExecutionsResponse{ Executions: ToWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } // FromListTaskListPartitionsRequest converts internal ListTaskListPartitionsRequest type to thrift func FromListTaskListPartitionsRequest(t *types.ListTaskListPartitionsRequest) *shared.ListTaskListPartitionsRequest { if t == nil { return nil } return &shared.ListTaskListPartitionsRequest{ Domain: &t.Domain, TaskList: FromTaskList(t.TaskList), } } // ToListTaskListPartitionsRequest converts thrift ListTaskListPartitionsRequest type to internal func ToListTaskListPartitionsRequest(t *shared.ListTaskListPartitionsRequest) *types.ListTaskListPartitionsRequest { if t == nil { return nil } return &types.ListTaskListPartitionsRequest{ Domain: t.GetDomain(), TaskList: ToTaskList(t.TaskList), } } // FromListTaskListPartitionsResponse converts internal ListTaskListPartitionsResponse type to thrift func FromListTaskListPartitionsResponse(t *types.ListTaskListPartitionsResponse) *shared.ListTaskListPartitionsResponse { if t == nil { return nil } return &shared.ListTaskListPartitionsResponse{ ActivityTaskListPartitions: FromTaskListPartitionMetadataArray(t.ActivityTaskListPartitions), DecisionTaskListPartitions: FromTaskListPartitionMetadataArray(t.DecisionTaskListPartitions), } } // ToListTaskListPartitionsResponse converts thrift ListTaskListPartitionsResponse type to internal func ToListTaskListPartitionsResponse(t *shared.ListTaskListPartitionsResponse) *types.ListTaskListPartitionsResponse { if t == nil { return nil } return &types.ListTaskListPartitionsResponse{ ActivityTaskListPartitions: ToTaskListPartitionMetadataArray(t.ActivityTaskListPartitions), DecisionTaskListPartitions: ToTaskListPartitionMetadataArray(t.DecisionTaskListPartitions), } } // FromGetTaskListsByDomainRequest converts internal GetTaskListsByDomainRequest type to thrift func FromGetTaskListsByDomainRequest(t *types.GetTaskListsByDomainRequest) *shared.GetTaskListsByDomainRequest { if t == nil { return nil } return &shared.GetTaskListsByDomainRequest{ DomainName: &t.Domain, } } // ToGetTaskListsByDomainRequest converts thrift GetTaskListsByDomainRequest type to internal func ToGetTaskListsByDomainRequest(t *shared.GetTaskListsByDomainRequest) *types.GetTaskListsByDomainRequest { if t == nil { return nil } return &types.GetTaskListsByDomainRequest{ Domain: t.GetDomainName(), } } // FromGetTaskListsByDomainResponse converts internal GetTaskListsByDomainResponse type to thrift func FromGetTaskListsByDomainResponse(t *types.GetTaskListsByDomainResponse) *shared.GetTaskListsByDomainResponse { if t == nil { return nil } return &shared.GetTaskListsByDomainResponse{ DecisionTaskListMap: FromDescribeTaskListResponseMap(t.GetDecisionTaskListMap()), ActivityTaskListMap: FromDescribeTaskListResponseMap(t.GetActivityTaskListMap()), } } // ToGetTaskListsByDomainResponse converts thrift GetTaskListsByDomainResponse type to internal func ToGetTaskListsByDomainResponse(t *shared.GetTaskListsByDomainResponse) *types.GetTaskListsByDomainResponse { if t == nil { return nil } return &types.GetTaskListsByDomainResponse{ DecisionTaskListMap: ToDescribeTaskListResponseMap(t.GetDecisionTaskListMap()), ActivityTaskListMap: ToDescribeTaskListResponseMap(t.GetActivityTaskListMap()), } } // FromDescribeTaskListResponseMap converts internal DescribeTaskListResponse map type to thrift func FromDescribeTaskListResponseMap(t map[string]*types.DescribeTaskListResponse) map[string]*shared.DescribeTaskListResponse { if t == nil { return nil } taskListMap := make(map[string]*shared.DescribeTaskListResponse, len(t)) for key, value := range t { taskListMap[key] = FromDescribeTaskListResponse(value) } return taskListMap } // ToDescribeTaskListResponseMap converts thrift DescribeTaskListResponse map type to internal func ToDescribeTaskListResponseMap(t map[string]*shared.DescribeTaskListResponse) map[string]*types.DescribeTaskListResponse { if t == nil { return nil } taskListMap := make(map[string]*types.DescribeTaskListResponse, len(t)) for key, value := range t { taskListMap[key] = ToDescribeTaskListResponse(value) } return taskListMap } // FromListWorkflowExecutionsRequest converts internal ListWorkflowExecutionsRequest type to thrift func FromListWorkflowExecutionsRequest(t *types.ListWorkflowExecutionsRequest) *shared.ListWorkflowExecutionsRequest { if t == nil { return nil } return &shared.ListWorkflowExecutionsRequest{ Domain: &t.Domain, PageSize: &t.PageSize, NextPageToken: t.NextPageToken, Query: &t.Query, } } // ToListWorkflowExecutionsRequest converts thrift ListWorkflowExecutionsRequest type to internal func ToListWorkflowExecutionsRequest(t *shared.ListWorkflowExecutionsRequest) *types.ListWorkflowExecutionsRequest { if t == nil { return nil } return &types.ListWorkflowExecutionsRequest{ Domain: t.GetDomain(), PageSize: t.GetPageSize(), NextPageToken: t.NextPageToken, Query: t.GetQuery(), } } // FromListWorkflowExecutionsResponse converts internal ListWorkflowExecutionsResponse type to thrift func FromListWorkflowExecutionsResponse(t *types.ListWorkflowExecutionsResponse) *shared.ListWorkflowExecutionsResponse { if t == nil { return nil } return &shared.ListWorkflowExecutionsResponse{ Executions: FromWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } // ToListWorkflowExecutionsResponse converts thrift ListWorkflowExecutionsResponse type to internal func ToListWorkflowExecutionsResponse(t *shared.ListWorkflowExecutionsResponse) *types.ListWorkflowExecutionsResponse { if t == nil { return nil } return &types.ListWorkflowExecutionsResponse{ Executions: ToWorkflowExecutionInfoArray(t.Executions), NextPageToken: t.NextPageToken, } } // FromMarkerRecordedEventAttributes converts internal MarkerRecordedEventAttributes type to thrift func FromMarkerRecordedEventAttributes(t *types.MarkerRecordedEventAttributes) *shared.MarkerRecordedEventAttributes { if t == nil { return nil } return &shared.MarkerRecordedEventAttributes{ MarkerName: &t.MarkerName, Details: t.Details, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, Header: FromHeader(t.Header), } } // ToMarkerRecordedEventAttributes converts thrift MarkerRecordedEventAttributes type to internal func ToMarkerRecordedEventAttributes(t *shared.MarkerRecordedEventAttributes) *types.MarkerRecordedEventAttributes { if t == nil { return nil } return &types.MarkerRecordedEventAttributes{ MarkerName: t.GetMarkerName(), Details: t.Details, DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), Header: ToHeader(t.Header), } } // FromMemo converts internal Memo type to thrift func FromMemo(t *types.Memo) *shared.Memo { if t == nil { return nil } return &shared.Memo{ Fields: t.Fields, } } // ToMemo converts thrift Memo type to internal func ToMemo(t *shared.Memo) *types.Memo { if t == nil { return nil } return &types.Memo{ Fields: t.Fields, } } // FromParentClosePolicy converts internal ParentClosePolicy type to thrift func FromParentClosePolicy(t *types.ParentClosePolicy) *shared.ParentClosePolicy { if t == nil { return nil } switch *t { case types.ParentClosePolicyAbandon: v := shared.ParentClosePolicyAbandon return &v case types.ParentClosePolicyRequestCancel: v := shared.ParentClosePolicyRequestCancel return &v case types.ParentClosePolicyTerminate: v := shared.ParentClosePolicyTerminate return &v } panic("unexpected enum value") } // ToParentClosePolicy converts thrift ParentClosePolicy type to internal func ToParentClosePolicy(t *shared.ParentClosePolicy) *types.ParentClosePolicy { if t == nil { return nil } switch *t { case shared.ParentClosePolicyAbandon: v := types.ParentClosePolicyAbandon return &v case shared.ParentClosePolicyRequestCancel: v := types.ParentClosePolicyRequestCancel return &v case shared.ParentClosePolicyTerminate: v := types.ParentClosePolicyTerminate return &v } panic("unexpected enum value") } // FromPendingActivityInfo converts internal PendingActivityInfo type to thrift func FromPendingActivityInfo(t *types.PendingActivityInfo) *shared.PendingActivityInfo { if t == nil { return nil } return &shared.PendingActivityInfo{ ActivityID: &t.ActivityID, ActivityType: FromActivityType(t.ActivityType), State: FromPendingActivityState(t.State), HeartbeatDetails: t.HeartbeatDetails, LastHeartbeatTimestamp: t.LastHeartbeatTimestamp, LastStartedTimestamp: t.LastStartedTimestamp, Attempt: &t.Attempt, MaximumAttempts: &t.MaximumAttempts, ScheduledTimestamp: t.ScheduledTimestamp, ExpirationTimestamp: t.ExpirationTimestamp, LastFailureReason: t.LastFailureReason, LastWorkerIdentity: &t.LastWorkerIdentity, LastFailureDetails: t.LastFailureDetails, StartedWorkerIdentity: &t.StartedWorkerIdentity, ScheduleID: &t.ScheduleID, } } // ToPendingActivityInfo converts thrift PendingActivityInfo type to internal func ToPendingActivityInfo(t *shared.PendingActivityInfo) *types.PendingActivityInfo { if t == nil { return nil } return &types.PendingActivityInfo{ ActivityID: t.GetActivityID(), ActivityType: ToActivityType(t.ActivityType), State: ToPendingActivityState(t.State), HeartbeatDetails: t.HeartbeatDetails, LastHeartbeatTimestamp: t.LastHeartbeatTimestamp, LastStartedTimestamp: t.LastStartedTimestamp, Attempt: t.GetAttempt(), MaximumAttempts: t.GetMaximumAttempts(), ScheduledTimestamp: t.ScheduledTimestamp, ExpirationTimestamp: t.ExpirationTimestamp, LastFailureReason: t.LastFailureReason, LastWorkerIdentity: t.GetLastWorkerIdentity(), LastFailureDetails: t.LastFailureDetails, StartedWorkerIdentity: t.GetStartedWorkerIdentity(), ScheduleID: t.GetScheduleID(), } } // FromPendingActivityState converts internal PendingActivityState type to thrift func FromPendingActivityState(t *types.PendingActivityState) *shared.PendingActivityState { if t == nil { return nil } switch *t { case types.PendingActivityStateScheduled: v := shared.PendingActivityStateScheduled return &v case types.PendingActivityStateStarted: v := shared.PendingActivityStateStarted return &v case types.PendingActivityStateCancelRequested: v := shared.PendingActivityStateCancelRequested return &v } panic("unexpected enum value") } // ToPendingActivityState converts thrift PendingActivityState type to internal func ToPendingActivityState(t *shared.PendingActivityState) *types.PendingActivityState { if t == nil { return nil } switch *t { case shared.PendingActivityStateScheduled: v := types.PendingActivityStateScheduled return &v case shared.PendingActivityStateStarted: v := types.PendingActivityStateStarted return &v case shared.PendingActivityStateCancelRequested: v := types.PendingActivityStateCancelRequested return &v } panic("unexpected enum value") } // FromPendingChildExecutionInfo converts internal PendingChildExecutionInfo type to thrift func FromPendingChildExecutionInfo(t *types.PendingChildExecutionInfo) *shared.PendingChildExecutionInfo { if t == nil { return nil } return &shared.PendingChildExecutionInfo{ Domain: &t.Domain, WorkflowID: &t.WorkflowID, RunID: &t.RunID, WorkflowTypName: &t.WorkflowTypeName, InitiatedID: &t.InitiatedID, ParentClosePolicy: FromParentClosePolicy(t.ParentClosePolicy), } } // ToPendingChildExecutionInfo converts thrift PendingChildExecutionInfo type to internal func ToPendingChildExecutionInfo(t *shared.PendingChildExecutionInfo) *types.PendingChildExecutionInfo { if t == nil { return nil } return &types.PendingChildExecutionInfo{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowID(), RunID: t.GetRunID(), WorkflowTypeName: t.GetWorkflowTypName(), InitiatedID: t.GetInitiatedID(), ParentClosePolicy: ToParentClosePolicy(t.ParentClosePolicy), } } // FromPendingDecisionInfo converts internal PendingDecisionInfo type to thrift func FromPendingDecisionInfo(t *types.PendingDecisionInfo) *shared.PendingDecisionInfo { if t == nil { return nil } return &shared.PendingDecisionInfo{ State: FromPendingDecisionState(t.State), ScheduledTimestamp: t.ScheduledTimestamp, StartedTimestamp: t.StartedTimestamp, Attempt: &t.Attempt, OriginalScheduledTimestamp: t.OriginalScheduledTimestamp, ScheduleID: &t.ScheduleID, } } // ToPendingDecisionInfo converts thrift PendingDecisionInfo type to internal func ToPendingDecisionInfo(t *shared.PendingDecisionInfo) *types.PendingDecisionInfo { if t == nil { return nil } return &types.PendingDecisionInfo{ State: ToPendingDecisionState(t.State), ScheduledTimestamp: t.ScheduledTimestamp, StartedTimestamp: t.StartedTimestamp, Attempt: t.GetAttempt(), OriginalScheduledTimestamp: t.OriginalScheduledTimestamp, ScheduleID: t.GetScheduleID(), } } // FromPendingDecisionState converts internal PendingDecisionState type to thrift func FromPendingDecisionState(t *types.PendingDecisionState) *shared.PendingDecisionState { if t == nil { return nil } switch *t { case types.PendingDecisionStateScheduled: v := shared.PendingDecisionStateScheduled return &v case types.PendingDecisionStateStarted: v := shared.PendingDecisionStateStarted return &v } panic("unexpected enum value") } // ToPendingDecisionState converts thrift PendingDecisionState type to internal func ToPendingDecisionState(t *shared.PendingDecisionState) *types.PendingDecisionState { if t == nil { return nil } switch *t { case shared.PendingDecisionStateScheduled: v := types.PendingDecisionStateScheduled return &v case shared.PendingDecisionStateStarted: v := types.PendingDecisionStateStarted return &v } panic("unexpected enum value") } // FromPollForActivityTaskRequest converts internal PollForActivityTaskRequest type to thrift func FromPollForActivityTaskRequest(t *types.PollForActivityTaskRequest) *shared.PollForActivityTaskRequest { if t == nil { return nil } return &shared.PollForActivityTaskRequest{ Domain: &t.Domain, TaskList: FromTaskList(t.TaskList), Identity: &t.Identity, TaskListMetadata: FromTaskListMetadata(t.TaskListMetadata), } } // ToPollForActivityTaskRequest converts thrift PollForActivityTaskRequest type to internal func ToPollForActivityTaskRequest(t *shared.PollForActivityTaskRequest) *types.PollForActivityTaskRequest { if t == nil { return nil } return &types.PollForActivityTaskRequest{ Domain: t.GetDomain(), TaskList: ToTaskList(t.TaskList), Identity: t.GetIdentity(), TaskListMetadata: ToTaskListMetadata(t.TaskListMetadata), } } // FromPollForActivityTaskResponse converts internal PollForActivityTaskResponse type to thrift func FromPollForActivityTaskResponse(t *types.PollForActivityTaskResponse) *shared.PollForActivityTaskResponse { if t == nil { return nil } return &shared.PollForActivityTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), ActivityId: &t.ActivityID, ActivityType: FromActivityType(t.ActivityType), Input: t.Input, ScheduledTimestamp: t.ScheduledTimestamp, ScheduleToCloseTimeoutSeconds: t.ScheduleToCloseTimeoutSeconds, StartedTimestamp: t.StartedTimestamp, StartToCloseTimeoutSeconds: t.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: t.HeartbeatTimeoutSeconds, Attempt: &t.Attempt, ScheduledTimestampOfThisAttempt: t.ScheduledTimestampOfThisAttempt, HeartbeatDetails: t.HeartbeatDetails, WorkflowType: FromWorkflowType(t.WorkflowType), WorkflowDomain: &t.WorkflowDomain, Header: FromHeader(t.Header), AutoConfigHint: FromAutoConfigHint(t.AutoConfigHint), } } // ToPollForActivityTaskResponse converts thrift PollForActivityTaskResponse type to internal func ToPollForActivityTaskResponse(t *shared.PollForActivityTaskResponse) *types.PollForActivityTaskResponse { if t == nil { return nil } return &types.PollForActivityTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), ActivityID: t.GetActivityId(), ActivityType: ToActivityType(t.ActivityType), Input: t.Input, ScheduledTimestamp: t.ScheduledTimestamp, ScheduleToCloseTimeoutSeconds: t.ScheduleToCloseTimeoutSeconds, StartedTimestamp: t.StartedTimestamp, StartToCloseTimeoutSeconds: t.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: t.HeartbeatTimeoutSeconds, Attempt: t.GetAttempt(), ScheduledTimestampOfThisAttempt: t.ScheduledTimestampOfThisAttempt, HeartbeatDetails: t.HeartbeatDetails, WorkflowType: ToWorkflowType(t.WorkflowType), WorkflowDomain: t.GetWorkflowDomain(), Header: ToHeader(t.Header), AutoConfigHint: ToAutoConfigHint(t.AutoConfigHint), } } // FromPollForDecisionTaskRequest converts internal PollForDecisionTaskRequest type to thrift func FromPollForDecisionTaskRequest(t *types.PollForDecisionTaskRequest) *shared.PollForDecisionTaskRequest { if t == nil { return nil } return &shared.PollForDecisionTaskRequest{ Domain: &t.Domain, TaskList: FromTaskList(t.TaskList), Identity: &t.Identity, BinaryChecksum: &t.BinaryChecksum, } } // ToPollForDecisionTaskRequest converts thrift PollForDecisionTaskRequest type to internal func ToPollForDecisionTaskRequest(t *shared.PollForDecisionTaskRequest) *types.PollForDecisionTaskRequest { if t == nil { return nil } return &types.PollForDecisionTaskRequest{ Domain: t.GetDomain(), TaskList: ToTaskList(t.TaskList), Identity: t.GetIdentity(), BinaryChecksum: t.GetBinaryChecksum(), } } // FromPollForDecisionTaskResponse converts internal PollForDecisionTaskResponse type to thrift func FromPollForDecisionTaskResponse(t *types.PollForDecisionTaskResponse) *shared.PollForDecisionTaskResponse { if t == nil { return nil } return &shared.PollForDecisionTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), WorkflowType: FromWorkflowType(t.WorkflowType), PreviousStartedEventId: t.PreviousStartedEventID, StartedEventId: &t.StartedEventID, Attempt: &t.Attempt, BacklogCountHint: &t.BacklogCountHint, History: FromHistory(t.History), NextPageToken: t.NextPageToken, Query: FromWorkflowQuery(t.Query), WorkflowExecutionTaskList: FromTaskList(t.WorkflowExecutionTaskList), ScheduledTimestamp: t.ScheduledTimestamp, StartedTimestamp: t.StartedTimestamp, Queries: FromWorkflowQueryMap(t.Queries), NextEventId: &t.NextEventID, TotalHistoryBytes: &t.TotalHistoryBytes, AutoConfigHint: FromAutoConfigHint(t.AutoConfigHint), } } // ToPollForDecisionTaskResponse converts thrift PollForDecisionTaskResponse type to internal func ToPollForDecisionTaskResponse(t *shared.PollForDecisionTaskResponse) *types.PollForDecisionTaskResponse { if t == nil { return nil } return &types.PollForDecisionTaskResponse{ TaskToken: t.TaskToken, WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), WorkflowType: ToWorkflowType(t.WorkflowType), PreviousStartedEventID: t.PreviousStartedEventId, StartedEventID: t.GetStartedEventId(), Attempt: t.GetAttempt(), BacklogCountHint: t.GetBacklogCountHint(), History: ToHistory(t.History), NextPageToken: t.NextPageToken, Query: ToWorkflowQuery(t.Query), WorkflowExecutionTaskList: ToTaskList(t.WorkflowExecutionTaskList), ScheduledTimestamp: t.ScheduledTimestamp, StartedTimestamp: t.StartedTimestamp, Queries: ToWorkflowQueryMap(t.Queries), NextEventID: t.GetNextEventId(), TotalHistoryBytes: t.GetTotalHistoryBytes(), AutoConfigHint: ToAutoConfigHint(t.AutoConfigHint), } } // FromPollerInfo converts internal PollerInfo type to thrift func FromPollerInfo(t *types.PollerInfo) *shared.PollerInfo { if t == nil { return nil } return &shared.PollerInfo{ LastAccessTime: t.LastAccessTime, Identity: &t.Identity, RatePerSecond: &t.RatePerSecond, } } // ToPollerInfo converts thrift PollerInfo type to internal func ToPollerInfo(t *shared.PollerInfo) *types.PollerInfo { if t == nil { return nil } return &types.PollerInfo{ LastAccessTime: t.LastAccessTime, Identity: t.GetIdentity(), RatePerSecond: t.GetRatePerSecond(), } } // FromQueryConsistencyLevel converts internal QueryConsistencyLevel type to thrift func FromQueryConsistencyLevel(t *types.QueryConsistencyLevel) *shared.QueryConsistencyLevel { if t == nil { return nil } switch *t { case types.QueryConsistencyLevelEventual: v := shared.QueryConsistencyLevelEventual return &v case types.QueryConsistencyLevelStrong: v := shared.QueryConsistencyLevelStrong return &v } panic("unexpected enum value") } // ToQueryConsistencyLevel converts thrift QueryConsistencyLevel type to internal func ToQueryConsistencyLevel(t *shared.QueryConsistencyLevel) *types.QueryConsistencyLevel { if t == nil { return nil } switch *t { case shared.QueryConsistencyLevelEventual: v := types.QueryConsistencyLevelEventual return &v case shared.QueryConsistencyLevelStrong: v := types.QueryConsistencyLevelStrong return &v } panic("unexpected enum value") } // FromQueryFailedError converts internal QueryFailedError type to thrift func FromQueryFailedError(t *types.QueryFailedError) *shared.QueryFailedError { if t == nil { return nil } return &shared.QueryFailedError{ Message: t.Message, } } // ToQueryFailedError converts thrift QueryFailedError type to internal func ToQueryFailedError(t *shared.QueryFailedError) *types.QueryFailedError { if t == nil { return nil } return &types.QueryFailedError{ Message: t.Message, } } // FromQueryRejectCondition converts internal QueryRejectCondition type to thrift func FromQueryRejectCondition(t *types.QueryRejectCondition) *shared.QueryRejectCondition { if t == nil { return nil } switch *t { case types.QueryRejectConditionNotOpen: v := shared.QueryRejectConditionNotOpen return &v case types.QueryRejectConditionNotCompletedCleanly: v := shared.QueryRejectConditionNotCompletedCleanly return &v } panic("unexpected enum value") } // ToQueryRejectCondition converts thrift QueryRejectCondition type to internal func ToQueryRejectCondition(t *shared.QueryRejectCondition) *types.QueryRejectCondition { if t == nil { return nil } switch *t { case shared.QueryRejectConditionNotOpen: v := types.QueryRejectConditionNotOpen return &v case shared.QueryRejectConditionNotCompletedCleanly: v := types.QueryRejectConditionNotCompletedCleanly return &v } panic("unexpected enum value") } // FromQueryRejected converts internal QueryRejected type to thrift func FromQueryRejected(t *types.QueryRejected) *shared.QueryRejected { if t == nil { return nil } return &shared.QueryRejected{ CloseStatus: FromWorkflowExecutionCloseStatus(t.CloseStatus), } } // ToQueryRejected converts thrift QueryRejected type to internal func ToQueryRejected(t *shared.QueryRejected) *types.QueryRejected { if t == nil { return nil } return &types.QueryRejected{ CloseStatus: ToWorkflowExecutionCloseStatus(t.CloseStatus), } } // FromQueryResultType converts internal QueryResultType type to thrift func FromQueryResultType(t *types.QueryResultType) *shared.QueryResultType { if t == nil { return nil } switch *t { case types.QueryResultTypeAnswered: v := shared.QueryResultTypeAnswered return &v case types.QueryResultTypeFailed: v := shared.QueryResultTypeFailed return &v } panic("unexpected enum value") } // ToQueryResultType converts thrift QueryResultType type to internal func ToQueryResultType(t *shared.QueryResultType) *types.QueryResultType { if t == nil { return nil } switch *t { case shared.QueryResultTypeAnswered: v := types.QueryResultTypeAnswered return &v case shared.QueryResultTypeFailed: v := types.QueryResultTypeFailed return &v } panic("unexpected enum value") } // FromQueryTaskCompletedType converts internal QueryTaskCompletedType type to thrift func FromQueryTaskCompletedType(t *types.QueryTaskCompletedType) *shared.QueryTaskCompletedType { if t == nil { return nil } switch *t { case types.QueryTaskCompletedTypeCompleted: v := shared.QueryTaskCompletedTypeCompleted return &v case types.QueryTaskCompletedTypeFailed: v := shared.QueryTaskCompletedTypeFailed return &v } panic("unexpected enum value") } // ToQueryTaskCompletedType converts thrift QueryTaskCompletedType type to internal func ToQueryTaskCompletedType(t *shared.QueryTaskCompletedType) *types.QueryTaskCompletedType { if t == nil { return nil } switch *t { case shared.QueryTaskCompletedTypeCompleted: v := types.QueryTaskCompletedTypeCompleted return &v case shared.QueryTaskCompletedTypeFailed: v := types.QueryTaskCompletedTypeFailed return &v } panic("unexpected enum value") } // FromQueryWorkflowRequest converts internal QueryWorkflowRequest type to thrift func FromQueryWorkflowRequest(t *types.QueryWorkflowRequest) *shared.QueryWorkflowRequest { if t == nil { return nil } return &shared.QueryWorkflowRequest{ Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), Query: FromWorkflowQuery(t.Query), QueryRejectCondition: FromQueryRejectCondition(t.QueryRejectCondition), QueryConsistencyLevel: FromQueryConsistencyLevel(t.QueryConsistencyLevel), } } // ToQueryWorkflowRequest converts thrift QueryWorkflowRequest type to internal func ToQueryWorkflowRequest(t *shared.QueryWorkflowRequest) *types.QueryWorkflowRequest { if t == nil { return nil } return &types.QueryWorkflowRequest{ Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), Query: ToWorkflowQuery(t.Query), QueryRejectCondition: ToQueryRejectCondition(t.QueryRejectCondition), QueryConsistencyLevel: ToQueryConsistencyLevel(t.QueryConsistencyLevel), } } // FromQueryWorkflowResponse converts internal QueryWorkflowResponse type to thrift func FromQueryWorkflowResponse(t *types.QueryWorkflowResponse) *shared.QueryWorkflowResponse { if t == nil { return nil } return &shared.QueryWorkflowResponse{ QueryResult: t.QueryResult, QueryRejected: FromQueryRejected(t.QueryRejected), } } // ToQueryWorkflowResponse converts thrift QueryWorkflowResponse type to internal func ToQueryWorkflowResponse(t *shared.QueryWorkflowResponse) *types.QueryWorkflowResponse { if t == nil { return nil } return &types.QueryWorkflowResponse{ QueryResult: t.QueryResult, QueryRejected: ToQueryRejected(t.QueryRejected), } } // FromAdminReapplyEventsRequest converts internal ReapplyEventsRequest type to thrift func FromAdminReapplyEventsRequest(t *types.ReapplyEventsRequest) *shared.ReapplyEventsRequest { if t == nil { return nil } return &shared.ReapplyEventsRequest{ DomainName: &t.DomainName, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Events: FromDataBlob(t.Events), } } // ToAdminReapplyEventsRequest converts thrift ReapplyEventsRequest type to internal func ToAdminReapplyEventsRequest(t *shared.ReapplyEventsRequest) *types.ReapplyEventsRequest { if t == nil { return nil } return &types.ReapplyEventsRequest{ DomainName: t.GetDomainName(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Events: ToDataBlob(t.Events), } } // FromRecordActivityTaskHeartbeatByIDRequest converts internal RecordActivityTaskHeartbeatByIDRequest type to thrift func FromRecordActivityTaskHeartbeatByIDRequest(t *types.RecordActivityTaskHeartbeatByIDRequest) *shared.RecordActivityTaskHeartbeatByIDRequest { if t == nil { return nil } return &shared.RecordActivityTaskHeartbeatByIDRequest{ Domain: &t.Domain, WorkflowID: &t.WorkflowID, RunID: &t.RunID, ActivityID: &t.ActivityID, Details: t.Details, Identity: &t.Identity, } } // ToRecordActivityTaskHeartbeatByIDRequest converts thrift RecordActivityTaskHeartbeatByIDRequest type to internal func ToRecordActivityTaskHeartbeatByIDRequest(t *shared.RecordActivityTaskHeartbeatByIDRequest) *types.RecordActivityTaskHeartbeatByIDRequest { if t == nil { return nil } return &types.RecordActivityTaskHeartbeatByIDRequest{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowID(), RunID: t.GetRunID(), ActivityID: t.GetActivityID(), Details: t.Details, Identity: t.GetIdentity(), } } // FromRecordActivityTaskHeartbeatRequest converts internal RecordActivityTaskHeartbeatRequest type to thrift func FromRecordActivityTaskHeartbeatRequest(t *types.RecordActivityTaskHeartbeatRequest) *shared.RecordActivityTaskHeartbeatRequest { if t == nil { return nil } return &shared.RecordActivityTaskHeartbeatRequest{ TaskToken: t.TaskToken, Details: t.Details, Identity: &t.Identity, } } // ToRecordActivityTaskHeartbeatRequest converts thrift RecordActivityTaskHeartbeatRequest type to internal func ToRecordActivityTaskHeartbeatRequest(t *shared.RecordActivityTaskHeartbeatRequest) *types.RecordActivityTaskHeartbeatRequest { if t == nil { return nil } return &types.RecordActivityTaskHeartbeatRequest{ TaskToken: t.TaskToken, Details: t.Details, Identity: t.GetIdentity(), } } // FromRecordActivityTaskHeartbeatResponse converts internal RecordActivityTaskHeartbeatResponse type to thrift func FromRecordActivityTaskHeartbeatResponse(t *types.RecordActivityTaskHeartbeatResponse) *shared.RecordActivityTaskHeartbeatResponse { if t == nil { return nil } return &shared.RecordActivityTaskHeartbeatResponse{ CancelRequested: &t.CancelRequested, } } // ToRecordActivityTaskHeartbeatResponse converts thrift RecordActivityTaskHeartbeatResponse type to internal func ToRecordActivityTaskHeartbeatResponse(t *shared.RecordActivityTaskHeartbeatResponse) *types.RecordActivityTaskHeartbeatResponse { if t == nil { return nil } return &types.RecordActivityTaskHeartbeatResponse{ CancelRequested: t.GetCancelRequested(), } } // FromRecordMarkerDecisionAttributes converts internal RecordMarkerDecisionAttributes type to thrift func FromRecordMarkerDecisionAttributes(t *types.RecordMarkerDecisionAttributes) *shared.RecordMarkerDecisionAttributes { if t == nil { return nil } return &shared.RecordMarkerDecisionAttributes{ MarkerName: &t.MarkerName, Details: t.Details, Header: FromHeader(t.Header), } } // ToRecordMarkerDecisionAttributes converts thrift RecordMarkerDecisionAttributes type to internal func ToRecordMarkerDecisionAttributes(t *shared.RecordMarkerDecisionAttributes) *types.RecordMarkerDecisionAttributes { if t == nil { return nil } return &types.RecordMarkerDecisionAttributes{ MarkerName: t.GetMarkerName(), Details: t.Details, Header: ToHeader(t.Header), } } // FromRefreshWorkflowTasksRequest converts internal RefreshWorkflowTasksRequest type to thrift func FromRefreshWorkflowTasksRequest(t *types.RefreshWorkflowTasksRequest) *shared.RefreshWorkflowTasksRequest { if t == nil { return nil } return &shared.RefreshWorkflowTasksRequest{ Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), } } // ToRefreshWorkflowTasksRequest converts thrift RefreshWorkflowTasksRequest type to internal func ToRefreshWorkflowTasksRequest(t *shared.RefreshWorkflowTasksRequest) *types.RefreshWorkflowTasksRequest { if t == nil { return nil } return &types.RefreshWorkflowTasksRequest{ Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), } } // FromRegisterDomainRequest converts internal RegisterDomainRequest type to thrift func FromRegisterDomainRequest(t *types.RegisterDomainRequest) *shared.RegisterDomainRequest { if t == nil { return nil } return &shared.RegisterDomainRequest{ Name: &t.Name, Description: &t.Description, OwnerEmail: &t.OwnerEmail, WorkflowExecutionRetentionPeriodInDays: &t.WorkflowExecutionRetentionPeriodInDays, EmitMetric: t.EmitMetric, Clusters: FromClusterReplicationConfigurationArray(t.Clusters), ActiveClusterName: &t.ActiveClusterName, ActiveClusters: FromActiveClusters(t.ActiveClusters), Data: t.Data, SecurityToken: &t.SecurityToken, IsGlobalDomain: &t.IsGlobalDomain, HistoryArchivalStatus: FromArchivalStatus(t.HistoryArchivalStatus), HistoryArchivalURI: &t.HistoryArchivalURI, VisibilityArchivalStatus: FromArchivalStatus(t.VisibilityArchivalStatus), VisibilityArchivalURI: &t.VisibilityArchivalURI, } } // ToRegisterDomainRequest converts thrift RegisterDomainRequest type to internal func ToRegisterDomainRequest(t *shared.RegisterDomainRequest) *types.RegisterDomainRequest { if t == nil { return nil } return &types.RegisterDomainRequest{ Name: t.GetName(), Description: t.GetDescription(), OwnerEmail: t.GetOwnerEmail(), WorkflowExecutionRetentionPeriodInDays: t.GetWorkflowExecutionRetentionPeriodInDays(), EmitMetric: t.EmitMetric, Clusters: ToClusterReplicationConfigurationArray(t.Clusters), ActiveClusterName: t.GetActiveClusterName(), ActiveClusters: ToActiveClusters(t.ActiveClusters), Data: t.Data, SecurityToken: t.GetSecurityToken(), IsGlobalDomain: t.GetIsGlobalDomain(), HistoryArchivalStatus: ToArchivalStatus(t.HistoryArchivalStatus), HistoryArchivalURI: t.GetHistoryArchivalURI(), VisibilityArchivalStatus: ToArchivalStatus(t.VisibilityArchivalStatus), VisibilityArchivalURI: t.GetVisibilityArchivalURI(), } } // FromRemoteSyncMatchedError converts internal RemoteSyncMatchedError type to thrift func FromRemoteSyncMatchedError(t *types.RemoteSyncMatchedError) *shared.RemoteSyncMatchedError { if t == nil { return nil } return &shared.RemoteSyncMatchedError{ Message: t.Message, } } // ToRemoteSyncMatchedError converts thrift RemoteSyncMatchedError type to internal func ToRemoteSyncMatchedError(t *shared.RemoteSyncMatchedError) *types.RemoteSyncMatchedError { if t == nil { return nil } return &types.RemoteSyncMatchedError{ Message: t.Message, } } // FromAdminRemoveTaskRequest converts internal RemoveTaskRequest type to thrift func FromAdminRemoveTaskRequest(t *types.RemoveTaskRequest) *shared.RemoveTaskRequest { if t == nil { return nil } return &shared.RemoveTaskRequest{ ShardID: &t.ShardID, Type: t.Type, TaskID: &t.TaskID, VisibilityTimestamp: t.VisibilityTimestamp, ClusterName: &t.ClusterName, } } // ToAdminRemoveTaskRequest converts thrift RemoveTaskRequest type to internal func ToAdminRemoveTaskRequest(t *shared.RemoveTaskRequest) *types.RemoveTaskRequest { if t == nil { return nil } return &types.RemoveTaskRequest{ ShardID: t.GetShardID(), Type: t.Type, TaskID: t.GetTaskID(), VisibilityTimestamp: t.VisibilityTimestamp, ClusterName: t.GetClusterName(), } } // FromRequestCancelActivityTaskDecisionAttributes converts internal RequestCancelActivityTaskDecisionAttributes type to thrift func FromRequestCancelActivityTaskDecisionAttributes(t *types.RequestCancelActivityTaskDecisionAttributes) *shared.RequestCancelActivityTaskDecisionAttributes { if t == nil { return nil } return &shared.RequestCancelActivityTaskDecisionAttributes{ ActivityId: &t.ActivityID, } } // ToRequestCancelActivityTaskDecisionAttributes converts thrift RequestCancelActivityTaskDecisionAttributes type to internal func ToRequestCancelActivityTaskDecisionAttributes(t *shared.RequestCancelActivityTaskDecisionAttributes) *types.RequestCancelActivityTaskDecisionAttributes { if t == nil { return nil } return &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: t.GetActivityId(), } } // FromRequestCancelActivityTaskFailedEventAttributes converts internal RequestCancelActivityTaskFailedEventAttributes type to thrift func FromRequestCancelActivityTaskFailedEventAttributes(t *types.RequestCancelActivityTaskFailedEventAttributes) *shared.RequestCancelActivityTaskFailedEventAttributes { if t == nil { return nil } return &shared.RequestCancelActivityTaskFailedEventAttributes{ ActivityId: &t.ActivityID, Cause: &t.Cause, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, } } // ToRequestCancelActivityTaskFailedEventAttributes converts thrift RequestCancelActivityTaskFailedEventAttributes type to internal func ToRequestCancelActivityTaskFailedEventAttributes(t *shared.RequestCancelActivityTaskFailedEventAttributes) *types.RequestCancelActivityTaskFailedEventAttributes { if t == nil { return nil } return &types.RequestCancelActivityTaskFailedEventAttributes{ ActivityID: t.GetActivityId(), Cause: t.GetCause(), DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), } } // FromRequestCancelExternalWorkflowExecutionDecisionAttributes converts internal RequestCancelExternalWorkflowExecutionDecisionAttributes type to thrift func FromRequestCancelExternalWorkflowExecutionDecisionAttributes(t *types.RequestCancelExternalWorkflowExecutionDecisionAttributes) *shared.RequestCancelExternalWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &shared.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: &t.Domain, WorkflowId: &t.WorkflowID, RunId: &t.RunID, Control: t.Control, ChildWorkflowOnly: &t.ChildWorkflowOnly, } } // ToRequestCancelExternalWorkflowExecutionDecisionAttributes converts thrift RequestCancelExternalWorkflowExecutionDecisionAttributes type to internal func ToRequestCancelExternalWorkflowExecutionDecisionAttributes(t *shared.RequestCancelExternalWorkflowExecutionDecisionAttributes) *types.RequestCancelExternalWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowId(), RunID: t.GetRunId(), Control: t.Control, ChildWorkflowOnly: t.GetChildWorkflowOnly(), } } // FromRequestCancelExternalWorkflowExecutionFailedEventAttributes converts internal RequestCancelExternalWorkflowExecutionFailedEventAttributes type to thrift func FromRequestCancelExternalWorkflowExecutionFailedEventAttributes(t *types.RequestCancelExternalWorkflowExecutionFailedEventAttributes) *shared.RequestCancelExternalWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &shared.RequestCancelExternalWorkflowExecutionFailedEventAttributes{ Cause: FromCancelExternalWorkflowExecutionFailedCause(t.Cause), DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), InitiatedEventId: &t.InitiatedEventID, Control: t.Control, } } // ToRequestCancelExternalWorkflowExecutionFailedEventAttributes converts thrift RequestCancelExternalWorkflowExecutionFailedEventAttributes type to internal func ToRequestCancelExternalWorkflowExecutionFailedEventAttributes(t *shared.RequestCancelExternalWorkflowExecutionFailedEventAttributes) *types.RequestCancelExternalWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &types.RequestCancelExternalWorkflowExecutionFailedEventAttributes{ Cause: ToCancelExternalWorkflowExecutionFailedCause(t.Cause), DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), InitiatedEventID: t.GetInitiatedEventId(), Control: t.Control, } } // FromRequestCancelExternalWorkflowExecutionInitiatedEventAttributes converts internal RequestCancelExternalWorkflowExecutionInitiatedEventAttributes type to thrift func FromRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(t *types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) *shared.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &shared.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Control: t.Control, ChildWorkflowOnly: &t.ChildWorkflowOnly, } } // ToRequestCancelExternalWorkflowExecutionInitiatedEventAttributes converts thrift RequestCancelExternalWorkflowExecutionInitiatedEventAttributes type to internal func ToRequestCancelExternalWorkflowExecutionInitiatedEventAttributes(t *shared.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) *types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Control: t.Control, ChildWorkflowOnly: t.GetChildWorkflowOnly(), } } // FromRequestCancelWorkflowExecutionRequest converts internal RequestCancelWorkflowExecutionRequest type to thrift func FromRequestCancelWorkflowExecutionRequest(t *types.RequestCancelWorkflowExecutionRequest) *shared.RequestCancelWorkflowExecutionRequest { if t == nil { return nil } return &shared.RequestCancelWorkflowExecutionRequest{ Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Identity: &t.Identity, RequestId: &t.RequestID, Cause: &t.Cause, FirstExecutionRunID: &t.FirstExecutionRunID, } } // ToRequestCancelWorkflowExecutionRequest converts thrift RequestCancelWorkflowExecutionRequest type to internal func ToRequestCancelWorkflowExecutionRequest(t *shared.RequestCancelWorkflowExecutionRequest) *types.RequestCancelWorkflowExecutionRequest { if t == nil { return nil } return &types.RequestCancelWorkflowExecutionRequest{ Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Identity: t.GetIdentity(), RequestID: t.GetRequestId(), Cause: t.GetCause(), FirstExecutionRunID: t.GetFirstExecutionRunID(), } } // FromResetPointInfo converts internal ResetPointInfo type to thrift func FromResetPointInfo(t *types.ResetPointInfo) *shared.ResetPointInfo { if t == nil { return nil } return &shared.ResetPointInfo{ BinaryChecksum: &t.BinaryChecksum, RunId: &t.RunID, FirstDecisionCompletedId: &t.FirstDecisionCompletedID, CreatedTimeNano: t.CreatedTimeNano, ExpiringTimeNano: t.ExpiringTimeNano, Resettable: &t.Resettable, } } // ToResetPointInfo converts thrift ResetPointInfo type to internal func ToResetPointInfo(t *shared.ResetPointInfo) *types.ResetPointInfo { if t == nil { return nil } return &types.ResetPointInfo{ BinaryChecksum: t.GetBinaryChecksum(), RunID: t.GetRunId(), FirstDecisionCompletedID: t.GetFirstDecisionCompletedId(), CreatedTimeNano: t.CreatedTimeNano, ExpiringTimeNano: t.ExpiringTimeNano, Resettable: t.GetResettable(), } } // FromResetPoints converts internal ResetPoints type to thrift func FromResetPoints(t *types.ResetPoints) *shared.ResetPoints { if t == nil { return nil } return &shared.ResetPoints{ Points: FromResetPointInfoArray(t.Points), } } // ToResetPoints converts thrift ResetPoints type to internal func ToResetPoints(t *shared.ResetPoints) *types.ResetPoints { if t == nil { return nil } return &types.ResetPoints{ Points: ToResetPointInfoArray(t.Points), } } // FromAdminResetQueueRequest converts internal ResetQueueRequest type to thrift func FromAdminResetQueueRequest(t *types.ResetQueueRequest) *shared.ResetQueueRequest { if t == nil { return nil } return &shared.ResetQueueRequest{ ShardID: &t.ShardID, ClusterName: &t.ClusterName, Type: t.Type, } } // ToAdminResetQueueRequest converts thrift ResetQueueRequest type to internal func ToAdminResetQueueRequest(t *shared.ResetQueueRequest) *types.ResetQueueRequest { if t == nil { return nil } return &types.ResetQueueRequest{ ShardID: t.GetShardID(), ClusterName: t.GetClusterName(), Type: t.Type, } } // FromResetStickyTaskListRequest converts internal ResetStickyTaskListRequest type to thrift func FromResetStickyTaskListRequest(t *types.ResetStickyTaskListRequest) *shared.ResetStickyTaskListRequest { if t == nil { return nil } return &shared.ResetStickyTaskListRequest{ Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), } } // ToResetStickyTaskListRequest converts thrift ResetStickyTaskListRequest type to internal func ToResetStickyTaskListRequest(t *shared.ResetStickyTaskListRequest) *types.ResetStickyTaskListRequest { if t == nil { return nil } return &types.ResetStickyTaskListRequest{ Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), } } // FromResetStickyTaskListResponse converts internal ResetStickyTaskListResponse type to thrift func FromResetStickyTaskListResponse(t *types.ResetStickyTaskListResponse) *shared.ResetStickyTaskListResponse { if t == nil { return nil } return &shared.ResetStickyTaskListResponse{} } // ToResetStickyTaskListResponse converts thrift ResetStickyTaskListResponse type to internal func ToResetStickyTaskListResponse(t *shared.ResetStickyTaskListResponse) *types.ResetStickyTaskListResponse { if t == nil { return nil } return &types.ResetStickyTaskListResponse{} } // FromResetWorkflowExecutionRequest converts internal ResetWorkflowExecutionRequest type to thrift func FromResetWorkflowExecutionRequest(t *types.ResetWorkflowExecutionRequest) *shared.ResetWorkflowExecutionRequest { if t == nil { return nil } return &shared.ResetWorkflowExecutionRequest{ Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Reason: &t.Reason, DecisionFinishEventId: &t.DecisionFinishEventID, RequestId: &t.RequestID, SkipSignalReapply: &t.SkipSignalReapply, } } // ToResetWorkflowExecutionRequest converts thrift ResetWorkflowExecutionRequest type to internal func ToResetWorkflowExecutionRequest(t *shared.ResetWorkflowExecutionRequest) *types.ResetWorkflowExecutionRequest { if t == nil { return nil } return &types.ResetWorkflowExecutionRequest{ Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Reason: t.GetReason(), DecisionFinishEventID: t.GetDecisionFinishEventId(), RequestID: t.GetRequestId(), SkipSignalReapply: t.GetSkipSignalReapply(), } } // FromResetWorkflowExecutionResponse converts internal ResetWorkflowExecutionResponse type to thrift func FromResetWorkflowExecutionResponse(t *types.ResetWorkflowExecutionResponse) *shared.ResetWorkflowExecutionResponse { if t == nil { return nil } return &shared.ResetWorkflowExecutionResponse{ RunId: &t.RunID, } } // ToResetWorkflowExecutionResponse converts thrift ResetWorkflowExecutionResponse type to internal func ToResetWorkflowExecutionResponse(t *shared.ResetWorkflowExecutionResponse) *types.ResetWorkflowExecutionResponse { if t == nil { return nil } return &types.ResetWorkflowExecutionResponse{ RunID: t.GetRunId(), } } // FromRespondActivityTaskCanceledByIDRequest converts internal RespondActivityTaskCanceledByIDRequest type to thrift func FromRespondActivityTaskCanceledByIDRequest(t *types.RespondActivityTaskCanceledByIDRequest) *shared.RespondActivityTaskCanceledByIDRequest { if t == nil { return nil } return &shared.RespondActivityTaskCanceledByIDRequest{ Domain: &t.Domain, WorkflowID: &t.WorkflowID, RunID: &t.RunID, ActivityID: &t.ActivityID, Details: t.Details, Identity: &t.Identity, } } // ToRespondActivityTaskCanceledByIDRequest converts thrift RespondActivityTaskCanceledByIDRequest type to internal func ToRespondActivityTaskCanceledByIDRequest(t *shared.RespondActivityTaskCanceledByIDRequest) *types.RespondActivityTaskCanceledByIDRequest { if t == nil { return nil } return &types.RespondActivityTaskCanceledByIDRequest{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowID(), RunID: t.GetRunID(), ActivityID: t.GetActivityID(), Details: t.Details, Identity: t.GetIdentity(), } } // FromRespondActivityTaskCanceledRequest converts internal RespondActivityTaskCanceledRequest type to thrift func FromRespondActivityTaskCanceledRequest(t *types.RespondActivityTaskCanceledRequest) *shared.RespondActivityTaskCanceledRequest { if t == nil { return nil } return &shared.RespondActivityTaskCanceledRequest{ TaskToken: t.TaskToken, Details: t.Details, Identity: &t.Identity, } } // ToRespondActivityTaskCanceledRequest converts thrift RespondActivityTaskCanceledRequest type to internal func ToRespondActivityTaskCanceledRequest(t *shared.RespondActivityTaskCanceledRequest) *types.RespondActivityTaskCanceledRequest { if t == nil { return nil } return &types.RespondActivityTaskCanceledRequest{ TaskToken: t.TaskToken, Details: t.Details, Identity: t.GetIdentity(), } } // FromRespondActivityTaskCompletedByIDRequest converts internal RespondActivityTaskCompletedByIDRequest type to thrift func FromRespondActivityTaskCompletedByIDRequest(t *types.RespondActivityTaskCompletedByIDRequest) *shared.RespondActivityTaskCompletedByIDRequest { if t == nil { return nil } return &shared.RespondActivityTaskCompletedByIDRequest{ Domain: &t.Domain, WorkflowID: &t.WorkflowID, RunID: &t.RunID, ActivityID: &t.ActivityID, Result: t.Result, Identity: &t.Identity, } } // ToRespondActivityTaskCompletedByIDRequest converts thrift RespondActivityTaskCompletedByIDRequest type to internal func ToRespondActivityTaskCompletedByIDRequest(t *shared.RespondActivityTaskCompletedByIDRequest) *types.RespondActivityTaskCompletedByIDRequest { if t == nil { return nil } return &types.RespondActivityTaskCompletedByIDRequest{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowID(), RunID: t.GetRunID(), ActivityID: t.GetActivityID(), Result: t.Result, Identity: t.GetIdentity(), } } // FromRespondActivityTaskCompletedRequest converts internal RespondActivityTaskCompletedRequest type to thrift func FromRespondActivityTaskCompletedRequest(t *types.RespondActivityTaskCompletedRequest) *shared.RespondActivityTaskCompletedRequest { if t == nil { return nil } return &shared.RespondActivityTaskCompletedRequest{ TaskToken: t.TaskToken, Result: t.Result, Identity: &t.Identity, } } // ToRespondActivityTaskCompletedRequest converts thrift RespondActivityTaskCompletedRequest type to internal func ToRespondActivityTaskCompletedRequest(t *shared.RespondActivityTaskCompletedRequest) *types.RespondActivityTaskCompletedRequest { if t == nil { return nil } return &types.RespondActivityTaskCompletedRequest{ TaskToken: t.TaskToken, Result: t.Result, Identity: t.GetIdentity(), } } // FromRespondActivityTaskFailedByIDRequest converts internal RespondActivityTaskFailedByIDRequest type to thrift func FromRespondActivityTaskFailedByIDRequest(t *types.RespondActivityTaskFailedByIDRequest) *shared.RespondActivityTaskFailedByIDRequest { if t == nil { return nil } return &shared.RespondActivityTaskFailedByIDRequest{ Domain: &t.Domain, WorkflowID: &t.WorkflowID, RunID: &t.RunID, ActivityID: &t.ActivityID, Reason: t.Reason, Details: t.Details, Identity: &t.Identity, } } // ToRespondActivityTaskFailedByIDRequest converts thrift RespondActivityTaskFailedByIDRequest type to internal func ToRespondActivityTaskFailedByIDRequest(t *shared.RespondActivityTaskFailedByIDRequest) *types.RespondActivityTaskFailedByIDRequest { if t == nil { return nil } return &types.RespondActivityTaskFailedByIDRequest{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowID(), RunID: t.GetRunID(), ActivityID: t.GetActivityID(), Reason: t.Reason, Details: t.Details, Identity: t.GetIdentity(), } } // FromRespondActivityTaskFailedRequest converts internal RespondActivityTaskFailedRequest type to thrift func FromRespondActivityTaskFailedRequest(t *types.RespondActivityTaskFailedRequest) *shared.RespondActivityTaskFailedRequest { if t == nil { return nil } return &shared.RespondActivityTaskFailedRequest{ TaskToken: t.TaskToken, Reason: t.Reason, Details: t.Details, Identity: &t.Identity, } } // ToRespondActivityTaskFailedRequest converts thrift RespondActivityTaskFailedRequest type to internal func ToRespondActivityTaskFailedRequest(t *shared.RespondActivityTaskFailedRequest) *types.RespondActivityTaskFailedRequest { if t == nil { return nil } return &types.RespondActivityTaskFailedRequest{ TaskToken: t.TaskToken, Reason: t.Reason, Details: t.Details, Identity: t.GetIdentity(), } } // FromRespondDecisionTaskCompletedRequest converts internal RespondDecisionTaskCompletedRequest type to thrift func FromRespondDecisionTaskCompletedRequest(t *types.RespondDecisionTaskCompletedRequest) *shared.RespondDecisionTaskCompletedRequest { if t == nil { return nil } return &shared.RespondDecisionTaskCompletedRequest{ TaskToken: t.TaskToken, Decisions: FromDecisionArray(t.Decisions), ExecutionContext: t.ExecutionContext, Identity: &t.Identity, StickyAttributes: FromStickyExecutionAttributes(t.StickyAttributes), ReturnNewDecisionTask: &t.ReturnNewDecisionTask, ForceCreateNewDecisionTask: &t.ForceCreateNewDecisionTask, BinaryChecksum: &t.BinaryChecksum, QueryResults: FromWorkflowQueryResultMap(t.QueryResults), } } // ToRespondDecisionTaskCompletedRequest converts thrift RespondDecisionTaskCompletedRequest type to internal func ToRespondDecisionTaskCompletedRequest(t *shared.RespondDecisionTaskCompletedRequest) *types.RespondDecisionTaskCompletedRequest { if t == nil { return nil } return &types.RespondDecisionTaskCompletedRequest{ TaskToken: t.TaskToken, Decisions: ToDecisionArray(t.Decisions), ExecutionContext: t.ExecutionContext, Identity: t.GetIdentity(), StickyAttributes: ToStickyExecutionAttributes(t.StickyAttributes), ReturnNewDecisionTask: t.GetReturnNewDecisionTask(), ForceCreateNewDecisionTask: t.GetForceCreateNewDecisionTask(), BinaryChecksum: t.GetBinaryChecksum(), QueryResults: ToWorkflowQueryResultMap(t.QueryResults), } } // FromRespondDecisionTaskCompletedResponse converts internal RespondDecisionTaskCompletedResponse type to thrift func FromRespondDecisionTaskCompletedResponse(t *types.RespondDecisionTaskCompletedResponse) *shared.RespondDecisionTaskCompletedResponse { if t == nil { return nil } return &shared.RespondDecisionTaskCompletedResponse{ DecisionTask: FromPollForDecisionTaskResponse(t.DecisionTask), ActivitiesToDispatchLocally: FromActivityLocalDispatchInfoMap(t.ActivitiesToDispatchLocally), } } // ToRespondDecisionTaskCompletedResponse converts thrift RespondDecisionTaskCompletedResponse type to internal func ToRespondDecisionTaskCompletedResponse(t *shared.RespondDecisionTaskCompletedResponse) *types.RespondDecisionTaskCompletedResponse { if t == nil { return nil } return &types.RespondDecisionTaskCompletedResponse{ DecisionTask: ToPollForDecisionTaskResponse(t.DecisionTask), ActivitiesToDispatchLocally: ToActivityLocalDispatchInfoMap(t.ActivitiesToDispatchLocally), } } // FromRespondDecisionTaskFailedRequest converts internal RespondDecisionTaskFailedRequest type to thrift func FromRespondDecisionTaskFailedRequest(t *types.RespondDecisionTaskFailedRequest) *shared.RespondDecisionTaskFailedRequest { if t == nil { return nil } return &shared.RespondDecisionTaskFailedRequest{ TaskToken: t.TaskToken, Cause: FromDecisionTaskFailedCause(t.Cause), Details: t.Details, Identity: &t.Identity, BinaryChecksum: &t.BinaryChecksum, } } // ToRespondDecisionTaskFailedRequest converts thrift RespondDecisionTaskFailedRequest type to internal func ToRespondDecisionTaskFailedRequest(t *shared.RespondDecisionTaskFailedRequest) *types.RespondDecisionTaskFailedRequest { if t == nil { return nil } return &types.RespondDecisionTaskFailedRequest{ TaskToken: t.TaskToken, Cause: ToDecisionTaskFailedCause(t.Cause), Details: t.Details, Identity: t.GetIdentity(), BinaryChecksum: t.GetBinaryChecksum(), } } // FromRespondQueryTaskCompletedRequest converts internal RespondQueryTaskCompletedRequest type to thrift func FromRespondQueryTaskCompletedRequest(t *types.RespondQueryTaskCompletedRequest) *shared.RespondQueryTaskCompletedRequest { if t == nil { return nil } return &shared.RespondQueryTaskCompletedRequest{ TaskToken: t.TaskToken, CompletedType: FromQueryTaskCompletedType(t.CompletedType), QueryResult: t.QueryResult, ErrorMessage: &t.ErrorMessage, WorkerVersionInfo: FromWorkerVersionInfo(t.WorkerVersionInfo), } } // ToRespondQueryTaskCompletedRequest converts thrift RespondQueryTaskCompletedRequest type to internal func ToRespondQueryTaskCompletedRequest(t *shared.RespondQueryTaskCompletedRequest) *types.RespondQueryTaskCompletedRequest { if t == nil { return nil } return &types.RespondQueryTaskCompletedRequest{ TaskToken: t.TaskToken, CompletedType: ToQueryTaskCompletedType(t.CompletedType), QueryResult: t.QueryResult, ErrorMessage: t.GetErrorMessage(), WorkerVersionInfo: ToWorkerVersionInfo(t.WorkerVersionInfo), } } // FromRetryPolicy converts internal RetryPolicy type to thrift func FromRetryPolicy(t *types.RetryPolicy) *shared.RetryPolicy { if t == nil { return nil } return &shared.RetryPolicy{ InitialIntervalInSeconds: &t.InitialIntervalInSeconds, BackoffCoefficient: &t.BackoffCoefficient, MaximumIntervalInSeconds: &t.MaximumIntervalInSeconds, MaximumAttempts: &t.MaximumAttempts, NonRetriableErrorReasons: t.NonRetriableErrorReasons, ExpirationIntervalInSeconds: &t.ExpirationIntervalInSeconds, } } // ToRetryPolicy converts thrift RetryPolicy type to internal func ToRetryPolicy(t *shared.RetryPolicy) *types.RetryPolicy { if t == nil { return nil } return &types.RetryPolicy{ InitialIntervalInSeconds: t.GetInitialIntervalInSeconds(), BackoffCoefficient: t.GetBackoffCoefficient(), MaximumIntervalInSeconds: t.GetMaximumIntervalInSeconds(), MaximumAttempts: t.GetMaximumAttempts(), NonRetriableErrorReasons: t.NonRetriableErrorReasons, ExpirationIntervalInSeconds: t.GetExpirationIntervalInSeconds(), } } // FromRetryTaskV2Error converts internal RetryTaskV2Error type to thrift func FromRetryTaskV2Error(t *types.RetryTaskV2Error) *shared.RetryTaskV2Error { if t == nil { return nil } return &shared.RetryTaskV2Error{ Message: t.Message, DomainId: &t.DomainID, WorkflowId: &t.WorkflowID, RunId: &t.RunID, StartEventId: t.StartEventID, StartEventVersion: t.StartEventVersion, EndEventId: t.EndEventID, EndEventVersion: t.EndEventVersion, } } // ToRetryTaskV2Error converts thrift RetryTaskV2Error type to internal func ToRetryTaskV2Error(t *shared.RetryTaskV2Error) *types.RetryTaskV2Error { if t == nil { return nil } return &types.RetryTaskV2Error{ Message: t.Message, DomainID: t.GetDomainId(), WorkflowID: t.GetWorkflowId(), RunID: t.GetRunId(), StartEventID: t.StartEventId, StartEventVersion: t.StartEventVersion, EndEventID: t.EndEventId, EndEventVersion: t.EndEventVersion, } } // FromRestartWorkflowExecutionRequest converts internal RestartWorkflowExecutionRequest type to thrift func FromRestartWorkflowExecutionRequest(t *types.RestartWorkflowExecutionRequest) *shared.RestartWorkflowExecutionRequest { if t == nil { return nil } return &shared.RestartWorkflowExecutionRequest{ Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Identity: &t.Identity, } } // ToRestartWorkflowExecutionRequest converts thrift RestartWorkflowExecutionRequest type to internal func ToRestartWorkflowExecutionRequest(t *shared.RestartWorkflowExecutionRequest) *types.RestartWorkflowExecutionRequest { if t == nil { return nil } return &types.RestartWorkflowExecutionRequest{ Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Identity: t.GetIdentity(), } } // FromScheduleActivityTaskDecisionAttributes converts internal ScheduleActivityTaskDecisionAttributes type to thrift func FromScheduleActivityTaskDecisionAttributes(t *types.ScheduleActivityTaskDecisionAttributes) *shared.ScheduleActivityTaskDecisionAttributes { if t == nil { return nil } return &shared.ScheduleActivityTaskDecisionAttributes{ ActivityId: &t.ActivityID, ActivityType: FromActivityType(t.ActivityType), Domain: &t.Domain, TaskList: FromTaskList(t.TaskList), Input: t.Input, ScheduleToCloseTimeoutSeconds: t.ScheduleToCloseTimeoutSeconds, ScheduleToStartTimeoutSeconds: t.ScheduleToStartTimeoutSeconds, StartToCloseTimeoutSeconds: t.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: t.HeartbeatTimeoutSeconds, RetryPolicy: FromRetryPolicy(t.RetryPolicy), Header: FromHeader(t.Header), RequestLocalDispatch: &t.RequestLocalDispatch, } } // ToScheduleActivityTaskDecisionAttributes converts thrift ScheduleActivityTaskDecisionAttributes type to internal func ToScheduleActivityTaskDecisionAttributes(t *shared.ScheduleActivityTaskDecisionAttributes) *types.ScheduleActivityTaskDecisionAttributes { if t == nil { return nil } return &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: t.GetActivityId(), ActivityType: ToActivityType(t.ActivityType), Domain: t.GetDomain(), TaskList: ToTaskList(t.TaskList), Input: t.Input, ScheduleToCloseTimeoutSeconds: t.ScheduleToCloseTimeoutSeconds, ScheduleToStartTimeoutSeconds: t.ScheduleToStartTimeoutSeconds, StartToCloseTimeoutSeconds: t.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: t.HeartbeatTimeoutSeconds, RetryPolicy: ToRetryPolicy(t.RetryPolicy), Header: ToHeader(t.Header), RequestLocalDispatch: t.GetRequestLocalDispatch(), } } // FromSearchAttributes converts internal SearchAttributes type to thrift func FromSearchAttributes(t *types.SearchAttributes) *shared.SearchAttributes { if t == nil { return nil } return &shared.SearchAttributes{ IndexedFields: t.IndexedFields, } } // ToSearchAttributes converts thrift SearchAttributes type to internal func ToSearchAttributes(t *shared.SearchAttributes) *types.SearchAttributes { if t == nil { return nil } return &types.SearchAttributes{ IndexedFields: t.IndexedFields, } } // FromServiceBusyError converts internal ServiceBusyError type to thrift func FromServiceBusyError(t *types.ServiceBusyError) *shared.ServiceBusyError { if t == nil { return nil } return &shared.ServiceBusyError{ Message: t.Message, Reason: &t.Reason, } } // ToServiceBusyError converts thrift ServiceBusyError type to internal func ToServiceBusyError(t *shared.ServiceBusyError) *types.ServiceBusyError { if t == nil { return nil } return &types.ServiceBusyError{ Message: t.Message, Reason: t.GetReason(), } } // FromSignalExternalWorkflowExecutionDecisionAttributes converts internal SignalExternalWorkflowExecutionDecisionAttributes type to thrift func FromSignalExternalWorkflowExecutionDecisionAttributes(t *types.SignalExternalWorkflowExecutionDecisionAttributes) *shared.SignalExternalWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &shared.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: &t.Domain, Execution: FromWorkflowExecution(t.Execution), SignalName: &t.SignalName, Input: t.Input, Control: t.Control, ChildWorkflowOnly: &t.ChildWorkflowOnly, } } // ToSignalExternalWorkflowExecutionDecisionAttributes converts thrift SignalExternalWorkflowExecutionDecisionAttributes type to internal func ToSignalExternalWorkflowExecutionDecisionAttributes(t *shared.SignalExternalWorkflowExecutionDecisionAttributes) *types.SignalExternalWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: t.GetDomain(), Execution: ToWorkflowExecution(t.Execution), SignalName: t.GetSignalName(), Input: t.Input, Control: t.Control, ChildWorkflowOnly: t.GetChildWorkflowOnly(), } } // FromSignalExternalWorkflowExecutionFailedCause converts internal SignalExternalWorkflowExecutionFailedCause type to thrift func FromSignalExternalWorkflowExecutionFailedCause(t *types.SignalExternalWorkflowExecutionFailedCause) *shared.SignalExternalWorkflowExecutionFailedCause { if t == nil { return nil } switch *t { case types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution: v := shared.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution return &v case types.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted: v := shared.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted return &v } panic("unexpected enum value") } // ToSignalExternalWorkflowExecutionFailedCause converts thrift SignalExternalWorkflowExecutionFailedCause type to internal func ToSignalExternalWorkflowExecutionFailedCause(t *shared.SignalExternalWorkflowExecutionFailedCause) *types.SignalExternalWorkflowExecutionFailedCause { if t == nil { return nil } switch *t { case shared.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution: v := types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution return &v case shared.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted: v := types.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted return &v } panic("unexpected enum value") } // FromSignalExternalWorkflowExecutionFailedEventAttributes converts internal SignalExternalWorkflowExecutionFailedEventAttributes type to thrift func FromSignalExternalWorkflowExecutionFailedEventAttributes(t *types.SignalExternalWorkflowExecutionFailedEventAttributes) *shared.SignalExternalWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &shared.SignalExternalWorkflowExecutionFailedEventAttributes{ Cause: FromSignalExternalWorkflowExecutionFailedCause(t.Cause), DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), InitiatedEventId: &t.InitiatedEventID, Control: t.Control, } } // ToSignalExternalWorkflowExecutionFailedEventAttributes converts thrift SignalExternalWorkflowExecutionFailedEventAttributes type to internal func ToSignalExternalWorkflowExecutionFailedEventAttributes(t *shared.SignalExternalWorkflowExecutionFailedEventAttributes) *types.SignalExternalWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &types.SignalExternalWorkflowExecutionFailedEventAttributes{ Cause: ToSignalExternalWorkflowExecutionFailedCause(t.Cause), DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), InitiatedEventID: t.GetInitiatedEventId(), Control: t.Control, } } // FromSignalExternalWorkflowExecutionInitiatedEventAttributes converts internal SignalExternalWorkflowExecutionInitiatedEventAttributes type to thrift func FromSignalExternalWorkflowExecutionInitiatedEventAttributes(t *types.SignalExternalWorkflowExecutionInitiatedEventAttributes) *shared.SignalExternalWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &shared.SignalExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), SignalName: &t.SignalName, Input: t.Input, Control: t.Control, ChildWorkflowOnly: &t.ChildWorkflowOnly, } } // ToSignalExternalWorkflowExecutionInitiatedEventAttributes converts thrift SignalExternalWorkflowExecutionInitiatedEventAttributes type to internal func ToSignalExternalWorkflowExecutionInitiatedEventAttributes(t *shared.SignalExternalWorkflowExecutionInitiatedEventAttributes) *types.SignalExternalWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &types.SignalExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), SignalName: t.GetSignalName(), Input: t.Input, Control: t.Control, ChildWorkflowOnly: t.GetChildWorkflowOnly(), } } // FromSignalWithStartWorkflowExecutionRequest converts internal SignalWithStartWorkflowExecutionRequest type to thrift func FromSignalWithStartWorkflowExecutionRequest(t *types.SignalWithStartWorkflowExecutionRequest) *shared.SignalWithStartWorkflowExecutionRequest { if t == nil { return nil } return &shared.SignalWithStartWorkflowExecutionRequest{ Domain: &t.Domain, WorkflowId: &t.WorkflowID, WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, Identity: &t.Identity, RequestId: &t.RequestID, WorkflowIdReusePolicy: FromWorkflowIDReusePolicy(t.WorkflowIDReusePolicy), SignalName: &t.SignalName, SignalInput: t.SignalInput, Control: t.Control, RetryPolicy: FromRetryPolicy(t.RetryPolicy), CronSchedule: &t.CronSchedule, Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), Header: FromHeader(t.Header), FirstRunAtTimestamp: t.FirstRunAtTimestamp, CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } // ToSignalWithStartWorkflowExecutionRequest converts thrift SignalWithStartWorkflowExecutionRequest type to internal func ToSignalWithStartWorkflowExecutionRequest(t *shared.SignalWithStartWorkflowExecutionRequest) *types.SignalWithStartWorkflowExecutionRequest { if t == nil { return nil } return &types.SignalWithStartWorkflowExecutionRequest{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowId(), WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, Identity: t.GetIdentity(), RequestID: t.GetRequestId(), WorkflowIDReusePolicy: ToWorkflowIDReusePolicy(t.WorkflowIdReusePolicy), SignalName: t.GetSignalName(), SignalInput: t.SignalInput, Control: t.Control, RetryPolicy: ToRetryPolicy(t.RetryPolicy), CronSchedule: t.GetCronSchedule(), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), Header: ToHeader(t.Header), FirstRunAtTimestamp: t.FirstRunAtTimestamp, CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } // FromSignalWorkflowExecutionRequest converts internal SignalWorkflowExecutionRequest type to thrift func FromSignalWorkflowExecutionRequest(t *types.SignalWorkflowExecutionRequest) *shared.SignalWorkflowExecutionRequest { if t == nil { return nil } return &shared.SignalWorkflowExecutionRequest{ Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), SignalName: &t.SignalName, Input: t.Input, Identity: &t.Identity, RequestId: &t.RequestID, Control: t.Control, } } // ToSignalWorkflowExecutionRequest converts thrift SignalWorkflowExecutionRequest type to internal func ToSignalWorkflowExecutionRequest(t *shared.SignalWorkflowExecutionRequest) *types.SignalWorkflowExecutionRequest { if t == nil { return nil } return &types.SignalWorkflowExecutionRequest{ Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), SignalName: t.GetSignalName(), Input: t.Input, Identity: t.GetIdentity(), RequestID: t.GetRequestId(), Control: t.Control, } } // FromStartChildWorkflowExecutionDecisionAttributes converts internal StartChildWorkflowExecutionDecisionAttributes type to thrift func FromStartChildWorkflowExecutionDecisionAttributes(t *types.StartChildWorkflowExecutionDecisionAttributes) *shared.StartChildWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &shared.StartChildWorkflowExecutionDecisionAttributes{ Domain: &t.Domain, WorkflowId: &t.WorkflowID, WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, ParentClosePolicy: FromParentClosePolicy(t.ParentClosePolicy), Control: t.Control, WorkflowIdReusePolicy: FromWorkflowIDReusePolicy(t.WorkflowIDReusePolicy), RetryPolicy: FromRetryPolicy(t.RetryPolicy), CronSchedule: &t.CronSchedule, Header: FromHeader(t.Header), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } // ToStartChildWorkflowExecutionDecisionAttributes converts thrift StartChildWorkflowExecutionDecisionAttributes type to internal func ToStartChildWorkflowExecutionDecisionAttributes(t *shared.StartChildWorkflowExecutionDecisionAttributes) *types.StartChildWorkflowExecutionDecisionAttributes { if t == nil { return nil } return &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowId(), WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, ParentClosePolicy: ToParentClosePolicy(t.ParentClosePolicy), Control: t.Control, WorkflowIDReusePolicy: ToWorkflowIDReusePolicy(t.WorkflowIdReusePolicy), RetryPolicy: ToRetryPolicy(t.RetryPolicy), CronSchedule: t.GetCronSchedule(), Header: ToHeader(t.Header), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } // FromStartChildWorkflowExecutionFailedEventAttributes converts internal StartChildWorkflowExecutionFailedEventAttributes type to thrift func FromStartChildWorkflowExecutionFailedEventAttributes(t *types.StartChildWorkflowExecutionFailedEventAttributes) *shared.StartChildWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &shared.StartChildWorkflowExecutionFailedEventAttributes{ Domain: &t.Domain, WorkflowId: &t.WorkflowID, WorkflowType: FromWorkflowType(t.WorkflowType), Cause: FromChildWorkflowExecutionFailedCause(t.Cause), Control: t.Control, InitiatedEventId: &t.InitiatedEventID, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, } } // ToStartChildWorkflowExecutionFailedEventAttributes converts thrift StartChildWorkflowExecutionFailedEventAttributes type to internal func ToStartChildWorkflowExecutionFailedEventAttributes(t *shared.StartChildWorkflowExecutionFailedEventAttributes) *types.StartChildWorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &types.StartChildWorkflowExecutionFailedEventAttributes{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowId(), WorkflowType: ToWorkflowType(t.WorkflowType), Cause: ToChildWorkflowExecutionFailedCause(t.Cause), Control: t.Control, InitiatedEventID: t.GetInitiatedEventId(), DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), } } // FromStartChildWorkflowExecutionInitiatedEventAttributes converts internal StartChildWorkflowExecutionInitiatedEventAttributes type to thrift func FromStartChildWorkflowExecutionInitiatedEventAttributes(t *types.StartChildWorkflowExecutionInitiatedEventAttributes) *shared.StartChildWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &shared.StartChildWorkflowExecutionInitiatedEventAttributes{ Domain: &t.Domain, WorkflowId: &t.WorkflowID, WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, ParentClosePolicy: FromParentClosePolicy(t.ParentClosePolicy), Control: t.Control, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, WorkflowIdReusePolicy: FromWorkflowIDReusePolicy(t.WorkflowIDReusePolicy), RetryPolicy: FromRetryPolicy(t.RetryPolicy), CronSchedule: &t.CronSchedule, Header: FromHeader(t.Header), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), DelayStartSeconds: t.DelayStartSeconds, JitterStartSeconds: t.JitterStartSeconds, FirstRunAtTimestamp: t.FirstRunAtTimestamp, CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } // ToStartChildWorkflowExecutionInitiatedEventAttributes converts thrift StartChildWorkflowExecutionInitiatedEventAttributes type to internal func ToStartChildWorkflowExecutionInitiatedEventAttributes(t *shared.StartChildWorkflowExecutionInitiatedEventAttributes) *types.StartChildWorkflowExecutionInitiatedEventAttributes { if t == nil { return nil } return &types.StartChildWorkflowExecutionInitiatedEventAttributes{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowId(), WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, ParentClosePolicy: ToParentClosePolicy(t.ParentClosePolicy), Control: t.Control, DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), WorkflowIDReusePolicy: ToWorkflowIDReusePolicy(t.WorkflowIdReusePolicy), RetryPolicy: ToRetryPolicy(t.RetryPolicy), CronSchedule: t.GetCronSchedule(), Header: ToHeader(t.Header), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), DelayStartSeconds: t.DelayStartSeconds, JitterStartSeconds: t.JitterStartSeconds, FirstRunAtTimestamp: t.FirstRunAtTimestamp, CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } // FromStartTimeFilter converts internal StartTimeFilter type to thrift func FromStartTimeFilter(t *types.StartTimeFilter) *shared.StartTimeFilter { if t == nil { return nil } return &shared.StartTimeFilter{ EarliestTime: t.EarliestTime, LatestTime: t.LatestTime, } } // ToStartTimeFilter converts thrift StartTimeFilter type to internal func ToStartTimeFilter(t *shared.StartTimeFilter) *types.StartTimeFilter { if t == nil { return nil } return &types.StartTimeFilter{ EarliestTime: t.EarliestTime, LatestTime: t.LatestTime, } } // FromStartTimerDecisionAttributes converts internal StartTimerDecisionAttributes type to thrift func FromStartTimerDecisionAttributes(t *types.StartTimerDecisionAttributes) *shared.StartTimerDecisionAttributes { if t == nil { return nil } return &shared.StartTimerDecisionAttributes{ TimerId: &t.TimerID, StartToFireTimeoutSeconds: t.StartToFireTimeoutSeconds, } } // ToStartTimerDecisionAttributes converts thrift StartTimerDecisionAttributes type to internal func ToStartTimerDecisionAttributes(t *shared.StartTimerDecisionAttributes) *types.StartTimerDecisionAttributes { if t == nil { return nil } return &types.StartTimerDecisionAttributes{ TimerID: t.GetTimerId(), StartToFireTimeoutSeconds: t.StartToFireTimeoutSeconds, } } func FromSignalWithStartWorkflowExecutionAsyncRequest(t *types.SignalWithStartWorkflowExecutionAsyncRequest) *shared.SignalWithStartWorkflowExecutionAsyncRequest { if t == nil { return nil } return &shared.SignalWithStartWorkflowExecutionAsyncRequest{ Request: FromSignalWithStartWorkflowExecutionRequest(t.SignalWithStartWorkflowExecutionRequest), } } func ToSignalWithStartWorkflowExecutionAsyncRequest(t *shared.SignalWithStartWorkflowExecutionAsyncRequest) *types.SignalWithStartWorkflowExecutionAsyncRequest { if t == nil { return nil } return &types.SignalWithStartWorkflowExecutionAsyncRequest{ SignalWithStartWorkflowExecutionRequest: ToSignalWithStartWorkflowExecutionRequest(t.Request), } } func FromSignalWithStartWorkflowExecutionAsyncResponse(t *types.SignalWithStartWorkflowExecutionAsyncResponse) *shared.SignalWithStartWorkflowExecutionAsyncResponse { if t == nil { return nil } return &shared.SignalWithStartWorkflowExecutionAsyncResponse{} } func ToSignalWithStartWorkflowExecutionAsyncResponse(t *shared.SignalWithStartWorkflowExecutionAsyncResponse) *types.SignalWithStartWorkflowExecutionAsyncResponse { if t == nil { return nil } return &types.SignalWithStartWorkflowExecutionAsyncResponse{} } func FromStartWorkflowExecutionAsyncRequest(t *types.StartWorkflowExecutionAsyncRequest) *shared.StartWorkflowExecutionAsyncRequest { if t == nil { return nil } return &shared.StartWorkflowExecutionAsyncRequest{ Request: FromStartWorkflowExecutionRequest(t.StartWorkflowExecutionRequest), } } func ToStartWorkflowExecutionAsyncRequest(t *shared.StartWorkflowExecutionAsyncRequest) *types.StartWorkflowExecutionAsyncRequest { if t == nil { return nil } return &types.StartWorkflowExecutionAsyncRequest{ StartWorkflowExecutionRequest: ToStartWorkflowExecutionRequest(t.Request), } } func FromStartWorkflowExecutionAsyncResponse(t *types.StartWorkflowExecutionAsyncResponse) *shared.StartWorkflowExecutionAsyncResponse { if t == nil { return nil } return &shared.StartWorkflowExecutionAsyncResponse{} } func ToStartWorkflowExecutionAsyncResponse(t *shared.StartWorkflowExecutionAsyncResponse) *types.StartWorkflowExecutionAsyncResponse { if t == nil { return nil } return &types.StartWorkflowExecutionAsyncResponse{} } // FromStartWorkflowExecutionRequest converts internal StartWorkflowExecutionRequest type to thrift func FromStartWorkflowExecutionRequest(t *types.StartWorkflowExecutionRequest) *shared.StartWorkflowExecutionRequest { if t == nil { return nil } thriftPolicy := FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy) return &shared.StartWorkflowExecutionRequest{ Domain: &t.Domain, WorkflowId: &t.WorkflowID, WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, Identity: &t.Identity, RequestId: &t.RequestID, WorkflowIdReusePolicy: FromWorkflowIDReusePolicy(t.WorkflowIDReusePolicy), RetryPolicy: FromRetryPolicy(t.RetryPolicy), CronSchedule: &t.CronSchedule, Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), Header: FromHeader(t.Header), DelayStartSeconds: t.DelayStartSeconds, JitterStartSeconds: t.JitterStartSeconds, FirstRunAtTimestamp: t.FirstRunAtTimeStamp, CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: thriftPolicy, } } // ToStartWorkflowExecutionRequest converts thrift StartWorkflowExecutionRequest type to internal func ToStartWorkflowExecutionRequest(t *shared.StartWorkflowExecutionRequest) *types.StartWorkflowExecutionRequest { if t == nil { return nil } return &types.StartWorkflowExecutionRequest{ Domain: t.GetDomain(), WorkflowID: t.GetWorkflowId(), WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, Identity: t.GetIdentity(), RequestID: t.GetRequestId(), WorkflowIDReusePolicy: ToWorkflowIDReusePolicy(t.WorkflowIdReusePolicy), RetryPolicy: ToRetryPolicy(t.RetryPolicy), CronSchedule: t.GetCronSchedule(), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), Header: ToHeader(t.Header), DelayStartSeconds: t.DelayStartSeconds, JitterStartSeconds: t.JitterStartSeconds, FirstRunAtTimeStamp: t.FirstRunAtTimestamp, CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), } } // FromRestartWorkflowExecutionResponse converts internal RestartWorkflowExecutionResponse type to thrift func FromRestartWorkflowExecutionResponse(t *types.RestartWorkflowExecutionResponse) *shared.RestartWorkflowExecutionResponse { if t == nil { return nil } return &shared.RestartWorkflowExecutionResponse{ RunId: &t.RunID, } } // FromStartWorkflowExecutionResponse converts internal StartWorkflowExecutionResponse type to thrift func FromStartWorkflowExecutionResponse(t *types.StartWorkflowExecutionResponse) *shared.StartWorkflowExecutionResponse { if t == nil { return nil } return &shared.StartWorkflowExecutionResponse{ RunId: &t.RunID, } } // ToStartWorkflowExecutionResponse converts thrift StartWorkflowExecutionResponse type to internal func ToStartWorkflowExecutionResponse(t *shared.StartWorkflowExecutionResponse) *types.StartWorkflowExecutionResponse { if t == nil { return nil } return &types.StartWorkflowExecutionResponse{ RunID: t.GetRunId(), } } // FromStickyExecutionAttributes converts internal StickyExecutionAttributes type to thrift func FromStickyExecutionAttributes(t *types.StickyExecutionAttributes) *shared.StickyExecutionAttributes { if t == nil { return nil } return &shared.StickyExecutionAttributes{ WorkerTaskList: FromTaskList(t.WorkerTaskList), ScheduleToStartTimeoutSeconds: t.ScheduleToStartTimeoutSeconds, } } // ToStickyExecutionAttributes converts thrift StickyExecutionAttributes type to internal func ToStickyExecutionAttributes(t *shared.StickyExecutionAttributes) *types.StickyExecutionAttributes { if t == nil { return nil } return &types.StickyExecutionAttributes{ WorkerTaskList: ToTaskList(t.WorkerTaskList), ScheduleToStartTimeoutSeconds: t.ScheduleToStartTimeoutSeconds, } } // FromSupportedClientVersions converts internal SupportedClientVersions type to thrift func FromSupportedClientVersions(t *types.SupportedClientVersions) *shared.SupportedClientVersions { if t == nil { return nil } return &shared.SupportedClientVersions{ GoSdk: &t.GoSdk, JavaSdk: &t.JavaSdk, } } // ToSupportedClientVersions converts thrift SupportedClientVersions type to internal func ToSupportedClientVersions(t *shared.SupportedClientVersions) *types.SupportedClientVersions { if t == nil { return nil } return &types.SupportedClientVersions{ GoSdk: t.GetGoSdk(), JavaSdk: t.GetJavaSdk(), } } // FromTaskIDBlock converts internal TaskIDBlock type to thrift func FromTaskIDBlock(t *types.TaskIDBlock) *shared.TaskIDBlock { if t == nil { return nil } return &shared.TaskIDBlock{ StartID: &t.StartID, EndID: &t.EndID, } } // ToTaskIDBlock converts thrift TaskIDBlock type to internal func ToTaskIDBlock(t *shared.TaskIDBlock) *types.TaskIDBlock { if t == nil { return nil } return &types.TaskIDBlock{ StartID: t.GetStartID(), EndID: t.GetEndID(), } } // FromTaskList converts internal TaskList type to thrift func FromTaskList(t *types.TaskList) *shared.TaskList { if t == nil { return nil } return &shared.TaskList{ Name: &t.Name, Kind: FromTaskListKind(t.Kind), } } func MigrateTaskList(name string, t *shared.TaskList) *types.TaskList { if t == nil && name != "" { return &types.TaskList{Name: name, Kind: types.TaskListKindNormal.Ptr()} } return ToTaskList(t) } // ToTaskList converts thrift TaskList type to internal func ToTaskList(t *shared.TaskList) *types.TaskList { if t == nil { return nil } return &types.TaskList{ Name: t.GetName(), Kind: ToTaskListKind(t.Kind), } } // FromTaskListKind converts internal TaskListKind type to thrift func FromTaskListKind(t *types.TaskListKind) *shared.TaskListKind { if t == nil { return nil } switch *t { case types.TaskListKindNormal: return shared.TaskListKindNormal.Ptr() case types.TaskListKindSticky: return shared.TaskListKindSticky.Ptr() case types.TaskListKindEphemeral: return shared.TaskListKindEphemeral.Ptr() } panic("unexpected enum value") } // ToTaskListKind converts thrift TaskListKind type to internal func ToTaskListKind(t *shared.TaskListKind) *types.TaskListKind { if t == nil { return nil } switch *t { case shared.TaskListKindNormal: return types.TaskListKindNormal.Ptr() case shared.TaskListKindSticky: return types.TaskListKindSticky.Ptr() case shared.TaskListKindEphemeral: return types.TaskListKindEphemeral.Ptr() } panic("unexpected enum value") } // FromTaskListMetadata converts internal TaskListMetadata type to thrift func FromTaskListMetadata(t *types.TaskListMetadata) *shared.TaskListMetadata { if t == nil { return nil } return &shared.TaskListMetadata{ MaxTasksPerSecond: t.MaxTasksPerSecond, } } // ToTaskListMetadata converts thrift TaskListMetadata type to internal func ToTaskListMetadata(t *shared.TaskListMetadata) *types.TaskListMetadata { if t == nil { return nil } return &types.TaskListMetadata{ MaxTasksPerSecond: t.MaxTasksPerSecond, } } // FromTaskListPartitionMetadata converts internal TaskListPartitionMetadata type to thrift func FromTaskListPartitionMetadata(t *types.TaskListPartitionMetadata) *shared.TaskListPartitionMetadata { if t == nil { return nil } return &shared.TaskListPartitionMetadata{ Key: &t.Key, OwnerHostName: &t.OwnerHostName, } } // ToTaskListPartitionMetadata converts thrift TaskListPartitionMetadata type to internal func ToTaskListPartitionMetadata(t *shared.TaskListPartitionMetadata) *types.TaskListPartitionMetadata { if t == nil { return nil } return &types.TaskListPartitionMetadata{ Key: t.GetKey(), OwnerHostName: t.GetOwnerHostName(), } } // FromTaskListStatus converts internal TaskListStatus type to thrift func FromTaskListStatus(t *types.TaskListStatus) *shared.TaskListStatus { if t == nil { return nil } return &shared.TaskListStatus{ BacklogCountHint: &t.BacklogCountHint, ReadLevel: &t.ReadLevel, AckLevel: &t.AckLevel, RatePerSecond: &t.RatePerSecond, TaskIDBlock: FromTaskIDBlock(t.TaskIDBlock), IsolationGroupMetrics: FromIsolationGroupMetricsMap(t.IsolationGroupMetrics), NewTasksPerSecond: &t.NewTasksPerSecond, Empty: &t.Empty, } } // ToTaskListStatus converts thrift TaskListStatus type to internal func ToTaskListStatus(t *shared.TaskListStatus) *types.TaskListStatus { if t == nil { return nil } return &types.TaskListStatus{ BacklogCountHint: t.GetBacklogCountHint(), ReadLevel: t.GetReadLevel(), AckLevel: t.GetAckLevel(), RatePerSecond: t.GetRatePerSecond(), TaskIDBlock: ToTaskIDBlock(t.TaskIDBlock), IsolationGroupMetrics: ToIsolationGroupMetricsMap(t.GetIsolationGroupMetrics()), NewTasksPerSecond: t.GetNewTasksPerSecond(), Empty: t.GetEmpty(), } } func FromIsolationGroupMetrics(t *types.IsolationGroupMetrics) *shared.IsolationGroupMetrics { if t == nil { return nil } return &shared.IsolationGroupMetrics{ NewTasksPerSecond: &t.NewTasksPerSecond, PollerCount: &t.PollerCount, } } func ToIsolationGroupMetrics(t *shared.IsolationGroupMetrics) *types.IsolationGroupMetrics { return &types.IsolationGroupMetrics{ NewTasksPerSecond: t.GetNewTasksPerSecond(), PollerCount: t.GetPollerCount(), } } // FromTaskListType converts internal TaskListType type to thrift func FromTaskListType(t *types.TaskListType) *shared.TaskListType { if t == nil { return nil } switch *t { case types.TaskListTypeDecision: v := shared.TaskListTypeDecision return &v case types.TaskListTypeActivity: v := shared.TaskListTypeActivity return &v } panic("unexpected enum value") } // ToTaskListType converts thrift TaskListType type to internal func ToTaskListType(t *shared.TaskListType) *types.TaskListType { if t == nil { return nil } switch *t { case shared.TaskListTypeDecision: v := types.TaskListTypeDecision return &v case shared.TaskListTypeActivity: v := types.TaskListTypeActivity return &v } panic("unexpected enum value") } // ToRestartWorkflowExecutionResponse converts thrift RestartWorkflowExecutionResponse type to internal func ToRestartWorkflowExecutionResponse(t *shared.RestartWorkflowExecutionResponse) *types.RestartWorkflowExecutionResponse { if t == nil { return nil } return &types.RestartWorkflowExecutionResponse{ RunID: t.GetRunId(), } } // FromTerminateWorkflowExecutionRequest converts internal TerminateWorkflowExecutionRequest type to thrift func FromTerminateWorkflowExecutionRequest(t *types.TerminateWorkflowExecutionRequest) *shared.TerminateWorkflowExecutionRequest { if t == nil { return nil } return &shared.TerminateWorkflowExecutionRequest{ Domain: &t.Domain, WorkflowExecution: FromWorkflowExecution(t.WorkflowExecution), Reason: &t.Reason, Details: t.Details, Identity: &t.Identity, FirstExecutionRunID: &t.FirstExecutionRunID, } } // ToTerminateWorkflowExecutionRequest converts thrift TerminateWorkflowExecutionRequest type to internal func ToTerminateWorkflowExecutionRequest(t *shared.TerminateWorkflowExecutionRequest) *types.TerminateWorkflowExecutionRequest { if t == nil { return nil } return &types.TerminateWorkflowExecutionRequest{ Domain: t.GetDomain(), WorkflowExecution: ToWorkflowExecution(t.WorkflowExecution), Reason: t.GetReason(), Details: t.Details, Identity: t.GetIdentity(), FirstExecutionRunID: t.GetFirstExecutionRunID(), } } // FromTimeoutType converts internal TimeoutType type to thrift func FromTimeoutType(t *types.TimeoutType) *shared.TimeoutType { if t == nil { return nil } switch *t { case types.TimeoutTypeStartToClose: v := shared.TimeoutTypeStartToClose return &v case types.TimeoutTypeScheduleToStart: v := shared.TimeoutTypeScheduleToStart return &v case types.TimeoutTypeScheduleToClose: v := shared.TimeoutTypeScheduleToClose return &v case types.TimeoutTypeHeartbeat: v := shared.TimeoutTypeHeartbeat return &v } panic("unexpected enum value") } // ToTimeoutType converts thrift TimeoutType type to internal func ToTimeoutType(t *shared.TimeoutType) *types.TimeoutType { if t == nil { return nil } switch *t { case shared.TimeoutTypeStartToClose: v := types.TimeoutTypeStartToClose return &v case shared.TimeoutTypeScheduleToStart: v := types.TimeoutTypeScheduleToStart return &v case shared.TimeoutTypeScheduleToClose: v := types.TimeoutTypeScheduleToClose return &v case shared.TimeoutTypeHeartbeat: v := types.TimeoutTypeHeartbeat return &v } panic("unexpected enum value") } // FromTimerCanceledEventAttributes converts internal TimerCanceledEventAttributes type to thrift func FromTimerCanceledEventAttributes(t *types.TimerCanceledEventAttributes) *shared.TimerCanceledEventAttributes { if t == nil { return nil } return &shared.TimerCanceledEventAttributes{ TimerId: &t.TimerID, StartedEventId: &t.StartedEventID, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, Identity: &t.Identity, } } // ToTimerCanceledEventAttributes converts thrift TimerCanceledEventAttributes type to internal func ToTimerCanceledEventAttributes(t *shared.TimerCanceledEventAttributes) *types.TimerCanceledEventAttributes { if t == nil { return nil } return &types.TimerCanceledEventAttributes{ TimerID: t.GetTimerId(), StartedEventID: t.GetStartedEventId(), DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), Identity: t.GetIdentity(), } } // FromTimerFiredEventAttributes converts internal TimerFiredEventAttributes type to thrift func FromTimerFiredEventAttributes(t *types.TimerFiredEventAttributes) *shared.TimerFiredEventAttributes { if t == nil { return nil } return &shared.TimerFiredEventAttributes{ TimerId: &t.TimerID, StartedEventId: &t.StartedEventID, } } // ToTimerFiredEventAttributes converts thrift TimerFiredEventAttributes type to internal func ToTimerFiredEventAttributes(t *shared.TimerFiredEventAttributes) *types.TimerFiredEventAttributes { if t == nil { return nil } return &types.TimerFiredEventAttributes{ TimerID: t.GetTimerId(), StartedEventID: t.GetStartedEventId(), } } // FromTimerStartedEventAttributes converts internal TimerStartedEventAttributes type to thrift func FromTimerStartedEventAttributes(t *types.TimerStartedEventAttributes) *shared.TimerStartedEventAttributes { if t == nil { return nil } return &shared.TimerStartedEventAttributes{ TimerId: &t.TimerID, StartToFireTimeoutSeconds: t.StartToFireTimeoutSeconds, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, } } // ToTimerStartedEventAttributes converts thrift TimerStartedEventAttributes type to internal func ToTimerStartedEventAttributes(t *shared.TimerStartedEventAttributes) *types.TimerStartedEventAttributes { if t == nil { return nil } return &types.TimerStartedEventAttributes{ TimerID: t.GetTimerId(), StartToFireTimeoutSeconds: t.StartToFireTimeoutSeconds, DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), } } // FromTransientDecisionInfo converts internal TransientDecisionInfo type to thrift func FromTransientDecisionInfo(t *types.TransientDecisionInfo) *shared.TransientDecisionInfo { if t == nil { return nil } return &shared.TransientDecisionInfo{ ScheduledEvent: FromHistoryEvent(t.ScheduledEvent), StartedEvent: FromHistoryEvent(t.StartedEvent), } } // ToTransientDecisionInfo converts thrift TransientDecisionInfo type to internal func ToTransientDecisionInfo(t *shared.TransientDecisionInfo) *types.TransientDecisionInfo { if t == nil { return nil } return &types.TransientDecisionInfo{ ScheduledEvent: ToHistoryEvent(t.ScheduledEvent), StartedEvent: ToHistoryEvent(t.StartedEvent), } } // FromUpdateDomainRequest converts internal UpdateDomainRequest type to thrift func FromUpdateDomainRequest(t *types.UpdateDomainRequest) *shared.UpdateDomainRequest { if t == nil { return nil } request := shared.UpdateDomainRequest{ Name: &t.Name, SecurityToken: &t.SecurityToken, DeleteBadBinary: t.DeleteBadBinary, FailoverTimeoutInSeconds: t.FailoverTimeoutInSeconds, } if t.Description != nil || t.OwnerEmail != nil || t.Data != nil { request.UpdatedInfo = &shared.UpdateDomainInfo{ Description: t.Description, OwnerEmail: t.OwnerEmail, Data: t.Data, } } if t.WorkflowExecutionRetentionPeriodInDays != nil || t.EmitMetric != nil || t.BadBinaries != nil || t.HistoryArchivalStatus != nil || t.HistoryArchivalURI != nil || t.VisibilityArchivalStatus != nil || t.VisibilityArchivalURI != nil { request.Configuration = &shared.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: t.WorkflowExecutionRetentionPeriodInDays, EmitMetric: t.EmitMetric, BadBinaries: FromBadBinaries(t.BadBinaries), HistoryArchivalStatus: FromArchivalStatus(t.HistoryArchivalStatus), HistoryArchivalURI: t.HistoryArchivalURI, VisibilityArchivalStatus: FromArchivalStatus(t.VisibilityArchivalStatus), VisibilityArchivalURI: t.VisibilityArchivalURI, } } if t.ActiveClusterName != nil || t.Clusters != nil || t.ActiveClusters != nil { request.ReplicationConfiguration = &shared.DomainReplicationConfiguration{ ActiveClusterName: t.ActiveClusterName, Clusters: FromClusterReplicationConfigurationArray(t.Clusters), ActiveClusters: FromActiveClusters(t.ActiveClusters), } } return &request } // ToUpdateDomainRequest converts thrift UpdateDomainRequest type to internal func ToUpdateDomainRequest(t *shared.UpdateDomainRequest) *types.UpdateDomainRequest { if t == nil { return nil } request := types.UpdateDomainRequest{ Name: t.GetName(), SecurityToken: t.GetSecurityToken(), DeleteBadBinary: t.DeleteBadBinary, FailoverTimeoutInSeconds: t.FailoverTimeoutInSeconds, } if t.UpdatedInfo != nil { request.Description = t.UpdatedInfo.Description request.OwnerEmail = t.UpdatedInfo.OwnerEmail request.Data = t.UpdatedInfo.Data } if t.Configuration != nil { request.WorkflowExecutionRetentionPeriodInDays = t.Configuration.WorkflowExecutionRetentionPeriodInDays request.EmitMetric = t.Configuration.EmitMetric request.BadBinaries = ToBadBinaries(t.Configuration.BadBinaries) request.HistoryArchivalStatus = ToArchivalStatus(t.Configuration.HistoryArchivalStatus) request.HistoryArchivalURI = t.Configuration.HistoryArchivalURI request.VisibilityArchivalStatus = ToArchivalStatus(t.Configuration.VisibilityArchivalStatus) request.VisibilityArchivalURI = t.Configuration.VisibilityArchivalURI } if t.ReplicationConfiguration != nil { request.ActiveClusterName = t.ReplicationConfiguration.ActiveClusterName request.Clusters = ToClusterReplicationConfigurationArray(t.ReplicationConfiguration.Clusters) request.ActiveClusters = ToActiveClusters(t.ReplicationConfiguration.ActiveClusters) } return &request } // FromUpdateDomainResponse converts internal UpdateDomainResponse type to thrift func FromUpdateDomainResponse(t *types.UpdateDomainResponse) *shared.UpdateDomainResponse { if t == nil { return nil } return &shared.UpdateDomainResponse{ DomainInfo: FromDomainInfo(t.DomainInfo), Configuration: FromDomainConfiguration(t.Configuration), ReplicationConfiguration: FromDomainReplicationConfiguration(t.ReplicationConfiguration), FailoverVersion: &t.FailoverVersion, IsGlobalDomain: &t.IsGlobalDomain, } } // ToUpdateDomainResponse converts thrift UpdateDomainResponse type to internal func ToUpdateDomainResponse(t *shared.UpdateDomainResponse) *types.UpdateDomainResponse { if t == nil { return nil } return &types.UpdateDomainResponse{ DomainInfo: ToDomainInfo(t.DomainInfo), Configuration: ToDomainConfiguration(t.Configuration), ReplicationConfiguration: ToDomainReplicationConfiguration(t.ReplicationConfiguration), FailoverVersion: t.GetFailoverVersion(), IsGlobalDomain: t.GetIsGlobalDomain(), } } // FromUpsertWorkflowSearchAttributesDecisionAttributes converts internal UpsertWorkflowSearchAttributesDecisionAttributes type to thrift func FromUpsertWorkflowSearchAttributesDecisionAttributes(t *types.UpsertWorkflowSearchAttributesDecisionAttributes) *shared.UpsertWorkflowSearchAttributesDecisionAttributes { if t == nil { return nil } return &shared.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: FromSearchAttributes(t.SearchAttributes), } } // ToUpsertWorkflowSearchAttributesDecisionAttributes converts thrift UpsertWorkflowSearchAttributesDecisionAttributes type to internal func ToUpsertWorkflowSearchAttributesDecisionAttributes(t *shared.UpsertWorkflowSearchAttributesDecisionAttributes) *types.UpsertWorkflowSearchAttributesDecisionAttributes { if t == nil { return nil } return &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: ToSearchAttributes(t.SearchAttributes), } } // FromUpsertWorkflowSearchAttributesEventAttributes converts internal UpsertWorkflowSearchAttributesEventAttributes type to thrift func FromUpsertWorkflowSearchAttributesEventAttributes(t *types.UpsertWorkflowSearchAttributesEventAttributes) *shared.UpsertWorkflowSearchAttributesEventAttributes { if t == nil { return nil } return &shared.UpsertWorkflowSearchAttributesEventAttributes{ DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, SearchAttributes: FromSearchAttributes(t.SearchAttributes), } } // ToUpsertWorkflowSearchAttributesEventAttributes converts thrift UpsertWorkflowSearchAttributesEventAttributes type to internal func ToUpsertWorkflowSearchAttributesEventAttributes(t *shared.UpsertWorkflowSearchAttributesEventAttributes) *types.UpsertWorkflowSearchAttributesEventAttributes { if t == nil { return nil } return &types.UpsertWorkflowSearchAttributesEventAttributes{ DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), SearchAttributes: ToSearchAttributes(t.SearchAttributes), } } // FromVersionHistories converts internal VersionHistories type to thrift func FromVersionHistories(t *types.VersionHistories) *shared.VersionHistories { if t == nil { return nil } return &shared.VersionHistories{ CurrentVersionHistoryIndex: &t.CurrentVersionHistoryIndex, Histories: FromVersionHistoryArray(t.Histories), } } // ToVersionHistories converts thrift VersionHistories type to internal func ToVersionHistories(t *shared.VersionHistories) *types.VersionHistories { if t == nil { return nil } return &types.VersionHistories{ CurrentVersionHistoryIndex: t.GetCurrentVersionHistoryIndex(), Histories: ToVersionHistoryArray(t.Histories), } } // FromVersionHistory converts internal VersionHistory type to thrift func FromVersionHistory(t *types.VersionHistory) *shared.VersionHistory { if t == nil { return nil } return &shared.VersionHistory{ BranchToken: t.BranchToken, Items: FromVersionHistoryItemArray(t.Items), } } // ToVersionHistory converts thrift VersionHistory type to internal func ToVersionHistory(t *shared.VersionHistory) *types.VersionHistory { if t == nil { return nil } return &types.VersionHistory{ BranchToken: t.BranchToken, Items: ToVersionHistoryItemArray(t.Items), } } // FromVersionHistoryItem converts internal VersionHistoryItem type to thrift func FromVersionHistoryItem(t *types.VersionHistoryItem) *shared.VersionHistoryItem { if t == nil { return nil } return &shared.VersionHistoryItem{ EventID: &t.EventID, Version: &t.Version, } } // ToVersionHistoryItem converts thrift VersionHistoryItem type to internal func ToVersionHistoryItem(t *shared.VersionHistoryItem) *types.VersionHistoryItem { if t == nil { return nil } return &types.VersionHistoryItem{ EventID: t.GetEventID(), Version: t.GetVersion(), } } // FromWorkerVersionInfo converts internal WorkerVersionInfo type to thrift func FromWorkerVersionInfo(t *types.WorkerVersionInfo) *shared.WorkerVersionInfo { if t == nil { return nil } return &shared.WorkerVersionInfo{ Impl: &t.Impl, FeatureVersion: &t.FeatureVersion, } } // ToWorkerVersionInfo converts thrift WorkerVersionInfo type to internal func ToWorkerVersionInfo(t *shared.WorkerVersionInfo) *types.WorkerVersionInfo { if t == nil { return nil } return &types.WorkerVersionInfo{ Impl: t.GetImpl(), FeatureVersion: t.GetFeatureVersion(), } } // FromWorkflowExecution converts internal WorkflowExecution type to thrift func FromWorkflowExecution(t *types.WorkflowExecution) *shared.WorkflowExecution { if t == nil { return nil } return &shared.WorkflowExecution{ WorkflowId: &t.WorkflowID, RunId: &t.RunID, } } // ToWorkflowExecution converts thrift WorkflowExecution type to internal func ToWorkflowExecution(t *shared.WorkflowExecution) *types.WorkflowExecution { if t == nil { return nil } return &types.WorkflowExecution{ WorkflowID: t.GetWorkflowId(), RunID: t.GetRunId(), } } // FromWorkflowExecutionAlreadyStartedError converts internal WorkflowExecutionAlreadyStartedError type to thrift func FromWorkflowExecutionAlreadyStartedError(t *types.WorkflowExecutionAlreadyStartedError) *shared.WorkflowExecutionAlreadyStartedError { if t == nil { return nil } return &shared.WorkflowExecutionAlreadyStartedError{ Message: &t.Message, StartRequestId: &t.StartRequestID, RunId: &t.RunID, } } // ToWorkflowExecutionAlreadyStartedError converts thrift WorkflowExecutionAlreadyStartedError type to internal func ToWorkflowExecutionAlreadyStartedError(t *shared.WorkflowExecutionAlreadyStartedError) *types.WorkflowExecutionAlreadyStartedError { if t == nil { return nil } return &types.WorkflowExecutionAlreadyStartedError{ Message: t.GetMessage(), StartRequestID: t.GetStartRequestId(), RunID: t.GetRunId(), } } // FromWorkflowExecutionCancelRequestedEventAttributes converts internal WorkflowExecutionCancelRequestedEventAttributes type to thrift func FromWorkflowExecutionCancelRequestedEventAttributes(t *types.WorkflowExecutionCancelRequestedEventAttributes) *shared.WorkflowExecutionCancelRequestedEventAttributes { if t == nil { return nil } return &shared.WorkflowExecutionCancelRequestedEventAttributes{ Cause: &t.Cause, ExternalInitiatedEventId: t.ExternalInitiatedEventID, ExternalWorkflowExecution: FromWorkflowExecution(t.ExternalWorkflowExecution), Identity: &t.Identity, RequestId: &t.RequestID, } } // ToWorkflowExecutionCancelRequestedEventAttributes converts thrift WorkflowExecutionCancelRequestedEventAttributes type to internal func ToWorkflowExecutionCancelRequestedEventAttributes(t *shared.WorkflowExecutionCancelRequestedEventAttributes) *types.WorkflowExecutionCancelRequestedEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionCancelRequestedEventAttributes{ Cause: t.GetCause(), ExternalInitiatedEventID: t.ExternalInitiatedEventId, ExternalWorkflowExecution: ToWorkflowExecution(t.ExternalWorkflowExecution), Identity: t.GetIdentity(), RequestID: t.GetRequestId(), } } // FromWorkflowExecutionCanceledEventAttributes converts internal WorkflowExecutionCanceledEventAttributes type to thrift func FromWorkflowExecutionCanceledEventAttributes(t *types.WorkflowExecutionCanceledEventAttributes) *shared.WorkflowExecutionCanceledEventAttributes { if t == nil { return nil } return &shared.WorkflowExecutionCanceledEventAttributes{ DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, Details: t.Details, } } // ToWorkflowExecutionCanceledEventAttributes converts thrift WorkflowExecutionCanceledEventAttributes type to internal func ToWorkflowExecutionCanceledEventAttributes(t *shared.WorkflowExecutionCanceledEventAttributes) *types.WorkflowExecutionCanceledEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionCanceledEventAttributes{ DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), Details: t.Details, } } // FromWorkflowExecutionCloseStatus converts internal WorkflowExecutionCloseStatus type to thrift func FromWorkflowExecutionCloseStatus(t *types.WorkflowExecutionCloseStatus) *shared.WorkflowExecutionCloseStatus { if t == nil { return nil } switch *t { case types.WorkflowExecutionCloseStatusCompleted: v := shared.WorkflowExecutionCloseStatusCompleted return &v case types.WorkflowExecutionCloseStatusFailed: v := shared.WorkflowExecutionCloseStatusFailed return &v case types.WorkflowExecutionCloseStatusCanceled: v := shared.WorkflowExecutionCloseStatusCanceled return &v case types.WorkflowExecutionCloseStatusTerminated: v := shared.WorkflowExecutionCloseStatusTerminated return &v case types.WorkflowExecutionCloseStatusContinuedAsNew: v := shared.WorkflowExecutionCloseStatusContinuedAsNew return &v case types.WorkflowExecutionCloseStatusTimedOut: v := shared.WorkflowExecutionCloseStatusTimedOut return &v } panic("unexpected enum value") } // ToWorkflowExecutionCloseStatus converts thrift WorkflowExecutionCloseStatus type to internal func ToWorkflowExecutionCloseStatus(t *shared.WorkflowExecutionCloseStatus) *types.WorkflowExecutionCloseStatus { if t == nil { return nil } switch *t { case shared.WorkflowExecutionCloseStatusCompleted: v := types.WorkflowExecutionCloseStatusCompleted return &v case shared.WorkflowExecutionCloseStatusFailed: v := types.WorkflowExecutionCloseStatusFailed return &v case shared.WorkflowExecutionCloseStatusCanceled: v := types.WorkflowExecutionCloseStatusCanceled return &v case shared.WorkflowExecutionCloseStatusTerminated: v := types.WorkflowExecutionCloseStatusTerminated return &v case shared.WorkflowExecutionCloseStatusContinuedAsNew: v := types.WorkflowExecutionCloseStatusContinuedAsNew return &v case shared.WorkflowExecutionCloseStatusTimedOut: v := types.WorkflowExecutionCloseStatusTimedOut return &v } panic("unexpected enum value") } // FromWorkflowExecutionStatus converts internal WorkflowExecutionStatus type to thrift func FromWorkflowExecutionStatus(t *types.WorkflowExecutionStatus) *shared.WorkflowExecutionStatus { if t == nil { return nil } switch *t { case types.WorkflowExecutionStatusPending: v := shared.WorkflowExecutionStatusPending return &v case types.WorkflowExecutionStatusStarted: v := shared.WorkflowExecutionStatusStarted return &v case types.WorkflowExecutionStatusCompleted: v := shared.WorkflowExecutionStatusCompleted return &v case types.WorkflowExecutionStatusFailed: v := shared.WorkflowExecutionStatusFailed return &v case types.WorkflowExecutionStatusCanceled: v := shared.WorkflowExecutionStatusCanceled return &v case types.WorkflowExecutionStatusTerminated: v := shared.WorkflowExecutionStatusTerminated return &v case types.WorkflowExecutionStatusContinuedAsNew: v := shared.WorkflowExecutionStatusContinuedAsNew return &v case types.WorkflowExecutionStatusTimedOut: v := shared.WorkflowExecutionStatusTimedOut return &v } panic("unexpected enum value") } // ToWorkflowExecutionStatus converts thrift WorkflowExecutionStatus type to internal func ToWorkflowExecutionStatus(t *shared.WorkflowExecutionStatus) *types.WorkflowExecutionStatus { if t == nil { return nil } switch *t { case shared.WorkflowExecutionStatusPending: v := types.WorkflowExecutionStatusPending return &v case shared.WorkflowExecutionStatusStarted: v := types.WorkflowExecutionStatusStarted return &v case shared.WorkflowExecutionStatusCompleted: v := types.WorkflowExecutionStatusCompleted return &v case shared.WorkflowExecutionStatusFailed: v := types.WorkflowExecutionStatusFailed return &v case shared.WorkflowExecutionStatusCanceled: v := types.WorkflowExecutionStatusCanceled return &v case shared.WorkflowExecutionStatusTerminated: v := types.WorkflowExecutionStatusTerminated return &v case shared.WorkflowExecutionStatusContinuedAsNew: v := types.WorkflowExecutionStatusContinuedAsNew return &v case shared.WorkflowExecutionStatusTimedOut: v := types.WorkflowExecutionStatusTimedOut return &v } panic("unexpected enum value") } // FromWorkflowExecutionCompletedEventAttributes converts internal WorkflowExecutionCompletedEventAttributes type to thrift func FromWorkflowExecutionCompletedEventAttributes(t *types.WorkflowExecutionCompletedEventAttributes) *shared.WorkflowExecutionCompletedEventAttributes { if t == nil { return nil } return &shared.WorkflowExecutionCompletedEventAttributes{ Result: t.Result, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, } } // ToWorkflowExecutionCompletedEventAttributes converts thrift WorkflowExecutionCompletedEventAttributes type to internal func ToWorkflowExecutionCompletedEventAttributes(t *shared.WorkflowExecutionCompletedEventAttributes) *types.WorkflowExecutionCompletedEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionCompletedEventAttributes{ Result: t.Result, DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), } } // FromWorkflowExecutionConfiguration converts internal WorkflowExecutionConfiguration type to thrift func FromWorkflowExecutionConfiguration(t *types.WorkflowExecutionConfiguration) *shared.WorkflowExecutionConfiguration { if t == nil { return nil } return &shared.WorkflowExecutionConfiguration{ TaskList: FromTaskList(t.TaskList), ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, } } // ToWorkflowExecutionConfiguration converts thrift WorkflowExecutionConfiguration type to internal func ToWorkflowExecutionConfiguration(t *shared.WorkflowExecutionConfiguration) *types.WorkflowExecutionConfiguration { if t == nil { return nil } return &types.WorkflowExecutionConfiguration{ TaskList: ToTaskList(t.TaskList), ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, } } // FromWorkflowExecutionContinuedAsNewEventAttributes converts internal WorkflowExecutionContinuedAsNewEventAttributes type to thrift func FromWorkflowExecutionContinuedAsNewEventAttributes(t *types.WorkflowExecutionContinuedAsNewEventAttributes) *shared.WorkflowExecutionContinuedAsNewEventAttributes { if t == nil { return nil } return &shared.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunId: &t.NewExecutionRunID, WorkflowType: FromWorkflowType(t.WorkflowType), TaskList: FromTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, BackoffStartIntervalInSeconds: t.BackoffStartIntervalInSeconds, Initiator: FromContinueAsNewInitiator(t.Initiator), FailureReason: t.FailureReason, FailureDetails: t.FailureDetails, LastCompletionResult: t.LastCompletionResult, Header: FromHeader(t.Header), Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), } } // ToWorkflowExecutionContinuedAsNewEventAttributes converts thrift WorkflowExecutionContinuedAsNewEventAttributes type to internal func ToWorkflowExecutionContinuedAsNewEventAttributes(t *shared.WorkflowExecutionContinuedAsNewEventAttributes) *types.WorkflowExecutionContinuedAsNewEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: t.GetNewExecutionRunId(), WorkflowType: ToWorkflowType(t.WorkflowType), TaskList: ToTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), BackoffStartIntervalInSeconds: t.BackoffStartIntervalInSeconds, Initiator: ToContinueAsNewInitiator(t.Initiator), FailureReason: t.FailureReason, FailureDetails: t.FailureDetails, LastCompletionResult: t.LastCompletionResult, Header: ToHeader(t.Header), Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), } } // FromWorkflowExecutionFailedEventAttributes converts internal WorkflowExecutionFailedEventAttributes type to thrift func FromWorkflowExecutionFailedEventAttributes(t *types.WorkflowExecutionFailedEventAttributes) *shared.WorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &shared.WorkflowExecutionFailedEventAttributes{ Reason: t.Reason, Details: t.Details, DecisionTaskCompletedEventId: &t.DecisionTaskCompletedEventID, } } // ToWorkflowExecutionFailedEventAttributes converts thrift WorkflowExecutionFailedEventAttributes type to internal func ToWorkflowExecutionFailedEventAttributes(t *shared.WorkflowExecutionFailedEventAttributes) *types.WorkflowExecutionFailedEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionFailedEventAttributes{ Reason: t.Reason, Details: t.Details, DecisionTaskCompletedEventID: t.GetDecisionTaskCompletedEventId(), } } // FromWorkflowExecutionFilter converts internal WorkflowExecutionFilter type to thrift func FromWorkflowExecutionFilter(t *types.WorkflowExecutionFilter) *shared.WorkflowExecutionFilter { if t == nil { return nil } return &shared.WorkflowExecutionFilter{ WorkflowId: &t.WorkflowID, RunId: &t.RunID, } } // ToWorkflowExecutionFilter converts thrift WorkflowExecutionFilter type to internal func ToWorkflowExecutionFilter(t *shared.WorkflowExecutionFilter) *types.WorkflowExecutionFilter { if t == nil { return nil } return &types.WorkflowExecutionFilter{ WorkflowID: t.GetWorkflowId(), RunID: t.GetRunId(), } } // FromWorkflowExecutionInfo converts internal WorkflowExecutionInfo type to thrift func FromWorkflowExecutionInfo(t *types.WorkflowExecutionInfo) *shared.WorkflowExecutionInfo { if t == nil { return nil } tlName := "" if t.TaskList != nil { tlName = t.TaskList.Name } return &shared.WorkflowExecutionInfo{ Execution: FromWorkflowExecution(t.Execution), Type: FromWorkflowType(t.Type), StartTime: t.StartTime, CloseTime: t.CloseTime, CloseStatus: FromWorkflowExecutionCloseStatus(t.CloseStatus), HistoryLength: &t.HistoryLength, ParentDomainId: t.ParentDomainID, ParentDomainName: t.ParentDomain, ParentInitatedId: t.ParentInitiatedID, ParentExecution: FromWorkflowExecution(t.ParentExecution), ExecutionTime: t.ExecutionTime, Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), AutoResetPoints: FromResetPoints(t.AutoResetPoints), TaskList: &tlName, TaskListInfo: FromTaskList(t.TaskList), IsCron: &t.IsCron, UpdateTime: t.UpdateTime, PartitionConfig: t.PartitionConfig, CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronSchedule: t.CronSchedule, ExecutionStatus: FromWorkflowExecutionStatus(t.ExecutionStatus), ScheduledExecutionTime: t.ScheduledExecutionTime, } } // ToWorkflowExecutionInfo converts thrift WorkflowExecutionInfo type to internal func ToWorkflowExecutionInfo(t *shared.WorkflowExecutionInfo) *types.WorkflowExecutionInfo { if t == nil { return nil } return &types.WorkflowExecutionInfo{ Execution: ToWorkflowExecution(t.Execution), Type: ToWorkflowType(t.Type), StartTime: t.StartTime, CloseTime: t.CloseTime, CloseStatus: ToWorkflowExecutionCloseStatus(t.CloseStatus), HistoryLength: t.GetHistoryLength(), ParentDomainID: t.ParentDomainId, ParentDomain: t.ParentDomainName, ParentInitiatedID: t.ParentInitatedId, ParentExecution: ToWorkflowExecution(t.ParentExecution), ExecutionTime: t.ExecutionTime, Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), AutoResetPoints: ToResetPoints(t.AutoResetPoints), TaskList: MigrateTaskList(t.GetTaskList(), t.TaskListInfo), IsCron: t.GetIsCron(), UpdateTime: t.UpdateTime, PartitionConfig: t.PartitionConfig, CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronSchedule: t.CronSchedule, ExecutionStatus: ToWorkflowExecutionStatus(t.ExecutionStatus), ScheduledExecutionTime: t.ScheduledExecutionTime, } } // FromWorkflowExecutionSignaledEventAttributes converts internal WorkflowExecutionSignaledEventAttributes type to thrift func FromWorkflowExecutionSignaledEventAttributes(t *types.WorkflowExecutionSignaledEventAttributes) *shared.WorkflowExecutionSignaledEventAttributes { if t == nil { return nil } return &shared.WorkflowExecutionSignaledEventAttributes{ SignalName: &t.SignalName, Input: t.Input, Identity: &t.Identity, RequestId: &t.RequestID, } } // ToWorkflowExecutionSignaledEventAttributes converts thrift WorkflowExecutionSignaledEventAttributes type to internal func ToWorkflowExecutionSignaledEventAttributes(t *shared.WorkflowExecutionSignaledEventAttributes) *types.WorkflowExecutionSignaledEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionSignaledEventAttributes{ SignalName: t.GetSignalName(), Input: t.Input, Identity: t.GetIdentity(), RequestID: t.GetRequestId(), } } // FromWorkflowExecutionStartedEventAttributes converts internal WorkflowExecutionStartedEventAttributes type to thrift func FromWorkflowExecutionStartedEventAttributes(t *types.WorkflowExecutionStartedEventAttributes) *shared.WorkflowExecutionStartedEventAttributes { if t == nil { return nil } return &shared.WorkflowExecutionStartedEventAttributes{ WorkflowType: FromWorkflowType(t.WorkflowType), ParentWorkflowDomain: t.ParentWorkflowDomain, ParentWorkflowExecution: FromWorkflowExecution(t.ParentWorkflowExecution), ParentInitiatedEventId: t.ParentInitiatedEventID, TaskList: FromTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, ContinuedExecutionRunId: &t.ContinuedExecutionRunID, Initiator: FromContinueAsNewInitiator(t.Initiator), ContinuedFailureReason: t.ContinuedFailureReason, ContinuedFailureDetails: t.ContinuedFailureDetails, LastCompletionResult: t.LastCompletionResult, OriginalExecutionRunId: &t.OriginalExecutionRunID, Identity: &t.Identity, FirstExecutionRunId: &t.FirstExecutionRunID, FirstScheduledTimeNano: timeToNano(t.FirstScheduleTime), RetryPolicy: FromRetryPolicy(t.RetryPolicy), Attempt: &t.Attempt, ExpirationTimestamp: t.ExpirationTimestamp, CronSchedule: &t.CronSchedule, FirstDecisionTaskBackoffSeconds: t.FirstDecisionTaskBackoffSeconds, Memo: FromMemo(t.Memo), SearchAttributes: FromSearchAttributes(t.SearchAttributes), PrevAutoResetPoints: FromResetPoints(t.PrevAutoResetPoints), Header: FromHeader(t.Header), PartitionConfig: t.PartitionConfig, RequestId: &t.RequestID, ActiveClusterSelectionPolicy: FromActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronOverlapPolicy: FromCronOverlapPolicy(t.CronOverlapPolicy), } } // ToWorkflowExecutionStartedEventAttributes converts thrift WorkflowExecutionStartedEventAttributes type to internal func ToWorkflowExecutionStartedEventAttributes(t *shared.WorkflowExecutionStartedEventAttributes) *types.WorkflowExecutionStartedEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: ToWorkflowType(t.WorkflowType), ParentWorkflowDomain: t.ParentWorkflowDomain, ParentWorkflowExecution: ToWorkflowExecution(t.ParentWorkflowExecution), ParentInitiatedEventID: t.ParentInitiatedEventId, TaskList: ToTaskList(t.TaskList), Input: t.Input, ExecutionStartToCloseTimeoutSeconds: t.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: t.TaskStartToCloseTimeoutSeconds, ContinuedExecutionRunID: t.GetContinuedExecutionRunId(), Initiator: ToContinueAsNewInitiator(t.Initiator), ContinuedFailureReason: t.ContinuedFailureReason, ContinuedFailureDetails: t.ContinuedFailureDetails, LastCompletionResult: t.LastCompletionResult, OriginalExecutionRunID: t.GetOriginalExecutionRunId(), Identity: t.GetIdentity(), FirstExecutionRunID: t.GetFirstExecutionRunId(), FirstScheduleTime: nanoToTime(t.FirstScheduledTimeNano), RetryPolicy: ToRetryPolicy(t.RetryPolicy), Attempt: t.GetAttempt(), ExpirationTimestamp: t.ExpirationTimestamp, CronSchedule: t.GetCronSchedule(), FirstDecisionTaskBackoffSeconds: t.FirstDecisionTaskBackoffSeconds, Memo: ToMemo(t.Memo), SearchAttributes: ToSearchAttributes(t.SearchAttributes), PrevAutoResetPoints: ToResetPoints(t.PrevAutoResetPoints), Header: ToHeader(t.Header), PartitionConfig: t.PartitionConfig, RequestID: t.GetRequestId(), ActiveClusterSelectionPolicy: ToActiveClusterSelectionPolicy(t.ActiveClusterSelectionPolicy), CronOverlapPolicy: ToCronOverlapPolicy(t.CronOverlapPolicy), } } func FromClusterAttribute(t *types.ClusterAttribute) *shared.ClusterAttribute { if t == nil { return nil } return &shared.ClusterAttribute{ Scope: &t.Scope, Name: &t.Name, } } func ToClusterAttribute(t *shared.ClusterAttribute) *types.ClusterAttribute { if t == nil { return nil } return &types.ClusterAttribute{ Scope: t.GetScope(), Name: t.GetName(), } } func FromActiveClusterSelectionPolicy(t *types.ActiveClusterSelectionPolicy) *shared.ActiveClusterSelectionPolicy { if t == nil { return nil } return &shared.ActiveClusterSelectionPolicy{ Strategy: FromActiveClusterSelectionStrategy(t.ActiveClusterSelectionStrategy), ExternalEntityType: &t.ExternalEntityType, ExternalEntityKey: &t.ExternalEntityKey, StickyRegion: &t.StickyRegion, ClusterAttribute: FromClusterAttribute(t.ClusterAttribute), } } func ToActiveClusterSelectionPolicy(t *shared.ActiveClusterSelectionPolicy) *types.ActiveClusterSelectionPolicy { if t == nil { return nil } return &types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: ToActiveClusterSelectionStrategy(t.Strategy), ExternalEntityType: t.GetExternalEntityType(), ExternalEntityKey: t.GetExternalEntityKey(), StickyRegion: t.GetStickyRegion(), ClusterAttribute: ToClusterAttribute(t.ClusterAttribute), } } func FromActiveClusterSelectionStrategy(t *types.ActiveClusterSelectionStrategy) *shared.ActiveClusterSelectionStrategy { if t == nil { return nil } switch *t { case types.ActiveClusterSelectionStrategyRegionSticky: return shared.ActiveClusterSelectionStrategyRegionSticky.Ptr() case types.ActiveClusterSelectionStrategyExternalEntity: return shared.ActiveClusterSelectionStrategyExternalEntity.Ptr() } panic("unexpected enum value") } func ToActiveClusterSelectionStrategy(t *shared.ActiveClusterSelectionStrategy) *types.ActiveClusterSelectionStrategy { if t == nil { return nil } switch *t { case shared.ActiveClusterSelectionStrategyRegionSticky: return types.ActiveClusterSelectionStrategyRegionSticky.Ptr() case shared.ActiveClusterSelectionStrategyExternalEntity: return types.ActiveClusterSelectionStrategyExternalEntity.Ptr() } panic("unexpected enum value") } // FromWorkflowExecutionTerminatedEventAttributes converts internal WorkflowExecutionTerminatedEventAttributes type to thrift func FromWorkflowExecutionTerminatedEventAttributes(t *types.WorkflowExecutionTerminatedEventAttributes) *shared.WorkflowExecutionTerminatedEventAttributes { if t == nil { return nil } return &shared.WorkflowExecutionTerminatedEventAttributes{ Reason: &t.Reason, Details: t.Details, Identity: &t.Identity, } } // ToWorkflowExecutionTerminatedEventAttributes converts thrift WorkflowExecutionTerminatedEventAttributes type to internal func ToWorkflowExecutionTerminatedEventAttributes(t *shared.WorkflowExecutionTerminatedEventAttributes) *types.WorkflowExecutionTerminatedEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionTerminatedEventAttributes{ Reason: t.GetReason(), Details: t.Details, Identity: t.GetIdentity(), } } // FromWorkflowExecutionTimedOutEventAttributes converts internal WorkflowExecutionTimedOutEventAttributes type to thrift func FromWorkflowExecutionTimedOutEventAttributes(t *types.WorkflowExecutionTimedOutEventAttributes) *shared.WorkflowExecutionTimedOutEventAttributes { if t == nil { return nil } return &shared.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: FromTimeoutType(t.TimeoutType), } } // ToWorkflowExecutionTimedOutEventAttributes converts thrift WorkflowExecutionTimedOutEventAttributes type to internal func ToWorkflowExecutionTimedOutEventAttributes(t *shared.WorkflowExecutionTimedOutEventAttributes) *types.WorkflowExecutionTimedOutEventAttributes { if t == nil { return nil } return &types.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: ToTimeoutType(t.TimeoutType), } } // FromWorkflowIDReusePolicy converts internal WorkflowIDReusePolicy type to thrift func FromWorkflowIDReusePolicy(t *types.WorkflowIDReusePolicy) *shared.WorkflowIdReusePolicy { if t == nil { return nil } switch *t { case types.WorkflowIDReusePolicyAllowDuplicateFailedOnly: v := shared.WorkflowIdReusePolicyAllowDuplicateFailedOnly return &v case types.WorkflowIDReusePolicyAllowDuplicate: v := shared.WorkflowIdReusePolicyAllowDuplicate return &v case types.WorkflowIDReusePolicyRejectDuplicate: v := shared.WorkflowIdReusePolicyRejectDuplicate return &v case types.WorkflowIDReusePolicyTerminateIfRunning: v := shared.WorkflowIdReusePolicyTerminateIfRunning return &v } panic("unexpected enum value") } // ToWorkflowIDReusePolicy converts thrift WorkflowIdReusePolicy type to internal func ToWorkflowIDReusePolicy(t *shared.WorkflowIdReusePolicy) *types.WorkflowIDReusePolicy { if t == nil { return nil } switch *t { case shared.WorkflowIdReusePolicyAllowDuplicateFailedOnly: v := types.WorkflowIDReusePolicyAllowDuplicateFailedOnly return &v case shared.WorkflowIdReusePolicyAllowDuplicate: v := types.WorkflowIDReusePolicyAllowDuplicate return &v case shared.WorkflowIdReusePolicyRejectDuplicate: v := types.WorkflowIDReusePolicyRejectDuplicate return &v case shared.WorkflowIdReusePolicyTerminateIfRunning: v := types.WorkflowIDReusePolicyTerminateIfRunning return &v } panic("unexpected enum value") } // FromWorkflowQuery converts internal WorkflowQuery type to thrift func FromWorkflowQuery(t *types.WorkflowQuery) *shared.WorkflowQuery { if t == nil { return nil } return &shared.WorkflowQuery{ QueryType: &t.QueryType, QueryArgs: t.QueryArgs, } } // ToWorkflowQuery converts thrift WorkflowQuery type to internal func ToWorkflowQuery(t *shared.WorkflowQuery) *types.WorkflowQuery { if t == nil { return nil } return &types.WorkflowQuery{ QueryType: t.GetQueryType(), QueryArgs: t.QueryArgs, } } // FromWorkflowQueryResult converts internal WorkflowQueryResult type to thrift func FromWorkflowQueryResult(t *types.WorkflowQueryResult) *shared.WorkflowQueryResult { if t == nil { return nil } return &shared.WorkflowQueryResult{ ResultType: FromQueryResultType(t.ResultType), Answer: t.Answer, ErrorMessage: &t.ErrorMessage, } } // ToWorkflowQueryResult converts thrift WorkflowQueryResult type to internal func ToWorkflowQueryResult(t *shared.WorkflowQueryResult) *types.WorkflowQueryResult { if t == nil { return nil } return &types.WorkflowQueryResult{ ResultType: ToQueryResultType(t.ResultType), Answer: t.Answer, ErrorMessage: t.GetErrorMessage(), } } // FromWorkflowType converts internal WorkflowType type to thrift func FromWorkflowType(t *types.WorkflowType) *shared.WorkflowType { if t == nil { return nil } return &shared.WorkflowType{ Name: &t.Name, } } // ToWorkflowType converts thrift WorkflowType type to internal func ToWorkflowType(t *shared.WorkflowType) *types.WorkflowType { if t == nil { return nil } return &types.WorkflowType{ Name: t.GetName(), } } // FromWorkflowTypeFilter converts internal WorkflowTypeFilter type to thrift func FromWorkflowTypeFilter(t *types.WorkflowTypeFilter) *shared.WorkflowTypeFilter { if t == nil { return nil } return &shared.WorkflowTypeFilter{ Name: &t.Name, } } // ToWorkflowTypeFilter converts thrift WorkflowTypeFilter type to internal func ToWorkflowTypeFilter(t *shared.WorkflowTypeFilter) *types.WorkflowTypeFilter { if t == nil { return nil } return &types.WorkflowTypeFilter{ Name: t.GetName(), } } // FromPendingActivityInfoArray converts internal PendingActivityInfo type array to thrift func FromPendingActivityInfoArray(t []*types.PendingActivityInfo) []*shared.PendingActivityInfo { if t == nil { return nil } v := make([]*shared.PendingActivityInfo, len(t)) for i := range t { v[i] = FromPendingActivityInfo(t[i]) } return v } // ToPendingActivityInfoArray converts thrift PendingActivityInfo type array to internal func ToPendingActivityInfoArray(t []*shared.PendingActivityInfo) []*types.PendingActivityInfo { if t == nil { return nil } v := make([]*types.PendingActivityInfo, len(t)) for i := range t { v[i] = ToPendingActivityInfo(t[i]) } return v } // FromHistoryEventArray converts internal HistoryEvent type array to thrift func FromHistoryEventArray(t []*types.HistoryEvent) []*shared.HistoryEvent { if t == nil { return nil } v := make([]*shared.HistoryEvent, len(t)) for i := range t { v[i] = FromHistoryEvent(t[i]) } return v } // ToHistoryEventArray converts thrift HistoryEvent type array to internal func ToHistoryEventArray(t []*shared.HistoryEvent) []*types.HistoryEvent { if t == nil { return nil } v := make([]*types.HistoryEvent, len(t)) for i := range t { v[i] = ToHistoryEvent(t[i]) } return v } // FromWorkflowExecutionInfoArray converts internal WorkflowExecutionInfo type array to thrift func FromWorkflowExecutionInfoArray(t []*types.WorkflowExecutionInfo) []*shared.WorkflowExecutionInfo { if t == nil { return nil } v := make([]*shared.WorkflowExecutionInfo, len(t)) for i := range t { v[i] = FromWorkflowExecutionInfo(t[i]) } return v } // ToWorkflowExecutionInfoArray converts thrift WorkflowExecutionInfo type array to internal func ToWorkflowExecutionInfoArray(t []*shared.WorkflowExecutionInfo) []*types.WorkflowExecutionInfo { if t == nil { return nil } v := make([]*types.WorkflowExecutionInfo, len(t)) for i := range t { v[i] = ToWorkflowExecutionInfo(t[i]) } return v } // FromPollerInfoArray converts internal PollerInfo type array to thrift func FromPollerInfoArray(t []*types.PollerInfo) []*shared.PollerInfo { if t == nil { return nil } v := make([]*shared.PollerInfo, len(t)) for i := range t { v[i] = FromPollerInfo(t[i]) } return v } // ToPollerInfoArray converts thrift PollerInfo type array to internal func ToPollerInfoArray(t []*shared.PollerInfo) []*types.PollerInfo { if t == nil { return nil } v := make([]*types.PollerInfo, len(t)) for i := range t { v[i] = ToPollerInfo(t[i]) } return v } // FromClusterReplicationConfigurationArray converts internal ClusterReplicationConfiguration type array to thrift func FromClusterReplicationConfigurationArray(t []*types.ClusterReplicationConfiguration) []*shared.ClusterReplicationConfiguration { if t == nil { return nil } v := make([]*shared.ClusterReplicationConfiguration, len(t)) for i := range t { v[i] = FromClusterReplicationConfiguration(t[i]) } return v } // ToClusterReplicationConfigurationArray converts thrift ClusterReplicationConfiguration type array to internal func ToClusterReplicationConfigurationArray(t []*shared.ClusterReplicationConfiguration) []*types.ClusterReplicationConfiguration { if t == nil { return nil } v := make([]*types.ClusterReplicationConfiguration, len(t)) for i := range t { v[i] = ToClusterReplicationConfiguration(t[i]) } return v } // FromDataBlobArray converts internal DataBlob type array to thrift func FromDataBlobArray(t []*types.DataBlob) []*shared.DataBlob { if t == nil { return nil } v := make([]*shared.DataBlob, len(t)) for i := range t { v[i] = FromDataBlob(t[i]) } return v } // ToDataBlobArray converts thrift DataBlob type array to internal func ToDataBlobArray(t []*shared.DataBlob) []*types.DataBlob { if t == nil { return nil } v := make([]*types.DataBlob, len(t)) for i := range t { v[i] = ToDataBlob(t[i]) } return v } // FromDescribeDomainResponseArray converts internal DescribeDomainResponse type array to thrift func FromDescribeDomainResponseArray(t []*types.DescribeDomainResponse) []*shared.DescribeDomainResponse { if t == nil { return nil } v := make([]*shared.DescribeDomainResponse, len(t)) for i := range t { v[i] = FromDescribeDomainResponse(t[i]) } return v } // ToDescribeDomainResponseArray converts thrift DescribeDomainResponse type array to internal func ToDescribeDomainResponseArray(t []*shared.DescribeDomainResponse) []*types.DescribeDomainResponse { if t == nil { return nil } v := make([]*types.DescribeDomainResponse, len(t)) for i := range t { v[i] = ToDescribeDomainResponse(t[i]) } return v } // FromDecisionArray converts internal Decision type array to thrift func FromDecisionArray(t []*types.Decision) []*shared.Decision { if t == nil { return nil } v := make([]*shared.Decision, len(t)) for i := range t { v[i] = FromDecision(t[i]) } return v } // ToDecisionArray converts thrift Decision type array to internal func ToDecisionArray(t []*shared.Decision) []*types.Decision { if t == nil { return nil } v := make([]*types.Decision, len(t)) for i := range t { v[i] = ToDecision(t[i]) } return v } // FromVersionHistoryArray converts internal VersionHistory type array to thrift func FromVersionHistoryArray(t []*types.VersionHistory) []*shared.VersionHistory { if t == nil { return nil } v := make([]*shared.VersionHistory, len(t)) for i := range t { v[i] = FromVersionHistory(t[i]) } return v } // ToVersionHistoryArray converts thrift VersionHistory type array to internal func ToVersionHistoryArray(t []*shared.VersionHistory) []*types.VersionHistory { if t == nil { return nil } v := make([]*types.VersionHistory, len(t)) for i := range t { v[i] = ToVersionHistory(t[i]) } return v } // FromVersionHistoryItemArray converts internal VersionHistoryItem type array to thrift func FromVersionHistoryItemArray(t []*types.VersionHistoryItem) []*shared.VersionHistoryItem { if t == nil { return nil } v := make([]*shared.VersionHistoryItem, len(t)) for i := range t { v[i] = FromVersionHistoryItem(t[i]) } return v } // ToVersionHistoryItemArray converts thrift VersionHistoryItem type array to internal func ToVersionHistoryItemArray(t []*shared.VersionHistoryItem) []*types.VersionHistoryItem { if t == nil { return nil } v := make([]*types.VersionHistoryItem, len(t)) for i := range t { v[i] = ToVersionHistoryItem(t[i]) } return v } // FromTaskListPartitionMetadataArray converts internal TaskListPartitionMetadata type array to thrift func FromTaskListPartitionMetadataArray(t []*types.TaskListPartitionMetadata) []*shared.TaskListPartitionMetadata { if t == nil { return nil } v := make([]*shared.TaskListPartitionMetadata, len(t)) for i := range t { v[i] = FromTaskListPartitionMetadata(t[i]) } return v } // ToTaskListPartitionMetadataArray converts thrift TaskListPartitionMetadata type array to internal func ToTaskListPartitionMetadataArray(t []*shared.TaskListPartitionMetadata) []*types.TaskListPartitionMetadata { if t == nil { return nil } v := make([]*types.TaskListPartitionMetadata, len(t)) for i := range t { v[i] = ToTaskListPartitionMetadata(t[i]) } return v } // FromResetPointInfoArray converts internal ResetPointInfo type array to thrift func FromResetPointInfoArray(t []*types.ResetPointInfo) []*shared.ResetPointInfo { if t == nil { return nil } v := make([]*shared.ResetPointInfo, len(t)) for i := range t { v[i] = FromResetPointInfo(t[i]) } return v } // ToResetPointInfoArray converts thrift ResetPointInfo type array to internal func ToResetPointInfoArray(t []*shared.ResetPointInfo) []*types.ResetPointInfo { if t == nil { return nil } v := make([]*types.ResetPointInfo, len(t)) for i := range t { v[i] = ToResetPointInfo(t[i]) } return v } // FromPendingChildExecutionInfoArray converts internal PendingChildExecutionInfo type array to thrift func FromPendingChildExecutionInfoArray(t []*types.PendingChildExecutionInfo) []*shared.PendingChildExecutionInfo { if t == nil { return nil } v := make([]*shared.PendingChildExecutionInfo, len(t)) for i := range t { v[i] = FromPendingChildExecutionInfo(t[i]) } return v } // ToPendingChildExecutionInfoArray converts thrift PendingChildExecutionInfo type array to internal func ToPendingChildExecutionInfoArray(t []*shared.PendingChildExecutionInfo) []*types.PendingChildExecutionInfo { if t == nil { return nil } v := make([]*types.PendingChildExecutionInfo, len(t)) for i := range t { v[i] = ToPendingChildExecutionInfo(t[i]) } return v } // FromHistoryBranchRangeArray converts internal HistoryBranchRange type array to thrift func FromHistoryBranchRangeArray(t []*types.HistoryBranchRange) []*shared.HistoryBranchRange { if t == nil { return nil } v := make([]*shared.HistoryBranchRange, len(t)) for i := range t { v[i] = FromHistoryBranchRange(t[i]) } return v } // ToHistoryBranchRangeArray converts thrift HistoryBranchRange type array to internal func ToHistoryBranchRangeArray(t []*shared.HistoryBranchRange) []*types.HistoryBranchRange { if t == nil { return nil } v := make([]*types.HistoryBranchRange, len(t)) for i := range t { v[i] = ToHistoryBranchRange(t[i]) } return v } // FromIndexedValueTypeMap converts internal IndexedValueType type map to thrift func FromIndexedValueTypeMap(t map[string]types.IndexedValueType) map[string]shared.IndexedValueType { if t == nil { return nil } v := make(map[string]shared.IndexedValueType, len(t)) for key := range t { v[key] = FromIndexedValueType(t[key]) } return v } // ToIndexedValueTypeMap converts thrift IndexedValueType type map to internal func ToIndexedValueTypeMap(t map[string]shared.IndexedValueType) map[string]types.IndexedValueType { if t == nil { return nil } v := make(map[string]types.IndexedValueType, len(t)) for key := range t { v[key] = ToIndexedValueType(t[key]) } return v } // FromWorkflowQueryMap converts internal WorkflowQuery type map to thrift func FromWorkflowQueryMap(t map[string]*types.WorkflowQuery) map[string]*shared.WorkflowQuery { if t == nil { return nil } v := make(map[string]*shared.WorkflowQuery, len(t)) for key := range t { v[key] = FromWorkflowQuery(t[key]) } return v } // ToWorkflowQueryMap converts thrift WorkflowQuery type map to internal func ToWorkflowQueryMap(t map[string]*shared.WorkflowQuery) map[string]*types.WorkflowQuery { if t == nil { return nil } v := make(map[string]*types.WorkflowQuery, len(t)) for key := range t { v[key] = ToWorkflowQuery(t[key]) } return v } // FromWorkflowQueryResultMap converts internal WorkflowQueryResult type map to thrift func FromWorkflowQueryResultMap(t map[string]*types.WorkflowQueryResult) map[string]*shared.WorkflowQueryResult { if t == nil { return nil } v := make(map[string]*shared.WorkflowQueryResult, len(t)) for key := range t { v[key] = FromWorkflowQueryResult(t[key]) } return v } // ToWorkflowQueryResultMap converts thrift WorkflowQueryResult type map to internal func ToWorkflowQueryResultMap(t map[string]*shared.WorkflowQueryResult) map[string]*types.WorkflowQueryResult { if t == nil { return nil } v := make(map[string]*types.WorkflowQueryResult, len(t)) for key := range t { v[key] = ToWorkflowQueryResult(t[key]) } return v } // FromActivityLocalDispatchInfoMap converts internal ActivityLocalDispatchInfo type map to thrift func FromActivityLocalDispatchInfoMap(t map[string]*types.ActivityLocalDispatchInfo) map[string]*shared.ActivityLocalDispatchInfo { if t == nil { return nil } v := make(map[string]*shared.ActivityLocalDispatchInfo, len(t)) for key := range t { v[key] = FromActivityLocalDispatchInfo(t[key]) } return v } // ToActivityLocalDispatchInfoMap converts thrift ActivityLocalDispatchInfo type map to internal func ToActivityLocalDispatchInfoMap(t map[string]*shared.ActivityLocalDispatchInfo) map[string]*types.ActivityLocalDispatchInfo { if t == nil { return nil } v := make(map[string]*types.ActivityLocalDispatchInfo, len(t)) for key := range t { v[key] = ToActivityLocalDispatchInfo(t[key]) } return v } // FromBadBinaryInfoMap converts internal BadBinaryInfo type map to thrift func FromBadBinaryInfoMap(t map[string]*types.BadBinaryInfo) map[string]*shared.BadBinaryInfo { if t == nil { return nil } v := make(map[string]*shared.BadBinaryInfo, len(t)) for key := range t { v[key] = FromBadBinaryInfo(t[key]) } return v } // ToBadBinaryInfoMap converts thrift BadBinaryInfo type map to internal func ToBadBinaryInfoMap(t map[string]*shared.BadBinaryInfo) map[string]*types.BadBinaryInfo { if t == nil { return nil } v := make(map[string]*types.BadBinaryInfo, len(t)) for key := range t { v[key] = ToBadBinaryInfo(t[key]) } return v } func FromIsolationGroupMetricsMap(t map[string]*types.IsolationGroupMetrics) map[string]*shared.IsolationGroupMetrics { if t == nil { return nil } v := make(map[string]*shared.IsolationGroupMetrics, len(t)) for key := range t { v[key] = FromIsolationGroupMetrics(t[key]) } return v } func ToIsolationGroupMetricsMap(t map[string]*shared.IsolationGroupMetrics) map[string]*types.IsolationGroupMetrics { if t == nil { return nil } v := make(map[string]*types.IsolationGroupMetrics, len(t)) for key := range t { v[key] = ToIsolationGroupMetrics(t[key]) } return v } // FromHistoryArray converts internal History array to thrift func FromHistoryArray(t []*types.History) []*shared.History { if t == nil { return nil } v := make([]*shared.History, len(t)) for i := range t { v[i] = FromHistory(t[i]) } return v } // ToHistoryArray converts thrift History array to internal func ToHistoryArray(t []*shared.History) []*types.History { if t == nil { return nil } v := make([]*types.History, len(t)) for i := range t { v[i] = ToHistory(t[i]) } return v } // FromCrossClusterTaskType converts internal CrossClusterTaskType type to thrift func FromCrossClusterTaskType(t *types.CrossClusterTaskType) *shared.CrossClusterTaskType { if t == nil { return nil } switch *t { case types.CrossClusterTaskTypeStartChildExecution: v := shared.CrossClusterTaskTypeStartChildExecution return &v case types.CrossClusterTaskTypeCancelExecution: v := shared.CrossClusterTaskTypeCancelExecution return &v case types.CrossClusterTaskTypeSignalExecution: v := shared.CrossClusterTaskTypeSignalExecution return &v case types.CrossClusterTaskTypeRecordChildWorkflowExeuctionComplete: v := shared.CrossClusterTaskTypeRecordChildWorkflowExecutionComplete return &v case types.CrossClusterTaskTypeApplyParentPolicy: v := shared.CrossClusterTaskTypeApplyParentClosePolicy return &v } panic("unexpected enum value") } // ToCrossClusterTaskType converts thrift CrossClusterTaskType type to internal func ToCrossClusterTaskType(t *shared.CrossClusterTaskType) *types.CrossClusterTaskType { if t == nil { return nil } switch *t { case shared.CrossClusterTaskTypeStartChildExecution: v := types.CrossClusterTaskTypeStartChildExecution return &v case shared.CrossClusterTaskTypeCancelExecution: v := types.CrossClusterTaskTypeCancelExecution return &v case shared.CrossClusterTaskTypeSignalExecution: v := types.CrossClusterTaskTypeSignalExecution return &v case shared.CrossClusterTaskTypeRecordChildWorkflowExecutionComplete: v := types.CrossClusterTaskTypeRecordChildWorkflowExeuctionComplete return &v case shared.CrossClusterTaskTypeApplyParentClosePolicy: v := types.CrossClusterTaskTypeApplyParentPolicy return &v } panic("unexpected enum value") } // FromCrossClusterTaskFailedCause converts internal CrossClusterTaskFailedCause type to thrift func FromCrossClusterTaskFailedCause(t *types.CrossClusterTaskFailedCause) *shared.CrossClusterTaskFailedCause { if t == nil { return nil } switch *t { case types.CrossClusterTaskFailedCauseDomainNotActive: v := shared.CrossClusterTaskFailedCauseDomainNotActive return &v case types.CrossClusterTaskFailedCauseDomainNotExists: v := shared.CrossClusterTaskFailedCauseDomainNotExists return &v case types.CrossClusterTaskFailedCauseWorkflowAlreadyRunning: v := shared.CrossClusterTaskFailedCauseWorkflowAlreadyRunning return &v case types.CrossClusterTaskFailedCauseWorkflowNotExists: v := shared.CrossClusterTaskFailedCauseWorkflowNotExists return &v case types.CrossClusterTaskFailedCauseWorkflowAlreadyCompleted: v := shared.CrossClusterTaskFailedCauseWorkflowAlreadyCompleted return &v case types.CrossClusterTaskFailedCauseUncategorized: v := shared.CrossClusterTaskFailedCauseUncategorized return &v } panic("unexpected enum value") } // ToCrossClusterTaskFailedCause converts thrift CrossClusterTaskFailedCause type to internal func ToCrossClusterTaskFailedCause(t *shared.CrossClusterTaskFailedCause) *types.CrossClusterTaskFailedCause { if t == nil { return nil } switch *t { case shared.CrossClusterTaskFailedCauseDomainNotActive: v := types.CrossClusterTaskFailedCauseDomainNotActive return &v case shared.CrossClusterTaskFailedCauseDomainNotExists: v := types.CrossClusterTaskFailedCauseDomainNotExists return &v case shared.CrossClusterTaskFailedCauseWorkflowAlreadyRunning: v := types.CrossClusterTaskFailedCauseWorkflowAlreadyRunning return &v case shared.CrossClusterTaskFailedCauseWorkflowNotExists: v := types.CrossClusterTaskFailedCauseWorkflowNotExists return &v case shared.CrossClusterTaskFailedCauseWorkflowAlreadyCompleted: v := types.CrossClusterTaskFailedCauseWorkflowAlreadyCompleted return &v case shared.CrossClusterTaskFailedCauseUncategorized: v := types.CrossClusterTaskFailedCauseUncategorized return &v } panic("unexpected enum value") } // FromGetTaskFailedCause converts internal GetTaskFailedCause to thrift func FromGetTaskFailedCause(t *types.GetTaskFailedCause) *shared.GetTaskFailedCause { if t == nil { return nil } switch *t { case types.GetTaskFailedCauseServiceBusy: v := shared.GetTaskFailedCauseServiceBusy return &v case types.GetTaskFailedCauseTimeout: v := shared.GetTaskFailedCauseTimeout return &v case types.GetTaskFailedCauseShardOwnershipLost: v := shared.GetTaskFailedCauseShardOwnershipLost return &v case types.GetTaskFailedCauseUncategorized: v := shared.GetTaskFailedCauseUncategorized return &v } panic("unexpected enum value") } // ToGetCrossClusterTaskFailedCause converts thrift GetTaskFailedCause type to internal func ToGetCrossClusterTaskFailedCause(t *shared.GetTaskFailedCause) *types.GetTaskFailedCause { if t == nil { return nil } switch *t { case shared.GetTaskFailedCauseServiceBusy: v := types.GetTaskFailedCauseServiceBusy return &v case shared.GetTaskFailedCauseTimeout: v := types.GetTaskFailedCauseTimeout return &v case shared.GetTaskFailedCauseShardOwnershipLost: v := types.GetTaskFailedCauseShardOwnershipLost return &v case shared.GetTaskFailedCauseUncategorized: v := types.GetTaskFailedCauseUncategorized return &v } panic("unexpected enum value") } // FromCrossClusterTaskInfo converts internal CrossClusterTaskInfo type to thrift func FromCrossClusterTaskInfo(t *types.CrossClusterTaskInfo) *shared.CrossClusterTaskInfo { if t == nil { return nil } return &shared.CrossClusterTaskInfo{ DomainID: &t.DomainID, WorkflowID: &t.WorkflowID, RunID: &t.RunID, TaskType: FromCrossClusterTaskType(t.TaskType), TaskState: &t.TaskState, TaskID: &t.TaskID, VisibilityTimestamp: t.VisibilityTimestamp, } } // ToCrossClusterTaskInfo converts thrift CrossClusterTaskInfo type to internal func ToCrossClusterTaskInfo(t *shared.CrossClusterTaskInfo) *types.CrossClusterTaskInfo { if t == nil { return nil } return &types.CrossClusterTaskInfo{ DomainID: t.GetDomainID(), WorkflowID: t.GetWorkflowID(), RunID: t.GetRunID(), TaskType: ToCrossClusterTaskType(t.TaskType), TaskState: t.GetTaskState(), TaskID: t.GetTaskID(), VisibilityTimestamp: t.VisibilityTimestamp, } } // FromCrossClusterStartChildExecutionRequestAttributes converts internal CrossClusterStartChildExecutionRequestAttributes type to thrift func FromCrossClusterStartChildExecutionRequestAttributes(t *types.CrossClusterStartChildExecutionRequestAttributes) *shared.CrossClusterStartChildExecutionRequestAttributes { if t == nil { return nil } return &shared.CrossClusterStartChildExecutionRequestAttributes{ TargetDomainID: &t.TargetDomainID, RequestID: &t.RequestID, InitiatedEventID: &t.InitiatedEventID, InitiatedEventAttributes: FromStartChildWorkflowExecutionInitiatedEventAttributes(t.InitiatedEventAttributes), TargetRunID: t.TargetRunID, PartitionConfig: t.PartitionConfig, } } // ToCrossClusterStartChildExecutionRequestAttributes converts thrift CrossClusterStartChildExecutionRequestAttributes type to internal func ToCrossClusterStartChildExecutionRequestAttributes(t *shared.CrossClusterStartChildExecutionRequestAttributes) *types.CrossClusterStartChildExecutionRequestAttributes { if t == nil { return nil } return &types.CrossClusterStartChildExecutionRequestAttributes{ TargetDomainID: t.GetTargetDomainID(), RequestID: t.GetRequestID(), InitiatedEventID: t.GetInitiatedEventID(), InitiatedEventAttributes: ToStartChildWorkflowExecutionInitiatedEventAttributes(t.InitiatedEventAttributes), TargetRunID: t.TargetRunID, PartitionConfig: t.PartitionConfig, } } // FromCrossClusterStartChildExecutionResponseAttributes converts internal CrossClusterStartChildExecutionResponseAttributes type to thrift func FromCrossClusterStartChildExecutionResponseAttributes(t *types.CrossClusterStartChildExecutionResponseAttributes) *shared.CrossClusterStartChildExecutionResponseAttributes { if t == nil { return nil } return &shared.CrossClusterStartChildExecutionResponseAttributes{ RunID: &t.RunID, } } // ToCrossClusterStartChildExecutionResponseAttributes converts thrift CrossClusterStartChildExecutionResponseAttributes type to internal func ToCrossClusterStartChildExecutionResponseAttributes(t *shared.CrossClusterStartChildExecutionResponseAttributes) *types.CrossClusterStartChildExecutionResponseAttributes { if t == nil { return nil } return &types.CrossClusterStartChildExecutionResponseAttributes{ RunID: t.GetRunID(), } } // FromCrossClusterCancelExecutionRequestAttributes converts internal CrossClusterCancelExecutionRequestAttributes type to thrift func FromCrossClusterCancelExecutionRequestAttributes(t *types.CrossClusterCancelExecutionRequestAttributes) *shared.CrossClusterCancelExecutionRequestAttributes { if t == nil { return nil } return &shared.CrossClusterCancelExecutionRequestAttributes{ TargetDomainID: &t.TargetDomainID, TargetWorkflowID: &t.TargetWorkflowID, TargetRunID: &t.TargetRunID, RequestID: &t.RequestID, InitiatedEventID: &t.InitiatedEventID, ChildWorkflowOnly: &t.ChildWorkflowOnly, } } // ToCrossClusterCancelExecutionRequestAttributes converts thrift CrossClusterCancelExecutionRequestAttributes type to internal func ToCrossClusterCancelExecutionRequestAttributes(t *shared.CrossClusterCancelExecutionRequestAttributes) *types.CrossClusterCancelExecutionRequestAttributes { if t == nil { return nil } return &types.CrossClusterCancelExecutionRequestAttributes{ TargetDomainID: t.GetTargetDomainID(), TargetWorkflowID: t.GetTargetWorkflowID(), TargetRunID: t.GetTargetRunID(), RequestID: t.GetRequestID(), InitiatedEventID: t.GetInitiatedEventID(), ChildWorkflowOnly: t.GetChildWorkflowOnly(), } } // FromCrossClusterCancelExecutionResponseAttributes converts internal CrossClusterCancelExecutionResponseAttributes type to thrift func FromCrossClusterCancelExecutionResponseAttributes(t *types.CrossClusterCancelExecutionResponseAttributes) *shared.CrossClusterCancelExecutionResponseAttributes { if t == nil { return nil } return &shared.CrossClusterCancelExecutionResponseAttributes{} } // ToCrossClusterCancelExecutionResponseAttributes converts thrift CrossClusterCancelExecutionResponseAttributes type to internal func ToCrossClusterCancelExecutionResponseAttributes(t *shared.CrossClusterCancelExecutionResponseAttributes) *types.CrossClusterCancelExecutionResponseAttributes { if t == nil { return nil } return &types.CrossClusterCancelExecutionResponseAttributes{} } // FromCrossClusterSignalExecutionRequestAttributes converts internal CrossClusterSignalExecutionRequestAttributes type to thrift func FromCrossClusterSignalExecutionRequestAttributes(t *types.CrossClusterSignalExecutionRequestAttributes) *shared.CrossClusterSignalExecutionRequestAttributes { if t == nil { return nil } return &shared.CrossClusterSignalExecutionRequestAttributes{ TargetDomainID: &t.TargetDomainID, TargetWorkflowID: &t.TargetWorkflowID, TargetRunID: &t.TargetRunID, RequestID: &t.RequestID, InitiatedEventID: &t.InitiatedEventID, ChildWorkflowOnly: &t.ChildWorkflowOnly, SignalName: &t.SignalName, SignalInput: t.SignalInput, Control: t.Control, } } // ToCrossClusterSignalExecutionRequestAttributes converts thrift CrossClusterSignalExecutionRequestAttributes type to internal func ToCrossClusterSignalExecutionRequestAttributes(t *shared.CrossClusterSignalExecutionRequestAttributes) *types.CrossClusterSignalExecutionRequestAttributes { if t == nil { return nil } return &types.CrossClusterSignalExecutionRequestAttributes{ TargetDomainID: t.GetTargetDomainID(), TargetWorkflowID: t.GetTargetWorkflowID(), TargetRunID: t.GetTargetRunID(), RequestID: t.GetRequestID(), InitiatedEventID: t.GetInitiatedEventID(), ChildWorkflowOnly: t.GetChildWorkflowOnly(), SignalName: t.GetSignalName(), SignalInput: t.SignalInput, Control: t.Control, } } // FromCrossClusterSignalExecutionResponseAttributes converts internal CrossClusterSignalExecutionResponseAttributes type to thrift func FromCrossClusterSignalExecutionResponseAttributes(t *types.CrossClusterSignalExecutionResponseAttributes) *shared.CrossClusterSignalExecutionResponseAttributes { if t == nil { return nil } return &shared.CrossClusterSignalExecutionResponseAttributes{} } // ToCrossClusterSignalExecutionResponseAttributes converts thrift CrossClusterSignalExecutionResponseAttributes type to internal func ToCrossClusterSignalExecutionResponseAttributes(t *shared.CrossClusterSignalExecutionResponseAttributes) *types.CrossClusterSignalExecutionResponseAttributes { if t == nil { return nil } return &types.CrossClusterSignalExecutionResponseAttributes{} } // FromCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes converts internal // CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes type to thrift func FromCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes( t *types.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes, ) *shared.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes { if t == nil { return nil } return &shared.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes{ TargetDomainID: &t.TargetDomainID, TargetWorkflowID: &t.TargetWorkflowID, TargetRunID: &t.TargetRunID, InitiatedEventID: &t.InitiatedEventID, CompletionEvent: FromHistoryEvent(t.CompletionEvent), } } // ToCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes converts thrift // CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes type to internal func ToCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes( t *shared.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes, ) *types.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes { if t == nil { return nil } return &types.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes{ TargetDomainID: t.GetTargetDomainID(), TargetWorkflowID: t.GetTargetWorkflowID(), TargetRunID: t.GetTargetRunID(), InitiatedEventID: t.GetInitiatedEventID(), CompletionEvent: ToHistoryEvent(t.CompletionEvent), } } // FromCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes converts internal // CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes type to thrift func FromCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes( t *types.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes, ) *shared.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes { if t == nil { return nil } return &shared.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes{} } // ToCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes converts thrift // CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes type to internal func ToCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes( t *shared.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes, ) *types.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes { if t == nil { return nil } return &types.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes{} } // FromApplyParentClosePolicyAttributes converts internal // ApplyParentClosePolicyAttributes types to thrift func FromApplyParentClosePolicyAttributes( t *types.ApplyParentClosePolicyAttributes, ) *shared.ApplyParentClosePolicyAttributes { if t == nil { return nil } return &shared.ApplyParentClosePolicyAttributes{ ChildDomainID: &t.ChildDomainID, ChildWorkflowID: &t.ChildWorkflowID, ChildRunID: &t.ChildRunID, ParentClosePolicy: FromParentClosePolicy(t.ParentClosePolicy), } } // ToApplyParentClosePolicyAttributes converts thrift // ApplyParentClosePolicyAttributes types to internal func ToApplyParentClosePolicyAttributes( t *shared.ApplyParentClosePolicyAttributes, ) *types.ApplyParentClosePolicyAttributes { if t == nil { return nil } return &types.ApplyParentClosePolicyAttributes{ ChildDomainID: t.GetChildDomainID(), ChildWorkflowID: t.GetChildWorkflowID(), ChildRunID: t.GetChildRunID(), ParentClosePolicy: ToParentClosePolicy(t.ParentClosePolicy), } } // FromApplyParentClosePolicyStatus converts thrift // ApplyParentClosePolicyStatus types to internal func FromApplyParentClosePolicyStatus( t *types.ApplyParentClosePolicyStatus, ) *shared.ApplyParentClosePolicyStatus { if t == nil { return nil } return &shared.ApplyParentClosePolicyStatus{ Completed: &t.Completed, FailedCause: FromCrossClusterTaskFailedCause(t.FailedCause), } } // ToApplyParentClosePolicyStatus converts thrift // ApplyParentClosePolicyStatus types to internal func ToApplyParentClosePolicyStatus( t *shared.ApplyParentClosePolicyStatus, ) *types.ApplyParentClosePolicyStatus { if t == nil { return nil } return &types.ApplyParentClosePolicyStatus{ Completed: t.GetCompleted(), FailedCause: ToCrossClusterTaskFailedCause(t.FailedCause), } } // FromApplyParentClosePolicyRequest converts thrift // ApplyParentClosePolicyRequest types to internal func FromApplyParentClosePolicyRequest( t *types.ApplyParentClosePolicyRequest, ) *shared.ApplyParentClosePolicyRequest { if t == nil { return nil } return &shared.ApplyParentClosePolicyRequest{ Child: FromApplyParentClosePolicyAttributes(t.Child), Status: FromApplyParentClosePolicyStatus(t.Status), } } // ToApplyParentClosePolicyRequest converts thrift // ApplyParentClosePolicyRequest types to internal func ToApplyParentClosePolicyRequest( t *shared.ApplyParentClosePolicyRequest, ) *types.ApplyParentClosePolicyRequest { if t == nil { return nil } return &types.ApplyParentClosePolicyRequest{ Child: ToApplyParentClosePolicyAttributes(t.Child), Status: ToApplyParentClosePolicyStatus(t.Status), } } // FromApplyParentClosePolicyRequestArray converts thrift // ApplyParentClosePolicyRequestArray types to internal func FromApplyParentClosePolicyRequestArray( t []*types.ApplyParentClosePolicyRequest, ) []*shared.ApplyParentClosePolicyRequest { if t == nil { return nil } v := make([]*shared.ApplyParentClosePolicyRequest, len(t)) for i := range t { v[i] = FromApplyParentClosePolicyRequest(t[i]) } return v } // ToApplyParentClosePolicyRequestArray converts internal // ApplyParentClosePolicyRequestArray types to thrift func ToApplyParentClosePolicyRequestArray( t []*shared.ApplyParentClosePolicyRequest, ) []*types.ApplyParentClosePolicyRequest { if t == nil { return nil } v := make([]*types.ApplyParentClosePolicyRequest, len(t)) for i := range t { v[i] = ToApplyParentClosePolicyRequest(t[i]) } return v } // FromApplyParentClosePolicyResult converts thrift // ApplyParentClosePolicyResult types to internal func FromApplyParentClosePolicyResult( t *types.ApplyParentClosePolicyResult, ) *shared.ApplyParentClosePolicyResult { if t == nil { return nil } return &shared.ApplyParentClosePolicyResult{ Child: FromApplyParentClosePolicyAttributes(t.Child), FailedCause: FromCrossClusterTaskFailedCause(t.FailedCause), } } // ToApplyParentClosePolicyResult converts thrift // ApplyParentClosePolicyResult types to internal func ToApplyParentClosePolicyResult( t *shared.ApplyParentClosePolicyResult, ) *types.ApplyParentClosePolicyResult { if t == nil { return nil } return &types.ApplyParentClosePolicyResult{ Child: ToApplyParentClosePolicyAttributes(t.Child), FailedCause: ToCrossClusterTaskFailedCause(t.FailedCause), } } // FromApplyParentClosePolicyResultArray converts internal // ApplyParentClosePolicyResultArray types to internal func FromApplyParentClosePolicyResultArray( t []*types.ApplyParentClosePolicyResult, ) []*shared.ApplyParentClosePolicyResult { if t == nil { return nil } v := make([]*shared.ApplyParentClosePolicyResult, len(t)) for i := range t { v[i] = FromApplyParentClosePolicyResult(t[i]) } return v } // ToApplyParentClosePolicyResultArray converts internal // ApplyParentClosePolicyResultArray types to thrift func ToApplyParentClosePolicyResultArray( t []*shared.ApplyParentClosePolicyResult, ) []*types.ApplyParentClosePolicyResult { if t == nil { return nil } v := make([]*types.ApplyParentClosePolicyResult, len(t)) for i := range t { v[i] = ToApplyParentClosePolicyResult(t[i]) } return v } // FromCrossClusterApplyParentClosePolicyRequestAttributes converts internal // CrossClusterApplyParentClosePolicyRequestAttributes types to thrift func FromCrossClusterApplyParentClosePolicyRequestAttributes( t *types.CrossClusterApplyParentClosePolicyRequestAttributes, ) *shared.CrossClusterApplyParentClosePolicyRequestAttributes { if t == nil { return nil } return &shared.CrossClusterApplyParentClosePolicyRequestAttributes{ Children: FromApplyParentClosePolicyRequestArray(t.Children), } } // ToCrossClusterApplyParentClosePolicyRequestAttributes converts thrift // CrossClusterApplyParentClosePolicyRequestAttributes types to internal func ToCrossClusterApplyParentClosePolicyRequestAttributes( t *shared.CrossClusterApplyParentClosePolicyRequestAttributes, ) *types.CrossClusterApplyParentClosePolicyRequestAttributes { if t == nil { return nil } return &types.CrossClusterApplyParentClosePolicyRequestAttributes{ Children: ToApplyParentClosePolicyRequestArray(t.Children), } } // FromCrossClusterApplyParentClosePolicyResponseAttributes converts internal // CrossClusterApplyParentClosePolicyResponseAttributes type to thrift func FromCrossClusterApplyParentClosePolicyResponseAttributes( t *types.CrossClusterApplyParentClosePolicyResponseAttributes, ) *shared.CrossClusterApplyParentClosePolicyResponseAttributes { if t == nil { return nil } return &shared.CrossClusterApplyParentClosePolicyResponseAttributes{ ChildrenStatus: FromApplyParentClosePolicyResultArray(t.ChildrenStatus), } } // ToCrossClusterApplyParentClosePolicyResponseAttributes converts thrift // CrossClusterApplyParentClosePolicyResponseAttributes type to internal func ToCrossClusterApplyParentClosePolicyResponseAttributes( t *shared.CrossClusterApplyParentClosePolicyResponseAttributes, ) *types.CrossClusterApplyParentClosePolicyResponseAttributes { if t == nil { return nil } return &types.CrossClusterApplyParentClosePolicyResponseAttributes{ ChildrenStatus: ToApplyParentClosePolicyResultArray(t.ChildrenStatus), } } // FromCrossClusterTaskRequest converts internal CrossClusterTaskRequest type to thrift func FromCrossClusterTaskRequest(t *types.CrossClusterTaskRequest) *shared.CrossClusterTaskRequest { if t == nil { return nil } return &shared.CrossClusterTaskRequest{ TaskInfo: FromCrossClusterTaskInfo(t.TaskInfo), StartChildExecutionAttributes: FromCrossClusterStartChildExecutionRequestAttributes(t.StartChildExecutionAttributes), CancelExecutionAttributes: FromCrossClusterCancelExecutionRequestAttributes(t.CancelExecutionAttributes), SignalExecutionAttributes: FromCrossClusterSignalExecutionRequestAttributes(t.SignalExecutionAttributes), RecordChildWorkflowExecutionCompleteAttributes: FromCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes(t.RecordChildWorkflowExecutionCompleteAttributes), ApplyParentClosePolicyAttributes: FromCrossClusterApplyParentClosePolicyRequestAttributes(t.ApplyParentClosePolicyAttributes), } } // ToCrossClusterTaskRequest converts thrift CrossClusterTaskRequest type to internal func ToCrossClusterTaskRequest(t *shared.CrossClusterTaskRequest) *types.CrossClusterTaskRequest { if t == nil { return nil } return &types.CrossClusterTaskRequest{ TaskInfo: ToCrossClusterTaskInfo(t.TaskInfo), StartChildExecutionAttributes: ToCrossClusterStartChildExecutionRequestAttributes(t.StartChildExecutionAttributes), CancelExecutionAttributes: ToCrossClusterCancelExecutionRequestAttributes(t.CancelExecutionAttributes), SignalExecutionAttributes: ToCrossClusterSignalExecutionRequestAttributes(t.SignalExecutionAttributes), RecordChildWorkflowExecutionCompleteAttributes: ToCrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes(t.RecordChildWorkflowExecutionCompleteAttributes), ApplyParentClosePolicyAttributes: ToCrossClusterApplyParentClosePolicyRequestAttributes(t.ApplyParentClosePolicyAttributes), } } // FromCrossClusterTaskResponse converts internal CrossClusterTaskResponse type to thrift func FromCrossClusterTaskResponse(t *types.CrossClusterTaskResponse) *shared.CrossClusterTaskResponse { if t == nil { return nil } return &shared.CrossClusterTaskResponse{ TaskID: &t.TaskID, TaskType: FromCrossClusterTaskType(t.TaskType), TaskState: &t.TaskState, FailedCause: FromCrossClusterTaskFailedCause(t.FailedCause), StartChildExecutionAttributes: FromCrossClusterStartChildExecutionResponseAttributes(t.StartChildExecutionAttributes), CancelExecutionAttributes: FromCrossClusterCancelExecutionResponseAttributes(t.CancelExecutionAttributes), SignalExecutionAttributes: FromCrossClusterSignalExecutionResponseAttributes(t.SignalExecutionAttributes), RecordChildWorkflowExecutionCompleteAttributes: FromCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes(t.RecordChildWorkflowExecutionCompleteAttributes), ApplyParentClosePolicyAttributes: FromCrossClusterApplyParentClosePolicyResponseAttributes(t.ApplyParentClosePolicyAttributes), } } // ToCrossClusterTaskResponse converts thrift CrossClusterTaskResponse type to internal func ToCrossClusterTaskResponse(t *shared.CrossClusterTaskResponse) *types.CrossClusterTaskResponse { if t == nil { return nil } return &types.CrossClusterTaskResponse{ TaskID: t.GetTaskID(), TaskType: ToCrossClusterTaskType(t.TaskType), TaskState: t.GetTaskState(), FailedCause: ToCrossClusterTaskFailedCause(t.FailedCause), StartChildExecutionAttributes: ToCrossClusterStartChildExecutionResponseAttributes(t.StartChildExecutionAttributes), CancelExecutionAttributes: ToCrossClusterCancelExecutionResponseAttributes(t.CancelExecutionAttributes), SignalExecutionAttributes: ToCrossClusterSignalExecutionResponseAttributes(t.SignalExecutionAttributes), RecordChildWorkflowExecutionCompleteAttributes: ToCrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes(t.RecordChildWorkflowExecutionCompleteAttributes), ApplyParentClosePolicyAttributes: ToCrossClusterApplyParentClosePolicyResponseAttributes(t.ApplyParentClosePolicyAttributes), } } // FromAdminGetCrossClusterTasksRequest converts internal GetCrossClusterTasksRequest type to thrift func FromAdminGetCrossClusterTasksRequest(t *types.GetCrossClusterTasksRequest) *shared.GetCrossClusterTasksRequest { if t == nil { return nil } return &shared.GetCrossClusterTasksRequest{ ShardIDs: t.ShardIDs, TargetCluster: &t.TargetCluster, } } // ToAdminGetCrossClusterTasksRequest converts thrift GetCrossClusterTasksRequest type to internal func ToAdminGetCrossClusterTasksRequest(t *shared.GetCrossClusterTasksRequest) *types.GetCrossClusterTasksRequest { if t == nil { return nil } return &types.GetCrossClusterTasksRequest{ ShardIDs: t.ShardIDs, TargetCluster: t.GetTargetCluster(), } } // FromCrossClusterTaskRequestArray converts internal CrossClusterTaskRequest type array to thrift func FromCrossClusterTaskRequestArray(t []*types.CrossClusterTaskRequest) []*shared.CrossClusterTaskRequest { if t == nil { return nil } v := make([]*shared.CrossClusterTaskRequest, len(t)) for i := range t { v[i] = FromCrossClusterTaskRequest(t[i]) } return v } // ToCrossClusterTaskRequestArray converts thrift CrossClusterTaskRequest type array to internal func ToCrossClusterTaskRequestArray(t []*shared.CrossClusterTaskRequest) []*types.CrossClusterTaskRequest { if t == nil { return nil } v := make([]*types.CrossClusterTaskRequest, len(t)) for i := range t { v[i] = ToCrossClusterTaskRequest(t[i]) } return v } // FromCrossClusterTaskRequestMap converts internal CrossClusterTaskRequest type map to thrift func FromCrossClusterTaskRequestMap(t map[int32][]*types.CrossClusterTaskRequest) map[int32][]*shared.CrossClusterTaskRequest { if t == nil { return nil } v := make(map[int32][]*shared.CrossClusterTaskRequest) for key := range t { v[key] = FromCrossClusterTaskRequestArray(t[key]) } return v } // ToCrossClusterTaskRequestMap converts thrift CrossClusterTaskRequest type map to internal func ToCrossClusterTaskRequestMap(t map[int32][]*shared.CrossClusterTaskRequest) map[int32][]*types.CrossClusterTaskRequest { if t == nil { return nil } v := make(map[int32][]*types.CrossClusterTaskRequest) for key := range t { v[key] = ToCrossClusterTaskRequestArray(t[key]) } return v } // FromGetTaskFailedCauseMap converts internal GetTaskFailedCause type map to thrift func FromGetTaskFailedCauseMap(t map[int32]types.GetTaskFailedCause) map[int32]shared.GetTaskFailedCause { if t == nil { return nil } v := make(map[int32]shared.GetTaskFailedCause) for key, value := range t { v[key] = *FromGetTaskFailedCause(&value) } return v } // ToGetTaskFailedCauseMap converts thrift GetTaskFailedCause type map to internal func ToGetTaskFailedCauseMap(t map[int32]shared.GetTaskFailedCause) map[int32]types.GetTaskFailedCause { if t == nil { return nil } v := make(map[int32]types.GetTaskFailedCause) for key, value := range t { v[key] = *ToGetCrossClusterTaskFailedCause(&value) } return v } // FromAdminGetCrossClusterTasksResponse converts internal GetCrossClusterTasksResponse type to thrift func FromAdminGetCrossClusterTasksResponse(t *types.GetCrossClusterTasksResponse) *shared.GetCrossClusterTasksResponse { if t == nil { return nil } return &shared.GetCrossClusterTasksResponse{ TasksByShard: FromCrossClusterTaskRequestMap(t.TasksByShard), FailedCauseByShard: FromGetTaskFailedCauseMap(t.FailedCauseByShard), } } // ToAdminGetCrossClusterTasksResponse converts thrift GetCrossClusterTasksResponse type to internal func ToAdminGetCrossClusterTasksResponse(t *shared.GetCrossClusterTasksResponse) *types.GetCrossClusterTasksResponse { if t == nil { return nil } return &types.GetCrossClusterTasksResponse{ TasksByShard: ToCrossClusterTaskRequestMap(t.TasksByShard), FailedCauseByShard: ToGetTaskFailedCauseMap(t.FailedCauseByShard), } } // FromCrossClusterTaskResponseArray converts internal CrossClusterTaskResponse type array to thrift func FromCrossClusterTaskResponseArray(t []*types.CrossClusterTaskResponse) []*shared.CrossClusterTaskResponse { if t == nil { return nil } v := make([]*shared.CrossClusterTaskResponse, len(t)) for i := range t { v[i] = FromCrossClusterTaskResponse(t[i]) } return v } // ToCrossClusterTaskResponseArray converts thrift CrossClusterTaskResponse type array to internal func ToCrossClusterTaskResponseArray(t []*shared.CrossClusterTaskResponse) []*types.CrossClusterTaskResponse { if t == nil { return nil } v := make([]*types.CrossClusterTaskResponse, len(t)) for i := range t { v[i] = ToCrossClusterTaskResponse(t[i]) } return v } // FromAdminRespondCrossClusterTasksCompletedRequest converts internal RespondCrossClusterTasksCompletedRequest type to thrift func FromAdminRespondCrossClusterTasksCompletedRequest(t *types.RespondCrossClusterTasksCompletedRequest) *shared.RespondCrossClusterTasksCompletedRequest { if t == nil { return nil } return &shared.RespondCrossClusterTasksCompletedRequest{ ShardID: &t.ShardID, TargetCluster: &t.TargetCluster, TaskResponses: FromCrossClusterTaskResponseArray(t.TaskResponses), FetchNewTasks: &t.FetchNewTasks, } } // ToAdminRespondCrossClusterTasksCompletedRequest converts thrift RespondCrossClusterTasksCompletedRequest type to internal func ToAdminRespondCrossClusterTasksCompletedRequest(t *shared.RespondCrossClusterTasksCompletedRequest) *types.RespondCrossClusterTasksCompletedRequest { if t == nil { return nil } return &types.RespondCrossClusterTasksCompletedRequest{ ShardID: t.GetShardID(), TargetCluster: t.GetTargetCluster(), TaskResponses: ToCrossClusterTaskResponseArray(t.TaskResponses), FetchNewTasks: t.GetFetchNewTasks(), } } // FromAdminRespondCrossClusterTasksCompletedResponse converts internal RespondCrossClusterTasksCompletedResponse type to thrift func FromAdminRespondCrossClusterTasksCompletedResponse(t *types.RespondCrossClusterTasksCompletedResponse) *shared.RespondCrossClusterTasksCompletedResponse { if t == nil { return nil } return &shared.RespondCrossClusterTasksCompletedResponse{ Tasks: FromCrossClusterTaskRequestArray(t.Tasks), } } // ToAdminRespondCrossClusterTasksCompletedResponse converts thrift RespondCrossClusterTasksCompletedResponse type to internal func ToAdminRespondCrossClusterTasksCompletedResponse(t *shared.RespondCrossClusterTasksCompletedResponse) *types.RespondCrossClusterTasksCompletedResponse { if t == nil { return nil } return &types.RespondCrossClusterTasksCompletedResponse{ Tasks: ToCrossClusterTaskRequestArray(t.Tasks), } } // FromStickyWorkerUnavailableError converts internal StickyWorkerUnavailableError type to thrift func FromStickyWorkerUnavailableError(t *types.StickyWorkerUnavailableError) *shared.StickyWorkerUnavailableError { if t == nil { return nil } return &shared.StickyWorkerUnavailableError{ Message: t.Message, } } // ToStickyWorkerUnavailableError converts thrift StickyWorkerUnavailableError type to internal func ToStickyWorkerUnavailableError(t *shared.StickyWorkerUnavailableError) *types.StickyWorkerUnavailableError { if t == nil { return nil } return &types.StickyWorkerUnavailableError{ Message: t.Message, } } // FromTaskListNotOwnedByHostError converts internal TaskListNotOwnedByHostError type to thrift func FromTaskListNotOwnedByHostError(t *cadence_errors.TaskListNotOwnedByHostError) *shared.TaskListNotOwnedByHostError { if t == nil { return nil } return &shared.TaskListNotOwnedByHostError{ OwnedByIdentity: t.OwnedByIdentity, MyIdentity: t.MyIdentity, TasklistName: t.TasklistName, } } // ToTaskListNotOwnedByHostError converts thrift TaskListNotOwnedByHostError type to internal func ToTaskListNotOwnedByHostError(t *shared.TaskListNotOwnedByHostError) *cadence_errors.TaskListNotOwnedByHostError { if t == nil { return nil } return &cadence_errors.TaskListNotOwnedByHostError{ OwnedByIdentity: t.OwnedByIdentity, MyIdentity: t.MyIdentity, TasklistName: t.TasklistName, } } // ToAny converts thrift Any type to internal func ToAny(t *shared.Any) *types.Any { if t == nil { return nil } return &types.Any{ ValueType: t.GetValueType(), Value: t.Value, } } // FromAny converts internal Any type to thrift func FromAny(t *types.Any) *shared.Any { if t == nil { return nil } dup := t.ValueType return &shared.Any{ ValueType: &dup, Value: t.Value, } } // FromWorkflowQuery converts internal WorkflowQuery type to thrift func FromAutoConfigHint(t *types.AutoConfigHint) *shared.AutoConfigHint { if t == nil { return nil } return &shared.AutoConfigHint{ PollerWaitTimeInMs: &t.PollerWaitTimeInMs, EnableAutoConfig: &t.EnableAutoConfig, } } // ToWorkflowQuery converts thrift WorkflowQuery type to internal func ToAutoConfigHint(t *shared.AutoConfigHint) *types.AutoConfigHint { if t == nil { return nil } return &types.AutoConfigHint{ PollerWaitTimeInMs: *t.PollerWaitTimeInMs, EnableAutoConfig: *t.EnableAutoConfig, } } func FromTaskKey(t *types.TaskKey) *shared.TaskKey { if t == nil { return nil } return &shared.TaskKey{ TaskID: common.Ptr(t.TaskID), ScheduledTimeNano: common.Ptr(t.ScheduledTimeNano), } } func ToTaskKey(t *shared.TaskKey) *types.TaskKey { if t == nil { return nil } return &types.TaskKey{ TaskID: t.GetTaskID(), ScheduledTimeNano: t.GetScheduledTimeNano(), } } func FromTaskRange(t *types.TaskRange) *shared.TaskRange { if t == nil { return nil } return &shared.TaskRange{ InclusiveMin: FromTaskKey(t.InclusiveMin), ExclusiveMax: FromTaskKey(t.ExclusiveMax), } } func ToTaskRange(t *shared.TaskRange) *types.TaskRange { if t == nil { return nil } return &types.TaskRange{ InclusiveMin: ToTaskKey(t.InclusiveMin), ExclusiveMax: ToTaskKey(t.ExclusiveMax), } } func FromVirtualSliceState(t *types.VirtualSliceState) *shared.VirtualSliceState { if t == nil { return nil } return &shared.VirtualSliceState{ TaskRange: FromTaskRange(t.TaskRange), Predicate: FromPredicate(t.Predicate), } } func ToVirtualSliceState(t *shared.VirtualSliceState) *types.VirtualSliceState { if t == nil { return nil } return &types.VirtualSliceState{ TaskRange: ToTaskRange(t.TaskRange), Predicate: ToPredicate(t.Predicate), } } func FromVirtualSliceStateArray(t []*types.VirtualSliceState) []*shared.VirtualSliceState { if t == nil { return nil } v := make([]*shared.VirtualSliceState, len(t)) for i := range t { v[i] = FromVirtualSliceState(t[i]) } return v } func ToVirtualSliceStateArray(t []*shared.VirtualSliceState) []*types.VirtualSliceState { if t == nil { return nil } v := make([]*types.VirtualSliceState, len(t)) for i := range t { v[i] = ToVirtualSliceState(t[i]) } return v } func FromVirtualQueueState(t *types.VirtualQueueState) *shared.VirtualQueueState { if t == nil { return nil } return &shared.VirtualQueueState{ VirtualSliceStates: FromVirtualSliceStateArray(t.VirtualSliceStates), } } func ToVirtualQueueState(t *shared.VirtualQueueState) *types.VirtualQueueState { if t == nil { return nil } return &types.VirtualQueueState{ VirtualSliceStates: ToVirtualSliceStateArray(t.VirtualSliceStates), } } func FromVirtualQueueStateMap(t map[int64]*types.VirtualQueueState) map[int64]*shared.VirtualQueueState { if t == nil { return nil } v := make(map[int64]*shared.VirtualQueueState, len(t)) for key := range t { v[key] = FromVirtualQueueState(t[key]) } return v } func ToVirtualQueueStateMap(t map[int64]*shared.VirtualQueueState) map[int64]*types.VirtualQueueState { if t == nil { return nil } v := make(map[int64]*types.VirtualQueueState, len(t)) for key := range t { v[key] = ToVirtualQueueState(t[key]) } return v } func FromQueueState(t *types.QueueState) *shared.QueueState { if t == nil { return nil } return &shared.QueueState{ VirtualQueueStates: FromVirtualQueueStateMap(t.VirtualQueueStates), ExclusiveMaxReadLevel: FromTaskKey(t.ExclusiveMaxReadLevel), } } func ToQueueState(t *shared.QueueState) *types.QueueState { if t == nil { return nil } return &types.QueueState{ VirtualQueueStates: ToVirtualQueueStateMap(t.VirtualQueueStates), ExclusiveMaxReadLevel: ToTaskKey(t.ExclusiveMaxReadLevel), } } func FromCronOverlapPolicy(t *types.CronOverlapPolicy) *shared.CronOverlapPolicy { if t == nil { return nil } switch *t { case types.CronOverlapPolicyBufferOne: return shared.CronOverlapPolicyBufferone.Ptr() case types.CronOverlapPolicySkipped: return shared.CronOverlapPolicySkipped.Ptr() } panic("unexpected enum value") } func ToCronOverlapPolicy(t *shared.CronOverlapPolicy) *types.CronOverlapPolicy { if t == nil { return nil } switch *t { case shared.CronOverlapPolicyBufferone: return types.CronOverlapPolicyBufferOne.Ptr() case shared.CronOverlapPolicySkipped: return types.CronOverlapPolicySkipped.Ptr() } panic("unexpected enum value") } ================================================ FILE: common/types/mapper/thrift/shared_test.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/testing/testdatagen" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/testutils" "github.com/uber/cadence/common/types/testdata" ) func TestCrossClusterTaskInfoConversion(t *testing.T) { for _, item := range []*types.CrossClusterTaskInfo{nil, {}, &testdata.CrossClusterTaskInfo} { assert.Equal(t, item, ToCrossClusterTaskInfo(FromCrossClusterTaskInfo(item))) } } func TestCrossClusterTaskRequestConversion(t *testing.T) { for _, item := range []*types.CrossClusterTaskRequest{ nil, {}, &testdata.CrossClusterTaskRequestStartChildExecution, &testdata.CrossClusterTaskRequestCancelExecution, &testdata.CrossClusterTaskRequestSignalExecution, } { assert.Equal(t, item, ToCrossClusterTaskRequest(FromCrossClusterTaskRequest(item))) } } func TestCrossClusterTaskResponseConversion(t *testing.T) { for _, item := range []*types.CrossClusterTaskResponse{ nil, {}, &testdata.CrossClusterTaskResponseStartChildExecution, &testdata.CrossClusterTaskResponseCancelExecution, &testdata.CrossClusterTaskResponseSignalExecution, } { assert.Equal(t, item, ToCrossClusterTaskResponse(FromCrossClusterTaskResponse(item))) } } func TestCrossClusterTaskRequestArrayConversion(t *testing.T) { for _, item := range [][]*types.CrossClusterTaskRequest{nil, {}, testdata.CrossClusterTaskRequestArray} { assert.Equal(t, item, ToCrossClusterTaskRequestArray(FromCrossClusterTaskRequestArray(item))) } } func TestCrossClusterTaskResponseArrayConversion(t *testing.T) { for _, item := range [][]*types.CrossClusterTaskResponse{nil, {}, testdata.CrossClusterTaskResponseArray} { assert.Equal(t, item, ToCrossClusterTaskResponseArray(FromCrossClusterTaskResponseArray(item))) } } func TestCrossClusterTaskRequestMapConversion(t *testing.T) { for _, item := range []map[int32][]*types.CrossClusterTaskRequest{nil, {}, testdata.CrossClusterTaskRequestMap} { assert.Equal(t, item, ToCrossClusterTaskRequestMap(FromCrossClusterTaskRequestMap(item))) } } func TestGetTaskFailedCauseMapConversion(t *testing.T) { for _, item := range []map[int32]types.GetTaskFailedCause{nil, {}, testdata.GetCrossClusterTaskFailedCauseMap} { assert.Equal(t, item, ToGetTaskFailedCauseMap(FromGetTaskFailedCauseMap(item))) } } func TestGetCrossClusterTasksRequestConversion(t *testing.T) { for _, item := range []*types.GetCrossClusterTasksRequest{nil, {}, &testdata.GetCrossClusterTasksRequest} { assert.Equal(t, item, ToAdminGetCrossClusterTasksRequest(FromAdminGetCrossClusterTasksRequest(item))) } } func TestGetCrossClusterTasksResponseConversion(t *testing.T) { for _, item := range []*types.GetCrossClusterTasksResponse{nil, {}, &testdata.GetCrossClusterTasksResponse} { assert.Equal(t, item, ToAdminGetCrossClusterTasksResponse(FromAdminGetCrossClusterTasksResponse(item))) } } func TestRespondCrossClusterTasksCompletedRequestConversion(t *testing.T) { for _, item := range []*types.RespondCrossClusterTasksCompletedRequest{nil, {}, &testdata.RespondCrossClusterTasksCompletedRequest} { assert.Equal(t, item, ToAdminRespondCrossClusterTasksCompletedRequest(FromAdminRespondCrossClusterTasksCompletedRequest(item))) } } func TestRespondCrossClusterTasksCompletedResponseConversion(t *testing.T) { for _, item := range []*types.RespondCrossClusterTasksCompletedResponse{nil, {}, &testdata.RespondCrossClusterTasksCompletedResponse} { assert.Equal(t, item, ToAdminRespondCrossClusterTasksCompletedResponse(FromAdminRespondCrossClusterTasksCompletedResponse(item))) } } func TestGetFailoverInfoRequestConversion(t *testing.T) { for _, item := range []*types.GetFailoverInfoRequest{nil, {}, &testdata.GetFailoverInfoRequest} { assert.Equal(t, item, ToGetFailoverInfoRequest(FromGetFailoverInfoRequest(item))) } } func TestGetFailoverInfoResponseConversion(t *testing.T) { for _, item := range []*types.GetFailoverInfoResponse{nil, {}, &testdata.GetFailoverInfoResponse} { assert.Equal(t, item, ToGetFailoverInfoResponse(FromGetFailoverInfoResponse(item))) } } func TestCrossClusterApplyParentClosePolicyRequestAttributesConversion(t *testing.T) { item := testdata.CrossClusterApplyParentClosePolicyRequestAttributes assert.Equal( t, &item, ToCrossClusterApplyParentClosePolicyRequestAttributes( FromCrossClusterApplyParentClosePolicyRequestAttributes(&item), ), ) } func TestApplyParentClosePolicyAttributesConversion(t *testing.T) { item := testdata.ApplyParentClosePolicyAttributes assert.Equal( t, &item, ToApplyParentClosePolicyAttributes( FromApplyParentClosePolicyAttributes(&item), ), ) } func TestApplyParentClosePolicyResultConversion(t *testing.T) { item := testdata.ApplyParentClosePolicyResult assert.Equal( t, &item, ToApplyParentClosePolicyResult( FromApplyParentClosePolicyResult(&item), ), ) } func TestCrossClusterApplyParentClosePolicyResponseConversion(t *testing.T) { item := testdata.CrossClusterApplyParentClosePolicyResponseWithChildren assert.Equal( t, &item, ToCrossClusterApplyParentClosePolicyResponseAttributes( FromCrossClusterApplyParentClosePolicyResponseAttributes(&item), ), ) } func TestIsolationGroupToDomainBlobConversion(t *testing.T) { zone1 := "zone-1" zone2 := "zone-2" drained := shared.IsolationGroupStateDrained healthy := shared.IsolationGroupStateHealthy tests := map[string]struct { in *types.IsolationGroupConfiguration expectedOut *shared.IsolationGroupConfiguration }{ "valid input": { in: &types.IsolationGroupConfiguration{ "zone-1": { Name: zone1, State: types.IsolationGroupStateDrained, }, "zone-2": { Name: zone2, State: types.IsolationGroupStateHealthy, }, }, expectedOut: &shared.IsolationGroupConfiguration{ IsolationGroups: []*shared.IsolationGroupPartition{ { Name: &zone1, State: &drained, }, { Name: &zone2, State: &healthy, }, }, }, }, "empty input": { in: &types.IsolationGroupConfiguration{}, expectedOut: &shared.IsolationGroupConfiguration{}, }, "nil input": { in: nil, expectedOut: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { out := FromIsolationGroupConfig(td.in) assert.Equal(t, td.expectedOut, out) roundTrip := ToIsolationGroupConfig(out) assert.Equal(t, td.in, roundTrip) }) } } func TestIsolationGroupFromDomainBlobConversion(t *testing.T) { zone1 := "zone-1" zone2 := "zone-2" drained := shared.IsolationGroupStateDrained healthy := shared.IsolationGroupStateHealthy tests := map[string]struct { in *shared.IsolationGroupConfiguration expectedOut *types.IsolationGroupConfiguration }{ "valid input": { in: &shared.IsolationGroupConfiguration{ IsolationGroups: []*shared.IsolationGroupPartition{ { Name: &zone1, State: &drained, }, { Name: &zone2, State: &healthy, }, }, }, expectedOut: &types.IsolationGroupConfiguration{ "zone-1": { Name: zone1, State: types.IsolationGroupStateDrained, }, "zone-2": { Name: zone2, State: types.IsolationGroupStateHealthy, }, }, }, "empty input": { in: &shared.IsolationGroupConfiguration{}, expectedOut: &types.IsolationGroupConfiguration{}, }, "nil input": { in: nil, expectedOut: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { out := ToIsolationGroupConfig(td.in) assert.Equal(t, td.expectedOut, out) roundTrip := FromIsolationGroupConfig(out) assert.Equal(t, td.in, roundTrip) }) } } func TestWorkflowExecutionInfoConversion(t *testing.T) { for _, item := range []*types.WorkflowExecutionInfo{nil, {}, &testdata.WorkflowExecutionInfo, &testdata.CronWorkflowExecutionInfo, &testdata.WorkflowExecutionInfoEphemeral} { assert.Equal(t, item, ToWorkflowExecutionInfo(FromWorkflowExecutionInfo(item))) } } func TestWorkflowExecutionInfo_MigrateTaskList(t *testing.T) { tlName := "foo" otherName := "bar" cases := []struct { name string in *shared.WorkflowExecutionInfo out *types.WorkflowExecutionInfo }{ { name: "nil", in: &shared.WorkflowExecutionInfo{}, out: &types.WorkflowExecutionInfo{ TaskList: nil, }, }, { name: "name only", in: &shared.WorkflowExecutionInfo{ TaskList: &tlName, }, out: &types.WorkflowExecutionInfo{ TaskList: &types.TaskList{Name: tlName, Kind: types.TaskListKindNormal.Ptr()}, }, }, { name: "tl only", in: &shared.WorkflowExecutionInfo{ TaskListInfo: &shared.TaskList{Name: &tlName, Kind: shared.TaskListKindNormal.Ptr()}, }, out: &types.WorkflowExecutionInfo{ TaskList: &types.TaskList{Name: tlName, Kind: types.TaskListKindNormal.Ptr()}, }, }, { name: "both", in: &shared.WorkflowExecutionInfo{ TaskList: &otherName, TaskListInfo: &shared.TaskList{Name: &tlName, Kind: shared.TaskListKindNormal.Ptr()}, }, out: &types.WorkflowExecutionInfo{ TaskList: &types.TaskList{Name: tlName, Kind: types.TaskListKindNormal.Ptr()}, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.out, ToWorkflowExecutionInfo(tc.in)) }) } } func TestActivityLocalDispatchInfoConversion(t *testing.T) { testCases := []*types.ActivityLocalDispatchInfo{ nil, {}, &testdata.ActivityLocalDispatchInfo, } for _, original := range testCases { thriftObj := FromActivityLocalDispatchInfo(original) roundTripObj := ToActivityLocalDispatchInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestActivityTaskCancelRequestedEventAttributesConversion(t *testing.T) { testCases := []*types.ActivityTaskCancelRequestedEventAttributes{ nil, {}, &testdata.ActivityTaskCancelRequestedEventAttributes, } for _, original := range testCases { thriftObj := FromActivityTaskCancelRequestedEventAttributes(original) roundTripObj := ToActivityTaskCancelRequestedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestActivityTaskCanceledEventAttributesConversion(t *testing.T) { testCases := []*types.ActivityTaskCanceledEventAttributes{ nil, {}, &testdata.ActivityTaskCanceledEventAttributes, } for _, original := range testCases { thriftObj := FromActivityTaskCanceledEventAttributes(original) roundTripObj := ToActivityTaskCanceledEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestAccessDeniedErrorConversion(t *testing.T) { testCases := []*types.AccessDeniedError{ nil, {}, &testdata.AccessDeniedError, } for _, original := range testCases { thriftObj := FromAccessDeniedError(original) roundTripObj := ToAccessDeniedError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestActivityTaskCompletedEventAttributesConversion(t *testing.T) { testCases := []*types.ActivityTaskCompletedEventAttributes{ nil, {}, &testdata.ActivityTaskCompletedEventAttributes, } for _, original := range testCases { thriftObj := FromActivityTaskCompletedEventAttributes(original) roundTripObj := ToActivityTaskCompletedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestActivityTaskFailedEventAttributesConversion(t *testing.T) { testCases := []*types.ActivityTaskFailedEventAttributes{ nil, {}, &testdata.ActivityTaskFailedEventAttributes, } for _, original := range testCases { thriftObj := FromActivityTaskFailedEventAttributes(original) roundTripObj := ToActivityTaskFailedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestActivityTaskScheduledEventAttributesConversion(t *testing.T) { testCases := []*types.ActivityTaskScheduledEventAttributes{ nil, {}, &testdata.ActivityTaskScheduledEventAttributes, } for _, original := range testCases { thriftObj := FromActivityTaskScheduledEventAttributes(original) roundTripObj := ToActivityTaskScheduledEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestActivityTaskStartedEventAttributesConversion(t *testing.T) { testCases := []*types.ActivityTaskStartedEventAttributes{ nil, {}, &testdata.ActivityTaskStartedEventAttributes, } for _, original := range testCases { thriftObj := FromActivityTaskStartedEventAttributes(original) roundTripObj := ToActivityTaskStartedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestActivityTaskTimedEventAttributesConversion(t *testing.T) { testCases := []*types.ActivityTaskTimedOutEventAttributes{ nil, {}, &testdata.ActivityTaskTimedOutEventAttributes, } for _, original := range testCases { thriftObj := FromActivityTaskTimedOutEventAttributes(original) roundTripObj := ToActivityTaskTimedOutEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestArchivalStatusConversion(t *testing.T) { enabledStatus := types.ArchivalStatus(1) disabledStatus := types.ArchivalStatus(0) testCases := []*types.ArchivalStatus{ nil, &enabledStatus, &disabledStatus, } for _, original := range testCases { thriftObj := FromArchivalStatus(original) roundTripObj := ToArchivalStatus(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestBadBinariesConversion(t *testing.T) { testCases := []*types.BadBinaries{ nil, {}, &testdata.BadBinaries, } for _, original := range testCases { thriftObj := FromBadBinaries(original) roundTripObj := ToBadBinaries(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestBadBinaryInfoConversion(t *testing.T) { testCases := []*types.BadBinaryInfo{ nil, {}, &testdata.BadBinaryInfo, } for _, original := range testCases { thriftObj := FromBadBinaryInfo(original) roundTripObj := ToBadBinaryInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestBadRequestErrorConversion(t *testing.T) { testCases := []*types.BadRequestError{ nil, {}, {Message: "Error message for bad request"}, } for _, original := range testCases { thriftObj := FromBadRequestError(original) roundTripObj := ToBadRequestError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestCancelExternalWorkflowExecutionFailedCauseConversion(t *testing.T) { testCases := []*types.CancelExternalWorkflowExecutionFailedCause{ nil, types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr(), types.CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted.Ptr(), } for _, original := range testCases { thriftObj := FromCancelExternalWorkflowExecutionFailedCause(original) roundTripObj := ToCancelExternalWorkflowExecutionFailedCause(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestCancelTimerDecisionAttributesConversion(t *testing.T) { testCases := []*types.CancelTimerDecisionAttributes{ nil, {}, &testdata.CancelTimerDecisionAttributes, } for _, original := range testCases { thriftObj := FromCancelTimerDecisionAttributes(original) roundTripObj := ToCancelTimerDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestCancelTimerFailedEventAttributesConversion(t *testing.T) { testCases := []*types.CancelTimerFailedEventAttributes{ nil, {}, &testdata.CancelTimerFailedEventAttributes, } for _, original := range testCases { thriftObj := FromCancelTimerFailedEventAttributes(original) roundTripObj := ToCancelTimerFailedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestCancelWorkflowExecutionDecisionAttributesConversion(t *testing.T) { testCases := []*types.CancelWorkflowExecutionDecisionAttributes{ nil, {}, &testdata.CancelWorkflowExecutionDecisionAttributes, } for _, original := range testCases { thriftObj := FromCancelWorkflowExecutionDecisionAttributes(original) roundTripObj := ToCancelWorkflowExecutionDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestCancellationAlreadyRequestedErrorConversion(t *testing.T) { testCases := []*types.CancellationAlreadyRequestedError{ nil, {}, &testdata.CancellationAlreadyRequestedError, } for _, original := range testCases { thriftObj := FromCancellationAlreadyRequestedError(original) roundTripObj := ToCancellationAlreadyRequestedError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestChildWorkflowExecutionCanceledEventAttributesConversion(t *testing.T) { testCases := []*types.ChildWorkflowExecutionCanceledEventAttributes{ nil, {}, &testdata.ChildWorkflowExecutionCanceledEventAttributes, } for _, original := range testCases { thriftObj := FromChildWorkflowExecutionCanceledEventAttributes(original) roundTripObj := ToChildWorkflowExecutionCanceledEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestChildWorkflowExecutionCompletedEventAttributesConversion(t *testing.T) { testCases := []*types.ChildWorkflowExecutionCompletedEventAttributes{ nil, {}, &testdata.ChildWorkflowExecutionCompletedEventAttributes, } for _, original := range testCases { thriftObj := FromChildWorkflowExecutionCompletedEventAttributes(original) roundTripObj := ToChildWorkflowExecutionCompletedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestChildWorkflowExecutionFailedEventAttributesConversion(t *testing.T) { testCases := []*types.ChildWorkflowExecutionFailedEventAttributes{ nil, {}, &testdata.ChildWorkflowExecutionFailedEventAttributes, } for _, original := range testCases { thriftObj := FromChildWorkflowExecutionFailedEventAttributes(original) roundTripObj := ToChildWorkflowExecutionFailedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestChildWorkflowExecutionStartedEventAttributesConversion(t *testing.T) { testCases := []*types.ChildWorkflowExecutionStartedEventAttributes{ nil, {}, &testdata.ChildWorkflowExecutionStartedEventAttributes, } for _, original := range testCases { thriftObj := FromChildWorkflowExecutionStartedEventAttributes(original) roundTripObj := ToChildWorkflowExecutionStartedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestChildWorkflowExecutionTerminatedEventAttributesConversion(t *testing.T) { testCases := []*types.ChildWorkflowExecutionTerminatedEventAttributes{ nil, {}, &testdata.ChildWorkflowExecutionTerminatedEventAttributes, } for _, original := range testCases { thriftObj := FromChildWorkflowExecutionTerminatedEventAttributes(original) roundTripObj := ToChildWorkflowExecutionTerminatedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestChildWorkflowExecutionTimedOutEventAttributesConversion(t *testing.T) { testCases := []*types.ChildWorkflowExecutionTimedOutEventAttributes{ nil, {}, &testdata.ChildWorkflowExecutionTimedOutEventAttributes, } for _, original := range testCases { thriftObj := FromChildWorkflowExecutionTimedOutEventAttributes(original) roundTripObj := ToChildWorkflowExecutionTimedOutEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestClientVersionNotSupportedErrorConversion(t *testing.T) { testCases := []*types.ClientVersionNotSupportedError{ nil, {}, &testdata.ClientVersionNotSupportedError, } for _, original := range testCases { thriftObj := FromClientVersionNotSupportedError(original) roundTripObj := ToClientVersionNotSupportedError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestFeatureNotEnabledErrorConversion(t *testing.T) { testCases := []*types.FeatureNotEnabledError{ nil, {}, {FeatureFlag: "test-feature-flag"}, } for _, original := range testCases { thriftObj := FromFeatureNotEnabledError(original) roundTripObj := ToFeatureNotEnabledError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestCloseShardRequestConversion(t *testing.T) { testCases := []*types.CloseShardRequest{ nil, {}, {ShardID: 5}, } for _, original := range testCases { thriftObj := FromAdminCloseShardRequest(original) roundTripObj := ToAdminCloseShardRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestClusterInfoConversion(t *testing.T) { testCases := []*types.ClusterInfo{ nil, {}, &testdata.ClusterInfo, } for _, original := range testCases { thriftObj := FromGetClusterInfoResponse(original) roundTripObj := ToGetClusterInfoResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestClusterReplicationConfigurationConversion(t *testing.T) { testCases := []*types.ClusterReplicationConfiguration{ nil, {}, &testdata.ClusterReplicationConfiguration, } for _, original := range testCases { thriftObj := FromClusterReplicationConfiguration(original) roundTripObj := ToClusterReplicationConfiguration(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestCompleteWorkflowExecutionDecisionAttributesConversion(t *testing.T) { testCases := []*types.CompleteWorkflowExecutionDecisionAttributes{ nil, {}, &testdata.CompleteWorkflowExecutionDecisionAttributes, } for _, original := range testCases { thriftObj := FromCompleteWorkflowExecutionDecisionAttributes(original) roundTripObj := ToCompleteWorkflowExecutionDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestContinueAsNewInitiatorConversion(t *testing.T) { testCases := []*types.ContinueAsNewInitiator{ nil, types.ContinueAsNewInitiatorDecider.Ptr(), types.ContinueAsNewInitiatorRetryPolicy.Ptr(), types.ContinueAsNewInitiatorCronSchedule.Ptr(), } for _, original := range testCases { thriftObj := FromContinueAsNewInitiator(original) roundTripObj := ToContinueAsNewInitiator(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestContinueAsNewWorkflowExecutionDecisionAttributesConversion(t *testing.T) { testCases := []*types.ContinueAsNewWorkflowExecutionDecisionAttributes{ nil, {}, &testdata.ContinueAsNewWorkflowExecutionDecisionAttributes, } for _, original := range testCases { thriftObj := FromContinueAsNewWorkflowExecutionDecisionAttributes(original) roundTripObj := ToContinueAsNewWorkflowExecutionDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestCountWorkflowExecutionsRequestConversion(t *testing.T) { testCases := []*types.CountWorkflowExecutionsRequest{ nil, {}, &testdata.CountWorkflowExecutionsRequest, } for _, original := range testCases { thriftObj := FromCountWorkflowExecutionsRequest(original) roundTripObj := ToCountWorkflowExecutionsRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestCountWorkflowExecutionsResponseConversion(t *testing.T) { testCases := []*types.CountWorkflowExecutionsResponse{ nil, {}, &testdata.CountWorkflowExecutionsResponse, } for _, original := range testCases { thriftObj := FromCountWorkflowExecutionsResponse(original) roundTripObj := ToCountWorkflowExecutionsResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestCurrentBranchChangedErrorConversion(t *testing.T) { testCases := []*types.CurrentBranchChangedError{ nil, {}, &testdata.CurrentBranchChangedError, } for _, original := range testCases { thriftObj := FromCurrentBranchChangedError(original) roundTripObj := ToCurrentBranchChangedError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDataBlobConversion(t *testing.T) { testCases := []*types.DataBlob{ nil, {}, &testdata.DataBlob, } for _, original := range testCases { thriftObj := FromDataBlob(original) roundTripObj := ToDataBlob(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDecisionConverson(t *testing.T) { testCases := []*types.Decision{ nil, {}, { DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &testdata.ScheduleActivityTaskDecisionAttributes, }, } for _, original := range testCases { thriftObj := FromDecision(original) roundTripObj := ToDecision(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDecisionTaskCompletedEventAttributesConversion(t *testing.T) { testCases := []*types.DecisionTaskCompletedEventAttributes{ nil, {}, &testdata.DecisionTaskCompletedEventAttributes, } for _, original := range testCases { thriftObj := FromDecisionTaskCompletedEventAttributes(original) roundTripObj := ToDecisionTaskCompletedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDecisionTaskFailedCauseConversion(t *testing.T) { testCases := []*types.DecisionTaskFailedCause{ nil, types.DecisionTaskFailedCauseUnhandledDecision.Ptr(), types.DecisionTaskFailedCauseBadScheduleActivityAttributes.Ptr(), types.DecisionTaskFailedCauseBadRequestCancelActivityAttributes.Ptr(), types.DecisionTaskFailedCauseBadStartTimerAttributes.Ptr(), types.DecisionTaskFailedCauseBadCancelTimerAttributes.Ptr(), types.DecisionTaskFailedCauseBadRecordMarkerAttributes.Ptr(), types.DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseBadContinueAsNewAttributes.Ptr(), types.DecisionTaskFailedCauseStartTimerDuplicateID.Ptr(), types.DecisionTaskFailedCauseResetStickyTasklist.Ptr(), types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure.Ptr(), types.DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseBadStartChildExecutionAttributes.Ptr(), types.DecisionTaskFailedCauseForceCloseDecision.Ptr(), types.DecisionTaskFailedCauseFailoverCloseDecision.Ptr(), types.DecisionTaskFailedCauseBadSignalInputSize.Ptr(), types.DecisionTaskFailedCauseResetWorkflow.Ptr(), types.DecisionTaskFailedCauseBadBinary.Ptr(), types.DecisionTaskFailedCauseScheduleActivityDuplicateID.Ptr(), types.DecisionTaskFailedCauseBadSearchAttributes.Ptr(), } for _, original := range testCases { thriftObj := FromDecisionTaskFailedCause(original) roundTripObj := ToDecisionTaskFailedCause(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDecisionTaskFailedEventAttributesConversion(t *testing.T) { testCases := []*types.DecisionTaskFailedEventAttributes{ nil, {}, &testdata.DecisionTaskFailedEventAttributes, } for _, original := range testCases { thriftObj := FromDecisionTaskFailedEventAttributes(original) roundTripObj := ToDecisionTaskFailedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDecisionTaskScheduledEventAttributesConversion(t *testing.T) { testCases := []*types.DecisionTaskScheduledEventAttributes{ nil, {}, &testdata.DecisionTaskScheduledEventAttributes, } for _, original := range testCases { thriftObj := FromDecisionTaskScheduledEventAttributes(original) roundTripObj := ToDecisionTaskScheduledEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDecisionTaskStartedEventAttributesConversion(t *testing.T) { testCases := []*types.DecisionTaskStartedEventAttributes{ nil, {}, &testdata.DecisionTaskStartedEventAttributes, } for _, original := range testCases { thriftObj := FromDecisionTaskStartedEventAttributes(original) roundTripObj := ToDecisionTaskStartedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDecisionTaskTimedOutCauseConversion(t *testing.T) { testCases := []*types.DecisionTaskTimedOutCause{ nil, types.DecisionTaskTimedOutCauseTimeout.Ptr(), types.DecisionTaskTimedOutCauseReset.Ptr(), } for _, original := range testCases { thriftObj := FromDecisionTaskTimedOutCause(original) roundTripObj := ToDecisionTaskTimedOutCause(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDecisionTaskTimedOutEventAttributesConversion(t *testing.T) { testCases := []*types.DecisionTaskTimedOutEventAttributes{ nil, {}, &testdata.DecisionTaskTimedOutEventAttributes, } for _, original := range testCases { thriftObj := FromDecisionTaskTimedOutEventAttributes(original) roundTripObj := ToDecisionTaskTimedOutEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDecisionTypeConversion(t *testing.T) { testCases := []*types.DecisionType{ nil, types.DecisionTypeScheduleActivityTask.Ptr(), types.DecisionTypeRequestCancelActivityTask.Ptr(), types.DecisionTypeStartTimer.Ptr(), types.DecisionTypeCompleteWorkflowExecution.Ptr(), types.DecisionTypeFailWorkflowExecution.Ptr(), types.DecisionTypeCancelTimer.Ptr(), types.DecisionTypeCancelWorkflowExecution.Ptr(), types.DecisionTypeRequestCancelExternalWorkflowExecution.Ptr(), types.DecisionTypeRecordMarker.Ptr(), types.DecisionTypeContinueAsNewWorkflowExecution.Ptr(), types.DecisionTypeStartChildWorkflowExecution.Ptr(), types.DecisionTypeSignalExternalWorkflowExecution.Ptr(), types.DecisionTypeUpsertWorkflowSearchAttributes.Ptr(), } for _, original := range testCases { thriftObj := FromDecisionType(original) roundTripObj := ToDecisionType(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDeleteDomainRequestConversion(t *testing.T) { testCases := []*types.DeleteDomainRequest{ nil, {}, &testdata.DeleteDomainRequest, } for _, original := range testCases { thriftObj := FromDeleteDomainRequest(original) roundTripObj := ToDeleteDomainRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDeprecateDomainRequestConversion(t *testing.T) { testCases := []*types.DeprecateDomainRequest{ nil, {}, &testdata.DeprecateDomainRequest, } for _, original := range testCases { thriftObj := FromDeprecateDomainRequest(original) roundTripObj := ToDeprecateDomainRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeDomainRequestConversion(t *testing.T) { testCases := []*types.DescribeDomainRequest{ nil, {}, {Name: common.StringPtr("test-name"), UUID: common.StringPtr("test-uuid")}, } for _, original := range testCases { thriftObj := FromDescribeDomainRequest(original) roundTripObj := ToDescribeDomainRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeDomainResponseConversion(t *testing.T) { testCases := []*types.DescribeDomainResponse{ nil, {}, &testdata.DescribeDomainResponse, } for _, original := range testCases { thriftObj := FromDescribeDomainResponse(original) roundTripObj := ToDescribeDomainResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDecribeHistoryHostRequstConversion(t *testing.T) { testCases := []*types.DescribeHistoryHostRequest{ nil, {}, {HostAddress: common.StringPtr("test-host-address")}, } for _, original := range testCases { thriftObj := FromAdminDescribeHistoryHostRequest(original) roundTripObj := ToAdminDescribeHistoryHostRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeShardDistributionRequestConversion(t *testing.T) { testCases := []*types.DescribeShardDistributionRequest{ nil, {}, {PageSize: 100}, } for _, original := range testCases { thriftObj := FromAdminDescribeShardDistributionRequest(original) roundTripObj := ToAdminDescribeShardDistributionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeHistoryHostResponseConversion(t *testing.T) { testCases := []*types.DescribeHistoryHostResponse{ nil, {}, {NumberOfShards: 10, ShardIDs: []int32{1, 2, 3}, Address: "test-address"}, } for _, original := range testCases { thriftObj := FromAdminDescribeHistoryHostResponse(original) roundTripObj := ToAdminDescribeHistoryHostResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeShardDistributionResponseConversion(t *testing.T) { testCases := []*types.DescribeShardDistributionResponse{ nil, {}, {NumberOfShards: 10, Shards: map[int32]string{1: "test-host-1", 2: "test-host-2"}}, } for _, original := range testCases { thriftObj := FromAdminDescribeShardDistributionResponse(original) roundTripObj := ToAdminDescribeShardDistributionResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeQueueRequestConversion(t *testing.T) { testCases := []*types.DescribeQueueRequest{ nil, {}, {ShardID: 10}, } for _, original := range testCases { thriftObj := FromAdminDescribeQueueRequest(original) roundTripObj := ToAdminDescribeQueueRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeQueueResponseConversion(t *testing.T) { testCases := []*types.DescribeQueueResponse{ nil, {}, &testdata.HistoryDescribeQueueResponse, } for _, original := range testCases { thriftObj := FromAdminDescribeQueueResponse(original) roundTripObj := ToAdminDescribeQueueResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeTaskListRequestConversion(t *testing.T) { testCases := []*types.DescribeTaskListRequest{ nil, {}, &testdata.DescribeTaskListRequest, } for _, original := range testCases { thriftObj := FromDescribeTaskListRequest(original) roundTripObj := ToDescribeTaskListRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeTaskListResponseConversion(t *testing.T) { testCases := []*types.DescribeTaskListResponse{ nil, {}, &testdata.DescribeTaskListResponse, } for _, original := range testCases { thriftObj := FromDescribeTaskListResponse(original) roundTripObj := ToDescribeTaskListResponse(thriftObj) opt := cmpopts.IgnoreFields(types.DescribeTaskListResponse{}, "PartitionConfig") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestDescribeWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.DescribeWorkflowExecutionRequest{ nil, {}, &testdata.DescribeWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromDescribeWorkflowExecutionRequest(original) roundTripObj := ToDescribeWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeWorkflowExecutionResponseConversion(t *testing.T) { testCases := []*types.DescribeWorkflowExecutionResponse{ nil, {}, &testdata.DescribeWorkflowExecutionResponse, } for _, original := range testCases { thriftObj := FromDescribeWorkflowExecutionResponse(original) roundTripObj := ToDescribeWorkflowExecutionResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDiagnoseWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.DiagnoseWorkflowExecutionRequest{ nil, {}, &testdata.DiagnoseWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromDiagnoseWorkflowExecutionRequest(original) roundTripObj := ToDiagnoseWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDiagnoseWorkflowExecutionResponseConversion(t *testing.T) { testCases := []*types.DiagnoseWorkflowExecutionResponse{ nil, {}, &testdata.DiagnoseWorkflowExecutionResponse, } for _, original := range testCases { thriftObj := FromDiagnoseWorkflowExecutionResponse(original) roundTripObj := ToDiagnoseWorkflowExecutionResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDomainAlreadyExistsErrorConversion(t *testing.T) { testCases := []*types.DomainAlreadyExistsError{ nil, {}, {Message: "test-message"}, } for _, original := range testCases { thriftObj := FromDomainAlreadyExistsError(original) roundTripObj := ToDomainAlreadyExistsError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDomainCacheInfoConversion(t *testing.T) { testCases := []*types.DomainCacheInfo{ nil, {}, &testdata.DomainCacheInfo, } for _, original := range testCases { thriftObj := FromDomainCacheInfo(original) roundTripObj := ToDomainCacheInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDomainConfigurationConversion(t *testing.T) { testCases := []*types.DomainConfiguration{ nil, {}, &testdata.DomainConfiguration, } for _, original := range testCases { thriftObj := FromDomainConfiguration(original) roundTripObj := ToDomainConfiguration(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDomainInfoConversion(t *testing.T) { testCases := []*types.DomainInfo{ nil, {}, &testdata.DomainInfo, } for _, original := range testCases { thriftObj := FromDomainInfo(original) roundTripObj := ToDomainInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestFailoverInfoConversion(t *testing.T) { testCases := []*types.FailoverInfo{ nil, {}, &testdata.FailoverInfo, } for _, original := range testCases { thriftObj := FromFailoverInfo(original) roundTripObj := ToFailoverInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDomainNotActiveErrorConversion(t *testing.T) { testCases := []*types.DomainNotActiveError{ nil, {}, {Message: "test-message"}, {Message: "test-message", DomainName: "test-domain", CurrentCluster: "test-current-cluster", ActiveCluster: "test-active-cluster"}, {Message: "test-message", DomainName: "test-domain", CurrentCluster: "test-current-cluster", ActiveClusters: []string{"test-active-cluster-1", "test-active-cluster-2"}}, } for _, original := range testCases { thriftObj := FromDomainNotActiveError(original) roundTripObj := ToDomainNotActiveError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDomainReplicationConfigurationConversion(t *testing.T) { testCases := []*types.DomainReplicationConfiguration{ nil, {}, &testdata.DomainReplicationConfiguration, } for _, original := range testCases { thriftObj := FromDomainReplicationConfiguration(original) roundTripObj := ToDomainReplicationConfiguration(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDomainStatusConversion(t *testing.T) { testCases := []*types.DomainStatus{ nil, types.DomainStatusRegistered.Ptr(), types.DomainStatusDeprecated.Ptr(), types.DomainStatusDeleted.Ptr(), } for _, original := range testCases { thriftObj := FromDomainStatus(original) roundTripObj := ToDomainStatus(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestEncodingTypeConversion(t *testing.T) { testCases := []*types.EncodingType{ nil, types.EncodingTypeThriftRW.Ptr(), types.EncodingTypeJSON.Ptr(), } for _, original := range testCases { thriftObj := FromEncodingType(original) roundTripObj := ToEncodingType(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestEntityNotExistsErrorConversion(t *testing.T) { testCases := []*types.EntityNotExistsError{ nil, {}, &testdata.EntityNotExistsError, } for _, original := range testCases { thriftObj := FromEntityNotExistsError(original) roundTripObj := ToEntityNotExistsError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestWorkflowExecutionAlreadyCompletedErrorConversion(t *testing.T) { testCases := []*types.WorkflowExecutionAlreadyCompletedError{ nil, {}, &testdata.WorkflowExecutionAlreadyCompletedError, } for _, original := range testCases { thriftObj := FromWorkflowExecutionAlreadyCompletedError(original) roundTripObj := ToWorkflowExecutionAlreadyCompletedError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestEventTypeConversion(t *testing.T) { testCases := []*types.EventType{ nil, types.EventTypeWorkflowExecutionStarted.Ptr(), types.EventTypeWorkflowExecutionCompleted.Ptr(), types.EventTypeWorkflowExecutionFailed.Ptr(), types.EventTypeWorkflowExecutionTimedOut.Ptr(), types.EventTypeDecisionTaskScheduled.Ptr(), types.EventTypeDecisionTaskStarted.Ptr(), types.EventTypeDecisionTaskCompleted.Ptr(), types.EventTypeDecisionTaskTimedOut.Ptr(), types.EventTypeDecisionTaskFailed.Ptr(), types.EventTypeActivityTaskScheduled.Ptr(), types.EventTypeActivityTaskStarted.Ptr(), types.EventTypeActivityTaskCompleted.Ptr(), types.EventTypeActivityTaskFailed.Ptr(), types.EventTypeActivityTaskTimedOut.Ptr(), types.EventTypeActivityTaskCancelRequested.Ptr(), types.EventTypeActivityTaskCanceled.Ptr(), types.EventTypeTimerStarted.Ptr(), types.EventTypeTimerFired.Ptr(), types.EventTypeCancelTimerFailed.Ptr(), types.EventTypeTimerCanceled.Ptr(), types.EventTypeWorkflowExecutionCancelRequested.Ptr(), types.EventTypeWorkflowExecutionCanceled.Ptr(), types.EventTypeRequestCancelExternalWorkflowExecutionInitiated.Ptr(), types.EventTypeRequestCancelExternalWorkflowExecutionFailed.Ptr(), types.EventTypeExternalWorkflowExecutionCancelRequested.Ptr(), types.EventTypeMarkerRecorded.Ptr(), types.EventTypeWorkflowExecutionSignaled.Ptr(), types.EventTypeWorkflowExecutionTerminated.Ptr(), types.EventTypeWorkflowExecutionContinuedAsNew.Ptr(), types.EventTypeStartChildWorkflowExecutionInitiated.Ptr(), types.EventTypeStartChildWorkflowExecutionFailed.Ptr(), types.EventTypeChildWorkflowExecutionStarted.Ptr(), types.EventTypeChildWorkflowExecutionCompleted.Ptr(), types.EventTypeChildWorkflowExecutionFailed.Ptr(), types.EventTypeChildWorkflowExecutionCanceled.Ptr(), types.EventTypeChildWorkflowExecutionTimedOut.Ptr(), types.EventTypeChildWorkflowExecutionTerminated.Ptr(), types.EventTypeSignalExternalWorkflowExecutionInitiated.Ptr(), types.EventTypeSignalExternalWorkflowExecutionFailed.Ptr(), types.EventTypeExternalWorkflowExecutionSignaled.Ptr(), types.EventTypeUpsertWorkflowSearchAttributes.Ptr(), } for _, original := range testCases { thriftObj := FromEventType(original) roundTripObj := ToEventType(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestExternalWorkflowExecutionSignaledEventAttributesConversion(t *testing.T) { testCases := []*types.ExternalWorkflowExecutionSignaledEventAttributes{ nil, {}, &testdata.ExternalWorkflowExecutionSignaledEventAttributes, } for _, original := range testCases { thriftObj := FromExternalWorkflowExecutionSignaledEventAttributes(original) roundTripObj := ToExternalWorkflowExecutionSignaledEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestFailWorkflowExecutionDecisionAttributesConversion(t *testing.T) { testCases := []*types.FailWorkflowExecutionDecisionAttributes{ nil, {}, &testdata.FailWorkflowExecutionDecisionAttributes, } for _, original := range testCases { thriftObj := FromFailWorkflowExecutionDecisionAttributes(original) roundTripObj := ToFailWorkflowExecutionDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestGetSearchAttributesResponseConversion(t *testing.T) { testCases := []*types.GetSearchAttributesResponse{ nil, {}, &testdata.GetSearchAttributesResponse, } for _, original := range testCases { thriftObj := FromGetSearchAttributesResponse(original) roundTripObj := ToGetSearchAttributesResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestGetWorkflowExecutionHistoryRequestConversion(t *testing.T) { testCases := []*types.GetWorkflowExecutionHistoryRequest{ nil, {}, &testdata.GetWorkflowExecutionHistoryRequest, } for _, original := range testCases { thriftObj := FromGetWorkflowExecutionHistoryRequest(original) roundTripObj := ToGetWorkflowExecutionHistoryRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestGetWorkflowExecutionHistoryResponseConvesion(t *testing.T) { testCases := []*types.GetWorkflowExecutionHistoryResponse{ nil, {}, &testdata.GetWorkflowExecutionHistoryResponse, } for _, original := range testCases { thriftObj := FromGetWorkflowExecutionHistoryResponse(original) roundTripObj := ToGetWorkflowExecutionHistoryResponse(thriftObj) opt := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestHeaderConversion(t *testing.T) { testCases := []*types.Header{ nil, {}, &testdata.Header, } for _, original := range testCases { thriftObj := FromHeader(original) roundTripObj := ToHeader(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryConversion(t *testing.T) { testCases := []*types.History{ nil, {}, &testdata.History, } for _, original := range testCases { thriftObj := FromHistory(original) roundTripObj := ToHistory(thriftObj) opt := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestHistoryBranchConversion(t *testing.T) { testCases := []*types.HistoryBranch{ nil, {}, {TreeID: "test-tree-id", BranchID: "test-branch-id"}, } for _, original := range testCases { thriftObj := FromHistoryBranch(original) roundTripObj := ToHistoryBranch(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryBranchRangeConversion(t *testing.T) { testCases := []*types.HistoryBranchRange{ nil, {}, {BranchID: "test-branch-id", BeginNodeID: 5, EndNodeID: 10}, } for _, original := range testCases { thriftObj := FromHistoryBranchRange(original) roundTripObj := ToHistoryBranchRange(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryEventConversion(t *testing.T) { testCases := []*types.HistoryEvent{ nil, {}, { ID: 5, Timestamp: common.Int64Ptr(10), EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &testdata.WorkflowExecutionStartedEventAttributes, }, } for _, original := range testCases { thriftObj := FromHistoryEvent(original) roundTripObj := ToHistoryEvent(thriftObj) opt := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestHistoryEventFilterTypeConversion(t *testing.T) { testCases := []*types.HistoryEventFilterType{ nil, types.HistoryEventFilterTypeAllEvent.Ptr(), types.HistoryEventFilterTypeCloseEvent.Ptr(), } for _, original := range testCases { thriftObj := FromHistoryEventFilterType(original) roundTripObj := ToHistoryEventFilterType(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestIndexedValueTypeConversion(t *testing.T) { testCases := []types.IndexedValueType{ types.IndexedValueTypeString, types.IndexedValueTypeKeyword, types.IndexedValueTypeInt, types.IndexedValueTypeDouble, types.IndexedValueTypeBool, types.IndexedValueTypeDatetime, } for _, original := range testCases { thriftObj := FromIndexedValueType(original) roundTripObj := ToIndexedValueType(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestInternalDataInconsistencyErrorConversion(t *testing.T) { testCases := []*types.InternalDataInconsistencyError{ nil, {}, &testdata.InternalDataInconsistencyError, } for _, original := range testCases { thriftObj := FromInternalDataInconsistencyError(original) roundTripObj := ToInternalDataInconsistencyError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestInternalServiceErrorConversion(t *testing.T) { testCases := []*types.InternalServiceError{ nil, {}, &testdata.InternalServiceError, } for _, original := range testCases { thriftObj := FromInternalServiceError(original) roundTripObj := ToInternalServiceError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestLimitExceededErrorConversion(t *testing.T) { testCases := []*types.LimitExceededError{ nil, {}, &testdata.LimitExceededError, } for _, original := range testCases { thriftObj := FromLimitExceededError(original) roundTripObj := ToLimitExceededError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListArchivedWorkflowExecutionsRequestConversion(t *testing.T) { testCases := []*types.ListArchivedWorkflowExecutionsRequest{ nil, {}, &testdata.ListArchivedWorkflowExecutionsRequest, } for _, original := range testCases { thriftObj := FromListArchivedWorkflowExecutionsRequest(original) roundTripObj := ToListArchivedWorkflowExecutionsRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListArchivedWorkflowExecutionsResponseConversion(t *testing.T) { testCases := []*types.ListArchivedWorkflowExecutionsResponse{ nil, {}, &testdata.ListArchivedWorkflowExecutionsResponse, } for _, original := range testCases { thriftObj := FromListArchivedWorkflowExecutionsResponse(original) roundTripObj := ToListArchivedWorkflowExecutionsResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListClosedWorkflowExecutionsRequestConversion(t *testing.T) { testCases := []*types.ListClosedWorkflowExecutionsRequest{ nil, {}, {Domain: "test-domain", MaximumPageSize: 100, NextPageToken: []byte("test-next-page-token")}, } for _, original := range testCases { thriftObj := FromListClosedWorkflowExecutionsRequest(original) roundTripObj := ToListClosedWorkflowExecutionsRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListClosedWorkflowExecutionsResponseConversion(t *testing.T) { testCases := []*types.ListClosedWorkflowExecutionsResponse{ nil, {}, &testdata.ListClosedWorkflowExecutionsResponse, } for _, original := range testCases { thriftObj := FromListClosedWorkflowExecutionsResponse(original) roundTripObj := ToListClosedWorkflowExecutionsResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListDomainsRequestConversion(t *testing.T) { testCases := []*types.ListDomainsRequest{ nil, {}, &testdata.ListDomainsRequest, } for _, original := range testCases { thriftObj := FromListDomainsRequest(original) roundTripObj := ToListDomainsRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListDomainsResponseConversion(t *testing.T) { testCases := []*types.ListDomainsResponse{ nil, {}, &testdata.ListDomainsResponse, } for _, original := range testCases { thriftObj := FromListDomainsResponse(original) roundTripObj := ToListDomainsResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListOpenWorkflowExecutionsRequestConversion(t *testing.T) { testCases := []*types.ListOpenWorkflowExecutionsRequest{ nil, {}, {Domain: "test-domain", MaximumPageSize: 100, NextPageToken: []byte("test-next-page-token")}, } for _, original := range testCases { thriftObj := FromListOpenWorkflowExecutionsRequest(original) roundTripObj := ToListOpenWorkflowExecutionsRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListOpenWorkflowExecutionsResponseConversion(t *testing.T) { testCases := []*types.ListOpenWorkflowExecutionsResponse{ nil, {}, &testdata.ListOpenWorkflowExecutionsResponse, } for _, original := range testCases { thriftObj := FromListOpenWorkflowExecutionsResponse(original) roundTripObj := ToListOpenWorkflowExecutionsResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListTaskListPartitionsRequestConversion(t *testing.T) { testCases := []*types.ListTaskListPartitionsRequest{ nil, {}, &testdata.ListTaskListPartitionsRequest, } for _, original := range testCases { thriftObj := FromListTaskListPartitionsRequest(original) roundTripObj := ToListTaskListPartitionsRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListTaskListPartitionsResponseConversion(t *testing.T) { testCases := []*types.ListTaskListPartitionsResponse{ nil, {}, &testdata.ListTaskListPartitionsResponse, } for _, original := range testCases { thriftObj := FromListTaskListPartitionsResponse(original) roundTripObj := ToListTaskListPartitionsResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestGetTaskListsByDomainRequestConversion(t *testing.T) { testCases := []*types.GetTaskListsByDomainRequest{ nil, {}, &testdata.MatchingGetTaskListsByDomainRequest, } for _, original := range testCases { thriftObj := FromGetTaskListsByDomainRequest(original) roundTripObj := ToGetTaskListsByDomainRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestGetTaskListsByDomainResponseConversion(t *testing.T) { testCases := []*types.GetTaskListsByDomainResponse{ nil, {}, &testdata.GetTaskListsByDomainResponse, } for _, original := range testCases { thriftObj := FromGetTaskListsByDomainResponse(original) roundTripObj := ToGetTaskListsByDomainResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestDescribeTaskListResponseMapConversion(t *testing.T) { testCases := []map[string]*types.DescribeTaskListResponse{ nil, {}, {"test-key": &testdata.DescribeTaskListResponse}, } for _, original := range testCases { thriftObj := FromDescribeTaskListResponseMap(original) roundTripObj := ToDescribeTaskListResponseMap(thriftObj) opt := cmpopts.IgnoreFields(types.DescribeTaskListResponse{}, "PartitionConfig") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestListWorkflowExecutionsRequestConversion(t *testing.T) { testCases := []*types.ListWorkflowExecutionsRequest{ nil, {}, &testdata.ListWorkflowExecutionsRequest, } for _, original := range testCases { thriftObj := FromListWorkflowExecutionsRequest(original) roundTripObj := ToListWorkflowExecutionsRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListWorkflowExecutionsResponseConversion(t *testing.T) { testCases := []*types.ListWorkflowExecutionsResponse{ nil, {}, &testdata.ListWorkflowExecutionsResponse, } for _, original := range testCases { thriftObj := FromListWorkflowExecutionsResponse(original) roundTripObj := ToListWorkflowExecutionsResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestMarkerRecordedEventAttributesConversion(t *testing.T) { testCases := []*types.MarkerRecordedEventAttributes{ nil, {}, &testdata.MarkerRecordedEventAttributes, } for _, original := range testCases { thriftObj := FromMarkerRecordedEventAttributes(original) roundTripObj := ToMarkerRecordedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestMemoConversion(t *testing.T) { testCases := []*types.Memo{ nil, {}, &testdata.Memo, } for _, original := range testCases { thriftObj := FromMemo(original) roundTripObj := ToMemo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestParentClosePolicyConversion(t *testing.T) { testCases := []*types.ParentClosePolicy{ nil, types.ParentClosePolicyAbandon.Ptr(), types.ParentClosePolicyRequestCancel.Ptr(), types.ParentClosePolicyTerminate.Ptr(), } for _, original := range testCases { thriftObj := FromParentClosePolicy(original) roundTripObj := ToParentClosePolicy(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPendingActivityInfoConversion(t *testing.T) { testCases := []*types.PendingActivityInfo{ nil, {}, &testdata.PendingActivityInfo, } for _, original := range testCases { thriftObj := FromPendingActivityInfo(original) roundTripObj := ToPendingActivityInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPendingActivityStateConversion(t *testing.T) { testCases := []*types.PendingActivityState{ nil, types.PendingActivityStateScheduled.Ptr(), types.PendingActivityStateStarted.Ptr(), types.PendingActivityStateCancelRequested.Ptr(), } for _, original := range testCases { thriftObj := FromPendingActivityState(original) roundTripObj := ToPendingActivityState(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPendingChildExecutionInfoConversion(t *testing.T) { testCases := []*types.PendingChildExecutionInfo{ nil, {}, &testdata.PendingChildExecutionInfo, } for _, original := range testCases { thriftObj := FromPendingChildExecutionInfo(original) roundTripObj := ToPendingChildExecutionInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPendingDecisionInfoConversion(t *testing.T) { testCases := []*types.PendingDecisionInfo{ nil, {}, &testdata.PendingDecisionInfo, } for _, original := range testCases { thriftObj := FromPendingDecisionInfo(original) roundTripObj := ToPendingDecisionInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPendingDecisionStateConversion(t *testing.T) { testCases := []*types.PendingDecisionState{ nil, types.PendingDecisionStateScheduled.Ptr(), types.PendingDecisionStateStarted.Ptr(), } for _, original := range testCases { thriftObj := FromPendingDecisionState(original) roundTripObj := ToPendingDecisionState(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPollForActivityTaskRequestConversion(t *testing.T) { testCases := []*types.PollForActivityTaskRequest{ nil, {}, &testdata.PollForActivityTaskRequest, } for _, original := range testCases { thriftObj := FromPollForActivityTaskRequest(original) roundTripObj := ToPollForActivityTaskRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPollForActivityTaskResponseConversion(t *testing.T) { testCases := []*types.PollForActivityTaskResponse{ nil, {}, &testdata.PollForActivityTaskResponse, } for _, original := range testCases { thriftObj := FromPollForActivityTaskResponse(original) roundTripObj := ToPollForActivityTaskResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPollForDecisionTaskRequestConversion(t *testing.T) { testCases := []*types.PollForDecisionTaskRequest{ nil, {}, &testdata.PollForDecisionTaskRequest, } for _, original := range testCases { thriftObj := FromPollForDecisionTaskRequest(original) roundTripObj := ToPollForDecisionTaskRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestPollForDecisionTaskResponseConversion(t *testing.T) { testCases := []*types.PollForDecisionTaskResponse{ nil, {}, &testdata.PollForDecisionTaskResponse, } for _, original := range testCases { thriftObj := FromPollForDecisionTaskResponse(original) roundTripObj := ToPollForDecisionTaskResponse(thriftObj) opt := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestPollerInfoConversion(t *testing.T) { testCases := []*types.PollerInfo{ nil, {}, &testdata.PollerInfo, } for _, original := range testCases { thriftObj := FromPollerInfo(original) roundTripObj := ToPollerInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestQueryConsistencyLevelConversion(t *testing.T) { testCases := []*types.QueryConsistencyLevel{ nil, types.QueryConsistencyLevelEventual.Ptr(), types.QueryConsistencyLevelStrong.Ptr(), } for _, original := range testCases { thriftObj := FromQueryConsistencyLevel(original) roundTripObj := ToQueryConsistencyLevel(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestQueryFailedErrorConversion(t *testing.T) { testCases := []*types.QueryFailedError{ nil, {}, &testdata.QueryFailedError, } for _, original := range testCases { thriftObj := FromQueryFailedError(original) roundTripObj := ToQueryFailedError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestQueryRejectConditionConversion(t *testing.T) { testCases := []*types.QueryRejectCondition{ nil, types.QueryRejectConditionNotOpen.Ptr(), types.QueryRejectConditionNotCompletedCleanly.Ptr(), } for _, original := range testCases { thriftObj := FromQueryRejectCondition(original) roundTripObj := ToQueryRejectCondition(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestQueryRejectedConversion(t *testing.T) { testCases := []*types.QueryRejected{ nil, {}, &testdata.QueryRejected, } for _, original := range testCases { thriftObj := FromQueryRejected(original) roundTripObj := ToQueryRejected(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestQueryResultTypeConversion(t *testing.T) { testCases := []*types.QueryResultType{ nil, types.QueryResultTypeAnswered.Ptr(), types.QueryResultTypeFailed.Ptr(), } for _, original := range testCases { thriftObj := FromQueryResultType(original) roundTripObj := ToQueryResultType(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestQueryTaskCompletedTypeConversion(t *testing.T) { testCases := []*types.QueryTaskCompletedType{ nil, types.QueryTaskCompletedTypeCompleted.Ptr(), types.QueryTaskCompletedTypeFailed.Ptr(), } for _, original := range testCases { thriftObj := FromQueryTaskCompletedType(original) roundTripObj := ToQueryTaskCompletedType(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestQueryWorkflowRequestConversion(t *testing.T) { testCases := []*types.QueryWorkflowRequest{ nil, {}, &testdata.QueryWorkflowRequest, } for _, original := range testCases { thriftObj := FromQueryWorkflowRequest(original) roundTripObj := ToQueryWorkflowRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestQueryWorkflowResponseConversion(t *testing.T) { testCases := []*types.QueryWorkflowResponse{ nil, {}, &testdata.QueryWorkflowResponse, } for _, original := range testCases { thriftObj := FromQueryWorkflowResponse(original) roundTripObj := ToQueryWorkflowResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestReapplyEventsRequestConversion(t *testing.T) { testCases := []*types.ReapplyEventsRequest{ nil, {}, {DomainName: "test-domain-name", WorkflowExecution: &testdata.WorkflowExecution}, } for _, original := range testCases { thriftObj := FromAdminReapplyEventsRequest(original) roundTripObj := ToAdminReapplyEventsRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRecordActivityTaskHeartbeatByIDRequestConversion(t *testing.T) { testCases := []*types.RecordActivityTaskHeartbeatByIDRequest{ nil, {}, &testdata.RecordActivityTaskHeartbeatByIDRequest, } for _, original := range testCases { thriftObj := FromRecordActivityTaskHeartbeatByIDRequest(original) roundTripObj := ToRecordActivityTaskHeartbeatByIDRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRecordActivityTaskHeartbeatRequestConversion(t *testing.T) { testCases := []*types.RecordActivityTaskHeartbeatRequest{ nil, {}, &testdata.RecordActivityTaskHeartbeatRequest, } for _, original := range testCases { thriftObj := FromRecordActivityTaskHeartbeatRequest(original) roundTripObj := ToRecordActivityTaskHeartbeatRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRecordActivityTaskHeartbeatResponseConversion(t *testing.T) { testCases := []*types.RecordActivityTaskHeartbeatResponse{ nil, {}, &testdata.RecordActivityTaskHeartbeatResponse, } for _, original := range testCases { thriftObj := FromRecordActivityTaskHeartbeatResponse(original) roundTripObj := ToRecordActivityTaskHeartbeatResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRecordMarkerDecisionAttributesConversion(t *testing.T) { testCases := []*types.RecordMarkerDecisionAttributes{ nil, {}, &testdata.RecordMarkerDecisionAttributes, } for _, original := range testCases { thriftObj := FromRecordMarkerDecisionAttributes(original) roundTripObj := ToRecordMarkerDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRefreshWorkflowTasksRequestConversion(t *testing.T) { testCases := []*types.RefreshWorkflowTasksRequest{ nil, {}, {Domain: "test-domain", Execution: &testdata.WorkflowExecution}, } for _, original := range testCases { thriftObj := FromRefreshWorkflowTasksRequest(original) roundTripObj := ToRefreshWorkflowTasksRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRegisterDomainRequestConversion(t *testing.T) { testCases := []*types.RegisterDomainRequest{ nil, {}, &testdata.RegisterDomainRequest, } for _, original := range testCases { thriftObj := FromRegisterDomainRequest(original) roundTripObj := ToRegisterDomainRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRegisterDomainRequestFuzz(t *testing.T) { t.Run("round trip from internal", func(t *testing.T) { testutils.EnsureFuzzCoverage(t, []string{ "nil", "empty", "filled", }, func(t *testing.T, f *fuzz.Fuzzer) string { // Configure fuzzer to generate valid enum values fuzzer := f.Funcs( func(e *types.ArchivalStatus, c fuzz.Continue) { *e = types.ArchivalStatus(c.Intn(2)) // 0-1 are valid values (Disabled=0, Enabled=1) }, ).NilChance(0.3) var orig *types.RegisterDomainRequest fuzzer.Fuzz(&orig) out := ToRegisterDomainRequest(FromRegisterDomainRequest(orig)) assert.Equal(t, orig, out, "RegisterDomainRequest did not survive round-tripping") if orig == nil { return "nil" } if orig.Name == "" && orig.ActiveClusterName == "" && orig.ActiveClusters == nil { return "empty" } return "filled" }) }) } func TestRemoteSyncMatchedErrorConversion(t *testing.T) { testCases := []*types.RemoteSyncMatchedError{ nil, {}, &testdata.RemoteSyncMatchedError, } for _, original := range testCases { thriftObj := FromRemoteSyncMatchedError(original) roundTripObj := ToRemoteSyncMatchedError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRemoveTaskRequestConversion(t *testing.T) { testCases := []*types.RemoveTaskRequest{ nil, {}, {ShardID: 10, Type: common.Int32Ptr(1), TaskID: 5, ClusterName: "test-cluster-name"}, } for _, original := range testCases { thriftObj := FromAdminRemoveTaskRequest(original) roundTripObj := ToAdminRemoveTaskRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRequestCancelActivityTaskDecisionAttributesConversion(t *testing.T) { testCases := []*types.RequestCancelActivityTaskDecisionAttributes{ nil, {}, &testdata.RequestCancelActivityTaskDecisionAttributes, } for _, original := range testCases { thriftObj := FromRequestCancelActivityTaskDecisionAttributes(original) roundTripObj := ToRequestCancelActivityTaskDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRequestCancelActivityTaskFailedEventAttributesConversion(t *testing.T) { testCases := []*types.RequestCancelActivityTaskFailedEventAttributes{ nil, {}, &testdata.RequestCancelActivityTaskFailedEventAttributes, } for _, original := range testCases { thriftObj := FromRequestCancelActivityTaskFailedEventAttributes(original) roundTripObj := ToRequestCancelActivityTaskFailedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRequestCancelExternalWorkflowExecutionDecisionAttributesConversion(t *testing.T) { testCases := []*types.RequestCancelExternalWorkflowExecutionDecisionAttributes{ nil, {}, &testdata.RequestCancelExternalWorkflowExecutionDecisionAttributes, } for _, original := range testCases { thriftObj := FromRequestCancelExternalWorkflowExecutionDecisionAttributes(original) roundTripObj := ToRequestCancelExternalWorkflowExecutionDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRequestCancelExternalWorkflowExecutionFailedEventAttributesConversion(t *testing.T) { testCases := []*types.RequestCancelExternalWorkflowExecutionFailedEventAttributes{ nil, {}, &testdata.RequestCancelExternalWorkflowExecutionFailedEventAttributes, } for _, original := range testCases { thriftObj := FromRequestCancelExternalWorkflowExecutionFailedEventAttributes(original) roundTripObj := ToRequestCancelExternalWorkflowExecutionFailedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRequestCancelWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.RequestCancelWorkflowExecutionRequest{ nil, {}, &testdata.RequestCancelWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromRequestCancelWorkflowExecutionRequest(original) roundTripObj := ToRequestCancelWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestResetPointInfoConversion(t *testing.T) { testCases := []*types.ResetPointInfo{ nil, {}, &testdata.ResetPointInfo, } for _, original := range testCases { thriftObj := FromResetPointInfo(original) roundTripObj := ToResetPointInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestResetPointsConversion(t *testing.T) { testCases := []*types.ResetPoints{ nil, {}, &testdata.ResetPoints, } for _, original := range testCases { thriftObj := FromResetPoints(original) roundTripObj := ToResetPoints(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestResetQueueRequestConversion(t *testing.T) { testCases := []*types.ResetQueueRequest{ nil, {}, {ShardID: 10, ClusterName: "test-cluster-name"}, } for _, original := range testCases { thriftObj := FromAdminResetQueueRequest(original) roundTripObj := ToAdminResetQueueRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestResetStickyTaskListRequestConversion(t *testing.T) { testCases := []*types.ResetStickyTaskListRequest{ nil, {}, &testdata.ResetStickyTaskListRequest, } for _, original := range testCases { thriftObj := FromResetStickyTaskListRequest(original) roundTripObj := ToResetStickyTaskListRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestResetStickyTaskListResponseConversion(t *testing.T) { testCases := []*types.ResetStickyTaskListResponse{ nil, {}, &testdata.ResetStickyTaskListResponse, } for _, original := range testCases { thriftObj := FromResetStickyTaskListResponse(original) roundTripObj := ToResetStickyTaskListResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestResetWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.ResetWorkflowExecutionRequest{ nil, {}, &testdata.ResetWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromResetWorkflowExecutionRequest(original) roundTripObj := ToResetWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestResetWorkflowExecutionResponseConversion(t *testing.T) { testCases := []*types.ResetWorkflowExecutionResponse{ nil, {}, &testdata.ResetWorkflowExecutionResponse, } for _, original := range testCases { thriftObj := FromResetWorkflowExecutionResponse(original) roundTripObj := ToResetWorkflowExecutionResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRespondActivityTaskCanceledByIDRequestConversion(t *testing.T) { testCases := []*types.RespondActivityTaskCanceledByIDRequest{ nil, {}, &testdata.RespondActivityTaskCanceledByIDRequest, } for _, original := range testCases { thriftObj := FromRespondActivityTaskCanceledByIDRequest(original) roundTripObj := ToRespondActivityTaskCanceledByIDRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRespondActivityTaskCanceledRequestConversion(t *testing.T) { testCases := []*types.RespondActivityTaskCanceledRequest{ nil, {}, &testdata.RespondActivityTaskCanceledRequest, } for _, original := range testCases { thriftObj := FromRespondActivityTaskCanceledRequest(original) roundTripObj := ToRespondActivityTaskCanceledRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRespondActivityTaskCompletedByIDRequestConversion(t *testing.T) { testCases := []*types.RespondActivityTaskCompletedByIDRequest{ nil, {}, &testdata.RespondActivityTaskCompletedByIDRequest, } for _, original := range testCases { thriftObj := FromRespondActivityTaskCompletedByIDRequest(original) roundTripObj := ToRespondActivityTaskCompletedByIDRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRespondActivityTaskCompletedRequestConversion(t *testing.T) { testCases := []*types.RespondActivityTaskCompletedRequest{ nil, {}, &testdata.RespondActivityTaskCompletedRequest, } for _, original := range testCases { thriftObj := FromRespondActivityTaskCompletedRequest(original) roundTripObj := ToRespondActivityTaskCompletedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRespondActivityTaskFailedByIDRequestConversion(t *testing.T) { testCases := []*types.RespondActivityTaskFailedByIDRequest{ nil, {}, &testdata.RespondActivityTaskFailedByIDRequest, } for _, original := range testCases { thriftObj := FromRespondActivityTaskFailedByIDRequest(original) roundTripObj := ToRespondActivityTaskFailedByIDRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRespondActivityTaskFailedRequestConversion(t *testing.T) { testCases := []*types.RespondActivityTaskFailedRequest{ nil, {}, &testdata.RespondActivityTaskFailedRequest, } for _, original := range testCases { thriftObj := FromRespondActivityTaskFailedRequest(original) roundTripObj := ToRespondActivityTaskFailedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRespondDecisionTaskCompletedRequestConversion(t *testing.T) { testCases := []*types.RespondDecisionTaskCompletedRequest{ nil, {}, &testdata.RespondDecisionTaskCompletedRequest, } for _, original := range testCases { thriftObj := FromRespondDecisionTaskCompletedRequest(original) roundTripObj := ToRespondDecisionTaskCompletedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRespondDecisionTaskFailedRequestConversion(t *testing.T) { testCases := []*types.RespondDecisionTaskFailedRequest{ nil, {}, &testdata.RespondDecisionTaskFailedRequest, } for _, original := range testCases { thriftObj := FromRespondDecisionTaskFailedRequest(original) roundTripObj := ToRespondDecisionTaskFailedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRespondQueryTaskCompletedRequestConversion(t *testing.T) { testCases := []*types.RespondQueryTaskCompletedRequest{ nil, {}, &testdata.RespondQueryTaskCompletedRequest, } for _, original := range testCases { thriftObj := FromRespondQueryTaskCompletedRequest(original) roundTripObj := ToRespondQueryTaskCompletedRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRetryPolicyConversion(t *testing.T) { testCases := []*types.RetryPolicy{ nil, {}, &testdata.RetryPolicy, } for _, original := range testCases { thriftObj := FromRetryPolicy(original) roundTripObj := ToRetryPolicy(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRetryTaskV2ErrorConversion(t *testing.T) { testCases := []*types.RetryTaskV2Error{ nil, {}, &testdata.RetryTaskV2Error, } for _, original := range testCases { thriftObj := FromRetryTaskV2Error(original) roundTripObj := ToRetryTaskV2Error(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRestartWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.RestartWorkflowExecutionRequest{ nil, {}, {Domain: "test-domain", WorkflowExecution: &testdata.WorkflowExecution}, } for _, original := range testCases { thriftObj := FromRestartWorkflowExecutionRequest(original) roundTripObj := ToRestartWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestScheduleActivityTaskDecisionAttributesConversion(t *testing.T) { testCases := []*types.ScheduleActivityTaskDecisionAttributes{ nil, {}, &testdata.ScheduleActivityTaskDecisionAttributes, } for _, original := range testCases { thriftObj := FromScheduleActivityTaskDecisionAttributes(original) roundTripObj := ToScheduleActivityTaskDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSearchAttributesConversion(t *testing.T) { testCases := []*types.SearchAttributes{ nil, {}, &testdata.SearchAttributes, } for _, original := range testCases { thriftObj := FromSearchAttributes(original) roundTripObj := ToSearchAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestServiceBusyErrorConversion(t *testing.T) { testCases := []*types.ServiceBusyError{ nil, {}, &testdata.ServiceBusyError, } for _, original := range testCases { thriftObj := FromServiceBusyError(original) roundTripObj := ToServiceBusyError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSignalExternalWorkflowExecutionDecisionAttributesConversion(t *testing.T) { testCases := []*types.SignalExternalWorkflowExecutionDecisionAttributes{ nil, {}, &testdata.SignalExternalWorkflowExecutionDecisionAttributes, } for _, original := range testCases { thriftObj := FromSignalExternalWorkflowExecutionDecisionAttributes(original) roundTripObj := ToSignalExternalWorkflowExecutionDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSignalExternalWorkflowExecutionFailedCauseConversion(t *testing.T) { testCases := []*types.SignalExternalWorkflowExecutionFailedCause{ nil, types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution.Ptr(), types.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted.Ptr(), } for _, original := range testCases { thriftObj := FromSignalExternalWorkflowExecutionFailedCause(original) roundTripObj := ToSignalExternalWorkflowExecutionFailedCause(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSignalExternalWorkflowExecutionFailedEventAttributesConversion(t *testing.T) { testCases := []*types.SignalExternalWorkflowExecutionFailedEventAttributes{ nil, {}, &testdata.SignalExternalWorkflowExecutionFailedEventAttributes, } for _, original := range testCases { thriftObj := FromSignalExternalWorkflowExecutionFailedEventAttributes(original) roundTripObj := ToSignalExternalWorkflowExecutionFailedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSignalExternalWorkflowExecutionInitiatedEventAttributesConversion(t *testing.T) { testCases := []*types.SignalExternalWorkflowExecutionInitiatedEventAttributes{ nil, {}, &testdata.SignalExternalWorkflowExecutionInitiatedEventAttributes, } for _, original := range testCases { thriftObj := FromSignalExternalWorkflowExecutionInitiatedEventAttributes(original) roundTripObj := ToSignalExternalWorkflowExecutionInitiatedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSignalWithStartWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.SignalWithStartWorkflowExecutionRequest{ nil, {}, &testdata.SignalWithStartWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromSignalWithStartWorkflowExecutionRequest(original) roundTripObj := ToSignalWithStartWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSignalWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.SignalWorkflowExecutionRequest{ nil, {}, &testdata.SignalWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromSignalWorkflowExecutionRequest(original) roundTripObj := ToSignalWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStartChildWorkflowExecutionDecisionAttributesConversion(t *testing.T) { testCases := []*types.StartChildWorkflowExecutionDecisionAttributes{ nil, {}, &testdata.StartChildWorkflowExecutionDecisionAttributes, } for _, original := range testCases { thriftObj := FromStartChildWorkflowExecutionDecisionAttributes(original) roundTripObj := ToStartChildWorkflowExecutionDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStartChildWorkflowExecutionFailedEventAttributesConversion(t *testing.T) { testCases := []*types.StartChildWorkflowExecutionFailedEventAttributes{ nil, {}, &testdata.StartChildWorkflowExecutionFailedEventAttributes, } for _, original := range testCases { thriftObj := FromStartChildWorkflowExecutionFailedEventAttributes(original) roundTripObj := ToStartChildWorkflowExecutionFailedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStartChildWorkflowExecutionInitiatedEventAttributesConversion(t *testing.T) { testCases := []*types.StartChildWorkflowExecutionInitiatedEventAttributes{ nil, {}, &testdata.StartChildWorkflowExecutionInitiatedEventAttributes, } for _, original := range testCases { thriftObj := FromStartChildWorkflowExecutionInitiatedEventAttributes(original) roundTripObj := ToStartChildWorkflowExecutionInitiatedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStartTimeFilterConversion(t *testing.T) { testCases := []*types.StartTimeFilter{ nil, {}, &testdata.StartTimeFilter, } for _, original := range testCases { thriftObj := FromStartTimeFilter(original) roundTripObj := ToStartTimeFilter(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStartTimerDecisionAttributesConversion(t *testing.T) { testCases := []*types.StartTimerDecisionAttributes{ nil, {}, &testdata.StartTimerDecisionAttributes, } for _, original := range testCases { thriftObj := FromStartTimerDecisionAttributes(original) roundTripObj := ToStartTimerDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStartWorkflowExecutionRequestConversion(t *testing.T) { testCases := []*types.StartWorkflowExecutionRequest{ nil, {}, &testdata.StartWorkflowExecutionRequest, } for _, original := range testCases { thriftObj := FromStartWorkflowExecutionRequest(original) roundTripObj := ToStartWorkflowExecutionRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStartWorkflowExecutionAsyncRequestConversion(t *testing.T) { testCases := []*types.StartWorkflowExecutionAsyncRequest{ nil, {}, &testdata.StartWorkflowExecutionAsyncRequest, } for _, original := range testCases { thriftObj := FromStartWorkflowExecutionAsyncRequest(original) roundTripObj := ToStartWorkflowExecutionAsyncRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSignalWithStartWorkflowExecutionAsyncRequestConversion(t *testing.T) { testCases := []*types.SignalWithStartWorkflowExecutionAsyncRequest{ nil, {}, &testdata.SignalWithStartWorkflowExecutionAsyncRequest, } for _, original := range testCases { thriftObj := FromSignalWithStartWorkflowExecutionAsyncRequest(original) roundTripObj := ToSignalWithStartWorkflowExecutionAsyncRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestRestartWorkflowExecutionResponseConversion(t *testing.T) { testCases := []*types.RestartWorkflowExecutionResponse{ nil, {}, {RunID: "test-run-id"}, } for _, original := range testCases { thriftObj := FromRestartWorkflowExecutionResponse(original) roundTripObj := ToRestartWorkflowExecutionResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStartWorkflowExecutionResponseConversion(t *testing.T) { testCases := []*types.StartWorkflowExecutionResponse{ nil, {}, {RunID: "test-run-id"}, } for _, original := range testCases { thriftObj := FromStartWorkflowExecutionResponse(original) roundTripObj := ToStartWorkflowExecutionResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStartWorkflowExecutionAsyncResponseConversion(t *testing.T) { testCases := []*types.StartWorkflowExecutionAsyncResponse{ nil, {}, &testdata.StartWorkflowExecutionAsyncResponse, } for _, original := range testCases { thriftObj := FromStartWorkflowExecutionAsyncResponse(original) roundTripObj := ToStartWorkflowExecutionAsyncResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSignalWithStartWorkflowExecutionAsyncResponseConversion(t *testing.T) { testCases := []*types.SignalWithStartWorkflowExecutionAsyncResponse{ nil, {}, &testdata.SignalWithStartWorkflowExecutionAsyncResponse, } for _, original := range testCases { thriftObj := FromSignalWithStartWorkflowExecutionAsyncResponse(original) roundTripObj := ToSignalWithStartWorkflowExecutionAsyncResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStickyExecutionAttributesConversion(t *testing.T) { testCases := []*types.StickyExecutionAttributes{ nil, {}, &testdata.StickyExecutionAttributes, } for _, original := range testCases { thriftObj := FromStickyExecutionAttributes(original) roundTripObj := ToStickyExecutionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestSupportedClientVersionsConversion(t *testing.T) { testCases := []*types.SupportedClientVersions{ nil, {}, &testdata.SupportedClientVersions, } for _, original := range testCases { thriftObj := FromSupportedClientVersions(original) roundTripObj := ToSupportedClientVersions(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTaskIDBlockConversion(t *testing.T) { testCases := []*types.TaskIDBlock{ nil, {}, &testdata.TaskIDBlock, } for _, original := range testCases { thriftObj := FromTaskIDBlock(original) roundTripObj := ToTaskIDBlock(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTaskListConversion(t *testing.T) { testCases := []*types.TaskList{ nil, {}, &testdata.TaskList, } for _, original := range testCases { thriftObj := FromTaskList(original) roundTripObj := ToTaskList(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTaskListMetadataConversion(t *testing.T) { testCases := []*types.TaskListMetadata{ nil, {}, &testdata.TaskListMetadata, } for _, original := range testCases { thriftObj := FromTaskListMetadata(original) roundTripObj := ToTaskListMetadata(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTaskListPartitionMetadataConversion(t *testing.T) { testCases := []*types.TaskListPartitionMetadata{ nil, {}, &testdata.TaskListPartitionMetadata, } for _, original := range testCases { thriftObj := FromTaskListPartitionMetadata(original) roundTripObj := ToTaskListPartitionMetadata(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTaskListStatusConversion(t *testing.T) { testCases := []*types.TaskListStatus{ nil, {}, &testdata.TaskListStatus, } for _, original := range testCases { thriftObj := FromTaskListStatus(original) roundTripObj := ToTaskListStatus(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTaskListTypeConversion(t *testing.T) { testCases := []*types.TaskListType{ nil, types.TaskListTypeDecision.Ptr(), types.TaskListTypeActivity.Ptr(), } for _, original := range testCases { thriftObj := FromTaskListType(original) roundTripObj := ToTaskListType(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTimeoutTypeConversion(t *testing.T) { testCases := []*types.TimeoutType{ nil, types.TimeoutTypeStartToClose.Ptr(), types.TimeoutTypeScheduleToStart.Ptr(), types.TimeoutTypeScheduleToClose.Ptr(), types.TimeoutTypeHeartbeat.Ptr(), } for _, original := range testCases { thriftObj := FromTimeoutType(original) roundTripObj := ToTimeoutType(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTimerCanceledEventAttributesConversion(t *testing.T) { testCases := []*types.TimerCanceledEventAttributes{ nil, {}, &testdata.TimerCanceledEventAttributes, } for _, original := range testCases { thriftObj := FromTimerCanceledEventAttributes(original) roundTripObj := ToTimerCanceledEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTimerFiredEventAttributesConversion(t *testing.T) { testCases := []*types.TimerFiredEventAttributes{ nil, {}, &testdata.TimerFiredEventAttributes, } for _, original := range testCases { thriftObj := FromTimerFiredEventAttributes(original) roundTripObj := ToTimerFiredEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTimerStartedEventAttributesConversion(t *testing.T) { testCases := []*types.TimerStartedEventAttributes{ nil, {}, &testdata.TimerStartedEventAttributes, } for _, original := range testCases { thriftObj := FromTimerStartedEventAttributes(original) roundTripObj := ToTimerStartedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTransientDecisionInfoConversion(t *testing.T) { testCases := []*types.TransientDecisionInfo{ nil, {}, &testdata.TransientDecisionInfo, } for _, original := range testCases { thriftObj := FromTransientDecisionInfo(original) roundTripObj := ToTransientDecisionInfo(thriftObj) opt := cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "ParentWorkflowDomainID") if diff := cmp.Diff(original, roundTripObj, opt); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } } } func TestUpdateDomainRequestConversion(t *testing.T) { testCases := []*types.UpdateDomainRequest{ nil, {}, &testdata.UpdateDomainRequest, } for _, original := range testCases { thriftObj := FromUpdateDomainRequest(original) roundTripObj := ToUpdateDomainRequest(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestUpdateDomainResponseConversion(t *testing.T) { testCases := []*types.UpdateDomainResponse{ nil, {}, &testdata.UpdateDomainResponse, } for _, original := range testCases { thriftObj := FromUpdateDomainResponse(original) roundTripObj := ToUpdateDomainResponse(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestUpsertWorkflowSearchAttributesDecisionAttributesConversion(t *testing.T) { testCases := []*types.UpsertWorkflowSearchAttributesDecisionAttributes{ nil, {}, &testdata.UpsertWorkflowSearchAttributesDecisionAttributes, } for _, original := range testCases { thriftObj := FromUpsertWorkflowSearchAttributesDecisionAttributes(original) roundTripObj := ToUpsertWorkflowSearchAttributesDecisionAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestUpsertWorkflowSearchAttributesEventAttributesConversion(t *testing.T) { testCases := []*types.UpsertWorkflowSearchAttributesEventAttributes{ nil, {}, &testdata.UpsertWorkflowSearchAttributesEventAttributes, } for _, original := range testCases { thriftObj := FromUpsertWorkflowSearchAttributesEventAttributes(original) roundTripObj := ToUpsertWorkflowSearchAttributesEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestVersionHistoriesConversion(t *testing.T) { testCases := []*types.VersionHistories{ nil, {}, &testdata.VersionHistories, } for _, original := range testCases { thriftObj := FromVersionHistories(original) roundTripObj := ToVersionHistories(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestVersionHistoryConversion(t *testing.T) { testCases := []*types.VersionHistory{ nil, {}, &testdata.VersionHistory, } for _, original := range testCases { thriftObj := FromVersionHistory(original) roundTripObj := ToVersionHistory(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestVersionHistoryItemConversion(t *testing.T) { testCases := []*types.VersionHistoryItem{ nil, {}, &testdata.VersionHistoryItem, } for _, original := range testCases { thriftObj := FromVersionHistoryItem(original) roundTripObj := ToVersionHistoryItem(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestWorkerVersionInfoConversion(t *testing.T) { testCases := []*types.WorkerVersionInfo{ nil, {}, &testdata.WorkerVersionInfo, } for _, original := range testCases { thriftObj := FromWorkerVersionInfo(original) roundTripObj := ToWorkerVersionInfo(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestWorkflowExecutionConversion(t *testing.T) { testCases := []*types.WorkflowExecution{ nil, {}, &testdata.WorkflowExecution, } for _, original := range testCases { thriftObj := FromWorkflowExecution(original) roundTripObj := ToWorkflowExecution(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestWorkflowExecutionAlreadyStartedErrorConversion(t *testing.T) { testCases := []*types.WorkflowExecutionAlreadyStartedError{ nil, {}, &testdata.WorkflowExecutionAlreadyStartedError, } for _, original := range testCases { thriftObj := FromWorkflowExecutionAlreadyStartedError(original) roundTripObj := ToWorkflowExecutionAlreadyStartedError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestWorkflowExecutionCancelRequestedEventAttributesConversion(t *testing.T) { testCases := []*types.WorkflowExecutionCancelRequestedEventAttributes{ nil, {}, &testdata.WorkflowExecutionCancelRequestedEventAttributes, } for _, original := range testCases { thriftObj := FromWorkflowExecutionCancelRequestedEventAttributes(original) roundTripObj := ToWorkflowExecutionCancelRequestedEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestWorkflowExecutionCanceledEventAttributesConversion(t *testing.T) { testCases := []*types.WorkflowExecutionCanceledEventAttributes{ nil, {}, &testdata.WorkflowExecutionCanceledEventAttributes, } for _, original := range testCases { thriftObj := FromWorkflowExecutionCanceledEventAttributes(original) roundTripObj := ToWorkflowExecutionCanceledEventAttributes(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestWorkflowExecutionCloseStatusConversion(t *testing.T) { testCases := []*types.WorkflowExecutionCloseStatus{ nil, types.WorkflowExecutionCloseStatusCompleted.Ptr(), types.WorkflowExecutionCloseStatusFailed.Ptr(), types.WorkflowExecutionCloseStatusCanceled.Ptr(), types.WorkflowExecutionCloseStatusTerminated.Ptr(), types.WorkflowExecutionCloseStatusContinuedAsNew.Ptr(), types.WorkflowExecutionCloseStatusTimedOut.Ptr(), } for _, original := range testCases { thriftObj := FromWorkflowExecutionCloseStatus(original) roundTripObj := ToWorkflowExecutionCloseStatus(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestWorkflowExecutionFilterConversion(t *testing.T) { testCases := []*types.WorkflowExecutionFilter{ nil, {}, &testdata.WorkflowExecutionFilter, } for _, original := range testCases { thriftObj := FromWorkflowExecutionFilter(original) roundTripObj := ToWorkflowExecutionFilter(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestWorkflowIDReusePolicyConversion(t *testing.T) { testCases := []*types.WorkflowIDReusePolicy{ nil, types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), types.WorkflowIDReusePolicyAllowDuplicateFailedOnly.Ptr(), types.WorkflowIDReusePolicyRejectDuplicate.Ptr(), types.WorkflowIDReusePolicyTerminateIfRunning.Ptr(), } for _, original := range testCases { thriftObj := FromWorkflowIDReusePolicy(original) roundTripObj := ToWorkflowIDReusePolicy(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestWorkflowTypeFilterConversion(t *testing.T) { testCases := []*types.WorkflowTypeFilter{ nil, {}, &testdata.WorkflowTypeFilter, } for _, original := range testCases { thriftObj := FromWorkflowTypeFilter(original) roundTripObj := ToWorkflowTypeFilter(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryBranchRangeArrayConversion(t *testing.T) { testCases := [][]*types.HistoryBranchRange{ nil, {}, { {BranchID: "test-branch-id-1", BeginNodeID: 1, EndNodeID: 2}, {BranchID: "test-branch-id-2", BeginNodeID: 5, EndNodeID: 6}, }, } for _, original := range testCases { thriftObj := FromHistoryBranchRangeArray(original) roundTripObj := ToHistoryBranchRangeArray(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestActivityLocalDispatchInfoMapConversion(t *testing.T) { testCases := []map[string]*types.ActivityLocalDispatchInfo{ nil, {}, { "test-key-1": &testdata.ActivityLocalDispatchInfo, "test-key-2": &testdata.ActivityLocalDispatchInfo, }, } for _, original := range testCases { thriftObj := FromActivityLocalDispatchInfoMap(original) roundTripObj := ToActivityLocalDispatchInfoMap(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestHistoryArrayConversion(t *testing.T) { testCases := [][]*types.History{ nil, {}, { {Events: []*types.HistoryEvent{{ID: 1}, {ID: 2}}}, {Events: []*types.HistoryEvent{{ID: 3}, {ID: 4}}}, }, } for _, original := range testCases { thriftObj := FromHistoryArray(original) roundTripObj := ToHistoryArray(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestStickyWorkerUnavailableErrorConversion(t *testing.T) { testCases := []*types.StickyWorkerUnavailableError{ nil, {}, &testdata.StickyWorkerUnavailableError, } for _, original := range testCases { thriftObj := FromStickyWorkerUnavailableError(original) roundTripObj := ToStickyWorkerUnavailableError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestFromTaskListNotOwnedByHostError(t *testing.T) { testCases := []*cadence_errors.TaskListNotOwnedByHostError{ nil, &testdata.TaskListNotOwnedByHostError, } for _, original := range testCases { thriftObj := FromTaskListNotOwnedByHostError(original) roundTripObj := ToTaskListNotOwnedByHostError(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestAny(t *testing.T) { t.Run("sanity check", func(t *testing.T) { // test the main fields are mapped correctly internal := types.Any{ ValueType: "testing", Value: []byte(`test`), } rpc := shared.Any{ ValueType: common.StringPtr("testing"), Value: []byte(`test`), } assert.Equal(t, &rpc, FromAny(&internal)) assert.Equal(t, &internal, ToAny(&rpc)) }) t.Run("round trip nils", func(t *testing.T) { // somewhat annoying in fuzzing and there are few possibilities, so tested separately assert.Nil(t, FromAny(ToAny(nil)), "nil thrift -> internal -> thrift => should result in nil") assert.Nil(t, ToAny(FromAny(nil)), "nil internal -> thrift -> internal => should result in nil") }) t.Run("round trip from internal", func(t *testing.T) { // fuzz test to check round trip data works as it should when starting from internal types testutils.EnsureFuzzCoverage(t, []string{ "empty data", "filled data", }, func(t *testing.T, f *fuzz.Fuzzer) string { var orig types.Any f.Fuzz(&orig) out := ToAny(FromAny(&orig)) assert.Equal(t, &orig, out, "did not survive round-tripping") // report what branch of behavior was executed so we can ensure stable coverage if len(orig.Value) == 0 { return "empty data" // ignoring nil vs empty difference } return "filled data" }) }) t.Run("round trip from thrift", func(t *testing.T) { // fuzz test to check round trip data works as it should testutils.EnsureFuzzCoverage(t, []string{ "nil id", "empty data", "filled data", }, func(t *testing.T, f *fuzz.Fuzzer) string { var orig shared.Any f.Fuzz(&orig) out := FromAny(ToAny(&orig)) if orig.ValueType == nil { // internal type does not support nil strings, so it will be transformed to an empty string. assert.Equal(t, "", *out.ValueType, "empty value type did not survive round-tripping") assert.Equal(t, orig.Value, out.Value, "value did not survive round-tripping") return "nil id" } // non-nil ValueType can check all fields assert.Equal(t, &orig, out, "did not survive round-tripping") if len(orig.Value) == 0 { return "empty data" // ignoring nil vs empty difference } return "filled data" }) }) } func TestTaskKeyConversion(t *testing.T) { testCases := []*types.TaskKey{ nil, &types.TaskKey{}, &testdata.TaskKey, } for _, original := range testCases { thriftObj := FromTaskKey(original) roundTripObj := ToTaskKey(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestTaskRangeConversion(t *testing.T) { testCases := []*types.TaskRange{ nil, &types.TaskRange{}, &testdata.TaskRange, } for _, original := range testCases { thriftObj := FromTaskRange(original) roundTripObj := ToTaskRange(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestVirtualSliceStateConversion(t *testing.T) { testCases := []*types.VirtualSliceState{ nil, &types.VirtualSliceState{}, &testdata.VirtualSliceState, } for _, original := range testCases { thriftObj := FromVirtualSliceState(original) roundTripObj := ToVirtualSliceState(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestVirtualQueueStateConversion(t *testing.T) { testCases := []*types.VirtualQueueState{ nil, &types.VirtualQueueState{}, &testdata.VirtualQueueState, } for _, original := range testCases { thriftObj := FromVirtualQueueState(original) roundTripObj := ToVirtualQueueState(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestQueueStateConversion(t *testing.T) { testCases := []*types.QueueState{ nil, &types.QueueState{}, &testdata.QueueState, } for _, original := range testCases { thriftObj := FromQueueState(original) roundTripObj := ToQueueState(thriftObj) assert.Equal(t, original, roundTripObj) } } // TODO: The way we're testing mappers doesn't have good coverage. The round trip tests don't cover serialization and deserialization of thrift. // We should also generate test data in thrift types and do round trip tests from thrift to internal and back to thrift. func TestActiveClusterSelectionPolicyConversion(t *testing.T) { testCases := []*types.ActiveClusterSelectionPolicy{ nil, {}, &testdata.ActiveClusterSelectionPolicyExternalEntity, &testdata.ActiveClusterSelectionPolicyRegionSticky, &testdata.ActiveClusterSelectionPolicyWithClusterAttribute, } for _, original := range testCases { thriftObj := FromActiveClusterSelectionPolicy(original) roundTripObj := ToActiveClusterSelectionPolicy(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestActiveClustersConversion(t *testing.T) { testCases := []*types.ActiveClusters{ nil, {}, { AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster1", FailoverVersion: 1, }, "us-east-1": { ActiveClusterName: "cluster2", FailoverVersion: 2, }, }, }, }, }, { AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster1", FailoverVersion: 1, }, "us-east-1": { ActiveClusterName: "cluster2", FailoverVersion: 2, }, }, }, "datacenter": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "dc1": { ActiveClusterName: "cluster1", FailoverVersion: 10, }, }, }, }, }, { AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster1", FailoverVersion: 1, }, }, }, }, }, } for _, original := range testCases { thriftObj := FromActiveClusters(original) roundTripObj := ToActiveClusters(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestClusterAttributeScopeConversion(t *testing.T) { testCases := []*types.ClusterAttributeScope{ nil, {}, { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west-1": { ActiveClusterName: "cluster1", FailoverVersion: 1, }, "us-east-1": { ActiveClusterName: "cluster2", FailoverVersion: 2, }, }, }, } for _, original := range testCases { thriftObj := FromClusterAttributeScope(original) roundTripObj := ToClusterAttributeScope(thriftObj) assert.Equal(t, original, roundTripObj) } } func TestListFailoverHistoryResponseConversion(t *testing.T) { fuzzer := testdatagen.New(t) for i := 0; i < 100; i++ { var response types.ListFailoverHistoryResponse fuzzer.Fuzz(&response) thriftResponse := FromListFailoverHistoryResponse(&response) toResponse := ToListFailoverHistoryResponse(thriftResponse) assert.Equal(t, &response, toResponse) } } ================================================ FILE: common/types/matching.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "fmt" "strconv" "strings" ) // AddActivityTaskRequest is an internal type (TBD...) type AddActivityTaskRequest struct { DomainUUID string `json:"domainUUID,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` SourceDomainUUID string `json:"sourceDomainUUID,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` ScheduleID int64 `json:"scheduleId,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` Source *TaskSource `json:"source,omitempty"` ForwardedFrom string `json:"forwardedFrom,omitempty"` ActivityTaskDispatchInfo *ActivityTaskDispatchInfo `json:"activityTaskDispatchInfo,omitempty"` PartitionConfig map[string]string } // GetDomainUUID is an internal getter (TBD...) func (v *AddActivityTaskRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetExecution is an internal getter (TBD...) func (v *AddActivityTaskRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // GetSourceDomainUUID is an internal getter (TBD...) func (v *AddActivityTaskRequest) GetSourceDomainUUID() (o string) { if v != nil { return v.SourceDomainUUID } return } // GetTaskList is an internal getter (TBD...) func (v *AddActivityTaskRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetScheduleID is an internal getter (TBD...) func (v *AddActivityTaskRequest) GetScheduleID() (o int64) { if v != nil { return v.ScheduleID } return } // GetScheduleToStartTimeoutSeconds is an internal getter (TBD...) func (v *AddActivityTaskRequest) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // GetSource is an internal getter (TBD...) func (v *AddActivityTaskRequest) GetSource() (o TaskSource) { if v != nil && v.Source != nil { return *v.Source } return } // GetForwardedFrom is an internal getter (TBD...) func (v *AddActivityTaskRequest) GetForwardedFrom() (o string) { if v != nil { return v.ForwardedFrom } return } // GetPartitionConfig is an internal getter (TBD...) func (v *AddActivityTaskRequest) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // ActivityTaskDispatchInfo is an internal type (TBD...) type ActivityTaskDispatchInfo struct { ScheduledEvent *HistoryEvent `json:"scheduledEvent,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Attempt *int64 `json:"attempt,omitempty"` ScheduledTimestampOfThisAttempt *int64 `json:"scheduledTimestampOfThisAttempt,omitempty"` HeartbeatDetails []byte `json:"heartbeatDetails,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` WorkflowDomain string `json:"workflowDomain,omitempty"` } // AddDecisionTaskRequest is an internal type (TBD...) type AddDecisionTaskRequest struct { DomainUUID string `json:"domainUUID,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` ScheduleID int64 `json:"scheduleId,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` Source *TaskSource `json:"source,omitempty"` ForwardedFrom string `json:"forwardedFrom,omitempty"` PartitionConfig map[string]string } // GetDomainUUID is an internal getter (TBD...) func (v *AddDecisionTaskRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetExecution is an internal getter (TBD...) func (v *AddDecisionTaskRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // GetTaskList is an internal getter (TBD...) func (v *AddDecisionTaskRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetScheduleID is an internal getter (TBD...) func (v *AddDecisionTaskRequest) GetScheduleID() (o int64) { if v != nil { return v.ScheduleID } return } // GetScheduleToStartTimeoutSeconds is an internal getter (TBD...) func (v *AddDecisionTaskRequest) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // GetSource is an internal getter (TBD...) func (v *AddDecisionTaskRequest) GetSource() (o TaskSource) { if v != nil && v.Source != nil { return *v.Source } return } // GetForwardedFrom is an internal getter (TBD...) func (v *AddDecisionTaskRequest) GetForwardedFrom() (o string) { if v != nil { return v.ForwardedFrom } return } // GetPartitionConfig is an internal getter (TBD...) func (v *AddDecisionTaskRequest) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } type AddActivityTaskResponse struct { PartitionConfig *TaskListPartitionConfig } type AddDecisionTaskResponse struct { PartitionConfig *TaskListPartitionConfig } // CancelOutstandingPollRequest is an internal type (TBD...) type CancelOutstandingPollRequest struct { DomainUUID string `json:"domainUUID,omitempty"` TaskListType *int32 `json:"taskListType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` PollerID string `json:"pollerID,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *CancelOutstandingPollRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetTaskListType is an internal getter (TBD...) func (v *CancelOutstandingPollRequest) GetTaskListType() (o int32) { if v != nil && v.TaskListType != nil { return *v.TaskListType } return } // GetTaskList is an internal getter (TBD...) func (v *CancelOutstandingPollRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetPollerID is an internal getter (TBD...) func (v *CancelOutstandingPollRequest) GetPollerID() (o string) { if v != nil { return v.PollerID } return } // MatchingDescribeTaskListRequest is an internal type (TBD...) type MatchingDescribeTaskListRequest struct { DomainUUID string `json:"domainUUID,omitempty"` DescRequest *DescribeTaskListRequest `json:"descRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *MatchingDescribeTaskListRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetDescRequest is an internal getter (TBD...) func (v *MatchingDescribeTaskListRequest) GetDescRequest() (o *DescribeTaskListRequest) { if v != nil && v.DescRequest != nil { return v.DescRequest } return } // MatchingListTaskListPartitionsRequest is an internal type (TBD...) type MatchingListTaskListPartitionsRequest struct { Domain string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *MatchingListTaskListPartitionsRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetTaskList is an internal getter (TBD...) func (v *MatchingListTaskListPartitionsRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // MatchingGetTaskListsByDomainRequest is an internal type (TBD...) type MatchingGetTaskListsByDomainRequest struct { Domain string `json:"domain,omitempty"` } // GetDomainName is an internal getter (TBD...) func (v *MatchingGetTaskListsByDomainRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // MatchingPollForActivityTaskRequest is an internal type (TBD...) type MatchingPollForActivityTaskRequest struct { DomainUUID string `json:"domainUUID,omitempty"` PollerID string `json:"pollerID,omitempty"` PollRequest *PollForActivityTaskRequest `json:"pollRequest,omitempty"` ForwardedFrom string `json:"forwardedFrom,omitempty"` IsolationGroup string } // GetDomainUUID is an internal getter (TBD...) func (v *MatchingPollForActivityTaskRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetPollerID is an internal getter (TBD...) func (v *MatchingPollForActivityTaskRequest) GetPollerID() (o string) { if v != nil { return v.PollerID } return } // GetPollRequest is an internal getter (TBD...) func (v *MatchingPollForActivityTaskRequest) GetPollRequest() (o *PollForActivityTaskRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // GetTaskList is an internal getter. func (v *MatchingPollForActivityTaskRequest) GetTaskList() (o *TaskList) { if v != nil && v.PollRequest != nil { return v.PollRequest.TaskList } return } // GetForwardedFrom is an internal getter (TBD...) func (v *MatchingPollForActivityTaskRequest) GetForwardedFrom() (o string) { if v != nil { return v.ForwardedFrom } return } // GetIsolatioonGroup is an internal getter (TBD...) func (v *MatchingPollForActivityTaskRequest) GetIsolationGroup() (o string) { if v != nil { return v.IsolationGroup } return } // MatchingPollForDecisionTaskRequest is an internal type (TBD...) type MatchingPollForDecisionTaskRequest struct { DomainUUID string `json:"domainUUID,omitempty"` PollerID string `json:"pollerID,omitempty"` PollRequest *PollForDecisionTaskRequest `json:"pollRequest,omitempty"` ForwardedFrom string `json:"forwardedFrom,omitempty"` IsolationGroup string } // GetDomainUUID is an internal getter (TBD...) func (v *MatchingPollForDecisionTaskRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetPollerID is an internal getter (TBD...) func (v *MatchingPollForDecisionTaskRequest) GetPollerID() (o string) { if v != nil { return v.PollerID } return } // GetPollRequest is an internal getter (TBD...) func (v *MatchingPollForDecisionTaskRequest) GetPollRequest() (o *PollForDecisionTaskRequest) { if v != nil && v.PollRequest != nil { return v.PollRequest } return } // GetTaskList is an internal getter. func (v *MatchingPollForDecisionTaskRequest) GetTaskList() (o *TaskList) { if v != nil && v.PollRequest != nil { return v.PollRequest.TaskList } return } // GetForwardedFrom is an internal getter (TBD...) func (v *MatchingPollForDecisionTaskRequest) GetForwardedFrom() (o string) { if v != nil { return v.ForwardedFrom } return } // GetIsolatioonGroup is an internal getter (TBD...) func (v *MatchingPollForDecisionTaskRequest) GetIsolationGroup() (o string) { if v != nil { return v.IsolationGroup } return } type TaskListPartition struct { IsolationGroups []string } type TaskListPartitionConfig struct { Version int64 ReadPartitions map[int]*TaskListPartition WritePartitions map[int]*TaskListPartition } // MatchingPollForDecisionTaskResponse is an internal type (TBD...) type MatchingPollForDecisionTaskResponse struct { TaskToken []byte `json:"taskToken,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` PreviousStartedEventID *int64 `json:"previousStartedEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` Attempt int64 `json:"attempt,omitempty"` NextEventID int64 `json:"nextEventId,omitempty"` BacklogCountHint int64 `json:"backlogCountHint,omitempty"` StickyExecutionEnabled bool `json:"stickyExecutionEnabled,omitempty"` Query *WorkflowQuery `json:"query,omitempty"` DecisionInfo *TransientDecisionInfo `json:"decisionInfo,omitempty"` WorkflowExecutionTaskList *TaskList `json:"WorkflowExecutionTaskList,omitempty"` EventStoreVersion int32 `json:"eventStoreVersion,omitempty"` BranchToken []byte `json:"branchToken,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Queries map[string]*WorkflowQuery `json:"queries,omitempty"` TotalHistoryBytes int64 `json:"currentHistorySize,omitempty"` PartitionConfig *TaskListPartitionConfig LoadBalancerHints *LoadBalancerHints AutoConfigHint *AutoConfigHint } // GetWorkflowExecution is an internal getter (TBD...) func (v *MatchingPollForDecisionTaskResponse) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // GetPreviousStartedEventID is an internal getter (TBD...) func (v *MatchingPollForDecisionTaskResponse) GetPreviousStartedEventID() (o int64) { if v != nil && v.PreviousStartedEventID != nil { return *v.PreviousStartedEventID } return } // GetNextEventID is an internal getter (TBD...) func (v *MatchingPollForDecisionTaskResponse) GetNextEventID() (o int64) { if v != nil { return v.NextEventID } return } // GetStickyExecutionEnabled is an internal getter (TBD...) func (v *MatchingPollForDecisionTaskResponse) GetStickyExecutionEnabled() (o bool) { if v != nil { return v.StickyExecutionEnabled } return } // GetBranchToken is an internal getter (TBD...) func (v *MatchingPollForDecisionTaskResponse) GetBranchToken() (o []byte) { if v != nil && v.BranchToken != nil { return v.BranchToken } return } // GetTotalHistoryBytes is an internal getter of returning the history size in bytes func (v *MatchingPollForDecisionTaskResponse) GetTotalHistoryBytes() (o int64) { if v != nil { return v.TotalHistoryBytes } return } type MatchingPollForActivityTaskResponse struct { TaskToken []byte `json:"taskToken,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` ActivityID string `json:"activityId,omitempty"` ActivityType *ActivityType `json:"activityType,omitempty"` Input []byte `json:"input,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` ScheduleToCloseTimeoutSeconds *int32 `json:"scheduleToCloseTimeoutSeconds,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` StartToCloseTimeoutSeconds *int32 `json:"startToCloseTimeoutSeconds,omitempty"` HeartbeatTimeoutSeconds *int32 `json:"heartbeatTimeoutSeconds,omitempty"` Attempt int32 `json:"attempt,omitempty"` ScheduledTimestampOfThisAttempt *int64 `json:"scheduledTimestampOfThisAttempt,omitempty"` HeartbeatDetails []byte `json:"heartbeatDetails,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` WorkflowDomain string `json:"workflowDomain,omitempty"` Header *Header `json:"header,omitempty"` BacklogCountHint int64 `json:"backlogCountHint,omitempty"` PartitionConfig *TaskListPartitionConfig LoadBalancerHints *LoadBalancerHints AutoConfigHint *AutoConfigHint } // MatchingQueryWorkflowRequest is an internal type (TBD...) type MatchingQueryWorkflowRequest struct { DomainUUID string `json:"domainUUID,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` QueryRequest *QueryWorkflowRequest `json:"queryRequest,omitempty"` ForwardedFrom string `json:"forwardedFrom,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *MatchingQueryWorkflowRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetTaskList is an internal getter (TBD...) func (v *MatchingQueryWorkflowRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetQueryRequest is an internal getter (TBD...) func (v *MatchingQueryWorkflowRequest) GetQueryRequest() (o *QueryWorkflowRequest) { if v != nil && v.QueryRequest != nil { return v.QueryRequest } return } // GetForwardedFrom is an internal getter (TBD...) func (v *MatchingQueryWorkflowRequest) GetForwardedFrom() (o string) { if v != nil { return v.ForwardedFrom } return } type MatchingQueryWorkflowResponse struct { QueryResult []byte QueryRejected *QueryRejected PartitionConfig *TaskListPartitionConfig } // GetQueryResult is an internal getter (TBD...) func (v *MatchingQueryWorkflowResponse) GetQueryResult() (o []byte) { if v != nil { return v.QueryResult } return } // GetQueryRejected is an internal getter (TBD...) func (v *MatchingQueryWorkflowResponse) GetQueryRejected() (o *QueryRejected) { if v != nil { return v.QueryRejected } return } // GetPartitionConfig is an internal getter (TBD...) func (v *MatchingQueryWorkflowResponse) GetPartitionConfig() (o *TaskListPartitionConfig) { if v != nil { return v.PartitionConfig } return } // MatchingRespondQueryTaskCompletedRequest is an internal type (TBD...) type MatchingRespondQueryTaskCompletedRequest struct { DomainUUID string `json:"domainUUID,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` TaskID string `json:"taskID,omitempty"` CompletedRequest *RespondQueryTaskCompletedRequest `json:"completedRequest,omitempty"` } // GetDomainUUID is an internal getter (TBD...) func (v *MatchingRespondQueryTaskCompletedRequest) GetDomainUUID() (o string) { if v != nil { return v.DomainUUID } return } // GetTaskList is an internal getter (TBD...) func (v *MatchingRespondQueryTaskCompletedRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetTaskID is an internal getter (TBD...) func (v *MatchingRespondQueryTaskCompletedRequest) GetTaskID() (o string) { if v != nil { return v.TaskID } return } // GetCompletedRequest is an internal getter (TBD...) func (v *MatchingRespondQueryTaskCompletedRequest) GetCompletedRequest() (o *RespondQueryTaskCompletedRequest) { if v != nil && v.CompletedRequest != nil { return v.CompletedRequest } return } // TaskSource is an internal type (TBD...) type TaskSource int32 // Ptr is a helper function for getting pointer value func (e TaskSource) Ptr() *TaskSource { return &e } // String returns a readable string representation of TaskSource. func (e TaskSource) String() string { w := int32(e) switch w { case 0: return "HISTORY" case 1: return "DB_BACKLOG" } return fmt.Sprintf("TaskSource(%d)", w) } // UnmarshalText parses enum value from string representation func (e *TaskSource) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "HISTORY": *e = TaskSourceHistory return nil case "DB_BACKLOG": *e = TaskSourceDbBacklog return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "TaskSource", err) } *e = TaskSource(val) return nil } } // MarshalText encodes TaskSource to text. func (e TaskSource) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // TaskSourceHistory is an option for TaskSource TaskSourceHistory TaskSource = iota // TaskSourceDbBacklog is an option for TaskSource TaskSourceDbBacklog ) type MatchingUpdateTaskListPartitionConfigRequest struct { DomainUUID string TaskList *TaskList TaskListType *TaskListType PartitionConfig *TaskListPartitionConfig } func (v *MatchingUpdateTaskListPartitionConfigRequest) GetTaskListType() (o TaskListType) { if v != nil && v.TaskListType != nil { return *v.TaskListType } return } type MatchingUpdateTaskListPartitionConfigResponse struct{} type MatchingRefreshTaskListPartitionConfigRequest struct { DomainUUID string TaskList *TaskList TaskListType *TaskListType PartitionConfig *TaskListPartitionConfig } func (v *MatchingRefreshTaskListPartitionConfigRequest) GetTaskListType() (o TaskListType) { if v != nil && v.TaskListType != nil { return *v.TaskListType } return } type MatchingRefreshTaskListPartitionConfigResponse struct{} type LoadBalancerHints struct { BacklogCount int64 RatePerSecond float64 } ================================================ FILE: common/types/matching_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "errors" "testing" "github.com/stretchr/testify/assert" ) func TestAddActivityTaskRequest_GetDomainUUID(t *testing.T) { tests := []struct { name string req *AddActivityTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty domain", req: &AddActivityTaskRequest{}, want: "", }, { name: "domain", req: &AddActivityTaskRequest{DomainUUID: "test-domain"}, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDomainUUID() assert.Equal(t, tt.want, got) }) } } func TestAddActivityTaskRequest_GetExecution(t *testing.T) { tests := []struct { name string req *AddActivityTaskRequest want *WorkflowExecution }{ { name: "nil request", req: nil, want: nil, }, { name: "empty execution", req: &AddActivityTaskRequest{}, want: nil, }, { name: "execution", req: &AddActivityTaskRequest{Execution: &WorkflowExecution{WorkflowID: "test-workflow-id"}}, want: &WorkflowExecution{WorkflowID: "test-workflow-id"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetExecution() assert.Equal(t, tt.want, got) }) } } func TestAddActivityTaskRequest_GetSourceDomainUUID(t *testing.T) { tests := []struct { name string req *AddActivityTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty source domain", req: &AddActivityTaskRequest{}, want: "", }, { name: "source domain", req: &AddActivityTaskRequest{SourceDomainUUID: "test-source-domain"}, want: "test-source-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetSourceDomainUUID() assert.Equal(t, tt.want, got) }) } } func TestAddActivityTaskRequest_GetTaskList(t *testing.T) { tests := []struct { name string req *AddActivityTaskRequest want *TaskList }{ { name: "nil request", req: nil, want: nil, }, { name: "empty task list", req: &AddActivityTaskRequest{}, want: nil, }, { name: "task list", req: &AddActivityTaskRequest{TaskList: &TaskList{Name: "test-task-list"}}, want: &TaskList{Name: "test-task-list"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskList() assert.Equal(t, tt.want, got) }) } } func TestAddActivityTaskRequest_GetScheduleID(t *testing.T) { tests := []struct { name string req *AddActivityTaskRequest want int64 }{ { name: "nil request", req: nil, want: 0, }, { name: "empty schedule id", req: &AddActivityTaskRequest{}, want: 0, }, { name: "schedule id", req: &AddActivityTaskRequest{ScheduleID: 123}, want: 123, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetScheduleID() assert.Equal(t, tt.want, got) }) } } func TestAddActivityTaskRequest_GetScheduleToStartTimeoutSeconds(t *testing.T) { tests := []struct { name string req *AddActivityTaskRequest want int32 }{ { name: "nil request", req: nil, want: 0, }, { name: "empty schedule to start timeout", req: &AddActivityTaskRequest{}, want: 0, }, { name: "schedule to start timeout", req: &AddActivityTaskRequest{ScheduleToStartTimeoutSeconds: func() *int32 { i := int32(123); return &i }()}, want: 123, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetScheduleToStartTimeoutSeconds() assert.Equal(t, tt.want, got) }) } } func TestAddActivityTaskRequest_GetSource(t *testing.T) { tests := []struct { name string req *AddActivityTaskRequest want TaskSource }{ { name: "nil request", req: nil, want: TaskSource(0), }, { name: "empty source", req: &AddActivityTaskRequest{}, want: TaskSource(0), }, { name: "source", req: &AddActivityTaskRequest{Source: TaskSource(123).Ptr()}, want: TaskSource(123), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetSource() assert.Equal(t, tt.want, got) }) } } func TestAddActivityTaskRequest_GetForwardedFrom(t *testing.T) { tests := []struct { name string req *AddActivityTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty forwarded from", req: &AddActivityTaskRequest{}, want: "", }, { name: "forwarded from", req: &AddActivityTaskRequest{ForwardedFrom: "test-forwarded-from"}, want: "test-forwarded-from", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetForwardedFrom() assert.Equal(t, tt.want, got) }) } } func TestAddActivityTaskRequest_GetPartitionConfig(t *testing.T) { tests := []struct { name string req *AddActivityTaskRequest want map[string]string }{ { name: "nil request", req: nil, want: func() map[string]string { return nil }(), }, { name: "empty partition config", req: &AddActivityTaskRequest{}, want: func() map[string]string { return nil }(), }, { name: "partition config", req: &AddActivityTaskRequest{PartitionConfig: map[string]string{"test-key": "test-value"}}, want: map[string]string{"test-key": "test-value"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetPartitionConfig() assert.Equal(t, tt.want, got) }) } } func TestAddDecisionTaskRequest_GetDomainUUID(t *testing.T) { tests := []struct { name string req *AddDecisionTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty domain", req: &AddDecisionTaskRequest{}, want: "", }, { name: "domain", req: &AddDecisionTaskRequest{DomainUUID: "test-domain"}, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDomainUUID() assert.Equal(t, tt.want, got) }) } } func TestAddDecisionTaskRequest_GetExecution(t *testing.T) { tests := []struct { name string req *AddDecisionTaskRequest want *WorkflowExecution }{ { name: "nil request", req: nil, want: nil, }, { name: "empty execution", req: &AddDecisionTaskRequest{}, want: nil, }, { name: "execution", req: &AddDecisionTaskRequest{Execution: &WorkflowExecution{WorkflowID: "test-workflow-id"}}, want: &WorkflowExecution{WorkflowID: "test-workflow-id"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetExecution() assert.Equal(t, tt.want, got) }) } } func TestAddDecisionTaskRequest_GetTaskList(t *testing.T) { tests := []struct { name string req *AddDecisionTaskRequest want *TaskList }{ { name: "nil request", req: nil, want: nil, }, { name: "empty task list", req: &AddDecisionTaskRequest{}, want: nil, }, { name: "task list", req: &AddDecisionTaskRequest{TaskList: &TaskList{Name: "test-task-list"}}, want: &TaskList{Name: "test-task-list"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskList() assert.Equal(t, tt.want, got) }) } } func TestAddDecisionTaskRequest_GetScheduleID(t *testing.T) { tests := []struct { name string req *AddDecisionTaskRequest want int64 }{ { name: "nil request", req: nil, want: 0, }, { name: "empty schedule id", req: &AddDecisionTaskRequest{}, want: 0, }, { name: "schedule id", req: &AddDecisionTaskRequest{ScheduleID: 123}, want: 123, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetScheduleID() assert.Equal(t, tt.want, got) }) } } func TestAddDecisionTaskRequest_GetScheduleToStartTimeoutSeconds(t *testing.T) { tests := []struct { name string req *AddDecisionTaskRequest want int32 }{ { name: "nil request", req: nil, want: 0, }, { name: "empty schedule to start timeout", req: &AddDecisionTaskRequest{}, want: 0, }, { name: "schedule to start timeout", req: &AddDecisionTaskRequest{ScheduleToStartTimeoutSeconds: func() *int32 { i := int32(123); return &i }()}, want: 123, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetScheduleToStartTimeoutSeconds() assert.Equal(t, tt.want, got) }) } } func TestAddDecisionTaskRequest_GetSource(t *testing.T) { tests := []struct { name string req *AddDecisionTaskRequest want TaskSource }{ { name: "nil request", req: nil, want: TaskSource(0), }, { name: "empty source", req: &AddDecisionTaskRequest{}, want: TaskSource(0), }, { name: "source", req: &AddDecisionTaskRequest{Source: TaskSource(123).Ptr()}, want: TaskSource(123), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetSource() assert.Equal(t, tt.want, got) }) } } func TestAddDecisionTaskRequest_GetForwardedFrom(t *testing.T) { tests := []struct { name string req *AddDecisionTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty forwarded from", req: &AddDecisionTaskRequest{}, want: "", }, { name: "forwarded from", req: &AddDecisionTaskRequest{ForwardedFrom: "test-forwarded-from"}, want: "test-forwarded-from", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetForwardedFrom() assert.Equal(t, tt.want, got) }) } } func TestAddDecisionTaskRequest_GetPartitionConfig(t *testing.T) { tests := []struct { name string req *AddDecisionTaskRequest want map[string]string }{ { name: "nil request", req: nil, want: func() map[string]string { return nil }(), }, { name: "empty partition config", req: &AddDecisionTaskRequest{}, want: func() map[string]string { return nil }(), }, { name: "partition config", req: &AddDecisionTaskRequest{PartitionConfig: map[string]string{"test-key": "test-value"}}, want: map[string]string{"test-key": "test-value"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetPartitionConfig() assert.Equal(t, tt.want, got) }) } } func TestCancelOutstandingPollRequest_GetDomainUUID(t *testing.T) { tests := []struct { name string req *CancelOutstandingPollRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty domain", req: &CancelOutstandingPollRequest{}, want: "", }, { name: "domain", req: &CancelOutstandingPollRequest{DomainUUID: "test-domain"}, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDomainUUID() assert.Equal(t, tt.want, got) }) } } func TestCancelOutstandingPollRequest_GetTaskListType(t *testing.T) { tests := []struct { name string req *CancelOutstandingPollRequest want int32 }{ { name: "nil request", req: nil, want: 0, }, { name: "empty task list type", req: &CancelOutstandingPollRequest{}, want: 0, }, { name: "task list type", req: &CancelOutstandingPollRequest{TaskListType: func() *int32 { var v int32; v = int32(TaskListTypeActivity); return &v }()}, want: int32(TaskListTypeActivity), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskListType() assert.Equal(t, tt.want, got) }) } } func TestCancelOutstandingPollRequest_GetTaskList(t *testing.T) { tests := []struct { name string req *CancelOutstandingPollRequest want *TaskList }{ { name: "nil request", req: nil, want: nil, }, { name: "empty task list", req: &CancelOutstandingPollRequest{}, want: nil, }, { name: "task list", req: &CancelOutstandingPollRequest{TaskList: &TaskList{Name: "test-task-list"}}, want: &TaskList{Name: "test-task-list"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskList() assert.Equal(t, tt.want, got) }) } } func TestCancelOutstandingPollRequest_GetPollerID(t *testing.T) { tests := []struct { name string req *CancelOutstandingPollRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty poller id", req: &CancelOutstandingPollRequest{}, want: "", }, { name: "poller id", req: &CancelOutstandingPollRequest{PollerID: "test-poller-id"}, want: "test-poller-id", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetPollerID() assert.Equal(t, tt.want, got) }) } } func TestMatchingDescribeTaskListRequest_GetDomainUUID(t *testing.T) { tests := []struct { name string req *MatchingDescribeTaskListRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty domain", req: &MatchingDescribeTaskListRequest{}, want: "", }, { name: "domain", req: &MatchingDescribeTaskListRequest{DomainUUID: "test-domain"}, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDomainUUID() assert.Equal(t, tt.want, got) }) } } func TestMatchingDescribeTaskListRequest_GetDescRequest(t *testing.T) { tests := []struct { name string req *MatchingDescribeTaskListRequest want *DescribeTaskListRequest }{ { name: "nil request", req: nil, want: nil, }, { name: "empty desc request", req: &MatchingDescribeTaskListRequest{}, want: nil, }, { name: "desc request", req: &MatchingDescribeTaskListRequest{DescRequest: &DescribeTaskListRequest{Domain: "test-domain"}}, want: &DescribeTaskListRequest{Domain: "test-domain"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDescRequest() assert.Equal(t, tt.want, got) }) } } func TestMatchingListTaskListPartitionsRequest_GetDomain(t *testing.T) { tests := []struct { name string req *MatchingListTaskListPartitionsRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty domain", req: &MatchingListTaskListPartitionsRequest{}, want: "", }, { name: "domain", req: &MatchingListTaskListPartitionsRequest{Domain: "test-domain"}, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDomain() assert.Equal(t, tt.want, got) }) } } func TestMatchingListTaskListPartitionsRequest_GetTaskList(t *testing.T) { tests := []struct { name string req *MatchingListTaskListPartitionsRequest want *TaskList }{ { name: "nil request", req: nil, want: nil, }, { name: "empty task list", req: &MatchingListTaskListPartitionsRequest{}, want: nil, }, { name: "task list", req: &MatchingListTaskListPartitionsRequest{TaskList: &TaskList{Name: "test-task-list"}}, want: &TaskList{Name: "test-task-list"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskList() assert.Equal(t, tt.want, got) }) } } func TestMatchingGetTaskListsByDomainRequest_GetDomain(t *testing.T) { tests := []struct { name string req *MatchingGetTaskListsByDomainRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty domain", req: &MatchingGetTaskListsByDomainRequest{}, want: "", }, { name: "domain", req: &MatchingGetTaskListsByDomainRequest{Domain: "test-domain"}, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDomain() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForActivityTaskRequest_GetDomainUUID(t *testing.T) { tests := []struct { name string req *MatchingPollForActivityTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty domain", req: &MatchingPollForActivityTaskRequest{}, want: "", }, { name: "domain", req: &MatchingPollForActivityTaskRequest{DomainUUID: "test-domain"}, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDomainUUID() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForActivityTaskRequest_GetPollerID(t *testing.T) { tests := []struct { name string req *MatchingPollForActivityTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty poller id", req: &MatchingPollForActivityTaskRequest{}, want: "", }, { name: "poller id", req: &MatchingPollForActivityTaskRequest{PollerID: "test-poller-id"}, want: "test-poller-id", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetPollerID() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForActivityTaskRequest_GetPollRequest(t *testing.T) { tests := []struct { name string req *MatchingPollForActivityTaskRequest want *PollForActivityTaskRequest }{ { name: "nil request", req: nil, want: nil, }, { name: "empty poll request", req: &MatchingPollForActivityTaskRequest{}, want: nil, }, { name: "poll request", req: &MatchingPollForActivityTaskRequest{PollRequest: &PollForActivityTaskRequest{Domain: "test-domain"}}, want: &PollForActivityTaskRequest{Domain: "test-domain"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetPollRequest() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForActivityTaskRequest_GetTaskList(t *testing.T) { tests := []struct { name string req *MatchingPollForActivityTaskRequest want *TaskList }{ { name: "nil request", req: nil, want: nil, }, { name: "empty task list", req: &MatchingPollForActivityTaskRequest{}, want: nil, }, { name: "task list", req: &MatchingPollForActivityTaskRequest{PollRequest: &PollForActivityTaskRequest{TaskList: &TaskList{Name: "test-task-list"}}}, want: &TaskList{Name: "test-task-list"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskList() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForActivityTaskRequest_GetForwardedFrom(t *testing.T) { tests := []struct { name string req *MatchingPollForActivityTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty forwarded from", req: &MatchingPollForActivityTaskRequest{}, want: "", }, { name: "forwarded from", req: &MatchingPollForActivityTaskRequest{ForwardedFrom: "test-forwarded-from"}, want: "test-forwarded-from", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetForwardedFrom() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForActivityTaskRequest_GetIsolationGroup(t *testing.T) { tests := []struct { name string req *MatchingPollForActivityTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty isolation group", req: &MatchingPollForActivityTaskRequest{}, want: "", }, { name: "isolation group", req: &MatchingPollForActivityTaskRequest{IsolationGroup: "test-isolation-group"}, want: "test-isolation-group", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetIsolationGroup() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskRequest_GetDomainUUID(t *testing.T) { tests := []struct { name string req *MatchingPollForDecisionTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty domain", req: &MatchingPollForDecisionTaskRequest{}, want: "", }, { name: "domain", req: &MatchingPollForDecisionTaskRequest{DomainUUID: "test-domain"}, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDomainUUID() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskRequest_GetPollerID(t *testing.T) { tests := []struct { name string req *MatchingPollForDecisionTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty poller id", req: &MatchingPollForDecisionTaskRequest{}, want: "", }, { name: "poller id", req: &MatchingPollForDecisionTaskRequest{PollerID: "test-poller-id"}, want: "test-poller-id", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetPollerID() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskRequest_GetPollRequest(t *testing.T) { tests := []struct { name string req *MatchingPollForDecisionTaskRequest want *PollForDecisionTaskRequest }{ { name: "nil request", req: nil, want: nil, }, { name: "empty poll request", req: &MatchingPollForDecisionTaskRequest{}, want: nil, }, { name: "poll request", req: &MatchingPollForDecisionTaskRequest{PollRequest: &PollForDecisionTaskRequest{Domain: "test-domain"}}, want: &PollForDecisionTaskRequest{Domain: "test-domain"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetPollRequest() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskRequest_GetTaskList(t *testing.T) { tests := []struct { name string req *MatchingPollForDecisionTaskRequest want *TaskList }{ { name: "nil request", req: nil, want: nil, }, { name: "empty task list", req: &MatchingPollForDecisionTaskRequest{}, want: nil, }, { name: "task list", req: &MatchingPollForDecisionTaskRequest{PollRequest: &PollForDecisionTaskRequest{TaskList: &TaskList{Name: "test-task-list"}}}, want: &TaskList{Name: "test-task-list"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskList() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskRequest_GetForwardedFrom(t *testing.T) { tests := []struct { name string req *MatchingPollForDecisionTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty forwarded from", req: &MatchingPollForDecisionTaskRequest{}, want: "", }, { name: "forwarded from", req: &MatchingPollForDecisionTaskRequest{ForwardedFrom: "test-forwarded-from"}, want: "test-forwarded-from", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetForwardedFrom() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskRequest_GetIsolationGroup(t *testing.T) { tests := []struct { name string req *MatchingPollForDecisionTaskRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty isolation group", req: &MatchingPollForDecisionTaskRequest{}, want: "", }, { name: "isolation group", req: &MatchingPollForDecisionTaskRequest{IsolationGroup: "test-isolation-group"}, want: "test-isolation-group", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetIsolationGroup() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskResponse_GetWorkflowExecution(t *testing.T) { tests := []struct { name string resp *MatchingPollForDecisionTaskResponse want *WorkflowExecution }{ { name: "nil response", resp: nil, want: nil, }, { name: "empty workflow execution", resp: &MatchingPollForDecisionTaskResponse{}, want: nil, }, { name: "workflow execution", resp: &MatchingPollForDecisionTaskResponse{WorkflowExecution: &WorkflowExecution{WorkflowID: "test-workflow-id"}}, want: &WorkflowExecution{WorkflowID: "test-workflow-id"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.resp.GetWorkflowExecution() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskResponse_GetPreviousStartedEventID(t *testing.T) { tests := []struct { name string resp *MatchingPollForDecisionTaskResponse want int64 }{ { name: "nil response", resp: nil, want: 0, }, { name: "empty previous started event id", resp: &MatchingPollForDecisionTaskResponse{}, want: 0, }, { name: "previous started event id", resp: &MatchingPollForDecisionTaskResponse{PreviousStartedEventID: func() *int64 { v := int64(123); return &v }()}, want: 123, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.resp.GetPreviousStartedEventID() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskResponse_GetNextEventID(t *testing.T) { tests := []struct { name string resp *MatchingPollForDecisionTaskResponse want int64 }{ { name: "nil response", resp: nil, want: 0, }, { name: "empty next event id", resp: &MatchingPollForDecisionTaskResponse{}, want: 0, }, { name: "next event id", resp: &MatchingPollForDecisionTaskResponse{NextEventID: int64(123)}, want: 123, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.resp.GetNextEventID() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskResponse_GetStickyExecutionEnabled(t *testing.T) { tests := []struct { name string resp *MatchingPollForDecisionTaskResponse want bool }{ { name: "nil response", resp: nil, want: false, }, { name: "empty sticky execution enabled", resp: &MatchingPollForDecisionTaskResponse{}, want: false, }, { name: "sticky execution enabled", resp: &MatchingPollForDecisionTaskResponse{StickyExecutionEnabled: true}, want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.resp.GetStickyExecutionEnabled() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskResponse_GetBranchToken(t *testing.T) { tests := []struct { name string resp *MatchingPollForDecisionTaskResponse want []byte }{ { name: "nil response", resp: nil, want: nil, }, { name: "empty branch token", resp: &MatchingPollForDecisionTaskResponse{}, want: nil, }, { name: "branch token", resp: &MatchingPollForDecisionTaskResponse{BranchToken: []byte("test-branch-token")}, want: []byte("test-branch-token"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.resp.GetBranchToken() assert.Equal(t, tt.want, got) }) } } func TestMatchingPollForDecisionTaskResponse_GetTotalHistoryBytes(t *testing.T) { tests := []struct { name string resp *MatchingPollForDecisionTaskResponse want int64 }{ { name: "nil response", resp: nil, want: 0, }, { name: "empty total history bytes", resp: &MatchingPollForDecisionTaskResponse{}, want: 0, }, { name: "total history bytes", resp: &MatchingPollForDecisionTaskResponse{TotalHistoryBytes: 123}, want: 123, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.resp.GetTotalHistoryBytes() assert.Equal(t, tt.want, got) }) } } func TestMatchingQueryWorkflowRequest_GetDomainUUID(t *testing.T) { tests := []struct { name string req *MatchingQueryWorkflowRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty domain", req: &MatchingQueryWorkflowRequest{}, want: "", }, { name: "domain", req: &MatchingQueryWorkflowRequest{DomainUUID: "test-domain"}, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDomainUUID() assert.Equal(t, tt.want, got) }) } } func TestMatchingQueryWorkflowRequest_GetTaskList(t *testing.T) { tests := []struct { name string req *MatchingQueryWorkflowRequest want *TaskList }{ { name: "nil request", req: nil, want: nil, }, { name: "empty task list", req: &MatchingQueryWorkflowRequest{}, want: nil, }, { name: "task list", req: &MatchingQueryWorkflowRequest{TaskList: &TaskList{Name: "test-task-list"}}, want: &TaskList{Name: "test-task-list"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskList() assert.Equal(t, tt.want, got) }) } } func TestMatchingQueryWorkflowRequest_GetQueryRequest(t *testing.T) { tests := []struct { name string req *MatchingQueryWorkflowRequest want *QueryWorkflowRequest }{ { name: "nil request", req: nil, want: nil, }, { name: "empty query request", req: &MatchingQueryWorkflowRequest{}, want: nil, }, { name: "query request", req: &MatchingQueryWorkflowRequest{QueryRequest: &QueryWorkflowRequest{Domain: "test-domain"}}, want: &QueryWorkflowRequest{Domain: "test-domain"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetQueryRequest() assert.Equal(t, tt.want, got) }) } } func TestMatchingQueryWorkflowRequest_GetForwardedFrom(t *testing.T) { tests := []struct { name string req *MatchingQueryWorkflowRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty forwarded from", req: &MatchingQueryWorkflowRequest{}, want: "", }, { name: "forwarded from", req: &MatchingQueryWorkflowRequest{ForwardedFrom: "test-forwarded-from"}, want: "test-forwarded-from", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetForwardedFrom() assert.Equal(t, tt.want, got) }) } } func TestMatchingRespondQueryTaskCompletedRequest_GetDomainUUID(t *testing.T) { tests := []struct { name string req *MatchingRespondQueryTaskCompletedRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty domain", req: &MatchingRespondQueryTaskCompletedRequest{}, want: "", }, { name: "domain", req: &MatchingRespondQueryTaskCompletedRequest{DomainUUID: "test-domain"}, want: "test-domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetDomainUUID() assert.Equal(t, tt.want, got) }) } } func TestMatchingRespondQueryTaskCompletedRequest_GetTaskList(t *testing.T) { tests := []struct { name string req *MatchingRespondQueryTaskCompletedRequest want *TaskList }{ { name: "nil request", req: nil, want: nil, }, { name: "empty task list", req: &MatchingRespondQueryTaskCompletedRequest{}, want: nil, }, { name: "task list", req: &MatchingRespondQueryTaskCompletedRequest{TaskList: &TaskList{Name: "test-task-list"}}, want: &TaskList{Name: "test-task-list"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskList() assert.Equal(t, tt.want, got) }) } } func TestMatchingRespondQueryTaskCompletedRequest_GetTaskID(t *testing.T) { tests := []struct { name string req *MatchingRespondQueryTaskCompletedRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty task id", req: &MatchingRespondQueryTaskCompletedRequest{}, want: "", }, { name: "task id", req: &MatchingRespondQueryTaskCompletedRequest{TaskID: "test-task-id"}, want: "test-task-id", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskID() assert.Equal(t, tt.want, got) }) } } func TestMatchingRespondQueryTaskCompletedRequest_GetCompletedRequest(t *testing.T) { tests := []struct { name string req *MatchingRespondQueryTaskCompletedRequest want *RespondQueryTaskCompletedRequest }{ { name: "nil request", req: nil, want: nil, }, { name: "empty completed request", req: &MatchingRespondQueryTaskCompletedRequest{}, want: nil, }, { name: "completed request", req: &MatchingRespondQueryTaskCompletedRequest{CompletedRequest: &RespondQueryTaskCompletedRequest{ErrorMessage: "test-error-message"}}, want: &RespondQueryTaskCompletedRequest{ErrorMessage: "test-error-message"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetCompletedRequest() assert.Equal(t, tt.want, got) }) } } func TestTaskSource_Ptr(t *testing.T) { tests := []struct { name string s TaskSource want *TaskSource }{ { name: "nil", s: TaskSource(0), want: func() *TaskSource { i := TaskSource(0); return &i }(), }, { name: "non-nil", s: TaskSource(123), want: func() *TaskSource { i := TaskSource(123); return &i }(), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.s.Ptr() assert.Equal(t, tt.want, got) }) } } func TestTaskSource_String(t *testing.T) { tests := []struct { name string s TaskSource want string }{ { name: "history", s: TaskSource(0), want: "HISTORY", }, { name: "db-backlog", s: TaskSource(1), want: "DB_BACKLOG", }, { name: "default", s: TaskSource(123), want: "TaskSource(123)", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.s.String() assert.Equal(t, tt.want, got) }) } } func TestTaskSource_UnmarshalText(t *testing.T) { tests := []struct { name string text string want TaskSource err error }{ { name: "history", text: "HISTORY", want: TaskSource(0), err: nil, }, { name: "db_backlog", text: "DB_BACKLOG", want: TaskSource(1), err: nil, }, { name: "number in string", text: "123", want: TaskSource(123), err: nil, }, { name: "error", text: "unknown", err: errors.New("unknown enum value \"UNKNOWN\" for \"TaskSource\": strconv.ParseInt: parsing \"UNKNOWN\": invalid syntax"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var s TaskSource err := s.UnmarshalText([]byte(tt.text)) if tt.err != nil { assert.Error(t, err) assert.Equal(t, tt.err, err) } else { assert.NoError(t, err) assert.Equal(t, tt.want, s) } }) } } func TestTaskSource_MarshalText(t *testing.T) { tests := []struct { name string s TaskSource want string }{ { name: "history", s: TaskSource(0), want: "HISTORY", }, { name: "db-backlog", s: TaskSource(1), want: "DB_BACKLOG", }, { name: "default", s: TaskSource(123), want: "TaskSource(123)", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b, err := tt.s.MarshalText() assert.NoError(t, err) assert.Equal(t, tt.want, string(b)) }) } } func TestMatchingUpdateTaskListPartitionConfigRequest_GetTaskListType(t *testing.T) { tests := []struct { name string req *MatchingUpdateTaskListPartitionConfigRequest want TaskListType }{ { name: "nil request", req: nil, want: TaskListTypeDecision, }, { name: "empty request", req: &MatchingUpdateTaskListPartitionConfigRequest{}, want: TaskListTypeDecision, }, { name: "non empty request", req: &MatchingUpdateTaskListPartitionConfigRequest{TaskListType: TaskListTypeActivity.Ptr()}, want: TaskListTypeActivity, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskListType() assert.Equal(t, tt.want, got) }) } } func TestMatchingRefreshTaskListPartitionConfigRequest_GetTaskListType(t *testing.T) { tests := []struct { name string req *MatchingRefreshTaskListPartitionConfigRequest want TaskListType }{ { name: "nil request", req: nil, want: TaskListTypeDecision, }, { name: "empty request", req: &MatchingRefreshTaskListPartitionConfigRequest{}, want: TaskListTypeDecision, }, { name: "non empty request", req: &MatchingRefreshTaskListPartitionConfigRequest{TaskListType: TaskListTypeActivity.Ptr()}, want: TaskListTypeActivity, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetTaskListType() assert.Equal(t, tt.want, got) }) } } ================================================ FILE: common/types/predicate.go ================================================ package types type PredicateType int32 func (p PredicateType) Ptr() *PredicateType { return &p } const ( PredicateTypeUniversal PredicateType = iota PredicateTypeEmpty PredicateTypeDomainID NumPredicateTypes ) type UniversalPredicateAttributes struct{} func (u *UniversalPredicateAttributes) Copy() *UniversalPredicateAttributes { if u == nil { return nil } return &UniversalPredicateAttributes{} } type EmptyPredicateAttributes struct{} func (e *EmptyPredicateAttributes) Copy() *EmptyPredicateAttributes { if e == nil { return nil } return &EmptyPredicateAttributes{} } type DomainIDPredicateAttributes struct { DomainIDs []string IsExclusive *bool } func (d *DomainIDPredicateAttributes) Copy() *DomainIDPredicateAttributes { if d == nil { return nil } return &DomainIDPredicateAttributes{ DomainIDs: d.DomainIDs, IsExclusive: d.IsExclusive, } } func (d *DomainIDPredicateAttributes) GetIsExclusive() bool { if d.IsExclusive == nil { return false } return *d.IsExclusive } type Predicate struct { PredicateType PredicateType UniversalPredicateAttributes *UniversalPredicateAttributes EmptyPredicateAttributes *EmptyPredicateAttributes DomainIDPredicateAttributes *DomainIDPredicateAttributes } func (p *Predicate) Copy() *Predicate { if p == nil { return nil } return &Predicate{ PredicateType: p.PredicateType, UniversalPredicateAttributes: p.UniversalPredicateAttributes.Copy(), EmptyPredicateAttributes: p.EmptyPredicateAttributes.Copy(), DomainIDPredicateAttributes: p.DomainIDPredicateAttributes.Copy(), } } func (p *Predicate) GetDomainIDPredicateAttributes() *DomainIDPredicateAttributes { if p == nil { return nil } if p.PredicateType != PredicateTypeDomainID { return nil } return p.DomainIDPredicateAttributes } ================================================ FILE: common/types/replicator.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "fmt" "strconv" "strings" "unsafe" ) // DLQType is an internal type (TBD...) type DLQType int32 // Ptr is a helper function for getting pointer value func (e DLQType) Ptr() *DLQType { return &e } // String returns a readable string representation of DLQType. func (e DLQType) String() string { w := int32(e) switch w { case 0: return "Replication" case 1: return "Domain" } return fmt.Sprintf("DLQType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *DLQType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "REPLICATION": *e = DLQTypeReplication return nil case "DOMAIN": *e = DLQTypeDomain return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DLQType", err) } *e = DLQType(val) return nil } } // MarshalText encodes DLQType to text. func (e DLQType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // DLQTypeReplication is an option for DLQType DLQTypeReplication DLQType = iota // DLQTypeDomain is an option for DLQType DLQTypeDomain ) // DomainOperation is an internal type (TBD...) type DomainOperation int32 // Ptr is a helper function for getting pointer value func (e DomainOperation) Ptr() *DomainOperation { return &e } // String returns a readable string representation of DomainOperation. func (e DomainOperation) String() string { w := int32(e) switch w { case 0: return "Create" case 1: return "Update" case 2: return "Delete" } return fmt.Sprintf("DomainOperation(%d)", w) } // ByteSize returns the approximate memory used in bytes func (e *DomainOperation) ByteSize() uint64 { if e == nil { return 0 } return uint64(unsafe.Sizeof(*e)) } // UnmarshalText parses enum value from string representation func (e *DomainOperation) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "CREATE": *e = DomainOperationCreate return nil case "UPDATE": *e = DomainOperationUpdate return nil case "DELETE": *e = DomainOperationDelete return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DomainOperation", err) } *e = DomainOperation(val) return nil } } // MarshalText encodes DomainOperation to text. func (e DomainOperation) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // DomainOperationCreate is an option for DomainOperation DomainOperationCreate DomainOperation = iota // DomainOperationUpdate is an option for DomainOperation DomainOperationUpdate // DomainOperationDelete is an option for DomainOperation DomainOperationDelete ) // DomainTaskAttributes is an internal type (TBD...) type DomainTaskAttributes struct { DomainOperation *DomainOperation `json:"domainOperation,omitempty"` ID string `json:"id,omitempty"` Info *DomainInfo `json:"info,omitempty"` Config *DomainConfiguration `json:"config,omitempty"` ReplicationConfig *DomainReplicationConfiguration `json:"replicationConfig,omitempty"` ConfigVersion int64 `json:"configVersion,omitempty"` FailoverVersion int64 `json:"failoverVersion,omitempty"` PreviousFailoverVersion int64 `json:"previousFailoverVersion,omitempty"` } // GetDomainOperation is an internal getter (TBD...) func (v *DomainTaskAttributes) GetDomainOperation() (o DomainOperation) { if v != nil && v.DomainOperation != nil { return *v.DomainOperation } return } // GetID is an internal getter (TBD...) func (v *DomainTaskAttributes) GetID() (o string) { if v != nil { return v.ID } return } // GetInfo is an internal getter (TBD...) func (v *DomainTaskAttributes) GetInfo() (o *DomainInfo) { if v != nil && v.Info != nil { return v.Info } return } // GetConfigVersion is an internal getter (TBD...) func (v *DomainTaskAttributes) GetConfigVersion() (o int64) { if v != nil { return v.ConfigVersion } return } // GetFailoverVersion is an internal getter (TBD...) func (v *DomainTaskAttributes) GetFailoverVersion() (o int64) { if v != nil { return v.FailoverVersion } return } // GetPreviousFailoverVersion is an internal getter (TBD...) func (v *DomainTaskAttributes) GetPreviousFailoverVersion() (o int64) { if v != nil { return v.PreviousFailoverVersion } return } // ByteSize returns the approximate memory used in bytes func (v *DomainTaskAttributes) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += v.DomainOperation.ByteSize() size += uint64(len(v.ID)) size += v.Info.ByteSize() size += v.Config.ByteSize() size += v.ReplicationConfig.ByteSize() return size } // FailoverMarkerAttributes is an internal type (TBD...) type FailoverMarkerAttributes struct { DomainID string `json:"domainID,omitempty"` FailoverVersion int64 `json:"failoverVersion,omitempty"` CreationTime *int64 `json:"creationTime,omitempty"` } // GetDomainID is an internal getter (TBD...) func (v *FailoverMarkerAttributes) GetDomainID() (o string) { if v != nil { return v.DomainID } return } // GetFailoverVersion is an internal getter (TBD...) func (v *FailoverMarkerAttributes) GetFailoverVersion() (o int64) { if v != nil { return v.FailoverVersion } return } // GetCreationTime is an internal getter (TBD...) func (v *FailoverMarkerAttributes) GetCreationTime() (o int64) { if v != nil && v.CreationTime != nil { return *v.CreationTime } return } // ByteSize returns the approximate memory used in bytes func (v *FailoverMarkerAttributes) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += uint64(len(v.DomainID)) if v.CreationTime != nil { size += uint64(unsafe.Sizeof(*v.CreationTime)) } return size } // FailoverMarkers is an internal type (TBD...) type FailoverMarkers struct { FailoverMarkers []*FailoverMarkerAttributes `json:"failoverMarkers,omitempty"` } // GetDLQReplicationMessagesRequest is an internal type (TBD...) type GetDLQReplicationMessagesRequest struct { TaskInfos []*ReplicationTaskInfo `json:"taskInfos,omitempty"` } // GetTaskInfos is an internal getter (TBD...) func (v *GetDLQReplicationMessagesRequest) GetTaskInfos() (o []*ReplicationTaskInfo) { if v != nil && v.TaskInfos != nil { return v.TaskInfos } return } // GetDLQReplicationMessagesResponse is an internal type (TBD...) type GetDLQReplicationMessagesResponse struct { ReplicationTasks []*ReplicationTask `json:"replicationTasks,omitempty"` } // GetDomainReplicationMessagesRequest is an internal type (TBD...) type GetDomainReplicationMessagesRequest struct { LastRetrievedMessageID *int64 `json:"lastRetrievedMessageId,omitempty"` LastProcessedMessageID *int64 `json:"lastProcessedMessageId,omitempty"` ClusterName string `json:"clusterName,omitempty"` } // GetLastRetrievedMessageID is an internal getter (TBD...) func (v *GetDomainReplicationMessagesRequest) GetLastRetrievedMessageID() (o int64) { if v != nil && v.LastRetrievedMessageID != nil { return *v.LastRetrievedMessageID } return } // GetLastProcessedMessageID is an internal getter (TBD...) func (v *GetDomainReplicationMessagesRequest) GetLastProcessedMessageID() (o int64) { if v != nil && v.LastProcessedMessageID != nil { return *v.LastProcessedMessageID } return } // GetClusterName is an internal getter (TBD...) func (v *GetDomainReplicationMessagesRequest) GetClusterName() (o string) { if v != nil { return v.ClusterName } return } // GetDomainReplicationMessagesResponse is an internal type (TBD...) type GetDomainReplicationMessagesResponse struct { Messages *ReplicationMessages `json:"messages,omitempty"` } // GetReplicationMessagesRequest is an internal type (TBD...) type GetReplicationMessagesRequest struct { Tokens []*ReplicationToken `json:"tokens,omitempty"` ClusterName string `json:"clusterName,omitempty"` } // GetClusterName is an internal getter (TBD...) func (v *GetReplicationMessagesRequest) GetClusterName() (o string) { if v != nil { return v.ClusterName } return } // GetReplicationMessagesResponse is an internal type (TBD...) type GetReplicationMessagesResponse struct { MessagesByShard map[int32]*ReplicationMessages `json:"messagesByShard,omitempty"` } // GetMessagesByShard is an internal getter (TBD...) func (v *GetReplicationMessagesResponse) GetMessagesByShard() (o map[int32]*ReplicationMessages) { if v != nil && v.MessagesByShard != nil { return v.MessagesByShard } return } // GetEarliestCreationTime returns the earliest creation time of replication tasks if it exists, otherwise return nil func (v *GetReplicationMessagesResponse) GetEarliestCreationTime() *int64 { if v == nil { return nil } var earliestTime *int64 for _, messages := range v.MessagesByShard { creationTime := messages.GetEarliestCreationTime() if creationTime == nil { continue } if earliestTime == nil || *creationTime < *earliestTime { earliestTime = creationTime } } if earliestTime == nil { return nil } // avoid returning a pointer to the internal value // for immutability result := *earliestTime return &result } // HistoryTaskV2Attributes is an internal type (TBD...) type HistoryTaskV2Attributes struct { DomainID string `json:"domainId,omitempty"` WorkflowID string `json:"workflowId,omitempty"` RunID string `json:"runId,omitempty"` VersionHistoryItems []*VersionHistoryItem `json:"versionHistoryItems,omitempty"` Events *DataBlob `json:"events,omitempty"` NewRunEvents *DataBlob `json:"newRunEvents,omitempty"` } // GetDomainID is an internal getter (TBD...) func (v *HistoryTaskV2Attributes) GetDomainID() (o string) { if v != nil { return v.DomainID } return } // GetWorkflowID is an internal getter (TBD...) func (v *HistoryTaskV2Attributes) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *HistoryTaskV2Attributes) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetVersionHistoryItems is an internal getter (TBD...) func (v *HistoryTaskV2Attributes) GetVersionHistoryItems() (o []*VersionHistoryItem) { if v != nil && v.VersionHistoryItems != nil { return v.VersionHistoryItems } return } // GetEvents is an internal getter (TBD...) func (v *HistoryTaskV2Attributes) GetEvents() (o *DataBlob) { if v != nil && v.Events != nil { return v.Events } return } // GetNewRunEvents is an internal getter (TBD...) func (v *HistoryTaskV2Attributes) GetNewRunEvents() (o *DataBlob) { if v != nil && v.NewRunEvents != nil { return v.NewRunEvents } return } // ByteSize returns the approximate memory used in bytes func (v *HistoryTaskV2Attributes) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += uint64(len(v.DomainID)) size += uint64(len(v.WorkflowID)) size += uint64(len(v.RunID)) size += uint64(len(v.VersionHistoryItems)) * uint64(unsafe.Sizeof((*VersionHistoryItem)(nil))) for _, item := range v.VersionHistoryItems { size += item.ByteSize() } size += v.Events.ByteSize() size += v.NewRunEvents.ByteSize() return size } type CountDLQMessagesRequest struct { // ForceFetch will force fetching current values from DB // instead of using cached values used for emitting metrics. ForceFetch bool } type CountDLQMessagesResponse struct { History map[HistoryDLQCountKey]int64 Domain int64 } type HistoryDLQCountKey struct { ShardID int32 SourceCluster string } type HistoryCountDLQMessagesResponse struct { Entries map[HistoryDLQCountKey]int64 } // MergeDLQMessagesRequest is an internal type (TBD...) type MergeDLQMessagesRequest struct { Type *DLQType `json:"type,omitempty"` ShardID int32 `json:"shardID,omitempty"` SourceCluster string `json:"sourceCluster,omitempty"` InclusiveEndMessageID *int64 `json:"inclusiveEndMessageID,omitempty"` MaximumPageSize int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetType is an internal getter (TBD...) func (v *MergeDLQMessagesRequest) GetType() (o DLQType) { if v != nil && v.Type != nil { return *v.Type } return } // GetShardID is an internal getter (TBD...) func (v *MergeDLQMessagesRequest) GetShardID() (o int32) { if v != nil { return v.ShardID } return } // GetSourceCluster is an internal getter (TBD...) func (v *MergeDLQMessagesRequest) GetSourceCluster() (o string) { if v != nil { return v.SourceCluster } return } // GetInclusiveEndMessageID is an internal getter (TBD...) func (v *MergeDLQMessagesRequest) GetInclusiveEndMessageID() (o int64) { if v != nil && v.InclusiveEndMessageID != nil { return *v.InclusiveEndMessageID } return } // GetMaximumPageSize is an internal getter (TBD...) func (v *MergeDLQMessagesRequest) GetMaximumPageSize() (o int32) { if v != nil { return v.MaximumPageSize } return } // GetNextPageToken is an internal getter (TBD...) func (v *MergeDLQMessagesRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // MergeDLQMessagesResponse is an internal type (TBD...) type MergeDLQMessagesResponse struct { NextPageToken []byte `json:"nextPageToken,omitempty"` } // PurgeDLQMessagesRequest is an internal type (TBD...) type PurgeDLQMessagesRequest struct { Type *DLQType `json:"type,omitempty"` ShardID int32 `json:"shardID,omitempty"` SourceCluster string `json:"sourceCluster,omitempty"` InclusiveEndMessageID *int64 `json:"inclusiveEndMessageID,omitempty"` } // GetType is an internal getter (TBD...) func (v *PurgeDLQMessagesRequest) GetType() (o DLQType) { if v != nil && v.Type != nil { return *v.Type } return } // GetShardID is an internal getter (TBD...) func (v *PurgeDLQMessagesRequest) GetShardID() (o int32) { if v != nil { return v.ShardID } return } // GetSourceCluster is an internal getter (TBD...) func (v *PurgeDLQMessagesRequest) GetSourceCluster() (o string) { if v != nil { return v.SourceCluster } return } // GetInclusiveEndMessageID is an internal getter (TBD...) func (v *PurgeDLQMessagesRequest) GetInclusiveEndMessageID() (o int64) { if v != nil && v.InclusiveEndMessageID != nil { return *v.InclusiveEndMessageID } return } // ReadDLQMessagesRequest is an internal type (TBD...) type ReadDLQMessagesRequest struct { Type *DLQType `json:"type,omitempty"` ShardID int32 `json:"shardID,omitempty"` SourceCluster string `json:"sourceCluster,omitempty"` InclusiveEndMessageID *int64 `json:"inclusiveEndMessageID,omitempty"` MaximumPageSize int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetType is an internal getter (TBD...) func (v *ReadDLQMessagesRequest) GetType() (o DLQType) { if v != nil && v.Type != nil { return *v.Type } return } // GetShardID is an internal getter (TBD...) func (v *ReadDLQMessagesRequest) GetShardID() (o int32) { if v != nil { return v.ShardID } return } // GetSourceCluster is an internal getter (TBD...) func (v *ReadDLQMessagesRequest) GetSourceCluster() (o string) { if v != nil { return v.SourceCluster } return } // GetInclusiveEndMessageID is an internal getter (TBD...) func (v *ReadDLQMessagesRequest) GetInclusiveEndMessageID() (o int64) { if v != nil && v.InclusiveEndMessageID != nil { return *v.InclusiveEndMessageID } return } // GetMaximumPageSize is an internal getter (TBD...) func (v *ReadDLQMessagesRequest) GetMaximumPageSize() (o int32) { if v != nil { return v.MaximumPageSize } return } // GetNextPageToken is an internal getter (TBD...) func (v *ReadDLQMessagesRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // ReadDLQMessagesResponse is an internal type (TBD...) type ReadDLQMessagesResponse struct { Type *DLQType `json:"type,omitempty"` ReplicationTasks []*ReplicationTask `json:"replicationTasks,omitempty"` ReplicationTasksInfo []*ReplicationTaskInfo `json:"replicationTasksInfo,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // ReplicationMessages is an internal type (TBD...) type ReplicationMessages struct { ReplicationTasks []*ReplicationTask `json:"replicationTasks,omitempty"` LastRetrievedMessageID int64 `json:"lastRetrievedMessageId,omitempty"` HasMore bool `json:"hasMore,omitempty"` SyncShardStatus *SyncShardStatus `json:"syncShardStatus,omitempty"` } // GetReplicationTasks is an internal getter (TBD...) func (v *ReplicationMessages) GetReplicationTasks() (o []*ReplicationTask) { if v != nil && v.ReplicationTasks != nil { return v.ReplicationTasks } return } // GetLastRetrievedMessageID is an internal getter (TBD...) func (v *ReplicationMessages) GetLastRetrievedMessageID() (o int64) { if v != nil { return v.LastRetrievedMessageID } return } // GetHasMore is an internal getter (TBD...) func (v *ReplicationMessages) GetHasMore() (o bool) { if v != nil { return v.HasMore } return } // GetSyncShardStatus is an internal getter (TBD...) func (v *ReplicationMessages) GetSyncShardStatus() (o *SyncShardStatus) { if v != nil && v.SyncShardStatus != nil { return v.SyncShardStatus } return } // GetEarliestCreationTime returns the earliest message time in the replication tasks if it exists // otherwise return nil func (v *ReplicationMessages) GetEarliestCreationTime() *int64 { if v == nil { return nil } var earliestTime *int64 for _, task := range v.GetReplicationTasks() { if task.CreationTime == nil { continue } if earliestTime == nil || *task.CreationTime < *earliestTime { earliestTime = task.CreationTime } } if earliestTime == nil { return nil } // avoid returning a pointer to the internal value // for immutability result := *earliestTime return &result } // ReplicationMessagesSizeFn is a function type to calculate the size of ReplicationMessages type ReplicationMessagesSizeFn func(v *ReplicationMessages) int // ReplicationTaskSizeFn is a function type to calculate the size of a single ReplicationTask type ReplicationTaskSizeFn func(v *ReplicationTask) int // ReplicationTask is an internal type (TBD...) type ReplicationTask struct { TaskType *ReplicationTaskType `json:"taskType,omitempty"` SourceTaskID int64 `json:"sourceTaskId,omitempty"` DomainTaskAttributes *DomainTaskAttributes `json:"domainTaskAttributes,omitempty"` SyncShardStatusTaskAttributes *SyncShardStatusTaskAttributes `json:"syncShardStatusTaskAttributes,omitempty"` SyncActivityTaskAttributes *SyncActivityTaskAttributes `json:"syncActivityTaskAttributes,omitempty"` HistoryTaskV2Attributes *HistoryTaskV2Attributes `json:"historyTaskV2Attributes,omitempty"` FailoverMarkerAttributes *FailoverMarkerAttributes `json:"failoverMarkerAttributes,omitempty"` CreationTime *int64 `json:"creationTime,omitempty"` } // GetTaskType is an internal getter (TBD...) func (v *ReplicationTask) GetTaskType() (o ReplicationTaskType) { if v != nil && v.TaskType != nil { return *v.TaskType } return } // GetSourceTaskID is an internal getter (TBD...) func (v *ReplicationTask) GetSourceTaskID() (o int64) { if v != nil { return v.SourceTaskID } return } // GetSequenceID implements cache.AckCacheItem interface by delegating to GetSourceTaskID func (v *ReplicationTask) GetSequenceID() int64 { return v.GetSourceTaskID() } // GetDomainTaskAttributes is an internal getter (TBD...) func (v *ReplicationTask) GetDomainTaskAttributes() (o *DomainTaskAttributes) { if v != nil && v.DomainTaskAttributes != nil { return v.DomainTaskAttributes } return } // GetSyncActivityTaskAttributes is an internal getter (TBD...) func (v *ReplicationTask) GetSyncActivityTaskAttributes() (o *SyncActivityTaskAttributes) { if v != nil && v.SyncActivityTaskAttributes != nil { return v.SyncActivityTaskAttributes } return } // GetHistoryTaskV2Attributes is an internal getter (TBD...) func (v *ReplicationTask) GetHistoryTaskV2Attributes() (o *HistoryTaskV2Attributes) { if v != nil && v.HistoryTaskV2Attributes != nil { return v.HistoryTaskV2Attributes } return } // GetFailoverMarkerAttributes is an internal getter (TBD...) func (v *ReplicationTask) GetFailoverMarkerAttributes() (o *FailoverMarkerAttributes) { if v != nil && v.FailoverMarkerAttributes != nil { return v.FailoverMarkerAttributes } return } // GetCreationTime is an internal getter (TBD...) func (v *ReplicationTask) GetCreationTime() (o int64) { if v != nil && v.CreationTime != nil { return *v.CreationTime } return } // ByteSize returns the approximate memory used in bytes func (v *ReplicationTask) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += v.TaskType.ByteSize() size += v.DomainTaskAttributes.ByteSize() size += v.SyncShardStatusTaskAttributes.ByteSize() size += v.SyncActivityTaskAttributes.ByteSize() size += v.HistoryTaskV2Attributes.ByteSize() size += v.FailoverMarkerAttributes.ByteSize() if v.CreationTime != nil { size += uint64(unsafe.Sizeof(*v.CreationTime)) } return size } // ReplicationTaskInfo is an internal type (TBD...) type ReplicationTaskInfo struct { DomainID string `json:"domainID,omitempty"` WorkflowID string `json:"workflowID,omitempty"` RunID string `json:"runID,omitempty"` TaskType int16 `json:"taskType,omitempty"` TaskID int64 `json:"taskID,omitempty"` Version int64 `json:"version,omitempty"` FirstEventID int64 `json:"firstEventID,omitempty"` NextEventID int64 `json:"nextEventID,omitempty"` ScheduledID int64 `json:"scheduledID,omitempty"` } // GetDomainID is an internal getter (TBD...) func (v *ReplicationTaskInfo) GetDomainID() (o string) { if v != nil { return v.DomainID } return } // GetWorkflowID is an internal getter (TBD...) func (v *ReplicationTaskInfo) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *ReplicationTaskInfo) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetTaskType is an internal getter (TBD...) func (v *ReplicationTaskInfo) GetTaskType() (o int16) { if v != nil { return v.TaskType } return } // GetTaskID is an internal getter (TBD...) func (v *ReplicationTaskInfo) GetTaskID() (o int64) { if v != nil { return v.TaskID } return } // GetVersion is an internal getter (TBD...) func (v *ReplicationTaskInfo) GetVersion() (o int64) { if v != nil { return v.Version } return } // GetFirstEventID is an internal getter (TBD...) func (v *ReplicationTaskInfo) GetFirstEventID() (o int64) { if v != nil { return v.FirstEventID } return } // GetNextEventID is an internal getter (TBD...) func (v *ReplicationTaskInfo) GetNextEventID() (o int64) { if v != nil { return v.NextEventID } return } // GetScheduledID is an internal getter (TBD...) func (v *ReplicationTaskInfo) GetScheduledID() (o int64) { if v != nil { return v.ScheduledID } return } // ReplicationTaskType is an internal type (TBD...) type ReplicationTaskType int32 // Ptr is a helper function for getting pointer value func (e ReplicationTaskType) Ptr() *ReplicationTaskType { return &e } // String returns a readable string representation of ReplicationTaskType. func (e ReplicationTaskType) String() string { w := int32(e) switch w { case 0: return "Domain" case 1: return "History" case 2: return "SyncShardStatus" case 3: return "SyncActivity" case 4: return "HistoryMetadata" case 5: return "HistoryV2" case 6: return "FailoverMarker" } return fmt.Sprintf("ReplicationTaskType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *ReplicationTaskType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "DOMAIN": *e = ReplicationTaskTypeDomain return nil case "HISTORY": *e = ReplicationTaskTypeHistory return nil case "SYNCSHARDSTATUS": *e = ReplicationTaskTypeSyncShardStatus return nil case "SYNCACTIVITY": *e = ReplicationTaskTypeSyncActivity return nil case "HISTORYMETADATA": *e = ReplicationTaskTypeHistoryMetadata return nil case "HISTORYV2": *e = ReplicationTaskTypeHistoryV2 return nil case "FAILOVERMARKER": *e = ReplicationTaskTypeFailoverMarker return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ReplicationTaskType", err) } *e = ReplicationTaskType(val) return nil } } // MarshalText encodes ReplicationTaskType to text. func (e ReplicationTaskType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // ReplicationTaskTypeDomain is an option for ReplicationTaskType ReplicationTaskTypeDomain ReplicationTaskType = iota // ReplicationTaskTypeHistory is an option for ReplicationTaskType ReplicationTaskTypeHistory // ReplicationTaskTypeSyncShardStatus is an option for ReplicationTaskType ReplicationTaskTypeSyncShardStatus // ReplicationTaskTypeSyncActivity is an option for ReplicationTaskType ReplicationTaskTypeSyncActivity // ReplicationTaskTypeHistoryMetadata is an option for ReplicationTaskType ReplicationTaskTypeHistoryMetadata // ReplicationTaskTypeHistoryV2 is an option for ReplicationTaskType ReplicationTaskTypeHistoryV2 // ReplicationTaskTypeFailoverMarker is an option for ReplicationTaskType ReplicationTaskTypeFailoverMarker ) // ByteSize returns the approximate memory used in bytes func (e *ReplicationTaskType) ByteSize() uint64 { if e == nil { return 0 } return uint64(unsafe.Sizeof(*e)) } // ReplicationToken is an internal type (TBD...) type ReplicationToken struct { ShardID int32 `json:"shardID,omitempty"` LastRetrievedMessageID int64 `json:"lastRetrievedMessageId,omitempty"` LastProcessedMessageID int64 `json:"lastProcessedMessageId,omitempty"` } // GetShardID is an internal getter (TBD...) func (v *ReplicationToken) GetShardID() (o int32) { if v != nil { return v.ShardID } return } // GetLastRetrievedMessageID is an internal getter (TBD...) func (v *ReplicationToken) GetLastRetrievedMessageID() (o int64) { if v != nil { return v.LastRetrievedMessageID } return } // GetLastProcessedMessageID is an internal getter (TBD...) func (v *ReplicationToken) GetLastProcessedMessageID() (o int64) { if v != nil { return v.LastProcessedMessageID } return } // SyncActivityTaskAttributes is an internal type (TBD...) type SyncActivityTaskAttributes struct { DomainID string `json:"domainId,omitempty"` WorkflowID string `json:"workflowId,omitempty"` RunID string `json:"runId,omitempty"` Version int64 `json:"version,omitempty"` ScheduledID int64 `json:"scheduledId,omitempty"` ScheduledTime *int64 `json:"scheduledTime,omitempty"` StartedID int64 `json:"startedId,omitempty"` StartedTime *int64 `json:"startedTime,omitempty"` LastHeartbeatTime *int64 `json:"lastHeartbeatTime,omitempty"` Details []byte `json:"details,omitempty"` Attempt int32 `json:"attempt,omitempty"` LastFailureReason *string `json:"lastFailureReason,omitempty"` LastWorkerIdentity string `json:"lastWorkerIdentity,omitempty"` LastFailureDetails []byte `json:"lastFailureDetails,omitempty"` VersionHistory *VersionHistory `json:"versionHistory,omitempty"` } // GetDomainID is an internal getter (TBD...) func (v *SyncActivityTaskAttributes) GetDomainID() (o string) { if v != nil { return v.DomainID } return } // GetWorkflowID is an internal getter (TBD...) func (v *SyncActivityTaskAttributes) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *SyncActivityTaskAttributes) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetVersion is an internal getter (TBD...) func (v *SyncActivityTaskAttributes) GetVersion() (o int64) { if v != nil { return v.Version } return } // GetScheduledID is an internal getter (TBD...) func (v *SyncActivityTaskAttributes) GetScheduledID() (o int64) { if v != nil { return v.ScheduledID } return } // GetScheduledTime is an internal getter (TBD...) func (v *SyncActivityTaskAttributes) GetScheduledTime() (o int64) { if v != nil && v.ScheduledTime != nil { return *v.ScheduledTime } return } // GetStartedID is an internal getter (TBD...) func (v *SyncActivityTaskAttributes) GetStartedID() (o int64) { if v != nil { return v.StartedID } return } // GetStartedTime is an internal getter (TBD...) func (v *SyncActivityTaskAttributes) GetStartedTime() (o int64) { if v != nil && v.StartedTime != nil { return *v.StartedTime } return } // GetLastHeartbeatTime is an internal getter (TBD...) func (v *SyncActivityTaskAttributes) GetLastHeartbeatTime() (o int64) { if v != nil && v.LastHeartbeatTime != nil { return *v.LastHeartbeatTime } return } // GetVersionHistory is an internal getter (TBD...) func (v *SyncActivityTaskAttributes) GetVersionHistory() (o *VersionHistory) { if v != nil && v.VersionHistory != nil { return v.VersionHistory } return } // ByteSize returns the approximate memory used in bytes func (v *SyncActivityTaskAttributes) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += uint64(len(v.DomainID)) size += uint64(len(v.WorkflowID)) size += uint64(len(v.RunID)) if v.ScheduledTime != nil { size += uint64(unsafe.Sizeof(*v.ScheduledTime)) } if v.StartedTime != nil { size += uint64(unsafe.Sizeof(*v.StartedTime)) } if v.LastHeartbeatTime != nil { size += uint64(unsafe.Sizeof(*v.LastHeartbeatTime)) } size += uint64(len(v.Details)) if v.LastFailureReason != nil { size += uint64(unsafe.Sizeof(*v.LastFailureReason)) size += uint64(len(*v.LastFailureReason)) } size += uint64(len(v.LastWorkerIdentity)) size += uint64(len(v.LastFailureDetails)) size += v.VersionHistory.ByteSize() return size } // SyncShardStatus is an internal type (TBD...) type SyncShardStatus struct { Timestamp *int64 `json:"timestamp,omitempty"` } // GetTimestamp is an internal getter (TBD...) func (v *SyncShardStatus) GetTimestamp() (o int64) { if v != nil && v.Timestamp != nil { return *v.Timestamp } return } // SyncShardStatusTaskAttributes is an internal type (TBD...) type SyncShardStatusTaskAttributes struct { SourceCluster string `json:"sourceCluster,omitempty"` ShardID int64 `json:"shardId,omitempty"` Timestamp *int64 `json:"timestamp,omitempty"` } // ByteSize returns the approximate memory used in bytes func (v *SyncShardStatusTaskAttributes) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += uint64(len(v.SourceCluster)) if v.Timestamp != nil { size += uint64(unsafe.Sizeof(*v.Timestamp)) } return size } ================================================ FILE: common/types/replicator_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/thriftrw/ptr" ) func TestDLQType_Ptr(t *testing.T) { dlqType := DLQTypeReplication ptr := dlqType.Ptr() assert.Equal(t, &dlqType, ptr) } func TestDLQType_String(t *testing.T) { dlqType := DLQTypeReplication assert.Equal(t, "Replication", dlqType.String()) dlqType = DLQTypeDomain assert.Equal(t, "Domain", dlqType.String()) dlqType = 2 assert.Equal(t, "DLQType(2)", dlqType.String()) } func TestDLQType_UnmarshalText(t *testing.T) { var dlqType DLQType err := dlqType.UnmarshalText([]byte("Replication")) assert.NoError(t, err) assert.Equal(t, DLQTypeReplication, dlqType) err = dlqType.UnmarshalText([]byte("Domain")) assert.NoError(t, err) assert.Equal(t, DLQTypeDomain, dlqType) err = dlqType.UnmarshalText([]byte("2")) assert.NoError(t, err) assert.Equal(t, DLQType(2), dlqType) err = dlqType.UnmarshalText([]byte("Invalid")) assert.Error(t, err) } func TestDLQType_MarshalText(t *testing.T) { dlqType := DLQTypeReplication text, err := dlqType.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("Replication"), text) dlqType = DLQTypeDomain text, err = dlqType.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("Domain"), text) } func TestDomainOperation_Ptr(t *testing.T) { domainOp := DomainOperationCreate ptr := domainOp.Ptr() assert.Equal(t, &domainOp, ptr) } func TestDomainOperation_String(t *testing.T) { domainOp := DomainOperationCreate assert.Equal(t, "Create", domainOp.String()) domainOp = DomainOperationUpdate assert.Equal(t, "Update", domainOp.String()) domainOp = DomainOperationDelete assert.Equal(t, "Delete", domainOp.String()) domainOp = 3 assert.Equal(t, "DomainOperation(3)", domainOp.String()) } func TestDomainOperation_UnmarshalText(t *testing.T) { var domainOp DomainOperation err := domainOp.UnmarshalText([]byte("Create")) assert.NoError(t, err) assert.Equal(t, DomainOperationCreate, domainOp) err = domainOp.UnmarshalText([]byte("Update")) assert.NoError(t, err) assert.Equal(t, DomainOperationUpdate, domainOp) err = domainOp.UnmarshalText([]byte("2")) assert.NoError(t, err) assert.Equal(t, DomainOperation(2), domainOp) err = domainOp.UnmarshalText([]byte("Invalid")) assert.Error(t, err) } func TestDomainOperation_MarshalText(t *testing.T) { domainOp := DomainOperationCreate text, err := domainOp.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("Create"), text) domainOp = DomainOperationUpdate text, err = domainOp.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("Update"), text) } func TestDomainTaskAttributes_GetDomainOperation(t *testing.T) { domainOp := DomainOperationCreate testStruct := DomainTaskAttributes{ DomainOperation: &domainOp, } res := testStruct.GetDomainOperation() assert.Equal(t, domainOp, res) var nilStruct *DomainTaskAttributes res = nilStruct.GetDomainOperation() assert.Equal(t, DomainOperation(0), res) } func TestDomainTaskAttributes_GetID(t *testing.T) { testStruct := DomainTaskAttributes{ ID: "test-id", } res := testStruct.GetID() assert.Equal(t, "test-id", res) var nilStruct *DomainTaskAttributes res = nilStruct.GetID() assert.Equal(t, "", res) } func TestDomainTaskAttributes_GetInfo(t *testing.T) { domainInfo := &DomainInfo{} testStruct := DomainTaskAttributes{ Info: domainInfo, } res := testStruct.GetInfo() assert.Equal(t, domainInfo, res) var nilStruct *DomainTaskAttributes res = nilStruct.GetInfo() assert.Nil(t, res) } func TestDomainTaskAttributes_GetConfigVersion(t *testing.T) { testStruct := DomainTaskAttributes{ ConfigVersion: 123, } res := testStruct.GetConfigVersion() assert.Equal(t, int64(123), res) var nilStruct *DomainTaskAttributes res = nilStruct.GetConfigVersion() assert.Equal(t, int64(0), res) } func TestDomainTaskAttributes_GetFailoverVersion(t *testing.T) { testStruct := DomainTaskAttributes{ FailoverVersion: 456, } res := testStruct.GetFailoverVersion() assert.Equal(t, int64(456), res) var nilStruct *DomainTaskAttributes res = nilStruct.GetFailoverVersion() assert.Equal(t, int64(0), res) } func TestDomainTaskAttributes_GetPreviousFailoverVersion(t *testing.T) { testStruct := DomainTaskAttributes{ PreviousFailoverVersion: 789, } res := testStruct.GetPreviousFailoverVersion() assert.Equal(t, int64(789), res) var nilStruct *DomainTaskAttributes res = nilStruct.GetPreviousFailoverVersion() assert.Equal(t, int64(0), res) } func TestFailoverMarkerAttributes_GetDomainID(t *testing.T) { testStruct := FailoverMarkerAttributes{ DomainID: "domain-id", } res := testStruct.GetDomainID() assert.Equal(t, "domain-id", res) var nilStruct *FailoverMarkerAttributes res = nilStruct.GetDomainID() assert.Equal(t, "", res) } func TestFailoverMarkerAttributes_GetFailoverVersion(t *testing.T) { testStruct := FailoverMarkerAttributes{ FailoverVersion: 1234, } res := testStruct.GetFailoverVersion() assert.Equal(t, int64(1234), res) var nilStruct *FailoverMarkerAttributes res = nilStruct.GetFailoverVersion() assert.Equal(t, int64(0), res) } func TestFailoverMarkerAttributes_GetCreationTime(t *testing.T) { creationTime := int64(5678) testStruct := FailoverMarkerAttributes{ CreationTime: &creationTime, } res := testStruct.GetCreationTime() assert.Equal(t, creationTime, res) var nilStruct *FailoverMarkerAttributes res = nilStruct.GetCreationTime() assert.Equal(t, int64(0), res) } func TestMergeDLQMessagesRequest_GetType(t *testing.T) { dlqType := DLQTypeReplication testStruct := MergeDLQMessagesRequest{ Type: &dlqType, } res := testStruct.GetType() assert.Equal(t, dlqType, res) var nilStruct *MergeDLQMessagesRequest res = nilStruct.GetType() assert.Equal(t, DLQType(0), res) } func TestMergeDLQMessagesRequest_GetShardID(t *testing.T) { testStruct := MergeDLQMessagesRequest{ ShardID: 101, } res := testStruct.GetShardID() assert.Equal(t, int32(101), res) var nilStruct *MergeDLQMessagesRequest res = nilStruct.GetShardID() assert.Equal(t, int32(0), res) } func TestMergeDLQMessagesRequest_GetSourceCluster(t *testing.T) { testStruct := MergeDLQMessagesRequest{ SourceCluster: "cluster-1", } res := testStruct.GetSourceCluster() assert.Equal(t, "cluster-1", res) var nilStruct *MergeDLQMessagesRequest res = nilStruct.GetSourceCluster() assert.Equal(t, "", res) } func TestMergeDLQMessagesRequest_GetInclusiveEndMessageID(t *testing.T) { endMessageID := int64(102) testStruct := MergeDLQMessagesRequest{ InclusiveEndMessageID: &endMessageID, } res := testStruct.GetInclusiveEndMessageID() assert.Equal(t, endMessageID, res) var nilStruct *MergeDLQMessagesRequest res = nilStruct.GetInclusiveEndMessageID() assert.Equal(t, int64(0), res) } func TestGetDLQReplicationMessagesRequest_GetTaskInfos(t *testing.T) { taskInfos := []*ReplicationTaskInfo{{}, {}} testStruct := GetDLQReplicationMessagesRequest{ TaskInfos: taskInfos, } res := testStruct.GetTaskInfos() assert.Equal(t, taskInfos, res) var nilStruct *GetDLQReplicationMessagesRequest res = nilStruct.GetTaskInfos() assert.Nil(t, res) } func TestGetDomainReplicationMessagesRequest_GetLastRetrievedMessageID(t *testing.T) { lastRetrievedMessageID := int64(12345) testStruct := GetDomainReplicationMessagesRequest{ LastRetrievedMessageID: &lastRetrievedMessageID, } res := testStruct.GetLastRetrievedMessageID() assert.Equal(t, lastRetrievedMessageID, res) var nilStruct *GetDomainReplicationMessagesRequest res = nilStruct.GetLastRetrievedMessageID() assert.Equal(t, int64(0), res) } func TestGetDomainReplicationMessagesRequest_GetLastProcessedMessageID(t *testing.T) { lastProcessedMessageID := int64(67890) testStruct := GetDomainReplicationMessagesRequest{ LastProcessedMessageID: &lastProcessedMessageID, } res := testStruct.GetLastProcessedMessageID() assert.Equal(t, lastProcessedMessageID, res) var nilStruct *GetDomainReplicationMessagesRequest res = nilStruct.GetLastProcessedMessageID() assert.Equal(t, int64(0), res) } func TestGetDomainReplicationMessagesRequest_GetClusterName(t *testing.T) { testStruct := GetDomainReplicationMessagesRequest{ ClusterName: "test-cluster", } res := testStruct.GetClusterName() assert.Equal(t, "test-cluster", res) var nilStruct *GetDomainReplicationMessagesRequest res = nilStruct.GetClusterName() assert.Equal(t, "", res) } func TestGetReplicationMessagesRequest_GetClusterName(t *testing.T) { testStruct := GetReplicationMessagesRequest{ ClusterName: "test-cluster", } res := testStruct.GetClusterName() assert.Equal(t, "test-cluster", res) var nilStruct *GetReplicationMessagesRequest res = nilStruct.GetClusterName() assert.Equal(t, "", res) } func TestGetReplicationMessagesResponse_GetMessagesByShard(t *testing.T) { messagesByShard := map[int32]*ReplicationMessages{ 1: {}, 2: {}, } testStruct := GetReplicationMessagesResponse{ MessagesByShard: messagesByShard, } res := testStruct.GetMessagesByShard() assert.Equal(t, messagesByShard, res) var nilStruct *GetReplicationMessagesResponse res = nilStruct.GetMessagesByShard() assert.Nil(t, res) } func TestGetReplicationMessagesResponse_GetEarliestCreationTime(t *testing.T) { for name, c := range map[string]struct { response *GetReplicationMessagesResponse want *int64 }{ "nil": {response: nil, want: nil}, "zero messages": {response: &GetReplicationMessagesResponse{}, want: nil}, "no messages with creation time": { response: &GetReplicationMessagesResponse{ MessagesByShard: map[int32]*ReplicationMessages{ 1: {ReplicationTasks: []*ReplicationTask{{}, {}, {}}}, 2: {ReplicationTasks: []*ReplicationTask{{}, {}, {}}}, }, }, }, "a few messages with creation time": { response: &GetReplicationMessagesResponse{ MessagesByShard: map[int32]*ReplicationMessages{ 1: {ReplicationTasks: []*ReplicationTask{ {}, {CreationTime: ptr.Int64(20)}, {CreationTime: ptr.Int64(1000)}}, }, 2: {ReplicationTasks: []*ReplicationTask{ {CreationTime: ptr.Int64(10)}, {}, {CreationTime: ptr.Int64(12000)}}}, }, }, want: ptrInt64(10), }, } { t.Run(name, func(t *testing.T) { assert.Equal(t, c.want, c.response.GetEarliestCreationTime()) }) } } func TestCountDLQMessagesResponse(t *testing.T) { history := map[HistoryDLQCountKey]int64{ {ShardID: 1, SourceCluster: "cluster-1"}: 100, } testStruct := CountDLQMessagesResponse{ History: history, Domain: 200, } assert.Equal(t, history, testStruct.History) assert.Equal(t, int64(200), testStruct.Domain) // Test for empty history emptyStruct := CountDLQMessagesResponse{} assert.Nil(t, emptyStruct.History) assert.Equal(t, int64(0), emptyStruct.Domain) } func TestHistoryCountDLQMessagesResponse(t *testing.T) { entries := map[HistoryDLQCountKey]int64{ {ShardID: 1, SourceCluster: "cluster-1"}: 100, } testStruct := HistoryCountDLQMessagesResponse{ Entries: entries, } assert.Equal(t, entries, testStruct.Entries) // Test for empty entries emptyStruct := HistoryCountDLQMessagesResponse{} assert.Nil(t, emptyStruct.Entries) } func TestMergeDLQMessagesRequest_Getters(t *testing.T) { dlqType := DLQTypeReplication endMessageID := int64(102) nextPageToken := []byte("token") testStruct := MergeDLQMessagesRequest{ Type: &dlqType, ShardID: 101, SourceCluster: "cluster-1", InclusiveEndMessageID: &endMessageID, MaximumPageSize: 50, NextPageToken: nextPageToken, } assert.Equal(t, dlqType, testStruct.GetType()) assert.Equal(t, int32(101), testStruct.GetShardID()) assert.Equal(t, "cluster-1", testStruct.GetSourceCluster()) assert.Equal(t, endMessageID, testStruct.GetInclusiveEndMessageID()) assert.Equal(t, int32(50), testStruct.GetMaximumPageSize()) assert.Equal(t, nextPageToken, testStruct.GetNextPageToken()) // Test for nil values var nilStruct *MergeDLQMessagesRequest assert.Equal(t, DLQType(0), nilStruct.GetType()) assert.Equal(t, int32(0), nilStruct.GetShardID()) assert.Equal(t, "", nilStruct.GetSourceCluster()) assert.Equal(t, int64(0), nilStruct.GetInclusiveEndMessageID()) assert.Equal(t, int32(0), nilStruct.GetMaximumPageSize()) assert.Nil(t, nilStruct.GetNextPageToken()) } func TestHistoryTaskV2Attributes_GetDomainID(t *testing.T) { testStruct := HistoryTaskV2Attributes{ DomainID: "domain-id", } res := testStruct.GetDomainID() assert.Equal(t, "domain-id", res) var nilStruct *HistoryTaskV2Attributes res = nilStruct.GetDomainID() assert.Equal(t, "", res) } func TestHistoryTaskV2Attributes_GetWorkflowID(t *testing.T) { testStruct := HistoryTaskV2Attributes{ WorkflowID: "workflow-id", } res := testStruct.GetWorkflowID() assert.Equal(t, "workflow-id", res) var nilStruct *HistoryTaskV2Attributes res = nilStruct.GetWorkflowID() assert.Equal(t, "", res) } func TestHistoryTaskV2Attributes_GetRunID(t *testing.T) { testStruct := HistoryTaskV2Attributes{ RunID: "run-id", } res := testStruct.GetRunID() assert.Equal(t, "run-id", res) var nilStruct *HistoryTaskV2Attributes res = nilStruct.GetRunID() assert.Equal(t, "", res) } func TestHistoryTaskV2Attributes_GetVersionHistoryItems(t *testing.T) { versionHistoryItems := []*VersionHistoryItem{{}, {}} testStruct := HistoryTaskV2Attributes{ VersionHistoryItems: versionHistoryItems, } res := testStruct.GetVersionHistoryItems() assert.Equal(t, versionHistoryItems, res) var nilStruct *HistoryTaskV2Attributes res = nilStruct.GetVersionHistoryItems() assert.Nil(t, res) } func TestHistoryTaskV2Attributes_GetEvents(t *testing.T) { events := &DataBlob{} testStruct := HistoryTaskV2Attributes{ Events: events, } res := testStruct.GetEvents() assert.Equal(t, events, res) var nilStruct *HistoryTaskV2Attributes res = nilStruct.GetEvents() assert.Nil(t, res) } func TestHistoryTaskV2Attributes_GetNewRunEvents(t *testing.T) { newRunEvents := &DataBlob{} testStruct := HistoryTaskV2Attributes{ NewRunEvents: newRunEvents, } res := testStruct.GetNewRunEvents() assert.Equal(t, newRunEvents, res) var nilStruct *HistoryTaskV2Attributes res = nilStruct.GetNewRunEvents() assert.Nil(t, res) } func TestPurgeDLQMessagesRequest_Getters(t *testing.T) { dlqType := DLQTypeReplication endMessageID := int64(12345) testStruct := PurgeDLQMessagesRequest{ Type: &dlqType, ShardID: 101, SourceCluster: "test-cluster", InclusiveEndMessageID: &endMessageID, } assert.Equal(t, dlqType, testStruct.GetType()) assert.Equal(t, int32(101), testStruct.GetShardID()) assert.Equal(t, "test-cluster", testStruct.GetSourceCluster()) assert.Equal(t, endMessageID, testStruct.GetInclusiveEndMessageID()) // Test nil case var nilStruct *PurgeDLQMessagesRequest assert.Equal(t, DLQType(0), nilStruct.GetType()) assert.Equal(t, int32(0), nilStruct.GetShardID()) assert.Equal(t, "", nilStruct.GetSourceCluster()) assert.Equal(t, int64(0), nilStruct.GetInclusiveEndMessageID()) } func TestReadDLQMessagesRequest_Getters(t *testing.T) { dlqType := DLQTypeReplication endMessageID := int64(12345) nextPageToken := []byte("token") testStruct := ReadDLQMessagesRequest{ Type: &dlqType, ShardID: 101, SourceCluster: "test-cluster", InclusiveEndMessageID: &endMessageID, MaximumPageSize: 50, NextPageToken: nextPageToken, } assert.Equal(t, dlqType, testStruct.GetType()) assert.Equal(t, int32(101), testStruct.GetShardID()) assert.Equal(t, "test-cluster", testStruct.GetSourceCluster()) assert.Equal(t, endMessageID, testStruct.GetInclusiveEndMessageID()) assert.Equal(t, int32(50), testStruct.GetMaximumPageSize()) assert.Equal(t, nextPageToken, testStruct.GetNextPageToken()) // Test nil case var nilStruct *ReadDLQMessagesRequest assert.Equal(t, DLQType(0), nilStruct.GetType()) assert.Equal(t, int32(0), nilStruct.GetShardID()) assert.Equal(t, "", nilStruct.GetSourceCluster()) assert.Equal(t, int64(0), nilStruct.GetInclusiveEndMessageID()) assert.Equal(t, int32(0), nilStruct.GetMaximumPageSize()) assert.Nil(t, nilStruct.GetNextPageToken()) } func TestReplicationMessages_GetReplicationTasks(t *testing.T) { tasks := []*ReplicationTask{{}, {}} testStruct := ReplicationMessages{ ReplicationTasks: tasks, } res := testStruct.GetReplicationTasks() assert.Equal(t, tasks, res) var nilStruct *ReplicationMessages res = nilStruct.GetReplicationTasks() assert.Nil(t, res) } func TestReplicationMessages_GetLastRetrievedMessageID(t *testing.T) { testStruct := ReplicationMessages{ LastRetrievedMessageID: 12345, } res := testStruct.GetLastRetrievedMessageID() assert.Equal(t, int64(12345), res) var nilStruct *ReplicationMessages res = nilStruct.GetLastRetrievedMessageID() assert.Equal(t, int64(0), res) } func TestReplicationMessages_GetHasMore(t *testing.T) { testStruct := ReplicationMessages{ HasMore: true, } res := testStruct.GetHasMore() assert.True(t, res) var nilStruct *ReplicationMessages res = nilStruct.GetHasMore() assert.False(t, res) } func TestReplicationMessages_GetSyncShardStatus(t *testing.T) { status := &SyncShardStatus{} testStruct := ReplicationMessages{ SyncShardStatus: status, } res := testStruct.GetSyncShardStatus() assert.Equal(t, status, res) var nilStruct *ReplicationMessages res = nilStruct.GetSyncShardStatus() assert.Nil(t, res) } func TestReplicationMessages_GetEarliestCreationTime(t *testing.T) { for name, c := range map[string]struct { msgs *ReplicationMessages want *int64 }{ "nil": {msgs: nil, want: nil}, "zero tasks": {msgs: &ReplicationMessages{}, want: nil}, "no tasks with creation time": { msgs: &ReplicationMessages{ ReplicationTasks: []*ReplicationTask{ {}, {}, {}, }, }, want: nil, }, "a few tasks with creation time": { msgs: &ReplicationMessages{ ReplicationTasks: []*ReplicationTask{ {CreationTime: ptr.Int64(50)}, {CreationTime: ptr.Int64(20)}, {}, {}, {}, {CreationTime: ptr.Int64(100)}, }, }, want: ptr.Int64(20), }, } { t.Run(name, func(t *testing.T) { assert.Equal(t, c.want, c.msgs.GetEarliestCreationTime()) }) } } func TestReplicationTask_GetTaskType(t *testing.T) { taskType := ReplicationTaskTypeDomain testStruct := ReplicationTask{ TaskType: &taskType, } res := testStruct.GetTaskType() assert.Equal(t, taskType, res) var nilStruct *ReplicationTask res = nilStruct.GetTaskType() assert.Equal(t, ReplicationTaskType(0), res) } func TestReplicationTask_GetSourceTaskID(t *testing.T) { testStruct := ReplicationTask{ SourceTaskID: 12345, } res := testStruct.GetSourceTaskID() assert.Equal(t, int64(12345), res) var nilStruct *ReplicationTask res = nilStruct.GetSourceTaskID() assert.Equal(t, int64(0), res) } func TestReplicationTask_GetSequenceID(t *testing.T) { testStruct := ReplicationTask{ SourceTaskID: 12345, } res := testStruct.GetSequenceID() assert.Equal(t, int64(12345), res) var nilStruct *ReplicationTask res = nilStruct.GetSequenceID() assert.Equal(t, int64(0), res) } func TestReplicationTask_GetDomainTaskAttributes(t *testing.T) { domainTask := &DomainTaskAttributes{} testStruct := ReplicationTask{ DomainTaskAttributes: domainTask, } res := testStruct.GetDomainTaskAttributes() assert.Equal(t, domainTask, res) var nilStruct *ReplicationTask res = nilStruct.GetDomainTaskAttributes() assert.Nil(t, res) } func TestReplicationTask_GetSyncActivityTaskAttributes(t *testing.T) { syncActivityTask := &SyncActivityTaskAttributes{} testStruct := ReplicationTask{ SyncActivityTaskAttributes: syncActivityTask, } res := testStruct.GetSyncActivityTaskAttributes() assert.Equal(t, syncActivityTask, res) var nilStruct *ReplicationTask res = nilStruct.GetSyncActivityTaskAttributes() assert.Nil(t, res) } func TestReplicationTask_GetHistoryTaskV2Attributes(t *testing.T) { historyTaskV2 := &HistoryTaskV2Attributes{} testStruct := ReplicationTask{ HistoryTaskV2Attributes: historyTaskV2, } res := testStruct.GetHistoryTaskV2Attributes() assert.Equal(t, historyTaskV2, res) var nilStruct *ReplicationTask res = nilStruct.GetHistoryTaskV2Attributes() assert.Nil(t, res) } func TestReplicationTask_GetFailoverMarkerAttributes(t *testing.T) { failoverMarker := &FailoverMarkerAttributes{} testStruct := ReplicationTask{ FailoverMarkerAttributes: failoverMarker, } res := testStruct.GetFailoverMarkerAttributes() assert.Equal(t, failoverMarker, res) var nilStruct *ReplicationTask res = nilStruct.GetFailoverMarkerAttributes() assert.Nil(t, res) } func TestReplicationTask_GetCreationTime(t *testing.T) { creationTime := int64(123456) testStruct := ReplicationTask{ CreationTime: &creationTime, } res := testStruct.GetCreationTime() assert.Equal(t, creationTime, res) var nilStruct *ReplicationTask res = nilStruct.GetCreationTime() assert.Equal(t, int64(0), res) } func TestReplicationTaskInfo_GetDomainID(t *testing.T) { testStruct := ReplicationTaskInfo{ DomainID: "domain-id", } res := testStruct.GetDomainID() assert.Equal(t, "domain-id", res) var nilStruct *ReplicationTaskInfo res = nilStruct.GetDomainID() assert.Equal(t, "", res) } func TestReplicationTaskInfo_GetWorkflowID(t *testing.T) { testStruct := ReplicationTaskInfo{ WorkflowID: "workflow-id", } res := testStruct.GetWorkflowID() assert.Equal(t, "workflow-id", res) var nilStruct *ReplicationTaskInfo res = nilStruct.GetWorkflowID() assert.Equal(t, "", res) } func TestReplicationTaskInfo_GetRunID(t *testing.T) { testStruct := ReplicationTaskInfo{ RunID: "run-id", } res := testStruct.GetRunID() assert.Equal(t, "run-id", res) var nilStruct *ReplicationTaskInfo res = nilStruct.GetRunID() assert.Equal(t, "", res) } func TestReplicationTaskInfo_GetTaskType(t *testing.T) { testStruct := ReplicationTaskInfo{ TaskType: 1, } res := testStruct.GetTaskType() assert.Equal(t, int16(1), res) var nilStruct *ReplicationTaskInfo res = nilStruct.GetTaskType() assert.Equal(t, int16(0), res) } func TestReplicationTaskInfo_GetTaskID(t *testing.T) { testStruct := ReplicationTaskInfo{ TaskID: 12345, } res := testStruct.GetTaskID() assert.Equal(t, int64(12345), res) var nilStruct *ReplicationTaskInfo res = nilStruct.GetTaskID() assert.Equal(t, int64(0), res) } func TestReplicationTaskInfo_GetVersion(t *testing.T) { testStruct := ReplicationTaskInfo{ Version: 100, } res := testStruct.GetVersion() assert.Equal(t, int64(100), res) var nilStruct *ReplicationTaskInfo res = nilStruct.GetVersion() assert.Equal(t, int64(0), res) } func TestReplicationTaskInfo_GetFirstEventID(t *testing.T) { testStruct := ReplicationTaskInfo{ FirstEventID: 11111, } res := testStruct.GetFirstEventID() assert.Equal(t, int64(11111), res) var nilStruct *ReplicationTaskInfo res = nilStruct.GetFirstEventID() assert.Equal(t, int64(0), res) } func TestReplicationTaskInfo_GetNextEventID(t *testing.T) { testStruct := ReplicationTaskInfo{ NextEventID: 22222, } res := testStruct.GetNextEventID() assert.Equal(t, int64(22222), res) var nilStruct *ReplicationTaskInfo res = nilStruct.GetNextEventID() assert.Equal(t, int64(0), res) } func TestReplicationTaskInfo_GetScheduledID(t *testing.T) { testStruct := ReplicationTaskInfo{ ScheduledID: 33333, } res := testStruct.GetScheduledID() assert.Equal(t, int64(33333), res) var nilStruct *ReplicationTaskInfo res = nilStruct.GetScheduledID() assert.Equal(t, int64(0), res) } func TestReplicationToken_GetShardID(t *testing.T) { testStruct := ReplicationToken{ ShardID: 1, } res := testStruct.GetShardID() assert.Equal(t, int32(1), res) var nilStruct *ReplicationToken res = nilStruct.GetShardID() assert.Equal(t, int32(0), res) } func TestReplicationToken_GetLastRetrievedMessageID(t *testing.T) { testStruct := ReplicationToken{ LastRetrievedMessageID: 123456, } res := testStruct.GetLastRetrievedMessageID() assert.Equal(t, int64(123456), res) var nilStruct *ReplicationToken res = nilStruct.GetLastRetrievedMessageID() assert.Equal(t, int64(0), res) } func TestReplicationToken_GetLastProcessedMessageID(t *testing.T) { testStruct := ReplicationToken{ LastProcessedMessageID: 654321, } res := testStruct.GetLastProcessedMessageID() assert.Equal(t, int64(654321), res) var nilStruct *ReplicationToken res = nilStruct.GetLastProcessedMessageID() assert.Equal(t, int64(0), res) } func TestReplicationTaskType_Ptr(t *testing.T) { taskType := ReplicationTaskTypeDomain ptr := taskType.Ptr() assert.Equal(t, &taskType, ptr) } func TestReplicationTaskType_String(t *testing.T) { var taskType ReplicationTaskType taskType = ReplicationTaskTypeDomain assert.Equal(t, "Domain", taskType.String()) taskType = ReplicationTaskTypeHistory assert.Equal(t, "History", taskType.String()) taskType = ReplicationTaskTypeSyncShardStatus assert.Equal(t, "SyncShardStatus", taskType.String()) taskType = ReplicationTaskTypeSyncActivity assert.Equal(t, "SyncActivity", taskType.String()) taskType = ReplicationTaskTypeHistoryMetadata assert.Equal(t, "HistoryMetadata", taskType.String()) taskType = ReplicationTaskTypeHistoryV2 assert.Equal(t, "HistoryV2", taskType.String()) taskType = ReplicationTaskTypeFailoverMarker assert.Equal(t, "FailoverMarker", taskType.String()) // Test unknown task type taskType = ReplicationTaskType(10) assert.Equal(t, "ReplicationTaskType(10)", taskType.String()) } func TestReplicationTaskType_UnmarshalText(t *testing.T) { var taskType ReplicationTaskType err := taskType.UnmarshalText([]byte("DOMAIN")) assert.NoError(t, err) assert.Equal(t, ReplicationTaskTypeDomain, taskType) err = taskType.UnmarshalText([]byte("HISTORY")) assert.NoError(t, err) assert.Equal(t, ReplicationTaskTypeHistory, taskType) err = taskType.UnmarshalText([]byte("SYNCSHARDSTATUS")) assert.NoError(t, err) assert.Equal(t, ReplicationTaskTypeSyncShardStatus, taskType) err = taskType.UnmarshalText([]byte("SYNCACTIVITY")) assert.NoError(t, err) assert.Equal(t, ReplicationTaskTypeSyncActivity, taskType) err = taskType.UnmarshalText([]byte("HISTORYMETADATA")) assert.NoError(t, err) assert.Equal(t, ReplicationTaskTypeHistoryMetadata, taskType) err = taskType.UnmarshalText([]byte("HISTORYV2")) assert.NoError(t, err) assert.Equal(t, ReplicationTaskTypeHistoryV2, taskType) err = taskType.UnmarshalText([]byte("FAILOVERMARKER")) assert.NoError(t, err) assert.Equal(t, ReplicationTaskTypeFailoverMarker, taskType) // Test unknown string value err = taskType.UnmarshalText([]byte("UNKNOWN")) assert.Error(t, err) // Test numeric value parsing err = taskType.UnmarshalText([]byte("10")) assert.NoError(t, err) assert.Equal(t, ReplicationTaskType(10), taskType) } func TestReplicationTaskType_MarshalText(t *testing.T) { var taskType ReplicationTaskType taskType = ReplicationTaskTypeDomain text, err := taskType.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("Domain"), text) taskType = ReplicationTaskTypeHistory text, err = taskType.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("History"), text) taskType = ReplicationTaskTypeSyncShardStatus text, err = taskType.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("SyncShardStatus"), text) taskType = ReplicationTaskTypeSyncActivity text, err = taskType.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("SyncActivity"), text) taskType = ReplicationTaskTypeHistoryMetadata text, err = taskType.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("HistoryMetadata"), text) taskType = ReplicationTaskTypeHistoryV2 text, err = taskType.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("HistoryV2"), text) taskType = ReplicationTaskTypeFailoverMarker text, err = taskType.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("FailoverMarker"), text) // Test unknown task type taskType = ReplicationTaskType(10) text, err = taskType.MarshalText() assert.NoError(t, err) assert.Equal(t, []byte("ReplicationTaskType(10)"), text) } func TestSyncActivityTaskAttributes_GetDomainID(t *testing.T) { testStruct := SyncActivityTaskAttributes{ DomainID: "domain-id", } res := testStruct.GetDomainID() assert.Equal(t, "domain-id", res) var nilStruct *SyncActivityTaskAttributes res = nilStruct.GetDomainID() assert.Equal(t, "", res) } func TestSyncActivityTaskAttributes_GetWorkflowID(t *testing.T) { testStruct := SyncActivityTaskAttributes{ WorkflowID: "workflow-id", } res := testStruct.GetWorkflowID() assert.Equal(t, "workflow-id", res) var nilStruct *SyncActivityTaskAttributes res = nilStruct.GetWorkflowID() assert.Equal(t, "", res) } func TestSyncActivityTaskAttributes_GetRunID(t *testing.T) { testStruct := SyncActivityTaskAttributes{ RunID: "run-id", } res := testStruct.GetRunID() assert.Equal(t, "run-id", res) var nilStruct *SyncActivityTaskAttributes res = nilStruct.GetRunID() assert.Equal(t, "", res) } func TestSyncActivityTaskAttributes_GetVersion(t *testing.T) { testStruct := SyncActivityTaskAttributes{ Version: 12345, } res := testStruct.GetVersion() assert.Equal(t, int64(12345), res) var nilStruct *SyncActivityTaskAttributes res = nilStruct.GetVersion() assert.Equal(t, int64(0), res) } func TestSyncActivityTaskAttributes_GetScheduledID(t *testing.T) { testStruct := SyncActivityTaskAttributes{ ScheduledID: 67890, } res := testStruct.GetScheduledID() assert.Equal(t, int64(67890), res) var nilStruct *SyncActivityTaskAttributes res = nilStruct.GetScheduledID() assert.Equal(t, int64(0), res) } func TestSyncActivityTaskAttributes_GetScheduledTime(t *testing.T) { scheduledTime := int64(1234567890) testStruct := SyncActivityTaskAttributes{ ScheduledTime: &scheduledTime, } res := testStruct.GetScheduledTime() assert.Equal(t, scheduledTime, res) var nilStruct *SyncActivityTaskAttributes res = nilStruct.GetScheduledTime() assert.Equal(t, int64(0), res) } func TestSyncActivityTaskAttributes_GetStartedID(t *testing.T) { testStruct := SyncActivityTaskAttributes{ StartedID: 11111, } res := testStruct.GetStartedID() assert.Equal(t, int64(11111), res) var nilStruct *SyncActivityTaskAttributes res = nilStruct.GetStartedID() assert.Equal(t, int64(0), res) } func TestSyncActivityTaskAttributes_GetStartedTime(t *testing.T) { startedTime := int64(1234567890) testStruct := SyncActivityTaskAttributes{ StartedTime: &startedTime, } res := testStruct.GetStartedTime() assert.Equal(t, startedTime, res) var nilStruct *SyncActivityTaskAttributes res = nilStruct.GetStartedTime() assert.Equal(t, int64(0), res) } func TestSyncActivityTaskAttributes_GetLastHeartbeatTime(t *testing.T) { lastHeartbeatTime := int64(9876543210) testStruct := SyncActivityTaskAttributes{ LastHeartbeatTime: &lastHeartbeatTime, } res := testStruct.GetLastHeartbeatTime() assert.Equal(t, lastHeartbeatTime, res) var nilStruct *SyncActivityTaskAttributes res = nilStruct.GetLastHeartbeatTime() assert.Equal(t, int64(0), res) } func TestSyncActivityTaskAttributes_GetVersionHistory(t *testing.T) { versionHistory := &VersionHistory{} testStruct := SyncActivityTaskAttributes{ VersionHistory: versionHistory, } res := testStruct.GetVersionHistory() assert.Equal(t, versionHistory, res) var nilStruct *SyncActivityTaskAttributes res = nilStruct.GetVersionHistory() assert.Nil(t, res) } func TestSyncShardStatus_GetTimestamp(t *testing.T) { timestamp := int64(9876543210) testStruct := SyncShardStatus{ Timestamp: ×tamp, } res := testStruct.GetTimestamp() assert.Equal(t, timestamp, res) var nilStruct *SyncShardStatus res = nilStruct.GetTimestamp() assert.Equal(t, int64(0), res) } func TestReplicationTask_ByteSize(t *testing.T) { AssertReachablesImplementByteSize(t, (*ReplicationTask)(nil)) AssertByteSizeMatchesReflect(t, &ReplicationTask{}) } ================================================ FILE: common/types/schedule.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package types import ( "fmt" "strconv" "strings" "time" ) // --- Enums --- // ScheduleOverlapPolicy defines behavior when a scheduled run overlaps with a previous run. type ScheduleOverlapPolicy int32 const ( ScheduleOverlapPolicyInvalid ScheduleOverlapPolicy = iota ScheduleOverlapPolicySkipNew // Skip if previous still running ScheduleOverlapPolicyBuffer // Buffer and execute sequentially ScheduleOverlapPolicyConcurrent // Allow concurrent execution ScheduleOverlapPolicyCancelPrevious // Cancel previous, start new ScheduleOverlapPolicyTerminatePrevious // Terminate previous, start new ) func (e ScheduleOverlapPolicy) Ptr() *ScheduleOverlapPolicy { return &e } func (e ScheduleOverlapPolicy) String() string { switch e { case ScheduleOverlapPolicyInvalid: return "INVALID" case ScheduleOverlapPolicySkipNew: return "SKIP_NEW" case ScheduleOverlapPolicyBuffer: return "BUFFER" case ScheduleOverlapPolicyConcurrent: return "CONCURRENT" case ScheduleOverlapPolicyCancelPrevious: return "CANCEL_PREVIOUS" case ScheduleOverlapPolicyTerminatePrevious: return "TERMINATE_PREVIOUS" } return fmt.Sprintf("ScheduleOverlapPolicy(%d)", int32(e)) } func (e *ScheduleOverlapPolicy) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "INVALID": *e = ScheduleOverlapPolicyInvalid case "SKIP_NEW": *e = ScheduleOverlapPolicySkipNew case "BUFFER": *e = ScheduleOverlapPolicyBuffer case "CONCURRENT": *e = ScheduleOverlapPolicyConcurrent case "CANCEL_PREVIOUS": *e = ScheduleOverlapPolicyCancelPrevious case "TERMINATE_PREVIOUS": *e = ScheduleOverlapPolicyTerminatePrevious default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ScheduleOverlapPolicy", err) } *e = ScheduleOverlapPolicy(val) } return nil } func (e ScheduleOverlapPolicy) MarshalText() ([]byte, error) { return []byte(e.String()), nil } // ScheduleCatchUpPolicy defines how missed runs are handled on unpause or system recovery. type ScheduleCatchUpPolicy int32 const ( ScheduleCatchUpPolicyInvalid ScheduleCatchUpPolicy = iota ScheduleCatchUpPolicySkip // Skip all missed runs ScheduleCatchUpPolicyOne // Run only the most recent missed time ScheduleCatchUpPolicyAll // Run for each missed (up to window) ) func (e ScheduleCatchUpPolicy) Ptr() *ScheduleCatchUpPolicy { return &e } func (e ScheduleCatchUpPolicy) String() string { switch e { case ScheduleCatchUpPolicyInvalid: return "INVALID" case ScheduleCatchUpPolicySkip: return "SKIP" case ScheduleCatchUpPolicyOne: return "ONE" case ScheduleCatchUpPolicyAll: return "ALL" } return fmt.Sprintf("ScheduleCatchUpPolicy(%d)", int32(e)) } func (e *ScheduleCatchUpPolicy) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "INVALID": *e = ScheduleCatchUpPolicyInvalid case "SKIP": *e = ScheduleCatchUpPolicySkip case "ONE": *e = ScheduleCatchUpPolicyOne case "ALL": *e = ScheduleCatchUpPolicyAll default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ScheduleCatchUpPolicy", err) } *e = ScheduleCatchUpPolicy(val) } return nil } func (e ScheduleCatchUpPolicy) MarshalText() ([]byte, error) { return []byte(e.String()), nil } // --- Core Types --- // ScheduleSpec defines when a schedule should trigger. type ScheduleSpec struct { CronExpression string `json:"cronExpression,omitempty"` StartTime time.Time `json:"startTime,omitempty"` EndTime time.Time `json:"endTime,omitempty"` Jitter time.Duration `json:"jitter,omitempty"` } func (v *ScheduleSpec) GetCronExpression() (o string) { if v != nil { return v.CronExpression } return } func (v *ScheduleSpec) GetStartTime() (o time.Time) { if v != nil { return v.StartTime } return } func (v *ScheduleSpec) GetEndTime() (o time.Time) { if v != nil { return v.EndTime } return } func (v *ScheduleSpec) GetJitter() (o time.Duration) { if v != nil { return v.Jitter } return } // StartWorkflowAction defines a workflow to start when the schedule triggers. type StartWorkflowAction struct { WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"-"` // Potential PII WorkflowIDPrefix string `json:"workflowIdPrefix,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` Memo *Memo `json:"-"` // Filtering PII SearchAttributes *SearchAttributes `json:"-"` // Filtering PII } func (v *StartWorkflowAction) GetWorkflowType() *WorkflowType { if v != nil { return v.WorkflowType } return nil } func (v *StartWorkflowAction) GetTaskList() *TaskList { if v != nil { return v.TaskList } return nil } func (v *StartWorkflowAction) GetWorkflowIDPrefix() (o string) { if v != nil { return v.WorkflowIDPrefix } return } func (v *StartWorkflowAction) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } func (v *StartWorkflowAction) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // ScheduleAction defines the action to take when the schedule triggers. // Exactly one action field must be set. type ScheduleAction struct { StartWorkflow *StartWorkflowAction `json:"startWorkflow,omitempty"` } func (v *ScheduleAction) GetStartWorkflow() *StartWorkflowAction { if v != nil { return v.StartWorkflow } return nil } // SchedulePolicies configures schedule behavior. type SchedulePolicies struct { OverlapPolicy ScheduleOverlapPolicy `json:"overlapPolicy,omitempty"` CatchUpPolicy ScheduleCatchUpPolicy `json:"catchUpPolicy,omitempty"` CatchUpWindow time.Duration `json:"catchUpWindow,omitempty"` PauseOnFailure bool `json:"pauseOnFailure,omitempty"` BufferLimit int32 `json:"bufferLimit,omitempty"` ConcurrencyLimit int32 `json:"concurrencyLimit,omitempty"` } func (v *SchedulePolicies) GetOverlapPolicy() (o ScheduleOverlapPolicy) { if v != nil { return v.OverlapPolicy } return } func (v *SchedulePolicies) GetCatchUpPolicy() (o ScheduleCatchUpPolicy) { if v != nil { return v.CatchUpPolicy } return } func (v *SchedulePolicies) GetCatchUpWindow() (o time.Duration) { if v != nil { return v.CatchUpWindow } return } func (v *SchedulePolicies) GetPauseOnFailure() (o bool) { if v != nil { return v.PauseOnFailure } return } func (v *SchedulePolicies) GetBufferLimit() (o int32) { if v != nil { return v.BufferLimit } return } func (v *SchedulePolicies) GetConcurrencyLimit() (o int32) { if v != nil { return v.ConcurrencyLimit } return } // SchedulePauseInfo captures the state of a paused schedule (response-only, server-populated). type SchedulePauseInfo struct { Reason string `json:"reason,omitempty"` PausedAt time.Time `json:"pausedAt,omitempty"` PausedBy string `json:"pausedBy,omitempty"` } func (v *SchedulePauseInfo) GetReason() (o string) { if v != nil { return v.Reason } return } func (v *SchedulePauseInfo) GetPausedAt() (o time.Time) { if v != nil { return v.PausedAt } return } func (v *SchedulePauseInfo) GetPausedBy() (o string) { if v != nil { return v.PausedBy } return } // ScheduleState represents the current runtime state of a schedule. type ScheduleState struct { Paused bool `json:"paused,omitempty"` PauseInfo *SchedulePauseInfo `json:"pauseInfo,omitempty"` } func (v *ScheduleState) GetPaused() (o bool) { if v != nil { return v.Paused } return } func (v *ScheduleState) GetPauseInfo() *SchedulePauseInfo { if v != nil { return v.PauseInfo } return nil } // BackfillInfo tracks the progress of an ongoing backfill operation. type BackfillInfo struct { BackfillID string `json:"backfillId,omitempty"` StartTime time.Time `json:"startTime,omitempty"` EndTime time.Time `json:"endTime,omitempty"` RunsCompleted int32 `json:"runsCompleted,omitempty"` RunsTotal int32 `json:"runsTotal,omitempty"` } func (v *BackfillInfo) GetStartTime() (o time.Time) { if v != nil { return v.StartTime } return } func (v *BackfillInfo) GetEndTime() (o time.Time) { if v != nil { return v.EndTime } return } func (v *BackfillInfo) GetBackfillID() (o string) { if v != nil { return v.BackfillID } return } func (v *BackfillInfo) GetRunsCompleted() (o int32) { if v != nil { return v.RunsCompleted } return } func (v *BackfillInfo) GetRunsTotal() (o int32) { if v != nil { return v.RunsTotal } return } // ScheduleInfo provides runtime information about the schedule. type ScheduleInfo struct { LastRunTime time.Time `json:"lastRunTime,omitempty"` NextRunTime time.Time `json:"nextRunTime,omitempty"` TotalRuns int64 `json:"totalRuns,omitempty"` CreateTime time.Time `json:"createTime,omitempty"` LastUpdateTime time.Time `json:"lastUpdateTime,omitempty"` OngoingBackfills []*BackfillInfo `json:"ongoingBackfills,omitempty"` } func (v *ScheduleInfo) GetLastRunTime() (o time.Time) { if v != nil { return v.LastRunTime } return } func (v *ScheduleInfo) GetNextRunTime() (o time.Time) { if v != nil { return v.NextRunTime } return } func (v *ScheduleInfo) GetTotalRuns() (o int64) { if v != nil { return v.TotalRuns } return } func (v *ScheduleInfo) GetCreateTime() (o time.Time) { if v != nil { return v.CreateTime } return } func (v *ScheduleInfo) GetLastUpdateTime() (o time.Time) { if v != nil { return v.LastUpdateTime } return } func (v *ScheduleInfo) GetOngoingBackfills() (o []*BackfillInfo) { if v != nil { return v.OngoingBackfills } return } func (v *StartWorkflowAction) GetInput() (o []byte) { if v != nil { return v.Input } return } func (v *StartWorkflowAction) GetRetryPolicy() *RetryPolicy { if v != nil { return v.RetryPolicy } return nil } func (v *StartWorkflowAction) GetMemo() *Memo { if v != nil { return v.Memo } return nil } func (v *StartWorkflowAction) GetSearchAttributes() *SearchAttributes { if v != nil { return v.SearchAttributes } return nil } ================================================ FILE: common/types/schedule_service.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package types import "time" // --- Request/Response Types for Schedule APIs --- // ScheduleListEntry represents a single schedule in a list response. type ScheduleListEntry struct { ScheduleID string `json:"scheduleId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` State *ScheduleState `json:"state,omitempty"` CronExpression string `json:"cronExpression,omitempty"` } func (v *ScheduleListEntry) GetScheduleID() (o string) { if v != nil { return v.ScheduleID } return } func (v *ScheduleListEntry) GetWorkflowType() *WorkflowType { if v != nil { return v.WorkflowType } return nil } func (v *ScheduleListEntry) GetState() *ScheduleState { if v != nil { return v.State } return nil } func (v *ScheduleListEntry) GetCronExpression() (o string) { if v != nil { return v.CronExpression } return } // CreateScheduleRequest is the request to create a new schedule. type CreateScheduleRequest struct { Domain string `json:"domain,omitempty"` ScheduleID string `json:"scheduleId,omitempty"` Spec *ScheduleSpec `json:"spec,omitempty"` Action *ScheduleAction `json:"action,omitempty"` Policies *SchedulePolicies `json:"policies,omitempty"` Memo *Memo `json:"-"` // Filtering PII SearchAttributes *SearchAttributes `json:"-"` // Filtering PII } func (v *CreateScheduleRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } func (v *CreateScheduleRequest) GetScheduleID() (o string) { if v != nil { return v.ScheduleID } return } func (v *CreateScheduleRequest) GetSpec() *ScheduleSpec { if v != nil { return v.Spec } return nil } func (v *CreateScheduleRequest) GetAction() *ScheduleAction { if v != nil { return v.Action } return nil } func (v *CreateScheduleRequest) GetPolicies() *SchedulePolicies { if v != nil { return v.Policies } return nil } func (v *CreateScheduleRequest) GetMemo() *Memo { if v != nil { return v.Memo } return nil } func (v *CreateScheduleRequest) GetSearchAttributes() *SearchAttributes { if v != nil { return v.SearchAttributes } return nil } // CreateScheduleResponse is the response for creating a schedule. type CreateScheduleResponse struct{} // DescribeScheduleRequest is the request to describe a schedule. type DescribeScheduleRequest struct { Domain string `json:"domain,omitempty"` ScheduleID string `json:"scheduleId,omitempty"` } func (v *DescribeScheduleRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } func (v *DescribeScheduleRequest) GetScheduleID() (o string) { if v != nil { return v.ScheduleID } return } // DescribeScheduleResponse is the response for describing a schedule. type DescribeScheduleResponse struct { Spec *ScheduleSpec `json:"spec,omitempty"` Action *ScheduleAction `json:"action,omitempty"` Policies *SchedulePolicies `json:"policies,omitempty"` State *ScheduleState `json:"state,omitempty"` Info *ScheduleInfo `json:"info,omitempty"` Memo *Memo `json:"-"` // Filtering PII SearchAttributes *SearchAttributes `json:"-"` // Filtering PII } func (v *DescribeScheduleResponse) GetSpec() *ScheduleSpec { if v != nil { return v.Spec } return nil } func (v *DescribeScheduleResponse) GetAction() *ScheduleAction { if v != nil { return v.Action } return nil } func (v *DescribeScheduleResponse) GetPolicies() *SchedulePolicies { if v != nil { return v.Policies } return nil } func (v *DescribeScheduleResponse) GetState() *ScheduleState { if v != nil { return v.State } return nil } func (v *DescribeScheduleResponse) GetInfo() *ScheduleInfo { if v != nil { return v.Info } return nil } func (v *DescribeScheduleResponse) GetMemo() *Memo { if v != nil { return v.Memo } return nil } func (v *DescribeScheduleResponse) GetSearchAttributes() *SearchAttributes { if v != nil { return v.SearchAttributes } return nil } // UpdateScheduleRequest is the request to update a schedule. type UpdateScheduleRequest struct { Domain string `json:"domain,omitempty"` ScheduleID string `json:"scheduleId,omitempty"` Spec *ScheduleSpec `json:"spec,omitempty"` Action *ScheduleAction `json:"action,omitempty"` Policies *SchedulePolicies `json:"policies,omitempty"` SearchAttributes *SearchAttributes `json:"-"` // Filtering PII } func (v *UpdateScheduleRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } func (v *UpdateScheduleRequest) GetScheduleID() (o string) { if v != nil { return v.ScheduleID } return } func (v *UpdateScheduleRequest) GetSpec() *ScheduleSpec { if v != nil { return v.Spec } return nil } func (v *UpdateScheduleRequest) GetAction() *ScheduleAction { if v != nil { return v.Action } return nil } func (v *UpdateScheduleRequest) GetPolicies() *SchedulePolicies { if v != nil { return v.Policies } return nil } func (v *UpdateScheduleRequest) GetSearchAttributes() *SearchAttributes { if v != nil { return v.SearchAttributes } return nil } // UpdateScheduleResponse is the response for updating a schedule. type UpdateScheduleResponse struct{} // DeleteScheduleRequest is the request to delete a schedule. type DeleteScheduleRequest struct { Domain string `json:"domain,omitempty"` ScheduleID string `json:"scheduleId,omitempty"` } func (v *DeleteScheduleRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } func (v *DeleteScheduleRequest) GetScheduleID() (o string) { if v != nil { return v.ScheduleID } return } // DeleteScheduleResponse is the response for deleting a schedule. type DeleteScheduleResponse struct{} // PauseScheduleRequest is the request to pause a schedule. type PauseScheduleRequest struct { Domain string `json:"domain,omitempty"` ScheduleID string `json:"scheduleId,omitempty"` Reason string `json:"reason,omitempty"` } func (v *PauseScheduleRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } func (v *PauseScheduleRequest) GetScheduleID() (o string) { if v != nil { return v.ScheduleID } return } func (v *PauseScheduleRequest) GetReason() (o string) { if v != nil { return v.Reason } return } // PauseScheduleResponse is the response for pausing a schedule. type PauseScheduleResponse struct{} // UnpauseScheduleRequest is the request to resume a paused schedule. type UnpauseScheduleRequest struct { Domain string `json:"domain,omitempty"` ScheduleID string `json:"scheduleId,omitempty"` Reason string `json:"reason,omitempty"` CatchUpPolicy ScheduleCatchUpPolicy `json:"catchUpPolicy,omitempty"` } func (v *UnpauseScheduleRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } func (v *UnpauseScheduleRequest) GetScheduleID() (o string) { if v != nil { return v.ScheduleID } return } func (v *UnpauseScheduleRequest) GetReason() (o string) { if v != nil { return v.Reason } return } func (v *UnpauseScheduleRequest) GetCatchUpPolicy() (o ScheduleCatchUpPolicy) { if v != nil { return v.CatchUpPolicy } return } // UnpauseScheduleResponse is the response for resuming a schedule. type UnpauseScheduleResponse struct{} // ListSchedulesRequest is the request to list schedules in a domain. type ListSchedulesRequest struct { Domain string `json:"domain,omitempty"` PageSize int32 `json:"pageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } func (v *ListSchedulesRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } func (v *ListSchedulesRequest) GetPageSize() (o int32) { if v != nil { return v.PageSize } return } func (v *ListSchedulesRequest) GetNextPageToken() (o []byte) { if v != nil { return v.NextPageToken } return } // ListSchedulesResponse is the response for listing schedules. type ListSchedulesResponse struct { Schedules []*ScheduleListEntry `json:"schedules,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } func (v *ListSchedulesResponse) GetSchedules() (o []*ScheduleListEntry) { if v != nil { return v.Schedules } return } func (v *ListSchedulesResponse) GetNextPageToken() (o []byte) { if v != nil { return v.NextPageToken } return } // BackfillScheduleRequest is the request to trigger a backfill for a time range. type BackfillScheduleRequest struct { Domain string `json:"domain,omitempty"` ScheduleID string `json:"scheduleId,omitempty"` StartTime time.Time `json:"startTime,omitempty"` EndTime time.Time `json:"endTime,omitempty"` OverlapPolicy ScheduleOverlapPolicy `json:"overlapPolicy,omitempty"` BackfillID string `json:"backfillId,omitempty"` } func (v *BackfillScheduleRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } func (v *BackfillScheduleRequest) GetScheduleID() (o string) { if v != nil { return v.ScheduleID } return } func (v *BackfillScheduleRequest) GetStartTime() (o time.Time) { if v != nil { return v.StartTime } return } func (v *BackfillScheduleRequest) GetEndTime() (o time.Time) { if v != nil { return v.EndTime } return } func (v *BackfillScheduleRequest) GetOverlapPolicy() (o ScheduleOverlapPolicy) { if v != nil { return v.OverlapPolicy } return } func (v *BackfillScheduleRequest) GetBackfillID() (o string) { if v != nil { return v.BackfillID } return } // BackfillScheduleResponse is the response for triggering a backfill. type BackfillScheduleResponse struct{} ================================================ FILE: common/types/schedule_service_test.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package types import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestScheduleListEntry_NilGetters(t *testing.T) { var v *ScheduleListEntry assert.Equal(t, "", v.GetScheduleID()) assert.Nil(t, v.GetWorkflowType()) assert.Nil(t, v.GetState()) assert.Equal(t, "", v.GetCronExpression()) } func TestScheduleListEntry_Getters(t *testing.T) { wt := &WorkflowType{Name: "test-wf"} st := &ScheduleState{Paused: true} v := &ScheduleListEntry{ ScheduleID: "sched-1", WorkflowType: wt, State: st, CronExpression: "*/5 * * * *", } assert.Equal(t, "sched-1", v.GetScheduleID()) assert.Equal(t, wt, v.GetWorkflowType()) assert.Equal(t, st, v.GetState()) assert.Equal(t, "*/5 * * * *", v.GetCronExpression()) } func TestCreateScheduleRequest_NilGetters(t *testing.T) { var v *CreateScheduleRequest assert.Equal(t, "", v.GetDomain()) assert.Equal(t, "", v.GetScheduleID()) assert.Nil(t, v.GetSpec()) assert.Nil(t, v.GetAction()) assert.Nil(t, v.GetPolicies()) assert.Nil(t, v.GetMemo()) assert.Nil(t, v.GetSearchAttributes()) } func TestDescribeScheduleRequest_NilGetters(t *testing.T) { var v *DescribeScheduleRequest assert.Equal(t, "", v.GetDomain()) assert.Equal(t, "", v.GetScheduleID()) } func TestDescribeScheduleResponse_NilGetters(t *testing.T) { var v *DescribeScheduleResponse assert.Nil(t, v.GetSpec()) assert.Nil(t, v.GetAction()) assert.Nil(t, v.GetPolicies()) assert.Nil(t, v.GetState()) assert.Nil(t, v.GetInfo()) assert.Nil(t, v.GetMemo()) assert.Nil(t, v.GetSearchAttributes()) } func TestUpdateScheduleRequest_NilGetters(t *testing.T) { var v *UpdateScheduleRequest assert.Equal(t, "", v.GetDomain()) assert.Equal(t, "", v.GetScheduleID()) assert.Nil(t, v.GetSpec()) assert.Nil(t, v.GetAction()) assert.Nil(t, v.GetPolicies()) assert.Nil(t, v.GetSearchAttributes()) } func TestDeleteScheduleRequest_NilGetters(t *testing.T) { var v *DeleteScheduleRequest assert.Equal(t, "", v.GetDomain()) assert.Equal(t, "", v.GetScheduleID()) } func TestPauseScheduleRequest_NilGetters(t *testing.T) { var v *PauseScheduleRequest assert.Equal(t, "", v.GetDomain()) assert.Equal(t, "", v.GetScheduleID()) assert.Equal(t, "", v.GetReason()) } func TestUnpauseScheduleRequest_NilGetters(t *testing.T) { var v *UnpauseScheduleRequest assert.Equal(t, "", v.GetDomain()) assert.Equal(t, "", v.GetScheduleID()) assert.Equal(t, "", v.GetReason()) assert.Equal(t, ScheduleCatchUpPolicyInvalid, v.GetCatchUpPolicy()) } func TestUnpauseScheduleRequest_Getters(t *testing.T) { v := &UnpauseScheduleRequest{ Domain: "test-domain", ScheduleID: "sched-1", Reason: "resuming", CatchUpPolicy: ScheduleCatchUpPolicyAll, } assert.Equal(t, "test-domain", v.GetDomain()) assert.Equal(t, "sched-1", v.GetScheduleID()) assert.Equal(t, "resuming", v.GetReason()) assert.Equal(t, ScheduleCatchUpPolicyAll, v.GetCatchUpPolicy()) } func TestListSchedulesRequest_NilGetters(t *testing.T) { var v *ListSchedulesRequest assert.Equal(t, "", v.GetDomain()) assert.Equal(t, int32(0), v.GetPageSize()) assert.Nil(t, v.GetNextPageToken()) } func TestListSchedulesResponse_NilGetters(t *testing.T) { var v *ListSchedulesResponse assert.Nil(t, v.GetSchedules()) assert.Nil(t, v.GetNextPageToken()) } func TestBackfillScheduleRequest_NilGetters(t *testing.T) { var v *BackfillScheduleRequest assert.Equal(t, "", v.GetDomain()) assert.Equal(t, "", v.GetScheduleID()) assert.Equal(t, time.Time{}, v.GetStartTime()) assert.Equal(t, time.Time{}, v.GetEndTime()) assert.Equal(t, ScheduleOverlapPolicyInvalid, v.GetOverlapPolicy()) assert.Equal(t, "", v.GetBackfillID()) } func TestBackfillScheduleRequest_Getters(t *testing.T) { now := time.Now().Truncate(time.Second) v := &BackfillScheduleRequest{ Domain: "test-domain", ScheduleID: "sched-1", StartTime: now, EndTime: now.Add(time.Hour), OverlapPolicy: ScheduleOverlapPolicyBuffer, BackfillID: "bf-1", } assert.Equal(t, "test-domain", v.GetDomain()) assert.Equal(t, "sched-1", v.GetScheduleID()) assert.Equal(t, now, v.GetStartTime()) assert.Equal(t, now.Add(time.Hour), v.GetEndTime()) assert.Equal(t, ScheduleOverlapPolicyBuffer, v.GetOverlapPolicy()) assert.Equal(t, "bf-1", v.GetBackfillID()) } ================================================ FILE: common/types/schedule_test.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package types import ( "testing" "github.com/stretchr/testify/assert" ) func TestScheduleOverlapPolicy_UnmarshalText(t *testing.T) { tests := []struct { name string text string want ScheduleOverlapPolicy err bool }{ {name: "invalid", text: "INVALID", want: ScheduleOverlapPolicyInvalid}, {name: "skip_new", text: "SKIP_NEW", want: ScheduleOverlapPolicySkipNew}, {name: "buffer", text: "BUFFER", want: ScheduleOverlapPolicyBuffer}, {name: "concurrent", text: "CONCURRENT", want: ScheduleOverlapPolicyConcurrent}, {name: "cancel_previous", text: "CANCEL_PREVIOUS", want: ScheduleOverlapPolicyCancelPrevious}, {name: "terminate_previous", text: "TERMINATE_PREVIOUS", want: ScheduleOverlapPolicyTerminatePrevious}, {name: "lowercase", text: "skip_new", want: ScheduleOverlapPolicySkipNew}, {name: "numeric", text: "2", want: ScheduleOverlapPolicy(2)}, {name: "unknown", text: "UNKNOWN", err: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var got ScheduleOverlapPolicy err := got.UnmarshalText([]byte(tt.text)) if tt.err { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.want, got) } }) } } func TestScheduleOverlapPolicy_MarshalText(t *testing.T) { tests := []struct { name string val ScheduleOverlapPolicy want string }{ {name: "invalid", val: ScheduleOverlapPolicyInvalid, want: "INVALID"}, {name: "skip_new", val: ScheduleOverlapPolicySkipNew, want: "SKIP_NEW"}, {name: "buffer", val: ScheduleOverlapPolicyBuffer, want: "BUFFER"}, {name: "concurrent", val: ScheduleOverlapPolicyConcurrent, want: "CONCURRENT"}, {name: "cancel_previous", val: ScheduleOverlapPolicyCancelPrevious, want: "CANCEL_PREVIOUS"}, {name: "terminate_previous", val: ScheduleOverlapPolicyTerminatePrevious, want: "TERMINATE_PREVIOUS"}, {name: "unknown_numeric", val: ScheduleOverlapPolicy(99), want: "ScheduleOverlapPolicy(99)"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b, err := tt.val.MarshalText() assert.NoError(t, err) assert.Equal(t, tt.want, string(b)) }) } } func TestScheduleOverlapPolicy_RoundTrip(t *testing.T) { for _, val := range []ScheduleOverlapPolicy{ ScheduleOverlapPolicyInvalid, ScheduleOverlapPolicySkipNew, ScheduleOverlapPolicyBuffer, ScheduleOverlapPolicyConcurrent, ScheduleOverlapPolicyCancelPrevious, ScheduleOverlapPolicyTerminatePrevious, } { b, err := val.MarshalText() assert.NoError(t, err) var got ScheduleOverlapPolicy err = got.UnmarshalText(b) assert.NoError(t, err) assert.Equal(t, val, got) } } func TestScheduleCatchUpPolicy_UnmarshalText(t *testing.T) { tests := []struct { name string text string want ScheduleCatchUpPolicy err bool }{ {name: "invalid", text: "INVALID", want: ScheduleCatchUpPolicyInvalid}, {name: "skip", text: "SKIP", want: ScheduleCatchUpPolicySkip}, {name: "one", text: "ONE", want: ScheduleCatchUpPolicyOne}, {name: "all", text: "ALL", want: ScheduleCatchUpPolicyAll}, {name: "lowercase", text: "all", want: ScheduleCatchUpPolicyAll}, {name: "numeric", text: "1", want: ScheduleCatchUpPolicy(1)}, {name: "unknown", text: "UNKNOWN", err: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var got ScheduleCatchUpPolicy err := got.UnmarshalText([]byte(tt.text)) if tt.err { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.want, got) } }) } } func TestScheduleCatchUpPolicy_MarshalText(t *testing.T) { tests := []struct { name string val ScheduleCatchUpPolicy want string }{ {name: "invalid", val: ScheduleCatchUpPolicyInvalid, want: "INVALID"}, {name: "skip", val: ScheduleCatchUpPolicySkip, want: "SKIP"}, {name: "one", val: ScheduleCatchUpPolicyOne, want: "ONE"}, {name: "all", val: ScheduleCatchUpPolicyAll, want: "ALL"}, {name: "unknown_numeric", val: ScheduleCatchUpPolicy(99), want: "ScheduleCatchUpPolicy(99)"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b, err := tt.val.MarshalText() assert.NoError(t, err) assert.Equal(t, tt.want, string(b)) }) } } func TestScheduleCatchUpPolicy_RoundTrip(t *testing.T) { for _, val := range []ScheduleCatchUpPolicy{ ScheduleCatchUpPolicyInvalid, ScheduleCatchUpPolicySkip, ScheduleCatchUpPolicyOne, ScheduleCatchUpPolicyAll, } { b, err := val.MarshalText() assert.NoError(t, err) var got ScheduleCatchUpPolicy err = got.UnmarshalText(b) assert.NoError(t, err) assert.Equal(t, val, got) } } func TestScheduleOverlapPolicy_Ptr(t *testing.T) { val := ScheduleOverlapPolicyBuffer ptr := val.Ptr() assert.Equal(t, &val, ptr) } func TestScheduleCatchUpPolicy_Ptr(t *testing.T) { val := ScheduleCatchUpPolicyAll ptr := val.Ptr() assert.Equal(t, &val, ptr) } ================================================ FILE: common/types/sharddistributor.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "fmt" ) //go:generate enumer -type=ExecutorStatus,ShardStatus,AssignmentStatus,MigrationMode,HandoverType,LoadBalancingMode -json -output sharddistributor_statuses_enumer_generated.go type GetShardOwnerRequest struct { ShardKey string Namespace string } func (v *GetShardOwnerRequest) GetShardKey() (o string) { if v != nil { return v.ShardKey } return } func (v *GetShardOwnerRequest) GetNamespace() (o string) { if v != nil { return v.Namespace } return } type GetShardOwnerResponse struct { Owner string Namespace string Metadata map[string]string } func (v *GetShardOwnerResponse) GetOwner() (o string) { if v != nil { return v.Owner } return } func (v *GetShardOwnerResponse) GetNamespace() (o string) { if v != nil { return v.Namespace } return } func (v *GetShardOwnerResponse) GetMetadata() (o map[string]string) { if v != nil { return v.Metadata } return } type NamespaceNotFoundError struct { Namespace string } func (n *NamespaceNotFoundError) Error() (o string) { if n != nil { return fmt.Sprintf("namespace not found %v", n.Namespace) } return } type ShardNotFoundError struct { Namespace string ShardKey string } func (n *ShardNotFoundError) Error() (o string) { if n != nil { return fmt.Sprintf("shard not found %v:%v", n.Namespace, n.ShardKey) } return } type ExecutorHeartbeatRequest struct { Namespace string ExecutorID string Status ExecutorStatus ShardStatusReports map[string]*ShardStatusReport Metadata map[string]string } func (v *ExecutorHeartbeatRequest) GetNamespace() (o string) { if v != nil { return v.Namespace } return } func (v *ExecutorHeartbeatRequest) GetExecutorID() (o string) { if v != nil { return v.ExecutorID } return } func (v *ExecutorHeartbeatRequest) GetStatus() (o ExecutorStatus) { if v != nil { return v.Status } return } func (v *ExecutorHeartbeatRequest) GetShardStatusReports() (o map[string]*ShardStatusReport) { if v != nil { return v.ShardStatusReports } return } func (v *ExecutorHeartbeatRequest) GetMetadata() (o map[string]string) { if v != nil { return v.Metadata } return } // ExecutorStatus is persisted to the DB with a string value mapping. // Beware - if we want to change the name - it should be backward compatible and should be done in two steps. type ExecutorStatus int32 const ( ExecutorStatusINVALID ExecutorStatus = 0 ExecutorStatusACTIVE ExecutorStatus = 1 ExecutorStatusDRAINING ExecutorStatus = 2 ExecutorStatusDRAINED ExecutorStatus = 3 ) type ShardStatusReport struct { Status ShardStatus ShardLoad float64 } func (v *ShardStatusReport) GetStatus() (o ShardStatus) { if v != nil { return v.Status } return } func (v *ShardStatusReport) GetShardLoad() (o float64) { if v != nil { return v.ShardLoad } return } // ShardStatus is persisted to the DB with a string value mapping. // Beware - if we want to change the name - it should be backward compatible and should be done in two steps. type ShardStatus int32 const ( ShardStatusINVALID ShardStatus = 0 ShardStatusREADY ShardStatus = 1 ShardStatusDONE ShardStatus = 2 ) type ExecutorHeartbeatResponse struct { ShardAssignments map[string]*ShardAssignment MigrationMode MigrationMode } func (v *ExecutorHeartbeatResponse) GetShardAssignments() (o map[string]*ShardAssignment) { if v != nil { return v.ShardAssignments } return } func (v *ExecutorHeartbeatResponse) GetMigrationMode() (o MigrationMode) { if v != nil { return v.MigrationMode } return } type ShardAssignment struct { // Status indicates the current assignment status of the shard. Status AssignmentStatus `json:"status"` } func (v *ShardAssignment) GetStatus() (o AssignmentStatus) { if v != nil { return v.Status } return } // AssignmentStatus is persisted to the DB with a string value mapping. // Beware - if we want to change the name - it should be backward compatible and should be done in two steps. type AssignmentStatus int32 const ( AssignmentStatusINVALID AssignmentStatus = 0 AssignmentStatusREADY AssignmentStatus = 1 ) // HandoverType is used to indicate the type of handover that occurred during shard reassignment. // Type is persisted to the DB with a string value mapping. // Beware - if we want to change the name - it should be backward compatible and should be done in two steps. type HandoverType int32 const ( HandoverTypeINVALID HandoverType = 0 // HandoverTypeGRACEFUL // Graceful handover indicates that the shard was transferred in a way that allowed // the previous owner had a chance to finish processing before the shard was reassigned. HandoverTypeGRACEFUL HandoverType = 1 // HandoverTypeEMERGENCY // Emergency handover indicates that the shard was transferred abruptly without // allowing the previous owner to finish processing. HandoverTypeEMERGENCY HandoverType = 2 ) // Ptr returns a pointer to the HandoverType value. func (t HandoverType) Ptr() *HandoverType { return &t } type MigrationMode int32 const ( MigrationModeINVALID MigrationMode = 0 MigrationModeLOCALPASSTHROUGH MigrationMode = 1 MigrationModeLOCALPASSTHROUGHSHADOW MigrationMode = 2 MigrationModeDISTRIBUTEDPASSTHROUGH MigrationMode = 3 MigrationModeONBOARDED MigrationMode = 4 ) type LoadBalancingMode int32 const ( LoadBalancingModeINVALID LoadBalancingMode = 0 LoadBalancingModeNAIVE LoadBalancingMode = 1 LoadBalancingModeGREEDY LoadBalancingMode = 2 ) type WatchNamespaceStateRequest struct { Namespace string } func (v *WatchNamespaceStateRequest) GetNamespace() (o string) { if v != nil { return v.Namespace } return } type WatchNamespaceStateResponse struct { Executors []*ExecutorShardAssignment } func (v *WatchNamespaceStateResponse) GetExecutors() (o []*ExecutorShardAssignment) { if v != nil { return v.Executors } return } type ExecutorShardAssignment struct { ExecutorID string AssignedShards []*Shard Metadata map[string]string } func (v *ExecutorShardAssignment) GetExecutorID() (o string) { if v != nil { return v.ExecutorID } return } func (v *ExecutorShardAssignment) GetAssignedShards() (o []*Shard) { if v != nil { return v.AssignedShards } return } func (v *ExecutorShardAssignment) GetMetadata() (o map[string]string) { if v != nil { return v.Metadata } return } type Shard struct { ShardKey string } func (v *Shard) GetShardKey() (o string) { if v != nil { return v.ShardKey } return } ================================================ FILE: common/types/sharddistributor_statuses_enumer_generated.go ================================================ // Code generated by "enumer -type=ExecutorStatus,ShardStatus,AssignmentStatus,MigrationMode,HandoverType,LoadBalancingMode -json -output sharddistributor_statuses_enumer_generated.go"; DO NOT EDIT. package types import ( "encoding/json" "fmt" "strings" ) const _ExecutorStatusName = "ExecutorStatusINVALIDExecutorStatusACTIVEExecutorStatusDRAININGExecutorStatusDRAINED" var _ExecutorStatusIndex = [...]uint8{0, 21, 41, 63, 84} const _ExecutorStatusLowerName = "executorstatusinvalidexecutorstatusactiveexecutorstatusdrainingexecutorstatusdrained" func (i ExecutorStatus) String() string { if i < 0 || i >= ExecutorStatus(len(_ExecutorStatusIndex)-1) { return fmt.Sprintf("ExecutorStatus(%d)", i) } return _ExecutorStatusName[_ExecutorStatusIndex[i]:_ExecutorStatusIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ExecutorStatusNoOp() { var x [1]struct{} _ = x[ExecutorStatusINVALID-(0)] _ = x[ExecutorStatusACTIVE-(1)] _ = x[ExecutorStatusDRAINING-(2)] _ = x[ExecutorStatusDRAINED-(3)] } var _ExecutorStatusValues = []ExecutorStatus{ExecutorStatusINVALID, ExecutorStatusACTIVE, ExecutorStatusDRAINING, ExecutorStatusDRAINED} var _ExecutorStatusNameToValueMap = map[string]ExecutorStatus{ _ExecutorStatusName[0:21]: ExecutorStatusINVALID, _ExecutorStatusLowerName[0:21]: ExecutorStatusINVALID, _ExecutorStatusName[21:41]: ExecutorStatusACTIVE, _ExecutorStatusLowerName[21:41]: ExecutorStatusACTIVE, _ExecutorStatusName[41:63]: ExecutorStatusDRAINING, _ExecutorStatusLowerName[41:63]: ExecutorStatusDRAINING, _ExecutorStatusName[63:84]: ExecutorStatusDRAINED, _ExecutorStatusLowerName[63:84]: ExecutorStatusDRAINED, } var _ExecutorStatusNames = []string{ _ExecutorStatusName[0:21], _ExecutorStatusName[21:41], _ExecutorStatusName[41:63], _ExecutorStatusName[63:84], } // ExecutorStatusString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ExecutorStatusString(s string) (ExecutorStatus, error) { if val, ok := _ExecutorStatusNameToValueMap[s]; ok { return val, nil } if val, ok := _ExecutorStatusNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ExecutorStatus values", s) } // ExecutorStatusValues returns all values of the enum func ExecutorStatusValues() []ExecutorStatus { return _ExecutorStatusValues } // ExecutorStatusStrings returns a slice of all String values of the enum func ExecutorStatusStrings() []string { strs := make([]string, len(_ExecutorStatusNames)) copy(strs, _ExecutorStatusNames) return strs } // IsAExecutorStatus returns "true" if the value is listed in the enum definition. "false" otherwise func (i ExecutorStatus) IsAExecutorStatus() bool { for _, v := range _ExecutorStatusValues { if i == v { return true } } return false } // MarshalJSON implements the json.Marshaler interface for ExecutorStatus func (i ExecutorStatus) MarshalJSON() ([]byte, error) { return json.Marshal(i.String()) } // UnmarshalJSON implements the json.Unmarshaler interface for ExecutorStatus func (i *ExecutorStatus) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return fmt.Errorf("ExecutorStatus should be a string, got %s", data) } var err error *i, err = ExecutorStatusString(s) return err } const _ShardStatusName = "ShardStatusINVALIDShardStatusREADYShardStatusDONE" var _ShardStatusIndex = [...]uint8{0, 18, 34, 49} const _ShardStatusLowerName = "shardstatusinvalidshardstatusreadyshardstatusdone" func (i ShardStatus) String() string { if i < 0 || i >= ShardStatus(len(_ShardStatusIndex)-1) { return fmt.Sprintf("ShardStatus(%d)", i) } return _ShardStatusName[_ShardStatusIndex[i]:_ShardStatusIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ShardStatusNoOp() { var x [1]struct{} _ = x[ShardStatusINVALID-(0)] _ = x[ShardStatusREADY-(1)] _ = x[ShardStatusDONE-(2)] } var _ShardStatusValues = []ShardStatus{ShardStatusINVALID, ShardStatusREADY, ShardStatusDONE} var _ShardStatusNameToValueMap = map[string]ShardStatus{ _ShardStatusName[0:18]: ShardStatusINVALID, _ShardStatusLowerName[0:18]: ShardStatusINVALID, _ShardStatusName[18:34]: ShardStatusREADY, _ShardStatusLowerName[18:34]: ShardStatusREADY, _ShardStatusName[34:49]: ShardStatusDONE, _ShardStatusLowerName[34:49]: ShardStatusDONE, } var _ShardStatusNames = []string{ _ShardStatusName[0:18], _ShardStatusName[18:34], _ShardStatusName[34:49], } // ShardStatusString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ShardStatusString(s string) (ShardStatus, error) { if val, ok := _ShardStatusNameToValueMap[s]; ok { return val, nil } if val, ok := _ShardStatusNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ShardStatus values", s) } // ShardStatusValues returns all values of the enum func ShardStatusValues() []ShardStatus { return _ShardStatusValues } // ShardStatusStrings returns a slice of all String values of the enum func ShardStatusStrings() []string { strs := make([]string, len(_ShardStatusNames)) copy(strs, _ShardStatusNames) return strs } // IsAShardStatus returns "true" if the value is listed in the enum definition. "false" otherwise func (i ShardStatus) IsAShardStatus() bool { for _, v := range _ShardStatusValues { if i == v { return true } } return false } // MarshalJSON implements the json.Marshaler interface for ShardStatus func (i ShardStatus) MarshalJSON() ([]byte, error) { return json.Marshal(i.String()) } // UnmarshalJSON implements the json.Unmarshaler interface for ShardStatus func (i *ShardStatus) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return fmt.Errorf("ShardStatus should be a string, got %s", data) } var err error *i, err = ShardStatusString(s) return err } const _AssignmentStatusName = "AssignmentStatusINVALIDAssignmentStatusREADY" var _AssignmentStatusIndex = [...]uint8{0, 23, 44} const _AssignmentStatusLowerName = "assignmentstatusinvalidassignmentstatusready" func (i AssignmentStatus) String() string { if i < 0 || i >= AssignmentStatus(len(_AssignmentStatusIndex)-1) { return fmt.Sprintf("AssignmentStatus(%d)", i) } return _AssignmentStatusName[_AssignmentStatusIndex[i]:_AssignmentStatusIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _AssignmentStatusNoOp() { var x [1]struct{} _ = x[AssignmentStatusINVALID-(0)] _ = x[AssignmentStatusREADY-(1)] } var _AssignmentStatusValues = []AssignmentStatus{AssignmentStatusINVALID, AssignmentStatusREADY} var _AssignmentStatusNameToValueMap = map[string]AssignmentStatus{ _AssignmentStatusName[0:23]: AssignmentStatusINVALID, _AssignmentStatusLowerName[0:23]: AssignmentStatusINVALID, _AssignmentStatusName[23:44]: AssignmentStatusREADY, _AssignmentStatusLowerName[23:44]: AssignmentStatusREADY, } var _AssignmentStatusNames = []string{ _AssignmentStatusName[0:23], _AssignmentStatusName[23:44], } // AssignmentStatusString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func AssignmentStatusString(s string) (AssignmentStatus, error) { if val, ok := _AssignmentStatusNameToValueMap[s]; ok { return val, nil } if val, ok := _AssignmentStatusNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to AssignmentStatus values", s) } // AssignmentStatusValues returns all values of the enum func AssignmentStatusValues() []AssignmentStatus { return _AssignmentStatusValues } // AssignmentStatusStrings returns a slice of all String values of the enum func AssignmentStatusStrings() []string { strs := make([]string, len(_AssignmentStatusNames)) copy(strs, _AssignmentStatusNames) return strs } // IsAAssignmentStatus returns "true" if the value is listed in the enum definition. "false" otherwise func (i AssignmentStatus) IsAAssignmentStatus() bool { for _, v := range _AssignmentStatusValues { if i == v { return true } } return false } // MarshalJSON implements the json.Marshaler interface for AssignmentStatus func (i AssignmentStatus) MarshalJSON() ([]byte, error) { return json.Marshal(i.String()) } // UnmarshalJSON implements the json.Unmarshaler interface for AssignmentStatus func (i *AssignmentStatus) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return fmt.Errorf("AssignmentStatus should be a string, got %s", data) } var err error *i, err = AssignmentStatusString(s) return err } const _MigrationModeName = "MigrationModeINVALIDMigrationModeLOCALPASSTHROUGHMigrationModeLOCALPASSTHROUGHSHADOWMigrationModeDISTRIBUTEDPASSTHROUGHMigrationModeONBOARDED" var _MigrationModeIndex = [...]uint8{0, 20, 49, 84, 119, 141} const _MigrationModeLowerName = "migrationmodeinvalidmigrationmodelocalpassthroughmigrationmodelocalpassthroughshadowmigrationmodedistributedpassthroughmigrationmodeonboarded" func (i MigrationMode) String() string { if i < 0 || i >= MigrationMode(len(_MigrationModeIndex)-1) { return fmt.Sprintf("MigrationMode(%d)", i) } return _MigrationModeName[_MigrationModeIndex[i]:_MigrationModeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _MigrationModeNoOp() { var x [1]struct{} _ = x[MigrationModeINVALID-(0)] _ = x[MigrationModeLOCALPASSTHROUGH-(1)] _ = x[MigrationModeLOCALPASSTHROUGHSHADOW-(2)] _ = x[MigrationModeDISTRIBUTEDPASSTHROUGH-(3)] _ = x[MigrationModeONBOARDED-(4)] } var _MigrationModeValues = []MigrationMode{MigrationModeINVALID, MigrationModeLOCALPASSTHROUGH, MigrationModeLOCALPASSTHROUGHSHADOW, MigrationModeDISTRIBUTEDPASSTHROUGH, MigrationModeONBOARDED} var _MigrationModeNameToValueMap = map[string]MigrationMode{ _MigrationModeName[0:20]: MigrationModeINVALID, _MigrationModeLowerName[0:20]: MigrationModeINVALID, _MigrationModeName[20:49]: MigrationModeLOCALPASSTHROUGH, _MigrationModeLowerName[20:49]: MigrationModeLOCALPASSTHROUGH, _MigrationModeName[49:84]: MigrationModeLOCALPASSTHROUGHSHADOW, _MigrationModeLowerName[49:84]: MigrationModeLOCALPASSTHROUGHSHADOW, _MigrationModeName[84:119]: MigrationModeDISTRIBUTEDPASSTHROUGH, _MigrationModeLowerName[84:119]: MigrationModeDISTRIBUTEDPASSTHROUGH, _MigrationModeName[119:141]: MigrationModeONBOARDED, _MigrationModeLowerName[119:141]: MigrationModeONBOARDED, } var _MigrationModeNames = []string{ _MigrationModeName[0:20], _MigrationModeName[20:49], _MigrationModeName[49:84], _MigrationModeName[84:119], _MigrationModeName[119:141], } // MigrationModeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func MigrationModeString(s string) (MigrationMode, error) { if val, ok := _MigrationModeNameToValueMap[s]; ok { return val, nil } if val, ok := _MigrationModeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to MigrationMode values", s) } // MigrationModeValues returns all values of the enum func MigrationModeValues() []MigrationMode { return _MigrationModeValues } // MigrationModeStrings returns a slice of all String values of the enum func MigrationModeStrings() []string { strs := make([]string, len(_MigrationModeNames)) copy(strs, _MigrationModeNames) return strs } // IsAMigrationMode returns "true" if the value is listed in the enum definition. "false" otherwise func (i MigrationMode) IsAMigrationMode() bool { for _, v := range _MigrationModeValues { if i == v { return true } } return false } // MarshalJSON implements the json.Marshaler interface for MigrationMode func (i MigrationMode) MarshalJSON() ([]byte, error) { return json.Marshal(i.String()) } // UnmarshalJSON implements the json.Unmarshaler interface for MigrationMode func (i *MigrationMode) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return fmt.Errorf("MigrationMode should be a string, got %s", data) } var err error *i, err = MigrationModeString(s) return err } const _HandoverTypeName = "HandoverTypeINVALIDHandoverTypeGRACEFULHandoverTypeEMERGENCY" var _HandoverTypeIndex = [...]uint8{0, 19, 39, 60} const _HandoverTypeLowerName = "handovertypeinvalidhandovertypegracefulhandovertypeemergency" func (i HandoverType) String() string { if i < 0 || i >= HandoverType(len(_HandoverTypeIndex)-1) { return fmt.Sprintf("HandoverType(%d)", i) } return _HandoverTypeName[_HandoverTypeIndex[i]:_HandoverTypeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _HandoverTypeNoOp() { var x [1]struct{} _ = x[HandoverTypeINVALID-(0)] _ = x[HandoverTypeGRACEFUL-(1)] _ = x[HandoverTypeEMERGENCY-(2)] } var _HandoverTypeValues = []HandoverType{HandoverTypeINVALID, HandoverTypeGRACEFUL, HandoverTypeEMERGENCY} var _HandoverTypeNameToValueMap = map[string]HandoverType{ _HandoverTypeName[0:19]: HandoverTypeINVALID, _HandoverTypeLowerName[0:19]: HandoverTypeINVALID, _HandoverTypeName[19:39]: HandoverTypeGRACEFUL, _HandoverTypeLowerName[19:39]: HandoverTypeGRACEFUL, _HandoverTypeName[39:60]: HandoverTypeEMERGENCY, _HandoverTypeLowerName[39:60]: HandoverTypeEMERGENCY, } var _HandoverTypeNames = []string{ _HandoverTypeName[0:19], _HandoverTypeName[19:39], _HandoverTypeName[39:60], } // HandoverTypeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func HandoverTypeString(s string) (HandoverType, error) { if val, ok := _HandoverTypeNameToValueMap[s]; ok { return val, nil } if val, ok := _HandoverTypeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to HandoverType values", s) } // HandoverTypeValues returns all values of the enum func HandoverTypeValues() []HandoverType { return _HandoverTypeValues } // HandoverTypeStrings returns a slice of all String values of the enum func HandoverTypeStrings() []string { strs := make([]string, len(_HandoverTypeNames)) copy(strs, _HandoverTypeNames) return strs } // IsAHandoverType returns "true" if the value is listed in the enum definition. "false" otherwise func (i HandoverType) IsAHandoverType() bool { for _, v := range _HandoverTypeValues { if i == v { return true } } return false } // MarshalJSON implements the json.Marshaler interface for HandoverType func (i HandoverType) MarshalJSON() ([]byte, error) { return json.Marshal(i.String()) } // UnmarshalJSON implements the json.Unmarshaler interface for HandoverType func (i *HandoverType) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return fmt.Errorf("HandoverType should be a string, got %s", data) } var err error *i, err = HandoverTypeString(s) return err } const _LoadBalancingModeName = "LoadBalancingModeINVALIDLoadBalancingModeNAIVELoadBalancingModeGREEDY" var _LoadBalancingModeIndex = [...]uint8{0, 24, 46, 69} const _LoadBalancingModeLowerName = "loadbalancingmodeinvalidloadbalancingmodenaiveloadbalancingmodegreedy" func (i LoadBalancingMode) String() string { if i < 0 || i >= LoadBalancingMode(len(_LoadBalancingModeIndex)-1) { return fmt.Sprintf("LoadBalancingMode(%d)", i) } return _LoadBalancingModeName[_LoadBalancingModeIndex[i]:_LoadBalancingModeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _LoadBalancingModeNoOp() { var x [1]struct{} _ = x[LoadBalancingModeINVALID-(0)] _ = x[LoadBalancingModeNAIVE-(1)] _ = x[LoadBalancingModeGREEDY-(2)] } var _LoadBalancingModeValues = []LoadBalancingMode{LoadBalancingModeINVALID, LoadBalancingModeNAIVE, LoadBalancingModeGREEDY} var _LoadBalancingModeNameToValueMap = map[string]LoadBalancingMode{ _LoadBalancingModeName[0:24]: LoadBalancingModeINVALID, _LoadBalancingModeLowerName[0:24]: LoadBalancingModeINVALID, _LoadBalancingModeName[24:46]: LoadBalancingModeNAIVE, _LoadBalancingModeLowerName[24:46]: LoadBalancingModeNAIVE, _LoadBalancingModeName[46:69]: LoadBalancingModeGREEDY, _LoadBalancingModeLowerName[46:69]: LoadBalancingModeGREEDY, } var _LoadBalancingModeNames = []string{ _LoadBalancingModeName[0:24], _LoadBalancingModeName[24:46], _LoadBalancingModeName[46:69], } // LoadBalancingModeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func LoadBalancingModeString(s string) (LoadBalancingMode, error) { if val, ok := _LoadBalancingModeNameToValueMap[s]; ok { return val, nil } if val, ok := _LoadBalancingModeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to LoadBalancingMode values", s) } // LoadBalancingModeValues returns all values of the enum func LoadBalancingModeValues() []LoadBalancingMode { return _LoadBalancingModeValues } // LoadBalancingModeStrings returns a slice of all String values of the enum func LoadBalancingModeStrings() []string { strs := make([]string, len(_LoadBalancingModeNames)) copy(strs, _LoadBalancingModeNames) return strs } // IsALoadBalancingMode returns "true" if the value is listed in the enum definition. "false" otherwise func (i LoadBalancingMode) IsALoadBalancingMode() bool { for _, v := range _LoadBalancingModeValues { if i == v { return true } } return false } // MarshalJSON implements the json.Marshaler interface for LoadBalancingMode func (i LoadBalancingMode) MarshalJSON() ([]byte, error) { return json.Marshal(i.String()) } // UnmarshalJSON implements the json.Unmarshaler interface for LoadBalancingMode func (i *LoadBalancingMode) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return fmt.Errorf("LoadBalancingMode should be a string, got %s", data) } var err error *i, err = LoadBalancingModeString(s) return err } ================================================ FILE: common/types/sharddistributor_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "testing" "github.com/stretchr/testify/assert" ) func TestGetShardOwnerRequest_GetShardKey(t *testing.T) { tests := []struct { name string req *GetShardOwnerRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty request", req: &GetShardOwnerRequest{}, want: "", }, { name: "has shard key", req: &GetShardOwnerRequest{ShardKey: "shard-key"}, want: "shard-key", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetShardKey() assert.Equal(t, tt.want, got) }) } } func TestGetShardOwnerRequest_GetNamespace(t *testing.T) { tests := []struct { name string req *GetShardOwnerRequest want string }{ { name: "nil request", req: nil, want: "", }, { name: "empty request", req: &GetShardOwnerRequest{}, want: "", }, { name: "has namespace", req: &GetShardOwnerRequest{Namespace: "namespace"}, want: "namespace", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.req.GetNamespace() assert.Equal(t, tt.want, got) }) } } func TestGetShardOwnerResponse_GetOwner(t *testing.T) { tests := []struct { name string resp *GetShardOwnerResponse want string }{ { name: "nil response", resp: nil, want: "", }, { name: "empty response", resp: &GetShardOwnerResponse{}, want: "", }, { name: "has owner", resp: &GetShardOwnerResponse{Owner: "owner"}, want: "owner", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.resp.GetOwner() assert.Equal(t, tt.want, got) }) } } func TestGetShardOwnerResponse_GetNamespace(t *testing.T) { tests := []struct { name string resp *GetShardOwnerResponse want string }{ { name: "nil response", resp: nil, want: "", }, { name: "empty response", resp: &GetShardOwnerResponse{}, want: "", }, { name: "has namespace", resp: &GetShardOwnerResponse{Namespace: "namespace"}, want: "namespace", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.resp.GetNamespace() assert.Equal(t, tt.want, got) }) } } ================================================ FILE: common/types/shared.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "errors" "fmt" "sort" "strconv" "strings" "time" "unsafe" ) // AccessDeniedError is an internal type (TBD...) // TODO(c-warren): Move to common/types/errors.go type AccessDeniedError struct { Message string `json:"message,required"` } // ActivityLocalDispatchInfo is an internal type (TBD...) type ActivityLocalDispatchInfo struct { ActivityID string `json:"activityId,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` ScheduledTimestampOfThisAttempt *int64 `json:"scheduledTimestampOfThisAttempt,omitempty"` TaskToken []byte `json:"taskToken,omitempty"` } // GetScheduledTimestamp is an internal getter (TBD...) func (v *ActivityLocalDispatchInfo) GetScheduledTimestamp() (o int64) { if v != nil && v.ScheduledTimestamp != nil { return *v.ScheduledTimestamp } return } // ActivityTaskCancelRequestedEventAttributes is an internal type (TBD...) type ActivityTaskCancelRequestedEventAttributes struct { ActivityID string `json:"activityId,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` } // GetActivityID is an internal getter (TBD...) func (v *ActivityTaskCancelRequestedEventAttributes) GetActivityID() (o string) { if v != nil { return v.ActivityID } return } // Size returns the approximate memory used in bytes func (v *ActivityTaskCancelRequestedEventAttributes) ByteSize() uint64 { return 0 } // ActivityTaskCanceledEventAttributes is an internal type (TBD...) type ActivityTaskCanceledEventAttributes struct { Details []byte `json:"details,omitempty"` LatestCancelRequestedEventID int64 `json:"latestCancelRequestedEventId,omitempty"` ScheduledEventID int64 `json:"scheduledEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` Identity string `json:"identity,omitempty"` } // GetScheduledEventID is an internal getter (TBD...) func (v *ActivityTaskCanceledEventAttributes) GetScheduledEventID() (o int64) { if v != nil { return v.ScheduledEventID } return } // Size returns the approximate memory used in bytes func (v *ActivityTaskCanceledEventAttributes) ByteSize() uint64 { return 0 } // ActivityTaskCompletedEventAttributes is an internal type (TBD...) type ActivityTaskCompletedEventAttributes struct { Result []byte `json:"result,omitempty"` ScheduledEventID int64 `json:"scheduledEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` Identity string `json:"identity,omitempty"` } // GetScheduledEventID is an internal getter (TBD...) func (v *ActivityTaskCompletedEventAttributes) GetScheduledEventID() (o int64) { if v != nil { return v.ScheduledEventID } return } // GetStartedEventID is an internal getter (TBD...) func (v *ActivityTaskCompletedEventAttributes) GetStartedEventID() (o int64) { if v != nil { return v.StartedEventID } return } // Size returns the approximate memory used in bytes func (v *ActivityTaskCompletedEventAttributes) ByteSize() uint64 { return 0 } // ActivityTaskFailedEventAttributes is an internal type (TBD...) type ActivityTaskFailedEventAttributes struct { Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` ScheduledEventID int64 `json:"scheduledEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` Identity string `json:"identity,omitempty"` } // GetScheduledEventID is an internal getter (TBD...) func (v *ActivityTaskFailedEventAttributes) GetScheduledEventID() (o int64) { if v != nil { return v.ScheduledEventID } return } // GetStartedEventID is an internal getter (TBD...) func (v *ActivityTaskFailedEventAttributes) GetStartedEventID() (o int64) { if v != nil { return v.StartedEventID } return } // Size returns the approximate memory used in bytes func (v *ActivityTaskFailedEventAttributes) ByteSize() uint64 { return 0 } // ActivityTaskScheduledEventAttributes is an internal type (TBD...) type ActivityTaskScheduledEventAttributes struct { ActivityID string `json:"activityId,omitempty"` ActivityType *ActivityType `json:"activityType,omitempty"` Domain *string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ScheduleToCloseTimeoutSeconds *int32 `json:"scheduleToCloseTimeoutSeconds,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` StartToCloseTimeoutSeconds *int32 `json:"startToCloseTimeoutSeconds,omitempty"` HeartbeatTimeoutSeconds *int32 `json:"heartbeatTimeoutSeconds,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` Header *Header `json:"header,omitempty"` } // GetActivityID is an internal getter (TBD...) func (v *ActivityTaskScheduledEventAttributes) GetActivityID() (o string) { if v != nil { return v.ActivityID } return } // GetActivityType is an internal getter (TBD...) func (v *ActivityTaskScheduledEventAttributes) GetActivityType() (o *ActivityType) { if v != nil && v.ActivityType != nil { return v.ActivityType } return } // GetDomain is an internal getter (TBD...) func (v *ActivityTaskScheduledEventAttributes) GetDomain() (o string) { if v != nil && v.Domain != nil { return *v.Domain } return } // GetTaskList is an internal getter (TBD...) func (v *ActivityTaskScheduledEventAttributes) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetScheduleToCloseTimeoutSeconds is an internal getter (TBD...) func (v *ActivityTaskScheduledEventAttributes) GetScheduleToCloseTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToCloseTimeoutSeconds != nil { return *v.ScheduleToCloseTimeoutSeconds } return } // GetScheduleToStartTimeoutSeconds is an internal getter (TBD...) func (v *ActivityTaskScheduledEventAttributes) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // GetStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *ActivityTaskScheduledEventAttributes) GetStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.StartToCloseTimeoutSeconds != nil { return *v.StartToCloseTimeoutSeconds } return } // GetHeartbeatTimeoutSeconds is an internal getter (TBD...) func (v *ActivityTaskScheduledEventAttributes) GetHeartbeatTimeoutSeconds() (o int32) { if v != nil && v.HeartbeatTimeoutSeconds != nil { return *v.HeartbeatTimeoutSeconds } return } // Size returns the approximate memory used in bytes func (v *ActivityTaskScheduledEventAttributes) ByteSize() uint64 { return 0 } // ActivityTaskStartedEventAttributes is an internal type (TBD...) type ActivityTaskStartedEventAttributes struct { ScheduledEventID int64 `json:"scheduledEventId,omitempty"` Identity string `json:"identity,omitempty"` RequestID string `json:"requestId,omitempty"` Attempt int32 `json:"attempt,omitempty"` LastFailureReason *string `json:"lastFailureReason,omitempty"` LastFailureDetails []byte `json:"lastFailureDetails,omitempty"` } // GetScheduledEventID is an internal getter (TBD...) func (v *ActivityTaskStartedEventAttributes) GetScheduledEventID() (o int64) { if v != nil { return v.ScheduledEventID } return } // GetRequestID is an internal getter (TBD...) func (v *ActivityTaskStartedEventAttributes) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // Size returns the approximate memory used in bytes func (v *ActivityTaskStartedEventAttributes) ByteSize() uint64 { return 0 } // ActivityTaskTimedOutEventAttributes is an internal type (TBD...) type ActivityTaskTimedOutEventAttributes struct { Details []byte `json:"details,omitempty"` ScheduledEventID int64 `json:"scheduledEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` TimeoutType *TimeoutType `json:"timeoutType,omitempty"` LastFailureReason *string `json:"lastFailureReason,omitempty"` LastFailureDetails []byte `json:"lastFailureDetails,omitempty"` } // GetScheduledEventID is an internal getter (TBD...) func (v *ActivityTaskTimedOutEventAttributes) GetScheduledEventID() (o int64) { if v != nil { return v.ScheduledEventID } return } // GetTimeoutType is an internal getter (TBD...) func (v *ActivityTaskTimedOutEventAttributes) GetTimeoutType() (o TimeoutType) { if v != nil && v.TimeoutType != nil { return *v.TimeoutType } return } // Size returns the approximate memory used in bytes func (v *ActivityTaskTimedOutEventAttributes) ByteSize() uint64 { return 0 } // ActivityType is an internal type (TBD...) type ActivityType struct { Name string `json:"name,omitempty"` } // GetName is an internal getter (TBD...) func (v *ActivityType) GetName() (o string) { if v != nil { return v.Name } return } // ArchivalStatus is an internal type (TBD...) type ArchivalStatus int32 // Ptr is a helper function for getting pointer value func (e ArchivalStatus) Ptr() *ArchivalStatus { return &e } // String returns a readable string representation of ArchivalStatus. func (e ArchivalStatus) String() string { w := int32(e) switch w { case 0: return "DISABLED" case 1: return "ENABLED" } return fmt.Sprintf("ArchivalStatus(%d)", w) } // UnmarshalText parses enum value from string representation func (e *ArchivalStatus) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "DISABLED": *e = ArchivalStatusDisabled return nil case "ENABLED": *e = ArchivalStatusEnabled return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ArchivalStatus", err) } *e = ArchivalStatus(val) return nil } } // MarshalText encodes ArchivalStatus to text. func (e ArchivalStatus) MarshalText() ([]byte, error) { return []byte(e.String()), nil } // ByteSize returns the approximate memory used in bytes func (e *ArchivalStatus) ByteSize() uint64 { if e == nil { return 0 } return uint64(unsafe.Sizeof(*e)) } const ( // ArchivalStatusDisabled is an option for ArchivalStatus ArchivalStatusDisabled ArchivalStatus = iota // ArchivalStatusEnabled is an option for ArchivalStatus ArchivalStatusEnabled ) // BadBinaries is an internal type (TBD...) type BadBinaries struct { Binaries map[string]*BadBinaryInfo `json:"binaries,omitempty"` } // ByteSize returns the approximate memory used in bytes func (b *BadBinaries) ByteSize() uint64 { if b == nil { return 0 } size := uint64(unsafe.Sizeof(*b)) if b.Binaries != nil { for k, v := range b.Binaries { size += uint64(len(k)) + v.ByteSize() } } return size } // DeepCopy returns a deep copy of BadBinaries func (b *BadBinaries) DeepCopy() BadBinaries { if b == nil { return BadBinaries{} } result := BadBinaries{} if b.Binaries != nil { result.Binaries = make(map[string]*BadBinaryInfo, len(b.Binaries)) for k, v := range b.Binaries { if v != nil { copyV := &BadBinaryInfo{ Reason: v.Reason, Operator: v.Operator, } if v.CreatedTimeNano != nil { createdTime := *v.CreatedTimeNano copyV.CreatedTimeNano = &createdTime } result.Binaries[k] = copyV } else { // Preserve nil entries in the map result.Binaries[k] = nil } } } return result } // BadBinaryInfo is an internal type (TBD...) type BadBinaryInfo struct { Reason string `json:"reason,omitempty"` Operator string `json:"operator,omitempty"` CreatedTimeNano *int64 `json:"createdTimeNano,omitempty"` } // GetReason is an internal getter (TBD...) func (v *BadBinaryInfo) GetReason() (o string) { if v != nil { return v.Reason } return } // GetOperator is an internal getter (TBD...) func (v *BadBinaryInfo) GetOperator() (o string) { if v != nil { return v.Operator } return } // GetCreatedTimeNano is an internal getter (TBD...) func (v *BadBinaryInfo) GetCreatedTimeNano() (o int64) { if v != nil && v.CreatedTimeNano != nil { return *v.CreatedTimeNano } return } // ByteSize returns the approximate memory used in bytes func (v *BadBinaryInfo) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += uint64(len(v.Reason)) size += uint64(len(v.Operator)) if v.CreatedTimeNano != nil { size += uint64(unsafe.Sizeof(*v.CreatedTimeNano)) } return size } // BadRequestError is an internal type (TBD...) // TODO(c-warren): Move to common/types/errors.go type BadRequestError struct { Message string `json:"message,required"` } // CancelExternalWorkflowExecutionFailedCause is an internal type (TBD...) type CancelExternalWorkflowExecutionFailedCause int32 // Ptr is a helper function for getting pointer value func (e CancelExternalWorkflowExecutionFailedCause) Ptr() *CancelExternalWorkflowExecutionFailedCause { return &e } // String returns a readable string representation of CancelExternalWorkflowExecutionFailedCause. func (e CancelExternalWorkflowExecutionFailedCause) String() string { w := int32(e) switch w { case 0: return "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION" case 1: return "WORKFLOW_ALREADY_COMPLETED" } return fmt.Sprintf("CancelExternalWorkflowExecutionFailedCause(%d)", w) } // UnmarshalText parses enum value from string representation func (e *CancelExternalWorkflowExecutionFailedCause) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION": *e = CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution return nil case "WORKFLOW_ALREADY_COMPLETED": *e = CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "CancelExternalWorkflowExecutionFailedCause", err) } *e = CancelExternalWorkflowExecutionFailedCause(val) return nil } } // MarshalText encodes CancelExternalWorkflowExecutionFailedCause to text. func (e CancelExternalWorkflowExecutionFailedCause) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution is an option for CancelExternalWorkflowExecutionFailedCause CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution CancelExternalWorkflowExecutionFailedCause = iota CancelExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted ) // CancelTimerDecisionAttributes is an internal type (TBD...) type CancelTimerDecisionAttributes struct { TimerID string `json:"timerId,omitempty"` } // GetTimerID is an internal getter (TBD...) func (v *CancelTimerDecisionAttributes) GetTimerID() (o string) { if v != nil { return v.TimerID } return } // CancelTimerFailedEventAttributes is an internal type (TBD...) type CancelTimerFailedEventAttributes struct { TimerID string `json:"timerId,omitempty"` Cause string `json:"cause,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` Identity string `json:"identity,omitempty"` } // Size returns the approximate memory used in bytes func (v *CancelTimerFailedEventAttributes) ByteSize() uint64 { return 0 } // CancelWorkflowExecutionDecisionAttributes is an internal type (TBD...) type CancelWorkflowExecutionDecisionAttributes struct { Details []byte `json:"details,omitempty"` } // CancellationAlreadyRequestedError is an internal type (TBD...) type CancellationAlreadyRequestedError struct { Message string `json:"message,required"` } // ChildWorkflowExecutionCanceledEventAttributes is an internal type (TBD...) type ChildWorkflowExecutionCanceledEventAttributes struct { Details []byte `json:"details,omitempty"` Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` InitiatedEventID int64 `json:"initiatedEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` } // GetInitiatedEventID is an internal getter (TBD...) func (v *ChildWorkflowExecutionCanceledEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // Size returns the approximate memory used in bytes func (v *ChildWorkflowExecutionCanceledEventAttributes) ByteSize() uint64 { return 0 } // ChildWorkflowExecutionCompletedEventAttributes is an internal type (TBD...) type ChildWorkflowExecutionCompletedEventAttributes struct { Result []byte `json:"result,omitempty"` Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` InitiatedEventID int64 `json:"initiatedEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` } // GetInitiatedEventID is an internal getter (TBD...) func (v *ChildWorkflowExecutionCompletedEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // Size returns the approximate memory used in bytes func (v *ChildWorkflowExecutionCompletedEventAttributes) ByteSize() uint64 { return 0 } // ChildWorkflowExecutionFailedCause is an internal type (TBD...) type ChildWorkflowExecutionFailedCause int32 // Ptr is a helper function for getting pointer value func (e ChildWorkflowExecutionFailedCause) Ptr() *ChildWorkflowExecutionFailedCause { return &e } // String returns a readable string representation of ChildWorkflowExecutionFailedCause. func (e ChildWorkflowExecutionFailedCause) String() string { w := int32(e) switch w { case 0: return "WORKFLOW_ALREADY_RUNNING" } return fmt.Sprintf("ChildWorkflowExecutionFailedCause(%d)", w) } // UnmarshalText parses enum value from string representation func (e *ChildWorkflowExecutionFailedCause) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "WORKFLOW_ALREADY_RUNNING": *e = ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ChildWorkflowExecutionFailedCause", err) } *e = ChildWorkflowExecutionFailedCause(val) return nil } } // MarshalText encodes ChildWorkflowExecutionFailedCause to text. func (e ChildWorkflowExecutionFailedCause) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning is an option for ChildWorkflowExecutionFailedCause ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning ChildWorkflowExecutionFailedCause = iota ) // ChildWorkflowExecutionFailedEventAttributes is an internal type (TBD...) type ChildWorkflowExecutionFailedEventAttributes struct { Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` InitiatedEventID int64 `json:"initiatedEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` } // GetInitiatedEventID is an internal getter (TBD...) func (v *ChildWorkflowExecutionFailedEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // Size returns the approximate memory used in bytes func (v *ChildWorkflowExecutionFailedEventAttributes) ByteSize() uint64 { return 0 } // ChildWorkflowExecutionStartedEventAttributes is an internal type (TBD...) type ChildWorkflowExecutionStartedEventAttributes struct { Domain string `json:"domain,omitempty"` InitiatedEventID int64 `json:"initiatedEventId,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` Header *Header `json:"header,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *ChildWorkflowExecutionStartedEventAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetInitiatedEventID is an internal getter (TBD...) func (v *ChildWorkflowExecutionStartedEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // GetWorkflowExecution is an internal getter (TBD...) func (v *ChildWorkflowExecutionStartedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // Size returns the approximate memory used in bytes func (v *ChildWorkflowExecutionStartedEventAttributes) ByteSize() uint64 { return 0 } // ChildWorkflowExecutionTerminatedEventAttributes is an internal type (TBD...) type ChildWorkflowExecutionTerminatedEventAttributes struct { Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` InitiatedEventID int64 `json:"initiatedEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` } // GetInitiatedEventID is an internal getter (TBD...) func (v *ChildWorkflowExecutionTerminatedEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // Size returns the approximate memory used in bytes func (v *ChildWorkflowExecutionTerminatedEventAttributes) ByteSize() uint64 { return 0 } // ChildWorkflowExecutionTimedOutEventAttributes is an internal type (TBD...) type ChildWorkflowExecutionTimedOutEventAttributes struct { TimeoutType *TimeoutType `json:"timeoutType,omitempty"` Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` InitiatedEventID int64 `json:"initiatedEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` } // GetInitiatedEventID is an internal getter (TBD...) func (v *ChildWorkflowExecutionTimedOutEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // Size returns the approximate memory used in bytes func (v *ChildWorkflowExecutionTimedOutEventAttributes) ByteSize() uint64 { return 0 } // ClientVersionNotSupportedError is an internal type (TBD...) type ClientVersionNotSupportedError struct { FeatureVersion string `json:"featureVersion,required"` ClientImpl string `json:"clientImpl,required"` SupportedVersions string `json:"supportedVersions,required"` } // FeatureNotEnabledError is an internal type (TBD...) type FeatureNotEnabledError struct { FeatureFlag string `json:"featureFlag,required"` } // CloseShardRequest is an internal type (TBD...) type CloseShardRequest struct { ShardID int32 `json:"shardID,omitempty"` } // GetShardID is an internal getter (TBD...) func (v *CloseShardRequest) GetShardID() (o int32) { if v != nil { return v.ShardID } return } // ClusterInfo is an internal type (TBD...) type ClusterInfo struct { SupportedClientVersions *SupportedClientVersions `json:"supportedClientVersions,omitempty"` } // ClusterReplicationConfiguration is an internal type (TBD...) type ClusterReplicationConfiguration struct { ClusterName string `json:"clusterName,omitempty"` } // GetClusterName is an internal getter (TBD...) func (v *ClusterReplicationConfiguration) GetClusterName() (o string) { if v != nil { return v.ClusterName } return } // ByteSize returns the approximate memory used in bytes func (v *ClusterReplicationConfiguration) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += uint64(len(v.ClusterName)) return size } // CompleteWorkflowExecutionDecisionAttributes is an internal type (TBD...) type CompleteWorkflowExecutionDecisionAttributes struct { Result []byte `json:"result,omitempty"` } // ContinueAsNewInitiator is an internal type (TBD...) type ContinueAsNewInitiator int32 // Ptr is a helper function for getting pointer value func (e ContinueAsNewInitiator) Ptr() *ContinueAsNewInitiator { return &e } // String returns a readable string representation of ContinueAsNewInitiator. func (e ContinueAsNewInitiator) String() string { w := int32(e) switch w { case 0: return "Decider" case 1: return "RetryPolicy" case 2: return "CronSchedule" } return fmt.Sprintf("ContinueAsNewInitiator(%d)", w) } // UnmarshalText parses enum value from string representation func (e *ContinueAsNewInitiator) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "DECIDER": *e = ContinueAsNewInitiatorDecider return nil case "RETRYPOLICY": *e = ContinueAsNewInitiatorRetryPolicy return nil case "CRONSCHEDULE": *e = ContinueAsNewInitiatorCronSchedule return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ContinueAsNewInitiator", err) } *e = ContinueAsNewInitiator(val) return nil } } // MarshalText encodes ContinueAsNewInitiator to text. func (e ContinueAsNewInitiator) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // ContinueAsNewInitiatorDecider is an option for ContinueAsNewInitiator ContinueAsNewInitiatorDecider ContinueAsNewInitiator = iota // ContinueAsNewInitiatorRetryPolicy is an option for ContinueAsNewInitiator ContinueAsNewInitiatorRetryPolicy // ContinueAsNewInitiatorCronSchedule is an option for ContinueAsNewInitiator ContinueAsNewInitiatorCronSchedule ) // ContinueAsNewWorkflowExecutionDecisionAttributes is an internal type (TBD...) type ContinueAsNewWorkflowExecutionDecisionAttributes struct { WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` BackoffStartIntervalInSeconds *int32 `json:"backoffStartIntervalInSeconds,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` Initiator *ContinueAsNewInitiator `json:"initiator,omitempty"` FailureReason *string `json:"failureReason,omitempty"` FailureDetails []byte `json:"failureDetails,omitempty"` LastCompletionResult []byte `json:"lastCompletionResult,omitempty"` CronSchedule string `json:"cronSchedule,omitempty"` Header *Header `json:"header,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` JitterStartSeconds *int32 `json:"jitterStartSeconds,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // GetExecutionStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // GetTaskStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // GetBackoffStartIntervalInSeconds is an internal getter (TBD...) func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetBackoffStartIntervalInSeconds() (o int32) { if v != nil && v.BackoffStartIntervalInSeconds != nil { return *v.BackoffStartIntervalInSeconds } return } // GetJitterStartSeconds is an internal getter (TBD...) func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetJitterStartSeconds() (o int32) { if v != nil && v.JitterStartSeconds != nil { return *v.JitterStartSeconds } return } // GetInitiator is an internal getter (TBD...) func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetInitiator() (o ContinueAsNewInitiator) { if v != nil && v.Initiator != nil { return *v.Initiator } return } // GetSearchAttributes is an internal getter (TBD...) func (v *ContinueAsNewWorkflowExecutionDecisionAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // CountWorkflowExecutionsRequest is an internal type (TBD...) type CountWorkflowExecutionsRequest struct { Domain string `json:"domain,omitempty"` Query string `json:"query,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *CountWorkflowExecutionsRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetQuery is an internal getter (TBD...) func (v *CountWorkflowExecutionsRequest) GetQuery() (o string) { if v != nil { return v.Query } return } // CountWorkflowExecutionsResponse is an internal type (TBD...) type CountWorkflowExecutionsResponse struct { Count int64 `json:"count,omitempty"` } // GetCount is an internal getter (TBD...) func (v *CountWorkflowExecutionsResponse) GetCount() (o int64) { if v != nil { return v.Count } return } // CurrentBranchChangedError is an internal type (TBD...) type CurrentBranchChangedError struct { Message string `json:"message,required"` CurrentBranchToken []byte `json:"currentBranchToken,required"` } // GetCurrentBranchToken is an internal getter (TBD...) func (v *CurrentBranchChangedError) GetCurrentBranchToken() (o []byte) { if v != nil && v.CurrentBranchToken != nil { return v.CurrentBranchToken } return } // DataBlob is an internal type (TBD...) type DataBlob struct { EncodingType *EncodingType `json:"EncodingType,omitempty"` Data []byte `json:"Data,omitempty"` } // GetEncodingType is an internal getter (TBD...) func (v *DataBlob) GetEncodingType() (o EncodingType) { if v != nil && v.EncodingType != nil { return *v.EncodingType } return } // GetData is an internal getter (TBD...) func (v *DataBlob) GetData() (o []byte) { if v != nil && v.Data != nil { return v.Data } return } func (v *DataBlob) DeepCopy() *DataBlob { if v == nil { return nil } res := &DataBlob{ EncodingType: v.EncodingType, } if v.Data != nil { res.Data = make([]byte, len(v.Data)) copy(res.Data, v.Data) } return res } // ByteSize returns the approximate memory used in bytes func (v *DataBlob) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += v.EncodingType.ByteSize() size += uint64(len(v.Data)) return size } // Decision is an internal type (TBD...) type Decision struct { DecisionType *DecisionType `json:"decisionType,omitempty"` ScheduleActivityTaskDecisionAttributes *ScheduleActivityTaskDecisionAttributes `json:"scheduleActivityTaskDecisionAttributes,omitempty"` StartTimerDecisionAttributes *StartTimerDecisionAttributes `json:"startTimerDecisionAttributes,omitempty"` CompleteWorkflowExecutionDecisionAttributes *CompleteWorkflowExecutionDecisionAttributes `json:"completeWorkflowExecutionDecisionAttributes,omitempty"` FailWorkflowExecutionDecisionAttributes *FailWorkflowExecutionDecisionAttributes `json:"failWorkflowExecutionDecisionAttributes,omitempty"` RequestCancelActivityTaskDecisionAttributes *RequestCancelActivityTaskDecisionAttributes `json:"requestCancelActivityTaskDecisionAttributes,omitempty"` CancelTimerDecisionAttributes *CancelTimerDecisionAttributes `json:"cancelTimerDecisionAttributes,omitempty"` CancelWorkflowExecutionDecisionAttributes *CancelWorkflowExecutionDecisionAttributes `json:"cancelWorkflowExecutionDecisionAttributes,omitempty"` RequestCancelExternalWorkflowExecutionDecisionAttributes *RequestCancelExternalWorkflowExecutionDecisionAttributes `json:"requestCancelExternalWorkflowExecutionDecisionAttributes,omitempty"` RecordMarkerDecisionAttributes *RecordMarkerDecisionAttributes `json:"recordMarkerDecisionAttributes,omitempty"` ContinueAsNewWorkflowExecutionDecisionAttributes *ContinueAsNewWorkflowExecutionDecisionAttributes `json:"continueAsNewWorkflowExecutionDecisionAttributes,omitempty"` StartChildWorkflowExecutionDecisionAttributes *StartChildWorkflowExecutionDecisionAttributes `json:"startChildWorkflowExecutionDecisionAttributes,omitempty"` SignalExternalWorkflowExecutionDecisionAttributes *SignalExternalWorkflowExecutionDecisionAttributes `json:"signalExternalWorkflowExecutionDecisionAttributes,omitempty"` UpsertWorkflowSearchAttributesDecisionAttributes *UpsertWorkflowSearchAttributesDecisionAttributes `json:"upsertWorkflowSearchAttributesDecisionAttributes,omitempty"` } // GetDecisionType is an internal getter (TBD...) func (v *Decision) GetDecisionType() (o DecisionType) { if v != nil && v.DecisionType != nil { return *v.DecisionType } return } // DecisionTaskCompletedEventAttributes is an internal type (TBD...) type DecisionTaskCompletedEventAttributes struct { ExecutionContext []byte `json:"executionContext,omitempty"` ScheduledEventID int64 `json:"scheduledEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` Identity string `json:"identity,omitempty"` BinaryChecksum string `json:"binaryChecksum,omitempty"` } // GetStartedEventID is an internal getter (TBD...) func (v *DecisionTaskCompletedEventAttributes) GetStartedEventID() (o int64) { if v != nil { return v.StartedEventID } return } // GetBinaryChecksum is an internal getter (TBD...) func (v *DecisionTaskCompletedEventAttributes) GetBinaryChecksum() (o string) { if v != nil { return v.BinaryChecksum } return } // Size returns the approximate memory used in bytes func (v *DecisionTaskCompletedEventAttributes) ByteSize() uint64 { return 0 } // DecisionTaskFailedCause is an internal type (TBD...) type DecisionTaskFailedCause int32 // Ptr is a helper function for getting pointer value func (e DecisionTaskFailedCause) Ptr() *DecisionTaskFailedCause { return &e } // String returns a readable string representation of DecisionTaskFailedCause. func (e DecisionTaskFailedCause) String() string { w := int32(e) switch w { case 0: return "UNHANDLED_DECISION" case 1: return "BAD_SCHEDULE_ACTIVITY_ATTRIBUTES" case 2: return "BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES" case 3: return "BAD_START_TIMER_ATTRIBUTES" case 4: return "BAD_CANCEL_TIMER_ATTRIBUTES" case 5: return "BAD_RECORD_MARKER_ATTRIBUTES" case 6: return "BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES" case 7: return "BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES" case 8: return "BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES" case 9: return "BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES" case 10: return "BAD_CONTINUE_AS_NEW_ATTRIBUTES" case 11: return "START_TIMER_DUPLICATE_I_D" case 12: return "RESET_STICKY_TASKLIST" case 13: return "WORKFLOW_WORKER_UNHANDLED_FAILURE" case 14: return "BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES" case 15: return "BAD_START_CHILD_EXECUTION_ATTRIBUTES" case 16: return "FORCE_CLOSE_DECISION" case 17: return "FAILOVER_CLOSE_DECISION" case 18: return "BAD_SIGNAL_INPUT_SIZE" case 19: return "RESET_WORKFLOW" case 20: return "BAD_BINARY" case 21: return "SCHEDULE_ACTIVITY_DUPLICATE_I_D" case 22: return "BAD_SEARCH_ATTRIBUTES" } return fmt.Sprintf("DecisionTaskFailedCause(%d)", w) } // UnmarshalText parses enum value from string representation func (e *DecisionTaskFailedCause) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "UNHANDLED_DECISION": *e = DecisionTaskFailedCauseUnhandledDecision return nil case "BAD_SCHEDULE_ACTIVITY_ATTRIBUTES": *e = DecisionTaskFailedCauseBadScheduleActivityAttributes return nil case "BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES": *e = DecisionTaskFailedCauseBadRequestCancelActivityAttributes return nil case "BAD_START_TIMER_ATTRIBUTES": *e = DecisionTaskFailedCauseBadStartTimerAttributes return nil case "BAD_CANCEL_TIMER_ATTRIBUTES": *e = DecisionTaskFailedCauseBadCancelTimerAttributes return nil case "BAD_RECORD_MARKER_ATTRIBUTES": *e = DecisionTaskFailedCauseBadRecordMarkerAttributes return nil case "BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES": *e = DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes return nil case "BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES": *e = DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes return nil case "BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES": *e = DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes return nil case "BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES": *e = DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes return nil case "BAD_CONTINUE_AS_NEW_ATTRIBUTES": *e = DecisionTaskFailedCauseBadContinueAsNewAttributes return nil case "START_TIMER_DUPLICATE_I_D": *e = DecisionTaskFailedCauseStartTimerDuplicateID return nil case "RESET_STICKY_TASKLIST": *e = DecisionTaskFailedCauseResetStickyTasklist return nil case "WORKFLOW_WORKER_UNHANDLED_FAILURE": *e = DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure return nil case "BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES": *e = DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes return nil case "BAD_START_CHILD_EXECUTION_ATTRIBUTES": *e = DecisionTaskFailedCauseBadStartChildExecutionAttributes return nil case "FORCE_CLOSE_DECISION": *e = DecisionTaskFailedCauseForceCloseDecision return nil case "FAILOVER_CLOSE_DECISION": *e = DecisionTaskFailedCauseFailoverCloseDecision return nil case "BAD_SIGNAL_INPUT_SIZE": *e = DecisionTaskFailedCauseBadSignalInputSize return nil case "RESET_WORKFLOW": *e = DecisionTaskFailedCauseResetWorkflow return nil case "BAD_BINARY": *e = DecisionTaskFailedCauseBadBinary return nil case "SCHEDULE_ACTIVITY_DUPLICATE_I_D": *e = DecisionTaskFailedCauseScheduleActivityDuplicateID return nil case "BAD_SEARCH_ATTRIBUTES": *e = DecisionTaskFailedCauseBadSearchAttributes return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DecisionTaskFailedCause", err) } *e = DecisionTaskFailedCause(val) return nil } } // MarshalText encodes DecisionTaskFailedCause to text. func (e DecisionTaskFailedCause) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // DecisionTaskFailedCauseUnhandledDecision is an option for DecisionTaskFailedCause DecisionTaskFailedCauseUnhandledDecision DecisionTaskFailedCause = iota // DecisionTaskFailedCauseBadScheduleActivityAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadScheduleActivityAttributes // DecisionTaskFailedCauseBadRequestCancelActivityAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadRequestCancelActivityAttributes // DecisionTaskFailedCauseBadStartTimerAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadStartTimerAttributes // DecisionTaskFailedCauseBadCancelTimerAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadCancelTimerAttributes // DecisionTaskFailedCauseBadRecordMarkerAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadRecordMarkerAttributes // DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes // DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes // DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes // DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes // DecisionTaskFailedCauseBadContinueAsNewAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadContinueAsNewAttributes // DecisionTaskFailedCauseStartTimerDuplicateID is an option for DecisionTaskFailedCause DecisionTaskFailedCauseStartTimerDuplicateID // DecisionTaskFailedCauseResetStickyTasklist is an option for DecisionTaskFailedCause DecisionTaskFailedCauseResetStickyTasklist // DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure is an option for DecisionTaskFailedCause DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure // DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes // DecisionTaskFailedCauseBadStartChildExecutionAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadStartChildExecutionAttributes // DecisionTaskFailedCauseForceCloseDecision is an option for DecisionTaskFailedCause DecisionTaskFailedCauseForceCloseDecision // DecisionTaskFailedCauseFailoverCloseDecision is an option for DecisionTaskFailedCause DecisionTaskFailedCauseFailoverCloseDecision // DecisionTaskFailedCauseBadSignalInputSize is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadSignalInputSize // DecisionTaskFailedCauseResetWorkflow is an option for DecisionTaskFailedCause DecisionTaskFailedCauseResetWorkflow // DecisionTaskFailedCauseBadBinary is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadBinary // DecisionTaskFailedCauseScheduleActivityDuplicateID is an option for DecisionTaskFailedCause DecisionTaskFailedCauseScheduleActivityDuplicateID // DecisionTaskFailedCauseBadSearchAttributes is an option for DecisionTaskFailedCause DecisionTaskFailedCauseBadSearchAttributes ) // DecisionTaskFailedEventAttributes is an internal type (TBD...) type DecisionTaskFailedEventAttributes struct { ScheduledEventID int64 `json:"scheduledEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` Cause *DecisionTaskFailedCause `json:"cause,omitempty"` Details []byte `json:"details,omitempty"` Identity string `json:"identity,omitempty"` Reason *string `json:"reason,omitempty"` BaseRunID string `json:"baseRunId,omitempty"` NewRunID string `json:"newRunId,omitempty"` ForkEventVersion int64 `json:"forkEventVersion,omitempty"` BinaryChecksum string `json:"binaryChecksum,omitempty"` RequestID string `json:"requestId,omitempty"` } // GetCause is an internal getter (TBD...) func (v *DecisionTaskFailedEventAttributes) GetCause() (o DecisionTaskFailedCause) { if v != nil && v.Cause != nil { return *v.Cause } return } // GetDetails is an internal getter (TBD...) func (v *DecisionTaskFailedEventAttributes) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // GetBaseRunID is an internal getter (TBD...) func (v *DecisionTaskFailedEventAttributes) GetBaseRunID() (o string) { if v != nil { return v.BaseRunID } return } // GetNewRunID is an internal getter (TBD...) func (v *DecisionTaskFailedEventAttributes) GetNewRunID() (o string) { if v != nil { return v.NewRunID } return } // GetForkEventVersion is an internal getter (TBD...) func (v *DecisionTaskFailedEventAttributes) GetForkEventVersion() (o int64) { if v != nil { return v.ForkEventVersion } return } // GetRequestID is an internal getter (TBD...) func (v *DecisionTaskFailedEventAttributes) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // Size returns the approximate memory used in bytes func (v *DecisionTaskFailedEventAttributes) ByteSize() uint64 { return 0 } // DecisionTaskScheduledEventAttributes is an internal type (TBD...) type DecisionTaskScheduledEventAttributes struct { TaskList *TaskList `json:"taskList,omitempty"` StartToCloseTimeoutSeconds *int32 `json:"startToCloseTimeoutSeconds,omitempty"` Attempt int64 `json:"attempt,omitempty"` } // GetTaskList is an internal getter (TBD...) func (v *DecisionTaskScheduledEventAttributes) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *DecisionTaskScheduledEventAttributes) GetStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.StartToCloseTimeoutSeconds != nil { return *v.StartToCloseTimeoutSeconds } return } // GetAttempt is an internal getter (TBD...) func (v *DecisionTaskScheduledEventAttributes) GetAttempt() (o int64) { if v != nil { return v.Attempt } return } // Size returns the approximate memory used in bytes func (v *DecisionTaskScheduledEventAttributes) ByteSize() uint64 { return 0 } // DecisionTaskStartedEventAttributes is an internal type (TBD...) type DecisionTaskStartedEventAttributes struct { ScheduledEventID int64 `json:"scheduledEventId,omitempty"` Identity string `json:"identity,omitempty"` RequestID string `json:"requestId,omitempty"` } // GetScheduledEventID is an internal getter (TBD...) func (v *DecisionTaskStartedEventAttributes) GetScheduledEventID() (o int64) { if v != nil { return v.ScheduledEventID } return } // GetRequestID is an internal getter (TBD...) func (v *DecisionTaskStartedEventAttributes) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // Size returns the approximate memory used in bytes func (v *DecisionTaskStartedEventAttributes) ByteSize() uint64 { return 0 } // DecisionTaskTimedOutCause is an internal type (TBD...) type DecisionTaskTimedOutCause int32 // Ptr is a helper function for getting pointer value func (e DecisionTaskTimedOutCause) Ptr() *DecisionTaskTimedOutCause { return &e } // String returns a readable string representation of DecisionTaskTimedOutCause. func (e DecisionTaskTimedOutCause) String() string { w := int32(e) switch w { case 0: return "Timeout" case 1: return "Reset" } return fmt.Sprintf("DecisionTaskTimedOutCause(%d)", w) } // UnmarshalText parses enum value from string representation func (e *DecisionTaskTimedOutCause) UnmarshalText(value []byte) error { switch s := strings.ToLower(string(value)); s { case "timeout": *e = DecisionTaskTimedOutCauseTimeout return nil case "reset": *e = DecisionTaskTimedOutCauseReset return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DecisionTaskTimedOutCause", err) } *e = DecisionTaskTimedOutCause(val) return nil } } // MarshalText encodes DecisionTaskFailedCause to text. func (e DecisionTaskTimedOutCause) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // DecisionTaskTimedOutCauseTimeout is an option for DecisionTaskTimedOutCause DecisionTaskTimedOutCauseTimeout DecisionTaskTimedOutCause = iota // DecisionTaskTimedOutCauseReset is an option for DecisionTaskTimedOutCause DecisionTaskTimedOutCauseReset ) // DecisionTaskTimedOutEventAttributes is an internal type (TBD...) type DecisionTaskTimedOutEventAttributes struct { ScheduledEventID int64 `json:"scheduledEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` TimeoutType *TimeoutType `json:"timeoutType,omitempty"` BaseRunID string `json:"baseRunId,omitempty"` NewRunID string `json:"newRunId,omitempty"` ForkEventVersion int64 `json:"forkEventVersion,omitempty"` Reason string `json:"reason,omitempty"` Cause *DecisionTaskTimedOutCause `json:"cause,omitempty"` RequestID string `json:"requestId,omitempty"` } // GetScheduledEventID is an internal getter (TBD...) func (v *DecisionTaskTimedOutEventAttributes) GetScheduledEventID() (o int64) { if v != nil { return v.ScheduledEventID } return } // GetTimeoutType is an internal getter (TBD...) func (v *DecisionTaskTimedOutEventAttributes) GetTimeoutType() (o TimeoutType) { if v != nil && v.TimeoutType != nil { return *v.TimeoutType } return } // GetBaseRunID is an internal getter (TBD...) func (v *DecisionTaskTimedOutEventAttributes) GetBaseRunID() (o string) { if v != nil { return v.BaseRunID } return } // GetNewRunID is an internal getter (TBD...) func (v *DecisionTaskTimedOutEventAttributes) GetNewRunID() (o string) { if v != nil { return v.NewRunID } return } // GetForkEventVersion is an internal getter (TBD...) func (v *DecisionTaskTimedOutEventAttributes) GetForkEventVersion() (o int64) { if v != nil { return v.ForkEventVersion } return } // GetCause is an internal getter (TBD...) func (v *DecisionTaskTimedOutEventAttributes) GetCause() (o DecisionTaskTimedOutCause) { if v != nil && v.Cause != nil { return *v.Cause } return } // GetRequestID is an internal getter (TBD...) func (v *DecisionTaskTimedOutEventAttributes) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // Size returns the approximate memory used in bytes func (v *DecisionTaskTimedOutEventAttributes) ByteSize() uint64 { return 0 } // DecisionType is an internal type (TBD...) type DecisionType int32 // Ptr is a helper function for getting pointer value func (e DecisionType) Ptr() *DecisionType { return &e } // String returns a readable string representation of DecisionType. func (e DecisionType) String() string { w := int32(e) switch w { case 0: return "ScheduleActivityTask" case 1: return "RequestCancelActivityTask" case 2: return "StartTimer" case 3: return "CompleteWorkflowExecution" case 4: return "FailWorkflowExecution" case 5: return "CancelTimer" case 6: return "CancelWorkflowExecution" case 7: return "RequestCancelExternalWorkflowExecution" case 8: return "RecordMarker" case 9: return "ContinueAsNewWorkflowExecution" case 10: return "StartChildWorkflowExecution" case 11: return "SignalExternalWorkflowExecution" case 12: return "UpsertWorkflowSearchAttributes" } return fmt.Sprintf("DecisionType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *DecisionType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "SCHEDULEACTIVITYTASK": *e = DecisionTypeScheduleActivityTask return nil case "REQUESTCANCELACTIVITYTASK": *e = DecisionTypeRequestCancelActivityTask return nil case "STARTTIMER": *e = DecisionTypeStartTimer return nil case "COMPLETEWORKFLOWEXECUTION": *e = DecisionTypeCompleteWorkflowExecution return nil case "FAILWORKFLOWEXECUTION": *e = DecisionTypeFailWorkflowExecution return nil case "CANCELTIMER": *e = DecisionTypeCancelTimer return nil case "CANCELWORKFLOWEXECUTION": *e = DecisionTypeCancelWorkflowExecution return nil case "REQUESTCANCELEXTERNALWORKFLOWEXECUTION": *e = DecisionTypeRequestCancelExternalWorkflowExecution return nil case "RECORDMARKER": *e = DecisionTypeRecordMarker return nil case "CONTINUEASNEWWORKFLOWEXECUTION": *e = DecisionTypeContinueAsNewWorkflowExecution return nil case "STARTCHILDWORKFLOWEXECUTION": *e = DecisionTypeStartChildWorkflowExecution return nil case "SIGNALEXTERNALWORKFLOWEXECUTION": *e = DecisionTypeSignalExternalWorkflowExecution return nil case "UPSERTWORKFLOWSEARCHATTRIBUTES": *e = DecisionTypeUpsertWorkflowSearchAttributes return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DecisionType", err) } *e = DecisionType(val) return nil } } // MarshalText encodes DecisionType to text. func (e DecisionType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // DecisionTypeScheduleActivityTask is an option for DecisionType DecisionTypeScheduleActivityTask DecisionType = iota // DecisionTypeRequestCancelActivityTask is an option for DecisionType DecisionTypeRequestCancelActivityTask // DecisionTypeStartTimer is an option for DecisionType DecisionTypeStartTimer // DecisionTypeCompleteWorkflowExecution is an option for DecisionType DecisionTypeCompleteWorkflowExecution // DecisionTypeFailWorkflowExecution is an option for DecisionType DecisionTypeFailWorkflowExecution // DecisionTypeCancelTimer is an option for DecisionType DecisionTypeCancelTimer // DecisionTypeCancelWorkflowExecution is an option for DecisionType DecisionTypeCancelWorkflowExecution // DecisionTypeRequestCancelExternalWorkflowExecution is an option for DecisionType DecisionTypeRequestCancelExternalWorkflowExecution // DecisionTypeRecordMarker is an option for DecisionType DecisionTypeRecordMarker // DecisionTypeContinueAsNewWorkflowExecution is an option for DecisionType DecisionTypeContinueAsNewWorkflowExecution // DecisionTypeStartChildWorkflowExecution is an option for DecisionType DecisionTypeStartChildWorkflowExecution // DecisionTypeSignalExternalWorkflowExecution is an option for DecisionType DecisionTypeSignalExternalWorkflowExecution // DecisionTypeUpsertWorkflowSearchAttributes is an option for DecisionType DecisionTypeUpsertWorkflowSearchAttributes ) // DeleteDomainRequest is an internal type (TBD...) type DeleteDomainRequest struct { Name string `json:"name,omitempty"` SecurityToken string `json:"securityToken,omitempty"` } // GetName is an internal getter (TBD...) func (v *DeleteDomainRequest) GetName() (o string) { if v != nil { return v.Name } return } // DeprecateDomainRequest is an internal type (TBD...) type DeprecateDomainRequest struct { Name string `json:"name,omitempty"` SecurityToken string `json:"securityToken,omitempty"` } // GetName is an internal getter (TBD...) func (v *DeprecateDomainRequest) GetName() (o string) { if v != nil { return v.Name } return } // DescribeDomainRequest is an internal type (TBD...) type DescribeDomainRequest struct { Name *string `json:"name,omitempty"` UUID *string `json:"uuid,omitempty"` } // GetName is an internal getter (TBD...) func (v *DescribeDomainRequest) GetName() (o string) { if v != nil && v.Name != nil { return *v.Name } return } // GetUUID is an internal getter (TBD...) func (v *DescribeDomainRequest) GetUUID() (o string) { if v != nil && v.UUID != nil { return *v.UUID } return } // DescribeDomainResponse is an internal type (TBD...) type DescribeDomainResponse struct { DomainInfo *DomainInfo `json:"domainInfo,omitempty"` Configuration *DomainConfiguration `json:"configuration,omitempty"` ReplicationConfiguration *DomainReplicationConfiguration `json:"replicationConfiguration,omitempty"` FailoverVersion int64 `json:"failoverVersion,omitempty"` IsGlobalDomain bool `json:"isGlobalDomain,omitempty"` FailoverInfo *FailoverInfo `json:"failoverInfo,omitempty"` } func (v *DomainReplicationConfiguration) GetActiveClusters() (o *ActiveClusters) { if v != nil && v.ActiveClusters != nil { return v.ActiveClusters } return } // GetDomainInfo is an internal getter (TBD...) func (v *DescribeDomainResponse) GetDomainInfo() (o *DomainInfo) { if v != nil && v.DomainInfo != nil { return v.DomainInfo } return } // GetFailoverVersion is an internal getter (TBD...) func (v *DescribeDomainResponse) GetFailoverVersion() (o int64) { if v != nil { return v.FailoverVersion } return } // GetIsGlobalDomain is an internal getter (TBD...) func (v *DescribeDomainResponse) GetIsGlobalDomain() (o bool) { if v != nil { return v.IsGlobalDomain } return } // GetFailoverInfo is an internal getter (TBD...) func (v *DescribeDomainResponse) GetFailoverInfo() (o *FailoverInfo) { if v != nil { return v.FailoverInfo } return } // FailoverDomainRequest is an internal type (TBD...) type FailoverDomainRequest struct { DomainName string `json:"domainName,omitempty"` DomainActiveClusterName *string `json:"domainActiveClusterName,omitempty"` ActiveClusters *ActiveClusters `json:"activeClusters,omitempty"` Reason *string `json:"reason,omitempty"` } func (v *FailoverDomainRequest) ToUpdateDomainRequest() *UpdateDomainRequest { if v == nil { return nil } return &UpdateDomainRequest{ Name: v.DomainName, ActiveClusterName: v.DomainActiveClusterName, ActiveClusters: v.ActiveClusters, FailoverReason: v.Reason, } } // GetDomainName is an internal getter (TBD...) func (v *FailoverDomainRequest) GetDomainName() (o string) { if v != nil { return v.DomainName } return } // GetDomainActiveClusterName is an internal getter (TBD...) func (v *FailoverDomainRequest) GetDomainActiveClusterName() (o string) { if v != nil && v.DomainActiveClusterName != nil { return *v.DomainActiveClusterName } return } // GetDomain is an internal getter (TBD...) func (v *FailoverDomainRequest) GetDomain() (o string) { if v != nil { return v.DomainName } return } // FailoverDomainResponse is an internal type (TBD...) type FailoverDomainResponse struct { DomainInfo *DomainInfo `json:"domainInfo,omitempty"` Configuration *DomainConfiguration `json:"configuration,omitempty"` ReplicationConfiguration *DomainReplicationConfiguration `json:"replicationConfiguration,omitempty"` FailoverVersion int64 `json:"failoverVersion,omitempty"` IsGlobalDomain bool `json:"isGlobalDomain,omitempty"` } // GetDomainInfo is an internal getter (TBD...) func (v *FailoverDomainResponse) GetDomainInfo() (o *DomainInfo) { if v != nil && v.DomainInfo != nil { return v.DomainInfo } return } // GetConfiguration is an internal getter (TBD...) func (v *FailoverDomainResponse) GetConfiguration() (o *DomainConfiguration) { if v != nil && v.Configuration != nil { return v.Configuration } return } // GetReplicationConfiguration is an internal getter (TBD...) func (v *FailoverDomainResponse) GetReplicationConfiguration() (o *DomainReplicationConfiguration) { if v != nil && v.ReplicationConfiguration != nil { return v.ReplicationConfiguration } return } // GetFailoverVersion is an internal getter (TBD...) func (v *FailoverDomainResponse) GetFailoverVersion() (o int64) { if v != nil { return v.FailoverVersion } return } // GetIsGlobalDomain is an internal getter (TBD...) func (v *FailoverDomainResponse) GetIsGlobalDomain() (o bool) { if v != nil { return v.IsGlobalDomain } return } // ListFailoverHistoryRequest is an internal type (TBD...) type ListFailoverHistoryRequest struct { Filters *ListFailoverHistoryRequestFilters `json:"filters,omitempty"` Pagination *PaginationOptions `json:"pagination,omitempty"` } // GetFilters is an internal getter (TBD...) func (v *ListFailoverHistoryRequest) GetFilters() (o *ListFailoverHistoryRequestFilters) { if v != nil && v.Filters != nil { return v.Filters } return } // GetPagination is an internal getter (TBD...) func (v *ListFailoverHistoryRequest) GetPagination() (o *PaginationOptions) { if v != nil && v.Pagination != nil { return v.Pagination } return } // ListFailoverHistoryRequestFilters is an internal type (TBD...) type ListFailoverHistoryRequestFilters struct { DomainID string `json:"domainId,omitempty"` } // GetDomainID is an internal getter (TBD...) func (v *ListFailoverHistoryRequestFilters) GetDomainID() (o string) { if v != nil { return v.DomainID } return } // ListFailoverHistoryResponse is an internal type (TBD...) type ListFailoverHistoryResponse struct { FailoverEvents []*FailoverEvent `json:"failoverEvents,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetFailoverEvents is an internal getter (TBD...) func (v *ListFailoverHistoryResponse) GetFailoverEvents() (o []*FailoverEvent) { if v != nil && v.FailoverEvents != nil { return v.FailoverEvents } return } // GetNextPageToken is an internal getter (TBD...) func (v *ListFailoverHistoryResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // PaginationOptions is an internal type (TBD...) type PaginationOptions struct { PageSize *int32 `json:"pageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetPageSize is an internal getter (TBD...) func (v *PaginationOptions) GetPageSize() (o int32) { if v != nil && v.PageSize != nil { return *v.PageSize } return } // GetNextPageToken is an internal getter (TBD...) func (v *PaginationOptions) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // FailoverEvent is an internal type (TBD...) type FailoverEvent struct { ID *string `json:"id,omitempty"` CreatedTime *int64 `json:"createdTime,omitempty"` FailoverType *FailoverType `json:"failoverType,omitempty"` ClusterFailovers []*ClusterFailover `json:"clusterFailovers,omitempty"` } // GetID is an internal getter (TBD...) func (v *FailoverEvent) GetID() (o string) { if v != nil && v.ID != nil { return *v.ID } return } // GetCreatedTime is an internal getter (TBD...) func (v *FailoverEvent) GetCreatedTime() (o int64) { if v != nil && v.CreatedTime != nil { return *v.CreatedTime } return } // GetFailoverType is an internal getter (TBD...) func (v *FailoverEvent) GetFailoverType() (o FailoverType) { if v != nil && v.FailoverType != nil { return *v.FailoverType } return } // GetClusterFailovers is an internal getter (TBD...) func (v *FailoverEvent) GetClusterFailovers() (o []*ClusterFailover) { if v != nil && v.ClusterFailovers != nil { return v.ClusterFailovers } return } // FailoverType is an internal type (TBD...) type FailoverType int32 // this should line up with the IDL values in common.proto // ie FailoverType_FAILOVER_TYPE_FORCE = 1 // ie FailoverType_FAILOVER_TYPE_GRACEFUL = 2 const ( FailoverTypeInvalid = FailoverType(0) FailoverTypeForce = FailoverType(1) FailoverTypeGraceful = FailoverType(2) ) // Ptr is a helper routine that returns a pointer to given FailoverType value. func (e FailoverType) Ptr() *FailoverType { return &e } // ClusterFailover is an internal type (TBD...) type ClusterFailover struct { FromCluster *ActiveClusterInfo `json:"fromCluster,omitempty"` ToCluster *ActiveClusterInfo `json:"toCluster,omitempty"` ClusterAttribute *ClusterAttribute `json:"clusterAttribute,omitempty"` } // GetFromCluster is an internal getter (TBD...) func (v *ClusterFailover) GetFromCluster() (o *ActiveClusterInfo) { if v != nil && v.FromCluster != nil { return v.FromCluster } return } // GetToCluster is an internal getter (TBD...) func (v *ClusterFailover) GetToCluster() (o *ActiveClusterInfo) { if v != nil && v.ToCluster != nil { return v.ToCluster } return } // GetClusterAttribute is an internal getter (TBD...) func (v *ClusterFailover) GetClusterAttribute() (o *ClusterAttribute) { if v != nil && v.ClusterAttribute != nil { return v.ClusterAttribute } return } // DescribeHistoryHostRequest is an internal type (TBD...) type DescribeHistoryHostRequest struct { HostAddress *string `json:"hostAddress,omitempty"` ShardIDForHost *int32 `json:"shardIdForHost,omitempty"` ExecutionForHost *WorkflowExecution `json:"executionForHost,omitempty"` } // DescribeShardDistributionRequest is an internal type (TBD...) type DescribeShardDistributionRequest struct { PageSize int32 `json:"pageSize,omitempty"` PageID int32 `json:"pageID,omitempty"` } // GetHostAddress is an internal getter (TBD...) func (v *DescribeHistoryHostRequest) GetHostAddress() (o string) { if v != nil && v.HostAddress != nil { return *v.HostAddress } return } // GetShardIDForHost is an internal getter (TBD...) func (v *DescribeHistoryHostRequest) GetShardIDForHost() (o int32) { if v != nil && v.ShardIDForHost != nil { return *v.ShardIDForHost } return } // DescribeShardDistributionResponse is an internal type (TBD...) type DescribeShardDistributionResponse struct { NumberOfShards int32 `json:"numberOfShards,omitempty"` Shards map[int32]string `json:"shardIDs,omitempty"` } // DescribeHistoryHostResponse is an internal type (TBD...) type DescribeHistoryHostResponse struct { NumberOfShards int32 `json:"numberOfShards,omitempty"` ShardIDs []int32 `json:"shardIDs,omitempty"` DomainCache *DomainCacheInfo `json:"domainCache,omitempty"` ShardControllerStatus string `json:"shardControllerStatus,omitempty"` Address string `json:"address,omitempty"` } // DescribeQueueRequest is an internal type (TBD...) type DescribeQueueRequest struct { ShardID int32 `json:"shardID,omitempty"` ClusterName string `json:"clusterName,omitempty"` Type *int32 `json:"type,omitempty"` } // GetShardID is an internal getter (TBD...) func (v *DescribeQueueRequest) GetShardID() (o int32) { if v != nil { return v.ShardID } return } // GetClusterName is an internal getter (TBD...) func (v *DescribeQueueRequest) GetClusterName() (o string) { if v != nil { return v.ClusterName } return } // GetType is an internal getter (TBD...) func (v *DescribeQueueRequest) GetType() (o int32) { if v != nil && v.Type != nil { return *v.Type } return } // DescribeQueueResponse is an internal type (TBD...) type DescribeQueueResponse struct { ProcessingQueueStates []string `json:"processingQueueStates,omitempty"` } // DescribeTaskListRequest is an internal type (TBD...) type DescribeTaskListRequest struct { Domain string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` TaskListType *TaskListType `json:"taskListType,omitempty"` IncludeTaskListStatus bool `json:"includeTaskListStatus,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *DescribeTaskListRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetTaskList is an internal getter (TBD...) func (v *DescribeTaskListRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetTaskListType is an internal getter (TBD...) func (v *DescribeTaskListRequest) GetTaskListType() (o TaskListType) { if v != nil && v.TaskListType != nil { return *v.TaskListType } return } // GetIncludeTaskListStatus is an internal getter (TBD...) func (v *DescribeTaskListRequest) GetIncludeTaskListStatus() (o bool) { if v != nil { return v.IncludeTaskListStatus } return } // DescribeTaskListResponse is an internal type (TBD...) type DescribeTaskListResponse struct { Pollers []*PollerInfo `json:"pollers,omitempty"` TaskListStatus *TaskListStatus `json:"taskListStatus,omitempty"` PartitionConfig *TaskListPartitionConfig `json:"partitionConfig,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` } // GetPollers is an internal getter (TBD...) func (v *DescribeTaskListResponse) GetPollers() (o []*PollerInfo) { if v != nil && v.Pollers != nil { return v.Pollers } return } // GetTaskListStatus is an internal getter (TBD...) func (v *DescribeTaskListResponse) GetTaskListStatus() (o *TaskListStatus) { if v != nil && v.TaskListStatus != nil { return v.TaskListStatus } return } // DescribeWorkflowExecutionRequest is an internal type (TBD...) type DescribeWorkflowExecutionRequest struct { Domain string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` QueryConsistencyLevel *QueryConsistencyLevel `json:"queryConsistencyLevel,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *DescribeWorkflowExecutionRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetExecution is an internal getter (TBD...) func (v *DescribeWorkflowExecutionRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // GetQueryConsistencyLevel is an internal getter (TBD...) func (v *DescribeWorkflowExecutionRequest) GetQueryConsistencyLevel() (o QueryConsistencyLevel) { if v != nil && v.QueryConsistencyLevel != nil { return *v.QueryConsistencyLevel } return } // DescribeWorkflowExecutionResponse is an internal type (TBD...) type DescribeWorkflowExecutionResponse struct { ExecutionConfiguration *WorkflowExecutionConfiguration `json:"executionConfiguration,omitempty"` WorkflowExecutionInfo *WorkflowExecutionInfo `json:"workflowExecutionInfo,omitempty"` PendingActivities []*PendingActivityInfo `json:"pendingActivities,omitempty"` PendingChildren []*PendingChildExecutionInfo `json:"pendingChildren,omitempty"` PendingDecision *PendingDecisionInfo `json:"pendingDecision,omitempty"` } // GetWorkflowExecutionInfo is an internal getter (TBD...) func (v *DescribeWorkflowExecutionResponse) GetWorkflowExecutionInfo() (o *WorkflowExecutionInfo) { if v != nil && v.WorkflowExecutionInfo != nil { return v.WorkflowExecutionInfo } return } // GetPendingActivities is an internal getter (TBD...) func (v *DescribeWorkflowExecutionResponse) GetPendingActivities() (o []*PendingActivityInfo) { if v != nil && v.PendingActivities != nil { return v.PendingActivities } return } // DomainAlreadyExistsError is an internal type (TBD...) type DomainAlreadyExistsError struct { Message string `json:"message,required"` } // DomainCacheInfo is an internal type (TBD...) type DomainCacheInfo struct { NumOfItemsInCacheByID int64 `json:"numOfItemsInCacheByID,omitempty"` NumOfItemsInCacheByName int64 `json:"numOfItemsInCacheByName,omitempty"` } // DomainConfiguration is an internal type (TBD...) type DomainConfiguration struct { WorkflowExecutionRetentionPeriodInDays int32 `json:"workflowExecutionRetentionPeriodInDays,omitempty"` EmitMetric bool `json:"emitMetric,omitempty"` BadBinaries *BadBinaries `json:"badBinaries,omitempty"` HistoryArchivalStatus *ArchivalStatus `json:"historyArchivalStatus,omitempty"` HistoryArchivalURI string `json:"historyArchivalURI,omitempty"` VisibilityArchivalStatus *ArchivalStatus `json:"visibilityArchivalStatus,omitempty"` VisibilityArchivalURI string `json:"visibilityArchivalURI,omitempty"` IsolationGroups *IsolationGroupConfiguration `json:"isolationGroupConfiguration,omitempty"` AsyncWorkflowConfig *AsyncWorkflowConfiguration `json:"asyncWorkflowConfiguration,omitempty"` } // GetWorkflowExecutionRetentionPeriodInDays is an internal getter (TBD...) func (v *DomainConfiguration) GetWorkflowExecutionRetentionPeriodInDays() (o int32) { if v != nil { return v.WorkflowExecutionRetentionPeriodInDays } return } // GetEmitMetric is an internal getter (TBD...) func (v *DomainConfiguration) GetEmitMetric() (o bool) { if v != nil { return v.EmitMetric } return } // GetBadBinaries is an internal getter (TBD...) func (v *DomainConfiguration) GetBadBinaries() (o *BadBinaries) { if v != nil && v.BadBinaries != nil { return v.BadBinaries } return } // GetHistoryArchivalStatus is an internal getter (TBD...) func (v *DomainConfiguration) GetHistoryArchivalStatus() (o ArchivalStatus) { if v != nil && v.HistoryArchivalStatus != nil { return *v.HistoryArchivalStatus } return } // GetHistoryArchivalURI is an internal getter (TBD...) func (v *DomainConfiguration) GetHistoryArchivalURI() (o string) { if v != nil { return v.HistoryArchivalURI } return } // GetVisibilityArchivalStatus is an internal getter (TBD...) func (v *DomainConfiguration) GetVisibilityArchivalStatus() (o ArchivalStatus) { if v != nil && v.VisibilityArchivalStatus != nil { return *v.VisibilityArchivalStatus } return } // GetVisibilityArchivalURI is an internal getter (TBD...) func (v *DomainConfiguration) GetVisibilityArchivalURI() (o string) { if v != nil { return v.VisibilityArchivalURI } return } // GetIsolationGroupsConfiguration is an internal getter (TBD...) func (v *DomainConfiguration) GetIsolationGroupsConfiguration() IsolationGroupConfiguration { if v.IsolationGroups != nil { return *v.IsolationGroups } return nil } func (v *DomainConfiguration) GetAsyncWorkflowConfiguration() AsyncWorkflowConfiguration { if v.AsyncWorkflowConfig != nil { return *v.AsyncWorkflowConfig } return AsyncWorkflowConfiguration{} } // ByteSize returns the approximate memory used in bytes func (v *DomainConfiguration) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += v.BadBinaries.ByteSize() size += v.HistoryArchivalStatus.ByteSize() size += uint64(len(v.HistoryArchivalURI)) size += v.VisibilityArchivalStatus.ByteSize() size += uint64(len(v.VisibilityArchivalURI)) size += v.IsolationGroups.ByteSize() size += v.AsyncWorkflowConfig.ByteSize() return size } // DomainInfo is an internal type (TBD...) type DomainInfo struct { Name string `json:"name,omitempty"` Status *DomainStatus `json:"status,omitempty"` Description string `json:"description,omitempty"` OwnerEmail string `json:"ownerEmail,omitempty"` Data map[string]string `json:"data,omitempty"` UUID string `json:"uuid,omitempty"` } // GetName is an internal getter (TBD...) func (v *DomainInfo) GetName() (o string) { if v != nil { return v.Name } return } // GetStatus is an internal getter (TBD...) func (v *DomainInfo) GetStatus() (o DomainStatus) { if v != nil && v.Status != nil { return *v.Status } return } // GetDescription is an internal getter (TBD...) func (v *DomainInfo) GetDescription() (o string) { if v != nil { return v.Description } return } // GetOwnerEmail is an internal getter (TBD...) func (v *DomainInfo) GetOwnerEmail() (o string) { if v != nil { return v.OwnerEmail } return } // GetData is an internal getter (TBD...) func (v *DomainInfo) GetData() (o map[string]string) { if v != nil && v.Data != nil { return v.Data } return } // GetUUID is an internal getter (TBD...) func (v *DomainInfo) GetUUID() (o string) { if v != nil { return v.UUID } return } // ByteSize returns the approximate memory used in bytes func (v *DomainInfo) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += uint64(len(v.Name)) size += v.Status.ByteSize() size += uint64(len(v.Description)) size += uint64(len(v.OwnerEmail)) for k, val := range v.Data { size += uint64(len(k)) size += uint64(len(val)) } size += uint64(len(v.UUID)) return size } // DomainNotActiveError is an internal type. // this is a retriable error and *must* be retried under at least // some circumstances due to domain failover races. // TODO(c-warren): Move to common/types/errors.go type DomainNotActiveError struct { Message string `json:"message"` DomainName string `json:"domainName"` CurrentCluster string `json:"currentCluster"` ActiveCluster string `json:"activeCluster,omitempty"` ActiveClusters []string `json:"activeClusters,omitempty"` } // GetCurrentCluster is an internal getter (TBD...) func (v *DomainNotActiveError) GetCurrentCluster() (o string) { if v != nil { return v.CurrentCluster } return } // GetActiveCluster is an internal getter (TBD...) func (v *DomainNotActiveError) GetActiveCluster() (o string) { if v != nil { return v.ActiveCluster } return } // GetActiveClusters is an internal getter (TBD...) func (v *DomainNotActiveError) GetActiveClusters() (o []string) { if v != nil { return v.ActiveClusters } return } // DomainReplicationConfiguration is an internal type (TBD...) type DomainReplicationConfiguration struct { ActiveClusterName string `json:"activeClusterName,omitempty"` Clusters []*ClusterReplicationConfiguration `json:"clusters,omitempty"` ActiveClusters *ActiveClusters `json:"activeClusters,omitempty"` } func (v *DomainReplicationConfiguration) IsActiveActive() bool { if v == nil || v.ActiveClusters == nil { return false } // Check to see if a ClusterAttribute has been configured for this domain. if len(v.ActiveClusters.AttributeScopes) > 0 { for _, scope := range v.ActiveClusters.AttributeScopes { if len(scope.ClusterAttributes) > 0 { return true } } } return false } // GetActiveClusterName is an internal getter (TBD...) func (v *DomainReplicationConfiguration) GetActiveClusterName() (o string) { if v != nil { return v.ActiveClusterName } return } // GetClusters is an internal getter (TBD...) func (v *DomainReplicationConfiguration) GetClusters() (o []*ClusterReplicationConfiguration) { if v != nil && v.Clusters != nil { return v.Clusters } return } // ByteSize returns the approximate memory used in bytes func (v *DomainReplicationConfiguration) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += uint64(len(v.ActiveClusterName)) size += uint64(len(v.Clusters)) * uint64(unsafe.Sizeof((*ClusterReplicationConfiguration)(nil))) for _, e := range v.Clusters { size += e.ByteSize() } size += v.ActiveClusters.ByteSize() return size } // ActiveClusters defines the mapping of cluster attributes to their active cluster. // It contains a list of scopes, each of which contains a list of attribute to cluster mappings. // The following example uses 'cityID' as the attribute scope, and cities as the attributes names: // ClusterAttributes allow groups of workflows to be processed by different clusters within a domain. // // ActiveClusters = { // "cityID": { // any attribute scope, e.g a region, datacenter, city, etc. // "seattle": { // "activeClusterName": "cluster1", // "failoverVersion": 100, // }, // "san_francisco": { // "activeClusterName": "cluster2", // "failoverVersion": 200, // }, // }, // } // // TODO(c-warren): Rename to ClusterAttributes type ActiveClusters struct { // ClusterAttributes // Keyed by a scope type (e.g region, datacenter, city, etc.). // The value is a ClusterAttributeScope - a map of unique names (e.g seattle, san_francisco, etc.) to an ActiveClusterInfo. AttributeScopes map[string]ClusterAttributeScope `json:"attributeScopes,omitempty" yaml:"attributeScopes,omitempty"` } type ClusterAttributeNotFoundError struct { ScopeType string AttributeName string } func (e *ClusterAttributeNotFoundError) Error() string { return fmt.Sprintf("cluster attribute %s not found in scope %s", e.AttributeName, e.ScopeType) } // UndefinedFailoverVersion is used to indicate that a failover version that is not defined. // Failover versions are valid for Int64 >= 0 // and, but implication, 0 is a valid failover version. To distinguish between it and an undefined // failover version, we use a negative value. // todo (david.porter) move this to constants package and give it a type const UndefinedFailoverVersion = int64(-1) // GetFailoverVersionForAttribute returns the failover version for a given attribute. // if a value is not found it returns -1 and an error func (v *ActiveClusters) GetFailoverVersionForAttribute(scopeType, attributeName string) (int64, error) { if v == nil { return UndefinedFailoverVersion, &ClusterAttributeNotFoundError{ ScopeType: scopeType, AttributeName: attributeName, } } scope, ok := v.AttributeScopes[scopeType] if !ok { return UndefinedFailoverVersion, &ClusterAttributeNotFoundError{ ScopeType: scopeType, AttributeName: attributeName, } } info, ok := scope.ClusterAttributes[attributeName] if !ok { return UndefinedFailoverVersion, &ClusterAttributeNotFoundError{ ScopeType: scopeType, AttributeName: attributeName, } } return info.FailoverVersion, nil } func (v *ActiveClusters) GetAttributeScopes() map[string]ClusterAttributeScope { if v != nil && v.AttributeScopes != nil { return v.AttributeScopes } return nil } // TODO(c-warren): Move to common/types/errors.go? var ( ErrActiveClusterInfoNotFound = errors.New("active cluster info not found") ErrDomainNotActiveActive = errors.New("domain is not configured for active-active") ) // GetActiveClusterByClusterAttribute retrieves the ActiveClusterInfo for a given cluster attribute. // An attribute is a scope, name pair (e.g. region, dca or city, tokyo). // Returns ActiveCluster and FailoverVersion if found, otherwise returns an error. func (v *ActiveClusters) GetActiveClusterByClusterAttribute(scopeType, attributeName string) (ActiveClusterInfo, error) { if v == nil { return ActiveClusterInfo{}, ErrDomainNotActiveActive } if scopeType == "" || attributeName == "" { return ActiveClusterInfo{}, fmt.Errorf("scopeType or attributeName is empty") } scope, ok := v.AttributeScopes[scopeType] if !ok { return ActiveClusterInfo{}, fmt.Errorf("scopeType not found %s: %w", scopeType, ErrActiveClusterInfoNotFound) } return scope.GetActiveClusterByClusterAttribute(attributeName) } // GetAllClusters returns a sorted, deduplicated list of all attribute names from both // For the "region" scope, these are region names; for other scopes, these are the attribute names. func (v *ActiveClusters) GetAllClusters() []string { if v == nil { return []string{} } // Set of attribute names (e.g., region names for "region" scope) attributeNames := make(map[string]struct{}) // Collect attribute names from new format if v.AttributeScopes != nil { for _, scope := range v.AttributeScopes { for attributeName := range scope.ClusterAttributes { attributeNames[attributeName] = struct{}{} } } } // Convert to sorted slice result := make([]string, 0, len(attributeNames)) for name := range attributeNames { result = append(result, name) } // Sort for deterministic output sort.Strings(result) return result } // SetClusterForClusterAttribute sets the ActiveClusterInfo for a given cluster attribute. // If the receiver is nil, this method is a no-op. func (v *ActiveClusters) SetClusterForClusterAttribute(scopeType, attributeName string, info ActiveClusterInfo) error { if v == nil { return ErrDomainNotActiveActive } if scopeType == "" || attributeName == "" { return fmt.Errorf("scopeType or attributeName is empty") } if v.AttributeScopes == nil { v.AttributeScopes = make(map[string]ClusterAttributeScope) } scope, ok := v.AttributeScopes[scopeType] if !ok { scope = ClusterAttributeScope{ ClusterAttributes: make(map[string]ActiveClusterInfo), } } scope.ClusterAttributes[attributeName] = info v.AttributeScopes[scopeType] = scope return nil } // ByteSize returns the approximate memory used in bytes func (v *ActiveClusters) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) for k, scope := range v.AttributeScopes { size += uint64(len(k)) + scope.ByteSize() - uint64(unsafe.Sizeof(scope)) } return size } // ClusterAttributeScope is a map of unique attribute names to the active cluster for that attribute. // It can be used to determine the current failover version for a workflow associated with that attribute. type ClusterAttributeScope struct { ClusterAttributes map[string]ActiveClusterInfo `json:"clusterAttributes,omitempty" yaml:"clusterAttributes,omitempty"` } func (v *ClusterAttributeScope) GetActiveClusterByClusterAttribute(name string) (ActiveClusterInfo, error) { if v == nil { return ActiveClusterInfo{}, ErrActiveClusterInfoNotFound } clusterInfo, ok := v.ClusterAttributes[name] if !ok { return ActiveClusterInfo{}, fmt.Errorf("attribute not found %s: %w", name, ErrActiveClusterInfoNotFound) } return clusterInfo, nil } // ByteSize returns the approximate memory used in bytes func (v *ClusterAttributeScope) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) for k, val := range v.ClusterAttributes { // ByteSize implementation must match the logic in the reflection-based calculator used in the tests from common/types/test_util.go. // reflection-based calculator purposely ignores Go's internal map bucket/storage and treats each map element as // key: dynamic payload only (e.g., len(string)), no string header // value: dynamic payload only (e.g., for a struct, just its fields' dynamic payload), no struct header, no inline ints/bools size += uint64(len(k)) + val.ByteSize() - uint64(unsafe.Sizeof(val)) } return size } // ActiveClusterInfo defines failover information for a ClusterAttribute. type ActiveClusterInfo struct { ActiveClusterName string `json:"activeClusterName,omitempty" yaml:"activeClusterName,omitempty"` FailoverVersion int64 `json:"failoverVersion" yaml:"failoverVersion"` } func (v *ActiveClusterInfo) GetActiveClusterName() string { if v == nil { return "" } return v.ActiveClusterName } // ByteSize returns the approximate memory used in bytes func (v ActiveClusterInfo) ByteSize() uint64 { return uint64(unsafe.Sizeof(v)) + uint64(len(v.ActiveClusterName)) } func (v *ActiveClusters) DeepCopy() *ActiveClusters { if v == nil { return nil } result := &ActiveClusters{} if v.AttributeScopes != nil { result.AttributeScopes = make(map[string]ClusterAttributeScope, len(v.AttributeScopes)) for scopeType, scope := range v.AttributeScopes { copiedScope := ClusterAttributeScope{} if scope.ClusterAttributes != nil { copiedScope.ClusterAttributes = make(map[string]ActiveClusterInfo, len(scope.ClusterAttributes)) for attrName, attrInfo := range scope.ClusterAttributes { copiedScope.ClusterAttributes[attrName] = attrInfo } } result.AttributeScopes[scopeType] = copiedScope } } return result } type ClusterAttribute struct { Scope string `json:"scope,omitempty" yaml:"scope,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` } func (c *ClusterAttribute) GetScope() string { if c == nil { return "" } return c.Scope } func (c *ClusterAttribute) GetName() string { if c == nil { return "" } return c.Name } func (c *ClusterAttribute) Equals(other *ClusterAttribute) bool { if c == nil && other == nil { return true } if c == nil || other == nil { return false } return c.Scope == other.Scope && c.Name == other.Name } type ActiveClusterSelectionStrategy int32 const ( ActiveClusterSelectionStrategyRegionSticky ActiveClusterSelectionStrategy = iota ActiveClusterSelectionStrategyExternalEntity ) type ActiveClusterSelectionPolicy struct { ActiveClusterSelectionStrategy *ActiveClusterSelectionStrategy `json:"activeClusterSelectionStrategy,omitempty"` StickyRegion string `json:"stickyRegion,omitempty"` ExternalEntityType string `json:"externalEntityType,omitempty"` ExternalEntityKey string `json:"externalEntityKey,omitempty"` // TODO(active-active): Remove the fields above ClusterAttribute *ClusterAttribute `json:"clusterAttribute,omitempty" yaml:"clusterAttribute,omitempty"` } func (p *ActiveClusterSelectionPolicy) GetClusterAttribute() *ClusterAttribute { if p == nil { return nil } return p.ClusterAttribute } func (p *ActiveClusterSelectionPolicy) Equals(other *ActiveClusterSelectionPolicy) bool { if p == nil && other == nil { return true } if p == nil || other == nil { return false } return p.ClusterAttribute.Equals(other.ClusterAttribute) } func (e ActiveClusterSelectionStrategy) Ptr() *ActiveClusterSelectionStrategy { return &e } func (e ActiveClusterSelectionStrategy) String() string { switch e { case ActiveClusterSelectionStrategyRegionSticky: return "REGION_STICKY" case ActiveClusterSelectionStrategyExternalEntity: return "EXTERNAL_ENTITY" } return fmt.Sprintf("ActiveClusterSelectionStrategy(%d)", e) } func (e ActiveClusterSelectionStrategy) MarshalText() ([]byte, error) { return []byte(e.String()), nil } func (e *ActiveClusterSelectionStrategy) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "REGION_STICKY": *e = ActiveClusterSelectionStrategyRegionSticky return nil case "EXTERNAL_ENTITY": *e = ActiveClusterSelectionStrategyExternalEntity return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ActiveClusterSelectionStrategy", err) } *e = ActiveClusterSelectionStrategy(val) return nil } } // DomainStatus is an internal type (TBD...) type DomainStatus int32 // Ptr is a helper function for getting pointer value func (e DomainStatus) Ptr() *DomainStatus { return &e } // String returns a readable string representation of DomainStatus. func (e DomainStatus) String() string { w := int32(e) switch w { case 0: return "REGISTERED" case 1: return "DEPRECATED" case 2: return "DELETED" } return fmt.Sprintf("DomainStatus(%d)", w) } // UnmarshalText parses enum value from string representation func (e *DomainStatus) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "REGISTERED": *e = DomainStatusRegistered return nil case "DEPRECATED": *e = DomainStatusDeprecated return nil case "DELETED": *e = DomainStatusDeleted return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "DomainStatus", err) } *e = DomainStatus(val) return nil } } // MarshalText encodes DomainStatus to text. func (e DomainStatus) MarshalText() ([]byte, error) { return []byte(e.String()), nil } // ByteSize returns the approximate memory used in bytes func (e *DomainStatus) ByteSize() uint64 { if e == nil { return 0 } return uint64(unsafe.Sizeof(*e)) } const ( // DomainStatusRegistered is an option for DomainStatus DomainStatusRegistered DomainStatus = iota // DomainStatusDeprecated is an option for DomainStatus DomainStatusDeprecated // DomainStatusDeleted is an option for DomainStatus DomainStatusDeleted ) // EncodingType is an internal type (TBD...) type EncodingType int32 // Ptr is a helper function for getting pointer value func (e EncodingType) Ptr() *EncodingType { return &e } // String returns a readable string representation of EncodingType. func (e EncodingType) String() string { w := int32(e) switch w { case 0: return "ThriftRW" case 1: return "JSON" } return fmt.Sprintf("EncodingType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *EncodingType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "THRIFTRW": *e = EncodingTypeThriftRW return nil case "JSON": *e = EncodingTypeJSON return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "EncodingType", err) } *e = EncodingType(val) return nil } } // MarshalText encodes EncodingType to text. func (e EncodingType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // EncodingTypeThriftRW is an option for EncodingType EncodingTypeThriftRW EncodingType = iota // EncodingTypeJSON is an option for EncodingType EncodingTypeJSON ) // ByteSize returns the approximate memory used in bytes func (e *EncodingType) ByteSize() uint64 { if e == nil { return 0 } return uint64(unsafe.Sizeof(*e)) } // EntityNotExistsError is an internal type (TBD...) type EntityNotExistsError struct { Message string `json:"message,required"` CurrentCluster string `json:"currentCluster,omitempty"` ActiveCluster string `json:"activeCluster,omitempty"` ActiveClusters []string `json:"activeClusters,omitempty"` } // WorkflowExecutionAlreadyCompletedError is an internal type (TBD...) type WorkflowExecutionAlreadyCompletedError struct { Message string `json:"message,required"` } // EventType is an internal type (TBD...) type EventType int32 // Ptr is a helper function for getting pointer value func (e EventType) Ptr() *EventType { return &e } // String returns a readable string representation of EventType. func (e EventType) String() string { w := int32(e) switch w { case 0: return "WorkflowExecutionStarted" case 1: return "WorkflowExecutionCompleted" case 2: return "WorkflowExecutionFailed" case 3: return "WorkflowExecutionTimedOut" case 4: return "DecisionTaskScheduled" case 5: return "DecisionTaskStarted" case 6: return "DecisionTaskCompleted" case 7: return "DecisionTaskTimedOut" case 8: return "DecisionTaskFailed" case 9: return "ActivityTaskScheduled" case 10: return "ActivityTaskStarted" case 11: return "ActivityTaskCompleted" case 12: return "ActivityTaskFailed" case 13: return "ActivityTaskTimedOut" case 14: return "ActivityTaskCancelRequested" case 15: return "RequestCancelActivityTaskFailed" case 16: return "ActivityTaskCanceled" case 17: return "TimerStarted" case 18: return "TimerFired" case 19: return "CancelTimerFailed" case 20: return "TimerCanceled" case 21: return "WorkflowExecutionCancelRequested" case 22: return "WorkflowExecutionCanceled" case 23: return "RequestCancelExternalWorkflowExecutionInitiated" case 24: return "RequestCancelExternalWorkflowExecutionFailed" case 25: return "ExternalWorkflowExecutionCancelRequested" case 26: return "MarkerRecorded" case 27: return "WorkflowExecutionSignaled" case 28: return "WorkflowExecutionTerminated" case 29: return "WorkflowExecutionContinuedAsNew" case 30: return "StartChildWorkflowExecutionInitiated" case 31: return "StartChildWorkflowExecutionFailed" case 32: return "ChildWorkflowExecutionStarted" case 33: return "ChildWorkflowExecutionCompleted" case 34: return "ChildWorkflowExecutionFailed" case 35: return "ChildWorkflowExecutionCanceled" case 36: return "ChildWorkflowExecutionTimedOut" case 37: return "ChildWorkflowExecutionTerminated" case 38: return "SignalExternalWorkflowExecutionInitiated" case 39: return "SignalExternalWorkflowExecutionFailed" case 40: return "ExternalWorkflowExecutionSignaled" case 41: return "UpsertWorkflowSearchAttributes" } return fmt.Sprintf("EventType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *EventType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "WORKFLOWEXECUTIONSTARTED": *e = EventTypeWorkflowExecutionStarted return nil case "WORKFLOWEXECUTIONCOMPLETED": *e = EventTypeWorkflowExecutionCompleted return nil case "WORKFLOWEXECUTIONFAILED": *e = EventTypeWorkflowExecutionFailed return nil case "WORKFLOWEXECUTIONTIMEDOUT": *e = EventTypeWorkflowExecutionTimedOut return nil case "DECISIONTASKSCHEDULED": *e = EventTypeDecisionTaskScheduled return nil case "DECISIONTASKSTARTED": *e = EventTypeDecisionTaskStarted return nil case "DECISIONTASKCOMPLETED": *e = EventTypeDecisionTaskCompleted return nil case "DECISIONTASKTIMEDOUT": *e = EventTypeDecisionTaskTimedOut return nil case "DECISIONTASKFAILED": *e = EventTypeDecisionTaskFailed return nil case "ACTIVITYTASKSCHEDULED": *e = EventTypeActivityTaskScheduled return nil case "ACTIVITYTASKSTARTED": *e = EventTypeActivityTaskStarted return nil case "ACTIVITYTASKCOMPLETED": *e = EventTypeActivityTaskCompleted return nil case "ACTIVITYTASKFAILED": *e = EventTypeActivityTaskFailed return nil case "ACTIVITYTASKTIMEDOUT": *e = EventTypeActivityTaskTimedOut return nil case "ACTIVITYTASKCANCELREQUESTED": *e = EventTypeActivityTaskCancelRequested return nil case "REQUESTCANCELACTIVITYTASKFAILED": *e = EventTypeRequestCancelActivityTaskFailed return nil case "ACTIVITYTASKCANCELED": *e = EventTypeActivityTaskCanceled return nil case "TIMERSTARTED": *e = EventTypeTimerStarted return nil case "TIMERFIRED": *e = EventTypeTimerFired return nil case "CANCELTIMERFAILED": *e = EventTypeCancelTimerFailed return nil case "TIMERCANCELED": *e = EventTypeTimerCanceled return nil case "WORKFLOWEXECUTIONCANCELREQUESTED": *e = EventTypeWorkflowExecutionCancelRequested return nil case "WORKFLOWEXECUTIONCANCELED": *e = EventTypeWorkflowExecutionCanceled return nil case "REQUESTCANCELEXTERNALWORKFLOWEXECUTIONINITIATED": *e = EventTypeRequestCancelExternalWorkflowExecutionInitiated return nil case "REQUESTCANCELEXTERNALWORKFLOWEXECUTIONFAILED": *e = EventTypeRequestCancelExternalWorkflowExecutionFailed return nil case "EXTERNALWORKFLOWEXECUTIONCANCELREQUESTED": *e = EventTypeExternalWorkflowExecutionCancelRequested return nil case "MARKERRECORDED": *e = EventTypeMarkerRecorded return nil case "WORKFLOWEXECUTIONSIGNALED": *e = EventTypeWorkflowExecutionSignaled return nil case "WORKFLOWEXECUTIONTERMINATED": *e = EventTypeWorkflowExecutionTerminated return nil case "WORKFLOWEXECUTIONCONTINUEDASNEW": *e = EventTypeWorkflowExecutionContinuedAsNew return nil case "STARTCHILDWORKFLOWEXECUTIONINITIATED": *e = EventTypeStartChildWorkflowExecutionInitiated return nil case "STARTCHILDWORKFLOWEXECUTIONFAILED": *e = EventTypeStartChildWorkflowExecutionFailed return nil case "CHILDWORKFLOWEXECUTIONSTARTED": *e = EventTypeChildWorkflowExecutionStarted return nil case "CHILDWORKFLOWEXECUTIONCOMPLETED": *e = EventTypeChildWorkflowExecutionCompleted return nil case "CHILDWORKFLOWEXECUTIONFAILED": *e = EventTypeChildWorkflowExecutionFailed return nil case "CHILDWORKFLOWEXECUTIONCANCELED": *e = EventTypeChildWorkflowExecutionCanceled return nil case "CHILDWORKFLOWEXECUTIONTIMEDOUT": *e = EventTypeChildWorkflowExecutionTimedOut return nil case "CHILDWORKFLOWEXECUTIONTERMINATED": *e = EventTypeChildWorkflowExecutionTerminated return nil case "SIGNALEXTERNALWORKFLOWEXECUTIONINITIATED": *e = EventTypeSignalExternalWorkflowExecutionInitiated return nil case "SIGNALEXTERNALWORKFLOWEXECUTIONFAILED": *e = EventTypeSignalExternalWorkflowExecutionFailed return nil case "EXTERNALWORKFLOWEXECUTIONSIGNALED": *e = EventTypeExternalWorkflowExecutionSignaled return nil case "UPSERTWORKFLOWSEARCHATTRIBUTES": *e = EventTypeUpsertWorkflowSearchAttributes return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "EventType", err) } *e = EventType(val) return nil } } // MarshalText encodes EventType to text. func (e EventType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // EventTypeWorkflowExecutionStarted is an option for EventType EventTypeWorkflowExecutionStarted EventType = iota // EventTypeWorkflowExecutionCompleted is an option for EventType EventTypeWorkflowExecutionCompleted // EventTypeWorkflowExecutionFailed is an option for EventType EventTypeWorkflowExecutionFailed // EventTypeWorkflowExecutionTimedOut is an option for EventType EventTypeWorkflowExecutionTimedOut // EventTypeDecisionTaskScheduled is an option for EventType EventTypeDecisionTaskScheduled // EventTypeDecisionTaskStarted is an option for EventType EventTypeDecisionTaskStarted // EventTypeDecisionTaskCompleted is an option for EventType EventTypeDecisionTaskCompleted // EventTypeDecisionTaskTimedOut is an option for EventType EventTypeDecisionTaskTimedOut // EventTypeDecisionTaskFailed is an option for EventType EventTypeDecisionTaskFailed // EventTypeActivityTaskScheduled is an option for EventType EventTypeActivityTaskScheduled // EventTypeActivityTaskStarted is an option for EventType EventTypeActivityTaskStarted // EventTypeActivityTaskCompleted is an option for EventType EventTypeActivityTaskCompleted // EventTypeActivityTaskFailed is an option for EventType EventTypeActivityTaskFailed // EventTypeActivityTaskTimedOut is an option for EventType EventTypeActivityTaskTimedOut // EventTypeActivityTaskCancelRequested is an option for EventType EventTypeActivityTaskCancelRequested // EventTypeRequestCancelActivityTaskFailed is an option for EventType EventTypeRequestCancelActivityTaskFailed // EventTypeActivityTaskCanceled is an option for EventType EventTypeActivityTaskCanceled // EventTypeTimerStarted is an option for EventType EventTypeTimerStarted // EventTypeTimerFired is an option for EventType EventTypeTimerFired // EventTypeCancelTimerFailed is an option for EventType EventTypeCancelTimerFailed // EventTypeTimerCanceled is an option for EventType EventTypeTimerCanceled // EventTypeWorkflowExecutionCancelRequested is an option for EventType EventTypeWorkflowExecutionCancelRequested // EventTypeWorkflowExecutionCanceled is an option for EventType EventTypeWorkflowExecutionCanceled // EventTypeRequestCancelExternalWorkflowExecutionInitiated is an option for EventType EventTypeRequestCancelExternalWorkflowExecutionInitiated // EventTypeRequestCancelExternalWorkflowExecutionFailed is an option for EventType EventTypeRequestCancelExternalWorkflowExecutionFailed // EventTypeExternalWorkflowExecutionCancelRequested is an option for EventType EventTypeExternalWorkflowExecutionCancelRequested // EventTypeMarkerRecorded is an option for EventType EventTypeMarkerRecorded // EventTypeWorkflowExecutionSignaled is an option for EventType EventTypeWorkflowExecutionSignaled // EventTypeWorkflowExecutionTerminated is an option for EventType EventTypeWorkflowExecutionTerminated // EventTypeWorkflowExecutionContinuedAsNew is an option for EventType EventTypeWorkflowExecutionContinuedAsNew // EventTypeStartChildWorkflowExecutionInitiated is an option for EventType EventTypeStartChildWorkflowExecutionInitiated // EventTypeStartChildWorkflowExecutionFailed is an option for EventType EventTypeStartChildWorkflowExecutionFailed // EventTypeChildWorkflowExecutionStarted is an option for EventType EventTypeChildWorkflowExecutionStarted // EventTypeChildWorkflowExecutionCompleted is an option for EventType EventTypeChildWorkflowExecutionCompleted // EventTypeChildWorkflowExecutionFailed is an option for EventType EventTypeChildWorkflowExecutionFailed // EventTypeChildWorkflowExecutionCanceled is an option for EventType EventTypeChildWorkflowExecutionCanceled // EventTypeChildWorkflowExecutionTimedOut is an option for EventType EventTypeChildWorkflowExecutionTimedOut // EventTypeChildWorkflowExecutionTerminated is an option for EventType EventTypeChildWorkflowExecutionTerminated // EventTypeSignalExternalWorkflowExecutionInitiated is an option for EventType EventTypeSignalExternalWorkflowExecutionInitiated // EventTypeSignalExternalWorkflowExecutionFailed is an option for EventType EventTypeSignalExternalWorkflowExecutionFailed // EventTypeExternalWorkflowExecutionSignaled is an option for EventType EventTypeExternalWorkflowExecutionSignaled // EventTypeUpsertWorkflowSearchAttributes is an option for EventType EventTypeUpsertWorkflowSearchAttributes ) // ExternalWorkflowExecutionCancelRequestedEventAttributes is an internal type (TBD...) type ExternalWorkflowExecutionCancelRequestedEventAttributes struct { InitiatedEventID int64 `json:"initiatedEventId,omitempty"` Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` } // GetInitiatedEventID is an internal getter (TBD...) func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // GetDomain is an internal getter (TBD...) func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // Size returns the approximate memory used in bytes func (v *ExternalWorkflowExecutionCancelRequestedEventAttributes) ByteSize() uint64 { return 0 } // ExternalWorkflowExecutionSignaledEventAttributes is an internal type (TBD...) type ExternalWorkflowExecutionSignaledEventAttributes struct { InitiatedEventID int64 `json:"initiatedEventId,omitempty"` Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Control []byte `json:"control,omitempty"` } // GetInitiatedEventID is an internal getter (TBD...) func (v *ExternalWorkflowExecutionSignaledEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // GetDomain is an internal getter (TBD...) func (v *ExternalWorkflowExecutionSignaledEventAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // Size returns the approximate memory used in bytes func (v *ExternalWorkflowExecutionSignaledEventAttributes) ByteSize() uint64 { return 0 } // FailWorkflowExecutionDecisionAttributes is an internal type (TBD...) type FailWorkflowExecutionDecisionAttributes struct { Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` } // GetReason is an internal getter (TBD...) func (v *FailWorkflowExecutionDecisionAttributes) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // GetSearchAttributesResponse is an internal type (TBD...) type GetSearchAttributesResponse struct { Keys map[string]IndexedValueType `json:"keys,omitempty"` } // GetKeys is an internal getter (TBD...) func (v *GetSearchAttributesResponse) GetKeys() (o map[string]IndexedValueType) { if v != nil && v.Keys != nil { return v.Keys } return } // GetWorkflowExecutionHistoryRequest is an internal type (TBD...) type GetWorkflowExecutionHistoryRequest struct { Domain string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` MaximumPageSize int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` WaitForNewEvent bool `json:"waitForNewEvent,omitempty"` HistoryEventFilterType *HistoryEventFilterType `json:"HistoryEventFilterType,omitempty"` SkipArchival bool `json:"skipArchival,omitempty"` QueryConsistencyLevel *QueryConsistencyLevel `json:"queryConsistencyLevel,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *GetWorkflowExecutionHistoryRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetExecution is an internal getter (TBD...) func (v *GetWorkflowExecutionHistoryRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // GetMaximumPageSize is an internal getter (TBD...) func (v *GetWorkflowExecutionHistoryRequest) GetMaximumPageSize() (o int32) { if v != nil { return v.MaximumPageSize } return } // GetNextPageToken is an internal getter (TBD...) func (v *GetWorkflowExecutionHistoryRequest) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // GetWaitForNewEvent is an internal getter (TBD...) func (v *GetWorkflowExecutionHistoryRequest) GetWaitForNewEvent() (o bool) { if v != nil { return v.WaitForNewEvent } return } // GetHistoryEventFilterType is an internal getter (TBD...) func (v *GetWorkflowExecutionHistoryRequest) GetHistoryEventFilterType() (o HistoryEventFilterType) { if v != nil && v.HistoryEventFilterType != nil { return *v.HistoryEventFilterType } return } // GetSkipArchival is an internal getter (TBD...) func (v *GetWorkflowExecutionHistoryRequest) GetSkipArchival() (o bool) { if v != nil { return v.SkipArchival } return } // GetQueryConsistencyLevel is an internal getter (TBD...) func (v *GetWorkflowExecutionHistoryRequest) GetQueryConsistencyLevel() (o QueryConsistencyLevel) { if v != nil && v.QueryConsistencyLevel != nil { return *v.QueryConsistencyLevel } return } // GetWorkflowExecutionHistoryResponse is an internal type (TBD...) type GetWorkflowExecutionHistoryResponse struct { History *History `json:"history,omitempty"` RawHistory []*DataBlob `json:"rawHistory,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` Archived bool `json:"archived,omitempty"` } // GetHistory is an internal getter (TBD...) func (v *GetWorkflowExecutionHistoryResponse) GetHistory() (o *History) { if v != nil && v.History != nil { return v.History } return } // GetArchived is an internal getter (TBD...) func (v *GetWorkflowExecutionHistoryResponse) GetArchived() (o bool) { if v != nil { return v.Archived } return } // FailoverInfo is an internal type (TBD...) type FailoverInfo struct { FailoverVersion int64 `json:"failoverVersion,omitempty"` FailoverStartTimestamp int64 `json:"failoverStartTimestamp,omitempty"` FailoverExpireTimestamp int64 `json:"failoverExpireTimestamp,omitempty"` CompletedShardCount int32 `json:"completedShardCount,omitempty"` PendingShards []int32 `json:"pendingShards,omitempty"` } // GetFailoverVersion is an internal getter (TBD...) func (v *FailoverInfo) GetFailoverVersion() (o int64) { if v != nil { return v.FailoverVersion } return } // GetFailoverStartTimestamp is an internal getter (TBD...) func (v *FailoverInfo) GetFailoverStartTimestamp() (o int64) { if v != nil { return v.FailoverStartTimestamp } return } // GetFailoverExpireTimestamp is an internal getter (TBD...) func (v *FailoverInfo) GetFailoverExpireTimestamp() (o int64) { if v != nil { return v.FailoverExpireTimestamp } return } // GetCompletedShardCount is an internal getter (TBD...) func (v *FailoverInfo) GetCompletedShardCount() (o int32) { if v != nil { return v.CompletedShardCount } return } // GetPendingShards is an internal getter (TBD...) func (v *FailoverInfo) GetPendingShards() (o []int32) { if v != nil { return v.PendingShards } return } // Header is an internal type (TBD...) type Header struct { Fields map[string][]byte `json:"fields,omitempty"` } // History is an internal type (TBD...) type History struct { Events []*HistoryEvent `json:"events,omitempty"` } // GetEvents is an internal getter (TBD...) func (v *History) GetEvents() (o []*HistoryEvent) { if v != nil && v.Events != nil { return v.Events } return } // HistoryBranch is an internal type (TBD...) type HistoryBranch struct { TreeID string BranchID string Ancestors []*HistoryBranchRange } // HistoryBranchRange is an internal type (TBD...) type HistoryBranchRange struct { BranchID string BeginNodeID int64 EndNodeID int64 } // HistoryEvent is an internal type (TBD...) type HistoryEvent struct { ID int64 `json:"eventId,omitempty"` Timestamp *int64 `json:"timestamp,omitempty"` EventType *EventType `json:"eventType,omitempty"` Version int64 `json:"version,omitempty"` TaskID int64 `json:"taskId,omitempty"` WorkflowExecutionStartedEventAttributes *WorkflowExecutionStartedEventAttributes `json:"workflowExecutionStartedEventAttributes,omitempty"` WorkflowExecutionCompletedEventAttributes *WorkflowExecutionCompletedEventAttributes `json:"workflowExecutionCompletedEventAttributes,omitempty"` WorkflowExecutionFailedEventAttributes *WorkflowExecutionFailedEventAttributes `json:"workflowExecutionFailedEventAttributes,omitempty"` WorkflowExecutionTimedOutEventAttributes *WorkflowExecutionTimedOutEventAttributes `json:"workflowExecutionTimedOutEventAttributes,omitempty"` DecisionTaskScheduledEventAttributes *DecisionTaskScheduledEventAttributes `json:"decisionTaskScheduledEventAttributes,omitempty"` DecisionTaskStartedEventAttributes *DecisionTaskStartedEventAttributes `json:"decisionTaskStartedEventAttributes,omitempty"` DecisionTaskCompletedEventAttributes *DecisionTaskCompletedEventAttributes `json:"decisionTaskCompletedEventAttributes,omitempty"` DecisionTaskTimedOutEventAttributes *DecisionTaskTimedOutEventAttributes `json:"decisionTaskTimedOutEventAttributes,omitempty"` DecisionTaskFailedEventAttributes *DecisionTaskFailedEventAttributes `json:"decisionTaskFailedEventAttributes,omitempty"` ActivityTaskScheduledEventAttributes *ActivityTaskScheduledEventAttributes `json:"activityTaskScheduledEventAttributes,omitempty"` ActivityTaskStartedEventAttributes *ActivityTaskStartedEventAttributes `json:"activityTaskStartedEventAttributes,omitempty"` ActivityTaskCompletedEventAttributes *ActivityTaskCompletedEventAttributes `json:"activityTaskCompletedEventAttributes,omitempty"` ActivityTaskFailedEventAttributes *ActivityTaskFailedEventAttributes `json:"activityTaskFailedEventAttributes,omitempty"` ActivityTaskTimedOutEventAttributes *ActivityTaskTimedOutEventAttributes `json:"activityTaskTimedOutEventAttributes,omitempty"` TimerStartedEventAttributes *TimerStartedEventAttributes `json:"timerStartedEventAttributes,omitempty"` TimerFiredEventAttributes *TimerFiredEventAttributes `json:"timerFiredEventAttributes,omitempty"` ActivityTaskCancelRequestedEventAttributes *ActivityTaskCancelRequestedEventAttributes `json:"activityTaskCancelRequestedEventAttributes,omitempty"` RequestCancelActivityTaskFailedEventAttributes *RequestCancelActivityTaskFailedEventAttributes `json:"requestCancelActivityTaskFailedEventAttributes,omitempty"` ActivityTaskCanceledEventAttributes *ActivityTaskCanceledEventAttributes `json:"activityTaskCanceledEventAttributes,omitempty"` TimerCanceledEventAttributes *TimerCanceledEventAttributes `json:"timerCanceledEventAttributes,omitempty"` CancelTimerFailedEventAttributes *CancelTimerFailedEventAttributes `json:"cancelTimerFailedEventAttributes,omitempty"` MarkerRecordedEventAttributes *MarkerRecordedEventAttributes `json:"markerRecordedEventAttributes,omitempty"` WorkflowExecutionSignaledEventAttributes *WorkflowExecutionSignaledEventAttributes `json:"workflowExecutionSignaledEventAttributes,omitempty"` WorkflowExecutionTerminatedEventAttributes *WorkflowExecutionTerminatedEventAttributes `json:"workflowExecutionTerminatedEventAttributes,omitempty"` WorkflowExecutionCancelRequestedEventAttributes *WorkflowExecutionCancelRequestedEventAttributes `json:"workflowExecutionCancelRequestedEventAttributes,omitempty"` WorkflowExecutionCanceledEventAttributes *WorkflowExecutionCanceledEventAttributes `json:"workflowExecutionCanceledEventAttributes,omitempty"` RequestCancelExternalWorkflowExecutionInitiatedEventAttributes *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes `json:"requestCancelExternalWorkflowExecutionInitiatedEventAttributes,omitempty"` RequestCancelExternalWorkflowExecutionFailedEventAttributes *RequestCancelExternalWorkflowExecutionFailedEventAttributes `json:"requestCancelExternalWorkflowExecutionFailedEventAttributes,omitempty"` ExternalWorkflowExecutionCancelRequestedEventAttributes *ExternalWorkflowExecutionCancelRequestedEventAttributes `json:"externalWorkflowExecutionCancelRequestedEventAttributes,omitempty"` WorkflowExecutionContinuedAsNewEventAttributes *WorkflowExecutionContinuedAsNewEventAttributes `json:"workflowExecutionContinuedAsNewEventAttributes,omitempty"` StartChildWorkflowExecutionInitiatedEventAttributes *StartChildWorkflowExecutionInitiatedEventAttributes `json:"startChildWorkflowExecutionInitiatedEventAttributes,omitempty"` StartChildWorkflowExecutionFailedEventAttributes *StartChildWorkflowExecutionFailedEventAttributes `json:"startChildWorkflowExecutionFailedEventAttributes,omitempty"` ChildWorkflowExecutionStartedEventAttributes *ChildWorkflowExecutionStartedEventAttributes `json:"childWorkflowExecutionStartedEventAttributes,omitempty"` ChildWorkflowExecutionCompletedEventAttributes *ChildWorkflowExecutionCompletedEventAttributes `json:"childWorkflowExecutionCompletedEventAttributes,omitempty"` ChildWorkflowExecutionFailedEventAttributes *ChildWorkflowExecutionFailedEventAttributes `json:"childWorkflowExecutionFailedEventAttributes,omitempty"` ChildWorkflowExecutionCanceledEventAttributes *ChildWorkflowExecutionCanceledEventAttributes `json:"childWorkflowExecutionCanceledEventAttributes,omitempty"` ChildWorkflowExecutionTimedOutEventAttributes *ChildWorkflowExecutionTimedOutEventAttributes `json:"childWorkflowExecutionTimedOutEventAttributes,omitempty"` ChildWorkflowExecutionTerminatedEventAttributes *ChildWorkflowExecutionTerminatedEventAttributes `json:"childWorkflowExecutionTerminatedEventAttributes,omitempty"` SignalExternalWorkflowExecutionInitiatedEventAttributes *SignalExternalWorkflowExecutionInitiatedEventAttributes `json:"signalExternalWorkflowExecutionInitiatedEventAttributes,omitempty"` SignalExternalWorkflowExecutionFailedEventAttributes *SignalExternalWorkflowExecutionFailedEventAttributes `json:"signalExternalWorkflowExecutionFailedEventAttributes,omitempty"` ExternalWorkflowExecutionSignaledEventAttributes *ExternalWorkflowExecutionSignaledEventAttributes `json:"externalWorkflowExecutionSignaledEventAttributes,omitempty"` UpsertWorkflowSearchAttributesEventAttributes *UpsertWorkflowSearchAttributesEventAttributes `json:"upsertWorkflowSearchAttributesEventAttributes,omitempty"` } // GetTimestamp is an internal getter (TBD...) func (v *HistoryEvent) GetTimestamp() (o int64) { if v != nil && v.Timestamp != nil { return *v.Timestamp } return } // GetEventType is an internal getter (TBD...) func (v *HistoryEvent) GetEventType() (o EventType) { if v != nil && v.EventType != nil { return *v.EventType } return } // GetWorkflowExecutionStartedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetWorkflowExecutionStartedEventAttributes() (o *WorkflowExecutionStartedEventAttributes) { if v != nil && v.WorkflowExecutionStartedEventAttributes != nil { return v.WorkflowExecutionStartedEventAttributes } return } // GetWorkflowExecutionCompletedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetWorkflowExecutionCompletedEventAttributes() (o *WorkflowExecutionCompletedEventAttributes) { if v != nil && v.WorkflowExecutionCompletedEventAttributes != nil { return v.WorkflowExecutionCompletedEventAttributes } return } // GetWorkflowExecutionFailedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetWorkflowExecutionFailedEventAttributes() (o *WorkflowExecutionFailedEventAttributes) { if v != nil && v.WorkflowExecutionFailedEventAttributes != nil { return v.WorkflowExecutionFailedEventAttributes } return } // GetWorkflowExecutionTimedOutEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetWorkflowExecutionTimedOutEventAttributes() (o *WorkflowExecutionTimedOutEventAttributes) { if v != nil && v.WorkflowExecutionTimedOutEventAttributes != nil { return v.WorkflowExecutionTimedOutEventAttributes } return } // GetDecisionTaskScheduledEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetDecisionTaskScheduledEventAttributes() (o *DecisionTaskScheduledEventAttributes) { if v != nil && v.DecisionTaskScheduledEventAttributes != nil { return v.DecisionTaskScheduledEventAttributes } return } // GetDecisionTaskStartedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetDecisionTaskStartedEventAttributes() (o *DecisionTaskStartedEventAttributes) { if v != nil && v.DecisionTaskStartedEventAttributes != nil { return v.DecisionTaskStartedEventAttributes } return } // GetDecisionTaskCompletedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetDecisionTaskCompletedEventAttributes() (o *DecisionTaskCompletedEventAttributes) { if v != nil && v.DecisionTaskCompletedEventAttributes != nil { return v.DecisionTaskCompletedEventAttributes } return } // GetDecisionTaskTimedOutEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetDecisionTaskTimedOutEventAttributes() (o *DecisionTaskTimedOutEventAttributes) { if v != nil && v.DecisionTaskTimedOutEventAttributes != nil { return v.DecisionTaskTimedOutEventAttributes } return } // GetDecisionTaskFailedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetDecisionTaskFailedEventAttributes() (o *DecisionTaskFailedEventAttributes) { if v != nil && v.DecisionTaskFailedEventAttributes != nil { return v.DecisionTaskFailedEventAttributes } return } // GetActivityTaskScheduledEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetActivityTaskScheduledEventAttributes() (o *ActivityTaskScheduledEventAttributes) { if v != nil && v.ActivityTaskScheduledEventAttributes != nil { return v.ActivityTaskScheduledEventAttributes } return } // GetActivityTaskStartedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetActivityTaskStartedEventAttributes() (o *ActivityTaskStartedEventAttributes) { if v != nil && v.ActivityTaskStartedEventAttributes != nil { return v.ActivityTaskStartedEventAttributes } return } // GetActivityTaskCompletedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetActivityTaskCompletedEventAttributes() (o *ActivityTaskCompletedEventAttributes) { if v != nil && v.ActivityTaskCompletedEventAttributes != nil { return v.ActivityTaskCompletedEventAttributes } return } // GetActivityTaskFailedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetActivityTaskFailedEventAttributes() (o *ActivityTaskFailedEventAttributes) { if v != nil && v.ActivityTaskFailedEventAttributes != nil { return v.ActivityTaskFailedEventAttributes } return } // GetActivityTaskTimedOutEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetActivityTaskTimedOutEventAttributes() (o *ActivityTaskTimedOutEventAttributes) { if v != nil && v.ActivityTaskTimedOutEventAttributes != nil { return v.ActivityTaskTimedOutEventAttributes } return } // GetTimerStartedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetTimerStartedEventAttributes() (o *TimerStartedEventAttributes) { if v != nil && v.TimerStartedEventAttributes != nil { return v.TimerStartedEventAttributes } return } // GetTimerFiredEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetTimerFiredEventAttributes() (o *TimerFiredEventAttributes) { if v != nil && v.TimerFiredEventAttributes != nil { return v.TimerFiredEventAttributes } return } // GetActivityTaskCancelRequestedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetActivityTaskCancelRequestedEventAttributes() (o *ActivityTaskCancelRequestedEventAttributes) { if v != nil && v.ActivityTaskCancelRequestedEventAttributes != nil { return v.ActivityTaskCancelRequestedEventAttributes } return } // GetRequestCancelActivityTaskFailedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetRequestCancelActivityTaskFailedEventAttributes() (o *RequestCancelActivityTaskFailedEventAttributes) { if v != nil && v.RequestCancelActivityTaskFailedEventAttributes != nil { return v.RequestCancelActivityTaskFailedEventAttributes } return } // GetActivityTaskCanceledEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetActivityTaskCanceledEventAttributes() (o *ActivityTaskCanceledEventAttributes) { if v != nil && v.ActivityTaskCanceledEventAttributes != nil { return v.ActivityTaskCanceledEventAttributes } return } // GetTimerCanceledEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetTimerCanceledEventAttributes() (o *TimerCanceledEventAttributes) { if v != nil && v.TimerCanceledEventAttributes != nil { return v.TimerCanceledEventAttributes } return } // GetCancelTimerFailedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetCancelTimerFailedEventAttributes() (o *CancelTimerFailedEventAttributes) { if v != nil && v.CancelTimerFailedEventAttributes != nil { return v.CancelTimerFailedEventAttributes } return } // GetMarkerRecordedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetMarkerRecordedEventAttributes() (o *MarkerRecordedEventAttributes) { if v != nil && v.MarkerRecordedEventAttributes != nil { return v.MarkerRecordedEventAttributes } return } // GetWorkflowExecutionSignaledEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetWorkflowExecutionSignaledEventAttributes() (o *WorkflowExecutionSignaledEventAttributes) { if v != nil && v.WorkflowExecutionSignaledEventAttributes != nil { return v.WorkflowExecutionSignaledEventAttributes } return } // GetWorkflowExecutionTerminatedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetWorkflowExecutionTerminatedEventAttributes() (o *WorkflowExecutionTerminatedEventAttributes) { if v != nil && v.WorkflowExecutionTerminatedEventAttributes != nil { return v.WorkflowExecutionTerminatedEventAttributes } return } // GetWorkflowExecutionCancelRequestedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetWorkflowExecutionCancelRequestedEventAttributes() (o *WorkflowExecutionCancelRequestedEventAttributes) { if v != nil && v.WorkflowExecutionCancelRequestedEventAttributes != nil { return v.WorkflowExecutionCancelRequestedEventAttributes } return } // GetWorkflowExecutionCanceledEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetWorkflowExecutionCanceledEventAttributes() (o *WorkflowExecutionCanceledEventAttributes) { if v != nil && v.WorkflowExecutionCanceledEventAttributes != nil { return v.WorkflowExecutionCanceledEventAttributes } return } // GetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetRequestCancelExternalWorkflowExecutionInitiatedEventAttributes() (o *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) { if v != nil && v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes != nil { return v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes } return } // GetRequestCancelExternalWorkflowExecutionFailedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetRequestCancelExternalWorkflowExecutionFailedEventAttributes() (o *RequestCancelExternalWorkflowExecutionFailedEventAttributes) { if v != nil && v.RequestCancelExternalWorkflowExecutionFailedEventAttributes != nil { return v.RequestCancelExternalWorkflowExecutionFailedEventAttributes } return } // GetExternalWorkflowExecutionCancelRequestedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetExternalWorkflowExecutionCancelRequestedEventAttributes() (o *ExternalWorkflowExecutionCancelRequestedEventAttributes) { if v != nil && v.ExternalWorkflowExecutionCancelRequestedEventAttributes != nil { return v.ExternalWorkflowExecutionCancelRequestedEventAttributes } return } // GetWorkflowExecutionContinuedAsNewEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetWorkflowExecutionContinuedAsNewEventAttributes() (o *WorkflowExecutionContinuedAsNewEventAttributes) { if v != nil && v.WorkflowExecutionContinuedAsNewEventAttributes != nil { return v.WorkflowExecutionContinuedAsNewEventAttributes } return } // GetStartChildWorkflowExecutionInitiatedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetStartChildWorkflowExecutionInitiatedEventAttributes() (o *StartChildWorkflowExecutionInitiatedEventAttributes) { if v != nil && v.StartChildWorkflowExecutionInitiatedEventAttributes != nil { return v.StartChildWorkflowExecutionInitiatedEventAttributes } return } // GetStartChildWorkflowExecutionFailedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetStartChildWorkflowExecutionFailedEventAttributes() (o *StartChildWorkflowExecutionFailedEventAttributes) { if v != nil && v.StartChildWorkflowExecutionFailedEventAttributes != nil { return v.StartChildWorkflowExecutionFailedEventAttributes } return } // GetChildWorkflowExecutionStartedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetChildWorkflowExecutionStartedEventAttributes() (o *ChildWorkflowExecutionStartedEventAttributes) { if v != nil && v.ChildWorkflowExecutionStartedEventAttributes != nil { return v.ChildWorkflowExecutionStartedEventAttributes } return } // GetChildWorkflowExecutionCompletedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetChildWorkflowExecutionCompletedEventAttributes() (o *ChildWorkflowExecutionCompletedEventAttributes) { if v != nil && v.ChildWorkflowExecutionCompletedEventAttributes != nil { return v.ChildWorkflowExecutionCompletedEventAttributes } return } // GetChildWorkflowExecutionFailedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetChildWorkflowExecutionFailedEventAttributes() (o *ChildWorkflowExecutionFailedEventAttributes) { if v != nil && v.ChildWorkflowExecutionFailedEventAttributes != nil { return v.ChildWorkflowExecutionFailedEventAttributes } return } // GetChildWorkflowExecutionCanceledEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetChildWorkflowExecutionCanceledEventAttributes() (o *ChildWorkflowExecutionCanceledEventAttributes) { if v != nil && v.ChildWorkflowExecutionCanceledEventAttributes != nil { return v.ChildWorkflowExecutionCanceledEventAttributes } return } // GetChildWorkflowExecutionTimedOutEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetChildWorkflowExecutionTimedOutEventAttributes() (o *ChildWorkflowExecutionTimedOutEventAttributes) { if v != nil && v.ChildWorkflowExecutionTimedOutEventAttributes != nil { return v.ChildWorkflowExecutionTimedOutEventAttributes } return } // GetChildWorkflowExecutionTerminatedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetChildWorkflowExecutionTerminatedEventAttributes() (o *ChildWorkflowExecutionTerminatedEventAttributes) { if v != nil && v.ChildWorkflowExecutionTerminatedEventAttributes != nil { return v.ChildWorkflowExecutionTerminatedEventAttributes } return } // GetSignalExternalWorkflowExecutionInitiatedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetSignalExternalWorkflowExecutionInitiatedEventAttributes() (o *SignalExternalWorkflowExecutionInitiatedEventAttributes) { if v != nil && v.SignalExternalWorkflowExecutionInitiatedEventAttributes != nil { return v.SignalExternalWorkflowExecutionInitiatedEventAttributes } return } // GetSignalExternalWorkflowExecutionFailedEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetSignalExternalWorkflowExecutionFailedEventAttributes() (o *SignalExternalWorkflowExecutionFailedEventAttributes) { if v != nil && v.SignalExternalWorkflowExecutionFailedEventAttributes != nil { return v.SignalExternalWorkflowExecutionFailedEventAttributes } return } // GetExternalWorkflowExecutionSignaledEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetExternalWorkflowExecutionSignaledEventAttributes() (o *ExternalWorkflowExecutionSignaledEventAttributes) { if v != nil && v.ExternalWorkflowExecutionSignaledEventAttributes != nil { return v.ExternalWorkflowExecutionSignaledEventAttributes } return } // GetUpsertWorkflowSearchAttributesEventAttributes is an internal getter (TBD...) func (v *HistoryEvent) GetUpsertWorkflowSearchAttributesEventAttributes() (o *UpsertWorkflowSearchAttributesEventAttributes) { if v != nil && v.UpsertWorkflowSearchAttributesEventAttributes != nil { return v.UpsertWorkflowSearchAttributesEventAttributes } return } // Size is an internal method to get the estimated size of the event func (v *HistoryEvent) ByteSize() uint64 { if v == nil { return 0 } size := uint64(8 + 8 + 4 + 8 + 8) // size of ID, Timestamp, EventType, Version, TaskID if v.WorkflowExecutionStartedEventAttributes != nil { size += v.WorkflowExecutionStartedEventAttributes.ByteSize() } if v.WorkflowExecutionCompletedEventAttributes != nil { size += v.WorkflowExecutionCompletedEventAttributes.ByteSize() } if v.WorkflowExecutionFailedEventAttributes != nil { size += v.WorkflowExecutionFailedEventAttributes.ByteSize() } if v.WorkflowExecutionTimedOutEventAttributes != nil { size += v.WorkflowExecutionTimedOutEventAttributes.ByteSize() } if v.WorkflowExecutionCancelRequestedEventAttributes != nil { size += v.WorkflowExecutionCancelRequestedEventAttributes.ByteSize() } if v.WorkflowExecutionCanceledEventAttributes != nil { size += v.WorkflowExecutionCanceledEventAttributes.ByteSize() } if v.WorkflowExecutionTerminatedEventAttributes != nil { size += v.WorkflowExecutionTerminatedEventAttributes.ByteSize() } if v.WorkflowExecutionContinuedAsNewEventAttributes != nil { size += v.WorkflowExecutionContinuedAsNewEventAttributes.ByteSize() } if v.WorkflowExecutionSignaledEventAttributes != nil { size += v.WorkflowExecutionSignaledEventAttributes.ByteSize() } if v.DecisionTaskScheduledEventAttributes != nil { size += v.DecisionTaskScheduledEventAttributes.ByteSize() } if v.DecisionTaskStartedEventAttributes != nil { size += v.DecisionTaskStartedEventAttributes.ByteSize() } if v.DecisionTaskCompletedEventAttributes != nil { size += v.DecisionTaskCompletedEventAttributes.ByteSize() } if v.DecisionTaskTimedOutEventAttributes != nil { size += v.DecisionTaskTimedOutEventAttributes.ByteSize() } if v.DecisionTaskFailedEventAttributes != nil { size += v.DecisionTaskFailedEventAttributes.ByteSize() } if v.ActivityTaskScheduledEventAttributes != nil { size += v.ActivityTaskScheduledEventAttributes.ByteSize() } if v.ActivityTaskStartedEventAttributes != nil { size += v.ActivityTaskStartedEventAttributes.ByteSize() } if v.ActivityTaskCompletedEventAttributes != nil { size += v.ActivityTaskCompletedEventAttributes.ByteSize() } if v.ActivityTaskFailedEventAttributes != nil { size += v.ActivityTaskFailedEventAttributes.ByteSize() } if v.ActivityTaskTimedOutEventAttributes != nil { size += v.ActivityTaskTimedOutEventAttributes.ByteSize() } if v.ActivityTaskCancelRequestedEventAttributes != nil { size += v.ActivityTaskCancelRequestedEventAttributes.ByteSize() } if v.ActivityTaskCanceledEventAttributes != nil { size += v.ActivityTaskCanceledEventAttributes.ByteSize() } if v.RequestCancelActivityTaskFailedEventAttributes != nil { size += v.RequestCancelActivityTaskFailedEventAttributes.ByteSize() } if v.TimerStartedEventAttributes != nil { size += v.TimerStartedEventAttributes.ByteSize() } if v.TimerFiredEventAttributes != nil { size += v.TimerFiredEventAttributes.ByteSize() } if v.TimerCanceledEventAttributes != nil { size += v.TimerCanceledEventAttributes.ByteSize() } if v.CancelTimerFailedEventAttributes != nil { size += v.CancelTimerFailedEventAttributes.ByteSize() } if v.MarkerRecordedEventAttributes != nil { size += v.MarkerRecordedEventAttributes.ByteSize() } if v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes != nil { size += v.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes.ByteSize() } if v.RequestCancelExternalWorkflowExecutionFailedEventAttributes != nil { size += v.RequestCancelExternalWorkflowExecutionFailedEventAttributes.ByteSize() } if v.ExternalWorkflowExecutionCancelRequestedEventAttributes != nil { size += v.ExternalWorkflowExecutionCancelRequestedEventAttributes.ByteSize() } if v.SignalExternalWorkflowExecutionInitiatedEventAttributes != nil { size += v.SignalExternalWorkflowExecutionInitiatedEventAttributes.ByteSize() } if v.SignalExternalWorkflowExecutionFailedEventAttributes != nil { size += v.SignalExternalWorkflowExecutionFailedEventAttributes.ByteSize() } if v.ExternalWorkflowExecutionSignaledEventAttributes != nil { size += v.ExternalWorkflowExecutionSignaledEventAttributes.ByteSize() } if v.StartChildWorkflowExecutionInitiatedEventAttributes != nil { size += v.StartChildWorkflowExecutionInitiatedEventAttributes.ByteSize() } if v.StartChildWorkflowExecutionFailedEventAttributes != nil { size += v.StartChildWorkflowExecutionFailedEventAttributes.ByteSize() } if v.ChildWorkflowExecutionStartedEventAttributes != nil { size += v.ChildWorkflowExecutionStartedEventAttributes.ByteSize() } if v.ChildWorkflowExecutionCompletedEventAttributes != nil { size += v.ChildWorkflowExecutionCompletedEventAttributes.ByteSize() } if v.ChildWorkflowExecutionFailedEventAttributes != nil { size += v.ChildWorkflowExecutionFailedEventAttributes.ByteSize() } if v.ChildWorkflowExecutionCanceledEventAttributes != nil { size += v.ChildWorkflowExecutionCanceledEventAttributes.ByteSize() } if v.ChildWorkflowExecutionTimedOutEventAttributes != nil { size += v.ChildWorkflowExecutionTimedOutEventAttributes.ByteSize() } if v.ChildWorkflowExecutionTerminatedEventAttributes != nil { size += v.ChildWorkflowExecutionTerminatedEventAttributes.ByteSize() } if v.UpsertWorkflowSearchAttributesEventAttributes != nil { size += v.UpsertWorkflowSearchAttributesEventAttributes.ByteSize() } return size } // HistoryEventFilterType is an internal type (TBD...) type HistoryEventFilterType int32 // Ptr is a helper function for getting pointer value func (e HistoryEventFilterType) Ptr() *HistoryEventFilterType { return &e } // String returns a readable string representation of HistoryEventFilterType. func (e HistoryEventFilterType) String() string { w := int32(e) switch w { case 0: return "ALL_EVENT" case 1: return "CLOSE_EVENT" } return fmt.Sprintf("HistoryEventFilterType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *HistoryEventFilterType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "ALL_EVENT": *e = HistoryEventFilterTypeAllEvent return nil case "CLOSE_EVENT": *e = HistoryEventFilterTypeCloseEvent return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "HistoryEventFilterType", err) } *e = HistoryEventFilterType(val) return nil } } // MarshalText encodes HistoryEventFilterType to text. func (e HistoryEventFilterType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // HistoryEventFilterTypeAllEvent is an option for HistoryEventFilterType HistoryEventFilterTypeAllEvent HistoryEventFilterType = iota // HistoryEventFilterTypeCloseEvent is an option for HistoryEventFilterType HistoryEventFilterTypeCloseEvent ) // IndexedValueType is an internal type (TBD...) type IndexedValueType int32 // Ptr is a helper function for getting pointer value func (e IndexedValueType) Ptr() *IndexedValueType { return &e } // String returns a readable string representation of IndexedValueType. func (e IndexedValueType) String() string { w := int32(e) switch w { case 0: return "STRING" case 1: return "KEYWORD" case 2: return "INT" case 3: return "DOUBLE" case 4: return "BOOL" case 5: return "DATETIME" } return fmt.Sprintf("IndexedValueType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *IndexedValueType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "STRING": *e = IndexedValueTypeString return nil case "KEYWORD": *e = IndexedValueTypeKeyword return nil case "INT": *e = IndexedValueTypeInt return nil case "DOUBLE": *e = IndexedValueTypeDouble return nil case "BOOL": *e = IndexedValueTypeBool return nil case "DATETIME": *e = IndexedValueTypeDatetime return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "IndexedValueType", err) } *e = IndexedValueType(val) return nil } } // MarshalText encodes IndexedValueType to text. func (e IndexedValueType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // IndexedValueTypeString is an option for IndexedValueType IndexedValueTypeString IndexedValueType = iota // IndexedValueTypeKeyword is an option for IndexedValueType IndexedValueTypeKeyword // IndexedValueTypeInt is an option for IndexedValueType IndexedValueTypeInt // IndexedValueTypeDouble is an option for IndexedValueType IndexedValueTypeDouble // IndexedValueTypeBool is an option for IndexedValueType IndexedValueTypeBool // IndexedValueTypeDatetime is an option for IndexedValueType IndexedValueTypeDatetime ) // InternalDataInconsistencyError is an internal type (TBD...) type InternalDataInconsistencyError struct { Message string `json:"message,required"` } // InternalServiceError is an internal type (TBD...) type InternalServiceError struct { Message string `json:"message,required"` } // GetMessage is an internal getter (TBD...) func (v *InternalServiceError) GetMessage() (o string) { if v != nil { return v.Message } return } // LimitExceededError is an internal type (TBD...) type LimitExceededError struct { Message string `json:"message,required"` } // ListArchivedWorkflowExecutionsRequest is an internal type (TBD...) type ListArchivedWorkflowExecutionsRequest struct { Domain string `json:"domain,omitempty"` PageSize int32 `json:"pageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` Query string `json:"query,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *ListArchivedWorkflowExecutionsRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetPageSize is an internal getter (TBD...) func (v *ListArchivedWorkflowExecutionsRequest) GetPageSize() (o int32) { if v != nil { return v.PageSize } return } // GetQuery is an internal getter (TBD...) func (v *ListArchivedWorkflowExecutionsRequest) GetQuery() (o string) { if v != nil { return v.Query } return } // ListArchivedWorkflowExecutionsResponse is an internal type (TBD...) type ListArchivedWorkflowExecutionsResponse struct { Executions []*WorkflowExecutionInfo `json:"executions,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetExecutions is an internal getter (TBD...) func (v *ListArchivedWorkflowExecutionsResponse) GetExecutions() (o []*WorkflowExecutionInfo) { if v != nil && v.Executions != nil { return v.Executions } return } // ListClosedWorkflowExecutionsRequest is an internal type (TBD...) type ListClosedWorkflowExecutionsRequest struct { Domain string `json:"domain,omitempty"` MaximumPageSize int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` StartTimeFilter *StartTimeFilter `json:"StartTimeFilter,omitempty"` ExecutionFilter *WorkflowExecutionFilter `json:"executionFilter,omitempty"` TypeFilter *WorkflowTypeFilter `json:"typeFilter,omitempty"` StatusFilter *WorkflowExecutionCloseStatus `json:"statusFilter,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *ListClosedWorkflowExecutionsRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetMaximumPageSize is an internal getter (TBD...) func (v *ListClosedWorkflowExecutionsRequest) GetMaximumPageSize() (o int32) { if v != nil { return v.MaximumPageSize } return } // GetStatusFilter is an internal getter (TBD...) func (v *ListClosedWorkflowExecutionsRequest) GetStatusFilter() (o WorkflowExecutionCloseStatus) { if v != nil && v.StatusFilter != nil { return *v.StatusFilter } return } // ListClosedWorkflowExecutionsResponse is an internal type (TBD...) type ListClosedWorkflowExecutionsResponse struct { Executions []*WorkflowExecutionInfo `json:"executions,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetExecutions is an internal getter (TBD...) func (v *ListClosedWorkflowExecutionsResponse) GetExecutions() (o []*WorkflowExecutionInfo) { if v != nil && v.Executions != nil { return v.Executions } return } // ListDomainsRequest is an internal type (TBD...) type ListDomainsRequest struct { PageSize int32 `json:"pageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetPageSize is an internal getter (TBD...) func (v *ListDomainsRequest) GetPageSize() (o int32) { if v != nil { return v.PageSize } return } // ListDomainsResponse is an internal type (TBD...) type ListDomainsResponse struct { Domains []*DescribeDomainResponse `json:"domains,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetDomains is an internal getter (TBD...) func (v *ListDomainsResponse) GetDomains() (o []*DescribeDomainResponse) { if v != nil && v.Domains != nil { return v.Domains } return } // GetNextPageToken is an internal getter (TBD...) func (v *ListDomainsResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // ListOpenWorkflowExecutionsRequest is an internal type (TBD...) type ListOpenWorkflowExecutionsRequest struct { Domain string `json:"domain,omitempty"` MaximumPageSize int32 `json:"maximumPageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` StartTimeFilter *StartTimeFilter `json:"StartTimeFilter,omitempty"` ExecutionFilter *WorkflowExecutionFilter `json:"executionFilter,omitempty"` TypeFilter *WorkflowTypeFilter `json:"typeFilter,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *ListOpenWorkflowExecutionsRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetMaximumPageSize is an internal getter (TBD...) func (v *ListOpenWorkflowExecutionsRequest) GetMaximumPageSize() (o int32) { if v != nil { return v.MaximumPageSize } return } // ListOpenWorkflowExecutionsResponse is an internal type (TBD...) type ListOpenWorkflowExecutionsResponse struct { Executions []*WorkflowExecutionInfo `json:"executions,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetExecutions is an internal getter (TBD...) func (v *ListOpenWorkflowExecutionsResponse) GetExecutions() (o []*WorkflowExecutionInfo) { if v != nil && v.Executions != nil { return v.Executions } return } // ListTaskListPartitionsRequest is an internal type (TBD...) type ListTaskListPartitionsRequest struct { Domain string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *ListTaskListPartitionsRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetTaskList is an internal getter (TBD...) func (v *ListTaskListPartitionsRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // ListTaskListPartitionsResponse is an internal type (TBD...) type ListTaskListPartitionsResponse struct { ActivityTaskListPartitions []*TaskListPartitionMetadata `json:"activityTaskListPartitions,omitempty"` DecisionTaskListPartitions []*TaskListPartitionMetadata `json:"decisionTaskListPartitions,omitempty"` } // GetTaskListsByDomainRequest is an internal type (TBD...) type GetTaskListsByDomainRequest struct { Domain string `json:"domain,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *GetTaskListsByDomainRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetTaskListsByDomainResponse is an internal type (TBD...) type GetTaskListsByDomainResponse struct { DecisionTaskListMap map[string]*DescribeTaskListResponse `json:"decisionTaskListMap,omitempty"` ActivityTaskListMap map[string]*DescribeTaskListResponse `json:"activityTaskListMap,omitempty"` } // GetDecisionTaskListMap is an internal getter (TBD...) func (v *GetTaskListsByDomainResponse) GetDecisionTaskListMap() (o map[string]*DescribeTaskListResponse) { if v != nil && v.DecisionTaskListMap != nil { return v.DecisionTaskListMap } return } // GetActivityTaskListMap is an internal getter (TBD...) func (v *GetTaskListsByDomainResponse) GetActivityTaskListMap() (o map[string]*DescribeTaskListResponse) { if v != nil && v.ActivityTaskListMap != nil { return v.ActivityTaskListMap } return } // ListWorkflowExecutionsRequest is an internal type (TBD...) type ListWorkflowExecutionsRequest struct { Domain string `json:"domain,omitempty"` PageSize int32 `json:"pageSize,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` Query string `json:"query,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *ListWorkflowExecutionsRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetPageSize is an internal getter (TBD...) func (v *ListWorkflowExecutionsRequest) GetPageSize() (o int32) { if v != nil { return v.PageSize } return } // GetQuery is an internal getter (TBD...) func (v *ListWorkflowExecutionsRequest) GetQuery() (o string) { if v != nil { return v.Query } return } // ListWorkflowExecutionsResponse is an internal type (TBD...) type ListWorkflowExecutionsResponse struct { Executions []*WorkflowExecutionInfo `json:"executions,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` } // GetExecutions is an internal getter (TBD...) func (v *ListWorkflowExecutionsResponse) GetExecutions() (o []*WorkflowExecutionInfo) { if v != nil && v.Executions != nil { return v.Executions } return } // GetNextPageToken is an internal getter (TBD...) func (v *ListWorkflowExecutionsResponse) GetNextPageToken() (o []byte) { if v != nil && v.NextPageToken != nil { return v.NextPageToken } return } // MarkerRecordedEventAttributes is an internal type (TBD...) type MarkerRecordedEventAttributes struct { MarkerName string `json:"markerName,omitempty"` Details []byte `json:"details,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` Header *Header `json:"header,omitempty"` } // GetMarkerName is an internal getter (TBD...) func (v *MarkerRecordedEventAttributes) GetMarkerName() (o string) { if v != nil { return v.MarkerName } return } // Size returns the approximate memory used in bytes func (v *MarkerRecordedEventAttributes) ByteSize() uint64 { return 0 } // Memo is an internal type (TBD...) type Memo struct { Fields map[string][]byte `json:"fields,omitempty"` } // GetFields is an internal getter (TBD...) func (v *Memo) GetFields() (o map[string][]byte) { if v != nil && v.Fields != nil { return v.Fields } return } // ParentClosePolicy is an internal type (TBD...) type ParentClosePolicy int32 // Ptr is a helper function for getting pointer value func (e ParentClosePolicy) Ptr() *ParentClosePolicy { return &e } // String returns a readable string representation of ParentClosePolicy. func (e ParentClosePolicy) String() string { w := int32(e) switch w { case 0: return "ABANDON" case 1: return "REQUEST_CANCEL" case 2: return "TERMINATE" } return fmt.Sprintf("ParentClosePolicy(%d)", w) } // UnmarshalText parses enum value from string representation func (e *ParentClosePolicy) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "ABANDON": *e = ParentClosePolicyAbandon return nil case "REQUEST_CANCEL": *e = ParentClosePolicyRequestCancel return nil case "TERMINATE": *e = ParentClosePolicyTerminate return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "ParentClosePolicy", err) } *e = ParentClosePolicy(val) return nil } } // MarshalText encodes ParentClosePolicy to text. func (e ParentClosePolicy) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // ParentClosePolicyAbandon is an option for ParentClosePolicy ParentClosePolicyAbandon ParentClosePolicy = iota // ParentClosePolicyRequestCancel is an option for ParentClosePolicy ParentClosePolicyRequestCancel // ParentClosePolicyTerminate is an option for ParentClosePolicy ParentClosePolicyTerminate ) // PendingActivityInfo is an internal type (TBD...) type PendingActivityInfo struct { ActivityID string `json:"activityID,omitempty"` ActivityType *ActivityType `json:"activityType,omitempty"` State *PendingActivityState `json:"state,omitempty"` HeartbeatDetails []byte `json:"heartbeatDetails,omitempty"` LastHeartbeatTimestamp *int64 `json:"lastHeartbeatTimestamp,omitempty"` LastStartedTimestamp *int64 `json:"lastStartedTimestamp,omitempty"` Attempt int32 `json:"attempt,omitempty"` MaximumAttempts int32 `json:"maximumAttempts,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` ExpirationTimestamp *int64 `json:"expirationTimestamp,omitempty"` LastFailureReason *string `json:"lastFailureReason,omitempty"` StartedWorkerIdentity string `json:"startedWorkerIdentity,omitempty"` LastWorkerIdentity string `json:"lastWorkerIdentity,omitempty"` LastFailureDetails []byte `json:"lastFailureDetails,omitempty"` ScheduleID int64 `json:"scheduleID,omitempty"` } // GetActivityID is an internal getter (TBD...) func (v *PendingActivityInfo) GetActivityID() (o string) { if v != nil { return v.ActivityID } return } // GetState is an internal getter (TBD...) func (v *PendingActivityInfo) GetState() (o PendingActivityState) { if v != nil && v.State != nil { return *v.State } return } // GetHeartbeatDetails is an internal getter (TBD...) func (v *PendingActivityInfo) GetHeartbeatDetails() (o []byte) { if v != nil && v.HeartbeatDetails != nil { return v.HeartbeatDetails } return } // GetLastHeartbeatTimestamp is an internal getter (TBD...) func (v *PendingActivityInfo) GetLastHeartbeatTimestamp() (o int64) { if v != nil && v.LastHeartbeatTimestamp != nil { return *v.LastHeartbeatTimestamp } return } // GetAttempt is an internal getter (TBD...) func (v *PendingActivityInfo) GetAttempt() (o int32) { if v != nil { return v.Attempt } return } // GetMaximumAttempts is an internal getter (TBD...) func (v *PendingActivityInfo) GetMaximumAttempts() (o int32) { if v != nil { return v.MaximumAttempts } return } // GetLastFailureReason is an internal getter (TBD...) func (v *PendingActivityInfo) GetLastFailureReason() (o string) { if v != nil && v.LastFailureReason != nil { return *v.LastFailureReason } return } // GetStartedWorkerIdentity is an internal getter (TBD...) func (v *PendingActivityInfo) GetStartedWorkerIdentity() (o string) { if v != nil { return v.StartedWorkerIdentity } return } // GetLastWorkerIdentity is an internal getter (TBD...) func (v *PendingActivityInfo) GetLastWorkerIdentity() (o string) { if v != nil { return v.LastWorkerIdentity } return } // GetLastFailureDetails is an internal getter (TBD...) func (v *PendingActivityInfo) GetLastFailureDetails() (o []byte) { if v != nil && v.LastFailureDetails != nil { return v.LastFailureDetails } return } // GetScheduleID is an internal getter (TBD...) func (v *PendingActivityInfo) GetScheduleID() (o int64) { if v != nil { return v.ScheduleID } return } // PendingActivityState is an internal type (TBD...) type PendingActivityState int32 // Ptr is a helper function for getting pointer value func (e PendingActivityState) Ptr() *PendingActivityState { return &e } // String returns a readable string representation of PendingActivityState. func (e PendingActivityState) String() string { w := int32(e) switch w { case 0: return "SCHEDULED" case 1: return "STARTED" case 2: return "CANCEL_REQUESTED" } return fmt.Sprintf("PendingActivityState(%d)", w) } // UnmarshalText parses enum value from string representation func (e *PendingActivityState) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "SCHEDULED": *e = PendingActivityStateScheduled return nil case "STARTED": *e = PendingActivityStateStarted return nil case "CANCEL_REQUESTED": *e = PendingActivityStateCancelRequested return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "PendingActivityState", err) } *e = PendingActivityState(val) return nil } } // MarshalText encodes PendingActivityState to text. func (e PendingActivityState) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // PendingActivityStateScheduled is an option for PendingActivityState PendingActivityStateScheduled PendingActivityState = iota // PendingActivityStateStarted is an option for PendingActivityState PendingActivityStateStarted // PendingActivityStateCancelRequested is an option for PendingActivityState PendingActivityStateCancelRequested ) // PendingChildExecutionInfo is an internal type (TBD...) type PendingChildExecutionInfo struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowID,omitempty"` RunID string `json:"runID,omitempty"` WorkflowTypeName string `json:"workflowTypeName,omitempty"` InitiatedID int64 `json:"initiatedID,omitempty"` ParentClosePolicy *ParentClosePolicy `json:"parentClosePolicy,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *PendingChildExecutionInfo) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowID is an internal getter (TBD...) func (v *PendingChildExecutionInfo) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *PendingChildExecutionInfo) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetWorkflowTypeName is an internal getter (TBD...) func (v *PendingChildExecutionInfo) GetWorkflowTypeName() (o string) { if v != nil { return v.WorkflowTypeName } return } // PendingDecisionInfo is an internal type (TBD...) type PendingDecisionInfo struct { State *PendingDecisionState `json:"state,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Attempt int64 `json:"attempt,omitempty"` OriginalScheduledTimestamp *int64 `json:"originalScheduledTimestamp,omitempty"` ScheduleID int64 `json:"scheduleID,omitempty"` } // PendingDecisionState is an internal type (TBD...) type PendingDecisionState int32 // Ptr is a helper function for getting pointer value func (e PendingDecisionState) Ptr() *PendingDecisionState { return &e } // String returns a readable string representation of PendingDecisionState. func (e PendingDecisionState) String() string { w := int32(e) switch w { case 0: return "SCHEDULED" case 1: return "STARTED" } return fmt.Sprintf("PendingDecisionState(%d)", w) } // UnmarshalText parses enum value from string representation func (e *PendingDecisionState) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "SCHEDULED": *e = PendingDecisionStateScheduled return nil case "STARTED": *e = PendingDecisionStateStarted return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "PendingDecisionState", err) } *e = PendingDecisionState(val) return nil } } // MarshalText encodes PendingDecisionState to text. func (e PendingDecisionState) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // PendingDecisionStateScheduled is an option for PendingDecisionState PendingDecisionStateScheduled PendingDecisionState = iota // PendingDecisionStateStarted is an option for PendingDecisionState PendingDecisionStateStarted ) // PollForActivityTaskRequest is an internal type (TBD...) type PollForActivityTaskRequest struct { Domain string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Identity string `json:"identity,omitempty"` TaskListMetadata *TaskListMetadata `json:"taskListMetadata,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *PollForActivityTaskRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetTaskList is an internal getter (TBD...) func (v *PollForActivityTaskRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetIdentity is an internal getter (TBD...) func (v *PollForActivityTaskRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // PollForActivityTaskResponse is an internal type (TBD...) type PollForActivityTaskResponse struct { TaskToken []byte `json:"taskToken,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` ActivityID string `json:"activityId,omitempty"` ActivityType *ActivityType `json:"activityType,omitempty"` Input []byte `json:"input,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` ScheduleToCloseTimeoutSeconds *int32 `json:"scheduleToCloseTimeoutSeconds,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` StartToCloseTimeoutSeconds *int32 `json:"startToCloseTimeoutSeconds,omitempty"` HeartbeatTimeoutSeconds *int32 `json:"heartbeatTimeoutSeconds,omitempty"` Attempt int32 `json:"attempt,omitempty"` ScheduledTimestampOfThisAttempt *int64 `json:"scheduledTimestampOfThisAttempt,omitempty"` HeartbeatDetails []byte `json:"heartbeatDetails,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` WorkflowDomain string `json:"workflowDomain,omitempty"` Header *Header `json:"header,omitempty"` AutoConfigHint *AutoConfigHint `json:"autoConfigHint,omitempty"` } // GetActivityID is an internal getter (TBD...) func (v *PollForActivityTaskResponse) GetActivityID() (o string) { if v != nil { return v.ActivityID } return } // PollForDecisionTaskRequest is an internal type (TBD...) type PollForDecisionTaskRequest struct { Domain string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Identity string `json:"identity,omitempty"` BinaryChecksum string `json:"binaryChecksum,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *PollForDecisionTaskRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetTaskList is an internal getter (TBD...) func (v *PollForDecisionTaskRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetIdentity is an internal getter (TBD...) func (v *PollForDecisionTaskRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // GetBinaryChecksum is an internal getter (TBD...) func (v *PollForDecisionTaskRequest) GetBinaryChecksum() (o string) { if v != nil { return v.BinaryChecksum } return } // PollForDecisionTaskResponse is an internal type (TBD...) type PollForDecisionTaskResponse struct { TaskToken []byte `json:"taskToken,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` PreviousStartedEventID *int64 `json:"previousStartedEventId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` Attempt int64 `json:"attempt,omitempty"` BacklogCountHint int64 `json:"backlogCountHint,omitempty"` History *History `json:"history,omitempty"` NextPageToken []byte `json:"nextPageToken,omitempty"` Query *WorkflowQuery `json:"query,omitempty"` WorkflowExecutionTaskList *TaskList `json:"WorkflowExecutionTaskList,omitempty"` ScheduledTimestamp *int64 `json:"scheduledTimestamp,omitempty"` StartedTimestamp *int64 `json:"startedTimestamp,omitempty"` Queries map[string]*WorkflowQuery `json:"queries,omitempty"` NextEventID int64 `json:"nextEventId,omitempty"` TotalHistoryBytes int64 `json:"historySize,omitempty"` AutoConfigHint *AutoConfigHint `json:"autoConfigHint,omitempty"` } // GetTaskToken is an internal getter (TBD...) func (v *PollForDecisionTaskResponse) GetTaskToken() (o []byte) { if v != nil && v.TaskToken != nil { return v.TaskToken } return } // GetPreviousStartedEventID is an internal getter (TBD...) func (v *PollForDecisionTaskResponse) GetPreviousStartedEventID() (o int64) { if v != nil && v.PreviousStartedEventID != nil { return *v.PreviousStartedEventID } return } // GetStartedEventID is an internal getter (TBD...) func (v *PollForDecisionTaskResponse) GetStartedEventID() (o int64) { if v != nil { return v.StartedEventID } return } // GetAttempt is an internal getter (TBD...) func (v *PollForDecisionTaskResponse) GetAttempt() (o int64) { if v != nil { return v.Attempt } return } // GetQueries is an internal getter (TBD...) func (v *PollForDecisionTaskResponse) GetQueries() (o map[string]*WorkflowQuery) { if v != nil && v.Queries != nil { return v.Queries } return } // GetNextEventID is an internal getter (TBD...) func (v *PollForDecisionTaskResponse) GetNextEventID() (o int64) { if v != nil { return v.NextEventID } return } func (v *PollForDecisionTaskResponse) GetHistorySize() (o int64) { if v != nil { return v.TotalHistoryBytes } return } // PollerInfo is an internal type (TBD...) type PollerInfo struct { LastAccessTime *int64 `json:"lastAccessTime,omitempty"` Identity string `json:"identity,omitempty"` RatePerSecond float64 `json:"ratePerSecond,omitempty"` } // GetLastAccessTime is an internal getter (TBD...) func (v *PollerInfo) GetLastAccessTime() (o int64) { if v != nil && v.LastAccessTime != nil { return *v.LastAccessTime } return } // GetIdentity is an internal getter (TBD...) func (v *PollerInfo) GetIdentity() (o string) { if v != nil { return v.Identity } return } // GetRatePerSecond is an internal getter (TBD...) func (v *PollerInfo) GetRatePerSecond() (o float64) { if v != nil { return v.RatePerSecond } return } // QueryConsistencyLevel is an internal type (TBD...) type QueryConsistencyLevel int32 // Ptr is a helper function for getting pointer value func (e QueryConsistencyLevel) Ptr() *QueryConsistencyLevel { return &e } // String returns a readable string representation of QueryConsistencyLevel. func (e QueryConsistencyLevel) String() string { w := int32(e) switch w { case 0: return "EVENTUAL" case 1: return "STRONG" } return fmt.Sprintf("QueryConsistencyLevel(%d)", w) } // UnmarshalText parses enum value from string representation func (e *QueryConsistencyLevel) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "EVENTUAL": *e = QueryConsistencyLevelEventual return nil case "STRONG": *e = QueryConsistencyLevelStrong return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "QueryConsistencyLevel", err) } *e = QueryConsistencyLevel(val) return nil } } // MarshalText encodes QueryConsistencyLevel to text. func (e QueryConsistencyLevel) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // QueryConsistencyLevelEventual is an option for QueryConsistencyLevel QueryConsistencyLevelEventual QueryConsistencyLevel = iota // QueryConsistencyLevelStrong is an option for QueryConsistencyLevel QueryConsistencyLevelStrong ) // QueryFailedError is an internal type (TBD...) type QueryFailedError struct { Message string `json:"message,required"` } // QueryRejectCondition is an internal type (TBD...) type QueryRejectCondition int32 // Ptr is a helper function for getting pointer value func (e QueryRejectCondition) Ptr() *QueryRejectCondition { return &e } // String returns a readable string representation of QueryRejectCondition. func (e QueryRejectCondition) String() string { w := int32(e) switch w { case 0: return "NOT_OPEN" case 1: return "NOT_COMPLETED_CLEANLY" } return fmt.Sprintf("QueryRejectCondition(%d)", w) } // UnmarshalText parses enum value from string representation func (e *QueryRejectCondition) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "NOT_OPEN": *e = QueryRejectConditionNotOpen return nil case "NOT_COMPLETED_CLEANLY": *e = QueryRejectConditionNotCompletedCleanly return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "QueryRejectCondition", err) } *e = QueryRejectCondition(val) return nil } } // MarshalText encodes QueryRejectCondition to text. func (e QueryRejectCondition) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // QueryRejectConditionNotOpen is an option for QueryRejectCondition QueryRejectConditionNotOpen QueryRejectCondition = iota // QueryRejectConditionNotCompletedCleanly is an option for QueryRejectCondition QueryRejectConditionNotCompletedCleanly ) // QueryRejected is an internal type (TBD...) type QueryRejected struct { CloseStatus *WorkflowExecutionCloseStatus `json:"closeStatus,omitempty"` } // QueryResultType is an internal type (TBD...) type QueryResultType int32 // Ptr is a helper function for getting pointer value func (e QueryResultType) Ptr() *QueryResultType { return &e } // String returns a readable string representation of QueryResultType. func (e QueryResultType) String() string { w := int32(e) switch w { case 0: return "ANSWERED" case 1: return "FAILED" } return fmt.Sprintf("QueryResultType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *QueryResultType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "ANSWERED": *e = QueryResultTypeAnswered return nil case "FAILED": *e = QueryResultTypeFailed return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "QueryResultType", err) } *e = QueryResultType(val) return nil } } // MarshalText encodes QueryResultType to text. func (e QueryResultType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // QueryResultTypeAnswered is an option for QueryResultType QueryResultTypeAnswered QueryResultType = iota // QueryResultTypeFailed is an option for QueryResultType QueryResultTypeFailed ) // QueryTaskCompletedType is an internal type (TBD...) type QueryTaskCompletedType int32 // Ptr is a helper function for getting pointer value func (e QueryTaskCompletedType) Ptr() *QueryTaskCompletedType { return &e } // String returns a readable string representation of QueryTaskCompletedType. func (e QueryTaskCompletedType) String() string { w := int32(e) switch w { case 0: return "COMPLETED" case 1: return "FAILED" } return fmt.Sprintf("QueryTaskCompletedType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *QueryTaskCompletedType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "COMPLETED": *e = QueryTaskCompletedTypeCompleted return nil case "FAILED": *e = QueryTaskCompletedTypeFailed return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "QueryTaskCompletedType", err) } *e = QueryTaskCompletedType(val) return nil } } // MarshalText encodes QueryTaskCompletedType to text. func (e QueryTaskCompletedType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // QueryTaskCompletedTypeCompleted is an option for QueryTaskCompletedType QueryTaskCompletedTypeCompleted QueryTaskCompletedType = iota // QueryTaskCompletedTypeFailed is an option for QueryTaskCompletedType QueryTaskCompletedTypeFailed ) // QueryWorkflowRequest is an internal type (TBD...) type QueryWorkflowRequest struct { Domain string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` Query *WorkflowQuery `json:"query,omitempty"` QueryRejectCondition *QueryRejectCondition `json:"queryRejectCondition,omitempty"` QueryConsistencyLevel *QueryConsistencyLevel `json:"queryConsistencyLevel,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *QueryWorkflowRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetExecution is an internal getter (TBD...) func (v *QueryWorkflowRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // GetQuery is an internal getter (TBD...) func (v *QueryWorkflowRequest) GetQuery() (o *WorkflowQuery) { if v != nil && v.Query != nil { return v.Query } return } // GetQueryRejectCondition is an internal getter (TBD...) func (v *QueryWorkflowRequest) GetQueryRejectCondition() (o QueryRejectCondition) { if v != nil && v.QueryRejectCondition != nil { return *v.QueryRejectCondition } return } // GetQueryConsistencyLevel is an internal getter (TBD...) func (v *QueryWorkflowRequest) GetQueryConsistencyLevel() (o QueryConsistencyLevel) { if v != nil && v.QueryConsistencyLevel != nil { return *v.QueryConsistencyLevel } return } // QueryWorkflowResponse is an internal type (TBD...) type QueryWorkflowResponse struct { QueryResult []byte `json:"queryResult,omitempty"` QueryRejected *QueryRejected `json:"queryRejected,omitempty"` } // GetQueryResult is an internal getter (TBD...) func (v *QueryWorkflowResponse) GetQueryResult() (o []byte) { if v != nil && v.QueryResult != nil { return v.QueryResult } return } // GetQueryRejected is an internal getter (TBD...) func (v *QueryWorkflowResponse) GetQueryRejected() (o *QueryRejected) { if v != nil && v.QueryRejected != nil { return v.QueryRejected } return } // ReapplyEventsRequest is an internal type (TBD...) type ReapplyEventsRequest struct { DomainName string `json:"domainName,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Events *DataBlob `json:"events,omitempty"` } // GetDomainName is an internal getter (TBD...) func (v *ReapplyEventsRequest) GetDomainName() (o string) { if v != nil { return v.DomainName } return } // GetWorkflowExecution is an internal getter (TBD...) func (v *ReapplyEventsRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // GetEvents is an internal getter (TBD...) func (v *ReapplyEventsRequest) GetEvents() (o *DataBlob) { if v != nil && v.Events != nil { return v.Events } return } // RecordActivityTaskHeartbeatByIDRequest is an internal type (TBD...) type RecordActivityTaskHeartbeatByIDRequest struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowID,omitempty"` RunID string `json:"runID,omitempty"` ActivityID string `json:"activityID,omitempty"` Details []byte `json:"details,omitempty"` Identity string `json:"identity,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *RecordActivityTaskHeartbeatByIDRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowID is an internal getter (TBD...) func (v *RecordActivityTaskHeartbeatByIDRequest) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *RecordActivityTaskHeartbeatByIDRequest) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetActivityID is an internal getter (TBD...) func (v *RecordActivityTaskHeartbeatByIDRequest) GetActivityID() (o string) { if v != nil { return v.ActivityID } return } // RecordActivityTaskHeartbeatRequest is an internal type (TBD...) type RecordActivityTaskHeartbeatRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Details []byte `json:"details,omitempty"` Identity string `json:"identity,omitempty"` } // RecordActivityTaskHeartbeatResponse is an internal type (TBD...) type RecordActivityTaskHeartbeatResponse struct { CancelRequested bool `json:"cancelRequested,omitempty"` } // RecordMarkerDecisionAttributes is an internal type (TBD...) type RecordMarkerDecisionAttributes struct { MarkerName string `json:"markerName,omitempty"` Details []byte `json:"details,omitempty"` Header *Header `json:"header,omitempty"` } // GetMarkerName is an internal getter (TBD...) func (v *RecordMarkerDecisionAttributes) GetMarkerName() (o string) { if v != nil { return v.MarkerName } return } // RefreshWorkflowTasksRequest is an internal type (TBD...) type RefreshWorkflowTasksRequest struct { Domain string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *RefreshWorkflowTasksRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetExecution is an internal getter (TBD...) func (v *RefreshWorkflowTasksRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // RegisterDomainRequest is an internal type (TBD...) type RegisterDomainRequest struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` OwnerEmail string `json:"ownerEmail,omitempty"` WorkflowExecutionRetentionPeriodInDays int32 `json:"workflowExecutionRetentionPeriodInDays,omitempty"` EmitMetric *bool `json:"emitMetric,omitempty"` Clusters []*ClusterReplicationConfiguration `json:"clusters,omitempty"` ActiveClusterName string `json:"activeClusterName,omitempty"` ActiveClusters *ActiveClusters `json:"activeClusters,omitempty"` Data map[string]string `json:"data,omitempty"` SecurityToken string `json:"securityToken,omitempty"` IsGlobalDomain bool `json:"isGlobalDomain,omitempty"` HistoryArchivalStatus *ArchivalStatus `json:"historyArchivalStatus,omitempty"` HistoryArchivalURI string `json:"historyArchivalURI,omitempty"` VisibilityArchivalStatus *ArchivalStatus `json:"visibilityArchivalStatus,omitempty"` VisibilityArchivalURI string `json:"visibilityArchivalURI,omitempty"` } // GetName is an internal getter (TBD...) func (v *RegisterDomainRequest) GetName() (o string) { if v != nil { return v.Name } return } // GetDescription is an internal getter (TBD...) func (v *RegisterDomainRequest) GetDescription() (o string) { if v != nil { return v.Description } return } // GetOwnerEmail is an internal getter (TBD...) func (v *RegisterDomainRequest) GetOwnerEmail() (o string) { if v != nil { return v.OwnerEmail } return } // GetWorkflowExecutionRetentionPeriodInDays is an internal getter (TBD...) func (v *RegisterDomainRequest) GetWorkflowExecutionRetentionPeriodInDays() (o int32) { if v != nil { return v.WorkflowExecutionRetentionPeriodInDays } return } // GetEmitMetric is an internal getter (TBD...) func (v *RegisterDomainRequest) GetEmitMetric() (o bool) { if v != nil && v.EmitMetric != nil { return *v.EmitMetric } o = true return } // GetActiveClusterName is an internal getter (TBD...) func (v *RegisterDomainRequest) GetActiveClusterName() (o string) { if v != nil { return v.ActiveClusterName } return } // GetData is an internal getter (TBD...) func (v *RegisterDomainRequest) GetData() (o map[string]string) { if v != nil && v.Data != nil { return v.Data } return } // GetIsGlobalDomain is an internal getter (TBD...) func (v *RegisterDomainRequest) GetIsGlobalDomain() (o bool) { if v != nil { return v.IsGlobalDomain } return } // GetHistoryArchivalURI is an internal getter (TBD...) func (v *RegisterDomainRequest) GetHistoryArchivalURI() (o string) { if v != nil { return v.HistoryArchivalURI } return } // GetVisibilityArchivalURI is an internal getter (TBD...) func (v *RegisterDomainRequest) GetVisibilityArchivalURI() (o string) { if v != nil { return v.VisibilityArchivalURI } return } // Size returns the approximate memory used in bytes func (v *RegisterDomainRequest) ByteSize() uint64 { return 0 } // RemoteSyncMatchedError is an internal type (TBD...) type RemoteSyncMatchedError struct { Message string `json:"message,required"` } // Size returns the approximate memory used in bytes func (v *RemoteSyncMatchedError) ByteSize() uint64 { return 0 } // RemoveTaskRequest is an internal type (TBD...) type RemoveTaskRequest struct { ShardID int32 `json:"shardID,omitempty"` Type *int32 `json:"type,omitempty"` TaskID int64 `json:"taskID,omitempty"` VisibilityTimestamp *int64 `json:"visibilityTimestamp,omitempty"` ClusterName string `json:"clusterName,omitempty"` } // GetShardID is an internal getter (TBD...) func (v *RemoveTaskRequest) GetShardID() (o int32) { if v != nil { return v.ShardID } return } // GetType is an internal getter (TBD...) func (v *RemoveTaskRequest) GetType() (o int32) { if v != nil && v.Type != nil { return *v.Type } return } // GetTaskID is an internal getter (TBD...) func (v *RemoveTaskRequest) GetTaskID() (o int64) { if v != nil { return v.TaskID } return } // GetVisibilityTimestamp is an internal getter (TBD...) func (v *RemoveTaskRequest) GetVisibilityTimestamp() (o int64) { if v != nil && v.VisibilityTimestamp != nil { return *v.VisibilityTimestamp } return } // GetClusterName is an internal getter (TBD...) func (v *RemoveTaskRequest) GetClusterName() (o string) { if v != nil { return v.ClusterName } return } // Size returns the approximate memory used in bytes func (v *RemoveTaskRequest) ByteSize() uint64 { return 0 } // RequestCancelActivityTaskDecisionAttributes is an internal type (TBD...) type RequestCancelActivityTaskDecisionAttributes struct { ActivityID string `json:"activityId,omitempty"` } // GetActivityID is an internal getter (TBD...) func (v *RequestCancelActivityTaskDecisionAttributes) GetActivityID() (o string) { if v != nil { return v.ActivityID } return } // Size returns the approximate memory used in bytes func (v *RequestCancelActivityTaskDecisionAttributes) ByteSize() uint64 { return 0 } // RequestCancelActivityTaskFailedEventAttributes is an internal type (TBD...) type RequestCancelActivityTaskFailedEventAttributes struct { ActivityID string `json:"activityId,omitempty"` Cause string `json:"cause,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` } // Size returns the approximate memory used in bytes func (v *RequestCancelActivityTaskFailedEventAttributes) ByteSize() uint64 { return 0 } // RequestCancelExternalWorkflowExecutionDecisionAttributes is an internal type (TBD...) type RequestCancelExternalWorkflowExecutionDecisionAttributes struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowId,omitempty"` RunID string `json:"runId,omitempty"` Control []byte `json:"control,omitempty"` ChildWorkflowOnly bool `json:"childWorkflowOnly,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowID is an internal getter (TBD...) func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) GetRunID() (o string) { if v != nil { return v.RunID } return } // Size returns the approximate memory used in bytes func (v *RequestCancelExternalWorkflowExecutionDecisionAttributes) ByteSize() uint64 { return 0 } // RequestCancelExternalWorkflowExecutionFailedEventAttributes is an internal type (TBD...) type RequestCancelExternalWorkflowExecutionFailedEventAttributes struct { Cause *CancelExternalWorkflowExecutionFailedCause `json:"cause,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` InitiatedEventID int64 `json:"initiatedEventId,omitempty"` Control []byte `json:"control,omitempty"` } // GetDecisionTaskCompletedEventID is an internal getter (TBD...) func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) GetDecisionTaskCompletedEventID() (o int64) { if v != nil { return v.DecisionTaskCompletedEventID } return } // GetDomain is an internal getter (TBD...) func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetInitiatedEventID is an internal getter (TBD...) func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // Size returns the approximate memory used in bytes func (v *RequestCancelExternalWorkflowExecutionFailedEventAttributes) ByteSize() uint64 { return 0 } // RequestCancelExternalWorkflowExecutionInitiatedEventAttributes is an internal type (TBD...) type RequestCancelExternalWorkflowExecutionInitiatedEventAttributes struct { DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Control []byte `json:"control,omitempty"` ChildWorkflowOnly bool `json:"childWorkflowOnly,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowExecution is an internal getter (TBD...) func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // GetChildWorkflowOnly is an internal getter (TBD...) func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) GetChildWorkflowOnly() (o bool) { if v != nil { return v.ChildWorkflowOnly } return } // Size returns the approximate memory used in bytes func (v *RequestCancelExternalWorkflowExecutionInitiatedEventAttributes) ByteSize() uint64 { return 0 } // RequestCancelWorkflowExecutionRequest is an internal type (TBD...) type RequestCancelWorkflowExecutionRequest struct { Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Identity string `json:"identity,omitempty"` RequestID string `json:"requestId,omitempty"` Cause string `json:"cause,omitempty"` FirstExecutionRunID string `json:"first_execution_run_id,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *RequestCancelWorkflowExecutionRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowExecution is an internal getter (TBD...) func (v *RequestCancelWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // GetRequestID is an internal getter (TBD...) func (v *RequestCancelWorkflowExecutionRequest) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // GetFirstExecutionRunID is an internal getter (TBD...) func (v *RequestCancelWorkflowExecutionRequest) GetFirstExecutionRunID() (o string) { if v != nil { return v.FirstExecutionRunID } return } // Size returns the approximate memory used in bytes func (v *RequestCancelWorkflowExecutionRequest) ByteSize() uint64 { return 0 } // ResetPointInfo is an internal type (TBD...) type ResetPointInfo struct { BinaryChecksum string `json:"binaryChecksum,omitempty"` RunID string `json:"runId,omitempty"` FirstDecisionCompletedID int64 `json:"firstDecisionCompletedId,omitempty"` CreatedTimeNano *int64 `json:"createdTimeNano,omitempty"` ExpiringTimeNano *int64 `json:"expiringTimeNano,omitempty"` Resettable bool `json:"resettable,omitempty"` } // GetBinaryChecksum is an internal getter (TBD...) func (v *ResetPointInfo) GetBinaryChecksum() (o string) { if v != nil { return v.BinaryChecksum } return } // GetRunID is an internal getter (TBD...) func (v *ResetPointInfo) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetFirstDecisionCompletedID is an internal getter (TBD...) func (v *ResetPointInfo) GetFirstDecisionCompletedID() (o int64) { if v != nil { return v.FirstDecisionCompletedID } return } // GetCreatedTimeNano is an internal getter (TBD...) func (v *ResetPointInfo) GetCreatedTimeNano() (o int64) { if v != nil && v.CreatedTimeNano != nil { return *v.CreatedTimeNano } return } // GetExpiringTimeNano is an internal getter (TBD...) func (v *ResetPointInfo) GetExpiringTimeNano() (o int64) { if v != nil && v.ExpiringTimeNano != nil { return *v.ExpiringTimeNano } return } // GetResettable is an internal getter (TBD...) func (v *ResetPointInfo) GetResettable() (o bool) { if v != nil { return v.Resettable } return } // Size returns the approximate memory used in bytes func (v *ResetPointInfo) ByteSize() uint64 { return 0 } // ResetPoints is an internal type (TBD...) type ResetPoints struct { Points []*ResetPointInfo `json:"points,omitempty"` } // Size returns the approximate memory used in bytes func (v *ResetPoints) ByteSize() uint64 { return 0 } // ResetQueueRequest is an internal type (TBD...) type ResetQueueRequest struct { ShardID int32 `json:"shardID,omitempty"` ClusterName string `json:"clusterName,omitempty"` Type *int32 `json:"type,omitempty"` } // GetShardID is an internal getter (TBD...) func (v *ResetQueueRequest) GetShardID() (o int32) { if v != nil { return v.ShardID } return } // GetClusterName is an internal getter (TBD...) func (v *ResetQueueRequest) GetClusterName() (o string) { if v != nil { return v.ClusterName } return } // GetType is an internal getter (TBD...) func (v *ResetQueueRequest) GetType() (o int32) { if v != nil && v.Type != nil { return *v.Type } return } // Size returns the approximate memory used in bytes func (v *ResetQueueRequest) ByteSize() uint64 { return 0 } // ResetStickyTaskListRequest is an internal type (TBD...) type ResetStickyTaskListRequest struct { Domain string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *ResetStickyTaskListRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetExecution is an internal getter (TBD...) func (v *ResetStickyTaskListRequest) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // Size returns the approximate memory used in bytes func (v *ResetStickyTaskListRequest) ByteSize() uint64 { return 0 } // ResetStickyTaskListResponse is an internal type (TBD...) type ResetStickyTaskListResponse struct { } // Size returns the approximate memory used in bytes func (v *ResetStickyTaskListResponse) ByteSize() uint64 { return 0 } // ResetWorkflowExecutionRequest is an internal type (TBD...) type ResetWorkflowExecutionRequest struct { Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Reason string `json:"reason,omitempty"` DecisionFinishEventID int64 `json:"decisionFinishEventId,omitempty"` RequestID string `json:"requestId,omitempty"` SkipSignalReapply bool `json:"skipSignalReapply,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *ResetWorkflowExecutionRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowExecution is an internal getter (TBD...) func (v *ResetWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // GetReason is an internal getter (TBD...) func (v *ResetWorkflowExecutionRequest) GetReason() (o string) { if v != nil { return v.Reason } return } // GetDecisionFinishEventID is an internal getter (TBD...) func (v *ResetWorkflowExecutionRequest) GetDecisionFinishEventID() (o int64) { if v != nil { return v.DecisionFinishEventID } return } // GetRequestID is an internal getter (TBD...) func (v *ResetWorkflowExecutionRequest) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // GetSkipSignalReapply is an internal getter (TBD...) func (v *ResetWorkflowExecutionRequest) GetSkipSignalReapply() (o bool) { if v != nil { return v.SkipSignalReapply } return } // Size returns the approximate memory used in bytes func (v *ResetWorkflowExecutionRequest) ByteSize() uint64 { return 0 } // ResetWorkflowExecutionResponse is an internal type (TBD...) type ResetWorkflowExecutionResponse struct { RunID string `json:"runId,omitempty"` } // GetRunID is an internal getter (TBD...) func (v *ResetWorkflowExecutionResponse) GetRunID() (o string) { if v != nil { return v.RunID } return } // Size returns the approximate memory used in bytes func (v *ResetWorkflowExecutionResponse) ByteSize() uint64 { return 0 } // RespondActivityTaskCanceledByIDRequest is an internal type (TBD...) type RespondActivityTaskCanceledByIDRequest struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowID,omitempty"` RunID string `json:"runID,omitempty"` ActivityID string `json:"activityID,omitempty"` Details []byte `json:"details,omitempty"` Identity string `json:"identity,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *RespondActivityTaskCanceledByIDRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowID is an internal getter (TBD...) func (v *RespondActivityTaskCanceledByIDRequest) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *RespondActivityTaskCanceledByIDRequest) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetActivityID is an internal getter (TBD...) func (v *RespondActivityTaskCanceledByIDRequest) GetActivityID() (o string) { if v != nil { return v.ActivityID } return } // GetIdentity is an internal getter (TBD...) func (v *RespondActivityTaskCanceledByIDRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // Size returns the approximate memory used in bytes func (v *RespondActivityTaskCanceledByIDRequest) ByteSize() uint64 { return 0 } // RespondActivityTaskCanceledRequest is an internal type (TBD...) type RespondActivityTaskCanceledRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Details []byte `json:"details,omitempty"` Identity string `json:"identity,omitempty"` } // GetIdentity is an internal getter (TBD...) func (v *RespondActivityTaskCanceledRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // Size returns the approximate memory used in bytes func (v *RespondActivityTaskCanceledRequest) ByteSize() uint64 { return 0 } // RespondActivityTaskCompletedByIDRequest is an internal type (TBD...) type RespondActivityTaskCompletedByIDRequest struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowID,omitempty"` RunID string `json:"runID,omitempty"` ActivityID string `json:"activityID,omitempty"` Result []byte `json:"result,omitempty"` Identity string `json:"identity,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *RespondActivityTaskCompletedByIDRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowID is an internal getter (TBD...) func (v *RespondActivityTaskCompletedByIDRequest) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *RespondActivityTaskCompletedByIDRequest) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetActivityID is an internal getter (TBD...) func (v *RespondActivityTaskCompletedByIDRequest) GetActivityID() (o string) { if v != nil { return v.ActivityID } return } // GetIdentity is an internal getter (TBD...) func (v *RespondActivityTaskCompletedByIDRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // Size returns the approximate memory used in bytes func (v *RespondActivityTaskCompletedByIDRequest) ByteSize() uint64 { return 0 } // RespondActivityTaskCompletedRequest is an internal type (TBD...) type RespondActivityTaskCompletedRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Result []byte `json:"result,omitempty"` Identity string `json:"identity,omitempty"` } // GetIdentity is an internal getter (TBD...) func (v *RespondActivityTaskCompletedRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // Size returns the approximate memory used in bytes func (v *RespondActivityTaskCompletedRequest) ByteSize() uint64 { return 0 } // RespondActivityTaskFailedByIDRequest is an internal type (TBD...) type RespondActivityTaskFailedByIDRequest struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowID,omitempty"` RunID string `json:"runID,omitempty"` ActivityID string `json:"activityID,omitempty"` Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` Identity string `json:"identity,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *RespondActivityTaskFailedByIDRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowID is an internal getter (TBD...) func (v *RespondActivityTaskFailedByIDRequest) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *RespondActivityTaskFailedByIDRequest) GetRunID() (o string) { if v != nil { return v.RunID } return } // GetActivityID is an internal getter (TBD...) func (v *RespondActivityTaskFailedByIDRequest) GetActivityID() (o string) { if v != nil { return v.ActivityID } return } // GetIdentity is an internal getter (TBD...) func (v *RespondActivityTaskFailedByIDRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // Size returns the approximate memory used in bytes func (v *RespondActivityTaskFailedByIDRequest) ByteSize() uint64 { return 0 } // RespondActivityTaskFailedRequest is an internal type (TBD...) type RespondActivityTaskFailedRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` Identity string `json:"identity,omitempty"` } // GetReason is an internal getter (TBD...) func (v *RespondActivityTaskFailedRequest) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // GetDetails is an internal getter (TBD...) func (v *RespondActivityTaskFailedRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // GetIdentity is an internal getter (TBD...) func (v *RespondActivityTaskFailedRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // Size returns the approximate memory used in bytes func (v *RespondActivityTaskFailedRequest) ByteSize() uint64 { return 0 } // RespondDecisionTaskCompletedRequest is an internal type (TBD...) type RespondDecisionTaskCompletedRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Decisions []*Decision `json:"decisions,omitempty"` ExecutionContext []byte `json:"executionContext,omitempty"` Identity string `json:"identity,omitempty"` StickyAttributes *StickyExecutionAttributes `json:"stickyAttributes,omitempty"` ReturnNewDecisionTask bool `json:"returnNewDecisionTask,omitempty"` ForceCreateNewDecisionTask bool `json:"forceCreateNewDecisionTask,omitempty"` BinaryChecksum string `json:"binaryChecksum,omitempty"` QueryResults map[string]*WorkflowQueryResult `json:"queryResults,omitempty"` } // GetIdentity is an internal getter (TBD...) func (v *RespondDecisionTaskCompletedRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // GetReturnNewDecisionTask is an internal getter (TBD...) func (v *RespondDecisionTaskCompletedRequest) GetReturnNewDecisionTask() (o bool) { if v != nil { return v.ReturnNewDecisionTask } return } // GetForceCreateNewDecisionTask is an internal getter (TBD...) func (v *RespondDecisionTaskCompletedRequest) GetForceCreateNewDecisionTask() (o bool) { if v != nil { return v.ForceCreateNewDecisionTask } return } // GetBinaryChecksum is an internal getter (TBD...) func (v *RespondDecisionTaskCompletedRequest) GetBinaryChecksum() (o string) { if v != nil { return v.BinaryChecksum } return } // GetQueryResults is an internal getter (TBD...) func (v *RespondDecisionTaskCompletedRequest) GetQueryResults() (o map[string]*WorkflowQueryResult) { if v != nil && v.QueryResults != nil { return v.QueryResults } return } // Size returns the approximate memory used in bytes func (v *RespondDecisionTaskCompletedRequest) ByteSize() uint64 { return 0 } // RespondDecisionTaskCompletedResponse is an internal type (TBD...) type RespondDecisionTaskCompletedResponse struct { DecisionTask *PollForDecisionTaskResponse `json:"decisionTask,omitempty"` ActivitiesToDispatchLocally map[string]*ActivityLocalDispatchInfo `json:"activitiesToDispatchLocally,omitempty"` } // GetDecisionTask is an internal getter (TBD...) func (v *RespondDecisionTaskCompletedResponse) GetDecisionTask() (o *PollForDecisionTaskResponse) { if v != nil && v.DecisionTask != nil { return v.DecisionTask } return } // Size returns the approximate memory used in bytes func (v *RespondDecisionTaskCompletedResponse) ByteSize() uint64 { return 0 } // RespondDecisionTaskFailedRequest is an internal type (TBD...) type RespondDecisionTaskFailedRequest struct { TaskToken []byte `json:"taskToken,omitempty"` Cause *DecisionTaskFailedCause `json:"cause,omitempty"` Details []byte `json:"details,omitempty"` Identity string `json:"identity,omitempty"` BinaryChecksum string `json:"binaryChecksum,omitempty"` } // GetCause is an internal getter (TBD...) func (v *RespondDecisionTaskFailedRequest) GetCause() (o DecisionTaskFailedCause) { if v != nil && v.Cause != nil { return *v.Cause } return } // GetIdentity is an internal getter (TBD...) func (v *RespondDecisionTaskFailedRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // GetBinaryChecksum is an internal getter (TBD...) func (v *RespondDecisionTaskFailedRequest) GetBinaryChecksum() (o string) { if v != nil { return v.BinaryChecksum } return } // RespondQueryTaskCompletedRequest is an internal type (TBD...) type RespondQueryTaskCompletedRequest struct { TaskToken []byte `json:"taskToken,omitempty"` CompletedType *QueryTaskCompletedType `json:"completedType,omitempty"` QueryResult []byte `json:"queryResult,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` WorkerVersionInfo *WorkerVersionInfo `json:"workerVersionInfo,omitempty"` } // GetCompletedType is an internal getter (TBD...) func (v *RespondQueryTaskCompletedRequest) GetCompletedType() (o QueryTaskCompletedType) { if v != nil && v.CompletedType != nil { return *v.CompletedType } return } // GetQueryResult is an internal getter (TBD...) func (v *RespondQueryTaskCompletedRequest) GetQueryResult() (o []byte) { if v != nil && v.QueryResult != nil { return v.QueryResult } return } // GetErrorMessage is an internal getter (TBD...) func (v *RespondQueryTaskCompletedRequest) GetErrorMessage() (o string) { if v != nil { return v.ErrorMessage } return } // GetWorkerVersionInfo is an internal getter (TBD...) func (v *RespondQueryTaskCompletedRequest) GetWorkerVersionInfo() (o *WorkerVersionInfo) { if v != nil && v.WorkerVersionInfo != nil { return v.WorkerVersionInfo } return } // RetryPolicy is an internal type (TBD...) type RetryPolicy struct { InitialIntervalInSeconds int32 `json:"initialIntervalInSeconds,omitempty"` BackoffCoefficient float64 `json:"backoffCoefficient,omitempty"` MaximumIntervalInSeconds int32 `json:"maximumIntervalInSeconds,omitempty"` MaximumAttempts int32 `json:"maximumAttempts,omitempty"` NonRetriableErrorReasons []string `json:"nonRetriableErrorReasons,omitempty"` ExpirationIntervalInSeconds int32 `json:"expirationIntervalInSeconds,omitempty"` } // GetInitialIntervalInSeconds is an internal getter (TBD...) func (v *RetryPolicy) GetInitialIntervalInSeconds() (o int32) { if v != nil { return v.InitialIntervalInSeconds } return } // GetBackoffCoefficient is an internal getter (TBD...) func (v *RetryPolicy) GetBackoffCoefficient() (o float64) { if v != nil { return v.BackoffCoefficient } return } // GetMaximumIntervalInSeconds is an internal getter (TBD...) func (v *RetryPolicy) GetMaximumIntervalInSeconds() (o int32) { if v != nil { return v.MaximumIntervalInSeconds } return } // GetMaximumAttempts is an internal getter (TBD...) func (v *RetryPolicy) GetMaximumAttempts() (o int32) { if v != nil { return v.MaximumAttempts } return } // GetNonRetriableErrorReasons is an internal getter (TBD...) func (v *RetryPolicy) GetNonRetriableErrorReasons() (o []string) { if v != nil && v.NonRetriableErrorReasons != nil { return v.NonRetriableErrorReasons } return } // GetExpirationIntervalInSeconds is an internal getter (TBD...) func (v *RetryPolicy) GetExpirationIntervalInSeconds() (o int32) { if v != nil { return v.ExpirationIntervalInSeconds } return } // RetryTaskV2Error is an internal type (TBD...) type RetryTaskV2Error struct { Message string `json:"message,required"` DomainID string `json:"domainId,omitempty"` WorkflowID string `json:"workflowId,omitempty"` RunID string `json:"runId,omitempty"` StartEventID *int64 `json:"startEventId,omitempty"` StartEventVersion *int64 `json:"startEventVersion,omitempty"` EndEventID *int64 `json:"endEventId,omitempty"` EndEventVersion *int64 `json:"endEventVersion,omitempty"` } // GetDomainID is an internal getter (TBD...) func (v *RetryTaskV2Error) GetDomainID() (o string) { if v != nil { return v.DomainID } return } // GetWorkflowID is an internal getter (TBD...) func (v *RetryTaskV2Error) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *RetryTaskV2Error) GetRunID() (o string) { if v != nil { return v.RunID } return } // ScheduleActivityTaskDecisionAttributes is an internal type (TBD...) type ScheduleActivityTaskDecisionAttributes struct { ActivityID string `json:"activityId,omitempty"` ActivityType *ActivityType `json:"activityType,omitempty"` Domain string `json:"domain,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ScheduleToCloseTimeoutSeconds *int32 `json:"scheduleToCloseTimeoutSeconds,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` StartToCloseTimeoutSeconds *int32 `json:"startToCloseTimeoutSeconds,omitempty"` HeartbeatTimeoutSeconds *int32 `json:"heartbeatTimeoutSeconds,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` Header *Header `json:"header,omitempty"` RequestLocalDispatch bool `json:"requestLocalDispatch,omitempty"` } // GetActivityID is an internal getter (TBD...) func (v *ScheduleActivityTaskDecisionAttributes) GetActivityID() (o string) { if v != nil { return v.ActivityID } return } // GetActivityType is an internal getter (TBD...) func (v *ScheduleActivityTaskDecisionAttributes) GetActivityType() (o *ActivityType) { if v != nil && v.ActivityType != nil { return v.ActivityType } return } // GetDomain is an internal getter (TBD...) func (v *ScheduleActivityTaskDecisionAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetScheduleToCloseTimeoutSeconds is an internal getter (TBD...) func (v *ScheduleActivityTaskDecisionAttributes) GetScheduleToCloseTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToCloseTimeoutSeconds != nil { return *v.ScheduleToCloseTimeoutSeconds } return } // GetScheduleToStartTimeoutSeconds is an internal getter (TBD...) func (v *ScheduleActivityTaskDecisionAttributes) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // GetStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *ScheduleActivityTaskDecisionAttributes) GetStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.StartToCloseTimeoutSeconds != nil { return *v.StartToCloseTimeoutSeconds } return } // GetHeartbeatTimeoutSeconds is an internal getter (TBD...) func (v *ScheduleActivityTaskDecisionAttributes) GetHeartbeatTimeoutSeconds() (o int32) { if v != nil && v.HeartbeatTimeoutSeconds != nil { return *v.HeartbeatTimeoutSeconds } return } // SearchAttributes is an internal type (TBD...) type SearchAttributes struct { IndexedFields map[string][]byte `json:"indexedFields,omitempty"` } // GetIndexedFields is an internal getter (TBD...) func (v *SearchAttributes) GetIndexedFields() (o map[string][]byte) { if v != nil && v.IndexedFields != nil { return v.IndexedFields } return } // ServiceBusyError is an internal type (TBD...) type ServiceBusyError struct { Message string `json:"message,required"` Reason string `json:"reason,omitempty"` } // SignalExternalWorkflowExecutionDecisionAttributes is an internal type (TBD...) type SignalExternalWorkflowExecutionDecisionAttributes struct { Domain string `json:"domain,omitempty"` Execution *WorkflowExecution `json:"execution,omitempty"` SignalName string `json:"signalName,omitempty"` Input []byte `json:"input,omitempty"` Control []byte `json:"control,omitempty"` ChildWorkflowOnly bool `json:"childWorkflowOnly,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *SignalExternalWorkflowExecutionDecisionAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetSignalName is an internal getter (TBD...) func (v *SignalExternalWorkflowExecutionDecisionAttributes) GetSignalName() (o string) { if v != nil { return v.SignalName } return } // SignalExternalWorkflowExecutionFailedCause is an internal type (TBD...) type SignalExternalWorkflowExecutionFailedCause int32 // Ptr is a helper function for getting pointer value func (e SignalExternalWorkflowExecutionFailedCause) Ptr() *SignalExternalWorkflowExecutionFailedCause { return &e } // String returns a readable string representation of SignalExternalWorkflowExecutionFailedCause. func (e SignalExternalWorkflowExecutionFailedCause) String() string { w := int32(e) switch w { case 0: return "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION" case 1: return "WORKFLOW_ALREADY_COMPLETED" } return fmt.Sprintf("SignalExternalWorkflowExecutionFailedCause(%d)", w) } // UnmarshalText parses enum value from string representation func (e *SignalExternalWorkflowExecutionFailedCause) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION": *e = SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution return nil case "WORKFLOW_ALREADY_COMPLETED": *e = SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "SignalExternalWorkflowExecutionFailedCause", err) } *e = SignalExternalWorkflowExecutionFailedCause(val) return nil } } // MarshalText encodes SignalExternalWorkflowExecutionFailedCause to text. func (e SignalExternalWorkflowExecutionFailedCause) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution is an option for SignalExternalWorkflowExecutionFailedCause SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution SignalExternalWorkflowExecutionFailedCause = iota SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted ) // SignalExternalWorkflowExecutionFailedEventAttributes is an internal type (TBD...) type SignalExternalWorkflowExecutionFailedEventAttributes struct { Cause *SignalExternalWorkflowExecutionFailedCause `json:"cause,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` InitiatedEventID int64 `json:"initiatedEventId,omitempty"` Control []byte `json:"control,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *SignalExternalWorkflowExecutionFailedEventAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetInitiatedEventID is an internal getter (TBD...) func (v *SignalExternalWorkflowExecutionFailedEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // Size returns the approximate memory used in bytes func (v *SignalExternalWorkflowExecutionFailedEventAttributes) ByteSize() uint64 { return 0 } // SignalExternalWorkflowExecutionInitiatedEventAttributes is an internal type (TBD...) type SignalExternalWorkflowExecutionInitiatedEventAttributes struct { DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` SignalName string `json:"signalName,omitempty"` Input []byte `json:"input,omitempty"` Control []byte `json:"control,omitempty"` ChildWorkflowOnly bool `json:"childWorkflowOnly,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowExecution is an internal getter (TBD...) func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // GetSignalName is an internal getter (TBD...) func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetSignalName() (o string) { if v != nil { return v.SignalName } return } // GetChildWorkflowOnly is an internal getter (TBD...) func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) GetChildWorkflowOnly() (o bool) { if v != nil { return v.ChildWorkflowOnly } return } // Size returns the approximate memory used in bytes func (v *SignalExternalWorkflowExecutionInitiatedEventAttributes) ByteSize() uint64 { return 0 } // SignalWithStartWorkflowExecutionRequest is an internal type (TBD...) type SignalWithStartWorkflowExecutionRequest struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"-"` // Filtering PII ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` Identity string `json:"identity,omitempty"` RequestID string `json:"requestId,omitempty"` WorkflowIDReusePolicy *WorkflowIDReusePolicy `json:"workflowIdReusePolicy,omitempty"` SignalName string `json:"signalName,omitempty"` SignalInput []byte `json:"-"` // Filtering PII Control []byte `json:"control,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` CronSchedule string `json:"cronSchedule,omitempty"` Memo *Memo `json:"-"` // Filtering PII SearchAttributes *SearchAttributes `json:"-"` // Filtering PII Header *Header `json:"header,omitempty"` DelayStartSeconds *int32 `json:"delayStartSeconds,omitempty"` JitterStartSeconds *int32 `json:"jitterStartSeconds,omitempty"` FirstRunAtTimestamp *int64 `json:"firstRunAtTimestamp,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowID is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetWorkflowType is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // GetTaskList is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetTaskList() (o *TaskList) { if v != nil && v.TaskList != nil { return v.TaskList } return } // GetExecutionStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // GetTaskStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // GetIdentity is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // GetRequestID is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // GetWorkflowIDReusePolicy is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetWorkflowIDReusePolicy() (o WorkflowIDReusePolicy) { if v != nil && v.WorkflowIDReusePolicy != nil { return *v.WorkflowIDReusePolicy } return } // GetSignalName is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetSignalName() (o string) { if v != nil { return v.SignalName } return } // GetSignalInput is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetSignalInput() (o []byte) { if v != nil && v.SignalInput != nil { return v.SignalInput } return } // GetCronSchedule is an internal getter (TBD...) func (v *SignalWithStartWorkflowExecutionRequest) GetCronSchedule() (o string) { if v != nil { return v.CronSchedule } return } // SignalWithStartWorkflowExecutionAsyncRequest is an internal type (TBD...) type SignalWithStartWorkflowExecutionAsyncRequest struct { *SignalWithStartWorkflowExecutionRequest } // SignalWithStartWorkflowExecutionAsyncResponse is an internal type (TBD...) type SignalWithStartWorkflowExecutionAsyncResponse struct { } // SignalWorkflowExecutionRequest is an internal type (TBD...) type SignalWorkflowExecutionRequest struct { Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` SignalName string `json:"signalName,omitempty"` Input []byte `json:"-"` // Filtering PII Identity string `json:"identity,omitempty"` RequestID string `json:"requestId,omitempty"` Control []byte `json:"control,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *SignalWorkflowExecutionRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowExecution is an internal getter (TBD...) func (v *SignalWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // GetSignalName is an internal getter (TBD...) func (v *SignalWorkflowExecutionRequest) GetSignalName() (o string) { if v != nil { return v.SignalName } return } // GetInput is an internal getter (TBD...) func (v *SignalWorkflowExecutionRequest) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // GetIdentity is an internal getter (TBD...) func (v *SignalWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // GetRequestID is an internal getter (TBD...) func (v *SignalWorkflowExecutionRequest) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // StartChildWorkflowExecutionDecisionAttributes is an internal type (TBD...) type StartChildWorkflowExecutionDecisionAttributes struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` ParentClosePolicy *ParentClosePolicy `json:"parentClosePolicy,omitempty"` Control []byte `json:"control,omitempty"` WorkflowIDReusePolicy *WorkflowIDReusePolicy `json:"workflowIdReusePolicy,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` CronSchedule string `json:"cronSchedule,omitempty"` Header *Header `json:"header,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *StartChildWorkflowExecutionDecisionAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowID is an internal getter (TBD...) func (v *StartChildWorkflowExecutionDecisionAttributes) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetExecutionStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *StartChildWorkflowExecutionDecisionAttributes) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // GetTaskStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *StartChildWorkflowExecutionDecisionAttributes) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // GetParentClosePolicy is an internal getter (TBD...) func (v *StartChildWorkflowExecutionDecisionAttributes) GetParentClosePolicy() (o ParentClosePolicy) { if v != nil && v.ParentClosePolicy != nil { return *v.ParentClosePolicy } return } // GetCronSchedule is an internal getter (TBD...) func (v *StartChildWorkflowExecutionDecisionAttributes) GetCronSchedule() (o string) { if v != nil { return v.CronSchedule } return } // StartChildWorkflowExecutionFailedEventAttributes is an internal type (TBD...) type StartChildWorkflowExecutionFailedEventAttributes struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` Cause *ChildWorkflowExecutionFailedCause `json:"cause,omitempty"` Control []byte `json:"control,omitempty"` InitiatedEventID int64 `json:"initiatedEventId,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *StartChildWorkflowExecutionFailedEventAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetInitiatedEventID is an internal getter (TBD...) func (v *StartChildWorkflowExecutionFailedEventAttributes) GetInitiatedEventID() (o int64) { if v != nil { return v.InitiatedEventID } return } // Size returns the approximate memory used in bytes func (v *StartChildWorkflowExecutionFailedEventAttributes) ByteSize() uint64 { return 0 } // StartChildWorkflowExecutionInitiatedEventAttributes is an internal type (TBD...) type StartChildWorkflowExecutionInitiatedEventAttributes struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` ParentClosePolicy *ParentClosePolicy `json:"parentClosePolicy,omitempty"` Control []byte `json:"control,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` WorkflowIDReusePolicy *WorkflowIDReusePolicy `json:"workflowIdReusePolicy,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` CronSchedule string `json:"cronSchedule,omitempty"` Header *Header `json:"header,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` DelayStartSeconds *int32 `json:"delayStartSeconds,omitempty"` JitterStartSeconds *int32 `json:"jitterStartSeconds,omitempty"` FirstRunAtTimestamp *int64 `json:"firstRunAtTimestamp,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowID is an internal getter (TBD...) func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetWorkflowType is an internal getter (TBD...) func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetWorkflowType() (o *WorkflowType) { if v != nil && v.WorkflowType != nil { return v.WorkflowType } return } // GetParentClosePolicy is an internal getter (TBD...) func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetParentClosePolicy() (o ParentClosePolicy) { if v != nil && v.ParentClosePolicy != nil { return *v.ParentClosePolicy } return } // GetExecutionStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *StartChildWorkflowExecutionInitiatedEventAttributes) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // Size returns the approximate memory used in bytes func (v *StartChildWorkflowExecutionInitiatedEventAttributes) ByteSize() uint64 { return 0 } // StartTimeFilter is an internal type (TBD...) type StartTimeFilter struct { EarliestTime *int64 `json:"earliestTime,omitempty"` LatestTime *int64 `json:"latestTime,omitempty"` } // GetEarliestTime is an internal getter (TBD...) func (v *StartTimeFilter) GetEarliestTime() (o int64) { if v != nil && v.EarliestTime != nil { return *v.EarliestTime } return } // GetLatestTime is an internal getter (TBD...) func (v *StartTimeFilter) GetLatestTime() (o int64) { if v != nil && v.LatestTime != nil { return *v.LatestTime } return } // StartTimerDecisionAttributes is an internal type (TBD...) type StartTimerDecisionAttributes struct { TimerID string `json:"timerId,omitempty"` StartToFireTimeoutSeconds *int64 `json:"startToFireTimeoutSeconds,omitempty"` } // GetTimerID is an internal getter (TBD...) func (v *StartTimerDecisionAttributes) GetTimerID() (o string) { if v != nil { return v.TimerID } return } // GetStartToFireTimeoutSeconds is an internal getter (TBD...) func (v *StartTimerDecisionAttributes) GetStartToFireTimeoutSeconds() (o int64) { if v != nil && v.StartToFireTimeoutSeconds != nil { return *v.StartToFireTimeoutSeconds } return } type RestartWorkflowExecutionRequest struct { Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Identity string `json:"identity,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *RestartWorkflowExecutionRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowExecution is an internal getter (TBD...) func (v *RestartWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } type DiagnoseWorkflowExecutionRequest struct { Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Identity string `json:"identity,omitempty"` } // GetDomain returns the domain func (v *DiagnoseWorkflowExecutionRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowExecution returns the workflow execution func (v *DiagnoseWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // GetIdentity returns the identity func (v *DiagnoseWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } type DiagnoseWorkflowExecutionResponse struct { Domain string `json:"domain,omitempty"` DiagnosticWorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` } // GetDomain returns the fomain func (v *DiagnoseWorkflowExecutionResponse) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetDiagnosticWorkflowExecution returns the workflow execution func (v *DiagnoseWorkflowExecutionResponse) GetDiagnosticWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.DiagnosticWorkflowExecution != nil { return v.DiagnosticWorkflowExecution } return } // StartWorkflowExecutionRequest is an internal type (TBD...) type StartWorkflowExecutionRequest struct { Domain string `json:"domain,omitempty"` WorkflowID string `json:"workflowId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"-"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` Identity string `json:"identity,omitempty"` RequestID string `json:"requestId,omitempty"` WorkflowIDReusePolicy *WorkflowIDReusePolicy `json:"workflowIdReusePolicy,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` CronSchedule string `json:"cronSchedule,omitempty"` Memo *Memo `json:"-"` SearchAttributes *SearchAttributes `json:"-"` Header *Header `json:"header,omitempty"` DelayStartSeconds *int32 `json:"delayStartSeconds,omitempty"` JitterStartSeconds *int32 `json:"jitterStartSeconds,omitempty"` FirstRunAtTimeStamp *int64 `json:"firstRunAtTimeStamp,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowID is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetExecutionStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // GetTaskStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // GetDelayStartSeconds is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetDelayStartSeconds() (o int32) { if v != nil && v.DelayStartSeconds != nil { return *v.DelayStartSeconds } return } // GetJitterStartSeconds is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetJitterStartSeconds() (o int32) { if v != nil && v.JitterStartSeconds != nil { return *v.JitterStartSeconds } return } // GetFirstRunAtTimeStamp is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetFirstRunAtTimeStamp() (o int64) { if v != nil && v.FirstRunAtTimeStamp != nil { return *v.FirstRunAtTimeStamp } return } // GetRequestID is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // GetWorkflowIDReusePolicy is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetWorkflowIDReusePolicy() (o WorkflowIDReusePolicy) { if v != nil && v.WorkflowIDReusePolicy != nil { return *v.WorkflowIDReusePolicy } return } // GetCronSchedule is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetCronSchedule() (o string) { if v != nil { return v.CronSchedule } return } // GetCronOverlapPolicy is an internal getter (TBD...) func (v *StartWorkflowExecutionRequest) GetCronOverlapPolicy() (o CronOverlapPolicy) { if v != nil && v.CronOverlapPolicy != nil { return *v.CronOverlapPolicy } return } // StartWorkflowExecutionResponse is an internal type (TBD...) type StartWorkflowExecutionResponse struct { RunID string `json:"runId,omitempty"` } // GetRunID is an internal getter (TBD...) func (v *StartWorkflowExecutionResponse) GetRunID() (o string) { if v != nil { return v.RunID } return } type StartWorkflowExecutionAsyncRequest struct { *StartWorkflowExecutionRequest } type StartWorkflowExecutionAsyncResponse struct { } // RestartWorkflowExecutionResponse is an internal type (TBD...) type RestartWorkflowExecutionResponse struct { RunID string `json:"runId,omitempty"` } // GetRunID is an internal getter (TBD...) func (v *RestartWorkflowExecutionResponse) GetRunID() (o string) { if v != nil { return v.RunID } return } // StickyExecutionAttributes is an internal type (TBD...) type StickyExecutionAttributes struct { WorkerTaskList *TaskList `json:"workerTaskList,omitempty"` ScheduleToStartTimeoutSeconds *int32 `json:"scheduleToStartTimeoutSeconds,omitempty"` } // GetScheduleToStartTimeoutSeconds is an internal getter (TBD...) func (v *StickyExecutionAttributes) GetScheduleToStartTimeoutSeconds() (o int32) { if v != nil && v.ScheduleToStartTimeoutSeconds != nil { return *v.ScheduleToStartTimeoutSeconds } return } // SupportedClientVersions is an internal type (TBD...) type SupportedClientVersions struct { GoSdk string `json:"goSdk,omitempty"` JavaSdk string `json:"javaSdk,omitempty"` } // TaskIDBlock is an internal type (TBD...) type TaskIDBlock struct { StartID int64 `json:"startID,omitempty"` EndID int64 `json:"endID,omitempty"` } // GetStartID is an internal getter (TBD...) func (v *TaskIDBlock) GetStartID() (o int64) { if v != nil { return v.StartID } return } // GetEndID is an internal getter (TBD...) func (v *TaskIDBlock) GetEndID() (o int64) { if v != nil { return v.EndID } return } // TaskList is an internal type (TBD...) type TaskList struct { Name string `json:"name,omitempty"` Kind *TaskListKind `json:"kind,omitempty"` } // GetName is an internal getter (TBD...) func (v *TaskList) GetName() (o string) { if v != nil { return v.Name } return } // GetKind is an internal getter (TBD...) func (v *TaskList) GetKind() (o TaskListKind) { if v != nil && v.Kind != nil { return *v.Kind } return } // TaskListKind is an internal type (TBD...) type TaskListKind int32 // Ptr is a helper function for getting pointer value func (e TaskListKind) Ptr() *TaskListKind { return &e } // String returns a readable string representation of TaskListKind. func (e TaskListKind) String() string { w := int32(e) switch w { case 0: return "NORMAL" case 1: return "STICKY" case 2: return "EPHEMERAL" } return fmt.Sprintf("TaskListKind(%d)", w) } // UnmarshalText parses enum value from string representation func (e *TaskListKind) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "NORMAL": *e = TaskListKindNormal return nil case "STICKY": *e = TaskListKindSticky return nil case "EPHEMERAL": *e = TaskListKindEphemeral return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "TaskListKind", err) } *e = TaskListKind(val) return nil } } // MarshalText encodes TaskListKind to text. func (e TaskListKind) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // TaskListKindNormal is an option for TaskListKind TaskListKindNormal TaskListKind = iota // TaskListKindSticky is an option for TaskListKind TaskListKindSticky // TaskListKindEphemeral is an option for TaskListKind TaskListKindEphemeral ) // TaskListMetadata is an internal type (TBD...) type TaskListMetadata struct { MaxTasksPerSecond *float64 `json:"maxTasksPerSecond,omitempty"` } // TaskListPartitionMetadata is an internal type (TBD...) type TaskListPartitionMetadata struct { Key string `json:"key,omitempty"` OwnerHostName string `json:"ownerHostName,omitempty"` } // GetKey is an internal getter (TBD...) func (v *TaskListPartitionMetadata) GetKey() (o string) { if v != nil { return v.Key } return } // GetOwnerHostName is an internal getter (TBD...) func (v *TaskListPartitionMetadata) GetOwnerHostName() (o string) { if v != nil { return v.OwnerHostName } return } type IsolationGroupMetrics struct { NewTasksPerSecond float64 `json:"newTasksPerSecond,omitempty"` PollerCount int64 `json:"pollerCount,omitempty"` } // TaskListStatus is an internal type (TBD...) type TaskListStatus struct { BacklogCountHint int64 `json:"backlogCountHint,omitempty"` ReadLevel int64 `json:"readLevel,omitempty"` AckLevel int64 `json:"ackLevel,omitempty"` RatePerSecond float64 `json:"ratePerSecond,omitempty"` TaskIDBlock *TaskIDBlock `json:"taskIDBlock,omitempty"` IsolationGroupMetrics map[string]*IsolationGroupMetrics `json:"isolationGroupMetrics,omitempty"` NewTasksPerSecond float64 `json:"newTasksPerSecond,omitempty"` Empty bool `json:"empty,omitempty"` } // GetBacklogCountHint is an internal getter (TBD...) func (v *TaskListStatus) GetBacklogCountHint() (o int64) { if v != nil { return v.BacklogCountHint } return } // GetReadLevel is an internal getter (TBD...) func (v *TaskListStatus) GetReadLevel() (o int64) { if v != nil { return v.ReadLevel } return } // GetAckLevel is an internal getter (TBD...) func (v *TaskListStatus) GetAckLevel() (o int64) { if v != nil { return v.AckLevel } return } // GetRatePerSecond is an internal getter (TBD...) func (v *TaskListStatus) GetRatePerSecond() (o float64) { if v != nil { return v.RatePerSecond } return } // GetTaskIDBlock is an internal getter (TBD...) func (v *TaskListStatus) GetTaskIDBlock() (o *TaskIDBlock) { if v != nil && v.TaskIDBlock != nil { return v.TaskIDBlock } return } // TaskListType is an internal type (TBD...) type TaskListType int32 // Ptr is a helper function for getting pointer value func (e TaskListType) Ptr() *TaskListType { return &e } // String returns a readable string representation of TaskListType. func (e TaskListType) String() string { w := int32(e) switch w { case 0: return "Decision" case 1: return "Activity" } return fmt.Sprintf("TaskListType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *TaskListType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "DECISION": *e = TaskListTypeDecision return nil case "ACTIVITY": *e = TaskListTypeActivity return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "TaskListType", err) } *e = TaskListType(val) return nil } } // MarshalText encodes TaskListType to text. func (e TaskListType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // TaskListTypeDecision is an option for TaskListType TaskListTypeDecision TaskListType = iota // TaskListTypeActivity is an option for TaskListType TaskListTypeActivity ) // TerminateWorkflowExecutionRequest is an internal type (TBD...) type TerminateWorkflowExecutionRequest struct { Domain string `json:"domain,omitempty"` WorkflowExecution *WorkflowExecution `json:"workflowExecution,omitempty"` Reason string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` Identity string `json:"identity,omitempty"` FirstExecutionRunID string `json:"first_execution_run_id,omitempty"` } // GetDomain is an internal getter (TBD...) func (v *TerminateWorkflowExecutionRequest) GetDomain() (o string) { if v != nil { return v.Domain } return } // GetWorkflowExecution is an internal getter (TBD...) func (v *TerminateWorkflowExecutionRequest) GetWorkflowExecution() (o *WorkflowExecution) { if v != nil && v.WorkflowExecution != nil { return v.WorkflowExecution } return } // GetReason is an internal getter (TBD...) func (v *TerminateWorkflowExecutionRequest) GetReason() (o string) { if v != nil { return v.Reason } return } // GetDetails is an internal getter (TBD...) func (v *TerminateWorkflowExecutionRequest) GetDetails() (o []byte) { if v != nil && v.Details != nil { return v.Details } return } // GetIdentity is an internal getter (TBD...) func (v *TerminateWorkflowExecutionRequest) GetIdentity() (o string) { if v != nil { return v.Identity } return } // GetFirstExecutionRunID is an internal getter (TBD...) func (v *TerminateWorkflowExecutionRequest) GetFirstExecutionRunID() (o string) { if v != nil { return v.FirstExecutionRunID } return } // TimeoutType is an internal type (TBD...) type TimeoutType int32 // Ptr is a helper function for getting pointer value func (e TimeoutType) Ptr() *TimeoutType { return &e } // String returns a readable string representation of TimeoutType. func (e TimeoutType) String() string { w := int32(e) switch w { case 0: return "START_TO_CLOSE" case 1: return "SCHEDULE_TO_START" case 2: return "SCHEDULE_TO_CLOSE" case 3: return "HEARTBEAT" } return fmt.Sprintf("TimeoutType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *TimeoutType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "START_TO_CLOSE": *e = TimeoutTypeStartToClose return nil case "SCHEDULE_TO_START": *e = TimeoutTypeScheduleToStart return nil case "SCHEDULE_TO_CLOSE": *e = TimeoutTypeScheduleToClose return nil case "HEARTBEAT": *e = TimeoutTypeHeartbeat return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "TimeoutType", err) } *e = TimeoutType(val) return nil } } // MarshalText encodes TimeoutType to text. func (e TimeoutType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // TimeoutTypeStartToClose is an option for TimeoutType TimeoutTypeStartToClose TimeoutType = iota // TimeoutTypeScheduleToStart is an option for TimeoutType TimeoutTypeScheduleToStart // TimeoutTypeScheduleToClose is an option for TimeoutType TimeoutTypeScheduleToClose // TimeoutTypeHeartbeat is an option for TimeoutType TimeoutTypeHeartbeat ) // TimerCanceledEventAttributes is an internal type (TBD...) type TimerCanceledEventAttributes struct { TimerID string `json:"timerId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` Identity string `json:"identity,omitempty"` } // GetTimerID is an internal getter (TBD...) func (v *TimerCanceledEventAttributes) GetTimerID() (o string) { if v != nil { return v.TimerID } return } // Size returns the approximate memory used in bytes func (v *TimerCanceledEventAttributes) ByteSize() uint64 { return 0 } // TimerFiredEventAttributes is an internal type (TBD...) type TimerFiredEventAttributes struct { TimerID string `json:"timerId,omitempty"` StartedEventID int64 `json:"startedEventId,omitempty"` } // GetTimerID is an internal getter (TBD...) func (v *TimerFiredEventAttributes) GetTimerID() (o string) { if v != nil { return v.TimerID } return } // GetStartedEventID is an internal getter (TBD...) func (v *TimerFiredEventAttributes) GetStartedEventID() (o int64) { if v != nil { return v.StartedEventID } return } // Size returns the approximate memory used in bytes func (v *TimerFiredEventAttributes) ByteSize() uint64 { return 0 } // TimerStartedEventAttributes is an internal type (TBD...) type TimerStartedEventAttributes struct { TimerID string `json:"timerId,omitempty"` StartToFireTimeoutSeconds *int64 `json:"startToFireTimeoutSeconds,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` } // GetTimerID is an internal getter (TBD...) func (v *TimerStartedEventAttributes) GetTimerID() (o string) { if v != nil { return v.TimerID } return } // GetStartToFireTimeoutSeconds is an internal getter (TBD...) func (v *TimerStartedEventAttributes) GetStartToFireTimeoutSeconds() (o int64) { if v != nil && v.StartToFireTimeoutSeconds != nil { return *v.StartToFireTimeoutSeconds } return } // Size returns the approximate memory used in bytes func (v *TimerStartedEventAttributes) ByteSize() uint64 { return 0 } // TransientDecisionInfo is an internal type (TBD...) type TransientDecisionInfo struct { ScheduledEvent *HistoryEvent `json:"scheduledEvent,omitempty"` StartedEvent *HistoryEvent `json:"startedEvent,omitempty"` } // UpdateDomainRequest is an internal type (TBD...) type UpdateDomainRequest struct { Name string `json:"name,omitempty"` Description *string `json:"description,omitempty"` OwnerEmail *string `json:"ownerEmail,omitempty"` Data map[string]string `json:"data,omitempty"` WorkflowExecutionRetentionPeriodInDays *int32 `json:"workflowExecutionRetentionPeriodInDays,omitempty"` EmitMetric *bool `json:"emitMetric,omitempty"` BadBinaries *BadBinaries `json:"badBinaries,omitempty"` HistoryArchivalStatus *ArchivalStatus `json:"historyArchivalStatus,omitempty"` HistoryArchivalURI *string `json:"historyArchivalURI,omitempty"` VisibilityArchivalStatus *ArchivalStatus `json:"visibilityArchivalStatus,omitempty"` VisibilityArchivalURI *string `json:"visibilityArchivalURI,omitempty"` ActiveClusterName *string `json:"activeClusterName,omitempty"` ActiveClusters *ActiveClusters `json:"activeClusters,omitempty"` Clusters []*ClusterReplicationConfiguration `json:"clusters,omitempty"` SecurityToken string `json:"securityToken,omitempty"` DeleteBadBinary *string `json:"deleteBadBinary,omitempty"` FailoverTimeoutInSeconds *int32 `json:"failoverTimeoutInSeconds,omitempty"` FailoverReason *string `json:"failoverReason,omitempty"` } // IsAFailoverRequest identifies if any part of the request is a failover request // and if so, will return true // this includes: // // - an active cluster change (force failver) // - any failvoer timeout values (for graceful failover) // - or a change to one of the domain's cluster-attribute fields (active-active failover) // // this is not a validation function // and doesn't attempt to give valid or coherent combinations func (v *UpdateDomainRequest) IsAFailoverRequest() bool { return v.ActiveClusterName != nil || (v.FailoverTimeoutInSeconds != nil && *v.FailoverTimeoutInSeconds > 0) || (v.ActiveClusters != nil && len(v.ActiveClusters.AttributeScopes) > 0) } // GetName is an internal getter (TBD...) func (v *UpdateDomainRequest) GetName() (o string) { if v != nil { return v.Name } return } // GetFailoverTimeoutInSeconds is an internal getter (TBD...) func (v *UpdateDomainRequest) GetFailoverTimeoutInSeconds() (o int32) { if v != nil && v.FailoverTimeoutInSeconds != nil { return *v.FailoverTimeoutInSeconds } return } // GetHistoryArchivalURI is an internal getter (TBD...) func (v *UpdateDomainRequest) GetHistoryArchivalURI() (o string) { if v != nil && v.HistoryArchivalURI != nil { return *v.HistoryArchivalURI } return } // GetVisibilityArchivalURI is an internal getter (TBD...) func (v *UpdateDomainRequest) GetVisibilityArchivalURI() (o string) { if v != nil && v.VisibilityArchivalURI != nil { return *v.VisibilityArchivalURI } return } // UpdateDomainResponse is an internal type (TBD...) type UpdateDomainResponse struct { DomainInfo *DomainInfo `json:"domainInfo,omitempty"` Configuration *DomainConfiguration `json:"configuration,omitempty"` ReplicationConfiguration *DomainReplicationConfiguration `json:"replicationConfiguration,omitempty"` FailoverVersion int64 `json:"failoverVersion,omitempty"` IsGlobalDomain bool `json:"isGlobalDomain,omitempty"` } func (v *UpdateDomainResponse) ToFailoverDomainResponse() *FailoverDomainResponse { if v == nil { return nil } return &FailoverDomainResponse{ DomainInfo: v.DomainInfo, Configuration: v.Configuration, ReplicationConfiguration: v.ReplicationConfiguration, FailoverVersion: v.FailoverVersion, IsGlobalDomain: v.IsGlobalDomain, } } // GetDomainInfo is an internal getter (TBD...) func (v *UpdateDomainResponse) GetDomainInfo() (o *DomainInfo) { if v != nil && v.DomainInfo != nil { return v.DomainInfo } return } // GetFailoverVersion is an internal getter (TBD...) func (v *UpdateDomainResponse) GetFailoverVersion() (o int64) { if v != nil { return v.FailoverVersion } return } // GetIsGlobalDomain is an internal getter (TBD...) func (v *UpdateDomainResponse) GetIsGlobalDomain() (o bool) { if v != nil { return v.IsGlobalDomain } return } // UpsertWorkflowSearchAttributesDecisionAttributes is an internal type (TBD...) type UpsertWorkflowSearchAttributesDecisionAttributes struct { SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` } // GetSearchAttributes is an internal getter (TBD...) func (v *UpsertWorkflowSearchAttributesDecisionAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // UpsertWorkflowSearchAttributesEventAttributes is an internal type (TBD...) type UpsertWorkflowSearchAttributesEventAttributes struct { DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` } // GetSearchAttributes is an internal getter (TBD...) func (v *UpsertWorkflowSearchAttributesEventAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // Size returns the approximate memory used in bytes func (v *UpsertWorkflowSearchAttributesEventAttributes) ByteSize() uint64 { return 0 } // VersionHistories is an internal type (TBD...) type VersionHistories struct { CurrentVersionHistoryIndex int32 `json:"currentVersionHistoryIndex,omitempty"` Histories []*VersionHistory `json:"histories,omitempty"` } // GetCurrentVersionHistoryIndex is an internal getter (TBD...) func (v *VersionHistories) GetCurrentVersionHistoryIndex() (o int32) { if v != nil { return v.CurrentVersionHistoryIndex } return } // VersionHistory is an internal type (TBD...) type VersionHistory struct { BranchToken []byte `json:"branchToken,omitempty"` Items []*VersionHistoryItem `json:"items,omitempty"` } // GetItems is an internal getter (TBD...) func (v *VersionHistory) GetItems() (o []*VersionHistoryItem) { if v != nil && v.Items != nil { return v.Items } return } // ByteSize returns the approximate memory used in bytes func (v *VersionHistory) ByteSize() uint64 { if v == nil { return 0 } size := uint64(unsafe.Sizeof(*v)) size += uint64(len(v.BranchToken)) // [] *VersionHistoryItem backing array: len * pointer size size += uint64(len(v.Items)) * uint64(unsafe.Sizeof((*VersionHistoryItem)(nil))) for _, e := range v.Items { size += e.ByteSize() } return size } // VersionHistoryItem is an internal type (TBD...) type VersionHistoryItem struct { EventID int64 `json:"eventID,omitempty"` Version int64 `json:"version,omitempty"` } // GetVersion is an internal getter (TBD...) func (v *VersionHistoryItem) GetVersion() (o int64) { if v != nil { return v.Version } return } // ByteSize returns the approximate memory used in bytes func (v *VersionHistoryItem) ByteSize() uint64 { if v == nil { return 0 } return uint64(unsafe.Sizeof(*v)) } // WorkerVersionInfo is an internal type (TBD...) type WorkerVersionInfo struct { Impl string `json:"impl,omitempty"` FeatureVersion string `json:"featureVersion,omitempty"` } // GetImpl is an internal getter (TBD...) func (v *WorkerVersionInfo) GetImpl() (o string) { if v != nil { return v.Impl } return } // GetFeatureVersion is an internal getter (TBD...) func (v *WorkerVersionInfo) GetFeatureVersion() (o string) { if v != nil { return v.FeatureVersion } return } // WorkflowExecution is an internal type (TBD...) type WorkflowExecution struct { WorkflowID string `json:"workflowId,omitempty"` RunID string `json:"runId,omitempty"` } // GetWorkflowID is an internal getter (TBD...) func (v *WorkflowExecution) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // GetRunID is an internal getter (TBD...) func (v *WorkflowExecution) GetRunID() (o string) { if v != nil { return v.RunID } return } // WorkflowExecutionAlreadyStartedError is an internal type (TBD...) type WorkflowExecutionAlreadyStartedError struct { Message string `json:"message,omitempty"` StartRequestID string `json:"startRequestId,omitempty"` RunID string `json:"runId,omitempty"` } // GetMessage is an internal getter (TBD...) func (v *WorkflowExecutionAlreadyStartedError) GetMessage() (o string) { if v != nil { return v.Message } return } // WorkflowExecutionCancelRequestedEventAttributes is an internal type (TBD...) type WorkflowExecutionCancelRequestedEventAttributes struct { Cause string `json:"cause,omitempty"` ExternalInitiatedEventID *int64 `json:"externalInitiatedEventId,omitempty"` ExternalWorkflowExecution *WorkflowExecution `json:"externalWorkflowExecution,omitempty"` Identity string `json:"identity,omitempty"` RequestID string `json:"requestId,omitempty"` } // Size returns the approximate memory used in bytes func (v *WorkflowExecutionCancelRequestedEventAttributes) ByteSize() uint64 { return 0 } // WorkflowExecutionCanceledEventAttributes is an internal type (TBD...) type WorkflowExecutionCanceledEventAttributes struct { DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` Details []byte `json:"details,omitempty"` } // Size returns the approximate memory used in bytes func (v *WorkflowExecutionCanceledEventAttributes) ByteSize() uint64 { return 0 } // WorkflowExecutionCloseStatus is an internal type (TBD...) type WorkflowExecutionCloseStatus int32 // Ptr is a helper function for getting pointer value func (e WorkflowExecutionCloseStatus) Ptr() *WorkflowExecutionCloseStatus { return &e } // String returns a readable string representation of WorkflowExecutionCloseStatus. func (e WorkflowExecutionCloseStatus) String() string { w := int32(e) switch w { case 0: return "COMPLETED" case 1: return "FAILED" case 2: return "CANCELED" case 3: return "TERMINATED" case 4: return "CONTINUED_AS_NEW" case 5: return "TIMED_OUT" } return fmt.Sprintf("WorkflowExecutionCloseStatus(%d)", w) } type WorkflowExecutionStatus int32 // Ptr is a helper function for getting pointer value func (e WorkflowExecutionStatus) Ptr() *WorkflowExecutionStatus { return &e } // String returns a readable string representation of WorkflowExecutionStatus. func (e WorkflowExecutionStatus) String() string { w := int32(e) switch w { case 0: return "PENDING" case 1: return "STARTED" case 2: return "COMPLETED" case 3: return "FAILED" case 4: return "CANCELED" case 5: return "TERMINATED" case 6: return "CONTINUED_AS_NEW" case 7: return "TIMED_OUT" } return fmt.Sprintf("WorkflowExecutionStatus(%d)", w) } // UnmarshalText parses enum value from string representation func (e *WorkflowExecutionStatus) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "PENDING": *e = WorkflowExecutionStatusPending return nil case "STARTED": *e = WorkflowExecutionStatusStarted return nil case "COMPLETED": *e = WorkflowExecutionStatusCompleted return nil case "FAILED": *e = WorkflowExecutionStatusFailed return nil case "CANCELED": *e = WorkflowExecutionStatusCanceled return nil case "TERMINATED": *e = WorkflowExecutionStatusTerminated return nil case "CONTINUED_AS_NEW": *e = WorkflowExecutionStatusContinuedAsNew return nil case "TIMED_OUT": *e = WorkflowExecutionStatusTimedOut return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "WorkflowExecutionStatus", err) } *e = WorkflowExecutionStatus(val) return nil } } // MarshalText encodes WorkflowExecutionStatus to text. func (e WorkflowExecutionStatus) MarshalText() ([]byte, error) { return []byte(e.String()), nil } // UnmarshalText parses enum value from string representation func (e *WorkflowExecutionCloseStatus) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "COMPLETED": *e = WorkflowExecutionCloseStatusCompleted return nil case "FAILED": *e = WorkflowExecutionCloseStatusFailed return nil case "CANCELED": *e = WorkflowExecutionCloseStatusCanceled return nil case "TERMINATED": *e = WorkflowExecutionCloseStatusTerminated return nil case "CONTINUED_AS_NEW": *e = WorkflowExecutionCloseStatusContinuedAsNew return nil case "TIMED_OUT": *e = WorkflowExecutionCloseStatusTimedOut return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "WorkflowExecutionCloseStatus", err) } *e = WorkflowExecutionCloseStatus(val) return nil } } // MarshalText encodes WorkflowExecutionCloseStatus to text. func (e WorkflowExecutionCloseStatus) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // WorkflowExecutionCloseStatusCompleted is an option for WorkflowExecutionCloseStatus WorkflowExecutionCloseStatusCompleted WorkflowExecutionCloseStatus = iota // WorkflowExecutionCloseStatusFailed is an option for WorkflowExecutionCloseStatus WorkflowExecutionCloseStatusFailed // WorkflowExecutionCloseStatusCanceled is an option for WorkflowExecutionCloseStatus WorkflowExecutionCloseStatusCanceled // WorkflowExecutionCloseStatusTerminated is an option for WorkflowExecutionCloseStatus WorkflowExecutionCloseStatusTerminated // WorkflowExecutionCloseStatusContinuedAsNew is an option for WorkflowExecutionCloseStatus WorkflowExecutionCloseStatusContinuedAsNew // WorkflowExecutionCloseStatusTimedOut is an option for WorkflowExecutionCloseStatus WorkflowExecutionCloseStatusTimedOut ) const ( // WorkflowExecutionStatusPending is an option for WorkflowExecutionStatus WorkflowExecutionStatusPending WorkflowExecutionStatus = iota // WorkflowExecutionStatusStarted is an option for WorkflowExecutionStatus WorkflowExecutionStatusStarted // WorkflowExecutionStatusCompleted is an option for WorkflowExecutionStatus WorkflowExecutionStatusCompleted // WorkflowExecutionStatusFailed is an option for WorkflowExecutionStatus WorkflowExecutionStatusFailed // WorkflowExecutionStatusCanceled is an option for WorkflowExecutionStatus WorkflowExecutionStatusCanceled // WorkflowExecutionStatusTerminated is an option for WorkflowExecutionStatus WorkflowExecutionStatusTerminated // WorkflowExecutionStatusContinuedAsNew is an option for WorkflowExecutionStatus WorkflowExecutionStatusContinuedAsNew // WorkflowExecutionStatusTimedOut is an option for WorkflowExecutionStatus WorkflowExecutionStatusTimedOut ) // WorkflowExecutionCompletedEventAttributes is an internal type (TBD...) type WorkflowExecutionCompletedEventAttributes struct { Result []byte `json:"result,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` } // Size returns the approximate memory used in bytes func (v *WorkflowExecutionCompletedEventAttributes) ByteSize() uint64 { return 0 } // WorkflowExecutionConfiguration is an internal type (TBD...) type WorkflowExecutionConfiguration struct { TaskList *TaskList `json:"taskList,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` } // WorkflowExecutionContinuedAsNewEventAttributes is an internal type (TBD...) type WorkflowExecutionContinuedAsNewEventAttributes struct { NewExecutionRunID string `json:"newExecutionRunId,omitempty"` WorkflowType *WorkflowType `json:"workflowType,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` BackoffStartIntervalInSeconds *int32 `json:"backoffStartIntervalInSeconds,omitempty"` Initiator *ContinueAsNewInitiator `json:"initiator,omitempty"` FailureReason *string `json:"failureReason,omitempty"` FailureDetails []byte `json:"failureDetails,omitempty"` LastCompletionResult []byte `json:"lastCompletionResult,omitempty"` Header *Header `json:"header,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` JitterStartSeconds *int32 `json:"jitterStartSeconds,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // GetNewExecutionRunID is an internal getter (TBD...) func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetNewExecutionRunID() (o string) { if v != nil { return v.NewExecutionRunID } return } // GetInitiator is an internal getter (TBD...) func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetInitiator() (o ContinueAsNewInitiator) { if v != nil && v.Initiator != nil { return *v.Initiator } return } // GetFailureReason is an internal getter (TBD...) func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetFailureReason() (o string) { if v != nil && v.FailureReason != nil { return *v.FailureReason } return } // GetLastCompletionResult is an internal getter (TBD...) func (v *WorkflowExecutionContinuedAsNewEventAttributes) GetLastCompletionResult() (o []byte) { if v != nil && v.LastCompletionResult != nil { return v.LastCompletionResult } return } // Size returns the approximate memory used in bytes func (v *WorkflowExecutionContinuedAsNewEventAttributes) ByteSize() uint64 { return 0 } // WorkflowExecutionFailedEventAttributes is an internal type (TBD...) type WorkflowExecutionFailedEventAttributes struct { Reason *string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` DecisionTaskCompletedEventID int64 `json:"decisionTaskCompletedEventId,omitempty"` } // GetReason is an internal getter (TBD...) func (v *WorkflowExecutionFailedEventAttributes) GetReason() (o string) { if v != nil && v.Reason != nil { return *v.Reason } return } // Size returns the approximate memory used in bytes func (v *WorkflowExecutionFailedEventAttributes) ByteSize() uint64 { return 0 } // WorkflowExecutionFilter is an internal type (TBD...) type WorkflowExecutionFilter struct { WorkflowID string `json:"workflowId,omitempty"` RunID string `json:"runId,omitempty"` } // GetWorkflowID is an internal getter (TBD...) func (v *WorkflowExecutionFilter) GetWorkflowID() (o string) { if v != nil { return v.WorkflowID } return } // WorkflowExecutionInfo is an internal type (TBD...) type WorkflowExecutionInfo struct { Execution *WorkflowExecution `json:"execution,omitempty"` Type *WorkflowType `json:"type,omitempty"` StartTime *int64 `json:"startTime,omitempty"` CloseTime *int64 `json:"closeTime,omitempty"` CloseStatus *WorkflowExecutionCloseStatus `json:"closeStatus,omitempty"` HistoryLength int64 `json:"historyLength,omitempty"` // should be history count ParentDomainID *string `json:"parentDomainId,omitempty"` ParentDomain *string `json:"parentDomain,omitempty"` ParentExecution *WorkflowExecution `json:"parentExecution,omitempty"` ParentInitiatedID *int64 `json:"parentInitiatedId,omitempty"` ExecutionTime *int64 `json:"executionTime,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` AutoResetPoints *ResetPoints `json:"autoResetPoints,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` IsCron bool `json:"isCron,omitempty"` UpdateTime *int64 `json:"updateTime,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` CronSchedule *string `json:"cronSchedule,omitempty"` ExecutionStatus *WorkflowExecutionStatus `json:"executionStatus,omitempty"` ScheduledExecutionTime *int64 `json:"scheduledExecutionTime,omitempty"` } // GetExecution is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetExecution() (o *WorkflowExecution) { if v != nil && v.Execution != nil { return v.Execution } return } // GetType is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetType() (o *WorkflowType) { if v != nil && v.Type != nil { return v.Type } return } // GetStartTime is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetStartTime() (o int64) { if v != nil && v.StartTime != nil { return *v.StartTime } return } // GetCloseTime is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetCloseTime() (o int64) { if v != nil && v.CloseTime != nil { return *v.CloseTime } return } // GetCloseStatus is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetCloseStatus() (o WorkflowExecutionCloseStatus) { if v != nil && v.CloseStatus != nil { return *v.CloseStatus } return } // GetExecutionTime is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetExecutionTime() (o int64) { if v != nil && v.ExecutionTime != nil { return *v.ExecutionTime } return } // GetUpdateTime is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetUpdateTime() (o int64) { if v != nil && v.UpdateTime != nil { return *v.UpdateTime } return } // GetSearchAttributes is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // GetPartitionConfig is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // GetCronSchedule is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetCronSchedule() (o string) { if v != nil && v.CronSchedule != nil { return *v.CronSchedule } return } // GetExecutionStatus is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetExecutionStatus() (o WorkflowExecutionStatus) { if v != nil && v.ExecutionStatus != nil { return *v.ExecutionStatus } return } // GetScheduledExecutionTime is an internal getter (TBD...) func (v *WorkflowExecutionInfo) GetScheduledExecutionTime() (o int64) { if v != nil && v.ScheduledExecutionTime != nil { return *v.ScheduledExecutionTime } return } // WorkflowExecutionSignaledEventAttributes is an internal type (TBD...) type WorkflowExecutionSignaledEventAttributes struct { SignalName string `json:"signalName,omitempty"` Input []byte `json:"input,omitempty"` Identity string `json:"identity,omitempty"` RequestID string `json:"requestId,omitempty"` } // GetSignalName is an internal getter (TBD...) func (v *WorkflowExecutionSignaledEventAttributes) GetSignalName() (o string) { if v != nil { return v.SignalName } return } // GetInput is an internal getter (TBD...) func (v *WorkflowExecutionSignaledEventAttributes) GetInput() (o []byte) { if v != nil && v.Input != nil { return v.Input } return } // GetIdentity is an internal getter (TBD...) func (v *WorkflowExecutionSignaledEventAttributes) GetIdentity() (o string) { if v != nil { return v.Identity } return } // GetRequestID is an internal getter (TBD...) func (v *WorkflowExecutionSignaledEventAttributes) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // Size returns the approximate memory used in bytes func (v *WorkflowExecutionSignaledEventAttributes) ByteSize() uint64 { return 0 } // WorkflowExecutionStartedEventAttributes is an internal type (TBD...) type WorkflowExecutionStartedEventAttributes struct { WorkflowType *WorkflowType `json:"workflowType,omitempty"` ParentWorkflowDomainID *string `json:"parentWorkflowDomainID,omitempty"` ParentWorkflowDomain *string `json:"parentWorkflowDomain,omitempty"` ParentWorkflowExecution *WorkflowExecution `json:"parentWorkflowExecution,omitempty"` ParentInitiatedEventID *int64 `json:"parentInitiatedEventId,omitempty"` TaskList *TaskList `json:"taskList,omitempty"` Input []byte `json:"input,omitempty"` ExecutionStartToCloseTimeoutSeconds *int32 `json:"executionStartToCloseTimeoutSeconds,omitempty"` TaskStartToCloseTimeoutSeconds *int32 `json:"taskStartToCloseTimeoutSeconds,omitempty"` ContinuedExecutionRunID string `json:"continuedExecutionRunId,omitempty"` Initiator *ContinueAsNewInitiator `json:"initiator,omitempty"` ContinuedFailureReason *string `json:"continuedFailureReason,omitempty"` ContinuedFailureDetails []byte `json:"continuedFailureDetails,omitempty"` LastCompletionResult []byte `json:"lastCompletionResult,omitempty"` OriginalExecutionRunID string `json:"originalExecutionRunId,omitempty"` Identity string `json:"identity,omitempty"` FirstExecutionRunID string `json:"firstExecutionRunId,omitempty"` FirstScheduleTime *time.Time `json:"firstScheduleTimeNano,omitempty"` RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"` Attempt int32 `json:"attempt,omitempty"` ExpirationTimestamp *int64 `json:"expirationTimestamp,omitempty"` CronSchedule string `json:"cronSchedule,omitempty"` FirstDecisionTaskBackoffSeconds *int32 `json:"firstDecisionTaskBackoffSeconds,omitempty"` Memo *Memo `json:"memo,omitempty"` SearchAttributes *SearchAttributes `json:"searchAttributes,omitempty"` PrevAutoResetPoints *ResetPoints `json:"prevAutoResetPoints,omitempty"` Header *Header `json:"header,omitempty"` JitterStartSeconds *int32 `json:"jitterStartSeconds,omitempty"` PartitionConfig map[string]string `json:"partitionConfig,omitempty"` RequestID string `json:"requestId,omitempty"` CronOverlapPolicy *CronOverlapPolicy `json:"cronOverlapPolicy,omitempty"` ActiveClusterSelectionPolicy *ActiveClusterSelectionPolicy `json:"activeClusterSelectionPolicy,omitempty"` } // GetParentWorkflowDomain is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetParentWorkflowDomain() (o string) { if v != nil && v.ParentWorkflowDomain != nil { return *v.ParentWorkflowDomain } return } // GetParentInitiatedEventID is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetParentInitiatedEventID() (o int64) { if v != nil && v.ParentInitiatedEventID != nil { return *v.ParentInitiatedEventID } return } // GetExecutionStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetExecutionStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.ExecutionStartToCloseTimeoutSeconds != nil { return *v.ExecutionStartToCloseTimeoutSeconds } return } // GetTaskStartToCloseTimeoutSeconds is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetTaskStartToCloseTimeoutSeconds() (o int32) { if v != nil && v.TaskStartToCloseTimeoutSeconds != nil { return *v.TaskStartToCloseTimeoutSeconds } return } // GetContinuedExecutionRunID is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetContinuedExecutionRunID() (o string) { if v != nil { return v.ContinuedExecutionRunID } return } // GetInitiator is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetInitiator() (o ContinueAsNewInitiator) { if v != nil && v.Initiator != nil { return *v.Initiator } return } // GetFirstExecutionRunID is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetFirstExecutionRunID() (o string) { if v != nil { return v.FirstExecutionRunID } return } // GetFirstScheduledTime is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetFirstScheduledTime() (o time.Time) { if v != nil && v.FirstScheduleTime != nil { return *v.FirstScheduleTime } return } // GetAttempt is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetAttempt() (o int32) { if v != nil { return v.Attempt } return } // GetExpirationTimestamp is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetExpirationTimestamp() (o int64) { if v != nil && v.ExpirationTimestamp != nil { return *v.ExpirationTimestamp } return } // GetCronSchedule is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetCronSchedule() (o string) { if v != nil { return v.CronSchedule } return } // GetFirstDecisionTaskBackoffSeconds is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetFirstDecisionTaskBackoffSeconds() (o int32) { if v != nil && v.FirstDecisionTaskBackoffSeconds != nil { return *v.FirstDecisionTaskBackoffSeconds } return } func (v *WorkflowExecutionStartedEventAttributes) GetJitterStartSeconds() (o int32) { if v != nil && v.JitterStartSeconds != nil { return *v.JitterStartSeconds } return } // GetMemo is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetMemo() (o *Memo) { if v != nil && v.Memo != nil { return v.Memo } return } // GetSearchAttributes is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetSearchAttributes() (o *SearchAttributes) { if v != nil && v.SearchAttributes != nil { return v.SearchAttributes } return } // GetPrevAutoResetPoints is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetPrevAutoResetPoints() (o *ResetPoints) { if v != nil && v.PrevAutoResetPoints != nil { return v.PrevAutoResetPoints } return } // GetPartitionConfig is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // GetRequestID is an internal getter (TBD...) func (v *WorkflowExecutionStartedEventAttributes) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // Size returns the approximate memory used in bytes func (v *WorkflowExecutionStartedEventAttributes) ByteSize() uint64 { return 0 } // WorkflowExecutionTerminatedEventAttributes is an internal type (TBD...) type WorkflowExecutionTerminatedEventAttributes struct { Reason string `json:"reason,omitempty"` Details []byte `json:"details,omitempty"` Identity string `json:"identity,omitempty"` } // GetReason is an internal getter (TBD...) func (v *WorkflowExecutionTerminatedEventAttributes) GetReason() (o string) { if v != nil { return v.Reason } return } // GetIdentity is an internal getter (TBD...) func (v *WorkflowExecutionTerminatedEventAttributes) GetIdentity() (o string) { if v != nil { return v.Identity } return } // Size returns the approximate memory used in bytes func (v *WorkflowExecutionTerminatedEventAttributes) ByteSize() uint64 { return 0 } // WorkflowExecutionTimedOutEventAttributes is an internal type (TBD...) type WorkflowExecutionTimedOutEventAttributes struct { TimeoutType *TimeoutType `json:"timeoutType,omitempty"` } // GetTimeoutType is an internal getter (TBD...) func (v *WorkflowExecutionTimedOutEventAttributes) GetTimeoutType() (o TimeoutType) { if v != nil && v.TimeoutType != nil { return *v.TimeoutType } return } // Size returns the approximate memory used in bytes func (v *WorkflowExecutionTimedOutEventAttributes) ByteSize() uint64 { return 0 } // WorkflowIDReusePolicy is an internal type (TBD...) type WorkflowIDReusePolicy int32 // Ptr is a helper function for getting pointer value func (e WorkflowIDReusePolicy) Ptr() *WorkflowIDReusePolicy { return &e } // String returns a readable string representation of WorkflowIDReusePolicy. func (e WorkflowIDReusePolicy) String() string { w := int32(e) switch w { case 0: return "AllowDuplicateFailedOnly" case 1: return "AllowDuplicate" case 2: return "RejectDuplicate" case 3: return "TerminateIfRunning" } return fmt.Sprintf("WorkflowIDReusePolicy(%d)", w) } // UnmarshalText parses enum value from string representation func (e *WorkflowIDReusePolicy) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "ALLOWDUPLICATEFAILEDONLY": *e = WorkflowIDReusePolicyAllowDuplicateFailedOnly return nil case "ALLOWDUPLICATE": *e = WorkflowIDReusePolicyAllowDuplicate return nil case "REJECTDUPLICATE": *e = WorkflowIDReusePolicyRejectDuplicate return nil case "TERMINATEIFRUNNING": *e = WorkflowIDReusePolicyTerminateIfRunning return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "WorkflowIDReusePolicy", err) } *e = WorkflowIDReusePolicy(val) return nil } } // MarshalText encodes WorkflowIDReusePolicy to text. func (e WorkflowIDReusePolicy) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // WorkflowIDReusePolicyAllowDuplicateFailedOnly is an option for WorkflowIDReusePolicy WorkflowIDReusePolicyAllowDuplicateFailedOnly WorkflowIDReusePolicy = iota // WorkflowIDReusePolicyAllowDuplicate is an option for WorkflowIDReusePolicy WorkflowIDReusePolicyAllowDuplicate // WorkflowIDReusePolicyRejectDuplicate is an option for WorkflowIDReusePolicy WorkflowIDReusePolicyRejectDuplicate // WorkflowIDReusePolicyTerminateIfRunning is an option for WorkflowIDReusePolicy WorkflowIDReusePolicyTerminateIfRunning ) // WorkflowQuery is an internal type (TBD...) type WorkflowQuery struct { QueryType string `json:"queryType,omitempty"` QueryArgs []byte `json:"queryArgs,omitempty"` } // GetQueryType is an internal getter (TBD...) func (v *WorkflowQuery) GetQueryType() (o string) { if v != nil { return v.QueryType } return } // GetQueryArgs is an internal getter (TBD...) func (v *WorkflowQuery) GetQueryArgs() (o []byte) { if v != nil && v.QueryArgs != nil { return v.QueryArgs } return } // WorkflowQueryResult is an internal type (TBD...) type WorkflowQueryResult struct { ResultType *QueryResultType `json:"resultType,omitempty"` Answer []byte `json:"answer,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` } // GetResultType is an internal getter (TBD...) func (v *WorkflowQueryResult) GetResultType() (o QueryResultType) { if v != nil && v.ResultType != nil { return *v.ResultType } return } // GetAnswer is an internal getter (TBD...) func (v *WorkflowQueryResult) GetAnswer() (o []byte) { if v != nil && v.Answer != nil { return v.Answer } return } // GetErrorMessage is an internal getter (TBD...) func (v *WorkflowQueryResult) GetErrorMessage() (o string) { if v != nil { return v.ErrorMessage } return } // WorkflowType is an internal type (TBD...) type WorkflowType struct { Name string `json:"name,omitempty"` } // GetName is an internal getter (TBD...) func (v *WorkflowType) GetName() (o string) { if v != nil { return v.Name } return } // WorkflowTypeFilter is an internal type (TBD...) type WorkflowTypeFilter struct { Name string `json:"name,omitempty"` } // GetName is an internal getter (TBD...) func (v *WorkflowTypeFilter) GetName() (o string) { if v != nil { return v.Name } return } // CrossClusterTaskType is an internal type (TBD...) type CrossClusterTaskType int32 // Ptr is a helper function for getting pointer value func (e CrossClusterTaskType) Ptr() *CrossClusterTaskType { return &e } // String returns a readable string representation of CrossClusterTaskType. func (e CrossClusterTaskType) String() string { w := int32(e) switch w { case 0: return "StartChildExecution" case 1: return "CancelExecution" case 2: return "SignalExecution" case 3: return "RecordChildWorkflowExecutionComplete" case 4: return "ApplyParentClosePolicy" } return fmt.Sprintf("CrossClusterTaskType(%d)", w) } // UnmarshalText parses enum value from string representation func (e *CrossClusterTaskType) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "STARTCHILDEXECUTION": *e = CrossClusterTaskTypeStartChildExecution return nil case "CANCELEXECUTION": *e = CrossClusterTaskTypeCancelExecution return nil case "SIGNALEXECUTION": *e = CrossClusterTaskTypeSignalExecution return nil case "RECORDCHILDWORKLOWEXECUTIONCOMPLETE": *e = CrossClusterTaskTypeRecordChildWorkflowExeuctionComplete return nil case "APPLYPARENTCLOSEPOLICY": *e = CrossClusterTaskTypeApplyParentPolicy return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "CrossClusterTaskType", err) } *e = CrossClusterTaskType(val) return nil } } // MarshalText encodes CrossClusterTaskType to text. func (e CrossClusterTaskType) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // CrossClusterTaskTypeStartChildExecution is an option for CrossClusterTaskType CrossClusterTaskTypeStartChildExecution CrossClusterTaskType = iota // CrossClusterTaskTypeCancelExecution is an option for CrossClusterTaskType CrossClusterTaskTypeCancelExecution // CrossClusterTaskTypeSignalExecution is an option for CrossClusterTaskType CrossClusterTaskTypeSignalExecution // CrossClusterTaskTypeRecordChildWorkflowExeuctionComplete is an option for CrossClusterTaskType CrossClusterTaskTypeRecordChildWorkflowExeuctionComplete // CrossClusterTaskTypeApplyParentPolicy is an option for CrossClusterTaskType CrossClusterTaskTypeApplyParentPolicy ) // CrossClusterTaskFailedCause is an internal type (TBD...) type CrossClusterTaskFailedCause int32 // Ptr is a helper function for getting pointer value func (e CrossClusterTaskFailedCause) Ptr() *CrossClusterTaskFailedCause { return &e } // String returns a readable string representation of CrossClusterTaskFailedCause. func (e CrossClusterTaskFailedCause) String() string { w := int32(e) switch w { case 0: return "DOMAIN_NOT_ACTIVE" case 1: return "DOMAIN_NOT_EXISTS" case 2: return "WORKFLOW_ALREADY_RUNNING" case 3: return "WORKFLOW_NOT_EXISTS" case 4: return "WORKFLOW_ALREADY_COMPLETED" case 5: return "UNCATEGORIZED" } return fmt.Sprintf("CrossClusterTaskFailedCause(%d)", w) } // UnmarshalText parses enum value from string representation func (e *CrossClusterTaskFailedCause) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "DOMAIN_NOT_ACTIVE": *e = CrossClusterTaskFailedCauseDomainNotActive return nil case "DOMAIN_NOT_EXISTS": *e = CrossClusterTaskFailedCauseDomainNotExists return nil case "WORKFLOW_ALREADY_RUNNING": *e = CrossClusterTaskFailedCauseWorkflowAlreadyRunning return nil case "WORKFLOW_NOT_EXISTS": *e = CrossClusterTaskFailedCauseWorkflowNotExists return nil case "WORKFLOW_ALREADY_COMPLETED": *e = CrossClusterTaskFailedCauseWorkflowAlreadyCompleted return nil case "UNCATEGORIZED": *e = CrossClusterTaskFailedCauseUncategorized return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "CrossClusterTaskFailedCause", err) } *e = CrossClusterTaskFailedCause(val) return nil } } // MarshalText encodes CrossClusterTaskFailedCause to text. func (e CrossClusterTaskFailedCause) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // CrossClusterTaskFailedCauseDomainNotActive is an option for CrossClusterTaskFailedCause CrossClusterTaskFailedCauseDomainNotActive CrossClusterTaskFailedCause = iota // CrossClusterTaskFailedCauseDomainNotExists is an option for CrossClusterTaskFailedCause CrossClusterTaskFailedCauseDomainNotExists // CrossClusterTaskFailedCauseWorkflowAlreadyRunning is an option for CrossClusterTaskFailedCause CrossClusterTaskFailedCauseWorkflowAlreadyRunning // CrossClusterTaskFailedCauseWorkflowNotExists is an option for CrossClusterTaskFailedCause CrossClusterTaskFailedCauseWorkflowNotExists // CrossClusterTaskFailedCauseWorkflowAlreadyCompleted is an option for CrossClusterTaskFailedCause CrossClusterTaskFailedCauseWorkflowAlreadyCompleted // CrossClusterTaskFailedCauseUncategorized is an option for CrossClusterTaskFailedCause CrossClusterTaskFailedCauseUncategorized ) // GetTaskFailedCause is an internal type (TBD...) type GetTaskFailedCause int32 // Ptr is a helper function for getting pointer value func (e GetTaskFailedCause) Ptr() *GetTaskFailedCause { return &e } // String returns a readable string representation of GetCrossClusterTaskFailedCause. func (e GetTaskFailedCause) String() string { w := int32(e) switch w { case 0: return "SERVICE_BUSY" case 1: return "TIMEOUT" case 2: return "SHARD_OWNERSHIP_LOST" case 3: return "UNCATEGORIZED" } return fmt.Sprintf("GetCrossClusterTaskFailedCause(%d)", w) } // UnmarshalText parses enum value from string representation func (e *GetTaskFailedCause) UnmarshalText(value []byte) error { switch s := strings.ToUpper(string(value)); s { case "SERVICE_BUSY": *e = GetTaskFailedCauseServiceBusy return nil case "TIMEOUT": *e = GetTaskFailedCauseTimeout return nil case "SHARD_OWNERSHIP_LOST": *e = GetTaskFailedCauseShardOwnershipLost return nil case "UNCATEGORIZED": *e = GetTaskFailedCauseUncategorized return nil default: val, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("unknown enum value %q for %q: %v", s, "GetCrossClusterTaskFailedCause", err) } *e = GetTaskFailedCause(val) return nil } } // MarshalText encodes GetCrossClusterTaskFailedCause to text. func (e GetTaskFailedCause) MarshalText() ([]byte, error) { return []byte(e.String()), nil } const ( // GetTaskFailedCauseServiceBusy is an option for GetCrossClusterTaskFailedCause GetTaskFailedCauseServiceBusy GetTaskFailedCause = iota // GetTaskFailedCauseTimeout is an option for GetCrossClusterTaskFailedCause GetTaskFailedCauseTimeout // GetTaskFailedCauseShardOwnershipLost is an option for GetCrossClusterTaskFailedCause GetTaskFailedCauseShardOwnershipLost // GetTaskFailedCauseUncategorized is an option for GetCrossClusterTaskFailedCause GetTaskFailedCauseUncategorized ) // CrossClusterTaskInfo is an internal type (TBD...) type CrossClusterTaskInfo struct { DomainID string `json:"domainID,omitempty"` WorkflowID string `json:"workflowID,omitempty"` RunID string `json:"runID,omitempty"` TaskType *CrossClusterTaskType `json:"taskType,omitempty"` TaskState int16 `json:"taskState,omitempty"` TaskID int64 `json:"taskID,omitempty"` VisibilityTimestamp *int64 `json:"visibilityTimestamp,omitempty"` } // GetTaskType is an internal getter (TBD...) func (v *CrossClusterTaskInfo) GetTaskType() (o CrossClusterTaskType) { if v != nil && v.TaskType != nil { return *v.TaskType } return } // GetTaskID is an internal getter (TBD...) func (v *CrossClusterTaskInfo) GetTaskID() (o int64) { if v != nil { return v.TaskID } return } // GetVisibilityTimestamp is an internal getter (TBD...) func (v *CrossClusterTaskInfo) GetVisibilityTimestamp() (o int64) { if v != nil && v.VisibilityTimestamp != nil { return *v.VisibilityTimestamp } return } // CrossClusterStartChildExecutionRequestAttributes is an internal type (TBD...) type CrossClusterStartChildExecutionRequestAttributes struct { TargetDomainID string `json:"targetDomainID,omitempty"` RequestID string `json:"requestID,omitempty"` InitiatedEventID int64 `json:"initiatedEventID,omitempty"` InitiatedEventAttributes *StartChildWorkflowExecutionInitiatedEventAttributes `json:"initiatedEventAttributes,omitempty"` TargetRunID *string `json:"targetRunID,omitempty"` PartitionConfig map[string]string } // GetRequestID is an internal getter (TBD...) func (v *CrossClusterStartChildExecutionRequestAttributes) GetRequestID() (o string) { if v != nil { return v.RequestID } return } // GetInitiatedEventAttributes is an internal getter (TBD...) func (v *CrossClusterStartChildExecutionRequestAttributes) GetInitiatedEventAttributes() (o *StartChildWorkflowExecutionInitiatedEventAttributes) { if v != nil && v.InitiatedEventAttributes != nil { return v.InitiatedEventAttributes } return } // GetTargetRunID is an internal getter (TBD...) func (v *CrossClusterStartChildExecutionRequestAttributes) GetTargetRunID() (o string) { if v != nil && v.TargetRunID != nil { return *v.TargetRunID } return } // GetTargetRunID is an internal getter (TBD...) func (v *CrossClusterStartChildExecutionRequestAttributes) GetPartitionConfig() (o map[string]string) { if v != nil && v.PartitionConfig != nil { return v.PartitionConfig } return } // CrossClusterStartChildExecutionResponseAttributes is an internal type (TBD...) type CrossClusterStartChildExecutionResponseAttributes struct { RunID string `json:"runID,omitempty"` } // GetRunID is an internal getter (TBD...) func (v *CrossClusterStartChildExecutionResponseAttributes) GetRunID() (o string) { if v != nil { return v.RunID } return } // CrossClusterCancelExecutionRequestAttributes is an internal type (TBD...) type CrossClusterCancelExecutionRequestAttributes struct { TargetDomainID string `json:"targetDomainID,omitempty"` TargetWorkflowID string `json:"targetWorkflowID,omitempty"` TargetRunID string `json:"targetRunID,omitempty"` RequestID string `json:"requestID,omitempty"` InitiatedEventID int64 `json:"initiatedEventID,omitempty"` ChildWorkflowOnly bool `json:"childWorkflowOnly,omitempty"` } // CrossClusterCancelExecutionResponseAttributes is an internal type (TBD...) type CrossClusterCancelExecutionResponseAttributes struct { } // CrossClusterSignalExecutionRequestAttributes is an internal type (TBD...) type CrossClusterSignalExecutionRequestAttributes struct { TargetDomainID string `json:"targetDomainID,omitempty"` TargetWorkflowID string `json:"targetWorkflowID,omitempty"` TargetRunID string `json:"targetRunID,omitempty"` RequestID string `json:"requestID,omitempty"` InitiatedEventID int64 `json:"initiatedEventID,omitempty"` ChildWorkflowOnly bool `json:"childWorkflowOnly,omitempty"` SignalName string `json:"signalName,omitempty"` SignalInput []byte `json:"signalInput,omitempty"` Control []byte `json:"control,omitempty"` } // CrossClusterSignalExecutionResponseAttributes is an internal type (TBD...) type CrossClusterSignalExecutionResponseAttributes struct { } type CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes struct { TargetDomainID string `json:"targetDomainID,omitempty"` TargetWorkflowID string `json:"targetWorkflowID,omitempty"` TargetRunID string `json:"targetRunID,omitempty"` InitiatedEventID int64 `json:"initiatedEventID,omitempty"` CompletionEvent *HistoryEvent `json:"completionEvent,omitempty"` } // CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes is an internal type (TBD...) type CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes struct { } type ApplyParentClosePolicyStatus struct { Completed bool `json:"completed,omitempty"` FailedCause *CrossClusterTaskFailedCause `json:"failedCause,omitempty"` } type ApplyParentClosePolicyAttributes struct { ChildDomainID string `json:"ChildDomainID,omitempty"` ChildWorkflowID string `json:"ChildWorkflowID,omitempty"` ChildRunID string `json:"ChildRunID,omitempty"` ParentClosePolicy *ParentClosePolicy `json:"parentClosePolicy,omitempty"` } // GetParentClosePolicy is an internal getter (TBD...) func (v *ApplyParentClosePolicyAttributes) GetParentClosePolicy() (o *ParentClosePolicy) { if v != nil { return v.ParentClosePolicy } return } type ApplyParentClosePolicyRequest struct { Child *ApplyParentClosePolicyAttributes `json:"child,omitempty"` Status *ApplyParentClosePolicyStatus `json:"status,omitempty"` } type CrossClusterApplyParentClosePolicyRequestAttributes struct { Children []*ApplyParentClosePolicyRequest `json:"children,omitempty"` } type ApplyParentClosePolicyResult struct { Child *ApplyParentClosePolicyAttributes `json:"child,omitempty"` FailedCause *CrossClusterTaskFailedCause `json:"failedCause,omitempty"` } // CrossClusterApplyParentClosePolicyResponseAttributes is an internal type (TBD...) type CrossClusterApplyParentClosePolicyResponseAttributes struct { ChildrenStatus []*ApplyParentClosePolicyResult `json:"childrenStatus,omitempty"` } // CrossClusterTaskRequest is an internal type (TBD...) type CrossClusterTaskRequest struct { TaskInfo *CrossClusterTaskInfo `json:"taskInfo,omitempty"` StartChildExecutionAttributes *CrossClusterStartChildExecutionRequestAttributes `json:"startChildExecutionAttributes,omitempty"` CancelExecutionAttributes *CrossClusterCancelExecutionRequestAttributes `json:"cancelExecutionAttributes,omitempty"` SignalExecutionAttributes *CrossClusterSignalExecutionRequestAttributes `json:"signalExecutionAttributes,omitempty"` RecordChildWorkflowExecutionCompleteAttributes *CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes `json:"RecordChildWorkflowExecutionCompleteAttributes,omitempty"` ApplyParentClosePolicyAttributes *CrossClusterApplyParentClosePolicyRequestAttributes `json:"ApplyParentClosePolicyAttributes,omitempty"` } // CrossClusterTaskResponse is an internal type (TBD...) type CrossClusterTaskResponse struct { TaskID int64 `json:"taskID,omitempty"` TaskType *CrossClusterTaskType `json:"taskType,omitempty"` TaskState int16 `json:"taskState,omitempty"` FailedCause *CrossClusterTaskFailedCause `json:"failedCause,omitempty"` StartChildExecutionAttributes *CrossClusterStartChildExecutionResponseAttributes `json:"startChildExecutionAttributes,omitempty"` CancelExecutionAttributes *CrossClusterCancelExecutionResponseAttributes `json:"cancelExecutionAttributes,omitempty"` SignalExecutionAttributes *CrossClusterSignalExecutionResponseAttributes `json:"signalExecutionAttributes,omitempty"` RecordChildWorkflowExecutionCompleteAttributes *CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes `json:"RecordChildWorkflowExecutionCompleteAttributes,omitempty"` ApplyParentClosePolicyAttributes *CrossClusterApplyParentClosePolicyResponseAttributes `json:"ApplyParentClosePolicyAttributes,omitempty"` } // GetTaskID is an internal getter (TBD...) func (v *CrossClusterTaskResponse) GetTaskID() (o int64) { if v != nil { return v.TaskID } return } // GetTaskType is an internal getter (TBD...) func (v *CrossClusterTaskResponse) GetTaskType() (o CrossClusterTaskType) { if v != nil && v.TaskType != nil { return *v.TaskType } return } // GetFailedCause is an internal getter (TBD...) func (v *CrossClusterTaskResponse) GetFailedCause() (o CrossClusterTaskFailedCause) { if v != nil && v.FailedCause != nil { return *v.FailedCause } return } // GetCrossClusterTasksRequest is an internal type (TBD...) type GetCrossClusterTasksRequest struct { ShardIDs []int32 `json:"shardIDs,omitempty"` TargetCluster string `json:"targetCluster,omitempty"` } // GetShardIDs is an internal getter (TBD...) func (v *GetCrossClusterTasksRequest) GetShardIDs() (o []int32) { if v != nil && v.ShardIDs != nil { return v.ShardIDs } return } // GetTargetCluster is an internal getter (TBD...) func (v *GetCrossClusterTasksRequest) GetTargetCluster() (o string) { if v != nil { return v.TargetCluster } return } // GetCrossClusterTasksResponse is an internal type (TBD...) type GetCrossClusterTasksResponse struct { TasksByShard map[int32][]*CrossClusterTaskRequest `json:"tasksByShard,omitempty"` FailedCauseByShard map[int32]GetTaskFailedCause `json:"failedCauseByShard,omitempty"` } // GetTasksByShard is an internal getter (TBD...) func (v *GetCrossClusterTasksResponse) GetTasksByShard() (o map[int32][]*CrossClusterTaskRequest) { if v != nil && v.TasksByShard != nil { return v.TasksByShard } return } // RespondCrossClusterTasksCompletedRequest is an internal type (TBD...) type RespondCrossClusterTasksCompletedRequest struct { ShardID int32 `json:"shardID,omitempty"` TargetCluster string `json:"targetCluster,omitempty"` TaskResponses []*CrossClusterTaskResponse `json:"taskResponses,omitempty"` FetchNewTasks bool `json:"fetchNewTasks,omitempty"` } // GetShardID is an internal getter (TBD...) func (v *RespondCrossClusterTasksCompletedRequest) GetShardID() (o int32) { if v != nil { return v.ShardID } return } // GetFetchNewTasks is an internal getter (TBD...) func (v *RespondCrossClusterTasksCompletedRequest) GetFetchNewTasks() (o bool) { if v != nil { return v.FetchNewTasks } return } // RespondCrossClusterTasksCompletedResponse is an internal type (TBD...) type RespondCrossClusterTasksCompletedResponse struct { Tasks []*CrossClusterTaskRequest `json:"tasks,omitempty"` } // StickyWorkerUnavailableError is an internal type (TBD...) type StickyWorkerUnavailableError struct { Message string `json:"message,required"` } // ReadOnlyPartitionError is an internal type (TBD...) type ReadOnlyPartitionError struct { Message string `json:"message,required"` } // Any is an internal mirror of google.protobuf.Any, serving the same purposes, but // intentionally breaking direct compatibility because it may hold data that is not // actually protobuf encoded. // // All uses of Any must either: // - check that ValueType is a recognized type, and deserialize based on its contents // - or just pass it along as an opaque type for something else to use // // Contents are intentionally undefined to allow external definitions, e.g. from // third-party plugins that are not part of this source repository. type Any struct { // ValueType describes the type of encoded data in Value. // // No structure or allowed values are defined, but you are strongly encouraged // to use hard-coded strings (or unambiguous prefixes) or URLs when possible. // // For more concise encoding of exclusively known types, use e.g. DataBlob instead. ValueType string `json:"value_type"` // Value holds arbitrary bytes, and is described by ValueType. // // To interpret, you MUST check ValueType. Value []byte `json:"value"` } // AutoConfigHint is an internal type (TBD...) type AutoConfigHint struct { EnableAutoConfig bool `json:"enableAutoConfig"` PollerWaitTimeInMs int64 `json:"pollerWaitTimeInMs"` } type CronOverlapPolicy int32 const ( CronOverlapPolicySkipped CronOverlapPolicy = iota CronOverlapPolicyBufferOne ) func (v CronOverlapPolicy) String() string { switch v { case CronOverlapPolicySkipped: return "SKIP" case CronOverlapPolicyBufferOne: return "BUFFERONE" } return "UNKNOWN" } // Ptr is a helper function for getting pointer to CronOverlapPolicy func (v CronOverlapPolicy) Ptr() *CronOverlapPolicy { return &v } ================================================ FILE: common/types/shared_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package types import ( "testing" "unsafe" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" ) func TestDataBlobDeepCopy(t *testing.T) { tests := []struct { name string input *DataBlob }{ { name: "nil", input: nil, }, { name: "empty", input: &DataBlob{}, }, { name: "thrift ok", input: &DataBlob{ EncodingType: EncodingTypeThriftRW.Ptr(), Data: []byte("some thrift data"), }, }, { name: "json ok", input: &DataBlob{ EncodingType: EncodingTypeJSON.Ptr(), Data: []byte("some json data"), }, }, } for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { got := tc.input.DeepCopy() assert.Equal(t, tc.input, got) if tc.input != nil && tc.input.Data != nil && identicalByteArray(tc.input.Data, got.Data) { t.Error("expected DeepCopy to return a new data slice") } }) } } func TestActiveClustersConfigDeepCopy(t *testing.T) { normalConfig := &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": { ClusterAttributes: map[string]ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "us-east-1-cluster", FailoverVersion: 1, }, "us-east-2": { ActiveClusterName: "us-east-2-cluster", FailoverVersion: 2, }, }, }, }, } tests := []struct { name string input *ActiveClusters expect *ActiveClusters }{ { name: "nil case", input: nil, expect: nil, }, { name: "empty case with nil map", input: &ActiveClusters{}, expect: &ActiveClusters{}, }, { name: "empty case with empty map", input: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{}, }, expect: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{}, }, }, { name: "normal case", input: normalConfig, expect: normalConfig, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { deepCopy := tc.input.DeepCopy() if diff := cmp.Diff(tc.expect, deepCopy); diff != "" { t.Errorf("DeepCopy() mismatch (-want +got):\n%s", diff) } }) } } // Todo (david.porter) delete this test and codegen this func TestActiveClustersDeepCopyMutationIsolation(t *testing.T) { t.Run("modifying nested ClusterAttributes map in original should not affect copy", func(t *testing.T) { original := &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": { ClusterAttributes: map[string]ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 100, }, }, }, }, } copied := original.DeepCopy() assert.Equal(t, original, copied) scope := original.AttributeScopes["region"] scope.ClusterAttributes["us-west-1"] = ActiveClusterInfo{ ActiveClusterName: "cluster2", FailoverVersion: 200, } original.AttributeScopes["region"] = scope assert.Len(t, original.AttributeScopes["region"].ClusterAttributes, 2) assert.Len(t, copied.AttributeScopes["region"].ClusterAttributes, 1) assert.Contains(t, original.AttributeScopes["region"].ClusterAttributes, "us-west-1") assert.NotContains(t, copied.AttributeScopes["region"].ClusterAttributes, "us-west-1") }) } func TestIsActiveActiveDomain(t *testing.T) { tests := []struct { name string activeClusters *DomainReplicationConfiguration want bool }{ { name: "empty DomainReplicationConfiguration should return false", activeClusters: &DomainReplicationConfiguration{}, want: false, }, { name: "nil receiver should return false", activeClusters: nil, want: false, }, { name: "empty ActiveClusters should return false", activeClusters: &DomainReplicationConfiguration{ActiveClusters: &ActiveClusters{}}, want: false, }, { name: "ActiveClusters with only old format populated should return true", activeClusters: &DomainReplicationConfiguration{ ActiveClusters: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": { ClusterAttributes: map[string]ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "cluster1"}, }, }, }, }, }, want: true, }, { name: "ActiveClusters with only new format populated should return true", activeClusters: &DomainReplicationConfiguration{ ActiveClusters: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": {ClusterAttributes: map[string]ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "cluster1"}, }}, }, }, }, want: true, }, { name: "ActiveClusters with both formats populated should return true", activeClusters: &DomainReplicationConfiguration{ ActiveClusters: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": {ClusterAttributes: map[string]ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "cluster1"}, }}, }, }, }, want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.activeClusters.IsActiveActive() assert.Equal(t, tt.want, got) }) } } // identicalByteArray returns true if a and b are the same slice, false otherwise. func identicalByteArray(a, b []byte) bool { return len(a) == len(b) && unsafe.SliceData(a) == unsafe.SliceData(b) } func TestActiveClusters_GetAllClusters(t *testing.T) { tests := []struct { name string activeClusters *ActiveClusters want []string }{ { name: "nil receiver should return empty slice", activeClusters: nil, want: []string{}, }, { name: "empty ActiveClusters should return empty slice", activeClusters: &ActiveClusters{}, want: []string{}, }, { name: "only old format populated should return attribute names from old format sorted", activeClusters: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": { ClusterAttributes: map[string]ActiveClusterInfo{ "us-west-1": {ActiveClusterName: "cluster2"}, "us-east-1": {ActiveClusterName: "cluster1"}, "eu-west-1": {ActiveClusterName: "cluster3"}, }, }, }, }, want: []string{"eu-west-1", "us-east-1", "us-west-1"}, }, { name: "only new format populated should return attribute names from new format sorted", activeClusters: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": { ClusterAttributes: map[string]ActiveClusterInfo{ "ap-south-1": {ActiveClusterName: "cluster4"}, "eu-north-1": {ActiveClusterName: "cluster5"}, }, }, }, }, want: []string{"ap-south-1", "eu-north-1"}, }, { name: "both formats with different attribute names should return deduplicated sorted list", activeClusters: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": { ClusterAttributes: map[string]ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "cluster1"}, "us-west-1": {ActiveClusterName: "cluster2"}, "eu-west-1": {ActiveClusterName: "cluster3"}, "ap-south-1": {ActiveClusterName: "cluster4"}, }, }, }, }, want: []string{"ap-south-1", "eu-west-1", "us-east-1", "us-west-1"}, }, { name: "both formats with overlapping attribute names should return deduplicated sorted list", activeClusters: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": { ClusterAttributes: map[string]ActiveClusterInfo{ "us-east-1": {ActiveClusterName: "cluster1"}, "us-west-1": {ActiveClusterName: "cluster2"}, "eu-west-1": {ActiveClusterName: "cluster3"}, }, }, }, }, want: []string{"eu-west-1", "us-east-1", "us-west-1"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.activeClusters.GetAllClusters() if diff := cmp.Diff(tt.want, got); diff != "" { t.Errorf("GetAllClusters() mismatch (-want +got):\n%s", diff) } }) } } func TestActiveClusters_GetFailoverVersionForAttribute(t *testing.T) { tests := []struct { name string activeClusters *ActiveClusters scopeType string attributeName string expected int64 expectedErr error }{ { name: "normal value / success case - this should provide the failover version", activeClusters: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": { ClusterAttributes: map[string]ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 100, }, "us-west-2": { ActiveClusterName: "cluster2", FailoverVersion: 200, }, }, }, }, }, scopeType: "region", attributeName: "us-east-1", expected: 100, }, { name: "normal value / success case for zero values", activeClusters: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": { ClusterAttributes: map[string]ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 0, }, }, }, }, }, scopeType: "region", attributeName: "us-east-1", expected: 0, }, { name: "nil receiver should return an error", activeClusters: nil, scopeType: "region", attributeName: "us-east-1", expected: -1, expectedErr: &ClusterAttributeNotFoundError{ ScopeType: "region", AttributeName: "us-east-1", }, }, { name: "empty ActiveClusters should return an error", activeClusters: &ActiveClusters{}, scopeType: "region", attributeName: "us-east-1", expected: -1, expectedErr: &ClusterAttributeNotFoundError{ ScopeType: "region", AttributeName: "us-east-1", }, }, { name: "nil AttributeScopes should return an error", activeClusters: &ActiveClusters{ AttributeScopes: nil, }, scopeType: "region", attributeName: "us-east-1", expected: -1, expectedErr: &ClusterAttributeNotFoundError{ ScopeType: "region", AttributeName: "us-east-1", }, }, { name: "scopeType not found should return an error", activeClusters: &ActiveClusters{ AttributeScopes: map[string]ClusterAttributeScope{ "region": { ClusterAttributes: map[string]ActiveClusterInfo{ "us-east-1": { ActiveClusterName: "cluster1", FailoverVersion: 100, }, }, }, }, }, scopeType: "datacenter", attributeName: "dc1", expected: -1, expectedErr: &ClusterAttributeNotFoundError{ ScopeType: "datacenter", AttributeName: "dc1", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, gotErr := tt.activeClusters.GetFailoverVersionForAttribute(tt.scopeType, tt.attributeName) assert.Equal(t, tt.expected, got) assert.Equal(t, tt.expectedErr, gotErr) }) } } func TestBadBinariesDeepCopy(t *testing.T) { tests := []struct { name string input *BadBinaries }{ { name: "nil", input: nil, }, { name: "empty", input: &BadBinaries{}, }, { name: "multiple binaries", input: &BadBinaries{ Binaries: map[string]*BadBinaryInfo{ "bad1": { Reason: "reason1", Operator: "op1", CreatedTimeNano: func() *int64 { i := int64(111); return &i }(), }, "bad2": { Reason: "reason2", Operator: "op2", }, "bad3": { Reason: "reason3", Operator: "op3", CreatedTimeNano: func() *int64 { i := int64(333); return &i }(), }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { copied := tt.input.DeepCopy() // Test for nil input if tt.input == nil { assert.Empty(t, copied.Binaries) return } // Verify values are equal assert.Equal(t, tt.input.Binaries, copied.Binaries, "values should be equal") // Verify maps are different pointers (if not nil) if tt.input.Binaries != nil { assert.True(t, &tt.input.Binaries != &copied.Binaries, "maps should have different memory addresses") // Verify each BadBinaryInfo is a different pointer for key, originalInfo := range tt.input.Binaries { copiedInfo := copied.Binaries[key] if originalInfo != nil { assert.NotNil(t, copiedInfo) assert.Equal(t, originalInfo.Reason, copiedInfo.Reason) assert.Equal(t, originalInfo.Operator, copiedInfo.Operator) assert.True(t, originalInfo != copiedInfo, "BadBinaryInfo should be different pointers") // Verify CreatedTimeNano is deep copied if originalInfo.CreatedTimeNano != nil { assert.NotNil(t, copiedInfo.CreatedTimeNano) assert.Equal(t, *originalInfo.CreatedTimeNano, *copiedInfo.CreatedTimeNano) assert.True(t, originalInfo.CreatedTimeNano != copiedInfo.CreatedTimeNano, "CreatedTimeNano pointers should be different") } } } // Verify modifications to original don't affect copy if len(tt.input.Binaries) > 0 { originalLen := len(copied.Binaries) tt.input.Binaries["new-bad"] = &BadBinaryInfo{Reason: "new-reason"} assert.Equal(t, originalLen, len(copied.Binaries), "modifying original should not affect copy") assert.NotContains(t, copied.Binaries, "new-bad") } } }) } } ================================================ FILE: common/types/test_util.go ================================================ package types import ( "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/testing/testdatagen" ) type byteSizer interface{ ByteSize() uint64 } // AssertByteSizeMatchesReflect verifies v.(byteSizer).ByteSize() // matches an independent reflective deep-size computation. func AssertByteSizeMatchesReflect(t *testing.T, v any) { t.Helper() gen := testdatagen.New(t) for i := 0; i < 100; i++ { gen.Fuzz(v) rv := reflect.ValueOf(v) if !rv.IsValid() { t.Fatalf("nil/invalid value") } // Must implement ByteSize (on value or pointer) bs, ok := v.(byteSizer) if !ok { // if they passed a non-pointer, check its pointer receiver too if rv.Kind() != reflect.Ptr { if pv, ok2 := reflect.New(rv.Type()).Interface().(byteSizer); ok2 { bs = pv rv = rv.Addr() ok = true } } } assert.True(t, ok, "%T does not implement ByteSize()", v) // Compare impl vs reflect-based computation. root := rv if root.Kind() == reflect.Ptr { root = root.Elem() } exp := uint64(root.Type().Size()) + structPayloadSize(root) got := bs.ByteSize() assert.Equal(t, exp, got, "ByteSize mismatch for %T: got=%d expected=%d (likely a new/changed field not reflected in ByteSize)", v, got, exp) } } // AssertReachablesImplementByteSize ensures every reachable named struct // under root implements ByteSize (on value or pointer receiver). // You can pass a value (&T{}) or a type via (*T)(nil). func AssertReachablesImplementByteSize(t *testing.T, root any) { t.Helper() rt := reflect.TypeOf(root) assert.NotNil(t, rt, "nil type") if rt.Kind() == reflect.Ptr { rt = rt.Elem() } seen := map[reflect.Type]bool{} var missing []string checkStructHasByteSize(rt, seen, &missing) assert.Equal(t, 0, len(missing), "reachable struct types missing ByteSize(): %v", missing) } // ------------------- helpers ------------------- func structPayloadSize(v reflect.Value) uint64 { if v.Kind() == reflect.Ptr { if v.IsNil() { return 0 } return typePayloadSize(v.Elem()) } return typePayloadSize(v) } func typePayloadSize(v reflect.Value) uint64 { if !v.IsValid() { return 0 } switch v.Kind() { case reflect.Ptr: if v.IsNil() { return 0 } // DO NOT call ByteSize() here — keep independence // the size calculation is done recursively using reflection to provide an independent check on the ByteSize impl // Therefore, the size calculated by reflection is the ground truth and the ByteSize impl must match it elem := v.Elem() // Add the pointed-to value's header size (since it's a separate allocation) // plus its dynamic payload. switch elem.Kind() { case reflect.Struct, reflect.String, reflect.Slice, reflect.Map, reflect.Array: return uint64(elem.Type().Size()) + typePayloadSize(elem) default: // scalar pointed-to: just the scalar's size return uint64(elem.Type().Size()) } case reflect.Struct: var sum uint64 for i := 0; i < v.NumField(); i++ { sum += fieldPayloadSize(v.Field(i)) } return sum case reflect.String: return uint64(v.Len()) // header counted by parent (or pointer case above) case reflect.Slice: return slicePayloadSize(v) case reflect.Array: var sum uint64 for i := 0; i < v.Len(); i++ { sum += typePayloadSize(v.Index(i)) } return sum case reflect.Map: var sum uint64 for _, k := range v.MapKeys() { sum += typePayloadSize(k) sum += typePayloadSize(v.MapIndex(k)) } return sum default: return 0 } } func fieldPayloadSize(f reflect.Value) uint64 { switch f.Kind() { case reflect.Ptr: // pointer slot already in parent header; add referent header+payload if f.IsNil() { return 0 } elem := f.Elem() switch elem.Kind() { case reflect.Struct, reflect.String, reflect.Slice, reflect.Map, reflect.Array: return uint64(elem.Type().Size()) + typePayloadSize(elem) default: return uint64(elem.Type().Size()) } case reflect.Struct, reflect.Map: return typePayloadSize(f) case reflect.String: return uint64(f.Len()) case reflect.Slice: return slicePayloadSize(f) case reflect.Array: var sum uint64 for i := 0; i < f.Len(); i++ { sum += typePayloadSize(f.Index(i)) } return sum default: return 0 } } func slicePayloadSize(v reflect.Value) uint64 { if v.IsNil() { return 0 } n := v.Len() elem := v.Type().Elem() sum := uint64(n) * uint64(elem.Size()) // backing array switch elem.Kind() { case reflect.String, reflect.Slice, reflect.Array, reflect.Map, reflect.Struct, reflect.Ptr: for i := 0; i < n; i++ { sum += typePayloadSize(v.Index(i)) } } return sum } func isScalar(k reflect.Kind) bool { switch k { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: return true default: return false } } func checkStructHasByteSize(t reflect.Type, seen map[reflect.Type]bool, missing *[]string) { switch t.Kind() { case reflect.Ptr: checkStructHasByteSize(t.Elem(), seen, missing) return case reflect.Struct: if t.Name() != "" { if seen[t] { return } seen[t] = true // value or pointer receiver ok if _, ok := t.MethodByName("ByteSize"); !ok { if _, ok := reflect.PtrTo(t).MethodByName("ByteSize"); !ok { *missing = append(*missing, t.String()) } } } for i := 0; i < t.NumField(); i++ { checkStructHasByteSize(t.Field(i).Type, seen, missing) } case reflect.Slice, reflect.Array: checkStructHasByteSize(t.Elem(), seen, missing) case reflect.Map: checkStructHasByteSize(t.Key(), seen, missing) checkStructHasByteSize(t.Elem(), seen, missing) } } ================================================ FILE: common/types/testdata/common.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) const ( WorkflowID = "WorkflowID" RunID = "RunID" RunID1 = "RunID1" RunID2 = "RunID2" RunID3 = "RunID3" ActivityID = "ActivityID" RequestID = "RequestID" TimerID = "TimerID" WorkflowTypeName = "WorkflowTypeName" ActivityTypeName = "ActivityTypeName" TaskListName = "TaskListName" MarkerName = "MarkerName" SignalName = "SignalName" QueryType = "QueryType" HostName = "HostName" HostName2 = "HostName2" Identity = "Identity" CronSchedule = "CronSchedule" Checksum = "Checksum" Reason = "Reason" Cause = "Cause" SecurityToken = "SecurityToken" VisibilityQuery = "VisibilityQuery" FeatureVersion = "FeatureVersion" ClientImpl = "ClientImpl" ClientLibraryVersion = "ClientLibraryVersion" SupportedVersions = "SupportedVersions" FeatureFlag = "FeatureFlag" Namespace = "Namespace" ShardKey = "ShardKey" Attempt = 2 ScheduleID = 5 PageSize = 10 HistoryLength = 20 BacklogCountHint = 30 AckLevel = 1001 ReadLevel = 1002 RatePerSecond = 3.14 TaskID = 444 ShardID = 12345 MessageID1 = 50001 MessageID2 = 50002 EventStoreVersion = 333 HistorySizeInBytes = 77779 EventID1 = int64(1) EventID2 = int64(2) EventID3 = int64(3) EventID4 = int64(4) Version1 = int64(11) Version2 = int64(22) Version3 = int64(33) IsolationGroup = "dca1" ) var ( Timestamp = time.Now().Unix() Timestamp1 = Timestamp + 1 Timestamp2 = Timestamp + 2 Timestamp3 = Timestamp + 3 Timestamp4 = Timestamp + 4 Timestamp5 = Timestamp + 5 ) var ( Duration1 = int32(11) Duration2 = int32(12) Duration3 = int32(13) Duration4 = int32(14) ) var ( Token1 = []byte{1, 0} Token2 = []byte{2, 0} Token3 = []byte{3, 0} ) var ( Payload1 = []byte{10, 0} Payload2 = []byte{20, 0} Payload3 = []byte{30, 0} ) var Attempt2 = int64(2) var ( ExecutionContext = []byte{110, 0} Control = []byte{120, 0} NextPageToken = []byte{130, 0} TaskToken = []byte{140, 0} BranchToken = []byte{150, 0} QueryArgs = []byte{9, 9, 9} ) var ( FailureReason = "FailureReason" FailureDetails = []byte{190, 0} ) var PartitionConfig = map[string]string{ "zone": IsolationGroup, } var ( HealthStatus = types.HealthStatus{ Ok: true, Msg: "HealthStatusMessage", } WorkflowExecution = types.WorkflowExecution{ WorkflowID: WorkflowID, RunID: RunID, } WorkflowType = types.WorkflowType{ Name: WorkflowTypeName, } ActivityType = types.ActivityType{ Name: ActivityTypeName, } TaskList = types.TaskList{ Name: TaskListName, Kind: types.TaskListKindNormal.Ptr(), } RetryPolicy = types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1.1, MaximumIntervalInSeconds: 2, MaximumAttempts: 3, NonRetriableErrorReasons: []string{"a", "b"}, ExpirationIntervalInSeconds: 4, } Header = types.Header{ Fields: map[string][]byte{ "HeaderField1": {211, 0}, "HeaderField2": {212, 0}, }, } Memo = types.Memo{ Fields: map[string][]byte{ "MemoField1": {221, 0}, "MemoField2": {222, 0}, }, } SearchAttributes = types.SearchAttributes{ IndexedFields: map[string][]byte{ "IndexedField1": {231, 0}, "IndexedField2": {232, 0}, }, } PayloadMap = map[string][]byte{ "Payload1": Payload1, "Payload2": Payload2, } ResetPointInfo = types.ResetPointInfo{ BinaryChecksum: Checksum, RunID: RunID1, FirstDecisionCompletedID: EventID1, CreatedTimeNano: &Timestamp1, ExpiringTimeNano: &Timestamp2, Resettable: true, } ResetPointInfoArray = []*types.ResetPointInfo{ &ResetPointInfo, } ResetPoints = types.ResetPoints{ Points: ResetPointInfoArray, } DataBlob = types.DataBlob{ EncodingType: &EncodingType, Data: []byte{7, 7, 7}, } DataBlobArray = []*types.DataBlob{ &DataBlob, } StickyExecutionAttributes = types.StickyExecutionAttributes{ WorkerTaskList: &TaskList, ScheduleToStartTimeoutSeconds: &Duration1, } ActivityLocalDispatchInfo = types.ActivityLocalDispatchInfo{ ActivityID: ActivityID, ScheduledTimestamp: &Timestamp1, StartedTimestamp: &Timestamp2, ScheduledTimestampOfThisAttempt: &Timestamp3, TaskToken: TaskToken, } ActivityLocalDispatchInfoMap = map[string]*types.ActivityLocalDispatchInfo{ ActivityID: &ActivityLocalDispatchInfo, } TaskListMetadata = types.TaskListMetadata{ MaxTasksPerSecond: common.Float64Ptr(RatePerSecond), } WorkerVersionInfo = types.WorkerVersionInfo{ Impl: ClientImpl, FeatureVersion: FeatureVersion, } PollerInfo = types.PollerInfo{ LastAccessTime: &Timestamp1, Identity: Identity, RatePerSecond: RatePerSecond, } PollerInfoArray = []*types.PollerInfo{ &PollerInfo, } TaskListStatus = types.TaskListStatus{ BacklogCountHint: BacklogCountHint, ReadLevel: ReadLevel, AckLevel: AckLevel, RatePerSecond: RatePerSecond, TaskIDBlock: &TaskIDBlock, IsolationGroupMetrics: map[string]*types.IsolationGroupMetrics{ "dca": { NewTasksPerSecond: 10, PollerCount: 1, }, }, NewTasksPerSecond: 10, Empty: true, } TaskIDBlock = types.TaskIDBlock{ StartID: 551, EndID: 559, } TaskListPartitionMetadata = types.TaskListPartitionMetadata{ Key: "Key", OwnerHostName: "HostName", } TaskListPartitionMetadataArray = []*types.TaskListPartitionMetadata{ &TaskListPartitionMetadata, } SupportedClientVersions = types.SupportedClientVersions{ GoSdk: "GoSDK", JavaSdk: "JavaSDK", } IndexedValueTypeMap = map[string]types.IndexedValueType{ "IndexedValueType1": IndexedValueType, } IsolationGroupConfiguration = types.IsolationGroupConfiguration{ IsolationGroup: { Name: IsolationGroup, State: types.IsolationGroupStateHealthy, }, "zone 1": { Name: "zone 1", State: types.IsolationGroupStateDrained, }, } ParentExecutionInfo = types.ParentExecutionInfo{ DomainUUID: DomainID, Domain: DomainName, Execution: &WorkflowExecution, InitiatedID: EventID1, } ClusterInfo = types.ClusterInfo{ SupportedClientVersions: &SupportedClientVersions, } MembershipInfo = types.MembershipInfo{ CurrentHost: &HostInfo, ReachableMembers: []string{HostName}, Rings: RingInfoArray, } HostInfo = types.HostInfo{ Identity: HostName, } HostInfoArray = []*types.HostInfo{ &HostInfo, } RingInfo = types.RingInfo{ Role: "Role", MemberCount: 1, Members: HostInfoArray, } RingInfoArray = []*types.RingInfo{ &RingInfo, } DomainCacheInfo = types.DomainCacheInfo{ NumOfItemsInCacheByID: int64(2000), NumOfItemsInCacheByName: int64(3000), } VersionHistoryItem = types.VersionHistoryItem{ EventID: EventID1, Version: Version1, } VersionHistoryItemArray = []*types.VersionHistoryItem{ &VersionHistoryItem, } VersionHistory = types.VersionHistory{ BranchToken: BranchToken, Items: VersionHistoryItemArray, } VersionHistoryArray = []*types.VersionHistory{ &VersionHistory, } VersionHistories = types.VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: VersionHistoryArray, } TransientDecisionInfo = types.TransientDecisionInfo{ ScheduledEvent: &HistoryEvent_WorkflowExecutionStarted, StartedEvent: &HistoryEvent_WorkflowExecutionStarted, } FailoverMarkerToken = types.FailoverMarkerToken{ ShardIDs: []int32{ShardID}, FailoverMarker: &FailoverMarkerAttributes, } FailoverMarkerTokenArray = []*types.FailoverMarkerToken{ &FailoverMarkerToken, } WorkflowExecutionFilter = types.WorkflowExecutionFilter{ WorkflowID: WorkflowID, RunID: RunID, } WorkflowTypeFilter = types.WorkflowTypeFilter{ Name: WorkflowTypeName, } StartTimeFilter = types.StartTimeFilter{ EarliestTime: &Timestamp1, LatestTime: &Timestamp2, } WorkflowExecutionInfo = types.WorkflowExecutionInfo{ Execution: &WorkflowExecution, Type: &WorkflowType, StartTime: &Timestamp1, CloseTime: &Timestamp2, CloseStatus: &WorkflowExecutionCloseStatus, HistoryLength: HistoryLength, ParentDomainID: common.StringPtr(DomainID), ParentDomain: common.StringPtr(DomainName), ParentExecution: &WorkflowExecution, ParentInitiatedID: common.Int64Ptr(EventID1), ExecutionTime: &Timestamp3, Memo: &Memo, SearchAttributes: &SearchAttributes, AutoResetPoints: &ResetPoints, TaskList: &types.TaskList{Name: TaskListName, Kind: types.TaskListKindNormal.Ptr()}, PartitionConfig: PartitionConfig, CronOverlapPolicy: &CronOverlapPolicy, } WorkflowExecutionInfoEphemeral = types.WorkflowExecutionInfo{ Execution: &WorkflowExecution, Type: &WorkflowType, StartTime: &Timestamp1, CloseTime: &Timestamp2, CloseStatus: &WorkflowExecutionCloseStatus, HistoryLength: HistoryLength, ParentDomainID: common.StringPtr(DomainID), ParentDomain: common.StringPtr(DomainName), ParentExecution: &WorkflowExecution, ParentInitiatedID: common.Int64Ptr(EventID1), ExecutionTime: &Timestamp3, Memo: &Memo, SearchAttributes: &SearchAttributes, AutoResetPoints: &ResetPoints, TaskList: &types.TaskList{Name: TaskListName, Kind: types.TaskListKindEphemeral.Ptr()}, PartitionConfig: PartitionConfig, CronOverlapPolicy: &CronOverlapPolicy, } CronWorkflowExecutionInfo = types.WorkflowExecutionInfo{ Execution: &WorkflowExecution, Type: &WorkflowType, StartTime: &Timestamp1, CloseTime: &Timestamp2, CloseStatus: &WorkflowExecutionCloseStatus, HistoryLength: HistoryLength, ParentDomainID: common.StringPtr(DomainID), ParentDomain: common.StringPtr(DomainName), ParentExecution: &WorkflowExecution, ParentInitiatedID: common.Int64Ptr(EventID1), ExecutionTime: &Timestamp3, Memo: &Memo, SearchAttributes: &SearchAttributes, AutoResetPoints: &ResetPoints, TaskList: &types.TaskList{Name: TaskListName, Kind: types.TaskListKindNormal.Ptr()}, PartitionConfig: PartitionConfig, IsCron: true, CronOverlapPolicy: &CronOverlapPolicy, } WorkflowExecutionInfoArray = []*types.WorkflowExecutionInfo{&WorkflowExecutionInfo} WorkflowQuery = types.WorkflowQuery{ QueryType: QueryType, QueryArgs: QueryArgs, } WorkflowQueryMap = map[string]*types.WorkflowQuery{ "WorkflowQuery1": &WorkflowQuery, } WorkflowQueryResult = types.WorkflowQueryResult{ ResultType: &QueryResultType, Answer: Payload1, ErrorMessage: ErrorMessage, } WorkflowQueryResultMap = map[string]*types.WorkflowQueryResult{ "WorkflowQuery1": &WorkflowQueryResult, } QueryRejected = types.QueryRejected{ CloseStatus: &WorkflowExecutionCloseStatus, } WorkflowExecutionConfiguration = types.WorkflowExecutionConfiguration{ TaskList: &TaskList, ExecutionStartToCloseTimeoutSeconds: &Duration1, TaskStartToCloseTimeoutSeconds: &Duration2, } PendingActivityInfo = types.PendingActivityInfo{ ActivityID: ActivityID, ActivityType: &ActivityType, State: &PendingActivityState, HeartbeatDetails: Payload1, LastHeartbeatTimestamp: &Timestamp1, LastStartedTimestamp: &Timestamp2, Attempt: Attempt, MaximumAttempts: 3, ScheduledTimestamp: &Timestamp3, ExpirationTimestamp: &Timestamp4, LastFailureReason: &FailureReason, LastWorkerIdentity: Identity, LastFailureDetails: FailureDetails, StartedWorkerIdentity: Identity, ScheduleID: ScheduleID, } PendingActivityInfoArray = []*types.PendingActivityInfo{ &PendingActivityInfo, } PendingChildExecutionInfo = types.PendingChildExecutionInfo{ Domain: DomainName, WorkflowID: WorkflowID, RunID: RunID, WorkflowTypeName: WorkflowTypeName, InitiatedID: EventID1, ParentClosePolicy: &ParentClosePolicy, } PendingChildExecutionInfoArray = []*types.PendingChildExecutionInfo{ &PendingChildExecutionInfo, } PendingDecisionInfo = types.PendingDecisionInfo{ State: &PendingDecisionState, ScheduledTimestamp: &Timestamp1, StartedTimestamp: &Timestamp2, Attempt: Attempt, OriginalScheduledTimestamp: &Timestamp3, ScheduleID: ScheduleID, } AutoConfigHint = types.AutoConfigHint{ EnableAutoConfig: false, PollerWaitTimeInMs: 10, } TaskKey = types.TaskKey{ TaskID: TaskID, ScheduledTimeNano: Timestamp1, } TaskKey2 = types.TaskKey{ TaskID: TaskID + 1, ScheduledTimeNano: Timestamp2, } TaskRange = types.TaskRange{ InclusiveMin: &TaskKey, ExclusiveMax: &TaskKey2, } VirtualSliceState = types.VirtualSliceState{ TaskRange: &TaskRange, } VirtualQueueState = types.VirtualQueueState{ VirtualSliceStates: []*types.VirtualSliceState{&VirtualSliceState}, } QueueState = types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ 0: &VirtualQueueState, }, ExclusiveMaxReadLevel: &TaskKey, } ActiveClusterSelectionPolicyRegionSticky = types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyRegionSticky.Ptr(), StickyRegion: "region1", } ActiveClusterSelectionPolicyExternalEntity = types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyExternalEntity.Ptr(), ExternalEntityType: "externalEntityType1", ExternalEntityKey: "externalEntityKey1", } ClusterAttribute = types.ClusterAttribute{ Scope: "region", Name: "us-west-1", } ActiveClusterSelectionPolicyWithClusterAttribute = types.ActiveClusterSelectionPolicy{ ClusterAttribute: &ClusterAttribute, } CronOverlapPolicy = types.CronOverlapPolicySkipped ) ================================================ FILE: common/types/testdata/config_store.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "github.com/uber/cadence/common/types" ) const ( DynamicConfigValueName = "test_config" DynamicConfigFilterName = "test_filter" DynamicConfigEntryName = "test_entry" DynamicConfigBlobSchemaVersion = int64(1) ) var ( DynamicConfigFilter = types.DynamicConfigFilter{ Name: DynamicConfigFilterName, Value: &DataBlob, } DynamicConfigValue = types.DynamicConfigValue{ Value: &DataBlob, Filters: []*types.DynamicConfigFilter{&DynamicConfigFilter}, } DynamicConfigEntry = types.DynamicConfigEntry{ Name: DynamicConfigEntryName, Values: []*types.DynamicConfigValue{&DynamicConfigValue}, } DynamicConfigBlob = types.DynamicConfigBlob{ SchemaVersion: DynamicConfigBlobSchemaVersion, Entries: []*types.DynamicConfigEntry{&DynamicConfigEntry}, } ) ================================================ FILE: common/types/testdata/decision.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) var ( DecisionArray = []*types.Decision{ &Decision_CancelTimer, &Decision_CancelWorkflowExecution, &Decision_CompleteWorkflowExecution, &Decision_ContinueAsNewWorkflowExecution, &Decision_FailWorkflowExecution, &Decision_RecordMarker, &Decision_RequestCancelActivityTask, &Decision_RequestCancelExternalWorkflowExecution, &Decision_ScheduleActivityTask, &Decision_SignalExternalWorkflowExecution, &Decision_StartChildWorkflowExecution, &Decision_StartTimer, &Decision_UpsertWorkflowSearchAttributes, } Decision_CancelTimer = types.Decision{ DecisionType: types.DecisionTypeCancelTimer.Ptr(), CancelTimerDecisionAttributes: &CancelTimerDecisionAttributes, } Decision_CancelWorkflowExecution = types.Decision{ DecisionType: types.DecisionTypeCancelWorkflowExecution.Ptr(), CancelWorkflowExecutionDecisionAttributes: &CancelWorkflowExecutionDecisionAttributes, } Decision_CompleteWorkflowExecution = types.Decision{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &CompleteWorkflowExecutionDecisionAttributes, } Decision_ContinueAsNewWorkflowExecution = types.Decision{ DecisionType: types.DecisionTypeContinueAsNewWorkflowExecution.Ptr(), ContinueAsNewWorkflowExecutionDecisionAttributes: &ContinueAsNewWorkflowExecutionDecisionAttributes, } Decision_FailWorkflowExecution = types.Decision{ DecisionType: types.DecisionTypeFailWorkflowExecution.Ptr(), FailWorkflowExecutionDecisionAttributes: &FailWorkflowExecutionDecisionAttributes, } Decision_RecordMarker = types.Decision{ DecisionType: types.DecisionTypeRecordMarker.Ptr(), RecordMarkerDecisionAttributes: &RecordMarkerDecisionAttributes, } Decision_RequestCancelActivityTask = types.Decision{ DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &RequestCancelActivityTaskDecisionAttributes, } Decision_RequestCancelExternalWorkflowExecution = types.Decision{ DecisionType: types.DecisionTypeRequestCancelExternalWorkflowExecution.Ptr(), RequestCancelExternalWorkflowExecutionDecisionAttributes: &RequestCancelExternalWorkflowExecutionDecisionAttributes, } Decision_ScheduleActivityTask = types.Decision{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &ScheduleActivityTaskDecisionAttributes, } Decision_SignalExternalWorkflowExecution = types.Decision{ DecisionType: types.DecisionTypeSignalExternalWorkflowExecution.Ptr(), SignalExternalWorkflowExecutionDecisionAttributes: &SignalExternalWorkflowExecutionDecisionAttributes, } Decision_StartChildWorkflowExecution = types.Decision{ DecisionType: types.DecisionTypeStartChildWorkflowExecution.Ptr(), StartChildWorkflowExecutionDecisionAttributes: &StartChildWorkflowExecutionDecisionAttributes, } Decision_StartTimer = types.Decision{ DecisionType: types.DecisionTypeStartTimer.Ptr(), StartTimerDecisionAttributes: &StartTimerDecisionAttributes, } Decision_UpsertWorkflowSearchAttributes = types.Decision{ DecisionType: types.DecisionTypeUpsertWorkflowSearchAttributes.Ptr(), UpsertWorkflowSearchAttributesDecisionAttributes: &UpsertWorkflowSearchAttributesDecisionAttributes, } CancelTimerDecisionAttributes = types.CancelTimerDecisionAttributes{ TimerID: TimerID, } CancelWorkflowExecutionDecisionAttributes = types.CancelWorkflowExecutionDecisionAttributes{ Details: Payload1, } CompleteWorkflowExecutionDecisionAttributes = types.CompleteWorkflowExecutionDecisionAttributes{ Result: Payload1, } ContinueAsNewWorkflowExecutionDecisionAttributes = types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: &WorkflowType, TaskList: &TaskList, Input: Payload1, ExecutionStartToCloseTimeoutSeconds: &Duration1, TaskStartToCloseTimeoutSeconds: &Duration2, BackoffStartIntervalInSeconds: &Duration3, RetryPolicy: &RetryPolicy, Initiator: &ContinueAsNewInitiator, FailureReason: &FailureReason, FailureDetails: FailureDetails, LastCompletionResult: Payload2, CronSchedule: CronSchedule, Header: &Header, Memo: &Memo, SearchAttributes: &SearchAttributes, JitterStartSeconds: &Duration1, CronOverlapPolicy: &CronOverlapPolicy, } FailWorkflowExecutionDecisionAttributes = types.FailWorkflowExecutionDecisionAttributes{ Reason: &FailureReason, Details: FailureDetails, } RecordMarkerDecisionAttributes = types.RecordMarkerDecisionAttributes{ MarkerName: MarkerName, Details: Payload1, Header: &Header, } RequestCancelActivityTaskDecisionAttributes = types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: ActivityID, } RequestCancelExternalWorkflowExecutionDecisionAttributes = types.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: DomainName, WorkflowID: WorkflowID, RunID: RunID, Control: Control, ChildWorkflowOnly: true, } ScheduleActivityTaskDecisionAttributes = types.ScheduleActivityTaskDecisionAttributes{ ActivityID: ActivityID, ActivityType: &ActivityType, Domain: DomainName, TaskList: &TaskList, Input: Payload1, ScheduleToCloseTimeoutSeconds: &Duration1, ScheduleToStartTimeoutSeconds: &Duration2, StartToCloseTimeoutSeconds: &Duration3, HeartbeatTimeoutSeconds: &Duration4, RetryPolicy: &RetryPolicy, Header: &Header, RequestLocalDispatch: true, } SignalExternalWorkflowExecutionDecisionAttributes = types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: DomainName, Execution: &WorkflowExecution, SignalName: SignalName, Input: Payload1, Control: Control, ChildWorkflowOnly: true, } StartChildWorkflowExecutionDecisionAttributes = types.StartChildWorkflowExecutionDecisionAttributes{ Domain: DomainName, WorkflowID: WorkflowID, WorkflowType: &WorkflowType, TaskList: &TaskList, Input: Payload1, ExecutionStartToCloseTimeoutSeconds: &Duration1, TaskStartToCloseTimeoutSeconds: &Duration2, ParentClosePolicy: &ParentClosePolicy, Control: Control, WorkflowIDReusePolicy: &WorkflowIDReusePolicy, RetryPolicy: &RetryPolicy, CronSchedule: CronSchedule, Header: &Header, Memo: &Memo, SearchAttributes: &SearchAttributes, CronOverlapPolicy: &CronOverlapPolicy, } StartTimerDecisionAttributes = types.StartTimerDecisionAttributes{ TimerID: TimerID, StartToFireTimeoutSeconds: common.Int64Ptr(int64(Duration1)), } UpsertWorkflowSearchAttributesDecisionAttributes = types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: &SearchAttributes, } ) ================================================ FILE: common/types/testdata/domain.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import "github.com/uber/cadence/common/types" const ( DomainID = "DomainID" DomainName = "DomainName" DomainDescription = "DomainDescription" DomainOwnerEmail = "DomainOwnerEmail" DomainDataKey = "DomainDataKey" DomainDataValue = "DomainDataValue" DomainRetention = 3 DomainEmitMetric = true ClusterName1 = "ClusterName1" ClusterName2 = "ClusterName2" Region1 = "Region1" Region2 = "Region2" BadBinaryReason = "BadBinaryReason" BadBinaryOperator = "BadBinaryOperator" HistoryArchivalURI = "HistoryArchivalURI" VisibilityArchivalURI = "VisibilityArchivalURI" DeleteBadBinary = "DeleteBadBinary" FailoverVersion1 = 301 FailoverVersion2 = 302 ErrorReason = "ErrorReason" FailoverEventID = "failover-event-123" FailoverTime = int64(1234567890000000000) ) var ( BadBinaryInfo = types.BadBinaryInfo{ Reason: BadBinaryReason, Operator: BadBinaryOperator, CreatedTimeNano: &Timestamp1, } BadBinaryInfoMap = map[string]*types.BadBinaryInfo{ "BadBinary1": &BadBinaryInfo, } BadBinaries = types.BadBinaries{ Binaries: BadBinaryInfoMap, } DomainData = map[string]string{DomainDataKey: DomainDataValue} DomainInfo = types.DomainInfo{ Name: DomainName, Status: &DomainStatus, Description: DomainDescription, OwnerEmail: DomainOwnerEmail, Data: DomainData, UUID: DomainID, } DomainConfiguration = types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: DomainRetention, EmitMetric: DomainEmitMetric, BadBinaries: &BadBinaries, HistoryArchivalStatus: &ArchivalStatus, HistoryArchivalURI: HistoryArchivalURI, VisibilityArchivalStatus: &ArchivalStatus, VisibilityArchivalURI: VisibilityArchivalURI, IsolationGroups: &types.IsolationGroupConfiguration{ "zone-1": { Name: "zone-1", State: types.IsolationGroupStateHealthy, }, "zone-2": { Name: "zone-2", State: types.IsolationGroupStateDrained, }, }, AsyncWorkflowConfig: &types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "custom", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("custom queue config"), }, }, } DomainReplicationConfiguration = types.DomainReplicationConfiguration{ ActiveClusterName: ClusterName1, Clusters: ClusterReplicationConfigurationArray, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ Region1: { ActiveClusterName: ClusterName1, FailoverVersion: FailoverVersion1, }, Region2: { ActiveClusterName: ClusterName2, FailoverVersion: FailoverVersion2, }, }, }, }, }, } ClusterReplicationConfiguration = types.ClusterReplicationConfiguration{ ClusterName: ClusterName1, } ClusterReplicationConfigurationArray = []*types.ClusterReplicationConfiguration{ &ClusterReplicationConfiguration, } FailoverInfo = types.FailoverInfo{ FailoverVersion: 1, FailoverStartTimestamp: 1, FailoverExpireTimestamp: 10, CompletedShardCount: 10, PendingShards: []int32{1, 2, 3}, } ActiveClusterInfo1 = types.ActiveClusterInfo{ ActiveClusterName: ClusterName1, FailoverVersion: FailoverVersion1, } ActiveClusterInfo2 = types.ActiveClusterInfo{ ActiveClusterName: ClusterName2, FailoverVersion: FailoverVersion2, } ClusterAttributeFailover = types.ClusterAttribute{ Scope: "region", Name: Region1, } ClusterFailover = types.ClusterFailover{ FromCluster: &ActiveClusterInfo1, ToCluster: &ActiveClusterInfo2, ClusterAttribute: &ClusterAttributeFailover, } FailoverEventIDPtr = FailoverEventID FailoverTimePtr = FailoverTime FailoverEvent = types.FailoverEvent{ ID: &FailoverEventIDPtr, CreatedTime: &FailoverTimePtr, FailoverType: types.FailoverTypeForce.Ptr(), ClusterFailovers: []*types.ClusterFailover{&ClusterFailover}, } ListFailoverHistoryRequestFilters = types.ListFailoverHistoryRequestFilters{ DomainID: DomainID, } PaginationOptions = types.PaginationOptions{ PageSize: &[]int32{10}[0], NextPageToken: []byte("next-page-token"), } ListFailoverHistoryRequest = types.ListFailoverHistoryRequest{ Filters: &ListFailoverHistoryRequestFilters, Pagination: &PaginationOptions, } ListFailoverHistoryResponse = types.ListFailoverHistoryResponse{ FailoverEvents: []*types.FailoverEvent{&FailoverEvent}, NextPageToken: []byte("next-page-token"), } ) ================================================ FILE: common/types/testdata/enum.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import "github.com/uber/cadence/common/types" var ( ArchivalStatus = types.ArchivalStatusEnabled CancelExternalWorkflowExecutionFailedCause = types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution ChildWorkflowExecutionFailedCause = types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning ContinueAsNewInitiator = types.ContinueAsNewInitiatorRetryPolicy DecisionTaskFailedCause = types.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes DecisionTaskTimedOutCause = types.DecisionTaskTimedOutCauseReset DecisionType = types.DecisionTypeCancelTimer DomainStatus = types.DomainStatusRegistered EncodingType = types.EncodingTypeJSON EventType = types.EventTypeWorkflowExecutionStarted HistoryEventFilterType = types.HistoryEventFilterTypeCloseEvent IndexedValueType = types.IndexedValueTypeInt ParentClosePolicy = types.ParentClosePolicyTerminate ParentClosePolicy2 = types.ParentClosePolicyRequestCancel PendingActivityState = types.PendingActivityStateCancelRequested PendingDecisionState = types.PendingDecisionStateStarted QueryConsistencyLevel = types.QueryConsistencyLevelStrong QueryRejectCondition = types.QueryRejectConditionNotCompletedCleanly QueryResultType = types.QueryResultTypeFailed QueryTaskCompletedType = types.QueryTaskCompletedTypeFailed SignalExternalWorkflowExecutionFailedCause = types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution TaskListKind = types.TaskListKindSticky TaskListType = types.TaskListTypeActivity TimeoutType = types.TimeoutTypeScheduleToStart WorkflowExecutionCloseStatus = types.WorkflowExecutionCloseStatusContinuedAsNew WorkflowIDReusePolicy = types.WorkflowIDReusePolicyTerminateIfRunning ) ================================================ FILE: common/types/testdata/error.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "github.com/uber/cadence/common" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/types" ) const ( ErrorMessage = "ErrorMessage" ) var ( AccessDeniedError = types.AccessDeniedError{ Message: ErrorMessage, } BadRequestError = types.BadRequestError{ Message: ErrorMessage, } CancellationAlreadyRequestedError = types.CancellationAlreadyRequestedError{ Message: ErrorMessage, } ClientVersionNotSupportedError = types.ClientVersionNotSupportedError{ FeatureVersion: FeatureVersion, ClientImpl: ClientImpl, SupportedVersions: SupportedVersions, } FeatureNotEnabledError = types.FeatureNotEnabledError{ FeatureFlag: FeatureFlag, } CurrentBranchChangedError = types.CurrentBranchChangedError{ Message: ErrorMessage, CurrentBranchToken: BranchToken, } DomainAlreadyExistsError = types.DomainAlreadyExistsError{ Message: ErrorMessage, } DomainNotActiveError = types.DomainNotActiveError{ Message: ErrorMessage, DomainName: DomainName, CurrentCluster: ClusterName1, ActiveCluster: ClusterName2, ActiveClusters: []string{ClusterName2}, } EntityNotExistsError = types.EntityNotExistsError{ Message: ErrorMessage, CurrentCluster: ClusterName1, ActiveCluster: ClusterName2, ActiveClusters: []string{ClusterName2}, } WorkflowExecutionAlreadyCompletedError = types.WorkflowExecutionAlreadyCompletedError{ Message: ErrorMessage, } EventAlreadyStartedError = types.EventAlreadyStartedError{ Message: ErrorMessage, } InternalDataInconsistencyError = types.InternalDataInconsistencyError{ Message: ErrorMessage, } InternalServiceError = types.InternalServiceError{ Message: ErrorMessage, } LimitExceededError = types.LimitExceededError{ Message: ErrorMessage, } QueryFailedError = types.QueryFailedError{ Message: ErrorMessage, } RemoteSyncMatchedError = types.RemoteSyncMatchedError{ Message: ErrorMessage, } RetryTaskV2Error = types.RetryTaskV2Error{ Message: ErrorMessage, DomainID: DomainID, WorkflowID: WorkflowID, RunID: RunID, StartEventID: common.Int64Ptr(EventID1), StartEventVersion: common.Int64Ptr(Version1), EndEventID: common.Int64Ptr(EventID2), EndEventVersion: common.Int64Ptr(Version2), } ServiceBusyError = types.ServiceBusyError{ Message: ErrorMessage, Reason: ErrorReason, } ShardOwnershipLostError = types.ShardOwnershipLostError{ Message: ErrorMessage, Owner: HostName, } WorkflowExecutionAlreadyStartedError = types.WorkflowExecutionAlreadyStartedError{ Message: ErrorMessage, StartRequestID: RequestID, RunID: RunID, } StickyWorkerUnavailableError = types.StickyWorkerUnavailableError{ Message: ErrorMessage, } ReadOnlyPartitionError = types.ReadOnlyPartitionError{ Message: ErrorMessage, } TaskListNotOwnedByHostError = cadence_errors.TaskListNotOwnedByHostError{ OwnedByIdentity: HostName, MyIdentity: HostName2, TasklistName: TaskListName, } NamespaceNotFoundError = types.NamespaceNotFoundError{ Namespace: Namespace, } ShardNotFoundError = types.ShardNotFoundError{ Namespace: Namespace, ShardKey: ShardKey, } ) var Errors = []error{ &AccessDeniedError, &BadRequestError, &CancellationAlreadyRequestedError, &ClientVersionNotSupportedError, &FeatureNotEnabledError, &CurrentBranchChangedError, &DomainAlreadyExistsError, &DomainNotActiveError, &EntityNotExistsError, &WorkflowExecutionAlreadyCompletedError, &EventAlreadyStartedError, &InternalDataInconsistencyError, &InternalServiceError, &LimitExceededError, &QueryFailedError, &RemoteSyncMatchedError, &RetryTaskV2Error, &ServiceBusyError, &ShardOwnershipLostError, &WorkflowExecutionAlreadyStartedError, &StickyWorkerUnavailableError, &ReadOnlyPartitionError, &TaskListNotOwnedByHostError, &NamespaceNotFoundError, &ShardNotFoundError, } ================================================ FILE: common/types/testdata/history.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "github.com/pborman/uuid" "github.com/uber/cadence/.gen/go/history" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) var ( History = types.History{ Events: HistoryEventArray, } HistoryEventArray = []*types.HistoryEvent{ &HistoryEvent_WorkflowExecutionStarted, &HistoryEvent_WorkflowExecutionStarted, &HistoryEvent_WorkflowExecutionCompleted, &HistoryEvent_WorkflowExecutionFailed, &HistoryEvent_WorkflowExecutionTimedOut, &HistoryEvent_DecisionTaskScheduled, &HistoryEvent_DecisionTaskStarted, &HistoryEvent_DecisionTaskCompleted, &HistoryEvent_DecisionTaskTimedOut, &HistoryEvent_DecisionTaskFailed, &HistoryEvent_ActivityTaskScheduled, &HistoryEvent_ActivityTaskStarted, &HistoryEvent_ActivityTaskCompleted, &HistoryEvent_ActivityTaskFailed, &HistoryEvent_ActivityTaskTimedOut, &HistoryEvent_ActivityTaskCancelRequested, &HistoryEvent_RequestCancelActivityTaskFailed, &HistoryEvent_ActivityTaskCanceled, &HistoryEvent_TimerStarted, &HistoryEvent_TimerFired, &HistoryEvent_CancelTimerFailed, &HistoryEvent_TimerCanceled, &HistoryEvent_WorkflowExecutionCancelRequested, &HistoryEvent_WorkflowExecutionCanceled, &HistoryEvent_RequestCancelExternalWorkflowExecutionInitiated, &HistoryEvent_RequestCancelExternalWorkflowExecutionFailed, &HistoryEvent_ExternalWorkflowExecutionCancelRequested, &HistoryEvent_MarkerRecorded, &HistoryEvent_WorkflowExecutionSignaled, &HistoryEvent_WorkflowExecutionTerminated, &HistoryEvent_WorkflowExecutionContinuedAsNew, &HistoryEvent_StartChildWorkflowExecutionInitiated, &HistoryEvent_StartChildWorkflowExecutionFailed, &HistoryEvent_ChildWorkflowExecutionStarted, &HistoryEvent_ChildWorkflowExecutionCompleted, &HistoryEvent_ChildWorkflowExecutionFailed, &HistoryEvent_ChildWorkflowExecutionCanceled, &HistoryEvent_ChildWorkflowExecutionTimedOut, &HistoryEvent_ChildWorkflowExecutionTerminated, &HistoryEvent_SignalExternalWorkflowExecutionInitiated, &HistoryEvent_SignalExternalWorkflowExecutionFailed, &HistoryEvent_ExternalWorkflowExecutionSignaled, &HistoryEvent_UpsertWorkflowSearchAttributes, } HistoryEvent_WorkflowExecutionStarted = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeWorkflowExecutionStarted.Ptr() e.WorkflowExecutionStartedEventAttributes = &WorkflowExecutionStartedEventAttributes }) HistoryEvent_WorkflowExecutionCompleted = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeWorkflowExecutionCompleted.Ptr() e.WorkflowExecutionCompletedEventAttributes = &WorkflowExecutionCompletedEventAttributes }) HistoryEvent_WorkflowExecutionFailed = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeWorkflowExecutionFailed.Ptr() e.WorkflowExecutionFailedEventAttributes = &WorkflowExecutionFailedEventAttributes }) HistoryEvent_WorkflowExecutionTimedOut = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeWorkflowExecutionTimedOut.Ptr() e.WorkflowExecutionTimedOutEventAttributes = &WorkflowExecutionTimedOutEventAttributes }) HistoryEvent_DecisionTaskScheduled = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeDecisionTaskScheduled.Ptr() e.DecisionTaskScheduledEventAttributes = &DecisionTaskScheduledEventAttributes }) HistoryEvent_DecisionTaskStarted = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeDecisionTaskStarted.Ptr() e.DecisionTaskStartedEventAttributes = &DecisionTaskStartedEventAttributes }) HistoryEvent_DecisionTaskCompleted = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeDecisionTaskCompleted.Ptr() e.DecisionTaskCompletedEventAttributes = &DecisionTaskCompletedEventAttributes }) HistoryEvent_DecisionTaskTimedOut = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeDecisionTaskTimedOut.Ptr() e.DecisionTaskTimedOutEventAttributes = &DecisionTaskTimedOutEventAttributes }) HistoryEvent_DecisionTaskFailed = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeDecisionTaskFailed.Ptr() e.DecisionTaskFailedEventAttributes = &DecisionTaskFailedEventAttributes }) HistoryEvent_ActivityTaskScheduled = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeActivityTaskScheduled.Ptr() e.ActivityTaskScheduledEventAttributes = &ActivityTaskScheduledEventAttributes }) HistoryEvent_ActivityTaskStarted = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeActivityTaskStarted.Ptr() e.ActivityTaskStartedEventAttributes = &ActivityTaskStartedEventAttributes }) HistoryEvent_ActivityTaskCompleted = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeActivityTaskCompleted.Ptr() e.ActivityTaskCompletedEventAttributes = &ActivityTaskCompletedEventAttributes }) HistoryEvent_ActivityTaskFailed = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeActivityTaskFailed.Ptr() e.ActivityTaskFailedEventAttributes = &ActivityTaskFailedEventAttributes }) HistoryEvent_ActivityTaskTimedOut = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeActivityTaskTimedOut.Ptr() e.ActivityTaskTimedOutEventAttributes = &ActivityTaskTimedOutEventAttributes }) HistoryEvent_ActivityTaskCancelRequested = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeActivityTaskCancelRequested.Ptr() e.ActivityTaskCancelRequestedEventAttributes = &ActivityTaskCancelRequestedEventAttributes }) HistoryEvent_RequestCancelActivityTaskFailed = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeRequestCancelActivityTaskFailed.Ptr() e.RequestCancelActivityTaskFailedEventAttributes = &RequestCancelActivityTaskFailedEventAttributes }) HistoryEvent_ActivityTaskCanceled = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeActivityTaskCanceled.Ptr() e.ActivityTaskCanceledEventAttributes = &ActivityTaskCanceledEventAttributes }) HistoryEvent_TimerStarted = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeTimerStarted.Ptr() e.TimerStartedEventAttributes = &TimerStartedEventAttributes }) HistoryEvent_TimerFired = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeTimerFired.Ptr() e.TimerFiredEventAttributes = &TimerFiredEventAttributes }) HistoryEvent_CancelTimerFailed = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeCancelTimerFailed.Ptr() e.CancelTimerFailedEventAttributes = &CancelTimerFailedEventAttributes }) HistoryEvent_TimerCanceled = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeTimerCanceled.Ptr() e.TimerCanceledEventAttributes = &TimerCanceledEventAttributes }) HistoryEvent_WorkflowExecutionCancelRequested = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeWorkflowExecutionCancelRequested.Ptr() e.WorkflowExecutionCancelRequestedEventAttributes = &WorkflowExecutionCancelRequestedEventAttributes }) HistoryEvent_WorkflowExecutionCanceled = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeWorkflowExecutionCanceled.Ptr() e.WorkflowExecutionCanceledEventAttributes = &WorkflowExecutionCanceledEventAttributes }) HistoryEvent_RequestCancelExternalWorkflowExecutionInitiated = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeRequestCancelExternalWorkflowExecutionInitiated.Ptr() e.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes = &RequestCancelExternalWorkflowExecutionInitiatedEventAttributes }) HistoryEvent_RequestCancelExternalWorkflowExecutionFailed = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeRequestCancelExternalWorkflowExecutionFailed.Ptr() e.RequestCancelExternalWorkflowExecutionFailedEventAttributes = &RequestCancelExternalWorkflowExecutionFailedEventAttributes }) HistoryEvent_ExternalWorkflowExecutionCancelRequested = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeExternalWorkflowExecutionCancelRequested.Ptr() e.ExternalWorkflowExecutionCancelRequestedEventAttributes = &ExternalWorkflowExecutionCancelRequestedEventAttributes }) HistoryEvent_MarkerRecorded = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeMarkerRecorded.Ptr() e.MarkerRecordedEventAttributes = &MarkerRecordedEventAttributes }) HistoryEvent_WorkflowExecutionSignaled = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeWorkflowExecutionSignaled.Ptr() e.WorkflowExecutionSignaledEventAttributes = &WorkflowExecutionSignaledEventAttributes }) HistoryEvent_WorkflowExecutionTerminated = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeWorkflowExecutionTerminated.Ptr() e.WorkflowExecutionTerminatedEventAttributes = &WorkflowExecutionTerminatedEventAttributes }) HistoryEvent_WorkflowExecutionContinuedAsNew = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeWorkflowExecutionContinuedAsNew.Ptr() e.WorkflowExecutionContinuedAsNewEventAttributes = &WorkflowExecutionContinuedAsNewEventAttributes }) HistoryEvent_StartChildWorkflowExecutionInitiated = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeStartChildWorkflowExecutionInitiated.Ptr() e.StartChildWorkflowExecutionInitiatedEventAttributes = &StartChildWorkflowExecutionInitiatedEventAttributes }) HistoryEvent_StartChildWorkflowExecutionFailed = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeStartChildWorkflowExecutionFailed.Ptr() e.StartChildWorkflowExecutionFailedEventAttributes = &StartChildWorkflowExecutionFailedEventAttributes }) HistoryEvent_ChildWorkflowExecutionStarted = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeChildWorkflowExecutionStarted.Ptr() e.ChildWorkflowExecutionStartedEventAttributes = &ChildWorkflowExecutionStartedEventAttributes }) HistoryEvent_ChildWorkflowExecutionCompleted = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeChildWorkflowExecutionCompleted.Ptr() e.ChildWorkflowExecutionCompletedEventAttributes = &ChildWorkflowExecutionCompletedEventAttributes }) HistoryEvent_ChildWorkflowExecutionFailed = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeChildWorkflowExecutionFailed.Ptr() e.ChildWorkflowExecutionFailedEventAttributes = &ChildWorkflowExecutionFailedEventAttributes }) HistoryEvent_ChildWorkflowExecutionCanceled = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeChildWorkflowExecutionCanceled.Ptr() e.ChildWorkflowExecutionCanceledEventAttributes = &ChildWorkflowExecutionCanceledEventAttributes }) HistoryEvent_ChildWorkflowExecutionTimedOut = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeChildWorkflowExecutionTimedOut.Ptr() e.ChildWorkflowExecutionTimedOutEventAttributes = &ChildWorkflowExecutionTimedOutEventAttributes }) HistoryEvent_ChildWorkflowExecutionTerminated = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeChildWorkflowExecutionTerminated.Ptr() e.ChildWorkflowExecutionTerminatedEventAttributes = &ChildWorkflowExecutionTerminatedEventAttributes }) HistoryEvent_SignalExternalWorkflowExecutionInitiated = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeSignalExternalWorkflowExecutionInitiated.Ptr() e.SignalExternalWorkflowExecutionInitiatedEventAttributes = &SignalExternalWorkflowExecutionInitiatedEventAttributes }) HistoryEvent_SignalExternalWorkflowExecutionFailed = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeSignalExternalWorkflowExecutionFailed.Ptr() e.SignalExternalWorkflowExecutionFailedEventAttributes = &SignalExternalWorkflowExecutionFailedEventAttributes }) HistoryEvent_ExternalWorkflowExecutionSignaled = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeExternalWorkflowExecutionSignaled.Ptr() e.ExternalWorkflowExecutionSignaledEventAttributes = &ExternalWorkflowExecutionSignaledEventAttributes }) HistoryEvent_UpsertWorkflowSearchAttributes = generateEvent(func(e *types.HistoryEvent) { e.EventType = types.EventTypeUpsertWorkflowSearchAttributes.Ptr() e.UpsertWorkflowSearchAttributesEventAttributes = &UpsertWorkflowSearchAttributesEventAttributes }) WorkflowExecutionStartedEventAttributes = types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &WorkflowType, ParentWorkflowDomainID: common.StringPtr(DomainID), ParentWorkflowDomain: common.StringPtr(DomainName), ParentWorkflowExecution: &WorkflowExecution, ParentInitiatedEventID: common.Int64Ptr(EventID1), TaskList: &TaskList, Input: Payload1, ExecutionStartToCloseTimeoutSeconds: &Duration1, TaskStartToCloseTimeoutSeconds: &Duration2, ContinuedExecutionRunID: RunID1, Initiator: &ContinueAsNewInitiator, ContinuedFailureReason: &FailureReason, ContinuedFailureDetails: FailureDetails, LastCompletionResult: Payload2, OriginalExecutionRunID: RunID2, Identity: Identity, FirstExecutionRunID: RunID3, RetryPolicy: &RetryPolicy, Attempt: Attempt, ExpirationTimestamp: &Timestamp1, CronSchedule: CronSchedule, FirstDecisionTaskBackoffSeconds: &Duration3, Memo: &Memo, SearchAttributes: &SearchAttributes, PrevAutoResetPoints: &ResetPoints, Header: &Header, PartitionConfig: PartitionConfig, RequestID: RequestID, ActiveClusterSelectionPolicy: &ActiveClusterSelectionPolicyExternalEntity, CronOverlapPolicy: &CronOverlapPolicy, } WorkflowExecutionCompletedEventAttributes = types.WorkflowExecutionCompletedEventAttributes{ Result: Payload1, DecisionTaskCompletedEventID: EventID1, } WorkflowExecutionFailedEventAttributes = types.WorkflowExecutionFailedEventAttributes{ Reason: &FailureReason, Details: FailureDetails, DecisionTaskCompletedEventID: EventID1, } WorkflowExecutionTimedOutEventAttributes = types.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: &TimeoutType, } DecisionTaskScheduledEventAttributes = types.DecisionTaskScheduledEventAttributes{ TaskList: &TaskList, StartToCloseTimeoutSeconds: &Duration1, Attempt: Attempt, } DecisionTaskStartedEventAttributes = types.DecisionTaskStartedEventAttributes{ ScheduledEventID: EventID1, Identity: Identity, RequestID: RequestID, } DecisionTaskCompletedEventAttributes = types.DecisionTaskCompletedEventAttributes{ ExecutionContext: ExecutionContext, ScheduledEventID: EventID1, StartedEventID: EventID2, Identity: Identity, BinaryChecksum: Checksum, } DecisionTaskTimedOutEventAttributes = types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: EventID1, StartedEventID: EventID2, TimeoutType: &TimeoutType, BaseRunID: RunID1, NewRunID: RunID2, ForkEventVersion: Version1, Reason: Reason, Cause: &DecisionTaskTimedOutCause, RequestID: RequestID, } DecisionTaskFailedEventAttributes = types.DecisionTaskFailedEventAttributes{ ScheduledEventID: EventID1, StartedEventID: EventID2, Cause: &DecisionTaskFailedCause, Details: FailureDetails, Identity: Identity, Reason: &FailureReason, BaseRunID: RunID1, NewRunID: RunID2, ForkEventVersion: Version1, BinaryChecksum: Checksum, RequestID: RequestID, } ActivityTaskScheduledEventAttributes = types.ActivityTaskScheduledEventAttributes{ ActivityID: ActivityID, ActivityType: &ActivityType, Domain: common.StringPtr(DomainName), TaskList: &TaskList, Input: Payload1, ScheduleToCloseTimeoutSeconds: &Duration1, ScheduleToStartTimeoutSeconds: &Duration2, StartToCloseTimeoutSeconds: &Duration3, HeartbeatTimeoutSeconds: &Duration4, DecisionTaskCompletedEventID: EventID1, RetryPolicy: &RetryPolicy, Header: &Header, } ActivityTaskStartedEventAttributes = types.ActivityTaskStartedEventAttributes{ ScheduledEventID: EventID1, Identity: Identity, RequestID: RequestID, Attempt: Attempt, LastFailureReason: &FailureReason, LastFailureDetails: FailureDetails, } ActivityTaskCompletedEventAttributes = types.ActivityTaskCompletedEventAttributes{ Result: Payload1, ScheduledEventID: EventID1, StartedEventID: EventID2, Identity: Identity, } ActivityTaskFailedEventAttributes = types.ActivityTaskFailedEventAttributes{ Reason: &FailureReason, Details: FailureDetails, ScheduledEventID: EventID1, StartedEventID: EventID2, Identity: Identity, } ActivityTaskTimedOutEventAttributes = types.ActivityTaskTimedOutEventAttributes{ Details: Payload1, ScheduledEventID: EventID1, StartedEventID: EventID2, TimeoutType: &TimeoutType, LastFailureReason: &FailureReason, LastFailureDetails: FailureDetails, } TimerStartedEventAttributes = types.TimerStartedEventAttributes{ TimerID: TimerID, StartToFireTimeoutSeconds: common.Int64Ptr(int64(Duration1)), DecisionTaskCompletedEventID: EventID1, } TimerFiredEventAttributes = types.TimerFiredEventAttributes{ TimerID: TimerID, StartedEventID: EventID1, } ActivityTaskCancelRequestedEventAttributes = types.ActivityTaskCancelRequestedEventAttributes{ ActivityID: ActivityID, DecisionTaskCompletedEventID: EventID1, } RequestCancelActivityTaskFailedEventAttributes = types.RequestCancelActivityTaskFailedEventAttributes{ ActivityID: ActivityID, Cause: Cause, DecisionTaskCompletedEventID: EventID1, } ActivityTaskCanceledEventAttributes = types.ActivityTaskCanceledEventAttributes{ Details: Payload1, LatestCancelRequestedEventID: EventID1, ScheduledEventID: EventID2, StartedEventID: EventID3, Identity: Identity, } TimerCanceledEventAttributes = types.TimerCanceledEventAttributes{ TimerID: TimerID, StartedEventID: EventID1, DecisionTaskCompletedEventID: EventID2, Identity: Identity, } CancelTimerFailedEventAttributes = types.CancelTimerFailedEventAttributes{ TimerID: TimerID, Cause: Cause, DecisionTaskCompletedEventID: EventID1, Identity: Identity, } MarkerRecordedEventAttributes = types.MarkerRecordedEventAttributes{ MarkerName: MarkerName, Details: Payload1, DecisionTaskCompletedEventID: EventID1, Header: &Header, } WorkflowExecutionSignaledEventAttributes = types.WorkflowExecutionSignaledEventAttributes{ SignalName: SignalName, Input: Payload1, Identity: Identity, RequestID: RequestID, } WorkflowExecutionTerminatedEventAttributes = types.WorkflowExecutionTerminatedEventAttributes{ Reason: Reason, Details: Payload1, Identity: Identity, } WorkflowExecutionCancelRequestedEventAttributes = types.WorkflowExecutionCancelRequestedEventAttributes{ Cause: Cause, ExternalInitiatedEventID: common.Int64Ptr(EventID1), ExternalWorkflowExecution: &WorkflowExecution, Identity: Identity, RequestID: RequestID, } WorkflowExecutionCanceledEventAttributes = types.WorkflowExecutionCanceledEventAttributes{ DecisionTaskCompletedEventID: EventID1, Details: Payload1, } RequestCancelExternalWorkflowExecutionInitiatedEventAttributes = types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventID: EventID1, Domain: DomainName, WorkflowExecution: &WorkflowExecution, Control: Control, ChildWorkflowOnly: true, } RequestCancelExternalWorkflowExecutionFailedEventAttributes = types.RequestCancelExternalWorkflowExecutionFailedEventAttributes{ Cause: &CancelExternalWorkflowExecutionFailedCause, DecisionTaskCompletedEventID: EventID1, Domain: DomainName, WorkflowExecution: &WorkflowExecution, InitiatedEventID: EventID2, Control: Control, } ExternalWorkflowExecutionCancelRequestedEventAttributes = types.ExternalWorkflowExecutionCancelRequestedEventAttributes{ InitiatedEventID: EventID1, Domain: DomainName, WorkflowExecution: &WorkflowExecution, } WorkflowExecutionContinuedAsNewEventAttributes = types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: RunID1, WorkflowType: &WorkflowType, TaskList: &TaskList, Input: Payload1, ExecutionStartToCloseTimeoutSeconds: &Duration1, TaskStartToCloseTimeoutSeconds: &Duration2, DecisionTaskCompletedEventID: EventID1, BackoffStartIntervalInSeconds: &Duration3, Initiator: &ContinueAsNewInitiator, FailureReason: &FailureReason, FailureDetails: FailureDetails, LastCompletionResult: Payload2, Header: &Header, Memo: &Memo, SearchAttributes: &SearchAttributes, ActiveClusterSelectionPolicy: &ActiveClusterSelectionPolicyExternalEntity, CronOverlapPolicy: &CronOverlapPolicy, } StartChildWorkflowExecutionInitiatedEventAttributes = types.StartChildWorkflowExecutionInitiatedEventAttributes{ Domain: DomainName, WorkflowID: WorkflowID, WorkflowType: &WorkflowType, TaskList: &TaskList, Input: Payload1, ExecutionStartToCloseTimeoutSeconds: &Duration1, TaskStartToCloseTimeoutSeconds: &Duration2, ParentClosePolicy: &ParentClosePolicy, Control: Control, DecisionTaskCompletedEventID: EventID1, WorkflowIDReusePolicy: &WorkflowIDReusePolicy, RetryPolicy: &RetryPolicy, CronSchedule: CronSchedule, Header: &Header, Memo: &Memo, SearchAttributes: &SearchAttributes, DelayStartSeconds: &Duration3, JitterStartSeconds: &Duration4, FirstRunAtTimestamp: &Timestamp1, CronOverlapPolicy: &CronOverlapPolicy, } StartChildWorkflowExecutionFailedEventAttributes = types.StartChildWorkflowExecutionFailedEventAttributes{ Domain: DomainName, WorkflowID: WorkflowID, WorkflowType: &WorkflowType, Cause: &ChildWorkflowExecutionFailedCause, Control: Control, InitiatedEventID: EventID1, DecisionTaskCompletedEventID: EventID2, } ChildWorkflowExecutionStartedEventAttributes = types.ChildWorkflowExecutionStartedEventAttributes{ Domain: DomainName, InitiatedEventID: EventID1, WorkflowExecution: &WorkflowExecution, WorkflowType: &WorkflowType, Header: &Header, } ChildWorkflowExecutionCompletedEventAttributes = types.ChildWorkflowExecutionCompletedEventAttributes{ Result: Payload1, Domain: DomainName, WorkflowExecution: &WorkflowExecution, WorkflowType: &WorkflowType, InitiatedEventID: EventID1, StartedEventID: EventID2, } ChildWorkflowExecutionFailedEventAttributes = types.ChildWorkflowExecutionFailedEventAttributes{ Reason: &FailureReason, Details: FailureDetails, Domain: DomainName, WorkflowExecution: &WorkflowExecution, WorkflowType: &WorkflowType, InitiatedEventID: EventID1, StartedEventID: EventID2, } ChildWorkflowExecutionCanceledEventAttributes = types.ChildWorkflowExecutionCanceledEventAttributes{ Details: Payload1, Domain: DomainName, WorkflowExecution: &WorkflowExecution, WorkflowType: &WorkflowType, InitiatedEventID: EventID1, StartedEventID: EventID2, } ChildWorkflowExecutionTimedOutEventAttributes = types.ChildWorkflowExecutionTimedOutEventAttributes{ TimeoutType: &TimeoutType, Domain: DomainName, WorkflowExecution: &WorkflowExecution, WorkflowType: &WorkflowType, InitiatedEventID: EventID1, StartedEventID: EventID2, } ChildWorkflowExecutionTerminatedEventAttributes = types.ChildWorkflowExecutionTerminatedEventAttributes{ Domain: DomainName, WorkflowExecution: &WorkflowExecution, WorkflowType: &WorkflowType, InitiatedEventID: EventID1, StartedEventID: EventID2, } SignalExternalWorkflowExecutionInitiatedEventAttributes = types.SignalExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventID: EventID1, Domain: DomainName, WorkflowExecution: &WorkflowExecution, SignalName: SignalName, Input: Payload1, Control: Control, ChildWorkflowOnly: true, } SignalExternalWorkflowExecutionFailedEventAttributes = types.SignalExternalWorkflowExecutionFailedEventAttributes{ Cause: &SignalExternalWorkflowExecutionFailedCause, DecisionTaskCompletedEventID: EventID1, Domain: DomainName, WorkflowExecution: &WorkflowExecution, InitiatedEventID: EventID2, Control: Control, } ExternalWorkflowExecutionSignaledEventAttributes = types.ExternalWorkflowExecutionSignaledEventAttributes{ InitiatedEventID: EventID1, Domain: DomainName, WorkflowExecution: &WorkflowExecution, Control: Control, } UpsertWorkflowSearchAttributesEventAttributes = types.UpsertWorkflowSearchAttributesEventAttributes{ DecisionTaskCompletedEventID: EventID1, SearchAttributes: &SearchAttributes, } GetFailoverInfoRequest = types.GetFailoverInfoRequest{ DomainID: uuid.NewUUID().String(), } GetFailoverInfoResponse = types.GetFailoverInfoResponse{ CompletedShardCount: 0, PendingShards: []int32{1, 2, 3}, } RatelimitUpdateRequest = types.RatelimitUpdateRequest{ Any: &types.Any{ ValueType: history.WeightedRatelimitUsageAnyType, // only correct value currently Value: []byte("test request"), // invalid contents, but not inspected in these tests }, } RatelimitUpdateResponse = types.RatelimitUpdateResponse{ Any: &types.Any{ ValueType: history.WeightedRatelimitQuotasAnyType, // only correct value currently Value: []byte("test response"), // invalid contents, but not inspected in these tests }, } ) func generateEvent(modifier func(e *types.HistoryEvent)) types.HistoryEvent { e := types.HistoryEvent{ ID: EventID1, Timestamp: &Timestamp1, Version: Version1, TaskID: TaskID, } modifier(&e) return e } ================================================ FILE: common/types/testdata/queue.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) const ( TaskState = int16(1) ) var ( CrossClusterTaskInfo = *generateCrossClusterTaskInfo(types.CrossClusterTaskTypeStartChildExecution) CrossClusterStartChildExecutionRequestAttributes = types.CrossClusterStartChildExecutionRequestAttributes{ TargetDomainID: DomainID, RequestID: RequestID, InitiatedEventID: EventID1, InitiatedEventAttributes: &StartChildWorkflowExecutionInitiatedEventAttributes, TargetRunID: common.StringPtr(RunID1), PartitionConfig: PartitionConfig, } CrossClusterStartChildExecutionResponseAttributes = types.CrossClusterStartChildExecutionResponseAttributes{ RunID: RunID, } CrossClusterCancelExecutionRequestAttributes = types.CrossClusterCancelExecutionRequestAttributes{ TargetDomainID: DomainID, TargetWorkflowID: WorkflowID, TargetRunID: RunID, RequestID: RequestID, InitiatedEventID: EventID1, ChildWorkflowOnly: true, } CrossClusterCancelExecutionResponseAttributes = types.CrossClusterCancelExecutionResponseAttributes{} CrossClusterSignalExecutionRequestAttributes = types.CrossClusterSignalExecutionRequestAttributes{ TargetDomainID: DomainID, TargetWorkflowID: WorkflowID, TargetRunID: RunID, RequestID: RequestID, InitiatedEventID: EventID1, ChildWorkflowOnly: true, SignalName: SignalName, SignalInput: Payload1, Control: Control, } CrossClusterSignalExecutionResponseAttributes = types.CrossClusterSignalExecutionResponseAttributes{} CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes = types.CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes{ TargetDomainID: DomainID, TargetWorkflowID: WorkflowID, TargetRunID: RunID, InitiatedEventID: EventID1, CompletionEvent: &HistoryEvent_ChildWorkflowExecutionCompleted, } CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes = types.CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes{} CrossClusterApplyParentClosePolicyRequestAttributes = types.CrossClusterApplyParentClosePolicyRequestAttributes{ Children: []*types.ApplyParentClosePolicyRequest{ { Child: &types.ApplyParentClosePolicyAttributes{ ChildDomainID: DomainID, ChildWorkflowID: WorkflowID, ChildRunID: RunID, ParentClosePolicy: &ParentClosePolicy, }, Status: &types.ApplyParentClosePolicyStatus{ Completed: false, FailedCause: types.CrossClusterTaskFailedCauseDomainNotActive.Ptr(), }, }, }, } CrossClusterApplyParentClosePolicyResponseAttributes = types.CrossClusterApplyParentClosePolicyResponseAttributes{} CrossClusterTaskRequestStartChildExecution = types.CrossClusterTaskRequest{ TaskInfo: generateCrossClusterTaskInfo(types.CrossClusterTaskTypeStartChildExecution), StartChildExecutionAttributes: &CrossClusterStartChildExecutionRequestAttributes, } CrossClusterTaskRequestCancelExecution = types.CrossClusterTaskRequest{ TaskInfo: generateCrossClusterTaskInfo(types.CrossClusterTaskTypeCancelExecution), CancelExecutionAttributes: &CrossClusterCancelExecutionRequestAttributes, } CrossClusterTaskRequestSignalExecution = types.CrossClusterTaskRequest{ TaskInfo: generateCrossClusterTaskInfo(types.CrossClusterTaskTypeSignalExecution), SignalExecutionAttributes: &CrossClusterSignalExecutionRequestAttributes, } CrossClusterTaskRequestRecordChildExecutionComplete = types.CrossClusterTaskRequest{ TaskInfo: generateCrossClusterTaskInfo(types.CrossClusterTaskTypeRecordChildWorkflowExeuctionComplete), RecordChildWorkflowExecutionCompleteAttributes: &CrossClusterRecordChildWorkflowExecutionCompleteRequestAttributes, } CrossClusterTaskRequestApplyParentClosePolicy = types.CrossClusterTaskRequest{ TaskInfo: generateCrossClusterTaskInfo(types.CrossClusterTaskTypeApplyParentPolicy), ApplyParentClosePolicyAttributes: &CrossClusterApplyParentClosePolicyRequestAttributes, } CrossClusterTaskResponseStartChildExecution = types.CrossClusterTaskResponse{ TaskID: TaskID, TaskType: types.CrossClusterTaskTypeStartChildExecution.Ptr(), TaskState: 1, StartChildExecutionAttributes: &CrossClusterStartChildExecutionResponseAttributes, } CrossClusterTaskResponseCancelExecution = types.CrossClusterTaskResponse{ TaskID: TaskID, TaskType: types.CrossClusterTaskTypeCancelExecution.Ptr(), TaskState: 3, FailedCause: types.CrossClusterTaskFailedCauseDomainNotActive.Ptr(), CancelExecutionAttributes: &types.CrossClusterCancelExecutionResponseAttributes{}, } CrossClusterTaskResponseSignalExecution = types.CrossClusterTaskResponse{ TaskID: TaskID, TaskType: types.CrossClusterTaskTypeSignalExecution.Ptr(), TaskState: 3, FailedCause: types.CrossClusterTaskFailedCauseWorkflowNotExists.Ptr(), SignalExecutionAttributes: &types.CrossClusterSignalExecutionResponseAttributes{}, } CrossClusterTaskResponseRecordChildExecutionComplete = types.CrossClusterTaskResponse{ TaskID: TaskID, TaskType: types.CrossClusterTaskTypeRecordChildWorkflowExeuctionComplete.Ptr(), TaskState: 1, RecordChildWorkflowExecutionCompleteAttributes: &CrossClusterRecordChildWorkflowExecutionCompleteResponseAttributes, } CrossClusterTaskResponseApplyParentClosePolicy = types.CrossClusterTaskResponse{ TaskID: TaskID, TaskType: types.CrossClusterTaskTypeApplyParentPolicy.Ptr(), TaskState: 1, ApplyParentClosePolicyAttributes: &CrossClusterApplyParentClosePolicyResponseAttributes, } CrossClusterTaskRequestArray = []*types.CrossClusterTaskRequest{ &CrossClusterTaskRequestStartChildExecution, &CrossClusterTaskRequestCancelExecution, &CrossClusterTaskRequestSignalExecution, &CrossClusterTaskRequestRecordChildExecutionComplete, &CrossClusterTaskRequestApplyParentClosePolicy, } CrossClusterTaskResponseArray = []*types.CrossClusterTaskResponse{ &CrossClusterTaskResponseStartChildExecution, &CrossClusterTaskResponseCancelExecution, &CrossClusterTaskResponseSignalExecution, &CrossClusterTaskResponseRecordChildExecutionComplete, &CrossClusterTaskResponseApplyParentClosePolicy, } CrossClusterTaskRequestMap = map[int32][]*types.CrossClusterTaskRequest{ ShardID + 1: {}, ShardID + 2: CrossClusterTaskRequestArray, } GetCrossClusterTaskFailedCauseMap = map[int32]types.GetTaskFailedCause{ ShardID + 3: types.GetTaskFailedCauseServiceBusy, ShardID + 4: types.GetTaskFailedCauseTimeout, ShardID + 5: types.GetTaskFailedCauseShardOwnershipLost, ShardID + 6: types.GetTaskFailedCauseUncategorized, } GetCrossClusterTasksRequest = types.GetCrossClusterTasksRequest{ ShardIDs: []int32{ShardID}, TargetCluster: ClusterName1, } GetCrossClusterTasksResponse = types.GetCrossClusterTasksResponse{ TasksByShard: CrossClusterTaskRequestMap, FailedCauseByShard: GetCrossClusterTaskFailedCauseMap, } RespondCrossClusterTasksCompletedRequest = types.RespondCrossClusterTasksCompletedRequest{ ShardID: ShardID, TargetCluster: ClusterName1, TaskResponses: CrossClusterTaskResponseArray, FetchNewTasks: true, } RespondCrossClusterTasksCompletedResponse = types.RespondCrossClusterTasksCompletedResponse{ Tasks: CrossClusterTaskRequestArray, } ApplyParentClosePolicyAttributes = types.ApplyParentClosePolicyAttributes{ ChildDomainID: "testDomain", ChildWorkflowID: "testChildWorkflowID", ChildRunID: "testChildRunID", ParentClosePolicy: &ParentClosePolicy, } ApplyParentClosePolicyResult = types.ApplyParentClosePolicyResult{ Child: &ApplyParentClosePolicyAttributes, FailedCause: types.CrossClusterTaskFailedCauseDomainNotActive.Ptr(), } ApplyParentClosePolicyResult2 = types.ApplyParentClosePolicyResult{ Child: &types.ApplyParentClosePolicyAttributes{ ChildDomainID: "testDomain2", ChildWorkflowID: "testChildWorkflowID2", ChildRunID: "testChildRunID2", ParentClosePolicy: &ParentClosePolicy2, }, FailedCause: types.CrossClusterTaskFailedCauseWorkflowAlreadyRunning.Ptr(), } CrossClusterApplyParentClosePolicyResponseWithChildren = types.CrossClusterApplyParentClosePolicyResponseAttributes{ ChildrenStatus: []*types.ApplyParentClosePolicyResult{ &ApplyParentClosePolicyResult, &ApplyParentClosePolicyResult2, }, } ) func generateCrossClusterTaskInfo( taskType types.CrossClusterTaskType, ) *types.CrossClusterTaskInfo { return &types.CrossClusterTaskInfo{ DomainID: DomainID, WorkflowID: WorkflowID, RunID: RunID, TaskType: taskType.Ptr(), TaskState: TaskState, VisibilityTimestamp: &Timestamp, } } ================================================ FILE: common/types/testdata/replication.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) const ( TaskType = int16(1) MaxiumuPageSize = int32(5) ) var ( ReplicationMessages = types.ReplicationMessages{ ReplicationTasks: ReplicationTaskArray, LastRetrievedMessageID: MessageID1, HasMore: true, SyncShardStatus: &SyncShardStatus, } ReplicationMessagesMap = map[int32]*types.ReplicationMessages{ ShardID: &ReplicationMessages, } SyncShardStatus = types.SyncShardStatus{ Timestamp: &Timestamp1, } ReplicationToken = types.ReplicationToken{ ShardID: ShardID, LastRetrievedMessageID: MessageID1, LastProcessedMessageID: MessageID2, } ReplicationTokenArray = []*types.ReplicationToken{ &ReplicationToken, } ReplicationTaskInfo = types.ReplicationTaskInfo{ DomainID: DomainID, WorkflowID: WorkflowID, RunID: RunID, TaskType: TaskType, TaskID: TaskID, Version: Version1, FirstEventID: EventID1, NextEventID: EventID2, ScheduledID: EventID3, } ReplicationTaskInfoArray = []*types.ReplicationTaskInfo{ &ReplicationTaskInfo, } ReplicationTask_Domain = types.ReplicationTask{ TaskType: types.ReplicationTaskTypeDomain.Ptr(), SourceTaskID: TaskID, DomainTaskAttributes: &DomainTaskAttributes, CreationTime: &Timestamp1, } ReplicationTask_SyncShard = types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncShardStatus.Ptr(), SourceTaskID: TaskID, SyncShardStatusTaskAttributes: &SyncShardStatusTaskAttributes, CreationTime: &Timestamp1, } ReplicationTask_SyncActivity = types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SourceTaskID: TaskID, SyncActivityTaskAttributes: &SyncActivityTaskAttributes, CreationTime: &Timestamp1, } ReplicationTask_History = types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), SourceTaskID: TaskID, HistoryTaskV2Attributes: &HistoryTaskV2Attributes, CreationTime: &Timestamp1, } ReplicationTask_Failover = types.ReplicationTask{ TaskType: types.ReplicationTaskTypeFailoverMarker.Ptr(), SourceTaskID: TaskID, FailoverMarkerAttributes: &FailoverMarkerAttributes, CreationTime: &Timestamp1, } ReplicationTaskArray = []*types.ReplicationTask{ &ReplicationTask_Domain, &ReplicationTask_SyncShard, &ReplicationTask_SyncActivity, &ReplicationTask_History, &ReplicationTask_Failover, } DomainTaskAttributes = types.DomainTaskAttributes{ DomainOperation: types.DomainOperationUpdate.Ptr(), ID: DomainID, Info: &DomainInfo, Config: &DomainConfiguration, ReplicationConfig: &DomainReplicationConfiguration, ConfigVersion: Version1, FailoverVersion: FailoverVersion1, PreviousFailoverVersion: FailoverVersion2, } SyncShardStatusTaskAttributes = types.SyncShardStatusTaskAttributes{ SourceCluster: ClusterName1, ShardID: ShardID, Timestamp: &Timestamp1, } SyncActivityTaskAttributes = types.SyncActivityTaskAttributes{ DomainID: DomainID, WorkflowID: WorkflowID, RunID: RunID, Version: Version1, ScheduledID: EventID1, ScheduledTime: &Timestamp1, StartedID: EventID2, StartedTime: &Timestamp2, LastHeartbeatTime: &Timestamp3, Details: Payload1, Attempt: Attempt, LastFailureReason: &FailureReason, LastWorkerIdentity: Identity, LastFailureDetails: FailureDetails, VersionHistory: &VersionHistory, } HistoryTaskV2Attributes = types.HistoryTaskV2Attributes{ DomainID: DomainID, WorkflowID: WorkflowID, RunID: RunID, VersionHistoryItems: VersionHistoryItemArray, Events: &DataBlob, NewRunEvents: &DataBlob, } FailoverMarkerAttributes = types.FailoverMarkerAttributes{ DomainID: DomainID, FailoverVersion: FailoverVersion1, CreationTime: &Timestamp1, } FailoverMarkerAttributesArray = []*types.FailoverMarkerAttributes{ &FailoverMarkerAttributes, } PersistenceFeatures = []*types.PersistenceFeature{ { Key: "PersistenceFeature", Enabled: true, }, nil, } PersistenceSettings = []*types.PersistenceSetting{ { Key: "PersistenceKey", Value: "PersistenceValue", }, nil, } PersistenceInfoMap = map[string]*types.PersistenceInfo{ "Backend": { Backend: "Backend", Settings: PersistenceSettings, Features: PersistenceFeatures, }, "Invalid": nil, } GetDomainReplicationMessagesRequest = types.GetDomainReplicationMessagesRequest{ LastRetrievedMessageID: common.Int64Ptr(MessageID1), LastProcessedMessageID: common.Int64Ptr(MessageID2), ClusterName: ClusterName1, } MergeDLQMessagesRequest = types.MergeDLQMessagesRequest{ Type: types.DLQTypeDomain.Ptr(), ShardID: ShardID, SourceCluster: ClusterName1, InclusiveEndMessageID: common.Int64Ptr(MessageID1), MaximumPageSize: MaxiumuPageSize, NextPageToken: Token1, } PurgeDLQMessagesRequest = types.PurgeDLQMessagesRequest{ Type: types.DLQTypeDomain.Ptr(), ShardID: ShardID, SourceCluster: ClusterName1, InclusiveEndMessageID: common.Int64Ptr(MessageID1), } ReadDLQMessagesRequest = types.ReadDLQMessagesRequest{ Type: types.DLQTypeDomain.Ptr(), ShardID: ShardID, SourceCluster: ClusterName1, InclusiveEndMessageID: common.Int64Ptr(MessageID1), MaximumPageSize: MaxiumuPageSize, NextPageToken: Token1, } ReadDLQMessagesResponse = types.ReadDLQMessagesResponse{ Type: types.DLQTypeDomain.Ptr(), ReplicationTasks: ReplicationTaskArray, ReplicationTasksInfo: ReplicationTaskInfoArray, NextPageToken: Token1, } ) ================================================ FILE: common/types/testdata/schedule.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package testdata import ( "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) var ( scheduleTime1 = time.Date(2026, 3, 1, 10, 0, 0, 0, time.UTC) scheduleTime2 = time.Date(2026, 6, 15, 12, 30, 0, 0, time.UTC) scheduleTime3 = time.Date(2026, 9, 1, 8, 0, 0, 0, time.UTC) scheduleTime4 = time.Date(2026, 12, 31, 23, 59, 59, 0, time.UTC) scheduleTime5 = time.Date(2026, 1, 15, 14, 0, 0, 0, time.UTC) ScheduleSpec = types.ScheduleSpec{ CronExpression: "*/5 * * * *", StartTime: scheduleTime1, EndTime: scheduleTime4, Jitter: 30 * time.Second, } ScheduleStartWorkflowAction = types.StartWorkflowAction{ WorkflowType: &WorkflowType, TaskList: &TaskList, Input: Payload1, WorkflowIDPrefix: "sched-wf-", ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), RetryPolicy: &RetryPolicy, Memo: &Memo, SearchAttributes: &SearchAttributes, } ScheduleAction = types.ScheduleAction{ StartWorkflow: &ScheduleStartWorkflowAction, } SchedulePolicies = types.SchedulePolicies{ OverlapPolicy: types.ScheduleOverlapPolicyBuffer, CatchUpPolicy: types.ScheduleCatchUpPolicyAll, CatchUpWindow: time.Hour, PauseOnFailure: true, BufferLimit: 5, ConcurrencyLimit: 2, } SchedulePauseInfo = types.SchedulePauseInfo{ Reason: "maintenance window", PausedAt: scheduleTime2, PausedBy: "sample_admin@uber.com", } ScheduleState = types.ScheduleState{ Paused: true, PauseInfo: &SchedulePauseInfo, } ScheduleBackfillInfo = types.BackfillInfo{ BackfillID: "backfill-001", StartTime: scheduleTime1, EndTime: scheduleTime3, RunsCompleted: 15, RunsTotal: 30, } ScheduleBackfillInfo2 = types.BackfillInfo{ BackfillID: "backfill-002", StartTime: scheduleTime3, EndTime: scheduleTime4, RunsCompleted: 0, RunsTotal: 10, } ScheduleInfo = types.ScheduleInfo{ LastRunTime: scheduleTime2, NextRunTime: scheduleTime3, TotalRuns: 42, CreateTime: scheduleTime5, LastUpdateTime: scheduleTime2, OngoingBackfills: []*types.BackfillInfo{&ScheduleBackfillInfo, &ScheduleBackfillInfo2}, } ScheduleListEntry = types.ScheduleListEntry{ ScheduleID: "my-schedule-id", WorkflowType: &WorkflowType, State: &ScheduleState, CronExpression: "*/5 * * * *", } CreateScheduleRequest = types.CreateScheduleRequest{ Domain: DomainName, ScheduleID: "my-schedule-id", Spec: &ScheduleSpec, Action: &ScheduleAction, Policies: &SchedulePolicies, Memo: &Memo, SearchAttributes: &SearchAttributes, } CreateScheduleResponse = types.CreateScheduleResponse{} DescribeScheduleRequest = types.DescribeScheduleRequest{ Domain: DomainName, ScheduleID: "my-schedule-id", } DescribeScheduleResponse = types.DescribeScheduleResponse{ Spec: &ScheduleSpec, Action: &ScheduleAction, Policies: &SchedulePolicies, State: &ScheduleState, Info: &ScheduleInfo, Memo: &Memo, SearchAttributes: &SearchAttributes, } UpdateScheduleRequest = types.UpdateScheduleRequest{ Domain: DomainName, ScheduleID: "my-schedule-id", Spec: &ScheduleSpec, Action: &ScheduleAction, Policies: &SchedulePolicies, SearchAttributes: &SearchAttributes, } UpdateScheduleResponse = types.UpdateScheduleResponse{} DeleteScheduleRequest = types.DeleteScheduleRequest{ Domain: DomainName, ScheduleID: "my-schedule-id", } DeleteScheduleResponse = types.DeleteScheduleResponse{} PauseScheduleRequest = types.PauseScheduleRequest{ Domain: DomainName, ScheduleID: "my-schedule-id", Reason: "maintenance window", } PauseScheduleResponse = types.PauseScheduleResponse{} UnpauseScheduleRequest = types.UnpauseScheduleRequest{ Domain: DomainName, ScheduleID: "my-schedule-id", Reason: "maintenance complete", CatchUpPolicy: types.ScheduleCatchUpPolicyOne, } UnpauseScheduleResponse = types.UnpauseScheduleResponse{} ListSchedulesRequest = types.ListSchedulesRequest{ Domain: DomainName, PageSize: 10, NextPageToken: []byte("next-page-token"), } ListSchedulesResponse = types.ListSchedulesResponse{ Schedules: []*types.ScheduleListEntry{&ScheduleListEntry}, NextPageToken: []byte("next-page-token-2"), } BackfillScheduleRequest = types.BackfillScheduleRequest{ Domain: DomainName, ScheduleID: "my-schedule-id", StartTime: scheduleTime1, EndTime: scheduleTime3, OverlapPolicy: types.ScheduleOverlapPolicyConcurrent, BackfillID: "backfill-003", } BackfillScheduleResponse = types.BackfillScheduleResponse{} ) ================================================ FILE: common/types/testdata/service_admin.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "fmt" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) const QueueType = 2 var ( AdminAddSearchAttributeRequest = types.AddSearchAttributeRequest{ SearchAttribute: IndexedValueTypeMap, SecurityToken: SecurityToken, } AdminCloseShardRequest = types.CloseShardRequest{ ShardID: ShardID, } AdminDeleteWorkflowRequest = types.AdminDeleteWorkflowRequest{ Domain: DomainID, Execution: &WorkflowExecution, } AdminDeleteWorkflowResponse = types.AdminDeleteWorkflowResponse{ HistoryDeleted: true, ExecutionsDeleted: false, VisibilityDeleted: true, } AdminDescribeClusterResponse = types.DescribeClusterResponse{ SupportedClientVersions: &SupportedClientVersions, MembershipInfo: &MembershipInfo, PersistenceInfo: PersistenceInfoMap, } AdminDescribeHistoryHostRequest_ByHost = types.DescribeHistoryHostRequest{ HostAddress: common.StringPtr(HostName), } AdminDescribeHistoryHostRequest_ByShard = types.DescribeHistoryHostRequest{ ShardIDForHost: common.Int32Ptr(ShardID), } AdminDescribeHistoryHostRequest_ByExecution = types.DescribeHistoryHostRequest{ ExecutionForHost: &WorkflowExecution, } AdminDescribeHistoryHostResponse = types.DescribeHistoryHostResponse{ NumberOfShards: 1, ShardIDs: []int32{ShardID}, DomainCache: &DomainCacheInfo, ShardControllerStatus: "ShardControllerStatus", Address: HostName, } AdminDescribeQueueRequest = types.DescribeQueueRequest{ ShardID: ShardID, ClusterName: ClusterName1, Type: common.Int32Ptr(QueueType), } AdminDescribeQueueResponse = types.DescribeQueueResponse{ ProcessingQueueStates: []string{"state1", "state2"}, } AdminDescribeShardDistributionRequest = types.DescribeShardDistributionRequest{ PageSize: PageSize, PageID: 1, } AdminDescribeShardDistributionResponse = types.DescribeShardDistributionResponse{ NumberOfShards: 2, Shards: map[int32]string{ 0: "shard1", 1: "shard2", }, } AdminDescribeWorkflowExecutionRequest = types.AdminDescribeWorkflowExecutionRequest{ Domain: DomainName, Execution: &WorkflowExecution, } AdminDescribeWorkflowExecutionResponse = types.AdminDescribeWorkflowExecutionResponse{ ShardID: fmt.Sprint(ShardID), HistoryAddr: HostName, MutableStateInCache: "MutableStateInCache", MutableStateInDatabase: "MutableStateInDatabase", } AdminGetDLQReplicationMessagesRequest = types.GetDLQReplicationMessagesRequest{ TaskInfos: ReplicationTaskInfoArray, } AdminGetDLQReplicationMessagesResponse = types.GetDLQReplicationMessagesResponse{ ReplicationTasks: ReplicationTaskArray, } AdminGetDomainIsolationGroupsRequest = types.GetDomainIsolationGroupsRequest{ Domain: DomainName, } AdminGetDomainIsolationGroupsResponse = types.GetDomainIsolationGroupsResponse{ IsolationGroups: IsolationGroupConfiguration, } AdminGetDomainReplicationMessagesRequest = types.GetDomainReplicationMessagesRequest{ LastRetrievedMessageID: common.Int64Ptr(MessageID1), LastProcessedMessageID: common.Int64Ptr(MessageID2), ClusterName: ClusterName1, } AdminGetDomainReplicationMessagesResponse = types.GetDomainReplicationMessagesResponse{ Messages: &ReplicationMessages, } AdminGetDynamicConfigRequest = types.GetDynamicConfigRequest{ ConfigName: DynamicConfigEntryName, Filters: []*types.DynamicConfigFilter{&DynamicConfigFilter}, } AdminGetDynamicConfigResponse = types.GetDynamicConfigResponse{Value: &DataBlob} AdminGetGlobalIsolationGroupsRequest = types.GetGlobalIsolationGroupsRequest{} AdminGetReplicationMessagesRequest = types.GetReplicationMessagesRequest{ Tokens: ReplicationTokenArray, ClusterName: ClusterName1, } AdminGetReplicationMessagesResponse = types.GetReplicationMessagesResponse{ MessagesByShard: ReplicationMessagesMap, } AdminGetWorkflowExecutionRawHistoryV2Request = types.GetWorkflowExecutionRawHistoryV2Request{ Domain: DomainName, Execution: &WorkflowExecution, StartEventID: common.Int64Ptr(EventID1), StartEventVersion: common.Int64Ptr(Version1), EndEventID: common.Int64Ptr(EventID2), EndEventVersion: common.Int64Ptr(EventID2), MaximumPageSize: PageSize, NextPageToken: NextPageToken, } AdminGetWorkflowExecutionRawHistoryV2Response = types.GetWorkflowExecutionRawHistoryV2Response{ NextPageToken: NextPageToken, HistoryBatches: DataBlobArray, VersionHistory: &VersionHistory, } AdminCountDLQMessagesRequest = types.CountDLQMessagesRequest{ForceFetch: true} AdminCountDLQMessagesResponse = types.CountDLQMessagesResponse{ History: HistoryCountDLQMessagesResponse.Entries, Domain: 123456, } AdminListDynamicConfigRequest = types.ListDynamicConfigRequest{ ConfigName: DynamicConfigEntryName, } AdminListDynamicConfigResponse = types.ListDynamicConfigResponse{ Entries: []*types.DynamicConfigEntry{ { Name: DynamicConfigEntryName, Values: []*types.DynamicConfigValue{ &DynamicConfigValue, }, }, nil, }, } AdminMaintainCorruptWorkflowRequest = types.AdminMaintainWorkflowRequest{ Domain: DomainName, Execution: &WorkflowExecution, } AdminMaintainCorruptWorkflowResponse = types.AdminMaintainWorkflowResponse{ HistoryDeleted: false, ExecutionsDeleted: true, VisibilityDeleted: false, } AdminMergeDLQMessagesRequest = types.MergeDLQMessagesRequest{ Type: types.DLQTypeDomain.Ptr(), ShardID: ShardID, SourceCluster: ClusterName1, InclusiveEndMessageID: common.Int64Ptr(MessageID1), MaximumPageSize: PageSize, NextPageToken: NextPageToken, } AdminMergeDLQMessagesResponse = types.MergeDLQMessagesResponse{ NextPageToken: NextPageToken, } AdminPurgeDLQMessagesRequest = types.PurgeDLQMessagesRequest{ Type: types.DLQTypeDomain.Ptr(), ShardID: ShardID, SourceCluster: ClusterName1, InclusiveEndMessageID: common.Int64Ptr(MessageID1), } AdminReadDLQMessagesRequest = types.ReadDLQMessagesRequest{ Type: types.DLQTypeDomain.Ptr(), ShardID: ShardID, SourceCluster: ClusterName1, InclusiveEndMessageID: common.Int64Ptr(MessageID1), MaximumPageSize: PageSize, NextPageToken: NextPageToken, } AdminReadDLQMessagesResponse = types.ReadDLQMessagesResponse{ Type: types.DLQTypeDomain.Ptr(), ReplicationTasks: ReplicationTaskArray, ReplicationTasksInfo: ReplicationTaskInfoArray, NextPageToken: NextPageToken, } AdminReapplyEventsRequest = types.ReapplyEventsRequest{ DomainName: DomainName, WorkflowExecution: &WorkflowExecution, Events: &DataBlob, } AdminRefreshWorkflowTasksRequest = types.RefreshWorkflowTasksRequest{ Domain: DomainName, Execution: &WorkflowExecution, } AdminRemoveTaskRequest = types.RemoveTaskRequest{ ShardID: ShardID, Type: common.Int32Ptr(QueueType), TaskID: TaskID, VisibilityTimestamp: &Timestamp1, ClusterName: ClusterName1, } AdminResendReplicationTasksRequest = types.ResendReplicationTasksRequest{ DomainID: DomainID, WorkflowID: WorkflowID, RunID: RunID, RemoteCluster: ClusterName1, StartEventID: common.Int64Ptr(EventID1), StartVersion: common.Int64Ptr(Version1), EndEventID: common.Int64Ptr(EventID2), EndVersion: common.Int64Ptr(EventID2), } AdminResetQueueRequest = types.ResetQueueRequest{ ShardID: ShardID, ClusterName: ClusterName1, Type: common.Int32Ptr(QueueType), } AdminGetCrossClusterTasksRequest = GetCrossClusterTasksRequest AdminGetCrossClusterTasksResponse = GetCrossClusterTasksResponse AdminRespondCrossClusterTasksCompletedRequest = RespondCrossClusterTasksCompletedRequest AdminRespondCrossClusterTasksCompletedResponse = RespondCrossClusterTasksCompletedResponse AdminRestoreDynamicConfigRequest = types.RestoreDynamicConfigRequest{ ConfigName: DynamicConfigEntryName, Filters: []*types.DynamicConfigFilter{ &DynamicConfigFilter, }, } AdminUpdateDomainIsolationGroupsRequest = types.UpdateDomainIsolationGroupsRequest{ Domain: DomainName, IsolationGroups: IsolationGroupConfiguration, } AdminUpdateDomainIsolationGroupsResponse = types.UpdateDomainIsolationGroupsResponse{} AdminUpdateDynamicConfigRequest = types.UpdateDynamicConfigRequest{ ConfigName: DynamicConfigEntryName, ConfigValues: []*types.DynamicConfigValue{ { Filters: []*types.DynamicConfigFilter{ &DynamicConfigFilter, nil, }, Value: &DataBlob, }, nil, }, } AdminUpdateGlobalIsolationGroupsResponse = types.UpdateGlobalIsolationGroupsResponse{} AdminUpdateTaskListPartitionConfigRequest = types.UpdateTaskListPartitionConfigRequest{ Domain: DomainName, TaskList: &TaskList, TaskListType: &TaskListType, PartitionConfig: &TaskListPartitionConfig, } AdminUpdateTaskListPartitionConfigResponse = types.UpdateTaskListPartitionConfigResponse{} ) ================================================ FILE: common/types/testdata/service_frontend.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) var ( RegisterDomainRequest = types.RegisterDomainRequest{ Name: DomainName, Description: DomainDescription, OwnerEmail: DomainOwnerEmail, WorkflowExecutionRetentionPeriodInDays: DomainRetention, EmitMetric: common.BoolPtr(DomainEmitMetric), Clusters: ClusterReplicationConfigurationArray, ActiveClusterName: ClusterName1, ActiveClusters: &ActiveClusters, Data: DomainData, SecurityToken: SecurityToken, IsGlobalDomain: true, HistoryArchivalStatus: &ArchivalStatus, HistoryArchivalURI: HistoryArchivalURI, VisibilityArchivalStatus: &ArchivalStatus, VisibilityArchivalURI: VisibilityArchivalURI, } DescribeDomainRequest_ID = types.DescribeDomainRequest{ UUID: common.StringPtr(DomainID), } DescribeDomainRequest_Name = types.DescribeDomainRequest{ Name: common.StringPtr(DomainName), } DescribeDomainResponse = types.DescribeDomainResponse{ DomainInfo: &DomainInfo, Configuration: &DomainConfiguration, ReplicationConfiguration: &DomainReplicationConfiguration, FailoverVersion: FailoverVersion1, IsGlobalDomain: true, } ListDomainsRequest = types.ListDomainsRequest{ PageSize: PageSize, NextPageToken: NextPageToken, } ListDomainsResponse = types.ListDomainsResponse{ Domains: []*types.DescribeDomainResponse{&DescribeDomainResponse}, NextPageToken: NextPageToken, } UpdateDomainRequest = types.UpdateDomainRequest{ Name: DomainName, Description: common.StringPtr(DomainDescription), OwnerEmail: common.StringPtr(DomainOwnerEmail), Data: DomainData, WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(DomainRetention), BadBinaries: &BadBinaries, HistoryArchivalStatus: &ArchivalStatus, HistoryArchivalURI: common.StringPtr(HistoryArchivalURI), VisibilityArchivalStatus: &ArchivalStatus, VisibilityArchivalURI: common.StringPtr(VisibilityArchivalURI), ActiveClusterName: common.StringPtr(ClusterName1), ActiveClusters: &ActiveClusters, Clusters: ClusterReplicationConfigurationArray, SecurityToken: SecurityToken, DeleteBadBinary: common.StringPtr(DeleteBadBinary), FailoverTimeoutInSeconds: &Duration1, } FailoverDomainRequest = types.FailoverDomainRequest{ DomainName: DomainName, DomainActiveClusterName: common.StringPtr(ClusterName1), ActiveClusters: &ActiveClusters, } FailoverDomainRequest_OnlyActiveClusters = types.FailoverDomainRequest{ DomainName: DomainName, // Explicitly set to nil to test ActiveActive failovers DomainActiveClusterName: nil, ActiveClusters: &ActiveClusters, } ActiveClusters = types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ Region1: { ActiveClusterName: ClusterName1, FailoverVersion: FailoverVersion1, }, Region2: { ActiveClusterName: ClusterName2, FailoverVersion: FailoverVersion2, }, }, }, }, } UpdateDomainResponse = types.UpdateDomainResponse{ DomainInfo: &DomainInfo, Configuration: &DomainConfiguration, ReplicationConfiguration: &DomainReplicationConfiguration, FailoverVersion: FailoverVersion1, IsGlobalDomain: true, } DeleteDomainRequest = types.DeleteDomainRequest{ Name: DomainName, SecurityToken: SecurityToken, } DeprecateDomainRequest = types.DeprecateDomainRequest{ Name: DomainName, SecurityToken: SecurityToken, } ListWorkflowExecutionsRequest = types.ListWorkflowExecutionsRequest{ Domain: DomainName, PageSize: PageSize, NextPageToken: NextPageToken, Query: VisibilityQuery, } ListWorkflowExecutionsResponse = types.ListWorkflowExecutionsResponse{ Executions: WorkflowExecutionInfoArray, NextPageToken: NextPageToken, } ListOpenWorkflowExecutionsRequest_ExecutionFilter = types.ListOpenWorkflowExecutionsRequest{ Domain: DomainName, MaximumPageSize: PageSize, NextPageToken: NextPageToken, StartTimeFilter: &StartTimeFilter, ExecutionFilter: &WorkflowExecutionFilter, } ListOpenWorkflowExecutionsRequest_TypeFilter = types.ListOpenWorkflowExecutionsRequest{ Domain: DomainName, MaximumPageSize: PageSize, NextPageToken: NextPageToken, StartTimeFilter: &StartTimeFilter, TypeFilter: &WorkflowTypeFilter, } ListOpenWorkflowExecutionsResponse = types.ListOpenWorkflowExecutionsResponse{ Executions: WorkflowExecutionInfoArray, NextPageToken: NextPageToken, } ListClosedWorkflowExecutionsRequest_ExecutionFilter = types.ListClosedWorkflowExecutionsRequest{ Domain: DomainName, MaximumPageSize: PageSize, NextPageToken: NextPageToken, StartTimeFilter: &StartTimeFilter, ExecutionFilter: &WorkflowExecutionFilter, } ListClosedWorkflowExecutionsRequest_TypeFilter = types.ListClosedWorkflowExecutionsRequest{ Domain: DomainName, MaximumPageSize: PageSize, NextPageToken: NextPageToken, StartTimeFilter: &StartTimeFilter, TypeFilter: &WorkflowTypeFilter, } ListClosedWorkflowExecutionsRequest_StatusFilter = types.ListClosedWorkflowExecutionsRequest{ Domain: DomainName, MaximumPageSize: PageSize, NextPageToken: NextPageToken, StartTimeFilter: &StartTimeFilter, StatusFilter: &WorkflowExecutionCloseStatus, } ListClosedWorkflowExecutionsResponse = types.ListClosedWorkflowExecutionsResponse{ Executions: WorkflowExecutionInfoArray, NextPageToken: NextPageToken, } ListArchivedWorkflowExecutionsRequest = types.ListArchivedWorkflowExecutionsRequest{ Domain: DomainName, PageSize: PageSize, NextPageToken: NextPageToken, Query: VisibilityQuery, } ListArchivedWorkflowExecutionsResponse = types.ListArchivedWorkflowExecutionsResponse{ Executions: WorkflowExecutionInfoArray, NextPageToken: NextPageToken, } CountWorkflowExecutionsRequest = types.CountWorkflowExecutionsRequest{ Domain: DomainName, Query: VisibilityQuery, } CountWorkflowExecutionsResponse = types.CountWorkflowExecutionsResponse{ Count: int64(8), } GetSearchAttributesResponse = types.GetSearchAttributesResponse{ Keys: IndexedValueTypeMap, } PollForDecisionTaskRequest = types.PollForDecisionTaskRequest{ Domain: DomainName, TaskList: &TaskList, Identity: Identity, BinaryChecksum: Checksum, } PollForDecisionTaskResponse = types.PollForDecisionTaskResponse{ TaskToken: TaskToken, WorkflowExecution: &WorkflowExecution, WorkflowType: &WorkflowType, PreviousStartedEventID: common.Int64Ptr(EventID1), StartedEventID: EventID2, Attempt: Attempt, BacklogCountHint: BacklogCountHint, History: &History, NextPageToken: NextPageToken, Query: &WorkflowQuery, WorkflowExecutionTaskList: &TaskList, ScheduledTimestamp: &Timestamp1, StartedTimestamp: &Timestamp2, Queries: WorkflowQueryMap, NextEventID: EventID3, AutoConfigHint: &AutoConfigHint, } RespondDecisionTaskCompletedRequest = types.RespondDecisionTaskCompletedRequest{ TaskToken: TaskToken, Decisions: DecisionArray, ExecutionContext: ExecutionContext, Identity: Identity, StickyAttributes: &StickyExecutionAttributes, ReturnNewDecisionTask: true, ForceCreateNewDecisionTask: true, BinaryChecksum: Checksum, QueryResults: WorkflowQueryResultMap, } RespondDecisionTaskCompletedResponse = types.RespondDecisionTaskCompletedResponse{ DecisionTask: &PollForDecisionTaskResponse, ActivitiesToDispatchLocally: ActivityLocalDispatchInfoMap, } RespondDecisionTaskFailedRequest = types.RespondDecisionTaskFailedRequest{ TaskToken: TaskToken, Cause: &DecisionTaskFailedCause, Details: Payload1, Identity: Identity, BinaryChecksum: Checksum, } PollForActivityTaskRequest = types.PollForActivityTaskRequest{ Domain: DomainName, TaskList: &TaskList, Identity: Identity, TaskListMetadata: &TaskListMetadata, } PollForActivityTaskResponse = types.PollForActivityTaskResponse{ TaskToken: TaskToken, WorkflowExecution: &WorkflowExecution, ActivityID: ActivityID, ActivityType: &ActivityType, Input: Payload1, ScheduledTimestamp: &Timestamp1, ScheduleToCloseTimeoutSeconds: &Duration1, StartedTimestamp: &Timestamp2, StartToCloseTimeoutSeconds: &Duration2, HeartbeatTimeoutSeconds: &Duration3, Attempt: Attempt, ScheduledTimestampOfThisAttempt: &Timestamp3, HeartbeatDetails: Payload2, WorkflowType: &WorkflowType, WorkflowDomain: DomainName, Header: &Header, AutoConfigHint: &AutoConfigHint, } RespondActivityTaskCompletedRequest = types.RespondActivityTaskCompletedRequest{ TaskToken: TaskToken, Result: Payload1, Identity: Identity, } RespondActivityTaskCompletedByIDRequest = types.RespondActivityTaskCompletedByIDRequest{ Domain: DomainName, WorkflowID: WorkflowID, RunID: RunID, ActivityID: ActivityID, Result: Payload1, Identity: Identity, } RespondActivityTaskFailedRequest = types.RespondActivityTaskFailedRequest{ TaskToken: TaskToken, Reason: &FailureReason, Details: FailureDetails, Identity: Identity, } RespondActivityTaskFailedByIDRequest = types.RespondActivityTaskFailedByIDRequest{ Domain: DomainName, WorkflowID: WorkflowID, RunID: RunID, ActivityID: ActivityID, Reason: &FailureReason, Details: FailureDetails, Identity: Identity, } RespondActivityTaskCanceledRequest = types.RespondActivityTaskCanceledRequest{ TaskToken: TaskToken, Details: Payload1, Identity: Identity, } RespondActivityTaskCanceledByIDRequest = types.RespondActivityTaskCanceledByIDRequest{ Domain: DomainName, WorkflowID: WorkflowID, RunID: RunID, ActivityID: ActivityID, Details: Payload1, Identity: Identity, } RecordActivityTaskHeartbeatRequest = types.RecordActivityTaskHeartbeatRequest{ TaskToken: TaskToken, Details: Payload1, Identity: Identity, } RecordActivityTaskHeartbeatResponse = types.RecordActivityTaskHeartbeatResponse{ CancelRequested: true, } RecordActivityTaskHeartbeatByIDRequest = types.RecordActivityTaskHeartbeatByIDRequest{ Domain: DomainName, WorkflowID: WorkflowID, RunID: RunID, ActivityID: ActivityID, Details: Payload1, Identity: Identity, } RespondQueryTaskCompletedRequest = types.RespondQueryTaskCompletedRequest{ TaskToken: TaskToken, CompletedType: &QueryTaskCompletedType, QueryResult: Payload1, ErrorMessage: ErrorMessage, WorkerVersionInfo: &WorkerVersionInfo, } RequestCancelWorkflowExecutionRequest = types.RequestCancelWorkflowExecutionRequest{ Domain: DomainName, WorkflowExecution: &WorkflowExecution, Identity: Identity, RequestID: RequestID, FirstExecutionRunID: RunID, } StartWorkflowExecutionRequest = types.StartWorkflowExecutionRequest{ Domain: DomainName, WorkflowID: WorkflowID, WorkflowType: &WorkflowType, TaskList: &TaskList, Input: Payload1, ExecutionStartToCloseTimeoutSeconds: &Duration1, TaskStartToCloseTimeoutSeconds: &Duration2, Identity: Identity, RequestID: RequestID, WorkflowIDReusePolicy: &WorkflowIDReusePolicy, RetryPolicy: &RetryPolicy, CronSchedule: CronSchedule, CronOverlapPolicy: &CronOverlapPolicy, Memo: &Memo, SearchAttributes: &SearchAttributes, Header: &Header, FirstRunAtTimeStamp: &Timestamp1, ActiveClusterSelectionPolicy: &ActiveClusterSelectionPolicyExternalEntity, } StartWorkflowExecutionResponse = types.StartWorkflowExecutionResponse{ RunID: RunID, } StartWorkflowExecutionAsyncRequest = types.StartWorkflowExecutionAsyncRequest{ StartWorkflowExecutionRequest: &StartWorkflowExecutionRequest, } StartWorkflowExecutionAsyncResponse = types.StartWorkflowExecutionAsyncResponse{} SignalWorkflowExecutionRequest = types.SignalWorkflowExecutionRequest{ Domain: DomainName, WorkflowExecution: &WorkflowExecution, SignalName: SignalName, Input: Payload1, Identity: Identity, RequestID: RequestID, Control: Control, } SignalWithStartWorkflowExecutionRequest = types.SignalWithStartWorkflowExecutionRequest{ Domain: DomainName, WorkflowID: WorkflowID, WorkflowType: &WorkflowType, TaskList: &TaskList, Input: Payload1, ExecutionStartToCloseTimeoutSeconds: &Duration1, TaskStartToCloseTimeoutSeconds: &Duration2, Identity: Identity, RequestID: RequestID, WorkflowIDReusePolicy: &WorkflowIDReusePolicy, SignalName: SignalName, SignalInput: Payload2, Control: Control, RetryPolicy: &RetryPolicy, CronSchedule: CronSchedule, CronOverlapPolicy: &CronOverlapPolicy, Memo: &Memo, SearchAttributes: &SearchAttributes, Header: &Header, FirstRunAtTimestamp: &Timestamp1, ActiveClusterSelectionPolicy: &ActiveClusterSelectionPolicyRegionSticky, } SignalWithStartWorkflowExecutionAsyncRequest = types.SignalWithStartWorkflowExecutionAsyncRequest{ SignalWithStartWorkflowExecutionRequest: &SignalWithStartWorkflowExecutionRequest, } SignalWithStartWorkflowExecutionAsyncResponse = types.SignalWithStartWorkflowExecutionAsyncResponse{} ResetWorkflowExecutionRequest = types.ResetWorkflowExecutionRequest{ Domain: DomainName, WorkflowExecution: &WorkflowExecution, Reason: Reason, DecisionFinishEventID: EventID1, RequestID: RequestID, SkipSignalReapply: true, } ResetWorkflowExecutionResponse = types.ResetWorkflowExecutionResponse{ RunID: RunID, } TerminateWorkflowExecutionRequest = types.TerminateWorkflowExecutionRequest{ Domain: DomainName, WorkflowExecution: &WorkflowExecution, Reason: Reason, Details: Payload1, Identity: Identity, FirstExecutionRunID: RunID, } DescribeWorkflowExecutionRequest = types.DescribeWorkflowExecutionRequest{ Domain: DomainName, Execution: &WorkflowExecution, QueryConsistencyLevel: &QueryConsistencyLevel, } DescribeWorkflowExecutionResponse = types.DescribeWorkflowExecutionResponse{ ExecutionConfiguration: &WorkflowExecutionConfiguration, WorkflowExecutionInfo: &WorkflowExecutionInfo, PendingActivities: PendingActivityInfoArray, PendingChildren: PendingChildExecutionInfoArray, PendingDecision: &PendingDecisionInfo, } DiagnoseWorkflowExecutionRequest = types.DiagnoseWorkflowExecutionRequest{ Domain: DomainName, WorkflowExecution: &WorkflowExecution, Identity: Identity, } DiagnoseWorkflowExecutionResponse = types.DiagnoseWorkflowExecutionResponse{ Domain: DomainName, DiagnosticWorkflowExecution: &WorkflowExecution, } QueryWorkflowRequest = types.QueryWorkflowRequest{ Domain: DomainName, Execution: &WorkflowExecution, Query: &WorkflowQuery, QueryRejectCondition: &QueryRejectCondition, QueryConsistencyLevel: &QueryConsistencyLevel, } QueryWorkflowResponse = types.QueryWorkflowResponse{ QueryResult: Payload1, QueryRejected: &QueryRejected, } DescribeTaskListRequest = types.DescribeTaskListRequest{ Domain: DomainName, TaskList: &TaskList, TaskListType: &TaskListType, IncludeTaskListStatus: true, } DescribeTaskListResponse = types.DescribeTaskListResponse{ Pollers: PollerInfoArray, TaskListStatus: &TaskListStatus, PartitionConfig: &TaskListPartitionConfig, TaskList: &TaskList, } ListTaskListPartitionsRequest = types.ListTaskListPartitionsRequest{ Domain: DomainName, TaskList: &TaskList, } ListTaskListPartitionsResponse = types.ListTaskListPartitionsResponse{ ActivityTaskListPartitions: TaskListPartitionMetadataArray, DecisionTaskListPartitions: TaskListPartitionMetadataArray, } ResetStickyTaskListRequest = types.ResetStickyTaskListRequest{ Domain: DomainName, Execution: &WorkflowExecution, } ResetStickyTaskListResponse = types.ResetStickyTaskListResponse{} GetWorkflowExecutionHistoryRequest = types.GetWorkflowExecutionHistoryRequest{ Domain: DomainName, Execution: &WorkflowExecution, MaximumPageSize: PageSize, NextPageToken: NextPageToken, WaitForNewEvent: true, HistoryEventFilterType: &HistoryEventFilterType, SkipArchival: true, QueryConsistencyLevel: &QueryConsistencyLevel, } GetWorkflowExecutionHistoryResponse = types.GetWorkflowExecutionHistoryResponse{ History: &History, RawHistory: DataBlobArray, NextPageToken: NextPageToken, Archived: true, } DescribeDomainResponseArray = []*types.DescribeDomainResponse{ &DescribeDomainResponse, } ) ================================================ FILE: common/types/testdata/service_history.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) var ( HistoryCloseShardRequest = AdminCloseShardRequest HistoryDescribeHistoryHostRequest = types.DescribeHistoryHostRequest{} HistoryDescribeHistoryHostResponse = AdminDescribeHistoryHostResponse HistoryDescribeMutableStateRequest = types.DescribeMutableStateRequest{ DomainUUID: DomainID, Execution: &WorkflowExecution, } HistoryDescribeMutableStateResponse = types.DescribeMutableStateResponse{ MutableStateInCache: "MutableStateInCache", MutableStateInDatabase: "MutableStateInDatabase", } HistoryDescribeQueueRequest = AdminDescribeQueueRequest HistoryDescribeQueueResponse = AdminDescribeQueueResponse HistoryDescribeWorkflowExecutionRequest = types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: DomainID, Request: &DescribeWorkflowExecutionRequest, } HistoryDescribeWorkflowExecutionResponse = DescribeWorkflowExecutionResponse HistoryGetDLQReplicationMessagesRequest = AdminGetDLQReplicationMessagesRequest HistoryGetDLQReplicationMessagesResponse = AdminGetDLQReplicationMessagesResponse HistoryGetMutableStateRequest = types.GetMutableStateRequest{ DomainUUID: DomainID, Execution: &WorkflowExecution, ExpectedNextEventID: EventID1, CurrentBranchToken: BranchToken, VersionHistoryItem: &VersionHistoryItem, } HistoryGetMutableStateResponse = types.GetMutableStateResponse{ Execution: &WorkflowExecution, WorkflowType: &WorkflowType, NextEventID: EventID1, PreviousStartedEventID: common.Int64Ptr(EventID2), LastFirstEventID: EventID3, TaskList: &TaskList, StickyTaskList: &TaskList, ClientLibraryVersion: ClientLibraryVersion, ClientFeatureVersion: FeatureVersion, ClientImpl: ClientImpl, IsWorkflowRunning: true, StickyTaskListScheduleToStartTimeout: &Duration1, EventStoreVersion: EventStoreVersion, CurrentBranchToken: BranchToken, WorkflowState: common.Int32Ptr(1), // persistence.WorkflowStateRunning WorkflowCloseState: common.Int32Ptr(6), // persistence.WorkflowCloseStatusTimedOut VersionHistories: &VersionHistories, IsStickyTaskListEnabled: true, HistorySize: HistorySizeInBytes, } HistoryGetReplicationMessagesRequest = AdminGetReplicationMessagesRequest HistoryGetReplicationMessagesResponse = AdminGetReplicationMessagesResponse HistoryCountDLQMessagesRequest = types.CountDLQMessagesRequest{ForceFetch: true} HistoryCountDLQMessagesResponse = types.HistoryCountDLQMessagesResponse{Entries: map[types.HistoryDLQCountKey]int64{types.HistoryDLQCountKey{1, "A"}: 10}} HistoryMergeDLQMessagesRequest = AdminMergeDLQMessagesRequest HistoryMergeDLQMessagesResponse = AdminMergeDLQMessagesResponse HistoryNotifyFailoverMarkersRequest = types.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: FailoverMarkerTokenArray, } HistoryPollMutableStateRequest = types.PollMutableStateRequest{ DomainUUID: DomainID, Execution: &WorkflowExecution, ExpectedNextEventID: EventID1, CurrentBranchToken: BranchToken, } HistoryPollMutableStateResponse = types.PollMutableStateResponse{ Execution: &WorkflowExecution, WorkflowType: &WorkflowType, NextEventID: EventID1, PreviousStartedEventID: common.Int64Ptr(EventID2), LastFirstEventID: EventID3, TaskList: &TaskList, StickyTaskList: &TaskList, ClientLibraryVersion: ClientLibraryVersion, ClientFeatureVersion: FeatureVersion, ClientImpl: ClientImpl, StickyTaskListScheduleToStartTimeout: &Duration1, CurrentBranchToken: BranchToken, VersionHistories: &VersionHistories, WorkflowState: common.Int32Ptr(5), // persistence.WorkflowStateCorrupted WorkflowCloseState: common.Int32Ptr(6), // persistence.WorkflowCloseStatusTimedOut } HistoryPurgeDLQMessagesRequest = AdminPurgeDLQMessagesRequest HistoryQueryWorkflowRequest = types.HistoryQueryWorkflowRequest{ DomainUUID: DomainID, Request: &QueryWorkflowRequest, } HistoryQueryWorkflowResponse = types.HistoryQueryWorkflowResponse{ Response: &QueryWorkflowResponse, } HistoryReadDLQMessagesRequest = AdminReadDLQMessagesRequest HistoryReadDLQMessagesResponse = AdminReadDLQMessagesResponse HistoryReapplyEventsRequest = types.HistoryReapplyEventsRequest{ DomainUUID: DomainID, Request: &AdminReapplyEventsRequest, } HistoryRecordActivityTaskHeartbeatRequest = types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: DomainID, HeartbeatRequest: &RecordActivityTaskHeartbeatRequest, } HistoryRecordActivityTaskHeartbeatResponse = RecordActivityTaskHeartbeatResponse HistoryRecordActivityTaskStartedRequest = types.RecordActivityTaskStartedRequest{ DomainUUID: DomainID, WorkflowExecution: &WorkflowExecution, ScheduleID: EventID1, TaskID: TaskID, RequestID: RequestID, PollRequest: &PollForActivityTaskRequest, } HistoryRecordActivityTaskStartedResponse = types.RecordActivityTaskStartedResponse{ ScheduledEvent: &HistoryEvent_WorkflowExecutionStarted, StartedTimestamp: &Timestamp1, Attempt: Attempt, ScheduledTimestampOfThisAttempt: &Timestamp2, HeartbeatDetails: Payload1, WorkflowType: &WorkflowType, WorkflowDomain: DomainName, } HistoryRecordChildExecutionCompletedRequest = types.RecordChildExecutionCompletedRequest{ DomainUUID: DomainID, WorkflowExecution: &WorkflowExecution, InitiatedID: EventID1, CompletedExecution: &WorkflowExecution, CompletionEvent: &HistoryEvent_WorkflowExecutionStarted, StartedID: EventID2, } HistoryRecordDecisionTaskStartedRequest = types.RecordDecisionTaskStartedRequest{ DomainUUID: DomainID, WorkflowExecution: &WorkflowExecution, ScheduleID: EventID1, TaskID: TaskID, RequestID: RequestID, PollRequest: &PollForDecisionTaskRequest, } HistoryRecordDecisionTaskStartedResponse = types.RecordDecisionTaskStartedResponse{ WorkflowType: &WorkflowType, PreviousStartedEventID: common.Int64Ptr(EventID1), ScheduledEventID: EventID2, StartedEventID: EventID3, NextEventID: EventID4, Attempt: Attempt, StickyExecutionEnabled: true, DecisionInfo: &TransientDecisionInfo, WorkflowExecutionTaskList: &TaskList, EventStoreVersion: EventStoreVersion, BranchToken: BranchToken, ScheduledTimestamp: &Timestamp1, StartedTimestamp: &Timestamp2, Queries: WorkflowQueryMap, HistorySize: HistorySizeInBytes, } HistoryRefreshWorkflowTasksRequest = types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: DomainID, Request: &AdminRefreshWorkflowTasksRequest, } HistoryRemoveSignalMutableStateRequest = types.RemoveSignalMutableStateRequest{ DomainUUID: DomainID, WorkflowExecution: &WorkflowExecution, RequestID: RequestID, } HistoryRemoveTaskRequest = AdminRemoveTaskRequest HistoryReplicateEventsV2Request = types.ReplicateEventsV2Request{ DomainUUID: DomainID, WorkflowExecution: &WorkflowExecution, VersionHistoryItems: VersionHistoryItemArray, Events: &DataBlob, NewRunEvents: &DataBlob, } HistoryRequestCancelWorkflowExecutionRequest = types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: DomainID, CancelRequest: &RequestCancelWorkflowExecutionRequest, ExternalInitiatedEventID: common.Int64Ptr(EventID1), ExternalWorkflowExecution: &WorkflowExecution, ChildWorkflowOnly: true, } HistoryResetQueueRequest = AdminResetQueueRequest HistoryResetStickyTaskListRequest = types.HistoryResetStickyTaskListRequest{ DomainUUID: DomainID, Execution: &WorkflowExecution, } HistoryResetWorkflowExecutionRequest = types.HistoryResetWorkflowExecutionRequest{ DomainUUID: DomainID, ResetRequest: &ResetWorkflowExecutionRequest, } HistoryResetWorkflowExecutionResponse = types.ResetWorkflowExecutionResponse{ RunID: RunID, } HistoryRespondActivityTaskCanceledRequest = types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: DomainID, CancelRequest: &RespondActivityTaskCanceledRequest, } HistoryRespondActivityTaskCompletedRequest = types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: DomainID, CompleteRequest: &RespondActivityTaskCompletedRequest, } HistoryRespondActivityTaskFailedRequest = types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: DomainID, FailedRequest: &RespondActivityTaskFailedRequest, } HistoryRespondDecisionTaskCompletedRequest = types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: DomainID, CompleteRequest: &RespondDecisionTaskCompletedRequest, } HistoryRespondDecisionTaskCompletedResponse = types.HistoryRespondDecisionTaskCompletedResponse{ StartedResponse: &HistoryRecordDecisionTaskStartedResponse, ActivitiesToDispatchLocally: ActivityLocalDispatchInfoMap, } HistoryRespondDecisionTaskFailedRequest = types.HistoryRespondDecisionTaskFailedRequest{ DomainUUID: DomainID, FailedRequest: &RespondDecisionTaskFailedRequest, } HistoryScheduleDecisionTaskRequest = types.ScheduleDecisionTaskRequest{ DomainUUID: DomainID, WorkflowExecution: &WorkflowExecution, IsFirstDecision: true, } HistorySignalWithStartWorkflowExecutionRequest = types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: DomainID, SignalWithStartRequest: &SignalWithStartWorkflowExecutionRequest, PartitionConfig: PartitionConfig, } HistorySignalWithStartWorkflowExecutionResponse = types.StartWorkflowExecutionResponse{ RunID: RunID, } HistorySignalWorkflowExecutionRequest = types.HistorySignalWorkflowExecutionRequest{ DomainUUID: DomainID, SignalRequest: &SignalWorkflowExecutionRequest, ExternalWorkflowExecution: &WorkflowExecution, ChildWorkflowOnly: true, } HistoryStartWorkflowExecutionRequest = types.HistoryStartWorkflowExecutionRequest{ DomainUUID: DomainID, StartRequest: &StartWorkflowExecutionRequest, ParentExecutionInfo: &ParentExecutionInfo, Attempt: Attempt, ExpirationTimestamp: &Timestamp1, ContinueAsNewInitiator: &ContinueAsNewInitiator, ContinuedFailureReason: &FailureReason, ContinuedFailureDetails: FailureDetails, LastCompletionResult: Payload1, FirstDecisionTaskBackoffSeconds: &Duration1, PartitionConfig: PartitionConfig, } HistoryStartWorkflowExecutionResponse = types.StartWorkflowExecutionResponse{ RunID: RunID, } HistorySyncActivityRequest = types.SyncActivityRequest{ DomainID: DomainID, WorkflowID: WorkflowID, RunID: RunID, Version: Version1, ScheduledID: EventID1, ScheduledTime: &Timestamp1, StartedID: EventID1, StartedTime: &Timestamp2, LastHeartbeatTime: &Timestamp3, Details: Payload1, Attempt: Attempt, LastFailureReason: &FailureReason, LastWorkerIdentity: Identity, LastFailureDetails: FailureDetails, VersionHistory: &VersionHistory, } HistorySyncShardStatusRequest = types.SyncShardStatusRequest{ SourceCluster: ClusterName1, ShardID: ShardID, Timestamp: &Timestamp1, } HistoryTerminateWorkflowExecutionRequest = types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: DomainID, TerminateRequest: &TerminateWorkflowExecutionRequest, ExternalWorkflowExecution: &WorkflowExecution, ChildWorkflowOnly: true, } HistoryGetCrossClusterTasksRequest = GetCrossClusterTasksRequest HistoryGetCrossClusterTasksResponse = GetCrossClusterTasksResponse HistoryRespondCrossClusterTasksCompletedRequest = RespondCrossClusterTasksCompletedRequest HistoryRespondCrossClusterTasksCompletedResponse = RespondCrossClusterTasksCompletedResponse ) ================================================ FILE: common/types/testdata/service_matching.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import ( "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) const ( ForwardedFrom = "ForwardedFrom" PollerID = "PollerID" ) var ( TaskListPartitionConfig = types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, 2: { IsolationGroups: []string{"foo"}, }, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, 1: { IsolationGroups: []string{"bar"}, }, }, } LoadBalancerHints = types.LoadBalancerHints{ BacklogCount: 1000, RatePerSecond: 1.0, } MatchingAddActivityTaskRequest = types.AddActivityTaskRequest{ DomainUUID: DomainID, Execution: &WorkflowExecution, SourceDomainUUID: DomainID, TaskList: &TaskList, ScheduleID: EventID1, ScheduleToStartTimeoutSeconds: &Duration1, Source: types.TaskSourceDbBacklog.Ptr(), ForwardedFrom: ForwardedFrom, PartitionConfig: PartitionConfig, } MatchingAddDecisionTaskRequest = types.AddDecisionTaskRequest{ DomainUUID: DomainID, Execution: &WorkflowExecution, TaskList: &TaskList, ScheduleID: EventID1, ScheduleToStartTimeoutSeconds: &Duration1, Source: types.TaskSourceDbBacklog.Ptr(), ForwardedFrom: ForwardedFrom, PartitionConfig: PartitionConfig, } MatchingAddActivityTaskResponse = types.AddActivityTaskResponse{ PartitionConfig: &TaskListPartitionConfig, } MatchingAddDecisionTaskResponse = types.AddDecisionTaskResponse{ PartitionConfig: &TaskListPartitionConfig, } MatchingCancelOutstandingPollRequest = types.CancelOutstandingPollRequest{ DomainUUID: DomainID, TaskListType: common.Int32Ptr(int32(TaskListType)), TaskList: &TaskList, PollerID: PollerID, } MatchingDescribeTaskListRequest = types.MatchingDescribeTaskListRequest{ DomainUUID: DomainID, DescRequest: &DescribeTaskListRequest, } MatchingDescribeTaskListResponse = types.DescribeTaskListResponse{ Pollers: PollerInfoArray, TaskListStatus: &TaskListStatus, TaskList: &TaskList, } MatchingListTaskListPartitionsRequest = types.MatchingListTaskListPartitionsRequest{ Domain: DomainName, TaskList: &TaskList, } MatchingListTaskListPartitionsResponse = types.ListTaskListPartitionsResponse{ ActivityTaskListPartitions: TaskListPartitionMetadataArray, DecisionTaskListPartitions: TaskListPartitionMetadataArray, } MatchingPollForActivityTaskRequest = types.MatchingPollForActivityTaskRequest{ DomainUUID: DomainID, PollerID: PollerID, PollRequest: &PollForActivityTaskRequest, ForwardedFrom: ForwardedFrom, IsolationGroup: IsolationGroup, } MatchingPollForActivityTaskResponse = types.MatchingPollForActivityTaskResponse{ TaskToken: TaskToken, WorkflowExecution: &WorkflowExecution, ActivityID: ActivityID, ActivityType: &ActivityType, Input: Payload1, ScheduledTimestamp: &Timestamp1, ScheduleToCloseTimeoutSeconds: &Duration1, StartedTimestamp: &Timestamp2, StartToCloseTimeoutSeconds: &Duration2, HeartbeatTimeoutSeconds: &Duration3, Attempt: Attempt, ScheduledTimestampOfThisAttempt: &Timestamp3, HeartbeatDetails: Payload2, WorkflowType: &WorkflowType, WorkflowDomain: DomainName, Header: &Header, PartitionConfig: &TaskListPartitionConfig, LoadBalancerHints: &LoadBalancerHints, AutoConfigHint: &AutoConfigHint, } MatchingPollForDecisionTaskRequest = types.MatchingPollForDecisionTaskRequest{ DomainUUID: DomainID, PollerID: PollerID, PollRequest: &PollForDecisionTaskRequest, ForwardedFrom: ForwardedFrom, IsolationGroup: IsolationGroup, } MatchingPollForDecisionTaskResponse = types.MatchingPollForDecisionTaskResponse{ TaskToken: TaskToken, WorkflowExecution: &WorkflowExecution, WorkflowType: &WorkflowType, PreviousStartedEventID: common.Int64Ptr(EventID1), StartedEventID: EventID2, Attempt: Attempt, NextEventID: EventID3, BacklogCountHint: BacklogCountHint, StickyExecutionEnabled: true, Query: &WorkflowQuery, DecisionInfo: &TransientDecisionInfo, WorkflowExecutionTaskList: &TaskList, EventStoreVersion: EventStoreVersion, BranchToken: BranchToken, ScheduledTimestamp: &Timestamp1, StartedTimestamp: &Timestamp2, Queries: WorkflowQueryMap, PartitionConfig: &TaskListPartitionConfig, LoadBalancerHints: &LoadBalancerHints, AutoConfigHint: &AutoConfigHint, } MatchingQueryWorkflowRequest = types.MatchingQueryWorkflowRequest{ DomainUUID: DomainID, TaskList: &TaskList, QueryRequest: &QueryWorkflowRequest, ForwardedFrom: ForwardedFrom, } MatchingQueryWorkflowResponse = types.MatchingQueryWorkflowResponse{ QueryResult: Payload1, QueryRejected: &QueryRejected, PartitionConfig: &TaskListPartitionConfig, } MatchingRespondQueryTaskCompletedRequest = types.MatchingRespondQueryTaskCompletedRequest{ DomainUUID: DomainID, TaskList: &TaskList, TaskID: "TaskID", CompletedRequest: &RespondQueryTaskCompletedRequest, } MatchingGetTaskListsByDomainRequest = types.GetTaskListsByDomainRequest{ Domain: DomainName, } GetTaskListsByDomainResponse = types.GetTaskListsByDomainResponse{ DecisionTaskListMap: DescribeTaskListResponseMap, ActivityTaskListMap: DescribeTaskListResponseMap, } DescribeTaskListResponseMap = map[string]*types.DescribeTaskListResponse{DomainName: &MatchingDescribeTaskListResponse} MatchingActivityTaskDispatchInfo = types.ActivityTaskDispatchInfo{ ScheduledEvent: &HistoryEvent_WorkflowExecutionStarted, StartedTimestamp: &Timestamp1, Attempt: &Attempt2, ScheduledTimestampOfThisAttempt: &Timestamp1, HeartbeatDetails: Payload2, WorkflowType: &WorkflowType, WorkflowDomain: DomainName, } MatchingUpdateTaskListPartitionConfigRequest = types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: DomainID, TaskList: &TaskList, TaskListType: &TaskListType, PartitionConfig: &TaskListPartitionConfig, } MatchingRefreshTaskListPartitionConfigRequest = types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: DomainID, TaskList: &TaskList, TaskListType: &TaskListType, PartitionConfig: &TaskListPartitionConfig, } ) ================================================ FILE: common/types/testdata/service_sharddistributor.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testdata import "github.com/uber/cadence/common/types" var ( ShardDistributorGetShardOwnerRequest = types.GetShardOwnerRequest{ ShardKey: "shard-key", Namespace: "namespace", } ShardDistributorGetShardOwnerResponse = types.GetShardOwnerResponse{ Owner: "owner", Namespace: "namespace", Metadata: map[string]string{"key-1": "value-1", "key-2": "value-2"}, } ShardDistributorExecutorHeartbeatRequest = types.ExecutorHeartbeatRequest{ Namespace: "namespace", ExecutorID: "executor-id", Status: types.ExecutorStatusACTIVE, ShardStatusReports: map[string]*types.ShardStatusReport{ "shard-key-1": { Status: types.ShardStatusREADY, ShardLoad: 0.5, }, "shard-key-2": { Status: types.ShardStatusINVALID, ShardLoad: 0.75, }, }, Metadata: map[string]string{ "key-1": "value-1", "key-2": "value-2", }, } ShardDistributorExecutorHeartbeatResponse = types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{ "shard-key-1": { Status: types.AssignmentStatusREADY, }, "shard-key-2": { Status: types.AssignmentStatusINVALID, }, }, } ShardDistributorWatchNamespaceStateRequest = types.WatchNamespaceStateRequest{ Namespace: "namespace", } ShardDistributorWatchNamespaceStateResponse = types.WatchNamespaceStateResponse{ Executors: []*types.ExecutorShardAssignment{ { ExecutorID: "executor-1", AssignedShards: []*types.Shard{&types.Shard{ShardKey: "shard-1"}, &types.Shard{ShardKey: "shard-2"}}, Metadata: map[string]string{"key-1": "value-1"}, }, { ExecutorID: "executor-2", AssignedShards: []*types.Shard{&types.Shard{ShardKey: "shard-3"}}, Metadata: map[string]string{"key-2": "value-2"}, }, }, } ) ================================================ FILE: common/util/file_util.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package util import ( "errors" "io/ioutil" "os" "strings" ) var ( // ErrDirectoryExpected indicates that a directory was expected ErrDirectoryExpected = errors.New("a path to a directory was expected") // ErrFileExpected indicates that a file was expected ErrFileExpected = errors.New("a path to a file was expected") ) // FileExists returns true if file exists, false otherwise. // Returns error if could not verify if file exists or if path specifies directory instead of file. func FileExists(filepath string) (bool, error) { if info, err := os.Stat(filepath); err != nil { if os.IsNotExist(err) { return false, nil } return false, err } else if info.IsDir() { return false, ErrFileExpected } return true, nil } // DirectoryExists returns true if directory exists, false otherwise. // Returns error if could not verify if directory exists or if path does not point at directory. func DirectoryExists(path string) (bool, error) { if info, err := os.Stat(path); err != nil { if os.IsNotExist(err) { return false, nil } return false, err } else if !info.IsDir() { return false, ErrDirectoryExpected } return true, nil } // MkdirAll creates all subdirectories in given path. func MkdirAll(path string, dirMode os.FileMode) error { return os.MkdirAll(path, dirMode) } // WriteFile is used to write a file. If file already exists it is overwritten. // If file does not already exist it is created. func WriteFile(filepath string, data []byte, fileMode os.FileMode) (retErr error) { if err := os.Remove(filepath); err != nil && !os.IsNotExist(err) { return err } f, err := os.Create(filepath) if err != nil { return err } defer func() { err := f.Close() if err != nil { retErr = err } }() if err = f.Chmod(fileMode); err != nil { return err } if _, err = f.Write(data); err != nil { return err } return nil } // ReadFile reads the contents of a file specified by filepath // WARNING: callers of this method should be extremely careful not to use it in a context where filepath is supplied by the user. func ReadFile(filepath string) ([]byte, error) { // #nosec return ioutil.ReadFile(filepath) } // ListFiles lists all files in a directory. func ListFiles(dirPath string) ([]string, error) { if info, err := os.Stat(dirPath); err != nil { return nil, err } else if !info.IsDir() { return nil, ErrDirectoryExpected } f, err := os.Open(dirPath) if err != nil { return nil, err } fileNames, err := f.Readdirnames(-1) f.Close() if err != nil { return nil, err } return fileNames, nil } // ListFilesByPrefix lists all files in directory with prefix. func ListFilesByPrefix(dirPath string, prefix string) ([]string, error) { fileNames, err := ListFiles(dirPath) if err != nil { return nil, err } var filteredFileNames []string for _, name := range fileNames { if strings.HasPrefix(name, prefix) { filteredFileNames = append(filteredFileNames, name) } } return filteredFileNames, nil } ================================================ FILE: common/util/file_util_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package util import ( "io/ioutil" "os" "path/filepath" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) const ( testDirMode = os.FileMode(0700) testFileMode = os.FileMode(0600) ) type FileUtilSuite struct { *require.Assertions suite.Suite } func TestFileUtilSuite(t *testing.T) { suite.Run(t, new(FileUtilSuite)) } func (s *FileUtilSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *FileUtilSuite) TestFileExists() { dir := s.T().TempDir() exists, err := FileExists(dir) s.Error(err) s.False(exists) filename := "test-file-name" exists, err = FileExists(filepath.Join(dir, filename)) s.NoError(err) s.False(exists) s.createFile(dir, filename) exists, err = FileExists(filepath.Join(dir, filename)) s.NoError(err) s.True(exists) } func (s *FileUtilSuite) TestDirectoryExists() { dir := s.T().TempDir() subdir := "subdir" exists, err := DirectoryExists(filepath.Join(dir, subdir)) s.NoError(err) s.False(exists) filename := "test-file-name" s.createFile(dir, filename) fpath := filepath.Join(dir, filename) exists, err = DirectoryExists(fpath) s.Error(err) s.False(exists) } func (s *FileUtilSuite) TestMkdirAll() { dir := s.T().TempDir() s.NoError(MkdirAll(dir, testDirMode)) s.assertDirectoryExists(dir) subDirPath := filepath.Join(dir, "subdir_1", "subdir_2", "subdir_3") s.assertDirectoryNotExists(subDirPath) s.NoError(MkdirAll(subDirPath, testDirMode)) s.assertDirectoryExists(subDirPath) s.assertCorrectFileMode(subDirPath) filename := "test-file-name" s.createFile(dir, filename) fpath := filepath.Join(dir, filename) s.Error(MkdirAll(fpath, testDirMode)) } func (s *FileUtilSuite) TestWriteFile() { dir := s.T().TempDir() filename := "test-file-name" fpath := filepath.Join(dir, filename) s.NoError(WriteFile(fpath, []byte("file body 1"), testFileMode)) s.assertFileExists(fpath) s.assertCorrectFileMode(fpath) s.NoError(WriteFile(fpath, []byte("file body 2"), testFileMode)) s.assertFileExists(fpath) s.assertCorrectFileMode(fpath) s.Error(WriteFile(dir, []byte(""), testFileMode)) s.assertFileExists(fpath) } func (s *FileUtilSuite) TestReadFile() { dir := s.T().TempDir() filename := "test-file-name" fpath := filepath.Join(dir, filename) data, err := ReadFile(fpath) s.Error(err) s.Empty(data) err = WriteFile(fpath, []byte("file contents"), testFileMode) s.NoError(err) data, err = ReadFile(fpath) s.NoError(err) s.Equal("file contents", string(data)) } func (s *FileUtilSuite) TestListFilesByPrefix() { dir := s.T().TempDir() filename := "test-file-name" fpath := filepath.Join(dir, filename) files, err := ListFilesByPrefix(fpath, "test-") s.Error(err) s.Nil(files) subDirPath := filepath.Join(dir, "subdir") s.NoError(MkdirAll(subDirPath, testDirMode)) s.assertDirectoryExists(subDirPath) expectedFileNames := []string{"file_1", "file_2", "file_3"} for _, f := range expectedFileNames { s.createFile(dir, f) } for _, f := range []string{"randomFile", "fileWithOtherPrefix"} { s.createFile(dir, f) } actualFileNames, err := ListFilesByPrefix(dir, "file_") s.NoError(err) s.Equal(len(expectedFileNames), len(actualFileNames)) } func (s *FileUtilSuite) assertCorrectFileMode(path string) { info, err := os.Stat(path) s.NoError(err) mode := testFileMode if info.IsDir() { mode = testDirMode | os.ModeDir } s.Equal(mode, info.Mode()) } func (s *FileUtilSuite) createFile(dir string, filename string) { err := ioutil.WriteFile(filepath.Join(dir, filename), []byte("file contents"), testFileMode) s.Nil(err) } func (s *FileUtilSuite) assertFileExists(filepath string) { exists, err := FileExists(filepath) s.NoError(err) s.True(exists) } func (s *FileUtilSuite) assertDirectoryExists(path string) { exists, err := DirectoryExists(path) s.NoError(err) s.True(exists) } func (s *FileUtilSuite) assertDirectoryNotExists(path string) { exists, err := DirectoryExists(path) s.NoError(err) s.False(exists) } ================================================ FILE: common/util.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package common import ( "context" "encoding/json" "errors" "fmt" "math" "math/rand" "strings" "sync" "time" "github.com/dgryski/go-farm" "github.com/pborman/uuid" "go.uber.org/yarpc/yarpcerrors" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/constants" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) const ( golandMapReserverNumberOfBytes = 48 retryPersistenceOperationInitialInterval = 50 * time.Millisecond retryPersistenceOperationMaxInterval = 10 * time.Second retryPersistenceOperationExpirationInterval = 30 * time.Second historyServiceOperationInitialInterval = 50 * time.Millisecond historyServiceOperationMaxInterval = 10 * time.Second historyServiceOperationExpirationInterval = 30 * time.Second recordTaskStartedInitialInterval = 50 * time.Millisecond recordTaskStartedMaxInterval = 1 * time.Second recordTaskStartedExpirationInterval = 3 * time.Second matchingServiceOperationInitialInterval = 1000 * time.Millisecond matchingServiceOperationMaxInterval = 10 * time.Second matchingServiceOperationExpirationInterval = 30 * time.Second frontendServiceOperationInitialInterval = 200 * time.Millisecond frontendServiceOperationMaxInterval = 5 * time.Second frontendServiceOperationExpirationInterval = 15 * time.Second shardDistributorServiceOperationInitialInterval = 200 * time.Millisecond shardDistributorServiceOperationMaxInterval = 10 * time.Second shardDistributorServiceOperationExpirationInterval = 15 * time.Second adminServiceOperationInitialInterval = 200 * time.Millisecond adminServiceOperationMaxInterval = 5 * time.Second adminServiceOperationExpirationInterval = 15 * time.Second retryKafkaOperationInitialInterval = 50 * time.Millisecond retryKafkaOperationMaxInterval = 10 * time.Second retryKafkaOperationMaxAttempts = 10 retryTaskProcessingInitialInterval = 50 * time.Millisecond retryTaskProcessingMaxInterval = 100 * time.Millisecond retryTaskProcessingMaxAttempts = 3 replicationServiceBusyInitialInterval = 2 * time.Second replicationServiceBusyMaxInterval = 10 * time.Second replicationServiceBusyExpirationInterval = 5 * time.Minute taskCompleterInitialInterval = 1 * time.Second taskCompleterMaxInterval = 10 * time.Second taskCompleterExpirationInterval = 5 * time.Minute domainCacheInitialInterval = 1 * time.Second domainCacheMaxInterval = 5 * time.Second domainCacheExpirationInterval = 2 * time.Minute contextExpireThreshold = 10 * time.Millisecond // FailureReasonCompleteResultExceedsLimit is failureReason for complete result exceeds limit FailureReasonCompleteResultExceedsLimit = "COMPLETE_RESULT_EXCEEDS_LIMIT" // FailureReasonFailureDetailsExceedsLimit is failureReason for failure details exceeds limit FailureReasonFailureDetailsExceedsLimit = "FAILURE_DETAILS_EXCEEDS_LIMIT" // FailureReasonCancelDetailsExceedsLimit is failureReason for cancel details exceeds limit FailureReasonCancelDetailsExceedsLimit = "CANCEL_DETAILS_EXCEEDS_LIMIT" // FailureReasonHeartbeatExceedsLimit is failureReason for heartbeat exceeds limit FailureReasonHeartbeatExceedsLimit = "HEARTBEAT_EXCEEDS_LIMIT" // FailureReasonDecisionBlobSizeExceedsLimit is the failureReason for decision blob exceeds size limit FailureReasonDecisionBlobSizeExceedsLimit = "DECISION_BLOB_SIZE_EXCEEDS_LIMIT" // FailureReasonSizeExceedsLimit is reason to fail workflow when history size or count exceed limit FailureReasonSizeExceedsLimit = "HISTORY_EXCEEDS_LIMIT" // FailureReasonTransactionSizeExceedsLimit is the failureReason for when transaction cannot be committed because it exceeds size limit FailureReasonTransactionSizeExceedsLimit = "TRANSACTION_SIZE_EXCEEDS_LIMIT" // FailureReasonDecisionAttemptsExceedsLimit is reason to fail workflow when decision attempts fail too many times FailureReasonDecisionAttemptsExceedsLimit = "DECISION_ATTEMPTS_EXCEEDS_LIMIT" // FailureReasonPendingActivityExceedsLimit is reason to fail overflow when pending activity exceeds limit FailureReasonPendingActivityExceedsLimit = "PENDING_ACTIVITY_EXCEEDS_LIMIT" ) var ( // ErrBlobSizeExceedsLimit is error for event blob size exceeds limit ErrBlobSizeExceedsLimit = &types.BadRequestError{Message: "Blob data size exceeds limit."} // ErrContextTimeoutTooShort is error for setting a very short context timeout when calling a long poll API ErrContextTimeoutTooShort = &types.BadRequestError{Message: "Context timeout is too short."} // ErrContextTimeoutNotSet is error for not setting a context timeout when calling a long poll API ErrContextTimeoutNotSet = &types.BadRequestError{Message: "Context timeout is not set."} // ErrDecisionResultCountTooLarge error for decision result count exceeds limit ErrDecisionResultCountTooLarge = &types.BadRequestError{Message: "Decision result count exceeds limit."} stickyTaskListMetricTag = metrics.TaskListTag("__sticky__") ephemeralTaskListMetricTag = metrics.TaskListTag("__ephemeral__") ) // AwaitWaitGroup calls Wait on the given wait // Returns true if the Wait() call succeeded before the timeout // Returns false if the Wait() did not return before the timeout func AwaitWaitGroup(wg *sync.WaitGroup, timeout time.Duration) bool { doneC := make(chan struct{}) go func() { wg.Wait() close(doneC) }() select { case <-doneC: return true case <-time.After(timeout): return false } } // CreatePersistenceRetryPolicy creates a retry policy for persistence layer operations func CreatePersistenceRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(retryPersistenceOperationInitialInterval) policy.SetMaximumInterval(retryPersistenceOperationMaxInterval) policy.SetExpirationInterval(retryPersistenceOperationExpirationInterval) return policy } // CreateHistoryServiceRetryPolicy creates a retry policy for calls to history service func CreateHistoryServiceRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(historyServiceOperationInitialInterval) policy.SetMaximumInterval(historyServiceOperationMaxInterval) policy.SetExpirationInterval(historyServiceOperationExpirationInterval) return policy } func CreateRecordTaskStartedRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(recordTaskStartedInitialInterval) policy.SetMaximumInterval(recordTaskStartedMaxInterval) policy.SetExpirationInterval(recordTaskStartedExpirationInterval) return policy } // CreateMatchingServiceRetryPolicy creates a retry policy for calls to matching service func CreateMatchingServiceRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(matchingServiceOperationInitialInterval) policy.SetMaximumInterval(matchingServiceOperationMaxInterval) policy.SetExpirationInterval(matchingServiceOperationExpirationInterval) return policy } // CreateFrontendServiceRetryPolicy creates a retry policy for calls to frontend service func CreateFrontendServiceRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(frontendServiceOperationInitialInterval) policy.SetMaximumInterval(frontendServiceOperationMaxInterval) policy.SetExpirationInterval(frontendServiceOperationExpirationInterval) return policy } func CreateShardDistributorServiceRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(shardDistributorServiceOperationInitialInterval) policy.SetMaximumInterval(shardDistributorServiceOperationMaxInterval) policy.SetExpirationInterval(shardDistributorServiceOperationExpirationInterval) return policy } // CreateAdminServiceRetryPolicy creates a retry policy for calls to matching service func CreateAdminServiceRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(adminServiceOperationInitialInterval) policy.SetMaximumInterval(adminServiceOperationMaxInterval) policy.SetExpirationInterval(adminServiceOperationExpirationInterval) return policy } // CreateDlqPublishRetryPolicy creates a retry policy for kafka operation func CreateDlqPublishRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(retryKafkaOperationInitialInterval) policy.SetMaximumInterval(retryKafkaOperationMaxInterval) policy.SetMaximumAttempts(retryKafkaOperationMaxAttempts) return policy } // CreateTaskProcessingRetryPolicy creates a retry policy for task processing func CreateTaskProcessingRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(retryTaskProcessingInitialInterval) policy.SetMaximumInterval(retryTaskProcessingMaxInterval) policy.SetMaximumAttempts(retryTaskProcessingMaxAttempts) return policy } // CreateReplicationServiceBusyRetryPolicy creates a retry policy to handle replication service busy func CreateReplicationServiceBusyRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(replicationServiceBusyInitialInterval) policy.SetMaximumInterval(replicationServiceBusyMaxInterval) policy.SetExpirationInterval(replicationServiceBusyExpirationInterval) return policy } // CreateTaskCompleterRetryPolicy creates a retry policy to handle tasks not being started func CreateTaskCompleterRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(taskCompleterInitialInterval) policy.SetMaximumInterval(taskCompleterMaxInterval) policy.SetExpirationInterval(taskCompleterExpirationInterval) return policy } // CreateDomainCacheRetryPolicy creates a retry policy to handle domain cache refresh failures func CreateDomainCacheRetryPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(domainCacheInitialInterval) policy.SetMaximumInterval(domainCacheMaxInterval) policy.SetExpirationInterval(domainCacheExpirationInterval) return policy } // IsValidIDLength checks if id is valid according to its length func IsValidIDLength( id string, scope metrics.Scope, warnLimit int, errorLimit int, metricsCounter metrics.MetricIdx, domainName string, logger log.Logger, idTypeViolationTag tag.Tag, ) bool { if len(id) > warnLimit { scope.IncCounter(metricsCounter) logger.Warn("ID length exceeds limit.", tag.WorkflowDomainName(domainName), tag.Name(id), idTypeViolationTag) } return len(id) <= errorLimit } // CheckDecisionResultLimit checks if decision result count exceeds limits. func CheckDecisionResultLimit( actualSize int, limit int, scope metrics.Scope, ) error { scope.RecordTimer(metrics.DecisionResultCount, time.Duration(actualSize)) if limit > 0 && actualSize > limit { return ErrDecisionResultCountTooLarge } return nil } // ToServiceTransientError converts an error to ServiceTransientError func ToServiceTransientError(err error) error { if err == nil || IsServiceTransientError(err) { return err } return yarpcerrors.Newf(yarpcerrors.CodeUnavailable, err.Error()) } // HistoryRetryFuncFrontendExceptions checks if an error should be retried // in a call from frontend func FrontendRetry(err error) bool { var sbErr *types.ServiceBusyError if errors.As(err, &sbErr) { // If the service busy error is due to workflow id rate limiting, proxy it to the caller return sbErr.Reason != constants.WorkflowIDRateLimitReason } return IsServiceTransientError(err) } // IsExpectedError checks if an error is expected to happen in normal operation of the system func IsExpectedError(err error) bool { return IsServiceTransientError(err) || IsEntityNotExistsError(err) || errors.As(err, new(*types.WorkflowExecutionAlreadyCompletedError)) } // IsServiceTransientError checks if the error is a transient error. func IsServiceTransientError(err error) bool { var ( typesInternalServiceError *types.InternalServiceError typesServiceBusyError *types.ServiceBusyError typesShardOwnershipLostError *types.ShardOwnershipLostError typesTaskListNotOwnedByHostError *cadence_errors.TaskListNotOwnedByHostError yarpcErrorsStatus *yarpcerrors.Status ) switch { case errors.As(err, &typesInternalServiceError): return true case errors.As(err, &typesServiceBusyError): return true case errors.As(err, &typesShardOwnershipLostError): return true case errors.As(err, &typesTaskListNotOwnedByHostError): return true case errors.As(err, &yarpcErrorsStatus): // We only selectively retry the following yarpc errors client can safe retry with a backoff if yarpcerrors.IsUnavailable(err) || yarpcerrors.IsUnknown(err) || yarpcerrors.IsCancelled(err) || yarpcerrors.IsInternal(err) { return true } return false } return false } // IsEntityNotExistsError checks if the error is an entity not exists error. func IsEntityNotExistsError(err error) bool { _, ok := err.(*types.EntityNotExistsError) return ok } // IsServiceBusyError checks if the error is a service busy error. func IsServiceBusyError(err error) bool { switch err.(type) { case *types.ServiceBusyError: return true } return false } // IsContextTimeoutError checks if the error is context timeout error func IsContextTimeoutError(err error) bool { switch err := err.(type) { case *types.InternalServiceError: return err.Message == context.DeadlineExceeded.Error() } return err == context.DeadlineExceeded || yarpcerrors.IsDeadlineExceeded(err) } // WorkflowIDToHistoryShard is used to map a workflowID to a shardID func WorkflowIDToHistoryShard(workflowID string, numberOfShards int) int { hash := farm.Fingerprint32([]byte(workflowID)) return int(hash % uint32(numberOfShards)) } // DomainIDToHistoryShard is used to map a domainID to a shardID func DomainIDToHistoryShard(domainID string, numberOfShards int) int { hash := farm.Fingerprint32([]byte(domainID)) return int(hash % uint32(numberOfShards)) } // IsValidContext checks that the thrift context is not expired on cancelled. // Returns nil if the context is still valid. Otherwise, returns the result of // ctx.Err() func IsValidContext(ctx context.Context) error { ch := ctx.Done() if ch != nil { select { case <-ch: return ctx.Err() default: // go to the next line } } deadline, ok := ctx.Deadline() if ok && time.Until(deadline) < contextExpireThreshold { return context.DeadlineExceeded } return nil } // emptyCancelFunc wraps an empty func by context.CancelFunc interface var emptyCancelFunc = context.CancelFunc(func() {}) // CreateChildContext creates a child context which shorted context timeout // from the given parent context // tailroom must be in range [0, 1] and // (1-tailroom) * parent timeout will be the new child context timeout // if tailroom is less 0, tailroom will be considered as 0 // if tailroom is greater than 1, tailroom wil be considered as 1 func CreateChildContext( parent context.Context, tailroom float64, ) (context.Context, context.CancelFunc) { if parent == nil { return nil, emptyCancelFunc } if parent.Err() != nil { return parent, emptyCancelFunc } now := time.Now() deadline, ok := parent.Deadline() if !ok || deadline.Before(now) { return parent, emptyCancelFunc } // if tailroom is about or less 0, then return a context with the same deadline as parent if tailroom <= 0 { return context.WithDeadline(parent, deadline) } // if tailroom is about or greater 1, then return a context with deadline of now if tailroom >= 1 { return context.WithDeadline(parent, now) } newDeadline := now.Add(time.Duration(math.Ceil(float64(deadline.Sub(now)) * (1.0 - tailroom)))) return context.WithDeadline(parent, newDeadline) } // GenerateRandomString is used for generate test string func GenerateRandomString(n int) string { if n <= 0 { return "" } letterRunes := []rune("random") b := make([]rune, n) for i := range b { b[i] = letterRunes[rand.Intn(len(letterRunes))] } return string(b) } // CreateMatchingPollForDecisionTaskResponse create response for matching's PollForDecisionTask func CreateMatchingPollForDecisionTaskResponse(historyResponse *types.RecordDecisionTaskStartedResponse, workflowExecution *types.WorkflowExecution, token []byte) *types.MatchingPollForDecisionTaskResponse { matchingResp := &types.MatchingPollForDecisionTaskResponse{ WorkflowExecution: workflowExecution, TaskToken: token, Attempt: historyResponse.GetAttempt(), WorkflowType: historyResponse.WorkflowType, StartedEventID: historyResponse.StartedEventID, StickyExecutionEnabled: historyResponse.StickyExecutionEnabled, NextEventID: historyResponse.NextEventID, DecisionInfo: historyResponse.DecisionInfo, WorkflowExecutionTaskList: historyResponse.WorkflowExecutionTaskList, BranchToken: historyResponse.BranchToken, ScheduledTimestamp: historyResponse.ScheduledTimestamp, StartedTimestamp: historyResponse.StartedTimestamp, Queries: historyResponse.Queries, TotalHistoryBytes: historyResponse.HistorySize, } if historyResponse.GetPreviousStartedEventID() != constants.EmptyEventID { matchingResp.PreviousStartedEventID = historyResponse.PreviousStartedEventID } return matchingResp } // ValidateRetryPolicy validates a retry policy func ValidateRetryPolicy(policy *types.RetryPolicy) error { if policy == nil { // nil policy is valid which means no retry return nil } if policy.GetInitialIntervalInSeconds() <= 0 { return &types.BadRequestError{Message: "InitialIntervalInSeconds must be greater than 0 on retry policy."} } if policy.GetBackoffCoefficient() < 1 { return &types.BadRequestError{Message: "BackoffCoefficient cannot be less than 1 on retry policy."} } if policy.GetMaximumIntervalInSeconds() < 0 { return &types.BadRequestError{Message: "MaximumIntervalInSeconds cannot be less than 0 on retry policy."} } if policy.GetMaximumIntervalInSeconds() > 0 && policy.GetMaximumIntervalInSeconds() < policy.GetInitialIntervalInSeconds() { return &types.BadRequestError{Message: "MaximumIntervalInSeconds cannot be less than InitialIntervalInSeconds on retry policy."} } if policy.GetMaximumAttempts() < 0 { return &types.BadRequestError{Message: "MaximumAttempts cannot be less than 0 on retry policy."} } if policy.GetExpirationIntervalInSeconds() < 0 { return &types.BadRequestError{Message: "ExpirationIntervalInSeconds cannot be less than 0 on retry policy."} } if policy.GetMaximumAttempts() == 0 && policy.GetExpirationIntervalInSeconds() == 0 { return &types.BadRequestError{Message: "MaximumAttempts and ExpirationIntervalInSeconds are both 0. At least one of them must be specified."} } return nil } // CreateHistoryStartWorkflowRequest create a start workflow request for history func CreateHistoryStartWorkflowRequest( domainID string, startRequest *types.StartWorkflowExecutionRequest, now time.Time, partitionConfig map[string]string, ) (*types.HistoryStartWorkflowExecutionRequest, error) { histRequest := &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: startRequest, PartitionConfig: partitionConfig, } delayStartSeconds := startRequest.GetDelayStartSeconds() jitterStartSeconds := startRequest.GetJitterStartSeconds() firstRunAtTimestamp := startRequest.GetFirstRunAtTimeStamp() firstDecisionTaskBackoffSeconds := delayStartSeconds // if the user specified a timestamp for the first run, we will use that as the start time, // ignoring the delayStartSeconds, jitterStartSeconds, and cronSchedule // The following condition guarantees two things: // - The logic is only triggered when the user specifies a first run timestamp // - AND that timestamp is only triggered ONCE hence not interfering with other scheduling logic if firstRunAtTimestamp > now.UnixNano() { firstDecisionTaskBackoffSeconds = int32((firstRunAtTimestamp - now.UnixNano()) / int64(time.Second)) } else { if len(startRequest.GetCronSchedule()) > 0 { delayedStartTime := now.Add(time.Second * time.Duration(delayStartSeconds)) var err error firstDecisionTaskBackoffSeconds, err = backoff.GetBackoffForNextScheduleInSeconds( startRequest.GetCronSchedule(), delayedStartTime, delayedStartTime, jitterStartSeconds, startRequest.GetCronOverlapPolicy()) if err != nil { return nil, err } // backoff seconds was calculated based on delayed start time, so we need to // add the delayStartSeconds to that backoff. firstDecisionTaskBackoffSeconds += delayStartSeconds } else if jitterStartSeconds > 0 { // Add a random jitter to start time, if requested. firstDecisionTaskBackoffSeconds += rand.Int31n(jitterStartSeconds + 1) } } histRequest.FirstDecisionTaskBackoffSeconds = Int32Ptr(firstDecisionTaskBackoffSeconds) if startRequest.RetryPolicy != nil && startRequest.RetryPolicy.GetExpirationIntervalInSeconds() > 0 { expirationInSeconds := startRequest.RetryPolicy.GetExpirationIntervalInSeconds() + firstDecisionTaskBackoffSeconds // expirationTime calculates from first decision task schedule to the end of the workflow deadline := now.Add(time.Duration(expirationInSeconds) * time.Second) histRequest.ExpirationTimestamp = Int64Ptr(deadline.Round(time.Millisecond).UnixNano()) } return histRequest, nil } // CheckEventBlobSizeLimit checks if a blob data exceeds limits. It logs a warning if it exceeds warnLimit, // and return ErrBlobSizeExceedsLimit if it exceeds errorLimit. func CheckEventBlobSizeLimit( actualSize int, warnLimit int, errorLimit int, domainID string, domainName string, workflowID string, runID string, scope metrics.Scope, logger log.Logger, blobSizeViolationOperationTag tag.Tag, ) error { scope.RecordTimer(metrics.EventBlobSize, time.Duration(actualSize)) if errorLimit < warnLimit { logger.Warn("Error limit is less than warn limit.", tag.WorkflowDomainName(domainName), tag.WorkflowDomainID(domainID)) warnLimit = errorLimit } if actualSize <= warnLimit { return nil } tags := []tag.Tag{ tag.WorkflowDomainName(domainName), tag.WorkflowDomainID(domainID), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.WorkflowSize(int64(actualSize)), blobSizeViolationOperationTag, } if actualSize <= errorLimit { logger.Warn("Blob size close to the limit.", tags...) return nil } scope.Tagged(metrics.DomainTag(domainName)).IncCounter(metrics.EventBlobSizeExceedLimit) logger.Error("Blob size exceeds limit.", tags...) return ErrBlobSizeExceedsLimit } // ValidateLongPollContextTimeout check if the context timeout for a long poll handler is too short or below a normal value. // If the timeout is not set or too short, it logs an error, and return ErrContextTimeoutNotSet or ErrContextTimeoutTooShort // accordingly. If the timeout is only below a normal value, it just logs an info and return nil. func ValidateLongPollContextTimeout( ctx context.Context, handlerName string, logger log.Logger, ) error { deadline, err := ValidateLongPollContextTimeoutIsSet(ctx, handlerName, logger) if err != nil { return err } timeout := time.Until(deadline) if timeout < constants.MinLongPollTimeout { err := ErrContextTimeoutTooShort logger.Error("Context timeout is too short for long poll API.", tag.WorkflowHandlerName(handlerName), tag.Error(err), tag.WorkflowPollContextTimeout(timeout)) return err } if timeout < constants.CriticalLongPollTimeout { logger.Debug("Context timeout is lower than critical value for long poll API.", tag.WorkflowHandlerName(handlerName), tag.WorkflowPollContextTimeout(timeout)) } return nil } // ValidateLongPollContextTimeoutIsSet checks if the context timeout is set for long poll requests. func ValidateLongPollContextTimeoutIsSet( ctx context.Context, handlerName string, logger log.Logger, ) (time.Time, error) { deadline, ok := ctx.Deadline() if !ok { err := ErrContextTimeoutNotSet logger.Error("Context timeout not set for long poll API.", tag.WorkflowHandlerName(handlerName), tag.Error(err)) return deadline, err } return deadline, nil } // ValidateDomainUUID checks if the given domainID string is a valid UUID func ValidateDomainUUID( domainUUID string, ) error { if domainUUID == "" { return &types.BadRequestError{Message: "Missing domain UUID."} } else if uuid.Parse(domainUUID) == nil { return &types.BadRequestError{Message: "Invalid domain UUID."} } return nil } // GetSizeOfMapStringToByteArray get size of map[string][]byte func GetSizeOfMapStringToByteArray(input map[string][]byte) int { if input == nil { return 0 } res := 0 for k, v := range input { res += len(k) + len(v) } return res + golandMapReserverNumberOfBytes } // GetSizeOfHistoryEvent returns approximate size in bytes of the history event taking into account byte arrays only now func GetSizeOfHistoryEvent(event *types.HistoryEvent) uint64 { if event == nil || event.EventType == nil { return 0 } res := 0 switch *event.EventType { case types.EventTypeWorkflowExecutionStarted: res += len(event.WorkflowExecutionStartedEventAttributes.Input) res += len(event.WorkflowExecutionStartedEventAttributes.ContinuedFailureDetails) res += len(event.WorkflowExecutionStartedEventAttributes.LastCompletionResult) if event.WorkflowExecutionStartedEventAttributes.Memo != nil { res += GetSizeOfMapStringToByteArray(event.WorkflowExecutionStartedEventAttributes.Memo.Fields) } if event.WorkflowExecutionStartedEventAttributes.Header != nil { res += GetSizeOfMapStringToByteArray(event.WorkflowExecutionStartedEventAttributes.Header.Fields) } if event.WorkflowExecutionStartedEventAttributes.SearchAttributes != nil { res += GetSizeOfMapStringToByteArray(event.WorkflowExecutionStartedEventAttributes.SearchAttributes.IndexedFields) } case types.EventTypeWorkflowExecutionCompleted: res += len(event.WorkflowExecutionCompletedEventAttributes.Result) case types.EventTypeWorkflowExecutionFailed: res += len(event.WorkflowExecutionFailedEventAttributes.Details) case types.EventTypeWorkflowExecutionTimedOut: case types.EventTypeDecisionTaskScheduled: case types.EventTypeDecisionTaskStarted: case types.EventTypeDecisionTaskCompleted: res += len(event.DecisionTaskCompletedEventAttributes.ExecutionContext) case types.EventTypeDecisionTaskTimedOut: case types.EventTypeDecisionTaskFailed: res += len(event.DecisionTaskFailedEventAttributes.Details) case types.EventTypeActivityTaskScheduled: res += len(event.ActivityTaskScheduledEventAttributes.Input) if event.ActivityTaskScheduledEventAttributes.Header != nil { res += GetSizeOfMapStringToByteArray(event.ActivityTaskScheduledEventAttributes.Header.Fields) } case types.EventTypeActivityTaskStarted: res += len(event.ActivityTaskStartedEventAttributes.LastFailureDetails) case types.EventTypeActivityTaskCompleted: res += len(event.ActivityTaskCompletedEventAttributes.Result) case types.EventTypeActivityTaskFailed: res += len(event.ActivityTaskFailedEventAttributes.Details) case types.EventTypeActivityTaskTimedOut: res += len(event.ActivityTaskTimedOutEventAttributes.Details) res += len(event.ActivityTaskTimedOutEventAttributes.LastFailureDetails) case types.EventTypeActivityTaskCancelRequested: case types.EventTypeRequestCancelActivityTaskFailed: case types.EventTypeActivityTaskCanceled: res += len(event.ActivityTaskCanceledEventAttributes.Details) case types.EventTypeTimerStarted: case types.EventTypeTimerFired: case types.EventTypeCancelTimerFailed: case types.EventTypeTimerCanceled: case types.EventTypeWorkflowExecutionCancelRequested: case types.EventTypeWorkflowExecutionCanceled: res += len(event.WorkflowExecutionCanceledEventAttributes.Details) case types.EventTypeRequestCancelExternalWorkflowExecutionInitiated: res += len(event.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes.Control) case types.EventTypeRequestCancelExternalWorkflowExecutionFailed: res += len(event.RequestCancelExternalWorkflowExecutionFailedEventAttributes.Control) case types.EventTypeExternalWorkflowExecutionCancelRequested: case types.EventTypeMarkerRecorded: res += len(event.MarkerRecordedEventAttributes.Details) case types.EventTypeWorkflowExecutionSignaled: res += len(event.WorkflowExecutionSignaledEventAttributes.Input) case types.EventTypeWorkflowExecutionTerminated: res += len(event.WorkflowExecutionTerminatedEventAttributes.Details) case types.EventTypeWorkflowExecutionContinuedAsNew: res += len(event.WorkflowExecutionContinuedAsNewEventAttributes.Input) if event.WorkflowExecutionContinuedAsNewEventAttributes.Memo != nil { res += GetSizeOfMapStringToByteArray(event.WorkflowExecutionContinuedAsNewEventAttributes.Memo.Fields) } if event.WorkflowExecutionContinuedAsNewEventAttributes.Header != nil { res += GetSizeOfMapStringToByteArray(event.WorkflowExecutionContinuedAsNewEventAttributes.Header.Fields) } if event.WorkflowExecutionContinuedAsNewEventAttributes.SearchAttributes != nil { res += GetSizeOfMapStringToByteArray(event.WorkflowExecutionContinuedAsNewEventAttributes.SearchAttributes.IndexedFields) } case types.EventTypeStartChildWorkflowExecutionInitiated: res += len(event.StartChildWorkflowExecutionInitiatedEventAttributes.Input) res += len(event.StartChildWorkflowExecutionInitiatedEventAttributes.Control) if event.StartChildWorkflowExecutionInitiatedEventAttributes.Memo != nil { res += GetSizeOfMapStringToByteArray(event.StartChildWorkflowExecutionInitiatedEventAttributes.Memo.Fields) } if event.StartChildWorkflowExecutionInitiatedEventAttributes.Header != nil { res += GetSizeOfMapStringToByteArray(event.StartChildWorkflowExecutionInitiatedEventAttributes.Header.Fields) } if event.StartChildWorkflowExecutionInitiatedEventAttributes.SearchAttributes != nil { res += GetSizeOfMapStringToByteArray(event.StartChildWorkflowExecutionInitiatedEventAttributes.SearchAttributes.IndexedFields) } case types.EventTypeStartChildWorkflowExecutionFailed: res += len(event.StartChildWorkflowExecutionFailedEventAttributes.Control) case types.EventTypeChildWorkflowExecutionStarted: if event.ChildWorkflowExecutionStartedEventAttributes != nil && event.ChildWorkflowExecutionStartedEventAttributes.Header != nil { res += GetSizeOfMapStringToByteArray(event.ChildWorkflowExecutionStartedEventAttributes.Header.Fields) } case types.EventTypeChildWorkflowExecutionCompleted: res += len(event.ChildWorkflowExecutionCompletedEventAttributes.Result) case types.EventTypeChildWorkflowExecutionFailed: res += len(event.ChildWorkflowExecutionFailedEventAttributes.Details) case types.EventTypeChildWorkflowExecutionCanceled: res += len(event.ChildWorkflowExecutionCanceledEventAttributes.Details) case types.EventTypeChildWorkflowExecutionTimedOut: case types.EventTypeChildWorkflowExecutionTerminated: case types.EventTypeSignalExternalWorkflowExecutionInitiated: res += len(event.SignalExternalWorkflowExecutionInitiatedEventAttributes.Input) res += len(event.SignalExternalWorkflowExecutionInitiatedEventAttributes.Control) case types.EventTypeSignalExternalWorkflowExecutionFailed: res += len(event.SignalExternalWorkflowExecutionFailedEventAttributes.Control) case types.EventTypeExternalWorkflowExecutionSignaled: res += len(event.ExternalWorkflowExecutionSignaledEventAttributes.Control) case types.EventTypeUpsertWorkflowSearchAttributes: if event.UpsertWorkflowSearchAttributesEventAttributes.SearchAttributes != nil { res += GetSizeOfMapStringToByteArray(event.UpsertWorkflowSearchAttributesEventAttributes.SearchAttributes.IndexedFields) } } return uint64(res) } // IsJustOrderByClause return true is query start with order by func IsJustOrderByClause(clause string) bool { whereClause := strings.TrimSpace(clause) whereClause = strings.ToLower(whereClause) return strings.HasPrefix(whereClause, "order by") } // ConvertIndexedValueTypeToInternalType takes fieldType as interface{} and convert to IndexedValueType. // Because different implementation of dynamic config client may lead to different types func ConvertIndexedValueTypeToInternalType(fieldType interface{}, logger log.Logger) types.IndexedValueType { switch t := fieldType.(type) { case float64: return types.IndexedValueType(t) case int: return types.IndexedValueType(t) case types.IndexedValueType: return t case []byte: var result types.IndexedValueType if err := result.UnmarshalText(t); err != nil { logger.Error("unknown index value type", tag.Value(fieldType), tag.ValueType(t), tag.Error(err)) return fieldType.(types.IndexedValueType) // it will panic and been captured by logger } return result case string: var result types.IndexedValueType if err := result.UnmarshalText([]byte(t)); err != nil { logger.Error("unknown index value type", tag.Value(fieldType), tag.ValueType(t), tag.Error(err)) return fieldType.(types.IndexedValueType) // it will panic and been captured by logger } return result default: // Unknown fieldType, please make sure dynamic config return correct value type logger.Error("unknown index value type", tag.Value(fieldType), tag.ValueType(t)) return fieldType.(types.IndexedValueType) // it will panic and been captured by logger } } // DeserializeSearchAttributeValue takes json encoded search attribute value and it's type as input, then // unmarshal the value into a concrete type and return the value func DeserializeSearchAttributeValue(value []byte, valueType types.IndexedValueType) (interface{}, error) { switch valueType { case types.IndexedValueTypeString, types.IndexedValueTypeKeyword: var val string if err := json.Unmarshal(value, &val); err != nil { var listVal []string err = json.Unmarshal(value, &listVal) return listVal, err } return val, nil case types.IndexedValueTypeInt: var val int64 if err := json.Unmarshal(value, &val); err != nil { var listVal []int64 err = json.Unmarshal(value, &listVal) return listVal, err } return val, nil case types.IndexedValueTypeDouble: var val float64 if err := json.Unmarshal(value, &val); err != nil { var listVal []float64 err = json.Unmarshal(value, &listVal) return listVal, err } return val, nil case types.IndexedValueTypeBool: var val bool if err := json.Unmarshal(value, &val); err != nil { var listVal []bool err = json.Unmarshal(value, &listVal) return listVal, err } return val, nil case types.IndexedValueTypeDatetime: var val time.Time if err := json.Unmarshal(value, &val); err != nil { var listVal []time.Time err = json.Unmarshal(value, &listVal) return listVal, err } return val, nil default: return nil, fmt.Errorf("error: unknown index value type [%v]", valueType) } } // IsAdvancedVisibilityWritingEnabled returns true if we should write to advanced visibility func IsAdvancedVisibilityWritingEnabled(advancedVisibilityWritingMode string, isAdvancedVisConfigExist bool) bool { return advancedVisibilityWritingMode != constants.AdvancedVisibilityModeOff && isAdvancedVisConfigExist } // IsAdvancedVisibilityReadingEnabled returns true if we should read from advanced visibility func IsAdvancedVisibilityReadingEnabled(isAdvancedVisReadEnabled, isAdvancedVisConfigExist bool) bool { return isAdvancedVisReadEnabled && isAdvancedVisConfigExist } // IsStickyTaskConditionError is error from matching engine func IsStickyTaskConditionError(err error) bool { if e, ok := err.(*types.InternalServiceError); ok { return e.GetMessage() == constants.StickyTaskConditionFailedErrorMsg } return false } // DurationToDays converts time.Duration to number of 24 hour days func DurationToDays(d time.Duration) int32 { return int32(d / (24 * time.Hour)) } // DurationToSeconds converts time.Duration to number of seconds func DurationToSeconds(d time.Duration) int64 { return int64(d / time.Second) } // DaysToDuration converts number of 24 hour days to time.Duration func DaysToDuration(d int32) time.Duration { return time.Duration(d) * (24 * time.Hour) } // SecondsToDuration converts number of seconds to time.Duration func SecondsToDuration(d int64) time.Duration { return time.Duration(d) * time.Second } // SleepWithMinDuration sleeps for the minimum of desired and available duration // returns the remaining available time duration func SleepWithMinDuration(desired time.Duration, available time.Duration) time.Duration { d := min(desired, available) if d > 0 { time.Sleep(d) } return available - d } // ConvertErrToGetTaskFailedCause converts error to GetTaskFailedCause func ConvertErrToGetTaskFailedCause(err error) types.GetTaskFailedCause { if IsContextTimeoutError(err) { return types.GetTaskFailedCauseTimeout } if IsServiceBusyError(err) { return types.GetTaskFailedCauseServiceBusy } if _, ok := err.(*types.ShardOwnershipLostError); ok { return types.GetTaskFailedCauseShardOwnershipLost } return types.GetTaskFailedCauseUncategorized } // ConvertGetTaskFailedCauseToErr converts GetTaskFailedCause to error func ConvertGetTaskFailedCauseToErr(failedCause types.GetTaskFailedCause) error { switch failedCause { case types.GetTaskFailedCauseServiceBusy: return &types.ServiceBusyError{} case types.GetTaskFailedCauseTimeout: return context.DeadlineExceeded case types.GetTaskFailedCauseShardOwnershipLost: return &types.ShardOwnershipLostError{} default: return &types.InternalServiceError{Message: "uncategorized error"} } } // IntersectionStringSlice get the intersection of 2 string slices func IntersectionStringSlice(a, b []string) []string { var result []string m := make(map[string]struct{}) for _, item := range a { m[item] = struct{}{} } for _, item := range b { if _, ok := m[item]; ok { result = append(result, item) } } return result } // GetTaskListTag returns the task list tag for the given task list name and kind func GetTaskListTag(taskListName string, taskListKind types.TaskListKind) metrics.Tag { taskListTag := metrics.TaskListUnknownTag() if taskListName != "" && taskListKind == types.TaskListKindNormal { taskListTag = metrics.TaskListTag(taskListName) } if taskListKind == types.TaskListKindSticky { taskListTag = stickyTaskListMetricTag } if taskListKind == types.TaskListKindEphemeral { taskListTag = ephemeralTaskListMetricTag } return taskListTag } // NewPerTaskListScope creates a tasklist metrics scope func NewPerTaskListScope( domainName string, taskListName string, taskListKind types.TaskListKind, client metrics.Client, scopeIdx metrics.ScopeIdx, ) metrics.Scope { domainTag := metrics.DomainUnknownTag() if domainName != "" { domainTag = metrics.DomainTag(domainName) } taskListTag := GetTaskListTag(taskListName, taskListKind) return client.Scope(scopeIdx, domainTag, taskListTag) } ================================================ FILE: common/util_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package common import ( "context" "errors" "fmt" "math/rand" "reflect" "runtime" "strconv" "sync" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "go.uber.org/yarpc/yarpcerrors" "golang.org/x/exp/maps" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) func TestIsServiceTransientError(t *testing.T) { for name, c := range map[string]struct { err error want bool }{ "ContextTimeout": { err: context.DeadlineExceeded, want: false, }, "YARPCCanceled": { err: yarpcerrors.CancelledErrorf("connection closing"), want: true, }, "YARPCDeadlineExceeded": { err: yarpcerrors.DeadlineExceededErrorf("yarpc deadline exceeded"), want: false, }, "YARPCUnavailable": { err: yarpcerrors.UnavailableErrorf("yarpc unavailable"), want: true, }, "YARPCUnavailable wrapped": { err: fmt.Errorf("wrapped err: %w", yarpcerrors.UnavailableErrorf("yarpc unavailable")), want: true, }, "YARPCUnknown": { err: yarpcerrors.UnknownErrorf("yarpc unknown"), want: true, }, "YARPCInternal": { err: yarpcerrors.InternalErrorf("yarpc internal"), want: true, }, "ContextCancel": { err: context.Canceled, want: false, }, "ServiceBusyError": { err: &types.ServiceBusyError{}, want: true, }, "ShardOwnershipLostError": { err: &types.ShardOwnershipLostError{}, want: true, }, } { t.Run(name, func(t *testing.T) { require.Equal(t, c.want, IsServiceTransientError(c.err)) }) } } func TestIsExpectedError(t *testing.T) { for name, c := range map[string]struct { err error want bool }{ "An error": { err: assert.AnError, want: false, }, "Transient error": { err: &types.ServiceBusyError{}, want: true, }, "Entity not exists error": { err: &types.EntityNotExistsError{}, want: true, }, "Already completed error": { err: &types.WorkflowExecutionAlreadyCompletedError{}, want: true, }, } { t.Run(name, func(t *testing.T) { require.Equal(t, c.want, IsExpectedError(c.err)) }) } } func TestFrontendRetry(t *testing.T) { tests := []struct { name string err error want bool }{ { name: "ServiceBusyError due to workflow id rate limiting", err: &types.ServiceBusyError{Reason: constants.WorkflowIDRateLimitReason}, want: false, }, { name: "ServiceBusyError not due to workflow id rate limiting", err: &types.ServiceBusyError{Reason: "some other reason"}, want: true, }, { name: "ServiceBusyError empty reason", err: &types.ServiceBusyError{Reason: ""}, want: true, }, { name: "Non-ServiceBusyError", err: errors.New("some random error"), want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { require.Equal(t, tt.want, FrontendRetry(tt.err)) }) } } func TestIsContextTimeoutError(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) defer cancel() time.Sleep(50 * time.Millisecond) require.True(t, IsContextTimeoutError(ctx.Err())) require.True(t, IsContextTimeoutError(&types.InternalServiceError{Message: ctx.Err().Error()})) yarpcErr := yarpcerrors.DeadlineExceededErrorf("yarpc deadline exceeded") require.True(t, IsContextTimeoutError(yarpcErr)) require.False(t, IsContextTimeoutError(errors.New("some random error"))) ctx, cancel = context.WithCancel(context.Background()) cancel() require.False(t, IsContextTimeoutError(ctx.Err())) } func TestCreateHistoryStartWorkflowRequest_ExpirationTimeWithCron(t *testing.T) { domainID := uuid.New() request := &types.StartWorkflowExecutionRequest{ RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 60, ExpirationIntervalInSeconds: 60, }, CronSchedule: "@every 300s", } now := time.Now() startRequest, err := CreateHistoryStartWorkflowRequest(domainID, request, now, nil) require.NoError(t, err) require.NotNil(t, startRequest) expirationTime := startRequest.GetExpirationTimestamp() require.NotNil(t, expirationTime) require.True(t, time.Unix(0, expirationTime).Sub(now) > 60*time.Second) } // Test to ensure we get the right value for FirstDecisionTaskBackoff during StartWorkflow request, // with & without cron, delayStart and jitterStart. // - Also see tests in cron_test.go for more exhaustive testing. func TestFirstDecisionTaskBackoffDuringStartWorkflow(t *testing.T) { firstRunTimestampPast, _ := time.Parse(time.RFC3339, "2017-12-17T08:00:00+00:00") firstRunTimestampFuture, _ := time.Parse(time.RFC3339, "2018-12-17T08:00:02+00:00") firstRunAtTimestampMap := map[string]int64{ "null": 0, "past": firstRunTimestampPast.UnixNano(), "future": firstRunTimestampFuture.UnixNano(), } var tests = []struct { cron bool jitterStartSeconds int32 delayStartSeconds int32 firstRunAtTimeCategory string }{ // Null first run at cases {true, 0, 0, "null"}, {true, 15, 0, "null"}, {true, 0, 600, "null"}, {true, 15, 600, "null"}, {false, 0, 0, "null"}, {false, 15, 0, "null"}, {false, 0, 600, "null"}, {false, 15, 600, "null"}, // Past first run at cases {true, 0, 0, "past"}, {true, 15, 0, "past"}, {true, 0, 600, "past"}, {true, 15, 600, "past"}, {false, 0, 0, "past"}, {false, 15, 0, "past"}, {false, 0, 600, "past"}, {false, 15, 600, "past"}, // Future first run at cases {true, 0, 0, "future"}, {true, 15, 0, "future"}, {true, 0, 600, "future"}, {true, 15, 600, "future"}, {false, 0, 0, "future"}, {false, 15, 0, "future"}, {false, 0, 600, "future"}, {false, 15, 600, "future"}, } rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) for idx, tt := range tests { t.Run(strconv.Itoa(idx), func(t *testing.T) { domainID := uuid.New() request := &types.StartWorkflowExecutionRequest{ FirstRunAtTimeStamp: Int64Ptr(firstRunAtTimestampMap[tt.firstRunAtTimeCategory]), DelayStartSeconds: Int32Ptr(tt.delayStartSeconds), JitterStartSeconds: Int32Ptr(tt.jitterStartSeconds), } if tt.cron { request.CronSchedule = "* * * * *" } // Run X loops so that the test isn't flaky, since jitter adds randomness. caseCount := 10 exactCount := 0 for i := 0; i < caseCount; i++ { // Start at the minute boundary so we know what the backoff should be startTime, _ := time.Parse(time.RFC3339, "2018-12-17T08:00:00+00:00") startRequest, err := CreateHistoryStartWorkflowRequest(domainID, request, startTime, nil) require.NoError(t, err) require.NotNil(t, startRequest) backoffSeconds := startRequest.GetFirstDecisionTaskBackoffSeconds() expectedWithoutJitter := tt.delayStartSeconds if tt.cron { expectedWithoutJitter += 60 } expectedMax := expectedWithoutJitter + tt.jitterStartSeconds if backoffSeconds == expectedWithoutJitter { exactCount++ } if tt.firstRunAtTimeCategory == "future" { require.Equal(t, int32(2), backoffSeconds, "test specs = %v", tt) } else { if tt.jitterStartSeconds == 0 { require.Equal(t, expectedWithoutJitter, backoffSeconds, "test specs = %v", tt) } else { require.True(t, backoffSeconds >= expectedWithoutJitter && backoffSeconds <= expectedMax, "test specs = %v, backoff (%v) should be >= %v and <= %v", tt, backoffSeconds, expectedWithoutJitter, expectedMax) } } } // If firstRunTimestamp is either null or past ONLY // If jitter is > 0, we want to detect whether jitter is being applied - BUT we don't want the test // to be flaky if the code randomly chooses a jitter of 0, so we try to have enough data points by // checking the next X cron times AND by choosing a jitter thats not super low. if tt.firstRunAtTimeCategory != "future" { if tt.jitterStartSeconds > 0 && exactCount == caseCount { // Test to make sure a jitter test case sometimes doesn't get exact values t.Fatalf("FAILED to jitter properly? Test specs = %v\n", tt) } else if tt.jitterStartSeconds == 0 && exactCount != caseCount { // Test to make sure a non-jitter test case always gets exact values t.Fatalf("Jittered when we weren't supposed to? Test specs = %v\n", tt) } } }) } } func TestCreateHistoryStartWorkflowRequest(t *testing.T) { var tests = []struct { delayStartSeconds int cronSeconds int jitterStartSeconds int }{ {0, 0, 0}, {100, 0, 0}, {100, 300, 0}, {0, 0, 2000}, {100, 0, 2000}, {0, 300, 2000}, {100, 300, 2000}, {0, 300, 2000}, {0, 300, 2000}, } for idx, tt := range tests { t.Run(strconv.Itoa(idx), func(t *testing.T) { testExpirationTime(t, tt.delayStartSeconds, tt.cronSeconds, tt.jitterStartSeconds) }) } } func testExpirationTime(t *testing.T, delayStartSeconds int, cronSeconds int, jitterSeconds int) { domainID := uuid.New() request := &types.StartWorkflowExecutionRequest{ RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 60, ExpirationIntervalInSeconds: 60, }, DelayStartSeconds: Int32Ptr(int32(delayStartSeconds)), JitterStartSeconds: Int32Ptr(int32(jitterSeconds)), } if cronSeconds > 0 { request.CronSchedule = fmt.Sprintf("@every %ds", cronSeconds) } minDelay := delayStartSeconds + cronSeconds maxDelay := delayStartSeconds + 2*cronSeconds + jitterSeconds now := time.Now() startRequest, err := CreateHistoryStartWorkflowRequest(domainID, request, now, nil) require.NoError(t, err) require.NotNil(t, startRequest) expirationTime := startRequest.GetExpirationTimestamp() require.NotNil(t, expirationTime) // Since we assign the expiration time after we create the workflow request, // There's a chance that the test thread might sleep or get deprioritized and // expirationTime - now may not be equal to DelayStartSeconds. Adding 2 seconds // buffer to avoid this test being flaky require.True( t, time.Unix(0, expirationTime).Sub(now) >= (time.Duration(minDelay)+58)*time.Second, "Integration test took too short: %f seconds vs %f seconds", time.Unix(0, expirationTime).Sub(now).Round(time.Millisecond).Seconds(), ((time.Duration(minDelay) + 58) * time.Second).Round(time.Millisecond).Seconds(), ) require.True( t, time.Unix(0, expirationTime).Sub(now) < (time.Duration(maxDelay)+68)*time.Second, "Integration test took too long: %f seconds vs %f seconds", time.Unix(0, expirationTime).Sub(now).Round(time.Millisecond).Seconds(), ((time.Duration(minDelay) + 68) * time.Second).Round(time.Millisecond).Seconds(), ) } func TestCreateHistoryStartWorkflowRequest_ExpirationTimeWithoutCron(t *testing.T) { domainID := uuid.New() request := &types.StartWorkflowExecutionRequest{ RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 60, ExpirationIntervalInSeconds: 60, }, } now := time.Now() startRequest, err := CreateHistoryStartWorkflowRequest(domainID, request, now, nil) require.NoError(t, err) require.NotNil(t, startRequest) expirationTime := startRequest.GetExpirationTimestamp() require.NotNil(t, expirationTime) delta := time.Unix(0, expirationTime).Sub(now) require.True(t, delta > 58*time.Second) require.True(t, delta < 62*time.Second) } func TestConvertIndexedValueTypeToInternalType(t *testing.T) { values := []types.IndexedValueType{types.IndexedValueTypeString, types.IndexedValueTypeKeyword, types.IndexedValueTypeInt, types.IndexedValueTypeDouble, types.IndexedValueTypeBool, types.IndexedValueTypeDatetime} for _, expected := range values { require.Equal(t, expected, ConvertIndexedValueTypeToInternalType(int(expected), nil)) require.Equal(t, expected, ConvertIndexedValueTypeToInternalType(float64(expected), nil)) buffer, err := expected.MarshalText() require.NoError(t, err) require.Equal(t, expected, ConvertIndexedValueTypeToInternalType(buffer, nil)) require.Equal(t, expected, ConvertIndexedValueTypeToInternalType(string(buffer), nil)) } } func TestValidateDomainUUID(t *testing.T) { testCases := []struct { msg string domainUUID string valid bool }{ { msg: "empty", domainUUID: "", valid: false, }, { msg: "invalid", domainUUID: "some random uuid", valid: false, }, { msg: "valid", domainUUID: uuid.New(), valid: true, }, } for _, tc := range testCases { t.Run(tc.msg, func(t *testing.T) { err := ValidateDomainUUID(tc.domainUUID) if tc.valid { require.NoError(t, err) } else { require.Error(t, err) } }) } } func TestConvertErrToGetTaskFailedCause(t *testing.T) { testCases := []struct { err error expectedFailedCause types.GetTaskFailedCause }{ { err: errors.New("some random error"), expectedFailedCause: types.GetTaskFailedCauseUncategorized, }, { err: context.DeadlineExceeded, expectedFailedCause: types.GetTaskFailedCauseTimeout, }, { err: &types.ServiceBusyError{}, expectedFailedCause: types.GetTaskFailedCauseServiceBusy, }, { err: &types.ShardOwnershipLostError{}, expectedFailedCause: types.GetTaskFailedCauseShardOwnershipLost, }, } for _, tc := range testCases { require.Equal(t, tc.expectedFailedCause, ConvertErrToGetTaskFailedCause(tc.err)) } } func TestToServiceTransientError(t *testing.T) { t.Run("it converts nil", func(t *testing.T) { assert.NoError(t, ToServiceTransientError(nil)) }) t.Run("it keeps transient errors", func(t *testing.T) { err := &types.InternalServiceError{} assert.Equal(t, err, ToServiceTransientError(err)) assert.True(t, IsServiceTransientError(ToServiceTransientError(err))) }) t.Run("it converts errors to transient errors", func(t *testing.T) { err := fmt.Errorf("error") assert.True(t, IsServiceTransientError(ToServiceTransientError(err))) }) } func TestIntersectionStringSlice(t *testing.T) { t.Run("it returns all items", func(t *testing.T) { a := []string{"a", "b", "c"} b := []string{"a", "b", "c"} c := IntersectionStringSlice(a, b) assert.ElementsMatch(t, []string{"a", "b", "c"}, c) }) t.Run("it returns no item", func(t *testing.T) { a := []string{"a", "b", "c"} b := []string{"d", "e", "f"} c := IntersectionStringSlice(a, b) assert.ElementsMatch(t, []string{}, c) }) t.Run("it returns intersection", func(t *testing.T) { a := []string{"a", "b", "c"} b := []string{"c", "b", "f"} c := IntersectionStringSlice(a, b) assert.ElementsMatch(t, []string{"c", "b"}, c) }) } func TestAwaitWaitGroup(t *testing.T) { t.Run("wait group done before timeout", func(t *testing.T) { var wg sync.WaitGroup wg.Add(1) wg.Done() got := AwaitWaitGroup(&wg, time.Second) require.True(t, got) }) t.Run("wait group done after timeout", func(t *testing.T) { var ( wg sync.WaitGroup doneC = make(chan struct{}) ) wg.Add(1) go func() { <-doneC wg.Done() }() got := AwaitWaitGroup(&wg, time.Microsecond) require.False(t, got) doneC <- struct{}{} close(doneC) }) } func TestIsValidIDLength(t *testing.T) { var ( // test setup scope = metrics.NoopScope // arguments metricCounter = metrics.MetricIdx(0) idTypeViolationTag = tag.ClusterName("idTypeViolationTag") domainName = "domain_name" id = "12345" ) mockWarnCall := func(logger *log.MockLogger) { logger.EXPECT().Warn( "ID length exceeds limit.", []tag.Tag{ tag.WorkflowDomainName(domainName), tag.Name(id), idTypeViolationTag, }, ).Times(1) } t.Run("valid id length, no warnings", func(t *testing.T) { logger := log.NewMockLogger(gomock.NewController(t)) got := IsValidIDLength(id, scope, 7, 10, metricCounter, domainName, logger, idTypeViolationTag) require.True(t, got, "expected true, because id length is 5 and it's less than error limit 10") }) t.Run("valid id length, with warnings", func(t *testing.T) { logger := log.NewMockLogger(gomock.NewController(t)) mockWarnCall(logger) got := IsValidIDLength(id, scope, 4, 10, metricCounter, domainName, logger, idTypeViolationTag) require.True(t, got, "expected true, because id length is 5 and it's less than error limit 10") }) t.Run("non valid id length", func(t *testing.T) { logger := log.NewMockLogger(gomock.NewController(t)) mockWarnCall(logger) got := IsValidIDLength(id, scope, 1, 4, metricCounter, domainName, logger, idTypeViolationTag) require.False(t, got, "expected false, because id length is 5 and it's more than error limit 4") }) } func TestIsEntityNotExistsError(t *testing.T) { t.Run("is entity not exists error", func(t *testing.T) { err := &types.EntityNotExistsError{} require.True(t, IsEntityNotExistsError(err), "expected true, because err is entity not exists error") }) t.Run("is not entity not exists error", func(t *testing.T) { err := fmt.Errorf("generic error") require.False(t, IsEntityNotExistsError(err), "expected false, because err is a generic error") }) } func TestCreateXXXRetryPolicyWithSetExpirationInterval(t *testing.T) { for name, c := range map[string]struct { createFn func() backoff.RetryPolicy wantInitialInterval time.Duration wantMaximumInterval time.Duration wantSetExpirationInterval time.Duration }{ "CreatePersistenceRetryPolicy": { createFn: CreatePersistenceRetryPolicy, wantInitialInterval: retryPersistenceOperationInitialInterval, wantMaximumInterval: retryPersistenceOperationMaxInterval, wantSetExpirationInterval: retryPersistenceOperationExpirationInterval, }, "CreateHistoryServiceRetryPolicy": { createFn: CreateHistoryServiceRetryPolicy, wantInitialInterval: historyServiceOperationInitialInterval, wantMaximumInterval: historyServiceOperationMaxInterval, wantSetExpirationInterval: historyServiceOperationExpirationInterval, }, "CreateMatchingServiceRetryPolicy": { createFn: CreateMatchingServiceRetryPolicy, wantInitialInterval: matchingServiceOperationInitialInterval, wantMaximumInterval: matchingServiceOperationMaxInterval, wantSetExpirationInterval: matchingServiceOperationExpirationInterval, }, "CreateFrontendServiceRetryPolicy": { createFn: CreateFrontendServiceRetryPolicy, wantInitialInterval: frontendServiceOperationInitialInterval, wantMaximumInterval: frontendServiceOperationMaxInterval, wantSetExpirationInterval: frontendServiceOperationExpirationInterval, }, "CreateAdminServiceRetryPolicy": { createFn: CreateAdminServiceRetryPolicy, wantInitialInterval: adminServiceOperationInitialInterval, wantMaximumInterval: adminServiceOperationMaxInterval, wantSetExpirationInterval: adminServiceOperationExpirationInterval, }, "CreateReplicationServiceBusyRetryPolicy": { createFn: CreateReplicationServiceBusyRetryPolicy, wantInitialInterval: replicationServiceBusyInitialInterval, wantMaximumInterval: replicationServiceBusyMaxInterval, wantSetExpirationInterval: replicationServiceBusyExpirationInterval, }, "CreateShardDistributorServiceRetryPolicy": { createFn: CreateShardDistributorServiceRetryPolicy, wantInitialInterval: shardDistributorServiceOperationInitialInterval, wantMaximumInterval: shardDistributorServiceOperationMaxInterval, wantSetExpirationInterval: shardDistributorServiceOperationExpirationInterval, }, } { t.Run(name, func(t *testing.T) { want := backoff.NewExponentialRetryPolicy(c.wantInitialInterval) want.SetMaximumInterval(c.wantMaximumInterval) want.SetExpirationInterval(c.wantSetExpirationInterval) got := c.createFn() require.Equal(t, want, got) }) } } func TestCreateXXXRetryPolicyWithMaximumAttempts(t *testing.T) { for name, c := range map[string]struct { createFn func() backoff.RetryPolicy wantInitialInterval time.Duration wantMaximumInterval time.Duration wantMaximumAttempts int }{ "CreateDlqPublishRetryPolicy": { createFn: CreateDlqPublishRetryPolicy, wantInitialInterval: retryKafkaOperationInitialInterval, wantMaximumInterval: retryKafkaOperationMaxInterval, wantMaximumAttempts: retryKafkaOperationMaxAttempts, }, "CreateTaskProcessingRetryPolicy": { createFn: CreateTaskProcessingRetryPolicy, wantInitialInterval: retryTaskProcessingInitialInterval, wantMaximumInterval: retryTaskProcessingMaxInterval, wantMaximumAttempts: retryTaskProcessingMaxAttempts, }, } { t.Run(name, func(t *testing.T) { want := backoff.NewExponentialRetryPolicy(c.wantInitialInterval) want.SetMaximumInterval(c.wantMaximumInterval) want.SetMaximumAttempts(c.wantMaximumAttempts) got := c.createFn() require.Equal(t, want, got) }) } } func TestValidateRetryPolicy_Success(t *testing.T) { for name, policy := range map[string]*types.RetryPolicy{ "nil policy": nil, "MaximumAttempts is no zero": { InitialIntervalInSeconds: 2, BackoffCoefficient: 1, MaximumIntervalInSeconds: 0, MaximumAttempts: 1, ExpirationIntervalInSeconds: 0, }, "ExpirationIntervalInSeconds is no zero": { InitialIntervalInSeconds: 2, BackoffCoefficient: 1, MaximumIntervalInSeconds: 0, MaximumAttempts: 0, ExpirationIntervalInSeconds: 1, }, "MaximumIntervalInSeconds is greater than InitialIntervalInSeconds": { InitialIntervalInSeconds: 2, BackoffCoefficient: 1, MaximumIntervalInSeconds: 0, MaximumAttempts: 0, ExpirationIntervalInSeconds: 1, }, "MaximumIntervalInSeconds equals InitialIntervalInSeconds": { InitialIntervalInSeconds: 2, BackoffCoefficient: 1, MaximumIntervalInSeconds: 2, MaximumAttempts: 0, ExpirationIntervalInSeconds: 1, }, } { t.Run(name, func(t *testing.T) { require.NoError(t, ValidateRetryPolicy(policy)) }) } } func TestValidateRetryPolicy_Error(t *testing.T) { for name, c := range map[string]struct { policy *types.RetryPolicy wantErr *types.BadRequestError }{ "InitialIntervalInSeconds equals 0": { policy: &types.RetryPolicy{ InitialIntervalInSeconds: 0, }, wantErr: &types.BadRequestError{Message: "InitialIntervalInSeconds must be greater than 0 on retry policy."}, }, "InitialIntervalInSeconds less than 0": { policy: &types.RetryPolicy{ InitialIntervalInSeconds: -1, }, wantErr: &types.BadRequestError{Message: "InitialIntervalInSeconds must be greater than 0 on retry policy."}, }, "BackoffCoefficient equals 0": { policy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 0, }, wantErr: &types.BadRequestError{Message: "BackoffCoefficient cannot be less than 1 on retry policy."}, }, "MaximumIntervalInSeconds equals -1": { policy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1, MaximumIntervalInSeconds: -1, }, wantErr: &types.BadRequestError{Message: "MaximumIntervalInSeconds cannot be less than 0 on retry policy."}, }, "MaximumIntervalInSeconds equals 1 and less than InitialIntervalInSeconds": { policy: &types.RetryPolicy{ InitialIntervalInSeconds: 2, BackoffCoefficient: 1, MaximumIntervalInSeconds: 1, }, wantErr: &types.BadRequestError{Message: "MaximumIntervalInSeconds cannot be less than InitialIntervalInSeconds on retry policy."}, }, "MaximumAttempts equals -1": { policy: &types.RetryPolicy{ InitialIntervalInSeconds: 2, BackoffCoefficient: 1, MaximumIntervalInSeconds: 0, MaximumAttempts: -1, }, wantErr: &types.BadRequestError{Message: "MaximumAttempts cannot be less than 0 on retry policy."}, }, "ExpirationIntervalInSeconds equals -1": { policy: &types.RetryPolicy{ InitialIntervalInSeconds: 2, BackoffCoefficient: 1, MaximumIntervalInSeconds: 0, MaximumAttempts: 0, ExpirationIntervalInSeconds: -1, }, wantErr: &types.BadRequestError{Message: "ExpirationIntervalInSeconds cannot be less than 0 on retry policy."}, }, "MaximumAttempts and ExpirationIntervalInSeconds equal 0": { policy: &types.RetryPolicy{ InitialIntervalInSeconds: 2, BackoffCoefficient: 1, MaximumIntervalInSeconds: 0, MaximumAttempts: 0, ExpirationIntervalInSeconds: 0, }, wantErr: &types.BadRequestError{Message: "MaximumAttempts and ExpirationIntervalInSeconds are both 0. At least one of them must be specified."}, }, } { t.Run(name, func(t *testing.T) { got := ValidateRetryPolicy(c.policy) require.Error(t, got) require.ErrorContains(t, got, c.wantErr.Message) }) } } func TestConvertGetTaskFailedCauseToErr(t *testing.T) { for cause, wantErr := range map[types.GetTaskFailedCause]error{ types.GetTaskFailedCauseServiceBusy: &types.ServiceBusyError{}, types.GetTaskFailedCauseTimeout: context.DeadlineExceeded, types.GetTaskFailedCauseShardOwnershipLost: &types.ShardOwnershipLostError{}, types.GetTaskFailedCauseUncategorized: &types.InternalServiceError{Message: "uncategorized error"}, } { t.Run(cause.String(), func(t *testing.T) { gotErr := ConvertGetTaskFailedCauseToErr(cause) require.Equal(t, wantErr, gotErr) }) } } func TestWorkflowIDToHistoryShard(t *testing.T) { for _, c := range []struct { workflowID string numberOfShards int want int }{ { workflowID: "", numberOfShards: 1000, want: 242, }, { workflowID: "workflowId", numberOfShards: 1000, want: 580, }, } { t.Run(fmt.Sprintf("%s-%v", c.workflowID, c.numberOfShards), func(t *testing.T) { got := WorkflowIDToHistoryShard(c.workflowID, c.numberOfShards) require.Equal(t, c.want, got) }) } } func TestDomainIDToHistoryShard(t *testing.T) { for _, c := range []struct { domainID string numberOfShards int want int }{ { domainID: "", numberOfShards: 1000, want: 242, }, { domainID: "domainId", numberOfShards: 1000, want: 600, }, } { t.Run(fmt.Sprintf("%s-%v", c.domainID, c.numberOfShards), func(t *testing.T) { got := DomainIDToHistoryShard(c.domainID, c.numberOfShards) require.Equal(t, c.want, got) }) } } func TestGenerateRandomString(t *testing.T) { for input, wantSize := range map[int]int{ -1: 0, 0: 0, 10: 10, } { t.Run(fmt.Sprintf("%d", input), func(t *testing.T) { got := GenerateRandomString(input) require.Len(t, got, wantSize) }) } } func TestIsValidContext(t *testing.T) { t.Run("background context", func(t *testing.T) { require.NoError(t, IsValidContext(context.Background())) }) t.Run("canceled context", func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() got := IsValidContext(ctx) require.Error(t, got) require.ErrorIs(t, got, context.Canceled) }) t.Run("deadline exceeded context", func(t *testing.T) { ctx, _ := context.WithTimeout(context.Background(), -time.Second) got := IsValidContext(ctx) require.Error(t, got) require.ErrorIs(t, got, context.DeadlineExceeded) }) t.Run("context with deadline exceeded contextExpireThreshold", func(t *testing.T) { ctx, _ := context.WithTimeout(context.Background(), contextExpireThreshold/2) got := IsValidContext(ctx) require.Error(t, got) require.ErrorIs(t, got, context.DeadlineExceeded, "context.DeadlineExceeded should be returned, because context timeout is not later than now + contextExpireThreshold") }) t.Run("valid context", func(t *testing.T) { ctx, _ := context.WithTimeout(context.Background(), contextExpireThreshold*2) require.NoError(t, IsValidContext(ctx), "nil should be returned, because context timeout is later than now + contextExpireThreshold") }) } func TestCreateChildContext(t *testing.T) { t.Run("nil parent", func(t *testing.T) { gotCtx, gotFunc := CreateChildContext(nil, 0) require.Nil(t, gotCtx) require.Equal(t, funcName(emptyCancelFunc), funcName(gotFunc)) }) t.Run("canceled parent", func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() gotCtx, gotFunc := CreateChildContext(ctx, 0) require.Equal(t, ctx, gotCtx) require.Equal(t, funcName(emptyCancelFunc), funcName(gotFunc)) }) t.Run("non-canceled parent without deadline", func(t *testing.T) { ctx, _ := context.WithCancel(context.Background()) gotCtx, gotFunc := CreateChildContext(ctx, 0) require.Equal(t, ctx, gotCtx) require.Equal(t, funcName(emptyCancelFunc), funcName(gotFunc)) }) t.Run("context with deadline exceeded", func(t *testing.T) { ctx, _ := context.WithTimeout(context.Background(), -time.Second) gotCtx, gotFunc := CreateChildContext(ctx, 0) require.Equal(t, ctx, gotCtx) require.Equal(t, funcName(emptyCancelFunc), funcName(gotFunc)) }) t.Run("tailroom is less or equal to 0", func(t *testing.T) { testCase := func(t *testing.T, tailroom float64) { deadline := time.Now().Add(time.Hour) ctx, _ := context.WithDeadline(context.Background(), deadline) gotCtx, gotFunc := CreateChildContext(ctx, tailroom) gotDeadline, ok := gotCtx.Deadline() require.True(t, ok) require.Equal(t, deadline, gotDeadline, "deadline should be equal to parent deadline") require.NotEqual(t, ctx, gotCtx) require.NotEqual(t, funcName(emptyCancelFunc), funcName(gotFunc)) } t.Run("0", func(t *testing.T) { testCase(t, 0) }) t.Run("-1", func(t *testing.T) { testCase(t, -1) }) }) t.Run("tailroom is greater or equal to 1", func(t *testing.T) { testCase := func(t *testing.T, tailroom float64) { deadline := time.Now().Add(time.Hour) ctx, _ := context.WithDeadline(context.Background(), deadline) // we can't mock time.Now, but we know that the deadline should be in // range between the start and finish of function's execution beforeNow := time.Now() gotCtx, gotFunc := CreateChildContext(ctx, tailroom) afterNow := time.Now() gotDeadline, ok := gotCtx.Deadline() require.True(t, ok) require.NotEqual(t, deadline, gotDeadline) require.Less(t, gotDeadline, deadline) // gotDeadline should be between beforeNow and afterNow (exclusive) require.GreaterOrEqual(t, afterNow, gotDeadline) require.LessOrEqual(t, beforeNow, gotDeadline) require.NotEqual(t, ctx, gotCtx) require.NotEqual(t, funcName(emptyCancelFunc), funcName(gotFunc)) } t.Run("1", func(t *testing.T) { testCase(t, 1) }) t.Run("2", func(t *testing.T) { testCase(t, 2) }) }) t.Run("tailroom is 0.5", func(t *testing.T) { now := time.Now() deadline := now.Add(time.Hour) ctx, _ := context.WithDeadline(context.Background(), deadline) gotCtx, gotFunc := CreateChildContext(ctx, 0.5) gotDeadline, ok := gotCtx.Deadline() require.True(t, ok) require.NotEqual(t, deadline, gotDeadline) require.Less(t, gotDeadline, deadline) // we can't mock time.Now, so we assume that the deadline should be // in range 29:59 and 30:01 minutes after start minDeadline := now.Add(30*time.Minute - time.Second) maxDeadline := now.Add(30*time.Minute + time.Second) // gotDeadline should be between minDeadline and maxDeadline (exclusive) require.GreaterOrEqual(t, maxDeadline, gotDeadline) require.LessOrEqual(t, minDeadline, gotDeadline) require.NotEqual(t, ctx, gotCtx) require.NotEqual(t, funcName(emptyCancelFunc), funcName(gotFunc)) }) } // funcName returns the name of the function func funcName(fn any) string { return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() } func TestDeserializeSearchAttributeValue_Success(t *testing.T) { for name, c := range map[string]struct { value string valueType types.IndexedValueType wantValue any }{ "string": { value: `"string"`, valueType: types.IndexedValueTypeString, wantValue: "string", }, "[]string": { value: `["1", "2", "3"]`, valueType: types.IndexedValueTypeString, wantValue: []string{"1", "2", "3"}, }, "keyword": { value: `"keyword"`, valueType: types.IndexedValueTypeKeyword, wantValue: "keyword", }, "[]keyword": { value: `["1", "2", "3"]`, valueType: types.IndexedValueTypeKeyword, wantValue: []string{"1", "2", "3"}, }, "int": { value: `1`, valueType: types.IndexedValueTypeInt, wantValue: int64(1), }, "[]int": { value: `[1, 2, 3]`, valueType: types.IndexedValueTypeInt, wantValue: []int64{1, 2, 3}, }, "double": { value: `1.1`, valueType: types.IndexedValueTypeDouble, wantValue: 1.1, }, "[]double": { value: `[1.1, 2.2, 3.3]`, valueType: types.IndexedValueTypeDouble, wantValue: []float64{1.1, 2.2, 3.3}, }, "bool": { value: `true`, valueType: types.IndexedValueTypeBool, wantValue: true, }, "[]bool": { value: `[true, false, true]`, valueType: types.IndexedValueTypeBool, wantValue: []bool{true, false, true}, }, "datetime": { value: `"2020-01-01T00:00:00Z"`, valueType: types.IndexedValueTypeDatetime, wantValue: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), }, "[]datetime": { value: `["2020-01-01T00:00:00Z", "2020-01-02T00:00:00Z", "2020-01-03T00:00:00Z"]`, valueType: types.IndexedValueTypeDatetime, wantValue: []time.Time{ time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC), time.Date(2020, 1, 3, 0, 0, 0, 0, time.UTC), }, }, } { t.Run(name, func(t *testing.T) { gotValue, err := DeserializeSearchAttributeValue([]byte(c.value), c.valueType) require.NoError(t, err) require.Equal(t, c.wantValue, gotValue) }) } } func TestDeserializeSearchAttributeValue_Error(t *testing.T) { for name, c := range map[string]struct { value string valueType types.IndexedValueType wantErrorMsg string }{ "invalid string": { value: `"string`, valueType: types.IndexedValueTypeString, wantErrorMsg: "unexpected end of JSON input", }, "invalid keyword": { value: `"keyword`, valueType: types.IndexedValueTypeKeyword, wantErrorMsg: "unexpected end of JSON input", }, "invalid int": { value: `1.1`, valueType: types.IndexedValueTypeInt, wantErrorMsg: "json: cannot unmarshal number into Go value of type []int64", }, "invalid double": { value: `1as`, valueType: types.IndexedValueTypeDouble, wantErrorMsg: "invalid character 'a' after top-level value", }, "invalid bool": { value: `1`, valueType: types.IndexedValueTypeBool, wantErrorMsg: "json: cannot unmarshal number into Go value of type []bool", }, "invalid datetime": { value: `1`, valueType: types.IndexedValueTypeDatetime, wantErrorMsg: "json: cannot unmarshal number into Go value of type []time.Time", }, "invalid value type": { value: `1`, valueType: types.IndexedValueType(100), wantErrorMsg: fmt.Sprintf("error: unknown index value type [%v]", types.IndexedValueType(100)), }, } { t.Run(name, func(t *testing.T) { gotValue, err := DeserializeSearchAttributeValue([]byte(c.value), c.valueType) require.Error(t, err) require.ErrorContains(t, err, c.wantErrorMsg) require.Nil(t, gotValue) }) } } // someMapStringToByteArraySize is the size of someMapStringToByteArray calculated by GetSizeOfMapStringToByteArray const someMapStringToByteArraySize = 66 var someMapStringToByteArray = map[string][]byte{ "key": []byte("value"), "key2": []byte("value2"), } // someBytesArraySize is len(someBytesArray) const someBytesArraySize = 17 var someBytesArray = []byte("some random bytes") func TestGetSizeOfMapStringToByteArray(t *testing.T) { require.Equal(t, someMapStringToByteArraySize, GetSizeOfMapStringToByteArray(someMapStringToByteArray)) } func TestGetSizeOfHistoryEvent_NilEvent(t *testing.T) { require.Equal(t, uint64(0), GetSizeOfHistoryEvent(nil)) require.Equal(t, uint64(0), GetSizeOfHistoryEvent(&types.HistoryEvent{}), "should be zero, because eventType is nil") } func TestGetSizeOfHistoryEvent(t *testing.T) { for eventType, c := range map[types.EventType]struct { event *types.HistoryEvent want uint64 }{ types.EventTypeWorkflowExecutionStarted: { event: &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ Input: someBytesArray, // 17 bytes ContinuedFailureDetails: someBytesArray, // 17 bytes LastCompletionResult: someBytesArray, // 17 bytes Memo: &types.Memo{ Fields: someMapStringToByteArray, // 66 bytes }, Header: &types.Header{ Fields: someMapStringToByteArray, // 66 bytes }, SearchAttributes: &types.SearchAttributes{ IndexedFields: someMapStringToByteArray, // 66 bytes }, }, }, want: 3*someBytesArraySize + 3*someMapStringToByteArraySize, }, types.EventTypeWorkflowExecutionCompleted: { event: &types.HistoryEvent{ WorkflowExecutionCompletedEventAttributes: &types.WorkflowExecutionCompletedEventAttributes{ Result: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeWorkflowExecutionFailed: { event: &types.HistoryEvent{ WorkflowExecutionFailedEventAttributes: &types.WorkflowExecutionFailedEventAttributes{ Details: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeWorkflowExecutionTimedOut: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeDecisionTaskScheduled: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeDecisionTaskStarted: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeDecisionTaskCompleted: { event: &types.HistoryEvent{ DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ExecutionContext: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeDecisionTaskTimedOut: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeDecisionTaskFailed: { event: &types.HistoryEvent{ DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ Details: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeActivityTaskScheduled: { event: &types.HistoryEvent{ ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ Input: someBytesArray, // 17 bytes Header: &types.Header{ Fields: someMapStringToByteArray, // 66 bytes }, }, }, want: someBytesArraySize + someMapStringToByteArraySize, }, types.EventTypeActivityTaskStarted: { event: &types.HistoryEvent{ ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ LastFailureDetails: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeActivityTaskCompleted: { event: &types.HistoryEvent{ ActivityTaskCompletedEventAttributes: &types.ActivityTaskCompletedEventAttributes{ Result: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeActivityTaskFailed: { event: &types.HistoryEvent{ ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ Details: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeActivityTaskTimedOut: { event: &types.HistoryEvent{ ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ Details: someBytesArray, // 17 bytes LastFailureDetails: someBytesArray, // 17 bytes }, }, want: 2 * someBytesArraySize, }, types.EventTypeActivityTaskCancelRequested: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeRequestCancelActivityTaskFailed: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeActivityTaskCanceled: { event: &types.HistoryEvent{ ActivityTaskCanceledEventAttributes: &types.ActivityTaskCanceledEventAttributes{ Details: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeTimerStarted: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeTimerFired: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeCancelTimerFailed: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeTimerCanceled: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeWorkflowExecutionCancelRequested: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeWorkflowExecutionCanceled: { event: &types.HistoryEvent{ WorkflowExecutionCanceledEventAttributes: &types.WorkflowExecutionCanceledEventAttributes{ Details: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeRequestCancelExternalWorkflowExecutionInitiated: { event: &types.HistoryEvent{ RequestCancelExternalWorkflowExecutionInitiatedEventAttributes: &types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ Control: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeRequestCancelExternalWorkflowExecutionFailed: { event: &types.HistoryEvent{ RequestCancelExternalWorkflowExecutionFailedEventAttributes: &types.RequestCancelExternalWorkflowExecutionFailedEventAttributes{ Control: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeExternalWorkflowExecutionCancelRequested: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeMarkerRecorded: { event: &types.HistoryEvent{ MarkerRecordedEventAttributes: &types.MarkerRecordedEventAttributes{ Details: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeWorkflowExecutionSignaled: { event: &types.HistoryEvent{ WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ Input: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeWorkflowExecutionTerminated: { event: &types.HistoryEvent{ WorkflowExecutionTerminatedEventAttributes: &types.WorkflowExecutionTerminatedEventAttributes{ Details: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeWorkflowExecutionContinuedAsNew: { event: &types.HistoryEvent{ WorkflowExecutionContinuedAsNewEventAttributes: &types.WorkflowExecutionContinuedAsNewEventAttributes{ Input: someBytesArray, // 17 bytes Memo: &types.Memo{ Fields: someMapStringToByteArray, // 66 bytes }, Header: &types.Header{ Fields: someMapStringToByteArray, // 66 bytes }, SearchAttributes: &types.SearchAttributes{ IndexedFields: someMapStringToByteArray, // 66 bytes }, }, }, want: someBytesArraySize + 3*someMapStringToByteArraySize, }, types.EventTypeStartChildWorkflowExecutionInitiated: { event: &types.HistoryEvent{ StartChildWorkflowExecutionInitiatedEventAttributes: &types.StartChildWorkflowExecutionInitiatedEventAttributes{ Input: someBytesArray, // 17 bytes Control: someBytesArray, // 17 bytes Memo: &types.Memo{ Fields: someMapStringToByteArray, // 66 bytes }, Header: &types.Header{ Fields: someMapStringToByteArray, // 66 bytes }, SearchAttributes: &types.SearchAttributes{ IndexedFields: someMapStringToByteArray, // 66 bytes }, }, }, want: 2*someBytesArraySize + 3*someMapStringToByteArraySize, }, types.EventTypeStartChildWorkflowExecutionFailed: { event: &types.HistoryEvent{ StartChildWorkflowExecutionFailedEventAttributes: &types.StartChildWorkflowExecutionFailedEventAttributes{ Control: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeChildWorkflowExecutionStarted: { event: &types.HistoryEvent{ ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ Header: &types.Header{ Fields: someMapStringToByteArray, // 66 bytes }, }, }, want: someMapStringToByteArraySize, }, types.EventTypeChildWorkflowExecutionCompleted: { event: &types.HistoryEvent{ ChildWorkflowExecutionCompletedEventAttributes: &types.ChildWorkflowExecutionCompletedEventAttributes{ Result: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeChildWorkflowExecutionFailed: { event: &types.HistoryEvent{ ChildWorkflowExecutionFailedEventAttributes: &types.ChildWorkflowExecutionFailedEventAttributes{ Details: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeChildWorkflowExecutionCanceled: { event: &types.HistoryEvent{ ChildWorkflowExecutionCanceledEventAttributes: &types.ChildWorkflowExecutionCanceledEventAttributes{ Details: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeChildWorkflowExecutionTimedOut: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeChildWorkflowExecutionTerminated: {event: &types.HistoryEvent{}, want: 0}, types.EventTypeSignalExternalWorkflowExecutionInitiated: { event: &types.HistoryEvent{ SignalExternalWorkflowExecutionInitiatedEventAttributes: &types.SignalExternalWorkflowExecutionInitiatedEventAttributes{ Input: someBytesArray, // 17 bytes Control: someBytesArray, // 17 bytes }, }, want: 2 * someBytesArraySize, }, types.EventTypeSignalExternalWorkflowExecutionFailed: { event: &types.HistoryEvent{ SignalExternalWorkflowExecutionFailedEventAttributes: &types.SignalExternalWorkflowExecutionFailedEventAttributes{ Control: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeExternalWorkflowExecutionSignaled: { event: &types.HistoryEvent{ ExternalWorkflowExecutionSignaledEventAttributes: &types.ExternalWorkflowExecutionSignaledEventAttributes{ Control: someBytesArray, // 17 bytes }, }, want: someBytesArraySize, }, types.EventTypeUpsertWorkflowSearchAttributes: { event: &types.HistoryEvent{ UpsertWorkflowSearchAttributesEventAttributes: &types.UpsertWorkflowSearchAttributesEventAttributes{ SearchAttributes: &types.SearchAttributes{ IndexedFields: someMapStringToByteArray, // 66 bytes }, }, }, want: someMapStringToByteArraySize, }, } { t.Run(eventType.String(), func(t *testing.T) { if c.event != nil { c.event.EventType = &eventType } got := GetSizeOfHistoryEvent(c.event) require.Equal(t, c.want, got) }) } } func TestIsAdvancedVisibilityWritingEnabled(t *testing.T) { for name, c := range map[string]struct { advancedVisibilityWritingMode string isAdvancedVisConfigExist bool want bool }{ "mode is someMode, config exist": { advancedVisibilityWritingMode: "someMode", isAdvancedVisConfigExist: true, want: true, }, "mode is someMode, config not exist": { advancedVisibilityWritingMode: "someMode", isAdvancedVisConfigExist: false, want: false, }, "mode is off, config exist": { advancedVisibilityWritingMode: "off", isAdvancedVisConfigExist: true, want: false, }, } { t.Run(name, func(t *testing.T) { got := IsAdvancedVisibilityWritingEnabled(c.advancedVisibilityWritingMode, c.isAdvancedVisConfigExist) require.Equal(t, c.want, got) }) } } func TestValidateLongPollContextTimeout(t *testing.T) { const handlerName = "testHandler" t.Run("context timeout is not set", func(t *testing.T) { logger := log.NewMockLogger(gomock.NewController(t)) logger.EXPECT().Error( "Context timeout not set for long poll API.", []tag.Tag{ tag.WorkflowHandlerName(handlerName), tag.Error(ErrContextTimeoutNotSet), }, ) ctx := context.Background() got := ValidateLongPollContextTimeout(ctx, handlerName, logger) require.Error(t, got) require.ErrorIs(t, got, ErrContextTimeoutNotSet) }) t.Run("context timeout is set, but less than MinLongPollTimeout", func(t *testing.T) { logger := log.NewMockLogger(gomock.NewController(t)) logger.EXPECT().Error( "Context timeout is too short for long poll API.", // we can't mock time between deadline and now, so we just check it as it is gomock.Any(), ) ctx, _ := context.WithTimeout(context.Background(), time.Second) got := ValidateLongPollContextTimeout(ctx, handlerName, logger) require.Error(t, got) require.ErrorIs(t, got, ErrContextTimeoutTooShort, "should return ErrContextTimeoutTooShort, because context timeout is less than MinLongPollTimeout") }) t.Run("context timeout is set, but less than CriticalLongPollTimeout", func(t *testing.T) { logger := log.NewMockLogger(gomock.NewController(t)) logger.EXPECT().Debug( "Context timeout is lower than critical value for long poll API.", // we can't mock time between deadline and now, so we just check it as it is gomock.Any(), ) ctx, _ := context.WithTimeout(context.Background(), 15*time.Second) got := ValidateLongPollContextTimeout(ctx, handlerName, logger) require.NoError(t, got) }) t.Run("context timeout is set, but greater than CriticalLongPollTimeout", func(t *testing.T) { logger := log.NewMockLogger(gomock.NewController(t)) ctx, _ := context.WithTimeout(context.Background(), 30*time.Second) got := ValidateLongPollContextTimeout(ctx, handlerName, logger) require.NoError(t, got) }) } func TestDurationToDays(t *testing.T) { for duration, want := range map[time.Duration]int32{ 0: 0, time.Hour: 0, 24 * time.Hour: 1, 25 * time.Hour: 1, 48 * time.Hour: 2, } { t.Run(duration.String(), func(t *testing.T) { got := DurationToDays(duration) require.Equal(t, want, got) }) } } func TestDurationToSeconds(t *testing.T) { for duration, want := range map[time.Duration]int64{ 0: 0, time.Second: 1, time.Second + time.Second/2: 1, 2 * time.Second: 2, } { t.Run(duration.String(), func(t *testing.T) { got := DurationToSeconds(duration) require.Equal(t, want, got) }) } } func TestDaysToDuration(t *testing.T) { for days, want := range map[int32]time.Duration{ 0: 0, 1: 24 * time.Hour, 2: 48 * time.Hour, } { t.Run(strconv.Itoa(int(days)), func(t *testing.T) { got := DaysToDuration(days) require.Equal(t, want, got) }) } } func TestSecondsToDuration(t *testing.T) { for seconds, want := range map[int64]time.Duration{ 0: 0, 1: time.Second, 2: 2 * time.Second, } { t.Run(strconv.Itoa(int(seconds)), func(t *testing.T) { got := SecondsToDuration(seconds) require.Equal(t, want, got) }) } } func TestNewPerTaskListScope(t *testing.T) { assert.NotNil(t, NewPerTaskListScope("test-domain", "test-tasklist", types.TaskListKindNormal, metrics.NewNoopMetricsClient(), 0)) assert.NotNil(t, NewPerTaskListScope("test-domain", "test-tasklist", types.TaskListKindSticky, metrics.NewNoopMetricsClient(), 0)) assert.NotNil(t, NewPerTaskListScope("test-domain", "test-tasklist", types.TaskListKindEphemeral, metrics.NewNoopMetricsClient(), 0)) } func TestCheckEventBlobSizeLimit(t *testing.T) { for name, c := range map[string]struct { blobSize int warnSize int errSize int wantErr error prepareLogger func(*log.MockLogger) assertMetrics func(tally.Snapshot) }{ "blob size is less than limit": { blobSize: 10, warnSize: 20, errSize: 30, wantErr: nil, }, "blob size is greater than warn limit": { blobSize: 21, warnSize: 20, errSize: 30, wantErr: nil, prepareLogger: func(logger *log.MockLogger) { logger.EXPECT().Warn("Blob size close to the limit.", gomock.Any()).Times(1) }, }, "blob size is greater than error limit": { blobSize: 31, warnSize: 20, errSize: 30, wantErr: ErrBlobSizeExceedsLimit, prepareLogger: func(logger *log.MockLogger) { logger.EXPECT().Error("Blob size exceeds limit.", gomock.Any()).Times(1) }, assertMetrics: func(snapshot tally.Snapshot) { counters := snapshot.Counters() assert.Len(t, counters, 1) values := maps.Values(counters) assert.Equal(t, "test.blob_size_exceed_limit", values[0].Name()) assert.Equal(t, int64(1), values[0].Value()) }, }, "error limit is less then warn limit": { blobSize: 21, warnSize: 30, errSize: 20, wantErr: ErrBlobSizeExceedsLimit, prepareLogger: func(logger *log.MockLogger) { logger.EXPECT().Warn("Error limit is less than warn limit.", gomock.Any()).Times(1) logger.EXPECT().Error("Blob size exceeds limit.", gomock.Any()).Times(1) }, }, } { t.Run(name, func(t *testing.T) { testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.History, metrics.HistogramMigration{}) logger := log.NewMockLogger(gomock.NewController(t)) if c.prepareLogger != nil { c.prepareLogger(logger) } const ( domainID = "testDomainID" domainName = "testDomainName" workflowID = "testWorkflowID" runID = "testRunID" ) got := CheckEventBlobSizeLimit( c.blobSize, c.warnSize, c.errSize, domainID, domainName, workflowID, runID, metricsClient.Scope(1), logger, tag.OperationName("testOperation"), ) require.Equal(t, c.wantErr, got) if c.assertMetrics != nil { c.assertMetrics(testScope.Snapshot()) } }) } } ================================================ FILE: common/visibility/validate_search_attribute_key.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package visibility import ( "fmt" "regexp" "strings" ) var ( validSearchAttributeKey = regexp.MustCompile(`^[a-zA-Z][a-zA-Z_0-9]*$`) nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9]+`) ) // ValidateSearchAttributeKey checks if the search attribute key has valid format func ValidateSearchAttributeKey(name string) error { if !validSearchAttributeKey.MatchString(name) { return fmt.Errorf("has to be contain alphanumeric and start with a letter") } return nil } // SanitizeSearchAttributeKey try to sanitize the search attribute key func SanitizeSearchAttributeKey(name string) (string, error) { sanitized := nonAlphanumericRegex.ReplaceAllString(name, "_") // remove leading and trailing underscores sanitized = strings.Trim(sanitized, "_") // remove leading numbers sanitized = strings.TrimLeft(sanitized, "0123456789") return sanitized, ValidateSearchAttributeKey(sanitized) } ================================================ FILE: common/visibility/validate_search_attribute_key_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package visibility import ( "testing" ) func TestValidateSearchAttributeKey(t *testing.T) { type args struct { name string } tests := []struct { name string args args wantErr bool }{ { name: "pure character", args: args{name: "CustomStringField"}, }, { name: "alphanumeric", args: args{name: "CustomStringField1"}, }, { name: "start with number", args: args{name: "1CustomStringField"}, wantErr: true, }, { name: "contain special character", args: args{name: "CustomStringField!"}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if err := ValidateSearchAttributeKey(tt.args.name); (err != nil) != tt.wantErr { t.Errorf("ValidateSearchAttributeKey() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestSanitizeSearchAttributeKey(t *testing.T) { type args struct { name string } tests := []struct { name string args args want string wantErr bool }{ { name: "pure character", args: args{name: "CustomStringField"}, want: "CustomStringField", }, { name: "alphanumeric", args: args{name: "CustomStringField1"}, want: "CustomStringField1", }, { name: "start with number", args: args{name: "1CustomStringField"}, want: "CustomStringField", }, { name: "contain special character", args: args{name: "CustomStringField!"}, want: "CustomStringField", }, { name: "contain special character in the middle", args: args{name: "CustomString-Field"}, want: "CustomString_Field", }, { name: "all numbers", args: args{name: "1234567890"}, want: "", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := SanitizeSearchAttributeKey(tt.args.name) if (err != nil) != tt.wantErr { t.Errorf("SanitizeSearchAttributeKey() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("SanitizeSearchAttributeKey() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: config/base.yaml ================================================ log: stdout: true level: info levelKey: "level" ================================================ FILE: config/bench/base.yaml ================================================ log: stdout: true level: info ================================================ FILE: config/bench/basic.json ================================================ { "useBasicVisibilityValidation": true, "contextTimeoutInSeconds": 3, "failureThreshold": 0.01, "totalLaunchCount": 100, "routineCount": 1, "waitTimeBufferInSeconds": 10, "chainSequence": 4, "concurrentCount": 1, "payloadSizeBytes": 1024, "executionStartToCloseTimeoutInSeconds": 10 } ================================================ FILE: config/bench/basic_panic.json ================================================ { "totalLaunchCount": 5000, "routineCount": 5, "chainSequence": 4, "concurrentCount": 1, "payloadSizeBytes": 1024, "contextTimeoutInSeconds": 3, "executionStartToCloseTimeoutInSeconds": 1200, "panicStressWorkflow": true, "failureThreshold": 0.05 } ================================================ FILE: config/bench/cancellation.json ================================================ { "totalLaunchCount": 3000, "concurrency": 1, "contextTimeoutInSeconds": 3 } ================================================ FILE: config/bench/concurrent_execution.json ================================================ { "totalBatches": 15, "concurrency": 5, "batchType": "childWorkflow", "batchSize": 100, "batchPeriodInSeconds": 60, "batchMaxLatencyInSeconds": 10, "batchTimeoutInSeconds": 30 } ================================================ FILE: config/bench/cron.json ================================================ { "testSuites": [ { "name": "syncAPI", "domain": "cadence-bench-sync", "configs": [ { "name": "basic", "timeoutInSeconds": 600, "basic": { "totalLaunchCount": 10, "routineCount": 1, "chainSequence": 4, "concurrentCount": 1, "payloadSizeBytes": 1024, "executionStartToCloseTimeoutInSeconds": 60, "contextTimeoutInSeconds": 3 } }, { "name": "basic", "description": "panic workflow test", "timeoutInSeconds": 600, "basic": { "totalLaunchCount": 10, "routineCount": 1, "chainSequence": 4, "concurrentCount": 1, "payloadSizeBytes": 1024, "executionStartToCloseTimeoutInSeconds": 180, "contextTimeoutInSeconds": 3, "panicStressWorkflow": true } }, { "name": "cancellation", "timeoutInSeconds": 600, "cancellation": { "totalLaunchCount": 3000, "concurrency": 1, "contextTimeoutInSeconds": 2 } }, { "name": "signal", "timeoutInSeconds": 1800, "signal": { "loaderCount": 10, "loadTestWorkflowCount": 1000, "signalCount": 50, "signalDataSize": 2700, "rateLimit": 7, "workflowExecutionTimeoutInSeconds": 600, "decisionTaskTimeoutInSeconds": 30, "campaignCount": 30, "actionRate": 0.02, "failureRate": 0, "signalBeforeContinueAsNew": 10, "enableRollingWindow": true, "maxSignalDelayInSeconds": 5, "maxSignalDelayCount": 5, "failureThreshold": 0.05 } } ] }, { "name": "batchOp", "domain": "cadence-bench-batch", "configs": [ { "name": "timer", "timeoutInSeconds": 600, "timer": { "TotalTimerCount": 100, "TimerPerWorkflow": 1, "ShortestTimerDurationInSeconds": 300, "LongestTimerDurationInSeconds": 300, "MaxTimerLatencyInSeconds": 2, "TimerTimeoutInSeconds": 60, "RoutineCount": 2 } }, { "name": "concurrent-execution", "description": "batch of activities", "timeoutInSeconds": 600, "concurrentExec": { "totalBatches": 15, "concurrency": 5, "batchType": "activity", "batchSize": 100, "batchPeriodInSeconds": 60, "batchMaxLatencyInSeconds": 10, "batchTimeoutInSeconds": 30 } }, { "name": "concurrent-execution", "description": "batch of child workflows", "timeoutInSeconds": 600, "concurrentExec": { "totalBatches": 15, "concurrency": 5, "batchType": "childWorkflow", "batchSize": 100, "batchPeriodInSeconds": 60, "batchMaxLatencyInSeconds": 10, "batchTimeoutInSeconds": 30 } } ] } ] } ================================================ FILE: config/bench/development.yaml ================================================ bench: name: "cadence-bench" domains: ["cadence-bench", "cadence-bench-sync", "cadence-bench-batch"] # it will start workers on all those domains(also try to register if not exists) numTaskLists: 3 # it will start workers listening on cadence-bench-tl-0, cadence-bench-tl-1, cadence-bench-tl-2 cadence: service: "cadence-frontend" host: "${CADENCE_FRONTEND_ADDRESS:127.0.0.1:7833}" metrics: statsd: ~ prometheus: timerType: "histogram" listenAddress: "127.0.0.1:8005" ================================================ FILE: config/bench/signal.json ================================================ { "loaderCount": 10, "loadTestWorkflowCount": 1000, "signalCount": 50, "signalDataSize": 2700, "rateLimit": 7, "workflowExecutionTimeoutInSeconds": 600, "decisionTaskTimeoutInSeconds": 30, "campaignCount": 30, "actionRate": 0.02, "failureRate": 0, "signalBeforeContinueAsNew": 10, "enableRollingWindow": false, "maxSignalDelayInSeconds": 5, "maxSignalDelayCount": 5, "failureThreshold": 0.05 } ================================================ FILE: config/bench/timer.json ================================================ { "totalTimerCount": 100, "timerPerWorkflow": 1, "shortestTimerDurationInSeconds": 300, "longestTimerDurationInSeconds": 300, "maxTimerLatencyInSeconds": 2, "timerTimeoutInSeconds": 60, "routineCount": 2 } ================================================ FILE: config/canary/base.yaml ================================================ log: stdout: true level: info ================================================ FILE: config/canary/development.yaml ================================================ canary: domains: ["cadence-canary"] crossClusterTestMode: "test-all" canaryDomainClusters: ["cluster0", "cluster1", "cluster2"] excludes: ["workflow.searchAttributes", "workflow.batch", "workflow.archival.history", "workflow.archival.visibility"] cadence: service: "cadence-frontend" address: "127.0.0.1:7833" #host: "127.0.0.1:7933" # replace address with host if using Thrift for compatibility metrics: statsd: ~ prometheus: timerType: "histogram" listenAddress: "127.0.0.1:8005" ================================================ FILE: config/credentials/client.key ================================================ -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+Hfqm5fJj1JK3 ERJkG17ilJF5RuB4YURsV7bDg6F25M9QTfqGu2kD4ethMDu3s2jn4BHDwKnMUjUK eUdZdr5Nl+cv/IM3c5cJ0zme/in1ZKb958S3auSIWVg9BA/CC/9EWO8lhfS0fp6Q QGsE+eg0XKEFumOPvPny+rM/L6o+CmgXQxzEe/3Ao0bMO74Ni62YggHFh47oAm1d Gw7KnCZ04YxCNFVYjbg6DOCA5hJrOf3JkhQI8LfGmRh8V9Q3tDYaWhGL/VuiYqsg KIhr1sUO+LUJoDBUUbAWE2RVC/XsHB/U999mbg9WgzsJtKCzuOgXq8pT7Nviz/Ue 5y5bNWpvAgMBAAECggEBAJeu04zVac68i1IEDOhR7iJY5rgcFHv3HuBSGz9ihjCV 3dH0ZS2Z2O+AEIw6L2sheVGHNKU50j9yV19D4+k9FBhUWd+vWAGl95ufKxnvLwra g2GQySKN5kfjAMOtueTYLZIaYrNNbS+U1m3A96HAwcMFJHOtgkHsRH/YBaskHG/h /V5699nhlptOeYK0I8v3WlnEtV5JpuBjRPdZDBwMGRf+ZlJADVzPPaFdTDXIwC/n Ok69NsPHkG6XogDpWhW5sT3hIjOLU+r9uRXz4nx6UCi0Wnf3qX3smoHL1s6phRWE qCriT8pdgfo5Efx4Rx2FoD/Evwb12lh5AlnZo43YUAECgYEA5VZHAXn+E/OgY+x+ BmoNjrjMXRHkA4gHFwXEiGrEfUIleSUSdSA0VMYFN/KoQrjkAqkS4gIbD8s8M6jx TKvOuD7UlDUcs1N9uvVJrHS1nvF9tBCOkAQIcWpdmli1CcqDz1g0WYpYARpX1KNA TcXtZhkeAqkbA+R6zmh3fMSz3s8CgYEA1DhnyWgiflcDaTagY1lpQKGED7FzQ7os qY9Hd7Ni1nS8FTnFmUN9SxaFtj/NryUjo8D4HkgEjvZ3clYCIMZk/M1Uw71uQFOC DxeJWqHCdTgXqLT8J7pmlEyUdFBQPtusEPWpiqeJTgpfWtuYBv5W9KajF9NLkgxW lit9LhrBomECgYA2zahILQvPXr+sQT5YcA1vVq/XacgQI/h1wvYVF1Z/DEiCK56+ iISY4X0rwnv8/cvW6upNQe2Pl6R6P9vx03ihxLnt9Mc3/zKjc6w4Xudr5Q+B8Rbw SVZvK9jUYnBYt6FJ8i0IXKvtD4t/j8d1DmBNrqDiR+DhzD6ghoxPL35ANwKBgFDH j6n4e3i/wkqgQnjOpeO2F0Cp+QgEq892/Gsx2yNql9U0gmSObfjQ/+CZdL+AUYqZ f1h3bVTFuD3LQ1AadIJN3kALsRXHM7Bu8xeOjyhzS34qNfkhWL6GT9PeZk0m7N4f xINEtl0AUb+R6tJuqcbCTz5YbWGQVL0gnKuZ/67BAoGBANVO3HP3ivOwsPEPWnmA SPc3k6GgpzcjPxpadVgG/9z8oyjH9q9xP84NzMNXftbS8gE41bYyNgAaql1CGXUk fF4ZMlWV/PKCm4s3+r9umlfE2bGY1EjiYzlR4atzpspfOTNnMyQZMunoCR2/vUAD C2LCXCBeUgP9tkEaZwu0lgBt -----END PRIVATE KEY----- ================================================ FILE: config/credentials/keytest ================================================ -----BEGIN PRIVATE KEY----- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCxy6SW0eKVqr6j mAhUTg8bBaX56y8nYRoSlbDqiCv3xSfXVgnigqTAiQvDiIijS294Ig0DdxmbFpbV F6NFYoSpgW1DWth3oGycUAddaHZHcDUW2kboA1QQaQTs4Al7b8vjYT8iYn4ZDFG9 qu0icMzJ+JF8Hv4vaffAb42L6EE0y0BpfhbBuPaG6nbUmO6j3pO6FwYiYEUEBMZZ phHbCp7/pvL3Al7E8eVj/W4OndKfMXV0IuOnvpIYJ/Jio4YL8/GFCLuYPF4f3+0g L6W39LPBzWfyBku2VGVkeKe9hryWFZepVjDpYPyHgtOAAur2Jj5t4PU8VBJjUaRC 23RGFHB5AgMBAAECggEABj1T9Orf0W9nskDQ2QQ7cuVdZEJjpMrbTK1Aw1L8/Qc9 TSkINDEayaV9mn1RXe61APcBSdP4ER7nXfTZiQ21LhLcWWg9T3cbh1b70oRqyI9z Pi6HSBeWz4kfUBX9izMQFBZKzjYn6qaJp1b8bGXKRWkcvPRZqLhmsRPmeH3xrOHe qsIDhYXMjRoOgEUxLbk8iPLP6nx0icPJl/tHK2l76R+1Ko6TBE69Md2krUIuh0u4 nm9n+Az+0GuvkFsLw5KMGhSBeqB+ez5qtFa8T8CUCn98IjiUDOwgZdFrNldFLcZf putw7O2qCA9LT+mFBQ6CVsVu/9tKeXQ9sJ7p3lxhwQKBgQDjt7HNIabLncdXPMu0 ByRyNVme0+Y1vbj9Q7iodk77hvlzWpD1p5Oyvq7cN+Cb4c1iO/ZQXMyUw+9hLgmf LNquH2d4hK1Jerzc/ciwu6dUBsCW8+0VJd4M2UNN15rJMPvbZGmqMq9Np1iCTCjE dvHo7xjPcJhsbhMbHq+PaUU7OQKBgQDH4KuaHBFTGUPkRaQGAZNRB8dDvSExV6ID Pblzr80g9kKHUnQCQfIDLjHVgDbTaSCdRw7+EXRyRmLy5mfPWEbUFfIemEpEcEcb 3geWeVDx4Z/FwprWFuVifRopRSQ/FAbMXLIui7OHXWLEtzBvLkR/uS2VIVPm10PV pbh2EXifQQKBgQDbcOLbjelBYLt/euvGgfeCQ50orIS1Fy5UidVCKjh0tR5gJk95 G1L+tjilqQc+0LtuReBYkwTm+2YMXSQSi1P05fh9MEYZgDjOMZYbkcpu887V6Rx3 +7Te5uOv+OyFozmhs0MMK6m5iGGHtsK2iPUYBoj/Jj8MhorM4KZH6ic4KQKBgQCl 3zIpg09xSc9Iue5juZz6qtzXvzWzkAj4bZnggq1VxGfzix6Q3Q8tSoG6r1tQWLbj Lpwnhm6/guAMud6+eIDW8ptqfnFrmE26t6hOXMEq6lXANT5vmrKj6DP0uddZrZHy uJ55+B91n68elvPP4HKiGBfW4cCSGmTGAXAyM0+JwQKBgQCz2cNiFrr+oEnlHDLg EqsiEufppT4FSZPy9/MtuWuMgEOBu34cckYaai+nahQLQvH62KskTK0EUjE1ywub NPORuXcugxIBMHWyseOS7lrtrlSBxU9gntS7jHdM3IMrrUy9YZBvPvFGP0wLdpKM nvt3vT46hs3n28XZpb18uRkSDw== -----END PRIVATE KEY----- ================================================ FILE: config/credentials/keytest.pub ================================================ -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAscukltHilaq+o5gIVE4P GwWl+esvJ2EaEpWw6ogr98Un11YJ4oKkwIkLw4iIo0tveCINA3cZmxaW1RejRWKE qYFtQ1rYd6BsnFAHXWh2R3A1FtpG6ANUEGkE7OAJe2/L42E/ImJ+GQxRvartInDM yfiRfB7+L2n3wG+Ni+hBNMtAaX4Wwbj2hup21Jjuo96TuhcGImBFBATGWaYR2wqe /6by9wJexPHlY/1uDp3SnzF1dCLjp76SGCfyYqOGC/PxhQi7mDxeH9/tIC+lt/Sz wc1n8gZLtlRlZHinvYa8lhWXqVYw6WD8h4LTgALq9iY+beD1PFQSY1GkQtt0RhRw eQIDAQAB -----END PUBLIC KEY----- ================================================ FILE: config/development.yaml ================================================ persistence: defaultStore: cass-default visibilityStore: cass-visibility numHistoryShards: 4 datastores: cass-default: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence" connectTimeout: 2s # defaults to 2s if not defined timeout: 5s # defaults to 10s if not defined consistency: LOCAL_QUORUM # default value serialConsistency: LOCAL_SERIAL # default value cass-visibility: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_visibility" ringpop: name: cadence bootstrapMode: hosts bootstrapHosts: [ "127.0.0.1:7933", "127.0.0.1:7934", "127.0.0.1:7935" ] maxJoinDuration: 30s services: frontend: rpc: port: ${FRONTEND_PORT:7933} grpcPort: ${FRONTEND_PORT_GRPC:7833} bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: ${FRONTEND_PORT_PPROF:7936} matching: rpc: port: ${MATCHING_PORT:7935} grpcPort: ${MATCHING_PORT_GRPC:7835} bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: ${MATCHING_PORT_PPROF:7938} history: rpc: port: ${HISTORY_PORT:7934} grpcPort: ${HISTORY_PORT_GRPC:7834} bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: ${HISTORY_PORT_PPROF:7937} worker: rpc: port: ${WORKER_PORT:7939} bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: ${WORKER_PORT_PPROF:7940} shard-distributor: rpc: port: 7941 grpcPort: 7943 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7942 clusterGroupMetadata: failoverVersionIncrement: 10 primaryClusterName: "cluster0" currentClusterName: "cluster0" clusterGroup: cluster0: enabled: true initialFailoverVersion: 0 newInitialFailoverVersion: 1 # migrating to this new failover version rpcAddress: "localhost:7833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" dcRedirectionPolicy: policy: "noop" toDC: "" archival: history: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" gstorage: credentialsPath: "/tmp/gcloud/keyfile.json" visibility: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" domainDefaults: archival: history: status: "enabled" URI: "file:///tmp/cadence_archival/development" visibility: status: "enabled" URI: "file:///tmp/cadence_vis_archival/development" dynamicconfig: client: filebased configstore: pollInterval: "10s" updateRetryAttempts: 2 FetchTimeout: "2s" UpdateTimeout: "2s" filebased: filepath: "config/dynamicconfig/development.yaml" pollInterval: "10s" blobstore: filestore: outputDirectory: "/tmp/blobstore" shardDistributorClient: hostPort: "localhost:7943" shardDistribution: election: leaderPeriod: 60s maxRandomDelay: 1s failedElectionCooldown: 1s namespaces: - name: shard-distributor-canary type: fixed mode: onboarded shardNum: 32 - name: shard-distributor-canary-ephemeral mode: onboarded type: ephemeral - name: test-local-passthrough type: fixed mode: local_pass - name: test-local-passthrough-shadow type: ephemeral mode: local_pass_shadow - name: test-distributed-passthrough type: fixed mode: distributed_pass - name: test-external-assignment mode: distributed_pass type: ephemeral leaderStore: storageParams: endpoints: [localhost:2379] dialTimeout: 1s prefix: "leader" store: storageParams: endpoints: [localhost:2379] dialTimeout: 1s prefix: "store" process: period: 1s heartbeatTTL: 2s shard-distributor-matching: namespaces: - namespace: cadence-matching-dev heartbeat_interval: 1s migration_mode: local_pass ttl_shard: 5m ttl_report: 1m ================================================ FILE: config/development_async_wf_kafka_queue.yaml ================================================ persistence: defaultStore: cass-default visibilityStore: cass-visibility numHistoryShards: 4 datastores: cass-default: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence" cass-visibility: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_visibility" ringpop: name: cadence bootstrapMode: hosts bootstrapHosts: [ "127.0.0.1:7933", "127.0.0.1:7934", "127.0.0.1:7935" ] maxJoinDuration: 30s services: frontend: rpc: port: 7933 grpcPort: 7833 bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7936 matching: rpc: port: 7935 grpcPort: 7835 bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7938 history: rpc: port: 7934 grpcPort: 7834 bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7937 worker: rpc: port: 7939 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7940 clusterGroupMetadata: failoverVersionIncrement: 10 primaryClusterName: "cluster0" currentClusterName: "cluster0" clusterGroup: cluster0: enabled: true initialFailoverVersion: 0 newInitialFailoverVersion: 1 # migrating to this new failover version rpcAddress: "localhost:7833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" dcRedirectionPolicy: policy: "noop" toDC: "" archival: history: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" gstorage: credentialsPath: "/tmp/gcloud/keyfile.json" visibility: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" domainDefaults: archival: history: status: "enabled" URI: "file:///tmp/cadence_archival/development" visibility: status: "enabled" URI: "file:///tmp/cadence_vis_archival/development" dynamicconfig: client: filebased configstore: pollInterval: "10s" updateRetryAttempts: 2 FetchTimeout: "2s" UpdateTimeout: "2s" filebased: filepath: "config/dynamicconfig/development.yaml" pollInterval: "10s" blobstore: filestore: outputDirectory: "/tmp/blobstore" asyncWorkflowQueues: queue1: type: "kafka" config: connection: brokers: - "localhost:9092" topic: "async-wf-topic1" ================================================ FILE: config/development_es_opensearch.yaml ================================================ persistence: advancedVisibilityStore: es-visibility datastores: es-visibility: elasticsearch: disableSniff: true version: "os2" tls: enabled: false url: scheme: "http" host: "127.0.0.1:9200" indices: visibility: cadence-visibility-dev kafka: tls: enabled: false clusters: test: brokers: - 127.0.0.1:9092 topics: cadence-visibility-dev: cluster: test cadence-visibility-dev-dlq: cluster: test applications: visibility: topic: cadence-visibility-dev dlq-topic: cadence-visibility-dev-dlq dynamicconfig: client: filebased filebased: filepath: "config/dynamicconfig/development_es.yaml" ================================================ FILE: config/development_es_opensearch_migration.yaml ================================================ persistence: advancedVisibilityStore: os-visibility datastores: es-visibility: elasticsearch: disableSniff: true version: "v7" url: scheme: "http" host: "127.0.0.1:9201" indices: visibility: cadence-visibility-dev os-visibility: elasticsearch: disableSniff: true version: "os2" username: "admin" password: "DevTestInitial123!" tls: enabled: true url: scheme: "https" host: "127.0.0.1:9200" indices: visibility: cadence-visibility-dev migration: enabled: true consumerName: "cadence-visibility-dev-os-consumer" kafka: tls: enabled: false clusters: test: brokers: - 127.0.0.1:9092 topics: cadence-visibility-dev: cluster: test cadence-visibility-dev-dlq: cluster: test applications: visibility: topic: cadence-visibility-dev dlq-topic: cadence-visibility-dev-dlq dynamicconfig: client: filebased filebased: filepath: "config/dynamicconfig/development_es.yaml" ================================================ FILE: config/development_es_v6.yaml ================================================ persistence: advancedVisibilityStore: es-visibility datastores: es-visibility: elasticsearch: version: "v6" url: scheme: "http" host: "127.0.0.1:9200" indices: visibility: cadence-visibility-dev kafka: tls: enabled: false clusters: test: brokers: - 127.0.0.1:9092 topics: cadence-visibility-dev: cluster: test cadence-visibility-dev-dlq: cluster: test applications: visibility: topic: cadence-visibility-dev dlq-topic: cadence-visibility-dev-dlq dynamicconfig: client: filebased filebased: filepath: "config/dynamicconfig/development_es.yaml" ================================================ FILE: config/development_es_v7.yaml ================================================ persistence: advancedVisibilityStore: es-visibility datastores: es-visibility: elasticsearch: disableSniff: true version: "v7" url: scheme: "http" host: "127.0.0.1:9200" indices: visibility: cadence-visibility-dev kafka: tls: enabled: false clusters: test: brokers: - 127.0.0.1:9092 topics: cadence-visibility-dev: cluster: test cadence-visibility-dev-dlq: cluster: test applications: visibility: topic: cadence-visibility-dev dlq-topic: cadence-visibility-dev-dlq dynamicconfig: client: filebased filebased: filepath: "config/dynamicconfig/development_es.yaml" ================================================ FILE: config/development_generic_oauth.yaml ================================================ persistence: advancedVisibilityStore: es-visibility datastores: es-visibility: elasticsearch: version: "v7" url: scheme: "http" host: "127.0.0.1:9200" indices: visibility: cadence-visibility-dev kafka: tls: enabled: false clusters: test: brokers: - 127.0.0.1:9092 topics: cadence-visibility-dev: cluster: test cadence-visibility-dev-dlq: cluster: test applications: visibility: topic: cadence-visibility-dev dlq-topic: cadence-visibility-dev-dlq dynamicconfig: client: filebased filebased: filepath: "config/dynamicconfig/development_es.yaml" authorization: oauthAuthorizer: enable: true maxJwtTTL: 600000000 jwtCredentials: algorithm: "RS256" publicKey: "config/credentials/keytest.pub" # provider section can be used to validate token issued by 3rd party provider (Okta, AWS, Google, etc.) provider: jwksURL: # AWS cognito example: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_hNq90rT473/.well-known/jwks.json" # Custom data is extracted from token using JMES Path query language: https://jmespath.org/tutorial.html adminAttributePath: # AWS cognito example: "permissions | contains(@, 'admin:true')" groupsAttributePath: # AWS cognito example: "\"cognito:groups\" | join(', ', @)" clusterGroupMetadata: failoverVersionIncrement: 10 masterClusterName: "cluster0" currentClusterName: "cluster0" clusterGroup: cluster0: enabled: true initialFailoverVersion: 0 rpcAddress: "localhost:7833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" authorizationProvider: enable: true type: "OAuthAuthorization" privateKey: "config/credentials/keytest" ================================================ FILE: config/development_http_api.yaml ================================================ services: frontend: rpc: port: 7933 grpcPort: 7833 bindOnLocalHost: true grpcMaxMsgSize: 33554432 # enable HTTP server, allow to call Start worfklow using HTTP API # Use curl to start a workflow: # curl http://0.0.0.0:8800 \ # -H 'context-ttl-ms: 2000' \ # -H 'rpc-caller: rpc-client-name' \ # -H 'rpc-service: cadence-frontend' \ # -H 'rpc-encoding: json' \ # -H 'rpc-procedure: uber.cadence.api.v1.WorkflowAPI::StartWorkflowExecution' \ # -X POST --data @data.json # Where data.json content looks something like this: # { # "domain": "samples-domain", # "workflowId": "workflowid123", # "execution_start_to_close_timeout": "11s", # "task_start_to_close_timeout": "10s", # "workflowType": { # "name": "workflow_type" # }, # "taskList": { # "name": "tasklist-name" # }, # "identity": "My custom identity", # "requestId": "4D1E4058-6FCF-4BA8-BF16-8FA8B02F9651" # } http: # To enable HTTP TLS uncomment the following section #tls: # enabled: true # certFile: config/credentials/keytest.crt # keyFile: config/credentials/keytest # caFiles: # - config/credentials/client.crt # requireClientAuth: true #TLSMode: enforced port: 8800 procedures: # list of available API procedures # Admin API - uber.cadence.admin.v1.AdminAPI::AddSearchAttribute - uber.cadence.admin.v1.AdminAPI::CloseShard - uber.cadence.admin.v1.AdminAPI::CountDLQMessages - uber.cadence.admin.v1.AdminAPI::DeleteWorkflow - uber.cadence.admin.v1.AdminAPI::DescribeCluster - uber.cadence.admin.v1.AdminAPI::DescribeHistoryHost - uber.cadence.admin.v1.AdminAPI::DescribeQueue - uber.cadence.admin.v1.AdminAPI::DescribeShardDistribution - uber.cadence.admin.v1.AdminAPI::DescribeWorkflowExecution - uber.cadence.admin.v1.AdminAPI::GetCrossClusterTasks - uber.cadence.admin.v1.AdminAPI::GetDLQReplicationMessages - uber.cadence.admin.v1.AdminAPI::GetDomainIsolationGroups - uber.cadence.admin.v1.AdminAPI::GetDomainReplicationMessages - uber.cadence.admin.v1.AdminAPI::GetDynamicConfig - uber.cadence.admin.v1.AdminAPI::GetGlobalIsolationGroups - uber.cadence.admin.v1.AdminAPI::GetReplicationMessages - uber.cadence.admin.v1.AdminAPI::GetWorkflowExecutionRawHistoryV2 - uber.cadence.admin.v1.AdminAPI::ListDynamicConfig - uber.cadence.admin.v1.AdminAPI::MaintainCorruptWorkflow - uber.cadence.admin.v1.AdminAPI::MergeDLQMessages - uber.cadence.admin.v1.AdminAPI::PurgeDLQMessages - uber.cadence.admin.v1.AdminAPI::ReadDLQMessages - uber.cadence.admin.v1.AdminAPI::ReapplyEvents - uber.cadence.admin.v1.AdminAPI::RefreshWorkflowTasks - uber.cadence.admin.v1.AdminAPI::RemoveTask - uber.cadence.admin.v1.AdminAPI::ResendReplicationTasks - uber.cadence.admin.v1.AdminAPI::ResetQueue - uber.cadence.admin.v1.AdminAPI::RespondCrossClusterTasksCompleted - uber.cadence.admin.v1.AdminAPI::RestoreDynamicConfig - uber.cadence.admin.v1.AdminAPI::UpdateDomainIsolationGroups - uber.cadence.admin.v1.AdminAPI::UpdateDynamicConfig - uber.cadence.admin.v1.AdminAPI::UpdateGlobalIsolationGroups # Domain operations related API - uber.cadence.api.v1.DomainAPI::DeprecateDomain - uber.cadence.api.v1.DomainAPI::DescribeDomain - uber.cadence.api.v1.DomainAPI::ListDomains - uber.cadence.api.v1.DomainAPI::RegisterDomain - uber.cadence.api.v1.DomainAPI::UpdateDomain # Health check endpoint - uber.cadence.api.v1.MetaAPI::Health # Searching for workflows - uber.cadence.api.v1.VisibilityAPI::CountWorkflowExecutions - uber.cadence.api.v1.VisibilityAPI::GetSearchAttributes - uber.cadence.api.v1.VisibilityAPI::ListArchivedWorkflowExecutions - uber.cadence.api.v1.VisibilityAPI::ListClosedWorkflowExecutions - uber.cadence.api.v1.VisibilityAPI::ListOpenWorkflowExecutions - uber.cadence.api.v1.VisibilityAPI::ListWorkflowExecutions - uber.cadence.api.v1.VisibilityAPI::ScanWorkflowExecutions # Workflow execution related API endpoints - uber.cadence.api.v1.WorkflowAPI::DescribeTaskList - uber.cadence.api.v1.WorkflowAPI::DescribeWorkflowExecution - uber.cadence.api.v1.WorkflowAPI::GetClusterInfo - uber.cadence.api.v1.WorkflowAPI::GetTaskListsByDomain - uber.cadence.api.v1.WorkflowAPI::GetWorkflowExecutionHistory - uber.cadence.api.v1.WorkflowAPI::ListTaskListPartitions - uber.cadence.api.v1.WorkflowAPI::QueryWorkflow - uber.cadence.api.v1.WorkflowAPI::RefreshWorkflowTasks - uber.cadence.api.v1.WorkflowAPI::RequestCancelWorkflowExecution - uber.cadence.api.v1.WorkflowAPI::ResetWorkflowExecution - uber.cadence.api.v1.WorkflowAPI::RestartWorkflowExecution - uber.cadence.api.v1.WorkflowAPI::SignalWithStartWorkflowExecution - uber.cadence.api.v1.WorkflowAPI::SignalWorkflowExecution - uber.cadence.api.v1.WorkflowAPI::StartWorkflowExecution - uber.cadence.api.v1.WorkflowAPI::TerminateWorkflowExecution metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7936 ================================================ FILE: config/development_instance2.yaml ================================================ # # this is a copy of development.yaml, but with different ports for the services, # and bootstrapping on development.yaml's service hosts. # # this is intended for running multiple instances locally, within the same cluster. # start `--env development` first, then `--env development_instance2`. # persistence: defaultStore: cass-default visibilityStore: cass-visibility numHistoryShards: 4 datastores: cass-default: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence" connectTimeout: 2s # defaults to 2s if not defined timeout: 5s # defaults to 10s if not defined consistency: LOCAL_QUORUM # default value serialConsistency: LOCAL_SERIAL # default value cass-visibility: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_visibility" ringpop: name: cadence bootstrapMode: hosts bootstrapHosts: [ "127.0.0.1:7933", "127.0.0.1:7934", "127.0.0.1:7935", # development.yaml list, for instance 1 "127.0.0.1:27933", "127.0.0.1:27934", "127.0.0.1:27935" # for this instance ] maxJoinDuration: 30s services: frontend: rpc: port: 27933 grpcPort: 27833 bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 27936 matching: rpc: port: 27935 grpcPort: 27835 bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 27938 history: rpc: port: 27934 grpcPort: 27834 bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 27937 worker: rpc: port: 27939 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 27940 clusterGroupMetadata: failoverVersionIncrement: 10 primaryClusterName: "cluster0" currentClusterName: "cluster0" clusterGroup: cluster0: enabled: true initialFailoverVersion: 0 newInitialFailoverVersion: 1 # migrating to this new failover version rpcAddress: "localhost:7833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" dcRedirectionPolicy: policy: "noop" toDC: "" archival: history: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" gstorage: credentialsPath: "/tmp/gcloud/keyfile.json" visibility: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" domainDefaults: archival: history: status: "enabled" URI: "file:///tmp/cadence_archival/development" visibility: status: "enabled" URI: "file:///tmp/cadence_vis_archival/development" dynamicconfig: client: filebased configstore: pollInterval: "10s" updateRetryAttempts: 2 FetchTimeout: "2s" UpdateTimeout: "2s" filebased: filepath: "config/dynamicconfig/development.yaml" pollInterval: "10s" blobstore: filestore: outputDirectory: "/tmp/blobstore" ================================================ FILE: config/development_multiple_cassandra.yaml ================================================ persistence: defaultStore: cass-default visibilityStore: cass-visibility numHistoryShards: 4 datastores: cass-default: shardedNosql: defaultShard: nosqlDbShard1 shardingPolicy: historyShardMapping: - start: 0 end: 0 shard: nosqlDbShard1 - start: 1 end: 2 shard: nosqlDbShard2 - start: 3 end: 3 shard: nosqlDbShard1 taskListHashing: shardOrder: - nosqlDbShard1 - nosqlDbShard2 connections: nosqlDbShard1: nosqlPlugin: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence" port: 9042 nosqlDbShard2: nosqlPlugin: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence" port: 9043 cass-visibility: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_visibility" ringpop: name: cadence bootstrapMode: hosts bootstrapHosts: [ "127.0.0.1:7933", "127.0.0.1:7934", "127.0.0.1:7935" ] maxJoinDuration: 30s services: frontend: rpc: port: 7933 grpcPort: 7833 bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7936 matching: rpc: port: 7935 grpcPort: 7835 bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7938 history: rpc: port: 7934 grpcPort: 7834 bindOnLocalHost: true grpcMaxMsgSize: 33554432 metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7937 worker: rpc: port: 7939 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7940 clusterGroupMetadata: failoverVersionIncrement: 10 primaryClusterName: "cluster0" currentClusterName: "cluster0" clusterGroup: cluster0: enabled: true initialFailoverVersion: 0 rpcAddress: "localhost:7833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" dcRedirectionPolicy: policy: "noop" toDC: "" archival: history: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" gstorage: credentialsPath: "/tmp/gcloud/keyfile.json" visibility: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" domainDefaults: archival: history: status: "enabled" URI: "file:///tmp/cadence_archival/development" visibility: status: "enabled" URI: "file:///tmp/cadence_vis_archival/development" dynamicconfig: client: filebased configstore: pollInterval: "10s" updateRetryAttempts: 2 FetchTimeout: "2s" UpdateTimeout: "2s" filebased: filepath: "config/dynamicconfig/development.yaml" pollInterval: "10s" blobstore: filestore: outputDirectory: "/tmp/blobstore" ================================================ FILE: config/development_multiple_mysql.yaml ================================================ persistence: defaultStore: mysql-default visibilityStore: "" # useMultipleDatabases must use advancedVisibilityStore only. Due to deep merging with development.yaml, this need to override to empty advancedVisibilityStore: es-visibility datastores: mysql-default: sql: pluginName: "mysql" connectProtocol: "tcp" maxConns: 20 maxIdleConns: 20 maxConnLifetime: "1h" useMultipleDatabases: true nShards: 4 multipleDatabasesConfig: - user: "root" password: "cadence" connectAddr: "127.0.0.1:3306" databaseName: "cadence0" - user: "root" password: "cadence" connectAddr: "127.0.0.1:3306" databaseName: "cadence1" - user: "root" password: "cadence" connectAddr: "127.0.0.1:3306" databaseName: "cadence2" - user: "root" password: "cadence" connectAddr: "127.0.0.1:3306" databaseName: "cadence3" es-visibility: elasticsearch: version: "v7" url: scheme: "http" host: "127.0.0.1:9200" indices: visibility: cadence-visibility-dev kafka: tls: enabled: false clusters: test: brokers: - 127.0.0.1:9092 topics: cadence-visibility-dev: cluster: test cadence-visibility-dev-dlq: cluster: test applications: visibility: topic: cadence-visibility-dev dlq-topic: cadence-visibility-dev-dlq dynamicconfig: client: filebased filebased: filepath: "config/dynamicconfig/development_es.yaml" ================================================ FILE: config/development_mysql.yaml ================================================ persistence: defaultStore: mysql-default visibilityStore: mysql-visibility datastores: mysql-default: sql: pluginName: "mysql" databaseName: "cadence" connectAddr: "127.0.0.1:3306" connectProtocol: "tcp" user: "root" password: "cadence" maxConns: 20 maxIdleConns: 20 maxConnLifetime: "1h" mysql-visibility: sql: pluginName: "mysql" databaseName: "cadence_visibility" connectAddr: "127.0.0.1:3306" connectProtocol: "tcp" user: "root" password: "cadence" maxConns: 2 maxIdleConns: 2 maxConnLifetime: "1h" ================================================ FILE: config/development_oauth.yaml ================================================ persistence: advancedVisibilityStore: es-visibility datastores: es-visibility: elasticsearch: version: "v7" url: scheme: "http" host: "127.0.0.1:9200" indices: visibility: cadence-visibility-dev kafka: tls: enabled: false clusters: test: brokers: - 127.0.0.1:9092 topics: cadence-visibility-dev: cluster: test cadence-visibility-dev-dlq: cluster: test applications: visibility: topic: cadence-visibility-dev dlq-topic: cadence-visibility-dev-dlq dynamicconfig: client: filebased filebased: filepath: "config/dynamicconfig/development_es.yaml" authorization: oauthAuthorizer: enable: true maxJwtTTL: 600000000 jwtCredentials: algorithm: "RS256" publicKey: "config/credentials/keytest.pub" clusterGroupMetadata: failoverVersionIncrement: 10 masterClusterName: "cluster0" currentClusterName: "cluster0" clusterGroup: cluster0: enabled: true initialFailoverVersion: 0 rpcAddress: "localhost:7833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" authorizationProvider: enable: true type: "OAuthAuthorization" privateKey: "config/credentials/keytest" ================================================ FILE: config/development_pinot.yaml ================================================ persistence: advancedVisibilityStore: pinot-visibility datastores: pinot-visibility: pinot: broker: "localhost:8099" cluster: pinot-test table: "cadence_visibility_pinot" migration: enabled: false es-visibility: elasticsearch: version: "v6" url: scheme: "http" host: "127.0.0.1:9200" indices: visibility: cadence-visibility-dev kafka: tls: enabled: false clusters: test: brokers: - 127.0.0.1:9092 topics: cadence-visibility-dev: cluster: test cadence-visibility-dev-dlq: cluster: test cadence-visibility-pinot: cluster: test cadence-visibility-pinot-dlq: cluster: test applications: visibility: topic: cadence-visibility-dev dlq-topic: cadence-visibility-dev-dlq pinot-visibility: topic: cadence-visibility-pinot dlq-topic: cadence-visibility-pinot-dlq dynamicconfig: client: filebased filebased: filepath: "config/dynamicconfig/development_pinot.yaml" ================================================ FILE: config/development_postgres.yaml ================================================ persistence: defaultStore: postgres-default visibilityStore: postgres-visibility datastores: postgres-default: sql: pluginName: "postgres" databaseName: "cadence" connectAddr: "127.0.0.1:5432" connectProtocol: "tcp" user: "postgres" password: "cadence" maxConns: 20 maxIdleConns: 20 maxConnLifetime: "1h" postgres-visibility: sql: pluginName: "postgres" databaseName: "cadence_visibility" connectAddr: "127.0.0.1:5432" connectProtocol: "tcp" user: "postgres" password: "cadence" maxConns: 2 maxIdleConns: 2 maxConnLifetime: "1h" ================================================ FILE: config/development_prometheus.yaml ================================================ services: frontend: metrics: statsd: ~ prometheus: timerType: "histogram" listenAddress: "127.0.0.1:8000" matching: metrics: statsd: ~ prometheus: timerType: "histogram" listenAddress: "127.0.0.1:8001" history: metrics: statsd: ~ prometheus: timerType: "histogram" listenAddress: "127.0.0.1:8002" worker: metrics: statsd: ~ prometheus: timerType: "histogram" listenAddress: "127.0.0.1:8003" shard-distributor: metrics: statsd: ~ prometheus: timerType: "histogram" listenAddress: "127.0.0.1:8004" ================================================ FILE: config/development_scylla.yaml ================================================ persistence: defaultStore: scylla-default visibilityStore: scylla-visibility datastores: scylla-default: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence" scylla-visibility: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_visibility" ================================================ FILE: config/development_sqlite.yaml ================================================ persistence: defaultStore: sqlite-default visibilityStore: sqlite-visibility datastores: sqlite-default: sql: pluginName: "sqlite" maxConns: 1 maxIdleConns: 1 maxConnLifetime: "128h" databaseName: "cadence.db" sqlite-visibility: sql: pluginName: "sqlite" maxConns: 1 maxIdleConns: 1 maxConnLifetime: "128h" databaseName: "cadence_visibility.db" ================================================ FILE: config/development_tls.yaml ================================================ services: frontend: rpc: tls: enabled: true certFile: config/credentials/keytest.crt keyFile: config/credentials/keytest caFiles: - config/credentials/client.crt requireClientAuth: true matching: rpc: tls: enabled: true certFile: config/credentials/keytest.crt keyFile: config/credentials/keytest history: rpc: tls: enabled: true certFile: config/credentials/keytest.crt keyFile: config/credentials/keytest clusterGroupMetadata: clusterGroup: cluster0: tls: enabled: true ================================================ FILE: config/development_xdc_cluster0.yaml ================================================ persistence: defaultStore: cass-default visibilityStore: cass-visibility numHistoryShards: 4 datastores: cass-default: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_cluster0" cass-visibility: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_visibility_cluster0" ringpop: name: cadence_cluster0 bootstrapMode: hosts bootstrapHosts: [ "127.0.0.1:7933", "127.0.0.1:7934", "127.0.0.1:7935", "127.0.0.1:7940" ] maxJoinDuration: 30s services: frontend: rpc: port: 7933 grpcPort: 7833 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster0" pprof: port: 7936 matching: rpc: port: 7935 grpcPort: 7835 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster0" pprof: port: 7938 history: rpc: port: 7934 grpcPort: 7834 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster0" pprof: port: 7937 worker: rpc: port: 7940 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster0" pprof: port: 7941 shard-distributor: rpc: port: 7951 grpcPort: 7941 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7952 clusterGroupMetadata: failoverVersionIncrement: 10 primaryClusterName: "cluster0" currentClusterName: "cluster0" clusterRedirectionPolicy: policy: "all-domain-apis-forwarding" # if network communication overhead between clusters is high, consider use "selected-apis-forwarding" instead, but workflow/activity workers need to be connected to each cluster to keep high availability clusterGroup: cluster0: enabled: true initialFailoverVersion: 1 rpcName: "cadence-frontend" rpcAddress: "localhost:7833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" cluster1: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: "localhost:8833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" cluster2: enabled: true initialFailoverVersion: 2 rpcName: "cadence-frontend" rpcAddress: "localhost:9833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" archival: history: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" visibility: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" domainDefaults: archival: history: status: "enabled" URI: "file:///tmp/cadence_archival/development" visibility: status: "enabled" URI: "file:///tmp/cadence_vis_archival/development" blobstore: filestore: outputDirectory: "/tmp/blobstore" dynamicconfig: client: filebased configstore: pollInterval: "10s" updateRetryAttempts: 2 FetchTimeout: "2s" UpdateTimeout: "2s" filebased: filepath: "config/dynamicconfig/development.yaml" pollInterval: "10s" shardDistribution: election: leaderPeriod: 60s maxRandomDelay: 1s failedElectionCooldown: 1s namespaces: - name: shard-distributor-canary type: fixed mode: onboarded shardNum: 32 - name: test-external-assignment mode: distributed_pass type: ephemeral leaderStore: storageParams: endpoints: [localhost:2379] dialTimeout: 1s prefix: "leader" store: storageParams: endpoints: [localhost:2379] dialTimeout: 1s prefix: "store" process: period: 1s heartbeatTTL: 2s shard-distributor-matching: namespaces: - namespace: cadence-matching-dev heartbeat_interval: 1s migration_mode: local_pass ttl_shard: 5m ttl_report: 1m ================================================ FILE: config/development_xdc_cluster1.yaml ================================================ persistence: defaultStore: cass-default visibilityStore: cass-visibility numHistoryShards: 4 datastores: cass-default: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_cluster1" cass-visibility: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_visibility_cluster1" ringpop: name: cadence_cluster1 bootstrapMode: hosts bootstrapHosts: [ "127.0.0.1:8933", "127.0.0.1:8934", "127.0.0.1:8935", "127.0.0.1:8940" ] maxJoinDuration: 30s services: frontend: rpc: port: 8933 grpcPort: 8833 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster1" pprof: port: 8936 matching: rpc: port: 8935 grpcPort: 8835 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster1" pprof: port: 8938 history: rpc: port: 8934 grpcPort: 8834 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster1" pprof: port: 8937 worker: rpc: port: 8940 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster1" pprof: port: 8941 shard-distributor: rpc: port: 7952 grpcPort: 7942 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7953 clusterGroupMetadata: failoverVersionIncrement: 10 primaryClusterName: "cluster0" currentClusterName: "cluster1" clusterRedirectionPolicy: policy: "all-domain-apis-forwarding" # if network communication overhead between clusters is high, consider use "selected-apis-forwarding" instead, but workflow/activity workers need to be connected to each cluster to keep high availability clusterGroup: cluster0: enabled: true initialFailoverVersion: 1 rpcName: "cadence-frontend" rpcAddress: "localhost:7833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" cluster1: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: "localhost:8833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" cluster2: enabled: true initialFailoverVersion: 2 rpcName: "cadence-frontend" rpcAddress: "localhost:9833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" archival: history: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" visibility: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" domainDefaults: archival: history: status: "enabled" URI: "file:///tmp/cadence_archival/development" visibility: status: "enabled" URI: "file:///tmp/cadence_vis_archival/development" blobstore: filestore: outputDirectory: "/tmp/blobstore" dynamicconfig: client: filebased configstore: pollInterval: "10s" updateRetryAttempts: 2 FetchTimeout: "2s" UpdateTimeout: "2s" filebased: filepath: "config/dynamicconfig/development.yaml" pollInterval: "10s" shardDistribution: election: leaderPeriod: 60s maxRandomDelay: 1s failedElectionCooldown: 1s namespaces: - name: shard-distributor-canary type: fixed mode: onboarded shardNum: 32 - name: test-external-assignment mode: distributed_pass type: ephemeral leaderStore: storageParams: endpoints: [localhost:2379] dialTimeout: 1s prefix: "leader" store: storageParams: endpoints: [localhost:2379] dialTimeout: 1s prefix: "store" process: period: 1s heartbeatTTL: 2s shard-distributor-matching: namespaces: - namespace: cadence-matching-dev heartbeat_interval: 1s migration_mode: local_pass ttl_shard: 5m ttl_report: 1m ================================================ FILE: config/development_xdc_cluster2.yaml ================================================ persistence: defaultStore: cass-default visibilityStore: cass-visibility numHistoryShards: 4 datastores: cass-default: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_cluster2" cass-visibility: nosql: pluginName: "cassandra" hosts: "127.0.0.1" keyspace: "cadence_visibility_cluster2" ringpop: name: cadence_cluster2 bootstrapMode: hosts bootstrapHosts: [ "127.0.0.1:9933", "127.0.0.1:9934", "127.0.0.1:9935", "127.0.0.1:9940" ] maxJoinDuration: 30s services: frontend: rpc: port: 9933 grpcPort: 9833 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster2" pprof: port: 9936 matching: rpc: port: 9935 grpcPort: 9835 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster2" pprof: port: 9938 history: rpc: port: 9934 grpcPort: 9834 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster2" pprof: port: 9937 worker: rpc: port: 9940 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence_cluster2" pprof: port: 9941 shard-distributor: rpc: port: 7953 grpcPort: 7943 bindOnLocalHost: true metrics: statsd: hostPort: "127.0.0.1:8125" prefix: "cadence" pprof: port: 7954 clusterGroupMetadata: failoverVersionIncrement: 10 primaryClusterName: "cluster0" currentClusterName: "cluster2" clusterRedirectionPolicy: policy: "all-domain-apis-forwarding" # if network communication overhead between clusters is high, consider use "selected-apis-forwarding" instead, but workflow/activity workers need to be connected to each cluster to keep high availability clusterGroup: cluster0: enabled: true initialFailoverVersion: 1 rpcName: "cadence-frontend" rpcAddress: "localhost:7833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" cluster1: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: "localhost:8833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" cluster2: enabled: true initialFailoverVersion: 2 rpcName: "cadence-frontend" rpcAddress: "localhost:9833" # this is to let worker service and XDC replicator connected to the frontend service. In cluster setup, localhost will not work rpcTransport: "grpc" archival: history: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" visibility: status: "enabled" enableRead: true provider: filestore: fileMode: "0666" dirMode: "0766" domainDefaults: archival: history: status: "enabled" URI: "file:///tmp/cadence_archival/development" visibility: status: "enabled" URI: "file:///tmp/cadence_vis_archival/development" blobstore: filestore: outputDirectory: "/tmp/blobstore" dynamicconfig: client: filebased configstore: pollInterval: "10s" updateRetryAttempts: 2 FetchTimeout: "2s" UpdateTimeout: "2s" filebased: filepath: "config/dynamicconfig/development.yaml" pollInterval: "10s" shardDistribution: election: leaderPeriod: 60s maxRandomDelay: 1s failedElectionCooldown: 1s namespaces: - name: shard-distributor-canary type: fixed mode: onboarded shardNum: 32 - name: test-external-assignment mode: distributed_pass type: ephemeral leaderStore: storageParams: endpoints: [localhost:2379] dialTimeout: 1s prefix: "leader" store: storageParams: endpoints: [localhost:2379] dialTimeout: 1s prefix: "store" process: period: 1s heartbeatTTL: 2s shard-distributor-matching: namespaces: - namespace: cadence-matching-dev heartbeat_interval: 1s migration_mode: local_pass ttl_shard: 5m ttl_report: 1m ================================================ FILE: config/dynamicconfig/README.md ================================================ Use development.yaml file to override the default dynamic config value (they are specified when creating the service config). Each key can have zero or more values and each value can have zero or more constraints. There are only three types of constraint: 1. domainName: string 2. taskListName: string 3. taskType: int (0:Decision, 1:Activity) A value will be selected and returned if all its has exactly the same constraints as the ones specified in query filters (including the number of constraints). Please use the following format: ``` testGetBoolPropertyKey: - value: false - value: true constraints: domainName: "global-samples-domain" - value: false constraints: domainName: "samples-domain" testGetDurationPropertyKey: - value: "1m" constraints: domainName: "samples-domain" taskListName: "longIdleTimeTasklist" testGetFloat64PropertyKey: - value: 12.0 constraints: domainName: "samples-domain" testGetMapPropertyKey: - value: key1: 1 key2: "value 2" key3: - false - key4: true key5: 2.0 ``` ================================================ FILE: config/dynamicconfig/development.yaml ================================================ frontend.enableClientVersionCheck: - value: true constraints: {} system.minRetentionDays: - value: 0 constraints: {} matching.enableStandbyTaskCompletion: - value: true constraints: {} history.standbyClusterDelay: - value: 30s constraints: {} history.standbyTaskMissingEventsResendDelay: - value: 30s constraints: {} history.EnableConsistentQueryByDomain: - value: true constraints: {} history.useNewInitialFailoverVersion: - value: true constraints: {} system.enableDomainAuditLogging: - value: true constraints: {} history.enableStrongIdempotency: - value: true constraints: {} frontend.globalRatelimiterMode: - value: local frontend.validSearchAttributes: - value: BinaryChecksums: 1 CadenceChangeVersion: 1 CloseStatus: 2 CloseTime: 2 CustomBoolField: 4 CustomDatetimeField: 5 CustomDomain: 1 CustomDoubleField: 3 CustomIntField: 2 CustomKeywordField: 1 CustomStringField: 0 DomainID: 1 ExecutionTime: 2 HistoryLength: 2 IsCron: 1 NewKey: 1 NumClusters: 2 Operator: 1 Passed: 4 RolloutID: 1 RunID: 1 ShardID: 2 StartTime: 2 TaskList: 1 UpdateTime: 2 WorkflowID: 1 WorkflowType: 1 addon: 1 addon-type: 1 environment: 1 project: 1 service: 1 user: 1 IsDeleted: 4 ExecutionStatus: 2 CronSchedule: 1 ScheduledExecutionTime: 2 constraints: {} system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" system.domainAuditLogTTL: - value: "15m" matching.enableClientAutoConfig: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true history.enableCleanupOrphanedHistoryBranchOnWorkflowCreation: - value: true shardDistributor.migrationMode: - value: "onboarded" - value: "local_pass" constraints: namespace: "test-local-passthrough" - value: "local_pass_shadow" constraints: namespace: "test-local-passthrough-shadow" - value: "distributed_pass" constraints: namespace: "test-distributed-passthrough" - value: "distributed_pass" constraints: namespace: "test-external-assignment" ================================================ FILE: config/dynamicconfig/development_es.yaml ================================================ frontend.enableClientVersionCheck: - value: true system.writeVisibilityStoreName: - value: "es" system.readVisibilityStoreName: - value: "es" frontend.validSearchAttributes: - value: DomainID: 1 WorkflowID: 1 RunID: 1 WorkflowType: 1 StartTime: 2 ExecutionTime: 2 CloseTime: 2 CloseStatus: 2 HistoryLength: 2 TaskList: 1 IsCron: 4 NumClusters: 2 ClusterAttributeScope: 1 ClusterAttributeName: 1 UpdateTime: 2 CustomStringField: 0 CustomKeywordField: 1 CustomIntField: 2 CustomDoubleField: 3 CustomBoolField: 4 CustomDatetimeField: 5 project: 1 service: 1 environment: 1 addon: 1 addon-type: 1 user: 1 CustomDomain: 1 Operator: 1 RolloutID: 1 CadenceChangeVersion: 1 BinaryChecksums: 1 Passed: 4 ShardID: 2 ExecutionStatus: 2 CronSchedule: 1 ScheduledExecutionTime: 2 system.minRetentionDays: - value: 0 history.EnableConsistentQueryByDomain: - value: true constraints: {} frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true constraints: {} ================================================ FILE: config/dynamicconfig/development_pinot.yaml ================================================ frontend.enableClientVersionCheck: - value: true system.writeVisibilityStoreName: - value: "pinot" system.readVisibilityStoreName: - value: "pinot" system.enableLogCustomerQueryParameter: - value: false frontend.validSearchAttributes: - value: DomainID: 1 WorkflowID: 1 RunID: 1 WorkflowType: 1 StartTime: 2 ExecutionTime: 2 CloseTime: 2 CloseStatus: 2 HistoryLength: 2 TaskList: 1 IsCron: 1 NumClusters: 2 UpdateTime: 2 CustomStringField: 0 CustomKeywordField: 1 CustomIntField: 2 CustomDoubleField: 3 CustomBoolField: 4 CustomDatetimeField: 5 project: 1 service: 1 environment: 1 addon: 1 addon-type: 1 user: 1 CustomDomain: 1 Operator: 1 RolloutID: 1 CadenceChangeVersion: 1 BinaryChecksums: 1 Passed: 4 ShardID: 2 IsDeleted: 4 system.minRetentionDays: - value: 0 history.EnableConsistentQueryByDomain: - value: true constraints: {} ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive.yml ================================================ # This file is used as dynamicconfig override for "activeactive" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activeactive.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_child.yml ================================================ # This file is used as dynamicconfig override for "activeactive" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activeactive.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_cron.yml ================================================ # This file is used as dynamicconfig override for "activeactive_cron" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activeactive_cron.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_invalid_cluster_attribute.yml ================================================ # This file is used as dynamicconfig override for "activeactive" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activeactive.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_regional_failover.yml ================================================ # This file is used as dynamicconfig override for "activeactive_regional_failover" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activeactive_regional_failover.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_regional_failover_start_same_wfid.yml ================================================ # This file is used as dynamicconfig override for "activeactive_regional_failover_start_same_wfid" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activeactive_regional_failover_start_same_wfid.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_regional_failover_start_same_wfid_2.yml ================================================ # This file is used as dynamicconfig override for "activeactive_regional_failover_start_same_wfid_2" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activeactive_regional_failover_start_same_wfid_2.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_same_wfid.yml ================================================ # This file is used as dynamicconfig override for "activeactive" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activeactive.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 5s history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_same_wfid_signalwithstart.yml ================================================ # This file is used as dynamicconfig override for "activeactive_same_wfid_signalwithstart" replication simulation scenario # configured via simulation/replication/testdata/replication_simulation_activeactive_same_wfid_signalwithstart.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_same_wfid_signalwithstart_delayed.yml ================================================ # This file is used as dynamicconfig override for "activeactive_same_wfid_signalwithstart_delayed" replication simulation scenario # configured via simulation/replication/testdata/replication_simulation_activeactive_same_wfid_signalwithstart_delayed.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_signalwithstart_terminateifrunning.yml ================================================ # This file is used as dynamicconfig override for "activeactive" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activeactive.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activeactive_start_terminateifrunning.yml ================================================ # This file is used as dynamicconfig override for "activeactive" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activeactive.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_activepassive_to_activeactive.yml ================================================ # This file is used as dynamicconfig override for "activepassive_to_activeactive" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_activepassive_to_activeactive.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.standbyTaskMissingEventsResendDelay: - value: 5s history.standbyTaskMissingEventsDiscardDelay: - value: 10s history.standbyClusterDelay: - value: 10s history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_budget_manager.yml ================================================ # This file is used as dynamicconfig override for "budget_manager" replication simulation scenario # It enables the replication budget manager to test cache capacity management during replication system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" # Replication task processor settings history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms # Set budget limits for testing # Max size in bytes - set to a moderate value to test capacity enforcement history.replicationBudgetManagerMaxSizeBytes: - value: 1048576 # 1MB constraints: {} # Max count - set to a moderate value to test count enforcement history.replicationBudgetManagerMaxSizeCount: - value: 100 constraints: {} # Soft cap threshold - test fair-share enforcement history.replicationBudgetManagerSoftCapThreshold: - value: 0.8 # 80% soft cap constraints: {} # Cache settings for replication history.replicatorCacheCapacity: - value: 50 constraints: {} history.replicatorCacheMaxSize: - value: 524288 # 512KB per cache constraints: {} ================================================ FILE: config/dynamicconfig/replication_simulation_clusterredirection.yml ================================================ # This file is used as dynamicconfig override for "clusterredirection" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_clusterredirection.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.EnableConsistentQueryByDomain: - value: true history.EnableConsistentQuery: - value: true ================================================ FILE: config/dynamicconfig/replication_simulation_default.yml ================================================ # This file is used as dynamicconfig override for "default" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_default.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms ================================================ FILE: config/dynamicconfig/replication_simulation_reset.yml ================================================ # This file is used as dynamicconfig override for "reset" replication simulation scenario configured via simulation/replication/testdata/replication_simulation_reset.yaml system.writeVisibilityStoreName: - value: "db" system.readVisibilityStoreName: - value: "db" history.replicatorTaskBatchSize: - value: 25 constraints: {} frontend.failoverCoolDown: - value: 5s history.ReplicationTaskProcessorStartWait: # default is 5s. repl task processor sleeps this much before processing received messages. - value: 10ms history.enableTimerQueueV2: - value: true constraints: {} history.enableTransferQueueV2: - value: true constraints: {} ================================================ FILE: docker/README.md ================================================ Quickstart for development with local Cadence server ==================================== **Prerequisite**: [Docker + Docker compose](https://docs.docker.com/engine/installation/) Following steps will bring up the docker container running cadence server along with all its dependencies (cassandra, prometheus, grafana). Exposes cadence frontend on ports 7933 (tchannel) / 7833 (grpc), web on port 8088, and grafana on port 3000. ``` cd $GOPATH/src/github.com/uber/cadence/docker docker compose up ``` > Note: Above command will run with `master-auto-setup` image, which is a changing image all the time. > You can use a released image if you want a stable version. See the below section of "Using a released image". To update your `master-auto-setup` image to the latest version ``` docker pull ubercadence/server:master-auto-setup ``` * View Cadence-Web at http://localhost:8088 * View metrics at http://localhost:3000 Using different docker-compose files ----------------------- By default `docker compose up` will run with `docker-compose.yml` in this folder. This compose file is running with Cassandra, with basic visibility, using Prometheus for emitting metric, with Grafana access. We also provide several other compose files for different features/modes: * docker-compose-es.yml enables advanced visibility with ElasticSearch 6.x * docker-compose-es-v7.yml enables advanced visibility with ElasticSearch 7.x * docker-compose-mysql.yml uses MySQL as persistence storage * docker-compose-postgres.yml uses PostgreSQL as persistence storage * docker-compose-statsd.yaml runs with Statsd+Graphite * docker-compose-multiclusters.yaml runs with 2 cadence clusters For example: ``` docker compose -f docker-compose-mysql.yml up ``` Also feel free to make your own to combine the above features. Run canary and bench(load test) ----------------------- After a local cadence server started, use the below command to run canary ro bench test ``` docker compose -f docker-compose-bench.yml up ``` and ``` docker compose -f docker-compose-canary.yml up ``` Using a released image ----------------------- The above compose files all using master image. It's taking the latest bits on the master branch of this repo. You may want to use more stable version from our release process. With every tagged release of the cadence server, there is also a corresponding docker image that's uploaded to docker hub. In addition, the release will also contain a **docker.tar.gz** file (docker-compose startup scripts). Go [here](https://github.com/cadence-workflow/cadence/releases/latest) to download a latest **docker.tar.gz** Execute the following commands to start a pre-built image along with all dependencies. ``` tar -xzvf docker.tar.gz cd docker docker compose up ``` DIY: Building an image for any tag or branch ----------------------------------------- Replace **YOUR_TAG** and **YOUR_CHECKOUT_BRANCH_OR_TAG** in the below command to build: You can checkout a [release tag](https://github.com/cadence-workflow/cadence/tags) (e.g. v0.21.3) or any branch you are interested. ``` cd $GOPATH/src/github.com/uber/cadence git checkout YOUR_CHECKOUT_BRANCH_OR_TAG docker build . -t ubercadence/:YOUR_TAG ``` You can specify `--build-arg TARGET=` to build different binaries. There are three targets supported: * server. Default target if not specified. This will build a regular server binary. * auto-setup. The image will setup all the DB/ElasticSearch schema during startup. * cli. This image is for [CLI](https://cadenceworkflow.io/docs/cli/). For example of auto-setup images: ``` cd $GOPATH/src/github.com/uber/cadence git checkout YOUR_CHECKOUT_BRANCH docker build . -t ubercadence/server:YOUR_TAG-auto-setup --build-arg TARGET=auto-setup ``` Replace the tag of **image: ubercadence/server** to **YOUR_TAG** in docker-compose.yml . Then stop service and remove all containers using the below commands. ``` docker compose down docker compose up ``` DIY: Troubleshooting docker builds ---------------------------------- Note that Docker has been making changes to its build system, and the new system is currently missing some capabilities that the old one had, and makes major changes to how you control it. When searching for workarounds, make sure you are looking at modern answers, and consider specifically searching for "buildkit" solutions. You can also disable buildkit explicitly with `DOCKER_BUILDKIT=0 docker build ...`. For output limiting (e.g. `[output clipped ...]` messages), or for anything that requires changing buildkit environment variables or other options, start a new builder and use it to build with: ``` # create a new builder with your options docker buildx create ... # which will print out a name, use it in the build step. # now use the exact same command as normal, but it prepends `buildx` and adds a builder flag. docker buildx build . -t ubercadence/:YOUR_TAG --builder ``` For output limiting (e.g. `[output clipped ...]` messages), you can fix this with some buildkit env variables: ``` docker buildx create --driver-opt env.BUILDKIT_STEP_LOG_MAX_SIZE=-1 --driver-opt env.BUILDKIT_STEP_LOG_MAX_SPEED=-1 ``` DIY: Running a custom cadence server locally alongside cadence requirements --------------------------------------------------------------------------- If you want to test out a custom-built cadence server, while running all the normal cadence dependencies, there's a simple workflow to do that: Make your cadence server changes and build using "make bins". Then start everything, stop cadence server, and run your own cadence-server: ``` docker compose up docker stop docker-cadence-1 ./cadence-server start ``` Using docker image for production ========================= In a typical production setting, dependencies (cassandra / statsd server) are managed / started independently of the cadence-server. To use the container in a production setting, use the following docker command: ``` docker run -e CASSANDRA_SEEDS=10.x.x.x -- csv of cassandra server ipaddrs -e CASSANDRA_USER= -- Cassandra username -e CASSANDRA_PASSWORD= -- Cassandra password -e KEYSPACE= -- Cassandra keyspace -e VISIBILITY_KEYSPACE= -- Cassandra visibility keyspace, if using basic visibility -e KAFKA_SEEDS=10.x.x.x -- Kafka broker seed, if using ElasticSearch + Kafka for advanced visibility feature -e CASSANDRA_PROTO_VERSION= -- Cassandra protocol version -e ES_SEEDS=10.x.x.x -- ElasticSearch seed , if using ElasticSearch + Kafka for advanced visibility feature -e RINGPOP_SEEDS=10.x.x.x,10.x.x.x -- csv of ipaddrs for gossip bootstrap -e STATSD_ENDPOINT=10.x.x.x:8125 -- statsd server endpoint -e NUM_HISTORY_SHARDS=1024 -- Number of history shards -e SERVICES=history,matching -- Spinup only the provided services, separated by commas, options are frontend,history,matching and worker -e LOG_LEVEL=debug,info -- Logging level -e DYNAMIC_CONFIG_FILE_PATH= -- Dynamic config file to be watched, default to /etc/cadence/config/dynamicconfig/development.yaml, but you can choose /etc/cadence/config/dynamicconfig/development_es.yaml if using ElasticSearch ubercadence/server: ``` Note that each env variable has a default value, so you don't have to specify it if the default works for you. For more options to configure the docker, please refer to `config_template.yaml`. For ``, use `auto-setup` images only for first initial setup, and use regular ones for production deployment. See the above explanation about `auto-setup`. When upgrading, follow the release instrusctions if version upgrades require some configuration or schema changes. ================================================ FILE: docker/config/bench/development.yaml ================================================ log: stdout: true level: info bench: name: "cadence-bench" domains: ["cadence-bench", "cadence-bench-sync", "cadence-bench-batch"] numTaskLists: 3 cadence: service: "cadence-frontend" host: "${CADENCE_FRONTEND_ADDRESS:host.docker.internal:7933}" # see https://docs.docker.com/desktop/mac/networking/ ================================================ FILE: docker/config/canary/development.yaml ================================================ log: stdout: true level: info canary: domains: ["cadence-canary"] excludes: ["workflow.searchAttributes", "workflow.batch", "workflow.archival.history", "workflow.archival.visibility"] cadence: service: "cadence-frontend" address: "host.docker.internal:7833" # address is for gRPC #host: "host.docker.internal:7933" # for using thrift, replace address with host ================================================ FILE: docker/config_template.yaml ================================================ log: stdout: true level: {{ default .Env.LOG_LEVEL "info" }} persistence: numHistoryShards: {{ default .Env.NUM_HISTORY_SHARDS "4" }} defaultStore: default visibilityStore: visibility {{- $es := default .Env.ENABLE_ES "false" | lower -}} {{- if eq $es "true" }} advancedVisibilityStore: es-visibility {{- end }} datastores: {{- $db := default .Env.DB "cassandra" | lower -}} {{- if or (eq $db "cassandra") (eq $db "scylla") }} default: nosql: pluginName: "cassandra" hosts: {{ default .Env.CASSANDRA_SEEDS "" }} keyspace: {{ default .Env.KEYSPACE "cadence" }} user: {{ default .Env.CASSANDRA_USER "" }} password: {{ default .Env.CASSANDRA_PASSWORD "" }} protoVersion: {{ default .Env.CASSANDRA_PROTO_VERSION "4" }} visibility: nosql: pluginName: "cassandra" hosts: {{ default .Env.CASSANDRA_SEEDS "" }} keyspace: {{ default .Env.VISIBILITY_KEYSPACE "cadence_visibility" }} user: {{ default .Env.CASSANDRA_USER "" }} password: {{ default .Env.CASSANDRA_PASSWORD "" }} protoVersion: {{ default .Env.CASSANDRA_PROTO_VERSION "4" }} {{- else if eq $db "mysql" }} default: sql: pluginName: "mysql" databaseName: {{ default .Env.DBNAME "cadence" }} connectAddr: "{{ default .Env.MYSQL_SEEDS "" }}:{{ default .Env.DB_PORT "3306" }}" connectProtocol: "tcp" user: {{ default .Env.MYSQL_USER "" }} password: {{ default .Env.MYSQL_PWD "" }} {{- if .Env.MYSQL_TX_ISOLATION_COMPAT }} connectAttributes: tx_isolation: 'READ-COMMITTED' {{- end }} visibility: sql: pluginName: "mysql" databaseName: {{ default .Env.VISIBILITY_DBNAME "cadence_visibility" }} connectAddr: "{{ default .Env.MYSQL_SEEDS "" }}:{{ default .Env.DB_PORT "3306" }}" connectProtocol: "tcp" user: {{ default .Env.MYSQL_USER "" }} password: {{ default .Env.MYSQL_PWD "" }} {{- if .Env.MYSQL_TX_ISOLATION_COMPAT }} connectAttributes: tx_isolation: 'READ-COMMITTED' {{- end }} {{- else if eq $db "postgres" }} default: sql: pluginName: "postgres" encodingType: "thriftrw" decodingTypes: ["thriftrw"] databaseName: {{ default .Env.DBNAME "cadence" }} connectAddr: "{{ default .Env.POSTGRES_SEEDS "" }}:{{ default .Env.DB_PORT "5432" }}" connectProtocol: "tcp" user: {{ default .Env.POSTGRES_USER "" }} password: {{ default .Env.POSTGRES_PWD "" }} maxConns: 20 maxIdleConns: 20 maxConnLifetime: "1h" visibility: sql: pluginName: "postgres" encodingType: "thriftrw" decodingTypes: ["thriftrw"] databaseName: {{ default .Env.VISIBILITY_DBNAME "cadence_visibility" }} connectAddr: "{{ default .Env.POSTGRES_SEEDS "" }}:{{ default .Env.DB_PORT "5432" }}" connectProtocol: "tcp" user: {{ default .Env.POSTGRES_USER "" }} password: {{ default .Env.POSTGRES_PWD "" }} maxConns: 20 maxIdleConns: 20 maxConnLifetime: "1h" {{- end }} {{- if eq $es "true" }} es-visibility: elasticsearch: version: {{ default .Env.ES_VERSION "" }} username: {{ default .Env.ES_USER "" }} password: {{ default .Env.ES_PWD "" }} url: scheme: "http" host: "{{ default .Env.ES_SEEDS "" }}:{{ default .Env.ES_PORT "9200" }}" indices: visibility: {{ default .Env.VISIBILITY_NAME "cadence-visibility-dev" }} {{- end }} ringpop: name: cadence broadcastAddress: {{ default .Env.BROADCAST_ADDRESS "" }} bootstrapMode: {{ default .Env.RINGPOP_BOOTSTRAP_MODE "hosts" }} {{- if .Env.RINGPOP_SEEDS }} bootstrapHosts: {{- range $seed := (split .Env.RINGPOP_SEEDS ",") }} - {{ . }} {{- end }} {{- else }} bootstrapHosts: - {{ .Env.HOST_IP }}:{{ default .Env.FRONTEND_PORT "7933" }} - {{ .Env.HOST_IP }}:{{ default .Env.HISTORY_PORT "7934" }} - {{ .Env.HOST_IP }}:{{ default .Env.MATCHING_PORT "7935" }} - {{ .Env.HOST_IP }}:{{ default .Env.WORKER_PORT "7939" }} {{- end }} maxJoinDuration: 30s services: frontend: rpc: port: {{ default .Env.FRONTEND_PORT "7933" }} grpcPort: {{ default .Env.GRPC_FRONTEND_PORT "7833" }} bindOnIP: {{ default .Env.BIND_ON_IP "127.0.0.1" }} {{- if .Env.FRONTEND_HTTP_PORT }} http: port: {{ .Env.FRONTEND_HTTP_PORT }} procedures: {{- range $seed := (split .Env.FRONTEND_HTTP_PROCEDURES ",") }} - {{ . }} {{- end }} {{- end }} {{- if .Env.STATSD_ENDPOINT }} metrics: statsd: hostPort: {{ .Env.STATSD_ENDPOINT }} prefix: {{ default .Env.STATSD_FRONTEND_PREFIX "cadence-frontend" }} {{- else if .Env.PROMETHEUS_ENDPOINT }} metrics: prometheus: timerType: {{ default .Env.PROMETHEUS_TIMER_TYPE "histogram" }} listenAddress: {{ .Env.PROMETHEUS_ENDPOINT }} {{- else if .Env.PROMETHEUS_ENDPOINT_0 }} metrics: prometheus: timerType: {{ default .Env.PROMETHEUS_TIMER_TYPE "histogram" }} listenAddress: {{ .Env.PROMETHEUS_ENDPOINT_0 }} {{- end }} {{- if .Env.FRONTEND_PPROF_PORT }} pprof: port: {{ .Env.FRONTEND_PPROF_PORT }} host: {{ default .Env.BIND_ON_IP "localhost" }} {{- end }} matching: rpc: port: {{ default .Env.MATCHING_PORT "7935" }} grpcPort: {{ default .Env.GRPC_MATCHING_PORT "7835" }} bindOnIP: {{ default .Env.BIND_ON_IP "127.0.0.1" }} {{- if .Env.STATSD_ENDPOINT }} metrics: statsd: hostPort: {{ .Env.STATSD_ENDPOINT }} prefix: {{ default .Env.STATSD_MATCHING_PREFIX "cadence-matching" }} {{- else if .Env.PROMETHEUS_ENDPOINT }} metrics: prometheus: timerType: {{ default .Env.PROMETHEUS_TIMER_TYPE "histogram" }} listenAddress: {{ .Env.PROMETHEUS_ENDPOINT }} {{- else if .Env.PROMETHEUS_ENDPOINT_1 }} metrics: prometheus: timerType: {{ default .Env.PROMETHEUS_TIMER_TYPE "histogram" }} listenAddress: {{ .Env.PROMETHEUS_ENDPOINT_1 }} {{- end }} {{- if .Env.MATCHING_PPROF_PORT }} pprof: port: {{ .Env.MATCHING_PPROF_PORT }} host: {{ default .Env.BIND_ON_IP "localhost" }} {{- end }} history: rpc: port: {{ default .Env.HISTORY_PORT "7934" }} grpcPort: {{ default .Env.GRPC_HISTORY_PORT "7834" }} bindOnIP: {{ default .Env.BIND_ON_IP "127.0.0.1" }} {{- if .Env.STATSD_ENDPOINT }} metrics: statsd: hostPort: {{ .Env.STATSD_ENDPOINT }} prefix: {{ default .Env.STATSD_HISTORY_PREFIX "cadence-history" }} {{- else if .Env.PROMETHEUS_ENDPOINT }} metrics: prometheus: timerType: {{ default .Env.PROMETHEUS_TIMER_TYPE "histogram" }} listenAddress: {{ .Env.PROMETHEUS_ENDPOINT }} {{- else if .Env.PROMETHEUS_ENDPOINT_2 }} metrics: prometheus: timerType: {{ default .Env.PROMETHEUS_TIMER_TYPE "histogram" }} listenAddress: {{ .Env.PROMETHEUS_ENDPOINT_2 }} {{- end }} {{- if .Env.HISTORY_PPROF_PORT }} pprof: port: {{ .Env.HISTORY_PPROF_PORT }} host: {{ default .Env.BIND_ON_IP "localhost" }} {{- end }} worker: rpc: port: {{ default .Env.WORKER_PORT "7939" }} bindOnIP: {{ default .Env.BIND_ON_IP "127.0.0.1" }} {{- if .Env.STATSD_ENDPOINT }} metrics: statsd: hostPort: {{ .Env.STATSD_ENDPOINT }} prefix: {{ default .Env.STATSD_WORKER_PREFIX "cadence-worker" }} {{- else if .Env.PROMETHEUS_ENDPOINT }} metrics: prometheus: timerType: {{ default .Env.PROMETHEUS_TIMER_TYPE "histogram" }} listenAddress: {{ .Env.PROMETHEUS_ENDPOINT }} {{- else if .Env.PROMETHEUS_ENDPOINT_3 }} metrics: prometheus: timerType: {{ default .Env.PROMETHEUS_TIMER_TYPE "histogram" }} listenAddress: {{ .Env.PROMETHEUS_ENDPOINT_3 }} {{- end }} {{- if .Env.WORKER_PPROF_PORT }} pprof: port: {{ .Env.WORKER_PPROF_PORT }} host: {{ default .Env.BIND_ON_IP "localhost" }} {{- end }} clusterGroupMetadata: clusterRedirectionPolicy: policy: {{ default .Env.CLUSTER_REDIRECT_POLICY "all-domain-apis-forwarding" }} failoverVersionIncrement: 10 primaryClusterName: "cluster0" {{- if .Env.IS_NOT_PRIMARY }} currentClusterName: "cluster1" {{- else }} currentClusterName: "cluster0" {{- end }} clusterGroup: cluster0: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: {{ default .Env.PRIMARY_FRONTEND_SERVICE "cadence" }}:{{ default .Env.FRONTEND_PORT "7833" }} rpcTransport: "grpc" authorizationProvider: enable: {{ default .Env.ENABLE_OAUTH "false" }} type: "OAuthAuthorization" privateKey: {{ default .Env.OAUTH_PRIVATE_KEY "" }} {{- if or .Env.ENABLE_GLOBAL_DOMAIN }} cluster1: enabled: true initialFailoverVersion: 2 rpcName: "cadence-frontend" rpcAddress: {{ default .Env.SECONDARY_FRONTEND_SERVICE "cadence-secondary" }}:{{ default .Env.FRONTEND_PORT "7833" }} rpcTransport: "grpc" authorizationProvider: enable: {{ default .Env.ENABLE_OAUTH "false" }} type: "OAuthAuthorization" privateKey: {{ default .Env.OAUTH_PRIVATE_KEY "" }} {{- end }} archival: history: status: {{ default .Env.HISTORY_ARCHIVAL_STATUS "disabled" }} enableRead: {{ default .Env.HISTORY_ARCHIVAL_ENABLE_READ "false" }} provider: filestore: fileMode: {{ default .Env.HISTORY_ARCHIVAL_FILE_MODE "" }} dirMode: {{ default .Env.HISTORY_ARCHIVAL_DIR_MODE "" }} visibility: status: {{ default .Env.VISIBILITY_ARCHIVAL_STATUS "disabled" }} enableRead: {{ default .Env.VISIBILITY_ARCHIVAL_ENABLE_READ "false" }} provider: filestore: fileMode: {{ default .Env.VISIBILITY_ARCHIVAL_FILE_MODE "" }} dirMode: {{ default .Env.VISIBILITY_ARCHIVAL_DIR_MODE "" }} domainDefaults: archival: history: status: {{ default .Env.DOMAIN_DEFAULTS_HISTORY_ARCHIVAL_STATUS "disabled" }} URI: {{ default .Env.DOMAIN_DEFAULTS_HISTORY_ARCHIVAL_URI "" }} visibility: status: {{ default .Env.DOMAIN_DEFAULTS_VISIBILITY_ARCHIVAL_STATUS "disabled" }} URI: {{ default .Env.DOMAIN_DEFAULTS_VISIBILITY_ARCHIVAL_URI "" }} kafka: tls: enabled: false clusters: test: brokers: - {{ default .Env.KAFKA_SEEDS "" }}:{{ default .Env.KAFKA_PORT "9092" }} topics: {{ default .Env.VISIBILITY_NAME "cadence-visibility-dev" }}: cluster: test {{ default .Env.VISIBILITY_NAME "cadence-visibility-dev" }}-dlq: cluster: test applications: visibility: topic: {{ default .Env.VISIBILITY_NAME "cadence-visibility-dev" }} dlq-topic: {{ default .Env.VISIBILITY_NAME "cadence-visibility-dev" }}-dlq publicClient: {{- if .Env.IS_NOT_PRIMARY }} hostPort: {{ default .Env.SECONDARY_FRONTEND_SERVICE "cadence" }}:{{ default .Env.FRONTEND_PORT "7833" }} {{- else }} hostPort: {{ default .Env.PRIMARY_FRONTEND_SERVICE "cadence" }}:{{ default .Env.FRONTEND_PORT "7833" }} {{- end }} dynamicconfig: client: filebased filebased: filepath: {{ default .Env.DYNAMIC_CONFIG_FILE_PATH "/etc/cadence/config/dynamicconfig/development.yaml" }} pollInterval: "60s" blobstore: filestore: outputDirectory: {{ default .Env.FILE_BLOB_STORE_OUTPUT_DIRECTYORY "" }} authorization: oauthAuthorizer: enable: {{ default .Env.ENABLE_OAUTH "false" }} maxJwtTTL: {{ default .Env.OAUTH_MAX_JWT_TTL "86400" }} jwtCredentials: algorithm: "RS256" publicKey: {{ default .Env.OAUTH_PUBLIC_KEY "" }} {{- if .Env.ASYNC_WF_KAFKA_QUEUE_ENABLED }} asyncWorkflowQueues: queue1: type: "kafka" config: connection: brokers: - {{ default .Env.KAFKA_SEEDS "" }}:{{ default .Env.KAFKA_PORT "9092" }} topic: {{ default .Env.ASYNC_WF_KAFKA_QUEUE_TOPIC "default-topic" }} {{- end }} shard-distributor-matching: namespaces: - namespace: {{ default .Env.SHARD_DISTRIBUTOR_MATCHING_NAMESPACE "cadence-matching" }} heartbeat_interval: {{ default .Env.SHARD_DISTRIBUTOR_MATCHING_HEARTBEAT_INTERVAL "1s" }} migration_mode: {{ default .Env.SHARD_DISTRIBUTOR_MATCHING_MIGRATION_MODE "local_pass" }} ttl_shard: {{ default .Env.SHARD_DISTRIBUTOR_MATCHING_TTL_SHARD "5m" }} ttl_report: {{ default .Env.SHARD_DISTRIBUTOR_MATCHING_TTL_REPORT "1m" }} ================================================ FILE: docker/dev/cassandra-esv7-kafka.yml ================================================ version: '3' services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3 ports: - "9200:9200" environment: - discovery.type=single-node kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" ================================================ FILE: docker/dev/cassandra-opensearch-kafka-migration.yml ================================================ version: '3' services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" opensearch: image: opensearchproject/opensearch:2.13.0 environment: - discovery.type=single-node - OPENSEARCH_SECURITY_SSL_HTTP_ENABLED=false - cluster.name=opensearch-cluster - bootstrap.memory_lock=true - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" - OPENSEARCH_INITIAL_ADMIN_PASSWORD=DevTestInitial123! # Sets the demo admin user password when using demo configuration, required for OpenSearch 2.12 and later ports: - 9200:9200 # REST API - 9600:9600 # Performance Analyzer elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3 ports: - "9201:9200" environment: - discovery.type=single-node opensearch-dashboards: image: opensearchproject/opensearch-dashboards:2.13.0 container_name: opensearch-dashboards ports: - 5601:5601 expose: - "5601" # Expose port 5601 for web access to OpenSearch Dashboards environment: OPENSEARCH_HOSTS: '["https://opensearch:9200"]' # Define the OpenSearch nodes that OpenSearch Dashboards will query kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" ================================================ FILE: docker/dev/cassandra-opensearch-kafka.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" opensearch: image: opensearchproject/opensearch:2.13.0 environment: - discovery.type=single-node - OPENSEARCH_SECURITY_SSL_HTTP_ENABLED=false - "DISABLE_SECURITY_PLUGIN=true" - cluster.name=opensearch-cluster - bootstrap.memory_lock=true - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" - OPENSEARCH_INITIAL_ADMIN_PASSWORD=DevTestInitial123! # Sets the demo admin user password when using demo configuration, required for OpenSearch 2.12 and later ports: - 9200:9200 # REST API - 9600:9600 # Performance Analyzer opensearch-dashboards: image: opensearchproject/opensearch-dashboards:2.13.0 container_name: opensearch-dashboards ports: - 5601:5601 expose: - "5601" # Expose port 5601 for web access to OpenSearch Dashboards environment: OPENSEARCH_HOSTS: '["http://opensearch:9200"]' # Define the OpenSearch nodes that OpenSearch Dashboards will query DISABLE_SECURITY_DASHBOARDS_PLUGIN: 'true' # Disable security for dashboards kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/dev/cassandra-pinot-kafka.yml ================================================ version: '3' services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" zookeeper: image: zookeeper:3.5.8 container_name: zookeeper ports: - "2181:2181" environment: - ZOOKEEPER_CLIENT_PORT=2181 - ZOOKEEPER_TICK_TIME=2000 pinot-controller: image: apachepinot/pinot:1.1.0 command: "StartController -zkAddress zookeeper:2181 -controllerPort 9001" container_name: pinot-controller restart: unless-stopped ports: - "9001:9001" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms1G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-controller.log" depends_on: - zookeeper pinot-broker: image: apachepinot/pinot:1.1.0 command: "StartBroker -zkAddress zookeeper:2181" restart: unless-stopped container_name: "pinot-broker" ports: - "8099:8099" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms4G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-broker.log" depends_on: - pinot-controller pinot-server: image: apachepinot/pinot:1.1.0 command: "StartServer -zkAddress zookeeper:2181" restart: unless-stopped container_name: "pinot-server" ports: - "8098:8098" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms4G -Xmx16G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-server.log" depends_on: - pinot-broker kafka: image: docker.io/bitnamilegacy/kafka:3.7 restart: unless-stopped container_name: "kafka" ports: - "9092:9092" - "9093:9093" environment: - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=PLAINTEXT:PLAINTEXT,OUTSIDE:PLAINTEXT - KAFKA_CFG_LISTENERS=PLAINTEXT://:9093,OUTSIDE://:9092 - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9093,OUTSIDE://localhost:9092 - KAFKA_CFG_BROKER_ID=0 - ALLOW_PLAINTEXT_LISTENER=yes depends_on: - zookeeper ================================================ FILE: docker/dev/cassandra-testing/docker-compose-local-caas-cluster.yaml ================================================ networks: cassandra-net: driver: bridge services: cassandra-1: image: "cassandra:4.1.3" # cassandra:4.1.3 container_name: "cassandra-1" ports: - 7000:7000 - 9042:9042 networks: - cassandra-net environment: - CASSANDRA_START_RPC=true # default - CASSANDRA_RPC_ADDRESS=0.0.0.0 # default - CASSANDRA_LISTEN_ADDRESS=auto # default, use IP addr of container # = CASSANDRA_BROADCAST_ADDRESS - CASSANDRA_CLUSTER_NAME=my-cluster - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch - CASSANDRA_DC=my-datacenter-1 volumes: - cassandra-node-1:/var/lib/cassandra:rw healthcheck: test: ["CMD-SHELL", "nodetool status", ";", "sleep", "40"] interval: 2m start_period: 2m timeout: 10s retries: 3 cassandra-2: image: "cassandra:4.1.3" # cassandra:4.1.3 container_name: "cassandra-2" ports: - 9043:9042 networks: - cassandra-net environment: - CASSANDRA_START_RPC=true # default - CASSANDRA_RPC_ADDRESS=0.0.0.0 # default - CASSANDRA_LISTEN_ADDRESS=auto # default, use IP addr of container # = CASSANDRA_BROADCAST_ADDRESS - CASSANDRA_CLUSTER_NAME=my-cluster - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch - CASSANDRA_DC=my-datacenter-1 - CASSANDRA_SEEDS=cassandra-1 depends_on: cassandra-1: condition: service_healthy volumes: - cassandra-node-2:/var/lib/cassandra:rw healthcheck: test: ["CMD-SHELL", "nodetool status"] interval: 2m start_period: 2m timeout: 10s retries: 3 cassandra-3: image: "cassandra:4.1.3" # cassandra:4.1.3 container_name: "cassandra-3" ports: - 9044:9042 networks: - cassandra-net environment: - CASSANDRA_START_RPC=true # default - CASSANDRA_RPC_ADDRESS=0.0.0.0 # default - CASSANDRA_LISTEN_ADDRESS=auto # default, use IP addr of container # = CASSANDRA_BROADCAST_ADDRESS - CASSANDRA_CLUSTER_NAME=my-cluster - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch - CASSANDRA_DC=my-datacenter-1 - CASSANDRA_SEEDS=cassandra-1 depends_on: cassandra-2: condition: service_healthy volumes: - cassandra-node-3:/var/lib/cassandra:rw healthcheck: test: ["CMD-SHELL", "nodetool status"] interval: 2m start_period: 2m timeout: 10s retries: 3 volumes: cassandra-node-1: cassandra-node-2: cassandra-node-3: ================================================ FILE: docker/dev/cassandra.yml ================================================ version: '3' services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" ================================================ FILE: docker/dev/mongo-esv7-kafka.yml ================================================ version: '3' services: mongo: image: mongo:5 restart: always ports: - 27017:27017 environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: cadence mongo-express: image: mongo-express restart: always ports: - 8081:8081 environment: ME_CONFIG_MONGODB_ADMINUSERNAME: root ME_CONFIG_MONGODB_ADMINPASSWORD: cadence ME_CONFIG_MONGODB_URL: mongodb://root:cadence@mongo:27017/ elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3 ports: - "9200:9200" environment: - discovery.type=single-node kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" ================================================ FILE: docker/dev/mysql-esv7-kafka.yml ================================================ version: '3' services: mysql: image: mysql:8.0 ports: - "3306:3306" environment: - "MYSQL_ROOT_PASSWORD=cadence" elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3 ports: - "9200:9200" environment: - discovery.type=single-node kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" ================================================ FILE: docker/dev/mysql.yml ================================================ version: '3' services: mysql: image: mysql:8.0 ports: - "3306:3306" environment: - "MYSQL_ROOT_PASSWORD=cadence" ================================================ FILE: docker/dev/postgres.yml ================================================ version: '3' services: postgres: image: postgres:17.4 environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: cadence ports: - "5432:5432" ================================================ FILE: docker/docker-compose-archival-filestore.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' node-exporter: image: prom/node-exporter ports: - '9100:9100' cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" - "7936:7936" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" - "FRONTEND_PPROF_PORT=7936" - "LOG_LEVEL=debug" - "HISTORY_ARCHIVAL_STATUS=enabled" - "HISTORY_ARCHIVAL_ENABLE_READ=true" - "HISTORY_ARCHIVAL_FILE_MODE=0100644" - "HISTORY_ARCHIVAL_DIR_MODE=0040000" - "DOMAIN_DEFAULTS_HISTORY_ARCHIVAL_STATUS=enabled" - "DOMAIN_DEFAULTS_HISTORY_ARCHIVAL_URI=file:///etc/cadence/archival/history" - "VISIBILITY_ARCHIVAL_STATUS=enabled" - "VISIBILITY_ARCHIVAL_ENABLE_READ=true" - "VISIBILITY_ARCHIVAL_FILE_MODE=0100644" - "VISIBILITY_ARCHIVAL_DIR_MODE=0040000" - "DOMAIN_DEFAULTS_VISIBILITY_ARCHIVAL_STATUS=enabled" - "DOMAIN_DEFAULTS_VISIBILITY_ARCHIVAL_URI=file:///etc/cadence/archival/visibility" volumes: - ./archival:/etc/cadence/archival depends_on: cassandra: condition: service_healthy prometheus: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-async-wf-kafka-v4.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' node-exporter: image: prom/node-exporter ports: - '9100:9100' cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "ASYNC_WF_KAFKA_QUEUE_ENABLED=true" - "ASYNC_WF_KAFKA_QUEUE_TOPIC=async-wf-topic1" - "KAFKA_SEEDS=kafka" - "KAFKA_PORT=9092" - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" - "LOG_LEVEL=debug" depends_on: cassandra: condition: service_healthy prometheus: condition: service_started kafka: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' kafka: image: apache/kafka:4.0.0 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" ================================================ FILE: docker/docker-compose-async-wf-kafka.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' node-exporter: image: prom/node-exporter ports: - '9100:9100' cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "ASYNC_WF_KAFKA_QUEUE_ENABLED=true" - "ASYNC_WF_KAFKA_QUEUE_TOPIC=async-wf-topic1" - "KAFKA_SEEDS=kafka" - "KAFKA_PORT=9092" - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" - "LOG_LEVEL=debug" depends_on: cassandra: condition: service_healthy prometheus: condition: service_started kafka: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" ================================================ FILE: docker/docker-compose-bench.yml ================================================ services: cadence-bench: image: ubercadence/cadence-bench:master volumes: - ./config/bench:/etc/cadence-bench/config/bench ================================================ FILE: docker/docker-compose-canary.yml ================================================ services: cadence-canary: image: ubercadence/cadence-canary:master volumes: - ./config/canary:/etc/cadence-canary/config/canary environment: - "CADENCE_CANARY_MODE=all" # this will run both worker and cron starter ================================================ FILE: docker/docker-compose-es-v7.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3 ports: - "9200:9200" environment: - discovery.type=single-node cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development_es.yaml" - "ENABLE_ES=true" - "ES_SEEDS=elasticsearch" - "ES_VERSION=v7" - "KAFKA_SEEDS=kafka" depends_on: cassandra: condition: service_healthy prometheus: condition: service_started kafka: condition: service_started elasticsearch: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-es.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.22 ports: - "9200:9200" environment: - discovery.type=single-node cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development_es.yaml" - "ENABLE_ES=true" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" depends_on: cassandra: condition: service_healthy prometheus: condition: service_started kafka: condition: service_started elasticsearch: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-http-api.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' node-exporter: image: prom/node-exporter ports: - '9100:9100' cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" - "8800:8800" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" - "FRONTEND_HTTP_PORT=8800" - "FRONTEND_HTTP_PROCEDURES=uber.cadence.api.v1.WorkflowAPI::StartWorkflowExecution,uber.cadence.api.v1.WorkflowAPI::SignalWorkflowExecution,uber.cadence.api.v1.WorkflowAPI::QueryWorkflow,uber.cadence.api.v1.WorkflowAPI::DescribeWorkflowExecution,uber.cadence.api.v1.VisibilityAPI::ListWorkflowExecutions" depends_on: cassandra: condition: service_healthy prometheus: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-multiclusters-cass-mysql-es.yaml ================================================ version: '3' services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 mysql: image: mysql:8.0 ports: - "3306:3306" environment: - "MYSQL_ROOT_PASSWORD=root" prometheus: image: prom/prometheus:latest volumes: - ./prometheus_multiclusters:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.22 ports: - "9200:9200" environment: - discovery.type=single-node cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development_es.yaml" - "ENABLE_GLOBAL_DOMAIN=true" - "KEYSPACE=cadence_primary" - "VISIBILITY_KEYSPACE=cadence_visibility_primary" - "STATSD_FRONTEND_PREFIX=cadence-frontend-primary" - "STATSD_MATCHING_PREFIX=cadence-matching-primary" - "STATSD_HISTORY_PREFIX=cadence-history-primary" - "STATSD_WORKER_PREFIX=cadence-worker-primary" - "CLUSTER_REDIRECT_POLICY=selected-apis-forwarding" - "ENABLE_ES=true" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "VISIBILITY_NAME=cadence-visibility-primary" - "PRIMARY_FRONTEND_SERVICE=cadence" depends_on: cassandra: condition: service_healthy prometheus: condition: service_started kafka: condition: service_started elasticsearch: condition: service_started cadence-secondary: image: ubercadence/server:master-auto-setup ports: - "9001:9001" - "9002:9002" - "9003:9003" - "9004:9004" - "7943:7933" - "7944:7934" - "7945:7935" - "7949:7939" - "7843:7833" environment: - "DB=mysql" - "MYSQL_USER=root" - "MYSQL_PWD=root" - "MYSQL_SEEDS=mysql" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:9001" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:9002" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:9003" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:9004" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development_es.yaml" - "IS_NOT_PRIMARY=true" - "ENABLE_GLOBAL_DOMAIN=true" - "STATSD_FRONTEND_PREFIX=cadence-frontend-secondary" - "STATSD_MATCHING_PREFIX=cadence-matching-secondary" - "STATSD_HISTORY_PREFIX=cadence-history-secondary" - "STATSD_WORKER_PREFIX=cadence-worker-secondary" - "CLUSTER_REDIRECT_POLICY=selected-apis-forwarding" - "ENABLE_ES=true" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "VISIBILITY_NAME=cadence-visibility-secondary" - "SECONDARY_FRONTEND_SERVICE=cadence-secondary" depends_on: - mysql - prometheus - kafka - elasticsearch cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833,cadence-secondary:7833" - "CADENCE_GRPC_SERVICES_NAMES=cadence-frontend,cadence-frontend" - "CADENCE_CLUSTERS_NAMES=cluster0,cluster1" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence - cadence-secondary grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-multiclusters-es.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest volumes: - ./prometheus_multiclusters:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.22 ports: - "9200:9200" environment: - discovery.type=single-node cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development_es.yaml" - "ENABLE_GLOBAL_DOMAIN=true" - "KEYSPACE=cadence_primary" - "VISIBILITY_KEYSPACE=cadence_visibility_primary" - "STATSD_FRONTEND_PREFIX=cadence-frontend-primary" - "STATSD_MATCHING_PREFIX=cadence-matching-primary" - "STATSD_HISTORY_PREFIX=cadence-history-primary" - "STATSD_WORKER_PREFIX=cadence-worker-primary" - "CLUSTER_REDIRECT_POLICY=selected-apis-forwarding" - "ENABLE_ES=true" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "VISIBILITY_NAME=cadence-visibility-primary" - "PRIMARY_FRONTEND_SERVICE=cadence" depends_on: cassandra: condition: service_healthy prometheus: condition: service_started kafka: condition: service_started elasticsearch: condition: service_started cadence-secondary: image: ubercadence/server:master-auto-setup ports: - "9001:9001" - "9002:9002" - "9003:9003" - "9004:9004" - "7943:7933" - "7944:7934" - "7945:7935" - "7949:7939" - "7843:7833" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:9001" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:9002" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:9003" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:9004" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development_es.yaml" - "IS_NOT_PRIMARY=true" - "ENABLE_GLOBAL_DOMAIN=true" - "KEYSPACE=cadence_secondary" - "VISIBILITY_KEYSPACE=cadence_visibility_secondary" - "STATSD_FRONTEND_PREFIX=cadence-frontend-secondary" - "STATSD_MATCHING_PREFIX=cadence-matching-secondary" - "STATSD_HISTORY_PREFIX=cadence-history-secondary" - "STATSD_WORKER_PREFIX=cadence-worker-secondary" - "CLUSTER_REDIRECT_POLICY=selected-apis-forwarding" - "ENABLE_ES=true" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "VISIBILITY_NAME=cadence-visibility-secondary" - "SECONDARY_FRONTEND_SERVICE=cadence-secondary" depends_on: cassandra: condition: service_healthy prometheus: condition: service_started kafka: condition: service_started elasticsearch: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833,cadence-secondary:7833" - "CADENCE_GRPC_SERVICES_NAMES=cadence-frontend,cadence-frontend" - "CADENCE_CLUSTERS_NAMES=cluster0,cluster1" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence - cadence-secondary grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-multiclusters.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 # Setup schemas for primary and secondary clusters cadence-schema-setup: image: ubercadence/server:master-auto-setup environment: - "CASSANDRA_SEEDS=cassandra" - "CASSANDRA_USER=cassandra" - "CASSANDRA_PASSWORD=cassandra" - "CASSANDRA_PROTO_VERSION=4" - "RF=1" - "PRIMARY_KEYSPACE=cadence_primary" - "PRIMARY_VISIBILITY_KEYSPACE=cadence_visibility_primary" - "SECONDARY_KEYSPACE=cadence_secondary" - "SECONDARY_VISIBILITY_KEYSPACE=cadence_visibility_secondary" volumes: - ./setup-multiclusters-schema.sh:/setup-multiclusters-schema.sh command: ["/setup-multiclusters-schema.sh"] restart: "no" depends_on: cassandra: condition: service_healthy prometheus: image: prom/prometheus:latest volumes: - ./prometheus_multiclusters:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "BIND_ON_IP=0.0.0.0" - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" - "ENABLE_GLOBAL_DOMAIN=true" - "KEYSPACE=cadence_primary" - "VISIBILITY_KEYSPACE=cadence_visibility_primary" - "STATSD_FRONTEND_PREFIX=cadence-frontend-primary" - "STATSD_MATCHING_PREFIX=cadence-matching-primary" - "STATSD_HISTORY_PREFIX=cadence-history-primary" - "STATSD_WORKER_PREFIX=cadence-worker-primary" - "CLUSTER_REDIRECT_POLICY=selected-apis-forwarding" depends_on: cassandra: condition: service_healthy cadence-schema-setup: condition: service_completed_successfully prometheus: condition: service_started cadence-secondary: image: ubercadence/server:master-auto-setup ports: - "9001:9001" - "9002:9002" - "9003:9003" - "9004:9004" - "7943:7933" - "7944:7934" - "7945:7935" - "7949:7939" - "7843:7833" environment: - "BIND_ON_IP=0.0.0.0" - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:9001" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:9002" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:9003" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:9004" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" - "IS_NOT_PRIMARY=true" - "ENABLE_GLOBAL_DOMAIN=true" - "KEYSPACE=cadence_secondary" - "VISIBILITY_KEYSPACE=cadence_visibility_secondary" - "STATSD_FRONTEND_PREFIX=cadence-frontend-secondary" - "STATSD_MATCHING_PREFIX=cadence-matching-secondary" - "STATSD_HISTORY_PREFIX=cadence-history-secondary" - "STATSD_WORKER_PREFIX=cadence-worker-secondary" - "CLUSTER_REDIRECT_POLICY=selected-apis-forwarding" depends_on: cassandra: condition: service_healthy cadence-schema-setup: condition: service_completed_successfully prometheus: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833,cadence-secondary:7833" - "CADENCE_GRPC_SERVICES_NAMES=cadence-frontend,cadence-frontend" - "CADENCE_CLUSTERS_NAMES=cluster0,cluster1" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence - cadence-secondary grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-mysql.yml ================================================ services: mysql: platform: linux/amd64 image: mysql:8.0 ports: - "3306:3306" environment: - "MYSQL_ROOT_PASSWORD=root" prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "DB=mysql" - "MYSQL_USER=root" - "MYSQL_PWD=root" - "MYSQL_SEEDS=mysql" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" depends_on: - mysql - prometheus cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-oauth.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' node-exporter: image: prom/node-exporter ports: - '9100:9100' cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" - "ENABLE_OAUTH=true" - "OAUTH_PUBLIC_KEY=config/credentials/keytest.pub" - "OAUTH_PRIVATE_KEY=config/credentials/keytest" depends_on: - cassandra - prometheus cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-opensearch.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" opensearch: image: opensearchproject/opensearch:2.13.0 ports: - "9200:9200" - "9600:9600" environment: - discovery.type=single-node - OPENSEARCH_SECURITY_SSL_HTTP_ENABLED=false - DISABLE_SECURITY_PLUGIN=true - cluster.name=opensearch-cluster - bootstrap.memory_lock=true - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" opensearch-dashboards: image: opensearchproject/opensearch-dashboards:2.13.0 ports: - "5601:5601" environment: OPENSEARCH_HOSTS: '["http://opensearch:9200"]' DISABLE_SECURITY_DASHBOARDS_PLUGIN: "true" depends_on: - opensearch cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development_es.yaml" - "ENABLE_ES=true" - "ES_SEEDS=opensearch" - "ES_VERSION=os2" - "KAFKA_SEEDS=kafka" depends_on: cassandra: condition: service_healthy prometheus: condition: service_started kafka: condition: service_started opensearch: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-pinot.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" zookeeper: image: zookeeper:3.5.8 container_name: zookeeper ports: - "2181:2181" environment: ZOOKEEPER_CLIENT_PORT: 2181 ZOOKEEPER_TICK_TIME: 2000 pinot-controller: image: apachepinot/pinot:latest command: "StartController -zkAddress zookeeper:2181" container_name: pinot-controller restart: unless-stopped ports: - "9000:9000" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms1G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-controller.log" depends_on: - zookeeper pinot-broker: image: apachepinot/pinot:latest command: "StartBroker -zkAddress zookeeper:2181" restart: unless-stopped container_name: "pinot-broker" ports: - "8099:8099" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms4G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-broker.log" depends_on: - pinot-controller pinot-server: image: apachepinot/pinot:latest command: "StartServer -zkAddress zookeeper:2181" restart: unless-stopped container_name: "pinot-server" ports: - "8098:8098" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms4G -Xmx16G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-server.log" depends_on: - pinot-broker cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development_es.yaml" - "ENABLE_ES=true" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" depends_on: - cassandra - prometheus - kafka - elasticsearch cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-postgres.yml ================================================ services: postgres: image: postgres:17.4 environment: POSTGRES_USER: cadence POSTGRES_PASSWORD: cadence ports: - "5432:5432" prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "DB=postgres" - "DB_PORT=5432" - "POSTGRES_USER=cadence" - "POSTGRES_PWD=cadence" - "POSTGRES_SEEDS=postgres" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" depends_on: - postgres - prometheus cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-scylla.yml ================================================ services: scylla: image: scylladb/scylla:4.3.1 command: - "--smp" - "1" ports: - "9042:9042" prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "DB=scylla" - "CASSANDRA_SEEDS=scylla" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" depends_on: - scylla - prometheus cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/docker-compose-statsd.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 statsd: image: graphiteapp/graphite-statsd ports: - "8080:80" - "2003:2003" - "8125:8125" - "8126:8126" cadence: image: ubercadence/server:master-auto-setup ports: - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" environment: - "CASSANDRA_SEEDS=cassandra" - "STATSD_ENDPOINT=statsd:8125" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" depends_on: cassandra: condition: service_healthy statsd: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence ================================================ FILE: docker/docker-compose.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' node-exporter: image: prom/node-exporter ports: - '9100:9100' cadence: image: ubercadence/server:master-auto-setup ports: - "8000:8000" - "8001:8001" - "8002:8002" - "8003:8003" - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" - "7833:7833" - "7936:7936" environment: - "CASSANDRA_SEEDS=cassandra" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" - "FRONTEND_PPROF_PORT=7936" - "LOG_LEVEL=debug" depends_on: cassandra: condition: service_healthy prometheus: condition: service_started cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence:7833" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence grafana: image: grafana/grafana volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' ================================================ FILE: docker/domain/cassandra.cql ================================================ -- Insert into domains table INSERT INTO domains ( id, domain ) VALUES ( 123e4567-e89b-12d3-a456-426614174000, -- Replace with your UUID { id: 123e4567-e89b-12d3-a456-426614174000, -- Replace with your UUID name: 'default', status: 0, -- Registered description: 'This is an example domain.', data: {'key1': 'value1', 'key2': 'value2'}, owner_email: 'owner@example.com' } ) IF NOT EXISTS; -- Insert into domains_by_name_v2 table INSERT INTO domains_by_name_v2 ( domains_partition, name, domain, config, replication_config, is_global_domain, config_version, failover_version, failover_notification_version, notification_version ) VALUES ( 0, 'default', { id: 123e4567-e89b-12d3-a456-426614174000, -- Replace with your UUID name: 'default', status: 0, -- Registered description: 'This is an example domain.', data: {'key1': 'value1', 'key2': 'value2'}, owner_email: 'owner@example.com' }, { retention: 7, emit_metric: True, history_archival_status: 0, -- Default to disabled visibility_archival_status: 0 -- Default to disabled }, { active_cluster_name: 'cluster0', clusters: [{cluster_name: 'cluster0'}] }, True, -- is_global_domain 1, -- config_version 0, -- failover_version 0, -- failover_notification_version 0 -- notification_version ) IF NOT EXISTS; ================================================ FILE: docker/domain/mysql.sql ================================================ -- Insert into domains table INSERT INTO domains ( shard_id, id, name, data, data_encoding, is_global ) VALUES ( 54321, -- Default shard_id UNHEX(REPLACE(UUID(), '-', '')), -- Generate a 16-byte UUID 'default', -- Domain name '{"key1":"value1","key2":"value2"}', -- Example data as JSON 'json', -- Encoding type 1 -- Set to 1 for a global domain ) ON DUPLICATE KEY UPDATE name = VALUES(name); -- Insert into domain_metadata table INSERT INTO domain_metadata ( notification_version ) VALUES ( 1 ) ON DUPLICATE KEY UPDATE notification_version = VALUES(notification_version); ================================================ FILE: docker/domain/postgres.sql ================================================ -- Insert into domains table INSERT INTO domains ( shard_id, id, name, data, data_encoding, is_global ) VALUES ( 54321, -- Default shard_id gen_random_uuid(), -- Generate a UUID for the domain ID 'default', -- Domain name '{"key1":"value1","key2":"value2"}', -- Example JSON data 'json', -- Encoding type TRUE -- Set to TRUE for a global domain ) ON CONFLICT (shard_id, id) DO UPDATE SET name = EXCLUDED.name, data = EXCLUDED.data, data_encoding = EXCLUDED.data_encoding, is_global = EXCLUDED.is_global; -- Insert into domain_metadata table INSERT INTO domain_metadata ( id, notification_version ) VALUES ( 1, -- Default ID 1 -- Notification version ) ON CONFLICT (id) DO UPDATE SET notification_version = EXCLUDED.notification_version; ================================================ FILE: docker/entrypoint.sh ================================================ #!/bin/bash HOST_IP=`hostname -i` export HOST_IP if [ "$BIND_ON_LOCALHOST" == true ] || [ "$BIND_ON_IP" == "127.0.0.1" ]; then export BIND_ON_IP="127.0.0.1" export HOST_IP="127.0.0.1" elif [ -z "$BIND_ON_IP" ]; then # not binding to localhost and bind_on_ip is empty - use default host ip addr export BIND_ON_IP=$HOST_IP elif [ "$BIND_ON_IP" != "0.0.0.0" ]; then # binding to a user specified addr, make sure HOST_IP also uses the same addr export HOST_IP=$BIND_ON_IP fi # this env variable is deprecated export BIND_ON_LOCALHOST=false exec "$@" ================================================ FILE: docker/github_actions/Dockerfile ================================================ FROM golang:1.23.4-bullseye # Tried to set Python to ignore warnings due to the instructions at this link: # https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation # But this causes all the pip installs to fail, so don't do this: # ENV PYTHONWARNINGS=ignore::yaml.YAMLLoadWarning # ENV PYTHONWARNINGS=ignore RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ gettext-base \ libyaml-dev \ python3-pip \ python-setuptools \ time \ unzip \ ca-certificates \ && rm -rf /var/lib/apt/lists/* RUN pip3 install cqlsh && cqlsh --version # verbose test output from `make`, can be disabled with V=0 ENV V=0 # allow git-status and similar to work RUN git config --global --add safe.directory /cadence # https://github.com/docker-library/golang/blob/c1baf037d71331eb0b8d4c70cff4c29cf124c5e0/1.4/Dockerfile RUN mkdir -p /cadence WORKDIR /cadence # Copy go mod dependencies and try to share the module download cache COPY go.* /cadence COPY internal/tools/go.* /cadence/internal/tools/ COPY cmd/server/go.* /cadence/cmd/server/ COPY common/archiver/gcloud/go.* /cadence/common/archiver/gcloud/ # go.work means this downloads everything, not just the top module RUN go mod download ================================================ FILE: docker/github_actions/README.md ================================================ # Running tests ## Testing the build locally To try out the build locally, start from the root folder of this repo (cadence) and run the following commands. unit tests: ```bash docker compose -f docker/github_actions/docker-compose-local.yml build unit-test ``` NOTE: You would expect TestServerStartup to fail here as we don't have a way to install the schema like we do in pipeline.yml integration tests: ```bash docker compose -f docker/github_actions/docker-compose-local.yml build integration-test-cassandra ``` cross DC integration tests: ```bash docker compose -f docker/github_actions/docker-compose-local.yml build integration-test-ndc-cassandra ``` Run the integration tests: unit tests: ```bash docker compose -f docker/github_actions/docker-compose-local.yml run unit-test ``` integration tests: ```bash docker compose -f docker/github_actions/docker-compose-local.yml run integration-test-cassandra ``` cross DC integration tests: ```bash docker compose -f docker/github_actions/docker-compose-local.yml run integration-test-ndc-cassandra ``` ## Testing the build in Github Actions Creating a PR against the master branch will trigger the Github Actions build. Members of the Cadence team can view the build pipeline here on Github. Eventually this pipeline should be made public. It will need to ignore third party PRs for safety reasons. ================================================ FILE: docker/github_actions/docker-compose-cassandra-lwt.yml ================================================ services: cass1: container_name: cass1 hostname: cass1 image: cassandra:4.1.1 ports: - "9042:9042" environment: &environment # Declare and save environments variables into "environment" MAX_HEAP_SIZE: 256M HEAP_NEWSIZE: 128M CASSANDRA_SEEDS: "cass1,cass2" # The first two nodes will be seeds CASSANDRA_CLUSTER_NAME: SolarSystem CASSANDRA_DC: Mars CASSANDRA_RACK: West CASSANDRA_ENDPOINT_SNITCH: GossipingPropertyFileSnitch CASSANDRA_NUM_TOKENS: 128 healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 networks: services-network: aliases: - integration-test cass2: container_name: cass2 hostname: cass2 image: cassandra:4.1.1 ports: - "9043:9042" environment: *environment # point to "environment" to use the same environment variables as cass1 healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 networks: services-network: aliases: - integration-test test-cass-lwt: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: - /bin/sh - -e - -c - > go test -timeout 180s -run ^TestCassandraLWT$ -count 1 -v -tags cassandralwt github.com/uber/cadence/host | tee test.log ports: - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" environment: # - "CASSANDRA_HOST=cass1" - "CASSANDRA=1" - "CASSANDRA_SEEDS=cass1,cass2" depends_on: cass1: condition: service_healthy cass2: condition: service_healthy volumes: - ../../:/cadence networks: services-network: aliases: - integration-test networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose-es7.yml ================================================ services: cassandra: image: cassandra:4.1.1 environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" networks: services-network: aliases: - cassandra healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" networks: services-network: aliases: - kafka elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3 networks: services-network: aliases: - elasticsearch environment: - discovery.type=single-node integration-test-cassandra: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" - "ES_VERSION=v7" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose-local-async-wf.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" networks: services-network: aliases: - cassandra environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" networks: services-network: aliases: - kafka integration-test-async-wf: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: - /bin/sh - -e - -c - > go test -timeout 60s -run ^TestAsyncWFIntegrationSuite$ -tags asyncwfintegration -count 1 -v github.com/uber/cadence/host | tee test.log ports: - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" environment: - "ASYNC_WF_KAFKA_QUEUE_TOPIC=async-wf-topic1" - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "KAFKA_PORT=9092" depends_on: cassandra: condition: service_healthy kafka: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose-local-es7.yml ================================================ services: cassandra: image: cassandra:4.1.1 environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" ports: - "9042:9042" networks: services-network: aliases: - cassandra healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" networks: services-network: aliases: - kafka elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3 ports: - "9200:9200" networks: services-network: aliases: - elasticsearch environment: - discovery.type=single-node integration-test-cassandra: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: - /bin/sh - -e - -c - | make cover_integration_profile ports: - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" environment: - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" - "ES_VERSION=v7" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose-local-history-simulation.yml ================================================ services: cassandra: image: cassandra:4.1.1 environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" ports: - "9042:9042" networks: services-network: aliases: - cassandra healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:v3.0.1 volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/history_simulation_prometheus.yml' ports: - '9090:9090' networks: services-network: aliases: - prometheus grafana: image: grafana/grafana:11.4.0 volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' networks: services-network: aliases: - grafana history-simulator: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: - /bin/sh - -e - -c - > go test -timeout 300s -run ^TestHistorySimulation.*$ -count 1 -v github.com/uber/cadence/simulation/history | tee test.log environment: - "HISTORY_LOG_EVENTS=true" - "CASSANDRA_HOST=cassandra" - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" depends_on: prometheus: condition: service_started grafana: condition: service_started cassandra: condition: service_healthy ports: # expose prometheus ports so they can be scraped - '8306:8306' - '8307:8307' - '8308:8308' - '8309:8309' volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - cadence networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose-local-matching-simulation.yml ================================================ services: cassandra: image: cassandra:4.1.1 environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" expose: - "9042" networks: services-network: aliases: - cassandra healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:v3.0.1 volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/matching_simulation_prometheus.yml' ports: - '9090:9090' networks: services-network: aliases: - prometheus grafana: image: grafana/grafana:11.4.0 volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' networks: services-network: aliases: - grafana matching-simulator: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: - /bin/sh - -e - -c - > go test -timeout 180s -run ^TestMatchingSimulation.*$ -count 1 -v github.com/uber/cadence/simulation/matching | tee test.log environment: - "MATCHING_LOG_EVENTS=true" - "CASSANDRA_HOST=cassandra" - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" depends_on: prometheus: condition: service_started grafana: condition: service_started cassandra: condition: service_healthy ports: # expose prometheus ports so they can be scraped - '8306:8306' - '8307:8307' - '8308:8308' - '8309:8309' volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - cadence networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose-local-pinot.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" networks: services-network: aliases: - cassandra healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest networks: services-network: aliases: - prometheus volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' kafka: image: docker.io/bitnamilegacy/kafka:3.7 restart: unless-stopped container_name: "kafka" ports: - "9092:9092" - "9093:9093" environment: - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=PLAINTEXT:PLAINTEXT,OUTSIDE:PLAINTEXT - KAFKA_CFG_LISTENERS=PLAINTEXT://:9093,OUTSIDE://:9092 - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9093,OUTSIDE://kafka:9092 - KAFKA_CFG_BROKER_ID=0 - ALLOW_PLAINTEXT_LISTENER=yes # Topic settings - KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true depends_on: - zookeeper networks: services-network: aliases: - kafka # will be deleted later when we get rid of ES usages elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3 # version here must be the same as the one in testdata/integration_pinot_cluster ports: - "9200:9200" networks: services-network: aliases: - elasticsearch environment: - discovery.type=single-node zookeeper: image: zookeeper:3.5.8 restart: always hostname: zookeeper container_name: zookeeper ports: - '2181:2181' environment: - ZOOKEEPER_CLIENT_PORT=2181 - ZOOKEEPER_TICK_TIME=2000 networks: services-network: aliases: - zookeeper pinot-controller: image: apachepinot/pinot:latest command: "StartController -zkAddress zookeeper:2181 -controllerPort 9001" container_name: pinot-controller restart: unless-stopped ports: - "9001:9001" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms1G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-controller.log" depends_on: - zookeeper networks: services-network: aliases: - pinot-controller pinot-broker: image: apachepinot/pinot:latest command: "StartBroker -zkAddress zookeeper:2181" restart: unless-stopped container_name: "pinot-broker" ports: - "8099:8099" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms4G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-broker.log" depends_on: - pinot-controller networks: services-network: aliases: - pinot-broker pinot-server: image: apachepinot/pinot:latest command: "StartServer -zkAddress zookeeper:2181" restart: unless-stopped container_name: "pinot-server" ports: - "8098:8098" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms4G -Xmx16G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-server.log" depends_on: - pinot-broker networks: services-network: aliases: - pinot-server pinot-admin: image: apachepinot/pinot:latest container_name: pinot-admin depends_on: - pinot-controller - pinot-broker - pinot-server entrypoint: ["/bin/sh", "-c", "cp /schema/pinot/create_pinot_table.sh /opt/pinot/create_pinot_table.sh && chmod +x /opt/pinot/create_pinot_table.sh && /opt/pinot/create_pinot_table.sh"] volumes: - ../../schema:/schema # Ensure the schema files and script are available in the container networks: services-network: aliases: - pinot-server integration-test-cassandra-pinot: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: - /bin/sh - -e - -c - > go test -timeout 600s -run ^TestPinotIntegrationSuite$ -tags pinotintegration -count 1 -v github.com/uber/cadence/host | tee test.log ports: - "7933:7933" - "7934:7934" - "7935:7935" - "7939:7939" environment: - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "TEST_TAG=pinotintegration" - "ES_VERSION=v7" - "KAFKA_SEEDS=kafka" - "PINOT_SEEDS=pinot-broker" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started pinot-controller: condition: service_started pinot-broker: condition: service_started pinot-server: condition: service_started pinot-admin: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test networks: services-network: enable_ipv6: true ipam: config: - subnet: 2001:0DB8::/112 name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose-local-replication-simulation.yml ================================================ services: cassandra: image: cassandra:4.1.1 environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" ports: - "9042:9042" networks: services-network: aliases: - cassandra healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:v3.0.1 volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/replication_simulation_prometheus.yml' ports: - '9090:9090' networks: services-network: aliases: - prometheus grafana: image: grafana/grafana:11.4.0 volumes: - ./grafana:/etc/grafana user: "1000" depends_on: - prometheus ports: - '3000:3000' networks: services-network: aliases: - grafana cadence-cluster0: build: context: ../../ dockerfile: ./Dockerfile${DOCKERFILE_SUFFIX} args: TARGET: auto-setup command: - /start.sh ports: - "7933:7933" # frontend thrift - "7833:7833" # frontend grpc - "7934:7934" # history thrift - "7834:7834" # history grpc - "7935:7935" # matching thrift - "7835:7835" # matching grpc - "7939:7939" # worker thrift - "7000:7000" # frontend prometheus - "7001:7001" # matching prometheus - "7002:7002" # history prometheus - "7003:7003" # worker prometheus environment: - DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/replication_simulation_${SCENARIO}.yml - "CLUSTER_REDIRECT_POLICY=selected-apis-forwarding" - "BIND_ON_IP=0.0.0.0" - "PRIMARY_FRONTEND_SERVICE=cadence-cluster0" - "SECONDARY_FRONTEND_SERVICE=cadence-cluster1" - "CASSANDRA_SEEDS=cassandra" - "ENABLE_GLOBAL_DOMAIN=true" - "KEYSPACE=cadence_primary" - "VISIBILITY_KEYSPACE=cadence_visibility_primary" - "LOG_LEVEL=debug" - "MATCHING_LOG_EVENTS=true" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:7000" # frontend scrape endpoint - "PROMETHEUS_ENDPOINT_1=0.0.0.0:7001" # matching scrape endpoint - "PROMETHEUS_ENDPOINT_2=0.0.0.0:7002" # history scrape endpoint - "PROMETHEUS_ENDPOINT_3=0.0.0.0:7003" # worker scrape endpoint depends_on: cassandra: condition: service_healthy prometheus: condition: service_started networks: services-network: aliases: - cadence-cluster0 cadence-cluster1: build: context: ../../ dockerfile: ./Dockerfile${DOCKERFILE_SUFFIX} args: TARGET: auto-setup command: - /start.sh ports: # cluster1 uses 8xxx host ports to avoid conflicts with cluster0 - "8933:7933" # frontend thrift - "8833:7833" # frontend grpc - "8934:7934" # history thrift - "8834:7834" # history grpc - "8935:7935" # matching thrift - "8835:7835" # matching grpc - "8939:7939" # worker thrift - "8000:8000" # frontend prometheus - "8001:8001" # matching prometheus - "8002:8002" # history prometheus - "8003:8003" # worker prometheus environment: - DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/replication_simulation_${SCENARIO}.yml - "CLUSTER_REDIRECT_POLICY=selected-apis-forwarding" - "BIND_ON_IP=0.0.0.0" - "PRIMARY_FRONTEND_SERVICE=cadence-cluster0" - "SECONDARY_FRONTEND_SERVICE=cadence-cluster1" - "CASSANDRA_SEEDS=cassandra" - "IS_NOT_PRIMARY=true" - "ENABLE_GLOBAL_DOMAIN=true" - "KEYSPACE=cadence_secondary" - "VISIBILITY_KEYSPACE=cadence_visibility_secondary" - "LOG_LEVEL=debug" - "MATCHING_LOG_EVENTS=true" - "PROMETHEUS_ENDPOINT_0=0.0.0.0:8000" # frontend scrape endpoint - "PROMETHEUS_ENDPOINT_1=0.0.0.0:8001" # matching scrape endpoint - "PROMETHEUS_ENDPOINT_2=0.0.0.0:8002" # history scrape endpoint - "PROMETHEUS_ENDPOINT_3=0.0.0.0:8003" # worker scrape endpoint depends_on: cassandra: condition: service_healthy prometheus: condition: service_started networks: services-network: aliases: - cadence-cluster1 cadence-web: image: ubercadence/web:latest environment: - "CADENCE_GRPC_PEERS=cadence-cluster0:7833,cadence-cluster1:7833" - "CADENCE_GRPC_SERVICES_NAMES=cadence-frontend,cadence-frontend" - "CADENCE_CLUSTERS_NAMES=cluster0,cluster1" - "CADENCE_HISTORY_PAGE_V2_ENABLED=OPT_OUT" ports: - "8088:8088" depends_on: - cadence-cluster0 - cadence-cluster1 networks: services-network: aliases: - cadence-web cadence-worker0: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} working_dir: /cadence/simulation/replication/worker/cmd command: - /bin/sh - -e - -c - > go run *.go --cluster cluster0 | tee worker0.log environment: - REPLICATION_SIMULATION_CONFIG=testdata/replication_simulation_${SCENARIO}.yaml depends_on: cadence-cluster0: condition: service_started cadence-cluster1: condition: service_started healthcheck: test: ["CMD", "curl", "-f", "http://localhost:6060/health"] interval: 10s timeout: 2s retries: 20 volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory - ../../simulation/replication/testdata/:/cadence/simulation/replication/worker/cmd/testdata/ networks: services-network: aliases: - cadence-worker0 cadence-worker1: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} working_dir: /cadence/simulation/replication/worker/cmd command: - /bin/sh - -e - -c - > go run *.go --cluster cluster1 | tee worker1.log environment: - REPLICATION_SIMULATION_CONFIG=testdata/replication_simulation_${SCENARIO}.yaml depends_on: cadence-cluster0: condition: service_started cadence-cluster1: condition: service_started healthcheck: test: ["CMD", "curl", "-f", "http://localhost:6060/health"] interval: 10s timeout: 2s retries: 20 volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory - ../../simulation/replication/testdata/:/cadence/simulation/replication/worker/cmd/testdata/ networks: services-network: aliases: - cadence-worker1 replication-simulator: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: - /bin/sh - -e - -c - > go test -timeout 240s -run ^TestReplicationSimulation.*$ -count 1 -v github.com/uber/cadence/simulation/replication | tee test.log depends_on: cadence-cluster0: condition: service_started cadence-worker0: condition: service_started cadence-cluster1: condition: service_started cadence-worker1: condition: service_started cadence-web: condition: service_started grafana: condition: service_started ports: # expose prometheus ports so they can be scraped - '8306:8306' - '8307:8307' - '8308:8308' - '8309:8309' volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - replication-simulator networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose-local.yml ================================================ services: cassandra: image: cassandra:4.1.1 environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" expose: - "9042" networks: services-network: aliases: - cassandra healthcheck: test: [ "CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces" ] interval: 15s timeout: 30s retries: 10 mysql: image: mysql:8.0 environment: MYSQL_DATABASE: db MYSQL_ROOT_PASSWORD: cadence volumes: - ./mysql-init:/docker-entrypoint-initdb.d expose: - "3306" networks: services-network: aliases: - mysql postgres: image: postgres:17.4 environment: POSTGRES_PASSWORD: cadence POSTGRES_USER: cadence expose: - "5432" networks: services-network: aliases: - postgres kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" networks: services-network: aliases: - kafka elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.22 expose: - "9200" networks: services-network: aliases: - elasticsearch environment: - discovery.type=single-node mongo: image: mongo:5 restart: always networks: services-network: aliases: - mongo environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: cadence unit-test: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: sh -c "make .just-build && make cover_profile" volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - unit-test integration-test-cassandra: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: make cover_integration_profile environment: - "CASSANDRA_HOST=cassandra" - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - integration-test integration-test-cassandra-queue-v2-alert: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: make cover_integration_profile environment: - "CASSANDRA_HOST=cassandra" - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" - "ENABLE_QUEUE_V2=true" - "ENABLE_QUEUE_V2_ALERT=true" - "V=1" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - integration-test integration-test-mysql: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: make cover_integration_profile environment: - "MYSQL=1" - "MYSQL_SEEDS=mysql" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "PERSISTENCE_TYPE=sql" - "PERSISTENCE_PLUGIN=mysql" - "TEST_TAG=esintegration" depends_on: - mysql - elasticsearch - kafka volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - integration-test integration-test-postgres: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: make cover_integration_profile environment: - "POSTGRES=1" - "POSTGRES_SEEDS=postgres" - "PERSISTENCE_PLUGIN=postgres" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "PERSISTENCE_TYPE=sql" - "TEST_TAG=esintegration" depends_on: - postgres - elasticsearch - kafka volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - integration-test integration-test-v2: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: make cover_integration_profile EVENTSV2=true environment: - "CASSANDRA_HOST=cassandra" - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - integration-test integration-test-ndc-cassandra: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: make cover_ndc_profile environment: - "CASSANDRA_HOST=cassandra" - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - integration-test-ndc integration-test-ndc-mysql: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} command: make cover_ndc_profile environment: - "MYSQL=1" - "MYSQL_SEEDS=mysql" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "PERSISTENCE_TYPE=sql" - "PERSISTENCE_PLUGIN=mysql" - "TEST_TAG=esintegration" depends_on: - mysql - elasticsearch - kafka volumes: - ../../:/cadence - /cadence/.build/ # ensure we don't mount the build directory - /cadence/.bin/ # ensure we don't mount the bin directory networks: services-network: aliases: - integration-test-ndc networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose-opensearch2.yml ================================================ services: cassandra: image: cassandra:4.1.1 environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" networks: services-network: aliases: - cassandra healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" networks: services-network: aliases: - kafka elasticsearch: image: opensearchproject/opensearch:2.13.0 networks: services-network: aliases: - elasticsearch environment: - discovery.type=single-node - OPENSEARCH_SECURITY_SSL_HTTP_ENABLED=false - "DISABLE_SECURITY_PLUGIN=true" - cluster.name=opensearch-cluster - bootstrap.memory_lock=true - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" - OPENSEARCH_INITIAL_ADMIN_PASSWORD=DevTestInitial123! # Sets the demo admin user password when using demo configuration, required for OpenSearch 2.12 and later ports: - 9200:9200 # REST API - 9600:9600 # Performance Analyzer integration-test-cassandra: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" - "ES_VERSION=os2" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose-pinot.yml ================================================ services: cassandra: image: cassandra:4.1.1 ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" networks: services-network: aliases: - cassandra healthcheck: test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"] interval: 15s timeout: 30s retries: 10 prometheus: image: prom/prometheus:latest networks: services-network: aliases: - prometheus volumes: - ./prometheus:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' ports: - '9090:9090' kafka: image: docker.io/bitnamilegacy/kafka:3.7 restart: unless-stopped container_name: "kafka" ports: - "9092:9092" - "9093:9093" environment: - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=PLAINTEXT:PLAINTEXT,OUTSIDE:PLAINTEXT - KAFKA_CFG_LISTENERS=PLAINTEXT://:9093,OUTSIDE://:9092 - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9093,OUTSIDE://kafka:9092 - KAFKA_CFG_BROKER_ID=0 - ALLOW_PLAINTEXT_LISTENER=yes # Topic settings - KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true depends_on: - zookeeper networks: services-network: aliases: - kafka # will be deleted later when we get rid of ES usages elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3 # version here must be the same as the one in testdata/integration_pinot_cluster ports: - "9200:9200" networks: services-network: aliases: - elasticsearch environment: - discovery.type=single-node zookeeper: image: zookeeper:3.5.8 restart: always hostname: zookeeper container_name: zookeeper ports: - '2181:2181' environment: - ZOOKEEPER_CLIENT_PORT=2181 - ZOOKEEPER_TICK_TIME=2000 networks: services-network: aliases: - zookeeper # Needs a new pinot version to run the new json match query pinot-controller: image: apachepinot/pinot:latest command: "StartController -zkAddress zookeeper:2181 -controllerPort 9001" container_name: pinot-controller restart: unless-stopped ports: - "9001:9001" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms1G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-controller.log" depends_on: - zookeeper networks: services-network: aliases: - pinot-controller pinot-broker: image: apachepinot/pinot:latest command: "StartBroker -zkAddress zookeeper:2181" restart: unless-stopped container_name: "pinot-broker" ports: - "8099:8099" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms4G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-broker.log" depends_on: - pinot-controller networks: services-network: aliases: - pinot-broker pinot-server: image: apachepinot/pinot:latest command: "StartServer -zkAddress zookeeper:2181" restart: unless-stopped container_name: "pinot-server" ports: - "8098:8098" environment: JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms4G -Xmx16G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-server.log" depends_on: - pinot-broker networks: services-network: aliases: - pinot-server pinot-admin: image: apachepinot/pinot:latest container_name: pinot-admin depends_on: - pinot-controller - pinot-broker - pinot-server entrypoint: ["/bin/sh", "-c", "cp /schema/pinot/create_pinot_table.sh /opt/pinot/create_pinot_table.sh && chmod +x /opt/pinot/create_pinot_table.sh && /opt/pinot/create_pinot_table.sh"] volumes: - ../../schema:/schema # Ensure the schema files and script are available in the container networks: services-network: aliases: - pinot-server integration-test-cassandra-pinot: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "TEST_TAG=pinotintegration" - "ES_VERSION=v7" - "KAFKA_SEEDS=kafka" - "PINOT_SEEDS=pinot-broker" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started pinot-controller: condition: service_started pinot-broker: condition: service_started pinot-server: condition: service_started pinot-admin: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test networks: services-network: enable_ipv6: true ipam: config: - subnet: 2001:0DB8::/112 name: services-network driver: bridge ================================================ FILE: docker/github_actions/docker-compose.yml ================================================ services: cassandra: image: cassandra:4.1.1 environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" networks: services-network: aliases: - cassandra healthcheck: test: [ "CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces" ] interval: 15s timeout: 30s retries: 10 mysql: image: mysql:8.0 environment: - "MYSQL_ROOT_PASSWORD=cadence" networks: services-network: aliases: - mysql postgres: image: postgres:17.4 environment: POSTGRES_PASSWORD: cadence ports: - "5432:5432" networks: services-network: aliases: - postgres kafka: image: docker.io/bitnamilegacy/kafka:3.7 hostname: kafka container_name: kafka ports: - "9092:9092" environment: # KRaft settings - "KAFKA_CFG_NODE_ID=0" - "KAFKA_CFG_PROCESS_ROLES=controller,broker" - "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093" # Listeners - "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" - "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092" - "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" - "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" - "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" # Topic settings - "KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true" networks: services-network: aliases: - kafka healthcheck: test: ["CMD-SHELL", "kafka-topics.sh --bootstrap-server localhost:9092 --list"] interval: 15s timeout: 30s retries: 10 elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.22 networks: services-network: aliases: - elasticsearch environment: - discovery.type=single-node healthcheck: test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -q '\"status\":\"green\"\\|\"status\":\"yellow\"'"] interval: 15s timeout: 30s retries: 10 mongo: image: mongo:5 restart: always networks: services-network: aliases: - mongo environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: cadence etcd: image: bitnamilegacy/etcd:3.5.5 restart: always networks: services-network: aliases: - etcd environment: ALLOW_NONE_AUTHENTICATION: "yes" ETCD_ADVERTISE_CLIENT_URLS: "http://etcd:2379" unit-test: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} volumes: - ../../:/cadence networks: services-network: aliases: - unit-test integration-test-cassandra: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test integration-test-cassandra-queue-v2: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" - "ENABLE_QUEUE_V2=true" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test integration-test-cassandra-queue-v2-alert: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" - "ENABLE_QUEUE_V2=true" - "ENABLE_QUEUE_V2_ALERT=true" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test integration-test-mysql: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "MYSQL=1" - "MYSQL_SEEDS=mysql" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "PERSISTENCE_TYPE=sql" - "PERSISTENCE_PLUGIN=mysql" - "TEST_TAG=esintegration" depends_on: - mysql - elasticsearch - kafka volumes: - ../../:/cadence networks: services-network: aliases: - integration-test integration-test-postgres: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "POSTGRES=1" - "POSTGRES_SEEDS=postgres" - "PERSISTENCE_PLUGIN=postgres" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "PERSISTENCE_TYPE=sql" - "TEST_TAG=esintegration" depends_on: - postgres - elasticsearch - kafka volumes: - ../../:/cadence networks: services-network: aliases: - integration-test integration-test-sqlite: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "PERSISTENCE_PLUGIN=sqlite" - "PERSISTENCE_TYPE=sql" volumes: - ../../:/cadence networks: services-network: aliases: - integration-test integration-test-ndc-cassandra: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "TEST_TAG=esintegration" depends_on: cassandra: condition: service_healthy elasticsearch: condition: service_started kafka: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test-ndc integration-test-ndc-mysql: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "MYSQL=1" - "MYSQL_SEEDS=mysql" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "PERSISTENCE_TYPE=sql" - "PERSISTENCE_PLUGIN=mysql" - "TEST_TAG=esintegration" depends_on: - mysql - elasticsearch - kafka volumes: - ../../:/cadence networks: services-network: aliases: - integration-test-ndc integration-test-ndc-postgres: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "POSTGRES=1" - "POSTGRES_SEEDS=postgres" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "PERSISTENCE_TYPE=sql" - "PERSISTENCE_PLUGIN=postgres" - "TEST_TAG=esintegration" depends_on: - postgres - elasticsearch - kafka volumes: - ../../:/cadence networks: services-network: aliases: - integration-test-ndc integration-test-async-wf: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "ASYNC_WF_KAFKA_QUEUE_TOPIC=async-wf-topic1" - "CASSANDRA=1" - "CASSANDRA_SEEDS=cassandra" - "ES_SEEDS=elasticsearch" - "KAFKA_SEEDS=kafka" - "KAFKA_PORT=9092" depends_on: cassandra: condition: service_healthy kafka: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test-async-wf integration-test-with-etcd: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - "ETCD=1" - "ETCD_ENDPOINTS=http://etcd:2379" depends_on: etcd: condition: service_started volumes: - ../../:/cadence networks: services-network: aliases: - integration-test-with-etcd coverage-report: build: context: ../../ dockerfile: ./docker/github_actions/Dockerfile${DOCKERFILE_SUFFIX} environment: - CI - GITHUB_BRANCH - GITHUB_RUN_URL - GITHUB_REPO - GITHUB_RUN_CREATOR - GITHUB_RUN_CREATOR_EMAIL - GITHUB_RUN_CREATOR_TEAMS - GITHUB_PULL_REQUEST_REPO volumes: - ../../:/cadence networks: services-network: name: services-network driver: bridge ================================================ FILE: docker/github_actions/grafana/grafana.ini ================================================ [auth.anonymous] enabled = true org_role = Admin ================================================ FILE: docker/github_actions/grafana/provisioning/dashboards/cadence-archival.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 6, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 8, "panels": [], "title": "Overall", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "history" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 1, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (changes(archiver_client_history_request{operation=\"ArchiverClient\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "history", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(archiver_client_visibility_request{operation=\"ArchiverClient\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "visibility", "range": true, "refId": "B", "useBackend": false } ], "title": "Archivals Triggered Per Second", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "archiver_client_visibility_inline_archive_attempt" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 2, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (changes(archiver_client_history_request{operation=\"ArchiverClient\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "archiver_client_history_request", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (changes(archiver_client_history_inline_archive_attempt{operation=\"ArchiverClient\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "archiver_client_history_inline_archive_attempt", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(archiver_client_visibility_request{operation=\"ArchiverClient\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "archiver_client_visibility_request", "range": true, "refId": "C", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(archiver_client_visibility_inline_archive_attempt{operation=\"ArchiverClient\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "archiver_client_visibility_inline_archive_attempt", "range": true, "refId": "D", "useBackend": false } ], "title": "Client Requests vs Inline Attempts", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "archiver_client_history_request" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 9 }, "id": 3, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(archiver_client_history_request{operation=\"ArchiverClient\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "archiver_client_history_request", "range": true, "refId": "A", "useBackend": false } ], "title": "Archiver History Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 9 }, "id": 4, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(archiver_client_history_inline_archive_attempt{operation=\"ArchiverClient\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "archiver_client_history_inline_archive_attempt", "range": true, "refId": "A", "useBackend": false } ], "title": "Client History Inline Attemptss", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 17 }, "id": 5, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, operation) (rate(archiver_client_history_request_per_domain{domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "", "hide": false, "instant": false, "range": true, "refId": "B" } ], "title": "History Requests Per Domain Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 17 }, "id": 6, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, operation) (rate(archiver_client_history_inline_archive_attempt_per_domain[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History Inline Attempt Per Domain Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 25 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(workflow_cleanup_archive[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Workflow Cleanup Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 25 }, "id": 9, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "", "hide": false, "instant": false, "range": true, "refId": "A" } ], "title": "History Inline Attempt Per Domain Per Operation", "type": "timeseries" } ], "preload": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "allowCustomValue": false, "current": { "text": "", "value": "" }, "definition": "label_values(activity_end_to_end_latency_sum,domain)", "includeAll": false, "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_sum,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "0.99", "value": "0.99" }, "label": "Quantiles", "name": "quantiles", "options": [ { "selected": false, "text": "0.5", "value": "0.5" }, { "selected": false, "text": "0.95", "value": "0.95" }, { "selected": true, "text": "0.99", "value": "0.99" } ], "query": "0.5, 0.95, 0.99", "type": "custom" } ] }, "time": { "from": "now-6h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Cadence-Archival", "uid": "1B0efRyGx", "version": 1 } ================================================ FILE: docker/github_actions/grafana/provisioning/dashboards/cadence-client-overall.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 2, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 10, "panels": [], "title": "Overview", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 11, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_requests[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests per operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 9, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "max by(active_cluster) (active_cluster{cadence_service=\"cadence_history\", operation=\"DomainCache\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Deploymets", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 9 }, "id": 14, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "avg by(operation) (rate(cadence_client_errors{domain!~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Errors per operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "HistoryClientRespondDecisionTaskCompleted" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 9 }, "id": 16, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_client_latency_bucket[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": " Latency Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 17 }, "id": 17, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(TaskList) (rate(cadence_activity_poll_total{domain=~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(TaskList) (rate(cadence_activity_poll_transient_failed[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Activity poll counter", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 17 }, "id": 20, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(schedule_activity_decision[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity decision per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 25 }, "id": 19, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(activity_end_to_end_latency_sum{domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity end to end processing time", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 25 }, "id": 18, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(workflow_started_count[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Workflow Started Count", "type": "timeseries" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 33 }, "id": 2, "panels": [], "title": "Ratelimiter", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 34 }, "id": 1, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(global_ratelimit_collection) (rate(global_ratelimiter_allowed_requests{is_primary=\"true\", cadence_service=\"cadence_frontend\", operation=\"GlobalRatelimiter\", global_ratelimit_key=~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Limiter in use", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 34 }, "id": 3, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "max by(global_ratelimit_key, global_ratelimit_collection) (global_ratelimiter_quota{global_ratelimit_key=\"$domain\"})", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Quotas", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 42 }, "id": 5, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(global_ratelimit_collection, global_ratelimit_key, global_ratelimit_type) (rate(global_ratelimiter_allowed_requests{is_primary=\"true\", cadence_service=\"cadence_frontend\", operation=\"GlobalRatelimiter\", global_ratelimit_key=~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 42 }, "id": 4, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum(rate(global_ratelimiter_allowed_requests{is_primary=\"true\", cadence_service=\"cadence_frontend\", operation=\"GlobalRatelimiter\", global_ratelimit_type=~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Allowed", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 50 }, "id": 6, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(global_ratelimit_collection, global_ratelimit_key, global_ratelimit_type) (rate(global_ratelimiter_rejected_requests{is_primary=\"true\", cadence_service=\"cadence_frontend\", operation=\"GlobalRatelimiter\", global_ratelimit_key=~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Rejected", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 50 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(global_ratelimit_collection, global_ratelimit_key, global_ratelimit_type) (rate(global_ratelimiter_rejected_requests{is_primary=\"false\", cadence_service=\"cadence_frontend\", operation=\"GlobalRatelimiter\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Rejected (shadow)", "type": "timeseries" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 58 }, "id": 29, "panels": [], "title": "Workflow", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 59 }, "id": 26, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, domain) (rate(cadence_workflow_endtoend_latency_bucket{domain=~\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Workflow End to End Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 59 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(namespace, domain) (rate(cadence_worker_panic{domain=~\"$domain\"}[2m]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "NonDeterministicError and Worker Panic", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 67 }, "id": 27, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "editorMode": "code", "expr": "sum(count(cadence_sticky_cache_total_forced_eviction{domain=\"$domain\"})) by (domain)", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Workflow Sticky Cache Eviction", "type": "timeseries" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 75 }, "id": 34, "panels": [], "title": "Activity", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 76 }, "id": 30, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "editorMode": "builder", "expr": "sum(count(cadence_activity_task_failed{domain=\"$domain\"})) by (activitytype)", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Activity Task Operations", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 76 }, "id": 33, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "avg by(domain, tasklist, activitytype) (cadence_activity_execution_latency{domain=~\"$domain\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Execution Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 84 }, "id": 31, "options": { "legend": { "calcs": [ "mean", "max", "min", "sum", "lastNotNull" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, tasklist) (rate(cadence_requests_per_tl{cadence_service=\"cadence_matching\", domain=~\"$domain\"}[5m]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "ActivityTasks scheduled per Second", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 84 }, "id": 32, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "avg by(Domain, TaskList, ActivityType) (cadence_activity_scheduled_to_start_latency_count{domain=~\"$domain\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Scheduled To Start Latency", "type": "timeseries" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 92 }, "id": 37, "panels": [], "title": "Service", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 93 }, "id": 35, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "editorMode": "code", "expr": "(1 - count(cadence_error{domain=\"$domain\"})by(domain)/count(cadence_request{domain=\"$domain\"})by(domain)) * 100", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Service API success rate", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 93 }, "id": 36, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "editorMode": "code", "expr": "sum(avg_over_time(cadence_latency{Domain=\"$domain\"} [1m])) by (Domain)", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Service API Latency (TODO exclude long-poll APIs)", "type": "timeseries" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 101 }, "id": 24, "panels": [], "title": "History", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 102 }, "id": 22, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "editorMode": "code", "expr": "histogram_quantile($quantiles, (max by (le, domain) (history_size_bucket)))*1000000000", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Max History Size", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 102 }, "id": 23, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "editorMode": "code", "expr": "sum by (domain) (history_count_sum{domain!=\"all\"}/history_count_count{domain!=\"all\"})\n*1000000000", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Avg History Length ", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 110 }, "id": 25, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "editorMode": "code", "expr": "max(histogram_quantile(1, max(event_blob_size_bucket) by (le)))*1000000000", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Max Event Blob Size", "type": "timeseries" } ], "preload": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "current": { "text": "", "value": "" }, "definition": "label_values(activity_end_to_end_latency_sum,domain)", "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_sum,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "0.99", "value": "0.99" }, "label": "Quantiles", "name": "quantiles", "options": [ { "selected": false, "text": "0.5", "value": "0.5" }, { "selected": false, "text": "0.95", "value": "0.95" }, { "selected": true, "text": "0.99", "value": "0.99" } ], "query": "0.5, 0.95, 0.99", "type": "custom" } ] }, "time": { "from": "now-6h", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Cadence-Client", "uid": "dehkspwgabvuoc", "version": 1 } ================================================ FILE: docker/github_actions/grafana/provisioning/dashboards/cadence-frontend.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 3, "links": [], "panels": [ { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 6, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 2, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Request Counts Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 3, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_errors{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_errors_entity_not_exists{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_errors_per_tl{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "C", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "rate(cadence_PollForActivityTask_cadence_error{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval])", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "D", "useBackend": false } ], "title": "Error Breakdown", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 25 }, "id": 4, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "rate(cadence_errors{cadence_service=\"cadence_frontend\"}[$__rate_interval])", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Error Counts Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 25 }, "id": 5, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_errors{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Request Vs Error", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "ms", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 33 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend API Latencies", "type": "timeseries" } ], "title": "Overall", "type": "row" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 1 }, "id": 12, "panels": [], "title": "Frontend-Client", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 2 }, "id": 8, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_errors{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_requests{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Frontend Client Request Vs Error", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 2 }, "id": 9, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_requests{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Client Request Counts Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "ms", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 10 }, "id": 11, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_client_latency_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Client API Latencies", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 10 }, "id": 10, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_errors{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Client Request Counts Per API", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 18 }, "id": 27, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 19 }, "id": 13, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"StartWorkflowExecution\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "StartWorfklowExecution", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "ms", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 19 }, "id": 14, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"StartWorkflowExecution\", domain=\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "StartWorkflowExection Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 27 }, "id": 21, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, tasklist) (rate(cadence_requests_per_tl{cadence_service=\"$services\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests per operation per task", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 27 }, "id": 26, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, tasklist) (rate(workflow_success{cadence_service=\"$services\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Workflow success per operation per task", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 35 }, "id": 22, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"RespondActivityTaskCompleted\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Respond Activity task completed", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "sec", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 35 }, "id": 25, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"RespondActivityTaskCompleted\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "RespondActivity Task Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 43 }, "id": 17, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(TaskList) (rate(cadence_PollForActivityTask_cadence_request[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForActivityTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "ms", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 43 }, "id": 23, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"PollForActivityTask\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForActivityTask Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 51 }, "id": 19, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(TaskList) (rate(cadence_PollForDecisionTask_cadence_request[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForDecisionTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 51 }, "id": 24, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"PollForDecisionTask\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForDecisionTask Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 59 }, "id": 15, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_requests{operation=\"DescribeDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "DescribeDomain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 59 }, "id": 16, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"DescribeDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "DescribeDomain Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 67 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_requests{operation=\"GetWorkflowExecutionHistory\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GetWorkflowExecutionHistory", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 67 }, "id": 29, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"CancelWorkflowExecution\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "CancelWorkflowExecution", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 75 }, "id": 30, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"TerminateWorkflowExecution\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "TerminateWorkflowExecution", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 75 }, "id": 31, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"RegisterDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "RegisterDomain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 83 }, "id": 33, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"DescribeDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "DescribeDomain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 83 }, "id": 32, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"UpdateDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "UpdateDomain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 91 }, "id": 34, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"DepricateDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "DepricateDomain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 91 }, "id": 35, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"AddDecisionTask\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddDecisionTask", "type": "timeseries" } ], "title": "API", "type": "row" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 19 }, "id": 39, "panels": [], "title": "Persistence", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 20 }, "id": 36, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_requests{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Requests Vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 20 }, "id": 37, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_requests{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "sec", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 24, "x": 0, "y": 28 }, "id": 38, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, avg by(operation, le) (rate(persistence_latency_bucket{cadence_service=\"$services\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors_entity_not_exists{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Operation Latencies p99", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 36 }, "id": 42, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "{le=\"1.0000000000000001e-07\", operation=\"StartWorkflowExecution\"}" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 317 }, "id": 40, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(event_blob_size_bucket{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Overall Query Sizes", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "{le=\"1.0000000000000001e-07\", operation=\"StartWorkflowExecution\"}" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 317 }, "id": 41, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, domain) (rate(event_blob_size_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Query Sizes by Domain", "type": "timeseries" } ], "title": "Query sizes", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 37 }, "id": 44, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "histogram_quantile(0.95, sum by(le) (rate(cadence_authorization_latency_bucket{cadence_service=\"cadence_frontend\"}[1m0s])))" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 318 }, "id": 43, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_authorization_latency_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Authorization latency per API", "type": "timeseries" } ], "title": "Authorization", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 38 }, "id": 52, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 319 }, "id": 47, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_allocated{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Total Memory Allocation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "NumGC is the number of completed GC cycles.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 319 }, "id": 46, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(memory_num_gc{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GC Num", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 327 }, "id": 45, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(num_goroutines{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Number of goroutines", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "HeapInuse is bytes in in-use spans.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 327 }, "id": 49, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_heapinuse{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Heap in Use (HeapAlloc)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "HeapIdle is bytes in idle (unused) spans.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 335 }, "id": 48, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_heapidle{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Heap Available (HeapIdle)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "Bytes of stack memory obtained from the OS. Plus any memory obtained directly from the OS for OS thread stacks.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 335 }, "id": 51, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(memory_stack{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Stack In use", "type": "timeseries" } ], "title": "Go Gc Detailed", "type": "row" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 39 }, "id": 57, "panels": [], "title": "Replication", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 40 }, "id": 54, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "rate(cadence_requests{operation=\"GetReplicationMessage\", \"$services\"=\"$services\"}[$__rate_interval])", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GetReplicationMessage Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 40 }, "id": 53, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "rate(cadence_requests{operation=\"GetDomainReplicationMessage\", \"$services\"=\"$services\"}[$__rate_interval])", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GetDomainReplicationMessage Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 48 }, "id": 55, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le) (rate(cadence_latency_bucket{operation=\"GetReplicationMessage\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GetReplicationMessage Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 48 }, "id": 56, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le) (rate(cadence_latency_bucket{operation=\"GetDomainReplicationMessage\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GetDomainReplicationMessage Latency", "type": "timeseries" } ], "preload": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "current": { "text": "samples_domain", "value": "samples_domain" }, "definition": "label_values(activity_end_to_end_latency_bucket,domain)", "includeAll": true, "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_bucket,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "cadence_frontend", "value": "cadence_frontend" }, "definition": "label_values(cadence_requests,cadence_service)", "label": "services", "name": "services", "options": [], "query": { "qryType": 1, "query": "label_values(cadence_requests,cadence_service)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "p99", "value": "p99" }, "label": "latency", "name": "latency", "options": [ { "selected": true, "text": "p99", "value": "p99" }, { "selected": false, "text": "upper", "value": "upper" }, { "selected": false, "text": "mean", "value": "mean" }, { "selected": false, "text": "median", "value": "median" }, { "selected": false, "text": "lower", "value": "lower" } ], "query": "p99,upper,mean,median,lower", "type": "custom" } ] }, "time": { "from": "now-5m", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Cadence-Frontend", "uid": "cekm8fs6ps000f", "version": 1 } ================================================ FILE: docker/github_actions/grafana/provisioning/dashboards/cadence-history.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 5, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 9, "panels": [], "title": "API", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Request Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 8, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History API p99 Latencies", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 9 }, "id": 26, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 505 }, "id": 21, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(complete_workflow_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(schedule_activity_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(fail_workflow_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "C" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cancel_workflow_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "D" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(start_timer_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "E" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cancel_activity_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "F" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cancel_timer_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "G" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cancel_activity_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "H" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(record_marker_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "I" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cancel_external_workflow_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "J" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(continue_as_new_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "K" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(child_workflow_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "L" } ], "title": "Decision Breakdown", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 505 }, "id": 22, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(multiple_completion_decisions{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(failed_decisions{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B" } ], "title": "Bad Decisions", "type": "timeseries" } ], "title": "Decisions", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 10 }, "id": 14, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 505 }, "id": 10, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(cadence_client_requests{cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{cadence_service}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (changes(cadence_client_errors{cadence_service=\"cadence_history\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{cadence_service}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "History Clients Requests Vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 505 }, "id": 11, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_client_requests{cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (changes(cadence_client_errors{cadence_service=\"cadence_history\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "History Clients Request Count Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 513 }, "id": 12, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(cadence_client_errors{cadence_service=\"cadence_history\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "History Client Error Counts Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 513 }, "id": 13, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_client_latency_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History Clients API Latencies", "type": "timeseries" } ], "title": "History Clients", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 11 }, "id": 20, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 505 }, "id": 15, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cache_requests{cache_type=\"mutablestate\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cache_miss{cadence_service=\"cadence_history\", cache_type=\"mutablestate\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (miss)", "range": true, "refId": "B", "useBackend": false } ], "title": "Mutable State Cache", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 505 }, "id": 16, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cache_latency_bucket{cache_type=\"mutablestate\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Mutable State Cache Latencies", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 513 }, "id": 17, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cache_requests{cache_type=\"events\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cache_miss{cadence_service=\"cadence_history\", cache_type=\"events\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (miss)", "range": true, "refId": "B", "useBackend": false } ], "title": "Events Cache", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 513 }, "id": 18, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cache_latency_bucket{cache_type=\"mutablestate\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Events Cache Latencies", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "WorkflowContext" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 521 }, "id": 19, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, operation) (rate(workflow_context_lock_latency_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Execution Lock Held Duration", "type": "timeseries" } ], "title": "History Caches", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 12 }, "id": 30, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 9 }, "id": 23, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(acquire_shards_count{cadence_service=\"cadence_history\", operation=\"ShardController\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Shard Movement", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 9 }, "id": 27, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(get_engine_for_shard_latency_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (get_engine)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_per_shard_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (persistence_latency_per_shard)", "range": true, "refId": "B", "useBackend": false } ], "title": "p99 Latencies", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ShardInfo (shard info replication)" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 457 }, "id": 25, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(shardinfo_transfer_lag_count{cadence_service=\"cadence_history\", operation=\"ShardInfo\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (shard info transfer)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(shardinfo_replication_lag_count{cadence_service=\"cadence_history\", operation=\"ShardInfo\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (shard info replication)", "range": true, "refId": "B", "useBackend": false } ], "title": "Replication / Transfer p99 Lag (buggy)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 457 }, "id": 24, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_timer_lag_bucket{operation=\"ShardInfo\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer p99 Lag", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ShardInfo" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 465 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_transfer_diff_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Level p99 Diff", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ShardInfo" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 465 }, "id": 29, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_timer_diff_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer Level p99 Diff", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ShardInfo (timer failover)" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 473 }, "id": 31, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_transfer_failover_in_progress_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (transfer failover)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_timer_failover_in_progress_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (timer failover)", "range": true, "refId": "B", "useBackend": false } ], "title": "Failover p99 In Progress", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ShardInfo (transfer active pending task)" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 473 }, "id": 32, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_transfer_active_pending_task_bucket[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (transfer active pending task)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_timer_active_pending_task_bucket[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (timer active pending task)", "range": true, "refId": "B", "useBackend": false } ], "title": "Pending p99 Tasks", "type": "timeseries" } ], "title": "ShardController", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 13 }, "id": 38, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 14 }, "id": 33, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(persistence_requests{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "persistence_requests", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "rate(persistence_errors_entity_not_exists{cadence_service=\"cadence_history\"}[$__rate_interval])", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "persistence_errors", "range": true, "refId": "B", "useBackend": false } ], "title": "Requests vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 14 }, "id": 34, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "rate(persistence_errors_entity_not_exists{cadence_service=\"cadence_history\"}[$__rate_interval])", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "EntityNotExists", "range": true, "refId": "B", "useBackend": false } ], "title": "Error Breakdown", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 118 }, "id": 35, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_requests{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 118 }, "id": 36, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors_entity_not_exists{cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Error Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 126 }, "id": 37, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "avg by(operation) (rate(persistence_latency_bucket{cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Operation p99 Latency", "type": "timeseries" } ], "title": "Persistence", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 14 }, "id": 47, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 15 }, "id": 39, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(ack_level_update{cadence_service=\"cadence_history\", operation=\"TimerActiveQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Ack Level Update", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "TimerActiveQueueProcessor" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 15 }, "id": 40, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(ack_level_update{cadence_service=\"cadence_history\", operation=\"TimerStandByQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Standby Ack Level Update", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 23 }, "id": 41, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(new_timer_notifications{operation=\"TimerActiveQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Notifications", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 23 }, "id": 42, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "rate(new_timer_notifications{operation=\"TimerStandByQueueProcessor\"}[$__rate_interval])", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Standby New Timer Notifications", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 31 }, "id": 43, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(task_requests{operation=\"TimerActiveTaskActivityTimeout\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 31 }, "id": 44, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_latency_processing_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Processing p99 Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 39 }, "id": 46, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_attempt_bucket{operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer p99 Attempt", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 39 }, "id": 45, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_latency_queue_bucket{operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Queue p99 Latency", "type": "timeseries" } ], "title": "Timer Processor", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 15 }, "id": 48, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 16 }, "id": 49, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(ack_level_update{cadence_service=\"cadence_history\", operation=\"TransferActiveQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Ack Level Update", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "TimerActiveQueueProcessor" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 16 }, "id": 50, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(ack_level_update{cadence_service=\"cadence_history\", operation=\"TransferStandByQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Standby Ack Level Update", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 24 }, "id": 51, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(new_timer_notifications{operation=\"TransferActiveQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Notifications", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 24 }, "id": 52, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "rate(new_timer_notifications{operation=\"TranferStandByQueueProcessor\"}[$__rate_interval])", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Standby New Timer Notifications", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 32 }, "id": 53, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(task_requests{operation=\"TransferActiveTaskActivityTimeout\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 32 }, "id": 54, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_latency_processing_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Processing p99 Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 40 }, "id": 55, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_attempt_bucket{operation=~\"TransferActiveTaskActivityTimeout|TransferActiveTaskDecisionTimeout|TransferActiveTaskWorkflowTimeout\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer p99 Attempt", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 40 }, "id": 56, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_latency_queue_bucket{operation=~\"TransferActiveTaskActivityTimeout|TransferActiveTaskDecisionTimeout|TransferActiveTaskWorkflowTimeout\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Queue p99 Latency", "type": "timeseries" } ], "title": "Transfer Processor", "type": "row" } ], "preload": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "current": { "text": "samples_domain", "value": "samples_domain" }, "definition": "label_values(activity_end_to_end_latency_bucket,domain)", "includeAll": true, "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_bucket,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "cadence_history", "value": "cadence_history" }, "definition": "label_values(cadence_requests,cadence_service)", "label": "services", "name": "services", "options": [], "query": { "qryType": 1, "query": "label_values(cadence_requests,cadence_service)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "p99", "value": "p99" }, "label": "latency", "name": "latency", "options": [ { "selected": true, "text": "p99", "value": "p99" }, { "selected": false, "text": "upper", "value": "upper" }, { "selected": false, "text": "mean", "value": "mean" }, { "selected": false, "text": "median", "value": "median" }, { "selected": false, "text": "lower", "value": "lower" } ], "query": "p99,upper,mean,median,lower", "type": "custom" } ] }, "time": { "from": "now-5m", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Cadence-History", "uid": "cekm8fs6ps000h", "version": 1 } ================================================ FILE: docker/github_actions/grafana/provisioning/dashboards/cadence-matching.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 3, "links": [], "panels": [ { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 6, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 1, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_errors_entity_not_exists{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "interval": "", "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Overall Requests Vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 2, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_errors_entity_not_exists{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Errors Breakdown", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 113 }, "id": 3, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 113 }, "id": 4, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.99, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"cadence_matching\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "API Latencies (p99)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 121 }, "id": 5, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(tasklist_managers{cadence_service=\"cadence_matching\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Task Managers Per Operation", "type": "timeseries" } ], "title": "Overall", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 1 }, "id": 15, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 2 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"PollForActivityTask\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForActivityTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 2 }, "id": 8, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"$services\", operation=\"PollForActivityTask\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForActivityTask Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 114 }, "id": 9, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_requests{operation=\"PollForDecisionTask\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_errors{operation=\"PollForDecisionTask\", cadence_service=\"$services\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B" } ], "title": "PollForDecisionTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 114 }, "id": 10, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"$services\", operation=\"PollForDecisionTask\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForDecisionTask Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 122 }, "id": 11, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"AddActivityTask\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddActivityTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 122 }, "id": 13, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"$services\", operation=\"AddActivityTask\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddActivityTask Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 130 }, "id": 12, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"AddDecisionTask\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddDecisionTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 130 }, "id": 14, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"$services\", operation=\"AddDecisionTask\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddDecisionTask Latency", "type": "timeseries" } ], "title": "API", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 2 }, "id": 23, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "sec", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 3 }, "id": 16, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (changes(poll_success_per_tl{operation=\"TaskListMgr\", domain=\"$domain\", \"$services\"=\"$services\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "{{operation}} (async)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (changes(poll_success_sync_per_tl{cadence_service=\"$services\", domain=\"$domain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (sync)", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(poll_timeouts_per_tl{operation=\"TaskListMgr\", cadence_service=\"$services\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (poll_timeouts)", "range": true, "refId": "C", "useBackend": false } ], "title": "TaskPoll", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 3 }, "id": 17, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by() (rate(cadence_PollForActivityTask_cadence_request{cadence_service=\"$services\", \"$domain\"=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Task Matcher Stats", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 115 }, "id": 19, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, avg by(le, operation) (rate(cadence_latency_per_tl_bucket{operation=\"AddActivityTask\", domain=\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddActivityTask Latency(p99) (forwarding requests excluded)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 115 }, "id": 18, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum(rate(cadence_PollForDecisionTask_cadence_request{cadence_service=\"$services\", \"$services\"=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Task Matcher Stat", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 123 }, "id": 20, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_per_tl_bucket{operation=\"AddDecisionTask\", cadence_service=\"cadence_matching\", domain=\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddDecisionTask Latency(p99) (forwarding requests excluded)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 123 }, "id": 22, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"PollForActivityTask\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForActivityTask QPS (forwarding requests excluded)", "type": "timeseries" } ], "title": "TaskListManager", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 3 }, "id": 27, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }, "id": 24, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_requests{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Requests Vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }, "id": 25, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_bucket{cadence_service=\"cadence_matching\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Operation Latencies p99", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "GetTaskListSize" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 100 }, "id": 26, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Errors Per API", "type": "timeseries" } ], "title": "Persistence", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 4 }, "id": 31, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 5 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_requests{cadence_role=\"history_client\", cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 5 }, "id": 30, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.99, sum by(le, operation) (rate(cadence_client_latency_bucket{cadence_service=\"cadence_matching\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "API Latency (p99)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 69 }, "id": 29, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_errors_redirection{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Errors Per API", "type": "timeseries" } ], "title": "History Client", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 5 }, "id": 34, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 6 }, "id": 32, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (changes(task_count_per_tl{tasklistType=\"activity\", cadence_service=\"cadence_matching\", domain=\"$domain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Task Backlog", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 6 }, "id": 33, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (changes(task_backlog_per_tl{tasklistType=\"decision\", cadence_service=\"cadence_matching\", domain=\"$domain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Task Backlog", "type": "timeseries" } ], "title": "Backlog (per task list)", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 6 }, "id": 37, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 7 }, "id": 35, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(memory_num_gc{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GC Num", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 7 }, "id": 36, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(memory_allocated{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Total allocation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 39 }, "id": 38, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(num_goroutines{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Number of goroutines", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "HeapInuse is bytes in in-use spans.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 39 }, "id": 39, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_heapinuse{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Heap in Use (HeapAlloc)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "HeapIdle is bytes in idle (unused) spans.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 47 }, "id": 40, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_heapidle{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Heap Available (HeapIdle)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "Bytes of stack memory obtained from the OS. Plus any memory obtained directly from the OS for OS thread stacks.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 47 }, "id": 41, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_stack{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Stack In use", "type": "timeseries" } ], "title": "Go GC detailed", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 7 }, "id": 48, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 }, "id": 42, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(tasklist) (rate(estimated_add_task_qps_per_tl{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"activity\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Adaptive Task List Scaler for Activity", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 }, "id": 43, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (rate(estimated_add_task_qps_per_tl{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"decision\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Adaptive Task List Scaler for Decision", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 16 }, "id": 44, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (rate(task_list_partition_config_num_read{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"activity\", operation=\"TaskListMgr\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Task List Read Partition Count", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 16 }, "id": 45, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (rate(task_list_partition_config_num_read{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"decision\", operation=\"TaskListMgr\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Task List Read Partition Count", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 24 }, "id": 46, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (rate(task_list_partition_config_num_write{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"activity\", operation=\"TaskListMgr\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Task List Write Partition Count", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 24 }, "id": 47, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (rate(task_list_partition_config_num_write{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"decision\", operation=\"TaskListMgr\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Task List Write Partition Count", "type": "timeseries" } ], "title": "Task List Partition Config & Adaptive Task List Scaler (WIP)", "type": "row" } ], "preload": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "current": { "text": "samples_domain", "value": "samples_domain" }, "definition": "label_values(activity_end_to_end_latency_bucket,domain)", "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_bucket,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "cadence_matching", "value": "cadence_matching" }, "definition": "label_values(cadence_requests,cadence_service)", "label": "services", "name": "services", "options": [], "query": { "qryType": 1, "query": "label_values(cadence_requests,cadence_service)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "p99", "value": "p99" }, "label": "latency", "name": "latency", "options": [ { "selected": true, "text": "p99", "value": "p99" }, { "selected": false, "text": "upper", "value": "upper" }, { "selected": false, "text": "mean", "value": "mean" }, { "selected": false, "text": "median", "value": "median" }, { "selected": false, "text": "lower", "value": "lower" } ], "query": "p99,upper,mean,median,lower", "type": "custom" } ] }, "time": { "from": "now-1h", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Cadence Matching", "uid": "del7jwba0qgw0e", "version": 1 } ================================================ FILE: docker/github_actions/grafana/provisioning/dashboards/cadence-persistence.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "target": { "limit": 100, "matchAny": false, "tags": [], "type": "dashboard" }, "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 2, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 12, "panels": [], "title": "Overall", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 0, "y": 1 }, "id": 17, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_response_row_size_count{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Response Row Count Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 12, "y": 1 }, "id": 18, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_response_payload_size_sum{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Response Payload Size Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 0, "y": 12 }, "id": 8, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (rate(persistence_requests_per_domain{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 12, "y": 12 }, "id": 2, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_requests{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests per operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 0, "y": 23 }, "id": 3, "options": { "legend": { "calcs": [ "mean", "max" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Max", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(persistence_latency_bucket{cadence_service=\"$services\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Latencies Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 12, "y": 23 }, "id": 10, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, operation) (rate(persistence_requests_per_domain{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Domain Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 9, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "fieldMinMax": false, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 0, "y": 34 }, "id": 1, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_empty_response{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Empty Response Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 12, "y": 34 }, "id": 9, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, shard_id) (rate(persistence_requests_per_shard{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} - {{shard_id}}(shard_id)", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Operation Per Shard", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 0, "y": 45 }, "id": 11, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors_entity_not_exists{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Errors Per Operation ", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 56 }, "id": 16, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 160 }, "id": 14, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "right", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "asc" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(le, operation) (rate(persistence_requests{operation=~\"CreateWorkflowExecution|UpdateWorkflowExecution|LeaseTaskList|UpdateTaskList|CreateShard|UpdateShard\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "LWT Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 160 }, "id": 15, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "right", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, max by(operation, le) (rate(persistence_latency_bucket{operation=~\"CreateWorkflowExecution|UpdateWorkflowExecution|LeaseTaskList|UpdateTaskList|CreateShard|UpdateShard\", cadence_service=\"$services\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "LWT Latency", "type": "timeseries" } ], "title": "LWT Operations", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 57 }, "id": 22, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 161 }, "id": 19, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, cadence_service) (rate(persistence_requests{operation=~\"ReadHistoryBranch|AppendHistoryNodes|GetHistoryTasks\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 161 }, "id": 21, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, cadence_service) (rate(persistence_empty_response{operation=~\"ReadHistoryBranch|AppendHistoryNodes|GetHistoryTasks\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Empty Response Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 193 }, "id": 20, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors_entity_not_exists{operation=~\"ReadHistoryBranch|AppendHistoryNodes|GetHistoryTasks\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Errors Per Operation", "type": "timeseries" } ], "title": "History Tasks", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 58 }, "id": 29, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 162 }, "id": 23, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_requests{operation=~\"CreateShard|GetShard|UpdateShard\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_empty_response{operation=~\"CreateShard|GetShard|UpdateShard\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Shard CRUD", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 162 }, "id": 24, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, operation) (rate(persistence_latency_bucket{operation=~\"CreateShard|GetShard|UpdateShard\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Shard CRUD Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 194 }, "id": 25, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_requests{operation=~\"CompleteTask|CreateTask|GetTaskListSize\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_empty_response{operation=~\"CompleteTask|CreateTask|GetTaskListSize\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Task CRUD", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 194 }, "id": 26, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, max by(le, operation) (rate(persistence_latency_bucket{operation=~\"CompleteTask|CreateTask|GetTaskListSize\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Task CRUD Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 202 }, "id": 27, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_requests{operation=~\"LeaseTaskList|UpdateTaskList|GetTaskListSize\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_empty_response{operation=~\"LeaseTaskList|UpdateTaskList|GetTaskListSize\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "TaskList CRUD", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 202 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, max by(le, operation) (rate(persistence_latency_bucket{operation=~\"LeaseTaskList|UpdateTaskList|GetTaskListSize\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "TaskList CRUD Latency", "type": "timeseries" } ], "title": "ShardMgr & TaskMgr", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 59 }, "id": 32, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 163 }, "id": 30, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_requests{operation=~\"CreateWorkflowExecution|UpdateWorkflowExecution|GetWorkflowExecution|DeleteWorkflowExecution\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors{operation=~\"CreateWorkflowExecution|UpdateWorkflowExecution|GetWorkflowExecution|DeleteWorkflowExecution\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B" } ], "title": "WorkflowExecution CRUD", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 163 }, "id": 31, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_bucket{operation=~\"CreateWorkflowExecution|UpdateWorkflowExecution|GetWorkflowExecution|DeleteWorkflowExecution\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "C", "useBackend": false } ], "title": "WorkflowExecution CRUD Latency", "type": "timeseries" } ], "title": "Execution Manager", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 60 }, "id": 35, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 164 }, "id": 33, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(persistence_requests{operation=~\"CreateDomain|DeleteDomain|UpdateDomain|DeleteDomainByName|GetDomain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(persistence_errors_entity_not_exists{operation=~\"CreateDomain|DeleteDomain|UpdateDomain|DeleteDomainByName|GetDomain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Domain CRUD", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "min": 4, "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 164 }, "id": 34, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_bucket{operation=~\"CreateDomain|DeleteDomain|UpdateDomain|DeleteDomainByName|GetDomain\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Domain CRUD Latency", "type": "timeseries" } ], "title": "MetadataManager", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 61 }, "id": 38, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 165 }, "id": 36, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_requests{operation=~\"RecordWorkflowExecutionStarted|RecordWorkflowExecutionClosed\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_errors_entity_not_exists{operation=~\"RecordWorkflowExecutionStarted|RecordWorkflowExecutionClosed\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Visibility Open/Close", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "dtdurationms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 165 }, "id": 37, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_bucket{operation=~\"RecordWorkflowExecutionStarted|RecordWorkflowExecutionClosed\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Visibility Open/Close Latency", "type": "timeseries" } ], "title": "VisibilityManager", "type": "row" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 62 }, "id": 45, "panels": [], "title": "Persistence Hot Shard Detection", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 63 }, "id": 44, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, shard_id) (rate(persistence_requests_per_shard{operation=\"UpdateWorkflowExecution\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Total updateworkflowexecution tracked shardId based requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 63 }, "id": 39, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(task_requests_per_domain{cadence_service=\"$services\", domain=\"$domain\", operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer Task Request Per Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 71 }, "id": 40, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(task_requests_per_domain{domain=\"$domain\", cadence_service=\"$services\", operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Task Request Per Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 71 }, "id": 43, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, shard_id) (rate(persistence_latency_per_shard_bucket{operation=\"ShardIdPersistenceRequest\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "shardidpersistencerequest p99 latency shardId", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 79 }, "id": 41, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_per_domain_bucket{domain=~\"$domain\", cadence_service=~\"$services\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Persistence Latency Per Domain", "type": "timeseries" } ], "preload": false, "refresh": "", "schemaVersion": 41, "tags": [], "templating": { "list": [ { "allowCustomValue": false, "current": { "text": "All", "value": "$__all" }, "definition": "label_values(cadence_requests,cadence_service)", "includeAll": false, "label": "services", "name": "services", "options": [], "query": { "qryType": 1, "query": "label_values(cadence_requests,cadence_service)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "allowCustomValue": false, "current": { "text": "upper", "value": "upper" }, "label": "latency", "name": "latency", "options": [ { "selected": false, "text": "p99", "value": "p99" }, { "selected": true, "text": "upper", "value": "upper" }, { "selected": false, "text": "mean", "value": "mean" }, { "selected": false, "text": "median", "value": "median" }, { "selected": false, "text": "lower", "value": "lower" } ], "query": "p99,upper,mean,median,lower", "type": "custom" }, { "allowCustomValue": true, "current": { "text": "samples_domain", "value": "samples_domain" }, "definition": "label_values(activity_end_to_end_latency_sum,domain)", "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_sum,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" } ] }, "time": { "from": "now-6h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Cadence-Persistence", "uid": "b00f7dd5-3dd6-4cf9-b17b-425e071e2b5b", "version": 1 } ================================================ FILE: docker/github_actions/grafana/provisioning/dashboards/cadence-server.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "datasource", "uid": "grafana" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "target": { "limit": 100, "matchAny": false, "tags": [], "type": "dashboard" }, "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 7, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 44, "panels": [], "title": "Frontend", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 6, "w": 23, "x": 0, "y": 1 }, "id": 57, "options": { "displayMode": "gradient", "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": false }, "maxVizHeight": 300, "minVizHeight": 16, "minVizWidth": 8, "namePlacement": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ "max" ], "fields": "", "values": false }, "showUnfilled": true, "sizing": "auto", "text": {}, "valueMode": "color" }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (cadence_requests{cadence_service=\"cadence_frontend\", operation=~\"SignalWithStartWorkflowExecution|SignalWorkflowxecution|StartWorkflowExecution|TerminateWorkflowExecution|ResetWorkflowExecution|RequestCancelWorkflowexecution|ListWorkflowExecutions\"})", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "{{domain}}", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "", "interval": "", "legendFormat": "", "refId": "A" } ], "title": "Top Domains for Regular Requests", "type": "bargauge" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 6, "w": 23, "x": 0, "y": 7 }, "id": 58, "options": { "displayMode": "gradient", "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": false }, "maxVizHeight": 300, "minVizHeight": 16, "minVizWidth": 8, "namePlacement": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ "mean" ], "fields": "", "values": false }, "showUnfilled": true, "sizing": "auto", "text": {}, "valueMode": "color" }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (cadence_requests{cadence_service=\"cadence_frontend\", operation=~\"PollForDecisionTask|GetWorkflowExecutionHistory|RespondDecisionTaskCompleted\"})", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "{{domain}}{{operation}}", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "cadence_requests{cadence_service=\"cadence_worker\",namespace=\"$namespace\"}", "hide": true, "interval": "", "legendFormat": "", "refId": "A" } ], "title": "Top Domains for Workflow Worker Requests", "type": "bargauge" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 6, "w": 23, "x": 0, "y": 13 }, "id": 60, "options": { "displayMode": "gradient", "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": false }, "maxVizHeight": 300, "minVizHeight": 16, "minVizWidth": 8, "namePlacement": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ "mean" ], "fields": "", "values": false }, "showUnfilled": true, "sizing": "auto", "text": {}, "valueMode": "color" }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (cadence_requests{cadence_service=\"cadence_frontend\"})", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "{{domain}}{{operation}}", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "cadence_requests{cadence_service=\"cadence_worker\",namespace=\"$namespace\"}", "hide": true, "interval": "", "legendFormat": "", "refId": "A" } ], "title": "Top Domains for All Requests", "type": "bargauge" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 6, "w": 23, "x": 0, "y": 19 }, "id": 59, "options": { "displayMode": "gradient", "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": false }, "maxVizHeight": 300, "minVizHeight": 16, "minVizWidth": 8, "namePlacement": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ "mean" ], "fields": "", "values": false }, "showUnfilled": true, "sizing": "auto", "text": {}, "valueMode": "color" }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(cadence_requests{cadence_service=\"cadence_frontend\",namespace=\"$namespace\", operation=~\"PollForActivityTask|RespondActivityTaskCompleted\"}) by (domain)", "hide": false, "interval": "", "legendFormat": "{{domain}}{{operation}}", "refId": "B" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "cadence_requests{cadence_service=\"cadence_worker\",namespace=\"$namespace\"}", "hide": true, "interval": "", "legendFormat": "", "refId": "A" } ], "title": "Top Domains for Activity Worker Requests", "type": "bargauge" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 25 }, "id": 48, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (rate(cadence_requests{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "All API per second per domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 25 }, "id": 49, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "API per second(breakdown per operation)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 33 }, "id": 50, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(cadence_latency_bucket{operation!~\"CountWorkflowExecutions|GetWorkflowExecutionHistory|ListClosedWorkflowExecutions|ListOpenWorkflowExecutions|ListWorkflowExecutions|PollForActivityTask|PollForDecisionTask|QueryWorkflow\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "{{operation}}", "range": true, "refId": "B", "useBackend": false } ], "title": "Regular API Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 33 }, "id": 51, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(cadence_latency_bucket{operation=~\"CountWorkflowExecutions|GetWorkflowExecutionHistory|ListClosedWorkflowExecutions|ListOpenWorkflowExecutions|ListWorkflowExecutions|PollForActivityTask|PollForDecisionTask|QueryWorkflow\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "ListWorkflow API Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 41 }, "id": 52, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"PollForDecisionTask\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"PollForActivityTask\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": true, "interval": "", "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Long Poll API Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 41 }, "id": 56, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, operation) (cadence_requests{cadence_service=\"cadence_frontend\", operation!~\"CountWorkflowExecutions|GetWorkflowExecutionHistory|ListClosedWorkflowExecutions|ListOpenWorkflowExecutions|ListWorkflowExecutions|PollForActivityTask|PollForDecisionTask|QueryWorkflow\"})", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "Domain: {{domain}}- Operation: {{operation}}", "range": true, "refId": "B", "useBackend": false } ], "title": "Regular API Per Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 49 }, "id": 54, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_errors{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "Internal server error", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_bad_request{cadence_service=\"cadence_frontend\"}[2m]))", "hide": false, "interval": "", "legendFormat": "Bad Requests error", "refId": "B" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_query_failed{cadence_service=\"cadence_frontend\"}[2m]))", "interval": "", "legendFormat": "QueryFailed error", "refId": "C" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_context_timeout{cadence_service=\"cadence_frontend\"}[2m]))", "interval": "", "legendFormat": "ContextTimeout error", "refId": "D" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_entity_not_exists{cadence_service=\"cadence_frontend\"}[2m]))", "interval": "", "legendFormat": "EntityNotExists error", "refId": "E" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_execution_already_started{cadence_service=\"cadence_frontend\"}[2m]))", "interval": "", "legendFormat": "WorkflowAlreadyStarted error", "refId": "F" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_workflow_execution_already_completed{cadence_service=\"cadence_frontend\"}[2m]))", "interval": "", "legendFormat": "WorkflowAlreadyCompleted", "refId": "G" } ], "title": "API errors per second(breakdown per operation)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 49 }, "id": 53, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"cadence_frontend\", operation=~\"GetWorkflowExecutionHistory|QueryWorkflow\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "GetHistory/QueryWorkflow API Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 57 }, "id": 55, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, operation) (changes(cadence_requests{cadence_service=\"cadence_frontend\", operation=~\"SignalWithStartworkflowExecution|SignalWorkflowExecution|StartWorkflowExecution|TerminateWorkflowExecution|ResetWorkflowExecution|RequestCancelWorkflowExecution|ListWorkflowExecutions\", domain=\"$domain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "Domain: {{domain}}- Operation: {{operation}}", "range": true, "refId": "B", "useBackend": false } ], "title": "WorkflowClient API per seconds by domain", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 65 }, "id": 96, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 641 }, "id": 93, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(restarts[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Service Restarts", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 641 }, "id": 95, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(acquire_shards_count[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (shard acquire count)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(sharditem_created_count[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "shard item created count", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (changes(numshards_gauge[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "num of shard gauge", "range": true, "refId": "C", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (changes(persistence_requests_per_shard[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "persistence requests per shard", "range": true, "refId": "D", "useBackend": false } ], "title": "Shard Movements per sec", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1537 }, "id": 94, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "avg by(cadence_service) (num_goroutines)", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Goroutines (avg)", "type": "timeseries" } ], "title": "Service Metrics", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 66 }, "id": 101, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1618 }, "id": 97, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(cadence_requests{cadence_service=\"cadence_history\", operation=~\"StartWorkflowExecution|SignalWorkflowExecution\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "External Events per sec", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1618 }, "id": 98, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=~\"RespondActivityTaskCompleted|RecordActivityTaskHeartbeat|RecordActivityTaskStarted|RespondDecisionTaskCompleted\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activities per sec ", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1626 }, "id": 100, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(cadence_requests{operation=~\"PollForActivityTask|PollForDecisionTask|CancelOutstandingPoll\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (changes(poll_success_sync{cadence_service=\"cadence_matching\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "poll success sync", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(poll_timeouts{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "poll timeout", "range": true, "refId": "C", "useBackend": false } ], "title": "Polls per sec", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1626 }, "id": 99, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=~\"RecordDecisionTaskStarted|RespondDecisionTaskCompleted\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decisions per sec", "type": "timeseries" } ], "title": "Rates per second", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 67 }, "id": 106, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, "x": 0, "y": 643 }, "id": 102, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_version) (build_information{cadence_service=\"cadence_frontend\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Versions", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, "x": 6, "y": 643 }, "id": 103, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_version) (build_information{cadence_service=\"cadence_history\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History Versions", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, "x": 12, "y": 643 }, "id": 105, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_version) (build_information{cadence_service=\"cadence_worker\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Worker Versions", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, "x": 18, "y": 643 }, "id": 104, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_version) (build_information{cadence_service=\"cadence_matching\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Matching Versions", "type": "timeseries" } ], "title": "Server Version Metrics", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 68 }, "id": 111, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 644 }, "id": 107, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(task_requests{cadence_service=\"cadence_history\", operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer Tasks Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "TimerActiveTaskActivityTimeout" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 644 }, "id": 108, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (task_latency_bucket{operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\"}))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer Task Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "TimerActiveTaskActivityTimeout" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1108 }, "id": 109, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (changes(task_requests_per_domain{operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer Tasks Per Domain per sec", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "TimerActiveTaskActivityTimeout" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1108 }, "id": 110, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, domain) (rate(task_latency_per_domain_bucket{operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Timer Task Latency", "type": "timeseries" } ], "title": "Timer Task Processing", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 69 }, "id": 117, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 24, "x": 0, "y": 645 }, "id": 112, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(__name__) (rate(task_requests{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "requests", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(__name__) (rate(task_errors{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "errors", "range": true, "refId": "B", "useBackend": false } ], "title": "Transfer Tasks Requests Vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 797 }, "id": 116, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(task_requests{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Tasks Requests Per Type", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 797 }, "id": 113, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.99, sum by(le, operation) (rate(task_latency_bucket{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Task Latency (upper)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 805 }, "id": 114, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (rate(task_requests_per_domain{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Tasks Per Domain (Top 20)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 805 }, "id": 115, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.99, sum by(le, domain) (rate(task_latency_per_domain_bucket{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Task upper Latency per Domain (Top 20)", "type": "timeseries" } ], "title": "Transfer Task Processing", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 70 }, "id": 125, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 641 }, "id": 118, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, domain) (rate(history_size_bucket{operation=\"ExecutionStats\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History Size By Domain (Top 20)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 641 }, "id": 119, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, domain) (rate(history_count_bucket{operation=~\"ExecutionStats\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History Event Count By Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1105 }, "id": 120, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, domain) (rate(event_blob_size_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Blob Sizes Top Domains", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1105 }, "id": 121, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, operation) (rate(event_blob_size_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Blob Sizes Top Operations", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ScheduleActivityTask" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1113 }, "id": 123, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, domain) (rate(event_blob_size_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Blob Sizes by Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ScheduleActivityTask" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1113 }, "id": 122, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, decisionType) (rate(event_blob_size_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Blob Sizes by DecisionType", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ScheduleActivityTask" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1121 }, "id": 124, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, domain) (rate(decision_result_count_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Result Count Top Domains", "type": "timeseries" } ], "title": "Size and Count Limits", "type": "row" } ], "preload": false, "refresh": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "current": { "text": "all", "value": "all" }, "datasource": "Prometheus", "definition": "label_values(activity_end_to_end_latency_sum,domain)", "includeAll": false, "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_sum,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" } ] }, "time": { "from": "now-5m", "to": "now" }, "timepicker": { "refresh_intervals": [ "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d" ] }, "timezone": "", "title": "Cadence-Server", "uid": "1B0efRyGz", "version": 1 } ================================================ FILE: docker/github_actions/grafana/provisioning/dashboards/default.yaml ================================================ apiVersion: 1 providers: - name: Cadence-Basic # A uniquely identifiable name for the provider folder: Cadence type: file options: path: /etc/grafana/provisioning/dashboards/ ================================================ FILE: docker/github_actions/grafana/provisioning/datasources/default.yaml ================================================ datasources: - name: Prometheus type: prometheus url: http://prometheus:9090 version: 1 editable: true ================================================ FILE: docker/github_actions/prometheus/.gitignore ================================================ *.log ================================================ FILE: docker/github_actions/prometheus/matching_simulation_prometheus.yml ================================================ global: scrape_interval: 5s external_labels: monitor: 'cadence-monitor' query_log_file: /etc/prometheus/query.log scrape_failure_log_file: /etc/prometheus/scrape.log scrape_configs: - job_name: 'prometheus' static_configs: - targets: # addresses to scrape - 'cadence:8306' - 'cadence:8307' - 'cadence:8308' - 'cadence:8309' ================================================ FILE: docker/github_actions/prometheus/replication_simulation_prometheus.yml ================================================ global: scrape_interval: 5s external_labels: monitor: 'cadence-monitor' query_log_file: /etc/prometheus/query.log scrape_failure_log_file: /etc/prometheus/scrape.log scrape_configs: - job_name: 'prometheus' static_configs: - targets: # addresses to scrape from cluster0 - 'cadence-cluster0:7000' # frontend - 'cadence-cluster0:7001' # matching - 'cadence-cluster0:7002' # history - 'cadence-cluster0:7003' # worker labels: cluster: 'cluster0' - targets: # addresses to scrape from cluster1 - 'cadence-cluster1:8000' # frontend - 'cadence-cluster1:8001' # matching - 'cadence-cluster1:8002' # history - 'cadence-cluster1:8003' # worker labels: cluster: 'cluster1' ================================================ FILE: docker/grafana/grafana.ini ================================================ [auth.anonymous] enabled = true org_role = Admin ================================================ FILE: docker/grafana/provisioning/dashboards/cadence-archival.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 6, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 8, "panels": [], "title": "Overall", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "history" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 1, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (changes(archiver_client_history_request{operation=\"ArchiverClient\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "history", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(archiver_client_visibility_request{operation=\"ArchiverClient\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "visibility", "range": true, "refId": "B", "useBackend": false } ], "title": "Archivals Triggered Per Second", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "archiver_client_visibility_inline_archive_attempt" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 2, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (changes(archiver_client_history_request{operation=\"ArchiverClient\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "archiver_client_history_request", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (changes(archiver_client_history_inline_archive_attempt{operation=\"ArchiverClient\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "archiver_client_history_inline_archive_attempt", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(archiver_client_visibility_request{operation=\"ArchiverClient\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "archiver_client_visibility_request", "range": true, "refId": "C", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(archiver_client_visibility_inline_archive_attempt{operation=\"ArchiverClient\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "archiver_client_visibility_inline_archive_attempt", "range": true, "refId": "D", "useBackend": false } ], "title": "Client Requests vs Inline Attempts", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "archiver_client_history_request" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 9 }, "id": 3, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(archiver_client_history_request{operation=\"ArchiverClient\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "archiver_client_history_request", "range": true, "refId": "A", "useBackend": false } ], "title": "Archiver History Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 9 }, "id": 4, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(archiver_client_history_inline_archive_attempt{operation=\"ArchiverClient\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "archiver_client_history_inline_archive_attempt", "range": true, "refId": "A", "useBackend": false } ], "title": "Client History Inline Attemptss", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 17 }, "id": 5, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, operation) (rate(archiver_client_history_request_per_domain{domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "", "hide": false, "instant": false, "range": true, "refId": "B" } ], "title": "History Requests Per Domain Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 17 }, "id": 6, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, operation) (rate(archiver_client_history_inline_archive_attempt_per_domain[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History Inline Attempt Per Domain Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 25 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(workflow_cleanup_archive[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Workflow Cleanup Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 25 }, "id": 9, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "", "hide": false, "instant": false, "range": true, "refId": "A" } ], "title": "History Inline Attempt Per Domain Per Operation", "type": "timeseries" } ], "preload": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "allowCustomValue": false, "current": { "text": "", "value": "" }, "definition": "label_values(activity_end_to_end_latency_sum,domain)", "includeAll": false, "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_sum,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "0.99", "value": "0.99" }, "label": "Quantiles", "name": "quantiles", "options": [ { "selected": false, "text": "0.5", "value": "0.5" }, { "selected": false, "text": "0.95", "value": "0.95" }, { "selected": true, "text": "0.99", "value": "0.99" } ], "query": "0.5, 0.95, 0.99", "type": "custom" } ] }, "time": { "from": "now-6h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Cadence-Archival", "uid": "1B0efRyGx", "version": 1 } ================================================ FILE: docker/grafana/provisioning/dashboards/cadence-client-overall.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 8, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 10, "panels": [], "title": "Overview", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 44, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(workflowType) (rate(workflow_success[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "builder", "expr": "sum by(workflowType) (rate(workflow_cancel[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "builder", "expr": "sum by(workflowType) (rate(workflow_continued_as_new[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "C" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "builder", "expr": "sum by(workflowType) (rate(workflow_failed[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "D" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "builder", "expr": "sum by(workflowType) (rate(workflow_timeout[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "E" } ], "title": "Workflow Success Counters", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 9, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "max by(active_cluster) (active_cluster{cadence_service=\"cadence_history\", operation=\"DomainCache\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Deploymets", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 9 }, "id": 11, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_client_requests[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests per operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "HistoryClientRespondDecisionTaskCompleted" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": true, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 9 }, "id": 16, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_client_latency_bucket[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": " Latency Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 17 }, "id": 14, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "avg by(operation) (rate(cadence_client_errors{domain!~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Errors per operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 17 }, "id": 20, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(schedule_activity_decision[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity decision per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 25 }, "id": 17, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(TaskList) (rate(cadence_activity_poll_total{domain=~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(TaskList) (rate(cadence_activity_poll_transient_failed[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Activity poll counter", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 25 }, "id": 18, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(workflow_started_count[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Workflow Started Count", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 33 }, "id": 19, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(activity_end_to_end_latency_sum{domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity end to end processing time", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 41 }, "id": 2, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 66 }, "id": 1, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(global_ratelimit_collection) (rate(global_ratelimiter_allowed_requests{is_primary=\"true\", cadence_service=\"cadence_frontend\", operation=\"GlobalRatelimiter\", global_ratelimit_key=~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Limiter in use", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 66 }, "id": 3, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "max by(global_ratelimit_key, global_ratelimit_collection) (global_ratelimiter_quota{global_ratelimit_key=\"$domain\"})", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Quotas", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 90 }, "id": 5, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(global_ratelimit_collection, global_ratelimit_key, global_ratelimit_type) (rate(global_ratelimiter_allowed_requests{is_primary=\"true\", cadence_service=\"cadence_frontend\", operation=\"GlobalRatelimiter\", global_ratelimit_key=~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 90 }, "id": 4, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum(rate(global_ratelimiter_allowed_requests{is_primary=\"true\", cadence_service=\"cadence_frontend\", operation=\"GlobalRatelimiter\", global_ratelimit_type=~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Allowed", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 98 }, "id": 6, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(global_ratelimit_collection, global_ratelimit_key, global_ratelimit_type) (rate(global_ratelimiter_rejected_requests{is_primary=\"true\", cadence_service=\"cadence_frontend\", operation=\"GlobalRatelimiter\", global_ratelimit_key=~\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Rejected", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 98 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(global_ratelimit_collection, global_ratelimit_key, global_ratelimit_type) (rate(global_ratelimiter_rejected_requests{is_primary=\"false\", cadence_service=\"cadence_frontend\", operation=\"GlobalRatelimiter\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Rejected (shadow)", "type": "timeseries" } ], "title": "Ratelimiter", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 42 }, "id": 29, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 67 }, "id": 26, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, domain) (rate(cadence_workflow_endtoend_latency_bucket{domain=~\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Workflow End to End Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 67 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(namespace, domain) (rate(cadence_worker_panic{domain=~\"$domain\"}[2m]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "NonDeterministicError and Worker Panic", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 75 }, "id": 27, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "editorMode": "code", "expr": "sum(count(cadence_sticky_cache_total_forced_eviction{domain=\"$domain\"})) by (domain)", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Workflow Sticky Cache Eviction", "type": "timeseries" } ], "title": "Workflow", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 43 }, "id": 34, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 340 }, "id": 30, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "editorMode": "builder", "expr": "sum(count(cadence_activity_task_failed{domain=\"$domain\"})) by (activitytype)", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Activity Task Operations", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 340 }, "id": 33, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "avg by(domain, tasklist, activitytype) (cadence_activity_execution_latency{domain=~\"$domain\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Execution Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 372 }, "id": 31, "options": { "legend": { "calcs": [ "mean", "max", "min", "sum", "lastNotNull" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, tasklist) (rate(cadence_requests_per_tl{cadence_service=\"cadence_matching\", domain=~\"$domain\"}[5m]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "ActivityTasks scheduled per Second", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 372 }, "id": 32, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.2", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "avg by(Domain, TaskList, ActivityType) (cadence_activity_scheduled_to_start_latency_count{domain=~\"$domain\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Scheduled To Start Latency", "type": "timeseries" } ], "title": "Activity", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 44 }, "id": 37, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 205 }, "id": 35, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "editorMode": "code", "expr": "(1 - count(cadence_error{domain=\"$domain\"})by(domain)/count(cadence_request{domain=\"$domain\"})by(domain)) * 100", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Service API success rate", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 205 }, "id": 36, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "editorMode": "code", "expr": "sum(avg_over_time(cadence_latency{Domain=\"$domain\"} [1m])) by (Domain)", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Service API Latency (TODO exclude long-poll APIs)", "type": "timeseries" } ], "title": "Service", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 45 }, "id": 24, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 214 }, "id": 22, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "editorMode": "code", "expr": "histogram_quantile($quantiles, (max by (le, domain) (history_size_bucket)))*1000000000", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Max History Size", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 214 }, "id": 23, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "editorMode": "code", "expr": "sum by (domain) (history_count_sum{domain!=\"all\"}/history_count_count{domain!=\"all\"})\n*1000000000", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Avg History Length ", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 238 }, "id": 25, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.0", "targets": [ { "editorMode": "code", "expr": "max(histogram_quantile(1, max(event_blob_size_bucket) by (le)))*1000000000", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Max Event Blob Size", "type": "timeseries" } ], "title": "History", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 46 }, "id": 38, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "custom": { "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "scaleDistribution": { "type": "linear" } } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 215 }, "id": 40, "options": { "calculate": false, "cellGap": 1, "color": { "exponent": 0.5, "fill": "dark-orange", "mode": "scheme", "reverse": false, "scale": "exponential", "scheme": "Oranges", "steps": 64 }, "exemplars": { "color": "rgba(255,0,255,0.7)" }, "filterValues": { "le": 1e-9 }, "legend": { "show": true }, "rowsFrame": { "layout": "auto" }, "tooltip": { "mode": "single", "showColorScale": false, "yHistogram": false }, "yAxis": { "axisPlacement": "left", "min": 0, "reverse": false, "unit": "short" } }, "pluginVersion": "12.2.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "exemplar": false, "expr": "rate(cadence_concurrency_auto_scaler_poller_quota_bucket{WorkerType=\"ActivityWorker\",Domain=\"$domain\"}[$__rate_interval])", "format": "heatmap", "fullMetaSearch": false, "includeNullMetadata": true, "instant": false, "interval": "", "legendFormat": "{{le}}", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Poller Quota", "type": "heatmap" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "custom": { "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "scaleDistribution": { "type": "linear" } } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 215 }, "id": 41, "options": { "calculate": false, "cellGap": 1, "color": { "exponent": 0.5, "fill": "dark-orange", "mode": "scheme", "reverse": false, "scale": "exponential", "scheme": "Oranges", "steps": 64 }, "exemplars": { "color": "rgba(255,0,255,0.7)" }, "filterValues": { "le": 1e-9 }, "legend": { "show": true }, "rowsFrame": { "layout": "auto" }, "tooltip": { "mode": "single", "showColorScale": false, "yHistogram": false }, "yAxis": { "axisPlacement": "left", "min": 0, "reverse": false, "unit": "short" } }, "pluginVersion": "12.2.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "exemplar": false, "expr": "rate(cadence_concurrency_auto_scaler_poller_quota_bucket{WorkerType=\"DecisionWorker\", Domain=\"$domain\"}[$__rate_interval])", "format": "heatmap", "fullMetaSearch": false, "includeNullMetadata": true, "instant": false, "interval": "", "legendFormat": "{{le}}", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Poller Quota", "type": "heatmap" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "lower wait time indicates busy pollers and thus need more poller to avoid high schedule to start latency", "fieldConfig": { "defaults": { "custom": { "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "scaleDistribution": { "type": "linear" } } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 223 }, "id": 43, "options": { "calculate": false, "cellGap": 1, "color": { "exponent": 0.5, "fill": "dark-orange", "mode": "scheme", "reverse": false, "scale": "exponential", "scheme": "Oranges", "steps": 64 }, "exemplars": { "color": "rgba(255,0,255,0.7)" }, "filterValues": { "le": 1e-9 }, "legend": { "show": true }, "rowsFrame": { "layout": "auto" }, "tooltip": { "mode": "single", "showColorScale": false, "yHistogram": false }, "yAxis": { "axisPlacement": "left", "reverse": false, "unit": "ms" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "rate(cadence_concurrency_auto_scaler_poller_wait_time_bucket{WorkerType=\"ActivityWorker\",Domain=\"$domain\"}[ $__rate_interval])", "instant": false, "legendFormat": "{{le}}", "range": true, "refId": "A" } ], "title": "Activity Poller Wait Time", "type": "heatmap" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "lower wait time indicates busy pollers and thus need more poller to avoid high schedule to start latency", "fieldConfig": { "defaults": { "custom": { "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "scaleDistribution": { "type": "linear" } } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 223 }, "id": 42, "options": { "calculate": false, "cellGap": 1, "color": { "exponent": 0.5, "fill": "dark-orange", "mode": "scheme", "reverse": false, "scale": "exponential", "scheme": "Oranges", "steps": 64 }, "exemplars": { "color": "rgba(255,0,255,0.7)" }, "filterValues": { "le": 1e-9 }, "legend": { "show": true }, "rowsFrame": { "layout": "auto" }, "tooltip": { "mode": "single", "showColorScale": false, "yHistogram": false }, "yAxis": { "axisPlacement": "left", "reverse": false, "unit": "ms" } }, "pluginVersion": "12.2.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "rate(cadence_concurrency_auto_scaler_poller_wait_time_bucket{WorkerType=\"DecisionWorker\", Domain=\"$domain\"}[$__rate_interval])", "instant": false, "legendFormat": "{{le}}", "range": true, "refId": "A" } ], "title": "Decision Poller Wait Time", "type": "heatmap" } ], "title": "Concurrency", "type": "row" } ], "preload": false, "schemaVersion": 42, "tags": [], "templating": { "list": [ { "current": { "text": "", "value": "" }, "definition": "label_values(activity_end_to_end_latency_sum,domain)", "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_sum,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "0.99", "value": "0.99" }, "label": "Quantiles", "name": "quantiles", "options": [ { "selected": false, "text": "0.5", "value": "0.5" }, { "selected": false, "text": "0.95", "value": "0.95" }, { "selected": true, "text": "0.99", "value": "0.99" } ], "query": "0.5, 0.95, 0.99", "type": "custom" } ] }, "time": { "from": "now-6h", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Cadence-Client", "uid": "dehkspwgabvuoc", "version": 1 } ================================================ FILE: docker/grafana/provisioning/dashboards/cadence-frontend.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 3, "links": [], "panels": [ { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 6, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 2, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Request Counts Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 3, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_errors{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_errors_entity_not_exists{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_errors_per_tl{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "C", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "rate(cadence_PollForActivityTask_cadence_error{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval])", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "D", "useBackend": false } ], "title": "Error Breakdown", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 25 }, "id": 4, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "rate(cadence_errors{cadence_service=\"cadence_frontend\"}[$__rate_interval])", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Error Counts Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 25 }, "id": 5, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_errors{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Request Vs Error", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "ms", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 33 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend API Latencies", "type": "timeseries" } ], "title": "Overall", "type": "row" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 1 }, "id": 12, "panels": [], "title": "Frontend-Client", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 2 }, "id": 8, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_errors{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_requests{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Frontend Client Request Vs Error", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 2 }, "id": 9, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_requests{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Client Request Counts Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "ms", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 10 }, "id": 11, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_client_latency_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Client API Latencies", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 10 }, "id": 10, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_errors{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Client Request Counts Per API", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 18 }, "id": 27, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 19 }, "id": 13, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"StartWorkflowExecution\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "StartWorfklowExecution", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "ms", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 19 }, "id": 14, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"StartWorkflowExecution\", domain=\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "StartWorkflowExection Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 27 }, "id": 21, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, tasklist) (rate(cadence_requests_per_tl{cadence_service=\"$services\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests per operation per task", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 27 }, "id": 26, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, tasklist) (rate(workflow_success{cadence_service=\"$services\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Workflow success per operation per task", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 35 }, "id": 22, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"RespondActivityTaskCompleted\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Respond Activity task completed", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "sec", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 35 }, "id": 25, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"RespondActivityTaskCompleted\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "RespondActivity Task Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 43 }, "id": 17, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(TaskList) (rate(cadence_PollForActivityTask_cadence_request[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForActivityTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "ms", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 43 }, "id": 23, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"PollForActivityTask\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForActivityTask Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 51 }, "id": 19, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(TaskList) (rate(cadence_PollForDecisionTask_cadence_request[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForDecisionTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 51 }, "id": 24, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"PollForDecisionTask\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForDecisionTask Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 59 }, "id": 15, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_requests{operation=\"DescribeDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "DescribeDomain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 59 }, "id": 16, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"DescribeDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "DescribeDomain Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 67 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_requests{operation=\"GetWorkflowExecutionHistory\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GetWorkflowExecutionHistory", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 67 }, "id": 29, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"CancelWorkflowExecution\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "CancelWorkflowExecution", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 75 }, "id": 30, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"TerminateWorkflowExecution\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "TerminateWorkflowExecution", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 75 }, "id": 31, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"RegisterDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "RegisterDomain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 83 }, "id": 33, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"DescribeDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "DescribeDomain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 83 }, "id": 32, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"UpdateDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "UpdateDomain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 91 }, "id": 34, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"DepricateDomain\", cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "DepricateDomain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 91 }, "id": 35, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"AddDecisionTask\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddDecisionTask", "type": "timeseries" } ], "title": "API", "type": "row" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 19 }, "id": 39, "panels": [], "title": "Persistence", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 20 }, "id": 36, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_requests{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Requests Vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 20 }, "id": 37, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_requests{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "sec", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 24, "x": 0, "y": 28 }, "id": 38, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, avg by(operation, le) (rate(persistence_latency_bucket{cadence_service=\"$services\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors_entity_not_exists{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Operation Latencies p99", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 36 }, "id": 42, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "{le=\"1.0000000000000001e-07\", operation=\"StartWorkflowExecution\"}" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 317 }, "id": 40, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(event_blob_size_bucket{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Overall Query Sizes", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "{le=\"1.0000000000000001e-07\", operation=\"StartWorkflowExecution\"}" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 317 }, "id": 41, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, domain) (rate(event_blob_size_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Query Sizes by Domain", "type": "timeseries" } ], "title": "Query sizes", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 37 }, "id": 44, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "histogram_quantile(0.95, sum by(le) (rate(cadence_authorization_latency_bucket{cadence_service=\"cadence_frontend\"}[1m0s])))" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 318 }, "id": 43, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_authorization_latency_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Authorization latency per API", "type": "timeseries" } ], "title": "Authorization", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 38 }, "id": 52, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 319 }, "id": 47, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_allocated{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Total Memory Allocation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "NumGC is the number of completed GC cycles.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 319 }, "id": 46, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(memory_num_gc{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GC Num", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 327 }, "id": 45, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(num_goroutines{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Number of goroutines", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "HeapInuse is bytes in in-use spans.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 327 }, "id": 49, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_heapinuse{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Heap in Use (HeapAlloc)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "HeapIdle is bytes in idle (unused) spans.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 335 }, "id": 48, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_heapidle{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Heap Available (HeapIdle)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "Bytes of stack memory obtained from the OS. Plus any memory obtained directly from the OS for OS thread stacks.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 335 }, "id": 51, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(memory_stack{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Stack In use", "type": "timeseries" } ], "title": "Go Gc Detailed", "type": "row" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 39 }, "id": 57, "panels": [], "title": "Replication", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 40 }, "id": 54, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "rate(cadence_requests{operation=\"GetReplicationMessage\", \"$services\"=\"$services\"}[$__rate_interval])", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GetReplicationMessage Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 40 }, "id": 53, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "rate(cadence_requests{operation=\"GetDomainReplicationMessage\", \"$services\"=\"$services\"}[$__rate_interval])", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GetDomainReplicationMessage Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 48 }, "id": 55, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le) (rate(cadence_latency_bucket{operation=\"GetReplicationMessage\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GetReplicationMessage Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 48 }, "id": 56, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le) (rate(cadence_latency_bucket{operation=\"GetDomainReplicationMessage\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GetDomainReplicationMessage Latency", "type": "timeseries" } ], "preload": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "current": { "text": "samples_domain", "value": "samples_domain" }, "definition": "label_values(activity_end_to_end_latency_bucket,domain)", "includeAll": true, "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_bucket,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "cadence_frontend", "value": "cadence_frontend" }, "definition": "label_values(cadence_requests,cadence_service)", "label": "services", "name": "services", "options": [], "query": { "qryType": 1, "query": "label_values(cadence_requests,cadence_service)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "p99", "value": "p99" }, "label": "latency", "name": "latency", "options": [ { "selected": true, "text": "p99", "value": "p99" }, { "selected": false, "text": "upper", "value": "upper" }, { "selected": false, "text": "mean", "value": "mean" }, { "selected": false, "text": "median", "value": "median" }, { "selected": false, "text": "lower", "value": "lower" } ], "query": "p99,upper,mean,median,lower", "type": "custom" } ] }, "time": { "from": "now-5m", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Cadence-Frontend", "uid": "cekm8fs6ps000f", "version": 1 } ================================================ FILE: docker/grafana/provisioning/dashboards/cadence-history.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 5, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 9, "panels": [], "title": "API", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Request Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 8, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History API p99 Latencies", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 9 }, "id": 26, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 505 }, "id": 21, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(complete_workflow_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(schedule_activity_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(fail_workflow_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "C" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cancel_workflow_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "D" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(start_timer_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "E" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cancel_activity_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "F" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cancel_timer_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "G" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cancel_activity_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "H" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(record_marker_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "I" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cancel_external_workflow_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "J" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(continue_as_new_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "K" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(child_workflow_decision{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "L" } ], "title": "Decision Breakdown", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 505 }, "id": 22, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(multiple_completion_decisions{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(failed_decisions{operation=\"RespondDecisionTaskCompleted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B" } ], "title": "Bad Decisions", "type": "timeseries" } ], "title": "Decisions", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 10 }, "id": 14, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 505 }, "id": 10, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(cadence_client_requests{cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{cadence_service}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (changes(cadence_client_errors{cadence_service=\"cadence_history\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{cadence_service}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "History Clients Requests Vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 505 }, "id": 11, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_client_requests{cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (changes(cadence_client_errors{cadence_service=\"cadence_history\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "History Clients Request Count Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 513 }, "id": 12, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(cadence_client_errors{cadence_service=\"cadence_history\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "History Client Error Counts Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 513 }, "id": 13, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_client_latency_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History Clients API Latencies", "type": "timeseries" } ], "title": "History Clients", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 11 }, "id": 20, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 505 }, "id": 15, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cache_requests{cache_type=\"mutablestate\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cache_miss{cadence_service=\"cadence_history\", cache_type=\"mutablestate\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (miss)", "range": true, "refId": "B", "useBackend": false } ], "title": "Mutable State Cache", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 505 }, "id": 16, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cache_latency_bucket{cache_type=\"mutablestate\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Mutable State Cache Latencies", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 513 }, "id": 17, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cache_requests{cache_type=\"events\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cache_miss{cadence_service=\"cadence_history\", cache_type=\"events\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (miss)", "range": true, "refId": "B", "useBackend": false } ], "title": "Events Cache", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 513 }, "id": 18, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cache_latency_bucket{cache_type=\"mutablestate\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Events Cache Latencies", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "WorkflowContext" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 521 }, "id": 19, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, operation) (rate(workflow_context_lock_latency_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Execution Lock Held Duration", "type": "timeseries" } ], "title": "History Caches", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 12 }, "id": 30, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 9 }, "id": 23, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(acquire_shards_count{cadence_service=\"cadence_history\", operation=\"ShardController\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Shard Movement", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 9 }, "id": 27, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(get_engine_for_shard_latency_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (get_engine)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_per_shard_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (persistence_latency_per_shard)", "range": true, "refId": "B", "useBackend": false } ], "title": "p99 Latencies", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ShardInfo (shard info replication)" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 457 }, "id": 25, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(shardinfo_transfer_lag_count{cadence_service=\"cadence_history\", operation=\"ShardInfo\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (shard info transfer)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(shardinfo_replication_lag_count{cadence_service=\"cadence_history\", operation=\"ShardInfo\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (shard info replication)", "range": true, "refId": "B", "useBackend": false } ], "title": "Replication / Transfer p99 Lag (buggy)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 457 }, "id": 24, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_timer_lag_bucket{operation=\"ShardInfo\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer p99 Lag", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ShardInfo" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 465 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_transfer_diff_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Level p99 Diff", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ShardInfo" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 465 }, "id": 29, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_timer_diff_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer Level p99 Diff", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ShardInfo (timer failover)" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 473 }, "id": 31, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_transfer_failover_in_progress_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (transfer failover)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_timer_failover_in_progress_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (timer failover)", "range": true, "refId": "B", "useBackend": false } ], "title": "Failover p99 In Progress", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ShardInfo (transfer active pending task)" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 473 }, "id": 32, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_transfer_active_pending_task_bucket[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (transfer active pending task)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(shardinfo_timer_active_pending_task_bucket[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (timer active pending task)", "range": true, "refId": "B", "useBackend": false } ], "title": "Pending p99 Tasks", "type": "timeseries" } ], "title": "ShardController", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 13 }, "id": 38, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 14 }, "id": 33, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(persistence_requests{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "persistence_requests", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "rate(persistence_errors_entity_not_exists{cadence_service=\"cadence_history\"}[$__rate_interval])", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "persistence_errors", "range": true, "refId": "B", "useBackend": false } ], "title": "Requests vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 14 }, "id": 34, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "rate(persistence_errors_entity_not_exists{cadence_service=\"cadence_history\"}[$__rate_interval])", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "EntityNotExists", "range": true, "refId": "B", "useBackend": false } ], "title": "Error Breakdown", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 118 }, "id": 35, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_requests{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 118 }, "id": 36, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors_entity_not_exists{cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Error Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 126 }, "id": 37, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "avg by(operation) (rate(persistence_latency_bucket{cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Operation p99 Latency", "type": "timeseries" } ], "title": "Persistence", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 14 }, "id": 47, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 15 }, "id": 39, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(ack_level_update{cadence_service=\"cadence_history\", operation=\"TimerActiveQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Ack Level Update", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "TimerActiveQueueProcessor" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 15 }, "id": 40, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(ack_level_update{cadence_service=\"cadence_history\", operation=\"TimerStandByQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Standby Ack Level Update", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 23 }, "id": 41, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(new_timer_notifications{operation=\"TimerActiveQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Notifications", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 23 }, "id": 42, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "rate(new_timer_notifications{operation=\"TimerStandByQueueProcessor\"}[$__rate_interval])", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Standby New Timer Notifications", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 31 }, "id": 43, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(task_requests{operation=\"TimerActiveTaskActivityTimeout\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 31 }, "id": 44, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_latency_processing_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Processing p99 Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 39 }, "id": 46, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_attempt_bucket{operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer p99 Attempt", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 39 }, "id": 45, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_latency_queue_bucket{operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Queue p99 Latency", "type": "timeseries" } ], "title": "Timer Processor", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 15 }, "id": 48, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 16 }, "id": 49, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(ack_level_update{cadence_service=\"cadence_history\", operation=\"TransferActiveQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Ack Level Update", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "TimerActiveQueueProcessor" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 16 }, "id": 50, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(ack_level_update{cadence_service=\"cadence_history\", operation=\"TransferStandByQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Standby Ack Level Update", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 24 }, "id": 51, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(new_timer_notifications{operation=\"TransferActiveQueueProcessor\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Notifications", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 24 }, "id": 52, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "rate(new_timer_notifications{operation=\"TranferStandByQueueProcessor\"}[$__rate_interval])", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Standby New Timer Notifications", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 32 }, "id": 53, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(task_requests{operation=\"TransferActiveTaskActivityTimeout\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 32 }, "id": 54, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_latency_processing_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Processing p99 Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 40 }, "id": 55, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_attempt_bucket{operation=~\"TransferActiveTaskActivityTimeout|TransferActiveTaskDecisionTimeout|TransferActiveTaskWorkflowTimeout\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer p99 Attempt", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 40 }, "id": 56, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(task_latency_queue_bucket{operation=~\"TransferActiveTaskActivityTimeout|TransferActiveTaskDecisionTimeout|TransferActiveTaskWorkflowTimeout\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Active Timer Queue p99 Latency", "type": "timeseries" } ], "title": "Transfer Processor", "type": "row" } ], "preload": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "current": { "text": "samples_domain", "value": "samples_domain" }, "definition": "label_values(activity_end_to_end_latency_bucket,domain)", "includeAll": true, "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_bucket,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "cadence_history", "value": "cadence_history" }, "definition": "label_values(cadence_requests,cadence_service)", "label": "services", "name": "services", "options": [], "query": { "qryType": 1, "query": "label_values(cadence_requests,cadence_service)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "p99", "value": "p99" }, "label": "latency", "name": "latency", "options": [ { "selected": true, "text": "p99", "value": "p99" }, { "selected": false, "text": "upper", "value": "upper" }, { "selected": false, "text": "mean", "value": "mean" }, { "selected": false, "text": "median", "value": "median" }, { "selected": false, "text": "lower", "value": "lower" } ], "query": "p99,upper,mean,median,lower", "type": "custom" } ] }, "time": { "from": "now-5m", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Cadence-History", "uid": "cekm8fs6ps000h", "version": 1 } ================================================ FILE: docker/grafana/provisioning/dashboards/cadence-matching.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 3, "links": [], "panels": [ { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 6, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 1, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_errors_entity_not_exists{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "interval": "", "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Overall Requests Vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 2, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_errors_entity_not_exists{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Errors Breakdown", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 113 }, "id": 3, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 113 }, "id": 4, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.99, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"cadence_matching\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "API Latencies (p99)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 121 }, "id": 5, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(tasklist_managers{cadence_service=\"cadence_matching\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Task Managers Per Operation", "type": "timeseries" } ], "title": "Overall", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 1 }, "id": 15, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 2 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"PollForActivityTask\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForActivityTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 2 }, "id": 8, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"$services\", operation=\"PollForActivityTask\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForActivityTask Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 114 }, "id": 9, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_requests{operation=\"PollForDecisionTask\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "code", "expr": "sum by(operation) (rate(cadence_errors{operation=\"PollForDecisionTask\", cadence_service=\"$services\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B" } ], "title": "PollForDecisionTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 114 }, "id": 10, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"$services\", operation=\"PollForDecisionTask\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForDecisionTask Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 122 }, "id": 11, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"AddActivityTask\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddActivityTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 122 }, "id": 13, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"$services\", operation=\"AddActivityTask\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddActivityTask Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 130 }, "id": 12, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"AddDecisionTask\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddDecisionTask", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 130 }, "id": 14, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"$services\", operation=\"AddDecisionTask\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddDecisionTask Latency", "type": "timeseries" } ], "title": "API", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 2 }, "id": 23, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "sec", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 3 }, "id": 16, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (changes(poll_success_per_tl{operation=\"TaskListMgr\", domain=\"$domain\", \"$services\"=\"$services\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "{{operation}} (async)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (changes(poll_success_sync_per_tl{cadence_service=\"$services\", domain=\"$domain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (sync)", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(poll_timeouts_per_tl{operation=\"TaskListMgr\", cadence_service=\"$services\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (poll_timeouts)", "range": true, "refId": "C", "useBackend": false } ], "title": "TaskPoll", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 3 }, "id": 17, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by() (rate(cadence_PollForActivityTask_cadence_request{cadence_service=\"$services\", \"$domain\"=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Task Matcher Stats", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 115 }, "id": 19, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, avg by(le, operation) (rate(cadence_latency_per_tl_bucket{operation=\"AddActivityTask\", domain=\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddActivityTask Latency(p99) (forwarding requests excluded)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 115 }, "id": 18, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum(rate(cadence_PollForDecisionTask_cadence_request{cadence_service=\"$services\", \"$services\"=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Task Matcher Stat", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 123 }, "id": 20, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(cadence_latency_per_tl_bucket{operation=\"AddDecisionTask\", cadence_service=\"cadence_matching\", domain=\"$domain\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "AddDecisionTask Latency(p99) (forwarding requests excluded)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 123 }, "id": 22, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=\"PollForActivityTask\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "PollForActivityTask QPS (forwarding requests excluded)", "type": "timeseries" } ], "title": "TaskListManager", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 3 }, "id": 27, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }, "id": 24, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_requests{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Requests Vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }, "id": 25, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_bucket{cadence_service=\"cadence_matching\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Operation Latencies p99", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "GetTaskListSize" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 100 }, "id": 26, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Errors Per API", "type": "timeseries" } ], "title": "Persistence", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 4 }, "id": 31, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 5 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_requests{cadence_role=\"history_client\", cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per API", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 5 }, "id": 30, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.99, sum by(le, operation) (rate(cadence_client_latency_bucket{cadence_service=\"cadence_matching\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "API Latency (p99)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 69 }, "id": 29, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_client_errors_redirection{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Errors Per API", "type": "timeseries" } ], "title": "History Client", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 5 }, "id": 34, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 6 }, "id": 32, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (changes(task_count_per_tl{tasklistType=\"activity\", cadence_service=\"cadence_matching\", domain=\"$domain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Task Backlog", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 6 }, "id": 33, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (changes(task_backlog_per_tl{tasklistType=\"decision\", cadence_service=\"cadence_matching\", domain=\"$domain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Task Backlog", "type": "timeseries" } ], "title": "Backlog (per task list)", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 6 }, "id": 37, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 7 }, "id": 35, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(memory_num_gc{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "GC Num", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 7 }, "id": 36, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(memory_allocated{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Total allocation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 39 }, "id": 38, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(num_goroutines{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Number of goroutines", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "HeapInuse is bytes in in-use spans.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 39 }, "id": 39, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_heapinuse{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Heap in Use (HeapAlloc)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "HeapIdle is bytes in idle (unused) spans.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 47 }, "id": 40, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_heapidle{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Heap Available (HeapIdle)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "description": "Bytes of stack memory obtained from the OS. Plus any memory obtained directly from the OS for OS thread stacks.", "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 47 }, "id": 41, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(cadence_service) (rate(memory_stack{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Stack In use", "type": "timeseries" } ], "title": "Go GC detailed", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 7 }, "id": 48, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 }, "id": 42, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(tasklist) (rate(estimated_add_task_qps_per_tl{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"activity\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Adaptive Task List Scaler for Activity", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 }, "id": 43, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (rate(estimated_add_task_qps_per_tl{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"decision\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Adaptive Task List Scaler for Decision", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 16 }, "id": 44, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (rate(task_list_partition_config_num_read{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"activity\", operation=\"TaskListMgr\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Task List Read Partition Count", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 16 }, "id": 45, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (rate(task_list_partition_config_num_read{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"decision\", operation=\"TaskListMgr\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Task List Read Partition Count", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 24 }, "id": 46, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (rate(task_list_partition_config_num_write{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"activity\", operation=\"TaskListMgr\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activity Task List Write Partition Count", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 24 }, "id": 47, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(tasklist) (rate(task_list_partition_config_num_write{cadence_service=\"$services\", domain=\"$domain\", tasklistType=\"decision\", operation=\"TaskListMgr\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Task List Write Partition Count", "type": "timeseries" } ], "title": "Task List Partition Config & Adaptive Task List Scaler (WIP)", "type": "row" } ], "preload": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "current": { "text": "samples_domain", "value": "samples_domain" }, "definition": "label_values(activity_end_to_end_latency_bucket,domain)", "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_bucket,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "cadence_matching", "value": "cadence_matching" }, "definition": "label_values(cadence_requests,cadence_service)", "label": "services", "name": "services", "options": [], "query": { "qryType": 1, "query": "label_values(cadence_requests,cadence_service)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "current": { "text": "p99", "value": "p99" }, "label": "latency", "name": "latency", "options": [ { "selected": true, "text": "p99", "value": "p99" }, { "selected": false, "text": "upper", "value": "upper" }, { "selected": false, "text": "mean", "value": "mean" }, { "selected": false, "text": "median", "value": "median" }, { "selected": false, "text": "lower", "value": "lower" } ], "query": "p99,upper,mean,median,lower", "type": "custom" } ] }, "time": { "from": "now-1h", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Cadence Matching", "uid": "del7jwba0qgw0e", "version": 1 } ================================================ FILE: docker/grafana/provisioning/dashboards/cadence-persistence.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "target": { "limit": 100, "matchAny": false, "tags": [], "type": "dashboard" }, "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 2, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 12, "panels": [], "title": "Overall", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 0, "y": 1 }, "id": 17, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_response_row_size_count{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Response Row Count Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 12, "y": 1 }, "id": 18, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_response_payload_size_sum{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Response Payload Size Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 0, "y": 12 }, "id": 8, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (rate(persistence_requests_per_domain{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 12, "y": 12 }, "id": 2, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_requests{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests per operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 0, "y": 23 }, "id": 3, "options": { "legend": { "calcs": [ "mean", "max" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Max", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(persistence_latency_bucket{cadence_service=\"$services\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Latencies Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 12, "y": 23 }, "id": 10, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, operation) (rate(persistence_requests_per_domain{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Domain Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 9, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "fieldMinMax": false, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 0, "y": 34 }, "id": 1, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_empty_response{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Empty Response Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 12, "y": 34 }, "id": 9, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, shard_id) (rate(persistence_requests_per_shard{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} - {{shard_id}}(shard_id)", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Operation Per Shard", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" } ] } }, "overrides": [] }, "gridPos": { "h": 11, "w": 12, "x": 0, "y": 45 }, "id": 11, "options": { "legend": { "calcs": [ "mean", "sum" ], "displayMode": "table", "placement": "bottom", "showLegend": true, "sortBy": "Total", "sortDesc": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors_entity_not_exists{cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Errors Per Operation ", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 56 }, "id": 16, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 160 }, "id": 14, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "right", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "asc" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(le, operation) (rate(persistence_requests{operation=~\"CreateWorkflowExecution|UpdateWorkflowExecution|LeaseTaskList|UpdateTaskList|CreateShard|UpdateShard\", cadence_service=\"$services\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "LWT Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 160 }, "id": 15, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "right", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, max by(operation, le) (rate(persistence_latency_bucket{operation=~\"CreateWorkflowExecution|UpdateWorkflowExecution|LeaseTaskList|UpdateTaskList|CreateShard|UpdateShard\", cadence_service=\"$services\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "LWT Latency", "type": "timeseries" } ], "title": "LWT Operations", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 57 }, "id": 22, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 161 }, "id": 19, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, cadence_service) (rate(persistence_requests{operation=~\"ReadHistoryBranch|AppendHistoryNodes|GetHistoryTasks\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Requests Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 161 }, "id": 21, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, cadence_service) (rate(persistence_empty_response{operation=~\"ReadHistoryBranch|AppendHistoryNodes|GetHistoryTasks\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Empty Response Per Operation", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 193 }, "id": 20, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors_entity_not_exists{operation=~\"ReadHistoryBranch|AppendHistoryNodes|GetHistoryTasks\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Errors Per Operation", "type": "timeseries" } ], "title": "History Tasks", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 58 }, "id": 29, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 162 }, "id": 23, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_requests{operation=~\"CreateShard|GetShard|UpdateShard\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_empty_response{operation=~\"CreateShard|GetShard|UpdateShard\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Shard CRUD", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 162 }, "id": 24, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, operation) (rate(persistence_latency_bucket{operation=~\"CreateShard|GetShard|UpdateShard\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Shard CRUD Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 194 }, "id": 25, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_requests{operation=~\"CompleteTask|CreateTask|GetTaskListSize\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_empty_response{operation=~\"CompleteTask|CreateTask|GetTaskListSize\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Task CRUD", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 194 }, "id": 26, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, max by(le, operation) (rate(persistence_latency_bucket{operation=~\"CompleteTask|CreateTask|GetTaskListSize\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Task CRUD Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 202 }, "id": 27, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_requests{operation=~\"LeaseTaskList|UpdateTaskList|GetTaskListSize\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_empty_response{operation=~\"LeaseTaskList|UpdateTaskList|GetTaskListSize\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "TaskList CRUD", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 202 }, "id": 28, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "histogram_quantile(0.95, max by(le, operation) (rate(persistence_latency_bucket{operation=~\"LeaseTaskList|UpdateTaskList|GetTaskListSize\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "TaskList CRUD Latency", "type": "timeseries" } ], "title": "ShardMgr & TaskMgr", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 59 }, "id": 32, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 163 }, "id": 30, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_requests{operation=~\"CreateWorkflowExecution|UpdateWorkflowExecution|GetWorkflowExecution|DeleteWorkflowExecution\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "editorMode": "builder", "expr": "sum by(operation) (rate(persistence_errors{operation=~\"CreateWorkflowExecution|UpdateWorkflowExecution|GetWorkflowExecution|DeleteWorkflowExecution\"}[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B" } ], "title": "WorkflowExecution CRUD", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 163 }, "id": 31, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_bucket{operation=~\"CreateWorkflowExecution|UpdateWorkflowExecution|GetWorkflowExecution|DeleteWorkflowExecution\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "C", "useBackend": false } ], "title": "WorkflowExecution CRUD Latency", "type": "timeseries" } ], "title": "Execution Manager", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 60 }, "id": 35, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 164 }, "id": 33, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(persistence_requests{operation=~\"CreateDomain|DeleteDomain|UpdateDomain|DeleteDomainByName|GetDomain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(persistence_errors_entity_not_exists{operation=~\"CreateDomain|DeleteDomain|UpdateDomain|DeleteDomainByName|GetDomain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Domain CRUD", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "min": 4, "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 164 }, "id": 34, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_bucket{operation=~\"CreateDomain|DeleteDomain|UpdateDomain|DeleteDomainByName|GetDomain\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Domain CRUD Latency", "type": "timeseries" } ], "title": "MetadataManager", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 61 }, "id": 38, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 165 }, "id": 36, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_requests{operation=~\"RecordWorkflowExecutionStarted|RecordWorkflowExecutionClosed\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "{{operation}} (request)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(operation) (rate(persistence_errors_entity_not_exists{operation=~\"RecordWorkflowExecutionStarted|RecordWorkflowExecutionClosed\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (error)", "range": true, "refId": "B", "useBackend": false } ], "title": "Visibility Open/Close", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "dtdurationms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 165 }, "id": 37, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_bucket{operation=~\"RecordWorkflowExecutionStarted|RecordWorkflowExecutionClosed\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Visibility Open/Close Latency", "type": "timeseries" } ], "title": "VisibilityManager", "type": "row" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 62 }, "id": 45, "panels": [], "title": "Persistence Hot Shard Detection", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 63 }, "id": 44, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation, shard_id) (rate(persistence_requests_per_shard{operation=\"UpdateWorkflowExecution\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Total updateworkflowexecution tracked shardId based requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 63 }, "id": 39, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(task_requests_per_domain{cadence_service=\"$services\", domain=\"$domain\", operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer Task Request Per Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 71 }, "id": 40, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(task_requests_per_domain{domain=\"$domain\", cadence_service=\"$services\", operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Task Request Per Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 71 }, "id": 43, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, shard_id) (rate(persistence_latency_per_shard_bucket{operation=\"ShardIdPersistenceRequest\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "shardidpersistencerequest p99 latency shardId", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 79 }, "id": 41, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.0.1", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (rate(persistence_latency_per_domain_bucket{domain=~\"$domain\", cadence_service=~\"$services\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Persistence Latency Per Domain", "type": "timeseries" } ], "preload": false, "refresh": "", "schemaVersion": 41, "tags": [], "templating": { "list": [ { "allowCustomValue": false, "current": { "text": "All", "value": "$__all" }, "definition": "label_values(cadence_requests,cadence_service)", "includeAll": false, "label": "services", "name": "services", "options": [], "query": { "qryType": 1, "query": "label_values(cadence_requests,cadence_service)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" }, { "allowCustomValue": false, "current": { "text": "upper", "value": "upper" }, "label": "latency", "name": "latency", "options": [ { "selected": false, "text": "p99", "value": "p99" }, { "selected": true, "text": "upper", "value": "upper" }, { "selected": false, "text": "mean", "value": "mean" }, { "selected": false, "text": "median", "value": "median" }, { "selected": false, "text": "lower", "value": "lower" } ], "query": "p99,upper,mean,median,lower", "type": "custom" }, { "allowCustomValue": true, "current": { "text": "samples_domain", "value": "samples_domain" }, "definition": "label_values(activity_end_to_end_latency_sum,domain)", "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_sum,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" } ] }, "time": { "from": "now-6h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Cadence-Persistence", "uid": "b00f7dd5-3dd6-4cf9-b17b-425e071e2b5b", "version": 1 } ================================================ FILE: docker/grafana/provisioning/dashboards/cadence-server.json ================================================ { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "datasource", "uid": "grafana" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "target": { "limit": 100, "matchAny": false, "tags": [], "type": "dashboard" }, "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 7, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 44, "panels": [], "title": "Frontend", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 6, "w": 23, "x": 0, "y": 1 }, "id": 57, "options": { "displayMode": "gradient", "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": false }, "maxVizHeight": 300, "minVizHeight": 16, "minVizWidth": 8, "namePlacement": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ "max" ], "fields": "", "values": false }, "showUnfilled": true, "sizing": "auto", "text": {}, "valueMode": "color" }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (cadence_requests{cadence_service=\"cadence_frontend\", operation=~\"SignalWithStartWorkflowExecution|SignalWorkflowxecution|StartWorkflowExecution|TerminateWorkflowExecution|ResetWorkflowExecution|RequestCancelWorkflowexecution|ListWorkflowExecutions\"})", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "{{domain}}", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "", "interval": "", "legendFormat": "", "refId": "A" } ], "title": "Top Domains for Regular Requests", "type": "bargauge" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 6, "w": 23, "x": 0, "y": 7 }, "id": 58, "options": { "displayMode": "gradient", "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": false }, "maxVizHeight": 300, "minVizHeight": 16, "minVizWidth": 8, "namePlacement": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ "mean" ], "fields": "", "values": false }, "showUnfilled": true, "sizing": "auto", "text": {}, "valueMode": "color" }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (cadence_requests{cadence_service=\"cadence_frontend\", operation=~\"PollForDecisionTask|GetWorkflowExecutionHistory|RespondDecisionTaskCompleted\"})", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "{{domain}}{{operation}}", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "cadence_requests{cadence_service=\"cadence_worker\",namespace=\"$namespace\"}", "hide": true, "interval": "", "legendFormat": "", "refId": "A" } ], "title": "Top Domains for Workflow Worker Requests", "type": "bargauge" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 6, "w": 23, "x": 0, "y": 13 }, "id": 60, "options": { "displayMode": "gradient", "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": false }, "maxVizHeight": 300, "minVizHeight": 16, "minVizWidth": 8, "namePlacement": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ "mean" ], "fields": "", "values": false }, "showUnfilled": true, "sizing": "auto", "text": {}, "valueMode": "color" }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (cadence_requests{cadence_service=\"cadence_frontend\"})", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "{{domain}}{{operation}}", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "cadence_requests{cadence_service=\"cadence_worker\",namespace=\"$namespace\"}", "hide": true, "interval": "", "legendFormat": "", "refId": "A" } ], "title": "Top Domains for All Requests", "type": "bargauge" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 6, "w": 23, "x": 0, "y": 19 }, "id": 59, "options": { "displayMode": "gradient", "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": false }, "maxVizHeight": 300, "minVizHeight": 16, "minVizWidth": 8, "namePlacement": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ "mean" ], "fields": "", "values": false }, "showUnfilled": true, "sizing": "auto", "text": {}, "valueMode": "color" }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(cadence_requests{cadence_service=\"cadence_frontend\",namespace=\"$namespace\", operation=~\"PollForActivityTask|RespondActivityTaskCompleted\"}) by (domain)", "hide": false, "interval": "", "legendFormat": "{{domain}}{{operation}}", "refId": "B" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "cadence_requests{cadence_service=\"cadence_worker\",namespace=\"$namespace\"}", "hide": true, "interval": "", "legendFormat": "", "refId": "A" } ], "title": "Top Domains for Activity Worker Requests", "type": "bargauge" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 25 }, "id": 48, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (rate(cadence_requests{cadence_service=\"cadence_frontend\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "All API per second per domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 25 }, "id": 49, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "API per second(breakdown per operation)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 33 }, "id": 50, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(cadence_latency_bucket{operation!~\"CountWorkflowExecutions|GetWorkflowExecutionHistory|ListClosedWorkflowExecutions|ListOpenWorkflowExecutions|ListWorkflowExecutions|PollForActivityTask|PollForDecisionTask|QueryWorkflow\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "{{operation}}", "range": true, "refId": "B", "useBackend": false } ], "title": "Regular API Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 33 }, "id": 51, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(cadence_latency_bucket{operation=~\"CountWorkflowExecutions|GetWorkflowExecutionHistory|ListClosedWorkflowExecutions|ListOpenWorkflowExecutions|ListWorkflowExecutions|PollForActivityTask|PollForDecisionTask|QueryWorkflow\", cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "ListWorkflow API Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 41 }, "id": 52, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"PollForDecisionTask\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(cadence_latency_bucket{operation=\"PollForActivityTask\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": true, "interval": "", "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Long Poll API Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 41 }, "id": 56, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, operation) (cadence_requests{cadence_service=\"cadence_frontend\", operation!~\"CountWorkflowExecutions|GetWorkflowExecutionHistory|ListClosedWorkflowExecutions|ListOpenWorkflowExecutions|ListWorkflowExecutions|PollForActivityTask|PollForDecisionTask|QueryWorkflow\"})", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "Domain: {{domain}}- Operation: {{operation}}", "range": true, "refId": "B", "useBackend": false } ], "title": "Regular API Per Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 49 }, "id": 54, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_errors{cadence_service=\"cadence_frontend\", domain=\"$domain\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "interval": "", "legendFormat": "Internal server error", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_bad_request{cadence_service=\"cadence_frontend\"}[2m]))", "hide": false, "interval": "", "legendFormat": "Bad Requests error", "refId": "B" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_query_failed{cadence_service=\"cadence_frontend\"}[2m]))", "interval": "", "legendFormat": "QueryFailed error", "refId": "C" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_context_timeout{cadence_service=\"cadence_frontend\"}[2m]))", "interval": "", "legendFormat": "ContextTimeout error", "refId": "D" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_entity_not_exists{cadence_service=\"cadence_frontend\"}[2m]))", "interval": "", "legendFormat": "EntityNotExists error", "refId": "E" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_execution_already_started{cadence_service=\"cadence_frontend\"}[2m]))", "interval": "", "legendFormat": "WorkflowAlreadyStarted error", "refId": "F" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "expr": "sum(rate(cadence_errors_workflow_execution_already_completed{cadence_service=\"cadence_frontend\"}[2m]))", "interval": "", "legendFormat": "WorkflowAlreadyCompleted", "refId": "G" } ], "title": "API errors per second(breakdown per operation)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 49 }, "id": 53, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.9, sum by(le, operation) (rate(cadence_latency_bucket{cadence_service=\"cadence_frontend\", operation=~\"GetWorkflowExecutionHistory|QueryWorkflow\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "GetHistory/QueryWorkflow API Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 57 }, "id": 55, "options": { "alertThreshold": true, "legend": { "calcs": [ "mean", "lastNotNull", "max", "min", "sum" ], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "multi", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain, operation) (changes(cadence_requests{cadence_service=\"cadence_frontend\", operation=~\"SignalWithStartworkflowExecution|SignalWorkflowExecution|StartWorkflowExecution|TerminateWorkflowExecution|ResetWorkflowExecution|RequestCancelWorkflowExecution|ListWorkflowExecutions\", domain=\"$domain\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "interval": "", "legendFormat": "Domain: {{domain}}- Operation: {{operation}}", "range": true, "refId": "B", "useBackend": false } ], "title": "WorkflowClient API per seconds by domain", "type": "timeseries" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 65 }, "id": 96, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 641 }, "id": 93, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_service) (rate(restarts[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Service Restarts", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 641 }, "id": 95, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(acquire_shards_count[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "{{operation}} (shard acquire count)", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(sharditem_created_count[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "shard item created count", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (changes(numshards_gauge[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "num of shard gauge", "range": true, "refId": "C", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (changes(persistence_requests_per_shard[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "persistence requests per shard", "range": true, "refId": "D", "useBackend": false } ], "title": "Shard Movements per sec", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1537 }, "id": 94, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "avg by(cadence_service) (num_goroutines)", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Goroutines (avg)", "type": "timeseries" } ], "title": "Service Metrics", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 66 }, "id": 101, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1618 }, "id": 97, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(cadence_requests{cadence_service=\"cadence_history\", operation=~\"StartWorkflowExecution|SignalWorkflowExecution\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "External Events per sec", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1618 }, "id": 98, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=~\"RespondActivityTaskCompleted|RecordActivityTaskHeartbeat|RecordActivityTaskStarted|RespondDecisionTaskCompleted\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Activities per sec ", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1626 }, "id": 100, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(cadence_requests{operation=~\"PollForActivityTask|PollForDecisionTask|CancelOutstandingPoll\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (changes(poll_success_sync{cadence_service=\"cadence_matching\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "poll success sync", "range": true, "refId": "B", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(__name__) (rate(poll_timeouts{cadence_service=\"cadence_matching\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "poll timeout", "range": true, "refId": "C", "useBackend": false } ], "title": "Polls per sec", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1626 }, "id": 99, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(cadence_requests{operation=~\"RecordDecisionTaskStarted|RespondDecisionTaskCompleted\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decisions per sec", "type": "timeseries" } ], "title": "Rates per second", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 67 }, "id": 106, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, "x": 0, "y": 643 }, "id": 102, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_version) (build_information{cadence_service=\"cadence_frontend\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Versions", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, "x": 6, "y": 643 }, "id": 103, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_version) (build_information{cadence_service=\"cadence_history\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History Versions", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, "x": 12, "y": 643 }, "id": 105, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_version) (build_information{cadence_service=\"cadence_worker\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Worker Versions", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, "x": 18, "y": 643 }, "id": 104, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(cadence_version) (build_information{cadence_service=\"cadence_matching\"})", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Matching Versions", "type": "timeseries" } ], "title": "Server Version Metrics", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 68 }, "id": 111, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 644 }, "id": 107, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (changes(task_requests{cadence_service=\"cadence_history\", operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\"}[$__interval]))", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer Tasks Requests", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "TimerActiveTaskActivityTimeout" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 644 }, "id": 108, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, operation) (task_latency_bucket{operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\"}))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer Task Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "TimerActiveTaskActivityTimeout" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1108 }, "id": 109, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (changes(task_requests_per_domain{operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\"}[$__interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Timer Tasks Per Domain per sec", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] }, "unit": "ms" }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "TimerActiveTaskActivityTimeout" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1108 }, "id": 110, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, domain) (rate(task_latency_per_domain_bucket{operation=~\"TimerActiveTaskActivityTimeout|TimerActiveTaskDecisionTimeout|TimerActiveTaskWorkflowTimeout\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B", "useBackend": false } ], "title": "Timer Task Latency", "type": "timeseries" } ], "title": "Timer Task Processing", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 69 }, "id": 117, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 24, "x": 0, "y": 645 }, "id": 112, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "code", "expr": "sum by(__name__) (rate(task_requests{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "requests", "range": true, "refId": "A", "useBackend": false }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "code", "expr": "sum by(__name__) (rate(task_errors{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "instant": false, "legendFormat": "errors", "range": true, "refId": "B", "useBackend": false } ], "title": "Transfer Tasks Requests Vs Errors", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 797 }, "id": 116, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(operation) (rate(task_requests{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Tasks Requests Per Type", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 797 }, "id": 113, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.99, sum by(le, operation) (rate(task_latency_bucket{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Task Latency (upper)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 805 }, "id": 114, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "sum by(domain) (rate(task_requests_per_domain{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\", cadence_service=\"cadence_history\"}[$__rate_interval]))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Tasks Per Domain (Top 20)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 805 }, "id": 115, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.99, sum by(le, domain) (rate(task_latency_per_domain_bucket{operation=~\"TransferActiveTaskCloseExecution|TransferActiveTaskDecision|TransferActiveTaskRecordWorkflowStarted\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Transfer Task upper Latency per Domain (Top 20)", "type": "timeseries" } ], "title": "Transfer Task Processing", "type": "row" }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 70 }, "id": 125, "panels": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 641 }, "id": 118, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, domain) (rate(history_size_bucket{operation=\"ExecutionStats\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History Size By Domain (Top 20)", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 641 }, "id": 119, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, sum by(le, domain) (rate(history_count_bucket{operation=~\"ExecutionStats\", cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "History Event Count By Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1105 }, "id": 120, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, domain) (rate(event_blob_size_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Blob Sizes Top Domains", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1105 }, "id": 121, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, operation) (rate(event_blob_size_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "includeNullMetadata": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Frontend Blob Sizes Top Operations", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ScheduleActivityTask" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1113 }, "id": 123, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, domain) (rate(event_blob_size_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Blob Sizes by Domain", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ScheduleActivityTask" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1113 }, "id": 122, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, decisionType) (rate(event_blob_size_bucket{cadence_service=\"cadence_history\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Blob Sizes by DecisionType", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [ { "__systemRef": "hideSeriesFrom", "matcher": { "id": "byNames", "options": { "mode": "exclude", "names": [ "ScheduleActivityTask" ], "prefix": "All except:", "readOnly": true } }, "properties": [ { "id": "custom.hideFrom", "value": { "legend": false, "tooltip": false, "viz": true } } ] } ] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1121 }, "id": 124, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "PBFA97CFB590B2093" }, "disableTextWrap": false, "editorMode": "builder", "expr": "histogram_quantile(0.95, max by(le, domain) (rate(decision_result_count_bucket{cadence_service=\"cadence_frontend\"}[$__rate_interval])))", "fullMetaSearch": false, "hide": false, "includeNullMetadata": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A", "useBackend": false } ], "title": "Decision Result Count Top Domains", "type": "timeseries" } ], "title": "Size and Count Limits", "type": "row" } ], "preload": false, "refresh": false, "schemaVersion": 41, "tags": [], "templating": { "list": [ { "current": { "text": "all", "value": "all" }, "datasource": "Prometheus", "definition": "label_values(activity_end_to_end_latency_sum,domain)", "includeAll": false, "label": "Domain", "name": "domain", "options": [], "query": { "qryType": 1, "query": "label_values(activity_end_to_end_latency_sum,domain)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "type": "query" } ] }, "time": { "from": "now-5m", "to": "now" }, "timepicker": { "refresh_intervals": [ "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d" ] }, "timezone": "", "title": "Cadence-Server", "uid": "1B0efRyGz", "version": 1 } ================================================ FILE: docker/grafana/provisioning/dashboards/default.yaml ================================================ apiVersion: 1 providers: - name: Cadence-Basic # A uniquely identifiable name for the provider folder: Cadence type: file options: path: /etc/grafana/provisioning/dashboards/ ================================================ FILE: docker/grafana/provisioning/datasources/default.yaml ================================================ datasources: - name: Prometheus type: prometheus url: http://host.docker.internal:9090 version: 1 editable: true ================================================ FILE: docker/prometheus/prometheus.yml ================================================ global: scrape_interval: 5s external_labels: monitor: 'cadence-monitor' scrape_configs: - job_name: 'prometheus' static_configs: - targets: # addresses to scrape - 'cadence:9090' - 'cadence:8000' - 'cadence:8001' - 'cadence:8002' - 'cadence:8003' - 'host.docker.internal:8004' # Endpoint for Cadence samples running on localhost ================================================ FILE: docker/prometheus_multiclusters/prometheus.yml ================================================ global: scrape_interval: 5s external_labels: monitor: 'cadence-monitor' scrape_configs: - job_name: 'prometheus' static_configs: - targets: # addresses to scrape - 'cadence:9090' - 'cadence:8000' - 'cadence:8001' - 'cadence:8002' - 'cadence:8003' - 'cadence:9000' - 'cadence:9001' - 'cadence:9002' - 'cadence:9003' ================================================ FILE: docker/setup-multiclusters-schema.sh ================================================ #!/bin/bash set -ex # Default values CASSANDRA_SEEDS="${CASSANDRA_SEEDS:-cassandra}" CASSANDRA_USER="${CASSANDRA_USER:-cassandra}" CASSANDRA_PASSWORD="${CASSANDRA_PASSWORD:-cassandra}" CASSANDRA_PROTO_VERSION="${CASSANDRA_PROTO_VERSION:-4}" REPLICATION_FACTOR="${RF:-1}" # Keyspaces for multiclusters setup PRIMARY_KEYSPACE="${PRIMARY_KEYSPACE:-cadence_primary}" PRIMARY_VISIBILITY_KEYSPACE="${PRIMARY_VISIBILITY_KEYSPACE:-cadence_visibility_primary}" SECONDARY_KEYSPACE="${SECONDARY_KEYSPACE:-cadence_secondary}" SECONDARY_VISIBILITY_KEYSPACE="${SECONDARY_VISIBILITY_KEYSPACE:-cadence_visibility_secondary}" # Schema directories SCHEMA_DIR="/etc/cadence/schema/cassandra/cadence/versioned" VISIBILITY_SCHEMA_DIR="/etc/cadence/schema/cassandra/visibility/versioned" wait_for_cassandra() { echo "Waiting for Cassandra to be ready..." server=$(echo $CASSANDRA_SEEDS | awk -F ',' '{print $1}') # Wait for Cassandra to be fully ready with timeout echo "Waiting for Cassandra to be fully ready..." timeout=300 # 5 minutes timeout counter=0 while [[ $counter -lt $timeout ]]; do if cadence-cassandra-tool --ep $server --u $CASSANDRA_USER --pw $CASSANDRA_PASSWORD --pv $CASSANDRA_PROTO_VERSION create -k test_keyspace --rf 1 2>/dev/null; then echo 'cassandra started and ready' return 0 fi echo "waiting for cassandra to be fully ready (attempt $((counter/5 + 1)))" sleep 5 counter=$((counter + 5)) done echo "Error: Cassandra did not become ready within $timeout seconds" exit 1 } setup_keyspace_schema() { local keyspace=$1 local visibility_keyspace=$2 echo "Setting up schema for keyspace: $keyspace" # Create keyspace cadence-cassandra-tool --ep $CASSANDRA_SEEDS create -k $keyspace --rf $REPLICATION_FACTOR # Setup schema versioning cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $keyspace setup-schema -v 0.0 # Update to latest schema cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $keyspace update-schema -d $SCHEMA_DIR echo "Setting up visibility schema for keyspace: $visibility_keyspace" # Create visibility keyspace cadence-cassandra-tool --ep $CASSANDRA_SEEDS create -k $visibility_keyspace --rf $REPLICATION_FACTOR # Setup visibility schema versioning cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $visibility_keyspace setup-schema -v 0.0 # Update visibility to latest schema cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $visibility_keyspace update-schema -d $VISIBILITY_SCHEMA_DIR } setup_primary_cluster() { echo "Setting up primary cluster schema..." setup_keyspace_schema $PRIMARY_KEYSPACE $PRIMARY_VISIBILITY_KEYSPACE # Note: Domain registration will be handled by the Cadence services when they start echo "Primary cluster schema setup completed. Domain registration will be handled by Cadence services." } setup_secondary_cluster() { echo "Setting up secondary cluster schema..." setup_keyspace_schema $SECONDARY_KEYSPACE $SECONDARY_VISIBILITY_KEYSPACE # Note: Domain registration will be handled by the Cadence services when they start echo "Secondary cluster schema setup completed. Domain registration will be handled by Cadence services." } main() { echo "Starting multiclusters schema setup..." # Wait for Cassandra to be ready wait_for_cassandra # Setup both clusters setup_primary_cluster setup_secondary_cluster echo "Multiclusters schema setup completed successfully!" } # Run the main function main "$@" ================================================ FILE: docker/start-cadence.sh ================================================ #!/bin/bash set -ex CONFIG_TEMPLATE_PATH="${CONFIG_TEMPLATE_PATH:-/etc/cadence/config/config_template.yaml}" dockerize -template $CONFIG_TEMPLATE_PATH:/etc/cadence/config/docker.yaml exec cadence-server --root $CADENCE_HOME --env docker start --services=$SERVICES ================================================ FILE: docker/start.sh ================================================ #!/bin/bash set -x DB="${DB:-cassandra}" ENABLE_ES="${ENABLE_ES:-false}" ES_PORT="${ES_PORT:-9200}" ES_VERSION="${ES_VERSION:-v6}" RF=${RF:-1} # cassandra env export CASSANDRA_USER="${CASSANDRA_USER:-cassandra}" export CASSANDRA_PASSWORD="${CASSANDRA_PASSWORD:-cassandra}" export KEYSPACE="${KEYSPACE:-cadence}" export VISIBILITY_KEYSPACE="${VISIBILITY_KEYSPACE:-cadence_visibility}" export CASSANDRA_PROTO_VERSION="${CASSANDRA_PROTO_VERSION:-4}" # mysql env export DBNAME="${DBNAME:-cadence}" export VISIBILITY_DBNAME="${VISIBILITY_DBNAME:-cadence_visibility}" export DB_PORT=${DB_PORT:-3306} # elasticsearch env export VISIBILITY_NAME="${VISIBILITY_NAME:-cadence-visibility-dev}" setup_cassandra_schema() { SCHEMA_DIR=$CADENCE_HOME/schema/cassandra/cadence/versioned cadence-cassandra-tool --ep $CASSANDRA_SEEDS create -k $KEYSPACE --rf $RF cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $KEYSPACE setup-schema -v 0.0 cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $KEYSPACE update-schema -d $SCHEMA_DIR VISIBILITY_SCHEMA_DIR=$CADENCE_HOME/schema/cassandra/visibility/versioned cadence-cassandra-tool --ep $CASSANDRA_SEEDS create -k $VISIBILITY_KEYSPACE --rf $RF cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $VISIBILITY_KEYSPACE setup-schema -v 0.0 cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $VISIBILITY_KEYSPACE update-schema -d $VISIBILITY_SCHEMA_DIR echo "Registering domain for Cassandra..." cqlsh $CASSANDRA_SEEDS -k $KEYSPACE -f /etc/cadence/domain/cassandra.cql } setup_mysql_schema() { SCHEMA_DIR=$CADENCE_HOME/schema/mysql/v8/cadence/versioned if [ "$MYSQL_TX_ISOLATION_COMPAT" == "true" ]; then CONNECT_ATTR='--connect-attributes tx_isolation=READ-COMMITTED' fi cadence-sql-tool --ep $MYSQL_SEEDS -u $MYSQL_USER --pw $MYSQL_PWD $CONNECT_ATTR create --db $DBNAME cadence-sql-tool --ep $MYSQL_SEEDS -u $MYSQL_USER --pw $MYSQL_PWD $CONNECT_ATTR --db $DBNAME setup-schema -v 0.0 cadence-sql-tool --ep $MYSQL_SEEDS -u $MYSQL_USER --pw $MYSQL_PWD $CONNECT_ATTR --db $DBNAME update-schema -d $SCHEMA_DIR VISIBILITY_SCHEMA_DIR=$CADENCE_HOME/schema/mysql/v8/visibility/versioned cadence-sql-tool --ep $MYSQL_SEEDS -u $MYSQL_USER --pw $MYSQL_PWD $CONNECT_ATTR create --db $VISIBILITY_DBNAME cadence-sql-tool --ep $MYSQL_SEEDS -u $MYSQL_USER --pw $MYSQL_PWD --db $VISIBILITY_DBNAME $CONNECT_ATTR setup-schema -v 0.0 cadence-sql-tool --ep $MYSQL_SEEDS -u $MYSQL_USER --pw $MYSQL_PWD --db $VISIBILITY_DBNAME $CONNECT_ATTR update-schema -d $VISIBILITY_SCHEMA_DIR echo "Registering domain for MySQL..." mysql -h $MYSQL_HOST -u $MYSQL_USER -p $MYSQL_PASSWORD $MYSQL_DATABASE < /etc/cadence/domain/mysql.sql } setup_postgres_schema() { SCHEMA_DIR=$CADENCE_HOME/schema/postgres/cadence/versioned cadence-sql-tool --plugin postgres --ep $POSTGRES_SEEDS -u $POSTGRES_USER --pw "$POSTGRES_PWD" -p $DB_PORT create --db $DBNAME cadence-sql-tool --plugin postgres --ep $POSTGRES_SEEDS -u $POSTGRES_USER --pw "$POSTGRES_PWD" -p $DB_PORT --db $DBNAME setup-schema -v 0.0 cadence-sql-tool --plugin postgres --ep $POSTGRES_SEEDS -u $POSTGRES_USER --pw "$POSTGRES_PWD" -p $DB_PORT --db $DBNAME update-schema -d $SCHEMA_DIR VISIBILITY_SCHEMA_DIR=$CADENCE_HOME/schema/postgres/visibility/versioned cadence-sql-tool --plugin postgres --ep $POSTGRES_SEEDS -u $POSTGRES_USER --pw "$POSTGRES_PWD" -p $DB_PORT create --db $VISIBILITY_DBNAME cadence-sql-tool --plugin postgres --ep $POSTGRES_SEEDS -u $POSTGRES_USER --pw "$POSTGRES_PWD" -p $DB_PORT --db $VISIBILITY_DBNAME setup-schema -v 0.0 cadence-sql-tool --plugin postgres --ep $POSTGRES_SEEDS -u $POSTGRES_USER --pw "$POSTGRES_PWD" -p $DB_PORT --db $VISIBILITY_DBNAME update-schema -d $VISIBILITY_SCHEMA_DIR echo "Registering domain for PostgreSQL..." PGPASSWORD=$POSTGRES_PASSWORD psql -h $POSTGRES_HOST -U $POSTGRES_USER -d $POSTGRES_DATABASE -f /etc/cadence/domain/postgres.sql } setup_es_template() { SCHEMA_FILE=$CADENCE_HOME/schema/elasticsearch/$ES_VERSION/visibility/index_template.json server=`echo $ES_SEEDS | awk -F ',' '{print $1}'` URL="http://$server:$ES_PORT/_template/cadence-visibility-template" curl -X PUT $URL -H 'Content-Type: application/json' --data-binary "@$SCHEMA_FILE" URL="http://$server:$ES_PORT/$VISIBILITY_NAME" curl -X PUT $URL } setup_schema() { if [ "$DB" == "mysql" ]; then echo 'setup mysql schema' setup_mysql_schema elif [ "$DB" == "postgres" ]; then echo 'setup postgres schema' setup_postgres_schema else echo 'setup cassandra schema' setup_cassandra_schema fi if [ "$ENABLE_ES" == "true" ]; then setup_es_template fi } wait_for_cassandra() { server=`echo $CASSANDRA_SEEDS | awk -F ',' '{print $1}'` until cqlsh -u $CASSANDRA_USER -p $CASSANDRA_PASSWORD --cqlversion=3.4.6 --protocol-version=$CASSANDRA_PROTO_VERSION $server < /dev/null; do echo 'waiting for cassandra to start up' sleep 1 done echo 'cassandra started' } wait_for_scylla() { server=`echo $CASSANDRA_SEEDS | awk -F ',' '{print $1}'` until cqlsh -u $CASSANDRA_USER -p $CASSANDRA_PASSWORD --cqlversion=3.3.1 --protocol-version=$CASSANDRA_PROTO_VERSION $server < /dev/null; do echo 'waiting for scylla to start up' sleep 1 done echo 'scylla started' } wait_for_mysql() { server=`echo $MYSQL_SEEDS | awk -F ',' '{print $1}'` nc -z $server $DB_PORT < /dev/null until [ $? -eq 0 ]; do echo 'waiting for mysql to start up' sleep 1 nc -z $server $DB_PORT < /dev/null done echo 'mysql started' } wait_for_postgres() { server=`echo $POSTGRES_SEEDS | awk -F ',' '{print $1}'` nc -z $server $DB_PORT < /dev/null until [ $? -eq 0 ]; do echo 'waiting for postgres to start up' sleep 1 nc -z $server $DB_PORT < /dev/null done echo 'postgres started' } wait_for_es() { server=`echo $ES_SEEDS | awk -F ',' '{print $1}'` URL="http://$server:$ES_PORT" curl -s $URL > /dev/null 2>&1 until [ $? -eq 0 ]; do echo 'waiting for elasticsearch to start up' sleep 1 curl -s $URL > /dev/null 2>&1 done echo 'elasticsearch started' } wait_for_db() { if [ "$DB" == "mysql" ]; then wait_for_mysql elif [ "$DB" == "postgres" ]; then wait_for_postgres elif [ "$DB" == "scylla" ]; then wait_for_scylla else wait_for_cassandra fi if [ "$ENABLE_ES" == "true" ]; then wait_for_es fi } wait_for_db if [ "$SKIP_SCHEMA_SETUP" != true ]; then setup_schema fi exec /start-cadence.sh ================================================ FILE: docs/cassandra-executions-table.md ================================================ # Overview The `executions` table in Cassandra is a table used for storing mutiple types of entities, because Cassandra doesn't support cross-table LWTs. The `type` column in `executions` table indicates the type of entity stored in that row. Since the table stores all the entities, the columns are the union set of columns of all entities. And the data in each column have different meaning for different type of entities. We'll explain for each type of entity the usage of the columns in `executions` table. ## Shard The `shard` entities are the rows with `type=0`, which stores the information of Cadence shards. The following columns are used: * `shard_id`: the `id` of the Cadence shard * `type`: indicates the type of entity, which is always `0` for `shard` * `shard`: stores the information of `shard` * `range_id`: used to manage changes to the shard's configuration and ownership Other columns are set to a constant only because they're part of primary key columns: * `domain_id`: 10000000-1000-f000-f000-000000000000 * `workflow_id`: 20000000-1000-f000-f000-000000000000 * `run_id`: 30000000-1000-f000-f000-000000000000 * `visibility_ts`: 2000-01-01 00:00:00.000000+0000 * `task_id`: -11 ## Execution The `execution` entities are the rows with `type=1`, which stores the information of workflow. And there are 2 types of executions: `current_execution` and `concrete_execution`. ### Current Execution The `current_execution` entities has a constant value in the `run_id` column which equals `30000000-0000-f000-f000-000000000001`. The follwing columns are set: * `shard_id`: the `id` of the Cadence shard * `type`: indicates the type of the entity * `domain_id`: the `id` of the domain of the workflow * `workflow_id`: the `id` of the workflow * `current_run_id`: the `run_id` of the most recent workflow among the workflows with the same `workflow_id` * `execution`: stores some information about the workflow * `workflow_last_write_version`: the failover version of the workflow last updated by a Cadence cluster * `workflow_state`: the state of the workflow The following columns are set to a constant value: * `run_id`: 30000000-0000-f000-f000-000000000001 * `visibility_ts`: 2000-01-01 00:00:00.000000+0000 * `task_id`: -10 ### Concrete Execution The `concrete_execution` entities stores the information of each run of a workflow. These columns are set: * `shard_id`: the `id` of the Cadence shard * `type`: indicates the type of the entity * `domain_id`: the `id` of the domain of the workflow * `workflow_id`: the `id` of the workflow * `run_id`: the `id` of the instance of the workflow * `execution`: stores some information about the workflow * `next_event_id`: the `id` of the next event of the workflow * `activity_map`: the information of activities of the workflow * `timer_map`: the information of timers of the workflow * `child_executions_map`: the information of child workflows of the workflow * `request_cancel_map`: the information of cancellation requests of the workflow * `signal_map`: the information of signals applied to the workflow * `signal_requested`: the ids of the signals applied to the workflow * `buffered_events_list`: the buffered events of the workflow * `workflow_last_write_version`: the failover version of the workflow last updated by a Cadence cluster * `workflow_state`: the state of the workflow * `version_histories`: stores the information tracking the version history of the workflow * `version_histories_encoding`: the encoding type of `version_histories` column * `checksum`: a piece of data used for detecting data corruption in database The following columns are set to a constant value: * `visibility_ts`: 2000-01-01 00:00:00.000000+0000 * `task_id`: -10 ## Transfer Task The `transfer_task` entities are the rows with `type=2`. The following columns are set: * `shard_id`: the `id` of the Cadence shard * `type`: indicates the type of the entity * `task_id`: the `id` of the task * `transfer`: the information of the task The following columns are set to a constant value: * `domain_id`: 10000000-3000-f000-f000-000000000000 * `workflow_id`: 20000000-3000-f000-f000-000000000000 * `run_id`: 30000000-3000-f000-f000-000000000000 * `visibility_ts`: 2000-01-01 00:00:00.000000+0000 ## Timer Task The `timer_task` entities are the rows with `type=3`. The following columns are set: * `shard_id`: the `id` of the Cadence shard * `type`: indicates the type of the entity * `visibility_ts`: the timestamp of the timer task * `task_id`: the `id` of the task * `timer`: the information of the task The following columns are set to a constant value: * `domain_id`: 10000000-4000-f000-f000-000000000000 * `workflow_id`: 20000000-4000-f000-f000-000000000000 * `run_id`: 30000000-4000-f000-f000-000000000000 ## Replication Task The `replication_task` entities are the rows with `type=4`. The following columns are set: * `shard_id`: the `id` of the Cadence shard * `type`: indicates the type of the entity * `task_id`: the `id` of the task * `replication`: the information of the task The following columns are set to a constant value: * `domain_id`: 10000000-5000-f000-f000-000000000000 * `workflow_id`: 20000000-5000-f000-f000-000000000000 * `run_id`: 30000000-5000-f000-f000-000000000000 * `visibility_ts`: 2000-01-01 00:00:00.000000+0000 ## Replication DLQ Task The `dlq` entities are the rows with `type=5`. The following columns are set: * `shard_id`: the `id` of the Cadence shard * `type`: indicates the type of the entity * `workflow_id`: the source cluster of the replication task * `task_id`: the `id` of the task * `replication`: the information of the task The following columns are set to a constant value: * `domain_id`: 10000000-6000-f000-f000-000000000000 * `run_id`: 30000000-6000-f000-f000-000000000000 * `visibility_ts`: 2000-01-01 00:00:00.000000+0000 ## Cross Cluster Task The `cross_cluster_task` entities are the rows with `type=6`. The following columns are set: * `shard_id`: the `id` of the Cadence shard * `type`: indicates the type of the entity * `workflow_id`: the target cluster which the task is applied to * `task_id`: the `id` of the task * `cross_cluster`: the information of the task The following columns are set to a constant value: * `domain_id`: 10000000-7000-f000-f000-000000000000 * `run_id`: 30000000-7000-f000-f000-000000000000 * `visibility_ts`: 2000-01-01 00:00:00.000000+0000 ## Workflow Requests The `workflow_requests` entities are rows with `type` in `{7, 8, 9, 10}`. The following columns are set: * `shard_id`: the `id` of the Cadence shard * `type`: indicates the type of the entity (7 = StartWorkflowRequest, 8 = SignalWorkflowRequest, 9 = CancelWorkflowRequest, 10 = ResetWorkflowRequest) * `domain_id`: the `id` of the domain * `workflow_id`: the `id` of the workflow which the request is applied to * `run_id`: the `id` of the request * `task_id`: the failover version of the domain when the request is applied * `current_run_id`: the `run_id` of the workflow which the request is applied to The following columns are set to a constant value: * `visibility_ts`: 2000-01-01 00:00:00.000000+0000 ================================================ FILE: docs/design/1533-host-specific-tasklist.md ================================================ # Proposal: Resource-Specific Tasklist Author: Yichao Yang (@yycptt) Last updated: July 2019 Discussion at ## Abstract Currently, there’s no straightforward way for a Cadence user to run multiple activities (a session) on a single activity worker. Although there is the option to explicitly specify a host-specific tasklist name or manually execute special activities to get information about the activity worker, these approaches have limitations and are error prone. Besides running multiple activities on a single worker, a user may also want to limit the total number of sessions running concurrently on a single worker if those sessions consume worker resources. This document proposes several new APIs and their implementations so that a user can create sessions, execute activities, and limit the number of concurrent sessions through a simple API call. ## Use Cases ### Machine Learning Model Training A user can model the entire training process with multiple activities, for example, downloading datasets, data cleaning, model training, and parameter uploading. In this case, all activities should be executed by one activity worker. Otherwise, the dataset might be downloaded multiple times. In addition, as a dataset typically consumes a lot of disk space and model training requires GPU support, the number of total models training on a single worker should be limited. ### Service Deployment In this case, a session represents the deployment of a service to one worker and the number of concurrent deployments is critical. A user controls that number by limiting the number of concurrently running sessions on an activity worker. ## Proposal The scope of this proposal is limited to the Cadence client. Cadence server doesn’t need to be changed at all. The basic idea here is that we should write special workflow code for users to perform activity scheduling and worker monitoring, and expose simple APIs to users. Once users don’t need to care about those implementation details, they can focus on their own business logic. The proposal includes both API changes and implementations. It aims at solving three problems: 1. How to ensure multiple activities are executed on one activity worker. 2. How to limit the number of concurrent sessions on an activity worker. 3. How to carry over session information between different workflow runs. The following sections will first go through the APIs exposed to users, explain how to use them, and provide sample code. Any developer with previous Cadence experience should be able to understand the APIs and start using them immediately. After that I will explain the model behind those APIs and how they are implemented. ### API Changes #### New Go Client API There will be four new APIs available when writing a workflow: ```go sessionCtx, err := workflow.CreateSession(ctx Context, so *SessionOptions) ``` A user calls this API to create a session on the worker that polls the task list specified in the ActivityOptions (or in the StartWorkflowOptions if the task list name is not specified in the ActivityOptions). All activities executed within the returned sessionCtx (a new context which contains metadata information of the created session) are considered to be part of the session and will be executed on the same worker. The sessionCtx will be cancelled if the worker executing this session dies or CompleteSession() is called. The SessionOptions struct contains two fields: `ExecutionTimeout`, which specifies the maximum amount of time the session can run and `CreationTimeout`, which specifies how long session creation can take before returning an error. CreateSession() will return an error if the context passed in already contains an open session. If all the workers are currently busy and unable to handle new sessions, the framework will keep retrying until the CreationTimeout you specified in the SessionOptions has passed before returning an error. When executing an activity within a session, a user might get three types of errors: 1. Those returned from user activities. The session will not be marked as failed in this case, so the user can return whatever error they want and apply their business logic as necessary. If a user wants to end a session due to the error returned from the activity, use the CompleteSession() API below. 2. A special `ErrSessionFailed` error: this error means the session has failed due to worker failure and the session is marked as failed in the background. In this case, no activities can be executed using this context. The user can choose how to handle the failure. They can create a new session to retry or end the workflow with an error. 3. Cancelled error: If a session activity has been scheduled before worker failure is detected, it will be cancelled afterwards and a cancelled error will be returned. ```go workflow.CompleteSession(sessionCtx Context) ``` This API is used to complete a session. It releases the resources reserved on the worker and cancels the session context (therefore all the activities using that session context). It does nothing when the session is already closed or failed. ```go sessionInfoPtr := workflow.GetSessionInfo(sessionCtx Context) ``` This API returns session metadata stored in the context. If the context passed in doesn’t contain any session metadata, this API will return a nil pointer. For now, the only exported fields in sessionInfo are: sessionID, which is a unique identifier for a session, and hostname. ```go sessionCtx, err := workflow.RecreateSession(ctx Context, recreateToken []byte, so *SessionOptions) ``` For long running sessions, user might want to split it into multiple runs while still wanting all the activities executed by the same worker. This API is designed for such a use case. Its usage is the same as CreateSession() except that it also takes in a recreateToken. The token encodes the resource specific tasklist name on which the new session should be created. A user can get the token by calling the GetRecreateToken() method of the SessionInfo object. #### Example The basic usage looks like the following (it belongs to a larger workflow): ```go sessionCtx, err := CreateSession(ctx) if err != nil { // Creation failed. Wrong ctx or too many outstanding sessions. } defer CompleteSession(sessionCtx) err = ExecuteActivity(sessionCtx, DownloadFileActivity, filename).Get(sessionCtx, nil) if err != nil { // Session(worker) has failed or activity itself returns an error. // User can perform normal error handling here and decide whether creating a new session is needed. // If they decide to create a new session, they need to call CompleteSession() so that worker resources can be released. We recommend that users create a function for a session and call defer CompleteSession(sessionCtx) after a session is created. If session retry is needed, this function can be called multiple times if the error returned is retriable. } err = ExecuteActivity(sessionCtx, ProcessFileActivity, filename).Get(sessionCtx, nil) if err != nil { // Session(worker) has failed or activity itself returns an error. } err = ExecuteActivity(sessionCtx, UploadFileActivity, filename).Get(sessionCtx, nil) if err != nil { // Session(worker) has failed or activity itself returns an error. } ``` #### New Worker Options There will be three new options available when creating a worker: * **EnableSessionWorker** This flag controls whether the worker will accept activities that belong to a session. * **SessionResourceID** An identifier of the resource that will be consumed if a session is executed on the worker. For example, if a worker has a GPU, and a session for training a machine learning model is executed on the worker, then some memory on the GPU will be consumed. In this case, the resourceID can be the identifier of the GPU. **NOTE:** Users must ensure that only one worker uses a certain resourceID on a host. * **MaxCurrentSessionExecutionSize** Because a session may consume some kind of resource, a user can use this option to control the maximum number of sessions running in the worker process at the same time. ## Implementation ### Session Model All sessions are **resource specific**. This means that a session is always tied to some kind of resource (not the worker). The resource can be CPU, GPU, memory, file descriptors, etc., and we want all the activities within a session to be executed by the same worker that owns the resource. If a worker dies and restarts, as long as it owns the same resource, we can try to reestablish the session (this is future work). Also, the user needs to ensure that the resource is owned by only one worker on a single host. Note that when creating a session, a user doesn’t need to specify the resource that is consumed by the session. This is because right now one worker owns only one resource, so there’s only one resource type on the worker. As a result, as long as the user specifies the correct tasklist in the context passed into the createSession API, the correct type of resource will be consumed (since there’s only one). Later, when a worker can support multiple types of resources, users will need to specify the resource type when creating a session. ### Workflow Context When a user calls the CreateSession() or RecreateSession() API, a structure that contains information about the created session will be stored in the returned context. The structure contains seven pieces of information: 1. **SessionID**: a unique ID for the created session. (Exported) 2. **Hostname**: the hostname of the worker that is responsible for executing the session. (Exported) 3. **ResourceID**: the resource consumed by the session. (Will Be Exported) 4. **tasklist**: the resource specific tasklist used by this session. (Not Exported) 5. **sessionState**: the state of the session (Not Exported). It can take one of the three values below: 1. **Open**: when the session is created and running 2. **Failed**: when the worker is down and the session can’t continue 3. **Closed**: when the user closes the session through the API call and the session is successfully closed 6. **sessionCancelFunc**: the function used to cancel the sessionCtx. It will cancel both the creation activity (see below) and all user activities. (Not Exported) 7. **completionCtx**: the context for executing the completion activity. It’s different from the sessionCtx as we want to cancel the session context when CompleteSession() is called, but the completion activity (see below) still needs to be executed. (Not Exported) When a user calls the CompleteSession() API, the state of the session will be changed to "Closed" in place, which is why this API doesn’t return a new context variable. There is no way to set the state of a session to “Failed”. The state of a session is “Failed” only when the worker executing the session is down and the change from “Open” to “Failed” is done in the background. Also notice that it’s possible that the worker is down, but the session state is still “Open” (since it takes some time to detect the worker failure). In this case, when a user executes an activity, the activity will still be scheduled on the resource specific tasklist. However, it will be cancelled after worker failure is detected and the user will get a cancelled error. ### Workflow Worker When scheduling an activity, the workflow worker needs to check the context to see if this activity belongs to a session. If so and the state of that session is "Open", get the resource specific tasklist from the context and use it to override the tasklist value specified in the activity option. If on the other hand, the state is "Failed", the ExecuteActivity call will immediately fail without scheduling the activity and return an ErrSessionFailed error through Future.Get(). CreateSession() and CompleteSession() really do are **schedule special activities** and get some information from the worker which executes these activities. * For CreateSession(), a special **session creation activity** will be scheduled on a global tasklist which is only used for this type of activity. During the execution of that activity, a signal containing the resource specific tasklist name will be sent back to the workflow (with other information like hostname). Once the signal is received by the worker, the creation is considered successful and the tasklist name will be stored in the session context. The creation activity also performs periodic heartbeat throughout the whole lifetime of the session. As a result, if the activity worker is down, the workflow can be notified and set the session state to “Failed”. * For RecreateSession(), the same thing happens. The only difference is that the session creation activity will be **scheduled on the resource specific task list instead of a global one**. * For CompleteSession(), a special **completion activity** will be scheduled on the resource specific tasklist. The purpose of this activity is to stop the corresponding creation activity created by the CreateSession()/CreateSessionWithHostID() API call, so that the resource used by the session can be released. Note that, **the completion activity must be scheduled on the resource specific tasklist (not a global one)** since we need to make sure the completion activity and the creation activity it needs to stop are running by the same worker. ### Activity Worker When `EnableSessionWorker` is set to true, two more activity workers will be started. * This worker polls from a global tasklist whose name is derived from the tasklist name specified in the workerOption and executes only the special session creation activity. This special activity serves several purposes: 1. **Send the resource specific tasklist name** back to the workflow through signal. 2. Keep **heart beating** to Cadence server until the end of the session. If the heart beating returns the `EntityNotExist` Error, this means the workflow/session has gone and this special activity should stop to release its resources. If on the other hand, the worker is down, Cadence server will send a heartbeat timeout error to the workflow, so the workflow can be notified and update the session state. Later, an ExecuteActivity() call to the failed session context will also fail. 3. Check the number of currently running sessions on the worker. If the maximum number of concurrent sessions has been reached, return an error to fail the creation. One detail we need to ensure is that when the maximum number of concurrent sessions has been reached, this worker should stop polling from the global task list. * Session Activity Worker: this worker only polls from the resource specific tasklist. There are three kinds of activities that can be run by this worker: 1. User activities in a session that belongs to this host. 2. Special activity to complete a session. The sole purpose of this activity is to stop the long running session creation activity so that new sessions can be executed. 3. Special session creation activity scheduled by RecreateSession(). When domain failover happens, since no worker will poll from the resource specific task, all activities within a session will timeout and the user will get an error. Also, Cadence server will detect the heartbeat timeout from the session creation activity. ### Sequence Diagrams This section illustrates the sequence diagrams for situations that may occur during the session execution. * Here is the sequence diagram for a normal session execution. ![image alt text](1533-host-specific-tasklist-image_0.png) * When activity worker fails during the execution. ![image alt text](1533-host-specific-tasklist-image_1.png) * When the workflow failed or terminated during the execution. ![image alt text](1533-host-specific-tasklist-image_2.png) * When there are too many outstanding sessions and the user is trying to create a new one. The assumption here is that each activity worker is configured to have **MaxCurrentSessionExecutionSize = 1**. Resource specific tasklist and session activity worker are omitted in the diagram. ![image alt text](1533-host-specific-tasklist-image_3.png) * An example of RecreateSession(). ![image alt text](1533-host-specific-tasklist-image_4.png) ## Open Issues * Support automatic session reestablishing when worker goes down. * Allow session activities to access session information such as sessionID, resourceID, etc. This information is useful for logging and debugging. * Support multiple resources per worker and allow user to specify which type of resource a session will consume when creating the session. ================================================ FILE: docs/design/2215-synchronous-request-reply.md ================================================ # Proposal: Synchronous Request Reply Authors: Maxim Fateev (@mfateev), Andrew Dawson (@andrewjdawson2016) Last updated: July 16, 2019 Discussion at [https://github.com/cadence-workflow/cadence/issues/2215](https://github.com/cadence-workflow/cadence/issues/2215) ## Abstract Currently Cadence workflows only process events (in form of signals) asynchronously. The only synchronous operations supported by workflows are query and waiting for workflow completion. Query is a read only operation that cannot mutate a workflow state. And waiting for a workflow completion is applicable to a pretty narrow set of use cases. There is a clear need for workflows processing mutating requests synchronously. This document goes over various workflow related request/reply use cases, identifies requirements and proposes solutions. ## Use Cases ### Synchronous Update For example customer service needs to notify a workflow that the task was matched to a specific customer service representative. The notification should fail if the task is not available for dispatch anymore. This can happen if the call is duplicated by the matching system or the task state has changed for other reasons. ### Page Flow For example placing an order through a web UI or mobile app involves going through multiple screens. The data submitted from a screen is sent to a workflow which might execute activities and return information about the next page to present. In most cases the latency of this interaction is expected to be low. ### Conditional Update Various scenarios would benefit from transactional guarantees. In its simplest form an update should not go through if a state of a workflow has changed since the last query. For example the page flow should reject an update from a cached browser page if a current state of the workflow expects a different one. While it is possible to implement this check in an application code a framework support for such guarantee would simplify the programming model. ### Queue Consumer Queue Consumer receives a task from Kafka, constructs a workflow id from it and forwards it to the appropriate instance. If workflow cannot process message because it is corrupted it can reject it with an error which is eventually going to put it into the DLQ. Ideally if message is rejected it is not recorded in the history to not increase the history size due to retries. It also would allow workflow efficiently filter out unnecessary messages. ### Waiting for a State Change For example continuous deployment show the current workflow state in the UI and update it once the workflow state changes. Currently they keep executing query periodically to learn about the state changes. The ideal solution would allow waiting for state changes using a long poll that is much more efficient. It also should scale to the large number of pollers watching a single workflow execution which is common in continuous deployment scenarios. ## Proposal The proposal consists of three parts. 1. Guarantee read after write consistency between signal and query 2. Long poll on query result changes 3. Update operation that should complete within a single decision All three together cover all the listed use cases. ### Guarantee read after write consistency between signal and query Currently, there is a race condition when a query emitted after a signal sees a stale workflow state without the update from the signal. Accounting for this inconsistency complicates the workflow logic. Fixing this race condition allows the signal-then-query pattern to be used for request-reply scenarios. The drawback of this approach is suboptimal performance since it requires at least two decisions and multiple database updates. But, together with the long polling on query result changes feature, it is a reasonable way to implement update-and-wait for long-running update operations. The reverse consistency of failing a signal if the workflow state has changed since the last query is also possible when requested. The basic idea is to return a current workflow (or query result) version with a query in the form of an opaque token. Then include the token into the SignalWorkflowExecution call arguments. The call is rejected if the version doesn’t match the one in the token. #### Implementation Details Here is the sequence diagram of the current QueryWorkflow API implementation: ![image alt text](2215-synchronous-request-reply_0.png) The race condition happens because a query always creates its own special decision task even if there is an outstanding decision task caused by an external event. To not return state that workflow hasn’t reached yet the query ignores all the new events after the already processed decision task. This approach has another issue of failing queries for newly created workflows that haven’t yet seen a decision task. The solution is to not create a separate decision task for a query, but include the query into an already outstanding one. It would be done by modifying the history service startDecisionTask method to return all the outstanding queries. And the client side should be modified to execute a decision as usual and then execute all the queries and insert both decisions and the query results into RespondDecisionTaskCompleted. The main drawback of this approach is that a query cannot be processed while there is a started decision task. Usually decision task processing is pretty fast, but if it executes local activities the latency of a query can increase dramatically. The current implementation already has this problem when a sticky workflow execution (which is on by default) is used. But currently the delay happens on the client worker which serializes processing of decision tasks by the same workflow instance. The naive implementation would add DecisionTaskScheduled/Started/Completed events to the history on every query. This is not acceptable as queries are read only operations and incurring the cost of all these updates and correspondent history growth would defeat its purpose. So the proposal is to skip any database updates if there is no outstanding decision task and rely on the in-memory cache of mutable state for such decision task handling. In case of a shard movement the state is going to be lost and the query request is going to fail. The reply to a decision task for such forgotten query can be safely ignored. If an external event like activity completion or signal is received when there is an outstanding decision task caused by a query then the event and the correspondent events including DecisionTaskScheduled and later DecisionTaskStarted and DecisionTaskCompleted are added to the history without adding the duplicated task to the task list. The probability of a query failure in case of shard movement can be reduced by a frontend retrying it to a host that assumed the shard ownership. Here is the sequence diagram for the proposed consistent QueryWorkflowExecution API: ![image alt text](2215-synchronous-request-reply_1.png) The next diagram covers situation when query is received when another decision task is already started: ![image alt text](2215-synchronous-request-reply_2.png) The current decision state diagram ![image alt text](2215-synchronous-request-reply_3.png) Diagram with query task added ![image alt text](2215-synchronous-request-reply_4.png) ### Long poll on query result changes The usage scenario is to include a token with a current workflow version (as nextEventId value) into QueryWorkflowExecution result. Then if the token is included into the next QueryWorkflowExecution call do not return the call until the workflow version changes. If it doesn’t change after the specified timeout (let’s say a minute) return the call with empty no changes result. An alternative is to use hash of the query result as the version. In this case workflow changes that don’t affect the query result are not going to notify the query callers. #### Implementation Details If nextEventId is used as a version, a QueryWorkflowExecution call to the History Service checks the version value against the current workflow nextEventId value. If they do not match the usual query path is executed. If the versions match the long poll request is registered with in-memory mutable state cache. At some point the workflow execution receives an event that causes a decision task creation. When the decision task is started the queries registered with the workflow are included into it. The decision task completion includes the query results and all the registered long poll queries are notified about them. If no decision task is scheduled for the long poll timeout then the query is returned with no changes empty result. If hash of the query result is used as a version then mutable state has to persist the map of hash(queryName + queryArgs) to hash(queryResult). And any new query request is checked against the correspondent value in this map. If it matches then it is registered with the mutable state. When a decision task due to an external event completes it includes query results. The query result hash is checked against the already stored result hashes. If the hash value has changed then the poll requests are notified. ### Update operation that should complete within a single decision This section is not fully designed yet. ## Open issues 1. Design update operation. There is an outstanding design issue for how update works in a multiple DC model. 2. Agree on the client model for synchronous request reply primitives. ================================================ FILE: docs/design/2290-cadence-ndc.md ================================================ # Design doc: Cadence NDC Author: Cadence Team Last updated: Dec 2019 Reference: [#2290](https://github.com/cadence-workflow/cadence/issues/2290) ## Abstract Cadence Cross DC is a feature which asynchronously replicates workflows from active data center to other passive data centers, for backup & state reconstruction. When necessary, customer can failover to any of the data centers which have the backup for high availability. Cadence Cross DC is an AP (in terms of CAP). This doc explains the high level concepts about Cadence Cross DC (mainly the N data center version). ## Newly Introduced Components 1. Version 2. Version history 3. Workflow history conflict resolution 4. Zombie workflows 5. Workflow task processing ### Version Version is a newly introduced concept in Cadence Cross DC which describes the chronological order of events (per customer domain). Cadence Cross DC is AP, all domain change events & workflow history events are replicated asynchronously for high throughput. This means that data across data centers are not strongly consistent. To guarantee that domain data & workflow data will achieve eventual consistency (especially when there is data conflict during a failover), version is introduced and attached to customers' domains. All workflow history events generated in a domain will also come with the version in that domain. All participating data centers are pre-configured with a unique initial version, and a shared version increment: * `initial version < shared version increment` When performing failover for one domain from one data center to another data center, the version attached to the domain will be changed by the following rule: * for all versions which follow `version % (shared version increment) == (active data centers' initial version)`, find the smallest version which has `version >= old version in domain` When there is a data conflict, comparison will be made and workflow history events with the highest version will win. When a data center is trying to mutate a workflow, version will be checked. A data center can mutate a workflow if and only if * version in the domain belongs to this data center, i.e. `(version in domain) % (shared version increment) == (this data centers' initial version)` * the version of this workflow's last event is equal or less then version in domain, i.e. `(last event's version) <= (version in domain)` Domain version change example: * Data center A comes with initial version: 1 * Data center B comes with initial version: 2 * Shared version increment: 10 T = 0: domain α is registered, with active data center set to data center A ``` domain α's version is 1 all workflows events generated within this domain, will come with version 1 ``` T = 1: domain β is registered, with active data center set to data center B ``` domain β's version is 2 all workflows events generated within this domain, will come with version 2 ``` T = 2: domain α is updated to with active data center set to data center B ``` domain α's version is 2 all workflows events generated within this domain, will come with version 2 ``` T = 3: domain β is updated to with active data center set to data center A ``` domain β's version is 11 all workflows events generated within this domain, will come with version 11 ``` ### Version History Version history is a newly introduced concept which provides high level summary about version information of workflow history. Whenever there is a new workflow history event generated, the version from domain will be attached. Workflow mutable state will keep track of all history events & corresponding version. Example, version history without data conflict: * Data center A comes with initial version: 1 * Data center B comes with initial version: 2 * Shared version increment: 10 T = 0: adding event with event ID == 1 & version == 1 View in both data center A & B ``` | -------- | ------------- | --------------- | ------- | | Events | Version History | | -------- | ------------- | --------------- | ------- | | Event ID | Event Version | Event ID | Version | | -------- | ------------- | --------------- | ------- | | 1 | 1 | 1 | 1 | | -------- | ------------- | --------------- | ------- | ``` T = 1: adding event with event ID == 2 & version == 1 View in both data center A & B ``` | -------- | ------------- | --------------- | ------- | | Events | Version History | | -------- | ------------- | --------------- | ------- | | Event ID | Event Version | Event ID | Version | | -------- | ------------- | --------------- | ------- | | 1 | 1 | 2 | 1 | | 2 | 1 | | | | -------- | ------------- | --------------- | ------- | ``` T = 2: adding event with event ID == 3 & version == 1 View in both data center A & B ``` | -------- | ------------- | --------------- | ------- | | Events | Version History | | -------- | ------------- | --------------- | ------- | | Event ID | Event Version | Event ID | Version | | -------- | ------------- | --------------- | ------- | | 1 | 1 | 3 | 1 | | 2 | 1 | | | | 3 | 1 | | | | -------- | ------------- | --------------- | ------- | ``` T = 3: domain failover triggered, domain version is now 2 adding event with event ID == 4 & version == 2 View in both data center A & B ``` | -------- | ------------- | --------------- | ------- | | Events | Version History | | -------- | ------------- | --------------- | ------- | | Event ID | Event Version | Event ID | Version | | -------- | ------------- | --------------- | ------- | | 1 | 1 | 3 | 1 | | 2 | 1 | 4 | 2 | | 3 | 1 | | | | 4 | 2 | | | | -------- | ------------- | --------------- | ------- | ``` T = 4: adding event with event ID == 5 & version == 2 View in both data center A & B ``` | -------- | ------------- | --------------- | ------- | | Events | Version History | | -------- | ------------- | --------------- | ------- | | Event ID | Event Version | Event ID | Version | | -------- | ------------- | --------------- | ------- | | 1 | 1 | 3 | 1 | | 2 | 1 | 5 | 2 | | 3 | 1 | | | | 4 | 2 | | | | 5 | 2 | | | | -------- | ------------- | --------------- | ------- | ``` Since Cadence is AP, during failover (change of active data center of a domain), there exist case that more than one data center can modify a workflow, causing divergence of workflow history. Below shows how version history will look like under such conditions. Example, version history with data conflict: Below will show version history of the same workflow in 2 different data centers. * Data center A comes with initial version: 1 * Data center B comes with initial version: 2 * Data center C comes with initial version: 3 * Shared version increment: 10 T = 0: View in both data center B & C ``` | -------- | ------------- | --------------- | ------- | | Events | Version History | | -------- | ------------- | --------------- | ------- | | Event ID | Event Version | Event ID | Version | | -------- | ------------- | --------------- | ------- | | 1 | 1 | 2 | 1 | | 2 | 1 | 3 | 2 | | 3 | 2 | | | | -------- | ------------- | --------------- | ------- | ``` T = 1: adding event with event ID == 4 & version == 2 in data center B ``` | -------- | ------------- | --------------- | ------- | | Events | Version History | | -------- | ------------- | --------------- | ------- | | Event ID | Event Version | Event ID | Version | | -------- | ------------- | --------------- | ------- | | 1 | 1 | 2 | 1 | | 2 | 1 | 4 | 2 | | 3 | 2 | | | | 4 | 2 | | | | -------- | ------------- | --------------- | ------- | ``` T = 1: domain failover to data center C, adding event with event ID == 4 & version == 3 in data center C ``` | -------- | ------------- | --------------- | ------- | | Events | Version History | | -------- | ------------- | --------------- | ------- | | Event ID | Event Version | Event ID | Version | | -------- | ------------- | --------------- | ------- | | 1 | 1 | 2 | 1 | | 2 | 1 | 3 | 2 | | 3 | 2 | 4 | 3 | | 4 | 3 | | | | -------- | ------------- | --------------- | ------- | ``` T = 2: replication task from data center C arrives in data center B Note: below are a tree structures ``` | -------- | ------------- | | Events | | -------- | ------------- | | Event ID | Event Version | | -------- | ------------- | | 1 | 1 | | 2 | 1 | | 3 | 2 | | -------- | ------------- | | | ------------- | ------------ | | | | -------- | ------------- | | -------- | ------------- | | Event ID | Event Version | | Event ID | Event Version | | -------- | ------------- | | -------- | ------------- | | 4 | 2 | | 4 | 3 | | -------- | ------------- | | -------- | ------------- | | --------------- | ------- | | Version History | | --------------- | ------- | | Event ID | Version | | --------------- | ------- | | 2 | 1 | | 3 | 2 | | --------------- | ------- | | | ------- | ------------------- | | | | --------------- | ------- | | --------------- | ------- | | Event ID | Version | | Event ID | Version | | --------------- | ------- | | --------------- | ------- | | 4 | 2 | | 4 | 3 | | --------------- | ------- | | --------------- | ------- | ``` T = 2: replication task from data center B arrives in data center C, same as above ### Workflow History Conflict Resolution When a workflow encounters divergence of workflow history, proper conflict resolution should be applied. In Cadence NDC, workflow history events are modeled as a tree, as shown in the second example in [### Version History]. Workflows which encounters divergence will have more than one history branches. Among all history branches, the history branch with the highest version is considered as the `current branch` and workflow mutable state is a summary of the current branch. Whenever there is a switch between workflow history branches, a complete rebuild of workflow mutable state will occur. ### Zombie Workflows There is an existing contract that for any domain & workflow ID combination, there can be at most one run (domain & workflow ID & run ID) open / running. Cadence NDC aims to keep the workflow state as up-to-date as possible among all participating data centers. Due to the nature of Cadence NDC, i.e. workflow history events are replicated asynchronously, different run (same domain & workflow ID) can arrive at target data center at different times, sometimes out of order, as shown below: ``` | ------------- | | ------------- | | ------------- | | Data Center A | | Network Layer | | Data Center B | | ------------- | | ------------- | | ------------- | | | | | Run 1 Replication Events | | | -----------------------> | | | | | | Run 2 Replication Events | | | -----------------------> | | | | | | | | | | | | | Run 2 Replication Events | | | -----------------------> | | | | | | Run 1 Replication Events | | | -----------------------> | | | | | ------------- | | ------------- | | ------------- | | Data Center A | | Network Layer | | Data Center B | | ------------- | | ------------- | | ------------- | ``` Since run 2 appears in data center B first, run 1 cannot be replicated as runnable due to rule `at most one run open` (see above), thus, `zombie` workflow state is introduced. Zombie state indicates a workflow which cannot be actively mutated by a data center (assuming corresponding domain is active in this data center). A zombie workflow can only be changed by replication task. Run 1 will be replicated similar to run 2, except run 1's workflow state will be zombie before run 1 reaches completion. ### Workflow Task Processing In the context of Cadence NDC, workflow mutable state is an entity which tracks all pending tasks. Prior to the introduction of Cadence NDC, workflow history events are from a single branch, and Cadence server will only append new events to workflow history. After the introduction of Cadence NDC, it is possible that a workflow can have multiple workflow history branches. Tasks generated according to one history branch maybe invalidated by history branch switching during conflict resolution. Example: T = 0: task A is generated according to event ID: 4, version: 2 ``` | -------- | ------------- | | Events | | -------- | ------------- | | Event ID | Event Version | | -------- | ------------- | | 1 | 1 | | 2 | 1 | | 3 | 2 | | -------- | ------------- | | | | -------- | ------------- | | Event ID | Event Version | | -------- | ------------- | | 4 | 2 | <-- task A belongs to this event | -------- | ------------- | ``` T = 1: conflict resolution happens, workflow mutable state is rebuilt and history event ID: 4, version: 3 is written down to persistence ``` | -------- | ------------- | | Events | | -------- | ------------- | | Event ID | Event Version | | -------- | ------------- | | 1 | 1 | | 2 | 1 | | 3 | 2 | | -------- | ------------- | | | ------------- | -------------------------------------------- | | | | -------- | ------------- | | -------- | ------------- | | Event ID | Event Version | | Event ID | Event Version | | -------- | ------------- | | -------- | ------------- | | 4 | 2 | <-- task A belongs to this event | 4 | 3 | <-- current branch / mutable state | -------- | ------------- | | -------- | ------------- | ``` T = 2: task A is loaded. At this time, due to the rebuilt of workflow mutable state (conflict resolution), task A is no longer relevant (task A's corresponding event belongs to non-current branch). Task processing logic will verify both the event ID and version of the task against corresponding workflow mutable state, then discard task A. ================================================ FILE: docs/design/active-active/active-active.md ================================================ # Design doc: Cadence Active-Active Author: [@taylanisikdemir](https://github.com/taylanisikdemir) Last updated: Apr 2025 ## Abstract Multiple Cadence clusters can be configured to form a cluster group. Each cluster group can have multiple domains. Currently domains are the smallest unit of isolation which supports two modes: 1. **Local Domain:** Only one cluster in the group hosts the domain. No replication. 2. **Active-Passive Domain:** Domain is active in one cluster and passive in other cluster(s). All workflows of the domain are replicated to other specified passive clusters. See [NDC doc](../2290-cadence-ndc.md) for more details. This document describes the design and implementation of active-active domains which is a new mode where a domain can be active in multiple clusters but individual workflows are only active in one cluster at any given time. ## Background The main drawbacks of Active-Passive approach are: - **Cross region call forwarding:** Limiting domain to a single region may lead to cross region dependency in hot paths if the user wants to process requests in multiple regions. Passive regions will forward requests to the active region. This leads to increased latency, cost and potential timeouts. - **Cross region data inconsistency:** Sticking with a single active Cadence region may lead to cross region data inconsistency issues for external data stores. Example: - A request originates in Region-0. - It writes something to a data store in Region-0 and starts a workflow for further processing. - Domain is active in Region-1 which means Cadence will forward requests to Region-1 and workers in Region-1 will process the workflow. - Worker in Region-1 is likely to read stale data from the data store in Region-1 because of eventual consistency of cross region data stores. - **Underutilized workers in passive region(s):** Domain owners must deploy workers to both regions to be ready for failover. Workers in the passive region(s) stay idle. In the event of a failover, workers in the passive region(s) will need to scale up and if this is not done quickly, workflows will observe delays (potentially time outs). - **Complexity of using multiple domains:** Current workaround to achieve active-active processing with Cadence is to create multiple domains and mark one of them active per region. E.g. domain-region0 and domain-region1. This requires users to remember which domain a specific workflow is created when they want to signal/describe the workflow. They can’t seamlessly create workflows in both regions for a single domain. ## Design Active-active domains are a new mode of isolation where domain is active in multiple clusters but individual workflows are only active in one cluster at any given time. Users can create workflows in any of the active clusters under the same domain. Each workflow will be considered active in one of the domain's active clusters. The per-workflow active cluster selection mechanism will be dynamic/extensible to support different strategies. Before diving into the details of active-active domains design, let's look at how active-passive domains work at high level. Then we will cover how active-active domains will be implemented in follow up sections. ### Active-Passive domain's failover version As the name suggests, an active-passive domain is active in one cluster and passive in other clusters. All the workflows of the domain are replicated to other passive clusters. Active cluster is determined by the failover version of the domain in the database. - Cluster group configuration: ``` clusterGroupMetadata: failoverVersionIncrement: 10 clusterGroup: cluster0: initialFailoverVersion: 0 ... cluster1: initialFailoverVersion: 2 ... ``` Active-Passive domains take the initial failover version from the cluster group configuration when they are created based on their ActiveClusterName field in the domain configuration. - Failover version in domain records: Failover version is a property of active-passive domains which is updated as part of failover. Domain's failover version value uniquely maps to one of the clusters by using following formula: `domain.failoverVersion % clusterGroup.failoverVersionIncrement`. Let's look at an example: | Domain | Failover Version | |--------|------------------| | Domain-A | 0 | | Domain-B | 102 | Domain-A is active in cluster-1 because failover version `0 % 100 = 0` which belongs to cluster-1 based on above configuration. Domain-B is active in cluster-2 because failover version `102 % 100 = 2` which belongs to cluster-2 based on above configuration. Domain-B was failed over in the past, otherwise its failover version would be `0` or `2` (initialFailoverVersion in cluster group configuration). ### Clusters and Regions There's no "region" concept in Cadence today. However, with active-active domains, we need to introduce the concept of regions. The constraints are going to be: 1. Each cluster belongs to one region. 2. There can be multiple clusters in a region. 3. A workflow of an active-active domain can ONLY be active in one cluster in a region. 4. A workflow of an active-active domain can be passive in any number of clusters in any number of regions. ![Active-Active One Cluster Per Region](./active-active-one-cluster-per-region.png) The underlying reason for these constraints is mainly to reuse existing failover version to cluster mapping that relies on failover versions. By restricting active-active workflows of active-active domains to have only one active cluster per region, we can reuse the failover version mapping mechanism to determine the active cluster of a workflow. Active-active domains do NOT have a failover version in the database because "activeness" is not a property of a domain. Instead, workflows of active-active domains will be associated with an "entity" which has a failover version based on the region. The region information will be defined in cluster group configuration. e.g. ``` clusterGroupMetadata: primaryClusterName: cluster0 failoverVersionIncrement: 100 regions: # new field. us-west: initialFailoverVersion: 1 us-east: initialFailoverVersion: 3 clusterGroup: cluster0: initialFailoverVersion: 0 region: us-west cluster1: initialFailoverVersion: 2 region: us-west cluster2: initialFailoverVersion: 4 region: us-east cluster3: initialFailoverVersion: 6 region: eu-west ``` Notice that regions also have a failover version now. This will be used to determine the active cluster of a workflow based on following lookups: - Workflow maps to an entity (or directly to a region). This is static and cannot change over time. - Entity maps to a region. This is dynamic and can change over time. - Region maps to a cluster. This is static and cannot change over time. Note that there can be more than one cluster in a region but an active-active domain can only have one active cluster per region. In other words, - All workflows of active-passive domains use the failover version of the cluster that it is active in. - Individual workflows of active-active domains use the failover version of the region that the entity that it is associated with belongs to. Let's look at a few examples: **Example 1:** Assume that we have an active-active domain called "Domain-AA-Sticky-Region" which exists in multiple regions and would like to have its workflows be active in the region they are started. We think this will be the most common use case so it's going to be the default behavior. **Example 2:** Assume that we have an active-active domain called "Domain-AA-User-Location" which exists in multiple regions and runs workflows per user. Each user's workflow is supposed to be processed by the cluster closest to the user's current location. Users can move around which means their workflow's active cluster will change over time. ### Active cluster selection strategies The active cluster per workflow selection strategies must support the examples given in the previous section and must be extensible to support other use cases. To drive such extensibility, we will introduce a new table called EntityActiveRegion to store mapping and the active cluster of each workflow (of an active-active domain) will be determined based on that information. The workflows themselves need to be associated with an entity which can be specified by the user in start workflow request. 1. Pick the cluster that received the start workflow request This is the default behavior and requires no additional configuration. 2. Pick the cluster based on an external entity specified in the start workflow request. This is useful when the user wants to dynamically change which cluster a workflow should be active in during the lifetime of workflow. e.g. In "Domain-AA-User-Location" example, a workflow that is started in cluster-0 but user moved to another region and the workflow needs to be active in cluster-1 going forward. Workflow start parameters: | Parameter | Description | Example | |-----------|-------------|---------| | active-region.lookup-source | Where to look up to determine the active region of the workflow for a given key. Ideally it should support the watch API. A corresponding watcher must be implemented within Cadence. | "custom.user-location-provider" | | active-region.lookup-key | The key to look up from the above source. | "userid-123" | The lookup source "custom.user-location-provider" is a custom provider that can be implemented by the user. Initially we are not going to define an RPC protocol for this and it has to be implemented within Cadence. Workflow start request determines which cluster selection strategy to be used. If both `active-region.lookup-source` and `active-region.lookup-key` are set, then the workflow will be associated with the entity specified and the active cluster will be determined based on that entity's region. Otherwise, the default behavior is to pick the cluster that received the start workflow request. ### Workflow activeness metadata in execution table A new row will be added to executions table as part of StartWorkflow LWT for active-active-domains. It will grab external entity source/key info from workflow’s headers if provided. If no such header is provided then current cluster’s region is stored as origin. These records are immutable and are replicated as part of regular per-shard replication tasks. | Shard | RowType | Domain | WFID | RunID | Data | |-------|---------|--------|------|-------|------| | 1 | ActivenessMetadata | test-domain | wf1 | 123 | Origin=us-west | | 2 | ActivenessMetadata | test-domain | wf2 | 456 | EntityType=user-location, EntityKey=london | In the above example: - `wf1` is created on us-west. No external entity associations exist. Its tasks will have failover version of the active cluster in us-west specified in domain's `RegionToClusterMap` which is 0. - `wf2` is created with an external entity association. Its tasks will have failover version of following row in `EntityActiveRegion` table: which is 5. Its tasks will be processed by cluster3 because version 5 maps to us-east region and cluster3 is active in us-east. - `wf3` is a long running workflow which was created when the domain was active-passive (migrated to active-active later on). It doesn't have an activeness metadata row in the executions table. Its existing tasks already have failover version of the cluster that it was active in so it will continue to be active in that cluster. Such workflows will continue to be processed with same failover version by the same cluster. NOTE: Long running workflows of active-passive domains like `wf3` will required the active-active domain to include pre-migration ActiveClusterName in active cluster config. Migrating a domain from active-passive to active-active and removing existing ActiveClusterName from active cluster config will break such workflows. ### Domain records Domain records will have a new field called ActiveClusters which contains the information about region to cluster mapping and failover versions of the clusters. Active-active domains will have this field set and ActiveClusterName field will be empty. Active-passive domains will not have this field set and ActiveClusterName field will be set. Domain replication config: ``` { // Clusters can be a subset of clusters in the group. Clusters: [cluster0, cluster1, cluster2, cluster3], // Active clusters can have at most one from each region. Remaining clusters will be considered passive. ActiveClusters: { RegionToClusterMap: { us-west: { ActiveClusterName: cluster0, FailoverVersion: 0, # failover version of cluster0. }, us-east: { ActiveClusterName: cluster3, FailoverVersion: 6, # failover version of cluster3. }, }, }, } ``` **RegionToClusterMap:** A map of regions to clusters that the domain can have active workflows in. It's initially formed based on cluster group configuration and given active clusters of the domain. Each region that the domain is active in will have an entry in this map. Workflow task versions are assigned based on the failover version of the region that their associated entity belongs to. If there's no entity associated with a workflow, then the task version is assigned based on the failover version of the region that the workflow is created on. Active-passive domains can be turned into active-active domains by setting the ActiveClusters field. There can be open workflows with task versions mapping to a cluster failover version instead of a region failover version. Mapping logic will support this fallback to support migration from active-passive to active-active domains. e.g. A long running workflow of an active-passive domain has task in the queue with version 0. This is supposed to be active in cluster0. If we make this domain active-active, the task version should resolve to: - cluster0 if domain is active in cluster0 (direct mapping). - cluster1 if domain is active in cluster1 (fallback to active cluster of the region). - if domain has no active cluster in the region, then it's a resolution error and the task will not be processed as active anywhere. ### EntityActiveRegion Lookup Table This table is used to lookup the active cluster of a workflow based on the entity that the workflow is associated with. - Primary key of the table is - A new plugin system will be introduced to watch supported entity types and populate the table - Watchers are per cluster group - There’s one primary cluster for the group and watcher runs on that cluster - This table is replicated to all clusters in the cluster group from the primary cluster - Failover Version - Each entity record has a failover version to be used as workflow task versions - Initially set based on cluster metadata - Incremented based on Increment value from cluster metadata whenever mapping for a key changes | EntityType | EntityKey | Region | Failover Version | LastUpdated | |------------|-----------|--------|------------------|-------------| | user-location | seattle | us-west | 1 | | | user-location | boston | us-east | 3 | | | user-location | london | eu-west | 5 | | | user-location | frankfurt | eu-central | 7 | | This table is not domain specific so failover versions are not the failover versions of a cluster but instead the failover versions of a region. Region to cluster mapping is specific to domain. Workflows associated with an entity will have task versions as indicated in the table above. Since these workflows are bound to a region, we cannot use the cluster failover versions. Entity updates are replicated to all clusters and history engines will subscribe to these updates to notify task queues. This is similar to how domain failover changes are handled today. Notifying the task queues wake up the queues to resume processing and they will be able to apply active/standby logic based on new failover versions. ### Failover scenarios **1. Regional failover (disabling a region):** This is a typical operation when the operator updates `ActiveClusterName` field in the domain record to failover the active-passive domain to one of the passive clusters. It's called cluster failover because of active-passive domains where only one cluster can be active. In the active-active domain context, this operation will be done by updating `ActiveClusters` field in the domain record. Arbitrary changes can be made to the `ActiveClusters` field as long as following criterias are satisfied - it's a subset of clusters in the cluster group. - it has at most one active cluster per region. For example, let's say we want to failover cluster0 to cluster3. (us-west -> us-east). Before failover: ``` ActiveClusters: { RegionToClusterMap: { us-west: { ActiveClusterName: cluster0, FailoverVersion: 0, }, us-east: { ActiveClusterName: cluster3, FailoverVersion: 6, }, }, } ``` After failover: ``` ActiveClusters: { RegionToClusterMap: { us-west: { // failover version of us-west region is changed to 6 so that it maps to us-east region. ActiveClusterName: cluster3, FailoverVersion: 6, }, us-east: { ActiveClusterName: cluster3, FailoverVersion: 6, }, }, } ``` Failover version to cluster mapping: - Workflows whose task version resolves to region us-west will start using version 6 from now on. - Workflows whose task version resolves to region us-east will continue using version 6. When the domain is in this state, the start workflowrequests (regardless of cluster selection strategy) will be forwarded to cluster3 and they will be sticky to that region. **2. Within region failover (changing active cluster of a region):** In the example active-active domain above, the cluster0 & cluster1 are available in us-west region and cluster0 is active. If we want to failover from cluster0 to cluster1, then we need to update the `RegionToClusterMap` in the domain record and replace cluster0 with cluster1. Before failover: ``` ActiveClusters: { RegionToClusterMap: { us-west: { ActiveClusterName: cluster0, FailoverVersion: 0, }, us-east: { ActiveClusterName: cluster3, FailoverVersion: 6, }, }, } ``` After failover: ``` ActiveClusters: { RegionToClusterMap: { us-west: { ActiveClusterName: cluster1, FailoverVersion: 2, }, us-east: { ActiveClusterName: cluster3, FailoverVersion: 6, }, }, } ``` Failover version to cluster mapping: - Workflows whose task version resolves to region us-west will start using version 2 from now on. - Workflows whose task version resolves to region us-east will continue using version 6. **3. External entity failover:** The external entity support is optional. If no entity is provided, the workflow will be associated with the region that the workflow is created on and they will be failed over as explained in the previous section. If an external entity is provided for a workflow, then it can be failed over by updating the corresponding record in `EntityActiveRegion` lookup table. Let's say we want to failover the entity with key "seattle" to region us-east so it gets processed by cluster3. Before failover: | EntityType | EntityKey | Region | Failover Version | LastUpdated | |------------|-----------|--------|------------------|-------------| | user-location | seattle | us-west | 1 | | After failover: | EntityType | EntityKey | Region | Failover Version | LastUpdated | |------------|-----------|--------|------------------|-------------| | user-location | seattle | us-east | 3 | | Notice that the failover version is updated to 3. This means that all workflows associated with this entity will be considered active in us-east region from now on. Since there can only be one active cluster per region, the corresponding workflows will be active in cluster3. Failover version to cluster mapping: There's no change in the failover version to cluster mapping in entity failovers. Same versions map to same clusters as before. However, new task versions of workflows belonging to "seattle" entity will be 3 from now on and therefore they will be active in cluster3. **4. Active-passive to active-active migration:** When an active-passive domain is migrated to active-active, - The domain record will have the `ActiveClusters` field set - The existing `ActiveClusterName` field will be left as is. - The failover version of the domain will be incremented. Even though this is not used for task versions for active-active domains, it's incremented to indicate there was a change in replication config. Before migration: ``` ActiveClusterName: cluster0, FailoverVersion: 0 ``` After migration: ``` ActiveClusterName: cluster0, FailoverVersion: 100, ActiveClusters: { RegionToClusterMap: { us-west: { ActiveClusterName: cluster0, FailoverVersion: 0, }, us-east: { ActiveClusterName: cluster3, FailoverVersion: 6, }, }, } ``` ### API Call Forwarding Cadence frontend proxies some API calls to the active cluster of the domain. Currently, this request to cluster lookup is done by checking the failover version of the active-passive domain. For active-active domains, the lookup will be per workflow based on the active cluster of the workflow. Same set of APIs are going to be forwarded to the active cluster. #### APIs that are not subject to forwarding. Always served by current cluster: - Cluster APIs - Health - GetClusterInfo - Visibility APIs - CountWorkflowExecutions - GetSearchAttributes - ListArchivedWorkflowExecutions - ListClosedWorkflowExecutions - ListOpenWorkflowExecutions - ListWorkflowExecutions - ScanWorkflowExecutions - Domain APIs - DeprecateDomain - DescribeDomain - RegisterDomain - UpdateDomain - ListDomains - Tasklist APIs - DescribeTaskList - ListTaskListPartitions - GetTaskListsByDomain - ResetStickyTaskList - Worker APIs - PollForActivityTask - PollForDecisionTask - RespondDecisionTaskCompleted - RespondDecisionTaskFailed - RespondQueryTaskCompleted - Per workflow APIs - RestartWorkflowExecution - RequestCancelWorkflowExecution - RefreshWorkflowTasks - DiagnoseWorkflowExecution - SignalWithStartWorkflowExecutionAsync - StartWorkflowExecutionAsync Note: Async API calls might be ok without forwarding (current state). We want these to be low latency and high throughput. So better to avoid checking workflow execution records. This means potentially duplicate workflow creations in active-active mode if user retries in multiple regions. Check the reconciliation logic that is supposed to dedupe these. We may revisit this in the future. #### APIs that are forwarded to active side (selectedAPIsForwardingRedirectionPolicyAPIAllowlistV2): - Per workflow APIs - StartWorkflowExecution - SignalWorkflowExecution - SignalWithStartWorkflowExecution - ResetWorkflowExecution - TerminateWorkflowExecution - QueryWorkflow: Forwarded to active cluster if strong consistency parameter is set to true. - DescribeWorkflowExecution: Forwarded to active cluster if strong consistency parameter is set to true. (This is not supported as of Apr 2025 but the work is in progress.) - GetWorkflowExecutionHistory: Forwarded to active cluster if strong consistency parameter is set to true. (This is not supported as of Apr 2025 but the work is in progress.) - Worker APIs - RecordActivityTaskHeartbeat - RecordActivityTaskHeartbeatByID - RespondActivityTaskCanceled - RespondActivityTaskCanceledByID - RespondActivityTaskCompleted - RespondActivityTaskCompletedByID - RespondActivityTaskFailed - RespondActivityTaskFailedByID ## Limitations Below is a list of limitations of active-active domains. - **Passive side tasklist processing:** Tasklist processing will be disabled for workflows that are passive in a cluster. This is to avoid mixing tasklists of a domain with active and passive tasks. Instead, workflows will resume processing after failover by relying on decision/activity timeout tasks. Misconfigured (very high) timeout values can lead to workflows not being processed for a long time which is already a risk in active-passive mode. - **History Queue V2 dependency:** History Queue V2 must be enabled in the cluster to be able to use active-active domains. Legacy queue implementation is not handling initial backoff timer tasks correctly for active-active domains. See comments in `allocateTimerIDsLocked`. V2 queues can be enabled via dynamic config. ``` history.enableTransferQueueV2: - value: true history.enableTimerQueueV2: - value: true ``` - **Cassandra dependency:** Cassandra is the only supported persistence store for active-active domains. This is because the new entity region lookup table is stored in Cassandra. This is a temporary limitation and we will support other persistence stores in the future. - **Workflow id conflict:** Multiple clusters can start workflows independently with the same workflow id. This can lead to conflicts in active-active mode. Conflict resolution mechanism should take care of this by terminating one of the workflows. - **Graceful failover:** Graceful failover is not supported for active-active domains. It is not a mode that is frequently used for active-passive domains either. - **External entity cardinality:** All cadence frontend and history services will have to make quick decisions on which cluster a workflow is active in. This requires caching all the domain data (already done for active-passive domains) and also caching the new entity region lookup table. This cache should be manageable in terms of memory usage so there will be a limit to the number of entities. Actual limit is going to be configurable based on available memory but ideally it should be in the order of thousands. ## Defining a new external entity type // TODO(active-active): define external entity providers as plugins. See resource_impl.go ================================================ FILE: docs/design/domain-updates/fencing-tokens.md ================================================ # Domain versions and fencing tokens with Cassandra Last Updated: October 2025 ### Background Cadence uses [fencing tokens](https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html) heavily due to it's reliance on databases which don't allow for cross-table transactions (in Cassandra particularly). In most instances, they're used simply as a counter, to perform optimistic updates with a compare-and-set, such that if they're operating in stale data, it's integrity can be protected. However, there's a few cases where they pass additional information such as the cluster that's active as well. There's quite a few of these tokens which have different purposes, particularly on the Domain side, and keeping track of their intent is a little tricky. This is a basic catelog of their mechanism and intent with respect to domains. The intent of this document is to provide something of an overview. ### Notification version: **Purpose**: cluster-level version guard for domain updates to prevent racing writes for the domains tables. **Where it’s set**: on every domain update/create/delete it’s set via a conditional write to the following magical ‘domain’ `cadence-domain-metadata` record: In Cassandra, this record is read like this:: ```sql SELECT notification_version FROM domains_by_name_v2 WHERE domains_partition = 0 and name = 'cadence-domain-metadata' ``` It's therefore set at the cluster level, though captured at a point in time on indivial domains (see FailoverNotificationVersion) This is a simple monotonic counter. It goes up by \+1 every domain update for any domain in the cluster. In Cassandra, writes to the domains table are done with a conditional write to the `cadence-domain-metadata` pseudo-record as a means to guard against out-of-order updates. In practice these values move up together accross clusters whenever there is a global domain update due to the domain's data being replicated, but these notification values are **not** comparable. For example, consider the following scenario. ### Failover Versions in each cluster: | Cluster | notification_version value | | ---------|----------------------------| | cluster0 | 10 | | cluster1 | 10 | When a new global domain, replicated to both these clusters is registered, the `notification_version` will be increemented in both | Cluster | notification_version value | | ---------|----------------------------| | cluster0 | 11 | | cluster1 | 11 | However, subsequently, if a 3 local domains, which are not replicated are registered in cluster1, the following will occur: | Cluster | notification_version value | | ---------|----------------------------| | cluster0 | 11 | | cluster1 | 14 | ### Domain FailoverVersion **Purpose:** (in normal domains): - To indicate which cluster is active - Specifies which domain updates are more recent - Allows workflows to merge history branches with last-write-wins This is a somewhat more complicated counter which also indicates where it was generated (as in, which cluster generated the event). It is a monotonic counter, always going up, but the values by which it go up are incremented to indicate their origin, with something like a bitmask at the end of the counter acting as an identifier of the cluster (it's actually modulo, but it is used like a networking bitmask). It uses the 'initial-failover-version' - an arbitrary unique int acting as a cluster identifier set a the cluster configuration level. The value changes for each failover. This is perhaps best illustrated with an example: ##### Example: Given three clusters with the following intitial-failover-versions: ``` clusterA: 0 clusterB: 1 clusterC: 2 ``` And given a `failover-increment` of `100` (this the value used to modulo the origin cluster) And given a domain `test-domain`, available and replicated to all three clusters, - It is first registered (arbitrarily) to be active in clusterA, in which case the `FailoverVersion` is set to `0`. - If it is failed over to clusterB, then the `FailoverVersion` will become `1`. - If it is failed back to clusterA, then it will jump up to the next increment and be `100` (which, modulo 100, gives 0, representing `clusterA`) Since this value is attached to each workflow as it's executing and each history event as it's being written, each history event therefore can be value can therefore be modulo'd by the `failover-increment` to get its origin and given two history events in a history branch, the more recent can be picked by the value being higher (since history branches in both clusters for a given workflow may diverge during a failover briefly). ### FailoverNotificationVersion This is unfortunately similarly named to the Failover version, but it has a different purpose: This is value which captures the current state of the domains at the point of processing them through the replication stack and during Graceful Failover. The values are **not** replicated and **not** comparable across clusters and its purpose is to prevent domain replication messages being replayed out of order. **Where it’s set:** When a domain is replicating, in the worker that’s processing the message, it grabs the current notification version on the side that’s taking the incoming domain update message and adds it to the fields being written. ```go // common/domain/replicationTaskExecutor.go running in the worker service as a faux singleton func (h *domainReplicationTaskExecutorImpl) handleDomainUpdateReplicationTask(ctx context.Context, task *types.DomainTaskAttributes) error { // first we need to get the current notification version since we need to it for conditional update metadata, err := h.domainManager.GetMetadata(ctx) if err != nil { h.logger.Error("Error getting metadata while handling replication task", tag.Error(err)) return err } // this is the global notification_version for all domains notificationVersion := metadata.NotificationVersion resp, err := h.domainManager.GetDomain(ctx, &persistence.GetDomainRequest{ Name: task.Info.GetName(), }) // Where it will be set if the FailoverVersion is more recent then recently seen // ie, the failover event is more recent then stored in the DB if resp.FailoverVersion < task.GetFailoverVersion() { // ... request.FailoverNotificationVersion = notificationVersion // ... } // ... where it will be written to the DB return h.domainManager.UpdateDomain(ctx, request) ``` ### Where is it used The failoverNotificationVersion is used as a guard to avoid out of order updates in the domain callback. This is running in all services, in the domain cache, and is triggered as a part of its internal refresh loop. It’s used to trigger a graceful failover trigger in history. ### How it increments: - Every time there's a notification via the domain replication stack, the domains are comparing their current FailoverNotificationValue to the value (?). Given the following state for the domain `test35`: | Cluster | value | | ---------|-------| | cluster0 | 19 | | cluster1 | 21 | | cluster2 | 20 | And the following metata values in the record `cadence-domain-metadata` | Cluster | value | | ---------|-------| | cluster0 | 43 | | cluster1 | 35 | | cluster2 | 33 | After failing over domain `test35`, the expected values for the FailoverNotificationVersion in each cluster for the domain `test35` is: | Cluster | value | | ---------|-------| | cluster0 | 43 | | cluster1 | 33 | | cluster2 | 32 | And the following metata values in the record `cadence-domain-metadata`. These will have been updated by the domain data replicating | Cluster | value | | ---------|-------| | cluster0 | 44 | | cluster1 | 36 | | cluster2 | 33 | ### Active-Active domains and ClusterAttributes Active-active domains split their notion of their workflows into subgroups called 'ClusterAttributes' which can be used by workflows using the domain to determine which cluster is active. They are a sub-implementation of FailoverVersion, ie the domain may have multiple ClusterAttributes, each with their own Version which functions similarly to the normal FailoverVersion. Ie, a domain may validly have the following configuration: For example, given three replicating clusters, with the following intitial-failover-versions ``` clusterA: 0 clusterB: 1 clusterC: 2 ``` And a failover increment of 100: For example: An active-active domain may have the following (this is a JSON representation of the domain `desc` command of a sample domain) ``` { "domainInfo": { "name": "active-active-domain", "status": "REGISTERED", "uuid": "177b38ef-ed54-4c0e-b84b-1b52e3c3598e" }, "replicationConfiguration": { "activeClusterName": "cluster2", "clusters": [ { "clusterName": "cluster0" }, { "clusterName": "cluster1" }, { "clusterName": "cluster2" } ], "activeClusters": { "attributeScopes": { "location": { "clusterAttributes": { "cityA": { "activeClusterName": "cluster0", "failoverVersion": 10 }, "cityB": { "activeClusterName": "cluster2", "failoverVersion": 12 } } } } } }, "failoverVersion": 52, "isGlobalDomain": true } ``` In this instance, the domain has a 'default' active cluster (cluster2, indicated by the failoverVersion 52), as well as two cluster-attributes for the 'cities' attribute: `cityA` which is active in cluster0 and `cityB` which is active in cluster2. These can be failed over and active in clusters independently of the domain's default active cluster. Workflows using either of these cluster-attributes will only ever be active in one cluster at a time - that restriction remains. ================================================ FILE: docs/design/graceful-domain-failover/3051-graceful-domain-failover.md ================================================ # Design doc: Cadence graceful domain failover Author: Cadence Team Last updated: Mar 2020 Reference: [#3051](https://github.com/cadence-workflow/cadence/issues/3051) ## Abstract Cadence supports domain failover with a multi-cluster setup. However, the problems with the current failover are: 1. Workflow progress could be lost. 2. No causal consistency guarantee on the domain level. The graceful domain failover uses to solve those two problems. ## Use cases Users can trigger graceful domain failover via Cadence CLI (timeout is optional): `cadence --domain cadence-global domain update -failover_type graceful -active_cluster XYZ -failover_timeout 120s` Users can force complete domain failover via Cadence CLI: `cadence --domain cadence-global domain update -failover_type force -active_cluster XYZ` ## Prerequisites There are conditions before starting a graceful domain failover. 1. All clusters are available. 2. The networking between clusters is available. 3. The domain state has to be stable before graceful failover. ## Limitation No concurrent graceful domain failover will be supported. ## Proposal Due to the complexity of the protocol, it will go through the architecture from cross-cluster level, single cluster level to the host level. The underlying protocol is to insert markers in the active-to-passive cluster to indicate the boundary when the domain switches to passive. On the other side, the passive-to-active cluster listens to those markers and switches domain to active after receiving all the markers. Besides, after the failover marker inserted in the active-to-passive cluster, the shard cannot accept new external requests. Before the passive-to-active receives all failover marks, no shard will start to process tasks with active logic. ### Cross cluster level ![cross clusters sequence diagram](3051-cross-clusters.png) 1, The operator issues a graceful failover to the passive-to-active cluster. 2 - 3, The passive-to-active cluster gets domain data from all other clusters for two purposes: 1. Make sure the network and clusters are available before starting the graceful failover. 2. Make sure there is no ongoing failover. 4 - 5, If the check fails, return an error to the operator indicating the graceful failover abort. 6, After the graceful failover initiated, cluster Y updates the domain to pending_active with a higher failover version to database. 7, Respond to the operator indicating the graceful failover initiated. 8, The domain update event in step 6 replicates to cluster X. 9, Cluster X updates the domain with the higher failover version and sets the domain to passive. 10, Each shard receives a domain failover notification. The shard persists the pending failover marker and inserts the marker to the replication queue. 11, The inserted failover marker replicates to cluster Y. 12, Each shard in cluster Y listens to the failover marker and reports the ‘ready’ state to the failover coordinator after it receives the failover marker. Even if the shard receives the failover marker, the shard won't process tasks as active until all shards receive failover markers. 13, The failover coordinator updates domain from pending_active to active when received ‘ready’ signal from all shards. 14, The failover coordinator updates domain from pending_active to active when the timeout hits and regardless of how many ‘ready’ signals it received. From the high-level sequence diagram, it explains how the protocol works within multi-clusters. There is detail at the cluster level. ### Cluster X ![cross cluster X sequence diagram](3051-clusterX.png) 1, Frontend receives a domain replication message. 2, Frontend updates the domain data in the database with the active cluster sets to Cluster Y and a higher failover version. 3, Domain cache fetches domain updates in a refresh loop. 4, The database returns the domain data. 5-6, After the domain updates, domain cache sends a domain failover notification to each shard. 7, Shard 1 updates the shard info with a pending failover marker to insert. 8, Shard 1 try to insert the failover marker and remove the pending failover marker from shard info after successful insertion. 9, Shard 2 updates the shard info with a pending failover marker to insert. 10, Shard 2 try to insert the failover marker and remove the pending failover marker from shard info after successful insertion. ### Cluster Y ![cross cluster Y sequence diagram](3051-clusterY.png) 1, The graceful domain failover request sends to the Frontend service. 2, Frontend updates the domain in the database with a flag indicating the domain is Pending_Active. 3, Domain cache fetches domain updates in a refresh loop. 4, The database returns the domain data. 5, After the domain updates, domain cache sends a domain failover notification to each shard. 6, After the domain updates, domain cache sends a domain failover notification to each shard. 7, In shard 1, the engine notified the coordinator about the domain failover. Happy case: 8, Shard 2 receives the failover marker. 9, Shard 1 receives the failover marker. 10, Shard 2 reports the ‘ready’ state to Coordinator. 11, Shard 1 reports the ‘ready’ state to Coordinator. 12, The coordinator persists the states from each shard. Failure case: 13, Shard2 does not receive the failover marker. 14, Shard 1 receives failover marker. 15, Shard 1 reports the ‘ready’ state to Coordinator. 16, The graceful failover timeout reached. After: 17, Coordinator update domain to active via frontend. 18, Frontend updates the domain in the database with the active state. 19, Domain cache fetches domain updates in a refresh loop. 20, The database returns the domain data. 21, After the domain updates, domain cache sends a domain failover notification to each shard. 22, After the domain updates, domain cache sends a domain failover notification to each shard. 23, Shard 1 starts to process events as active. 24, Shard 2 starts to process events as active. ## Implementation New components: 1. New state in domain 2. New task processor 3. Failover marker 4. Failover coordinator 5. Buffer queue ### Domain A new state "Pending_Active "introduced when domain moves from Passive to Active. When domain is in Pending_Active, no task will be processed as active. ![Domain state transition](3051-state-transition.png) Active to Passive: This happens when a domain failover from ‘active’ to ‘passive’ in the cluster. Passive to Pending_Active: This happens when a domain failover from ‘passive’ to ‘active’ in the cluster. In this pending-active cluster, it first updates domain state to pending. Pending_Active to Passive: This happens when the domain is in ‘pending_active’, the coordinator receives a domain failover notification with a higher version and the domain failovers to another cluster. Then the domain moves back to passive. Pending_Active to Active: The coordinator moves domain from ‘pending_active’ to ‘active’ in the scenarios: 1. All shards received the failover notification and failover markers. 2. The failover timeout reaches, and the domain is not ‘active’. ### Task processor As the new state introduced during graceful domain failover in the passive cluster, a new task processor introduces here to handle the task in Pending_Active state. Transfer: Blocked on processing tasks during failover and continue to process tasks after domain switches to active. Timer: Blocked on processing tasks during failover and continue to process tasks after domain switches to active. ### Failover marker FailoverMarker { *replicationTask failoverVersion int64 sourceCluster string Timestamp time.Time } ### Failover coordinator With the graceful failover protocol, we need to maintain a global state (in the same cluster) of all shards. So we need a new component for it. The coordinator could be a stand-alone component or elect a leader from the shards. This new component is to maintain a global state of all shards during a failover. To keep the global state, each shard does heartbeat to the coordinator to send the last X minutes failover marker (X is the max graceful failover timeout we support). The coordinator persists the state in memory and updates this state to database periodically. The state can be stored in the shard table. The state struct looks like: map[string][]*int32 The key contains the domain and the failover version. The value is a slice of shard ID. Failover timeout Currently, each history host has a component domain cache. Each shard on the same host gets domain failover notification from the domain cache. Domain cache periodically checks the database and updates the domain in memory. The failover timeout can leverage this component. During the graceful failover domain update, we record the timeout in the domain data. Domain cache reads all domain data periodically and checks if any of the graceful failover should be timed out. If the graceful failover times out, the domain cache sends a notification to shard to update the domain from pending_active to active. ### [Open for discussion] Buffer queue During the graceful failover, the task processing pauses. New external requests will be rejected when the shard already inserted the failover marker. This may causes problems if the caller cares about the availability of the APIs. One of the options is to provide this buffer queue in the passive-to-active cluster to buffer those external requests. The trade-off of this feature is that it introduces complex to invalids the requests in the buffer queue. This feature is independent of the graceful failover. But we list the option here for further discussion. Those APIs includes: 1. StartWorkflowExecution 2. SignalWithStartWorkflowExecution 3. SignalWorkflowExecution 4. CancelWorkflowExecution 5. TerminateWorkflowExecution With the current architecture, we can store those events in a queue. This queue has three types of processors. Active processor: process the messages with the active logic. PendingActive processor: Do not process messages in the buffer queue. Passive processor: forward the messages to the active cluster. #### Handle signal/cancel/terminate 1. Send a remote call to the source cluster to get the workflow state. 2. Add the message to the buffered queue if the workflow is open. #### Handle start workflow 1. Send a remote call to the source cluster to make sure there is no open workflow with the same workflow id. 2. Inserts a start workflow task in the buffer queue and creates the mutable state, history event (no timer or transfer task will be generated). The purpose of the start workflow task is to regenerate the timer tasks and transfer tasks once the domain becomes active. The purpose of the mutable state and history event is to record the workflow start event for deduplication and generate replication tasks to other clusters to sync on the workflow data. The generated history events will be replicated to all clusters. This process is required because all clusters should have the same workflow data. ================================================ FILE: docs/design/history-queue-v2/history-queue-v2.md ================================================ # Design doc: History Queue V2 Author: [@Shaddoll](https://github.com/Shaddoll) Last Updated: Jun 2025 ## Abstract Cadence is a multi-tenant workflow orchestration platform used by multiple customers. It currently runs three types of history queues within the History Service: the transfer queue, timer queue, and replication queue. These queues are responsible for: - Loading history tasks from database - Submitting history tasks to history task scheduler for task execution - Periodically persisting queue states (i.e., processing checkpoints) to the database so they can be resumed upon shard restarts History Queue V1 suffers from poor isolation between domains, leading to noisy neighbor issues. Additionally, its implementation is poorly structured and prone to bugs. History Queue V2 aims to replace V1 with a more robust and testable design, offering better code quality and improved task isolation. ## Architecture ![History Queue V2 Components](./history-queue-v2.png) Let's take transfer queue as an example. The above diagram displays the architecture of history queue v2. ## Implementation Details Below is a breakdown of the main components, ordered from low-level to high-level: **Virtual Slice**: This is the atomic component responsible for loading tasks from database with a given range and filter. **Virtual Queue**: This is the component running a background job calling virtual slice to load tasks from database and submit history tasks to history task scheduler for execution. It has multiple virtual slices ordered by their range and they don't have any intersection. And the background job processes virtual slices sequentially in the order of task range. **Virtual Queue Manager**: This is the parent component of virtual queues. Since each virtual queue has its own background job, they are isolated and that's how we achieve isolation in history queue v2. Inside a virtual queue manager, there is at least a root virtual queue component and optionally, there might be some other virtual queues. Virtual queues can be dynamically created and destructed, and the intersection of tasks processed by different virtual queues should be none. By default, there is only 1 root virtual queue. **Queue Reader**: This is the component responsible for reading tasks from database on demand and it's shared by all virtual slices in the history queue v2 instance. **Monitor & Mitigator**: Monitor is a component checking the queue processing of virtual queues and making decision to create new virtual queue. Mitigator is the component to perform the decision made by monitor. These 2 components won't be implemented in the initial release of history queue v2. **Transfer Queue**: This is one instance of history queue v2, which is responsible for the critical jobs of transfer tasks. There is also timer queue and replication queue today and new category of history queues will be created in future. ================================================ FILE: docs/design/index.md ================================================ # Design Documents - Resource Specific Tasklist [1533-host-specific-tasklist.md](1533-host-specific-tasklist.md) - Synchronous Request Reply [2215-synchronous-request-reply.md](2215-synchronous-request-reply.md) - N Data Center Replication [2290-cadence-ndc.md](2290-cadence-ndc.md) - Graceful domain failover [3051-graceful-domain-failover.md](graceful-domain-failover/3051-graceful-domain-failover.md) ================================================ FILE: docs/design/workflow-shadowing/2547-workflow-shadowing.md ================================================ # Design doc: Cadence Workflow Shadowing Author: Yu Xia (@yux0), Yichao Yang (@yycptt) Last updated: Apr 2021 Reference: [#2547](https://github.com/cadence-workflow/cadence/issues/2547) ## Abstract Cadence client libraries use workflow history and workflow definition to determine the current workflow state which is used by the workflow (decision) task for deciding the next step in the workflow. Workflow histories are immutable and persisted in the Cadence server, while workflow definitions are controlled by users via worker deployments. A non-deterministic error may happen when re-building the workflow state using a version of the workflow definition that is different than the one generating the workflow history. Typically caused by a non-backward compatible workflow definition change. For example: | Version | Workflow Definition | Workflow History | | --------| --------------------| ---------------- | | V1 | 1. Start workflow
2. Start activity A
3. Complete workflow | 1. Workflow started
2. Activity A started | | V2 | 1. Start workflow
2. Start activity B
3. Complete workflow | 1. Workflow started
2. Activity B started | When a Cadence client uses V1 history and V2 definition to build the workflow state, it will expect information of activity A in the workflow state as it sees an activity started event for it. However, it's unable to find Activity A since another activity is specified in V2 definition. This will lead to a non-deterministic error during workflow (decision) task processing. Depending on the non-deterministic error handling policy, the workflow will fail immediately or get blocked until manual operation is involved. This type of error has caused several incidents for our customers in the past. What make the situation worse is that understanding and mitigating the non-deterministic error is usually time-consuming. Blindly reverting the bad deployment will only make the situation worse as histories generated by the new workflow definition are also not compatible with the old one. The right mitigation requires not only manual operations, but also a deep understanding of why the non-deterministic error is happening, which will greatly increase the time needed to mitigate the issue. ### Goals - The goal of this project is to detect the non-deterministic errors in pre-prod environments, notify customers on this error and reduce the number of incidents caused by the non-deterministic errors. ### Non-Goals - 100% prevent non-deterministic errors: this project will not provide a bulletproof solution to prevent non-deterministic errors from happening in production. - Provide mitigation solutions to non-deterministic errors: this project will not focus on how to solve existing non-deterministic errors for customers. ## Proposal The proposal is creating a new worker mode called shadowing which reuses the existing workflow replay test framework available in both Golang and Java clients to only execute replay tests using the new workflow definition and workflow histories from production environments. When the user worker is running in the shadowing mode, the normal activity and decision worker component will not be started, instead a new replay worker will be started to replay production workflow histories. More specifically, this replay worker will call the `ScanWorkflowExecution` API to get a list of workflow visibility records and then for each workflow, get the workflow history from Cadence server by calling `GetWorkflowExecutionHistory` API and replay it against the new workflow definition using the client side replay test framework. Upon replay failure, metrics and log messages will be emitted to notify the user that there are non-deterministic changes in the new workflow definition. Note that as long as the worker can talk to the Cadence server to fetch visibility records and workflow history, it can be run in the shadowing mode. This means users have the flexibility to run it in the local environment during development to facilitate a rapid development cycle or/and make it part of the deployment process to run on a dedicated host in staging/preprod environment to get a better test coverage and catch rare cases. Users will also have the option to specify what kind of workflows they would like to shadow. As an example, if the user doesn’t use the query feature, then shadowing for the closed workflow may not necessary. Or if a user only changed the definition for one workflow type, only workflow histories of that type need to be shadowed and checked. Please check the Implementation section for the detailed options users have. The major advantages of this approach are: - **Simplicity**, very little code change required for Cadence server - **Can be run in both local and staging/preprod/canary environments** - **Generic approach that fits our internal deployment process, CI/CD pipeline and also open source customers**. The downsides however are: - **Requires some effort on the user end** to setup the release pipeline and shadowing environment and we need to provide guidelines for them. - The **load on Cadence server and our ElasticSearch cluster will increase** due to the added `GetWorkflowExecution` and `ScanWorkflowExecutions` call. But this increase can be easily controlled by the server side rate limiter. Check the “Open Questions” section for an estimate for the load. - The dedicated **shadow worker will become idle after the replay test is done**. ![deployment pipeline](2547-deployment-pipeline.png) ## Other Considered Options ### [History Generation with task mocking](https://github.com/cadence-workflow/cadence-go-client/issues/1050) The idea of this approach is similar to the local replay test framework. But instead of asking customers to get an existing workflow history from production, it will ask customers to define possible workflow inputs, activity input/output, signals, etc. and then generate multiple workflow histories based on different combinations of values based on a certain workflow version. Random errors will also be injected to cover failure cases. Upon workflow code change, those generated workflows can be replayed against the new workflow definition to see if there are any non-deterministic changes. The approach can be viewed as some form of automated test where test cases are auto-generated based on various inputs to the workflow. As it can validate the changes through local tests, **the development cycle can be faster**. This could improve the local development experience and could be considered in vNext phase. However, this also means that **users need to learn a set of new APIs** for specifying different types of inputs and update the test if there are new activities or signal channels. **Coverage might also be a concern** as many inputs are coming from upstream services, like Kafka, and it’s hard to specify all different possible parameters. Finally, from an implementation perspective, the new automated test framework is **language specific** and needs the effort to re-design API and re-implement for different client libraries. ### Integration test with archived (partial) history This approach reuses existing history archival code paths to dump workflow histories that match a certain query to blob storage. When a user changes the workflow code, they can connect to the blob storage, and run an integration test locally to replay all of the archived history to see if there’s a non-deterministic error. Since not all blob storage support List operation when there's a large number of items, we may also need to rely on visibility archival for getting a list of workflowIDs and runIDs. The approach **doesn’t require too many changes on server end** as most code paths for history/visibility archival can be reused. We only need to figure out when and how to sample the workflow for archiving, and the destination for archiving. It also **avoids the idle worker problem** and **won’t incur any load increase on Cadence server** as the testing framework will talk directly to the storage for storing archived history and visibility records. There are also several downsides associated with this approach: - We need to decide who owns and manages the blob storage. - If Cadence owns it, GDPR constraints might be a concern as some workflow histories will contain PII data and can’t be archived for too long. **A set of APIs for managing the storage** will also be necessary. - Register user callbacks for managing storage. - If users own it, it will **increase their burden for managing it**. E.g. regularly clean up the unneeded workflows (may due to GDPR constraints), update query to replay only recent workflows to ensure a reasonable test time. - Extra work for setting additional history and visibility archival systems. - The integration test may take a long time to replay all the archived workflows **making it not ideal to run locally**. - Since the test is long running and not part of the deployment process, **users may tend to skip the test** if they believe the change they made is backward-compatible and causing issues when deployed to production. - From an open source perspective, users need to have an archival system setup, which we only support S3 and GCP for now. If those two don't work for the customer, they may have to **implement their own archiver**. On the client side, we can provide a blob storage interface, but customers still need to **implement the logic for fetching history and/or visibility records for blob storage**. - Finally since this approach takes blob storage as an external dependency, it **won’t be functioning when the dependencies are down**. ### Replication based replay test The final approach is: reusing the replication stack to dispatch decision tasks to workers at the standby cluster and perform the replay check. However this approach only works for global domain users, so it’s not a valid option. ## Implementation ### Worker Implementation In the proposal of workflow shadowing, the worker does shadowing traffic in 3 steps. 1. Get a list of workflow visibility records. 2. For each record, get the workflow history. 3. Run the replay test on the workflow history. These steps can be implemented in native code or in Cadence workflow. For the local development environment, we will provide a testing framework for shadowing workflow from production, so that users can validate their change sooner and speed up their development process. This kind of integration test can also be easily integrated with existing release pipelines to enforce a check upon landing code. For staging/production environment, we incline to use a Cadence workflow to control the shadowing because of the following advantages: 1. **Scalability**: support large shadowing traffic. 2. **Extensibility**: easy to extend to different languages. 3. **Maintenance**: no special worker code to support the shadowing mode. 4. **Visibility**: shadowing progress and result can easily be viewed using the Cadence Web UI and metric dashboards. As metrics will be emitted during the shadowing processing, it can also be integrated with release pipelines for checking workflow compatibility before deploying to production environments. The disadvantage is that users are required to start a Cadence server to run the shadow workflow. ### Shadow Worker Options The following options will be added to the Worker Option struct in both Golang and Java client: - **EnableShadowWorker**: a boolean flag indicates whether the worker should run in shadow mode. - **ShadowMode**: - **Normal**: stop shadowing after a scan for all workflows, this will be the default selected value. A new iteration will be triggered upon worker restart or deployment. - **Continuous**: keep shadowing until the exit condition is met. Since open workflows will keep making progress, two replays might lead to different results. This mode is useful for user’s whose workflow will block on a long timer. - **Domain**: user domain name. - **TaskList**: the name of the task list the shadowing activity worker should poll from. - **ShadowWorkflowQuery**: a visibility query for all the workflows that need to be replayed. If specified the following three options will be ignored. - **ShadowWorkflowTypes**: a list of workflow types that need to be checked. - **ShadowWorkflowStartTimeFilter**: a time range for the workflow start time. - **ShadowWorkflowStatus**: a list of workflow status. Workflow will be checked only if its close status is in the list. Option for open workflow will also be provided and will be the default. - **SamplingRate**: a float indicating the percentage of workflows (returned by workflow scan) that should be replayed. - **Concurrency**: concurrency of replay checks, which is the number of parallel replay activities in the shadow workflow. ### Local Shadow Test Options The options provided by the local test framework will be similar to the shadow worker options, but as in tests there’s only one single thread for scanning and replaying, only concurrency equal to 1 will be accepted. The option for EnableShadowWorker, Domain, and TaskList will also be removed. ### Workflow Code Ownership We decide to keep the workflow code at the server side due to the following reasons: 1. The shadowing logic for Java and Go clients are identical. By keeping the shadowing workflow definition at the server side, we only need to implement it once, and don’t need to worry about keeping two workflow definitions in sync. 2. Since Cadence server owns the workflow code, we can update the workflow definition at any time and even make non-backward compatible changes without having to ask customers to update their client dependency. The downsides are: 1. Cadence team needs to provide workers to process the decision task generated by the shadowing workflow. 2. Shadowing workflow from all the customers will live in one domain, so they may affect each other’s execution as the total amount of resources allocated to a given domain is limited. However, considering the fact that the load generated by shadowing workflow is small and it’s unlikely many users will run the shadowing workflow at the same time, those downsides should not be a concern. ### Metrics The following metrics will be emitted in the shadow workflow: 1. Number of succeeded/skipped/failed workflow replays 2. Latency for each replay test 3. Start/Complete/Continue_as_new of shadow workflow 4. Latency for each shadow workflow ================================================ FILE: docs/flow.md ================================================ # Flow ## Workflow processing flow Below diagram demonstrates how a workflow is processed by Cadence workers and backend components. Note that this is a partial diagram that only highlights selected components and their interactions. There is more happening behind the scenes. [comment]: <> (To visualize mermaid flowchart below, install Mermaid plugin for your IDE. Works in github out of the box) ```mermaid flowchart TD User((Domain X Admin)) -->|"0. Deploy worker with config: domain, tasklist(s)"| Worker[[\nDomain X Worker\n\n]] ExternalInitiator(("Domain X\nAnother Service/CLI")) -->|"1. Start/Signal Workflow"| Frontend Worker -->|2. Poll tasks for domain: X, tasklist: tl1| Frontend[[\nCadence Frontend\n\n]] Frontend -->|3. Forward Poll request| Matching[[\nCadence Matching\n\n]] Matching -->|4. Poll| TaskListManager(Tasklist Manager for tl1) TaskListManager -->|5. Long Poll| WaitUntilTaskFound WaitUntilTaskFound --> |6. Return task to worker| Worker Worker -->|7. Generate decisions\n& respond| Frontend Frontend -->|8. Respond decisions| History[[\nCadence History\n\n]] History -->|9. Mutable state update| ExecutionsStore[(Executions Store)] History -->|10.a. Notify| HistoryQueue(History Queues - per shard) HistoryQueue -->|Periodic task fetch| ExecutionsStore HistoryQueue -->|11.b. Execute transfer task| TransferTaskExecutor(Transfer Task Executor) HistoryQueue -->|11.a. Execute timer task| TimerTaskExecutor(Timer Task Executor) HistoryQueue -->|Periodic per-shard offset save| ShardsStore[(Shards Store)] TransferTaskExecutor -->|12.b. Add task| Matching TimerTaskExecutor -->|12.a. Add task| Matching Matching -->|13. Add task| TaskListManager TaskListManager -->|14. Add task| SyncMatch{Check sync match} SyncMatch -->|15.b. Found poller - Do sync match| SyncMatched SyncMatch -->|15.a. No pollers - Save task| TasksStore[(Tasks Store)] TaskListManager -->|Periodic task fetch| TasksStore ``` ### Cassandra Notes: In order to leverage [Cassandra Light Weight Transactions](https://www.yugabyte.com/blog/apache-cassandra-lightweight-transactions-secondary-indexes-tunable-consistency/), Cadence stores multiple types of records in the same `executions` table. ([ref](https://github.com/cadence-workflow/cadence/blob/51758676ce9d9609e736c64f94dc387ed2c75b7c/schema/cassandra/cadence/schema.cql#L338)) - **Shards Store**: Shard records are stored in `executions` table with type=0. - **Executions Store**: Execution records are stored in `executions` table with type=1 - **Tasks Store**: Task records are stored in `executions` table with type={2,3,4,5} ================================================ FILE: docs/howtos/async-api.md ================================================ # Async API ## Overview Triggering too many workflows at the same time may overload the underlying storage system. It's typical the Cadence operator to set up quotas for these APIs via `frontend.rps` dynamic config. This means the caller service has to backoff and retry if the request is rejected by the Cadence server. When you have millions of workflows to trigger, you may want to consider using the Async APIs. More specifically, - `StartWorkflowExecutionAsync` - `SignalWithStartWorkflowExecutionAsync` These APIs are designed to be more efficient than the regular APIs. They don't wait for the workflow to be started or signaled. Instead, they queue a message to underlying queue system and return. The queue system supported currently is Kafka. The Cadence server (workers service) will poll the queue and process the messages. ## Caveats - Global availability: Regional failovers will work as is for Global Cadence Domains. The async workflow requests will start on the active side once failed over. Previously enqueued requests will be forwarded to active side by Cadence in an idempotent way. If the regions are disconnected or the previously active region is fully down then the leftover messages in the queue will be processed once the region is healthy again. - Idempotency: To avoid any edge cases and achieve full idempotency, Cadence dedupes requests based on request id. Request id is not exposed from our fat clients used internally so if you have additional retries on top of what client library already performs then you might send duplicate requests. See workflowid reuse policy to get around duplicate request problems. - Workflow id reuse policy: If you enqueue duplicate requests with same workflow id and choose a reuse policy that causes failure to start, the failure will be discarded. Since duplicate delivery is given with queue systems (at least once) avoid using WorkflowIDReusePolicyTerminateIfRunning . - Run id: Async APIs accept the same input parameters as their corresponding sync versions but do NOT return run id. Based on our discussions with multiple Cadence users, run id is discarded almost all the time so by switching to Async APIs you are getting pretty much the same semantics. - Request size and rate limits: Async APIs can support higher rate limits than the regular APIs. The default rate limit is 10k rps. You can adjust the rate limit via `frontend.asyncrps` dynamic config. Your kafka topic might be the bottleneck so you can adjust the topic configuration accordingly. - Delays: Async API requests are queued and consumed by Cadence backend. There can be some unexpected delays in this flow due to high number of messages/bytes etc. Basically your workflows don't start immediately and the delay depends on various factors. ## How to use This section walks through how to use the Async APIs on a local Cadence cluster. 1. **Start a local Cadence cluster with async workflow queue enabled.** ``` docker compose -f docker/docker-compose-async-wf-kafka.yml up ``` This will start Cadence server, Cadence UI, Kafka, Cassandra, Prometheus and Grafana containers. Notice the environment variables in the docker compose file: - `ASYNC_WF_KAFKA_QUEUE_ENABLED`: This is set to `true` to enable the async workflow queue. - `ASYNC_WF_KAFKA_QUEUE_TOPIC`: This is the name of the Kafka topic to use for the async workflow queue. - `KAFKA_SEEDS`: This is the list of Kafka brokers to use for the async workflow queue. - `KAFKA_PORT`: This is the port to use for the Kafka brokers. When Cadence server container starts, it will materialize the ./docker/config_template.yaml file and populate `asyncWorkflowQueues` section with the following content: ``` asyncWorkflowQueues: queue1: type: "kafka" config: connection: brokers: - kafka:9092 topic: async-wf-topic1 ``` Now the Cadence server recognizes `queue1` as a valid async workflow queue. However, it doesn't interact with Kafka yet. Note: It may take a minute for the containers to be ready. Check their status via `docker ps` command and once all are running, you can proceed to the next step. 2. **Register a domain (or skip this step if you already have a domain)** ``` docker run -t --rm --network host ubercadence/cli:master \ --address localhost:7933 \ --domain test-domain \ domain register ``` 3. **Enable async APIs for the domain** Configure `test-domain` to use `queue1` as the async workflow queue. ``` docker run -t --rm --network host ubercadence/cli:master \ --address localhost:7933 \ --domain test-domain \ admin async-wf-queue update \ --json "{\"PredefinedQueueName\":\"queue1\", \"Enabled\": true}" ``` Note: If you get "Domain update too frequent." error, you can try to wait for a minute and run the command again. Validate the new configuration: ``` docker run -t --rm --network host ubercadence/cli:master \ --address localhost:7933 \ --domain test-domain \ admin async-wf-queue get ``` Cadence server will start a consumer to poll from the kafka topic within a minute. If more than one domain is using the same queue, the consumer will be shared across all domains. 4. **Start a workflow asynchronously** The `test-domain` is now ready to accept async workflow requests. Update your worker (or modify samples code) to use one of the async APIs: - [StartWorkflowExecutionAsync](https://github.com/cadence-workflow/cadence-idl/blob/0e56e57909d9fa738eaa8d7a9561ea16acdf51e4/proto/uber/cadence/api/v1/service_workflow.proto#L49) - [SignalWithStartWorkflowExecutionAsync](https://github.com/cadence-workflow/cadence-idl/blob/0e56e57909d9fa738eaa8d7a9561ea16acdf51e4/proto/uber/cadence/api/v1/service_workflow.proto#L63) ================================================ FILE: docs/howtos/cassandra-fql.md ================================================ # View Cassandra full query logs 1. Run cadence components via docker compose ``` docker compose docker/docker-compose.yml up ``` 2. First enable fql via nodetool ([ref](https://cassandra.apache.org/doc/stable/cassandra/operating/fqllogging.html#enabling-fql)) ``` docker exec $(docker inspect --format="{{.Id}}" docker-cassandra-1) nodetool enablefullquerylog --path /tmp/cassandra_fql ``` 3. Check `/tmp/cassandra_fql` folder exists ``` docker exec $(docker inspect --format="{{.Id}}" docker-cassandra-1) ls /tmp/cassandra_fql ``` 4. Run some workflows to generate queries (optional) 5. Inspect the full query log dump via fqltool (it's under tools/bin in default apache cassandra installation) ``` docker cp $(docker inspect --format="{{.Id}}" docker-cassandra-1):/tmp/cassandra_fql/ /tmp/cassandra_fql/ && fqltool dump /tmp/cassandra_fql | less ``` ================================================ FILE: docs/howtos/setup-cadence-locally-with-replication.md ================================================ ### How to run Cadence locally with replication This is a guide for using a local laptop instance and running three Cadence clusters, all cross-replicted. They will (for the purposes of demonstration) be entirely independentent, but poll one another for workflows for replication and write to their own keyspaces independently. It's a good way to work through how Cadence's domains are configured with replication, how failovers work and so forth. This is a somewhat more advanced tutorial, and so it's recommended to ensure that you have a basic familiarity with Cadence and it's concepts first before trying this. #### Prerequisites - Docker - The ability to perform a local go build #### Getting started All commands are expected to be run from the repo's base path. 1. Ensure there's no existing containers running or preexisting state. You'll be creating a new keyspace and a new docker-container *only* for Cassandra, and so this can conflict in a few ways with other cassandra instances for fully-containerized cadence. Be sure to stop and/or remove them before running this. 2. Ensure you build the `cadence-server` binaries by using `make bins`. This will build the main application and produce a binary build of the local instance of cadence in the repo's base path. 3. Start docker with the XDC configuration: `docker compose -f docker/dev/cassandra.yml up -d` 4. setup the schema for each of the instances with `until make install-schema-xdc; do sleep 1; done` (since Cassandra can take a while to setup, loop this check until it completes). 5. start (in tmux or terminal windows) each of the Cadence instances - `./cadence-server --config config --zone xdc --env development_xdc_cluster0 start` - `./cadence-server --config config --zone xdc --env development_xdc_cluster1 start` - `./cadence-server --config config --zone xdc --env development_xdc_cluster2 start` 6. Register a global domain that has multiple clusters enabled using the cadence CLI, so that it's worklows will be replicated across clusters: `./cadence --domain test-domain domain register --gd true --clusters cluster0,cluster1` 7. Start and test workflows as needed and failover the domain to make another cluster active with a command such as this: `./cadence --domain test-domain domain update --ac cluster1` ================================================ FILE: docs/migration/cassandra-shard-info.md ================================================ # What? The [`shard`](https://github.com/cadence-workflow/cadence/blob/v1.3.3/schema/cassandra/cadence/schema.cql#L1) Cassandra data type and [`shard`](https://github.com/cadence-workflow/cadence/blob/v1.3.3/schema/cassandra/cadence/schema.cql#L368) column in Cassandra [`executions`](https://github.com/cadence-workflow/cadence/blob/v1.3.3/schema/cassandra/cadence/schema.cql#L357) table is deprecated in favor of [`data`](https://github.com/cadence-workflow/cadence/blob/v1.3.3/schema/cassandra/cadence/schema.cql#L366C3-L366C7) and [`data_encoding`](https://github.com/cadence-workflow/cadence/blob/v1.3.3/schema/cassandra/cadence/schema.cql#L367) field. # Why? The process to introduce new field to existing data type is quite tedious because we need to update the corresponding Cassandra data type. # How? 1. Update server to v1.3.1 or a newer version 2. Set this dynamic configuration value to true - [`history.readNoSQLShardFromDataBlob`](https://github.com/cadence-workflow/cadence/blob/v1.3.3/common/dynamicconfig/dynamicproperties/constants.go#L4578C18-L4578C52) 3. Check the metrics with these tags: - operation: getshard - name: nosql_shard_store_read_from_original_column OR nosql_shard_store_read_from_data_blob These metrics are only emitted during service start time, and after migration you should only see metrics with `name:nosql_shard_store_read_from_data_blob` tag. # Status Starting from 1.3.4, the default value of [`history.readNoSQLShardFromDataBlob`](https://github.com/cadence-workflow/cadence/blob/v1.3.3/common/dynamicconfig/dynamicproperties/constants.go#L4578C18-L4578C52) is set to `true`. And we're planning to remove this dynamic config in later version. ================================================ FILE: docs/migration/tasklist-partition-config.md ================================================ # What? This document writes the steps we need to execute to migrate the number of task list partitions configuration from dynamic configuration to the database. For background knowledge about task list partition, please read this [doc](../scalable_tasklist.md). # Why? We're doing this migration because we want to programmatically update the number of task list partitions. Not all implementations of dynamic configuration dependencies support update operation. # How? 1. Check the existing number of partitions for the task list you want to migrate: - [matching.numTasklistReadPartitions](https://github.com/cadence-workflow/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3350) - [matching.numTasklistWritePartitions](https://github.com/cadence-workflow/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3344) 2. Run the following ClI commands to update the number of partitions of the task list you want to migrate and make sure that task list type parameter is not missing in your commands: ``` cadence admin tasklist update-partition -h ``` To get the number of partitions from database, use the following CLI command: ``` cadence admin tasklist describe -h ``` 3. Set this dynamic configuration value to true for the task list you want to migrate: - [matching.enableGetNumberOfPartitionsFromCache](https://github.com/cadence-workflow/cadence/blob/v1.2.15-prerelease02/common/dynamicconfig/constants.go#L4008) 4. Repeat the steps for all task lists. However, you can skip the steps if the number of partitions of the task list is 1. 5. You can enable adaptive task list scaler for the task list. Set [matching.enableAdaptiveScaler](https://github.com/cadence-workflow/cadence/blob/v1.2.17/common/dynamicconfig/constants.go#L4012) to true for the task list. # Status As of v1.2.17, the default value of [matching.enableGetNumberOfPartitionsFromCache](https://github.com/cadence-workflow/cadence/blob/v1.2.17/common/dynamicconfig/constants.go#L4004) is still false. # Plan We're planning to change the default value of [matching.enableGetNumberOfPartitionsFromCache](https://github.com/cadence-workflow/cadence/blob/v1.2.17/common/dynamicconfig/constants.go#L4004) to `true` in v2. We're planning to deprecate [matching.numTasklistReadPartitions](https://github.com/cadence-workflow/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3350) and [matching.numTasklistWritePartitions](https://github.com/cadence-workflow/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3344), but we haven't decided when to do it. Please be prepared for the migration. ================================================ FILE: docs/non-deterministic-error.md ================================================ # Abstract This article is for Cadence developers to understand and address issues with non-deterministic errors. It gives an introduction to the nature and categories of the non-deterministic error, and provide different ways for the fix. It uses Golang as example. # What is non-deterministic error ## Some Internals of Cadence workflow A Cadence workflow can be viewed as a long running process on a distributed operating system(OS). The process’s state and dispatching is owned by Cadence server, and customers’ workers provide CPU/memory resources to execute the process’s code. For most of the time, this process(workflow) is owned by a worker and running like in other normal OS. But because this is a distributed OS, the workflow ownership can be transferred to other workers and continue to run from the previous state. Unlike other OS, this is not restarting from the beginning. This is how workflow is fault tolerant to certain host failures. ![image alt text](images/non-deterministic-err.1.png) Non-deterministic issues arise during this workflow ownership transfer. Cadence is designed with event sourcing, meaning that it persists each workflow state mutation as "**history event**", instead of the whole workflow state machine. After switching to another worker, the workflow state machine has to be rebuilt. The process of rebuilding the state machine is called “**history replay**”. Any failure during this history replay is a “**non-deterministic error**”. ![image alt text](images/non-deterministic-err.2.png) Even if a workflow ownership doesn’t change, history replay is also required under some circumstances: * A workflow stack(of an execution) lives in worker’s memory, a worker can own many workflow executions. So a worker can run out of memory, it has to kick off some workflow executions (with LRU) and rebuild them when necessary. * Sometimes the stack can be stale because of some errors. In Cadence, Workflow ownership is called "stickiness". Worker memory cache for workflow stacks is called "sticky cache". ## Cadence History Protocol History replay process is under some basic rules/concepts. In Cadence, we use "close" to describe the opposite status of “open”. For activity/decision, close includes complete,timeout,fail. For workflow, close means complete/timeout/fail/terminate/cancel/continueAsNew. ### Decision * Decision is the key of history replay. It drives the progress of a workflow. * The decision state machine must be **Scheduled->Started->Closed** * The first decision task is triggered by server. Starting from that, the rest of the decision tasks are triggered by some events of the workflow itself -- when those events mean something the workflow could be waiting for. For example, Signaled, ActivityClosed, ChildWorkflowClosed, TimerFired events. Events like ActivityStarted won’t trigger decisions. * When a decision is started(internally called in flight), there cannot be any other events written into history before a decision is closed. Those events will be put into a buffer until the decision is closed -- flush buffer will write the events into history. * **In executing mode**, a decision task will try to complete with some entities: Activities/Timers/Childworkflows/etc Scheduled. * **In history replay mode**, decisions use all of those above to rebuild a stack. **Activities/Timers/ChildWorkflows/etc will not be re-executed during history replay.** ### Activity * State machine is **Scheduled->Started->Closed** * Activity is scheduled by DecisionCompleted * Activity started by worker. Normally, ActivityStartedEvent can be put at any place in history except for in between DecisionStarted and DecisionClose. * But Activity with RetryPolicy is a special case. Cadence will only write down Started event when Activity is finally closed. * Activity completed/failed by worker, or timed out by server -- they all consider activity closed, and it will trigger a decision task if no decision task is ongoing. * Like in the above, only ActivityClose events could trigger a decision. ### Local activity * Local activity is executed within decision is processing in flight. * Local activity is only recorded with DecisionCompleted -- no state machine needed. * Local activity completed will trigger another decision ### Timer * State machine is **Scheduled->Fired/Canceled** * Timer is the implementation of "workflow.Sleep()" and “workflow.NewTimer(). * Timer is scheduled by DecisionCompleted * Timer fired by server. It would trigger a decision. * Timer canceled by worker(when workflow is canceled) ### ChildWorkflow * State machine is **Initiated->Started->Closed** * ChildWorkflow is initiated by DecisionCompleted * ChildWorkflow is started by server(returning runID). It could trigger a decision. * ChildWorkflow close events can be"canceled/failed/completed". It could trigger a decision. ### SignalExternal/RequestCancel * State machine is **Initiated->Closed** * They are both initiated by DecisionCompleted. * Closed(completed/failed) by server, it could trigger a decision. ### More explanation of BufferedEvents When a decision is in flight, if something like a signal comes in, Cadence has to put it into buffer. That’s because for the next decision, SDK always processes unhandled events starting from last decision completed. There cannot be any other events to record between decision started and close event. [This may cause some issues](https://github.com/cadence-workflow/cadence/issues/2934) if you are sending a signal to self within a local activities. ## An Example of history protocol To understand the protocol, consider an example of the following workflow code: ```go func Workflow(ctx workflow.Context) error { ao := workflow.ActivityOptions{ ... } ctx = workflow.WithActivityOptions(ctx, ao) var a int err := workflow.ExecuteActivity(ctx, activityA).Get(ctx, &a) if err != nil { return err } workflow.Sleep(time.Minute) err = workflow.ExecuteActivity(ctx, activityB, a).Get(ctx, nil) if err != nil { return err } workflow.Sleep(time.Hour) return nil } ``` The workflow will execute activityA, then wait for 1 minute, then execute activityB, finally waits for 1 hour to complete. The history will be as follows if everything runs smoothly (no errors, timeouts, retries, etc): ``` ID:1 Workflow Started ID:2 DecisionTaskScheduled : first decision triggered by server ID:3 DecisionTaskStarted ID:4 DecisionTaskCompleted ID:5 ActivityTaskScheduled : activityA is scheduled by decision ID:6 ActivityTaskStarted : started by worker ID:7 ActivityTaskCompleted : completed with result of var a ID:8 DecisionTaskScheduled : triggered by ActivityCompleted ID:9 DecisionTaskStarted ID:10 DecisionTaskCompleted ID:11 TimerStarted : decision scheduled a timer for 1 minute ID:12 TimerFired : fired after 1 minute ID:13 DecisionTaskScheduled : triggered by TimerFired ID:14 DecisionTaskStarted ID:15 DecisionTaskCompleted ID:16 ActivityTaskScheduled: activityB scheduled by decision with param a ID:17 ActivityTaskStarted : started by worker ID:18 ActivityTaskCompleted : completed with nil as error ID:19 DecisionTaskScheduled : triggered by ActivityCompleted ID:20 DecisionTaskStarted ID:21 DecisionTaskCompleted ID:22 TimerStarted : decision scheduled a timer for 1 hour ID:23 TimerFired : fired after 1 hour ID:24 DecisionTaskScheduled : triggered by TimerFired ID:25 DecisionTaskStarted ID:26 DecisionTaskCompleted ID:27 WorkflowCompleted : completed by decision ``` ## Categories of non-deterministic errors ### Missing decision [Error message](https://github.com/cadence-workflow/cadence-go-client/blob/e5081b085b0333bac23f198e57959681e0aee987/internal/internal_task_handlers.go#L1206): ```go fmt.Errorf("nondeterministic workflow: missing replay decision for %s", util.HistoryEventToString(e)) ``` This means after replay code, the decision is scheduled less than history events. Using the previous history as an example, when the workflow is waiting at the one hour timer(event ID 22), if we delete the line of : ```go workflow.Sleep(time.Hour) ``` and restart worker, then it will run into this error. Because in the history, the workflow has a timer event that is supposed to fire in one hour. However, during replay, there is no logic to schedule that timer. ### Extra decision [Error message](https://github.com/cadence-workflow/cadence-go-client/blob/e5081b085b0333bac23f198e57959681e0aee987/internal/internal_task_handlers.go#L1210): ```go fmt.Errorf("nondeterministic workflow: extra replay decision for %s", util.DecisionToString(d)) ``` This is basically the opposite of the previous case, which means that during replay, Cadence generates more decisions than those in history events. Using the previous history as an example, when the workflow is waiting at the one hour timer(event ID 22), if we change the line of : ```go err = workflow.ExecuteActivity(ctx, activityB, a).Get(ctx, nil) ``` to ```go fb := workflow.ExecuteActivity(ctx, activityB, a) fc := workflow.ExecuteActivity(ctx, activityC, a) err = fb.Get(ctx,nil) if err != nil { return err } err = fc.Get(ctx,nil) if err != nil { return err } ``` And restart worker, then it will run into this error. Because in the history, the workflow has scheduled only activityB after the one minute timer, however, during replay, there are two activities scheduled in a decision( in parallel). ### Decision mismatch [Error message](https://github.com/cadence-workflow/cadence-go-client/blob/e5081b085b0333bac23f198e57959681e0aee987/internal/internal_task_handlers.go#L1214): ```go fmt.Errorf("nondeterministic workflow: history event is %s, replay decision is %s",util.HistoryEventToString(e), util.DecisionToString(d)) ``` This means after replay code, the decision scheduled is different than the one in history. Using the previous history as an example, when the workflow is waiting at the one hour timer(event ID 22), if we change the line of : ```go err = workflow.ExecuteActivity(ctx, activityB, a).Get(ctx, nil) ``` to ```go err = workflow.ExecuteActivity(ctx, activityC, a).Get(ctx, nil) ``` And restart worker, then it will run into this error. Because in the history, the workflow has scheduled activityB with input a, but during replay, it schedules activityC. ### Decision State Machine Panic [Error message](https://github.com/cadence-workflow/cadence-go-client/blob/e5081b085b0333bac23f198e57959681e0aee987/internal/internal_decision_state_machine.go#L693): ```go fmt.Sprintf("unknown decision %v, possible causes are nondeterministic workflow definition code"+" or incompatible change in the workflow definition", id) ``` This usually means workflow history is corrupted due to some bug. For example, the same activity can be scheduled and differentiated by activityID. So ActivityIDs for different activities are supposed to be unique in workflow history. If however we have an ActivityID collision, replay will run into this error. ## What can cause non-deterministic errors * Changing the order of executing activities/timer/childWorkflows/signalExternal/CancelRequest * Changing signature of activities * Changing duration of timer * Using time.Now() instead of workflow.Now() * Use golang builtin "go" to start goroutine in workflow, instead of “workflow.Go” * Use golang builtin channel instead of "workflow.Channel" for inter goroutines communication * time.Sleep() will not work, even though it doesn’t cause non-deterministic errors, but changing to workflow.Sleep() will. For those needs, see "How to address non-deterministic issues" in the next section. ## What will NOT cause non-deterministic errors * Activity input/output is a struct, changing the details of the struct * Adding/changing some code that will not affect history, for example, code about logs/metrics * Change retry policy of activity/local activity * ## Find the Non-Deterministic Code Workflow logic can be complicated and changes to workflow code can be non-trivial and non-isolated. In case you are not able to pinpoint the exact code change that introduces the workflow logic and non-deterministic error, you can download the workflow history with non-deterministic error and [replay it locally](https://github.com/cadence-workflow/cadence-go-client/blob/master/worker/worker.go#L96). This is [an example](https://github.com/cadence-workflow/cadence-samples/blob/03293b934579e0353e08e75c2f46a84a5a7b2df0/cmd/samples/recipes/helloworld/replay_test.go#L39) of using this utility. You can do this with different versions of your workflow code to see the difference in behavior. However, it could be hard if you have too many versions or your code is too complicated to debug. In this case, you can run replay in debug mode to help you to step into your workflow logic. To do this you first change [this code](https://github.com/cadence-workflow/cadence-go-client/blob/cc25a04f6f74c54ea9ae330741f63ae6df15f4df/internal/internal_event_handlers.go#L429) into ```go func (wc *workflowEnvironmentImpl) GenerateSequence() int32 { result := wc.counterID wc.counterID++ if wc.counterID == THE_ID_WITH_ERROR { fmt.Println("PAUSE HERE IN DEBUG MODE") } return result } ``` THE_ID_WITH_ERROR is the ActivityID/TimerID of activities/timers of decision runs into non-deterministic error during your replay with current code. When you pause the replay thread in the fmt.Println("PAUSE HERE IN DEBUG MODE") , trace back in the stack you will see the position of your workflow code that run into non-deterministic error. Currently this debugging experience is not very ideal. [This proposal will help](https://github.com/cadence-workflow/cadence/issues/2801). ## Automatic history replay and non-deterministic error detection We have ideas on how non-deterministic errors can be detected automatically and safe rollout can be achieved. See [this issue](https://github.com/cadence-workflow/cadence/issues/2547). # What to do with non-deterministic errors ## Related worker configs ### NonDeterministicWorkflowPolicy This is a worker option to decide what to do with non-deterministic workflows. Default is **NonDeterministicWorkflowPolicyBlockWorkflow** Another option is **NonDeterministicWorkflowPolicyFailWorkflow** which will fail the workflow immediately. You may want that if it is Okay to fail the trouble workflows for now(you can reopen later with reset) so that workers won’t keep on retrying the workflows. ### DisableStickyExecution It defaults to false which means all workflows will stay in stickyCache unless there is memory pressure which causes them to be evicted. This is the desired behavior in production as it saves replay efforts. However it could hide potential non-deterministic errors exactly because of this reason. When troubleshooting it might be helpful to let a small number of workers run with stickiness disabled, so that it always replays the whole history when execution decision tasks. ## GetVersion() & SideEffect() If you know some code change will cause non-deterministic errors, then use [workflow.GetVersion()](https://cadenceworkflow.io/docs/07_goclient/14_workflow_versioning) API to prevent it. This API will let the workflow that has finished the changing code to go with old code path, but let the workflows that hasn’t to go with new code path. For example of "missing decision", instead of simply deleting the code, we could change it to: ```go v := workflow.GetVersion(ctx, "delete_timer", workflow.DefaultVersion, 1) if v == workflow.DefaultVersion{ // run with old code workflow.Sleep(time.Hour) }else{ // run with new code // do nothing as we are deleting the timer } ``` Another similar API to prevent non-deterministic error is [workflow.SideEffect()](https://cadenceworkflow.io/docs/07_goclient/10_side_effect). ## BinaryChecksum & workflow.Now() What if Non-deterministic code change has been deployed without GetVersions()? Sometimes we may forget to use GetVersions(), or misuse it. This could be a serious problem because after deployment, we probably cannot rollback: because some workflows has run with new code but some workflows has stuck. Rollback will save the stuck workflows but also stuck other workflows. The best way is to use BinaryChecksum and Now() to let workflow diverge at the breaking changes. **workflow.GetInfo().[BinaryChecksum](https://github.com/cadence-workflow/cadence-go-client/issues/925)** is the checksum of the binary that made that decision. **workflow.[Now](https://github.com/cadence-workflow/cadence-go-client/issues/926)()** is timestamp that the decision is made. For better experience, you should integrate with binaryChecksum is in a format of "**Your GIT_REF**" by **worker.SetBinaryChecksum()** API. Use the "extra decision" as an example. After deploy the code change, then there are workflow W1 stuck because of extra decision, however workflow W2 has started the two in-parallel activities. If we rollback the code, W1 will be fixed but W2 will be stuck. We can fix it by changing the code to: ```go if *(workflow.GetInfo(ctx).BinaryChecksum)== "BINARY_BEFORE_BAD_DEPLOYMENT" || workflow.Now(ctx) < DeploymentStartTime { // run with old code err = workflow.ExecuteActivity(ctx, activityB, a).Get(ctx, nil) if err != nil { return err } }else{ // run with new code fb := workflow.ExecuteActivity(ctx, activityB, a) fc := workflow.ExecuteActivity(ctx, activityC, a) err = fb.Get(ctx,nil) if err != nil { return err } err = fc.Get(ctx,nil) if err != nil { return err } } ``` BINARY_BEFORE_BAD_DEPLOYMENT is the previous binary checksum, DeploymentStartTime that deployment start time. This will tell the workflows that has finished the decision with previous binary, or the decision is old enough that is not finished with new code, then it should go with the old code. We need DeploymentStartTime because W1 could be started by different binary checksums. ## [Reset workflow](https://cadenceworkflow.io/docs/08_cli#restart-reset-workflow) The last solution is to reset the workflows. A process in real OS can only move forward but never go back to previous state. However, a Cadence workflow can go back to previous state since we have stored the history as a list. Internally reset a workflow will use history as a tree. It takes a history as base, and fork a new branch from it. So that you will reset many times without losing the history(until history is deleted after retention). Reset will start a new run with new runID like continueAsNew. After forking from the base, reset will also collect all the signals along the chain of continueAsNew from the base history. This is an important feature as we can consider signals are external events that we don’t want to lose. However, reset will schedule and execute some activities that has done before. So after reset, you may see some activities are re-executed. Same applies for timer/ChildWorkflows. If you don’t want to re-execute, you can emit a signal to self to identify that this activity/timer/childWorkflow is done -- since reset will collect signals after resetting point. Note that reset with [child workflows](https://github.com/cadence-workflow/cadence/issues/2951) is not fully supported yet. ## Primitive Reset CLI Command This reset command is for resetting one workflow. We may use this command to manually resetting particular workflow for experiment or mitigation. It takes workflowID/runID for base history. It takes either eventID or eventType for forking point. EventID has to be decisionCloseEventID as we designed reset must be done by decision boundary. ResetType support these: LastDecisionCompleted, LastContinuedAsNew, BadBinary ,FirstDecisionCompleted. If the workflowID has an open run, you need to be aware of [this race condition](https://github.com/cadence-workflow/cadence/issues/2930) when resetting it. ## Batch Reset CLI Command Reset-batch is for resetting a list of workflows. Usually reset-batch is more useful/powerful. Reset-batch command has many arguments, most of them are of two types: to decide what workflows to reset and where to reset to: 1. What workflows to reset * Input file or query: only one of them is needed. Query will be same as List command(advanced functionality based on ElasticSearch). * Exclude file- for filtering out some workflows that we don’t want to reset * SkipIfCurrentOpen * SkipIfBaseNotCurrent * NonDeterministicOnly 2. Where to reset * ResetType * ResetBadBinaryChecksum Other arguments: Parallism will decide how fast you want to reset. To be safe, you may use DryRun option for only printing some logs before actually executing it. For example, in the case of "Decision State Machine Panic", we might have to reset the workflows by command: *$nohup cadence --do samples-domain --env prod wf reset-batch --reason "fix outage" --query “WorkflowType=’SampleWorkflow’ AND CloseTime=missing” --dry-run --reset-type --non_deterministic_only --skip_base_not_current &> reset.log &* For reset type, you may try LastDecisionCompleted. Then try FirstDecisionCompleted. We should also provide [firstPanicDecision](https://github.com/cadence-workflow/cadence/issues/2952) resetType . ## AutoReset Workflow Cadence also provides a command to reset all progress made by any binary given a binaryChecksum. The way it works is to store the first decision completed ID as an **auto-reset point** for any binaryChecksum. Then when a customer mark a binary checksum is bad, the badBinaryChecksum will be stored in domainConfig. Whenever an open workflow make any progress, it will reset the workflow to the auto-reset point. There are some limitations: 1. There are only a limited number(20 by default) of auto-reset points for each workflow. Beyond that the auto-reset points will be rotated. 2. It only applies to open workflows. 3. It only reset when the open workflow make a decision respond 4. [It could be much improved by this proposal, ](https://github.com/cadence-workflow/cadence/issues/2810) However, you can use reset batch command to achieve the same to both open/closed workflows and without waiting for making decision respond. ================================================ FILE: docs/persistence.md ================================================ # Overview Cadence has a well defined API interface at the persistence layer. Any database that supports multi-row transactions on a single shard or partition can be made to work with cadence. This includes cassandra, dynamoDB, auroraDB, MySQL, Postgres and may others. There are currently three supported database implementations at the persistence layer - cassandra and MySQL/Postgres. This doc shows how to run cadence with cassandra and MySQL(Postgres is mostly the same). It also describes the steps involved in adding support for a new database at the persistence layer. # Getting started on mac ## Cassandra ### Start cassandra ``` brew install cassandra brew services start cassandra ``` ### Install cadence schema ``` cd $GOPATH/github.com/uber/cadence make install-schema ``` > NOTE: See [CONTRIBUTING](/CONTRIBUTING.md) for prerequisite of make command. > ### Start cadence server ``` cd $GOPATH/github.com/uber/cadence ./cadence-server start --services=frontend,matching,history,worker ``` ## MySQL ### Start MySQL server ``` brew install mysql brew services start mysql ``` ### Install cadence schema ``` cd $GOPATH/github.com/uber/cadence make install-schema-mysql ``` When run tests and CLI command locally, Cadence by default uses a user `uber` with password `uber`, with privileges of creating databases. You can use the following command to create user(role) and grant access. In the mysql shell: ``` > CREATE USER 'uber'@'%' IDENTIFIED BY 'uber'; > GRANT ALL PRIVILEGES ON *.* TO 'uber'@'%'; ``` ### Start cadence server ``` cd $GOPATH/github.com/uber/cadence cp config/development_mysql.yaml config/development.yaml ./cadence-server start --services=frontend,matching,history,worker ``` ## PostgresQL ### Start PostgresQL server ``` brew install postgres brew services start postgres ``` When run tests and CLI command locally, Cadence by default uses a superuser `postgres` with password `cadence`. You can use the following command to create user(role) and grant access: ``` $psql postgres postgres=# CREATE USER postgres WITH PASSWORD 'cadence'; CREATE ROLE postgres=# ALTER USER postgres WITH SUPERUSER; ALTER ROLE ``` ### Install cadence schema ``` cd $GOPATH/github.com/uber/cadence make install-schema-postgres ``` ### Start cadence server ``` cd $GOPATH/github.com/uber/cadence cp config/development_postgres.yaml config/development.yaml ./cadence-server start --services=frontend,matching,history,worker ``` # Configuration ## Common to all persistence implementations There are two major sub-subsystems within cadence that need persistence - cadence-core and visibility. cadence-core is the workflow engine that uses persistence to store state tied to domains, workflows, workflow histories, task lists etc. cadence-core powers almost all of the cadence APIs. cadence-core could be further broken down into multiple subs-systems that have slightly different persistence workload characteristics. But for the purpose of simplicity, we don't expose these sub-systems today but this may change in future. Visibility is the sub-system that powers workflow search. This includes APIs such as ListOpenWorkflows and ListClosedWorkflows. Today, it is possible to run a cadence server with cadence-core backed by one database and cadence-visibility backed by another kind of database.To get the full feature set of visibility, the recommendation is to use elastic search as the persistence layer. However, it is also possible to run visibility with limited feature set against Cassandra or MySQL today. The top level persistence configuration looks like the following: ``` persistence: defaultStore: datastore1 -- Name of the datastore that powers cadence-core visibilityStore: datastore2 -- Name of the datastore that powers cadence-visibility numHistoryShards: 1024 -- Number of cadence history shards, this limits the scalability of single cadence cluster datastores: -- Map of datastore-name -> datastore connection params datastore1: nosql: ... datastore2: sql: ... ``` ## Note on numHistoryShards Internally, cadence uses shards to distribute workflow ownership across different hosts. Shards are necessary for the horizontal scalability of cadence service. The number of shards for a cadence cluster is picked at cluster provisioning time and cannot be changed after that. One way to think about shards is the following - if you have a cluster with N shards, then cadence cluster can be of size 1 to N. But beyond N, you won't be able to add more hosts to scale. In future, we may add support to dynamically split shards but this is not supported as of today. Greater the number of shards, greater the concurrency and horizontal scalability. ## Cassandra ``` persistence: ... datastores: datastore1: nosql: pluginName: "cassandra" hosts: "127.0.0.1,127.0.0.2" -- CSV of cassandra hosts to connect to User: "user-name" Password: "password" keyspace: "cadence" -- Name of the cassandra keyspace datacenter: "us-east-1a" -- Cassandra datacenter filter to limit queries to a single dc (optional) maxConns: 2 -- Number of tcp conns to cassandra server (single sub-system on one host) (optional) ``` ## MySQL/Postgres The default isolation level for MySQL/Postgres is READ-COMMITTED. Note that for MySQL 5.6 and below only, the isolation level needs to be specified explicitly in the config via connectAttributes. ``` persistence: ... datastores: datastore1: sql: pluginName: "mysql" -- name of the go sql plugin databaseName: "cadence" -- name of the database to connect to connectAddr: "127.0.0.1:3306" -- connection address, could be ip address or domain socket connectProtocol: "tcp" -- connection protocol, tcp or anything that SQL Data Source Name accepts user: "uber" password: "uber" maxConns: 20 -- max number of connections to sql server from one host (optional) maxIdleConns: 20 -- max number of idle conns to sql server from one host (optional) maxConnLifetime: "1h" -- max connection lifetime before it is discarded (optional) connectAttributes: -- custom dsn attributes, map of key-value pairs tx_isolation: "READ-COMMITTED" -- required only for mysql 5.6 and below, optional otherwise ``` ## Multiple SQL(MySQL/Postgres) databases To run Cadence clusters in a much larger scale using SQL database, multiple databases can be used as a sharded SQL database cluster. Set `useMultipleDatabases` to `true` and specify all databases' user/password/address using `multipleDatabasesConfig`: ```yaml persistence: ... datastores: datastore1: sql: pluginName: "mysql" -- name of the go sql plugin connectProtocol: "tcp" -- connection protocol, tcp or anything that SQL Data Source Name accepts maxConnLifetime: "1h" -- max connection lifetime before it is discarded (optional) useMultipleDatabases: true -- this enabled the multiple SQL databases as sharded SQL cluster nShards: 4 -- the number of shards -- in this mode, it needs to be greater than one and equalt to the length of multipleDatabasesConfig multipleDatabasesConfig: -- each entry will represent a shard of the cluster - user: "root" password: "cadence" connectAddr: "127.0.0.1:3306" databaseName: "cadence0" - user: "root" password: "cadence" connectAddr: "127.0.0.1:3306" databaseName: "cadence1" - user: "root" password: "cadence" connectAddr: "127.0.0.1:3306" databaseName: "cadence2" - user: "root" password: "cadence" connectAddr: "127.0.0.1:3306" databaseName: "cadence3" ``` How Cadence implement the sharding: * Workflow execution and historyShard records are sharded based on historyShardID(which is calculated `historyShardID =hash(workflowID) % numHistoryShards` ), `dbShardID = historyShardID % numDBShards` * Workflow History is sharded based on history treeID(a treeID usually is the runID unless it has reset. In case of reset, it will share the same tree as the base run). In that case, `dbShardID = hash(treeID) % numDBShards` * Workflow tasks(for workflow/activity workers) is sharded based on domainID + tasklistName. `dbShardID = hash(domainID + tasklistName ) % numDBShards` * Workflow visibility is sharded based on domainID like we said above. `dbShardID = hash(domainID ) % numDBShards` * However, due to potential scalability issue, Cadence requires advanced visibility to run with multiple SQL database mode. * Internal domain records is using single shard, it’s only writing when register/update domain, and read is protected by domainCache `dbShardID = DefaultShardID(0)` * Internal queue records is using single shard. Similarly, the read/write is low enough that it’s okay to not sharded. `dbShardID = DefaultShardID(0)` # Adding support for new database ## For SQL Database As there are many shared concepts and functionalities in SQL database, we abstracted those common code so that is much easier to implement persistence interfaces with any SQL database. It requires your database supports SQL operations like explicit transaction(with pessimistic locking) This interface is tied to a specific schema i.e. the way data is laid out across tables and the table names themselves are fixed. However, you get the flexibility wrt how you store the data within a table (i.e. column names and types are not fixed). The API interface can be found [here](https://github.com/cadence-workflow/cadence/blob/master/common/persistence/sql/plugins/interfaces.go). It's basically a CRUD API for every table in the schema. A sample schema definition for mysql that uses this interface can be found [here](https://github.com/cadence-workflow/cadence/blob/master/schema/mysql/v8/cadence/schema.sql) Any database that supports this interface can be plugged in with cadence server. We have implemented Postgres within the repo, and also here is [**an example**](https://github.com/longquanzheng/cadence-extensions/tree/master/cadence-sqlite) to implement any database externally. ## For other Non-SQL Database For databases that don't support SQL operations like explicit transaction(with pessimistic locking), Cadence requires at least supporting: 1. Multi-row single shard conditional write(also called LightWeight transaction by Cassandra terminology) 2. Strong consistency Read/Write operations This NoSQL persistence API interface can be found [here](https://github.com/cadence-workflow/cadence/blob/master/common/persistence/nosql/nosqlplugin/interfaces.go). Currently this is only implemented with Cassandra. DynamoDB and MongoDB are in progress. ================================================ FILE: docs/roadmap.md ================================================ # Cadence Roadmap The following is a high-level quarterly roadmap of the [Cadence](https://cadenceworkflow.io/) project. ## Q3 2019 * [Resource-Specific Tasklist (a.k.a session)](https://github.com/cadence-workflow/cadence/blob/master/docs/design/1533-host-specific-tasklist.md) * [Visibility on Elastic Search](https://github.com/cadence-workflow/cadence/blob/master/docs/visibility-on-elasticsearch.md) * Scalable tasklist * MySQL support ## Q4 2019 * [Multi-DC support for Cadence replication](https://github.com/cadence-workflow/cadence/blob/master/docs/design/2290-cadence-ndc.md) * Workflow history archival * Workflow visibility archival * [Synchronous Request Reply](https://github.com/cadence-workflow/cadence/blob/master/docs/design/2215-synchronous-request-reply.md) * Postgres SQL support ## Q1 2020 * Service availability and reliability improvements * [Domain level AuthN and AuthZ support](https://github.com/cadence-workflow/cadence/issues/2833) * Graceful domain failover design * UI bug fixes and performance improvements ## Q2 2020 * Kafka deprecation for Cadence replication * Graceful domain failover * Multi-tenancy task prioritization and resource isolation * UI and CLI feature parity ## Q3 2020 and beyond * GRPC support * Parent-child affinity * Task priority * Reset 2.0 (more flexibility and handle child) * Safe workflow code rollout ================================================ FILE: docs/scalable_tasklist.md ================================================ # Overview Matching is a sharded service which is sharded by tasklist, which means that all requests to a certain tasklist has to be processed by one Matching host. To avoid a single Matching host becoming the bottleneck and make the system scalable, scalable tasklist was introduced to allow a tasklist to be partitioned so that the requests can be processed by multiple Matching hosts. The partitions are transparent to customers, so when a request to a scalable tasklist arrives at Cadence server, it has to select a partition for the request. We'll describe the architecture of scalable tasklist and the selection algorithm we use. # Requirements There are 2 requirements for this feature: 1. Ensure a fair distribution of the requests among all partitions 2. Maximize the utilization of customer pollers The first requirement is straightforward, which is the reason for introducing scalable tasklist. The second requirement is to make sure pollers are not waiting at a partition without any task because the default polling timeout is 60s. # Architecture ![image alt text](images/scalable-tasklist-forwarding.png) The partitions are organized in a tree-structure. The number of child nodes is configurable, but in the diagram we just show a scalable tasklist with 6 partitions organized in a binary tree. When a partition receives a request, it can forward the request to its parent partition recursively until the root partition being reached. # Configuration The number of partitions of a tasklist is configured by 2 dynamicconfigs: 1. [matching.numTasklistReadPartitions](https://github.com/cadence-workflow/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3350) 2. [matching.numTasklistWritePartitions](https://github.com/cadence-workflow/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3344) We're migrating this configuration from dynamicconfig to database. The following dynamicconfig is used to control where to read the number of partitions from: - [matching.enableGetNumberOfPartitionsFromCache](https://github.com/cadence-workflow/cadence/blob/v1.2.15-prerelease02/common/dynamicconfig/constants.go#L4008) To update the number of partitions, use the following CLI command: ``` cadence admin tasklist update-partition -h ``` To get the number of partitions from database, use the following CLI command: ``` cadence admin tasklist describe -h ``` The tree-structure and forwarding mechanism is configured by these dynamicconfigs: 1. [matching.forwarderMaxChildrenPerNode](https://github.com/cadence-workflow/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3374) 2. [matching.forwarderMaxOutstandingPolls](https://github.com/cadence-workflow/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3356) 3. [matching.forwarderMaxOutstandingTasks](https://github.com/cadence-workflow/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3362) 4. [matching.forwarderMaxRatePerSecond](https://github.com/cadence-workflow/cadence/blob/v1.2.13/common/dynamicconfig/constants.go#L3368) # Selection Algorithms The selection algorithms are implemented as a LoadBalancer in [client/matching package](https://github.com/cadence-workflow/cadence/blob/v1.2.13/client/matching/loadbalancer.go#L37). ## Random Selection This is the first algorithm and it's been widely adopted in production. It's completely stateless and uses a shared nothing architecture. The probabilistic model of discrete uniform distribution guarantees the fairness of the distribution of requests. And the utilization is improved by the tree-structure. For example, as shown in the diagram, if a task is produced to partition-5, but a poller is assigned to partition-3, we don't want the poller to wait at partition-3 for 60s and retry the poll request. And the retry has a 5/6 probability of not hitting partition-5. With the tree-structure and forwarding mechanism, the poller request and task are forwarded to root partition. So an idle poller waiting at partition-3 is utilized in this case. ## Round-Robin Selection This algorithm also ensure a fair distribution of requests even with a small number of requests. It also uses a shared nothing architecture but it's soft stateful because it uses a cache to remember the previous selected partition. ## Weighted Selection The algorithm selects a partition based on the backlog size of each partition if any partition has a backlog size larger than 100. If all partitions don't have a large backlog, it falls back to round-robin selection. It can be proven mathematically that it's better than the previous 2 algorithms. For a tasklist with $N$ partitions, assuming the number of tasks in each partition is represented by $L_i$, the utilization fraction of partition $i$ is: $$U_i = \frac{L_i}{\Sigma_{i=0}^{n-1}L_i}, \forall i \in \{0,1,..,N-1\}$$ According to Little's Law, in a stable queue system, the average number of tasks in a queue $$L = \lambda W$$ where $\lambda$ is the average arrival rate and $W$ is the average wait time a task spends in the queue. A queue is stable if the utilization factor $\rho$ is less than 1, where $$\rho = \frac{\lambda}{X\mu}$$ Here $\lambda$ is the arrival rate of tasks, $\mu$ is the service rate of a single poller, and $X$ is the number of pollers. However, there might be some time that $\rho$ is greater than 1, in which case, tasks are not dropped but persisted into the database. Assuming the number of tasks in the database for partition $i$ is $B_{i}$, the arrival rate is $\lambda_{i}$, the utilization fraction is: $$U_i = \frac{B_i+\lambda_iW}{\Sigma_{i=0}^{n-1}B_i+W\Sigma_{i=0}^{n-1}\lambda_i}, \forall i \in \{0,1,..,N-1\}$$ To maximize the utilization of pollers, the probability of partition $i$ being selected for a poller should be: $$P_i = U_{i}, \forall i \in \{0,1,..,N-1\}$$ Assuming $B_i=0, \forall i \in \{0,1,..,N-1\}$ and $\lambda_i=\frac{\lambda}{N}, \forall i \in \{0,1,..,N-1\}$, $$P_i = \frac{1}{N}, \forall i \in \{0,1,..,N-1\}$$ It means that when the queue system is stable and the producer traffic is evenly distributed among all partitions, selecting partition based on number of tasks is equivalent to random and round-robin selection. Here λ is the producer QPS and W is the average matching latency. In production, we observed that the average matching latency is about 10ms, and most tasklists only have a few hundred producer QPS of traffic. So for most stable partitions, $L$ is less than 10. So if no partition has a very large backlog size, it's safe to fallback to random selection or round-robin selection algorithm. ================================================ FILE: docs/setup/MYSQL_SETUP.md ================================================ # Setup local MySQL with Docker This document describes how to install MySQL 8.0 locally with Docker. >Note: Install the docker on your machine before installing the MySQL. * Make sure any MySQL containers are terminated and removed ``` docker ps -a docker kill && docker rm # remove any MySQL containers. ``` * Fetch docker image (version 8.0 is what Travis runs so its what you will want locally) ``` docker pull mysql/mysql-server:8.0 ``` * Run docker container (note the port mapping so that 3306 is exposed locally) ``` docker run -p 3306:3306 --name=mysql1 -d mysql/mysql-server:8.0 ``` * When docker starts up the root MySQL user will have an auto generated password. You need to get that password to log into the container ``` docker logs mysql1 2>&1 | grep GENERATED # The result looks like: [Entrypoint] GENERATED ROOT PASSWORD: iHqEvRYm6UP#YN$es;YnV3m(oJ ``` * Log into the container (when prompted for password use the password gotten from last step). ``` docker exec -it mysql1 mysql -uroot -p ``` * Before any SQL operations can be performed you must reset the root user's password (use anything you like in replace of root_password). ``` SET PASSWORD = PASSWORD('root_password'); ``` * Now create the user that local MySQL tests will use. Also grant all privileges to user. ``` CREATE USER 'uber'@'%' IDENTIFIED BY 'uber'; GRANT ALL PRIVILEGES ON *.* TO 'uber'@'%'; ``` ================================================ FILE: docs/setup/POSTGRES_SETUP.md ================================================ # Setup local Postgres with Docker This document describes how to install latest Postgre locally with Docker. >Note: Install the docker on your machine before installing the MySQL. * Make sure any MySQL containers are terminated and removed ``` docker ps -a docker kill && docker rm # remove any Postgres containers. ``` * Fetch docker image ``` docker pull postgres ``` * Run docker container (note the port mapping so that 5432 is exposed locally) ``` mkdir -p ~/docker/volumes/postgres docker run --rm --name pg-docker -e POSTGRES_PASSWORD=cadence -d -p 5432:5432 -v ~/docker/volumes/postgres:/var/lib/postgresql/data postgres ``` * Log into the container (when prompted for password use the password gotten from last step). ``` psql -h localhost -U postgres -d cadence ``` ================================================ FILE: docs/toc.md ================================================ # Table of Contents - [Persistence](persistence.md) - [Visibility on ElasticSearch](visibility-on-elasticsearch.md) ================================================ FILE: docs/visibility-on-elasticsearch.md ================================================ # Overview Cadence visibility APIs allow users to list open or closed workflows with filters such as WorkflowType or WorkflowID. With Cassandra, there are issues around scalability and performance, for example: - list large amount of workflows may kill Cassandra node. - data is partitioned by domain, which means writing large amount of workflow to one domain will cause Cassandra nodes hotspots. - query with filter is slow for large data. (With MySQL, there might be similar issues but not tested) In addition, Cadence want to support users to perform flexible query with multiple filters on even custom info. That's why Cadence add support for enhanced visibility features on top of ElasticSearch (Note as ES below), which includes: - new visibility APIs to List/Scan/Count workflows with SQL like query - search attributes feature to support users provide custom info # Quick Start ## Local Cadence Docker Setup 1. Increase docker memory to higher 6GB. Docker -> Preference -> advanced -> memory limit 2. Get docker compose file. Run `curl -O https://raw.githubusercontent.com/uber/cadence/master/docker/docker-compose-es.yml` 3. Start cadence docker which contains Kafka, Zookeeper and ElasticSearch. Run `docker compose -f docker-compose-es.yml up` 4. From docker output log, make sure ES and cadence started correctly. If encounter disk space not enough, try `docker system prune -a --volumes` 5. Register local domain and start using it. `cadence --do samples-domain d re` ## CLI Search Attributes Support Make sure Cadence CLI version is 0.6.4+ ### new list API examples ``` cadence --do samples-domain wf list -q 'WorkflowType = "main.Workflow" and (WorkflowID = "1645a588-4772-4dab-b276-5f9db108b3a8" or RunID = "be66519b-5f09-40cd-b2e8-20e4106244dc")' cadence --do samples-domain wf list -q 'WorkflowType = "main.Workflow" StartTime > "2019-06-07T16:46:34-08:00" and CloseTime = missing' ``` To list only open workflows, add `CloseTime = missing` to the end of query. ### start workflow with search attributes ``` cadence --do samples-domain workflow start --tl helloWorldGroup --wt main.Workflow --et 60 --dt 10 -i '"input arg"' -search_attr_key 'CustomIntField | CustomKeywordField | CustomStringField | CustomBoolField | CustomDatetimeField' -search_attr_value '5 | keyword1 | my test | true | 2019-06-07T16:16:36-08:00' ``` Note: start workflow with search attributes but without ES will succeed as normal, but will not be searchable and will not be shown in list result. ### search workflow with new list API ``` cadence --do samples-domain wf list -q '(CustomKeywordField = "keyword1" and CustomIntField >= 5) or CustomKeywordField = "keyword2"' -psa cadence --do samples-domain wf list -q 'CustomKeywordField in ("keyword2", "keyword1") and CustomIntField >= 5 and CloseTime between "2018-06-07T16:16:36-08:00" and "2019-06-07T16:46:34-08:00" order by CustomDatetimeField desc' -psa ``` (Search attributes can be updated inside workflow, see example [here](https://github.com/cadence-workflow/cadence-samples/tree/master/cmd/samples/recipes/searchattributes). # Details ## Dependencies - Zookeeper - for Kafka to start - Kafka - message queue for visibility data - ElasticSearch v6+ - for data search (early ES version may not support some queries) ## Configuration ``` persistence: ... advancedVisibilityStore: es-visibility ... datastore: es-visibility: elasticsearch: url: scheme: "http" host: "127.0.0.1:9200" indices: visibility: cadence-visibility-dev ``` This part is used to config advanced visibility store to ElasticSearch. - `url` is for Cadence to discover ES - `indices/visibility` is ElasticSearch index name for the deployment. Optional TLS Support can be enabled by setting the TLS config as follows: ```yaml elasticsearch: url: scheme: "https" host: "127.0.0.1:9200" indices: visibility: cadence-visibility-dev tls: enabled: true caFile: /secrets/cadence/elasticsearch_cert.pem enableHostVerification: true serverName: myServerName certFile: /secrets/cadence/certfile.crt keyFile: /secrets/cadence/keyfile.key sslmode: false ``` Also need to add a kafka topic to visibility, as shown below. ``` kafka: ... applications: visibility: topic: cadence-visibility-dev dlq-topic: cadence-visibility-dev-dlq ... ``` There are dynamic configs to control ElasticSearch visibility features: - `system.writeVisibilityStoreName` is an string property to control how to write visibility to data store. `"off"` means do not write to advanced data store, same as db `"es"` means only write to advanced data store (in this case is es), `"db,es"` means write to both DB (Cassandra or MySQL) and advanced data store - `system.readVisibilityStoreName` is a string property to control the read source for Cadence List APIs. ================================================ FILE: environment/env.go ================================================ // Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package environment import ( "fmt" "os" "strconv" ) const ( // Localhost default localhost Localhost = "127.0.0.1" // CassandraSeeds env CassandraSeeds = "CASSANDRA_SEEDS" // CassandraPort env CassandraPort = "CASSANDRA_DB_PORT" // CassandraDefaultPort Cassandra default port CassandraDefaultPort = "9042" // CassandraUsername env CassandraUsername = "CASSANDRA_DB_USERNAME" // CassandraDefaultUsername Cassandra default username CassandraDefaultUsername = "" // CassandraPassword env CassandraPassword = "CASSANDRA_DB_PASSWORD" // CassandraDefaultPassword Cassandra default password CassandraDefaultPassword = "" // CassandraAllowedAuthenticators env CassandraAllowedAuthenticators = "CASSANDRA_DB_ALLOWED_AUTHENTICATORS" // CassandraProtoVersion env CassandraProtoVersion = "CASSANDRA_PROTO_VERSION" // CassandraDefaultProtoVersion Cassandra default protocol version CassandraDefaultProtoVersion = "4" // CassandraDefaultProtoVersionInteger Cassandra default protocol version int version CassandraDefaultProtoVersionInteger = 4 // MySQLSeeds env MySQLSeeds = "MYSQL_SEEDS" // MySQLPort env MySQLPort = "MYSQL_PORT" // MySQLDefaultPort is MySQL default port MySQLDefaultPort = "3306" // MySQLUser env MySQLUser = "MYSQL_USER" // MySQLDefaultUser is default user MySQLDefaultUser = "root" // MySQLPassword env MySQLPassword = "MYSQL_PASSWORD" // MySQLDefaultPassword is default password MySQLDefaultPassword = "cadence" // MongoSeeds env MongoSeeds = "MONGO_SEEDS" // MongoPort env MongoPort = "MONGO_PORT" // MongoDefaultPort is Mongo default port MongoDefaultPort = "27017" // KafkaSeeds env KafkaSeeds = "KAFKA_SEEDS" // KafkaPort env KafkaPort = "KAFKA_PORT" // KafkaDefaultPort Kafka default port KafkaDefaultPort = "9092" // ESSeeds env ESSeeds = "ES_SEEDS" // ESPort env ESPort = "ES_PORT" // ESDefaultPort ES default port ESDefaultPort = "9200" // ESVersion is the ElasticSearch version ESVersion = "ES_VERSION" // ESDefaultVersion is the default version ESDefaultVersion = "v6" // PostgresSeeds env PostgresSeeds = "POSTGRES_SEEDS" // PostgresPort env PostgresPort = "POSTGRES_PORT" // PostgresDefaultPort Postgres default port PostgresDefaultPort = "5432" // CLITransportProtocol env CLITransportProtocol = "CADENCE_CLI_TRANSPORT_PROTOCOL" // DefaultCLITransportProtocol CLI default channel DefaultCLITransportProtocol = "tchannel" ) var envDefaults = map[string]string{ CassandraSeeds: Localhost, CassandraPort: CassandraDefaultPort, CassandraProtoVersion: CassandraDefaultProtoVersion, MySQLSeeds: Localhost, MySQLPort: MySQLDefaultPort, PostgresSeeds: Localhost, PostgresPort: PostgresDefaultPort, KafkaSeeds: Localhost, KafkaPort: KafkaDefaultPort, ESSeeds: Localhost, ESPort: ESDefaultPort, CLITransportProtocol: DefaultCLITransportProtocol, } // SetupEnv setup the necessary env func SetupEnv() error { for k, v := range envDefaults { if os.Getenv(k) == "" { if err := setEnv(k, v); err != nil { return err } } } return nil } // GetCassandraAddress return the cassandra address func GetCassandraAddress() string { if addr := os.Getenv(CassandraSeeds); addr != "" { return addr } return envDefaults[CassandraSeeds] } // GetCassandraPort return the cassandra port func GetCassandraPort() (int, error) { port := os.Getenv(CassandraPort) if port == "" { port = envDefaults[CassandraPort] } return strconv.Atoi(port) } // GetCassandraUsername return the cassandra username func GetCassandraUsername() string { user := os.Getenv(CassandraUsername) if user != "" { return user } return CassandraDefaultUsername } // GetCassandraPassword return the cassandra password func GetCassandraPassword() string { pass := os.Getenv(CassandraPassword) if pass != "" { return pass } return CassandraDefaultPassword } // GetCassandraAllowedAuthenticators return the cassandra allowed authenticators func GetCassandraAllowedAuthenticators() []string { var authenticators []string configuredAuthenticators := os.Getenv(CassandraAllowedAuthenticators) if configuredAuthenticators == "" { return authenticators } authenticators = append(authenticators, configuredAuthenticators) return authenticators } // GetCassandraProtoVersion return the cassandra protocol version func GetCassandraProtoVersion() (int, error) { protoVersion := os.Getenv(CassandraProtoVersion) if protoVersion == "" { protoVersion = envDefaults[CassandraProtoVersion] } return strconv.Atoi(protoVersion) } // GetMySQLAddress return the MySQL address func GetMySQLAddress() string { addr := os.Getenv(MySQLSeeds) if addr == "" { addr = Localhost } return addr } // GetMySQLPort return the MySQL port func GetMySQLPort() (int, error) { port := os.Getenv(MySQLPort) if port == "" { port = MySQLDefaultPort } return strconv.Atoi(port) } // GetMySQLUser return the MySQL user func GetMySQLUser() string { user := os.Getenv(MySQLUser) if user == "" { user = MySQLDefaultUser } return user } // GetMySQLPassword return the MySQL password func GetMySQLPassword() string { pw := os.Getenv(MySQLPassword) if pw == "" { pw = MySQLDefaultPassword } return pw } // GetPostgresAddress return the Postgres address func GetPostgresAddress() string { addr := os.Getenv(PostgresSeeds) if addr == "" { addr = Localhost } return addr } // GetPostgresPort return the Postgres port func GetPostgresPort() (int, error) { port := os.Getenv(PostgresPort) if port == "" { port = PostgresDefaultPort } return strconv.Atoi(port) } // GetESVersion return the ElasticSearch version func GetESVersion() string { version := os.Getenv(ESVersion) if version == "" { version = ESDefaultVersion } return version } // GetMongoAddress return the MySQL address func GetMongoAddress() string { addr := os.Getenv(MongoSeeds) if addr == "" { addr = Localhost } return addr } // GetMongoPort return the MySQL port func GetMongoPort() (int, error) { port := os.Getenv(MongoPort) if port == "" { port = MongoDefaultPort } return strconv.Atoi(port) } func setEnv(key string, val string) error { if err := os.Setenv(key, val); err != nil { return fmt.Errorf("setting env %q: %w", key, err) } return nil } ================================================ FILE: environment/env_test.go ================================================ // Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package environment import ( "os" "strconv" "testing" "github.com/google/go-cmp/cmp" ) func TestSetupEnv(t *testing.T) { if err := SetupEnv(); err != nil { t.Fatalf("SetupEnv() failed, err: %v", err) } } func TestGetCassandraAddress(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantVal any }{ { name: "default", envVarKey: CassandraSeeds, envVarVal: "", wantVal: envDefaults[CassandraSeeds], }, { name: "custom address", envVarKey: CassandraSeeds, envVarVal: "casseed", wantVal: "casseed", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal := GetCassandraAddress() if gotVal != tt.wantVal { t.Fatalf("GetCassandraAddress() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetCassandraPort(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantErr bool wantVal any }{ { name: "default", envVarKey: CassandraPort, envVarVal: "", wantErr: false, wantVal: mustConvertInt(t, envDefaults[CassandraPort]), }, { name: "non-int port value", envVarKey: CassandraPort, envVarVal: "xyz", wantErr: true, }, { name: "custom port value", envVarKey: CassandraPort, envVarVal: "1001", wantVal: 1001, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal, err := GetCassandraPort() if (err != nil) != tt.wantErr { t.Fatalf("GetCassandraPort() error = %v, wantErr %v", err, tt.wantErr) } if err != nil || tt.wantErr { return } if gotVal != tt.wantVal { t.Fatalf("GetCassandraPort() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetCassandraUsername(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantVal any }{ { name: "default", envVarKey: CassandraUsername, envVarVal: "", wantVal: CassandraDefaultUsername, }, { name: "custom user name", envVarKey: CassandraUsername, envVarVal: "user1", wantVal: "user1", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal := GetCassandraUsername() if gotVal != tt.wantVal { t.Fatalf("GetCassandraUsername() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetCassandraPassword(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantVal any }{ { name: "default", envVarKey: CassandraPassword, envVarVal: "", wantVal: CassandraDefaultUsername, }, { name: "custom pw", envVarKey: CassandraPassword, envVarVal: "xyz123", wantVal: "xyz123", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal := GetCassandraPassword() if gotVal != tt.wantVal { t.Fatalf("GetCassandraPassword() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetCassandraAllowedAuthenticators(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantVal []string }{ { name: "default", envVarKey: CassandraAllowedAuthenticators, envVarVal: "", wantVal: nil, }, { name: "custom authenticators", envVarKey: CassandraAllowedAuthenticators, envVarVal: "auth1", wantVal: []string{"auth1"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal := GetCassandraAllowedAuthenticators() if diff := cmp.Diff(gotVal, tt.wantVal); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } }) } } func TestGetCassandraProtoVersion(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantErr bool wantVal any }{ { name: "default", envVarKey: CassandraProtoVersion, envVarVal: "", wantErr: false, wantVal: mustConvertInt(t, envDefaults[CassandraProtoVersion]), }, { name: "non-int proto version", envVarKey: CassandraProtoVersion, envVarVal: "xyz", wantErr: true, }, { name: "custom proto version", envVarKey: CassandraProtoVersion, envVarVal: "35", wantVal: 35, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal, err := GetCassandraProtoVersion() if (err != nil) != tt.wantErr { t.Fatalf("GetCassandraProtoVersion() error = %v, wantErr %v", err, tt.wantErr) } if err != nil || tt.wantErr { return } if gotVal != tt.wantVal { t.Fatalf("GetCassandraProtoVersion() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetMySQLAddress(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantVal any }{ { name: "default", envVarKey: MySQLSeeds, envVarVal: "", wantVal: Localhost, }, { name: "custom", envVarKey: MySQLSeeds, envVarVal: "mysqlseed", wantVal: "mysqlseed", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal := GetMySQLAddress() if gotVal != tt.wantVal { t.Fatalf("GetMySQLAddress() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetMySQLPort(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantErr bool wantVal any }{ { name: "default", envVarKey: MySQLPort, envVarVal: "", wantErr: false, wantVal: mustConvertInt(t, MySQLDefaultPort), }, { name: "non-int port", envVarKey: MySQLPort, envVarVal: "xyz", wantErr: true, }, { name: "custom port", envVarKey: MySQLPort, envVarVal: "8787", wantVal: 8787, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal, err := GetMySQLPort() if (err != nil) != tt.wantErr { t.Fatalf("GetMySQLPort() error = %v, wantErr %v", err, tt.wantErr) } if err != nil || tt.wantErr { return } if gotVal != tt.wantVal { t.Fatalf("GetMySQLPort() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetMySQLUser(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantVal any }{ { name: "default", envVarKey: MySQLUser, envVarVal: "", wantVal: MySQLDefaultUser, }, { name: "custom", envVarKey: MySQLUser, envVarVal: "mysqluser", wantVal: "mysqluser", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal := GetMySQLUser() if gotVal != tt.wantVal { t.Fatalf("GetMySQLUser() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetMySQLPassword(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantVal any }{ { name: "default", envVarKey: MySQLPassword, envVarVal: "", wantVal: MySQLDefaultPassword, }, { name: "custom", envVarKey: MySQLPassword, envVarVal: "klmno", wantVal: "klmno", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal := GetMySQLPassword() if gotVal != tt.wantVal { t.Fatalf("GetMySQLPassword() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetPostgresAddress(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantVal any }{ { name: "default", envVarKey: PostgresSeeds, envVarVal: "", wantVal: Localhost, }, { name: "custom", envVarKey: PostgresSeeds, envVarVal: "postgresseed", wantVal: "postgresseed", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal := GetPostgresAddress() if gotVal != tt.wantVal { t.Fatalf("GetPostgresAddress() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetPostgresPort(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantErr bool wantVal any }{ { name: "default", envVarKey: PostgresPort, envVarVal: "", wantErr: false, wantVal: mustConvertInt(t, PostgresDefaultPort), }, { name: "non-int port", envVarKey: PostgresPort, envVarVal: "xyz", wantErr: true, }, { name: "custom port", envVarKey: PostgresPort, envVarVal: "8787", wantVal: 8787, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal, err := GetPostgresPort() if (err != nil) != tt.wantErr { t.Fatalf("GetPostgresPort() error = %v, wantErr %v", err, tt.wantErr) } if err != nil || tt.wantErr { return } if gotVal != tt.wantVal { t.Fatalf("GetPostgresPort() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetESVersion(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantVal any }{ { name: "default", envVarKey: ESVersion, envVarVal: "", wantVal: ESDefaultVersion, }, { name: "custom", envVarKey: ESVersion, envVarVal: "v1234", wantVal: "v1234", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal := GetESVersion() if gotVal != tt.wantVal { t.Fatalf("GetESVersion() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetMongoAddress(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantVal any }{ { name: "default", envVarKey: MongoSeeds, envVarVal: "", wantVal: Localhost, }, { name: "custom", envVarKey: MongoSeeds, envVarVal: "mongoseed", wantVal: "mongoseed", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal := GetMongoAddress() if gotVal != tt.wantVal { t.Fatalf("GetMongoAddress() = %v, want %v", gotVal, tt.wantVal) } }) } } func TestGetMongoPort(t *testing.T) { tests := []struct { name string envVarKey string envVarVal string wantErr bool wantVal any }{ { name: "default", envVarKey: MongoPort, envVarVal: "", wantErr: false, wantVal: mustConvertInt(t, MongoDefaultPort), }, { name: "non-int port", envVarKey: MongoPort, envVarVal: "xyz", wantErr: true, }, { name: "custom port", envVarKey: MongoPort, envVarVal: "8787", wantVal: 8787, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv(tt.envVarKey, tt.envVarVal) gotVal, err := GetMongoPort() if (err != nil) != tt.wantErr { t.Fatalf("GetMongoPort() error = %v, wantErr %v", err, tt.wantErr) } if err != nil || tt.wantErr { return } if gotVal != tt.wantVal { t.Fatalf("GetMongoPort() = %v, want %v", gotVal, tt.wantVal) } }) } } func mustConvertInt(t *testing.T, s string) int { v, err := strconv.Atoi(s) if err != nil { t.Fatalf("failed to convert string to int, err: %v", err) } return v } ================================================ FILE: go.mod ================================================ module github.com/uber/cadence go 1.23.0 toolchain go1.23.4 require ( github.com/MicahParks/keyfunc/v2 v2.1.0 github.com/VividCortex/mysqlerr v1.0.0 github.com/aws/aws-sdk-go v1.54.12 github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748 github.com/cch123/elasticsql v0.0.0-20190321073543-a1a440758eb9 github.com/dave/dst v0.26.2 github.com/davecgh/go-spew v1.1.1 github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da github.com/emirpasic/gods v0.0.0-20190624094223-e689965507ab github.com/fatih/color v1.13.0 github.com/go-sql-driver/mysql v1.7.1 github.com/gocql/gocql v0.0.0-20211015133455-b225f9b53fa1 github.com/gogo/protobuf v1.3.2 github.com/golang-jwt/jwt/v5 v5.2.0 github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-version v1.2.0 github.com/iancoleman/strcase v0.2.0 github.com/jmespath/go-jmespath v0.4.0 github.com/jmoiron/sqlx v1.2.1-0.20200615141059-0794cb1f47ee github.com/jonboulle/clockwork v0.5.0 github.com/lib/pq v1.2.0 github.com/m3db/prometheus_client_golang v0.8.1 github.com/olekukonko/tablewriter v0.0.4 github.com/olivere/elastic v6.2.37+incompatible github.com/olivere/elastic/v7 v7.0.21 github.com/opentracing/opentracing-go v1.2.0 github.com/otiai10/copy v1.1.1 github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 github.com/sirupsen/logrus v1.9.0 // indirect github.com/startreedata/pinot-client-go v0.2.0 // latest release supports pinot v0.12.0 which is also internal version github.com/stretchr/testify v1.10.0 github.com/uber-go/tally v3.3.15+incompatible github.com/uber/cadence-idl v0.0.0-20260313102523-57782092adb4 github.com/uber/ringpop-go v0.8.5 github.com/uber/tchannel-go v1.22.2 github.com/urfave/cli/v2 v2.27.4 github.com/valyala/fastjson v1.4.1 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 go.etcd.io/etcd/client/v3 v3.5.5 go.mongodb.org/mongo-driver v1.7.3 go.uber.org/atomic v1.10.0 go.uber.org/cadence v0.19.0 go.uber.org/config v1.4.0 go.uber.org/fx v1.23.0 go.uber.org/multierr v1.10.0 go.uber.org/thriftrw v1.29.2 go.uber.org/yarpc v1.70.3 go.uber.org/zap v1.26.0 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 golang.org/x/time v0.5.0 golang.org/x/tools v0.22.0 gonum.org/v1/gonum v0.7.0 google.golang.org/grpc v1.59.0 gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19 gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/IBM/sarama v1.45.2 github.com/Masterminds/semver/v3 v3.2.1 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 github.com/google/gofuzz v1.0.0 github.com/mark3labs/mcp-go v0.18.0 github.com/ncruces/go-sqlite3 v0.22.0 github.com/opensearch-project/opensearch-go/v4 v4.1.0 github.com/robfig/cron/v3 v3.0.1 go.etcd.io/etcd/api/v3 v3.5.5 go.uber.org/mock v0.5.0 ) require ( github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/ncruces/julianday v1.0.0 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/robfig/cron v1.2.0 // indirect github.com/rogpeppe/go-internal v1.6.1 // indirect github.com/tetratelabs/wazero v1.8.2 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect ) require ( github.com/BurntSushi/toml v1.3.2 // indirect github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect github.com/apache/thrift v0.17.0 // indirect github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/cristalhq/jwt/v3 v3.1.0 // indirect github.com/eapache/go-resiliency v1.7.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect github.com/eapache/queue v1.1.0 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/go-zookeeper/zk v1.0.3 // indirect github.com/gogo/googleapis v1.3.2 // indirect github.com/gogo/status v1.1.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect github.com/jcmturner/gofork v1.7.6 // indirect github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/jessevdk/go-flags v1.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kisielk/errcheck v1.5.0 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/m3db/prometheus_client_model v0.1.0 // indirect github.com/m3db/prometheus_common v0.1.0 // indirect github.com/m3db/prometheus_procfs v0.8.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.9 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.7 // indirect github.com/mattn/go-sqlite3 v1.11.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.11.1 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/uber-common/bark v1.2.1 // indirect github.com/uber-go/mapdecode v1.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/stringprep v1.0.3 // indirect github.com/xdg/stringprep v1.0.0 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect go.uber.org/dig v1.18.0 // indirect go.uber.org/goleak v1.2.0 go.uber.org/net/metrics v1.3.0 // indirect golang.org/x/crypto v0.38.0 // indirect golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/mod v0.18.0 golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.3.2 // indirect ) // ringpop-go and tchannel-go depends on older version of thrift, yarpc brings up newer version replace github.com/apache/thrift => github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7 // DO NOT USE as it misses mysql/config store fix retract v1.2.3 ================================================ FILE: go.sum ================================================ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/IBM/sarama v1.45.2 h1:8m8LcMCu3REcwpa7fCP6v2fuPuzVwXDAM2DOv3CBrKw= github.com/IBM/sarama v1.45.2/go.mod h1:ppaoTcVdGv186/z6MEKsMm70A5fwJfRTpstI37kVn3Y= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/VividCortex/mysqlerr v1.0.0 h1:5pZ2TZA+YnzPgzBfiUWGqWmKDVNBdrkf9g+DNe1Tiq8= github.com/VividCortex/mysqlerr v1.0.0/go.mod h1:xERx8E4tBhLvpjzdUyQiSfUxeMcATEQrflDAfXsqcAE= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7 h1:Fv9bK1Q+ly/ROk4aJsVMeuIwPel4bEnD8EPiI91nZMg= github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/aws/aws-sdk-go v1.34.13/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.54.12 h1:xPDB+GSBZq0rJbmDZF+EyfMbnWRyfEPcn7PZ7bJjXSw= github.com/aws/aws-sdk-go v1.54.12/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3 h1:wOysYcIdqv3WnvwqFFzrYCFALPED7qkUGaLXu359GSc= github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3/go.mod h1:UMqtWQTnOe4byzwe7Zhwh8f8s+36uszN51sJrSIZlTE= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748 h1:bXxS5/Z3/dfc8iFniQfgogNBomo0u+1//9eP+jl8GVo= github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= github.com/cch123/elasticsql v0.0.0-20190321073543-a1a440758eb9 h1:2rukpuvOpZryti4j58JHH5f0qJXxYdTYpkgNYx8iLdg= github.com/cch123/elasticsql v0.0.0-20190321073543-a1a440758eb9/go.mod h1:h4Tt1A91nOVAYsWdoxlXwKYPfxkxeTuRFkEMUQaRVBo= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cristalhq/jwt/v3 v3.1.0 h1:iLeL9VzB0SCtjCy9Kg53rMwTcrNm+GHyVcz2eUujz6s= github.com/cristalhq/jwt/v3 v3.1.0/go.mod h1:XOnIXst8ozq/esy5N1XOlSyQqBd+84fxJ99FK+1jgL8= github.com/dave/dst v0.26.2 h1:lnxLAKI3tx7MgLNVDirFCsDTlTG9nKTk7GcptKcWSwY= github.com/dave/dst v0.26.2/go.mod h1:UMDJuIRPfyUCC78eFuB+SV/WI8oDeyFDvM/JR6NI3IU= github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e/go.mod h1:qZqlPyPvfsDJt+3wHJ1EvSXDuVjFTK0j2p/ca+gtsb8= github.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA= github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/emirpasic/gods v0.0.0-20190624094223-e689965507ab h1:eTc1vwMHNg4WtS95PtYi3FFCKwlPjtN/Lw9IALTRtd8= github.com/emirpasic/gods v0.0.0-20190624094223-e689965507ab/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gocql/gocql v0.0.0-20211015133455-b225f9b53fa1 h1:px9qUCy/RNJNsfCam4m2IxWGxNuimkrioEF0vrrbPsg= github.com/gocql/gocql v0.0.0-20211015133455-b225f9b53fa1/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.3.2 h1:kX1es4djPJrsDhY7aZKJy7aZasdcB5oSOEphMjSB53c= github.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3-0.20190920234318-1680a479a2cf/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20181127221834-b4f47329b966/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.1-0.20200615141059-0794cb1f47ee h1:59lyMGvZusByi7Rvctn8cxdVAjhiOnqCv3G5DrYApYQ= github.com/jmoiron/sqlx v1.2.1-0.20200615141059-0794cb1f47ee/go.mod h1:ClpsPFzLpSBl7MvJ+BhV0JHz4vmKRBarpvZ9644v9Oo= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/m3db/prometheus_client_golang v0.8.1 h1:t7w/tcFws81JL1j5sqmpqcOyQOpH4RDOmIe3A3fdN3w= github.com/m3db/prometheus_client_golang v0.8.1/go.mod h1:8R/f1xYhXWq59KD/mbRqoBulXejss7vYtYzWmruNUwI= github.com/m3db/prometheus_client_model v0.1.0 h1:cg1+DiuyT6x8h9voibtarkH1KT6CmsewBSaBhe8wzLo= github.com/m3db/prometheus_client_model v0.1.0/go.mod h1:Qfsxn+LypxzF+lNhak7cF7k0zxK7uB/ynGYoj80zcD4= github.com/m3db/prometheus_common v0.1.0 h1:YJu6eCIV6MQlcwND24cRG/aRkZDX1jvYbsNNs1ZYr0w= github.com/m3db/prometheus_common v0.1.0/go.mod h1:EBmDQaMAy4B8i+qsg1wMXAelLNVbp49i/JOeVszQ/rs= github.com/m3db/prometheus_procfs v0.8.1 h1:LsxWzVELhDU9sLsZTaFLCeAwCn7bC7qecZcK4zobs/g= github.com/m3db/prometheus_procfs v0.8.1/go.mod h1:N8lv8fLh3U3koZx1Bnisj60GYUMDpWb09x1R+dmMOJo= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mark3labs/mcp-go v0.18.0 h1:YuhgIVjNlTG2ZOwmrkORWyPTp0dz1opPEqvsPtySXao= github.com/mark3labs/mcp-go v0.18.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncruces/go-sqlite3 v0.22.0 h1:FkGSBhd0TY6e66k1LVhyEpA+RnG/8QkQNed5pjIk4cs= github.com/ncruces/go-sqlite3 v0.22.0/go.mod h1:ueXOZXYZS2OFQirCU3mHneDwJm5fGKHrtccYBeGEV7M= github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M= github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olivere/elastic v6.2.37+incompatible h1:UfSGJem5czY+x/LqxgeCBgjDn6St+z8OnsCuxwD3L0U= github.com/olivere/elastic v6.2.37+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGeB5G1iqDKVBWLNSYW8yfJ8= github.com/olivere/elastic/v7 v7.0.21 h1:58a2pMlLketCsLyKg8kJNJG+OZIFKrSQXX6gJBpqqlg= github.com/olivere/elastic/v7 v7.0.21/go.mod h1:Kh7iIsXIBl5qRQOBFoylCsXVTtye3keQU2Y/YbR7HD8= github.com/opensearch-project/opensearch-go/v4 v4.1.0 h1:YXNaMpMU0PC7suGyP13EuczkDT3K54QajgDnLKCZAz8= github.com/opensearch-project/opensearch-go/v4 v4.1.0/go.mod h1:aSTMFGSLEoiG19US6Oo5udvWCjHap3mRcWBNV8rAFak= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/otiai10/copy v1.1.1 h1:PH7IFlRQ6Fv9vYmuXbDRLdgTHoP1w483kPNUP2bskpo= github.com/otiai10/copy v1.1.1/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pborman/uuid v0.0.0-20160209185913-a97ce2ca70fa/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs983HfUfpkw9OTFD9tbBfAViHE= github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a h1:AA9vgIBDjMHPC2McaGPojgV2dcI78ZC0TLNhYCXEKH8= github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a/go.mod h1:lzZQ3Noex5pfAy7mkAeCjcBDteYU85uWWnJ/y6gKU8k= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.8.0/go.mod h1:PC/OgXc+UN7B4ALwvn1yzVZmVwvhXp5JsbBv6wSv6i0= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.9/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/samuel/go-thrift v0.0.0-20191111193933-5165175b40af h1:EiWVfh8mr40yFZEui2oF0d45KgH48PkB2H0Z0GANvSI= github.com/samuel/go-thrift v0.0.0-20191111193933-5165175b40af/go.mod h1:Vrkh1pnjV9Bl8c3P9zH0/D4NlOHWP5d4/hF4YTULaec= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/startreedata/pinot-client-go v0.2.0 h1:Pv4W3HgxxGbB9GogRwNqfNyqPrOpScZuhQRc9kLM90A= github.com/startreedata/pinot-client-go v0.2.0/go.mod h1:vTz6Bu4dWIQIsfUoqFtgMV2QqBjeuSaDA8vxkOoYnLg= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 h1:7z3LSn867ex6VSaahyKadf4WtSsJIgne6A1WLOAGM8A= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4= github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/uber-common/bark v1.2.1 h1:cREJ9b7CpTjwZr0/5wV82fXlitoCIEHHnt9WkQ4lIk0= github.com/uber-common/bark v1.2.1/go.mod h1:g0ZuPcD7XiExKHynr93Q742G/sbrdVQkghrqLGOoFuY= github.com/uber-go/mapdecode v1.0.0 h1:euUEFM9KnuCa1OBixz1xM+FIXmpixyay5DLymceOVrU= github.com/uber-go/mapdecode v1.0.0/go.mod h1:b5nP15FwXTgpjTjeA9A2uTHXV5UJCl4arwKpP0FP1Hw= github.com/uber-go/tally v3.3.12+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU= github.com/uber-go/tally v3.3.15+incompatible h1:9hLSgNBP28CjIaDmAuRTq9qV+UZY+9PcvAkXO4nNMwg= github.com/uber-go/tally v3.3.15+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU= github.com/uber/cadence-idl v0.0.0-20211111101836-d6b70b60eb8c/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/uber/cadence-idl v0.0.0-20260313102523-57782092adb4 h1:1kAlcf7STXalFhDzv6vUz3XZtGOXQfwDF7IBKHu8GwI= github.com/uber/cadence-idl v0.0.0-20260313102523-57782092adb4/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM= github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/uber/ringpop-go v0.8.5 h1:aBa/SHmmFRcAXA63k7uBheoTL8tCmH7L+OgktB1AF/o= github.com/uber/ringpop-go v0.8.5/go.mod h1:zVI6eGO6L7pG14GkntHsSOfmUAWQ7B4lvmzly4IT4ls= github.com/uber/tchannel-go v1.16.0/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo= github.com/uber/tchannel-go v1.22.2 h1:NKA5FVESYh6Ij6V+tujK+IFZnBKDyUHdsBY264UYhgk= github.com/uber/tchannel-go v1.22.2/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo= github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= github.com/wI2L/jsondiff v0.6.0 h1:zrsH3FbfVa3JO9llxrcDy/XLkYPLgoMX6Mz3T2PP2AI= github.com/wI2L/jsondiff v0.6.0/go.mod h1:D6aQ5gKgPF9g17j+E9N7aasmU1O+XvfmWm1y8UMmNpw= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryBNl9eKOeqQ58Y/Qpo3Q9QNxKHX5uzzQ= github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8= go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= go.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI= go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= go.mongodb.org/mongo-driver v1.7.3 h1:G4l/eYY9VrQAK/AUgkV0koQKzQnyddnWxrd/Etf0jIs= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/cadence v0.19.0 h1:EvDXwIJ0lAAxL2i8ne/vG/TeoJM6xkAyqgTRFmIWG+c= go.uber.org/cadence v0.19.0/go.mod h1:s91dOf0kcJbumPscRIVFV/4Xq/exhefzpXmnDiRRTxs= go.uber.org/config v1.4.0 h1:upnMPpMm6WlbZtXoasNkK4f0FhxwS+W4Iqz5oNznehQ= go.uber.org/config v1.4.0/go.mod h1:aCyrMHmUAc/s2h9sv1koP84M9ZF/4K+g2oleyESO/Ig= go.uber.org/dig v1.8.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= go.uber.org/dig v1.10.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= go.uber.org/dig v1.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw= go.uber.org/dig v1.18.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.10.0/go.mod h1:vLRicqpG/qQEzno4SYU86iCwfT95EZza+Eba0ItuxqY= go.uber.org/fx v1.13.1/go.mod h1:bREWhavnedxpJeTq9pQT53BbvwhUv7TcpsOqcH4a+3w= go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg= go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU= go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/net/metrics v1.3.0 h1:iRLPuVecNYf/wIV+mQaA4IgN8ghifu3q1B4IT6HfwyY= go.uber.org/net/metrics v1.3.0/go.mod h1:pEQrSDGNWT5IVpekWzee5//uHjI4gmgZFkobfw3bv8I= go.uber.org/thriftrw v1.25.0/go.mod h1:IcIfSeZgc59AlYb0xr0DlDKIdD7SgjnFpG9BXCPyy9g= go.uber.org/thriftrw v1.29.2 h1:pRuFLzbGvTcnYwGSjizWRHlbJUzGhu84sRiL1h1kUd8= go.uber.org/thriftrw v1.29.2/go.mod h1:YcjXveberDd28/Bs34SwHy3yu85x/jB4UA2gIcz/Eo0= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/yarpc v1.55.0/go.mod h1:V2JUPDWHYGNpvyuroYjf0KFjwvBCtcFJLuvZqv7TWA0= go.uber.org/yarpc v1.70.3 h1:yykHwzRD9/bgDtlOWoVuXbSZoU91Id2dWJO1CDSRHnI= go.uber.org/yarpc v1.70.3/go.mod h1:EH6I6K1HxBbOxZIJfhdDf+H+cvXPHmJyRvpfPqES20U= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20180920145803-b19384d3c130/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.0.0-20170927054726-6dc17368e09b/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191104232314-dc038396d1f0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191226212025-6b505debf4bc/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117215004-fe56e6335763/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.7.0 h1:Hdks0L0hgznZLG9nzXb8vZ0rRvqNvAcgAp84y7Mwkgw= gonum.org/v1/gonum v0.7.0/go.mod h1:L02bwd0sqlsvRv41G7wGWFCsVNZFv/k1xzGIxeANHGM= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a h1:myvhA4is3vrit1a6NZCWBIwN0kNEnX21DJOJX/NvIfI= google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19 h1:WB265cn5OpO+hK3pikC9hpP1zI/KTwmyMFKloW9eOVc= gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34= honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= ================================================ FILE: go.work ================================================ go 1.23.0 // This file is primarily for gopls and IDEs it supports, as it does not // understand submodules correctly without it. // // It also slightly improves `go run` and `go test` behavior, as they work from // the top level regardless of the target's submodule, but it isn't quite // sufficient as things like `go build ./...` still do not recurse into submodules. // // Hence the Makefile's `make build` and `make tidy`, and changes to `make test`-alikes. // Maybe we can remove those some day, but not currently at least. // // Anyway, add any new submodules to here, and make sure they're all using // similar `replace repo => ../relative/path` replaces so it all stays compatible // at all times during development time. use ( . ./cmd/server ./common/archiver/gcloud // DO NOT include, tools dependencies are intentionally separate. // ./internal/tools ) // technically only a minimum version, but forced to be precise in makefile targets. // must be kept in sync with docker files to avoid double-downloading. // // this should be safe to raise any time as it only impacts us, as this affects the // Go version used to build within this workspace only, it does not affect dependencies. // but note that it needs to be a version that docker + mac + linux all support, as // they all must be in sync. toolchain go1.23.4 ================================================ FILE: go.work.sum ================================================ bazil.org/fuse v0.0.0-20180421153158-65cc252bf669 h1:FNCRpXiquG1aoyqcIWVFmpTSKVcx2bQD38uZZeGtdlw= bitbucket.org/creachadair/shell v0.0.6 h1:reJflDbKqnlnqb4Oo2pQ1/BqmY/eCWcNGHrIUO8qIzc= cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= cloud.google.com/go/accessapproval v1.7.2 h1:W55SFrY6EVlcmmRGUk0rGhuy3j4fn7UtEocib/zADVE= cloud.google.com/go/accessapproval v1.7.2/go.mod h1:/gShiq9/kK/h8T/eEn1BTzalDvk0mZxJlhfw0p+Xuc0= cloud.google.com/go/accesscontextmanager v1.8.2 h1:jcOXen2u13aHgOHibUjxyPI+fZzVhElxy2gzJJlOOHg= cloud.google.com/go/accesscontextmanager v1.8.2/go.mod h1:E6/SCRM30elQJ2PKtFMs2YhfJpZSNcJyejhuzoId4Zk= cloud.google.com/go/aiplatform v1.51.1 h1:g+y03dll9HnX9U0oBKIqUOI+8VQWT1QJF12VGxkal0Q= cloud.google.com/go/aiplatform v1.51.1/go.mod h1:kY3nIMAVQOK2XDqDPHaOuD9e+FdMA6OOpfBjsvaFSOo= cloud.google.com/go/analytics v0.21.4 h1:SScWR8i/M8h7h3lFKtOYcj0r4272aL+KvRRrsu39Vec= cloud.google.com/go/analytics v0.21.4/go.mod h1:zZgNCxLCy8b2rKKVfC1YkC2vTrpfZmeRCySM3aUbskA= cloud.google.com/go/apigateway v1.6.2 h1:I46jVrhr2M1JJ1lK7JGn2BvybN44muEh+LSjBQ1l9hw= cloud.google.com/go/apigateway v1.6.2/go.mod h1:CwMC90nnZElorCW63P2pAYm25AtQrHfuOkbRSHj0bT8= cloud.google.com/go/apigeeconnect v1.6.2 h1:7LzOTW34EH2julg0MQVt+U9ZdmiCKcg6fef/ugKL2Xo= cloud.google.com/go/apigeeconnect v1.6.2/go.mod h1:s6O0CgXT9RgAxlq3DLXvG8riw8PYYbU/v25jqP3Dy18= cloud.google.com/go/apigeeregistry v0.7.2 h1:MESEjKSfz4TvLAzT2KPimDDvhOyQlcq7aFFREG2PRt4= cloud.google.com/go/apigeeregistry v0.7.2/go.mod h1:9CA2B2+TGsPKtfi3F7/1ncCCsL62NXBRfM6iPoGSM+8= cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ= cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= cloud.google.com/go/appengine v1.8.2 h1:0/OFV0FQKgi0AB4E8NuYN0JY3hJzND4ftRpK7P26uaw= cloud.google.com/go/appengine v1.8.2/go.mod h1:WMeJV9oZ51pvclqFN2PqHoGnys7rK0rz6s3Mp6yMvDo= cloud.google.com/go/area120 v0.8.2 h1:h/wMtPPsgFJfMce1b9M24Od8RuKt8CWENwr+X24tBhE= cloud.google.com/go/area120 v0.8.2/go.mod h1:a5qfo+x77SRLXnCynFWPUZhnZGeSgvQ+Y0v1kSItkh4= cloud.google.com/go/artifactregistry v1.14.3 h1:Ssv6f+jgfhDdhu43AaHUaSosIYpQ+TPCJNwqYSJT1AE= cloud.google.com/go/artifactregistry v1.14.3/go.mod h1:A2/E9GXnsyXl7GUvQ/2CjHA+mVRoWAXC0brg2os+kNI= cloud.google.com/go/asset v1.15.1 h1:+9f5/s/U0AGZSPLTOMcXSZ5NDB5jQ2Szr+WQPgPA8bk= cloud.google.com/go/asset v1.15.1/go.mod h1:yX/amTvFWRpp5rcFq6XbCxzKT8RJUam1UoboE179jU4= cloud.google.com/go/assuredworkloads v1.11.2 h1:EbPyk3fC8sTxSIPoFrCR9P1wRTVdXcRxvPqFK8/wdso= cloud.google.com/go/assuredworkloads v1.11.2/go.mod h1:O1dfr+oZJMlE6mw0Bp0P1KZSlj5SghMBvTpZqIcUAW4= cloud.google.com/go/automl v1.13.2 h1:kUN4Y6N61AsNdXsdZIug1c+2pTJ5tg9xUA6+yn0Wf8Y= cloud.google.com/go/automl v1.13.2/go.mod h1:gNY/fUmDEN40sP8amAX3MaXkxcqPIn7F1UIIPZpy4Mg= cloud.google.com/go/baremetalsolution v1.2.1 h1:uRpZsKiWFDyT1sARZVRKqnOmf2mpRfVas7KMC3/MA4I= cloud.google.com/go/baremetalsolution v1.2.1/go.mod h1:3qKpKIw12RPXStwQXcbhfxVj1dqQGEvcmA+SX/mUR88= cloud.google.com/go/batch v1.5.1 h1:+8ZogCLFauglOE5ybTCWscoexD7Z8k4XW27RVTKNEoo= cloud.google.com/go/batch v1.5.1/go.mod h1:RpBuIYLkQu8+CWDk3dFD/t/jOCGuUpkpX+Y0n1Xccs8= cloud.google.com/go/beyondcorp v1.0.1 h1:uQpsXwttlV0+AXHdB5qaZl1mz2SsyYV1PKgTR74noaQ= cloud.google.com/go/beyondcorp v1.0.1/go.mod h1:zl/rWWAFVeV+kx+X2Javly7o1EIQThU4WlkynffL/lk= cloud.google.com/go/bigquery v1.56.0 h1:LHIc9E7Kw+ftFpQFKzZYBB88IAFz7qONawXXx0F3QBo= cloud.google.com/go/bigquery v1.56.0/go.mod h1:KDcsploXTEY7XT3fDQzMUZlpQLHzE4itubHrnmhUrZA= cloud.google.com/go/billing v1.17.2 h1:ozS/MNj6KKz8Reuw7tIG8Ycucq/YpSf3u3XCqrupbcg= cloud.google.com/go/billing v1.17.2/go.mod h1:u/AdV/3wr3xoRBk5xvUzYMS1IawOAPwQMuHgHMdljDg= cloud.google.com/go/binaryauthorization v1.7.1 h1:i2S+/G36VA1UG8gdcQLpq5I58/w/RzAnjQ65scKozFg= cloud.google.com/go/binaryauthorization v1.7.1/go.mod h1:GTAyfRWYgcbsP3NJogpV3yeunbUIjx2T9xVeYovtURE= cloud.google.com/go/certificatemanager v1.7.2 h1:Xytp8O0/EDh2nVscHhFQpicY9YAT3f3R7D7pv/z29uE= cloud.google.com/go/certificatemanager v1.7.2/go.mod h1:15SYTDQMd00kdoW0+XY5d9e+JbOPjp24AvF48D8BbcQ= cloud.google.com/go/channel v1.17.1 h1:+1B+Gj/3SJSLGJZXCp3dWiseMVHoSZ7Xo6Klg1fqM64= cloud.google.com/go/channel v1.17.1/go.mod h1:xqfzcOZAcP4b/hUDH0GkGg1Sd5to6di1HOJn/pi5uBQ= cloud.google.com/go/cloudbuild v1.14.1 h1:Tp0ITIlFam7T8K/TyeceITtpw1f8+KxVKwYyiyWDPK8= cloud.google.com/go/cloudbuild v1.14.1/go.mod h1:K7wGc/3zfvmYWOWwYTgF/d/UVJhS4pu+HAy7PL7mCsU= cloud.google.com/go/clouddms v1.7.1 h1:LrtqeR2xKV3juG5N7eeUgW+PqdMClOWH2U9PN3EpfFw= cloud.google.com/go/clouddms v1.7.1/go.mod h1:o4SR8U95+P7gZ/TX+YbJxehOCsM+fe6/brlrFquiszk= cloud.google.com/go/cloudtasks v1.12.2 h1:IoJI49JClvv2+NYvcABRgTO9y4veAUFlaOTigm+xXqE= cloud.google.com/go/cloudtasks v1.12.2/go.mod h1:A7nYkjNlW2gUoROg1kvJrQGhJP/38UaWwsnuBDOBVUk= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/contactcenterinsights v1.11.1 h1:dEfCjtdYjS3n8/1HEKbJaOL31l3dEs3q9aeaNsyrJBc= cloud.google.com/go/contactcenterinsights v1.11.1/go.mod h1:FeNP3Kg8iteKM80lMwSk3zZZKVxr+PGnAId6soKuXwE= cloud.google.com/go/container v1.26.1 h1:1CXjOL/dZZ2jXX1CYWqlxmXqJbZo8HwQX4DJxLzgQWo= cloud.google.com/go/container v1.26.1/go.mod h1:5smONjPRUxeEpDG7bMKWfDL4sauswqEtnBK1/KKpR04= cloud.google.com/go/containeranalysis v0.11.1 h1:PHh4KTcMpCjYgxfV+TzvP24wolTGP9lGbqh9sBNHxjs= cloud.google.com/go/containeranalysis v0.11.1/go.mod h1:rYlUOM7nem1OJMKwE1SadufX0JP3wnXj844EtZAwWLY= cloud.google.com/go/datacatalog v1.18.1 h1:xJp9mZrc2HPaoxIz3sP9pCmf/impifweQ/yGG9VBfio= cloud.google.com/go/datacatalog v1.18.1/go.mod h1:TzAWaz+ON1tkNr4MOcak8EBHX7wIRX/gZKM+yTVsv+A= cloud.google.com/go/dataflow v0.9.2 h1:cpu2OeNxnYVadAIXETLRS5riz3KUR8ErbTojAQTFJVg= cloud.google.com/go/dataflow v0.9.2/go.mod h1:vBfdBZ/ejlTaYIGB3zB4T08UshH70vbtZeMD+urnUSo= cloud.google.com/go/dataform v0.8.2 h1:l155O3DS7pfyR91maS4l92bEjKbkbWie3dpgltZ1Q68= cloud.google.com/go/dataform v0.8.2/go.mod h1:X9RIqDs6NbGPLR80tnYoPNiO1w0wenKTb8PxxlhTMKM= cloud.google.com/go/datafusion v1.7.2 h1:CIIXp4bbwck49ZTV/URabJaV48jVB86THyVBWGgeDjw= cloud.google.com/go/datafusion v1.7.2/go.mod h1:62K2NEC6DRlpNmI43WHMWf9Vg/YvN6QVi8EVwifElI0= cloud.google.com/go/datalabeling v0.8.2 h1:4N5mbjauemzaatxGOFVpV2i8HiXSUUhyNRBU+dCBHl0= cloud.google.com/go/datalabeling v0.8.2/go.mod h1:cyDvGHuJWu9U/cLDA7d8sb9a0tWLEletStu2sTmg3BE= cloud.google.com/go/dataplex v1.10.1 h1:8Irss8sIalm/X8r0Masv5KJRkddcxov3TiW8W96FmC4= cloud.google.com/go/dataplex v1.10.1/go.mod h1:1MzmBv8FvjYfc7vDdxhnLFNskikkB+3vl475/XdCDhs= cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= cloud.google.com/go/dataproc/v2 v2.2.1 h1:BPjIIkTCAOHUkMtWKqae55qEku5K09LVbQ46LYt7r1s= cloud.google.com/go/dataproc/v2 v2.2.1/go.mod h1:QdAJLaBjh+l4PVlVZcmrmhGccosY/omC1qwfQ61Zv/o= cloud.google.com/go/dataqna v0.8.2 h1:vJ9JVKDgDG7AQMbTD8pdWaogJ4c/yHn0qer+q0nFIaw= cloud.google.com/go/dataqna v0.8.2/go.mod h1:KNEqgx8TTmUipnQsScOoDpq/VlXVptUqVMZnt30WAPs= cloud.google.com/go/datastore v1.15.0 h1:0P9WcsQeTWjuD1H14JIY7XQscIPQ4Laje8ti96IC5vg= cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= cloud.google.com/go/datastream v1.10.1 h1:XWiXV1hzs8oAd54//wcb1L15Jl7MnZ/cY2B8XCmu0xE= cloud.google.com/go/datastream v1.10.1/go.mod h1:7ngSYwnw95YFyTd5tOGBxHlOZiL+OtpjheqU7t2/s/c= cloud.google.com/go/deploy v1.13.1 h1:eV5MdoQJGdac/k7D97SDjD8iLE4jCzL42UCAgG6j0iE= cloud.google.com/go/deploy v1.13.1/go.mod h1:8jeadyLkH9qu9xgO3hVWw8jVr29N1mnW42gRJT8GY6g= cloud.google.com/go/dialogflow v1.44.1 h1:Ml/hgEzU3AN0tjNSSv4/QmG1nqwYEsiCySKMkWMqUmI= cloud.google.com/go/dialogflow v1.44.1/go.mod h1:n/h+/N2ouKOO+rbe/ZnI186xImpqvCVj2DdsWS/0EAk= cloud.google.com/go/dlp v1.10.2 h1:sWOATigjZOKmA2rVOSjIcKLCtL2ifdawaukx+H9iffk= cloud.google.com/go/dlp v1.10.2/go.mod h1:ZbdKIhcnyhILgccwVDzkwqybthh7+MplGC3kZVZsIOQ= cloud.google.com/go/documentai v1.23.2 h1:IAKWBngDFTxABdAH52uAn0osPDemyegyRmf5IQKznHw= cloud.google.com/go/documentai v1.23.2/go.mod h1:Q/wcRT+qnuXOpjAkvOV4A+IeQl04q2/ReT7SSbytLSo= cloud.google.com/go/domains v0.9.2 h1:SjpTtaTNRPPajrGiZEtxz9dpElO4PxuDWFvU4JpV1gk= cloud.google.com/go/domains v0.9.2/go.mod h1:3YvXGYzZG1Temjbk7EyGCuGGiXHJwVNmwIf+E/cUp5I= cloud.google.com/go/edgecontainer v1.1.2 h1:B+Acb/0frXUxc60i6lC0JtXrBFAKoS7ZELmet9+ySo8= cloud.google.com/go/edgecontainer v1.1.2/go.mod h1:wQRjIzqxEs9e9wrtle4hQPSR1Y51kqN75dgF7UllZZ4= cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= cloud.google.com/go/essentialcontacts v1.6.3 h1:xrGTLRTzunQk5XhBIkdftuC00B9MUoEXi7Pjgeu1kMM= cloud.google.com/go/essentialcontacts v1.6.3/go.mod h1:yiPCD7f2TkP82oJEFXFTou8Jl8L6LBRPeBEkTaO0Ggo= cloud.google.com/go/eventarc v1.13.1 h1:FmEcxG5rX3LaUB2nRjf2Pas5J5TtVrVznaHN5rxYxnQ= cloud.google.com/go/eventarc v1.13.1/go.mod h1:EqBxmGHFrruIara4FUQ3RHlgfCn7yo1HYsu2Hpt/C3Y= cloud.google.com/go/filestore v1.7.2 h1:/Nnk5pOoY1Lx6A42hJ2eBYcBfqKvLcnh8fV4egopvY4= cloud.google.com/go/filestore v1.7.2/go.mod h1:TYOlyJs25f/omgj+vY7/tIG/E7BX369triSPzE4LdgE= cloud.google.com/go/firestore v1.13.0 h1:/3S4RssUV4GO/kvgJZB+tayjhOfyAHs+KcpJgRVu/Qk= cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= cloud.google.com/go/functions v1.15.2 h1:DpT51zU3UMTt64efB4a9hE9B98Kb0fZC3IfaVp7GnkE= cloud.google.com/go/functions v1.15.2/go.mod h1:CHAjtcR6OU4XF2HuiVeriEdELNcnvRZSk1Q8RMqy4lE= cloud.google.com/go/gaming v1.9.0 h1:7vEhFnZmd931Mo7sZ6pJy7uQPDxF7m7v8xtBheG08tc= cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= cloud.google.com/go/gkebackup v1.3.2 h1:1fnA934a/0oz7nU22gTzmGYFVi6V13Q/hCkdC99K178= cloud.google.com/go/gkebackup v1.3.2/go.mod h1:OMZbXzEJloyXMC7gqdSB+EOEQ1AKcpGYvO3s1ec5ixk= cloud.google.com/go/gkeconnect v0.8.2 h1:AuR3YNK0DgLVrmcc8o4sBrU0dVs/SULSuLh4Gmn1e10= cloud.google.com/go/gkeconnect v0.8.2/go.mod h1:6nAVhwchBJYgQCXD2pHBFQNiJNyAd/wyxljpaa6ZPrY= cloud.google.com/go/gkehub v0.14.2 h1:7rddjV52z0RbToFYj1B39R9dsn+6IXgx4DduEH7N25Q= cloud.google.com/go/gkehub v0.14.2/go.mod h1:iyjYH23XzAxSdhrbmfoQdePnlMj2EWcvnR+tHdBQsCY= cloud.google.com/go/gkemulticloud v1.0.1 h1:V82LxEvFIGJnebn7BBdOUKcVlNQqBaubbKtLgRicHow= cloud.google.com/go/gkemulticloud v1.0.1/go.mod h1:AcrGoin6VLKT/fwZEYuqvVominLriQBCKmbjtnbMjG8= cloud.google.com/go/grafeas v0.3.0 h1:oyTL/KjiUeBs9eYLw/40cpSZglUC+0F7X4iu/8t7NWs= cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= cloud.google.com/go/gsuiteaddons v1.6.2 h1:vR7E1gR85x0wlbUek3cZYJ67U67GpNrboNCRiF/VSSc= cloud.google.com/go/gsuiteaddons v1.6.2/go.mod h1:K65m9XSgs8hTF3X9nNTPi8IQueljSdYo9F+Mi+s4MyU= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= cloud.google.com/go/iap v1.9.1 h1:J5r6CL6EakRmsMRIm2yV0PF5zfIm4sMQbQfPhSTnRzA= cloud.google.com/go/iap v1.9.1/go.mod h1:SIAkY7cGMLohLSdBR25BuIxO+I4fXJiL06IBL7cy/5Q= cloud.google.com/go/ids v1.4.2 h1:KqvR28pAnIss6d2pmGOQ+Fcsi3FOWDVhqdr6QaVvqsI= cloud.google.com/go/ids v1.4.2/go.mod h1:3vw8DX6YddRu9BncxuzMyWn0g8+ooUjI2gslJ7FH3vk= cloud.google.com/go/iot v1.7.2 h1:qFNv3teWkONIPmuY2mzodEnHb6E67ch2OZ6216ycUiU= cloud.google.com/go/iot v1.7.2/go.mod h1:q+0P5zr1wRFpw7/MOgDXrG/HVA+l+cSwdObffkrpnSg= cloud.google.com/go/kms v1.15.3 h1:RYsbxTRmk91ydKCzekI2YjryO4c5Y2M80Zwcs9/D/cI= cloud.google.com/go/kms v1.15.3/go.mod h1:AJdXqHxS2GlPyduM99s9iGqi2nwbviBbhV/hdmt4iOQ= cloud.google.com/go/language v1.11.1 h1:BjU7Ljhh0ZYnZC8jZwiezf1FH75yijJ4raAScseqCns= cloud.google.com/go/language v1.11.1/go.mod h1:Xyid9MG9WOX3utvDbpX7j3tXDmmDooMyMDqgUVpH17U= cloud.google.com/go/lifesciences v0.9.2 h1:0naTq5qUWoRt/b5P+SZ/0mun7ZTlhpJZJsUxhCmLv1c= cloud.google.com/go/lifesciences v0.9.2/go.mod h1:QHEOO4tDzcSAzeJg7s2qwnLM2ji8IRpQl4p6m5Z9yTA= cloud.google.com/go/logging v1.8.1 h1:26skQWPeYhvIasWKm48+Eq7oUqdcdbwsCVwz5Ys0FvU= cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= cloud.google.com/go/longrunning v0.5.2 h1:u+oFqfEwwU7F9dIELigxbe0XVnBAo9wqMuQLA50CZ5k= cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= cloud.google.com/go/managedidentities v1.6.2 h1:QijSmmWHb3EzYQr8SrjWe941ba9G5sTCF5PvhhMM8CM= cloud.google.com/go/managedidentities v1.6.2/go.mod h1:5c2VG66eCa0WIq6IylRk3TBW83l161zkFvCj28X7jn8= cloud.google.com/go/maps v1.4.1 h1:/wp8wImC3tHIHOoaQGRA+KyH3as/Dvp+3J/NqJQBiPQ= cloud.google.com/go/maps v1.4.1/go.mod h1:BxSa0BnW1g2U2gNdbq5zikLlHUuHW0GFWh7sgML2kIY= cloud.google.com/go/mediatranslation v0.8.2 h1:nyBZbNX1j34H00n+irnQraCogrkRWntQsDoA6s8OfKo= cloud.google.com/go/mediatranslation v0.8.2/go.mod h1:c9pUaDRLkgHRx3irYE5ZC8tfXGrMYwNZdmDqKMSfFp8= cloud.google.com/go/memcache v1.10.2 h1:WLJALO3FxuStMiYdSQwiQBDBcs4G8DDwZQmXK+YzAWk= cloud.google.com/go/memcache v1.10.2/go.mod h1:f9ZzJHLBrmd4BkguIAa/l/Vle6uTHzHokdnzSWOdQ6A= cloud.google.com/go/metastore v1.13.1 h1:tLemzNMjKY+xdJUDQt9v5+fQqSufTNgKHHQmihG5ay8= cloud.google.com/go/metastore v1.13.1/go.mod h1:IbF62JLxuZmhItCppcIfzBBfUFq0DIB9HPDoLgWrVOU= cloud.google.com/go/monitoring v1.16.1 h1:CTklIuUkS5nCricGojPwdkSgPsCTX2HmYTxFDg+UvpU= cloud.google.com/go/monitoring v1.16.1/go.mod h1:6HsxddR+3y9j+o/cMJH6q/KJ/CBTvM/38L/1m7bTRJ4= cloud.google.com/go/networkconnectivity v1.14.1 h1:uR+ASueYNodsPCd9wcYEedqjH4+LaCkKqltRBF6CmB4= cloud.google.com/go/networkconnectivity v1.14.1/go.mod h1:LyGPXR742uQcDxZ/wv4EI0Vu5N6NKJ77ZYVnDe69Zug= cloud.google.com/go/networkmanagement v1.9.1 h1:ZK6i6FVQNc1t3fecM3hf9Nu6Kr9C95xr+zMVORYd8ak= cloud.google.com/go/networkmanagement v1.9.1/go.mod h1:CCSYgrQQvW73EJawO2QamemYcOb57LvrDdDU51F0mcI= cloud.google.com/go/networksecurity v0.9.2 h1:fA73AX//KWaqNKOvuQ00WUD3Z/XMhiMhHSFTEl2Wxec= cloud.google.com/go/networksecurity v0.9.2/go.mod h1:jG0SeAttWzPMUILEHDUvFYdQTl8L/E/KC8iZDj85lEI= cloud.google.com/go/notebooks v1.10.1 h1:j/G3r6SPoWzD6CZZrDffZGwgGALvxWwtKJHJ4GF17WA= cloud.google.com/go/notebooks v1.10.1/go.mod h1:5PdJc2SgAybE76kFQCWrTfJolCOUQXF97e+gteUUA6A= cloud.google.com/go/optimization v1.5.1 h1:71wTxJz8gRrVEHF4fw18sGynAyNQwatxCJBI3m3Rd4c= cloud.google.com/go/optimization v1.5.1/go.mod h1:NC0gnUD5MWVAF7XLdoYVPmYYVth93Q6BUzqAq3ZwtV8= cloud.google.com/go/orchestration v1.8.2 h1:lb+Vphr+x2V9ukHwLjyaXJpbPuPhaKdobQx3UAOeSsQ= cloud.google.com/go/orchestration v1.8.2/go.mod h1:T1cP+6WyTmh6LSZzeUhvGf0uZVmJyTx7t8z7Vg87+A0= cloud.google.com/go/orgpolicy v1.11.2 h1:Dnfh5sj3aIAuJzH4Q4rBp6lCJ/IdXRBbwQ0/nQsUySE= cloud.google.com/go/orgpolicy v1.11.2/go.mod h1:biRDpNwfyytYnmCRWZWxrKF22Nkz9eNVj9zyaBdpm1o= cloud.google.com/go/osconfig v1.12.2 h1:AjHbw8MgKKaTFAEJWGdOYtMED3wUXKLtvdfP8Uzbuy0= cloud.google.com/go/osconfig v1.12.2/go.mod h1:eh9GPaMZpI6mEJEuhEjUJmaxvQ3gav+fFEJon1Y8Iw0= cloud.google.com/go/oslogin v1.11.1 h1:r3JYeLf004krfXhRMDfYKlBdMgDDc2q2PM1bomb5Luw= cloud.google.com/go/oslogin v1.11.1/go.mod h1:OhD2icArCVNUxKqtK0mcSmKL7lgr0LVlQz+v9s1ujTg= cloud.google.com/go/phishingprotection v0.8.2 h1:BIv/42ooQXh/jW8BW2cgO0E6yRPbEdvqH3JzKV7BlmI= cloud.google.com/go/phishingprotection v0.8.2/go.mod h1:LhJ91uyVHEYKSKcMGhOa14zMMWfbEdxG032oT6ECbC8= cloud.google.com/go/policytroubleshooter v1.9.1 h1:92YSoPZE62QkNM0G6Nl6PICKUyv4aNgsdtWWceJR6ys= cloud.google.com/go/policytroubleshooter v1.9.1/go.mod h1:MYI8i0bCrL8cW+VHN1PoiBTyNZTstCg2WUw2eVC4c4U= cloud.google.com/go/privatecatalog v0.9.2 h1:gxL4Kn9IXt3tdIOpDPEDPI/kBBLVzaAX5wq6IbOYi8A= cloud.google.com/go/privatecatalog v0.9.2/go.mod h1:RMA4ATa8IXfzvjrhhK8J6H4wwcztab+oZph3c6WmtFc= cloud.google.com/go/pubsub v1.33.0 h1:6SPCPvWav64tj0sVX/+npCBKhUi/UjJehy9op/V3p2g= cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= cloud.google.com/go/pubsublite v1.8.1 h1:pX+idpWMIH30/K7c0epN6V703xpIcMXWRjKJsz0tYGY= cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= cloud.google.com/go/recaptchaenterprise/v2 v2.8.1 h1:06V6+edT20PcrFJfH0TVWMZpZCUpSCADgwGwhkMsGmY= cloud.google.com/go/recaptchaenterprise/v2 v2.8.1/go.mod h1:JZYZJOeZjgSSTGP4uz7NlQ4/d1w5hGmksVgM0lbEij0= cloud.google.com/go/recommendationengine v0.8.2 h1:odf0TZXtwoZ5kJaWBlaE9D0AV+WJLLs+/SRSuE4T/ds= cloud.google.com/go/recommendationengine v0.8.2/go.mod h1:QIybYHPK58qir9CV2ix/re/M//Ty10OxjnnhWdaKS1Y= cloud.google.com/go/recommender v1.11.1 h1:GI4EBCMTLfC8I8R+e13ZaTAa8ZZ0KRPdS99hGtJYyaU= cloud.google.com/go/recommender v1.11.1/go.mod h1:sGwFFAyI57v2Hc5LbIj+lTwXipGu9NW015rkaEM5B18= cloud.google.com/go/redis v1.13.2 h1:2ZtIGspMT65wern2rjX35XPCCJxVKF4J0P1S99bac3k= cloud.google.com/go/redis v1.13.2/go.mod h1:0Hg7pCMXS9uz02q+LoEVl5dNHUkIQv+C/3L76fandSA= cloud.google.com/go/resourcemanager v1.9.2 h1:lC3PjJMHLPlZKqLfan6FkEb3X1F8oCRc1ylY7vRHvDQ= cloud.google.com/go/resourcemanager v1.9.2/go.mod h1:OujkBg1UZg5lX2yIyMo5Vz9O5hf7XQOSV7WxqxxMtQE= cloud.google.com/go/resourcesettings v1.6.2 h1:feqx2EcLRgtmwNHzeLw5Og4Wcy4vcZxw62b0x/QNu60= cloud.google.com/go/resourcesettings v1.6.2/go.mod h1:mJIEDd9MobzunWMeniaMp6tzg4I2GvD3TTmPkc8vBXk= cloud.google.com/go/retail v1.14.2 h1:ed5hWjpOwfsi6E9kj2AFzkz5ScT3aZs7o3MUM0YITUM= cloud.google.com/go/retail v1.14.2/go.mod h1:W7rrNRChAEChX336QF7bnMxbsjugcOCPU44i5kbLiL8= cloud.google.com/go/run v1.3.1 h1:xc46W9kxJI2De9hmpqHEBSSLJhP3bSZl86LdlJa5zm8= cloud.google.com/go/run v1.3.1/go.mod h1:cymddtZOzdwLIAsmS6s+Asl4JoXIDm/K1cpZTxV4Q5s= cloud.google.com/go/scheduler v1.10.2 h1:lgUd1D84JEgNzzHRlcZEIoQ6Ny10YWe8RNH1knhouNk= cloud.google.com/go/scheduler v1.10.2/go.mod h1:O3jX6HRH5eKCA3FutMw375XHZJudNIKVonSCHv7ropY= cloud.google.com/go/secretmanager v1.11.2 h1:52Z78hH8NBWIqbvIG0wi0EoTaAmSx99KIOAmDXIlX0M= cloud.google.com/go/secretmanager v1.11.2/go.mod h1:MQm4t3deoSub7+WNwiC4/tRYgDBHJgJPvswqQVB1Vss= cloud.google.com/go/security v1.15.2 h1:VNpdJNfMeHSJZ+647QtzPrvZ6rWChBklLm/NY64RVW8= cloud.google.com/go/security v1.15.2/go.mod h1:2GVE/v1oixIRHDaClVbHuPcZwAqFM28mXuAKCfMgYIg= cloud.google.com/go/securitycenter v1.23.1 h1:Epx7Gm9ZRPRiFfwDFplka2zKCS0J3cpm0Et1KwI2tvY= cloud.google.com/go/securitycenter v1.23.1/go.mod h1:w2HV3Mv/yKhbXKwOCu2i8bCuLtNP1IMHuiYQn4HJq5s= cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= cloud.google.com/go/servicedirectory v1.11.1 h1:SXhbxsfQJBsUDeo743x5AnVe8ifC7qjXU3bSTT6t/+Q= cloud.google.com/go/servicedirectory v1.11.1/go.mod h1:tJywXimEWzNzw9FvtNjsQxxJ3/41jseeILgwU/QLrGI= cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY= cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4= cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= cloud.google.com/go/shell v1.7.2 h1:zk0Cf2smbFlAdhBQ5tXESZzzmsTfGc31fJfI6a0SVD8= cloud.google.com/go/shell v1.7.2/go.mod h1:KqRPKwBV0UyLickMn0+BY1qIyE98kKyI216sH/TuHmc= cloud.google.com/go/spanner v1.50.0 h1:QrJFOpaxCXdXF+GkiruLz642PHxkdj68PbbnLw3O2Zw= cloud.google.com/go/spanner v1.50.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= cloud.google.com/go/speech v1.19.1 h1:z035FMLs98jpnqcP5xZZ6Es+g6utbeVoUH64BaTzTSU= cloud.google.com/go/speech v1.19.1/go.mod h1:WcuaWz/3hOlzPFOVo9DUsblMIHwxP589y6ZMtaG+iAA= cloud.google.com/go/storagetransfer v1.10.1 h1:CU03oYLauu7xRV25fFmozHZHA/SokLQlC20Ip/UvFro= cloud.google.com/go/storagetransfer v1.10.1/go.mod h1:rS7Sy0BtPviWYTTJVWCSV4QrbBitgPeuK4/FKa4IdLs= cloud.google.com/go/talent v1.6.3 h1:TyJqwhmncdW5CL4rzYSYKJrR9YAe0iNqHtJTnnOaEyM= cloud.google.com/go/talent v1.6.3/go.mod h1:xoDO97Qd4AK43rGjJvyBHMskiEf3KulgYzcH6YWOVoo= cloud.google.com/go/texttospeech v1.7.2 h1:Ac53sRkUo8UMSuhyyWRFJvWEaX8vm0EFwwiTAxeVYuU= cloud.google.com/go/texttospeech v1.7.2/go.mod h1:VYPT6aTOEl3herQjFHYErTlSZJ4vB00Q2ZTmuVgluD4= cloud.google.com/go/tpu v1.6.2 h1:SAFzyGp6mU37lfLTV0cNQwu7tqH4X8b4RCpQZ1s+mYM= cloud.google.com/go/tpu v1.6.2/go.mod h1:NXh3NDwt71TsPZdtGWgAG5ThDfGd32X1mJ2cMaRlVgU= cloud.google.com/go/trace v1.10.2 h1:80Rh4JSqJLfe/xGNrpyO4MQxiFDXcHG1XrsevfmrIRQ= cloud.google.com/go/trace v1.10.2/go.mod h1:NPXemMi6MToRFcSxRl2uDnu/qAlAQ3oULUphcHGh1vA= cloud.google.com/go/translate v1.9.1 h1:gNPBVMINs+aZMB8BW+IfrHLLTfdq0t0GMwa31NmOXY4= cloud.google.com/go/translate v1.9.1/go.mod h1:TWIgDZknq2+JD4iRcojgeDtqGEp154HN/uL6hMvylS8= cloud.google.com/go/video v1.20.1 h1:yMfxQ4N/fXNDsCKNKw9W+FpdrJPj5CDu+FuAJBmGuoo= cloud.google.com/go/video v1.20.1/go.mod h1:3gJS+iDprnj8SY6pe0SwLeC5BUW80NjhwX7INWEuWGU= cloud.google.com/go/videointelligence v1.11.2 h1:vAKuM4YHwZy1W5P7hGJdfXriovqHHUZKhDBq8o4nqfg= cloud.google.com/go/videointelligence v1.11.2/go.mod h1:ocfIGYtIVmIcWk1DsSGOoDiXca4vaZQII1C85qtoplc= cloud.google.com/go/vision/v2 v2.7.3 h1:o8iiH4UsI6O8wO2Ax2r88fLG1RzYQIFevUQY7hXPZeM= cloud.google.com/go/vision/v2 v2.7.3/go.mod h1:V0IcLCY7W+hpMKXK1JYE0LV5llEqVmj+UJChjvA1WsM= cloud.google.com/go/vmmigration v1.7.2 h1:ObE8VWzL+xkU22IsPEMvPCWArnSQ85dEwR5fzgaOvA4= cloud.google.com/go/vmmigration v1.7.2/go.mod h1:iA2hVj22sm2LLYXGPT1pB63mXHhrH1m/ruux9TwWLd8= cloud.google.com/go/vmwareengine v1.0.1 h1:Bj9WECvQk1fkx8IG7gqII3+g1CzhqkPOV84WXvifpFg= cloud.google.com/go/vmwareengine v1.0.1/go.mod h1:aT3Xsm5sNx0QShk1Jc1B8OddrxAScYLwzVoaiXfdzzk= cloud.google.com/go/vpcaccess v1.7.2 h1:3qKiWvzK07eIa943mCvkcZB4gimxaQKKGdNoX01ps7A= cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2MsFaFqxeY1HU= cloud.google.com/go/webrisk v1.9.2 h1:1NZppagzdGO0hVMJsUhZQ5a3Iu2cNyNObu85VFcvIVA= cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= cloud.google.com/go/websecurityscanner v1.6.2 h1:V7PhbJ2OvpGHINL67RBhpwU3+g4MOoqOeL/sFYrogeE= cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= cloud.google.com/go/workflows v1.12.1 h1:jvhSfcfAoOt0nILm7aZPJAHdpoe571qrJyc2ZlngaJk= cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9 h1:yxE46rQA0QaqPGqN2UnwXvgCrRqtjR1CsGSWVTRjvv4= contrib.go.opencensus.io/exporter/stackdriver v0.13.5 h1:TNaexHK16gPUoc7uzELKOU7JULqccn1NDuqUxmxSqfo= contrib.go.opencensus.io/integrations/ocsql v0.1.7 h1:G3k7C0/W44zcqkpRSFyjU9f6HZkbwIrL//qqnlqWZ60= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= github.com/Azure/azure-amqp-common-go/v3 v3.1.0 h1:1N4YSkWYWffOpQHromYdOucBSQXhNRKzqtgICy6To8Q= github.com/Azure/azure-service-bus-go v0.10.11 h1:GBg2mcLQA3af+w+ZuYhiAv58OWSVmQYWcds4nro8Xko= github.com/Azure/go-amqp v0.13.7 h1:ukcCtx138ZmOfHbdALuh9yoJhGtOY3+yaKApfzNvhSk= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/GoogleCloudPlatform/cloudsql-proxy v1.22.0 h1:aTDBS16pX1X4ZR/GFsC2NcOCYJ1hDJwJm3WmKRA905Q= github.com/Groxx/cadence-idl v0.0.0-20240822223859-88ddf9ae415b h1:BilPoUNotX3lcSyIZSYNS8guWjI1y24Y0s/5v8n9Hgs= github.com/Groxx/cadence-idl v0.0.0-20240822223859-88ddf9ae415b/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/ProtonMail/gopenpgp/v2 v2.2.2/go.mod h1:ajUlBGvxMH1UBZnaYO3d1FSVzjiC6kK9XlZYGiDCvpM= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af h1:wVe6/Ea46ZMeNkQjjBW6xcqyQA/j5e0D6GytH95g0gQ= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg= github.com/apache/arrow/go/v12 v12.0.0 h1:xtZE63VWl7qLdB0JObIXvvhGjoVNrQ9ciIHG2OK5cmc= github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= github.com/apex/logs v1.0.0 h1:adOwhOTeXzZTnVuEK13wuJNBFutP0sOfutRS8NY+G6A= github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a h1:2KLQMJ8msqoPHIPDufkxVcoTtcmE5+1sL9950m4R9Pk= github.com/aphistic/sweet v0.2.0 h1:I4z+fAUqvKfvZV/CHi5dV0QuwbmIvYYFDjG0Ss5QpAs= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY= github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= github.com/aws/aws-sdk-go-v2 v1.30.1 h1:4y/5Dvfrhd1MxRDD77SrfsDaj8kUkkljU7XE83NPV+o= github.com/aws/aws-sdk-go-v2 v1.30.1/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= github.com/aws/aws-sdk-go-v2/config v1.18.8 h1:lDpy0WM8AHsywOnVrOHaSMfpaiV2igOw8D7svkFkXVA= github.com/aws/aws-sdk-go-v2/config v1.18.25 h1:JuYyZcnMPBiFqn87L2cRppo+rNwgah6YwD3VuyvaW6Q= github.com/aws/aws-sdk-go-v2/config v1.27.23 h1:Cr/gJEa9NAS7CDAjbnB7tHYb3aLZI2gVggfmSAasDac= github.com/aws/aws-sdk-go-v2/config v1.27.23/go.mod h1:WMMYHqLCFu5LH05mFOF5tsq1PGEMfKbu083VKqLCd0o= github.com/aws/aws-sdk-go-v2/credentials v1.13.8 h1:vTrwTvv5qAwjWIGhZDSBH/oQHuIQjGmD232k01FUh6A= github.com/aws/aws-sdk-go-v2/credentials v1.13.24 h1:PjiYyls3QdCrzqUN35jMWtUK1vqVZ+zLfdOa/UPFDp0= github.com/aws/aws-sdk-go-v2/credentials v1.17.23 h1:G1CfmLVoO2TdQ8z9dW+JBc/r8+MqyPQhXCafNZcXVZo= github.com/aws/aws-sdk-go-v2/credentials v1.17.23/go.mod h1:V/DvSURn6kKgcuKEk4qwSwb/fZ2d++FFARtWSbXnLqY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9 h1:Aznqksmd6Rfv2HQN9cpqIV/lQRMaIpJkLLaJ1ZI76no= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9/go.mod h1:WQr3MY7AxGNxaqAtsDWn+fBxmd4XvLkzeqQ8P1VM0/w= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 h1:5SAoZ4jYpGH4721ZNoS1znQrhOfZinOhc4XuTXx/nVc= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13/go.mod h1:+rdA6ZLpaSeM7tSg/B0IEDinCIBJGmW8rKDFkYpP04g= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 h1:WIijqeaAO7TYFLbhsZmi2rgLEAtWOC1LhxCAVTJlSKw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13/go.mod h1:i+kbfa76PQbWw/ULoWnp51EYVWH4ENln76fLQE3lXT8= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33DF/c6q3RnZAmvQdQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15 h1:I9zMeF107l0rJrpnHpjEiiTSCKYAIw8mALiXcPsGBiA= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15/go.mod h1:9xWJ3Q/S6Ojusz1UIkfycgD1mGirJfLLKqq3LPT7WN8= github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 h1:/2gzjhQowRLarkkBOGPXSRnb8sQ2RVsjdG1C/UliK/c= github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGXsLBI6al083tpjJzY= github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 h1:p1GahKIjyMDZtiKoIn0/jAj/TkMzfzndDv5+zi2Mhgc= github.com/aws/aws-sdk-go-v2/service/sso v1.22.1/go.mod h1:/vWdhoIoYA5hYoPZ6fm7Sv4d8701PiG5VKe8/pPJL60= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 h1:Jfly6mRxk2ZOSlbCvZfKNS7TukSx1mIzhSsqZ/IGSZI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.1 h1:lCEv9f8f+zJ8kcFeAjRZsekLd/x5SAm96Cva+VbUdo8= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.1/go.mod h1:xyFHA4zGxgYkdD73VeezHt3vSKEG9EmFnGwoKlP00u4= github.com/aws/aws-sdk-go-v2/service/sts v1.18.0 h1:kOO++CYo50RcTFISESluhWEi5Prhg+gaSs4whWabiZU= github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 h1:2DQLAKDteoEDI8zpCzqBMaZlJuoE9iTYD0gFmXVax9E= github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 h1:+woJ607dllHJQtsnJLi52ycuqHMwlW+Wqm2Ppsfp4nQ= github.com/aws/aws-sdk-go-v2/service/sts v1.30.1/go.mod h1:jiNR3JqT15Dm+QWq2SRgh0x0bCNSRP2L25+CqPNpJlQ= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc= github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11/go.mod h1:je2KZ+LxaCNvCoKg32jtOIULcFogJKcL1ZWUaIBjKj0= github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a h1:W8b4lQ4tFF21aspRGoBuCNV6V2fFJBF+pm1J6OY8Lys= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e h1:l99YKCdrK4Lvb/zTupt0GMPfNbncAGf8Cv/t1sYLOg0= github.com/dave/jennifer v1.2.0 h1:S15ZkFMRoJ36mGAQgWL1tnr0NQJh9rZ8qatseX/VbBc= github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e h1:xURkGi4RydhyaYR6PzcyHTueQudxY4LgxN1oYEPJHa0= github.com/dave/rebecca v0.9.1 h1:jxVfdOxRirbXL28vXMvUvJ1in3djwkVKXCq339qhBL0= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7qg9dX7pc218= github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk= github.com/devigned/tab v0.1.1 h1:3mD6Kb1mUOYeLpJvTVSDwSg5ZsfSxfvxGRTxRsJsITA= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90 h1:WXb3TSNmHp2vHoCroCIB1foO/yQ36swABL8aOVeDpgg= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fullstorydev/grpcurl v1.6.0 h1:p8BB6VZF8O7w6MxGr3KJ9E6EVKaswCevSALK6FBtMzA= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo= github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ= github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd h1:hSkbZ9XSyjyBirMeqSqUrK+9HboWrweVlzRNqoBi2d4= github.com/gobuffalo/depgen v0.1.0 h1:31atYa/UW9V5q8vMJ+W6wd64OaaTHUrCUXER358zLM4= github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU= github.com/gobuffalo/flect v0.1.3 h1:3GQ53z7E3o00C/yy7Ko8VXqQXoJGLkrTQCLTF1EjoXU= github.com/gobuffalo/genny v0.1.1 h1:iQ0D6SpNXIxu52WESsD+KoQ7af2e3nCfnSBoSF/hKe0= github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211 h1:mSVZ4vj4khv+oThUfS+SQU3UuFIZ5Zo6UNcvK8E8Mz8= github.com/gobuffalo/gogen v0.1.1 h1:dLg+zb+uOyd/mKeQUYIbwbNmfRsr9hd/WtYWepmayhI= github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2 h1:8thhT+kUJMTMy3HlX4+y9Da+BNJck+p109tqqKp7WDs= github.com/gobuffalo/mapi v1.0.2 h1:fq9WcL1BYrm36SzK6+aAnZ8hcp+SrmnDyAxhNx8dvJk= github.com/gobuffalo/packd v0.1.0 h1:4sGKOD8yaYJ+dek1FDkwcxCHA40M4kfKgFHx8N2kwbU= github.com/gobuffalo/packr/v2 v2.2.0 h1:Ir9W9XIm9j7bhhkKE9cokvtTl1vBm62A/fene/ZCj6A= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5/VEHync2rJGi+epHNIeRSWjzGA+4= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/certificate-transparency-go v1.1.1 h1:6JHXZhXEvilMcTjR4MGZn5KV0IRkcFl4CJx5iHVhjFE= github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-pkcs11 v0.2.0 h1:5meDPB26aJ98f+K9G21f0AqZwo/S5BJMJh8nuhMbdsI= github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9 h1:OF1IPgv+F4NmqmJ98KTjdN97Vs1JxDPB3vbmYzV2dpk= github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/pprof v0.0.0-20181127221834-b4f47329b966 h1:zpjeU3rN5R22t0iguDarIAL75+2acLnDqGLOiPttMjk= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/rpmpack v0.0.0-20210410105602-e20c988a6f5a h1:XC048Fc/OB2rUl/BxruopEl2u/EP6cJNFveVxI1cvdk= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k= github.com/google/trillian v1.3.11 h1:pPzJPkK06mvXId1LHEAJxIegGgHzzp/FUnycPYfoCMI= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4= github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= github.com/hashicorp/memberlist v0.3.0 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy770So= github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0= github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/karrick/godirwalk v1.10.3 h1:lOpSw2vJP0y5eLBW906QwKsUK/fe/QDyoqM5rnnuPDY= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/letsencrypt/pkcs11key/v4 v4.0.0 h1:qLc/OznH7xMr5ARJgkZCCWk+EomQkiNTOoOF5LAgagc= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lyft/protoc-gen-star/v2 v2.0.3 h1:/3+/2sWyXeMLzKd1bX+ixWKgEMsULrIivpDsuaF441o= github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSWpyXmGpgOc62nK5HWUBKAGc3Qqa5k= github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1 h1:29NKShH4TWd3lxCDUhS4Xe16EWMA753dtIxYtwddklU= github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5 h1:0KqC6/sLy7fDpBdybhVkkv4Yz+PmB7c9Dz9z3dLW804= github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI= github.com/muesli/mango v0.1.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4= github.com/muesli/mango-cobra v1.2.0 h1:DQvjzAM0PMZr85Iv9LIMaYISpTOliMEg+uMFtNbYvWg= github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA= github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg= github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0= github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8= github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-proto-validators v0.2.0 h1:F6LFfmgVnfULfaRsQWBbe7F7ocuHCr9+7m+GAeDzNbQ= github.com/ncruces/sort v0.1.2 h1:zKQ9CA4fpHPF6xsUhRTfi5EEryspuBpe/QA4VWQOV1U= github.com/ncruces/sort v0.1.2/go.mod h1:vEJUTBJtebIuCMmXD18GKo5GJGhsay+xZFOoBEIXFmE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE= github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE= github.com/pseudomuto/protoc-gen-doc v1.3.2 h1:61vWZuxYa8D7Rn4h+2dgoTNqnluBmJya2MgbqO32z6g= github.com/pseudomuto/protokit v0.2.0 h1:hlnBDcy3YEDXH7kc9gV+NLaN0cDzhDvD1s7Y6FZ8RpM= github.com/quasilyte/go-ruleguard/dsl v0.3.19 h1:5+KTKb2YREUYiqZFEIuifFyBxlcCUPWgNZkWy71XS0Q= github.com/quasilyte/go-ruleguard/dsl v0.3.19/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71 h1:CNooiryw5aisadVfzneSZPswRWvnVW8hF1bS/vo8ReI= github.com/remyoudompheng/go-dbus v0.0.0-20121104212943-b7232d34b1d5 h1:CvqZS4QYHBRvx7AeFdimd16HCbLlYsvQMcKDACpJW/c= github.com/remyoudompheng/go-dbus v0.0.0-20121104212943-b7232d34b1d5/go.mod h1:+u151txRmLpwxBmpYn9z3d1sdJdjRPQpsXuYeY9jNls= github.com/remyoudompheng/go-liblzma v0.0.0-20190506200333-81bf2d431b96 h1:J8J/cgLDRuqXJnwIrRDBvtl+LLsdg7De74znW/BRRq4= github.com/remyoudompheng/go-liblzma v0.0.0-20190506200333-81bf2d431b96/go.mod h1:90HvCY7+oHHUKkbeMCiHt1WuFR2/hPJ9QrljDG+v6ls= github.com/remyoudompheng/go-misc v0.0.0-20190427085024-2d6ac652a50e h1:eTWZyPUnHcuGRDiryS/l2I7FfKjbU3IBx3IjqHPxuKU= github.com/remyoudompheng/go-misc v0.0.0-20190427085024-2d6ac652a50e/go.mod h1:80FQABjoFzZ2M5uEa6FUaJYEmqU2UOKojlFVak1UAwI= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/sagikazarmark/crypt v0.9.0 h1:fipzMFW34hFUEc4D7fsLQFtE7yElkpgyS2zruedRdZk= github.com/sagikazarmark/crypt v0.9.0/go.mod h1:RnH7sEhxfdnPm1z+XMgSLjWTEIjyK4z2dw6+4vHTMuo= github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b h1:+gCnWOZV8Z/8jehJ2CdqB47Z3S+SREmQcuXkRFLNsiI= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/shirou/gopsutil/v3 v3.22.4 h1:srAQaiX6jX/cYL6q29aE0m8lOskT9CurZ9N61YR3yoI= github.com/shirou/gopsutil/v3 v3.22.4/go.mod h1:D01hZJ4pVHPpCTZ3m3T2+wDF2YAGfd+H4ifUguaQzHM= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e h1:MZM7FHLqUHYI0Y/mQAt3d2aYa0SiNms/hFqC9qJYolM= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/smartystreets/gunit v1.4.2 h1:tyWYZffdPhQPfK5VsMQXfauwnJkqg7Tv5DLuQVYxq3Q= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spf13/afero v1.3.3 h1:p5gZEKLYoL7wh8VrJesMaYeNxdEd1v3cb4irOk9zB54= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/taylanisikdemir/cadence-idl v0.0.0-20250604205405-c829a7dc2e0c h1:EtJglCmzRnUbKX67v9svqAnYlrgPwCE+PUnG1kB2ooY= github.com/taylanisikdemir/cadence-idl v0.0.0-20250604205405-c829a7dc2e0c/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/timl3136/cadence-idl v0.0.0-20240716221550-3529a2618736 h1:UoaE2FX56QyLA0VGMJ16cll3haA5aZaFnfw5ZFDBzVU= github.com/timl3136/cadence-idl v0.0.0-20240716221550-3529a2618736/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/timl3136/cadence-idl v0.0.0-20240716224349-f9e143d54910 h1:cxtZkrE5AumMJDXFKVOUG+Q0GSHHM05t90/NKFdt+6E= github.com/timl3136/cadence-idl v0.0.0-20240716224349-f9e143d54910/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/timl3136/cadence-idl v0.0.0-20240716230216-2e5d5a0163bb h1:fxnwcw5UF0BT3goT+6avQ3sRddDSmwpN+x1h3enQj9k= github.com/timl3136/cadence-idl v0.0.0-20240716230216-2e5d5a0163bb/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/tj/go-buffer v1.1.0 h1:Lo2OsPHlIxXF24zApe15AbK3bJLAOvkkxEA6Ux4c47M= github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2 h1:eGaGNxrtoZf/mBURsnNQKDR7u50Klgcf2eFDQEnc8Bc= github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b h1:m74UWYy+HBs+jMFR9mdZU6shPewugMyH5+GV6LNgW8w= github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966 h1:j6JEOq5QWFker+d7mFQYOhjTZonQ7YkLTHm56dbn+yM= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/twitchtv/twirp v5.8.0+incompatible h1:DTfGS9u/jHbo34cBB+qhzVHRaAq+tRois71j8pvjQ5M= github.com/uber/cadence-idl v0.0.0-20250611195003-65a2b63c543d h1:jR6NMYjgXor7lwiMPOkH+MQvBkqa/q7QcBrVQJ804JY= github.com/uber/cadence-idl v0.0.0-20250611195003-65a2b63c543d/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTcjMJzCM= github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8 h1:EVObHAr8DqpoJCVv6KYTle8FEImKhtkfcZetNqxDoJQ= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c h1:/RwRVN9EdXAVtdHxP7Ndn/tfmM9/goiwU0QTnLBgS4w= go.etcd.io/etcd/api/v3 v3.5.6 h1:Cy2qx3npLcYqTKqGJzMypnMv2tiRyifZJ17BlWIWA7A= go.etcd.io/etcd/api/v3 v3.5.6/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= go.etcd.io/etcd/client/pkg/v3 v3.5.6 h1:TXQWYceBKqLp4sa87rcPs11SXxUA/mHwH975v+BDvLU= go.etcd.io/etcd/client/pkg/v3 v3.5.6/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= go.etcd.io/etcd/client/v2 v2.305.6 h1:fIDR0p4KMjw01MJMfUIDWdQbjo06PD6CeYM5z4EHLi0= go.etcd.io/etcd/client/v2 v2.305.6/go.mod h1:BHha8XJGe8vCIBfWBpbBLVZ4QjOIlfoouvOwydu63E0= go.etcd.io/etcd/client/v3 v3.5.6 h1:coLs69PWCXE9G4FKquzNaSHrRyMCAXwF+IX1tAPVO8E= go.etcd.io/etcd/client/v3 v3.5.6/go.mod h1:f6GRinRMCsFVv9Ht42EyY7nfsVGwrNO0WEoS2pRKzQk= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403 h1:rKyWXYDfrVOpMFBion4Pmx5sJbQreQNXycHvm4KwJSg= go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opentelemetry.io/otel v1.0.1 h1:4XKyXmfqJLOQ7feyV5DB6gsBFZ0ltB8vLtp6pj4JIcc= go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= go.opentelemetry.io/otel/trace v1.0.1 h1:StTeIH6Q3G4r0Fiw34LTokUFESZgIDUr0qIJ7mKmAfw= go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/thriftrw v1.29.1/go.mod h1:YcjXveberDd28/Bs34SwHy3yu85x/jB4UA2gIcz/Eo0= golang.org/x/arch v0.0.0-20180920145803-b19384d3c130 h1:Vsc61gop4hfHdzQNolo6Fi/sw7TnJ2yl3ZR4i7bYirs= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/mobile v0.0.0-20200801112145-973feb4309de h1:OVJ6QQUBAesB8CZijKDSsXX7xYVtUhrkY0gwMfbi4p4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b h1:Qh4dB5D/WpoUUp3lSod7qgoyEHbDGPUWjIbnqdqqe1k= google.golang.org/api v0.139.0 h1:A1TrCPgMmOiYu0AiNkvQIpIx+D8blHTDcJ5EogkP7LI= google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk= google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc h1:g3hIDl0jRNd9PPTs2uBzYuaD5mQuwOkZY0vSc0LR32o= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577 h1:ZX0eQu2J+jOO87sq8fQG8J/Nfp7D7BhHpixIE5EYK/k= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405 h1:o4S3HvTUEXgRsNSUQsALDVog0O9F/U1JJlHmmUN8Uas= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= lukechampine.com/adiantum v1.1.1 h1:4fp6gTxWCqpEbLy40ExiYDDED3oUNWx5cTqBCtPdZqA= lukechampine.com/adiantum v1.1.1/go.mod h1:LrAYVnTYLnUtE/yMp5bQr0HstAf060YUF8nM0B6+rUw= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= ================================================ FILE: host/activity_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "bytes" "encoding/binary" "errors" "fmt" "math/rand" "strconv" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/tasklist" ) func (s *IntegrationSuite) TestActivityHeartBeatWorkflow_Success() { id := "integration-heartbeat-test" wt := "integration-heartbeat-test-type" tl := "integration-heartbeat-test-tasklist" identity := "worker1" activityName := "activity_timer" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl header := &types.Header{ Fields: map[string][]byte{"tracing": []byte("sample data")}, } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, Header: header, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false activityCount := int32(1) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), Header: header, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(15), ScheduleToStartTimeoutSeconds: common.Int32Ptr(1), StartToCloseTimeoutSeconds: common.Int32Ptr(15), HeartbeatTimeoutSeconds: common.Int32Ptr(1), }, }}, nil } s.Logger.Info("Completing Workflow.") workflowComplete = true return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } activityExecutedCount := 0 atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Equal(id, execution.WorkflowID) s.Equal(activityName, activityType.Name) for i := 0; i < 10; i++ { s.Logger.Info("Heartbeating for activity", tag.WorkflowActivityID(activityID), tag.Counter(i)) ctx, cancel := createContext() _, err := s.Engine.RecordActivityTaskHeartbeat(ctx, &types.RecordActivityTaskHeartbeatRequest{ TaskToken: taskToken, Details: []byte("details")}) cancel() s.Nil(err) time.Sleep(10 * time.Millisecond) } activityExecutedCount++ return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil || err == tasklist.ErrNoTasks) err = poller.PollAndProcessActivityTask(false) s.True(err == nil || err == tasklist.ErrNoTasks) s.Logger.Info("Waiting for workflow to complete", tag.WorkflowRunID(we.RunID)) s.False(workflowComplete) _, err = poller.PollAndProcessDecisionTask(false, false) s.Nil(err) s.True(workflowComplete) s.True(activityExecutedCount == 1) // go over history and verify that the activity task scheduled event has header on it events := s.getHistory(s.DomainName, &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }) for _, event := range events { if *event.EventType == types.EventTypeActivityTaskScheduled { s.Equal(header, event.ActivityTaskScheduledEventAttributes.Header) } } } func (s *IntegrationSuite) TestActivityHeartbeatDetailsDuringRetry() { id := "integration-heartbeat-details-retry-test" wt := "integration-heartbeat-details-retry-type" tl := "integration-heartbeat-details-retry-tasklist" identity := "worker1" activityName := "activity_heartbeat_retry" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false activitiesScheduled := false dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activitiesScheduled { activitiesScheduled = true return nil, []*types.Decision{ { DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "0", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: nil, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(4), ScheduleToStartTimeoutSeconds: common.Int32Ptr(4), StartToCloseTimeoutSeconds: common.Int32Ptr(4), HeartbeatTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 3, MaximumIntervalInSeconds: 1, BackoffCoefficient: 1, ExpirationIntervalInSeconds: 100, }, }, }, }, nil } workflowComplete = true s.Logger.Info("Completing Workflow.") return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } activityExecutedCount := 0 heartbeatDetails := []byte("details") atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Equal(id, execution.WorkflowID) s.Equal(activityName, activityType.Name) var err error if activityExecutedCount == 0 { s.Logger.Info("Heartbeating for activity:", tag.WorkflowActivityID(activityID)) ctx, cancel := createContext() _, err = s.Engine.RecordActivityTaskHeartbeat(ctx, &types.RecordActivityTaskHeartbeatRequest{ TaskToken: taskToken, Details: heartbeatDetails}) cancel() s.Nil(err) // Trigger heartbeat timeout and retry time.Sleep(time.Second * 2) } else if activityExecutedCount == 1 { // return an error and retry err = errors.New("retryable-error") } activityExecutedCount++ return nil, false, err } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } describeWorkflowExecution := func() (*types.DescribeWorkflowExecutionResponse, error) { ctx, cancel := createContext() defer cancel() return s.Engine.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) } _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) for i := 0; i != 3; i++ { err = poller.PollAndProcessActivityTask(false) if i == 0 { // first time, hearbeat timeout, respond activity complete will fail s.Error(err) } else { // second time, retryable error s.Nil(err) } dweResponse, err := describeWorkflowExecution() s.Nil(err) pendingActivities := dweResponse.GetPendingActivities() if i == 2 { // third time, complete activity, no pending info s.Equal(0, len(pendingActivities)) } else { s.Equal(1, len(pendingActivities)) pendingActivity := pendingActivities[0] s.Equal(int32(3), pendingActivity.GetMaximumAttempts()) s.Equal(int32(i+1), pendingActivity.GetAttempt()) s.Equal(types.PendingActivityStateScheduled, pendingActivity.GetState()) if i == 0 { s.Equal("cadenceInternal:Timeout HEARTBEAT", pendingActivity.GetLastFailureReason()) s.Nil(pendingActivity.GetLastFailureDetails()) } else { // i == 1 expectedErrString := "retryable-error" s.Equal(expectedErrString, pendingActivity.GetLastFailureReason()) s.Equal([]byte(expectedErrString), pendingActivity.GetLastFailureDetails()) } s.Equal(identity, pendingActivity.GetLastWorkerIdentity()) scheduledTS := pendingActivity.ScheduledTimestamp lastHeartbeatTS := pendingActivity.LastHeartbeatTimestamp expirationTS := pendingActivity.ExpirationTimestamp s.NotNil(scheduledTS) s.NotNil(lastHeartbeatTS) s.NotNil(expirationTS) s.Nil(pendingActivity.LastStartedTimestamp) s.True(*scheduledTS > *lastHeartbeatTS) s.True(*expirationTS > *scheduledTS) s.Equal(heartbeatDetails, pendingActivity.GetHeartbeatDetails()) } } _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) s.True(workflowComplete) s.Equal(3, activityExecutedCount) } func (s *IntegrationSuite) TestActivityRetry() { id := "integration-activity-retry-test" wt := "integration-activity-retry-type" tl := "integration-activity-retry-tasklist" identity := "worker1" identity2 := "worker2" activityName := "activity_retry" timeoutActivityName := "timeout_activity" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false activitiesScheduled := false var activityAScheduled, activityAFailed, activityBScheduled, activityBTimeout *types.HistoryEvent dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activitiesScheduled { activitiesScheduled = true return nil, []*types.Decision{ { DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "A", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: []byte("1"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(4), ScheduleToStartTimeoutSeconds: common.Int32Ptr(4), StartToCloseTimeoutSeconds: common.Int32Ptr(4), HeartbeatTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 3, MaximumIntervalInSeconds: 1, NonRetriableErrorReasons: []string{"bad-bug"}, BackoffCoefficient: 1, ExpirationIntervalInSeconds: 100, }, }, }, { DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "B", ActivityType: &types.ActivityType{Name: timeoutActivityName}, TaskList: &types.TaskList{Name: "no_worker_tasklist"}, Input: []byte("2"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(5), ScheduleToStartTimeoutSeconds: common.Int32Ptr(5), StartToCloseTimeoutSeconds: common.Int32Ptr(5), HeartbeatTimeoutSeconds: common.Int32Ptr(0), }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { switch event.GetEventType() { case types.EventTypeActivityTaskScheduled: switch event.ActivityTaskScheduledEventAttributes.GetActivityID() { case "A": activityAScheduled = event case "B": activityBScheduled = event } case types.EventTypeActivityTaskFailed: if event.ActivityTaskFailedEventAttributes.GetScheduledEventID() == activityAScheduled.ID { activityAFailed = event } case types.EventTypeActivityTaskTimedOut: if event.ActivityTaskTimedOutEventAttributes.GetScheduledEventID() == activityBScheduled.ID { activityBTimeout = event } } } } if activityAFailed != nil && activityBTimeout != nil { s.Logger.Info("Completing Workflow.") workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } return nil, []*types.Decision{}, nil } activityExecutedCount := 0 atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Equal(id, execution.WorkflowID) s.Equal(activityName, activityType.Name) var err error if activityExecutedCount == 0 { err = errors.New("bad-luck-please-retry") } else if activityExecutedCount == 1 { err = errors.New("bad-bug") } activityExecutedCount++ return nil, false, err } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } poller2 := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } describeWorkflowExecution := func() (*types.DescribeWorkflowExecutionResponse, error) { ctx, cancel := createContext() defer cancel() return s.Engine.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) } _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) err = poller.PollAndProcessActivityTask(false) s.True(err == nil || err == tasklist.ErrNoTasks, err) descResp, err := describeWorkflowExecution() s.Nil(err) for _, pendingActivity := range descResp.GetPendingActivities() { if pendingActivity.GetActivityID() == "A" { expectedErrString := "bad-luck-please-retry" s.Equal(expectedErrString, pendingActivity.GetLastFailureReason()) s.Equal([]byte(expectedErrString), pendingActivity.GetLastFailureDetails()) s.Equal(identity, pendingActivity.GetLastWorkerIdentity()) } } err = poller2.PollAndProcessActivityTask(false) s.True(err == nil || err == tasklist.ErrNoTasks, err) descResp, err = describeWorkflowExecution() s.Nil(err) for _, pendingActivity := range descResp.GetPendingActivities() { if pendingActivity.GetActivityID() == "A" { expectedErrString := "bad-bug" s.Equal(expectedErrString, pendingActivity.GetLastFailureReason()) s.Equal([]byte(expectedErrString), pendingActivity.GetLastFailureDetails()) s.Equal(identity2, pendingActivity.GetLastWorkerIdentity()) } } s.Logger.Info("Waiting for workflow to complete", tag.WorkflowRunID(we.RunID)) for i := 0; i < 3; i++ { s.False(workflowComplete) s.Logger.Info("Processing decision task:", tag.Counter(i)) _, err := poller.PollAndProcessDecisionTaskWithoutRetry(false, false) if err != nil { s.printWorkflowHistory(s.DomainName, &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }) } s.Nil(err, "Poll for decision task failed.") if workflowComplete { break } } s.True(workflowComplete) s.True(activityExecutedCount == 2) } func (s *IntegrationSuite) TestActivityHeartBeatWorkflow_Timeout() { id := "integration-heartbeat-timeout-test" wt := "integration-heartbeat-timeout-test-type" tl := "integration-heartbeat-timeout-test-tasklist" identity := "worker1" activityName := "activity_timer" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false activityCount := int32(1) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { s.Logger.Info("Calling DecisionTask Handler: %d, %d.", tag.Counter(int(activityCounter)), tag.Number(int64(activityCount))) if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(15), ScheduleToStartTimeoutSeconds: common.Int32Ptr(1), StartToCloseTimeoutSeconds: common.Int32Ptr(15), HeartbeatTimeoutSeconds: common.Int32Ptr(1), }, }}, nil } workflowComplete = true return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } activityExecutedCount := 0 atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Equal(id, execution.WorkflowID) s.Equal(activityName, activityType.Name) // Timing out more than HB time. time.Sleep(2 * time.Second) activityExecutedCount++ return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil || err == tasklist.ErrNoTasks) err = poller.PollAndProcessActivityTask(false) s.Error(err) s.Logger.Info("Waiting for workflow to complete", tag.WorkflowRunID(we.RunID)) s.False(workflowComplete) _, err = poller.PollAndProcessDecisionTask(false, false) s.Nil(err) s.True(workflowComplete) } func (s *IntegrationSuite) TestActivityTimeouts() { id := "integration-activity-timeout-test" wt := "integration-activity-timeout-test-type" tl := "integration-activity-timeout-test-tasklist" identity := "worker1" activityName := "timeout_activity" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(300), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false workflowFailed := false activitiesScheduled := false activitiesMap := map[int64]*types.HistoryEvent{} failWorkflow := false failReason := "" var activityATimedout, activityBTimedout, activityCTimedout, activityDTimedout bool dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activitiesScheduled { activitiesScheduled = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "A", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: "NoWorker"}, Input: []byte("ScheduleToStart"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(35), ScheduleToStartTimeoutSeconds: common.Int32Ptr(3), // ActivityID A is expected to timeout using ScheduleToStart StartToCloseTimeoutSeconds: common.Int32Ptr(30), HeartbeatTimeoutSeconds: common.Int32Ptr(0), }, }, { DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "B", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: []byte("ScheduleToClose"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(7), // ActivityID B is expected to timeout using ScheduleClose ScheduleToStartTimeoutSeconds: common.Int32Ptr(5), StartToCloseTimeoutSeconds: common.Int32Ptr(10), HeartbeatTimeoutSeconds: common.Int32Ptr(0), }, }, { DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "C", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: []byte("StartToClose"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(15), ScheduleToStartTimeoutSeconds: common.Int32Ptr(1), StartToCloseTimeoutSeconds: common.Int32Ptr(5), // ActivityID C is expected to timeout using StartToClose HeartbeatTimeoutSeconds: common.Int32Ptr(0), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumIntervalInSeconds: 1, BackoffCoefficient: 1, ExpirationIntervalInSeconds: 3, // activity expiration time will not be extended, so it won't retry }, }, }, { DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "D", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: []byte("Heartbeat"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(35), ScheduleToStartTimeoutSeconds: common.Int32Ptr(20), StartToCloseTimeoutSeconds: common.Int32Ptr(15), HeartbeatTimeoutSeconds: common.Int32Ptr(3), // ActivityID D is expected to timeout using Heartbeat }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { if event.GetEventType() == types.EventTypeActivityTaskScheduled { activitiesMap[event.ID] = event } if event.GetEventType() == types.EventTypeActivityTaskTimedOut { timeoutEvent := event.ActivityTaskTimedOutEventAttributes scheduledEvent, ok := activitiesMap[timeoutEvent.GetScheduledEventID()] if !ok { return nil, []*types.Decision{{ DecisionType: types.DecisionTypeFailWorkflowExecution.Ptr(), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr("ScheduledEvent not found."), }, }}, nil } switch timeoutEvent.GetTimeoutType() { case types.TimeoutTypeScheduleToStart: if scheduledEvent.ActivityTaskScheduledEventAttributes.GetActivityID() == "A" { activityATimedout = true } else { failWorkflow = true failReason = "ActivityID A is expected to timeout with ScheduleToStart" } case types.TimeoutTypeScheduleToClose: if scheduledEvent.ActivityTaskScheduledEventAttributes.GetActivityID() == "B" { activityBTimedout = true } else { failWorkflow = true failReason = "ActivityID B is expected to timeout with ScheduleToClose" } case types.TimeoutTypeStartToClose: if scheduledEvent.ActivityTaskScheduledEventAttributes.GetActivityID() == "C" { activityCTimedout = true } else { failWorkflow = true failReason = "ActivityID C is expected to timeout with StartToClose" } case types.TimeoutTypeHeartbeat: if scheduledEvent.ActivityTaskScheduledEventAttributes.GetActivityID() == "D" { activityDTimedout = true } else { failWorkflow = true failReason = "ActivityID D is expected to timeout with Heartbeat" } } } } } if failWorkflow { s.Logger.Error("Failing types.") workflowFailed = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeFailWorkflowExecution.Ptr(), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(failReason), }, }}, nil } if activityATimedout && activityBTimedout && activityCTimedout && activityDTimedout { s.Logger.Info("Completing Workflow.") workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } return nil, []*types.Decision{}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Equal(id, execution.WorkflowID) s.Equal(activityName, activityType.Name) timeoutType := string(input) switch timeoutType { case "ScheduleToStart": s.Fail("Activity A not expected to be started.") case "ScheduleToClose": s.Logger.Info("Sleeping activityB for 6 seconds.") time.Sleep(7 * time.Second) case "StartToClose": s.Logger.Info("Sleeping activityC for 8 seconds.") time.Sleep(8 * time.Second) case "Heartbeat": s.Logger.Info("Starting hearbeat activity.") go func() { for i := 0; i < 6; i++ { s.Logger.Info("Heartbeating for activity", tag.WorkflowActivityID(activityID), tag.Counter(i)) ctx, cancel := createContext() _, err := s.Engine.RecordActivityTaskHeartbeat(ctx, &types.RecordActivityTaskHeartbeatRequest{ TaskToken: taskToken, Details: []byte(strconv.Itoa(i))}) cancel() s.Nil(err) time.Sleep(1 * time.Second) } s.Logger.Info("End Heartbeating.") }() s.Logger.Info("Sleeping hearbeat activity.") time.Sleep(10 * time.Second) } return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil || err == tasklist.ErrNoTasks) for i := 0; i < 3; i++ { go func() { err = poller.PollAndProcessActivityTask(false) s.Logger.Info("Activity Processing Completed. Error", tag.Error(err)) }() } s.Logger.Info("Waiting for workflow to complete", tag.WorkflowRunID(we.RunID)) for i := 0; i < 10; i++ { s.Logger.Info("Processing decision task: %v", tag.Counter(i)) _, err := poller.PollAndProcessDecisionTask(false, false) s.Nil(err, "Poll for decision task failed.") if workflowComplete || workflowFailed { break } } s.True(workflowComplete) s.False(workflowFailed) } func (s *IntegrationSuite) TestActivityHeartbeatTimeouts() { id := "integration-activity-heartbeat-timeout-test" wt := "integration-activity-heartbeat-timeout-test-type" tl := "integration-activity-heartbeat-timeout-test-tasklist" identity := "worker1" activityName := "timeout_activity" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(70), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false activitiesScheduled := false lastHeartbeatMap := make(map[int64]int) failWorkflow := false failReason := "" activityCount := 10 activitiesTimedout := 0 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activitiesScheduled { activitiesScheduled = true decisions := []*types.Decision{} for i := 0; i < activityCount; i++ { aID := fmt.Sprintf("activity_%v", i) d := &types.Decision{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: aID, ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: []byte("Heartbeat"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(60), ScheduleToStartTimeoutSeconds: common.Int32Ptr(5), StartToCloseTimeoutSeconds: common.Int32Ptr(60), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, } decisions = append(decisions, d) } return nil, decisions, nil } else if previousStartedEventID > 0 { ProcessLoop: for _, event := range history.Events[previousStartedEventID:] { if event.GetEventType() == types.EventTypeActivityTaskScheduled { lastHeartbeatMap[event.ID] = 0 } if event.GetEventType() == types.EventTypeActivityTaskCompleted || event.GetEventType() == types.EventTypeActivityTaskFailed { failWorkflow = true failReason = "Expected activities to timeout but seeing completion instead" } if event.GetEventType() == types.EventTypeActivityTaskTimedOut { timeoutEvent := event.ActivityTaskTimedOutEventAttributes _, ok := lastHeartbeatMap[timeoutEvent.GetScheduledEventID()] if !ok { failWorkflow = true failReason = "ScheduledEvent not found." break ProcessLoop } switch timeoutEvent.GetTimeoutType() { case types.TimeoutTypeHeartbeat: activitiesTimedout++ scheduleID := timeoutEvent.GetScheduledEventID() lastHeartbeat, _ := strconv.Atoi(string(timeoutEvent.Details)) lastHeartbeatMap[scheduleID] = lastHeartbeat default: failWorkflow = true failReason = "Expected Heartbeat timeout but recieved another timeout" break ProcessLoop } } } } if failWorkflow { s.Logger.Error("Failing types. Reason: %v", tag.Value(failReason)) workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeFailWorkflowExecution.Ptr(), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(failReason), }, }}, nil } if activitiesTimedout == activityCount { s.Logger.Info("Completing Workflow.") workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } return nil, []*types.Decision{}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Logger.Info("Starting heartbeat activity. ID", tag.WorkflowActivityID(activityID)) for i := 0; i < 10; i++ { if !workflowComplete { s.Logger.Info("Heartbeating for activity", tag.WorkflowActivityID(activityID), tag.Counter(i)) ctx, cancel := createContext() _, err := s.Engine.RecordActivityTaskHeartbeat(ctx, &types.RecordActivityTaskHeartbeatRequest{ TaskToken: taskToken, Details: []byte(strconv.Itoa(i))}) cancel() if err != nil { s.Logger.Error("Activity heartbeat failed", tag.WorkflowActivityID(activityID), tag.Counter(i), tag.Error(err)) } secondsToSleep := rand.Intn(3) s.Logger.Info("Activity ID '%v' sleeping for: %v seconds", tag.WorkflowActivityID(activityID), tag.Number(int64(secondsToSleep))) time.Sleep(time.Duration(secondsToSleep) * time.Second) } } s.Logger.Info("End Heartbeating.", tag.WorkflowActivityID(activityID)) s.Logger.Info("Sleeping activity before completion", tag.WorkflowActivityID(activityID)) time.Sleep(7 * time.Second) return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil || err == tasklist.ErrNoTasks) for i := 0; i < activityCount; i++ { go func() { err := poller.PollAndProcessActivityTask(false) s.Logger.Info("Activity Processing Completed.", tag.Error(err)) }() } s.Logger.Info("Waiting for workflow to complete", tag.WorkflowRunID(we.RunID)) for i := 0; i < 10; i++ { s.Logger.Info("Processing decision task", tag.Counter(i)) _, err := poller.PollAndProcessDecisionTask(false, false) s.Nil(err, "Poll for decision task failed.") if workflowComplete { break } } s.True(workflowComplete) s.False(failWorkflow, failReason) s.Equal(activityCount, activitiesTimedout) s.Equal(activityCount, len(lastHeartbeatMap)) for aID, lastHeartbeat := range lastHeartbeatMap { s.Logger.Info("Last heartbeat for activity with scheduleID", tag.Counter(int(aID)), tag.Number(int64(lastHeartbeat))) s.Equal(9, lastHeartbeat) } } func (s *IntegrationSuite) TestActivityCancellation() { id := "integration-activity-cancellation-test" wt := "integration-activity-cancellation-test-type" tl := "integration-activity-cancellation-test-tasklist" identity := "worker1" activityName := "activity_timer" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution: response", tag.WorkflowRunID(we.GetRunID())) activityCounter := int32(0) scheduleActivity := true requestCancellation := false dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if scheduleActivity { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(15), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(15), HeartbeatTimeoutSeconds: common.Int32Ptr(0), }, }}, nil } if requestCancellation { return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), }, }}, nil } s.Logger.Info("Completing Workflow.") return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } activityExecutedCount := 0 atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Equal(id, execution.WorkflowID) s.Equal(activityName, activityType.GetName()) for i := 0; i < 10; i++ { s.Logger.Info("Heartbeating for activity", tag.WorkflowActivityID(activityID), tag.Counter(i)) ctx, cancel := createContext() response, err := s.Engine.RecordActivityTaskHeartbeat(ctx, &types.RecordActivityTaskHeartbeatRequest{ TaskToken: taskToken, Details: []byte("details")}) cancel() if response.CancelRequested { return []byte("Activity Cancelled."), true, nil } s.Nil(err) time.Sleep(10 * time.Millisecond) } activityExecutedCount++ return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil || err == tasklist.ErrNoTasks, err) cancelCh := make(chan struct{}) go func() { s.Logger.Info("Trying to cancel the task in a different thread.") scheduleActivity = false requestCancellation = true _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil || err == tasklist.ErrNoTasks, err) cancelCh <- struct{}{} }() err = poller.PollAndProcessActivityTask(false) s.True(err == nil || err == tasklist.ErrNoTasks, err) <-cancelCh s.Logger.Info("Waiting for workflow to complete", tag.WorkflowRunID(we.RunID)) } func (s *IntegrationSuite) TestActivityCancellationNotStarted() { id := "integration-activity-notstarted-cancellation-test" wt := "integration-activity-notstarted-cancellation-test-type" tl := "integration-activity-notstarted-cancellation-test-tasklist" identity := "worker1" activityName := "activity_notstarted" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecutionn", tag.WorkflowRunID(we.GetRunID())) activityCounter := int32(0) scheduleActivity := true requestCancellation := false dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if scheduleActivity { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) s.Logger.Info("Scheduling activity.") return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(15), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(15), HeartbeatTimeoutSeconds: common.Int32Ptr(0), }, }}, nil } if requestCancellation { s.Logger.Info("Requesting cancellation.") return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), }, }}, nil } s.Logger.Info("Completing Workflow.") return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // dummy activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Fail("activity should not run") return nil, false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil || err == tasklist.ErrNoTasks) // Send signal so that worker can send an activity cancel signalName := "my signal" signalInput := []byte("my signal input.") ctx, cancel = createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, SignalName: signalName, Input: signalInput, Identity: identity, }) s.Nil(err) // Process signal in decider and send request cancellation scheduleActivity = false requestCancellation = true _, err = poller.PollAndProcessDecisionTask(false, false) s.Nil(err) scheduleActivity = false requestCancellation = false _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil || err == tasklist.ErrNoTasks) } ================================================ FILE: host/archival_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "bytes" "context" "encoding/binary" "fmt" "strconv" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const ( retryLimit = 20 retryBackoffTime = 200 * time.Millisecond ) func (s *IntegrationSuite) TestArchival_TimerQueueProcessor() { s.True(s.TestCluster.archiverBase.metadata.GetHistoryConfig().ClusterConfiguredForArchival()) domainID := s.getDomainID(s.ArchivalDomainName) WorkflowID := "archival-timer-queue-processor-workflow-id" workflowType := "archival-timer-queue-processor-type" taskList := "archival-timer-queue-processor-task-list" numActivities := 1 numRuns := 1 RunID := s.startAndFinishWorkflow(WorkflowID, workflowType, taskList, s.ArchivalDomainName, domainID, numActivities, numRuns)[0] execution := &types.WorkflowExecution{ WorkflowID: WorkflowID, RunID: RunID, } s.True(s.isHistoryArchived(s.ArchivalDomainName, execution)) s.True(s.isHistoryDeleted(domainID, execution)) s.True(s.isMutableStateDeleted(domainID, execution)) } func (s *IntegrationSuite) TestArchival_ContinueAsNew() { s.True(s.TestCluster.archiverBase.metadata.GetHistoryConfig().ClusterConfiguredForArchival()) domainID := s.getDomainID(s.ArchivalDomainName) WorkflowID := "archival-continueAsNew-workflow-id" workflowType := "archival-continueAsNew-workflow-type" taskList := "archival-continueAsNew-task-list" numActivities := 1 numRuns := 5 RunIDs := s.startAndFinishWorkflow(WorkflowID, workflowType, taskList, s.ArchivalDomainName, domainID, numActivities, numRuns) for _, RunID := range RunIDs { execution := &types.WorkflowExecution{ WorkflowID: WorkflowID, RunID: RunID, } s.True(s.isHistoryArchived(s.ArchivalDomainName, execution)) s.True(s.isHistoryDeleted(domainID, execution)) s.True(s.isMutableStateDeleted(domainID, execution)) } } func (s *IntegrationSuite) TestArchival_ArchiverWorker() { s.True(s.TestCluster.archiverBase.metadata.GetHistoryConfig().ClusterConfiguredForArchival()) domainID := s.getDomainID(s.ArchivalDomainName) WorkflowID := "archival-archiver-worker-workflow-id" workflowType := "archival-archiver-worker-workflow-type" taskList := "archival-archiver-worker-task-list" numActivities := 10 RunID := s.startAndFinishWorkflow(WorkflowID, workflowType, taskList, s.ArchivalDomainName, domainID, numActivities, 1)[0] execution := &types.WorkflowExecution{ WorkflowID: WorkflowID, RunID: RunID, } s.True(s.isHistoryArchived(s.ArchivalDomainName, execution)) s.True(s.isHistoryDeleted(domainID, execution)) s.True(s.isMutableStateDeleted(domainID, execution)) } func (s *IntegrationSuite) TestVisibilityArchival() { s.True(s.TestCluster.archiverBase.metadata.GetVisibilityConfig().ClusterConfiguredForArchival()) domainID := s.getDomainID(s.ArchivalDomainName) WorkflowID := "archival-visibility-workflow-id" workflowType := "archival-visibility-workflow-type" taskList := "archival-visibility-task-list" numActivities := 3 numRuns := 5 startTime := time.Now().UnixNano() s.startAndFinishWorkflow(WorkflowID, workflowType, taskList, s.ArchivalDomainName, domainID, numActivities, numRuns) s.startAndFinishWorkflow("some other WorkflowID", "some other workflow type", taskList, s.ArchivalDomainName, domainID, numActivities, numRuns) endTime := time.Now().UnixNano() var executions []*types.WorkflowExecutionInfo for i := 0; i != retryLimit; i++ { executions = []*types.WorkflowExecutionInfo{} request := &types.ListArchivedWorkflowExecutionsRequest{ Domain: s.ArchivalDomainName, PageSize: 2, Query: fmt.Sprintf("CloseTime >= %v and CloseTime <= %v and WorkflowType = '%s'", startTime, endTime, workflowType), } for len(executions) == 0 || request.NextPageToken != nil { ctx, cancel := createContext() response, err := s.Engine.ListArchivedWorkflowExecutions(ctx, request) cancel() s.NoError(err) s.NotNil(response) executions = append(executions, response.GetExecutions()...) request.NextPageToken = response.NextPageToken } if len(executions) == numRuns { break } time.Sleep(retryBackoffTime) } for _, execution := range executions { s.Equal(WorkflowID, execution.GetExecution().GetWorkflowID()) s.Equal(workflowType, execution.GetType().GetName()) s.NotZero(execution.StartTime) s.NotZero(execution.ExecutionTime) s.NotZero(execution.CloseTime) } } func (s *IntegrationSuite) getDomainID(domain string) string { ctx, cancel := createContext() defer cancel() domainResp, err := s.Engine.DescribeDomain(ctx, &types.DescribeDomainRequest{ Name: common.StringPtr(s.ArchivalDomainName), }) s.Nil(err) return domainResp.DomainInfo.GetUUID() } func (s *IntegrationSuite) isHistoryArchived(domain string, execution *types.WorkflowExecution) bool { request := &types.GetWorkflowExecutionHistoryRequest{ Domain: s.ArchivalDomainName, Execution: execution, } for i := 0; i < retryLimit; i++ { ctx, cancel := createContext() getHistoryResp, err := s.Engine.GetWorkflowExecutionHistory(ctx, request) cancel() if err == nil && getHistoryResp != nil && getHistoryResp.GetArchived() { return true } time.Sleep(retryBackoffTime) } return false } func (s *IntegrationSuite) isHistoryDeleted(domainID string, execution *types.WorkflowExecution) bool { shardID := common.WorkflowIDToHistoryShard(execution.WorkflowID, s.TestClusterConfig.HistoryConfig.NumHistoryShards) request := &persistence.GetHistoryTreeRequest{ TreeID: execution.GetRunID(), ShardID: common.IntPtr(shardID), DomainName: s.DomainName, } for i := 0; i < retryLimit; i++ { ctx, cancel := context.WithTimeout(context.Background(), defaultTestPersistenceTimeout) resp, err := s.TestCluster.testBase.HistoryV2Mgr.GetHistoryTree(ctx, request) s.Nil(err) cancel() if len(resp.Branches) == 0 { return true } time.Sleep(retryBackoffTime) } return false } func (s *IntegrationSuite) isMutableStateDeleted(domainID string, execution *types.WorkflowExecution) bool { request := &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: *execution, } for i := 0; i < retryLimit; i++ { ctx, cancel := context.WithTimeout(context.Background(), defaultTestPersistenceTimeout) _, err := s.TestCluster.testBase.ExecutionManager.GetWorkflowExecution(ctx, request) cancel() if _, ok := err.(*types.EntityNotExistsError); ok { return true } time.Sleep(retryBackoffTime) } return false } func (s *IntegrationSuite) startAndFinishWorkflow(id, wt, tl, domain, domainID string, numActivities, numRuns int) []string { identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{ Name: wt, } taskList := &types.TaskList{ Name: tl, } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: domain, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) RunIDs := make([]string, numRuns) workflowComplete := false activityCount := int32(numActivities) activityCounter := int32(0) expectedActivityID := int32(1) runCounter := 1 dtHandler := func( execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID int64, startedEventID int64, history *types.History, ) ([]byte, []*types.Decision, error) { RunIDs[runCounter-1] = execution.GetRunID() if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } if runCounter < numRuns { activityCounter = int32(0) expectedActivityID = int32(1) runCounter++ return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeContinueAsNewWorkflowExecution.Ptr(), ContinueAsNewWorkflowExecutionDecisionAttributes: &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: workflowType, TaskList: &types.TaskList{Name: tl}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), }, }}, nil } workflowComplete = true return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } atHandler := func( execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte, ) ([]byte, bool, error) { s.Equal(id, execution.WorkflowID) s.Equal(activityName, activityType.Name) id, _ := strconv.Atoi(activityID) s.Equal(int(expectedActivityID), id) buf := bytes.NewReader(input) var in int32 binary.Read(buf, binary.LittleEndian, &in) s.Equal(expectedActivityID, in) expectedActivityID++ return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: domain, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } for run := 0; run < numRuns; run++ { for i := 0; i < numActivities; i++ { _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) if i%2 == 0 { err = poller.PollAndProcessActivityTask(false) } else { // just for testing respondActivityTaskCompleteByID err = poller.PollAndProcessActivityTaskWithID(false) } s.Logger.Info("PollAndProcessActivityTask", tag.Error(err)) s.Nil(err) } _, err = poller.PollAndProcessDecisionTask(false, false) s.Nil(err) } s.True(workflowComplete) for run := 1; run < numRuns; run++ { s.NotEqual(RunIDs[run-1], RunIDs[run]) } return RunIDs } ================================================ FILE: host/async_wf_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:build !race && asyncwfintegration // +build !race,asyncwfintegration /* To run locally: 1. Stop the previous run if any docker compose -f docker/github_actions/docker-compose-local-async-wf.yml down 2. Build the integration-test-async-wf image docker compose -f docker/github_actions/docker-compose-local-async-wf.yml build integration-test-async-wf 3. Run the test in the docker container docker compose -f docker/github_actions/docker-compose-local-async-wf.yml run --rm integration-test-async-wf 4. Full test run logs can be found at test.log file */ package host import ( "flag" "fmt" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/types" _ "github.com/uber/cadence/common/asyncworkflow/queue/kafka" // needed to load kafka asyncworkflow queue ) func TestAsyncWFIntegrationSuite(t *testing.T) { flag.Parse() confPath := "testdata/integration_async_wf_with_kafka_cluster.yaml" clusterConfig, err := GetTestClusterConfig(confPath) if err != nil { t.Fatalf("failed creating cluster config from %s, err: %v", confPath, err) } clusterConfig.TimeSource = clock.NewMockedTimeSource() clusterConfig.FrontendDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.FrontendFailoverCoolDown: time.Duration(0), dynamicproperties.EnableReadFromClosedExecutionV2: true, } testCluster := NewPersistenceTestCluster(t, clusterConfig) s := new(AsyncWFIntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } func (s *AsyncWFIntegrationSuite) SetupSuite() { s.SetupLogger() s.Logger.Info("Running integration test against test cluster") clusterMetadata := NewClusterMetadata(s.T(), s.TestClusterConfig) dc := persistence.DynamicConfiguration{ EnableCassandraAllConsistencyLevelDelete: dynamicproperties.GetBoolPropertyFn(true), PersistenceSampleLoggingRate: dynamicproperties.GetIntPropertyFn(100), EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(true), EnableHistoryTaskDualWriteMode: dynamicproperties.GetBoolPropertyFn(true), ReadNoSQLHistoryTaskFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(true), HistoryNodeDeleteBatchSize: dynamicproperties.GetIntPropertyFn(1000), } params := pt.TestBaseParams{ DefaultTestCluster: s.DefaultTestCluster, VisibilityTestCluster: s.VisibilityTestCluster, ClusterMetadata: clusterMetadata, DynamicConfiguration: dc, } cluster, err := NewCluster(s.T(), s.TestClusterConfig, s.Logger, params) s.Require().NoError(err) s.TestCluster = cluster s.Engine = s.TestCluster.GetFrontendClient() s.AdminClient = s.TestCluster.GetAdminClient() s.DomainName = s.RandomizeStr("integration-test-domain") s.Require().NoError(s.RegisterDomain(s.DomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.SecondaryDomainName = s.RandomizeStr("unused-test-domain") s.Require().NoError(s.RegisterDomain(s.SecondaryDomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.domainCacheRefresh() } func (s *AsyncWFIntegrationSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *AsyncWFIntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() } func (s *AsyncWFIntegrationSuite) TestStartWorkflowExecutionAsync() { tests := []struct { name string wantStartFailure bool asyncWFCfg *types.AsyncWorkflowConfiguration secondaryCfg *types.AsyncWorkflowConfiguration }{ { name: "start workflow execution async fails because domain missing async queue", wantStartFailure: true, secondaryCfg: &types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-async-wf-queue", }, }, { name: "start workflow execution async fails because async queue is disabled", asyncWFCfg: &types.AsyncWorkflowConfiguration{ Enabled: false, }, wantStartFailure: true, secondaryCfg: &types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-async-wf-queue", }, }, { name: "start workflow execution async succeeds and workflow starts", asyncWFCfg: &types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "test-async-wf-queue", }, secondaryCfg: &types.AsyncWorkflowConfiguration{ Enabled: false, PredefinedQueueName: "test-async-wf-queue", }, }, } for _, tc := range tests { tc := tc s.T().Run(tc.name, func(t *testing.T) { // advance the time so each test has a unique start time s.TestClusterConfig.TimeSource.Advance(time.Second) ctx, cancel := createContext() defer cancel() if tc.asyncWFCfg != nil { _, err := s.AdminClient.UpdateDomainAsyncWorkflowConfiguraton(ctx, &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: s.DomainName, Configuration: tc.asyncWFCfg, }) if err != nil { t.Fatalf("UpdateDomainAsyncWorkflowConfiguraton() failed: %v", err) } s.domainCacheRefresh() } if tc.secondaryCfg != nil { _, err := s.AdminClient.UpdateDomainAsyncWorkflowConfiguraton(ctx, &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: s.SecondaryDomainName, Configuration: tc.secondaryCfg, }) if err != nil { t.Fatalf("UpdateDomainAsyncWorkflowConfiguraton() failed: %v", err) } s.domainCacheRefresh() } startTime := s.TestClusterConfig.TimeSource.Now().UnixNano() wfID := fmt.Sprintf("async-wf-integration-start-workflow-test-%d", startTime) wfType := "async-wf-integration-start-workflow-test-type" taskList := "async-wf-integration-start-workflow-test-tasklist" identity := "worker1" asyncReq := &types.StartWorkflowExecutionAsyncRequest{ StartWorkflowExecutionRequest: &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: wfID, WorkflowType: &types.WorkflowType{ Name: wfType, }, TaskList: &types.TaskList{ Name: taskList, }, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, }, } _, err := s.Engine.StartWorkflowExecutionAsync(ctx, asyncReq) if tc.wantStartFailure != (err != nil) { t.Errorf("StartWorkflowExecutionAsync() failed: %v, wantStartFailure: %v", err, tc.wantStartFailure) } if err != nil || tc.wantStartFailure { return } // there's no worker or poller for async workflow, so we just validate whether it started. // this is sufficient to verify the async workflow start path. for i := 0; i < 30; i++ { resp, err := s.Engine.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: wfID, }, }) if err != nil { t.Logf("Workflow execution not found yet. DescribeWorkflowExecution() returned err: %v", err) time.Sleep(time.Second) s.TestClusterConfig.TimeSource.Advance(time.Second) continue } if resp.GetWorkflowExecutionInfo() != nil { t.Logf("DescribeWorkflowExecution() found the execution: %#v", resp.GetWorkflowExecutionInfo()) return } } t.Fatal("Async started workflow not found") }) } } ================================================ FILE: host/cancel_workflow_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "bytes" "encoding/binary" "strconv" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) func (s *IntegrationSuite) TestExternalRequestCancelWorkflowExecution() { id := "integration-request-cancel-workflow-test" wt := "integration-request-cancel-workflow-test-type" tl := "integration-request-cancel-workflow-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) activityCount := int32(1) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCancelWorkflowExecution.Ptr(), CancelWorkflowExecutionDecisionAttributes: &types.CancelWorkflowExecutionDecisionAttributes{ Details: []byte("Cancelled"), }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) err = poller.PollAndProcessActivityTask(false) s.Logger.Info("PollAndProcessActivityTask", tag.Error(err)) s.Nil(err) ctx, cancel = createContext() defer cancel() err = s.Engine.RequestCancelWorkflowExecution(ctx, &types.RequestCancelWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) s.Nil(err) ctx, cancel = createContext() defer cancel() err = s.Engine.RequestCancelWorkflowExecution(ctx, &types.RequestCancelWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) s.NotNil(err) s.IsType(&types.CancellationAlreadyRequestedError{}, err) _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) executionCancelled := false GetHistoryLoop: for i := 1; i < 3; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if *lastEvent.EventType != types.EventTypeWorkflowExecutionCanceled { s.Logger.Warn("Execution not cancelled yet.") time.Sleep(100 * time.Millisecond) continue GetHistoryLoop } cancelledEventAttributes := lastEvent.WorkflowExecutionCanceledEventAttributes s.Equal("Cancelled", string(cancelledEventAttributes.Details)) executionCancelled = true break GetHistoryLoop } s.True(executionCancelled) } func (s *IntegrationSuite) TestRequestCancelWorkflowDecisionExecution() { id := "integration-cancel-workflow-decision-test" wt := "integration-cancel-workflow-decision-test-type" tl := "integration-cancel-workflow-decision-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) foreignRequest := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.ForeignDomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel = createContext() defer cancel() we2, err0 := s.Engine.StartWorkflowExecution(ctx, foreignRequest) s.Nil(err0) s.Logger.Info("StartWorkflowExecution on foreign Domain: %v, response: %v \n", tag.WorkflowDomainName(s.ForeignDomainName), tag.WorkflowRunID(we2.RunID)) activityCount := int32(1) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelExternalWorkflowExecution.Ptr(), RequestCancelExternalWorkflowExecutionDecisionAttributes: &types.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: s.ForeignDomainName, WorkflowID: id, RunID: we2.RunID, }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } foreignActivityCount := int32(1) foreignActivityCounter := int32(0) foreignDtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if foreignActivityCounter < foreignActivityCount { foreignActivityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, foreignActivityCounter)) return []byte(strconv.Itoa(int(foreignActivityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(foreignActivityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return []byte(strconv.Itoa(int(foreignActivityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCancelWorkflowExecution.Ptr(), CancelWorkflowExecutionDecisionAttributes: &types.CancelWorkflowExecutionDecisionAttributes{ Details: []byte("Cancelled"), }, }}, nil } foreignPoller := &TaskPoller{ Engine: s.Engine, Domain: s.ForeignDomainName, TaskList: taskList, Identity: identity, DecisionHandler: foreignDtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Start both current and foreign workflows to make some progress. _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) _, err = foreignPoller.PollAndProcessDecisionTask(false, false) s.Logger.Info("foreign PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) err = foreignPoller.PollAndProcessActivityTask(false) s.Logger.Info("foreign PollAndProcessActivityTask", tag.Error(err)) s.Nil(err) // Cancel the foreign workflow with this decision request. _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) cancellationSent := false intiatedEventID := 10 CheckHistoryLoopForCancelSent: for i := 1; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-2] if *lastEvent.EventType != types.EventTypeExternalWorkflowExecutionCancelRequested { s.Logger.Info("Cancellation still not sent.") time.Sleep(100 * time.Millisecond) continue CheckHistoryLoopForCancelSent } externalWorkflowExecutionCancelRequestedEvent := lastEvent.ExternalWorkflowExecutionCancelRequestedEventAttributes s.Equal(int64(intiatedEventID), externalWorkflowExecutionCancelRequestedEvent.InitiatedEventID) s.Equal(id, externalWorkflowExecutionCancelRequestedEvent.WorkflowExecution.WorkflowID) s.Equal(we2.RunID, externalWorkflowExecutionCancelRequestedEvent.WorkflowExecution.RunID) cancellationSent = true break } s.True(cancellationSent) // Accept cancellation. _, err = foreignPoller.PollAndProcessDecisionTask(false, false) s.Logger.Info("foreign PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) executionCancelled := false GetHistoryLoop: for i := 1; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.ForeignDomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we2.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if *lastEvent.EventType != types.EventTypeWorkflowExecutionCanceled { s.Logger.Warn("Execution not cancelled yet.") time.Sleep(100 * time.Millisecond) continue GetHistoryLoop } cancelledEventAttributes := lastEvent.WorkflowExecutionCanceledEventAttributes s.Equal("Cancelled", string(cancelledEventAttributes.Details)) executionCancelled = true // Find cancel requested event and verify it. var cancelRequestEvent *types.HistoryEvent for _, x := range history.Events { if *x.EventType == types.EventTypeWorkflowExecutionCancelRequested { cancelRequestEvent = x } } s.NotNil(cancelRequestEvent) cancelRequestEventAttributes := cancelRequestEvent.WorkflowExecutionCancelRequestedEventAttributes s.Equal(int64(intiatedEventID), *cancelRequestEventAttributes.ExternalInitiatedEventID) s.Equal(id, cancelRequestEventAttributes.ExternalWorkflowExecution.WorkflowID) s.Equal(we.RunID, cancelRequestEventAttributes.ExternalWorkflowExecution.RunID) break GetHistoryLoop } s.True(executionCancelled) } func (s *IntegrationSuite) TestRequestCancelWorkflowDecisionExecution_UnKnownTarget() { id := "integration-cancel-unknown-workflow-decision-test" wt := "integration-cancel-unknown-workflow-decision-test-type" tl := "integration-cancel-unknown-workflow-decision-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) activityCount := int32(1) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelExternalWorkflowExecution.Ptr(), RequestCancelExternalWorkflowExecutionDecisionAttributes: &types.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: s.ForeignDomainName, WorkflowID: "workflow_not_exist", RunID: we.RunID, }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Start workflows to make some progress. _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // Cancel the foreign workflow with this decision request. _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) cancellationSentFailed := false intiatedEventID := 10 CheckHistoryLoopForCancelSent: for i := 1; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-2] if *lastEvent.EventType != types.EventTypeRequestCancelExternalWorkflowExecutionFailed { s.Logger.Info("Cancellaton not cancelled yet.") time.Sleep(100 * time.Millisecond) continue CheckHistoryLoopForCancelSent } requestCancelExternalWorkflowExecutionFailedEvetn := lastEvent.RequestCancelExternalWorkflowExecutionFailedEventAttributes s.Equal(int64(intiatedEventID), requestCancelExternalWorkflowExecutionFailedEvetn.InitiatedEventID) s.Equal("workflow_not_exist", requestCancelExternalWorkflowExecutionFailedEvetn.WorkflowExecution.WorkflowID) s.Equal(we.RunID, requestCancelExternalWorkflowExecutionFailedEvetn.WorkflowExecution.RunID) cancellationSentFailed = true break } s.True(cancellationSentFailed) } ================================================ FILE: host/cassandra_lwt_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:build !race && cassandralwt // +build !race,cassandralwt /* To run locally: 1. Stop the previous run if any docker compose -f docker/github_actions/docker-compose-cassandra-lwt.yml down 2. Build the integration-test-async-wf image docker compose -f docker/github_actions/docker-compose-cassandra-lwt.yml build test-cass-lwt 3. Run the test in the docker container docker compose -f docker/github_actions/docker-compose-cassandra-lwt.yml run --rm test-cass-lwt 4. Full test run logs can be found at test.log file */ /* -- Benchmark Results -- Single node cassandra cluster, maxConns=2, replicas=1: Total updates: 1000, concurrency: 1, success: 1000, failed: 0, avg duration: 7.41ms, max duration: 26.9ms, min duration: 4.8ms, elapsed: 7.42s Total updates: 1000, concurrency: 10, success: 1000, failed: 0, avg duration: 54.54ms, max duration: 808.2ms, min duration: 2.8ms, elapsed: 5.16s Total updates: 1000, concurrency: 20, success: 999, failed: 1, avg duration: 90.61ms, max duration: 1.075s, min duration: 2.8ms, elapsed: 4.63s Total updates: 1000, concurrency: 40, success: 993, failed: 7, avg duration: 145.95ms, max duration: 1.071s, min duration: 2.2ms, elapsed: 3.78s Total updates: 1000, concurrency: 80, success: 976, failed: 24, avg duration: 254.02ms, max duration: 1.093s, min duration: 1.4ms, elapsed: 3.36s Two nodes cassandra cluster, maxConns=2, replicas=1: Total updates: 1000, concurrency: 10, success: 1000, failed: 0, avg duration: 52.37s, max duration: 681.20ms, min duration: 2.91ms, elapsed: 5.29s Total updates: 1000, concurrency: 100, success: 949, failed: 51, avg duration: 283.95ms, max duration: 1.11s, min duration: 2.21ms, elapsed: 3.09s Two nodes cassandra cluster, maxConns=2, replicas=2: Total updates: 1000, concurrency: 10, success: 898, failed: 102, avg duration: 367.31ms, max duration: 1.12s, min duration: 7.98ms, elapsed: 36.88s Total updates: 1000, concurrency: 60, success: 160, failed: 840, avg duration: 951.3ms, max duration: 1.11s, min duration: 8.99ms, elapsed: 16.35s Total updates: 1000, concurrency: 80, success: 71, failed: 929, avg duration: 1.00s, max duration: 1.12s, min duration: 8.10ms, elapsed: 13.02s Total updates: 1000, concurrency: 100, success: 38, failed: 962, avg duration: 1.02s, max duration: 1.11s, min duration: 7.89ms, elapsed: 10.52s Two nodes cassandra cluster, maxConns=0, replicas=2: Total updates: 1000, concurrency: 1, success: 1000, failed: 0, avg duration: 11.75ms, max duration: 50.09ms, min duration: 6.69ms, elapsed: 11.76s Total updates: 1000, concurrency: 10, success: 900, failed: 100, avg duration: 370.96ms, max duration: 1.12s, min duration: 7.92ms, elapsed: 37.31s Total updates: 1000, concurrency: 20, success: 642, failed: 358, avg duration: 602.63ms, max duration: 1.12s, min duration: 5.95ms, elapsed: 30.40s Total updates: 1000, concurrency: 40, success: 365, failed: 635, avg duration: 826.45ms, max duration: 1.13s, min duration: 7.83ms, elapsed: 21.12s Total updates: 1000, concurrency: 80, success: 64, failed: 936, avg duration: 1.00s, max duration: 1.12s, min duration: 6.87ms, elapsed: 12.93s Total updates: 1000, concurrency: 100, success: 29, failed: 971, avg duration: 1.02s, max duration: 1.11s, min duration: 36.97ms, elapsed: 10.51s */ package host import ( "context" "flag" "fmt" "sync" "testing" "time" "github.com/pborman/uuid" "go.uber.org/multierr" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql/public" persistencetests "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/types" "github.com/uber/cadence/testflags" ) const ( updateRequestsChannelSize = 1000 concurrency = 10 totalUpdates = 1000 replicas = 1 maxConns = 2 ) type updateStats struct { updateNum int duration time.Duration ts time.Time err error } type aggStats struct { successCnt int failCnt int totalDuration time.Duration maxDuration time.Duration minDuration time.Duration lastUpdate time.Time errs error } func TestCassandraLWT(t *testing.T) { flag.Parse() testflags.RequireCassandra(t) t.Logf("Running Cassandra LWT tests, concurrency: %d", concurrency) testBase := public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{ Replicas: replicas, MaxConns: maxConns, }) testBase.Setup() defer testBase.TearDownWorkflowStore() // Create workflows wfID := fmt.Sprintf("workflow-%v", 0) wfInfo, err := createWorkflow(testBase, wfID) if err != nil { t.Fatalf("createWorkflow failed: %v", err) } updateReq := infoToUpdateReq(testBase, wfInfo) t.Logf("Created %d workflows", concurrency) ctx, cancel := context.WithCancel(context.Background()) defer cancel() updateRequestsCh := make(chan int, updateRequestsChannelSize) // Start a goroutine to consume the update stats var aggStats aggStats statsCh := make(chan updateStats, totalUpdates) var statCollectorWG sync.WaitGroup statCollectorWG.Add(1) go func() { defer statCollectorWG.Done() for i := 0; i < totalUpdates; i++ { stats := <-statsCh if stats.err != nil { aggStats.errs = multierr.Append(aggStats.errs, fmt.Errorf("updateNum: %v, duration: %v, err: %v", stats.updateNum, stats.duration, stats.err)) aggStats.failCnt++ } else { aggStats.successCnt++ } aggStats.totalDuration += stats.duration if aggStats.maxDuration < stats.duration { aggStats.maxDuration = stats.duration } if aggStats.minDuration == 0 || aggStats.minDuration > stats.duration { aggStats.minDuration = stats.duration } if aggStats.lastUpdate.Before(stats.ts) { aggStats.lastUpdate = stats.ts } } }() // Start concurrent handlers to update the workflows. Each one will update its own workflow var updateHandlersWG sync.WaitGroup for i := 0; i < concurrency; i++ { updateHandlersWG.Add(1) go updateHandler(ctx, testBase, updateRequestsCh, statsCh, &updateHandlersWG, i, updateReq) } // wait a bit before starting the updates time.Sleep(1 * time.Second) now := time.Now() for i := 0; i < totalUpdates; i++ { updateRequestsCh <- i } // wait for the updates to complete statCollectorWG.Wait() t.Logf("Total updates: %v, concurrency: %d, success: %v, failed: %v, avg duration: %v, max duration: %v, min duration: %v, elapsed: %v", totalUpdates, concurrency, aggStats.successCnt, aggStats.failCnt, aggStats.totalDuration/time.Duration(totalUpdates), aggStats.maxDuration, aggStats.minDuration, aggStats.lastUpdate.Sub(now), ) if aggStats.errs != nil { msg := aggStats.errs.Error() len := len(msg) if len > 1000 { len = 1000 } t.Errorf("Errors: %s", msg[:len-1]) } // close the channel to signal the handlers to exit close(updateRequestsCh) // wait for the handlers to exit updateHandlersWG.Wait() } func updateHandler( ctx context.Context, s *persistencetests.TestBase, incomingUpdatesCh chan int, statsCh chan updateStats, wg *sync.WaitGroup, handlerNo int, updateReq *persistence.UpdateWorkflowExecutionRequest, ) { defer wg.Done() for { select { case <-ctx.Done(): s.T().Logf("updateHandler %v exiting because context done", handlerNo) return case updateNum, ok := <-incomingUpdatesCh: if !ok { s.T().Logf("updateHandler %v exiting because channel closed", handlerNo) return } start := time.Now() err := updateWorkflow(s, updateReq) end := time.Now() statsCh <- updateStats{ updateNum: updateNum, duration: end.Sub(start), ts: end, err: err, } } } } // createWorkflow creates a new workflow and returns the workflow info to be used for updates func createWorkflow(s *persistencetests.TestBase, wfID string) (*persistence.WorkflowMutableState, error) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() domainID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: wfID, RunID: uuid.New(), } tasklist := "tasklist1" workflowType := "workflowtype1" workflowTimeout := int32(10) decisionTimeout := int32(14) lastProcessedEventID := int64(0) nextEventID := int64(3) csum := checksum.Checksum{ Flavor: checksum.FlavorIEEECRC32OverThriftBinary, Version: 22, Value: uuid.NewRandom(), } versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := persistence.NewVersionHistories(versionHistory) req := &persistence.CreateWorkflowExecutionRequest{ NewWorkflowSnapshot: persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CreateRequestID: uuid.New(), DomainID: domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), FirstExecutionRunID: workflowExecution.GetRunID(), TaskList: tasklist, WorkflowTypeName: workflowType, WorkflowTimeout: workflowTimeout, DecisionStartToCloseTimeout: decisionTimeout, LastFirstEventID: constants.FirstEventID, NextEventID: nextEventID, LastProcessedEvent: lastProcessedEventID, State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, }, ExecutionStats: &persistence.ExecutionStats{}, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: persistence.CreateWorkflowModeBrandNew, } _, err := s.ExecutionManager.CreateWorkflowExecution(ctx, req) if err != nil { return nil, fmt.Errorf("ExecutionManager.CreateWorkflowExecution failed: %v", err) } info, err := s.GetWorkflowExecutionInfo(ctx, domainID, workflowExecution) if err != nil { return nil, fmt.Errorf("GetWorkflowExecutionInfo failed: %v", err) } if info.ExecutionInfo.State != persistence.WorkflowStateCreated { return nil, fmt.Errorf("Unexpected state: %v", info.ExecutionInfo.State) } if info.ExecutionInfo.CloseStatus != persistence.WorkflowCloseStatusNone { return nil, fmt.Errorf("Unexpected close status: %v", info.ExecutionInfo.CloseStatus) } return info, nil } func infoToUpdateReq(s *persistencetests.TestBase, info *persistence.WorkflowMutableState) *persistence.UpdateWorkflowExecutionRequest { updatedInfo := info.ExecutionInfo updatedStats := info.ExecutionStats updatedInfo.State = persistence.WorkflowStateRunning nextEventID := int64(3) csum := checksum.Checksum{ Flavor: checksum.FlavorIEEECRC32OverThriftBinary, Version: 22, Value: uuid.NewRandom(), } versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ { EventID: nextEventID, Version: constants.EmptyVersion, }, }) versionHistories := persistence.NewVersionHistories(versionHistory) return &persistence.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: updatedInfo, ExecutionStats: updatedStats, Condition: nextEventID, Checksum: csum, VersionHistories: versionHistories, }, RangeID: s.ShardInfo.RangeID, Mode: persistence.UpdateWorkflowModeUpdateCurrent, } } func updateWorkflow(s *persistencetests.TestBase, req *persistence.UpdateWorkflowExecutionRequest) error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _, err := s.ExecutionManager.UpdateWorkflowExecution(ctx, req) if err == nil { return nil } return fmt.Errorf("ExecutionManager.UpdateWorkflowExecution failed: %v", err) } ================================================ FILE: host/cli/cassandra/cassandra_tool_cqlclient_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/testflags" "github.com/uber/cadence/tools/cassandra" "github.com/uber/cadence/tools/common/schema/test" _ "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql/public" // needed to load the default gocql client ) type ( CQLClientTestSuite struct { test.DBTestBase } ) var _ test.DB = (cassandra.CqlClient)(nil) func TestCQLClientTestSuite(t *testing.T) { testflags.RequireCassandra(t) suite.Run(t, new(CQLClientTestSuite)) } func (s *CQLClientTestSuite) SetupTest() { s.Assertions = require.New(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil } func (s *CQLClientTestSuite) SetupSuite() { client, err := NewTestCQLClient(cassandra.SystemKeyspace) if err != nil { s.Log.Fatal("error creating CQLClient, ", tag.Error(err)) } s.SetupSuiteBase(client) } func (s *CQLClientTestSuite) TearDownSuite() { s.TearDownSuiteBase() } func (s *CQLClientTestSuite) TestParseCQLFile() { s.RunParseFileTest(CreateTestCQLFileContent()) } func (s *CQLClientTestSuite) TestCQLClient() { client, err := NewTestCQLClient(s.DBName) s.Nil(err) s.RunCreateTest(client) s.RunUpdateTest(client) s.RunDropTest(client) client.Close() } ================================================ FILE: host/cli/cassandra/cassandra_tool_setupTask_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "log" "os" "testing" "github.com/stretchr/testify/suite" "github.com/uber/cadence/environment" "github.com/uber/cadence/testflags" "github.com/uber/cadence/tools/cassandra" "github.com/uber/cadence/tools/common/schema/test" ) type ( SetupSchemaTestSuite struct { test.SetupSchemaTestBase client cassandra.CqlClient } ) func TestSetupSchemaTestSuite(t *testing.T) { testflags.RequireCassandra(t) suite.Run(t, new(SetupSchemaTestSuite)) } func (s *SetupSchemaTestSuite) SetupSuite() { os.Setenv("CASSANDRA_HOST", environment.GetCassandraAddress()) client, err := NewTestCQLClient(cassandra.SystemKeyspace) if err != nil { log.Fatal("Error creating CQLClient") } s.client = client s.SetupSuiteBase(client) } func (s *SetupSchemaTestSuite) TearDownSuite() { s.TearDownSuiteBase() } func (s *SetupSchemaTestSuite) TestCreateKeyspace() { s.Nil(cassandra.RunTool([]string{"./tool", "create", "-k", "foobar123", "--rf", "1"})) err := s.client.DropKeyspace("foobar123") s.Nil(err) } func (s *SetupSchemaTestSuite) TestSetupSchema() { client, err := NewTestCQLClient(s.DBName) s.Nil(err) s.RunSetupTest(cassandra.BuildCLIOptions(), client, "-k", CreateTestCQLFileContent(), []string{"tasks", "events"}) } ================================================ FILE: host/cli/cassandra/cassandra_tool_updateTask_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "log" "os" "testing" "github.com/stretchr/testify/suite" "github.com/uber/cadence/schema/cassandra" "github.com/uber/cadence/testflags" cassandra2 "github.com/uber/cadence/tools/cassandra" "github.com/uber/cadence/tools/common/schema/test" ) type UpdateSchemaTestSuite struct { test.UpdateSchemaTestBase } func TestUpdateSchemaTestSuite(t *testing.T) { testflags.RequireCassandra(t) suite.Run(t, new(UpdateSchemaTestSuite)) } func (s *UpdateSchemaTestSuite) SetupSuite() { client, err := NewTestCQLClient(cassandra2.SystemKeyspace) if err != nil { log.Fatal("Error creating CQLClient") } s.SetupSuiteBase(client) } func (s *UpdateSchemaTestSuite) TearDownSuite() { s.TearDownSuiteBase() } func (s *UpdateSchemaTestSuite) TestUpdateSchema() { client, err := NewTestCQLClient(s.DBName) s.Nil(err) defer client.Close() s.RunUpdateSchemaTest(cassandra2.BuildCLIOptions(), client, "-k", CreateTestCQLFileContent(), []string{"events", "tasks"}) } func (s *UpdateSchemaTestSuite) TestDryrun() { client, err := NewTestCQLClient(s.DBName) s.Nil(err) defer client.Close() dir := rootRelativePath + "schema/cassandra/cadence/versioned" s.RunDryrunTest(cassandra2.BuildCLIOptions(), client, "-k", dir, cassandra.Version) } func (s *UpdateSchemaTestSuite) TestVisibilityDryrun() { client, err := NewTestCQLClient(s.DBName) s.Nil(err) defer client.Close() dir := rootRelativePath + "schema/cassandra/visibility/versioned" s.RunDryrunTest(cassandra2.BuildCLIOptions(), client, "-k", dir, cassandra.VisibilityVersion) } func (s *UpdateSchemaTestSuite) TestShortcut() { client, err := NewTestCQLClient(s.DBName) s.Nil(err) defer client.Close() dir := rootRelativePath + "schema/cassandra/cadence/versioned" cqlshArgs := []string{"--cqlversion=3.4.6", "-e", "DESC KEYSPACE %s;"} if cassandraHost := os.Getenv("CASSANDRA_HOST"); cassandraHost != "" { cqlshArgs = append(cqlshArgs, cassandraHost) } s.RunShortcutTest(cassandra2.BuildCLIOptions(), client, "-k", dir, "cqlsh", cqlshArgs...) } ================================================ FILE: host/cli/cassandra/cassandra_tool_version_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "fmt" "io/ioutil" "log" "math/rand" "os" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" cassandra_db "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/environment" "github.com/uber/cadence/testflags" "github.com/uber/cadence/tools/cassandra" ) type ( VersionTestSuite struct { *require.Assertions // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, not merely log an error suite.Suite } ) func TestVersionTestSuite(t *testing.T) { testflags.RequireCassandra(t) suite.Run(t, new(VersionTestSuite)) } func (s *VersionTestSuite) SetupTest() { s.Assertions = require.New(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil } func (s *VersionTestSuite) TestVerifyCompatibleVersion() { keyspace := "cadence_test" visKeyspace := "cadence_visibility_test" cqlFile := rootRelativePath + "schema/cassandra/cadence/schema.cql" visCqlFile := rootRelativePath + "schema/cassandra/visibility/schema.cql" defer s.createKeyspace(keyspace)() defer s.createKeyspace(visKeyspace)() s.Nil(cassandra.RunTool([]string{ "./tool", "-k", keyspace, "-q", "setup-schema", "-f", cqlFile, "-version", "10.0", "-o", })) s.Nil(cassandra.RunTool([]string{ "./tool", "-k", visKeyspace, "-q", "setup-schema", "-f", visCqlFile, "-version", "10.0", "-o", })) defaultCfg := config.NoSQL{ PluginName: cassandra_db.PluginName, Hosts: environment.GetCassandraAddress(), Port: cassandra.DefaultCassandraPort, User: environment.GetCassandraUsername(), Password: environment.GetCassandraPassword(), Keyspace: keyspace, } visibilityCfg := defaultCfg visibilityCfg.Keyspace = visKeyspace cfg := config.Persistence{ DefaultStore: "default", VisibilityStore: "visibility", DataStores: map[string]config.DataStore{ "default": {NoSQL: &defaultCfg}, "visibility": {NoSQL: &visibilityCfg}, }, TransactionSizeLimit: dynamicproperties.GetIntPropertyFn(constants.DefaultTransactionSizeLimit), ErrorInjectionRate: dynamicproperties.GetFloatPropertyFn(0), } s.NoError(cassandra.VerifyCompatibleVersion(cfg, gocql.All)) } func (s *VersionTestSuite) TestCheckCompatibleVersion() { flags := []struct { expectedVersion string actualVersion string errStr string expectedFail bool }{ {"2.0", "1.0", "version mismatch", false}, {"1.0", "1.0", "", false}, {"1.0", "2.0", "", false}, {"1.0", "abc", "reading schema version: table schema_version does not exist", true}, } for _, flag := range flags { s.runCheckCompatibleVersion(flag.expectedVersion, flag.actualVersion, flag.errStr, flag.expectedFail) } } func (s *VersionTestSuite) createKeyspace(keyspace string) func() { protoVersion, err := environment.GetCassandraProtoVersion() s.NoError(err) cfg := &cassandra.CQLClientConfig{ Hosts: environment.GetCassandraAddress(), Port: cassandra.DefaultCassandraPort, Keyspace: "system", Timeout: cassandra.DefaultTimeout, NumReplicas: 1, ProtoVersion: protoVersion, } client, err := cassandra.NewCQLClient(cfg, gocql.All) s.NoError(err) err = client.CreateKeyspace(keyspace) if err != nil { log.Fatalf("error creating Keyspace, err=%v", err) } return func() { s.NoError(client.DropKeyspace(keyspace)) client.Close() } } func (s *VersionTestSuite) runCheckCompatibleVersion( expected string, actual string, errStr string, expectedFail bool, ) { r := rand.New(rand.NewSource(time.Now().UnixNano())) keyspace := fmt.Sprintf("version_test_%v", r.Int63()) defer s.createKeyspace(keyspace)() tmpDir := s.T().TempDir() subdir := tmpDir + "/" + keyspace s.NoError(os.Mkdir(subdir, os.FileMode(0744))) s.createSchemaForVersion(subdir, actual) if expected != actual { s.createSchemaForVersion(subdir, expected) } cqlFile := subdir + "/v" + actual + "/tmp.cql" if expectedFail { s.Error(cassandra.RunTool([]string{ "./tool", "-k", keyspace, "setup-schema", "-f", cqlFile, "-version", actual, "-o", })) os.RemoveAll(subdir + "/v" + actual) } else { s.NoError(cassandra.RunTool([]string{ "./tool", "-k", keyspace, "setup-schema", "-f", cqlFile, "-version", actual, "-o", })) } cfg := config.NoSQL{ PluginName: cassandra_db.PluginName, Hosts: environment.GetCassandraAddress(), Port: cassandra.DefaultCassandraPort, User: environment.GetCassandraUsername(), Password: environment.GetCassandraPassword(), Keyspace: keyspace, } err := cassandra.CheckCompatibleVersion(cfg, expected, gocql.All) if len(errStr) > 0 { s.Errorf(err, "error=%v", errStr) s.Contains(err.Error(), errStr) } else { s.NoError(err) } } func (s *VersionTestSuite) createSchemaForVersion(subdir string, v string) { vDir := subdir + "/v" + v s.NoError(os.Mkdir(vDir, os.FileMode(0744))) cqlFile := vDir + "/tmp.cql" s.NoError(ioutil.WriteFile(cqlFile, []byte{}, os.FileMode(0644))) } ================================================ FILE: host/cli/cassandra/utils.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/environment" "github.com/uber/cadence/tools/cassandra" ) // NOTE: change this when moving the test files around during refactoring // Path to root folder of cadence repo const rootRelativePath = "../../../" func NewTestCQLClient(keyspace string) (cassandra.CqlClient, error) { protoVersion, err := environment.GetCassandraProtoVersion() if err != nil { return nil, err } return cassandra.NewCQLClient(&cassandra.CQLClientConfig{ Hosts: environment.GetCassandraAddress(), Port: cassandra.DefaultCassandraPort, Keyspace: keyspace, Timeout: cassandra.DefaultTimeout, User: environment.GetCassandraUsername(), Password: environment.GetCassandraPassword(), AllowedAuthenticators: environment.GetCassandraAllowedAuthenticators(), NumReplicas: 1, ProtoVersion: protoVersion, }, gocql.All) } func CreateTestCQLFileContent() string { return ` -- test cql file content CREATE TABLE events ( domain_id uuid, workflow_id text, run_id uuid, -- We insert a batch of events with each append transaction. -- This field stores the event id of first event in the batch. first_event_id bigint, range_id bigint, tx_id bigint, data blob, -- Batch of workflow execution history events as a blob data_encoding text, -- Protocol used for history serialization data_version int, -- history blob version PRIMARY KEY ((domain_id, workflow_id, run_id), first_event_id) ); -- Stores activity or workflow tasks CREATE TABLE tasks ( domain_id uuid, task_list_name text, task_list_type int, -- enum TaskListType {ActivityTask, DecisionTask} type int, -- enum rowType {Task, TaskList} task_id bigint, -- unique identifier for tasks, monotonically increasing range_id bigint static, -- Used to ensure that only one process can write to the table task text, task_list text, PRIMARY KEY ((domain_id, task_list_name, task_list_type), type, task_id) ); ` } ================================================ FILE: host/cli/sql/cli_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "testing" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/persistence/sql/sqlplugin/mysql" "github.com/uber/cadence/testflags" ) func TestMySQLConnTestSuite(t *testing.T) { testflags.RequireMySQL(t) suite.Run(t, NewSQLConnTestSuite(mysql.PluginName)) } func TestMySQLHandlerTestSuite(t *testing.T) { testflags.RequireMySQL(t) suite.Run(t, NewHandlerTestSuite(mysql.PluginName)) } func TestMySQLSetupSchemaTestSuite(t *testing.T) { testflags.RequireMySQL(t) suite.Run(t, NewSetupSchemaTestSuite(mysql.PluginName)) } func TestMySQLUpdateSchemaTestSuite(t *testing.T) { testflags.RequireMySQL(t) suite.Run(t, NewUpdateSchemaTestSuite(mysql.PluginName)) } func TestMySQLVersionTestSuite(t *testing.T) { testflags.RequireMySQL(t) suite.Run(t, NewVersionTestSuite(mysql.PluginName)) } ================================================ FILE: host/cli/sql/connTest.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "fmt" "log" "net" "strconv" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/environment" "github.com/uber/cadence/tools/common/schema/test" "github.com/uber/cadence/tools/sql" ) type ( // SQLConnTestSuite defines a test suite SQLConnTestSuite struct { test.DBTestBase pluginName string } ) var _ test.DB = (*sql.Connection)(nil) // NewSQLConnTestSuite returns the test suite func NewSQLConnTestSuite(pluginName string) *SQLConnTestSuite { return &SQLConnTestSuite{ pluginName: pluginName, } } // SetupTest setups test func (s *SQLConnTestSuite) SetupTest() { s.Assertions = require.New(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil } // SetupSuite setups test suite func (s *SQLConnTestSuite) SetupSuite() { conn, err := newTestConn("", s.pluginName) if err != nil { log.Fatal(fmt.Sprintf("failed creating sql conn with error: %v", tag.Error(err))) } s.SetupSuiteBase(conn) } // TearDownSuite tear down test suite func (s *SQLConnTestSuite) TearDownSuite() { s.TearDownSuiteBase() } // TestParseCQLFile test func (s *SQLConnTestSuite) TestParseCQLFile() { s.RunParseFileTest(createTestSQLFileContent()) } // TestSQLConn test // TODO refactor the whole package to support testing against Postgres // https://github.com/uber/cadence/issues/2856 func (s *SQLConnTestSuite) TestSQLConn() { port, err := environment.GetMySQLPort() s.Nil(err) conn, err := sql.NewConnection(&config.SQL{ ConnectAddr: net.JoinHostPort( environment.GetMySQLAddress(), strconv.Itoa(port), ), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), PluginName: s.pluginName, DatabaseName: s.DBName, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, }) s.Nil(err) s.RunCreateTest(conn) s.RunUpdateTest(conn) s.RunDropTest(conn) conn.Close() } func newTestConn(database, pluginName string) (*sql.Connection, error) { port, err := environment.GetMySQLPort() if err != nil { return nil, err } return sql.NewConnection(&config.SQL{ ConnectAddr: net.JoinHostPort( environment.GetMySQLAddress(), strconv.Itoa(port), ), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), PluginName: pluginName, DatabaseName: database, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, }) } func createTestSQLFileContent() string { return ` -- test sql file content CREATE TABLE task_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, first_event_id BIGINT NOT NULL, -- version BIGINT NOT NULL, next_event_id BIGINT NOT NULL, history MEDIUMBLOB, history_encoding VARCHAR(16) NOT NULL, new_run_history BLOB, new_run_history_encoding VARCHAR(16) NOT NULL DEFAULT 'json', event_store_version INT NOT NULL, -- indiciates which version of event store to query new_run_event_store_version INT NOT NULL, -- indiciates which version of event store to query for new run(continueAsNew) PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, first_event_id) ); CREATE TABLE tasks ( domain_id BINARY(16) NOT NULL, task_list_name VARCHAR(255) NOT NULL, task_type TINYINT NOT NULL, -- {Activity, Decision} task_id BIGINT NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (domain_id, task_list_name, task_type, task_id) ); ` } ================================================ FILE: host/cli/sql/handlerTest.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "net" "strconv" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/config" "github.com/uber/cadence/environment" "github.com/uber/cadence/tools/sql" ) type ( // HandlerTestSuite defines a test suite HandlerTestSuite struct { *require.Assertions // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, not merely log an error suite.Suite pluginName string } ) // NewHandlerTestSuite returns a test suite func NewHandlerTestSuite(pluginName string) *HandlerTestSuite { return &HandlerTestSuite{ pluginName: pluginName, } } // SetupTest setups test func (s *HandlerTestSuite) SetupTest() { s.Assertions = require.New(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil } // TestValidateConnectConfig test func (s *HandlerTestSuite) TestValidateConnectConfig() { cfg := new(config.SQL) s.NotNil(sql.ValidateConnectConfig(cfg)) port, err := environment.GetMySQLPort() s.NoError(err) cfg.ConnectAddr = net.JoinHostPort( environment.GetMySQLAddress(), strconv.Itoa(port), ) s.NotNil(sql.ValidateConnectConfig(cfg)) cfg.DatabaseName = "foobar" s.Nil(sql.ValidateConnectConfig(cfg)) cfg.TLS = &config.TLS{} cfg.TLS.Enabled = true s.NotNil(sql.ValidateConnectConfig(cfg)) cfg.TLS.CaFile = "ca.pem" s.Nil(sql.ValidateConnectConfig(cfg)) cfg.TLS.KeyFile = "key_file" cfg.TLS.CertFile = "" s.NotNil(sql.ValidateConnectConfig(cfg)) cfg.TLS.KeyFile = "" cfg.TLS.CertFile = "cert_file" s.NotNil(sql.ValidateConnectConfig(cfg)) cfg.TLS.KeyFile = "key_file" cfg.TLS.CertFile = "cert_file" s.Nil(sql.ValidateConnectConfig(cfg)) } ================================================ FILE: host/cli/sql/setuptaskTest.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "log" "os" "github.com/uber/cadence/environment" "github.com/uber/cadence/tools/common/schema/test" "github.com/uber/cadence/tools/sql" ) type ( // SetupSchemaTestSuite defines a test suite SetupSchemaTestSuite struct { test.SetupSchemaTestBase conn *sql.Connection pluginName string } ) // NewSetupSchemaTestSuite returns a test suite func NewSetupSchemaTestSuite(pluginName string) *SetupSchemaTestSuite { return &SetupSchemaTestSuite{ pluginName: pluginName, } } // SetupSuite setup test suite func (s *SetupSchemaTestSuite) SetupSuite() { os.Setenv("SQL_HOST", environment.GetMySQLAddress()) os.Setenv("SQL_USER", environment.GetMySQLUser()) os.Setenv("SQL_PASSWORD", environment.GetMySQLPassword()) conn, err := newTestConn("", s.pluginName) if err != nil { log.Fatalf("error creating sql connection:%v", err) } s.conn = conn s.SetupSuiteBase(conn) } // TearDownSuite tear down test suite func (s *SetupSchemaTestSuite) TearDownSuite() { s.TearDownSuiteBase() } // TestCreateDatabase test func (s *SetupSchemaTestSuite) TestCreateDatabase() { s.NoError(sql.RunTool([]string{"./tool", "-u", environment.GetMySQLUser(), "--pw", environment.GetMySQLPassword(), "create", "--db", "foobar123"})) err := s.conn.DropDatabase("foobar123") s.Nil(err) } // TestSetupSchema test func (s *SetupSchemaTestSuite) TestSetupSchema() { conn, err := newTestConn(s.DBName, s.pluginName) s.Nil(err) s.RunSetupTest(sql.BuildCLIOptions(), conn, "--db", createTestSQLFileContent(), []string{"task_maps", "tasks"}) } ================================================ FILE: host/cli/sql/updatetaskTest.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "log" "os" "github.com/uber/cadence/environment" "github.com/uber/cadence/schema/mysql" "github.com/uber/cadence/tools/common/schema/test" "github.com/uber/cadence/tools/sql" ) // UpdateSchemaTestSuite defines a test suite type UpdateSchemaTestSuite struct { test.UpdateSchemaTestBase pluginName string } // NewUpdateSchemaTestSuite returns a test suite func NewUpdateSchemaTestSuite(pluginName string) *UpdateSchemaTestSuite { return &UpdateSchemaTestSuite{ pluginName: pluginName, } } // SetupSuite setups test suite func (s *UpdateSchemaTestSuite) SetupSuite() { os.Setenv("SQL_HOST", environment.GetMySQLAddress()) os.Setenv("SQL_USER", environment.GetMySQLUser()) os.Setenv("SQL_PASSWORD", environment.GetMySQLPassword()) conn, err := newTestConn("", s.pluginName) if err != nil { log.Fatal("Error creating CQLClient") } s.SetupSuiteBase(conn) } // TearDownSuite tear down test suite func (s *UpdateSchemaTestSuite) TearDownSuite() { s.TearDownSuiteBase() } // TestUpdateSchema test func (s *UpdateSchemaTestSuite) TestUpdateSchema() { conn, err := newTestConn(s.DBName, s.pluginName) s.Nil(err) defer conn.Close() s.RunUpdateSchemaTest(sql.BuildCLIOptions(), conn, "--db", createTestSQLFileContent(), []string{"task_maps", "tasks"}) } // TestDryrun test func (s *UpdateSchemaTestSuite) TestDryrun() { conn, err := newTestConn(s.DBName, s.pluginName) s.Nil(err) defer conn.Close() dir := "../../../schema/mysql/v8/cadence/versioned" s.RunDryrunTest(sql.BuildCLIOptions(), conn, "--db", dir, mysql.Version) } // TestVisibilityDryrun test func (s *UpdateSchemaTestSuite) TestVisibilityDryrun() { conn, err := newTestConn(s.DBName, s.pluginName) s.Nil(err) defer conn.Close() dir := "../../../schema/mysql/v8/visibility/versioned" s.RunDryrunTest(sql.BuildCLIOptions(), conn, "--db", dir, mysql.VisibilityVersion) } ================================================ FILE: host/cli/sql/utils.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sql // NOTE: change this when moving the test files around during refactoring // Path to root folder of cadence repo const rootRelativePath = "../../../" ================================================ FILE: host/cli/sql/versionTest.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sql import ( "fmt" "io/ioutil" "math/rand" "os" "strconv" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/environment" "github.com/uber/cadence/tools/sql" ) type ( // VersionTestSuite defines a test suite VersionTestSuite struct { *require.Assertions // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, not merely log an error suite.Suite pluginName string } ) // NewVersionTestSuite returns a test suite func NewVersionTestSuite(pluginName string) *VersionTestSuite { return &VersionTestSuite{ pluginName: pluginName, } } // SetupTest setups test suite func (s *VersionTestSuite) SetupTest() { s.Assertions = require.New(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil } // TestVerifyCompatibleVersion test func (s *VersionTestSuite) TestVerifyCompatibleVersion() { database := "cadence_test" visDatabase := "cadence_visibility_test" sqlFile := rootRelativePath + "schema/mysql/v8/cadence/schema.sql" visSQLFile := rootRelativePath + "schema/mysql/v8/visibility/schema.sql" defer s.createDatabase(database)() defer s.createDatabase(visDatabase)() mysqlPort, err := environment.GetMySQLPort() s.NoError(err) port := strconv.Itoa(mysqlPort) err = sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", database, "-pl", s.pluginName, "-q", "setup-schema", "-f", sqlFile, "-version", "10.0", "-o", }) s.NoError(err) err = sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", visDatabase, "-pl", s.pluginName, "-q", "setup-schema", "-f", visSQLFile, "-version", "10.0", "-o", }) s.NoError(err) defaultCfg := config.SQL{ ConnectAddr: fmt.Sprintf("%v:%v", environment.GetMySQLAddress(), port), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), PluginName: s.pluginName, DatabaseName: database, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, } visibilityCfg := defaultCfg visibilityCfg.DatabaseName = visDatabase cfg := config.Persistence{ DefaultStore: "default", VisibilityStore: "visibility", DataStores: map[string]config.DataStore{ "default": {SQL: &defaultCfg}, "visibility": {SQL: &visibilityCfg}, }, TransactionSizeLimit: dynamicproperties.GetIntPropertyFn(constants.DefaultTransactionSizeLimit), ErrorInjectionRate: dynamicproperties.GetFloatPropertyFn(0), } s.NoError(sql.VerifyCompatibleVersion(cfg)) } // TestCheckCompatibleVersion test func (s *VersionTestSuite) TestCheckCompatibleVersion() { flags := []struct { expectedVersion string actualVersion string errStr string expectedFail bool }{ {"2.0", "1.0", "version mismatch", false}, {"1.0", "1.0", "", false}, {"1.0", "2.0", "", false}, {"1.0", "abc", "schema_version' doesn't exist", true}, } for _, flag := range flags { s.runCheckCompatibleVersion(flag.expectedVersion, flag.actualVersion, flag.errStr, flag.expectedFail) } } func (s *VersionTestSuite) createDatabase(database string) func() { connection, err := newTestConn("", s.pluginName) s.NoError(err) err = connection.CreateDatabase(database) s.NoError(err) return func() { s.NoError(connection.DropDatabase(database)) connection.Close() } } func (s *VersionTestSuite) runCheckCompatibleVersion( expected string, actual string, errStr string, expectedFail bool, ) { r := rand.New(rand.NewSource(time.Now().UnixNano())) database := fmt.Sprintf("version_test_%v", r.Int63()) defer s.createDatabase(database)() tmpDir := s.T().TempDir() subdir := tmpDir + "/" + database s.NoError(os.Mkdir(subdir, os.FileMode(0744))) s.createSchemaForVersion(subdir, actual) if expected != actual { s.createSchemaForVersion(subdir, expected) } sqlFile := subdir + "/v" + actual + "/tmp.sql" mysqlPort, err := environment.GetMySQLPort() s.NoError(err) port := strconv.Itoa(mysqlPort) err = sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", database, "-pl", s.pluginName, "setup-schema", "-f", sqlFile, "-version", actual, "-o", }) if expectedFail { s.Error(err) } else { s.NoError(err) } cfg := config.SQL{ ConnectAddr: fmt.Sprintf("%v:%v", environment.GetMySQLAddress(), port), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), PluginName: s.pluginName, DatabaseName: database, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, } err = sql.CheckCompatibleVersion(cfg, expected) if len(errStr) > 0 { s.Error(err) s.Contains(err.Error(), errStr) } else { s.NoError(err) } } func (s *VersionTestSuite) createSchemaForVersion(subdir string, v string) { vDir := subdir + "/v" + v s.NoError(os.Mkdir(vDir, os.FileMode(0744))) cqlFile := vDir + "/tmp.sql" s.NoError(ioutil.WriteFile(cqlFile, []byte{}, os.FileMode(0644))) } func (s *VersionTestSuite) TestMultipleDatabaseVersionInCompatible() { r1 := rand.New(rand.NewSource(time.Now().UnixNano())) r2 := rand.New(rand.NewSource(time.Now().UnixNano())) database1 := fmt.Sprintf("version_test_%v", r1.Int63()) database2 := fmt.Sprintf("version_test_%v", r2.Int63()) defer s.createDatabase(database1)() defer s.createDatabase(database2)() tmpDir := s.T().TempDir() subdir := tmpDir + "/" + "db" s.NoError(os.Mkdir(subdir, os.FileMode(0744))) s.createSchemaForVersion(subdir, "0.1") s.createSchemaForVersion(subdir, "0.2") s.createSchemaForVersion(subdir, "0.3") mysqlPort, err := environment.GetMySQLPort() s.NoError(err) port := strconv.Itoa(mysqlPort) s.NoError(sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", database1, "-pl", s.pluginName, "-q", "setup-schema", "-version", "0.2", "-o", })) s.NoError(sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", database2, "-pl", s.pluginName, "-q", "setup-schema", "-version", "0.3", "-o", })) cfg := config.SQL{ PluginName: s.pluginName, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, UseMultipleDatabases: true, NumShards: 2, MultipleDatabasesConfig: []config.MultipleDatabasesConfigEntry{ { ConnectAddr: fmt.Sprintf("%v:%v", environment.GetMySQLAddress(), port), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), DatabaseName: database1, }, { ConnectAddr: fmt.Sprintf("%v:%v", environment.GetMySQLAddress(), port), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), DatabaseName: database2, }, }, } err = sql.CheckCompatibleVersion(cfg, "0.3") s.Error(err) s.Contains(err.Error(), "version mismatch") } func (s *VersionTestSuite) TestMultipleDatabaseVersionAllLowerCompatible() { r1 := rand.New(rand.NewSource(time.Now().UnixNano())) r2 := rand.New(rand.NewSource(time.Now().UnixNano())) database1 := fmt.Sprintf("version_test_%v", r1.Int63()) database2 := fmt.Sprintf("version_test_%v", r2.Int63()) defer s.createDatabase(database1)() defer s.createDatabase(database2)() tmpDir := s.T().TempDir() subdir := tmpDir + "/" + "db" s.NoError(os.Mkdir(subdir, os.FileMode(0744))) s.createSchemaForVersion(subdir, "0.1") s.createSchemaForVersion(subdir, "0.2") s.createSchemaForVersion(subdir, "0.3") mysqlPort, err := environment.GetMySQLPort() s.NoError(err) port := strconv.Itoa(mysqlPort) s.NoError(sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", database1, "-pl", s.pluginName, "-q", "setup-schema", "-version", "0.2", "-o", })) s.NoError(sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", database2, "-pl", s.pluginName, "-q", "setup-schema", "-version", "0.2", "-o", })) cfg := config.SQL{ PluginName: s.pluginName, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, UseMultipleDatabases: true, NumShards: 2, MultipleDatabasesConfig: []config.MultipleDatabasesConfigEntry{ { ConnectAddr: fmt.Sprintf("%v:%v", environment.GetMySQLAddress(), port), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), DatabaseName: database1, }, { ConnectAddr: fmt.Sprintf("%v:%v", environment.GetMySQLAddress(), port), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), DatabaseName: database2, }, }, } err = sql.CheckCompatibleVersion(cfg, "0.2") s.NoError(err) } func (s *VersionTestSuite) TestMultipleDatabaseVersionPartialLowerCompatible() { r1 := rand.New(rand.NewSource(time.Now().UnixNano())) r2 := rand.New(rand.NewSource(time.Now().UnixNano())) database1 := fmt.Sprintf("version_test_%v", r1.Int63()) database2 := fmt.Sprintf("version_test_%v", r2.Int63()) defer s.createDatabase(database1)() defer s.createDatabase(database2)() tmpDir := s.T().TempDir() subdir := tmpDir + "/" + "db" s.NoError(os.Mkdir(subdir, os.FileMode(0744))) s.createSchemaForVersion(subdir, "0.1") s.createSchemaForVersion(subdir, "0.2") s.createSchemaForVersion(subdir, "0.3") mysqlPort, err := environment.GetMySQLPort() s.NoError(err) port := strconv.Itoa(mysqlPort) s.NoError(sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", database1, "-pl", s.pluginName, "-q", "setup-schema", "-version", "0.3", "-o", })) s.NoError(sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", database2, "-pl", s.pluginName, "-q", "setup-schema", "-version", "0.2", "-o", })) cfg := config.SQL{ PluginName: s.pluginName, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, UseMultipleDatabases: true, NumShards: 2, MultipleDatabasesConfig: []config.MultipleDatabasesConfigEntry{ { ConnectAddr: fmt.Sprintf("%v:%v", environment.GetMySQLAddress(), port), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), DatabaseName: database1, }, { ConnectAddr: fmt.Sprintf("%v:%v", environment.GetMySQLAddress(), port), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), DatabaseName: database2, }, }, } err = sql.CheckCompatibleVersion(cfg, "0.2") s.NoError(err) } func (s *VersionTestSuite) TestMultipleDatabaseVersionExactlyMatchCompatible() { r1 := rand.New(rand.NewSource(time.Now().UnixNano())) r2 := rand.New(rand.NewSource(time.Now().UnixNano())) database1 := fmt.Sprintf("version_test_%v", r1.Int63()) database2 := fmt.Sprintf("version_test_%v", r2.Int63()) defer s.createDatabase(database1)() defer s.createDatabase(database2)() tmpDir := s.T().TempDir() subdir := tmpDir + "/" + "db" s.NoError(os.Mkdir(subdir, os.FileMode(0744))) s.createSchemaForVersion(subdir, "0.1") s.createSchemaForVersion(subdir, "0.2") s.createSchemaForVersion(subdir, "0.3") mysqlPort, err := environment.GetMySQLPort() s.NoError(err) port := strconv.Itoa(mysqlPort) s.NoError(sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", database1, "-pl", s.pluginName, "-q", "setup-schema", "-version", "0.3", "-o", })) s.NoError(sql.RunTool([]string{ "./tool", "-ep", environment.GetMySQLAddress(), "-p", port, "-u", environment.GetMySQLUser(), "-pw", environment.GetMySQLPassword(), "-db", database2, "-pl", s.pluginName, "-q", "setup-schema", "-version", "0.3", "-o", })) cfg := config.SQL{ PluginName: s.pluginName, EncodingType: "thriftrw", DecodingTypes: []string{"thriftrw"}, UseMultipleDatabases: true, NumShards: 2, MultipleDatabasesConfig: []config.MultipleDatabasesConfigEntry{ { ConnectAddr: fmt.Sprintf("%v:%v", environment.GetMySQLAddress(), port), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), DatabaseName: database1, }, { ConnectAddr: fmt.Sprintf("%v:%v", environment.GetMySQLAddress(), port), User: environment.GetMySQLUser(), Password: environment.GetMySQLPassword(), DatabaseName: database2, }, }, } err = sql.CheckCompatibleVersion(cfg, "0.3") s.NoError(err) } ================================================ FILE: host/client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc" historyv1 "github.com/uber/cadence/.gen/proto/history/v1" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/client/wrappers/grpc" "github.com/uber/cadence/common/service" ) // AdminClient is the interface exposed by admin service client type AdminClient interface { admin.Client } // FrontendClient is the interface exposed by frontend service client type FrontendClient interface { frontend.Client } // HistoryClient is the interface exposed by history service client type HistoryClient interface { history.Client } type MatchingClient interface { matching.Client } // NewAdminClient creates a client to cadence admin client func NewAdminClient(d *yarpc.Dispatcher) AdminClient { return grpc.NewAdminClient(adminv1.NewAdminAPIYARPCClient(d.ClientConfig(testOutboundName(service.Frontend)))) } // NewFrontendClient creates a client to cadence frontend client func NewFrontendClient(d *yarpc.Dispatcher) FrontendClient { config := d.ClientConfig(testOutboundName(service.Frontend)) return grpc.NewFrontendClient( apiv1.NewDomainAPIYARPCClient(config), apiv1.NewWorkflowAPIYARPCClient(config), apiv1.NewWorkerAPIYARPCClient(config), apiv1.NewVisibilityAPIYARPCClient(config), ) } // NewHistoryClient creates a client to cadence history service client func NewHistoryClient(d *yarpc.Dispatcher) HistoryClient { return grpc.NewHistoryClient(historyv1.NewHistoryAPIYARPCClient(d.ClientConfig(testOutboundName(service.History)))) } ================================================ FILE: host/client_integration_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "bytes" "context" "encoding/gob" "errors" "flag" "fmt" "reflect" "strconv" "strings" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/client" "go.uber.org/cadence/compatibility" "go.uber.org/cadence/encoded" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/yarpc" "go.uber.org/yarpc/transport/grpc" "go.uber.org/zap" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/service" ) func init() { workflow.Register(testDataConverterWorkflow) activity.Register(testActivity) workflow.Register(testParentWorkflow) workflow.Register(testChildWorkflow) } func TestClientIntegrationSuite(t *testing.T) { flag.Parse() clusterConfig, err := GetTestClusterConfig("testdata/clientintegrationtestcluster.yaml") if err != nil { panic(err) } testCluster := NewPersistenceTestCluster(t, clusterConfig) s := new(ClientIntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } func (s *ClientIntegrationSuite) SetupSuite() { s.setupSuite() var err error s.wfService, err = s.buildServiceClient() if err != nil { s.Logger.Fatal("Error when build service client", tag.Error(err)) } s.wfClient = client.NewClient(s.wfService, s.DomainName, nil) s.taskList = "client-integration-test-tasklist" s.worker = worker.New(s.wfService, s.DomainName, s.taskList, worker.Options{}) if err := s.worker.Start(); err != nil { s.Logger.Fatal("Error when start worker", tag.Error(err)) } else { s.Logger.Info("Worker started") } } func (s *ClientIntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() } func (s *ClientIntegrationSuite) buildServiceClient() (workflowserviceclient.Interface, error) { cadenceClientName := "cadence-client" hostPort := "127.0.0.1:7114" // use grpc port if TestFlags.FrontendAddr != "" { hostPort = TestFlags.FrontendAddr } ch := grpc.NewTransport( grpc.ServerMaxRecvMsgSize(32*1024*1024), grpc.ClientMaxRecvMsgSize(32*1024*1024), ) dispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: cadenceClientName, Outbounds: yarpc.Outbounds{ service.Frontend: {Unary: ch.NewSingleOutbound(hostPort)}, }, }) if dispatcher == nil { s.Logger.Fatal("No RPC dispatcher provided to create a connection to Cadence Service") } if err := dispatcher.Start(); err != nil { s.Logger.Fatal("Failed to create outbound transport channel", tag.Error(err)) } cc := dispatcher.ClientConfig(service.Frontend) return compatibility.NewThrift2ProtoAdapter( apiv1.NewDomainAPIYARPCClient(cc), apiv1.NewWorkflowAPIYARPCClient(cc), apiv1.NewWorkerAPIYARPCClient(cc), apiv1.NewVisibilityAPIYARPCClient(cc), ), nil } func (s *ClientIntegrationSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } // testDataConverter implements encoded.DataConverter using gob type testDataConverter struct { NumOfCallToData int // for testing to know testDataConverter is called as expected NumOfCallFromData int } func (tdc *testDataConverter) ToData(value ...interface{}) ([]byte, error) { tdc.NumOfCallToData++ var buf bytes.Buffer enc := gob.NewEncoder(&buf) for i, obj := range value { if err := enc.Encode(obj); err != nil { return nil, fmt.Errorf( "unable to encode argument: %d, %v, with gob error: %v", i, reflect.TypeOf(obj), err) } } return buf.Bytes(), nil } func (tdc *testDataConverter) FromData(input []byte, valuePtr ...interface{}) error { tdc.NumOfCallFromData++ dec := gob.NewDecoder(bytes.NewBuffer(input)) for i, obj := range valuePtr { if err := dec.Decode(obj); err != nil { return fmt.Errorf( "unable to decode argument: %d, %v, with gob error: %v", i, reflect.TypeOf(obj), err) } } return nil } func newTestDataConverter() encoded.DataConverter { return &testDataConverter{} } func testActivity(ctx context.Context, msg string) (string, error) { return "hello_" + msg, nil } func testDataConverterWorkflow(ctx workflow.Context, tl string) (string, error) { ao := workflow.ActivityOptions{ ScheduleToStartTimeout: 20 * time.Second, StartToCloseTimeout: 40 * time.Second, } ctx = workflow.WithActivityOptions(ctx, ao) var result string err := workflow.ExecuteActivity(ctx, testActivity, "world").Get(ctx, &result) if err != nil { return "", err } // use another converter to run activity, // with new taskList so that worker with same data converter can properly process tasks. var result1 string ctx1 := workflow.WithDataConverter(ctx, newTestDataConverter()) ctx1 = workflow.WithTaskList(ctx1, tl) err1 := workflow.ExecuteActivity(ctx1, testActivity, "world1").Get(ctx1, &result1) if err1 != nil { return "", err1 } return result + "," + result1, nil } func (s *ClientIntegrationSuite) startWorkerWithDataConverter(tl string, dataConverter encoded.DataConverter) worker.Worker { opts := worker.Options{} if dataConverter != nil { opts.DataConverter = dataConverter } worker := worker.New(s.wfService, s.DomainName, tl, opts) if err := worker.Start(); err != nil { s.Logger.Fatal("Error when start worker with data converter", tag.Error(err)) } return worker } func (s *ClientIntegrationSuite) TestClientDataConverter() { tl := "client-integration-data-converter-activity-tasklist" dc := newTestDataConverter() worker := s.startWorkerWithDataConverter(tl, dc) defer worker.Stop() id := "client-integration-data-converter-workflow" workflowOptions := client.StartWorkflowOptions{ ID: id, TaskList: s.taskList, ExecutionStartToCloseTimeout: 60 * time.Second, } ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() we, err := s.wfClient.ExecuteWorkflow(ctx, workflowOptions, testDataConverterWorkflow, tl) if err != nil { s.Logger.Fatal("Start workflow with err", tag.Error(err)) } s.NotNil(we) s.True(we.GetRunID() != "") var res string err = we.Get(ctx, &res) s.NoError(err) s.Equal("hello_world,hello_world1", res) // to ensure custom data converter is used, this number might be different if client changed. d := dc.(*testDataConverter) s.Equal(1, d.NumOfCallToData) s.Equal(1, d.NumOfCallFromData) } func (s *ClientIntegrationSuite) TestClientDataConverter_Failed() { tl := "client-integration-data-converter-activity-failed-tasklist" worker := s.startWorkerWithDataConverter(tl, nil) // mismatch of data converter defer worker.Stop() id := "client-integration-data-converter-failed-workflow" workflowOptions := client.StartWorkflowOptions{ ID: id, TaskList: s.taskList, ExecutionStartToCloseTimeout: 60 * time.Second, } ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() we, err := s.wfClient.ExecuteWorkflow(ctx, workflowOptions, testDataConverterWorkflow, tl) if err != nil { s.Logger.Fatal("Start workflow with err", tag.Error(err)) } s.NotNil(we) s.True(we.GetRunID() != "") var res string err = we.Get(ctx, &res) s.Error(err) // Get history to make sure only the 2nd activity is failed because of mismatch of data converter iter := s.wfClient.GetWorkflowHistory(ctx, id, we.GetRunID(), false, 0) completedAct := 0 failedAct := 0 for iter.HasNext() { event, err := iter.Next() s.Nil(err) if event.GetEventType() == shared.EventTypeActivityTaskCompleted { completedAct++ } if event.GetEventType() == shared.EventTypeActivityTaskFailed { failedAct++ attr := event.ActivityTaskFailedEventAttributes s.True(strings.HasPrefix(string(attr.Details), "unable to decode the activity function input bytes with error")) } } s.Equal(1, completedAct) s.Equal(1, failedAct) } var childTaskList = "client-integration-data-converter-child-tasklist" func testParentWorkflow(ctx workflow.Context) (string, error) { logger := workflow.GetLogger(ctx) execution := workflow.GetInfo(ctx).WorkflowExecution childID := fmt.Sprintf("child_workflow:%v", execution.RunID) cwo := workflow.ChildWorkflowOptions{ WorkflowID: childID, ExecutionStartToCloseTimeout: time.Minute, } ctx = workflow.WithChildOptions(ctx, cwo) var result string err := workflow.ExecuteChildWorkflow(ctx, testChildWorkflow, 0, 3).Get(ctx, &result) if err != nil { logger.Error("Parent execution received child execution failure.", zap.Error(err)) return "", err } childID1 := fmt.Sprintf("child_workflow1:%v", execution.RunID) cwo1 := workflow.ChildWorkflowOptions{ WorkflowID: childID1, ExecutionStartToCloseTimeout: time.Minute, TaskList: childTaskList, } ctx1 := workflow.WithChildOptions(ctx, cwo1) ctx1 = workflow.WithDataConverter(ctx1, newTestDataConverter()) var result1 string err1 := workflow.ExecuteChildWorkflow(ctx1, testChildWorkflow, 0, 2).Get(ctx1, &result1) if err1 != nil { logger.Error("Parent execution received child execution 1 failure.", zap.Error(err1)) return "", err1 } res := fmt.Sprintf("Complete child1 %s times, complete child2 %s times", result, result1) logger.Info("Parent execution completed.", zap.String("Result", res)) return res, nil } func testChildWorkflow(ctx workflow.Context, totalCount, runCount int) (string, error) { logger := workflow.GetLogger(ctx) logger.Info("Child workflow execution started.") if runCount <= 0 { logger.Error("Invalid valid for run count.", zap.Int("RunCount", runCount)) return "", errors.New("invalid run count") } totalCount++ runCount-- if runCount == 0 { result := fmt.Sprintf("Child workflow execution completed after %v runs", totalCount) logger.Info("Child workflow completed.", zap.String("Result", result)) return strconv.Itoa(totalCount), nil } logger.Info("Child workflow starting new run.", zap.Int("RunCount", runCount), zap.Int("TotalCount", totalCount)) return "", workflow.NewContinueAsNewError(ctx, testChildWorkflow, totalCount, runCount) } func (s *ClientIntegrationSuite) TestClientDataConverter_WithChild() { dc := newTestDataConverter() worker := s.startWorkerWithDataConverter(childTaskList, dc) defer worker.Stop() id := "client-integration-data-converter-with-child-workflow" workflowOptions := client.StartWorkflowOptions{ ID: id, TaskList: s.taskList, ExecutionStartToCloseTimeout: 60 * time.Second, } ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() we, err := s.wfClient.ExecuteWorkflow(ctx, workflowOptions, testParentWorkflow) if err != nil { s.Logger.Fatal("Start workflow with err", tag.Error(err)) } s.NotNil(we) s.True(we.GetRunID() != "") var res string err = we.Get(ctx, &res) s.NoError(err) s.Equal("Complete child1 3 times, complete child2 2 times", res) // to ensure custom data converter is used, this number might be different if client changed. d := dc.(*testDataConverter) s.Equal(3, d.NumOfCallToData) s.Equal(2, d.NumOfCallFromData) } func (s *ClientIntegrationSuite) Test_StickyWorkerRestartDecisionTask() { testCases := []struct { name string waitTime time.Duration doQuery bool doSignal bool delayCheck func(duration time.Duration) bool }{ { name: "new_query_after_10s_no_delay", waitTime: 10 * time.Second, doQuery: true, delayCheck: func(duration time.Duration) bool { return duration < 5*time.Second }, }, { name: "new_query_immediately_expect_5s_delay", waitTime: 0, doQuery: true, delayCheck: func(duration time.Duration) bool { return duration > 5*time.Second }, }, { name: "new_workflow_task_after_10s_no_delay", waitTime: 10 * time.Second, doSignal: true, delayCheck: func(duration time.Duration) bool { return duration < 5*time.Second }, }, { name: "new_workflow_task_immediately_expect_5s_delay", waitTime: 0, doSignal: true, delayCheck: func(duration time.Duration) bool { return duration > 5*time.Second }, }, } for _, tt := range testCases { s.Run(tt.name, func() { workflowFn := func(ctx workflow.Context) (string, error) { workflow.SetQueryHandler(ctx, "test", func() (string, error) { return "query works", nil }) signalCh := workflow.GetSignalChannel(ctx, "test") var msg string signalCh.Receive(ctx, &msg) return msg, nil } taskList := "task-list-" + tt.name oldWorker := worker.New(s.wfService, s.DomainName, taskList, worker.Options{}) oldWorker.RegisterWorkflow(workflowFn) if err := oldWorker.Start(); err != nil { s.Logger.Fatal("Error when start worker", tag.Error(err)) } id := "test-sticky-delay" + tt.name workflowOptions := client.StartWorkflowOptions{ ID: id, TaskList: taskList, ExecutionStartToCloseTimeout: 20 * time.Second, } ctx, cancel := context.WithTimeout(context.Background(), 1000*time.Second) defer cancel() workflowRun, err := s.wfClient.ExecuteWorkflow(ctx, workflowOptions, workflowFn) if err != nil { s.Logger.Fatal("Start workflow failed with err", tag.Error(err)) } s.NotNil(workflowRun) s.True(workflowRun.GetRunID() != "") findFirstWorkflowTaskCompleted := false WaitForFirstWorkflowTaskComplete: for i := 0; i < 10; i++ { // wait until first workflow task completed (so we know sticky is set on workflow) iter := s.wfClient.GetWorkflowHistory(ctx, id, "", false, 0) for iter.HasNext() { evt, err := iter.Next() s.NoError(err) if evt.GetEventType() == shared.EventTypeDecisionTaskCompleted { findFirstWorkflowTaskCompleted = true break WaitForFirstWorkflowTaskComplete } } time.Sleep(time.Second) } s.True(findFirstWorkflowTaskCompleted) // stop old worker oldWorker.Stop() // maybe wait for 10s, which will make matching aware the old sticky worker is unavailable time.Sleep(tt.waitTime) // start a new worker newWorker := worker.New(s.wfService, s.DomainName, taskList, worker.Options{}) newWorker.RegisterWorkflow(workflowFn) if err := newWorker.Start(); err != nil { s.Logger.Fatal("Error when start worker", tag.Error(err)) } defer newWorker.Stop() startTime := time.Now() // send a signal, and workflow should complete immediately, there should not be 5s delay if tt.doSignal { err = s.wfClient.SignalWorkflow(ctx, id, "", "test", "test") s.NoError(err) err = workflowRun.Get(ctx, nil) s.NoError(err) } else if tt.doQuery { // send a signal, and workflow should complete immediately, there should not be 5s delay queryResult, err := s.wfClient.QueryWorkflow(ctx, id, "", "test", "test") s.NoError(err) var queryResultStr string err = queryResult.Get(&queryResultStr) s.NoError(err) s.Equal("query works", queryResultStr) } endTime := time.Now() duration := endTime.Sub(startTime) s.True(tt.delayCheck(duration), "delay check failed: %s", duration) }) } } ================================================ FILE: host/continue_as_new_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "bytes" "encoding/binary" "strconv" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) func (s *IntegrationSuite) TestContinueAsNewWorkflow() { id := "integration-continue-as-new-workflow-test" wt := "integration-continue-as-new-workflow-test-type" tl := "integration-continue-as-new-workflow-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl header := &types.Header{ Fields: map[string][]byte{"tracing": []byte("sample payload")}, } memo := &types.Memo{ Fields: map[string][]byte{"memoKey": []byte("memoVal")}, } searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{"CustomKeywordField": []byte(`"1"`)}, } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, Header: header, Memo: memo, SearchAttributes: searchAttr, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false continueAsNewCount := int32(10) continueAsNewCounter := int32(0) var previousRunID string var lastRunStartedEvent *types.HistoryEvent dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if continueAsNewCounter < continueAsNewCount { previousRunID = execution.GetRunID() continueAsNewCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, continueAsNewCounter)) return []byte(strconv.Itoa(int(continueAsNewCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeContinueAsNewWorkflowExecution.Ptr(), ContinueAsNewWorkflowExecutionDecisionAttributes: &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: workflowType, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), Header: header, Memo: memo, SearchAttributes: searchAttr, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), }, }}, nil } lastRunStartedEvent = history.Events[0] workflowComplete = true return []byte(strconv.Itoa(int(continueAsNewCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } for i := 0; i < 10; i++ { ctx, cancel := createContext() descResp, err := s.Engine.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, }, }) cancel() s.NoError(err) s.NotZero(descResp.WorkflowExecutionInfo.GetStartTime()) _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err, strconv.Itoa(i)) } s.False(workflowComplete) _, err := poller.PollAndProcessDecisionTask(false, false) s.Nil(err) s.True(workflowComplete) s.Equal(previousRunID, lastRunStartedEvent.WorkflowExecutionStartedEventAttributes.GetContinuedExecutionRunID()) s.Equal(header, lastRunStartedEvent.WorkflowExecutionStartedEventAttributes.Header) s.Equal(memo, lastRunStartedEvent.WorkflowExecutionStartedEventAttributes.Memo) s.Equal(searchAttr, lastRunStartedEvent.WorkflowExecutionStartedEventAttributes.SearchAttributes) } func (s *IntegrationSuite) TestContinueAsNewWorkflow_Timeout() { id := "integration-continue-as-new-workflow-timeout-test" wt := "integration-continue-as-new-workflow-timeout-test-type" tl := "integration-continue-as-new-workflow-timeout-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false continueAsNewCount := int32(1) continueAsNewCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if continueAsNewCounter < continueAsNewCount { continueAsNewCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, continueAsNewCounter)) return []byte(strconv.Itoa(int(continueAsNewCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeContinueAsNewWorkflowExecution.Ptr(), ContinueAsNewWorkflowExecutionDecisionAttributes: &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: workflowType, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), // set timeout to 1 TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), }, }}, nil } workflowComplete = true return []byte(strconv.Itoa(int(continueAsNewCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } // process the decision and continue as new _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.False(workflowComplete) time.Sleep(1 * time.Second) // wait 1 second for timeout GetHistoryLoop: for i := 0; i < 20; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, }, }) cancel() s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if *lastEvent.EventType != types.EventTypeWorkflowExecutionTimedOut { s.Logger.Warn("Execution not timedout yet.") time.Sleep(200 * time.Millisecond) continue GetHistoryLoop } timeoutEventAttributes := lastEvent.WorkflowExecutionTimedOutEventAttributes s.Equal(types.TimeoutTypeStartToClose, *timeoutEventAttributes.TimeoutType) workflowComplete = true break GetHistoryLoop } s.True(workflowComplete) } func (s *IntegrationSuite) TestWorkflowContinueAsNew_TaskID() { id := "integration-wf-continue-as-new-task-id-test" wt := "integration-wf-continue-as-new-task-id-type" tl := "integration-wf-continue-as-new-task-id-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) var executions []*types.WorkflowExecution continueAsNewed := false dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { executions = append(executions, execution) if !continueAsNewed { continueAsNewed = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeContinueAsNewWorkflowExecution.Ptr(), ContinueAsNewWorkflowExecutionDecisionAttributes: &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), }, }}, nil } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("succeed"), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } minTaskID := int64(0) _, err := poller.PollAndProcessDecisionTask(false, false) s.Nil(err) events := s.getHistory(s.DomainName, executions[0]) s.True(len(events) != 0) for _, event := range events { s.True(event.TaskID > minTaskID) minTaskID = event.TaskID } _, err = poller.PollAndProcessDecisionTask(false, false) s.Nil(err) events = s.getHistory(s.DomainName, executions[1]) s.True(len(events) != 0) for _, event := range events { s.True(event.TaskID > minTaskID) minTaskID = event.TaskID } } func (s *IntegrationSuite) TestChildWorkflowWithContinueAsNew() { parentID := "integration-child-workflow-with-continue-as-new-test-parent" childID := "integration-child-workflow-with-continue-as-new-test-child" wtParent := "integration-child-workflow-with-continue-as-new-test-parent-type" wtChild := "integration-child-workflow-with-continue-as-new-test-child-type" tl := "integration-child-workflow-with-continue-as-new-test-tasklist" identity := "worker1" parentWorkflowType := &types.WorkflowType{} parentWorkflowType.Name = wtParent childWorkflowType := &types.WorkflowType{} childWorkflowType.Name = wtChild taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: parentID, WorkflowType: parentWorkflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic childComplete := false childExecutionStarted := false childData := int32(1) continueAsNewCount := int32(10) continueAsNewCounter := int32(0) var startedEvent *types.HistoryEvent var completedEvent *types.HistoryEvent dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { s.Logger.Info("Processing decision task for WorkflowID:", tag.WorkflowID(execution.WorkflowID)) // Child Decider Logic if execution.WorkflowID == childID { if continueAsNewCounter < continueAsNewCount { continueAsNewCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, continueAsNewCounter)) return []byte(strconv.Itoa(int(continueAsNewCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeContinueAsNewWorkflowExecution.Ptr(), ContinueAsNewWorkflowExecutionDecisionAttributes: &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ Input: buf.Bytes(), }, }}, nil } childComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Child Done."), }, }}, nil } // Parent Decider Logic if execution.WorkflowID == parentID { if !childExecutionStarted { s.Logger.Info("Starting child execution.") childExecutionStarted = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, childData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeStartChildWorkflowExecution.Ptr(), StartChildWorkflowExecutionDecisionAttributes: &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: s.DomainName, WorkflowID: childID, WorkflowType: childWorkflowType, Input: buf.Bytes(), }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeChildWorkflowExecutionStarted { startedEvent = event return nil, []*types.Decision{}, nil } if *event.EventType == types.EventTypeChildWorkflowExecutionCompleted { completedEvent = event return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } } } } return nil, nil, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } // Make first decision to start child execution _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(childExecutionStarted) // Process ChildExecution Started event and all generations of child executions for i := 0; i < 11; i++ { s.Logger.Warn("decision: %v", tag.Counter(i)) _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) } s.False(childComplete) s.NotNil(startedEvent) // Process Child Execution final decision to complete it _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(childComplete) // Process ChildExecution completed event and complete parent execution _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.NotNil(completedEvent) completedAttributes := completedEvent.ChildWorkflowExecutionCompletedEventAttributes s.Equal(s.DomainName, completedAttributes.Domain) s.Equal(childID, completedAttributes.WorkflowExecution.WorkflowID) s.NotEqual(startedEvent.ChildWorkflowExecutionStartedEventAttributes.WorkflowExecution.RunID, completedAttributes.WorkflowExecution.RunID) s.Equal(wtChild, completedAttributes.WorkflowType.Name) s.Equal([]byte("Child Done."), completedAttributes.Result) s.Logger.Info("Parent Workflow Execution History: ") } ================================================ FILE: host/decision_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "encoding/json" "strconv" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) func (s *IntegrationSuite) TestDecisionHeartbeatingWithEmptyResult() { id := uuid.New() wt := "integration-workflow-decision-heartbeating-local-activities" tl := id identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{ Name: tl, Kind: types.TaskListKindNormal.Ptr(), } stickyTaskList := &types.TaskList{ Name: "test-sticky-tasklist", Kind: types.TaskListKindSticky.Ptr(), } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(20), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(3), Identity: identity, } ctx, cancel := createContext() defer cancel() resp0, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) we := &types.WorkflowExecution{ WorkflowID: id, RunID: resp0.RunID, } s.assertLastHistoryEvent(we, 2, types.EventTypeDecisionTaskScheduled) // start decision ctx, cancel = createContext() defer cancel() resp1, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) s.Nil(err1) s.Equal(int64(0), resp1.GetAttempt()) s.assertLastHistoryEvent(we, 3, types.EventTypeDecisionTaskStarted) taskToken := resp1.GetTaskToken() hbTimeout := 0 for i := 0; i < 12; i++ { ctx, cancel := createContext() resp2, err2 := s.Engine.RespondDecisionTaskCompleted(ctx, &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: []*types.Decision{}, StickyAttributes: &types.StickyExecutionAttributes{ WorkerTaskList: stickyTaskList, ScheduleToStartTimeoutSeconds: common.Int32Ptr(5), }, ReturnNewDecisionTask: true, ForceCreateNewDecisionTask: true, }) cancel() if _, ok := err2.(*types.EntityNotExistsError); ok { hbTimeout++ ctx, cancel := createContext() resp, err := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) cancel() s.Nil(err) taskToken = resp.GetTaskToken() } else { s.Nil(err2) taskToken = resp2.DecisionTask.GetTaskToken() } time.Sleep(time.Second) } s.Equal(2, hbTimeout) ctx, cancel = createContext() defer cancel() resp5, err5 := s.Engine.RespondDecisionTaskCompleted(ctx, &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: []*types.Decision{ { DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("efg"), }, }, }, StickyAttributes: &types.StickyExecutionAttributes{ WorkerTaskList: stickyTaskList, ScheduleToStartTimeoutSeconds: common.Int32Ptr(5), }, ReturnNewDecisionTask: true, ForceCreateNewDecisionTask: false, }) s.Nil(err5) s.Nil(resp5.DecisionTask) s.assertLastHistoryEvent(we, 41, types.EventTypeWorkflowExecutionCompleted) } func (s *IntegrationSuite) TestDecisionHeartbeatingWithLocalActivitiesResult() { id := uuid.New() wt := "integration-workflow-decision-heartbeating-local-activities" tl := id identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{ Name: tl, Kind: types.TaskListKindNormal.Ptr(), } stikyTaskList := &types.TaskList{ Name: "test-sticky-tasklist", Kind: types.TaskListKindSticky.Ptr(), } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(20), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(5), Identity: identity, } ctx, cancel := createContext() defer cancel() resp0, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) we := &types.WorkflowExecution{ WorkflowID: id, RunID: resp0.RunID, } s.assertLastHistoryEvent(we, 2, types.EventTypeDecisionTaskScheduled) // start decision ctx, cancel = createContext() defer cancel() resp1, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) s.Nil(err1) s.Equal(int64(0), resp1.GetAttempt()) s.assertLastHistoryEvent(we, 3, types.EventTypeDecisionTaskStarted) ctx, cancel = createContext() defer cancel() resp2, err2 := s.Engine.RespondDecisionTaskCompleted(ctx, &types.RespondDecisionTaskCompletedRequest{ TaskToken: resp1.GetTaskToken(), Decisions: []*types.Decision{}, StickyAttributes: &types.StickyExecutionAttributes{ WorkerTaskList: stikyTaskList, ScheduleToStartTimeoutSeconds: common.Int32Ptr(5), }, ReturnNewDecisionTask: true, ForceCreateNewDecisionTask: true, }) s.Nil(err2) ctx, cancel = createContext() defer cancel() resp3, err3 := s.Engine.RespondDecisionTaskCompleted(ctx, &types.RespondDecisionTaskCompletedRequest{ TaskToken: resp2.DecisionTask.GetTaskToken(), Decisions: []*types.Decision{ { DecisionType: types.DecisionTypeRecordMarker.Ptr(), RecordMarkerDecisionAttributes: &types.RecordMarkerDecisionAttributes{ MarkerName: "localActivity1", Details: []byte("abc"), }, }, }, StickyAttributes: &types.StickyExecutionAttributes{ WorkerTaskList: stikyTaskList, ScheduleToStartTimeoutSeconds: common.Int32Ptr(5), }, ReturnNewDecisionTask: true, ForceCreateNewDecisionTask: true, }) s.Nil(err3) ctx, cancel = createContext() defer cancel() resp4, err4 := s.Engine.RespondDecisionTaskCompleted(ctx, &types.RespondDecisionTaskCompletedRequest{ TaskToken: resp3.DecisionTask.GetTaskToken(), Decisions: []*types.Decision{ { DecisionType: types.DecisionTypeRecordMarker.Ptr(), RecordMarkerDecisionAttributes: &types.RecordMarkerDecisionAttributes{ MarkerName: "localActivity2", Details: []byte("abc"), }, }, }, StickyAttributes: &types.StickyExecutionAttributes{ WorkerTaskList: stikyTaskList, ScheduleToStartTimeoutSeconds: common.Int32Ptr(5), }, ReturnNewDecisionTask: true, ForceCreateNewDecisionTask: true, }) s.Nil(err4) ctx, cancel = createContext() defer cancel() resp5, err5 := s.Engine.RespondDecisionTaskCompleted(ctx, &types.RespondDecisionTaskCompletedRequest{ TaskToken: resp4.DecisionTask.GetTaskToken(), Decisions: []*types.Decision{ { DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("efg"), }, }, }, StickyAttributes: &types.StickyExecutionAttributes{ WorkerTaskList: stikyTaskList, ScheduleToStartTimeoutSeconds: common.Int32Ptr(5), }, ReturnNewDecisionTask: true, ForceCreateNewDecisionTask: false, }) s.Nil(err5) s.Nil(resp5.DecisionTask) expectedHistory := []types.EventType{ types.EventTypeWorkflowExecutionStarted, types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskCompleted, types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskCompleted, types.EventTypeMarkerRecorded, types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskCompleted, types.EventTypeMarkerRecorded, types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskCompleted, types.EventTypeWorkflowExecutionCompleted, } s.assertHistory(we, expectedHistory) } func (s *IntegrationSuite) TestWorkflowTerminationSignalBeforeRegularDecisionStarted() { id := uuid.New() wt := "integration-workflow-transient-decision-test-type" tl := id identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() resp0, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) we := &types.WorkflowExecution{ WorkflowID: id, RunID: resp0.RunID, } s.assertLastHistoryEvent(we, 2, types.EventTypeDecisionTaskScheduled) ctx, cancel = createContext() defer cancel() err0 = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, SignalName: "sig-for-integ-test", Input: []byte(""), Identity: "integ test", RequestID: uuid.New(), }) s.Nil(err0) s.assertLastHistoryEvent(we, 3, types.EventTypeWorkflowExecutionSignaled) ctx, cancel = createContext() defer cancel() // start this transient decision, the attempt should be cleared and it becomes again a regular decision resp1, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) s.Nil(err1) s.Equal(int64(0), resp1.GetAttempt()) s.assertLastHistoryEvent(we, 4, types.EventTypeDecisionTaskStarted) ctx, cancel = createContext() defer cancel() // then terminate the worklfow err := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, Reason: "test-reason", }) s.Nil(err) expectedHistory := []types.EventType{ types.EventTypeWorkflowExecutionStarted, types.EventTypeDecisionTaskScheduled, types.EventTypeWorkflowExecutionSignaled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskFailed, types.EventTypeWorkflowExecutionTerminated, } s.assertHistory(we, expectedHistory) } func (s *IntegrationSuite) TestWorkflowTerminationSignalAfterRegularDecisionStarted() { id := uuid.New() wt := "integration-workflow-transient-decision-test-type" tl := id identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() resp0, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) we := &types.WorkflowExecution{ WorkflowID: id, RunID: resp0.RunID, } s.assertLastHistoryEvent(we, 2, types.EventTypeDecisionTaskScheduled) ctx, cancel = createContext() defer cancel() // start decision to make signals into bufferedEvents _, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) s.Nil(err1) s.assertLastHistoryEvent(we, 3, types.EventTypeDecisionTaskStarted) ctx, cancel = createContext() defer cancel() // this signal should be buffered err0 = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, SignalName: "sig-for-integ-test", Input: []byte(""), Identity: "integ test", RequestID: uuid.New(), }) s.Nil(err0) s.assertLastHistoryEvent(we, 3, types.EventTypeDecisionTaskStarted) ctx, cancel = createContext() defer cancel() // then terminate the worklfow err := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, Reason: "test-reason", }) s.Nil(err) expectedHistory := []types.EventType{ types.EventTypeWorkflowExecutionStarted, types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskFailed, types.EventTypeWorkflowExecutionSignaled, types.EventTypeWorkflowExecutionTerminated, } s.assertHistory(we, expectedHistory) } func (s *IntegrationSuite) TestWorkflowTerminationSignalAfterRegularDecisionStartedAndFailDecision() { id := uuid.New() wt := "integration-workflow-transient-decision-test-type" tl := id identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() resp0, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) we := &types.WorkflowExecution{ WorkflowID: id, RunID: resp0.RunID, } s.assertLastHistoryEvent(we, 2, types.EventTypeDecisionTaskScheduled) cause := types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure ctx, cancel = createContext() defer cancel() // start decision to make signals into bufferedEvents resp1, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) s.Nil(err1) s.assertLastHistoryEvent(we, 3, types.EventTypeDecisionTaskStarted) ctx, cancel = createContext() defer cancel() // this signal should be buffered err0 = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, SignalName: "sig-for-integ-test", Input: []byte(""), Identity: "integ test", RequestID: uuid.New(), }) s.Nil(err0) s.assertLastHistoryEvent(we, 3, types.EventTypeDecisionTaskStarted) ctx, cancel = createContext() defer cancel() // fail this decision to flush buffer, and then another decision will be scheduled err2 := s.Engine.RespondDecisionTaskFailed(ctx, &types.RespondDecisionTaskFailedRequest{ TaskToken: resp1.GetTaskToken(), Cause: &cause, Identity: "integ test", }) s.Nil(err2) s.assertLastHistoryEvent(we, 6, types.EventTypeDecisionTaskScheduled) ctx, cancel = createContext() defer cancel() // then terminate the worklfow err := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, Reason: "test-reason", }) s.Nil(err) expectedHistory := []types.EventType{ types.EventTypeWorkflowExecutionStarted, types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskFailed, types.EventTypeWorkflowExecutionSignaled, types.EventTypeDecisionTaskScheduled, types.EventTypeWorkflowExecutionTerminated, } s.assertHistory(we, expectedHistory) } func (s *IntegrationSuite) TestWorkflowTerminationSignalBeforeTransientDecisionStarted() { id := uuid.New() wt := "integration-workflow-transient-decision-test-type" tl := id identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() resp0, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) we := &types.WorkflowExecution{ WorkflowID: id, RunID: resp0.RunID, } s.assertLastHistoryEvent(we, 2, types.EventTypeDecisionTaskScheduled) cause := types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure for i := 0; i < 10; i++ { ctx, cancel := createContext() resp1, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) cancel() s.Nil(err1) s.Equal(int64(i), resp1.GetAttempt()) if i == 0 { // first time is regular decision s.Equal(int64(3), resp1.GetStartedEventID()) } else { // the rest is transient decision s.Equal(int64(6), resp1.GetStartedEventID()) } ctx, cancel = createContext() err2 := s.Engine.RespondDecisionTaskFailed(ctx, &types.RespondDecisionTaskFailedRequest{ TaskToken: resp1.GetTaskToken(), Cause: &cause, Identity: "integ test", }) cancel() s.Nil(err2) } s.assertLastHistoryEvent(we, 4, types.EventTypeDecisionTaskFailed) ctx, cancel = createContext() defer cancel() err0 = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, SignalName: "sig-for-integ-test", Input: []byte(""), Identity: "integ test", RequestID: uuid.New(), }) s.Nil(err0) s.assertLastHistoryEvent(we, 5, types.EventTypeWorkflowExecutionSignaled) ctx, cancel = createContext() defer cancel() // start this transient decision, the attempt should be cleared and it becomes again a regular decision resp1, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) s.Nil(err1) s.Equal(int64(0), resp1.GetAttempt()) s.assertLastHistoryEvent(we, 7, types.EventTypeDecisionTaskStarted) ctx, cancel = createContext() defer cancel() // then terminate the worklfow err := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, Reason: "test-reason", }) s.Nil(err) expectedHistory := []types.EventType{ types.EventTypeWorkflowExecutionStarted, types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskFailed, types.EventTypeWorkflowExecutionSignaled, types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskFailed, types.EventTypeWorkflowExecutionTerminated, } s.assertHistory(we, expectedHistory) } func (s *IntegrationSuite) TestWorkflowTerminationSignalAfterTransientDecisionStarted() { id := uuid.New() wt := "integration-workflow-transient-decision-test-type" tl := id identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() resp0, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) we := &types.WorkflowExecution{ WorkflowID: id, RunID: resp0.RunID, } s.assertLastHistoryEvent(we, 2, types.EventTypeDecisionTaskScheduled) cause := types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure for i := 0; i < 10; i++ { ctx, cancel := createContext() resp1, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) cancel() s.Nil(err1) s.Equal(int64(i), resp1.GetAttempt()) if i == 0 { // first time is regular decision s.Equal(int64(3), resp1.GetStartedEventID()) } else { // the rest is transient decision s.Equal(int64(6), resp1.GetStartedEventID()) } ctx, cancel = createContext() err2 := s.Engine.RespondDecisionTaskFailed(ctx, &types.RespondDecisionTaskFailedRequest{ TaskToken: resp1.GetTaskToken(), Cause: &cause, Identity: "integ test", }) cancel() s.Nil(err2) } s.assertLastHistoryEvent(we, 4, types.EventTypeDecisionTaskFailed) ctx, cancel = createContext() defer cancel() // start decision to make signals into bufferedEvents _, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) s.Nil(err1) s.assertLastHistoryEvent(we, 4, types.EventTypeDecisionTaskFailed) ctx, cancel = createContext() defer cancel() // this signal should be buffered err0 = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, SignalName: "sig-for-integ-test", Input: []byte(""), Identity: "integ test", RequestID: uuid.New(), }) s.Nil(err0) s.assertLastHistoryEvent(we, 4, types.EventTypeDecisionTaskFailed) ctx, cancel = createContext() defer cancel() // then terminate the worklfow err := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, Reason: "test-reason", }) s.Nil(err) expectedHistory := []types.EventType{ types.EventTypeWorkflowExecutionStarted, types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskFailed, types.EventTypeWorkflowExecutionSignaled, types.EventTypeWorkflowExecutionTerminated, } s.assertHistory(we, expectedHistory) } func (s *IntegrationSuite) TestWorkflowTerminationSignalAfterTransientDecisionStartedAndFailDecision() { id := uuid.New() wt := "integration-workflow-transient-decision-test-type" tl := id identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() resp0, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) we := &types.WorkflowExecution{ WorkflowID: id, RunID: resp0.RunID, } s.assertLastHistoryEvent(we, 2, types.EventTypeDecisionTaskScheduled) cause := types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure for i := 0; i < 10; i++ { ctx, cancel := createContext() resp1, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) cancel() s.Nil(err1) s.Equal(int64(i), resp1.GetAttempt()) if i == 0 { // first time is regular decision s.Equal(int64(3), resp1.GetStartedEventID()) } else { // the rest is transient decision s.Equal(int64(6), resp1.GetStartedEventID()) } ctx, cancel = createContext() err2 := s.Engine.RespondDecisionTaskFailed(ctx, &types.RespondDecisionTaskFailedRequest{ TaskToken: resp1.GetTaskToken(), Cause: &cause, Identity: "integ test", }) cancel() s.Nil(err2) } s.assertLastHistoryEvent(we, 4, types.EventTypeDecisionTaskFailed) ctx, cancel = createContext() defer cancel() // start decision to make signals into bufferedEvents resp1, err1 := s.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: taskList, Identity: identity, }) s.Nil(err1) s.assertLastHistoryEvent(we, 4, types.EventTypeDecisionTaskFailed) ctx, cancel = createContext() defer cancel() // this signal should be buffered err0 = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, SignalName: "sig-for-integ-test", Input: []byte(""), Identity: "integ test", RequestID: uuid.New(), }) s.Nil(err0) s.assertLastHistoryEvent(we, 4, types.EventTypeDecisionTaskFailed) ctx, cancel = createContext() defer cancel() // fail this decision to flush buffer err2 := s.Engine.RespondDecisionTaskFailed(ctx, &types.RespondDecisionTaskFailedRequest{ TaskToken: resp1.GetTaskToken(), Cause: &cause, Identity: "integ test", }) s.Nil(err2) s.assertLastHistoryEvent(we, 6, types.EventTypeDecisionTaskScheduled) ctx, cancel = createContext() defer cancel() // then terminate the worklfow err := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: we, Reason: "test-reason", }) s.Nil(err) expectedHistory := []types.EventType{ types.EventTypeWorkflowExecutionStarted, types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskFailed, types.EventTypeWorkflowExecutionSignaled, types.EventTypeDecisionTaskScheduled, types.EventTypeWorkflowExecutionTerminated, } s.assertHistory(we, expectedHistory) } func (s *IntegrationSuite) assertHistory(we *types.WorkflowExecution, expectedHistory []types.EventType) { ctx, cancel := createContext() defer cancel() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: we, }) s.Nil(err) history := historyResponse.History data, err := json.MarshalIndent(history, "", " ") s.Nil(err) s.Equal(len(expectedHistory), len(history.Events), string(data)) for i, e := range history.Events { s.Equal(expectedHistory[i], e.GetEventType(), "%v, %v, %v", strconv.Itoa(i), e.GetEventType().String(), string(data)) } } func (s *IntegrationSuite) assertLastHistoryEvent(we *types.WorkflowExecution, count int, eventType types.EventType) { ctx, cancel := createContext() defer cancel() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: we, }) s.Nil(err) history := historyResponse.History data, err := json.MarshalIndent(history, "", " ") s.Nil(err) s.Equal(count, len(history.Events), string(data)) s.Equal(eventType, history.Events[len(history.Events)-1].GetEventType(), string(data)) } ================================================ FILE: host/decision_timeout_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package host import ( "flag" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) func TestDecisionTimeoutMaxAttemptsIntegrationSuite(t *testing.T) { flag.Parse() clusterConfig, err := GetTestClusterConfig("testdata/integration_decision_timeout_cluster.yaml") if err != nil { panic(err) } clusterConfig.HistoryDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.DecisionRetryMaxAttempts: 1, dynamicproperties.EnforceDecisionTaskAttempts: true, } testCluster := NewPersistenceTestCluster(t, clusterConfig) s := new(DecisionTimeoutMaxAttemptsIntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } func (s *DecisionTimeoutMaxAttemptsIntegrationSuite) SetupSuite() { s.setupSuite() } func (s *DecisionTimeoutMaxAttemptsIntegrationSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *DecisionTimeoutMaxAttemptsIntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() } func (s *DecisionTimeoutMaxAttemptsIntegrationSuite) TestDecisionTimeoutExceedsMaxAttempts() { id := "integration-decision-timeout-max-attempts-test" wt := "integration-decision-timeout-max-attempts-test-type" tl := "integration-decision-timeout-max-attempts-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{Name: wt} taskList := &types.TaskList{Name: tl} request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), // 1 second timeout Identity: identity, } ctx, cancel := createContext() defer cancel() resp, err := s.Engine.StartWorkflowExecution(ctx, request) s.NoError(err) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(resp.RunID)) poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, Logger: s.Logger, T: s.T(), } // First decision task and drop poller.PollAndProcessDecisionTask(false, true) // Second decision task and drop poller.PollAndProcessDecisionTask(false, true) we := &types.WorkflowExecution{ WorkflowID: id, RunID: resp.RunID, } ctx, cancel = createContext() defer cancel() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: we, HistoryEventFilterType: types.HistoryEventFilterTypeCloseEvent.Ptr(), WaitForNewEvent: true, }) s.NoError(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] s.Equal(types.EventTypeWorkflowExecutionTerminated, lastEvent.GetEventType(), "Expected workflow to be terminated, but last event was %v", lastEvent.GetEventType()) s.NotNil(lastEvent.WorkflowExecutionTerminatedEventAttributes) s.Equal(common.FailureReasonDecisionAttemptsExceedsLimit, lastEvent.WorkflowExecutionTerminatedEventAttributes.Reason) } ================================================ FILE: host/dynamicconfig.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "sync" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) var ( // Override values for dynamic configs staticOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.FrontendUserRPS: 3000, dynamicproperties.FrontendVisibilityListMaxQPS: 200, dynamicproperties.FrontendESIndexMaxResultWindow: defaultTestValueOfESIndexMaxResultWindow, dynamicproperties.MatchingNumTasklistWritePartitions: 3, dynamicproperties.MatchingNumTasklistReadPartitions: 3, dynamicproperties.TimerProcessorHistoryArchivalSizeLimit: 5 * 1024, dynamicproperties.ReplicationTaskProcessorErrorRetryMaxAttempts: 1, dynamicproperties.WriteVisibilityStoreName: constants.AdvancedVisibilityModeOff, dynamicproperties.DecisionHeartbeatTimeout: 5 * time.Second, dynamicproperties.ReplicationTaskFetcherAggregationInterval: 200 * time.Millisecond, dynamicproperties.ReplicationTaskFetcherErrorRetryWait: 50 * time.Millisecond, dynamicproperties.ReplicationTaskProcessorErrorRetryWait: time.Millisecond, dynamicproperties.EnableConsistentQueryByDomain: true, dynamicproperties.MinRetentionDays: 0, dynamicproperties.WorkflowDeletionJitterRange: 1, dynamicproperties.EnableActiveClusterSelectionPolicyInStartWorkflow: true, } ) type dynamicClient struct { sync.RWMutex overrides map[dynamicproperties.Key]interface{} client dynamicconfig.Client } func (d *dynamicClient) GetValue(name dynamicproperties.Key) (interface{}, error) { d.RLock() if val, ok := d.overrides[name]; ok { d.RUnlock() return val, nil } d.RUnlock() return d.client.GetValue(name) } func (d *dynamicClient) GetValueWithFilters(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) (interface{}, error) { d.RLock() if val, ok := d.overrides[name]; ok { d.RUnlock() return val, nil } d.RUnlock() return d.client.GetValueWithFilters(name, filters) } func (d *dynamicClient) GetIntValue(name dynamicproperties.IntKey, filters map[dynamicproperties.Filter]interface{}) (int, error) { d.RLock() if val, ok := d.overrides[name]; ok { if intVal, ok := val.(int); ok { d.RUnlock() return intVal, nil } } d.RUnlock() return d.client.GetIntValue(name, filters) } func (d *dynamicClient) GetFloatValue(name dynamicproperties.FloatKey, filters map[dynamicproperties.Filter]interface{}) (float64, error) { d.RLock() if val, ok := d.overrides[name]; ok { if floatVal, ok := val.(float64); ok { d.RUnlock() return floatVal, nil } } d.RUnlock() return d.client.GetFloatValue(name, filters) } func (d *dynamicClient) GetBoolValue(name dynamicproperties.BoolKey, filters map[dynamicproperties.Filter]interface{}) (bool, error) { d.RLock() if val, ok := d.overrides[name]; ok { if boolVal, ok := val.(bool); ok { d.RUnlock() return boolVal, nil } } d.RUnlock() return d.client.GetBoolValue(name, filters) } func (d *dynamicClient) GetStringValue(name dynamicproperties.StringKey, filters map[dynamicproperties.Filter]interface{}) (string, error) { d.RLock() if val, ok := d.overrides[name]; ok { if stringVal, ok := val.(string); ok { d.RUnlock() return stringVal, nil } } d.RUnlock() return d.client.GetStringValue(name, filters) } func (d *dynamicClient) GetMapValue(name dynamicproperties.MapKey, filters map[dynamicproperties.Filter]interface{}) (map[string]interface{}, error) { d.RLock() if val, ok := d.overrides[name]; ok { if mapVal, ok := val.(map[string]interface{}); ok { d.RUnlock() return mapVal, nil } } d.RUnlock() return d.client.GetMapValue(name, filters) } func (d *dynamicClient) GetDurationValue(name dynamicproperties.DurationKey, filters map[dynamicproperties.Filter]interface{}) (time.Duration, error) { d.RLock() if val, ok := d.overrides[name]; ok { if durationVal, ok := val.(time.Duration); ok { d.RUnlock() return durationVal, nil } } d.RUnlock() return d.client.GetDurationValue(name, filters) } func (d *dynamicClient) GetListValue(name dynamicproperties.ListKey, filters map[dynamicproperties.Filter]interface{}) ([]interface{}, error) { d.RLock() if val, ok := d.overrides[name]; ok { if listVal, ok := val.([]interface{}); ok { d.RUnlock() return listVal, nil } } d.RUnlock() return d.client.GetListValue(name, filters) } func (d *dynamicClient) UpdateValue(name dynamicproperties.Key, value interface{}) error { if name == dynamicproperties.WriteVisibilityStoreName { // override for es integration tests d.Lock() defer d.Unlock() d.overrides[dynamicproperties.WriteVisibilityStoreName] = value.(string) return nil } else if name == dynamicproperties.ReadVisibilityStoreName { // override for pinot integration tests d.Lock() defer d.Unlock() d.overrides[dynamicproperties.ReadVisibilityStoreName] = value.(string) return nil } return d.client.UpdateValue(name, value) } func (d *dynamicClient) OverrideValue(name dynamicproperties.Key, value interface{}) { d.Lock() defer d.Unlock() d.overrides[name] = value } func (d *dynamicClient) ListValue(name dynamicproperties.Key) ([]*types.DynamicConfigEntry, error) { return d.client.ListValue(name) } func (d *dynamicClient) RestoreValue(name dynamicproperties.Key, filters map[dynamicproperties.Filter]interface{}) error { return d.client.RestoreValue(name, filters) } var _ dynamicconfig.Client = (*dynamicClient)(nil) // newIntegrationConfigClient - returns a dynamic config client for integration testing func newIntegrationConfigClient(client dynamicconfig.Client, overrides map[dynamicproperties.Key]interface{}) *dynamicClient { integrationClient := &dynamicClient{ overrides: make(map[dynamicproperties.Key]interface{}), client: client, } for key, value := range staticOverrides { integrationClient.OverrideValue(key, value) } for key, value := range overrides { integrationClient.OverrideValue(key, value) } return integrationClient } ================================================ FILE: host/elastic_search_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // to run locally, make sure kafka and es is running, // then run cmd `go test -v ./host -run TestElasticsearchIntegrationSuite -tags esintegration` package host import ( "bytes" "encoding/json" "flag" "fmt" "strconv" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence/sql/sqlplugin/sqlite" "github.com/uber/cadence/common/types" "github.com/uber/cadence/environment" "github.com/uber/cadence/host/esutils" ) const ( numOfRetry = 50 waitTimeInMs = 400 waitForESToSettle = 4 * time.Second // wait es shards for some time ensure data consistent ) type ElasticSearchIntegrationSuite struct { // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions *IntegrationBase esClient esutils.ESClient testSearchAttributeKey string testSearchAttributeVal string } func TestElasticsearchIntegrationSuite(t *testing.T) { flag.Parse() if TestFlags.SQLPluginName == sqlite.PluginName { t.Skipf("SQLite plugin is not supported for integration test with ES, skipping %v", t.Name()) } clusterConfig, err := GetTestClusterConfig("testdata/integration_elasticsearch_" + environment.GetESVersion() + "_cluster.yaml") if err != nil { panic(err) } testCluster := NewPersistenceTestCluster(t, clusterConfig) s := new(ElasticSearchIntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } // This cluster use customized threshold for history config func (s *ElasticSearchIntegrationSuite) SetupSuite() { s.setupSuite() s.esClient = esutils.CreateESClient(s.Suite.T(), s.TestClusterConfig.ESConfig.URL.String(), environment.GetESVersion()) s.esClient.PutIndexTemplate(s.Suite.T(), "testdata/es_"+environment.GetESVersion()+"_index_template.json", "test-visibility-template") indexName := s.TestClusterConfig.ESConfig.Indices[constants.VisibilityAppName] s.esClient.CreateIndex(s.Suite.T(), indexName) s.putIndexSettings(s.Suite.T(), indexName, defaultTestValueOfESIndexMaxResultWindow) } func (s *ElasticSearchIntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() s.esClient.DeleteIndex(s.Suite.T(), s.TestClusterConfig.ESConfig.Indices[constants.VisibilityAppName]) } func (s *ElasticSearchIntegrationSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) s.testSearchAttributeKey = definition.CustomStringField s.testSearchAttributeVal = "test value" } func (s *ElasticSearchIntegrationSuite) TestListOpenWorkflow() { id := "es-integration-start-workflow-test" wt := "es-integration-start-workflow-test-type" tl := "es-integration-start-workflow-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } request.SearchAttributes = searchAttr startTime := time.Now().UnixNano() ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) startFilter := &types.StartTimeFilter{} startFilter.EarliestTime = common.Int64Ptr(startTime) var openExecution *types.WorkflowExecutionInfo for i := 0; i < numOfRetry; i++ { startFilter.LatestTime = common.Int64Ptr(time.Now().UnixNano()) ctx, cancel := createContext() resp, err := s.Engine.ListOpenWorkflowExecutions(ctx, &types.ListOpenWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: defaultTestValueOfESIndexMaxResultWindow, StartTimeFilter: startFilter, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: id, }, }) s.Nil(err) if len(resp.GetExecutions()) == 1 { openExecution = resp.GetExecutions()[0] break } time.Sleep(waitTimeInMs * time.Millisecond) cancel() } s.NotNil(openExecution) s.Equal(we.GetRunID(), openExecution.GetExecution().GetRunID()) s.Equal(attrValBytes, openExecution.SearchAttributes.GetIndexedFields()[s.testSearchAttributeKey]) } func (s *ElasticSearchIntegrationSuite) TestListWorkflow() { id := "es-integration-list-workflow-test" wt := "es-integration-list-workflow-test-type" tl := "es-integration-list-workflow-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s"`, id) s.testHelperForReadOnce(we.GetRunID(), query, false, false) } func (s *ElasticSearchIntegrationSuite) TestListWorkflowByClusterAttribute() { if TestFlags.SQLPluginName != "" { s.T().Skipf("active-active is not supported for SQL plugin, skipping %v", s.T().Name()) } id := "es-active-active-integration-list-workflow-test" wt := "es-active-active-integration-list-workflow-test-type" tl := "es-active-active-integration-list-workflow-test-tasklist" request := s.createActiveActiveStartWorkflowExecutionRequest(id, wt, tl, &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, }) ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) descRequest := &types.DescribeWorkflowExecutionRequest{ Domain: s.ActiveActiveDomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, }, } ctx, cancel = createContext() defer cancel() descResp, err := s.Engine.DescribeWorkflowExecution(ctx, descRequest) s.Nil(err) s.Equal("region", descResp.WorkflowExecutionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute().GetScope()) s.Equal("us-east", descResp.WorkflowExecutionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute().GetName()) query := `ClusterAttributeScope = "region" and ClusterAttributeName = "us-east"` s.testHelperForReadOnceWithDomain(s.ActiveActiveDomainName, we.GetRunID(), query, false, false) } func (s *ElasticSearchIntegrationSuite) startWorkflow( prefix string, isCron bool, ) *types.StartWorkflowExecutionResponse { id := "es-integration-list-workflow-" + prefix + "-test" wt := "es-integration-list-workflow-" + prefix + "test-type" tl := "es-integration-list-workflow-" + prefix + "test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) if isCron { request.CronSchedule = "*/5 * * * *" // every 5 minutes } ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s"`, id) s.testHelperForReadOnce(we.GetRunID(), query, false, false) return we } func (s *ElasticSearchIntegrationSuite) TestListCronWorkflows() { we1 := s.startWorkflow("cron", true) we2 := s.startWorkflow("regular", false) query := fmt.Sprintf(`IsCron = "true"`) s.testHelperForReadOnce(we1.GetRunID(), query, false, true) query = fmt.Sprintf(`IsCron = "false"`) s.testHelperForReadOnce(we2.GetRunID(), query, false, true) } func (s *ElasticSearchIntegrationSuite) TestIsGlobalSearchAttribute() { we := s.startWorkflow("local", true) // global domains are disabled for this integration test, so we can only test the false case query := fmt.Sprintf(`NumClusters = "1"`) s.testHelperForReadOnce(we.GetRunID(), query, false, true) } func (s *ElasticSearchIntegrationSuite) TestListWorkflow_ExecutionTime() { id := "es-integration-list-workflow-execution-time-test" wt := "es-integration-list-workflow-execution-time-test-type" tl := "es-integration-list-workflow-execution-time-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) cronID := id + "-cron" request.CronSchedule = "@every 1m" request.WorkflowID = cronID ctx, cancel = createContext() defer cancel() weCron, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`(WorkflowID = "%s" or WorkflowID = "%s") and ExecutionTime < %v`, id, cronID, time.Now().UnixNano()+int64(time.Minute)) s.testHelperForReadOnce(weCron.GetRunID(), query, false, false) query = fmt.Sprintf(`WorkflowID = "%s"`, id) s.testHelperForReadOnce(we.GetRunID(), query, false, false) } func (s *ElasticSearchIntegrationSuite) TestListWorkflow_SearchAttribute() { id := "es-integration-list-workflow-by-search-attr-test" wt := "es-integration-list-workflow-by-search-attr-test-type" tl := "es-integration-list-workflow-by-search-attr-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } request.SearchAttributes = searchAttr ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s" and %s = "%s"`, id, s.testSearchAttributeKey, s.testSearchAttributeVal) s.testHelperForReadOnce(we.GetRunID(), query, false, false) // test upsert dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { upsertDecision := &types.Decision{ DecisionType: types.DecisionTypeUpsertWorkflowSearchAttributes.Ptr(), UpsertWorkflowSearchAttributesDecisionAttributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: getUpsertSearchAttributes(), }} return nil, []*types.Decision{upsertDecision}, nil } taskList := &types.TaskList{Name: tl} poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, StickyTaskList: taskList, Identity: "worker1", DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } _, newTask, err := poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( false, false, true, true, int64(0), 1, true, nil) s.Nil(err) s.NotNil(newTask) s.NotNil(newTask.DecisionTask) time.Sleep(waitForESToSettle) listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: int32(2), Query: fmt.Sprintf(`WorkflowType = '%s' and CloseTime = missing and BinaryChecksums = 'binary-v1'`, wt), } // verify upsert data is on ES s.testListResultForUpsertSearchAttributes(listRequest) // verify DescribeWorkflowExecution descRequest := &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, }, } ctx, cancel = createContext() defer cancel() descResp, err := s.Engine.DescribeWorkflowExecution(ctx, descRequest) s.Nil(err) expectedSearchAttributes := getUpsertSearchAttributes() s.Equal(expectedSearchAttributes, descResp.WorkflowExecutionInfo.GetSearchAttributes()) } func (s *ElasticSearchIntegrationSuite) TestListWorkflow_PageToken() { id := "es-integration-list-workflow-token-test" wt := "es-integration-list-workflow-token-test-type" tl := "es-integration-list-workflow-token-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) numOfWorkflows := defaultTestValueOfESIndexMaxResultWindow - 1 // == 4 pageSize := 3 s.testListWorkflowHelper(numOfWorkflows, pageSize, request, id, wt, false) } func (s *ElasticSearchIntegrationSuite) TestListWorkflow_SearchAfter() { id := "es-integration-list-workflow-searchAfter-test" wt := "es-integration-list-workflow-searchAfter-test-type" tl := "es-integration-list-workflow-searchAfter-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) numOfWorkflows := defaultTestValueOfESIndexMaxResultWindow + 1 // == 6 pageSize := 4 s.testListWorkflowHelper(numOfWorkflows, pageSize, request, id, wt, false) } func (s *ElasticSearchIntegrationSuite) TestListWorkflow_OrQuery() { id := "es-integration-list-workflow-or-query-test" wt := "es-integration-list-workflow-or-query-test-type" tl := "es-integration-list-workflow-or-query-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) // start 3 workflows key := definition.CustomIntField attrValBytes, _ := json.Marshal(1) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ key: attrValBytes, }, } request.SearchAttributes = searchAttr ctx, cancel := createContext() defer cancel() we1, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) request.RequestID = uuid.New() request.WorkflowID = id + "-2" attrValBytes, _ = json.Marshal(2) searchAttr.IndexedFields[key] = attrValBytes ctx, cancel = createContext() defer cancel() we2, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) request.RequestID = uuid.New() request.WorkflowID = id + "-3" attrValBytes, _ = json.Marshal(3) searchAttr.IndexedFields[key] = attrValBytes ctx, cancel = createContext() defer cancel() we3, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) time.Sleep(waitForESToSettle) // query 1 workflow with search attr query1 := fmt.Sprintf(`CustomIntField = %d`, 1) var openExecution *types.WorkflowExecutionInfo listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: defaultTestValueOfESIndexMaxResultWindow, Query: query1, } for i := 0; i < numOfRetry; i++ { ctx, cancel := createContext() resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) cancel() s.Nil(err) if len(resp.GetExecutions()) == 1 { openExecution = resp.GetExecutions()[0] break } time.Sleep(waitTimeInMs * time.Millisecond) } s.NotNil(openExecution) s.Equal(we1.GetRunID(), openExecution.GetExecution().GetRunID()) s.True(openExecution.GetExecutionTime() >= openExecution.GetStartTime()) searchValBytes := openExecution.SearchAttributes.GetIndexedFields()[key] var searchVal int json.Unmarshal(searchValBytes, &searchVal) s.Equal(1, searchVal) // query with or clause query2 := fmt.Sprintf(`CustomIntField = %d or CustomIntField = %d`, 1, 2) listRequest.Query = query2 var openExecutions []*types.WorkflowExecutionInfo for i := 0; i < numOfRetry; i++ { ctx, cancel := createContext() resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) cancel() s.Nil(err) if len(resp.GetExecutions()) == 2 { openExecutions = resp.GetExecutions() break } time.Sleep(waitTimeInMs * time.Millisecond) } s.Equal(2, len(openExecutions)) e1 := openExecutions[0] e2 := openExecutions[1] if e1.GetExecution().GetRunID() != we1.GetRunID() { // results are sorted by [CloseTime,RunID] desc, so find the correct mapping first e1, e2 = e2, e1 } s.Equal(we1.GetRunID(), e1.GetExecution().GetRunID()) s.Equal(we2.GetRunID(), e2.GetExecution().GetRunID()) searchValBytes = e2.SearchAttributes.GetIndexedFields()[key] json.Unmarshal(searchValBytes, &searchVal) s.Equal(2, searchVal) // query for open query3 := fmt.Sprintf(`(CustomIntField = %d or CustomIntField = %d) and CloseTime = missing`, 2, 3) listRequest.Query = query3 for i := 0; i < numOfRetry; i++ { ctx, cancel := createContext() resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) cancel() s.Nil(err) if len(resp.GetExecutions()) == 2 { openExecutions = resp.GetExecutions() break } time.Sleep(waitTimeInMs * time.Millisecond) } s.Equal(2, len(openExecutions)) e1 = openExecutions[0] e2 = openExecutions[1] s.Equal(we3.GetRunID(), e1.GetExecution().GetRunID()) s.Equal(we2.GetRunID(), e2.GetExecution().GetRunID()) searchValBytes = e1.SearchAttributes.GetIndexedFields()[key] json.Unmarshal(searchValBytes, &searchVal) s.Equal(3, searchVal) } // To test last page search trigger max window size error func (s *ElasticSearchIntegrationSuite) TestListWorkflow_MaxWindowSize() { id := "es-integration-list-workflow-max-window-size-test" wt := "es-integration-list-workflow-max-window-size-test-type" tl := "es-integration-list-workflow-max-window-size-test-tasklist" startRequest := s.createStartWorkflowExecutionRequest(id, wt, tl) for i := 0; i < defaultTestValueOfESIndexMaxResultWindow; i++ { startRequest.RequestID = uuid.New() startRequest.WorkflowID = id + strconv.Itoa(i) ctx, cancel := createContext() _, err := s.Engine.StartWorkflowExecution(ctx, startRequest) cancel() s.Nil(err) } time.Sleep(waitForESToSettle) var listResp *types.ListWorkflowExecutionsResponse var nextPageToken []byte listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: int32(defaultTestValueOfESIndexMaxResultWindow), NextPageToken: nextPageToken, Query: fmt.Sprintf(`WorkflowType = '%s' and CloseTime = missing`, wt), } // get first page for i := 0; i < numOfRetry; i++ { ctx, cancel := createContext() resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) cancel() s.Nil(err) if len(resp.GetExecutions()) == defaultTestValueOfESIndexMaxResultWindow { listResp = resp break } time.Sleep(waitTimeInMs * time.Millisecond) } s.NotNil(listResp) s.True(len(listResp.GetNextPageToken()) != 0) // the last request listRequest.NextPageToken = listResp.GetNextPageToken() ctx, cancel := createContext() defer cancel() resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) s.Nil(err) s.True(len(resp.GetExecutions()) == 0) s.True(len(resp.GetNextPageToken()) == 0) } func (s *ElasticSearchIntegrationSuite) TestListWorkflow_OrderBy() { id := "es-integration-list-workflow-order-by-test" wt := "es-integration-list-workflow-order-by-test-type" tl := "es-integration-list-workflow-order-by-test-tasklist" startRequest := s.createStartWorkflowExecutionRequest(id, wt, tl) for i := 0; i < defaultTestValueOfESIndexMaxResultWindow+1; i++ { // start 6 startRequest.RequestID = uuid.New() startRequest.WorkflowID = id + strconv.Itoa(i) if i < defaultTestValueOfESIndexMaxResultWindow-1 { // 4 workflow has search attr intVal, _ := json.Marshal(i) doubleVal, _ := json.Marshal(float64(i)) strVal, _ := json.Marshal(strconv.Itoa(i)) timeVal, _ := json.Marshal(time.Now()) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ definition.CustomIntField: intVal, definition.CustomDoubleField: doubleVal, definition.CustomKeywordField: strVal, definition.CustomDatetimeField: timeVal, }, } startRequest.SearchAttributes = searchAttr } else { startRequest.SearchAttributes = &types.SearchAttributes{} } ctx, cancel := createContext() _, err := s.Engine.StartWorkflowExecution(ctx, startRequest) cancel() s.Nil(err) } time.Sleep(waitForESToSettle) desc := "desc" asc := "asc" queryTemplate := `WorkflowType = "%s" order by %s %s` pageSize := int32(defaultTestValueOfESIndexMaxResultWindow) // order by CloseTime asc query1 := fmt.Sprintf(queryTemplate, wt, definition.CloseTime, asc) var openExecutions []*types.WorkflowExecutionInfo listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: pageSize, Query: query1, } for i := 0; i < numOfRetry; i++ { ctx, cancel := createContext() resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) cancel() s.Nil(err) if int32(len(resp.GetExecutions())) == listRequest.GetPageSize() { openExecutions = resp.GetExecutions() break } time.Sleep(waitTimeInMs * time.Millisecond) } s.NotNil(openExecutions) for i := int32(1); i < pageSize; i++ { s.True(openExecutions[i-1].GetCloseTime() <= openExecutions[i].GetCloseTime()) } // greatest effort to reduce duplicate code testHelper := func(query, searchAttrKey string, prevVal, currVal interface{}) { listRequest.Query = query listRequest.NextPageToken = []byte{} ctx, cancel := createContext() defer cancel() resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) s.Nil(err) openExecutions = resp.GetExecutions() dec := json.NewDecoder(bytes.NewReader(openExecutions[0].GetSearchAttributes().GetIndexedFields()[searchAttrKey])) dec.UseNumber() err = dec.Decode(&prevVal) s.Nil(err) for i := int32(1); i < pageSize; i++ { indexedFields := openExecutions[i].GetSearchAttributes().GetIndexedFields() searchAttrBytes, ok := indexedFields[searchAttrKey] if !ok { // last one doesn't have search attr s.Equal(pageSize-1, i) break } dec := json.NewDecoder(bytes.NewReader(searchAttrBytes)) dec.UseNumber() err = dec.Decode(&currVal) s.Nil(err) var v1, v2 interface{} switch searchAttrKey { case definition.CustomIntField: v1, _ = prevVal.(json.Number).Int64() v2, _ = currVal.(json.Number).Int64() s.True(v1.(int64) >= v2.(int64)) case definition.CustomDoubleField: v1, _ = prevVal.(json.Number).Float64() v2, _ = currVal.(json.Number).Float64() s.True(v1.(float64) >= v2.(float64)) case definition.CustomKeywordField: s.True(prevVal.(string) >= currVal.(string)) case definition.CustomDatetimeField: v1, _ = time.Parse(time.RFC3339, prevVal.(string)) v2, _ = time.Parse(time.RFC3339, currVal.(string)) s.True(v1.(time.Time).After(v2.(time.Time))) } prevVal = currVal } listRequest.NextPageToken = resp.GetNextPageToken() ctx, cancel = createContext() defer cancel() resp, err = s.Engine.ListWorkflowExecutions(ctx, listRequest) // last page s.Nil(err) s.Equal(1, len(resp.GetExecutions())) } // order by CustomIntField desc field := definition.CustomIntField query := fmt.Sprintf(queryTemplate, wt, field, desc) var int1, int2 int testHelper(query, field, int1, int2) // order by CustomDoubleField desc field = definition.CustomDoubleField query = fmt.Sprintf(queryTemplate, wt, field, desc) var double1, double2 float64 testHelper(query, field, double1, double2) // order by CustomKeywordField desc field = definition.CustomKeywordField query = fmt.Sprintf(queryTemplate, wt, field, desc) var s1, s2 string testHelper(query, field, s1, s2) // order by CustomDatetimeField desc field = definition.CustomDatetimeField query = fmt.Sprintf(queryTemplate, wt, field, desc) var t1, t2 time.Time testHelper(query, field, t1, t2) } func (s *ElasticSearchIntegrationSuite) testListWorkflowHelper(numOfWorkflows, pageSize int, startRequest *types.StartWorkflowExecutionRequest, wid, wType string, isScan bool) { // start enough number of workflows for i := 0; i < numOfWorkflows; i++ { startRequest.RequestID = uuid.New() startRequest.WorkflowID = wid + strconv.Itoa(i) ctx, cancel := createContext() _, err := s.Engine.StartWorkflowExecution(ctx, startRequest) cancel() s.Nil(err) } time.Sleep(waitForESToSettle) var openExecutions []*types.WorkflowExecutionInfo var nextPageToken []byte listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: int32(pageSize), NextPageToken: nextPageToken, Query: fmt.Sprintf(`WorkflowType = '%s' and CloseTime = missing`, wType), } // test first page for i := 0; i < numOfRetry; i++ { var resp *types.ListWorkflowExecutionsResponse var err error ctx, cancel := createContext() if isScan { resp, err = s.Engine.ScanWorkflowExecutions(ctx, listRequest) } else { resp, err = s.Engine.ListWorkflowExecutions(ctx, listRequest) } cancel() s.Nil(err) if len(resp.GetExecutions()) == pageSize { openExecutions = resp.GetExecutions() nextPageToken = resp.GetNextPageToken() break } time.Sleep(waitTimeInMs * time.Millisecond) } s.NotNil(openExecutions) s.NotNil(nextPageToken) s.True(len(nextPageToken) > 0) // test last page listRequest.NextPageToken = nextPageToken inIf := false for i := 0; i < numOfRetry; i++ { var resp *types.ListWorkflowExecutionsResponse var err error ctx, cancel := createContext() if isScan { resp, err = s.Engine.ScanWorkflowExecutions(ctx, listRequest) } else { resp, err = s.Engine.ListWorkflowExecutions(ctx, listRequest) } cancel() s.Nil(err) if len(resp.GetExecutions()) == numOfWorkflows-pageSize { inIf = true openExecutions = resp.GetExecutions() nextPageToken = resp.GetNextPageToken() break } time.Sleep(waitTimeInMs * time.Millisecond) } s.True(inIf) s.NotNil(openExecutions) s.Nil(nextPageToken) } func (s *ElasticSearchIntegrationSuite) testHelperForReadOnce(runID, query string, isScan bool, isAnyMatchOk bool) { s.testHelperForReadOnceWithDomain(s.DomainName, runID, query, isScan, isAnyMatchOk) } func (s *ElasticSearchIntegrationSuite) testHelperForReadOnceWithDomain(domainName string, runID, query string, isScan bool, isAnyMatchOk bool) { var openExecution *types.WorkflowExecutionInfo listRequest := &types.ListWorkflowExecutionsRequest{ Domain: domainName, PageSize: defaultTestValueOfESIndexMaxResultWindow, Query: query, } Retry: for i := 0; i < numOfRetry; i++ { var resp *types.ListWorkflowExecutionsResponse var err error ctx, cancel := createContext() if isScan { resp, err = s.Engine.ScanWorkflowExecutions(ctx, listRequest) } else { resp, err = s.Engine.ListWorkflowExecutions(ctx, listRequest) } cancel() s.Nil(err) logStr := fmt.Sprintf("Results for query '%s' (desired runId: %s): \n", query, runID) s.Logger.Info(logStr) for _, e := range resp.GetExecutions() { logStr = fmt.Sprintf("Execution: %+v, %+v \n", e.Execution, e) s.Logger.Info(logStr) } if len(resp.GetExecutions()) == 1 { openExecution = resp.GetExecutions()[0] break } if isAnyMatchOk { for _, e := range resp.GetExecutions() { if e.Execution.RunID == runID { openExecution = e break Retry } } } time.Sleep(waitTimeInMs * time.Millisecond) } s.NotNil(openExecution) s.Equal(runID, openExecution.GetExecution().GetRunID()) s.True(openExecution.GetExecutionTime() >= openExecution.GetStartTime()) if openExecution.SearchAttributes != nil && len(openExecution.SearchAttributes.GetIndexedFields()) > 0 { searchValBytes := openExecution.SearchAttributes.GetIndexedFields()[s.testSearchAttributeKey] var searchVal string json.Unmarshal(searchValBytes, &searchVal) s.Equal(s.testSearchAttributeVal, searchVal) } } func (s *ElasticSearchIntegrationSuite) TestScanWorkflow() { id := "es-integration-scan-workflow-test" wt := "es-integration-scan-workflow-test-type" tl := "es-integration-scan-workflow-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s"`, id) s.testHelperForReadOnce(we.GetRunID(), query, true, false) } func (s *ElasticSearchIntegrationSuite) TestScanWorkflow_SearchAttribute() { id := "es-integration-scan-workflow-search-attr-test" wt := "es-integration-scan-workflow-search-attr-test-type" tl := "es-integration-scan-workflow-search-attr-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } request.SearchAttributes = searchAttr ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s" and %s = "%s"`, id, s.testSearchAttributeKey, s.testSearchAttributeVal) s.testHelperForReadOnce(we.GetRunID(), query, true, false) } func (s *ElasticSearchIntegrationSuite) TestScanWorkflow_PageToken() { id := "es-integration-scan-workflow-token-test" wt := "es-integration-scan-workflow-token-test-type" tl := "es-integration-scan-workflow-token-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } numOfWorkflows := 4 pageSize := 3 s.testListWorkflowHelper(numOfWorkflows, pageSize, request, id, wt, true) } func (s *ElasticSearchIntegrationSuite) TestCountWorkflow() { id := "es-integration-count-workflow-test" wt := "es-integration-count-workflow-test-type" tl := "es-integration-count-workflow-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } request.SearchAttributes = searchAttr ctx, cancel := createContext() defer cancel() _, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s" and %s = "%s"`, id, s.testSearchAttributeKey, s.testSearchAttributeVal) countRequest := &types.CountWorkflowExecutionsRequest{ Domain: s.DomainName, Query: query, } var resp *types.CountWorkflowExecutionsResponse for i := 0; i < numOfRetry; i++ { ctx, cancel := createContext() resp, err = s.Engine.CountWorkflowExecutions(ctx, countRequest) cancel() s.Nil(err) if resp.GetCount() == int64(1) { break } time.Sleep(waitTimeInMs * time.Millisecond) } s.Equal(int64(1), resp.GetCount()) query = fmt.Sprintf(`WorkflowID = "%s" and %s = "%s"`, id, s.testSearchAttributeKey, "noMatch") countRequest.Query = query ctx, cancel = createContext() defer cancel() resp, err = s.Engine.CountWorkflowExecutions(ctx, countRequest) s.Nil(err) s.Equal(int64(0), resp.GetCount()) } func (s *ElasticSearchIntegrationSuite) createStartWorkflowExecutionRequest(id, wt, tl string) *types.StartWorkflowExecutionRequest { identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } return request } func (s *ElasticSearchIntegrationSuite) createActiveActiveStartWorkflowExecutionRequest(id, wt, tl string, policy *types.ActiveClusterSelectionPolicy) *types.StartWorkflowExecutionRequest { identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.ActiveActiveDomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, ActiveClusterSelectionPolicy: policy, } return request } func (s *ElasticSearchIntegrationSuite) TestUpsertWorkflowExecution() { id := "es-integration-upsert-workflow-test" wt := "es-integration-upsert-workflow-test-type" tl := "es-integration-upsert-workflow-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) decisionCount := 0 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { upsertDecision := &types.Decision{ DecisionType: types.DecisionTypeUpsertWorkflowSearchAttributes.Ptr(), UpsertWorkflowSearchAttributesDecisionAttributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{}} // handle first upsert if decisionCount == 0 { decisionCount++ attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) upsertSearchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } upsertDecision.UpsertWorkflowSearchAttributesDecisionAttributes.SearchAttributes = upsertSearchAttr return nil, []*types.Decision{upsertDecision}, nil } // handle second upsert, which update existing field and add new field if decisionCount == 1 { decisionCount++ upsertDecision.UpsertWorkflowSearchAttributesDecisionAttributes.SearchAttributes = getUpsertSearchAttributes() return nil, []*types.Decision{upsertDecision}, nil } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, StickyTaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } // process 1st decision and assert decision is handled correctly. _, newTask, err := poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( false, false, true, true, int64(0), 1, true, nil) s.Nil(err) s.NotNil(newTask) s.NotNil(newTask.DecisionTask) s.Equal(int64(3), newTask.DecisionTask.GetPreviousStartedEventID()) s.Equal(int64(7), newTask.DecisionTask.GetStartedEventID()) s.Equal(4, len(newTask.DecisionTask.History.Events)) s.Equal(types.EventTypeDecisionTaskCompleted, newTask.DecisionTask.History.Events[0].GetEventType()) s.Equal(types.EventTypeUpsertWorkflowSearchAttributes, newTask.DecisionTask.History.Events[1].GetEventType()) s.Equal(types.EventTypeDecisionTaskScheduled, newTask.DecisionTask.History.Events[2].GetEventType()) s.Equal(types.EventTypeDecisionTaskStarted, newTask.DecisionTask.History.Events[3].GetEventType()) time.Sleep(waitForESToSettle) // verify upsert data is on ES listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: int32(2), Query: fmt.Sprintf(`WorkflowType = '%s' and CloseTime = missing`, wt), } verified := false for i := 0; i < numOfRetry; i++ { ctx, cancel := createContext() resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) cancel() s.Nil(err) if len(resp.GetExecutions()) == 1 { execution := resp.GetExecutions()[0] retrievedSearchAttr := execution.SearchAttributes if retrievedSearchAttr != nil && len(retrievedSearchAttr.GetIndexedFields()) > 0 { searchValBytes := retrievedSearchAttr.GetIndexedFields()[s.testSearchAttributeKey] var searchVal string json.Unmarshal(searchValBytes, &searchVal) s.Equal(s.testSearchAttributeVal, searchVal) verified = true break } } time.Sleep(waitTimeInMs * time.Millisecond) } s.True(verified) // process 2nd decision and assert decision is handled correctly. _, newTask, err = poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( false, false, true, true, int64(0), 1, true, nil) s.Nil(err) s.NotNil(newTask) s.NotNil(newTask.DecisionTask) s.Equal(4, len(newTask.DecisionTask.History.Events)) s.Equal(types.EventTypeDecisionTaskCompleted, newTask.DecisionTask.History.Events[0].GetEventType()) s.Equal(types.EventTypeUpsertWorkflowSearchAttributes, newTask.DecisionTask.History.Events[1].GetEventType()) s.Equal(types.EventTypeDecisionTaskScheduled, newTask.DecisionTask.History.Events[2].GetEventType()) s.Equal(types.EventTypeDecisionTaskStarted, newTask.DecisionTask.History.Events[3].GetEventType()) time.Sleep(waitForESToSettle) // verify upsert data is on ES s.testListResultForUpsertSearchAttributes(listRequest) } func (s *ElasticSearchIntegrationSuite) testListResultForUpsertSearchAttributes(listRequest *types.ListWorkflowExecutionsRequest) { verified := false for i := 0; i < numOfRetry; i++ { ctx, cancel := createContext() resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) cancel() s.Nil(err) if len(resp.GetExecutions()) == 1 { execution := resp.GetExecutions()[0] retrievedSearchAttr := execution.SearchAttributes if retrievedSearchAttr != nil && len(retrievedSearchAttr.GetIndexedFields()) == 3 { fields := retrievedSearchAttr.GetIndexedFields() searchValBytes := fields[s.testSearchAttributeKey] var searchVal string err := json.Unmarshal(searchValBytes, &searchVal) s.Nil(err) s.Equal("another string", searchVal) searchValBytes2 := fields[definition.CustomIntField] var searchVal2 int err = json.Unmarshal(searchValBytes2, &searchVal2) s.Nil(err) s.Equal(123, searchVal2) binaryChecksumsBytes := fields[definition.BinaryChecksums] var binaryChecksums []string err = json.Unmarshal(binaryChecksumsBytes, &binaryChecksums) s.Nil(err) s.Equal([]string{"binary-v1", "binary-v2"}, binaryChecksums) verified = true break } } time.Sleep(waitTimeInMs * time.Millisecond) } s.True(verified) } func getUpsertSearchAttributes() *types.SearchAttributes { attrValBytes1, _ := json.Marshal("another string") attrValBytes2, _ := json.Marshal(123) binaryChecksums, _ := json.Marshal([]string{"binary-v1", "binary-v2"}) upsertSearchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ definition.CustomStringField: attrValBytes1, definition.CustomIntField: attrValBytes2, definition.BinaryChecksums: binaryChecksums, }, } return upsertSearchAttr } func (s *ElasticSearchIntegrationSuite) TestUpsertWorkflowExecution_InvalidKey() { id := "es-integration-upsert-workflow-failed-test" wt := "es-integration-upsert-workflow-failed-test-type" tl := "es-integration-upsert-workflow-failed-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { upsertDecision := &types.Decision{ DecisionType: types.DecisionTypeUpsertWorkflowSearchAttributes.Ptr(), UpsertWorkflowSearchAttributesDecisionAttributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: &types.SearchAttributes{ IndexedFields: map[string][]byte{ "INVALIDKEY": []byte(`1`), }, }, }} return nil, []*types.Decision{upsertDecision}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, StickyTaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.Nil(err) ctx, cancel = createContext() defer cancel() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) s.Nil(err) history := historyResponse.History decisionFailedEvent := history.GetEvents()[3] s.Equal(types.EventTypeDecisionTaskFailed, decisionFailedEvent.GetEventType()) failedDecisionAttr := decisionFailedEvent.DecisionTaskFailedEventAttributes s.Equal(types.DecisionTaskFailedCauseBadSearchAttributes, failedDecisionAttr.GetCause()) s.True(len(failedDecisionAttr.GetDetails()) > 0) } func (s *ElasticSearchIntegrationSuite) putIndexSettings(t *testing.T, indexName string, maxResultWindowSize int) { err := s.esClient.PutMaxResultWindow(t, indexName, maxResultWindowSize) s.Require().NoError(err) s.verifyMaxResultWindowSize(t, indexName, maxResultWindowSize) } func (s *ElasticSearchIntegrationSuite) verifyMaxResultWindowSize(t *testing.T, indexName string, targetSize int) { for i := 0; i < numOfRetry; i++ { currentWindow, err := s.esClient.GetMaxResultWindow(t, indexName) s.Require().NoError(err) if currentWindow == strconv.Itoa(targetSize) { return } time.Sleep(waitTimeInMs * time.Millisecond) } s.FailNow(fmt.Sprintf("ES max result window size hasn't reach target size within %v.", (numOfRetry*waitTimeInMs)*time.Millisecond)) } ================================================ FILE: host/esutils/client_os2.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esutils import ( "encoding/json" "fmt" "net/http" "os" "strings" "testing" "time" "github.com/opensearch-project/opensearch-go/v4" osapi "github.com/opensearch-project/opensearch-go/v4/opensearchapi" "github.com/stretchr/testify/require" ) type ( os2Client struct { client *osapi.Client } ) func newOS2Client(url string) (*os2Client, error) { osClient, err := osapi.NewClient(osapi.Config{ Client: opensearch.Config{ Addresses: []string{url}, MaxRetries: 5, RetryBackoff: func(i int) time.Duration { return time.Duration(i) * 100 * time.Millisecond }, Username: "admin", Password: "DevTestInitial123!", // Admin password from docker setup. Required for >= OSv2.12 }, }) return &os2Client{ client: osClient, }, err } func (os2 *os2Client) PutIndexTemplate(t *testing.T, templateConfigFile, templateName string) { // This function is used exclusively in tests. Excluding it from security checks. // #nosec template, err := os.Open(templateConfigFile) require.NoError(t, err) req := osapi.IndexTemplateCreateReq{ Body: template, IndexTemplate: templateName, } ctx, cancel := createContext() defer cancel() resp, err := os2.client.IndexTemplate.Create(ctx, req) require.NoError(t, err) require.Truef(t, resp.Acknowledged, "OS2 put index template unacknowledged: %s", resp.Inspect().Response.Body) } func (os2 *os2Client) CreateIndex(t *testing.T, indexName string) { existsReq := osapi.IndicesExistsReq{ Indices: []string{indexName}, } ctx, cancel := createContext() defer cancel() resp, err := os2.client.Indices.Exists(ctx, existsReq) if resp.StatusCode != http.StatusNotFound { require.NoError(t, err) } if resp.StatusCode == http.StatusOK { deleteReq := osapi.IndicesDeleteReq{ Indices: []string{indexName}, } ctx, cancel := createContext() defer cancel() resp, err := os2.client.Indices.Delete(ctx, deleteReq) require.Nil(t, err) require.Truef(t, resp.Acknowledged, "OS2 delete index unacknowledged: %s", resp.Inspect().Response.Body) } resp.Body.Close() createReq := osapi.IndicesCreateReq{ Index: indexName, } ctx, cancel = createContext() defer cancel() createResp, err := os2.client.Indices.Create(ctx, createReq) require.NoError(t, err) require.Truef(t, createResp.Acknowledged, "OS2 create index unacknowledged: %s", createResp.Inspect().Response.Body) } func (os2 *os2Client) DeleteIndex(t *testing.T, indexName string) { deleteReq := osapi.IndicesDeleteReq{ Indices: []string{indexName}, } ctx, cancel := createContext() defer cancel() resp, err := os2.client.Indices.Delete(ctx, deleteReq) require.NoError(t, err) require.True(t, resp.Acknowledged, fmt.Sprintf("OS2 delete index unacknowledged: %s", resp.Inspect().Response.Body)) } func (os2 *os2Client) PutMaxResultWindow(t *testing.T, indexName string, maxResultWindow int) error { req := osapi.SettingsPutReq{ Body: strings.NewReader(fmt.Sprintf(`{"index": {"max_result_window": %d}}`, maxResultWindow)), Indices: []string{indexName}, } ctx, cancel := createContext() defer cancel() resp, err := os2.client.Indices.Settings.Put(ctx, req) require.NoError(t, err) require.Truef(t, resp.Acknowledged, "OS2 put index settings unacknowledged: %s", resp.Inspect().Response.Body) return nil } func (os2 *os2Client) GetMaxResultWindow(t *testing.T, indexName string) (string, error) { req := &osapi.SettingsGetReq{ Indices: []string{indexName}, } ctx, cancel := createContext() defer cancel() res, err := os2.client.Indices.Settings.Get(ctx, req) require.NoError(t, err) indexSettings, ok := res.Indices[indexName] if !ok { return "", fmt.Errorf("no settings for index %q", indexName) } type indexSettingsData struct { Index struct { Window any `json:"max_result_window"` } `json:"index"` } var out indexSettingsData err = json.Unmarshal(indexSettings.Settings, &out) require.NoError(t, err) if out.Index.Window == nil { return "", fmt.Errorf("no max_result_window value found in index settings") } return out.Index.Window.(string), nil } ================================================ FILE: host/esutils/client_v6.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esutils import ( "fmt" "os" "testing" "time" "github.com/olivere/elastic" "github.com/stretchr/testify/require" ) type ( v6Client struct { client *elastic.Client } ) func newV6Client(url string) (*v6Client, error) { esClient, err := elastic.NewClient( elastic.SetURL(url), elastic.SetRetrier(elastic.NewBackoffRetrier(elastic.NewExponentialBackoff(128*time.Millisecond, 513*time.Millisecond))), ) return &v6Client{ client: esClient, }, err } func (es *v6Client) PutIndexTemplate(t *testing.T, templateConfigFile, templateName string) { // This function is used exclusively in tests. Excluding it from security checks. // #nosec template, err := os.ReadFile(templateConfigFile) require.NoError(t, err) ctx, cancel := createContext() defer cancel() putTemplate, err := es.client.IndexPutTemplate(templateName).BodyString(string(template)).Do(ctx) require.NoError(t, err) require.True(t, putTemplate.Acknowledged) } func (es *v6Client) CreateIndex(t *testing.T, indexName string) { ctx, cancel := createContext() defer cancel() exists, err := es.client.IndexExists(indexName).Do(ctx) require.NoError(t, err) if exists { ctx, cancel := createContext() defer cancel() deleteTestIndex, err := es.client.DeleteIndex(indexName).Do(ctx) require.Nil(t, err) require.True(t, deleteTestIndex.Acknowledged) } ctx, cancel = createContext() defer cancel() createTestIndex, err := es.client.CreateIndex(indexName).Do(ctx) require.Nil(t, err) require.True(t, createTestIndex.Acknowledged) } func (es *v6Client) DeleteIndex(t *testing.T, indexName string) { ctx, cancel := createContext() defer cancel() deleteTestIndex, err := es.client.DeleteIndex(indexName).Do(ctx) require.Nil(t, err) require.True(t, deleteTestIndex.Acknowledged) } func (es *v6Client) PutMaxResultWindow(t *testing.T, indexName string, maxResultWindow int) error { ctx, cancel := createContext() defer cancel() _, err := es.client.IndexPutSettings(indexName). BodyString(fmt.Sprintf(`{"max_result_window" : %d}`, maxResultWindow)). Do(ctx) require.NoError(t, err) return err } func (es *v6Client) GetMaxResultWindow(t *testing.T, indexName string) (string, error) { ctx, cancel := createContext() defer cancel() settings, err := es.client.IndexGetSettings(indexName).Do(ctx) require.NoError(t, err) return settings[indexName].Settings["index"].(map[string]interface{})["max_result_window"].(string), nil } ================================================ FILE: host/esutils/client_v7.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esutils import ( "fmt" "os" "testing" "time" "github.com/olivere/elastic/v7" "github.com/stretchr/testify/require" ) type ( v7Client struct { client *elastic.Client } ) func newV7Client(url string) (*v7Client, error) { esClient, err := elastic.NewClient( elastic.SetURL(url), elastic.SetRetrier(elastic.NewBackoffRetrier(elastic.NewExponentialBackoff(128*time.Millisecond, 513*time.Millisecond))), ) return &v7Client{ client: esClient, }, err } func (es *v7Client) PutIndexTemplate(t *testing.T, templateConfigFile, templateName string) { // This function is used exclusively in tests. Excluding it from security checks. // #nosec template, err := os.ReadFile(templateConfigFile) require.NoError(t, err) ctx, cancel := createContext() defer cancel() putTemplate, err := es.client.IndexPutTemplate(templateName).BodyString(string(template)).Do(ctx) require.NoError(t, err) require.True(t, putTemplate.Acknowledged) } func (es *v7Client) CreateIndex(t *testing.T, indexName string) { ctx, cancel := createContext() defer cancel() exists, err := es.client.IndexExists(indexName).Do(ctx) require.NoError(t, err) if exists { ctx, cancel := createContext() defer cancel() deleteTestIndex, err := es.client.DeleteIndex(indexName).Do(ctx) require.Nil(t, err) require.True(t, deleteTestIndex.Acknowledged) } ctx, cancel = createContext() defer cancel() createTestIndex, err := es.client.CreateIndex(indexName).Do(ctx) require.NoError(t, err) require.True(t, createTestIndex.Acknowledged) } func (es *v7Client) DeleteIndex(t *testing.T, indexName string) { ctx, cancel := createContext() defer cancel() deleteTestIndex, err := es.client.DeleteIndex(indexName).Do(ctx) require.Nil(t, err) require.True(t, deleteTestIndex.Acknowledged) } func (es *v7Client) PutMaxResultWindow(t *testing.T, indexName string, maxResultWindow int) error { ctx, cancel := createContext() defer cancel() _, err := es.client.IndexPutSettings(indexName). BodyString(fmt.Sprintf(`{"max_result_window" : %d}`, maxResultWindow)). Do(ctx) require.NoError(t, err) return err } func (es *v7Client) GetMaxResultWindow(t *testing.T, indexName string) (string, error) { ctx, cancel := createContext() defer cancel() settings, err := es.client.IndexGetSettings(indexName).Do(ctx) require.NoError(t, err) return settings[indexName].Settings["index"].(map[string]interface{})["max_result_window"].(string), nil } ================================================ FILE: host/esutils/interfaces.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esutils import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" ) type ( // ESClient is ElasicSearch client for running test suite to be implemented in different versions of ES. // Those interfaces are only being used by tests so we don't implement in common/elasticsearch pkg. ESClient interface { PutIndexTemplate(t *testing.T, templateConfigFile, templateName string) CreateIndex(t *testing.T, indexName string) DeleteIndex(t *testing.T, indexName string) PutMaxResultWindow(t *testing.T, indexName string, maxResultWindow int) error GetMaxResultWindow(t *testing.T, indexName string) (string, error) } ) // CreateESClient create ElasticSearch client for test func CreateESClient(t *testing.T, url string, version string) ESClient { var client ESClient var err error switch version { case "v6": client, err = newV6Client(url) case "v7": client, err = newV7Client(url) case "os2": client, err = newOS2Client(url) default: assert.FailNow(t, fmt.Sprintf("not supported ES version: %s", version)) } assert.NoError(t, err) return client } func createContext() (context.Context, context.CancelFunc) { ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) return ctx, cancel } ================================================ FILE: host/flag.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import "flag" // TestFlags contains the feature flags for integration tests var TestFlags struct { FrontendAddr string PersistenceType string SQLPluginName string TestClusterConfigFile string } func init() { flag.StringVar(&TestFlags.FrontendAddr, "frontendAddress", "", "host:port for cadence frontend service") flag.StringVar(&TestFlags.PersistenceType, "persistenceType", "cassandra", "type of persistence store - [cassandra or sql]") flag.StringVar(&TestFlags.SQLPluginName, "sqlPluginName", "mysql", "type of sql store - [mysql or postgres]") flag.StringVar(&TestFlags.TestClusterConfigFile, "TestClusterConfigFile", "", "test cluster config file location") } ================================================ FILE: host/integration_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "bytes" "encoding/binary" "encoding/json" "errors" "flag" "fmt" "math" "os" "sort" "strconv" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/engine/engineimpl" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/matching/tasklist" ) func TestIntegrationSuite(t *testing.T) { flag.Parse() configPath := "testdata/integration_test_cluster.yaml" // TODO: remove this logic once we deprecate history queue v1 if os.Getenv("ENABLE_QUEUE_V2") == "true" { configPath = "testdata/integration_queuev2_cluster.yaml" if os.Getenv("ENABLE_QUEUE_V2_ALERT") == "true" { configPath = "testdata/integration_queuev2_with_alert_cluster.yaml" } } clusterConfig, err := GetTestClusterConfig(configPath) if err != nil { panic(err) } testCluster := NewPersistenceTestCluster(t, clusterConfig) s := new(IntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } func (s *IntegrationSuite) SetupSuite() { s.setupSuite() } func (s *IntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() } func (s *IntegrationSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } func (s *IntegrationSuite) TestStartWorkflowExecution() { id := "integration-start-workflow-test" wt := "integration-start-workflow-test-type" tl := "integration-start-workflow-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, Header: &types.Header{ Fields: map[string][]byte{ "test-key": []byte("test-value"), "empty-value": {}, }, }, } ctx, cancel := createContext() defer cancel() we0, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) ctx, cancel = createContext() defer cancel() we1, err1 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err1) s.Equal(we0.RunID, we1.RunID) newRequest := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel = createContext() defer cancel() _, err2 := s.Engine.StartWorkflowExecution(ctx, newRequest) s.NotNil(err2) s.IsType(&types.WorkflowExecutionAlreadyStartedError{}, err2) s.T().Logf("Unable to start workflow execution: %v\n", err2.Error()) } func (s *IntegrationSuite) TestStartWorkflowExecution_StartTimestampMatch() { id := "integration-start-workflow-start-timestamp-test" wt := "integration-start-workflow-start-timestamptest-type" tl := "integration-start-workflow-start-timestamp-test-tasklist" identity := "worker1" request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: &types.WorkflowType{Name: wt}, TaskList: &types.TaskList{Name: tl}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we0, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) var historyStartTime time.Time ctx, cancel = createContext() defer cancel() histResp, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we0.GetRunID(), }, }) s.NoError(err) for _, event := range histResp.GetHistory().GetEvents() { if event.GetEventType() == types.EventTypeWorkflowExecutionStarted { historyStartTime = time.Unix(0, event.GetTimestamp()) break } } ctx, cancel = createContext() defer cancel() descResp, err := s.Engine.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we0.GetRunID(), }, }) s.NoError(err) s.WithinDuration( historyStartTime, time.Unix(0, descResp.GetWorkflowExecutionInfo().GetStartTime()), time.Millisecond, ) var listResp *types.ListOpenWorkflowExecutionsResponse for i := 0; i != 20; i++ { ctx, cancel := createContext() listResp, err = s.Engine.ListOpenWorkflowExecutions(ctx, &types.ListOpenWorkflowExecutionsRequest{ Domain: s.DomainName, StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Int64Ptr(historyStartTime.Add(-time.Minute).UnixNano()), LatestTime: common.Int64Ptr(time.Now().UnixNano()), }, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: id, RunID: we0.GetRunID(), }, }) cancel() s.NoError(err) if len(listResp.Executions) == 0 { time.Sleep(100 * time.Millisecond) } } if listResp == nil || len(listResp.Executions) == 0 { s.Fail("unable to get workflow visibility records") } s.WithinDuration( historyStartTime, time.Unix(0, listResp.Executions[0].GetStartTime()), time.Millisecond, ) } func (s *IntegrationSuite) TestStartWorkflowExecution_IDReusePolicy() { id := "integration-start-workflow-id-reuse-test" wt := "integration-start-workflow-id-reuse-type" tl := "integration-start-workflow-id-reuse-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl createStartRequest := func(policy types.WorkflowIDReusePolicy) *types.StartWorkflowExecutionRequest { return &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, WorkflowIDReusePolicy: &policy, } } request := createStartRequest(types.WorkflowIDReusePolicyAllowDuplicateFailedOnly) ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) // Test policies when workflow is running policies := []types.WorkflowIDReusePolicy{ types.WorkflowIDReusePolicyAllowDuplicateFailedOnly, types.WorkflowIDReusePolicyAllowDuplicate, types.WorkflowIDReusePolicyRejectDuplicate, } for _, policy := range policies { newRequest := createStartRequest(policy) ctx, cancel := createContext() _, err1 := s.Engine.StartWorkflowExecution(ctx, newRequest) cancel() s.Error(err1) s.IsType(&types.WorkflowExecutionAlreadyStartedError{}, err1) } // Test TerminateIfRunning policy when workflow is running policy := types.WorkflowIDReusePolicyTerminateIfRunning newRequest := createStartRequest(policy) ctx, cancel = createContext() defer cancel() we1, err1 := s.Engine.StartWorkflowExecution(ctx, newRequest) s.NoError(err1) s.NotEqual(we.GetRunID(), we1.GetRunID()) // verify terminate status executionTerminated := false GetHistoryLoop: for i := 0; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if lastEvent.GetEventType() != types.EventTypeWorkflowExecutionTerminated { s.Logger.Warn("Execution not terminated yet.") time.Sleep(100 * time.Millisecond) continue GetHistoryLoop } terminateEventAttributes := lastEvent.WorkflowExecutionTerminatedEventAttributes s.Equal(engineimpl.TerminateIfRunningReason, terminateEventAttributes.GetReason()) s.Equal(fmt.Sprintf(engineimpl.TerminateIfRunningDetailsTemplate, we1.GetRunID()), string(terminateEventAttributes.Details)) s.Equal(execution.IdentityHistoryService, terminateEventAttributes.GetIdentity()) executionTerminated = true break GetHistoryLoop } s.True(executionTerminated) ctx, cancel = createContext() defer cancel() // Terminate current workflow execution err = s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we1.RunID, }, Reason: "kill workflow", Identity: identity, }) s.Nil(err) // test policy AllowDuplicateFailedOnly policy = types.WorkflowIDReusePolicyAllowDuplicateFailedOnly newRequest = createStartRequest(policy) ctx, cancel = createContext() defer cancel() we2, err2 := s.Engine.StartWorkflowExecution(ctx, newRequest) s.NoError(err2) s.NotEqual(we1.GetRunID(), we2.GetRunID()) // complete workflow instead of terminate dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { return []byte(strconv.Itoa(0)), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) ctx, cancel = createContext() defer cancel() // duplicate requests we3, err3 := s.Engine.StartWorkflowExecution(ctx, newRequest) s.NoError(err3) s.Equal(we2.GetRunID(), we3.GetRunID()) // new request, same policy newRequest = createStartRequest(policy) ctx, cancel = createContext() defer cancel() _, err3 = s.Engine.StartWorkflowExecution(ctx, newRequest) s.Error(err3) s.IsType(&types.WorkflowExecutionAlreadyStartedError{}, err3) // test policy RejectDuplicate policy = types.WorkflowIDReusePolicyRejectDuplicate newRequest = createStartRequest(policy) ctx, cancel = createContext() defer cancel() _, err3 = s.Engine.StartWorkflowExecution(ctx, newRequest) s.Error(err3) s.IsType(&types.WorkflowExecutionAlreadyStartedError{}, err3) // test policy AllowDuplicate policy = types.WorkflowIDReusePolicyAllowDuplicate newRequest = createStartRequest(policy) ctx, cancel = createContext() defer cancel() we4, err4 := s.Engine.StartWorkflowExecution(ctx, newRequest) s.NoError(err4) s.NotEqual(we3.GetRunID(), we4.GetRunID()) // complete workflow _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // test policy TerminateIfRunning policy = types.WorkflowIDReusePolicyTerminateIfRunning newRequest = createStartRequest(policy) ctx, cancel = createContext() defer cancel() we5, err5 := s.Engine.StartWorkflowExecution(ctx, newRequest) s.NoError(err5) s.NotEqual(we4.GetRunID(), we5.GetRunID()) } func (s *IntegrationSuite) TestTerminateWorkflow() { id := "integration-terminate-workflow-test" wt := "integration-terminate-workflow-test-type" tl := "integration-terminate-workflow-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) activityCount := int32(1) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) terminateReason := "terminate reason." terminateDetails := []byte("terminate details.") ctx, cancel = createContext() defer cancel() err = s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, Reason: terminateReason, Details: terminateDetails, Identity: identity, }) s.Nil(err) executionTerminated := false GetHistoryLoop: for i := 0; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if *lastEvent.EventType != types.EventTypeWorkflowExecutionTerminated { s.Logger.Warn("Execution not terminated yet.") time.Sleep(100 * time.Millisecond) continue GetHistoryLoop } terminateEventAttributes := lastEvent.WorkflowExecutionTerminatedEventAttributes s.Equal(terminateReason, terminateEventAttributes.Reason) s.Equal(terminateDetails, terminateEventAttributes.Details) s.Equal(identity, terminateEventAttributes.Identity) executionTerminated = true break GetHistoryLoop } s.True(executionTerminated) newExecutionStarted := false StartNewExecutionLoop: for i := 0; i < 10; i++ { request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() newExecution, err := s.Engine.StartWorkflowExecution(ctx, request) cancel() if err != nil { s.Logger.Warn("Start New Execution failed. Error", tag.Error(err)) time.Sleep(100 * time.Millisecond) continue StartNewExecutionLoop } s.Logger.Info("New Execution Started with the same ID", tag.WorkflowID(id), tag.WorkflowRunID(newExecution.RunID)) newExecutionStarted = true break StartNewExecutionLoop } s.True(newExecutionStarted) } func (s *IntegrationSuite) TestSequentialWorkflow() { RunSequentialWorkflow( s, "integration-sequential-workflow-test", "integration-sequential-workflow-test-type", "integration-sequential-workflow-test-tasklist", 0, ) } func (s *IntegrationSuite) TestDelayStartWorkflow() { startWorkflowTS := time.Now() RunSequentialWorkflow( s, "integration-delay-start-workflow-test", "integration-delay-start-workflow-test-type", "integration-delay-start-workflow-test-tasklist", 10, ) targetBackoffDuration := time.Second * 10 backoffDurationTolerance := time.Millisecond * 4000 backoffDuration := time.Since(startWorkflowTS) s.True( backoffDuration > targetBackoffDuration, "Backoff duration(%f s) should have been at least 5 seconds", time.Duration(backoffDuration).Round(time.Millisecond).Seconds(), ) s.True( backoffDuration < targetBackoffDuration+backoffDurationTolerance, "Integration test too long: %f seconds", time.Duration(backoffDuration).Round(time.Millisecond).Seconds(), ) } func (s *IntegrationSuite) TestSignalDoesNotOverrideDelayStart() { id := "integration-signal-delay-start-test" wt := "integration-signal-delay-start-test-type" tl := "integration-signal-delay-start-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{Name: wt} taskList := &types.TaskList{Name: tl} // Start workflow with a very large delay (300s) so it won't fire during the test request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(600), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, DelayStartSeconds: common.Int32Ptr(300), } ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.NoError(err) s.NotNil(we) // Signal the workflow before the delay expires signalName := "test-signal" signalInput := []byte(`"payload"`) ctx2, cancel2 := createContext() defer cancel2() err = s.Engine.SignalWorkflowExecution(ctx2, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, SignalName: signalName, Input: signalInput, Identity: identity, }) s.NoError(err) // Describe the workflow and verify no decision task is pending ctx3, cancel3 := createContext() defer cancel3() descResp, err := s.Engine.DescribeWorkflowExecution(ctx3, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) s.NoError(err) s.NotNil(descResp) // Workflow should still be open (no close time, no close status) s.Nil(descResp.WorkflowExecutionInfo.CloseStatus) // No decision task should have been scheduled — the signal must not override DelayStart s.Nil(descResp.PendingDecision, "Signal should not schedule a decision task before DelayStart expires") } func RunSequentialWorkflow( s *IntegrationSuite, workflowID string, workflowTypeStr string, taskListStr string, delayStartSeconds int32, ) { identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = workflowTypeStr taskList := &types.TaskList{} taskList.Name = taskListStr request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: workflowID, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, DelayStartSeconds: common.Int32Ptr(delayStartSeconds), } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false activityCount := int32(10) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: taskListStr}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } workflowComplete = true return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } expectedActivity := int32(1) atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Equal(workflowID, execution.WorkflowID) s.Equal(activityName, activityType.Name) id, _ := strconv.Atoi(ActivityID) s.Equal(int(expectedActivity), id) buf := bytes.NewReader(input) var in int32 binary.Read(buf, binary.LittleEndian, &in) s.Equal(expectedActivity, in) expectedActivity++ return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } for i := 0; i < 10; i++ { _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) if i%2 == 0 { err = poller.PollAndProcessActivityTask(false) } else { // just for testing respondActivityTaskCompleteByID err = poller.PollAndProcessActivityTaskWithID(false) } s.Logger.Info("PollAndProcessActivityTask", tag.Error(err)) s.Nil(err) } s.False(workflowComplete) _, err := poller.PollAndProcessDecisionTask(false, false) s.Nil(err) s.True(workflowComplete) } func (s *IntegrationSuite) TestCompleteDecisionTaskAndCreateNewOne() { id := "integration-complete-decision-create-new-test" wt := "integration-complete-decision-create-new-test-type" tl := "integration-complete-decision-create-new-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) decisionCount := 0 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if decisionCount < 2 { decisionCount++ return nil, []*types.Decision{{ DecisionType: types.DecisionTypeRecordMarker.Ptr(), RecordMarkerDecisionAttributes: &types.RecordMarkerDecisionAttributes{ MarkerName: "test-marker", }, }}, nil } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, StickyTaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } _, newTask, err := poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( false, false, true, true, int64(0), 1, true, nil) s.Nil(err) s.NotNil(newTask) s.NotNil(newTask.DecisionTask) s.Equal(int64(3), newTask.DecisionTask.GetPreviousStartedEventID()) s.Equal(int64(7), newTask.DecisionTask.GetStartedEventID()) s.Equal(4, len(newTask.DecisionTask.History.Events)) s.Equal(types.EventTypeDecisionTaskCompleted, newTask.DecisionTask.History.Events[0].GetEventType()) s.Equal(types.EventTypeMarkerRecorded, newTask.DecisionTask.History.Events[1].GetEventType()) s.Equal(types.EventTypeDecisionTaskScheduled, newTask.DecisionTask.History.Events[2].GetEventType()) s.Equal(types.EventTypeDecisionTaskStarted, newTask.DecisionTask.History.Events[3].GetEventType()) } func (s *IntegrationSuite) TestDecisionAndActivityTimeoutsWorkflow() { id := "integration-timeouts-workflow-test" wt := "integration-timeouts-workflow-test-type" tl := "integration-timeouts-workflow-test-tasklist" identity := "worker1" activityName := "activity_timer" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false activityCount := int32(4) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(1), ScheduleToStartTimeoutSeconds: common.Int32Ptr(1), StartToCloseTimeoutSeconds: common.Int32Ptr(1), HeartbeatTimeoutSeconds: common.Int32Ptr(1), }, }}, nil } s.Logger.Info("Completing types.") workflowComplete = true return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Equal(id, execution.WorkflowID) s.Equal(activityName, activityType.Name) s.Logger.Info("Activity ID", tag.WorkflowActivityID(ActivityID)) return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } for i := 0; i < 8; i++ { dropDecisionTask := (i%2 == 0) s.Logger.Info("Calling Decision Task", tag.Counter(i)) var err error if dropDecisionTask { _, err = poller.PollAndProcessDecisionTask(false, true) } else { _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, false, int64(1)) } if err != nil { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History PrettyPrintHistory(history, s.Logger) } s.True(err == nil || err == tasklist.ErrNoTasks, "%v", err) if !dropDecisionTask { s.Logger.Info("Calling Activity Task: %d", tag.Counter(i)) err = poller.PollAndProcessActivityTask(i%4 == 0) s.True(err == nil || err == tasklist.ErrNoTasks) } } s.Logger.Info("Waiting for workflow to complete", tag.WorkflowRunID(we.RunID)) s.False(workflowComplete) _, err := poller.PollAndProcessDecisionTask(false, false) s.Nil(err) s.True(workflowComplete) } func (s *IntegrationSuite) TestWorkflowRetry() { id := "integration-wf-retry-test" wt := "integration-wf-retry-type" tl := "integration-wf-retry-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl initialIntervalInSeconds := 1 backoffCoefficient := 1.5 maximumAttempts := 5 request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: int32(initialIntervalInSeconds), MaximumAttempts: int32(maximumAttempts), MaximumIntervalInSeconds: 1, NonRetriableErrorReasons: []string{"bad-bug"}, BackoffCoefficient: backoffCoefficient, ExpirationIntervalInSeconds: 100, }, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) var executions []*types.WorkflowExecution attemptCount := 0 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { executions = append(executions, execution) attemptCount++ if attemptCount == maximumAttempts { return nil, []*types.Decision{ { DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("succeed-after-retry"), }, }}, nil } return nil, []*types.Decision{ { DecisionType: types.DecisionTypeFailWorkflowExecution.Ptr(), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr("retryable-error"), Details: nil, }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } describeWorkflowExecution := func(execution *types.WorkflowExecution) (*types.DescribeWorkflowExecutionResponse, error) { ctx, cancel := createContext() defer cancel() return s.Engine.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: execution, }) } for i := 0; i != maximumAttempts; i++ { _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) events := s.getHistory(s.DomainName, executions[i]) if i == maximumAttempts-1 { s.Equal(types.EventTypeWorkflowExecutionCompleted, events[len(events)-1].GetEventType()) } else { s.Equal(types.EventTypeWorkflowExecutionContinuedAsNew, events[len(events)-1].GetEventType()) } s.Equal(int32(i), events[0].GetWorkflowExecutionStartedEventAttributes().GetAttempt()) dweResponse, err := describeWorkflowExecution(executions[i]) s.Nil(err) backoff := time.Duration(0) if i > 0 { backoff = time.Duration(float64(initialIntervalInSeconds)*math.Pow(backoffCoefficient, float64(i-1))) * time.Second // retry backoff cannot larger than MaximumIntervalInSeconds if backoff > time.Second { backoff = time.Second } } expectedExecutionTime := dweResponse.WorkflowExecutionInfo.GetStartTime() + backoff.Nanoseconds() s.Equal(expectedExecutionTime, dweResponse.WorkflowExecutionInfo.GetExecutionTime()) } } func (s *IntegrationSuite) TestWorkflowRetryFailures() { id := "integration-wf-retry-failures-test" wt := "integration-wf-retry-failures-type" tl := "integration-wf-retry-failures-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl workflowImpl := func(attempts int, errorReason string, executions *[]*types.WorkflowExecution) decisionTaskHandler { attemptCount := 0 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { *executions = append(*executions, execution) attemptCount++ if attemptCount == attempts { return nil, []*types.Decision{ { DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("succeed-after-retry"), }, }}, nil } return nil, []*types.Decision{ { DecisionType: types.DecisionTypeFailWorkflowExecution.Ptr(), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ // Reason: common.StringPtr("retryable-error"), Reason: common.StringPtr(errorReason), Details: nil, }, }}, nil } return dtHandler } // Fail using attempt request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 3, MaximumIntervalInSeconds: 1, NonRetriableErrorReasons: []string{"bad-bug"}, BackoffCoefficient: 1, ExpirationIntervalInSeconds: 100, }, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) executions := []*types.WorkflowExecution{} dtHandler := workflowImpl(5, "retryable-error", &executions) poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) events := s.getHistory(s.DomainName, executions[0]) s.Equal(types.EventTypeWorkflowExecutionContinuedAsNew, events[len(events)-1].GetEventType()) s.Equal(int32(0), events[0].GetWorkflowExecutionStartedEventAttributes().GetAttempt()) _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) events = s.getHistory(s.DomainName, executions[1]) s.Equal(types.EventTypeWorkflowExecutionContinuedAsNew, events[len(events)-1].GetEventType()) s.Equal(int32(1), events[0].GetWorkflowExecutionStartedEventAttributes().GetAttempt()) _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) events = s.getHistory(s.DomainName, executions[2]) s.Equal(types.EventTypeWorkflowExecutionFailed, events[len(events)-1].GetEventType()) s.Equal(int32(2), events[0].GetWorkflowExecutionStartedEventAttributes().GetAttempt()) // Fail error reason request = &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 3, MaximumIntervalInSeconds: 1, NonRetriableErrorReasons: []string{"bad-bug"}, BackoffCoefficient: 1, ExpirationIntervalInSeconds: 100, }, } ctx, cancel = createContext() defer cancel() we, err0 = s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) executions = []*types.WorkflowExecution{} dtHandler = workflowImpl(5, "bad-bug", &executions) poller = &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) events = s.getHistory(s.DomainName, executions[0]) s.Equal(types.EventTypeWorkflowExecutionFailed, events[len(events)-1].GetEventType()) s.Equal(int32(0), events[0].GetWorkflowExecutionStartedEventAttributes().GetAttempt()) } func (s *IntegrationSuite) TestCronWorkflow() { id := "integration-wf-cron-test" wt := "integration-wf-cron-type" tl := "integration-wf-cron-tasklist" identity := "worker1" cronSchedule := "@every 3s" targetBackoffDuration := time.Second * 3 backoffDurationTolerance := time.Millisecond * 500 workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl memo := &types.Memo{ Fields: map[string][]byte{"memoKey": []byte("memoVal")}, } searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{"CustomKeywordField": []byte(`"1"`)}, } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, CronSchedule: cronSchedule, // minimum interval by standard spec is 1m (* * * * *), use non-standard descriptor for short interval for test Memo: memo, SearchAttributes: searchAttr, } startWorkflowTS := time.Now() ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) var executions []*types.WorkflowExecution attemptCount := 0 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { executions = append(executions, execution) attemptCount++ if attemptCount == 2 { return nil, []*types.Decision{ { DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("cron-test-result"), }, }}, nil } return nil, []*types.Decision{ { DecisionType: types.DecisionTypeFailWorkflowExecution.Ptr(), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr("cron-test-error"), Details: nil, }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } startFilter := &types.StartTimeFilter{} startFilter.EarliestTime = common.Int64Ptr(startWorkflowTS.UnixNano()) startFilter.LatestTime = common.Int64Ptr(time.Now().UnixNano()) // Sleep some time before checking the open executions. // This will not cost extra time as the polling for first decision task will be blocked for 3 seconds. time.Sleep(2 * time.Second) ctx, cancel = createContext() defer cancel() resp, err := s.Engine.ListOpenWorkflowExecutions(ctx, &types.ListOpenWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: startFilter, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: id, }, }) s.Nil(err) s.Equal(1, len(resp.GetExecutions())) executionInfo := resp.GetExecutions()[0] s.Equal(targetBackoffDuration.Nanoseconds(), executionInfo.GetExecutionTime()-executionInfo.GetStartTime()) _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) // Make sure the cron workflow start running at a proper time, in this case 3 seconds after the // startWorkflowExecution request backoffDuration := time.Since(startWorkflowTS) s.True(backoffDuration > targetBackoffDuration) s.True(backoffDuration < targetBackoffDuration+backoffDurationTolerance) _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) s.Equal(3, attemptCount) ctx, cancel = createContext() defer cancel() terminateErr := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, }, }) s.NoError(terminateErr) events := s.getHistory(s.DomainName, executions[0]) lastEvent := events[len(events)-1] s.Equal(types.EventTypeWorkflowExecutionContinuedAsNew, lastEvent.GetEventType()) attributes := lastEvent.WorkflowExecutionContinuedAsNewEventAttributes s.Equal(types.ContinueAsNewInitiatorCronSchedule, attributes.GetInitiator()) s.Equal("cron-test-error", attributes.GetFailureReason()) s.Equal(0, len(attributes.GetLastCompletionResult())) s.Equal(memo, attributes.Memo) s.Equal(searchAttr, attributes.SearchAttributes) events = s.getHistory(s.DomainName, executions[1]) lastEvent = events[len(events)-1] s.Equal(types.EventTypeWorkflowExecutionContinuedAsNew, lastEvent.GetEventType()) attributes = lastEvent.WorkflowExecutionContinuedAsNewEventAttributes s.Equal(types.ContinueAsNewInitiatorCronSchedule, attributes.GetInitiator()) s.Equal("", attributes.GetFailureReason()) s.Equal("cron-test-result", string(attributes.GetLastCompletionResult())) s.Equal(memo, attributes.Memo) s.Equal(searchAttr, attributes.SearchAttributes) events = s.getHistory(s.DomainName, executions[2]) lastEvent = events[len(events)-1] s.Equal(types.EventTypeWorkflowExecutionContinuedAsNew, lastEvent.GetEventType()) attributes = lastEvent.WorkflowExecutionContinuedAsNewEventAttributes s.Equal(types.ContinueAsNewInitiatorCronSchedule, attributes.GetInitiator()) s.Equal("cron-test-error", attributes.GetFailureReason()) s.Equal("cron-test-result", string(attributes.GetLastCompletionResult())) s.Equal(memo, attributes.Memo) s.Equal(searchAttr, attributes.SearchAttributes) startFilter.LatestTime = common.Int64Ptr(time.Now().UnixNano()) var closedExecutions []*types.WorkflowExecutionInfo for i := 0; i < 10; i++ { ctx, cancel := createContext() resp, err := s.Engine.ListClosedWorkflowExecutions(ctx, &types.ListClosedWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: startFilter, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: id, }, }) cancel() s.Nil(err) if len(resp.GetExecutions()) == 4 { closedExecutions = resp.GetExecutions() break } time.Sleep(200 * time.Millisecond) } s.NotNil(closedExecutions) ctx, cancel = createContext() defer cancel() dweResponse, err := s.Engine.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) s.Nil(err) expectedExecutionTime := dweResponse.WorkflowExecutionInfo.GetStartTime() + 3*time.Second.Nanoseconds() s.Equal(expectedExecutionTime, dweResponse.WorkflowExecutionInfo.GetExecutionTime()) sort.Slice(closedExecutions, func(i, j int) bool { return closedExecutions[i].GetStartTime() < closedExecutions[j].GetStartTime() }) lastExecution := closedExecutions[0] for i := 1; i != 4; i++ { executionInfo := closedExecutions[i] expectedBackoff := executionInfo.GetExecutionTime() - lastExecution.GetExecutionTime() // The execution time calculate based on last execution close time // However, the current execution time is based on the current start time // This code is to remove the diff between current start time and last execution close time // TODO: Remove this line once we unify the time source executionTimeDiff := executionInfo.GetStartTime() - lastExecution.GetCloseTime() // The backoff between any two executions should be multiplier of the target backoff duration which is 3 in this test // However, it's difficult to guarantee accuracy within a second, we allows 1s as buffering... backoffSeconds := int(time.Duration(expectedBackoff - executionTimeDiff).Round(time.Second).Seconds()) targetBackoffSeconds := int(targetBackoffDuration.Seconds()) targetDiff := int(math.Abs(float64(backoffSeconds%targetBackoffSeconds-3))) % 3 s.True( targetDiff <= 1, "Still Flaky?:((%v-%v) - (%v-%v)), backoffSeconds: %v, targetBackoffSeconds: %v, targetDiff:%v", backoffSeconds, executionInfo.GetExecutionTime(), lastExecution.GetExecutionTime(), executionInfo.GetStartTime(), lastExecution.GetCloseTime(), targetBackoffSeconds, targetDiff, ) lastExecution = executionInfo } } func (s *IntegrationSuite) TestCronWorkflowTimeout() { id := "integration-wf-cron-timeout-test" wt := "integration-wf-cron-timeout-type" tl := "integration-wf-cron-timeout-tasklist" identity := "worker1" cronSchedule := "@every 3s" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl memo := &types.Memo{ Fields: map[string][]byte{"memoKey": []byte("memoVal")}, } searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{"CustomKeywordField": []byte(`"1"`)}, } retryPolicy := &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2.0, MaximumIntervalInSeconds: 1, ExpirationIntervalInSeconds: 1, MaximumAttempts: 0, NonRetriableErrorReasons: []string{"bad-error"}, } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), // set workflow timeout to 1s TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, CronSchedule: cronSchedule, // minimum interval by standard spec is 1m (* * * * *), use non-standard descriptor for short interval for test Memo: memo, SearchAttributes: searchAttr, RetryPolicy: retryPolicy, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) var executions []*types.WorkflowExecution dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { executions = append(executions, execution) return nil, []*types.Decision{ { DecisionType: types.DecisionTypeStartTimer.Ptr(), StartTimerDecisionAttributes: &types.StartTimerDecisionAttributes{ TimerID: "timer-id", StartToFireTimeoutSeconds: common.Int64Ptr(5), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) time.Sleep(1 * time.Second) // wait for workflow timeout // check when workflow timeout, continueAsNew event contains expected fields events := s.getHistory(s.DomainName, executions[0]) lastEvent := events[len(events)-1] s.Equal(types.EventTypeWorkflowExecutionContinuedAsNew, lastEvent.GetEventType()) attributes := lastEvent.WorkflowExecutionContinuedAsNewEventAttributes s.Equal(types.ContinueAsNewInitiatorCronSchedule, attributes.GetInitiator()) s.Equal("cadenceInternal:Timeout START_TO_CLOSE", attributes.GetFailureReason()) s.Equal(memo, attributes.Memo) s.Equal(searchAttr, attributes.SearchAttributes) firstStartWorkflowEvent := events[0] s.NotNil(firstStartWorkflowEvent.WorkflowExecutionStartedEventAttributes.ExpirationTimestamp) _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) // check new run contains expected fields events = s.getHistory(s.DomainName, executions[1]) firstEvent := events[0] s.Equal(types.EventTypeWorkflowExecutionStarted, firstEvent.GetEventType()) startAttributes := firstEvent.WorkflowExecutionStartedEventAttributes s.Equal(memo, startAttributes.Memo) s.Equal(searchAttr, startAttributes.SearchAttributes) s.NotNil(firstEvent.WorkflowExecutionStartedEventAttributes.ExpirationTimestamp) // test that the new workflow has a different expiration timestamp from the first workflow s.True(*firstEvent.WorkflowExecutionStartedEventAttributes.ExpirationTimestamp > *firstStartWorkflowEvent.WorkflowExecutionStartedEventAttributes.ExpirationTimestamp) // terminate cron ctx, cancel = createContext() defer cancel() terminateErr := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, }, }) s.NoError(terminateErr) } func (s *IntegrationSuite) TestSequential_UserTimers() { id := "integration-sequential-user-timers-test" wt := "integration-sequential-user-timers-test-type" tl := "integration-sequential-user-timers-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false timerCount := int32(4) timerCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if timerCounter < timerCount { timerCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, timerCounter)) return []byte(strconv.Itoa(int(timerCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeStartTimer.Ptr(), StartTimerDecisionAttributes: &types.StartTimerDecisionAttributes{ TimerID: fmt.Sprintf("timer-id-%d", timerCounter), StartToFireTimeoutSeconds: common.Int64Ptr(1), }, }}, nil } workflowComplete = true return []byte(strconv.Itoa(int(timerCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: nil, Logger: s.Logger, T: s.T(), } for i := 0; i < 4; i++ { _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask: completed") s.Nil(err) } s.False(workflowComplete) _, err := poller.PollAndProcessDecisionTask(false, false) s.Nil(err) s.True(workflowComplete) } func (s *IntegrationSuite) TestRateLimitBufferedEvents() { id := "integration-rate-limit-buffered-events-test" wt := "integration-rate-limit-buffered-events-test-type" tl := "integration-rate-limit-buffered-events-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, } // decider logic workflowComplete := false signalsSent := false signalCount := 0 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, h *types.History) ([]byte, []*types.Decision, error) { // Count signals for _, event := range h.Events[previousStartedEventID:] { if event.GetEventType() == types.EventTypeWorkflowExecutionSignaled { signalCount++ } } if !signalsSent { signalsSent = true // Buffered Signals for i := 0; i < 100; i++ { buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, int64(i)) s.Nil(s.sendSignal(s.DomainName, workflowExecution, "SignalName", buf.Bytes(), identity)) } buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, int64(101)) signalErr := s.sendSignal(s.DomainName, workflowExecution, "SignalName", buf.Bytes(), identity) s.Nil(signalErr) // this decision will be ignored as he decision task is already failed return nil, []*types.Decision{}, nil } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: nil, Logger: s.Logger, T: s.T(), } // first decision to send 101 signals, the last signal will force fail decision and flush buffered events. _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.EqualError(err, "Decision task not found.") // Process signal in decider _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(workflowComplete) s.Equal(101, signalCount) // check that all 101 signals are received. } func (s *IntegrationSuite) TestBufferedEvents() { id := "integration-buffered-events-test" wt := "integration-buffered-events-test-type" tl := "integration-buffered-events-test-tasklist" identity := "worker1" signalName := "buffered-signal" workflowType := &types.WorkflowType{Name: wt} taskList := &types.TaskList{Name: tl} // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic workflowComplete := false signalSent := false var signalEvent *types.HistoryEvent dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !signalSent { signalSent = true ctx, cancel := createContext() defer cancel() // this will create new event when there is in-flight decision task, and the new event will be buffered err := s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, }, SignalName: "buffered-signal", Input: []byte("buffered-signal-input"), Identity: identity, }) s.NoError(err) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: "test-activity-type"}, TaskList: &types.TaskList{Name: tl}, Input: []byte("test-input"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if previousStartedEventID > 0 && signalEvent == nil { for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { signalEvent = event } } } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: nil, Logger: s.Logger, T: s.T(), } // first decision, which sends signal and the signal event should be buffered to append after first decision closed _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) ctx, cancel = createContext() defer cancel() // check history, the signal event should be after the complete decision task histResp, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) s.NoError(err) s.NotNil(histResp.History.Events) s.True(len(histResp.History.Events) >= 6) s.Equal(histResp.History.Events[3].GetEventType(), types.EventTypeDecisionTaskCompleted) s.Equal(histResp.History.Events[4].GetEventType(), types.EventTypeActivityTaskScheduled) s.Equal(histResp.History.Events[5].GetEventType(), types.EventTypeWorkflowExecutionSignaled) // Process signal in decider _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.NotNil(signalEvent) s.Equal(signalName, signalEvent.WorkflowExecutionSignaledEventAttributes.SignalName) s.Equal(identity, signalEvent.WorkflowExecutionSignaledEventAttributes.Identity) s.True(workflowComplete) } func (s *IntegrationSuite) TestDescribeWorkflowExecution() { id := "integration-describe-wfe-test" wt := "integration-describe-wfe-test-type" tl := "integration-describe-wfe-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{Name: wt} taskList := &types.TaskList{Name: tl} childID := id + "-child" childType := wt + "-child" childTaskList := tl + "-child" // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, CronOverlapPolicy: types.CronOverlapPolicySkipped.Ptr(), } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) describeWorkflowExecution := func() (*types.DescribeWorkflowExecutionResponse, error) { ctx, cancel := createContext() defer cancel() return s.Engine.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) } dweResponse, err := describeWorkflowExecution() s.Nil(err) s.True(nil == dweResponse.WorkflowExecutionInfo.CloseTime) s.Equal(int64(2), dweResponse.WorkflowExecutionInfo.HistoryLength) // WorkflowStarted, DecisionScheduled s.Equal(dweResponse.WorkflowExecutionInfo.GetStartTime(), dweResponse.WorkflowExecutionInfo.GetExecutionTime()) // decider logic workflowComplete := false signalSent := false dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !signalSent { signalSent = true s.NoError(err) return nil, []*types.Decision{ { DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: "test-activity-type"}, TaskList: &types.TaskList{Name: tl}, Input: []byte("test-input"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }, { DecisionType: types.DecisionTypeStartChildWorkflowExecution.Ptr(), StartChildWorkflowExecutionDecisionAttributes: &types.StartChildWorkflowExecutionDecisionAttributes{ WorkflowID: childID, WorkflowType: &types.WorkflowType{Name: childType}, TaskList: &types.TaskList{Name: childTaskList}, Input: []byte("child-workflow-input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(200), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Control: nil, CronOverlapPolicy: types.CronOverlapPolicySkipped.Ptr(), }, }, }, nil } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // first decision to schedule new activity _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // wait for child workflow to start for i := 0; i != 10; i++ { dweResponse, err = describeWorkflowExecution() s.Nil(err) if len(dweResponse.PendingChildren) == 1 && dweResponse.PendingChildren[0].GetRunID() != "" { break } time.Sleep(100 * time.Millisecond) } s.NotEmpty(dweResponse.PendingChildren[0].GetRunID(), "unable to start child workflow") s.True(nil == dweResponse.WorkflowExecutionInfo.CloseStatus) // DecisionStarted, DecisionCompleted, ActivityScheduled, ChildWorkflowInit, ChildWorkflowStarted, DecisionTaskScheduled s.Equal(int64(8), dweResponse.WorkflowExecutionInfo.HistoryLength) s.Equal(1, len(dweResponse.PendingActivities)) s.Equal("test-activity-type", dweResponse.PendingActivities[0].ActivityType.GetName()) s.Equal(int64(0), dweResponse.PendingActivities[0].GetLastHeartbeatTimestamp()) s.Equal(1, len(dweResponse.PendingChildren)) s.Equal(s.DomainName, dweResponse.PendingChildren[0].GetDomain()) s.Equal(childID, dweResponse.PendingChildren[0].GetWorkflowID()) s.Equal(childType, dweResponse.PendingChildren[0].GetWorkflowTypeName()) s.Equal(types.CronOverlapPolicySkipped, *dweResponse.WorkflowExecutionInfo.CronOverlapPolicy) s.Equal(false, dweResponse.WorkflowExecutionInfo.IsCron) // process activity task err = poller.PollAndProcessActivityTask(false) dweResponse, err = describeWorkflowExecution() s.Nil(err) s.True(nil == dweResponse.WorkflowExecutionInfo.CloseStatus) s.Equal(int64(10), dweResponse.WorkflowExecutionInfo.HistoryLength) // ActivityTaskStarted, ActivityTaskCompleted s.Equal(0, len(dweResponse.PendingActivities)) // Process signal in decider _, err = poller.PollAndProcessDecisionTask(false, false) s.Nil(err) s.True(workflowComplete) dweResponse, err = describeWorkflowExecution() s.Nil(err) s.Equal(types.WorkflowExecutionCloseStatusCompleted, *dweResponse.WorkflowExecutionInfo.CloseStatus) s.Equal(int64(13), dweResponse.WorkflowExecutionInfo.HistoryLength) // DecisionStarted, DecisionCompleted, WorkflowCompleted } func (s *IntegrationSuite) TestVisibility() { startTime := time.Now().UnixNano() // Start 2 workflow executions id1 := "integration-visibility-test1" id2 := "integration-visibility-test2" wt := "integration-visibility-test-type" tl := "integration-visibility-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl startRequest := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id1, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() startResponse, err0 := s.Engine.StartWorkflowExecution(ctx, startRequest) s.Nil(err0) // Now complete one of the executions dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { return []byte{}, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: nil, Logger: s.Logger, T: s.T(), } _, err1 := poller.PollAndProcessDecisionTask(false, false) s.Nil(err1) // wait until the start workflow is done var nextToken []byte historyEventFilterType := types.HistoryEventFilterTypeCloseEvent for { ctx, cancel := createContext() historyResponse, historyErr := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: startRequest.Domain, Execution: &types.WorkflowExecution{ WorkflowID: startRequest.WorkflowID, RunID: startResponse.RunID, }, WaitForNewEvent: true, HistoryEventFilterType: &historyEventFilterType, NextPageToken: nextToken, }) cancel() s.Nil(historyErr) if len(historyResponse.NextPageToken) == 0 { break } nextToken = historyResponse.NextPageToken } startRequest = &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id2, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel = createContext() defer cancel() _, err2 := s.Engine.StartWorkflowExecution(ctx, startRequest) s.Nil(err2) startFilter := &types.StartTimeFilter{} startFilter.EarliestTime = common.Int64Ptr(startTime) startFilter.LatestTime = common.Int64Ptr(time.Now().UnixNano()) closedCount := 0 openCount := 0 var historyLength int64 for i := 0; i < 10; i++ { ctx, cancel := createContext() resp, err3 := s.Engine.ListClosedWorkflowExecutions(ctx, &types.ListClosedWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: startFilter, }) cancel() s.Nil(err3) closedCount = len(resp.Executions) if closedCount == 1 { historyLength = resp.Executions[0].HistoryLength break } s.Logger.Info("Closed WorkflowExecution is not yet visible") time.Sleep(100 * time.Millisecond) } s.Equal(1, closedCount) s.Equal(int64(5), historyLength) for i := 0; i < 10; i++ { ctx, cancel := createContext() resp, err4 := s.Engine.ListOpenWorkflowExecutions(ctx, &types.ListOpenWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: startFilter, }) cancel() s.Nil(err4) openCount = len(resp.Executions) if openCount == 1 { break } s.Logger.Info("Open WorkflowExecution is not yet visible") time.Sleep(100 * time.Millisecond) } s.Equal(1, openCount) } func (s *IntegrationSuite) TestChildWorkflowExecution() { parentID := "integration-child-workflow-test-parent" childID := "integration-child-workflow-test-child" wtParent := "integration-child-workflow-test-parent-type" wtChild := "integration-child-workflow-test-child-type" tlParent := "integration-child-workflow-test-parent-tasklist" tlChild := "integration-child-workflow-test-child-tasklist" identity := "worker1" parentWorkflowType := &types.WorkflowType{} parentWorkflowType.Name = wtParent childWorkflowType := &types.WorkflowType{} childWorkflowType.Name = wtChild taskListParent := &types.TaskList{} taskListParent.Name = tlParent taskListChild := &types.TaskList{} taskListChild.Name = tlChild header := &types.Header{ Fields: map[string][]byte{"tracing": []byte("sample payload")}, } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: parentID, WorkflowType: parentWorkflowType, TaskList: taskListParent, Input: nil, Header: header, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic childComplete := false childExecutionStarted := false var startedEvent *types.HistoryEvent var completedEvent *types.HistoryEvent memoInfo, _ := json.Marshal("memo") memo := &types.Memo{ Fields: map[string][]byte{ "Info": memoInfo, }, } attrValBytes, _ := json.Marshal("attrVal") searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ "CustomKeywordField": attrValBytes, }, } // Parent Decider Logic dtHandlerParent := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { s.Logger.Info("Processing decision task for ", tag.WorkflowID(execution.WorkflowID)) if execution.WorkflowID == parentID { if !childExecutionStarted { s.Logger.Info("Starting child execution.") childExecutionStarted = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeStartChildWorkflowExecution.Ptr(), StartChildWorkflowExecutionDecisionAttributes: &types.StartChildWorkflowExecutionDecisionAttributes{ WorkflowID: childID, WorkflowType: childWorkflowType, TaskList: taskListChild, Input: []byte("child-workflow-input"), Header: header, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(200), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Control: nil, Memo: memo, SearchAttributes: searchAttr, }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeChildWorkflowExecutionStarted { startedEvent = event return nil, []*types.Decision{}, nil } if *event.EventType == types.EventTypeChildWorkflowExecutionCompleted { completedEvent = event return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } } } } return nil, nil, nil } var childStartedEvent *types.HistoryEvent // Child Decider Logic dtHandlerChild := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if previousStartedEventID <= 0 { childStartedEvent = history.Events[0] } s.Logger.Info("Processing decision task for Child ", tag.WorkflowID(execution.WorkflowID)) childComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Child Done."), }, }}, nil } pollerParent := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskListParent, Identity: identity, DecisionHandler: dtHandlerParent, Logger: s.Logger, T: s.T(), } pollerChild := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskListChild, Identity: identity, DecisionHandler: dtHandlerChild, Logger: s.Logger, T: s.T(), } // Make first decision to start child execution _, err := pollerParent.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(childExecutionStarted) // Process ChildExecution Started event and Process Child Execution and complete it _, err = pollerParent.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) _, err = pollerChild.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.NotNil(startedEvent) s.True(childComplete) s.NotNil(childStartedEvent) s.Equal(types.EventTypeWorkflowExecutionStarted, childStartedEvent.GetEventType()) s.Equal(s.DomainName, childStartedEvent.WorkflowExecutionStartedEventAttributes.GetParentWorkflowDomain()) s.Equal(parentID, childStartedEvent.WorkflowExecutionStartedEventAttributes.ParentWorkflowExecution.GetWorkflowID()) s.Equal(we.GetRunID(), childStartedEvent.WorkflowExecutionStartedEventAttributes.ParentWorkflowExecution.GetRunID()) s.Equal(startedEvent.ChildWorkflowExecutionStartedEventAttributes.GetInitiatedEventID(), childStartedEvent.WorkflowExecutionStartedEventAttributes.GetParentInitiatedEventID()) s.Equal(header, startedEvent.ChildWorkflowExecutionStartedEventAttributes.Header) s.Equal(header, childStartedEvent.WorkflowExecutionStartedEventAttributes.Header) s.Equal(memo, childStartedEvent.WorkflowExecutionStartedEventAttributes.GetMemo()) s.Equal(searchAttr, childStartedEvent.WorkflowExecutionStartedEventAttributes.GetSearchAttributes()) // Process ChildExecution completed event and complete parent execution _, err = pollerParent.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.NotNil(completedEvent) completedAttributes := completedEvent.ChildWorkflowExecutionCompletedEventAttributes s.Equal(s.DomainName, completedAttributes.Domain) s.Equal(childID, completedAttributes.WorkflowExecution.WorkflowID) s.Equal(wtChild, completedAttributes.WorkflowType.Name) s.Equal([]byte("Child Done."), completedAttributes.Result) } func (s *IntegrationSuite) TestCronChildWorkflowExecution() { parentID := "integration-cron-child-workflow-test-parent" childID := "integration-cron-child-workflow-test-child" wtParent := "integration-cron-child-workflow-test-parent-type" wtChild := "integration-cron-child-workflow-test-child-type" tlParent := "integration-cron-child-workflow-test-parent-tasklist" tlChild := "integration-cron-child-workflow-test-child-tasklist" identity := "worker1" cronSchedule := "@every 3s" targetBackoffDuration := time.Second * 3 backoffDurationTolerance := time.Second parentWorkflowType := &types.WorkflowType{} parentWorkflowType.Name = wtParent childWorkflowType := &types.WorkflowType{} childWorkflowType.Name = wtChild taskListParent := &types.TaskList{} taskListParent.Name = tlParent taskListChild := &types.TaskList{} taskListChild.Name = tlChild request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: parentID, WorkflowType: parentWorkflowType, TaskList: taskListParent, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } startParentWorkflowTS := time.Now() ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic childExecutionStarted := false var terminatedEvent *types.HistoryEvent var startChildWorkflowTS time.Time // Parent Decider Logic dtHandlerParent := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { s.Logger.Info("Processing decision task for ", tag.WorkflowID(execution.WorkflowID)) if !childExecutionStarted { s.Logger.Info("Starting child execution.") childExecutionStarted = true startChildWorkflowTS = time.Now() return nil, []*types.Decision{{ DecisionType: types.DecisionTypeStartChildWorkflowExecution.Ptr(), StartChildWorkflowExecutionDecisionAttributes: &types.StartChildWorkflowExecutionDecisionAttributes{ WorkflowID: childID, WorkflowType: childWorkflowType, TaskList: taskListChild, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(200), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Control: nil, CronSchedule: cronSchedule, }, }}, nil } for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeChildWorkflowExecutionTerminated { terminatedEvent = event return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } } return nil, nil, nil } // Child Decider Logic dtHandlerChild := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { s.Logger.Info("Processing decision task for Child ", tag.WorkflowID(execution.WorkflowID)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{}, }}, nil } pollerParent := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskListParent, Identity: identity, DecisionHandler: dtHandlerParent, Logger: s.Logger, T: s.T(), } pollerChild := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskListChild, Identity: identity, DecisionHandler: dtHandlerChild, Logger: s.Logger, T: s.T(), } // Make first decision to start child execution _, err := pollerParent.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(childExecutionStarted) // Process ChildExecution Started event _, err = pollerParent.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) startFilter := &types.StartTimeFilter{} startFilter.EarliestTime = common.Int64Ptr(startChildWorkflowTS.UnixNano()) for i := 0; i < 2; i++ { // Sleep some time before checking the open executions. // This will not cost extra time as the polling for first decision task will be blocked for 3 seconds. time.Sleep(2 * time.Second) startFilter.LatestTime = common.Int64Ptr(time.Now().UnixNano()) ctx, cancel := createContext() resp, err := s.Engine.ListOpenWorkflowExecutions(ctx, &types.ListOpenWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: startFilter, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: childID, }, }) cancel() s.Nil(err) s.Equal(1, len(resp.GetExecutions())) _, err = pollerChild.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) backoffDuration := time.Since(startChildWorkflowTS) s.True(backoffDuration < targetBackoffDuration+backoffDurationTolerance) startChildWorkflowTS = time.Now() } ctx, cancel = createContext() defer cancel() // terminate the childworkflow terminateErr := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childID, }, }) s.Nil(terminateErr) // Process ChildExecution terminated event and complete parent execution _, err = pollerParent.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.NotNil(terminatedEvent) terminatedAttributes := terminatedEvent.ChildWorkflowExecutionTerminatedEventAttributes s.Equal(s.DomainName, terminatedAttributes.Domain) s.Equal(childID, terminatedAttributes.WorkflowExecution.WorkflowID) s.Equal(wtChild, terminatedAttributes.WorkflowType.Name) startFilter.EarliestTime = common.Int64Ptr(startParentWorkflowTS.UnixNano()) startFilter.LatestTime = common.Int64Ptr(time.Now().UnixNano()) var closedExecutions []*types.WorkflowExecutionInfo for i := 0; i < 10; i++ { ctx, cancel := createContext() resp, err := s.Engine.ListClosedWorkflowExecutions(ctx, &types.ListClosedWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: startFilter, }) cancel() s.Nil(err) if len(resp.GetExecutions()) == 4 { closedExecutions = resp.GetExecutions() break } time.Sleep(200 * time.Millisecond) } s.NotNil(closedExecutions) sort.Slice(closedExecutions, func(i, j int) bool { return closedExecutions[i].GetStartTime() < closedExecutions[j].GetStartTime() }) // The first parent is not the cron workflow, only verify child workflow with cron schedule lastExecution := closedExecutions[1] for i := 2; i != 4; i++ { executionInfo := closedExecutions[i] // Round up the time precision to seconds expectedBackoff := executionInfo.GetExecutionTime()/1000000000 - lastExecution.GetExecutionTime()/1000000000 // The execution time calculate based on last execution close time // However, the current execution time is based on the current start time // This code is to remove the diff between current start time and last execution close time // TODO: Remove this line once we unify the time source. executionTimeDiff := executionInfo.GetStartTime()/1000000000 - lastExecution.GetCloseTime()/1000000000 // The backoff between any two executions should be multiplier of the target backoff duration which is 3 in this test s.Equal(int64(0), int64(expectedBackoff-executionTimeDiff)/1000000000%(targetBackoffDuration.Nanoseconds()/1000000000)) lastExecution = executionInfo } } func (s *IntegrationSuite) TestWorkflowTimeout() { startTime := time.Now().UnixNano() id := "integration-workflow-timeout" wt := "integration-workflow-timeout-type" tl := "integration-workflow-timeout-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false GetHistoryLoop: for i := 0; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if *lastEvent.EventType != types.EventTypeWorkflowExecutionTimedOut { s.Logger.Warn("Execution not timedout yet.") time.Sleep(200 * time.Millisecond) continue GetHistoryLoop } timeoutEventAttributes := lastEvent.WorkflowExecutionTimedOutEventAttributes s.Equal(types.TimeoutTypeStartToClose, *timeoutEventAttributes.TimeoutType) workflowComplete = true break GetHistoryLoop } s.True(workflowComplete) startFilter := &types.StartTimeFilter{} startFilter.EarliestTime = common.Int64Ptr(startTime) startFilter.LatestTime = common.Int64Ptr(time.Now().UnixNano()) closedCount := 0 ListClosedLoop: for i := 0; i < 10; i++ { ctx, cancel := createContext() resp, err3 := s.Engine.ListClosedWorkflowExecutions(ctx, &types.ListClosedWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: startFilter, }) cancel() s.Nil(err3) closedCount = len(resp.Executions) if closedCount == 0 { s.Logger.Info("Closed WorkflowExecution is not yet visibile") time.Sleep(1000 * time.Millisecond) continue ListClosedLoop } break ListClosedLoop } s.Equal(1, closedCount) } func (s *IntegrationSuite) TestDecisionTaskFailed() { id := "integration-decisiontask-failed-test" wt := "integration-decisiontask-failed-test-type" tl := "integration-decisiontask-failed-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, } // decider logic workflowComplete := false activityScheduled := false activityData := int32(1) failureCount := 10 signalCount := 0 sendSignal := false lastDecisionTimestamp := int64(0) // var signalEvent *types.HistoryEvent dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { // Count signals for _, event := range history.Events[previousStartedEventID:] { if event.GetEventType() == types.EventTypeWorkflowExecutionSignaled { signalCount++ } } // Some signals received on this decision if signalCount == 1 { return nil, []*types.Decision{}, nil } // Send signals during decision if sendSignal { s.sendSignal(s.DomainName, workflowExecution, "signalC", nil, identity) s.sendSignal(s.DomainName, workflowExecution, "signalD", nil, identity) s.sendSignal(s.DomainName, workflowExecution, "signalE", nil, identity) sendSignal = false } if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if failureCount > 0 { // Otherwise decrement failureCount and keep failing decisions failureCount-- return nil, nil, errors.New("Decider Panic") } workflowComplete = true time.Sleep(time.Second) s.Logger.Warn(fmt.Sprintf("PrevStarted: %v, StartedEventID: %v, Size: %v", previousStartedEventID, startedEventID, len(history.Events))) lastDecisionEvent := history.Events[startedEventID-1] s.Equal(types.EventTypeDecisionTaskStarted, lastDecisionEvent.GetEventType()) lastDecisionTimestamp = lastDecisionEvent.GetTimestamp() return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Make first decision to schedule activity _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // process activity err = poller.PollAndProcessActivityTask(false) s.Logger.Info("PollAndProcessActivityTask", tag.Error(err)) s.Nil(err) // fail decision 5 times for i := 0; i < 5; i++ { _, err := poller.PollAndProcessDecisionTaskWithAttempt(false, false, false, false, int64(i)) s.Nil(err) } err = s.sendSignal(s.DomainName, workflowExecution, "signalA", nil, identity) s.Nil(err, "failed to send signal to execution") // process signal _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.Equal(1, signalCount) // send another signal to trigger decision err = s.sendSignal(s.DomainName, workflowExecution, "signalB", nil, identity) s.Nil(err, "failed to send signal to execution") // fail decision 2 more times for i := 0; i < 2; i++ { _, err := poller.PollAndProcessDecisionTaskWithAttempt(false, false, false, false, int64(i)) s.Nil(err) } s.Equal(3, signalCount) // now send a signal during failed decision sendSignal = true _, err = poller.PollAndProcessDecisionTaskWithAttempt(false, false, false, false, int64(2)) s.Nil(err) s.Equal(4, signalCount) // fail decision 1 more times for i := 0; i < 2; i++ { _, err := poller.PollAndProcessDecisionTaskWithAttempt(false, false, false, false, int64(i)) s.Nil(err) } s.Equal(12, signalCount) // Make complete workflow decision _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, false, int64(2)) s.Logger.Info("pollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(workflowComplete) s.Equal(16, signalCount) events := s.getHistory(s.DomainName, workflowExecution) var lastEvent *types.HistoryEvent var lastDecisionStartedEvent *types.HistoryEvent lastIdx := 0 for i, e := range events { if e.GetEventType() == types.EventTypeDecisionTaskStarted { lastDecisionStartedEvent = e lastIdx = i } lastEvent = e } s.Equal(types.EventTypeWorkflowExecutionCompleted, lastEvent.GetEventType()) s.Logger.Info(fmt.Sprintf("Last Decision Time: %v, Last Decision History Timestamp: %v, Complete Timestamp: %v", time.Unix(0, lastDecisionTimestamp), time.Unix(0, lastDecisionStartedEvent.GetTimestamp()), time.Unix(0, lastEvent.GetTimestamp()))) s.Equal(lastDecisionTimestamp, lastDecisionStartedEvent.GetTimestamp()) s.True(time.Duration(lastEvent.GetTimestamp()-lastDecisionTimestamp) >= time.Second) s.Equal(2, len(events)-lastIdx-1) decisionCompletedEvent := events[lastIdx+1] workflowCompletedEvent := events[lastIdx+2] s.Equal(types.EventTypeDecisionTaskCompleted, decisionCompletedEvent.GetEventType()) s.Equal(types.EventTypeWorkflowExecutionCompleted, workflowCompletedEvent.GetEventType()) } func (s *IntegrationSuite) TestGetPollerHistory() { WorkflowID := "integration-get-poller-history" workflowTypeName := "integration-get-poller-history-type" tasklistName := "integration-get-poller-history-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = workflowTypeName taskList := &types.TaskList{} taskList.Name = tasklistName // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: WorkflowID, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic activityScheduled := false activityData := int32(1) // var signalEvent *types.HistoryEvent dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: taskList, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(25), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(25), }, }}, nil } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // this function poll events from history side testDescribeTaskList := func(domain string, tasklist *types.TaskList, tasklistType types.TaskListType) []*types.PollerInfo { ctx, cancel := createContext() defer cancel() listResp, err := s.Engine.ListTaskListPartitions(ctx, &types.ListTaskListPartitionsRequest{ Domain: domain, TaskList: tasklist, }) s.NoError(err) pollers := make(map[string]*types.PollerInfo) var partitions []*types.TaskListPartitionMetadata if tasklistType == types.TaskListTypeActivity { partitions = listResp.ActivityTaskListPartitions } else { partitions = listResp.DecisionTaskListPartitions } for _, partition := range partitions { responseInner, errInner := s.Engine.DescribeTaskList(ctx, &types.DescribeTaskListRequest{ Domain: domain, TaskList: &types.TaskList{Name: partition.Key, Kind: tasklist.Kind}, TaskListType: &tasklistType, }) s.Nil(errInner) for _, poller := range responseInner.Pollers { pollers[poller.GetIdentity()] = poller } } var results []*types.PollerInfo for _, poller := range pollers { results = append(results, poller) } return results } before := time.Now() // when no one polling on the tasklist (activity or decition), there shall be no poller information pollerInfos := testDescribeTaskList(s.DomainName, taskList, types.TaskListTypeActivity) s.Empty(pollerInfos) pollerInfos = testDescribeTaskList(s.DomainName, taskList, types.TaskListTypeDecision) s.Empty(pollerInfos) _, errDecision := poller.PollAndProcessDecisionTask(false, false) s.Nil(errDecision) pollerInfos = testDescribeTaskList(s.DomainName, taskList, types.TaskListTypeActivity) s.Empty(pollerInfos) pollerInfos = testDescribeTaskList(s.DomainName, taskList, types.TaskListTypeDecision) s.Equal(1, len(pollerInfos)) s.Equal(identity, pollerInfos[0].GetIdentity()) s.True(time.Unix(0, pollerInfos[0].GetLastAccessTime()).After(before)) s.NotEmpty(pollerInfos[0].GetLastAccessTime()) errActivity := poller.PollAndProcessActivityTask(false) s.Nil(errActivity) pollerInfos = testDescribeTaskList(s.DomainName, taskList, types.TaskListTypeActivity) s.Equal(1, len(pollerInfos)) s.Equal(identity, pollerInfos[0].GetIdentity()) s.True(time.Unix(0, pollerInfos[0].GetLastAccessTime()).After(before)) s.NotEmpty(pollerInfos[0].GetLastAccessTime()) pollerInfos = testDescribeTaskList(s.DomainName, taskList, types.TaskListTypeDecision) s.Equal(1, len(pollerInfos)) s.Equal(identity, pollerInfos[0].GetIdentity()) s.True(time.Unix(0, pollerInfos[0].GetLastAccessTime()).After(before)) s.NotEmpty(pollerInfos[0].GetLastAccessTime()) } func (s *IntegrationSuite) TestTransientDecisionTimeout() { id := "integration-transient-decision-timeout-test" wt := "integration-transient-decision-timeout-test-type" tl := "integration-transient-decision-timeout-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, } // decider logic workflowComplete := false failDecision := true signalCount := 0 // var signalEvent *types.HistoryEvent dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if failDecision { failDecision = false return nil, nil, errors.New("Decider Panic") } // Count signals for _, event := range history.Events[previousStartedEventID:] { if event.GetEventType() == types.EventTypeWorkflowExecutionSignaled { signalCount++ } } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: nil, Logger: s.Logger, T: s.T(), } // First decision immediately fails and schedules a transient decision _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // Now send a signal when transient decision is scheduled err = s.sendSignal(s.DomainName, workflowExecution, "signalA", nil, identity) s.Nil(err, "failed to send signal to execution") // Drop decision task to cause a Decision Timeout _, err = poller.PollAndProcessDecisionTask(false, true) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // Now process signal and complete workflow execution _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, false, int64(1)) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.Equal(1, signalCount) s.True(workflowComplete) } func (s *IntegrationSuite) TestNoTransientDecisionAfterFlushBufferedEvents() { id := "integration-no-transient-decision-after-flush-buffered-events-test" wt := "integration-no-transient-decision-after-flush-buffered-events-test-type" tl := "integration-no-transient-decision-after-flush-buffered-events-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{Name: wt} taskList := &types.TaskList{Name: tl} // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(20), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic workflowComplete := false continueAsNewAndSignal := false dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !continueAsNewAndSignal { continueAsNewAndSignal = true ctx, cancel := createContext() defer cancel() // this will create new event when there is in-flight decision task, and the new event will be buffered err := s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, }, SignalName: "buffered-signal-1", Input: []byte("buffered-signal-input"), Identity: identity, }) s.NoError(err) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeContinueAsNewWorkflowExecution.Ptr(), ContinueAsNewWorkflowExecutionDecisionAttributes: &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1000), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(100), }, }}, nil } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } // fist decision, this try to do a continue as new but there is a buffered event, // so it will fail and create a new decision _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("pollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // second decision, which will complete the workflow // this expect the decision to have attempt == 0 _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, false, 0) s.Logger.Info("pollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(workflowComplete) } func (s *IntegrationSuite) TestRelayDecisionTimeout() { id := "integration-relay-decision-timeout-test" wt := "integration-relay-decision-timeout-test-type" tl := "integration-relay-decision-timeout-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, } workflowComplete, isFirst := false, true dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if isFirst { isFirst = false return nil, []*types.Decision{{ DecisionType: types.DecisionTypeRecordMarker.Ptr(), RecordMarkerDecisionAttributes: &types.RecordMarkerDecisionAttributes{ MarkerName: "test-marker", }, }}, nil } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{}, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: nil, Logger: s.Logger, T: s.T(), } // First decision task complete with a marker decision, and request to relay decision (immediately return a new decision task) _, newTask, err := poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( false, false, false, false, 0, 3, true, nil) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.NotNil(newTask) s.NotNil(newTask.DecisionTask) time.Sleep(time.Second * 2) // wait 2s for relay decision to timeout decisionTaskTimeout := false for i := 0; i < 3; i++ { events := s.getHistory(s.DomainName, workflowExecution) if len(events) >= 8 { s.Equal(types.EventTypeDecisionTaskTimedOut, events[7].GetEventType()) s.Equal(types.TimeoutTypeStartToClose, events[7].DecisionTaskTimedOutEventAttributes.GetTimeoutType()) decisionTaskTimeout = true break } time.Sleep(time.Second) } // verify relay decision task timeout s.True(decisionTaskTimeout) // Now complete workflow _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, false, int64(1)) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(workflowComplete) } func (s *IntegrationSuite) TestTaskProcessingProtectionForRateLimitError() { id := "integration-task-processing-protection-for-rate-limit-error-test" wt := "integration-task-processing-protection-for-rate-limit-error-test-type" tl := "integration-task-processing-protection-for-rate-limit-error-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(601), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(600), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, } // decider logic workflowComplete := false signalCount := 0 createUserTimer := false dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, h *types.History) ([]byte, []*types.Decision, error) { if !createUserTimer { createUserTimer = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeStartTimer.Ptr(), StartTimerDecisionAttributes: &types.StartTimerDecisionAttributes{ TimerID: "timer-id-1", StartToFireTimeoutSeconds: common.Int64Ptr(5), }, }}, nil } // Count signals for _, event := range h.Events[previousStartedEventID:] { if event.GetEventType() == types.EventTypeWorkflowExecutionSignaled { signalCount++ } } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: nil, Logger: s.Logger, T: s.T(), } // Process first decision to create user timer _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // Send one signal to create a new decision buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, int64(0)) s.Nil(s.sendSignal(s.DomainName, workflowExecution, "SignalName", buf.Bytes(), identity)) // Drop decision to cause all events to be buffered from now on _, err = poller.PollAndProcessDecisionTask(false, true) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // Buffered 100 Signals for i := 1; i < 101; i++ { buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, int64(i)) s.Nil(s.sendSignal(s.DomainName, workflowExecution, "SignalName", buf.Bytes(), identity)) } // 101 signal, which will fail the decision buf = new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, int64(101)) signalErr := s.sendSignal(s.DomainName, workflowExecution, "SignalName", buf.Bytes(), identity) s.Nil(signalErr) // Process signal in decider _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, false, 0) s.Logger.Info("pollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(workflowComplete) s.Equal(102, signalCount) } func (s *IntegrationSuite) TestStickyTimeout_NonTransientDecision() { id := "integration-sticky-timeout-non-transient-decision" wt := "integration-sticky-timeout-non-transient-decision-type" tl := "integration-sticky-timeout-non-transient-decision-tasklist" stl := "integration-sticky-timeout-non-transient-decision-tasklist-sticky" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl stickyTaskList := &types.TaskList{} stickyTaskList.Name = stl stickyScheduleToStartTimeoutSeconds := common.Int32Ptr(2) // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, } // decider logic localActivityDone := false failureCount := 5 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !localActivityDone { localActivityDone = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeRecordMarker.Ptr(), RecordMarkerDecisionAttributes: &types.RecordMarkerDecisionAttributes{ MarkerName: "local activity marker", Details: []byte("local activity data"), }, }}, nil } if failureCount > 0 { // send a signal on third failure to be buffered, forcing a non-transient decision when buffer is flushed /*if failureCount == 3 { err := s.Engine.SignalWorkflowExecution(createContext(), &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: workflowExecution, SignalName: common.StringPtr("signalB"), Input: []byte("signal input"), Identity: identity, RequestID: uuid.New(), }) s.Nil(err) }*/ failureCount-- return nil, nil, errors.New("non deterministic error") } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), StickyTaskList: stickyTaskList, StickyScheduleToStartTimeoutSeconds: stickyScheduleToStartTimeoutSeconds, } _, err := poller.PollAndProcessDecisionTaskWithAttempt(false, false, false, true, int64(0)) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) ctx, cancel = createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: workflowExecution, SignalName: "signalA", Input: []byte("signal input"), Identity: identity, RequestID: uuid.New(), }) s.NoError(err) // Wait for decision timeout stickyTimeout := false WaitForStickyTimeoutLoop: for i := 0; i < 10; i++ { events := s.getHistory(s.DomainName, workflowExecution) for _, event := range events { if event.GetEventType() == types.EventTypeDecisionTaskTimedOut { s.Equal(types.TimeoutTypeScheduleToStart, event.DecisionTaskTimedOutEventAttributes.GetTimeoutType()) stickyTimeout = true break WaitForStickyTimeoutLoop } } time.Sleep(time.Second) } s.True(stickyTimeout, "Decision not timed out.") for i := 0; i < 3; i++ { _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, true, int64(i)) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) } ctx, cancel = createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: workflowExecution, SignalName: "signalB", Input: []byte("signal input"), Identity: identity, RequestID: uuid.New(), }) s.Nil(err) for i := 0; i < 2; i++ { _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, true, int64(i)) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) } decisionTaskFailed := false events := s.getHistory(s.DomainName, workflowExecution) for _, event := range events { if event.GetEventType() == types.EventTypeDecisionTaskFailed { decisionTaskFailed = true break } } s.True(decisionTaskFailed) // Complete workflow execution _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, true, int64(2)) s.NoError(err) // Assert for single decision task failed and workflow completion failedDecisions := 0 workflowComplete := false events = s.getHistory(s.DomainName, workflowExecution) for _, event := range events { switch event.GetEventType() { case types.EventTypeDecisionTaskFailed: failedDecisions++ case types.EventTypeWorkflowExecutionCompleted: workflowComplete = true } } s.True(workflowComplete, "Workflow not complete") s.Equal(2, failedDecisions, "Mismatched failed decision count") } func (s *IntegrationSuite) TestStickyTasklistResetThenTimeout() { id := "integration-reset-sticky-fire-schedule-to-start-timeout" wt := "integration-reset-sticky-fire-schedule-to-start-timeout-type" tl := "integration-reset-sticky-fire-schedule-to-start-timeout-tasklist" stl := "integration-reset-sticky-fire-schedule-to-start-timeout-tasklist-sticky" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl stickyTaskList := &types.TaskList{} stickyTaskList.Name = stl stickyScheduleToStartTimeoutSeconds := common.Int32Ptr(2) // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, } // decider logic localActivityDone := false failureCount := 5 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !localActivityDone { localActivityDone = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeRecordMarker.Ptr(), RecordMarkerDecisionAttributes: &types.RecordMarkerDecisionAttributes{ MarkerName: "local activity marker", Details: []byte("local activity data"), }, }}, nil } if failureCount > 0 { failureCount-- return nil, nil, errors.New("non deterministic error") } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), StickyTaskList: stickyTaskList, StickyScheduleToStartTimeoutSeconds: stickyScheduleToStartTimeoutSeconds, } _, err := poller.PollAndProcessDecisionTaskWithAttempt(false, false, false, true, int64(0)) s.Logger.Info("PollAndProcessDecisionTask: %v", tag.Error(err)) s.Nil(err) ctx, cancel = createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: workflowExecution, SignalName: "signalA", Input: []byte("signal input"), Identity: identity, RequestID: uuid.New(), }) s.NoError(err) ctx, cancel = createContext() defer cancel() // Reset sticky tasklist before sticky decision task starts s.Engine.ResetStickyTaskList(ctx, &types.ResetStickyTaskListRequest{ Domain: s.DomainName, Execution: workflowExecution, }) // Wait for decision timeout stickyTimeout := false WaitForStickyTimeoutLoop: for i := 0; i < 10; i++ { events := s.getHistory(s.DomainName, workflowExecution) for _, event := range events { if event.GetEventType() == types.EventTypeDecisionTaskTimedOut { s.Equal(types.TimeoutTypeScheduleToStart, event.DecisionTaskTimedOutEventAttributes.GetTimeoutType()) stickyTimeout = true break WaitForStickyTimeoutLoop } } time.Sleep(time.Second) } s.True(stickyTimeout, "Decision not timed out.") for i := 0; i < 3; i++ { // we will jump from sticky decision to transient decision in this case (decisionAttempt increased) // even though the error is sticky scheduleToStart timeout // since we can't tell if the decision is a sticky decision or not when the timer fires // (stickiness cleared by the ResetStickyTaskList API call above) _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, true, int64(i+1)) s.Logger.Info("PollAndProcessDecisionTask: %v", tag.Error(err)) s.Nil(err) } ctx, cancel = createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: workflowExecution, SignalName: "signalB", Input: []byte("signal input"), Identity: identity, RequestID: uuid.New(), }) s.Nil(err) for i := 0; i < 2; i++ { _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, true, int64(i)) s.Logger.Info("PollAndProcessDecisionTask: %v", tag.Error(err)) s.Nil(err) } decisionTaskFailed := false events := s.getHistory(s.DomainName, workflowExecution) for _, event := range events { if event.GetEventType() == types.EventTypeDecisionTaskFailed { decisionTaskFailed = true break } } s.True(decisionTaskFailed) // Complete workflow execution _, err = poller.PollAndProcessDecisionTaskWithAttempt(true, false, false, true, int64(2)) s.NoError(err) // Assert for single decision task failed and workflow completion failedDecisions := 0 workflowComplete := false events = s.getHistory(s.DomainName, workflowExecution) for _, event := range events { switch event.GetEventType() { case types.EventTypeDecisionTaskFailed: failedDecisions++ case types.EventTypeWorkflowExecutionCompleted: workflowComplete = true } } s.True(workflowComplete, "Workflow not complete") s.Equal(1, failedDecisions, "Mismatched failed decision count") } func (s *IntegrationSuite) TestBufferedEventsOutOfOrder() { id := "integration-buffered-events-out-of-order-test" wt := "integration-buffered-events-out-of-order-test-type" tl := "integration-buffered-events-out-of-order-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{Name: wt} taskList := &types.TaskList{Name: tl} // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(20), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, } // decider logic workflowComplete := false firstDecision := false secondDecision := false dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { s.Logger.Info(fmt.Sprintf("Decider called: first: %v, second: %v, complete: %v\n", firstDecision, secondDecision, workflowComplete)) if !firstDecision { firstDecision = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeRecordMarker.Ptr(), RecordMarkerDecisionAttributes: &types.RecordMarkerDecisionAttributes{ MarkerName: "some random marker name", Details: []byte("some random marker details"), }, }, { DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "Activity-1", ActivityType: &types.ActivityType{Name: "ActivityType"}, Domain: s.DomainName, TaskList: taskList, Input: []byte("some random activity input"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(100), StartToCloseTimeoutSeconds: common.Int32Ptr(100), HeartbeatTimeoutSeconds: common.Int32Ptr(100), }, }}, nil } if !secondDecision { secondDecision = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeRecordMarker.Ptr(), RecordMarkerDecisionAttributes: &types.RecordMarkerDecisionAttributes{ MarkerName: "some random marker name", Details: []byte("some random marker details"), }, }}, nil } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // first decision, which will schedule an activity and add marker _, task, err := poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( true, false, false, false, int64(0), 1, true, nil) s.Logger.Info("pollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // This will cause activity start and complete to be buffered err = poller.PollAndProcessActivityTask(false) s.Logger.Info("pollAndProcessActivityTask", tag.Error(err)) s.Nil(err) // second decision, completes another local activity and forces flush of buffered activity events newDecisionTask := task.GetDecisionTask() s.NotNil(newDecisionTask) task, err = poller.HandlePartialDecision(newDecisionTask) s.Logger.Info("pollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.NotNil(task) // third decision, which will close workflow newDecisionTask = task.GetDecisionTask() s.NotNil(newDecisionTask) task, err = poller.HandlePartialDecision(newDecisionTask) s.Logger.Info("pollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.Nil(task.DecisionTask) events := s.getHistory(s.DomainName, workflowExecution) var scheduleEvent, startedEvent, completedEvent *types.HistoryEvent for _, event := range events { switch event.GetEventType() { case types.EventTypeActivityTaskScheduled: scheduleEvent = event case types.EventTypeActivityTaskStarted: startedEvent = event case types.EventTypeActivityTaskCompleted: completedEvent = event } } s.NotNil(scheduleEvent) s.NotNil(startedEvent) s.NotNil(completedEvent) s.True(startedEvent.ID < completedEvent.ID) s.Equal(scheduleEvent.ID, startedEvent.ActivityTaskStartedEventAttributes.GetScheduledEventID()) s.Equal(scheduleEvent.ID, completedEvent.ActivityTaskCompletedEventAttributes.GetScheduledEventID()) s.Equal(startedEvent.ID, completedEvent.ActivityTaskCompletedEventAttributes.GetStartedEventID()) s.True(workflowComplete) } type startFunc func() (*types.StartWorkflowExecutionResponse, error) func (s *IntegrationSuite) TestStartWithMemo() { id := "integration-start-with-memo-test" wt := "integration-start-with-memo-test-type" tl := "integration-start-with-memo-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl memoInfo, _ := json.Marshal(id) memo := &types.Memo{ Fields: map[string][]byte{ "Info": memoInfo, }, } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, Memo: memo, } fn := func() (*types.StartWorkflowExecutionResponse, error) { ctx, cancel := createContext() defer cancel() return s.Engine.StartWorkflowExecution(ctx, request) } s.startWithMemoHelper(fn, id, taskList, memo) } func (s *IntegrationSuite) TestSignalWithStartWithMemo() { id := "integration-signal-with-start-with-memo-test" wt := "integration-signal-with-start-with-memo-test-type" tl := "integration-signal-with-start-with-memo-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl memoInfo, _ := json.Marshal(id) memo := &types.Memo{ Fields: map[string][]byte{ "Info": memoInfo, }, } signalName := "my signal" signalInput := []byte("my signal input.") request := &types.SignalWithStartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), SignalName: signalName, SignalInput: signalInput, Identity: identity, Memo: memo, } fn := func() (*types.StartWorkflowExecutionResponse, error) { ctx, cancel := createContext() defer cancel() return s.Engine.SignalWithStartWorkflowExecution(ctx, request) } s.startWithMemoHelper(fn, id, taskList, memo) } func (s *IntegrationSuite) TestCancelTimer() { id := "integration-cancel-timer-test" wt := "integration-cancel-timer-test-type" tl := "integration-cancel-timer-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1000), Identity: identity, } ctx, cancel := createContext() defer cancel() creatResp, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: creatResp.GetRunID(), } TimerID := "1" timerScheduled := false signalDelivered := false timerCancelled := false workflowComplete := false timer := int64(2000) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !timerScheduled { timerScheduled = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeStartTimer.Ptr(), StartTimerDecisionAttributes: &types.StartTimerDecisionAttributes{ TimerID: TimerID, StartToFireTimeoutSeconds: common.Int64Ptr(timer), }, }}, nil } ctx, cancel := createContext() defer cancel() resp, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: workflowExecution, MaximumPageSize: 200, }) s.Nil(err) for _, event := range resp.History.Events { switch event.GetEventType() { case types.EventTypeWorkflowExecutionSignaled: signalDelivered = true case types.EventTypeTimerCanceled: timerCancelled = true } } if !signalDelivered { s.Fail("should receive a signal") } if !timerCancelled { return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCancelTimer.Ptr(), CancelTimerDecisionAttributes: &types.CancelTimerDecisionAttributes{ TimerID: TimerID, }, }}, nil } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: nil, Logger: s.Logger, T: s.T(), } // schedule the timer _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask: completed") s.Nil(err) s.Nil(s.sendSignal(s.DomainName, workflowExecution, "random signal name", []byte("random signal payload"), identity)) // receive the signal & cancel the timer _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask: completed") s.Nil(err) s.Nil(s.sendSignal(s.DomainName, workflowExecution, "random signal name", []byte("random signal payload"), identity)) // complete the workflow _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask: completed") s.Nil(err) s.True(workflowComplete) ctx, cancel = createContext() defer cancel() resp, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: workflowExecution, MaximumPageSize: 200, }) s.Nil(err) for _, event := range resp.History.Events { switch event.GetEventType() { case types.EventTypeWorkflowExecutionSignaled: signalDelivered = true case types.EventTypeTimerCanceled: timerCancelled = true case types.EventTypeTimerFired: s.Fail("timer got fired") } } } func (s *IntegrationSuite) TestCancelTimer_CancelFiredAndBuffered() { id := "integration-cancel-timer-fired-and-buffered-test" wt := "integration-cancel-timer-fired-and-buffered-test-type" tl := "integration-cancel-timer-fired-and-buffered-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1000), Identity: identity, } ctx, cancel := createContext() defer cancel() creatResp, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: creatResp.GetRunID(), } TimerID := 1 timerScheduled := false signalDelivered := false timerCancelled := false workflowComplete := false timer := int64(4) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !timerScheduled { timerScheduled = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeStartTimer.Ptr(), StartTimerDecisionAttributes: &types.StartTimerDecisionAttributes{ TimerID: fmt.Sprintf("%v", TimerID), StartToFireTimeoutSeconds: common.Int64Ptr(timer), }, }}, nil } ctx, cancel := createContext() defer cancel() resp, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: workflowExecution, MaximumPageSize: 200, }) s.Nil(err) for _, event := range resp.History.Events { switch event.GetEventType() { case types.EventTypeWorkflowExecutionSignaled: signalDelivered = true case types.EventTypeTimerCanceled: timerCancelled = true } } if !signalDelivered { s.Fail("should receive a signal") } if !timerCancelled { time.Sleep(time.Duration(2*timer) * time.Second) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCancelTimer.Ptr(), CancelTimerDecisionAttributes: &types.CancelTimerDecisionAttributes{ TimerID: fmt.Sprintf("%v", TimerID), }, }}, nil } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: nil, Logger: s.Logger, T: s.T(), } // schedule the timer _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask: completed") s.Nil(err) s.Nil(s.sendSignal(s.DomainName, workflowExecution, "random signal name", []byte("random signal payload"), identity)) // receive the signal & cancel the timer _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask: completed") s.Nil(err) s.Nil(s.sendSignal(s.DomainName, workflowExecution, "random signal name", []byte("random signal payload"), identity)) // complete the workflow _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask: completed") s.Nil(err) s.True(workflowComplete) ctx, cancel = createContext() defer cancel() resp, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: workflowExecution, MaximumPageSize: 200, }) s.Nil(err) for _, event := range resp.History.Events { switch event.GetEventType() { case types.EventTypeWorkflowExecutionSignaled: signalDelivered = true case types.EventTypeTimerCanceled: timerCancelled = true case types.EventTypeTimerFired: s.Fail("timer got fired") } } } // helper function for TestStartWithMemo and TestSignalWithStartWithMemo to reduce duplicate code func (s *IntegrationSuite) startWithMemoHelper(startFn startFunc, id string, taskList *types.TaskList, memo *types.Memo) { identity := "worker1" we, err0 := startFn() s.Nil(err0) s.Logger.Info("StartWorkflowExecution: response", tag.WorkflowRunID(we.RunID)) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { return []byte(strconv.Itoa(1)), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } // verify open visibility var openExecutionInfo *types.WorkflowExecutionInfo for i := 0; i < 10; i++ { ctx, cancel := createContext() resp, err1 := s.Engine.ListOpenWorkflowExecutions(ctx, &types.ListOpenWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Int64Ptr(0), LatestTime: common.Int64Ptr(time.Now().UnixNano()), }, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: id, }, }) cancel() s.Nil(err1) if len(resp.Executions) == 1 { openExecutionInfo = resp.Executions[0] break } s.Logger.Info("Open WorkflowExecution is not yet visible") time.Sleep(100 * time.Millisecond) } s.NotNil(openExecutionInfo) s.Equal(memo, openExecutionInfo.Memo) // make progress of workflow _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask: %v", tag.Error(err)) s.Nil(err) // verify history execution := &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, } ctx, cancel := createContext() defer cancel() historyResponse, historyErr := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: execution, }) s.Nil(historyErr) history := historyResponse.History firstEvent := history.Events[0] s.Equal(types.EventTypeWorkflowExecutionStarted, firstEvent.GetEventType()) startdEventAttributes := firstEvent.WorkflowExecutionStartedEventAttributes s.Equal(memo, startdEventAttributes.Memo) // verify DescribeWorkflowExecution result descRequest := &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: execution, } ctx, cancel = createContext() defer cancel() descResp, err := s.Engine.DescribeWorkflowExecution(ctx, descRequest) s.Nil(err) s.Equal(memo, descResp.WorkflowExecutionInfo.Memo) // verify closed visibility var closedExecutionInfo *types.WorkflowExecutionInfo for i := 0; i < 10; i++ { ctx, cancel := createContext() resp, err1 := s.Engine.ListClosedWorkflowExecutions(ctx, &types.ListClosedWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Int64Ptr(0), LatestTime: common.Int64Ptr(time.Now().UnixNano()), }, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: id, }, }) cancel() s.Nil(err1) if len(resp.Executions) == 1 { closedExecutionInfo = resp.Executions[0] break } s.Logger.Info("Closed WorkflowExecution is not yet visible") time.Sleep(100 * time.Millisecond) } s.NotNil(closedExecutionInfo) s.Equal(memo, closedExecutionInfo.Memo) } func (s *IntegrationSuite) sendSignal(domainName string, execution *types.WorkflowExecution, signalName string, input []byte, identity string) error { ctx, cancel := createContext() defer cancel() return s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: domainName, WorkflowExecution: execution, SignalName: signalName, Input: input, Identity: identity, }) } // TestDescribeCluster tests that DescribeCluster API returns a valid response without error func (s *IntegrationSuite) TestDescribeCluster() { ctx, cancel := createContext() defer cancel() response, err := s.AdminClient.DescribeCluster(ctx) s.Require().NoError(err) s.Require().NotNil(response) } ================================================ FILE: host/integration_test_cron.go ================================================ package host import ( "fmt" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) func (s *IntegrationSuite) TestCronWorkflowWithOverlapPolicy() { id := "integration-wf-cron-overlap-test" wt := "integration-wf-cron-overlap-type" tl := "integration-wf-cron-overlap-tasklist" identity := "worker1" cronSchedule := "@every 5s" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Test both overlap policies testCases := []struct { name string cronOverlapPolicy types.CronOverlapPolicy }{ { name: "SkippedPolicy", cronOverlapPolicy: types.CronOverlapPolicySkipped, }, { name: "BufferOnePolicy", cronOverlapPolicy: types.CronOverlapPolicyBufferOne, }, } for _, tc := range testCases { s.Run(tc.name, func() { workflowID := id + "-" + tc.name request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: workflowID, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, CronSchedule: cronSchedule, CronOverlapPolicy: &tc.cronOverlapPolicy, } ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) // Logging for debug fmt.Printf("StartWorkflowExecution RunID: %s\n", we.RunID) var executions []*types.WorkflowExecution attemptCount := 0 executionTimes := make([]time.Time, 0) // Decision task handler that simulates long-running workflow dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { executions = append(executions, execution) attemptCount++ executionTimes = append(executionTimes, time.Now()) if attemptCount == 1 { return nil, []*types.Decision{ { DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte(fmt.Sprintf("execution-%d-complete", attemptCount)), }, }, }, nil } return nil, []*types.Decision{ { DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte(fmt.Sprintf("execution-%d-complete", attemptCount)), }, }, }, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } for i := 0; i < 3; i++ { _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) time.Sleep(500 * time.Millisecond) } s.True(attemptCount >= 2, "Expected at least 2 executions, got %d", attemptCount) if len(executionTimes) >= 2 { for i := 1; i < len(executionTimes); i++ { timeDiff := executionTimes[i].Sub(executionTimes[i-1]) if tc.cronOverlapPolicy == types.CronOverlapPolicySkipped { s.True(timeDiff >= 1500*time.Millisecond, "Expected execution %d to be spaced by at least 1.5s from previous, got %v", i, timeDiff) } else { s.True(timeDiff >= 0, "Expected non-negative time difference, got %v", timeDiff) } } } if len(executions) > 0 { events := s.getHistory(s.DomainName, executions[0]) s.True(len(events) > 0, "Expected workflow history to have events") lastEvent := events[len(events)-1] s.Equal(types.EventTypeWorkflowExecutionContinuedAsNew, lastEvent.GetEventType()) attributes := lastEvent.WorkflowExecutionContinuedAsNewEventAttributes s.Equal(types.ContinueAsNewInitiatorCronSchedule, attributes.GetInitiator()) } ctx, cancel = createContext() defer cancel() terminateErr := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, }, }) s.NoError(terminateErr) ctx, cancel = createContext() defer cancel() dweResponse, err := s.Engine.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: we.RunID, }, }) s.Nil(err) // Verify that the workflow execution info is returned after termination s.NotNil(dweResponse.WorkflowExecutionInfo) s.True(dweResponse.WorkflowExecutionInfo.IsCron, "Expected workflow to be marked as cron") s.Equal(tc.cronOverlapPolicy, *dweResponse.WorkflowExecutionInfo.CronOverlapPolicy) }) } } func (s *IntegrationSuite) TestCronWorkflowOverlapBehavior() { id := "integration-wf-cron-overlap-behavior-test" wt := "integration-wf-cron-overlap-behavior-type" tl := "integration-wf-cron-overlap-behavior-tasklist" identity := "worker1" cronSchedule := "@every 1s" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Test both overlap policies with realistic overlap scenarios testCases := []struct { name string cronOverlapPolicy types.CronOverlapPolicy workflowDuration time.Duration }{ { name: "SkippedPolicy_ShortWorkflow", cronOverlapPolicy: types.CronOverlapPolicySkipped, workflowDuration: 500 * time.Millisecond, }, { name: "SkippedPolicy_LongWorkflow", cronOverlapPolicy: types.CronOverlapPolicySkipped, workflowDuration: 2 * time.Second, }, { name: "BufferOnePolicy_ShortWorkflow", cronOverlapPolicy: types.CronOverlapPolicyBufferOne, workflowDuration: 500 * time.Millisecond, }, { name: "BufferOnePolicy_LongWorkflow", cronOverlapPolicy: types.CronOverlapPolicyBufferOne, workflowDuration: 2 * time.Second, }, } for _, tc := range testCases { s.Run(tc.name, func() { workflowID := id + "-" + tc.name request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: workflowID, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, CronSchedule: cronSchedule, CronOverlapPolicy: &tc.cronOverlapPolicy, } ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) var executions []*types.WorkflowExecution attemptCount := 0 executionStartTimes := make([]time.Time, 0) executionEndTimes := make([]time.Time, 0) // Decision task handler that simulates workflows with controlled duration dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { executions = append(executions, execution) attemptCount++ startTime := time.Now() executionStartTimes = append(executionStartTimes, startTime) // Simulate workflow duration by sleeping time.Sleep(tc.workflowDuration) endTime := time.Now() executionEndTimes = append(executionEndTimes, endTime) // Complete the workflow return nil, []*types.Decision{ { DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte(fmt.Sprintf("execution-%d-complete-duration-%v", attemptCount, tc.workflowDuration)), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } // Process multiple decision tasks to observe overlap behavior // For long workflows, we'll process fewer tasks to avoid test timeout maxTasks := 3 if tc.workflowDuration > time.Second { maxTasks = 2 } for i := 0; i < maxTasks; i++ { _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil, err) } // Verify we have executions s.True(attemptCount >= 1, "Expected at least 1 execution, got %d", attemptCount) // Analyze overlap behavior - only if we have multiple executions if len(executionStartTimes) >= 2 { for i := 1; i < len(executionStartTimes); i++ { timeBetweenStarts := executionStartTimes[i].Sub(executionStartTimes[i-1]) timeBetweenEndAndNextStart := executionStartTimes[i].Sub(executionEndTimes[i-1]) s.Logger.Info("Execution timing analysis", tag.Counter(i), tag.WorkflowID(fmt.Sprintf("timeBetweenStarts:%v", timeBetweenStarts)), tag.WorkflowRunID(fmt.Sprintf("timeBetweenEndAndNextStart:%v", timeBetweenEndAndNextStart)), tag.WorkflowID(fmt.Sprintf("workflowDuration:%v", tc.workflowDuration)), tag.WorkflowID(fmt.Sprintf("overlapPolicy:%s", tc.cronOverlapPolicy.String()))) if tc.cronOverlapPolicy == types.CronOverlapPolicySkipped { // For skipped policy, if workflow duration > cron interval, // subsequent executions should be skipped until previous completes if tc.workflowDuration > time.Second { // Should wait for previous execution to complete s.True(timeBetweenEndAndNextStart >= 0, "Expected next execution to start after previous completion, got %v", timeBetweenEndAndNextStart) } else { // Short workflows should follow cron schedule s.True(timeBetweenStarts >= 800*time.Millisecond, "Expected executions to be spaced by cron interval, got %v", timeBetweenStarts) } } else { // For buffer one policy, next execution should start after previous completion // (but we're more lenient with timing in test environment) s.True(timeBetweenEndAndNextStart >= 0, "Expected next execution to start after previous completion, got %v", timeBetweenEndAndNextStart) } } } // Verify workflow history if len(executions) > 0 { events := s.getHistory(s.DomainName, executions[0]) s.True(len(events) > 0, "Expected workflow history to have events") // Check that the last event is a continue-as-new event lastEvent := events[len(events)-1] s.Equal(types.EventTypeWorkflowExecutionContinuedAsNew, lastEvent.GetEventType()) attributes := lastEvent.WorkflowExecutionContinuedAsNewEventAttributes s.Equal(types.ContinueAsNewInitiatorCronSchedule, attributes.GetInitiator()) } // Terminate the cron workflow ctx, cancel = createContext() defer cancel() terminateErr := s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, }, }) s.NoError(terminateErr) }) } } ================================================ FILE: host/integrationbase.go ================================================ // Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "context" "encoding/json" "fmt" "os" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/suite" "go.uber.org/yarpc" "go.uber.org/yarpc/transport/tchannel" "gopkg.in/yaml.v2" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/persistence-tests/testcluster" "github.com/uber/cadence/common/persistence/sql/sqlplugin/sqlite" "github.com/uber/cadence/common/types" "github.com/uber/cadence/environment" ) type ( // IntegrationBase is a base struct for integration tests IntegrationBase struct { suite.Suite TestCluster *TestCluster TestClusterConfig *TestClusterConfig Engine FrontendClient HistoryClient HistoryClient AdminClient AdminClient Logger log.Logger DomainName string SecondaryDomainName string TestRawHistoryDomainName string ForeignDomainName string ArchivalDomainName string ActiveActiveDomainName string DefaultTestCluster testcluster.PersistenceTestCluster VisibilityTestCluster testcluster.PersistenceTestCluster } IntegrationBaseParams struct { T *testing.T DefaultTestCluster testcluster.PersistenceTestCluster VisibilityTestCluster testcluster.PersistenceTestCluster TestClusterConfig *TestClusterConfig } ) func NewIntegrationBase(params IntegrationBaseParams) *IntegrationBase { return &IntegrationBase{ DefaultTestCluster: params.DefaultTestCluster, VisibilityTestCluster: params.VisibilityTestCluster, TestClusterConfig: params.TestClusterConfig, } } func (s *IntegrationBase) setupSuite() { s.SetupLogger() if s.TestClusterConfig.FrontendAddress != "" { s.Logger.Info("Running integration test against specified frontend", tag.Address(TestFlags.FrontendAddr)) channel, err := tchannel.NewChannelTransport(tchannel.ServiceName("cadence-frontend")) s.Require().NoError(err) dispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: "unittest", Outbounds: yarpc.Outbounds{ "cadence-frontend": {Unary: channel.NewSingleOutbound(TestFlags.FrontendAddr)}, }, InboundMiddleware: yarpc.InboundMiddleware{ Unary: &versionMiddleware{}, }, }) if err := dispatcher.Start(); err != nil { s.Logger.Fatal("Failed to create outbound transport channel", tag.Error(err)) } s.Engine = NewFrontendClient(dispatcher) s.HistoryClient = NewHistoryClient(dispatcher) s.AdminClient = NewAdminClient(dispatcher) } else { s.Logger.Info("Running integration test against test cluster") clusterMetadata := NewClusterMetadata(s.T(), s.TestClusterConfig) dc := persistence.DynamicConfiguration{ EnableSQLAsyncTransaction: dynamicproperties.GetBoolPropertyFn(false), EnableCassandraAllConsistencyLevelDelete: dynamicproperties.GetBoolPropertyFn(true), PersistenceSampleLoggingRate: dynamicproperties.GetIntPropertyFn(100), EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(true), EnableHistoryTaskDualWriteMode: dynamicproperties.GetBoolPropertyFn(true), ReadNoSQLHistoryTaskFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(true), HistoryNodeDeleteBatchSize: dynamicproperties.GetIntPropertyFn(1000), } params := pt.TestBaseParams{ DefaultTestCluster: s.DefaultTestCluster, VisibilityTestCluster: s.VisibilityTestCluster, ClusterMetadata: clusterMetadata, DynamicConfiguration: dc, } cluster, err := NewCluster(s.T(), s.TestClusterConfig, s.Logger, params) s.Require().NoError(err) s.TestCluster = cluster s.Engine = s.TestCluster.GetFrontendClient() s.HistoryClient = s.TestCluster.GetHistoryClient() s.AdminClient = s.TestCluster.GetAdminClient() } s.TestRawHistoryDomainName = "TestRawHistoryDomain" s.DomainName = s.RandomizeStr("integration-test-domain") s.Require().NoError( s.RegisterDomain(s.DomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.Require().NoError( s.RegisterDomain(s.TestRawHistoryDomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.ForeignDomainName = s.RandomizeStr("integration-foreign-test-domain") s.Require().NoError( s.RegisterDomain(s.ForeignDomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.Require().NoError(s.registerArchivalDomain()) s.ActiveActiveDomainName = s.RandomizeStr("integration-active-active-test-domain") s.Require().NoError(s.RegisterDomain(s.ActiveActiveDomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": {ActiveClusterName: s.TestCluster.testBase.ClusterMetadata.GetCurrentClusterName()}, }, }, "city": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "tokyo": {ActiveClusterName: s.TestCluster.testBase.ClusterMetadata.GetCurrentClusterName()}, }, }, }, })) // this sleep is necessary because domainv2 cache gets refreshed in the // background only every domainCacheRefreshInterval period time.Sleep(cache.DomainCacheRefreshInterval + time.Second) } func (s *IntegrationBase) SetupLogger() { s.Logger = testlogger.New(s.T()) } // GetTestClusterConfig return test cluster config func GetTestClusterConfig(configFile string) (*TestClusterConfig, error) { if err := environment.SetupEnv(); err != nil { return nil, err } configLocation := configFile if TestFlags.TestClusterConfigFile != "" { configLocation = TestFlags.TestClusterConfigFile } // This is just reading a config so it's less of a security concern // #nosec confContent, err := os.ReadFile(configLocation) if err != nil { return nil, fmt.Errorf("failed to read test cluster config file %v: %v", configLocation, err) } confContent = []byte(os.ExpandEnv(string(confContent))) var options TestClusterConfig if err := yaml.Unmarshal(confContent, &options); err != nil { return nil, fmt.Errorf("failed to decode test cluster config %v", tag.Error(err)) } options.FrontendAddress = TestFlags.FrontendAddr if options.ESConfig != nil { options.ESConfig.Indices[constants.VisibilityAppName] += uuid.New() } if options.Persistence.DBName == "" { options.Persistence.DBName = "test_" + pt.GenerateRandomDBName(10) } return &options, nil } // GetTestClusterConfigs return test cluster configs func GetTestClusterConfigs(configFile string) ([]*TestClusterConfig, error) { if err := environment.SetupEnv(); err != nil { return nil, err } fileName := configFile if TestFlags.TestClusterConfigFile != "" { fileName = TestFlags.TestClusterConfigFile } confContent, err := os.ReadFile(fileName) if err != nil { return nil, fmt.Errorf("failed to read test cluster config file %v: %v", fileName, err) } confContent = []byte(os.ExpandEnv(string(confContent))) var clusterConfigs []*TestClusterConfig if err := yaml.Unmarshal(confContent, &clusterConfigs); err != nil { return nil, fmt.Errorf("failed to decode test cluster config %v", tag.Error(err)) } return clusterConfigs, nil } func (s *IntegrationBase) TearDownBaseSuite() { if s.TestCluster != nil { s.TestCluster.TearDownCluster() s.TestCluster = nil s.Engine = nil s.AdminClient = nil } } func (s *IntegrationBase) RegisterDomain( domain string, retentionDays int, historyArchivalStatus types.ArchivalStatus, historyArchivalURI string, visibilityArchivalStatus types.ArchivalStatus, visibilityArchivalURI string, activeClusters *types.ActiveClusters, ) error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() isGlobalDomain := true if TestFlags.SQLPluginName == sqlite.PluginName { // There seems to be a bug in the SQLite plugin that causes integration tests to fail. // EnqueueMessage operation failed. Error: sqlite3: database is locked isGlobalDomain = false } return s.Engine.RegisterDomain(ctx, &types.RegisterDomainRequest{ Name: domain, Description: domain, WorkflowExecutionRetentionPeriodInDays: int32(retentionDays), HistoryArchivalStatus: &historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: &visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, ActiveClusters: activeClusters, IsGlobalDomain: isGlobalDomain, }) } func (s *IntegrationBase) domainCacheRefresh() { s.TestClusterConfig.TimeSource.Advance(cache.DomainCacheRefreshInterval + time.Second) // this sleep is necessary to yield execution to other goroutines. not 100% guaranteed to work time.Sleep(2 * time.Second) } func (s *IntegrationBase) RandomizeStr(id string) string { return fmt.Sprintf("%v-%v", id, uuid.New()) } func (s *IntegrationBase) printWorkflowHistory(domain string, execution *types.WorkflowExecution) { events := s.getHistory(domain, execution) history := &types.History{} history.Events = events PrettyPrintHistory(history, s.Logger) } func (s *IntegrationBase) getHistory(domain string, execution *types.WorkflowExecution) []*types.HistoryEvent { ctx, cancel := createContext() defer cancel() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: domain, Execution: execution, MaximumPageSize: 5, // Use small page size to force pagination code path }) s.Require().NoError(err) events := historyResponse.History.Events for historyResponse.NextPageToken != nil { ctx, cancel := createContext() historyResponse, err = s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: domain, Execution: execution, NextPageToken: historyResponse.NextPageToken, }) cancel() s.Require().NoError(err) events = append(events, historyResponse.History.Events...) } return events } // To register archival domain we can't use frontend API as the retention period is set to 0 for testing, // and request will be rejected by frontend. Here we make a call directly to persistence to register // the domain. func (s *IntegrationBase) registerArchivalDomain() error { ctx, cancel := context.WithTimeout(context.Background(), defaultTestPersistenceTimeout) defer cancel() s.ArchivalDomainName = s.RandomizeStr("integration-archival-enabled-domain") currentClusterName := s.TestCluster.testBase.ClusterMetadata.GetCurrentClusterName() domainRequest := &persistence.CreateDomainRequest{ Info: &persistence.DomainInfo{ ID: uuid.New(), Name: s.ArchivalDomainName, Status: persistence.DomainStatusRegistered, }, Config: &persistence.DomainConfig{ Retention: 0, HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: s.TestCluster.archiverBase.historyURI, VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: s.TestCluster.archiverBase.visibilityURI, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: currentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: currentClusterName}, }, }, IsGlobalDomain: false, FailoverVersion: constants.EmptyVersion, } response, err := s.TestCluster.testBase.DomainManager.CreateDomain(ctx, domainRequest) if err == nil { s.Logger.Info("Register domain succeeded", tag.WorkflowDomainName(s.ArchivalDomainName), tag.WorkflowDomainID(response.ID), ) } return err } // PrettyPrintHistory prints history in human readable format func PrettyPrintHistory(history *types.History, logger log.Logger) { data, err := json.MarshalIndent(history, "", " ") if err != nil { logger.Error("Error serializing history: %v\n", tag.Error(err)) } fmt.Println("******************************************") fmt.Println("History", tag.DetailInfo(string(data))) fmt.Println("******************************************") } ================================================ FILE: host/membership_hashring.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "github.com/dgryski/go-farm" "github.com/uber/cadence/common/membership" ) type simpleHashring struct { hosts []membership.HostInfo hashfunc func([]byte) uint32 } // newSimpleHashring returns a service resolver that maintains static mapping // between services and host info func newSimpleHashring(hosts []membership.HostInfo) *simpleHashring { return &simpleHashring{ hosts: hosts, hashfunc: farm.Fingerprint32, } } func (s *simpleHashring) Lookup(key string) (membership.HostInfo, error) { hash := int(s.hashfunc([]byte(key))) idx := hash % len(s.hosts) return s.hosts[idx], nil } func (s *simpleHashring) AddListener(name string, notifyChannel chan<- *membership.ChangedEvent) error { return nil } func (s *simpleHashring) RemoveListener(name string) error { return nil } func (s *simpleHashring) MemberCount() int { return len(s.hosts) } func (s *simpleHashring) Members() []membership.HostInfo { return s.hosts } ================================================ FILE: host/membership_resolver.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "errors" "fmt" "github.com/uber/cadence/common/membership" ) type simpleResolver struct { hostInfo membership.HostInfo resolvers map[string]*simpleHashring } // NewSimpleResolver returns a membership resolver interface func NewSimpleResolver(serviceName string, hosts map[string][]membership.HostInfo, currentHost membership.HostInfo) membership.Resolver { resolvers := make(map[string]*simpleHashring, len(hosts)) for service, hostList := range hosts { resolvers[service] = newSimpleHashring(hostList) } return &simpleResolver{ hostInfo: currentHost, resolvers: resolvers, } } func (s *simpleResolver) Start() { } func (s *simpleResolver) Stop() { } func (s *simpleResolver) EvictSelf() error { return nil } func (s *simpleResolver) WhoAmI() (membership.HostInfo, error) { return s.hostInfo, nil } func (s *simpleResolver) Subscribe(service string, name string, notifyChannel chan<- *membership.ChangedEvent) error { return nil } func (s *simpleResolver) Unsubscribe(service string, name string) error { return nil } func (s *simpleResolver) Lookup(service string, key string) (membership.HostInfo, error) { resolver, ok := s.resolvers[service] if !ok { return membership.HostInfo{}, fmt.Errorf("cannot lookup host for service %q", service) } return resolver.Lookup(key) } func (s *simpleResolver) MemberCount(service string) (int, error) { members, err := s.Members(service) return len(members), err } func (s *simpleResolver) Members(service string) ([]membership.HostInfo, error) { resolver, ok := s.resolvers[service] if !ok { return nil, fmt.Errorf("cannot lookup host for service %q", service) } return resolver.Members(), nil } func (s *simpleResolver) LookupByAddress(service string, address string) (membership.HostInfo, error) { resolver, ok := s.resolvers[service] if !ok { return membership.HostInfo{}, fmt.Errorf("cannot lookup host for service %q", service) } for _, m := range resolver.Members() { if belongs, err := m.Belongs(address); err == nil && belongs { return m, nil } } return membership.HostInfo{}, errors.New("host not found") } ================================================ FILE: host/ndc/integration_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( "context" "flag" "fmt" "sync/atomic" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "go.uber.org/yarpc" adminClient "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" pt "github.com/uber/cadence/common/persistence/persistence-tests" test "github.com/uber/cadence/common/testing" "github.com/uber/cadence/common/types" "github.com/uber/cadence/host" ) var ( clusterName = []string{"active", "standby", "other"} clusterReplicationConfig = []*types.ClusterReplicationConfiguration{ {ClusterName: clusterName[0]}, {ClusterName: clusterName[1]}, {ClusterName: clusterName[2]}, } ) func TestNDCIntegrationTestSuite(t *testing.T) { flag.Parse() clusterConfigs, err := host.GetTestClusterConfigs("../testdata/ndc_integration_test_clusters.yaml") if err != nil { panic(err) } clusterConfigs[0].WorkerConfig = &host.WorkerConfig{} clusterConfigs[1].WorkerConfig = &host.WorkerConfig{} testCluster := host.NewPersistenceTestCluster(t, clusterConfigs[0]) params := NDCIntegrationTestSuiteParams{ ClusterConfigs: clusterConfigs, DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, } s := NewNDCIntegrationTestSuite(params) suite.Run(t, s) } func (s *NDCIntegrationTestSuite) SetupSuite() { s.serializer = persistence.NewPayloadSerializer() s.logger = testlogger.New(s.T()) s.standByReplicationTasksChan = make(chan *types.ReplicationTask, 100) s.standByTaskID = 0 s.mockAdminClient = make(map[string]adminClient.Client) controller := gomock.NewController(s.T()) mockStandbyClient := adminClient.NewMockClient(controller) mockStandbyClient.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any()).DoAndReturn(s.GetReplicationMessagesMock).AnyTimes() mockOtherClient := adminClient.NewMockClient(controller) mockOtherClient.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any()).Return( &types.GetReplicationMessagesResponse{ MessagesByShard: make(map[int32]*types.ReplicationMessages), }, nil).AnyTimes() s.mockAdminClient["standby"] = mockStandbyClient s.mockAdminClient["other"] = mockOtherClient s.clusterConfigs[0].MockAdminClient = s.mockAdminClient clusterMetadata := host.NewClusterMetadata(s.T(), s.clusterConfigs[0]) dc := persistence.DynamicConfiguration{ EnableSQLAsyncTransaction: dynamicproperties.GetBoolPropertyFn(false), EnableCassandraAllConsistencyLevelDelete: dynamicproperties.GetBoolPropertyFn(true), EnableHistoryTaskDualWriteMode: dynamicproperties.GetBoolPropertyFn(true), ReadNoSQLHistoryTaskFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(true), HistoryNodeDeleteBatchSize: dynamicproperties.GetIntPropertyFn(1000), } params := pt.TestBaseParams{ DefaultTestCluster: s.defaultTestCluster, VisibilityTestCluster: s.VisibilityTestCluster, ClusterMetadata: clusterMetadata, DynamicConfiguration: dc, } cluster, err := host.NewCluster(s.T(), s.clusterConfigs[0], s.logger.WithTags(tag.ClusterName(clusterName[0])), params) s.Require().NoError(err) s.active = cluster s.RegisterDomain() s.version = s.clusterConfigs[1].ClusterGroupMetadata.ClusterGroup[s.clusterConfigs[1].ClusterGroupMetadata.CurrentClusterName].InitialFailoverVersion s.versionIncrement = s.clusterConfigs[0].ClusterGroupMetadata.FailoverVersionIncrement s.generator = test.InitializeHistoryEventGenerator(s.domainName, s.version) } func (s *NDCIntegrationTestSuite) GetReplicationMessagesMock( ctx context.Context, request *types.GetReplicationMessagesRequest, opts ...yarpc.CallOption, ) (*types.GetReplicationMessagesResponse, error) { select { case task := <-s.standByReplicationTasksChan: taskID := atomic.AddInt64(&s.standByTaskID, 1) task.SourceTaskID = taskID tasks := []*types.ReplicationTask{task} for len(s.standByReplicationTasksChan) > 0 { task = <-s.standByReplicationTasksChan taskID := atomic.AddInt64(&s.standByTaskID, 1) task.SourceTaskID = taskID tasks = append(tasks, task) } replicationMessage := &types.ReplicationMessages{ ReplicationTasks: tasks, LastRetrievedMessageID: tasks[len(tasks)-1].SourceTaskID, HasMore: true, } return &types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{0: replicationMessage}, }, nil default: return &types.GetReplicationMessagesResponse{ MessagesByShard: make(map[int32]*types.ReplicationMessages), }, nil } } func (s *NDCIntegrationTestSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) s.generator = test.InitializeHistoryEventGenerator(s.domainName, s.version) } func (s *NDCIntegrationTestSuite) TearDownSuite() { if s.generator != nil { s.generator.Reset() } s.active.TearDownCluster() } func (s *NDCIntegrationTestSuite) TestSingleBranch() { s.setupRemoteFrontendClients() workflowID := "ndc-single-branch-test" + uuid.New() workflowType := "event-generator-workflow-type" tasklist := "event-generator-taskList" // active has initial version 0 historyClient := s.active.GetHistoryClient() versions := []int64{101, 1, 201, 301, 401, 601, 501, 801, 1001, 901, 701, 1101} for _, version := range versions { runID := uuid.New() historyBatch := []*types.History{} s.generator = test.InitializeHistoryEventGenerator(s.domainName, version) for s.generator.HasNextVertex() { events := s.generator.GetNextVertices() historyEvents := &types.History{} for _, event := range events { historyEvents.Events = append(historyEvents.Events, event.GetData().(*types.HistoryEvent)) } historyBatch = append(historyBatch, historyEvents) } versionHistory := s.eventBatchesToVersionHistory(nil, historyBatch) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory, historyBatch, historyClient, ) err := s.verifyEventHistory(workflowID, runID, historyBatch) s.Require().NoError(err) } } func (s *NDCIntegrationTestSuite) verifyEventHistory( workflowID string, runID string, historyBatch []*types.History, ) error { // get replicated history events from passive side passiveClient := s.active.GetFrontendClient() ctx, cancel := s.createContext() defer cancel() replicatedHistory, err := passiveClient.GetWorkflowExecutionHistory( ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, MaximumPageSize: 1000, NextPageToken: nil, WaitForNewEvent: false, HistoryEventFilterType: types.HistoryEventFilterTypeAllEvent.Ptr(), }, ) if err != nil { return fmt.Errorf("failed to get history event from passive side: %v", err) } // compare origin events with replicated events batchIndex := 0 batch := historyBatch[batchIndex].Events eventIndex := 0 for _, event := range replicatedHistory.GetHistory().GetEvents() { if eventIndex >= len(batch) { batchIndex++ batch = historyBatch[batchIndex].Events eventIndex = 0 } originEvent := batch[eventIndex] eventIndex++ if originEvent.GetEventType() != event.GetEventType() { return fmt.Errorf("the replicated event (%v) and the origin event (%v) are not the same", originEvent.GetEventType().String(), event.GetEventType().String()) } } return nil } func (s *NDCIntegrationTestSuite) TestMultipleBranches() { s.setupRemoteFrontendClients() workflowID := "ndc-multiple-branches-test" + uuid.New() workflowType := "event-generator-workflow-type" tasklist := "event-generator-taskList" // active has initial version 0 historyClient := s.active.GetHistoryClient() versions := []int64{101, 1, 201} for _, version := range versions { runID := uuid.New() baseBranch := []*types.History{} baseGenerator := test.InitializeHistoryEventGenerator(s.domainName, version) baseGenerator.SetVersion(version) for i := 0; i < 10 && baseGenerator.HasNextVertex(); i++ { events := baseGenerator.GetNextVertices() historyEvents := &types.History{} for _, event := range events { historyEvents.Events = append(historyEvents.Events, event.GetData().(*types.HistoryEvent)) } baseBranch = append(baseBranch, historyEvents) } baseVersionHistory := s.eventBatchesToVersionHistory(nil, baseBranch) branch1 := []*types.History{} branchVersionHistory1 := baseVersionHistory.Duplicate() branchGenerator1 := baseGenerator.DeepCopy() for i := 0; i < 10 && branchGenerator1.HasNextVertex(); i++ { events := branchGenerator1.GetNextVertices() historyEvents := &types.History{} for _, event := range events { historyEvents.Events = append(historyEvents.Events, event.GetData().(*types.HistoryEvent)) } branch1 = append(branch1, historyEvents) } branchVersionHistory1 = s.eventBatchesToVersionHistory(branchVersionHistory1, branch1) branch2 := []*types.History{} branchVersionHistory2 := baseVersionHistory.Duplicate() branchGenerator2 := baseGenerator.DeepCopy() branchGenerator2.SetVersion(branchGenerator2.GetVersion() + 1) for i := 0; i < 10 && branchGenerator2.HasNextVertex(); i++ { events := branchGenerator2.GetNextVertices() historyEvents := &types.History{} for _, event := range events { historyEvents.Events = append(historyEvents.Events, event.GetData().(*types.HistoryEvent)) } branch2 = append(branch2, historyEvents) } branchVersionHistory2 = s.eventBatchesToVersionHistory(branchVersionHistory2, branch2) s.applyEvents( workflowID, runID, workflowType, tasklist, baseVersionHistory, baseBranch, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, branchVersionHistory1, branch1, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, branchVersionHistory2, branch2, historyClient, ) } } func (s *NDCIntegrationTestSuite) TestHandcraftedMultipleBranches() { s.setupRemoteFrontendClients() workflowID := "ndc-handcrafted-multiple-branches-test" + uuid.New() runID := uuid.New() workflowType := "event-generator-workflow-type" tasklist := "event-generator-taskList" identity := "worker-identity" // active has initial version 0 historyClient := s.active.GetHistoryClient() eventsBatch1 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 1, Version: 21, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1000), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1000), FirstDecisionTaskBackoffSeconds: common.Int32Ptr(100), }, }, { ID: 2, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 3, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 2, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 4, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 2, StartedEventID: 3, Identity: identity, }, }, { ID: 5, Version: 21, EventType: types.EventTypeMarkerRecorded.Ptr(), MarkerRecordedEventAttributes: &types.MarkerRecordedEventAttributes{ MarkerName: "some marker name", Details: []byte("some marker details"), DecisionTaskCompletedEventID: 4, }, }, { ID: 6, Version: 21, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ DecisionTaskCompletedEventID: 4, ActivityID: "0", ActivityType: &types.ActivityType{Name: "activity-type"}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(20), ScheduleToStartTimeoutSeconds: common.Int32Ptr(20), StartToCloseTimeoutSeconds: common.Int32Ptr(20), HeartbeatTimeoutSeconds: common.Int32Ptr(20), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 7, Version: 21, EventType: types.EventTypeActivityTaskStarted.Ptr(), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 6, Identity: identity, RequestID: uuid.New(), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 8, Version: 21, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some signal name 1", Input: []byte("some signal details 1"), Identity: identity, }, }, { ID: 9, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 10, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 9, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 11, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 9, StartedEventID: 10, Identity: identity, }, }, { ID: 12, Version: 21, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some signal name 2", Input: []byte("some signal details 2"), Identity: identity, }, }, { ID: 13, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, { ID: 14, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 13, Identity: identity, RequestID: uuid.New(), }, }, }}, } eventsBatch2 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 15, Version: 31, EventType: types.EventTypeWorkflowExecutionTimedOut.Ptr(), WorkflowExecutionTimedOutEventAttributes: &types.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, }}, } eventsBatch3 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 15, Version: 30, EventType: types.EventTypeDecisionTaskTimedOut.Ptr(), DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: 13, StartedEventID: 14, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, { ID: 16, Version: 30, EventType: types.EventTypeActivityTaskTimedOut.Ptr(), ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 6, StartedEventID: 7, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, { ID: 17, Version: 30, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 18, Version: 30, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 17, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 19, Version: 30, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 8, StartedEventID: 9, Identity: identity, }, }, { ID: 20, Version: 30, EventType: types.EventTypeWorkflowExecutionFailed.Ptr(), WorkflowExecutionFailedEventAttributes: &types.WorkflowExecutionFailedEventAttributes{ DecisionTaskCompletedEventID: 19, Reason: common.StringPtr("some random reason"), Details: nil, }, }, }}, } versionHistory1 := s.eventBatchesToVersionHistory(nil, eventsBatch1) versionHistory2, err := versionHistory1.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(14, 21), ) s.NoError(err) versionHistory2 = s.eventBatchesToVersionHistory(versionHistory2, eventsBatch2) versionHistory3, err := versionHistory1.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(14, 21), ) s.NoError(err) versionHistory3 = s.eventBatchesToVersionHistory(versionHistory3, eventsBatch3) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory1, eventsBatch1, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory3, eventsBatch3, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory2, eventsBatch2, historyClient, ) } func (s *NDCIntegrationTestSuite) TestHandcraftedResetWorkflow_Zombie() { s.setupRemoteFrontendClients() workflowID := "ndc-handcrafted-reset-workflow-test" + uuid.New() runID := uuid.New() newRunID := uuid.New() workflowType := "event-generator-workflow-type" tasklist := "event-generator-taskList" identity := "worker-identity" // active has initial version 0 historyClient := s.active.GetHistoryClient() eventsBatch1 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 1, Version: 21, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1000), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1000), FirstDecisionTaskBackoffSeconds: common.Int32Ptr(100), }, }, { ID: 2, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 3, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 2, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 4, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 2, StartedEventID: 3, Identity: identity, }, }, { ID: 5, Version: 21, EventType: types.EventTypeMarkerRecorded.Ptr(), MarkerRecordedEventAttributes: &types.MarkerRecordedEventAttributes{ MarkerName: "some marker name", Details: []byte("some marker details"), DecisionTaskCompletedEventID: 4, }, }, { ID: 6, Version: 21, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ DecisionTaskCompletedEventID: 4, ActivityID: "0", ActivityType: &types.ActivityType{Name: "activity-type"}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(20), ScheduleToStartTimeoutSeconds: common.Int32Ptr(20), StartToCloseTimeoutSeconds: common.Int32Ptr(20), HeartbeatTimeoutSeconds: common.Int32Ptr(20), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 7, Version: 21, EventType: types.EventTypeActivityTaskStarted.Ptr(), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 6, Identity: identity, RequestID: uuid.New(), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 8, Version: 21, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some signal name 1", Input: []byte("some signal details 1"), Identity: identity, }, }, { ID: 9, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 10, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 9, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 11, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 9, StartedEventID: 10, Identity: identity, }, }, { ID: 12, Version: 21, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some signal name 2", Input: []byte("some signal details 2"), Identity: identity, }, }, { ID: 13, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, { ID: 14, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 13, Identity: identity, RequestID: uuid.New(), }, }, }}, } eventsBatch2 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 15, Version: 30, EventType: types.EventTypeDecisionTaskTimedOut.Ptr(), DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: 13, StartedEventID: 14, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, { ID: 16, Version: 30, EventType: types.EventTypeActivityTaskTimedOut.Ptr(), ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 6, StartedEventID: 7, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, { ID: 17, Version: 30, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 18, Version: 30, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 17, Identity: identity, RequestID: uuid.New(), }, }, }}, } resetCause := types.DecisionTaskFailedCauseResetWorkflow dtFailedReason := "events-reapplication" eventsBatch3 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 11, Version: 22, EventType: types.EventTypeDecisionTaskFailed.Ptr(), DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ ScheduledEventID: 9, StartedEventID: 10, Identity: identity, Cause: &resetCause, Reason: &dtFailedReason, BaseRunID: runID, NewRunID: newRunID, ForkEventVersion: 21, }, }, }}, } versionHistory1 := s.eventBatchesToVersionHistory(nil, eventsBatch1) versionHistory2, err := versionHistory1.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(14, 21), ) s.NoError(err) versionHistory2 = s.eventBatchesToVersionHistory(versionHistory2, eventsBatch2) versionHistory3, err := versionHistory1.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(10, 21), ) s.NoError(err) versionHistory3 = s.eventBatchesToVersionHistory(versionHistory3, eventsBatch3) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory1, eventsBatch1, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory2, eventsBatch2, historyClient, ) s.applyEvents( workflowID, newRunID, workflowType, tasklist, versionHistory3, eventsBatch3, historyClient, ) } func (s *NDCIntegrationTestSuite) TestHandcraftedResetWorkflow() { s.setupRemoteFrontendClients() workflowID := "ndc-handcrafted-reset-workflow-test" + uuid.New() runID := uuid.New() newRunID := uuid.New() workflowType := "event-generator-workflow-type" tasklist := "event-generator-taskList" identity := "worker-identity" // active has initial version 0 historyClient := s.active.GetHistoryClient() eventsBatch1 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 1, Version: 21, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1000), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1000), FirstDecisionTaskBackoffSeconds: common.Int32Ptr(100), }, }, { ID: 2, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 3, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 2, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 4, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 2, StartedEventID: 3, Identity: identity, }, }, { ID: 5, Version: 21, EventType: types.EventTypeMarkerRecorded.Ptr(), MarkerRecordedEventAttributes: &types.MarkerRecordedEventAttributes{ MarkerName: "some marker name", Details: []byte("some marker details"), DecisionTaskCompletedEventID: 4, }, }, { ID: 6, Version: 21, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ DecisionTaskCompletedEventID: 4, ActivityID: "0", ActivityType: &types.ActivityType{Name: "activity-type"}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(20), ScheduleToStartTimeoutSeconds: common.Int32Ptr(20), StartToCloseTimeoutSeconds: common.Int32Ptr(20), HeartbeatTimeoutSeconds: common.Int32Ptr(20), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 7, Version: 21, EventType: types.EventTypeActivityTaskStarted.Ptr(), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 6, Identity: identity, RequestID: uuid.New(), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 8, Version: 21, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some signal name 1", Input: []byte("some signal details 1"), Identity: identity, }, }, { ID: 9, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 10, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 9, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 11, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 9, StartedEventID: 10, Identity: identity, }, }, { ID: 12, Version: 21, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some signal name 2", Input: []byte("some signal details 2"), Identity: identity, }, }, { ID: 13, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, { ID: 14, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 13, Identity: identity, RequestID: uuid.New(), }, }, }}, } eventsBatch2 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 15, Version: 30, EventType: types.EventTypeDecisionTaskTimedOut.Ptr(), DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: 13, StartedEventID: 14, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, { ID: 16, Version: 30, EventType: types.EventTypeActivityTaskTimedOut.Ptr(), ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 6, StartedEventID: 7, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, { ID: 17, Version: 30, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 18, Version: 30, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 17, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 19, Version: 30, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 8, StartedEventID: 9, Identity: identity, }, }, { ID: 20, Version: 30, EventType: types.EventTypeWorkflowExecutionFailed.Ptr(), WorkflowExecutionFailedEventAttributes: &types.WorkflowExecutionFailedEventAttributes{ DecisionTaskCompletedEventID: 19, Reason: common.StringPtr("some random reason"), Details: nil, }, }, }}, } resetCause := types.DecisionTaskFailedCauseResetWorkflow dtFailedReason := "events-reapplication" eventsBatch3 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 11, Version: 22, EventType: types.EventTypeDecisionTaskFailed.Ptr(), DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ ScheduledEventID: 9, StartedEventID: 10, Identity: identity, Cause: &resetCause, Reason: &dtFailedReason, BaseRunID: runID, NewRunID: newRunID, ForkEventVersion: 21, }, }, }}, } versionHistory1 := s.eventBatchesToVersionHistory(nil, eventsBatch1) versionHistory2, err := versionHistory1.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(14, 21), ) s.NoError(err) versionHistory2 = s.eventBatchesToVersionHistory(versionHistory2, eventsBatch2) versionHistory3, err := versionHistory1.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(10, 21), ) s.NoError(err) versionHistory3 = s.eventBatchesToVersionHistory(versionHistory3, eventsBatch3) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory1, eventsBatch1, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory2, eventsBatch2, historyClient, ) s.applyEvents( workflowID, newRunID, workflowType, tasklist, versionHistory3, eventsBatch3, historyClient, ) } func (s *NDCIntegrationTestSuite) TestHandcraftedMultipleBranchesWithZombieContinueAsNew() { s.setupRemoteFrontendClients() workflowID := "ndc-handcrafted-multiple-branches-with-continue-as-new-test" + uuid.New() runID := uuid.New() workflowType := "event-generator-workflow-type" tasklist := "event-generator-taskList" identity := "worker-identity" // active has initial version 0 historyClient := s.active.GetHistoryClient() eventsBatch1 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 1, Version: 21, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1000), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1000), FirstDecisionTaskBackoffSeconds: common.Int32Ptr(100), }, }, { ID: 2, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 3, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 2, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 4, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 2, StartedEventID: 3, Identity: identity, }, }, { ID: 5, Version: 21, EventType: types.EventTypeMarkerRecorded.Ptr(), MarkerRecordedEventAttributes: &types.MarkerRecordedEventAttributes{ MarkerName: "some marker name", Details: []byte("some marker details"), DecisionTaskCompletedEventID: 4, }, }, { ID: 6, Version: 21, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ DecisionTaskCompletedEventID: 4, ActivityID: "0", ActivityType: &types.ActivityType{Name: "activity-type"}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(20), ScheduleToStartTimeoutSeconds: common.Int32Ptr(20), StartToCloseTimeoutSeconds: common.Int32Ptr(20), HeartbeatTimeoutSeconds: common.Int32Ptr(20), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 7, Version: 21, EventType: types.EventTypeActivityTaskStarted.Ptr(), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 6, Identity: identity, RequestID: uuid.New(), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 8, Version: 21, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some signal name 1", Input: []byte("some signal details 1"), Identity: identity, }, }, { ID: 9, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 10, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 9, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 11, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 9, StartedEventID: 10, Identity: identity, }, }, { ID: 12, Version: 21, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some signal name 2", Input: []byte("some signal details 2"), Identity: identity, }, }, { ID: 13, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, { ID: 14, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 13, Identity: identity, RequestID: uuid.New(), }, }, }}, } eventsBatch2 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 15, Version: 32, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 8, StartedEventID: 9, Identity: identity, }, }, }}, // need to keep the workflow open for testing } eventsBatch3 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 15, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 8, StartedEventID: 9, Identity: identity, }, }, { ID: 16, Version: 21, EventType: types.EventTypeWorkflowExecutionContinuedAsNew.Ptr(), WorkflowExecutionContinuedAsNewEventAttributes: &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: uuid.New(), WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1000), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1000), DecisionTaskCompletedEventID: 19, Initiator: types.ContinueAsNewInitiatorDecider.Ptr(), }, }, }}, } versionHistory1 := s.eventBatchesToVersionHistory(nil, eventsBatch1) versionHistory2, err := versionHistory1.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(14, 21), ) s.NoError(err) versionHistory2 = s.eventBatchesToVersionHistory(versionHistory2, eventsBatch2) versionHistory3, err := versionHistory1.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(14, 21), ) s.NoError(err) versionHistory3 = s.eventBatchesToVersionHistory(versionHistory3, eventsBatch3) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory1, eventsBatch1, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory2, eventsBatch2, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory3, eventsBatch3, historyClient, ) } func (s *NDCIntegrationTestSuite) TestEventsReapply_ZombieWorkflow() { workflowID := "ndc-single-branch-test" + uuid.New() workflowType := "event-generator-workflow-type" tasklist := "event-generator-taskList" // active has initial version 0 historyClient := s.active.GetHistoryClient() version := int64(101) runID := uuid.New() historyBatch := []*types.History{} s.generator = test.InitializeHistoryEventGenerator(s.domainName, version) for s.generator.HasNextVertex() { events := s.generator.GetNextVertices() historyEvents := &types.History{} for _, event := range events { historyEvents.Events = append(historyEvents.Events, event.GetData().(*types.HistoryEvent)) } historyBatch = append(historyBatch, historyEvents) } versionHistory := s.eventBatchesToVersionHistory(nil, historyBatch) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory, historyBatch, historyClient, ) version = int64(1) runID = uuid.New() historyBatch = []*types.History{} s.generator = test.InitializeHistoryEventGenerator(s.domainName, version) // verify two batches of zombie workflow are call reapply API s.mockAdminClient["standby"].(*adminClient.MockClient).EXPECT().ReapplyEvents(gomock.Any(), gomock.Any()).Return(nil).Times(2) for i := 0; i < 2 && s.generator.HasNextVertex(); i++ { events := s.generator.GetNextVertices() historyEvents := &types.History{} for _, event := range events { historyEvents.Events = append(historyEvents.Events, event.GetData().(*types.HistoryEvent)) } historyBatch = append(historyBatch, historyEvents) } versionHistory = s.eventBatchesToVersionHistory(nil, historyBatch) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory, historyBatch, historyClient, ) } func (s *NDCIntegrationTestSuite) TestEventsReapply_UpdateNonCurrentBranch() { workflowID := "ndc-single-branch-test" + uuid.New() runID := uuid.New() workflowType := "event-generator-workflow-type" tasklist := "event-generator-taskList" version := int64(101) isWorkflowFinished := false historyClient := s.active.GetHistoryClient() s.generator = test.InitializeHistoryEventGenerator(s.domainName, version) baseBranch := []*types.History{} var taskID int64 for i := 0; i < 4 && s.generator.HasNextVertex(); i++ { events := s.generator.GetNextVertices() historyEvents := &types.History{} for _, event := range events { historyEvent := event.GetData().(*types.HistoryEvent) taskID = historyEvent.TaskID historyEvents.Events = append(historyEvents.Events, historyEvent) switch historyEvent.GetEventType() { case types.EventTypeWorkflowExecutionCompleted, types.EventTypeWorkflowExecutionFailed, types.EventTypeWorkflowExecutionTimedOut, types.EventTypeWorkflowExecutionTerminated, types.EventTypeWorkflowExecutionContinuedAsNew, types.EventTypeWorkflowExecutionCanceled: isWorkflowFinished = true } } baseBranch = append(baseBranch, historyEvents) } if isWorkflowFinished { // cannot proceed since the test below requires workflow not finished // this is ok since build kite will run this test several times s.logger.Info("Encounter finish workflow history event during randomization test, skip") return } versionHistory := s.eventBatchesToVersionHistory(nil, baseBranch) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory, baseBranch, historyClient, ) newGenerator := s.generator.DeepCopy() newBranch := []*types.History{} newVersionHistory := versionHistory.Duplicate() newGenerator.SetVersion(newGenerator.GetVersion() + 1) // simulate events from other cluster for i := 0; i < 4 && newGenerator.HasNextVertex(); i++ { events := newGenerator.GetNextVertices() historyEvents := &types.History{} for _, event := range events { history := event.GetData().(*types.HistoryEvent) taskID = history.TaskID historyEvents.Events = append(historyEvents.Events, history) } newBranch = append(newBranch, historyEvents) } newVersionHistory = s.eventBatchesToVersionHistory(newVersionHistory, newBranch) s.applyEvents( workflowID, runID, workflowType, tasklist, newVersionHistory, newBranch, historyClient, ) s.mockAdminClient["standby"].(*adminClient.MockClient).EXPECT().ReapplyEvents(gomock.Any(), gomock.Any()).Return(nil).Times(1) // Handcraft a stale signal event baseBranchLastEventBatch := baseBranch[len(baseBranch)-1].GetEvents() baseBranchLastEvent := baseBranchLastEventBatch[len(baseBranchLastEventBatch)-1] staleBranch := []*types.History{ { Events: []*types.HistoryEvent{ { ID: baseBranchLastEvent.ID + 1, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), Timestamp: common.Int64Ptr(time.Now().UnixNano()), Version: baseBranchLastEvent.Version, // dummy event from other cluster TaskID: taskID, WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "signal", Input: []byte{}, Identity: "ndc_integration_test", }, }, }, }, } staleVersionHistory := s.eventBatchesToVersionHistory(versionHistory.Duplicate(), staleBranch) s.applyEvents( workflowID, runID, workflowType, tasklist, staleVersionHistory, staleBranch, historyClient, ) } func (s *NDCIntegrationTestSuite) TestAdminGetWorkflowExecutionRawHistoryV2() { workflowID := "ndc-re-send-test" + uuid.New() runID := uuid.New() workflowType := "ndc-re-send-workflow-type" tasklist := "event-generator-taskList" identity := "ndc-re-send-test" historyClient := s.active.GetHistoryClient() adminClient := s.active.GetAdminClient() getHistory := func( domain string, workflowID string, runID string, startEventID *int64, startEventVersion *int64, endEventID *int64, endEventVersion *int64, pageSize int, token []byte, ) (*types.GetWorkflowExecutionRawHistoryV2Response, error) { execution := &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, } ctx, cancel := s.createContext() defer cancel() return adminClient.GetWorkflowExecutionRawHistoryV2(ctx, &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: domain, Execution: execution, StartEventID: startEventID, StartEventVersion: startEventVersion, EndEventID: endEventID, EndEventVersion: endEventVersion, MaximumPageSize: int32(pageSize), NextPageToken: token, }) } eventsBatch1 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 1, Version: 21, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1000), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1000), FirstDecisionTaskBackoffSeconds: common.Int32Ptr(100), }, }, { ID: 2, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 3, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 2, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 4, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 2, StartedEventID: 3, Identity: identity, }, }, { ID: 5, Version: 21, EventType: types.EventTypeMarkerRecorded.Ptr(), MarkerRecordedEventAttributes: &types.MarkerRecordedEventAttributes{ MarkerName: "some marker name", Details: []byte("some marker details"), DecisionTaskCompletedEventID: 4, }, }, { ID: 6, Version: 21, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ DecisionTaskCompletedEventID: 4, ActivityID: "0", ActivityType: &types.ActivityType{Name: "activity-type"}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(20), ScheduleToStartTimeoutSeconds: common.Int32Ptr(20), StartToCloseTimeoutSeconds: common.Int32Ptr(20), HeartbeatTimeoutSeconds: common.Int32Ptr(20), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 7, Version: 21, EventType: types.EventTypeActivityTaskStarted.Ptr(), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 6, Identity: identity, RequestID: uuid.New(), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 8, Version: 21, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some signal name 1", Input: []byte("some signal details 1"), Identity: identity, }, }, { ID: 9, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 10, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 9, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 11, Version: 21, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 9, StartedEventID: 10, Identity: identity, }, }, { ID: 12, Version: 21, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some signal name 2", Input: []byte("some signal details 2"), Identity: identity, }, }, { ID: 13, Version: 21, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, { ID: 14, Version: 21, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 13, Identity: identity, RequestID: uuid.New(), }, }, }}, } eventsBatch2 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 15, Version: 31, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 9, StartedEventID: 10, Identity: identity, }, }, { ID: 16, Version: 31, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ DecisionTaskCompletedEventID: 4, ActivityID: "0", ActivityType: &types.ActivityType{Name: "activity-type"}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(20), ScheduleToStartTimeoutSeconds: common.Int32Ptr(20), StartToCloseTimeoutSeconds: common.Int32Ptr(20), HeartbeatTimeoutSeconds: common.Int32Ptr(20), }, }, }}, } eventsBatch3 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 15, Version: 30, EventType: types.EventTypeDecisionTaskTimedOut.Ptr(), DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: 13, StartedEventID: 14, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, { ID: 16, Version: 30, EventType: types.EventTypeActivityTaskTimedOut.Ptr(), ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 6, StartedEventID: 7, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, { ID: 17, Version: 30, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(1000), Attempt: 0, }, }, }}, {Events: []*types.HistoryEvent{ { ID: 18, Version: 30, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 17, Identity: identity, RequestID: uuid.New(), }, }, }}, {Events: []*types.HistoryEvent{ { ID: 19, Version: 30, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 8, StartedEventID: 9, Identity: identity, }, }, { ID: 20, Version: 30, EventType: types.EventTypeWorkflowExecutionFailed.Ptr(), WorkflowExecutionFailedEventAttributes: &types.WorkflowExecutionFailedEventAttributes{ DecisionTaskCompletedEventID: 19, Reason: common.StringPtr("some random reason"), Details: nil, }, }, }}, } eventsBatch4 := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 17, Version: 32, EventType: types.EventTypeWorkflowExecutionTimedOut.Ptr(), WorkflowExecutionTimedOutEventAttributes: &types.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, }}, } versionHistory1 := s.eventBatchesToVersionHistory(nil, eventsBatch1) versionHistory2, err := versionHistory1.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(14, 21), ) s.NoError(err) versionHistory2 = s.eventBatchesToVersionHistory(versionHistory2, eventsBatch2) versionHistory3, err := versionHistory1.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(14, 21), ) s.NoError(err) versionHistory3 = s.eventBatchesToVersionHistory(versionHistory3, eventsBatch3) versionHistory4, err := versionHistory2.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(16, 31), ) s.NoError(err) versionHistory4 = s.eventBatchesToVersionHistory(versionHistory4, eventsBatch4) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory1, eventsBatch1, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory3, eventsBatch3, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory2, eventsBatch2, historyClient, ) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory4, eventsBatch4, historyClient, ) // GetWorkflowExecutionRawHistoryV2 start and end var token []byte batchCount := 0 for continuePaging := true; continuePaging; continuePaging = len(token) != 0 { resp, err := getHistory( s.domainName, workflowID, runID, common.Int64Ptr(14), common.Int64Ptr(21), common.Int64Ptr(20), common.Int64Ptr(30), 1, token, ) s.Nil(err) s.True(len(resp.HistoryBatches) <= 1) batchCount++ token = resp.NextPageToken } s.Equal(batchCount, 4) // GetWorkflowExecutionRawHistoryV2 start and end not on the same branch token = nil batchCount = 0 for continuePaging := true; continuePaging; continuePaging = len(token) != 0 { resp, err := getHistory( s.domainName, workflowID, runID, common.Int64Ptr(17), common.Int64Ptr(30), common.Int64Ptr(17), common.Int64Ptr(32), 1, token, ) s.Nil(err) s.True(len(resp.HistoryBatches) <= 1) batchCount++ token = resp.NextPageToken } s.Equal(batchCount, 2) // GetWorkflowExecutionRawHistoryV2 start boundary token = nil batchCount = 0 for continuePaging := true; continuePaging; continuePaging = len(token) != 0 { resp, err := getHistory( s.domainName, workflowID, runID, common.Int64Ptr(14), common.Int64Ptr(21), nil, nil, 1, token, ) s.Nil(err) s.True(len(resp.HistoryBatches) <= 1) batchCount++ token = resp.NextPageToken } s.Equal(batchCount, 3) // GetWorkflowExecutionRawHistoryV2 end boundary token = nil batchCount = 0 for continuePaging := true; continuePaging; continuePaging = len(token) != 0 { resp, err := getHistory( s.domainName, workflowID, runID, nil, nil, common.Int64Ptr(17), common.Int64Ptr(32), 1, token, ) s.Nil(err) s.True(len(resp.HistoryBatches) <= 1) batchCount++ token = resp.NextPageToken } s.Equal(batchCount, 10) } func (s *NDCIntegrationTestSuite) TestWorkflowStartTime() { s.setupRemoteFrontendClients() workflowID := "ndc-workflow-start-time-test" + uuid.New() workflowType := "start-time-test-workflow-type" tasklist := "start-time-test-tasklist" startTime := time.Now().Add(-time.Minute) runID := uuid.New() historyBatch := []*types.History{ {Events: []*types.HistoryEvent{ { ID: 1, Timestamp: common.Int64Ptr(startTime.UnixNano()), Version: 21, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1000), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1000), FirstDecisionTaskBackoffSeconds: common.Int32Ptr(100), }, }, }, }, } versionHistory := s.eventBatchesToVersionHistory(nil, historyBatch) s.applyEvents( workflowID, runID, workflowType, tasklist, versionHistory, historyBatch, s.active.GetHistoryClient(), ) err := s.verifyEventHistory(workflowID, runID, historyBatch) s.Require().NoError(err) // we are replicating to the `active` cluster, check the comments in // registerDomain() method below ctx, cancel := s.createContext() defer cancel() descResp, err := s.active.GetFrontendClient().DescribeWorkflowExecution( ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, }, ) s.NoError(err) s.WithinDuration( startTime, time.Unix(0, descResp.WorkflowExecutionInfo.GetStartTime()), time.Millisecond, ) } func (s *NDCIntegrationTestSuite) RegisterDomain() { s.domainName = "test-simple-workflow-ndc-" + common.GenerateRandomString(5) client1 := s.active.GetFrontendClient() // active ctx, cancel := s.createContext() defer cancel() err := client1.RegisterDomain(ctx, &types.RegisterDomainRequest{ Name: s.domainName, IsGlobalDomain: true, Clusters: clusterReplicationConfig, // make the active cluster `standby` and replicate to `active` cluster ActiveClusterName: clusterName[1], WorkflowExecutionRetentionPeriodInDays: 1, }) s.Require().NoError(err) descReq := &types.DescribeDomainRequest{ Name: common.StringPtr(s.domainName), } ctx, cancel = s.createContext() defer cancel() resp, err := client1.DescribeDomain(ctx, descReq) s.Require().NoError(err) s.Require().NotNil(resp) s.domainID = resp.GetDomainInfo().GetUUID() // Wait for domain cache to pick the change time.Sleep(2 * cache.DomainCacheRefreshInterval) s.logger.Info(fmt.Sprintf("Domain name: %v - ID: %v", s.domainName, s.domainID)) } func (s *NDCIntegrationTestSuite) generateNewRunHistory( event *types.HistoryEvent, domain string, workflowID string, runID string, version int64, workflowType string, taskList string, ) *persistence.DataBlob { // TODO temporary code to generate first event & version history // we should generate these as part of modeled based testing if event.GetWorkflowExecutionContinuedAsNewEventAttributes() == nil { return nil } event.WorkflowExecutionContinuedAsNewEventAttributes.NewExecutionRunID = uuid.New() firstScheduleTime := time.Unix(0, 100) newRunFirstEvent := &types.HistoryEvent{ ID: constants.FirstEventID, Timestamp: common.Int64Ptr(time.Now().UnixNano()), EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), Version: version, TaskID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: workflowType}, ParentWorkflowDomain: common.StringPtr(domain), ParentWorkflowExecution: &types.WorkflowExecution{ WorkflowID: uuid.New(), RunID: uuid.New(), }, ParentInitiatedEventID: common.Int64Ptr(event.ID), TaskList: &types.TaskList{ Name: taskList, Kind: types.TaskListKindNormal.Ptr(), }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(10), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), ContinuedExecutionRunID: runID, Initiator: types.ContinueAsNewInitiatorCronSchedule.Ptr(), OriginalExecutionRunID: runID, Identity: "NDC-test", FirstExecutionRunID: runID, FirstScheduleTime: &firstScheduleTime, Attempt: 0, ExpirationTimestamp: common.Int64Ptr(time.Now().Add(time.Minute).UnixNano()), }, } eventBlob, err := s.serializer.SerializeBatchEvents([]*types.HistoryEvent{newRunFirstEvent}, constants.EncodingTypeThriftRW) s.NoError(err) return eventBlob } func (s *NDCIntegrationTestSuite) toInternalDataBlob( blob *persistence.DataBlob, ) *types.DataBlob { if blob == nil { return nil } var encodingType types.EncodingType switch blob.GetEncoding() { case constants.EncodingTypeThriftRW: encodingType = types.EncodingTypeThriftRW case constants.EncodingTypeJSON, constants.EncodingTypeGob, constants.EncodingTypeUnknown, constants.EncodingTypeEmpty: panic(fmt.Sprintf("unsupported encoding type: %v", blob.GetEncoding())) default: panic(fmt.Sprintf("unknown encoding type: %v", blob.GetEncoding())) } return &types.DataBlob{ EncodingType: encodingType.Ptr(), Data: blob.Data, } } func (s *NDCIntegrationTestSuite) generateEventBlobs( workflowID string, runID string, workflowType string, tasklist string, batch *types.History, ) (*persistence.DataBlob, *persistence.DataBlob) { // TODO temporary code to generate next run first event // we should generate these as part of modeled based testing lastEvent := batch.Events[len(batch.Events)-1] newRunEventBlob := s.generateNewRunHistory( lastEvent, s.domainName, workflowID, runID, lastEvent.Version, workflowType, tasklist, ) // must serialize events batch after attempt on continue as new as generateNewRunHistory will // modify the NewExecutionRunID attr eventBlob, err := s.serializer.SerializeBatchEvents(batch.Events, constants.EncodingTypeThriftRW) s.NoError(err) return eventBlob, newRunEventBlob } func (s *NDCIntegrationTestSuite) applyEvents( workflowID string, runID string, workflowType string, tasklist string, versionHistory *persistence.VersionHistory, eventBatches []*types.History, historyClient host.HistoryClient, ) { for _, batch := range eventBatches { eventBlob, newRunEventBlob := s.generateEventBlobs(workflowID, runID, workflowType, tasklist, batch) req := &types.ReplicateEventsV2Request{ DomainUUID: s.domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, VersionHistoryItems: s.toInternalVersionHistoryItems(versionHistory), Events: s.toInternalDataBlob(eventBlob), NewRunEvents: s.toInternalDataBlob(newRunEventBlob), } ctx, cancel := s.createContext() err := historyClient.ReplicateEventsV2(ctx, req) cancel() s.Nil(err, "Failed to replicate history event") ctx, cancel = s.createContext() err = historyClient.ReplicateEventsV2(ctx, req) cancel() s.Nil(err, "Failed to dedup replicate history event") } } func (s *NDCIntegrationTestSuite) applyEventsThroughFetcher( workflowID string, runID string, workflowType string, tasklist string, versionHistory *persistence.VersionHistory, eventBatches []*types.History, ) { for _, batch := range eventBatches { eventBlob, newRunEventBlob := s.generateEventBlobs(workflowID, runID, workflowType, tasklist, batch) taskType := types.ReplicationTaskTypeHistoryV2 replicationTask := &types.ReplicationTask{ TaskType: &taskType, SourceTaskID: 1, HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: s.domainID, WorkflowID: workflowID, RunID: runID, VersionHistoryItems: s.toInternalVersionHistoryItems(versionHistory), Events: s.toInternalDataBlob(eventBlob), NewRunEvents: s.toInternalDataBlob(newRunEventBlob), }, } s.standByReplicationTasksChan <- replicationTask // this is to test whether dedup works s.standByReplicationTasksChan <- replicationTask } } func (s *NDCIntegrationTestSuite) eventBatchesToVersionHistory( versionHistory *persistence.VersionHistory, eventBatches []*types.History, ) *persistence.VersionHistory { // TODO temporary code to generate version history // we should generate version as part of modeled based testing if versionHistory == nil { versionHistory = persistence.NewVersionHistory(nil, nil) } for _, batch := range eventBatches { for _, event := range batch.Events { err := versionHistory.AddOrUpdateItem( persistence.NewVersionHistoryItem( event.ID, event.Version, )) s.NoError(err) } } return versionHistory } func (s *NDCIntegrationTestSuite) toInternalVersionHistoryItems( versionHistory *persistence.VersionHistory, ) []*types.VersionHistoryItem { if versionHistory == nil { return nil } return versionHistory.ToInternalType().Items } func (s *NDCIntegrationTestSuite) createContext() (context.Context, context.CancelFunc) { ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) return ctx, cancel } func (s *NDCIntegrationTestSuite) setupRemoteFrontendClients() { s.mockAdminClient["standby"].(*adminClient.MockClient).EXPECT().ReapplyEvents(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() s.mockAdminClient["other"].(*adminClient.MockClient).EXPECT().ReapplyEvents(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() } ================================================ FILE: host/ndc/replication_integration_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( "context" "math" "reflect" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common/persistence" test "github.com/uber/cadence/common/testing" "github.com/uber/cadence/common/types" ) const ( defaultTestPersistenceTimeout = 5 * time.Second ) func (s *NDCIntegrationTestSuite) TestReplicationMessageApplication() { workflowID := "replication-message-test" + uuid.New() runID := uuid.New() workflowType := "event-generator-workflow-type" tasklist := "event-generator-taskList" var historyBatch []*types.History s.generator = test.InitializeHistoryEventGenerator(s.domainName, 1) for s.generator.HasNextVertex() { events := s.generator.GetNextVertices() historyEvents := &types.History{} for _, event := range events { historyEvents.Events = append(historyEvents.Events, event.GetData().(*types.HistoryEvent)) } historyBatch = append(historyBatch, historyEvents) } versionHistory := s.eventBatchesToVersionHistory(nil, historyBatch) s.applyEventsThroughFetcher( workflowID, runID, workflowType, tasklist, versionHistory, historyBatch, ) // Applying replication messages through fetcher is Async. // So we need to retry a couple of times. for i := 0; i < 10; i++ { time.Sleep(time.Second) err := s.verifyEventHistory(workflowID, runID, historyBatch) if err == nil { return } } s.Fail("Verification of replicated messages failed") } func (s *NDCIntegrationTestSuite) TestReplicationMessageDLQ() { workflowID := "replication-message-dlq-test" + uuid.New() runID := uuid.New() workflowType := "event-generator-workflow-type" tasklist := "event-generator-taskList" var historyBatch []*types.History s.generator = test.InitializeHistoryEventGenerator(s.domainName, 1) events := s.generator.GetNextVertices() historyEvents := &types.History{} for _, event := range events { historyEvents.Events = append(historyEvents.Events, event.GetData().(*types.HistoryEvent)) } historyBatch = append(historyBatch, historyEvents) versionHistory := s.eventBatchesToVersionHistory(nil, historyBatch) s.NotNil(historyBatch) historyBatch[0].Events[1].Version = 2 s.applyEventsThroughFetcher( workflowID, runID, workflowType, tasklist, versionHistory, historyBatch, ) execMgrFactory := s.active.GetExecutionManagerFactory() executionManager, err := execMgrFactory.NewExecutionManager(0) s.NoError(err) expectedDLQMsgs := map[int64]bool{} for _, batch := range historyBatch { firstEventID := batch.Events[0].ID expectedDLQMsgs[firstEventID] = true } // Applying replication messages through fetcher is Async. // So we need to retry a couple of times. Loop: for i := 0; i < 60; i++ { time.Sleep(time.Second) actualDLQMsgs := map[int64]bool{} request := persistence.NewGetReplicationTasksFromDLQRequest( "standby", 0, math.MaxInt64, math.MaxInt64, nil, ) var token []byte for doPaging := true; doPaging; doPaging = len(token) > 0 { request.NextPageToken = token ctx, cancel := context.WithTimeout(context.Background(), defaultTestPersistenceTimeout) response, err := executionManager.GetReplicationTasksFromDLQ(ctx, request) cancel() if err != nil { continue Loop } token = response.NextPageToken for _, task := range response.Tasks { t, ok := task.(*persistence.HistoryReplicationTask) s.True(ok, "task is not a HistoryReplicationTask") firstEventID := t.FirstEventID actualDLQMsgs[firstEventID] = true } } if reflect.DeepEqual(expectedDLQMsgs, actualDLQMsgs) { return } } s.Fail("Failed to get messages from DLQ.") } ================================================ FILE: host/ndc/test_suites.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/persistence-tests/testcluster" "github.com/uber/cadence/common/testing" "github.com/uber/cadence/common/types" "github.com/uber/cadence/host" ) // NOTE: the following definitions can't be defined in *_test.go // since they need to be exported and used by our internal tests type ( NDCIntegrationTestSuite struct { // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions suite.Suite active *host.TestCluster generator testing.Generator serializer persistence.PayloadSerializer logger log.Logger domainName string domainID string version int64 versionIncrement int64 mockAdminClient map[string]admin.Client standByReplicationTasksChan chan *types.ReplicationTask standByTaskID int64 clusterConfigs []*host.TestClusterConfig defaultTestCluster testcluster.PersistenceTestCluster VisibilityTestCluster testcluster.PersistenceTestCluster } NDCIntegrationTestSuiteParams struct { ClusterConfigs []*host.TestClusterConfig DefaultTestCluster testcluster.PersistenceTestCluster VisibilityTestCluster testcluster.PersistenceTestCluster } ) func NewNDCIntegrationTestSuite(params NDCIntegrationTestSuiteParams) *NDCIntegrationTestSuite { return &NDCIntegrationTestSuite{ clusterConfigs: params.ClusterConfigs, defaultTestCluster: params.DefaultTestCluster, VisibilityTestCluster: params.VisibilityTestCluster, } } ================================================ FILE: host/onebox.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "context" "encoding/json" "fmt" "sync" "time" "github.com/pborman/uuid" "github.com/uber-go/tally" "github.com/uber-go/tally/prometheus" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" cwsc "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/compatibility" "go.uber.org/yarpc" "go.uber.org/yarpc/api/transport" adminClient "github.com/uber/cadence/client/admin" frontendClient "github.com/uber/cadence/client/frontend" historyClient "github.com/uber/cadence/client/history" matchingClient "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" carchiver "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/authorization" "github.com/uber/cadence/common/cache" cc "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/isolationgroup/isolationgroupapi" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/wrappers/metered" "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend" "github.com/uber/cadence/service/history" "github.com/uber/cadence/service/matching" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" sdconfig "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/worker" "github.com/uber/cadence/service/worker/archiver" "github.com/uber/cadence/service/worker/asyncworkflow" "github.com/uber/cadence/service/worker/indexer" "github.com/uber/cadence/service/worker/replicator" ) // Cadence hosts all of cadence services in one process type Cadence interface { Start() error Stop() GetAdminClient() adminClient.Client GetFrontendClient() frontendClient.Client FrontendHost() membership.HostInfo GetHistoryClient() historyClient.Client GetMatchingClient() matchingClient.Client GetMatchingClients() []matchingClient.Client GetExecutionManagerFactory() persistence.ExecutionManagerFactory } type ( cadenceImpl struct { frontendService common.Daemon matchingServices []common.Daemon historyServices []common.Daemon adminClient adminClient.Client frontendClient frontendClient.Client historyClient historyClient.Client matchingClients []matchingClient.Client logger log.Logger clusterMetadata cluster.Metadata persistenceConfig config.Persistence messagingClient messaging.Client domainManager persistence.DomainManager historyV2Mgr persistence.HistoryManager executionMgrFactory persistence.ExecutionManagerFactory domainReplicationQueue domain.ReplicationQueue shutdownCh chan struct{} shutdownWG sync.WaitGroup clusterNo int // cluster number replicator *replicator.Replicator clientWorker archiver.ClientWorker indexer *indexer.Indexer archiverMetadata carchiver.ArchivalMetadata archiverProvider provider.ArchiverProvider historyConfig *HistoryConfig matchingConfig *MatchingConfig esConfig *config.ElasticSearchConfig esClient elasticsearch.GenericClient workerConfig *WorkerConfig mockAdminClient map[string]adminClient.Client domainReplicationTaskExecutor domain.ReplicationTaskExecutor authorizationConfig config.Authorization pinotConfig *config.PinotVisibilityConfig pinotClient pinot.GenericClient asyncWFQueues map[string]config.AsyncWorkflowQueueProvider timeSource clock.TimeSource dynamicClient dynamicconfig.Client // dynamicconfig overrides per service frontendDynCfgOverrides map[dynamicproperties.Key]interface{} historyDynCfgOverrides map[dynamicproperties.Key]interface{} matchingDynCfgOverrides map[dynamicproperties.Key]interface{} workerDynCfgOverrides map[dynamicproperties.Key]interface{} } // HistoryConfig contains configs for history service HistoryConfig struct { // When MockClient is set, rest of the configs are ignored, history service is not started // and mock history client is passed to other services MockClient HistoryClient NumHistoryShards int NumHistoryHosts int HistoryCountLimitError int HistoryCountLimitWarn int SimulationConfig HistorySimulationConfig } HistorySimulationConfig struct { NumWorkflows int // WorkflowDeletionJitterRange defines the duration in minutes for workflow close tasks jittering // defaults to 0 to remove jittering WorkflowDeletionJitterRange int // EnableTransferQueueV2 enables queue v2 framework for transfer queue EnableTransferQueueV2 bool // EnableTimerQueueV2 enables queue v2 framework for timer queue EnableTimerQueueV2 bool } MatchingConfig struct { // number of matching host can be at most 4 due to existing static port assignments in onebox.go. // can be changed easily. NumMatchingHosts int SimulationConfig MatchingSimulationConfig } MatchingSimulationConfig struct { // Number of task list write partitions defaults to 1 TaskListWritePartitions int // Number of task list read partitions defaults to 1 TaskListReadPartitions int // At most N polls will be forwarded at a time. defaults to 20 ForwarderMaxOutstandingPolls int // At most N tasks will be forwarded at a time. defaults to 1 ForwarderMaxOutstandingTasks int // Forwarder rps limit defaults to 10 ForwarderMaxRatePerSecond int // Children per node. defaults to 20 ForwarderMaxChildrenPerNode int // LocalPollWaitTime. defaults to 0ms. LocalPollWaitTime time.Duration // LocalTaskWaitTime. defaults to 0ms. LocalTaskWaitTime time.Duration // RecordDecisionTaskStartedTime. The amount of time spent by History to complete RecordDecisionTaskStarted RecordDecisionTaskStartedTime time.Duration // TasklistLoadBalancerStrategy the strategy of load balancer. defaults to "random". TasklistLoadBalancerStrategy string // The pollers that will be created to process Pollers []SimulationPollerConfiguration Tasks []SimulationTaskConfiguration Backlogs []SimulationBacklogConfiguration // GetPartitionConfigFromDB indicates whether to get the partition config from DB or not. // This is a prerequisite for adaptive scaler. GetPartitionConfigFromDB bool // Adaptive scaler configurations EnableAdaptiveScaler bool PartitionDownscaleFactor float64 PartitionUpscaleRPS int PartitionUpscaleSustainedDuration time.Duration PartitionDownscaleSustainedDuration time.Duration AdaptiveScalerUpdateInterval time.Duration QPSTrackerInterval time.Duration TaskIsolationDuration time.Duration } SimulationPollerConfiguration struct { // The isolation group that pollers will be created with. Optional. IsolationGroup string // The number of pollers that will be created with this configuration. Defaults to 1 NumPollers int // TaskProcessTime. The amount of time spent by the poller in-between requests. Defaults to 1ms TaskProcessTime time.Duration // Poll request timeout defaults to 15 seconds PollTimeout time.Duration } SimulationTaskConfiguration struct { // The isolation groups that tasks will be evenly distributed between IsolationGroups []string // Number of task generators defaults to 1 NumTaskGenerators int // Upper limit of tasks to generate. Task generators will stop if total number of tasks generated reaches MaxTaskToGenerate during simulation // Defaults to 2k MaxTaskToGenerate int // Task generation QPS. Defaults to 40. TasksPerSecond int // The burst value for the rate limiter for task generation. Controls the maximum number of AddTask requests // that can be sent concurrently. For example, if you have TasksPerSecond, TasksBurst, and NumTaskGenerators all // set to 10 then every second you'll get 10 tasks added right at the start of the second. If you instead set // TasksBurst to 1 then you'd get a steady stream of tasks, with one task every 100ms. TasksBurst int // OverTime is a list of TasksProduceSpec that will be used to change the qps over time. // Each item has a duration and they will be applied in the given order. // If this is set, TasksPerSecond and TasksBurst will be ignored. OverTime []TasksProduceSpec } TasksProduceSpec struct { // Task generation qps TasksPerSecond int // The burst value for the rate limiter for task generation. TasksBurst int // The duration for which the settings will be applied. // If the duration is unset, the settings will be applied indefinitely. Duration *time.Duration } SimulationBacklogConfiguration struct { // The partition number Partition int // Do not set it to 0, because it's not guaranteed to add backlog to partition 0 // The backlog count BacklogCount int // The weight of each isolation group, can be empty IsolationGroups map[string]int } // CadenceParams contains everything needed to bootstrap Cadence CadenceParams struct { ClusterMetadata cluster.Metadata PersistenceConfig config.Persistence MessagingClient messaging.Client DomainManager persistence.DomainManager HistoryV2Mgr persistence.HistoryManager ExecutionMgrFactory persistence.ExecutionManagerFactory DomainReplicationQueue domain.ReplicationQueue Logger log.Logger ClusterNo int ArchiverMetadata carchiver.ArchivalMetadata ArchiverProvider provider.ArchiverProvider EnableReadHistoryFromArchival bool HistoryConfig *HistoryConfig MatchingConfig *MatchingConfig ESConfig *config.ElasticSearchConfig ESClient elasticsearch.GenericClient WorkerConfig *WorkerConfig MockAdminClient map[string]adminClient.Client DomainReplicationTaskExecutor domain.ReplicationTaskExecutor AuthorizationConfig config.Authorization PinotConfig *config.PinotVisibilityConfig PinotClient pinot.GenericClient AsyncWFQueues map[string]config.AsyncWorkflowQueueProvider TimeSource clock.TimeSource DynamicClient dynamicconfig.Client FrontendDynCfgOverrides map[dynamicproperties.Key]interface{} HistoryDynCfgOverrides map[dynamicproperties.Key]interface{} MatchingDynCfgOverrides map[dynamicproperties.Key]interface{} WorkerDynCfgOverrides map[dynamicproperties.Key]interface{} } ) // NewCadence returns an instance that hosts full cadence in one process func NewCadence(params *CadenceParams) Cadence { return &cadenceImpl{ logger: params.Logger, clusterMetadata: params.ClusterMetadata, persistenceConfig: params.PersistenceConfig, messagingClient: params.MessagingClient, domainManager: params.DomainManager, historyV2Mgr: params.HistoryV2Mgr, executionMgrFactory: params.ExecutionMgrFactory, domainReplicationQueue: params.DomainReplicationQueue, shutdownCh: make(chan struct{}), clusterNo: params.ClusterNo, esConfig: params.ESConfig, esClient: params.ESClient, archiverMetadata: params.ArchiverMetadata, archiverProvider: params.ArchiverProvider, historyConfig: params.HistoryConfig, matchingConfig: params.MatchingConfig, workerConfig: params.WorkerConfig, mockAdminClient: params.MockAdminClient, domainReplicationTaskExecutor: params.DomainReplicationTaskExecutor, authorizationConfig: params.AuthorizationConfig, pinotConfig: params.PinotConfig, pinotClient: params.PinotClient, asyncWFQueues: params.AsyncWFQueues, timeSource: params.TimeSource, dynamicClient: params.DynamicClient, frontendDynCfgOverrides: params.FrontendDynCfgOverrides, historyDynCfgOverrides: params.HistoryDynCfgOverrides, matchingDynCfgOverrides: params.MatchingDynCfgOverrides, workerDynCfgOverrides: params.WorkerDynCfgOverrides, } } func (c *cadenceImpl) enableWorker() bool { return c.workerConfig.EnableArchiver || c.workerConfig.EnableIndexer || c.workerConfig.EnableReplicator || c.workerConfig.EnableAsyncWFConsumer } func (c *cadenceImpl) Start() error { hosts := make(map[string][]membership.HostInfo) hosts[service.Frontend] = []membership.HostInfo{c.FrontendHost()} hosts[service.Matching] = c.MatchingHosts() hosts[service.History] = c.HistoryHosts() if c.enableWorker() { hosts[service.Worker] = []membership.HostInfo{c.WorkerServiceHost()} } // create cadence-system domain, this must be created before starting // the services - so directly use the metadataManager to create this if err := c.createSystemDomain(); err != nil { return err } var startWG sync.WaitGroup startWG.Add(1) go c.startHistory(hosts, &startWG) startWG.Wait() startWG.Add(1) go c.startMatching(hosts, &startWG) startWG.Wait() startWG.Add(1) go c.startFrontend(hosts, &startWG) startWG.Wait() if c.enableWorker() { startWG.Add(1) go c.startWorker(hosts, &startWG) startWG.Wait() } return nil } func (c *cadenceImpl) Stop() { serviceCount := 3 if c.enableWorker() { serviceCount++ } c.shutdownWG.Add(serviceCount) c.frontendService.Stop() for _, historyService := range c.historyServices { historyService.Stop() } for _, matchingService := range c.matchingServices { matchingService.Stop() } if c.workerConfig.EnableReplicator { c.replicator.Stop() } if c.workerConfig.EnableArchiver { c.clientWorker.Stop() } close(c.shutdownCh) c.shutdownWG.Wait() } func newHost(tchan uint16) membership.HostInfo { address := "127.0.0.1" return membership.NewDetailedHostInfo( fmt.Sprintf("%s:%d", address, tchan), fmt.Sprintf("%s_%d", address, tchan), membership.PortMap{ membership.PortTchannel: tchan, membership.PortGRPC: tchan + 10, }, ) } func (c *cadenceImpl) FrontendHost() membership.HostInfo { var tchan uint16 switch c.clusterNo { case 0: tchan = 7104 case 1: tchan = 8104 case 2: tchan = 9104 case 3: tchan = 10104 default: tchan = 7104 } return newHost(tchan) } func (c *cadenceImpl) FrontendPProfPort() int { switch c.clusterNo { case 0: return 7105 case 1: return 8105 case 2: return 9105 case 3: return 10105 default: return 7105 } } func (c *cadenceImpl) HistoryHosts() []membership.HostInfo { var hosts []membership.HostInfo var startPort int switch c.clusterNo { case 0: startPort = 7201 case 1: startPort = 8201 case 2: startPort = 9201 case 3: startPort = 10201 default: startPort = 7201 } for i := 0; i < c.historyConfig.NumHistoryHosts; i++ { port := startPort + i hosts = append(hosts, newHost(uint16(port))) } c.logger.Info("History hosts", tag.Value(hosts)) return hosts } func (c *cadenceImpl) HistoryPProfPorts() []int { var ports []int var startPort int switch c.clusterNo { case 0: startPort = 7301 case 1: startPort = 8301 case 2: startPort = 9301 case 3: startPort = 10301 default: startPort = 7301 } for i := 0; i < c.historyConfig.NumHistoryHosts; i++ { port := startPort + i ports = append(ports, port) } c.logger.Info("History pprof ports", tag.Value(ports)) return ports } func (c *cadenceImpl) MatchingHosts() []membership.HostInfo { var hosts []membership.HostInfo var tchan uint16 switch c.clusterNo { case 0: tchan = 7106 case 1: tchan = 8106 case 2: tchan = 9106 case 3: tchan = 10106 default: tchan = 7106 } for i := 0; i < c.matchingConfig.NumMatchingHosts; i++ { port := tchan + uint16(i) hosts = append(hosts, newHost(uint16(port))) } c.logger.Info("Matching hosts", tag.Value(hosts)) return hosts } func (c *cadenceImpl) MatchingPProfPorts() []int { var ports []int var startPort int switch c.clusterNo { case 0: startPort = 7206 case 1: startPort = 8206 case 2: startPort = 9206 case 3: startPort = 10206 default: startPort = 7206 } for i := 0; i < c.matchingConfig.NumMatchingHosts; i++ { port := startPort + i ports = append(ports, port) } c.logger.Info("Matching pprof ports", tag.Value(ports)) return ports } func (c *cadenceImpl) MatchingPrometheusPorts() []int { var ports []int var startPort int switch c.clusterNo { case 0: startPort = 7306 case 1: startPort = 8306 case 2: startPort = 9306 case 3: startPort = 10306 default: startPort = 7306 } for i := 0; i < c.matchingConfig.NumMatchingHosts; i++ { port := startPort + i ports = append(ports, port) } c.logger.Info("Matching prometheus ports", tag.Value(ports)) return ports } func (c *cadenceImpl) WorkerServiceHost() membership.HostInfo { var tchan uint16 switch c.clusterNo { case 0: tchan = 7108 case 1: tchan = 8108 case 2: tchan = 9108 case 3: tchan = 10108 default: tchan = 7108 } return newHost(tchan) } func (c *cadenceImpl) WorkerPProfPort() int { switch c.clusterNo { case 0: return 7109 case 1: return 8109 case 2: return 9109 case 3: return 10109 default: return 7109 } } func (c *cadenceImpl) GetAdminClient() adminClient.Client { return c.adminClient } func (c *cadenceImpl) GetFrontendClient() frontendClient.Client { return c.frontendClient } func (c *cadenceImpl) GetHistoryClient() historyClient.Client { return c.historyClient } func (c *cadenceImpl) GetMatchingClient() matchingClient.Client { return c.matchingClients[0] } func (c *cadenceImpl) GetMatchingClients() []matchingClient.Client { return c.matchingClients } func (c *cadenceImpl) startFrontend(hosts map[string][]membership.HostInfo, startWG *sync.WaitGroup) { params := new(resource.Params) params.ClusterRedirectionPolicy = &config.ClusterRedirectionPolicy{} params.Name = service.Frontend params.Logger = c.logger params.ThrottledLogger = c.logger params.TimeSource = c.timeSource params.PProfInitializer = newPProfInitializerImpl(c.logger, c.FrontendPProfPort()) params.MetricScope = tally.NewTestScope(service.Frontend, make(map[string]string)) params.MetricsClient = metrics.NewClient(params.MetricScope, service.GetMetricsServiceIdx(params.Name, c.logger), metrics.HistogramMigration{} /* default, only used in test setups */) params.RPCFactory = c.newRPCFactory(service.Frontend, c.FrontendHost(), params.MetricsClient) params.MembershipResolver = newMembershipResolver(params.Name, hosts, c.FrontendHost()) params.ClusterMetadata = c.clusterMetadata params.MessagingClient = c.messagingClient params.DynamicConfig = newIntegrationConfigClient(c.dynamicClient, c.frontendDynCfgOverrides) params.ArchivalMetadata = c.archiverMetadata params.ArchiverProvider = c.archiverProvider params.ESConfig = c.esConfig params.ESClient = c.esClient params.PinotConfig = c.pinotConfig params.PinotClient = c.pinotClient params.GetIsolationGroups = getFromDynamicConfig(params) var err error authorizer, err := authorization.NewAuthorizer(c.authorizationConfig, params.Logger, nil) if err != nil { c.logger.Fatal("Unable to create authorizer", tag.Error(err)) } params.Authorizer = authorizer params.PersistenceConfig, err = copyPersistenceConfig(c.persistenceConfig) if err != nil { c.logger.Fatal("Failed to copy persistence config for frontend", tag.Error(err)) } if c.pinotConfig != nil { pinotDataStoreName := "pinot-visibility" params.PersistenceConfig.AdvancedVisibilityStore = pinotDataStoreName params.DynamicConfig.UpdateValue(dynamicproperties.ReadVisibilityStoreName, constants.VisibilityModePinot) params.PersistenceConfig.DataStores[pinotDataStoreName] = config.DataStore{ Pinot: c.pinotConfig, } } else if c.esConfig != nil { esDataStoreName := "es-visibility" params.PersistenceConfig.AdvancedVisibilityStore = esDataStoreName params.PersistenceConfig.DataStores[esDataStoreName] = config.DataStore{ ElasticSearch: c.esConfig, } } if c.asyncWFQueues != nil { params.AsyncWorkflowQueueProvider, err = queue.NewAsyncQueueProvider(c.asyncWFQueues) if err != nil { c.logger.Fatal("error creating async queue provider", tag.Error(err)) } } frontendService, err := frontend.NewService(params) if err != nil { params.Logger.Fatal("unable to start frontend service", tag.Error(err)) } if c.mockAdminClient != nil { clientBean := frontendService.GetClientBean() if clientBean != nil { for serviceName, client := range c.mockAdminClient { clientBean.SetRemoteAdminClient(serviceName, client) } } } c.frontendService = frontendService c.frontendClient = NewFrontendClient(frontendService.GetDispatcher()) c.adminClient = NewAdminClient(frontendService.GetDispatcher()) go frontendService.Start() c.logger.Info("Started frontend service") startWG.Done() <-c.shutdownCh c.shutdownWG.Done() } func (c *cadenceImpl) startHistory(hosts map[string][]membership.HostInfo, startWG *sync.WaitGroup) { pprofPorts := c.HistoryPProfPorts() historyHosts := c.HistoryHosts() for i, hostport := range historyHosts { params := new(resource.Params) params.Name = service.History params.Logger = c.logger params.ThrottledLogger = c.logger params.TimeSource = c.timeSource params.PProfInitializer = newPProfInitializerImpl(c.logger, pprofPorts[i]) params.MetricScope = tally.NewTestScope(service.History, make(map[string]string)) params.MetricsClient = metrics.NewClient(params.MetricScope, service.GetMetricsServiceIdx(params.Name, c.logger), metrics.HistogramMigration{} /* default, only used in test setups */) params.RPCFactory = c.newRPCFactory(service.History, hostport, params.MetricsClient) params.MembershipResolver = newMembershipResolver(params.Name, hosts, hostport) params.ClusterMetadata = c.clusterMetadata params.MessagingClient = c.messagingClient integrationClient := newIntegrationConfigClient(c.dynamicClient, c.historyDynCfgOverrides) c.overrideHistoryDynamicConfig(integrationClient) params.DynamicConfig = integrationClient params.PublicClient = newPublicClient(params.RPCFactory.GetDispatcher()) params.ArchivalMetadata = c.archiverMetadata params.ArchiverProvider = c.archiverProvider params.ESConfig = c.esConfig params.ESClient = c.esClient params.PinotConfig = c.pinotConfig params.GetIsolationGroups = getFromDynamicConfig(params) var err error params.PersistenceConfig, err = copyPersistenceConfig(c.persistenceConfig) if err != nil { c.logger.Fatal("Failed to copy persistence config for history", tag.Error(err)) } if c.pinotConfig != nil { pinotDataStoreName := "pinot-visibility" params.PersistenceConfig.AdvancedVisibilityStore = pinotDataStoreName params.PersistenceConfig.DataStores[pinotDataStoreName] = config.DataStore{ Pinot: c.pinotConfig, ElasticSearch: c.esConfig, } params.DynamicConfig.UpdateValue(dynamicproperties.WriteVisibilityStoreName, constants.VisibilityModePinot) } else if c.esConfig != nil { esDataStoreName := "es-visibility" params.PersistenceConfig.AdvancedVisibilityStore = esDataStoreName params.PersistenceConfig.DataStores[esDataStoreName] = config.DataStore{ ElasticSearch: c.esConfig, } } historyService, err := history.NewService(params) if err != nil { params.Logger.Fatal("unable to start history service", tag.Error(err)) } if c.mockAdminClient != nil { clientBean := historyService.GetClientBean() if clientBean != nil { for serviceName, client := range c.mockAdminClient { clientBean.SetRemoteAdminClient(serviceName, client) } } } // TODO: this is not correct when there are multiple history hosts as later client will overwrite previous ones. // However current interface for getting history client doesn't specify which client it needs and the tests that use this API // depends on the fact that there's only one history host. // Need to change those tests and modify the interface for getting history client. c.historyClient = NewHistoryClient(historyService.GetDispatcher()) c.historyServices = append(c.historyServices, historyService) go historyService.Start() } startWG.Done() c.logger.Info(fmt.Sprintf("Started %d history services", len(c.historyServices))) <-c.shutdownCh c.shutdownWG.Done() } func (c *cadenceImpl) startMatching(hosts map[string][]membership.HostInfo, startWG *sync.WaitGroup) { pprofPorts := c.MatchingPProfPorts() promPorts := c.MatchingPrometheusPorts() for i, hostport := range c.MatchingHosts() { hostport.Identity() matchingHost := fmt.Sprintf("matching-host-%d:%s", i, hostport.Identity()) params := new(resource.Params) params.Name = service.Matching params.Logger = c.logger.WithTags(tag.Dynamic("matching-host", matchingHost)) params.ThrottledLogger = c.logger params.TimeSource = c.timeSource params.PProfInitializer = newPProfInitializerImpl(c.logger, pprofPorts[i]) metricsCfg := config.Metrics{ Prometheus: &prometheus.Configuration{ TimerType: "histogram", ListenAddress: fmt.Sprintf(":%d", promPorts[i]), }, } params.MetricScope = metricsCfg.NewScope(c.logger, service.Matching) params.MetricsClient = metrics.NewClient(params.MetricScope, service.GetMetricsServiceIdx(params.Name, c.logger), metrics.HistogramMigration{} /* default, only used in test setups */) params.RPCFactory = c.newRPCFactory(service.Matching, hostport, params.MetricsClient) params.MembershipResolver = newMembershipResolver(params.Name, hosts, hostport) params.ClusterMetadata = c.clusterMetadata params.DynamicConfig = newIntegrationConfigClient(c.dynamicClient, c.matchingDynCfgOverrides) params.ArchivalMetadata = c.archiverMetadata params.ArchiverProvider = c.archiverProvider params.GetIsolationGroups = getFromDynamicConfig(params) var err error params.PersistenceConfig, err = copyPersistenceConfig(c.persistenceConfig) if err != nil { c.logger.Fatal("Failed to copy persistence config for matching", tag.Error(err)) } if c.historyConfig.MockClient != nil { params.HistoryClientFn = func() historyClient.Client { return c.historyConfig.MockClient } } // Set default ShardDistributorMatchingConfig for integration tests params.ShardDistributorMatchingConfig = clientcommon.Config{ Namespaces: []clientcommon.NamespaceConfig{{ Namespace: "cadence-matching-integration", HeartBeatInterval: 1 * time.Second, MigrationMode: sdconfig.MigrationModeLOCALPASSTHROUGH, TTLShard: 5 * time.Minute, TTLReport: 1 * time.Minute, }}, } matchingService, err := matching.NewService(params) if err != nil { params.Logger.Fatal("unable to start matching service", tag.Error(err)) } clientBean := matchingService.GetClientBean() if c.mockAdminClient != nil { for serviceName, client := range c.mockAdminClient { clientBean.SetRemoteAdminClient(serviceName, client) } } // When there are multiple matching hosts the last client will overwrite previous ones. // It should be fine because the underlying client bean logic should still pick the right destination. matchingClient, err := clientBean.GetMatchingClient(matchingService.GetDomainCache().GetDomainName) if err != nil { params.Logger.Fatal("unable to get matching client", tag.Error(err)) } c.matchingClients = append(c.matchingClients, matchingClient) c.matchingServices = append(c.matchingServices, matchingService) go matchingService.Start() } startWG.Done() c.logger.Info(fmt.Sprintf("Started %d matching services", len(c.matchingServices))) <-c.shutdownCh c.shutdownWG.Done() } func (c *cadenceImpl) startWorker(hosts map[string][]membership.HostInfo, startWG *sync.WaitGroup) { defer c.shutdownWG.Done() params := new(resource.Params) params.Name = service.Worker params.Logger = c.logger params.ThrottledLogger = c.logger params.TimeSource = c.timeSource params.PProfInitializer = newPProfInitializerImpl(c.logger, c.WorkerPProfPort()) params.MetricScope = tally.NewTestScope(service.Worker, make(map[string]string)) params.MetricsClient = metrics.NewClient(params.MetricScope, service.GetMetricsServiceIdx(params.Name, c.logger), metrics.HistogramMigration{} /* default, only used in test setups */) params.RPCFactory = c.newRPCFactory(service.Worker, c.WorkerServiceHost(), params.MetricsClient) params.MembershipResolver = newMembershipResolver(params.Name, hosts, c.WorkerServiceHost()) params.ClusterMetadata = c.clusterMetadata params.DynamicConfig = newIntegrationConfigClient(c.dynamicClient, c.workerDynCfgOverrides) params.ArchivalMetadata = c.archiverMetadata params.ArchiverProvider = c.archiverProvider params.GetIsolationGroups = getFromDynamicConfig(params) var err error params.PersistenceConfig, err = copyPersistenceConfig(c.persistenceConfig) if err != nil { c.logger.Fatal("Failed to copy persistence config for worker", tag.Error(err)) } params.PublicClient = newPublicClient(params.RPCFactory.GetDispatcher()) service := NewService(params) service.Start() var replicatorDomainCache cache.DomainCache if c.workerConfig.EnableReplicator { metadataManager := metered.NewDomainManager(c.domainManager, service.GetMetricsClient(), c.logger, &c.persistenceConfig) replicatorDomainCache = cache.NewDomainCache(metadataManager, c.clusterMetadata, service.GetMetricsClient(), service.GetLogger()) replicatorDomainCache.Start() defer replicatorDomainCache.Stop() c.startWorkerReplicator(service) } var clientWorkerDomainCache cache.DomainCache if c.workerConfig.EnableArchiver { metadataProxyManager := metered.NewDomainManager(c.domainManager, service.GetMetricsClient(), c.logger, &c.persistenceConfig) clientWorkerDomainCache = cache.NewDomainCache(metadataProxyManager, c.clusterMetadata, service.GetMetricsClient(), service.GetLogger()) clientWorkerDomainCache.Start() defer clientWorkerDomainCache.Stop() c.startWorkerClientWorker(params, service, clientWorkerDomainCache) } if c.workerConfig.EnableIndexer { c.startWorkerIndexer(params, service) } var asyncWFDomainCache cache.DomainCache if c.workerConfig.EnableAsyncWFConsumer { queueProvider, err := queue.NewAsyncQueueProvider(c.asyncWFQueues) if err != nil { c.logger.Fatal("error creating async queue provider", tag.Error(err)) } metadataProxyManager := metered.NewDomainManager( c.domainManager, service.GetMetricsClient(), c.logger, &c.persistenceConfig) asyncWFDomainCache = cache.NewDomainCache( metadataProxyManager, c.clusterMetadata, service.GetMetricsClient(), service.GetLogger(), cache.WithTimeSource(params.TimeSource)) asyncWFDomainCache.Start() defer asyncWFDomainCache.Stop() cm := asyncworkflow.NewConsumerManager( service.GetLogger(), service.GetMetricsClient(), asyncWFDomainCache, queueProvider, c.frontendClient, asyncworkflow.WithTimeSource(params.TimeSource), asyncworkflow.WithRefreshInterval(time.Second), ) cm.Start() defer cm.Stop() } c.logger.Info("Started worker service") startWG.Done() <-c.shutdownCh if c.workerConfig.EnableReplicator { replicatorDomainCache.Stop() } if c.workerConfig.EnableArchiver { clientWorkerDomainCache.Stop() } } func (c *cadenceImpl) startWorkerReplicator(svc Service) { c.replicator = replicator.NewReplicator( c.clusterMetadata, svc.GetClientBean(), c.logger, svc.GetMetricsClient(), svc.GetHostInfo(), svc.GetMembershipResolver(), c.domainReplicationQueue, c.domainReplicationTaskExecutor, time.Millisecond, ) if err := c.replicator.Start(); err != nil { c.replicator.Stop() c.logger.Fatal("Fail to start replicator when start worker", tag.Error(err)) } } func (c *cadenceImpl) startWorkerClientWorker(params *resource.Params, svc Service, domainCache cache.DomainCache) { workerConfig := worker.NewConfig(params) workerConfig.ArchiverConfig.ArchiverConcurrency = dynamicproperties.GetIntPropertyFn(10) historyArchiverBootstrapContainer := &carchiver.HistoryBootstrapContainer{ HistoryV2Manager: c.historyV2Mgr, Logger: c.logger, MetricsClient: svc.GetMetricsClient(), ClusterMetadata: c.clusterMetadata, DomainCache: domainCache, } err := c.archiverProvider.RegisterBootstrapContainer(service.Worker, historyArchiverBootstrapContainer, &carchiver.VisibilityBootstrapContainer{}) if err != nil { c.logger.Fatal("Failed to register archiver bootstrap container for worker service", tag.Error(err)) } bc := &archiver.BootstrapContainer{ PublicClient: params.PublicClient, MetricsClient: svc.GetMetricsClient(), Logger: c.logger, HistoryV2Manager: c.historyV2Mgr, DomainCache: domainCache, Config: workerConfig.ArchiverConfig, ArchiverProvider: c.archiverProvider, } c.clientWorker = archiver.NewClientWorker(bc) if err := c.clientWorker.Start(); err != nil { c.clientWorker.Stop() c.logger.Fatal("Fail to start archiver when start worker", tag.Error(err)) } } func (c *cadenceImpl) startWorkerIndexer(params *resource.Params, service Service) { params.DynamicConfig.UpdateValue(dynamicproperties.WriteVisibilityStoreName, constants.VisibilityModeES) workerConfig := worker.NewConfig(params) c.indexer = indexer.NewIndexer( workerConfig.IndexerCfg, c.messagingClient, c.esClient, c.esConfig.Indices[constants.VisibilityAppName], c.esConfig.ConsumerName, c.logger, service.GetMetricsClient()) if err := c.indexer.Start(); err != nil { c.indexer.Stop() c.logger.Fatal("Fail to start indexer when start worker", tag.Error(err)) } } func (c *cadenceImpl) createSystemDomain() error { ctx, cancel := context.WithTimeout(context.Background(), defaultTestPersistenceTimeout) defer cancel() _, err := c.domainManager.CreateDomain(ctx, &persistence.CreateDomainRequest{ Info: &persistence.DomainInfo{ ID: uuid.New(), Name: "cadence-system", Status: persistence.DomainStatusRegistered, Description: "Cadence system domain", }, Config: &persistence.DomainConfig{ Retention: 1, HistoryArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalStatus: types.ArchivalStatusDisabled, }, ReplicationConfig: &persistence.DomainReplicationConfig{}, FailoverVersion: constants.EmptyVersion, }) if err != nil { if _, ok := err.(*types.DomainAlreadyExistsError); ok { return nil } return fmt.Errorf("failed to create cadence-system domain: %v", err) } return nil } func (c *cadenceImpl) GetExecutionManagerFactory() persistence.ExecutionManagerFactory { return c.executionMgrFactory } func (c *cadenceImpl) overrideHistoryDynamicConfig(client *dynamicClient) { client.OverrideValue(dynamicproperties.ReplicationTaskProcessorStartWait, time.Nanosecond) if c.workerConfig.EnableIndexer { client.OverrideValue(dynamicproperties.WriteVisibilityStoreName, constants.VisibilityModeES) } if c.historyConfig.HistoryCountLimitWarn != 0 { client.OverrideValue(dynamicproperties.HistoryCountLimitWarn, c.historyConfig.HistoryCountLimitWarn) } if c.historyConfig.HistoryCountLimitError != 0 { client.OverrideValue(dynamicproperties.HistoryCountLimitError, c.historyConfig.HistoryCountLimitError) } } // copyPersistenceConfig makes a deepcopy of persistence config. // This is just a temp fix for the race condition of persistence config. // The race condition happens because all the services are using the same datastore map in the config. // Also all services will retry to modify the maxQPS field in the datastore during start up and use the modified maxQPS value to create a persistence factory. func copyPersistenceConfig(pConfig config.Persistence) (config.Persistence, error) { copiedDataStores := make(map[string]config.DataStore) for name, value := range pConfig.DataStores { copiedDataStore := config.DataStore{} encodedDataStore, err := json.Marshal(value) if err != nil { return pConfig, err } if err = json.Unmarshal(encodedDataStore, &copiedDataStore); err != nil { return pConfig, err } copiedDataStores[name] = copiedDataStore } pConfig.DataStores = copiedDataStores return pConfig, nil } func newMembershipResolver(serviceName string, hosts map[string][]membership.HostInfo, currentHost membership.HostInfo) membership.Resolver { return NewSimpleResolver(serviceName, hosts, currentHost) } func newPProfInitializerImpl(logger log.Logger, port int) common.PProfInitializer { return &config.PProfInitializerImpl{ PProf: &config.PProf{ Port: port, }, Logger: logger, } } func newPublicClient(dispatcher *yarpc.Dispatcher) cwsc.Interface { config := dispatcher.ClientConfig(rpc.OutboundPublicClient) return compatibility.NewThrift2ProtoAdapter( apiv1.NewDomainAPIYARPCClient(config), apiv1.NewWorkflowAPIYARPCClient(config), apiv1.NewWorkerAPIYARPCClient(config), apiv1.NewVisibilityAPIYARPCClient(config), ) } func (c *cadenceImpl) newRPCFactory(serviceName string, host membership.HostInfo, metricsCl metrics.Client) rpc.Factory { tchannelAddress, err := host.GetNamedAddress(membership.PortTchannel) if err != nil { c.logger.Fatal("failed to get PortTchannel port from host", tag.Value(host), tag.Error(err)) } grpcAddress, err := host.GetNamedAddress(membership.PortGRPC) if err != nil { c.logger.Fatal("failed to get PortGRPC port from host", tag.Value(host), tag.Error(err)) } frontendGrpcAddress, err := c.FrontendHost().GetNamedAddress(membership.PortGRPC) if err != nil { c.logger.Fatal("failed to get frontend PortGRPC", tag.Value(c.FrontendHost()), tag.Error(err)) } directOutboundPCF := rpc.NewDirectPeerChooserFactory(serviceName, c.logger, metricsCl) directConnRetainFn := func(opts ...dynamicproperties.FilterOption) bool { return false } return rpc.NewFactory(c.logger, rpc.Params{ ServiceName: serviceName, TChannelAddress: tchannelAddress, GRPCAddress: grpcAddress, InboundMiddleware: yarpc.InboundMiddleware{ Unary: yarpc.UnaryInboundMiddleware(&versionMiddleware{}, &rpc.ClientPartitionConfigMiddleware{}, &rpc.ForwardPartitionConfigMiddleware{}), }, OutboundMiddleware: yarpc.OutboundMiddleware{ Unary: &rpc.ForwardPartitionConfigMiddleware{}, }, // For integration tests to generate client out of the same outbound. OutboundsBuilder: rpc.CombineOutbounds( rpc.NewSingleGRPCOutboundBuilder(testOutboundName(serviceName), serviceName, grpcAddress), rpc.NewSingleGRPCOutboundBuilder(rpc.OutboundPublicClient, service.Frontend, frontendGrpcAddress), rpc.NewCrossDCOutbounds(c.clusterMetadata.GetAllClusterInfo(), rpc.NewDNSPeerChooserFactory(0, c.logger)), rpc.NewDirectOutboundBuilder(service.History, true, nil, directOutboundPCF, directConnRetainFn), rpc.NewDirectOutboundBuilder(service.Matching, true, nil, directOutboundPCF, directConnRetainFn), ), }) } // testOutbound prefixes outbound with "test-" to not clash with other real Cadence outbounds. func testOutboundName(name string) string { return "test-" + name } type versionMiddleware struct { } func (vm *versionMiddleware) Handle(ctx context.Context, req *transport.Request, resw transport.ResponseWriter, h transport.UnaryHandler) error { req.Headers = req.Headers.With(common.LibraryVersionHeaderName, "1.0.0"). With(common.FeatureVersionHeaderName, cc.SupportedGoSDKVersion). With(common.ClientImplHeaderName, cc.GoSDK) return h.Handle(ctx, req, resw) } func getFromDynamicConfig(params *resource.Params) func() []string { return func() []string { list, err := params.DynamicConfig.GetListValue(dynamicproperties.AllIsolationGroups, nil) if err != nil { params.Logger.Error("failed to get isolation groups from config", tag.Error(err)) return nil } res, err := isolationgroupapi.MapAllIsolationGroupsResponse(list) if err != nil { params.Logger.Error("failed to map isolation groups from config", tag.Error(err)) return nil } return res } } ================================================ FILE: host/persistence/cassandra/cassandra_persistence_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "testing" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql/public" persistencetests "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/testflags" ) func TestCassandraHistoryPersistence(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.HistoryV2PersistenceSuite) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.Setup() suite.Run(t, s) } func TestCassandraMatchingPersistence(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.MatchingPersistenceSuite) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.Setup() suite.Run(t, s) } func TestCassandraDomainPersistence(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.MetadataPersistenceSuiteV2) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.Setup() suite.Run(t, s) } func TestCassandraShardPersistence(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.ShardPersistenceSuite) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.Setup() suite.Run(t, s) } func TestCassandraShardMigrationPersistence(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.ShardPersistenceSuite) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.TestBase.DynamicConfiguration.ReadNoSQLShardFromDataBlob = dynamicproperties.GetBoolPropertyFn(true) s.Setup() suite.Run(t, s) } func TestCassandraVisibilityPersistence(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.DBVisibilityPersistenceSuite) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.Setup() suite.Run(t, s) } func TestCassandraExecutionManager(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.ExecutionManagerSuite) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.Setup() suite.Run(t, s) } func TestCassandraExecutionManagerWithEventsV2(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.ExecutionManagerSuiteForEventsV2) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.Setup() suite.Run(t, s) } func TestCassandraQueuePersistence(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.QueuePersistenceSuite) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.Setup() suite.Run(t, s) } func TestCassandraConfigStorePersistence(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.ConfigStorePersistenceSuite) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.Setup() suite.Run(t, s) } func TestCassandraDomainAuditPersistence(t *testing.T) { testflags.RequireCassandra(t) s := new(persistencetests.DomainAuditPersistenceSuite) s.TestBase = public.NewTestBaseWithPublicCassandra(t, &persistencetests.TestBaseOptions{}) s.Setup() suite.Run(t, s) } ================================================ FILE: host/persistence/dynamodb/dynamodb_persistence_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package dynamodb import ( "testing" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/dynamodb" ) // This is to make sure adding new noop method when adding new nosql interfaces // Remove it when any other tests are implemented. func TestDynamoDBNoopStruct(t *testing.T) { _, _ = dynamodb.NewDynamoDB(config.NoSQL{}, nil) } func TestDynamoDBHistoryPersistence(t *testing.T) { // s := new(persistencetests.HistoryV2PersistenceSuite) // s.TestBase = public.NewTestBaseWithDynamoDB(&persistencetests.TestBaseOptions{}) // s.TestBase.Setup() // suite.Run(t, s) } func TestDynamoDBMatchingPersistence(t *testing.T) { // s := new(persistencetests.MatchingPersistenceSuite) // s.TestBase = public.NewTestBaseWithDynamoDB(&persistencetests.TestBaseOptions{}) // s.TestBase.Setup() // suite.Run(t, s) } func TestDynamoDBDomainPersistence(t *testing.T) { // s := new(persistencetests.MetadataPersistenceSuiteV2) // s.TestBase = public.NewTestBaseWithDynamoDB(&persistencetests.TestBaseOptions{}) // s.TestBase.Setup() // suite.Run(t, s) } func TestDynamoDBQueuePersistence(t *testing.T) { // s := new(persistencetests.QueuePersistenceSuite) // s.TestBase = public.NewTestBaseWithDynamoDB(&persistencetests.TestBaseOptions{}) // s.TestBase.Setup() // suite.Run(t, s) } ================================================ FILE: host/persistence/mongodb/mongodb_persistence_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tests import ( "testing" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/mongodb" persistencetests "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/environment" "github.com/uber/cadence/testflags" ) func TestMongoDBConfigStorePersistence(t *testing.T) { testflags.RequireMongoDB(t) s := new(persistencetests.ConfigStorePersistenceSuite) s.TestBase = NewTestBaseWithMongo(t) s.TestBase.Setup() suite.Run(t, s) } // TODO uncomment the test once HistoryEventsCRUD is implemented // func TestMongoDBHistoryPersistence(t *testing.T) { // s := new(persistencetests.HistoryV2PersistenceSuite) // s.TestBase = NewTestBaseWithMongo() // s.TestBase.Setup() // suite.Run(t, s) // } // TODO uncomment the test once TaskCRUD is implemented // func TestMongoDBMatchingPersistence(t *testing.T) { // s := new(persistencetests.MatchingPersistenceSuite) // s.TestBase = NewTestBaseWithMongo() // s.TestBase.Setup() // suite.Run(t, s) // } // TODO uncomment the test once DomainCRUD is implemented // func TestMongoDBDomainPersistence(t *testing.T) { // s := new(persistencetests.MetadataPersistenceSuiteV2) // s.TestBase = NewTestBaseWithMongo() // s.TestBase.Setup() // suite.Run(t, s) // } // TODO uncomment the test once MessageQueueCRUD is implemented // func TestMongoDBQueuePersistence(t *testing.T) { // s := new(persistencetests.QueuePersistenceSuite) // s.TestBase = NewTestBaseWithMongo() // s.TestBase.Setup() // suite.Run(t, s) // } // TODO uncomment the test once ShardCRUD is implemented // func TestMongoDBShardPersistence(t *testing.T) { // s := new(persistencetests.ShardPersistenceSuite) // s.TestBase = NewTestBaseWithMongo() // s.TestBase.Setup() // suite.Run(t, s) // } // TODO uncomment the test once VisibilityCRUD is implemented // func TestMongoDBVisibilityPersistence(t *testing.T) { // s := new(persistencetests.DBVisibilityPersistenceSuite) // s.TestBase = NewTestBaseWithMongo() // s.TestBase.Setup() // suite.Run(t, s) // } // TODO uncomment the test once WorkflowCRUD is implemented // func TestMongoDBExecutionManager(t *testing.T) { // s := new(persistencetests.ExecutionManagerSuite) // s.TestBase = NewTestBaseWithMongo() // s.TestBase.Setup() // suite.Run(t, s) // } // TODO uncomment the test once WorkflowCRUD is implemented // func TestMongoDBExecutionManagerWithEventsV2(t *testing.T) { // s := new(persistencetests.ExecutionManagerSuiteForEventsV2) // s.TestBase = NewTestBaseWithMongo() // s.TestBase.Setup() // suite.Run(t, s) // } func NewTestBaseWithMongo(t *testing.T) *persistencetests.TestBase { port, err := environment.GetMongoPort() if err != nil { t.Fatal(err) } options := &persistencetests.TestBaseOptions{ DBPluginName: mongodb.PluginName, DBHost: environment.GetMongoAddress(), DBUsername: "root", DBPassword: "cadence", DBPort: port, } return persistencetests.NewTestBaseWithNoSQL(t, options) } ================================================ FILE: host/persistence/mysql/mysql_persistence_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/sql/sqlplugin/mysql" "github.com/uber/cadence/testflags" ) func TestMySQLHistoryV2PersistenceSuite(t *testing.T) { testflags.RequireMySQL(t) s := new(pt.HistoryV2PersistenceSuite) option, err := mysql.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestMySQLMatchingPersistenceSuite(t *testing.T) { testflags.RequireMySQL(t) s := new(pt.MatchingPersistenceSuite) option, err := mysql.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestMySQLMetadataPersistenceSuiteV2(t *testing.T) { testflags.RequireMySQL(t) s := new(pt.MetadataPersistenceSuiteV2) option, err := mysql.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestMySQLShardPersistenceSuite(t *testing.T) { testflags.RequireMySQL(t) s := new(pt.ShardPersistenceSuite) option, err := mysql.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } type ExecutionManagerSuite struct { pt.ExecutionManagerSuite } func (s *ExecutionManagerSuite) TestCreateWorkflowExecutionWithWorkflowRequestsDedup() { s.T().Skip("skip the test until we store workflow_request in mysql") } func (s *ExecutionManagerSuite) TestUpdateWorkflowExecutionWithWorkflowRequestsDedup() { s.T().Skip("skip the test until we store workflow_request in mysql") } func TestMySQLExecutionManagerSuite(t *testing.T) { testflags.RequireMySQL(t) s := new(ExecutionManagerSuite) option, err := mysql.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestMySQLExecutionManagerWithEventsV2(t *testing.T) { testflags.RequireMySQL(t) s := new(pt.ExecutionManagerSuiteForEventsV2) option, err := mysql.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestMySQLVisibilityPersistenceSuite(t *testing.T) { testflags.RequireMySQL(t) s := new(pt.DBVisibilityPersistenceSuite) option, err := mysql.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestMySQLQueuePersistence(t *testing.T) { testflags.RequireMySQL(t) s := new(pt.QueuePersistenceSuite) option, err := mysql.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestMySQLConfigPersistence(t *testing.T) { testflags.RequireMySQL(t) s := new(pt.ConfigStorePersistenceSuite) option, err := mysql.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestMySQLDomainAuditPersistence(t *testing.T) { testflags.RequireMySQL(t) s := new(pt.DomainAuditPersistenceSuite) option, err := mysql.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } ================================================ FILE: host/persistence/postgres/postgres_persistence_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/sql/sqlplugin/postgres" "github.com/uber/cadence/testflags" ) func TestPostgresSQLHistoryV2PersistenceSuite(t *testing.T) { testflags.RequirePostgres(t) s := new(pt.HistoryV2PersistenceSuite) options, err := postgres.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, options) s.TestBase.Setup() suite.Run(t, s) } func TestPostgresSQLMatchingPersistenceSuite(t *testing.T) { testflags.RequirePostgres(t) s := new(pt.MatchingPersistenceSuite) options, err := postgres.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, options) s.TestBase.Setup() suite.Run(t, s) } func TestPostgresSQLMetadataPersistenceSuiteV2(t *testing.T) { testflags.RequirePostgres(t) s := new(pt.MetadataPersistenceSuiteV2) options, err := postgres.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, options) s.TestBase.Setup() suite.Run(t, s) } func TestPostgresSQLShardPersistenceSuite(t *testing.T) { testflags.RequirePostgres(t) s := new(pt.ShardPersistenceSuite) options, err := postgres.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, options) s.TestBase.Setup() suite.Run(t, s) } type ExecutionManagerSuite struct { pt.ExecutionManagerSuite } func (s *ExecutionManagerSuite) TestCreateWorkflowExecutionWithWorkflowRequestsDedup() { s.T().Skip("skip the test until we store workflow_request in postgres sql") } func (s *ExecutionManagerSuite) TestUpdateWorkflowExecutionWithWorkflowRequestsDedup() { s.T().Skip("skip the test until we store workflow_request in postgres sql") } func TestPostgresSQLExecutionManagerSuite(t *testing.T) { testflags.RequirePostgres(t) s := new(ExecutionManagerSuite) options, err := postgres.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, options) s.TestBase.Setup() suite.Run(t, s) } func TestPostgresSQLExecutionManagerWithEventsV2(t *testing.T) { testflags.RequirePostgres(t) s := new(pt.ExecutionManagerSuiteForEventsV2) option, err := postgres.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, option) s.TestBase.Setup() suite.Run(t, s) } func TestPostgresSQLVisibilityPersistenceSuite(t *testing.T) { testflags.RequirePostgres(t) s := new(pt.DBVisibilityPersistenceSuite) options, err := postgres.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, options) s.TestBase.Setup() suite.Run(t, s) } // TODO flaky test // https://github.com/uber/cadence/issues/2877 /* FAIL: TestPostgresSQLQueuePersistence/TestDomainReplicationQueue (0.26s) queuePersistenceTest.go:102: Error Trace: queuePersistenceTest.go:102 Error: Not equal: expected: 99 actual : 98 Test: TestPostgresSQLQueuePersistence/TestDomainReplicationQueue */ // func TestPostgresSQLQueuePersistence(t *testing.T) { // s := new(pt.QueuePersistenceSuite) // s.TestBase = pt.NewTestBaseWithSQL(GetTestClusterOption()) // s.TestBase.Setup() // suite.Run(t, s) // } func TestPostgresSQLConfigPersistence(t *testing.T) { testflags.RequirePostgres(t) s := new(pt.ConfigStorePersistenceSuite) options, err := postgres.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, options) s.TestBase.Setup() suite.Run(t, s) } func TestPostgresSQLDomainAuditPersistence(t *testing.T) { testflags.RequirePostgres(t) s := new(pt.DomainAuditPersistenceSuite) options, err := postgres.GetTestClusterOption() assert.NoError(t, err) s.TestBase = pt.NewTestBaseWithSQL(t, options) s.TestBase.Setup() suite.Run(t, s) } ================================================ FILE: host/pinot_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:build !race && pinotintegration // +build !race,pinotintegration /* To run locally with docker containers: 1. Stop the previous run if any docker compose -f docker/github_actions/docker-compose-local-pinot.yml down 2. Build the integration-test-async-wf image docker compose -f docker/github_actions/docker-compose-local-pinot.yml build integration-test-cassandra-pinot 3. Run the test in the docker container docker compose -f docker/github_actions/docker-compose-local-pinot.yml run --rm integration-test-cassandra-pinot To run locally natively (without docker), 1. make sure kafka and pinot is running, 2. then run cmd `go test -v ./host -run TestPinotIntegrationSuite -tags pinotintegration` 3. currently we have to manually add test table and delete the table for cleaning. waiting for the support to clean the data programmatically */ package host import ( "encoding/json" "flag" "fmt" "strconv" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" pt "github.com/uber/cadence/common/persistence/persistence-tests" pnt "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/common/types" "github.com/uber/cadence/host/pinotutils" ) const ( numberOfRetry = 50 waitTimeBetweenRetry = 400 * time.Millisecond waitForPinotToSettle = 4 * time.Second // wait pinot shards for some time ensure data consistent ) type PinotIntegrationSuite struct { *require.Assertions logger log.Logger *IntegrationBase pinotClient pnt.GenericClient testSearchAttributeKey string testSearchAttributeVal string } func TestPinotIntegrationSuite(t *testing.T) { flag.Parse() clusterConfig, err := GetTestClusterConfig("testdata/integration_pinot_cluster.yaml") if err != nil { panic(err) } testCluster := NewPersistenceTestCluster(t, clusterConfig) s := new(PinotIntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } func (s *PinotIntegrationSuite) SetupSuite() { s.SetupLogger() s.Logger.Info("Running integration test against test cluster") clusterMetadata := NewClusterMetadata(s.T(), s.TestClusterConfig) dc := persistence.DynamicConfiguration{ EnableSQLAsyncTransaction: dynamicproperties.GetBoolPropertyFn(false), EnableCassandraAllConsistencyLevelDelete: dynamicproperties.GetBoolPropertyFn(true), PersistenceSampleLoggingRate: dynamicproperties.GetIntPropertyFn(100), EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(true), EnableHistoryTaskDualWriteMode: dynamicproperties.GetBoolPropertyFn(true), ReadNoSQLHistoryTaskFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(true), HistoryNodeDeleteBatchSize: dynamicproperties.GetIntPropertyFn(1000), } params := pt.TestBaseParams{ DefaultTestCluster: s.DefaultTestCluster, VisibilityTestCluster: s.VisibilityTestCluster, ClusterMetadata: clusterMetadata, DynamicConfiguration: dc, } cluster, err := NewPinotTestCluster(s.T(), s.TestClusterConfig, s.Logger, params) s.Require().NoError(err) s.TestCluster = cluster s.Engine = s.TestCluster.GetFrontendClient() s.AdminClient = s.TestCluster.GetAdminClient() s.TestRawHistoryDomainName = "TestRawHistoryDomain" s.DomainName = s.RandomizeStr("integration-test-domain") s.Require().NoError( s.RegisterDomain(s.DomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.Require().NoError( s.RegisterDomain(s.TestRawHistoryDomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.ForeignDomainName = s.RandomizeStr("integration-foreign-test-domain") s.Require().NoError( s.RegisterDomain(s.ForeignDomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.Require().NoError(s.registerArchivalDomain()) // this sleep is necessary because domainv2 cache gets refreshed in the // background only every domainCacheRefreshInterval period time.Sleep(cache.DomainCacheRefreshInterval + time.Second) tableName := "cadence_visibility_pinot" // cadence_visibility_pinot_integration_test pinotConfig := &config.PinotVisibilityConfig{ Cluster: "", Broker: "localhost:8099", Table: tableName, ServiceName: "", Migration: config.VisibilityMigration{ Enabled: true, }, } s.pinotClient = pinotutils.CreatePinotClient(&s.Suite, pinotConfig, s.Logger) } func (s *PinotIntegrationSuite) SetupTest() { s.Assertions = require.New(s.T()) s.testSearchAttributeKey = definition.CustomStringField s.testSearchAttributeVal = "test value" } func (s *PinotIntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() // check how to clean up test_table // currently it is not supported } func (s *PinotIntegrationSuite) TestListOpenWorkflow() { id := "pinot-integration-start-workflow-test" wt := "pinot-integration-start-workflow-test-type" tl := "pinot-integration-start-workflow-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } request.SearchAttributes = searchAttr startTime := time.Now().UnixNano() ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) startFilter := &types.StartTimeFilter{} startFilter.EarliestTime = common.Int64Ptr(startTime) var openExecution *types.WorkflowExecutionInfo for i := 0; i < numberOfRetry; i++ { startFilter.LatestTime = common.Int64Ptr(time.Now().UnixNano()) resp, err := s.Engine.ListOpenWorkflowExecutions(ctx, &types.ListOpenWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: defaultTestValueOfESIndexMaxResultWindow, StartTimeFilter: startFilter, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: id, }, }) s.Nil(err) if len(resp.GetExecutions()) > 0 { openExecution = resp.GetExecutions()[0] break } time.Sleep(waitTimeBetweenRetry) } s.NotNil(openExecution) s.Equal(we.GetRunID(), openExecution.GetExecution().GetRunID()) s.Equal(attrValBytes, openExecution.SearchAttributes.GetIndexedFields()[s.testSearchAttributeKey]) } func (s *PinotIntegrationSuite) TestListWorkflow() { id := "pinot-integration-list-workflow-test" wt := "pinot-integration-list-workflow-test-type" tl := "pinot-integration-list-workflow-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s"`, id) s.testHelperForReadOnce(we.GetRunID(), query, false, false) } func (s *PinotIntegrationSuite) createStartWorkflowExecutionRequest(id, wt, tl string) *types.StartWorkflowExecutionRequest { identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } return request } func (s *PinotIntegrationSuite) testHelperForReadOnce(runID, query string, isScan bool, isAnyMatchOk bool) { s.testHelperForReadOnceWithDomain(s.DomainName, runID, query, isScan, isAnyMatchOk) } func (s *PinotIntegrationSuite) testHelperForReadOnceWithDomain(domainName string, runID, query string, isScan bool, isAnyMatchOk bool) { var openExecution *types.WorkflowExecutionInfo listRequest := &types.ListWorkflowExecutionsRequest{ Domain: domainName, PageSize: defaultTestValueOfESIndexMaxResultWindow, Query: query, } ctx, cancel := createContext() defer cancel() Retry: for i := 0; i < numberOfRetry; i++ { var resp *types.ListWorkflowExecutionsResponse var err error if isScan { resp, err = s.Engine.ScanWorkflowExecutions(ctx, listRequest) } else { resp, err = s.Engine.ListWorkflowExecutions(ctx, listRequest) } s.Nil(err) logStr := fmt.Sprintf("Results for query '%s' (desired runId: %s): \n", query, runID) s.Logger.Info(logStr) for _, e := range resp.GetExecutions() { logStr = fmt.Sprintf("Execution: %+v, %+v \n", e.Execution, e) s.Logger.Info(logStr) } if len(resp.GetExecutions()) == 1 { openExecution = resp.GetExecutions()[0] break } if isAnyMatchOk { for _, e := range resp.GetExecutions() { if e.Execution.RunID == runID { openExecution = e break Retry } } } time.Sleep(waitTimeBetweenRetry) } s.NotNil(openExecution) s.Equal(runID, openExecution.GetExecution().GetRunID()) s.True(openExecution.GetExecutionTime() >= openExecution.GetStartTime()) if openExecution.SearchAttributes != nil && len(openExecution.SearchAttributes.GetIndexedFields()) > 0 { searchValBytes := openExecution.SearchAttributes.GetIndexedFields()[s.testSearchAttributeKey] var searchVal string json.Unmarshal(searchValBytes, &searchVal) // pinot sets default values for all the columns, // this feature can break the test here when there is no actual search attributes upsert, it will still return something // TODO: update this after finding a good solution s.Equal(searchVal, searchVal) } } func (s *PinotIntegrationSuite) startWorkflow( prefix string, isCron bool, ) *types.StartWorkflowExecutionResponse { id := "pinot-integration-list-workflow-" + prefix + "-test" wt := "pinot-integration-list-workflow-" + prefix + "test-type" tl := "pinot-integration-list-workflow-" + prefix + "test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) if isCron { request.CronSchedule = "*/5 * * * *" // every 5 minutes } ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s"`, id) s.testHelperForReadOnce(we.GetRunID(), query, false, false) return we } func (s *PinotIntegrationSuite) TestListCronWorkflows() { we1 := s.startWorkflow("cron", true) we2 := s.startWorkflow("regular", false) query := fmt.Sprintf(`IsCron = "true"`) s.testHelperForReadOnce(we1.GetRunID(), query, false, true) query = fmt.Sprintf(`IsCron = "false"`) s.testHelperForReadOnce(we2.GetRunID(), query, false, true) } func (s *PinotIntegrationSuite) TestIsGlobalSearchAttribute() { we := s.startWorkflow("local", true) // global domains are disabled for this integration test, so we can only test the false case query := fmt.Sprintf(`NumClusters = "1"`) s.testHelperForReadOnce(we.GetRunID(), query, false, true) } func (s *PinotIntegrationSuite) TestListWorkflow_ExecutionTime() { id := "pinot-integration-list-workflow-execution-time-test" wt := "pinot-integration-list-workflow-execution-time-test-type" tl := "pinot-integration-list-workflow-execution-time-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) cronID := id + "-cron" request.CronSchedule = "@every 1m" request.WorkflowID = cronID weCron, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`(WorkflowID = '%s' or WorkflowID = '%s') and ExecutionTime < %v and ExecutionTime > 0`, id, cronID, time.Now().UnixNano()+int64(time.Minute)) s.testHelperForReadOnce(weCron.GetRunID(), query, false, false) query = fmt.Sprintf(`WorkflowID = '%s'`, id) s.testHelperForReadOnce(we.GetRunID(), query, false, false) } func (s *PinotIntegrationSuite) TestListWorkflow_SearchAttribute() { id := "pinot-integration-list-workflow-by-search-attr-test" wt := "pinot-integration-list-workflow-by-search-attr-test-type" tl := "pinot-integration-list-workflow-by-search-attr-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } request.SearchAttributes = searchAttr ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s" and %s = "%s"`, id, s.testSearchAttributeKey, s.testSearchAttributeVal) s.testHelperForReadOnce(we.GetRunID(), query, false, false) // test upsert dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { upsertDecision := &types.Decision{ DecisionType: types.DecisionTypeUpsertWorkflowSearchAttributes.Ptr(), UpsertWorkflowSearchAttributesDecisionAttributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: getPinotUpsertSearchAttributes(), }} return nil, []*types.Decision{upsertDecision}, nil } taskList := &types.TaskList{Name: tl} poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, StickyTaskList: taskList, Identity: "worker1", DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } _, newTask, err := poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( false, false, true, true, int64(0), 1, true, nil) s.Nil(err) s.NotNil(newTask) s.NotNil(newTask.DecisionTask) time.Sleep(waitForPinotToSettle) listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: int32(2), Query: fmt.Sprintf(`WorkflowType = '%s' and CloseTime = missing and BinaryChecksums = 'binary-v1'`, wt), } // verify upsert data is on Pinot s.testListResultForUpsertSearchAttributes(listRequest) // verify DescribeWorkflowExecution descRequest := &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, }, } descResp, err := s.Engine.DescribeWorkflowExecution(ctx, descRequest) s.Nil(err) expectedSearchAttributes := getPinotUpsertSearchAttributes() s.Equal(expectedSearchAttributes, descResp.WorkflowExecutionInfo.GetSearchAttributes()) } func (s *PinotIntegrationSuite) TestListWorkflow_PageToken() { id := "pinot-integration-list-workflow-token-test" wt := "pinot-integration-list-workflow-token-test-type" tl := "pinot-integration-list-workflow-token-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) numOfWorkflows := defaultTestValueOfESIndexMaxResultWindow - 1 // == 4 pageSize := 3 s.testListWorkflowHelper(numOfWorkflows, pageSize, request, id, wt, false) } func (s *PinotIntegrationSuite) TestListWorkflow_SearchAfter() { id := "pinot-integration-list-workflow-searchAfter-test" wt := "pinot-integration-list-workflow-searchAfter-test-type" tl := "pinot-integration-list-workflow-searchAfter-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) numOfWorkflows := defaultTestValueOfESIndexMaxResultWindow + 1 // == 6 pageSize := 4 s.testListWorkflowHelper(numOfWorkflows, pageSize, request, id, wt, false) } func (s *PinotIntegrationSuite) TestListWorkflow_OrQuery() { id := "pinot-integration-list-workflow-or-query-test" wt := "pinot-integration-list-workflow-or-query-test-type" tl := "pinot-integration-list-workflow-or-query-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) ctx, cancel := createContext() defer cancel() // start 3 workflows key := definition.CustomIntField attrValBytes, _ := json.Marshal(1) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ key: attrValBytes, }, } request.SearchAttributes = searchAttr we1, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) request.RequestID = uuid.New() request.WorkflowID = id + "-2" attrValBytes, _ = json.Marshal(2) searchAttr.IndexedFields[key] = attrValBytes we2, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) request.RequestID = uuid.New() request.WorkflowID = id + "-3" attrValBytes, _ = json.Marshal(3) searchAttr.IndexedFields[key] = attrValBytes we3, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) time.Sleep(waitForPinotToSettle) // query 1 workflow with search attr query1 := fmt.Sprintf(`CustomIntField = %d`, 1) var openExecution *types.WorkflowExecutionInfo listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: defaultTestValueOfESIndexMaxResultWindow, Query: query1, } for i := 0; i < numberOfRetry; i++ { resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) s.Nil(err) if len(resp.GetExecutions()) == 1 { openExecution = resp.GetExecutions()[0] break } time.Sleep(waitTimeBetweenRetry) } s.NotNil(openExecution) s.Equal(we1.GetRunID(), openExecution.GetExecution().GetRunID()) s.True(openExecution.GetExecutionTime() >= openExecution.GetStartTime()) searchValBytes := openExecution.SearchAttributes.GetIndexedFields()[key] var searchVal int json.Unmarshal(searchValBytes, &searchVal) s.Equal(1, searchVal) // query with or clause query2 := fmt.Sprintf(`CustomIntField = %d or CustomIntField = %d`, 1, 2) listRequest.Query = query2 var openExecutions []*types.WorkflowExecutionInfo for i := 0; i < numberOfRetry; i++ { resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) s.Nil(err) if len(resp.GetExecutions()) == 2 { openExecutions = resp.GetExecutions() break } time.Sleep(waitTimeBetweenRetry) } // TODO: need to clean up or every time we run, we have to delete the table. s.Equal(2, len(openExecutions)) e1 := openExecutions[0] e2 := openExecutions[1] if e1.GetExecution().GetRunID() != we1.GetRunID() { // results are sorted by [CloseTime,RunID] desc, so find the correct mapping first e1, e2 = e2, e1 } s.Equal(we1.GetRunID(), e1.GetExecution().GetRunID()) s.Equal(we2.GetRunID(), e2.GetExecution().GetRunID()) searchValBytes = e2.SearchAttributes.GetIndexedFields()[key] json.Unmarshal(searchValBytes, &searchVal) s.Equal(2, searchVal) // query for open query3 := fmt.Sprintf(`(CustomIntField = %d or CustomIntField = %d) and CloseTime = missing`, 2, 3) listRequest.Query = query3 for i := 0; i < numberOfRetry; i++ { resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) s.Nil(err) if len(resp.GetExecutions()) == 2 { openExecutions = resp.GetExecutions() break } time.Sleep(waitTimeBetweenRetry) } s.Equal(2, len(openExecutions)) e1 = openExecutions[0] e2 = openExecutions[1] s.Equal(we3.GetRunID(), e1.GetExecution().GetRunID()) s.Equal(we2.GetRunID(), e2.GetExecution().GetRunID()) searchValBytes = e1.SearchAttributes.GetIndexedFields()[key] json.Unmarshal(searchValBytes, &searchVal) s.Equal(3, searchVal) } // To test last page search trigger max window size error func (s *PinotIntegrationSuite) TestListWorkflow_MaxWindowSize() { id := "pinot-integration-list-workflow-max-window-size-test" wt := "pinot-integration-list-workflow-max-window-size-test-type" tl := "pinot-integration-list-workflow-max-window-size-test-tasklist" startRequest := s.createStartWorkflowExecutionRequest(id, wt, tl) ctx, cancel := createContext() defer cancel() for i := 0; i < defaultTestValueOfESIndexMaxResultWindow; i++ { startRequest.RequestID = uuid.New() startRequest.WorkflowID = id + strconv.Itoa(i) _, err := s.Engine.StartWorkflowExecution(ctx, startRequest) s.Nil(err) } time.Sleep(waitForPinotToSettle) var listResp *types.ListWorkflowExecutionsResponse var nextPageToken []byte listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: int32(defaultTestValueOfESIndexMaxResultWindow), NextPageToken: nextPageToken, Query: fmt.Sprintf(`WorkflowType = '%s' and CloseTime = missing`, wt), } // get first page for i := 0; i < numberOfRetry; i++ { resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) s.Nil(err) if len(resp.GetExecutions()) == defaultTestValueOfESIndexMaxResultWindow { listResp = resp break } time.Sleep(waitTimeBetweenRetry) } s.NotNil(listResp) s.True(len(listResp.GetNextPageToken()) != 0) // the last request listRequest.NextPageToken = listResp.GetNextPageToken() resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) s.Nil(err) s.True(len(resp.GetExecutions()) == 0) s.True(len(resp.GetNextPageToken()) == 0) } func (s *PinotIntegrationSuite) TestListWorkflow_OrderBy() { id := "pinot-integration-list-workflow-order-by-test" wt := "pinot-integration-list-workflow-order-by-test-type" tl := "pinot-integration-list-workflow-order-by-test-tasklist" startRequest := s.createStartWorkflowExecutionRequest(id, wt, tl) ctx, cancel := createContext() defer cancel() for i := 0; i < defaultTestValueOfESIndexMaxResultWindow+1; i++ { // start 6 startRequest.RequestID = uuid.New() startRequest.WorkflowID = id + strconv.Itoa(i) if i < defaultTestValueOfESIndexMaxResultWindow-1 { // 4 workflow has search attr intVal, _ := json.Marshal(i) doubleVal, _ := json.Marshal(float64(i)) strVal, _ := json.Marshal(strconv.Itoa(i)) timeVal, _ := json.Marshal(time.Now()) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ definition.CustomIntField: intVal, definition.CustomDoubleField: doubleVal, definition.CustomKeywordField: strVal, definition.CustomDatetimeField: timeVal, }, } startRequest.SearchAttributes = searchAttr } else { startRequest.SearchAttributes = &types.SearchAttributes{} } _, err := s.Engine.StartWorkflowExecution(ctx, startRequest) s.Nil(err) } time.Sleep(waitForPinotToSettle) // desc := "desc" asc := "asc" queryTemplate := `WorkflowType = "%s" order by %s %s` pageSize := int32(defaultTestValueOfESIndexMaxResultWindow) // order by CloseTime asc query1 := fmt.Sprintf(queryTemplate, wt, definition.CloseTime, asc) var openExecutions []*types.WorkflowExecutionInfo listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: pageSize, Query: query1, } for i := 0; i < numberOfRetry; i++ { resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) s.Nil(err) if int32(len(resp.GetExecutions())) == listRequest.GetPageSize() { openExecutions = resp.GetExecutions() break } time.Sleep(waitTimeBetweenRetry) } s.NotNil(openExecutions) for i := int32(1); i < pageSize; i++ { s.True(openExecutions[i-1].GetCloseTime() <= openExecutions[i].GetCloseTime()) } // comment out things below, because json index column can't use order by // greatest effort to reduce duplicate code // testHelper := func(query, searchAttrKey string, prevVal, currVal interface{}) { // listRequest.Query = query // listRequest.NextPageToken = []byte{} // resp, err := s.Engine.ListWorkflowExecutions(createContext(), listRequest) // s.Nil(err) // openExecutions = resp.GetExecutions() // dec := json.NewDecoder(bytes.NewReader(openExecutions[0].GetSearchAttributes().GetIndexedFields()[searchAttrKey])) // dec.UseNumber() // err = dec.Decode(&prevVal) // s.Nil(err) // for i := int32(1); i < pageSize; i++ { // indexedFields := openExecutions[i].GetSearchAttributes().GetIndexedFields() // searchAttrBytes, ok := indexedFields[searchAttrKey] // if !ok { // last one doesn't have search attr // s.Equal(pageSize-1, i) // break // } // dec := json.NewDecoder(bytes.NewReader(searchAttrBytes)) // dec.UseNumber() // err = dec.Decode(&currVal) // s.Nil(err) // var v1, v2 interface{} // switch searchAttrKey { // case definition.CustomIntField: // v1, _ = prevVal.(json.Number).Int64() // v2, _ = currVal.(json.Number).Int64() // s.True(v1.(int64) >= v2.(int64)) // case definition.CustomDoubleField: // v1, _ := strconv.ParseFloat(fmt.Sprint(prevVal), 64) // v2, _ := strconv.ParseFloat(fmt.Sprint(currVal), 64) // s.True(v1 >= v2) // case definition.CustomKeywordField: // s.True(prevVal.(string) >= currVal.(string)) // case definition.CustomDatetimeField: // v1, _ = strconv.ParseInt(fmt.Sprint(prevVal), 10, 64) // v2, _ = strconv.ParseInt(fmt.Sprint(currVal), 10, 64) // s.True(v1.(int64) >= v2.(int64)) // } // prevVal = currVal // } // listRequest.NextPageToken = resp.GetNextPageToken() // resp, err = s.Engine.ListWorkflowExecutions(createContext(), listRequest) // last page // s.Nil(err) // s.Equal(1, len(resp.GetExecutions())) // } // // // order by CustomIntField desc // field := definition.CustomIntField // query := fmt.Sprintf(queryTemplate, wt, field, desc) // var int1, int2 int // testHelper(query, field, int1, int2) // // // order by CustomDoubleField desc // field = definition.CustomDoubleField // query = fmt.Sprintf(queryTemplate, wt, field, desc) // var double1, double2 float64 // testHelper(query, field, double1, double2) // // // order by CustomKeywordField desc // field = definition.CustomKeywordField // query = fmt.Sprintf(queryTemplate, wt, field, desc) // var s1, s2 string // testHelper(query, field, s1, s2) // // // order by CustomDatetimeField desc // field = definition.CustomDatetimeField // query = fmt.Sprintf(queryTemplate, wt, field, desc) // var t1, t2 time.Time // testHelper(query, field, t1, t2) } func (s *PinotIntegrationSuite) testListWorkflowHelper(numOfWorkflows, pageSize int, startRequest *types.StartWorkflowExecutionRequest, wid, wType string, isScan bool) { ctx, cancel := createContext() defer cancel() // start enough number of workflows for i := 0; i < numOfWorkflows; i++ { startRequest.RequestID = uuid.New() startRequest.WorkflowID = wid + strconv.Itoa(i) _, err := s.Engine.StartWorkflowExecution(ctx, startRequest) s.Nil(err) } time.Sleep(waitForPinotToSettle) var openExecutions []*types.WorkflowExecutionInfo var nextPageToken []byte listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: int32(pageSize), NextPageToken: nextPageToken, Query: fmt.Sprintf(`WorkflowType = '%s' and CloseTime = missing`, wType), } // test first page for i := 0; i < numberOfRetry; i++ { var resp *types.ListWorkflowExecutionsResponse var err error if isScan { resp, err = s.Engine.ScanWorkflowExecutions(ctx, listRequest) } else { resp, err = s.Engine.ListWorkflowExecutions(ctx, listRequest) } s.Nil(err) if len(resp.GetExecutions()) == pageSize { openExecutions = resp.GetExecutions() nextPageToken = resp.GetNextPageToken() break } time.Sleep(waitTimeBetweenRetry) } s.NotNil(openExecutions) s.NotNil(nextPageToken) s.True(len(nextPageToken) > 0) // test last page listRequest.NextPageToken = nextPageToken inIf := false for i := 0; i < numberOfRetry; i++ { var resp *types.ListWorkflowExecutionsResponse var err error if isScan { resp, err = s.Engine.ScanWorkflowExecutions(ctx, listRequest) } else { resp, err = s.Engine.ListWorkflowExecutions(ctx, listRequest) } s.Nil(err) // ans, _ := json.Marshal(resp.GetExecutions()) // panic(fmt.Sprintf("ABUCSDK: %s", ans)) if len(resp.GetExecutions()) == numOfWorkflows-pageSize { inIf = true openExecutions = resp.GetExecutions() nextPageToken = resp.GetNextPageToken() break } time.Sleep(waitTimeBetweenRetry) } s.True(inIf) s.NotNil(openExecutions) s.Nil(nextPageToken) } func (s *PinotIntegrationSuite) TestScanWorkflow() { id := "pinot-integration-scan-workflow-test" wt := "pinot-integration-scan-workflow-test-type" tl := "pinot-integration-scan-workflow-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s"`, id) s.testHelperForReadOnce(we.GetRunID(), query, true, false) } func (s *PinotIntegrationSuite) TestScanWorkflow_SearchAttribute() { id := "pinot-integration-scan-workflow-search-attr-test" wt := "pinot-integration-scan-workflow-search-attr-test-type" tl := "pinot-integration-scan-workflow-search-attr-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } request.SearchAttributes = searchAttr ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s" and %s = "%s"`, id, s.testSearchAttributeKey, s.testSearchAttributeVal) s.testHelperForReadOnce(we.GetRunID(), query, true, false) } func (s *PinotIntegrationSuite) TestScanWorkflow_PageToken() { id := "pinot-integration-scan-workflow-token-test" wt := "pinot-integration-scan-workflow-token-test-type" tl := "pinot-integration-scan-workflow-token-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } numOfWorkflows := 4 pageSize := 3 s.testListWorkflowHelper(numOfWorkflows, pageSize, request, id, wt, true) } func (s *PinotIntegrationSuite) TestCountWorkflow() { id := "pinot-integration-count-workflow-test" wt := "pinot-integration-count-workflow-test-type" tl := "pinot-integration-count-workflow-test-tasklist" request := s.createStartWorkflowExecutionRequest(id, wt, tl) attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } request.SearchAttributes = searchAttr ctx, cancel := createContext() defer cancel() _, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) query := fmt.Sprintf(`WorkflowID = "%s" and %s = "%s"`, id, s.testSearchAttributeKey, s.testSearchAttributeVal) countRequest := &types.CountWorkflowExecutionsRequest{ Domain: s.DomainName, Query: query, } var resp *types.CountWorkflowExecutionsResponse for i := 0; i < numberOfRetry; i++ { resp, err = s.Engine.CountWorkflowExecutions(ctx, countRequest) s.Nil(err) if resp.GetCount() == int64(1) { break } time.Sleep(waitTimeBetweenRetry) } s.Equal(int64(1), resp.GetCount()) query = fmt.Sprintf(`WorkflowID = "%s" and %s = "%s"`, id, s.testSearchAttributeKey, "noMatch") countRequest.Query = query resp, err = s.Engine.CountWorkflowExecutions(ctx, countRequest) s.Nil(err) s.Equal(int64(0), resp.GetCount()) } func (s *PinotIntegrationSuite) TestUpsertWorkflowExecution() { id := "pinot-integration-upsert-workflow-test" wt := "pinot-integration-upsert-workflow-test-type" tl := "pinot-integration-upsert-workflow-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) decisionCount := 0 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { upsertDecision := &types.Decision{ DecisionType: types.DecisionTypeUpsertWorkflowSearchAttributes.Ptr(), UpsertWorkflowSearchAttributesDecisionAttributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{}} // handle first upsert if decisionCount == 0 { decisionCount++ attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) upsertSearchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } upsertDecision.UpsertWorkflowSearchAttributesDecisionAttributes.SearchAttributes = upsertSearchAttr return nil, []*types.Decision{upsertDecision}, nil } // handle second upsert, which update existing field and add new field if decisionCount == 1 { decisionCount++ upsertDecision.UpsertWorkflowSearchAttributesDecisionAttributes.SearchAttributes = getPinotUpsertSearchAttributes() return nil, []*types.Decision{upsertDecision}, nil } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, StickyTaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } // process 1st decision and assert decision is handled correctly. _, newTask, err := poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( false, false, true, true, int64(0), 1, true, nil) s.Nil(err) s.NotNil(newTask) s.NotNil(newTask.DecisionTask) s.Equal(int64(3), newTask.DecisionTask.GetPreviousStartedEventID()) s.Equal(int64(7), newTask.DecisionTask.GetStartedEventID()) s.Equal(4, len(newTask.DecisionTask.History.Events)) s.Equal(types.EventTypeDecisionTaskCompleted, newTask.DecisionTask.History.Events[0].GetEventType()) s.Equal(types.EventTypeUpsertWorkflowSearchAttributes, newTask.DecisionTask.History.Events[1].GetEventType()) s.Equal(types.EventTypeDecisionTaskScheduled, newTask.DecisionTask.History.Events[2].GetEventType()) s.Equal(types.EventTypeDecisionTaskStarted, newTask.DecisionTask.History.Events[3].GetEventType()) time.Sleep(waitForPinotToSettle) // verify upsert data is on Pinot listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.DomainName, PageSize: int32(2), // Query: fmt.Sprintf(`WorkflowType = '%s' and CloseTime = missing`, wt), Query: fmt.Sprintf(`WorkflowType = '%s' and CloseTime = missing`, wt), } verified := false for i := 0; i < numberOfRetry; i++ { resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) s.Nil(err) if len(resp.GetExecutions()) == 1 { execution := resp.GetExecutions()[0] retrievedSearchAttr := execution.SearchAttributes if retrievedSearchAttr != nil && len(retrievedSearchAttr.GetIndexedFields()) > 0 { searchValBytes := retrievedSearchAttr.GetIndexedFields()[s.testSearchAttributeKey] var searchVal string json.Unmarshal(searchValBytes, &searchVal) s.Equal(s.testSearchAttributeVal, searchVal) verified = true break } } time.Sleep(waitTimeBetweenRetry) } s.True(verified) // process 2nd decision and assert decision is handled correctly. _, newTask, err = poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( false, false, true, true, int64(0), 1, true, nil) s.Nil(err) s.NotNil(newTask) s.NotNil(newTask.DecisionTask) s.Equal(4, len(newTask.DecisionTask.History.Events)) s.Equal(types.EventTypeDecisionTaskCompleted, newTask.DecisionTask.History.Events[0].GetEventType()) s.Equal(types.EventTypeUpsertWorkflowSearchAttributes, newTask.DecisionTask.History.Events[1].GetEventType()) s.Equal(types.EventTypeDecisionTaskScheduled, newTask.DecisionTask.History.Events[2].GetEventType()) s.Equal(types.EventTypeDecisionTaskStarted, newTask.DecisionTask.History.Events[3].GetEventType()) time.Sleep(waitForPinotToSettle) // verify upsert data is on Pinot s.testListResultForUpsertSearchAttributes(listRequest) } func (s *PinotIntegrationSuite) testListResultForUpsertSearchAttributes(listRequest *types.ListWorkflowExecutionsRequest) { verified := false ctx, cancel := createContext() defer cancel() for i := 0; i < numberOfRetry; i++ { resp, err := s.Engine.ListWorkflowExecutions(ctx, listRequest) s.Nil(err) // res2B, _ := json.Marshal(resp.GetExecutions()) // panic(fmt.Sprintf("ABCDDDBUG: %s", listRequest.Query)) if len(resp.GetExecutions()) == 1 { execution := resp.GetExecutions()[0] retrievedSearchAttr := execution.SearchAttributes if retrievedSearchAttr != nil && len(retrievedSearchAttr.GetIndexedFields()) == 3 { // if retrievedSearchAttr != nil && len(retrievedSearchAttr.GetIndexedFields()) > 0 { fields := retrievedSearchAttr.GetIndexedFields() searchValBytes := fields[s.testSearchAttributeKey] var searchVal string err := json.Unmarshal(searchValBytes, &searchVal) s.Nil(err) s.Equal("another string", searchVal) searchValBytes2 := fields[definition.CustomIntField] var searchVal2 int err = json.Unmarshal(searchValBytes2, &searchVal2) s.Nil(err) s.Equal(123, searchVal2) binaryChecksumsBytes := fields[definition.BinaryChecksums] var binaryChecksums []string err = json.Unmarshal(binaryChecksumsBytes, &binaryChecksums) s.Nil(err) s.Equal([]string{"binary-v1", "binary-v2"}, binaryChecksums) verified = true break } } time.Sleep(waitTimeBetweenRetry) } s.True(verified) } func getPinotUpsertSearchAttributes() *types.SearchAttributes { attrValBytes1, _ := json.Marshal("another string") attrValBytes2, _ := json.Marshal(123) binaryChecksums, _ := json.Marshal([]string{"binary-v1", "binary-v2"}) upsertSearchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ definition.CustomStringField: attrValBytes1, definition.CustomIntField: attrValBytes2, definition.BinaryChecksums: binaryChecksums, }, } return upsertSearchAttr } func (s *PinotIntegrationSuite) TestUpsertWorkflowExecution_InvalidKey() { id := "pinot-integration-upsert-workflow-failed-test" wt := "pinot-integration-upsert-workflow-failed-test-type" tl := "pinot-integration-upsert-workflow-failed-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { upsertDecision := &types.Decision{ DecisionType: types.DecisionTypeUpsertWorkflowSearchAttributes.Ptr(), UpsertWorkflowSearchAttributesDecisionAttributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: &types.SearchAttributes{ IndexedFields: map[string][]byte{ "INVALIDKEY": []byte(`1`), }, }, }} return nil, []*types.Decision{upsertDecision}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, StickyTaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.Nil(err) defer cancel() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) s.Nil(err) history := historyResponse.History decisionFailedEvent := history.GetEvents()[3] s.Equal(types.EventTypeDecisionTaskFailed, decisionFailedEvent.GetEventType()) failedDecisionAttr := decisionFailedEvent.DecisionTaskFailedEventAttributes s.Equal(types.DecisionTaskFailedCauseBadSearchAttributes, failedDecisionAttr.GetCause()) s.True(len(failedDecisionAttr.GetDetails()) > 0) } ================================================ FILE: host/pinotutils/pinotClient.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package pinotutils import ( "github.com/startreedata/pinot-client-go/pinot" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" pnt "github.com/uber/cadence/common/pinot" ) func CreatePinotClient(s *suite.Suite, pinotConfig *config.PinotVisibilityConfig, logger log.Logger) pnt.GenericClient { pinotRawClient, err := pinot.NewFromBrokerList([]string{pinotConfig.Broker}) s.Require().NoError(err) pinotClient := pnt.NewPinotClient(pinotRawClient, logger, pinotConfig) return pinotClient } ================================================ FILE: host/query_workflow_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "bytes" "context" "encoding/binary" "errors" "sync/atomic" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) func (s *IntegrationSuite) TestQueryWorkflow_Sticky() { id := "interation-query-workflow-test-sticky" wt := "interation-query-workflow-test-sticky-type" tl := "interation-query-workflow-test-sticky-tasklist" stl := "interation-query-workflow-test-sticky-tasklist-sticky" identity := "worker1" activityName := "activity_type1" queryType := "test-query" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl stickyTaskList := &types.TaskList{} stickyTaskList.Name = stl stickyScheduleToStartTimeoutSeconds := common.Int32Ptr(10) // decider logic activityScheduled := false activityData := int32(1) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } queryHandler := func(task *types.PollForDecisionTaskResponse) ([]byte, error) { s.NotNil(task.Query) s.NotNil(task.Query.QueryType) if task.Query.QueryType == queryType { return []byte("query-result"), nil } return nil, errors.New("unknown-query-type") } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, QueryHandler: queryHandler, Logger: s.Logger, T: s.T(), StickyTaskList: stickyTaskList, StickyScheduleToStartTimeoutSeconds: stickyScheduleToStartTimeoutSeconds, } // Make a request with stick tasklist to refresh the stickiness, otherwise we won't be able to add // decisions to the sticky tasklist ctx, cancel := createContext() defer cancel() resp, err := poller.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: poller.Domain, TaskList: poller.StickyTaskList, Identity: poller.Identity, }) s.NotNil(resp) s.Equal(0, len(resp.TaskToken)) s.Nil(err) // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel = createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution: response", tag.WorkflowRunID(we.RunID)) // Make first decision to schedule activity _, err = poller.PollAndProcessDecisionTaskWithAttempt(false, false, false, true, int64(0)) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) type QueryResult struct { Resp *types.QueryWorkflowResponse Err error } queryResultCh := make(chan QueryResult) queryWorkflowFn := func(queryType string) { ctx, cancel := createContext() defer cancel() queryResp, err := s.Engine.QueryWorkflow(ctx, &types.QueryWorkflowRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, Query: &types.WorkflowQuery{ QueryType: queryType, }, }) queryResultCh <- QueryResult{Resp: queryResp, Err: err} } // call QueryWorkflow in separate goroutinue (because it is blocking). That will generate a query task go queryWorkflowFn(queryType) // process that query task, which should respond via RespondQueryTaskCompleted for { // loop until process the query task isQueryTask, errInner := poller.PollAndProcessDecisionTaskWithSticky(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(errInner) if isQueryTask { break } } // wait until query result is ready queryResult := <-queryResultCh s.NoError(queryResult.Err) s.NotNil(queryResult.Resp) s.NotNil(queryResult.Resp.QueryResult) queryResultString := string(queryResult.Resp.QueryResult) s.Equal("query-result", queryResultString) go queryWorkflowFn("invalid-query-type") for { // loop until process the query task isQueryTask, errInner := poller.PollAndProcessDecisionTaskWithSticky(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(errInner) if isQueryTask { break } } queryResult = <-queryResultCh s.NotNil(queryResult.Err) queryFailError, ok := queryResult.Err.(*types.QueryFailedError) s.True(ok) s.Equal("unknown-query-type", queryFailError.Message) } func (s *IntegrationSuite) TestQueryWorkflow_StickyTimeout() { id := "interation-query-workflow-test-sticky-timeout" wt := "interation-query-workflow-test-sticky-timeout-type" tl := "interation-query-workflow-test-sticky-timeout-tasklist" stl := "interation-query-workflow-test-sticky-timeout-tasklist-sticky" identity := "worker1" activityName := "activity_type1" queryType := "test-query" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl stickyTaskList := &types.TaskList{} stickyTaskList.Name = stl stickyScheduleToStartTimeoutSeconds := common.Int32Ptr(10) // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic activityScheduled := false activityData := int32(1) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } queryHandler := func(task *types.PollForDecisionTaskResponse) ([]byte, error) { s.NotNil(task.Query) s.NotNil(task.Query.QueryType) if task.Query.QueryType == queryType { return []byte("query-result"), nil } return nil, errors.New("unknown-query-type") } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, QueryHandler: queryHandler, Logger: s.Logger, T: s.T(), StickyTaskList: stickyTaskList, StickyScheduleToStartTimeoutSeconds: stickyScheduleToStartTimeoutSeconds, } // Make first decision to schedule activity _, err := poller.PollAndProcessDecisionTaskWithAttempt(false, false, false, true, int64(0)) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) type QueryResult struct { Resp *types.QueryWorkflowResponse Err error } queryResultCh := make(chan QueryResult) queryWorkflowFn := func(queryType string) { ctx, cancel := createContext() defer cancel() queryResp, err := s.Engine.QueryWorkflow(ctx, &types.QueryWorkflowRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, Query: &types.WorkflowQuery{ QueryType: queryType, }, }) queryResultCh <- QueryResult{Resp: queryResp, Err: err} } // call QueryWorkflow in separate goroutinue (because it is blocking). That will generate a query task go queryWorkflowFn(queryType) // process that query task, which should respond via RespondQueryTaskCompleted for { // loop until process the query task // here we poll on normal tasklist, to simulate a worker crash and restart // on the server side, server will first try the sticky tasklist and then the normal tasklist isQueryTask, errInner := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(errInner) if isQueryTask { break } } // wait until query result is ready queryResult := <-queryResultCh s.NoError(queryResult.Err) s.NotNil(queryResult.Resp) s.NotNil(queryResult.Resp.QueryResult) queryResultString := string(queryResult.Resp.QueryResult) s.Equal("query-result", queryResultString) } func (s *IntegrationSuite) TestQueryWorkflow_NonSticky() { id := "integration-query-workflow-test-non-sticky" wt := "integration-query-workflow-test-non-sticky-type" tl := "integration-query-workflow-test-non-sticky-tasklist" identity := "worker1" activityName := "activity_type1" queryType := "test-query" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic activityScheduled := false activityData := int32(1) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } queryHandler := func(task *types.PollForDecisionTaskResponse) ([]byte, error) { s.NotNil(task.Query) s.NotNil(task.Query.QueryType) if task.Query.QueryType == queryType { return []byte("query-result"), nil } return nil, errors.New("unknown-query-type") } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, QueryHandler: queryHandler, Logger: s.Logger, T: s.T(), } // Make first decision to schedule activity _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) type QueryResult struct { Resp *types.QueryWorkflowResponse Err error } queryResultCh := make(chan QueryResult) queryWorkflowFn := func(queryType string, rejectCondition *types.QueryRejectCondition) { ctx, cancel := createContext() defer cancel() queryResp, err := s.Engine.QueryWorkflow(ctx, &types.QueryWorkflowRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, Query: &types.WorkflowQuery{ QueryType: queryType, }, QueryRejectCondition: rejectCondition, }) queryResultCh <- QueryResult{Resp: queryResp, Err: err} } // call QueryWorkflow in separate goroutinue (because it is blocking). That will generate a query task go queryWorkflowFn(queryType, nil) // process that query task, which should respond via RespondQueryTaskCompleted for { // loop until process the query task isQueryTask, errInner := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(errInner) if isQueryTask { break } } // wait until query result is ready queryResult := <-queryResultCh s.NoError(queryResult.Err) s.NotNil(queryResult.Resp) s.NotNil(queryResult.Resp.QueryResult) s.Nil(queryResult.Resp.QueryRejected) queryResultString := string(queryResult.Resp.QueryResult) s.Equal("query-result", queryResultString) go queryWorkflowFn("invalid-query-type", nil) for { // loop until process the query task isQueryTask, errInner := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(errInner) if isQueryTask { break } } queryResult = <-queryResultCh s.NotNil(queryResult.Err) queryFailError, ok := queryResult.Err.(*types.QueryFailedError) s.True(ok) s.Equal("unknown-query-type", queryFailError.Message) // advance the state of the decider _, err = poller.PollAndProcessDecisionTask(false, false) s.NoError(err) go queryWorkflowFn(queryType, nil) // process that query task, which should respond via RespondQueryTaskCompleted for { // loop until process the query task isQueryTask, errInner := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(errInner) if isQueryTask { break } } // wait until query result is ready queryResult = <-queryResultCh s.NoError(queryResult.Err) s.NotNil(queryResult.Resp) s.NotNil(queryResult.Resp.QueryResult) s.Nil(queryResult.Resp.QueryRejected) queryResultString = string(queryResult.Resp.QueryResult) s.Equal("query-result", queryResultString) rejectCondition := types.QueryRejectConditionNotOpen go queryWorkflowFn(queryType, &rejectCondition) queryResult = <-queryResultCh s.NoError(queryResult.Err) s.NotNil(queryResult.Resp) s.Nil(queryResult.Resp.QueryResult) s.NotNil(queryResult.Resp.QueryRejected.CloseStatus) s.Equal(types.WorkflowExecutionCloseStatusCompleted, *queryResult.Resp.QueryRejected.CloseStatus) rejectCondition = types.QueryRejectConditionNotCompletedCleanly go queryWorkflowFn(queryType, &rejectCondition) for { // loop until process the query task isQueryTask, errInner := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(errInner) if isQueryTask { break } } // wait until query result is ready queryResult = <-queryResultCh s.NoError(queryResult.Err) s.NotNil(queryResult.Resp) s.NotNil(queryResult.Resp.QueryResult) s.Nil(queryResult.Resp.QueryRejected) queryResultString = string(queryResult.Resp.QueryResult) s.Equal("query-result", queryResultString) } func (s *IntegrationSuite) TestQueryWorkflow_Consistent_PiggybackQuery() { id := "integration-query-workflow-test-consistent-piggyback-query" wt := "integration-query-workflow-test-consistent-piggyback-query-type" tl := "integration-query-workflow-test-consistent-piggyback-query-tasklist" identity := "worker1" activityName := "activity_type1" queryType := "test-query" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic activityScheduled := false activityData := int32(1) var handledSignal atomic.Value handledSignal.Store(false) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { handledSignal.Store(true) return nil, []*types.Decision{}, nil } } } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } queryHandler := func(task *types.PollForDecisionTaskResponse) ([]byte, error) { s.NotNil(task.Query) s.NotNil(task.Query.QueryType) if task.Query.QueryType == queryType { return []byte("query-result"), nil } return nil, errors.New("unknown-query-type") } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, QueryHandler: queryHandler, Logger: s.Logger, T: s.T(), } // Make first decision to schedule activity _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) type QueryResult struct { Resp *types.QueryWorkflowResponse Err error } queryResultCh := make(chan QueryResult) queryWorkflowFn := func(queryType string, rejectCondition *types.QueryRejectCondition) { // before the query is answer the signal is not handled because the decision task is not dispatched // to the worker yet s.False(handledSignal.Load().(bool)) ctx, cancel := createContext() defer cancel() queryResp, err := s.Engine.QueryWorkflow(ctx, &types.QueryWorkflowRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, Query: &types.WorkflowQuery{ QueryType: queryType, }, QueryRejectCondition: rejectCondition, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }) // after the query is answered the signal is handled because query is consistent and since // signal came before query signal must be handled by the time query returns s.True(handledSignal.Load().(bool)) queryResultCh <- QueryResult{Resp: queryResp, Err: err} } // send signal to ensure there is an outstanding decision task to dispatch query on // otherwise query will just go through matching signalName := "my signal" signalInput := []byte("my signal input.") ctx, cancel = createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, SignalName: signalName, Input: signalInput, Identity: identity, }) s.Nil(err) // call QueryWorkflow in separate goroutine (because it is blocking). That will generate a query task // notice that the query comes after signal here but is consistent so it should reflect the state of the signal having been applied go queryWorkflowFn(queryType, nil) // ensure query has had enough time to at least start before a decision task is polled // if the decision task containing the signal is polled before query is started it will not impact // correctness but it will mean query will be able to be dispatched directly after signal // without being attached to the decision task signal is on <-time.After(time.Second) isQueryTask, _, errInner := poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( false, false, false, false, int64(0), 5, false, &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeAnswered.Ptr(), Answer: []byte("consistent query result"), }) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(errInner) s.False(isQueryTask) queryResult := <-queryResultCh s.NoError(queryResult.Err) s.NotNil(queryResult.Resp) s.NotNil(queryResult.Resp.QueryResult) s.Nil(queryResult.Resp.QueryRejected) queryResultString := string(queryResult.Resp.QueryResult) s.Equal("consistent query result", queryResultString) } func (s *IntegrationSuite) TestQueryWorkflow_Consistent_Timeout() { id := "integration-query-workflow-test-consistent-timeout" wt := "integration-query-workflow-test-consistent-timeout-type" tl := "integration-query-workflow-test-consistent-timeout-tasklist" identity := "worker1" activityName := "activity_type1" queryType := "test-query" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), // ensure longer than time takes to handle signal Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic activityScheduled := false activityData := int32(1) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), // ensure longer than time it takes to handle signal StartToCloseTimeoutSeconds: common.Int32Ptr(50), // ensure longer than time takes to handle signal HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { <-time.After(3 * time.Second) // take longer to respond to the decision task than the query waits for return nil, []*types.Decision{}, nil } } } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } queryHandler := func(task *types.PollForDecisionTaskResponse) ([]byte, error) { s.NotNil(task.Query) s.NotNil(task.Query.QueryType) if task.Query.QueryType == queryType { return []byte("query-result"), nil } return nil, errors.New("unknown-query-type") } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, QueryHandler: queryHandler, Logger: s.Logger, T: s.T(), } // Make first decision to schedule activity _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) type QueryResult struct { Resp *types.QueryWorkflowResponse Err error } queryResultCh := make(chan QueryResult) queryWorkflowFn := func(queryType string, rejectCondition *types.QueryRejectCondition) { shortCtx, cancel := context.WithTimeout(context.Background(), time.Second) queryResp, err := s.Engine.QueryWorkflow(shortCtx, &types.QueryWorkflowRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, Query: &types.WorkflowQuery{ QueryType: queryType, }, QueryRejectCondition: rejectCondition, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }) cancel() queryResultCh <- QueryResult{Resp: queryResp, Err: err} } signalName := "my signal" signalInput := []byte("my signal input.") ctx, cancel = createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, SignalName: signalName, Input: signalInput, Identity: identity, }) s.Nil(err) // call QueryWorkflow in separate goroutine (because it is blocking). That will generate a query task // notice that the query comes after signal here but is consistent so it should reflect the state of the signal having been applied go queryWorkflowFn(queryType, nil) // ensure query has had enough time to at least start before a decision task is polled // if the decision task containing the signal is polled before query is started it will not impact // correctness but it will mean query will be able to be dispatched directly after signal // without being attached to the decision task signal is on <-time.After(time.Second) _, err = poller.PollAndProcessDecisionTask(false, false) s.NoError(err) // wait for query to timeout queryResult := <-queryResultCh s.Error(queryResult.Err) // got a timeout error } func (s *IntegrationSuite) TestQueryWorkflow_Consistent_BlockedByStarted_NonSticky() { id := "integration-query-workflow-test-consistent-blocked-by-started-non-sticky" wt := "integration-query-workflow-test-consistent-blocked-by-started-non-sticky-type" tl := "integration-query-workflow-test-consistent-blocked-by-started-non-sticky-tasklist" identity := "worker1" activityName := "activity_type1" queryType := "test-query" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), // ensure longer than time takes to handle signal Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic activityScheduled := false activityData := int32(1) var handledSignal atomic.Value handledSignal.Store(false) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), // ensure longer than time it takes to handle signal StartToCloseTimeoutSeconds: common.Int32Ptr(50), // ensure longer than time takes to handle signal HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { // wait for some time to force decision task to stay in started state while query is issued <-time.After(5 * time.Second) handledSignal.Store(true) return nil, []*types.Decision{}, nil } } } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } queryHandler := func(task *types.PollForDecisionTaskResponse) ([]byte, error) { s.NotNil(task.Query) s.NotNil(task.Query.QueryType) if task.Query.QueryType == queryType { return []byte("query-result"), nil } return nil, errors.New("unknown-query-type") } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, QueryHandler: queryHandler, Logger: s.Logger, T: s.T(), } // Make first decision to schedule activity _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) type QueryResult struct { Resp *types.QueryWorkflowResponse Err error } queryResultCh := make(chan QueryResult) queryWorkflowFn := func(queryType string, rejectCondition *types.QueryRejectCondition) { s.False(handledSignal.Load().(bool)) ctx, cancel := createContext() defer cancel() queryResp, err := s.Engine.QueryWorkflow(ctx, &types.QueryWorkflowRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, Query: &types.WorkflowQuery{ QueryType: queryType, }, QueryRejectCondition: rejectCondition, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }) s.True(handledSignal.Load().(bool)) queryResultCh <- QueryResult{Resp: queryResp, Err: err} } // send a signal that will take 5 seconds to handle // this causes the signal to still be outstanding at the time query arrives signalName := "my signal" signalInput := []byte("my signal input.") ctx, cancel = createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, SignalName: signalName, Input: signalInput, Identity: identity, }) s.Nil(err) go func() { // wait for decision task for signal to get started before querying workflow // since signal processing takes 5 seconds and we wait only one second to issue the query // at the time the query comes in there will be a started decision task // only once signal completes can queryWorkflow unblock <-time.After(time.Second) queryWorkflowFn(queryType, nil) }() _, err = poller.PollAndProcessDecisionTask(false, false) s.NoError(err) <-time.After(time.Second) // query should not have been dispatched on the decision task which contains signal // because signal was already outstanding by the time query arrived select { case <-queryResultCh: s.Fail("query should not be ready yet") default: } // now that started decision task completes poll for next task which will be a query task // containing the buffered query isQueryTask, err := poller.PollAndProcessDecisionTask(false, false) s.True(isQueryTask) s.NoError(err) queryResult := <-queryResultCh s.NoError(queryResult.Err) s.NotNil(queryResult.Resp) s.NotNil(queryResult.Resp.QueryResult) s.Nil(queryResult.Resp.QueryRejected) queryResultString := string(queryResult.Resp.QueryResult) s.Equal("query-result", queryResultString) } func (s *IntegrationSuite) TestQueryWorkflow_Consistent_NewDecisionTask_Sticky() { id := "integration-query-workflow-test-consistent-new-decision-task-sticky" wt := "integration-query-workflow-test-consistent-new-decision-task-sticky-type" tl := "integration-query-workflow-test-consistent-new-decision-task-sticky-tasklist" stl := "integration-query-workflow-test-consistent-new-decision-task-sticky-tasklist-sticky" identity := "worker1" activityName := "activity_type1" queryType := "test-query" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl stickyTaskList := &types.TaskList{} stickyTaskList.Name = stl stickyTaskList.Kind = types.TaskListKindSticky.Ptr() stickyScheduleToStartTimeoutSeconds := common.Int32Ptr(10) // decider logic activityScheduled := false activityData := int32(1) var handledSignal atomic.Value handledSignal.Store(false) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), // ensure longer than time it takes to handle signal StartToCloseTimeoutSeconds: common.Int32Ptr(50), // ensure longer than time takes to handle signal HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { // wait for some time to force decision task to stay in started state while query is issued <-time.After(5 * time.Second) handledSignal.Store(true) return nil, []*types.Decision{}, nil } } } return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } queryHandler := func(task *types.PollForDecisionTaskResponse) ([]byte, error) { s.NotNil(task.Query) s.NotNil(task.Query.QueryType) if task.Query.QueryType == queryType { return []byte("query-result"), nil } return nil, errors.New("unknown-query-type") } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, QueryHandler: queryHandler, Logger: s.Logger, T: s.T(), StickyTaskList: stickyTaskList, StickyScheduleToStartTimeoutSeconds: stickyScheduleToStartTimeoutSeconds, } // Make a request with stick tasklist to refresh the stickiness, otherwise we won't be able to add // decisions to the sticky tasklist ctx, cancel := createContext() defer cancel() resp, err := poller.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: poller.Domain, TaskList: poller.StickyTaskList, Identity: poller.Identity, }) s.NotNil(resp) s.Equal(0, len(resp.TaskToken)) s.Nil(err) // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), // ensure longer than time takes to handle signal Identity: identity, } ctx, cancel = createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // Make first decision to schedule activity _, err = poller.PollAndProcessDecisionTaskWithAttempt(false, false, false, true, int64(0)) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) type QueryResult struct { Resp *types.QueryWorkflowResponse Err error } queryResultCh := make(chan QueryResult) queryWorkflowFn := func(queryType string, rejectCondition *types.QueryRejectCondition) { s.False(handledSignal.Load().(bool)) ctx, cancel := createContext() defer cancel() queryResp, err := s.Engine.QueryWorkflow(ctx, &types.QueryWorkflowRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, Query: &types.WorkflowQuery{ QueryType: queryType, }, QueryRejectCondition: rejectCondition, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }) s.True(handledSignal.Load().(bool)) queryResultCh <- QueryResult{Resp: queryResp, Err: err} } // send a signal that will take 5 seconds to handle // this causes the signal to still be outstanding at the time query arrives signalName := "my signal" signalInput := []byte("my signal input.") ctx, cancel = createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, SignalName: signalName, Input: signalInput, Identity: identity, }) s.Nil(err) go func() { // wait for decision task for signal to get started before querying workflow // since signal processing takes 5 seconds and we wait only one second to issue the query // at the time the query comes in there will be a started decision task // only once signal completes can queryWorkflow unblock <-time.After(time.Second) // at this point there is a decision task started on the worker so this second signal will become buffered signalName := "my signal" signalInput := []byte("my signal input.") ctx, cancel := createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, SignalName: signalName, Input: signalInput, Identity: identity, }) s.Nil(err) queryWorkflowFn(queryType, nil) }() _, err = poller.PollAndProcessDecisionTaskWithSticky(false, false) s.NoError(err) <-time.After(time.Second) // query should not have been dispatched on the decision task which contains signal // because signal was already outstanding by the time query arrived select { case <-queryResultCh: s.Fail("query should not be ready yet") default: } // now poll for decision task which contains the query which was buffered isQueryTask, _, errInner := poller.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( false, false, true, true, int64(0), 5, false, &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeAnswered.Ptr(), Answer: []byte("consistent query result"), }) // the task should not be a query task because at the time outstanding decision task completed // there existed a buffered event which triggered the creation of a new decision task which query was dispatched on s.False(isQueryTask) s.NoError(errInner) queryResult := <-queryResultCh s.NoError(queryResult.Err) s.NotNil(queryResult.Resp) s.NotNil(queryResult.Resp.QueryResult) s.Nil(queryResult.Resp.QueryRejected) queryResultString := string(queryResult.Resp.QueryResult) s.Equal("consistent query result", queryResultString) } func (s *IntegrationSuite) TestQueryWorkflow_BeforeFirstDecision() { id := "integration-test-query-workflow-before-first-decision" wt := "integration-test-query-workflow-before-first-decision-type" tl := "integration-test-query-workflow-before-first-decision-tasklist" identity := "worker1" queryType := "test-query" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) workflowExecution := &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, } ctx, cancel = createContext() defer cancel() // query workflow without any decision task should produce an error _, err := s.Engine.QueryWorkflow(ctx, &types.QueryWorkflowRequest{ Domain: s.DomainName, Execution: workflowExecution, Query: &types.WorkflowQuery{ QueryType: queryType, }, }) s.IsType(&types.QueryFailedError{}, err) s.Equal("workflow must handle at least one decision task before it can be queried", err.(*types.QueryFailedError).Message) } ================================================ FILE: host/reset_workflow_test.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "bytes" "encoding/binary" "strconv" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) func (s *IntegrationSuite) TestResetWorkflow() { id := "integration-reset-workflow-test" wt := "integration-reset-workflow-test-type" tl := "integration-reset-workflow-test-taskqueue" identity := "worker1" workflowType := &types.WorkflowType{Name: wt} tasklist := &types.TaskList{Name: tl} // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: tasklist, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.NoError(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.GetRunID())) // workflow logic workflowComplete := false activityData := int32(1) activityCount := 3 isFirstTaskProcessed := false isSecondTaskProcessed := false var firstActivityCompletionEvent *types.HistoryEvent wtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !isFirstTaskProcessed { // Schedule 3 activities on first workflow task isFirstTaskProcessed = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) var scheduleActivityCommands []*types.Decision for i := 1; i <= activityCount; i++ { scheduleActivityCommands = append(scheduleActivityCommands, &types.Decision{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(i), ActivityType: &types.ActivityType{Name: "ResetActivity"}, TaskList: tasklist, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(100), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }) } return nil, scheduleActivityCommands, nil } else if !isSecondTaskProcessed { // Confirm one activity completion on second workflow task isSecondTaskProcessed = true for _, event := range history.Events[previousStartedEventID:] { if event.GetEventType() == types.EventTypeActivityTaskCompleted { firstActivityCompletionEvent = event return nil, []*types.Decision{}, nil } } } // Complete workflow after reset workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: tasklist, Identity: identity, DecisionHandler: wtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Process first workflow decision task to schedule activities _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessWorkflowTask", tag.Error(err)) s.NoError(err) // Process one activity task which also creates second workflow task err = poller.PollAndProcessActivityTask(false) s.Logger.Info("Poll and process first activity", tag.Error(err)) s.NoError(err) // Process second workflow task which checks activity completion _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("Poll and process second workflow task", tag.Error(err)) s.NoError(err) // Find reset point (last completed decision task) events := s.getHistory(s.DomainName, &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }) var lastDecisionCompleted *types.HistoryEvent for _, event := range events { if event.GetEventType() == types.EventTypeDecisionTaskCompleted { lastDecisionCompleted = event } } ctx, cancel = createContext() defer cancel() // FIRST reset: Reset workflow execution, current is open resp, err := s.Engine.ResetWorkflowExecution(ctx, &types.ResetWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, Reason: "reset execution from test", DecisionFinishEventID: lastDecisionCompleted.ID, RequestID: uuid.New(), }) s.NoError(err) err = poller.PollAndProcessActivityTask(false) s.Logger.Info("Poll and process second activity", tag.Error(err)) s.NoError(err) err = poller.PollAndProcessActivityTask(false) s.Logger.Info("Poll and process third activity", tag.Error(err)) s.NoError(err) s.NotNil(firstActivityCompletionEvent) s.False(workflowComplete) // get the history of the first run again events = s.getHistory(s.DomainName, &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }) var firstRunStartTimestamp int64 var lastEvent *types.HistoryEvent for _, event := range events { if event.GetEventType() == types.EventTypeWorkflowExecutionStarted { firstRunStartTimestamp = event.GetTimestamp() } if event.GetEventType() == types.EventTypeDecisionTaskCompleted { lastDecisionCompleted = event } lastEvent = event } // assert the first run is closed, terminated by the previous reset s.Equal(types.EventTypeWorkflowExecutionTerminated, lastEvent.GetEventType()) // check the start time of mutable state for the second run, // it should be reset time although the start event is reused ctx, cancel = createContext() defer cancel() descResp, err := s.Engine.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: resp.GetRunID(), }, }) s.NoError(err) s.True(descResp.WorkflowExecutionInfo.GetStartTime() > firstRunStartTimestamp) ctx, cancel = createContext() defer cancel() // SECOND reset: reset the first run again, to exercise the code path of resetting closed workflow resp, err = s.Engine.ResetWorkflowExecution(ctx, &types.ResetWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }, Reason: "reset execution from test", DecisionFinishEventID: lastDecisionCompleted.ID, RequestID: uuid.New(), }) s.NoError(err) newRunID := resp.GetRunID() _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("Poll and process final decision task", tag.Error(err)) s.NoError(err) s.True(workflowComplete) // get the history of the newRunID events = s.getHistory(s.DomainName, &types.WorkflowExecution{ WorkflowID: id, RunID: newRunID, }) for _, event := range events { if event.GetEventType() == types.EventTypeDecisionTaskCompleted { lastDecisionCompleted = event } lastEvent = event } // assert the new run is closed, completed by decision task s.Equal(types.EventTypeWorkflowExecutionCompleted, lastEvent.GetEventType()) ctx, cancel = createContext() defer cancel() // THIRD reset: reset the workflow run that is after a reset _, err = s.Engine.ResetWorkflowExecution(ctx, &types.ResetWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: newRunID, }, Reason: "reset execution from test", DecisionFinishEventID: lastDecisionCompleted.ID, RequestID: uuid.New(), }) s.NoError(err) } func (s *IntegrationSuite) TestResetWorkflow_NoDecisionTaskCompleted() { id := "integration-reset-workflow-test-no-decision-completed" wt := "integration-reset-workflow-test-type--no-decision-completed" tl := "integration-reset-workflow-test-taskqueue-no-decision-completed" identity := "worker1" workflowType := &types.WorkflowType{Name: wt} tasklist := &types.TaskList{Name: tl} // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: tasklist, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.NoError(err0) workflowComplete := false activityData := int32(1) isFirstTaskProcessed := false wtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !isFirstTaskProcessed { // Schedule 3 activities on first workflow task isFirstTaskProcessed = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) var scheduleActivityCommands []*types.Decision scheduleActivityCommands = append(scheduleActivityCommands, &types.Decision{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: "ResetActivity"}, TaskList: tasklist, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(100), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }) return nil, scheduleActivityCommands, nil } // Complete workflow after reset workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: tasklist, Identity: identity, DecisionHandler: wtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Process first workflow decision task to schedule activities _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessWorkflowTask", tag.Error(err)) s.NoError(err) // Process one activity task which also creates second workflow task err = poller.PollAndProcessActivityTask(false) s.Logger.Info("Poll and process first activity", tag.Error(err)) s.NoError(err) // Find reset point (last completed decision task) events := s.getHistory(s.DomainName, &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }) var lastDecisionScheduled *types.HistoryEvent for _, event := range events { if event.GetEventType() == types.EventTypeDecisionTaskScheduled { lastDecisionScheduled = event } } s.NotNil(lastDecisionScheduled) describeDomainRequest := &types.DescribeDomainRequest{ Name: &s.DomainName, } ctx, cancel = createContext() defer cancel() describeDomainResponse, err := s.Engine.DescribeDomain(ctx, describeDomainRequest) s.NoError(err) recordDecisionStartedRequest := &types.RecordDecisionTaskStartedRequest{ DomainUUID: describeDomainResponse.DomainInfo.UUID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }, ScheduleID: lastDecisionScheduled.ID, TaskID: 1, RequestID: uuid.New(), PollRequest: &types.PollForDecisionTaskRequest{ Domain: s.DomainName, TaskList: tasklist, Identity: identity, BinaryChecksum: "", }, } ctx, cancel = createContext() defer cancel() // Record decision task started, to simulate the case where decision task is started but not completed _, err = s.HistoryClient.RecordDecisionTaskStarted(ctx, recordDecisionStartedRequest) s.NoError(err) ctx, cancel = createContext() defer cancel() // FIRST reset: Reset workflow execution, current is open _, err = s.Engine.ResetWorkflowExecution(ctx, &types.ResetWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, Reason: "reset execution from test", DecisionFinishEventID: lastDecisionScheduled.ID + 1, RequestID: uuid.New(), }) s.NoError(err) // get the history of the first run again events = s.getHistory(s.DomainName, &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }) var lastEvent *types.HistoryEvent for _, event := range events { lastEvent = event } // assert the first run is closed, terminated by the previous reset s.Equal(types.EventTypeWorkflowExecutionTerminated, lastEvent.GetEventType()) ctx, cancel = createContext() defer cancel() // SECOND reset: reset the first run again, to exercise the code path of resetting closed workflow resp, err := s.Engine.ResetWorkflowExecution(ctx, &types.ResetWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }, Reason: "reset execution from test", DecisionFinishEventID: lastDecisionScheduled.ID + 2, RequestID: uuid.New(), }) s.NoError(err) newRunID := resp.GetRunID() workflowComplete = false isFirstTaskProcessed = false // Process first workflow decision task to schedule activities _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessWorkflowTask", tag.Error(err)) s.NoError(err) s.getHistory(s.DomainName, &types.WorkflowExecution{ WorkflowID: id, RunID: newRunID, }) // Process one activity task which also creates second workflow task err = poller.PollAndProcessActivityTask(false) s.Logger.Info("Poll and process first activity", tag.Error(err)) s.NoError(err) _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("Poll and process final decision task", tag.Error(err)) s.NoError(err) s.True(workflowComplete) // get the history of the newRunID events = s.getHistory(s.DomainName, &types.WorkflowExecution{ WorkflowID: id, RunID: newRunID, }) for _, event := range events { lastEvent = event } // assert the new run is closed, completed by decision task s.Equal(types.EventTypeWorkflowExecutionCompleted, lastEvent.GetEventType()) } ================================================ FILE: host/retry_policy_workflow_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "strconv" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) func (s *IntegrationSuite) TestWorkflowRetryPolicyTimeout() { id := "integration-workflow-retry-policy-timeout-test" wt := "integration-workflow-retry-policy-timeout-test-type" tl := "integration-workflow-retry-policy-timeout-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl retryPolicy := &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2.0, MaximumIntervalInSeconds: 1, ExpirationIntervalInSeconds: 1, MaximumAttempts: 0, NonRetriableErrorReasons: []string{"bad-error"}, } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(3), Identity: identity, RetryPolicy: retryPolicy, } now := time.Now() ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) time.Sleep(3 * time.Second) // wait 1 second for timeout ctx, cancel = createContext() defer cancel() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, }, }) s.Nil(err) history := historyResponse.History firstEvent := history.Events[0] s.NotNil(firstEvent) s.Equal(firstEvent.EventType, types.EventTypeWorkflowExecutionStarted.Ptr()) expirationTime := firstEvent.WorkflowExecutionStartedEventAttributes.ExpirationTimestamp s.NotNil(expirationTime) delta := time.Unix(0, *expirationTime).Sub(now) s.True(delta > 0*time.Second) s.True(delta < 2*time.Second) lastEvent := history.Events[len(history.Events)-1] delta = time.Unix(0, common.Int64Default(lastEvent.Timestamp)).Sub(time.Unix(0, common.Int64Default(firstEvent.Timestamp))) s.True(delta > 2*time.Second) s.True(delta < 4*time.Second) } func (s *IntegrationSuite) TestWorkflowRetryPolicyContinueAsNewAsRetry() { id := "integration-workflow-retry-policy-continue-as-new-retry-test" wt := "integration-workflow-retry-policy-continue-as-new-retry-test-type" tl := "integration-workflow-retry-policy-continue-as-new-retry-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl retryPolicy := &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2.0, MaximumIntervalInSeconds: 3, ExpirationIntervalInSeconds: 3, MaximumAttempts: 0, NonRetriableErrorReasons: []string{"bad-error"}, } request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, RetryPolicy: retryPolicy, } now := time.Now() ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { return []byte(strconv.Itoa(0)), []*types.Decision{{ DecisionType: types.DecisionTypeFailWorkflowExecution.Ptr(), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr("fail-reason"), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) time.Sleep(3 * time.Second) // wait 1 second for timeout var history *types.History var lastEvent *types.HistoryEvent GetHistoryLoop: for i := 0; i < 20; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, }, }) cancel() s.Nil(err) history = historyResponse.History lastEvent = history.Events[len(history.Events)-1] if *lastEvent.EventType != types.EventTypeWorkflowExecutionTimedOut { s.Logger.Warn("Execution not timedout yet.") time.Sleep(200 * time.Millisecond) continue GetHistoryLoop } timeoutEventAttributes := lastEvent.WorkflowExecutionTimedOutEventAttributes s.Equal(types.TimeoutTypeStartToClose, *timeoutEventAttributes.TimeoutType) break GetHistoryLoop } firstEvent := history.Events[0] s.Equal(firstEvent.WorkflowExecutionStartedEventAttributes.Attempt, int32(1)) delta := time.Unix(0, common.Int64Default(firstEvent.WorkflowExecutionStartedEventAttributes.ExpirationTimestamp)).Sub(now) s.True(delta > 2*time.Second) s.True(delta < 4*time.Second) } ================================================ FILE: host/service.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "math/rand" "sync/atomic" "time" "github.com/uber-go/tally" "go.uber.org/yarpc" "github.com/uber/cadence/client" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/rpc" ) type ( // Service is the interface which must be implemented by all the services // TODO: Service contains many methods that are not used now that we have resource bean, these should be cleaned up Service interface { Start() Stop() GetLogger() log.Logger GetThrottledLogger() log.Logger GetMetricsClient() metrics.Client GetClientBean() client.Bean GetTimeSource() clock.TimeSource GetDispatcher() *yarpc.Dispatcher GetMembershipResolver() membership.Resolver GetHostInfo() membership.HostInfo GetClusterMetadata() cluster.Metadata GetMessagingClient() messaging.Client GetBlobstoreClient() blobstore.Client GetArchivalMetadata() archiver.ArchivalMetadata GetArchiverProvider() provider.ArchiverProvider GetPayloadSerializer() persistence.PayloadSerializer } // Service contains the objects specific to this service serviceImpl struct { status int32 sName string hostInfo membership.HostInfo dispatcher *yarpc.Dispatcher membershipResolver membership.Resolver rpcFactory rpc.Factory pprofInitializer common.PProfInitializer clientBean client.Bean timeSource clock.TimeSource numberOfHistoryShards int allIsolationGroups func() []string logger log.Logger throttledLogger log.Logger metricsScope tally.Scope runtimeMetricsReporter *metrics.RuntimeMetricsReporter metricsClient metrics.Client clusterMetadata cluster.Metadata messagingClient messaging.Client blobstoreClient blobstore.Client dynamicCollection *dynamicconfig.Collection archivalMetadata archiver.ArchivalMetadata archiverProvider provider.ArchiverProvider serializer persistence.PayloadSerializer } ) var _ Service = (*serviceImpl)(nil) // NewService instantiates a Service Instance // TODO: have a better name for Service. func NewService(params *resource.Params) Service { sVice := &serviceImpl{ status: common.DaemonStatusInitialized, sName: params.Name, logger: params.Logger, throttledLogger: params.ThrottledLogger, rpcFactory: params.RPCFactory, membershipResolver: params.MembershipResolver, pprofInitializer: params.PProfInitializer, timeSource: clock.NewRealTimeSource(), metricsScope: params.MetricScope, numberOfHistoryShards: params.PersistenceConfig.NumHistoryShards, allIsolationGroups: params.GetIsolationGroups, clusterMetadata: params.ClusterMetadata, metricsClient: params.MetricsClient, messagingClient: params.MessagingClient, blobstoreClient: params.BlobstoreClient, dynamicCollection: dynamicconfig.NewCollection( params.DynamicConfig, params.Logger, dynamicproperties.ClusterNameFilter(params.ClusterMetadata.GetCurrentClusterName()), ), archivalMetadata: params.ArchivalMetadata, archiverProvider: params.ArchiverProvider, serializer: persistence.NewPayloadSerializer(), } sVice.runtimeMetricsReporter = metrics.NewRuntimeMetricsReporter(params.MetricScope, time.Minute, sVice.GetLogger(), params.InstanceID) sVice.dispatcher = sVice.rpcFactory.GetDispatcher() if sVice.dispatcher == nil { sVice.logger.Fatal("Unable to create yarpc dispatcher") } return sVice } // Start starts a yarpc service func (h *serviceImpl) Start() { if !atomic.CompareAndSwapInt32(&h.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } var err error h.metricsScope.Counter(metrics.RestartCount).Inc(1) h.runtimeMetricsReporter.Start() if err := h.pprofInitializer.Start(); err != nil { h.logger.WithTags(tag.Error(err)).Fatal("Failed to start pprof") } if err := h.dispatcher.Start(); err != nil { h.logger.WithTags(tag.Error(err)).Fatal("Failed to start yarpc dispatcher") } h.membershipResolver.Start() hostInfo, err := h.membershipResolver.WhoAmI() if err != nil { h.logger.WithTags(tag.Error(err)).Fatal("failed to get host info from membership monitor") } h.hostInfo = hostInfo h.clientBean, err = client.NewClientBean( client.NewRPCClientFactory(h.rpcFactory, h.membershipResolver, h.metricsClient, h.dynamicCollection, h.numberOfHistoryShards, h.allIsolationGroups, h.logger), h.rpcFactory.GetDispatcher(), h.clusterMetadata, ) if err != nil { h.logger.WithTags(tag.Error(err)).Fatal("fail to initialize client bean") } // The service is now started up h.logger.Info("service started") // seed the random generator once for this service rand.Seed(time.Now().UTC().UnixNano()) } // Stop closes the associated transport func (h *serviceImpl) Stop() { if !atomic.CompareAndSwapInt32(&h.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } if h.membershipResolver != nil { h.membershipResolver.Stop() } if h.dispatcher != nil { h.dispatcher.Stop() //nolint:errcheck } h.runtimeMetricsReporter.Stop() } func (h *serviceImpl) GetLogger() log.Logger { return h.logger } func (h *serviceImpl) GetThrottledLogger() log.Logger { return h.throttledLogger } func (h *serviceImpl) GetMetricsClient() metrics.Client { return h.metricsClient } func (h *serviceImpl) GetClientBean() client.Bean { return h.clientBean } func (h *serviceImpl) GetTimeSource() clock.TimeSource { return h.timeSource } func (h *serviceImpl) GetMembershipResolver() membership.Resolver { return h.membershipResolver } func (h *serviceImpl) GetHostInfo() membership.HostInfo { return h.hostInfo } func (h *serviceImpl) GetDispatcher() *yarpc.Dispatcher { return h.dispatcher } // GetClusterMetadata returns the service cluster metadata func (h *serviceImpl) GetClusterMetadata() cluster.Metadata { return h.clusterMetadata } // GetMessagingClient returns the messaging client against Kafka func (h *serviceImpl) GetMessagingClient() messaging.Client { return h.messagingClient } func (h *serviceImpl) GetBlobstoreClient() blobstore.Client { return h.blobstoreClient } func (h *serviceImpl) GetArchivalMetadata() archiver.ArchivalMetadata { return h.archivalMetadata } func (h *serviceImpl) GetArchiverProvider() provider.ArchiverProvider { return h.archiverProvider } func (h *serviceImpl) GetPayloadSerializer() persistence.PayloadSerializer { return h.serializer } ================================================ FILE: host/signal_workflow_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "bytes" "context" "encoding/binary" "fmt" "strconv" "strings" "time" "github.com/pborman/uuid" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc" "github.com/uber/cadence/common" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/engine/engineimpl" "github.com/uber/cadence/service/history/execution" ) func (s *IntegrationSuite) TestSignalWorkflow() { id := "integration-signal-workflow-test" wt := "integration-signal-workflow-test-type" tl := "integration-signal-workflow-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl signal := func(runID string, signalName string, signalInput []byte, opts ...yarpc.CallOption) error { execution := types.WorkflowExecution{ WorkflowID: id, } if len(runID) > 0 { execution.RunID = runID } ctx, cancel := createContext() defer cancel() return s.Engine.SignalWorkflowExecution( ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &execution, SignalName: signalName, Input: signalInput, Identity: identity, }, opts..., ) } // Send a signal to non-exist workflow err0 := signal(uuid.New(), "failed signal.", nil) s.NotNil(err0) s.IsType(&types.EntityNotExistsError{}, err0) // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic workflowComplete := false activityScheduled := false activityData := int32(1) var signalEvent *types.HistoryEvent dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { signalEvent = event return nil, []*types.Decision{}, nil } } } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Make first decision to schedule activity _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // Send first signal using RunID signalName := "my signal" signalInput := []byte("my signal input.") err = signal(we.RunID, signalName, signalInput) s.Nil(err) // Process signal in decider _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.False(workflowComplete) s.True(signalEvent != nil) s.Equal(signalName, signalEvent.WorkflowExecutionSignaledEventAttributes.SignalName) s.Equal(signalInput, signalEvent.WorkflowExecutionSignaledEventAttributes.Input) s.Equal(identity, signalEvent.WorkflowExecutionSignaledEventAttributes.Identity) // Send another signal without RunID signalName = "another signal" signalInput = []byte("another signal input.") err = signal("", signalName, signalInput) s.Nil(err) // Process signal in decider _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.False(workflowComplete) s.True(signalEvent != nil) s.Equal(signalName, signalEvent.WorkflowExecutionSignaledEventAttributes.SignalName) s.Equal(signalInput, signalEvent.WorkflowExecutionSignaledEventAttributes.Input) s.Equal(identity, signalEvent.WorkflowExecutionSignaledEventAttributes.Identity) ctx, cancel = createContext() defer cancel() // Terminate workflow execution err = s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, }, Reason: "test signal", Details: nil, Identity: identity, }) s.Nil(err) // Send signal to terminated workflow err = signal(we.RunID, "failed signal 1.", nil) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) // Send signal by enabling WorkflowExecutionAlreadyCompletedError feature err = signal(we.RunID, "failed signal 1.", nil, yarpc.WithHeader(common.ClientFeatureFlagsHeaderName, client.FeatureFlagsHeader(apiv1.FeatureFlags{ WorkflowExecutionAlreadyCompletedErrorEnabled: true, }))) s.NotNil(err) s.IsType(&types.WorkflowExecutionAlreadyCompletedError{}, err) } func (s *IntegrationSuite) TestSignalWorkflow_DuplicateRequest() { id := "integration-signal-workflow-test-duplicate" wt := "integration-signal-workflow-test-duplicate-type" tl := "integration-signal-workflow-test-duplicate-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic workflowComplete := false activityScheduled := false activityData := int32(1) var signalEvent *types.HistoryEvent numOfSignaledEvent := 0 dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if previousStartedEventID > 0 { numOfSignaledEvent = 0 for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { signalEvent = event numOfSignaledEvent++ } } return nil, []*types.Decision{}, nil } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Make first decision to schedule activity _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // Send first signal signalName := "my signal" signalInput := []byte("my signal input.") RequestID := uuid.New() signalReqest := &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, SignalName: signalName, Input: signalInput, Identity: identity, RequestID: RequestID, } ctx, cancel = createContext() defer cancel() err = s.Engine.SignalWorkflowExecution(ctx, signalReqest) s.Nil(err) // Process signal in decider _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.False(workflowComplete) s.True(signalEvent != nil) s.Equal(signalName, signalEvent.WorkflowExecutionSignaledEventAttributes.SignalName) s.Equal(signalInput, signalEvent.WorkflowExecutionSignaledEventAttributes.Input) s.Equal(identity, signalEvent.WorkflowExecutionSignaledEventAttributes.Identity) s.Equal(1, numOfSignaledEvent) ctx, cancel = createContext() defer cancel() // Send another signal with same request id err = s.Engine.SignalWorkflowExecution(ctx, signalReqest) s.Nil(err) // Process signal in decider _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.False(workflowComplete) s.True(signalEvent != nil) s.Equal(0, numOfSignaledEvent) } func (s *IntegrationSuite) TestSignalExternalWorkflowDecision() { id := "integration-signal-external-workflow-test" wt := "integration-signal-external-workflow-test-type" tl := "integration-signal-external-workflow-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) foreignRequest := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.ForeignDomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel = createContext() defer cancel() we2, err0 := s.Engine.StartWorkflowExecution(ctx, foreignRequest) s.Nil(err0) s.Logger.Info("StartWorkflowExecution on foreign Domain", tag.WorkflowDomainName(s.ForeignDomainName), tag.WorkflowRunID(we2.RunID)) activityCount := int32(1) activityCounter := int32(0) signalName := "my signal" signalInput := []byte("my signal input.") dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeSignalExternalWorkflowExecution.Ptr(), SignalExternalWorkflowExecutionDecisionAttributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: s.ForeignDomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we2.GetRunID(), }, SignalName: signalName, Input: signalInput, }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } workflowComplete := false foreignActivityCount := int32(1) foreignActivityCounter := int32(0) var signalEvent *types.HistoryEvent foreignDtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if foreignActivityCounter < foreignActivityCount { foreignActivityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, foreignActivityCounter)) return []byte(strconv.Itoa(int(foreignActivityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(foreignActivityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { signalEvent = event return nil, []*types.Decision{}, nil } } } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } foreignPoller := &TaskPoller{ Engine: s.Engine, Domain: s.ForeignDomainName, TaskList: taskList, Identity: identity, DecisionHandler: foreignDtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Start both current and foreign workflows to make some progress. _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) _, err = foreignPoller.PollAndProcessDecisionTask(false, false) s.Logger.Info("foreign PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) err = foreignPoller.PollAndProcessActivityTask(false) s.Logger.Info("foreign PollAndProcessActivityTask", tag.Error(err)) s.Nil(err) // Signal the foreign workflow with this decision request. _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // in source workflow signalSent := false intiatedEventID := 10 CheckHistoryLoopForSignalSent: for i := 1; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History // common.PrettyPrintHistory(history, s.Logger) signalRequestedEvent := history.Events[len(history.Events)-2] if *signalRequestedEvent.EventType != types.EventTypeExternalWorkflowExecutionSignaled { s.Logger.Info("Signal still not sent.") time.Sleep(100 * time.Millisecond) continue CheckHistoryLoopForSignalSent } ewfeAttributes := signalRequestedEvent.ExternalWorkflowExecutionSignaledEventAttributes s.NotNil(ewfeAttributes) s.Equal(int64(intiatedEventID), ewfeAttributes.GetInitiatedEventID()) s.Equal(id, ewfeAttributes.WorkflowExecution.GetWorkflowID()) s.Equal(we2.RunID, ewfeAttributes.WorkflowExecution.RunID) signalSent = true break } s.True(signalSent) // process signal in decider for foreign workflow _, err = foreignPoller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.False(workflowComplete) s.True(signalEvent != nil) s.Equal(signalName, signalEvent.WorkflowExecutionSignaledEventAttributes.SignalName) s.Equal(signalInput, signalEvent.WorkflowExecutionSignaledEventAttributes.Input) s.Equal(execution.IdentityHistoryService, signalEvent.WorkflowExecutionSignaledEventAttributes.Identity) } func (s *IntegrationSuite) TestSignalWorkflow_Cron_NoDecisionTaskCreated() { id := "integration-signal-workflow-test-cron" wt := "integration-signal-workflow-test-cron-type" tl := "integration-signal-workflow-test-cron-tasklist" identity := "worker1" cronSpec := "@every 2s" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start workflow execution request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, CronSchedule: cronSpec, } now := time.Now() ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // Send first signal using RunID signalName := "my signal" signalInput := []byte("my signal input.") ctx, cancel = createContext() defer cancel() err := s.Engine.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, SignalName: signalName, Input: signalInput, Identity: identity, }) s.Nil(err) // decider logic var decisionTaskDelay time.Duration dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { decisionTaskDelay = time.Since(now) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.Logger, T: s.T(), } // Make first decision to schedule activity _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(decisionTaskDelay > time.Second*2) } func (s *IntegrationSuite) TestSignalExternalWorkflowDecision_WithoutRunID() { id := "integration-signal-external-workflow-test-without-run-id" wt := "integration-signal-external-workflow-test-without-run-id-type" tl := "integration-signal-external-workflow-test-without-run-id-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) foreignRequest := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.ForeignDomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel = createContext() defer cancel() we2, err0 := s.Engine.StartWorkflowExecution(ctx, foreignRequest) s.Nil(err0) s.Logger.Info("StartWorkflowExecution on foreign Domain", tag.WorkflowDomainName(s.ForeignDomainName), tag.WorkflowRunID(we2.RunID)) activityCount := int32(1) activityCounter := int32(0) signalName := "my signal" signalInput := []byte("my signal input.") dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeSignalExternalWorkflowExecution.Ptr(), SignalExternalWorkflowExecutionDecisionAttributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: s.ForeignDomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, // No RunID in decision }, SignalName: signalName, Input: signalInput, }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } workflowComplete := false foreignActivityCount := int32(1) foreignActivityCounter := int32(0) var signalEvent *types.HistoryEvent foreignDtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if foreignActivityCounter < foreignActivityCount { foreignActivityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, foreignActivityCounter)) return []byte(strconv.Itoa(int(foreignActivityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(foreignActivityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { signalEvent = event return nil, []*types.Decision{}, nil } } } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } foreignPoller := &TaskPoller{ Engine: s.Engine, Domain: s.ForeignDomainName, TaskList: taskList, Identity: identity, DecisionHandler: foreignDtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Start both current and foreign workflows to make some progress. _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) _, err = foreignPoller.PollAndProcessDecisionTask(false, false) s.Logger.Info("foreign PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) err = foreignPoller.PollAndProcessActivityTask(false) s.Logger.Info("foreign PollAndProcessActivityTask", tag.Error(err)) s.Nil(err) // Signal the foreign workflow with this decision request. _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // in source workflow signalSent := false intiatedEventID := 10 CheckHistoryLoopForSignalSent: for i := 1; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History signalRequestedEvent := history.Events[len(history.Events)-2] if *signalRequestedEvent.EventType != types.EventTypeExternalWorkflowExecutionSignaled { s.Logger.Info("Signal still not sent.") time.Sleep(100 * time.Millisecond) continue CheckHistoryLoopForSignalSent } ewfeAttributes := signalRequestedEvent.ExternalWorkflowExecutionSignaledEventAttributes s.NotNil(ewfeAttributes) s.Equal(int64(intiatedEventID), ewfeAttributes.GetInitiatedEventID()) s.Equal(id, ewfeAttributes.WorkflowExecution.GetWorkflowID()) s.Equal("", ewfeAttributes.WorkflowExecution.GetRunID()) signalSent = true break } s.True(signalSent) // process signal in decider for foreign workflow _, err = foreignPoller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.False(workflowComplete) s.True(signalEvent != nil) s.Equal(signalName, signalEvent.WorkflowExecutionSignaledEventAttributes.SignalName) s.Equal(signalInput, signalEvent.WorkflowExecutionSignaledEventAttributes.Input) s.Equal(execution.IdentityHistoryService, signalEvent.WorkflowExecutionSignaledEventAttributes.Identity) } func (s *IntegrationSuite) TestSignalExternalWorkflowDecision_UnKnownTarget() { id := "integration-signal-unknown-workflow-decision-test" wt := "integration-signal-unknown-workflow-decision-test-type" tl := "integration-signal-unknown-workflow-decision-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) activityCount := int32(1) activityCounter := int32(0) signalName := "my signal" signalInput := []byte("my signal input.") dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeSignalExternalWorkflowExecution.Ptr(), SignalExternalWorkflowExecutionDecisionAttributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: s.ForeignDomainName, Execution: &types.WorkflowExecution{ WorkflowID: "workflow_not_exist", RunID: we.GetRunID(), }, SignalName: signalName, Input: signalInput, }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Start workflows to make some progress. _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // Signal the foreign workflow with this decision request. _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) signalSentFailed := false intiatedEventID := 10 CheckHistoryLoopForCancelSent: for i := 1; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History signalFailedEvent := history.Events[len(history.Events)-2] if *signalFailedEvent.EventType != types.EventTypeSignalExternalWorkflowExecutionFailed { s.Logger.Info("Cancellaton not cancelled yet.") time.Sleep(100 * time.Millisecond) continue CheckHistoryLoopForCancelSent } signalExternalWorkflowExecutionFailedEventAttributes := signalFailedEvent.SignalExternalWorkflowExecutionFailedEventAttributes s.Equal(int64(intiatedEventID), signalExternalWorkflowExecutionFailedEventAttributes.InitiatedEventID) s.Equal("workflow_not_exist", signalExternalWorkflowExecutionFailedEventAttributes.WorkflowExecution.WorkflowID) s.Equal(we.RunID, signalExternalWorkflowExecutionFailedEventAttributes.WorkflowExecution.RunID) signalSentFailed = true break } s.True(signalSentFailed) } func (s *IntegrationSuite) TestSignalExternalWorkflowDecision_SignalSelf() { id := "integration-signal-self-workflow-decision-test" wt := "integration-signal-self-workflow-decision-test-type" tl := "integration-signal-self-workflow-decision-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) activityCount := int32(1) activityCounter := int32(0) signalName := "my signal" signalInput := []byte("my signal input.") dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeSignalExternalWorkflowExecution.Ptr(), SignalExternalWorkflowExecutionDecisionAttributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }, SignalName: signalName, Input: signalInput, }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Start workflows to make some progress. _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // Signal the foreign workflow with this decision request. _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) signalSentFailed := false intiatedEventID := 10 CheckHistoryLoopForCancelSent: for i := 1; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History signalFailedEvent := history.Events[len(history.Events)-2] if *signalFailedEvent.EventType != types.EventTypeSignalExternalWorkflowExecutionFailed { s.Logger.Info("Cancellaton not cancelled yet.") time.Sleep(100 * time.Millisecond) continue CheckHistoryLoopForCancelSent } signalExternalWorkflowExecutionFailedEventAttributes := signalFailedEvent.SignalExternalWorkflowExecutionFailedEventAttributes s.Equal(int64(intiatedEventID), signalExternalWorkflowExecutionFailedEventAttributes.InitiatedEventID) s.Equal(id, signalExternalWorkflowExecutionFailedEventAttributes.WorkflowExecution.WorkflowID) s.Equal(we.RunID, signalExternalWorkflowExecutionFailedEventAttributes.WorkflowExecution.RunID) signalSentFailed = true break } s.True(signalSentFailed) } func (s *IntegrationSuite) TestSignalWithStartWorkflow() { id := "integration-signal-with-start-workflow-test" wt := "integration-signal-with-start-workflow-test-type" tl := "integration-signal-with-start-workflow-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl header := &types.Header{ Fields: map[string][]byte{"tracing": []byte("sample data")}, } // Start a workflow request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) // decider logic workflowComplete := false activityScheduled := false activityData := int32(1) newWorkflowStarted := false var signalEvent, startedEvent *types.HistoryEvent dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if !activityScheduled { activityScheduled = true buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityData)) return nil, []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "1", ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } else if previousStartedEventID > 0 { for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { signalEvent = event return nil, []*types.Decision{}, nil } } } else if newWorkflowStarted { newWorkflowStarted = false signalEvent = nil startedEvent = nil for _, event := range history.Events[previousStartedEventID:] { if *event.EventType == types.EventTypeWorkflowExecutionSignaled { signalEvent = event } if *event.EventType == types.EventTypeWorkflowExecutionStarted { startedEvent = event } } if signalEvent != nil && startedEvent != nil { return nil, []*types.Decision{}, nil } } workflowComplete = true return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } // activity handler atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Make first decision to schedule activity _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) // Send a signal signalName := "my signal" signalInput := []byte("my signal input.") wfIDReusePolicy := types.WorkflowIDReusePolicyAllowDuplicate sRequest := &types.SignalWithStartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, Header: header, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), SignalName: signalName, SignalInput: signalInput, Identity: identity, WorkflowIDReusePolicy: &wfIDReusePolicy, } ctx, cancel = createContext() defer cancel() resp, err := s.Engine.SignalWithStartWorkflowExecution(ctx, sRequest) s.Nil(err) s.Equal(we.GetRunID(), resp.GetRunID()) // Process signal in decider _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.False(workflowComplete) s.True(signalEvent != nil) s.Equal(signalName, signalEvent.WorkflowExecutionSignaledEventAttributes.SignalName) s.Equal(signalInput, signalEvent.WorkflowExecutionSignaledEventAttributes.Input) s.Equal(identity, signalEvent.WorkflowExecutionSignaledEventAttributes.Identity) ctx, cancel = createContext() defer cancel() // Terminate workflow execution err = s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, }, Reason: "test signal", Details: nil, Identity: identity, }) s.Nil(err) // Send signal to terminated workflow signalName = "signal to terminate" signalInput = []byte("signal to terminate input.") sRequest.SignalName = signalName sRequest.SignalInput = signalInput sRequest.WorkflowID = id sRequest.RequestID = uuid.New() ctx, cancel = createContext() defer cancel() resp, err = s.Engine.SignalWithStartWorkflowExecution(ctx, sRequest) s.Nil(err) s.NotNil(resp.GetRunID()) s.NotEqual(we.GetRunID(), resp.GetRunID()) newWorkflowStarted = true // Process signal in decider _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.False(workflowComplete) s.True(signalEvent != nil) s.Equal(signalName, signalEvent.WorkflowExecutionSignaledEventAttributes.SignalName) s.Equal(signalInput, signalEvent.WorkflowExecutionSignaledEventAttributes.Input) s.Equal(identity, signalEvent.WorkflowExecutionSignaledEventAttributes.Identity) s.True(startedEvent != nil) s.Equal(header, startedEvent.WorkflowExecutionStartedEventAttributes.Header) // Send signal to not existed workflow id = "integration-signal-with-start-workflow-test-non-exist" signalName = "signal to non exist" signalInput = []byte("signal to non exist input.") sRequest.SignalName = signalName sRequest.SignalInput = signalInput sRequest.WorkflowID = id sRequest.RequestID = uuid.New() ctx, cancel = createContext() defer cancel() resp, err = s.Engine.SignalWithStartWorkflowExecution(ctx, sRequest) s.Nil(err) s.NotNil(resp.GetRunID()) newWorkflowStarted = true // Process signal in decider _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.False(workflowComplete) s.True(signalEvent != nil) s.Equal(signalName, signalEvent.WorkflowExecutionSignaledEventAttributes.SignalName) s.Equal(signalInput, signalEvent.WorkflowExecutionSignaledEventAttributes.Input) s.Equal(identity, signalEvent.WorkflowExecutionSignaledEventAttributes.Identity) // Assert visibility is correct listOpenRequest := &types.ListOpenWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Int64Ptr(0), LatestTime: common.Int64Ptr(time.Now().UnixNano()), }, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: id, }, } ctx, cancel = createContext() defer cancel() listResp, err := s.Engine.ListOpenWorkflowExecutions(ctx, listOpenRequest) s.NoError(err) s.Equal(1, len(listResp.Executions)) ctx, cancel = createContext() defer cancel() // Terminate workflow execution and assert visibility is correct err = s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, }, Reason: "kill workflow", Details: nil, Identity: identity, }) s.Nil(err) for i := 0; i < 10; i++ { // retry ctx, cancel := createContext() listResp, err = s.Engine.ListOpenWorkflowExecutions(ctx, listOpenRequest) cancel() s.NoError(err) if len(listResp.Executions) == 0 { break } time.Sleep(100 * time.Millisecond) } s.Equal(0, len(listResp.Executions)) listClosedRequest := &types.ListClosedWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Int64Ptr(0), LatestTime: common.Int64Ptr(time.Now().UnixNano()), }, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: id, }, } ctx, cancel = createContext() defer cancel() listClosedResp, err := s.Engine.ListClosedWorkflowExecutions(ctx, listClosedRequest) s.NoError(err) s.Equal(1, len(listClosedResp.Executions)) } func (s *IntegrationSuite) TestSignalWithStartWorkflow_IDReusePolicy() { id := "integration-signal-with-start-workflow-id-reuse-test" wt := "integration-signal-with-start-workflow-id-reuse-test-type" tl := "integration-signal-with-start-workflow-id-reuse-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl // Start a workflow request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false activityCount := int32(1) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } workflowComplete = true return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } // Start workflows, make some progress and complete workflow _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) _, err = poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) s.True(workflowComplete) // test policy WorkflowIDReusePolicyRejectDuplicate signalName := "my signal" signalInput := []byte("my signal input.") wfIDReusePolicy := types.WorkflowIDReusePolicyRejectDuplicate sRequest := &types.SignalWithStartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), SignalName: signalName, SignalInput: signalInput, Identity: identity, WorkflowIDReusePolicy: &wfIDReusePolicy, } ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) _, err = s.Engine.SignalWithStartWorkflowExecution(ctx, sRequest) cancel() s.Error(err) errMsg := err.(*types.WorkflowExecutionAlreadyStartedError).GetMessage() s.True(strings.Contains(errMsg, "reject duplicate workflow ID")) // test policy WorkflowIDReusePolicyAllowDuplicateFailedOnly wfIDReusePolicy = types.WorkflowIDReusePolicyAllowDuplicateFailedOnly ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) sRequest.RequestID = uuid.New() _, err = s.Engine.SignalWithStartWorkflowExecution(ctx, sRequest) cancel() s.Error(err) errMsg = err.(*types.WorkflowExecutionAlreadyStartedError).GetMessage() s.True(strings.Contains(errMsg, "allow duplicate workflow ID if last run failed")) // test policy WorkflowIDReusePolicyAllowDuplicate wfIDReusePolicy = types.WorkflowIDReusePolicyAllowDuplicate ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) sRequest.RequestID = uuid.New() resp, err := s.Engine.SignalWithStartWorkflowExecution(ctx, sRequest) cancel() s.Nil(err) s.NotEmpty(resp.GetRunID()) // Terminate workflow execution terminateWorkflow := func() { ctx, cancel := createContext() defer cancel() err = s.Engine.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: s.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, }, Reason: "test WorkflowIDReusePolicyAllowDuplicateFailedOnly", Details: nil, Identity: identity, }) s.Nil(err) } terminateWorkflow() // test policy WorkflowIDReusePolicyAllowDuplicateFailedOnly success start wfIDReusePolicy = types.WorkflowIDReusePolicyAllowDuplicateFailedOnly sRequest.RequestID = uuid.New() ctx, cancel = createContext() defer cancel() resp, err = s.Engine.SignalWithStartWorkflowExecution(ctx, sRequest) s.Nil(err) s.NotEmpty(resp.GetRunID()) // test policy WorkflowIDReusePolicyTerminateIfRunning wfIDReusePolicy = types.WorkflowIDReusePolicyTerminateIfRunning sRequest.RequestID = uuid.New() ctx, cancel = createContext() defer cancel() resp1, err1 := s.Engine.SignalWithStartWorkflowExecution(ctx, sRequest) s.Nil(err1) s.NotEmpty(resp1) // verify terminate status executionTerminated := false GetHistoryLoop: for i := 0; i < 10; i++ { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: resp.RunID, }, }) cancel() s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if lastEvent.GetEventType() != types.EventTypeWorkflowExecutionTerminated { s.Logger.Warn("Execution not terminated yet.") time.Sleep(100 * time.Millisecond) continue GetHistoryLoop } terminateEventAttributes := lastEvent.WorkflowExecutionTerminatedEventAttributes s.Equal(engineimpl.TerminateIfRunningReason, terminateEventAttributes.GetReason()) s.Equal(fmt.Sprintf(engineimpl.TerminateIfRunningDetailsTemplate, resp1.GetRunID()), string(terminateEventAttributes.Details)) s.Equal(execution.IdentityHistoryService, terminateEventAttributes.GetIdentity()) executionTerminated = true break GetHistoryLoop } s.True(executionTerminated) // terminate current run terminateWorkflow() // test clean start with WorkflowIDReusePolicyTerminateIfRunning sRequest.RequestID = uuid.New() ctx, cancel = createContext() defer cancel() resp2, err2 := s.Engine.SignalWithStartWorkflowExecution(ctx, sRequest) s.Nil(err2) s.NotEmpty(resp2) s.NotEqual(resp1.GetRunID(), resp2.GetRunID()) } ================================================ FILE: host/size_limit_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "bytes" "encoding/binary" "flag" "strconv" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) func TestSizeLimitIntegrationSuite(t *testing.T) { flag.Parse() clusterConfig, err := GetTestClusterConfig("testdata/integration_sizelimit_cluster.yaml") if err != nil { panic(err) } testCluster := NewPersistenceTestCluster(t, clusterConfig) s := new(SizeLimitIntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } // This cluster use customized threshold for history config func (s *SizeLimitIntegrationSuite) SetupSuite() { s.setupSuite() } func (s *SizeLimitIntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() } func (s *SizeLimitIntegrationSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } func (s *SizeLimitIntegrationSuite) TestTerminateWorkflowCausedBySizeLimit() { id := "integration-terminate-workflow-by-size-limit-test" wt := "integration-terminate-workflow-by-size-limit-test-type" tl := "integration-terminate-workflow-by-size-limit-test-tasklist" identity := "worker1" activityName := "activity_type1" workflowType := &types.WorkflowType{} workflowType.Name = wt taskList := &types.TaskList{} taskList.Name = tl request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, } ctx, cancel := createContext() defer cancel() we, err0 := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err0) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) activityCount := int32(4) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: tl}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }}, nil } return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } for i := int32(0); i < activityCount-1; i++ { _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) err = poller.PollAndProcessActivityTask(false) s.Logger.Info("PollAndProcessActivityTask", tag.Error(err)) s.Nil(err) } // process this decision will trigger history exceed limit error _, err := poller.PollAndProcessDecisionTask(false, false) s.Logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) ctx, cancel = createContext() defer cancel() // verify last event is terminated event historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: id, RunID: we.GetRunID(), }, }) s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] s.Equal(types.EventTypeWorkflowExecutionFailed, lastEvent.GetEventType()) failedEventAttributes := lastEvent.WorkflowExecutionFailedEventAttributes s.Equal(common.FailureReasonSizeExceedsLimit, failedEventAttributes.GetReason()) // verify visibility is correctly processed from open to close isCloseCorrect := false for i := 0; i < 10; i++ { ctx, cancel := createContext() resp, err1 := s.Engine.ListClosedWorkflowExecutions(ctx, &types.ListClosedWorkflowExecutionsRequest{ Domain: s.DomainName, MaximumPageSize: 100, StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Int64Ptr(0), LatestTime: common.Int64Ptr(time.Now().UnixNano()), }, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: id, }, }) cancel() s.Nil(err1) if len(resp.Executions) == 1 { isCloseCorrect = true break } s.Logger.Info("Closed WorkflowExecution is not yet visible") time.Sleep(100 * time.Millisecond) } s.True(isCloseCorrect) } ================================================ FILE: host/task_list_isolation_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package host import ( "errors" "flag" "strconv" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/yarpc" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) const ( isolationTl = "integration-task-list-isolation-tl" ) func TestTaskListIsolationSuite(t *testing.T) { flag.Parse() var isolationGroups = []any{ "a", "b", "c", } clusterConfig, err := GetTestClusterConfig("testdata/task_list_test_cluster.yaml") if err != nil { panic(err) } testCluster := NewPersistenceTestCluster(t, clusterConfig) clusterConfig.FrontendDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.EnableTasklistIsolation: true, dynamicproperties.AllIsolationGroups: isolationGroups, dynamicproperties.MatchingNumTasklistWritePartitions: 1, dynamicproperties.MatchingNumTasklistReadPartitions: 1, } clusterConfig.HistoryDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.EnableTasklistIsolation: true, dynamicproperties.AllIsolationGroups: isolationGroups, dynamicproperties.MatchingNumTasklistWritePartitions: 1, dynamicproperties.MatchingNumTasklistReadPartitions: 1, } clusterConfig.MatchingDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.EnableTasklistIsolation: true, dynamicproperties.AllIsolationGroups: isolationGroups, dynamicproperties.TaskIsolationDuration: time.Second * 5, dynamicproperties.MatchingNumTasklistWritePartitions: 1, dynamicproperties.MatchingNumTasklistReadPartitions: 1, } s := new(TaskListIsolationIntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } func (s *TaskListIsolationIntegrationSuite) SetupSuite() { s.setupSuite() } func (s *TaskListIsolationIntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() } func (s *TaskListIsolationIntegrationSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } func (s *TaskListIsolationIntegrationSuite) TestTaskListIsolation() { aPoller := s.createPoller("a") bPoller := s.createPoller("b") cancelB := bPoller.PollAndProcessDecisions() defer cancelB() cancelA := aPoller.PollAndProcessDecisions() defer cancelA() // Give pollers time to start time.Sleep(time.Second) // Running a single workflow is a bit of a coinflip: if isolation didn't work, it would pass 50% of the time. // Run 10 workflows to demonstrate that we consistently isolate tasks to the correct poller for i := 0; i < 10; i++ { runID := s.startWorkflow("a").RunID result, err := s.getWorkflowResult(runID) s.NoError(err) s.Equal("a", result) } } func (s *TaskListIsolationIntegrationSuite) TestTaskListIsolationLeak() { runID := s.startWorkflow("a").RunID bPoller := s.createPoller("b") // B will get the task as there are no pollers from A cancelB := bPoller.PollAndProcessDecisions() defer cancelB() result, err := s.getWorkflowResult(runID) s.NoError(err) s.Equal("b", result) } func (s *TaskListIsolationIntegrationSuite) createPoller(group string) *TaskPoller { return &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: &types.TaskList{Name: isolationTl, Kind: types.TaskListKindNormal.Ptr()}, Identity: group, DecisionHandler: func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { // Complete the workflow with the group name return []byte(strconv.Itoa(0)), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte(group), }, }}, nil }, Logger: s.Logger, T: s.T(), CallOptions: []yarpc.CallOption{withIsolationGroup(group)}, } } func (s *TaskListIsolationIntegrationSuite) startWorkflow(group string) *types.StartWorkflowExecutionResponse { identity := "test" request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: s.T().Name(), WorkflowType: &types.WorkflowType{ Name: "integration-task-list-isolation-type", }, TaskList: &types.TaskList{ Name: isolationTl, Kind: types.TaskListKindNormal.Ptr(), }, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(10), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), } ctx, cancel := createContext() defer cancel() result, err := s.Engine.StartWorkflowExecution(ctx, request, withIsolationGroup(group)) s.Nil(err) return result } func (s *TaskListIsolationIntegrationSuite) getWorkflowResult(runID string) (string, error) { ctx, cancel := createContext() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: s.T().Name(), RunID: runID, }, HistoryEventFilterType: types.HistoryEventFilterTypeCloseEvent.Ptr(), WaitForNewEvent: true, }) cancel() if err != nil { return "", err } history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if *lastEvent.EventType != types.EventTypeWorkflowExecutionCompleted { return "", errors.New("workflow didn't complete") } return string(lastEvent.WorkflowExecutionCompletedEventAttributes.Result), nil } func withIsolationGroup(group string) yarpc.CallOption { return yarpc.WithHeader(common.ClientIsolationGroupHeaderName, group) } ================================================ FILE: host/task_list_test.go ================================================ package host import ( "context" "errors" "flag" "fmt" "strconv" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/yarpc" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) const testWorkflowType = "test-workflow" func TestTaskListSuite(t *testing.T) { flag.Parse() clusterConfig, err := GetTestClusterConfig("testdata/task_list_test_cluster.yaml") if err != nil { panic(err) } testCluster := NewPersistenceTestCluster(t, clusterConfig) clusterConfig.FrontendDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.EnableTasklistIsolation: false, dynamicproperties.MatchingNumTasklistWritePartitions: 1, dynamicproperties.MatchingNumTasklistReadPartitions: 1, } clusterConfig.HistoryDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.EnableTasklistIsolation: false, dynamicproperties.MatchingNumTasklistWritePartitions: 1, dynamicproperties.MatchingNumTasklistReadPartitions: 1, } clusterConfig.MatchingDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.EnableTasklistIsolation: false, dynamicproperties.MatchingNumTasklistWritePartitions: 1, dynamicproperties.MatchingNumTasklistReadPartitions: 1, dynamicproperties.MatchingEnableReturnAllTaskListKinds: true, } s := new(TaskListIntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } func (s *TaskListIntegrationSuite) SetupSuite() { s.setupSuite() } func (s *TaskListIntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() } func (s *TaskListIntegrationSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) s.TaskListName = s.T().Name() } func (s *TaskListIntegrationSuite) TestDescribeTaskList_Status() { expectedTl := &types.TaskList{Name: s.TaskListName, Kind: types.TaskListKindNormal.Ptr()} initialStatus := &types.TaskListStatus{ BacklogCountHint: 0, ReadLevel: 0, AckLevel: 0, RatePerSecond: 100000, TaskIDBlock: &types.TaskIDBlock{ StartID: 1, EndID: 100000, }, // Isolation is disabled // IsolationGroupMetrics: map[string]*types.IsolationGroupMetrics{}, NewTasksPerSecond: 0, Empty: true, } withOneTask := &types.TaskListStatus{ BacklogCountHint: 1, ReadLevel: 1, AckLevel: 0, RatePerSecond: 100000, TaskIDBlock: &types.TaskIDBlock{ StartID: 1, EndID: 100000, }, // Isolation is disabled and NewTasksPerSecond is volatile // IsolationGroupMetrics: map[string]*types.IsolationGroupMetrics{}, // NewTasksPerSecond: 0, Empty: false, } completedStatus := &types.TaskListStatus{ BacklogCountHint: 0, ReadLevel: 1, AckLevel: 1, RatePerSecond: 100000, TaskIDBlock: &types.TaskIDBlock{ StartID: 1, EndID: 100000, }, // Isolation is disabled and NewTasksPerSecond is volatile // IsolationGroupMetrics: nil, // NewTasksPerSecond: 0, Empty: true, } // 0. Check before any tasks response := s.describeTaskList(types.TaskListTypeDecision) response.TaskListStatus.IsolationGroupMetrics = nil s.Equal(expectedTl, response.TaskList) s.Equal(initialStatus, response.TaskListStatus) // 1. After a task has been added but not yet completed runID := s.startWorkflow(types.TaskListKindNormal).RunID response = s.describeUntil(func(response *types.DescribeTaskListResponse) bool { return response.TaskListStatus.BacklogCountHint == 1 }) response.TaskListStatus.IsolationGroupMetrics = nil response.TaskListStatus.NewTasksPerSecond = 0 s.Equal(withOneTask, response.TaskListStatus) // 2. After the task has been completed poller := s.createPoller("result", types.TaskListKindNormal) cancelPoller := poller.PollAndProcessDecisions() defer cancelPoller() _, err := s.getWorkflowResult(runID) s.NoError(err, "failed to get workflow result") response = s.describeTaskList(types.TaskListTypeDecision) response.TaskListStatus.IsolationGroupMetrics = nil response.TaskListStatus.NewTasksPerSecond = 0 s.Equal(completedStatus, response.TaskListStatus) } // Run a Workflow containing only decision tasks on an ephemeral TaskList func (s *TaskListIntegrationSuite) TestEphemeralTaskList() { runID := s.startWorkflow(types.TaskListKindEphemeral).RunID response := s.describeWorkflow(s.workflowID(), runID) s.Equal(types.TaskListKindEphemeral, response.WorkflowExecutionInfo.TaskList.GetKind()) firstEvent := s.getStartedEvent(runID) s.Equal(types.TaskListKindEphemeral, firstEvent.TaskList.GetKind()) taskList := s.waitUntilTaskListExists(types.TaskListTypeDecision) s.Equal(types.TaskListKindEphemeral, *taskList.TaskList.Kind) poller := s.createPoller("result", types.TaskListKindEphemeral) cancelPoller := poller.PollAndProcessDecisions() defer cancelPoller() _, err := s.getWorkflowResult(runID) s.NoError(err, "failed to get workflow result") } // Run a workflow on a normal TaskList, with an activity on an ephemeral TaskList func (s *TaskListIntegrationSuite) TestEphemeralTaskList_EphemeralActivity() { activity := &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "the-one", ActivityType: &types.ActivityType{ Name: "activity-type", }, Domain: s.DomainName, TaskList: &types.TaskList{Name: s.TaskListName, Kind: types.TaskListKindEphemeral.Ptr()}, Input: []byte("input"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(20), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(10), } // Normal workflow with an ephemeral activity runID := s.startWorkflow(types.TaskListKindNormal).RunID taskList := s.waitUntilTaskListExists(types.TaskListTypeDecision) s.Equal(types.TaskListKindNormal, taskList.TaskList.GetKind()) poller := s.decisionPoller(types.TaskListKindNormal, &types.Decision{DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: activity}) cancelPoller := poller.PollAndProcessDecisions() defer cancelPoller() taskList = s.waitUntilTaskListExists(types.TaskListTypeActivity) s.Equal(types.TaskListKindEphemeral, taskList.TaskList.GetKind()) activityPoller := s.createEchoActivityPoller(types.TaskListKindEphemeral) cancelActivityPoller := activityPoller.PollAndProcessActivities() defer cancelActivityPoller() _, err := s.getWorkflowResult(runID) s.NoError(err, "failed to get workflow result") scheduled := s.getActivityScheduledEvent(runID) s.Equal(types.TaskListKindEphemeral, scheduled.TaskList.GetKind()) } // Run a workflow on an Ephemeral TaskList, which forces the activity to be dispatched to an Ephemeral TaskList. func (s *TaskListIntegrationSuite) TestEphemeralTaskList_EphemeralWorkflow() { activity := &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "the-one", ActivityType: &types.ActivityType{ Name: "activity-type", }, Domain: s.DomainName, TaskList: &types.TaskList{Name: s.TaskListName, Kind: types.TaskListKindNormal.Ptr()}, Input: []byte("input"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(20), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(10), } // Normal workflow with an ephemeral activity runID := s.startWorkflow(types.TaskListKindEphemeral).RunID // Just for sanity response := s.describeWorkflow(s.workflowID(), runID) s.Equal(types.TaskListKindEphemeral, response.WorkflowExecutionInfo.TaskList.GetKind()) taskList := s.waitUntilTaskListExists(types.TaskListTypeDecision) s.Equal(types.TaskListKindEphemeral, taskList.TaskList.GetKind()) poller := s.decisionPoller(types.TaskListKindEphemeral, &types.Decision{DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: activity}) cancelPoller := poller.PollAndProcessDecisions() defer cancelPoller() taskList = s.waitUntilTaskListExists(types.TaskListTypeActivity) s.Equal(types.TaskListKindEphemeral, taskList.TaskList.GetKind()) activityPoller := s.createEchoActivityPoller(types.TaskListKindEphemeral) cancelActivityPoller := activityPoller.PollAndProcessActivities() defer cancelActivityPoller() _, err := s.getWorkflowResult(runID) s.NoError(err, "failed to get workflow result") scheduled := s.getActivityScheduledEvent(runID) s.Equal(types.TaskListKindEphemeral, scheduled.TaskList.GetKind()) } func (s *TaskListIntegrationSuite) TestEphemeralTaskList_EphemeralChildWorkflow() { childWorkflowID := "ephemeral-child-workflow" activity := &types.StartChildWorkflowExecutionDecisionAttributes{ WorkflowID: childWorkflowID, WorkflowType: &types.WorkflowType{ Name: "noop", }, Domain: s.DomainName, TaskList: &types.TaskList{Name: s.TaskListName, Kind: types.TaskListKindNormal.Ptr()}, Input: []byte("input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(20), Control: nil, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), } // Normal workflow with an ephemeral activity runID := s.startWorkflow(types.TaskListKindEphemeral).RunID // Just for sanity response := s.describeWorkflow(s.workflowID(), runID) s.Equal(types.TaskListKindEphemeral, response.WorkflowExecutionInfo.TaskList.GetKind()) poller := s.decisionPoller(types.TaskListKindEphemeral, &types.Decision{DecisionType: types.DecisionTypeStartChildWorkflowExecution.Ptr(), StartChildWorkflowExecutionDecisionAttributes: activity}) cancelPoller := poller.PollAndProcessDecisions() defer cancelPoller() _, err := s.getWorkflowResult(runID) s.NoError(err, "failed to get workflow result") // Ensure the child is also ephemeral response = s.describeWorkflow(childWorkflowID, "") s.Equal(types.TaskListKindEphemeral, response.WorkflowExecutionInfo.TaskList.GetKind()) } func (s *TaskListIntegrationSuite) createEchoActivityPoller(tlKind types.TaskListKind) *TaskPoller { return &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: &types.TaskList{Name: s.TaskListName, Kind: tlKind.Ptr()}, Identity: "activity-poller", ActivityHandler: func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, takeToken []byte) ([]byte, bool, error) { return input, false, nil }, Logger: s.Logger, T: s.T(), CallOptions: []yarpc.CallOption{}, } } // decisionPoller creates a decision poller capable of running Workflows consisting of a single decision func (s *TaskListIntegrationSuite) decisionPoller(tlKind types.TaskListKind, decision *types.Decision) *TaskPoller { return &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: &types.TaskList{Name: s.TaskListName, Kind: tlKind.Ptr()}, Identity: "decision-poller", DecisionHandler: func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { // Treat any other workflow type as a no-op, and allow passing nil as a no-op if wt.GetName() != testWorkflowType || decision == nil { return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("done"), }, }}, nil } // Ignore the DecisionTaskScheduled and DecisionTaskStarted that'll be at the end latestEvent := history.Events[len(history.Events)-3] // Started Event only. We support one of three things: // - Activity Scheduled. We wait until it finishes // - Child Workflow. We wait until it finishes, ignoring the start. // - nil. We complete immediately if *latestEvent.EventType == types.EventTypeWorkflowExecutionStarted { if decision != nil { return nil, []*types.Decision{decision}, nil } } // Do nothing until the child workflow completes if *latestEvent.EventType == types.EventTypeChildWorkflowExecutionStarted { return nil, []*types.Decision{}, nil } // Once the decision is done we finish the workflow if *latestEvent.EventType == types.EventTypeActivityTaskCompleted || *latestEvent.EventType == types.EventTypeChildWorkflowExecutionCompleted { return nil, []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("done"), }, }}, nil } return nil, nil, fmt.Errorf("unexpected event type: %v", latestEvent.EventType) }, Logger: s.Logger, T: s.T(), CallOptions: []yarpc.CallOption{}, } } func (s *TaskListIntegrationSuite) createPoller(identity string, tlKind types.TaskListKind) *TaskPoller { return &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: &types.TaskList{Name: s.TaskListName, Kind: tlKind.Ptr()}, Identity: identity, DecisionHandler: func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { // Complete the workflow return []byte(strconv.Itoa(0)), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte(identity), }, }}, nil }, Logger: s.Logger, T: s.T(), CallOptions: []yarpc.CallOption{}, } } func (s *TaskListIntegrationSuite) startWorkflow(tlKind types.TaskListKind) *types.StartWorkflowExecutionResponse { identity := "test" request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: s.T().Name(), WorkflowType: &types.WorkflowType{ Name: "test-workflow", }, TaskList: &types.TaskList{ Name: s.TaskListName, Kind: tlKind.Ptr(), }, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(10), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: identity, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), } ctx, cancel := createContext() defer cancel() result, err := s.Engine.StartWorkflowExecution(ctx, request) s.Nil(err) return result } func (s *TaskListIntegrationSuite) workflowID() string { return s.T().Name() } func (s *TaskListIntegrationSuite) describeWorkflow(workflowID, runID string) *types.DescribeWorkflowExecutionResponse { request := &types.DescribeWorkflowExecutionRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, } ctx, cancel := createContext() defer cancel() result, err := s.Engine.DescribeWorkflowExecution(ctx, request) s.Nil(err) return result } func (s *TaskListIntegrationSuite) describeUntil(condition func(response *types.DescribeTaskListResponse) bool) *types.DescribeTaskListResponse { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() for { select { case <-ctx.Done(): s.FailNow("timed out waiting for condition") default: } result, err := s.Engine.DescribeTaskList(ctx, &types.DescribeTaskListRequest{ Domain: s.DomainName, TaskListType: types.TaskListTypeDecision.Ptr(), TaskList: &types.TaskList{ Name: s.TaskListName, Kind: types.TaskListKindNormal.Ptr(), }, IncludeTaskListStatus: true, }) if err != nil { s.T().Log("failed to describe task list: %w", err) time.Sleep(50 * time.Millisecond) continue } if condition(result) { return result } time.Sleep(50 * time.Millisecond) } } func (s *TaskListIntegrationSuite) waitUntilTaskListExists(tlType types.TaskListType) *types.DescribeTaskListResponse { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() for { select { case <-ctx.Done(): s.FailNow("timed out waiting for TaskList") default: } result, err := s.Engine.GetTaskListsByDomain(ctx, &types.GetTaskListsByDomainRequest{ Domain: s.DomainName, }) if err != nil { s.T().Log("failed to describe task list: %w", err) time.Sleep(50 * time.Millisecond) continue } if tlResponse, ok := result.ActivityTaskListMap[s.TaskListName]; ok && tlType == types.TaskListTypeActivity { return tlResponse } if tlResponse, ok := result.DecisionTaskListMap[s.TaskListName]; ok && tlType == types.TaskListTypeDecision { return tlResponse } time.Sleep(50 * time.Millisecond) } } func (s *TaskListIntegrationSuite) describeTaskList(tlType types.TaskListType) *types.DescribeTaskListResponse { ctx, cancel := createContext() defer cancel() result, err := s.Engine.DescribeTaskList(ctx, &types.DescribeTaskListRequest{ Domain: s.DomainName, TaskListType: tlType.Ptr(), TaskList: &types.TaskList{ Name: s.TaskListName, Kind: types.TaskListKindNormal.Ptr(), }, IncludeTaskListStatus: true, }) s.NoError(err, "failed to describe task list") return result } func (s *TaskListIntegrationSuite) getStartedEvent(runID string) *types.WorkflowExecutionStartedEventAttributes { ctx, cancel := createContext() defer cancel() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: s.T().Name(), RunID: runID, }, HistoryEventFilterType: types.HistoryEventFilterTypeAllEvent.Ptr(), }) s.NoError(err, "failed to get workflow history") history := historyResponse.History firstEvent := history.Events[0] if *firstEvent.EventType != types.EventTypeWorkflowExecutionStarted { s.FailNow("expected first event to be WorkflowExecutionStarted") } return firstEvent.WorkflowExecutionStartedEventAttributes } func (s *TaskListIntegrationSuite) getActivityScheduledEvent(runID string) *types.ActivityTaskScheduledEventAttributes { ctx, cancel := createContext() defer cancel() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: s.T().Name(), RunID: runID, }, HistoryEventFilterType: types.HistoryEventFilterTypeAllEvent.Ptr(), }) s.NoError(err, "failed to get workflow history") history := historyResponse.History for _, event := range history.Events { if *event.EventType == types.EventTypeActivityTaskScheduled { return event.ActivityTaskScheduledEventAttributes } } s.FailNow("failed to find EventTypeActivityTaskScheduled") return nil } func (s *TaskListIntegrationSuite) getWorkflowResult(runID string) (string, error) { ctx, cancel := createContext() defer cancel() historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: s.T().Name(), RunID: runID, }, HistoryEventFilterType: types.HistoryEventFilterTypeCloseEvent.Ptr(), WaitForNewEvent: true, }) if err != nil { return "", err } history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if *lastEvent.EventType != types.EventTypeWorkflowExecutionCompleted { return "", errors.New("workflow didn't complete") } return string(lastEvent.WorkflowExecutionCompletedEventAttributes.Result), nil } ================================================ FILE: host/taskpoller.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "context" "errors" "sync" "testing" "time" "github.com/stretchr/testify/require" "go.uber.org/yarpc" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/tasklist" ) type ( decisionTaskHandler func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) activityTaskHandler func(execution *types.WorkflowExecution, activityType *types.ActivityType, ActivityID string, input []byte, takeToken []byte) ([]byte, bool, error) queryHandler func(task *types.PollForDecisionTaskResponse) ([]byte, error) // TaskPoller is used in integration tests to poll decision or activity tasks TaskPoller struct { Engine FrontendClient Domain string TaskList *types.TaskList StickyTaskList *types.TaskList StickyScheduleToStartTimeoutSeconds *int32 Identity string DecisionHandler decisionTaskHandler ActivityHandler activityTaskHandler QueryHandler queryHandler Logger log.Logger T *testing.T CallOptions []yarpc.CallOption } ) // PollAndProcessDecisionTask for decision tasks func (p *TaskPoller) PollAndProcessDecisionTask(dumpHistory bool, dropTask bool) (isQueryTask bool, err error) { return p.PollAndProcessDecisionTaskWithAttempt(dumpHistory, dropTask, false, false, int64(0)) } // PollAndProcessDecisionTaskWithSticky for decision tasks func (p *TaskPoller) PollAndProcessDecisionTaskWithSticky(dumpHistory bool, dropTask bool) (isQueryTask bool, err error) { return p.PollAndProcessDecisionTaskWithAttempt(dumpHistory, dropTask, true, true, int64(0)) } // PollAndProcessDecisionTaskWithoutRetry for decision tasks func (p *TaskPoller) PollAndProcessDecisionTaskWithoutRetry(dumpHistory bool, dropTask bool) (isQueryTask bool, err error) { return p.PollAndProcessDecisionTaskWithAttemptAndRetry(dumpHistory, dropTask, false, false, int64(0), 1) } // PollAndProcessDecisionTaskWithAttempt for decision tasks func (p *TaskPoller) PollAndProcessDecisionTaskWithAttempt( dumpHistory bool, dropTask bool, pollStickyTaskList bool, respondStickyTaskList bool, decisionAttempt int64, ) (isQueryTask bool, err error) { return p.PollAndProcessDecisionTaskWithAttemptAndRetry( dumpHistory, dropTask, pollStickyTaskList, respondStickyTaskList, decisionAttempt, 5) } // PollAndProcessDecisionTaskWithAttemptAndRetry for decision tasks func (p *TaskPoller) PollAndProcessDecisionTaskWithAttemptAndRetry( dumpHistory bool, dropTask bool, pollStickyTaskList bool, respondStickyTaskList bool, decisionAttempt int64, retryCount int, ) (isQueryTask bool, err error) { isQueryTask, _, err = p.PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( dumpHistory, dropTask, pollStickyTaskList, respondStickyTaskList, decisionAttempt, retryCount, false, nil) return isQueryTask, err } // PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision for decision tasks func (p *TaskPoller) PollAndProcessDecisionTaskWithAttemptAndRetryAndForceNewDecision( dumpHistory bool, dropTask bool, pollStickyTaskList bool, respondStickyTaskList bool, decisionAttempt int64, retryCount int, forceCreateNewDecision bool, queryResult *types.WorkflowQueryResult, ) (isQueryTask bool, newTask *types.RespondDecisionTaskCompletedResponse, err error) { Loop: for attempt := 0; attempt < retryCount; attempt++ { taskList := p.TaskList if pollStickyTaskList { taskList = p.StickyTaskList } ctx, cancel := createContext() response, err1 := p.Engine.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: p.Domain, TaskList: taskList, Identity: p.Identity, }, p.CallOptions...) cancel() if err1 != nil { return false, nil, err1 } if response == nil || len(response.TaskToken) == 0 { p.Logger.Info("Empty Decision task: Polling again.") continue Loop } if response.GetNextEventID() == 0 { p.Logger.Fatal("NextEventID is not set for decision or query task") } var events []*types.HistoryEvent if response.Query == nil || !pollStickyTaskList { // if not query task, should have some history events // for non sticky query, there should be events returned history := response.History if history == nil { p.Logger.Fatal("History is nil") } events = history.Events if len(events) == 0 { p.Logger.Fatal("History Events are empty") } nextPageToken := response.NextPageToken for nextPageToken != nil { ctx, cancel := createContext() resp, err2 := p.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: p.Domain, Execution: response.WorkflowExecution, NextPageToken: nextPageToken, }, p.CallOptions...) cancel() if err2 != nil { return false, nil, err2 } events = append(events, resp.History.Events...) nextPageToken = resp.NextPageToken } } else { // for sticky query, there should be NO events returned // since worker side already has the state machine and we do not intend to update that. history := response.History nextPageToken := response.NextPageToken if !(history == nil || (len(history.Events) == 0 && nextPageToken == nil)) { // if history is not nil, and contains events or next token p.Logger.Fatal("History is not empty for sticky query") } } if dropTask { p.Logger.Info("Dropping Decision task: ") return false, nil, nil } if dumpHistory { PrettyPrintHistory(response.History, p.Logger) } // handle query task response if response.Query != nil { blob, err := p.QueryHandler(response) completeRequest := &types.RespondQueryTaskCompletedRequest{TaskToken: response.TaskToken} if err != nil { completeType := types.QueryTaskCompletedTypeFailed completeRequest.CompletedType = &completeType completeRequest.ErrorMessage = err.Error() } else { completeType := types.QueryTaskCompletedTypeCompleted completeRequest.CompletedType = &completeType completeRequest.QueryResult = blob } ctx, cancel := createContext() taskErr := p.Engine.RespondQueryTaskCompleted(ctx, completeRequest, p.CallOptions...) cancel() return true, nil, taskErr } // handle normal decision task / non query task response var lastDecisionScheduleEvent *types.HistoryEvent for _, e := range events { if e.GetEventType() == types.EventTypeDecisionTaskScheduled { lastDecisionScheduleEvent = e } } if lastDecisionScheduleEvent != nil && decisionAttempt > 0 { require.Equal(p.T, decisionAttempt, lastDecisionScheduleEvent.DecisionTaskScheduledEventAttributes.GetAttempt()) } executionCtx, decisions, err := p.DecisionHandler(response.WorkflowExecution, response.WorkflowType, common.Int64Default(response.PreviousStartedEventID), response.StartedEventID, response.History) if err != nil { p.Logger.Info("Failing Decision. Decision handler failed with error", tag.Error(err)) ctx, cancel := createContext() taskErr := p.Engine.RespondDecisionTaskFailed(ctx, &types.RespondDecisionTaskFailedRequest{ TaskToken: response.TaskToken, Cause: types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure.Ptr(), Details: []byte(err.Error()), Identity: p.Identity, }, p.CallOptions...) cancel() return isQueryTask, nil, taskErr } p.Logger.Info("Completing Decision. Decisions", tag.Value(decisions)) if !respondStickyTaskList { // non sticky tasklist ctx, cancel := createContext() newTask, err := p.Engine.RespondDecisionTaskCompleted(ctx, &types.RespondDecisionTaskCompletedRequest{ TaskToken: response.TaskToken, Identity: p.Identity, ExecutionContext: executionCtx, Decisions: decisions, ReturnNewDecisionTask: forceCreateNewDecision, ForceCreateNewDecisionTask: forceCreateNewDecision, QueryResults: getQueryResults(response.GetQueries(), queryResult), }, p.CallOptions...) cancel() return false, newTask, err } // sticky tasklist ctx, cancel = createContext() newTask, err := p.Engine.RespondDecisionTaskCompleted( ctx, &types.RespondDecisionTaskCompletedRequest{ TaskToken: response.TaskToken, Identity: p.Identity, ExecutionContext: executionCtx, Decisions: decisions, StickyAttributes: &types.StickyExecutionAttributes{ WorkerTaskList: p.StickyTaskList, ScheduleToStartTimeoutSeconds: p.StickyScheduleToStartTimeoutSeconds, }, ReturnNewDecisionTask: forceCreateNewDecision, ForceCreateNewDecisionTask: forceCreateNewDecision, QueryResults: getQueryResults(response.GetQueries(), queryResult), }, p.CallOptions..., ) cancel() return false, newTask, err } return false, nil, tasklist.ErrNoTasks } // HandlePartialDecision for decision task func (p *TaskPoller) HandlePartialDecision(response *types.PollForDecisionTaskResponse) ( *types.RespondDecisionTaskCompletedResponse, error) { if response == nil || len(response.TaskToken) == 0 { p.Logger.Info("Empty Decision task: Polling again.") return nil, nil } var events []*types.HistoryEvent history := response.History if history == nil { p.Logger.Fatal("History is nil") } events = history.Events if len(events) == 0 { p.Logger.Fatal("History Events are empty") } executionCtx, decisions, err := p.DecisionHandler(response.WorkflowExecution, response.WorkflowType, common.Int64Default(response.PreviousStartedEventID), response.StartedEventID, response.History) if err != nil { p.Logger.Info("Failing Decision. Decision handler failed with error: %v", tag.Error(err)) ctx, cancel := createContext() defer cancel() return nil, p.Engine.RespondDecisionTaskFailed(ctx, &types.RespondDecisionTaskFailedRequest{ TaskToken: response.TaskToken, Cause: types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure.Ptr(), Details: []byte(err.Error()), Identity: p.Identity, }, p.CallOptions...) } p.Logger.Info("Completing Decision. Decisions: %v", tag.Value(decisions)) // sticky tasklist ctx, cancel := createContext() defer cancel() newTask, err := p.Engine.RespondDecisionTaskCompleted( ctx, &types.RespondDecisionTaskCompletedRequest{ TaskToken: response.TaskToken, Identity: p.Identity, ExecutionContext: executionCtx, Decisions: decisions, StickyAttributes: &types.StickyExecutionAttributes{ WorkerTaskList: p.StickyTaskList, ScheduleToStartTimeoutSeconds: p.StickyScheduleToStartTimeoutSeconds, }, ReturnNewDecisionTask: true, ForceCreateNewDecisionTask: true, }, p.CallOptions..., ) return newTask, err } // PollAndProcessActivityTask for activity tasks func (p *TaskPoller) PollAndProcessActivityTask(dropTask bool) error { for attempt := 0; attempt < 5; attempt++ { ctx, ctxCancel := createContext() response, err1 := p.Engine.PollForActivityTask(ctx, &types.PollForActivityTaskRequest{ Domain: p.Domain, TaskList: p.TaskList, Identity: p.Identity, }, p.CallOptions...) ctxCancel() if err1 != nil { return err1 } if response == nil || len(response.TaskToken) == 0 { p.Logger.Info("Empty Activity task: Polling again.") return nil } if dropTask { p.Logger.Info("Dropping Activity task: ") return nil } p.Logger.Debug("Received Activity task", tag.Value(response)) result, cancel, err2 := p.ActivityHandler(response.WorkflowExecution, response.ActivityType, response.ActivityID, response.Input, response.TaskToken) if cancel { p.Logger.Info("Executing RespondActivityTaskCanceled") ctx, ctxCancel := createContext() taskErr := p.Engine.RespondActivityTaskCanceled(ctx, &types.RespondActivityTaskCanceledRequest{ TaskToken: response.TaskToken, Details: []byte("details"), Identity: p.Identity, }, p.CallOptions...) ctxCancel() return taskErr } if err2 != nil { ctx, ctxCancel := createContext() taskErr := p.Engine.RespondActivityTaskFailed(ctx, &types.RespondActivityTaskFailedRequest{ TaskToken: response.TaskToken, Reason: common.StringPtr(err2.Error()), Details: []byte(err2.Error()), Identity: p.Identity, }, p.CallOptions...) ctxCancel() return taskErr } ctx, ctxCancel = createContext() taskErr := p.Engine.RespondActivityTaskCompleted(ctx, &types.RespondActivityTaskCompletedRequest{ TaskToken: response.TaskToken, Identity: p.Identity, Result: result, }, p.CallOptions...) ctxCancel() return taskErr } return tasklist.ErrNoTasks } // PollAndProcessActivityTaskWithID is similar to PollAndProcessActivityTask but using RespondActivityTask...ByID func (p *TaskPoller) PollAndProcessActivityTaskWithID(dropTask bool) error { for attempt := 0; attempt < 5; attempt++ { ctx, ctxCancel := createContext() response, err1 := p.Engine.PollForActivityTask(ctx, &types.PollForActivityTaskRequest{ Domain: p.Domain, TaskList: p.TaskList, Identity: p.Identity, }, p.CallOptions...) ctxCancel() if err1 != nil { return err1 } if response == nil || len(response.TaskToken) == 0 { p.Logger.Info("Empty Activity task: Polling again.") return nil } if response.GetActivityID() == "" { p.Logger.Info("Empty ActivityID") return nil } if dropTask { p.Logger.Info("Dropping Activity task: ") return nil } p.Logger.Debug("Received Activity task: %v", tag.Value(response)) result, cancel, err2 := p.ActivityHandler(response.WorkflowExecution, response.ActivityType, response.ActivityID, response.Input, response.TaskToken) if cancel { p.Logger.Info("Executing RespondActivityTaskCanceled") ctx, ctxCancel := createContext() taskErr := p.Engine.RespondActivityTaskCanceledByID(ctx, &types.RespondActivityTaskCanceledByIDRequest{ Domain: p.Domain, WorkflowID: response.WorkflowExecution.GetWorkflowID(), RunID: response.WorkflowExecution.GetRunID(), ActivityID: response.GetActivityID(), Details: []byte("details"), Identity: p.Identity, }, p.CallOptions...) ctxCancel() return taskErr } if err2 != nil { ctx, ctxCancel := createContext() taskErr := p.Engine.RespondActivityTaskFailedByID(ctx, &types.RespondActivityTaskFailedByIDRequest{ Domain: p.Domain, WorkflowID: response.WorkflowExecution.GetWorkflowID(), RunID: response.WorkflowExecution.GetRunID(), ActivityID: response.GetActivityID(), Reason: common.StringPtr(err2.Error()), Details: []byte(err2.Error()), Identity: p.Identity, }, p.CallOptions...) ctxCancel() return taskErr } ctx, ctxCancel = createContext() taskErr := p.Engine.RespondActivityTaskCompletedByID(ctx, &types.RespondActivityTaskCompletedByIDRequest{ Domain: p.Domain, WorkflowID: response.WorkflowExecution.GetWorkflowID(), RunID: response.WorkflowExecution.GetRunID(), ActivityID: response.GetActivityID(), Identity: p.Identity, Result: result, }, p.CallOptions...) ctxCancel() return taskErr } return tasklist.ErrNoTasks } func (p *TaskPoller) PollAndProcessActivities() context.CancelFunc { var wg sync.WaitGroup wg.Add(1) ctx, cancel := context.WithCancel(context.Background()) go p.pollLoop(ctx, &wg, p.doPollActivityTask) return func() { cancel() wg.Wait() } } func (p *TaskPoller) PollAndProcessDecisions() context.CancelFunc { var wg sync.WaitGroup wg.Add(1) ctx, cancel := context.WithCancel(context.Background()) go p.pollLoop(ctx, &wg, p.doPollDecisionTask) return func() { cancel() wg.Wait() } } func (p *TaskPoller) pollLoop(ctx context.Context, wg *sync.WaitGroup, pollFunc func(context.Context) error) { for { select { case <-ctx.Done(): wg.Done() return default: err := pollFunc(ctx) if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { p.Logger.Error("Error while polling", tag.Error(err)) } } } } func (p *TaskPoller) doPollActivityTask(ctx context.Context) error { taskList := p.TaskList pollCtx, cancel := context.WithTimeout(ctx, time.Second*90) response, err := p.Engine.PollForActivityTask(pollCtx, &types.PollForActivityTaskRequest{ Domain: p.Domain, TaskList: taskList, Identity: p.Identity, }, p.CallOptions...) cancel() if err != nil { return err } if response == nil || len(response.TaskToken) == 0 { p.Logger.Info("Empty Decision task: Polling again.") return nil } result, shouldCancel, err := p.ActivityHandler(response.WorkflowExecution, response.ActivityType, response.ActivityID, response.Input, response.TaskToken) responseCtx, ctxCancel := context.WithTimeout(ctx, time.Second*5) defer ctxCancel() var responseErr error if err != nil { p.Logger.Info("Executed RespondActivityTaskFailed") responseErr = p.Engine.RespondActivityTaskFailed(responseCtx, &types.RespondActivityTaskFailedRequest{ TaskToken: response.TaskToken, Reason: common.StringPtr(err.Error()), Details: []byte(err.Error()), Identity: p.Identity, }, p.CallOptions...) } else if shouldCancel { p.Logger.Info("Executing RespondActivityTaskCanceled") responseErr = p.Engine.RespondActivityTaskCanceled(responseCtx, &types.RespondActivityTaskCanceledRequest{ TaskToken: response.TaskToken, Details: []byte("details"), Identity: p.Identity, }, p.CallOptions...) } else { p.Logger.Info("Executing RespondActivityTaskCompleted") responseErr = p.Engine.RespondActivityTaskCompleted(responseCtx, &types.RespondActivityTaskCompletedRequest{ TaskToken: response.TaskToken, Identity: p.Identity, Result: result, }, p.CallOptions...) } return responseErr } func (p *TaskPoller) doPollDecisionTask(ctx context.Context) error { taskList := p.TaskList pollCtx, cancel := context.WithTimeout(ctx, time.Second*90) response, err := p.Engine.PollForDecisionTask(pollCtx, &types.PollForDecisionTaskRequest{ Domain: p.Domain, TaskList: taskList, Identity: p.Identity, }, p.CallOptions...) cancel() if err != nil { return err } if response == nil || len(response.TaskToken) == 0 { p.Logger.Info("Empty Decision task: Polling again.") return nil } if response.GetNextEventID() == 0 { p.Logger.Fatal("NextEventID is not set for decision or query task") } var events []*types.HistoryEvent if response.Query == nil { // if not query task, should have some history events // for non sticky query, there should be events returned history := response.History if history == nil { p.Logger.Fatal("History is nil") } events = history.Events if len(events) == 0 { p.Logger.Fatal("History Events are empty") } nextPageToken := response.NextPageToken for nextPageToken != nil { historyCtx, cancel := context.WithTimeout(ctx, time.Second*90) resp, err2 := p.Engine.GetWorkflowExecutionHistory(historyCtx, &types.GetWorkflowExecutionHistoryRequest{ Domain: p.Domain, Execution: response.WorkflowExecution, NextPageToken: nextPageToken, }, p.CallOptions...) cancel() if err2 != nil { return err2 } events = append(events, resp.History.Events...) nextPageToken = resp.NextPageToken } } else { // for sticky query, there should be NO events returned // since worker side already has the state machine and we do not intend to update that. history := response.History nextPageToken := response.NextPageToken if !(history == nil || (len(history.Events) == 0 && nextPageToken == nil)) { // if history is not nil, and contains events or next token p.Logger.Fatal("History is not empty for sticky query") } } // handle query task response if response.Query != nil { blob, err := p.QueryHandler(response) completeRequest := &types.RespondQueryTaskCompletedRequest{TaskToken: response.TaskToken} if err != nil { completeType := types.QueryTaskCompletedTypeFailed completeRequest.CompletedType = &completeType completeRequest.ErrorMessage = err.Error() } else { completeType := types.QueryTaskCompletedTypeCompleted completeRequest.CompletedType = &completeType completeRequest.QueryResult = blob } respondCtx, cancel := context.WithTimeout(ctx, time.Second*90) taskErr := p.Engine.RespondQueryTaskCompleted(respondCtx, completeRequest, p.CallOptions...) cancel() return taskErr } executionCtx, decisions, err := p.DecisionHandler(response.WorkflowExecution, response.WorkflowType, common.Int64Default(response.PreviousStartedEventID), response.StartedEventID, response.History) if err != nil { p.Logger.Info("Failing Decision. Decision handler failed with error", tag.Error(err)) respondCtx, cancel := context.WithTimeout(ctx, time.Second*90) taskErr := p.Engine.RespondDecisionTaskFailed(respondCtx, &types.RespondDecisionTaskFailedRequest{ TaskToken: response.TaskToken, Cause: types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure.Ptr(), Details: []byte(err.Error()), Identity: p.Identity, }, p.CallOptions...) cancel() return taskErr } p.Logger.Info("Completing Decision. Decisions", tag.Value(decisions)) // non sticky tasklist respondCtx, cancel := context.WithTimeout(ctx, time.Second*90) _, err = p.Engine.RespondDecisionTaskCompleted(respondCtx, &types.RespondDecisionTaskCompletedRequest{ TaskToken: response.TaskToken, Identity: p.Identity, ExecutionContext: executionCtx, Decisions: decisions, ReturnNewDecisionTask: false, ForceCreateNewDecisionTask: false, }, p.CallOptions...) cancel() return err } func createContext() (context.Context, context.CancelFunc) { ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) return ctx, cancel } func getQueryResults(queries map[string]*types.WorkflowQuery, queryResult *types.WorkflowQueryResult) map[string]*types.WorkflowQueryResult { result := make(map[string]*types.WorkflowQueryResult) for k := range queries { result[k] = queryResult } return result } ================================================ FILE: host/test_suites.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "github.com/stretchr/testify/require" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/client" "go.uber.org/cadence/worker" ) // NOTE: the following definitions can't be defined in *_test.go // since they need to be exported and used by our internal tests type ( IntegrationSuite struct { // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions *IntegrationBase } IntegrationQueueV2Suite struct { *IntegrationSuite } SizeLimitIntegrationSuite struct { // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions *IntegrationBase } ClientIntegrationSuite struct { // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions *IntegrationBase wfService workflowserviceclient.Interface wfClient client.Client worker worker.Worker taskList string } AsyncWFIntegrationSuite struct { *require.Assertions *IntegrationBase } WorkflowIDRateLimitIntegrationSuite struct { *require.Assertions *IntegrationBase } WorkflowIDInternalRateLimitIntegrationSuite struct { *require.Assertions *IntegrationBase } TaskListIntegrationSuite struct { *require.Assertions *IntegrationBase TaskListName string } TaskListIsolationIntegrationSuite struct { *require.Assertions *IntegrationBase } DecisionTimeoutMaxAttemptsIntegrationSuite struct { *require.Assertions *IntegrationBase } ) ================================================ FILE: host/testcluster.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package host import ( "context" "io/ioutil" "os" "testing" "time" "github.com/startreedata/pinot-client-go/pinot" "github.com/uber-go/tally" adminClient "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/filestore" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/messaging/kafka" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/nosql" persistencetests "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/persistence/persistence-tests/testcluster" "github.com/uber/cadence/common/persistence/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin/mysql" "github.com/uber/cadence/common/persistence/sql/sqlplugin/postgres" "github.com/uber/cadence/common/persistence/sql/sqlplugin/sqlite" pnt "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/testflags" // the import is a test dependency _ "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql/public" ) type ( // TestCluster is a base struct for integration tests TestCluster struct { testBase *persistencetests.TestBase archiverBase *ArchiverBase host Cadence doneCh chan struct{} } // ArchiverBase is a base struct for archiver provider being used in integration tests ArchiverBase struct { metadata archiver.ArchivalMetadata provider provider.ArchiverProvider historyStoreDirectory string visibilityStoreDirectory string historyURI string visibilityURI string } // TestClusterConfig are config for a test cluster TestClusterConfig struct { FrontendAddress string EnableArchival bool IsPrimaryCluster bool ClusterNo int ClusterGroupMetadata config.ClusterGroupMetadata MessagingClientConfig *MessagingClientConfig Persistence persistencetests.TestBaseOptions HistoryConfig *HistoryConfig MatchingConfig *MatchingConfig ESConfig *config.ElasticSearchConfig WorkerConfig *WorkerConfig MockAdminClient map[string]adminClient.Client PinotConfig *config.PinotVisibilityConfig AsyncWFQueues map[string]config.AsyncWorkflowQueueProvider DynamicClientConfig dynamicconfig.FileBasedClientConfig // TimeSource is used to override the time source of internal components. // Note that most components don't respect this, and it's only used in a few places. // e.g. async workflow test's consumer manager and domain manager TimeSource clock.MockedTimeSource FrontendDynamicConfigOverrides map[dynamicproperties.Key]interface{} HistoryDynamicConfigOverrides map[dynamicproperties.Key]interface{} MatchingDynamicConfigOverrides map[dynamicproperties.Key]interface{} WorkerDynamicConfigOverrides map[dynamicproperties.Key]interface{} } // MessagingClientConfig is the config for messaging config MessagingClientConfig struct { UseMock bool KafkaConfig *config.KafkaConfig } // WorkerConfig is the config for enabling/disabling cadence worker WorkerConfig struct { EnableArchiver bool EnableIndexer bool EnableReplicator bool EnableAsyncWFConsumer bool } ) const ( defaultTestValueOfESIndexMaxResultWindow = 5 defaultTestPersistenceTimeout = 5 * time.Second ) // NewCluster creates and sets up the test cluster func NewCluster(t *testing.T, options *TestClusterConfig, logger log.Logger, params persistencetests.TestBaseParams) (*TestCluster, error) { testBase := persistencetests.NewTestBaseFromParams(t, params) testBase.Setup() setupShards(testBase, options.HistoryConfig.NumHistoryShards, logger) archiverBase := newArchiverBase(options.EnableArchival, logger) messagingClient := getMessagingClient(options.MessagingClientConfig, logger) pConfig := testBase.Config() pConfig.NumHistoryShards = options.HistoryConfig.NumHistoryShards var esClient elasticsearch.GenericClient if options.WorkerConfig.EnableIndexer { var err error esClient, err = elasticsearch.NewGenericClient(options.ESConfig, logger) if err != nil { return nil, err } pConfig.AdvancedVisibilityStore = "es-visibility" } scope := tally.NewTestScope("integration-test", nil) metricsClient := metrics.NewClient(scope, metrics.ServiceIdx(0), metrics.HistogramMigration{} /* default, only used in test setups */) domainReplicationQueue := domain.NewReplicationQueue( testBase.DomainReplicationQueueMgr, options.ClusterGroupMetadata.CurrentClusterName, metricsClient, logger, ) aConfig := noopAuthorizationConfig() doneCh := make(chan struct{}) dynamicClient, err := dynamicconfig.NewFileBasedClient( &options.DynamicClientConfig, logger, doneCh, ) if err != nil { return nil, err } cadenceParams := &CadenceParams{ ClusterMetadata: params.ClusterMetadata, PersistenceConfig: pConfig, MessagingClient: messagingClient, DomainManager: testBase.DomainManager, HistoryV2Mgr: testBase.HistoryV2Mgr, ExecutionMgrFactory: testBase.ExecutionMgrFactory, DomainReplicationQueue: domainReplicationQueue, Logger: logger, ClusterNo: options.ClusterNo, ESConfig: options.ESConfig, ESClient: esClient, ArchiverMetadata: archiverBase.metadata, ArchiverProvider: archiverBase.provider, HistoryConfig: options.HistoryConfig, MatchingConfig: options.MatchingConfig, WorkerConfig: options.WorkerConfig, MockAdminClient: options.MockAdminClient, DomainReplicationTaskExecutor: domain.NewReplicationTaskExecutor(testBase.DomainManager, newNoopDomainAuditManager(), clock.NewRealTimeSource(), logger, dynamicproperties.GetBoolPropertyFn(false)), AuthorizationConfig: aConfig, AsyncWFQueues: options.AsyncWFQueues, TimeSource: options.TimeSource, DynamicClient: dynamicClient, FrontendDynCfgOverrides: options.FrontendDynamicConfigOverrides, HistoryDynCfgOverrides: options.HistoryDynamicConfigOverrides, MatchingDynCfgOverrides: options.MatchingDynamicConfigOverrides, WorkerDynCfgOverrides: options.WorkerDynamicConfigOverrides, } cluster := NewCadence(cadenceParams) if err := cluster.Start(); err != nil { return nil, err } return &TestCluster{testBase: testBase, archiverBase: archiverBase, host: cluster, doneCh: doneCh}, nil } func NewPinotTestCluster(t *testing.T, options *TestClusterConfig, logger log.Logger, params persistencetests.TestBaseParams) (*TestCluster, error) { testBase := persistencetests.NewTestBaseFromParams(t, params) testBase.Setup() setupShards(testBase, options.HistoryConfig.NumHistoryShards, logger) archiverBase := newArchiverBase(options.EnableArchival, logger) messagingClient := getMessagingClient(options.MessagingClientConfig, logger) pConfig := testBase.Config() pConfig.NumHistoryShards = options.HistoryConfig.NumHistoryShards var esClient elasticsearch.GenericClient var pinotClient pnt.GenericClient if options.WorkerConfig.EnableIndexer { var err error if options.PinotConfig.Migration.Enabled { esClient, err = elasticsearch.NewGenericClient(options.ESConfig, logger) if err != nil { return nil, err } } } pConfig.AdvancedVisibilityStore = "pinot-visibility" pinotBroker := options.PinotConfig.Broker pinotRawClient, err := pinot.NewFromBrokerList([]string{pinotBroker}) if err != nil || pinotRawClient == nil { return nil, err } pinotClient = pnt.NewPinotClient(pinotRawClient, logger, options.PinotConfig) scope := tally.NewTestScope("integration-test", nil) metricsClient := metrics.NewClient(scope, metrics.ServiceIdx(0), metrics.HistogramMigration{} /* default, only used in test setups */) domainReplicationQueue := domain.NewReplicationQueue( testBase.DomainReplicationQueueMgr, options.ClusterGroupMetadata.CurrentClusterName, metricsClient, logger, ) aConfig := noopAuthorizationConfig() doneCh := make(chan struct{}) dynamicClient, err := dynamicconfig.NewFileBasedClient( &options.DynamicClientConfig, logger, doneCh, ) if err != nil { return nil, err } cadenceParams := &CadenceParams{ ClusterMetadata: params.ClusterMetadata, PersistenceConfig: pConfig, MessagingClient: messagingClient, DomainManager: testBase.DomainManager, HistoryV2Mgr: testBase.HistoryV2Mgr, ExecutionMgrFactory: testBase.ExecutionMgrFactory, DomainReplicationQueue: domainReplicationQueue, Logger: logger, ClusterNo: options.ClusterNo, ESConfig: options.ESConfig, ESClient: esClient, ArchiverMetadata: archiverBase.metadata, ArchiverProvider: archiverBase.provider, HistoryConfig: options.HistoryConfig, MatchingConfig: options.MatchingConfig, WorkerConfig: options.WorkerConfig, MockAdminClient: options.MockAdminClient, DomainReplicationTaskExecutor: domain.NewReplicationTaskExecutor(testBase.DomainManager, newNoopDomainAuditManager(), clock.NewRealTimeSource(), logger, dynamicproperties.GetBoolPropertyFn(false)), AuthorizationConfig: aConfig, PinotConfig: options.PinotConfig, PinotClient: pinotClient, DynamicClient: dynamicClient, } cluster := NewCadence(cadenceParams) if err := cluster.Start(); err != nil { return nil, err } return &TestCluster{testBase: testBase, archiverBase: archiverBase, host: cluster, doneCh: doneCh}, nil } func noopAuthorizationConfig() config.Authorization { return config.Authorization{ OAuthAuthorizer: config.OAuthAuthorizer{ Enable: false, }, NoopAuthorizer: config.NoopAuthorizer{ Enable: true, }, } } // NewClusterMetadata returns cluster metdata from config func NewClusterMetadata(t *testing.T, options *TestClusterConfig) cluster.Metadata { if options.ClusterGroupMetadata.PrimaryClusterName == "" { return cluster.GetTestClusterMetadata(true) } return cluster.NewMetadata( options.ClusterGroupMetadata, func(domain string) bool { return false }, metrics.NewNoopMetricsClient(), testlogger.New(t), ) } func NewPersistenceTestCluster(t *testing.T, clusterConfig *TestClusterConfig) testcluster.PersistenceTestCluster { // NOTE: Override here to keep consistent. clusterConfig will be used in the test for some purposes. clusterConfig.Persistence.StoreType = TestFlags.PersistenceType clusterConfig.Persistence.DBPluginName = TestFlags.SQLPluginName var testCluster testcluster.PersistenceTestCluster var err error if TestFlags.PersistenceType == config.StoreTypeCassandra { // TODO refactor to support other NoSQL ops := clusterConfig.Persistence ops.DBPluginName = "cassandra" testflags.RequireCassandra(t) testCluster = nosql.NewTestCluster(t, nosql.TestClusterParams{ PluginName: ops.DBPluginName, KeySpace: ops.DBName, Username: ops.DBUsername, Password: ops.DBPassword, Host: ops.DBHost, Port: ops.DBPort, ProtoVersion: ops.ProtoVersion, SchemaBaseDir: "", }) } else if TestFlags.PersistenceType == config.StoreTypeSQL { var ops *persistencetests.TestBaseOptions switch TestFlags.SQLPluginName { case mysql.PluginName: testflags.RequireMySQL(t) ops, err = mysql.GetTestClusterOption() case postgres.PluginName: testflags.RequirePostgres(t) ops, err = postgres.GetTestClusterOption() case sqlite.PluginName: ops = sqlite.GetTestClusterOption() default: t.Fatal("not supported plugin " + TestFlags.SQLPluginName) } if err != nil { t.Fatal(err) } testCluster, err = sql.NewTestCluster(TestFlags.SQLPluginName, clusterConfig.Persistence.DBName, ops.DBUsername, ops.DBPassword, ops.DBHost, ops.DBPort, ops.SchemaDir) if err != nil { t.Fatal(err) } } else { t.Fatal("not supported storage type" + TestFlags.PersistenceType) } return testCluster } func newNoopDomainAuditManager() persistence.DomainAuditManager { return &noopDomainAuditManager{} } type noopDomainAuditManager struct{} func (n *noopDomainAuditManager) GetName() string { return "noop" } func (n *noopDomainAuditManager) Close() {} func (n *noopDomainAuditManager) CreateDomainAuditLog(ctx context.Context, request *persistence.CreateDomainAuditLogRequest) (*persistence.CreateDomainAuditLogResponse, error) { return &persistence.CreateDomainAuditLogResponse{}, nil } func (n *noopDomainAuditManager) GetDomainAuditLogs(ctx context.Context, request *persistence.GetDomainAuditLogsRequest) (*persistence.GetDomainAuditLogsResponse, error) { return &persistence.GetDomainAuditLogsResponse{}, nil } func setupShards(testBase *persistencetests.TestBase, numHistoryShards int, logger log.Logger) { // shard 0 is always created, we create additional shards if needed for shardID := 1; shardID < numHistoryShards; shardID++ { ctx, cancel := context.WithTimeout(context.Background(), defaultTestPersistenceTimeout) err := testBase.CreateShard(ctx, shardID, "", 0) if err != nil { cancel() logger.Fatal("Failed to create shard", tag.Error(err)) } cancel() } } func newArchiverBase(enabled bool, logger log.Logger) *ArchiverBase { dcCollection := dynamicconfig.NewNopCollection() if !enabled { return &ArchiverBase{ metadata: archiver.NewArchivalMetadata(dcCollection, "", false, "", false, &config.ArchivalDomainDefaults{}), provider: provider.NewNoOpArchiverProvider(), } } historyStoreDirectory, err := ioutil.TempDir("", "test-history-archival") if err != nil { logger.Fatal("Failed to create temp dir for history archival", tag.Error(err)) } visibilityStoreDirectory, err := ioutil.TempDir("", "test-visibility-archival") if err != nil { logger.Fatal("Failed to create temp dir for visibility archival", tag.Error(err)) } cfg := &config.FilestoreArchiver{ FileMode: "0666", DirMode: "0766", } node, err := config.ToYamlNode(cfg) if err != nil { logger.Fatal("Should be impossible: failed to convert filestore archiver config to a yaml node") } archiverProvider := provider.NewArchiverProvider( config.HistoryArchiverProvider{config.FilestoreConfig: node}, config.VisibilityArchiverProvider{config.FilestoreConfig: node}, ) return &ArchiverBase{ metadata: archiver.NewArchivalMetadata(dcCollection, "enabled", true, "enabled", true, &config.ArchivalDomainDefaults{ History: config.HistoryArchivalDomainDefaults{ Status: "enabled", URI: "testScheme://test/history/archive/path", }, Visibility: config.VisibilityArchivalDomainDefaults{ Status: "enabled", URI: "testScheme://test/visibility/archive/path", }, }), provider: archiverProvider, historyStoreDirectory: historyStoreDirectory, visibilityStoreDirectory: visibilityStoreDirectory, historyURI: filestore.URIScheme + "://" + historyStoreDirectory, visibilityURI: filestore.URIScheme + "://" + visibilityStoreDirectory, } } func getMessagingClient(config *MessagingClientConfig, logger log.Logger) messaging.Client { if config == nil || config.UseMock { return mocks.NewMockMessagingClient(&mocks.KafkaProducer{}, nil) } checkApp := len(config.KafkaConfig.Applications) != 0 return kafka.NewKafkaClient(config.KafkaConfig, metrics.NewNoopMetricsClient(), logger, tally.NoopScope, checkApp) } // TearDownCluster tears down the test cluster func (tc *TestCluster) TearDownCluster() { tc.host.Stop() tc.host = nil close(tc.doneCh) tc.testBase.TearDownWorkflowStore() os.RemoveAll(tc.archiverBase.historyStoreDirectory) os.RemoveAll(tc.archiverBase.visibilityStoreDirectory) } // GetFrontendClient returns a frontend client from the test cluster func (tc *TestCluster) GetFrontendClient() FrontendClient { return tc.host.GetFrontendClient() } // GetAdminClient returns an admin client from the test cluster func (tc *TestCluster) GetAdminClient() AdminClient { return tc.host.GetAdminClient() } // GetHistoryClient returns a history client from the test cluster func (tc *TestCluster) GetHistoryClient() HistoryClient { return tc.host.GetHistoryClient() } // GetMatchingClient returns a matching client from the test cluster func (tc *TestCluster) GetMatchingClient() MatchingClient { return tc.host.GetMatchingClient() } func (tc *TestCluster) GetMatchingClients() []MatchingClient { clients := tc.host.GetMatchingClients() result := make([]MatchingClient, 0, len(clients)) for _, client := range clients { result = append(result, client) } return result } // GetExecutionManagerFactory returns an execution manager factory from the test cluster func (tc *TestCluster) GetExecutionManagerFactory() persistence.ExecutionManagerFactory { return tc.host.GetExecutionManagerFactory() } ================================================ FILE: host/testdata/clientintegrationtestcluster.yaml ================================================ enablearchival: false clusterno: 0 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: false enablereplicator: false enableindexer: false dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/dynamicconfig/integration_queuev2_test.yaml ================================================ history.enableTimerQueueV2: - value: true constraints: {} history.enableTransferQueueV2: - value: true constraints: {} frontend.warmupDuration: - value: "1s" constraints: {} ================================================ FILE: host/testdata/dynamicconfig/integration_queuev2_with_alert_test.yaml ================================================ frontend.warmupDuration: - value: "1s" constraints: {} history.enableTimerQueueV2: - value: true constraints: {} history.enableTransferQueueV2: - value: true constraints: {} history.shardUpdateMinInterval: - value: 3s constraints: {} history.timerProcessorUpdateAckInterval: - value: 5s constraints: {} history.timerProcessorUpdateAckIntervalJitterCoefficient: - value: 0 constraints: {} history.transferProcessorUpdateAckInterval: - value: 5s constraints: {} history.transferProcessorUpdateAckIntervalJitterCoefficient: - value: 0 constraints: {} history.queueProcessorPollBackoffInterval: - value: 5s constraints: {} history.virtualSliceForceAppendInterval: - value: 100ms constraints: {} # Only Enable 1 level of split, which has been verified in simulation history.queueMaxVirtualQueueCount: - value: 2 constraints: {} # Enable task rate limiter so that the number of pending tasks increases history.taskSchedulerEnableRateLimiter: - value: true constraints: {} history.taskSchedulerEnableRateLimiterShadowMode: - value: false constraints: {} history.taskSchedulerGlobalDomainRPS: - value: 30 constraints: {} # Enable pending task queue alert for integration test history.enableTransferQueueV2PendingTaskCountAlert: - value: true constraints: {} history.enableTimerQueueV2PendingTaskCountAlert: - value: true constraints: {} # Set a low number to trigger queue pause with load from tests history.queueMaxPendingTaskCount: - value: 20 constraints: {} # Set a low number to trigger queue split with load from tests history.queueCriticalPendingTaskCount: - value: 18 constraints: {} ================================================ FILE: host/testdata/dynamicconfig/integration_test.yaml ================================================ frontend.warmupDuration: - value: "1s" constraints: {} frontend.enableActiveClusterSelectionPolicyInStartWorkflow: - value: true constraints: {} ================================================ FILE: host/testdata/es_os2_index_template.json ================================================ { "index_patterns": [ "test-visibility*" ], "template": { "aliases": {}, "mappings": { "dynamic": "false", "properties": { "Attr": { "properties": { "BinaryChecksums": { "type": "keyword" }, "CadenceChangeVersion": { "type": "keyword" }, "CustomBoolField": { "type": "boolean" }, "CustomDatetimeField": { "type": "date" }, "CustomDomain": { "type": "keyword" }, "CustomDoubleField": { "type": "double" }, "CustomIntField": { "type": "long" }, "CustomKeywordField": { "type": "keyword" }, "CustomStringField": { "type": "text" }, "Operator": { "type": "keyword" }, "Passed": { "type": "boolean" }, "RolloutID": { "type": "keyword" }, "addon": { "type": "keyword" }, "addon-type": { "type": "keyword" }, "environment": { "type": "keyword" }, "project": { "type": "keyword" }, "service": { "type": "keyword" }, "user": { "type": "keyword" } } }, "CloseStatus": { "type": "integer" }, "CloseTime": { "type": "long" }, "DomainID": { "type": "keyword" }, "ExecutionTime": { "type": "long" }, "HistoryLength": { "type": "integer" }, "IsCron": { "type": "boolean" }, "KafkaKey": { "type": "keyword" }, "NumClusters": { "type": "long" }, "ClusterAttributeScope": { "type": "keyword" }, "ClusterAttributeName": { "type": "keyword" }, "RunID": { "type": "keyword" }, "ShardID": { "type": "long" }, "StartTime": { "type": "long" }, "WorkflowID": { "type": "keyword" }, "WorkflowType": { "type": "keyword" } } }, "settings": { "index": { "number_of_replicas": "0", "number_of_shards": "5" } } } } ================================================ FILE: host/testdata/es_v6_index_template.json ================================================ { "order": 0, "index_patterns": [ "test-visibility*" ], "settings": { "index": { "number_of_shards": "5", "number_of_replicas": "0" } }, "mappings": { "_doc": { "dynamic": "false", "properties": { "DomainID": { "type": "keyword" }, "WorkflowID": { "type": "keyword" }, "RunID": { "type": "keyword" }, "WorkflowType": { "type": "keyword" }, "StartTime": { "type": "long" }, "ExecutionTime": { "type": "long" }, "CloseTime": { "type": "long" }, "CloseStatus": { "type": "integer" }, "HistoryLength": { "type": "integer" }, "IsCron": { "type": "boolean" }, "NumClusters": { "type": "long" }, "ClusterAttributeScope": { "type": "keyword" }, "ClusterAttributeName": { "type": "keyword" }, "KafkaKey": { "type": "keyword" }, "Attr": { "properties": { "CadenceChangeVersion": { "type": "keyword" }, "CustomStringField": { "type": "text" }, "CustomKeywordField": { "type": "keyword"}, "CustomIntField": { "type": "long"}, "CustomBoolField": { "type": "boolean"}, "CustomDoubleField": { "type": "double"}, "CustomDatetimeField": { "type": "date"}, "project": { "type": "keyword"}, "service": { "type": "keyword"}, "environment": { "type": "keyword"}, "addon": { "type": "keyword"}, "addon-type": { "type": "keyword"}, "user": { "type": "keyword"}, "CustomDomain": { "type": "keyword"}, "Operator": { "type": "keyword"}, "RolloutID": { "type": "keyword"}, "BinaryChecksums": { "type": "keyword"}, "Passed": { "type": "boolean" } } } } } }, "aliases": {} } ================================================ FILE: host/testdata/es_v7_index_template.json ================================================ { "order": 0, "index_patterns": [ "test-visibility*" ], "settings": { "index": { "number_of_shards": "5", "number_of_replicas": "0" } }, "mappings": { "dynamic": "false", "properties": { "DomainID": { "type": "keyword" }, "WorkflowID": { "type": "keyword" }, "RunID": { "type": "keyword" }, "WorkflowType": { "type": "keyword" }, "StartTime": { "type": "long" }, "ExecutionTime": { "type": "long" }, "CloseTime": { "type": "long" }, "CloseStatus": { "type": "integer" }, "HistoryLength": { "type": "integer" }, "IsCron": { "type": "boolean" }, "NumClusters": { "type": "long" }, "ClusterAttributeScope": { "type": "keyword" }, "ClusterAttributeName": { "type": "keyword" }, "KafkaKey": { "type": "keyword" }, "Attr": { "properties": { "CadenceChangeVersion": { "type": "keyword" }, "CustomStringField": { "type": "text" }, "CustomKeywordField": { "type": "keyword"}, "CustomIntField": { "type": "long"}, "CustomBoolField": { "type": "boolean"}, "CustomDoubleField": { "type": "double"}, "CustomDatetimeField": { "type": "date"}, "project": { "type": "keyword"}, "service": { "type": "keyword"}, "environment": { "type": "keyword"}, "addon": { "type": "keyword"}, "addon-type": { "type": "keyword"}, "user": { "type": "keyword"}, "CustomDomain": { "type": "keyword"}, "Operator": { "type": "keyword"}, "RolloutID": { "type": "keyword"}, "BinaryChecksums": { "type": "keyword"}, "Passed": { "type": "boolean" } } } } }, "aliases": {} } ================================================ FILE: host/testdata/integration_async_wf_with_kafka_cluster.yaml ================================================ clusterno: 1 asyncwfqueues: # test-async-wf-queue is the name of the queue. # it is used in async_wf_test.go # topic must be created before running the tests. it's done automatically by wurstmeuster/kafka container # in docker/docker-compose-async-wf-kafka.yml test-async-wf-queue: type: "kafka" config: connection: brokers: - "${KAFKA_SEEDS}:${KAFKA_PORT}" topic: "${ASYNC_WF_KAFKA_QUEUE_TOPIC}" messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enableasyncwfconsumer: true dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/integration_decision_timeout_cluster.yaml ================================================ enablearchival: false clusterno: 0 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: false enablereplicator: false enableindexer: false dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/integration_elasticsearch_os2_cluster.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" topics: test-visibility-topic: cluster: test test-visibility-topic-dlq: cluster: test applications: visibility: topic: test-visibility-topic dlq-topic: test-visibility-topic-dlq historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: false enablereplicator: false enableindexer: true esconfig: version: "os2" url: scheme: "http" host: "${ES_SEEDS}:9200" indices: visibility: test-visibility- dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/integration_elasticsearch_v6_cluster.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" topics: test-visibility-topic: cluster: test test-visibility-topic-dlq: cluster: test applications: visibility: topic: test-visibility-topic dlq-topic: test-visibility-topic-dlq historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: false enablereplicator: false enableindexer: true esconfig: url: scheme: "http" host: "${ES_SEEDS}:9200" indices: visibility: test-visibility- dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/integration_elasticsearch_v7_cluster.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" topics: test-visibility-topic: cluster: test test-visibility-topic-dlq: cluster: test applications: visibility: topic: test-visibility-topic dlq-topic: test-visibility-topic-dlq historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: false enablereplicator: false enableindexer: true esconfig: version: "v7" url: scheme: "http" host: "${ES_SEEDS}:9200" indices: visibility: test-visibility- dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/integration_pinot_cluster.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" topics: test-visibility-topic: cluster: test test-visibility-topic-dlq: cluster: test cadence-visibility-pinot: cluster: test cadence-visibility-pinot-dlq: cluster: test applications: pinot-visibility: topic: cadence-visibility-pinot dlq-topic: cadence-visibility-pinot-dlq visibility: topic: test-visibility-topic dlq-topic: test-visibility-topic-dlq historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: false enablereplicator: false enableindexer: false esconfig: version: "v7" url: scheme: "http" host: "${ES_SEEDS}:9200" indices: visibility: test-visibility- pinotconfig: broker: "${PINOT_SEEDS}:8099" cluster: pinot-test table: "cadence_visibility_pinot" migration: enabled: true dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/integration_queuev2_cluster.yaml ================================================ enablearchival: true clusterno: 0 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: true enablereplicator: true enableindexer: false dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_queuev2_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/integration_queuev2_with_alert_cluster.yaml ================================================ enablearchival: true clusterno: 0 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: true enablereplicator: true enableindexer: false dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_queuev2_with_alert_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/integration_sizelimit_cluster.yaml ================================================ enablearchival: false clusterno: 0 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 historycountlimiterror: 20 historycountlimitwarn: 10 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: false enablereplicator: false enableindexer: false dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/integration_test_cluster.yaml ================================================ enablearchival: true clusterno: 0 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: true enablereplicator: true enableindexer: false dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/integration_wfidratelimit_cluster.yaml ================================================ enablearchival: true clusterno: 2 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: false enablereplicator: false enableindexer: false dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/ndc_integration_test_clusters.yaml ================================================ - persistence: dbname: integration_active clustergroupmetadata: failoverVersionIncrement: 10 primaryClusterName: "active" currentClusterName: "active" clusterGroup: active: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:7114" rpcTransport: "grpc" standby: enabled: true initialFailoverVersion: 1 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:8114" rpcTransport: "grpc" other: enabled: true initialFailoverVersion: 2 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:9114" rpcTransport: "grpc" enablearchival: false workerconfig: enablearchiver: false enablereplicator: true enableindexer: false clusterno: 0 historyconfig: numhistoryshards: 1 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" topics: active: cluster: test active-dlq: cluster: test standby: cluster: test standby-dlq: cluster: test other: cluster: test other-dlq: cluster: test cadence-cluster-topics: active: topic: active dlq-topic: active-dlq standby: topic: standby dlq-topic: standby-dlq other: topic: other dlq-topic: other-dlq applications: { } dynamicclientconfig: filepath: "../testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" - persistence: dbname: integration_standby clustergroupmetadata: failoverVersionIncrement: 10 primaryClusterName: "active" currentClusterName: "standby" clusterGroup: active: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:7114" rpcTransport: "grpc" standby: enabled: true initialFailoverVersion: 1 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:8114" rpcTransport: "grpc" other: enabled: true initialFailoverVersion: 2 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:9114" rpcTransport: "grpc" enablearchival: false workerconfig: enablearchiver: false enablereplicator: true enableindexer: false clusterno: 1 historyconfig: numhistoryshards: 1 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" topics: active: cluster: test active-dlq: cluster: test standby: cluster: test standby-dlq: cluster: test other: cluster: test other-dlq: cluster: test cadence-cluster-topics: active: topic: active dlq-topic: active-dlq standby: topic: standby dlq-topic: standby-dlq other: topic: other dlq-topic: other-dlq applications: { } - persistence: dbname: integration_other clustergroupmetadata: failoverVersionIncrement: 10 primaryClusterName: "active" currentClusterName: "other" clusterGroup: active: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:7114" rpcTransport: "grpc" standby: enabled: true initialFailoverVersion: 1 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:8114" rpcTransport: "grpc" other: enabled: true initialFailoverVersion: 2 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:9114" rpcTransport: "grpc" enablearchival: false workerconfig: enablearchiver: false enablereplicator: true enableindexer: false clusterno: 2 historyconfig: numhistoryshards: 1 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" dynamicclientconfig: filepath: "../testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/task_list_test_cluster.yaml ================================================ enablearchival: false clusterno: 0 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enablearchiver: false enablereplicator: false enableindexer: false dynamicclientconfig: filepath: "testdata/dynamicconfig/integration_test.yaml" pollInterval: "10s" ================================================ FILE: host/testdata/xdc_integration_es_clusters.yaml ================================================ - persistence: dbname: integration_active_es clustergroupmetadata: failoverVersionIncrement: 10 primaryClusterName: "active-es" currentClusterName: "active-es" clusterGroup: active-es: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:9114" rpcTransport: "grpc" standby-es: enabled: true initialFailoverVersion: 1 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:10114" rpcTransport: "grpc" enablearchival: false workerconfig: enablearchiver: false enablereplicator: true enableindexer: true clusterno: 2 historyconfig: numhistoryshards: 1 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" topics: active-es: cluster: test active-es-dlq: cluster: test standby-es: cluster: test standby-es-dlq: cluster: test test-visibility-topic-0: cluster: test test-visibility-topic-0-dlq: cluster: test cadence-cluster-topics: active-es: topic: active-es dlq-topic: active-es-dlq standby-es: topic: standby-es dlq-topic: standby-es-dlq applications: visibility: topic: test-visibility-topic-0 dlq-topic: test-visibility-topic-0-dlq esconfig: url: scheme: "http" host: "${ES_SEEDS}:9200" indices: visibility: test-visibility-0- - persistence: dbname: integration_standby_es clustergroupmetadata: failoverVersionIncrement: 10 primaryClusterName: "active-es" currentClusterName: "standby-es" clusterGroup: active-es: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:9114" rpcTransport: "grpc" standby-es: enabled: true initialFailoverVersion: 1 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:10114" rpcTransport: "grpc" enablearchival: false workerconfig: enablearchiver: false enablereplicator: true enableindexer: true clusterno: 3 historyconfig: numhistoryshards: 1 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" topics: test-visibility-topic-1: cluster: test test-visibility-topic-1-dlq: cluster: test applications: visibility: topic: test-visibility-topic-1 dlq-topic: test-visibility-topic-1-dlq esconfig: url: scheme: "http" host: "${ES_SEEDS}:9200" indices: visibility: test-visibility-1- ================================================ FILE: host/testdata/xdc_integration_test_clusters.yaml ================================================ - persistence: dbname: integration_active clustergroupmetadata: failoverVersionIncrement: 10 primaryClusterName: "active" currentClusterName: "active" clusterGroup: active: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:7114" rpcTransport: "grpc" standby: enabled: true initialFailoverVersion: 1 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:8114" rpcTransport: "grpc" enablearchival: false workerconfig: enablearchiver: false enablereplicator: true enableindexer: false clusterno: 0 historyconfig: numhistoryshards: 1 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" topics: active: cluster: test active-dlq: cluster: test standby: cluster: test standby-dlq: cluster: test cadence-cluster-topics: active: topic: active dlq-topic: active-dlq standby: topic: standby dlq-topic: standby-dlq applications: { } - persistence: dbname: integration_standby clustergroupmetadata: failoverVersionIncrement: 10 primaryClusterName: "active" currentClusterName: "standby" clusterGroup: active: enabled: true initialFailoverVersion: 0 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:7114" rpcTransport: "grpc" standby: enabled: true initialFailoverVersion: 1 rpcName: "cadence-frontend" rpcAddress: "127.0.0.1:8114" rpcTransport: "grpc" enablearchival: false workerconfig: enablearchiver: false enablereplicator: true enableindexer: false clusterno: 1 historyconfig: numhistoryshards: 1 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 messagingclientconfig: usemock: false kafkaconfig: clusters: test: brokers: - "${KAFKA_SEEDS}:9092" applications: { } ================================================ FILE: host/workflowidratelimit_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package host import ( "flag" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/types" ) func TestWorkflowIDRateLimitIntegrationSuite(t *testing.T) { // Loads the flags for persistence etc., if none are given they are set in ./flag.go flag.Parse() clusterConfig, err := GetTestClusterConfig("testdata/integration_wfidratelimit_cluster.yaml") require.NoError(t, err) clusterConfig.TimeSource = clock.NewMockedTimeSource() clusterConfig.HistoryDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.WorkflowIDExternalRPS: 5, } testCluster := NewPersistenceTestCluster(t, clusterConfig) s := new(WorkflowIDRateLimitIntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } func (s *WorkflowIDRateLimitIntegrationSuite) SetupSuite() { s.SetupLogger() s.Logger.Info("Running integration test against test cluster") clusterMetadata := NewClusterMetadata(s.T(), s.TestClusterConfig) dc := persistence.DynamicConfiguration{ EnableCassandraAllConsistencyLevelDelete: dynamicproperties.GetBoolPropertyFn(true), PersistenceSampleLoggingRate: dynamicproperties.GetIntPropertyFn(100), EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(true), EnableHistoryTaskDualWriteMode: dynamicproperties.GetBoolPropertyFn(true), ReadNoSQLHistoryTaskFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(true), HistoryNodeDeleteBatchSize: dynamicproperties.GetIntPropertyFn(1000), } params := pt.TestBaseParams{ DefaultTestCluster: s.DefaultTestCluster, VisibilityTestCluster: s.VisibilityTestCluster, ClusterMetadata: clusterMetadata, DynamicConfiguration: dc, } cluster, err := NewCluster(s.T(), s.TestClusterConfig, s.Logger, params) s.Require().NoError(err) s.TestCluster = cluster s.Engine = s.TestCluster.GetFrontendClient() s.AdminClient = s.TestCluster.GetAdminClient() s.DomainName = s.RandomizeStr("integration-test-domain") s.Require().NoError(s.RegisterDomain(s.DomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.domainCacheRefresh() } func (s *WorkflowIDRateLimitIntegrationSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *WorkflowIDRateLimitIntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() } func (s *WorkflowIDRateLimitIntegrationSuite) TestWorkflowIDSpecificRateLimits() { const ( testWorkflowID = "integration-workflow-specific-rate-limit-test" testWorkflowType = "integration-workflow-specific-rate-limit-test-type" testTaskListName = "integration-workflow-specific-rate-limit-test-taskList" testIdentity = "worker1" ) request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: testWorkflowID, WorkflowType: &types.WorkflowType{Name: testWorkflowType}, TaskList: &types.TaskList{Name: testTaskListName}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: testIdentity, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyTerminateIfRunning.Ptr(), } ctx, cancel := createContext() defer cancel() // The ratelimit is 5 per second with a burst of 5, so we should be able to start 5 workflows without any error for i := 0; i < 5; i++ { _, err := s.Engine.StartWorkflowExecution(ctx, request) assert.NoError(s.T(), err) } // Now we should get a rate limit error (with some fuzziness for time passing) limited := 0 for i := 0; i < 5; i++ { _, err := s.Engine.StartWorkflowExecution(ctx, request) var busyErr *types.ServiceBusyError if err != nil { if assert.ErrorAs(s.T(), err, &busyErr) { limited++ assert.Equal(s.T(), constants.WorkflowIDRateLimitReason, busyErr.Reason) } } } // 5 fails occasionally, trying 4. If needed, reduce to 3 or find a way to // make this test less sensitive to latency, as test-runner hosts vary a lot. assert.GreaterOrEqual(s.T(), limited, 4, "should have encountered some rate-limit errors after the burst was exhausted") // After 1 second (200ms at a minimum) we should be able to start more workflows without being limited time.Sleep(1 * time.Second) _, err := s.Engine.StartWorkflowExecution(ctx, request) assert.NoError(s.T(), err) } ================================================ FILE: host/workflowsidinternalratelimit_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package host import ( "bytes" "encoding/binary" "flag" "strconv" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/tasklist" ) func TestWorkflowIDInternalRateLimitIntegrationSuite(t *testing.T) { // Loads the flags for persistence etc., if none are given they are set in ./flag.go flag.Parse() clusterConfig, err := GetTestClusterConfig("testdata/integration_wfidratelimit_cluster.yaml") require.NoError(t, err) clusterConfig.TimeSource = clock.NewMockedTimeSource() clusterConfig.HistoryDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.WorkflowIDInternalRPS: 2, } testCluster := NewPersistenceTestCluster(t, clusterConfig) s := new(WorkflowIDInternalRateLimitIntegrationSuite) params := IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = NewIntegrationBase(params) suite.Run(t, s) } func (s *WorkflowIDInternalRateLimitIntegrationSuite) SetupSuite() { s.SetupLogger() s.Logger.Info("Running integration test against test cluster") clusterMetadata := NewClusterMetadata(s.T(), s.TestClusterConfig) dc := persistence.DynamicConfiguration{ EnableCassandraAllConsistencyLevelDelete: dynamicproperties.GetBoolPropertyFn(true), PersistenceSampleLoggingRate: dynamicproperties.GetIntPropertyFn(100), EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(true), EnableHistoryTaskDualWriteMode: dynamicproperties.GetBoolPropertyFn(true), ReadNoSQLHistoryTaskFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(true), HistoryNodeDeleteBatchSize: dynamicproperties.GetIntPropertyFn(1000), } params := pt.TestBaseParams{ DefaultTestCluster: s.DefaultTestCluster, VisibilityTestCluster: s.VisibilityTestCluster, ClusterMetadata: clusterMetadata, DynamicConfiguration: dc, } cluster, err := NewCluster(s.T(), s.TestClusterConfig, s.Logger, params) s.Require().NoError(err) s.TestCluster = cluster s.Engine = s.TestCluster.GetFrontendClient() s.AdminClient = s.TestCluster.GetAdminClient() s.DomainName = s.RandomizeStr("integration-test-domain") s.Require().NoError(s.RegisterDomain(s.DomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.domainCacheRefresh() } func (s *WorkflowIDInternalRateLimitIntegrationSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *WorkflowIDInternalRateLimitIntegrationSuite) TearDownSuite() { s.TearDownBaseSuite() } func (s *WorkflowIDInternalRateLimitIntegrationSuite) TestWorkflowIDSpecificInternalRateLimits() { const ( testWorkflowID = "integration-workflow-specific-internal-rate-limit-test" testWorkflowType = "integration-workflow-specific-internal-rate-limit-test-type" testTaskListName = "integration-workflow-specific-internal-rate-limit-test-taskList" testIdentity = "worker1" ) activityName := "test-activity" request := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: s.DomainName, WorkflowID: testWorkflowID, WorkflowType: &types.WorkflowType{Name: testWorkflowType}, TaskList: &types.TaskList{Name: testTaskListName}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: testIdentity, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyTerminateIfRunning.Ptr(), } ctx, cancel := createContext() defer cancel() we, err := s.Engine.StartWorkflowExecution(ctx, request) s.NoError(err) s.Logger.Info("StartWorkflowExecution", tag.WorkflowRunID(we.RunID)) workflowComplete := false activityCount := int32(5) activityCounter := int32(0) dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { if activityCounter < activityCount { activityCounter++ buf := new(bytes.Buffer) s.Nil(binary.Write(buf, binary.LittleEndian, activityCounter)) return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: strconv.Itoa(int(activityCounter)), ActivityType: &types.ActivityType{Name: activityName}, TaskList: &types.TaskList{Name: testTaskListName}, Input: buf.Bytes(), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(5), ScheduleToStartTimeoutSeconds: common.Int32Ptr(5), StartToCloseTimeoutSeconds: common.Int32Ptr(5), HeartbeatTimeoutSeconds: common.Int32Ptr(1), }, }}, nil } s.Logger.Info("Completing Workflow.") workflowComplete = true return []byte(strconv.Itoa(int(activityCounter))), []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: []byte("Done."), }, }}, nil } activityExecutedCount := int32(0) atHandler := func(execution *types.WorkflowExecution, activityType *types.ActivityType, activityID string, input []byte, taskToken []byte) ([]byte, bool, error) { s.Equal(testWorkflowID, execution.WorkflowID) s.Equal(activityName, activityType.Name) activityExecutedCount++ return []byte("Activity Result."), false, nil } poller := &TaskPoller{ Engine: s.Engine, Domain: s.DomainName, TaskList: &types.TaskList{Name: testTaskListName}, Identity: testIdentity, DecisionHandler: dtHandler, ActivityHandler: atHandler, Logger: s.Logger, T: s.T(), } for i := int32(0); i < activityCount; i++ { _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil || err == tasklist.ErrNoTasks) err = poller.PollAndProcessActivityTask(false) s.True(err == nil || err == tasklist.ErrNoTasks) } s.Logger.Info("Waiting for workflow to complete", tag.WorkflowRunID(we.RunID)) s.False(workflowComplete) _, err = poller.PollAndProcessDecisionTask(false, false) s.True(err == nil || err == tasklist.ErrNoTasks) s.True(workflowComplete) historyResponse, err := s.Engine.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, }, }) s.NoError(err) history := historyResponse.History firstEvent := history.Events[0] lastEvent := history.Events[len(history.Events)-1] // First 7 event ids --> 0 (Workflow start), 1-3 ( Decision scheduled,started,completed) , 4-6 (Activity scheduled,started,completed) post which the tasks will be rate limited since RPS is 2 eventBeforeRatelimited := history.Events[7] timeElapsedBeforeRatelimiting := time.Unix(0, common.Int64Default(eventBeforeRatelimited.Timestamp)).Sub(time.Unix(0, common.Int64Default(firstEvent.Timestamp))) s.True(timeElapsedBeforeRatelimiting < 1*time.Second) totalTime := time.Unix(0, common.Int64Default(lastEvent.Timestamp)).Sub(time.Unix(0, common.Int64Default(firstEvent.Timestamp))) s.True(totalTime > 3*time.Second) } ================================================ FILE: host/xdc/elasticsearch_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:build !race && esintegration // +build !race,esintegration // to run locally, make sure kafka and es is running, // then run cmd `go test -v ./host/xdc -run TestESCrossDCTestSuite -tags esintegration` package xdc import ( "encoding/json" "flag" "fmt" "io/ioutil" "os" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "gopkg.in/yaml.v2" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" "github.com/uber/cadence/environment" "github.com/uber/cadence/host" "github.com/uber/cadence/host/esutils" ) const ( numOfRetry = 100 waitTimeInMs = 400 waitForESToSettle = 4 * time.Second // wait es shards for some time ensure data consistent ) type esCrossDCTestSuite struct { // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, // not merely log an error *require.Assertions suite.Suite cluster1 *host.TestCluster cluster2 *host.TestCluster logger log.Logger clusterConfigs []*host.TestClusterConfig esClient esutils.ESClient testSearchAttributeKey string testSearchAttributeVal string } func TestESCrossDCTestSuite(t *testing.T) { flag.Parse() suite.Run(t, new(esCrossDCTestSuite)) } var ( clusterNameES = []string{"active-es", "standby-es"} clusterReplicationConfigES = []*types.ClusterReplicationConfiguration{ { ClusterName: clusterNameES[0], }, { ClusterName: clusterNameES[1], }, } ) func (s *esCrossDCTestSuite) SetupSuite() { s.logger = testlogger.New(s.T()) fileName := "../testdata/xdc_integration_es_clusters.yaml" if host.TestFlags.TestClusterConfigFile != "" { fileName = host.TestFlags.TestClusterConfigFile } s.Require().NoError(environment.SetupEnv()) confContent, err := ioutil.ReadFile(fileName) s.Require().NoError(err) confContent = []byte(os.ExpandEnv(string(confContent))) var clusterConfigs []*host.TestClusterConfig s.Require().NoError(yaml.Unmarshal(confContent, &clusterConfigs)) s.clusterConfigs = clusterConfigs c, err := host.NewCluster(s.T(), clusterConfigs[0], s.logger.WithTags(tag.ClusterName(clusterNameES[0]))) s.Require().NoError(err) s.cluster1 = c c, err = host.NewCluster(s.T(), clusterConfigs[1], s.logger.WithTags(tag.ClusterName(clusterNameES[1]))) s.Require().NoError(err) s.cluster2 = c s.esClient = esutils.CreateESClient(s.Suite, s.clusterConfigs[0].ESConfig.URL.String(), "v6") // TODO Do we also want to run v7 test here? s.esClient.PutIndexTemplate(s.Suite, "../testdata/es_index_v6_template.json", "test-visibility-template") s.esClient.CreateIndex(s.Suite, s.clusterConfigs[0].ESConfig.Indices[constants.VisibilityAppName]) s.esClient.CreateIndex(s.Suite, s.clusterConfigs[1].ESConfig.Indices[constants.VisibilityAppName]) s.testSearchAttributeKey = definition.CustomStringField s.testSearchAttributeVal = "test value" } func (s *esCrossDCTestSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) } func (s *esCrossDCTestSuite) TearDownSuite() { s.cluster1.TearDownCluster() s.cluster2.TearDownCluster() s.esClient.DeleteIndex(s.Suite, s.clusterConfigs[0].ESConfig.Indices[constants.VisibilityAppName]) s.esClient.DeleteIndex(s.Suite, s.clusterConfigs[1].ESConfig.Indices[constants.VisibilityAppName]) } func (s *esCrossDCTestSuite) TestSearchAttributes() { domainName := "test-xdc-search-attr-" + common.GenerateRandomString(5) client1 := s.cluster1.GetFrontendClient() // active regReq := &types.RegisterDomainRequest{ Name: domainName, Clusters: clusterReplicationConfigES, ActiveClusterName: clusterNameES[0], IsGlobalDomain: true, WorkflowExecutionRetentionPeriodInDays: 1, } err := client1.RegisterDomain(createContext(), regReq) s.NoError(err) descReq := &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), } resp, err := client1.DescribeDomain(createContext(), descReq) s.NoError(err) s.NotNil(resp) // Wait for domain cache to pick the change time.Sleep(cacheRefreshInterval) client2 := s.cluster2.GetFrontendClient() // standby resp2, err := client2.DescribeDomain(createContext(), descReq) s.NoError(err) s.NotNil(resp2) s.Equal(resp, resp2) // start a workflow id := "xdc-search-attr-test-" + uuid.New() wt := "xdc-search-attr-test-type" tl := "xdc-search-attr-test-tasklist" identity := "worker1" workflowType := &types.WorkflowType{Name: wt} taskList := &types.TaskList{Name: tl} attrValBytes, _ := json.Marshal(s.testSearchAttributeVal) searchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ s.testSearchAttributeKey: attrValBytes, }, } startReq := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: domainName, WorkflowID: id, WorkflowType: workflowType, TaskList: taskList, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Identity: identity, SearchAttributes: searchAttr, } startTime := time.Now().UnixNano() we, err := client1.StartWorkflowExecution(createContext(), startReq) s.Nil(err) s.NotNil(we.GetRunID()) s.logger.Info("StartWorkflowExecution \n", tag.WorkflowRunID(we.GetRunID())) startFilter := &types.StartTimeFilter{} startFilter.EarliestTime = common.Int64Ptr(startTime) query := fmt.Sprintf(`WorkflowID = "%s" and %s = "%s"`, id, s.testSearchAttributeKey, s.testSearchAttributeVal) listRequest := &types.ListWorkflowExecutionsRequest{ Domain: domainName, PageSize: 5, Query: query, } testListResult := func(client host.FrontendClient) { var openExecution *types.WorkflowExecutionInfo for i := 0; i < numOfRetry; i++ { startFilter.LatestTime = common.Int64Ptr(time.Now().UnixNano()) resp, err := client.ListWorkflowExecutions(createContext(), listRequest) s.Nil(err) if len(resp.GetExecutions()) == 1 { openExecution = resp.GetExecutions()[0] break } time.Sleep(waitTimeInMs * time.Millisecond) } s.NotNil(openExecution) s.Equal(we.GetRunID(), openExecution.GetExecution().GetRunID()) searchValBytes := openExecution.SearchAttributes.GetIndexedFields()[s.testSearchAttributeKey] var searchVal string json.Unmarshal(searchValBytes, &searchVal) s.Equal(s.testSearchAttributeVal, searchVal) } // List workflow in active engine1 := s.cluster1.GetFrontendClient() testListResult(engine1) // List workflow in standby engine2 := s.cluster2.GetFrontendClient() testListResult(engine2) // upsert search attributes dtHandler := func(execution *types.WorkflowExecution, wt *types.WorkflowType, previousStartedEventID, startedEventID int64, history *types.History) ([]byte, []*types.Decision, error) { upsertDecision := &types.Decision{ DecisionType: types.DecisionTypeUpsertWorkflowSearchAttributes.Ptr(), UpsertWorkflowSearchAttributesDecisionAttributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: getUpsertSearchAttributes(), }} return nil, []*types.Decision{upsertDecision}, nil } poller := host.TaskPoller{ Engine: client1, Domain: domainName, TaskList: taskList, Identity: identity, DecisionHandler: dtHandler, Logger: s.logger, T: s.T(), } _, err = poller.PollAndProcessDecisionTask(false, false) s.logger.Info("PollAndProcessDecisionTask", tag.Error(err)) s.Nil(err) time.Sleep(waitForESToSettle) listRequest = &types.ListWorkflowExecutionsRequest{ Domain: domainName, PageSize: int32(2), Query: fmt.Sprintf(`WorkflowType = '%s' and CloseTime = missing`, wt), } testListResult = func(client host.FrontendClient) { verified := false for i := 0; i < numOfRetry; i++ { resp, err := client.ListWorkflowExecutions(createContext(), listRequest) s.Nil(err) if len(resp.GetExecutions()) == 1 { execution := resp.GetExecutions()[0] retrievedSearchAttr := execution.SearchAttributes if retrievedSearchAttr != nil && len(retrievedSearchAttr.GetIndexedFields()) == 2 { fields := retrievedSearchAttr.GetIndexedFields() searchValBytes := fields[s.testSearchAttributeKey] var searchVal string json.Unmarshal(searchValBytes, &searchVal) s.Equal("another string", searchVal) searchValBytes2 := fields[definition.CustomIntField] var searchVal2 int json.Unmarshal(searchValBytes2, &searchVal2) s.Equal(123, searchVal2) verified = true break } } time.Sleep(waitTimeInMs * time.Millisecond) } s.True(verified) } // test upsert result in active testListResult(engine1) // terminate workflow terminateReason := "force terminate to make sure standby process tasks" terminateDetails := []byte("terminate details.") err = client1.TerminateWorkflowExecution(createContext(), &types.TerminateWorkflowExecutionRequest{ Domain: domainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: id, }, Reason: terminateReason, Details: terminateDetails, Identity: identity, }) s.Nil(err) // check terminate done executionTerminated := false getHistoryReq := &types.GetWorkflowExecutionHistoryRequest{ Domain: domainName, Execution: &types.WorkflowExecution{ WorkflowID: id, }, } GetHistoryLoop: for i := 0; i < 10; i++ { historyResponse, err := client1.GetWorkflowExecutionHistory(createContext(), getHistoryReq) s.Nil(err) history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if *lastEvent.EventType != types.EventTypeWorkflowExecutionTerminated { s.logger.Warn("Execution not terminated yet.") time.Sleep(100 * time.Millisecond) continue GetHistoryLoop } terminateEventAttributes := lastEvent.WorkflowExecutionTerminatedEventAttributes s.Equal(terminateReason, terminateEventAttributes.Reason) s.Equal(terminateDetails, terminateEventAttributes.Details) s.Equal(identity, terminateEventAttributes.Identity) executionTerminated = true break GetHistoryLoop } s.True(executionTerminated) // check history replicated to the other cluster var historyResponse *types.GetWorkflowExecutionHistoryResponse eventsReplicated := false GetHistoryLoop2: for i := 0; i < numOfRetry; i++ { historyResponse, err = client2.GetWorkflowExecutionHistory(createContext(), getHistoryReq) if err == nil { history := historyResponse.History lastEvent := history.Events[len(history.Events)-1] if *lastEvent.EventType == types.EventTypeWorkflowExecutionTerminated { terminateEventAttributes := lastEvent.WorkflowExecutionTerminatedEventAttributes s.Equal(terminateReason, terminateEventAttributes.Reason) s.Equal(terminateDetails, terminateEventAttributes.Details) s.Equal(identity, terminateEventAttributes.Identity) eventsReplicated = true break GetHistoryLoop2 } } time.Sleep(waitTimeInMs * time.Millisecond) } s.Nil(err) s.True(eventsReplicated) // test upsert result in standby testListResult(engine2) } func getUpsertSearchAttributes() *types.SearchAttributes { attrValBytes1, _ := json.Marshal("another string") attrValBytes2, _ := json.Marshal(123) upsertSearchAttr := &types.SearchAttributes{ IndexedFields: map[string][]byte{ definition.CustomStringField: attrValBytes1, definition.CustomIntField: attrValBytes2, }, } return upsertSearchAttr } ================================================ FILE: internal/tools/go.mod ================================================ module github.com/uber/cadence/internal/tools go 1.24.0 require ( github.com/daixiang0/gci v0.12.0 github.com/dmarkham/enumer v1.5.8 github.com/gogo/protobuf v1.3.2 github.com/hexdigest/gowrap v1.2.5 github.com/mgechev/revive v1.3.2 github.com/vektra/mockery/v2 v2.53.5 go.uber.org/mock v0.5.0 go.uber.org/nilaway v0.0.0-20251021214447-34f56b8c16b9 go.uber.org/thriftrw v1.29.2 go.uber.org/yarpc v1.70.3 golang.org/x/tools v0.38.0 ) require ( 4d63.com/gochecknoglobals v0.1.0 // indirect cel.dev/expr v0.16.1 // indirect cloud.google.com/go v0.116.0 // indirect cloud.google.com/go/auth v0.13.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect cloud.google.com/go/iam v1.2.2 // indirect cloud.google.com/go/kms v1.20.1 // indirect cloud.google.com/go/longrunning v0.6.2 // indirect cloud.google.com/go/monitoring v1.21.2 // indirect cloud.google.com/go/storage v1.49.0 // indirect code.gitea.io/sdk/gitea v0.14.0 // indirect github.com/AlekSi/pointer v1.2.0 // indirect github.com/Antonboom/errname v0.1.6 // indirect github.com/Antonboom/nilnil v0.1.1 // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect github.com/Azure/azure-sdk-for-go v54.0.0+incompatible // indirect github.com/Azure/azure-storage-blob-go v0.13.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.18 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect github.com/Azure/go-autorest/autorest/azure/auth v0.5.7 // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/BurntSushi/toml v1.2.1 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect github.com/GaijinEntertainment/go-exhaustruct/v2 v2.1.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect github.com/OpenPeeDeeP/depguard v1.1.0 // indirect github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect github.com/apex/log v1.9.0 // indirect github.com/ashanbrown/forbidigo v1.3.0 // indirect github.com/ashanbrown/makezero v1.1.1 // indirect github.com/aws/aws-sdk-go v1.38.35 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bkielbasa/cyclop v1.2.0 // indirect github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect github.com/bombsimon/wsl/v3 v3.3.0 // indirect github.com/breml/bidichk v0.2.3 // indirect github.com/breml/errchkjson v0.3.0 // indirect github.com/butuzov/ireturn v0.1.1 // indirect github.com/caarlos0/ctrlc v1.0.0 // indirect github.com/caarlos0/env/v6 v6.6.2 // indirect github.com/caarlos0/go-shellwords v1.0.12 // indirect github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e // indirect github.com/cavaliergopher/cpio v1.0.1 // indirect github.com/cenkalti/backoff v2.1.1+incompatible // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.9 // indirect github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 // indirect github.com/chigopher/pathlib v0.19.1 // indirect github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.4.3 // indirect github.com/dghubble/go-twitter v0.0.0-20201011215211-4b180d0cc78d // indirect github.com/dghubble/oauth1 v0.7.0 // indirect github.com/dghubble/sling v1.3.0 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/envoyproxy/go-control-plane v0.13.1 // indirect github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect github.com/esimonov/ifshort v1.0.4 // indirect github.com/ettle/strcase v0.1.1 // indirect github.com/fatih/color v1.15.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/firefart/nonamedreturns v1.0.1 // indirect github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fzipp/gocyclo v0.5.1 // indirect github.com/go-critic/go-critic v0.6.3 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect github.com/go-git/go-git/v5 v5.3.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-toolsmith/astcast v1.0.0 // indirect github.com/go-toolsmith/astcopy v1.0.0 // indirect github.com/go-toolsmith/astequal v1.0.1 // indirect github.com/go-toolsmith/astfmt v1.0.0 // indirect github.com/go-toolsmith/astp v1.0.0 // indirect github.com/go-toolsmith/strparse v1.0.0 // indirect github.com/go-toolsmith/typep v1.0.2 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gojuno/minimock/v3 v3.0.10 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a // indirect github.com/golangci/golangci-lint v1.46.2 // indirect github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect github.com/golangci/misspell v0.3.5 // indirect github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2 // indirect github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-github/v35 v35.2.0 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/google/uuid v1.6.0 // indirect github.com/google/wire v0.5.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 // indirect github.com/goreleaser/chglog v0.4.2 // indirect github.com/goreleaser/fileglob v1.3.0 // indirect github.com/goreleaser/goreleaser v0.169.0 // indirect github.com/goreleaser/nfpm/v2 v2.29.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.4.2 // indirect github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect github.com/gostaticanalysis/nilerr v0.1.1 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.6.8 // indirect github.com/hashicorp/go-version v1.4.0 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jessevdk/go-flags v1.5.0 // indirect github.com/jgautheron/goconst v1.5.1 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jinzhu/copier v0.4.0 // indirect github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/julz/importas v0.1.0 // indirect github.com/kevinburke/ssh_config v1.1.0 // indirect github.com/kisielk/errcheck v1.6.0 // indirect github.com/kisielk/gotool v1.0.0 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/kulti/thelper v0.6.2 // indirect github.com/kunwardeep/paralleltest v1.0.3 // indirect github.com/kyoh86/exportloopref v0.1.8 // indirect github.com/ldez/gomoddirectives v0.2.3 // indirect github.com/ldez/tagliatelle v0.3.1 // indirect github.com/leonklingele/grouper v1.1.0 // indirect github.com/lufeee/execinquery v1.2.1 // indirect github.com/maratori/testpackage v1.0.1 // indirect github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-ieproxy v0.0.1 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mbilski/exhaustivestruct v1.2.0 // indirect github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moricho/tparallel v0.2.1 // indirect github.com/nakabonne/nestif v0.3.1 // indirect github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect github.com/nishanths/exhaustive v0.7.11 // indirect github.com/nishanths/predeclared v0.2.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pascaldekloe/name v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polyfloyd/go-errorlint v1.0.0 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.6.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a // indirect github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5 // indirect github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect github.com/rs/zerolog v1.33.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryancurrah/gomodguard v1.2.3 // indirect github.com/ryanrolds/sqlclosecheck v0.3.0 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sanposhiho/wastedassign/v2 v2.0.6 // indirect github.com/securego/gosec/v2 v2.11.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/sivchari/containedctx v1.0.2 // indirect github.com/sivchari/tenv v1.5.0 // indirect github.com/sonatard/noctx v0.0.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/go-diff v0.6.1 // indirect github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/viper v1.20.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.10.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/sylvia7788/contextcheck v1.0.4 // indirect github.com/tdakkota/asciicheck v0.1.1 // indirect github.com/tetafro/godot v1.4.11 // indirect github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 // indirect github.com/tomarrell/wrapcheck/v2 v2.6.1 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/ultraware/funlen v0.0.3 // indirect github.com/ultraware/whitespace v0.0.5 // indirect github.com/uudashr/gocognit v1.0.5 // indirect github.com/xanzy/go-gitlab v0.50.0 // indirect github.com/xanzy/ssh-agent v0.3.1 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.2.0 // indirect gitlab.com/bosi/decorder v0.2.1 // indirect gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.29.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect go.opentelemetry.io/otel/sdk v1.29.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/fx v1.13.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect gocloud.dev v0.23.0 // indirect golang.org/x/crypto v0.43.0 // indirect golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect golang.org/x/mod v0.29.0 // indirect golang.org/x/net v0.46.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.37.0 // indirect golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 // indirect golang.org/x/term v0.36.0 // indirect golang.org/x/text v0.30.0 // indirect golang.org/x/time v0.8.0 // indirect golang.org/x/tools/go/expect v0.1.1-deprecated // indirect golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.215.0 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect google.golang.org/grpc v1.67.3 // indirect google.golang.org/protobuf v1.36.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.3.2 // indirect mvdan.cc/gofumpt v0.3.1 // indirect mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 // indirect ) ================================================ FILE: internal/tools/go.sum ================================================ 4d63.com/gochecknoglobals v0.1.0 h1:zeZSRqj5yCg28tCkIV/z/lWbwvNm5qnKVS15PI8nhD0= 4d63.com/gochecknoglobals v0.1.0/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo= bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= cel.dev/expr v0.16.1 h1:NR0+oFYzR1CqLFhTAqg3ql59G9VfN8fKq1TCHJ6gq1g= cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/firestore v1.5.0/go.mod h1:c4nNYR1qdq7eaZ+jSc5fonrQN2k3M7sWATcYTiakjEo= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA= cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= cloud.google.com/go/kms v1.20.1 h1:og29Wv59uf2FVaZlesaiDAqHFzHaoUyHI3HYp9VUHVg= cloud.google.com/go/kms v1.20.1/go.mod h1:LywpNiVCvzYNJWS9JUcGJSVTNSwPwi0vBAotzDqn2nc= cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk= cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM= cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc= cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU= cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w= cloud.google.com/go/pubsub v1.10.3/go.mod h1:FUcc28GpGxxACoklPsE1sCtbkY4Ix+ro7yvw+h82Jn4= cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.15.0/go.mod h1:mjjQMoxxyGH7Jr8K5qrx6N2O0AHsczI61sMNn03GIZI= cloud.google.com/go/storage v1.49.0 h1:zenOPBOWHCnojRd9aJZAyQXBYqkJkdQS42dxL55CIMw= cloud.google.com/go/storage v1.49.0/go.mod h1:k1eHhhpLvrPjVGfo0mOUPEJ4Y2+a/Hv5PiwehZI9qGU= cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI= cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io= code.gitea.io/sdk/gitea v0.14.0 h1:m4J352I3p9+bmJUfS+g0odeQzBY/5OXP91Gv6D4fnJ0= code.gitea.io/sdk/gitea v0.14.0/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE= github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0= github.com/Antonboom/errname v0.1.6 h1:LzIJZlyLOCSu51o3/t2n9Ck7PcoP9wdbrdaW6J8fX24= github.com/Antonboom/errname v0.1.6/go.mod h1:7lz79JAnuoMNDAWE9MeeIr1/c/VpSUWatBv2FH9NYpI= github.com/Antonboom/nilnil v0.1.1 h1:PHhrh5ANKFWRBh7TdYmyyq2gyT2lotnvFvvFbylF81Q= github.com/Antonboom/nilnil v0.1.1/go.mod h1:L1jBqoWM7AOeTD+tSquifKSesRHs4ZdaxvZR+xdJEaI= github.com/Azure/azure-amqp-common-go/v3 v3.1.0/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0= github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v51.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v54.0.0+incompatible h1:Bq3L9LF0DHCexlT0fccwxgrOMfjHx8LGz+d+L7gGQv4= github.com/Azure/azure-sdk-for-go v54.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-service-bus-go v0.10.11/go.mod h1:AWw9eTTWZVZyvgpPahD1ybz3a8/vT3GsJDS8KYex55U= github.com/Azure/azure-storage-blob-go v0.13.0 h1:lgWHvFh+UYBNVQLFHXkvul2f6yOPA9PIH82RTG2cSwc= github.com/Azure/azure-storage-blob-go v0.13.0/go.mod h1:pA9kNqtjUeQF2zOSu4s//nUdBD+e64lEuc4sVnuOfNs= github.com/Azure/go-amqp v0.13.0/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs= github.com/Azure/go-amqp v0.13.4/go.mod h1:wbpCKA8tR5MLgRyIu+bb+S6ECdIDdYJ0NlpFE9xsBPI= github.com/Azure/go-amqp v0.13.7/go.mod h1:wbpCKA8tR5MLgRyIu+bb+S6ECdIDdYJ0NlpFE9xsBPI= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/azure/auth v0.5.7 h1:8DQB8yl7aLQuP+nuR5e2RO6454OvFlSTXXaNHshc16s= github.com/Azure/go-autorest/autorest/azure/auth v0.5.7/go.mod h1:AkzUsqkrdmNhfP2i54HqINVQopw0CLDnvHpJ88Zz1eI= github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/GaijinEntertainment/go-exhaustruct/v2 v2.1.0 h1:LAPPhJ4KR5Z8aKVZF5S48csJkxL5RMKmE/98fMs1u5M= github.com/GaijinEntertainment/go-exhaustruct/v2 v2.1.0/go.mod h1:LGOGuvEgCfCQsy3JF2tRmpGDpzA53iZfyGEWSPwQ6/4= github.com/GoogleCloudPlatform/cloudsql-proxy v1.22.0/go.mod h1:mAm5O/zik2RFmcpigNjg6nMotDL8ZXJaxKzgGVcSMFA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1 h1:oTX4vsorBZo/Zdum6OKPA4o7544hm6smoRv1QjpTwGo= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1/go.mod h1:0wEl7vrAD8mehJyohS9HZy+WyEOaQO2mJx86Cvh93kM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard v1.1.0 h1:pjK9nLPS1FwQYGGpPxoMYpe7qACHOhAWQMQzV71i49o= github.com/OpenPeeDeeP/depguard v1.1.0/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc= github.com/ProtonMail/go-crypto v0.0.0-20210329181949-3900d675f39b/go.mod h1:HTM9X7e9oLwn7RiqLG0UVwVRJenLs3wN+tQ0NPAfwMQ= github.com/ProtonMail/go-crypto v0.0.0-20210408094314-bf0c5240ed99/go.mod h1:HTM9X7e9oLwn7RiqLG0UVwVRJenLs3wN+tQ0NPAfwMQ= github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c h1:bNpaLLv2Y4kslsdkdCwAYu8Bak1aGVtxwi8Z/wy4Yuo= github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1msqzFFb+G80MFmpjMw61IU+slm+wln4= github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4= github.com/ProtonMail/gopenpgp/v2 v2.1.8/go.mod h1:mjMvRMlOlBhNuaa3z0xOmEgAkba/Mu1Z8uWwYj4/6Ws= github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs= github.com/ProtonMail/gopenpgp/v2 v2.2.2/go.mod h1:ajUlBGvxMH1UBZnaYO3d1FSVzjiC6kK9XlZYGiDCvpM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/ashanbrown/forbidigo v1.3.0 h1:VkYIwb/xxdireGAdJNZoo24O4lmnEWkactplBlWTShc= github.com/ashanbrown/forbidigo v1.3.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.38.35 h1:7AlAO0FC+8nFjxiGKEmq0QLpiA8/XFr6eIxgRTwkdTg= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxjM= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/breml/bidichk v0.2.3 h1:qe6ggxpTfA8E75hdjWPZ581sY3a2lnl0IRxLQFelECI= github.com/breml/bidichk v0.2.3/go.mod h1:8u2C6DnAy0g2cEq+k/A2+tr9O1s+vHGxWn0LTc70T2A= github.com/breml/errchkjson v0.3.0 h1:YdDqhfqMT+I1vIxPSas44P+9Z9HzJwCeAzjB8PxP1xw= github.com/breml/errchkjson v0.3.0/go.mod h1:9Cogkyv9gcT8HREpzi3TiqBxCqDzo8awa92zSDFcofU= github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/caarlos0/ctrlc v1.0.0 h1:2DtF8GSIcajgffDFJzyG15vO+1PuBWOMUdFut7NnXhw= github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= github.com/caarlos0/env/v6 v6.6.2 h1:BypLXDWQTA32rS4UM7pBz+/0BOuvs6C7LSeQAxMwyvI= github.com/caarlos0/env/v6 v6.6.2/go.mod h1:P0BVSgU9zfkxfSpFUs6KsO3uWR4k3Ac0P66ibAGTybM= github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11 h1:IRrDwVlWQr6kS1U8/EtyA1+EHcc4yl8pndcqXWrEamg= github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11/go.mod h1:je2KZ+LxaCNvCoKg32jtOIULcFogJKcL1ZWUaIBjKj0= github.com/caarlos0/go-shellwords v1.0.12 h1:HWrUnu6lGbWfrDcFiHcZiwOLzHWjjrPVehULaTFgPp8= github.com/caarlos0/go-shellwords v1.0.12/go.mod h1:bYeeX1GrTLPl5cAMYEzdm272qdsQAZiaHgeF0KTk1Gw= github.com/caarlos0/testfs v0.4.3/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk= github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8= github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk= github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e h1:V9a67dfYqPLAvzk5hMQOXYJlZ4SLIXgyKIE+ZiHzgGQ= github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.9 h1:mPP4ucLrf/rKZiIG/a9IPXHGlh8p4CzgpyTy6EEutYk= github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 h1:W9o46d2kbNL06lq7UNDPV0zYLzkrde/bjIqO02eoll0= github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo= github.com/chigopher/pathlib v0.19.1 h1:RoLlUJc0CqBGwq239cilyhxPNLXTK+HXoASGyGznx5A= github.com/chigopher/pathlib v0.19.1/go.mod h1:tzC1dZLW8o33UQpWkNkhvPwL5n4yyFRFm/jL1YGWFvY= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/daixiang0/gci v0.12.0 h1:EQTG7FfKPlO4Ste+oN0kvz+gP4XswKx29D4fLrmwbiU= github.com/daixiang0/gci v0.12.0/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingaikin/go-header v0.4.3 h1:tEaZKAlqql6SKCY++utLmkPLd6K8IBM20Ha7UVm+mtU= github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dghubble/go-twitter v0.0.0-20201011215211-4b180d0cc78d h1:sBKr0A8iQ1qAOozedZ8Aox+Jpv+TeP1Qv7dcQyW8V+M= github.com/dghubble/go-twitter v0.0.0-20201011215211-4b180d0cc78d/go.mod h1:xfg4uS5LEzOj8PgZV7SQYRHbG7jPUnelEiaAVJxmhJE= github.com/dghubble/oauth1 v0.7.0 h1:AlpZdbRiJM4XGHIlQ8BuJ/wlpGwFEJNnB4Mc+78tA/w= github.com/dghubble/oauth1 v0.7.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4Tk1ujxk= github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU= github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dmarkham/enumer v1.5.8 h1:fIF11F9l5jyD++YYvxcSH5WgHfeaSGPaN/T4kOQ4qEM= github.com/dmarkham/enumer v1.5.8/go.mod h1:d10o8R3t/gROm2p3BXqTkMt2+HMuxEmWCXzorAruYak= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStBA= github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/firefart/nonamedreturns v1.0.1 h1:fSvcq6ZpK/uBAgJEGMvzErlzyM4NELLqqdTofVjVNag= github.com/firefart/nonamedreturns v1.0.1/go.mod h1:D3dpIBojGGNh5UfElmwPu73SwDCm+VKhHYqwlNOk2uQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= github.com/fzipp/gocyclo v0.5.1 h1:L66amyuYogbxl0j2U+vGqJXusPF2IkduvXLnYD5TFgw= github.com/fzipp/gocyclo v0.5.1/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-critic/go-critic v0.6.3 h1:abibh5XYBTASawfTQ0rA7dVtQT+6KzpGqb/J+DxRDaw= github.com/go-critic/go-critic v0.6.3/go.mod h1:c6b3ZP1MQ7o6lPR7Rv3lEf7pYQUmAcx8ABHgdZCQt/k= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs= github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc= github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8= github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= github.com/go-toolsmith/astequal v1.0.1 h1:JbSszi42Jiqu36Gnf363HWS9MTEAz67vTQLponh3Moc= github.com/go-toolsmith/astequal v1.0.1/go.mod h1:4oGA3EZXTVItV/ipGiOx7NWkY5veFfcsOJVS2YxltLw= github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= github.com/go-toolsmith/pkgload v1.0.2-0.20220101231613-e814995d17c5 h1:eD9POs68PHkwrx7hAB78z1cb6PfGq/jyWn3wJywsH1o= github.com/go-toolsmith/pkgload v1.0.2-0.20220101231613-e814995d17c5/go.mod h1:3NAwwmD4uY/yggRxoEjk/S00MIV3A+H7rrE3i87eYxM= github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYwvlk= github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/gojuno/minimock/v3 v3.0.4/go.mod h1:HqeqnwV8mAABn3pO5hqF+RE7gjA0jsN8cbbSogoGrzI= github.com/gojuno/minimock/v3 v3.0.10 h1:0UbfgdLHaNRPHWF/RFYPkwxV2KI+SE4tR0dDSFMD7+A= github.com/gojuno/minimock/v3 v3.0.10/go.mod h1:CFXcUJYnBe+1QuNzm+WmdPYtvi/+7zQcPcyQGsbcIXg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3-0.20190920234318-1680a479a2cf/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6J5HIP8ZtyMdiDscjMLfRBSPuzVVeo= github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ= github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks= github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= github.com/golangci/golangci-lint v1.46.2 h1:o90t/Xa6dhJbvy8Bz2RpzUXqrkigp19DLStMolTZbyo= github.com/golangci/golangci-lint v1.46.2/go.mod h1:3DkdHnxn9eoTTrpT2gB0TEv8KSziuoqe9FitgQLHvAY= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= github.com/golangci/misspell v0.3.5 h1:pLzmVdl3VxTOncgzHcvLOKirdvcx/TydsClUQXTehjo= github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2 h1:SgM7GDZTxtTTQPU84heOxy34iG5Du7F2jcoZnvp+fXI= github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2/go.mod h1:LK+zW4MpyytAWQRz0M4xnzEk50lSvqDQKfx304apFkY= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v35 v35.2.0 h1:s/soW8jauhjUC3rh8JI0FePuocj0DEI9DNBg/bVplE8= github.com/google/go-github/v35 v35.2.0/go.mod h1:s0515YVTI+IMrDoy9Y4pHt9ShGpzHvHO8rZ7L7acgvs= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-replayers/grpcreplay v1.0.0 h1:B5kVOzJ1hBgnevTgIWhSTatQ3608yu/2NnU0Ta1d0kY= github.com/google/go-replayers/grpcreplay v1.0.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= github.com/google/go-replayers/httpreplay v0.1.2 h1:HCfx+dQzwN9XbGTHF8qJ+67WN8glL9FTWV5rraCJ/jU= github.com/google/go-replayers/httpreplay v0.1.2/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible h1:xmapqc1AyLoB+ddYT6r04bD9lIjlOqGaREovi0SzFaE= github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/rpmpack v0.0.0-20210410105602-e20c988a6f5a/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw= github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 h1:PVRE9d4AQKmbelZ7emNig1+NT27DUmKZn5qXxfio54U= github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/goreleaser/chglog v0.1.2/go.mod h1:tTZsFuSZK4epDXfjMkxzcGbrIOXprf0JFp47BjIr3B8= github.com/goreleaser/chglog v0.4.2 h1:afmbT1d7lX/q+GF8wv3a1Dofs2j/Y9YkiCpGemWR6mI= github.com/goreleaser/chglog v0.4.2/go.mod h1:u/F03un4hMCQrp65qSWCkkC6T+G7YLKZ+AM2mITE47s= github.com/goreleaser/fileglob v1.2.0/go.mod h1:rFyb2pXaK3YdnYnSjn6lifw0h2Q6s8OfOsx6I6bXkKE= github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I= github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU= github.com/goreleaser/goreleaser v0.169.0 h1:QaTuoCK39LtVpRmocY6THXvnJCxdFHgyWJbqsHnS7K4= github.com/goreleaser/goreleaser v0.169.0/go.mod h1:cJIZtW13/jxPVpVIhLgNa/fXu8Zwn9RMuIoDoUIqwbY= github.com/goreleaser/nfpm/v2 v2.5.1/go.mod h1:Bq9OBKhvhTmdPh6lHUbVBKa3JCw61OgIFEau+vs2CO0= github.com/goreleaser/nfpm/v2 v2.29.0 h1:QW7MD5Od8ePAWqvC+kGQiF8OH5JkSKV+HcblcT0NX6A= github.com/goreleaser/nfpm/v2 v2.29.0/go.mod h1:+O8Rgz7geEXG1ym2Yl8CGPg5nP2LRuCgkBK6CQF+Q3c= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnqWxw9wCauCMKp+IBI= github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q= github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= github.com/gostaticanalysis/forcetypeassert v0.1.0 h1:6eUflI3DiGusXGK6X7cCcIgVCpZ2CiZ1Q7jl6ZxNV70= github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk= github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY= github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs= github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4= github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hexdigest/gowrap v1.1.7/go.mod h1:Z+nBFUDLa01iaNM+/jzoOA1JJ7sm51rnYFauKFUB5fs= github.com/hexdigest/gowrap v1.1.8/go.mod h1:H/JiFmQMp//tedlV8qt2xBdGzmne6bpbaSuiHmygnMw= github.com/hexdigest/gowrap v1.2.5 h1:rg1kPeynZGXqCdVT3M633j/k+7M7QUKqcuP0MchJD5E= github.com/hexdigest/gowrap v1.2.5/go.mod h1:hhd/3h63Nnym1bvjcoaLYsdpyRusHoJorMfkT8DZDh4= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k= github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jgautheron/goconst v1.5.1 h1:HxVbL1MhydKs8R8n/HE5NPvzfaYmQJA3o879lE4+WcM= github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o= github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.6.0 h1:YTDO4pNy7AUN/021p+JGHycQyYNIyMoenM1YDVK6RlY= github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.6.2 h1:K4xulKkwOCnT1CDms6Ex3uG1dvSMUUQe9zxgYQgbRXs= github.com/kulti/thelper v0.6.2/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= github.com/kunwardeep/paralleltest v1.0.3 h1:UdKIkImEAXjR1chUWLn+PNXqWUGs//7tzMeWuP7NhmI= github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77LoN/M= github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA= github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= github.com/ldez/tagliatelle v0.3.1 h1:3BqVVlReVUZwafJUwQ+oxbx2BEX2vUG4Yu/NOfMiKiM= github.com/ldez/tagliatelle v0.3.1/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leonklingele/grouper v1.1.0 h1:tC2y/ygPbMFSBOs3DcyaEMKnnwH7eYKzohOtRrf0SAg= github.com/leonklingele/grouper v1.1.0/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM= github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/maratori/testpackage v1.0.1 h1:QtJ5ZjqapShm0w5DosRjg0PRlSdAdlx+W6cCKoALdbQ= github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 h1:pWxk9e//NbPwfxat7RXkts09K+dEBJWakUWwICVqYbA= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 h1:zpIH83+oKzcpryru8ceC6BxnoG8TBrhgAvRg8obzup0= github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= github.com/mgechev/revive v1.3.2 h1:Wb8NQKBaALBJ3xrrj4zpwJwqwNA6nDpyJSEQWcCka6U= github.com/mgechev/revive v1.3.2/go.mod h1:UCLtc7o5vg5aXCwdUTU1kEBQ1v+YXPAkYDIDXbrs5I0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.1.2/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/moricho/tparallel v0.2.1 h1:95FytivzT6rYzdJLdtfn6m1bfFJylOJK41+lgv/EHf4= github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k= github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nishanths/exhaustive v0.7.11 h1:xV/WU3Vdwh5BUH4N06JNUznb6d5zhRPOnlgCrpNYNKA= github.com/nishanths/exhaustive v0.7.11/go.mod h1:gX+MP7DWMKJmNa1HfMozK+u04hQd3na9i0hyqf3/dOI= github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/name v1.0.0 h1:n7LKFgHixETzxpRv2R77YgPUFo85QHGZKrdaYm7eY5U= github.com/pascaldekloe/name v1.0.0/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.0/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polyfloyd/go-errorlint v1.0.0 h1:pDrQG0lrh68e602Wfp68BlUTRFoHn8PZYAjLgt2LFsM= github.com/polyfloyd/go-errorlint v1.0.0/go.mod h1:KZy4xxPJyy88/gldCe5OdW6OQRtNO3EZE7hXzmnebgA= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a/go.mod h1:lzZQ3Noex5pfAy7mkAeCjcBDteYU85uWWnJ/y6gKU8k= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.8.0/go.mod h1:PC/OgXc+UN7B4ALwvn1yzVZmVwvhXp5JsbBv6wSv6i0= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.9/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a h1:sWFavxtIctGrVs5SYZ5Ml1CvrDAs8Kf5kx2PI3C41dA= github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a/go.mod h1:VMX+OnnSw4LicdiEGtRSD/1X8kW7GuEscjYNr4cOIT4= github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/dsl v0.3.16/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5 h1:PDWGei+Rf2bBiuZIbZmM20J2ftEy9IeUCHA8HbQqed8= github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5/go.mod h1:wSEyW6O61xRV6zb6My3HxrQ5/8ke7NE2OayqCHa3xRM= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf68pVdQ3bCFZMDmnt9yqcMBro1pC7F+IPYMY= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryancurrah/gomodguard v1.2.3 h1:ww2fsjqocGCAFamzvv/b8IsRduuHHeK2MHTcTxZTQX8= github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg= github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw= github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/samuel/go-thrift v0.0.0-20191111193933-5165175b40af/go.mod h1:Vrkh1pnjV9Bl8c3P9zH0/D4NlOHWP5d4/hF4YTULaec= github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3335lYTyxRoA= github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/securego/gosec/v2 v2.11.0 h1:+PDkpzR41OI2jrw1q6AdXZCbsNGNGT7pQjal0H0cArI= github.com/securego/gosec/v2 v2.11.0/go.mod h1:SX8bptShuG8reGC0XS09+a4H2BoWSJi+fscA+Pulbpo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sivchari/containedctx v1.0.2 h1:0hLQKpgC53OVF1VT7CeoFHk9YKstur1XOgfYIc1yrHI= github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw= github.com/sivchari/tenv v1.5.0 h1:wxW0mFpKI6DIb3s6m1jCDYvkWXCskrimXMuGd0K/kSQ= github.com/sivchari/tenv v1.5.0/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sonatard/noctx v0.0.1 h1:VC1Qhl6Oxx9vvWo3UDgrGXYCeKCe3Wbw7qAWL6FrmTY= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0HZqLQ= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk= github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY= github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc= github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/sylvia7788/contextcheck v1.0.4 h1:MsiVqROAdr0efZc/fOCt0c235qm9XJqHtWwM+2h2B04= github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= github.com/tdakkota/asciicheck v0.1.1 h1:PKzG7JUTUmVspQTDqtkX9eSiLGossXTybutHwTXuO0A= github.com/tdakkota/asciicheck v0.1.1/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= github.com/tetafro/godot v1.4.11 h1:BVoBIqAf/2QdbFmSwAWnaIqDivZdOV0ZRwEm6jivLKw= github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 h1:kl4KhGNsJIbDHS9/4U9yQo1UcPQM0kOMJHn29EoH/Ro= github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tomarrell/wrapcheck/v2 v2.6.1 h1:Cf4a/iwuMp9s7kKrh74GTgijRVim0wEpKjgAsT7Wctw= github.com/tomarrell/wrapcheck/v2 v2.6.1/go.mod h1:Eo+Opt6pyMW1b6cNllOcDSSoHO0aTJ+iF6BfCUbHltA= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/tommy-muehle/go-mnd/v2 v2.5.0 h1:iAj0a8e6+dXSL7Liq0aXPox36FiN1dBbjA6lt9fl65s= github.com/tommy-muehle/go-mnd/v2 v2.5.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchtv/twirp v5.8.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/uber-common/bark v1.2.1/go.mod h1:g0ZuPcD7XiExKHynr93Q742G/sbrdVQkghrqLGOoFuY= github.com/uber-go/mapdecode v1.0.0 h1:euUEFM9KnuCa1OBixz1xM+FIXmpixyay5DLymceOVrU= github.com/uber-go/mapdecode v1.0.0/go.mod h1:b5nP15FwXTgpjTjeA9A2uTHXV5UJCl4arwKpP0FP1Hw= github.com/uber-go/tally v3.3.12+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU= github.com/uber-go/tally v3.3.15+incompatible h1:9hLSgNBP28CjIaDmAuRTq9qV+UZY+9PcvAkXO4nNMwg= github.com/uber-go/tally v3.3.15+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU= github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/uber/ringpop-go v0.8.5/go.mod h1:zVI6eGO6L7pG14GkntHsSOfmUAWQ7B4lvmzly4IT4ls= github.com/uber/tchannel-go v1.22.2 h1:NKA5FVESYh6Ij6V+tujK+IFZnBKDyUHdsBY264UYhgk= github.com/uber/tchannel-go v1.22.2/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA= github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= github.com/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI= github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/uudashr/gocognit v1.0.5 h1:rrSex7oHr3/pPLQ0xoWq108XMU8s678FJcQ+aSfOHa4= github.com/uudashr/gocognit v1.0.5/go.mod h1:wgYz0mitoKOTysqxTDMOUXg+Jb5SvtihkfmugIZYpEA= github.com/vektra/mockery/v2 v2.53.5 h1:iktAY68pNiMvLoHxKqlSNSv/1py0QF/17UGrrAMYDI8= github.com/vektra/mockery/v2 v2.53.5/go.mod h1:hIFFb3CvzPdDJJiU7J4zLRblUMv7OuezWsHPmswriwo= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= github.com/xanzy/go-gitlab v0.50.0 h1:t7IoYTrnLSbdEZN7d8X/5zcr+ZM4TZQ2mXa8MqWlAZQ= github.com/xanzy/go-gitlab v0.50.0/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o= github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= gitlab.com/bosi/decorder v0.2.1 h1:ehqZe8hI4w7O4b1vgsDZw1YU1PE7iJXrQWFMsocbQ1w= gitlab.com/bosi/decorder v0.2.1/go.mod h1:6C/nhLSbF6qZbYD8bRmISBwc6vcWdNsiIBkRvjJFrH0= gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8= gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k= go.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.2/go.mod h1:2D7ZejHVMIfog1221iLSYlQRzrtECw3kz4I4VAQm3qI= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ= go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/dig v1.8.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= go.uber.org/dig v1.10.0 h1:yLmDDj9/zuDjv3gz8GQGviXMs9TfysIUMUilCpgzUJY= go.uber.org/dig v1.10.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= go.uber.org/fx v1.10.0/go.mod h1:vLRicqpG/qQEzno4SYU86iCwfT95EZza+Eba0ItuxqY= go.uber.org/fx v1.13.1 h1:CFNTr1oin5OJ0VCZ8EycL3wzF29Jz2g0xe55RFsf2a4= go.uber.org/fx v1.13.1/go.mod h1:bREWhavnedxpJeTq9pQT53BbvwhUv7TcpsOqcH4a+3w= go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/net/metrics v1.3.0 h1:iRLPuVecNYf/wIV+mQaA4IgN8ghifu3q1B4IT6HfwyY= go.uber.org/net/metrics v1.3.0/go.mod h1:pEQrSDGNWT5IVpekWzee5//uHjI4gmgZFkobfw3bv8I= go.uber.org/nilaway v0.0.0-20251021214447-34f56b8c16b9 h1:48u0MW3ki2cfzv6woA/ljDFquyGSx0T99Qwf0l1RuWY= go.uber.org/nilaway v0.0.0-20251021214447-34f56b8c16b9/go.mod h1:pbGMVkhssd5Ee+eoqfgEk9mzoJoKZAhnTbl1QNcYDi0= go.uber.org/thriftrw v1.29.2 h1:pRuFLzbGvTcnYwGSjizWRHlbJUzGhu84sRiL1h1kUd8= go.uber.org/thriftrw v1.29.2/go.mod h1:YcjXveberDd28/Bs34SwHy3yu85x/jB4UA2gIcz/Eo0= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/yarpc v1.70.3 h1:yykHwzRD9/bgDtlOWoVuXbSZoU91Id2dWJO1CDSRHnI= go.uber.org/yarpc v1.70.3/go.mod h1:EH6I6K1HxBbOxZIJfhdDf+H+cvXPHmJyRvpfPqES20U= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= gocloud.dev v0.23.0 h1:u/6F8slWwaZPgGpjpNp0jzH+1P/M2ri7qEP3lFgbqBE= gocloud.dev v0.23.0/go.mod h1:zklCCIIo1N9ELkU2S2E7tW8P8eeMU7oGLeQCXdDwx9Q= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210420210106-798c2154c571/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201109165425-215b40eba54c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210223095934-7937bea0104d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190916130336-e45ffcd953cc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117215004-fe56e6335763/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200831203904-5a2aa26beb65/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA= google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/api v0.215.0 h1:jdYF4qnyczlEz2ReWIsosNLDuzXyvFHJtI5gcr0J7t0= google.golang.org/api v0.215.0/go.mod h1:fta3CVtuJYOEdugLNWm6WodzOS8KdFckABwN4I40hzY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210423144448-3a41ef94ed2b/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210506142907-4a47615972c2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A= google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8= google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.6/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34= honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= mvdan.cc/gofumpt v0.3.1 h1:avhhrOmv0IuvQVK7fvwV91oFSGAk5/6Po8GXTzICeu8= mvdan.cc/gofumpt v0.3.1/go.mod h1:w3ymliuxvzVx8DAutBnVyDqYb1Niy/yCJt/lk821YCE= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 h1:Jh3LAeMt1eGpxomyu3jVkmVZWW2MxZ1qIIV2TZ/nRio= mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5/go.mod h1:b8RRCBm0eeiWR8cfN88xeq2G5SG3VKGO+5UPWi5FSOY= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= ================================================ FILE: internal/tools/go.work ================================================ go 1.24.0 // this go.work file ensures that the root go.work DOES NOT // automatically include dependencies in tools, as otherwise it tries // to find minimum versions across it and all other modules. // // this isn't currently harmful, but it does risk preventing us from // upgrading our tools due to breaking changes in other dependencies // they might bring in. keeping them separate is best. use . ================================================ FILE: internal/tools/go.work.sum ================================================ bazil.org/fuse v0.0.0-20180421153158-65cc252bf669 h1:FNCRpXiquG1aoyqcIWVFmpTSKVcx2bQD38uZZeGtdlw= bitbucket.org/creachadair/shell v0.0.6 h1:reJflDbKqnlnqb4Oo2pQ1/BqmY/eCWcNGHrIUO8qIzc= cloud.google.com/go/accessapproval v1.6.0 h1:x0cEHro/JFPd7eS4BlEWNTMecIj2HdXjOVB5BtvwER0= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= cloud.google.com/go/accessapproval v1.8.2 h1:h4u1MypgeYXTGvnNc1luCBLDN4Kb9Re/gw0Atvoi8HE= cloud.google.com/go/accessapproval v1.8.2/go.mod h1:aEJvHZtpjqstffVwF/2mCXXSQmpskyzvw6zKLvLutZM= cloud.google.com/go/accesscontextmanager v1.7.0 h1:MG60JgnEoawHJrbWw0jGdv6HLNSf6gQvYRiXpuzqgEA= cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= cloud.google.com/go/accesscontextmanager v1.9.2 h1:P0uVixQft8aacbZ7VDZStNZdrftF24Hk8JkA3kfvfqI= cloud.google.com/go/accesscontextmanager v1.9.2/go.mod h1:T0Sw/PQPyzctnkw1pdmGAKb7XBA84BqQzH0fSU7wzJU= cloud.google.com/go/aiplatform v1.37.0 h1:zTw+suCVchgZyO+k847wjzdVjWmrAuehxdvcZvJwfGg= cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= cloud.google.com/go/aiplatform v1.69.0 h1:XvBzK8e6/6ufbi/i129Vmn/gVqFwbNPmRQ89K+MGlgc= cloud.google.com/go/aiplatform v1.69.0/go.mod h1:nUsIqzS3khlnWvpjfJbP+2+h+VrFyYsTm7RNCAViiY8= cloud.google.com/go/analytics v0.19.0 h1:LqAo3tAh2FU9+w/r7vc3hBjU23Kv7GhO/PDIW7kIYgM= cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= cloud.google.com/go/analytics v0.25.2 h1:KgJ5Taxtsnro/co7WIhmAHi5pzYAtvxu8LMqenPAlSo= cloud.google.com/go/analytics v0.25.2/go.mod h1:th0DIunqrhI1ZWVlT3PH2Uw/9ANX8YHfFDEPqf/+7xM= cloud.google.com/go/apigateway v1.5.0 h1:ZI9mVO7x3E9RK/BURm2p1aw9YTBSCQe3klmyP1WxWEg= cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= cloud.google.com/go/apigateway v1.7.2 h1:TRB5q0vvbT5Yx4bNSCWlqLJFJnhc7tDlCR9ccpo1vzg= cloud.google.com/go/apigateway v1.7.2/go.mod h1:+weId+9aR9J6GRwDka7jIUSrKEX60XGcikX7dGU8O7M= cloud.google.com/go/apigeeconnect v1.5.0 h1:sWOmgDyAsi1AZ48XRHcATC0tsi9SkPT7DA/+VCfkaeA= cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= cloud.google.com/go/apigeeconnect v1.7.2 h1:GHg0ddEQUZ08C1qC780P5wwY/jaIW8UtxuRQXLLuRXs= cloud.google.com/go/apigeeconnect v1.7.2/go.mod h1:he/SWi3A63fbyxrxD6jb67ak17QTbWjva1TFbT5w8Kw= cloud.google.com/go/apigeeregistry v0.6.0 h1:E43RdhhCxdlV+I161gUY2rI4eOaMzHTA5kNkvRsFXvc= cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= cloud.google.com/go/apigeeregistry v0.9.2 h1:fC3ZXEk2QsBxUlZZDZpbBGXC/ZQglCBmHDGgY5aNipg= cloud.google.com/go/apigeeregistry v0.9.2/go.mod h1:A5n/DwpG5NaP2fcLYGiFA9QfzpQhPRFNATO1gie8KM8= cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ= cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= cloud.google.com/go/appengine v1.7.1 h1:aBGDKmRIaRRoWJ2tAoN0oVSHoWLhtO9aj/NvUyP4aYs= cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= cloud.google.com/go/appengine v1.9.2 h1:pxAQ//FsyEQsaF9HJduPCOEvj9GV4fvnLARGz1+KDzM= cloud.google.com/go/appengine v1.9.2/go.mod h1:bK4dvmMG6b5Tem2JFZcjvHdxco9g6t1pwd3y/1qr+3s= cloud.google.com/go/area120 v0.7.1 h1:ugckkFh4XkHJMPhTIx0CyvdoBxmOpMe8rNs4Ok8GAag= cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= cloud.google.com/go/area120 v0.9.2 h1:LODm6TjW27/LJ4z4fBNJHRb+tlvy0gSu6Vb8j2lfluY= cloud.google.com/go/area120 v0.9.2/go.mod h1:Ar/KPx51UbrTWGVGgGzFnT7hFYQuk/0VOXkvHdTbQMI= cloud.google.com/go/artifactregistry v1.13.0 h1:o1Q80vqEB6Qp8WLEH3b8FBLNUCrGQ4k5RFj0sn/sgO8= cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= cloud.google.com/go/artifactregistry v1.16.0 h1:BZpz0x8HCG7hwTkD+GlUwPQVFGOo9w84t8kxQwwc0DA= cloud.google.com/go/artifactregistry v1.16.0/go.mod h1:LunXo4u2rFtvJjrGjO0JS+Gs9Eco2xbZU6JVJ4+T8Sk= cloud.google.com/go/asset v1.13.0 h1:YAsssO08BqZ6mncbb6FPlj9h6ACS7bJQUOlzciSfbNk= cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= cloud.google.com/go/asset v1.20.3 h1:/jQBAkZVUbsIczRepDkwaf/K5NcRYvQ6MBiWg5i20fU= cloud.google.com/go/asset v1.20.3/go.mod h1:797WxTDwdnFAJzbjZ5zc+P5iwqXc13yO9DHhmS6wl+o= cloud.google.com/go/assuredworkloads v1.10.0 h1:VLGnVFta+N4WM+ASHbhc14ZOItOabDLH1MSoDv+Xuag= cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= cloud.google.com/go/assuredworkloads v1.12.2 h1:6Y6a4V7CD50qtjvayhu7f5o35UFJP8ade7IbHNfdQEc= cloud.google.com/go/assuredworkloads v1.12.2/go.mod h1:/WeRr/q+6EQYgnoYrqCVgw7boMoDfjXZZev3iJxs2Iw= cloud.google.com/go/automl v1.12.0 h1:50VugllC+U4IGl3tDNcZaWvApHBTrn/TvyHDJ0wM+Uw= cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= cloud.google.com/go/automl v1.14.2 h1:RzR5Nx78iaF2FNAfaaQ/7o2b4VuQ17YbOaeK/DLYSW4= cloud.google.com/go/automl v1.14.2/go.mod h1:mIat+Mf77W30eWQ/vrhjXsXaRh8Qfu4WiymR0hR6Uxk= cloud.google.com/go/baremetalsolution v0.5.0 h1:2AipdYXL0VxMboelTTw8c1UJ7gYu35LZYUbuRv9Q28s= cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= cloud.google.com/go/baremetalsolution v1.3.2 h1:rhawlI+9gy/i1ZQbN/qL6FXHGXusWbfr6UoQdcCpybw= cloud.google.com/go/baremetalsolution v1.3.2/go.mod h1:3+wqVRstRREJV/puwaKAH3Pnn7ByreZG2aFRsavnoBQ= cloud.google.com/go/batch v0.7.0 h1:YbMt0E6BtqeD5FvSv1d56jbVsWEzlGm55lYte+M6Mzs= cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= cloud.google.com/go/batch v1.11.2 h1:OVhgpMMJc+mrFw51R3C06JKC0D6u125RlEBULpg78No= cloud.google.com/go/batch v1.11.2/go.mod h1:ehsVs8Y86Q4K+qhEStxICqQnNqH8cqgpCxx89cmU5h4= cloud.google.com/go/beyondcorp v0.5.0 h1:UkY2BTZkEUAVrgqnSdOJ4p3y9ZRBPEe1LkjgC8Bj/Pc= cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= cloud.google.com/go/beyondcorp v1.1.2 h1:hzKZf9ScvqTWqR8xGKVvD35ScQuxbMySELvJ0OW1usI= cloud.google.com/go/beyondcorp v1.1.2/go.mod h1:q6YWSkEsSZTU2WDt1qtz6P5yfv79wgktGtNbd0FJTLI= cloud.google.com/go/bigquery v1.50.0 h1:RscMV6LbnAmhAzD893Lv9nXXy2WCaJmbxYPWDLbGqNQ= cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= cloud.google.com/go/bigquery v1.64.0 h1:vSSZisNyhr2ioJE1OuYBQrnrpB7pIhRQm4jfjc7E/js= cloud.google.com/go/bigquery v1.64.0/go.mod h1:gy8Ooz6HF7QmA+TRtX8tZmXBKH5mCFBwUApGAb3zI7Y= cloud.google.com/go/bigtable v1.33.0 h1:2BDaWLRAwXO14DJL/u8crbV2oUbMZkIa2eGq8Yao1bk= cloud.google.com/go/bigtable v1.33.0/go.mod h1:HtpnH4g25VT1pejHRtInlFPnN5sjTxbQlsYBjh9t5l0= cloud.google.com/go/billing v1.13.0 h1:JYj28UYF5w6VBAh0gQYlgHJ/OD1oA+JgW29YZQU+UHM= cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= cloud.google.com/go/billing v1.19.2 h1:shcyz1UkrUxbPsqHL6L84ZdtBZ7yocaFFCxMInTsrNo= cloud.google.com/go/billing v1.19.2/go.mod h1:AAtih/X2nka5mug6jTAq8jfh1nPye0OjkHbZEZgU59c= cloud.google.com/go/binaryauthorization v1.5.0 h1:d3pMDBCCNivxt5a4eaV7FwL7cSH0H7RrEnFrTb1QKWs= cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= cloud.google.com/go/binaryauthorization v1.9.2 h1:zZX4cvtYSXc5ogOar1w5KA1BLz3j464RPSaR/HhroJ8= cloud.google.com/go/binaryauthorization v1.9.2/go.mod h1:T4nOcRWi2WX4bjfSRXJkUnpliVIqjP38V88Z10OvEv4= cloud.google.com/go/certificatemanager v1.6.0 h1:5C5UWeSt8Jkgp7OWn2rCkLmYurar/vIWIoSQ2+LaTOc= cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= cloud.google.com/go/certificatemanager v1.9.2 h1:/lO1ejN415kRaiO6DNNCHj0UvQujKP714q3l8gp4lsY= cloud.google.com/go/certificatemanager v1.9.2/go.mod h1:PqW+fNSav5Xz8bvUnJpATIRo1aaABP4mUg/7XIeAn6c= cloud.google.com/go/channel v1.12.0 h1:GpcQY5UJKeOekYgsX3QXbzzAc/kRGtBq43fTmyKe6Uw= cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= cloud.google.com/go/channel v1.19.1 h1:l4XcnfzJ5UGmqZQls0atcpD6ERDps4PLd5hXSyTWFv0= cloud.google.com/go/channel v1.19.1/go.mod h1:ungpP46l6XUeuefbA/XWpWWnAY3897CSRPXUbDstwUo= cloud.google.com/go/cloudbuild v1.9.0 h1:GHQCjV4WlPPVU/j3Rlpc8vNIDwThhd1U9qSY/NPZdko= cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= cloud.google.com/go/cloudbuild v1.19.0 h1:Uo0bL251yvyWsNtO3Og9m5Z4S48cgGf3IUX7xzOcl8s= cloud.google.com/go/cloudbuild v1.19.0/go.mod h1:ZGRqbNMrVGhknIIjwASa6MqoRTOpXIVMSI+Ew5DMPuY= cloud.google.com/go/clouddms v1.5.0 h1:E7v4TpDGUyEm1C/4KIrpVSOCTm0P6vWdHT0I4mostRA= cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= cloud.google.com/go/clouddms v1.8.2 h1:U53ztLRgTkclaxgmBBles+tv+nNcZ5fhbRbw3b2axFw= cloud.google.com/go/clouddms v1.8.2/go.mod h1:pe+JSp12u4mYOkwXpSMouyCCuQHL3a6xvWH2FgOcAt4= cloud.google.com/go/cloudtasks v1.10.0 h1:uK5k6abf4yligFgYFnG0ni8msai/dSv6mDmiBulU0hU= cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= cloud.google.com/go/cloudtasks v1.13.2 h1:x6Qw5JyNbH3reL0arUtlYf77kK6OVjZZ//8JCvUkLro= cloud.google.com/go/cloudtasks v1.13.2/go.mod h1:2pyE4Lhm7xY8GqbZKLnYk7eeuh8L0JwAvXx1ecKxYu8= cloud.google.com/go/compute v1.29.0 h1:Lph6d8oPi38NHkOr6S55Nus/Pbbcp37m/J0ohgKAefs= cloud.google.com/go/compute v1.29.0/go.mod h1:HFlsDurE5DpQZClAGf/cYh+gxssMhBxBovZDYkEn/Og= cloud.google.com/go/contactcenterinsights v1.6.0 h1:jXIpfcH/VYSE1SYcPzO0n1VVb+sAamiLOgCw45JbOQk= cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= cloud.google.com/go/contactcenterinsights v1.15.1 h1:cR/gQMweaG8RIWAlS5Jo1ARi8LUVQJ51t84EUefHeZ8= cloud.google.com/go/contactcenterinsights v1.15.1/go.mod h1:cFGxDVm/OwEVAHbU9UO4xQCtQFn0RZSrSUcF/oJ0Bbs= cloud.google.com/go/container v1.15.0 h1:NKlY/wCDapfVZlbVVaeuu2UZZED5Dy1z4Zx1KhEzm8c= cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= cloud.google.com/go/container v1.42.0 h1:sH9Hj9SoLeP+uKvLXc/04nWyWDiMo4Q85xfb1Nl5sAg= cloud.google.com/go/container v1.42.0/go.mod h1:YL6lDgCUi3frIWNIFU9qrmF7/6K1EYrtspmFTyyqJ+k= cloud.google.com/go/containeranalysis v0.9.0 h1:EQ4FFxNaEAg8PqQCO7bVQfWz9NVwZCUKaM1b3ycfx3U= cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= cloud.google.com/go/containeranalysis v0.13.2 h1:AG2gOcfZJFRiz+3SZCPnxU+gwbzKe++QSX/ej71Lom8= cloud.google.com/go/containeranalysis v0.13.2/go.mod h1:AiKvXJkc3HiqkHzVIt6s5M81wk+q7SNffc6ZlkTDgiE= cloud.google.com/go/datacatalog v1.13.0 h1:4H5IJiyUE0X6ShQBqgFFZvGGcrwGVndTwUSLP4c52gw= cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= cloud.google.com/go/datacatalog v1.23.0 h1:9F2zIbWNNmtrSkPIyGRQNsIugG5VgVVFip6+tXSdWLg= cloud.google.com/go/datacatalog v1.23.0/go.mod h1:9Wamq8TDfL2680Sav7q3zEhBJSPBrDxJU8WtPJ25dBM= cloud.google.com/go/dataflow v0.8.0 h1:eYyD9o/8Nm6EttsKZaEGD84xC17bNgSKCu0ZxwqUbpg= cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= cloud.google.com/go/dataflow v0.10.2 h1:o9P5/zR2mOYJmCnfp9/7RprKFZCwmSu3TvemQSmCaFM= cloud.google.com/go/dataflow v0.10.2/go.mod h1:+HIb4HJxDCZYuCqDGnBHZEglh5I0edi/mLgVbxDf0Ag= cloud.google.com/go/dataform v0.7.0 h1:Dyk+fufup1FR6cbHjFpMuP4SfPiF3LI3JtoIIALoq48= cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= cloud.google.com/go/dataform v0.10.2 h1:t16DoejuOHoxJR88qrpdmFFlCXA9+x5PKrqI9qiDYz0= cloud.google.com/go/dataform v0.10.2/go.mod h1:oZHwMBxG6jGZCVZqqMx+XWXK+dA/ooyYiyeRbUxI15M= cloud.google.com/go/datafusion v1.6.0 h1:sZjRnS3TWkGsu1LjYPFD/fHeMLZNXDK6PDHi2s2s/bk= cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= cloud.google.com/go/datafusion v1.8.2 h1:RPoHvIeXexXwlWhEU6DNgrYCh+C+FR2EXbrnMs2ptpI= cloud.google.com/go/datafusion v1.8.2/go.mod h1:XernijudKtVG/VEvxtLv08COyVuiYPraSxm+8hd4zXA= cloud.google.com/go/datalabeling v0.7.0 h1:ch4qA2yvddGRUrlfwrNJCr79qLqhS9QBwofPHfFlDIk= cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= cloud.google.com/go/datalabeling v0.9.2 h1:UesbU2kYIUWhHUcnFS86ANPbugEq98X9k1whTNcenlc= cloud.google.com/go/datalabeling v0.9.2/go.mod h1:8me7cCxwV/mZgYWtRAd3oRVGFD6UyT7hjMi+4GRyPpg= cloud.google.com/go/dataplex v1.6.0 h1:RvoZ5T7gySwm1CHzAw7yY1QwwqaGswunmqEssPxU/AM= cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= cloud.google.com/go/dataplex v1.19.2 h1:R2xnsZnuWpHi2NmBR0e43GZk2IZcQ1AFEAo1fUI0xsw= cloud.google.com/go/dataplex v1.19.2/go.mod h1:vsxxdF5dgk3hX8Ens9m2/pMNhQZklUhSgqTghZtF1v4= cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= cloud.google.com/go/dataproc/v2 v2.10.0 h1:B0b7eLRXzFTzb4UaxkGGidIF23l/Xpyce28m1Q0cHmU= cloud.google.com/go/dataproc/v2 v2.10.0/go.mod h1:HD16lk4rv2zHFhbm8gGOtrRaFohMDr9f0lAUMLmg1PM= cloud.google.com/go/dataqna v0.7.0 h1:yFzi/YU4YAdjyo7pXkBE2FeHbgz5OQQBVDdbErEHmVQ= cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= cloud.google.com/go/dataqna v0.9.2 h1:hrEcid5jK5fEdlYZ0eS8HJoq+ZCTRWSV7Av42V/G994= cloud.google.com/go/dataqna v0.9.2/go.mod h1:WCJ7pwD0Mi+4pIzFQ+b2Zqy5DcExycNKHuB+VURPPgs= cloud.google.com/go/datastore v1.11.0 h1:iF6I/HaLs3Ado8uRKMvZRvF/ZLkWaWE9i8AiHzbC774= cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= cloud.google.com/go/datastore v1.20.0 h1:NNpXoyEqIJmZFc0ACcwBEaXnmscUpcG4NkKnbCePmiM= cloud.google.com/go/datastore v1.20.0/go.mod h1:uFo3e+aEpRfHgtp5pp0+6M0o147KoPaYNaPAKpfh8Ew= cloud.google.com/go/datastream v1.7.0 h1:BBCBTnWMDwwEzQQmipUXxATa7Cm7CA/gKjKcR2w35T0= cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= cloud.google.com/go/datastream v1.11.2 h1:vgtrwwPfY7JFEDD0VARJK4qyiApnFnPkFRQVuczYb/w= cloud.google.com/go/datastream v1.11.2/go.mod h1:RnFWa5zwR5SzHxeZGJOlQ4HKBQPcjGfD219Qy0qfh2k= cloud.google.com/go/deploy v1.8.0 h1:otshdKEbmsi1ELYeCKNYppwV0UH5xD05drSdBm7ouTk= cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= cloud.google.com/go/deploy v1.25.0 h1:nYLFG2TSsYMJuengVru5P8iWnA5mNA4rKFV5YoOWQ3M= cloud.google.com/go/deploy v1.25.0/go.mod h1:h9uVCWxSDanXUereI5WR+vlZdbPJ6XGy+gcfC25v5rM= cloud.google.com/go/dialogflow v1.32.0 h1:uVlKKzp6G/VtSW0E7IH1Y5o0H48/UOCmqksG2riYCwQ= cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= cloud.google.com/go/dialogflow v1.60.0 h1:H+Q1SUeVU2La0Y0ZGEaKkhEXg3bj9Ceg5YKcMbyNOEc= cloud.google.com/go/dialogflow v1.60.0/go.mod h1:PjsrI+d2FI4BlGThxL0+Rua/g9vLI+2A1KL7s/Vo3pY= cloud.google.com/go/dlp v1.9.0 h1:1JoJqezlgu6NWCroBxr4rOZnwNFILXr4cB9dMaSKO4A= cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= cloud.google.com/go/dlp v1.20.0 h1:Wwz1FoZp3pyrTNkS5fncaAccP/AbqzLQuN5WMi3aVYQ= cloud.google.com/go/dlp v1.20.0/go.mod h1:nrGsA3r8s7wh2Ct9FWu69UjBObiLldNyQda2RCHgdaY= cloud.google.com/go/documentai v1.18.0 h1:KM3Xh0QQyyEdC8Gs2vhZfU+rt6OCPF0dwVwxKgLmWfI= cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= cloud.google.com/go/documentai v1.35.0 h1:DO4ut86a+Xa0gBq7j3FZJPavnKBNoznrg44csnobqIY= cloud.google.com/go/documentai v1.35.0/go.mod h1:ZotiWUlDE8qXSUqkJsGMQqVmfTMYATwJEYqbPXTR9kk= cloud.google.com/go/domains v0.8.0 h1:2ti/o9tlWL4N+wIuWUNH+LbfgpwxPr8J1sv9RHA4bYQ= cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= cloud.google.com/go/domains v0.10.2 h1:ekJCkuzbciXyPKkwPwvI+2Ov1GcGJtMXj/fbgilPFqg= cloud.google.com/go/domains v0.10.2/go.mod h1:oL0Wsda9KdJvvGNsykdalHxQv4Ri0yfdDkIi3bzTUwk= cloud.google.com/go/edgecontainer v1.0.0 h1:O0YVE5v+O0Q/ODXYsQHmHb+sYM8KNjGZw2pjX2Ws41c= cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= cloud.google.com/go/edgecontainer v1.4.0 h1:vpKTEkQPpkl55d6aUU2rzDFvTkMUATvBXfZSlI2KMR0= cloud.google.com/go/edgecontainer v1.4.0/go.mod h1:Hxj5saJT8LMREmAI9tbNTaBpW5loYiWFyisCjDhzu88= cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= cloud.google.com/go/errorreporting v0.3.1 h1:E/gLk+rL7u5JZB9oq72iL1bnhVlLrnfslrgcptjJEUE= cloud.google.com/go/errorreporting v0.3.1/go.mod h1:6xVQXU1UuntfAf+bVkFk6nld41+CPyF2NSPCyXE3Ztk= cloud.google.com/go/essentialcontacts v1.5.0 h1:gIzEhCoOT7bi+6QZqZIzX1Erj4SswMPIteNvYVlu+pM= cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= cloud.google.com/go/essentialcontacts v1.7.2 h1:a/reGTn7WblM5DgieiLbX6CswHgTneWrA4ZNS5E+1Bg= cloud.google.com/go/essentialcontacts v1.7.2/go.mod h1:NoCBlOIVteJFJU+HG9dIG/Cc9kt1K9ys9mbOaGPUmPc= cloud.google.com/go/eventarc v1.11.0 h1:fsJmNeqvqtk74FsaVDU6cH79lyZNCYP8Rrv7EhaB/PU= cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= cloud.google.com/go/eventarc v1.15.0 h1:IVU2EOR8P2f6N8eneuwspN122LR87v9G54B+7ihd1TY= cloud.google.com/go/eventarc v1.15.0/go.mod h1:PAd/pPIZdJtJQFJI1yDEUms1mqohdNuM1BFEVHHlVFg= cloud.google.com/go/filestore v1.6.0 h1:ckTEXN5towyTMu4q0uQ1Mde/JwTHur0gXs8oaIZnKfw= cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= cloud.google.com/go/filestore v1.9.2 h1:DYwMNAcF5bELHHMxRdkIWWZ3XicKp+ZpEBy+c6Gt4uY= cloud.google.com/go/filestore v1.9.2/go.mod h1:I9pM7Hoetq9a7djC1xtmtOeHSUYocna09ZP6x+PG1Xw= cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= cloud.google.com/go/firestore v1.17.0 h1:iEd1LBbkDZTFsLw3sTH50eyg4qe8eoG6CjocmEXO9aQ= cloud.google.com/go/firestore v1.17.0/go.mod h1:69uPx1papBsY8ZETooc71fOhoKkD70Q1DwMrtKuOT/Y= cloud.google.com/go/functions v1.13.0 h1:pPDqtsXG2g9HeOQLoquLbmvmb82Y4Ezdo1GXuotFoWg= cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= cloud.google.com/go/functions v1.19.2 h1:Cu2Gj1JBBJv9gi89r8LrZNsJhGwePnhttn4Blqw/EYI= cloud.google.com/go/functions v1.19.2/go.mod h1:SBzWwWuaFDLnUyStDAMEysVN1oA5ECLbP3/PfJ9Uk7Y= cloud.google.com/go/gaming v1.9.0 h1:7vEhFnZmd931Mo7sZ6pJy7uQPDxF7m7v8xtBheG08tc= cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= cloud.google.com/go/gkebackup v0.4.0 h1:za3QZvw6ujR0uyqkhomKKKNoXDyqYGPJies3voUK8DA= cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= cloud.google.com/go/gkebackup v1.6.2 h1:lWaSgjSonOXe41UhwQjts6lhDZdr5e882LNUTtnjZS0= cloud.google.com/go/gkebackup v1.6.2/go.mod h1:WsTSWqKJkGan1pkp5dS30oxb+Eaa6cLvxEUxKTUALwk= cloud.google.com/go/gkeconnect v0.7.0 h1:gXYKciHS/Lgq0GJ5Kc9SzPA35NGc3yqu6SkjonpEr2Q= cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= cloud.google.com/go/gkeconnect v0.12.0 h1:MuA3/aIuncXkXuUDGdbT7OLnIp7xpFhciuHAnQaoQz4= cloud.google.com/go/gkeconnect v0.12.0/go.mod h1:zn37LsFiNZxPN4iO7YbUk8l/E14pAJ7KxpoXoxt7Ly0= cloud.google.com/go/gkehub v0.12.0 h1:TqCSPsEBQ6oZSJgEYZ3XT8x2gUadbvfwI32YB0kuHCs= cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= cloud.google.com/go/gkehub v0.15.2 h1:CR5MPEP/Ogk5IahCq3O2fKS6TJZQi8mrnrysGHCs0g8= cloud.google.com/go/gkehub v0.15.2/go.mod h1:8YziTOpwbM8LM3r9cHaOMy2rNgJHXZCrrmGgcau9zbQ= cloud.google.com/go/gkemulticloud v0.5.0 h1:8I84Q4vl02rJRsFiinBxl7WCozfdLlUVBQuSrqr9Wtk= cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= cloud.google.com/go/gkemulticloud v1.4.1 h1:SvVD2nJTGScEDYygIQ5dI14oFYhgtJx8HazkT3aufEI= cloud.google.com/go/gkemulticloud v1.4.1/go.mod h1:KRvPYcx53bztNwNInrezdfNF+wwUom8Y3FuJBwhvFpQ= cloud.google.com/go/gsuiteaddons v1.5.0 h1:1mvhXqJzV0Vg5Fa95QwckljODJJfDFXV4pn+iL50zzA= cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= cloud.google.com/go/gsuiteaddons v1.7.2 h1:Rma+a2tCB2PV0Rm87Ywr4P96dCwGIm8vw8gF23ZlYoY= cloud.google.com/go/gsuiteaddons v1.7.2/go.mod h1:GD32J2rN/4APilqZw4JKmwV84+jowYYMkEVwQEYuAWc= cloud.google.com/go/iap v1.7.1 h1:PxVHFuMxmSZyfntKXHXhd8bo82WJ+LcATenq7HLdVnU= cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= cloud.google.com/go/iap v1.10.2 h1:rvM+FNIF2wIbwUU8299FhhVGak2f7oOvbW8J/I5oflE= cloud.google.com/go/iap v1.10.2/go.mod h1:cClgtI09VIfazEK6VMJr6bX8KQfuQ/D3xqX+d0wrUlI= cloud.google.com/go/ids v1.3.0 h1:fodnCDtOXuMmS8LTC2y3h8t24U8F3eKWfhi+3LY6Qf0= cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= cloud.google.com/go/ids v1.5.2 h1:EDYZQraE+Eq6BewUQxVRY8b3VUUo/MnjMfzSh1NGjx8= cloud.google.com/go/ids v1.5.2/go.mod h1:P+ccDD96joXlomfonEdCnyrHvE68uLonc7sJBPVM5T0= cloud.google.com/go/iot v1.6.0 h1:39W5BFSarRNZfVG0eXI5LYux+OVQT8GkgpHCnrZL2vM= cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= cloud.google.com/go/iot v1.8.2 h1:KMN0wujrPV7q0yfs4rt5CUl9Di8sQhJ0uohJn1h6yaI= cloud.google.com/go/iot v1.8.2/go.mod h1:UDwVXvRD44JIcMZr8pzpF3o4iPsmOO6fmbaIYCAg1ww= cloud.google.com/go/language v1.9.0 h1:7Ulo2mDk9huBoBi8zCE3ONOoBrL6UXfAI71CLQ9GEIM= cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= cloud.google.com/go/language v1.14.2 h1:rwrIOwcAgPTYbigOaiMSjKCvBy0xHZJbRc7HB/xMECA= cloud.google.com/go/language v1.14.2/go.mod h1:dviAbkxT9art+2ioL9AM05t+3Ql6UPfMpwq1cDsF+rg= cloud.google.com/go/lifesciences v0.8.0 h1:uWrMjWTsGjLZpCTWEAzYvyXj+7fhiZST45u9AgasasI= cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= cloud.google.com/go/lifesciences v0.10.2 h1:eZSaRgBwbnb/oXwCj1SGE0Kp534DuXpg55iYBWgN024= cloud.google.com/go/lifesciences v0.10.2/go.mod h1:vXDa34nz0T/ibUNoeHnhqI+Pn0OazUTdxemd0OLkyoY= cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= cloud.google.com/go/managedidentities v1.5.0 h1:ZRQ4k21/jAhrHBVKl/AY7SjgzeJwG1iZa+mJ82P+VNg= cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= cloud.google.com/go/managedidentities v1.7.2 h1:oWxuIhIwQC1Vfs1SZi1x389W2TV9uyPsAyZMJgZDND4= cloud.google.com/go/managedidentities v1.7.2/go.mod h1:t0WKYzagOoD3FNtJWSWcU8zpWZz2i9cw2sKa9RiPx5I= cloud.google.com/go/maps v0.7.0 h1:mv9YaczD4oZBZkM5XJl6fXQ984IkJNHPwkc8MUsdkBo= cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= cloud.google.com/go/maps v1.15.0 h1:bmFHlO6BL/smC6GD45r5j0ChjsyyevuJCSARdOL62TI= cloud.google.com/go/maps v1.15.0/go.mod h1:ZFqZS04ucwFiHSNU8TBYDUr3wYhj5iBFJk24Ibvpf3o= cloud.google.com/go/mediatranslation v0.7.0 h1:anPxH+/WWt8Yc3EdoEJhPMBRF7EhIdz426A+tuoA0OU= cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= cloud.google.com/go/mediatranslation v0.9.2 h1:p37R/k9+L33bUMO87gFyv93MwJ+9nuzVhXM5X+6ULwA= cloud.google.com/go/mediatranslation v0.9.2/go.mod h1:1xyRoDYN32THzy+QaU62vIMciX0CFexplju9t30XwUc= cloud.google.com/go/memcache v1.9.0 h1:8/VEmWCpnETCrBwS3z4MhT+tIdKgR1Z4Tr2tvYH32rg= cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= cloud.google.com/go/memcache v1.11.2 h1:GGgC2A9AClJN8VLbMUAPUxj/dNMFwz6Lj01gDxPw7os= cloud.google.com/go/memcache v1.11.2/go.mod h1:jIzHn79b0m5wbkax2SdlW5vNSbpaEk0yWHbeLpMIYZE= cloud.google.com/go/metastore v1.10.0 h1:QCFhZVe2289KDBQ7WxaHV2rAmPrmRAdLC6gbjUd3HPo= cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= cloud.google.com/go/metastore v1.14.2 h1:Euc9kLTKS8T6M1JVqQavwDFHu9UtT1//lGXSKjpO3/0= cloud.google.com/go/metastore v1.14.2/go.mod h1:dk4zOBhZIy3TFOQlI8sbOa+ef0FjAcCHEnd8dO2J+LE= cloud.google.com/go/monitoring v1.13.0 h1:2qsrgXGVoRXpP7otZ14eE1I568zAa92sJSDPyOJvwjM= cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= cloud.google.com/go/networkconnectivity v1.11.0 h1:ZD6b4Pk1jEtp/cx9nx0ZYcL3BKqDa+KixNDZ6Bjs1B8= cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= cloud.google.com/go/networkconnectivity v1.15.2 h1:CuBLrRKhPbzXkFGADopQUpMcdY+SSfoy/3RqsMH2pq4= cloud.google.com/go/networkconnectivity v1.15.2/go.mod h1:N1O01bEk5z9bkkWwXLKcN2T53QN49m/pSpjfUvlHDQY= cloud.google.com/go/networkmanagement v1.6.0 h1:8KWEUNGcpSX9WwZXq7FtciuNGPdPdPN/ruDm769yAEM= cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= cloud.google.com/go/networkmanagement v1.16.0 h1:oT7c2Oo9NT54XjnP4GMNj/HEywrFnBz0u6QLJ2iu8NE= cloud.google.com/go/networkmanagement v1.16.0/go.mod h1:Yc905R9U5jik5YMt76QWdG5WqzPU4ZsdI/mLnVa62/Q= cloud.google.com/go/networksecurity v0.8.0 h1:sOc42Ig1K2LiKlzG71GUVloeSJ0J3mffEBYmvu+P0eo= cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= cloud.google.com/go/networksecurity v0.10.2 h1://zFZM8XZZs+3Y6QKuLqwD5tZ+B/17KUo/rJpGW2tJs= cloud.google.com/go/networksecurity v0.10.2/go.mod h1:puU3Gwchd6Y/VTyMkL50GI2RSRMS3KXhcDBY1HSOcck= cloud.google.com/go/notebooks v1.8.0 h1:Kg2K3K7CbSXYJHZ1aGQpf1xi5x2GUvQWf2sFVuiZh8M= cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= cloud.google.com/go/notebooks v1.12.2 h1:BHIH9kf/02wSCcLAVttEXHSFAgSotgRg2y1YjR7VDCc= cloud.google.com/go/notebooks v1.12.2/go.mod h1:EkLwv8zwr8DUXnvzl944+sRBG+b73HEKzV632YYAGNI= cloud.google.com/go/optimization v1.3.1 h1:dj8O4VOJRB4CUwZXdmwNViH1OtI0WtWL867/lnYH248= cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= cloud.google.com/go/optimization v1.7.2 h1:yM4teRB60qyIm8cV4VRW4wepmHbXCoqv3QKGfKzylEQ= cloud.google.com/go/optimization v1.7.2/go.mod h1:msYgDIh1SGSfq6/KiWJQ/uxMkWq8LekPyn1LAZ7ifNE= cloud.google.com/go/orchestration v1.6.0 h1:Vw+CEXo8M/FZ1rb4EjcLv0gJqqw89b7+g+C/EmniTb8= cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= cloud.google.com/go/orchestration v1.11.1 h1:uZOwdQoAamx8+X0UdMqY/lro3/h/Zhb7SnfArufNVcc= cloud.google.com/go/orchestration v1.11.1/go.mod h1:RFHf4g88Lbx6oKhwFstYiId2avwb6oswGeAQ7Tjjtfw= cloud.google.com/go/orgpolicy v1.10.0 h1:XDriMWug7sd0kYT1QKofRpRHzjad0bK8Q8uA9q+XrU4= cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= cloud.google.com/go/orgpolicy v1.14.1 h1:c1QLoM5v8/aDKgYVCUaC039lD3GPvqAhTVOwsGhIoZQ= cloud.google.com/go/orgpolicy v1.14.1/go.mod h1:1z08Hsu1mkoH839X7C8JmnrqOkp2IZRSxiDw7W/Xpg4= cloud.google.com/go/osconfig v1.11.0 h1:PkSQx4OHit5xz2bNyr11KGcaFccL5oqglFPdTboyqwQ= cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= cloud.google.com/go/osconfig v1.14.2 h1:iBN87PQc+EGh5QqijM3CuxcibvDWmF+9k0eOJT27FO4= cloud.google.com/go/osconfig v1.14.2/go.mod h1:kHtsm0/j8ubyuzGciBsRxFlbWVjc4c7KdrwJw0+g+pQ= cloud.google.com/go/oslogin v1.9.0 h1:whP7vhpmc+ufZa90eVpkfbgzJRK/Xomjz+XCD4aGwWw= cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= cloud.google.com/go/oslogin v1.14.2 h1:6ehIKkALrLe9zUHwEmfXRVuSPm3HiUmEnnDRr7yLIo8= cloud.google.com/go/oslogin v1.14.2/go.mod h1:M7tAefCr6e9LFTrdWRQRrmMeKHbkvc4D9g6tHIjHySA= cloud.google.com/go/phishingprotection v0.7.0 h1:l6tDkT7qAEV49MNEJkEJTB6vOO/onbSOcNtAT09HPuA= cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= cloud.google.com/go/phishingprotection v0.9.2 h1:SaW0IPf/1fflnzomjy7+9EMtReXuxkYpUAf/77m5xL8= cloud.google.com/go/phishingprotection v0.9.2/go.mod h1:mSCiq3tD8fTJAuXq5QBHFKZqMUy8SfWsbUM9NpzJIRQ= cloud.google.com/go/policytroubleshooter v1.6.0 h1:yKAGC4p9O61ttZUswaq9GAn1SZnEzTd0vUYXD7ZBT7Y= cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= cloud.google.com/go/policytroubleshooter v1.11.2 h1:sTIH5AQ8tcgmnqrqlZfYWymjMhPh4ZEt4CvQGgG+kzc= cloud.google.com/go/policytroubleshooter v1.11.2/go.mod h1:1TdeCRv8Qsjcz2qC3wFltg/Mjga4HSpv8Tyr5rzvPsw= cloud.google.com/go/privatecatalog v0.8.0 h1:EPEJ1DpEGXLDnmc7mnCAqFmkwUJbIsaLAiLHVOkkwtc= cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= cloud.google.com/go/privatecatalog v0.10.2 h1:01RPfn8IL2//8UHAmImRraTFYM/3gAEiIxudWLWrp+0= cloud.google.com/go/privatecatalog v0.10.2/go.mod h1:o124dHoxdbO50ImR3T4+x3GRwBSTf4XTn6AatP8MgsQ= cloud.google.com/go/pubsub v1.30.0 h1:vCge8m7aUKBJYOgrZp7EsNDf6QMd2CAlXZqWTn3yq6s= cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= cloud.google.com/go/pubsub v1.45.1 h1:ZC/UzYcrmK12THWn1P72z+Pnp2vu/zCZRXyhAfP1hJY= cloud.google.com/go/pubsub v1.45.1/go.mod h1:3bn7fTmzZFwaUjllitv1WlsNMkqBgGUb3UdMhI54eCc= cloud.google.com/go/pubsublite v1.7.0 h1:cb9fsrtpINtETHiJ3ECeaVzrfIVhcGjhhJEjybHXHao= cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= cloud.google.com/go/pubsublite v1.8.2 h1:jLQozsEVr+c6tOU13vDugtnaBSUy/PD5zK6mhm+uF1Y= cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT6k8x/YgaOPI= cloud.google.com/go/recaptchaenterprise/v2 v2.7.0 h1:6iOCujSNJ0YS7oNymI64hXsjGq60T4FK1zdLugxbzvU= cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= cloud.google.com/go/recaptchaenterprise/v2 v2.19.0 h1:J/J7ZeVOX+sqn0hxzkOBfnQfBAzPZt8KaAuQoarQWQM= cloud.google.com/go/recaptchaenterprise/v2 v2.19.0/go.mod h1:vnbA2SpVPPwKeoFrCQxR+5a0JFRRytwBBG69Zj9pGfk= cloud.google.com/go/recommendationengine v0.7.0 h1:VibRFCwWXrFebEWKHfZAt2kta6pS7Tlimsnms0fjv7k= cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= cloud.google.com/go/recommendationengine v0.9.2 h1:RHVdmoNBdzgRJXI/3SV+GB5TTv/umsVguiaEvmKOh98= cloud.google.com/go/recommendationengine v0.9.2/go.mod h1:DjGfWZJ68ZF5ZuNgoTVXgajFAG0yLt4CJOpC0aMK3yw= cloud.google.com/go/recommender v1.9.0 h1:ZnFRY5R6zOVk2IDS1Jbv5Bw+DExCI5rFumsTnMXiu/A= cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= cloud.google.com/go/recommender v1.13.2 h1:xDFzlFk5Xp5MXnac468eicKM3MUo6UNdxoYuBMOF1mE= cloud.google.com/go/recommender v1.13.2/go.mod h1:XJau4M5Re8F4BM+fzF3fqSjxNJuM66fwF68VCy/ngGE= cloud.google.com/go/redis v1.11.0 h1:JoAd3SkeDt3rLFAAxEvw6wV4t+8y4ZzfZcZmddqphQ8= cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= cloud.google.com/go/redis v1.17.2 h1:QbW264RBH+NSVEQqlDoHfoxcreXK8QRRByTOR2CFbJs= cloud.google.com/go/redis v1.17.2/go.mod h1:h071xkcTMnJgQnU/zRMOVKNj5J6AttG16RDo+VndoNo= cloud.google.com/go/resourcemanager v1.7.0 h1:NRM0p+RJkaQF9Ee9JMnUV9BQ2QBIOq/v8M+Pbv/wmCs= cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= cloud.google.com/go/resourcemanager v1.10.2 h1:LpqZZGM0uJiu1YWM878AA8zZ/qOQ/Ngno60Q8RAraAI= cloud.google.com/go/resourcemanager v1.10.2/go.mod h1:5f+4zTM/ZOTDm6MmPOp6BQAhR0fi8qFPnvVGSoWszcc= cloud.google.com/go/resourcesettings v1.5.0 h1:8Dua37kQt27CCWHm4h/Q1XqCF6ByD7Ouu49xg95qJzI= cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= cloud.google.com/go/resourcesettings v1.8.2 h1:ISRX2HZHNS17F/EuIwzPrQwEyIyUJayGuLrS51yt6Wk= cloud.google.com/go/resourcesettings v1.8.2/go.mod h1:uEgtPiMA+xuBUM4Exu+ZkNpMYP0BLlYeJbyNHfrc+U0= cloud.google.com/go/retail v1.12.0 h1:1Dda2OpFNzIb4qWgFZjYlpP7sxX3aLeypKG6A3H4Yys= cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= cloud.google.com/go/retail v1.19.1 h1:FVzvA+VuEdNoMz2WzWZ5KwfG+CX+jSv+SOspyQPLuRs= cloud.google.com/go/retail v1.19.1/go.mod h1:W48zg0zmt2JMqmJKCuzx0/0XDLtovwzGAeJjmv6VPaE= cloud.google.com/go/run v0.9.0 h1:ydJQo+k+MShYnBfhaRHSZYeD/SQKZzZLAROyfpeD9zw= cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= cloud.google.com/go/run v1.7.0 h1:GJtHWUgi8CK+YPhmTR3tKBAmDmU9RRMYqiGKCmIgFG8= cloud.google.com/go/run v1.7.0/go.mod h1:IvJOg2TBb/5a0Qkc6crn5yTy5nkjcgSWQLhgO8QL8PQ= cloud.google.com/go/scheduler v1.9.0 h1:NpQAHtx3sulByTLe2dMwWmah8PWgeoieFPpJpArwFV0= cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= cloud.google.com/go/scheduler v1.11.2 h1:PfkvJP1qKu9NvFB65Ja/s918bPZWMBcYkg35Ljdw1Oc= cloud.google.com/go/scheduler v1.11.2/go.mod h1:GZSv76T+KTssX2I9WukIYQuQRf7jk1WI+LOcIEHUUHk= cloud.google.com/go/secretmanager v1.10.0 h1:pu03bha7ukxF8otyPKTFdDz+rr9sE3YauS5PliDXK60= cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= cloud.google.com/go/secretmanager v1.14.2 h1:2XscWCfy//l/qF96YE18/oUaNJynAx749Jg3u0CjQr8= cloud.google.com/go/secretmanager v1.14.2/go.mod h1:Q18wAPMM6RXLC/zVpWTlqq2IBSbbm7pKBlM3lCKsmjw= cloud.google.com/go/security v1.13.0 h1:PYvDxopRQBfYAXKAuDpFCKBvDOWPWzp9k/H5nB3ud3o= cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= cloud.google.com/go/security v1.18.2 h1:9Nzp9LGjiDvHqy7X7Q9GrS5lIHN0bI8RvDjkrl4ILO0= cloud.google.com/go/security v1.18.2/go.mod h1:3EwTcYw8554iEtgK8VxAjZaq2unFehcsgFIF9nOvQmU= cloud.google.com/go/securitycenter v1.19.0 h1:AF3c2s3awNTMoBtMX3oCUoOMmGlYxGOeuXSYHNBkf14= cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= cloud.google.com/go/securitycenter v1.35.2 h1:XkkE+IRE5/88drGPIuvETCSN7dAnWoqJahZzDbP5Hog= cloud.google.com/go/securitycenter v1.35.2/go.mod h1:AVM2V9CJvaWGZRHf3eG+LeSTSissbufD27AVBI91C8s= cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= cloud.google.com/go/servicedirectory v1.9.0 h1:SJwk0XX2e26o25ObYUORXx6torSFiYgsGkWSkZgkoSU= cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= cloud.google.com/go/servicedirectory v1.12.2 h1:W/oZmTUzlWbeSTujRbmG9v7HZyHcorj608tkcD3vVYE= cloud.google.com/go/servicedirectory v1.12.2/go.mod h1:F0TJdFjqqotiZRlMXgIOzszaplk4ZAmUV8ovHo08M2U= cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY= cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4= cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= cloud.google.com/go/shell v1.6.0 h1:wT0Uw7ib7+AgZST9eCDygwTJn4+bHMDtZo5fh7kGWDU= cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= cloud.google.com/go/shell v1.8.2 h1:lSfdEng3n7zZHzC40BJ4trEMyme3CGnLLnA09MlLQdQ= cloud.google.com/go/shell v1.8.2/go.mod h1:QQR12T6j/eKvqAQLv6R3ozeoqwJ0euaFSz2qLqG93Bs= cloud.google.com/go/spanner v1.45.0 h1:7VdjZ8zj4sHbDw55atp5dfY6kn1j9sam9DRNpPQhqR4= cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= cloud.google.com/go/spanner v1.73.0 h1:0bab8QDn6MNj9lNK6XyGAVFhMlhMU2waePPa6GZNoi8= cloud.google.com/go/spanner v1.73.0/go.mod h1:mw98ua5ggQXVWwp83yjwggqEmW9t8rjs9Po1ohcUGW4= cloud.google.com/go/speech v1.15.0 h1:JEVoWGNnTF128kNty7T4aG4eqv2z86yiMJPT9Zjp+iw= cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= cloud.google.com/go/speech v1.25.2 h1:rKOXU9LAZTOYHhRNB4gZDekNjJx21TktQpetBa5IzOk= cloud.google.com/go/speech v1.25.2/go.mod h1:KPFirZlLL8SqPaTtG6l+HHIFHPipjbemv4iFg7rTlYs= cloud.google.com/go/storagetransfer v1.8.0 h1:5T+PM+3ECU3EY2y9Brv0Sf3oka8pKmsCfpQ07+91G9o= cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= cloud.google.com/go/storagetransfer v1.11.2 h1:hMcP8ECmxedXjPxr2j3Ca45ro/TKEF+1YYjq2p5LMTI= cloud.google.com/go/storagetransfer v1.11.2/go.mod h1:FcM29aY4EyZ3yVPmW5SxhqUdhjgPBUOFyy4rqiQbias= cloud.google.com/go/talent v1.5.0 h1:nI9sVZPjMKiO2q3Uu0KhTDVov3Xrlpt63fghP9XjyEM= cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= cloud.google.com/go/talent v1.7.2 h1:KONR7KX/EXI3pO2cbSIDOBqhBzvgDS71vaMz8k4qRCg= cloud.google.com/go/talent v1.7.2/go.mod h1:k1sqlDgS9gbc0gMTRuRQpX6C6VB7bGUxSPcoTRWJod8= cloud.google.com/go/texttospeech v1.6.0 h1:H4g1ULStsbVtalbZGktyzXzw6jP26RjVGYx9RaYjBzc= cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= cloud.google.com/go/texttospeech v1.10.0 h1:icRAxYDtq3zO1T0YBT/fe8C/7pXoIqfkY4iYr5zG39I= cloud.google.com/go/texttospeech v1.10.0/go.mod h1:215FpCOyRxxrS7DSb2t7f4ylMz8dXsQg8+Vdup5IhP4= cloud.google.com/go/tpu v1.5.0 h1:/34T6CbSi+kTv5E19Q9zbU/ix8IviInZpzwz3rsFE+A= cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= cloud.google.com/go/tpu v1.7.2 h1:xPBJd7xZgtl3CgrZoaUf7zFPVVj68jmzzGTSzkcsOtQ= cloud.google.com/go/tpu v1.7.2/go.mod h1:0Y7dUo2LIbDUx0yQ/vnLC6e18FK6NrDfAhYS9wZ/2vs= cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no= cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= cloud.google.com/go/translate v1.7.0 h1:GvLP4oQ4uPdChBmBaUSa/SaZxCdyWELtlAaKzpHsXdA= cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= cloud.google.com/go/translate v1.12.2 h1:qECivi8O+jFI/vnvN9elK6CME+WAWy56GIBszF+/rNc= cloud.google.com/go/translate v1.12.2/go.mod h1:jjLVf2SVH2uD+BNM40DYvRRKSsuyKxVvs3YjTW/XSWY= cloud.google.com/go/video v1.15.0 h1:upIbnGI0ZgACm58HPjAeBMleW3sl5cT84AbYQ8PWOgM= cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= cloud.google.com/go/video v1.23.2 h1:CGAPOXTJMoZm9PeHkohBlMTy8lqN6VWCNDjp5VODfy8= cloud.google.com/go/video v1.23.2/go.mod h1:rNOr2pPHWeCbW0QsOwJRIe0ZiuwHpHtumK0xbiYB1Ew= cloud.google.com/go/videointelligence v1.10.0 h1:Uh5BdoET8XXqXX2uXIahGb+wTKbLkGH7s4GXR58RrG8= cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= cloud.google.com/go/videointelligence v1.12.2 h1:ZLElysepw9vfQGAKWfnxdnSnHSKbEn/nU/tmBnCJLfA= cloud.google.com/go/videointelligence v1.12.2/go.mod h1:8xKGlq0lNVyT8JgTkkCUCpyNJnYYEJVWGdqzv+UcwR8= cloud.google.com/go/vision/v2 v2.7.0 h1:8C8RXUJoflCI4yVdqhTy9tRyygSHmp60aP363z23HKg= cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= cloud.google.com/go/vision/v2 v2.9.2 h1:u4pu3gKps88oUe76WwVPeX9dgWVyyYopZ1s05FwsKEk= cloud.google.com/go/vision/v2 v2.9.2/go.mod h1:WuxjVQdAy4j4WZqY5Rr655EdAgi8B707Vdb5T8c90uo= cloud.google.com/go/vmmigration v1.6.0 h1:Azs5WKtfOC8pxvkyrDvt7J0/4DYBch0cVbuFfCCFt5k= cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= cloud.google.com/go/vmmigration v1.8.2 h1:Hpqv3fZ3Ri1OMhTNVJgxxsTou2ZlRzKbnc1dSybTP5Y= cloud.google.com/go/vmmigration v1.8.2/go.mod h1:FBejrsr8ZHmJb949BSOyr3D+/yCp9z9Hk0WtsTiHc1Q= cloud.google.com/go/vmwareengine v0.3.0 h1:b0NBu7S294l0gmtrT0nOJneMYgZapr5x9tVWvgDoVEM= cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= cloud.google.com/go/vmwareengine v1.3.2 h1:LmkojgSLvsRwU1+c0iiY2XoBkXYKzpArElHC9IDWakg= cloud.google.com/go/vmwareengine v1.3.2/go.mod h1:JsheEadzT0nfXOGkdnwtS1FhFAnj4g8qhi4rKeLi/AU= cloud.google.com/go/vpcaccess v1.6.0 h1:FOe6CuiQD3BhHJWt7E8QlbBcaIzVRddupwJlp7eqmn4= cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= cloud.google.com/go/vpcaccess v1.8.2 h1:nvrkqAjS2sorOu4YGCIXWz+Kk+5aAAdnaMD2tnsqeFg= cloud.google.com/go/vpcaccess v1.8.2/go.mod h1:4yvYKNjlNjvk/ffgZ0PuEhpzNJb8HybSM1otG2aDxnY= cloud.google.com/go/webrisk v1.8.0 h1:IY+L2+UwxcVm2zayMAtBhZleecdIFLiC+QJMzgb0kT0= cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= cloud.google.com/go/webrisk v1.10.2 h1:X7zSwS1mX2bxoZ30Ozh6lqiSLezl7RMBWwp5a3Mkxp4= cloud.google.com/go/webrisk v1.10.2/go.mod h1:c0ODT2+CuKCYjaeHO7b0ni4CUrJ95ScP5UFl9061Qq8= cloud.google.com/go/websecurityscanner v1.5.0 h1:AHC1xmaNMOZtNqxI9Rmm87IJEyPaRkOxeI0gpAacXGk= cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= cloud.google.com/go/websecurityscanner v1.7.2 h1:8/4rfJXcyxozbfzI0lDFPcPShRE6bJ4HQwgDAG9J4oQ= cloud.google.com/go/websecurityscanner v1.7.2/go.mod h1:728wF9yz2VCErfBaACA5px2XSYHQgkK812NmHcUsDXA= cloud.google.com/go/workflows v1.10.0 h1:FfGp9w0cYnaKZJhUOMqCOJCYT/WlvYBfTQhFWV3sRKI= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= cloud.google.com/go/workflows v1.13.2 h1:jYIxrDOVCGvTBHIAVhqQ+P8fhE0trm+Hf2hgL1YzmK0= cloud.google.com/go/workflows v1.13.2/go.mod h1:l5Wj2Eibqba4BsADIRzPLaevLmIuYF2W+wfFBkRG3vU= contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9 h1:yxE46rQA0QaqPGqN2UnwXvgCrRqtjR1CsGSWVTRjvv4= contrib.go.opencensus.io/exporter/stackdriver v0.13.5 h1:TNaexHK16gPUoc7uzELKOU7JULqccn1NDuqUxmxSqfo= contrib.go.opencensus.io/integrations/ocsql v0.1.7 h1:G3k7C0/W44zcqkpRSFyjU9f6HZkbwIrL//qqnlqWZ60= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= github.com/Azure/azure-amqp-common-go/v3 v3.1.0 h1:1N4YSkWYWffOpQHromYdOucBSQXhNRKzqtgICy6To8Q= github.com/Azure/azure-service-bus-go v0.10.11 h1:GBg2mcLQA3af+w+ZuYhiAv58OWSVmQYWcds4nro8Xko= github.com/Azure/go-amqp v0.13.7 h1:ukcCtx138ZmOfHbdALuh9yoJhGtOY3+yaKApfzNvhSk= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= github.com/GoogleCloudPlatform/cloudsql-proxy v1.22.0 h1:aTDBS16pX1X4ZR/GFsC2NcOCYJ1hDJwJm3WmKRA905Q= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg= github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7 h1:Fv9bK1Q+ly/ROk4aJsVMeuIwPel4bEnD8EPiI91nZMg= github.com/apex/logs v1.0.0 h1:adOwhOTeXzZTnVuEK13wuJNBFutP0sOfutRS8NY+G6A= github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a h1:2KLQMJ8msqoPHIPDufkxVcoTtcmE5+1sL9950m4R9Pk= github.com/aphistic/sweet v0.2.0 h1:I4z+fAUqvKfvZV/CHi5dV0QuwbmIvYYFDjG0Ss5QpAs= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo= github.com/bazelbuild/rules_go v0.49.0 h1:5vCbuvy8Q11g41lseGJDc5vxhDjJtfxr6nM/IC4VmqM= github.com/bazelbuild/rules_go v0.49.0/go.mod h1:Dhcz716Kqg1RHNWos+N6MlXNkjNP2EwZQ0LukRKJfMs= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE= github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748 h1:bXxS5/Z3/dfc8iFniQfgogNBomo0u+1//9eP+jl8GVo= github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a h1:W8b4lQ4tFF21aspRGoBuCNV6V2fFJBF+pm1J6OY8Lys= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7qg9dX7pc218= github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk= github.com/devigned/tab v0.1.1 h1:3mD6Kb1mUOYeLpJvTVSDwSg5ZsfSxfvxGRTxRsJsITA= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fullstorydev/grpcurl v1.6.0 h1:p8BB6VZF8O7w6MxGr3KJ9E6EVKaswCevSALK6FBtMzA= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo= github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ= github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/gogo/googleapis v1.3.2 h1:kX1es4djPJrsDhY7aZKJy7aZasdcB5oSOEphMjSB53c= github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/certificate-transparency-go v1.1.1 h1:6JHXZhXEvilMcTjR4MGZn5KV0IRkcFl4CJx5iHVhjFE= github.com/google/go-pkcs11 v0.3.0 h1:PVRnTgtArZ3QQqTGtbtjtnIkzl2iY2kt24yqbrf7td8= github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/rpmpack v0.0.0-20210410105602-e20c988a6f5a h1:XC048Fc/OB2rUl/BxruopEl2u/EP6cJNFveVxI1cvdk= github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k= github.com/google/trillian v1.3.11 h1:pPzJPkK06mvXId1LHEAJxIegGgHzzp/FUnycPYfoCMI= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4= github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY= github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= github.com/hashicorp/memberlist v0.3.0 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA= github.com/hashicorp/serf v0.9.7 h1:hkdgbqizGQHuU5IPqYM1JdSMV8nKfpuOnZYXssk9muY= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy770So= github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0= github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/jonboulle/clockwork v0.2.0 h1:J2SLSdy7HgElq8ekSl2Mxh6vrRNFxqbXGenYH2I02Vs= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/letsencrypt/pkcs11key/v4 v4.0.0 h1:qLc/OznH7xMr5ARJgkZCCWk+EomQkiNTOoOF5LAgagc= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4 h1:sIXJOMrYnQZJu7OB7ANSF4MYri2fTEGIsRLz6LwI4xE= github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw= github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw= github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1 h1:29NKShH4TWd3lxCDUhS4Xe16EWMA753dtIxYtwddklU= github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5 h1:0KqC6/sLy7fDpBdybhVkkv4Yz+PmB7c9Dz9z3dLW804= github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI= github.com/muesli/mango v0.1.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4= github.com/muesli/mango-cobra v1.2.0 h1:DQvjzAM0PMZr85Iv9LIMaYISpTOliMEg+uMFtNbYvWg= github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA= github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg= github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0= github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8= github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-proto-validators v0.2.0 h1:F6LFfmgVnfULfaRsQWBbe7F7ocuHCr9+7m+GAeDzNbQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI= github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM= github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a h1:AA9vgIBDjMHPC2McaGPojgV2dcI78ZC0TLNhYCXEKH8= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/pseudomuto/protoc-gen-doc v1.3.2 h1:61vWZuxYa8D7Rn4h+2dgoTNqnluBmJya2MgbqO32z6g= github.com/pseudomuto/protokit v0.2.0 h1:hlnBDcy3YEDXH7kc9gV+NLaN0cDzhDvD1s7Y6FZ8RpM= github.com/quasilyte/go-ruleguard/dsl v0.3.19 h1:5+KTKb2YREUYiqZFEIuifFyBxlcCUPWgNZkWy71XS0Q= github.com/quasilyte/go-ruleguard/dsl v0.3.19/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71 h1:CNooiryw5aisadVfzneSZPswRWvnVW8hF1bS/vo8ReI= github.com/remyoudompheng/go-dbus v0.0.0-20121104212943-b7232d34b1d5 h1:CvqZS4QYHBRvx7AeFdimd16HCbLlYsvQMcKDACpJW/c= github.com/remyoudompheng/go-dbus v0.0.0-20121104212943-b7232d34b1d5/go.mod h1:+u151txRmLpwxBmpYn9z3d1sdJdjRPQpsXuYeY9jNls= github.com/remyoudompheng/go-liblzma v0.0.0-20190506200333-81bf2d431b96 h1:J8J/cgLDRuqXJnwIrRDBvtl+LLsdg7De74znW/BRRq4= github.com/remyoudompheng/go-liblzma v0.0.0-20190506200333-81bf2d431b96/go.mod h1:90HvCY7+oHHUKkbeMCiHt1WuFR2/hPJ9QrljDG+v6ls= github.com/remyoudompheng/go-misc v0.0.0-20190427085024-2d6ac652a50e h1:eTWZyPUnHcuGRDiryS/l2I7FfKjbU3IBx3IjqHPxuKU= github.com/remyoudompheng/go-misc v0.0.0-20190427085024-2d6ac652a50e/go.mod h1:80FQABjoFzZ2M5uEa6FUaJYEmqU2UOKojlFVak1UAwI= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/sagikazarmark/crypt v0.5.0 h1:K6qABjdpr5rjHCw6q4rSdeM+8kNmdIHvEPDvEMkoai4= github.com/sagikazarmark/crypt v0.9.0 h1:fipzMFW34hFUEc4D7fsLQFtE7yElkpgyS2zruedRdZk= github.com/sagikazarmark/crypt v0.9.0/go.mod h1:RnH7sEhxfdnPm1z+XMgSLjWTEIjyK4z2dw6+4vHTMuo= github.com/samuel/go-thrift v0.0.0-20191111193933-5165175b40af h1:EiWVfh8mr40yFZEui2oF0d45KgH48PkB2H0Z0GANvSI= github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b h1:+gCnWOZV8Z/8jehJ2CdqB47Z3S+SREmQcuXkRFLNsiI= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/shirou/gopsutil/v3 v3.22.4 h1:srAQaiX6jX/cYL6q29aE0m8lOskT9CurZ9N61YR3yoI= github.com/shirou/gopsutil/v3 v3.22.4/go.mod h1:D01hZJ4pVHPpCTZ3m3T2+wDF2YAGfd+H4ifUguaQzHM= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e h1:MZM7FHLqUHYI0Y/mQAt3d2aYa0SiNms/hFqC9qJYolM= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q= github.com/smartystreets/gunit v1.0.0 h1:RyPDUFcJbvtXlhJPk7v+wnxZRY2EUokhEYl2EJOPToI= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 h1:7z3LSn867ex6VSaahyKadf4WtSsJIgne6A1WLOAGM8A= github.com/tj/go-buffer v1.1.0 h1:Lo2OsPHlIxXF24zApe15AbK3bJLAOvkkxEA6Ux4c47M= github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2 h1:eGaGNxrtoZf/mBURsnNQKDR7u50Klgcf2eFDQEnc8Bc= github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b h1:m74UWYy+HBs+jMFR9mdZU6shPewugMyH5+GV6LNgW8w= github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966 h1:j6JEOq5QWFker+d7mFQYOhjTZonQ7YkLTHm56dbn+yM= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/twitchtv/twirp v5.8.0+incompatible h1:DTfGS9u/jHbo34cBB+qhzVHRaAq+tRois71j8pvjQ5M= github.com/uber-common/bark v1.2.1 h1:cREJ9b7CpTjwZr0/5wV82fXlitoCIEHHnt9WkQ4lIk0= github.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM= github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= github.com/uber/ringpop-go v0.8.5 h1:aBa/SHmmFRcAXA63k7uBheoTL8tCmH7L+OgktB1AF/o= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTcjMJzCM= github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8 h1:EVObHAr8DqpoJCVv6KYTle8FEImKhtkfcZetNqxDoJQ= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c h1:/RwRVN9EdXAVtdHxP7Ndn/tfmM9/goiwU0QTnLBgS4w= go.etcd.io/etcd/api/v3 v3.5.2 h1:tXok5yLlKyuQ/SXSjtqHc4uzNaMqZi2XsoSPr/LlJXI= go.etcd.io/etcd/api/v3 v3.5.6 h1:Cy2qx3npLcYqTKqGJzMypnMv2tiRyifZJ17BlWIWA7A= go.etcd.io/etcd/api/v3 v3.5.6/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= go.etcd.io/etcd/client/pkg/v3 v3.5.2 h1:4hzqQ6hIb3blLyQ8usCU4h3NghkqcsohEQ3o3VetYxE= go.etcd.io/etcd/client/pkg/v3 v3.5.6 h1:TXQWYceBKqLp4sa87rcPs11SXxUA/mHwH975v+BDvLU= go.etcd.io/etcd/client/pkg/v3 v3.5.6/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= go.etcd.io/etcd/client/v2 v2.305.2 h1:ymrVwTkefuqA/rPkSW7/B4ApijbPVefRumkY+stNfS0= go.etcd.io/etcd/client/v2 v2.305.6 h1:fIDR0p4KMjw01MJMfUIDWdQbjo06PD6CeYM5z4EHLi0= go.etcd.io/etcd/client/v2 v2.305.6/go.mod h1:BHha8XJGe8vCIBfWBpbBLVZ4QjOIlfoouvOwydu63E0= go.etcd.io/etcd/client/v3 v3.5.6 h1:coLs69PWCXE9G4FKquzNaSHrRyMCAXwF+IX1tAPVO8E= go.etcd.io/etcd/client/v3 v3.5.6/go.mod h1:f6GRinRMCsFVv9Ht42EyY7nfsVGwrNO0WEoS2pRKzQk= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403 h1:rKyWXYDfrVOpMFBion4Pmx5sJbQreQNXycHvm4KwJSg= go.opentelemetry.io/otel v1.0.1 h1:4XKyXmfqJLOQ7feyV5DB6gsBFZ0ltB8vLtp6pj4JIcc= go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= go.opentelemetry.io/otel/trace v1.0.1 h1:StTeIH6Q3G4r0Fiw34LTokUFESZgIDUr0qIJ7mKmAfw= go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 h1:FR+oGxGfbQu1d+jglI3rCkjAjUnhRSZcUxr+DqlDLNo= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/mobile v0.0.0-20200801112145-973feb4309de h1:OVJ6QQUBAesB8CZijKDSsXX7xYVtUhrkY0gwMfbi4p4= golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto/googleapis/bytestream v0.0.0-20241223144023-3abc09e42ca8 h1:qlXhWiX84AGgaN7LuORWBEQCCTqj3szNbh2am45O3W8= google.golang.org/genproto/googleapis/bytestream v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:bLYPejkLzwgJuAHlIk1gdPOlx9CUYXLZi2rZxL/ursM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= ================================================ FILE: internal/tools/tools.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:build tools // +build tools package tools import ( // forces one import-order pattern _ "github.com/daixiang0/gci" // enumer for generating utility methods for const enums _ "github.com/dmarkham/enumer" // protobuf stuff _ "github.com/gogo/protobuf/protoc-gen-gofast" // gowrap for generating decorators for interface _ "github.com/hexdigest/gowrap" // replaces golint - configurable and much faster _ "github.com/mgechev/revive" // mockery for generating mocks _ "github.com/vektra/mockery/v2" // mockgen for generating mocks _ "go.uber.org/mock/mockgen" // nilaway for nil pointer analysis _ "go.uber.org/nilaway" // thriftrw code gen _ "go.uber.org/thriftrw" _ "go.uber.org/yarpc/encoding/protobuf/protoc-gen-yarpc-go" // yarpc plugin for thriftrw code gen _ "go.uber.org/yarpc/encoding/thrift/thriftrw-plugin-yarpc" // removes unused imports and formats _ "golang.org/x/tools/cmd/goimports" ) ================================================ FILE: proto/buf.yaml ================================================ version: v1beta1 build: roots: - public - internal lint: # Uber style rules: https://docs.buf.build/migration-prototool/#uber1-uber2 use: - DEFAULT ignore_only: FIELD_LOWER_SNAKE_CASE: - uber/cadence/history/v1/service.proto - uber/cadence/matching/v1/service.proto enum_zero_value_suffix: _INVALID service_suffix: API ================================================ FILE: proto/internal/uber/cadence/history/v1/service.proto ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. syntax = "proto3"; package uber.cadence.history.v1; option go_package = "github.com/uber/cadence/.gen/proto/history/v1;historyv1"; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; import "uber/cadence/api/v1/common.proto"; import "uber/cadence/api/v1/history.proto"; import "uber/cadence/api/v1/query.proto"; import "uber/cadence/api/v1/tasklist.proto"; import "uber/cadence/api/v1/workflow.proto"; import "uber/cadence/api/v1/service_workflow.proto"; import "uber/cadence/api/v1/service_worker.proto"; import "uber/cadence/admin/v1/cluster.proto"; import "uber/cadence/admin/v1/history.proto"; import "uber/cadence/admin/v1/queue.proto"; import "uber/cadence/admin/v1/replication.proto"; import "uber/cadence/shared/v1/any.proto"; import "uber/cadence/shared/v1/history.proto"; import "uber/cadence/shared/v1/workflow.proto"; // HistoryAPI provides API to start a new long running workflow instance, as well as query and update the history // of workflow instances already created. service HistoryAPI { // StartWorkflowExecution starts a new long running workflow instance. It will create the instance with // 'WorkflowExecutionStarted' event in history and also schedule the first DecisionTask for the worker to make the // first decision for this instance. It will return 'WorkflowExecutionAlreadyStartedError', if an instance already // exists with same workflowId. rpc StartWorkflowExecution(StartWorkflowExecutionRequest) returns (StartWorkflowExecutionResponse); // SignalWorkflowExecution is used to send a signal event to running workflow execution. This results in // WorkflowExecutionSignaled event recorded in the history and a decision task being created for the execution. rpc SignalWorkflowExecution(SignalWorkflowExecutionRequest) returns (SignalWorkflowExecutionResponse); // SignalWithStartWorkflowExecution is used to ensure sending a signal event to a workflow execution. // If workflow is running, this results in WorkflowExecutionSignaled event recorded in the history // and a decision task being created for the execution. // If workflow is not running or not found, it will first try start workflow with given WorkflowIDReusePolicy, // and record WorkflowExecutionStarted and WorkflowExecutionSignaled event in case of success. // It will return `WorkflowExecutionAlreadyStartedError` if start workflow failed with given policy. rpc SignalWithStartWorkflowExecution(SignalWithStartWorkflowExecutionRequest) returns (SignalWithStartWorkflowExecutionResponse); // ResetWorkflowExecution reset an existing workflow execution by a firstEventID of a existing event batch // in the history and immediately terminating the current execution instance. // After reset, the history will grow from nextFirstEventID. rpc ResetWorkflowExecution(ResetWorkflowExecutionRequest) returns (ResetWorkflowExecutionResponse); // TerminateWorkflowExecution terminates an existing workflow execution by recording WorkflowExecutionTerminated event // in the history and immediately terminating the execution instance. rpc TerminateWorkflowExecution(TerminateWorkflowExecutionRequest) returns (TerminateWorkflowExecutionResponse); // DescribeWorkflowExecution returns information about the specified workflow execution. rpc DescribeWorkflowExecution(DescribeWorkflowExecutionRequest) returns (DescribeWorkflowExecutionResponse); // QueryWorkflow returns query result for a specified workflow execution. rpc QueryWorkflow(QueryWorkflowRequest) returns (QueryWorkflowResponse); // Reset the sticky tasklist related information in mutable state of a given workflow. // Things cleared are: // 1. StickyTaskList // 2. StickyScheduleToStartTimeout // 3. ClientLibraryVersion // 4. ClientFeatureVersion // 5. ClientImpl rpc ResetStickyTaskList(ResetStickyTaskListRequest) returns (ResetStickyTaskListResponse); // Returns the information from mutable state of workflow execution. // It fails with 'EntityNotExistError' if specified workflow execution in unknown to the service. // It returns CurrentBranchChangedError if the workflow version branch has changed. rpc GetMutableState(GetMutableStateRequest) returns (GetMutableStateResponse); // Returns the information from mutable state of workflow execution. // It fails with 'EntityNotExistError' if specified workflow execution in unknown to the service. // It returns CurrentBranchChangedError if the workflow version branch has changed. rpc PollMutableState(PollMutableStateRequest) returns (PollMutableStateResponse); // RecordDecisionTaskStarted is called by the MatchingService before it hands a decision task to the application worker in response to // a PollForDecisionTask call. It records in the history the event that the decision task has started. It will return 'EventAlreadyStartedError', // if the workflow's execution history already includes a record of the event starting. rpc RecordDecisionTaskStarted(RecordDecisionTaskStartedRequest) returns (RecordDecisionTaskStartedResponse); // RespondDecisionTaskCompleted is called by application worker to complete a DecisionTask handed as a result of // 'PollForDecisionTask' API call. Completing a DecisionTask will result in new events for the workflow execution and // potentially new ActivityTask being created for corresponding decisions. It will also create a DecisionTaskCompleted // event in the history for that session. Use the 'taskToken' provided as response of PollForDecisionTask API call // for completing the DecisionTask. rpc RespondDecisionTaskCompleted(RespondDecisionTaskCompletedRequest) returns (RespondDecisionTaskCompletedResponse); // RespondDecisionTaskFailed is called by application worker to indicate failure. This results in // DecisionTaskFailedEvent written to the history and a new DecisionTask created. This API can be used by client to // either clear sticky tasklist or report ny panics during DecisionTask processing. rpc RespondDecisionTaskFailed(RespondDecisionTaskFailedRequest) returns (RespondDecisionTaskFailedResponse); // RecordActivityTaskStarted is called by the MatchingService before it hands a decision task to the application worker in response to // a PollForActivityTask call. It records in the history the event that the decision task has started. It will return 'EventAlreadyStartedError', // if the workflow's execution history already includes a record of the event starting. rpc RecordActivityTaskStarted(RecordActivityTaskStartedRequest) returns (RecordActivityTaskStartedResponse); // RespondActivityTaskCompleted is called by application worker when it is done processing an ActivityTask. It will // result in a new 'ActivityTaskCompleted' event being written to the workflow history and a new DecisionTask // created for the workflow so new decisions could be made. Use the 'taskToken' provided as response of // PollForActivityTask API call for completion. It fails with 'EntityNotExistsError' if the taskToken is not valid // anymore due to activity timeout. rpc RespondActivityTaskCompleted(RespondActivityTaskCompletedRequest) returns (RespondActivityTaskCompletedResponse); // RespondActivityTaskFailed is called by application worker when it is done processing an ActivityTask. It will // result in a new 'ActivityTaskFailed' event being written to the workflow history and a new DecisionTask // created for the workflow instance so new decisions could be made. Use the 'taskToken' provided as response of // PollForActivityTask API call for completion. It fails with 'EntityNotExistsError' if the taskToken is not valid // anymore due to activity timeout. rpc RespondActivityTaskFailed(RespondActivityTaskFailedRequest) returns (RespondActivityTaskFailedResponse); // RespondActivityTaskCanceled is called by application worker when it is successfully canceled an ActivityTask. It will // result in a new 'ActivityTaskCanceled' event being written to the workflow history and a new DecisionTask // created for the workflow instance so new decisions could be made. Use the 'taskToken' provided as response of // PollForActivityTask API call for completion. It fails with 'EntityNotExistsError' if the taskToken is not valid // anymore due to activity timeout. rpc RespondActivityTaskCanceled(RespondActivityTaskCanceledRequest) returns (RespondActivityTaskCanceledResponse); // RecordActivityTaskHeartbeat is called by application worker while it is processing an ActivityTask. If worker fails // to heartbeat within 'heartbeatTimeoutSeconds' interval for the ActivityTask, then it will be marked as timed out and // 'ActivityTaskTimedOut' event will be written to the workflow history. Calling 'RecordActivityTaskHeartbeat' will // fail with 'EntityNotExistsError' in such situations. Use the 'taskToken' provided as response of // PollForActivityTask API call for heart beating. rpc RecordActivityTaskHeartbeat(RecordActivityTaskHeartbeatRequest) returns (RecordActivityTaskHeartbeatResponse); // RequestCancelWorkflowExecution is called by application worker when it wants to request cancellation of a workflow instance. // It will result in a new 'WorkflowExecutionCancelRequested' event being written to the workflow history and a new DecisionTask // created for the workflow instance so new decisions could be made. It fails with 'EntityNotExistsError' if the workflow is not valid // anymore due to completion or doesn't exist. rpc RequestCancelWorkflowExecution(RequestCancelWorkflowExecutionRequest) returns (RequestCancelWorkflowExecutionResponse); // RemoveSignalMutableState is used to remove a signal request ID that was previously recorded. This is currently // used to clean execution info when signal decision finished. rpc RemoveSignalMutableState(RemoveSignalMutableStateRequest) returns (RemoveSignalMutableStateResponse); // ScheduleDecisionTask is used for creating a decision task for already started workflow execution. This is mainly // used by transfer queue processor during the processing of StartChildWorkflowExecution task, where it first starts // child execution without creating the decision task and then calls this API after updating the mutable state of // parent execution. rpc ScheduleDecisionTask(ScheduleDecisionTaskRequest) returns (ScheduleDecisionTaskResponse); // RecordChildExecutionCompleted is used for reporting the completion of child workflow execution to parent. // This is mainly called by transfer queue processor during the processing of DeleteExecution task. rpc RecordChildExecutionCompleted(RecordChildExecutionCompletedRequest) returns (RecordChildExecutionCompletedResponse); rpc ReplicateEventsV2(ReplicateEventsV2Request) returns (ReplicateEventsV2Response); // SyncShardStatus sync the status between shards. rpc SyncShardStatus(SyncShardStatusRequest) returns (SyncShardStatusResponse); // SyncActivity sync the activity status. rpc SyncActivity(SyncActivityRequest) returns (SyncActivityResponse); // DescribeMutableState returns information about the internal states of workflow mutable state. rpc DescribeMutableState(DescribeMutableStateRequest) returns (DescribeMutableStateResponse); // DescribeHistoryHost returns information about the internal states of a history host. rpc DescribeHistoryHost(DescribeHistoryHostRequest) returns (DescribeHistoryHostResponse); // CloseShard close the shard. rpc CloseShard(CloseShardRequest) returns (CloseShardResponse); // RemoveTask remove task based on type, task_id, shard_id. rpc RemoveTask(RemoveTaskRequest) returns (RemoveTaskResponse); // ResetQueue reset processing queue state based on cluster name and type. rpc ResetQueue(ResetQueueRequest) returns (ResetQueueResponse); // DescribeQueue return queue states based on cluster name and type. rpc DescribeQueue(DescribeQueueRequest) returns (DescribeQueueResponse); // GetReplicationMessages return replication messages based on the read level. rpc GetReplicationMessages(GetReplicationMessagesRequest) returns (GetReplicationMessagesResponse); // GetDLQReplicationMessages return replication messages based on DLQ info. rpc GetDLQReplicationMessages(GetDLQReplicationMessagesRequest) returns (GetDLQReplicationMessagesResponse); // ReapplyEvents applies stale events to the current workflow and current run. rpc ReapplyEvents(ReapplyEventsRequest) returns (ReapplyEventsResponse); // RefreshWorkflowTasks refreshes all tasks of a workflow. rpc RefreshWorkflowTasks(RefreshWorkflowTasksRequest) returns (RefreshWorkflowTasksResponse); // CountDLQMessages returns DLQ message count for each shard / source cluster. rpc CountDLQMessages(CountDLQMessagesRequest) returns (CountDLQMessagesResponse); // ReadDLQMessages returns messages from DLQ. rpc ReadDLQMessages(ReadDLQMessagesRequest) returns (ReadDLQMessagesResponse); // PurgeDLQMessages purges messages from DLQ. rpc PurgeDLQMessages(PurgeDLQMessagesRequest) returns (PurgeDLQMessagesResponse); // MergeDLQMessages merges messages from DLQ. rpc MergeDLQMessages(MergeDLQMessagesRequest) returns (MergeDLQMessagesResponse); // NotifyFailoverMarkers sends failover marker to the failover coordinator. rpc NotifyFailoverMarkers(NotifyFailoverMarkersRequest) returns (NotifyFailoverMarkersResponse); // GetCrossClusterTasks return cross cluster tasks based on cluster name. rpc GetCrossClusterTasks(GetCrossClusterTasksRequest) returns (GetCrossClusterTasksResponse); // RespondCrossClusterTasksCompleted responds the result of processing cross cluster tasks. rpc RespondCrossClusterTasksCompleted(RespondCrossClusterTasksCompletedRequest) returns (RespondCrossClusterTasksCompletedResponse); // GetFailoverInfo returns information about on-going failover. rpc GetFailoverInfo(GetFailoverInfoRequest) returns(GetFailoverInfoResponse); // RatelimitUpdate pushes global-ratelimiting data to aggregating hosts, // and returns data describing how to update the caller's ratelimits. // // For more details, see github.com/uber/cadence/common/quotas/global documentation. // // Request and response structures are intentionally loosely defined, to allow plugging // in externally-defined algorithms without changing protocol-level details. rpc RatelimitUpdate(RatelimitUpdateRequest) returns(RatelimitUpdateResponse); } message StartWorkflowExecutionRequest { api.v1.StartWorkflowExecutionRequest request = 1; string domain_id = 2; api.v1.ParentExecutionInfo parent_execution_info = 3; int32 attempt = 4; google.protobuf.Timestamp expiration_time = 5; api.v1.ContinueAsNewInitiator continue_as_new_initiator = 6; api.v1.Failure continued_failure = 7; api.v1.Payload last_completion_result = 8; google.protobuf.Duration first_decision_task_backoff = 9; map partition_config = 10; } message StartWorkflowExecutionResponse { string run_id = 1; } message SignalWorkflowExecutionRequest { api.v1.SignalWorkflowExecutionRequest request = 1; string domain_id = 2; // workflow execution that requests this signal, for making sure // the workflow being signaled is actually a child of the workflow // making the request api.v1.WorkflowExecution external_workflow_execution = 3; bool child_workflow_only = 4; } message SignalWorkflowExecutionResponse { } message SignalWithStartWorkflowExecutionRequest { api.v1.SignalWithStartWorkflowExecutionRequest request = 1; string domain_id = 2; map partition_config = 3; } message SignalWithStartWorkflowExecutionResponse { string run_id = 1; } message ResetWorkflowExecutionRequest { api.v1.ResetWorkflowExecutionRequest request = 1; string domain_id = 2; } message ResetWorkflowExecutionResponse { string run_id = 1; } message TerminateWorkflowExecutionRequest { api.v1.TerminateWorkflowExecutionRequest request = 1; string domain_id = 2; // workflow execution that requests this termination, for making sure // the workflow being terminated is actually a child of the workflow // making the request api.v1.WorkflowExecution external_workflow_execution = 3; bool child_workflow_only = 4; } message TerminateWorkflowExecutionResponse { } message DescribeWorkflowExecutionRequest { api.v1.DescribeWorkflowExecutionRequest request = 1; string domain_id = 2; } message DescribeWorkflowExecutionResponse { api.v1.WorkflowExecutionConfiguration execution_configuration = 1; api.v1.WorkflowExecutionInfo workflow_execution_info = 2; repeated api.v1.PendingActivityInfo pending_activities = 3; repeated api.v1.PendingChildExecutionInfo pending_children = 4; api.v1.PendingDecisionInfo pending_decision = 5; } message QueryWorkflowRequest { api.v1.QueryWorkflowRequest request = 1; string domain_id = 2; } message QueryWorkflowResponse { api.v1.Payload query_result = 1; api.v1.QueryRejected query_rejected = 2; } message ResetStickyTaskListRequest { api.v1.ResetStickyTaskListRequest request = 1; string domain_id = 2; } message ResetStickyTaskListResponse { } message GetMutableStateRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; int64 expected_next_event_id = 3; bytes current_branch_token = 4; admin.v1.VersionHistoryItem version_history_item = 5; } message GetMutableStateResponse { api.v1.WorkflowExecution workflow_execution = 1; api.v1.WorkflowType workflow_type = 2; int64 next_event_id = 3; google.protobuf.Int64Value previous_started_event_id = 4; int64 last_first_event_id = 5; api.v1.TaskList task_list = 6; api.v1.TaskList sticky_task_list = 7; string client_library_version = 8; string client_feature_version = 9; string client_impl = 10; google.protobuf.Duration sticky_task_list_schedule_to_start_timeout = 11; int32 event_store_version = 12; bytes current_branch_token = 13; shared.v1.WorkflowState workflow_state = 14; api.v1.WorkflowExecutionCloseStatus workflow_close_state = 15; shared.v1.VersionHistories version_histories = 16; bool is_sticky_task_list_enabled = 17; int64 history_size = 18; } message PollMutableStateRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; int64 expected_next_event_id = 3; bytes current_branch_token = 4; } message PollMutableStateResponse { api.v1.WorkflowExecution workflow_execution = 1; api.v1.WorkflowType workflow_type = 2; int64 next_event_id = 3; google.protobuf.Int64Value previous_started_event_id = 4; int64 last_first_event_id = 5; api.v1.TaskList task_list = 6; api.v1.TaskList sticky_task_list = 7; string client_library_version = 8; string client_feature_version = 9; string client_impl = 10; google.protobuf.Duration sticky_task_list_schedule_to_start_timeout = 11; bytes current_branch_token = 12; shared.v1.VersionHistories version_histories = 13; shared.v1.WorkflowState workflow_state = 14; api.v1.WorkflowExecutionCloseStatus workflow_close_state = 15; } message RecordDecisionTaskStartedRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; int64 schedule_id = 3; int64 task_id = 4; // Unique id of each poll request. Used to ensure at most once delivery of tasks. string request_id = 5; api.v1.PollForDecisionTaskRequest poll_request = 6; } message RecordDecisionTaskStartedResponse { api.v1.WorkflowType workflow_type = 1; google.protobuf.Int64Value previous_started_event_id = 2; int64 scheduled_event_id = 3; int64 started_event_id = 4; int64 next_event_id = 5; int32 attempt = 6; bool sticky_execution_enabled = 7; shared.v1.TransientDecisionInfo decision_info = 8; api.v1.TaskList workflow_execution_task_list = 9; int32 event_store_version = 10; bytes branch_token = 11; google.protobuf.Timestamp scheduled_time = 12; google.protobuf.Timestamp started_time = 13; map queries = 14; int64 history_size = 15; } message RecordActivityTaskStartedRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; int64 schedule_id = 3; int64 task_id = 4; // Unique id of each poll request. Used to ensure at most once delivery of tasks. string request_id = 5; api.v1.PollForActivityTaskRequest poll_request = 6; } message RecordActivityTaskStartedResponse { api.v1.HistoryEvent scheduled_event = 1; google.protobuf.Timestamp started_time = 2; int32 attempt = 3; google.protobuf.Timestamp scheduled_time_of_this_attempt = 4; api.v1.Payload heartbeat_details = 5; api.v1.WorkflowType workflow_type = 6; string workflow_domain = 7; } message RespondDecisionTaskCompletedRequest { api.v1.RespondDecisionTaskCompletedRequest request = 1; string domain_id = 2; } message RespondDecisionTaskCompletedResponse { RecordDecisionTaskStartedResponse started_response = 1; map activities_to_dispatch_locally = 2; } message RespondDecisionTaskFailedRequest { api.v1.RespondDecisionTaskFailedRequest request = 1; string domain_id = 2; } message RespondDecisionTaskFailedResponse { } message RecordActivityTaskHeartbeatRequest { api.v1.RecordActivityTaskHeartbeatRequest request = 1; string domain_id = 2; } message RecordActivityTaskHeartbeatResponse { bool cancel_requested = 1; } message RespondActivityTaskCompletedRequest { api.v1.RespondActivityTaskCompletedRequest request = 1; string domain_id = 2; } message RespondActivityTaskCompletedResponse { } message RespondActivityTaskFailedRequest { api.v1.RespondActivityTaskFailedRequest request = 1; string domain_id = 2; } message RespondActivityTaskFailedResponse { } message RespondActivityTaskCanceledRequest { api.v1.RespondActivityTaskCanceledRequest request = 1; string domain_id = 2; } message RespondActivityTaskCanceledResponse { } message RemoveSignalMutableStateRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; string request_id = 3; } message RemoveSignalMutableStateResponse { } message RequestCancelWorkflowExecutionRequest { string domain_id = 1; api.v1.RequestCancelWorkflowExecutionRequest cancel_request = 2; // workflow execution that requests this cancellation, for making sure // the workflow being cancelled is actually a child of the workflow // making the request api.v1.ExternalExecutionInfo external_execution_info = 3; bool child_workflow_only = 4; } message RequestCancelWorkflowExecutionResponse { } message ScheduleDecisionTaskRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; bool is_first_decision = 3; } message ScheduleDecisionTaskResponse { } // RecordChildExecutionCompletedRequest is used for reporting the completion of child execution to parent workflow // execution which started it. When a child execution is completed it creates this request and calls the // RecordChildExecutionCompleted API with the workflowExecution of parent. It also sets the completedExecution of the // child as it could potentially be different than the ChildExecutionStartedEvent of parent in the situation when // child creates multiple runs through ContinueAsNew before finally completing. message RecordChildExecutionCompletedRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; int64 initiated_id = 3; api.v1.WorkflowExecution completed_execution = 4; api.v1.HistoryEvent completion_event = 5; int64 started_id = 6; } message RecordChildExecutionCompletedResponse { } message ReplicateEventsV2Request { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; repeated admin.v1.VersionHistoryItem version_history_items = 3; api.v1.DataBlob events = 4; // New run events does not need version history since there is no prior events. api.v1.DataBlob new_run_events = 5; } message ReplicateEventsV2Response { } message SyncShardStatusRequest { string source_cluster = 1; int32 shard_id = 2; google.protobuf.Timestamp time = 3; } message SyncShardStatusResponse { } message SyncActivityRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; int64 version = 3; int64 scheduled_id = 4; google.protobuf.Timestamp scheduled_time = 5; int64 started_id = 6; google.protobuf.Timestamp started_time = 7; google.protobuf.Timestamp last_heartbeat_time = 8; api.v1.Payload details = 9; int32 attempt = 10; api.v1.Failure last_failure = 11; string last_worker_identity = 12; admin.v1.VersionHistory version_history = 13; } message SyncActivityResponse { } message DescribeMutableStateRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; } message DescribeMutableStateResponse { string mutable_state_in_cache = 1; string mutable_state_in_database = 2; } message DescribeHistoryHostRequest { } message DescribeHistoryHostResponse { int32 number_of_shards = 1; repeated int32 shard_ids = 2; admin.v1.DomainCacheInfo domain_cache = 3; string shard_controller_status = 4; string address = 5; } message CloseShardRequest { int32 shard_id = 1; } message CloseShardResponse { } message RemoveTaskRequest { int32 shard_id = 1; admin.v1.TaskType task_type = 2; int64 task_id = 3; google.protobuf.Timestamp visibility_time = 4; string cluster_name = 5; } message RemoveTaskResponse { } message ResetQueueRequest { int32 shard_id = 1; string cluster_name = 2; admin.v1.TaskType task_type = 3; } message ResetQueueResponse { } message DescribeQueueRequest { int32 shard_id = 1; string cluster_name = 2; admin.v1.TaskType task_type = 3; } message DescribeQueueResponse { repeated string processing_queue_states = 1; } message GetReplicationMessagesRequest { repeated admin.v1.ReplicationToken tokens = 1; string cluster_name = 2; } message GetReplicationMessagesResponse { map shard_messages = 1; } message GetDLQReplicationMessagesRequest { repeated admin.v1.ReplicationTaskInfo task_infos = 1; } message GetDLQReplicationMessagesResponse { repeated admin.v1.ReplicationTask replication_tasks = 1; } message ReapplyEventsRequest { string domain = 1; string domain_id = 2; api.v1.WorkflowExecution workflow_execution = 3; api.v1.DataBlob events = 4; } message ReapplyEventsResponse { } message RefreshWorkflowTasksRequest { string domain = 1; string domain_id = 2; api.v1.WorkflowExecution workflow_execution = 3; } message RefreshWorkflowTasksResponse { } message CountDLQMessagesRequest { bool forceFetch = 1; } message CountDLQMessagesResponse { repeated admin.v1.HistoryDLQCountEntry entries = 1; } message ReadDLQMessagesRequest { admin.v1.DLQType type = 1; int32 shard_id = 2; string source_cluster = 3; google.protobuf.Int64Value inclusive_end_message_id = 4; int32 page_size = 5; bytes next_page_token = 6; } message ReadDLQMessagesResponse { admin.v1.DLQType type = 1; repeated admin.v1.ReplicationTask replication_tasks = 2; repeated admin.v1.ReplicationTaskInfo replication_tasks_info = 3; bytes next_page_token = 4; } message PurgeDLQMessagesRequest { admin.v1.DLQType type = 1; int32 shard_id = 2; string source_cluster = 3; google.protobuf.Int64Value inclusive_end_message_id = 4; } message PurgeDLQMessagesResponse { } message MergeDLQMessagesRequest { admin.v1.DLQType type = 1; int32 shard_id = 2; string source_cluster = 3; google.protobuf.Int64Value inclusive_end_message_id = 4; int32 page_size = 5; bytes next_page_token = 6; } message MergeDLQMessagesResponse { bytes next_page_token = 1; } message NotifyFailoverMarkersRequest { repeated admin.v1.FailoverMarkerToken failover_marker_tokens = 1; } message NotifyFailoverMarkersResponse { } message GetCrossClusterTasksRequest { repeated int32 shard_ids = 1; string target_cluster = 2; } message GetCrossClusterTasksResponse { map tasks_by_shard = 1; map failed_cause_by_shard = 2; } message RespondCrossClusterTasksCompletedRequest { int32 shard_id = 1; string target_cluster = 2; repeated admin.v1.CrossClusterTaskResponse task_responses = 3; bool fetchNewTasks = 4; } message RespondCrossClusterTasksCompletedResponse { admin.v1.CrossClusterTaskRequests tasks = 1; } message GetFailoverInfoRequest { string domain_id = 1; } message GetFailoverInfoResponse { int32 completed_shard_count = 1; repeated int32 pending_shards = 2; } message RatelimitUpdateRequest { // impl-specific data. // likely some simple top-level keys and then either: // - map // - list // // this is a single blob rather than a collection to save on // repeated serialization of the type name, and to allow impls // to choose whatever structures are most-convenient for them. shared.v1.Any data = 1; } message RatelimitUpdateResponse { // impl-specific data. // // likely some simple top-level keys and then either: // - map // - list // // this is a single blob rather than a collection to save on // repeated serialization of the type name, and to allow impls // to choose whatever structures are most-convenient for them. shared.v1.Any data = 1; } ================================================ FILE: proto/internal/uber/cadence/indexer/v1/messages.proto ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. syntax = "proto3"; package uber.cadence.indexer.v1; option go_package = "github.com/uber/cadence/.gen/proto/indexer/v1;indexerv1"; import "uber/cadence/api/v1/common.proto"; message Message { MessageType message_type = 1; string domain_id = 2; api.v1.WorkflowExecution workflow_execution = 3; int64 version = 4; map fields = 5; } message Field { oneof data { string string_data = 1; int64 int_data = 2; bool bool_data = 3; bytes binary_data = 4; } } enum MessageType { MESSAGE_TYPE_INVALID = 0; MESSAGE_TYPE_INDEX = 1; MESSAGE_TYPE_DELETE = 2; } ================================================ FILE: proto/internal/uber/cadence/matching/v1/service.proto ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. syntax = "proto3"; package uber.cadence.matching.v1; option go_package = "github.com/uber/cadence/.gen/proto/matching/v1;matchingv1"; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; import "uber/cadence/api/v1/common.proto"; import "uber/cadence/api/v1/query.proto"; import "uber/cadence/api/v1/tasklist.proto"; import "uber/cadence/api/v1/service_worker.proto"; import "uber/cadence/api/v1/service_workflow.proto"; import "uber/cadence/api/v1/history.proto"; import "uber/cadence/shared/v1/history.proto"; import "uber/cadence/shared/v1/tasklist.proto"; // MatchingAPI is exposed to provide support for polling from long running applications. // Such applications are expected to have a worker which regularly polls for DecisionTask and ActivityTask. For each // DecisionTask, application is expected to process the history of events for that session and respond back with next // decisions. For each ActivityTask, application is expected to execute the actual logic for that task and respond back // with completion or failure. service MatchingAPI { // PollForDecisionTask is called by frontend to process DecisionTask from a specific taskList. A // DecisionTask is dispatched to callers for active workflow executions, with pending decisions. rpc PollForDecisionTask(PollForDecisionTaskRequest) returns (PollForDecisionTaskResponse); // PollForActivityTask is called by frontend to process ActivityTask from a specific taskList. ActivityTask // is dispatched to callers whenever a ScheduleTask decision is made for a workflow execution. rpc PollForActivityTask(PollForActivityTaskRequest) returns (PollForActivityTaskResponse); // AddDecisionTask is called by the history service when a decision task is scheduled, so that it can be dispatched // by the MatchingEngine. rpc AddDecisionTask(AddDecisionTaskRequest) returns (AddDecisionTaskResponse); // AddActivityTask is called by the history service when a decision task is scheduled, so that it can be dispatched // by the MatchingEngine. rpc AddActivityTask(AddActivityTaskRequest) returns (AddActivityTaskResponse); // QueryWorkflow is called by frontend to query a workflow. rpc QueryWorkflow(QueryWorkflowRequest) returns (QueryWorkflowResponse); // RespondQueryTaskCompleted is called by frontend to respond query completed. rpc RespondQueryTaskCompleted(RespondQueryTaskCompletedRequest) returns (RespondQueryTaskCompletedResponse); // CancelOutstandingPoll is called by frontend to unblock long polls on matching for zombie pollers. // Our rpc stack does not support context propagation, so when a client connection goes away frontend sees // cancellation of context for that handler, but any corresponding calls (long-poll) to matching service does not // see the cancellation propagated so it can unblock corresponding long-polls on its end. This results is tasks // being dispatched to zombie pollers in this situation. This API is added so everytime frontend makes a long-poll // api call to matching it passes in a pollerID and then calls this API when it detects client connection is closed // to unblock long polls for this poller and prevent tasks being sent to these zombie pollers. rpc CancelOutstandingPoll(CancelOutstandingPollRequest) returns (CancelOutstandingPollResponse); // DescribeTaskList returns information about the target tasklist, right now this API returns the // pollers which polled this tasklist in last few minutes. rpc DescribeTaskList(DescribeTaskListRequest) returns (DescribeTaskListResponse); // ListTaskListPartitions returns a map of partitionKey and hostAddress for a taskList rpc ListTaskListPartitions(ListTaskListPartitionsRequest) returns (ListTaskListPartitionsResponse); // GetTaskListsByDomain returns all tasklist for a given domain rpc GetTaskListsByDomain(GetTaskListsByDomainRequest) returns (GetTaskListsByDomainResponse); // UpdateTaskListPartitionConfig is called to update the partition config of a task list in the database // and notify all partitions of the task list to update the cache of partition config of the task list // his API is used by frontend service to forward update request initiated mostly from CLI tool to update // the partition config of a task list and notify all partitions of the task list to update their cache of task list partition config. rpc UpdateTaskListPartitionConfig(UpdateTaskListPartitionConfigRequest) returns (UpdateTaskListPartitionConfigResponse); // RefreshTaskListPartitionConfig is called to update the cache of partition config of a task list // This API is mainly used by matching service to notify a task list partition to update its cache // of task list partition config. It can also be used by frontend service to forward request initiated // from admin CLI tool to sync the cache of task list partition config if something goes wrong. rpc RefreshTaskListPartitionConfig(RefreshTaskListPartitionConfigRequest) returns (RefreshTaskListPartitionConfigResponse); } message TaskListPartition { repeated string isolation_groups = 1; } message TaskListPartitionConfig { int64 version = 1; int32 num_read_partitions = 2 [deprecated = true]; int32 num_write_partitions = 3 [deprecated = true]; map read_partitions = 4; map write_partitions = 5; } message LoadBalancerHints { int64 backlog_count = 1; double rate_per_second = 2; } message PollForDecisionTaskRequest { api.v1.PollForDecisionTaskRequest request = 1; string domain_id = 2; string poller_id = 3; string forwarded_from = 4; string isolation_group = 5; } message PollForDecisionTaskResponse { bytes task_token = 1; api.v1.WorkflowExecution workflow_execution = 2; api.v1.WorkflowType workflow_type = 3; google.protobuf.Int64Value previous_started_event_id = 4; int64 started_event_id = 5; int32 attempt = 6; int64 next_event_id = 7; int64 backlog_count_hint = 8; bool sticky_execution_enabled = 9; api.v1.WorkflowQuery query = 10; shared.v1.TransientDecisionInfo decision_info = 11; api.v1.TaskList workflow_execution_task_list = 12; int32 event_store_version = 13; bytes branch_token = 14; google.protobuf.Timestamp scheduled_time = 15; google.protobuf.Timestamp started_time = 16; map queries = 17; int64 total_history_bytes = 18; TaskListPartitionConfig partition_config = 19; LoadBalancerHints load_balancer_hints = 20; api.v1.AutoConfigHint auto_config_hint = 21; } message PollForActivityTaskRequest { api.v1.PollForActivityTaskRequest request = 1; string domain_id = 2; string poller_id = 3; string forwarded_from = 4; string isolation_group = 5; } message PollForActivityTaskResponse { bytes task_token = 1; api.v1.WorkflowExecution workflow_execution = 2; string activity_id = 3; api.v1.ActivityType activity_type = 4; api.v1.Payload input = 5; google.protobuf.Timestamp scheduled_time = 6; google.protobuf.Timestamp started_time = 7; google.protobuf.Duration schedule_to_close_timeout = 8; google.protobuf.Duration start_to_close_timeout = 9; google.protobuf.Duration heartbeat_timeout = 10; int32 attempt = 11; google.protobuf.Timestamp scheduled_time_of_this_attempt = 12; api.v1.Payload heartbeat_details = 13; api.v1.WorkflowType workflow_type = 14; string workflow_domain = 15; api.v1.Header header = 16; LoadBalancerHints load_balancer_hints = 17; TaskListPartitionConfig partition_config = 19; api.v1.AutoConfigHint auto_config_hint = 20; } message AddDecisionTaskRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; api.v1.TaskList task_list = 3; int64 schedule_id = 4; google.protobuf.Duration schedule_to_start_timeout = 5; shared.v1.TaskSource source = 6; string forwarded_from = 7; map partition_config = 8; } message AddDecisionTaskResponse { TaskListPartitionConfig partition_config = 1; } message AddActivityTaskRequest { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; string source_domain_id = 3; api.v1.TaskList task_list = 4; int64 schedule_id = 5; google.protobuf.Duration schedule_to_start_timeout = 6; shared.v1.TaskSource source = 7; string forwarded_from = 8; ActivityTaskDispatchInfo activityTaskDispatchInfo = 9; map partition_config = 10; } message ActivityTaskDispatchInfo { api.v1.HistoryEvent scheduled_event = 1; google.protobuf.Timestamp started_time = 2; int32 attempt = 3; google.protobuf.Timestamp scheduled_time_of_this_attempt = 4; api.v1.Payload heartbeat_details = 5; api.v1.WorkflowType workflow_type = 6; string workflow_domain = 7; } message AddActivityTaskResponse { TaskListPartitionConfig partition_config = 1; } message QueryWorkflowRequest { api.v1.QueryWorkflowRequest request = 1; string domain_id = 2; api.v1.TaskList task_list = 3; string forwarded_from = 4; } message QueryWorkflowResponse { api.v1.Payload query_result = 1; api.v1.QueryRejected query_rejected = 2; api.v1.TaskListPartitionConfig partition_config = 3; } message RespondQueryTaskCompletedRequest { api.v1.RespondQueryTaskCompletedRequest request = 1; string domain_id = 2; api.v1.TaskList task_list = 3; string task_id = 4; } message RespondQueryTaskCompletedResponse { } message CancelOutstandingPollRequest { string domain_id = 1; string poller_id = 2; api.v1.TaskListType task_list_type = 3; api.v1.TaskList task_list = 4; } message CancelOutstandingPollResponse { } message DescribeTaskListRequest { api.v1.DescribeTaskListRequest request = 1; string domain_id = 2; } message DescribeTaskListResponse { repeated api.v1.PollerInfo pollers = 1; api.v1.TaskListStatus task_list_status = 2; api.v1.TaskListPartitionConfig partition_config = 3; api.v1.TaskList task_list = 4; } message ListTaskListPartitionsRequest { string domain = 1; api.v1.TaskList task_list = 2; } message ListTaskListPartitionsResponse { repeated api.v1.TaskListPartitionMetadata activity_task_list_partitions = 1; repeated api.v1.TaskListPartitionMetadata decision_task_list_partitions = 2; } message GetTaskListsByDomainRequest { string domain = 1; } message GetTaskListsByDomainResponse { map decision_task_list_map = 1; map activity_task_list_map = 2; } message UpdateTaskListPartitionConfigRequest { string domain_id = 1; api.v1.TaskList task_list = 2; api.v1.TaskListType task_list_type = 3; api.v1.TaskListPartitionConfig partition_config = 4; } message UpdateTaskListPartitionConfigResponse {} message RefreshTaskListPartitionConfigRequest { string domain_id = 1; api.v1.TaskList task_list = 2; api.v1.TaskListType task_list_type = 3; api.v1.TaskListPartitionConfig partition_config = 4; } message RefreshTaskListPartitionConfigResponse { } ================================================ FILE: proto/internal/uber/cadence/sharddistributor/v1/canary.proto ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. syntax = "proto3"; package uber.cadence.sharddistributor.v1; option go_package = "github.com/uber/cadence/.gen/proto/sharddistributor/v1;sharddistributorv1"; // ShardDistributorExecutorCanaryAPI is used for canary testing executor-to-executor communication service ShardDistributorExecutorCanaryAPI { // Ping allows one executor to ping another executor that owns a specific shard rpc Ping(PingRequest) returns (PingResponse); } message PingRequest { string shard_key = 1; string namespace = 2; } message PingResponse { string executor_id = 1; bool owns_shard = 2; string shard_key = 3; } ================================================ FILE: proto/internal/uber/cadence/sharddistributor/v1/executor.proto ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. syntax = "proto3"; package uber.cadence.sharddistributor.v1; option go_package = "github.com/uber/cadence/.gen/proto/sharddistributor/v1;sharddistributorv1"; // ShardDistributorExecutionAPI is used update the state of the executor and fetch the next shard assignments. service ShardDistributorExecutorAPI { // Heartbeat reports the current state of the executor, and fetches the next shard assignments. rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse); } message HeartbeatRequest { string namespace = 1; string executor_id = 2; ExecutorStatus status = 3; map shard_status_reports = 4; map metadata = 5; } enum ExecutorStatus { EXECUTOR_STATUS_INVALID = 0; EXECUTOR_STATUS_ACTIVE = 1; EXECUTOR_STATUS_DRAINING = 2; EXECUTOR_STATUS_DRAINED = 3; } message ShardStatusReport { ShardStatus status = 1; double shard_load = 2; } // We only have one status for now, but when adding // graceful handover, we will need to add more statuses. // We do not need an "inactive" status, as we will not include // inactive shards in the heartbeat request. enum ShardStatus { SHARD_STATUS_INVALID = 0; SHARD_STATUS_READY = 1; SHARD_STATUS_DONE = 2; } message HeartbeatResponse { map shard_assignments = 1; MigrationMode migration_mode = 2; } message ShardAssignment { AssignmentStatus status = 1; } // We only have one status for now, but when adding // graceful handover, we will need to add more statuses. // We do not need an "inactive" status, as we will not include // inactive shards in the heartbeat request. enum AssignmentStatus { ASSIGNMENT_STATUS_INVALID = 0; ASSIGNMENT_STATUS_READY = 1; } // We handle the migration steps from SD side enum MigrationMode { MIGRATION_MODE_INVALID = 0; MIGRATION_MODE_LOCAL_PASSTHROUGH = 1; MIGRATION_MODE_LOCAL_PASSTHROUGH_SHADOW = 2; MIGRATION_MODE_DISTRIBUTED_PASSTHROUGH = 3; MIGRATION_MODE_ONBOARDED = 4; } ================================================ FILE: proto/internal/uber/cadence/sharddistributor/v1/service.proto ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. syntax = "proto3"; package uber.cadence.sharddistributor.v1; option go_package = "github.com/uber/cadence/.gen/proto/sharddistributor/v1;sharddistributorv1"; // ShardDistributorAPI is used to query shard distributor for the current owner of a shard service ShardDistributorAPI { // GetShardOwner returns the owner of a specific shard rpc GetShardOwner(GetShardOwnerRequest) returns (GetShardOwnerResponse); // WatchNamespaceState streams updates about executors and their assigned shards for a namespace rpc WatchNamespaceState(WatchNamespaceStateRequest) returns (stream WatchNamespaceStateResponse); } message GetShardOwnerRequest { string shard_key = 1; string namespace = 2; } message GetShardOwnerResponse { string owner = 1; string namespace = 2; map metadata = 3; } message NamespaceNotFoundError { string namespace = 1; } message ShardNotFoundError { string namespace = 1; string shard_key = 2; } message WatchNamespaceStateRequest { string namespace = 1; } message WatchNamespaceStateResponse { // Executor information with assigned shards repeated ExecutorInfo executors = 1; } message ExecutorInfo { string executor_id = 1; map metadata = 2; repeated Shard shards = 3; } message Shard { string shard_key = 1; } ================================================ FILE: proto/internal/uber/cadence/shared/v1/any.proto ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. syntax = "proto3"; package uber.cadence.shared.v1; option go_package = "github.com/uber/cadence/.gen/proto/shared/v1;sharedv1"; // Any is a logical duplicate of google.protobuf.Any, but is intentionally // breaking compatibility because it will not be directly used as a // google.protobuf.Any in our high-level RPC mappers. // // The intent of the type is the same, but it is not intended to be directly // compatible with google.protobuf.Any - this blob is RPC-type agnostic by // design (as the underlying data may be transported over proto or thrift), and // the data-bytes may or may not be proto-encoded. // // This is intentionally different from DataBlob, which supports only a handful // of known encodings so it can be interpreted everywhere. Any supports literally // any contents, and needs to be considered opaque until it is given to something // that is expecting it. // // See value_type to interpret the contents. message Any { // Type-string describing value's contents, and intentionally avoiding the // name "type" as it is often a special term. // This should usually be a hard-coded string of some kind. string value_type = 1; // Arbitrarily-encoded bytes, to be deserialized by a runtime implementation. // The contents are described by value_type. bytes value = 2; } ================================================ FILE: proto/internal/uber/cadence/shared/v1/error.proto ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. syntax = "proto3"; package uber.cadence.shared.v1; option go_package = "github.com/uber/cadence/.gen/proto/shared/v1;sharedv1"; import "uber/cadence/api/v1/common.proto"; import "uber/cadence/admin/v1/history.proto"; message CurrentBranchChangedError { bytes current_branch_token = 1; } message InternalDataInconsistencyError { } message EventAlreadyStartedError { } message RemoteSyncMatchedError { } message RetryTaskV2Error { string domain_id = 1; api.v1.WorkflowExecution workflow_execution = 2; admin.v1.VersionHistoryItem start_event = 3; admin.v1.VersionHistoryItem end_event = 4; } message ShardOwnershipLostError { string owner = 1; } message TaskListNotOwnedByHostError { string owned_by_identity = 1; string my_identity = 2; string task_list_name = 3; } ================================================ FILE: proto/internal/uber/cadence/shared/v1/history.proto ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. syntax = "proto3"; package uber.cadence.shared.v1; option go_package = "github.com/uber/cadence/.gen/proto/shared/v1;sharedv1"; import "uber/cadence/api/v1/history.proto"; import "uber/cadence/admin/v1/history.proto"; message TransientDecisionInfo { api.v1.HistoryEvent scheduled_event = 1; api.v1.HistoryEvent started_event = 2; } // VersionHistories contains all version histories from all branches. message VersionHistories { int32 current_version_history_index = 1; repeated admin.v1.VersionHistory histories = 2; } ================================================ FILE: proto/internal/uber/cadence/shared/v1/tasklist.proto ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. syntax = "proto3"; package uber.cadence.shared.v1; option go_package = "github.com/uber/cadence/.gen/proto/shared/v1;sharedv1"; // TaskSource is the source from which a task was produced. enum TaskSource { TASK_SOURCE_INVALID = 0; // Task produced by history service. TASK_SOURCE_HISTORY = 1; // Task produced from matching db backlog. TASK_SOURCE_DB_BACKLOG = 2; } ================================================ FILE: proto/internal/uber/cadence/shared/v1/workflow.proto ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. syntax = "proto3"; package uber.cadence.shared.v1; option go_package = "github.com/uber/cadence/.gen/proto/shared/v1;sharedv1"; enum WorkflowState { WORKFLOW_STATE_INVALID = 0; WORKFLOW_STATE_CREATED = 1; WORKFLOW_STATE_RUNNING = 2; WORKFLOW_STATE_COMPLETED = 3; WORKFLOW_STATE_ZOMBIE = 4; WORKFLOW_STATE_VOID = 5; WORKFLOW_STATE_CORRUPTED = 6; } ================================================ FILE: proto/persistenceblobs/v1/gogo.proto ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/temporalio/gogo-protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. syntax = "proto2"; package gogoproto; import "google/protobuf/descriptor.proto"; option go_package = ".gen/proto"; extend google.protobuf.EnumOptions { optional bool goproto_enum_prefix = 62001; optional bool goproto_enum_stringer = 62021; optional bool enum_stringer = 62022; optional string enum_customname = 62023; optional bool enumdecl = 62024; } extend google.protobuf.EnumValueOptions { optional string enumvalue_customname = 66001; } extend google.protobuf.FileOptions { optional bool goproto_getters_all = 63001; optional bool goproto_enum_prefix_all = 63002; optional bool goproto_stringer_all = 63003; optional bool verbose_equal_all = 63004; optional bool face_all = 63005; optional bool gostring_all = 63006; optional bool populate_all = 63007; optional bool stringer_all = 63008; optional bool onlyone_all = 63009; optional bool equal_all = 63013; optional bool description_all = 63014; optional bool testgen_all = 63015; optional bool benchgen_all = 63016; optional bool marshaler_all = 63017; optional bool unmarshaler_all = 63018; optional bool stable_marshaler_all = 63019; optional bool sizer_all = 63020; optional bool goproto_enum_stringer_all = 63021; optional bool enum_stringer_all = 63022; optional bool unsafe_marshaler_all = 63023; optional bool unsafe_unmarshaler_all = 63024; optional bool goproto_extensions_map_all = 63025; optional bool goproto_unrecognized_all = 63026; optional bool gogoproto_import = 63027; optional bool protosizer_all = 63028; optional bool compare_all = 63029; optional bool typedecl_all = 63030; optional bool enumdecl_all = 63031; optional bool goproto_registration = 63032; optional bool messagename_all = 63033; optional bool goproto_sizecache_all = 63034; optional bool goproto_unkeyed_all = 63035; } extend google.protobuf.MessageOptions { optional bool goproto_getters = 64001; optional bool goproto_stringer = 64003; optional bool verbose_equal = 64004; optional bool face = 64005; optional bool gostring = 64006; optional bool populate = 64007; optional bool stringer = 67008; optional bool onlyone = 64009; optional bool equal = 64013; optional bool description = 64014; optional bool testgen = 64015; optional bool benchgen = 64016; optional bool marshaler = 64017; optional bool unmarshaler = 64018; optional bool stable_marshaler = 64019; optional bool sizer = 64020; optional bool unsafe_marshaler = 64023; optional bool unsafe_unmarshaler = 64024; optional bool goproto_extensions_map = 64025; optional bool goproto_unrecognized = 64026; optional bool protosizer = 64028; optional bool compare = 64029; optional bool typedecl = 64030; optional bool messagename = 64033; optional bool goproto_sizecache = 64034; optional bool goproto_unkeyed = 64035; } extend google.protobuf.FieldOptions { optional bool nullable = 65001; optional bool embed = 65002; optional string customtype = 65003; optional string customname = 65004; optional string jsontag = 65005; optional string moretags = 65006; optional string casttype = 65007; optional string castkey = 65008; optional string castvalue = 65009; optional bool stdtime = 65010; optional bool stdduration = 65011; optional bool wktpointer = 65012; } ================================================ FILE: proto/persistenceblobs/v1/message.proto ================================================ // Modifications Copyright (c) 2020 Uber Technologies Inc. // Copyright (c) 2020 Temporal Technologies, Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. syntax = "proto3"; option go_package = ".gen/proto"; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; import "gogo.proto"; message DomainDetail { DomainInfo info = 1; DomainConfig config = 2; DomainReplicationConfig replication_config = 3; int64 config_version = 4; int64 failover_notification_version = 5; int64 failover_version = 6; google.protobuf.Timestamp failover_end_time = 7 [(gogoproto.stdtime) = true]; } message DomainInfo { string id = 1; DomainState state = 2; string name = 3; string description = 4; string owner = 5; map data = 6; } message DomainReplicationConfig { string active_cluster_name = 1; repeated string clusters = 2; } message DomainConfig { google.protobuf.Duration retention = 1 [(gogoproto.stdduration) = true]; string archival_bucket = 2; BadBinaries bad_binaries = 3; ArchivalState history_archival_state = 4; string history_archival_uri = 5; ArchivalState visibility_archival_state = 6; string visibility_archival_uri = 7; } message BadBinaries { map binaries = 1; } message BadBinaryInfo { string reason = 1; string operator = 2; google.protobuf.Timestamp create_time = 3 [(gogoproto.stdtime) = true]; } enum ArchivalState { ARCHIVAL_STATE_UNSPECIFIED = 0; ARCHIVAL_STATE_DISABLED = 1; ARCHIVAL_STATE_ENABLED = 2; } enum DomainState { DOMAIN_STATE_UNSPECIFIED = 0; DOMAIN_STATE_REGISTERED = 1; DOMAIN_STATE_DEPRECATED = 2; DOMAIN_STATE_DELETED = 3; } message ActivityInfo { int64 version = 1; int64 scheduled_event_batch_id = 2; HistoryEvent scheduled_event = 3; google.protobuf.Timestamp scheduled_time = 4 [(gogoproto.stdtime) = true]; int64 started_id = 5; HistoryEvent started_event = 6; google.protobuf.Timestamp started_time = 7 [(gogoproto.stdtime) = true]; string activity_id = 8; string request_id = 9; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" is used to indicate interval. --) google.protobuf.Duration schedule_to_start_timeout = 10 [(gogoproto.stdduration) = true]; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" is used to indicate interval. --) google.protobuf.Duration schedule_to_close_timeout = 11 [(gogoproto.stdduration) = true]; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" is used to indicate interval. --) google.protobuf.Duration start_to_close_timeout = 12 [(gogoproto.stdduration) = true]; google.protobuf.Duration heartbeat_timeout = 13 [(gogoproto.stdduration) = true]; bool cancel_requested = 14; int64 cancel_request_id = 15; int32 timer_task_status = 16; int32 attempt = 17; string task_list = 18; string started_identity = 19; bool has_retry_policy = 20; google.protobuf.Duration retry_initial_interval = 21 [(gogoproto.stdduration) = true]; google.protobuf.Duration retry_maximum_interval = 22 [(gogoproto.stdduration) = true]; int32 retry_maximum_attempts = 23; google.protobuf.Timestamp retry_expiration_time = 24 [(gogoproto.stdtime) = true]; double retry_backoff_coefficient = 25; repeated string retry_non_retryable_error_types = 26; Failure retry_last_failure = 27; string retry_last_worker_identity = 28; string domain_id = 29; int64 schedule_id = 30; } message HistoryEvent { int64 event_id = 1; google.protobuf.Timestamp event_time = 2 [(gogoproto.stdtime) = true]; EventType event_type = 3; int64 version = 4; int64 task_id = 5; oneof attributes { WorkflowExecutionStartedEventAttributes workflow_execution_started_event_attributes = 6; WorkflowExecutionCompletedEventAttributes workflow_execution_completed_event_attributes = 7; WorkflowExecutionFailedEventAttributes workflow_execution_failed_event_attributes = 8; WorkflowExecutionTimedOutEventAttributes workflow_execution_timed_out_event_attributes = 9; DecisionTaskScheduledEventAttributes decision_task_scheduled_event_attributes = 10; DecisionTaskStartedEventAttributes decision_task_started_event_attributes = 11; DecisionTaskCompletedEventAttributes decision_task_completed_event_attributes = 12; DecisionTaskTimedOutEventAttributes decision_task_timed_out_event_attributes = 13; DecisionTaskFailedEventAttributes decision_task_failed_event_attributes = 14; ActivityTaskScheduledEventAttributes activity_task_scheduled_event_attributes = 15; ActivityTaskStartedEventAttributes activity_task_started_event_attributes = 16; ActivityTaskCompletedEventAttributes activity_task_completed_event_attributes = 17; ActivityTaskFailedEventAttributes activity_task_failed_event_attributes = 18; ActivityTaskTimedOutEventAttributes activity_task_timed_out_event_attributes = 19; TimerStartedEventAttributes timer_started_event_attributes = 20; TimerFiredEventAttributes timer_fired_event_attributes = 21; ActivityTaskCancelRequestedEventAttributes activity_task_cancel_requested_event_attributes = 22; ActivityTaskCanceledEventAttributes activity_task_canceled_event_attributes = 23; TimerCanceledEventAttributes timer_canceled_event_attributes = 24; MarkerRecordedEventAttributes marker_recorded_event_attributes = 25; WorkflowExecutionSignaledEventAttributes workflow_execution_signaled_event_attributes = 26; WorkflowExecutionTerminatedEventAttributes workflow_execution_terminated_event_attributes = 27; WorkflowExecutionCancelRequestedEventAttributes workflow_execution_cancel_requested_event_attributes = 28; WorkflowExecutionCanceledEventAttributes workflow_execution_canceled_event_attributes = 29; RequestCancelExternalWorkflowExecutionInitiatedEventAttributes request_cancel_external_workflow_execution_initiated_event_attributes = 30; RequestCancelExternalWorkflowExecutionFailedEventAttributes request_cancel_external_workflow_execution_failed_event_attributes = 31; ExternalWorkflowExecutionCancelRequestedEventAttributes external_workflow_execution_cancel_requested_event_attributes = 32; WorkflowExecutionContinuedAsNewEventAttributes workflow_execution_continued_as_new_event_attributes = 33; StartChildWorkflowExecutionInitiatedEventAttributes start_child_workflow_execution_initiated_event_attributes = 34; StartChildWorkflowExecutionFailedEventAttributes start_child_workflow_execution_failed_event_attributes = 35; ChildWorkflowExecutionStartedEventAttributes child_workflow_execution_started_event_attributes = 36; ChildWorkflowExecutionCompletedEventAttributes child_workflow_execution_completed_event_attributes = 37; ChildWorkflowExecutionFailedEventAttributes child_workflow_execution_failed_event_attributes = 38; ChildWorkflowExecutionCanceledEventAttributes child_workflow_execution_canceled_event_attributes = 39; ChildWorkflowExecutionTimedOutEventAttributes child_workflow_execution_timed_out_event_attributes = 40; ChildWorkflowExecutionTerminatedEventAttributes child_workflow_execution_terminated_event_attributes = 41; SignalExternalWorkflowExecutionInitiatedEventAttributes signal_external_workflow_execution_initiated_event_attributes = 42; SignalExternalWorkflowExecutionFailedEventAttributes signal_external_workflow_execution_failed_event_attributes = 43; ExternalWorkflowExecutionSignaledEventAttributes external_workflow_execution_signaled_event_attributes = 44; UpsertWorkflowSearchAttributesEventAttributes upsert_workflow_search_attributes_event_attributes = 45; } } // Whenever this list of events is changed do change the function shouldBufferEvent in mutableStateBuilder.go to make sure to do the correct event ordering. enum EventType { EVENT_TYPE_UNSPECIFIED = 0; EVENT_TYPE_WORKFLOW_EXECUTION_STARTED = 1; EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED = 2; EVENT_TYPE_WORKFLOW_EXECUTION_FAILED = 3; EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT = 4; EVENT_TYPE_DECISION_TASK_SCHEDULED = 5; EVENT_TYPE_DECISION_TASK_STARTED = 6; EVENT_TYPE_DECISION_TASK_COMPLETED = 7; EVENT_TYPE_DECISION_TASK_TIMED_OUT = 8; EVENT_TYPE_DECISION_TASK_FAILED = 9; EVENT_TYPE_ACTIVITY_TASK_SCHEDULED = 10; EVENT_TYPE_ACTIVITY_TASK_STARTED = 11; EVENT_TYPE_ACTIVITY_TASK_COMPLETED = 12; EVENT_TYPE_ACTIVITY_TASK_FAILED = 13; EVENT_TYPE_ACTIVITY_TASK_TIMED_OUT = 14; EVENT_TYPE_ACTIVITY_TASK_CANCEL_REQUESTED = 15; EVENT_TYPE_ACTIVITY_TASK_CANCELED = 16; EVENT_TYPE_TIMER_STARTED = 17; EVENT_TYPE_TIMER_FIRED = 18; EVENT_TYPE_TIMER_CANCELED = 19; EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED = 20; EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED = 21; EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED = 22; EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED = 23; EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_CANCEL_REQUESTED = 24; EVENT_TYPE_MARKER_RECORDED = 25; EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED = 26; EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED = 27; EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW = 28; EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED = 29; EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_FAILED = 30; EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_STARTED = 31; EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_COMPLETED = 32; EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_FAILED = 33; EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_CANCELED = 34; EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TIMED_OUT = 35; EVENT_TYPE_CHILD_WORKFLOW_EXECUTION_TERMINATED = 36; EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED = 37; EVENT_TYPE_SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED = 38; EVENT_TYPE_EXTERNAL_WORKFLOW_EXECUTION_SIGNALED = 39; EVENT_TYPE_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES = 40; } message WorkflowExecutionStartedEventAttributes { WorkflowType workflow_type = 1; string parent_workflow_domain = 2; WorkflowExecution parent_workflow_execution = 3; int64 parent_initiated_event_id = 4; TaskList task_list = 5; Payloads input = 6; // Total workflow execution timeout including retries and continue as new. google.protobuf.Duration workflow_execution_timeout = 7 [(gogoproto.stdduration) = true]; // Timeout of a single workflow run. google.protobuf.Duration workflow_run_timeout = 8 [(gogoproto.stdduration) = true]; // Timeout of a single workflow task. google.protobuf.Duration decision_task_timeout = 9 [(gogoproto.stdduration) = true]; string continued_execution_run_id = 10; ContinueAsNewInitiator initiator = 11; Failure continued_failure = 12; Payloads last_completion_result = 13; // This is the runId when the WorkflowExecutionStarted event is written. string original_execution_run_id = 14; string identity = 15; // This is the very first runId along the chain of ContinueAsNew and Reset. string first_execution_run_id = 16; RetryPolicy retry_policy = 17; int32 attempt = 18; // The absolute time at which workflow is timed out. // This time is passed without change to the next run/retry of a workflow. google.protobuf.Timestamp workflow_execution_expiration_time = 19 [(gogoproto.stdtime) = true]; string cron_schedule = 20; google.protobuf.Duration first_decision_task_backoff = 21 [(gogoproto.stdduration) = true]; Memo memo = 22; SearchAttributes search_attributes = 23; ResetPoints prev_auto_reset_points = 24; Header header = 25; } message WorkflowExecutionCompletedEventAttributes { Payloads result = 1; int64 decision_task_completed_event_id = 2; } message WorkflowExecutionFailedEventAttributes { Failure failure = 1; RetryState retry_state = 2; int64 decision_task_completed_event_id = 3; } message WorkflowExecutionTimedOutEventAttributes { RetryState retry_state = 1; } message WorkflowExecutionContinuedAsNewEventAttributes { string new_execution_run_id = 1; WorkflowType workflow_type = 2; TaskList task_list = 3; Payloads input = 4; // workflow_execution_timeout is omitted as it shouldn'be overridden from within a workflow. // Timeout of a single workflow run. google.protobuf.Duration workflow_run_timeout = 5 [(gogoproto.stdduration) = true]; // Timeout of a single workflow task. google.protobuf.Duration decision_task_timeout = 6 [(gogoproto.stdduration) = true]; int64 decision_task_completed_event_id = 7; google.protobuf.Duration backoff_start_interval = 8 [(gogoproto.stdduration) = true]; ContinueAsNewInitiator initiator = 9; Failure failure = 10; Payloads last_completion_result = 11; Header header = 12; Memo memo = 13; SearchAttributes search_attributes = 14; } message DecisionTaskScheduledEventAttributes { TaskList task_list = 1; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" is used to indicate interval. --) google.protobuf.Duration start_to_close_timeout = 2 [(gogoproto.stdduration) = true]; int32 attempt = 3; } message DecisionTaskStartedEventAttributes { int64 scheduled_event_id = 1; string identity = 2; string request_id = 3; } message DecisionTaskCompletedEventAttributes { int64 scheduled_event_id = 1; int64 started_event_id = 2; string identity = 3; string binary_checksum = 4; } message DecisionTaskTimedOutEventAttributes { int64 scheduled_event_id = 1; int64 started_event_id = 2; TimeoutType timeout_type = 3; } message DecisionTaskFailedEventAttributes { int64 scheduled_event_id = 1; int64 started_event_id = 2; DecisionTaskFailedCause cause = 3; Failure failure = 4; string identity = 5; // For reset workflow. string base_run_id = 6; string new_run_id = 7; int64 fork_event_version = 8; string binary_checksum = 9; } message ActivityTaskScheduledEventAttributes { string activity_id = 1; ActivityType activity_type = 2; string domain = 3; TaskList task_list = 4; Header header = 5; Payloads input = 6; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" is used to indicate interval. --) // Indicates how long the caller is willing to wait for an activity completion. // Limits for how long retries are happening. Either this or start_to_close_timeout_seconds must be specified. google.protobuf.Duration schedule_to_close_timeout = 7 [(gogoproto.stdduration) = true]; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" is used to indicate interval. --) // Limits time an activity task can stay in a task list before a worker picks it up. // This timeout is always non retryable as all a retry would achieve is to put it back into the same list. // Defaults to schedule_to_close_timeout_seconds or workflow execution timeout if not specified. google.protobuf.Duration schedule_to_start_timeout = 8 [(gogoproto.stdduration) = true]; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" is used to indicate interval. --) // Maximum time an activity is allowed to execute after a pick up by a worker. // This timeout is always retryable. Either this or schedule_to_close_timeout_seconds must be specified. google.protobuf.Duration start_to_close_timeout = 9 [(gogoproto.stdduration) = true]; // Maximum time between successful worker heartbeats. google.protobuf.Duration heartbeat_timeout = 10 [(gogoproto.stdduration) = true]; int64 decision_task_completed_event_id = 11; // Activities are provided by a default retry policy controlled through the service dynamic configuration. // Retries are happening up to schedule_to_close_timeout. // To disable retries set retry_policy.maximum_attempts to 1. RetryPolicy retry_policy = 12; } message ActivityTaskStartedEventAttributes { int64 scheduled_event_id = 1; string identity = 2; string request_id = 3; int32 attempt = 4; Failure last_failure = 5; } message ActivityTaskCompletedEventAttributes { Payloads result = 1; int64 scheduled_event_id = 2; int64 started_event_id = 3; string identity = 4; } message ActivityTaskFailedEventAttributes { Failure failure = 1; int64 scheduled_event_id = 2; int64 started_event_id = 3; string identity = 4; RetryState retry_state = 5; } message ActivityTaskTimedOutEventAttributes { // For retry activity, it may have a failure before timeout. It is stored as `cause` in `failure`. Failure failure = 1; int64 scheduled_event_id = 2; int64 started_event_id = 3; RetryState retry_state = 4; } message ActivityTaskCancelRequestedEventAttributes { int64 scheduled_event_id = 1; int64 decision_task_completed_event_id = 2; } message ActivityTaskCanceledEventAttributes { Payloads details = 1; int64 latest_cancel_requested_event_id = 2; int64 scheduled_event_id = 3; int64 started_event_id = 4; string identity = 5; } message TimerStartedEventAttributes { string timer_id = 1; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" is used to indicate interval. --) google.protobuf.Duration start_to_fire_timeout = 2 [(gogoproto.stdduration) = true]; int64 decision_task_completed_event_id = 3; } message TimerFiredEventAttributes { string timer_id = 1; int64 started_event_id = 2; } message TimerCanceledEventAttributes { string timer_id = 1; int64 started_event_id = 2; int64 decision_task_completed_event_id = 3; string identity = 4; } message WorkflowExecutionCancelRequestedEventAttributes { string cause = 1; int64 external_initiated_event_id = 2; WorkflowExecution external_workflow_execution = 3; string identity = 4; } message WorkflowExecutionCanceledEventAttributes { int64 decision_task_completed_event_id = 1; Payloads details = 2; } message MarkerRecordedEventAttributes { string marker_name = 1; map details = 2; int64 decision_task_completed_event_id = 3; Header header = 4; Failure failure = 5; } message WorkflowExecutionSignaledEventAttributes { string signal_name = 1; Payloads input = 2; string identity = 3; } message WorkflowExecutionTerminatedEventAttributes { string reason = 1; Payloads details = 2; string identity = 3; } message RequestCancelExternalWorkflowExecutionInitiatedEventAttributes { int64 decision_task_completed_event_id = 1; string domain = 2; WorkflowExecution workflow_execution = 3; string control = 4; bool child_workflow_only = 5; } message RequestCancelExternalWorkflowExecutionFailedEventAttributes { CancelExternalWorkflowExecutionFailedCause cause = 1; int64 decision_task_completed_event_id = 2; string domain = 3; WorkflowExecution workflow_execution = 4; int64 initiated_event_id = 5; string control = 6; } message ExternalWorkflowExecutionCancelRequestedEventAttributes { int64 initiated_event_id = 1; string domain = 2; WorkflowExecution workflow_execution = 3; } message SignalExternalWorkflowExecutionInitiatedEventAttributes { int64 decision_task_completed_event_id = 1; string domain = 2; WorkflowExecution workflow_execution = 3; string signal_name = 4; Payloads input = 5; string control = 6; bool child_workflow_only = 7; } message SignalExternalWorkflowExecutionFailedEventAttributes { SignalExternalWorkflowExecutionFailedCause cause = 1; int64 decision_task_completed_event_id = 2; string domain = 3; WorkflowExecution workflow_execution = 4; int64 initiated_event_id = 5; string control = 6; } message ExternalWorkflowExecutionSignaledEventAttributes { int64 initiated_event_id = 1; string domain = 2; WorkflowExecution workflow_execution = 3; string control = 4; } message UpsertWorkflowSearchAttributesEventAttributes { int64 decision_task_completed_event_id = 1; SearchAttributes search_attributes = 2; } message StartChildWorkflowExecutionInitiatedEventAttributes { string domain = 1; string workflow_id = 2; WorkflowType workflow_type = 3; TaskList task_list = 4; Payloads input = 5; // Total workflow execution timeout including retries and continue as new. google.protobuf.Duration workflow_execution_timeout = 6 [(gogoproto.stdduration) = true]; // Timeout of a single workflow run. google.protobuf.Duration workflow_run_timeout = 7 [(gogoproto.stdduration) = true]; // Timeout of a single workflow task. google.protobuf.Duration decision_task_timeout = 8 [(gogoproto.stdduration) = true]; // Default: PARENT_CLOSE_POLICY_TERMINATE. ParentClosePolicy parent_close_policy = 9; string control = 10; int64 decision_task_completed_event_id = 11; // Default: WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE. WorkflowIdReusePolicy workflow_id_reuse_policy = 12; RetryPolicy retry_policy = 13; string cron_schedule = 14; Header header = 15; Memo memo = 16; SearchAttributes search_attributes = 17; } message StartChildWorkflowExecutionFailedEventAttributes { string domain = 1; string workflow_id = 2; WorkflowType workflow_type = 3; StartChildWorkflowExecutionFailedCause cause = 4; string control = 5; int64 initiated_event_id = 6; int64 decision_task_completed_event_id = 7; } message ChildWorkflowExecutionStartedEventAttributes { string domain = 1; int64 initiated_event_id = 2; WorkflowExecution workflow_execution = 3; WorkflowType workflow_type = 4; Header header = 5; } message ChildWorkflowExecutionCompletedEventAttributes { Payloads result = 1; string domain = 2; WorkflowExecution workflow_execution = 3; WorkflowType workflow_type = 4; int64 initiated_event_id = 5; int64 started_event_id = 6; } message ChildWorkflowExecutionFailedEventAttributes { Failure failure = 1; string domain = 2; WorkflowExecution workflow_execution = 3; WorkflowType workflow_type = 4; int64 initiated_event_id = 5; int64 started_event_id = 6; RetryState retry_state = 7; } message ChildWorkflowExecutionCanceledEventAttributes { Payloads details = 1; string domain = 2; WorkflowExecution workflow_execution = 3; WorkflowType workflow_type = 4; int64 initiated_event_id = 5; int64 started_event_id = 6; } message ChildWorkflowExecutionTimedOutEventAttributes { string domain = 1; WorkflowExecution workflow_execution = 2; WorkflowType workflow_type = 3; int64 initiated_event_id = 4; int64 started_event_id = 5; RetryState retry_state = 6; } message ChildWorkflowExecutionTerminatedEventAttributes { string domain = 1; WorkflowExecution workflow_execution = 2; WorkflowType workflow_type = 3; int64 initiated_event_id = 4; int64 started_event_id = 5; } message WorkflowExecution { string workflow_id = 1; string run_id = 2; } message WorkflowType { string name = 1; } message Payloads { repeated Payload payloads = 1; } message Payload { map metadata = 1; bytes data = 2; } enum RetryState { RETRY_STATE_UNSPECIFIED = 0; RETRY_STATE_IN_PROGRESS = 1; RETRY_STATE_NON_RETRYABLE_FAILURE = 2; RETRY_STATE_TIMEOUT = 3; RETRY_STATE_MAXIMUM_ATTEMPTS_REACHED = 4; RETRY_STATE_RETRY_POLICY_NOT_SET = 5; RETRY_STATE_INTERNAL_SERVER_ERROR = 6; RETRY_STATE_CANCEL_REQUESTED = 7; } message ApplicationFailureInfo { string type = 1; bool non_retryable = 2; Payloads details = 3; } message TimeoutFailureInfo { TimeoutType timeout_type = 1; Payloads last_heartbeat_details = 2; } message CanceledFailureInfo { Payloads details = 1; } message TerminatedFailureInfo { } message ServerFailureInfo { bool non_retryable = 1; } message ResetWorkflowFailureInfo { Payloads last_heartbeat_details = 1; } message ActivityFailureInfo { int64 scheduled_event_id = 1; int64 started_event_id = 2; string identity = 3; ActivityType activity_type = 4; string activity_id = 5; RetryState retry_state = 6; } message ChildWorkflowExecutionFailureInfo { string domain = 1; WorkflowExecution workflow_execution = 2; WorkflowType workflow_type = 3; int64 initiated_event_id = 4; int64 started_event_id = 5; RetryState retry_state = 6; } message Failure { string message = 1; string source = 2; string stack_trace = 3; Failure cause = 4; oneof failure_info { ApplicationFailureInfo application_failure_info = 5; TimeoutFailureInfo timeout_failure_info = 6; CanceledFailureInfo canceled_failure_info = 7; TerminatedFailureInfo terminated_failure_info = 8; ServerFailureInfo server_failure_info = 9; ResetWorkflowFailureInfo reset_workflow_failure_info = 10; ActivityFailureInfo activity_failure_info = 11; ChildWorkflowExecutionFailureInfo child_workflow_execution_failure_info = 12; } } message ActivityType { string name = 1; } enum TimeoutType { TIMEOUT_TYPE_UNSPECIFIED = 0; TIMEOUT_TYPE_START_TO_CLOSE = 1; TIMEOUT_TYPE_SCHEDULE_TO_START = 2; TIMEOUT_TYPE_SCHEDULE_TO_CLOSE = 3; TIMEOUT_TYPE_HEARTBEAT = 4; } enum StartChildWorkflowExecutionFailedCause { START_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_UNSPECIFIED = 0; START_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_EXISTS = 1; } enum CancelExternalWorkflowExecutionFailedCause { CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_UNSPECIFIED = 0; CANCEL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_EXTERNAL_WORKFLOW_EXECUTION_NOT_FOUND = 1; } enum SignalExternalWorkflowExecutionFailedCause { SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_UNSPECIFIED = 0; SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_EXTERNAL_WORKFLOW_EXECUTION_NOT_FOUND = 1; } message SearchAttributes { map indexed_fields = 1; } message Memo { map fields = 1; } message Header { map fields = 1; } message RetryPolicy { // Interval of the first retry. If retryBackoffCoefficient is 1.0 then it is used for all retries. google.protobuf.Duration initial_interval = 1 [(gogoproto.stdduration) = true]; // Coefficient used to calculate the next retry interval. // The next retry interval is previous interval multiplied by the coefficient. // Must be 1 or larger. double backoff_coefficient = 2; // Maximum interval between retries. Exponential backoff leads to interval increase. // This value is the cap of the increase. Default is 100x of the initial interval. google.protobuf.Duration maximum_interval = 3 [(gogoproto.stdduration) = true]; // Maximum number of attempts. When exceeded the retries stop even if not expired yet. // 1 disables retries. 0 means unlimited (up to the timeouts) int32 maximum_attempts = 4; // Non-Retryable errors types. Will stop retrying if error type matches this list. repeated string non_retryable_error_types = 5; } enum WorkflowIdReusePolicy { WORKFLOW_ID_REUSE_POLICY_UNSPECIFIED = 0; // Allow start a workflow execution using the same workflow Id, when workflow not running. WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE = 1; // Allow start a workflow execution using the same workflow Id, when workflow not running, and the last execution close state is in // [terminated, cancelled, timed out, failed]. WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY = 2; // Do not allow start a workflow execution using the same workflow Id at all. WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE = 3; } enum ParentClosePolicy { PARENT_CLOSE_POLICY_UNSPECIFIED = 0; // Terminate means terminating the child workflow. PARENT_CLOSE_POLICY_TERMINATE = 1; // Abandon means not doing anything on the child workflow. PARENT_CLOSE_POLICY_ABANDON = 2; // Cancel means requesting cancellation on the child workflow. PARENT_CLOSE_POLICY_REQUEST_CANCEL = 3; } message TaskList { string name = 1; // Default: TASK_LIST_KIND_NORMAL. TaskListKind kind = 2; } message TaskListMetadata { google.protobuf.DoubleValue max_tasks_per_second = 1; } message TaskListStatus { int64 backlog_count_hint = 1; int64 read_level = 2; int64 ack_level = 3; double rate_per_second = 4; TaskIdBlock task_id_block = 5; } message TaskIdBlock { int64 start_id = 1; int64 end_id = 2; } message TaskListPartitionMetadata { string key = 1; string owner_host_name = 2; } message PollerInfo { // Unix Nano google.protobuf.Timestamp last_access_time = 1 [(gogoproto.stdtime) = true]; string identity = 2; double rate_per_second = 3; } message StickyExecutionAttributes { TaskList worker_task_list = 1; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" is used to indicate interval. --) google.protobuf.Duration schedule_to_start_timeout = 2 [(gogoproto.stdduration) = true]; } enum TaskListKind { TASK_LIST_KIND_UNSPECIFIED = 0; TASK_LIST_KIND_NORMAL = 1; TASK_LIST_KIND_STICKY = 2; } enum TaskListType { TASK_LIST_TYPE_UNSPECIFIED = 0; // Workflow type of task list. TASK_LIST_TYPE_WORKFLOW = 1; // Activity type of task list. TASK_LIST_TYPE_ACTIVITY = 2; } enum DecisionTaskFailedCause { DECISION_TASK_FAILED_CAUSE_UNSPECIFIED = 0; DECISION_TASK_FAILED_CAUSE_UNHANDLED_COMMAND = 1; DECISION_TASK_FAILED_CAUSE_BAD_SCHEDULE_ACTIVITY_ATTRIBUTES = 2; DECISION_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES = 3; DECISION_TASK_FAILED_CAUSE_BAD_START_TIMER_ATTRIBUTES = 4; DECISION_TASK_FAILED_CAUSE_BAD_CANCEL_TIMER_ATTRIBUTES = 5; DECISION_TASK_FAILED_CAUSE_BAD_RECORD_MARKER_ATTRIBUTES = 6; DECISION_TASK_FAILED_CAUSE_BAD_COMPLETE_WORKFLOW_EXECUTION_ATTRIBUTES = 7; DECISION_TASK_FAILED_CAUSE_BAD_FAIL_WORKFLOW_EXECUTION_ATTRIBUTES = 8; DECISION_TASK_FAILED_CAUSE_BAD_CANCEL_WORKFLOW_EXECUTION_ATTRIBUTES = 9; DECISION_TASK_FAILED_CAUSE_BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES = 10; DECISION_TASK_FAILED_CAUSE_BAD_CONTINUE_AS_NEW_ATTRIBUTES = 11; DECISION_TASK_FAILED_CAUSE_START_TIMER_DUPLICATE_ID = 12; DECISION_TASK_FAILED_CAUSE_RESET_STICKY_TASK_LIST = 13; DECISION_TASK_FAILED_CAUSE_WORKFLOW_WORKER_UNHANDLED_FAILURE = 14; DECISION_TASK_FAILED_CAUSE_BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES = 15; DECISION_TASK_FAILED_CAUSE_BAD_START_CHILD_EXECUTION_ATTRIBUTES = 16; DECISION_TASK_FAILED_CAUSE_FORCE_CLOSE_COMMAND = 17; DECISION_TASK_FAILED_CAUSE_FAILOVER_CLOSE_COMMAND = 18; DECISION_TASK_FAILED_CAUSE_BAD_SIGNAL_INPUT_SIZE = 19; DECISION_TASK_FAILED_CAUSE_RESET_WORKFLOW = 20; DECISION_TASK_FAILED_CAUSE_BAD_BINARY = 21; DECISION_TASK_FAILED_CAUSE_SCHEDULE_ACTIVITY_DUPLICATE_ID = 22; DECISION_TASK_FAILED_CAUSE_BAD_SEARCH_ATTRIBUTES = 23; } enum ContinueAsNewInitiator { CONTINUE_AS_NEW_INITIATOR_UNSPECIFIED = 0; CONTINUE_AS_NEW_INITIATOR_WORKFLOW = 1; CONTINUE_AS_NEW_INITIATOR_RETRY = 2; CONTINUE_AS_NEW_INITIATOR_CRON_SCHEDULE = 3; } message ResetPoints { repeated ResetPointInfo points = 1; } message ResetPointInfo { string binary_checksum = 1; string run_id = 2; int64 first_decision_task_completed_id = 3; google.protobuf.Timestamp create_time = 4 [(gogoproto.stdtime) = true]; // (-- api-linter: core::0214::resource-expiry=disabled // aip.dev/not-precedent: TTL is not defined for ResetPointInfo. --) // The time that the run is deleted due to retention. google.protobuf.Timestamp expire_time = 5 [(gogoproto.stdtime) = true]; // false if the reset point has pending childWFs/reqCancels/signalExternals. bool resettable = 6; } message ChildExecutionInfo { int64 version = 1; int64 initiated_event_batch_id = 2; int64 started_id = 3; HistoryEvent initiated_event = 4; string started_workflow_id = 5; string started_run_id = 6; HistoryEvent started_event = 7; string create_request_id = 8; string domain = 9; string workflow_type_name = 10; ParentClosePolicy parent_close_policy = 11; int64 initiated_id = 12; } message WorkflowExecutionState { string create_request_id = 1; string run_id = 2; WorkflowExecutionState state = 3; WorkflowExecutionStatus status = 4; } message WorkflowExecutionInfo { string domain_id = 1; string workflow_id = 2; string parent_domain_id = 3; string parent_workflow_id = 4; string parent_run_id = 5; int64 initiated_id = 6; int64 completion_event_batch_id = 7; HistoryEvent completion_event = 8; string task_list = 9; string workflow_type_name = 10; google.protobuf.Duration workflow_execution_timeout = 11 [(gogoproto.stdduration) = true]; google.protobuf.Duration workflow_run_timeout = 12 [(gogoproto.stdduration) = true]; google.protobuf.Duration default_decision_task_timeout = 13 [(gogoproto.stdduration) = true]; int64 start_version = 14; ReplicationData replication_data = 16; int64 last_event_task_id = 17; int64 last_first_event_id = 18; int64 last_processed_event = 19; google.protobuf.Timestamp start_time = 20 [(gogoproto.stdtime) = true]; google.protobuf.Timestamp last_update_time = 21 [(gogoproto.stdtime) = true]; int64 decision_task_version = 22; int64 decision_task_schedule_id = 23; int64 decision_task_started_id = 24; google.protobuf.Duration decision_task_timeout = 25 [(gogoproto.stdduration) = true]; int32 decision_task_attempt = 26; google.protobuf.Timestamp decision_task_started_time = 27 [(gogoproto.stdtime) = true]; google.protobuf.Timestamp decision_task_scheduled_time = 28 [(gogoproto.stdtime) = true]; bool cancel_requested = 29; google.protobuf.Timestamp decision_task_original_scheduled_time = 30 [(gogoproto.stdtime) = true]; string decision_task_request_id = 31; string cancel_request_id = 32; string sticky_task_list = 33; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "to" is used to indicate interval. --) google.protobuf.Duration sticky_schedule_to_start_timeout = 34 [(gogoproto.stdduration) = true]; int32 retry_attempt = 35; google.protobuf.Duration retry_initial_interval = 36 [(gogoproto.stdduration) = true]; google.protobuf.Duration retry_maximum_interval = 37 [(gogoproto.stdduration) = true]; int32 retry_maximum_attempts = 38; double retry_backoff_coefficient = 39; google.protobuf.Timestamp retry_expiration_time = 40 [(gogoproto.stdtime) = true]; repeated string retry_non_retryable_error_types = 41; bool has_retry_policy = 42; string cron_schedule = 43; int32 event_store_version = 44; bytes event_branch_token = 45; int64 signal_count = 46; int64 history_size = 47; string client_library_version = 48; string client_feature_version = 49; string client_impl = 50; ResetPoints auto_reset_points = 51; map search_attributes = 52; map memo = 53; VersionHistories version_histories = 54; string first_execution_run_id = 55; } // (-- api-linter: core::0216::synonyms=disabled // aip.dev/not-precedent: There is WorkflowExecutionState already in another package. --) enum WorkflowExecutionStatus { WORKFLOW_EXECUTION_STATUS_UNSPECIFIED = 0; // Value 1 is hardcoded in SQL persistence. WORKFLOW_EXECUTION_STATUS_RUNNING = 1; WORKFLOW_EXECUTION_STATUS_COMPLETED = 2; WORKFLOW_EXECUTION_STATUS_FAILED = 3; WORKFLOW_EXECUTION_STATUS_CANCELED = 4; WORKFLOW_EXECUTION_STATUS_TERMINATED = 5; WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW = 6; WORKFLOW_EXECUTION_STATUS_TIMED_OUT = 7; } // ReplicationData represents mutable state information for global domains. // This information is used by replication protocol when applying events from remote clusters // only used in cassandra message ReplicationData { int64 last_write_event_id = 1; map last_replication_info = 2; } message ReplicationInfo { int64 version = 1; int64 last_event_id = 2; } // VersionHistory contains the version history of a branch. message VersionHistory { bytes branch_token = 1; repeated VersionHistoryItem items = 2; } // VersionHistories contains all version histories from all branches. message VersionHistories { int32 current_version_history_index = 1; repeated VersionHistory histories = 2; } // VersionHistoryItem contains signal eventId and the corresponding version. message VersionHistoryItem { int64 event_id = 1; int64 version = 2; } message HistoryTreeInfo { HistoryBranch branch_info = 1; // For fork operation to prevent race condition of leaking event data when forking branches fail. Also can be used for clean up leaked data. google.protobuf.Timestamp fork_time = 2 [(gogoproto.stdtime) = true]; // For lookup back to workflow during debugging, also background cleanup when fork operation cannot finish self cleanup due to crash. string info = 3; } // For history persistence to serialize/deserialize branch details. message HistoryBranch { string tree_id = 1; string branch_id = 2; repeated HistoryBranchRange ancestors = 3; } // HistoryBranchRange represents a piece of range for a branch. message HistoryBranchRange { // BranchId of original branch forked from. string branch_id = 1; // Beginning node for the range, inclusive. int64 begin_node_id = 2; // Ending node for the range, exclusive. int64 end_node_id = 3; } message ReplicationTask { ReplicationTaskType task_type = 1; int64 source_task_id = 2; oneof attributes { DomainTaskAttributes domain_task_attributes = 3; // TODO: deprecate once NDC migration is done. HistoryTaskAttributes history_task_attributes = 4; SyncShardStatusTaskAttributes sync_shard_status_task_attributes = 5; SyncActivityTaskAttributes sync_activity_task_attributes = 6; // TODO: deprecate once kafka deprecation is done. HistoryMetadataTaskAttributes history_metadata_task_attributes = 7; HistoryTaskV2Attributes history_task_v2_attributes = 8; } } enum ReplicationTaskType { REPLICATION_TASK_TYPE_UNSPECIFIED = 0; REPLICATION_TASK_TYPE_DOMAIN_TASK = 1; REPLICATION_TASK_TYPE_HISTORY_TASK = 2; REPLICATION_TASK_TYPE_SYNC_SHARD_STATUS_TASK = 3; REPLICATION_TASK_TYPE_SYNC_ACTIVITY_TASK = 4; REPLICATION_TASK_TYPE_HISTORY_METADATA_TASK = 5; REPLICATION_TASK_TYPE_HISTORY_V2_TASK = 6; } message DomainTaskAttributes { DomainOperation domain_operation = 1; string id = 2; DomainInfo info = 3; DomainConfig config = 4; DomainReplicationConfig replication_config = 5; int64 config_version = 6; int64 failover_version = 7; } enum DomainOperation { DOMAIN_OPERATION_UNSPECIFIED = 0; DOMAIN_OPERATION_CREATE = 1; DOMAIN_OPERATION_UPDATE = 2; DOMAIN_OPERATION_DELETE = 3; } message HistoryTaskAttributes { repeated string target_clusters = 1; string domain_id = 2; string workflow_id = 3; string run_id = 4; int64 first_event_id = 5; int64 next_event_id = 6; int64 version = 7; History history = 9; History new_run_history = 10; int32 new_run_event_store_version = 12; } message History { repeated HistoryEvent events = 1; } message SyncShardStatusTaskAttributes { string source_cluster = 1; int64 shard_id = 2; google.protobuf.Timestamp status_time = 3 [(gogoproto.stdtime) = true]; } message SyncActivityTaskAttributes { string domain_id = 1; string workflow_id = 2; string run_id = 3; int64 version = 4; int64 scheduled_id = 5; google.protobuf.Timestamp scheduled_time = 6 [(gogoproto.stdtime) = true]; int64 started_id = 7; google.protobuf.Timestamp started_time = 8 [(gogoproto.stdtime) = true]; google.protobuf.Timestamp last_heartbeat_time = 9 [(gogoproto.stdtime) = true]; Payloads details = 10; int32 attempt = 11; Failure last_failure = 12; string last_worker_identity = 13; VersionHistory version_history = 14; } message HistoryMetadataTaskAttributes { repeated string target_clusters = 1; string domain_id = 2; string workflow_id = 3; string run_id = 4; int64 first_event_id = 5; int64 next_event_id = 6; int64 version = 7; } message HistoryTaskV2Attributes { int64 task_id = 1; string domain_id = 2; string workflow_id = 3; string run_id = 4; repeated VersionHistoryItem version_history_items = 5; DataBlob events = 6; // New run events does not need version history since there is no prior events. DataBlob new_run_events = 7; } message DataBlob { EncodingType encoding_type = 1; bytes data = 2; } enum EncodingType { ENCODING_TYPE_UNSPECIFIED = 0; ENCODING_TYPE_PROTO3 = 1; ENCODING_TYPE_JSON = 2; } message QueueMetadataInfo { map clusterAckLevels = 1; } message ReplicationTaskInfo { string domain_id = 1; string workflow_id = 2; string run_id = 3; TaskType task_type = 4; int64 version = 5; int64 first_event_id = 6; int64 next_event_id = 7; int64 scheduled_id = 8; int32 event_store_version = 9; int32 new_run_event_store_version = 10; bytes branch_token = 11; bytes new_run_branch_token = 13; int64 task_id = 15; } enum TaskType { TASK_TYPE_UNSPECIFIED = 0; TASK_TYPE_REPLICATION_HISTORY = 1; TASK_TYPE_REPLICATION_SYNC_ACTIVITY = 2; TASK_TYPE_TRANSFER_DECISION_TASK = 3; TASK_TYPE_TRANSFER_ACTIVITY_TASK = 4; TASK_TYPE_TRANSFER_CLOSE_EXECUTION = 5; TASK_TYPE_TRANSFER_CANCEL_EXECUTION = 6; TASK_TYPE_TRANSFER_START_CHILD_EXECUTION = 7; TASK_TYPE_TRANSFER_SIGNAL_EXECUTION = 8; TASK_TYPE_TRANSFER_RECORD_WORKFLOW_STARTED = 9; TASK_TYPE_TRANSFER_RESET_WORKFLOW = 10; TASK_TYPE_TRANSFER_UPSERT_WORKFLOW_SEARCH_ATTRIBUTES = 11; TASK_TYPE_DECISION_TASK_TIMEOUT = 12; TASK_TYPE_ACTIVITY_TIMEOUT = 13; TASK_TYPE_USER_TIMER = 14; TASK_TYPE_WORKFLOW_RUN_TIMEOUT = 15; TASK_TYPE_DELETE_HISTORY_EVENT = 16; TASK_TYPE_ACTIVITY_RETRY_TIMER = 17; TASK_TYPE_WORKFLOW_BACKOFF_TIMER = 18; } message RequestCancelInfo { int64 version = 1; int64 initiated_event_batch_id = 2; string cancel_request_id = 3; int64 initiated_id = 4; } message ShardInfo { int32 shard_id = 1; int64 range_id = 2; string owner = 3; int64 replication_ack_level = 4; int64 transfer_ack_level = 5; // (-- api-linter: core::0140::prepositions=disabled // aip.dev/not-precedent: "since" is needed here. --) int32 stolen_since_renew = 6; google.protobuf.Timestamp update_time = 7 [(gogoproto.stdtime) = true]; google.protobuf.Timestamp timer_ack_level_time = 8 [(gogoproto.stdtime) = true]; int64 domain_notification_version = 9; map cluster_transfer_ack_level = 10; map cluster_timer_ack_level = 11 [(gogoproto.stdtime) = true]; map cluster_replication_level = 12; map replication_dlq_ack_level = 13; } message SignalInfo { int64 version = 1; int64 initiated_event_batch_id = 2; string request_id = 3; string name = 4; Payloads input = 5; string control = 6; int64 initiated_id = 7; } message TaskListInfo { string domain_id = 1; string name = 2; TaskListType task_type = 3; TaskListKind kind = 4; int64 ack_level = 5; google.protobuf.Timestamp expiry_time = 6 [(gogoproto.stdtime) = true]; google.protobuf.Timestamp last_update_time = 7 [(gogoproto.stdtime) = true]; } message TaskInfo { string domain_id = 1; string workflow_id = 2; string run_id = 3; int64 schedule_id = 4; google.protobuf.Timestamp create_time = 5 [(gogoproto.stdtime) = true]; google.protobuf.Timestamp expiry_time = 6 [(gogoproto.stdtime) = true]; } message AllocatedTaskInfo { TaskInfo data = 1; int64 task_id = 2; } message TimerInfo { int64 version = 1; int64 started_id = 2; google.protobuf.Timestamp expiry_time = 3 [(gogoproto.stdtime) = true]; int64 task_status = 4; // timerId serves the purpose of indicating whether a timer task is generated for this timer info. string timer_id = 5; } message TimerTaskInfo { string domain_id = 1; string workflow_id = 2; string run_id = 3; TaskType task_type = 4; TimeoutType timeout_type = 5; WorkflowBackoffType workflow_backoff_type = 6; int64 version = 7; int32 schedule_attempt = 8; int64 event_id = 9; int64 task_id = 10; google.protobuf.Timestamp task_timestamp = 11 [(gogoproto.stdtime) = true]; } message TransferTaskInfo { string domain_id = 1; string workflow_id = 2; string run_id = 3; TaskType task_type = 4; string target_domain_id = 5; string target_workflow_id = 6; string target_run_id = 7; string task_list = 8; bool target_child_workflow_only = 9; int64 schedule_id = 10; int64 version = 11; int64 task_id = 12; google.protobuf.Timestamp task_timestamp = 13 [(gogoproto.stdtime) = true]; bool record_visibility = 14; } enum WorkflowBackoffType { WORKFLOW_BACKOFF_TYPE_UNSPECIFIED = 0; WORKFLOW_BACKOFF_TYPE_RETRY = 1; WORKFLOW_BACKOFF_TYPE_CRON = 2; } ================================================ FILE: revive.toml ================================================ # config for https://github.com/mgechev/revive ignoreGeneratedHeader = false severity = "error" confidence = 0.8 errorCode = 1 warningCode = 0 [directive.specify-disable-reason] severity = "error" #### roughly what golint does. probably only disable noisy ones. [rule.blank-imports] [rule.context-as-argument] [rule.context-keys-type] [rule.dot-imports] [rule.error-naming] [rule.error-return] [rule.error-strings] [rule.errorf] # [rule.exported] # disabled due to lack of value / encouraging bad habits [rule.if-return] [rule.increment-decrement] [rule.indent-error-flow] # Disabled because we have 158 packages that need package comments; we could instead add ignore # directives for existing packages and require it for new packages. #[rule.package-comments] [rule.range] [rule.receiver-naming] [rule.time-naming] [rule.unexported-return] [rule.var-declaration] [rule.var-naming] #### higher value stuff # this is basically errcheck, warns on errs that are not checked. # strongly desired, but disabled due to 300 failures (to be tackled incrementally). # [rule.unhandled-error] # general defer gotchas. # # in particular: "recover" warns about unsafe use of recover(). # this has caught bugs that can allow crashes while seemingly safe, and are *extremely* hard to catch in review. # # the arguments are excluding only "call-chain", which would disallow `defer someFn(...)()` which is both useful and in use. [rule.defer] arguments=[["loop","method-call","recover","return", "immediate-recover"]] # string(int) is almost always a bug. # go vet considers this a fatal error, but only in 1.15 or newer, and go.mod currently targets 1.13 [rule.string-of-int] #### added because we currently have zero violations, and they seem decent enough to retain [rule.atomic] # correct use of sync code, important [rule.call-to-gc] # beneficial [rule.constant-logical-expr] # minor code simplifier [rule.identical-branches] # code simplifier / failures are pretty dubious [rule.modifies-parameter] # beneficial [rule.modifies-value-receiver] # probably beneficial, prevents subtle bugs [rule.range-val-address] # beneficial [rule.range-val-in-closure] # beneficial [rule.unconditional-recursion] # probably a good idea [rule.unreachable-code] # code simplifier [rule.waitgroup-by-value] # correct use of sync code, important #### unused utilities # [rule.file-header] # could possibly replace `copyright -verifyOnly`? # [rule.imports-blacklist] # simple way to ban imports / enforce wrappers, likely useful #### disabled but maybe desirable # [rule.bare-return] # probably beneficial as it's slightly error-prone, but 2,000 failures # [rule.bool-literal-in-expr] # minor code simplifier, few failures # [rule.confusing-results] # maybe beneficial, only a few failures # [rule.deep-exit] # probably a good idea in most code, some failures, but not trivial to adopt # [rule.duplicated-imports] # minor, but may be worthwhile. failures are weird but harmless # [rule.early-return] # minor code simplifier, a handful of failures # [rule.get-return] # existing failures are intentional + desirable, but in principle it's a fine idea # [rule.import-shadowing] # probably beneficial, but 750 failures # [rule.redefines-builtin-id] # probably beneficial, few failures # [rule.struct-tag] # probably beneficial, a few failures # [rule.superfluous-else] # minor code simplifier, a few failures # [rule.unexported-naming] # probably beneficial, but 300 failures # [rule.unused-parameter] # minor code simplifier / clarifier, but 250 failures # [rule.unused-receiver] # minor code simplifier / clarifier, but 500 failures #### probably undesirable # [rule.add-constant] # extremely noisy. 18,000 failures, overwhelmingly for tests or 0/1 which seem totally fine # [rule.argument-limit] # too arbitrary # [rule.cognitive-complexity] # dubious value, but possibly interesting # [rule.confusing-naming] # dubious value, ~50 failures # [rule.cyclomatic] # dubious value, but possibly interesting # [rule.empty-block] # easily noticed in code review, but also warns on documented no-op branches, which seem fine # [rule.empty-lines] # low value, many failures # [rule.flag-parameter] # interesting, but very noisy # [rule.function-result-limit] # too arbitrary, easily noticed in code review # [rule.line-length-limit] # too arbitrary # [rule.max-public-structs] # too arbitrary # [rule.unnecessary-stmt] # dubious value ================================================ FILE: schema/cassandra/README.md ================================================ What ---- This directory contains the cassandra schema for every keyspace that cadence owns. The directory structure is as follows ``` ./schema - visibility/ -- Contains schema for visibility data models - cadence/ -- Contains schema for default data models - keyspace.cql -- Contains the keyspace definition - schema.cql -- Contains the latest & greatest snapshot of the schema for the keyspace - versioned - v0.1/ - v0.2/ -- One directory per schema version change - v1.0/ - manifest.json -- json file describing the change - changes.cql -- changes in this version, only [CREATE, ALTER] commands are allowed ``` How --- Q: How do I update existing schema ? * Add your changes to schema.cql * Create a new schema version directory under ./schema/keyspace/versioned/vx.x * Add a manifest.json * Add your changes in a cql file * Update the unit test within ./tools/common/schema/updatetask_test.go `TestReadSchemaDirFromEmbeddings` with your version x.x * Once you are done with these use the ./cadence-cassandra-tool to update the schema Q: What's the format of manifest.json Example below: * MinCompatibleVersion is the minimum schema version that your code can handle * SchemaUpdateCqlFiles are list of .cql files containg your create/alter commands ``` { "CurrVersion": "0.1", "MinCompatibleVersion": "0.1", "Description": "base version of schema", "SchemaUpdateCqlFiles": [ "base.cql" ] } ``` ================================================ FILE: schema/cassandra/cadence/keyspace.cql ================================================ CREATE KEYSPACE IF NOT EXISTS cadence WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1}; ================================================ FILE: schema/cassandra/cadence/schema.cql ================================================ CREATE TYPE shard ( shard_id int, owner text, -- Host identifier processing the shard -- Range identifier used for generating ack ids for tasks within shard. -- Also used for optimistic concurrency and all writes to a shard are conditional on this value. range_id bigint, -- TO BE DEPRECATED, IN FAVOR OF range_id column in executions table -- This field keeps track of number of times owner for a shard changes before updating range_id or ack_levels stolen_since_renew int, updated_at timestamp, replication_ack_level bigint, transfer_ack_level bigint, -- TO BE DEPRECATED, IN FAVOR OF cluster_transfer_ack_level timer_ack_level timestamp, -- TO BE DEPRECATED, IN FAVOR OF cluster_timer_ack_level -- Mapping of cluster to corresponding transfer ack level cluster_transfer_ack_level map, -- TO BE DEPRECATED, IN FAVOR OF transfer_processing_queue_states -- Mapping of cluster to corresponding timer ack level cluster_timer_ack_level map, -- TO BE DEPRECATED, IN FAVOR OF timer_processing_queue_states domain_notification_version bigint, -- the global domain change version this shard is aware of -- Mapping of cluster to corresponding list of transfer queue processing states transfer_processing_queue_states blob, transfer_processing_queue_states_encoding text, -- Mapping of cluster to corresponding list of cross-cluster queue processing states cross_cluster_processing_queue_states blob, cross_cluster_processing_queue_states_encoding text, -- Mapping of cluster to corresponding list of timer queue processing states timer_processing_queue_states blob, timer_processing_queue_states_encoding text, -- Mapping of (remote) cluster to corresponding replication level (last replicated task_id) cluster_replication_level map, -- Mapping of (remote) cluster to corresponding replication DLQ ack level (last replicated task_id) replication_dlq_ack_level map, -- Data blob of pending failover markers pending_failover_markers blob, pending_failover_markers_encoding text ); --- Workflow execution and mutable state --- CREATE TYPE workflow_execution ( domain_id uuid, workflow_id text, run_id uuid, first_run_id uuid, -- Run ID of the first run of a ContinuedAsNew workflow parent_domain_id uuid, -- Domain ID of parent workflow which started the workflow execution parent_workflow_id text, -- ID of parent workflow which started the workflow execution parent_run_id uuid, -- RunID of parent workflow which started the workflow execution initiated_id bigint, -- Initiated event ID of parent workflow which started this execution completion_event_batch_id bigint, completion_event blob, -- Completion event used to communicate result to parent workflow execution completion_event_data_encoding text, -- Protocol used for history serialization task_list text, workflow_type_name text, workflow_timeout int, -- Workflow ExecutionStartToCloseTimeoutSeconds decision_task_timeout int, -- decision start to close timeout execution_context blob, state int, -- enum WorkflowState {Created, Running, Completed} close_status int, -- enum WorkflowCloseStatus {None, Completed, Failed, Canceled, Terminated, ContinuedAsNew, TimedOut} last_processed_event bigint, start_time timestamp, last_updated_time timestamp, create_request_id uuid, decision_version bigint, decision_schedule_id bigint, decision_started_id bigint, decision_request_id text, -- Identifier used by matching engine for retrying history service calls for recording task is started decision_timeout int, decision_attempt bigint, decision_timestamp bigint, -- this is decision started time decision_scheduled_timestamp bigint, -- this is decision scheduled time decision_original_scheduled_timestamp bigint, -- this is scheduled time of the first decision during heartbeat cancel_requested boolean, cancel_request_id text, sticky_task_list text, -- sticky worker task list sticky_schedule_to_start_timeout int, client_library_version text, client_feature_version text, client_impl text, attempt int, -- starting from 0 (for initial non-retry) has_retry_policy boolean,-- If there is a retry policy init_interval int, -- initial retry interval, in seconds backoff_coefficient double, max_interval int, -- max retry interval in seconds expiration_time timestamp, -- retry expiration time max_attempts int, -- max number of attempts including initial non-retry attempt non_retriable_errors list, event_store_version int, -- indicates which version of events persistence is using signal_count int, branch_token blob, history_size bigint, last_first_event_id bigint, next_event_id bigint, cron_schedule text, expiration_seconds int, -- retry expiration duration in seconds last_event_task_id bigint, auto_reset_points blob, -- the resetting points for auto-reset feature auto_reset_points_encoding text, -- encoding for auto_reset_points_data search_attributes map, memo map, partition_config map, cron_overlap_policy int, -- enum CronOverlapPolicy {Skip, BufferOne} task_list_kind int, -- enum TaskListKind {Normal, Sticky, Ephemeral}, active_cluster_selection_policy blob, -- active cluster selection policy applicable to active-active domains active_cluster_selection_policy_encoding text, -- encoding for active_cluster_selection_policy ); -- Replication information for each cluster CREATE TYPE replication_info ( version bigint, last_event_id bigint, ); -- This is used to store replication information for a workflow execution CREATE TYPE replication_state ( current_version bigint, -- current version for domain, incremented on failover start_version bigint, -- version of domain when the workflow execution was started last_write_version bigint, -- version of domain when the last event was written to history last_write_event_id bigint, -- last written event id for a given version last_replication_info map>, -- information about replication events from other clusters ); -- TODO: Remove fields that are left over from activity and workflow tasks. CREATE TYPE transfer_task ( domain_id uuid, -- The domain ID that this transfer task belongs to workflow_id text, -- The workflow ID that this transfer task belongs to run_id uuid, -- The run ID that this transfer task belongs to task_id bigint, visibility_ts timestamp, -- The timestamp when the transfer task is generated target_domain_id uuid, -- The external domain ID that this transfer task is doing work for. target_domain_ids set, -- The external domain ID that this transfer task is doing work for. target_workflow_id text, -- The external workflow ID that this transfer task is doing work for. target_run_id uuid, -- The external run ID that this transfer task is doing work for. target_child_workflow_only boolean, -- The whether target child workflow only. task_list text, type int, -- enum TaskType For local: {Decision, Activity, CloseExecution, CancelExecution, StartChildExecution, SignalExecution, RecordWorkflowStarted, ResetWorkflow, UpsertWorkflowSearchAttributes}, or for crossCluster {StartChildExecution, CancelExecution, SignalExecution} schedule_id bigint, version bigint, -- the failover version when this task is created, used to compare against the mutable state, in case the events got overwritten record_visibility boolean, -- indicates whether or not to create a visibility record original_task_list text, -- the original task list of the task if the task is a sticky decision task original_task_list_kind int, -- enum TaskListKind {Normal, Sticky, Ephemeral}, ); CREATE TYPE replication_task ( domain_id uuid, -- The domain ID that this replication task belongs to workflow_id text, -- The workflow ID that this replication task belongs to run_id uuid, -- The run ID that this replication task belongs to task_id bigint, type int, -- enum TaskType {History, SyncActivity, FailoverMarker} first_event_id bigint, -- Used by ReplicationTask to set the first event ID of the applied transaction next_event_id bigint, -- Used by ReplicationTask to set the next event ID of the applied transaction version bigint, -- Used by ReplicationTask to set the failover version of the applied transaction last_replication_info map>, -- Used by replication task to snapshot replication information when the transaction was applied scheduled_id bigint, -- Used by ReplicationTask to sync activity info event_store_version int, -- indicates which version of event store to query branch_token blob, -- if eventV2, then query with this token new_run_event_store_version int, -- indicates which version of event store to query for new run(continueAsNew) new_run_branch_token blob, -- if eventV2, then query with this token for new run(continueAsNew) reset_workflow boolean, -- whether the task is for resetWorkflowExecution created_time bigint, -- task creation timestamp ); CREATE TYPE timer_task ( domain_id uuid, workflow_id text, run_id uuid, visibility_ts timestamp, task_id bigint, type int, -- enum TaskType {DecisionTaskTimeout, ActivityTaskTimeout, UserTimer} timeout_type int, -- enum TimeoutType in IDL {START_TO_CLOSE, SCHEDULE_TO_START, SCHEDULE_TO_CLOSE, HEARTBEAT} event_id bigint, -- Corresponds to event ID in history that is responsible for this timer. schedule_attempt bigint, -- Used to retry failed decision tasks using mutable state version bigint, -- the failover version when this task is created, used to compare against the mutable state, in case the events got overwritten task_list text, -- the task list associated with the timer task ); -- Workflow activity in progress mutable state CREATE TYPE activity_info ( version bigint, schedule_id bigint, scheduled_event_batch_id bigint, scheduled_event blob, -- deprecated scheduled_time timestamp, started_id bigint, started_event blob, started_time timestamp, activity_id text, -- Client generated unique ID for the activity. request_id text, -- Identifier used by matching engine for retrying history service calls for recording task is started details blob, schedule_to_start_timeout int, schedule_to_close_timeout int, start_to_close_timeout int, heart_beat_timeout int, cancel_requested boolean, -- If a cancel request is made to cancel the activity in progress. cancel_request_id bigint, -- Event ID that identifies the cancel request. last_hb_updated_time timestamp, -- Last time the heartbeat is received. timer_task_status int, -- Indicates whether timers are created for this activity. attempt int, -- starting from 0 (for initial non-retry) task_list text, started_identity text, -- last started poller's identity has_retry_policy boolean,-- If there is a retry policy init_interval int, -- initial retry interval, in seconds backoff_coefficient double, max_interval int, -- max retry interval in seconds expiration_time timestamp, -- retry expiration time max_attempts int, -- max number of attempts including initial non-retry attempt non_retriable_errors list, last_failure_reason text, last_worker_identity text, -- Worker that returns the last failure reason last_failure_details blob, event_data_encoding text, -- Protocol used for history serialization task_list_kind int, -- enum TaskListKind {Normal, Sticky, Ephemeral}, ); -- User timer details CREATE TYPE timer_info ( version bigint, timer_id text, -- User defined timer ID started_id bigint, -- The event ID corresponding to timer started. expiry_time timestamp, -- Timestamp at which this timer expires or fires -- task_id is a misleading variable, it actually serves -- the purpose of indicating whether a timer task is -- generated for this timer info task_id bigint, ); -- Child execution in progress mutable state CREATE TYPE child_execution_info ( version bigint, initiated_id bigint, initiated_event_batch_id bigint, initiated_event blob, started_id bigint, started_workflow_id text, started_run_id uuid, started_event blob, -- deprecated create_request_id uuid, event_data_encoding text, -- Protocol used for history serialization domain_id uuid, domain_name text, -- deprecated workflow_type_name text, parent_close_policy int, ); -- External workflow cancellation in progress mutable state CREATE TYPE request_cancel_info ( version bigint, initiated_event_batch_id bigint, initiated_id bigint, cancel_request_id text, ); -- External workflow signal in progress mutable state CREATE TYPE signal_info ( version bigint, initiated_event_batch_id bigint, initiated_id bigint, signal_request_id uuid, signal_name text, input blob, control blob, ); -- Activity or workflow task in a task list CREATE TYPE task ( domain_id uuid, workflow_id text, run_id uuid, schedule_id bigint, created_time timestamp, partition_config map ); CREATE TYPE task_list_partition ( isolation_groups set ); CREATE TYPE task_list_partition_config ( version bigint, num_read_partitions int, num_write_partitions int, read_partitions map>, write_partitions map> ); CREATE TYPE task_list ( domain_id uuid, name text, type int, -- enum TaskRowType {ActivityTask, DecisionTask} ack_level bigint, -- task_id of the last acknowledged message kind int, -- enum TaskListKind {Normal, Sticky} last_updated timestamp, adaptive_partition_config frozen ); CREATE TYPE domain ( id uuid, name text, status int, -- enum DomainStatus {Registered, Deprecated, Deleted} description text, data map, -- Used for customized domain information, key values pair owner_email text, ); CREATE TYPE domain_config ( retention int, emit_metric boolean, archival_bucket text, -- deprecated, use the two uri fields below archival_status int, -- deprecated, use the two status fields below history_archival_status int, history_archival_uri text, visibility_archival_status int, visibility_archival_uri text, bad_binaries blob, bad_binaries_encoding blob, isolation_groups blob, isolation_groups_encoding text, async_workflow_config blob, async_workflow_config_encoding text, ); CREATE TYPE cluster_replication_config ( cluster_name text, ); CREATE TYPE domain_replication_config ( active_cluster_name text, clusters list>, active_clusters_config blob, active_clusters_config_encoding text, ); CREATE TYPE serialized_event_batch ( encoding_type text, version int, data blob, ); -- Storage for out of order replication tasks for an execution CREATE TYPE buffered_replication_task_info ( first_event_id bigint, next_event_id bigint, version bigint, history frozen, new_run_history frozen, event_store_version int, -- indicates which version of event store to query new_run_event_store_version int, -- indicates which version of event store to query for new run(continueAsNew) ); -- for history v2 events CREATE TYPE branch_range ( branch_id uuid, end_node_id bigint, -- exclusive node_id to represent the stopping point for this range ); CREATE TYPE checksum ( version int, -- version of the payload used to generate checksum flavor int, -- type of checksum e.g crc32OverThrift value blob, -- checksum bytes ); CREATE TABLE executions ( shard_id int, type int, -- enum RowType { Shard, Execution, TransferTask, TimerTask, ReplicationTask, CrossClusterTask} domain_id uuid, workflow_id text, run_id uuid, current_run_id uuid, visibility_ts timestamp, -- unique identifier for timer tasks for an execution task_id bigint, -- unique identifier for transfer and timer tasks for an execution data blob, -- this column will be used by Shard, Execution, TransferTask, TimerTask, ReplicationTask row types data_encoding text, shard frozen, execution frozen, transfer frozen, cross_cluster frozen, -- reuse the transfer_task type replication frozen, timer frozen, next_event_id bigint, -- This is needed to make conditional updates on session history range_id bigint, -- Increasing sequence identifier for transfer queue, checkpointed into shard info activity_map map>, timer_map map>, child_executions_map map>, request_cancel_map map>, signal_map map>, signal_requested set, buffered_events_list list>, replication_state frozen, -- Replication information part of mutable state buffered_replication_tasks_map map>, workflow_last_write_version bigint, workflow_state int, version_histories blob, -- the metadata of history branching version_histories_encoding text, checksum frozen, created_time timestamp, last_updated_time timestamp, PRIMARY KEY (shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE history_node ( tree_id uuid, branch_id uuid, node_id bigint, -- node_id: first eventID in a batch of events txn_id bigint, -- for override the same node_id: bigger txn_id wins data blob, -- Batch of workflow execution history events as a blob data_encoding text, -- Protocol used for history serialization created_time timestamp, PRIMARY KEY ((tree_id), branch_id, node_id, txn_id ) ) WITH CLUSTERING ORDER BY (branch_id ASC, node_id ASC, txn_id DESC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE history_tree ( tree_id uuid, branch_id uuid, ancestors list>, fork_time timestamp, -- For fork operation to prevent race condition to leak event data when forking branches info text, -- For background cleanup when fork operation cannot finish self cleanup due to crash created_time timestamp, PRIMARY KEY ((tree_id), branch_id ) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; -- Stores activity or workflow tasks CREATE TABLE tasks ( domain_id uuid, task_list_name text, task_list_type int, -- enum TaskListType {ActivityTask, DecisionTask} type int, -- enum rowType {Task, TaskList} task_id bigint, -- unique identifier for tasks, monotonically increasing range_id bigint, -- Used to ensure that only one process can write to the table task frozen, task_list frozen, created_time timestamp, last_updated_time timestamp, PRIMARY KEY ((domain_id, task_list_name, task_list_type), type, task_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; -- this table is only used for storage of mapping of domain uuid to domain name CREATE TABLE domains ( id uuid, domain frozen, config frozen, created_time timestamp, PRIMARY KEY (id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE domains_by_name_v2 ( domains_partition int, name text, domain frozen, config frozen, replication_config frozen, -- indicating active cluster and standby cluster used for replication is_global_domain boolean, -- indicating whether a domain is a global domain config_version bigint, -- indicating the version of domain config, excluding the failover / change of active cluster name failover_version bigint, -- indicating the version of active domain only, used for domain failover failover_notification_version bigint, -- indicating the last change related to domain failover previous_failover_version bigint, -- indicating the previouse failover version for graceful failover failover_end_time bigint, -- indicating domain failover state last_updated_time bigint, -- indicating the domain last update timestamp notification_version bigint, created_time timestamp, PRIMARY KEY (domains_partition, name) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; INSERT INTO domains_by_name_v2 ( domains_partition, name, domain, config, is_global_domain, config_version, failover_version, failover_notification_version, notification_version ) VALUES ( 0, 'cadence-system', { id: 32049b68-7872-4094-8e63-d0dd59896a83, name: 'cadence-system', description: 'cadence system workflow domain', owner_email: 'cadence-dev-group@uber.com' }, { retention:3, emit_metric:False }, False, 0, -24, -1, -1 ) IF NOT EXISTS; INSERT INTO domains ( id, domain ) VALUES ( 32049b68-7872-4094-8e63-d0dd59896a83, { name: 'cadence-system' } ) IF NOT EXISTS; CREATE TABLE queue ( queue_type int, message_id bigint, message_payload blob, created_time timestamp, PRIMARY KEY (queue_type, message_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE queue_metadata ( queue_type int, cluster_ack_level map, version bigint, created_time timestamp, last_updated_time timestamp, PRIMARY KEY (queue_type) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE cluster_config ( row_type int, version int, timestamp timestamp, values blob, encoding text, PRIMARY KEY (row_type, version) ) WITH CLUSTERING ORDER BY (version DESC); CREATE TABLE domain_audit_log ( domain_id uuid, event_id uuid, -- event_id is the unique identifier for this change to the domain state_before blob, -- state_before stores the domain state before the request state_before_encoding text, -- the encoding type used for state_before state_after blob, -- state_after stores the domain state after the request state_after_encoding text, -- the encoding type used for state_after operation_type int, -- operation_type stores the type of operation that was performed. It is deserialized as an enum and can be used to customize the serialization/deserialization strategy. created_time timestamp, -- created_time the time this row was inserted last_updated_time timestamp, identity text, -- the unique identifier of the user that made the change identity_type text, -- identity_type can be used to delineate between service, user, or other identities comment text, -- comment can be used when manual updates or creates are performed on the database as an audit trail PRIMARY KEY ((domain_id, operation_type), created_time, event_id) ) WITH CLUSTERING ORDER BY (created_time DESC, event_id ASC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; ================================================ FILE: schema/cassandra/cadence/versioned/s0.0-0.23/data_0_23.cql ================================================ INSERT INTO domains_by_name_v2 ( domains_partition, name, domain, config, is_global_domain, config_version, failover_version, failover_notification_version, notification_version ) VALUES ( 0, 'cadence-system', { id: 32049b68-7872-4094-8e63-d0dd59896a83, name: 'cadence-system', description: 'cadence system workflow domain', owner_email: 'cadence-dev-group@uber.com' }, { retention:3, emit_metric:False }, False, 0, -24, -1, -1 ) IF NOT EXISTS; INSERT INTO domains ( id, domain ) VALUES ( 32049b68-7872-4094-8e63-d0dd59896a83, { name: 'cadence-system' } ) IF NOT EXISTS; ================================================ FILE: schema/cassandra/cadence/versioned/s0.0-0.23/manifest.json ================================================ { "CurrVersion": "0.23", "MinCompatibleVersion": "0.23", "Description": "Squashed 0.0 -> 0.23 schema", "SchemaUpdateCqlFiles": [ "schema_0_23.cql", "data_0_23.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/s0.0-0.23/schema_0_23.cql ================================================ CREATE TYPE shard ( shard_id int, owner text, -- Host identifier processing the shard -- Range identifier used for generating ack ids for tasks within shard. -- Also used for optimistic concurrency and all writes to a shard are conditional on this value. range_id bigint, -- This field keeps track of number of times owner for a shard changes before updating range_id or ack_levels stolen_since_renew int, updated_at timestamp, transfer_ack_level bigint, -- TO BE DEPRECATED, IN FAVOR OF cluster_transfer_ack_level timer_ack_level timestamp, -- TO BE DEPRECATED, IN FAVOR OF cluster_timer_ack_level replication_ack_level bigint, -- Mapping of cluster to corresponding transfer ack level cluster_transfer_ack_level map, -- Mapping of cluster to corresponding timer ack level cluster_timer_ack_level map, domain_notification_version bigint, -- the global domain change version this shard is aware of -- Mapping of (remote) cluster to corresponding replication level (last replicated task_id) cluster_replication_level map, ); --- Workflow execution and mutable state --- CREATE TYPE workflow_execution ( domain_id uuid, workflow_id text, run_id uuid, parent_domain_id uuid, -- Domain ID of parent workflow which started the workflow execution parent_workflow_id text, -- ID of parent workflow which started the workflow execution parent_run_id uuid, -- RunID of parent workflow which started the workflow execution initiated_id bigint, -- Initiated event ID of parent workflow which started this execution completion_event blob, -- Completion event used to communicate result to parent workflow execution task_list text, workflow_type_name text, decision_task_timeout int, -- decision start to close timeout execution_context blob, state int, -- enum WorkflowState {Created, Running, Completed} close_status int, -- enum WorkflowCloseStatus {None, Completed, Failed, Canceled, Terminated, ContinuedAsNew, TimedOut} next_event_id bigint, last_processed_event bigint, start_time timestamp, last_updated_time timestamp, create_request_id uuid, decision_schedule_id bigint, decision_started_id bigint, decision_request_id text, -- Identifier used by matching engine for retrying history service calls for recording task is started decision_timeout int, cancel_requested boolean, cancel_request_id text, workflow_timeout int, -- Workflow ExecutionStartToCloseTimeoutSeconds sticky_task_list text, -- sticky worker task list sticky_schedule_to_start_timeout int, decision_attempt bigint, decision_timestamp bigint, -- this is decision started time client_library_version text, client_feature_version text, client_impl text, last_first_event_id bigint, decision_version bigint, attempt int, -- starting from 0 (for initial non-retry) has_retry_policy boolean,-- If there is a retry policy init_interval int, -- initial retry interval, in seconds backoff_coefficient double, max_interval int, -- max retry interval in seconds expiration_time timestamp, -- retry expiration time max_attempts int, -- max number of attempts including initial non-retry attempt non_retriable_errors list, history_size bigint, completion_event_data_encoding text, -- Protocol used for history serialization event_store_version int, -- indicates which version of events persistence is using branch_token blob, signal_count int, cron_schedule text, expiration_seconds int, -- retry expiration duration in seconds completion_event_batch_id bigint, last_event_task_id bigint, auto_reset_points blob, -- the resetting points for auto-reset feature auto_reset_points_encoding text, -- encoding for auto_reset_points_data decision_scheduled_timestamp bigint, -- this is decision scheduled time search_attributes map, memo map, decision_original_scheduled_timestamp bigint, -- this is scheduled time of the first decision during heartbeat ); -- Replication information for each cluster CREATE TYPE replication_info ( version bigint, last_event_id bigint, ); -- This is used to store replication information for a workflow execution CREATE TYPE replication_state ( current_version bigint, -- current version for domain, incremented on failover start_version bigint, -- version of domain when the workflow execution was started last_write_version bigint, -- version of domain when the last event was written to history last_write_event_id bigint, -- last written event id for a given version last_replication_info map>, -- information about replication events from other clusters ); -- TODO: Remove fields that are left over from activity and workflow tasks. CREATE TYPE transfer_task ( domain_id uuid, -- The domain ID that this transfer task belongs to workflow_id text, -- The workflow ID that this transfer task belongs to run_id uuid, -- The run ID that this transfer task belongs to task_id bigint, target_domain_id uuid, -- The external domain ID that this transfer task is doing work for. target_workflow_id text, -- The external workflow ID that this transfer task is doing work for. target_run_id uuid, -- The external run ID that this transfer task is doing work for. task_list text, type int, -- enum TaskType {ActivityTask, DecisionTask, DeleteExecution, CancelExecution, StartChildExecution, ReplicationTask} schedule_id bigint, target_child_workflow_only boolean, -- The whether target child workflow only. version bigint, -- the failover version when this task is created, used to compare against the mutable state, in case the events got overwritten visibility_ts timestamp, -- The timestamp when the transfer task is generated record_visibility boolean, -- indicates whether or not to create a visibility record ); CREATE TYPE replication_task ( domain_id uuid, -- The domain ID that this replication task belongs to workflow_id text, -- The workflow ID that this replication task belongs to run_id uuid, -- The run ID that this replication task belongs to task_id bigint, type int, -- enum TaskType {History, Heartbeat} first_event_id bigint, -- Used by ReplicationTask to set the first event ID of the applied transaction next_event_id bigint, -- Used by ReplicationTask to set the next event ID of the applied transaction version bigint, -- Used by ReplicationTask to set the failover version of the applied transaction last_replication_info map>, -- Used by replication task to snapshot replication information when the transaction was applied scheduled_id bigint, -- Used by ReplicationTask to sync activity info event_store_version int, -- indicates which version of event store to query branch_token blob, -- if eventV2, then query with this token new_run_event_store_version int, -- indicates which version of event store to query for new run(continueAsNew) new_run_branch_token blob, -- if eventV2, then query with this token for new run(continueAsNew) reset_workflow boolean, -- whether the task is for resetWorkflowExecution ); CREATE TYPE timer_task ( domain_id uuid, workflow_id text, run_id uuid, visibility_ts timestamp, task_id bigint, type int, -- enum TaskType {DecisionTaskTimeout, ActivityTaskTimeout, UserTimer} timeout_type int, -- enum TimeoutType in IDL {START_TO_CLOSE, SCHEDULE_TO_START, SCHEDULE_TO_CLOSE, HEARTBEAT} event_id bigint, -- Corresponds to event ID in history that is responsible for this timer. schedule_attempt bigint, -- Used to retry failed decision tasks using mutable state version bigint, -- the failover version when this task is created, used to compare against the mutable state, in case the events got overwritten ); -- Workflow activity in progress mutable state CREATE TYPE activity_info ( schedule_id bigint, scheduled_event blob, -- deprecated scheduled_time timestamp, started_id bigint, started_event blob, started_time timestamp, activity_id text, -- Client generated unique ID for the activity. request_id text, -- Identifier used by matching engine for retrying history service calls for recording task is started details blob, schedule_to_start_timeout int, schedule_to_close_timeout int, start_to_close_timeout int, heart_beat_timeout int, cancel_requested boolean, -- If a cancel request is made to cancel the activity in progress. cancel_request_id bigint, -- Event ID that identifies the cancel request. last_hb_updated_time timestamp, -- Last time the heartbeat is received. timer_task_status int, -- Indicates whether timers are created for this activity. version bigint, attempt int, -- starting from 0 (for initial non-retry) task_list text, started_identity text, -- last started poller's identity has_retry_policy boolean,-- If there is a retry policy init_interval int, -- initial retry interval, in seconds backoff_coefficient double, max_interval int, -- max retry interval in seconds expiration_time timestamp, -- retry expiration time max_attempts int, -- max number of attempts including initial non-retry attempt non_retriable_errors list, event_data_encoding text, -- Protocol used for history serialization scheduled_event_batch_id bigint, last_failure_reason text, last_worker_identity text, -- Worker that returns the last failure reason last_failure_details blob ); -- User timer details CREATE TYPE timer_info ( timer_id text, -- User defined timer ID started_id bigint, -- The event ID corresponding to timer started. expiry_time timestamp, -- Timestamp at which this timer expires or fires -- task_id is a misleading variable, it actually serves -- the purpose of indicating whether a timer task is -- generated for this timer info task_id bigint, version bigint, ); -- Child execution in progress mutable state CREATE TYPE child_execution_info ( initiated_id bigint, initiated_event blob, started_id bigint, started_event blob, -- deprecated create_request_id uuid, version bigint, event_data_encoding text, -- Protocol used for history serialization initiated_event_batch_id bigint, started_workflow_id text, started_run_id uuid, domain_name text, workflow_type_name text, parent_close_policy int, ); -- External workflow cancellation in progress mutable state CREATE TYPE request_cancel_info ( initiated_id bigint, cancel_request_id text, version bigint, initiated_event_batch_id bigint, ); -- External workflow signal in progress mutable state CREATE TYPE signal_info ( initiated_id bigint, signal_request_id uuid, signal_name text, input blob, control blob, version bigint, initiated_event_batch_id bigint, ); -- Activity or workflow task in a task list CREATE TYPE task ( domain_id uuid, workflow_id text, run_id uuid, schedule_id bigint, created_time timestamp ); CREATE TYPE task_list ( domain_id uuid, name text, type int, -- enum TaskRowType {ActivityTask, DecisionTask} ack_level bigint, -- task_id of the last acknowledged message kind int, -- enum TaskListKind {Normal, Sticky} last_updated timestamp ); CREATE TYPE domain ( id uuid, name text, status int, -- enum DomainStatus {Registered, Deprecated, Deleted} description text, owner_email text, data map, -- Used for customized domain information, key values pair ); CREATE TYPE domain_config ( retention int, emit_metric boolean, archival_bucket text, -- deprecated, use the two uri fields below archival_status int, -- deprecated, use the two status fields below bad_binaries blob, bad_binaries_encoding text, history_archival_status int, history_archival_uri text, visibility_archival_status int, visibility_archival_uri text ); CREATE TYPE cluster_replication_config ( cluster_name text, ); CREATE TYPE domain_replication_config ( active_cluster_name text, clusters list> ); CREATE TYPE serialized_event_batch ( encoding_type text, version int, data blob, ); -- Storage for out of order replication tasks for an execution CREATE TYPE buffered_replication_task_info ( first_event_id bigint, next_event_id bigint, version bigint, history frozen, new_run_history frozen, event_store_version int, -- indicates which version of event store to query new_run_event_store_version int, -- indicates which version of event store to query for new run(continueAsNew) ); -- for history v2 events CREATE TYPE branch_range ( branch_id uuid, end_node_id bigint, -- exclusive node_id to represent the stopping point for this range ); CREATE TABLE executions ( shard_id int, type int, -- enum RowType { Shard, Execution, TransferTask, TimerTask, ReplicationTask} domain_id uuid, workflow_id text, run_id uuid, current_run_id uuid, visibility_ts timestamp, -- unique identifier for timer tasks for an execution task_id bigint, -- unique identifier for transfer and timer tasks for an execution shard frozen, execution frozen, transfer frozen, replication frozen, timer frozen, next_event_id bigint, -- This is needed to make conditional updates on session history range_id bigint, -- Increasing sequence identifier for transfer queue, checkpointed into shard info activity_map map>, timer_map map>, child_executions_map map>, request_cancel_map map>, signal_map map>, signal_requested set, buffered_events_list list>, replication_state frozen, -- Replication information part of mutable state buffered_replication_tasks_map map>, workflow_last_write_version bigint, workflow_state int, version_histories blob, -- the metadata of history branching version_histories_encoding text, PRIMARY KEY (shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE history_node ( tree_id uuid, branch_id uuid, node_id bigint, -- node_id: first eventID in a batch of events txn_id bigint, -- for override the same node_id: bigger txn_id wins data blob, -- Batch of workflow execution history events as a blob data_encoding text, -- Protocol used for history serialization PRIMARY KEY ((tree_id), branch_id, node_id, txn_id ) ) WITH CLUSTERING ORDER BY (branch_id ASC, node_id ASC, txn_id DESC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE history_tree ( tree_id uuid, branch_id uuid, ancestors list>, fork_time timestamp, -- For fork operation to prevent race condition to leak event data when forking branches info text, -- For background cleanup when fork operation cannot finish self cleanup due to crash PRIMARY KEY ((tree_id), branch_id ) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; -- Stores activity or workflow tasks CREATE TABLE tasks ( domain_id uuid, task_list_name text, task_list_type int, -- enum TaskListType {ActivityTask, DecisionTask} type int, -- enum rowType {Task, TaskList} task_id bigint, -- unique identifier for tasks, monotonically increasing range_id bigint, -- Used to ensure that only one process can write to the table task frozen, task_list frozen, PRIMARY KEY ((domain_id, task_list_name, task_list_type), type, task_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; -- this table is only used for storage of mapping of domain uuid to domain name CREATE TABLE domains ( id uuid, domain frozen, config frozen, PRIMARY KEY (id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE domains_by_name_v2 ( domains_partition int, name text, domain frozen, config frozen, replication_config frozen, -- indicating active cluster and standby cluster used for replication is_global_domain boolean, -- indicating whether a domain is a global domain config_version bigint, -- indicating the version of domain config, excluding the failover / change of active cluster name failover_version bigint, -- indicating the version of active domain only, used for domain failover failover_notification_version bigint, -- indicating the last change related to domain failover notification_version bigint, PRIMARY KEY (domains_partition, name) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE queue ( queue_type int, message_id int, message_payload blob, PRIMARY KEY (queue_type, message_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE queue_metadata ( queue_type int, cluster_ack_level map, version bigint, PRIMARY KEY (queue_type) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE events ( domain_id uuid, workflow_id text, run_id uuid, -- We insert a batch of events with each append transaction. -- This field stores the event id of first event in the batch. first_event_id bigint, range_id bigint, tx_id bigint, data blob, -- Batch of workflow execution history events as a blob data_encoding text, -- Protocol used for history serialization data_version int, -- history blob version event_batch_version bigint, PRIMARY KEY ((domain_id, workflow_id, run_id), first_event_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; ================================================ FILE: schema/cassandra/cadence/versioned/v0.1/base.cql ================================================ CREATE TYPE shard ( shard_id int, owner text, -- Host identifier processing the shard -- Range identifier used for generating ack ids for tasks within shard. -- Also used for optimistic concurrency and all writes to a shard are conditional on this value. range_id bigint, -- This field keeps track of number of times owner for a shard changes before updating range_id or ack_levels stolen_since_renew int, updated_at timestamp, transfer_ack_level bigint, timer_ack_level timestamp, ); --- Workflow execution and mutable state --- CREATE TYPE workflow_execution ( domain_id uuid, workflow_id text, run_id uuid, parent_domain_id uuid, -- Domain ID of parent workflow which started the workflow execution parent_workflow_id text, -- ID of parent workflow which started the workflow execution parent_run_id uuid, -- RunID of parent workflow which started the workflow execution initiated_id bigint, -- Initiated event ID of parent workflow which started this execution completion_event blob, -- Completion event used to communicate result to parent workflow execution task_list text, workflow_type_name text, decision_task_timeout int, execution_context blob, state int, -- enum WorkflowState {Created, Running, Completed} close_status int, -- enum WorkflowCloseStatus {None, Completed, Failed, Canceled, Terminated, ContinuedAsNew, TimedOut} next_event_id bigint, last_processed_event bigint, start_time timestamp, last_updated_time timestamp, create_request_id uuid, decision_schedule_id bigint, decision_started_id bigint, decision_request_id text, -- Identifier used by matching engine for retrying history service calls for recording task is started decision_timeout int, cancel_requested boolean, cancel_request_id text, ); -- TODO: Remove fields that are left over from activity and workflow tasks. CREATE TYPE transfer_task ( domain_id uuid, -- The domain ID that this transfer task belongs to workflow_id text, -- The workflow ID that this transfer task belongs to run_id uuid, -- The run ID that this transfer task belongs to task_id bigint, target_domain_id uuid, -- The external domain ID that this transfer task is doing work for. target_workflow_id text, -- The external workflow ID that this transfer task is doing work for. target_run_id uuid, -- The external run ID that this transfer task is doing work for. task_list text, type int, -- enum TaskType {ActivityTask, DecisionTask, DeleteExecution, CancelExecution, StartChildExecution} schedule_id bigint, ); CREATE TYPE timer_task ( domain_id uuid, workflow_id text, run_id uuid, visibility_ts timestamp, task_id bigint, type int, -- enum TaskType {DecisionTaskTimeout, ActivityTaskTimeout, UserTimer} timeout_type int, -- enum TimeoutType in IDL {START_TO_CLOSE, SCHEDULE_TO_START, SCHEDULE_TO_CLOSE, HEARTBEAT} event_id bigint, -- Corresponds to event ID in history that is responsible for this timer. ); -- Workflow activity in progress mutable state CREATE TYPE activity_info ( schedule_id bigint, scheduled_event blob, scheduled_time timestamp, started_id bigint, started_event blob, started_time timestamp, activity_id text, -- Client generated unique ID for the activity. request_id text, -- Identifier used by matching engine for retrying history service calls for recording task is started details blob, schedule_to_start_timeout int, schedule_to_close_timeout int, start_to_close_timeout int, heart_beat_timeout int, cancel_requested boolean, -- If a cancel request is made to cancel the activity in progress. cancel_request_id bigint, -- Event ID that identifies the cancel request. last_hb_updated_time timestamp, -- Last time the heartbeat is received. timer_task_status int, -- Indicates wheter timers are created for this activity. ); -- User timer details CREATE TYPE timer_info ( timer_id text, -- User defined timer ID started_id bigint, -- The event ID corresponding to timer started. expiry_time timestamp, -- Timestamp at which this timer expires or fires task_id bigint, -- The task ID if we have one created for this timer ); -- Child execution in progress mutable state CREATE TYPE child_execution_info ( initiated_id bigint, initiated_event blob, started_id bigint, started_event blob, create_request_id uuid, ); -- External workflow cancellation in progress mutable state CREATE TYPE request_cancel_info ( initiated_id bigint, cancel_request_id text, ); -- Activity or workflow task in a task list CREATE TYPE task ( domain_id uuid, workflow_id text, run_id uuid, schedule_id bigint, ); CREATE TYPE task_list ( domain_id uuid, name text, type int, -- enum TaskRowType {ActivityTask, DecisionTask} ack_level bigint, -- task_id of the last acknowledged message ); CREATE TYPE domain ( id uuid, name text, status int, -- enum DomainStatus {Registered, Deprecated, Deleted} description text, owner_email text, ); CREATE TYPE domain_config ( retention int, emit_metric boolean ); CREATE TABLE executions ( shard_id int, type int, -- enum RowType { Shard, Execution, TransferTask, TimerTask} domain_id uuid, workflow_id text, run_id uuid, current_run_id uuid, visibility_ts timestamp, -- unique identifier for timer tasks for an execution task_id bigint, -- unique identifier for transfer and timer tasks for an execution shard frozen, execution frozen, transfer frozen, timer frozen, next_event_id bigint, -- This is needed to make conditional updates on session history range_id bigint, -- Increasing sequence identifier for transfer queue, checkpointed into shard info activity_map map>, timer_map map>, child_executions_map map>, request_cancel_map map>, PRIMARY KEY (shard_id, type, domain_id, workflow_id, run_id, visibility_ts, task_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE events ( domain_id uuid, workflow_id text, run_id uuid, -- We insert a batch of events with each append transaction. -- This field stores the event id of first event in the batch. first_event_id bigint, range_id bigint, tx_id bigint, data blob, -- Batch of workflow execution history events as a blob data_encoding text, -- Protocol used for history serialization data_version int, -- history blob version PRIMARY KEY ((domain_id, workflow_id, run_id), first_event_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; -- Stores activity or workflow tasks CREATE TABLE tasks ( domain_id uuid, task_list_name text, task_list_type int, -- enum TaskListType {ActivityTask, DecisionTask} type int, -- enum rowType {Task, TaskList} task_id bigint, -- unique identifier for tasks, monotonically increasing range_id bigint, -- Used to ensure that only one process can write to the table task frozen, task_list frozen, PRIMARY KEY ((domain_id, task_list_name, task_list_type), type, task_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE domains ( id uuid, domain frozen, config frozen, PRIMARY KEY (id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; ================================================ FILE: schema/cassandra/cadence/versioned/v0.1/manifest.json ================================================ { "CurrVersion": "0.1", "MinCompatibleVersion": "0.1", "Description": "base version of schema", "SchemaUpdateCqlFiles": [ "base.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.10/event_batch_version.cql ================================================ ALTER TABLE events ADD event_batch_version bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.10/execution_last_write_version_and_workflow_state.cql ================================================ ALTER TABLE executions ADD workflow_last_write_version bigint; ALTER TABLE executions ADD workflow_state int; ================================================ FILE: schema/cassandra/cadence/versioned/v0.10/manifest.json ================================================ { "CurrVersion": "0.10", "MinCompatibleVersion": "0.10", "Description": "Adding batch event verion to events table, alter execution table to have workflow state and last write version", "SchemaUpdateCqlFiles": [ "event_batch_version.cql", "execution_last_write_version_and_workflow_state.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.11/event_encoding.cql ================================================ ALTER TYPE workflow_execution ADD completion_event_data_encoding text; ALTER TYPE activity_info ADD event_data_encoding text; ALTER TYPE child_execution_info ADD event_data_encoding text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.11/history_size.cql ================================================ ALTER TYPE workflow_execution ADD history_size bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.11/manifest.json ================================================ { "CurrVersion": "0.11", "MinCompatibleVersion": "0.11", "Description": "Mutable state support for server-side retries, history size, and event encoding for mutableState", "SchemaUpdateCqlFiles": [ "workflow_retry.cql", "history_size.cql", "event_encoding.cql", "sync_activity.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.11/sync_activity.cql ================================================ ALTER TYPE replication_task ADD scheduled_id bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.11/workflow_retry.cql ================================================ ALTER TYPE workflow_execution ADD attempt int; ALTER TYPE workflow_execution ADD has_retry_policy boolean; ALTER TYPE workflow_execution ADD init_interval int; ALTER TYPE workflow_execution ADD backoff_coefficient double; ALTER TYPE workflow_execution ADD max_interval int; ALTER TYPE workflow_execution ADD expiration_time timestamp; ALTER TYPE workflow_execution ADD max_attempts int; ALTER TYPE workflow_execution ADD non_retriable_errors list; ================================================ FILE: schema/cassandra/cadence/versioned/v0.12/add_archival_config.cql ================================================ ALTER TYPE domain_config ADD archival_bucket text; ALTER TYPE domain_config ADD archival_status int; ================================================ FILE: schema/cassandra/cadence/versioned/v0.12/cron.cql ================================================ ALTER TYPE workflow_execution ADD cron_schedule text; ALTER TYPE workflow_execution ADD expiration_seconds int; ================================================ FILE: schema/cassandra/cadence/versioned/v0.12/events_v2.cql ================================================ CREATE TYPE branch_range ( branch_id uuid, end_node_id bigint, -- exclusive node_id to represent the stopping point for this range ); CREATE TABLE history_node ( tree_id uuid, branch_id uuid, node_id bigint, -- node_id: first eventID in a batch of events txn_id bigint, -- for override the same node_id: bigger txn_id wins data blob, -- Batch of workflow execution history events as a blob data_encoding text, -- Protocol used for history serialization PRIMARY KEY ((tree_id), branch_id, node_id, txn_id ) ) WITH CLUSTERING ORDER BY (branch_id ASC, node_id ASC, txn_id DESC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; CREATE TABLE history_tree ( tree_id uuid, branch_id uuid, ancestors list>, PRIMARY KEY ((tree_id), branch_id ) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; --- using eventsV2 ALTER TYPE workflow_execution ADD event_store_version int; ALTER TYPE workflow_execution ADD branch_token blob; ALTER TYPE replication_task ADD event_store_version int; ALTER TYPE replication_task ADD branch_token blob; ALTER TYPE replication_task ADD new_run_event_store_version int; ALTER TYPE replication_task ADD new_run_branch_token blob; ALTER TYPE buffered_replication_task_info ADD event_store_version int; ALTER TYPE buffered_replication_task_info ADD new_run_event_store_version int; ================================================ FILE: schema/cassandra/cadence/versioned/v0.12/manifest.json ================================================ { "CurrVersion": "0.12", "MinCompatibleVersion": "0.12", "Description": "Support history events v2, system workflow domain bootstrap, add archival config to domain config", "SchemaUpdateCqlFiles": [ "events_v2.cql", "signal_count.cql", "system_domain_bootstrap.cql", "cron.cql", "add_archival_config.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.12/signal_count.cql ================================================ ALTER TYPE workflow_execution ADD signal_count int; ================================================ FILE: schema/cassandra/cadence/versioned/v0.12/system_domain_bootstrap.cql ================================================ INSERT INTO domains ( id, domain ) VALUES ( 32049b68-7872-4094-8e63-d0dd59896a83, { name: 'cadence-system' } ) IF NOT EXISTS; ================================================ FILE: schema/cassandra/cadence/versioned/v0.13/events_cache.cql ================================================ ALTER TYPE activity_info ADD scheduled_event_batch_id bigint; ALTER TYPE child_execution_info ADD initiated_event_batch_id bigint; ALTER TYPE child_execution_info ADD started_workflow_id text; ALTER TYPE child_execution_info ADD started_run_id uuid; ALTER TYPE child_execution_info ADD domain_name text; ALTER TYPE child_execution_info ADD workflow_type_name text; ALTER TYPE workflow_execution ADD completion_event_batch_id bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.13/manifest.json ================================================ { "CurrVersion": "0.13", "MinCompatibleVersion": "0.13", "Description": "Support workflow reset. Move history events out of mutable state.", "SchemaUpdateCqlFiles": [ "reset.cql", "events_cache.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.13/reset.cql ================================================ ALTER TYPE transfer_task ADD record_visibility boolean; ALTER TYPE replication_task ADD reset_workflow boolean; ALTER TABLE history_tree ADD fork_time timestamp; ALTER TABLE history_tree ADD info text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.14/last_event_task_id.cql ================================================ ALTER TYPE workflow_execution ADD last_event_task_id bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.14/manifest.json ================================================ { "CurrVersion": "0.14", "MinCompatibleVersion": "0.14", "Description": "Added params to tasklist type to enable task_list deletions", "SchemaUpdateCqlFiles": [ "task_list.cql", "last_event_task_id.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.14/task_list.cql ================================================ ALTER TYPE task_list ADD last_updated timestamp; ================================================ FILE: schema/cassandra/cadence/versioned/v0.15/auto_reset.cql ================================================ ALTER TYPE domain_config ADD bad_binaries blob; ALTER TYPE domain_config ADD bad_binaries_encoding text; ALTER TYPE workflow_execution ADD auto_reset_points blob; ALTER TYPE workflow_execution ADD auto_reset_points_encoding text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.15/manifest.json ================================================ { "CurrVersion": "0.15", "MinCompatibleVersion": "0.15", "Description": "Added schema for auto-reset", "SchemaUpdateCqlFiles": [ "auto_reset.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.16/decision_scheduled_timestamp.cql ================================================ ALTER TYPE workflow_execution ADD decision_scheduled_timestamp bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.16/manifest.json ================================================ { "CurrVersion": "0.16", "MinCompatibleVersion": "0.16", "Description": "Added schema for decision_scheduled_timestamp", "SchemaUpdateCqlFiles": [ "decision_scheduled_timestamp.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.17/manifest.json ================================================ { "CurrVersion": "0.17", "MinCompatibleVersion": "0.17", "Description": "Added search attributes to execution", "SchemaUpdateCqlFiles": [ "search_attr.cql", "task_created_time.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.17/search_attr.cql ================================================ ALTER TYPE workflow_execution ADD search_attributes map; ================================================ FILE: schema/cassandra/cadence/versioned/v0.17/task_created_time.cql ================================================ ALTER TYPE task ADD created_time timestamp; ================================================ FILE: schema/cassandra/cadence/versioned/v0.18/activity_last_failure_info.cql ================================================ ALTER TYPE activity_info ADD last_failure_reason text; ALTER TYPE activity_info ADD last_worker_identity text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.18/manifest.json ================================================ { "CurrVersion": "0.18", "MinCompatibleVersion": "0.18", "Description": "Added last_failure_reason and last_worker_identity to activity_info", "SchemaUpdateCqlFiles": [ "activity_last_failure_info.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.19/archival_domain_config.cql ================================================ ALTER TYPE domain_config ADD history_archival_status int; ALTER TYPE domain_config ADD history_archival_uri text; ALTER TYPE domain_config ADD visibility_archival_status int; ALTER TYPE domain_config ADD visibility_archival_uri text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.19/manifest.json ================================================ { "CurrVersion": "0.19", "MinCompatibleVersion": "0.19", "Description": "Added archival related domain config to enable archiver interface", "SchemaUpdateCqlFiles": [ "archival_domain_config.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.2/add_buffered_events.cql ================================================ CREATE TYPE serialized_event_batch ( encoding_type text, version int, data blob, ); ALTER TABLE executions ADD buffered_events_list list>; ================================================ FILE: schema/cassandra/cadence/versioned/v0.2/add_sticky_tasklist.cql ================================================ ALTER TYPE workflow_execution ADD sticky_task_list text; ALTER TYPE workflow_execution ADD sticky_schedule_to_start_timeout int; ================================================ FILE: schema/cassandra/cadence/versioned/v0.2/add_wf_timeout.cql ================================================ ALTER TYPE workflow_execution ADD workflow_timeout int; ================================================ FILE: schema/cassandra/cadence/versioned/v0.2/fail_decision_mutable_state.cql ================================================ ALTER TYPE workflow_execution ADD decision_attempt bigint; ALTER TYPE workflow_execution ADD decision_timestamp bigint; ALTER TYPE timer_task ADD schedule_attempt bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.2/manifest.json ================================================ { "CurrVersion": "0.2", "MinCompatibleVersion": "0.2", "Description": "add workflow_timeout and buffered_events_list to mutable state", "SchemaUpdateCqlFiles": [ "add_wf_timeout.cql", "add_buffered_events.cql", "add_sticky_tasklist.cql", "fail_decision_mutable_state.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.20/manifest.json ================================================ { "CurrVersion": "0.20", "MinCompatibleVersion": "0.20", "Description": "Add memo to mutable state", "SchemaUpdateCqlFiles": [ "memo.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.20/memo.cql ================================================ ALTER TYPE workflow_execution ADD memo map; ================================================ FILE: schema/cassandra/cadence/versioned/v0.21/decision_original_scheduled_timestamp.cql ================================================ ALTER TYPE workflow_execution ADD decision_original_scheduled_timestamp bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.21/manifest.json ================================================ { "CurrVersion": "0.21", "MinCompatibleVersion": "0.21", "Description": "Add decision original scheduled timestamp", "SchemaUpdateCqlFiles": [ "decision_original_scheduled_timestamp.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.22/activity_last_failure_details.cql ================================================ ALTER TYPE activity_info ADD last_failure_details blob; ================================================ FILE: schema/cassandra/cadence/versioned/v0.22/cluster_replication_level.cql ================================================ ALTER TYPE shard ADD cluster_replication_level map; ================================================ FILE: schema/cassandra/cadence/versioned/v0.22/manifest.json ================================================ { "CurrVersion": "0.22", "MinCompatibleVersion": "0.22", "Description": "Add per cluster replication level (last replicated task_id) to shard info and last failure details to activity info", "SchemaUpdateCqlFiles": [ "request_cancel_signal_batch_event_id.cql", "cluster_replication_level.cql", "parent_close_policy.cql", "activity_last_failure_details.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.22/parent_close_policy.cql ================================================ ALTER TYPE child_execution_info ADD parent_close_policy int; ================================================ FILE: schema/cassandra/cadence/versioned/v0.22/request_cancel_signal_batch_event_id.cql ================================================ ALTER TYPE request_cancel_info ADD initiated_event_batch_id bigint; ALTER TYPE signal_info ADD initiated_event_batch_id bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.23/manifest.json ================================================ { "CurrVersion": "0.23", "MinCompatibleVersion": "0.23", "Description": "Add queue table, queue_metadata table and version history", "SchemaUpdateCqlFiles": [ "version_histories.cql", "queue.cql", "system_domain.cql", "queue_metadata.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.23/queue.cql ================================================ CREATE TABLE queue ( queue_type int, message_id int, message_payload blob, PRIMARY KEY (queue_type, message_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; ================================================ FILE: schema/cassandra/cadence/versioned/v0.23/queue_metadata.cql ================================================ CREATE TABLE queue_metadata ( queue_type int, cluster_ack_level map, version bigint, PRIMARY KEY (queue_type) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; ================================================ FILE: schema/cassandra/cadence/versioned/v0.23/system_domain.cql ================================================ INSERT INTO domains_by_name_v2 ( domains_partition, name, domain, config, is_global_domain, config_version, failover_version, failover_notification_version, notification_version ) VALUES ( 0, 'cadence-system', { id: 32049b68-7872-4094-8e63-d0dd59896a83, name: 'cadence-system', description: 'cadence system workflow domain', owner_email: 'cadence-dev-group@uber.com' }, { retention:3, emit_metric:False }, False, 0, -24, -1, -1 ) IF NOT EXISTS; ================================================ FILE: schema/cassandra/cadence/versioned/v0.23/version_histories.cql ================================================ ALTER TABLE executions ADD version_histories blob; ALTER TABLE executions ADD version_histories_encoding text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.24/checksum.cql ================================================ CREATE TYPE checksum (version int, flavor int, value blob); ALTER TABLE executions ADD checksum frozen; ================================================ FILE: schema/cassandra/cadence/versioned/v0.24/manifest.json ================================================ { "CurrVersion": "0.24", "MinCompatibleVersion": "0.23", "Description": "Added checksum to executions table", "SchemaUpdateCqlFiles": [ "checksum.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.25/manifest.json ================================================ { "CurrVersion": "0.25", "MinCompatibleVersion": "0.25", "Description": "Added replication dlq ack level mapping to shard type", "SchemaUpdateCqlFiles": [ "replication_dlq.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.25/replication_dlq.cql ================================================ ALTER TYPE shard ADD replication_dlq_ack_level map; ================================================ FILE: schema/cassandra/cadence/versioned/v0.26/domain_dlq_id.cql ================================================ DROP TABLE queue; CREATE TABLE queue ( queue_type int, message_id bigint, message_payload blob, PRIMARY KEY (queue_type, message_id) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; ================================================ FILE: schema/cassandra/cadence/versioned/v0.26/manifest.json ================================================ { "CurrVersion": "0.26", "MinCompatibleVersion": "0.26", "Description": "Alter domain DLQ message ID type to big integer", "SchemaUpdateCqlFiles": [ "domain_dlq_id.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.27/domain_failover_end_time.cql ================================================ ALTER TABLE domains_by_name_v2 ADD failover_end_time bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.27/manifest.json ================================================ { "CurrVersion": "0.27", "MinCompatibleVersion": "0.26", "Description": "Add graceful failover end time in domain data", "SchemaUpdateCqlFiles": [ "domain_failover_end_time.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.28/manifest.json ================================================ { "CurrVersion": "0.28", "MinCompatibleVersion": "0.27", "Description": "Add creation timestamp in replication task type and update shard info type", "SchemaUpdateCqlFiles": [ "replication_task_creation_time.cql", "previous_failover_version.cql", "shard_info_marker.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.28/previous_failover_version.cql ================================================ ALTER TABLE domains_by_name_v2 ADD previous_failover_version bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.28/replication_task_creation_time.cql ================================================ ALTER TYPE replication_task ADD created_time bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.28/shard_info_marker.cql ================================================ ALTER TYPE shard ADD pending_failover_markers blob; ALTER TYPE shard ADD pending_failover_markers_encoding text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.29/manifest.json ================================================ { "CurrVersion": "0.29", "MinCompatibleVersion": "0.28", "Description": "Add timer and transfer processing queue states to shard", "SchemaUpdateCqlFiles": [ "processing_queue_states.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.29/processing_queue_states.cql ================================================ ALTER TYPE shard ADD transfer_processing_queue_states blob; ALTER TYPE shard ADD transfer_processing_queue_states_encoding text; ALTER TYPE shard ADD timer_processing_queue_states blob; ALTER TYPE shard ADD timer_processing_queue_states_encoding text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.3/add_client_version.cql ================================================ ALTER TYPE workflow_execution ADD client_library_version text; ALTER TYPE workflow_execution ADD client_feature_version text; ALTER TYPE workflow_execution ADD client_impl text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.3/add_last_first_event_id.cql ================================================ ALTER TYPE workflow_execution ADD last_first_event_id bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.3/manifest.json ================================================ { "CurrVersion": "0.3", "MinCompatibleVersion": "0.3", "Description": "add client_library_version, client_feature_version, client_impl to mutable state", "SchemaUpdateCqlFiles": [ "add_client_version.cql", "add_last_first_event_id.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.30/domain_last_updated_time.cql ================================================ ALTER TABLE domains_by_name_v2 ADD last_updated_time bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.30/manifest.json ================================================ { "CurrVersion": "0.30", "MinCompatibleVersion": "0.29", "Description": "Add last updated time in domains table", "SchemaUpdateCqlFiles": [ "domain_last_updated_time.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.31/cross_cluster_queue.cql ================================================ ALTER TYPE shard ADD cross_cluster_processing_queue_states blob; ALTER TYPE shard ADD cross_cluster_processing_queue_states_encoding text; ALTER TABLE executions ADD cross_cluster frozen; ================================================ FILE: schema/cassandra/cadence/versioned/v0.31/manifest.json ================================================ { "CurrVersion": "0.31", "MinCompatibleVersion": "0.31", "Description": "Add cross cluster queue and states", "SchemaUpdateCqlFiles": [ "cross_cluster_queue.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.32/config_store.cql ================================================ CREATE TABLE cluster_config ( row_type int, version int, timestamp timestamp, values blob, encoding text, PRIMARY KEY (row_type, version) ) WITH CLUSTERING ORDER BY (version DESC); ================================================ FILE: schema/cassandra/cadence/versioned/v0.32/manifest.json ================================================ { "CurrVersion": "0.32", "MinCompatibleVersion": "0.32", "Description": "Added cluster_config table for config store support", "SchemaUpdateCqlFiles": [ "config_store.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.33/child_info_domain_id.cql ================================================ ALTER TYPE child_execution_info ADD domain_id uuid; ================================================ FILE: schema/cassandra/cadence/versioned/v0.33/manifest.json ================================================ { "CurrVersion": "0.33", "MinCompatibleVersion": "0.33", "Description": "Added target domain ids to the executions table", "SchemaUpdateCqlFiles": [ "target_domain_ids.cql", "child_info_domain_id.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.33/target_domain_ids.cql ================================================ ALTER TYPE transfer_task ADD target_domain_ids set; ================================================ FILE: schema/cassandra/cadence/versioned/v0.34/manifest.json ================================================ { "CurrVersion": "0.34", "MinCompatibleVersion": "0.34", "Description": "Added first run id to workflow execution type", "SchemaUpdateCqlFiles": [ "workflow_execution_first_run_id.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.34/workflow_execution_first_run_id.cql ================================================ ALTER TYPE workflow_execution ADD first_run_id uuid; ================================================ FILE: schema/cassandra/cadence/versioned/v0.35/manifest.json ================================================ { "CurrVersion": "0.35", "MinCompatibleVersion": "0.35", "Description": "Added partition config to workflow execution and task type", "SchemaUpdateCqlFiles": [ "partition_config.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.35/partition_config.cql ================================================ ALTER TYPE workflow_execution ADD partition_config map; ALTER TYPE task ADD partition_config map; ================================================ FILE: schema/cassandra/cadence/versioned/v0.36/isolation_groups.cql ================================================ ALTER TYPE domain_config ADD isolation_groups blob; ALTER TYPE domain_config ADD isolation_groups_encoding text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.36/manifest.json ================================================ { "CurrVersion": "0.36", "MinCompatibleVersion": "0.36", "Description": "Adding the domain configuration for isolation-groups", "SchemaUpdateCqlFiles": [ "isolation_groups.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.37/async_workflow_config.cql ================================================ ALTER TYPE domain_config ADD async_workflow_config blob; ALTER TYPE domain_config ADD async_workflow_config_encoding text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.37/manifest.json ================================================ { "CurrVersion": "0.37", "MinCompatibleVersion": "0.37", "Description": "Adding the domain configuration for async workflow config", "SchemaUpdateCqlFiles": [ "async_workflow_config.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.38/manifest.json ================================================ { "CurrVersion": "0.38", "MinCompatibleVersion": "0.38", "Description": "Adding the task list partition config to task list type", "SchemaUpdateCqlFiles": [ "task_list_partition_config.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.38/task_list_partition_config.cql ================================================ CREATE TYPE task_list_partition_config ( version bigint, num_read_partitions int, num_write_partitions int ); ALTER TYPE task_list ADD adaptive_partition_config frozen; ================================================ FILE: schema/cassandra/cadence/versioned/v0.39/manifest.json ================================================ { "CurrVersion": "0.39", "MinCompatibleVersion": "0.39", "Description": "Adding create time and last update time for Cassandra tables", "SchemaUpdateCqlFiles": [ "timestamps.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.39/timestamps.cql ================================================ ALTER TABLE executions ADD created_time timestamp; ALTER TABLE executions ADD last_updated_time timestamp; ALTER TABLE history_node ADD created_time timestamp; ALTER TABLE history_tree ADD created_time timestamp; ALTER TABLE tasks ADD created_time timestamp; ALTER TABLE tasks ADD last_updated_time timestamp; ALTER TABLE domains ADD created_time timestamp; ALTER TABLE domains_by_name_v2 ADD created_time timestamp; ALTER TABLE queue ADD created_time timestamp; ALTER TABLE queue_metadata ADD created_time timestamp; ALTER TABLE queue_metadata ADD last_updated_time timestamp; ================================================ FILE: schema/cassandra/cadence/versioned/v0.4/add_signal_decision.cql ================================================ CREATE TYPE signal_info ( initiated_id bigint, signal_request_id uuid, signal_name text, input blob, control blob, ); ALTER TABLE executions ADD signal_map map>; ALTER TABLE executions ADD signal_requested set; ================================================ FILE: schema/cassandra/cadence/versioned/v0.4/add_tasklist_kind.cql ================================================ ALTER TYPE task_list ADD kind int ; ================================================ FILE: schema/cassandra/cadence/versioned/v0.4/manifest.json ================================================ { "CurrVersion": "0.4", "MinCompatibleVersion": "0.4", "Description": "add signal_decision to mutable state, add kind to taskList", "SchemaUpdateCqlFiles": [ "add_signal_decision.cql", "add_tasklist_kind.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.40/isolation_partition_config.cql ================================================ CREATE TYPE task_list_partition ( isolation_groups set ); ALTER TYPE task_list_partition_config ADD read_partitions map>; ALTER TYPE task_list_partition_config ADD write_partitions map>; ================================================ FILE: schema/cassandra/cadence/versioned/v0.40/manifest.json ================================================ { "CurrVersion": "0.40", "MinCompatibleVersion": "0.40", "Description": "Adding an explicit list of task list partitions to support isolation group assignment", "SchemaUpdateCqlFiles": [ "isolation_partition_config.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.41/executions.cql ================================================ ALTER TABLE executions ADD data blob; ALTER TABLE executions ADD data_encoding text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.41/manifest.json ================================================ { "CurrVersion": "0.41", "MinCompatibleVersion": "0.41", "Description": "Adding task blob and task blob encoding column to executions table to represent all history tasks", "SchemaUpdateCqlFiles": [ "executions.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.42/domain_active_clusters_config.cql ================================================ ALTER TYPE domain_replication_config ADD active_clusters_config blob; ALTER TYPE domain_replication_config ADD active_clusters_config_encoding text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.42/manifest.json ================================================ { "CurrVersion": "0.42", "MinCompatibleVersion": "0.42", "Description": "Adding active_clusters_config and active_clusters_config_encoding column to domain_replication_config type", "SchemaUpdateCqlFiles": [ "domain_active_clusters_config.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.43/manifest.json ================================================ { "CurrVersion": "0.43", "MinCompatibleVersion": "0.43", "Description": "Adding new fields to workflow_execution and activity_info types to support following features: cron overlap policy, active-active domain, ephemeral task list", "SchemaUpdateCqlFiles": [ "workflow_execution_new_fields.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.43/workflow_execution_new_fields.cql ================================================ ALTER TYPE workflow_execution ADD cron_overlap_policy int; ALTER TYPE workflow_execution ADD active_cluster_selection_policy blob; ALTER TYPE workflow_execution ADD active_cluster_selection_policy_encoding text; ALTER TYPE workflow_execution ADD task_list_kind int; ALTER TYPE activity_info ADD task_list_kind int; ================================================ FILE: schema/cassandra/cadence/versioned/v0.44/domain_audit_log.cql ================================================ CREATE TABLE domain_audit_log ( domain_id uuid, event_id uuid, -- event_id is the unique identifier for this change to the domain state_before blob, -- state_before stores the domain state before the request state_before_encoding text, -- the encoding type used for state_before state_after blob, -- state_after stores the domain state after the request state_after_encoding text, -- the encoding type used for state_after operation_type int, -- operation_type stores the type of operation that was performed. It is deserialized as an enum and can be used to customize the serialization/deserialization strategy. created_time timestamp, -- created_time the time this row was inserted last_updated_time timestamp, identity text, -- the unique identifier of the user that made the change identity_type text, -- identity_type can be used to delineate between service, user, or other identities comment text, -- comment can be used when manual updates or creates are performed on the database as an audit trail PRIMARY KEY ((domain_id, operation_type), created_time, event_id) ) WITH CLUSTERING ORDER BY (created_time DESC, event_id ASC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; ================================================ FILE: schema/cassandra/cadence/versioned/v0.44/manifest.json ================================================ { "CurrVersion": "0.44", "MinCompatibleVersion": "0.44", "Description": "Adding domain_audit_log table to track domain update and failover history", "SchemaUpdateCqlFiles": [ "domain_audit_log.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.45/manifest.json ================================================ { "CurrVersion": "0.45", "MinCompatibleVersion": "0.45", "Description": "Adding original_task_list and task_list to transfer and timer tasks", "SchemaUpdateCqlFiles": [ "transfer_task.cql", "timer_task.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.45/timer_task.cql ================================================ ALTER TYPE timer_task ADD task_list text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.45/transfer_task.cql ================================================ ALTER TYPE transfer_task ADD original_task_list text; ================================================ FILE: schema/cassandra/cadence/versioned/v0.46/manifest.json ================================================ { "CurrVersion": "0.46", "MinCompatibleVersion": "0.46", "Description": "Adding original_task_list_kind to transfer task", "SchemaUpdateCqlFiles": [ "transfer_task.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.46/transfer_task.cql ================================================ ALTER TYPE transfer_task ADD original_task_list_kind int; ================================================ FILE: schema/cassandra/cadence/versioned/v0.5/add_replication_config.cql ================================================ CREATE TYPE cluster_replication_config ( cluster_name text, ); CREATE TYPE domain_replication_config ( active_cluster_name text, clusters list> ); ================================================ FILE: schema/cassandra/cadence/versioned/v0.5/add_target_child_workflow_only_to_transfer_task.cql ================================================ ALTER TYPE transfer_task ADD target_child_workflow_only boolean; ================================================ FILE: schema/cassandra/cadence/versioned/v0.5/manifest.json ================================================ { "CurrVersion": "0.5", "MinCompatibleVersion": "0.5", "Description": "add cross DC domain replication config, add target child workflow only to transfer task, add cross DC domain config verion, as a sequency ID to prevent out of order domain update, add flag indicating whether a domain is a global domain", "SchemaUpdateCqlFiles": [ "add_replication_config.cql", "add_target_child_workflow_only_to_transfer_task.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.6/add_shard_cluster_ack_level.cql ================================================ ALTER TYPE shard ADD cluster_transfer_ack_level map; ALTER TYPE shard ADD cluster_timer_ack_level map; ================================================ FILE: schema/cassandra/cadence/versioned/v0.6/history_replication_task.cql ================================================ -- Replication information for each cluster CREATE TYPE replication_info ( version bigint, last_event_id bigint, ); -- This is used to store replication information for a workflow execution CREATE TYPE replication_state ( current_version bigint, -- current version for domain, incremented on failover start_version bigint, -- version of domain when the workflow execution was started last_write_version bigint, -- version of domain when the last event was written to history last_write_event_id bigint, -- last written event id for a given version last_replication_info map>, -- information about replication events from other clusters ); -- Transfer task created for processing of replication event CREATE TYPE replication_task ( domain_id uuid, -- The domain ID that this transfer task belongs to workflow_id text, -- The workflow ID that this transfer task belongs to run_id uuid, -- The run ID that this transfer task belongs to task_id bigint, type int, -- enum TaskType {History, Heartbeat} first_event_id bigint, -- Used by ReplicationTask to set the first event ID of the applied transaction next_event_id bigint, -- Used by ReplicationTask to set the next event ID of the applied transaction version bigint, -- Used by ReplicationTask to set the failover version of the applied transaction last_replication_info map>, -- Used by replication task to snapshot replication information when the transaction was applied ); -- Replication information part of mutable state ALTER TABLE executions ADD replication_state frozen; -- Replication transfer task kept as part of executions table ALTER TABLE executions ADD replication frozen; -- Ack level checkpoint for replication task processor ALTER TYPE shard ADD replication_ack_level bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.6/manifest.json ================================================ { "CurrVersion": "0.6", "MinCompatibleVersion": "0.6", "Description": "Persistence support for replication of workflow execution, add cross DC shard attr cluster_transfer_ack_level and cluster_timer_ack_level", "SchemaUpdateCqlFiles": [ "history_replication_task.cql", "add_shard_cluster_ack_level.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.7/buffered_replication_task.cql ================================================ -- Storage for out of order replication tasks for an execution CREATE TYPE buffered_replication_task_info ( first_event_id bigint, next_event_id bigint, version bigint, history frozen, new_run_history frozen, ); -- Buffered replications tasks information part of mutable state ALTER TABLE executions ADD buffered_replication_tasks_map map>; ================================================ FILE: schema/cassandra/cadence/versioned/v0.7/failover_version_persistence.cql ================================================ -- the failover version when this task is created, used to compare against the mutable state, in case the events got overwritten ALTER TYPE transfer_task ADD version bigint; -- the failover version when this task is created, used to compare against the mutable state, in case the events got overwritten ALTER TYPE timer_task ADD version bigint; -- the failover version when this attr is created, used to compare against the mutable state, in case the events got overwritten ALTER TYPE workflow_execution ADD decision_version bigint; ALTER TYPE activity_info ADD version bigint; ALTER TYPE timer_info ADD version bigint; ALTER TYPE child_execution_info ADD version bigint; ALTER TYPE request_cancel_info ADD version bigint; ALTER TYPE signal_info ADD version bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.7/manifest.json ================================================ { "CurrVersion": "0.7", "MinCompatibleVersion": "0.7", "Description": "Support buffering for replication tasks within mutable state, add retry policy. Add version to transfer / timer task, as well as activity, user timer, decision, child workflow, cancellatioon and signal mutable state attrs.", "SchemaUpdateCqlFiles": [ "buffered_replication_task.cql", "failover_version_persistence.cql", "retry_policy.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.7/retry_policy.cql ================================================ ALTER TYPE activity_info ADD attempt int; ALTER TYPE activity_info ADD task_list text; ALTER TYPE activity_info ADD started_identity text; ALTER TYPE activity_info ADD has_retry_policy boolean; ALTER TYPE activity_info ADD init_interval int; ALTER TYPE activity_info ADD backoff_coefficient double; ALTER TYPE activity_info ADD max_interval int; ALTER TYPE activity_info ADD expiration_time timestamp; ALTER TYPE activity_info ADD max_attempts int; ALTER TYPE activity_info ADD non_retriable_errors list; ================================================ FILE: schema/cassandra/cadence/versioned/v0.8/domain_notification.cql ================================================ CREATE TABLE domains_by_name_v2 ( domains_partition int, name text, domain frozen, config frozen, replication_config frozen, -- indicating active cluster and standby cluster used for replication is_global_domain boolean, -- indicating whether a domain is a global domain config_version bigint, -- indicating the version of domain config, excluding the failover / change of active cluster name failover_version bigint, -- indicating the version of active domain only, used for domain failover failover_notification_version bigint, -- indicating the last change related to domain failover notification_version bigint, PRIMARY KEY (domains_partition, name) ) WITH COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' }; ALTER TYPE shard ADD domain_notification_version bigint; ================================================ FILE: schema/cassandra/cadence/versioned/v0.8/manifest.json ================================================ { "CurrVersion": "0.8", "MinCompatibleVersion": "0.8", "Description": "Support reliable domain change notification.", "SchemaUpdateCqlFiles": [ "domain_notification.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.9/domain_data.cql ================================================ ALTER TYPE domain ADD data map; ================================================ FILE: schema/cassandra/cadence/versioned/v0.9/manifest.json ================================================ { "CurrVersion": "0.9", "MinCompatibleVersion": "0.9", "Description": "Support domain customized key-value data, adding timestamp to transfer task.", "SchemaUpdateCqlFiles": [ "domain_data.cql", "transfer_timestamp.cql" ] } ================================================ FILE: schema/cassandra/cadence/versioned/v0.9/transfer_timestamp.cql ================================================ ALTER TYPE transfer_task ADD visibility_ts timestamp; ================================================ FILE: schema/cassandra/embed.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import "embed" //go:embed cadence/* visibility/* var SchemaFS embed.FS ================================================ FILE: schema/cassandra/version.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra // NOTE: whenever there is a new data base schema update, plz update the following versions // Version is the Cassandra database release version const Version = "0.46" // VisibilityVersion is the Cassandra visibility database release version const VisibilityVersion = "0.10" ================================================ FILE: schema/cassandra/visibility/keyspace.cql ================================================ CREATE KEYSPACE IF NOT EXISTS cadence_visibility WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1}; ================================================ FILE: schema/cassandra/visibility/schema.cql ================================================ CREATE TABLE open_executions ( domain_id uuid, domain_partition int, workflow_id text, run_id uuid, start_time timestamp, execution_time timestamp, workflow_type_name text, memo blob, encoding text, task_list text, is_cron boolean, num_clusters int, update_time timestamp, shard_id int, cron_schedule text, execution_status int, scheduled_execution_time timestamp, PRIMARY KEY ((domain_id, domain_partition), start_time, run_id) ) WITH CLUSTERING ORDER BY (start_time DESC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy', 'tombstone_threshold': 0.6 } AND GC_GRACE_SECONDS = 60; CREATE INDEX open_by_workflow_id ON open_executions (workflow_id); CREATE INDEX open_by_type ON open_executions (workflow_type_name); CREATE TABLE closed_executions ( domain_id uuid, domain_partition int, workflow_id text, run_id uuid, start_time timestamp, execution_time timestamp, close_time timestamp, status int, -- enum WorkflowExecutionCloseStatus {COMPLETED, FAILED, CANCELED, TERMINATED, CONTINUED_AS_NEW, TIMED_OUT} workflow_type_name text, history_length bigint, memo blob, encoding text, task_list text, is_cron boolean, num_clusters int, update_time timestamp, shard_id int, cron_schedule text, execution_status int, scheduled_execution_time timestamp, PRIMARY KEY ((domain_id, domain_partition), start_time, run_id) ) WITH CLUSTERING ORDER BY (start_time DESC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' } AND GC_GRACE_SECONDS = 172800; CREATE INDEX closed_by_workflow_id ON closed_executions (workflow_id); CREATE INDEX closed_by_close_time ON closed_executions (close_time); CREATE INDEX closed_by_type ON closed_executions (workflow_type_name); CREATE INDEX closed_by_status ON closed_executions (status); -- same as closed_executions but order by close_time CREATE TABLE closed_executions_v2 ( domain_id uuid, domain_partition int, workflow_id text, run_id uuid, start_time timestamp, execution_time timestamp, close_time timestamp, status int, -- enum WorkflowExecutionCloseStatus {COMPLETED, FAILED, CANCELED, TERMINATED, CONTINUED_AS_NEW, TIMED_OUT} workflow_type_name text, history_length bigint, memo blob, encoding text, task_list text, is_cron boolean, num_clusters int, update_time timestamp, shard_id int, cron_schedule text, execution_status int, scheduled_execution_time timestamp, PRIMARY KEY ((domain_id, domain_partition), close_time, run_id) ) WITH CLUSTERING ORDER BY (close_time DESC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' } AND GC_GRACE_SECONDS = 172800; CREATE INDEX closed_by_workflow_id_v2 ON closed_executions_v2 (workflow_id); CREATE INDEX closed_by_close_time_v2 ON closed_executions_v2 (close_time); CREATE INDEX closed_by_type_v2 ON closed_executions_v2 (workflow_type_name); CREATE INDEX closed_by_status_v2 ON closed_executions_v2 (status); ================================================ FILE: schema/cassandra/visibility/versioned/v0.1/base.cql ================================================ CREATE TABLE open_executions ( domain_id uuid, domain_partition int, workflow_id text, run_id uuid, start_time timestamp, workflow_type_name text, PRIMARY KEY ((domain_id, domain_partition), start_time, run_id) ) WITH CLUSTERING ORDER BY (start_time DESC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' } AND GC_GRACE_SECONDS = 172800; CREATE INDEX open_by_workflow_id ON open_executions (workflow_id); CREATE INDEX open_by_type ON open_executions (workflow_type_name); CREATE TABLE closed_executions ( domain_id uuid, domain_partition int, workflow_id text, run_id uuid, start_time timestamp, close_time timestamp, status int, -- enum WorkflowExecutionCloseStatus {COMPLETED, FAILED, CANCELED, TERMINATED, CONTINUED_AS_NEW, TIMED_OUT} workflow_type_name text, history_length bigint, PRIMARY KEY ((domain_id, domain_partition), start_time, run_id) ) WITH CLUSTERING ORDER BY (start_time DESC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' } AND GC_GRACE_SECONDS = 172800; CREATE INDEX closed_by_workflow_id ON closed_executions (workflow_id); CREATE INDEX closed_by_close_time ON closed_executions (close_time); CREATE INDEX closed_by_type ON closed_executions (workflow_type_name); CREATE INDEX closed_by_status ON closed_executions (status); ================================================ FILE: schema/cassandra/visibility/versioned/v0.1/manifest.json ================================================ { "CurrVersion": "0.1", "MinCompatibleVersion": "0.1", "Description": "base version of visibility schema", "SchemaUpdateCqlFiles": [ "base.cql" ] } ================================================ FILE: schema/cassandra/visibility/versioned/v0.10/add_execution_fields.cql ================================================ -- Add cron_schedule field to track cron workflow schedules ALTER TABLE open_executions ADD cron_schedule text; ALTER TABLE closed_executions ADD cron_schedule text; ALTER TABLE closed_executions_v2 ADD cron_schedule text; -- Add execution_status field to track workflow execution status (Pending, Started, or close status) ALTER TABLE open_executions ADD execution_status int; ALTER TABLE closed_executions ADD execution_status int; ALTER TABLE closed_executions_v2 ADD execution_status int; -- Add scheduled_execution_time field to track the actual scheduled execution time (start_time + first_decision_task_backoff) ALTER TABLE open_executions ADD scheduled_execution_time timestamp; ALTER TABLE closed_executions ADD scheduled_execution_time timestamp; ALTER TABLE closed_executions_v2 ADD scheduled_execution_time timestamp; ================================================ FILE: schema/cassandra/visibility/versioned/v0.10/manifest.json ================================================ { "CurrVersion": "0.10", "MinCompatibleVersion": "0.1", "Description": "add cron_schedule, execution_status, and scheduled_execution_time to visibility", "SchemaUpdateCqlFiles": [ "add_execution_fields.cql" ] } ================================================ FILE: schema/cassandra/visibility/versioned/v0.2/manifest.json ================================================ { "CurrVersion": "0.2", "MinCompatibleVersion": "0.2", "Description": "lower gc_grace_seconds and tweak compaction for open_executions table", "SchemaUpdateCqlFiles": [ "reduce_open_workflow_tombstones.cql" ] } ================================================ FILE: schema/cassandra/visibility/versioned/v0.2/reduce_open_workflow_tombstones.cql ================================================ ALTER TABLE open_executions WITH gc_grace_seconds=60 AND compaction = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy', 'tombstone_threshold': 0.4 }; ================================================ FILE: schema/cassandra/visibility/versioned/v0.3/manifest.json ================================================ { "CurrVersion": "0.3", "MinCompatibleVersion": "0.3", "Description": "change close_executions to order by close_time instead of start_time", "SchemaUpdateCqlFiles": [ "sort_by_close_time.cql" ] } ================================================ FILE: schema/cassandra/visibility/versioned/v0.3/sort_by_close_time.cql ================================================ -- same as closed_executions but order by close_time CREATE TABLE closed_executions_v2 ( domain_id uuid, domain_partition int, workflow_id text, run_id uuid, start_time timestamp, close_time timestamp, status int, -- enum WorkflowExecutionCloseStatus {COMPLETED, FAILED, CANCELED, TERMINATED, CONTINUED_AS_NEW, TIMED_OUT} workflow_type_name text, history_length bigint, PRIMARY KEY ((domain_id, domain_partition), close_time, run_id) ) WITH CLUSTERING ORDER BY (close_time DESC) AND COMPACTION = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy' } AND GC_GRACE_SECONDS = 172800; CREATE INDEX closed_by_workflow_id_v2 ON closed_executions_v2 (workflow_id); CREATE INDEX closed_by_close_time_v2 ON closed_executions_v2 (close_time); CREATE INDEX closed_by_type_v2 ON closed_executions_v2 (workflow_type_name); CREATE INDEX closed_by_status_v2 ON closed_executions_v2 (status); ================================================ FILE: schema/cassandra/visibility/versioned/v0.4/add_execution_time.cql ================================================ ALTER TABLE open_executions ADD execution_time timestamp; ALTER TABLE closed_executions ADD execution_time timestamp; ALTER TABLE closed_executions_v2 ADD execution_time timestamp; ================================================ FILE: schema/cassandra/visibility/versioned/v0.4/add_memo.cql ================================================ ALTER TABLE open_executions ADD memo blob; ALTER TABLE closed_executions ADD memo blob; ALTER TABLE closed_executions_v2 ADD memo blob; ALTER TABLE open_executions ADD encoding text; ALTER TABLE closed_executions ADD encoding text; ALTER TABLE closed_executions_v2 ADD encoding text; ================================================ FILE: schema/cassandra/visibility/versioned/v0.4/manifest.json ================================================ { "CurrVersion": "0.4", "MinCompatibleVersion": "0.4", "Description": "add execution_time field to visibility; add non-searchable field to visibility", "SchemaUpdateCqlFiles": [ "add_execution_time.cql", "add_memo.cql" ] } ================================================ FILE: schema/cassandra/visibility/versioned/v0.5/add_task_list.cql ================================================ ALTER TABLE open_executions ADD task_list text; ALTER TABLE closed_executions ADD task_list text; ALTER TABLE closed_executions_v2 ADD task_list text; ================================================ FILE: schema/cassandra/visibility/versioned/v0.5/manifest.json ================================================ { "CurrVersion": "0.5", "MinCompatibleVersion": "0.5", "Description": "add task_list field to visibility", "SchemaUpdateCqlFiles": [ "add_task_list.cql" ] } ================================================ FILE: schema/cassandra/visibility/versioned/v0.6/add_is_cron.cql ================================================ ALTER TABLE open_executions ADD is_cron boolean; ALTER TABLE closed_executions ADD is_cron boolean; ALTER TABLE closed_executions_v2 ADD is_cron boolean; ================================================ FILE: schema/cassandra/visibility/versioned/v0.6/manifest.json ================================================ { "CurrVersion": "0.6", "MinCompatibleVersion": "0.6", "Description": "add is_cron field to visibility", "SchemaUpdateCqlFiles": [ "add_is_cron.cql" ] } ================================================ FILE: schema/cassandra/visibility/versioned/v0.7/add_num_clusters.cql ================================================ ALTER TABLE open_executions ADD num_clusters int; ALTER TABLE closed_executions ADD num_clusters int; ALTER TABLE closed_executions_v2 ADD num_clusters int; ================================================ FILE: schema/cassandra/visibility/versioned/v0.7/manifest.json ================================================ { "CurrVersion": "0.7", "MinCompatibleVersion": "0.7", "Description": "add num_clusters field to visibility", "SchemaUpdateCqlFiles": [ "add_num_clusters.cql" ] } ================================================ FILE: schema/cassandra/visibility/versioned/v0.8/add_update_time.cql ================================================ ALTER TABLE open_executions ADD update_time timestamp; ALTER TABLE closed_executions ADD update_time timestamp; ALTER TABLE closed_executions_v2 ADD update_time timestamp; ================================================ FILE: schema/cassandra/visibility/versioned/v0.8/manifest.json ================================================ { "CurrVersion": "0.8", "MinCompatibleVersion": "0.8", "Description": "add update_time field to visibility", "SchemaUpdateCqlFiles": [ "add_update_time.cql" ] } ================================================ FILE: schema/cassandra/visibility/versioned/v0.9/add_shard_id.cql ================================================ ALTER TABLE open_executions ADD shard_id int; ALTER TABLE closed_executions ADD shard_id int; ALTER TABLE closed_executions_v2 ADD shard_id int; ================================================ FILE: schema/cassandra/visibility/versioned/v0.9/manifest.json ================================================ { "CurrVersion": "0.9", "MinCompatibleVersion": "0.9", "Description": "add shard_id to visibility", "SchemaUpdateCqlFiles": [ "add_shard_id.cql" ] } ================================================ FILE: schema/elasticsearch/embed.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package elasticsearch import _ "embed" // enable embedding //go:embed v6/visibility/index_template.json var IndexTemplateV6 []byte //go:embed v7/visibility/index_template.json var IndexTemplateV7 []byte //go:embed os2/visibility/index_template.json var IndexTemplateOS2 []byte ================================================ FILE: schema/elasticsearch/os2/visibility/index_template.json ================================================ { "aliases": {}, "index_patterns": [ "cadence-visibility-*" ], "mappings": { "dynamic": "false", "properties": { "Attr": { "properties": { "BinaryChecksums": { "type": "keyword" }, "CadenceChangeVersion": { "type": "keyword" }, "CustomBoolField": { "type": "boolean" }, "CustomDatetimeField": { "type": "date" }, "CustomDomain": { "type": "keyword" }, "CustomDoubleField": { "type": "double" }, "CustomIntField": { "type": "long" }, "CustomKeywordField": { "type": "keyword" }, "CustomStringField": { "type": "text" }, "Operator": { "type": "keyword" }, "Passed": { "type": "boolean" }, "RolloutID": { "type": "keyword" }, "addon": { "type": "keyword" }, "addon-type": { "type": "keyword" }, "environment": { "type": "keyword" }, "project": { "type": "keyword" }, "service": { "type": "keyword" }, "user": { "type": "keyword" } } }, "CloseStatus": { "type": "integer" }, "CloseTime": { "type": "long" }, "DomainID": { "type": "keyword" }, "ExecutionTime": { "type": "long" }, "HistoryLength": { "type": "integer" }, "IsCron": { "type": "boolean" }, "KafkaKey": { "type": "keyword" }, "NumClusters": { "type": "integer" }, "RunID": { "type": "keyword" }, "ShardID": { "type": "long" }, "CronSchedule": { "type": "keyword" }, "ExecutionStatus": { "type": "integer" }, "ScheduledExecutionTime": { "type": "long" }, "StartTime": { "type": "long" }, "TaskList": { "type": "keyword" }, "ClusterAttributeScope": { "type": "keyword" }, "ClusterAttributeName": { "type": "keyword" }, "UpdateTime": { "type": "long" }, "WorkflowID": { "type": "keyword" }, "WorkflowType": { "type": "keyword" } } }, "order": 0, "settings": { "index": { "number_of_replicas": "0", "number_of_shards": "5" } } } ================================================ FILE: schema/elasticsearch/v6/visibility/index_template.json ================================================ { "order": 0, "index_patterns": [ "cadence-visibility-*" ], "settings": { "index": { "number_of_shards": "5", "number_of_replicas": "0" } }, "mappings": { "_doc": { "dynamic": "false", "properties": { "DomainID": { "type": "keyword" }, "WorkflowID": { "type": "keyword" }, "RunID": { "type": "keyword" }, "WorkflowType": { "type": "keyword" }, "StartTime": { "type": "long" }, "ExecutionTime": { "type": "long" }, "CloseTime": { "type": "long" }, "CloseStatus": { "type": "integer" }, "HistoryLength": { "type": "integer" }, "KafkaKey": { "type": "keyword" }, "TaskList": { "type": "keyword" }, "IsCron": { "type": "boolean" }, "NumClusters": { "type": "integer" }, "ClusterAttributeScope": { "type": "keyword" }, "ClusterAttributeName": { "type": "keyword" }, "UpdateTime": { "type": "long" }, "ShardID": { "type": "long" }, "CronSchedule": { "type": "keyword" }, "ExecutionStatus": { "type": "integer" }, "ScheduledExecutionTime": { "type": "long" }, "Attr": { "properties": { "CadenceChangeVersion": { "type": "keyword" }, "CustomStringField": { "type": "text" }, "CustomKeywordField": { "type": "keyword"}, "CustomIntField": { "type": "long"}, "CustomBoolField": { "type": "boolean"}, "CustomDoubleField": { "type": "double"}, "CustomDatetimeField": { "type": "date"}, "project": { "type": "keyword"}, "service": { "type": "keyword"}, "environment": { "type": "keyword"}, "addon": { "type": "keyword"}, "addon-type": { "type": "keyword"}, "user": { "type": "keyword"}, "CustomDomain": { "type": "keyword"}, "Operator": { "type": "keyword"}, "RolloutID": { "type": "keyword"}, "BinaryChecksums": { "type": "keyword"}, "Passed": { "type": "boolean" } } } } } }, "aliases": {} } ================================================ FILE: schema/elasticsearch/v7/visibility/index_template.json ================================================ { "order": 0, "index_patterns": [ "cadence-visibility-*" ], "settings": { "index": { "number_of_shards": "5", "number_of_replicas": "0" } }, "mappings": { "dynamic": "false", "properties": { "DomainID": { "type": "keyword" }, "WorkflowID": { "type": "keyword" }, "RunID": { "type": "keyword" }, "WorkflowType": { "type": "keyword" }, "StartTime": { "type": "long" }, "ExecutionTime": { "type": "long" }, "CloseTime": { "type": "long" }, "CloseStatus": { "type": "integer" }, "HistoryLength": { "type": "integer" }, "KafkaKey": { "type": "keyword" }, "TaskList": { "type": "keyword" }, "IsCron": { "type": "boolean" }, "NumClusters": { "type": "integer" }, "ClusterAttributeScope": { "type": "keyword" }, "ClusterAttributeName": { "type": "keyword" }, "UpdateTime": { "type": "long" }, "ShardID": { "type": "long" }, "CronSchedule": { "type": "keyword" }, "ExecutionStatus": { "type": "integer" }, "ScheduledExecutionTime": { "type": "long" }, "Attr": { "properties": { "CadenceChangeVersion": { "type": "keyword" }, "CustomStringField": { "type": "text" }, "CustomKeywordField": { "type": "keyword"}, "CustomIntField": { "type": "long"}, "CustomBoolField": { "type": "boolean"}, "CustomDoubleField": { "type": "double"}, "CustomDatetimeField": { "type": "date"}, "project": { "type": "keyword"}, "service": { "type": "keyword"}, "environment": { "type": "keyword"}, "addon": { "type": "keyword"}, "addon-type": { "type": "keyword"}, "user": { "type": "keyword"}, "CustomDomain": { "type": "keyword"}, "Operator": { "type": "keyword"}, "RolloutID": { "type": "keyword"}, "BinaryChecksums": { "type": "keyword"}, "Passed": { "type": "boolean" } } } } }, "aliases": {} } ================================================ FILE: schema/mongodb/README.md ================================================ What ---- This directory contains the mongodb schema for every database that cadence owns. The directory structure is as follows ``` ./schema - cadence/ -- Contains schema for default data models - schema.json -- Contains the latest & greatest snapshot of the schema for the keyspace - schema.go -- Contains the collection schema in Golang structs -- because MongoDB collection is shemaless. - versioned - v0.1/ - v0.2/ -- One directory per schema version change - v1.0/ - manifest.json -- json file describing the change - changes.json -- changes in this version, only [create collection/index/documents] commands are allowed ``` ## MongoDB JSON schema format Below is an example of a schema JSON file containing two commands, for collection/index/documents creation. ```json [ { "create": "collection_name" }, { "createIndexes": "collection_name", "indexes": [ { "key": { "fieldnamea": 1, "fieldnameb": -1 }, "name": "fieldnamea_fieldnameb" } ], "writeConcern": { "w": "majority" } }, { "insert": "collection_name", "documents": [ { "fieldnamea": 1, "fieldnameb": 0, "fieldnamec": "1234" }, { "fieldnamea": 2, "fieldnameb": 1, "fieldnamec": "12344" }, { "fieldnamea": 2, "fieldnameb": 2, "fieldnamec": "12345" } ], "ordered": false } ] ``` How --- Q: How do I update existing schema ? * Add your changes to schema.json for snapshot * Create a new schema version directory under ./schema/<>/versioned/vx.x * Add a manifest.json * Add your changes in a json file ================================================ FILE: schema/mongodb/cadence/collectionSchema.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cadence // below are the names of all mongoDB collections const ( ClusterConfigCollectionName = "cluster_config" ) // NOTE1: MongoDB collection is schemaless -- there is no schema file for collection. We use Go lang structs to define the collection fields. // NOTE2: MongoDB doesn't allow using camel case or underscore in the field names // ClusterConfigCollectionEntry is the schema of configStore // IMPORTANT: making change to this struct is changing the MongoDB collection schema. Please make sure it's backward compatible(e.g., don't delete the field, or change the annotation value). type ClusterConfigCollectionEntry struct { ID int `json:"_id,omitempty"` RowType int `json:"rowtype"` Version int64 `json:"version"` Data []byte `json:"data"` DataEncoding string `json:"dataencoding"` UnixTimestampSeconds int64 `json:"unixtimestampseconds"` } ================================================ FILE: schema/mongodb/cadence/schema.json ================================================ [ { "create": "cluster_config" }, { "createIndexes": "cluster_config", "indexes": [ { "key": { "rowtype": 1, "version": -1 }, "name": "rowtype_version", "unique": true } ], "writeConcern": { "w": "majority" } } ] ================================================ FILE: schema/mongodb/cadence/versioned/v0.1/base.json ================================================ [ { "create": "cluster_config" }, { "createIndexes": "cluster_config", "indexes": [ { "key": { "rowtype": 1, "version": -1 }, "name": "rowtype_version", "unique": true } ], "writeConcern": { "w": "majority" } } ] ================================================ FILE: schema/mongodb/cadence/versioned/v0.1/manifest.json ================================================ { "CurrVersion": "0.1", "MinCompatibleVersion": "0.1", "Description": "base version of schema", "SchemaUpdateCqlFiles": [ "base.json" ] } ================================================ FILE: schema/mongodb/version.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mongodb // NOTE: whenever there is a new data base schema update, plz update the following versions // Version is the MongoDB database schema release version const Version = "0.1" ================================================ FILE: schema/mysql/embed.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql import "embed" //go:embed v8/cadence/* v8/visibility/* var SchemaFS embed.FS ================================================ FILE: schema/mysql/v8/cadence/database.sql ================================================ CREATE DATABASE cadence character set utf8; ================================================ FILE: schema/mysql/v8/cadence/schema.sql ================================================ CREATE TABLE domains( shard_id INT NOT NULL DEFAULT 54321, id BINARY(16) NOT NULL, name VARCHAR(255) UNIQUE NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, is_global TINYINT(1) NOT NULL, PRIMARY KEY(shard_id, id) ); CREATE TABLE domain_metadata ( `id` bigint(20) NOT NULL AUTO_INCREMENT, notification_version BIGINT NOT NULL, PRIMARY KEY (`id`) ); INSERT INTO domain_metadata (notification_version) VALUES (1); CREATE TABLE shards ( shard_id INT NOT NULL, -- range_id BIGINT NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id) ); CREATE TABLE transfer_tasks( shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE cross_cluster_tasks( target_cluster VARCHAR(255) NOT NULL, shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (target_cluster, shard_id, task_id) ); CREATE TABLE executions( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, -- next_event_id BIGINT NOT NULL, last_write_version BIGINT NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id) ); CREATE TABLE current_executions( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, -- run_id BINARY(16) NOT NULL, create_request_id VARCHAR(64) NOT NULL, state INT NOT NULL, close_status INT NOT NULL, start_version BIGINT NOT NULL, last_write_version BIGINT NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id) ); CREATE TABLE buffered_events ( id BIGINT AUTO_INCREMENT NOT NULL, shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (id) ); CREATE INDEX buffered_events_by_events_ids ON buffered_events(shard_id, domain_id, workflow_id, run_id); CREATE TABLE tasks ( domain_id BINARY(16) NOT NULL, task_list_name VARCHAR(255) NOT NULL, task_type TINYINT NOT NULL, -- {Activity, Decision} task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (domain_id, task_list_name, task_type, task_id) ); CREATE TABLE task_lists ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, name VARCHAR(255) NOT NULL, task_type TINYINT NOT NULL, -- {Activity, Decision} -- range_id BIGINT NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, name, task_type) ); CREATE TABLE replication_tasks ( shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE replication_tasks_dlq ( source_cluster_name VARCHAR(255) NOT NULL, shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (source_cluster_name, shard_id, task_id) ); CREATE TABLE timer_tasks ( shard_id INT NOT NULL, visibility_timestamp DATETIME(6) NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, visibility_timestamp, task_id) ); CREATE TABLE activity_info_maps ( -- each row corresponds to one key of one map shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, schedule_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), last_heartbeat_details BLOB, last_heartbeat_updated_time DATETIME(6) NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, schedule_id) ); CREATE TABLE timer_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, timer_id VARCHAR(255) NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, timer_id) ); CREATE TABLE child_execution_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE request_cancel_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE signal_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE buffered_replication_task_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, first_event_id BIGINT NOT NULL, -- version BIGINT NOT NULL, next_event_id BIGINT NOT NULL, history MEDIUMBLOB, history_encoding VARCHAR(16) NOT NULL, new_run_history MEDIUMBLOB, new_run_history_encoding VARCHAR(16) NOT NULL DEFAULT 'json', event_store_version INT NOT NULL, -- indicates which version of event store to query new_run_event_store_version INT NOT NULL, -- indicates which version of event store to query for new run(continueAsNew) PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, first_event_id) ); CREATE TABLE signals_requested_sets ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, signal_id VARCHAR(64) NOT NULL, -- PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, signal_id) ); -- history eventsV2: history_node stores history event data CREATE TABLE history_node ( shard_id INT NOT NULL, tree_id BINARY(16) NOT NULL, branch_id BINARY(16) NOT NULL, node_id BIGINT NOT NULL, txn_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( shard_id INT NOT NULL, tree_id BINARY(16) NOT NULL, branch_id BINARY(16) NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id) ); CREATE TABLE queue ( queue_type INT NOT NULL, message_id BIGINT NOT NULL, message_payload MEDIUMBLOB NOT NULL, PRIMARY KEY(queue_type, message_id) ); CREATE TABLE queue_metadata ( queue_type INT NOT NULL, data MEDIUMBLOB NOT NULL, PRIMARY KEY(queue_type) ); CREATE TABLE cluster_config ( row_type INT NOT NULL, version BIGINT NOT NULL, -- timestamp DATETIME(6) NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (row_type, version) ); CREATE TABLE domain_audit_log ( domain_id VARCHAR(255) NOT NULL, event_id VARCHAR(255) NOT NULL, -- state_before BLOB NOT NULL, state_before_encoding VARCHAR(16) NOT NULL, state_after BLOB NOT NULL, state_after_encoding VARCHAR(16) NOT NULL, operation_type INT NOT NULL, created_time DATETIME(6) NOT NULL, last_updated_time DATETIME(6) NOT NULL, identity VARCHAR(255) NOT NULL, identity_type VARCHAR(255) NOT NULL, comment VARCHAR(255) NOT NULL DEFAULT '', PRIMARY KEY (domain_id, operation_type, created_time, event_id) ); ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.1/base.sql ================================================ CREATE TABLE domains( shard_id INT NOT NULL DEFAULT 54321, id BINARY(16) NOT NULL, name VARCHAR(255) UNIQUE NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, is_global TINYINT(1) NOT NULL, PRIMARY KEY(shard_id, id) ); CREATE TABLE domain_metadata ( `id` bigint(20) NOT NULL AUTO_INCREMENT, notification_version BIGINT NOT NULL, PRIMARY KEY (`id`) ); INSERT INTO domain_metadata (notification_version) VALUES (1); CREATE TABLE shards ( shard_id INT NOT NULL, -- range_id BIGINT NOT NULL, data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id) ); CREATE TABLE transfer_tasks( shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE executions( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, -- next_event_id BIGINT NOT NULL, last_write_version BIGINT NOT NULL, data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id) ); CREATE TABLE current_executions( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, -- run_id BINARY(16) NOT NULL, create_request_id VARCHAR(64) NOT NULL, state INT NOT NULL, close_status INT NOT NULL, start_version BIGINT NOT NULL, last_write_version BIGINT NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id) ); CREATE TABLE buffered_events ( id BIGINT AUTO_INCREMENT NOT NULL, shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (id) ); CREATE INDEX buffered_events_by_events_ids ON buffered_events(shard_id, domain_id, workflow_id, run_id); CREATE TABLE tasks ( domain_id BINARY(16) NOT NULL, task_list_name VARCHAR(255) NOT NULL, task_type TINYINT NOT NULL, -- {Activity, Decision} task_id BIGINT NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (domain_id, task_list_name, task_type, task_id) ); CREATE TABLE task_lists ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, name VARCHAR(255) NOT NULL, task_type TINYINT NOT NULL, -- {Activity, Decision} -- range_id BIGINT NOT NULL, data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, name, task_type) ); CREATE TABLE replication_tasks ( shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE timer_tasks ( shard_id INT NOT NULL, visibility_timestamp DATETIME(6) NOT NULL, task_id BIGINT NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, visibility_timestamp, task_id) ); -- Deprecated in favor of history eventsV2 CREATE TABLE events ( domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, first_event_id BIGINT NOT NULL, -- batch_version BIGINT, range_id BIGINT NOT NULL, tx_id BIGINT NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (domain_id, workflow_id, run_id, first_event_id) ); CREATE TABLE activity_info_maps ( -- each row corresponds to one key of one map shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, schedule_id BIGINT NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16), last_heartbeat_details BLOB, last_heartbeat_updated_time DATETIME(6) NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, schedule_id) ); CREATE TABLE timer_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, timer_id VARCHAR(255) NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, timer_id) ); CREATE TABLE child_execution_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE request_cancel_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE signal_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE buffered_replication_task_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, first_event_id BIGINT NOT NULL, -- version BIGINT NOT NULL, next_event_id BIGINT NOT NULL, history MEDIUMBLOB, history_encoding VARCHAR(16) NOT NULL, new_run_history BLOB, new_run_history_encoding VARCHAR(16) NOT NULL DEFAULT 'json', event_store_version INT NOT NULL, -- indiciates which version of event store to query new_run_event_store_version INT NOT NULL, -- indiciates which version of event store to query for new run(continueAsNew) PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, first_event_id) ); CREATE TABLE signals_requested_sets ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, signal_id VARCHAR(64) NOT NULL, -- PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, signal_id) ); -- history eventsV2: history_node stores history event data CREATE TABLE history_node ( shard_id INT NOT NULL, tree_id BINARY(16) NOT NULL, branch_id BINARY(16) NOT NULL, node_id BIGINT NOT NULL, txn_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( shard_id INT NOT NULL, tree_id BINARY(16) NOT NULL, branch_id BINARY(16) NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id) ); ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.1/manifest.json ================================================ { "CurrVersion": "0.1", "MinCompatibleVersion": "0.1", "Description": "base version of schema", "SchemaUpdateCqlFiles": [ "base.sql" ] } ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.2/manifest.json ================================================ { "CurrVersion": "0.2", "MinCompatibleVersion": "0.2", "Description": "add queue and queue_metadata table", "SchemaUpdateCqlFiles": [ "queue.sql" ] } ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.2/queue.sql ================================================ CREATE TABLE queue ( queue_type INT NOT NULL, message_id BIGINT NOT NULL, message_payload BLOB NOT NULL, PRIMARY KEY(queue_type, message_id) ); CREATE TABLE queue_metadata ( queue_type INT NOT NULL, data BLOB NOT NULL, PRIMARY KEY(queue_type) ); ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.3/manifest.json ================================================ { "CurrVersion": "0.3", "MinCompatibleVersion": "0.3", "Description": "add replication_tasks_dlq table", "SchemaUpdateCqlFiles": [ "replication_tasks_dlq.sql" ] } ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.3/replication_tasks_dlq.sql ================================================ CREATE TABLE replication_tasks_dlq ( source_cluster_name VARCHAR(255) NOT NULL, shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data BLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (source_cluster_name, shard_id, task_id) ); ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.4/blob_size.sql ================================================ ALTER TABLE domains MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE shards MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE transfer_tasks MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE executions MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE tasks MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE task_lists MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE replication_tasks MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE replication_tasks_dlq MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE timer_tasks MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE activity_info_maps MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE timer_info_maps MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE child_execution_info_maps MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE request_cancel_info_maps MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE signal_info_maps MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE buffered_replication_task_maps MODIFY COLUMN new_run_history MEDIUMBLOB; ALTER TABLE history_tree MODIFY COLUMN data MEDIUMBLOB; ALTER TABLE queue MODIFY COLUMN message_payload MEDIUMBLOB; ALTER TABLE queue_metadata MODIFY COLUMN data MEDIUMBLOB; ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.4/manifest.json ================================================ { "CurrVersion": "0.4", "MinCompatibleVersion": "0.3", "Description": "modify blob size", "SchemaUpdateCqlFiles": [ "blob_size.sql" ] } ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.5/cross_cluster_table.sql ================================================ CREATE TABLE cross_cluster_tasks( target_cluster VARCHAR(255) NOT NULL, shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (target_cluster, shard_id, task_id) ); ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.5/manifest.json ================================================ { "CurrVersion": "0.5", "MinCompatibleVersion": "0.5", "Description": "create cross cluster table", "SchemaUpdateCqlFiles": [ "cross_cluster_table.sql" ] } ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.6/cluster_config.sql ================================================ CREATE TABLE cluster_config ( row_type INT NOT NULL, version BIGINT NOT NULL, -- timestamp DATETIME(6) NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (row_type, version) ); ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.6/manifest.json ================================================ { "CurrVersion": "0.6", "MinCompatibleVersion": "0.6", "Description": "create cluster config table", "SchemaUpdateCqlFiles": [ "cluster_config.sql" ] } ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.7/domain_audit_log.sql ================================================ CREATE TABLE domain_audit_log ( domain_id VARCHAR(255) NOT NULL, event_id VARCHAR(255) NOT NULL, -- state_before BLOB NOT NULL, state_before_encoding VARCHAR(16) NOT NULL, state_after BLOB NOT NULL, state_after_encoding VARCHAR(16) NOT NULL, operation_type INT NOT NULL, created_time DATETIME(6) NOT NULL, last_updated_time DATETIME(6) NOT NULL, identity VARCHAR(255) NOT NULL, identity_type VARCHAR(255) NOT NULL, comment VARCHAR(255) NOT NULL DEFAULT '', PRIMARY KEY (domain_id, operation_type, created_time, event_id) ); ================================================ FILE: schema/mysql/v8/cadence/versioned/v0.7/manifest.json ================================================ { "CurrVersion": "0.7", "MinCompatibleVersion": "0.7", "Description": "create domain audit log table", "SchemaUpdateCqlFiles": [ "domain_audit_log.sql" ] } ================================================ FILE: schema/mysql/v8/visibility/database.sql ================================================ CREATE DATABASE cadence_visibility character set utf8; ================================================ FILE: schema/mysql/v8/visibility/schema.sql ================================================ CREATE TABLE executions_visibility ( domain_id CHAR(64) NOT NULL, run_id CHAR(64) NOT NULL, start_time DATETIME(6) NOT NULL, execution_time DATETIME(6) NOT NULL, workflow_id VARCHAR(255) NOT NULL, workflow_type_name VARCHAR(255) NOT NULL, close_status INT, -- enum WorkflowExecutionCloseStatus {COMPLETED, FAILED, CANCELED, TERMINATED, CONTINUED_AS_NEW, TIMED_OUT} close_time DATETIME(6) NULL, history_length BIGINT, memo BLOB, encoding VARCHAR(64) NOT NULL, task_list VARCHAR(255) DEFAULT '' NOT NULL, is_cron BOOLEAN DEFAULT false NOT NULL, num_clusters INT NULL, update_time DATETIME(6) NULL, shard_id INT NULL, cron_schedule VARCHAR(255) NULL, execution_status INT NULL, scheduled_execution_time DATETIME(6) NULL, PRIMARY KEY (domain_id, run_id) ); CREATE INDEX by_type_start_time ON executions_visibility (domain_id, workflow_type_name, close_status, start_time DESC, run_id); CREATE INDEX by_workflow_id_start_time ON executions_visibility (domain_id, workflow_id, close_status, start_time DESC, run_id); CREATE INDEX by_status_by_close_time ON executions_visibility (domain_id, close_status, start_time DESC, run_id); CREATE INDEX by_close_time_by_status ON executions_visibility (domain_id, close_time DESC, run_id, close_status); ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.1/base.sql ================================================ CREATE TABLE executions_visibility ( domain_id CHAR(64) NOT NULL, run_id CHAR(64) NOT NULL, start_time DATETIME(6) NOT NULL, execution_time DATETIME(6) NOT NULL, workflow_id VARCHAR(255) NOT NULL, workflow_type_name VARCHAR(255) NOT NULL, close_status INT, -- enum WorkflowExecutionCloseStatus {COMPLETED, FAILED, CANCELED, TERMINATED, CONTINUED_AS_NEW, TIMED_OUT} close_time DATETIME(6) NULL, history_length BIGINT, memo BLOB, encoding VARCHAR(64) NOT NULL, PRIMARY KEY (domain_id, run_id) ); CREATE INDEX by_type_start_time ON executions_visibility (domain_id, workflow_type_name, close_status, start_time DESC, run_id); CREATE INDEX by_workflow_id_start_time ON executions_visibility (domain_id, workflow_id, close_status, start_time DESC, run_id); CREATE INDEX by_status_by_close_time ON executions_visibility (domain_id, close_status, start_time DESC, run_id); ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.1/manifest.json ================================================ { "CurrVersion": "0.1", "MinCompatibleVersion": "0.1", "Description": "base version of visibility schema", "SchemaUpdateCqlFiles": [ "base.sql" ] } ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.2/add_task_list.sql ================================================ ALTER TABLE executions_visibility ADD task_list varchar(255) DEFAULT ''; ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.2/manifest.json ================================================ { "CurrVersion": "0.2", "MinCompatibleVersion": "0.2", "Description": "add task_list field to visibility", "SchemaUpdateCqlFiles": [ "add_task_list.sql" ] } ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.3/manifest.json ================================================ { "CurrVersion": "0.3", "MinCompatibleVersion": "0.2", "Description": "add close time and closed state index", "SchemaUpdateCqlFiles": [ "vs_index.sql" ] } ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.3/vs_index.sql ================================================ CREATE INDEX by_close_time_by_status ON executions_visibility (domain_id, close_time DESC, run_id, close_status); ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.4/add_is_cron.sql ================================================ ALTER TABLE executions_visibility ADD is_cron BOOLEAN DEFAULT false; ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.4/manifest.json ================================================ { "CurrVersion": "0.4", "MinCompatibleVersion": "0.4", "Description": "add is_cron field to visibility", "SchemaUpdateCqlFiles": [ "add_is_cron.sql" ] } ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.5/add_num_clusters.sql ================================================ ALTER TABLE executions_visibility ADD num_clusters INT; ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.5/manifest.json ================================================ { "CurrVersion": "0.5", "MinCompatibleVersion": "0.5", "Description": "add num_clusters field to visibility", "SchemaUpdateCqlFiles": [ "add_num_clusters.sql" ] } ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.6/add_update_time.sql ================================================ ALTER TABLE executions_visibility ADD update_time DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6); ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.6/manifest.json ================================================ { "CurrVersion": "0.6", "MinCompatibleVersion": "0.6", "Description": "add update_time field to visibility", "SchemaUpdateCqlFiles": [ "add_update_time.sql" ] } ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.7/add_shard_id.sql ================================================ ALTER TABLE executions_visibility ADD shard_id INT DEFAULT -1; ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.7/manifest.json ================================================ { "CurrVersion": "0.7", "MinCompatibleVersion": "0.7", "Description": "add shard_id field to visibility", "SchemaUpdateCqlFiles": [ "add_shard_id.sql" ] } ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.8/add_execution_fields.sql ================================================ -- Add cron_schedule field to track cron workflow schedules ALTER TABLE executions_visibility ADD cron_schedule VARCHAR(255) NULL; -- Add execution_status field to track workflow execution status (Pending, Started, or close status) ALTER TABLE executions_visibility ADD execution_status INT NULL; -- Add scheduled_execution_time field to track the actual scheduled execution time (start_time + first_decision_task_backoff) ALTER TABLE executions_visibility ADD scheduled_execution_time DATETIME(6) NULL; ================================================ FILE: schema/mysql/v8/visibility/versioned/v0.8/manifest.json ================================================ { "CurrVersion": "0.8", "MinCompatibleVersion": "0.1", "Description": "add cron_schedule, execution_status, and scheduled_execution_time to visibility", "SchemaUpdateCqlFiles": [ "add_execution_fields.sql" ] } ================================================ FILE: schema/mysql/version.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package mysql // NOTE: whenever there is a new data base schema update, plz update the following versions // Version is the MySQL database release version const Version = "0.7" // VisibilityVersion is the MySQL visibility database release version const VisibilityVersion = "0.8" ================================================ FILE: schema/pinot/README.md ================================================ There are 3 ways to create the tables in Pinot, need to make sure Pinot server is running. To start pinot server, use `docker compose -f ./docker/dev/cassandra-pinot-kafka.yml up`. Pinot has 4 components need to be started in order, so some components may fail to start for the first time. You can try again or restart the failed components in docker. 1. Manually add schema and realtime table in the UI, the configs can be found in the cadence-visibility-config file. 2. Use Pinot launcher script to add schema and table, this requires to download Apache Pinot. `sh pinot-admin.sh AddTable \ -schemaFile [path_to_cadence]/cadence/Schema/Pinot/cadence-visibility-schema.json \ -tableConfigFile [path_to_cadence]/cadence/Schema/Pinot/cadence-visibility-config.json -exec` 3. Use docker to execute the launcher script. `docker exec -it pinot-controller bin/pinot-admin.sh AddTable \ -tableConfigFile /Schema/Pinot/cadence-visibility-schema.json \ -schemaFile /Schema/Pinot/cadence-visibility-config.json -exec` The result can be checked at http://localhost:9000/#/tables ================================================ FILE: schema/pinot/cadence-visibility-config.json ================================================ { "tableName": "cadence_visibility_pinot", "tableType": "REALTIME", "segmentsConfig": { "timeColumnName": "StartTime", "timeType": "MILLISECONDS", "schemaName": "cadence_visibility_pinot", "replicasPerPartition": "1" }, "tenants": {}, "tableIndexConfig": { "jsonIndexConfigs": { "Attr": { "excludeArray": false, "disableCrossArrayUnnest": true, "includePaths": null, "excludePaths": null, "excludeFields": null } }, "loadMode": "MMAP", "streamConfigs": { "streamType": "kafka", "stream.kafka.consumer.type": "lowlevel", "stream.kafka.topic.name": "cadence-visibility-pinot", "stream.kafka.decoder.class.name": "org.apache.pinot.plugin.stream.kafka.KafkaJSONMessageDecoder", "stream.kafka.hlc.zk.connect.string": "zookeeper:2181/kafka", "stream.kafka.consumer.factory.class.name": "org.apache.pinot.plugin.stream.kafka20.KafkaConsumerFactory", "stream.kafka.zk.broker.url": "zookeeper:2181/kafka", "stream.kafka.broker.list": "kafka:9093" } }, "routing": { "instanceSelectorType": "strictReplicaGroup" }, "upsertConfig": { "mode": "FULL", "deleteRecordColumn": "IsDeleted", "deletedKeysTTL": 86400, "hashFunction": "NONE", "enableSnapshot": false }, "metadata": {} } ================================================ FILE: schema/pinot/cadence-visibility-schema.json ================================================ { "schemaName": "cadence_visibility_pinot", "primaryKeyColumns": ["RunID"], "dimensionFieldSpecs": [ { "name": "DomainID", "dataType": "STRING" }, { "name": "WorkflowID", "dataType": "STRING" }, { "name": "RunID", "dataType": "STRING" }, { "name": "WorkflowType", "dataType": "STRING" }, { "name": "CloseStatus", "dataType": "INT" }, { "name": "HistoryLength", "dataType": "INT" }, { "name": "TaskList", "dataType": "STRING" }, { "name": "IsCron", "dataType": "BOOLEAN" }, { "name": "NumClusters", "dataType": "INT" }, { "name": "ShardID", "dataType": "INT" }, { "name": "Attr", "dataType": "JSON" }, { "name": "IsDeleted", "dataType": "BOOLEAN" } ], "dateTimeFieldSpecs": [{ "name": "StartTime", "dataType": "LONG", "format" : "1:MILLISECONDS:EPOCH", "granularity": "1:MILLISECONDS" },{ "name": "CloseTime", "dataType": "LONG", "format" : "1:MILLISECONDS:EPOCH", "granularity": "1:MILLISECONDS" },{ "name": "UpdateTime", "dataType": "LONG", "format" : "1:MILLISECONDS:EPOCH", "granularity": "1:MILLISECONDS" },{ "name": "ExecutionTime", "dataType": "LONG", "format" : "1:MILLISECONDS:EPOCH", "granularity": "1:MILLISECONDS" },{ "name": "EventTimeMs", "dataType": "LONG", "format" : "1:MILLISECONDS:EPOCH", "granularity": "1:MILLISECONDS" }] } ================================================ FILE: schema/pinot/create_pinot_table.sh ================================================ #!/bin/bash # Wait for Pinot components to start sleep 30 # Add the table /opt/pinot/bin/pinot-admin.sh AddTable \ -schemaFile /schema/pinot/cadence-visibility-schema.json \ -tableConfigFile /schema/pinot/cadence-visibility-config.json \ -controllerProtocol http \ -controllerHost pinot-controller \ -controllerPort 9001 \ -exec ================================================ FILE: schema/postgres/cadence/database.sql ================================================ CREATE DATABASE cadence; ================================================ FILE: schema/postgres/cadence/schema.sql ================================================ CREATE TABLE domains( shard_id INTEGER NOT NULL DEFAULT 54321, id BYTEA NOT NULL, name VARCHAR(255) UNIQUE NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, is_global BOOLEAN NOT NULL, PRIMARY KEY(shard_id, id) ); CREATE TABLE domain_metadata ( notification_version BIGINT NOT NULL ); INSERT INTO domain_metadata (notification_version) VALUES (1); CREATE TABLE shards ( shard_id INTEGER NOT NULL, -- range_id BIGINT NOT NULL, data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id) ); CREATE TABLE transfer_tasks( shard_id INTEGER NOT NULL, task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE cross_cluster_tasks( target_cluster VARCHAR(255) NOT NULL, shard_id INTEGER NOT NULL, task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (target_cluster, shard_id, task_id) ); CREATE TABLE executions( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id TEXT NOT NULL, run_id BYTEA NOT NULL, -- next_event_id BIGINT NOT NULL, last_write_version BIGINT NOT NULL, data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id) ); CREATE TABLE current_executions( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id TEXT NOT NULL, -- run_id BYTEA NOT NULL, create_request_id VARCHAR(64) NOT NULL, state INTEGER NOT NULL, close_status INTEGER NOT NULL, start_version BIGINT NOT NULL, last_write_version BIGINT NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id) ); CREATE TABLE buffered_events ( id BIGSERIAL NOT NULL, shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id TEXT NOT NULL, run_id BYTEA NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (id) ); CREATE INDEX buffered_events_by_events_ids ON buffered_events(shard_id, domain_id, workflow_id, run_id); CREATE TABLE tasks ( domain_id BYTEA NOT NULL, task_list_name VARCHAR(255) NOT NULL, task_type SMALLINT NOT NULL, -- {Activity, Decision} task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (domain_id, task_list_name, task_type, task_id) ); CREATE TABLE task_lists ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, name VARCHAR(255) NOT NULL, task_type SMALLINT NOT NULL, -- {Activity, Decision} -- range_id BIGINT NOT NULL, data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, name, task_type) ); CREATE TABLE replication_tasks ( shard_id INTEGER NOT NULL, task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE replication_tasks_dlq ( source_cluster_name VARCHAR(255) NOT NULL, shard_id INTEGER NOT NULL, task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (source_cluster_name, shard_id, task_id) ); CREATE TABLE timer_tasks ( shard_id INTEGER NOT NULL, visibility_timestamp TIMESTAMP NOT NULL, task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, visibility_timestamp, task_id) ); CREATE TABLE activity_info_maps ( -- each row corresponds to one key of one map shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id TEXT NOT NULL, run_id BYTEA NOT NULL, schedule_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16), last_heartbeat_details BYTEA, last_heartbeat_updated_time TIMESTAMP NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, schedule_id) ); CREATE TABLE timer_info_maps ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id TEXT NOT NULL, run_id BYTEA NOT NULL, timer_id VARCHAR(255) NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, timer_id) ); CREATE TABLE child_execution_info_maps ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id TEXT NOT NULL, run_id BYTEA NOT NULL, initiated_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE request_cancel_info_maps ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id TEXT NOT NULL, run_id BYTEA NOT NULL, initiated_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE signal_info_maps ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id TEXT NOT NULL, run_id BYTEA NOT NULL, initiated_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE buffered_replication_task_maps ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id TEXT NOT NULL, run_id BYTEA NOT NULL, first_event_id BIGINT NOT NULL, -- version BIGINT NOT NULL, next_event_id BIGINT NOT NULL, history BYTEA, history_encoding VARCHAR(16) NOT NULL, new_run_history BYTEA, new_run_history_encoding VARCHAR(16) NOT NULL DEFAULT 'json', event_store_version INTEGER NOT NULL, -- indiciates which version of event store to query new_run_event_store_version INTEGER NOT NULL, -- indiciates which version of event store to query for new run(continueAsNew) PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, first_event_id) ); CREATE TABLE signals_requested_sets ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id TEXT NOT NULL, run_id BYTEA NOT NULL, signal_id VARCHAR(64) NOT NULL, -- PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, signal_id) ); -- history eventsV2: history_node stores history event data CREATE TABLE history_node ( shard_id INTEGER NOT NULL, tree_id BYTEA NOT NULL, branch_id BYTEA NOT NULL, node_id BIGINT NOT NULL, txn_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( shard_id INTEGER NOT NULL, tree_id BYTEA NOT NULL, branch_id BYTEA NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id) ); CREATE TABLE queue ( queue_type INTEGER NOT NULL, message_id BIGINT NOT NULL, message_payload BYTEA NOT NULL, PRIMARY KEY(queue_type, message_id) ); CREATE TABLE queue_metadata ( queue_type INTEGER NOT NULL, data BYTEA NOT NULL, PRIMARY KEY(queue_type) ); CREATE TABLE cluster_config ( row_type INTEGER NOT NULL, version BIGINT NOT NULL, -- timestamp TIMESTAMP NOT NULL, data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (row_type, version) ); CREATE TABLE domain_audit_log ( domain_id TEXT NOT NULL, event_id TEXT NOT NULL, -- state_before BYTEA NOT NULL, state_before_encoding TEXT NOT NULL, state_after BYTEA NOT NULL, state_after_encoding TEXT NOT NULL, operation_type INTEGER NOT NULL, created_time TIMESTAMP NOT NULL, last_updated_time TIMESTAMP NOT NULL, identity TEXT NOT NULL, identity_type TEXT NOT NULL, comment TEXT NOT NULL DEFAULT '', PRIMARY KEY (domain_id, operation_type, created_time, event_id) ); ================================================ FILE: schema/postgres/cadence/versioned/v0.1/base.sql ================================================ CREATE TABLE domains( shard_id INTEGER NOT NULL DEFAULT 54321, id BYTEA NOT NULL, name VARCHAR(255) UNIQUE NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, is_global BOOLEAN NOT NULL, PRIMARY KEY(shard_id, id) ); CREATE TABLE domain_metadata ( notification_version BIGINT NOT NULL ); INSERT INTO domain_metadata (notification_version) VALUES (1); CREATE TABLE shards ( shard_id INTEGER NOT NULL, -- range_id BIGINT NOT NULL, data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id) ); CREATE TABLE transfer_tasks( shard_id INTEGER NOT NULL, task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE executions( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BYTEA NOT NULL, -- next_event_id BIGINT NOT NULL, last_write_version BIGINT NOT NULL, data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id) ); CREATE TABLE current_executions( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id VARCHAR(255) NOT NULL, -- run_id BYTEA NOT NULL, create_request_id VARCHAR(64) NOT NULL, state INTEGER NOT NULL, close_status INTEGER NOT NULL, start_version BIGINT NOT NULL, last_write_version BIGINT NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id) ); CREATE TABLE buffered_events ( id BIGSERIAL NOT NULL, shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BYTEA NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (id) ); CREATE INDEX buffered_events_by_events_ids ON buffered_events(shard_id, domain_id, workflow_id, run_id); CREATE TABLE tasks ( domain_id BYTEA NOT NULL, task_list_name VARCHAR(255) NOT NULL, task_type SMALLINT NOT NULL, -- {Activity, Decision} task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (domain_id, task_list_name, task_type, task_id) ); CREATE TABLE task_lists ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, name VARCHAR(255) NOT NULL, task_type SMALLINT NOT NULL, -- {Activity, Decision} -- range_id BIGINT NOT NULL, data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, name, task_type) ); CREATE TABLE replication_tasks ( shard_id INTEGER NOT NULL, task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE timer_tasks ( shard_id INTEGER NOT NULL, visibility_timestamp TIMESTAMP NOT NULL, task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, visibility_timestamp, task_id) ); CREATE TABLE activity_info_maps ( -- each row corresponds to one key of one map shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BYTEA NOT NULL, schedule_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16), last_heartbeat_details BYTEA, last_heartbeat_updated_time TIMESTAMP NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, schedule_id) ); CREATE TABLE timer_info_maps ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BYTEA NOT NULL, timer_id VARCHAR(255) NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, timer_id) ); CREATE TABLE child_execution_info_maps ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BYTEA NOT NULL, initiated_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE request_cancel_info_maps ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BYTEA NOT NULL, initiated_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE signal_info_maps ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BYTEA NOT NULL, initiated_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE buffered_replication_task_maps ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BYTEA NOT NULL, first_event_id BIGINT NOT NULL, -- version BIGINT NOT NULL, next_event_id BIGINT NOT NULL, history BYTEA, history_encoding VARCHAR(16) NOT NULL, new_run_history BYTEA, new_run_history_encoding VARCHAR(16) NOT NULL DEFAULT 'json', event_store_version INTEGER NOT NULL, -- indiciates which version of event store to query new_run_event_store_version INTEGER NOT NULL, -- indiciates which version of event store to query for new run(continueAsNew) PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, first_event_id) ); CREATE TABLE signals_requested_sets ( shard_id INTEGER NOT NULL, domain_id BYTEA NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BYTEA NOT NULL, signal_id VARCHAR(64) NOT NULL, -- PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, signal_id) ); -- history eventsV2: history_node stores history event data CREATE TABLE history_node ( shard_id INTEGER NOT NULL, tree_id BYTEA NOT NULL, branch_id BYTEA NOT NULL, node_id BIGINT NOT NULL, txn_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( shard_id INTEGER NOT NULL, tree_id BYTEA NOT NULL, branch_id BYTEA NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id) ); ================================================ FILE: schema/postgres/cadence/versioned/v0.1/manifest.json ================================================ { "CurrVersion": "0.1", "MinCompatibleVersion": "0.1", "Description": "base version of schema", "SchemaUpdateCqlFiles": [ "base.sql" ] } ================================================ FILE: schema/postgres/cadence/versioned/v0.2/manifest.json ================================================ { "CurrVersion": "0.2", "MinCompatibleVersion": "0.2", "Description": "add queue and queue_metadata table", "SchemaUpdateCqlFiles": [ "queue.sql" ] } ================================================ FILE: schema/postgres/cadence/versioned/v0.2/queue.sql ================================================ CREATE TABLE queue ( queue_type INTEGER NOT NULL, message_id BIGINT NOT NULL, message_payload BYTEA NOT NULL, PRIMARY KEY(queue_type, message_id) ); CREATE TABLE queue_metadata ( queue_type INTEGER NOT NULL, data BYTEA NOT NULL, PRIMARY KEY(queue_type) ); ================================================ FILE: schema/postgres/cadence/versioned/v0.3/manifest.json ================================================ { "CurrVersion": "0.3", "MinCompatibleVersion": "0.3", "Description": "add replication_tasks_dlq table", "SchemaUpdateCqlFiles": [ "replication_tasks_dlq.sql" ] } ================================================ FILE: schema/postgres/cadence/versioned/v0.3/replication_tasks_dlq.sql ================================================ CREATE TABLE replication_tasks_dlq ( source_cluster_name VARCHAR(255) NOT NULL, shard_id INTEGER NOT NULL, task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (source_cluster_name, shard_id, task_id) ); ================================================ FILE: schema/postgres/cadence/versioned/v0.4/cross_cluster_table.sql ================================================ CREATE TABLE cross_cluster_tasks( target_cluster VARCHAR(255) NOT NULL, shard_id INTEGER NOT NULL, task_id BIGINT NOT NULL, -- data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (target_cluster, shard_id, task_id) ); ================================================ FILE: schema/postgres/cadence/versioned/v0.4/manifest.json ================================================ { "CurrVersion": "0.4", "MinCompatibleVersion": "0.4", "Description": "create cross cluster table", "SchemaUpdateCqlFiles": [ "cross_cluster_table.sql" ] } ================================================ FILE: schema/postgres/cadence/versioned/v0.5/cluster_config.sql ================================================ CREATE TABLE cluster_config ( row_type INTEGER NOT NULL, version BIGINT NOT NULL, -- timestamp TIMESTAMP NOT NULL, data BYTEA NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (row_type, version) ); ================================================ FILE: schema/postgres/cadence/versioned/v0.5/manifest.json ================================================ { "CurrVersion": "0.5", "MinCompatibleVersion": "0.5", "Description": "create cluster config table", "SchemaUpdateCqlFiles": [ "cluster_config.sql" ] } ================================================ FILE: schema/postgres/cadence/versioned/v0.6/extend_workflow_id_length.sql ================================================ ALTER TABLE executions ALTER workflow_id TYPE text; ALTER TABLE current_executions ALTER workflow_id TYPE text; ALTER TABLE buffered_events ALTER workflow_id TYPE text; ALTER TABLE activity_info_maps ALTER workflow_id TYPE text; ALTER TABLE timer_info_maps ALTER workflow_id TYPE text; ALTER TABLE child_execution_info_maps ALTER workflow_id TYPE text; ALTER TABLE request_cancel_info_maps ALTER workflow_id TYPE text; ALTER TABLE signal_info_maps ALTER workflow_id TYPE text; ALTER TABLE buffered_replication_task_maps ALTER workflow_id TYPE text; ALTER TABLE signals_requested_sets ALTER workflow_id TYPE text; ================================================ FILE: schema/postgres/cadence/versioned/v0.6/manifest.json ================================================ { "CurrVersion": "0.6", "MinCompatibleVersion": "0.6", "Description": "update type of workflow_id to text to remove length restriction", "SchemaUpdateCqlFiles": [ "extend_workflow_id_length.sql" ] } ================================================ FILE: schema/postgres/cadence/versioned/v0.7/domain_audit_log.sql ================================================ CREATE TABLE domain_audit_log ( domain_id TEXT NOT NULL, event_id TEXT NOT NULL, -- state_before BYTEA NOT NULL, state_before_encoding TEXT NOT NULL, state_after BYTEA NOT NULL, state_after_encoding TEXT NOT NULL, operation_type INTEGER NOT NULL, created_time TIMESTAMP NOT NULL, last_updated_time TIMESTAMP NOT NULL, identity TEXT NOT NULL, identity_type TEXT NOT NULL, comment TEXT NOT NULL DEFAULT '', PRIMARY KEY (domain_id, operation_type, created_time, event_id) ); ================================================ FILE: schema/postgres/cadence/versioned/v0.7/manifest.json ================================================ { "CurrVersion": "0.7", "MinCompatibleVersion": "0.7", "Description": "create domain_audit_log table", "SchemaUpdateCqlFiles": [ "domain_audit_log.sql" ] } ================================================ FILE: schema/postgres/embed.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres import "embed" //go:embed cadence/* visibility/* var SchemaFS embed.FS ================================================ FILE: schema/postgres/version.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package postgres // NOTE: whenever there is a new data base schema update, plz update the following versions // Version is the Postgres database release version // Cadence supports both MySQL and Postgres officially, so upgrade should be perform for both MySQL and Postgres const Version = "0.7" // VisibilityVersion is the Postgres visibility database release version // Cadence supports both MySQL and Postgres officially, so upgrade should be perform for both MySQL and Postgres const VisibilityVersion = "0.8" ================================================ FILE: schema/postgres/visibility/database.sql ================================================ CREATE DATABASE cadence_visibility; ================================================ FILE: schema/postgres/visibility/schema.sql ================================================ CREATE TABLE executions_visibility ( domain_id CHAR(64) NOT NULL, run_id CHAR(64) NOT NULL, start_time TIMESTAMP NOT NULL, execution_time TIMESTAMP NOT NULL, workflow_id TEXT NOT NULL, workflow_type_name VARCHAR(255) NOT NULL, close_status INTEGER, -- enum WorkflowExecutionCloseStatus {COMPLETED, FAILED, CANCELED, TERMINATED, CONTINUED_AS_NEW, TIMED_OUT} close_time TIMESTAMP NULL, history_length BIGINT, memo BYTEA, encoding VARCHAR(64) NOT NULL, task_list VARCHAR(255) DEFAULT '' NOT NULL, is_cron BOOLEAN DEFAULT false NOT NULL, num_clusters INTEGER NULL, update_time TIMESTAMP NULL, shard_id INTEGER NULL, cron_schedule VARCHAR(255) NULL, execution_status INTEGER NULL, scheduled_execution_time TIMESTAMP NULL, PRIMARY KEY (domain_id, run_id) ); CREATE INDEX by_type_start_time ON executions_visibility (domain_id, workflow_type_name, close_status, start_time DESC, run_id); CREATE INDEX by_workflow_id_start_time ON executions_visibility (domain_id, workflow_id, close_status, start_time DESC, run_id); CREATE INDEX by_status_by_close_time ON executions_visibility (domain_id, close_status, start_time DESC, run_id); CREATE INDEX by_close_time_by_status ON executions_visibility (domain_id, close_time DESC, run_id, close_status); ================================================ FILE: schema/postgres/visibility/versioned/v0.1/base.sql ================================================ CREATE TABLE executions_visibility ( domain_id CHAR(64) NOT NULL, run_id CHAR(64) NOT NULL, start_time TIMESTAMP NOT NULL, execution_time TIMESTAMP NOT NULL, workflow_id VARCHAR(255) NOT NULL, workflow_type_name VARCHAR(255) NOT NULL, close_status INTEGER, -- enum WorkflowExecutionCloseStatus {COMPLETED, FAILED, CANCELED, TERMINATED, CONTINUED_AS_NEW, TIMED_OUT} close_time TIMESTAMP NULL, history_length BIGINT, memo BYTEA, encoding VARCHAR(64) NOT NULL, PRIMARY KEY (domain_id, run_id) ); CREATE INDEX by_type_start_time ON executions_visibility (domain_id, workflow_type_name, close_status, start_time DESC, run_id); CREATE INDEX by_workflow_id_start_time ON executions_visibility (domain_id, workflow_id, close_status, start_time DESC, run_id); CREATE INDEX by_status_by_close_time ON executions_visibility (domain_id, close_status, start_time DESC, run_id); ================================================ FILE: schema/postgres/visibility/versioned/v0.1/manifest.json ================================================ { "CurrVersion": "0.1", "MinCompatibleVersion": "0.1", "Description": "base version of visibility schema", "SchemaUpdateCqlFiles": [ "base.sql" ] } ================================================ FILE: schema/postgres/visibility/versioned/v0.2/add_task_list.sql ================================================ ALTER TABLE executions_visibility ADD task_list varchar(255) DEFAULT ''; ================================================ FILE: schema/postgres/visibility/versioned/v0.2/manifest.json ================================================ { "CurrVersion": "0.2", "MinCompatibleVersion": "0.2", "Description": "add task_list field to visibility", "SchemaUpdateCqlFiles": [ "add_task_list.sql" ] } ================================================ FILE: schema/postgres/visibility/versioned/v0.3/manifest.json ================================================ { "CurrVersion": "0.3", "MinCompatibleVersion": "0.2", "Description": "add close time and closed state index", "SchemaUpdateCqlFiles": [ "vs_index.sql" ] } ================================================ FILE: schema/postgres/visibility/versioned/v0.3/vs_index.sql ================================================ CREATE INDEX by_close_time_by_status ON executions_visibility (domain_id, close_time DESC, run_id, close_status); ================================================ FILE: schema/postgres/visibility/versioned/v0.4/add_is_cron.sql ================================================ ALTER TABLE executions_visibility ADD is_cron boolean DEFAULT false; ================================================ FILE: schema/postgres/visibility/versioned/v0.4/manifest.json ================================================ { "CurrVersion": "0.4", "MinCompatibleVersion": "0.4", "Description": "add is_cron field to visibility", "SchemaUpdateCqlFiles": [ "add_is_cron.sql" ] } ================================================ FILE: schema/postgres/visibility/versioned/v0.5/manifest.json ================================================ { "CurrVersion": "0.5", "MinCompatibleVersion": "0.5", "Description": "add num_clusters field to visibility", "SchemaUpdateCqlFiles": [ "num_clusters.sql" ] } ================================================ FILE: schema/postgres/visibility/versioned/v0.5/num_clusters.sql ================================================ ALTER TABLE executions_visibility ADD num_clusters INTEGER NULL; ================================================ FILE: schema/postgres/visibility/versioned/v0.6/add_update_time.sql ================================================ ALTER TABLE executions_visibility ADD update_time TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP; ================================================ FILE: schema/postgres/visibility/versioned/v0.6/manifest.json ================================================ { "CurrVersion": "0.6", "MinCompatibleVersion": "0.6", "Description": "add update_time field to visibility", "SchemaUpdateCqlFiles": [ "add_update_time.sql" ] } ================================================ FILE: schema/postgres/visibility/versioned/v0.7/add_shard_id.sql ================================================ ALTER TABLE executions_visibility ADD shard_id INTEGER NULL DEFAULT -1; ================================================ FILE: schema/postgres/visibility/versioned/v0.7/manifest.json ================================================ { "CurrVersion": "0.7", "MinCompatibleVersion": "0.7", "Description": "add shard_id field to visibility", "SchemaUpdateCqlFiles": [ "add_shard_id.sql" ] } ================================================ FILE: schema/postgres/visibility/versioned/v0.8/extend_workflow_id_length.sql ================================================ ALTER TABLE executions_visibility ALTER workflow_id TYPE TEXT; ================================================ FILE: schema/postgres/visibility/versioned/v0.8/manifest.json ================================================ { "CurrVersion": "0.8", "MinCompatibleVersion": "0.8", "Description": "update type of workflow_id to text to remove length restriction", "SchemaUpdateCqlFiles": [ "extend_workflow_id_length.sql" ] } ================================================ FILE: schema/postgres/visibility/versioned/v0.9/add_execution_fields.sql ================================================ -- Add cron_schedule field to track cron workflow schedules ALTER TABLE executions_visibility ADD cron_schedule VARCHAR(255) NULL; -- Add execution_status field to track workflow execution status (Pending, Started, or close status) ALTER TABLE executions_visibility ADD execution_status INTEGER NULL; -- Add scheduled_execution_time field to track the actual scheduled execution time (start_time + first_decision_task_backoff) ALTER TABLE executions_visibility ADD scheduled_execution_time TIMESTAMP NULL; ================================================ FILE: schema/postgres/visibility/versioned/v0.9/manifest.json ================================================ { "CurrVersion": "0.9", "MinCompatibleVersion": "0.1", "Description": "add cron_schedule, execution_status, and scheduled_execution_time to visibility", "SchemaUpdateCqlFiles": [ "add_execution_fields.sql" ] } ================================================ FILE: schema/sqlite/cadence/schema.sql ================================================ CREATE TABLE domains ( shard_id INT NOT NULL DEFAULT 54321, id BINARY(16) NOT NULL, name VARCHAR(255) UNIQUE NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, is_global TINYINT(1) NOT NULL, PRIMARY KEY (shard_id, id) ); CREATE TABLE domain_metadata ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, notification_version BIGINT NOT NULL ); INSERT INTO domain_metadata (notification_version) VALUES (1); CREATE TABLE shards ( shard_id INT NOT NULL, -- range_id BIGINT NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id) ); CREATE TABLE transfer_tasks ( shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE cross_cluster_tasks ( target_cluster VARCHAR(255) NOT NULL, shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (target_cluster, shard_id, task_id) ); CREATE TABLE executions ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, -- next_event_id BIGINT NOT NULL, last_write_version BIGINT NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id) ); CREATE TABLE current_executions ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, -- run_id BINARY(16) NOT NULL, create_request_id VARCHAR(64) NOT NULL, state INT NOT NULL, close_status INT NOT NULL, start_version BIGINT NOT NULL, last_write_version BIGINT NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id) ); CREATE TABLE buffered_events ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL ); CREATE INDEX buffered_events_by_events_ids ON buffered_events (shard_id, domain_id, workflow_id, run_id); CREATE TABLE tasks ( domain_id BINARY(16) NOT NULL, task_list_name VARCHAR(255) NOT NULL, task_type TINYINT NOT NULL, -- {Activity, Decision} task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (domain_id, task_list_name, task_type, task_id) ); CREATE TABLE task_lists ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, name VARCHAR(255) NOT NULL, task_type TINYINT NOT NULL, -- {Activity, Decision} -- range_id BIGINT NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, name, task_type) ); CREATE TABLE replication_tasks ( shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE replication_tasks_dlq ( source_cluster_name VARCHAR(255) NOT NULL, shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (source_cluster_name, shard_id, task_id) ); CREATE TABLE timer_tasks ( shard_id INT NOT NULL, visibility_timestamp DATETIME(6) NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, visibility_timestamp, task_id) ); CREATE TABLE activity_info_maps ( -- each row corresponds to one key of one map shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, schedule_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), last_heartbeat_details BLOB, last_heartbeat_updated_time DATETIME(6) NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, schedule_id) ); CREATE TABLE timer_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, timer_id VARCHAR(255) NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, timer_id) ); CREATE TABLE child_execution_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE request_cancel_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE signal_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE buffered_replication_task_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, first_event_id BIGINT NOT NULL, -- version BIGINT NOT NULL, next_event_id BIGINT NOT NULL, history MEDIUMBLOB, history_encoding VARCHAR(16) NOT NULL, new_run_history MEDIUMBLOB, new_run_history_encoding VARCHAR(16) NOT NULL DEFAULT 'json', event_store_version INT NOT NULL, -- indicates which version of event store to query new_run_event_store_version INT NOT NULL, -- indicates which version of event store to query for new run(continueAsNew) PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, first_event_id) ); CREATE TABLE signals_requested_sets ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, signal_id VARCHAR(64) NOT NULL, -- PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, signal_id) ); -- history eventsV2: history_node stores history event data CREATE TABLE history_node ( shard_id INT NOT NULL, tree_id BINARY(16) NOT NULL, branch_id BINARY(16) NOT NULL, node_id BIGINT NOT NULL, txn_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( shard_id INT NOT NULL, tree_id BINARY(16) NOT NULL, branch_id BINARY(16) NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id) ); CREATE TABLE queue ( queue_type INT NOT NULL, message_id BIGINT NOT NULL, message_payload MEDIUMBLOB NOT NULL, PRIMARY KEY (queue_type, message_id) ); CREATE TABLE queue_metadata ( queue_type INT NOT NULL, data MEDIUMBLOB NOT NULL, PRIMARY KEY (queue_type) ); CREATE TABLE cluster_config ( row_type INT NOT NULL, version BIGINT NOT NULL, -- timestamp DATETIME(6) NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (row_type, version) ); ================================================ FILE: schema/sqlite/cadence/versioned/v0.1/base.sql ================================================ CREATE TABLE domains ( shard_id INT NOT NULL DEFAULT 54321, id BINARY(16) NOT NULL, name VARCHAR(255) UNIQUE NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, is_global TINYINT(1) NOT NULL, PRIMARY KEY (shard_id, id) ); CREATE TABLE domain_metadata ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, notification_version BIGINT NOT NULL ); INSERT INTO domain_metadata (notification_version) VALUES (1); CREATE TABLE shards ( shard_id INT NOT NULL, -- range_id BIGINT NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id) ); CREATE TABLE transfer_tasks ( shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE cross_cluster_tasks ( target_cluster VARCHAR(255) NOT NULL, shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (target_cluster, shard_id, task_id) ); CREATE TABLE executions ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, -- next_event_id BIGINT NOT NULL, last_write_version BIGINT NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id) ); CREATE TABLE current_executions ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, -- run_id BINARY(16) NOT NULL, create_request_id VARCHAR(64) NOT NULL, state INT NOT NULL, close_status INT NOT NULL, start_version BIGINT NOT NULL, last_write_version BIGINT NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id) ); CREATE TABLE buffered_events ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL ); CREATE INDEX buffered_events_by_events_ids ON buffered_events (shard_id, domain_id, workflow_id, run_id); CREATE TABLE tasks ( domain_id BINARY(16) NOT NULL, task_list_name VARCHAR(255) NOT NULL, task_type TINYINT NOT NULL, -- {Activity, Decision} task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (domain_id, task_list_name, task_type, task_id) ); CREATE TABLE task_lists ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, name VARCHAR(255) NOT NULL, task_type TINYINT NOT NULL, -- {Activity, Decision} -- range_id BIGINT NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, domain_id, name, task_type) ); CREATE TABLE replication_tasks ( shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, task_id) ); CREATE TABLE replication_tasks_dlq ( source_cluster_name VARCHAR(255) NOT NULL, shard_id INT NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (source_cluster_name, shard_id, task_id) ); CREATE TABLE timer_tasks ( shard_id INT NOT NULL, visibility_timestamp DATETIME(6) NOT NULL, task_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, visibility_timestamp, task_id) ); CREATE TABLE activity_info_maps ( -- each row corresponds to one key of one map shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, schedule_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), last_heartbeat_details BLOB, last_heartbeat_updated_time DATETIME(6) NOT NULL, PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, schedule_id) ); CREATE TABLE timer_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, timer_id VARCHAR(255) NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, timer_id) ); CREATE TABLE child_execution_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE request_cancel_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE signal_info_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, initiated_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16), PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, initiated_id) ); CREATE TABLE buffered_replication_task_maps ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, first_event_id BIGINT NOT NULL, -- version BIGINT NOT NULL, next_event_id BIGINT NOT NULL, history MEDIUMBLOB, history_encoding VARCHAR(16) NOT NULL, new_run_history MEDIUMBLOB, new_run_history_encoding VARCHAR(16) NOT NULL DEFAULT 'json', event_store_version INT NOT NULL, -- indicates which version of event store to query new_run_event_store_version INT NOT NULL, -- indicates which version of event store to query for new run(continueAsNew) PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, first_event_id) ); CREATE TABLE signals_requested_sets ( shard_id INT NOT NULL, domain_id BINARY(16) NOT NULL, workflow_id VARCHAR(255) NOT NULL, run_id BINARY(16) NOT NULL, signal_id VARCHAR(64) NOT NULL, -- PRIMARY KEY (shard_id, domain_id, workflow_id, run_id, signal_id) ); -- history eventsV2: history_node stores history event data CREATE TABLE history_node ( shard_id INT NOT NULL, tree_id BINARY(16) NOT NULL, branch_id BINARY(16) NOT NULL, node_id BIGINT NOT NULL, txn_id BIGINT NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id, node_id, txn_id) ); -- history eventsV2: history_tree stores branch metadata CREATE TABLE history_tree ( shard_id INT NOT NULL, tree_id BINARY(16) NOT NULL, branch_id BINARY(16) NOT NULL, -- data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (shard_id, tree_id, branch_id) ); CREATE TABLE queue ( queue_type INT NOT NULL, message_id BIGINT NOT NULL, message_payload MEDIUMBLOB NOT NULL, PRIMARY KEY (queue_type, message_id) ); CREATE TABLE queue_metadata ( queue_type INT NOT NULL, data MEDIUMBLOB NOT NULL, PRIMARY KEY (queue_type) ); CREATE TABLE cluster_config ( row_type INT NOT NULL, version BIGINT NOT NULL, -- timestamp DATETIME(6) NOT NULL, data MEDIUMBLOB NOT NULL, data_encoding VARCHAR(16) NOT NULL, PRIMARY KEY (row_type, version) ); ================================================ FILE: schema/sqlite/cadence/versioned/v0.1/manifest.json ================================================ { "CurrVersion": "0.1", "MinCompatibleVersion": "0.1", "Description": "base version of schema", "SchemaUpdateCqlFiles": [ "base.sql" ] } ================================================ FILE: schema/sqlite/embed.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sqlite import "embed" //go:embed cadence/* visibility/* var SchemaFS embed.FS ================================================ FILE: schema/sqlite/version.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package sqlite // NOTE: whenever there is a new data base schema update, plz update the following versions // Version is the SQLite database release version const Version = "0.1" // VisibilityVersion is the SQLite visibility database release version const VisibilityVersion = "0.1" ================================================ FILE: schema/sqlite/visibility/schema.sql ================================================ CREATE TABLE executions_visibility ( domain_id CHAR(64) NOT NULL, run_id CHAR(64) NOT NULL, start_time DATETIME(6) NOT NULL, execution_time DATETIME(6) NOT NULL, workflow_id VARCHAR(255) NOT NULL, workflow_type_name VARCHAR(255) NOT NULL, close_status INT, -- enum WorkflowExecutionCloseStatus {COMPLETED, FAILED, CANCELED, TERMINATED, CONTINUED_AS_NEW, TIMED_OUT} close_time DATETIME(6) NULL, history_length BIGINT, memo BLOB, encoding VARCHAR(64) NOT NULL, task_list VARCHAR(255) DEFAULT '' NOT NULL, is_cron BOOLEAN DEFAULT false NOT NULL, num_clusters INT NULL, update_time DATETIME(6) NULL, shard_id INT NULL, cron_schedule TEXT NULL, execution_status INT NULL, scheduled_execution_time TIMESTAMP NULL, PRIMARY KEY (domain_id, run_id) ); CREATE INDEX by_type_start_time ON executions_visibility (domain_id, workflow_type_name, close_status, start_time DESC, run_id); CREATE INDEX by_workflow_id_start_time ON executions_visibility (domain_id, workflow_id, close_status, start_time DESC, run_id); CREATE INDEX by_status_by_close_time ON executions_visibility (domain_id, close_status, start_time DESC, run_id); CREATE INDEX by_close_time_by_status ON executions_visibility (domain_id, close_time DESC, run_id, close_status); ================================================ FILE: schema/sqlite/visibility/versioned/v0.1/base.sql ================================================ CREATE TABLE executions_visibility ( domain_id CHAR(64) NOT NULL, run_id CHAR(64) NOT NULL, start_time DATETIME(6) NOT NULL, execution_time DATETIME(6) NOT NULL, workflow_id VARCHAR(255) NOT NULL, workflow_type_name VARCHAR(255) NOT NULL, close_status INT, -- enum WorkflowExecutionCloseStatus {COMPLETED, FAILED, CANCELED, TERMINATED, CONTINUED_AS_NEW, TIMED_OUT} close_time DATETIME(6) NULL, history_length BIGINT, memo BLOB, encoding VARCHAR(64) NOT NULL, task_list VARCHAR(255) DEFAULT '' NOT NULL, is_cron BOOLEAN DEFAULT false NOT NULL, num_clusters INT NULL, update_time DATETIME(6) NULL, shard_id INT NULL, PRIMARY KEY (domain_id, run_id) ); CREATE INDEX by_type_start_time ON executions_visibility (domain_id, workflow_type_name, close_status, start_time DESC, run_id); CREATE INDEX by_workflow_id_start_time ON executions_visibility (domain_id, workflow_id, close_status, start_time DESC, run_id); CREATE INDEX by_status_by_close_time ON executions_visibility (domain_id, close_status, start_time DESC, run_id); CREATE INDEX by_close_time_by_status ON executions_visibility (domain_id, close_time DESC, run_id, close_status); ================================================ FILE: schema/sqlite/visibility/versioned/v0.1/manifest.json ================================================ { "CurrVersion": "0.1", "MinCompatibleVersion": "0.1", "Description": "base version of visibility schema", "SchemaUpdateCqlFiles": [ "base.sql" ] } ================================================ FILE: schema/sqlite/visibility/versioned/v0.2/add_execution_fields.sql ================================================ -- Add cron_schedule field to track cron workflow schedules ALTER TABLE executions_visibility ADD cron_schedule TEXT; -- Add execution_status field to track workflow execution status (Pending, Started, or close status) ALTER TABLE executions_visibility ADD execution_status INTEGER; -- Add scheduled_execution_time field to track the actual scheduled execution time (start_time + first_decision_task_backoff) ALTER TABLE executions_visibility ADD scheduled_execution_time TIMESTAMP; ================================================ FILE: schema/sqlite/visibility/versioned/v0.2/manifest.json ================================================ { "CurrVersion": "0.2", "MinCompatibleVersion": "0.1", "Description": "add cron_schedule, execution_status, and scheduled_execution_time to visibility", "SchemaUpdateCqlFiles": [ "add_execution_fields.sql" ] } ================================================ FILE: scripts/build-with-ldflags.sh ================================================ #!/bin/bash set -eo pipefail # add verbose env var for debugging test -n "$V" && set -x # must be on a separate line to trigger -e if it fails FLAGS="$(./scripts/get-ldflags.sh LDFLAG)" exec go build -ldflags "$FLAGS" "$@" ================================================ FILE: scripts/check-go-toolchain.sh ================================================ #!/usr/bin/env bash set -eo pipefail fail=0 bad() { echo -e "$@" >/dev/stderr fail=1 } if [[ $V == 1 ]]; then set -x fi target="${1#go}" root="$(git rev-parse --show-toplevel)" # this SHOULD match the dependencies in the goversion-lint check to avoid skipping it # check dockerfiles while read file; do # find "FROM golang:1.22.3-alpine3.18 ..." lines line="$(grep -i 'from golang:' "$file")" # remove "from golang:" prefix version="${line#*golang:}" # remove "-alpine..." suffix version="${version%-*}" # and make sure it matches if [[ "$version" != "$target" ]]; then bad "Wrong Go version in file $file:\n\t$line" fi done < <( find "$root" -name Dockerfile ) declare -a codecov_files=( "$root/.github/workflows/codecov-on-pr.yml" "$root/.github/workflows/codecov-on-master.yml" ); for codecov_file in "${codecov_files[@]}"; do # check workflows codecov_file="$root/.github/workflows/codecov-on-pr.yml" codecov_line="$(grep 'go-version:' "$codecov_file")" codecov_version="${codecov_line#*go-version: }" if [[ "$codecov_version" != "$target" ]]; then bad "Wrong Go version in file $codecov_file:\n\t$codecov_line" fi done if [[ $fail == 1 ]]; then bad "Makefile pins Go to go${target}, Dockerfiles and GitHub workflows should too." bad "Non-matching versions lead to pointless double-downloading to get the correct version." exit 1 fi ================================================ FILE: scripts/check-gomod-version.sh ================================================ #!/usr/bin/env bash set -eo pipefail [[ $2 = "-v" ]] && set -x; # go.work and `go list -modfile=...` seem to interact badly, and complain about duplicates. # the good news is that you can just drop that and `cd` to the folder and it works. if ! gomod="$(go list -mod=readonly -f '{{ .Module }}' "$1")"; then >&2 echo 'Error checking main go.mod.' exit 1 fi if ! toolmod="$(cd internal/tools; GOTOOLCHAIN=auto go list -mod=readonly -f '{{ .Module }}' "$1")"; then >&2 echo 'Error checking tools go.mod, cd to internal/tools to modify it.' exit 1 fi if [[ $gomod != "$toolmod" ]]; then >&2 echo "error: mismatched go.mod and tools go.mod" >&2 echo "ensure internal/tools/go.mod contains the same version as go.mod and try again:" >&2 echo -e "\t$gomod" exit 1 fi ================================================ FILE: scripts/docker-build.sh ================================================ #!/bin/bash set -ex # This script can be used to locally build all the images. Do NOT push these images. Dockerhub registry should only be updated via github workflow. echo "Building docker images..." docker build . -f Dockerfile -t ubercadence/server:master --build-arg TARGET=server docker build . -f Dockerfile -t ubercadence/server:master-auto-setup --build-arg TARGET=auto-setup docker build . -f Dockerfile -t ubercadence/cli:master --build-arg TARGET=cli docker build . -f Dockerfile -t ubercadence/cadence-bench:master --build-arg TARGET=bench docker build . -f Dockerfile -t ubercadence/cadence-canary:master --build-arg TARGET=canary ================================================ FILE: scripts/generate_cluster_attributes.sh ================================================ #!/bin/bash # Script to generate a domain update command with 3000 cluster attributes # Each attribute is a unique city assigned to one of three clusters set -e DOMAIN="${1:-test25}" NUM_ATTRIBUTES="${2:-3000}" echo "Generating command with $NUM_ATTRIBUTES cluster attributes for domain: $DOMAIN" echo "" # Array of city names from around the world # We'll use these as base names and add suffixes to reach 3000 CITIES=( # Major world cities "london" "paris" "berlin" "madrid" "rome" "moscow" "istanbul" "athens" "vienna" "prague" "amsterdam" "brussels" "copenhagen" "dublin" "helsinki" "lisbon" "oslo" "stockholm" "warsaw" "budapest" "tokyo" "beijing" "shanghai" "seoul" "bangkok" "singapore" "hong_kong" "mumbai" "delhi" "bangalore" "sydney" "melbourne" "auckland" "wellington" "brisbane" "perth" "adelaide" "canberra" "new_york" "los_angeles" "chicago" "houston" "phoenix" "philadelphia" "san_antonio" "san_diego" "dallas" "san_jose" "austin" "jacksonville" "fort_worth" "columbus" "charlotte" "san_francisco" "indianapolis" "seattle" "denver" "washington" "boston" "el_paso" "nashville" "detroit" "portland" "las_vegas" "memphis" "louisville" "baltimore" "milwaukee" "toronto" "montreal" "vancouver" "calgary" "ottawa" "edmonton" "winnipeg" "quebec_city" "hamilton" "victoria" "mexico_city" "guadalajara" "monterrey" "puebla" "tijuana" "leon" "ciudad_juarez" "zapopan" "merida" "san_luis_potosi" "sao_paulo" "rio_de_janeiro" "brasilia" "salvador" "fortaleza" "belo_horizonte" "manaus" "curitiba" "recife" "porto_alegre" "buenos_aires" "cordoba" "rosario" "mendoza" "la_plata" "mar_del_plata" "salta" "santa_fe" "san_juan" "resistencia" "bogota" "medellin" "cali" "barranquilla" "cartagena" "cucuta" "bucaramanga" "pereira" "ibague" "santa_marta" "lima" "arequipa" "trujillo" "chiclayo" "piura" "iquitos" "cusco" "huancayo" "tacna" "ica" "santiago" "valparaiso" "concepcion" "la_serena" "antofagasta" "temuco" "rancagua" "talca" "arica" "puerto_montt" "caracas" "maracaibo" "valencia" "barquisimeto" "maracay" "ciudad_guayana" "maturin" "barcelona" "puerto_la_cruz" "quito" "guayaquil" "cuenca" "santo_domingo" "machala" "duran" "portoviejo" "manta" "loja" "ambato" "cairo" "alexandria" "giza" "shubra_el_kheima" "port_said" "suez" "luxor" "al_mahallah_al_kubra" "tanta" "asyut" "johannesburg" "cape_town" "durban" "pretoria" "port_elizabeth" "pietermaritzburg" "benoni" "tembisa" "east_london" "vereeniging" "lagos" "kano" "ibadan" "kaduna" "port_harcourt" "benin_city" "maiduguri" "zaria" "aba" "jos" "nairobi" "mombasa" "kisumu" "nakuru" "eldoret" "thika" "malindi" "kitale" "garissa" "kakamega" "addis_ababa" "dire_dawa" "mekele" "gondar" "awasa" "bahir_dar" "dessie" "jimma" "jijiga" "shashamane" "kinshasa" "lubumbashi" "mbuji_mayi" "kisangani" "kananga" "likasi" "kolwezi" "tshikapa" "beni" "bukavu" "dar_es_salaam" "mwanza" "arusha" "dodoma" "mbeya" "morogoro" "tanga" "kahama" "tabora" "zanzibar" "kampala" "gulu" "lira" "mbarara" "jinja" "bwizibwera" "mbale" "mukono" "kasese" "masaka" "khartoum" "omdurman" "khartoum_north" "nyala" "port_sudan" "kassala" "el_obeid" "gedaref" "wad_medani" "al_qadarif" "accra" "kumasi" "tamale" "sekondi_takoradi" "ashaiman" "sunyani" "cape_coast" "obuasi" "teshie" "tema" "algiers" "oran" "constantine" "annaba" "blida" "batna" "djelfa" "setif" "sidi_bel_abbes" "biskra" "casablanca" "rabat" "fes" "marrakech" "agadir" "tangier" "meknes" "oujda" "kenitra" "tetouan" "tunis" "sfax" "sousse" "ettadhamen" "kairouan" "bizerte" "gabes" "ariana" "gafsa" "kasserine" "tripoli" "benghazi" "misrata" "tarhuna" "al_khums" "az_zawiya" "ajdabiya" "tobruk" "Sabha" "gharyan" "tehran" "mashhad" "isfahan" "karaj" "tabriz" "shiraz" "qom" "ahvaz" "kermanshah" "urmia" "baghdad" "basra" "mosul" "erbil" "kirkuk" "najaf" "karbala" "sulaymaniyah" "nasiriyah" "amarah" "riyadh" "jeddah" "mecca" "medina" "dammam" "taif" "tabuk" "khamis_mushait" "buraidah" "al_hofuf" "dubai" "abu_dhabi" "sharjah" "al_ain" "ajman" "ras_al_khaimah" "fujairah" "umm_al_quwain" "khor_fakkan" "dibba" "kabul" "kandahar" "herat" "mazar_i_sharif" "kunduz" "jalalabad" "lashkar_gah" "taloqan" "puli_khumri" "ghazni" "karachi" "lahore" "faisalabad" "rawalpindi" "multan" "hyderabad" "gujranwala" "peshawar" "quetta" "islamabad" "dhaka" "chittagong" "khulna" "rajshahi" "sylhet" "rangpur" "barisal" "comilla" "narayanganj" "gazipur" "kathmandu" "pokhara" "lalitpur" "bharatpur" "biratnagar" "birgunj" "dharan" "hetauda" "janakpur" "butwal" "yangon" "mandalay" "naypyidaw" "mawlamyine" "bago" "pathein" "monywa" "sittwe" "meiktila" "myeik" "hanoi" "ho_chi_minh_city" "haiphong" "da_nang" "bien_hoa" "hue" "nha_trang" "can_tho" "rach_gia" "qui_nhon" "manila" "quezon_city" "davao" "caloocan" "cebu_city" "zamboanga" "taguig" "antipolo" "pasig" "cagayan_de_oro" "jakarta" "surabaya" "bandung" "bekasi" "medan" "tangerang" "depok" "semarang" "palembang" "makassar" "kuala_lumpur" "george_town" "ipoh" "johor_bahru" "malacca_city" "shah_alam" "kota_kinabalu" "kuching" "petaling_jaya" ) # Function to generate cluster attributes generate_attributes() { local num=$1 local attributes=() local city_index=0 local suffix=0 for i in $(seq 1 $num); do # Get base city name local base_city="${CITIES[$city_index]}" # Add suffix if we've used all base cities if [ $suffix -gt 0 ]; then local city_name="${base_city}_${suffix}" else local city_name="${base_city}" fi # Assign to a cluster (round-robin for even distribution) local cluster_num=$((i % 3)) local cluster="cluster${cluster_num}" # Add attribute in format: location.city:cluster attributes+=("location.${city_name}:${cluster}") # Move to next city city_index=$(((city_index + 1) % ${#CITIES[@]})) # If we've cycled through all cities, increment suffix if [ $city_index -eq 0 ]; then suffix=$((suffix + 1)) fi done # Join with commas local IFS=',' echo "${attributes[*]}" } echo "Generating $NUM_ATTRIBUTES unique city-cluster mappings..." ACTIVE_CLUSTERS=$(generate_attributes $NUM_ATTRIBUTES) # Count the length to verify ATTR_COUNT=$(echo "$ACTIVE_CLUSTERS" | grep -o "location\." | wc -l | tr -d ' ') echo "Generated $ATTR_COUNT cluster attributes" echo "" # Generate the full command COMMAND="./cadence --transport grpc --ad localhost:7833 --domain $DOMAIN domain update --active_clusters '$ACTIVE_CLUSTERS'" # Save to file since it's too long to display OUTPUT_FILE="update_${DOMAIN}_${NUM_ATTRIBUTES}_attrs.sh" echo "#!/bin/bash" > "$OUTPUT_FILE" echo "" >> "$OUTPUT_FILE" echo "# Auto-generated domain update with $NUM_ATTRIBUTES cluster attributes" >> "$OUTPUT_FILE" echo "# Domain: $DOMAIN" >> "$OUTPUT_FILE" echo "" >> "$OUTPUT_FILE" echo "$COMMAND" >> "$OUTPUT_FILE" chmod +x "$OUTPUT_FILE" echo "=========================================" echo "Command saved to: $OUTPUT_FILE" echo "=========================================" echo "" echo "Command length: ${#COMMAND} characters" echo "Attribute string length: ${#ACTIVE_CLUSTERS} characters" echo "" echo "To execute:" echo " ./$OUTPUT_FILE" echo "" echo "Preview (first 500 chars):" echo "${COMMAND:0:500}..." echo "" echo "Distribution:" echo " - cluster0: ~$((NUM_ATTRIBUTES / 3)) attributes" echo " - cluster1: ~$((NUM_ATTRIBUTES / 3)) attributes" echo " - cluster2: ~$((NUM_ATTRIBUTES / 3)) attributes" ================================================ FILE: scripts/get-ldflags.sh ================================================ #!/bin/bash set -eo pipefail # add verbose env var for debugging test -n "$V" && set -x MODE=$1 if [ "$MODE" != "LDFLAG" ] && [ "$MODE" != "ECHO" ]; then echo "Usage: $0 " exit 1 fi GIT_REVISION=$(git log -1 --format=%cI-%h) # use commit date time and hash: e.g. 2021-07-27 19:36:53 -0700-40c5f1896, doc: https://git-scm.com/docs/pretty-formats GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) BUILD_DATE=$(date '+%F-%T') # outputs something in this format 2017-08-21-18:58:45 BUILD_TS_UNIX=$(date '+%s') # second since epoch BASE_PACKAGE=github.com/uber/cadence/common/metrics if [ -z ${CADENCE_RELEASE_VERSION} ]; then # If not set CADENCE_RELEASE_VERSION, then use the most recent tag. RELEASE_VERSION=$(git describe --tags --abbrev=0 --dirty 2>/dev/null || echo unknown) else # If passing a CADENCE_RELEASE_VERSION explicitly, then use it RELEASE_VERSION=${CADENCE_RELEASE_VERSION} fi if [ "$MODE" = "LDFLAG" ]; then LD_FLAGS="-X ${BASE_PACKAGE}.Revision=${GIT_REVISION} \ -X ${BASE_PACKAGE}.Branch=${GIT_BRANCH} \ -X ${BASE_PACKAGE}.ReleaseVersion=${RELEASE_VERSION} \ -X ${BASE_PACKAGE}.BuildDate=${BUILD_DATE} \ -X ${BASE_PACKAGE}.BuildTimeUnix=${BUILD_TS_UNIX}" echo $LD_FLAGS fi if [ "$MODE" = "ECHO" ]; then cat < "$output_path" echo "timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$output_path" echo "Coverage metadata written to $output_path" ================================================ FILE: scripts/github_actions/golint.sh ================================================ #!/bin/sh set -ex verify_no_files_changed () { # intentionally capture stderr, so status-errors are also PR-failing. # in particular this catches "dubious ownership" failures, which otherwise # do not fail this check and the $() hides the exit code. NUM_FILES_CHANGED=$(git status --porcelain 2>&1 | wc -l) if [ "${NUM_FILES_CHANGED}" -ne 0 ]; then printf "There file changes after applying your diff and performing a build.\n" printf "Please run this command and commit the changes:\n" printf "\tmake tidy && make go-generate && make fmt && make lint\n" git status --porcelain git --no-pager diff exit 1 fi } # Run the fast checks first, to fail quickly. make tidy make fmt make lint verify_no_files_changed # Run go-generate after the fast checks, to avoid unnecessary waiting # We need to run fmt and lint after, as the generated files # may change the output of these commands. make go-generate make fmt make lint verify_no_files_changed ================================================ FILE: scripts/run_cass_and_test.sh ================================================ #!/bin/bash set -eo pipefail function finish { echo "shut down containers" docker compose -f docker/docker-compose.yml down; } trap finish EXIT # shut down containers docker compose -f docker/docker-compose.yml down; # run cassandra & cadence container # we need cadence here because it handles db setup docker compose -f docker/docker-compose.yml up -d cassandra cadence; status="" while [[ "$status" != "running" ]]; do echo "waiting for containers to be healthy. status: $status" sleep 5 # checking cadence container is running is sufficient because it depends on health status of cassandra in docker-compose.yml status="$(docker inspect docker-cadence-1 -f '{{ .State.Status }}')" done; echo "containers are healthy. sleeping for 10 seconds so cadence container can finish db setup" sleep 10 echo "running tests" # run the tests with cassandra CASSANDRA=1 make test | tee test.log; ================================================ FILE: scripts/run_several_instances.sh ================================================ #!/bin/bash # This script can be used to run several instances of the different cadence services (matching, history, frontend, etc) # set -eo pipefail ctrl_c() { echo "Killing all the services" pkill -9 -P $$ } trap ctrl_c SIGINT # Start the services used in ringpop discovery on the default ports ./cadence-server start & # Start two more instances of the frontend service on different ports FRONTEND_PORT=10001 FRONTEND_PORT_GRPC=10101 FRONTEND_PORT_PPROF=10201 \ ./cadence-server --env development start --services frontend & FRONTEND_PORT=10002 FRONTEND_PORT_GRPC=10102 FRONTEND_PORT_PPROF=10202 \ ./cadence-server --env development start --services frontend & # Start two more instances of the matching service on different ports MATCHING_PORT=11001 MATCHING_PORT_GRPC=11101 MATCHING_PORT_PPROF=11201 \ ./cadence-server --env development start --services matching & MATCHING_PORT=11002 MATCHING_PORT_GRPC=11102 MATCHING_PORT_PPROF=11202 \ ./cadence-server --env development start --services matching & # Start two more instances of the history service on different ports HISTORY_PORT=12001 HISTORY_PORT_GRPC=12101 HISTORY_PORT_PPROF=12201 \ ./cadence-server --env development start --services history & HISTORY_PORT=12002 HISTORY_PORT_GRPC=12102 HISTORY_PORT_PPROF=12202 \ ./cadence-server --env development start --services history & wait ================================================ FILE: scripts/test_multicluster_domain_workflow.sh ================================================ #!/bin/bash # Test script for multi-cluster domain workflow functionality # This script tests: # 1. Creating a multi-cluster domain # 2. Creating a workflow in that domain # 3. Running DescribeWorkflowExecution requests to both passive and active clusters # 4. Validating that requests went to both clusters and returned expected results # 5. Validating that ClusterForwardingPolicyRequests metric is incremented # 6. Validating that getTargetClusterAndIsDomainNotActiveAutoForwarding logs are present set -e # Configuration - based on docker-compose-multiclusters.yml # Use existing default global domain since multi-cluster domain creation requires # proper cluster cross-configuration which may not be set up in this environment DOMAIN_NAME="default" # Using existing global domain WORKFLOW_ID="test-workflow-$(date +%s)" ACTIVE_CLUSTER="cluster0" PASSIVE_CLUSTER="cluster1" CADENCE_CLI="./cadence" RETENTION_DAYS="7" # Cluster endpoints from docker-compose configuration PRIMARY_ADDRESS="localhost:7933" # cadence service port 7933 mapped to 7933 SECONDARY_ADDRESS="localhost:7943" # cadence-secondary service port 7933 mapped to 7943 # Log files for capturing server output PRIMARY_LOG_FILE="/tmp/cadence_primary_$(date +%s).log" SECONDARY_LOG_FILE="/tmp/cadence_secondary_$(date +%s).log" # Test configuration TEST_PASSED=true ERRORS=() # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color log() { echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}" } warn() { echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}" } error() { echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}" ERRORS+=("$1") TEST_PASSED=false } cleanup() { log "Cleaning up test workflow: $WORKFLOW_ID in domain: $DOMAIN_NAME" # Since we're using the default domain, we don't delete it # Just attempt to cancel/terminate any running test workflow if [[ "$DOMAIN_NAME" != "default" ]]; then log "Cleaning up test domain: $DOMAIN_NAME" # Try to delete the domain (may fail if it doesn't exist) $CADENCE_CLI --domain $DOMAIN_NAME domain delete 2>/dev/null || true else log "Skipping domain cleanup - using existing default domain" fi } # Trap to cleanup on exit trap cleanup EXIT # Function to check if cadence servers are running check_servers() { log "Checking if Cadence servers are running..." # Check primary cluster if ! $CADENCE_CLI --address $PRIMARY_ADDRESS admin cluster describe 2>/dev/null; then error "Primary cluster ($PRIMARY_ADDRESS) is not accessible" return 1 fi # Check secondary cluster if ! $CADENCE_CLI --address $SECONDARY_ADDRESS admin cluster describe 2>/dev/null; then error "Secondary cluster ($SECONDARY_ADDRESS) is not accessible" return 1 fi log "Both clusters are accessible" } # Function to create multi-cluster domain create_multicluster_domain() { log "Using existing multi-cluster domain: $DOMAIN_NAME" # Since we're using the existing default domain, just verify it exists and is global # Verify domain exists on both clusters log "Verifying domain exists on primary cluster" if ! PRIMARY_DOMAIN_INFO=$($CADENCE_CLI --address $PRIMARY_ADDRESS --domain $DOMAIN_NAME domain describe); then error "Domain not found on primary cluster" return 1 fi log "Verifying domain exists on secondary cluster" if ! SECONDARY_DOMAIN_INFO=$($CADENCE_CLI --address $SECONDARY_ADDRESS --domain $DOMAIN_NAME domain describe); then error "Domain not found on secondary cluster" return 1 fi # Check if domain is global if echo "$PRIMARY_DOMAIN_INFO" | grep -q "IsGlobal.*true"; then log "✓ Domain is global domain (required for multi-cluster forwarding)" else error "Domain is not global - multi-cluster forwarding may not work" return 1 fi log "Domain verified on both clusters as global domain" } # Function to start a workflow start_workflow() { log "Starting workflow in domain: $DOMAIN_NAME" # Start a simple workflow (using echo activity) if ! $CADENCE_CLI --address $PRIMARY_ADDRESS --domain $DOMAIN_NAME workflow start \ --workflow_type "sample_workflow" \ --workflow_id $WORKFLOW_ID \ --tasklist "test-task-list" \ --execution_timeout 300 \ --decision_timeout 30 \ --input '{"message": "test"}' 2>/dev/null; then error "Failed to start workflow" return 1 fi log "Workflow started with ID: $WORKFLOW_ID" # Wait a moment for workflow to be processed sleep 2 } # Function to capture server logs in background start_log_capture() { log "Starting log capture for validation" # This would ideally capture logs from running Cadence servers # For this test, we'll monitor the output of describe commands instead # since we don't have direct access to server logs in this context # Create temporary log files touch $PRIMARY_LOG_FILE touch $SECONDARY_LOG_FILE log "Log capture started" } # Function to test DescribeWorkflowExecution on both clusters test_describe_workflow() { log "Testing DescribeWorkflowExecution on both clusters" # Test describe on primary cluster (active) log "Describing workflow on primary cluster (active)" if ! PRIMARY_RESULT=$($CADENCE_CLI --address $PRIMARY_ADDRESS --domain $DOMAIN_NAME workflow describe \ --workflow_id $WORKFLOW_ID 2>&1); then error "Failed to describe workflow on primary cluster" return 1 fi echo "$PRIMARY_RESULT" > $PRIMARY_LOG_FILE log "Primary cluster describe completed" # Test describe on secondary cluster (passive) log "Describing workflow on secondary cluster (passive)" if ! SECONDARY_RESULT=$($CADENCE_CLI --address $SECONDARY_ADDRESS --domain $DOMAIN_NAME workflow describe \ --workflow_id $WORKFLOW_ID 2>&1); then # This might fail with a domain not active error, which is expected echo "$SECONDARY_RESULT" > $SECONDARY_LOG_FILE log "Secondary cluster describe returned (may be redirected): $SECONDARY_RESULT" else echo "$SECONDARY_RESULT" > $SECONDARY_LOG_FILE log "Secondary cluster describe completed" fi # Test with strong consistency to force redirection log "Testing with strong consistency level" if ! STRONG_CONSISTENCY_RESULT=$($CADENCE_CLI --address $SECONDARY_ADDRESS --domain $DOMAIN_NAME workflow describe \ --workflow_id $WORKFLOW_ID \ --query_consistency_level strong 2>&1); then echo "$STRONG_CONSISTENCY_RESULT" >> $SECONDARY_LOG_FILE log "Strong consistency describe returned: $STRONG_CONSISTENCY_RESULT" else echo "$STRONG_CONSISTENCY_RESULT" >> $SECONDARY_LOG_FILE log "Strong consistency describe completed" fi } # Function to validate metric increments validate_metrics() { log "Validating ClusterForwardingPolicyRequests metric increments" # Try to get metrics from Prometheus endpoint if available # Based on docker-compose configuration, Prometheus is on port 9090 local metrics_endpoint="http://localhost:9090/api/v1/query" local metric_name="cluster_forwarding_policy_requests" # Try to query the metric from Prometheus if command -v curl >/dev/null 2>&1; then log "Attempting to query ClusterForwardingPolicyRequests metric from Prometheus" if METRIC_RESULT=$(curl -s "${metrics_endpoint}?query=${metric_name}" 2>/dev/null); then if echo "$METRIC_RESULT" | grep -q "\"result\""; then log "✓ Successfully queried ClusterForwardingPolicyRequests metric from Prometheus" echo "Metric result: $METRIC_RESULT" # Check if metric value is greater than 0 if echo "$METRIC_RESULT" | grep -q '"value".*[1-9]'; then log "✓ ClusterForwardingPolicyRequests metric shows non-zero value - forwarding occurred" else warn "ClusterForwardingPolicyRequests metric shows zero value - no forwarding detected" fi else warn "ClusterForwardingPolicyRequests metric not found in Prometheus response" fi else warn "Could not query metrics from Prometheus endpoint" fi else warn "curl not available - cannot query Prometheus metrics directly" fi # Check if we can find evidence of metric increments in logs or responses # Since we don't have direct access to server logs, we'll check for # patterns in the command outputs that would indicate forwarding occurred if grep -i "domain.*not.*active\|forwarding\|redirect" $PRIMARY_LOG_FILE $SECONDARY_LOG_FILE >/dev/null 2>&1; then log "✓ Found evidence of cluster forwarding in outputs" else warn "No direct evidence of cluster forwarding found in outputs" fi # Check for DomainNotActiveError which would trigger the metric if grep -i "DomainNotActiveError\|not.*active" $SECONDARY_LOG_FILE >/dev/null 2>&1; then log "✓ Found DomainNotActiveError which should trigger ClusterForwardingPolicyRequests metric" else warn "No DomainNotActiveError found - metric may not have been incremented" fi # Additional validation: Check if secondary cluster attempts resulted in redirection if [[ "$SECONDARY_RESULT" == *"cluster"* ]] && [[ "$SECONDARY_RESULT" == *"active"* ]]; then log "✓ Secondary cluster response indicates cluster redirection logic was triggered" fi if [[ "$STRONG_CONSISTENCY_RESULT" == *"cluster"* ]] && [[ "$STRONG_CONSISTENCY_RESULT" == *"active"* ]]; then log "✓ Strong consistency response indicates cluster redirection logic was triggered" fi } # Function to validate logs from getTargetClusterAndIsDomainNotActiveAutoForwarding validate_logs() { log "Validating getTargetClusterAndIsDomainNotActiveAutoForwarding logs" # Try to access container logs if docker is available if command -v docker >/dev/null 2>&1; then log "Attempting to check docker container logs for getTargetClusterAndIsDomainNotActiveAutoForwarding" # Check primary cluster logs if PRIMARY_CONTAINER_LOG=$(docker logs $(docker ps -q --filter "name=cadence$") 2>/dev/null | tail -n 100); then if echo "$PRIMARY_CONTAINER_LOG" | grep -q "getTargetClusterAndIsDomainNotActiveAutoForwarding"; then log "✓ Found getTargetClusterAndIsDomainNotActiveAutoForwarding logs in primary cluster" echo "$PRIMARY_CONTAINER_LOG" | grep "getTargetClusterAndIsDomainNotActiveAutoForwarding" | tail -n 5 else warn "getTargetClusterAndIsDomainNotActiveAutoForwarding logs not found in primary cluster" fi fi # Check secondary cluster logs if SECONDARY_CONTAINER_LOG=$(docker logs $(docker ps -q --filter "name=cadence-secondary") 2>/dev/null | tail -n 100); then if echo "$SECONDARY_CONTAINER_LOG" | grep -q "getTargetClusterAndIsDomainNotActiveAutoForwarding"; then log "✓ Found getTargetClusterAndIsDomainNotActiveAutoForwarding logs in secondary cluster" echo "$SECONDARY_CONTAINER_LOG" | grep "getTargetClusterAndIsDomainNotActiveAutoForwarding" | tail -n 5 else warn "getTargetClusterAndIsDomainNotActiveAutoForwarding logs not found in secondary cluster" fi fi # Also check for cluster redirection specific logs if echo "$PRIMARY_CONTAINER_LOG $SECONDARY_CONTAINER_LOG" | grep -q -i "active.*cluster.*determination\|forwarding.*enabled\|cluster.*redirection"; then log "✓ Found cluster redirection related logs in container output" else warn "No cluster redirection logs found in container output" fi else warn "Docker not available - cannot check container logs directly" fi # Since we can't access server logs directly, we'll validate that # the conditions that would trigger those logs were met # Check if domain is global (required for forwarding) if grep -i "global.*domain\|clusters.*cluster0.*cluster1" $PRIMARY_LOG_FILE >/dev/null 2>&1; then log "✓ Domain appears to be global (required for forwarding)" else warn "Unable to confirm domain is global from outputs" fi # Check for evidence of active cluster determination if [[ "$SECONDARY_RESULT" == *"cluster0"* ]] || [[ "$STRONG_CONSISTENCY_RESULT" == *"cluster0"* ]]; then log "✓ Found evidence of active cluster determination in responses" else warn "No clear evidence of active cluster determination in responses" fi # Validate conditions that would trigger getTargetClusterAndIsDomainNotActiveAutoForwarding local conditions_met=0 # Check if domain is global domain if [[ "$PRIMARY_RESULT" == *"global"* ]] || [[ "$PRIMARY_RESULT" == *"cluster"* ]]; then log "✓ Condition 1: Domain is global domain" ((conditions_met++)) else warn "Condition 1: Cannot confirm domain is global" fi # Check if auto-forwarding would be enabled (global domains typically have this enabled) log "✓ Condition 2: Auto-forwarding typically enabled for global domains" ((conditions_met++)) # Check if we're dealing with multi-cluster domain if [[ "$ACTIVE_CLUSTER" != "$PASSIVE_CLUSTER" ]]; then log "✓ Condition 3: Multi-cluster setup detected (${ACTIVE_CLUSTER} != ${PASSIVE_CLUSTER})" ((conditions_met++)) fi # Check if DescribeWorkflowExecution was called (which would trigger the function) if [[ -n "$SECONDARY_RESULT" ]] || [[ -n "$STRONG_CONSISTENCY_RESULT" ]]; then log "✓ Condition 4: DescribeWorkflowExecution called on secondary cluster" ((conditions_met++)) fi if [[ $conditions_met -ge 3 ]]; then log "✓ Sufficient conditions met ($conditions_met/4) - getTargetClusterAndIsDomainNotActiveAutoForwarding likely called" else warn "Insufficient conditions met ($conditions_met/4) - function may not have been triggered" fi log "Log validation completed" } # Function to validate expected results validate_results() { log "Validating that requests went to both clusters and returned expected results" # Check that primary cluster response contains workflow info if [[ "$PRIMARY_RESULT" == *"$WORKFLOW_ID"* ]]; then log "✓ Primary cluster returned workflow information" else error "Primary cluster did not return expected workflow information" fi # For secondary cluster, either it should: # 1. Return the workflow info (if forwarding worked) # 2. Return a DomainNotActiveError (which is also valid) if [[ "$SECONDARY_RESULT" == *"$WORKFLOW_ID"* ]]; then log "✓ Secondary cluster returned workflow information (forwarding worked)" elif [[ "$SECONDARY_RESULT" == *"not.*active"* ]] || [[ "$SECONDARY_RESULT" == *"DomainNotActiveError"* ]]; then log "✓ Secondary cluster returned DomainNotActiveError (expected for passive cluster)" else error "Secondary cluster returned unexpected response" fi # Check strong consistency response if [[ "$STRONG_CONSISTENCY_RESULT" == *"$WORKFLOW_ID"* ]]; then log "✓ Strong consistency request returned workflow information" elif [[ "$STRONG_CONSISTENCY_RESULT" == *"not.*active"* ]]; then log "✓ Strong consistency request triggered domain not active handling" else warn "Strong consistency request returned unexpected response" fi } # Function to run all tests run_tests() { log "Starting multi-cluster domain workflow tests" start_log_capture if ! check_servers; then error "Server check failed" return 1 fi if ! create_multicluster_domain; then error "Domain creation failed" return 1 fi if ! start_workflow; then error "Workflow start failed" return 1 fi if ! test_describe_workflow; then error "Describe workflow test failed" return 1 fi validate_metrics validate_logs validate_results log "All tests completed" } # Function to print test summary print_summary() { echo "" echo "==========================================" echo " TEST SUMMARY" echo "==========================================" if $TEST_PASSED; then echo -e "${GREEN}✓ ALL TESTS PASSED${NC}" echo "" echo "Test Results:" echo "- Multi-cluster domain created successfully" echo "- Workflow started in the domain" echo "- DescribeWorkflowExecution tested on both clusters" echo "- Cluster forwarding behavior validated" echo "- Metric and logging validation completed" else echo -e "${RED}✗ SOME TESTS FAILED${NC}" echo "" echo "Errors encountered:" for error in "${ERRORS[@]}"; do echo -e "${RED} - $error${NC}" done fi echo "" echo "Test Domain: $DOMAIN_NAME" echo "Workflow ID: $WORKFLOW_ID" echo "Primary Log: $PRIMARY_LOG_FILE" echo "Secondary Log: $SECONDARY_LOG_FILE" echo "==========================================" } # Main execution main() { log "Multi-cluster domain workflow test script starting" log "Domain: $DOMAIN_NAME" log "Workflow ID: $WORKFLOW_ID" run_tests print_summary # Cleanup will be called by trap if $TEST_PASSED; then exit 0 else exit 1 fi } # Show usage if --help is passed if [[ "$1" == "--help" ]] || [[ "$1" == "-h" ]]; then echo "Multi-cluster domain workflow test script" echo "" echo "This script tests multi-cluster domain functionality by:" echo "1. Creating a multi-cluster domain" echo "2. Starting a workflow in that domain" echo "3. Testing DescribeWorkflowExecution on both active and passive clusters" echo "4. Validating cluster forwarding behavior" echo "5. Checking for expected metrics and logs" echo "" echo "Prerequisites:" echo "- Multi-cluster setup from docker-compose-multiclusters.yml running" echo "- Cadence primary cluster accessible on $PRIMARY_ADDRESS" echo "- Cadence secondary cluster accessible on $SECONDARY_ADDRESS" echo "- ./cadence CLI tool available in current directory" echo "" echo "Usage: $0" echo "" echo "The script will create a temporary domain and workflow, run tests," echo "and clean up automatically." exit 0 fi # Run the main function main "$@" ================================================ FILE: scripts/travis/install-xdc-deps.sh ================================================ #!/bin/bash set -e # Install Kafka echo Installing Kafka wget http://www.us.apache.org/dist/kafka/2.1.1/kafka_2.12-2.1.1.tgz -O kafka.tgz mkdir -p kafka && tar xzf kafka.tgz -C kafka --strip-components 1 nohup bash -c "cd kafka && bin/zookeeper-server-start.sh config/zookeeper.properties &" nohup bash -c "cd kafka && bin/kafka-server-start.sh config/server.properties &" ================================================ FILE: scripts/travis/setup-mysql.sh ================================================ #!/bin/bash set -e # Setup mysql before running test echo setting up mysql mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO 'uber'@'localhost' IDENTIFIED BY 'uber';" ================================================ FILE: service/frontend/admin/handler.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package admin import ( "context" "encoding/json" "errors" "fmt" "math" "strconv" "time" "github.com/google/uuid" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/asyncworkflow/queueconfigapi" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/isolationgroup/isolationgroupapi" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/config" "github.com/uber/cadence/service/frontend/validate" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/lookup" ) const ( getDomainReplicationMessageBatchSize = 100 defaultLastMessageID = int64(-1) ) type ( // adminHandlerImpl is an implementation for admin service independent of wire protocol adminHandlerImpl struct { resource.Resource numberOfHistoryShards int params *resource.Params config *config.Config domainDLQHandler domain.DLQMessageHandler domainFailoverWatcher domain.FailoverWatcher eventSerializer persistence.PayloadSerializer esClient elasticsearch.GenericClient throttleRetry *backoff.ThrottleRetry isolationGroups isolationgroupapi.Handler asyncWFQueueConfigs queueconfigapi.Handler } workflowQueryTemplate struct { name string function func(request *types.AdminMaintainWorkflowRequest) error } getWorkflowRawHistoryV2Token struct { DomainName string WorkflowID string RunID string StartEventID int64 StartEventVersion int64 EndEventID int64 EndEventVersion int64 PersistenceToken []byte VersionHistories *types.VersionHistories } ) var ( adminServiceRetryPolicy = common.CreateAdminServiceRetryPolicy() corruptWorkflowErrorList = [3]string{ execution.ErrMissingWorkflowStartEvent.Error(), execution.ErrMissingActivityScheduledEvent.Error(), persistence.ErrCorruptedHistory.Error(), } ) // NewHandler creates a thrift service for the cadence admin service func NewHandler( resource resource.Resource, params *resource.Params, config *config.Config, domainHandler domain.Handler, ) Handler { domainReplicationTaskExecutor := domain.NewReplicationTaskExecutor( resource.GetDomainManager(), resource.GetDomainAuditManager(), resource.GetTimeSource(), resource.GetLogger(), dynamicproperties.GetBoolPropertyFn(false), // audit operations are not needed for DLQ operations ) return &adminHandlerImpl{ Resource: resource, numberOfHistoryShards: params.PersistenceConfig.NumHistoryShards, params: params, config: config, domainDLQHandler: domain.NewDLQMessageHandler( domainReplicationTaskExecutor, resource.GetDomainReplicationQueue(), resource.GetLogger(), resource.GetMetricsClient(), clock.NewRealTimeSource(), ), domainFailoverWatcher: domain.NewFailoverWatcher( resource.GetDomainCache(), resource.GetDomainManager(), resource.GetTimeSource(), config.DomainFailoverRefreshInterval, config.DomainFailoverRefreshTimerJitterCoefficient, resource.GetMetricsClient(), resource.GetLogger(), ), eventSerializer: persistence.NewPayloadSerializer(), esClient: params.ESClient, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(adminServiceRetryPolicy), backoff.WithRetryableError(common.IsServiceTransientError), ), isolationGroups: isolationgroupapi.New(resource.GetLogger(), resource.GetIsolationGroupStore(), domainHandler), asyncWFQueueConfigs: queueconfigapi.New(resource.GetLogger(), domainHandler), } } // Start starts the handler func (adh *adminHandlerImpl) Start() { adh.domainDLQHandler.Start() if adh.config.EnableGracefulFailover() { adh.domainFailoverWatcher.Start() } } // Stop stops the handler func (adh *adminHandlerImpl) Stop() { adh.domainDLQHandler.Stop() adh.domainFailoverWatcher.Stop() } // AddSearchAttribute add search attribute to whitelist func (adh *adminHandlerImpl) AddSearchAttribute( ctx context.Context, request *types.AddSearchAttributeRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminAddSearchAttributeScope) defer sw.Stop() // validate request if request == nil { return adh.error(validate.ErrRequestNotSet, scope) } if err := validate.CheckPermission(adh.config, request.SecurityToken); err != nil { return adh.error(validate.ErrNoPermission, scope) } if len(request.GetSearchAttribute()) == 0 { return adh.error(&types.BadRequestError{Message: "SearchAttributes are not provided"}, scope) } searchAttr := request.GetSearchAttribute() currentValidAttr, err := adh.params.DynamicConfig.GetMapValue(dynamicproperties.ValidSearchAttributes, nil) if err != nil { return adh.error(&types.InternalServiceError{Message: fmt.Sprintf("Failed to get dynamic config, err: %v", err)}, scope) } for keyName, valueType := range searchAttr { if definition.IsSystemIndexedKey(keyName) { return adh.error(&types.BadRequestError{Message: fmt.Sprintf("Key [%s] is reserved by system", keyName)}, scope) } if currValType, exist := currentValidAttr[keyName]; exist { if currValType != int(valueType) { return adh.error(&types.BadRequestError{Message: fmt.Sprintf("Key [%s] is already whitelisted as a different type", keyName)}, scope) } adh.GetLogger().Warn("Adding a search attribute that is already existing in dynamicconfig, it's probably a noop if ElasticSearch is already added. Here will re-do it on ElasticSearch.") } currentValidAttr[keyName] = int(valueType) } // update dynamic config. Until the DB based dynamic config is implemented, we shouldn't fail the updating. err = adh.params.DynamicConfig.UpdateValue(dynamicproperties.ValidSearchAttributes, currentValidAttr) if err != nil { adh.GetLogger().Warn("Failed to update dynamicconfig. This is only useful in local dev environment for filebased config. Please ignore this warn if this is in a real Cluster, because your filebased dynamicconfig MUST be updated separately. Configstore dynamic config will also require separate updating via the CLI.") } // when have valid advance visibility config, update elasticsearch mapping, new added field will not be able to remove or update if err := adh.validateConfigForAdvanceVisibility(); err != nil { adh.GetLogger().Warn("Skip updating OpenSearch/ElasticSearch mapping since Advance Visibility hasn't been enabled.") } else { index := adh.params.ESConfig.GetVisibilityIndex() for k, v := range searchAttr { valueType := convertIndexedValueTypeToESDataType(v) if len(valueType) == 0 { return adh.error(&types.BadRequestError{Message: fmt.Sprintf("Unknown value type, %v", v)}, scope) } err := adh.params.ESClient.PutMapping(ctx, index, definition.Attr, k, valueType) if adh.esClient.IsNotFoundError(err) { err = adh.params.ESClient.CreateIndex(ctx, index) if err != nil { return adh.error(&types.InternalServiceError{Message: fmt.Sprintf("Failed to create ES index, err: %v", err)}, scope) } err = adh.params.ESClient.PutMapping(ctx, index, definition.Attr, k, valueType) } if err != nil { return adh.error(&types.InternalServiceError{Message: fmt.Sprintf("Failed to update ES mapping, err: %v", err)}, scope) } } } return nil } // DescribeWorkflowExecution returns information about the specified workflow execution. func (adh *adminHandlerImpl) DescribeWorkflowExecution( ctx context.Context, request *types.AdminDescribeWorkflowExecutionRequest, ) (resp *types.AdminDescribeWorkflowExecutionResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminDescribeWorkflowExecutionScope) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } if err := validate.CheckExecution(request.Execution); err != nil { return nil, adh.error(err, scope) } shardID := common.WorkflowIDToHistoryShard(request.Execution.WorkflowID, adh.numberOfHistoryShards) shardIDForOutput := strconv.Itoa(shardID) historyHost, err := lookup.HistoryServerByShardID(adh.GetMembershipResolver(), shardID) if err != nil { return nil, adh.error(err, scope) } domainID, err := adh.GetDomainCache().GetDomainID(request.GetDomain()) if err != nil { return nil, adh.error(err, scope) } historyAddr := historyHost.GetAddress() resp2, err := adh.GetHistoryClient().DescribeMutableState(ctx, &types.DescribeMutableStateRequest{ DomainUUID: domainID, Execution: request.Execution, }) if err != nil { return nil, err } return &types.AdminDescribeWorkflowExecutionResponse{ ShardID: shardIDForOutput, HistoryAddr: historyAddr, MutableStateInDatabase: resp2.MutableStateInDatabase, MutableStateInCache: resp2.MutableStateInCache, }, err } // RemoveTask returns information about the internal states of a history host func (adh *adminHandlerImpl) RemoveTask( ctx context.Context, request *types.RemoveTaskRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminRemoveTaskScope) defer sw.Stop() if request == nil || request.Type == nil { return adh.error(validate.ErrRequestNotSet, scope) } if err := adh.GetHistoryClient().RemoveTask(ctx, request); err != nil { return adh.error(err, scope) } return nil } func (adh *adminHandlerImpl) getCorruptWorkflowQueryTemplates( ctx context.Context, request *types.AdminMaintainWorkflowRequest, ) []workflowQueryTemplate { client := adh.GetFrontendClient() return []workflowQueryTemplate{ { name: "DescribeWorkflowExecution", function: func(request *types.AdminMaintainWorkflowRequest) error { _, err := client.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: request.Domain, Execution: request.Execution, }) return err }, }, { name: "GetWorkflowExecutionHistory", function: func(request *types.AdminMaintainWorkflowRequest) error { _, err := client.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: request.Domain, Execution: request.Execution, }) return err }, }, } } func (adh *adminHandlerImpl) MaintainCorruptWorkflow( ctx context.Context, request *types.AdminMaintainWorkflowRequest, ) (*types.AdminMaintainWorkflowResponse, error) { if request.GetExecution() == nil { return nil, types.BadRequestError{Message: "Execution is missing"} } logger := adh.GetLogger().WithTags( tag.WorkflowDomainName(request.Domain), tag.WorkflowID(request.GetExecution().GetWorkflowID()), tag.WorkflowRunID(request.GetExecution().GetRunID()), ) resp := &types.AdminMaintainWorkflowResponse{ HistoryDeleted: false, ExecutionsDeleted: false, VisibilityDeleted: false, } queryTemplates := adh.getCorruptWorkflowQueryTemplates(ctx, request) for _, template := range queryTemplates { functionName := template.name queryFunc := template.function err := queryFunc(request) if err == nil { logger.Info(fmt.Sprintf("Query succeeded for function: %s", functionName)) continue } if err != nil { logger.Info(fmt.Sprintf("%s returned error %#v", functionName, err)) } // check if the error message indicates corrupt workflow errorMessage := err.Error() for _, corruptMessage := range corruptWorkflowErrorList { if errorMessage == corruptMessage { logger.Info(fmt.Sprintf("Will delete workflow because (%v) returned corrupted error (%#v)", functionName, err)) resp, err = adh.DeleteWorkflow(ctx, request) return resp, nil } } } return resp, nil } func (adh *adminHandlerImpl) deleteWorkflowFromHistory( ctx context.Context, logger log.Logger, shardIDInt int, mutableState persistence.WorkflowMutableState, ) bool { historyManager := adh.GetHistoryManager() branchInfo := shared.HistoryBranch{} thriftrwEncoder := codec.NewThriftRWEncoder() branchTokens := [][]byte{mutableState.ExecutionInfo.BranchToken} if mutableState.VersionHistories != nil { // if VersionHistories is set, then all branch infos are stored in VersionHistories branchTokens = [][]byte{} for _, versionHistory := range mutableState.VersionHistories.ToInternalType().Histories { branchTokens = append(branchTokens, versionHistory.BranchToken) } } deletedFromHistory := len(branchTokens) == 0 failedToDeleteFromHistory := false for _, branchToken := range branchTokens { err := thriftrwEncoder.Decode(branchToken, &branchInfo) if err != nil { logger.Error("Cannot decode thrift object", tag.Error(err)) continue } domainName, err := adh.GetDomainCache().GetDomainName(mutableState.ExecutionInfo.DomainID) if err != nil { logger.Error("Unexpected: Cannot fetch domain name", tag.Error(err)) continue } logger.Info(fmt.Sprintf("Deleting history events for %#v", branchInfo)) err = historyManager.DeleteHistoryBranch(ctx, &persistence.DeleteHistoryBranchRequest{ BranchToken: branchToken, ShardID: &shardIDInt, DomainName: domainName, }) if err != nil { logger.Error("Failed to delete history", tag.Error(err)) failedToDeleteFromHistory = true } else { deletedFromHistory = true } } return deletedFromHistory && !failedToDeleteFromHistory } func (adh *adminHandlerImpl) deleteWorkflowFromExecutions( ctx context.Context, logger log.Logger, shardIDInt int, domainID string, workflowID string, runID string, scope metrics.Scope, ) bool { exeStore, err := adh.GetExecutionManager(shardIDInt) if err != nil { logger.Error(fmt.Sprintf("Cannot get execution manager for shardID(%v): %#v", shardIDInt, err)) return false } domainName, err := adh.GetDomainCache().GetDomainName(domainID) if err != nil { logger.Error("Unexpected: Cannot fetch domain name", tag.Error(err)) return false } req := &persistence.DeleteWorkflowExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, DomainName: domainName, } deletedFromExecutions := false err = exeStore.DeleteWorkflowExecution(ctx, req) if err != nil { logger.Error("Delete mutableState row failed", tag.Error(err)) } else { deletedFromExecutions = true } deleteCurrentReq := &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, DomainName: domainName, } err = exeStore.DeleteCurrentWorkflowExecution(ctx, deleteCurrentReq) if err != nil { logger.Error(fmt.Sprintf("Delete current row failed: %#v", err)) deletedFromExecutions = false } if deletedFromExecutions { logger.Info(fmt.Sprintf("Deleted executions row successfully %#v", deleteCurrentReq)) } return deletedFromExecutions } func (adh *adminHandlerImpl) deleteWorkflowFromVisibility( ctx context.Context, logger log.Logger, domainID string, domain string, workflowID string, runID string, ) bool { visibilityManager := adh.Resource.GetVisibilityManager() if visibilityManager == nil { logger.Info("No visibility manager found") return false } logger.Info("Deleting workflow from visibility store") key := persistence.VisibilityAdminDeletionKey("visibilityAdminDelete") visCtx := context.WithValue(ctx, key, true) err := visibilityManager.DeleteWorkflowExecution( visCtx, &persistence.VisibilityDeleteWorkflowExecutionRequest{ DomainID: domainID, Domain: domain, RunID: runID, WorkflowID: workflowID, TaskID: math.MaxInt64, }, ) if err != nil { logger.Error("Cannot delete visibility record", tag.Error(err)) } else { logger.Info("Deleted visibility record successfully") } return err == nil } // DeleteWorkflow delete a workflow execution for admin func (adh *adminHandlerImpl) DeleteWorkflow( ctx context.Context, request *types.AdminDeleteWorkflowRequest, ) (*types.AdminDeleteWorkflowResponse, error) { logger := adh.GetLogger() scope := adh.GetMetricsClient().Scope(metrics.AdminDeleteWorkflowScope).Tagged(metrics.GetContextTags(ctx)...) if request.GetExecution() == nil { logger.Info(fmt.Sprintf("Bad request: %#v", request)) return nil, adh.error(validate.ErrRequestNotSet, scope) } domainName := request.GetDomain() workflowID := request.GetExecution().GetWorkflowID() runID := request.GetExecution().GetRunID() skipErrors := request.GetSkipErrors() resp, err := adh.DescribeWorkflowExecution( ctx, &types.AdminDescribeWorkflowExecutionRequest{ Domain: domainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, }) if err != nil { logger.Error("Describe workflow failed", tag.Error(err)) if !skipErrors { return nil, adh.error(err, scope) } } msStr := resp.GetMutableStateInDatabase() ms := persistence.WorkflowMutableState{} err = json.Unmarshal([]byte(msStr), &ms) if err != nil { logger.Error(fmt.Sprintf("DeleteWorkflow failed: Cannot unmarshal mutableState: %#v", err)) return nil, adh.error(err, scope) } domainID := ms.ExecutionInfo.DomainID logger = logger.WithTags( tag.WorkflowDomainID(domainID), tag.WorkflowDomainName(domainName), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), ) shardID := resp.GetShardID() shardIDInt, err := strconv.Atoi(shardID) if err != nil { logger.Error(fmt.Sprintf("Cannot convert shardID(%v) to int: %#v", shardID, err)) return nil, adh.error(err, scope) } ctx, cancel := context.WithTimeout(ctx, 60*time.Second) defer cancel() deletedFromHistory := adh.deleteWorkflowFromHistory(ctx, logger, shardIDInt, ms) deletedFromExecutions := adh.deleteWorkflowFromExecutions(ctx, logger, shardIDInt, domainID, workflowID, runID, scope) deletedFromVisibility := false if deletedFromExecutions { // Without deleting the executions record, let's not delete the visibility record. // If we do that, workflow won't be visible but it will exist in the DB deletedFromVisibility = adh.deleteWorkflowFromVisibility(ctx, logger, domainID, domainName, workflowID, runID) } return &types.AdminDeleteWorkflowResponse{ HistoryDeleted: deletedFromHistory, ExecutionsDeleted: deletedFromExecutions, VisibilityDeleted: deletedFromVisibility, }, nil } // CloseShard returns information about the internal states of a history host func (adh *adminHandlerImpl) CloseShard( ctx context.Context, request *types.CloseShardRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminCloseShardScope) defer sw.Stop() if request == nil { return adh.error(validate.ErrRequestNotSet, scope) } if err := adh.GetHistoryClient().CloseShard(ctx, request); err != nil { return adh.error(err, scope) } return nil } // ResetQueue resets processing queue states func (adh *adminHandlerImpl) ResetQueue( ctx context.Context, request *types.ResetQueueRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminResetQueueScope) defer sw.Stop() if request == nil || request.Type == nil { return adh.error(validate.ErrRequestNotSet, scope) } if request.GetClusterName() == "" { return adh.error(validate.ErrClusterNameNotSet, scope) } if err := adh.GetHistoryClient().ResetQueue(ctx, request); err != nil { return adh.error(err, scope) } return nil } // DescribeQueue describes processing queue states func (adh *adminHandlerImpl) DescribeQueue( ctx context.Context, request *types.DescribeQueueRequest, ) (resp *types.DescribeQueueResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminDescribeQueueScope) defer sw.Stop() if request == nil || request.Type == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } if request.GetClusterName() == "" { return nil, adh.error(validate.ErrClusterNameNotSet, scope) } return adh.GetHistoryClient().DescribeQueue(ctx, request) } // DescribeShardDistribution returns information about history shard distribution func (adh *adminHandlerImpl) DescribeShardDistribution( ctx context.Context, request *types.DescribeShardDistributionRequest, ) (resp *types.DescribeShardDistributionResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() _, sw := adh.startRequestProfile(ctx, metrics.AdminDescribeShardDistributionScope) defer sw.Stop() resp = &types.DescribeShardDistributionResponse{ NumberOfShards: int32(adh.numberOfHistoryShards), Shards: make(map[int32]string), } offset := int(request.PageID * request.PageSize) nextPageStart := offset + int(request.PageSize) for shardID := offset; shardID < adh.numberOfHistoryShards && shardID < nextPageStart; shardID++ { info, err := lookup.HistoryServerByShardID(adh.GetMembershipResolver(), shardID) if err != nil { resp.Shards[int32(shardID)] = "unknown" } else { resp.Shards[int32(shardID)] = info.Identity() } } return resp, nil } // DescribeHistoryHost returns information about the internal states of a history host func (adh *adminHandlerImpl) DescribeHistoryHost( ctx context.Context, request *types.DescribeHistoryHostRequest, ) (resp *types.DescribeHistoryHostResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminDescribeHistoryHostScope) defer sw.Stop() if request == nil || (request.ShardIDForHost == nil && request.ExecutionForHost == nil && request.HostAddress == nil) { return nil, adh.error(validate.ErrRequestNotSet, scope) } if request.ExecutionForHost != nil { if err := validate.CheckExecution(request.ExecutionForHost); err != nil { return nil, adh.error(err, scope) } } return adh.GetHistoryClient().DescribeHistoryHost(ctx, request) } // GetWorkflowExecutionRawHistoryV2 - retrieves the history of workflow execution func (adh *adminHandlerImpl) GetWorkflowExecutionRawHistoryV2( ctx context.Context, request *types.GetWorkflowExecutionRawHistoryV2Request, ) (resp *types.GetWorkflowExecutionRawHistoryV2Response, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminGetWorkflowExecutionRawHistoryV2Scope) defer sw.Stop() if err := adh.validateGetWorkflowExecutionRawHistoryV2Request( request, ); err != nil { return nil, adh.error(err, scope) } domainID, err := adh.GetDomainCache().GetDomainID(request.GetDomain()) if err != nil { return nil, adh.error(err, scope) } scope = scope.Tagged(metrics.DomainTag(request.GetDomain())) execution := request.Execution var pageToken *getWorkflowRawHistoryV2Token var targetVersionHistory *persistence.VersionHistory if request.NextPageToken == nil { response, err := adh.GetHistoryClient().GetMutableState(ctx, &types.GetMutableStateRequest{ DomainUUID: domainID, Execution: execution, }) if err != nil { return nil, adh.error(err, scope) } versionHistories := persistence.NewVersionHistoriesFromInternalType( response.GetVersionHistories(), ) targetVersionHistory, err = adh.setRequestDefaultValueAndGetTargetVersionHistory( request, versionHistories, ) if err != nil { return nil, adh.error(err, scope) } pageToken = adh.generatePaginationToken(request, versionHistories) } else { pageToken, err = deserializeRawHistoryToken(request.NextPageToken) if err != nil { return nil, adh.error(err, scope) } versionHistories := pageToken.VersionHistories if versionHistories == nil { return nil, adh.error(&types.BadRequestError{Message: "Invalid version histories."}, scope) } targetVersionHistory, err = adh.setRequestDefaultValueAndGetTargetVersionHistory( request, persistence.NewVersionHistoriesFromInternalType(versionHistories), ) if err != nil { return nil, adh.error(err, scope) } } if err := adh.validatePaginationToken( request, pageToken, ); err != nil { return nil, adh.error(err, scope) } if pageToken.StartEventID+1 == pageToken.EndEventID { // API is exclusive-exclusive. Return empty response here. return &types.GetWorkflowExecutionRawHistoryV2Response{ HistoryBatches: []*types.DataBlob{}, NextPageToken: nil, // no further pagination VersionHistory: targetVersionHistory.ToInternalType(), }, nil } pageSize := int(request.GetMaximumPageSize()) shardID := common.WorkflowIDToHistoryShard( execution.GetWorkflowID(), adh.numberOfHistoryShards, ) rawHistoryResponse, err := adh.GetHistoryManager().ReadRawHistoryBranch(ctx, &persistence.ReadHistoryBranchRequest{ BranchToken: targetVersionHistory.GetBranchToken(), // GetWorkflowExecutionRawHistoryV2 is exclusive exclusive. // ReadRawHistoryBranch is inclusive exclusive. MinEventID: pageToken.StartEventID + 1, MaxEventID: pageToken.EndEventID, PageSize: pageSize, NextPageToken: pageToken.PersistenceToken, ShardID: common.IntPtr(shardID), DomainName: request.GetDomain(), }) if err != nil { if _, ok := err.(*types.EntityNotExistsError); ok { // when no events can be returned from DB, DB layer will return // EntityNotExistsError, this API shall return empty response return &types.GetWorkflowExecutionRawHistoryV2Response{ HistoryBatches: []*types.DataBlob{}, NextPageToken: nil, // no further pagination VersionHistory: targetVersionHistory.ToInternalType(), }, nil } return nil, err } pageToken.PersistenceToken = rawHistoryResponse.NextPageToken size := rawHistoryResponse.Size scope.RecordTimer(metrics.HistorySize, time.Duration(size)) rawBlobs := rawHistoryResponse.HistoryEventBlobs blobs := []*types.DataBlob{} for _, blob := range rawBlobs { blobs = append(blobs, blob.ToInternal()) } result := &types.GetWorkflowExecutionRawHistoryV2Response{ HistoryBatches: blobs, VersionHistory: targetVersionHistory.ToInternalType(), } if len(pageToken.PersistenceToken) == 0 { result.NextPageToken = nil } else { result.NextPageToken, err = serializeRawHistoryToken(pageToken) if err != nil { return nil, err } } return result, nil } // DescribeCluster return information about cadence deployment func (adh *adminHandlerImpl) DescribeCluster( ctx context.Context, ) (resp *types.DescribeClusterResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminDescribeClusterScope) defer sw.Stop() // expose visibility store backend and if advanced options are available ave := types.PersistenceFeature{ Key: "advancedVisibilityEnabled", Enabled: adh.params.ESConfig != nil, } visibilityStoreInfo := types.PersistenceInfo{ Backend: adh.Resource.GetVisibilityManager().GetName(), Features: []*types.PersistenceFeature{&ave}, } // expose history store backend historyStoreInfo := types.PersistenceInfo{ Backend: adh.GetHistoryManager().GetName(), } membershipInfo := types.MembershipInfo{} if monitor := adh.GetMembershipResolver(); monitor != nil { currentHost, err := monitor.WhoAmI() if err != nil { return nil, adh.error(err, scope) } membershipInfo.CurrentHost = &types.HostInfo{ Identity: currentHost.Identity(), } var rings []*types.RingInfo for _, role := range service.ListWithRing { var servers []*types.HostInfo members, err := monitor.Members(role) if err != nil { return nil, adh.error(err, scope) } for _, server := range members { servers = append(servers, &types.HostInfo{ Identity: server.Identity(), }) membershipInfo.ReachableMembers = append(membershipInfo.ReachableMembers, server.Identity()) } rings = append(rings, &types.RingInfo{ Role: role, MemberCount: int32(len(servers)), Members: servers, }) } membershipInfo.Rings = rings } return &types.DescribeClusterResponse{ SupportedClientVersions: &types.SupportedClientVersions{ GoSdk: client.SupportedGoSDKVersion, JavaSdk: client.SupportedJavaSDKVersion, }, MembershipInfo: &membershipInfo, PersistenceInfo: map[string]*types.PersistenceInfo{ "visibilityStore": &visibilityStoreInfo, "historyStore": &historyStoreInfo, }, }, nil } // GetReplicationMessages returns new replication tasks since the read level provided in the token. func (adh *adminHandlerImpl) GetReplicationMessages( ctx context.Context, request *types.GetReplicationMessagesRequest, ) (resp *types.GetReplicationMessagesResponse, err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminGetReplicationMessagesScope) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } if request.ClusterName == "" { return nil, adh.error(validate.ErrClusterNameNotSet, scope) } resp, err = adh.GetHistoryRawClient().GetReplicationMessages(ctx, request) if err != nil { return nil, adh.error(err, scope) } return resp, nil } // GetDomainReplicationMessages returns new domain replication tasks since last retrieved task ID. func (adh *adminHandlerImpl) GetDomainReplicationMessages( ctx context.Context, request *types.GetDomainReplicationMessagesRequest, ) (resp *types.GetDomainReplicationMessagesResponse, err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminGetDomainReplicationMessagesScope) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } if adh.GetDomainReplicationQueue() == nil { return nil, adh.error(errors.New("domain replication queue not enabled for cluster"), scope) } lastMessageID := defaultLastMessageID if request.LastRetrievedMessageID != nil { lastMessageID = request.GetLastRetrievedMessageID() } if lastMessageID == defaultLastMessageID { clusterAckLevels, err := adh.GetDomainReplicationQueue().GetAckLevels(ctx) if err == nil { if ackLevel, ok := clusterAckLevels[request.GetClusterName()]; ok { lastMessageID = ackLevel } } } replicationTasks, lastMessageID, err := adh.GetDomainReplicationQueue().GetReplicationMessages( ctx, lastMessageID, getDomainReplicationMessageBatchSize, ) if err != nil { return nil, adh.error(err, scope) } lastProcessedMessageID := defaultLastMessageID if request.LastProcessedMessageID != nil { lastProcessedMessageID = request.GetLastProcessedMessageID() } if err := adh.GetDomainReplicationQueue().UpdateAckLevel(ctx, lastProcessedMessageID, request.GetClusterName()); err != nil { adh.GetLogger().Warn("Failed to update domain replication queue ack level.", tag.TaskID(int64(lastProcessedMessageID)), tag.ClusterName(request.GetClusterName())) } return &types.GetDomainReplicationMessagesResponse{ Messages: &types.ReplicationMessages{ ReplicationTasks: replicationTasks, LastRetrievedMessageID: lastMessageID, }, }, nil } // GetDLQReplicationMessages returns new replication tasks based on the dlq info. func (adh *adminHandlerImpl) GetDLQReplicationMessages( ctx context.Context, request *types.GetDLQReplicationMessagesRequest, ) (resp *types.GetDLQReplicationMessagesResponse, err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminGetDLQReplicationMessagesScope) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } if len(request.GetTaskInfos()) == 0 { return nil, adh.error(validate.ErrEmptyReplicationInfo, scope) } resp, err = adh.GetHistoryClient().GetDLQReplicationMessages(ctx, request) if err != nil { return nil, adh.error(err, scope) } return resp, nil } // ReapplyEvents applies stale events to the current workflow and the current run func (adh *adminHandlerImpl) ReapplyEvents( ctx context.Context, request *types.ReapplyEventsRequest, ) (err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminReapplyEventsScope) defer sw.Stop() if request == nil { return adh.error(validate.ErrRequestNotSet, scope) } if request.GetDomainName() == "" { return adh.error(validate.ErrDomainNotSet, scope) } if request.WorkflowExecution == nil { return adh.error(validate.ErrExecutionNotSet, scope) } if request.GetWorkflowExecution().GetWorkflowID() == "" { return adh.error(validate.ErrWorkflowIDNotSet, scope) } if request.GetEvents() == nil { return adh.error(validate.ErrWorkflowIDNotSet, scope) } domainEntry, err := adh.GetDomainCache().GetDomain(request.GetDomainName()) if err != nil { return adh.error(err, scope) } err = adh.GetHistoryClient().ReapplyEvents(ctx, &types.HistoryReapplyEventsRequest{ DomainUUID: domainEntry.GetInfo().ID, Request: request, }) if err != nil { return adh.error(err, scope) } return nil } // ReadDLQMessages reads messages from DLQ func (adh *adminHandlerImpl) ReadDLQMessages( ctx context.Context, request *types.ReadDLQMessagesRequest, ) (resp *types.ReadDLQMessagesResponse, err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminReadDLQMessagesScope) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } if request.Type == nil { return nil, adh.error(validate.ErrEmptyQueueType, scope) } if request.GetMaximumPageSize() <= 0 { request.MaximumPageSize = constants.ReadDLQMessagesPageSize } if request.InclusiveEndMessageID == nil { request.InclusiveEndMessageID = common.Ptr(constants.InclusiveEndMessageID) } var tasks []*types.ReplicationTask var token []byte var op func(ctx context.Context) error switch request.GetType() { case types.DLQTypeReplication: return adh.GetHistoryClient().ReadDLQMessages(ctx, request) case types.DLQTypeDomain: op = func(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() default: var err error tasks, token, err = adh.domainDLQHandler.Read( ctx, request.GetInclusiveEndMessageID(), int(request.GetMaximumPageSize()), request.GetNextPageToken()) return err } } default: return nil, &types.BadRequestError{Message: "The DLQ type is not supported."} } err = adh.throttleRetry.Do(ctx, op) if err != nil { return nil, adh.error(err, scope) } return &types.ReadDLQMessagesResponse{ ReplicationTasks: tasks, NextPageToken: token, }, nil } // PurgeDLQMessages purge messages from DLQ func (adh *adminHandlerImpl) PurgeDLQMessages( ctx context.Context, request *types.PurgeDLQMessagesRequest, ) (err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminPurgeDLQMessagesScope) defer sw.Stop() if request == nil { return adh.error(validate.ErrRequestNotSet, scope) } if request.Type == nil { return adh.error(validate.ErrEmptyQueueType, scope) } if request.InclusiveEndMessageID == nil { request.InclusiveEndMessageID = common.Ptr(constants.InclusiveEndMessageID) } var op func(ctx context.Context) error switch request.GetType() { case types.DLQTypeReplication: return adh.GetHistoryClient().PurgeDLQMessages(ctx, request) case types.DLQTypeDomain: op = func(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() default: return adh.domainDLQHandler.Purge( ctx, request.GetInclusiveEndMessageID(), ) } } default: return &types.BadRequestError{Message: "The DLQ type is not supported."} } err = adh.throttleRetry.Do(ctx, op) if err != nil { return adh.error(err, scope) } return nil } func (adh *adminHandlerImpl) CountDLQMessages( ctx context.Context, request *types.CountDLQMessagesRequest, ) (resp *types.CountDLQMessagesResponse, err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminCountDLQMessagesScope) defer sw.Stop() domain, err := adh.domainDLQHandler.Count(ctx, request.ForceFetch) if err != nil { return nil, adh.error(err, scope) } history, err := adh.GetHistoryClient().CountDLQMessages(ctx, request) if err != nil { err = adh.error(err, scope) } return &types.CountDLQMessagesResponse{ History: history.Entries, Domain: domain, }, err } // MergeDLQMessages merges DLQ messages func (adh *adminHandlerImpl) MergeDLQMessages( ctx context.Context, request *types.MergeDLQMessagesRequest, ) (resp *types.MergeDLQMessagesResponse, err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminMergeDLQMessagesScope) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } if request.Type == nil { return nil, adh.error(validate.ErrEmptyQueueType, scope) } if request.InclusiveEndMessageID == nil { request.InclusiveEndMessageID = common.Ptr(constants.InclusiveEndMessageID) } var token []byte var op func(ctx context.Context) error switch request.GetType() { case types.DLQTypeReplication: return adh.GetHistoryClient().MergeDLQMessages(ctx, request) case types.DLQTypeDomain: op = func(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() default: var err error token, err = adh.domainDLQHandler.Merge( ctx, request.GetInclusiveEndMessageID(), int(request.GetMaximumPageSize()), request.GetNextPageToken(), ) return err } } default: return nil, &types.BadRequestError{Message: "The DLQ type is not supported."} } err = adh.throttleRetry.Do(ctx, op) if err != nil { return nil, adh.error(err, scope) } return &types.MergeDLQMessagesResponse{ NextPageToken: token, }, nil } // RefreshWorkflowTasks re-generates the workflow tasks func (adh *adminHandlerImpl) RefreshWorkflowTasks( ctx context.Context, request *types.RefreshWorkflowTasksRequest, ) (err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminRefreshWorkflowTasksScope) defer sw.Stop() if request == nil { return adh.error(validate.ErrRequestNotSet, scope) } if err := validate.CheckExecution(request.Execution); err != nil { return adh.error(err, scope) } domainEntry, err := adh.GetDomainCache().GetDomain(request.GetDomain()) if err != nil { return adh.error(err, scope) } err = adh.GetHistoryClient().RefreshWorkflowTasks(ctx, &types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: domainEntry.GetInfo().ID, Request: request, }) if err != nil { return adh.error(err, scope) } return nil } // ResendReplicationTasks requests replication task from remote cluster func (adh *adminHandlerImpl) ResendReplicationTasks( ctx context.Context, request *types.ResendReplicationTasksRequest, ) (err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminResendReplicationTasksScope) defer sw.Stop() if request == nil { return adh.error(validate.ErrRequestNotSet, scope) } resender := ndc.NewHistoryResender( adh.GetDomainCache(), adh.GetClientBean(), func(ctx context.Context, request *types.ReplicateEventsV2Request) error { return adh.GetHistoryClient().ReplicateEventsV2(ctx, request) }, nil, nil, adh.GetLogger(), ) return resender.SendSingleWorkflowHistory( request.GetRemoteCluster(), request.DomainID, request.GetWorkflowID(), request.GetRunID(), request.StartEventID, request.StartVersion, request.EndEventID, request.EndVersion, ) } func (adh *adminHandlerImpl) GetCrossClusterTasks( ctx context.Context, request *types.GetCrossClusterTasksRequest, ) (resp *types.GetCrossClusterTasksResponse, err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminGetCrossClusterTasksScope) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } if request.TargetCluster == "" { return nil, adh.error(validate.ErrClusterNameNotSet, scope) } resp, err = adh.GetHistoryRawClient().GetCrossClusterTasks(ctx, request) if err != nil { return nil, adh.error(err, scope) } return resp, nil } func (adh *adminHandlerImpl) RespondCrossClusterTasksCompleted( ctx context.Context, request *types.RespondCrossClusterTasksCompletedRequest, ) (resp *types.RespondCrossClusterTasksCompletedResponse, err error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &err) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminRespondCrossClusterTasksCompletedScope) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } if request.TargetCluster == "" { return nil, adh.error(validate.ErrClusterNameNotSet, scope) } resp, err = adh.GetHistoryClient().RespondCrossClusterTasksCompleted(ctx, request) if err != nil { return nil, adh.error(err, scope) } return resp, nil } func (adh *adminHandlerImpl) validateGetWorkflowExecutionRawHistoryV2Request( request *types.GetWorkflowExecutionRawHistoryV2Request, ) error { execution := request.Execution if len(execution.GetWorkflowID()) == 0 { return &types.BadRequestError{Message: "Invalid WorkflowID."} } // TODO currently, this API is only going to be used by re-send history events // to remote cluster if kafka is lossy again, in the future, this API can be used // by CLI and client, then empty runID (meaning the current workflow) should be allowed if _, err := uuid.Parse(execution.GetRunID()); err != nil { return &types.BadRequestError{Message: "Invalid RunID."} } pageSize := int(request.GetMaximumPageSize()) if pageSize <= 0 { return &types.BadRequestError{Message: "Invalid PageSize."} } if (request.StartEventID != nil && request.StartEventVersion == nil) || (request.StartEventID == nil && request.StartEventVersion != nil) { return &types.BadRequestError{Message: "Invalid start event id and start event version combination."} } if (request.EndEventID != nil && request.EndEventVersion == nil) || (request.EndEventID == nil && request.EndEventVersion != nil) { return &types.BadRequestError{Message: "Invalid end event id and end event version combination."} } return nil } func (adh *adminHandlerImpl) validateConfigForAdvanceVisibility() error { if adh.params.ESConfig == nil || adh.params.ESClient == nil { return errors.New("ES related config not found") } return nil } func (adh *adminHandlerImpl) setRequestDefaultValueAndGetTargetVersionHistory( request *types.GetWorkflowExecutionRawHistoryV2Request, versionHistories *persistence.VersionHistories, ) (*persistence.VersionHistory, error) { targetBranch, err := versionHistories.GetCurrentVersionHistory() if err != nil { return nil, err } firstItem, err := targetBranch.GetFirstItem() if err != nil { return nil, err } lastItem, err := targetBranch.GetLastItem() if err != nil { return nil, err } if request.StartEventID == nil || request.StartEventVersion == nil { // If start event is not set, get the events from the first event // As the API is exclusive-exclusive, use first event id - 1 here request.StartEventID = common.Int64Ptr(constants.FirstEventID - 1) request.StartEventVersion = common.Int64Ptr(firstItem.Version) } if request.EndEventID == nil || request.EndEventVersion == nil { // If end event is not set, get the events until the end event // As the API is exclusive-exclusive, use end event id + 1 here request.EndEventID = common.Int64Ptr(lastItem.EventID + 1) request.EndEventVersion = common.Int64Ptr(lastItem.Version) } if request.GetStartEventID() < 0 { return nil, &types.BadRequestError{Message: "Invalid FirstEventID && NextEventID combination."} } // get branch based on the end event if end event is defined in the request if request.GetEndEventID() == lastItem.EventID+1 && request.GetEndEventVersion() == lastItem.Version { // this is a special case, target branch remains the same } else { endItem := persistence.NewVersionHistoryItem(request.GetEndEventID(), request.GetEndEventVersion()) _, targetBranch, err = versionHistories.FindFirstVersionHistoryByItem(endItem) if err != nil { return nil, err } } startItem := persistence.NewVersionHistoryItem(request.GetStartEventID(), request.GetStartEventVersion()) // If the request start event is defined. The start event may be on a different branch as current branch. // We need to find the LCA of the start event and the current branch. if request.GetStartEventID() == constants.FirstEventID-1 && request.GetStartEventVersion() == firstItem.Version { // this is a special case, start event is on the same branch as target branch } else { if !targetBranch.ContainsItem(startItem) { _, startBranch, err := versionHistories.FindFirstVersionHistoryByItem(startItem) if err != nil { return nil, err } startItem, err = targetBranch.FindLCAItem(startBranch) if err != nil { return nil, err } request.StartEventID = common.Int64Ptr(startItem.EventID) request.StartEventVersion = common.Int64Ptr(startItem.Version) } } return targetBranch, nil } func (adh *adminHandlerImpl) generatePaginationToken( request *types.GetWorkflowExecutionRawHistoryV2Request, versionHistories *persistence.VersionHistories, ) *getWorkflowRawHistoryV2Token { execution := request.Execution return &getWorkflowRawHistoryV2Token{ DomainName: request.GetDomain(), WorkflowID: execution.GetWorkflowID(), RunID: execution.GetRunID(), StartEventID: request.GetStartEventID(), StartEventVersion: request.GetStartEventVersion(), EndEventID: request.GetEndEventID(), EndEventVersion: request.GetEndEventVersion(), VersionHistories: versionHistories.ToInternalType(), PersistenceToken: nil, // this is the initialized value } } func (adh *adminHandlerImpl) validatePaginationToken( request *types.GetWorkflowExecutionRawHistoryV2Request, token *getWorkflowRawHistoryV2Token, ) error { execution := request.Execution if request.GetDomain() != token.DomainName || execution.GetWorkflowID() != token.WorkflowID || execution.GetRunID() != token.RunID || request.GetStartEventID() != token.StartEventID || request.GetStartEventVersion() != token.StartEventVersion || request.GetEndEventID() != token.EndEventID || request.GetEndEventVersion() != token.EndEventVersion { return &types.BadRequestError{Message: "Invalid pagination token."} } return nil } // startRequestProfile initiates recording of request metrics func (adh *adminHandlerImpl) startRequestProfile(ctx context.Context, scope metrics.ScopeIdx) (metrics.Scope, metrics.Stopwatch) { metricsScope := adh.GetMetricsClient().Scope(scope).Tagged(metrics.DomainUnknownTag()).Tagged(metrics.GetContextTags(ctx)...) sw := metricsScope.StartTimer(metrics.CadenceLatency) metricsScope.IncCounter(metrics.CadenceRequests) return metricsScope, sw } func (adh *adminHandlerImpl) error(err error, scope metrics.Scope) error { logger := adh.GetLogger().Helper() switch err.(type) { case *types.InternalServiceError: logger.Error("Internal service error", tag.Error(err)) scope.IncCounter(metrics.CadenceFailures) return err case *types.BadRequestError: scope.IncCounter(metrics.CadenceErrBadRequestCounter) return err case *types.ServiceBusyError: scope.IncCounter(metrics.CadenceErrServiceBusyCounter) return err case *types.EntityNotExistsError: return err default: logger.Error("Uncategorized error", tag.Error(err)) scope.IncCounter(metrics.CadenceFailures) return &types.InternalServiceError{Message: err.Error()} } } func convertIndexedValueTypeToESDataType(valueType types.IndexedValueType) string { switch valueType { case types.IndexedValueTypeString: return "text" case types.IndexedValueTypeKeyword: return "keyword" case types.IndexedValueTypeInt: return "long" case types.IndexedValueTypeDouble: return "double" case types.IndexedValueTypeBool: return "boolean" case types.IndexedValueTypeDatetime: return "date" default: return "" } } func serializeRawHistoryToken(token *getWorkflowRawHistoryV2Token) ([]byte, error) { if token == nil { return nil, nil } bytes, err := json.Marshal(token) return bytes, err } func deserializeRawHistoryToken(bytes []byte) (*getWorkflowRawHistoryV2Token, error) { token := &getWorkflowRawHistoryV2Token{} err := json.Unmarshal(bytes, token) return token, err } func (adh *adminHandlerImpl) GetDynamicConfig(ctx context.Context, request *types.GetDynamicConfigRequest) (_ *types.GetDynamicConfigResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminGetDynamicConfigScope) defer sw.Stop() if request == nil || request.ConfigName == "" { return nil, adh.error(validate.ErrRequestNotSet, scope) } keyVal, err := dynamicproperties.GetKeyFromKeyName(request.ConfigName) if err != nil { return nil, adh.error(err, scope) } var value interface{} if request.Filters == nil { value, err = adh.params.DynamicConfig.GetValue(keyVal) if err != nil { return nil, adh.error(err, scope) } } else { convFilters, err := convertFilterListToMap(request.Filters) if err != nil { return nil, adh.error(err, scope) } value, err = adh.params.DynamicConfig.GetValueWithFilters(keyVal, convFilters) if err != nil { return nil, adh.error(err, scope) } } data, err := json.Marshal(value) if err != nil { return nil, adh.error(err, scope) } return &types.GetDynamicConfigResponse{ Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: data, }, }, nil } func (adh *adminHandlerImpl) UpdateDynamicConfig(ctx context.Context, request *types.UpdateDynamicConfigRequest) (retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminUpdateDynamicConfigScope) defer sw.Stop() if request == nil || request.ConfigName == "" { return adh.error(validate.ErrRequestNotSet, scope) } keyVal, err := dynamicproperties.GetKeyFromKeyName(request.ConfigName) if err != nil { return adh.error(err, scope) } return adh.params.DynamicConfig.UpdateValue(keyVal, request.ConfigValues) } func (adh *adminHandlerImpl) RestoreDynamicConfig(ctx context.Context, request *types.RestoreDynamicConfigRequest) (retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminRestoreDynamicConfigScope) defer sw.Stop() if request == nil || request.ConfigName == "" { return adh.error(validate.ErrRequestNotSet, scope) } keyVal, err := dynamicproperties.GetKeyFromKeyName(request.ConfigName) if err != nil { return adh.error(err, scope) } var filters map[dynamicproperties.Filter]interface{} if request.Filters == nil { filters = nil } else { filters, err = convertFilterListToMap(request.Filters) if err != nil { return adh.error(validate.ErrInvalidFilters, scope) } } return adh.params.DynamicConfig.RestoreValue(keyVal, filters) } func (adh *adminHandlerImpl) ListDynamicConfig(ctx context.Context, request *types.ListDynamicConfigRequest) (_ *types.ListDynamicConfigResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.AdminListDynamicConfigScope) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } keyVal, err := dynamicproperties.GetKeyFromKeyName(request.ConfigName) if err != nil || request.ConfigName == "" { entries, err2 := adh.params.DynamicConfig.ListValue(nil) if err2 != nil { return nil, adh.error(err2, scope) } return &types.ListDynamicConfigResponse{ Entries: entries, }, nil } entries, err2 := adh.params.DynamicConfig.ListValue(keyVal) if err2 != nil { err = adh.error(err2, scope) return nil, adh.error(err, scope) } return &types.ListDynamicConfigResponse{ Entries: entries, }, nil } func (adh *adminHandlerImpl) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest) (_ *types.GetGlobalIsolationGroupsResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.GetGlobalIsolationGroups) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } resp, err := adh.isolationGroups.GetGlobalState(ctx) if err != nil { return nil, adh.error(err, scope) } return resp, nil } func (adh *adminHandlerImpl) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest) (_ *types.UpdateGlobalIsolationGroupsResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.UpdateGlobalIsolationGroups) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } err := adh.isolationGroups.UpdateGlobalState(ctx, *request) if err != nil { return nil, adh.error(err, scope) } return &types.UpdateGlobalIsolationGroupsResponse{}, nil } func (adh *adminHandlerImpl) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest) (_ *types.GetDomainIsolationGroupsResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.GetDomainIsolationGroups) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } resp, err := adh.isolationGroups.GetDomainState(ctx, types.GetDomainIsolationGroupsRequest{Domain: request.Domain}) if err != nil { return nil, adh.error(err, scope) } return resp, nil } func (adh *adminHandlerImpl) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest) (_ *types.UpdateDomainIsolationGroupsResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.UpdateDomainIsolationGroups) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } err := adh.isolationGroups.UpdateDomainState(ctx, *request) if err != nil { return nil, adh.error(err, scope) } return &types.UpdateDomainIsolationGroupsResponse{}, nil } func (adh *adminHandlerImpl) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.GetDomainAsyncWorkflowConfiguratonRequest) (_ *types.GetDomainAsyncWorkflowConfiguratonResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.GetDomainAsyncWorkflowConfiguraton) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } resp, err := adh.asyncWFQueueConfigs.GetConfiguraton(ctx, request) if err != nil { return nil, adh.error(err, scope) } return resp, nil } func (adh *adminHandlerImpl) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, request *types.UpdateDomainAsyncWorkflowConfiguratonRequest) (_ *types.UpdateDomainAsyncWorkflowConfiguratonResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.UpdateDomainAsyncWorkflowConfiguraton) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } resp, err := adh.asyncWFQueueConfigs.UpdateConfiguration(ctx, request) if err != nil { return nil, adh.error(err, scope) } return resp, nil } func (adh *adminHandlerImpl) UpdateTaskListPartitionConfig(ctx context.Context, request *types.UpdateTaskListPartitionConfigRequest) (_ *types.UpdateTaskListPartitionConfigResponse, retError error) { defer func() { log.CapturePanic(recover(), adh.GetLogger(), &retError) }() scope, sw := adh.startRequestProfile(ctx, metrics.UpdateTaskListPartitionConfig) defer sw.Stop() if request == nil { return nil, adh.error(validate.ErrRequestNotSet, scope) } domainID, err := adh.GetDomainCache().GetDomainID(request.Domain) if err != nil { return nil, adh.error(err, scope) } if request.TaskList == nil { return nil, adh.error(validate.ErrTaskListNotSet, scope) } if request.TaskList.Kind == nil { return nil, adh.error(&types.BadRequestError{Message: "Task list kind not set."}, scope) } if *request.TaskList.Kind != types.TaskListKindNormal { return nil, adh.error(&types.BadRequestError{Message: "Only normal tasklist's partition config can be updated."}, scope) } if request.TaskListType == nil { return nil, adh.error(&types.BadRequestError{Message: "Task list type not set."}, scope) } if request.PartitionConfig == nil { return nil, adh.error(&types.BadRequestError{Message: "Task list partition config is not set in the request."}, scope) } if len(request.PartitionConfig.WritePartitions) > len(request.PartitionConfig.ReadPartitions) { return nil, adh.error(&types.BadRequestError{Message: "The number of write partitions cannot be larger than the number of read partitions."}, scope) } if len(request.PartitionConfig.WritePartitions) <= 0 { return nil, adh.error(&types.BadRequestError{Message: "The number of partitions must be larger than 0."}, scope) } _, err = adh.GetMatchingClient().UpdateTaskListPartitionConfig(ctx, &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: domainID, TaskList: request.TaskList, TaskListType: request.TaskListType, PartitionConfig: request.PartitionConfig, }) if err != nil { return nil, err } return &types.UpdateTaskListPartitionConfigResponse{}, nil } func convertFromDataBlob(blob *types.DataBlob) (interface{}, error) { switch *blob.EncodingType { case types.EncodingTypeJSON: var v interface{} err := json.Unmarshal(blob.Data, &v) return v, err default: return nil, errors.New("unsupported blob encoding") } } func convertFilterListToMap(filters []*types.DynamicConfigFilter) (map[dynamicproperties.Filter]interface{}, error) { newFilters := make(map[dynamicproperties.Filter]interface{}) for _, filter := range filters { val, err := convertFromDataBlob(filter.Value) if err != nil { return nil, err } newFilters[dynamicproperties.ParseFilter(filter.Name)] = val } return newFilters, nil } ================================================ FILE: service/frontend/admin/handler_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package admin import ( "context" "encoding/json" "errors" "fmt" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/asyncworkflow/queueconfigapi" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" esmock "github.com/uber/cadence/common/elasticsearch/mocks" "github.com/uber/cadence/common/isolationgroup/isolationgroupapi" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" frontendcfg "github.com/uber/cadence/service/frontend/config" "github.com/uber/cadence/service/frontend/validate" ) type ( adminHandlerSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockResource *resource.Test mockHistoryClient *history.MockClient mockDomainCache *cache.MockDomainCache frontendClient *frontend.MockClient mockResolver *membership.MockResolver mockHistoryV2Mgr *mocks.HistoryV2Manager domainName string domainID string handler *adminHandlerImpl } ) func TestAdminHandlerSuite(t *testing.T) { s := new(adminHandlerSuite) suite.Run(t, s) } func (s *adminHandlerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.domainName = "some random domain name" s.domainID = "some random domain ID" s.controller = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.Frontend) s.mockDomainCache = s.mockResource.DomainCache s.mockHistoryClient = s.mockResource.HistoryClient s.mockHistoryV2Mgr = s.mockResource.HistoryMgr s.frontendClient = s.mockResource.FrontendClient s.mockResolver = s.mockResource.MembershipResolver params := &resource.Params{ Logger: testlogger.New(s.T()), ThrottledLogger: testlogger.New(s.T()), MetricScope: tally.NewTestScope(service.Frontend, make(map[string]string)), MetricsClient: metrics.NewNoopMetricsClient(), PersistenceConfig: config.Persistence{ NumHistoryShards: 1, }, } config := &frontendcfg.Config{ EnableAdminProtection: dynamicproperties.GetBoolPropertyFn(false), EnableGracefulFailover: dynamicproperties.GetBoolPropertyFn(false), } dh := domain.NewMockHandler(s.controller) s.handler = NewHandler(s.mockResource, params, config, dh).(*adminHandlerImpl) s.handler.Start() } func (s *adminHandlerSuite) TearDownTest() { s.controller.Finish() s.mockResource.Finish(s.T()) s.handler.Stop() } func (s *adminHandlerSuite) TestMaintainCorruptWorkflow_NormalWorkflow() { s.testMaintainCorruptWorkflow(nil, nil, false) } func (s *adminHandlerSuite) TestMaintainCorruptWorkflow_WorkflowDoesNotExist() { err := &types.EntityNotExistsError{Message: "Workflow does not exist"} s.testMaintainCorruptWorkflow(err, nil, false) } func (s *adminHandlerSuite) TestMaintainCorruptWorkflow_NoStartEvent() { s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(s.domainName, nil).AnyTimes() err := &types.InternalServiceError{Message: "unable to get workflow start event"} s.testMaintainCorruptWorkflow(err, nil, true) } func (s *adminHandlerSuite) TestMaintainCorruptWorkflow_NoStartEventHistory() { s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(s.domainName, nil).AnyTimes() err := &types.InternalServiceError{Message: "unable to get workflow start event"} s.testMaintainCorruptWorkflow(nil, err, true) } func (s *adminHandlerSuite) TestMaintainCorruptWorkflow_UnableToGetScheduledEvent() { s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(s.domainName, nil).AnyTimes() err := &types.InternalServiceError{Message: "unable to get activity scheduled event"} s.testMaintainCorruptWorkflow(err, nil, true) } func (s *adminHandlerSuite) TestMaintainCorruptWorkflow_UnableToGetScheduledEventHistory() { s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(s.domainName, nil).AnyTimes() err := &types.InternalServiceError{Message: "unable to get activity scheduled event"} s.testMaintainCorruptWorkflow(nil, err, true) } func (s *adminHandlerSuite) TestMaintainCorruptWorkflow_CorruptedHistory() { s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(s.domainName, nil).AnyTimes() err := &types.InternalDataInconsistencyError{ Message: "corrupted history event batch, eventID is not continuous", } s.testMaintainCorruptWorkflow(err, nil, true) } func (s *adminHandlerSuite) testMaintainCorruptWorkflow( describeWorkflowError error, getHistoryError error, expectDeletion bool, ) { handler := s.handler handler.params = &resource.Params{} ctx := context.Background() request := &types.AdminMaintainWorkflowRequest{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "someWorkflowID", RunID: uuid.New(), }, SkipErrors: true, } // need to reeturn error here to start deleting describeResp := &types.DescribeWorkflowExecutionResponse{} s.frontendClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()). Return(describeResp, describeWorkflowError) // need to reeturn error here to start deleting historyResponse := &types.GetWorkflowExecutionHistoryResponse{} s.frontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any()). Return(historyResponse, getHistoryError).AnyTimes() if expectDeletion { hostInfo := membership.NewHostInfo("taskListA:thriftPort") s.mockResolver.EXPECT().Lookup(gomock.Any(), gomock.Any()).Return(hostInfo, nil) s.mockDomainCache.EXPECT().GetDomainID(s.domainName).Return(s.domainID, nil) testMutableState := &types.DescribeMutableStateResponse{ MutableStateInDatabase: "{\"ExecutionInfo\":{\"BranchToken\":\"WQsACgAAACQ2MzI5YzEzMi1mMGI0LTQwZmUtYWYxMS1hODVmMDA3MzAzODQLABQAAAAkOWM5OWI1MjItMGEyZi00NTdmLWEyNDgtMWU0OTA0ZDg4YzVhDwAeDAAAAAAA\"}}", } s.mockHistoryClient.EXPECT().DescribeMutableState(gomock.Any(), gomock.Any()).Return(testMutableState, nil) s.mockHistoryV2Mgr.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(nil).Once() s.mockResource.ExecutionMgr.On("DeleteWorkflowExecution", mock.Anything, mock.Anything).Return(nil).Once() s.mockResource.ExecutionMgr.On("DeleteCurrentWorkflowExecution", mock.Anything, mock.Anything).Return(nil).Once() s.mockResource.VisibilityMgr.On("DeleteWorkflowExecution", mock.Anything, mock.Anything).Return(nil).Once() } _, err := handler.MaintainCorruptWorkflow(ctx, request) s.Nil(err) } func (s *adminHandlerSuite) Test_ConvertIndexedValueTypeToESDataType() { tests := []struct { input types.IndexedValueType expected string }{ { input: types.IndexedValueTypeString, expected: "text", }, { input: types.IndexedValueTypeKeyword, expected: "keyword", }, { input: types.IndexedValueTypeInt, expected: "long", }, { input: types.IndexedValueTypeDouble, expected: "double", }, { input: types.IndexedValueTypeBool, expected: "boolean", }, { input: types.IndexedValueTypeDatetime, expected: "date", }, { input: types.IndexedValueType(-1), expected: "", }, } for _, test := range tests { s.Equal(test.expected, convertIndexedValueTypeToESDataType(test.input)) } } func (s *adminHandlerSuite) Test_GetWorkflowExecutionRawHistoryV2_FailedOnInvalidWorkflowID() { ctx := context.Background() _, err := s.handler.GetWorkflowExecutionRawHistoryV2(ctx, &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "", RunID: uuid.New(), }, StartEventID: common.Int64Ptr(1), StartEventVersion: common.Int64Ptr(100), EndEventID: common.Int64Ptr(10), EndEventVersion: common.Int64Ptr(100), MaximumPageSize: 1, NextPageToken: nil, }) s.Error(err) } func (s *adminHandlerSuite) Test_GetWorkflowExecutionRawHistoryV2_FailedOnInvalidRunID() { ctx := context.Background() _, err := s.handler.GetWorkflowExecutionRawHistoryV2(ctx, &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "workflowID", RunID: "runID", }, StartEventID: common.Int64Ptr(1), StartEventVersion: common.Int64Ptr(100), EndEventID: common.Int64Ptr(10), EndEventVersion: common.Int64Ptr(100), MaximumPageSize: 1, NextPageToken: nil, }) s.Error(err) } func (s *adminHandlerSuite) Test_GetWorkflowExecutionRawHistoryV2_FailedOnInvalidSize() { ctx := context.Background() _, err := s.handler.GetWorkflowExecutionRawHistoryV2(ctx, &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "workflowID", RunID: uuid.New(), }, StartEventID: common.Int64Ptr(1), StartEventVersion: common.Int64Ptr(100), EndEventID: common.Int64Ptr(10), EndEventVersion: common.Int64Ptr(100), MaximumPageSize: -1, NextPageToken: nil, }) s.Error(err) } func (s *adminHandlerSuite) Test_GetWorkflowExecutionRawHistoryV2_FailedOnDomainCache() { ctx := context.Background() s.mockDomainCache.EXPECT().GetDomainID(s.domainName).Return("", fmt.Errorf("test")).Times(1) _, err := s.handler.GetWorkflowExecutionRawHistoryV2(ctx, &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "workflowID", RunID: uuid.New(), }, StartEventID: common.Int64Ptr(1), StartEventVersion: common.Int64Ptr(100), EndEventID: common.Int64Ptr(10), EndEventVersion: common.Int64Ptr(100), MaximumPageSize: 1, NextPageToken: nil, }) s.Error(err) } func (s *adminHandlerSuite) Test_GetWorkflowExecutionRawHistoryV2() { ctx := context.Background() s.mockDomainCache.EXPECT().GetDomainID(s.domainName).Return(s.domainID, nil).AnyTimes() branchToken := []byte{1} versionHistory := persistence.NewVersionHistory(branchToken, []*persistence.VersionHistoryItem{ persistence.NewVersionHistoryItem(int64(10), int64(100)), }) rawVersionHistories := persistence.NewVersionHistories(versionHistory) versionHistories := rawVersionHistories.ToInternalType() mState := &types.GetMutableStateResponse{ NextEventID: 11, CurrentBranchToken: branchToken, VersionHistories: versionHistories, } s.mockHistoryClient.EXPECT().GetMutableState(gomock.Any(), gomock.Any()).Return(mState, nil).AnyTimes() s.mockHistoryV2Mgr.On("ReadRawHistoryBranch", mock.Anything, mock.Anything).Return(&persistence.ReadRawHistoryBranchResponse{ HistoryEventBlobs: []*persistence.DataBlob{}, NextPageToken: []byte{}, Size: 0, }, nil) _, err := s.handler.GetWorkflowExecutionRawHistoryV2(ctx, &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "workflowID", RunID: uuid.New(), }, StartEventID: common.Int64Ptr(1), StartEventVersion: common.Int64Ptr(100), EndEventID: common.Int64Ptr(10), EndEventVersion: common.Int64Ptr(100), MaximumPageSize: 10, NextPageToken: nil, }) s.NoError(err) } func (s *adminHandlerSuite) Test_GetWorkflowExecutionRawHistoryV2_SameStartIDAndEndID() { ctx := context.Background() s.mockDomainCache.EXPECT().GetDomainID(s.domainName).Return(s.domainID, nil).AnyTimes() branchToken := []byte{1} versionHistory := persistence.NewVersionHistory(branchToken, []*persistence.VersionHistoryItem{ persistence.NewVersionHistoryItem(int64(10), int64(100)), }) rawVersionHistories := persistence.NewVersionHistories(versionHistory) versionHistories := rawVersionHistories.ToInternalType() mState := &types.GetMutableStateResponse{ NextEventID: 11, CurrentBranchToken: branchToken, VersionHistories: versionHistories, } s.mockHistoryClient.EXPECT().GetMutableState(gomock.Any(), gomock.Any()).Return(mState, nil).AnyTimes() resp, err := s.handler.GetWorkflowExecutionRawHistoryV2(ctx, &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "workflowID", RunID: uuid.New(), }, StartEventID: common.Int64Ptr(10), StartEventVersion: common.Int64Ptr(100), MaximumPageSize: 1, NextPageToken: nil, }) s.Nil(resp.NextPageToken) s.NoError(err) } func (s *adminHandlerSuite) Test_SetRequestDefaultValueAndGetTargetVersionHistory_DefinedStartAndEnd() { inputStartEventID := int64(1) inputStartVersion := int64(10) inputEndEventID := int64(100) inputEndVersion := int64(11) firstItem := persistence.NewVersionHistoryItem(inputStartEventID, inputStartVersion) endItem := persistence.NewVersionHistoryItem(inputEndEventID, inputEndVersion) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{firstItem, endItem}) versionHistories := persistence.NewVersionHistories(versionHistory) request := &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "workflowID", RunID: uuid.New(), }, StartEventID: common.Int64Ptr(inputStartEventID), StartEventVersion: common.Int64Ptr(inputStartVersion), EndEventID: common.Int64Ptr(inputEndEventID), EndEventVersion: common.Int64Ptr(inputEndVersion), MaximumPageSize: 10, NextPageToken: nil, } targetVersionHistory, err := s.handler.setRequestDefaultValueAndGetTargetVersionHistory( request, versionHistories, ) s.Equal(request.GetStartEventID(), inputStartEventID) s.Equal(request.GetEndEventID(), inputEndEventID) s.Equal(targetVersionHistory, versionHistory) s.NoError(err) } func (s *adminHandlerSuite) Test_SetRequestDefaultValueAndGetTargetVersionHistory_DefinedEndEvent() { inputStartEventID := int64(1) inputEndEventID := int64(100) inputStartVersion := int64(10) inputEndVersion := int64(11) firstItem := persistence.NewVersionHistoryItem(inputStartEventID, inputStartVersion) targetItem := persistence.NewVersionHistoryItem(inputEndEventID, inputEndVersion) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{firstItem, targetItem}) versionHistories := persistence.NewVersionHistories(versionHistory) request := &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "workflowID", RunID: uuid.New(), }, StartEventID: nil, StartEventVersion: nil, EndEventID: common.Int64Ptr(inputEndEventID), EndEventVersion: common.Int64Ptr(inputEndVersion), MaximumPageSize: 10, NextPageToken: nil, } targetVersionHistory, err := s.handler.setRequestDefaultValueAndGetTargetVersionHistory( request, versionHistories, ) s.Equal(request.GetStartEventID(), inputStartEventID-1) s.Equal(request.GetEndEventID(), inputEndEventID) s.Equal(targetVersionHistory, versionHistory) s.NoError(err) } func (s *adminHandlerSuite) Test_SetRequestDefaultValueAndGetTargetVersionHistory_DefinedStartEvent() { inputStartEventID := int64(1) inputEndEventID := int64(100) inputStartVersion := int64(10) inputEndVersion := int64(11) firstItem := persistence.NewVersionHistoryItem(inputStartEventID, inputStartVersion) targetItem := persistence.NewVersionHistoryItem(inputEndEventID, inputEndVersion) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{firstItem, targetItem}) versionHistories := persistence.NewVersionHistories(versionHistory) request := &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "workflowID", RunID: uuid.New(), }, StartEventID: common.Int64Ptr(inputStartEventID), StartEventVersion: common.Int64Ptr(inputStartVersion), EndEventID: nil, EndEventVersion: nil, MaximumPageSize: 10, NextPageToken: nil, } targetVersionHistory, err := s.handler.setRequestDefaultValueAndGetTargetVersionHistory( request, versionHistories, ) s.Equal(request.GetStartEventID(), inputStartEventID) s.Equal(request.GetEndEventID(), inputEndEventID+1) s.Equal(targetVersionHistory, versionHistory) s.NoError(err) } func (s *adminHandlerSuite) Test_SetRequestDefaultValueAndGetTargetVersionHistory_NonCurrentBranch() { inputStartEventID := int64(1) inputEndEventID := int64(100) inputStartVersion := int64(10) inputEndVersion := int64(101) item1 := persistence.NewVersionHistoryItem(inputStartEventID, inputStartVersion) item2 := persistence.NewVersionHistoryItem(inputEndEventID, inputEndVersion) versionHistory1 := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{item1, item2}) item3 := persistence.NewVersionHistoryItem(int64(10), int64(20)) item4 := persistence.NewVersionHistoryItem(int64(20), int64(51)) versionHistory2 := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{item1, item3, item4}) versionHistories := persistence.NewVersionHistories(versionHistory1) _, _, err := versionHistories.AddVersionHistory(versionHistory2) s.NoError(err) request := &types.GetWorkflowExecutionRawHistoryV2Request{ Domain: s.domainName, Execution: &types.WorkflowExecution{ WorkflowID: "workflowID", RunID: uuid.New(), }, StartEventID: common.Int64Ptr(9), StartEventVersion: common.Int64Ptr(20), EndEventID: common.Int64Ptr(inputEndEventID), EndEventVersion: common.Int64Ptr(inputEndVersion), MaximumPageSize: 10, NextPageToken: nil, } targetVersionHistory, err := s.handler.setRequestDefaultValueAndGetTargetVersionHistory( request, versionHistories, ) s.Equal(request.GetStartEventID(), inputStartEventID) s.Equal(request.GetEndEventID(), inputEndEventID) s.Equal(targetVersionHistory, versionHistory1) s.NoError(err) } func (s *adminHandlerSuite) Test_AddSearchAttribute_Validate() { handler := s.handler handler.params = &resource.Params{} ctx := context.Background() type test struct { Name string Request *types.AddSearchAttributeRequest Expected error } // request validation tests testCases1 := []test{ { Name: "nil request", Request: nil, Expected: &types.BadRequestError{Message: "Request is nil."}, }, { Name: "empty request", Request: &types.AddSearchAttributeRequest{}, Expected: &types.BadRequestError{Message: "SearchAttributes are not provided"}, }, } for _, testCase := range testCases1 { s.Equal(testCase.Expected, handler.AddSearchAttribute(ctx, testCase.Request)) } dynamicConfig := dynamicconfig.NewMockClient(s.controller) handler.params.DynamicConfig = dynamicConfig // add advanced visibility store related config handler.params.ESConfig = &config.ElasticSearchConfig{} esClient := &esmock.GenericClient{} defer func() { esClient.AssertExpectations(s.T()) }() handler.params.ESClient = esClient handler.esClient = esClient mockValidAttr := map[string]interface{}{ "testkey": types.IndexedValueTypeKeyword, } dynamicConfig.EXPECT().GetMapValue(dynamicproperties.ValidSearchAttributes, nil). Return(mockValidAttr, nil).AnyTimes() testCases2 := []test{ { Name: "reserved key", Request: &types.AddSearchAttributeRequest{ SearchAttribute: map[string]types.IndexedValueType{ "WorkflowID": 1, }, }, Expected: &types.BadRequestError{Message: "Key [WorkflowID] is reserved by system"}, }, { Name: "key already whitelisted", Request: &types.AddSearchAttributeRequest{ SearchAttribute: map[string]types.IndexedValueType{ "testkey": 1, }, }, Expected: &types.BadRequestError{Message: "Key [testkey] is already whitelisted as a different type"}, }, } for _, testCase := range testCases2 { s.Equal(testCase.Expected, handler.AddSearchAttribute(ctx, testCase.Request)) } dcUpdateTest := test{ Name: "dynamic config update failed", Request: &types.AddSearchAttributeRequest{ SearchAttribute: map[string]types.IndexedValueType{ "testkey2": -1, }, }, Expected: &types.BadRequestError{Message: "Unknown value type, IndexedValueType(-1)"}, } dynamicConfig.EXPECT().UpdateValue(dynamicproperties.ValidSearchAttributes, map[string]interface{}{ "testkey": types.IndexedValueTypeKeyword, "testkey2": -1, }).Return(errors.New("error")) err := handler.AddSearchAttribute(ctx, dcUpdateTest.Request) s.Equal(dcUpdateTest.Expected, err) // ES operations tests dynamicConfig.EXPECT().UpdateValue(gomock.Any(), gomock.Any()).Return(nil).Times(2) convertFailedTest := test{ Name: "unknown value type", Request: &types.AddSearchAttributeRequest{ SearchAttribute: map[string]types.IndexedValueType{ "testkey3": -1, }, }, Expected: &types.BadRequestError{Message: "Unknown value type, IndexedValueType(-1)"}, } s.Equal(convertFailedTest.Expected, handler.AddSearchAttribute(ctx, convertFailedTest.Request)) esClient.On("PutMapping", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(errors.New("error")) esClient.On("IsNotFoundError", mock.Anything).Return(false) esErrorTest := test{ Name: "es error", Request: &types.AddSearchAttributeRequest{ SearchAttribute: map[string]types.IndexedValueType{ "testkey4": 1, }, }, Expected: &types.InternalServiceError{Message: "Failed to update ES mapping, err: error"}, } s.Equal(esErrorTest.Expected, handler.AddSearchAttribute(ctx, esErrorTest.Request)) } func (s *adminHandlerSuite) Test_AddSearchAttribute_Permission() { ctx := context.Background() handler := s.handler handler.config = &frontendcfg.Config{ EnableAdminProtection: dynamicproperties.GetBoolPropertyFn(true), AdminOperationToken: dynamicproperties.GetStringPropertyFn(dynamicproperties.AdminOperationToken.DefaultString()), } type test struct { Name string Request *types.AddSearchAttributeRequest Expected error } testCases := []test{ { Name: "unknown token", Request: &types.AddSearchAttributeRequest{ SecurityToken: "unknown", }, Expected: validate.ErrNoPermission, }, { Name: "correct token", Request: &types.AddSearchAttributeRequest{ SecurityToken: dynamicproperties.AdminOperationToken.DefaultString(), }, Expected: &types.BadRequestError{Message: "SearchAttributes are not provided"}, }, } for _, testCase := range testCases { s.Equal(testCase.Expected, handler.AddSearchAttribute(ctx, testCase.Request)) } } func (s *adminHandlerSuite) Test_ConfigStore_NilRequest() { ctx := context.Background() handler := s.handler _, err := handler.GetDynamicConfig(ctx, nil) s.Error(err) err = handler.UpdateDynamicConfig(ctx, nil) s.Error(err) err = handler.RestoreDynamicConfig(ctx, nil) s.Error(err) } func (s *adminHandlerSuite) Test_DescribeShardDistribution() { s.mockResource.MembershipResolver.EXPECT().Lookup(service.History, string(rune(0))). Return(membership.NewHostInfo("127.0.0.1:1234"), nil) res, err := s.handler.DescribeShardDistribution( context.Background(), &types.DescribeShardDistributionRequest{PageSize: 10}, ) s.Require().NoError(err) s.Equal( &types.DescribeShardDistributionResponse{ NumberOfShards: 1, Shards: map[int32]string{0: "127.0.0.1:1234"}, }, res, ) } func (s *adminHandlerSuite) Test_ConfigStore_InvalidKey() { ctx := context.Background() handler := s.handler _, err := handler.GetDynamicConfig(ctx, &types.GetDynamicConfigRequest{ ConfigName: "invalid key", Filters: nil, }) s.Error(err) err = handler.UpdateDynamicConfig(ctx, &types.UpdateDynamicConfigRequest{ ConfigName: "invalid key", ConfigValues: nil, }) s.Error(err) err = handler.RestoreDynamicConfig(ctx, &types.RestoreDynamicConfigRequest{ ConfigName: "invalid key", Filters: nil, }) s.Error(err) } func (s *adminHandlerSuite) Test_GetDynamicConfig_NoFilter() { ctx := context.Background() handler := s.handler dynamicConfig := dynamicconfig.NewMockClient(s.controller) handler.params.DynamicConfig = dynamicConfig dynamicConfig.EXPECT(). GetValue(dynamicproperties.TestGetBoolPropertyKey). Return(true, nil).AnyTimes() resp, err := handler.GetDynamicConfig(ctx, &types.GetDynamicConfigRequest{ ConfigName: dynamicproperties.TestGetBoolPropertyKey.String(), Filters: nil, }) s.NoError(err) encTrue, err := json.Marshal(true) s.NoError(err) s.Equal(resp.Value.Data, encTrue) } func (s *adminHandlerSuite) Test_GetDynamicConfig_FilterMatch() { ctx := context.Background() handler := s.handler dynamicConfig := dynamicconfig.NewMockClient(s.controller) handler.params.DynamicConfig = dynamicConfig dynamicConfig.EXPECT(). GetValueWithFilters(dynamicproperties.TestGetBoolPropertyKey, map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: "samples_domain", }). Return(true, nil).AnyTimes() encDomainName, err := json.Marshal("samples_domain") s.NoError(err) resp, err := handler.GetDynamicConfig(ctx, &types.GetDynamicConfigRequest{ ConfigName: dynamicproperties.TestGetBoolPropertyKey.String(), Filters: []*types.DynamicConfigFilter{ { Name: dynamicproperties.DomainName.String(), Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: encDomainName, }, }, }, }) s.NoError(err) encTrue, err := json.Marshal(true) s.NoError(err) s.Equal(resp.Value.Data, encTrue) } func Test_GetGlobalIsolationGroups(t *testing.T) { validResponse := types.GetGlobalIsolationGroupsResponse{ IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": types.IsolationGroupPartition{ Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": types.IsolationGroupPartition{ Name: "zone-2", State: types.IsolationGroupStateHealthy, }, "zone-3": types.IsolationGroupPartition{ Name: "zone-3", State: types.IsolationGroupStateDrained, }, }, } tests := map[string]struct { ighandlerAffordance func(mock *isolationgroupapi.MockHandler) expectOut *types.GetGlobalIsolationGroupsResponse expectedErr error }{ "happy-path - no errors and payload is decoded and returned": { ighandlerAffordance: func(mock *isolationgroupapi.MockHandler) { mock.EXPECT().GetGlobalState(gomock.Any()).Return(&validResponse, nil) }, expectOut: &validResponse, }, "an error returned": { ighandlerAffordance: func(mock *isolationgroupapi.MockHandler) { mock.EXPECT().GetGlobalState(gomock.Any()).Return(nil, assert.AnError) }, expectedErr: &types.InternalServiceError{Message: assert.AnError.Error()}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) igMock := isolationgroupapi.NewMockHandler(goMock) td.ighandlerAffordance(igMock) handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), }, isolationGroups: igMock, } res, err := handler.GetGlobalIsolationGroups(context.Background(), &types.GetGlobalIsolationGroupsRequest{}) assert.Equal(t, td.expectOut, res) assert.Equal(t, td.expectedErr, err) }) } } func Test_UpdateGlobalIsolationGroups(t *testing.T) { validConfig := types.UpdateGlobalIsolationGroupsRequest{ IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": { Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": { Name: "zone-2", State: types.IsolationGroupStateHealthy, }, "zone-3": { Name: "zone-3", State: types.IsolationGroupStateDrained, }, }, } tests := map[string]struct { ighandlerAffordance func(mock *isolationgroupapi.MockHandler) input *types.UpdateGlobalIsolationGroupsRequest expectOut *types.UpdateGlobalIsolationGroupsResponse expectedErr error }{ "happy-path - update to the database": { input: &validConfig, ighandlerAffordance: func(mock *isolationgroupapi.MockHandler) { mock.EXPECT().UpdateGlobalState(gomock.Any(), validConfig).Return(nil) }, expectOut: &types.UpdateGlobalIsolationGroupsResponse{}, }, "happy-path - an error is returned": { input: &validConfig, ighandlerAffordance: func(mock *isolationgroupapi.MockHandler) { mock.EXPECT().UpdateGlobalState(gomock.Any(), validConfig).Return(assert.AnError) }, expectedErr: &types.InternalServiceError{Message: assert.AnError.Error()}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) igMock := isolationgroupapi.NewMockHandler(goMock) td.ighandlerAffordance(igMock) handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), }, isolationGroups: igMock, } res, err := handler.UpdateGlobalIsolationGroups(context.Background(), td.input) assert.Equal(t, td.expectOut, res) assert.Equal(t, td.expectedErr, err) }) } } func Test_GetDomainIsolationGroups(t *testing.T) { validResponse := types.GetDomainIsolationGroupsResponse{ IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": types.IsolationGroupPartition{ Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": types.IsolationGroupPartition{ Name: "zone-2", State: types.IsolationGroupStateHealthy, }, "zone-3": types.IsolationGroupPartition{ Name: "zone-3", State: types.IsolationGroupStateDrained, }, }, } tests := map[string]struct { ighandlerAffordance func(mock *isolationgroupapi.MockHandler) expectOut *types.GetDomainIsolationGroupsResponse expectedErr error }{ "happy-path - no errors and payload is decoded and returned": { ighandlerAffordance: func(mock *isolationgroupapi.MockHandler) { mock.EXPECT().GetDomainState(gomock.Any(), types.GetDomainIsolationGroupsRequest{ Domain: "domain", }).Return(&validResponse, nil) }, expectOut: &validResponse, }, "an error returned": { ighandlerAffordance: func(mock *isolationgroupapi.MockHandler) { mock.EXPECT().GetDomainState(gomock.Any(), types.GetDomainIsolationGroupsRequest{ Domain: "domain", }).Return(nil, assert.AnError) }, expectedErr: &types.InternalServiceError{Message: assert.AnError.Error()}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) igMock := isolationgroupapi.NewMockHandler(goMock) td.ighandlerAffordance(igMock) handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), }, isolationGroups: igMock, } res, err := handler.GetDomainIsolationGroups(context.Background(), &types.GetDomainIsolationGroupsRequest{ Domain: "domain", }) assert.Equal(t, td.expectOut, res) assert.Equal(t, td.expectedErr, err) }) } } func Test_UpdateDomainIsolationGroups(t *testing.T) { validConfig := types.UpdateDomainIsolationGroupsRequest{ Domain: "domain", IsolationGroups: types.IsolationGroupConfiguration{ "zone-1": { Name: "zone-1", State: types.IsolationGroupStateDrained, }, "zone-2": { Name: "zone-2", State: types.IsolationGroupStateHealthy, }, "zone-3": { Name: "zone-3", State: types.IsolationGroupStateDrained, }, }, } tests := map[string]struct { ighandlerAffordance func(mock *isolationgroupapi.MockHandler) input *types.UpdateDomainIsolationGroupsRequest expectOut *types.UpdateDomainIsolationGroupsResponse expectedErr error }{ "happy-path - update to the database": { input: &validConfig, ighandlerAffordance: func(mock *isolationgroupapi.MockHandler) { mock.EXPECT().UpdateDomainState(gomock.Any(), validConfig).Return(nil) }, expectOut: &types.UpdateDomainIsolationGroupsResponse{}, }, "happy-path - an error is returned": { input: &validConfig, ighandlerAffordance: func(mock *isolationgroupapi.MockHandler) { mock.EXPECT().UpdateDomainState(gomock.Any(), validConfig).Return(assert.AnError) }, expectedErr: &types.InternalServiceError{Message: assert.AnError.Error()}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) igMock := isolationgroupapi.NewMockHandler(goMock) td.ighandlerAffordance(igMock) handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), }, isolationGroups: igMock, } res, err := handler.UpdateDomainIsolationGroups(context.Background(), td.input) assert.Equal(t, td.expectOut, res) assert.Equal(t, td.expectedErr, err) }) } } func Test_GetDomainAsyncWorkflowConfiguraton(t *testing.T) { tests := map[string]struct { queueCfgHandlerMockFn func(mock *queueconfigapi.MockHandler) input *types.GetDomainAsyncWorkflowConfiguratonRequest wantResp *types.GetDomainAsyncWorkflowConfiguratonResponse wantErr error }{ "success": { input: &types.GetDomainAsyncWorkflowConfiguratonRequest{Domain: "test-domain"}, queueCfgHandlerMockFn: func(mock *queueconfigapi.MockHandler) { mock.EXPECT().GetConfiguraton(gomock.Any(), gomock.Any()).Return(&types.GetDomainAsyncWorkflowConfiguratonResponse{}, nil).Times(1) }, wantResp: &types.GetDomainAsyncWorkflowConfiguratonResponse{}, }, "nil request": { input: nil, wantErr: validate.ErrRequestNotSet, }, "queue config handler failed": { input: &types.GetDomainAsyncWorkflowConfiguratonRequest{Domain: "test-domain"}, queueCfgHandlerMockFn: func(mock *queueconfigapi.MockHandler) { mock.EXPECT().GetConfiguraton(gomock.Any(), gomock.Any()).Return(nil, errors.New("failed")).Times(1) }, wantErr: &types.InternalServiceError{Message: "failed"}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) queueCfgHandlerMock := queueconfigapi.NewMockHandler(goMock) if td.queueCfgHandlerMockFn != nil { td.queueCfgHandlerMockFn(queueCfgHandlerMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), }, asyncWFQueueConfigs: queueCfgHandlerMock, } res, err := handler.GetDomainAsyncWorkflowConfiguraton(context.Background(), td.input) assert.Equal(t, td.wantResp, res) assert.Equal(t, td.wantErr, err) }) } } func Test_UpdateDomainAsyncWorkflowConfiguraton(t *testing.T) { tests := map[string]struct { queueCfgHandlerMockFn func(mock *queueconfigapi.MockHandler) input *types.UpdateDomainAsyncWorkflowConfiguratonRequest wantResp *types.UpdateDomainAsyncWorkflowConfiguratonResponse wantErr error }{ "success": { input: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{Domain: "test-domain"}, queueCfgHandlerMockFn: func(mock *queueconfigapi.MockHandler) { mock.EXPECT().UpdateConfiguration(gomock.Any(), gomock.Any()).Return(&types.UpdateDomainAsyncWorkflowConfiguratonResponse{}, nil).Times(1) }, wantResp: &types.UpdateDomainAsyncWorkflowConfiguratonResponse{}, }, "nil request": { input: nil, wantErr: validate.ErrRequestNotSet, }, "queue config handler failed": { input: &types.UpdateDomainAsyncWorkflowConfiguratonRequest{Domain: "test-domain"}, queueCfgHandlerMockFn: func(mock *queueconfigapi.MockHandler) { mock.EXPECT().UpdateConfiguration(gomock.Any(), gomock.Any()).Return(nil, errors.New("failed")).Times(1) }, wantErr: &types.InternalServiceError{Message: "failed"}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) queueCfgHandlerMock := queueconfigapi.NewMockHandler(goMock) if td.queueCfgHandlerMockFn != nil { td.queueCfgHandlerMockFn(queueCfgHandlerMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), }, asyncWFQueueConfigs: queueCfgHandlerMock, } res, err := handler.UpdateDomainAsyncWorkflowConfiguraton(context.Background(), td.input) assert.Equal(t, td.wantResp, res) assert.Equal(t, td.wantErr, err) }) } } func Test_RemoveTask(t *testing.T) { tests := map[string]struct { input *types.RemoveTaskRequest hcHandlerFunc func(mock *history.MockClient) wantErr bool }{ "nil request": { input: nil, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "normal request": { input: &types.RemoveTaskRequest{ ShardID: 1, Type: common.Int32Ptr(2), }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().RemoveTask(gomock.Any(), &types.RemoveTaskRequest{ ShardID: 1, Type: common.Int32Ptr(2), }).Return(nil) }, wantErr: false, }, "request failed": { input: &types.RemoveTaskRequest{ ShardID: 1, Type: common.Int32Ptr(2), }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().RemoveTask(gomock.Any(), &types.RemoveTaskRequest{ ShardID: 1, Type: common.Int32Ptr(2), }).Return(assert.AnError) }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } err := handler.RemoveTask(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_CloseShard(t *testing.T) { tests := map[string]struct { input *types.CloseShardRequest hcHandlerFunc func(mock *history.MockClient) wantErr bool }{ "nil request": { input: nil, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "normal request": { input: &types.CloseShardRequest{ ShardID: 1, }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().CloseShard(gomock.Any(), &types.CloseShardRequest{ ShardID: 1, }).Return(nil) }, wantErr: false, }, "request failed": { input: &types.CloseShardRequest{ ShardID: 1, }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().CloseShard(gomock.Any(), &types.CloseShardRequest{ ShardID: 1, }).Return(assert.AnError) }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } err := handler.CloseShard(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_ResetQueue(t *testing.T) { tests := map[string]struct { input *types.ResetQueueRequest hcHandlerFunc func(mock *history.MockClient) wantErr bool }{ "nil request": { input: nil, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "missing cluster name request": { input: &types.ResetQueueRequest{ ShardID: 1, Type: common.Int32Ptr(1), }, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "normal request": { input: &types.ResetQueueRequest{ ShardID: 1, ClusterName: "test-cluster", Type: common.Int32Ptr(1), }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().ResetQueue(gomock.Any(), &types.ResetQueueRequest{ ShardID: 1, ClusterName: "test-cluster", Type: common.Int32Ptr(1), }).Return(nil).Times(1) }, wantErr: false, }, "normal request return error": { input: &types.ResetQueueRequest{ ShardID: 1, ClusterName: "test-cluster", Type: common.Int32Ptr(1), }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().ResetQueue(gomock.Any(), &types.ResetQueueRequest{ ShardID: 1, ClusterName: "test-cluster", Type: common.Int32Ptr(1), }).Return(assert.AnError).Times(1) }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } err := handler.ResetQueue(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_DescribeQueue(t *testing.T) { tests := map[string]struct { input *types.DescribeQueueRequest hcHandlerFunc func(mock *history.MockClient) wantErr bool }{ "nil request": { input: nil, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "missing cluster name request": { input: &types.DescribeQueueRequest{ ShardID: 1, Type: common.Int32Ptr(1), }, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "normal request": { input: &types.DescribeQueueRequest{ ShardID: 1, ClusterName: "test-cluster", Type: common.Int32Ptr(1), }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().DescribeQueue(gomock.Any(), &types.DescribeQueueRequest{ ShardID: 1, ClusterName: "test-cluster", Type: common.Int32Ptr(1), }).Return(nil, nil) }, wantErr: false, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } _, err := handler.DescribeQueue(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_DescribeHistoryHost(t *testing.T) { tests := map[string]struct { input *types.DescribeHistoryHostRequest hcHandlerFunc func(mock *history.MockClient) wantErr bool }{ "nil request": { input: nil, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "missing host address request": { input: &types.DescribeHistoryHostRequest{}, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "missing execution": { input: &types.DescribeHistoryHostRequest{ ExecutionForHost: &types.WorkflowExecution{ WorkflowID: "", RunID: uuid.New(), }, }, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "normal request": { input: &types.DescribeHistoryHostRequest{ ExecutionForHost: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: uuid.New(), }, }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().DescribeHistoryHost(gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: false, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } _, err := handler.DescribeHistoryHost(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_DescribeCluster(t *testing.T) { goMock := gomock.NewController(t) mockResource := resource.NewTest(t, goMock, metrics.Frontend) mockResource.VisibilityMgr.On("GetName").Return("test").Once() mockResource.HistoryMgr.On("GetName").Return("test").Once() mockResource.MembershipResolver.EXPECT().WhoAmI().Return(membership.NewHostInfo("1.0.0.1"), nil).AnyTimes() mockResource.MembershipResolver.EXPECT().Members(gomock.Any()).Return([]membership.HostInfo{ membership.NewHostInfo("1.0.0.1"), }, nil).AnyTimes() handler := adminHandlerImpl{ params: &resource.Params{ ESConfig: &config.ElasticSearchConfig{ Indices: map[string]string{}, }, }, Resource: mockResource, } _, err := handler.DescribeCluster(context.Background()) assert.NoError(t, err) } func Test_DescribeCluster_HostError(t *testing.T) { goMock := gomock.NewController(t) mockResource := resource.NewTest(t, goMock, metrics.Frontend) mockResource.VisibilityMgr.On("GetName").Return("test").Once() mockResource.HistoryMgr.On("GetName").Return("test").Once() mockResource.MembershipResolver.EXPECT().WhoAmI().Return(membership.NewHostInfo("1.0.0.1"), assert.AnError).AnyTimes() handler := adminHandlerImpl{ params: &resource.Params{ ESConfig: &config.ElasticSearchConfig{ Indices: map[string]string{}, }, }, Resource: mockResource, } _, err := handler.DescribeCluster(context.Background()) assert.Error(t, err) } func Test_DescribeCluster_MemberError(t *testing.T) { goMock := gomock.NewController(t) mockResource := resource.NewTest(t, goMock, metrics.Frontend) mockResource.VisibilityMgr.On("GetName").Return("test").Once() mockResource.HistoryMgr.On("GetName").Return("test").Once() mockResource.MembershipResolver.EXPECT().WhoAmI().Return(membership.NewHostInfo("1.0.0.1"), nil).AnyTimes() mockResource.MembershipResolver.EXPECT().Members(gomock.Any()).Return([]membership.HostInfo{ membership.NewHostInfo("1.0.0.1"), }, assert.AnError).AnyTimes() handler := adminHandlerImpl{ params: &resource.Params{ ESConfig: &config.ElasticSearchConfig{ Indices: map[string]string{}, }, }, Resource: mockResource, } _, err := handler.DescribeCluster(context.Background()) assert.Error(t, err) } func TestGetReplicationMessages(t *testing.T) { tests := map[string]struct { input *types.GetReplicationMessagesRequest hcHandlerFunc func(mock *history.MockClient) wantErr bool }{ "nil request": { input: nil, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "missing cluster name": { input: &types.GetReplicationMessagesRequest{}, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "normal request": { input: &types.GetReplicationMessagesRequest{ ClusterName: "test-cluster", }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: false, }, "return error": { input: &types.GetReplicationMessagesRequest{ ClusterName: "test-cluster", }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().GetReplicationMessages(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } _, err := handler.GetReplicationMessages(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_GetDomainReplicationMessages(t *testing.T) { tests := map[string]struct { input *types.GetDomainReplicationMessagesRequest hcHandlerFunc func(mock *history.MockClient) drqHandlerFunc func(mock *domain.MockReplicationQueue) wantErr bool }{ "nil request": { input: nil, hcHandlerFunc: func(mock *history.MockClient) {}, drqHandlerFunc: func(mock *domain.MockReplicationQueue) {}, wantErr: true, }, "with default last message ID": { input: &types.GetDomainReplicationMessagesRequest{ LastRetrievedMessageID: common.Int64Ptr(-1), // default value ClusterName: "test-cluster", }, hcHandlerFunc: func(mock *history.MockClient) { }, drqHandlerFunc: func(mock *domain.MockReplicationQueue) { mock.EXPECT().GetAckLevels(context.Background()).Return(map[string]int64{ "test-cluster": 1, }, nil) mock.EXPECT().GetReplicationMessages(context.Background(), int64(1), getDomainReplicationMessageBatchSize).Return(nil, int64(2), nil) mock.EXPECT().UpdateAckLevel(context.Background(), int64(-1), "test-cluster").Return(nil) }, wantErr: false, }, "with random last message ID but update ack level error": { input: &types.GetDomainReplicationMessagesRequest{ LastRetrievedMessageID: common.Int64Ptr(1), LastProcessedMessageID: common.Int64Ptr(1), }, hcHandlerFunc: func(mock *history.MockClient) { }, drqHandlerFunc: func(mock *domain.MockReplicationQueue) { mock.EXPECT().GetReplicationMessages(context.Background(), int64(1), getDomainReplicationMessageBatchSize).Return(nil, int64(2), nil) mock.EXPECT().UpdateAckLevel(context.Background(), int64(1), "").Return(assert.AnError) }, wantErr: false, }, "get replication messages error": { input: &types.GetDomainReplicationMessagesRequest{ LastRetrievedMessageID: common.Int64Ptr(1), LastProcessedMessageID: common.Int64Ptr(1), }, hcHandlerFunc: func(mock *history.MockClient) { }, drqHandlerFunc: func(mock *domain.MockReplicationQueue) { mock.EXPECT().GetReplicationMessages(context.Background(), int64(1), getDomainReplicationMessageBatchSize).Return(nil, int64(2), assert.AnError) }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) drqMock := domain.NewMockReplicationQueue(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } if td.drqHandlerFunc != nil { td.drqHandlerFunc(drqMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), DomainReplicationQueue: drqMock, }, } resp, err := handler.GetDomainReplicationMessages(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, int64(2), resp.Messages.LastRetrievedMessageID) } }) } } func Test_GetDLQReplicationMessages(t *testing.T) { tests := map[string]struct { input *types.GetDLQReplicationMessagesRequest hcHandlerFunc func(mock *history.MockClient) wantErr bool }{ "nil request": { input: nil, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "missing task info": { input: &types.GetDLQReplicationMessagesRequest{}, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "normal request": { input: &types.GetDLQReplicationMessagesRequest{ TaskInfos: []*types.ReplicationTaskInfo{ { DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: uuid.New(), TaskID: int64(1), TaskType: 1, Version: 0, FirstEventID: 1, }, }, }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().GetDLQReplicationMessages(gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: false, }, "get dql message error": { input: &types.GetDLQReplicationMessagesRequest{ TaskInfos: []*types.ReplicationTaskInfo{ { DomainID: "test-domain-id", WorkflowID: "test-workflow-id", }, }, }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().GetDLQReplicationMessages(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } _, err := handler.GetDLQReplicationMessages(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_ReapplyEvents(t *testing.T) { tests := map[string]struct { input *types.ReapplyEventsRequest hcHandlerFunc func(mock *history.MockClient) dcHandlerFunc func(mock *cache.MockDomainCache) wantErr bool }{ "nil request": { input: nil, wantErr: true, }, "empty domain name": { input: &types.ReapplyEventsRequest{}, wantErr: true, }, "empty workflow execution": { input: &types.ReapplyEventsRequest{ DomainName: "test-domain", }, wantErr: true, }, "empty workflow ID": { input: &types.ReapplyEventsRequest{ DomainName: "test-domain", WorkflowExecution: &types.WorkflowExecution{}, }, wantErr: true, }, "empty events": { input: &types.ReapplyEventsRequest{ DomainName: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", }, }, wantErr: true, }, "get domain error": { input: &types.ReapplyEventsRequest{ DomainName: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", }, Events: &types.DataBlob{}, }, dcHandlerFunc: func(mock *cache.MockDomainCache) { mock.EXPECT().GetDomain("test-domain").Return(nil, assert.AnError) }, wantErr: true, }, "normal request": { input: &types.ReapplyEventsRequest{ DomainName: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", }, Events: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("test-event-data"), }, }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().ReapplyEvents(context.Background(), &types.HistoryReapplyEventsRequest{ DomainUUID: "test-domain-id", Request: &types.ReapplyEventsRequest{ DomainName: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", }, Events: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("test-event-data"), }, }, }).Return(nil) }, dcHandlerFunc: func(mock *cache.MockDomainCache) { mock.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-domain-id"}, &persistence.DomainConfig{}, &persistence.DomainReplicationConfig{}, 0, ), nil) }, wantErr: false, }, "reapply events error": { input: &types.ReapplyEventsRequest{ DomainName: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", }, Events: &types.DataBlob{}, }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().ReapplyEvents(gomock.Any(), gomock.Any()).Return(assert.AnError) }, dcHandlerFunc: func(mock *cache.MockDomainCache) { mock.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-domain-id"}, &persistence.DomainConfig{}, &persistence.DomainReplicationConfig{}, 0, ), nil) }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) dcMock := cache.NewMockDomainCache(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } if td.dcHandlerFunc != nil { td.dcHandlerFunc(dcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, DomainCache: dcMock, }, } err := handler.ReapplyEvents(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_ReadDLQMessages(t *testing.T) { tests := map[string]struct { input *types.ReadDLQMessagesRequest hcHandlerFunc func(mock *history.MockClient) retryHandelrFunc func(mock *backoff.ThrottleRetry) wantErr bool }{ "nil request": { input: nil, wantErr: true, }, "missing type": { input: &types.ReadDLQMessagesRequest{}, wantErr: true, }, "type DLQTypeReplication": { input: &types.ReadDLQMessagesRequest{ Type: types.DLQTypeReplication.Ptr(), }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().ReadDLQMessages(gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: false, }, "type DLQTypeDomain": { input: &types.ReadDLQMessagesRequest{ Type: types.DLQTypeDomain.Ptr(), }, hcHandlerFunc: func(mock *history.MockClient) { }, retryHandelrFunc: func(mock *backoff.ThrottleRetry) { }, }, "default type": { input: &types.ReadDLQMessagesRequest{ Type: types.DLQType(22).Ptr(), }, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } if td.retryHandelrFunc != nil { policy := backoff.NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetMaximumInterval(5 * time.Millisecond) policy.SetMaximumAttempts(5) handler.throttleRetry = backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(func(_ error) bool { return true }), ) mockDlqHandler := domain.NewMockDLQMessageHandler(goMock) mockDlqHandler.EXPECT().Read(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil) handler.domainDLQHandler = mockDlqHandler } _, err := handler.ReadDLQMessages(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_PurgeDLQMessages(t *testing.T) { tests := map[string]struct { input *types.PurgeDLQMessagesRequest hcHandlerFunc func(mock *history.MockClient) retryHandelrFunc func(mock *backoff.ThrottleRetry) wantErr bool }{ "nil request": { input: nil, wantErr: true, }, "missing type": { input: &types.PurgeDLQMessagesRequest{}, wantErr: true, }, "type DLQTypeReplication": { input: &types.PurgeDLQMessagesRequest{ Type: types.DLQTypeReplication.Ptr(), }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().PurgeDLQMessages(gomock.Any(), gomock.Any()).Return(nil) }, wantErr: false, }, "type DLQTypeDomain": { input: &types.PurgeDLQMessagesRequest{ Type: types.DLQTypeDomain.Ptr(), }, hcHandlerFunc: func(mock *history.MockClient) { }, retryHandelrFunc: func(mock *backoff.ThrottleRetry) { }, }, "default type": { input: &types.PurgeDLQMessagesRequest{ Type: types.DLQType(22).Ptr(), }, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } if td.retryHandelrFunc != nil { policy := backoff.NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetMaximumInterval(5 * time.Millisecond) policy.SetMaximumAttempts(5) handler.throttleRetry = backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(func(_ error) bool { return true }), ) mockDlqHandler := domain.NewMockDLQMessageHandler(goMock) mockDlqHandler.EXPECT().Purge(gomock.Any(), gomock.Any()).Return(nil) handler.domainDLQHandler = mockDlqHandler } err := handler.PurgeDLQMessages(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_CountDQLMessages(t *testing.T) { tests := map[string]struct { input *types.CountDLQMessagesRequest hcHandlerFunc func(mock *history.MockClient) dlqHandlerFunc func(mock *domain.MockDLQMessageHandler) wantErr bool }{ "normal request": { input: &types.CountDLQMessagesRequest{}, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().CountDLQMessages(gomock.Any(), gomock.Any()).Return(&types.HistoryCountDLQMessagesResponse{ Entries: map[types.HistoryDLQCountKey]int64{ { ShardID: 1, SourceCluster: "test-cluster", }: 1, }, }, nil) }, dlqHandlerFunc: func(mock *domain.MockDLQMessageHandler) { mock.EXPECT().Count(gomock.Any(), gomock.Any()).Return(int64(1), nil) }, wantErr: false, }, "domain dlq handler error": { input: &types.CountDLQMessagesRequest{}, hcHandlerFunc: func(mock *history.MockClient) { }, dlqHandlerFunc: func(mock *domain.MockDLQMessageHandler) { mock.EXPECT().Count(gomock.Any(), gomock.Any()).Return(int64(1), assert.AnError) }, wantErr: true, }, "history client error": { input: &types.CountDLQMessagesRequest{}, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().CountDLQMessages(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) }, dlqHandlerFunc: func(mock *domain.MockDLQMessageHandler) { mock.EXPECT().Count(gomock.Any(), gomock.Any()).Return(int64(1), nil) }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) dlqMock := domain.NewMockDLQMessageHandler(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } if td.dlqHandlerFunc != nil { td.dlqHandlerFunc(dlqMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, domainDLQHandler: dlqMock, } _, err := handler.CountDLQMessages(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_MergeDLQMessages(t *testing.T) { tests := map[string]struct { input *types.MergeDLQMessagesRequest hcHandlerFunc func(mock *history.MockClient) retryHandelrFunc func(mock *backoff.ThrottleRetry) wantErr bool }{ "nil request": { input: nil, wantErr: true, }, "missing type": { input: &types.MergeDLQMessagesRequest{}, wantErr: true, }, "type DLQTypeReplication": { input: &types.MergeDLQMessagesRequest{ Type: types.DLQTypeReplication.Ptr(), }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().MergeDLQMessages(gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: false, }, "type DLQTypeDomain": { input: &types.MergeDLQMessagesRequest{ Type: types.DLQTypeDomain.Ptr(), }, hcHandlerFunc: func(mock *history.MockClient) { }, retryHandelrFunc: func(mock *backoff.ThrottleRetry) { }, }, "default type": { input: &types.MergeDLQMessagesRequest{ Type: types.DLQType(22).Ptr(), }, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } if td.retryHandelrFunc != nil { policy := backoff.NewExponentialRetryPolicy(1 * time.Millisecond) policy.SetMaximumInterval(5 * time.Millisecond) policy.SetMaximumAttempts(5) handler.throttleRetry = backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(func(_ error) bool { return true }), ) mockDlqHandler := domain.NewMockDLQMessageHandler(goMock) mockDlqHandler.EXPECT().Merge(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil) handler.domainDLQHandler = mockDlqHandler } _, err := handler.MergeDLQMessages(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_RefreshWorkflowTasks(t *testing.T) { tests := map[string]struct { input *types.RefreshWorkflowTasksRequest hcHandlerFunc func(mock *history.MockClient) dcHandlerFunc func(mock *cache.MockDomainCache) wantErr bool }{ "nil request": { input: nil, wantErr: true, }, "empty workflow execution": { input: &types.RefreshWorkflowTasksRequest{ Domain: "test-domain", }, wantErr: true, }, "check execution error": { input: &types.RefreshWorkflowTasksRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", }, }, dcHandlerFunc: func(mock *cache.MockDomainCache) { mock.EXPECT().GetDomain("test-domain").Return(nil, assert.AnError) }, wantErr: true, }, "normal request": { input: &types.RefreshWorkflowTasksRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", }, }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().RefreshWorkflowTasks(gomock.Any(), gomock.Any()).Return(nil) }, dcHandlerFunc: func(mock *cache.MockDomainCache) { mock.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-domain-id"}, &persistence.DomainConfig{}, &persistence.DomainReplicationConfig{}, 0, ), nil) }, wantErr: false, }, "refresh workflow tasks error": { input: &types.RefreshWorkflowTasksRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", }, }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().RefreshWorkflowTasks(gomock.Any(), gomock.Any()).Return(assert.AnError) }, dcHandlerFunc: func(mock *cache.MockDomainCache) { mock.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-domain-id"}, &persistence.DomainConfig{}, &persistence.DomainReplicationConfig{}, 0, ), nil) }, wantErr: true, }, } for _, tt := range tests { t.Run("", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() hcMock := history.NewMockClient(ctrl) dcMock := cache.NewMockDomainCache(ctrl) if tt.hcHandlerFunc != nil { tt.hcHandlerFunc(hcMock) } if tt.dcHandlerFunc != nil { tt.dcHandlerFunc(dcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, DomainCache: dcMock, }, } err := handler.RefreshWorkflowTasks(context.Background(), tt.input) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_ResendReplicationTasks(t *testing.T) { tests := map[string]struct { input *types.ResendReplicationTasksRequest hcHandlerFunc func(mock *history.MockClient) wantErr bool }{ "nil request": { input: nil, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "normal request": { input: &types.ResendReplicationTasksRequest{ RemoteCluster: "test-cluster", }, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } err := handler.ResendReplicationTasks(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_GetCrossClusterTasks(t *testing.T) { tests := map[string]struct { input *types.GetCrossClusterTasksRequest hcHandlerFunc func(mock *history.MockClient) wantErr bool }{ "nil request": { input: nil, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "missing cluster name": { input: &types.GetCrossClusterTasksRequest{}, hcHandlerFunc: func(mock *history.MockClient) { }, wantErr: true, }, "normal request": { input: &types.GetCrossClusterTasksRequest{ TargetCluster: "test-cluster", }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().GetCrossClusterTasks(gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: false, }, "return error": { input: &types.GetCrossClusterTasksRequest{ TargetCluster: "test-cluster", }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().GetCrossClusterTasks(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } _, err := handler.GetCrossClusterTasks(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_RespondCrossClusterTasksCompleted(t *testing.T) { tests := map[string]struct { input *types.RespondCrossClusterTasksCompletedRequest hcHandlerFunc func(mock *history.MockClient) wantErr bool }{ "nil request": { input: nil, wantErr: true, }, "empty target cluster": { input: &types.RespondCrossClusterTasksCompletedRequest{}, wantErr: true, }, "normal request": { input: &types.RespondCrossClusterTasksCompletedRequest{ TargetCluster: "test-cluster", }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().RespondCrossClusterTasksCompleted(gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: false, }, "respond error": { input: &types.RespondCrossClusterTasksCompletedRequest{ TargetCluster: "test-cluster", }, hcHandlerFunc: func(mock *history.MockClient) { mock.EXPECT().RespondCrossClusterTasksCompleted(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) if td.hcHandlerFunc != nil { td.hcHandlerFunc(hcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, } _, err := handler.RespondCrossClusterTasksCompleted(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func Test_SerializeRawHistoryToken(t *testing.T) { input := getWorkflowRawHistoryV2Token{ DomainName: "test-domain", } marshaledToken, _ := json.Marshal(input) tests := map[string]struct { input *getWorkflowRawHistoryV2Token wantResp []byte wantSerializeErr bool wantDeserializeErr bool }{ "normal request": { input: &getWorkflowRawHistoryV2Token{ DomainName: "test-domain", }, wantResp: marshaledToken, wantSerializeErr: false, wantDeserializeErr: false, }, "nil request": { input: nil, wantResp: nil, wantSerializeErr: false, wantDeserializeErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { resp, err := serializeRawHistoryToken(td.input) if td.wantSerializeErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, td.wantResp, resp) } // deserialize the serialized token res, err := deserializeRawHistoryToken(resp) if td.wantDeserializeErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, td.input, res) } }) } } func Test_ListDynamicConfig(t *testing.T) { tests := map[string]struct { input *types.ListDynamicConfigRequest dcHandlerFunc func(mock *dynamicconfig.MockClient) wantErr bool }{ "nil request": { input: nil, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) hcMock := history.NewMockClient(goMock) dcMock := dynamicconfig.NewMockClient(goMock) if td.dcHandlerFunc != nil { td.dcHandlerFunc(dcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), HistoryClient: hcMock, }, params: &resource.Params{ DynamicConfig: dcMock, }, } _, err := handler.ListDynamicConfig(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestValidateConfigForAdvanceVisibility_Error(t *testing.T) { handler := adminHandlerImpl{ params: &resource.Params{}, } err := handler.validateConfigForAdvanceVisibility() assert.Error(t, err, "ES related config not found") } func TestRestoreDynamicConfig(t *testing.T) { tests := map[string]struct { input *types.RestoreDynamicConfigRequest dcHandlerFunc func(mock *dynamicconfig.MockClient) wantErr bool }{ "nil request": { input: nil, wantErr: true, }, "invalid config name": { input: &types.RestoreDynamicConfigRequest{ ConfigName: "invalid", }, wantErr: true, }, "valid config name": { input: &types.RestoreDynamicConfigRequest{ ConfigName: "testGetIntPropertyKey", Filters: nil, }, dcHandlerFunc: func(mock *dynamicconfig.MockClient) { mock.EXPECT().RestoreValue(gomock.Any(), nil).Return(nil) }, wantErr: false, }, "valid config with invalid filters": { input: &types.RestoreDynamicConfigRequest{ ConfigName: "testGetIntPropertyKey", Filters: []*types.DynamicConfigFilter{ &types.DynamicConfigFilter{ Name: "test-filter", Value: &types.DataBlob{ Data: []byte("test-value"), }, }, }, }, wantErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) dcMock := dynamicconfig.NewMockClient(goMock) if td.dcHandlerFunc != nil { td.dcHandlerFunc(dcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), }, params: &resource.Params{ DynamicConfig: dcMock, }, } err := handler.RestoreDynamicConfig(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestListDynamicConfig(t *testing.T) { tests := map[string]struct { input *types.ListDynamicConfigRequest dcHandlerFunc func(mock *dynamicconfig.MockClient) wantErr bool }{ "nil request": { input: nil, wantErr: true, }, "invalid config name": { input: &types.ListDynamicConfigRequest{ ConfigName: "invalid", }, dcHandlerFunc: func(mock *dynamicconfig.MockClient) { mock.EXPECT().ListValue(gomock.Any()).Return(nil, assert.AnError) }, wantErr: true, }, "valid config name": { input: &types.ListDynamicConfigRequest{ ConfigName: "testGetIntPropertyKey", }, dcHandlerFunc: func(mock *dynamicconfig.MockClient) { mock.EXPECT().ListValue(gomock.Any()).Return(nil, nil) }, wantErr: false, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { goMock := gomock.NewController(t) dcMock := dynamicconfig.NewMockClient(goMock) if td.dcHandlerFunc != nil { td.dcHandlerFunc(dcMock) } handler := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), }, params: &resource.Params{ DynamicConfig: dcMock, }, } _, err := handler.ListDynamicConfig(context.Background(), td.input) if td.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestUpdateTaskListPartitionConfig(t *testing.T) { domainName := "domain-name" domainID := "domain-id" taskListName := "task-list" kind := types.TaskListKindNormal taskListType := types.TaskListTypeActivity partitionConfig := &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, } testCases := []struct { name string req *types.UpdateTaskListPartitionConfigRequest setupMocks func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) expectError bool expectedError string }{ { name: "success", req: &types.UpdateTaskListPartitionConfigRequest{ Domain: domainName, TaskList: &types.TaskList{Name: taskListName, Kind: &kind}, TaskListType: &taskListType, PartitionConfig: partitionConfig, }, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) mockMatchingClient.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: domainID, TaskList: &types.TaskList{Name: taskListName, Kind: &kind}, TaskListType: &taskListType, PartitionConfig: partitionConfig, }).Return(nil, nil) }, expectError: false, }, { name: "request not set", req: nil, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { // no mocks needed as the function should exit early }, expectError: true, expectedError: validate.ErrRequestNotSet.Error(), }, { name: "task list not set", req: &types.UpdateTaskListPartitionConfigRequest{ Domain: domainName, }, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) }, expectError: true, expectedError: validate.ErrTaskListNotSet.Error(), }, { name: "task list kind not set", req: &types.UpdateTaskListPartitionConfigRequest{ Domain: domainName, TaskList: &types.TaskList{Name: taskListName}, }, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) }, expectError: true, expectedError: "Task list kind not set.", }, { name: "invalid task list kind", req: &types.UpdateTaskListPartitionConfigRequest{ Domain: domainName, TaskList: &types.TaskList{Name: taskListName, Kind: types.TaskListKindSticky.Ptr()}, }, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) }, expectError: true, expectedError: "Only normal tasklist's partition config can be updated.", }, { name: "task list type not set", req: &types.UpdateTaskListPartitionConfigRequest{ Domain: domainName, TaskList: &types.TaskList{Name: taskListName, Kind: &kind}, }, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) }, expectError: true, expectedError: "Task list type not set.", }, { name: "partition config not set", req: &types.UpdateTaskListPartitionConfigRequest{ Domain: domainName, TaskList: &types.TaskList{Name: taskListName, Kind: &kind}, TaskListType: &taskListType, }, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) }, expectError: true, expectedError: "Task list partition config is not set in the request.", }, { name: "invalid partition config: write partitions > read partitions", req: &types.UpdateTaskListPartitionConfigRequest{ Domain: domainName, TaskList: &types.TaskList{ Name: taskListName, Kind: &kind, }, TaskListType: &taskListType, PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, }, }, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) }, expectError: true, expectedError: "The number of write partitions cannot be larger than the number of read partitions.", }, { name: "invalid partition config: write partitions <= 0", req: &types.UpdateTaskListPartitionConfigRequest{ Domain: domainName, TaskList: &types.TaskList{ Name: taskListName, Kind: &kind, }, TaskListType: &taskListType, PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, WritePartitions: nil, }, }, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) }, expectError: true, expectedError: "The number of partitions must be larger than 0.", }, { name: "domain cache error", req: &types.UpdateTaskListPartitionConfigRequest{ Domain: domainName, TaskList: &types.TaskList{ Name: taskListName, Kind: &kind, }, TaskListType: &taskListType, PartitionConfig: partitionConfig, }, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainID(domainName).Return("", errors.New("domain cache error")) }, expectError: true, expectedError: "domain cache error", }, { name: "matching client error", req: &types.UpdateTaskListPartitionConfigRequest{ Domain: domainName, TaskList: &types.TaskList{ Name: taskListName, Kind: &kind, }, TaskListType: &taskListType, PartitionConfig: partitionConfig, }, setupMocks: func(mockMatchingClient *matching.MockClient, mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainID(domainName).Return(domainID, nil) mockMatchingClient.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: domainID, TaskList: &types.TaskList{Name: taskListName, Kind: &kind}, TaskListType: &taskListType, PartitionConfig: partitionConfig, }).Return(nil, errors.New("matching client error")) }, expectError: true, expectedError: "matching client error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(ctrl) mockMatchingClient := matching.NewMockClient(ctrl) tc.setupMocks(mockMatchingClient, mockDomainCache) adh := adminHandlerImpl{ Resource: &resource.Test{ Logger: testlogger.New(t), MetricsClient: metrics.NewNoopMetricsClient(), DomainCache: mockDomainCache, MatchingClient: mockMatchingClient, }, } resp, err := adh.UpdateTaskListPartitionConfig(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.NotNil(t, resp) } }) } } ================================================ FILE: service/frontend/admin/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/service/frontend/admin //go:generate gowrap gen -g -p . -i Handler -t ../templates/accesscontrolled.tmpl -o ../wrappers/accesscontrolled/admin_generated.go -v handler=Admin //go:generate gowrap gen -g -p . -i Handler -t ../../templates/grpc.tmpl -o ../wrappers/grpc/admin_generated.go -v handler=Admin -v package=adminv1 -v path=github.com/uber/cadence-idl/go/proto/admin/v1 -v prefix=Admin //go:generate gowrap gen -g -p ../../../.gen/go/admin/adminserviceserver -i Interface -t ../../templates/thrift.tmpl -o ../wrappers/thrift/admin_generated.go -v handler=Admin -v prefix=Admin package admin import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) // Handler interface for admin service type Handler interface { common.Daemon AddSearchAttribute(context.Context, *types.AddSearchAttributeRequest) error CloseShard(context.Context, *types.CloseShardRequest) error DescribeCluster(context.Context) (*types.DescribeClusterResponse, error) DescribeShardDistribution(context.Context, *types.DescribeShardDistributionRequest) (*types.DescribeShardDistributionResponse, error) DescribeHistoryHost(context.Context, *types.DescribeHistoryHostRequest) (*types.DescribeHistoryHostResponse, error) DescribeQueue(context.Context, *types.DescribeQueueRequest) (*types.DescribeQueueResponse, error) DescribeWorkflowExecution(context.Context, *types.AdminDescribeWorkflowExecutionRequest) (*types.AdminDescribeWorkflowExecutionResponse, error) GetDLQReplicationMessages(context.Context, *types.GetDLQReplicationMessagesRequest) (*types.GetDLQReplicationMessagesResponse, error) GetDomainReplicationMessages(context.Context, *types.GetDomainReplicationMessagesRequest) (*types.GetDomainReplicationMessagesResponse, error) GetReplicationMessages(context.Context, *types.GetReplicationMessagesRequest) (*types.GetReplicationMessagesResponse, error) GetWorkflowExecutionRawHistoryV2(context.Context, *types.GetWorkflowExecutionRawHistoryV2Request) (*types.GetWorkflowExecutionRawHistoryV2Response, error) CountDLQMessages(context.Context, *types.CountDLQMessagesRequest) (*types.CountDLQMessagesResponse, error) MergeDLQMessages(context.Context, *types.MergeDLQMessagesRequest) (*types.MergeDLQMessagesResponse, error) PurgeDLQMessages(context.Context, *types.PurgeDLQMessagesRequest) error ReadDLQMessages(context.Context, *types.ReadDLQMessagesRequest) (*types.ReadDLQMessagesResponse, error) ReapplyEvents(context.Context, *types.ReapplyEventsRequest) error RefreshWorkflowTasks(context.Context, *types.RefreshWorkflowTasksRequest) error RemoveTask(context.Context, *types.RemoveTaskRequest) error ResendReplicationTasks(context.Context, *types.ResendReplicationTasksRequest) error ResetQueue(context.Context, *types.ResetQueueRequest) error GetCrossClusterTasks(context.Context, *types.GetCrossClusterTasksRequest) (*types.GetCrossClusterTasksResponse, error) RespondCrossClusterTasksCompleted(context.Context, *types.RespondCrossClusterTasksCompletedRequest) (*types.RespondCrossClusterTasksCompletedResponse, error) GetDynamicConfig(context.Context, *types.GetDynamicConfigRequest) (*types.GetDynamicConfigResponse, error) UpdateDynamicConfig(context.Context, *types.UpdateDynamicConfigRequest) error RestoreDynamicConfig(context.Context, *types.RestoreDynamicConfigRequest) error ListDynamicConfig(context.Context, *types.ListDynamicConfigRequest) (*types.ListDynamicConfigResponse, error) DeleteWorkflow(context.Context, *types.AdminDeleteWorkflowRequest) (*types.AdminDeleteWorkflowResponse, error) MaintainCorruptWorkflow(context.Context, *types.AdminMaintainWorkflowRequest) (*types.AdminMaintainWorkflowResponse, error) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest) (*types.GetGlobalIsolationGroupsResponse, error) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest) (*types.UpdateGlobalIsolationGroupsResponse, error) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest) (*types.GetDomainIsolationGroupsResponse, error) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest) (*types.UpdateDomainIsolationGroupsResponse, error) GetDomainAsyncWorkflowConfiguraton(context.Context, *types.GetDomainAsyncWorkflowConfiguratonRequest) (*types.GetDomainAsyncWorkflowConfiguratonResponse, error) UpdateDomainAsyncWorkflowConfiguraton(context.Context, *types.UpdateDomainAsyncWorkflowConfiguratonRequest) (*types.UpdateDomainAsyncWorkflowConfiguratonResponse, error) UpdateTaskListPartitionConfig(context.Context, *types.UpdateTaskListPartitionConfigRequest) (*types.UpdateTaskListPartitionConfigResponse, error) } ================================================ FILE: service/frontend/admin/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package admin -source interface.go -destination interface_mock.go -self_package github.com/uber/cadence/service/frontend/admin // // Package admin is a generated GoMock package. package admin import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockHandler is a mock of Handler interface. type MockHandler struct { ctrl *gomock.Controller recorder *MockHandlerMockRecorder isgomock struct{} } // MockHandlerMockRecorder is the mock recorder for MockHandler. type MockHandlerMockRecorder struct { mock *MockHandler } // NewMockHandler creates a new mock instance. func NewMockHandler(ctrl *gomock.Controller) *MockHandler { mock := &MockHandler{ctrl: ctrl} mock.recorder = &MockHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHandler) EXPECT() *MockHandlerMockRecorder { return m.recorder } // AddSearchAttribute mocks base method. func (m *MockHandler) AddSearchAttribute(arg0 context.Context, arg1 *types.AddSearchAttributeRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddSearchAttribute", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // AddSearchAttribute indicates an expected call of AddSearchAttribute. func (mr *MockHandlerMockRecorder) AddSearchAttribute(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSearchAttribute", reflect.TypeOf((*MockHandler)(nil).AddSearchAttribute), arg0, arg1) } // CloseShard mocks base method. func (m *MockHandler) CloseShard(arg0 context.Context, arg1 *types.CloseShardRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CloseShard", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // CloseShard indicates an expected call of CloseShard. func (mr *MockHandlerMockRecorder) CloseShard(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseShard", reflect.TypeOf((*MockHandler)(nil).CloseShard), arg0, arg1) } // CountDLQMessages mocks base method. func (m *MockHandler) CountDLQMessages(arg0 context.Context, arg1 *types.CountDLQMessagesRequest) (*types.CountDLQMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CountDLQMessages", arg0, arg1) ret0, _ := ret[0].(*types.CountDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CountDLQMessages indicates an expected call of CountDLQMessages. func (mr *MockHandlerMockRecorder) CountDLQMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountDLQMessages", reflect.TypeOf((*MockHandler)(nil).CountDLQMessages), arg0, arg1) } // DeleteWorkflow mocks base method. func (m *MockHandler) DeleteWorkflow(arg0 context.Context, arg1 *types.AdminDeleteWorkflowRequest) (*types.AdminDeleteWorkflowResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteWorkflow", arg0, arg1) ret0, _ := ret[0].(*types.AdminDeleteWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteWorkflow indicates an expected call of DeleteWorkflow. func (mr *MockHandlerMockRecorder) DeleteWorkflow(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWorkflow", reflect.TypeOf((*MockHandler)(nil).DeleteWorkflow), arg0, arg1) } // DescribeCluster mocks base method. func (m *MockHandler) DescribeCluster(arg0 context.Context) (*types.DescribeClusterResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeCluster", arg0) ret0, _ := ret[0].(*types.DescribeClusterResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeCluster indicates an expected call of DescribeCluster. func (mr *MockHandlerMockRecorder) DescribeCluster(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeCluster", reflect.TypeOf((*MockHandler)(nil).DescribeCluster), arg0) } // DescribeHistoryHost mocks base method. func (m *MockHandler) DescribeHistoryHost(arg0 context.Context, arg1 *types.DescribeHistoryHostRequest) (*types.DescribeHistoryHostResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeHistoryHost", arg0, arg1) ret0, _ := ret[0].(*types.DescribeHistoryHostResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeHistoryHost indicates an expected call of DescribeHistoryHost. func (mr *MockHandlerMockRecorder) DescribeHistoryHost(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeHistoryHost", reflect.TypeOf((*MockHandler)(nil).DescribeHistoryHost), arg0, arg1) } // DescribeQueue mocks base method. func (m *MockHandler) DescribeQueue(arg0 context.Context, arg1 *types.DescribeQueueRequest) (*types.DescribeQueueResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeQueue", arg0, arg1) ret0, _ := ret[0].(*types.DescribeQueueResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeQueue indicates an expected call of DescribeQueue. func (mr *MockHandlerMockRecorder) DescribeQueue(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeQueue", reflect.TypeOf((*MockHandler)(nil).DescribeQueue), arg0, arg1) } // DescribeShardDistribution mocks base method. func (m *MockHandler) DescribeShardDistribution(arg0 context.Context, arg1 *types.DescribeShardDistributionRequest) (*types.DescribeShardDistributionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeShardDistribution", arg0, arg1) ret0, _ := ret[0].(*types.DescribeShardDistributionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeShardDistribution indicates an expected call of DescribeShardDistribution. func (mr *MockHandlerMockRecorder) DescribeShardDistribution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeShardDistribution", reflect.TypeOf((*MockHandler)(nil).DescribeShardDistribution), arg0, arg1) } // DescribeWorkflowExecution mocks base method. func (m *MockHandler) DescribeWorkflowExecution(arg0 context.Context, arg1 *types.AdminDescribeWorkflowExecutionRequest) (*types.AdminDescribeWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.AdminDescribeWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeWorkflowExecution indicates an expected call of DescribeWorkflowExecution. func (mr *MockHandlerMockRecorder) DescribeWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).DescribeWorkflowExecution), arg0, arg1) } // GetCrossClusterTasks mocks base method. func (m *MockHandler) GetCrossClusterTasks(arg0 context.Context, arg1 *types.GetCrossClusterTasksRequest) (*types.GetCrossClusterTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCrossClusterTasks", arg0, arg1) ret0, _ := ret[0].(*types.GetCrossClusterTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCrossClusterTasks indicates an expected call of GetCrossClusterTasks. func (mr *MockHandlerMockRecorder) GetCrossClusterTasks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCrossClusterTasks", reflect.TypeOf((*MockHandler)(nil).GetCrossClusterTasks), arg0, arg1) } // GetDLQReplicationMessages mocks base method. func (m *MockHandler) GetDLQReplicationMessages(arg0 context.Context, arg1 *types.GetDLQReplicationMessagesRequest) (*types.GetDLQReplicationMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDLQReplicationMessages", arg0, arg1) ret0, _ := ret[0].(*types.GetDLQReplicationMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDLQReplicationMessages indicates an expected call of GetDLQReplicationMessages. func (mr *MockHandlerMockRecorder) GetDLQReplicationMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDLQReplicationMessages", reflect.TypeOf((*MockHandler)(nil).GetDLQReplicationMessages), arg0, arg1) } // GetDomainAsyncWorkflowConfiguraton mocks base method. func (m *MockHandler) GetDomainAsyncWorkflowConfiguraton(arg0 context.Context, arg1 *types.GetDomainAsyncWorkflowConfiguratonRequest) (*types.GetDomainAsyncWorkflowConfiguratonResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainAsyncWorkflowConfiguraton", arg0, arg1) ret0, _ := ret[0].(*types.GetDomainAsyncWorkflowConfiguratonResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainAsyncWorkflowConfiguraton indicates an expected call of GetDomainAsyncWorkflowConfiguraton. func (mr *MockHandlerMockRecorder) GetDomainAsyncWorkflowConfiguraton(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainAsyncWorkflowConfiguraton", reflect.TypeOf((*MockHandler)(nil).GetDomainAsyncWorkflowConfiguraton), arg0, arg1) } // GetDomainIsolationGroups mocks base method. func (m *MockHandler) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest) (*types.GetDomainIsolationGroupsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainIsolationGroups", ctx, request) ret0, _ := ret[0].(*types.GetDomainIsolationGroupsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainIsolationGroups indicates an expected call of GetDomainIsolationGroups. func (mr *MockHandlerMockRecorder) GetDomainIsolationGroups(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainIsolationGroups", reflect.TypeOf((*MockHandler)(nil).GetDomainIsolationGroups), ctx, request) } // GetDomainReplicationMessages mocks base method. func (m *MockHandler) GetDomainReplicationMessages(arg0 context.Context, arg1 *types.GetDomainReplicationMessagesRequest) (*types.GetDomainReplicationMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainReplicationMessages", arg0, arg1) ret0, _ := ret[0].(*types.GetDomainReplicationMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDomainReplicationMessages indicates an expected call of GetDomainReplicationMessages. func (mr *MockHandlerMockRecorder) GetDomainReplicationMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainReplicationMessages", reflect.TypeOf((*MockHandler)(nil).GetDomainReplicationMessages), arg0, arg1) } // GetDynamicConfig mocks base method. func (m *MockHandler) GetDynamicConfig(arg0 context.Context, arg1 *types.GetDynamicConfigRequest) (*types.GetDynamicConfigResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDynamicConfig", arg0, arg1) ret0, _ := ret[0].(*types.GetDynamicConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDynamicConfig indicates an expected call of GetDynamicConfig. func (mr *MockHandlerMockRecorder) GetDynamicConfig(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDynamicConfig", reflect.TypeOf((*MockHandler)(nil).GetDynamicConfig), arg0, arg1) } // GetGlobalIsolationGroups mocks base method. func (m *MockHandler) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest) (*types.GetGlobalIsolationGroupsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetGlobalIsolationGroups", ctx, request) ret0, _ := ret[0].(*types.GetGlobalIsolationGroupsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetGlobalIsolationGroups indicates an expected call of GetGlobalIsolationGroups. func (mr *MockHandlerMockRecorder) GetGlobalIsolationGroups(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGlobalIsolationGroups", reflect.TypeOf((*MockHandler)(nil).GetGlobalIsolationGroups), ctx, request) } // GetReplicationMessages mocks base method. func (m *MockHandler) GetReplicationMessages(arg0 context.Context, arg1 *types.GetReplicationMessagesRequest) (*types.GetReplicationMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReplicationMessages", arg0, arg1) ret0, _ := ret[0].(*types.GetReplicationMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetReplicationMessages indicates an expected call of GetReplicationMessages. func (mr *MockHandlerMockRecorder) GetReplicationMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationMessages", reflect.TypeOf((*MockHandler)(nil).GetReplicationMessages), arg0, arg1) } // GetWorkflowExecutionRawHistoryV2 mocks base method. func (m *MockHandler) GetWorkflowExecutionRawHistoryV2(arg0 context.Context, arg1 *types.GetWorkflowExecutionRawHistoryV2Request) (*types.GetWorkflowExecutionRawHistoryV2Response, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowExecutionRawHistoryV2", arg0, arg1) ret0, _ := ret[0].(*types.GetWorkflowExecutionRawHistoryV2Response) ret1, _ := ret[1].(error) return ret0, ret1 } // GetWorkflowExecutionRawHistoryV2 indicates an expected call of GetWorkflowExecutionRawHistoryV2. func (mr *MockHandlerMockRecorder) GetWorkflowExecutionRawHistoryV2(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowExecutionRawHistoryV2", reflect.TypeOf((*MockHandler)(nil).GetWorkflowExecutionRawHistoryV2), arg0, arg1) } // ListDynamicConfig mocks base method. func (m *MockHandler) ListDynamicConfig(arg0 context.Context, arg1 *types.ListDynamicConfigRequest) (*types.ListDynamicConfigResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListDynamicConfig", arg0, arg1) ret0, _ := ret[0].(*types.ListDynamicConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListDynamicConfig indicates an expected call of ListDynamicConfig. func (mr *MockHandlerMockRecorder) ListDynamicConfig(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDynamicConfig", reflect.TypeOf((*MockHandler)(nil).ListDynamicConfig), arg0, arg1) } // MaintainCorruptWorkflow mocks base method. func (m *MockHandler) MaintainCorruptWorkflow(arg0 context.Context, arg1 *types.AdminMaintainWorkflowRequest) (*types.AdminMaintainWorkflowResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MaintainCorruptWorkflow", arg0, arg1) ret0, _ := ret[0].(*types.AdminMaintainWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MaintainCorruptWorkflow indicates an expected call of MaintainCorruptWorkflow. func (mr *MockHandlerMockRecorder) MaintainCorruptWorkflow(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaintainCorruptWorkflow", reflect.TypeOf((*MockHandler)(nil).MaintainCorruptWorkflow), arg0, arg1) } // MergeDLQMessages mocks base method. func (m *MockHandler) MergeDLQMessages(arg0 context.Context, arg1 *types.MergeDLQMessagesRequest) (*types.MergeDLQMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MergeDLQMessages", arg0, arg1) ret0, _ := ret[0].(*types.MergeDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MergeDLQMessages indicates an expected call of MergeDLQMessages. func (mr *MockHandlerMockRecorder) MergeDLQMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MergeDLQMessages", reflect.TypeOf((*MockHandler)(nil).MergeDLQMessages), arg0, arg1) } // PurgeDLQMessages mocks base method. func (m *MockHandler) PurgeDLQMessages(arg0 context.Context, arg1 *types.PurgeDLQMessagesRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PurgeDLQMessages", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // PurgeDLQMessages indicates an expected call of PurgeDLQMessages. func (mr *MockHandlerMockRecorder) PurgeDLQMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PurgeDLQMessages", reflect.TypeOf((*MockHandler)(nil).PurgeDLQMessages), arg0, arg1) } // ReadDLQMessages mocks base method. func (m *MockHandler) ReadDLQMessages(arg0 context.Context, arg1 *types.ReadDLQMessagesRequest) (*types.ReadDLQMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadDLQMessages", arg0, arg1) ret0, _ := ret[0].(*types.ReadDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadDLQMessages indicates an expected call of ReadDLQMessages. func (mr *MockHandlerMockRecorder) ReadDLQMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDLQMessages", reflect.TypeOf((*MockHandler)(nil).ReadDLQMessages), arg0, arg1) } // ReapplyEvents mocks base method. func (m *MockHandler) ReapplyEvents(arg0 context.Context, arg1 *types.ReapplyEventsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReapplyEvents", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ReapplyEvents indicates an expected call of ReapplyEvents. func (mr *MockHandlerMockRecorder) ReapplyEvents(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReapplyEvents", reflect.TypeOf((*MockHandler)(nil).ReapplyEvents), arg0, arg1) } // RefreshWorkflowTasks mocks base method. func (m *MockHandler) RefreshWorkflowTasks(arg0 context.Context, arg1 *types.RefreshWorkflowTasksRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RefreshWorkflowTasks", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RefreshWorkflowTasks indicates an expected call of RefreshWorkflowTasks. func (mr *MockHandlerMockRecorder) RefreshWorkflowTasks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshWorkflowTasks", reflect.TypeOf((*MockHandler)(nil).RefreshWorkflowTasks), arg0, arg1) } // RemoveTask mocks base method. func (m *MockHandler) RemoveTask(arg0 context.Context, arg1 *types.RemoveTaskRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoveTask", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RemoveTask indicates an expected call of RemoveTask. func (mr *MockHandlerMockRecorder) RemoveTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveTask", reflect.TypeOf((*MockHandler)(nil).RemoveTask), arg0, arg1) } // ResendReplicationTasks mocks base method. func (m *MockHandler) ResendReplicationTasks(arg0 context.Context, arg1 *types.ResendReplicationTasksRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResendReplicationTasks", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ResendReplicationTasks indicates an expected call of ResendReplicationTasks. func (mr *MockHandlerMockRecorder) ResendReplicationTasks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResendReplicationTasks", reflect.TypeOf((*MockHandler)(nil).ResendReplicationTasks), arg0, arg1) } // ResetQueue mocks base method. func (m *MockHandler) ResetQueue(arg0 context.Context, arg1 *types.ResetQueueRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetQueue", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ResetQueue indicates an expected call of ResetQueue. func (mr *MockHandlerMockRecorder) ResetQueue(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetQueue", reflect.TypeOf((*MockHandler)(nil).ResetQueue), arg0, arg1) } // RespondCrossClusterTasksCompleted mocks base method. func (m *MockHandler) RespondCrossClusterTasksCompleted(arg0 context.Context, arg1 *types.RespondCrossClusterTasksCompletedRequest) (*types.RespondCrossClusterTasksCompletedResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondCrossClusterTasksCompleted", arg0, arg1) ret0, _ := ret[0].(*types.RespondCrossClusterTasksCompletedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RespondCrossClusterTasksCompleted indicates an expected call of RespondCrossClusterTasksCompleted. func (mr *MockHandlerMockRecorder) RespondCrossClusterTasksCompleted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondCrossClusterTasksCompleted", reflect.TypeOf((*MockHandler)(nil).RespondCrossClusterTasksCompleted), arg0, arg1) } // RestoreDynamicConfig mocks base method. func (m *MockHandler) RestoreDynamicConfig(arg0 context.Context, arg1 *types.RestoreDynamicConfigRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RestoreDynamicConfig", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RestoreDynamicConfig indicates an expected call of RestoreDynamicConfig. func (mr *MockHandlerMockRecorder) RestoreDynamicConfig(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestoreDynamicConfig", reflect.TypeOf((*MockHandler)(nil).RestoreDynamicConfig), arg0, arg1) } // Start mocks base method. func (m *MockHandler) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockHandlerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockHandler)(nil).Start)) } // Stop mocks base method. func (m *MockHandler) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockHandlerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockHandler)(nil).Stop)) } // UpdateDomainAsyncWorkflowConfiguraton mocks base method. func (m *MockHandler) UpdateDomainAsyncWorkflowConfiguraton(arg0 context.Context, arg1 *types.UpdateDomainAsyncWorkflowConfiguratonRequest) (*types.UpdateDomainAsyncWorkflowConfiguratonResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomainAsyncWorkflowConfiguraton", arg0, arg1) ret0, _ := ret[0].(*types.UpdateDomainAsyncWorkflowConfiguratonResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomainAsyncWorkflowConfiguraton indicates an expected call of UpdateDomainAsyncWorkflowConfiguraton. func (mr *MockHandlerMockRecorder) UpdateDomainAsyncWorkflowConfiguraton(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomainAsyncWorkflowConfiguraton", reflect.TypeOf((*MockHandler)(nil).UpdateDomainAsyncWorkflowConfiguraton), arg0, arg1) } // UpdateDomainIsolationGroups mocks base method. func (m *MockHandler) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest) (*types.UpdateDomainIsolationGroupsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomainIsolationGroups", ctx, request) ret0, _ := ret[0].(*types.UpdateDomainIsolationGroupsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomainIsolationGroups indicates an expected call of UpdateDomainIsolationGroups. func (mr *MockHandlerMockRecorder) UpdateDomainIsolationGroups(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomainIsolationGroups", reflect.TypeOf((*MockHandler)(nil).UpdateDomainIsolationGroups), ctx, request) } // UpdateDynamicConfig mocks base method. func (m *MockHandler) UpdateDynamicConfig(arg0 context.Context, arg1 *types.UpdateDynamicConfigRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDynamicConfig", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // UpdateDynamicConfig indicates an expected call of UpdateDynamicConfig. func (mr *MockHandlerMockRecorder) UpdateDynamicConfig(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDynamicConfig", reflect.TypeOf((*MockHandler)(nil).UpdateDynamicConfig), arg0, arg1) } // UpdateGlobalIsolationGroups mocks base method. func (m *MockHandler) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest) (*types.UpdateGlobalIsolationGroupsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateGlobalIsolationGroups", ctx, request) ret0, _ := ret[0].(*types.UpdateGlobalIsolationGroupsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateGlobalIsolationGroups indicates an expected call of UpdateGlobalIsolationGroups. func (mr *MockHandlerMockRecorder) UpdateGlobalIsolationGroups(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGlobalIsolationGroups", reflect.TypeOf((*MockHandler)(nil).UpdateGlobalIsolationGroups), ctx, request) } // UpdateTaskListPartitionConfig mocks base method. func (m *MockHandler) UpdateTaskListPartitionConfig(arg0 context.Context, arg1 *types.UpdateTaskListPartitionConfigRequest) (*types.UpdateTaskListPartitionConfigResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskListPartitionConfig", arg0, arg1) ret0, _ := ret[0].(*types.UpdateTaskListPartitionConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskListPartitionConfig indicates an expected call of UpdateTaskListPartitionConfig. func (mr *MockHandlerMockRecorder) UpdateTaskListPartitionConfig(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListPartitionConfig", reflect.TypeOf((*MockHandler)(nil).UpdateTaskListPartitionConfig), arg0, arg1) } ================================================ FILE: service/frontend/api/domain_handlers.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package api import ( "context" "fmt" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/validate" ) // RegisterDomain creates a new domain which can be used as a container for all resources. Domain is a top level // entity within Cadence, used as a container for all resources like workflow executions, tasklists, etc. Domain // acts as a sandbox and provides isolation for all resources within the domain. All resources belongs to exactly one // domain. func (wh *WorkflowHandler) RegisterDomain(ctx context.Context, registerRequest *types.RegisterDomainRequest) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if err := wh.requestValidator.ValidateRegisterDomainRequest(ctx, registerRequest); err != nil { return err } return wh.domainHandler.RegisterDomain(ctx, registerRequest) } // ListDomains returns the information and configuration for a registered domain. func (wh *WorkflowHandler) ListDomains( ctx context.Context, listRequest *types.ListDomainsRequest, ) (response *types.ListDomainsResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if listRequest == nil { return nil, validate.ErrRequestNotSet } return wh.domainHandler.ListDomains(ctx, listRequest) } // DescribeDomain returns the information and configuration for a registered domain. func (wh *WorkflowHandler) DescribeDomain( ctx context.Context, describeRequest *types.DescribeDomainRequest, ) (response *types.DescribeDomainResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateDescribeDomainRequest(ctx, describeRequest); err != nil { return nil, err } resp, err := wh.domainHandler.DescribeDomain(ctx, describeRequest) if err != nil { return nil, err } if resp.GetFailoverInfo() != nil && resp.GetFailoverInfo().GetFailoverExpireTimestamp() > 0 { // fetch ongoing failover info from history service failoverResp, err := wh.GetHistoryClient().GetFailoverInfo(ctx, &types.GetFailoverInfoRequest{ DomainID: resp.GetDomainInfo().UUID, }) if err != nil { // despite the error from history, return describe domain response wh.GetLogger().Error( fmt.Sprintf("Failed to get failover info for domain %s", resp.DomainInfo.GetName()), tag.Error(err), ) return resp, nil } resp.FailoverInfo.CompletedShardCount = failoverResp.GetCompletedShardCount() resp.FailoverInfo.PendingShards = failoverResp.GetPendingShards() } return resp, nil } // UpdateDomain is used to update the information and configuration for a registered domain. func (wh *WorkflowHandler) UpdateDomain( ctx context.Context, updateRequest *types.UpdateDomainRequest, ) (resp *types.UpdateDomainResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateUpdateDomainRequest(ctx, updateRequest); err != nil { return nil, err } domainName := updateRequest.GetName() logger := wh.GetLogger().WithTags( tag.WorkflowDomainName(domainName), tag.OperationName("DomainUpdate")) isFailover := isFailoverRequest(updateRequest) isGraceFailover := isGraceFailoverRequest(updateRequest) logger.Info(fmt.Sprintf( "Domain Update requested. isFailover: %v, isGraceFailover: %v, Request: %#v.", isFailover, isGraceFailover, updateRequest)) if isGraceFailover { if err := wh.checkOngoingFailover( ctx, &updateRequest.Name, ); err != nil { logger.Error("Graceful domain failover request failed. Not able to check ongoing failovers.", tag.Error(err)) return nil, err } } // TODO: call remote clusters to verify domain data resp, err := wh.domainHandler.UpdateDomain(ctx, updateRequest) if err != nil { logger.Error("Domain update operation failed.", tag.Error(err)) return nil, err } logger.Info("Domain update operation succeeded.") return resp, nil } // DeleteDomain permanently removes a domain record. This operation: // - Requires domain to be in DEPRECATED status // - Cannot be performed on domains with running workflows // - Is irreversible and removes all domain data func (wh *WorkflowHandler) DeleteDomain(ctx context.Context, deleteRequest *types.DeleteDomainRequest) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if err := wh.requestValidator.ValidateDeleteDomainRequest(ctx, deleteRequest); err != nil { return err } domainName := deleteRequest.GetName() resp, err := wh.domainHandler.DescribeDomain(ctx, &types.DescribeDomainRequest{Name: &domainName}) if err != nil { return err } if *resp.DomainInfo.Status != types.DomainStatusDeprecated { return &types.BadRequestError{Message: "Domain is not in a deprecated state."} } workflowList, err := wh.ListWorkflowExecutions(ctx, &types.ListWorkflowExecutionsRequest{ Domain: domainName, }) if err != nil { return err } if len(workflowList.Executions) != 0 { return &types.BadRequestError{Message: "Domain still have workflow execution history."} } return wh.domainHandler.DeleteDomain(ctx, deleteRequest) } // DeprecateDomain is used to update status of a registered domain to DEleTED. Once the domain is deleted // it cannot be used to start new workflow executions. Existing workflow executions will continue to run on // deprecated domains. func (wh *WorkflowHandler) DeprecateDomain(ctx context.Context, deprecateRequest *types.DeprecateDomainRequest) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if err := wh.requestValidator.ValidateDeprecateDomainRequest(ctx, deprecateRequest); err != nil { return err } return wh.domainHandler.DeprecateDomain(ctx, deprecateRequest) } // FailoverDomain is used to failover a registered domain to different cluster. func (wh *WorkflowHandler) FailoverDomain(ctx context.Context, failoverRequest *types.FailoverDomainRequest) (*types.FailoverDomainResponse, error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateFailoverDomainRequest(ctx, failoverRequest); err != nil { return nil, err } domainName := failoverRequest.GetDomainName() logger := wh.GetLogger().WithTags( tag.WorkflowDomainName(domainName), tag.OperationName("FailoverDomain")) logger.Info("Failover domain requested.", tag.ActiveClusterName(failoverRequest.GetDomainActiveClusterName()), tag.Dynamic("active-clusters-by-cluster-attribute", failoverRequest.ActiveClusters), ) failoverResp, err := wh.domainHandler.FailoverDomain(ctx, failoverRequest) if err != nil { logger.Error("Failover domain operation failed.", tag.Error(err)) return nil, err } logger.Info("Failover domain operation succeeded.") return failoverResp, nil } func (wh *WorkflowHandler) ListFailoverHistory(ctx context.Context, request *types.ListFailoverHistoryRequest) (*types.ListFailoverHistoryResponse, error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } logger := wh.GetLogger().WithTags( tag.OperationName("ListFailoverHistory")) logger.Info("List failover history request received.") domainAuditManager := wh.GetPersistenceBean().GetDomainAuditManager() if domainAuditManager == nil { return nil, &types.InternalServiceError{Message: "DomainAuditManager not available"} } filters := request.GetFilters() if filters == nil || filters.DomainID == "" { return nil, &types.BadRequestError{Message: "DomainID is required in filters"} } pageSize := 50 if request.GetPagination() != nil && request.GetPagination().PageSize != nil { pageSize = int(*request.GetPagination().PageSize) } auditLogsResp, err := domainAuditManager.GetDomainAuditLogs(ctx, &persistence.GetDomainAuditLogsRequest{ DomainID: filters.DomainID, OperationType: persistence.DomainAuditOperationTypeFailover, PageSize: pageSize, NextPageToken: request.GetPagination().GetNextPageToken(), }) if err != nil { logger.Error("Failed to get domain audit logs", tag.Error(err)) return nil, err } failoverEvents := make([]*types.FailoverEvent, 0, len(auditLogsResp.AuditLogs)) for _, auditLog := range auditLogsResp.AuditLogs { event := auditLog.ToFailoverEvents() if event != nil && event.ClusterFailovers != nil { failoverEvents = append(failoverEvents, event) } } return &types.ListFailoverHistoryResponse{ FailoverEvents: failoverEvents, NextPageToken: auditLogsResp.NextPageToken, }, nil } ================================================ FILE: service/frontend/api/domain_handlers_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package api import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestDeprecateDomain(t *testing.T) { testCases := []struct { name string req *types.DeprecateDomainRequest setupMocks func(*mockDeps) expectError bool expectedError string }{ { name: "success", req: &types.DeprecateDomainRequest{ Name: "domain-name", }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDeprecateDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DeprecateDomain(gomock.Any(), gomock.Any()).Return(nil) }, expectError: false, }, { name: "validation error", req: &types.DeprecateDomainRequest{ Name: "domain-name", }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDeprecateDomainRequest(gomock.Any(), gomock.Any()).Return(errors.New("validation error")) }, expectError: true, expectedError: "validation error", }, { name: "deprecate domain handler error", req: &types.DeprecateDomainRequest{ Name: "domain-name", }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDeprecateDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DeprecateDomain(gomock.Any(), gomock.Any()).Return(errors.New("handler error")) }, expectError: true, expectedError: "handler error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { wh, deps := setupMocksForWorkflowHandler(t) tc.setupMocks(deps) err := wh.DeprecateDomain(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestRegisterDomain(t *testing.T) { testCases := []struct { name string req *types.RegisterDomainRequest setupMocks func(*mockDeps) expectError bool expectedError string }{ { name: "success", req: &types.RegisterDomainRequest{ Name: "domain-name", }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateRegisterDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().RegisterDomain(gomock.Any(), gomock.Any()).Return(nil) }, expectError: false, }, { name: "validation error", req: &types.RegisterDomainRequest{ Name: "domain-name", }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateRegisterDomainRequest(gomock.Any(), gomock.Any()).Return(errors.New("validation error")) }, expectError: true, expectedError: "validation error", }, { name: "register domain handler error", req: &types.RegisterDomainRequest{ Name: "domain-name", }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateRegisterDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().RegisterDomain(gomock.Any(), gomock.Any()).Return(errors.New("handler error")) }, expectError: true, expectedError: "handler error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { wh, deps := setupMocksForWorkflowHandler(t) tc.setupMocks(deps) err := wh.RegisterDomain(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestDescribeDomain(t *testing.T) { domainName := "domain-name" // Define the domain name pointer to pass in requests testCases := []struct { name string req *types.DescribeDomainRequest setupMocks func(*mockDeps) expectError bool expectedError string verifyResp func(t *testing.T, resp *types.DescribeDomainResponse) }{ { name: "success without failover info", req: &types.DescribeDomainRequest{ Name: &domainName, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDescribeDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "domain-name", }, FailoverInfo: nil, }, nil) }, expectError: false, verifyResp: func(t *testing.T, resp *types.DescribeDomainResponse) { assert.NotNil(t, resp) assert.Equal(t, "domain-name", resp.DomainInfo.Name) }, }, { name: "success with failover info and no error from history client", req: &types.DescribeDomainRequest{ Name: &domainName, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDescribeDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "domain-name", UUID: "domain-id", }, FailoverInfo: &types.FailoverInfo{ FailoverExpireTimestamp: 1000, }, }, nil) deps.mockHistoryClient.EXPECT().GetFailoverInfo(gomock.Any(), &types.GetFailoverInfoRequest{ DomainID: "domain-id", }).Return(&types.GetFailoverInfoResponse{ CompletedShardCount: 5, PendingShards: []int32{10}, }, nil) }, expectError: false, verifyResp: func(t *testing.T, resp *types.DescribeDomainResponse) { assert.NotNil(t, resp) assert.Equal(t, int32(5), resp.FailoverInfo.CompletedShardCount) assert.Equal(t, []int32{10}, resp.FailoverInfo.PendingShards) }, }, { name: "error from validation", req: &types.DescribeDomainRequest{ Name: &domainName, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDescribeDomainRequest(gomock.Any(), gomock.Any()).Return(errors.New("validation error")) }, expectError: true, expectedError: "validation error", }, { name: "error from domain handler", req: &types.DescribeDomainRequest{ Name: &domainName, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDescribeDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(nil, errors.New("domain handler error")) }, expectError: true, expectedError: "domain handler error", }, { name: "error from history client", req: &types.DescribeDomainRequest{ Name: &domainName, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDescribeDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "domain-name", UUID: "domain-id", }, FailoverInfo: &types.FailoverInfo{ FailoverExpireTimestamp: 1000, }, }, nil) deps.mockHistoryClient.EXPECT().GetFailoverInfo(gomock.Any(), &types.GetFailoverInfoRequest{ DomainID: "domain-id", }).Return(nil, errors.New("history client error")) }, expectError: false, verifyResp: func(t *testing.T, resp *types.DescribeDomainResponse) { assert.NotNil(t, resp) assert.Equal(t, "domain-name", resp.DomainInfo.Name) assert.Zero(t, resp.FailoverInfo.CompletedShardCount) assert.Nil(t, resp.FailoverInfo.PendingShards) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { wh, deps := setupMocksForWorkflowHandler(t) tc.setupMocks(deps) resp, err := wh.DescribeDomain(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) tc.verifyResp(t, resp) } }) } } func TestDeleteDomain(t *testing.T) { domainName := "domain-name" testCases := []struct { name string req *types.DeleteDomainRequest setupMocks func(*mockDeps) expectError bool expectedError string }{ { name: "success_delete_deprecated_domain", req: &types.DeleteDomainRequest{ Name: domainName, }, setupMocks: func(deps *mockDeps) { deps.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return("testDomainID", nil).AnyTimes() deps.mockRequestValidator.EXPECT().ValidateDeleteDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockRequestValidator.EXPECT().ValidateListWorkflowExecutionsRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &domainName, }).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "deprecated-domain", Status: types.DomainStatusDeprecated.Ptr(), }, }, nil) deps.mockVisibilityMgr.On("ListWorkflowExecutions", mock.Anything, mock.Anything).Return(&persistence.ListWorkflowExecutionsResponse{Executions: []*types.WorkflowExecutionInfo{}}, nil) deps.mockDomainHandler.EXPECT().DeleteDomain(gomock.Any(), gomock.Any()).Return(nil) }, expectError: false, }, { name: "failure_domain_not_found", req: &types.DeleteDomainRequest{ Name: domainName, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDeleteDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &domainName, }).Return(nil, errors.New("domain not found")) }, expectError: true, expectedError: "domain not found", }, { name: "failure_domain_not_deprecated", req: &types.DeleteDomainRequest{ Name: domainName, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDeleteDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &domainName, }).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "active-domain", Status: types.DomainStatusRegistered.Ptr(), }, }, nil) }, expectError: true, expectedError: "Domain is not in a deprecated state.", }, { name: "failure_domain_has_workflow_history", req: &types.DeleteDomainRequest{ Name: domainName, }, setupMocks: func(deps *mockDeps) { deps.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return("testDomainID", nil).AnyTimes() deps.mockRequestValidator.EXPECT().ValidateDeleteDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockRequestValidator.EXPECT().ValidateListWorkflowExecutionsRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &domainName, }).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "domain-with-history", Status: types.DomainStatusDeprecated.Ptr(), }, }, nil) deps.mockVisibilityMgr.On("ListWorkflowExecutions", mock.Anything, mock.Anything).Return(&persistence.ListWorkflowExecutionsResponse{ Executions: []*types.WorkflowExecutionInfo{ { Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow", RunID: "test-run", }, }, }, }, nil) }, expectError: true, expectedError: "Domain still have workflow execution history.", }, { name: "failure_delete_domain_error", req: &types.DeleteDomainRequest{ Name: domainName, }, setupMocks: func(deps *mockDeps) { deps.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return("testDomainID", nil).AnyTimes() deps.mockRequestValidator.EXPECT().ValidateDeleteDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockRequestValidator.EXPECT().ValidateListWorkflowExecutionsRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainHandler.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &domainName, }).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "deprecated-domain", Status: types.DomainStatusDeprecated.Ptr(), }, }, nil) deps.mockVisibilityMgr.On("ListWorkflowExecutions", mock.Anything, mock.Anything).Return(&persistence.ListWorkflowExecutionsResponse{ Executions: []*types.WorkflowExecutionInfo{}, }, nil) deps.mockDomainHandler.EXPECT().DeleteDomain(gomock.Any(), gomock.Any()).Return(errors.New("delete domain error")) }, expectError: true, expectedError: "delete domain error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { wh, deps := setupMocksForWorkflowHandler(t) tc.setupMocks(deps) err := wh.DeleteDomain(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestListFailoverHistory(t *testing.T) { domainID := "test-domain-id" eventID1 := "event-id-1" customPageSize := int32(100) nextPageToken := []byte("next-page-token") testCases := []struct { name string req *types.ListFailoverHistoryRequest setupMocks func(*mockDeps) expectError bool expectedError string verifyResp func(t *testing.T, resp *types.ListFailoverHistoryResponse) }{ { name: "success_with_default_page_size", req: &types.ListFailoverHistoryRequest{ Filters: &types.ListFailoverHistoryRequestFilters{ DomainID: domainID, }, }, setupMocks: func(deps *mockDeps) { deps.mockResource.DomainAuditMgr.EXPECT().GetDomainAuditLogs( gomock.Any(), &persistence.GetDomainAuditLogsRequest{ DomainID: domainID, OperationType: persistence.DomainAuditOperationTypeFailover, PageSize: 50, NextPageToken: nil, }, ).Return(&persistence.GetDomainAuditLogsResponse{ AuditLogs: []*persistence.DomainAuditLog{ { EventID: eventID1, DomainID: domainID, StateBefore: &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "cluster-us-west", }, FailoverVersion: 1, }, StateAfter: &persistence.GetDomainResponse{ ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: "cluster-us-east", }, FailoverVersion: 2, }, }, }, NextPageToken: nextPageToken, }, nil) }, expectError: false, verifyResp: func(t *testing.T, resp *types.ListFailoverHistoryResponse) { assert.NotNil(t, resp) assert.Len(t, resp.FailoverEvents, 1) assert.Equal(t, nextPageToken, resp.NextPageToken) assert.NotNil(t, resp.FailoverEvents[0].ClusterFailovers) assert.Len(t, resp.FailoverEvents[0].ClusterFailovers, 1) assert.Equal(t, "cluster-us-west", resp.FailoverEvents[0].ClusterFailovers[0].FromCluster.ActiveClusterName) assert.Equal(t, "cluster-us-east", resp.FailoverEvents[0].ClusterFailovers[0].ToCluster.ActiveClusterName) }, }, { name: "success_with_custom_page_size", req: &types.ListFailoverHistoryRequest{ Filters: &types.ListFailoverHistoryRequestFilters{ DomainID: domainID, }, Pagination: &types.PaginationOptions{ PageSize: &customPageSize, }, }, setupMocks: func(deps *mockDeps) { deps.mockResource.DomainAuditMgr.EXPECT().GetDomainAuditLogs( gomock.Any(), &persistence.GetDomainAuditLogsRequest{ DomainID: domainID, OperationType: persistence.DomainAuditOperationTypeFailover, PageSize: 100, NextPageToken: nil, }, ).Return(&persistence.GetDomainAuditLogsResponse{ AuditLogs: []*persistence.DomainAuditLog{}, NextPageToken: nil, }, nil) }, expectError: false, verifyResp: func(t *testing.T, resp *types.ListFailoverHistoryResponse) { assert.NotNil(t, resp) assert.Len(t, resp.FailoverEvents, 0) assert.Nil(t, resp.NextPageToken) }, }, { name: "error_nil_filters", req: &types.ListFailoverHistoryRequest{ Filters: nil, }, setupMocks: func(deps *mockDeps) { // No mocks needed as validation fails early }, expectError: true, expectedError: "DomainID is required in filters", }, { name: "error_get_domain_audit_logs_fails", req: &types.ListFailoverHistoryRequest{ Filters: &types.ListFailoverHistoryRequestFilters{ DomainID: domainID, }, }, setupMocks: func(deps *mockDeps) { deps.mockResource.DomainAuditMgr.EXPECT().GetDomainAuditLogs( gomock.Any(), gomock.Any(), ).Return(nil, errors.New("persistence error")) }, expectError: true, expectedError: "persistence error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { wh, deps := setupMocksForWorkflowHandler(t) tc.setupMocks(deps) resp, err := wh.ListFailoverHistory(context.Background(), tc.req) if tc.expectError { assert.Error(t, err) assert.ErrorContains(t, err, tc.expectedError) assert.Nil(t, resp) } else { assert.NoError(t, err) tc.verifyResp(t, resp) } }) } } ================================================ FILE: service/frontend/api/handler.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package api import ( "context" "encoding/json" "errors" "fmt" "sync/atomic" "time" "github.com/google/uuid" "go.uber.org/yarpc" "golang.org/x/sync/errgroup" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/.gen/go/sqlblobs" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/elasticsearch/validator" cadenceerrors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" persistenceutils "github.com/uber/cadence/common/persistence/persistence-utils" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" "github.com/uber/cadence/service/frontend/config" "github.com/uber/cadence/service/frontend/validate" "github.com/uber/cadence/service/worker/diagnostics" ) const ( // HealthStatusOK is used when this node is healthy and rpc requests are allowed HealthStatusOK HealthStatus = iota + 1 // HealthStatusWarmingUp is used when the rpc handler is warming up HealthStatusWarmingUp // HealthStatusShuttingDown is used when the rpc handler is shutting down HealthStatusShuttingDown ) var _ Handler = (*WorkflowHandler)(nil) type ( // WorkflowHandler - Thrift handler interface for workflow service WorkflowHandler struct { resource.Resource shuttingDown int32 healthStatus int32 tokenSerializer common.TaskTokenSerializer config *config.Config versionChecker client.VersionChecker domainHandler domain.Handler visibilityQueryValidator *validator.VisibilityQueryValidator searchAttributesValidator *validator.SearchAttributesValidator throttleRetry *backoff.ThrottleRetry producerManager ProducerManager thriftrwEncoder codec.BinaryEncoder requestValidator RequestValidator } getHistoryContinuationToken struct { RunID string FirstEventID int64 NextEventID int64 IsWorkflowRunning bool PersistenceToken []byte TransientDecision *types.TransientDecisionInfo BranchToken []byte VersionHistoryItem *types.VersionHistoryItem } domainGetter interface { GetDomain() string } // HealthStatus is an enum that refers to the rpc handler health status HealthStatus int32 ) var ( frontendServiceRetryPolicy = common.CreateFrontendServiceRetryPolicy() ) // NewWorkflowHandler creates a thrift handler for the cadence service func NewWorkflowHandler( resource resource.Resource, config *config.Config, versionChecker client.VersionChecker, domainHandler domain.Handler, ) *WorkflowHandler { return &WorkflowHandler{ Resource: resource, config: config, healthStatus: int32(HealthStatusWarmingUp), tokenSerializer: common.NewJSONTaskTokenSerializer(), versionChecker: versionChecker, domainHandler: domainHandler, visibilityQueryValidator: validator.NewQueryValidator( config.ValidSearchAttributes, config.EnableQueryAttributeValidation, ), searchAttributesValidator: validator.NewSearchAttributesValidator( resource.GetLogger(), config.EnableQueryAttributeValidation, config.ValidSearchAttributes, config.SearchAttributesNumberOfKeysLimit, config.SearchAttributesSizeOfValueLimit, config.SearchAttributesTotalSizeLimit, ), throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(frontendServiceRetryPolicy), backoff.WithRetryableError(common.IsServiceTransientError), ), producerManager: NewProducerManager( resource.GetDomainCache(), resource.GetAsyncWorkflowQueueProvider(), resource.GetLogger(), resource.GetMetricsClient(), ), thriftrwEncoder: codec.NewThriftRWEncoder(), requestValidator: NewRequestValidator(resource.GetLogger(), resource.GetMetricsClient(), config), } } // Start starts the handler func (wh *WorkflowHandler) Start() { warmupTimer := time.NewTimer(wh.config.WarmupDuration()) go func() { <-warmupTimer.C wh.GetLogger().Warn("Service warmup duration has elapsed.") if atomic.CompareAndSwapInt32(&wh.healthStatus, int32(HealthStatusWarmingUp), int32(HealthStatusOK)) { wh.GetLogger().Warn("Warmup time has elapsed. Service is healthy.") } else { status := HealthStatus(atomic.LoadInt32(&wh.healthStatus)) wh.GetLogger().Warn(fmt.Sprintf("Warmup time has elapsed. Service status is: %v", status.String())) } }() } // Stop stops the handler func (wh *WorkflowHandler) Stop() { atomic.StoreInt32(&wh.shuttingDown, 1) } // UpdateHealthStatus sets the health status for this rpc handler. // This health status will be used within the rpc health check handler func (wh *WorkflowHandler) UpdateHealthStatus(status HealthStatus) { atomic.StoreInt32(&wh.healthStatus, int32(status)) } func (wh *WorkflowHandler) isShuttingDown() bool { return atomic.LoadInt32(&wh.shuttingDown) != 0 } // Health is for health check func (wh *WorkflowHandler) Health(ctx context.Context) (*types.HealthStatus, error) { status := HealthStatus(atomic.LoadInt32(&wh.healthStatus)) msg := status.String() if status != HealthStatusOK { wh.GetLogger().Warn(fmt.Sprintf("Service status is: %v", msg)) } return &types.HealthStatus{ Ok: status == HealthStatusOK, Msg: msg, }, nil } // DiagnoseWorkflowExecution is to diagnose a workflow execution func (wh *WorkflowHandler) DiagnoseWorkflowExecution(ctx context.Context, request *types.DiagnoseWorkflowExecutionRequest) (*types.DiagnoseWorkflowExecutionResponse, error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if request == nil { return nil, validate.ErrRequestNotSet } if request.GetDomain() == "" { return nil, validate.ErrDomainNotSet } if request.GetWorkflowExecution().GetWorkflowID() == "" || request.GetWorkflowExecution().GetRunID() == "" { return nil, validate.ErrExecutionNotSet } wfExecution := request.GetWorkflowExecution() diagnosticWorkflowDomain := "cadence-system" diagnosticWorkflowID := fmt.Sprintf("%s-%s", request.GetDomain(), wfExecution.GetRunID()) diagnosticWorkflowInput := diagnostics.DiagnosticsStarterWorkflowInput{ Domain: request.GetDomain(), WorkflowID: request.GetWorkflowExecution().GetWorkflowID(), RunID: request.GetWorkflowExecution().GetRunID(), Identity: request.GetIdentity(), } inputInBytes, err := json.Marshal(diagnosticWorkflowInput) if err != nil { return nil, err } resp, err := wh.StartWorkflowExecution(ctx, &types.StartWorkflowExecutionRequest{ Domain: diagnosticWorkflowDomain, WorkflowID: diagnosticWorkflowID, WorkflowType: &types.WorkflowType{ Name: "diagnostics-starter-workflow", }, TaskList: &types.TaskList{ Name: "diagnostics-wf-tasklist", }, Input: inputInBytes, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(86400), // 24 hours TaskStartToCloseTimeoutSeconds: common.Int32Ptr(300), // 5 minutes Identity: request.GetIdentity(), RequestID: uuid.New().String(), WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), }) if err != nil { var wfExecutionStartedError *types.WorkflowExecutionAlreadyStartedError if errors.As(err, &wfExecutionStartedError) { return &types.DiagnoseWorkflowExecutionResponse{ Domain: diagnosticWorkflowDomain, DiagnosticWorkflowExecution: &types.WorkflowExecution{ WorkflowID: diagnosticWorkflowID, RunID: wfExecutionStartedError.RunID, }, }, nil } return nil, err } return &types.DiagnoseWorkflowExecutionResponse{ Domain: diagnosticWorkflowDomain, DiagnosticWorkflowExecution: &types.WorkflowExecution{ WorkflowID: diagnosticWorkflowID, RunID: resp.GetRunID(), }, }, nil } // PollForActivityTask - Poll for an activity task. func (wh *WorkflowHandler) PollForActivityTask( ctx context.Context, pollRequest *types.PollForActivityTaskRequest, ) (resp *types.PollForActivityTaskResponse, retError error) { callTime := time.Now() if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if pollRequest == nil { return nil, validate.ErrRequestNotSet } domainName := pollRequest.GetDomain() if domainName == "" { return nil, validate.ErrDomainNotSet } scope := getMetricsScopeWithDomain(metrics.FrontendPollForActivityTaskScope, pollRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) wh.GetLogger().Debug("Received PollForActivityTask") if err := common.ValidateLongPollContextTimeout( ctx, "PollForActivityTask", wh.GetThrottledLogger().WithTags(tag.WorkflowDomainName(domainName), tag.WorkflowTaskListName(pollRequest.GetTaskList().GetName())), ); err != nil { return nil, err } idLengthWarnLimit := wh.config.MaxIDLengthWarnLimit() if !common.IsValidIDLength( domainName, scope, idLengthWarnLimit, wh.config.DomainNameMaxLength(domainName), metrics.CadenceErrDomainNameExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeDomainName) { return nil, validate.ErrDomainTooLong } if err := wh.validateTaskList(pollRequest.TaskList, scope, domainName); err != nil { return nil, err } if !common.IsValidIDLength( pollRequest.GetIdentity(), scope, idLengthWarnLimit, wh.config.IdentityMaxLength(domainName), metrics.CadenceErrIdentityExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeIdentity) { return nil, validate.ErrIdentityTooLong } domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return nil, err } isolationGroup := wh.getIsolationGroup(ctx, domainName) if !wh.waitUntilIsolationGroupHealthy(ctx, domainName, isolationGroup) { return &types.PollForActivityTaskResponse{}, nil } // it is possible that we wait for a very long time and the remaining time is not long enough for a long poll // in this case, return an empty response if err := common.ValidateLongPollContextTimeout( ctx, "PollForActivityTask", wh.GetThrottledLogger().WithTags(tag.WorkflowDomainName(domainName), tag.WorkflowTaskListName(pollRequest.GetTaskList().GetName())), ); err != nil { return &types.PollForActivityTaskResponse{}, nil } pollerID := uuid.New().String() var matchingResp *types.MatchingPollForActivityTaskResponse op := func(ctx context.Context) error { matchingResp, err = wh.GetMatchingClient().PollForActivityTask(ctx, &types.MatchingPollForActivityTaskRequest{ DomainUUID: domainID, PollerID: pollerID, PollRequest: pollRequest, IsolationGroup: isolationGroup, }) return err } err = wh.throttleRetry.Do(ctx, op) if err != nil { err = wh.cancelOutstandingPoll(ctx, err, domainID, persistence.TaskListTypeActivity, pollRequest.TaskList, pollerID) if err != nil { // For all other errors log an error and return it back to client. ctxTimeout := "not-set" ctxDeadline, ok := ctx.Deadline() if ok { ctxTimeout = ctxDeadline.Sub(callTime).String() } peerHostname, origErr := cadenceerrors.ExtractPeerHostname(err) wh.GetLogger().Error("PollForActivityTask failed.", tag.WorkflowTaskListName(pollRequest.GetTaskList().GetName()), tag.Value(ctxTimeout), tag.Error(origErr), tag.PeerHostname(peerHostname)) return nil, err } // Must be cancellation error. Doesn't matter what we return here. Client already went away. return nil, nil } return &types.PollForActivityTaskResponse{ TaskToken: matchingResp.TaskToken, WorkflowExecution: matchingResp.WorkflowExecution, ActivityID: matchingResp.ActivityID, ActivityType: matchingResp.ActivityType, Input: matchingResp.Input, ScheduledTimestamp: matchingResp.ScheduledTimestamp, ScheduleToCloseTimeoutSeconds: matchingResp.ScheduleToCloseTimeoutSeconds, StartedTimestamp: matchingResp.StartedTimestamp, StartToCloseTimeoutSeconds: matchingResp.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: matchingResp.HeartbeatTimeoutSeconds, Attempt: matchingResp.Attempt, ScheduledTimestampOfThisAttempt: matchingResp.ScheduledTimestampOfThisAttempt, HeartbeatDetails: matchingResp.HeartbeatDetails, WorkflowType: matchingResp.WorkflowType, WorkflowDomain: matchingResp.WorkflowDomain, Header: matchingResp.Header, AutoConfigHint: matchingResp.AutoConfigHint, }, nil } // PollForDecisionTask - Poll for a decision task. func (wh *WorkflowHandler) PollForDecisionTask( ctx context.Context, pollRequest *types.PollForDecisionTaskRequest, ) (resp *types.PollForDecisionTaskResponse, retError error) { callTime := time.Now() if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if pollRequest == nil { return nil, validate.ErrRequestNotSet } domainName := pollRequest.GetDomain() tags := []tag.Tag{tag.WorkflowDomainName(domainName)} if domainName == "" { return nil, validate.ErrDomainNotSet } scope := getMetricsScopeWithDomain(metrics.FrontendPollForDecisionTaskScope, pollRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) wh.GetLogger().Debug("Received PollForDecisionTask") if err := common.ValidateLongPollContextTimeout( ctx, "PollForDecisionTask", wh.GetThrottledLogger().WithTags(tag.WorkflowDomainName(domainName), tag.WorkflowTaskListName(pollRequest.GetTaskList().GetName())), ); err != nil { return nil, err } idLengthWarnLimit := wh.config.MaxIDLengthWarnLimit() if !common.IsValidIDLength( domainName, scope, idLengthWarnLimit, wh.config.DomainNameMaxLength(domainName), metrics.CadenceErrDomainNameExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeDomainName) { return nil, validate.ErrDomainTooLong } if !common.IsValidIDLength( pollRequest.GetIdentity(), scope, idLengthWarnLimit, wh.config.IdentityMaxLength(domainName), metrics.CadenceErrIdentityExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeIdentity) { return nil, validate.ErrIdentityTooLong } if err := wh.validateTaskList(pollRequest.TaskList, scope, domainName); err != nil { return nil, err } domainEntry, err := wh.GetDomainCache().GetDomain(domainName) if err != nil { return nil, err } domainID := domainEntry.GetInfo().ID wh.GetLogger().Debug("Poll for decision.", tag.WorkflowDomainName(domainName), tag.WorkflowDomainID(domainID)) if err := wh.checkBadBinary(domainEntry, pollRequest.GetBinaryChecksum()); err != nil { return nil, err } isolationGroup := wh.getIsolationGroup(ctx, domainName) if !wh.waitUntilIsolationGroupHealthy(ctx, domainName, isolationGroup) { return &types.PollForDecisionTaskResponse{}, nil } // it is possible that we wait for a very long time and the remaining time is not long enough for a long poll // in this case, return an empty response if err := common.ValidateLongPollContextTimeout( ctx, "PollForDecisionTask", wh.GetThrottledLogger().WithTags(tag.WorkflowDomainName(domainName), tag.WorkflowTaskListName(pollRequest.GetTaskList().GetName())), ); err != nil { return &types.PollForDecisionTaskResponse{}, nil } pollerID := uuid.New().String() var matchingResp *types.MatchingPollForDecisionTaskResponse op := func(ctx context.Context) error { matchingResp, err = wh.GetMatchingClient().PollForDecisionTask(ctx, &types.MatchingPollForDecisionTaskRequest{ DomainUUID: domainID, PollerID: pollerID, PollRequest: pollRequest, IsolationGroup: isolationGroup, }) return err } err = wh.throttleRetry.Do(ctx, op) if err != nil { err = wh.cancelOutstandingPoll(ctx, err, domainID, persistence.TaskListTypeDecision, pollRequest.TaskList, pollerID) if err != nil { // For all other errors log an error and return it back to client. ctxTimeout := "not-set" ctxDeadline, ok := ctx.Deadline() if ok { ctxTimeout = ctxDeadline.Sub(callTime).String() } peerHostname, origErr := cadenceerrors.ExtractPeerHostname(err) wh.GetLogger().Error("PollForDecisionTask failed.", tag.WorkflowTaskListName(pollRequest.GetTaskList().GetName()), tag.Value(ctxTimeout), tag.Error(origErr), tag.PeerHostname(peerHostname)) return nil, err } // Must be cancellation error. Doesn't matter what we return here. Client already went away. return nil, nil } tags = append(tags, []tag.Tag{tag.WorkflowID( matchingResp.GetWorkflowExecution().GetWorkflowID()), tag.WorkflowRunID(matchingResp.GetWorkflowExecution().GetRunID())}...) resp, err = wh.createPollForDecisionTaskResponse(ctx, scope, domainID, matchingResp, matchingResp.GetBranchToken()) if err != nil { return nil, err } return resp, nil } func (wh *WorkflowHandler) getIsolationGroup(ctx context.Context, domainName string) string { return isolationgroup.IsolationGroupFromContext(ctx) } func (wh *WorkflowHandler) getPartitionConfig(ctx context.Context, domainName string) map[string]string { return isolationgroup.ConfigFromContext(ctx) } func (wh *WorkflowHandler) isIsolationGroupHealthy(ctx context.Context, domainName, isolationGroup string) bool { if wh.GetIsolationGroupState() != nil && wh.config.EnableTasklistIsolation(domainName) { isDrained, err := wh.GetIsolationGroupState().IsDrained(ctx, domainName, isolationGroup) if err != nil { wh.GetLogger().Error("Failed to check if an isolation group is drained, assume it's healthy", tag.Error(err)) return true } return !isDrained } return true } func (wh *WorkflowHandler) waitUntilIsolationGroupHealthy(ctx context.Context, domainName, isolationGroup string) bool { if wh.GetIsolationGroupState() != nil && wh.config.EnableTasklistIsolation(domainName) { ticker := time.NewTicker(time.Second * 30) defer ticker.Stop() childCtx, cancel := common.CreateChildContext(ctx, 0.05) defer cancel() for { isDrained, err := wh.GetIsolationGroupState().IsDrained(childCtx, domainName, isolationGroup) if err != nil { wh.GetLogger().Error("Failed to check if an isolation group is drained, assume it's healthy", tag.Error(err)) return true } if !isDrained { break } select { case <-childCtx.Done(): return false case <-ticker.C: } } } return true } func (wh *WorkflowHandler) checkBadBinary(domainEntry *cache.DomainCacheEntry, binaryChecksum string) error { if domainEntry.GetConfig().BadBinaries.Binaries != nil { badBinaries := domainEntry.GetConfig().BadBinaries.Binaries _, ok := badBinaries[binaryChecksum] if ok { wh.GetMetricsClient().IncCounter(metrics.FrontendPollForDecisionTaskScope, metrics.CadenceErrBadBinaryCounter) return &types.BadRequestError{ Message: fmt.Sprintf("binary %v already marked as bad deployment", binaryChecksum), } } } return nil } func (wh *WorkflowHandler) cancelOutstandingPoll(ctx context.Context, err error, domainID string, taskListType int32, taskList *types.TaskList, pollerID string) error { // First check if this err is due to context cancellation. This means client connection to frontend is closed. if ctx.Err() == context.Canceled { // Our rpc stack does not propagates context cancellation to the other service. Lets make an explicit // call to matching to notify this poller is gone to prevent any tasks being dispatched to zombie pollers. err = wh.GetMatchingClient().CancelOutstandingPoll(context.Background(), &types.CancelOutstandingPollRequest{ DomainUUID: domainID, TaskListType: common.Int32Ptr(taskListType), TaskList: taskList, PollerID: pollerID, }) // We can not do much if this call fails. Just log the error and move on if err != nil { wh.GetLogger().Warn("Failed to cancel outstanding poller.", tag.WorkflowTaskListName(taskList.GetName()), tag.Error(err)) } // Clear error as we don't want to report context cancellation error to count against our SLA return nil } return err } // RecordActivityTaskHeartbeat - Record Activity Task Heart beat. func (wh *WorkflowHandler) RecordActivityTaskHeartbeat( ctx context.Context, heartbeatRequest *types.RecordActivityTaskHeartbeatRequest, ) (resp *types.RecordActivityTaskHeartbeatResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if heartbeatRequest == nil { return nil, validate.ErrRequestNotSet } wh.GetLogger().Debug("Received RecordActivityTaskHeartbeat") if heartbeatRequest.TaskToken == nil { return nil, validate.ErrTaskTokenNotSet } taskToken, err := wh.tokenSerializer.Deserialize(heartbeatRequest.TaskToken) if err != nil { return nil, err } if taskToken.DomainID == "" { return nil, validate.ErrDomainNotSet } domainName, err := wh.GetDomainCache().GetDomainName(taskToken.DomainID) if err != nil { return nil, err } dw := domainWrapper{ domain: domainName, } scope := getMetricsScopeWithDomain(metrics.FrontendRecordActivityTaskHeartbeatScope, dw, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(heartbeatRequest.Details), sizeLimitWarn, sizeLimitError, taskToken.DomainID, domainName, taskToken.WorkflowID, taskToken.RunID, scope, wh.GetLogger(), tag.BlobSizeViolationOperation("RecordActivityTaskHeartbeat"), ); err != nil { // heartbeat details exceed size limit, we would fail the activity immediately with explicit error reason failRequest := &types.RespondActivityTaskFailedRequest{ TaskToken: heartbeatRequest.TaskToken, Reason: common.StringPtr(common.FailureReasonHeartbeatExceedsLimit), Details: heartbeatRequest.Details[0:sizeLimitError], Identity: heartbeatRequest.Identity, } err = wh.GetHistoryClient().RespondActivityTaskFailed(ctx, &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: taskToken.DomainID, FailedRequest: failRequest, }) if err != nil { return nil, wh.normalizeVersionedErrors(ctx, err) } resp = &types.RecordActivityTaskHeartbeatResponse{CancelRequested: true} } else { resp, err = wh.GetHistoryClient().RecordActivityTaskHeartbeat(ctx, &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: taskToken.DomainID, HeartbeatRequest: heartbeatRequest, }) if err != nil { return nil, wh.normalizeVersionedErrors(ctx, err) } } return resp, nil } // RecordActivityTaskHeartbeatByID - Record Activity Task Heart beat. func (wh *WorkflowHandler) RecordActivityTaskHeartbeatByID( ctx context.Context, heartbeatRequest *types.RecordActivityTaskHeartbeatByIDRequest, ) (resp *types.RecordActivityTaskHeartbeatResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if heartbeatRequest == nil { return nil, validate.ErrRequestNotSet } domainName := heartbeatRequest.GetDomain() if domainName == "" { return nil, validate.ErrDomainNotSet } wh.GetLogger().Debug("Received RecordActivityTaskHeartbeatByID") domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return nil, err } workflowID := heartbeatRequest.GetWorkflowID() runID := heartbeatRequest.GetRunID() // runID is optional so can be empty activityID := heartbeatRequest.GetActivityID() if domainID == "" { return nil, validate.ErrDomainNotSet } if workflowID == "" { return nil, validate.ErrWorkflowIDNotSet } if activityID == "" { return nil, validate.ErrActivityIDNotSet } taskToken := &common.TaskToken{ DomainID: domainID, RunID: runID, WorkflowID: workflowID, ScheduleID: constants.EmptyEventID, ActivityID: activityID, } token, err := wh.tokenSerializer.Serialize(taskToken) if err != nil { return nil, err } scope := getMetricsScopeWithDomain(metrics.FrontendRecordActivityTaskHeartbeatByIDScope, heartbeatRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(heartbeatRequest.Details), sizeLimitWarn, sizeLimitError, taskToken.DomainID, domainName, taskToken.WorkflowID, taskToken.RunID, scope, wh.GetLogger(), tag.BlobSizeViolationOperation("RecordActivityTaskHeartbeatByID"), ); err != nil { // heartbeat details exceed size limit, we would fail the activity immediately with explicit error reason failRequest := &types.RespondActivityTaskFailedRequest{ TaskToken: token, Reason: common.StringPtr(common.FailureReasonHeartbeatExceedsLimit), Details: heartbeatRequest.Details[0:sizeLimitError], Identity: heartbeatRequest.Identity, } err = wh.GetHistoryClient().RespondActivityTaskFailed(ctx, &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: taskToken.DomainID, FailedRequest: failRequest, }) if err != nil { return nil, wh.normalizeVersionedErrors(ctx, err) } resp = &types.RecordActivityTaskHeartbeatResponse{CancelRequested: true} } else { req := &types.RecordActivityTaskHeartbeatRequest{ TaskToken: token, Details: heartbeatRequest.Details, Identity: heartbeatRequest.Identity, } resp, err = wh.GetHistoryClient().RecordActivityTaskHeartbeat(ctx, &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: taskToken.DomainID, HeartbeatRequest: req, }) if err != nil { return nil, wh.normalizeVersionedErrors(ctx, err) } } return resp, nil } // RespondActivityTaskCompleted - response to an activity task func (wh *WorkflowHandler) RespondActivityTaskCompleted( ctx context.Context, completeRequest *types.RespondActivityTaskCompletedRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if completeRequest == nil { return validate.ErrRequestNotSet } if completeRequest.TaskToken == nil { return validate.ErrTaskTokenNotSet } taskToken, err := wh.tokenSerializer.Deserialize(completeRequest.TaskToken) if err != nil { return err } if taskToken.DomainID == "" { return validate.ErrDomainNotSet } domainName, err := wh.GetDomainCache().GetDomainName(taskToken.DomainID) if err != nil { return err } dw := domainWrapper{ domain: domainName, } scope := getMetricsScopeWithDomain(metrics.FrontendRespondActivityTaskCompletedScope, dw, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if !common.IsValidIDLength( completeRequest.GetIdentity(), scope, wh.config.MaxIDLengthWarnLimit(), wh.config.IdentityMaxLength(domainName), metrics.CadenceErrIdentityExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeIdentity) { return validate.ErrIdentityTooLong } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(completeRequest.Result), sizeLimitWarn, sizeLimitError, taskToken.DomainID, domainName, taskToken.WorkflowID, taskToken.RunID, scope, wh.GetLogger(), tag.BlobSizeViolationOperation("RespondActivityTaskCompleted"), ); err != nil { // result exceeds blob size limit, we would record it as failure failRequest := &types.RespondActivityTaskFailedRequest{ TaskToken: completeRequest.TaskToken, Reason: common.StringPtr(common.FailureReasonCompleteResultExceedsLimit), Details: completeRequest.Result[0:sizeLimitError], Identity: completeRequest.Identity, } err = wh.GetHistoryClient().RespondActivityTaskFailed(ctx, &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: taskToken.DomainID, FailedRequest: failRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } } else { err = wh.GetHistoryClient().RespondActivityTaskCompleted(ctx, &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: taskToken.DomainID, CompleteRequest: completeRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } } return nil } // RespondActivityTaskCompletedByID - response to an activity task func (wh *WorkflowHandler) RespondActivityTaskCompletedByID( ctx context.Context, completeRequest *types.RespondActivityTaskCompletedByIDRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if completeRequest == nil { return validate.ErrRequestNotSet } domainName := completeRequest.GetDomain() if domainName == "" { return validate.ErrDomainNotSet } domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return err } workflowID := completeRequest.GetWorkflowID() runID := completeRequest.GetRunID() // runID is optional so can be empty activityID := completeRequest.GetActivityID() if domainID == "" { return validate.ErrDomainNotSet } if workflowID == "" { return validate.ErrWorkflowIDNotSet } if activityID == "" { return validate.ErrActivityIDNotSet } scope := getMetricsScopeWithDomain(metrics.FrontendRespondActivityTaskCompletedByIDScope, completeRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if !common.IsValidIDLength( completeRequest.GetIdentity(), scope, wh.config.MaxIDLengthWarnLimit(), wh.config.IdentityMaxLength(domainName), metrics.CadenceErrIdentityExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeIdentity) { return validate.ErrIdentityTooLong } taskToken := &common.TaskToken{ DomainID: domainID, RunID: runID, WorkflowID: workflowID, ScheduleID: constants.EmptyEventID, ActivityID: activityID, } token, err := wh.tokenSerializer.Serialize(taskToken) if err != nil { return err } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(completeRequest.Result), sizeLimitWarn, sizeLimitError, taskToken.DomainID, domainName, taskToken.WorkflowID, taskToken.RunID, scope, wh.GetLogger(), tag.BlobSizeViolationOperation("RespondActivityTaskCompletedByID"), ); err != nil { // result exceeds blob size limit, we would record it as failure failRequest := &types.RespondActivityTaskFailedRequest{ TaskToken: token, Reason: common.StringPtr(common.FailureReasonCompleteResultExceedsLimit), Details: completeRequest.Result[0:sizeLimitError], Identity: completeRequest.Identity, } err = wh.GetHistoryClient().RespondActivityTaskFailed(ctx, &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: taskToken.DomainID, FailedRequest: failRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } } else { req := &types.RespondActivityTaskCompletedRequest{ TaskToken: token, Result: completeRequest.Result, Identity: completeRequest.Identity, } err = wh.GetHistoryClient().RespondActivityTaskCompleted(ctx, &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: taskToken.DomainID, CompleteRequest: req, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } } return nil } // RespondActivityTaskFailed - response to an activity task failure func (wh *WorkflowHandler) RespondActivityTaskFailed( ctx context.Context, failedRequest *types.RespondActivityTaskFailedRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if failedRequest == nil { return validate.ErrRequestNotSet } if failedRequest.TaskToken == nil { return validate.ErrTaskTokenNotSet } taskToken, err := wh.tokenSerializer.Deserialize(failedRequest.TaskToken) if err != nil { return err } if taskToken.DomainID == "" { return validate.ErrDomainNotSet } domainName, err := wh.GetDomainCache().GetDomainName(taskToken.DomainID) if err != nil { return err } dw := domainWrapper{ domain: domainName, } scope := getMetricsScopeWithDomain(metrics.FrontendRespondActivityTaskFailedScope, dw, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if !common.IsValidIDLength( failedRequest.GetIdentity(), scope, wh.config.MaxIDLengthWarnLimit(), wh.config.IdentityMaxLength(domainName), metrics.CadenceErrIdentityExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeIdentity) { return validate.ErrIdentityTooLong } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(failedRequest.Details), sizeLimitWarn, sizeLimitError, taskToken.DomainID, domainName, taskToken.WorkflowID, taskToken.RunID, scope, wh.GetLogger(), tag.BlobSizeViolationOperation("RespondActivityTaskFailed"), ); err != nil { // details exceeds blob size limit, we would truncate the details and put a specific error reason failedRequest.Reason = common.StringPtr(common.FailureReasonFailureDetailsExceedsLimit) failedRequest.Details = failedRequest.Details[0:sizeLimitError] } err = wh.GetHistoryClient().RespondActivityTaskFailed(ctx, &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: taskToken.DomainID, FailedRequest: failedRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } return nil } // RespondActivityTaskFailedByID - response to an activity task failure func (wh *WorkflowHandler) RespondActivityTaskFailedByID( ctx context.Context, failedRequest *types.RespondActivityTaskFailedByIDRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if failedRequest == nil { return validate.ErrRequestNotSet } domainName := failedRequest.GetDomain() if domainName == "" { return validate.ErrDomainNotSet } domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return err } workflowID := failedRequest.GetWorkflowID() runID := failedRequest.GetRunID() // runID is optional so can be empty activityID := failedRequest.GetActivityID() if domainID == "" { return validate.ErrDomainNotSet } if workflowID == "" { return validate.ErrWorkflowIDNotSet } if activityID == "" { return validate.ErrActivityIDNotSet } scope := getMetricsScopeWithDomain(metrics.FrontendRespondActivityTaskFailedByIDScope, failedRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if !common.IsValidIDLength( failedRequest.GetIdentity(), scope, wh.config.MaxIDLengthWarnLimit(), wh.config.IdentityMaxLength(failedRequest.GetDomain()), metrics.CadenceErrIdentityExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeIdentity) { return validate.ErrIdentityTooLong } taskToken := &common.TaskToken{ DomainID: domainID, RunID: runID, WorkflowID: workflowID, ScheduleID: constants.EmptyEventID, ActivityID: activityID, } token, err := wh.tokenSerializer.Serialize(taskToken) if err != nil { return err } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(failedRequest.Details), sizeLimitWarn, sizeLimitError, taskToken.DomainID, domainName, taskToken.WorkflowID, taskToken.RunID, scope, wh.GetLogger(), tag.BlobSizeViolationOperation("RespondActivityTaskFailedByID"), ); err != nil { // details exceeds blob size limit, we would truncate the details and put a specific error reason failedRequest.Reason = common.StringPtr(common.FailureReasonFailureDetailsExceedsLimit) failedRequest.Details = failedRequest.Details[0:sizeLimitError] } req := &types.RespondActivityTaskFailedRequest{ TaskToken: token, Reason: failedRequest.Reason, Details: failedRequest.Details, Identity: failedRequest.Identity, } err = wh.GetHistoryClient().RespondActivityTaskFailed(ctx, &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: taskToken.DomainID, FailedRequest: req, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } return nil } // RespondActivityTaskCanceled - called to cancel an activity task func (wh *WorkflowHandler) RespondActivityTaskCanceled( ctx context.Context, cancelRequest *types.RespondActivityTaskCanceledRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if cancelRequest == nil { return validate.ErrRequestNotSet } if cancelRequest.TaskToken == nil { return validate.ErrTaskTokenNotSet } taskToken, err := wh.tokenSerializer.Deserialize(cancelRequest.TaskToken) if err != nil { return err } if taskToken.DomainID == "" { return validate.ErrDomainNotSet } domainName, err := wh.GetDomainCache().GetDomainName(taskToken.DomainID) if err != nil { return err } dw := domainWrapper{ domain: domainName, } scope := getMetricsScopeWithDomain(metrics.FrontendRespondActivityTaskCanceledScope, dw, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if !common.IsValidIDLength( cancelRequest.GetIdentity(), scope, wh.config.MaxIDLengthWarnLimit(), wh.config.IdentityMaxLength(domainName), metrics.CadenceErrIdentityExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeIdentity) { return validate.ErrIdentityTooLong } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(cancelRequest.Details), sizeLimitWarn, sizeLimitError, taskToken.DomainID, domainName, taskToken.WorkflowID, taskToken.RunID, scope, wh.GetLogger(), tag.BlobSizeViolationOperation("RespondActivityTaskCanceled"), ); err != nil { // details exceeds blob size limit, we would record it as failure failRequest := &types.RespondActivityTaskFailedRequest{ TaskToken: cancelRequest.TaskToken, Reason: common.StringPtr(common.FailureReasonCancelDetailsExceedsLimit), Details: cancelRequest.Details[0:sizeLimitError], Identity: cancelRequest.Identity, } err = wh.GetHistoryClient().RespondActivityTaskFailed(ctx, &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: taskToken.DomainID, FailedRequest: failRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } } else { err = wh.GetHistoryClient().RespondActivityTaskCanceled(ctx, &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: taskToken.DomainID, CancelRequest: cancelRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } } return nil } // RespondActivityTaskCanceledByID - called to cancel an activity task func (wh *WorkflowHandler) RespondActivityTaskCanceledByID( ctx context.Context, cancelRequest *types.RespondActivityTaskCanceledByIDRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if cancelRequest == nil { return validate.ErrRequestNotSet } domainName := cancelRequest.GetDomain() if domainName == "" { return validate.ErrDomainNotSet } domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return err } workflowID := cancelRequest.GetWorkflowID() runID := cancelRequest.GetRunID() // runID is optional so can be empty activityID := cancelRequest.GetActivityID() if domainID == "" { return validate.ErrDomainNotSet } if workflowID == "" { return validate.ErrWorkflowIDNotSet } if activityID == "" { return validate.ErrActivityIDNotSet } scope := getMetricsScopeWithDomain(metrics.FrontendRespondActivityTaskCanceledByIDScope, cancelRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if !common.IsValidIDLength( cancelRequest.GetIdentity(), scope, wh.config.MaxIDLengthWarnLimit(), wh.config.IdentityMaxLength(cancelRequest.GetDomain()), metrics.CadenceErrIdentityExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeIdentity) { return validate.ErrIdentityTooLong } taskToken := &common.TaskToken{ DomainID: domainID, RunID: runID, WorkflowID: workflowID, ScheduleID: constants.EmptyEventID, ActivityID: activityID, } token, err := wh.tokenSerializer.Serialize(taskToken) if err != nil { return err } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(cancelRequest.Details), sizeLimitWarn, sizeLimitError, taskToken.DomainID, domainName, taskToken.WorkflowID, taskToken.RunID, scope, wh.GetLogger(), tag.BlobSizeViolationOperation("RespondActivityTaskCanceledByID"), ); err != nil { // details exceeds blob size limit, we would record it as failure failRequest := &types.RespondActivityTaskFailedRequest{ TaskToken: token, Reason: common.StringPtr(common.FailureReasonCancelDetailsExceedsLimit), Details: cancelRequest.Details[0:sizeLimitError], Identity: cancelRequest.Identity, } err = wh.GetHistoryClient().RespondActivityTaskFailed(ctx, &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: taskToken.DomainID, FailedRequest: failRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } } else { req := &types.RespondActivityTaskCanceledRequest{ TaskToken: token, Details: cancelRequest.Details, Identity: cancelRequest.Identity, } err = wh.GetHistoryClient().RespondActivityTaskCanceled(ctx, &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: taskToken.DomainID, CancelRequest: req, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } } return nil } // RespondDecisionTaskCompleted - response to a decision task func (wh *WorkflowHandler) RespondDecisionTaskCompleted( ctx context.Context, completeRequest *types.RespondDecisionTaskCompletedRequest, ) (resp *types.RespondDecisionTaskCompletedResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if completeRequest == nil { return nil, validate.ErrRequestNotSet } if completeRequest.TaskToken == nil { return nil, validate.ErrTaskTokenNotSet } taskToken, err := wh.tokenSerializer.Deserialize(completeRequest.TaskToken) if err != nil { return nil, err } if taskToken.DomainID == "" { return nil, validate.ErrDomainNotSet } domainName, err := wh.GetDomainCache().GetDomainName(taskToken.DomainID) if err != nil { return nil, err } dw := domainWrapper{ domain: domainName, } scope := getMetricsScopeWithDomain(metrics.FrontendRespondDecisionTaskCompletedScope, dw, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if !common.IsValidIDLength( completeRequest.GetIdentity(), scope, wh.config.MaxIDLengthWarnLimit(), wh.config.IdentityMaxLength(domainName), metrics.CadenceErrIdentityExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeIdentity) { return nil, validate.ErrIdentityTooLong } if err := common.CheckDecisionResultLimit( len(completeRequest.Decisions), wh.config.DecisionResultCountLimit(domainName), scope); err != nil { return nil, err } histResp, err := wh.GetHistoryClient().RespondDecisionTaskCompleted(ctx, &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: taskToken.DomainID, CompleteRequest: completeRequest}, ) if err != nil { return nil, wh.normalizeVersionedErrors(ctx, err) } completedResp := &types.RespondDecisionTaskCompletedResponse{} completedResp.ActivitiesToDispatchLocally = histResp.ActivitiesToDispatchLocally if completeRequest.GetReturnNewDecisionTask() && histResp != nil && histResp.StartedResponse != nil { taskToken := &common.TaskToken{ DomainID: taskToken.DomainID, WorkflowID: taskToken.WorkflowID, RunID: taskToken.RunID, ScheduleID: histResp.StartedResponse.GetScheduledEventID(), ScheduleAttempt: histResp.StartedResponse.GetAttempt(), } token, _ := wh.tokenSerializer.Serialize(taskToken) workflowExecution := &types.WorkflowExecution{ WorkflowID: taskToken.WorkflowID, RunID: taskToken.RunID, } matchingResp := common.CreateMatchingPollForDecisionTaskResponse(histResp.StartedResponse, workflowExecution, token) newDecisionTask, err := wh.createPollForDecisionTaskResponse(ctx, scope, taskToken.DomainID, matchingResp, matchingResp.GetBranchToken()) if err != nil { return nil, err } completedResp.DecisionTask = newDecisionTask } return completedResp, nil } // RespondDecisionTaskFailed - failed response to a decision task func (wh *WorkflowHandler) RespondDecisionTaskFailed( ctx context.Context, failedRequest *types.RespondDecisionTaskFailedRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if failedRequest == nil { return validate.ErrRequestNotSet } if failedRequest.TaskToken == nil { return validate.ErrTaskTokenNotSet } taskToken, err := wh.tokenSerializer.Deserialize(failedRequest.TaskToken) if err != nil { return err } if taskToken.DomainID == "" { return validate.ErrDomainNotSet } domainName, err := wh.GetDomainCache().GetDomainName(taskToken.DomainID) if err != nil { return err } dw := domainWrapper{ domain: domainName, } scope := getMetricsScopeWithDomain(metrics.FrontendRespondDecisionTaskFailedScope, dw, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if !common.IsValidIDLength( failedRequest.GetIdentity(), scope, wh.config.MaxIDLengthWarnLimit(), wh.config.IdentityMaxLength(domainName), metrics.CadenceErrIdentityExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeIdentity) { return validate.ErrIdentityTooLong } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(failedRequest.Details), sizeLimitWarn, sizeLimitError, taskToken.DomainID, domainName, taskToken.WorkflowID, taskToken.RunID, scope, wh.GetLogger(), tag.BlobSizeViolationOperation("RespondDecisionTaskFailed"), ); err != nil { // details exceed, we would just truncate the size for decision task failed as the details is not used anywhere by client code failedRequest.Details = failedRequest.Details[0:sizeLimitError] } err = wh.GetHistoryClient().RespondDecisionTaskFailed(ctx, &types.HistoryRespondDecisionTaskFailedRequest{ DomainUUID: taskToken.DomainID, FailedRequest: failedRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } return nil } // RespondQueryTaskCompleted - response to a query task func (wh *WorkflowHandler) RespondQueryTaskCompleted( ctx context.Context, completeRequest *types.RespondQueryTaskCompletedRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if completeRequest == nil { return validate.ErrRequestNotSet } if completeRequest.TaskToken == nil { return validate.ErrTaskTokenNotSet } queryTaskToken, err := wh.tokenSerializer.DeserializeQueryTaskToken(completeRequest.TaskToken) if err != nil { return err } if queryTaskToken.DomainID == "" || queryTaskToken.TaskList == "" || queryTaskToken.TaskID == "" { return validate.ErrInvalidTaskToken } domainName, err := wh.GetDomainCache().GetDomainName(queryTaskToken.DomainID) if err != nil { return err } dw := domainWrapper{ domain: domainName, } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) scope := getMetricsScopeWithDomain(metrics.FrontendRespondQueryTaskCompletedScope, dw, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if err := common.CheckEventBlobSizeLimit( len(completeRequest.GetQueryResult()), sizeLimitWarn, sizeLimitError, queryTaskToken.DomainID, domainName, queryTaskToken.WorkflowID, queryTaskToken.RunID, scope, wh.GetLogger(), tag.BlobSizeViolationOperation("RespondQueryTaskCompleted"), ); err != nil { completeRequest = &types.RespondQueryTaskCompletedRequest{ TaskToken: completeRequest.TaskToken, CompletedType: types.QueryTaskCompletedTypeFailed.Ptr(), QueryResult: nil, ErrorMessage: err.Error(), } } call := yarpc.CallFromContext(ctx) completeRequest.WorkerVersionInfo = &types.WorkerVersionInfo{ Impl: call.Header(common.ClientImplHeaderName), FeatureVersion: call.Header(common.FeatureVersionHeaderName), } matchingRequest := &types.MatchingRespondQueryTaskCompletedRequest{ DomainUUID: queryTaskToken.DomainID, TaskList: &types.TaskList{Name: queryTaskToken.TaskList}, TaskID: queryTaskToken.TaskID, CompletedRequest: completeRequest, } err = wh.GetMatchingClient().RespondQueryTaskCompleted(ctx, matchingRequest) if err != nil { return err } return nil } func (wh *WorkflowHandler) StartWorkflowExecutionAsync( ctx context.Context, startRequest *types.StartWorkflowExecutionAsyncRequest, ) (resp *types.StartWorkflowExecutionAsyncResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } scope := getMetricsScopeWithDomain(metrics.FrontendStartWorkflowExecutionAsyncScope, startRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) // validate request before pushing to queue err := wh.validateStartWorkflowExecutionRequest(ctx, startRequest.StartWorkflowExecutionRequest, scope) if err != nil { return nil, err } producer, err := wh.producerManager.GetProducerByDomain(startRequest.GetDomain()) if err != nil { return nil, err } // Serialize the message to be sent to the queue. // Avoid JSON because json encoding of requests excludes PII fields such as input. JSON encoded request are logged by acccess controlled api layer for audit purposes. payload, err := wh.thriftrwEncoder.Encode(thrift.FromStartWorkflowExecutionAsyncRequest(startRequest)) if err != nil { return nil, fmt.Errorf("failed to encode StartWorkflowExecutionAsyncRequest: %v", err) } scope.RecordTimer(metrics.AsyncRequestPayloadSize, time.Duration(len(payload))) // propagate the headers from the context to the message header := &shared.Header{ Fields: make(map[string][]byte), } for k, v := range yarpc.CallFromContext(ctx).OriginalHeaders() { header.Fields[k] = []byte(v) } messageType := sqlblobs.AsyncRequestTypeStartWorkflowExecutionAsyncRequest message := &sqlblobs.AsyncRequestMessage{ PartitionKey: common.StringPtr(startRequest.GetWorkflowID()), Type: &messageType, Header: header, Encoding: common.StringPtr(string(constants.EncodingTypeThriftRW)), Payload: payload, } err = producer.Publish(ctx, message) if err != nil { return nil, err } return &types.StartWorkflowExecutionAsyncResponse{}, nil } // StartWorkflowExecution - Creates a new workflow execution func (wh *WorkflowHandler) StartWorkflowExecution( ctx context.Context, startRequest *types.StartWorkflowExecutionRequest, ) (resp *types.StartWorkflowExecutionResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } scope := getMetricsScopeWithDomain(metrics.FrontendStartWorkflowExecutionScope, startRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) err := wh.validateStartWorkflowExecutionRequest(ctx, startRequest, scope) if err != nil { return nil, err } domainName := startRequest.GetDomain() domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return nil, err } historyRequest, err := common.CreateHistoryStartWorkflowRequest( domainID, startRequest, time.Now(), wh.getPartitionConfig(ctx, domainName)) if err != nil { return nil, err } // for debugging jitter workflow // will be removed later jitterStartSeconds := startRequest.GetJitterStartSeconds() if startRequest.GetDomain() == "cadence-canary" && jitterStartSeconds > 0 { wh.GetLogger().Debug("Start workflow execution request domainID", tag.WorkflowDomainID(domainID), tag.WorkflowID(startRequest.WorkflowID), tag.Dynamic("JitterStartSeconds", jitterStartSeconds), tag.Dynamic("firstDecisionTaskBackoffSeconds", historyRequest.GetFirstDecisionTaskBackoffSeconds()), ) } else { wh.GetLogger().Debug("Start workflow execution request domainID", tag.WorkflowDomainID(domainID)) } resp, err = wh.GetHistoryClient().StartWorkflowExecution(ctx, historyRequest) if err != nil { return nil, err } return resp, nil } func (wh *WorkflowHandler) validateStartWorkflowExecutionRequest(ctx context.Context, startRequest *types.StartWorkflowExecutionRequest, scope metrics.Scope) error { if startRequest == nil { return validate.ErrRequestNotSet } domainName := startRequest.GetDomain() if domainName == "" { return validate.ErrDomainNotSet } if startRequest.GetWorkflowID() == "" { return validate.ErrWorkflowIDNotSet } if _, err := uuid.Parse(startRequest.RequestID); err != nil { return &types.BadRequestError{Message: fmt.Sprintf("requestId %q is not a valid UUID", startRequest.RequestID)} } if startRequest.WorkflowType == nil || startRequest.WorkflowType.GetName() == "" { return validate.ErrWorkflowTypeNotSet } idLengthWarnLimit := wh.config.MaxIDLengthWarnLimit() if !common.IsValidIDLength( domainName, scope, idLengthWarnLimit, wh.config.DomainNameMaxLength(domainName), metrics.CadenceErrDomainNameExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeDomainName) { return validate.ErrDomainTooLong } if !common.IsValidIDLength( startRequest.GetWorkflowID(), scope, idLengthWarnLimit, wh.config.WorkflowIDMaxLength(domainName), metrics.CadenceErrWorkflowIDExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeWorkflowID) { return validate.ErrWorkflowIDTooLong } if err := common.ValidateRetryPolicy(startRequest.RetryPolicy); err != nil { return err } wh.GetLogger().Debug( "Received StartWorkflowExecution. WorkflowID", tag.WorkflowID(startRequest.GetWorkflowID())) if !common.IsValidIDLength( startRequest.WorkflowType.GetName(), scope, idLengthWarnLimit, wh.config.WorkflowTypeMaxLength(domainName), metrics.CadenceErrWorkflowTypeExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeWorkflowType) { return validate.ErrWorkflowTypeTooLong } if err := wh.validateTaskList(startRequest.TaskList, scope, domainName); err != nil { return err } if startRequest.GetExecutionStartToCloseTimeoutSeconds() <= 0 { return validate.ErrInvalidExecutionStartToCloseTimeoutSeconds } if startRequest.GetTaskStartToCloseTimeoutSeconds() <= 0 { return validate.ErrInvalidTaskStartToCloseTimeoutSeconds } if startRequest.GetDelayStartSeconds() < 0 { return validate.ErrInvalidDelayStartSeconds } if startRequest.GetJitterStartSeconds() < 0 { return validate.ErrInvalidJitterStartSeconds } jitter := startRequest.GetJitterStartSeconds() cron := startRequest.GetCronSchedule() if cron != "" { if _, err := backoff.ValidateSchedule(startRequest.GetCronSchedule()); err != nil { return err } } if jitter > 0 && cron != "" { // Calculate the cron duration and ensure that jitter is not greater than the cron duration, // because that would be confusing to users. // Request using start/end time zero value, which will get us an exact answer (i.e. its not in the // middle of a minute) backoffSeconds, err := backoff.GetBackoffForNextScheduleInSeconds(cron, time.Time{}, time.Time{}, jitter, startRequest.GetCronOverlapPolicy()) if err != nil { return err } if jitter > backoffSeconds { return validate.ErrInvalidJitterStartSeconds2 } } if !common.IsValidIDLength( startRequest.GetRequestID(), scope, idLengthWarnLimit, wh.config.RequestIDMaxLength(domainName), metrics.CadenceErrRequestIDExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeRequestID) { return validate.ErrRequestIDTooLong } if err := wh.searchAttributesValidator.ValidateSearchAttributes(startRequest.SearchAttributes, domainName); err != nil { return err } wh.GetLogger().Debug("Start workflow execution request domain", tag.WorkflowDomainName(domainName)) domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return err } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) actualSize := len(startRequest.Input) if startRequest.Memo != nil { actualSize += common.GetSizeOfMapStringToByteArray(startRequest.Memo.GetFields()) } if err := common.CheckEventBlobSizeLimit( actualSize, sizeLimitWarn, sizeLimitError, domainID, domainName, startRequest.GetWorkflowID(), "", scope, wh.GetLogger(), tag.BlobSizeViolationOperation("StartWorkflowExecution"), ); err != nil { return err } isolationGroup := wh.getIsolationGroup(ctx, domainName) if !wh.isIsolationGroupHealthy(ctx, domainName, isolationGroup) { return &types.BadRequestError{fmt.Sprintf("Domain %s is drained from isolation group %s.", domainName, isolationGroup)} } return nil } // GetWorkflowExecutionHistory - retrieves the history of workflow execution func (wh *WorkflowHandler) GetWorkflowExecutionHistory( ctx context.Context, getRequest *types.GetWorkflowExecutionHistoryRequest, ) (resp *types.GetWorkflowExecutionHistoryResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if getRequest == nil { return nil, validate.ErrRequestNotSet } domainName := getRequest.GetDomain() wfExecution := getRequest.GetWorkflowExecution() if domainName == "" { return nil, validate.ErrDomainNotSet } if err := validate.CheckExecution(wfExecution); err != nil { return nil, err } domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return nil, err } if getRequest.GetMaximumPageSize() <= 0 { getRequest.MaximumPageSize = int32(wh.config.HistoryMaxPageSize(getRequest.GetDomain())) } // force limit page size if exceed if getRequest.GetMaximumPageSize() > constants.GetHistoryMaxPageSize { wh.GetThrottledLogger().Warn("GetHistory page size is larger than threshold", tag.WorkflowID(getRequest.Execution.GetWorkflowID()), tag.WorkflowRunID(getRequest.Execution.GetRunID()), tag.WorkflowDomainID(domainID), tag.WorkflowSize(int64(getRequest.GetMaximumPageSize()))) getRequest.MaximumPageSize = constants.GetHistoryMaxPageSize } scope := getMetricsScopeWithDomain(metrics.FrontendGetWorkflowExecutionHistoryScope, getRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if !getRequest.GetSkipArchival() { enableArchivalRead := wh.GetArchivalMetadata().GetHistoryConfig().ReadEnabled() historyArchived := wh.historyArchived(ctx, getRequest, domainID) if enableArchivalRead && historyArchived { return wh.getArchivedHistory(ctx, getRequest, domainID) } } // this function return the following 8 things, // 1. branch token // 2. the workflow run ID // 3. the last first event ID (the event ID of the last batch of events in the history) // 4. the next event ID // 5. whether the workflow is closed // 6. The version history // 7. the workflow close status // 8. error if any queryHistory := func( domainUUID string, execution *types.WorkflowExecution, expectedNextEventID int64, currentBranchToken []byte, versionHistoryItem *persistence.VersionHistoryItem, ) ([]byte, string, int64, int64, bool, *types.VersionHistoryItem, string, error) { response, err := wh.GetHistoryClient().PollMutableState(ctx, &types.PollMutableStateRequest{ DomainUUID: domainUUID, Execution: execution, ExpectedNextEventID: expectedNextEventID, CurrentBranchToken: currentBranchToken, VersionHistoryItem: versionHistoryItem.ToInternalType(), }) if err != nil { return nil, "", 0, 0, false, nil, "", err } isWorkflowRunning := response.GetWorkflowCloseState() == persistence.WorkflowCloseStatusNone currentVersionHistory, err := persistence.NewVersionHistoriesFromInternalType(response.VersionHistories).GetCurrentVersionHistory() if err != nil { wh.GetLogger().Error("Failed to get current version history", tag.Dynamic("version-histories", response.VersionHistories)) return nil, "", 0, 0, false, nil, "", fmt.Errorf("failed to get the current version from the response from history: %w", err) } wfCloseStatus := "Running" if !isWorkflowRunning { wfCloseStatus = persistence.ToInternalWorkflowExecutionCloseStatus(int(response.GetWorkflowCloseState())).String() } lastVersionHistoryItem, err := currentVersionHistory.GetLastItem() if err != nil { return nil, "", 0, 0, false, nil, "", err } return response.CurrentBranchToken, response.Execution.GetRunID(), response.GetLastFirstEventID(), response.GetNextEventID(), isWorkflowRunning, lastVersionHistoryItem.ToInternalType(), wfCloseStatus, nil } isLongPoll := getRequest.GetWaitForNewEvent() isCloseEventOnly := getRequest.GetHistoryEventFilterType() == types.HistoryEventFilterTypeCloseEvent execution := getRequest.Execution token := &getHistoryContinuationToken{} var runID string lastFirstEventID := constants.FirstEventID var nextEventID int64 var isWorkflowRunning bool var workflowCloseStatus string var workflowCloseTime *time.Time // process the token for paging queryNextEventID := constants.EndEventID if getRequest.NextPageToken != nil { token, err = deserializeHistoryToken(getRequest.NextPageToken) if err != nil { return nil, validate.ErrInvalidNextPageToken } if execution.RunID != "" && execution.GetRunID() != token.RunID { return nil, validate.ErrNextPageTokenRunIDMismatch } execution.RunID = token.RunID // we need to update the current next event ID and whether workflow is running if len(token.PersistenceToken) == 0 && isLongPoll && token.IsWorkflowRunning { logger := wh.GetLogger().WithTags( tag.WorkflowDomainName(getRequest.GetDomain()), tag.WorkflowID(getRequest.Execution.GetWorkflowID()), tag.WorkflowRunID(getRequest.Execution.GetRunID()), ) // TODO: for now we only log the invalid timeout (this is done inside the helper function) in case // this change breaks existing customers. Once we are sure no one is calling this API with very short timeout // we can return the error. _ = common.ValidateLongPollContextTimeout(ctx, "GetWorkflowExecutionHistory", logger) if !isCloseEventOnly { queryNextEventID = token.NextEventID } vh := persistence.NewVersionHistoryItemFromInternalType(token.VersionHistoryItem) token.BranchToken, _, lastFirstEventID, nextEventID, isWorkflowRunning, token.VersionHistoryItem, workflowCloseStatus, err = queryHistory(domainID, execution, queryNextEventID, token.BranchToken, vh) if err != nil { return nil, err } token.FirstEventID = token.NextEventID token.NextEventID = nextEventID token.IsWorkflowRunning = isWorkflowRunning } } else { if !isCloseEventOnly { queryNextEventID = constants.FirstEventID } token.BranchToken, runID, lastFirstEventID, nextEventID, isWorkflowRunning, token.VersionHistoryItem, workflowCloseStatus, err = queryHistory(domainID, execution, queryNextEventID, nil, nil) if err != nil { return nil, err } execution.RunID = runID token.RunID = runID token.FirstEventID = constants.FirstEventID token.NextEventID = nextEventID token.IsWorkflowRunning = isWorkflowRunning token.PersistenceToken = nil } call := yarpc.CallFromContext(ctx) clientFeatureVersion := call.Header(common.FeatureVersionHeaderName) clientImpl := call.Header(common.ClientImplHeaderName) supportsRawHistoryQuery := wh.versionChecker.SupportsRawHistoryQuery(clientImpl, clientFeatureVersion) == nil isRawHistoryEnabled := wh.config.SendRawWorkflowHistory(domainName) && supportsRawHistoryQuery history := &types.History{} history.Events = []*types.HistoryEvent{} var historyBlob []*types.DataBlob // helper function to just getHistory getHistory := func(firstEventID, nextEventID int64, nextPageToken []byte) error { if isRawHistoryEnabled { historyBlob, token.PersistenceToken, err = wh.getRawHistory( ctx, scope, domainID, domainName, *execution, firstEventID, nextEventID, getRequest.GetMaximumPageSize(), nextPageToken, token.TransientDecision, token.BranchToken, ) } else { history, token.PersistenceToken, err = wh.getHistory( ctx, scope, domainID, domainName, *execution, firstEventID, nextEventID, getRequest.GetMaximumPageSize(), nextPageToken, token.TransientDecision, token.BranchToken, ) } if err != nil { return err } return nil } if isCloseEventOnly { if !isWorkflowRunning { if err := getHistory(lastFirstEventID, nextEventID, nil); err != nil { return nil, err } if isRawHistoryEnabled { // since getHistory func will not return empty history, so the below is safe historyBlob = historyBlob[len(historyBlob)-1:] } else { // since getHistory func will not return empty history, so the below is safe history.Events = history.Events[len(history.Events)-1:] } token = nil } else if isLongPoll { // set the persistence token to be nil so next time we will query history for updates token.PersistenceToken = nil } else { token = nil } } else { // return all events if token.FirstEventID >= token.NextEventID { // currently there is no new event history.Events = []*types.HistoryEvent{} if !isWorkflowRunning { token = nil } } else { if err := getHistory(token.FirstEventID, token.NextEventID, token.PersistenceToken); err != nil { return nil, err } // here, for long pull on history events, we need to intercept the paging token from cassandra // and do something clever if len(token.PersistenceToken) == 0 && (!token.IsWorkflowRunning || !isLongPoll) { // meaning, there is no more history to be returned token = nil } } } nextToken, err := serializeHistoryToken(token) if err != nil { return nil, err } // Extract close time from history events for closed workflows if !isWorkflowRunning && len(history.Events) > 0 { // Get the last event (close event) timestamp lastEvent := history.Events[len(history.Events)-1] if lastEvent.Timestamp != nil { t := time.Unix(0, *lastEvent.Timestamp) workflowCloseTime = &t } } wh.emitGetWorkflowExecutionHistoryMetrics(domainName, domainID, execution.GetWorkflowID(), workflowCloseStatus, workflowCloseTime) return &types.GetWorkflowExecutionHistoryResponse{ History: history, RawHistory: historyBlob, NextPageToken: nextToken, Archived: false, }, nil } // SignalWorkflowExecution is used to send a signal event to running workflow execution. This results in // WorkflowExecutionSignaled event recorded in the history and a decision task being created for the execution. func (wh *WorkflowHandler) SignalWorkflowExecution( ctx context.Context, signalRequest *types.SignalWorkflowExecutionRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if signalRequest == nil { return validate.ErrRequestNotSet } domainName := signalRequest.GetDomain() wfExecution := signalRequest.GetWorkflowExecution() if domainName == "" { return validate.ErrDomainNotSet } if err := validate.CheckExecution(wfExecution); err != nil { return err } scope := getMetricsScopeWithDomain(metrics.FrontendSignalWorkflowExecutionScope, signalRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) idLengthWarnLimit := wh.config.MaxIDLengthWarnLimit() if !common.IsValidIDLength( domainName, scope, idLengthWarnLimit, wh.config.DomainNameMaxLength(domainName), metrics.CadenceErrDomainNameExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeDomainName) { return validate.ErrDomainTooLong } if signalRequest.GetSignalName() == "" { return validate.ErrSignalNameNotSet } if !common.IsValidIDLength( signalRequest.GetSignalName(), scope, idLengthWarnLimit, wh.config.SignalNameMaxLength(domainName), metrics.CadenceErrSignalNameExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeSignalName) { return validate.ErrSignalNameTooLong } if !common.IsValidIDLength( signalRequest.GetRequestID(), scope, idLengthWarnLimit, wh.config.RequestIDMaxLength(domainName), metrics.CadenceErrRequestIDExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeRequestID) { return validate.ErrRequestIDTooLong } domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return err } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(signalRequest.Input), sizeLimitWarn, sizeLimitError, domainID, domainName, signalRequest.GetWorkflowExecution().GetWorkflowID(), signalRequest.GetWorkflowExecution().GetRunID(), scope, wh.GetLogger(), tag.BlobSizeViolationOperation("SignalWorkflowExecution"), ); err != nil { return err } isolationGroup := wh.getIsolationGroup(ctx, domainName) if !wh.isIsolationGroupHealthy(ctx, domainName, isolationGroup) { return &types.BadRequestError{fmt.Sprintf("Domain %s is drained from isolation group %s.", domainName, isolationGroup)} } err = wh.GetHistoryClient().SignalWorkflowExecution(ctx, &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: domainID, SignalRequest: signalRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } return nil } func (wh *WorkflowHandler) SignalWithStartWorkflowExecutionAsync( ctx context.Context, signalWithStartRequest *types.SignalWithStartWorkflowExecutionAsyncRequest, ) (resp *types.SignalWithStartWorkflowExecutionAsyncResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } scope := getMetricsScopeWithDomain(metrics.FrontendSignalWithStartWorkflowExecutionAsyncScope, signalWithStartRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) // validate request before pushing to queue err := wh.validateSignalWithStartWorkflowExecutionRequest(ctx, signalWithStartRequest.SignalWithStartWorkflowExecutionRequest, scope) if err != nil { return nil, err } producer, err := wh.producerManager.GetProducerByDomain(signalWithStartRequest.GetDomain()) if err != nil { return nil, err } // Serialize the message to be sent to the queue. // Avoid JSON because json encoding of requests excludes PII fields such as input. JSON encoded request are logged by acccess controlled api layer for audit purposes. payload, err := wh.thriftrwEncoder.Encode(thrift.FromSignalWithStartWorkflowExecutionAsyncRequest(signalWithStartRequest)) if err != nil { return nil, fmt.Errorf("failed to encode SignalWithStartWorkflowExecutionAsyncRequest: %v", err) } scope.RecordTimer(metrics.AsyncRequestPayloadSize, time.Duration(len(payload))) // propagate the headers from the context to the message header := &shared.Header{ Fields: map[string][]byte{}, } for k, v := range yarpc.CallFromContext(ctx).OriginalHeaders() { header.Fields[k] = []byte(v) } messageType := sqlblobs.AsyncRequestTypeSignalWithStartWorkflowExecutionAsyncRequest message := &sqlblobs.AsyncRequestMessage{ PartitionKey: common.StringPtr(signalWithStartRequest.GetWorkflowID()), Type: &messageType, Header: header, Encoding: common.StringPtr(string(constants.EncodingTypeThriftRW)), Payload: payload, } err = producer.Publish(ctx, message) if err != nil { return nil, err } return &types.SignalWithStartWorkflowExecutionAsyncResponse{}, nil } // SignalWithStartWorkflowExecution is used to ensure sending a signal event to a workflow execution. // If workflow is running, this results in WorkflowExecutionSignaled event recorded in the history // and a decision task being created for the execution. // If workflow is not running or not found, this results in WorkflowExecutionStarted and WorkflowExecutionSignaled // event recorded in history, and a decision task being created for the execution func (wh *WorkflowHandler) SignalWithStartWorkflowExecution( ctx context.Context, signalWithStartRequest *types.SignalWithStartWorkflowExecutionRequest, ) (resp *types.StartWorkflowExecutionResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } scope := getMetricsScopeWithDomain(metrics.FrontendSignalWithStartWorkflowExecutionScope, signalWithStartRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) err := wh.validateSignalWithStartWorkflowExecutionRequest(ctx, signalWithStartRequest, scope) if err != nil { return nil, err } domainName := signalWithStartRequest.GetDomain() domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return nil, err } resp, err = wh.GetHistoryClient().SignalWithStartWorkflowExecution(ctx, &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: signalWithStartRequest, PartitionConfig: wh.getPartitionConfig(ctx, domainName), }) if err != nil { return nil, err } return resp, nil } func (wh *WorkflowHandler) validateSignalWithStartWorkflowExecutionRequest(ctx context.Context, signalWithStartRequest *types.SignalWithStartWorkflowExecutionRequest, scope metrics.Scope) error { if signalWithStartRequest == nil { return validate.ErrRequestNotSet } domainName := signalWithStartRequest.GetDomain() if domainName == "" { return validate.ErrDomainNotSet } if signalWithStartRequest.GetWorkflowID() == "" { return validate.ErrWorkflowIDNotSet } idLengthWarnLimit := wh.config.MaxIDLengthWarnLimit() if !common.IsValidIDLength( domainName, scope, idLengthWarnLimit, wh.config.DomainNameMaxLength(domainName), metrics.CadenceErrDomainNameExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeDomainName) { return validate.ErrDomainTooLong } if !common.IsValidIDLength( signalWithStartRequest.GetWorkflowID(), scope, idLengthWarnLimit, wh.config.WorkflowIDMaxLength(domainName), metrics.CadenceErrWorkflowIDExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeWorkflowID) { return validate.ErrWorkflowIDTooLong } if signalWithStartRequest.GetSignalName() == "" { return validate.ErrSignalNameNotSet } if !common.IsValidIDLength( signalWithStartRequest.GetSignalName(), scope, idLengthWarnLimit, wh.config.SignalNameMaxLength(domainName), metrics.CadenceErrSignalNameExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeSignalName) { return validate.ErrSignalNameTooLong } if signalWithStartRequest.WorkflowType == nil || signalWithStartRequest.WorkflowType.GetName() == "" { return validate.ErrWorkflowTypeNotSet } if !common.IsValidIDLength( signalWithStartRequest.WorkflowType.GetName(), scope, idLengthWarnLimit, wh.config.WorkflowTypeMaxLength(domainName), metrics.CadenceErrWorkflowTypeExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeWorkflowType) { return validate.ErrWorkflowTypeTooLong } if err := wh.validateTaskList(signalWithStartRequest.TaskList, scope, domainName); err != nil { return err } if !common.IsValidIDLength( signalWithStartRequest.GetRequestID(), scope, idLengthWarnLimit, wh.config.RequestIDMaxLength(domainName), metrics.CadenceErrRequestIDExceededWarnLimit, domainName, wh.GetLogger(), tag.IDTypeRequestID) { return validate.ErrRequestIDTooLong } if signalWithStartRequest.GetExecutionStartToCloseTimeoutSeconds() <= 0 { return validate.ErrInvalidExecutionStartToCloseTimeoutSeconds } if signalWithStartRequest.GetTaskStartToCloseTimeoutSeconds() <= 0 { return validate.ErrInvalidTaskStartToCloseTimeoutSeconds } if err := common.ValidateRetryPolicy(signalWithStartRequest.RetryPolicy); err != nil { return err } if signalWithStartRequest.GetCronSchedule() != "" { if _, err := backoff.ValidateSchedule(signalWithStartRequest.GetCronSchedule()); err != nil { return err } } if err := wh.searchAttributesValidator.ValidateSearchAttributes(signalWithStartRequest.SearchAttributes, domainName); err != nil { return err } domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return err } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) if err := common.CheckEventBlobSizeLimit( len(signalWithStartRequest.SignalInput), sizeLimitWarn, sizeLimitError, domainID, domainName, signalWithStartRequest.GetWorkflowID(), "", scope, wh.GetLogger(), tag.BlobSizeViolationOperation("SignalWithStartWorkflowExecution"), ); err != nil { return err } actualSize := len(signalWithStartRequest.Input) + common.GetSizeOfMapStringToByteArray(signalWithStartRequest.Memo.GetFields()) if err := common.CheckEventBlobSizeLimit( actualSize, sizeLimitWarn, sizeLimitError, domainID, domainName, signalWithStartRequest.GetWorkflowID(), "", scope, wh.GetLogger(), tag.BlobSizeViolationOperation("SignalWithStartWorkflowExecution"), ); err != nil { return err } isolationGroup := wh.getIsolationGroup(ctx, domainName) if !wh.isIsolationGroupHealthy(ctx, domainName, isolationGroup) { return &types.BadRequestError{fmt.Sprintf("Domain %s is drained from isolation group %s.", domainName, isolationGroup)} } return nil } // TerminateWorkflowExecution terminates an existing workflow execution by recording WorkflowExecutionTerminated event // in the history and immediately terminating the execution instance. func (wh *WorkflowHandler) TerminateWorkflowExecution( ctx context.Context, terminateRequest *types.TerminateWorkflowExecutionRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if terminateRequest == nil { return validate.ErrRequestNotSet } domainName := terminateRequest.GetDomain() wfExecution := terminateRequest.GetWorkflowExecution() if terminateRequest.GetDomain() == "" { return validate.ErrDomainNotSet } if err := validate.CheckExecution(wfExecution); err != nil { return err } domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return err } err = wh.GetHistoryClient().TerminateWorkflowExecution(ctx, &types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: domainID, TerminateRequest: terminateRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } return nil } // ResetWorkflowExecution reset an existing workflow execution to the nextFirstEventID // in the history and immediately terminating the current execution instance. func (wh *WorkflowHandler) ResetWorkflowExecution( ctx context.Context, resetRequest *types.ResetWorkflowExecutionRequest, ) (resp *types.ResetWorkflowExecutionResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if resetRequest == nil { return nil, validate.ErrRequestNotSet } domainName := resetRequest.GetDomain() wfExecution := resetRequest.GetWorkflowExecution() if domainName == "" { return nil, validate.ErrDomainNotSet } if err := validate.CheckExecution(wfExecution); err != nil { return nil, err } domainID, err := wh.GetDomainCache().GetDomainID(resetRequest.GetDomain()) if err != nil { return nil, err } resp, err = wh.GetHistoryClient().ResetWorkflowExecution(ctx, &types.HistoryResetWorkflowExecutionRequest{ DomainUUID: domainID, ResetRequest: resetRequest, }) if err != nil { return nil, err } return resp, nil } // RequestCancelWorkflowExecution - requests to cancel a workflow execution func (wh *WorkflowHandler) RequestCancelWorkflowExecution( ctx context.Context, cancelRequest *types.RequestCancelWorkflowExecutionRequest, ) (retError error) { if wh.isShuttingDown() { return validate.ErrShuttingDown } if cancelRequest == nil { return validate.ErrRequestNotSet } domainName := cancelRequest.GetDomain() wfExecution := cancelRequest.GetWorkflowExecution() if domainName == "" { return validate.ErrDomainNotSet } if err := validate.CheckExecution(wfExecution); err != nil { return err } domainID, err := wh.GetDomainCache().GetDomainID(cancelRequest.GetDomain()) if err != nil { return err } err = wh.GetHistoryClient().RequestCancelWorkflowExecution(ctx, &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: domainID, CancelRequest: cancelRequest, }) if err != nil { return wh.normalizeVersionedErrors(ctx, err) } return nil } // RestartWorkflowExecution - retrieves info for an existing workflow then restarts it func (wh *WorkflowHandler) RestartWorkflowExecution(ctx context.Context, request *types.RestartWorkflowExecutionRequest) (resp *types.RestartWorkflowExecutionResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if request == nil { return nil, validate.ErrRequestNotSet } domainName := request.GetDomain() wfExecution := request.GetWorkflowExecution() if request.GetDomain() == "" { return nil, validate.ErrDomainNotSet } if err := validate.CheckExecution(wfExecution); err != nil { return nil, err } domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return nil, err } isolationGroup := wh.getIsolationGroup(ctx, domainName) if !wh.isIsolationGroupHealthy(ctx, domainName, isolationGroup) { return nil, &types.BadRequestError{ Message: fmt.Sprintf("Domain %s is drained from isolation group %s.", domainName, isolationGroup)} } history, err := wh.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: domainName, Execution: &types.WorkflowExecution{ WorkflowID: wfExecution.WorkflowID, RunID: wfExecution.RunID, }, SkipArchival: true, }) if err != nil { return nil, validate.ErrHistoryNotFound } startRequest := constructRestartWorkflowRequest(history.History.Events[0].WorkflowExecutionStartedEventAttributes, domainName, request.Identity, wfExecution.WorkflowID) req, err := common.CreateHistoryStartWorkflowRequest(domainID, startRequest, time.Now(), wh.getPartitionConfig(ctx, domainName)) if err != nil { return nil, err } startResp, err := wh.GetHistoryClient().StartWorkflowExecution(ctx, req) if err != nil { return nil, wh.normalizeVersionedErrors(ctx, err) } resp = &types.RestartWorkflowExecutionResponse{ RunID: startResp.RunID, } return resp, nil } // GetSearchAttributes return valid indexed keys func (wh *WorkflowHandler) GetSearchAttributes(ctx context.Context) (resp *types.GetSearchAttributesResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } keys := wh.config.ValidSearchAttributes() resp = &types.GetSearchAttributesResponse{ Keys: wh.convertIndexedKeyToThrift(keys), } return resp, nil } // QueryWorkflow returns query result for a specified workflow execution func (wh *WorkflowHandler) QueryWorkflow( ctx context.Context, queryRequest *types.QueryWorkflowRequest, ) (resp *types.QueryWorkflowResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if queryRequest == nil { return nil, validate.ErrRequestNotSet } domainName := queryRequest.GetDomain() wfExecution := queryRequest.GetExecution() if domainName == "" { return nil, validate.ErrDomainNotSet } if err := validate.CheckExecution(wfExecution); err != nil { return nil, err } if wh.config.DisallowQuery(domainName) { return nil, validate.ErrQueryDisallowedForDomain } if queryRequest.Query == nil { return nil, validate.ErrQueryNotSet } if queryRequest.Query.GetQueryType() == "" { return nil, validate.ErrQueryTypeNotSet } domainID, err := wh.GetDomainCache().GetDomainID(domainName) if err != nil { return nil, err } sizeLimitError := wh.config.BlobSizeLimitError(domainName) sizeLimitWarn := wh.config.BlobSizeLimitWarn(domainName) scope := getMetricsScopeWithDomain(metrics.FrontendQueryWorkflowScope, queryRequest, wh.GetMetricsClient()).Tagged(metrics.GetContextTags(ctx)...) if err := common.CheckEventBlobSizeLimit( len(queryRequest.GetQuery().GetQueryArgs()), sizeLimitWarn, sizeLimitError, domainID, domainName, queryRequest.GetExecution().GetWorkflowID(), queryRequest.GetExecution().GetRunID(), scope, wh.GetLogger(), tag.BlobSizeViolationOperation("QueryWorkflow")); err != nil { return nil, err } req := &types.HistoryQueryWorkflowRequest{ DomainUUID: domainID, Request: queryRequest, } hResponse, err := wh.GetHistoryClient().QueryWorkflow(ctx, req) if err != nil { return nil, err } return hResponse.GetResponse(), nil } // DescribeWorkflowExecution returns information about the specified workflow execution. func (wh *WorkflowHandler) DescribeWorkflowExecution( ctx context.Context, request *types.DescribeWorkflowExecutionRequest, ) (resp *types.DescribeWorkflowExecutionResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if request == nil { return nil, validate.ErrRequestNotSet } domainName := request.GetDomain() wfExecution := request.GetExecution() if domainName == "" { return nil, validate.ErrDomainNotSet } if err := validate.CheckExecution(wfExecution); err != nil { return nil, err } domainID, err := wh.GetDomainCache().GetDomainID(request.GetDomain()) if err != nil { return nil, err } response, err := wh.GetHistoryClient().DescribeWorkflowExecution(ctx, &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: domainID, Request: request, }) wh.emitDescribeWorkflowExecutionMetrics(domainName, response, err) if err != nil { return nil, err } return response, nil } func (wh *WorkflowHandler) getRawHistory( ctx context.Context, scope metrics.Scope, domainID string, domainName string, execution types.WorkflowExecution, firstEventID int64, nextEventID int64, pageSize int32, nextPageToken []byte, transientDecision *types.TransientDecisionInfo, branchToken []byte, ) ([]*types.DataBlob, []byte, error) { rawHistory := []*types.DataBlob{} shardID := common.WorkflowIDToHistoryShard(execution.WorkflowID, wh.config.NumHistoryShards) resp, err := wh.GetHistoryManager().ReadRawHistoryBranch(ctx, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: int(pageSize), NextPageToken: nextPageToken, ShardID: common.IntPtr(shardID), DomainName: domainName, }) if err != nil { return nil, nil, err } var encoding *types.EncodingType for _, data := range resp.HistoryEventBlobs { switch data.Encoding { case constants.EncodingTypeJSON: encoding = types.EncodingTypeJSON.Ptr() case constants.EncodingTypeThriftRW: encoding = types.EncodingTypeThriftRW.Ptr() default: panic(fmt.Sprintf("Invalid encoding type for raw history, encoding type: %s", data.Encoding)) } rawHistory = append(rawHistory, &types.DataBlob{ EncodingType: encoding, Data: data.Data, }) } if len(resp.NextPageToken) == 0 && transientDecision != nil { if err := wh.validateTransientDecisionEvents(nextEventID, transientDecision); err != nil { scope.IncCounter(metrics.CadenceErrIncompleteHistoryCounter) wh.GetLogger().Error("getHistory error", tag.WorkflowDomainID(domainID), tag.WorkflowID(execution.GetWorkflowID()), tag.WorkflowRunID(execution.GetRunID()), tag.Error(err)) } blob, err := wh.GetPayloadSerializer().SerializeBatchEvents( []*types.HistoryEvent{transientDecision.ScheduledEvent, transientDecision.StartedEvent}, constants.EncodingTypeThriftRW) if err != nil { return nil, nil, err } rawHistory = append(rawHistory, &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: blob.Data, }) } return rawHistory, resp.NextPageToken, nil } func (wh *WorkflowHandler) getHistory( ctx context.Context, scope metrics.Scope, domainID string, domainName string, execution types.WorkflowExecution, firstEventID, nextEventID int64, pageSize int32, nextPageToken []byte, transientDecision *types.TransientDecisionInfo, branchToken []byte, ) (*types.History, []byte, error) { var size int isFirstPage := len(nextPageToken) == 0 shardID := common.WorkflowIDToHistoryShard(execution.WorkflowID, wh.config.NumHistoryShards) var err error historyEvents, size, nextPageToken, err := persistenceutils.ReadFullPageV2Events(ctx, wh.GetHistoryManager(), &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: int(pageSize), NextPageToken: nextPageToken, ShardID: common.IntPtr(shardID), DomainName: domainName, }) if err != nil { return nil, nil, err } scope.RecordTimer(metrics.HistorySize, time.Duration(size)) isLastPage := len(nextPageToken) == 0 if err := verifyHistoryIsComplete( historyEvents, firstEventID, nextEventID-1, isFirstPage, isLastPage, int(pageSize)); err != nil { scope.IncCounter(metrics.CadenceErrIncompleteHistoryCounter) wh.GetLogger().Error("getHistory: incomplete history", tag.WorkflowDomainID(domainID), tag.WorkflowID(execution.GetWorkflowID()), tag.WorkflowRunID(execution.GetRunID()), tag.Error(err)) return nil, nil, err } if len(nextPageToken) == 0 && transientDecision != nil { if err := wh.validateTransientDecisionEvents(nextEventID, transientDecision); err != nil { scope.IncCounter(metrics.CadenceErrIncompleteHistoryCounter) wh.GetLogger().Error("getHistory error", tag.WorkflowDomainID(domainID), tag.WorkflowID(execution.GetWorkflowID()), tag.WorkflowRunID(execution.GetRunID()), tag.Error(err)) } // Append the transient decision events once we are done enumerating everything from the events table historyEvents = append(historyEvents, transientDecision.ScheduledEvent, transientDecision.StartedEvent) } executionHistory := &types.History{} executionHistory.Events = historyEvents return executionHistory, nextPageToken, nil } func (wh *WorkflowHandler) validateTransientDecisionEvents( expectedNextEventID int64, decision *types.TransientDecisionInfo, ) error { if decision.ScheduledEvent.ID == expectedNextEventID && decision.StartedEvent.ID == expectedNextEventID+1 { return nil } return fmt.Errorf( "invalid transient decision: "+ "expectedScheduledEventID=%v expectedStartedEventID=%v but have scheduledEventID=%v startedEventID=%v", expectedNextEventID, expectedNextEventID+1, decision.ScheduledEvent.ID, decision.StartedEvent.ID) } func (wh *WorkflowHandler) validateTaskList(t *types.TaskList, scope metrics.Scope, domain string) error { if t == nil || t.GetName() == "" { return validate.ErrTaskListNotSet } if !common.IsValidIDLength( t.GetName(), scope, wh.config.MaxIDLengthWarnLimit(), wh.config.TaskListNameMaxLength(domain), metrics.CadenceErrTaskListNameExceededWarnLimit, domain, wh.GetLogger(), tag.IDTypeTaskListName) { return validate.ErrTaskListTooLong } return nil } func (wh *WorkflowHandler) createPollForDecisionTaskResponse( ctx context.Context, scope metrics.Scope, domainID string, matchingResp *types.MatchingPollForDecisionTaskResponse, branchToken []byte, ) (*types.PollForDecisionTaskResponse, error) { if matchingResp.WorkflowExecution == nil { // this will happen if there is no decision task to be send to worker / caller return &types.PollForDecisionTaskResponse{ AutoConfigHint: matchingResp.AutoConfigHint, }, nil } var history *types.History var continuation []byte var err error if matchingResp.GetStickyExecutionEnabled() && matchingResp.Query != nil { // meaning sticky query, we should not return any events to worker // since query task only check the current status history = &types.History{ Events: []*types.HistoryEvent{}, } } else { // here we have 3 cases: // 1. sticky && non query task // 2. non sticky && non query task // 3. non sticky && query task // for 1, partial history have to be send back // for 2 and 3, full history have to be send back var persistenceToken []byte firstEventID := constants.FirstEventID nextEventID := matchingResp.GetNextEventID() if matchingResp.GetStickyExecutionEnabled() { firstEventID = matchingResp.GetPreviousStartedEventID() + 1 } domainName, dErr := wh.GetDomainCache().GetDomainName(domainID) if dErr != nil { return nil, dErr } scope = scope.Tagged(metrics.DomainTag(domainName)) history, persistenceToken, err = wh.getHistory( ctx, scope, domainID, domainName, *matchingResp.WorkflowExecution, firstEventID, nextEventID, int32(wh.config.HistoryMaxPageSize(domainName)), nil, matchingResp.DecisionInfo, branchToken, ) if err != nil { return nil, err } if len(persistenceToken) != 0 { continuation, err = serializeHistoryToken(&getHistoryContinuationToken{ RunID: matchingResp.WorkflowExecution.GetRunID(), FirstEventID: firstEventID, NextEventID: nextEventID, PersistenceToken: persistenceToken, TransientDecision: matchingResp.DecisionInfo, BranchToken: branchToken, }) if err != nil { return nil, err } } } resp := &types.PollForDecisionTaskResponse{ TaskToken: matchingResp.TaskToken, WorkflowExecution: matchingResp.WorkflowExecution, WorkflowType: matchingResp.WorkflowType, PreviousStartedEventID: matchingResp.PreviousStartedEventID, StartedEventID: matchingResp.StartedEventID, // this field is not set for query tasks as there's no decision task started event Query: matchingResp.Query, BacklogCountHint: matchingResp.BacklogCountHint, Attempt: matchingResp.Attempt, History: history, NextPageToken: continuation, WorkflowExecutionTaskList: matchingResp.WorkflowExecutionTaskList, ScheduledTimestamp: matchingResp.ScheduledTimestamp, StartedTimestamp: matchingResp.StartedTimestamp, Queries: matchingResp.Queries, NextEventID: matchingResp.NextEventID, TotalHistoryBytes: matchingResp.TotalHistoryBytes, AutoConfigHint: matchingResp.AutoConfigHint, } return resp, nil } func verifyHistoryIsComplete( events []*types.HistoryEvent, expectedFirstEventID int64, expectedLastEventID int64, isFirstPage bool, isLastPage bool, pageSize int, ) error { nEvents := len(events) if nEvents == 0 { if isLastPage { // we seem to be returning a non-nil pageToken on the lastPage which // in turn cases the client to call getHistory again - only to find // there are no more events to consume - bail out if this is the case here return nil } return fmt.Errorf("invalid history: contains zero events") } firstEventID := events[0].ID lastEventID := events[nEvents-1].ID if !isFirstPage { // atleast one page of history has been read previously if firstEventID <= expectedFirstEventID { // not first page and no events have been read in the previous pages - not possible return &types.InternalServiceError{ Message: fmt.Sprintf( "invalid history: expected first eventID to be > %v but got %v", expectedFirstEventID, firstEventID), } } expectedFirstEventID = firstEventID } if !isLastPage { // estimate lastEventID based on pageSize. This is a lower bound // since the persistence layer counts "batch of events" as a single page expectedLastEventID = expectedFirstEventID + int64(pageSize) - 1 } nExpectedEvents := expectedLastEventID - expectedFirstEventID + 1 if firstEventID == expectedFirstEventID && ((isLastPage && lastEventID == expectedLastEventID && int64(nEvents) == nExpectedEvents) || (!isLastPage && lastEventID >= expectedLastEventID && int64(nEvents) >= nExpectedEvents)) { return nil } return &types.InternalServiceError{ Message: fmt.Sprintf( "incomplete history: "+ "expected events [%v-%v] but got events [%v-%v] of length %v:"+ "isFirstPage=%v,isLastPage=%v,pageSize=%v", expectedFirstEventID, expectedLastEventID, firstEventID, lastEventID, nEvents, isFirstPage, isLastPage, pageSize), } } func deserializeHistoryToken(bytes []byte) (*getHistoryContinuationToken, error) { token := &getHistoryContinuationToken{} err := json.Unmarshal(bytes, token) return token, err } func serializeHistoryToken(token *getHistoryContinuationToken) ([]byte, error) { if token == nil { return nil, nil } bytes, err := json.Marshal(token) return bytes, err } func isFailoverRequest(updateRequest *types.UpdateDomainRequest) bool { return updateRequest.ActiveClusterName != nil || updateRequest.ActiveClusters != nil } func isGraceFailoverRequest(updateRequest *types.UpdateDomainRequest) bool { return updateRequest.FailoverTimeoutInSeconds != nil } func (wh *WorkflowHandler) checkOngoingFailover( ctx context.Context, domainName *string, ) error { enabledClusters := wh.GetClusterMetadata().GetEnabledClusterInfo() respChan := make(chan *types.DescribeDomainResponse, len(enabledClusters)) g := &errgroup.Group{} for clusterName := range enabledClusters { frontendClient, err := wh.GetRemoteFrontendClient(clusterName) if err != nil { return &types.BadRequestError{Message: err.Error()} } g.Go(func() (e error) { defer func() { log.CapturePanic(recover(), wh.GetLogger(), &e) }() resp, _ := frontendClient.DescribeDomain(ctx, &types.DescribeDomainRequest{Name: domainName}) respChan <- resp return nil }) } g.Wait() close(respChan) var failoverVersion *int64 for resp := range respChan { if resp == nil { return &types.InternalServiceError{ Message: "Failed to verify failover version from all clusters", } } if failoverVersion == nil { failoverVersion = &resp.FailoverVersion } if *failoverVersion != resp.GetFailoverVersion() { return &types.BadRequestError{ Message: "Concurrent failover is not allow.", } } } return nil } func (wh *WorkflowHandler) historyArchived(ctx context.Context, request *types.GetWorkflowExecutionHistoryRequest, domainID string) bool { if request.GetWorkflowExecution() == nil || request.GetWorkflowExecution().GetRunID() == "" { return false } getMutableStateRequest := &types.GetMutableStateRequest{ DomainUUID: domainID, Execution: request.Execution, } _, err := wh.GetHistoryClient().GetMutableState(ctx, getMutableStateRequest) if err == nil { return false } switch err.(type) { case *types.EntityNotExistsError: // the only case in which history is assumed to be archived is if getting mutable state returns entity not found error return true } return false } func (wh *WorkflowHandler) getArchivedHistory( ctx context.Context, request *types.GetWorkflowExecutionHistoryRequest, domainID string, ) (*types.GetWorkflowExecutionHistoryResponse, error) { entry, err := wh.GetDomainCache().GetDomainByID(domainID) if err != nil { return nil, err } URIString := entry.GetConfig().HistoryArchivalURI if URIString == "" { // if URI is empty, it means the domain has never enabled for archival. // the error is not "workflow has passed retention period", because // we have no way to tell if the requested workflow exists or not. return nil, validate.ErrHistoryNotFound } URI, err := archiver.NewURI(URIString) if err != nil { return nil, err } historyArchiver, err := wh.GetArchiverProvider().GetHistoryArchiver(URI.Scheme(), service.Frontend) if err != nil { return nil, err } resp, err := historyArchiver.Get(ctx, URI, &archiver.GetHistoryRequest{ DomainID: domainID, WorkflowID: request.GetWorkflowExecution().GetWorkflowID(), RunID: request.GetWorkflowExecution().GetRunID(), NextPageToken: request.GetNextPageToken(), PageSize: int(request.GetMaximumPageSize()), }) if err != nil { return nil, err } history := &types.History{} for _, batch := range resp.HistoryBatches { history.Events = append(history.Events, batch.Events...) } return &types.GetWorkflowExecutionHistoryResponse{ History: history, NextPageToken: resp.NextPageToken, Archived: true, }, nil } func (wh *WorkflowHandler) convertIndexedKeyToThrift(keys map[string]interface{}) map[string]types.IndexedValueType { converted := make(map[string]types.IndexedValueType) for k, v := range keys { converted[k] = common.ConvertIndexedValueTypeToInternalType(v, wh.GetLogger()) } return converted } func (wh *WorkflowHandler) isListRequestPageSizeTooLarge(pageSize int32, domain string) bool { return common.IsAdvancedVisibilityReadingEnabled(wh.config.ReadVisibilityStoreName(domain) != "db", wh.config.IsAdvancedVisConfigExist) && pageSize > int32(wh.config.ESIndexMaxResultWindow()) } // GetClusterInfo return information about cadence deployment func (wh *WorkflowHandler) GetClusterInfo( ctx context.Context, ) (resp *types.ClusterInfo, err error) { return &types.ClusterInfo{ SupportedClientVersions: &types.SupportedClientVersions{ GoSdk: client.SupportedGoSDKVersion, JavaSdk: client.SupportedJavaSDKVersion, }, }, nil } type domainWrapper struct { domain string } func (d domainWrapper) GetDomain() string { return d.domain } func (hs HealthStatus) String() string { switch hs { case HealthStatusOK: return "OK" case HealthStatusWarmingUp: return "WarmingUp" case HealthStatusShuttingDown: return "ShuttingDown" default: return "unknown" } } func (wh *WorkflowHandler) emitDescribeWorkflowExecutionMetrics(domain string, response *types.DescribeWorkflowExecutionResponse, err error) { scope := wh.GetMetricsClient().Scope(metrics.FrontendDescribeWorkflowExecutionStatusScope, metrics.DomainTag(domain)) if err != nil || response == nil { scope.IncCounter(metrics.DescribeWorkflowStatusError) return } status := "unknown" if response.WorkflowExecutionInfo != nil && response.WorkflowExecutionInfo.CloseStatus != nil { status = response.WorkflowExecutionInfo.CloseStatus.String() } scope = scope.Tagged(metrics.WorkflowCloseStatusTag(status)) scope.IncCounter(metrics.DescribeWorkflowStatusCount) } func (wh *WorkflowHandler) emitGetWorkflowExecutionHistoryMetrics(domainName, domainID, workflowID, workflowCloseStatus string, closeTime *time.Time) { domainEntry, err := wh.GetDomainCache().GetDomainByID(domainID) if err != nil { wh.GetLogger().Warn("Failed to get domain entry for metrics", tag.WorkflowDomainName(domainName), tag.Error(err)) return } retentionDays := domainEntry.GetRetentionDays(workflowID) scope := wh.GetMetricsClient().Scope( metrics.FrontendGetWorkflowExecutionHistoryScope, metrics.DomainTag(domainName), metrics.WorkflowCloseStatusTag(workflowCloseStatus), ) // For closed workflows with close time, calculate days remaining until retention expires // For running workflows emitting the retention days value metricValue := float64(retentionDays) if closeTime != nil { // (closeTime + retentionDays) - now = days remaining retentionExpiry := closeTime.Add(time.Duration(retentionDays) * 24 * time.Hour) daysRemaining := time.Until(retentionExpiry).Hours() / 24 metricValue = daysRemaining } scope.UpdateGauge(metrics.WorkflowExecutionHistoryAccess, metricValue) } // Some error types are introduced later that some clients might not support // To make them backward compatible, we continue returning the legacy error types // for older clients func (wh *WorkflowHandler) normalizeVersionedErrors(ctx context.Context, err error) error { switch err.(type) { case *types.WorkflowExecutionAlreadyCompletedError: call := yarpc.CallFromContext(ctx) clientFeatureVersion := call.Header(common.FeatureVersionHeaderName) clientImpl := call.Header(common.ClientImplHeaderName) featureFlags := client.GetFeatureFlagsFromHeader(call) vErr := wh.versionChecker.SupportsWorkflowAlreadyCompletedError(clientImpl, clientFeatureVersion, featureFlags) if vErr == nil { return err } return &types.EntityNotExistsError{Message: "Workflow execution already completed."} default: return err } } func constructRestartWorkflowRequest(w *types.WorkflowExecutionStartedEventAttributes, domain string, identity string, workflowID string) *types.StartWorkflowExecutionRequest { startRequest := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New().String(), Domain: domain, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{ Name: w.WorkflowType.Name, }, TaskList: &types.TaskList{ Name: w.TaskList.Name, Kind: w.TaskList.Kind, }, Input: w.Input, ExecutionStartToCloseTimeoutSeconds: w.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: w.TaskStartToCloseTimeoutSeconds, Identity: identity, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyTerminateIfRunning.Ptr(), CronOverlapPolicy: w.CronOverlapPolicy, ActiveClusterSelectionPolicy: w.ActiveClusterSelectionPolicy, } startRequest.CronSchedule = w.CronSchedule startRequest.RetryPolicy = w.RetryPolicy startRequest.JitterStartSeconds = w.JitterStartSeconds startRequest.ActiveClusterSelectionPolicy = w.ActiveClusterSelectionPolicy startRequest.DelayStartSeconds = common.Int32Ptr(0) startRequest.Header = w.Header startRequest.Memo = w.Memo startRequest.SearchAttributes = w.SearchAttributes return startRequest } func getMetricsScopeWithDomain( scope metrics.ScopeIdx, d domainGetter, metricsClient metrics.Client, ) metrics.Scope { var metricsScope metrics.Scope if d != nil { metricsScope = metricsClient.Scope(scope).Tagged(metrics.DomainTag(d.GetDomain())) } else { metricsScope = metricsClient.Scope(scope).Tagged(metrics.DomainUnknownTag()) } return metricsScope } ================================================ FILE: service/frontend/api/handler_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package api import ( "context" "encoding/json" "errors" "fmt" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "go.uber.org/yarpc/yarpctest" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/domain" dc "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" frontendcfg "github.com/uber/cadence/service/frontend/config" "github.com/uber/cadence/service/frontend/validate" ) const ( numHistoryShards = 10 testDomain = "test-domain" canaryDomain = "cadence-canary" testDomainID = "e4f90ec0-1313-45be-9877-8aa41f72a45a" testWorkflowID = "test-workflow-id" testRunID = "2c8b555f-1f55-4955-9d1c-b980194555c9" testHistoryArchivalURI = "testScheme://history/URI" testVisibilityArchivalURI = "testScheme://visibility/URI" ) type ( workflowHandlerSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockResource *resource.Test mockDomainCache *cache.MockDomainCache mockHistoryClient *history.MockClient mockMatchingClient *matching.MockClient domainHandler domain.Handler mockProducer *mocks.KafkaProducer mockMessagingClient messaging.Client mockMetadataMgr *mocks.MetadataManager mockHistoryV2Mgr *mocks.HistoryV2Manager mockVisibilityMgr *mocks.VisibilityManager mockArchivalMetadata *archiver.MockArchivalMetadata mockArchiverProvider *provider.MockArchiverProvider mockHistoryArchiver *archiver.HistoryArchiverMock mockVisibilityArchiver *archiver.VisibilityArchiverMock mockVersionChecker *client.MockVersionChecker mockTokenSerializer *common.MockTaskTokenSerializer mockDomainAuditManager *persistence.MockDomainAuditManager testDomain string testDomainID string } ) func TestWorkflowHandlerSuite(t *testing.T) { s := new(workflowHandlerSuite) suite.Run(t, s) } func (s *workflowHandlerSuite) SetupSuite() { } func (s *workflowHandlerSuite) TearDownSuite() { } func (s *workflowHandlerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.testDomain = testDomain s.testDomainID = testDomainID s.controller = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.Frontend) s.mockDomainCache = s.mockResource.DomainCache s.mockHistoryClient = s.mockResource.HistoryClient s.mockMatchingClient = s.mockResource.MatchingClient s.mockMetadataMgr = s.mockResource.MetadataMgr s.mockHistoryV2Mgr = s.mockResource.HistoryMgr s.mockVisibilityMgr = s.mockResource.VisibilityMgr s.mockArchivalMetadata = s.mockResource.ArchivalMetadata s.mockArchiverProvider = s.mockResource.ArchiverProvider s.mockTokenSerializer = common.NewMockTaskTokenSerializer(s.controller) s.mockProducer = &mocks.KafkaProducer{} s.mockMessagingClient = mocks.NewMockMessagingClient(s.mockProducer, nil) s.mockHistoryArchiver = &archiver.HistoryArchiverMock{} s.mockVisibilityArchiver = &archiver.VisibilityArchiverMock{} s.mockVersionChecker = client.NewMockVersionChecker(s.controller) s.mockDomainAuditManager = persistence.NewMockDomainAuditManager(s.controller) // these tests don't mock the domain handler config := s.newConfig(dc.NewInMemoryClient()) s.domainHandler = domain.NewHandler( config.DomainConfig, s.mockResource.GetLogger(), s.mockResource.GetDomainManager(), s.mockDomainAuditManager, s.mockResource.GetClusterMetadata(), domain.NewDomainReplicator(s.mockProducer, s.mockResource.GetLogger()), s.mockResource.GetArchivalMetadata(), s.mockResource.GetArchiverProvider(), s.mockResource.GetTimeSource(), ) mockMonitor := s.mockResource.MembershipResolver mockMonitor.EXPECT().MemberCount(service.Frontend).Return(5, nil).AnyTimes() s.mockVersionChecker.EXPECT().ClientSupported(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() } func (s *workflowHandlerSuite) TearDownTest() { s.controller.Finish() s.mockResource.Finish(s.T()) s.mockProducer.AssertExpectations(s.T()) s.mockHistoryArchiver.AssertExpectations(s.T()) s.mockVisibilityArchiver.AssertExpectations(s.T()) } func (s *workflowHandlerSuite) getWorkflowHandler(config *frontendcfg.Config) *WorkflowHandler { return NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, s.domainHandler) } func (s *workflowHandlerSuite) TestDisableListVisibilityByFilter() { config := s.newConfig(dc.NewInMemoryClient()) config.DisableListVisibilityByFilter = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) wh := s.getWorkflowHandler(config) s.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return(s.testDomainID, nil).AnyTimes() // test list open by wid listRequest := &types.ListOpenWorkflowExecutionsRequest{ Domain: s.testDomain, StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Int64Ptr(0), LatestTime: common.Int64Ptr(time.Now().UnixNano()), }, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: "wid", }, } _, err := wh.ListOpenWorkflowExecutions(context.Background(), listRequest) s.Error(err) s.Equal(validate.ErrNoPermission, err) // test list open by workflow type listRequest.ExecutionFilter = nil listRequest.TypeFilter = &types.WorkflowTypeFilter{ Name: "workflow-type", } _, err = wh.ListOpenWorkflowExecutions(context.Background(), listRequest) s.Error(err) s.Equal(validate.ErrNoPermission, err) // test list close by wid listRequest2 := &types.ListClosedWorkflowExecutionsRequest{ Domain: s.testDomain, StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Int64Ptr(0), LatestTime: common.Int64Ptr(time.Now().UnixNano()), }, ExecutionFilter: &types.WorkflowExecutionFilter{ WorkflowID: "wid", }, } _, err = wh.ListClosedWorkflowExecutions(context.Background(), listRequest2) s.Error(err) s.Equal(validate.ErrNoPermission, err) // test list close by workflow type listRequest2.ExecutionFilter = nil listRequest2.TypeFilter = &types.WorkflowTypeFilter{ Name: "workflow-type", } _, err = wh.ListClosedWorkflowExecutions(context.Background(), listRequest2) s.Error(err) s.Equal(validate.ErrNoPermission, err) // test list close by workflow status listRequest2.TypeFilter = nil failedStatus := types.WorkflowExecutionCloseStatusFailed listRequest2.StatusFilter = &failedStatus _, err = wh.ListClosedWorkflowExecutions(context.Background(), listRequest2) s.Error(err) s.Equal(validate.ErrNoPermission, err) } func (s *workflowHandlerSuite) TestPollForTask_Failed_ContextTimeoutTooShort() { config := s.newConfig(dc.NewInMemoryClient()) wh := s.getWorkflowHandler(config) bgCtx := context.Background() _, err := wh.PollForDecisionTask(bgCtx, &types.PollForDecisionTaskRequest{ Domain: s.testDomain, }) s.Error(err) s.Equal(common.ErrContextTimeoutNotSet, err) _, err = wh.PollForActivityTask(bgCtx, &types.PollForActivityTaskRequest{ Domain: s.testDomain, }) s.Error(err) s.Equal(common.ErrContextTimeoutNotSet, err) shortCtx, cancel := context.WithTimeout(bgCtx, constants.MinLongPollTimeout-time.Millisecond) defer cancel() _, err = wh.PollForDecisionTask(shortCtx, &types.PollForDecisionTaskRequest{ Domain: s.testDomain, }) s.Error(err) s.Equal(common.ErrContextTimeoutTooShort, err) _, err = wh.PollForActivityTask(shortCtx, &types.PollForActivityTaskRequest{ Domain: s.testDomain, }) s.Error(err) s.Equal(common.ErrContextTimeoutTooShort, err) } func (s *workflowHandlerSuite) TestPollForDecisionTask_AutoConfigHint() { for _, tt := range []struct { name string response *types.MatchingPollForDecisionTaskResponse expected *types.PollForDecisionTaskResponse }{ { "success", &types.MatchingPollForDecisionTaskResponse{ TaskToken: []byte("some value"), WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "rid", }, AutoConfigHint: &types.AutoConfigHint{EnableAutoConfig: true, PollerWaitTimeInMs: 1000}, }, &types.PollForDecisionTaskResponse{ TaskToken: []byte("some value"), History: &types.History{ Events: []*types.HistoryEvent{}, }, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "rid", }, AutoConfigHint: &types.AutoConfigHint{EnableAutoConfig: true, PollerWaitTimeInMs: 1000}, }, }, { "success with empty poll", &types.MatchingPollForDecisionTaskResponse{ AutoConfigHint: &types.AutoConfigHint{EnableAutoConfig: true, PollerWaitTimeInMs: 1000}, }, &types.PollForDecisionTaskResponse{ AutoConfigHint: &types.AutoConfigHint{EnableAutoConfig: true, PollerWaitTimeInMs: 1000}, }, }, } { s.T().Run(tt.name, func(t *testing.T) { s.mockDomainCache.EXPECT().GetDomain(s.testDomain).Return(cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomain, ID: s.testDomainID}, &persistence.DomainConfig{}, "", ), nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil).AnyTimes() s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.Anything).Return( &persistence.ReadHistoryBranchResponse{}, nil).Maybe() s.mockResource.MatchingClient.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any()).Return( tt.response, nil).Times(1) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() config := s.newConfig(dc.NewInMemoryClient()) wh := s.getWorkflowHandler(config) resp, err := wh.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.testDomain, TaskList: &types.TaskList{ Name: "task-list", }, }) assert.NoError(t, err) assert.Equal(t, tt.expected, resp) }) } } func (s *workflowHandlerSuite) TestPollForDecisionTask_IsolationGroupDrained() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) wh := s.getWorkflowHandler(config) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() isolationGroup := "dca1" ctx = isolationgroup.ContextWithIsolationGroup(ctx, isolationGroup) s.mockDomainCache.EXPECT().GetDomain(s.testDomain).Return(cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomain}, &persistence.DomainConfig{}, "", ), nil) s.mockResource.IsolationGroups.EXPECT().IsDrained(gomock.Any(), s.testDomain, isolationGroup).Return(true, nil).AnyTimes() resp, err := wh.PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{ Domain: s.testDomain, TaskList: &types.TaskList{ Name: "task-list", }, }) s.NoError(err) s.Equal(&types.PollForDecisionTaskResponse{}, resp) } func (s *workflowHandlerSuite) TestPollForActivityTask_IsolationGroupDrained() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) wh := s.getWorkflowHandler(config) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() isolationGroup := "dca1" ctx = isolationgroup.ContextWithIsolationGroup(ctx, isolationGroup) s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockResource.IsolationGroups.EXPECT().IsDrained(gomock.Any(), s.testDomain, isolationGroup).Return(true, nil).AnyTimes() resp, err := wh.PollForActivityTask(ctx, &types.PollForActivityTaskRequest{ Domain: s.testDomain, TaskList: &types.TaskList{ Name: "task-list", }, }) s.NoError(err) s.Equal(&types.PollForActivityTaskResponse{}, resp) } func (s *workflowHandlerSuite) TestPollForActivityTask() { for _, tt := range []struct { name string response *types.MatchingPollForActivityTaskResponse expected *types.PollForActivityTaskResponse }{ { "success", &types.MatchingPollForActivityTaskResponse{ TaskToken: []byte("token"), WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "rid", }, ActivityID: "1", Input: []byte(`{"key": "value"}`), AutoConfigHint: &types.AutoConfigHint{EnableAutoConfig: true, PollerWaitTimeInMs: 1000}, }, &types.PollForActivityTaskResponse{ TaskToken: []byte("token"), WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "rid", }, ActivityID: "1", Input: []byte(`{"key": "value"}`), AutoConfigHint: &types.AutoConfigHint{EnableAutoConfig: true, PollerWaitTimeInMs: 1000}, }, }, { "success with empty polls", &types.MatchingPollForActivityTaskResponse{ AutoConfigHint: &types.AutoConfigHint{EnableAutoConfig: true, PollerWaitTimeInMs: 1000}, }, &types.PollForActivityTaskResponse{ AutoConfigHint: &types.AutoConfigHint{EnableAutoConfig: true, PollerWaitTimeInMs: 1000}, }, }, } { s.T().Run(tt.name, func(t *testing.T) { config := s.newConfig(dc.NewInMemoryClient()) config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) wh := s.getWorkflowHandler(config) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() isolationGroup := "dca1" ctx = isolationgroup.ContextWithIsolationGroup(ctx, isolationGroup) s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockResource.IsolationGroups.EXPECT().IsDrained(gomock.Any(), s.testDomain, isolationGroup).Return(false, nil).AnyTimes() s.mockMatchingClient.EXPECT().PollForActivityTask(gomock.Any(), gomock.Any()).Return(tt.response, nil) resp, err := wh.PollForActivityTask(ctx, &types.PollForActivityTaskRequest{ Domain: s.testDomain, TaskList: &types.TaskList{ Name: "task-list", }, }) s.NoError(err) s.Equal(tt.expected, resp) }) } } func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_RequestIdNotSet() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) wh := s.getWorkflowHandler(config) startWorkflowExecutionRequest := &types.StartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{ Name: "workflow-type", }, TaskList: &types.TaskList{ Name: "task-list", }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2, MaximumIntervalInSeconds: 2, MaximumAttempts: 1, ExpirationIntervalInSeconds: 1, }, } _, err := wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest) s.Error(err) s.Equal(&types.BadRequestError{Message: "requestId \"\" is not a valid UUID"}, err) startWorkflowExecutionRequest.RequestID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" _, err = wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest) s.Error(err) s.Equal(&types.BadRequestError{Message: "requestId \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\" is not a valid UUID"}, err) } func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_BadDelayStartSeconds() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) wh := s.getWorkflowHandler(config) startWorkflowExecutionRequest := &types.StartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{ Name: "workflow-type", }, TaskList: &types.TaskList{ Name: "task-list", }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2, MaximumIntervalInSeconds: 2, MaximumAttempts: 1, ExpirationIntervalInSeconds: 1, }, RequestID: uuid.New(), DelayStartSeconds: common.Int32Ptr(-1), } _, err := wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest) s.Error(err) s.Equal(validate.ErrInvalidDelayStartSeconds, err) } func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_StartRequestNotSet() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) wh := s.getWorkflowHandler(config) _, err := wh.StartWorkflowExecution(context.Background(), nil) s.Error(err) s.Equal(validate.ErrRequestNotSet, err) } func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_DomainNotSet() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) wh := s.getWorkflowHandler(config) startWorkflowExecutionRequest := &types.StartWorkflowExecutionRequest{ WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{ Name: "workflow-type", }, TaskList: &types.TaskList{ Name: "task-list", }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2, MaximumIntervalInSeconds: 2, MaximumAttempts: 1, ExpirationIntervalInSeconds: 1, }, RequestID: uuid.New(), } _, err := wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest) s.Error(err) s.Equal(validate.ErrDomainNotSet, err) } func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_WorkflowIdNotSet() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) wh := s.getWorkflowHandler(config) startWorkflowExecutionRequest := &types.StartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowType: &types.WorkflowType{ Name: "workflow-type", }, TaskList: &types.TaskList{ Name: "task-list", }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2, MaximumIntervalInSeconds: 2, MaximumAttempts: 1, ExpirationIntervalInSeconds: 1, }, RequestID: uuid.New(), } _, err := wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest) s.Error(err) s.Equal(validate.ErrWorkflowIDNotSet, err) } func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_WorkflowTypeNotSet() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) wh := s.getWorkflowHandler(config) startWorkflowExecutionRequest := &types.StartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{ Name: "", }, TaskList: &types.TaskList{ Name: "task-list", }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2, MaximumIntervalInSeconds: 2, MaximumAttempts: 1, ExpirationIntervalInSeconds: 1, }, RequestID: uuid.New(), } _, err := wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest) s.Error(err) s.Equal(validate.ErrWorkflowTypeNotSet, err) } func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_TaskListNotSet() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) wh := s.getWorkflowHandler(config) startWorkflowExecutionRequest := &types.StartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{ Name: "workflow-type", }, TaskList: &types.TaskList{ Name: "", }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2, MaximumIntervalInSeconds: 2, MaximumAttempts: 1, ExpirationIntervalInSeconds: 1, }, RequestID: uuid.New(), } _, err := wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest) s.Error(err) s.Equal(validate.ErrTaskListNotSet, err) } func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_InvalidExecutionStartToCloseTimeout() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) wh := s.getWorkflowHandler(config) startWorkflowExecutionRequest := &types.StartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{ Name: "workflow-type", }, TaskList: &types.TaskList{ Name: "task-list", }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(0), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2, MaximumIntervalInSeconds: 2, MaximumAttempts: 1, ExpirationIntervalInSeconds: 1, }, RequestID: uuid.New(), } _, err := wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest) s.Error(err) s.Equal(validate.ErrInvalidExecutionStartToCloseTimeoutSeconds, err) } func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_InvalidTaskStartToCloseTimeout() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) wh := s.getWorkflowHandler(config) startWorkflowExecutionRequest := &types.StartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{ Name: "workflow-type", }, TaskList: &types.TaskList{ Name: "task-list", }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(0), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2, MaximumIntervalInSeconds: 2, MaximumAttempts: 1, ExpirationIntervalInSeconds: 1, }, RequestID: uuid.New(), } _, err := wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest) s.Error(err) s.Equal(validate.ErrInvalidTaskStartToCloseTimeoutSeconds, err) } func (s *workflowHandlerSuite) TestStartWorkflowExecution_IsolationGroupDrained() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) wh := s.getWorkflowHandler(config) startWorkflowExecutionRequest := &types.StartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{ Name: "workflow-type", }, TaskList: &types.TaskList{ Name: "task-list", }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2, MaximumIntervalInSeconds: 2, MaximumAttempts: 1, ExpirationIntervalInSeconds: 1, }, RequestID: uuid.New(), } isolationGroup := "dca1" ctx := isolationgroup.ContextWithIsolationGroup(context.Background(), isolationGroup) s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockResource.IsolationGroups.EXPECT().IsDrained(gomock.Any(), s.testDomain, isolationGroup).Return(true, nil) _, err := wh.StartWorkflowExecution(ctx, startWorkflowExecutionRequest) s.Error(err) s.IsType(err, &types.BadRequestError{}) } func (s *workflowHandlerSuite) TestStartWorkflowExecution_LogJitterTime() { config := s.newConfig(dc.NewInMemoryClient()) config.UserRPS = dynamicproperties.GetIntPropertyFn(10) wh := s.getWorkflowHandler(config) jitterStart := int32(10) startWorkflowExecutionRequest := &types.StartWorkflowExecutionRequest{ Domain: canaryDomain, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{ Name: "workflow-type", }, TaskList: &types.TaskList{ Name: "task-list", }, JitterStartSeconds: &jitterStart, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2, MaximumIntervalInSeconds: 2, MaximumAttempts: 1, ExpirationIntervalInSeconds: 1, }, RequestID: uuid.New(), } s.mockDomainCache.EXPECT().GetDomainID(canaryDomain).Return(s.testDomainID, nil).Times(2) s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(&types.StartWorkflowExecutionResponse{RunID: "test-rid"}, nil) _, err := wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest) s.NoError(err) } func (s *workflowHandlerSuite) TestDiagnoseWorkflowExecution_Success() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := &types.DiagnoseWorkflowExecutionRequest{ Domain: testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, Identity: "", } diagnosticWfDomain := "cadence-system" diagnosticWfID := fmt.Sprintf("%s-%s", testDomain, testRunID) diagnosticWfRunID := "123" resp := &types.DiagnoseWorkflowExecutionResponse{ Domain: diagnosticWfDomain, DiagnosticWorkflowExecution: &types.WorkflowExecution{ WorkflowID: diagnosticWfID, RunID: diagnosticWfRunID, }, } s.mockDomainCache.EXPECT().GetDomainID(diagnosticWfDomain).Return(s.testDomainID, nil).Times(2) s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(&types.StartWorkflowExecutionResponse{RunID: diagnosticWfRunID}, nil) result, err := wh.DiagnoseWorkflowExecution(context.Background(), req) s.NoError(err) s.Equal(resp, result) } func (s *workflowHandlerSuite) TestDiagnoseWorkflowExecution_Success_WfAlreadyRunning() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := &types.DiagnoseWorkflowExecutionRequest{ Domain: testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, Identity: "", } diagnosticWfDomain := "cadence-system" diagnosticWfID := fmt.Sprintf("%s-%s", testDomain, testRunID) diagnosticWfRunID := "123" resp := &types.DiagnoseWorkflowExecutionResponse{ Domain: diagnosticWfDomain, DiagnosticWorkflowExecution: &types.WorkflowExecution{ WorkflowID: diagnosticWfID, RunID: diagnosticWfRunID, }, } s.mockDomainCache.EXPECT().GetDomainID(diagnosticWfDomain).Return(s.testDomainID, nil).Times(2) s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &types.WorkflowExecutionAlreadyStartedError{ Message: "Workflow execution is already running", RunID: diagnosticWfRunID, }) result, err := wh.DiagnoseWorkflowExecution(context.Background(), req) s.NoError(err) s.Equal(resp, result) } func (s *workflowHandlerSuite) TestDiagnoseWorkflowExecution_Failed_RequestNotSet() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) result, err := wh.DiagnoseWorkflowExecution(context.Background(), nil) s.Error(err) s.Equal(validate.ErrRequestNotSet, err) s.Nil(result) } func (s *workflowHandlerSuite) TestDiagnoseWorkflowExecution_Failed_DomainNotSet() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) result, err := wh.DiagnoseWorkflowExecution(context.Background(), &types.DiagnoseWorkflowExecutionRequest{ Domain: "", }) s.Error(err) s.Equal(validate.ErrDomainNotSet, err) s.Nil(result) } func (s *workflowHandlerSuite) TestDiagnoseWorkflowExecution_Failed_ExecutionNotSet() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) result, err := wh.DiagnoseWorkflowExecution(context.Background(), &types.DiagnoseWorkflowExecutionRequest{ Domain: testDomain, }) s.Error(err) s.Equal(validate.ErrExecutionNotSet, err) s.Nil(result) } func (s *workflowHandlerSuite) TestRecordActivityTaskHeartbeat_Success() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) taskToken := common.TaskToken{ DomainID: s.testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, ActivityID: "1", } taskTokenBytes, err := wh.tokenSerializer.Serialize(&taskToken) s.NoError(err) req := &types.RecordActivityTaskHeartbeatRequest{ TaskToken: taskTokenBytes, Details: nil, Identity: "", } resp := &types.RecordActivityTaskHeartbeatResponse{CancelRequested: false} s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) s.mockHistoryClient.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: s.testDomainID, HeartbeatRequest: req, }).Return(resp, nil) result, err := wh.RecordActivityTaskHeartbeat(context.Background(), req) s.NoError(err) s.Equal(resp, result) } func (s *workflowHandlerSuite) TestRecordActivityTaskHeartbeat_RequestNotSet() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) result, err := wh.RecordActivityTaskHeartbeat(context.Background(), nil /*request is not set*/) s.Error(err) s.Equal(validate.ErrRequestNotSet, err) s.Nil(result) } func (s *workflowHandlerSuite) TestRecordActivityTaskHeartbeat_TaskTokenNotSet() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) result, err := wh.RecordActivityTaskHeartbeat(context.Background(), &types.RecordActivityTaskHeartbeatRequest{ TaskToken: nil, // task token is not set Details: nil, Identity: "", }) s.Error(err) s.Equal(validate.ErrTaskTokenNotSet, err) s.Nil(result) } func (s *workflowHandlerSuite) TestRecordActivityTaskHeartbeatByID_Success() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := &types.RecordActivityTaskHeartbeatByIDRequest{ Domain: s.testDomain, WorkflowID: testWorkflowID, RunID: testRunID, ActivityID: "1", } resp := &types.RecordActivityTaskHeartbeatResponse{CancelRequested: false} s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockHistoryClient.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), gomock.Any()).Return(resp, nil) result, err := wh.RecordActivityTaskHeartbeatByID(context.Background(), req) s.NoError(err) s.Equal(resp, result) } func (s *workflowHandlerSuite) TestRecordActivityTaskHeartbeatByID_RequestNotSet() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) result, err := wh.RecordActivityTaskHeartbeatByID(context.Background(), nil /*request is not set*/) s.Error(err) s.Equal(validate.ErrRequestNotSet, err) s.Nil(result) } func (s *workflowHandlerSuite) TestRecordActivityTaskHeartbeatByID_DomainNotSet() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) result, err := wh.RecordActivityTaskHeartbeatByID( context.Background(), &types.RecordActivityTaskHeartbeatByIDRequest{ Domain: "", // domain not set }) s.Error(err) s.Equal(validate.ErrDomainNotSet, err) s.Nil(result) } func (s *workflowHandlerSuite) TestRespondActivityTaskCompleted_Success() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) taskToken := common.TaskToken{ DomainID: s.testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, ActivityID: "1", } taskTokenBytes, err := wh.tokenSerializer.Serialize(&taskToken) s.NoError(err) req := &types.RespondActivityTaskCompletedRequest{ TaskToken: taskTokenBytes, } s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) s.mockHistoryClient.EXPECT().RespondActivityTaskCompleted(gomock.Any(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: taskToken.DomainID, CompleteRequest: req, }).Return(nil) err = wh.RespondActivityTaskCompleted(context.Background(), req) // only checking for successful write here s.NoError(err) } func (s *workflowHandlerSuite) TestRespondActivityTaskCompletedByID_Success() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := &types.RespondActivityTaskCompletedByIDRequest{ Domain: s.testDomain, WorkflowID: testWorkflowID, RunID: testRunID, ActivityID: "1", } s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockHistoryClient.EXPECT().RespondActivityTaskCompleted(gomock.Any(), gomock.Any()).Return(nil) err := wh.RespondActivityTaskCompletedByID(context.Background(), req) // only checking for successful write here s.NoError(err) } func buildRespondActivityTaskFailedRequest(taskToken common.TaskToken) *types.RespondActivityTaskFailedRequest { serializer := common.NewJSONTaskTokenSerializer() taskTokenBytes, err := serializer.Serialize(&taskToken) if err != nil { panic(err) } return &types.RespondActivityTaskFailedRequest{ TaskToken: taskTokenBytes, } } func TestRespondActivityTaskFailed(t *testing.T) { failedRequest := buildRespondActivityTaskFailedRequest(common.TaskToken{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, ActivityID: "1", }) type fields struct { shuttingDown int32 } type args struct { ctx context.Context failedRequest *types.RespondActivityTaskFailedRequest } tests := []struct { name string fields fields setupMocks func(*resource.Test, *client.MockVersionChecker) args args wantErr assert.ErrorAssertionFunc }{ { name: "Success", fields: fields{ shuttingDown: 0, }, setupMocks: func(t *resource.Test, mockVersionChecker *client.MockVersionChecker) { t.HistoryClient.EXPECT().RespondActivityTaskFailed(gomock.Any(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: testDomainID, FailedRequest: failedRequest, }).Return(nil) t.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-id", nil) }, args: args{ context.Background(), failedRequest, }, wantErr: assert.NoError, }, { name: "Error when shutting down", fields: fields{shuttingDown: 1}, setupMocks: func(t *resource.Test, mockVersionChecker *client.MockVersionChecker) { }, args: args{ context.Background(), buildRespondActivityTaskFailedRequest(common.TaskToken{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, ActivityID: "1", }), }, wantErr: assert.Error, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockResource := resource.NewTest(t, mockCtrl, metrics.Frontend) mockVersionChecker := client.NewMockVersionChecker(mockCtrl) tt.setupMocks(mockResource, mockVersionChecker) mockProducerManager := NewMockProducerManager(mockCtrl) config := frontendcfg.NewConfig( dc.NewCollection( dc.NewInMemoryClient(), mockResource.GetLogger(), ), numHistoryShards, false, "hostname", mockResource.GetLogger(), ) wh := NewWorkflowHandler(mockResource, config, mockVersionChecker, nil) wh.shuttingDown = tt.fields.shuttingDown wh.producerManager = mockProducerManager tt.wantErr(t, wh.RespondActivityTaskFailed(tt.args.ctx, tt.args.failedRequest), fmt.Sprintf("RespondActivityTaskFailed(%v, %v)", tt.args.ctx, tt.args.failedRequest)) }) } } func (s *workflowHandlerSuite) TestRegisterDomain_Failure_MissingDomainDataKey() { dynamicClient := dc.NewInMemoryClient() err := dynamicClient.UpdateValue(dynamicproperties.RequiredDomainDataKeys, map[string]interface{}{"Tier": true}) s.NoError(err) cfg := s.newConfig(dynamicClient) wh := s.getWorkflowHandler(cfg) req := registerDomainRequest( types.ArchivalStatusEnabled.Ptr(), testHistoryArchivalURI, types.ArchivalStatusEnabled.Ptr(), testVisibilityArchivalURI, ) err = wh.RegisterDomain(context.Background(), req) s.Error(err) s.Contains(err.Error(), "domain data error, missing required key") } func (s *workflowHandlerSuite) TestRegisterDomain_Failure_InvalidArchivalURI() { s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "random URI")) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "random URI")) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}) s.mockHistoryArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockVisibilityArchiver.On("ValidateURI", mock.Anything).Return(errors.New("invalid URI")) s.mockArchiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.mockHistoryArchiver, nil) s.mockArchiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.mockVisibilityArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := registerDomainRequest( types.ArchivalStatusEnabled.Ptr(), testHistoryArchivalURI, types.ArchivalStatusEnabled.Ptr(), testVisibilityArchivalURI, ) err := wh.RegisterDomain(context.Background(), req) s.Error(err) } func (s *workflowHandlerSuite) TestRegisterDomain_Success_EnabledWithNoArchivalURI() { s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", testHistoryArchivalURI)) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", testVisibilityArchivalURI)) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}) s.mockMetadataMgr.On("CreateDomain", mock.Anything, mock.Anything).Return(&persistence.CreateDomainResponse{ ID: "test-id", }, nil) s.mockHistoryArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockVisibilityArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockArchiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.mockHistoryArchiver, nil) s.mockArchiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.mockVisibilityArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := registerDomainRequest(types.ArchivalStatusEnabled.Ptr(), "", types.ArchivalStatusEnabled.Ptr(), "") err := wh.RegisterDomain(context.Background(), req) s.NoError(err) } func (s *workflowHandlerSuite) TestRegisterDomain_Success_EnabledWithArchivalURI() { s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "invalidURI")) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "invalidURI")) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}) s.mockMetadataMgr.On("CreateDomain", mock.Anything, mock.Anything).Return(&persistence.CreateDomainResponse{ ID: "test-id", }, nil) s.mockHistoryArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockVisibilityArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockArchiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.mockHistoryArchiver, nil) s.mockArchiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.mockVisibilityArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := registerDomainRequest( types.ArchivalStatusEnabled.Ptr(), testHistoryArchivalURI, types.ArchivalStatusEnabled.Ptr(), testVisibilityArchivalURI, ) err := wh.RegisterDomain(context.Background(), req) s.NoError(err) } func (s *workflowHandlerSuite) TestRegisterDomain_Success_ClusterNotConfiguredForArchival() { s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewDisabledArchvialConfig()) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewDisabledArchvialConfig()) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}) s.mockMetadataMgr.On("CreateDomain", mock.Anything, mock.Anything).Return(&persistence.CreateDomainResponse{ ID: "test-id", }, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := registerDomainRequest( types.ArchivalStatusEnabled.Ptr(), testVisibilityArchivalURI, types.ArchivalStatusEnabled.Ptr(), "invalidURI", ) err := wh.RegisterDomain(context.Background(), req) s.NoError(err) } func (s *workflowHandlerSuite) TestRegisterDomain_Success_NotEnabled() { s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}) s.mockMetadataMgr.On("CreateDomain", mock.Anything, mock.Anything).Return(&persistence.CreateDomainResponse{ ID: "test-id", }, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := registerDomainRequest(nil, "", nil, "") err := wh.RegisterDomain(context.Background(), req) s.NoError(err) } func (s *workflowHandlerSuite) TestListDomains_Success() { domainResponse := persistenceGetDomainResponse( &domain.ArchivalState{}, &domain.ArchivalState{}, ) listDomainResp := &persistence.ListDomainsResponse{ Domains: []*persistence.GetDomainResponse{ domainResponse, domainResponse, }, } s.mockMetadataMgr. On("ListDomains", mock.Anything, mock.Anything). Return(listDomainResp, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) result, err := wh.ListDomains(context.Background(), &types.ListDomainsRequest{}) s.NoError(err) s.Equal(2, len(result.GetDomains())) } func (s *workflowHandlerSuite) TestListDomains_RequestNotSet() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) result, err := wh.ListDomains(context.Background(), nil /* list request is not set */) s.Error(err) s.Equal(validate.ErrRequestNotSet, err) s.Nil(result) } func (s *workflowHandlerSuite) TestHealth_Status() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) // workflow handler gets initial health status as HealthStatusWarmingUp result, err := wh.Health(context.Background()) // Health check looks for HealthStatusOK s.NoError(err) s.False(result.Ok) s.Equal("WarmingUp", result.Msg) wh.UpdateHealthStatus(HealthStatusOK) result, err = wh.Health(context.Background()) s.NoError(err) s.True(result.Ok) s.Equal("OK", result.Msg) wh.UpdateHealthStatus(HealthStatusShuttingDown) result, err = wh.Health(context.Background()) s.NoError(err) s.False(result.Ok) s.Equal("ShuttingDown", result.Msg) wh.UpdateHealthStatus(HealthStatus(-1)) result, err = wh.Health(context.Background()) s.NoError(err) s.False(result.Ok) s.Equal("unknown", result.Msg) } func (s *workflowHandlerSuite) TestGetClusterInfo() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.GetClusterInfo(context.Background()) s.NoError(err) s.Equal("1.7.0", resp.SupportedClientVersions.GoSdk) s.Equal("1.5.0", resp.SupportedClientVersions.JavaSdk) } func (s *workflowHandlerSuite) TestDescribeDomain_Success_ArchivalDisabled() { getDomainResp := persistenceGetDomainResponse( &domain.ArchivalState{Status: types.ArchivalStatusDisabled, URI: ""}, &domain.ArchivalState{Status: types.ArchivalStatusDisabled, URI: ""}, ) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(getDomainResp, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := &types.DescribeDomainRequest{ Name: common.StringPtr(s.testDomain), } result, err := wh.DescribeDomain(context.Background(), req) s.NoError(err) s.NotNil(result) s.NotNil(result.Configuration) s.Equal(types.ArchivalStatusDisabled, result.Configuration.GetHistoryArchivalStatus()) s.Equal("", result.Configuration.GetHistoryArchivalURI()) s.Equal(types.ArchivalStatusDisabled, result.Configuration.GetVisibilityArchivalStatus()) s.Equal("", result.Configuration.GetVisibilityArchivalURI()) } func (s *workflowHandlerSuite) TestDescribeDomain_Success_ArchivalEnabled() { getDomainResp := persistenceGetDomainResponse( &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: testHistoryArchivalURI}, &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: testVisibilityArchivalURI}, ) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(getDomainResp, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) req := &types.DescribeDomainRequest{ Name: common.StringPtr(s.testDomain), } result, err := wh.DescribeDomain(context.Background(), req) s.NoError(err) s.NotNil(result) s.NotNil(result.Configuration) s.Equal(types.ArchivalStatusEnabled, result.Configuration.GetHistoryArchivalStatus()) s.Equal(testHistoryArchivalURI, result.Configuration.GetHistoryArchivalURI()) s.Equal(types.ArchivalStatusEnabled, result.Configuration.GetVisibilityArchivalStatus()) s.Equal(testVisibilityArchivalURI, result.Configuration.GetVisibilityArchivalURI()) } func (s *workflowHandlerSuite) TestUpdateDomain_Failure_UpdateExistingArchivalURI() { s.mockMetadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: int64(0), }, nil) getDomainResp := persistenceGetDomainResponse( &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: testHistoryArchivalURI}, &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: testVisibilityArchivalURI}, ) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(getDomainResp, nil) s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockHistoryArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockArchiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.mockHistoryArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) updateReq := updateRequest( nil, nil, common.StringPtr("updated visibility URI"), nil, ) _, err := wh.UpdateDomain(context.Background(), updateReq) s.Error(err) } func (s *workflowHandlerSuite) TestUpdateDomain_Failure_InvalidArchivalURI() { s.mockMetadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: int64(0), }, nil) getDomainResp := persistenceGetDomainResponse( &domain.ArchivalState{Status: types.ArchivalStatusDisabled, URI: ""}, &domain.ArchivalState{Status: types.ArchivalStatusDisabled, URI: ""}, ) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(getDomainResp, nil) s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockHistoryArchiver.On("ValidateURI", mock.Anything).Return(errors.New("invalid URI")) s.mockArchiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.mockHistoryArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) updateReq := updateRequest( common.StringPtr("testScheme://invalid/updated/history/URI"), types.ArchivalStatusEnabled.Ptr(), nil, nil, ) _, err := wh.UpdateDomain(context.Background(), updateReq) s.Error(err) } func (s *workflowHandlerSuite) TestUpdateDomain_Success_ArchivalEnabledToArchivalDisabledWithoutSettingURI() { s.mockMetadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: int64(0), }, nil) getDomainResp := persistenceGetDomainResponse( &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: testHistoryArchivalURI}, &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: testVisibilityArchivalURI}, ) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(getDomainResp, nil) s.mockMetadataMgr.On("UpdateDomain", mock.Anything, mock.Anything).Return(nil) s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockHistoryArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockVisibilityArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockArchiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.mockHistoryArchiver, nil) s.mockArchiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.mockVisibilityArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) updateReq := updateRequest( nil, types.ArchivalStatusDisabled.Ptr(), nil, types.ArchivalStatusDisabled.Ptr(), ) result, err := wh.UpdateDomain(context.Background(), updateReq) s.NoError(err) s.NotNil(result) s.NotNil(result.Configuration) s.Equal(types.ArchivalStatusDisabled, result.Configuration.GetHistoryArchivalStatus()) s.Equal(testHistoryArchivalURI, result.Configuration.GetHistoryArchivalURI()) s.Equal(types.ArchivalStatusDisabled, result.Configuration.GetVisibilityArchivalStatus()) s.Equal(testVisibilityArchivalURI, result.Configuration.GetVisibilityArchivalURI()) } func (s *workflowHandlerSuite) TestUpdateDomain_Success_ClusterNotConfiguredForArchival() { s.mockMetadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: int64(0), }, nil) getDomainResp := persistenceGetDomainResponse( &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: "some random history URI"}, &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: "some random visibility URI"}, ) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(getDomainResp, nil) s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewDisabledArchvialConfig()) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewDisabledArchvialConfig()) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) updateReq := updateRequest(nil, types.ArchivalStatusDisabled.Ptr(), nil, nil) result, err := wh.UpdateDomain(context.Background(), updateReq) s.NoError(err) s.NotNil(result) s.NotNil(result.Configuration) s.Equal(types.ArchivalStatusEnabled, result.Configuration.GetHistoryArchivalStatus()) s.Equal("some random history URI", result.Configuration.GetHistoryArchivalURI()) s.Equal(types.ArchivalStatusEnabled, result.Configuration.GetVisibilityArchivalStatus()) s.Equal("some random visibility URI", result.Configuration.GetVisibilityArchivalURI()) } func (s *workflowHandlerSuite) TestUpdateDomain_Success_ArchivalEnabledToArchivalDisabledWithSettingBucket() { s.mockMetadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: int64(0), }, nil) getDomainResp := persistenceGetDomainResponse( &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: testHistoryArchivalURI}, &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: testVisibilityArchivalURI}, ) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(getDomainResp, nil) s.mockMetadataMgr.On("UpdateDomain", mock.Anything, mock.Anything).Return(nil) s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockHistoryArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockVisibilityArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockArchiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.mockHistoryArchiver, nil) s.mockArchiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.mockVisibilityArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) updateReq := updateRequest( common.StringPtr(testHistoryArchivalURI), types.ArchivalStatusDisabled.Ptr(), common.StringPtr(testVisibilityArchivalURI), types.ArchivalStatusDisabled.Ptr(), ) result, err := wh.UpdateDomain(context.Background(), updateReq) s.NoError(err) s.NotNil(result) s.NotNil(result.Configuration) s.Equal(types.ArchivalStatusDisabled, result.Configuration.GetHistoryArchivalStatus()) s.Equal(testHistoryArchivalURI, result.Configuration.GetHistoryArchivalURI()) s.Equal(types.ArchivalStatusDisabled, result.Configuration.GetVisibilityArchivalStatus()) s.Equal(testVisibilityArchivalURI, result.Configuration.GetVisibilityArchivalURI()) } func (s *workflowHandlerSuite) TestUpdateDomain_Success_ArchivalEnabledToEnabled() { s.mockMetadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: int64(0), }, nil) getDomainResp := persistenceGetDomainResponse( &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: testHistoryArchivalURI}, &domain.ArchivalState{Status: types.ArchivalStatusEnabled, URI: testVisibilityArchivalURI}, ) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(getDomainResp, nil) s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockHistoryArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockVisibilityArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockArchiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.mockHistoryArchiver, nil) s.mockArchiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.mockVisibilityArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) updateReq := updateRequest( common.StringPtr(testHistoryArchivalURI), types.ArchivalStatusEnabled.Ptr(), common.StringPtr(testVisibilityArchivalURI), types.ArchivalStatusEnabled.Ptr(), ) result, err := wh.UpdateDomain(context.Background(), updateReq) s.NoError(err) s.NotNil(result) s.NotNil(result.Configuration) s.Equal(types.ArchivalStatusEnabled, result.Configuration.GetHistoryArchivalStatus()) s.Equal(testHistoryArchivalURI, result.Configuration.GetHistoryArchivalURI()) s.Equal(types.ArchivalStatusEnabled, result.Configuration.GetVisibilityArchivalStatus()) s.Equal(testVisibilityArchivalURI, result.Configuration.GetVisibilityArchivalURI()) } func (s *workflowHandlerSuite) TestUpdateDomain_Success_ArchivalNeverEnabledToEnabled() { s.mockMetadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: int64(0), }, nil) getDomainResp := persistenceGetDomainResponse( &domain.ArchivalState{Status: types.ArchivalStatusDisabled, URI: ""}, &domain.ArchivalState{Status: types.ArchivalStatusDisabled, URI: ""}, ) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(getDomainResp, nil) s.mockMetadataMgr.On("UpdateDomain", mock.Anything, mock.Anything).Return(nil) s.mockArchivalMetadata.On("GetHistoryConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "some random URI")) s.mockHistoryArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockVisibilityArchiver.On("ValidateURI", mock.Anything).Return(nil) s.mockArchiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.mockHistoryArchiver, nil) s.mockArchiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.mockVisibilityArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) updateReq := updateRequest( common.StringPtr(testHistoryArchivalURI), types.ArchivalStatusEnabled.Ptr(), common.StringPtr(testVisibilityArchivalURI), types.ArchivalStatusEnabled.Ptr(), ) result, err := wh.UpdateDomain(context.Background(), updateReq) s.NoError(err) s.NotNil(result) s.NotNil(result.Configuration) s.Equal(types.ArchivalStatusEnabled, result.Configuration.GetHistoryArchivalStatus()) s.Equal(testHistoryArchivalURI, result.Configuration.GetHistoryArchivalURI()) s.Equal(types.ArchivalStatusEnabled, result.Configuration.GetVisibilityArchivalStatus()) s.Equal(testVisibilityArchivalURI, result.Configuration.GetVisibilityArchivalURI()) } func (s *workflowHandlerSuite) TestUpdateDomain_Success_FailOver() { s.mockMetadataMgr.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: int64(0), }, nil) getDomainResp := persistenceGetDomainResponseForFailoverTest( &domain.ArchivalState{Status: types.ArchivalStatusDisabled, URI: ""}, &domain.ArchivalState{Status: types.ArchivalStatusDisabled, URI: ""}, ) // This test is simulating a domain failover from the point of view of the 'standby' cluster // for a domain where the cluster 'active' is being failed over to 'standby'. The test is executing // in the 'standby' cluster, so the above is setting the configuration to appear that way. s.mockResource.ClusterMetadata = cluster.TestPassiveClusterMetadata // Re-instantiate the domain-handler object due to it relying on it // pulling in the mock cluster metadata object mutated above. // Todo (David.Porter) consider refactoring these tests // to be setup without mutation and without as long dependency chains s.domainHandler = domain.NewHandler( s.newConfig(dc.NewInMemoryClient()).DomainConfig, s.mockResource.GetLogger(), s.mockResource.GetDomainManager(), s.mockDomainAuditManager, s.mockResource.GetClusterMetadata(), domain.NewDomainReplicator(s.mockProducer, s.mockResource.GetLogger()), s.mockResource.GetArchivalMetadata(), s.mockResource.GetArchiverProvider(), s.mockResource.GetTimeSource(), ) s.mockMetadataMgr.On("GetDomain", mock.Anything, mock.Anything).Return(getDomainResp, nil) s.mockMetadataMgr.On("UpdateDomain", mock.Anything, mock.Anything).Return(nil) s.mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil).Once() s.mockResource.RemoteFrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()). Return(describeDomainResponseServer, nil).AnyTimes() wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) updateReq := updateFailoverRequest( common.StringPtr(testHistoryArchivalURI), types.ArchivalStatusEnabled.Ptr(), common.StringPtr(testVisibilityArchivalURI), types.ArchivalStatusEnabled.Ptr(), common.Int32Ptr(1), common.StringPtr(cluster.TestAlternativeClusterName), ) result, err := wh.UpdateDomain(context.Background(), updateReq) s.NoError(err) s.NotNil(result) s.NotNil(result.Configuration) s.Equal(result.ReplicationConfiguration.ActiveClusterName, cluster.TestAlternativeClusterName) } func (s *workflowHandlerSuite) TestUpdateDomain_Failure_FailoverLockdown() { dynamicClient := dc.NewInMemoryClient() err := dynamicClient.UpdateValue(dynamicproperties.Lockdown, true) s.NoError(err) wh := s.getWorkflowHandler(s.newConfig(dynamicClient)) updateReq := updateFailoverRequest( common.StringPtr(testHistoryArchivalURI), types.ArchivalStatusEnabled.Ptr(), common.StringPtr(testVisibilityArchivalURI), types.ArchivalStatusEnabled.Ptr(), common.Int32Ptr(1), common.StringPtr(cluster.TestAlternativeClusterName), ) resp, err := wh.UpdateDomain(context.Background(), updateReq) s.Nil(resp) s.Error(err) } func (s *workflowHandlerSuite) TestHistoryArchived() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) getHistoryRequest := &types.GetWorkflowExecutionHistoryRequest{} s.False(wh.historyArchived(context.Background(), getHistoryRequest, s.testDomain)) getHistoryRequest = &types.GetWorkflowExecutionHistoryRequest{ Execution: &types.WorkflowExecution{}, } s.False(wh.historyArchived(context.Background(), getHistoryRequest, s.testDomain)) s.mockHistoryClient.EXPECT().GetMutableState(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) getHistoryRequest = &types.GetWorkflowExecutionHistoryRequest{ Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, } s.False(wh.historyArchived(context.Background(), getHistoryRequest, s.testDomain)) s.mockHistoryClient.EXPECT().GetMutableState(gomock.Any(), gomock.Any()).Return(nil, &types.EntityNotExistsError{Message: "got archival indication error"}).Times(1) getHistoryRequest = &types.GetWorkflowExecutionHistoryRequest{ Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, } s.True(wh.historyArchived(context.Background(), getHistoryRequest, s.testDomain)) s.mockHistoryClient.EXPECT().GetMutableState(gomock.Any(), gomock.Any()).Return(nil, errors.New("got non-archival indication error")).Times(1) getHistoryRequest = &types.GetWorkflowExecutionHistoryRequest{ Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, } s.False(wh.historyArchived(context.Background(), getHistoryRequest, s.testDomain)) } func (s *workflowHandlerSuite) TestGetArchivedHistory_Failure_DomainCacheEntryError() { s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(nil, errors.New("error getting domain")).Times(1) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.getArchivedHistory(context.Background(), getHistoryRequest(nil), s.testDomainID) s.Nil(resp) s.Error(err) } func (s *workflowHandlerSuite) TestGetArchivedHistory_Failure_ArchivalURIEmpty() { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomain}, &persistence.DomainConfig{ HistoryArchivalStatus: types.ArchivalStatusDisabled, HistoryArchivalURI: "", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", }, "", ) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(domainEntry, nil).AnyTimes() wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.getArchivedHistory(context.Background(), getHistoryRequest(nil), s.testDomainID) s.Nil(resp) s.Error(err) } func (s *workflowHandlerSuite) TestGetArchivedHistory_Failure_InvalidURI() { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomain}, &persistence.DomainConfig{ HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: "uri without scheme", VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", }, "", ) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(domainEntry, nil).AnyTimes() wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.getArchivedHistory(context.Background(), getHistoryRequest(nil), s.testDomainID) s.Nil(resp) s.Error(err) } func (s *workflowHandlerSuite) TestGetArchivedHistory_Success_GetFirstPage() { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomain}, &persistence.DomainConfig{ HistoryArchivalStatus: types.ArchivalStatusEnabled, HistoryArchivalURI: testHistoryArchivalURI, VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "", }, "", ) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(domainEntry, nil).AnyTimes() nextPageToken := []byte{'1', '2', '3'} historyBatch1 := &types.History{ Events: []*types.HistoryEvent{ {ID: 1}, {ID: 2}, }, } historyBatch2 := &types.History{ Events: []*types.HistoryEvent{ {ID: 3}, {ID: 4}, {ID: 5}, }, } expectedHistory := &types.History{} expectedHistory.Events = append(expectedHistory.Events, historyBatch1.Events...) expectedHistory.Events = append(expectedHistory.Events, historyBatch2.Events...) s.mockHistoryArchiver.On("Get", mock.Anything, mock.Anything, mock.Anything).Return(&archiver.GetHistoryResponse{ NextPageToken: nextPageToken, HistoryBatches: []*types.History{historyBatch1, historyBatch2}, }, nil) s.mockArchiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.mockHistoryArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.getArchivedHistory(context.Background(), getHistoryRequest(nil), s.testDomainID) s.NoError(err) s.NotNil(resp) s.NotNil(resp.History) s.Equal(expectedHistory, resp.History) s.Equal(nextPageToken, resp.NextPageToken) s.True(resp.GetArchived()) } func (s *workflowHandlerSuite) TestGetHistory() { domainID := uuid.New() domainName := uuid.New() firstEventID := int64(100) nextEventID := int64(101) branchToken := []byte{1} we := types.WorkflowExecution{ WorkflowID: "wid", RunID: "rid", } shardID := common.WorkflowIDToHistoryShard(we.WorkflowID, numHistoryShards) req := &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: 0, NextPageToken: []byte{}, ShardID: common.IntPtr(shardID), DomainName: domainName, } s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, req).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { ID: int64(100), }, }, NextPageToken: []byte{}, Size: 1, LastFirstEventID: nextEventID, }, nil).Once() wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) scope := metrics.NoopScope actualHistory, token, err := wh.getHistory(context.Background(), scope, domainID, domainName, we, firstEventID, nextEventID, 0, []byte{}, nil, branchToken) s.NoError(err) s.NotNil(actualHistory) s.Equal([]byte{}, token) } func (s *workflowHandlerSuite) TestListArchivedVisibility_Failure_InvalidRequest() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.ListArchivedWorkflowExecutions(context.Background(), &types.ListArchivedWorkflowExecutionsRequest{}) s.Nil(resp) s.Error(err) } func (s *workflowHandlerSuite) TestListArchivedVisibility_Failure_ClusterNotConfiguredForArchival() { s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewDisabledArchvialConfig()) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.ListArchivedWorkflowExecutions(context.Background(), listArchivedWorkflowExecutionsTestRequest()) s.Nil(resp) s.Error(err) } func (s *workflowHandlerSuite) TestListArchivedVisibility_Failure_DomainCacheEntryError() { s.mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(nil, errors.New("error getting domain")) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "random URI")) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.ListArchivedWorkflowExecutions(context.Background(), listArchivedWorkflowExecutionsTestRequest()) s.Nil(resp) s.Error(err) } func (s *workflowHandlerSuite) TestListArchivedVisibility_Failure_DomainNotConfiguredForArchival() { s.mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewLocalDomainCacheEntryForTest( nil, &persistence.DomainConfig{ VisibilityArchivalStatus: types.ArchivalStatusDisabled, }, "", ), nil) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "random URI")) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.ListArchivedWorkflowExecutions(context.Background(), listArchivedWorkflowExecutionsTestRequest()) s.Nil(resp) s.Error(err) } func (s *workflowHandlerSuite) TestListArchivedVisibility_Failure_InvalidURI() { s.mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomain}, &persistence.DomainConfig{ VisibilityArchivalStatus: types.ArchivalStatusDisabled, VisibilityArchivalURI: "uri without scheme", }, "", ), nil) s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "random URI")) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.ListArchivedWorkflowExecutions(context.Background(), listArchivedWorkflowExecutionsTestRequest()) s.Nil(resp) s.Error(err) } func (s *workflowHandlerSuite) TestListArchivedVisibility_Success() { s.mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomain}, &persistence.DomainConfig{ VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: testVisibilityArchivalURI, }, "", ), nil).AnyTimes() s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "random URI")) s.mockVisibilityArchiver.On("Query", mock.Anything, mock.Anything, mock.Anything).Return(&archiver.QueryVisibilityResponse{}, nil) s.mockArchiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.mockVisibilityArchiver, nil) wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) resp, err := wh.ListArchivedWorkflowExecutions(context.Background(), listArchivedWorkflowExecutionsTestRequest()) s.NotNil(resp) s.NoError(err) } func (s *workflowHandlerSuite) TestGetSearchAttributes() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) ctx := context.Background() resp, err := wh.GetSearchAttributes(ctx) s.NoError(err) s.NotNil(resp) } func (s *workflowHandlerSuite) TestGetWorkflowExecutionHistory__Success__RawHistoryEnabledTransientDecisionEmitted() { var nextEventID int64 = 5 s.getWorkflowExecutionHistory(5, &types.TransientDecisionInfo{ StartedEvent: &types.HistoryEvent{ID: nextEventID + 1}, ScheduledEvent: &types.HistoryEvent{ID: nextEventID}, }, []*types.HistoryEvent{{}, {}, {}}) } func (s *workflowHandlerSuite) TestGetWorkflowExecutionHistory__Success__RawHistoryEnabledNoTransientDecisionEmitted() { var nextEventID int64 = 5 s.getWorkflowExecutionHistory(5, &types.TransientDecisionInfo{ StartedEvent: &types.HistoryEvent{ID: nextEventID + 1}, ScheduledEvent: &types.HistoryEvent{ID: nextEventID}, }, []*types.HistoryEvent{{}, {}, {}}) } func (s *workflowHandlerSuite) TestRestartWorkflowExecution() { testCases := []struct { name string setupMocks func() request *types.RestartWorkflowExecutionRequest setupContext func() context.Context enableTaskListIsolation bool expectError bool expectedErrType interface{} expectedRunID string description string }{ { name: "When request is nil it should return RequestNotSet error", setupMocks: func() {}, request: nil, setupContext: func() context.Context { return context.Background() }, enableTaskListIsolation: false, expectError: true, expectedErrType: validate.ErrRequestNotSet, description: "nil request should be rejected", }, { name: "When domain is empty it should return DomainNotSet error", setupMocks: func() {}, request: &types.RestartWorkflowExecutionRequest{ Domain: "", }, setupContext: func() context.Context { return context.Background() }, enableTaskListIsolation: false, expectError: true, expectedErrType: validate.ErrDomainNotSet, description: "empty domain should be rejected", }, { name: "When workflow execution is nil it should return ExecutionNotSet error", setupMocks: func() {}, request: &types.RestartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: nil, }, setupContext: func() context.Context { return context.Background() }, enableTaskListIsolation: false, expectError: true, expectedErrType: validate.ErrExecutionNotSet, description: "nil workflow execution should be rejected", }, { name: "When workflow ID is empty it should return WorkflowIDNotSet error", setupMocks: func() {}, request: &types.RestartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "", }, }, setupContext: func() context.Context { return context.Background() }, enableTaskListIsolation: false, expectError: true, expectedErrType: validate.ErrWorkflowIDNotSet, description: "empty workflow ID should be rejected", }, { name: "When domain cache returns error it should propagate the error", setupMocks: func() { s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(nil, errors.New("domain cache error")).AnyTimes() s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("", errors.New("domain cache error")) }, request: &types.RestartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, }, }, setupContext: func() context.Context { return context.Background() }, enableTaskListIsolation: false, expectError: true, description: "domain cache errors should be propagated", }, { name: "When isolation group is drained it should return BadRequest error", setupMocks: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockResource.IsolationGroups.EXPECT().IsDrained(gomock.Any(), s.testDomain, "dca1").Return(true, nil) }, request: &types.RestartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, }, Identity: "", }, setupContext: func() context.Context { isolationGroup := "dca1" return isolationgroup.ContextWithIsolationGroup(context.Background(), isolationGroup) }, enableTaskListIsolation: true, expectError: true, expectedErrType: &types.BadRequestError{}, description: "drained isolation group should be rejected", }, { name: "When PollMutableState fails it should propagate the error", setupMocks: func() { s.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return(s.testDomainID, nil).AnyTimes() s.mockHistoryClient.EXPECT().PollMutableState(gomock.Any(), gomock.Any()).Return(nil, errors.New("poll mutable state error")) }, request: &types.RestartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, }, }, setupContext: func() context.Context { return context.Background() }, enableTaskListIsolation: false, expectError: true, description: "PollMutableState errors should be propagated", }, { name: "When history read fails it should propagate the error", setupMocks: func() { s.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return(s.testDomainID, nil).AnyTimes() s.mockHistoryClient.EXPECT().PollMutableState(gomock.Any(), gomock.Any()).Return(&types.PollMutableStateResponse{ CurrentBranchToken: []byte(""), Execution: &types.WorkflowExecution{ WorkflowID: testRunID, }, LastFirstEventID: 0, NextEventID: 2, VersionHistories: &types.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*types.VersionHistory{ { BranchToken: []byte("token"), Items: []*types.VersionHistoryItem{ { EventID: 1, Version: 1, }, }, }, }, }, }, nil).AnyTimes() s.mockVersionChecker.EXPECT().SupportsRawHistoryQuery(gomock.Any(), gomock.Any()).Return(nil).Times(1) s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.Anything).Return(nil, errors.New("history read error")).Once() }, request: &types.RestartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, }, }, setupContext: func() context.Context { return context.Background() }, enableTaskListIsolation: false, expectError: true, description: "history read errors should be propagated", }, { name: "When StartWorkflowExecution fails it should propagate the error", setupMocks: func() { s.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return(s.testDomainID, nil).AnyTimes() s.mockHistoryClient.EXPECT().PollMutableState(gomock.Any(), gomock.Any()).Return(&types.PollMutableStateResponse{ CurrentBranchToken: []byte(""), Execution: &types.WorkflowExecution{ WorkflowID: testRunID, }, LastFirstEventID: 0, NextEventID: 2, VersionHistories: &types.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*types.VersionHistory{ { BranchToken: []byte("token"), Items: []*types.VersionHistoryItem{ { EventID: 1, Version: 1, }, }, }, }, }, }, nil).AnyTimes() s.mockVersionChecker.EXPECT().SupportsRawHistoryQuery(gomock.Any(), gomock.Any()).Return(nil).Times(1) s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.Anything).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{&types.HistoryEvent{ ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{ Name: "workflowtype", }, TaskList: &types.TaskList{ Name: "tasklist", }, }, }}, }, nil).Once() s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, errors.New("start workflow error")) }, request: &types.RestartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, }, }, setupContext: func() context.Context { return context.Background() }, enableTaskListIsolation: false, expectError: true, description: "StartWorkflowExecution errors should be propagated", }, { name: "When all conditions are valid it should successfully restart workflow", setupMocks: func() { s.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return(s.testDomainID, nil).AnyTimes() s.mockHistoryClient.EXPECT().PollMutableState(gomock.Any(), gomock.Any()).Return(&types.PollMutableStateResponse{ CurrentBranchToken: []byte(""), Execution: &types.WorkflowExecution{ WorkflowID: testRunID, }, LastFirstEventID: 0, NextEventID: 2, VersionHistories: &types.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*types.VersionHistory{ { BranchToken: []byte("token"), Items: []*types.VersionHistoryItem{ { EventID: 1, Version: 1, }, }, }, }, }, }, nil).AnyTimes() s.mockVersionChecker.EXPECT().SupportsRawHistoryQuery(gomock.Any(), gomock.Any()).Return(nil).Times(1) s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.Anything).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{&types.HistoryEvent{ ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{ Name: "workflowtype", }, TaskList: &types.TaskList{ Name: "tasklist", }, }, }}, }, nil).Once() s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(&types.StartWorkflowExecutionResponse{ RunID: testRunID, }, nil) }, request: &types.RestartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, }, Identity: "", }, setupContext: func() context.Context { return context.Background() }, enableTaskListIsolation: false, expectError: false, expectedRunID: testRunID, description: "valid request should successfully restart workflow", }, { name: "When ActiveClusterSelectionPolicy is preserved it should copy policy to new workflow", setupMocks: func() { s.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return(s.testDomainID, nil).AnyTimes() s.mockHistoryClient.EXPECT().PollMutableState(gomock.Any(), gomock.Any()).Return(&types.PollMutableStateResponse{ CurrentBranchToken: []byte(""), Execution: &types.WorkflowExecution{ WorkflowID: testRunID, }, LastFirstEventID: 0, NextEventID: 2, VersionHistories: &types.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*types.VersionHistory{ { BranchToken: []byte("token"), Items: []*types.VersionHistoryItem{ { EventID: 1, Version: 1, }, }, }, }, }, }, nil).AnyTimes() s.mockVersionChecker.EXPECT().SupportsRawHistoryQuery(gomock.Any(), gomock.Any()).Return(nil).Times(1) s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.Anything).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{&types.HistoryEvent{ ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{ Name: "workflowtype", }, TaskList: &types.TaskList{ Name: "tasklist", }, ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyRegionSticky.Ptr(), StickyRegion: "us-west-1", }, }, }}, }, nil).Once() // Capture the start request to verify ActiveClusterSelectionPolicy s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, request *types.HistoryStartWorkflowExecutionRequest, opts ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { // Verify ActiveClusterSelectionPolicy is preserved if request.StartRequest.ActiveClusterSelectionPolicy == nil { return nil, errors.New("expected ActiveClusterSelectionPolicy to be preserved") } if *request.StartRequest.ActiveClusterSelectionPolicy.ActiveClusterSelectionStrategy != types.ActiveClusterSelectionStrategyRegionSticky { return nil, errors.New("ActiveClusterSelectionStrategy not preserved") } if request.StartRequest.ActiveClusterSelectionPolicy.StickyRegion != "us-west-1" { return nil, errors.New("StickyRegion not preserved") } return &types.StartWorkflowExecutionResponse{RunID: testRunID}, nil }, ) }, request: &types.RestartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, }, Identity: "", }, setupContext: func() context.Context { return context.Background() }, enableTaskListIsolation: false, expectError: false, expectedRunID: testRunID, description: "ActiveClusterSelectionPolicy should be preserved in restarted workflow", }, } for _, tc := range testCases { s.Run(tc.name, func() { // Setup dynamic client dynamicClient := dc.NewInMemoryClient() err := dynamicClient.UpdateValue(dynamicproperties.SendRawWorkflowHistory, false) s.NoError(err) // Setup config config := s.newConfig(dynamicClient) config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomain(tc.enableTaskListIsolation) wh := s.getWorkflowHandler(config) tc.setupMocks() ctx := tc.setupContext() resp, err := wh.RestartWorkflowExecution(ctx, tc.request) if tc.expectError { s.Error(err, tc.description) if tc.expectedErrType != nil { s.IsType(tc.expectedErrType, err) } } else { s.NoError(err, tc.description) s.NotNil(resp) if tc.expectedRunID != "" { s.Equal(tc.expectedRunID, resp.GetRunID()) } } }) } } func (s *workflowHandlerSuite) getWorkflowExecutionHistory(nextEventID int64, transientDecision *types.TransientDecisionInfo, historyEvents []*types.HistoryEvent) { dynamicClient := dc.NewInMemoryClient() err := dynamicClient.UpdateValue(dynamicproperties.SendRawWorkflowHistory, true) s.NoError(err) wh := s.getWorkflowHandler( frontendcfg.NewConfig( dc.NewCollection( dynamicClient, s.mockResource.GetLogger()), numHistoryShards, false, "hostname", s.mockResource.GetLogger(), ), ) ctx := context.Background() domainEntry := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ID: s.testDomainID, Name: s.testDomain}, &persistence.DomainConfig{}, true, nil, 0, nil, 0, 0, 0) s.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return(s.testDomainID, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(domainEntry, nil).AnyTimes() s.mockVersionChecker.EXPECT().SupportsRawHistoryQuery(gomock.Any(), gomock.Any()).Return(nil).Times(1) blob, _ := wh.GetPayloadSerializer().SerializeBatchEvents(historyEvents, constants.EncodingTypeThriftRW) s.mockHistoryV2Mgr.On("ReadRawHistoryBranch", mock.Anything, mock.Anything).Return(&persistence.ReadRawHistoryBranchResponse{ HistoryEventBlobs: []*persistence.DataBlob{blob}, NextPageToken: []byte{}, }, nil).Once() token, _ := json.Marshal(&getHistoryContinuationToken{ FirstEventID: 1, NextEventID: nextEventID, RunID: testRunID, TransientDecision: transientDecision, }) resp, err := wh.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: s.testDomain, Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, SkipArchival: true, NextPageToken: token, }) s.NoError(err) s.NotNil(resp) s.NotNil(resp.RawHistory) s.Equal(2, len(resp.RawHistory)) events := deserializeBlobDataToHistoryEvents(wh, resp.RawHistory) s.NotNil(events) if transientDecision != nil { s.Equal(len(historyEvents)+2, len(events)) } else { s.Equal(len(historyEvents), len(events)) } } func deserializeBlobDataToHistoryEvents(wh *WorkflowHandler, dataBlobs []*types.DataBlob) []*types.HistoryEvent { var historyEvents []*types.HistoryEvent for _, batch := range dataBlobs { events, err := wh.GetPayloadSerializer().DeserializeBatchEvents(&persistence.DataBlob{Data: batch.Data, Encoding: constants.EncodingTypeThriftRW}) if err != nil { return nil } historyEvents = append(historyEvents, events...) } return historyEvents } func (s *workflowHandlerSuite) TestListWorkflowExecutions() { config := s.newConfig(dc.NewInMemoryClient()) wh := s.getWorkflowHandler(config) s.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return(s.testDomainID, nil).AnyTimes() s.mockVisibilityMgr.On("ListWorkflowExecutions", mock.Anything, mock.Anything).Return(&persistence.ListWorkflowExecutionsResponse{}, nil).Once() listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.testDomain, PageSize: int32(config.ESIndexMaxResultWindow()), } ctx := context.Background() query := "WorkflowID = 'wid'" listRequest.Query = query _, err := wh.ListWorkflowExecutions(ctx, listRequest) s.NoError(err) s.Equal(query, listRequest.GetQuery()) query = "InvalidKey = 'a'" listRequest.Query = query _, err = wh.ListWorkflowExecutions(ctx, listRequest) s.NotNil(err) listRequest.PageSize = int32(config.ESIndexMaxResultWindow() + 1) _, err = wh.ListWorkflowExecutions(ctx, listRequest) s.NotNil(err) } func (s *workflowHandlerSuite) TestScantWorkflowExecutions() { config := s.newConfig(dc.NewInMemoryClient()) wh := s.getWorkflowHandler(config) s.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return(s.testDomainID, nil).AnyTimes() s.mockVisibilityMgr.On("ScanWorkflowExecutions", mock.Anything, mock.Anything).Return(&persistence.ListWorkflowExecutionsResponse{}, nil).Once() listRequest := &types.ListWorkflowExecutionsRequest{ Domain: s.testDomain, PageSize: int32(config.ESIndexMaxResultWindow()), } ctx := context.Background() query := "WorkflowID = 'wid'" listRequest.Query = query _, err := wh.ScanWorkflowExecutions(ctx, listRequest) s.NoError(err) s.Equal(query, listRequest.GetQuery()) query = "InvalidKey = 'a'" listRequest.Query = query _, err = wh.ScanWorkflowExecutions(ctx, listRequest) s.NotNil(err) listRequest.PageSize = int32(config.ESIndexMaxResultWindow() + 1) _, err = wh.ListWorkflowExecutions(ctx, listRequest) s.NotNil(err) } func (s *workflowHandlerSuite) TestCountWorkflowExecutions() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) s.mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return(s.testDomainID, nil).AnyTimes() s.mockVisibilityMgr.On("CountWorkflowExecutions", mock.Anything, mock.Anything).Return(&persistence.CountWorkflowExecutionsResponse{}, nil).Once() countRequest := &types.CountWorkflowExecutionsRequest{ Domain: s.testDomain, } ctx := context.Background() query := "WorkflowID = 'wid'" countRequest.Query = query _, err := wh.CountWorkflowExecutions(ctx, countRequest) s.NoError(err) s.Equal(query, countRequest.GetQuery()) query = "InvalidKey = 'a'" countRequest.Query = query _, err = wh.CountWorkflowExecutions(ctx, countRequest) s.NotNil(err) } func (s *workflowHandlerSuite) TestConvertIndexedKeyToThrift() { wh := s.getWorkflowHandler(s.newConfig(dc.NewInMemoryClient())) m := map[string]interface{}{ "key1": float64(0), "key2": float64(1), "key3": float64(2), "key4": float64(3), "key5": float64(4), "key6": float64(5), "key1i": 0, "key2i": 1, "key3i": 2, "key4i": 3, "key5i": 4, "key6i": 5, "key1t": types.IndexedValueTypeString, "key2t": types.IndexedValueTypeKeyword, "key3t": types.IndexedValueTypeInt, "key4t": types.IndexedValueTypeDouble, "key5t": types.IndexedValueTypeBool, "key6t": types.IndexedValueTypeDatetime, "key1s": "STRING", "key2s": "KEYWORD", "key3s": "INT", "key4s": "DOUBLE", "key5s": "BOOL", "key6s": "DATETIME", } result := wh.convertIndexedKeyToThrift(m) s.Equal(types.IndexedValueTypeString, result["key1"]) s.Equal(types.IndexedValueTypeKeyword, result["key2"]) s.Equal(types.IndexedValueTypeInt, result["key3"]) s.Equal(types.IndexedValueTypeDouble, result["key4"]) s.Equal(types.IndexedValueTypeBool, result["key5"]) s.Equal(types.IndexedValueTypeDatetime, result["key6"]) s.Equal(types.IndexedValueTypeString, result["key1i"]) s.Equal(types.IndexedValueTypeKeyword, result["key2i"]) s.Equal(types.IndexedValueTypeInt, result["key3i"]) s.Equal(types.IndexedValueTypeDouble, result["key4i"]) s.Equal(types.IndexedValueTypeBool, result["key5i"]) s.Equal(types.IndexedValueTypeDatetime, result["key6i"]) s.Equal(types.IndexedValueTypeString, result["key1t"]) s.Equal(types.IndexedValueTypeKeyword, result["key2t"]) s.Equal(types.IndexedValueTypeInt, result["key3t"]) s.Equal(types.IndexedValueTypeDouble, result["key4t"]) s.Equal(types.IndexedValueTypeBool, result["key5t"]) s.Equal(types.IndexedValueTypeDatetime, result["key6t"]) s.Equal(types.IndexedValueTypeString, result["key1s"]) s.Equal(types.IndexedValueTypeKeyword, result["key2s"]) s.Equal(types.IndexedValueTypeInt, result["key3s"]) s.Equal(types.IndexedValueTypeDouble, result["key4s"]) s.Equal(types.IndexedValueTypeBool, result["key5s"]) s.Equal(types.IndexedValueTypeDatetime, result["key6s"]) s.Panics(func() { wh.convertIndexedKeyToThrift(map[string]interface{}{ "invalidType": "unknown", }) }) } func (s *workflowHandlerSuite) TestVerifyHistoryIsComplete() { events := make([]*types.HistoryEvent, 50) for i := 0; i < len(events); i++ { events[i] = &types.HistoryEvent{ID: int64(i + 1)} } var eventsWithHoles []*types.HistoryEvent eventsWithHoles = append(eventsWithHoles, events[9:12]...) eventsWithHoles = append(eventsWithHoles, events[20:31]...) testCases := []struct { events []*types.HistoryEvent firstEventID int64 lastEventID int64 isFirstPage bool isLastPage bool pageSize int isResultErr bool }{ {events[:1], 1, 1, true, true, 1000, false}, {events[:5], 1, 5, true, true, 1000, false}, {events[9:31], 10, 31, true, true, 1000, false}, {events[9:29], 10, 50, true, false, 20, false}, {events[9:30], 10, 50, true, false, 20, false}, {events[9:29], 1, 50, false, false, 20, false}, {events[9:29], 1, 29, false, true, 20, false}, {eventsWithHoles, 1, 50, false, false, 22, true}, {eventsWithHoles, 10, 50, true, false, 22, true}, {eventsWithHoles, 1, 31, false, true, 22, true}, {eventsWithHoles, 10, 31, true, true, 1000, true}, {events[9:31], 9, 31, true, true, 1000, true}, {events[9:31], 9, 50, true, false, 22, true}, {events[9:31], 11, 31, true, true, 1000, true}, {events[9:31], 11, 50, true, false, 22, true}, {events[9:31], 10, 30, true, true, 1000, true}, {events[9:31], 1, 30, false, true, 22, true}, {events[9:31], 10, 32, true, true, 1000, true}, {events[9:31], 1, 32, false, true, 22, true}, } for i, tc := range testCases { err := verifyHistoryIsComplete(tc.events, tc.firstEventID, tc.lastEventID, tc.isFirstPage, tc.isLastPage, tc.pageSize) if tc.isResultErr { s.Error(err, "testcase %v failed", i) } else { s.NoError(err, "testcase %v failed", i) } } } func (s *workflowHandlerSuite) newConfig(dynamicClient dc.Client) *frontendcfg.Config { config := frontendcfg.NewConfig( dc.NewCollection( dynamicClient, s.mockResource.GetLogger(), ), numHistoryShards, false, "hostname", s.mockResource.GetLogger(), ) config.EmitSignalNameMetricsTag = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) return config } func (s *workflowHandlerSuite) TestRespondActivityTaskFailedByID() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer validRequest := &types.RespondActivityTaskFailedByIDRequest{ Domain: s.testDomain, WorkflowID: testWorkflowID, RunID: testRunID, ActivityID: "activityID", Identity: "identity", Details: make([]byte, 1000), } testInput := map[string]struct { request *types.RespondActivityTaskFailedByIDRequest expectError bool mockFn func() }{ "shutting down": { request: validRequest, mockFn: func() { wh.shuttingDown = int32(1) }, expectError: true, }, "nil request": { request: nil, mockFn: func() {}, expectError: true, }, "empty domain": { request: &types.RespondActivityTaskFailedByIDRequest{ Domain: "", }, mockFn: func() {}, expectError: true, }, "cannot get domain ID": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("", errors.New("error getting domain ID")) }, expectError: true, }, "empty domain ID": { request: &types.RespondActivityTaskFailedByIDRequest{ Domain: s.testDomain, WorkflowID: "", }, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("", nil) }, expectError: true, }, "empty workflow ID": { request: &types.RespondActivityTaskFailedByIDRequest{ Domain: s.testDomain, WorkflowID: "", }, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) }, expectError: true, }, "empty activity ID": { request: &types.RespondActivityTaskFailedByIDRequest{ Domain: s.testDomain, WorkflowID: testWorkflowID, ActivityID: "", }, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) }, expectError: true, }, "exceeds id length limit": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) wh.config.MaxIDLengthWarnLimit = dynamicproperties.GetIntPropertyFn(1) wh.config.IdentityMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1) }, expectError: true, }, "serialzation failure": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockTokenSerializer.EXPECT().Serialize(gomock.Any()).Return(nil, errors.New("failed to deserialize token")) }, expectError: true, }, "return exceeds blob size limit": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockTokenSerializer.EXPECT().Serialize(gomock.Any()).Return(make([]byte, 100), nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1) s.mockHistoryClient.EXPECT().RespondActivityTaskFailed(gomock.Any(), gomock.Any()).Return(nil) }, expectError: false, }, "history client returns error": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockTokenSerializer.EXPECT().Serialize(gomock.Any()).Return(make([]byte, 100), nil) s.mockHistoryClient.EXPECT().RespondActivityTaskFailed(gomock.Any(), gomock.Any()).Return(errors.New("error")) }, expectError: true, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := wh.RespondActivityTaskFailedByID(context.Background(), input.request) if input.expectError { s.Error(err) } else { s.NoError(err) } wh.shuttingDown = int32(0) wh.config.MaxIDLengthWarnLimit = dynamicproperties.GetIntPropertyFn(1000) wh.config.IdentityMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1000) }) } } func (s *workflowHandlerSuite) TestRespondActivityTaskCanceled() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer validInput := &types.RespondActivityTaskCanceledRequest{ TaskToken: []byte("token"), Identity: "identity", Details: make([]byte, 1000), } testInput := map[string]struct { input *types.RespondActivityTaskCanceledRequest mockFn func() expectError bool }{ "shutting down": { input: validInput, mockFn: func() { wh.shuttingDown = int32(1) }, expectError: true, }, "nil request": { input: nil, mockFn: func() {}, expectError: true, }, "empty task token": { input: &types.RespondActivityTaskCanceledRequest{ TaskToken: nil, }, mockFn: func() {}, expectError: true, }, "deserialzation failure": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(nil, errors.New("failed to deserialize token")) }, expectError: true, }, "empty domain ID": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: ""}, nil) }, expectError: true, }, "cannot get domain name": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return("", errors.New("error getting domain name")) }, expectError: true, }, "exceeds id length limit": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.MaxIDLengthWarnLimit = dynamicproperties.GetIntPropertyFn(1) wh.config.IdentityMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1) }, expectError: true, }, "exceeds blob size limit": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1) s.mockHistoryClient.EXPECT().RespondActivityTaskFailed(gomock.Any(), gomock.Any()).Return(errors.New("error")) }, expectError: true, }, "history client returns error": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1000) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1000) s.mockHistoryClient.EXPECT().RespondActivityTaskCanceled(gomock.Any(), gomock.Any()).Return(errors.New("error")) }, expectError: true, }, "no error": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1000) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1000) s.mockHistoryClient.EXPECT().RespondActivityTaskCanceled(gomock.Any(), gomock.Any()).Return(nil) }, expectError: false, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := wh.RespondActivityTaskCanceled(context.Background(), input.input) if input.expectError { s.Error(err) } else { s.NoError(err) } wh.shuttingDown = int32(0) wh.config.MaxIDLengthWarnLimit = dynamicproperties.GetIntPropertyFn(1000) wh.config.IdentityMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1000) }) } } func (s *workflowHandlerSuite) TestRespondActivityTaskCanceledByID() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer validInput := &types.RespondActivityTaskCanceledByIDRequest{ Domain: s.testDomain, WorkflowID: testWorkflowID, RunID: testRunID, ActivityID: "activityID", Identity: "identity", Details: make([]byte, 1000), } testInput := map[string]struct { request *types.RespondActivityTaskCanceledByIDRequest expectError bool mockFn func() }{ "shutting down": { request: validInput, mockFn: func() { wh.shuttingDown = int32(1) }, expectError: true, }, "nil request": { request: nil, mockFn: func() {}, expectError: true, }, "empty domain name": { request: &types.RespondActivityTaskCanceledByIDRequest{ Domain: "", }, mockFn: func() {}, expectError: true, }, "cannot get domain ID": { request: validInput, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("", errors.New("error getting domain ID")) }, expectError: true, }, "empty domain ID": { request: &types.RespondActivityTaskCanceledByIDRequest{ Domain: s.testDomain, }, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("", nil) }, expectError: true, }, "empty workflow ID": { request: &types.RespondActivityTaskCanceledByIDRequest{ Domain: s.testDomain, WorkflowID: "", }, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) }, expectError: true, }, "empty activity ID": { request: &types.RespondActivityTaskCanceledByIDRequest{ Domain: s.testDomain, WorkflowID: testWorkflowID, ActivityID: "", }, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) }, expectError: true, }, "exceeds id length limit": { request: validInput, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) wh.config.MaxIDLengthWarnLimit = dynamicproperties.GetIntPropertyFn(1) wh.config.IdentityMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1) }, expectError: true, }, "serialization failure": { request: validInput, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockTokenSerializer.EXPECT().Serialize(gomock.Any()).Return(nil, errors.New("failed to deserialize token")) }, expectError: true, }, "exceeds blob size limit": { request: validInput, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockTokenSerializer.EXPECT().Serialize(gomock.Any()).Return(make([]byte, 100), nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(10) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(10) s.mockHistoryClient.EXPECT().RespondActivityTaskFailed(gomock.Any(), gomock.Any()).Return(errors.New("error")) }, expectError: true, }, "history client returns error": { request: validInput, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockTokenSerializer.EXPECT().Serialize(gomock.Any()).Return(make([]byte, 5), nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1000) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1000) s.mockHistoryClient.EXPECT().RespondActivityTaskCanceled(gomock.Any(), gomock.Any()).Return(errors.New("error")) }, expectError: true, }, "success": { request: validInput, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockTokenSerializer.EXPECT().Serialize(gomock.Any()).Return(make([]byte, 5), nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1000) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1000) s.mockHistoryClient.EXPECT().RespondActivityTaskCanceled(gomock.Any(), gomock.Any()).Return(nil) }, expectError: false, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := wh.RespondActivityTaskCanceledByID(context.Background(), input.request) if input.expectError { s.Error(err) } else { s.NoError(err) } wh.shuttingDown = int32(0) wh.config.MaxIDLengthWarnLimit = dynamicproperties.GetIntPropertyFn(1000) wh.config.IdentityMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1000) }) } } func (s *workflowHandlerSuite) TestRespondDecisionTaskCompleted() { validRequest := &types.RespondDecisionTaskCompletedRequest{ TaskToken: []byte("token"), Identity: "identity", Decisions: make([]*types.Decision, 100), } mockResp := &types.HistoryRespondDecisionTaskCompletedResponse{ StartedResponse: &types.RecordDecisionTaskStartedResponse{ Attempt: 1, ScheduledEventID: 2, }, } config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer testInput := map[string]struct { input *types.RespondDecisionTaskCompletedRequest mockFn func() expectError bool expectErrorType error }{ "shutting down": { input: validRequest, mockFn: func() { wh.shuttingDown = int32(1) }, expectError: true, expectErrorType: validate.ErrShuttingDown, }, "nil request": { input: nil, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrRequestNotSet, }, "nil task token": { input: &types.RespondDecisionTaskCompletedRequest{ TaskToken: nil, }, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrTaskTokenNotSet, }, "deserialization failure": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(nil, errors.New("failed to deserialize token")) }, expectError: true, }, "empty domain ID": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: ""}, nil) }, expectError: true, expectErrorType: validate.ErrDomainNotSet, }, "cannot get domain name": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return("", errors.New("error getting domain name")) }, expectError: true, }, "exceeds id length limit": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.MaxIDLengthWarnLimit = dynamicproperties.GetIntPropertyFn(1) wh.config.IdentityMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1) }, expectError: true, expectErrorType: validate.ErrIdentityTooLong, }, "exceeds decision size limit": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.DecisionResultCountLimit = dynamicproperties.GetIntPropertyFilteredByDomain(10) }, expectError: true, }, "history client returns error": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) s.mockHistoryClient.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), gomock.Any()).Return(nil, errors.New("error")) }, expectError: true, }, "no error": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) s.mockHistoryClient.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), gomock.Any()).Return(mockResp, nil) }, expectError: false, }, "return new decision task true": { input: &types.RespondDecisionTaskCompletedRequest{ TaskToken: []byte("token"), Identity: "identity", Decisions: make([]*types.Decision, 100), ReturnNewDecisionTask: true, }, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockTokenSerializer.EXPECT().Serialize(gomock.Any()).Return([]byte("new task token"), nil) s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(s.testDomain, nil).Times(2) s.mockHistoryClient.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), gomock.Any()).Return(mockResp, nil) s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.Anything).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{}, NextPageToken: []byte{}, Size: 0, LastFirstEventID: 1, }, nil).Once() }, expectError: false, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() _, err := wh.RespondDecisionTaskCompleted(context.Background(), input.input) if input.expectError { s.Error(err) if input.expectErrorType != nil { s.ErrorIs(err, input.expectErrorType) } } else { s.NoError(err) } wh.shuttingDown = int32(0) wh.config.MaxIDLengthWarnLimit = dynamicproperties.GetIntPropertyFn(1000) wh.config.IdentityMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1000) wh.config.DecisionResultCountLimit = dynamicproperties.GetIntPropertyFilteredByDomain(1000) }) } } func (s *workflowHandlerSuite) TestRespondDecisionTaskFailed() { validRequest := &types.RespondDecisionTaskFailedRequest{ TaskToken: []byte("token"), Cause: types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure.Ptr(), Identity: "identity", Details: make([]byte, 1000), } config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer testInput := map[string]struct { input *types.RespondDecisionTaskFailedRequest mockFn func() expectError bool expectErrorType error }{ "shutting down": { input: validRequest, mockFn: func() { wh.shuttingDown = int32(1) }, expectError: true, expectErrorType: validate.ErrShuttingDown, }, "nil request": { input: nil, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrRequestNotSet, }, "nil task token": { input: &types.RespondDecisionTaskFailedRequest{ TaskToken: nil, }, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrTaskTokenNotSet, }, "deserialization failure": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(nil, errors.New("failed to deserialize token")) }, expectError: true, }, "empty domain ID": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: ""}, nil) }, expectError: true, expectErrorType: validate.ErrDomainNotSet, }, "cannot get domain name": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return("", errors.New("error getting domain name")) }, expectError: true, }, "exceeds id length limit": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.MaxIDLengthWarnLimit = dynamicproperties.GetIntPropertyFn(1) wh.config.IdentityMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1) }, expectError: true, expectErrorType: validate.ErrIdentityTooLong, }, "exceeds blob size limit": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1) s.mockHistoryClient.EXPECT().RespondDecisionTaskFailed(gomock.Any(), gomock.Any()).Return(nil) }, expectError: false, }, "history client returns error": { input: validRequest, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{DomainID: s.testDomainID}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1000) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1000) s.mockHistoryClient.EXPECT().RespondDecisionTaskFailed(gomock.Any(), gomock.Any()).Return(errors.New("error")) }, expectError: true, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := wh.RespondDecisionTaskFailed(context.Background(), input.input) if input.expectError { s.Error(err) if input.expectErrorType != nil { s.ErrorIs(err, input.expectErrorType) } } else { s.NoError(err) } wh.shuttingDown = int32(0) wh.config.MaxIDLengthWarnLimit = dynamicproperties.GetIntPropertyFn(1000) wh.config.IdentityMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1000) }) } } func (s *workflowHandlerSuite) TestRespondQueryTaskCompleted() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer validInput := &types.RespondQueryTaskCompletedRequest{ TaskToken: []byte("token"), QueryResult: []byte(`{"result": "result"}`), } testInput := map[string]struct { input *types.RespondQueryTaskCompletedRequest mockFn func() expectError bool expectErrorType error }{ "shutting down": { input: validInput, mockFn: func() { wh.shuttingDown = int32(1) }, expectError: true, expectErrorType: validate.ErrShuttingDown, }, "nil request": { input: nil, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrRequestNotSet, }, "empty task token": { input: &types.RespondQueryTaskCompletedRequest{ TaskToken: nil, }, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrTaskTokenNotSet, }, "deserialzation failure": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().DeserializeQueryTaskToken(gomock.Any()).Return(nil, errors.New("failed to deserialize token")) }, expectError: true, }, "empty domain ID": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().DeserializeQueryTaskToken(gomock.Any()).Return(&common.QueryTaskToken{DomainID: ""}, nil) }, expectError: true, expectErrorType: validate.ErrInvalidTaskToken, }, "cannot get domain name": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().DeserializeQueryTaskToken(gomock.Any()).Return(&common.QueryTaskToken{ DomainID: s.testDomainID, TaskList: "tasklist", TaskID: "taskID"}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return("", errors.New("error getting domain name")) }, expectError: true, }, "exceed blob size limit and success": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().DeserializeQueryTaskToken(gomock.Any()).Return(&common.QueryTaskToken{ DomainID: s.testDomainID, TaskList: "tasklist", TaskID: "taskID"}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1) s.mockMatchingClient.EXPECT().RespondQueryTaskCompleted(gomock.Any(), gomock.Any()).Return(nil) }, expectError: false, }, "matching client returns error": { input: validInput, mockFn: func() { s.mockTokenSerializer.EXPECT().DeserializeQueryTaskToken(gomock.Any()).Return(&common.QueryTaskToken{ DomainID: s.testDomainID, TaskList: "tasklist", TaskID: "taskID"}, nil) s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return(s.testDomain, nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1000) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1000) s.mockMatchingClient.EXPECT().RespondQueryTaskCompleted(gomock.Any(), gomock.Any()).Return(errors.New("error")) }, expectError: true, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := wh.RespondQueryTaskCompleted(context.Background(), input.input) if input.expectError { s.Error(err) if input.expectErrorType != nil { s.ErrorIs(err, input.expectErrorType) } } else { s.NoError(err) } wh.shuttingDown = int32(0) }) } } func (s *workflowHandlerSuite) TestStartWorkflowExecution_Remaining() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer validRequest := &types.StartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: "wid", RequestID: testRunID, WorkflowType: &types.WorkflowType{ Name: "wType", }, TaskList: &types.TaskList{ Name: "tasklist", }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(100), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(100), } testInput := map[string]struct { Request *types.StartWorkflowExecutionRequest MockFn func() ExpectError bool ExpectErrorType error }{ "shutting down": { Request: validRequest, MockFn: func() { wh.shuttingDown = int32(1) }, ExpectError: true, ExpectErrorType: validate.ErrShuttingDown, }, "cannot get domain ID": { Request: validRequest, MockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("domainid", nil) s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("", errors.New("error getting domain ID")) }, ExpectError: true, }, "history client returns error": { Request: validRequest, MockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, errors.New("error")) }, ExpectError: true, }, "success": { Request: validRequest, MockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, nil) }, ExpectError: false, }, } for name, input := range testInput { s.Run(name, func() { input.MockFn() _, err := wh.StartWorkflowExecution(context.Background(), input.Request) if input.ExpectError { s.Error(err) if input.ExpectErrorType != nil { s.Equal(input.ExpectErrorType, err) } } else { s.NoError(err) } wh.shuttingDown = int32(0) }) } } func (s *workflowHandlerSuite) TestSignalWorkflowExecution() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer validRequest := &types.SignalWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, SignalName: "signal", RequestID: testRunID, Input: make([]byte, 1000), } testInput := map[string]struct { request *types.SignalWorkflowExecutionRequest expectError bool mockFn func() expectErrorType error }{ "shutting down": { request: validRequest, mockFn: func() { wh.shuttingDown = int32(1) }, expectError: true, expectErrorType: validate.ErrShuttingDown, }, "nil request": { request: nil, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrRequestNotSet, }, "empty domain": { request: &types.SignalWorkflowExecutionRequest{ Domain: "", }, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrDomainNotSet, }, "empty workflow ID": { request: &types.SignalWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "", }, }, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrWorkflowIDNotSet, }, "domain length exceeds limit": { request: validRequest, mockFn: func() { wh.config.DomainNameMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1) }, expectError: true, expectErrorType: validate.ErrDomainTooLong, }, "empty signal name": { request: &types.SignalWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, SignalName: "", }, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrSignalNameNotSet, }, "signal name length exceeds limit": { request: validRequest, mockFn: func() { wh.config.SignalNameMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1) }, expectError: true, expectErrorType: validate.ErrSignalNameTooLong, }, "requestID length exceeds limit": { request: validRequest, mockFn: func() { wh.config.RequestIDMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(1) }, expectError: true, expectErrorType: validate.ErrRequestIDTooLong, }, "cannot get domain ID": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("", errors.New("error getting domain ID")) }, expectError: true, }, "input exceeds blob size limit": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1) }, expectError: true, }, "history client returns error": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockHistoryClient.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()).Return(errors.New("error")) }, expectError: true, }, "success": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockHistoryClient.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil) }, expectError: false, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := wh.SignalWorkflowExecution(context.Background(), input.request) if input.expectError { s.Error(err) if input.expectErrorType != nil { s.ErrorIs(err, input.expectErrorType) } } else { s.NoError(err) } wh.shuttingDown = int32(0) wh.config.DomainNameMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(200) wh.config.SignalNameMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(200) wh.config.RequestIDMaxLength = dynamicproperties.GetIntPropertyFilteredByDomain(200) wh.config.BlobSizeLimitWarn = dynamicproperties.GetIntPropertyFilteredByDomain(1000) wh.config.BlobSizeLimitError = dynamicproperties.GetIntPropertyFilteredByDomain(1000) }) } } func updateRequest( historyArchivalURI *string, historyArchivalStatus *types.ArchivalStatus, visibilityArchivalURI *string, visibilityArchivalStatus *types.ArchivalStatus, ) *types.UpdateDomainRequest { return &types.UpdateDomainRequest{ Name: "test-name", HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, } } func updateFailoverRequest( historyArchivalURI *string, historyArchivalStatus *types.ArchivalStatus, visibilityArchivalURI *string, visibilityArchivalStatus *types.ArchivalStatus, failoverTimeoutInSeconds *int32, activeClusterName *string, ) *types.UpdateDomainRequest { return &types.UpdateDomainRequest{ Name: "test-name", HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, FailoverTimeoutInSeconds: failoverTimeoutInSeconds, ActiveClusterName: activeClusterName, } } func persistenceGetDomainResponse(historyArchivalState, visibilityArchivalState *domain.ArchivalState) *persistence.GetDomainResponse { return &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ ID: "test-id", Name: "test-name", Status: 0, Description: "test-description", OwnerEmail: "test-owner-email", Data: make(map[string]string), }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: historyArchivalState.Status, HistoryArchivalURI: historyArchivalState.URI, VisibilityArchivalStatus: visibilityArchivalState.Status, VisibilityArchivalURI: visibilityArchivalState.URI, }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: cluster.TestCurrentClusterName, }, }, }, IsGlobalDomain: false, ConfigVersion: 0, FailoverVersion: 0, FailoverNotificationVersion: 0, NotificationVersion: 0, } } func persistenceGetDomainResponseForFailoverTest(historyArchivalState, visibilityArchivalState *domain.ArchivalState) *persistence.GetDomainResponse { return &persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ ID: "test-id", Name: "test-name", Status: 0, Description: "test-description", OwnerEmail: "test-owner-email", Data: make(map[string]string), }, Config: &persistence.DomainConfig{ Retention: 1, EmitMetric: true, HistoryArchivalStatus: historyArchivalState.Status, HistoryArchivalURI: historyArchivalState.URI, VisibilityArchivalStatus: visibilityArchivalState.Status, VisibilityArchivalURI: visibilityArchivalState.URI, }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: cluster.TestAlternativeClusterName, }, }, }, IsGlobalDomain: true, ConfigVersion: 0, FailoverVersion: 0, FailoverNotificationVersion: 0, NotificationVersion: 0, } } func registerDomainRequest( historyArchivalStatus *types.ArchivalStatus, historyArchivalURI string, visibilityArchivalStatus *types.ArchivalStatus, visibilityArchivalURI string, ) *types.RegisterDomainRequest { return &types.RegisterDomainRequest{ Name: "test-domain", Description: "test-description", OwnerEmail: "test-owner-email", WorkflowExecutionRetentionPeriodInDays: 10, EmitMetric: common.BoolPtr(true), Clusters: []*types.ClusterReplicationConfiguration{ { ClusterName: cluster.TestCurrentClusterName, }, }, ActiveClusterName: cluster.TestCurrentClusterName, Data: make(map[string]string), SecurityToken: "token", HistoryArchivalStatus: historyArchivalStatus, HistoryArchivalURI: historyArchivalURI, VisibilityArchivalStatus: visibilityArchivalStatus, VisibilityArchivalURI: visibilityArchivalURI, IsGlobalDomain: false, } } func getHistoryRequest(nextPageToken []byte) *types.GetWorkflowExecutionHistoryRequest { return &types.GetWorkflowExecutionHistoryRequest{ Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, NextPageToken: nextPageToken, } } func listArchivedWorkflowExecutionsTestRequest() *types.ListArchivedWorkflowExecutionsRequest { return &types.ListArchivedWorkflowExecutionsRequest{ Domain: "some random domain name", PageSize: 10, Query: "some random query string", } } var describeDomainResponseServer = &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "test-domain", Description: "a test domain", OwnerEmail: "test@uber.com", }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 3, EmitMetric: true, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*types.ClusterReplicationConfiguration{ { ClusterName: cluster.TestCurrentClusterName, }, { ClusterName: cluster.TestAlternativeClusterName, }, }, }, } func TestStartWorkflowExecutionAsync(t *testing.T) { testCases := []struct { name string setupMocks func(*MockProducerManager) request *types.StartWorkflowExecutionAsyncRequest wantErr bool }{ { name: "Success case", setupMocks: func(mockQueue *MockProducerManager) { mockProducer := &mocks.KafkaProducer{} mockQueue.EXPECT().GetProducerByDomain(gomock.Any()).Return(mockProducer, nil) mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil) }, request: &types.StartWorkflowExecutionAsyncRequest{ StartWorkflowExecutionRequest: &types.StartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, TaskList: &types.TaskList{ Name: "test-task-list", }, Input: []byte("test-input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "test-identity", RequestID: uuid.New(), }, }, wantErr: false, }, { name: "Error case - failed to get async queue producer", setupMocks: func(mockQueue *MockProducerManager) { mockQueue.EXPECT().GetProducerByDomain(gomock.Any()).Return(nil, errors.New("test-error")) }, request: &types.StartWorkflowExecutionAsyncRequest{ StartWorkflowExecutionRequest: &types.StartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, TaskList: &types.TaskList{ Name: "test-task-list", }, Input: []byte("test-input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "test-identity", RequestID: uuid.New(), }, }, wantErr: true, }, { name: "Error case - failed to publish message", setupMocks: func(mockQueue *MockProducerManager) { mockProducer := &mocks.KafkaProducer{} mockQueue.EXPECT().GetProducerByDomain(gomock.Any()).Return(mockProducer, nil) mockProducer.On("Publish", mock.Anything, mock.Anything).Return(errors.New("test-error")) }, request: &types.StartWorkflowExecutionAsyncRequest{ StartWorkflowExecutionRequest: &types.StartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, TaskList: &types.TaskList{ Name: "test-task-list", }, Input: []byte("test-input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "test-identity", RequestID: uuid.New(), }, }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockResource := resource.NewTest(t, mockCtrl, metrics.Frontend) mockResource.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("test-domain-id", nil) mockVersionChecker := client.NewMockVersionChecker(mockCtrl) mockProducerManager := NewMockProducerManager(mockCtrl) cfg := frontendcfg.NewConfig( dc.NewCollection( dc.NewInMemoryClient(), mockResource.GetLogger(), ), numHistoryShards, false, "hostname", mockResource.GetLogger(), ) wh := NewWorkflowHandler(mockResource, cfg, mockVersionChecker, nil) wh.producerManager = mockProducerManager tc.setupMocks(mockProducerManager) _, err := wh.StartWorkflowExecutionAsync(context.Background(), tc.request) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestSignalWithStartWorkflowExecutionAsync(t *testing.T) { testCases := []struct { name string setupMocks func(*MockProducerManager) request *types.SignalWithStartWorkflowExecutionAsyncRequest wantErr bool }{ { name: "Success case", setupMocks: func(mockQueue *MockProducerManager) { mockProducer := &mocks.KafkaProducer{} mockQueue.EXPECT().GetProducerByDomain(gomock.Any()).Return(mockProducer, nil) mockProducer.On("Publish", mock.Anything, mock.Anything).Return(nil) }, request: &types.SignalWithStartWorkflowExecutionAsyncRequest{ SignalWithStartWorkflowExecutionRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, TaskList: &types.TaskList{ Name: "test-task-list", }, Input: []byte("test-input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "test-identity", RequestID: uuid.New(), SignalName: "test-signal-name", }, }, wantErr: false, }, { name: "Error case - failed to get async queue producer", setupMocks: func(mockQueue *MockProducerManager) { mockQueue.EXPECT().GetProducerByDomain(gomock.Any()).Return(nil, errors.New("test-error")) }, request: &types.SignalWithStartWorkflowExecutionAsyncRequest{ SignalWithStartWorkflowExecutionRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, TaskList: &types.TaskList{ Name: "test-task-list", }, Input: []byte("test-input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "test-identity", RequestID: uuid.New(), SignalName: "test-signal-name", }, }, wantErr: true, }, { name: "Error case - failed to publish message", setupMocks: func(mockQueue *MockProducerManager) { mockProducer := &mocks.KafkaProducer{} mockQueue.EXPECT().GetProducerByDomain(gomock.Any()).Return(mockProducer, nil) mockProducer.On("Publish", mock.Anything, mock.Anything).Return(errors.New("test-error")) }, request: &types.SignalWithStartWorkflowExecutionAsyncRequest{ SignalWithStartWorkflowExecutionRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, TaskList: &types.TaskList{ Name: "test-task-list", }, Input: []byte("test-input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "test-identity", RequestID: uuid.New(), SignalName: "test-signal-name", }, }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockResource := resource.NewTest(t, mockCtrl, metrics.Frontend) mockResource.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("test-domain-id", nil) mockVersionChecker := client.NewMockVersionChecker(mockCtrl) mockProducerManager := NewMockProducerManager(mockCtrl) cfg := frontendcfg.NewConfig( dc.NewCollection( dc.NewInMemoryClient(), mockResource.GetLogger(), ), numHistoryShards, false, "hostname", mockResource.GetLogger(), ) wh := NewWorkflowHandler(mockResource, cfg, mockVersionChecker, nil) wh.producerManager = mockProducerManager tc.setupMocks(mockProducerManager) _, err := wh.SignalWithStartWorkflowExecutionAsync(context.Background(), tc.request) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestRequestCancelWorkflowExecution(t *testing.T) { testCases := []struct { name string setupMocks func(checkerMock *client.MockVersionChecker, mockResource *resource.Test) cancelRequest *types.RequestCancelWorkflowExecutionRequest shuttingDown int32 wantErr bool err error }{ { name: "Success case", setupMocks: func(mockVersionChecker *client.MockVersionChecker, mockResource *resource.Test) { mockResource.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("test-domain-id", nil).Times(1) mockResource.HistoryClient.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, cancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Domain: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, }, }, { name: "Error case - is shutting down", setupMocks: func(_ *client.MockVersionChecker, _ *resource.Test) {}, shuttingDown: 1, err: validate.ErrShuttingDown, }, { name: "Error case - error request not set", setupMocks: func(mockVersionChecker *client.MockVersionChecker, _ *resource.Test) { }, err: validate.ErrRequestNotSet, }, { name: "Error case - domain name not set", setupMocks: func(mockVersionChecker *client.MockVersionChecker, _ *resource.Test) { }, cancelRequest: &types.RequestCancelWorkflowExecutionRequest{}, err: validate.ErrDomainNotSet, }, { name: "Error case - check execution error", setupMocks: func(mockVersionChecker *client.MockVersionChecker, _ *resource.Test) { }, cancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Domain: "test-domain", }, err: validate.ErrExecutionNotSet, }, { name: "Error case - get domain ID error", setupMocks: func(mockVersionChecker *client.MockVersionChecker, mockResource *resource.Test) { mockResource.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("", errors.New("get-domain-id-error")).Times(1) }, cancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Domain: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, }, err: errors.New("get-domain-id-error"), }, { name: "Error case - RequestCancelWorkflowExecution error", setupMocks: func(mockVersionChecker *client.MockVersionChecker, mockResource *resource.Test) { mockResource.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("test-domain-id", nil).Times(1) mockResource.HistoryClient.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), gomock.Any()).Return(errors.New("request-cancel-workflow-execution-error")).Times(1) }, cancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Domain: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, }, err: errors.New("request-cancel-workflow-execution-error"), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockResource := resource.NewTest(t, mockCtrl, metrics.Frontend) mockVersionChecker := client.NewMockVersionChecker(mockCtrl) cfg := frontendcfg.NewConfig( dc.NewCollection( dc.NewInMemoryClient(), mockResource.GetLogger(), ), numHistoryShards, false, "hostname", mockResource.GetLogger(), ) wh := NewWorkflowHandler(mockResource, cfg, mockVersionChecker, nil) wh.shuttingDown = tc.shuttingDown tc.setupMocks(mockVersionChecker, mockResource) err := wh.RequestCancelWorkflowExecution(context.Background(), tc.cancelRequest) if tc.err != nil { assert.Error(t, err) assert.Equal(t, tc.err, err) } else { assert.NoError(t, err) } }) } } func TestQueryWorkflow(t *testing.T) { testCases := []struct { name string setupMocks func(*client.MockVersionChecker, *resource.Test) queryRequest *types.QueryWorkflowRequest inMemoryClient dc.Client isShuttingDown int32 err error }{ { name: "Success case", setupMocks: func(mockVersionChecker *client.MockVersionChecker, resourceMock *resource.Test) { resourceMock.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("test-domain-id", nil).Times(1) resourceMock.HistoryClient.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()).Return( &types.HistoryQueryWorkflowResponse{ Response: &types.QueryWorkflowResponse{ QueryResult: []byte("test-result"), }, }, nil).Times(1) }, inMemoryClient: dc.NewInMemoryClient(), queryRequest: &types.QueryWorkflowRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, Query: &types.WorkflowQuery{ QueryType: "test-query-type", }, }, }, { name: "Error case - is shutting down", setupMocks: func(_ *client.MockVersionChecker, _ *resource.Test) {}, isShuttingDown: 1, err: validate.ErrShuttingDown, }, { name: "Error case - query request not set", setupMocks: func(mockVersionChecker *client.MockVersionChecker, _ *resource.Test) { }, inMemoryClient: dc.NewInMemoryClient(), err: validate.ErrRequestNotSet, }, { name: "Error case - domain not set", setupMocks: func(mockVersionChecker *client.MockVersionChecker, _ *resource.Test) { }, inMemoryClient: dc.NewInMemoryClient(), queryRequest: &types.QueryWorkflowRequest{}, err: validate.ErrDomainNotSet, }, { name: "Error case - check execution error", setupMocks: func(mockVersionChecker *client.MockVersionChecker, _ *resource.Test) { }, inMemoryClient: dc.NewInMemoryClient(), queryRequest: &types.QueryWorkflowRequest{ Domain: "test-domain", }, err: validate.ErrExecutionNotSet, }, { name: "Error case - query disallowed for domain", setupMocks: func(mockVersionChecker *client.MockVersionChecker, resourceMock *resource.Test) { }, inMemoryClient: func() dc.Client { inMemoryClient := dc.NewInMemoryClient() inMemoryClient.UpdateValue(dynamicproperties.DisallowQuery, true) return inMemoryClient }(), queryRequest: &types.QueryWorkflowRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, }, err: validate.ErrQueryDisallowedForDomain, }, { name: "Error case - query not set", setupMocks: func(mockVersionChecker *client.MockVersionChecker, resourceMock *resource.Test) { }, inMemoryClient: dc.NewInMemoryClient(), queryRequest: &types.QueryWorkflowRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, }, err: validate.ErrQueryNotSet, }, { name: "Error case - query type not set", setupMocks: func(mockVersionChecker *client.MockVersionChecker, resourceMock *resource.Test) { }, inMemoryClient: dc.NewInMemoryClient(), queryRequest: &types.QueryWorkflowRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, Query: &types.WorkflowQuery{}, }, err: validate.ErrQueryTypeNotSet, }, { name: "Error case - get domain ID error", setupMocks: func(mockVersionChecker *client.MockVersionChecker, resourceMock *resource.Test) { resourceMock.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("", errors.New("get-domain-id-error")).Times(1) }, inMemoryClient: dc.NewInMemoryClient(), queryRequest: &types.QueryWorkflowRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, Query: &types.WorkflowQuery{ QueryType: "test-query-type", }, }, err: errors.New("get-domain-id-error"), }, { name: "Error case - CheckEventBlobSizeLimit error", setupMocks: func(mockVersionChecker *client.MockVersionChecker, resourceMock *resource.Test) { resourceMock.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("test-domain-id", nil).Times(1) }, inMemoryClient: dc.NewInMemoryClient(), queryRequest: &types.QueryWorkflowRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, Query: &types.WorkflowQuery{ QueryType: "test-query-type", QueryArgs: []byte("test-query-args"), }, }, err: common.ErrBlobSizeExceedsLimit, }, { name: "Error case - QueryWorkflow error", setupMocks: func(mockVersionChecker *client.MockVersionChecker, resourceMock *resource.Test) { resourceMock.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("test-domain-id", nil).Times(1) resourceMock.HistoryClient.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()).Return(nil, errors.New("query-workflow-error")).Times(1) }, inMemoryClient: dc.NewInMemoryClient(), queryRequest: &types.QueryWorkflowRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, Query: &types.WorkflowQuery{ QueryType: "test-query-type", }, }, err: errors.New("query-workflow-error"), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockResource := resource.NewTest(t, mockCtrl, metrics.Frontend) mockVersionChecker := client.NewMockVersionChecker(mockCtrl) cfg := frontendcfg.NewConfig( dc.NewCollection( tc.inMemoryClient, mockResource.GetLogger(), ), numHistoryShards, false, "hostname", mockResource.GetLogger(), ) cfg.BlobSizeLimitError = func(domain string) int { return 10 } cfg.BlobSizeLimitWarn = func(domain string) int { return 9 } wh := NewWorkflowHandler(mockResource, cfg, mockVersionChecker, nil) wh.shuttingDown = tc.isShuttingDown tc.setupMocks(mockVersionChecker, mockResource) queryResult, err := wh.QueryWorkflow(context.Background(), tc.queryRequest) if tc.err != nil { assert.Error(t, err) assert.Equal(t, tc.err, err) } else { assert.NoError(t, err) assert.Equal(t, []byte("test-result"), queryResult.QueryResult) } }) } } func TestDescribeWorkflowExecution(t *testing.T) { resp := &types.DescribeWorkflowExecutionResponse{ ExecutionConfiguration: &types.WorkflowExecutionConfiguration{ TaskList: &types.TaskList{ Name: "test-task-list", }, }, } testCases := []struct { name string setupMocks func(mockVersionChecker *client.MockVersionChecker, mockResource *resource.Test) describeRequest *types.DescribeWorkflowExecutionRequest isShuttingDown int32 err error }{ { name: "Success case", setupMocks: func(mockVersionChecker *client.MockVersionChecker, mockResource *resource.Test) { mockResource.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("test-domain-id", nil).Times(1) mockResource.HistoryClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(resp, nil).Times(1) }, describeRequest: &types.DescribeWorkflowExecutionRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, }, }, { name: "Error case - is shutting down", setupMocks: func(_ *client.MockVersionChecker, _ *resource.Test) {}, isShuttingDown: 1, err: validate.ErrShuttingDown, }, { name: "Error case - describe request not set", setupMocks: func(mockVersionChecker *client.MockVersionChecker, _ *resource.Test) { }, err: validate.ErrRequestNotSet, }, { name: "Error case - domain name not set", setupMocks: func(mockVersionChecker *client.MockVersionChecker, _ *resource.Test) { }, describeRequest: &types.DescribeWorkflowExecutionRequest{}, err: validate.ErrDomainNotSet, }, { name: "Error case - check execution error", setupMocks: func(mockVersionChecker *client.MockVersionChecker, _ *resource.Test) { }, describeRequest: &types.DescribeWorkflowExecutionRequest{ Domain: "test-domain", }, err: validate.ErrExecutionNotSet, }, { name: "Error case - get domain ID error", setupMocks: func(mockVersionChecker *client.MockVersionChecker, mockResource *resource.Test) { mockResource.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("", errors.New("get-domain-id-error")).Times(1) }, describeRequest: &types.DescribeWorkflowExecutionRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, }, err: errors.New("get-domain-id-error"), }, { name: "Error case - DescribeWorkflowExecution error", setupMocks: func(mockVersionChecker *client.MockVersionChecker, mockResource *resource.Test) { mockResource.DomainCache.EXPECT().GetDomainID(gomock.Any()).Return("test-domain-id", nil).Times(1) mockResource.HistoryClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, errors.New("describe-workflow-execution-error")).Times(1) }, describeRequest: &types.DescribeWorkflowExecutionRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", }, }, err: errors.New("describe-workflow-execution-error"), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockResource := resource.NewTest(t, mockCtrl, metrics.Frontend) mockVersionChecker := client.NewMockVersionChecker(mockCtrl) cfg := frontendcfg.NewConfig( dc.NewCollection( dc.NewInMemoryClient(), mockResource.GetLogger(), ), numHistoryShards, false, "hostname", mockResource.GetLogger(), ) wh := NewWorkflowHandler(mockResource, cfg, mockVersionChecker, nil) wh.shuttingDown = tc.isShuttingDown tc.setupMocks(mockVersionChecker, mockResource) describeResponse, err := wh.DescribeWorkflowExecution(context.Background(), tc.describeRequest) if tc.err != nil { assert.Error(t, err) assert.Equal(t, tc.err, err) } else { assert.NoError(t, err) assert.Equal(t, resp, describeResponse) } }) } } func (s *workflowHandlerSuite) TestSignalWithStartWorkflowExecution() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer validRequest := &types.SignalWithStartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: testWorkflowID, Identity: "identity", SignalName: "signal", Input: nil, WorkflowType: &types.WorkflowType{Name: "wType"}, TaskList: &types.TaskList{Name: "taskList"}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(10), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), } testInput := map[string]struct { request *types.SignalWithStartWorkflowExecutionRequest expectError bool mockFn func() }{ "shutting down": { request: validRequest, mockFn: func() { wh.shuttingDown = int32(1) }, expectError: true, }, "nil request": { request: nil, mockFn: func() {}, expectError: true, }, "empty domain": { request: &types.SignalWithStartWorkflowExecutionRequest{ Domain: "", }, mockFn: func() {}, expectError: true, }, "empty workflow ID": { request: &types.SignalWithStartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: "", }, mockFn: func() {}, expectError: true, }, "empty workflow type": { request: &types.SignalWithStartWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowID: testWorkflowID, WorkflowType: nil, }, mockFn: func() {}, expectError: true, }, "cannot get domain ID": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("", errors.New("error getting domain ID")) }, expectError: true, }, "history client error": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil).Times(2) s.mockHistoryClient.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, errors.New("error")) }, expectError: true, }, "success": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil).Times(2) s.mockHistoryClient.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, nil) }, expectError: false, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() _, err := wh.SignalWithStartWorkflowExecution(context.Background(), input.request) if input.expectError { s.Error(err) } else { s.NoError(err) } wh.shuttingDown = int32(0) }) } } func (s *workflowHandlerSuite) TestResetWorkflowExecution() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer validRequest := &types.ResetWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, Reason: "reason", } testInput := map[string]struct { request *types.ResetWorkflowExecutionRequest expectError bool mockFn func() expectErrorType error }{ "shutting down": { request: validRequest, mockFn: func() { wh.shuttingDown = int32(1) }, expectError: true, expectErrorType: validate.ErrShuttingDown, }, "nil request": { request: nil, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrRequestNotSet, }, "empty domain": { request: &types.ResetWorkflowExecutionRequest{ Domain: "", }, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrDomainNotSet, }, "empty workflow ID": { request: &types.ResetWorkflowExecutionRequest{ Domain: s.testDomain, }, mockFn: func() {}, expectError: true, }, "empty workflow execution": { request: &types.ResetWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: nil, }, mockFn: func() {}, expectError: true, }, "cannot get domain ID": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("", errors.New("error getting domain ID")) }, expectError: true, }, "history client error": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockHistoryClient.EXPECT().ResetWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, errors.New("error")) }, expectError: true, }, "success": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockHistoryClient.EXPECT().ResetWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, nil) }, expectError: false, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() _, err := wh.ResetWorkflowExecution(context.Background(), input.request) if input.expectError { s.Error(err) if input.expectErrorType != nil { s.ErrorIs(err, input.expectErrorType) } } else { s.NoError(err) } wh.shuttingDown = int32(0) }) } } func (s *workflowHandlerSuite) TestTerminateWorkflowExecution() { config := s.newConfig(dc.NewInMemoryClient()) config.EnableClientVersionCheck = dynamicproperties.GetBoolPropertyFn(true) wh := NewWorkflowHandler(s.mockResource, config, s.mockVersionChecker, nil) wh.tokenSerializer = s.mockTokenSerializer validRequest := &types.TerminateWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, Reason: "reason", Details: nil, Identity: "identity", } testInput := map[string]struct { request *types.TerminateWorkflowExecutionRequest expectError bool mockFn func() expectErrorType error }{ "shutting down": { request: validRequest, mockFn: func() { wh.shuttingDown = int32(1) }, expectError: true, expectErrorType: validate.ErrShuttingDown, }, "nil request": { request: nil, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrRequestNotSet, }, "empty domain": { request: &types.TerminateWorkflowExecutionRequest{ Domain: "", }, mockFn: func() {}, expectError: true, expectErrorType: validate.ErrDomainNotSet, }, "empty workflow ID": { request: &types.TerminateWorkflowExecutionRequest{ Domain: s.testDomain, }, mockFn: func() {}, expectError: true, }, "empty workflow execution": { request: &types.TerminateWorkflowExecutionRequest{ Domain: s.testDomain, WorkflowExecution: nil, }, mockFn: func() {}, expectError: true, }, "cannot get domain ID": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return("", errors.New("error getting domain ID")) }, expectError: true, }, "history client error": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockHistoryClient.EXPECT().TerminateWorkflowExecution(gomock.Any(), gomock.Any()).Return(errors.New("error")) }, expectError: true, }, "success": { request: validRequest, mockFn: func() { s.mockDomainCache.EXPECT().GetDomainID(s.testDomain).Return(s.testDomainID, nil) s.mockHistoryClient.EXPECT().TerminateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil) }, expectError: false, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := wh.TerminateWorkflowExecution(context.Background(), input.request) if input.expectError { s.Error(err) if input.expectErrorType != nil { s.ErrorIs(err, input.expectErrorType) } } else { s.NoError(err) } wh.shuttingDown = int32(0) }) } } func (s *workflowHandlerSuite) TestNormalizeVersionedErrors() { config := s.newConfig(dc.NewInMemoryClient()) wh := s.getWorkflowHandler(config) ctx := yarpctest.ContextWithCall(context.Background(), &yarpctest.Call{ Headers: map[string]string{ common.FeatureVersionHeaderName: "feature-version", common.ClientImplHeaderName: "impl-header", common.ClientFeatureFlagsHeaderName: "", }, }) s.mockVersionChecker.EXPECT().SupportsWorkflowAlreadyCompletedError("impl-header", "feature-version", apiv1.FeatureFlags{}).Return(nil) err := wh.normalizeVersionedErrors(ctx, &types.WorkflowExecutionAlreadyCompletedError{}) s.IsType(err, &types.WorkflowExecutionAlreadyCompletedError{}) s.mockVersionChecker.EXPECT().SupportsWorkflowAlreadyCompletedError("impl-header", "feature-version", apiv1.FeatureFlags{}).Return(errors.New("error")) err = wh.normalizeVersionedErrors(ctx, &types.WorkflowExecutionAlreadyCompletedError{}) s.IsType(err, &types.EntityNotExistsError{}) } func TestWorkflowDescribeEmitStatusMetrics(t *testing.T) { tests := map[string]struct { res *types.DescribeWorkflowExecutionResponse err error expectedCounters map[string]tally.CounterSnapshot }{ "valid closed workflow": { res: &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{ CloseStatus: common.Ptr(types.WorkflowExecutionCloseStatusCompleted), }, }, expectedCounters: map[string]tally.CounterSnapshot{ "describe_wf_status+domain=some-domain,operation=DescribeWorkflowExecutionStatus,workflow_close_status=COMPLETED": &counterSnapshotMock{ name: "describe_wf_status", tags: map[string]string{ "domain": "some-domain", "workflow_close_status": "COMPLETED", "operation": "DescribeWorkflowExecutionStatus", }, value: 1, }, }, }, "A workflow not found": { res: nil, err: &types.EntityNotExistsError{}, expectedCounters: map[string]tally.CounterSnapshot{ "describe_wf_error+domain=some-domain,operation=DescribeWorkflowExecutionStatus": &counterSnapshotMock{ name: "describe_wf_error", tags: map[string]string{ "domain": "some-domain", "operation": "DescribeWorkflowExecutionStatus", }, value: 1, }, }, }, "A invalid input 1": { res: nil, err: nil, expectedCounters: map[string]tally.CounterSnapshot{ "describe_wf_error+domain=some-domain,operation=DescribeWorkflowExecutionStatus": &counterSnapshotMock{ name: "describe_wf_error", tags: map[string]string{ "domain": "some-domain", "operation": "DescribeWorkflowExecutionStatus", }, value: 1, }, }, }, "invalid input 2": { res: &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{ // intentionally nil // CloseStatus: common.Ptr(types.WorkflowExecutionCloseStatusCompleted), }, }, expectedCounters: map[string]tally.CounterSnapshot{ "describe_wf_status+domain=some-domain,operation=DescribeWorkflowExecutionStatus,workflow_close_status=unknown": &counterSnapshotMock{ name: "describe_wf_status", tags: map[string]string{ "domain": "some-domain", "workflow_close_status": "unknown", "operation": "DescribeWorkflowExecutionStatus", }, value: 1, }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { scope := tally.NewTestScope("", nil) mockR := resource.Test{ MetricsScope: scope, MetricsClient: metrics.NewClient(scope, 1, metrics.HistogramMigration{}), } wh := WorkflowHandler{ Resource: &mockR, } wh.emitDescribeWorkflowExecutionMetrics("some-domain", td.res, td.err) snap := scope.Snapshot() for k, v := range td.expectedCounters { _, ok := snap.Counters()[k] if !ok { t.Errorf("the metric string expected was not found. Expected a map with this key: %q\ngot %v", k, snap.Counters()) return } assert.Equal(t, snap.Counters()[k].Name(), v.Name()) assert.Equal(t, snap.Counters()[k].Value(), v.Value()) assert.Equal(t, snap.Counters()[k].Tags(), v.Tags()) } }) } } type counterSnapshotMock struct { name string tags map[string]string value int64 } func (cs *counterSnapshotMock) Name() string { return cs.name } func (cs *counterSnapshotMock) Tags() map[string]string { return cs.tags } func (cs *counterSnapshotMock) Value() int64 { return cs.value } func TestConstructRestartWorkflowRequest(t *testing.T) { testCases := []struct { name string originalAttributes *types.WorkflowExecutionStartedEventAttributes domain string identity string workflowID string expectPanic bool description string }{ { // TODO(tim): This should not panic, return an error name: "nil originalAttributes should panic", originalAttributes: nil, domain: "test-domain", identity: "test-identity", workflowID: "test-workflow-id", expectPanic: true, description: "nil originalAttributes should cause panic to prevent nil pointer dereference", }, { // TODO(tim): This should not panic, return an error name: "nil WorkflowType should panic", originalAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: nil, TaskList: &types.TaskList{Name: "testTaskList"}, }, domain: "test-domain", identity: "test-identity", workflowID: "test-workflow-id", expectPanic: true, description: "nil WorkflowType should cause panic", }, { // TODO(tim): This should not panic, return an error name: "nil TaskList should panic", originalAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: "testWorkflow"}, TaskList: nil, }, domain: "test-domain", identity: "test-identity", workflowID: "test-workflow-id", expectPanic: true, description: "nil TaskList should cause panic", }, { name: "complete field validation for restart request", originalAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: "testWorkflow"}, TaskList: &types.TaskList{Name: "testTaskList", Kind: types.TaskListKindNormal.Ptr()}, Input: []byte("test-input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(60), Header: &types.Header{Fields: map[string][]byte{"key": []byte("value")}}, Memo: &types.Memo{Fields: map[string][]byte{"memo": []byte("data")}}, SearchAttributes: &types.SearchAttributes{IndexedFields: map[string][]byte{"attr": []byte("val")}}, RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2.0, MaximumIntervalInSeconds: 10, MaximumAttempts: 3, ExpirationIntervalInSeconds: 100, }, CronSchedule: "0 */2 * * *", FirstDecisionTaskBackoffSeconds: common.Int32Ptr(30), ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyRegionSticky.Ptr(), StickyRegion: "us-west-2", }, }, domain: "test-domain", identity: "test-identity", workflowID: "test-workflow-id", expectPanic: false, description: "complete field validation ensures all fields are properly set", }, { name: "ActiveClusterSelectionPolicy with ExternalEntity strategy", originalAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: "testWorkflow"}, TaskList: &types.TaskList{Name: "testTaskList"}, ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyExternalEntity.Ptr(), ExternalEntityType: "order", ExternalEntityKey: "order-789", }, }, domain: "test-domain", identity: "test-identity", workflowID: "test-workflow-id", expectPanic: false, description: "ExternalEntity policy should be completely preserved", }, { name: "nil ActiveClusterSelectionPolicy should remain nil", originalAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: "testWorkflow"}, TaskList: &types.TaskList{Name: "testTaskList"}, ActiveClusterSelectionPolicy: nil, }, domain: "test-domain", identity: "test-identity", workflowID: "test-workflow-id", expectPanic: false, description: "nil ActiveClusterSelectionPolicy should remain nil in restart request", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if tc.expectPanic { assert.Panics(t, func() { constructRestartWorkflowRequest( tc.originalAttributes, tc.domain, tc.identity, tc.workflowID, ) }, tc.description) return } // Execute constructRestartWorkflowRequest startRequest := constructRestartWorkflowRequest( tc.originalAttributes, tc.domain, tc.identity, tc.workflowID, ) // Validate RequestID is a non-empty UUID assert.NotEmpty(t, startRequest.RequestID, "RequestID should be non-empty") parsedUUID := uuid.Parse(startRequest.RequestID) assert.NotNil(t, parsedUUID, "RequestID should be a valid UUID") // Validate input parameters are correctly set assert.Equal(t, tc.domain, startRequest.Domain, "Domain should match input") assert.Equal(t, tc.workflowID, startRequest.WorkflowID, "WorkflowID should match input") assert.Equal(t, tc.identity, startRequest.Identity, "Identity should match input") // Validate fields from workflow attributes assert.Equal(t, tc.originalAttributes.WorkflowType.Name, startRequest.WorkflowType.Name, "WorkflowType.Name should match") assert.Equal(t, tc.originalAttributes.TaskList.Name, startRequest.TaskList.Name, "TaskList.Name should match") assert.Equal(t, tc.originalAttributes.TaskList.Kind, startRequest.TaskList.Kind, "TaskList.Kind should match") assert.Equal(t, tc.originalAttributes.Input, startRequest.Input, "Input should match") assert.Equal(t, tc.originalAttributes.ExecutionStartToCloseTimeoutSeconds, startRequest.ExecutionStartToCloseTimeoutSeconds, "ExecutionStartToCloseTimeoutSeconds should match") assert.Equal(t, tc.originalAttributes.TaskStartToCloseTimeoutSeconds, startRequest.TaskStartToCloseTimeoutSeconds, "TaskStartToCloseTimeoutSeconds should match") // Validate WorkflowIDReusePolicy is correctly set assert.NotNil(t, startRequest.WorkflowIDReusePolicy, "WorkflowIDReusePolicy should not be nil") assert.Equal(t, types.WorkflowIDReusePolicyTerminateIfRunning, *startRequest.WorkflowIDReusePolicy, "WorkflowIDReusePolicy should be TerminateIfRunning") // Validate optional fields from workflow attributes assert.Equal(t, tc.originalAttributes.ActiveClusterSelectionPolicy, startRequest.ActiveClusterSelectionPolicy, "ActiveClusterSelectionPolicy should match") assert.Equal(t, tc.originalAttributes.CronSchedule, startRequest.CronSchedule, "CronSchedule should match") assert.Equal(t, tc.originalAttributes.RetryPolicy, startRequest.RetryPolicy, "RetryPolicy should match") assert.Equal(t, tc.originalAttributes.Header, startRequest.Header, "Header should match") assert.Equal(t, tc.originalAttributes.Memo, startRequest.Memo, "Memo should match") assert.Equal(t, tc.originalAttributes.SearchAttributes, startRequest.SearchAttributes, "SearchAttributes should match") // Validate DelayStartSeconds is set to 0 for restart requests assert.NotNil(t, startRequest.DelayStartSeconds, "DelayStartSeconds should not be nil") assert.Equal(t, int32(0), *startRequest.DelayStartSeconds, "DelayStartSeconds should be 0 for restart requests") }) } } ================================================ FILE: service/frontend/api/interface.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/service/frontend/api //go:generate gowrap gen -g -p . -i Handler -t ../templates/accesscontrolled.tmpl -o ../wrappers/accesscontrolled/api_generated.go -v handler=API //go:generate gowrap gen -g -p . -i Handler -t ../templates/clusterredirection.tmpl -o ../wrappers/clusterredirection/api_generated.go //go:generate gowrap gen -g -p . -i Handler -t ../templates/versioncheck.tmpl -o ../wrappers/versioncheck/api_generated.go //go:generate gowrap gen -g -p . -i Handler -t ../templates/metered.tmpl -o ../wrappers/metered/api_generated.go -v handler=API //go:generate gowrap gen -g -p . -i Handler -t ../templates/ratelimited.tmpl -o ../wrappers/ratelimited/api_generated.go -v handler=API //go:generate gowrap gen -g -p . -i Handler -t ../../templates/grpc.tmpl -o ../wrappers/grpc/api_generated.go -v handler=API -v package=apiv1 -v path=github.com/uber/cadence-idl/go/proto/api/v1 -v prefix= //go:generate gowrap gen -g -p ../../../.gen/go/cadence/workflowserviceserver -i Interface -t ../../templates/thrift.tmpl -o ../wrappers/thrift/api_generated.go -v handler=API -v prefix= package api import ( "context" "github.com/uber/cadence/common/types" ) type ( // Handler is interface wrapping frontend handler Handler interface { Health(context.Context) (*types.HealthStatus, error) CountWorkflowExecutions(context.Context, *types.CountWorkflowExecutionsRequest) (*types.CountWorkflowExecutionsResponse, error) DeleteDomain(context.Context, *types.DeleteDomainRequest) error DeprecateDomain(context.Context, *types.DeprecateDomainRequest) error DescribeDomain(context.Context, *types.DescribeDomainRequest) (*types.DescribeDomainResponse, error) DescribeTaskList(context.Context, *types.DescribeTaskListRequest) (*types.DescribeTaskListResponse, error) DescribeWorkflowExecution(context.Context, *types.DescribeWorkflowExecutionRequest) (*types.DescribeWorkflowExecutionResponse, error) DiagnoseWorkflowExecution(context.Context, *types.DiagnoseWorkflowExecutionRequest) (*types.DiagnoseWorkflowExecutionResponse, error) GetClusterInfo(context.Context) (*types.ClusterInfo, error) GetSearchAttributes(context.Context) (*types.GetSearchAttributesResponse, error) GetWorkflowExecutionHistory(context.Context, *types.GetWorkflowExecutionHistoryRequest) (*types.GetWorkflowExecutionHistoryResponse, error) ListArchivedWorkflowExecutions(context.Context, *types.ListArchivedWorkflowExecutionsRequest) (*types.ListArchivedWorkflowExecutionsResponse, error) ListClosedWorkflowExecutions(context.Context, *types.ListClosedWorkflowExecutionsRequest) (*types.ListClosedWorkflowExecutionsResponse, error) ListDomains(context.Context, *types.ListDomainsRequest) (*types.ListDomainsResponse, error) ListOpenWorkflowExecutions(context.Context, *types.ListOpenWorkflowExecutionsRequest) (*types.ListOpenWorkflowExecutionsResponse, error) ListTaskListPartitions(context.Context, *types.ListTaskListPartitionsRequest) (*types.ListTaskListPartitionsResponse, error) GetTaskListsByDomain(context.Context, *types.GetTaskListsByDomainRequest) (*types.GetTaskListsByDomainResponse, error) RefreshWorkflowTasks(context.Context, *types.RefreshWorkflowTasksRequest) error ListWorkflowExecutions(context.Context, *types.ListWorkflowExecutionsRequest) (*types.ListWorkflowExecutionsResponse, error) PollForActivityTask(context.Context, *types.PollForActivityTaskRequest) (*types.PollForActivityTaskResponse, error) PollForDecisionTask(context.Context, *types.PollForDecisionTaskRequest) (*types.PollForDecisionTaskResponse, error) QueryWorkflow(context.Context, *types.QueryWorkflowRequest) (*types.QueryWorkflowResponse, error) RecordActivityTaskHeartbeat(context.Context, *types.RecordActivityTaskHeartbeatRequest) (*types.RecordActivityTaskHeartbeatResponse, error) RecordActivityTaskHeartbeatByID(context.Context, *types.RecordActivityTaskHeartbeatByIDRequest) (*types.RecordActivityTaskHeartbeatResponse, error) RegisterDomain(context.Context, *types.RegisterDomainRequest) error RequestCancelWorkflowExecution(context.Context, *types.RequestCancelWorkflowExecutionRequest) error ResetStickyTaskList(context.Context, *types.ResetStickyTaskListRequest) (*types.ResetStickyTaskListResponse, error) ResetWorkflowExecution(context.Context, *types.ResetWorkflowExecutionRequest) (*types.ResetWorkflowExecutionResponse, error) RespondActivityTaskCanceled(context.Context, *types.RespondActivityTaskCanceledRequest) error RespondActivityTaskCanceledByID(context.Context, *types.RespondActivityTaskCanceledByIDRequest) error RespondActivityTaskCompleted(context.Context, *types.RespondActivityTaskCompletedRequest) error RespondActivityTaskCompletedByID(context.Context, *types.RespondActivityTaskCompletedByIDRequest) error RespondActivityTaskFailed(context.Context, *types.RespondActivityTaskFailedRequest) error RespondActivityTaskFailedByID(context.Context, *types.RespondActivityTaskFailedByIDRequest) error RespondDecisionTaskCompleted(context.Context, *types.RespondDecisionTaskCompletedRequest) (*types.RespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed(context.Context, *types.RespondDecisionTaskFailedRequest) error RespondQueryTaskCompleted(context.Context, *types.RespondQueryTaskCompletedRequest) error RestartWorkflowExecution(context.Context, *types.RestartWorkflowExecutionRequest) (*types.RestartWorkflowExecutionResponse, error) ScanWorkflowExecutions(context.Context, *types.ListWorkflowExecutionsRequest) (*types.ListWorkflowExecutionsResponse, error) SignalWithStartWorkflowExecution(context.Context, *types.SignalWithStartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) SignalWithStartWorkflowExecutionAsync(context.Context, *types.SignalWithStartWorkflowExecutionAsyncRequest) (*types.SignalWithStartWorkflowExecutionAsyncResponse, error) SignalWorkflowExecution(context.Context, *types.SignalWorkflowExecutionRequest) error StartWorkflowExecution(context.Context, *types.StartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) StartWorkflowExecutionAsync(context.Context, *types.StartWorkflowExecutionAsyncRequest) (*types.StartWorkflowExecutionAsyncResponse, error) TerminateWorkflowExecution(context.Context, *types.TerminateWorkflowExecutionRequest) error UpdateDomain(context.Context, *types.UpdateDomainRequest) (*types.UpdateDomainResponse, error) FailoverDomain(context.Context, *types.FailoverDomainRequest) (*types.FailoverDomainResponse, error) ListFailoverHistory(context.Context, *types.ListFailoverHistoryRequest) (*types.ListFailoverHistoryResponse, error) } ) ================================================ FILE: service/frontend/api/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package api -source interface.go -destination interface_mock.go -self_package github.com/uber/cadence/service/frontend/api // // Package api is a generated GoMock package. package api import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockHandler is a mock of Handler interface. type MockHandler struct { ctrl *gomock.Controller recorder *MockHandlerMockRecorder isgomock struct{} } // MockHandlerMockRecorder is the mock recorder for MockHandler. type MockHandlerMockRecorder struct { mock *MockHandler } // NewMockHandler creates a new mock instance. func NewMockHandler(ctrl *gomock.Controller) *MockHandler { mock := &MockHandler{ctrl: ctrl} mock.recorder = &MockHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHandler) EXPECT() *MockHandlerMockRecorder { return m.recorder } // CountWorkflowExecutions mocks base method. func (m *MockHandler) CountWorkflowExecutions(arg0 context.Context, arg1 *types.CountWorkflowExecutionsRequest) (*types.CountWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CountWorkflowExecutions", arg0, arg1) ret0, _ := ret[0].(*types.CountWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CountWorkflowExecutions indicates an expected call of CountWorkflowExecutions. func (mr *MockHandlerMockRecorder) CountWorkflowExecutions(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountWorkflowExecutions", reflect.TypeOf((*MockHandler)(nil).CountWorkflowExecutions), arg0, arg1) } // DeleteDomain mocks base method. func (m *MockHandler) DeleteDomain(arg0 context.Context, arg1 *types.DeleteDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteDomain", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // DeleteDomain indicates an expected call of DeleteDomain. func (mr *MockHandlerMockRecorder) DeleteDomain(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDomain", reflect.TypeOf((*MockHandler)(nil).DeleteDomain), arg0, arg1) } // DeprecateDomain mocks base method. func (m *MockHandler) DeprecateDomain(arg0 context.Context, arg1 *types.DeprecateDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeprecateDomain", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // DeprecateDomain indicates an expected call of DeprecateDomain. func (mr *MockHandlerMockRecorder) DeprecateDomain(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeprecateDomain", reflect.TypeOf((*MockHandler)(nil).DeprecateDomain), arg0, arg1) } // DescribeDomain mocks base method. func (m *MockHandler) DescribeDomain(arg0 context.Context, arg1 *types.DescribeDomainRequest) (*types.DescribeDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeDomain", arg0, arg1) ret0, _ := ret[0].(*types.DescribeDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeDomain indicates an expected call of DescribeDomain. func (mr *MockHandlerMockRecorder) DescribeDomain(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDomain", reflect.TypeOf((*MockHandler)(nil).DescribeDomain), arg0, arg1) } // DescribeTaskList mocks base method. func (m *MockHandler) DescribeTaskList(arg0 context.Context, arg1 *types.DescribeTaskListRequest) (*types.DescribeTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeTaskList", arg0, arg1) ret0, _ := ret[0].(*types.DescribeTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeTaskList indicates an expected call of DescribeTaskList. func (mr *MockHandlerMockRecorder) DescribeTaskList(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTaskList", reflect.TypeOf((*MockHandler)(nil).DescribeTaskList), arg0, arg1) } // DescribeWorkflowExecution mocks base method. func (m *MockHandler) DescribeWorkflowExecution(arg0 context.Context, arg1 *types.DescribeWorkflowExecutionRequest) (*types.DescribeWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.DescribeWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeWorkflowExecution indicates an expected call of DescribeWorkflowExecution. func (mr *MockHandlerMockRecorder) DescribeWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).DescribeWorkflowExecution), arg0, arg1) } // DiagnoseWorkflowExecution mocks base method. func (m *MockHandler) DiagnoseWorkflowExecution(arg0 context.Context, arg1 *types.DiagnoseWorkflowExecutionRequest) (*types.DiagnoseWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DiagnoseWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.DiagnoseWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DiagnoseWorkflowExecution indicates an expected call of DiagnoseWorkflowExecution. func (mr *MockHandlerMockRecorder) DiagnoseWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiagnoseWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).DiagnoseWorkflowExecution), arg0, arg1) } // FailoverDomain mocks base method. func (m *MockHandler) FailoverDomain(arg0 context.Context, arg1 *types.FailoverDomainRequest) (*types.FailoverDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FailoverDomain", arg0, arg1) ret0, _ := ret[0].(*types.FailoverDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // FailoverDomain indicates an expected call of FailoverDomain. func (mr *MockHandlerMockRecorder) FailoverDomain(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FailoverDomain", reflect.TypeOf((*MockHandler)(nil).FailoverDomain), arg0, arg1) } // GetClusterInfo mocks base method. func (m *MockHandler) GetClusterInfo(arg0 context.Context) (*types.ClusterInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetClusterInfo", arg0) ret0, _ := ret[0].(*types.ClusterInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // GetClusterInfo indicates an expected call of GetClusterInfo. func (mr *MockHandlerMockRecorder) GetClusterInfo(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterInfo", reflect.TypeOf((*MockHandler)(nil).GetClusterInfo), arg0) } // GetSearchAttributes mocks base method. func (m *MockHandler) GetSearchAttributes(arg0 context.Context) (*types.GetSearchAttributesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSearchAttributes", arg0) ret0, _ := ret[0].(*types.GetSearchAttributesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSearchAttributes indicates an expected call of GetSearchAttributes. func (mr *MockHandlerMockRecorder) GetSearchAttributes(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSearchAttributes", reflect.TypeOf((*MockHandler)(nil).GetSearchAttributes), arg0) } // GetTaskListsByDomain mocks base method. func (m *MockHandler) GetTaskListsByDomain(arg0 context.Context, arg1 *types.GetTaskListsByDomainRequest) (*types.GetTaskListsByDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskListsByDomain", arg0, arg1) ret0, _ := ret[0].(*types.GetTaskListsByDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTaskListsByDomain indicates an expected call of GetTaskListsByDomain. func (mr *MockHandlerMockRecorder) GetTaskListsByDomain(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskListsByDomain", reflect.TypeOf((*MockHandler)(nil).GetTaskListsByDomain), arg0, arg1) } // GetWorkflowExecutionHistory mocks base method. func (m *MockHandler) GetWorkflowExecutionHistory(arg0 context.Context, arg1 *types.GetWorkflowExecutionHistoryRequest) (*types.GetWorkflowExecutionHistoryResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowExecutionHistory", arg0, arg1) ret0, _ := ret[0].(*types.GetWorkflowExecutionHistoryResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetWorkflowExecutionHistory indicates an expected call of GetWorkflowExecutionHistory. func (mr *MockHandlerMockRecorder) GetWorkflowExecutionHistory(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowExecutionHistory", reflect.TypeOf((*MockHandler)(nil).GetWorkflowExecutionHistory), arg0, arg1) } // Health mocks base method. func (m *MockHandler) Health(arg0 context.Context) (*types.HealthStatus, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Health", arg0) ret0, _ := ret[0].(*types.HealthStatus) ret1, _ := ret[1].(error) return ret0, ret1 } // Health indicates an expected call of Health. func (mr *MockHandlerMockRecorder) Health(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockHandler)(nil).Health), arg0) } // ListArchivedWorkflowExecutions mocks base method. func (m *MockHandler) ListArchivedWorkflowExecutions(arg0 context.Context, arg1 *types.ListArchivedWorkflowExecutionsRequest) (*types.ListArchivedWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListArchivedWorkflowExecutions", arg0, arg1) ret0, _ := ret[0].(*types.ListArchivedWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListArchivedWorkflowExecutions indicates an expected call of ListArchivedWorkflowExecutions. func (mr *MockHandlerMockRecorder) ListArchivedWorkflowExecutions(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListArchivedWorkflowExecutions", reflect.TypeOf((*MockHandler)(nil).ListArchivedWorkflowExecutions), arg0, arg1) } // ListClosedWorkflowExecutions mocks base method. func (m *MockHandler) ListClosedWorkflowExecutions(arg0 context.Context, arg1 *types.ListClosedWorkflowExecutionsRequest) (*types.ListClosedWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListClosedWorkflowExecutions", arg0, arg1) ret0, _ := ret[0].(*types.ListClosedWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListClosedWorkflowExecutions indicates an expected call of ListClosedWorkflowExecutions. func (mr *MockHandlerMockRecorder) ListClosedWorkflowExecutions(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListClosedWorkflowExecutions", reflect.TypeOf((*MockHandler)(nil).ListClosedWorkflowExecutions), arg0, arg1) } // ListDomains mocks base method. func (m *MockHandler) ListDomains(arg0 context.Context, arg1 *types.ListDomainsRequest) (*types.ListDomainsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListDomains", arg0, arg1) ret0, _ := ret[0].(*types.ListDomainsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListDomains indicates an expected call of ListDomains. func (mr *MockHandlerMockRecorder) ListDomains(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDomains", reflect.TypeOf((*MockHandler)(nil).ListDomains), arg0, arg1) } // ListFailoverHistory mocks base method. func (m *MockHandler) ListFailoverHistory(arg0 context.Context, arg1 *types.ListFailoverHistoryRequest) (*types.ListFailoverHistoryResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListFailoverHistory", arg0, arg1) ret0, _ := ret[0].(*types.ListFailoverHistoryResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListFailoverHistory indicates an expected call of ListFailoverHistory. func (mr *MockHandlerMockRecorder) ListFailoverHistory(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListFailoverHistory", reflect.TypeOf((*MockHandler)(nil).ListFailoverHistory), arg0, arg1) } // ListOpenWorkflowExecutions mocks base method. func (m *MockHandler) ListOpenWorkflowExecutions(arg0 context.Context, arg1 *types.ListOpenWorkflowExecutionsRequest) (*types.ListOpenWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListOpenWorkflowExecutions", arg0, arg1) ret0, _ := ret[0].(*types.ListOpenWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListOpenWorkflowExecutions indicates an expected call of ListOpenWorkflowExecutions. func (mr *MockHandlerMockRecorder) ListOpenWorkflowExecutions(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOpenWorkflowExecutions", reflect.TypeOf((*MockHandler)(nil).ListOpenWorkflowExecutions), arg0, arg1) } // ListTaskListPartitions mocks base method. func (m *MockHandler) ListTaskListPartitions(arg0 context.Context, arg1 *types.ListTaskListPartitionsRequest) (*types.ListTaskListPartitionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTaskListPartitions", arg0, arg1) ret0, _ := ret[0].(*types.ListTaskListPartitionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTaskListPartitions indicates an expected call of ListTaskListPartitions. func (mr *MockHandlerMockRecorder) ListTaskListPartitions(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTaskListPartitions", reflect.TypeOf((*MockHandler)(nil).ListTaskListPartitions), arg0, arg1) } // ListWorkflowExecutions mocks base method. func (m *MockHandler) ListWorkflowExecutions(arg0 context.Context, arg1 *types.ListWorkflowExecutionsRequest) (*types.ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListWorkflowExecutions", arg0, arg1) ret0, _ := ret[0].(*types.ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListWorkflowExecutions indicates an expected call of ListWorkflowExecutions. func (mr *MockHandlerMockRecorder) ListWorkflowExecutions(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListWorkflowExecutions", reflect.TypeOf((*MockHandler)(nil).ListWorkflowExecutions), arg0, arg1) } // PollForActivityTask mocks base method. func (m *MockHandler) PollForActivityTask(arg0 context.Context, arg1 *types.PollForActivityTaskRequest) (*types.PollForActivityTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PollForActivityTask", arg0, arg1) ret0, _ := ret[0].(*types.PollForActivityTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForActivityTask indicates an expected call of PollForActivityTask. func (mr *MockHandlerMockRecorder) PollForActivityTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForActivityTask", reflect.TypeOf((*MockHandler)(nil).PollForActivityTask), arg0, arg1) } // PollForDecisionTask mocks base method. func (m *MockHandler) PollForDecisionTask(arg0 context.Context, arg1 *types.PollForDecisionTaskRequest) (*types.PollForDecisionTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PollForDecisionTask", arg0, arg1) ret0, _ := ret[0].(*types.PollForDecisionTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForDecisionTask indicates an expected call of PollForDecisionTask. func (mr *MockHandlerMockRecorder) PollForDecisionTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForDecisionTask", reflect.TypeOf((*MockHandler)(nil).PollForDecisionTask), arg0, arg1) } // QueryWorkflow mocks base method. func (m *MockHandler) QueryWorkflow(arg0 context.Context, arg1 *types.QueryWorkflowRequest) (*types.QueryWorkflowResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryWorkflow", arg0, arg1) ret0, _ := ret[0].(*types.QueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // QueryWorkflow indicates an expected call of QueryWorkflow. func (mr *MockHandlerMockRecorder) QueryWorkflow(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockHandler)(nil).QueryWorkflow), arg0, arg1) } // RecordActivityTaskHeartbeat mocks base method. func (m *MockHandler) RecordActivityTaskHeartbeat(arg0 context.Context, arg1 *types.RecordActivityTaskHeartbeatRequest) (*types.RecordActivityTaskHeartbeatResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordActivityTaskHeartbeat", arg0, arg1) ret0, _ := ret[0].(*types.RecordActivityTaskHeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordActivityTaskHeartbeat indicates an expected call of RecordActivityTaskHeartbeat. func (mr *MockHandlerMockRecorder) RecordActivityTaskHeartbeat(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordActivityTaskHeartbeat", reflect.TypeOf((*MockHandler)(nil).RecordActivityTaskHeartbeat), arg0, arg1) } // RecordActivityTaskHeartbeatByID mocks base method. func (m *MockHandler) RecordActivityTaskHeartbeatByID(arg0 context.Context, arg1 *types.RecordActivityTaskHeartbeatByIDRequest) (*types.RecordActivityTaskHeartbeatResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordActivityTaskHeartbeatByID", arg0, arg1) ret0, _ := ret[0].(*types.RecordActivityTaskHeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordActivityTaskHeartbeatByID indicates an expected call of RecordActivityTaskHeartbeatByID. func (mr *MockHandlerMockRecorder) RecordActivityTaskHeartbeatByID(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordActivityTaskHeartbeatByID", reflect.TypeOf((*MockHandler)(nil).RecordActivityTaskHeartbeatByID), arg0, arg1) } // RefreshWorkflowTasks mocks base method. func (m *MockHandler) RefreshWorkflowTasks(arg0 context.Context, arg1 *types.RefreshWorkflowTasksRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RefreshWorkflowTasks", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RefreshWorkflowTasks indicates an expected call of RefreshWorkflowTasks. func (mr *MockHandlerMockRecorder) RefreshWorkflowTasks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshWorkflowTasks", reflect.TypeOf((*MockHandler)(nil).RefreshWorkflowTasks), arg0, arg1) } // RegisterDomain mocks base method. func (m *MockHandler) RegisterDomain(arg0 context.Context, arg1 *types.RegisterDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RegisterDomain", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RegisterDomain indicates an expected call of RegisterDomain. func (mr *MockHandlerMockRecorder) RegisterDomain(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterDomain", reflect.TypeOf((*MockHandler)(nil).RegisterDomain), arg0, arg1) } // RequestCancelWorkflowExecution mocks base method. func (m *MockHandler) RequestCancelWorkflowExecution(arg0 context.Context, arg1 *types.RequestCancelWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RequestCancelWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RequestCancelWorkflowExecution indicates an expected call of RequestCancelWorkflowExecution. func (mr *MockHandlerMockRecorder) RequestCancelWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestCancelWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).RequestCancelWorkflowExecution), arg0, arg1) } // ResetStickyTaskList mocks base method. func (m *MockHandler) ResetStickyTaskList(arg0 context.Context, arg1 *types.ResetStickyTaskListRequest) (*types.ResetStickyTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetStickyTaskList", arg0, arg1) ret0, _ := ret[0].(*types.ResetStickyTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetStickyTaskList indicates an expected call of ResetStickyTaskList. func (mr *MockHandlerMockRecorder) ResetStickyTaskList(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetStickyTaskList", reflect.TypeOf((*MockHandler)(nil).ResetStickyTaskList), arg0, arg1) } // ResetWorkflowExecution mocks base method. func (m *MockHandler) ResetWorkflowExecution(arg0 context.Context, arg1 *types.ResetWorkflowExecutionRequest) (*types.ResetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.ResetWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetWorkflowExecution indicates an expected call of ResetWorkflowExecution. func (mr *MockHandlerMockRecorder) ResetWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).ResetWorkflowExecution), arg0, arg1) } // RespondActivityTaskCanceled mocks base method. func (m *MockHandler) RespondActivityTaskCanceled(arg0 context.Context, arg1 *types.RespondActivityTaskCanceledRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskCanceled", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCanceled indicates an expected call of RespondActivityTaskCanceled. func (mr *MockHandlerMockRecorder) RespondActivityTaskCanceled(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCanceled", reflect.TypeOf((*MockHandler)(nil).RespondActivityTaskCanceled), arg0, arg1) } // RespondActivityTaskCanceledByID mocks base method. func (m *MockHandler) RespondActivityTaskCanceledByID(arg0 context.Context, arg1 *types.RespondActivityTaskCanceledByIDRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskCanceledByID", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCanceledByID indicates an expected call of RespondActivityTaskCanceledByID. func (mr *MockHandlerMockRecorder) RespondActivityTaskCanceledByID(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCanceledByID", reflect.TypeOf((*MockHandler)(nil).RespondActivityTaskCanceledByID), arg0, arg1) } // RespondActivityTaskCompleted mocks base method. func (m *MockHandler) RespondActivityTaskCompleted(arg0 context.Context, arg1 *types.RespondActivityTaskCompletedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskCompleted", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCompleted indicates an expected call of RespondActivityTaskCompleted. func (mr *MockHandlerMockRecorder) RespondActivityTaskCompleted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCompleted", reflect.TypeOf((*MockHandler)(nil).RespondActivityTaskCompleted), arg0, arg1) } // RespondActivityTaskCompletedByID mocks base method. func (m *MockHandler) RespondActivityTaskCompletedByID(arg0 context.Context, arg1 *types.RespondActivityTaskCompletedByIDRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskCompletedByID", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCompletedByID indicates an expected call of RespondActivityTaskCompletedByID. func (mr *MockHandlerMockRecorder) RespondActivityTaskCompletedByID(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCompletedByID", reflect.TypeOf((*MockHandler)(nil).RespondActivityTaskCompletedByID), arg0, arg1) } // RespondActivityTaskFailed mocks base method. func (m *MockHandler) RespondActivityTaskFailed(arg0 context.Context, arg1 *types.RespondActivityTaskFailedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskFailed", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskFailed indicates an expected call of RespondActivityTaskFailed. func (mr *MockHandlerMockRecorder) RespondActivityTaskFailed(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskFailed", reflect.TypeOf((*MockHandler)(nil).RespondActivityTaskFailed), arg0, arg1) } // RespondActivityTaskFailedByID mocks base method. func (m *MockHandler) RespondActivityTaskFailedByID(arg0 context.Context, arg1 *types.RespondActivityTaskFailedByIDRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskFailedByID", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskFailedByID indicates an expected call of RespondActivityTaskFailedByID. func (mr *MockHandlerMockRecorder) RespondActivityTaskFailedByID(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskFailedByID", reflect.TypeOf((*MockHandler)(nil).RespondActivityTaskFailedByID), arg0, arg1) } // RespondDecisionTaskCompleted mocks base method. func (m *MockHandler) RespondDecisionTaskCompleted(arg0 context.Context, arg1 *types.RespondDecisionTaskCompletedRequest) (*types.RespondDecisionTaskCompletedResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondDecisionTaskCompleted", arg0, arg1) ret0, _ := ret[0].(*types.RespondDecisionTaskCompletedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RespondDecisionTaskCompleted indicates an expected call of RespondDecisionTaskCompleted. func (mr *MockHandlerMockRecorder) RespondDecisionTaskCompleted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondDecisionTaskCompleted", reflect.TypeOf((*MockHandler)(nil).RespondDecisionTaskCompleted), arg0, arg1) } // RespondDecisionTaskFailed mocks base method. func (m *MockHandler) RespondDecisionTaskFailed(arg0 context.Context, arg1 *types.RespondDecisionTaskFailedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondDecisionTaskFailed", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondDecisionTaskFailed indicates an expected call of RespondDecisionTaskFailed. func (mr *MockHandlerMockRecorder) RespondDecisionTaskFailed(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondDecisionTaskFailed", reflect.TypeOf((*MockHandler)(nil).RespondDecisionTaskFailed), arg0, arg1) } // RespondQueryTaskCompleted mocks base method. func (m *MockHandler) RespondQueryTaskCompleted(arg0 context.Context, arg1 *types.RespondQueryTaskCompletedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondQueryTaskCompleted", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondQueryTaskCompleted indicates an expected call of RespondQueryTaskCompleted. func (mr *MockHandlerMockRecorder) RespondQueryTaskCompleted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondQueryTaskCompleted", reflect.TypeOf((*MockHandler)(nil).RespondQueryTaskCompleted), arg0, arg1) } // RestartWorkflowExecution mocks base method. func (m *MockHandler) RestartWorkflowExecution(arg0 context.Context, arg1 *types.RestartWorkflowExecutionRequest) (*types.RestartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RestartWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.RestartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RestartWorkflowExecution indicates an expected call of RestartWorkflowExecution. func (mr *MockHandlerMockRecorder) RestartWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestartWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).RestartWorkflowExecution), arg0, arg1) } // ScanWorkflowExecutions mocks base method. func (m *MockHandler) ScanWorkflowExecutions(arg0 context.Context, arg1 *types.ListWorkflowExecutionsRequest) (*types.ListWorkflowExecutionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ScanWorkflowExecutions", arg0, arg1) ret0, _ := ret[0].(*types.ListWorkflowExecutionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ScanWorkflowExecutions indicates an expected call of ScanWorkflowExecutions. func (mr *MockHandlerMockRecorder) ScanWorkflowExecutions(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScanWorkflowExecutions", reflect.TypeOf((*MockHandler)(nil).ScanWorkflowExecutions), arg0, arg1) } // SignalWithStartWorkflowExecution mocks base method. func (m *MockHandler) SignalWithStartWorkflowExecution(arg0 context.Context, arg1 *types.SignalWithStartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignalWithStartWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.StartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SignalWithStartWorkflowExecution indicates an expected call of SignalWithStartWorkflowExecution. func (mr *MockHandlerMockRecorder) SignalWithStartWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWithStartWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).SignalWithStartWorkflowExecution), arg0, arg1) } // SignalWithStartWorkflowExecutionAsync mocks base method. func (m *MockHandler) SignalWithStartWorkflowExecutionAsync(arg0 context.Context, arg1 *types.SignalWithStartWorkflowExecutionAsyncRequest) (*types.SignalWithStartWorkflowExecutionAsyncResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignalWithStartWorkflowExecutionAsync", arg0, arg1) ret0, _ := ret[0].(*types.SignalWithStartWorkflowExecutionAsyncResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SignalWithStartWorkflowExecutionAsync indicates an expected call of SignalWithStartWorkflowExecutionAsync. func (mr *MockHandlerMockRecorder) SignalWithStartWorkflowExecutionAsync(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWithStartWorkflowExecutionAsync", reflect.TypeOf((*MockHandler)(nil).SignalWithStartWorkflowExecutionAsync), arg0, arg1) } // SignalWorkflowExecution mocks base method. func (m *MockHandler) SignalWorkflowExecution(arg0 context.Context, arg1 *types.SignalWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignalWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // SignalWorkflowExecution indicates an expected call of SignalWorkflowExecution. func (mr *MockHandlerMockRecorder) SignalWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).SignalWorkflowExecution), arg0, arg1) } // StartWorkflowExecution mocks base method. func (m *MockHandler) StartWorkflowExecution(arg0 context.Context, arg1 *types.StartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StartWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.StartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // StartWorkflowExecution indicates an expected call of StartWorkflowExecution. func (mr *MockHandlerMockRecorder) StartWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).StartWorkflowExecution), arg0, arg1) } // StartWorkflowExecutionAsync mocks base method. func (m *MockHandler) StartWorkflowExecutionAsync(arg0 context.Context, arg1 *types.StartWorkflowExecutionAsyncRequest) (*types.StartWorkflowExecutionAsyncResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StartWorkflowExecutionAsync", arg0, arg1) ret0, _ := ret[0].(*types.StartWorkflowExecutionAsyncResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // StartWorkflowExecutionAsync indicates an expected call of StartWorkflowExecutionAsync. func (mr *MockHandlerMockRecorder) StartWorkflowExecutionAsync(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartWorkflowExecutionAsync", reflect.TypeOf((*MockHandler)(nil).StartWorkflowExecutionAsync), arg0, arg1) } // TerminateWorkflowExecution mocks base method. func (m *MockHandler) TerminateWorkflowExecution(arg0 context.Context, arg1 *types.TerminateWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TerminateWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // TerminateWorkflowExecution indicates an expected call of TerminateWorkflowExecution. func (mr *MockHandlerMockRecorder) TerminateWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TerminateWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).TerminateWorkflowExecution), arg0, arg1) } // UpdateDomain mocks base method. func (m *MockHandler) UpdateDomain(arg0 context.Context, arg1 *types.UpdateDomainRequest) (*types.UpdateDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomain", arg0, arg1) ret0, _ := ret[0].(*types.UpdateDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateDomain indicates an expected call of UpdateDomain. func (mr *MockHandlerMockRecorder) UpdateDomain(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomain", reflect.TypeOf((*MockHandler)(nil).UpdateDomain), arg0, arg1) } ================================================ FILE: service/frontend/api/list_workflow_handlers.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package api import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/validate" ) // CountWorkflowExecutions - count number of workflow executions in a domain func (wh *WorkflowHandler) CountWorkflowExecutions( ctx context.Context, countRequest *types.CountWorkflowExecutionsRequest, ) (resp *types.CountWorkflowExecutionsResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateCountWorkflowExecutionsRequest(ctx, countRequest); err != nil { return nil, err } validatedQuery, err := wh.visibilityQueryValidator.ValidateQuery(countRequest.GetQuery()) if err != nil { return nil, err } domain := countRequest.GetDomain() domainID, err := wh.GetDomainCache().GetDomainID(domain) if err != nil { return nil, err } req := &persistence.CountWorkflowExecutionsRequest{ DomainUUID: domainID, Domain: domain, Query: validatedQuery, } persistenceResp, err := wh.GetVisibilityManager().CountWorkflowExecutions(ctx, req) if err != nil { return nil, err } resp = &types.CountWorkflowExecutionsResponse{ Count: persistenceResp.Count, } return resp, nil } // ScanWorkflowExecutions - retrieves info for large amount of workflow executions in a domain without order func (wh *WorkflowHandler) ScanWorkflowExecutions( ctx context.Context, listRequest *types.ListWorkflowExecutionsRequest, ) (resp *types.ListWorkflowExecutionsResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateListWorkflowExecutionsRequest(ctx, listRequest); err != nil { return nil, err } validatedQuery, err := wh.visibilityQueryValidator.ValidateQuery(listRequest.GetQuery()) if err != nil { return nil, err } domain := listRequest.GetDomain() domainID, err := wh.GetDomainCache().GetDomainID(domain) if err != nil { return nil, err } req := &persistence.ListWorkflowExecutionsByQueryRequest{ DomainUUID: domainID, Domain: domain, PageSize: int(listRequest.GetPageSize()), NextPageToken: listRequest.NextPageToken, Query: validatedQuery, } persistenceResp, err := wh.GetVisibilityManager().ScanWorkflowExecutions(ctx, req) if err != nil { return nil, err } resp = &types.ListWorkflowExecutionsResponse{} resp.Executions = persistenceResp.Executions resp.NextPageToken = persistenceResp.NextPageToken return resp, nil } // ListOpenWorkflowExecutions - retrieves info for open workflow executions in a domain func (wh *WorkflowHandler) ListOpenWorkflowExecutions( ctx context.Context, listRequest *types.ListOpenWorkflowExecutionsRequest, ) (resp *types.ListOpenWorkflowExecutionsResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateListOpenWorkflowExecutionsRequest(ctx, listRequest); err != nil { return nil, err } domain := listRequest.GetDomain() domainID, err := wh.GetDomainCache().GetDomainID(domain) if err != nil { return nil, err } baseReq := persistence.ListWorkflowExecutionsRequest{ DomainUUID: domainID, Domain: domain, PageSize: int(listRequest.GetMaximumPageSize()), NextPageToken: listRequest.NextPageToken, EarliestTime: listRequest.StartTimeFilter.GetEarliestTime(), LatestTime: listRequest.StartTimeFilter.GetLatestTime(), } var persistenceResp *persistence.ListWorkflowExecutionsResponse if listRequest.ExecutionFilter != nil { if wh.config.DisableListVisibilityByFilter(domain) { err = validate.ErrNoPermission } else { persistenceResp, err = wh.GetVisibilityManager().ListOpenWorkflowExecutionsByWorkflowID( ctx, &persistence.ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: baseReq, WorkflowID: listRequest.ExecutionFilter.GetWorkflowID(), }) } wh.GetLogger().Debug("List open workflow with filter", tag.WorkflowDomainName(listRequest.GetDomain()), tag.WorkflowListWorkflowFilterByID) } else if listRequest.TypeFilter != nil { if wh.config.DisableListVisibilityByFilter(domain) { err = validate.ErrNoPermission } else { persistenceResp, err = wh.GetVisibilityManager().ListOpenWorkflowExecutionsByType( ctx, &persistence.ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: baseReq, WorkflowTypeName: listRequest.TypeFilter.GetName(), }, ) } wh.GetLogger().Debug("List open workflow with filter", tag.WorkflowDomainName(listRequest.GetDomain()), tag.WorkflowListWorkflowFilterByType) } else { persistenceResp, err = wh.GetVisibilityManager().ListOpenWorkflowExecutions(ctx, &baseReq) } if err != nil { return nil, err } resp = &types.ListOpenWorkflowExecutionsResponse{} resp.Executions = persistenceResp.Executions resp.NextPageToken = persistenceResp.NextPageToken return resp, nil } // ListArchivedWorkflowExecutions - retrieves archived info for closed workflow executions in a domain func (wh *WorkflowHandler) ListArchivedWorkflowExecutions( ctx context.Context, listRequest *types.ListArchivedWorkflowExecutionsRequest, ) (resp *types.ListArchivedWorkflowExecutionsResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateListArchivedWorkflowExecutionsRequest(ctx, listRequest); err != nil { return nil, err } if !wh.GetArchivalMetadata().GetVisibilityConfig().ClusterConfiguredForArchival() { return nil, &types.BadRequestError{Message: "Cluster is not configured for visibility archival"} } if !wh.GetArchivalMetadata().GetVisibilityConfig().ReadEnabled() { return nil, &types.BadRequestError{Message: "Cluster is not configured for reading archived visibility records"} } entry, err := wh.GetDomainCache().GetDomain(listRequest.GetDomain()) if err != nil { return nil, err } if entry.GetConfig().VisibilityArchivalStatus != types.ArchivalStatusEnabled { return nil, &types.BadRequestError{Message: "Domain is not configured for visibility archival"} } URI, err := archiver.NewURI(entry.GetConfig().VisibilityArchivalURI) if err != nil { return nil, err } visibilityArchiver, err := wh.GetArchiverProvider().GetVisibilityArchiver(URI.Scheme(), service.Frontend) if err != nil { return nil, err } archiverRequest := &archiver.QueryVisibilityRequest{ DomainID: entry.GetInfo().ID, PageSize: int(listRequest.GetPageSize()), NextPageToken: listRequest.NextPageToken, Query: listRequest.GetQuery(), } archiverResponse, err := visibilityArchiver.Query(ctx, URI, archiverRequest) if err != nil { return nil, err } // special handling of ExecutionTime for cron or retry for _, execution := range archiverResponse.Executions { if execution.GetExecutionTime() == 0 { execution.ExecutionTime = common.Int64Ptr(execution.GetStartTime()) } } return &types.ListArchivedWorkflowExecutionsResponse{ Executions: archiverResponse.Executions, NextPageToken: archiverResponse.NextPageToken, }, nil } // ListClosedWorkflowExecutions - retrieves info for closed workflow executions in a domain func (wh *WorkflowHandler) ListClosedWorkflowExecutions( ctx context.Context, listRequest *types.ListClosedWorkflowExecutionsRequest, ) (resp *types.ListClosedWorkflowExecutionsResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateListClosedWorkflowExecutionsRequest(ctx, listRequest); err != nil { return nil, err } domain := listRequest.GetDomain() domainID, err := wh.GetDomainCache().GetDomainID(domain) if err != nil { return nil, err } baseReq := persistence.ListWorkflowExecutionsRequest{ DomainUUID: domainID, Domain: domain, PageSize: int(listRequest.GetMaximumPageSize()), NextPageToken: listRequest.NextPageToken, EarliestTime: listRequest.StartTimeFilter.GetEarliestTime(), LatestTime: listRequest.StartTimeFilter.GetLatestTime(), } var persistenceResp *persistence.ListWorkflowExecutionsResponse if listRequest.ExecutionFilter != nil { if wh.config.DisableListVisibilityByFilter(domain) { err = validate.ErrNoPermission } else { persistenceResp, err = wh.GetVisibilityManager().ListClosedWorkflowExecutionsByWorkflowID( ctx, &persistence.ListWorkflowExecutionsByWorkflowIDRequest{ ListWorkflowExecutionsRequest: baseReq, WorkflowID: listRequest.ExecutionFilter.GetWorkflowID(), }, ) } wh.GetLogger().Debug("List closed workflow with filter", tag.WorkflowDomainName(listRequest.GetDomain()), tag.WorkflowListWorkflowFilterByID) } else if listRequest.TypeFilter != nil { if wh.config.DisableListVisibilityByFilter(domain) { err = validate.ErrNoPermission } else { persistenceResp, err = wh.GetVisibilityManager().ListClosedWorkflowExecutionsByType( ctx, &persistence.ListWorkflowExecutionsByTypeRequest{ ListWorkflowExecutionsRequest: baseReq, WorkflowTypeName: listRequest.TypeFilter.GetName(), }, ) } wh.GetLogger().Debug("List closed workflow with filter", tag.WorkflowDomainName(listRequest.GetDomain()), tag.WorkflowListWorkflowFilterByType) } else if listRequest.StatusFilter != nil { if wh.config.DisableListVisibilityByFilter(domain) { err = validate.ErrNoPermission } else { persistenceResp, err = wh.GetVisibilityManager().ListClosedWorkflowExecutionsByStatus( ctx, &persistence.ListClosedWorkflowExecutionsByStatusRequest{ ListWorkflowExecutionsRequest: baseReq, Status: listRequest.GetStatusFilter(), }, ) } wh.GetLogger().Debug("List closed workflow with filter", tag.WorkflowDomainName(listRequest.GetDomain()), tag.WorkflowListWorkflowFilterByStatus) } else { persistenceResp, err = wh.GetVisibilityManager().ListClosedWorkflowExecutions(ctx, &baseReq) } if err != nil { return nil, err } resp = &types.ListClosedWorkflowExecutionsResponse{} resp.Executions = persistenceResp.Executions resp.NextPageToken = persistenceResp.NextPageToken return resp, nil } // ListWorkflowExecutions - retrieves info for workflow executions in a domain func (wh *WorkflowHandler) ListWorkflowExecutions( ctx context.Context, listRequest *types.ListWorkflowExecutionsRequest, ) (resp *types.ListWorkflowExecutionsResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateListWorkflowExecutionsRequest(ctx, listRequest); err != nil { return nil, err } validatedQuery, err := wh.visibilityQueryValidator.ValidateQuery(listRequest.GetQuery()) if err != nil { return nil, err } domain := listRequest.GetDomain() domainID, err := wh.GetDomainCache().GetDomainID(domain) if err != nil { return nil, err } req := &persistence.ListWorkflowExecutionsByQueryRequest{ DomainUUID: domainID, Domain: domain, PageSize: int(listRequest.GetPageSize()), NextPageToken: listRequest.NextPageToken, Query: validatedQuery, } persistenceResp, err := wh.GetVisibilityManager().ListWorkflowExecutions(ctx, req) if err != nil { return nil, err } resp = &types.ListWorkflowExecutionsResponse{} resp.Executions = persistenceResp.Executions resp.NextPageToken = persistenceResp.NextPageToken return resp, nil } ================================================ FILE: service/frontend/api/producer_manager.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination producer_manager_mock.go -self_package github.com/uber/cadence/service/frontend/api package api import ( "fmt" "time" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/asyncworkflow/queue/provider" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type ( // ProducerManager is used to create a producer for a domain. // Producer is used for Async APIs such as StartWorkflowExecutionAsync ProducerManager interface { GetProducerByDomain(domain string) (messaging.Producer, error) } producerManagerImpl struct { domainCache cache.DomainCache provider queue.Provider logger log.Logger metricsClient metrics.Client producerCache cache.Cache } ) func NewProducerManager( domainCache cache.DomainCache, provider queue.Provider, logger log.Logger, metricsClient metrics.Client, ) ProducerManager { return &producerManagerImpl{ domainCache: domainCache, provider: provider, logger: logger, metricsClient: metricsClient, producerCache: cache.New(&cache.Options{ TTL: time.Minute * 5, InitialCapacity: 5, MaxCount: 100, Pin: true, MetricsScope: metricsClient.Scope(metrics.PersistenceGetShardScope), // was metrics.Frontend, using incorrect int Logger: logger, }), } } // GetProducerByDomain returns a producer for a domain func (q *producerManagerImpl) GetProducerByDomain( domain string, ) (messaging.Producer, error) { domainEntry, err := q.domainCache.GetDomain(domain) if err != nil { return nil, err } if !domainEntry.GetConfig().AsyncWorkflowConfig.Enabled { return nil, &types.BadRequestError{Message: fmt.Sprintf("async workflow is not enabled for domain %v", domain)} } queueName := domainEntry.GetConfig().AsyncWorkflowConfig.PredefinedQueueName var queue provider.Queue if queueName != "" { queue, err = q.provider.GetPredefinedQueue(queueName) if err != nil { return nil, err } } else { queue, err = q.provider.GetQueue(domainEntry.GetConfig().AsyncWorkflowConfig.QueueType, domainEntry.GetConfig().AsyncWorkflowConfig.QueueConfig) if err != nil { return nil, err } } queueID := queue.ID() val := q.producerCache.Get(queueID) if val != nil { return val.(messaging.Producer), nil } producer, err := queue.CreateProducer(&provider.Params{Logger: q.logger, MetricsClient: q.metricsClient}) if err != nil { return nil, err } // PutIfNotExist is thread safe, and will either return the value that was already in the cache or the value we just created // another thread might have inserted a value between the Get and PutIfNotExist, but that is ok // it should never return an error as we do not use Pin val, err = q.producerCache.PutIfNotExist(queueID, producer) if err != nil { return nil, err } return val.(messaging.Producer), nil } ================================================ FILE: service/frontend/api/producer_manager_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: producer_manager.go // // Generated by this command: // // mockgen -package api -source producer_manager.go -destination producer_manager_mock.go -self_package github.com/uber/cadence/service/frontend/api // // Package api is a generated GoMock package. package api import ( reflect "reflect" gomock "go.uber.org/mock/gomock" messaging "github.com/uber/cadence/common/messaging" ) // MockProducerManager is a mock of ProducerManager interface. type MockProducerManager struct { ctrl *gomock.Controller recorder *MockProducerManagerMockRecorder isgomock struct{} } // MockProducerManagerMockRecorder is the mock recorder for MockProducerManager. type MockProducerManagerMockRecorder struct { mock *MockProducerManager } // NewMockProducerManager creates a new mock instance. func NewMockProducerManager(ctrl *gomock.Controller) *MockProducerManager { mock := &MockProducerManager{ctrl: ctrl} mock.recorder = &MockProducerManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProducerManager) EXPECT() *MockProducerManagerMockRecorder { return m.recorder } // GetProducerByDomain mocks base method. func (m *MockProducerManager) GetProducerByDomain(domain string) (messaging.Producer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetProducerByDomain", domain) ret0, _ := ret[0].(messaging.Producer) ret1, _ := ret[1].(error) return ret0, ret1 } // GetProducerByDomain indicates an expected call of GetProducerByDomain. func (mr *MockProducerManagerMockRecorder) GetProducerByDomain(domain any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProducerByDomain", reflect.TypeOf((*MockProducerManager)(nil).GetProducerByDomain), domain) } ================================================ FILE: service/frontend/api/producer_manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package api import ( "fmt" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/asyncworkflow/queue/provider" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestGetProducerByDomain(t *testing.T) { testCases := []struct { name string domain string mockSetup func(*cache.MockDomainCache, *queue.MockProvider, *provider.MockQueue, *cache.MockCache) wantErr bool }{ { name: "Success case - cache miss, predefined queue", domain: "test-domain", mockSetup: func(mockDomainCache *cache.MockDomainCache, mockProvider *queue.MockProvider, mockQueue *provider.MockQueue, mockProducerCache *cache.MockCache) { mockDomainCache.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( nil, &persistence.DomainConfig{ AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "testQueue", }, }, nil, 0, ), nil) mockProvider.EXPECT().GetPredefinedQueue("testQueue").Return(mockQueue, nil) mockQueue.EXPECT().ID().Return("q1") mockProducerCache.EXPECT().Get(gomock.Any()).Return(nil) producer := messaging.NewNoopProducer() mockQueue.EXPECT().CreateProducer(gomock.Any()).Return(producer, nil) mockProducerCache.EXPECT().PutIfNotExist("q1", producer).Return(producer, nil) }, wantErr: false, }, { name: "Success case - cache miss, customized queue", domain: "test-domain", mockSetup: func(mockDomainCache *cache.MockDomainCache, mockProvider *queue.MockProvider, mockQueue *provider.MockQueue, mockProducerCache *cache.MockCache) { mockDomainCache.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( nil, &persistence.DomainConfig{ AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"brokers":["localhost:9092"],"topics":["test-topic"]}`), }, }, }, nil, 0, ), nil) mockProvider.EXPECT().GetQueue("kafka", gomock.Any()).Return(mockQueue, nil) mockQueue.EXPECT().ID().Return("q1") mockProducerCache.EXPECT().Get(gomock.Any()).Return(nil) producer := messaging.NewNoopProducer() mockQueue.EXPECT().CreateProducer(gomock.Any()).Return(producer, nil) mockProducerCache.EXPECT().PutIfNotExist("q1", producer).Return(producer, nil) }, wantErr: false, }, { name: "Success case - cache hit, predefined queue", domain: "test-domain", mockSetup: func(mockDomainCache *cache.MockDomainCache, mockProvider *queue.MockProvider, mockQueue *provider.MockQueue, mockProducerCache *cache.MockCache) { mockDomainCache.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( nil, &persistence.DomainConfig{ AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "testQueue", }, }, nil, 0, ), nil) mockProvider.EXPECT().GetPredefinedQueue("testQueue").Return(mockQueue, nil) mockQueue.EXPECT().ID().Return("q1") producer := messaging.NewNoopProducer() mockProducerCache.EXPECT().Get(gomock.Any()).Return(producer) }, wantErr: false, }, { name: "Success case - cache hit, customized queue", domain: "test-domain", mockSetup: func(mockDomainCache *cache.MockDomainCache, mockProvider *queue.MockProvider, mockQueue *provider.MockQueue, mockProducerCache *cache.MockCache) { mockDomainCache.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( nil, &persistence.DomainConfig{ AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"brokers":["localhost:9092"],"topics":["test-topic"]}`), }, }, }, nil, 0, ), nil) mockProvider.EXPECT().GetQueue("kafka", gomock.Any()).Return(mockQueue, nil) mockQueue.EXPECT().ID().Return("q1") producer := messaging.NewNoopProducer() mockProducerCache.EXPECT().Get(gomock.Any()).Return(producer) }, wantErr: false, }, { name: "Error case - domain cache error", domain: "test-domain", mockSetup: func(mockDomainCache *cache.MockDomainCache, mockProvider *queue.MockProvider, mockQueue *provider.MockQueue, mockProducerCache *cache.MockCache) { mockDomainCache.EXPECT().GetDomain("test-domain").Return(nil, fmt.Errorf("error")) }, wantErr: true, }, { name: "Error case - provider error", domain: "test-domain", mockSetup: func(mockDomainCache *cache.MockDomainCache, mockProvider *queue.MockProvider, mockQueue *provider.MockQueue, mockProducerCache *cache.MockCache) { mockDomainCache.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( nil, &persistence.DomainConfig{ AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"brokers":["localhost:9092"],"topics":["test-topic"]}`), }, }, }, nil, 0, ), nil) mockProvider.EXPECT().GetQueue("kafka", gomock.Any()).Return(nil, fmt.Errorf("error")) }, wantErr: true, }, { name: "Error case - cache miss, create producer error", domain: "test-domain", mockSetup: func(mockDomainCache *cache.MockDomainCache, mockProvider *queue.MockProvider, mockQueue *provider.MockQueue, mockProducerCache *cache.MockCache) { mockDomainCache.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( nil, &persistence.DomainConfig{ AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"brokers":["localhost:9092"],"topics":["test-topic"]}`), }, }, }, nil, 0, ), nil) mockProvider.EXPECT().GetQueue("kafka", gomock.Any()).Return(mockQueue, nil) mockQueue.EXPECT().ID().Return("q1") mockProducerCache.EXPECT().Get(gomock.Any()).Return(nil) mockQueue.EXPECT().CreateProducer(gomock.Any()).Return(nil, fmt.Errorf("error")) }, wantErr: true, }, { name: "Error case - async wf not enabled", domain: "test-domain", mockSetup: func(mockDomainCache *cache.MockDomainCache, mockProvider *queue.MockProvider, mockQueue *provider.MockQueue, mockProducerCache *cache.MockCache) { mockDomainCache.EXPECT().GetDomain("test-domain").Return(cache.NewGlobalDomainCacheEntryForTest( nil, &persistence.DomainConfig{ AsyncWorkflowConfig: types.AsyncWorkflowConfiguration{ Enabled: false, }, }, nil, 0, ), nil) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockProvider := queue.NewMockProvider(mockCtrl) mockQueue := provider.NewMockQueue(mockCtrl) mockProducerCache := cache.NewMockCache(mockCtrl) producerManager := NewProducerManager( mockDomainCache, mockProvider, log.NewNoop(), metrics.NewNoopMetricsClient(), ) producerManager.(*producerManagerImpl).producerCache = mockProducerCache tc.mockSetup(mockDomainCache, mockProvider, mockQueue, mockProducerCache) producer, err := producerManager.GetProducerByDomain(tc.domain) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.NotNil(t, producer) } }) } } ================================================ FILE: service/frontend/api/refresh_workflow_tasks.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package api import ( "context" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/validate" ) // RefreshWorkflowTasks re-generates the workflow tasks func (wh *WorkflowHandler) RefreshWorkflowTasks( ctx context.Context, request *types.RefreshWorkflowTasksRequest, ) error { if wh.isShuttingDown() { return validate.ErrShuttingDown } if err := wh.requestValidator.ValidateRefreshWorkflowTasksRequest(ctx, request); err != nil { return err } domainEntry, err := wh.GetDomainCache().GetDomain(request.GetDomain()) if err != nil { return err } err = wh.GetHistoryClient().RefreshWorkflowTasks(ctx, &types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: domainEntry.GetInfo().ID, Request: request, }) if err != nil { return err } return nil } ================================================ FILE: service/frontend/api/refresh_workflow_tasks_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package api import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" frontendcfg "github.com/uber/cadence/service/frontend/config" ) var testDomainCacheEntry = cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: "domain", ID: "domain-id"}, &persistence.DomainConfig{}, "", ) type mockDeps struct { mockResource *resource.Test mockDomainCache *cache.MockDomainCache mockHistoryClient *history.MockClient mockMatchingClient *matching.MockClient mockProducer *mocks.KafkaProducer mockMessagingClient messaging.Client mockMetadataMgr *mocks.MetadataManager mockHistoryV2Mgr *mocks.HistoryV2Manager mockVisibilityMgr *mocks.VisibilityManager mockArchivalMetadata *archiver.MockArchivalMetadata mockArchiverProvider *provider.MockArchiverProvider mockHistoryArchiver *archiver.HistoryArchiverMock mockVisibilityArchiver *archiver.VisibilityArchiverMock mockVersionChecker *client.MockVersionChecker mockTokenSerializer *common.MockTaskTokenSerializer mockDomainHandler *domain.MockHandler mockRequestValidator *MockRequestValidator dynamicClient dynamicconfig.Client } func setupMocksForWorkflowHandler(t *testing.T) (*WorkflowHandler, *mockDeps) { ctrl := gomock.NewController(t) mockResource := resource.NewTest(t, ctrl, metrics.Frontend) mockProducer := &mocks.KafkaProducer{} dynamicClient := dynamicconfig.NewInMemoryClient() deps := &mockDeps{ mockResource: mockResource, mockDomainCache: mockResource.DomainCache, mockHistoryClient: mockResource.HistoryClient, mockMatchingClient: mockResource.MatchingClient, mockMetadataMgr: mockResource.MetadataMgr, mockHistoryV2Mgr: mockResource.HistoryMgr, mockVisibilityMgr: mockResource.VisibilityMgr, mockArchivalMetadata: mockResource.ArchivalMetadata, mockArchiverProvider: mockResource.ArchiverProvider, mockTokenSerializer: common.NewMockTaskTokenSerializer(ctrl), mockProducer: mockProducer, mockMessagingClient: mocks.NewMockMessagingClient(mockProducer, nil), mockHistoryArchiver: &archiver.HistoryArchiverMock{}, mockVisibilityArchiver: &archiver.VisibilityArchiverMock{}, mockVersionChecker: client.NewMockVersionChecker(ctrl), mockDomainHandler: domain.NewMockHandler(ctrl), mockRequestValidator: NewMockRequestValidator(ctrl), dynamicClient: dynamicClient, } logger := testlogger.New(t) config := frontendcfg.NewConfig( dynamicconfig.NewCollection( dynamicClient, logger, ), numHistoryShards, false, "hostname", logger, ) wh := NewWorkflowHandler(deps.mockResource, config, deps.mockVersionChecker, deps.mockDomainHandler) wh.requestValidator = deps.mockRequestValidator return wh, deps } func TestRefreshWorkflowTasks(t *testing.T) { testCases := []struct { name string req *types.RefreshWorkflowTasksRequest setupMocks func(*mockDeps) expectError bool expectedError string }{ { name: "success", req: &types.RefreshWorkflowTasksRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateRefreshWorkflowTasksRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainCache.EXPECT().GetDomain("domain").Return(testDomainCacheEntry, nil) deps.mockHistoryClient.EXPECT().RefreshWorkflowTasks(gomock.Any(), &types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: "domain-id", Request: &types.RefreshWorkflowTasksRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, }).Return(nil) }, expectError: false, }, { name: "history client error", req: &types.RefreshWorkflowTasksRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateRefreshWorkflowTasksRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainCache.EXPECT().GetDomain("domain").Return(testDomainCacheEntry, nil) deps.mockHistoryClient.EXPECT().RefreshWorkflowTasks(gomock.Any(), &types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: "domain-id", Request: &types.RefreshWorkflowTasksRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, }).Return(errors.New("history error")) }, expectError: true, expectedError: "history error", }, { name: "cache error", req: &types.RefreshWorkflowTasksRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateRefreshWorkflowTasksRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainCache.EXPECT().GetDomain("domain").Return(nil, errors.New("cache error")) }, expectError: true, expectedError: "cache error", }, { name: "validator error", req: &types.RefreshWorkflowTasksRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateRefreshWorkflowTasksRequest(gomock.Any(), gomock.Any()).Return(errors.New("validator error")) }, expectError: true, expectedError: "validator error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { wh, deps := setupMocksForWorkflowHandler(t) tc.setupMocks(deps) err := wh.RefreshWorkflowTasks(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: service/frontend/api/request_validator.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination request_validator_mock.go -self_package github.com/uber/cadence/service/frontend/api requestValidator package api import ( "context" "fmt" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/config" "github.com/uber/cadence/service/frontend/validate" ) type ( RequestValidator interface { ValidateRefreshWorkflowTasksRequest(context.Context, *types.RefreshWorkflowTasksRequest) error ValidateDescribeTaskListRequest(context.Context, *types.DescribeTaskListRequest) error ValidateListTaskListPartitionsRequest(context.Context, *types.ListTaskListPartitionsRequest) error ValidateGetTaskListsByDomainRequest(context.Context, *types.GetTaskListsByDomainRequest) error ValidateResetStickyTaskListRequest(context.Context, *types.ResetStickyTaskListRequest) error ValidateCountWorkflowExecutionsRequest(context.Context, *types.CountWorkflowExecutionsRequest) error ValidateListWorkflowExecutionsRequest(context.Context, *types.ListWorkflowExecutionsRequest) error ValidateListOpenWorkflowExecutionsRequest(context.Context, *types.ListOpenWorkflowExecutionsRequest) error ValidateListArchivedWorkflowExecutionsRequest(context.Context, *types.ListArchivedWorkflowExecutionsRequest) error ValidateListClosedWorkflowExecutionsRequest(context.Context, *types.ListClosedWorkflowExecutionsRequest) error ValidateRegisterDomainRequest(context.Context, *types.RegisterDomainRequest) error ValidateDescribeDomainRequest(context.Context, *types.DescribeDomainRequest) error ValidateUpdateDomainRequest(context.Context, *types.UpdateDomainRequest) error ValidateDeleteDomainRequest(context.Context, *types.DeleteDomainRequest) error ValidateDeprecateDomainRequest(context.Context, *types.DeprecateDomainRequest) error ValidateFailoverDomainRequest(context.Context, *types.FailoverDomainRequest) error } requestValidatorImpl struct { logger log.Logger metricsClient metrics.Client config *config.Config } ) func NewRequestValidator(logger log.Logger, metricsClient metrics.Client, config *config.Config) RequestValidator { return &requestValidatorImpl{ logger: logger, metricsClient: metricsClient, config: config, } } func (v *requestValidatorImpl) validateTaskList(t *types.TaskList, scope metrics.Scope, domain string) error { if t == nil || t.GetName() == "" { return validate.ErrTaskListNotSet } if !common.IsValidIDLength( t.GetName(), scope, v.config.MaxIDLengthWarnLimit(), v.config.TaskListNameMaxLength(domain), metrics.CadenceErrTaskListNameExceededWarnLimit, domain, v.logger, tag.IDTypeTaskListName) { return validate.ErrTaskListTooLong } return nil } func checkRequiredDomainDataKVs(requiredDomainDataKeys map[string]interface{}, domainData map[string]string) error { // check requiredDomainDataKeys for k := range requiredDomainDataKeys { _, ok := domainData[k] if !ok { return fmt.Errorf("domain data error, missing required key %v . All required keys: %v", k, requiredDomainDataKeys) } } return nil } func checkFailOverPermission(config *config.Config, domainName string) error { if config.Lockdown(domainName) { return validate.ErrDomainInLockdown } return nil } func (v *requestValidatorImpl) isListRequestPageSizeTooLarge(pageSize int32, domain string) bool { return common.IsAdvancedVisibilityReadingEnabled(v.config.ReadVisibilityStoreName(domain) != "db", v.config.IsAdvancedVisConfigExist) && pageSize > int32(v.config.ESIndexMaxResultWindow()) } func (v *requestValidatorImpl) ValidateRefreshWorkflowTasksRequest(ctx context.Context, req *types.RefreshWorkflowTasksRequest) error { if req == nil { return validate.ErrRequestNotSet } return validate.CheckExecution(req.Execution) } func (v *requestValidatorImpl) ValidateDescribeTaskListRequest(ctx context.Context, request *types.DescribeTaskListRequest) error { if request == nil { return validate.ErrRequestNotSet } if request.GetDomain() == "" { return validate.ErrDomainNotSet } if request.TaskListType == nil { return validate.ErrTaskListTypeNotSet } scope := getMetricsScopeWithDomain(metrics.FrontendDescribeTaskListScope, request, v.metricsClient).Tagged(metrics.GetContextTags(ctx)...) return v.validateTaskList(request.TaskList, scope, request.GetDomain()) } func (v *requestValidatorImpl) ValidateListTaskListPartitionsRequest(ctx context.Context, request *types.ListTaskListPartitionsRequest) error { if request == nil { return validate.ErrRequestNotSet } if request.GetDomain() == "" { return validate.ErrDomainNotSet } scope := getMetricsScopeWithDomain(metrics.FrontendListTaskListPartitionsScope, request, v.metricsClient).Tagged(metrics.GetContextTags(ctx)...) return v.validateTaskList(request.TaskList, scope, request.GetDomain()) } func (v *requestValidatorImpl) ValidateGetTaskListsByDomainRequest(ctx context.Context, request *types.GetTaskListsByDomainRequest) error { if request == nil { return validate.ErrRequestNotSet } if request.GetDomain() == "" { return validate.ErrDomainNotSet } return nil } func (v *requestValidatorImpl) ValidateResetStickyTaskListRequest(ctx context.Context, resetRequest *types.ResetStickyTaskListRequest) error { if resetRequest == nil { return validate.ErrRequestNotSet } domainName := resetRequest.GetDomain() if domainName == "" { return validate.ErrDomainNotSet } wfExecution := resetRequest.GetExecution() return validate.CheckExecution(wfExecution) } func (v *requestValidatorImpl) ValidateCountWorkflowExecutionsRequest(ctx context.Context, countRequest *types.CountWorkflowExecutionsRequest) error { if countRequest == nil { return validate.ErrRequestNotSet } if countRequest.GetDomain() == "" { return validate.ErrDomainNotSet } return nil } func (v *requestValidatorImpl) ValidateListWorkflowExecutionsRequest(ctx context.Context, listRequest *types.ListWorkflowExecutionsRequest) error { if listRequest == nil { return validate.ErrRequestNotSet } if listRequest.GetDomain() == "" { return validate.ErrDomainNotSet } if listRequest.GetPageSize() <= 0 { listRequest.PageSize = int32(v.config.VisibilityMaxPageSize(listRequest.GetDomain())) } if v.isListRequestPageSizeTooLarge(listRequest.GetPageSize(), listRequest.GetDomain()) { return &types.BadRequestError{Message: fmt.Sprintf("Pagesize is larger than allow %d", v.config.ESIndexMaxResultWindow())} } return nil } func (v *requestValidatorImpl) ValidateListOpenWorkflowExecutionsRequest(ctx context.Context, listRequest *types.ListOpenWorkflowExecutionsRequest) error { if listRequest == nil { return validate.ErrRequestNotSet } if listRequest.GetDomain() == "" { return validate.ErrDomainNotSet } if listRequest.StartTimeFilter == nil { return &types.BadRequestError{Message: "StartTimeFilter is required"} } if listRequest.StartTimeFilter.EarliestTime == nil { return &types.BadRequestError{Message: "EarliestTime in StartTimeFilter is required"} } if listRequest.StartTimeFilter.LatestTime == nil { return &types.BadRequestError{Message: "LatestTime in StartTimeFilter is required"} } if listRequest.StartTimeFilter.GetEarliestTime() > listRequest.StartTimeFilter.GetLatestTime() { return &types.BadRequestError{Message: "EarliestTime in StartTimeFilter should not be larger than LatestTime"} } if listRequest.ExecutionFilter != nil && listRequest.TypeFilter != nil { return &types.BadRequestError{Message: "Only one of ExecutionFilter or TypeFilter is allowed"} } if listRequest.GetMaximumPageSize() <= 0 { listRequest.MaximumPageSize = int32(v.config.VisibilityMaxPageSize(listRequest.GetDomain())) } if v.isListRequestPageSizeTooLarge(listRequest.GetMaximumPageSize(), listRequest.GetDomain()) { return &types.BadRequestError{Message: fmt.Sprintf("Pagesize is larger than allow %d", v.config.ESIndexMaxResultWindow())} } return nil } func (v *requestValidatorImpl) ValidateListArchivedWorkflowExecutionsRequest(ctx context.Context, listRequest *types.ListArchivedWorkflowExecutionsRequest) error { if listRequest == nil { return validate.ErrRequestNotSet } if listRequest.GetDomain() == "" { return validate.ErrDomainNotSet } if listRequest.GetPageSize() <= 0 { listRequest.PageSize = int32(v.config.VisibilityMaxPageSize(listRequest.GetDomain())) } maxPageSize := v.config.VisibilityArchivalQueryMaxPageSize() if int(listRequest.GetPageSize()) > maxPageSize { return &types.BadRequestError{Message: fmt.Sprintf("Pagesize is larger than allowed %d", maxPageSize)} } return nil } func (v *requestValidatorImpl) ValidateListClosedWorkflowExecutionsRequest(ctx context.Context, listRequest *types.ListClosedWorkflowExecutionsRequest) error { if listRequest == nil { return validate.ErrRequestNotSet } if listRequest.GetDomain() == "" { return validate.ErrDomainNotSet } if listRequest.StartTimeFilter == nil { return &types.BadRequestError{Message: "StartTimeFilter is required"} } if listRequest.StartTimeFilter.EarliestTime == nil { return &types.BadRequestError{Message: "EarliestTime in StartTimeFilter is required"} } if listRequest.StartTimeFilter.LatestTime == nil { return &types.BadRequestError{Message: "LatestTime in StartTimeFilter is required"} } if listRequest.StartTimeFilter.GetEarliestTime() > listRequest.StartTimeFilter.GetLatestTime() { return &types.BadRequestError{Message: "EarliestTime in StartTimeFilter should not be larger than LatestTime"} } filterCount := 0 if listRequest.ExecutionFilter != nil { filterCount++ } if listRequest.TypeFilter != nil { filterCount++ } if listRequest.StatusFilter != nil { filterCount++ } if filterCount > 1 { return &types.BadRequestError{Message: "Only one of ExecutionFilter, TypeFilter or StatusFilter is allowed"} } // If ExecutionFilter is provided with one of TypeFilter or StatusFilter, use ExecutionFilter and ignore other filter if listRequest.GetMaximumPageSize() <= 0 { listRequest.MaximumPageSize = int32(v.config.VisibilityMaxPageSize(listRequest.GetDomain())) } if v.isListRequestPageSizeTooLarge(listRequest.GetMaximumPageSize(), listRequest.GetDomain()) { return &types.BadRequestError{Message: fmt.Sprintf("Pagesize is larger than allow %d", v.config.ESIndexMaxResultWindow())} } return nil } func (v *requestValidatorImpl) ValidateRegisterDomainRequest(ctx context.Context, registerRequest *types.RegisterDomainRequest) error { if registerRequest == nil { return validate.ErrRequestNotSet } if registerRequest.GetName() == "" { return validate.ErrDomainNotSet } domain := registerRequest.GetName() scope := v.metricsClient.Scope(metrics.FrontendRegisterDomainScope).Tagged(metrics.DomainTag(domain)).Tagged(metrics.GetContextTags(ctx)...) if !common.IsValidIDLength( domain, scope, v.config.MaxIDLengthWarnLimit(), v.config.DomainNameMaxLength(domain), metrics.CadenceErrTaskListNameExceededWarnLimit, domain, v.logger, tag.IDTypeDomainName) { return validate.ErrDomainTooLong } if registerRequest.GetWorkflowExecutionRetentionPeriodInDays() > int32(v.config.DomainConfig.MaxRetentionDays()) { return validate.ErrInvalidRetention } if err := checkRequiredDomainDataKVs(v.config.DomainConfig.RequiredDomainDataKeys(), registerRequest.GetData()); err != nil { return err } return validate.CheckPermission(v.config, registerRequest.SecurityToken) } func (v *requestValidatorImpl) ValidateDescribeDomainRequest(ctx context.Context, describeRequest *types.DescribeDomainRequest) error { if describeRequest == nil { return validate.ErrRequestNotSet } if describeRequest.GetName() == "" && describeRequest.GetUUID() == "" { return validate.ErrDomainNotSet } return nil } func (v *requestValidatorImpl) ValidateUpdateDomainRequest(ctx context.Context, updateRequest *types.UpdateDomainRequest) error { if updateRequest == nil { return validate.ErrRequestNotSet } if updateRequest.GetName() == "" { return validate.ErrDomainNotSet } if updateRequest.WorkflowExecutionRetentionPeriodInDays != nil && *updateRequest.WorkflowExecutionRetentionPeriodInDays > int32(v.config.DomainConfig.MaxRetentionDays()) { return validate.ErrInvalidRetention } isFailover := isFailoverRequest(updateRequest) // don't require permission for failover request if isFailover { // reject the failover if the cluster is in lockdown if err := checkFailOverPermission(v.config, updateRequest.GetName()); err != nil { return err } } else { if err := validate.CheckPermission(v.config, updateRequest.SecurityToken); err != nil { return err } } return nil } func (v *requestValidatorImpl) ValidateDeleteDomainRequest(ctx context.Context, deleteRequest *types.DeleteDomainRequest) error { if deleteRequest == nil { return validate.ErrRequestNotSet } if deleteRequest.GetName() == "" { return validate.ErrDomainNotSet } return validate.CheckPermission(v.config, deleteRequest.SecurityToken) } func (v *requestValidatorImpl) ValidateDeprecateDomainRequest(ctx context.Context, deprecateRequest *types.DeprecateDomainRequest) error { if deprecateRequest == nil { return validate.ErrRequestNotSet } if deprecateRequest.GetName() == "" { return validate.ErrDomainNotSet } return validate.CheckPermission(v.config, deprecateRequest.SecurityToken) } func (v *requestValidatorImpl) ValidateFailoverDomainRequest(ctx context.Context, failoverDomainRequest *types.FailoverDomainRequest) error { if failoverDomainRequest == nil { return validate.ErrRequestNotSet } if failoverDomainRequest.GetDomainName() == "" { return validate.ErrDomainNotSet } if failoverDomainRequest.DomainActiveClusterName == nil && failoverDomainRequest.ActiveClusters == nil { return &types.BadRequestError{Message: "DomainActiveClusterName or ActiveClusters must be provided to failover the domain"} } // Security token is not required for failover request - reject the failover if the cluster is in lockdown return checkFailOverPermission(v.config, failoverDomainRequest.GetDomainName()) } ================================================ FILE: service/frontend/api/request_validator_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: request_validator.go // // Generated by this command: // // mockgen -package api -source request_validator.go -destination request_validator_mock.go -self_package github.com/uber/cadence/service/frontend/api requestValidator // // Package api is a generated GoMock package. package api import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockRequestValidator is a mock of RequestValidator interface. type MockRequestValidator struct { ctrl *gomock.Controller recorder *MockRequestValidatorMockRecorder isgomock struct{} } // MockRequestValidatorMockRecorder is the mock recorder for MockRequestValidator. type MockRequestValidatorMockRecorder struct { mock *MockRequestValidator } // NewMockRequestValidator creates a new mock instance. func NewMockRequestValidator(ctrl *gomock.Controller) *MockRequestValidator { mock := &MockRequestValidator{ctrl: ctrl} mock.recorder = &MockRequestValidatorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockRequestValidator) EXPECT() *MockRequestValidatorMockRecorder { return m.recorder } // ValidateCountWorkflowExecutionsRequest mocks base method. func (m *MockRequestValidator) ValidateCountWorkflowExecutionsRequest(arg0 context.Context, arg1 *types.CountWorkflowExecutionsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateCountWorkflowExecutionsRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateCountWorkflowExecutionsRequest indicates an expected call of ValidateCountWorkflowExecutionsRequest. func (mr *MockRequestValidatorMockRecorder) ValidateCountWorkflowExecutionsRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateCountWorkflowExecutionsRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateCountWorkflowExecutionsRequest), arg0, arg1) } // ValidateDeleteDomainRequest mocks base method. func (m *MockRequestValidator) ValidateDeleteDomainRequest(arg0 context.Context, arg1 *types.DeleteDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateDeleteDomainRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateDeleteDomainRequest indicates an expected call of ValidateDeleteDomainRequest. func (mr *MockRequestValidatorMockRecorder) ValidateDeleteDomainRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateDeleteDomainRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateDeleteDomainRequest), arg0, arg1) } // ValidateDeprecateDomainRequest mocks base method. func (m *MockRequestValidator) ValidateDeprecateDomainRequest(arg0 context.Context, arg1 *types.DeprecateDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateDeprecateDomainRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateDeprecateDomainRequest indicates an expected call of ValidateDeprecateDomainRequest. func (mr *MockRequestValidatorMockRecorder) ValidateDeprecateDomainRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateDeprecateDomainRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateDeprecateDomainRequest), arg0, arg1) } // ValidateDescribeDomainRequest mocks base method. func (m *MockRequestValidator) ValidateDescribeDomainRequest(arg0 context.Context, arg1 *types.DescribeDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateDescribeDomainRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateDescribeDomainRequest indicates an expected call of ValidateDescribeDomainRequest. func (mr *MockRequestValidatorMockRecorder) ValidateDescribeDomainRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateDescribeDomainRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateDescribeDomainRequest), arg0, arg1) } // ValidateDescribeTaskListRequest mocks base method. func (m *MockRequestValidator) ValidateDescribeTaskListRequest(arg0 context.Context, arg1 *types.DescribeTaskListRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateDescribeTaskListRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateDescribeTaskListRequest indicates an expected call of ValidateDescribeTaskListRequest. func (mr *MockRequestValidatorMockRecorder) ValidateDescribeTaskListRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateDescribeTaskListRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateDescribeTaskListRequest), arg0, arg1) } // ValidateFailoverDomainRequest mocks base method. func (m *MockRequestValidator) ValidateFailoverDomainRequest(arg0 context.Context, arg1 *types.FailoverDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateFailoverDomainRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateFailoverDomainRequest indicates an expected call of ValidateFailoverDomainRequest. func (mr *MockRequestValidatorMockRecorder) ValidateFailoverDomainRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateFailoverDomainRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateFailoverDomainRequest), arg0, arg1) } // ValidateGetTaskListsByDomainRequest mocks base method. func (m *MockRequestValidator) ValidateGetTaskListsByDomainRequest(arg0 context.Context, arg1 *types.GetTaskListsByDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateGetTaskListsByDomainRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateGetTaskListsByDomainRequest indicates an expected call of ValidateGetTaskListsByDomainRequest. func (mr *MockRequestValidatorMockRecorder) ValidateGetTaskListsByDomainRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateGetTaskListsByDomainRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateGetTaskListsByDomainRequest), arg0, arg1) } // ValidateListArchivedWorkflowExecutionsRequest mocks base method. func (m *MockRequestValidator) ValidateListArchivedWorkflowExecutionsRequest(arg0 context.Context, arg1 *types.ListArchivedWorkflowExecutionsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateListArchivedWorkflowExecutionsRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateListArchivedWorkflowExecutionsRequest indicates an expected call of ValidateListArchivedWorkflowExecutionsRequest. func (mr *MockRequestValidatorMockRecorder) ValidateListArchivedWorkflowExecutionsRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateListArchivedWorkflowExecutionsRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateListArchivedWorkflowExecutionsRequest), arg0, arg1) } // ValidateListClosedWorkflowExecutionsRequest mocks base method. func (m *MockRequestValidator) ValidateListClosedWorkflowExecutionsRequest(arg0 context.Context, arg1 *types.ListClosedWorkflowExecutionsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateListClosedWorkflowExecutionsRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateListClosedWorkflowExecutionsRequest indicates an expected call of ValidateListClosedWorkflowExecutionsRequest. func (mr *MockRequestValidatorMockRecorder) ValidateListClosedWorkflowExecutionsRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateListClosedWorkflowExecutionsRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateListClosedWorkflowExecutionsRequest), arg0, arg1) } // ValidateListOpenWorkflowExecutionsRequest mocks base method. func (m *MockRequestValidator) ValidateListOpenWorkflowExecutionsRequest(arg0 context.Context, arg1 *types.ListOpenWorkflowExecutionsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateListOpenWorkflowExecutionsRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateListOpenWorkflowExecutionsRequest indicates an expected call of ValidateListOpenWorkflowExecutionsRequest. func (mr *MockRequestValidatorMockRecorder) ValidateListOpenWorkflowExecutionsRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateListOpenWorkflowExecutionsRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateListOpenWorkflowExecutionsRequest), arg0, arg1) } // ValidateListTaskListPartitionsRequest mocks base method. func (m *MockRequestValidator) ValidateListTaskListPartitionsRequest(arg0 context.Context, arg1 *types.ListTaskListPartitionsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateListTaskListPartitionsRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateListTaskListPartitionsRequest indicates an expected call of ValidateListTaskListPartitionsRequest. func (mr *MockRequestValidatorMockRecorder) ValidateListTaskListPartitionsRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateListTaskListPartitionsRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateListTaskListPartitionsRequest), arg0, arg1) } // ValidateListWorkflowExecutionsRequest mocks base method. func (m *MockRequestValidator) ValidateListWorkflowExecutionsRequest(arg0 context.Context, arg1 *types.ListWorkflowExecutionsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateListWorkflowExecutionsRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateListWorkflowExecutionsRequest indicates an expected call of ValidateListWorkflowExecutionsRequest. func (mr *MockRequestValidatorMockRecorder) ValidateListWorkflowExecutionsRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateListWorkflowExecutionsRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateListWorkflowExecutionsRequest), arg0, arg1) } // ValidateRefreshWorkflowTasksRequest mocks base method. func (m *MockRequestValidator) ValidateRefreshWorkflowTasksRequest(arg0 context.Context, arg1 *types.RefreshWorkflowTasksRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateRefreshWorkflowTasksRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateRefreshWorkflowTasksRequest indicates an expected call of ValidateRefreshWorkflowTasksRequest. func (mr *MockRequestValidatorMockRecorder) ValidateRefreshWorkflowTasksRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRefreshWorkflowTasksRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateRefreshWorkflowTasksRequest), arg0, arg1) } // ValidateRegisterDomainRequest mocks base method. func (m *MockRequestValidator) ValidateRegisterDomainRequest(arg0 context.Context, arg1 *types.RegisterDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateRegisterDomainRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateRegisterDomainRequest indicates an expected call of ValidateRegisterDomainRequest. func (mr *MockRequestValidatorMockRecorder) ValidateRegisterDomainRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRegisterDomainRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateRegisterDomainRequest), arg0, arg1) } // ValidateResetStickyTaskListRequest mocks base method. func (m *MockRequestValidator) ValidateResetStickyTaskListRequest(arg0 context.Context, arg1 *types.ResetStickyTaskListRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateResetStickyTaskListRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateResetStickyTaskListRequest indicates an expected call of ValidateResetStickyTaskListRequest. func (mr *MockRequestValidatorMockRecorder) ValidateResetStickyTaskListRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateResetStickyTaskListRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateResetStickyTaskListRequest), arg0, arg1) } // ValidateUpdateDomainRequest mocks base method. func (m *MockRequestValidator) ValidateUpdateDomainRequest(arg0 context.Context, arg1 *types.UpdateDomainRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateUpdateDomainRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateUpdateDomainRequest indicates an expected call of ValidateUpdateDomainRequest. func (mr *MockRequestValidatorMockRecorder) ValidateUpdateDomainRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateUpdateDomainRequest", reflect.TypeOf((*MockRequestValidator)(nil).ValidateUpdateDomainRequest), arg0, arg1) } ================================================ FILE: service/frontend/api/request_validator_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package api import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" frontendcfg "github.com/uber/cadence/service/frontend/config" ) func setupMocksForRequestValidator(t *testing.T) (*requestValidatorImpl, *mockDeps) { logger := testlogger.New(t) metricsClient := metrics.NewNoopMetricsClient() dynamicClient := dynamicconfig.NewInMemoryClient() config := frontendcfg.NewConfig( dynamicconfig.NewCollection( dynamicClient, logger, ), numHistoryShards, true, "hostname", logger, ) deps := &mockDeps{ dynamicClient: dynamicClient, } v := NewRequestValidator(logger, metricsClient, config) return v.(*requestValidatorImpl), deps } func TestValidateRefreshWorkflowTasksRequest(t *testing.T) { testCases := []struct { name string req *types.RefreshWorkflowTasksRequest expectError bool expectedError string }{ { name: "success", req: &types.RefreshWorkflowTasksRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "execution not set", req: &types.RefreshWorkflowTasksRequest{ Domain: "domain", Execution: nil, }, expectError: true, expectedError: "Execution is not set on request.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, _ := setupMocksForRequestValidator(t) err := v.ValidateRefreshWorkflowTasksRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateValidateDescribeTaskListRequest(t *testing.T) { testCases := []struct { name string req *types.DescribeTaskListRequest expectError bool expectedError string }{ { name: "success", req: &types.DescribeTaskListRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, TaskListType: types.TaskListTypeActivity.Ptr(), }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.DescribeTaskListRequest{ Domain: "", }, expectError: true, expectedError: "Domain not set on request.", }, { name: "task list type not set", req: &types.DescribeTaskListRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, }, expectError: true, expectedError: "TaskListType is not set on request.", }, { name: "task list not set", req: &types.DescribeTaskListRequest{ Domain: "domain", TaskListType: types.TaskListTypeActivity.Ptr(), }, expectError: true, expectedError: "TaskList is not set on request.", }, { name: "task list name not set", req: &types.DescribeTaskListRequest{ Domain: "domain", TaskListType: types.TaskListTypeActivity.Ptr(), TaskList: &types.TaskList{}, }, expectError: true, expectedError: "TaskList is not set on request.", }, { name: "task list name too long", req: &types.DescribeTaskListRequest{ Domain: "domain", TaskListType: types.TaskListTypeActivity.Ptr(), TaskList: &types.TaskList{ Name: "taskListName", }, }, expectError: true, expectedError: "TaskList length exceeds limit.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskListNameMaxLength, 5)) err := v.ValidateDescribeTaskListRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateValidateListTaskListPartitionsRequest(t *testing.T) { testCases := []struct { name string req *types.ListTaskListPartitionsRequest expectError bool expectedError string }{ { name: "success", req: &types.ListTaskListPartitionsRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.ListTaskListPartitionsRequest{ Domain: "", }, expectError: true, expectedError: "Domain not set on request.", }, { name: "task list not set", req: &types.ListTaskListPartitionsRequest{ Domain: "domain", }, expectError: true, expectedError: "TaskList is not set on request.", }, { name: "task list name not set", req: &types.ListTaskListPartitionsRequest{ Domain: "domain", TaskList: &types.TaskList{}, }, expectError: true, expectedError: "TaskList is not set on request.", }, { name: "task list name too long", req: &types.ListTaskListPartitionsRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "taskListName", }, }, expectError: true, expectedError: "TaskList length exceeds limit.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskListNameMaxLength, 5)) err := v.ValidateListTaskListPartitionsRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateValidateGetTaskListsByDomainRequest(t *testing.T) { testCases := []struct { name string req *types.GetTaskListsByDomainRequest expectError bool expectedError string }{ { name: "success", req: &types.GetTaskListsByDomainRequest{ Domain: "domain", }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.GetTaskListsByDomainRequest{ Domain: "", }, expectError: true, expectedError: "Domain not set on request.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskListNameMaxLength, 5)) err := v.ValidateGetTaskListsByDomainRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateValidateResetStickyTaskListRequest(t *testing.T) { testCases := []struct { name string req *types.ResetStickyTaskListRequest expectError bool expectedError string }{ { name: "success", req: &types.ResetStickyTaskListRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wid", }, }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.ResetStickyTaskListRequest{ Domain: "", }, expectError: true, expectedError: "Domain not set on request.", }, { name: "execution not set", req: &types.ResetStickyTaskListRequest{ Domain: "domain", Execution: nil, }, expectError: true, expectedError: "Execution is not set on request.", }, { name: "workflowID not set", req: &types.ResetStickyTaskListRequest{ Domain: "domain", Execution: &types.WorkflowExecution{}, }, expectError: true, expectedError: "WorkflowId is not set on request.", }, { name: "Invalid RunID", req: &types.ResetStickyTaskListRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "a", }, }, expectError: true, expectedError: "Invalid RunId.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskListNameMaxLength, 5)) err := v.ValidateResetStickyTaskListRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateCountWorkflowExecutionsRequest(t *testing.T) { testCases := []struct { name string req *types.CountWorkflowExecutionsRequest expectError bool expectedError string }{ { name: "success", req: &types.CountWorkflowExecutionsRequest{ Domain: "domain", }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.CountWorkflowExecutionsRequest{ Domain: "", }, expectError: true, expectedError: "Domain not set on request.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, _ := setupMocksForRequestValidator(t) err := v.ValidateCountWorkflowExecutionsRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateListWorkflowExecutionsRequest(t *testing.T) { testCases := []struct { name string req *types.ListWorkflowExecutionsRequest expectError bool expectedError string }{ { name: "success", req: &types.ListWorkflowExecutionsRequest{ Domain: "domain", }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.ListWorkflowExecutionsRequest{ Domain: "", }, expectError: true, expectedError: "Domain not set on request.", }, { name: "page size too large", req: &types.ListWorkflowExecutionsRequest{ Domain: "domain", PageSize: 101, }, expectError: true, expectedError: "Pagesize is larger than allow 100", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.FrontendESIndexMaxResultWindow, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.FrontendVisibilityMaxPageSize, 10)) err := v.ValidateListWorkflowExecutionsRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, int32(10), tc.req.GetPageSize()) } }) } } func TestValidateListOpenWorkflowExecutionsRequest(t *testing.T) { testCases := []struct { name string req *types.ListOpenWorkflowExecutionsRequest expectError bool expectedError string }{ { name: "success", req: &types.ListOpenWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(1)), LatestTime: common.Ptr(int64(2)), }, MaximumPageSize: 0, }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.ListOpenWorkflowExecutionsRequest{ Domain: "", }, expectError: true, expectedError: "Domain not set on request.", }, { name: "startTimeFilter not set", req: &types.ListOpenWorkflowExecutionsRequest{ Domain: "domain", }, expectError: true, expectedError: "StartTimeFilter is required", }, { name: "Earliest time not set", req: &types.ListOpenWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{}, }, expectError: true, expectedError: "EarliestTime in StartTimeFilter is required", }, { name: "Latest time not set", req: &types.ListOpenWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(1)), }, }, expectError: true, expectedError: "LatestTime in StartTimeFilter is required", }, { name: "earliest time later than latest time", req: &types.ListOpenWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(3)), LatestTime: common.Ptr(int64(2)), }, }, expectError: true, expectedError: "EarliestTime in StartTimeFilter should not be larger than LatestTime", }, { name: "both execution and type filter are specified", req: &types.ListOpenWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(1)), LatestTime: common.Ptr(int64(2)), }, ExecutionFilter: &types.WorkflowExecutionFilter{}, TypeFilter: &types.WorkflowTypeFilter{}, }, expectError: true, expectedError: "Only one of ExecutionFilter or TypeFilter is allowed", }, { name: "page size too large", req: &types.ListOpenWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(1)), LatestTime: common.Ptr(int64(2)), }, MaximumPageSize: 101, }, expectError: true, expectedError: "Pagesize is larger than allow 100", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.FrontendESIndexMaxResultWindow, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.FrontendVisibilityMaxPageSize, 10)) err := v.ValidateListOpenWorkflowExecutionsRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, int32(10), tc.req.GetMaximumPageSize()) } }) } } func TestValidateListClosedWorkflowExecutionsRequest(t *testing.T) { testCases := []struct { name string req *types.ListClosedWorkflowExecutionsRequest expectError bool expectedError string }{ { name: "success", req: &types.ListClosedWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(1)), LatestTime: common.Ptr(int64(2)), }, MaximumPageSize: 0, }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.ListClosedWorkflowExecutionsRequest{ Domain: "", }, expectError: true, expectedError: "Domain not set on request.", }, { name: "startTimeFilter not set", req: &types.ListClosedWorkflowExecutionsRequest{ Domain: "domain", }, expectError: true, expectedError: "StartTimeFilter is required", }, { name: "Earliest time not set", req: &types.ListClosedWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{}, }, expectError: true, expectedError: "EarliestTime in StartTimeFilter is required", }, { name: "Latest time not set", req: &types.ListClosedWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(1)), }, }, expectError: true, expectedError: "LatestTime in StartTimeFilter is required", }, { name: "earliest time later than latest time", req: &types.ListClosedWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(3)), LatestTime: common.Ptr(int64(2)), }, }, expectError: true, expectedError: "EarliestTime in StartTimeFilter should not be larger than LatestTime", }, { name: "both execution and type filter are specified", req: &types.ListClosedWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(1)), LatestTime: common.Ptr(int64(2)), }, ExecutionFilter: &types.WorkflowExecutionFilter{}, TypeFilter: &types.WorkflowTypeFilter{}, }, expectError: true, expectedError: "Only one of ExecutionFilter, TypeFilter or StatusFilter is allowed", }, { name: "both execution and status filter are specified", req: &types.ListClosedWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(1)), LatestTime: common.Ptr(int64(2)), }, ExecutionFilter: &types.WorkflowExecutionFilter{}, StatusFilter: types.WorkflowExecutionCloseStatusFailed.Ptr(), }, expectError: true, expectedError: "Only one of ExecutionFilter, TypeFilter or StatusFilter is allowed", }, { name: "both type and status filter are specified", req: &types.ListClosedWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(1)), LatestTime: common.Ptr(int64(2)), }, TypeFilter: &types.WorkflowTypeFilter{}, StatusFilter: types.WorkflowExecutionCloseStatusFailed.Ptr(), }, expectError: true, expectedError: "Only one of ExecutionFilter, TypeFilter or StatusFilter is allowed", }, { name: "page size too large", req: &types.ListClosedWorkflowExecutionsRequest{ Domain: "domain", StartTimeFilter: &types.StartTimeFilter{ EarliestTime: common.Ptr(int64(1)), LatestTime: common.Ptr(int64(2)), }, MaximumPageSize: 101, }, expectError: true, expectedError: "Pagesize is larger than allow 100", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.FrontendESIndexMaxResultWindow, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.FrontendVisibilityMaxPageSize, 10)) err := v.ValidateListClosedWorkflowExecutionsRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, int32(10), tc.req.GetMaximumPageSize()) } }) } } func TestValidateListArchivedWorkflowExecutionsRequest(t *testing.T) { testCases := []struct { name string req *types.ListArchivedWorkflowExecutionsRequest expectError bool expectedError string }{ { name: "success", req: &types.ListArchivedWorkflowExecutionsRequest{ Domain: "domain", }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.ListArchivedWorkflowExecutionsRequest{ Domain: "", }, expectError: true, expectedError: "Domain not set on request.", }, { name: "page size too large", req: &types.ListArchivedWorkflowExecutionsRequest{ Domain: "domain", PageSize: 101, }, expectError: true, expectedError: "Pagesize is larger than allowed 100", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.VisibilityArchivalQueryMaxPageSize, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.FrontendVisibilityMaxPageSize, 10)) err := v.ValidateListArchivedWorkflowExecutionsRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, int32(10), tc.req.GetPageSize()) } }) } } func TestValidateDeprecateDomainRequest(t *testing.T) { testCases := []struct { name string req *types.DeprecateDomainRequest expectError bool expectedError string }{ { name: "success", req: &types.DeprecateDomainRequest{ Name: "domain", SecurityToken: "token", }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.DeprecateDomainRequest{ Name: "", }, expectError: true, expectedError: "Domain not set on request.", }, { name: "wrong token", req: &types.DeprecateDomainRequest{ Name: "domain", SecurityToken: "to", }, expectError: true, expectedError: "No permission to do this operation.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.EnableAdminProtection, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.AdminOperationToken, "token")) err := v.ValidateDeprecateDomainRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateDescribeDomainRequest(t *testing.T) { testCases := []struct { name string req *types.DescribeDomainRequest expectError bool expectedError string }{ { name: "success - with domain name", req: &types.DescribeDomainRequest{ Name: common.Ptr("domain"), }, expectError: false, }, { name: "success - with domain id", req: &types.DescribeDomainRequest{ UUID: common.Ptr("id"), }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.DescribeDomainRequest{}, expectError: true, expectedError: "Domain not set on request.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, _ := setupMocksForRequestValidator(t) err := v.ValidateDescribeDomainRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateUpdateDomainRequest(t *testing.T) { testCases := []struct { name string req *types.UpdateDomainRequest expectError bool expectedError string }{ { name: "success - non failover", req: &types.UpdateDomainRequest{ Name: "domain", SecurityToken: "token", }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.UpdateDomainRequest{}, expectError: true, expectedError: "Domain not set on request.", }, { name: "invalid retention", req: &types.UpdateDomainRequest{ Name: "domain", WorkflowExecutionRetentionPeriodInDays: common.Ptr(int32(100)), }, expectError: true, expectedError: "RetentionDays is invalid.", }, { name: "wrong token", req: &types.UpdateDomainRequest{ Name: "domain", SecurityToken: "to", }, expectError: true, expectedError: "No permission to do this operation.", }, { name: "lockdown rejects active-passive failover", req: &types.UpdateDomainRequest{ Name: "domain", ActiveClusterName: common.Ptr("a"), }, expectError: true, expectedError: "Domain is not accepting fail overs at this time due to lockdown.", }, { name: "lockdown rejects active-active failover", req: &types.UpdateDomainRequest{ Name: "domain", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region0": { ActiveClusterName: "cluster0", FailoverVersion: 1, }, }, }, }, }, }, expectError: true, expectedError: "Domain is not accepting fail overs at this time due to lockdown.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.EnableAdminProtection, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.AdminOperationToken, "token")) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MaxRetentionDays, 3)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.Lockdown, true)) err := v.ValidateUpdateDomainRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateRegisterDomainRequest(t *testing.T) { testCases := []struct { name string req *types.RegisterDomainRequest expectError bool expectedError string }{ { name: "success", req: &types.RegisterDomainRequest{ Name: "domain", SecurityToken: "token", Data: map[string]string{"tier": "3"}, }, expectError: false, }, { name: "not set", req: nil, expectError: true, expectedError: "Request is nil.", }, { name: "domain not set", req: &types.RegisterDomainRequest{}, expectError: true, expectedError: "Domain not set on request.", }, { name: "name too long", req: &types.RegisterDomainRequest{ Name: "domain-name-toooooooooooooo-long", }, expectError: true, expectedError: "Domain length exceeds limit.", }, { name: "invalid retention", req: &types.RegisterDomainRequest{ Name: "domain", WorkflowExecutionRetentionPeriodInDays: 100, }, expectError: true, expectedError: "RetentionDays is invalid.", }, { name: "missing data key", req: &types.RegisterDomainRequest{ Name: "domain", SecurityToken: "to", }, expectError: true, expectedError: "domain data error, missing required key tier . All required keys: map[tier:true]", }, { name: "wrong token", req: &types.RegisterDomainRequest{ Name: "domain", SecurityToken: "to", Data: map[string]string{"tier": "3"}, }, expectError: true, expectedError: "No permission to do this operation.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { v, deps := setupMocksForRequestValidator(t) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.EnableAdminProtection, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.AdminOperationToken, "token")) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MaxRetentionDays, 3)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.DomainNameMaxLength, 10)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.RequiredDomainDataKeys, map[string]interface{}{"tier": true})) err := v.ValidateRegisterDomainRequest(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: service/frontend/api/shutting_down_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package api import ( "context" "reflect" "testing" "github.com/stretchr/testify/assert" ) func TestShuttingDownError(t *testing.T) { wh, _ := setupMocksForWorkflowHandler(t) wh.Stop() // get all methods of Handler interface tt := reflect.TypeOf(struct{ Handler }{}) methodNames := make(map[string]struct{}) for i := 0; i < tt.NumMethod(); i++ { methodNames[tt.Method(i).Name] = struct{}{} } delete(methodNames, "GetClusterInfo") delete(methodNames, "Health") v := reflect.ValueOf(wh) for name := range methodNames { method := v.MethodByName(name) methodType := method.Type() if methodType.Kind() != reflect.Func { t.Fatalf("method: %s is not a function - %s", name, methodType.String()) } if methodType.IsVariadic() { t.Fatalf("method: %s is variadic - %s", name, methodType.String()) } if methodType.NumIn() < 1 || methodType.NumIn() > 2 { t.Fatalf("method: %s has wrong number of inputs - %s", name, methodType.String()) } var results []reflect.Value if methodType.NumIn() == 1 { results = method.Call([]reflect.Value{reflect.ValueOf(context.Background())}) } else { results = method.Call([]reflect.Value{reflect.ValueOf(context.Background()), reflect.Zero(methodType.In(1))}) } if len(results) == 1 { err, ok := results[0].Interface().(error) if !ok { t.Fatalf("method: %s has wrong output type - %s", name, methodType.String()) } assert.ErrorContains(t, err, "Shutting down") } else if len(results) == 2 { err, ok := results[1].Interface().(error) if !ok { t.Fatalf("method: %s has wrong output type - %s", name, methodType.String()) } assert.ErrorContains(t, err, "Shutting down") } else { t.Fatalf("method: %s has wrong number of outputs - %s", name, methodType.String()) } } } ================================================ FILE: service/frontend/api/task_list_handlers.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package api import ( "context" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/validate" ) // DescribeTaskList returns information about the target tasklist, right now this API returns the // pollers which polled this tasklist in last few minutes. If includeTaskListStatus field is true, // it will also return status of tasklist's ackManager (readLevel, ackLevel, backlogCountHint and taskIDBlock). func (wh *WorkflowHandler) DescribeTaskList( ctx context.Context, request *types.DescribeTaskListRequest, ) (resp *types.DescribeTaskListResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateDescribeTaskListRequest(ctx, request); err != nil { return nil, err } domainID, err := wh.GetDomainCache().GetDomainID(request.GetDomain()) if err != nil { return nil, err } response, err := wh.GetMatchingClient().DescribeTaskList(ctx, &types.MatchingDescribeTaskListRequest{ DomainUUID: domainID, DescRequest: request, }) if err != nil { return nil, err } return response, nil } // ListTaskListPartitions returns all the partition and host for a taskList func (wh *WorkflowHandler) ListTaskListPartitions( ctx context.Context, request *types.ListTaskListPartitionsRequest, ) (resp *types.ListTaskListPartitionsResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateListTaskListPartitionsRequest(ctx, request); err != nil { return nil, err } resp, err := wh.GetMatchingClient().ListTaskListPartitions(ctx, &types.MatchingListTaskListPartitionsRequest{ Domain: request.Domain, TaskList: request.TaskList, }) return resp, err } // GetTaskListsByDomain returns all the partition and host for a taskList func (wh *WorkflowHandler) GetTaskListsByDomain( ctx context.Context, request *types.GetTaskListsByDomainRequest, ) (resp *types.GetTaskListsByDomainResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateGetTaskListsByDomainRequest(ctx, request); err != nil { return nil, err } resp, err := wh.GetMatchingClient().GetTaskListsByDomain(ctx, &types.GetTaskListsByDomainRequest{ Domain: request.Domain, }) return resp, err } // ResetStickyTaskList reset the volatile information in mutable state of a given workflow. func (wh *WorkflowHandler) ResetStickyTaskList( ctx context.Context, resetRequest *types.ResetStickyTaskListRequest, ) (resp *types.ResetStickyTaskListResponse, retError error) { if wh.isShuttingDown() { return nil, validate.ErrShuttingDown } if err := wh.requestValidator.ValidateResetStickyTaskListRequest(ctx, resetRequest); err != nil { return nil, err } domainID, err := wh.GetDomainCache().GetDomainID(resetRequest.GetDomain()) if err != nil { return nil, err } _, err = wh.GetHistoryClient().ResetStickyTaskList(ctx, &types.HistoryResetStickyTaskListRequest{ DomainUUID: domainID, Execution: resetRequest.Execution, }) if err != nil { return nil, wh.normalizeVersionedErrors(ctx, err) } return &types.ResetStickyTaskListResponse{}, nil } ================================================ FILE: service/frontend/api/task_list_handlers_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package api import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/types" ) func TestDescribeTaskList(t *testing.T) { testCases := []struct { name string req *types.DescribeTaskListRequest setupMocks func(*mockDeps) expectError bool expectedError string expected *types.DescribeTaskListResponse }{ { name: "success", req: &types.DescribeTaskListRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, TaskListType: types.TaskListTypeActivity.Ptr(), }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDescribeTaskListRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainCache.EXPECT().GetDomainID("domain").Return("domain-id", nil) deps.mockMatchingClient.EXPECT().DescribeTaskList(gomock.Any(), &types.MatchingDescribeTaskListRequest{ DomainUUID: "domain-id", DescRequest: &types.DescribeTaskListRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, TaskListType: types.TaskListTypeActivity.Ptr(), }, }).Return(&types.DescribeTaskListResponse{}, nil) }, expectError: false, expected: &types.DescribeTaskListResponse{}, }, { name: "matching client error", req: &types.DescribeTaskListRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, TaskListType: types.TaskListTypeActivity.Ptr(), }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDescribeTaskListRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainCache.EXPECT().GetDomainID("domain").Return("domain-id", nil) deps.mockMatchingClient.EXPECT().DescribeTaskList(gomock.Any(), &types.MatchingDescribeTaskListRequest{ DomainUUID: "domain-id", DescRequest: &types.DescribeTaskListRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, TaskListType: types.TaskListTypeActivity.Ptr(), }, }).Return(nil, errors.New("matching client error")) }, expectError: true, expectedError: "matching client error", }, { name: "domain cache error", req: &types.DescribeTaskListRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, TaskListType: types.TaskListTypeActivity.Ptr(), }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDescribeTaskListRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainCache.EXPECT().GetDomainID("domain").Return("", errors.New("domain cache error")) }, expectError: true, expectedError: "domain cache error", }, { name: "validator error", req: &types.DescribeTaskListRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, TaskListType: types.TaskListTypeActivity.Ptr(), }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateDescribeTaskListRequest(gomock.Any(), gomock.Any()).Return(errors.New("validator error")) }, expectError: true, expectedError: "validator error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { wh, deps := setupMocksForWorkflowHandler(t) tc.setupMocks(deps) resp, err := wh.DescribeTaskList(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } func TestListTaskListPartitions(t *testing.T) { testCases := []struct { name string req *types.ListTaskListPartitionsRequest setupMocks func(*mockDeps) expectError bool expectedError string expected *types.ListTaskListPartitionsResponse }{ { name: "success", req: &types.ListTaskListPartitionsRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateListTaskListPartitionsRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockMatchingClient.EXPECT().ListTaskListPartitions(gomock.Any(), &types.MatchingListTaskListPartitionsRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, }).Return(&types.ListTaskListPartitionsResponse{}, nil) }, expectError: false, expected: &types.ListTaskListPartitionsResponse{}, }, { name: "matching client error", req: &types.ListTaskListPartitionsRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateListTaskListPartitionsRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockMatchingClient.EXPECT().ListTaskListPartitions(gomock.Any(), &types.MatchingListTaskListPartitionsRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, }).Return(nil, errors.New("matching client error")) }, expectError: true, expectedError: "matching client error", }, { name: "validator error", req: &types.ListTaskListPartitionsRequest{ Domain: "domain", TaskList: &types.TaskList{ Name: "tl", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateListTaskListPartitionsRequest(gomock.Any(), gomock.Any()).Return(errors.New("validator error")) }, expectError: true, expectedError: "validator error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { wh, deps := setupMocksForWorkflowHandler(t) tc.setupMocks(deps) resp, err := wh.ListTaskListPartitions(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } func TestGetTaskListsByDomain(t *testing.T) { testCases := []struct { name string req *types.GetTaskListsByDomainRequest setupMocks func(*mockDeps) expectError bool expectedError string expected *types.GetTaskListsByDomainResponse }{ { name: "success", req: &types.GetTaskListsByDomainRequest{ Domain: "domain", }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateGetTaskListsByDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockMatchingClient.EXPECT().GetTaskListsByDomain(gomock.Any(), &types.GetTaskListsByDomainRequest{ Domain: "domain", }).Return(&types.GetTaskListsByDomainResponse{}, nil) }, expectError: false, expected: &types.GetTaskListsByDomainResponse{}, }, { name: "matching client error", req: &types.GetTaskListsByDomainRequest{ Domain: "domain", }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateGetTaskListsByDomainRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockMatchingClient.EXPECT().GetTaskListsByDomain(gomock.Any(), &types.GetTaskListsByDomainRequest{ Domain: "domain", }).Return(nil, errors.New("matching client error")) }, expectError: true, expectedError: "matching client error", }, { name: "validator error", req: &types.GetTaskListsByDomainRequest{ Domain: "domain", }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateGetTaskListsByDomainRequest(gomock.Any(), gomock.Any()).Return(errors.New("validator error")) }, expectError: true, expectedError: "validator error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { wh, deps := setupMocksForWorkflowHandler(t) tc.setupMocks(deps) resp, err := wh.GetTaskListsByDomain(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } func TestResetStickyTaskList(t *testing.T) { testCases := []struct { name string req *types.ResetStickyTaskListRequest setupMocks func(*mockDeps) expectError bool expectedError string expected *types.ResetStickyTaskListResponse }{ { name: "success", req: &types.ResetStickyTaskListRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateResetStickyTaskListRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainCache.EXPECT().GetDomainID("domain").Return("domain-id", nil) deps.mockHistoryClient.EXPECT().ResetStickyTaskList(gomock.Any(), &types.HistoryResetStickyTaskListRequest{ DomainUUID: "domain-id", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }).Return(&types.HistoryResetStickyTaskListResponse{}, nil) }, expectError: false, expected: &types.ResetStickyTaskListResponse{}, }, { name: "history client error", req: &types.ResetStickyTaskListRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateResetStickyTaskListRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainCache.EXPECT().GetDomainID("domain").Return("domain-id", nil) deps.mockHistoryClient.EXPECT().ResetStickyTaskList(gomock.Any(), &types.HistoryResetStickyTaskListRequest{ DomainUUID: "domain-id", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }).Return(nil, errors.New("history client error")) }, expectError: true, expectedError: "history client error", }, { name: "domain cache error", req: &types.ResetStickyTaskListRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateResetStickyTaskListRequest(gomock.Any(), gomock.Any()).Return(nil) deps.mockDomainCache.EXPECT().GetDomainID("domain").Return("", errors.New("domain cache error")) }, expectError: true, expectedError: "domain cache error", }, { name: "validator error", req: &types.ResetStickyTaskListRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: "wf", }, }, setupMocks: func(deps *mockDeps) { deps.mockRequestValidator.EXPECT().ValidateResetStickyTaskListRequest(gomock.Any(), gomock.Any()).Return(errors.New("validator error")) }, expectError: true, expectedError: "validator error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { wh, deps := setupMocksForWorkflowHandler(t) tc.setupMocks(deps) resp, err := wh.ResetStickyTaskList(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, resp) } }) } } ================================================ FILE: service/frontend/config/config.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" ) // Config represents configuration for cadence-frontend service type Config struct { NumHistoryShards int IsAdvancedVisConfigExist bool DomainConfig domain.Config PersistenceMaxQPS dynamicproperties.IntPropertyFn PersistenceGlobalMaxQPS dynamicproperties.IntPropertyFn VisibilityMaxPageSize dynamicproperties.IntPropertyFnWithDomainFilter EnableVisibilitySampling dynamicproperties.BoolPropertyFn EnableReadFromClosedExecutionV2 dynamicproperties.BoolPropertyFn // deprecated: never used for ratelimiting, only sampling-based failure injection, and only on database-based visibility VisibilityListMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter EnableLogCustomerQueryParameter dynamicproperties.BoolPropertyFnWithDomainFilter ReadVisibilityStoreName dynamicproperties.StringPropertyFnWithDomainFilter // deprecated: never read from ESVisibilityListMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter ESIndexMaxResultWindow dynamicproperties.IntPropertyFn HistoryMaxPageSize dynamicproperties.IntPropertyFnWithDomainFilter UserRPS dynamicproperties.IntPropertyFn WorkerRPS dynamicproperties.IntPropertyFn VisibilityRPS dynamicproperties.IntPropertyFn AsyncRPS dynamicproperties.IntPropertyFn MaxDomainUserRPSPerInstance dynamicproperties.IntPropertyFnWithDomainFilter MaxDomainWorkerRPSPerInstance dynamicproperties.IntPropertyFnWithDomainFilter MaxDomainVisibilityRPSPerInstance dynamicproperties.IntPropertyFnWithDomainFilter MaxDomainAsyncRPSPerInstance dynamicproperties.IntPropertyFnWithDomainFilter GlobalDomainUserRPS dynamicproperties.IntPropertyFnWithDomainFilter GlobalDomainWorkerRPS dynamicproperties.IntPropertyFnWithDomainFilter GlobalDomainVisibilityRPS dynamicproperties.IntPropertyFnWithDomainFilter GlobalDomainAsyncRPS dynamicproperties.IntPropertyFnWithDomainFilter MaxWorkerPollDelay dynamicproperties.DurationPropertyFnWithDomainFilter RateLimiterBypassCallerTypes dynamicproperties.ListPropertyFn EnableClientVersionCheck dynamicproperties.BoolPropertyFn EnableQueryAttributeValidation dynamicproperties.BoolPropertyFn DisallowQuery dynamicproperties.BoolPropertyFnWithDomainFilter ShutdownDrainDuration dynamicproperties.DurationPropertyFn WarmupDuration dynamicproperties.DurationPropertyFn Lockdown dynamicproperties.BoolPropertyFnWithDomainFilter // global ratelimiter config, uses GlobalDomain*RPS for RPS configuration GlobalRatelimiterKeyMode dynamicproperties.StringPropertyWithRatelimitKeyFilter GlobalRatelimiterUpdateInterval dynamicproperties.DurationPropertyFn // isolation configuration EnableTasklistIsolation dynamicproperties.BoolPropertyFnWithDomainFilter EnableDomainAuditLogging dynamicproperties.BoolPropertyFn // id length limits MaxIDLengthWarnLimit dynamicproperties.IntPropertyFn DomainNameMaxLength dynamicproperties.IntPropertyFnWithDomainFilter IdentityMaxLength dynamicproperties.IntPropertyFnWithDomainFilter WorkflowIDMaxLength dynamicproperties.IntPropertyFnWithDomainFilter SignalNameMaxLength dynamicproperties.IntPropertyFnWithDomainFilter WorkflowTypeMaxLength dynamicproperties.IntPropertyFnWithDomainFilter RequestIDMaxLength dynamicproperties.IntPropertyFnWithDomainFilter TaskListNameMaxLength dynamicproperties.IntPropertyFnWithDomainFilter // security protection settings EnableAdminProtection dynamicproperties.BoolPropertyFn AdminOperationToken dynamicproperties.StringPropertyFn DisableListVisibilityByFilter dynamicproperties.BoolPropertyFnWithDomainFilter // size limit system protection BlobSizeLimitError dynamicproperties.IntPropertyFnWithDomainFilter BlobSizeLimitWarn dynamicproperties.IntPropertyFnWithDomainFilter ThrottledLogRPS dynamicproperties.IntPropertyFn // Domain specific config EnableDomainNotActiveAutoForwarding dynamicproperties.BoolPropertyFnWithDomainFilter EnableGracefulFailover dynamicproperties.BoolPropertyFn DomainFailoverRefreshInterval dynamicproperties.DurationPropertyFn DomainFailoverRefreshTimerJitterCoefficient dynamicproperties.FloatPropertyFn EnableActiveClusterSelectionPolicyInStartWorkflow dynamicproperties.BoolPropertyFnWithDomainFilter // ValidSearchAttributes is legal indexed keys that can be used in list APIs ValidSearchAttributes dynamicproperties.MapPropertyFn SearchAttributesNumberOfKeysLimit dynamicproperties.IntPropertyFnWithDomainFilter SearchAttributesSizeOfValueLimit dynamicproperties.IntPropertyFnWithDomainFilter SearchAttributesTotalSizeLimit dynamicproperties.IntPropertyFnWithDomainFilter PinotOptimizedQueryColumns dynamicproperties.MapPropertyFn // VisibilityArchival system protection VisibilityArchivalQueryMaxPageSize dynamicproperties.IntPropertyFn SendRawWorkflowHistory dynamicproperties.BoolPropertyFnWithDomainFilter // max number of decisions per RespondDecisionTaskCompleted request (unlimited by default) DecisionResultCountLimit dynamicproperties.IntPropertyFnWithDomainFilter // Debugging // Emit signal related metrics with signal name tag. Be aware of cardinality. EmitSignalNameMetricsTag dynamicproperties.BoolPropertyFnWithDomainFilter // HostName for machine running the service HostName string } // NewConfig returns new service config with default values func NewConfig(dc *dynamicconfig.Collection, numHistoryShards int, isAdvancedVisConfigExist bool, hostName string, logger log.Logger) *Config { logger.Debugf("Creating new frontend config for host %s, numHistoryShards: %d, isAdvancedVisConfigExist: %t", hostName, numHistoryShards, isAdvancedVisConfigExist) return &Config{ NumHistoryShards: numHistoryShards, IsAdvancedVisConfigExist: isAdvancedVisConfigExist, PersistenceMaxQPS: dc.GetIntProperty(dynamicproperties.FrontendPersistenceMaxQPS), PersistenceGlobalMaxQPS: dc.GetIntProperty(dynamicproperties.FrontendPersistenceGlobalMaxQPS), VisibilityMaxPageSize: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendVisibilityMaxPageSize), EnableVisibilitySampling: dc.GetBoolProperty(dynamicproperties.EnableVisibilitySampling), EnableReadFromClosedExecutionV2: dc.GetBoolProperty(dynamicproperties.EnableReadFromClosedExecutionV2), VisibilityListMaxQPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendVisibilityListMaxQPS), ESVisibilityListMaxQPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendESVisibilityListMaxQPS), ReadVisibilityStoreName: dc.GetStringPropertyFilteredByDomain(dynamicproperties.ReadVisibilityStoreName), EnableLogCustomerQueryParameter: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableLogCustomerQueryParameter), ESIndexMaxResultWindow: dc.GetIntProperty(dynamicproperties.FrontendESIndexMaxResultWindow), HistoryMaxPageSize: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendHistoryMaxPageSize), UserRPS: dc.GetIntProperty(dynamicproperties.FrontendUserRPS), WorkerRPS: dc.GetIntProperty(dynamicproperties.FrontendWorkerRPS), VisibilityRPS: dc.GetIntProperty(dynamicproperties.FrontendVisibilityRPS), AsyncRPS: dc.GetIntProperty(dynamicproperties.FrontendAsyncRPS), MaxDomainUserRPSPerInstance: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendMaxDomainUserRPSPerInstance), MaxDomainWorkerRPSPerInstance: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendMaxDomainWorkerRPSPerInstance), MaxDomainVisibilityRPSPerInstance: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendMaxDomainVisibilityRPSPerInstance), MaxDomainAsyncRPSPerInstance: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendMaxDomainAsyncRPSPerInstance), GlobalDomainUserRPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendGlobalDomainUserRPS), GlobalDomainWorkerRPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendGlobalDomainWorkerRPS), GlobalDomainVisibilityRPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendGlobalDomainVisibilityRPS), GlobalDomainAsyncRPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendGlobalDomainAsyncRPS), MaxWorkerPollDelay: dc.GetDurationPropertyFilteredByDomain(dynamicproperties.FrontendMaxWorkerPollDelay), RateLimiterBypassCallerTypes: dc.GetListProperty(dynamicproperties.RateLimiterBypassCallerTypes), GlobalRatelimiterKeyMode: dc.GetStringPropertyFilteredByRatelimitKey(dynamicproperties.FrontendGlobalRatelimiterMode), GlobalRatelimiterUpdateInterval: dc.GetDurationProperty(dynamicproperties.GlobalRatelimiterUpdateInterval), MaxIDLengthWarnLimit: dc.GetIntProperty(dynamicproperties.MaxIDLengthWarnLimit), DomainNameMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.DomainNameMaxLength), IdentityMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.IdentityMaxLength), WorkflowIDMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.WorkflowIDMaxLength), SignalNameMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.SignalNameMaxLength), WorkflowTypeMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.WorkflowTypeMaxLength), RequestIDMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.RequestIDMaxLength), TaskListNameMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.TaskListNameMaxLength), EnableAdminProtection: dc.GetBoolProperty(dynamicproperties.EnableAdminProtection), AdminOperationToken: dc.GetStringProperty(dynamicproperties.AdminOperationToken), DisableListVisibilityByFilter: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.DisableListVisibilityByFilter), BlobSizeLimitError: dc.GetIntPropertyFilteredByDomain(dynamicproperties.BlobSizeLimitError), BlobSizeLimitWarn: dc.GetIntPropertyFilteredByDomain(dynamicproperties.BlobSizeLimitWarn), ThrottledLogRPS: dc.GetIntProperty(dynamicproperties.FrontendThrottledLogRPS), ShutdownDrainDuration: dc.GetDurationProperty(dynamicproperties.FrontendShutdownDrainDuration), WarmupDuration: dc.GetDurationProperty(dynamicproperties.FrontendWarmupDuration), EnableDomainNotActiveAutoForwarding: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableDomainNotActiveAutoForwarding), EnableGracefulFailover: dc.GetBoolProperty(dynamicproperties.EnableGracefulFailover), DomainFailoverRefreshInterval: dc.GetDurationProperty(dynamicproperties.DomainFailoverRefreshInterval), DomainFailoverRefreshTimerJitterCoefficient: dc.GetFloat64Property(dynamicproperties.DomainFailoverRefreshTimerJitterCoefficient), EnableActiveClusterSelectionPolicyInStartWorkflow: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableActiveClusterSelectionPolicyInStartWorkflow), EnableClientVersionCheck: dc.GetBoolProperty(dynamicproperties.EnableClientVersionCheck), EnableQueryAttributeValidation: dc.GetBoolProperty(dynamicproperties.EnableQueryAttributeValidation), ValidSearchAttributes: dc.GetMapProperty(dynamicproperties.ValidSearchAttributes), SearchAttributesNumberOfKeysLimit: dc.GetIntPropertyFilteredByDomain(dynamicproperties.SearchAttributesNumberOfKeysLimit), SearchAttributesSizeOfValueLimit: dc.GetIntPropertyFilteredByDomain(dynamicproperties.SearchAttributesSizeOfValueLimit), SearchAttributesTotalSizeLimit: dc.GetIntPropertyFilteredByDomain(dynamicproperties.SearchAttributesTotalSizeLimit), PinotOptimizedQueryColumns: dc.GetMapProperty(dynamicproperties.PinotOptimizedQueryColumns), VisibilityArchivalQueryMaxPageSize: dc.GetIntProperty(dynamicproperties.VisibilityArchivalQueryMaxPageSize), DisallowQuery: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.DisallowQuery), SendRawWorkflowHistory: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.SendRawWorkflowHistory), DecisionResultCountLimit: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendDecisionResultCountLimit), EmitSignalNameMetricsTag: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.FrontendEmitSignalNameMetricsTag), Lockdown: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.Lockdown), EnableTasklistIsolation: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableTasklistIsolation), EnableDomainAuditLogging: dc.GetBoolProperty(dynamicproperties.EnableDomainAuditLogging), DomainConfig: domain.Config{ MaxBadBinaryCount: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendMaxBadBinaries), MinRetentionDays: dc.GetIntProperty(dynamicproperties.MinRetentionDays), MaxRetentionDays: dc.GetIntProperty(dynamicproperties.MaxRetentionDays), FailoverCoolDown: dc.GetDurationPropertyFilteredByDomain(dynamicproperties.FrontendFailoverCoolDown), RequiredDomainDataKeys: dc.GetMapProperty(dynamicproperties.RequiredDomainDataKeys), FailoverHistoryMaxSize: dc.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendFailoverHistoryMaxSize), EnableDomainAuditLogging: dc.GetBoolProperty(dynamicproperties.EnableDomainAuditLogging), }, HostName: hostName, } } ================================================ FILE: service/frontend/config/config_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package config import ( "reflect" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" ) type configTestCase struct { key dynamicproperties.Key value interface{} } var ignoreField = configTestCase{key: dynamicproperties.UnknownIntKey} func TestNewConfig(t *testing.T) { fields := map[string]configTestCase{ "NumHistoryShards": {nil, 1001}, "IsAdvancedVisConfigExist": {nil, true}, "HostName": {nil, "hostname"}, "DomainConfig": ignoreField, // Handle this separately since it's also a config object "PersistenceMaxQPS": {dynamicproperties.FrontendPersistenceMaxQPS, 1}, "PersistenceGlobalMaxQPS": {dynamicproperties.FrontendPersistenceGlobalMaxQPS, 2}, "VisibilityMaxPageSize": {dynamicproperties.FrontendVisibilityMaxPageSize, 3}, "EnableVisibilitySampling": {dynamicproperties.EnableVisibilitySampling, true}, "EnableReadFromClosedExecutionV2": {dynamicproperties.EnableReadFromClosedExecutionV2, false}, "VisibilityListMaxQPS": {dynamicproperties.FrontendVisibilityListMaxQPS, 4}, "ESVisibilityListMaxQPS": {dynamicproperties.FrontendESVisibilityListMaxQPS, 5}, "ReadVisibilityStoreName": {dynamicproperties.ReadVisibilityStoreName, "es"}, "EnableLogCustomerQueryParameter": {dynamicproperties.EnableLogCustomerQueryParameter, true}, "ESIndexMaxResultWindow": {dynamicproperties.FrontendESIndexMaxResultWindow, 6}, "HistoryMaxPageSize": {dynamicproperties.FrontendHistoryMaxPageSize, 7}, "UserRPS": {dynamicproperties.FrontendUserRPS, 8}, "WorkerRPS": {dynamicproperties.FrontendWorkerRPS, 9}, "VisibilityRPS": {dynamicproperties.FrontendVisibilityRPS, 10}, "AsyncRPS": {dynamicproperties.FrontendAsyncRPS, 11}, "MaxDomainUserRPSPerInstance": {dynamicproperties.FrontendMaxDomainUserRPSPerInstance, 12}, "MaxDomainWorkerRPSPerInstance": {dynamicproperties.FrontendMaxDomainWorkerRPSPerInstance, 13}, "MaxDomainVisibilityRPSPerInstance": {dynamicproperties.FrontendMaxDomainVisibilityRPSPerInstance, 14}, "MaxDomainAsyncRPSPerInstance": {dynamicproperties.FrontendMaxDomainAsyncRPSPerInstance, 15}, "GlobalDomainUserRPS": {dynamicproperties.FrontendGlobalDomainUserRPS, 16}, "GlobalDomainWorkerRPS": {dynamicproperties.FrontendGlobalDomainWorkerRPS, 17}, "GlobalDomainVisibilityRPS": {dynamicproperties.FrontendGlobalDomainVisibilityRPS, 18}, "GlobalDomainAsyncRPS": {dynamicproperties.FrontendGlobalDomainAsyncRPS, 19}, "MaxWorkerPollDelay": {dynamicproperties.FrontendMaxWorkerPollDelay, time.Duration(30)}, "MaxIDLengthWarnLimit": {dynamicproperties.MaxIDLengthWarnLimit, 20}, "DomainNameMaxLength": {dynamicproperties.DomainNameMaxLength, 21}, "IdentityMaxLength": {dynamicproperties.IdentityMaxLength, 22}, "WorkflowIDMaxLength": {dynamicproperties.WorkflowIDMaxLength, 23}, "SignalNameMaxLength": {dynamicproperties.SignalNameMaxLength, 24}, "WorkflowTypeMaxLength": {dynamicproperties.WorkflowTypeMaxLength, 25}, "RequestIDMaxLength": {dynamicproperties.RequestIDMaxLength, 26}, "TaskListNameMaxLength": {dynamicproperties.TaskListNameMaxLength, 27}, "EnableAdminProtection": {dynamicproperties.EnableAdminProtection, true}, "AdminOperationToken": {dynamicproperties.AdminOperationToken, "token"}, "DisableListVisibilityByFilter": {dynamicproperties.DisableListVisibilityByFilter, false}, "BlobSizeLimitError": {dynamicproperties.BlobSizeLimitError, 29}, "BlobSizeLimitWarn": {dynamicproperties.BlobSizeLimitWarn, 30}, "ThrottledLogRPS": {dynamicproperties.FrontendThrottledLogRPS, 31}, "ShutdownDrainDuration": {dynamicproperties.FrontendShutdownDrainDuration, time.Duration(32)}, "WarmupDuration": {dynamicproperties.FrontendWarmupDuration, time.Duration(40)}, "EnableDomainNotActiveAutoForwarding": {dynamicproperties.EnableDomainNotActiveAutoForwarding, true}, "EnableGracefulFailover": {dynamicproperties.EnableGracefulFailover, false}, "DomainFailoverRefreshInterval": {dynamicproperties.DomainFailoverRefreshInterval, time.Duration(33)}, "DomainFailoverRefreshTimerJitterCoefficient": {dynamicproperties.DomainFailoverRefreshTimerJitterCoefficient, 34.0}, "EnableActiveClusterSelectionPolicyInStartWorkflow": {dynamicproperties.EnableActiveClusterSelectionPolicyInStartWorkflow, true}, "EnableClientVersionCheck": {dynamicproperties.EnableClientVersionCheck, true}, "EnableQueryAttributeValidation": {dynamicproperties.EnableQueryAttributeValidation, false}, "ValidSearchAttributes": {dynamicproperties.ValidSearchAttributes, map[string]interface{}{"foo": "bar"}}, "SearchAttributesNumberOfKeysLimit": {dynamicproperties.SearchAttributesNumberOfKeysLimit, 35}, "SearchAttributesSizeOfValueLimit": {dynamicproperties.SearchAttributesSizeOfValueLimit, 36}, "SearchAttributesTotalSizeLimit": {dynamicproperties.SearchAttributesTotalSizeLimit, 37}, "VisibilityArchivalQueryMaxPageSize": {dynamicproperties.VisibilityArchivalQueryMaxPageSize, 38}, "DisallowQuery": {dynamicproperties.DisallowQuery, true}, "SendRawWorkflowHistory": {dynamicproperties.SendRawWorkflowHistory, false}, "DecisionResultCountLimit": {dynamicproperties.FrontendDecisionResultCountLimit, 39}, "EmitSignalNameMetricsTag": {dynamicproperties.FrontendEmitSignalNameMetricsTag, true}, "Lockdown": {dynamicproperties.Lockdown, false}, "EnableTasklistIsolation": {dynamicproperties.EnableTasklistIsolation, true}, "GlobalRatelimiterKeyMode": {dynamicproperties.FrontendGlobalRatelimiterMode, "disabled"}, "GlobalRatelimiterUpdateInterval": {dynamicproperties.GlobalRatelimiterUpdateInterval, 3 * time.Second}, "PinotOptimizedQueryColumns": {dynamicproperties.PinotOptimizedQueryColumns, map[string]interface{}{"foo": "bar"}}, "EnableDomainAuditLogging": {dynamicproperties.EnableDomainAuditLogging, true}, "RateLimiterBypassCallerTypes": {dynamicproperties.RateLimiterBypassCallerTypes, []interface{}{"cli", "ui"}}, } domainFields := map[string]configTestCase{ "MaxBadBinaryCount": {dynamicproperties.FrontendMaxBadBinaries, 40}, "MinRetentionDays": {dynamicproperties.MinRetentionDays, 41}, "MaxRetentionDays": {dynamicproperties.MaxRetentionDays, 42}, "FailoverCoolDown": {dynamicproperties.FrontendFailoverCoolDown, time.Duration(43)}, "RequiredDomainDataKeys": {dynamicproperties.RequiredDomainDataKeys, map[string]interface{}{"bar": "baz"}}, "FailoverHistoryMaxSize": {dynamicproperties.FrontendFailoverHistoryMaxSize, 44}, "EnableDomainAuditLogging": {dynamicproperties.EnableDomainAuditLogging, true}, } client := dynamicconfig.NewInMemoryClient() logger := testlogger.New(t) dc := dynamicconfig.NewCollection(client, logger) config := NewConfig(dc, 1001, true, "hostname", logger) assertFieldsMatch(t, *config, client, fields) assertFieldsMatch(t, config.DomainConfig, client, domainFields) } func assertFieldsMatch(t *testing.T, config interface{}, client dynamicconfig.Client, fields map[string]configTestCase) { configType := reflect.ValueOf(config) for i := 0; i < configType.NumField(); i++ { f := configType.Field(i) fieldName := configType.Type().Field(i).Name if expected, ok := fields[fieldName]; ok { if expected.key == ignoreField.key { continue } if expected.key != nil { err := client.UpdateValue(expected.key, expected.value) if err != nil { t.Errorf("Failed to update config for %s: %s", fieldName, err) return } } actual := getValue(&f) assert.Equal(t, expected.value, actual) } else { t.Errorf("Unknown property on Config: %s", fieldName) } } } func getValue(f *reflect.Value) interface{} { switch f.Kind() { case reflect.Func: switch fn := f.Interface().(type) { case dynamicproperties.IntPropertyFn: return fn() case dynamicproperties.IntPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.BoolPropertyFn: return fn() case dynamicproperties.BoolPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.DurationPropertyFn: return fn() case dynamicproperties.DurationPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.FloatPropertyFn: return fn() case dynamicproperties.MapPropertyFn: return fn() case dynamicproperties.StringPropertyFn: return fn() case dynamicproperties.StringPropertyWithRatelimitKeyFilter: return fn("user:domain") case dynamicproperties.StringPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.ListPropertyFn: return fn() default: panic("Unable to handle type: " + f.Type().Name()) } default: return f.Interface() } } ================================================ FILE: service/frontend/service.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package frontend import ( "context" "fmt" "sync/atomic" "time" "go.uber.org/multierr" "github.com/uber/cadence/common" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/quotas/global/collection" "github.com/uber/cadence/common/quotas/permember" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/frontend/admin" "github.com/uber/cadence/service/frontend/api" "github.com/uber/cadence/service/frontend/config" "github.com/uber/cadence/service/frontend/wrappers/accesscontrolled" "github.com/uber/cadence/service/frontend/wrappers/clusterredirection" "github.com/uber/cadence/service/frontend/wrappers/grpc" "github.com/uber/cadence/service/frontend/wrappers/metered" "github.com/uber/cadence/service/frontend/wrappers/ratelimited" "github.com/uber/cadence/service/frontend/wrappers/thrift" "github.com/uber/cadence/service/frontend/wrappers/versioncheck" ) // Service represents the cadence-frontend service type Service struct { resource.Resource status int32 handler *api.WorkflowHandler adminHandler admin.Handler stopC chan struct{} config *config.Config params *resource.Params ratelimiterCollections globalRatelimiterCollections } // NewService builds a new cadence-frontend service func NewService( params *resource.Params, ) (resource.Resource, error) { isAdvancedVisExistInConfig := len(params.PersistenceConfig.AdvancedVisibilityStore) != 0 dc := dynamicconfig.NewCollection( params.DynamicConfig, params.Logger, dynamicproperties.ClusterNameFilter(params.ClusterMetadata.GetCurrentClusterName()), ) serviceConfig := config.NewConfig( dc, params.PersistenceConfig.NumHistoryShards, isAdvancedVisExistInConfig, params.HostName, params.Logger, ) serviceResource, err := resource.New( params, service.Frontend, &service.Config{ PersistenceMaxQPS: serviceConfig.PersistenceMaxQPS, PersistenceGlobalMaxQPS: serviceConfig.PersistenceGlobalMaxQPS, ThrottledLoggerMaxRPS: serviceConfig.ThrottledLogRPS, WriteVisibilityStoreName: nil, // frontend service never write EnableLogCustomerQueryParameter: serviceConfig.EnableLogCustomerQueryParameter, ReadVisibilityStoreName: serviceConfig.ReadVisibilityStoreName, EnableDBVisibilitySampling: serviceConfig.EnableVisibilitySampling, EnableReadDBVisibilityFromClosedExecutionV2: serviceConfig.EnableReadFromClosedExecutionV2, DBVisibilityListMaxQPS: serviceConfig.VisibilityListMaxQPS, WriteDBVisibilityOpenMaxQPS: nil, // frontend service never write WriteDBVisibilityClosedMaxQPS: nil, // frontend service never write ESVisibilityListMaxQPS: serviceConfig.ESVisibilityListMaxQPS, ESIndexMaxResultWindow: serviceConfig.ESIndexMaxResultWindow, ValidSearchAttributes: serviceConfig.ValidSearchAttributes, IsErrorRetryableFunction: common.FrontendRetry, PinotOptimizedQueryColumns: serviceConfig.PinotOptimizedQueryColumns, }, ) if err != nil { return nil, err } return &Service{ Resource: serviceResource, status: common.DaemonStatusInitialized, config: serviceConfig, stopC: make(chan struct{}), params: params, }, nil } // Start starts the service func (s *Service) Start() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } logger := s.GetLogger() logger.Info("frontend starting") // domain handler's shared between admin and workflow handler, so instantiate it centrally and share it dh := domain.NewHandler( s.config.DomainConfig, s.GetLogger(), s.GetDomainManager(), s.GetDomainAuditManager(), s.GetClusterMetadata(), domain.NewDomainReplicator(s.GetDomainReplicationQueue(), s.GetLogger()), s.GetArchivalMetadata(), s.GetArchiverProvider(), s.GetTimeSource(), ) // Base handler s.handler = api.NewWorkflowHandler(s, s.config, client.NewVersionChecker(), dh) collections, err := s.createGlobalQuotaCollections() if err != nil { logger.Fatal("constructing ratelimiter collections", tag.Error(err)) } userRateLimiter := quotas.NewMultiStageRateLimiter(quotas.NewDynamicRateLimiter(s.config.UserRPS.AsFloat64()), collections.user) workerRateLimiter := quotas.NewMultiStageRateLimiter(quotas.NewDynamicRateLimiter(s.config.WorkerRPS.AsFloat64()), collections.worker) visibilityRateLimiter := quotas.NewMultiStageRateLimiter(quotas.NewDynamicRateLimiter(s.config.VisibilityRPS.AsFloat64()), collections.visibility) asyncRateLimiter := quotas.NewMultiStageRateLimiter(quotas.NewDynamicRateLimiter(s.config.AsyncRPS.AsFloat64()), collections.async) // Additional decorations var handler api.Handler = s.handler handler = versioncheck.NewAPIHandler(handler, s.config, client.NewVersionChecker()) callerBypass := quotas.NewCallerBypass(s.config.RateLimiterBypassCallerTypes) handler = ratelimited.NewAPIHandler(handler, s.GetDomainCache(), userRateLimiter, workerRateLimiter, visibilityRateLimiter, asyncRateLimiter, s.config.MaxWorkerPollDelay, callerBypass) handler = metered.NewAPIHandler(handler, s.GetLogger(), s.GetMetricsClient(), s.GetDomainCache(), s.config) if s.params.ClusterRedirectionPolicy != nil { handler = clusterredirection.NewAPIHandler(handler, s, s.config, *s.params.ClusterRedirectionPolicy) } handler = accesscontrolled.NewAPIHandler(handler, s, s.params.Authorizer, s.params.AuthorizationConfig) // Register the latest (most decorated) handler thriftHandler := thrift.NewAPIHandler(handler) thriftHandler.Register(s.GetDispatcher()) grpcHandler := grpc.NewAPIHandler(handler) grpcHandler.Register(s.GetDispatcher()) s.adminHandler = admin.NewHandler(s, s.params, s.config, dh) s.adminHandler = accesscontrolled.NewAdminHandler(s.adminHandler, s, s.params.Authorizer, s.params.AuthorizationConfig) adminThriftHandler := thrift.NewAdminHandler(s.adminHandler) adminThriftHandler.Register(s.GetDispatcher()) adminGRPCHandler := grpc.NewAdminHandler(s.adminHandler) adminGRPCHandler.Register(s.GetDispatcher()) // must start resource first s.Resource.Start() startCtx, cancel := context.WithTimeout(context.Background(), time.Second) // should take nearly no time at all defer cancel() if err := collections.user.OnStart(startCtx); err != nil { logger.Fatal("failed to start user global ratelimiter collection", tag.Error(err)) } if err := collections.worker.OnStart(startCtx); err != nil { logger.Fatal("failed to start worker global ratelimiter collection", tag.Error(err)) } if err := collections.visibility.OnStart(startCtx); err != nil { logger.Fatal("failed to start visibility global ratelimiter collection", tag.Error(err)) } if err := collections.async.OnStart(startCtx); err != nil { logger.Fatal("failed to start async global ratelimiter collection", tag.Error(err)) } cancel() s.ratelimiterCollections = collections // save so they can be stopped later s.handler.Start() s.adminHandler.Start() // base (service is not started in frontend or admin handler) in case of race condition in yarpc registration function logger.Info("frontend started") <-s.stopC } type globalRatelimiterCollections struct { user, worker, visibility, async *collection.Collection } // ratelimiterCollections contains the "base" ratelimiters that make up both: // - "local" limits, which do not use global-load-balancing to adjust to request load // - fallbacks within "global" limits, for when the global-load information cannot be retrieved (startup, errors, etc) type ratelimiterCollections struct { user, worker, visibility, async *quotas.Collection } func (s *Service) createGlobalQuotaCollections() (globalRatelimiterCollections, error) { create := func(name string, local, global *quotas.Collection, targetRPS dynamicproperties.IntPropertyFnWithDomainFilter) (*collection.Collection, error) { c, err := collection.New( name, local, global, s.config.GlobalRatelimiterUpdateInterval, targetRPS, s.config.GlobalRatelimiterKeyMode, s.GetRatelimiterAggregatorsClient(), s.GetLogger(), s.GetMetricsClient(), ) if err != nil { return nil, fmt.Errorf("error creating %v collection: %w", name, err) } return c, nil } var combinedErr error // to safely shadow global ratelimits, we must make duplicate *quota.Collection collections // so they do not share data when the global limiter decides to use its local fallback. // these are then combined into the global/algorithm.Collection to handle all limiting calls local, global := s.createBaseLimiters(), s.createBaseLimiters() user, err := create("user", local.user, global.user, s.config.GlobalDomainUserRPS) combinedErr = multierr.Combine(combinedErr, err) worker, err := create("worker", local.worker, global.worker, s.config.GlobalDomainWorkerRPS) combinedErr = multierr.Combine(combinedErr, err) visibility, err := create("visibility", local.visibility, global.visibility, s.config.GlobalDomainVisibilityRPS) combinedErr = multierr.Combine(combinedErr, err) async, err := create("async", local.async, global.async, s.config.GlobalDomainAsyncRPS) combinedErr = multierr.Combine(combinedErr, err) return globalRatelimiterCollections{ user: user, worker: worker, visibility: visibility, async: async, }, combinedErr } func (s *Service) createBaseLimiters() ratelimiterCollections { create := func(shared, perInstance dynamicproperties.IntPropertyFnWithDomainFilter) *quotas.Collection { return quotas.NewCollection(permember.NewPerMemberDynamicRateLimiterFactory( service.Frontend, shared, perInstance, s.GetMembershipResolver(), )) } return ratelimiterCollections{ user: create(s.config.GlobalDomainUserRPS, s.config.MaxDomainUserRPSPerInstance), worker: create(s.config.GlobalDomainWorkerRPS, s.config.MaxDomainWorkerRPSPerInstance), visibility: create(s.config.GlobalDomainVisibilityRPS, s.config.MaxDomainVisibilityRPSPerInstance), async: create(s.config.GlobalDomainAsyncRPS, s.config.MaxDomainAsyncRPSPerInstance), } } // Stop stops the service func (s *Service) Stop() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } // initiate graceful shutdown: // 1. Fail rpc health check, this will cause client side load balancer to stop forwarding requests to this node // 2. wait for failure detection time // 3. stop taking new requests by returning InternalServiceError // 4. Wait for a second // 5. Stop everything forcefully and return requestDrainTime := min(time.Second, s.config.ShutdownDrainDuration()) failureDetectionTime := max(0, s.config.ShutdownDrainDuration()-requestDrainTime) s.GetLogger().Info("ShutdownHandler: Updating rpc health status to ShuttingDown") s.handler.UpdateHealthStatus(api.HealthStatusShuttingDown) s.GetLogger().Info("ShutdownHandler: Waiting for others to discover I am unhealthy") time.Sleep(failureDetectionTime) s.handler.Stop() s.adminHandler.Stop() s.stopRatelimiters() s.GetLogger().Info("ShutdownHandler: Draining traffic") time.Sleep(requestDrainTime) close(s.stopC) s.Resource.Stop() s.params.Logger.Info("frontend stopped") } func (s *Service) stopRatelimiters() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) // should take nearly no time at all defer cancel() if err := s.ratelimiterCollections.user.OnStop(ctx); err != nil { s.GetLogger().Error("failed to stop user global ratelimiter collection", tag.Error(err)) } if err := s.ratelimiterCollections.worker.OnStop(ctx); err != nil { s.GetLogger().Error("failed to stop worker global ratelimiter collection", tag.Error(err)) } if err := s.ratelimiterCollections.visibility.OnStop(ctx); err != nil { s.GetLogger().Error("failed to stop visibility global ratelimiter collection", tag.Error(err)) } if err := s.ratelimiterCollections.async.OnStop(ctx); err != nil { s.GetLogger().Error("failed to stop async global ratelimiter collection", tag.Error(err)) } } ================================================ FILE: service/frontend/templates/accesscontrolled.tmpl ================================================ import ( "context" "github.com/uber/cadence/common/authorization" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/tag" {{- if eq .Interface.Name "Handler"}} "github.com/uber/cadence/common/metrics" {{end}} "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" ) {{$permissionMap := dict "CountWorkflowExecutions" "PermissionRead"}} {{$permissionMap = set $permissionMap "DeleteDomain" "PermissionAdmin"}} {{$permissionMap = set $permissionMap "DeprecateDomain" "PermissionAdmin"}} {{$permissionMap = set $permissionMap "DescribeDomain" "PermissionRead"}} {{$permissionMap = set $permissionMap "DescribeTaskList" "PermissionRead"}} {{$permissionMap = set $permissionMap "DescribeWorkflowExecution" "PermissionRead"}} {{$permissionMap = set $permissionMap "GetWorkflowExecutionHistory" "PermissionRead"}} {{$permissionMap = set $permissionMap "ListArchivedWorkflowExecutions" "PermissionRead"}} {{$permissionMap = set $permissionMap "ListClosedWorkflowExecutions" "PermissionRead"}} {{$permissionMap = set $permissionMap "ListOpenWorkflowExecutions" "PermissionRead"}} {{$permissionMap = set $permissionMap "ListWorkflowExecutions" "PermissionRead"}} {{$permissionMap = set $permissionMap "PollForActivityTask" "PermissionProcess"}} {{$permissionMap = set $permissionMap "PollForDecisionTask" "PermissionProcess"}} {{$permissionMap = set $permissionMap "QueryWorkflow" "PermissionRead"}} {{$permissionMap = set $permissionMap "RegisterDomain" "PermissionAdmin"}} {{$permissionMap = set $permissionMap "RequestCancelWorkflowExecution" "PermissionWrite"}} {{$permissionMap = set $permissionMap "ResetWorkflowExecution" "PermissionWrite"}} {{$permissionMap = set $permissionMap "RestartWorkflowExecution" "PermissionWrite"}} {{$permissionMap = set $permissionMap "ScanWorkflowExecutions" "PermissionRead"}} {{$permissionMap = set $permissionMap "SignalWithStartWorkflowExecution" "PermissionWrite"}} {{$permissionMap = set $permissionMap "SignalWithStartWorkflowExecutionAsync" "PermissionWrite"}} {{$permissionMap = set $permissionMap "SignalWorkflowExecution" "PermissionWrite"}} {{$permissionMap = set $permissionMap "StartWorkflowExecution" "PermissionWrite"}} {{$permissionMap = set $permissionMap "StartWorkflowExecutionAsync" "PermissionWrite"}} {{$permissionMap = set $permissionMap "TerminateWorkflowExecution" "PermissionWrite"}} {{$permissionMap = set $permissionMap "ListTaskListPartitions" "PermissionRead"}} {{$permissionMap = set $permissionMap "GetTaskListsByDomain" "PermissionRead"}} {{$permissionMap = set $permissionMap "RefreshWorkflowTasks" "PermissionWrite"}} {{$permissionMap = set $permissionMap "UpdateDomain" "PermissionAdmin"}} {{$permissionMap = set $permissionMap "FailoverDomain" "PermissionWrite"}} {{$adminPermissionMap := dict }} {{$adminPermissionMap = set $adminPermissionMap "DescribeCluster" "PermissionRead"}} {{$nonDomainAuthAPIs := list "RegisterDomain" "DescribeDomain" "UpdateDomain" "DeprecateDomain" "DeleteDomain" "GetSearchAttributes" "GetClusterInfo" "ResetStickyTaskList" "RecordActivityTaskHeartbeat" "RespondActivityTaskCanceled" "RespondActivityTaskCompleted" "RespondActivityTaskFailed" "RespondDecisionTaskCompleted" "RespondDecisionTaskFailed" "RespondQueryTaskCompleted"}} {{$taskListAuthAPIs := list "PollForActivityTask" "PollForDecisionTask"}} {{$workflowTypeAuthAPIs := list "SignalWithStartWorkflowExecution" "StartWorkflowExecution" "SignalWithStartWorkflowExecutionAsync" "StartWorkflowExecutionAsync"}} {{$interfaceName := .Interface.Name}} {{$interfaceType := .Interface.Type}} {{$handlerName := (index .Vars "handler")}} {{ $decorator := (printf "%s%s" (down $handlerName) $interfaceName) }} {{ $Decorator := (printf "%s%s" $handlerName $interfaceName) }} // {{$decorator}} frontend handler wrapper for authentication and authorization type {{$decorator}} struct { handler {{.Interface.Type}} authorizer authorization.Authorizer resource.Resource } // New{{$Decorator}} creates frontend handler with authentication support func New{{$Decorator}}(handler {{$.Interface.Type}}, resource resource.Resource, authorizer authorization.Authorizer, cfg config.Authorization) {{.Interface.Type}} { if authorizer == nil { var err error authorizer, err = authorization.NewAuthorizer(cfg, resource.GetLogger(), resource.GetDomainCache()) if err != nil { resource.GetLogger().Fatal("Error when initiating the Authorizer", tag.Error(err)) } } return &{{$decorator}}{ handler: handler, authorizer: authorizer, Resource: resource, } } {{range $method := .Interface.Methods}} func (a *{{$decorator}}) {{$method.Declaration}} { {{- if or (eq $method.Name "Start") (eq $method.Name "Stop")}} a.handler.{{$method.Call}} } {{- else}} {{- if or (eq $interfaceType "admin.Handler") (hasKey $permissionMap $method.Name) }} {{- if and (eq $interfaceType "api.Handler") (ge (len $method.Params) 2)}} {{- if has $method.Name $nonDomainAuthAPIs}} scope := a.GetMetricsClient().Scope(metrics.Frontend{{$method.Name}}Scope) {{- else}} scope := a.getMetricsScopeWithDomain(metrics.Frontend{{$method.Name}}Scope, {{(index $method.Params 1).Name}}.GetDomain()) {{- end}} {{- end}} attr := &authorization.Attributes{ APIName: "{{$method.Name}}", {{- if eq $interfaceType "admin.Handler"}} {{- if hasKey $adminPermissionMap $method.Name}} Permission: authorization.{{get $adminPermissionMap $method.Name}}, {{- else}} Permission: authorization.PermissionAdmin, {{- end}} {{- else if hasKey $permissionMap $method.Name}} Permission: authorization.{{get $permissionMap $method.Name}}, {{- end}} {{- if ge (len $method.Params) 2}} RequestBody: authorization.NewFilteredRequestBody( {{(index $method.Params 1).Name}} ), {{- if not (or (has $method.Name $nonDomainAuthAPIs) (eq $interfaceType "admin.Handler"))}} DomainName: {{(index $method.Params 1).Name}}.GetDomain(), {{- else if eq $method.Name "DescribeDomain"}} DomainName: {{(index $method.Params 1).Name}}.GetName(), {{- end}} {{- if has $method.Name $taskListAuthAPIs}} TaskList: {{(index $method.Params 1).Name}}.TaskList, {{- end}} {{- if has $method.Name $workflowTypeAuthAPIs}} WorkflowType: {{(index $method.Params 1).Name}}.WorkflowType, TaskList: {{(index $method.Params 1).Name}}.TaskList, {{- end}} {{- end}} } {{- if eq $interfaceType "admin.Handler"}} isAuthorized, err := a.isAuthorized(ctx, attr) {{- else}} isAuthorized, err := a.isAuthorized(ctx, attr, scope) {{- end}} if err != nil { {{- if eq (len $method.Results) 1}} return err {{- else}} return nil, err {{- end}} } if !isAuthorized { {{- if eq (len $method.Results) 1}} return errUnauthorized {{- else}} return nil, errUnauthorized {{- end}} } {{- end}} return a.handler.{{$method.Call}} } {{- end}} {{end}} ================================================ FILE: service/frontend/templates/clusterredirection.tmpl ================================================ import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" frontendcfg "github.com/uber/cadence/service/frontend/config" ) {{$nonForwardingAPIs := list "Health" "DeprecateDomain" "DeleteDomain" "DescribeDomain" "FailoverDomain" "ListDomains" "RegisterDomain" "UpdateDomain" "GetSearchAttributes" "GetClusterInfo" "DiagnoseWorkflowExecution" "ListFailoverHistory"}} {{$domainIDAPIs := list "RecordActivityTaskHeartbeat" "RespondActivityTaskCanceled" "RespondActivityTaskCompleted" "RespondActivityTaskFailed" "RespondDecisionTaskCompleted" "RespondDecisionTaskFailed" "RespondQueryTaskCompleted"}} {{$startWFAPIs := list "StartWorkflowExecution" "StartWorkflowExecutionAsync" "SignalWithStartWorkflowExecution" "SignalWithStartWorkflowExecutionAsync"}} {{$nonstartWFAPIs := list "DescribeWorkflowExecutionRequest" "GetWorkflowExecutionHistory" "QueryWorkflowRequest" "RequestCancelWorkflowExecution" "ResetWorkflowExecution" "RestartWorkflowExecution" "SignalWorkflowExecution" "TerminateWorkflowExecution" }} {{$queryTaskTokenAPIs := list "RespondQueryTaskCompleted"}} {{$readAPIsWithStrongConsistency := list "QueryWorkflow" "DescribeWorkflowExecution" "GetWorkflowExecutionHistory"}} type ( // ClusterRedirectionHandlerImpl is simple wrapper over frontend service, doing redirection based on policy for global domains not being active in current cluster clusterRedirectionHandler struct { resource.Resource currentClusterName string redirectionPolicy ClusterRedirectionPolicy tokenSerializer common.TaskTokenSerializer domainCache cache.DomainCache config *frontendcfg.Config frontendHandler api.Handler callOptions []yarpc.CallOption } ) // NewAPIHandler creates a frontend handler to handle cluster redirection for global domains not being active in current cluster func NewAPIHandler( wfHandler api.Handler, resource resource.Resource, config *frontendcfg.Config, policy config.ClusterRedirectionPolicy, ) api.Handler { dcRedirectionPolicy := RedirectionPolicyGenerator( resource.GetClusterMetadata(), config, policy, resource.GetLogger(), resource.GetActiveClusterManager(), resource.GetMetricsClient(), ) return &clusterRedirectionHandler{ Resource: resource, currentClusterName: resource.GetClusterMetadata().GetCurrentClusterName(), domainCache: resource.GetDomainCache(), config: config, redirectionPolicy: dcRedirectionPolicy, tokenSerializer: common.NewJSONTaskTokenSerializer(), frontendHandler: wfHandler, callOptions: []yarpc.CallOption{yarpc.WithHeader(common.AutoforwardingClusterHeaderName, resource.GetClusterMetadata().GetCurrentClusterName())}, } } {{range $method := .Interface.Methods}} func (handler *clusterRedirectionHandler) {{$method.Declaration}} { {{- if has $method.Name $nonForwardingAPIs}} return handler.frontendHandler.{{$method.Call}} {{- else}} var ( apiName = "{{$method.Name}}" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) {{- if has $method.Name $readAPIsWithStrongConsistency}} // Only autoforward strong consistent queries, this is done for two reasons: // 1. Query is meant to be fast, autoforwarding all queries will increase latency. // 2. If eventual consistency was requested then the results from running out of local dc will be fine. if {{(index $method.Params 1).Name}}.GetQueryConsistencyLevel() == types.QueryConsistencyLevelStrong { requestedConsistencyLevel = types.QueryConsistencyLevelStrong } {{- end}} var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirection{{$method.Name}}Scope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() {{if has $method.Name $domainIDAPIs}} var idGetter domainIDGetter {{if has $method.Name $queryTaskTokenAPIs}} idGetter, err = handler.tokenSerializer.DeserializeQueryTaskToken({{(index $method.Params 1).Name}}.TaskToken) {{- else}} idGetter, err = handler.tokenSerializer.Deserialize({{(index $method.Params 1).Name}}.TaskToken) {{- end}} if err == nil { domainEntry, err = handler.domainCache.GetDomainByID(idGetter.GetDomainID()) } {{- else}} domainEntry, err = handler.domainCache.GetDomain({{(index $method.Params 1).Name}}.Domain) {{- end}} if err != nil { {{- if eq (len $method.Results) 1}} return err {{- else}} return nil, err {{- end}} } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution {{- if has $method.Name $startWFAPIs}} if !handler.config.EnableActiveClusterSelectionPolicyInStartWorkflow(domainEntry.GetInfo().Name) { {{(index $method.Params 1).Name}}.ActiveClusterSelectionPolicy = nil } actClSelPolicyForNewWF = {{(index $method.Params 1).Name}}.ActiveClusterSelectionPolicy {{- if eq $method.Name "SignalWithStartWorkflowExecution"}} workflowExecution = &types.WorkflowExecution{ WorkflowID: {{(index $method.Params 1).Name}}.GetWorkflowID(), } {{- end}} {{- else if has $method.Name $nonstartWFAPIs}} workflowExecution = {{(index $method.Params 1).Name}}.GetWorkflowExecution() {{- end}} err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: {{$method.ResultsNames}} = handler.frontendHandler.{{$method.Call}} default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } {{$method.ResultsNames}} = remoteClient.{{$method.Name}}({{$method.Params.Pass}}, handler.callOptions...) } return err }) return {{$method.ResultsNames}} {{- end}} } {{end}} ================================================ FILE: service/frontend/templates/metered.tmpl ================================================ import ( "context" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" "github.com/uber/cadence/service/frontend/config" ) {{- $nonDomainSpecificAPIs := list "Health" "DeprecateDomain" "DeleteDomain" "DescribeDomain" "ListDomains" "RegisterDomain" "UpdateDomain" "GetSearchAttributes" "GetClusterInfo" "ListFailoverHistory"}} {{- $domainIDAPIs := list "RecordActivityTaskHeartbeat" "RespondActivityTaskCanceled" "RespondActivityTaskCompleted" "RespondActivityTaskFailed" "RespondDecisionTaskCompleted" "RespondDecisionTaskFailed" "RespondQueryTaskCompleted"}} {{- $queryTaskTokenAPIs := list "RespondQueryTaskCompleted"}} {{- $pollerAPIs := list "PollForActivityTask" "PollForDecisionTask"}} {{- $interfaceName := .Interface.Name}} {{- $interfaceType := .Interface.Type}} {{- $handlerName := (index .Vars "handler")}} {{- $decorator := (printf "%s%s" (down $handlerName) $interfaceName) }} {{- $Decorator := (printf "%s%s" $handlerName $interfaceName) }} // {{$decorator}} frontend handler wrapper for authentication and authorization type {{$decorator}} struct { handler {{.Interface.Type}} logger log.Logger metricsClient metrics.Client domainCache cache.DomainCache cfg *config.Config tokenSerializer common.TaskTokenSerializer } // New{{$Decorator}} creates frontend handler with metrics and logging func New{{$Decorator}}(handler {{$.Interface.Type}}, logger log.Logger, metricsClient metrics.Client, domainCache cache.DomainCache, cfg *config.Config) {{.Interface.Type}} { return &{{$decorator}}{ handler: handler, logger: logger, metricsClient: metricsClient, domainCache: domainCache, cfg: cfg, tokenSerializer: common.NewJSONTaskTokenSerializer(), } } {{range $method := .Interface.Methods}} func (h *{{$decorator}}) {{$method.Declaration}} { {{- if eq $method.Name "Health"}} {{ $method.Pass "h.handler." }} {{- else}} defer func() { log.CapturePanic(recover(), h.logger, &err) }() {{- if eq $method.Name "SignalWorkflowExecution"}} ctx = h.withSignalName(ctx, {{(index $method.Params 1).Name}}.GetDomain(), {{(index $method.Params 1).Name}}.GetSignalName()) {{- end}} tags := []tag.Tag{tag.WorkflowHandlerName("{{$method.Name}}")} {{- $scope := printf "metrics.Frontend%sScope" $method.Name}} {{- $domainMetricTag := "metrics.DomainUnknownTag()"}} {{- if not (has $method.Name $nonDomainSpecificAPIs) }} {{- $domain := printf "%s.GetDomain()" (index $method.Params 1).Name}} {{- if has $method.Name $domainIDAPIs}} {{- $domain = "domainName"}} {{- if has $method.Name $queryTaskTokenAPIs}} token, err := h.tokenSerializer.DeserializeQueryTaskToken({{(index $method.Params 1).Name}}.TaskToken) {{- else}} token, err := h.tokenSerializer.Deserialize({{(index $method.Params 1).Name}}.TaskToken) {{- end}} if err != nil { return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } {{- if has $method.Name $queryTaskTokenAPIs}} tags = append(tags, tag.WorkflowDomainName(domainName)) {{- else}} tags = append(tags, tag.WorkflowDomainName(domainName), tag.WorkflowID(token.WorkflowID), tag.WorkflowRunID(token.RunID)) {{- end}} {{- else}} tags = append(tags, to{{printf "%sRequest" $method.Name}}Tags({{(index $method.Params 1).Name}})...) {{- end}} {{- $domainMetricTag = printf "metrics.DomainTag(%s)" $domain}} {{- end}} {{- if has $method.Name $pollerAPIs}} scope := common.NewPerTaskListScope({{(index $method.Params 1).Name}}.Domain, {{(index $method.Params 1).Name}}.TaskList.GetName(), {{(index $method.Params 1).Name}}.TaskList.GetKind(), h.metricsClient, {{$scope}}).Tagged(metrics.GetContextTags(ctx)...) scope.IncCounter(metrics.CadenceRequestsPerTaskListWithoutRollup) sw := scope.StartTimer(metrics.CadenceLatencyPerTaskList) defer sw.Stop() scopePerDomain := h.metricsClient.Scope({{$scope}}).Tagged(append(metrics.GetContextTags(ctx), {{$domainMetricTag}})...) scopePerDomain.IncCounter(metrics.CadenceRequests) swPerDomain := scopePerDomain.StartTimer(metrics.CadenceLatency) defer swPerDomain.Stop() {{- else}} scope := h.metricsClient.Scope({{$scope}}).Tagged(append(metrics.GetContextTags(ctx), {{$domainMetricTag}})...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() {{- end}} logger := h.logger.WithTags(tags...) {{$method.ResultsNames}} = h.handler.{{$method.Call}} if err != nil { {{- if eq (len $method.Results) 1}} return h.handleErr(err, scope, logger) {{- else}} return nil, h.handleErr(err, scope, logger) {{- end}} } return {{$method.ResultsNames}} {{- end}} } {{- end}} ================================================ FILE: service/frontend/templates/ratelimited.tmpl ================================================ import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/service/frontend/api" "github.com/uber/cadence/service/frontend/validate" ) {{$ratelimitTypeMap := dict "PollForActivityTask" "ratelimitTypeWorkerPoll"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "PollForDecisionTask" "ratelimitTypeWorkerPoll"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RecordActivityTaskHeartbeat" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RecordActivityTaskHeartbeatByID" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "ResetStickyTaskList" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RespondActivityTaskCanceled" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RespondActivityTaskCanceledByID" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RespondActivityTaskCompleted" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RespondActivityTaskCompletedByID" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RespondActivityTaskFailed" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RespondActivityTaskFailedByID" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RespondDecisionTaskCompleted" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RespondDecisionTaskFailed" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RespondQueryTaskCompleted" "ratelimitTypeWorker"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "DescribeTaskList" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "DescribeWorkflowExecution" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "DiagnoseWorkflowExecution" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "FailoverDomain" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "GetTaskListsByDomain" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "GetWorkflowExecutionHistory" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "ListTaskListPartitions" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "QueryWorkflow" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RefreshWorkflowTasks" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RequestCancelWorkflowExecution" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "ResetWorkflowExecution" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RestartWorkflowExecution" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "SignalWorkflowExecution" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "SignalWithStartWorkflowExecution" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "StartWorkflowExecution" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "TerminateWorkflowExecution" "ratelimitTypeUser"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "CountWorkflowExecutions" "ratelimitTypeVisibility"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "ListArchivedWorkflowExecutions" "ratelimitTypeVisibility"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "ListClosedWorkflowExecutions" "ratelimitTypeVisibility"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "ListOpenWorkflowExecutions" "ratelimitTypeVisibility"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "ListWorkflowExecutions" "ratelimitTypeVisibility"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "ScanWorkflowExecutions" "ratelimitTypeVisibility"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "StartWorkflowExecutionAsync" "ratelimitTypeAsync"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "SignalWithStartWorkflowExecutionAsync" "ratelimitTypeAsync"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "Health" "ratelimitTypeNoop"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "DeleteDomain" "ratelimitTypeNoop"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "DeprecateDomain" "ratelimitTypeNoop"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "DescribeDomain" "ratelimitTypeNoop"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "ListFailoverHistory" "ratelimitTypeNoop"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "GetClusterInfo" "ratelimitTypeNoop"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "GetSearchAttributes" "ratelimitTypeNoop"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "ListDomains" "ratelimitTypeNoop"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "RegisterDomain" "ratelimitTypeNoop"}} {{$ratelimitTypeMap = set $ratelimitTypeMap "UpdateDomain" "ratelimitTypeNoop"}} {{$domainIDAPIs := list "RecordActivityTaskHeartbeat" "RespondActivityTaskCanceled" "RespondActivityTaskCompleted" "RespondActivityTaskFailed" "RespondDecisionTaskCompleted" "RespondDecisionTaskFailed" "RespondQueryTaskCompleted"}} {{$queryTaskTokenAPIs := list "RespondQueryTaskCompleted"}} {{$nonBlockingAPIs := list "RecordActivityTaskHeartbeat" "RecordActivityTaskHeartbeatByID" "RespondActivityTaskCompleted" "RespondActivityTaskCompletedByID" "RespondActivityTaskFailed" "RespondActivityTaskFailedByID" "RespondActivityTaskCanceled" "RespondActivityTaskCanceledByID" "RespondDecisionTaskCompleted" "RespondDecisionTaskFailed" "RespondQueryTaskCompleted" "ResetStickyTaskList"}} {{$interfaceName := .Interface.Name}} {{$handlerName := (index .Vars "handler")}} {{ $decorator := (printf "%s%s" (down $handlerName) $interfaceName) }} {{ $Decorator := (printf "%s%s" $handlerName $interfaceName) }} // {{$decorator}} implements {{.Interface.Type}} interface instrumented with rate limiter. type {{$decorator}} struct { wrapped {{.Interface.Type}} tokenSerializer common.TaskTokenSerializer domainCache cache.DomainCache userRateLimiter quotas.Policy workerRateLimiter quotas.Policy visibilityRateLimiter quotas.Policy asyncRateLimiter quotas.Policy maxWorkerPollDelay dynamicproperties.DurationPropertyFnWithDomainFilter callerBypass quotas.CallerBypass } // New{{$Decorator}} creates a new instance of {{$interfaceName}} with ratelimiter. func New{{$Decorator}}( wrapped {{.Interface.Type}}, domainCache cache.DomainCache, userRateLimiter quotas.Policy, workerRateLimiter quotas.Policy, visibilityRateLimiter quotas.Policy, asyncRateLimiter quotas.Policy, maxWorkerPollDelay dynamicproperties.DurationPropertyFnWithDomainFilter, callerBypass quotas.CallerBypass, ) {{.Interface.Type}} { return &{{$decorator}}{ wrapped: wrapped, tokenSerializer: common.NewJSONTaskTokenSerializer(), domainCache: domainCache, userRateLimiter: userRateLimiter, workerRateLimiter: workerRateLimiter, visibilityRateLimiter: visibilityRateLimiter, asyncRateLimiter: asyncRateLimiter, maxWorkerPollDelay: maxWorkerPollDelay, callerBypass: callerBypass, } } {{range $method := .Interface.Methods}} func (h *{{$decorator}}) {{$method.Declaration}} { {{- $ratelimitType := get $ratelimitTypeMap $method.Name}} {{- if not (eq $ratelimitType "ratelimitTypeNoop")}} if {{(index $method.Params 1).Name}} == nil { err = validate.ErrRequestNotSet return } {{- $domain := printf "%s.GetDomain()" (index $method.Params 1).Name}} {{- if has $method.Name $domainIDAPIs}} {{- $domain = "domainName"}} if {{(index $method.Params 1).Name}}.TaskToken == nil { err = validate.ErrTaskTokenNotSet return } {{- if has $method.Name $queryTaskTokenAPIs}} token, err := h.tokenSerializer.DeserializeQueryTaskToken({{(index $method.Params 1).Name}}.TaskToken) {{- else}} token, err := h.tokenSerializer.Deserialize({{(index $method.Params 1).Name}}.TaskToken) {{- end}} if err != nil { return } if token.DomainID == "" { err = validate.ErrDomainNotSet return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } {{- else}} if {{$domain}} == "" { err = validate.ErrDomainNotSet return } {{- end}} {{- if has $method.Name $nonBlockingAPIs}} // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain({{(index $method.Params 0).Name}}, {{$ratelimitType}}, {{$domain}}) {{- else}} if limitErr := h.allowDomain({{(index $method.Params 0).Name}}, {{$ratelimitType}}, {{$domain}}); limitErr != nil { err = limitErr return } {{- end}} {{- end}} {{$method.Pass "h.wrapped."}} } {{end}} ================================================ FILE: service/frontend/templates/versioncheck.tmpl ================================================ import ( "context" "github.com/uber/cadence/common/client" "github.com/uber/cadence/service/frontend/api" "github.com/uber/cadence/service/frontend/config" ) {{$excludedAPIs := list "Health" "GetClusterInfo" "ListTaskListPartitions" "GetTaskListsByDomain" "RefreshWorkflowTasks" }} type ( versionCheckHandler struct { frontendHandler api.Handler config *config.Config versionChecker client.VersionChecker } ) func NewAPIHandler( wfHandler api.Handler, config *config.Config, versionChecker client.VersionChecker, ) api.Handler { return &versionCheckHandler{ frontendHandler: wfHandler, config: config, versionChecker: versionChecker, } } {{range $method := .Interface.Methods}} func (h *versionCheckHandler) {{$method.Declaration}} { {{- if not (has $method.Name $excludedAPIs)}} err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } {{- end}} {{$method.Pass "h.frontendHandler."}} } {{end}} ================================================ FILE: service/frontend/validate/errors.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package validate import ( "github.com/google/uuid" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/config" ) var ( ErrInvalidFilters = &types.BadRequestError{Message: "Request Filters are invalid, unable to parse."} ErrDomainNotSet = &types.BadRequestError{Message: "Domain not set on request."} ErrTaskTokenNotSet = &types.BadRequestError{Message: "Task token not set on request."} ErrInvalidTaskToken = &types.BadRequestError{Message: "Invalid TaskToken."} ErrTaskListNotSet = &types.BadRequestError{Message: "TaskList is not set on request."} ErrTaskListTypeNotSet = &types.BadRequestError{Message: "TaskListType is not set on request."} ErrExecutionNotSet = &types.BadRequestError{Message: "Execution is not set on request."} ErrWorkflowIDNotSet = &types.BadRequestError{Message: "WorkflowId is not set on request."} ErrActivityIDNotSet = &types.BadRequestError{Message: "ActivityID is not set on request."} ErrSignalNameNotSet = &types.BadRequestError{Message: "SignalName is not set on request."} ErrInvalidRunID = &types.BadRequestError{Message: "Invalid RunId."} ErrInvalidNextPageToken = &types.BadRequestError{Message: "Invalid NextPageToken."} ErrNextPageTokenRunIDMismatch = &types.BadRequestError{Message: "RunID in the request does not match the NextPageToken."} ErrQueryNotSet = &types.BadRequestError{Message: "WorkflowQuery is not set on request."} ErrQueryTypeNotSet = &types.BadRequestError{Message: "QueryType is not set on request."} ErrRequestNotSet = &types.BadRequestError{Message: "Request is nil."} ErrNoPermission = &types.BadRequestError{Message: "No permission to do this operation."} ErrWorkflowTypeNotSet = &types.BadRequestError{Message: "WorkflowType is not set on request."} ErrInvalidRetention = &types.BadRequestError{Message: "RetentionDays is invalid."} ErrInvalidExecutionStartToCloseTimeoutSeconds = &types.BadRequestError{Message: "A valid ExecutionStartToCloseTimeoutSeconds is not set on request."} ErrInvalidTaskStartToCloseTimeoutSeconds = &types.BadRequestError{Message: "A valid TaskStartToCloseTimeoutSeconds is not set on request."} ErrInvalidDelayStartSeconds = &types.BadRequestError{Message: "A valid DelayStartSeconds is not set on request."} ErrInvalidJitterStartSeconds = &types.BadRequestError{Message: "A valid JitterStartSeconds is not set on request (negative)."} ErrInvalidJitterStartSeconds2 = &types.BadRequestError{Message: "A valid JitterStartSeconds is not set on request (larger than cron duration)."} ErrQueryDisallowedForDomain = &types.BadRequestError{Message: "Domain is not allowed to query, please contact cadence team to re-enable queries."} ErrClusterNameNotSet = &types.BadRequestError{Message: "Cluster name is not set."} ErrEmptyReplicationInfo = &types.BadRequestError{Message: "Replication task info is not set."} ErrEmptyQueueType = &types.BadRequestError{Message: "Queue type is not set."} ErrDomainInLockdown = &types.BadRequestError{Message: "Domain is not accepting fail overs at this time due to lockdown."} ErrShuttingDown = &types.InternalServiceError{Message: "Shutting down"} // Err for archival ErrHistoryNotFound = &types.BadRequestError{Message: "Requested workflow history not found, may have passed retention period."} // Err for string too long ErrDomainTooLong = &types.BadRequestError{Message: "Domain length exceeds limit."} ErrWorkflowTypeTooLong = &types.BadRequestError{Message: "WorkflowType length exceeds limit."} ErrWorkflowIDTooLong = &types.BadRequestError{Message: "WorkflowID length exceeds limit."} ErrSignalNameTooLong = &types.BadRequestError{Message: "SignalName length exceeds limit."} ErrTaskListTooLong = &types.BadRequestError{Message: "TaskList length exceeds limit."} ErrRequestIDTooLong = &types.BadRequestError{Message: "RequestID length exceeds limit."} ErrIdentityTooLong = &types.BadRequestError{Message: "Identity length exceeds limit."} ) func CheckPermission( config *config.Config, securityToken string, ) error { if config.EnableAdminProtection() { if securityToken == "" { return ErrNoPermission } requiredToken := config.AdminOperationToken() if securityToken != requiredToken { return ErrNoPermission } } return nil } func CheckExecution(w *types.WorkflowExecution) error { if w == nil { return ErrExecutionNotSet } if w.GetWorkflowID() == "" { return ErrWorkflowIDNotSet } if w.GetRunID() != "" { if _, err := uuid.Parse(w.GetRunID()); err != nil { return ErrInvalidRunID } } return nil } ================================================ FILE: service/frontend/wrappers/accesscontrolled/access_controlled.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package accesscontrolled import ( "context" "github.com/uber/cadence/common/authorization" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) var errUnauthorized = &types.AccessDeniedError{Message: "Request unauthorized."} func (a *adminHandler) isAuthorized(ctx context.Context, attr *authorization.Attributes) (bool, error) { result, err := a.authorizer.Authorize(ctx, attr) if err != nil { return false, err } isAuth := result.Decision == authorization.DecisionAllow return isAuth, nil } func (a *apiHandler) isAuthorized( ctx context.Context, attr *authorization.Attributes, scope metrics.Scope, ) (bool, error) { sw := scope.StartTimer(metrics.CadenceAuthorizationLatency) defer sw.Stop() result, err := a.authorizer.Authorize(ctx, attr) if err != nil { scope.IncCounter(metrics.CadenceErrAuthorizeFailedCounter) return false, err } isAuth := result.Decision == authorization.DecisionAllow if !isAuth { scope.IncCounter(metrics.CadenceErrUnauthorizedCounter) } return isAuth, nil } // getMetricsScopeWithDomain return metrics scope with domain tag func (a *apiHandler) getMetricsScopeWithDomain( scope metrics.ScopeIdx, domain string, ) metrics.Scope { if domain != "" { return a.GetMetricsClient().Scope(scope).Tagged(metrics.DomainTag(domain)) } return a.GetMetricsClient().Scope(scope).Tagged(metrics.DomainUnknownTag()) } ================================================ FILE: service/frontend/wrappers/accesscontrolled/access_controlled_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package accesscontrolled import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/authorization" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/admin" ) func TestIsAuthorized(t *testing.T) { testCases := []struct { name string mockSetup func(*authorization.MockAuthorizer, *mocks.Scope) isAuthorized bool wantErr bool }{ { name: "Succes case", mockSetup: func(authorizer *authorization.MockAuthorizer, scope *mocks.Scope) { authorizer.EXPECT().Authorize(gomock.Any(), gomock.Any()).Return(authorization.Result{Decision: authorization.DecisionAllow}, nil) scope.On("StartTimer", metrics.CadenceAuthorizationLatency).Return(metrics.NewTestStopwatch()).Once() }, isAuthorized: true, wantErr: false, }, { name: "Error case - unauthorized", mockSetup: func(authorizer *authorization.MockAuthorizer, scope *mocks.Scope) { authorizer.EXPECT().Authorize(gomock.Any(), gomock.Any()).Return(authorization.Result{Decision: authorization.DecisionDeny}, nil) scope.On("StartTimer", metrics.CadenceAuthorizationLatency).Return(metrics.NewTestStopwatch()).Once() scope.On("IncCounter", metrics.CadenceErrUnauthorizedCounter).Return().Once() }, isAuthorized: false, wantErr: false, }, { name: "Error case - authorization error", mockSetup: func(authorizer *authorization.MockAuthorizer, scope *mocks.Scope) { authorizer.EXPECT().Authorize(gomock.Any(), gomock.Any()).Return(authorization.Result{}, errors.New("some random error")) scope.On("StartTimer", metrics.CadenceAuthorizationLatency).Return(metrics.NewTestStopwatch()).Once() scope.On("IncCounter", metrics.CadenceErrAuthorizeFailedCounter).Return().Once() }, isAuthorized: false, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockAuthorizer := authorization.NewMockAuthorizer(controller) mockMetricsScope := &mocks.Scope{} tc.mockSetup(mockAuthorizer, mockMetricsScope) handler := &apiHandler{authorizer: mockAuthorizer} got, err := handler.isAuthorized(context.Background(), &authorization.Attributes{}, mockMetricsScope) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.isAuthorized, got) } }) } } func TestDescribeCluster(t *testing.T) { someErr := errors.New("some random err") testCases := []struct { name string mockSetup func(*authorization.MockAuthorizer, *admin.MockHandler) wantErr error }{ { name: "Success case", mockSetup: func(authorizer *authorization.MockAuthorizer, adminHandler *admin.MockHandler) { authorizer.EXPECT().Authorize(gomock.Any(), gomock.Any()).Return(authorization.Result{Decision: authorization.DecisionAllow}, nil) adminHandler.EXPECT().DescribeCluster(gomock.Any()).Return(&types.DescribeClusterResponse{}, nil) }, wantErr: nil, }, { name: "Error case - unauthorized", mockSetup: func(authorizer *authorization.MockAuthorizer, adminHandler *admin.MockHandler) { authorizer.EXPECT().Authorize(gomock.Any(), gomock.Any()).Return(authorization.Result{Decision: authorization.DecisionDeny}, nil) }, wantErr: errUnauthorized, }, { name: "Error case - authorization error", mockSetup: func(authorizer *authorization.MockAuthorizer, adminHandler *admin.MockHandler) { authorizer.EXPECT().Authorize(gomock.Any(), gomock.Any()).Return(authorization.Result{}, someErr) }, wantErr: someErr, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) mockAuthorizer := authorization.NewMockAuthorizer(controller) mockAdminHandler := admin.NewMockHandler(controller) tc.mockSetup(mockAuthorizer, mockAdminHandler) handler := &adminHandler{authorizer: mockAuthorizer, handler: mockAdminHandler} _, err := handler.DescribeCluster(context.Background()) if tc.wantErr != nil { assert.Error(t, err) assert.ErrorIs(t, err, tc.wantErr) } else { assert.NoError(t, err) } }) } } ================================================ FILE: service/frontend/wrappers/accesscontrolled/admin_generated.go ================================================ package accesscontrolled // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/accesscontrolled.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/authorization" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/admin" ) // adminHandler frontend handler wrapper for authentication and authorization type adminHandler struct { handler admin.Handler authorizer authorization.Authorizer resource.Resource } // NewAdminHandler creates frontend handler with authentication support func NewAdminHandler(handler admin.Handler, resource resource.Resource, authorizer authorization.Authorizer, cfg config.Authorization) admin.Handler { if authorizer == nil { var err error authorizer, err = authorization.NewAuthorizer(cfg, resource.GetLogger(), resource.GetDomainCache()) if err != nil { resource.GetLogger().Fatal("Error when initiating the Authorizer", tag.Error(err)) } } return &adminHandler{ handler: handler, authorizer: authorizer, Resource: resource, } } func (a *adminHandler) AddSearchAttribute(ctx context.Context, ap1 *types.AddSearchAttributeRequest) (err error) { attr := &authorization.Attributes{ APIName: "AddSearchAttribute", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(ap1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.AddSearchAttribute(ctx, ap1) } func (a *adminHandler) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest) (err error) { attr := &authorization.Attributes{ APIName: "CloseShard", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(cp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.CloseShard(ctx, cp1) } func (a *adminHandler) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest) (cp2 *types.CountDLQMessagesResponse, err error) { attr := &authorization.Attributes{ APIName: "CountDLQMessages", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(cp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.CountDLQMessages(ctx, cp1) } func (a *adminHandler) DeleteWorkflow(ctx context.Context, ap1 *types.AdminDeleteWorkflowRequest) (ap2 *types.AdminDeleteWorkflowResponse, err error) { attr := &authorization.Attributes{ APIName: "DeleteWorkflow", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(ap1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.DeleteWorkflow(ctx, ap1) } func (a *adminHandler) DescribeCluster(ctx context.Context) (dp1 *types.DescribeClusterResponse, err error) { attr := &authorization.Attributes{ APIName: "DescribeCluster", Permission: authorization.PermissionRead, } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.DescribeCluster(ctx) } func (a *adminHandler) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest) (dp2 *types.DescribeHistoryHostResponse, err error) { attr := &authorization.Attributes{ APIName: "DescribeHistoryHost", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(dp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.DescribeHistoryHost(ctx, dp1) } func (a *adminHandler) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest) (dp2 *types.DescribeQueueResponse, err error) { attr := &authorization.Attributes{ APIName: "DescribeQueue", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(dp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.DescribeQueue(ctx, dp1) } func (a *adminHandler) DescribeShardDistribution(ctx context.Context, dp1 *types.DescribeShardDistributionRequest) (dp2 *types.DescribeShardDistributionResponse, err error) { attr := &authorization.Attributes{ APIName: "DescribeShardDistribution", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(dp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.DescribeShardDistribution(ctx, dp1) } func (a *adminHandler) DescribeWorkflowExecution(ctx context.Context, ap1 *types.AdminDescribeWorkflowExecutionRequest) (ap2 *types.AdminDescribeWorkflowExecutionResponse, err error) { attr := &authorization.Attributes{ APIName: "DescribeWorkflowExecution", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(ap1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.DescribeWorkflowExecution(ctx, ap1) } func (a *adminHandler) GetCrossClusterTasks(ctx context.Context, gp1 *types.GetCrossClusterTasksRequest) (gp2 *types.GetCrossClusterTasksResponse, err error) { attr := &authorization.Attributes{ APIName: "GetCrossClusterTasks", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(gp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetCrossClusterTasks(ctx, gp1) } func (a *adminHandler) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { attr := &authorization.Attributes{ APIName: "GetDLQReplicationMessages", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(gp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetDLQReplicationMessages(ctx, gp1) } func (a *adminHandler) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, gp1 *types.GetDomainAsyncWorkflowConfiguratonRequest) (gp2 *types.GetDomainAsyncWorkflowConfiguratonResponse, err error) { attr := &authorization.Attributes{ APIName: "GetDomainAsyncWorkflowConfiguraton", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(gp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetDomainAsyncWorkflowConfiguraton(ctx, gp1) } func (a *adminHandler) GetDomainIsolationGroups(ctx context.Context, request *types.GetDomainIsolationGroupsRequest) (gp1 *types.GetDomainIsolationGroupsResponse, err error) { attr := &authorization.Attributes{ APIName: "GetDomainIsolationGroups", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(request), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetDomainIsolationGroups(ctx, request) } func (a *adminHandler) GetDomainReplicationMessages(ctx context.Context, gp1 *types.GetDomainReplicationMessagesRequest) (gp2 *types.GetDomainReplicationMessagesResponse, err error) { attr := &authorization.Attributes{ APIName: "GetDomainReplicationMessages", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(gp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetDomainReplicationMessages(ctx, gp1) } func (a *adminHandler) GetDynamicConfig(ctx context.Context, gp1 *types.GetDynamicConfigRequest) (gp2 *types.GetDynamicConfigResponse, err error) { attr := &authorization.Attributes{ APIName: "GetDynamicConfig", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(gp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetDynamicConfig(ctx, gp1) } func (a *adminHandler) GetGlobalIsolationGroups(ctx context.Context, request *types.GetGlobalIsolationGroupsRequest) (gp1 *types.GetGlobalIsolationGroupsResponse, err error) { attr := &authorization.Attributes{ APIName: "GetGlobalIsolationGroups", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(request), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetGlobalIsolationGroups(ctx, request) } func (a *adminHandler) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest) (gp2 *types.GetReplicationMessagesResponse, err error) { attr := &authorization.Attributes{ APIName: "GetReplicationMessages", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(gp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetReplicationMessages(ctx, gp1) } func (a *adminHandler) GetWorkflowExecutionRawHistoryV2(ctx context.Context, gp1 *types.GetWorkflowExecutionRawHistoryV2Request) (gp2 *types.GetWorkflowExecutionRawHistoryV2Response, err error) { attr := &authorization.Attributes{ APIName: "GetWorkflowExecutionRawHistoryV2", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(gp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetWorkflowExecutionRawHistoryV2(ctx, gp1) } func (a *adminHandler) ListDynamicConfig(ctx context.Context, lp1 *types.ListDynamicConfigRequest) (lp2 *types.ListDynamicConfigResponse, err error) { attr := &authorization.Attributes{ APIName: "ListDynamicConfig", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(lp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.ListDynamicConfig(ctx, lp1) } func (a *adminHandler) MaintainCorruptWorkflow(ctx context.Context, ap1 *types.AdminMaintainWorkflowRequest) (ap2 *types.AdminMaintainWorkflowResponse, err error) { attr := &authorization.Attributes{ APIName: "MaintainCorruptWorkflow", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(ap1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.MaintainCorruptWorkflow(ctx, ap1) } func (a *adminHandler) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest) (mp2 *types.MergeDLQMessagesResponse, err error) { attr := &authorization.Attributes{ APIName: "MergeDLQMessages", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(mp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.MergeDLQMessages(ctx, mp1) } func (a *adminHandler) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest) (err error) { attr := &authorization.Attributes{ APIName: "PurgeDLQMessages", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(pp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.PurgeDLQMessages(ctx, pp1) } func (a *adminHandler) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest) (rp2 *types.ReadDLQMessagesResponse, err error) { attr := &authorization.Attributes{ APIName: "ReadDLQMessages", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(rp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.ReadDLQMessages(ctx, rp1) } func (a *adminHandler) ReapplyEvents(ctx context.Context, rp1 *types.ReapplyEventsRequest) (err error) { attr := &authorization.Attributes{ APIName: "ReapplyEvents", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(rp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.ReapplyEvents(ctx, rp1) } func (a *adminHandler) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest) (err error) { attr := &authorization.Attributes{ APIName: "RefreshWorkflowTasks", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(rp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.RefreshWorkflowTasks(ctx, rp1) } func (a *adminHandler) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest) (err error) { attr := &authorization.Attributes{ APIName: "RemoveTask", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(rp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.RemoveTask(ctx, rp1) } func (a *adminHandler) ResendReplicationTasks(ctx context.Context, rp1 *types.ResendReplicationTasksRequest) (err error) { attr := &authorization.Attributes{ APIName: "ResendReplicationTasks", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(rp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.ResendReplicationTasks(ctx, rp1) } func (a *adminHandler) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest) (err error) { attr := &authorization.Attributes{ APIName: "ResetQueue", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(rp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.ResetQueue(ctx, rp1) } func (a *adminHandler) RespondCrossClusterTasksCompleted(ctx context.Context, rp1 *types.RespondCrossClusterTasksCompletedRequest) (rp2 *types.RespondCrossClusterTasksCompletedResponse, err error) { attr := &authorization.Attributes{ APIName: "RespondCrossClusterTasksCompleted", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(rp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.RespondCrossClusterTasksCompleted(ctx, rp1) } func (a *adminHandler) RestoreDynamicConfig(ctx context.Context, rp1 *types.RestoreDynamicConfigRequest) (err error) { attr := &authorization.Attributes{ APIName: "RestoreDynamicConfig", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(rp1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.RestoreDynamicConfig(ctx, rp1) } func (a *adminHandler) Start() { a.handler.Start() } func (a *adminHandler) Stop() { a.handler.Stop() } func (a *adminHandler) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, up1 *types.UpdateDomainAsyncWorkflowConfiguratonRequest) (up2 *types.UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { attr := &authorization.Attributes{ APIName: "UpdateDomainAsyncWorkflowConfiguraton", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(up1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.UpdateDomainAsyncWorkflowConfiguraton(ctx, up1) } func (a *adminHandler) UpdateDomainIsolationGroups(ctx context.Context, request *types.UpdateDomainIsolationGroupsRequest) (up1 *types.UpdateDomainIsolationGroupsResponse, err error) { attr := &authorization.Attributes{ APIName: "UpdateDomainIsolationGroups", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(request), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.UpdateDomainIsolationGroups(ctx, request) } func (a *adminHandler) UpdateDynamicConfig(ctx context.Context, up1 *types.UpdateDynamicConfigRequest) (err error) { attr := &authorization.Attributes{ APIName: "UpdateDynamicConfig", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(up1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.UpdateDynamicConfig(ctx, up1) } func (a *adminHandler) UpdateGlobalIsolationGroups(ctx context.Context, request *types.UpdateGlobalIsolationGroupsRequest) (up1 *types.UpdateGlobalIsolationGroupsResponse, err error) { attr := &authorization.Attributes{ APIName: "UpdateGlobalIsolationGroups", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(request), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.UpdateGlobalIsolationGroups(ctx, request) } func (a *adminHandler) UpdateTaskListPartitionConfig(ctx context.Context, up1 *types.UpdateTaskListPartitionConfigRequest) (up2 *types.UpdateTaskListPartitionConfigResponse, err error) { attr := &authorization.Attributes{ APIName: "UpdateTaskListPartitionConfig", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(up1), } isAuthorized, err := a.isAuthorized(ctx, attr) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.UpdateTaskListPartitionConfig(ctx, up1) } ================================================ FILE: service/frontend/wrappers/accesscontrolled/api_generated.go ================================================ package accesscontrolled // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/accesscontrolled.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/authorization" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" ) // apiHandler frontend handler wrapper for authentication and authorization type apiHandler struct { handler api.Handler authorizer authorization.Authorizer resource.Resource } // NewAPIHandler creates frontend handler with authentication support func NewAPIHandler(handler api.Handler, resource resource.Resource, authorizer authorization.Authorizer, cfg config.Authorization) api.Handler { if authorizer == nil { var err error authorizer, err = authorization.NewAuthorizer(cfg, resource.GetLogger(), resource.GetDomainCache()) if err != nil { resource.GetLogger().Fatal("Error when initiating the Authorizer", tag.Error(err)) } } return &apiHandler{ handler: handler, authorizer: authorizer, Resource: resource, } } func (a *apiHandler) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest) (cp2 *types.CountWorkflowExecutionsResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendCountWorkflowExecutionsScope, cp1.GetDomain()) attr := &authorization.Attributes{ APIName: "CountWorkflowExecutions", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(cp1), DomainName: cp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.CountWorkflowExecutions(ctx, cp1) } func (a *apiHandler) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest) (err error) { scope := a.GetMetricsClient().Scope(metrics.FrontendDeleteDomainScope) attr := &authorization.Attributes{ APIName: "DeleteDomain", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(dp1), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.DeleteDomain(ctx, dp1) } func (a *apiHandler) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest) (err error) { scope := a.GetMetricsClient().Scope(metrics.FrontendDeprecateDomainScope) attr := &authorization.Attributes{ APIName: "DeprecateDomain", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(dp1), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.DeprecateDomain(ctx, dp1) } func (a *apiHandler) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest) (dp2 *types.DescribeDomainResponse, err error) { scope := a.GetMetricsClient().Scope(metrics.FrontendDescribeDomainScope) attr := &authorization.Attributes{ APIName: "DescribeDomain", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(dp1), DomainName: dp1.GetName(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.DescribeDomain(ctx, dp1) } func (a *apiHandler) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest) (dp2 *types.DescribeTaskListResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendDescribeTaskListScope, dp1.GetDomain()) attr := &authorization.Attributes{ APIName: "DescribeTaskList", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(dp1), DomainName: dp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.DescribeTaskList(ctx, dp1) } func (a *apiHandler) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendDescribeWorkflowExecutionScope, dp1.GetDomain()) attr := &authorization.Attributes{ APIName: "DescribeWorkflowExecution", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(dp1), DomainName: dp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.DescribeWorkflowExecution(ctx, dp1) } func (a *apiHandler) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { return a.handler.DiagnoseWorkflowExecution(ctx, dp1) } func (a *apiHandler) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest) (fp2 *types.FailoverDomainResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendFailoverDomainScope, fp1.GetDomain()) attr := &authorization.Attributes{ APIName: "FailoverDomain", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(fp1), DomainName: fp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.FailoverDomain(ctx, fp1) } func (a *apiHandler) GetClusterInfo(ctx context.Context) (cp1 *types.ClusterInfo, err error) { return a.handler.GetClusterInfo(ctx) } func (a *apiHandler) GetSearchAttributes(ctx context.Context) (gp1 *types.GetSearchAttributesResponse, err error) { return a.handler.GetSearchAttributes(ctx) } func (a *apiHandler) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest) (gp2 *types.GetTaskListsByDomainResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendGetTaskListsByDomainScope, gp1.GetDomain()) attr := &authorization.Attributes{ APIName: "GetTaskListsByDomain", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(gp1), DomainName: gp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetTaskListsByDomain(ctx, gp1) } func (a *apiHandler) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendGetWorkflowExecutionHistoryScope, gp1.GetDomain()) attr := &authorization.Attributes{ APIName: "GetWorkflowExecutionHistory", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(gp1), DomainName: gp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.GetWorkflowExecutionHistory(ctx, gp1) } func (a *apiHandler) Health(ctx context.Context) (hp1 *types.HealthStatus, err error) { return a.handler.Health(ctx) } func (a *apiHandler) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendListArchivedWorkflowExecutionsScope, lp1.GetDomain()) attr := &authorization.Attributes{ APIName: "ListArchivedWorkflowExecutions", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(lp1), DomainName: lp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.ListArchivedWorkflowExecutions(ctx, lp1) } func (a *apiHandler) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendListClosedWorkflowExecutionsScope, lp1.GetDomain()) attr := &authorization.Attributes{ APIName: "ListClosedWorkflowExecutions", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(lp1), DomainName: lp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.ListClosedWorkflowExecutions(ctx, lp1) } func (a *apiHandler) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest) (lp2 *types.ListDomainsResponse, err error) { return a.handler.ListDomains(ctx, lp1) } func (a *apiHandler) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest) (lp2 *types.ListFailoverHistoryResponse, err error) { return a.handler.ListFailoverHistory(ctx, lp1) } func (a *apiHandler) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendListOpenWorkflowExecutionsScope, lp1.GetDomain()) attr := &authorization.Attributes{ APIName: "ListOpenWorkflowExecutions", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(lp1), DomainName: lp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.ListOpenWorkflowExecutions(ctx, lp1) } func (a *apiHandler) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest) (lp2 *types.ListTaskListPartitionsResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendListTaskListPartitionsScope, lp1.GetDomain()) attr := &authorization.Attributes{ APIName: "ListTaskListPartitions", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(lp1), DomainName: lp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.ListTaskListPartitions(ctx, lp1) } func (a *apiHandler) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest) (lp2 *types.ListWorkflowExecutionsResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendListWorkflowExecutionsScope, lp1.GetDomain()) attr := &authorization.Attributes{ APIName: "ListWorkflowExecutions", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(lp1), DomainName: lp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.ListWorkflowExecutions(ctx, lp1) } func (a *apiHandler) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest) (pp2 *types.PollForActivityTaskResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendPollForActivityTaskScope, pp1.GetDomain()) attr := &authorization.Attributes{ APIName: "PollForActivityTask", Permission: authorization.PermissionProcess, RequestBody: authorization.NewFilteredRequestBody(pp1), DomainName: pp1.GetDomain(), TaskList: pp1.TaskList, } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.PollForActivityTask(ctx, pp1) } func (a *apiHandler) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest) (pp2 *types.PollForDecisionTaskResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendPollForDecisionTaskScope, pp1.GetDomain()) attr := &authorization.Attributes{ APIName: "PollForDecisionTask", Permission: authorization.PermissionProcess, RequestBody: authorization.NewFilteredRequestBody(pp1), DomainName: pp1.GetDomain(), TaskList: pp1.TaskList, } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.PollForDecisionTask(ctx, pp1) } func (a *apiHandler) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest) (qp2 *types.QueryWorkflowResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendQueryWorkflowScope, qp1.GetDomain()) attr := &authorization.Attributes{ APIName: "QueryWorkflow", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(qp1), DomainName: qp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.QueryWorkflow(ctx, qp1) } func (a *apiHandler) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { return a.handler.RecordActivityTaskHeartbeat(ctx, rp1) } func (a *apiHandler) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { return a.handler.RecordActivityTaskHeartbeatByID(ctx, rp1) } func (a *apiHandler) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest) (err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendRefreshWorkflowTasksScope, rp1.GetDomain()) attr := &authorization.Attributes{ APIName: "RefreshWorkflowTasks", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(rp1), DomainName: rp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.RefreshWorkflowTasks(ctx, rp1) } func (a *apiHandler) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest) (err error) { scope := a.GetMetricsClient().Scope(metrics.FrontendRegisterDomainScope) attr := &authorization.Attributes{ APIName: "RegisterDomain", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(rp1), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.RegisterDomain(ctx, rp1) } func (a *apiHandler) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest) (err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendRequestCancelWorkflowExecutionScope, rp1.GetDomain()) attr := &authorization.Attributes{ APIName: "RequestCancelWorkflowExecution", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(rp1), DomainName: rp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.RequestCancelWorkflowExecution(ctx, rp1) } func (a *apiHandler) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest) (rp2 *types.ResetStickyTaskListResponse, err error) { return a.handler.ResetStickyTaskList(ctx, rp1) } func (a *apiHandler) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest) (rp2 *types.ResetWorkflowExecutionResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendResetWorkflowExecutionScope, rp1.GetDomain()) attr := &authorization.Attributes{ APIName: "ResetWorkflowExecution", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(rp1), DomainName: rp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.ResetWorkflowExecution(ctx, rp1) } func (a *apiHandler) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest) (err error) { return a.handler.RespondActivityTaskCanceled(ctx, rp1) } func (a *apiHandler) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest) (err error) { return a.handler.RespondActivityTaskCanceledByID(ctx, rp1) } func (a *apiHandler) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest) (err error) { return a.handler.RespondActivityTaskCompleted(ctx, rp1) } func (a *apiHandler) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest) (err error) { return a.handler.RespondActivityTaskCompletedByID(ctx, rp1) } func (a *apiHandler) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest) (err error) { return a.handler.RespondActivityTaskFailed(ctx, rp1) } func (a *apiHandler) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest) (err error) { return a.handler.RespondActivityTaskFailedByID(ctx, rp1) } func (a *apiHandler) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { return a.handler.RespondDecisionTaskCompleted(ctx, rp1) } func (a *apiHandler) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest) (err error) { return a.handler.RespondDecisionTaskFailed(ctx, rp1) } func (a *apiHandler) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest) (err error) { return a.handler.RespondQueryTaskCompleted(ctx, rp1) } func (a *apiHandler) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest) (rp2 *types.RestartWorkflowExecutionResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendRestartWorkflowExecutionScope, rp1.GetDomain()) attr := &authorization.Attributes{ APIName: "RestartWorkflowExecution", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(rp1), DomainName: rp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.RestartWorkflowExecution(ctx, rp1) } func (a *apiHandler) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest) (lp2 *types.ListWorkflowExecutionsResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendScanWorkflowExecutionsScope, lp1.GetDomain()) attr := &authorization.Attributes{ APIName: "ScanWorkflowExecutions", Permission: authorization.PermissionRead, RequestBody: authorization.NewFilteredRequestBody(lp1), DomainName: lp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.ScanWorkflowExecutions(ctx, lp1) } func (a *apiHandler) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest) (sp2 *types.StartWorkflowExecutionResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendSignalWithStartWorkflowExecutionScope, sp1.GetDomain()) attr := &authorization.Attributes{ APIName: "SignalWithStartWorkflowExecution", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(sp1), DomainName: sp1.GetDomain(), WorkflowType: sp1.WorkflowType, TaskList: sp1.TaskList, } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.SignalWithStartWorkflowExecution(ctx, sp1) } func (a *apiHandler) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendSignalWithStartWorkflowExecutionAsyncScope, sp1.GetDomain()) attr := &authorization.Attributes{ APIName: "SignalWithStartWorkflowExecutionAsync", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(sp1), DomainName: sp1.GetDomain(), WorkflowType: sp1.WorkflowType, TaskList: sp1.TaskList, } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.SignalWithStartWorkflowExecutionAsync(ctx, sp1) } func (a *apiHandler) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest) (err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendSignalWorkflowExecutionScope, sp1.GetDomain()) attr := &authorization.Attributes{ APIName: "SignalWorkflowExecution", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(sp1), DomainName: sp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.SignalWorkflowExecution(ctx, sp1) } func (a *apiHandler) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest) (sp2 *types.StartWorkflowExecutionResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendStartWorkflowExecutionScope, sp1.GetDomain()) attr := &authorization.Attributes{ APIName: "StartWorkflowExecution", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(sp1), DomainName: sp1.GetDomain(), WorkflowType: sp1.WorkflowType, TaskList: sp1.TaskList, } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.StartWorkflowExecution(ctx, sp1) } func (a *apiHandler) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendStartWorkflowExecutionAsyncScope, sp1.GetDomain()) attr := &authorization.Attributes{ APIName: "StartWorkflowExecutionAsync", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(sp1), DomainName: sp1.GetDomain(), WorkflowType: sp1.WorkflowType, TaskList: sp1.TaskList, } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.StartWorkflowExecutionAsync(ctx, sp1) } func (a *apiHandler) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest) (err error) { scope := a.getMetricsScopeWithDomain(metrics.FrontendTerminateWorkflowExecutionScope, tp1.GetDomain()) attr := &authorization.Attributes{ APIName: "TerminateWorkflowExecution", Permission: authorization.PermissionWrite, RequestBody: authorization.NewFilteredRequestBody(tp1), DomainName: tp1.GetDomain(), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return err } if !isAuthorized { return errUnauthorized } return a.handler.TerminateWorkflowExecution(ctx, tp1) } func (a *apiHandler) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest) (up2 *types.UpdateDomainResponse, err error) { scope := a.GetMetricsClient().Scope(metrics.FrontendUpdateDomainScope) attr := &authorization.Attributes{ APIName: "UpdateDomain", Permission: authorization.PermissionAdmin, RequestBody: authorization.NewFilteredRequestBody(up1), } isAuthorized, err := a.isAuthorized(ctx, attr, scope) if err != nil { return nil, err } if !isAuthorized { return nil, errUnauthorized } return a.handler.UpdateDomain(ctx, up1) } ================================================ FILE: service/frontend/wrappers/clusterredirection/api_generated.go ================================================ package clusterredirection // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/clusterredirection.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" frontendcfg "github.com/uber/cadence/service/frontend/config" ) type ( // ClusterRedirectionHandlerImpl is simple wrapper over frontend service, doing redirection based on policy for global domains not being active in current cluster clusterRedirectionHandler struct { resource.Resource currentClusterName string redirectionPolicy ClusterRedirectionPolicy tokenSerializer common.TaskTokenSerializer domainCache cache.DomainCache config *frontendcfg.Config frontendHandler api.Handler callOptions []yarpc.CallOption } ) // NewAPIHandler creates a frontend handler to handle cluster redirection for global domains not being active in current cluster func NewAPIHandler( wfHandler api.Handler, resource resource.Resource, config *frontendcfg.Config, policy config.ClusterRedirectionPolicy, ) api.Handler { dcRedirectionPolicy := RedirectionPolicyGenerator( resource.GetClusterMetadata(), config, policy, resource.GetLogger(), resource.GetActiveClusterManager(), resource.GetMetricsClient(), ) return &clusterRedirectionHandler{ Resource: resource, currentClusterName: resource.GetClusterMetadata().GetCurrentClusterName(), domainCache: resource.GetDomainCache(), config: config, redirectionPolicy: dcRedirectionPolicy, tokenSerializer: common.NewJSONTaskTokenSerializer(), frontendHandler: wfHandler, callOptions: []yarpc.CallOption{yarpc.WithHeader(common.AutoforwardingClusterHeaderName, resource.GetClusterMetadata().GetCurrentClusterName())}, } } func (handler *clusterRedirectionHandler) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest) (cp2 *types.CountWorkflowExecutionsResponse, err error) { var ( apiName = "CountWorkflowExecutions" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionCountWorkflowExecutionsScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(cp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: cp2, err = handler.frontendHandler.CountWorkflowExecutions(ctx, cp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } cp2, err = remoteClient.CountWorkflowExecutions(ctx, cp1, handler.callOptions...) } return err }) return cp2, err } func (handler *clusterRedirectionHandler) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest) (err error) { return handler.frontendHandler.DeleteDomain(ctx, dp1) } func (handler *clusterRedirectionHandler) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest) (err error) { return handler.frontendHandler.DeprecateDomain(ctx, dp1) } func (handler *clusterRedirectionHandler) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest) (dp2 *types.DescribeDomainResponse, err error) { return handler.frontendHandler.DescribeDomain(ctx, dp1) } func (handler *clusterRedirectionHandler) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest) (dp2 *types.DescribeTaskListResponse, err error) { var ( apiName = "DescribeTaskList" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionDescribeTaskListScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(dp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: dp2, err = handler.frontendHandler.DescribeTaskList(ctx, dp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } dp2, err = remoteClient.DescribeTaskList(ctx, dp1, handler.callOptions...) } return err }) return dp2, err } func (handler *clusterRedirectionHandler) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { var ( apiName = "DescribeWorkflowExecution" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) // Only autoforward strong consistent queries, this is done for two reasons: // 1. Query is meant to be fast, autoforwarding all queries will increase latency. // 2. If eventual consistency was requested then the results from running out of local dc will be fine. if dp1.GetQueryConsistencyLevel() == types.QueryConsistencyLevelStrong { requestedConsistencyLevel = types.QueryConsistencyLevelStrong } var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionDescribeWorkflowExecutionScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(dp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: dp2, err = handler.frontendHandler.DescribeWorkflowExecution(ctx, dp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } dp2, err = remoteClient.DescribeWorkflowExecution(ctx, dp1, handler.callOptions...) } return err }) return dp2, err } func (handler *clusterRedirectionHandler) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { return handler.frontendHandler.DiagnoseWorkflowExecution(ctx, dp1) } func (handler *clusterRedirectionHandler) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest) (fp2 *types.FailoverDomainResponse, err error) { return handler.frontendHandler.FailoverDomain(ctx, fp1) } func (handler *clusterRedirectionHandler) GetClusterInfo(ctx context.Context) (cp1 *types.ClusterInfo, err error) { return handler.frontendHandler.GetClusterInfo(ctx) } func (handler *clusterRedirectionHandler) GetSearchAttributes(ctx context.Context) (gp1 *types.GetSearchAttributesResponse, err error) { return handler.frontendHandler.GetSearchAttributes(ctx) } func (handler *clusterRedirectionHandler) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest) (gp2 *types.GetTaskListsByDomainResponse, err error) { var ( apiName = "GetTaskListsByDomain" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionGetTaskListsByDomainScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(gp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: gp2, err = handler.frontendHandler.GetTaskListsByDomain(ctx, gp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } gp2, err = remoteClient.GetTaskListsByDomain(ctx, gp1, handler.callOptions...) } return err }) return gp2, err } func (handler *clusterRedirectionHandler) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { var ( apiName = "GetWorkflowExecutionHistory" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) // Only autoforward strong consistent queries, this is done for two reasons: // 1. Query is meant to be fast, autoforwarding all queries will increase latency. // 2. If eventual consistency was requested then the results from running out of local dc will be fine. if gp1.GetQueryConsistencyLevel() == types.QueryConsistencyLevelStrong { requestedConsistencyLevel = types.QueryConsistencyLevelStrong } var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionGetWorkflowExecutionHistoryScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(gp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution workflowExecution = gp1.GetWorkflowExecution() err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: gp2, err = handler.frontendHandler.GetWorkflowExecutionHistory(ctx, gp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } gp2, err = remoteClient.GetWorkflowExecutionHistory(ctx, gp1, handler.callOptions...) } return err }) return gp2, err } func (handler *clusterRedirectionHandler) Health(ctx context.Context) (hp1 *types.HealthStatus, err error) { return handler.frontendHandler.Health(ctx) } func (handler *clusterRedirectionHandler) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { var ( apiName = "ListArchivedWorkflowExecutions" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionListArchivedWorkflowExecutionsScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(lp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: lp2, err = handler.frontendHandler.ListArchivedWorkflowExecutions(ctx, lp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } lp2, err = remoteClient.ListArchivedWorkflowExecutions(ctx, lp1, handler.callOptions...) } return err }) return lp2, err } func (handler *clusterRedirectionHandler) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { var ( apiName = "ListClosedWorkflowExecutions" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionListClosedWorkflowExecutionsScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(lp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: lp2, err = handler.frontendHandler.ListClosedWorkflowExecutions(ctx, lp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } lp2, err = remoteClient.ListClosedWorkflowExecutions(ctx, lp1, handler.callOptions...) } return err }) return lp2, err } func (handler *clusterRedirectionHandler) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest) (lp2 *types.ListDomainsResponse, err error) { return handler.frontendHandler.ListDomains(ctx, lp1) } func (handler *clusterRedirectionHandler) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest) (lp2 *types.ListFailoverHistoryResponse, err error) { return handler.frontendHandler.ListFailoverHistory(ctx, lp1) } func (handler *clusterRedirectionHandler) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { var ( apiName = "ListOpenWorkflowExecutions" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionListOpenWorkflowExecutionsScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(lp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: lp2, err = handler.frontendHandler.ListOpenWorkflowExecutions(ctx, lp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } lp2, err = remoteClient.ListOpenWorkflowExecutions(ctx, lp1, handler.callOptions...) } return err }) return lp2, err } func (handler *clusterRedirectionHandler) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest) (lp2 *types.ListTaskListPartitionsResponse, err error) { var ( apiName = "ListTaskListPartitions" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionListTaskListPartitionsScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(lp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: lp2, err = handler.frontendHandler.ListTaskListPartitions(ctx, lp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } lp2, err = remoteClient.ListTaskListPartitions(ctx, lp1, handler.callOptions...) } return err }) return lp2, err } func (handler *clusterRedirectionHandler) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest) (lp2 *types.ListWorkflowExecutionsResponse, err error) { var ( apiName = "ListWorkflowExecutions" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionListWorkflowExecutionsScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(lp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: lp2, err = handler.frontendHandler.ListWorkflowExecutions(ctx, lp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } lp2, err = remoteClient.ListWorkflowExecutions(ctx, lp1, handler.callOptions...) } return err }) return lp2, err } func (handler *clusterRedirectionHandler) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest) (pp2 *types.PollForActivityTaskResponse, err error) { var ( apiName = "PollForActivityTask" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionPollForActivityTaskScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(pp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: pp2, err = handler.frontendHandler.PollForActivityTask(ctx, pp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } pp2, err = remoteClient.PollForActivityTask(ctx, pp1, handler.callOptions...) } return err }) return pp2, err } func (handler *clusterRedirectionHandler) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest) (pp2 *types.PollForDecisionTaskResponse, err error) { var ( apiName = "PollForDecisionTask" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionPollForDecisionTaskScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(pp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: pp2, err = handler.frontendHandler.PollForDecisionTask(ctx, pp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } pp2, err = remoteClient.PollForDecisionTask(ctx, pp1, handler.callOptions...) } return err }) return pp2, err } func (handler *clusterRedirectionHandler) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest) (qp2 *types.QueryWorkflowResponse, err error) { var ( apiName = "QueryWorkflow" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) // Only autoforward strong consistent queries, this is done for two reasons: // 1. Query is meant to be fast, autoforwarding all queries will increase latency. // 2. If eventual consistency was requested then the results from running out of local dc will be fine. if qp1.GetQueryConsistencyLevel() == types.QueryConsistencyLevelStrong { requestedConsistencyLevel = types.QueryConsistencyLevelStrong } var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionQueryWorkflowScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(qp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: qp2, err = handler.frontendHandler.QueryWorkflow(ctx, qp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } qp2, err = remoteClient.QueryWorkflow(ctx, qp1, handler.callOptions...) } return err }) return qp2, err } func (handler *clusterRedirectionHandler) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { var ( apiName = "RecordActivityTaskHeartbeat" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRecordActivityTaskHeartbeatScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() var idGetter domainIDGetter idGetter, err = handler.tokenSerializer.Deserialize(rp1.TaskToken) if err == nil { domainEntry, err = handler.domainCache.GetDomainByID(idGetter.GetDomainID()) } if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: rp2, err = handler.frontendHandler.RecordActivityTaskHeartbeat(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } rp2, err = remoteClient.RecordActivityTaskHeartbeat(ctx, rp1, handler.callOptions...) } return err }) return rp2, err } func (handler *clusterRedirectionHandler) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { var ( apiName = "RecordActivityTaskHeartbeatByID" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRecordActivityTaskHeartbeatByIDScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(rp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: rp2, err = handler.frontendHandler.RecordActivityTaskHeartbeatByID(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } rp2, err = remoteClient.RecordActivityTaskHeartbeatByID(ctx, rp1, handler.callOptions...) } return err }) return rp2, err } func (handler *clusterRedirectionHandler) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest) (err error) { var ( apiName = "RefreshWorkflowTasks" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRefreshWorkflowTasksScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(rp1.Domain) if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.RefreshWorkflowTasks(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.RefreshWorkflowTasks(ctx, rp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest) (err error) { return handler.frontendHandler.RegisterDomain(ctx, rp1) } func (handler *clusterRedirectionHandler) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest) (err error) { var ( apiName = "RequestCancelWorkflowExecution" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRequestCancelWorkflowExecutionScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(rp1.Domain) if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution workflowExecution = rp1.GetWorkflowExecution() err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.RequestCancelWorkflowExecution(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.RequestCancelWorkflowExecution(ctx, rp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest) (rp2 *types.ResetStickyTaskListResponse, err error) { var ( apiName = "ResetStickyTaskList" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionResetStickyTaskListScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(rp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: rp2, err = handler.frontendHandler.ResetStickyTaskList(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } rp2, err = remoteClient.ResetStickyTaskList(ctx, rp1, handler.callOptions...) } return err }) return rp2, err } func (handler *clusterRedirectionHandler) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest) (rp2 *types.ResetWorkflowExecutionResponse, err error) { var ( apiName = "ResetWorkflowExecution" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionResetWorkflowExecutionScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(rp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution workflowExecution = rp1.GetWorkflowExecution() err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: rp2, err = handler.frontendHandler.ResetWorkflowExecution(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } rp2, err = remoteClient.ResetWorkflowExecution(ctx, rp1, handler.callOptions...) } return err }) return rp2, err } func (handler *clusterRedirectionHandler) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest) (err error) { var ( apiName = "RespondActivityTaskCanceled" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRespondActivityTaskCanceledScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() var idGetter domainIDGetter idGetter, err = handler.tokenSerializer.Deserialize(rp1.TaskToken) if err == nil { domainEntry, err = handler.domainCache.GetDomainByID(idGetter.GetDomainID()) } if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.RespondActivityTaskCanceled(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.RespondActivityTaskCanceled(ctx, rp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest) (err error) { var ( apiName = "RespondActivityTaskCanceledByID" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRespondActivityTaskCanceledByIDScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(rp1.Domain) if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.RespondActivityTaskCanceledByID(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.RespondActivityTaskCanceledByID(ctx, rp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest) (err error) { var ( apiName = "RespondActivityTaskCompleted" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRespondActivityTaskCompletedScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() var idGetter domainIDGetter idGetter, err = handler.tokenSerializer.Deserialize(rp1.TaskToken) if err == nil { domainEntry, err = handler.domainCache.GetDomainByID(idGetter.GetDomainID()) } if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.RespondActivityTaskCompleted(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.RespondActivityTaskCompleted(ctx, rp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest) (err error) { var ( apiName = "RespondActivityTaskCompletedByID" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRespondActivityTaskCompletedByIDScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(rp1.Domain) if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.RespondActivityTaskCompletedByID(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.RespondActivityTaskCompletedByID(ctx, rp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest) (err error) { var ( apiName = "RespondActivityTaskFailed" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRespondActivityTaskFailedScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() var idGetter domainIDGetter idGetter, err = handler.tokenSerializer.Deserialize(rp1.TaskToken) if err == nil { domainEntry, err = handler.domainCache.GetDomainByID(idGetter.GetDomainID()) } if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.RespondActivityTaskFailed(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.RespondActivityTaskFailed(ctx, rp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest) (err error) { var ( apiName = "RespondActivityTaskFailedByID" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRespondActivityTaskFailedByIDScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(rp1.Domain) if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.RespondActivityTaskFailedByID(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.RespondActivityTaskFailedByID(ctx, rp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { var ( apiName = "RespondDecisionTaskCompleted" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRespondDecisionTaskCompletedScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() var idGetter domainIDGetter idGetter, err = handler.tokenSerializer.Deserialize(rp1.TaskToken) if err == nil { domainEntry, err = handler.domainCache.GetDomainByID(idGetter.GetDomainID()) } if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: rp2, err = handler.frontendHandler.RespondDecisionTaskCompleted(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } rp2, err = remoteClient.RespondDecisionTaskCompleted(ctx, rp1, handler.callOptions...) } return err }) return rp2, err } func (handler *clusterRedirectionHandler) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest) (err error) { var ( apiName = "RespondDecisionTaskFailed" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRespondDecisionTaskFailedScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() var idGetter domainIDGetter idGetter, err = handler.tokenSerializer.Deserialize(rp1.TaskToken) if err == nil { domainEntry, err = handler.domainCache.GetDomainByID(idGetter.GetDomainID()) } if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.RespondDecisionTaskFailed(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.RespondDecisionTaskFailed(ctx, rp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest) (err error) { var ( apiName = "RespondQueryTaskCompleted" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRespondQueryTaskCompletedScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() var idGetter domainIDGetter idGetter, err = handler.tokenSerializer.DeserializeQueryTaskToken(rp1.TaskToken) if err == nil { domainEntry, err = handler.domainCache.GetDomainByID(idGetter.GetDomainID()) } if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.RespondQueryTaskCompleted(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.RespondQueryTaskCompleted(ctx, rp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest) (rp2 *types.RestartWorkflowExecutionResponse, err error) { var ( apiName = "RestartWorkflowExecution" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionRestartWorkflowExecutionScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(rp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution workflowExecution = rp1.GetWorkflowExecution() err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: rp2, err = handler.frontendHandler.RestartWorkflowExecution(ctx, rp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } rp2, err = remoteClient.RestartWorkflowExecution(ctx, rp1, handler.callOptions...) } return err }) return rp2, err } func (handler *clusterRedirectionHandler) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest) (lp2 *types.ListWorkflowExecutionsResponse, err error) { var ( apiName = "ScanWorkflowExecutions" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionScanWorkflowExecutionsScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(lp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: lp2, err = handler.frontendHandler.ScanWorkflowExecutions(ctx, lp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } lp2, err = remoteClient.ScanWorkflowExecutions(ctx, lp1, handler.callOptions...) } return err }) return lp2, err } func (handler *clusterRedirectionHandler) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest) (sp2 *types.StartWorkflowExecutionResponse, err error) { var ( apiName = "SignalWithStartWorkflowExecution" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionSignalWithStartWorkflowExecutionScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(sp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution if !handler.config.EnableActiveClusterSelectionPolicyInStartWorkflow(domainEntry.GetInfo().Name) { sp1.ActiveClusterSelectionPolicy = nil } actClSelPolicyForNewWF = sp1.ActiveClusterSelectionPolicy workflowExecution = &types.WorkflowExecution{ WorkflowID: sp1.GetWorkflowID(), } err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: sp2, err = handler.frontendHandler.SignalWithStartWorkflowExecution(ctx, sp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } sp2, err = remoteClient.SignalWithStartWorkflowExecution(ctx, sp1, handler.callOptions...) } return err }) return sp2, err } func (handler *clusterRedirectionHandler) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { var ( apiName = "SignalWithStartWorkflowExecutionAsync" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionSignalWithStartWorkflowExecutionAsyncScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(sp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution if !handler.config.EnableActiveClusterSelectionPolicyInStartWorkflow(domainEntry.GetInfo().Name) { sp1.ActiveClusterSelectionPolicy = nil } actClSelPolicyForNewWF = sp1.ActiveClusterSelectionPolicy err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: sp2, err = handler.frontendHandler.SignalWithStartWorkflowExecutionAsync(ctx, sp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } sp2, err = remoteClient.SignalWithStartWorkflowExecutionAsync(ctx, sp1, handler.callOptions...) } return err }) return sp2, err } func (handler *clusterRedirectionHandler) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest) (err error) { var ( apiName = "SignalWorkflowExecution" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionSignalWorkflowExecutionScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(sp1.Domain) if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution workflowExecution = sp1.GetWorkflowExecution() err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.SignalWorkflowExecution(ctx, sp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.SignalWorkflowExecution(ctx, sp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest) (sp2 *types.StartWorkflowExecutionResponse, err error) { var ( apiName = "StartWorkflowExecution" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionStartWorkflowExecutionScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(sp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution if !handler.config.EnableActiveClusterSelectionPolicyInStartWorkflow(domainEntry.GetInfo().Name) { sp1.ActiveClusterSelectionPolicy = nil } actClSelPolicyForNewWF = sp1.ActiveClusterSelectionPolicy err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: sp2, err = handler.frontendHandler.StartWorkflowExecution(ctx, sp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } sp2, err = remoteClient.StartWorkflowExecution(ctx, sp1, handler.callOptions...) } return err }) return sp2, err } func (handler *clusterRedirectionHandler) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { var ( apiName = "StartWorkflowExecutionAsync" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionStartWorkflowExecutionAsyncScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(sp1.Domain) if err != nil { return nil, err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution if !handler.config.EnableActiveClusterSelectionPolicyInStartWorkflow(domainEntry.GetInfo().Name) { sp1.ActiveClusterSelectionPolicy = nil } actClSelPolicyForNewWF = sp1.ActiveClusterSelectionPolicy err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: sp2, err = handler.frontendHandler.StartWorkflowExecutionAsync(ctx, sp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } sp2, err = remoteClient.StartWorkflowExecutionAsync(ctx, sp1, handler.callOptions...) } return err }) return sp2, err } func (handler *clusterRedirectionHandler) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest) (err error) { var ( apiName = "TerminateWorkflowExecution" cluster string requestedConsistencyLevel types.QueryConsistencyLevel = getRequestedConsistencyLevelFromContext(ctx) ) var domainEntry *cache.DomainCacheEntry scope, startTime := handler.beforeCall(metrics.DCRedirectionTerminateWorkflowExecutionScope) defer func() { handler.afterCall(recover(), scope, startTime, domainEntry, cluster, &err) }() domainEntry, err = handler.domainCache.GetDomain(tp1.Domain) if err != nil { return err } var actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy var workflowExecution *types.WorkflowExecution workflowExecution = tp1.GetWorkflowExecution() err = handler.redirectionPolicy.Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, func(targetDC string) error { cluster = targetDC switch { case targetDC == handler.currentClusterName: err = handler.frontendHandler.TerminateWorkflowExecution(ctx, tp1) default: remoteClient, clientErr := handler.GetRemoteFrontendClient(targetDC) if clientErr != nil { return clientErr } err = remoteClient.TerminateWorkflowExecution(ctx, tp1, handler.callOptions...) } return err }) return err } func (handler *clusterRedirectionHandler) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest) (up2 *types.UpdateDomainResponse, err error) { return handler.frontendHandler.UpdateDomain(ctx, up1) } ================================================ FILE: service/frontend/wrappers/clusterredirection/api_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package clusterredirection import ( "context" "errors" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" frontendcfg "github.com/uber/cadence/service/frontend/config" ) type ( clusterRedirectionHandlerSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockResource *resource.Test mockFrontendHandler *api.MockHandler mockRemoteFrontendClient *frontend.MockClient mockClusterRedirectionPolicy *MockClusterRedirectionPolicy domainName string domainID string domainCacheEntry *cache.DomainCacheEntry currentClusterName string alternativeClusterName string config *frontendcfg.Config handler *clusterRedirectionHandler } ) func TestForwardingPolicyV2ContainsV1(t *testing.T) { require.NotEqual(t, selectedAPIsForwardingRedirectionPolicyAPIAllowlistV2, selectedAPIsForwardingRedirectionPolicyAPIAllowlist) for k := range selectedAPIsForwardingRedirectionPolicyAPIAllowlist { _, ok := selectedAPIsForwardingRedirectionPolicyAPIAllowlistV2[k] require.True(t, ok, "v2 does not contain a key that is in v1: %v", k) } } func TestClusterRedirectionHandlerSuite(t *testing.T) { s := new(clusterRedirectionHandlerSuite) suite.Run(t, s) } func (s *clusterRedirectionHandlerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.domainName = "some random domain name" s.domainID = "some random domain ID" s.currentClusterName = cluster.TestCurrentClusterName s.alternativeClusterName = cluster.TestAlternativeClusterName s.controller = gomock.NewController(s.T()) s.mockClusterRedirectionPolicy = NewMockClusterRedirectionPolicy(s.controller) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.Frontend) s.domainCacheEntry = cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ID: s.domainID, Name: s.domainName}, &persistence.DomainConfig{}, true, nil, 0, nil, 0, 0, 0) s.mockResource.DomainCache.EXPECT().GetDomain(s.domainName).Return(s.domainCacheEntry, nil).AnyTimes() s.mockResource.DomainCache.EXPECT().GetDomainByID(s.domainID).Return(s.domainCacheEntry, nil).AnyTimes() s.mockRemoteFrontendClient = s.mockResource.RemoteFrontendClient s.config = frontendcfg.NewConfig( dynamicconfig.NewCollection( dynamicconfig.NewNopClient(), s.mockResource.GetLogger(), ), 0, false, "hostname", s.mockResource.GetLogger(), ) dh := domain.NewMockHandler(s.controller) frontendHandler := api.NewWorkflowHandler(s.mockResource, s.config, client.NewVersionChecker(), dh) s.mockFrontendHandler = api.NewMockHandler(s.controller) s.handler = NewAPIHandler(frontendHandler, s.mockResource, s.config, config.ClusterRedirectionPolicy{}).(*clusterRedirectionHandler) s.handler.frontendHandler = s.mockFrontendHandler s.handler.redirectionPolicy = s.mockClusterRedirectionPolicy } func (s *clusterRedirectionHandlerSuite) TearDownTest() { s.controller.Finish() s.mockResource.Finish(s.T()) } func (s *clusterRedirectionHandlerSuite) TestDescribeTaskList() { apiName := "DescribeTaskList" ctx := context.Background() req := &types.DescribeTaskListRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().DescribeTaskList(ctx, req).Return(&types.DescribeTaskListResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().DescribeTaskList(ctx, req, s.handler.callOptions).Return(&types.DescribeTaskListResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.DescribeTaskList(ctx, req) s.Nil(err) s.Equal(&types.DescribeTaskListResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestDescribeTaskListError() { apiName := "DescribeTaskList" ctx := context.Background() req := &types.DescribeTaskListRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().DescribeTaskList(ctx, req).Return(nil, errors.New("some error")) err := callFn(s.currentClusterName) s.NotNil(err) return err }). Times(1) resp, err := s.handler.DescribeTaskList(ctx, req) s.ErrorContains(err, "some error") s.Nil(resp) } func (s *clusterRedirectionHandlerSuite) TestDescribeWorkflowExecution() { apiName := "DescribeWorkflowExecution" ctx := context.Background() req := &types.DescribeWorkflowExecutionRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().DescribeWorkflowExecution(ctx, req).Return(&types.DescribeWorkflowExecutionResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().DescribeWorkflowExecution(ctx, req, s.handler.callOptions).Return(&types.DescribeWorkflowExecutionResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.DescribeWorkflowExecution(ctx, req) s.Nil(err) s.Equal(&types.DescribeWorkflowExecutionResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestDescribeWorkflowExecutionStrongConsistency() { apiName := "DescribeWorkflowExecution" ctx := context.Background() req := &types.DescribeWorkflowExecutionRequest{ Domain: s.domainName, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelStrong, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().DescribeWorkflowExecution(ctx, req).Return(&types.DescribeWorkflowExecutionResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().DescribeWorkflowExecution(ctx, req, s.handler.callOptions).Return(&types.DescribeWorkflowExecutionResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.DescribeWorkflowExecution(ctx, req) s.Nil(err) s.Equal(&types.DescribeWorkflowExecutionResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestGetWorkflowExecutionHistory() { apiName := "GetWorkflowExecutionHistory" ctx := context.Background() req := &types.GetWorkflowExecutionHistoryRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().GetWorkflowExecutionHistory(ctx, req).Return(&types.GetWorkflowExecutionHistoryResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().GetWorkflowExecutionHistory(ctx, req, s.handler.callOptions).Return(&types.GetWorkflowExecutionHistoryResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.GetWorkflowExecutionHistory(ctx, req) s.Nil(err) s.Equal(&types.GetWorkflowExecutionHistoryResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestGetWorkflowExecutionHistoryStrongConsistency() { apiName := "GetWorkflowExecutionHistory" ctx := context.Background() req := &types.GetWorkflowExecutionHistoryRequest{ Domain: s.domainName, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), Execution: &types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: "some random run ID", }, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, req.Execution, nil, apiName, types.QueryConsistencyLevelStrong, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().GetWorkflowExecutionHistory(ctx, req).Return(&types.GetWorkflowExecutionHistoryResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().GetWorkflowExecutionHistory(ctx, req, s.handler.callOptions).Return(&types.GetWorkflowExecutionHistoryResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.GetWorkflowExecutionHistory(ctx, req) s.Nil(err) s.Equal(&types.GetWorkflowExecutionHistoryResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestListArchivedWorkflowExecutions() { apiName := "ListArchivedWorkflowExecutions" ctx := context.Background() req := &types.ListArchivedWorkflowExecutionsRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().ListArchivedWorkflowExecutions(ctx, req).Return(&types.ListArchivedWorkflowExecutionsResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().ListArchivedWorkflowExecutions(ctx, req, s.handler.callOptions).Return(&types.ListArchivedWorkflowExecutionsResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.ListArchivedWorkflowExecutions(ctx, req) s.Nil(err) s.Equal(&types.ListArchivedWorkflowExecutionsResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestListClosedWorkflowExecutions() { apiName := "ListClosedWorkflowExecutions" ctx := context.Background() req := &types.ListClosedWorkflowExecutionsRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().ListClosedWorkflowExecutions(ctx, req).Return(&types.ListClosedWorkflowExecutionsResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().ListClosedWorkflowExecutions(ctx, req, s.handler.callOptions).Return(&types.ListClosedWorkflowExecutionsResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.ListClosedWorkflowExecutions(ctx, req) s.Nil(err) s.Equal(&types.ListClosedWorkflowExecutionsResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestListOpenWorkflowExecutions() { apiName := "ListOpenWorkflowExecutions" ctx := context.Background() req := &types.ListOpenWorkflowExecutionsRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().ListOpenWorkflowExecutions(ctx, req).Return(&types.ListOpenWorkflowExecutionsResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().ListOpenWorkflowExecutions(ctx, req, s.handler.callOptions).Return(&types.ListOpenWorkflowExecutionsResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.ListOpenWorkflowExecutions(ctx, req) s.Nil(err) s.Equal(&types.ListOpenWorkflowExecutionsResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestListWorkflowExecutions() { apiName := "ListWorkflowExecutions" ctx := context.Background() req := &types.ListWorkflowExecutionsRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().ListWorkflowExecutions(ctx, req).Return(&types.ListWorkflowExecutionsResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().ListWorkflowExecutions(ctx, req, s.handler.callOptions).Return(&types.ListWorkflowExecutionsResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.ListWorkflowExecutions(ctx, req) s.Nil(err) s.Equal(&types.ListWorkflowExecutionsResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestScanWorkflowExecutions() { apiName := "ScanWorkflowExecutions" ctx := context.Background() req := &types.ListWorkflowExecutionsRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().ScanWorkflowExecutions(ctx, req).Return(&types.ListWorkflowExecutionsResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().ScanWorkflowExecutions(ctx, req, s.handler.callOptions).Return(&types.ListWorkflowExecutionsResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.ScanWorkflowExecutions(ctx, req) s.Nil(err) s.Equal(&types.ListWorkflowExecutionsResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestCountWorkflowExecutions() { apiName := "CountWorkflowExecutions" ctx := context.Background() req := &types.CountWorkflowExecutionsRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().CountWorkflowExecutions(ctx, req).Return(&types.CountWorkflowExecutionsResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().CountWorkflowExecutions(ctx, req, s.handler.callOptions).Return(&types.CountWorkflowExecutionsResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.CountWorkflowExecutions(ctx, req) s.Nil(err) s.Equal(&types.CountWorkflowExecutionsResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestPollForActivityTask() { apiName := "PollForActivityTask" ctx := context.Background() req := &types.PollForActivityTaskRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().PollForActivityTask(ctx, req).Return(&types.PollForActivityTaskResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().PollForActivityTask(ctx, req, s.handler.callOptions).Return(&types.PollForActivityTaskResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.PollForActivityTask(ctx, req) s.Nil(err) s.Equal(&types.PollForActivityTaskResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestPollForDecisionTask() { apiName := "PollForDecisionTask" ctx := context.Background() req := &types.PollForDecisionTaskRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().PollForDecisionTask(ctx, req).Return(&types.PollForDecisionTaskResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().PollForDecisionTask(ctx, req, s.handler.callOptions).Return(&types.PollForDecisionTaskResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.PollForDecisionTask(ctx, req) s.Nil(err) s.Equal(&types.PollForDecisionTaskResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestQueryWorkflow() { apiName := "QueryWorkflow" ctx := context.Background() req := &types.QueryWorkflowRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().QueryWorkflow(ctx, req).Return(&types.QueryWorkflowResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().QueryWorkflow(ctx, req, s.handler.callOptions).Return(&types.QueryWorkflowResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.QueryWorkflow(ctx, req) s.Nil(err) s.Equal(&types.QueryWorkflowResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestQueryWorkflowWithStrongConsistency() { apiName := "QueryWorkflow" ctx := context.Background() req := &types.QueryWorkflowRequest{ Domain: s.domainName, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelStrong, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().QueryWorkflow(ctx, req).Return(&types.QueryWorkflowResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().QueryWorkflow(ctx, req, s.handler.callOptions).Return(&types.QueryWorkflowResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.QueryWorkflow(ctx, req) s.Nil(err) s.Equal(&types.QueryWorkflowResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestRecordActivityTaskHeartbeat() { apiName := "RecordActivityTaskHeartbeat" ctx := context.Background() token, err := s.handler.tokenSerializer.Serialize(&common.TaskToken{ DomainID: s.domainID, }) s.Nil(err) req := &types.RecordActivityTaskHeartbeatRequest{ TaskToken: token, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RecordActivityTaskHeartbeat(ctx, req).Return(&types.RecordActivityTaskHeartbeatResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RecordActivityTaskHeartbeat(ctx, req, s.handler.callOptions).Return(&types.RecordActivityTaskHeartbeatResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.RecordActivityTaskHeartbeat(ctx, req) s.Nil(err) s.Equal(&types.RecordActivityTaskHeartbeatResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestRecordActivityTaskHeartbeatByID() { apiName := "RecordActivityTaskHeartbeatByID" ctx := context.Background() req := &types.RecordActivityTaskHeartbeatByIDRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RecordActivityTaskHeartbeatByID(ctx, req).Return(&types.RecordActivityTaskHeartbeatResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RecordActivityTaskHeartbeatByID(ctx, req, s.handler.callOptions).Return(&types.RecordActivityTaskHeartbeatResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.RecordActivityTaskHeartbeatByID(ctx, req) s.Nil(err) s.Equal(&types.RecordActivityTaskHeartbeatResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestRequestCancelWorkflowExecution() { apiName := "RequestCancelWorkflowExecution" ctx := context.Background() req := &types.RequestCancelWorkflowExecutionRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RequestCancelWorkflowExecution(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RequestCancelWorkflowExecution(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err := s.handler.RequestCancelWorkflowExecution(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestResetStickyTaskList() { apiName := "ResetStickyTaskList" ctx := context.Background() req := &types.ResetStickyTaskListRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().ResetStickyTaskList(ctx, req).Return(&types.ResetStickyTaskListResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().ResetStickyTaskList(ctx, req, s.handler.callOptions).Return(&types.ResetStickyTaskListResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.ResetStickyTaskList(ctx, req) s.Nil(err) s.Equal(&types.ResetStickyTaskListResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestResetWorkflowExecution() { apiName := "ResetWorkflowExecution" ctx := context.Background() req := &types.ResetWorkflowExecutionRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().ResetWorkflowExecution(ctx, req).Return(&types.ResetWorkflowExecutionResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().ResetWorkflowExecution(ctx, req, s.handler.callOptions).Return(&types.ResetWorkflowExecutionResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.ResetWorkflowExecution(ctx, req) s.Nil(err) s.Equal(&types.ResetWorkflowExecutionResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestRespondActivityTaskCanceled() { apiName := "RespondActivityTaskCanceled" ctx := context.Background() token, err := s.handler.tokenSerializer.Serialize(&common.TaskToken{ DomainID: s.domainID, }) s.Nil(err) req := &types.RespondActivityTaskCanceledRequest{ TaskToken: token, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RespondActivityTaskCanceled(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RespondActivityTaskCanceled(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err = s.handler.RespondActivityTaskCanceled(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestRespondActivityTaskCanceledByID() { apiName := "RespondActivityTaskCanceledByID" ctx := context.Background() req := &types.RespondActivityTaskCanceledByIDRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RespondActivityTaskCanceledByID(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RespondActivityTaskCanceledByID(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err := s.handler.RespondActivityTaskCanceledByID(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestRespondActivityTaskCompleted() { apiName := "RespondActivityTaskCompleted" ctx := context.Background() token, err := s.handler.tokenSerializer.Serialize(&common.TaskToken{ DomainID: s.domainID, }) s.Nil(err) req := &types.RespondActivityTaskCompletedRequest{ TaskToken: token, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RespondActivityTaskCompleted(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RespondActivityTaskCompleted(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err = s.handler.RespondActivityTaskCompleted(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestRespondActivityTaskCompletedByID() { apiName := "RespondActivityTaskCompletedByID" ctx := context.Background() req := &types.RespondActivityTaskCompletedByIDRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RespondActivityTaskCompletedByID(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RespondActivityTaskCompletedByID(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err := s.handler.RespondActivityTaskCompletedByID(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestRespondActivityTaskFailed() { apiName := "RespondActivityTaskFailed" ctx := context.Background() token, err := s.handler.tokenSerializer.Serialize(&common.TaskToken{ DomainID: s.domainID, }) s.Nil(err) req := &types.RespondActivityTaskFailedRequest{ TaskToken: token, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RespondActivityTaskFailed(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RespondActivityTaskFailed(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err = s.handler.RespondActivityTaskFailed(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestRespondActivityTaskFailedByID() { apiName := "RespondActivityTaskFailedByID" ctx := context.Background() req := &types.RespondActivityTaskFailedByIDRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RespondActivityTaskFailedByID(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RespondActivityTaskFailedByID(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err := s.handler.RespondActivityTaskFailedByID(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestRespondDecisionTaskCompleted() { apiName := "RespondDecisionTaskCompleted" ctx := context.Background() token, err := s.handler.tokenSerializer.Serialize(&common.TaskToken{ DomainID: s.domainID, }) s.Nil(err) req := &types.RespondDecisionTaskCompletedRequest{ TaskToken: token, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RespondDecisionTaskCompleted(ctx, req).Return(&types.RespondDecisionTaskCompletedResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RespondDecisionTaskCompleted(ctx, req, s.handler.callOptions).Return(&types.RespondDecisionTaskCompletedResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.RespondDecisionTaskCompleted(ctx, req) s.Nil(err) s.Equal(&types.RespondDecisionTaskCompletedResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestRespondDecisionTaskFailed() { apiName := "RespondDecisionTaskFailed" ctx := context.Background() token, err := s.handler.tokenSerializer.Serialize(&common.TaskToken{ DomainID: s.domainID, }) s.Nil(err) req := &types.RespondDecisionTaskFailedRequest{ TaskToken: token, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RespondDecisionTaskFailed(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RespondDecisionTaskFailed(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err = s.handler.RespondDecisionTaskFailed(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestRespondQueryTaskCompleted() { apiName := "RespondQueryTaskCompleted" ctx := context.Background() token, err := s.handler.tokenSerializer.SerializeQueryTaskToken(&common.QueryTaskToken{ DomainID: s.domainID, }) s.NoError(err) req := &types.RespondQueryTaskCompletedRequest{ TaskToken: token, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().RespondQueryTaskCompleted(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().RespondQueryTaskCompleted(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err = s.handler.RespondQueryTaskCompleted(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestSignalWithStartWorkflowExecution() { apiName := "SignalWithStartWorkflowExecution" ctx := context.Background() req := &types.SignalWithStartWorkflowExecutionRequest{ Domain: s.domainName, ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "region-a", }, }, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, &types.WorkflowExecution{}, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic req2 := &types.SignalWithStartWorkflowExecutionRequest{ Domain: s.domainName, } s.mockFrontendHandler.EXPECT().SignalWithStartWorkflowExecution(ctx, req2).Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().SignalWithStartWorkflowExecution(ctx, req2, s.handler.callOptions).Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.SignalWithStartWorkflowExecution(ctx, req) s.Nil(err) s.Equal(&types.StartWorkflowExecutionResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestSignalWithStartWorkflowExecutionWithActiveClusterSelectionPolicy() { s.handler.config.EnableActiveClusterSelectionPolicyInStartWorkflow = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) apiName := "SignalWithStartWorkflowExecution" ctx := context.Background() req := &types.SignalWithStartWorkflowExecutionRequest{ Domain: s.domainName, ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "region-a", }, }, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, &types.WorkflowExecution{}, req.ActiveClusterSelectionPolicy, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().SignalWithStartWorkflowExecution(ctx, req).Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().SignalWithStartWorkflowExecution(ctx, req, s.handler.callOptions).Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.SignalWithStartWorkflowExecution(ctx, req) s.Nil(err) s.Equal(&types.StartWorkflowExecutionResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestSignalWorkflowExecution() { apiName := "SignalWorkflowExecution" ctx := context.Background() req := &types.SignalWorkflowExecutionRequest{ Domain: s.domainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, req.WorkflowExecution, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().SignalWorkflowExecution(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().SignalWorkflowExecution(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err := s.handler.SignalWorkflowExecution(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestStartWorkflowExecution() { apiName := "StartWorkflowExecution" ctx := context.Background() req := &types.StartWorkflowExecutionRequest{ Domain: s.domainName, ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "region-b", }, }, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic req2 := &types.StartWorkflowExecutionRequest{ Domain: s.domainName, } s.mockFrontendHandler.EXPECT().StartWorkflowExecution(ctx, req2).Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().StartWorkflowExecution(ctx, req2, s.handler.callOptions).Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.StartWorkflowExecution(ctx, req) s.Nil(err) s.Equal(&types.StartWorkflowExecutionResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestStartWorkflowExecutionWithActiveClusterSelectionPolicy() { s.handler.config.EnableActiveClusterSelectionPolicyInStartWorkflow = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) apiName := "StartWorkflowExecution" ctx := context.Background() req := &types.StartWorkflowExecutionRequest{ Domain: s.domainName, ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "region-b", }, }, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, req.ActiveClusterSelectionPolicy, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().StartWorkflowExecution(ctx, req).Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().StartWorkflowExecution(ctx, req, s.handler.callOptions).Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.StartWorkflowExecution(ctx, req) s.Nil(err) s.Equal(&types.StartWorkflowExecutionResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestTerminateWorkflowExecution() { apiName := "TerminateWorkflowExecution" ctx := context.Background() req := &types.TerminateWorkflowExecutionRequest{ Domain: s.domainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, req.WorkflowExecution, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().TerminateWorkflowExecution(ctx, req).Return(nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().TerminateWorkflowExecution(ctx, req, s.handler.callOptions).Return(nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) err := s.handler.TerminateWorkflowExecution(ctx, req) s.Nil(err) } func (s *clusterRedirectionHandlerSuite) TestListTaskListPartitions() { apiName := "ListTaskListPartitions" req := &types.ListTaskListPartitionsRequest{ Domain: s.domainName, TaskList: &types.TaskList{ Name: "test_tesk_list", Kind: types.TaskListKind(0).Ptr(), }, } ctx := context.Background() s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().ListTaskListPartitions(ctx, req).Return(&types.ListTaskListPartitionsResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().ListTaskListPartitions(ctx, req, s.handler.callOptions).Return(&types.ListTaskListPartitionsResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.ListTaskListPartitions(ctx, req) s.Nil(err) s.Equal(&types.ListTaskListPartitionsResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestGetTaskListsByDomain() { apiName := "GetTaskListsByDomain" ctx := context.Background() req := &types.GetTaskListsByDomainRequest{ Domain: s.domainName, } s.mockClusterRedirectionPolicy.EXPECT().Redirect(ctx, s.domainCacheEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, gomock.Any()). DoAndReturn(func(ctx context.Context, domainCacheEntry *cache.DomainCacheEntry, wfExec *types.WorkflowExecution, selPlcy *types.ActiveClusterSelectionPolicy, apiName string, consistencyLevel types.QueryConsistencyLevel, callFn func(targetDC string) error) error { // validate callFn logic s.mockFrontendHandler.EXPECT().GetTaskListsByDomain(ctx, req).Return(&types.GetTaskListsByDomainResponse{}, nil).Times(1) err := callFn(s.currentClusterName) s.Nil(err) s.mockRemoteFrontendClient.EXPECT().GetTaskListsByDomain(ctx, req, s.handler.callOptions).Return(&types.GetTaskListsByDomainResponse{}, nil).Times(1) err = callFn(s.alternativeClusterName) s.Nil(err) return nil }). Times(1) resp, err := s.handler.GetTaskListsByDomain(ctx, req) s.Nil(err) s.Equal(&types.GetTaskListsByDomainResponse{}, resp) } func (s *clusterRedirectionHandlerSuite) TestGetTaskListsByDomainError() { s.handler.redirectionPolicy = newSelectedOrAllAPIsForwardingPolicy( s.currentClusterName, s.config, true, selectedAPIsForwardingRedirectionPolicyAPIAllowlistV2, "", s.mockResource.GetLogger(), s.mockResource.GetActiveClusterManager(), s.mockResource.GetMetricsClient(), ) ctx := context.Background() req := &types.GetTaskListsByDomainRequest{ Domain: "custom domain name", } domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: "custom domain name"}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: s.alternativeClusterName, }, 1, ) s.mockResource.DomainCache.EXPECT().GetDomain("custom domain name").Return(domainEntry, nil).Times(1) s.mockRemoteFrontendClient.EXPECT().GetTaskListsByDomain(ctx, req, s.handler.callOptions).Return(nil, &types.InternalServiceError{Message: "test error"}).Times(1) resp, err := s.handler.GetTaskListsByDomain(ctx, req) s.Error(err) // the resp is initialized to nil, since inner function is not called s.Nil(resp) } ================================================ FILE: service/frontend/wrappers/clusterredirection/callwrappers.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clusterredirection import ( "time" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type ( domainIDGetter interface { GetDomainID() string } ) func (handler *clusterRedirectionHandler) beforeCall( scope metrics.ScopeIdx, ) (metrics.Scope, time.Time) { return handler.GetMetricsClient().Scope(scope), handler.GetTimeSource().Now() } func (handler *clusterRedirectionHandler) afterCall( recovered interface{}, scope metrics.Scope, startTime time.Time, domainEntry *cache.DomainCacheEntry, cluster string, retError *error, ) { var extraTags []tag.Tag if domainEntry != nil { extraTags = append(extraTags, tag.WorkflowDomainName(domainEntry.GetInfo().Name)) extraTags = append(extraTags, tag.WorkflowDomainID(domainEntry.GetInfo().ID)) } log.CapturePanic(recovered, handler.GetLogger().WithTags(extraTags...), retError) scope = scope.Tagged(metrics.TargetClusterTag(cluster)) scope.IncCounter(metrics.CadenceDcRedirectionClientRequests) scope.RecordTimer(metrics.CadenceDcRedirectionClientLatency, handler.GetTimeSource().Now().Sub(startTime)) if *retError != nil { scope.IncCounter(metrics.CadenceDcRedirectionClientFailures) } } ================================================ FILE: service/frontend/wrappers/clusterredirection/policy.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package clusterredirection import ( "context" "errors" "fmt" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" frontendcfg "github.com/uber/cadence/service/frontend/config" ) //go:generate mockgen -package $GOPACKAGE -destination policy_mock.go -self_package github.com/uber/cadence/service/frontend/wrappers/clusterredirection github.com/uber/cadence/service/frontend/wrappers/clusterredirection ClusterRedirectionPolicy const ( // DCRedirectionPolicyDefault means no redirection DCRedirectionPolicyDefault = "" // DCRedirectionPolicyNoop means no redirection DCRedirectionPolicyNoop = "noop" // DCRedirectionPolicySelectedAPIsForwarding means forwarding the following non-worker APIs based domain // 1. StartWorkflowExecution // 2. SignalWithStartWorkflowExecution // 3. SignalWorkflowExecution // 4. RequestCancelWorkflowExecution // 5. TerminateWorkflowExecution // 6. ResetWorkflow // please also reference selectedAPIsForwardingRedirectionPolicyAPIAllowlist and DCRedirectionPolicySelectedAPIsForwardingV2 DCRedirectionPolicySelectedAPIsForwarding = "selected-apis-forwarding" // DCRedirectionPolicySelectedAPIsForwardingV2 forwards everything in DCRedirectionPolicySelectedAPIsForwarding, // as well as activity completions (sync and async). // This is done because activity results are generally deemed "useful" and relatively costly to re-do (when it is // even possible to redo), but activity workers themselves may be datacenter-specific. // // This will likely replace DCRedirectionPolicySelectedAPIsForwarding soon. // // 1-6. from DCRedirectionPolicySelectedAPIsForwarding // 7. RespondActivityTaskCanceled // 8. RespondActivityTaskCanceledByID // 9. RespondActivityTaskCompleted // 10. RespondActivityTaskCompletedByID // 11. RespondActivityTaskFailed // 12. RespondActivityTaskFailedByID // please also reference selectedAPIsForwardingRedirectionPolicyAPIAllowlistV2 DCRedirectionPolicySelectedAPIsForwardingV2 = "selected-apis-forwarding-v2" // DCRedirectionPolicyAllDomainAPIsForwarding means forwarding all the worker and non-worker APIs based domain, // and falling back to DCRedirectionPolicySelectedAPIsForwarding when the current active cluster is not the // cluster migration target. DCRedirectionPolicyAllDomainAPIsForwarding = "all-domain-apis-forwarding" // DCRedirectionPolicyAllDomainAPIsForwardingV2 means forwarding all the worker and non-worker APIs based domain, // and falling back to DCRedirectionPolicySelectedAPIsForwardingV2 when the current active cluster is not the // cluster migration target. DCRedirectionPolicyAllDomainAPIsForwardingV2 = "all-domain-apis-forwarding-v2" ) type ( // ClusterRedirectionPolicy is a DC redirection policy interface ClusterRedirectionPolicy interface { // Redirect redirects applicable API calls to active cluster based on given parameters and configured forwarding policy. // domainEntry (required): domain cache entry // workflowExecution (optional): workflow execution (only used for existing workflow API calls on active-active domains) // actClSelPolicyForNewWF (optional): active cluster selection policy for new workflow (only used for new workflow API calls on active-active domains) // apiName (required): API name // requestedConsistencyLevel (required): requested consistency level // call (required): function to call the API on the target cluster Redirect( ctx context.Context, domainEntry *cache.DomainCacheEntry, workflowExecution *types.WorkflowExecution, actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy, apiName string, requestedConsistencyLevel types.QueryConsistencyLevel, call func(string) error, ) error } // noopRedirectionPolicy is DC redirection policy which does nothing noopRedirectionPolicy struct { currentClusterName string } // selectedOrAllAPIsForwardingRedirectionPolicy is a DC redirection policy // which (based on domain) forwards selected APIs calls or all domain APIs to active cluster selectedOrAllAPIsForwardingRedirectionPolicy struct { currentClusterName string config *frontendcfg.Config allDomainAPIs bool selectedAPIs map[string]struct{} targetCluster string logger log.Logger activeClusterManager activecluster.Manager metricsClient metrics.Client } ) // selectedAPIsForwardingRedirectionPolicyAPIAllowlist contains a list of non-worker APIs which can be redirected. // This is paired with DCRedirectionPolicySelectedAPIsForwarding - keep both lists up to date. var selectedAPIsForwardingRedirectionPolicyAPIAllowlist = map[string]struct{}{ "StartWorkflowExecution": {}, "SignalWithStartWorkflowExecution": {}, "SignalWorkflowExecution": {}, "RequestCancelWorkflowExecution": {}, "TerminateWorkflowExecution": {}, "ResetWorkflowExecution": {}, } // selectedAPIsForwardingRedirectionPolicyAPIAllowlistV2 contains a list of non-worker APIs which can be redirected. // This is paired with DCRedirectionPolicySelectedAPIsForwardingV2 - keep both lists up to date. var selectedAPIsForwardingRedirectionPolicyAPIAllowlistV2 = map[string]struct{}{ // from selectedAPIsForwardingRedirectionPolicyAPIAllowlist "StartWorkflowExecution": {}, "SignalWithStartWorkflowExecution": {}, "SignalWorkflowExecution": {}, "RequestCancelWorkflowExecution": {}, "TerminateWorkflowExecution": {}, "ResetWorkflowExecution": {}, // additional endpoints "RespondActivityTaskCanceled": {}, "RespondActivityTaskCanceledByID": {}, "RespondActivityTaskCompleted": {}, "RespondActivityTaskCompletedByID": {}, "RespondActivityTaskFailed": {}, "RespondActivityTaskFailedByID": {}, } // allowedAPIsForDeprecatedDomains contains a list of APIs that are allowed to be called on deprecated domains var allowedAPIsForDeprecatedDomains = map[string]struct{}{ "ListWorkflowExecutions": {}, "CountWorkflowExecutions": {}, "ScanWorkflowExecutions": {}, "TerminateWorkflowExecution": {}, } // RedirectionPolicyGenerator generate corresponding redirection policy func RedirectionPolicyGenerator( clusterMetadata cluster.Metadata, config *frontendcfg.Config, policy config.ClusterRedirectionPolicy, logger log.Logger, activeClusterManager activecluster.Manager, metricsClient metrics.Client, ) ClusterRedirectionPolicy { switch policy.Policy { case DCRedirectionPolicyDefault: // default policy, noop return newNoopRedirectionPolicy(clusterMetadata.GetCurrentClusterName()) case DCRedirectionPolicyNoop: return newNoopRedirectionPolicy(clusterMetadata.GetCurrentClusterName()) case DCRedirectionPolicySelectedAPIsForwarding: currentClusterName := clusterMetadata.GetCurrentClusterName() return newSelectedOrAllAPIsForwardingPolicy(currentClusterName, config, false, selectedAPIsForwardingRedirectionPolicyAPIAllowlist, "", logger, activeClusterManager, metricsClient) case DCRedirectionPolicySelectedAPIsForwardingV2: currentClusterName := clusterMetadata.GetCurrentClusterName() return newSelectedOrAllAPIsForwardingPolicy(currentClusterName, config, false, selectedAPIsForwardingRedirectionPolicyAPIAllowlistV2, "", logger, activeClusterManager, metricsClient) case DCRedirectionPolicyAllDomainAPIsForwarding: currentClusterName := clusterMetadata.GetCurrentClusterName() return newSelectedOrAllAPIsForwardingPolicy(currentClusterName, config, true, selectedAPIsForwardingRedirectionPolicyAPIAllowlist, policy.AllDomainApisForwardingTargetCluster, logger, activeClusterManager, metricsClient) case DCRedirectionPolicyAllDomainAPIsForwardingV2: currentClusterName := clusterMetadata.GetCurrentClusterName() return newSelectedOrAllAPIsForwardingPolicy(currentClusterName, config, true, selectedAPIsForwardingRedirectionPolicyAPIAllowlistV2, policy.AllDomainApisForwardingTargetCluster, logger, activeClusterManager, metricsClient) default: panic(fmt.Sprintf("Unknown DC redirection policy %v", policy.Policy)) } } // newNoopRedirectionPolicy is DC redirection policy which does nothing func newNoopRedirectionPolicy(currentClusterName string) *noopRedirectionPolicy { return &noopRedirectionPolicy{ currentClusterName: currentClusterName, } } // Redirect redirect the API call based on domain ID func (policy *noopRedirectionPolicy) Redirect( ctx context.Context, domainEntry *cache.DomainCacheEntry, workflowExecution *types.WorkflowExecution, actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy, apiName string, requestedConsistencyLevel types.QueryConsistencyLevel, call func(string) error, ) error { return call(policy.currentClusterName) } // newSelectedOrAllAPIsForwardingPolicy creates a forwarding policy for selected APIs based on domain func newSelectedOrAllAPIsForwardingPolicy( currentClusterName string, config *frontendcfg.Config, allDomainAPIs bool, selectedAPIs map[string]struct{}, targetCluster string, logger log.Logger, activeClusterManager activecluster.Manager, metricsClient metrics.Client, ) *selectedOrAllAPIsForwardingRedirectionPolicy { return &selectedOrAllAPIsForwardingRedirectionPolicy{ currentClusterName: currentClusterName, config: config, allDomainAPIs: allDomainAPIs, selectedAPIs: selectedAPIs, targetCluster: targetCluster, logger: logger, activeClusterManager: activeClusterManager, metricsClient: metricsClient, } } func (policy *selectedOrAllAPIsForwardingRedirectionPolicy) Redirect( ctx context.Context, domainEntry *cache.DomainCacheEntry, workflowExecution *types.WorkflowExecution, actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy, apiName string, requestedConsistencyLevel types.QueryConsistencyLevel, call func(string) error, ) error { if domainEntry.IsDeprecatedOrDeleted() { if _, ok := allowedAPIsForDeprecatedDomains[apiName]; !ok { return &types.DomainNotActiveError{ Message: "domain is deprecated or deleted.", DomainName: domainEntry.GetInfo().Name, CurrentCluster: policy.currentClusterName, ActiveCluster: policy.currentClusterName, } } } return policy.withRedirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, call) } func (policy *selectedOrAllAPIsForwardingRedirectionPolicy) withRedirect( ctx context.Context, domainEntry *cache.DomainCacheEntry, workflowExecution *types.WorkflowExecution, requestedActiveClusterSelectionPolicy *types.ActiveClusterSelectionPolicy, apiName string, requestedConsistencyLevel types.QueryConsistencyLevel, call func(string) error, ) error { targetDC, enableDomainNotActiveForwarding := policy.getTargetClusterAndIsDomainNotActiveAutoForwarding(ctx, domainEntry, workflowExecution, requestedActiveClusterSelectionPolicy, apiName, requestedConsistencyLevel) domainName := domainEntry.GetInfo().Name policy.logger.Debug( "Calling API on target cluster for domain", tag.OperationName(apiName), tag.ClusterName(policy.currentClusterName), tag.ActiveClusterName(targetDC), tag.WorkflowDomainName(domainName), ) err := call(targetDC) scope := policy.metricsClient.Scope(metrics.DCRedirectionForwardingPolicyScope).Tagged( append( metrics.GetContextTags(ctx), metrics.DomainTag(domainName), metrics.SourceClusterTag(policy.currentClusterName), metrics.TargetClusterTag(targetDC), metrics.IsActiveActiveDomainTag(requestedActiveClusterSelectionPolicy != nil), metrics.QueryConsistencyLevelTag(requestedConsistencyLevel.String()), )..., ) if err == nil { scope.IncCounter(metrics.ClusterForwardingPolicyRequests) return nil } var domainNotActiveErr *types.DomainNotActiveError ok := errors.As(err, &domainNotActiveErr) if !ok || !enableDomainNotActiveForwarding { policy.logger.Debug("Redirection not enabled for request", tag.WorkflowDomainName(domainName), tag.OperationName(apiName), tag.Error(err)) scope.IncCounter(metrics.ClusterForwardingPolicyRequests) return err } scope = scope.Tagged(metrics.ActiveClusterTag(domainNotActiveErr.ActiveCluster)) scope.IncCounter(metrics.ClusterForwardingPolicyRequests) if domainNotActiveErr.ActiveCluster == "" { policy.logger.Debug( "No active cluster specified in the error returned from cluster, skipping redirect", tag.ClusterName(targetDC), tag.WorkflowDomainName(domainName), tag.OperationName(apiName), ) return err } if domainNotActiveErr.ActiveCluster == targetDC { policy.logger.Debug( "No need to redirect to new target cluster", tag.ClusterName(targetDC), tag.WorkflowDomainName(domainName), tag.OperationName(apiName), ) return err } policy.logger.Debug( "Calling API on new target cluster for domain as indicated by response from cluster", tag.OperationName(apiName), tag.ActiveClusterName(domainNotActiveErr.ActiveCluster), tag.WorkflowDomainName(domainName), tag.ClusterName(targetDC), ) return call(domainNotActiveErr.ActiveCluster) } // return two values: the target cluster name, and whether or not forwarding to the active cluster func (policy *selectedOrAllAPIsForwardingRedirectionPolicy) getTargetClusterAndIsDomainNotActiveAutoForwarding( ctx context.Context, domainEntry *cache.DomainCacheEntry, workflowExecution *types.WorkflowExecution, requestedActiveClusterSelectionPolicy *types.ActiveClusterSelectionPolicy, apiName string, requestedConsistencyLevel types.QueryConsistencyLevel, ) (string, bool) { if !domainEntry.IsGlobalDomain() { // Do not do dc redirection if domain is local domain, // for global domains with 1 dc, it's still useful to do auto-forwarding during cluster migration policy.logger.Debug( "Local domain, routing to current cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.ClusterName(policy.currentClusterName), ) return policy.currentClusterName, false } if !policy.config.EnableDomainNotActiveAutoForwarding(domainEntry.GetInfo().Name) { // Do not do dc redirection if auto-forwarding dynamicconfig is not enabled policy.logger.Debug( "Auto-forwarding dynamicconfig is not enabled, routing to current cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.ClusterName(policy.currentClusterName), ) return policy.currentClusterName, false } currentActiveCluster := domainEntry.GetReplicationConfig().ActiveClusterName if domainEntry.GetReplicationConfig().IsActiveActive() { workflowActiveCluster := policy.activeClusterForActiveActiveDomainRequest(ctx, domainEntry, workflowExecution, requestedActiveClusterSelectionPolicy, apiName) policy.logger.Debug( "Active-active domain, routing to active cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.ClusterName(currentActiveCluster), tag.ActiveClusterName(workflowActiveCluster), ) currentActiveCluster = workflowActiveCluster } if policy.allDomainAPIs { if policy.targetCluster == "" { policy.logger.Debug( "All domain APIs forwarding, routing to active cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.ClusterName(policy.currentClusterName), tag.ActiveClusterName(currentActiveCluster), ) return currentActiveCluster, true } if policy.targetCluster == currentActiveCluster { policy.logger.Debug( "All domain APIs forwarding, routing to active cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.ClusterName(policy.currentClusterName), tag.ActiveClusterName(currentActiveCluster), ) return currentActiveCluster, true } // fallback to selected APIs if targetCluster is not empty and not the same as currentActiveCluster } if requestedConsistencyLevel == types.QueryConsistencyLevelStrong { policy.logger.Debug( "Query requested strong consistency, routing to active cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.ClusterName(policy.currentClusterName), tag.ActiveClusterName(currentActiveCluster), ) return currentActiveCluster, true } _, ok := policy.selectedAPIs[apiName] if !ok { // do not do dc redirection if API is not whitelisted policy.logger.Debug( "API is not whitelisted, routing to current cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.ClusterName(policy.currentClusterName), tag.OperationName(apiName), ) return policy.currentClusterName, false } policy.logger.Debug( "API is whitelisted, routing to active cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.ClusterName(policy.currentClusterName), tag.ActiveClusterName(currentActiveCluster), ) return currentActiveCluster, true } func (policy *selectedOrAllAPIsForwardingRedirectionPolicy) activeClusterForActiveActiveDomainRequest( ctx context.Context, domainEntry *cache.DomainCacheEntry, workflowExecution *types.WorkflowExecution, requestedActiveClusterSelectionPolicy *types.ActiveClusterSelectionPolicy, apiName string, ) string { policy.logger.Debug("Determining active cluster for active-active domain request", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.Dynamic("execution", workflowExecution), tag.OperationName(apiName)) if apiName == "SignalWithStartWorkflowExecution" { existingActiveClusterSelectionPolicy, running, err := policy.activeClusterManager.GetActiveClusterSelectionPolicyForCurrentWorkflow(ctx, domainEntry.GetInfo().ID, workflowExecution.WorkflowID) if err != nil { policy.logger.Error("Failed to get active cluster selection policy for current workflow, using current cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.OperationName(apiName), tag.Error(err)) return policy.currentClusterName } // if current workflow is still running, use the policy for the current workflow if running { requestedActiveClusterSelectionPolicy = existingActiveClusterSelectionPolicy } return policy.activeClusterByClusterAttribute(ctx, domainEntry, requestedActiveClusterSelectionPolicy, apiName) } else if apiName == "StartWorkflowExecution" { return policy.activeClusterByClusterAttribute(ctx, domainEntry, requestedActiveClusterSelectionPolicy, apiName) } if workflowExecution == nil || workflowExecution.WorkflowID == "" { policy.logger.Debug("Workflow execution is nil or workflow id is empty, using current cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.OperationName(apiName)) return policy.currentClusterName } activeClusterInfo, err := policy.activeClusterManager.GetActiveClusterInfoByWorkflow(ctx, domainEntry.GetInfo().ID, workflowExecution.WorkflowID, workflowExecution.RunID) if err != nil { policy.logger.Error("Failed to get active cluster of workflow, using current cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.OperationName(apiName), tag.Error(err)) return policy.currentClusterName } policy.logger.Debug("Get active cluster info by workflow for active-active domain request", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.OperationName(apiName), tag.ActiveClusterName(activeClusterInfo.ActiveClusterName)) return activeClusterInfo.ActiveClusterName } func (policy *selectedOrAllAPIsForwardingRedirectionPolicy) activeClusterByClusterAttribute(ctx context.Context, domainEntry *cache.DomainCacheEntry, requestedActiveClusterSelectionPolicy *types.ActiveClusterSelectionPolicy, apiName string) string { policy.logger.Debug("Active cluster selection policy by cluster attribute", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.OperationName(apiName), tag.Dynamic("policy", requestedActiveClusterSelectionPolicy)) activeClusterInfo, err := policy.activeClusterManager.GetActiveClusterInfoByClusterAttribute(ctx, domainEntry.GetInfo().ID, requestedActiveClusterSelectionPolicy.GetClusterAttribute()) if err != nil { policy.logger.Error("Failed to lookup active cluster by cluster attribute, using current cluster", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.OperationName(apiName), tag.Error(err)) return policy.currentClusterName } return activeClusterInfo.ActiveClusterName } ================================================ FILE: service/frontend/wrappers/clusterredirection/policy_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/frontend/wrappers/clusterredirection (interfaces: ClusterRedirectionPolicy) // // Generated by this command: // // mockgen -package clusterredirection -destination policy_mock.go -self_package github.com/uber/cadence/service/frontend/wrappers/clusterredirection github.com/uber/cadence/service/frontend/wrappers/clusterredirection ClusterRedirectionPolicy // // Package clusterredirection is a generated GoMock package. package clusterredirection import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" cache "github.com/uber/cadence/common/cache" types "github.com/uber/cadence/common/types" ) // MockClusterRedirectionPolicy is a mock of ClusterRedirectionPolicy interface. type MockClusterRedirectionPolicy struct { ctrl *gomock.Controller recorder *MockClusterRedirectionPolicyMockRecorder isgomock struct{} } // MockClusterRedirectionPolicyMockRecorder is the mock recorder for MockClusterRedirectionPolicy. type MockClusterRedirectionPolicyMockRecorder struct { mock *MockClusterRedirectionPolicy } // NewMockClusterRedirectionPolicy creates a new mock instance. func NewMockClusterRedirectionPolicy(ctrl *gomock.Controller) *MockClusterRedirectionPolicy { mock := &MockClusterRedirectionPolicy{ctrl: ctrl} mock.recorder = &MockClusterRedirectionPolicyMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClusterRedirectionPolicy) EXPECT() *MockClusterRedirectionPolicyMockRecorder { return m.recorder } // Redirect mocks base method. func (m *MockClusterRedirectionPolicy) Redirect(ctx context.Context, domainEntry *cache.DomainCacheEntry, workflowExecution *types.WorkflowExecution, actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy, apiName string, requestedConsistencyLevel types.QueryConsistencyLevel, call func(string) error) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Redirect", ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, call) ret0, _ := ret[0].(error) return ret0 } // Redirect indicates an expected call of Redirect. func (mr *MockClusterRedirectionPolicyMockRecorder) Redirect(ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, call any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Redirect", reflect.TypeOf((*MockClusterRedirectionPolicy)(nil).Redirect), ctx, domainEntry, workflowExecution, actClSelPolicyForNewWF, apiName, requestedConsistencyLevel, call) } ================================================ FILE: service/frontend/wrappers/clusterredirection/policy_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package clusterredirection import ( "context" "errors" "fmt" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" frontendcfg "github.com/uber/cadence/service/frontend/config" ) type ( noopDCRedirectionPolicySuite struct { suite.Suite *require.Assertions currentClusterName string policy *noopRedirectionPolicy } selectedAPIsForwardingRedirectionPolicySuite struct { suite.Suite *require.Assertions controller *gomock.Controller activeClusterManager *activecluster.MockManager domainName string domainID string currentClusterName string alternativeClusterName string mockConfig *frontendcfg.Config mockMetricsClient metrics.Client policy *selectedOrAllAPIsForwardingRedirectionPolicy } ) func TestNoopDCRedirectionPolicySuite(t *testing.T) { s := new(noopDCRedirectionPolicySuite) suite.Run(t, s) } func (s *noopDCRedirectionPolicySuite) SetupTest() { s.Assertions = require.New(s.T()) s.currentClusterName = cluster.TestCurrentClusterName s.policy = newNoopRedirectionPolicy(s.currentClusterName) } func (s *noopDCRedirectionPolicySuite) TearDownTest() { } func (s *noopDCRedirectionPolicySuite) TestWithDomainRedirect() { domainName := "some random domain name" domainID := "some random domain ID" domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{}, cluster.TestCurrentClusterName, ) apiName := "any random API name" callCount := 0 callFn := func(targetCluster string) error { callCount++ s.Equal(s.currentClusterName, targetCluster) return nil } err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) s.Equal(2, callCount) } func (s *noopDCRedirectionPolicySuite) TestWithDomainRedirectForAllowedAPIs() { domainName := "some random domain name" domainID := "some random domain ID" domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{}, cluster.TestCurrentClusterName, ) callCount := 0 callFn := func(targetCluster string) error { callCount++ s.Equal(s.currentClusterName, targetCluster) return nil } // Test all allowed APIs for deprecated domains allowedAPIs := []string{ "ListWorkflowExecutions", "CountWorkflowExecutions", "ScanWorkflowExecutions", "TerminateWorkflowExecution", } for _, apiName := range allowedAPIs { err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) } // Verify that each API was tested for both domain ID and domain name redirects s.Equal(2*len(allowedAPIs), callCount) } func TestSelectedAPIsForwardingRedirectionPolicySuite(t *testing.T) { s := new(selectedAPIsForwardingRedirectionPolicySuite) suite.Run(t, s) } func (s *selectedAPIsForwardingRedirectionPolicySuite) SetupSuite() { } func (s *selectedAPIsForwardingRedirectionPolicySuite) TearDownSuite() { } func (s *selectedAPIsForwardingRedirectionPolicySuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.domainName = "some random domain name" s.domainID = "some random domain ID" s.currentClusterName = cluster.TestCurrentClusterName s.alternativeClusterName = cluster.TestAlternativeClusterName logger := testlogger.New(s.T()) s.mockConfig = frontendcfg.NewConfig(dynamicconfig.NewCollection( dynamicconfig.NewNopClient(), logger, ), 0, false, "hostname", logger, ) s.mockMetricsClient = metrics.NewNoopMetricsClient() s.activeClusterManager = activecluster.NewMockManager(s.controller) s.policy = newSelectedOrAllAPIsForwardingPolicy( s.currentClusterName, s.mockConfig, false, selectedAPIsForwardingRedirectionPolicyAPIAllowlist, "", logger, s.activeClusterManager, s.mockMetricsClient, ) } func (s *selectedAPIsForwardingRedirectionPolicySuite) TearDownTest() { s.controller.Finish() } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestWithDomainRedirect_LocalDomain() { domainEntry := s.setupLocalDomain() apiName := "any random API name" callCount := 0 callFn := func(targetCluster string) error { callCount++ s.Equal(s.currentClusterName, targetCluster) return nil } err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) for apiName := range selectedAPIsForwardingRedirectionPolicyAPIAllowlist { err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) } s.Equal(2*(len(selectedAPIsForwardingRedirectionPolicyAPIAllowlist)+1), callCount) } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestWithDomainRedirect_GlobalDomain_NoForwarding_DomainNotWhitelisted() { domainEntry := s.setupGlobalDomainWithTwoReplicationCluster(false, true) domainNotActiveErr := &types.DomainNotActiveError{ CurrentCluster: s.currentClusterName, ActiveCluster: s.alternativeClusterName, } callCount := 0 callFn := func(targetCluster string) error { callCount++ s.Equal(s.currentClusterName, targetCluster) return domainNotActiveErr } for apiName := range selectedAPIsForwardingRedirectionPolicyAPIAllowlist { err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.NotNil(err) s.Equal(err.Error(), domainNotActiveErr.Error()) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.NotNil(err) s.Equal(err.Error(), domainNotActiveErr.Error()) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelStrong, callFn) s.NotNil(err) s.Equal(err.Error(), domainNotActiveErr.Error()) } s.Equal(3*len(selectedAPIsForwardingRedirectionPolicyAPIAllowlist), callCount) } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestWithDomainRedirect_GlobalDomain_Forwarding_APINotWhitelisted() { domainEntry := s.setupGlobalDomainWithTwoReplicationCluster(true, true) apiName := "any random API name" domainNotActiveErr := &types.DomainNotActiveError{ CurrentCluster: s.currentClusterName, ActiveCluster: s.alternativeClusterName, } callCount := 0 callFn := func(targetCluster string) error { callCount++ s.Equal(s.currentClusterName, targetCluster) return domainNotActiveErr } err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.NotNil(err) s.Equal(err.Error(), domainNotActiveErr.Error()) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.NotNil(err) s.Equal(err.Error(), domainNotActiveErr.Error()) s.Equal(2, callCount) } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestGetTargetDataCenter_GlobalDomain_Forwarding_CurrentCluster() { domainEntry := s.setupGlobalDomainWithTwoReplicationCluster(true, true) callCount := 0 callFn := func(targetCluster string) error { callCount++ s.Equal(s.currentClusterName, targetCluster) return nil } for apiName := range selectedAPIsForwardingRedirectionPolicyAPIAllowlist { err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) } s.Equal(2*len(selectedAPIsForwardingRedirectionPolicyAPIAllowlist), callCount) } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestGetTargetDataCenter_GlobalDomain_Forwarding_AlternativeCluster() { domainEntry := s.setupGlobalDomainWithTwoReplicationCluster(true, false) callCount := 0 callFn := func(targetCluster string) error { callCount++ s.Equal(s.alternativeClusterName, targetCluster) return nil } for apiName := range selectedAPIsForwardingRedirectionPolicyAPIAllowlist { err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) } s.Equal(2*len(selectedAPIsForwardingRedirectionPolicyAPIAllowlist), callCount) } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestGetTargetDataCenter_GlobalDomain_Forwarding_AlternativeCluster_StrongConsistency() { domainEntry := s.setupGlobalDomainWithTwoReplicationCluster(true, false) callCount := 0 callFn := func(targetCluster string) error { callCount++ s.Equal(s.alternativeClusterName, targetCluster) return nil } apiName := "any random API name" err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelStrong, callFn) s.Nil(err) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelStrong, callFn) s.Nil(err) s.Equal(2, callCount) } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestGetTargetDataCenter_GlobalDomain_Forwarding_CurrentClusterToAlternativeCluster() { domainEntry := s.setupGlobalDomainWithTwoReplicationCluster(true, true) currentClustercallCount := 0 alternativeClustercallCount := 0 callFn := func(targetCluster string) error { switch targetCluster { case s.currentClusterName: currentClustercallCount++ return &types.DomainNotActiveError{ CurrentCluster: s.currentClusterName, ActiveCluster: s.alternativeClusterName, } case s.alternativeClusterName: alternativeClustercallCount++ return nil default: panic(fmt.Sprintf("unknown cluster name %v", targetCluster)) } } for apiName := range selectedAPIsForwardingRedirectionPolicyAPIAllowlist { err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) } s.Equal(2*len(selectedAPIsForwardingRedirectionPolicyAPIAllowlist), currentClustercallCount) s.Equal(2*len(selectedAPIsForwardingRedirectionPolicyAPIAllowlist), alternativeClustercallCount) } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestGetTargetDataCenter_GlobalDomain_Forwarding_AlternativeClusterToCurrentCluster() { domainEntry := s.setupGlobalDomainWithTwoReplicationCluster(true, false) currentClustercallCount := 0 alternativeClustercallCount := 0 callFn := func(targetCluster string) error { switch targetCluster { case s.currentClusterName: currentClustercallCount++ return nil case s.alternativeClusterName: alternativeClustercallCount++ return &types.DomainNotActiveError{ CurrentCluster: s.alternativeClusterName, ActiveCluster: s.currentClusterName, } default: panic(fmt.Sprintf("unknown cluster name %v", targetCluster)) } } for apiName := range selectedAPIsForwardingRedirectionPolicyAPIAllowlist { err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Nil(err) } s.Equal(2*len(selectedAPIsForwardingRedirectionPolicyAPIAllowlist), currentClustercallCount) s.Equal(2*len(selectedAPIsForwardingRedirectionPolicyAPIAllowlist), alternativeClustercallCount) } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestGetTargetDataCenter_GlobalDomain_Forwarding_ToSameCluster_Skipped() { domainEntry := s.setupGlobalDomainWithTwoReplicationCluster(true, false) callFn := func(targetCluster string) error { switch targetCluster { case s.alternativeClusterName: return &types.DomainNotActiveError{ // this shouldn't happen but if it does, we should skip the redirect CurrentCluster: s.alternativeClusterName, ActiveCluster: s.alternativeClusterName, } default: panic(fmt.Sprintf("unknown cluster name %v", targetCluster)) } } for apiName := range selectedAPIsForwardingRedirectionPolicyAPIAllowlist { err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) var domainNotActiveErr *types.DomainNotActiveError s.ErrorAs(err, &domainNotActiveErr) s.Equal(s.alternativeClusterName, domainNotActiveErr.ActiveCluster) } } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestGetTargetDataCenter_GlobalDomain_Forwarding_EmptyClusster_Skipped() { domainEntry := s.setupGlobalDomainWithTwoReplicationCluster(true, false) callFn := func(targetCluster string) error { switch targetCluster { case s.alternativeClusterName: return &types.DomainNotActiveError{ // this shouldn't happen but if it does, we should skip the redirect CurrentCluster: s.alternativeClusterName, ActiveCluster: "", } default: panic(fmt.Sprintf("unknown cluster name %v", targetCluster)) } } for apiName := range selectedAPIsForwardingRedirectionPolicyAPIAllowlist { err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) var domainNotActiveErr *types.DomainNotActiveError s.ErrorAs(err, &domainNotActiveErr) s.Equal("", domainNotActiveErr.ActiveCluster) } } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestGetTargetDataCenter_GlobalDomain_Forwarding_DeprecatedDomain() { domainEntry := s.setupGlobalDeprecatedDomainWithTwoReplicationCluster(true, false) currentClustercallCount := 0 alternativeClustercallCount := 0 callFn := func(targetCluster string) error { switch targetCluster { case s.currentClusterName: currentClustercallCount++ return nil case s.alternativeClusterName: alternativeClustercallCount++ return &types.DomainNotActiveError{ CurrentCluster: s.alternativeClusterName, ActiveCluster: s.currentClusterName, } default: panic(fmt.Sprintf("unknown cluster name %v", targetCluster)) } } // Test non-allowed APIs for apiName := range selectedAPIsForwardingRedirectionPolicyAPIAllowlist { if _, ok := allowedAPIsForDeprecatedDomains[apiName]; ok { continue // Skip allowed APIs } err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Error(err) s.Equal("domain is deprecated or deleted.", err.Error()) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.Error(err) s.Equal("domain is deprecated or deleted.", err.Error()) } s.Equal(0, currentClustercallCount) s.Equal(0, alternativeClustercallCount) // Test allowed APIs for apiName := range allowedAPIsForDeprecatedDomains { err := s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.NoError(err) err = s.policy.Redirect(context.Background(), domainEntry, nil, nil, apiName, types.QueryConsistencyLevelEventual, callFn) s.NoError(err) } // Verify that allowed APIs were called on the current cluster s.Equal(2*len(allowedAPIsForDeprecatedDomains), currentClustercallCount) s.Equal(2, alternativeClustercallCount) } func (s *selectedAPIsForwardingRedirectionPolicySuite) setupLocalDomain() *cache.DomainCacheEntry { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{ID: s.domainID, Name: s.domainName}, &persistence.DomainConfig{Retention: 1}, cluster.TestCurrentClusterName, ) return domainEntry } func (s *selectedAPIsForwardingRedirectionPolicySuite) setupGlobalDomainWithTwoReplicationCluster(forwardingEnabled bool, isRecordActive bool) *cache.DomainCacheEntry { activeCluster := s.alternativeClusterName if isRecordActive { activeCluster = s.currentClusterName } domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: s.domainID, Name: s.domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: activeCluster, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, // not used ) s.mockConfig.EnableDomainNotActiveAutoForwarding = dynamicproperties.GetBoolPropertyFnFilteredByDomain(forwardingEnabled) return domainEntry } func (s *selectedAPIsForwardingRedirectionPolicySuite) setupGlobalDeprecatedDomainWithTwoReplicationCluster(forwardingEnabled bool, isRecordActive bool) *cache.DomainCacheEntry { activeCluster := s.alternativeClusterName if isRecordActive { activeCluster = s.currentClusterName } domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: s.domainID, Name: s.domainName, Status: persistence.DomainStatusDeprecated}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: activeCluster, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, // not used ) s.mockConfig.EnableDomainNotActiveAutoForwarding = dynamicproperties.GetBoolPropertyFnFilteredByDomain(forwardingEnabled) return domainEntry } func (s *selectedAPIsForwardingRedirectionPolicySuite) setupActiveActiveDomainWithTwoReplicationCluster(forwardingEnabled bool) *cache.DomainCacheEntry { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: s.domainID, Name: s.domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": { ActiveClusterName: s.currentClusterName, FailoverVersion: 1, }, "us-west": { ActiveClusterName: s.alternativeClusterName, FailoverVersion: 2, }, }, }, }, }, }, 1234, // not used ) s.mockConfig.EnableDomainNotActiveAutoForwarding = dynamicproperties.GetBoolPropertyFnFilteredByDomain(forwardingEnabled) return domainEntry } func (s *selectedAPIsForwardingRedirectionPolicySuite) TestActiveClusterForActiveActiveDomainRequest() { domainEntry := s.setupActiveActiveDomainWithTwoReplicationCluster(true) usEastStickyPlcy := &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, } usWestStickyPlcy := &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, } tests := []struct { name string apiName string domainEntry *cache.DomainCacheEntry workflowExecution *types.WorkflowExecution actClSelPolicyForNewWF *types.ActiveClusterSelectionPolicy mockFn func(activeClusterManager *activecluster.MockManager) want string }{ { name: "new workflow with policy", apiName: "StartWorkflowExecution", domainEntry: domainEntry, actClSelPolicyForNewWF: usWestStickyPlcy, mockFn: func(activeClusterManager *activecluster.MockManager) { activeClusterManager.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), domainEntry.GetInfo().ID, usWestStickyPlcy.GetClusterAttribute()).Return(&types.ActiveClusterInfo{ ActiveClusterName: s.alternativeClusterName, FailoverVersion: 2, }, nil) }, want: s.alternativeClusterName, }, { name: "new workflow with policy - lookup failed", apiName: "StartWorkflowExecution", domainEntry: domainEntry, actClSelPolicyForNewWF: usEastStickyPlcy, mockFn: func(activeClusterManager *activecluster.MockManager) { activeClusterManager.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), domainEntry.GetInfo().ID, usEastStickyPlcy.GetClusterAttribute()).Return(nil, errors.New("lookup failed")) }, want: s.currentClusterName, }, { name: "existing workflow - missing workflow execution", apiName: "SignalWorkflowExecution", domainEntry: domainEntry, mockFn: func(activeClusterManager *activecluster.MockManager) { }, want: s.currentClusterName, }, { name: "existing workflow - missing workflow id", apiName: "SignalWorkflowExecution", domainEntry: domainEntry, workflowExecution: &types.WorkflowExecution{ RunID: "run1", }, mockFn: func(activeClusterManager *activecluster.MockManager) { }, want: s.currentClusterName, }, { name: "existing workflow - lookup failed", apiName: "SignalWorkflowExecution", domainEntry: domainEntry, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf1", RunID: "run1", }, mockFn: func(activeClusterManager *activecluster.MockManager) { activeClusterManager.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), domainEntry.GetInfo().ID, "wf1", "run1").Return(nil, errors.New("lookup failed")) }, want: s.currentClusterName, }, { name: "SignalWithStartWorkflowExecution - workflow running, use current workflow policy", apiName: "SignalWithStartWorkflowExecution", domainEntry: domainEntry, actClSelPolicyForNewWF: usEastStickyPlcy, // This should be ignored when workflow is running workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf1", }, mockFn: func(activeClusterManager *activecluster.MockManager) { // Returns the current workflow's policy and running=true activeClusterManager.EXPECT().GetActiveClusterSelectionPolicyForCurrentWorkflow(gomock.Any(), domainEntry.GetInfo().ID, "wf1").Return(usWestStickyPlcy, true, nil) // Should use the west policy (from current workflow), not the east policy (from new workflow param) activeClusterManager.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), domainEntry.GetInfo().ID, usWestStickyPlcy.GetClusterAttribute()).Return(&types.ActiveClusterInfo{ ActiveClusterName: s.alternativeClusterName, FailoverVersion: 2, }, nil) }, want: s.alternativeClusterName, }, { name: "SignalWithStartWorkflowExecution - workflow not running, use new workflow policy", apiName: "SignalWithStartWorkflowExecution", domainEntry: domainEntry, actClSelPolicyForNewWF: usWestStickyPlcy, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf1", }, mockFn: func(activeClusterManager *activecluster.MockManager) { // Returns policy but running=false activeClusterManager.EXPECT().GetActiveClusterSelectionPolicyForCurrentWorkflow(gomock.Any(), domainEntry.GetInfo().ID, "wf1").Return(usEastStickyPlcy, false, nil) // Should use the west policy (from new workflow param), not the east policy (from current workflow) activeClusterManager.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), domainEntry.GetInfo().ID, usWestStickyPlcy.GetClusterAttribute()).Return(&types.ActiveClusterInfo{ ActiveClusterName: s.alternativeClusterName, FailoverVersion: 2, }, nil) }, want: s.alternativeClusterName, }, { name: "SignalWithStartWorkflowExecution - lookup failed, use current cluster", apiName: "SignalWithStartWorkflowExecution", domainEntry: domainEntry, actClSelPolicyForNewWF: usWestStickyPlcy, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf1", }, mockFn: func(activeClusterManager *activecluster.MockManager) { // Lookup fails activeClusterManager.EXPECT().GetActiveClusterSelectionPolicyForCurrentWorkflow(gomock.Any(), domainEntry.GetInfo().ID, "wf1").Return(nil, false, errors.New("lookup failed")) }, want: s.currentClusterName, }, { name: "existing workflow - success", apiName: "SignalWorkflowExecution", domainEntry: domainEntry, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf1", RunID: "run1", }, mockFn: func(activeClusterManager *activecluster.MockManager) { activeClusterManager.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), domainEntry.GetInfo().ID, "wf1", "run1").Return(&types.ActiveClusterInfo{ ActiveClusterName: s.alternativeClusterName, FailoverVersion: 2, }, nil) }, want: s.alternativeClusterName, }, } for _, test := range tests { s.Run(test.name, func() { activeClusterManager := activecluster.NewMockManager(s.controller) test.mockFn(activeClusterManager) s.policy.activeClusterManager = activeClusterManager apiName := test.apiName if apiName == "" { apiName = "any random API name" } s.Equal(test.want, s.policy.activeClusterForActiveActiveDomainRequest( context.Background(), test.domainEntry, test.workflowExecution, test.actClSelPolicyForNewWF, apiName, )) }) } } ================================================ FILE: service/frontend/wrappers/clusterredirection/utils.go ================================================ package clusterredirection import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/types" ) func getRequestedConsistencyLevelFromContext(ctx context.Context) types.QueryConsistencyLevel { call := yarpc.CallFromContext(ctx) if call == nil { return types.QueryConsistencyLevelEventual } featureFlags := client.GetFeatureFlagsFromHeader(call) if featureFlags.AutoforwardingEnabled { return types.QueryConsistencyLevelStrong } return types.QueryConsistencyLevelEventual } ================================================ FILE: service/frontend/wrappers/clusterredirection/utils_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package clusterredirection import ( "context" "testing" "github.com/stretchr/testify/assert" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc/yarpctest" "github.com/uber/cadence/common" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/types" ) func TestGetRequestedConsistencyLevelFromContext(t *testing.T) { tests := []struct { name string featureFlags apiv1.FeatureFlags expected types.QueryConsistencyLevel }{ { name: "empty feature flags", featureFlags: apiv1.FeatureFlags{}, expected: types.QueryConsistencyLevelEventual, }, { name: "auto forwarding disabled", featureFlags: apiv1.FeatureFlags{AutoforwardingEnabled: false}, expected: types.QueryConsistencyLevelEventual, }, { name: "autoforwarding enabled", featureFlags: apiv1.FeatureFlags{AutoforwardingEnabled: true}, expected: types.QueryConsistencyLevelStrong, }, { name: "no autoforwarding field", featureFlags: apiv1.FeatureFlags{ WorkflowExecutionAlreadyCompletedErrorEnabled: true, }, expected: types.QueryConsistencyLevelEventual, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := yarpctest.ContextWithCall(context.Background(), &yarpctest.Call{ Headers: map[string]string{common.ClientFeatureFlagsHeaderName: client.FeatureFlagsHeader(tt.featureFlags)}, }) result := getRequestedConsistencyLevelFromContext(ctx) assert.Equal(t, tt.expected, result) }) } } ================================================ FILE: service/frontend/wrappers/grpc/admin_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" "github.com/uber/cadence/common/types/mapper/proto" "github.com/uber/cadence/service/frontend/admin" ) type AdminHandler struct { h admin.Handler } func NewAdminHandler(h admin.Handler) AdminHandler { return AdminHandler{h} } func (g AdminHandler) AddSearchAttribute(ctx context.Context, request *adminv1.AddSearchAttributeRequest) (*adminv1.AddSearchAttributeResponse, error) { err := g.h.AddSearchAttribute(ctx, proto.ToAdminAddSearchAttributeRequest(request)) return &adminv1.AddSearchAttributeResponse{}, proto.FromError(err) } func (g AdminHandler) CloseShard(ctx context.Context, request *adminv1.CloseShardRequest) (*adminv1.CloseShardResponse, error) { err := g.h.CloseShard(ctx, proto.ToAdminCloseShardRequest(request)) return &adminv1.CloseShardResponse{}, proto.FromError(err) } func (g AdminHandler) CountDLQMessages(ctx context.Context, request *adminv1.CountDLQMessagesRequest) (*adminv1.CountDLQMessagesResponse, error) { response, err := g.h.CountDLQMessages(ctx, proto.ToAdminCountDLQMessagesRequest(request)) return proto.FromAdminCountDLQMessagesResponse(response), proto.FromError(err) } func (g AdminHandler) DeleteWorkflow(ctx context.Context, request *adminv1.DeleteWorkflowRequest) (*adminv1.DeleteWorkflowResponse, error) { response, err := g.h.DeleteWorkflow(ctx, proto.ToAdminDeleteWorkflowRequest(request)) return proto.FromAdminDeleteWorkflowResponse(response), proto.FromError(err) } func (g AdminHandler) DescribeCluster(ctx context.Context, request *adminv1.DescribeClusterRequest) (*adminv1.DescribeClusterResponse, error) { response, err := g.h.DescribeCluster(ctx) return proto.FromAdminDescribeClusterResponse(response), proto.FromError(err) } func (g AdminHandler) DescribeHistoryHost(ctx context.Context, request *adminv1.DescribeHistoryHostRequest) (*adminv1.DescribeHistoryHostResponse, error) { response, err := g.h.DescribeHistoryHost(ctx, proto.ToAdminDescribeHistoryHostRequest(request)) return proto.FromAdminDescribeHistoryHostResponse(response), proto.FromError(err) } func (g AdminHandler) DescribeQueue(ctx context.Context, request *adminv1.DescribeQueueRequest) (*adminv1.DescribeQueueResponse, error) { response, err := g.h.DescribeQueue(ctx, proto.ToAdminDescribeQueueRequest(request)) return proto.FromAdminDescribeQueueResponse(response), proto.FromError(err) } func (g AdminHandler) DescribeShardDistribution(ctx context.Context, request *adminv1.DescribeShardDistributionRequest) (*adminv1.DescribeShardDistributionResponse, error) { response, err := g.h.DescribeShardDistribution(ctx, proto.ToAdminDescribeShardDistributionRequest(request)) return proto.FromAdminDescribeShardDistributionResponse(response), proto.FromError(err) } func (g AdminHandler) DescribeWorkflowExecution(ctx context.Context, request *adminv1.DescribeWorkflowExecutionRequest) (*adminv1.DescribeWorkflowExecutionResponse, error) { response, err := g.h.DescribeWorkflowExecution(ctx, proto.ToAdminDescribeWorkflowExecutionRequest(request)) return proto.FromAdminDescribeWorkflowExecutionResponse(response), proto.FromError(err) } func (g AdminHandler) GetCrossClusterTasks(ctx context.Context, request *adminv1.GetCrossClusterTasksRequest) (*adminv1.GetCrossClusterTasksResponse, error) { response, err := g.h.GetCrossClusterTasks(ctx, proto.ToAdminGetCrossClusterTasksRequest(request)) return proto.FromAdminGetCrossClusterTasksResponse(response), proto.FromError(err) } func (g AdminHandler) GetDLQReplicationMessages(ctx context.Context, request *adminv1.GetDLQReplicationMessagesRequest) (*adminv1.GetDLQReplicationMessagesResponse, error) { response, err := g.h.GetDLQReplicationMessages(ctx, proto.ToAdminGetDLQReplicationMessagesRequest(request)) return proto.FromAdminGetDLQReplicationMessagesResponse(response), proto.FromError(err) } func (g AdminHandler) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, request *adminv1.GetDomainAsyncWorkflowConfiguratonRequest) (*adminv1.GetDomainAsyncWorkflowConfiguratonResponse, error) { response, err := g.h.GetDomainAsyncWorkflowConfiguraton(ctx, proto.ToAdminGetDomainAsyncWorkflowConfiguratonRequest(request)) return proto.FromAdminGetDomainAsyncWorkflowConfiguratonResponse(response), proto.FromError(err) } func (g AdminHandler) GetDomainIsolationGroups(ctx context.Context, request *adminv1.GetDomainIsolationGroupsRequest) (*adminv1.GetDomainIsolationGroupsResponse, error) { response, err := g.h.GetDomainIsolationGroups(ctx, proto.ToAdminGetDomainIsolationGroupsRequest(request)) return proto.FromAdminGetDomainIsolationGroupsResponse(response), proto.FromError(err) } func (g AdminHandler) GetDomainReplicationMessages(ctx context.Context, request *adminv1.GetDomainReplicationMessagesRequest) (*adminv1.GetDomainReplicationMessagesResponse, error) { response, err := g.h.GetDomainReplicationMessages(ctx, proto.ToAdminGetDomainReplicationMessagesRequest(request)) return proto.FromAdminGetDomainReplicationMessagesResponse(response), proto.FromError(err) } func (g AdminHandler) GetDynamicConfig(ctx context.Context, request *adminv1.GetDynamicConfigRequest) (*adminv1.GetDynamicConfigResponse, error) { response, err := g.h.GetDynamicConfig(ctx, proto.ToAdminGetDynamicConfigRequest(request)) return proto.FromAdminGetDynamicConfigResponse(response), proto.FromError(err) } func (g AdminHandler) GetGlobalIsolationGroups(ctx context.Context, request *adminv1.GetGlobalIsolationGroupsRequest) (*adminv1.GetGlobalIsolationGroupsResponse, error) { response, err := g.h.GetGlobalIsolationGroups(ctx, proto.ToAdminGetGlobalIsolationGroupsRequest(request)) return proto.FromAdminGetGlobalIsolationGroupsResponse(response), proto.FromError(err) } func (g AdminHandler) GetReplicationMessages(ctx context.Context, request *adminv1.GetReplicationMessagesRequest) (*adminv1.GetReplicationMessagesResponse, error) { response, err := g.h.GetReplicationMessages(ctx, proto.ToAdminGetReplicationMessagesRequest(request)) return proto.FromAdminGetReplicationMessagesResponse(response), proto.FromError(err) } func (g AdminHandler) GetWorkflowExecutionRawHistoryV2(ctx context.Context, request *adminv1.GetWorkflowExecutionRawHistoryV2Request) (*adminv1.GetWorkflowExecutionRawHistoryV2Response, error) { response, err := g.h.GetWorkflowExecutionRawHistoryV2(ctx, proto.ToAdminGetWorkflowExecutionRawHistoryV2Request(request)) return proto.FromAdminGetWorkflowExecutionRawHistoryV2Response(response), proto.FromError(err) } func (g AdminHandler) ListDynamicConfig(ctx context.Context, request *adminv1.ListDynamicConfigRequest) (*adminv1.ListDynamicConfigResponse, error) { response, err := g.h.ListDynamicConfig(ctx, proto.ToAdminListDynamicConfigRequest(request)) return proto.FromAdminListDynamicConfigResponse(response), proto.FromError(err) } func (g AdminHandler) MaintainCorruptWorkflow(ctx context.Context, request *adminv1.MaintainCorruptWorkflowRequest) (*adminv1.MaintainCorruptWorkflowResponse, error) { response, err := g.h.MaintainCorruptWorkflow(ctx, proto.ToAdminMaintainCorruptWorkflowRequest(request)) return proto.FromAdminMaintainCorruptWorkflowResponse(response), proto.FromError(err) } func (g AdminHandler) MergeDLQMessages(ctx context.Context, request *adminv1.MergeDLQMessagesRequest) (*adminv1.MergeDLQMessagesResponse, error) { response, err := g.h.MergeDLQMessages(ctx, proto.ToAdminMergeDLQMessagesRequest(request)) return proto.FromAdminMergeDLQMessagesResponse(response), proto.FromError(err) } func (g AdminHandler) PurgeDLQMessages(ctx context.Context, request *adminv1.PurgeDLQMessagesRequest) (*adminv1.PurgeDLQMessagesResponse, error) { err := g.h.PurgeDLQMessages(ctx, proto.ToAdminPurgeDLQMessagesRequest(request)) return &adminv1.PurgeDLQMessagesResponse{}, proto.FromError(err) } func (g AdminHandler) ReadDLQMessages(ctx context.Context, request *adminv1.ReadDLQMessagesRequest) (*adminv1.ReadDLQMessagesResponse, error) { response, err := g.h.ReadDLQMessages(ctx, proto.ToAdminReadDLQMessagesRequest(request)) return proto.FromAdminReadDLQMessagesResponse(response), proto.FromError(err) } func (g AdminHandler) ReapplyEvents(ctx context.Context, request *adminv1.ReapplyEventsRequest) (*adminv1.ReapplyEventsResponse, error) { err := g.h.ReapplyEvents(ctx, proto.ToAdminReapplyEventsRequest(request)) return &adminv1.ReapplyEventsResponse{}, proto.FromError(err) } func (g AdminHandler) RefreshWorkflowTasks(ctx context.Context, request *adminv1.RefreshWorkflowTasksRequest) (*adminv1.RefreshWorkflowTasksResponse, error) { err := g.h.RefreshWorkflowTasks(ctx, proto.ToAdminRefreshWorkflowTasksRequest(request)) return &adminv1.RefreshWorkflowTasksResponse{}, proto.FromError(err) } func (g AdminHandler) RemoveTask(ctx context.Context, request *adminv1.RemoveTaskRequest) (*adminv1.RemoveTaskResponse, error) { err := g.h.RemoveTask(ctx, proto.ToAdminRemoveTaskRequest(request)) return &adminv1.RemoveTaskResponse{}, proto.FromError(err) } func (g AdminHandler) ResendReplicationTasks(ctx context.Context, request *adminv1.ResendReplicationTasksRequest) (*adminv1.ResendReplicationTasksResponse, error) { err := g.h.ResendReplicationTasks(ctx, proto.ToAdminResendReplicationTasksRequest(request)) return &adminv1.ResendReplicationTasksResponse{}, proto.FromError(err) } func (g AdminHandler) ResetQueue(ctx context.Context, request *adminv1.ResetQueueRequest) (*adminv1.ResetQueueResponse, error) { err := g.h.ResetQueue(ctx, proto.ToAdminResetQueueRequest(request)) return &adminv1.ResetQueueResponse{}, proto.FromError(err) } func (g AdminHandler) RespondCrossClusterTasksCompleted(ctx context.Context, request *adminv1.RespondCrossClusterTasksCompletedRequest) (*adminv1.RespondCrossClusterTasksCompletedResponse, error) { response, err := g.h.RespondCrossClusterTasksCompleted(ctx, proto.ToAdminRespondCrossClusterTasksCompletedRequest(request)) return proto.FromAdminRespondCrossClusterTasksCompletedResponse(response), proto.FromError(err) } func (g AdminHandler) RestoreDynamicConfig(ctx context.Context, request *adminv1.RestoreDynamicConfigRequest) (*adminv1.RestoreDynamicConfigResponse, error) { err := g.h.RestoreDynamicConfig(ctx, proto.ToAdminRestoreDynamicConfigRequest(request)) return &adminv1.RestoreDynamicConfigResponse{}, proto.FromError(err) } func (g AdminHandler) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, request *adminv1.UpdateDomainAsyncWorkflowConfiguratonRequest) (*adminv1.UpdateDomainAsyncWorkflowConfiguratonResponse, error) { response, err := g.h.UpdateDomainAsyncWorkflowConfiguraton(ctx, proto.ToAdminUpdateDomainAsyncWorkflowConfiguratonRequest(request)) return proto.FromAdminUpdateDomainAsyncWorkflowConfiguratonResponse(response), proto.FromError(err) } func (g AdminHandler) UpdateDomainIsolationGroups(ctx context.Context, request *adminv1.UpdateDomainIsolationGroupsRequest) (*adminv1.UpdateDomainIsolationGroupsResponse, error) { response, err := g.h.UpdateDomainIsolationGroups(ctx, proto.ToAdminUpdateDomainIsolationGroupsRequest(request)) return proto.FromAdminUpdateDomainIsolationGroupsResponse(response), proto.FromError(err) } func (g AdminHandler) UpdateDynamicConfig(ctx context.Context, request *adminv1.UpdateDynamicConfigRequest) (*adminv1.UpdateDynamicConfigResponse, error) { err := g.h.UpdateDynamicConfig(ctx, proto.ToAdminUpdateDynamicConfigRequest(request)) return &adminv1.UpdateDynamicConfigResponse{}, proto.FromError(err) } func (g AdminHandler) UpdateGlobalIsolationGroups(ctx context.Context, request *adminv1.UpdateGlobalIsolationGroupsRequest) (*adminv1.UpdateGlobalIsolationGroupsResponse, error) { response, err := g.h.UpdateGlobalIsolationGroups(ctx, proto.ToAdminUpdateGlobalIsolationGroupsRequest(request)) return proto.FromAdminUpdateGlobalIsolationGroupsResponse(response), proto.FromError(err) } func (g AdminHandler) UpdateTaskListPartitionConfig(ctx context.Context, request *adminv1.UpdateTaskListPartitionConfigRequest) (*adminv1.UpdateTaskListPartitionConfigResponse, error) { response, err := g.h.UpdateTaskListPartitionConfig(ctx, proto.ToAdminUpdateTaskListPartitionConfigRequest(request)) return proto.FromAdminUpdateTaskListPartitionConfigResponse(response), proto.FromError(err) } ================================================ FILE: service/frontend/wrappers/grpc/api_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "github.com/uber/cadence/common/types/mapper/proto" "github.com/uber/cadence/service/frontend/api" ) type APIHandler struct { h api.Handler } func NewAPIHandler(h api.Handler) APIHandler { return APIHandler{h} } func (g APIHandler) CountWorkflowExecutions(ctx context.Context, request *apiv1.CountWorkflowExecutionsRequest) (*apiv1.CountWorkflowExecutionsResponse, error) { response, err := g.h.CountWorkflowExecutions(ctx, proto.ToCountWorkflowExecutionsRequest(request)) return proto.FromCountWorkflowExecutionsResponse(response), proto.FromError(err) } func (g APIHandler) DeleteDomain(ctx context.Context, request *apiv1.DeleteDomainRequest) (*apiv1.DeleteDomainResponse, error) { err := g.h.DeleteDomain(ctx, proto.ToDeleteDomainRequest(request)) return &apiv1.DeleteDomainResponse{}, proto.FromError(err) } func (g APIHandler) DeprecateDomain(ctx context.Context, request *apiv1.DeprecateDomainRequest) (*apiv1.DeprecateDomainResponse, error) { err := g.h.DeprecateDomain(ctx, proto.ToDeprecateDomainRequest(request)) return &apiv1.DeprecateDomainResponse{}, proto.FromError(err) } func (g APIHandler) DescribeDomain(ctx context.Context, request *apiv1.DescribeDomainRequest) (*apiv1.DescribeDomainResponse, error) { response, err := g.h.DescribeDomain(ctx, proto.ToDescribeDomainRequest(request)) return proto.FromDescribeDomainResponse(response), proto.FromError(err) } func (g APIHandler) DescribeTaskList(ctx context.Context, request *apiv1.DescribeTaskListRequest) (*apiv1.DescribeTaskListResponse, error) { response, err := g.h.DescribeTaskList(ctx, proto.ToDescribeTaskListRequest(request)) return proto.FromDescribeTaskListResponse(response), proto.FromError(err) } func (g APIHandler) DescribeWorkflowExecution(ctx context.Context, request *apiv1.DescribeWorkflowExecutionRequest) (*apiv1.DescribeWorkflowExecutionResponse, error) { response, err := g.h.DescribeWorkflowExecution(ctx, proto.ToDescribeWorkflowExecutionRequest(request)) return proto.FromDescribeWorkflowExecutionResponse(response), proto.FromError(err) } func (g APIHandler) DiagnoseWorkflowExecution(ctx context.Context, request *apiv1.DiagnoseWorkflowExecutionRequest) (*apiv1.DiagnoseWorkflowExecutionResponse, error) { response, err := g.h.DiagnoseWorkflowExecution(ctx, proto.ToDiagnoseWorkflowExecutionRequest(request)) return proto.FromDiagnoseWorkflowExecutionResponse(response), proto.FromError(err) } func (g APIHandler) FailoverDomain(ctx context.Context, request *apiv1.FailoverDomainRequest) (*apiv1.FailoverDomainResponse, error) { response, err := g.h.FailoverDomain(ctx, proto.ToFailoverDomainRequest(request)) return proto.FromFailoverDomainResponse(response), proto.FromError(err) } func (g APIHandler) GetClusterInfo(ctx context.Context, request *apiv1.GetClusterInfoRequest) (*apiv1.GetClusterInfoResponse, error) { response, err := g.h.GetClusterInfo(ctx) return proto.FromGetClusterInfoResponse(response), proto.FromError(err) } func (g APIHandler) GetSearchAttributes(ctx context.Context, request *apiv1.GetSearchAttributesRequest) (*apiv1.GetSearchAttributesResponse, error) { response, err := g.h.GetSearchAttributes(ctx) return proto.FromGetSearchAttributesResponse(response), proto.FromError(err) } func (g APIHandler) GetTaskListsByDomain(ctx context.Context, request *apiv1.GetTaskListsByDomainRequest) (*apiv1.GetTaskListsByDomainResponse, error) { response, err := g.h.GetTaskListsByDomain(ctx, proto.ToGetTaskListsByDomainRequest(request)) return proto.FromGetTaskListsByDomainResponse(response), proto.FromError(err) } func (g APIHandler) GetWorkflowExecutionHistory(ctx context.Context, request *apiv1.GetWorkflowExecutionHistoryRequest) (*apiv1.GetWorkflowExecutionHistoryResponse, error) { response, err := g.h.GetWorkflowExecutionHistory(ctx, proto.ToGetWorkflowExecutionHistoryRequest(request)) return proto.FromGetWorkflowExecutionHistoryResponse(response), proto.FromError(err) } func (g APIHandler) ListArchivedWorkflowExecutions(ctx context.Context, request *apiv1.ListArchivedWorkflowExecutionsRequest) (*apiv1.ListArchivedWorkflowExecutionsResponse, error) { response, err := g.h.ListArchivedWorkflowExecutions(ctx, proto.ToListArchivedWorkflowExecutionsRequest(request)) return proto.FromListArchivedWorkflowExecutionsResponse(response), proto.FromError(err) } func (g APIHandler) ListClosedWorkflowExecutions(ctx context.Context, request *apiv1.ListClosedWorkflowExecutionsRequest) (*apiv1.ListClosedWorkflowExecutionsResponse, error) { response, err := g.h.ListClosedWorkflowExecutions(ctx, proto.ToListClosedWorkflowExecutionsRequest(request)) return proto.FromListClosedWorkflowExecutionsResponse(response), proto.FromError(err) } func (g APIHandler) ListDomains(ctx context.Context, request *apiv1.ListDomainsRequest) (*apiv1.ListDomainsResponse, error) { response, err := g.h.ListDomains(ctx, proto.ToListDomainsRequest(request)) return proto.FromListDomainsResponse(response), proto.FromError(err) } func (g APIHandler) ListFailoverHistory(ctx context.Context, request *apiv1.ListFailoverHistoryRequest) (*apiv1.ListFailoverHistoryResponse, error) { response, err := g.h.ListFailoverHistory(ctx, proto.ToListFailoverHistoryRequest(request)) return proto.FromListFailoverHistoryResponse(response), proto.FromError(err) } func (g APIHandler) ListOpenWorkflowExecutions(ctx context.Context, request *apiv1.ListOpenWorkflowExecutionsRequest) (*apiv1.ListOpenWorkflowExecutionsResponse, error) { response, err := g.h.ListOpenWorkflowExecutions(ctx, proto.ToListOpenWorkflowExecutionsRequest(request)) return proto.FromListOpenWorkflowExecutionsResponse(response), proto.FromError(err) } func (g APIHandler) ListTaskListPartitions(ctx context.Context, request *apiv1.ListTaskListPartitionsRequest) (*apiv1.ListTaskListPartitionsResponse, error) { response, err := g.h.ListTaskListPartitions(ctx, proto.ToListTaskListPartitionsRequest(request)) return proto.FromListTaskListPartitionsResponse(response), proto.FromError(err) } func (g APIHandler) ListWorkflowExecutions(ctx context.Context, request *apiv1.ListWorkflowExecutionsRequest) (*apiv1.ListWorkflowExecutionsResponse, error) { response, err := g.h.ListWorkflowExecutions(ctx, proto.ToListWorkflowExecutionsRequest(request)) return proto.FromListWorkflowExecutionsResponse(response), proto.FromError(err) } func (g APIHandler) PollForActivityTask(ctx context.Context, request *apiv1.PollForActivityTaskRequest) (*apiv1.PollForActivityTaskResponse, error) { response, err := g.h.PollForActivityTask(ctx, proto.ToPollForActivityTaskRequest(request)) return proto.FromPollForActivityTaskResponse(response), proto.FromError(err) } func (g APIHandler) PollForDecisionTask(ctx context.Context, request *apiv1.PollForDecisionTaskRequest) (*apiv1.PollForDecisionTaskResponse, error) { response, err := g.h.PollForDecisionTask(ctx, proto.ToPollForDecisionTaskRequest(request)) return proto.FromPollForDecisionTaskResponse(response), proto.FromError(err) } func (g APIHandler) QueryWorkflow(ctx context.Context, request *apiv1.QueryWorkflowRequest) (*apiv1.QueryWorkflowResponse, error) { response, err := g.h.QueryWorkflow(ctx, proto.ToQueryWorkflowRequest(request)) return proto.FromQueryWorkflowResponse(response), proto.FromError(err) } func (g APIHandler) RecordActivityTaskHeartbeat(ctx context.Context, request *apiv1.RecordActivityTaskHeartbeatRequest) (*apiv1.RecordActivityTaskHeartbeatResponse, error) { response, err := g.h.RecordActivityTaskHeartbeat(ctx, proto.ToRecordActivityTaskHeartbeatRequest(request)) return proto.FromRecordActivityTaskHeartbeatResponse(response), proto.FromError(err) } func (g APIHandler) RecordActivityTaskHeartbeatByID(ctx context.Context, request *apiv1.RecordActivityTaskHeartbeatByIDRequest) (*apiv1.RecordActivityTaskHeartbeatByIDResponse, error) { response, err := g.h.RecordActivityTaskHeartbeatByID(ctx, proto.ToRecordActivityTaskHeartbeatByIDRequest(request)) return proto.FromRecordActivityTaskHeartbeatByIDResponse(response), proto.FromError(err) } func (g APIHandler) RefreshWorkflowTasks(ctx context.Context, request *apiv1.RefreshWorkflowTasksRequest) (*apiv1.RefreshWorkflowTasksResponse, error) { err := g.h.RefreshWorkflowTasks(ctx, proto.ToRefreshWorkflowTasksRequest(request)) return &apiv1.RefreshWorkflowTasksResponse{}, proto.FromError(err) } func (g APIHandler) RegisterDomain(ctx context.Context, request *apiv1.RegisterDomainRequest) (*apiv1.RegisterDomainResponse, error) { err := g.h.RegisterDomain(ctx, proto.ToRegisterDomainRequest(request)) return &apiv1.RegisterDomainResponse{}, proto.FromError(err) } func (g APIHandler) RequestCancelWorkflowExecution(ctx context.Context, request *apiv1.RequestCancelWorkflowExecutionRequest) (*apiv1.RequestCancelWorkflowExecutionResponse, error) { err := g.h.RequestCancelWorkflowExecution(ctx, proto.ToRequestCancelWorkflowExecutionRequest(request)) return &apiv1.RequestCancelWorkflowExecutionResponse{}, proto.FromError(err) } func (g APIHandler) ResetStickyTaskList(ctx context.Context, request *apiv1.ResetStickyTaskListRequest) (*apiv1.ResetStickyTaskListResponse, error) { response, err := g.h.ResetStickyTaskList(ctx, proto.ToResetStickyTaskListRequest(request)) return proto.FromResetStickyTaskListResponse(response), proto.FromError(err) } func (g APIHandler) ResetWorkflowExecution(ctx context.Context, request *apiv1.ResetWorkflowExecutionRequest) (*apiv1.ResetWorkflowExecutionResponse, error) { response, err := g.h.ResetWorkflowExecution(ctx, proto.ToResetWorkflowExecutionRequest(request)) return proto.FromResetWorkflowExecutionResponse(response), proto.FromError(err) } func (g APIHandler) RespondActivityTaskCanceled(ctx context.Context, request *apiv1.RespondActivityTaskCanceledRequest) (*apiv1.RespondActivityTaskCanceledResponse, error) { err := g.h.RespondActivityTaskCanceled(ctx, proto.ToRespondActivityTaskCanceledRequest(request)) return &apiv1.RespondActivityTaskCanceledResponse{}, proto.FromError(err) } func (g APIHandler) RespondActivityTaskCanceledByID(ctx context.Context, request *apiv1.RespondActivityTaskCanceledByIDRequest) (*apiv1.RespondActivityTaskCanceledByIDResponse, error) { err := g.h.RespondActivityTaskCanceledByID(ctx, proto.ToRespondActivityTaskCanceledByIDRequest(request)) return &apiv1.RespondActivityTaskCanceledByIDResponse{}, proto.FromError(err) } func (g APIHandler) RespondActivityTaskCompleted(ctx context.Context, request *apiv1.RespondActivityTaskCompletedRequest) (*apiv1.RespondActivityTaskCompletedResponse, error) { err := g.h.RespondActivityTaskCompleted(ctx, proto.ToRespondActivityTaskCompletedRequest(request)) return &apiv1.RespondActivityTaskCompletedResponse{}, proto.FromError(err) } func (g APIHandler) RespondActivityTaskCompletedByID(ctx context.Context, request *apiv1.RespondActivityTaskCompletedByIDRequest) (*apiv1.RespondActivityTaskCompletedByIDResponse, error) { err := g.h.RespondActivityTaskCompletedByID(ctx, proto.ToRespondActivityTaskCompletedByIDRequest(request)) return &apiv1.RespondActivityTaskCompletedByIDResponse{}, proto.FromError(err) } func (g APIHandler) RespondActivityTaskFailed(ctx context.Context, request *apiv1.RespondActivityTaskFailedRequest) (*apiv1.RespondActivityTaskFailedResponse, error) { err := g.h.RespondActivityTaskFailed(ctx, proto.ToRespondActivityTaskFailedRequest(request)) return &apiv1.RespondActivityTaskFailedResponse{}, proto.FromError(err) } func (g APIHandler) RespondActivityTaskFailedByID(ctx context.Context, request *apiv1.RespondActivityTaskFailedByIDRequest) (*apiv1.RespondActivityTaskFailedByIDResponse, error) { err := g.h.RespondActivityTaskFailedByID(ctx, proto.ToRespondActivityTaskFailedByIDRequest(request)) return &apiv1.RespondActivityTaskFailedByIDResponse{}, proto.FromError(err) } func (g APIHandler) RespondDecisionTaskCompleted(ctx context.Context, request *apiv1.RespondDecisionTaskCompletedRequest) (*apiv1.RespondDecisionTaskCompletedResponse, error) { response, err := g.h.RespondDecisionTaskCompleted(ctx, proto.ToRespondDecisionTaskCompletedRequest(request)) return proto.FromRespondDecisionTaskCompletedResponse(response), proto.FromError(err) } func (g APIHandler) RespondDecisionTaskFailed(ctx context.Context, request *apiv1.RespondDecisionTaskFailedRequest) (*apiv1.RespondDecisionTaskFailedResponse, error) { err := g.h.RespondDecisionTaskFailed(ctx, proto.ToRespondDecisionTaskFailedRequest(request)) return &apiv1.RespondDecisionTaskFailedResponse{}, proto.FromError(err) } func (g APIHandler) RespondQueryTaskCompleted(ctx context.Context, request *apiv1.RespondQueryTaskCompletedRequest) (*apiv1.RespondQueryTaskCompletedResponse, error) { err := g.h.RespondQueryTaskCompleted(ctx, proto.ToRespondQueryTaskCompletedRequest(request)) return &apiv1.RespondQueryTaskCompletedResponse{}, proto.FromError(err) } func (g APIHandler) RestartWorkflowExecution(ctx context.Context, request *apiv1.RestartWorkflowExecutionRequest) (*apiv1.RestartWorkflowExecutionResponse, error) { response, err := g.h.RestartWorkflowExecution(ctx, proto.ToRestartWorkflowExecutionRequest(request)) return proto.FromRestartWorkflowExecutionResponse(response), proto.FromError(err) } func (g APIHandler) ScanWorkflowExecutions(ctx context.Context, request *apiv1.ScanWorkflowExecutionsRequest) (*apiv1.ScanWorkflowExecutionsResponse, error) { response, err := g.h.ScanWorkflowExecutions(ctx, proto.ToScanWorkflowExecutionsRequest(request)) return proto.FromScanWorkflowExecutionsResponse(response), proto.FromError(err) } func (g APIHandler) SignalWithStartWorkflowExecution(ctx context.Context, request *apiv1.SignalWithStartWorkflowExecutionRequest) (*apiv1.SignalWithStartWorkflowExecutionResponse, error) { response, err := g.h.SignalWithStartWorkflowExecution(ctx, proto.ToSignalWithStartWorkflowExecutionRequest(request)) return proto.FromSignalWithStartWorkflowExecutionResponse(response), proto.FromError(err) } func (g APIHandler) SignalWithStartWorkflowExecutionAsync(ctx context.Context, request *apiv1.SignalWithStartWorkflowExecutionAsyncRequest) (*apiv1.SignalWithStartWorkflowExecutionAsyncResponse, error) { response, err := g.h.SignalWithStartWorkflowExecutionAsync(ctx, proto.ToSignalWithStartWorkflowExecutionAsyncRequest(request)) return proto.FromSignalWithStartWorkflowExecutionAsyncResponse(response), proto.FromError(err) } func (g APIHandler) SignalWorkflowExecution(ctx context.Context, request *apiv1.SignalWorkflowExecutionRequest) (*apiv1.SignalWorkflowExecutionResponse, error) { err := g.h.SignalWorkflowExecution(ctx, proto.ToSignalWorkflowExecutionRequest(request)) return &apiv1.SignalWorkflowExecutionResponse{}, proto.FromError(err) } func (g APIHandler) StartWorkflowExecution(ctx context.Context, request *apiv1.StartWorkflowExecutionRequest) (*apiv1.StartWorkflowExecutionResponse, error) { response, err := g.h.StartWorkflowExecution(ctx, proto.ToStartWorkflowExecutionRequest(request)) return proto.FromStartWorkflowExecutionResponse(response), proto.FromError(err) } func (g APIHandler) StartWorkflowExecutionAsync(ctx context.Context, request *apiv1.StartWorkflowExecutionAsyncRequest) (*apiv1.StartWorkflowExecutionAsyncResponse, error) { response, err := g.h.StartWorkflowExecutionAsync(ctx, proto.ToStartWorkflowExecutionAsyncRequest(request)) return proto.FromStartWorkflowExecutionAsyncResponse(response), proto.FromError(err) } func (g APIHandler) TerminateWorkflowExecution(ctx context.Context, request *apiv1.TerminateWorkflowExecutionRequest) (*apiv1.TerminateWorkflowExecutionResponse, error) { err := g.h.TerminateWorkflowExecution(ctx, proto.ToTerminateWorkflowExecutionRequest(request)) return &apiv1.TerminateWorkflowExecutionResponse{}, proto.FromError(err) } func (g APIHandler) UpdateDomain(ctx context.Context, request *apiv1.UpdateDomainRequest) (*apiv1.UpdateDomainResponse, error) { response, err := g.h.UpdateDomain(ctx, proto.ToUpdateDomainRequest(request)) return proto.FromUpdateDomainResponse(response), proto.FromError(err) } ================================================ FILE: service/frontend/wrappers/grpc/share.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package grpc import ( "context" adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc" "github.com/uber/cadence/common/types/mapper/proto" ) func (g AdminHandler) Register(dispatcher *yarpc.Dispatcher) { dispatcher.Register(adminv1.BuildAdminAPIYARPCProcedures(g)) } func (g APIHandler) Register(dispatcher *yarpc.Dispatcher) { dispatcher.Register(apiv1.BuildDomainAPIYARPCProcedures(g)) dispatcher.Register(apiv1.BuildWorkflowAPIYARPCProcedures(g)) dispatcher.Register(apiv1.BuildWorkerAPIYARPCProcedures(g)) dispatcher.Register(apiv1.BuildVisibilityAPIYARPCProcedures(g)) dispatcher.Register(apiv1.BuildMetaAPIYARPCProcedures(g)) } func (g APIHandler) Health(ctx context.Context, request *apiv1.HealthRequest) (*apiv1.HealthResponse, error) { response, err := g.h.Health(ctx) return proto.FromHealthResponse(response), proto.FromError(err) } ================================================ FILE: service/frontend/wrappers/metered/api_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" "github.com/uber/cadence/service/frontend/config" ) // apiHandler frontend handler wrapper for authentication and authorization type apiHandler struct { handler api.Handler logger log.Logger metricsClient metrics.Client domainCache cache.DomainCache cfg *config.Config tokenSerializer common.TaskTokenSerializer } // NewAPIHandler creates frontend handler with metrics and logging func NewAPIHandler(handler api.Handler, logger log.Logger, metricsClient metrics.Client, domainCache cache.DomainCache, cfg *config.Config) api.Handler { return &apiHandler{ handler: handler, logger: logger, metricsClient: metricsClient, domainCache: domainCache, cfg: cfg, tokenSerializer: common.NewJSONTaskTokenSerializer(), } } func (h *apiHandler) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest) (cp2 *types.CountWorkflowExecutionsResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("CountWorkflowExecutions")} tags = append(tags, toCountWorkflowExecutionsRequestTags(cp1)...) scope := h.metricsClient.Scope(metrics.FrontendCountWorkflowExecutionsScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(cp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) cp2, err = h.handler.CountWorkflowExecutions(ctx, cp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return cp2, err } func (h *apiHandler) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("DeleteDomain")} scope := h.metricsClient.Scope(metrics.FrontendDeleteDomainScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainUnknownTag())...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.DeleteDomain(ctx, dp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("DeprecateDomain")} scope := h.metricsClient.Scope(metrics.FrontendDeprecateDomainScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainUnknownTag())...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.DeprecateDomain(ctx, dp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest) (dp2 *types.DescribeDomainResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("DescribeDomain")} scope := h.metricsClient.Scope(metrics.FrontendDescribeDomainScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainUnknownTag())...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) dp2, err = h.handler.DescribeDomain(ctx, dp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return dp2, err } func (h *apiHandler) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest) (dp2 *types.DescribeTaskListResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("DescribeTaskList")} tags = append(tags, toDescribeTaskListRequestTags(dp1)...) scope := h.metricsClient.Scope(metrics.FrontendDescribeTaskListScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(dp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) dp2, err = h.handler.DescribeTaskList(ctx, dp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return dp2, err } func (h *apiHandler) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("DescribeWorkflowExecution")} tags = append(tags, toDescribeWorkflowExecutionRequestTags(dp1)...) scope := h.metricsClient.Scope(metrics.FrontendDescribeWorkflowExecutionScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(dp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) dp2, err = h.handler.DescribeWorkflowExecution(ctx, dp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return dp2, err } func (h *apiHandler) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("DiagnoseWorkflowExecution")} tags = append(tags, toDiagnoseWorkflowExecutionRequestTags(dp1)...) scope := h.metricsClient.Scope(metrics.FrontendDiagnoseWorkflowExecutionScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(dp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) dp2, err = h.handler.DiagnoseWorkflowExecution(ctx, dp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return dp2, err } func (h *apiHandler) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest) (fp2 *types.FailoverDomainResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("FailoverDomain")} tags = append(tags, toFailoverDomainRequestTags(fp1)...) scope := h.metricsClient.Scope(metrics.FrontendFailoverDomainScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(fp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) fp2, err = h.handler.FailoverDomain(ctx, fp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return fp2, err } func (h *apiHandler) GetClusterInfo(ctx context.Context) (cp1 *types.ClusterInfo, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("GetClusterInfo")} scope := h.metricsClient.Scope(metrics.FrontendGetClusterInfoScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainUnknownTag())...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) cp1, err = h.handler.GetClusterInfo(ctx) if err != nil { return nil, h.handleErr(err, scope, logger) } return cp1, err } func (h *apiHandler) GetSearchAttributes(ctx context.Context) (gp1 *types.GetSearchAttributesResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("GetSearchAttributes")} scope := h.metricsClient.Scope(metrics.FrontendGetSearchAttributesScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainUnknownTag())...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) gp1, err = h.handler.GetSearchAttributes(ctx) if err != nil { return nil, h.handleErr(err, scope, logger) } return gp1, err } func (h *apiHandler) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest) (gp2 *types.GetTaskListsByDomainResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("GetTaskListsByDomain")} tags = append(tags, toGetTaskListsByDomainRequestTags(gp1)...) scope := h.metricsClient.Scope(metrics.FrontendGetTaskListsByDomainScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(gp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) gp2, err = h.handler.GetTaskListsByDomain(ctx, gp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return gp2, err } func (h *apiHandler) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("GetWorkflowExecutionHistory")} tags = append(tags, toGetWorkflowExecutionHistoryRequestTags(gp1)...) scope := h.metricsClient.Scope(metrics.FrontendGetWorkflowExecutionHistoryScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(gp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) gp2, err = h.handler.GetWorkflowExecutionHistory(ctx, gp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return gp2, err } func (h *apiHandler) Health(ctx context.Context) (hp1 *types.HealthStatus, err error) { return h.handler.Health(ctx) } func (h *apiHandler) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("ListArchivedWorkflowExecutions")} tags = append(tags, toListArchivedWorkflowExecutionsRequestTags(lp1)...) scope := h.metricsClient.Scope(metrics.FrontendListArchivedWorkflowExecutionsScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(lp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) lp2, err = h.handler.ListArchivedWorkflowExecutions(ctx, lp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return lp2, err } func (h *apiHandler) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("ListClosedWorkflowExecutions")} tags = append(tags, toListClosedWorkflowExecutionsRequestTags(lp1)...) scope := h.metricsClient.Scope(metrics.FrontendListClosedWorkflowExecutionsScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(lp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) lp2, err = h.handler.ListClosedWorkflowExecutions(ctx, lp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return lp2, err } func (h *apiHandler) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest) (lp2 *types.ListDomainsResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("ListDomains")} scope := h.metricsClient.Scope(metrics.FrontendListDomainsScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainUnknownTag())...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) lp2, err = h.handler.ListDomains(ctx, lp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return lp2, err } func (h *apiHandler) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest) (lp2 *types.ListFailoverHistoryResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("ListFailoverHistory")} scope := h.metricsClient.Scope(metrics.FrontendListFailoverHistoryScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainUnknownTag())...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) lp2, err = h.handler.ListFailoverHistory(ctx, lp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return lp2, err } func (h *apiHandler) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("ListOpenWorkflowExecutions")} tags = append(tags, toListOpenWorkflowExecutionsRequestTags(lp1)...) scope := h.metricsClient.Scope(metrics.FrontendListOpenWorkflowExecutionsScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(lp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) lp2, err = h.handler.ListOpenWorkflowExecutions(ctx, lp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return lp2, err } func (h *apiHandler) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest) (lp2 *types.ListTaskListPartitionsResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("ListTaskListPartitions")} tags = append(tags, toListTaskListPartitionsRequestTags(lp1)...) scope := h.metricsClient.Scope(metrics.FrontendListTaskListPartitionsScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(lp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) lp2, err = h.handler.ListTaskListPartitions(ctx, lp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return lp2, err } func (h *apiHandler) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest) (lp2 *types.ListWorkflowExecutionsResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("ListWorkflowExecutions")} tags = append(tags, toListWorkflowExecutionsRequestTags(lp1)...) scope := h.metricsClient.Scope(metrics.FrontendListWorkflowExecutionsScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(lp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) lp2, err = h.handler.ListWorkflowExecutions(ctx, lp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return lp2, err } func (h *apiHandler) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest) (pp2 *types.PollForActivityTaskResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("PollForActivityTask")} tags = append(tags, toPollForActivityTaskRequestTags(pp1)...) scope := common.NewPerTaskListScope(pp1.Domain, pp1.TaskList.GetName(), pp1.TaskList.GetKind(), h.metricsClient, metrics.FrontendPollForActivityTaskScope).Tagged(metrics.GetContextTags(ctx)...) scope.IncCounter(metrics.CadenceRequestsPerTaskListWithoutRollup) sw := scope.StartTimer(metrics.CadenceLatencyPerTaskList) defer sw.Stop() scopePerDomain := h.metricsClient.Scope(metrics.FrontendPollForActivityTaskScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(pp1.GetDomain()))...) scopePerDomain.IncCounter(metrics.CadenceRequests) swPerDomain := scopePerDomain.StartTimer(metrics.CadenceLatency) defer swPerDomain.Stop() logger := h.logger.WithTags(tags...) pp2, err = h.handler.PollForActivityTask(ctx, pp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return pp2, err } func (h *apiHandler) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest) (pp2 *types.PollForDecisionTaskResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("PollForDecisionTask")} tags = append(tags, toPollForDecisionTaskRequestTags(pp1)...) scope := common.NewPerTaskListScope(pp1.Domain, pp1.TaskList.GetName(), pp1.TaskList.GetKind(), h.metricsClient, metrics.FrontendPollForDecisionTaskScope).Tagged(metrics.GetContextTags(ctx)...) scope.IncCounter(metrics.CadenceRequestsPerTaskListWithoutRollup) sw := scope.StartTimer(metrics.CadenceLatencyPerTaskList) defer sw.Stop() scopePerDomain := h.metricsClient.Scope(metrics.FrontendPollForDecisionTaskScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(pp1.GetDomain()))...) scopePerDomain.IncCounter(metrics.CadenceRequests) swPerDomain := scopePerDomain.StartTimer(metrics.CadenceLatency) defer swPerDomain.Stop() logger := h.logger.WithTags(tags...) pp2, err = h.handler.PollForDecisionTask(ctx, pp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return pp2, err } func (h *apiHandler) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest) (qp2 *types.QueryWorkflowResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("QueryWorkflow")} tags = append(tags, toQueryWorkflowRequestTags(qp1)...) scope := h.metricsClient.Scope(metrics.FrontendQueryWorkflowScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(qp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) qp2, err = h.handler.QueryWorkflow(ctx, qp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return qp2, err } func (h *apiHandler) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RecordActivityTaskHeartbeat")} token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } tags = append(tags, tag.WorkflowDomainName(domainName), tag.WorkflowID(token.WorkflowID), tag.WorkflowRunID(token.RunID)) scope := h.metricsClient.Scope(metrics.FrontendRecordActivityTaskHeartbeatScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(domainName))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) rp2, err = h.handler.RecordActivityTaskHeartbeat(ctx, rp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return rp2, err } func (h *apiHandler) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RecordActivityTaskHeartbeatByID")} tags = append(tags, toRecordActivityTaskHeartbeatByIDRequestTags(rp1)...) scope := h.metricsClient.Scope(metrics.FrontendRecordActivityTaskHeartbeatByIDScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(rp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) rp2, err = h.handler.RecordActivityTaskHeartbeatByID(ctx, rp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return rp2, err } func (h *apiHandler) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RefreshWorkflowTasks")} tags = append(tags, toRefreshWorkflowTasksRequestTags(rp1)...) scope := h.metricsClient.Scope(metrics.FrontendRefreshWorkflowTasksScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(rp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RefreshWorkflowTasks(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RegisterDomain")} scope := h.metricsClient.Scope(metrics.FrontendRegisterDomainScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainUnknownTag())...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RegisterDomain(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RequestCancelWorkflowExecution")} tags = append(tags, toRequestCancelWorkflowExecutionRequestTags(rp1)...) scope := h.metricsClient.Scope(metrics.FrontendRequestCancelWorkflowExecutionScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(rp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RequestCancelWorkflowExecution(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest) (rp2 *types.ResetStickyTaskListResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("ResetStickyTaskList")} tags = append(tags, toResetStickyTaskListRequestTags(rp1)...) scope := h.metricsClient.Scope(metrics.FrontendResetStickyTaskListScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(rp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) rp2, err = h.handler.ResetStickyTaskList(ctx, rp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return rp2, err } func (h *apiHandler) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest) (rp2 *types.ResetWorkflowExecutionResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("ResetWorkflowExecution")} tags = append(tags, toResetWorkflowExecutionRequestTags(rp1)...) scope := h.metricsClient.Scope(metrics.FrontendResetWorkflowExecutionScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(rp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) rp2, err = h.handler.ResetWorkflowExecution(ctx, rp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return rp2, err } func (h *apiHandler) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RespondActivityTaskCanceled")} token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } tags = append(tags, tag.WorkflowDomainName(domainName), tag.WorkflowID(token.WorkflowID), tag.WorkflowRunID(token.RunID)) scope := h.metricsClient.Scope(metrics.FrontendRespondActivityTaskCanceledScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(domainName))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RespondActivityTaskCanceled(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RespondActivityTaskCanceledByID")} tags = append(tags, toRespondActivityTaskCanceledByIDRequestTags(rp1)...) scope := h.metricsClient.Scope(metrics.FrontendRespondActivityTaskCanceledByIDScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(rp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RespondActivityTaskCanceledByID(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RespondActivityTaskCompleted")} token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } tags = append(tags, tag.WorkflowDomainName(domainName), tag.WorkflowID(token.WorkflowID), tag.WorkflowRunID(token.RunID)) scope := h.metricsClient.Scope(metrics.FrontendRespondActivityTaskCompletedScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(domainName))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RespondActivityTaskCompleted(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RespondActivityTaskCompletedByID")} tags = append(tags, toRespondActivityTaskCompletedByIDRequestTags(rp1)...) scope := h.metricsClient.Scope(metrics.FrontendRespondActivityTaskCompletedByIDScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(rp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RespondActivityTaskCompletedByID(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RespondActivityTaskFailed")} token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } tags = append(tags, tag.WorkflowDomainName(domainName), tag.WorkflowID(token.WorkflowID), tag.WorkflowRunID(token.RunID)) scope := h.metricsClient.Scope(metrics.FrontendRespondActivityTaskFailedScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(domainName))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RespondActivityTaskFailed(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RespondActivityTaskFailedByID")} tags = append(tags, toRespondActivityTaskFailedByIDRequestTags(rp1)...) scope := h.metricsClient.Scope(metrics.FrontendRespondActivityTaskFailedByIDScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(rp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RespondActivityTaskFailedByID(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RespondDecisionTaskCompleted")} token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } tags = append(tags, tag.WorkflowDomainName(domainName), tag.WorkflowID(token.WorkflowID), tag.WorkflowRunID(token.RunID)) scope := h.metricsClient.Scope(metrics.FrontendRespondDecisionTaskCompletedScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(domainName))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) rp2, err = h.handler.RespondDecisionTaskCompleted(ctx, rp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return rp2, err } func (h *apiHandler) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RespondDecisionTaskFailed")} token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } tags = append(tags, tag.WorkflowDomainName(domainName), tag.WorkflowID(token.WorkflowID), tag.WorkflowRunID(token.RunID)) scope := h.metricsClient.Scope(metrics.FrontendRespondDecisionTaskFailedScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(domainName))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RespondDecisionTaskFailed(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RespondQueryTaskCompleted")} token, err := h.tokenSerializer.DeserializeQueryTaskToken(rp1.TaskToken) if err != nil { return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } tags = append(tags, tag.WorkflowDomainName(domainName)) scope := h.metricsClient.Scope(metrics.FrontendRespondQueryTaskCompletedScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(domainName))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.RespondQueryTaskCompleted(ctx, rp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest) (rp2 *types.RestartWorkflowExecutionResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("RestartWorkflowExecution")} tags = append(tags, toRestartWorkflowExecutionRequestTags(rp1)...) scope := h.metricsClient.Scope(metrics.FrontendRestartWorkflowExecutionScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(rp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) rp2, err = h.handler.RestartWorkflowExecution(ctx, rp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return rp2, err } func (h *apiHandler) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest) (lp2 *types.ListWorkflowExecutionsResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("ScanWorkflowExecutions")} tags = append(tags, toScanWorkflowExecutionsRequestTags(lp1)...) scope := h.metricsClient.Scope(metrics.FrontendScanWorkflowExecutionsScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(lp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) lp2, err = h.handler.ScanWorkflowExecutions(ctx, lp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return lp2, err } func (h *apiHandler) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest) (sp2 *types.StartWorkflowExecutionResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("SignalWithStartWorkflowExecution")} tags = append(tags, toSignalWithStartWorkflowExecutionRequestTags(sp1)...) scope := h.metricsClient.Scope(metrics.FrontendSignalWithStartWorkflowExecutionScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(sp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) sp2, err = h.handler.SignalWithStartWorkflowExecution(ctx, sp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return sp2, err } func (h *apiHandler) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("SignalWithStartWorkflowExecutionAsync")} tags = append(tags, toSignalWithStartWorkflowExecutionAsyncRequestTags(sp1)...) scope := h.metricsClient.Scope(metrics.FrontendSignalWithStartWorkflowExecutionAsyncScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(sp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) sp2, err = h.handler.SignalWithStartWorkflowExecutionAsync(ctx, sp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return sp2, err } func (h *apiHandler) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() ctx = h.withSignalName(ctx, sp1.GetDomain(), sp1.GetSignalName()) tags := []tag.Tag{tag.WorkflowHandlerName("SignalWorkflowExecution")} tags = append(tags, toSignalWorkflowExecutionRequestTags(sp1)...) scope := h.metricsClient.Scope(metrics.FrontendSignalWorkflowExecutionScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(sp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.SignalWorkflowExecution(ctx, sp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest) (sp2 *types.StartWorkflowExecutionResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("StartWorkflowExecution")} tags = append(tags, toStartWorkflowExecutionRequestTags(sp1)...) scope := h.metricsClient.Scope(metrics.FrontendStartWorkflowExecutionScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(sp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) sp2, err = h.handler.StartWorkflowExecution(ctx, sp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return sp2, err } func (h *apiHandler) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("StartWorkflowExecutionAsync")} tags = append(tags, toStartWorkflowExecutionAsyncRequestTags(sp1)...) scope := h.metricsClient.Scope(metrics.FrontendStartWorkflowExecutionAsyncScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(sp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) sp2, err = h.handler.StartWorkflowExecutionAsync(ctx, sp1) if err != nil { return nil, h.handleErr(err, scope, logger) } return sp2, err } func (h *apiHandler) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("TerminateWorkflowExecution")} tags = append(tags, toTerminateWorkflowExecutionRequestTags(tp1)...) scope := h.metricsClient.Scope(metrics.FrontendTerminateWorkflowExecutionScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainTag(tp1.GetDomain()))...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) err = h.handler.TerminateWorkflowExecution(ctx, tp1) if err != nil { return h.handleErr(err, scope, logger) } return err } func (h *apiHandler) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest) (up2 *types.UpdateDomainResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() tags := []tag.Tag{tag.WorkflowHandlerName("UpdateDomain")} scope := h.metricsClient.Scope(metrics.FrontendUpdateDomainScope).Tagged(append(metrics.GetContextTags(ctx), metrics.DomainUnknownTag())...) scope.IncCounter(metrics.CadenceRequests) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() logger := h.logger.WithTags(tags...) up2, err = h.handler.UpdateDomain(ctx, up1) if err != nil { return nil, h.handleErr(err, scope, logger) } return up2, err } ================================================ FILE: service/frontend/wrappers/metered/metered.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metered import ( "context" "errors" "fmt" "go.uber.org/yarpc/yarpcerrors" "github.com/uber/cadence/common/constants" commonerrors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) func (h *apiHandler) handleErr(err error, scope metrics.Scope, logger log.Logger) error { logger = logger.Helper() // attempt to extract hostname if possible hostname, err := commonerrors.ExtractPeerHostname(err) if hostname != "" { logger = logger.WithTags(tag.PeerHostname(hostname)) } switch err := err.(type) { case *types.InternalServiceError: logger.Error("Internal service error", tag.Error(err)) scope.IncCounter(metrics.CadenceFailures) return frontendInternalServiceError("cadence internal error, msg: %v", err.Message) case *types.BadRequestError: scope.IncCounter(metrics.CadenceErrBadRequestCounter) return err case *types.DomainNotActiveError: scope.IncCounter(metrics.CadenceErrBadRequestCounter) return err case *types.ServiceBusyError: scope.IncCounter(metrics.CadenceErrServiceBusyCounter) return err case *types.EntityNotExistsError: scope.IncCounter(metrics.CadenceErrEntityNotExistsCounter) return err case *types.WorkflowExecutionAlreadyCompletedError: scope.IncCounter(metrics.CadenceErrWorkflowExecutionAlreadyCompletedCounter) return err case *types.WorkflowExecutionAlreadyStartedError: scope.IncCounter(metrics.CadenceErrExecutionAlreadyStartedCounter) return err case *types.DomainAlreadyExistsError: scope.IncCounter(metrics.CadenceErrDomainAlreadyExistsCounter) return err case *types.CancellationAlreadyRequestedError: scope.IncCounter(metrics.CadenceErrCancellationAlreadyRequestedCounter) return err case *types.QueryFailedError: scope.IncCounter(metrics.CadenceErrQueryFailedCounter) return err case *types.LimitExceededError: scope.IncCounter(metrics.CadenceErrLimitExceededCounter) return err case *types.ClientVersionNotSupportedError: scope.IncCounter(metrics.CadenceErrClientVersionNotSupportedCounter) return err case *yarpcerrors.Status: if err.Code() == yarpcerrors.CodeDeadlineExceeded { logger.Error("Frontend request timedout", tag.Error(err)) scope.IncCounter(metrics.CadenceErrContextTimeoutCounter) return err } if err.Code() == yarpcerrors.CodeCancelled { scope.IncCounter(metrics.CadenceErrGRPCConnectionClosingCounter) logger.Warn(constants.GRPCConnectionClosingError, tag.Error(err)) return err } } if errors.Is(err, context.DeadlineExceeded) { logger.Error("Frontend request timedout", tag.Error(err)) scope.IncCounter(metrics.CadenceErrContextTimeoutCounter) return err } logger.Error("Uncategorized error", tag.Error(err)) scope.IncCounter(metrics.CadenceFailures) return frontendInternalServiceError("cadence internal uncategorized error, msg: %v", err.Error()) } func (h *apiHandler) withSignalName( ctx context.Context, domainName string, signalName string, ) context.Context { if h.cfg.EmitSignalNameMetricsTag(domainName) { return metrics.TagContext(ctx, metrics.SignalNameTag(signalName)) } return ctx } func frontendInternalServiceError(fmtStr string, args ...interface{}) error { // NOTE: For internal error, we can't return thrift error from cadence-frontend. // Because in uber internal metrics, thrift errors are counted as user errors. return fmt.Errorf(fmtStr, args...) } func toCountWorkflowExecutionsRequestTags(req *types.CountWorkflowExecutionsRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), } } func toDescribeTaskListRequestTags(req *types.DescribeTaskListRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowTaskListName(req.GetTaskList().GetName()), tag.WorkflowTaskListType(int(req.GetTaskListType())), tag.WorkflowTaskListKind(int32(req.GetTaskList().GetKind())), } } func toDescribeWorkflowExecutionRequestTags(req *types.DescribeWorkflowExecutionRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetExecution().GetRunID()), } } func toDiagnoseWorkflowExecutionRequestTags(req *types.DiagnoseWorkflowExecutionRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetWorkflowExecution().GetRunID()), } } func toGetTaskListsByDomainRequestTags(req *types.GetTaskListsByDomainRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), } } func toGetWorkflowExecutionHistoryRequestTags(req *types.GetWorkflowExecutionHistoryRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetWorkflowExecution().GetRunID()), } } func toListArchivedWorkflowExecutionsRequestTags(req *types.ListArchivedWorkflowExecutionsRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), } } func toListClosedWorkflowExecutionsRequestTags(req *types.ListClosedWorkflowExecutionsRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), } } func toListOpenWorkflowExecutionsRequestTags(req *types.ListOpenWorkflowExecutionsRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), } } func toListTaskListPartitionsRequestTags(req *types.ListTaskListPartitionsRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowTaskListName(req.GetTaskList().GetName()), tag.WorkflowTaskListKind(int32(req.GetTaskList().GetKind())), } } func toListWorkflowExecutionsRequestTags(req *types.ListWorkflowExecutionsRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), } } func toPollForActivityTaskRequestTags(req *types.PollForActivityTaskRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowTaskListName(req.GetTaskList().GetName()), tag.WorkflowTaskListKind(int32(req.GetTaskList().GetKind())), } } func toPollForDecisionTaskRequestTags(req *types.PollForDecisionTaskRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowTaskListName(req.GetTaskList().GetName()), tag.WorkflowTaskListKind(int32(req.GetTaskList().GetKind())), } } func toQueryWorkflowRequestTags(req *types.QueryWorkflowRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetExecution().GetRunID()), } } func toRecordActivityTaskHeartbeatByIDRequestTags(req *types.RecordActivityTaskHeartbeatByIDRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowID()), tag.WorkflowRunID(req.GetRunID()), } } func toRefreshWorkflowTasksRequestTags(req *types.RefreshWorkflowTasksRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetExecution().GetRunID()), } } func toRequestCancelWorkflowExecutionRequestTags(req *types.RequestCancelWorkflowExecutionRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetWorkflowExecution().GetRunID()), } } func toResetStickyTaskListRequestTags(req *types.ResetStickyTaskListRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetExecution().GetRunID()), } } func toResetWorkflowExecutionRequestTags(req *types.ResetWorkflowExecutionRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetWorkflowExecution().GetRunID()), } } func toRespondActivityTaskCanceledByIDRequestTags(req *types.RespondActivityTaskCanceledByIDRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowID()), tag.WorkflowRunID(req.GetRunID()), } } func toRespondActivityTaskCompletedByIDRequestTags(req *types.RespondActivityTaskCompletedByIDRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowID()), tag.WorkflowRunID(req.GetRunID()), } } func toRespondActivityTaskFailedByIDRequestTags(req *types.RespondActivityTaskFailedByIDRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowID()), tag.WorkflowRunID(req.GetRunID()), } } func toRestartWorkflowExecutionRequestTags(req *types.RestartWorkflowExecutionRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetWorkflowExecution().GetRunID()), } } func toSignalWithStartWorkflowExecutionRequestTags(req *types.SignalWithStartWorkflowExecutionRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowID()), tag.WorkflowType(req.WorkflowType.GetName()), tag.WorkflowSignalName(req.GetSignalName()), } } func toSignalWithStartWorkflowExecutionAsyncRequestTags(req *types.SignalWithStartWorkflowExecutionAsyncRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowID()), tag.WorkflowType(req.WorkflowType.GetName()), tag.WorkflowSignalName(req.GetSignalName()), } } func toSignalWorkflowExecutionRequestTags(req *types.SignalWorkflowExecutionRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetWorkflowExecution().GetRunID()), tag.WorkflowSignalName(req.GetSignalName()), } } func toStartWorkflowExecutionRequestTags(req *types.StartWorkflowExecutionRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowID()), tag.WorkflowType(req.WorkflowType.GetName()), tag.WorkflowCronSchedule(req.GetCronSchedule()), } } func toStartWorkflowExecutionAsyncRequestTags(req *types.StartWorkflowExecutionAsyncRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowID()), tag.WorkflowType(req.WorkflowType.GetName()), tag.WorkflowCronSchedule(req.GetCronSchedule()), } } func toTerminateWorkflowExecutionRequestTags(req *types.TerminateWorkflowExecutionRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), tag.WorkflowID(req.GetWorkflowExecution().GetWorkflowID()), tag.WorkflowRunID(req.GetWorkflowExecution().GetRunID()), } } func toScanWorkflowExecutionsRequestTags(req *types.ListWorkflowExecutionsRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), } } func toFailoverDomainRequestTags(req *types.FailoverDomainRequest) []tag.Tag { return []tag.Tag{ tag.WorkflowDomainName(req.GetDomain()), } } ================================================ FILE: service/frontend/wrappers/metered/metered_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metered import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "go.uber.org/yarpc/yarpcerrors" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" "github.com/uber/cadence/service/frontend/config" ) func TestContextMetricsTags(t *testing.T) { ctrl := gomock.NewController(t) mockHandler := api.NewMockHandler(ctrl) mockHandler.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.CountWorkflowExecutionsResponse{}, nil).Times(1) mockDomainCache := cache.NewMockDomainCache(ctrl) testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.Frontend, metrics.HistogramMigration{}) handler := NewAPIHandler(mockHandler, testlogger.New(t), metricsClient, mockDomainCache, nil) tag := metrics.TransportTag("grpc") ctx := metrics.TagContext(context.Background(), tag) handler.CountWorkflowExecutions(ctx, nil) snapshot := testScope.Snapshot() for _, counter := range snapshot.Counters() { if counter.Name() == "test.cadence_requests" { assert.Equal(t, tag.Value(), counter.Tags()[tag.Key()]) return } } assert.Fail(t, "counter not found") } func TestSignalMetricHasSignalName(t *testing.T) { ctrl := gomock.NewController(t) mockHandler := api.NewMockHandler(ctrl) mockHandler.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) mockDomainCache := cache.NewMockDomainCache(ctrl) testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.Frontend, metrics.HistogramMigration{}) handler := NewAPIHandler(mockHandler, testlogger.New(t), metricsClient, mockDomainCache, &config.Config{EmitSignalNameMetricsTag: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true)}) signalRequest := &types.SignalWorkflowExecutionRequest{ SignalName: "test_signal", } err := handler.SignalWorkflowExecution(context.Background(), signalRequest) if err != nil { return } expectedMetrics := make(map[string]bool) expectedMetrics["test.cadence_requests"] = false snapshot := testScope.Snapshot() for _, counter := range snapshot.Counters() { if _, ok := expectedMetrics[counter.Name()]; ok { expectedMetrics[counter.Name()] = true } if val, ok := counter.Tags()["signalName"]; ok { assert.Equal(t, val, "test_signal") } else { assert.Fail(t, "Couldn't find signalName tag") } } assert.True(t, expectedMetrics["test.cadence_requests"]) } func TestHandleErr_InternalServiceError(t *testing.T) { logger := testlogger.New(t) testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.Frontend, metrics.HistogramMigration{}) handler := &apiHandler{} err := handler.handleErr(&types.InternalServiceError{Message: "internal error"}, metricsClient.Scope(0), logger) assert.NotNil(t, err) assert.Contains(t, err.Error(), "cadence internal error") } func TestHandleErr_ClientConnectionClosingError(t *testing.T) { logger := testlogger.New(t) testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.Frontend, metrics.HistogramMigration{}) handler := &apiHandler{} err := handler.handleErr(errors.New(constants.GRPCConnectionClosingError), metricsClient.Scope(0), logger) assert.NotNil(t, err) assert.Contains(t, err.Error(), constants.GRPCConnectionClosingError) } func TestHandleErr_UncategorizedError(t *testing.T) { logger := testlogger.New(t) testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.Frontend, metrics.HistogramMigration{}) handler := &apiHandler{} err := handler.handleErr(errors.New("unknown error"), metricsClient.Scope(0), logger) assert.NotNil(t, err) assert.Contains(t, err.Error(), "cadence internal uncategorized error") } func TestToSignalWithStartWorkflowExecutionRequestTags(t *testing.T) { req := &types.SignalWithStartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, SignalName: "test-signal", } tags := toSignalWithStartWorkflowExecutionRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowType("test-workflow-type"), tag.WorkflowSignalName("test-signal"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToDescribeWorkflowExecutionRequestTags(t *testing.T) { req := &types.DescribeWorkflowExecutionRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } tags := toDescribeWorkflowExecutionRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToStartWorkflowExecutionRequestTags(t *testing.T) { req := &types.StartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, CronSchedule: "test-cron-schedule", } tags := toStartWorkflowExecutionRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowType("test-workflow-type"), tag.WorkflowCronSchedule("test-cron-schedule"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToStartWorkflowExecutionAsyncRequestTags(t *testing.T) { req := &types.StartWorkflowExecutionAsyncRequest{ StartWorkflowExecutionRequest: &types.StartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, CronSchedule: "test-cron-schedule", }, } tags := toStartWorkflowExecutionAsyncRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowType("test-workflow-type"), tag.WorkflowCronSchedule("test-cron-schedule"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToTerminateWorkflowExecutionRequestTags(t *testing.T) { req := &types.TerminateWorkflowExecutionRequest{ Domain: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } tags := toTerminateWorkflowExecutionRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToScanWorkflowExecutionsRequestTags(t *testing.T) { req := &types.ListWorkflowExecutionsRequest{ Domain: "test-domain", } tags := toScanWorkflowExecutionsRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToListTaskListPartitionsRequestTags(t *testing.T) { kind := types.TaskListKindNormal req := &types.ListTaskListPartitionsRequest{ Domain: "test-domain", TaskList: &types.TaskList{ Name: "test-task-list", Kind: &kind, }, } tags := toListTaskListPartitionsRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowTaskListName("test-task-list"), tag.WorkflowTaskListKind(int32(types.TaskListKindNormal)), } assert.ElementsMatch(t, expectedTags, tags) } func TestToGetTaskListsByDomainRequestTags(t *testing.T) { req := &types.GetTaskListsByDomainRequest{ Domain: "test-domain", } tags := toGetTaskListsByDomainRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToGetWorkflowExecutionHistoryRequestTags(t *testing.T) { req := &types.GetWorkflowExecutionHistoryRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } tags := toGetWorkflowExecutionHistoryRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToListArchivedWorkflowExecutionsRequestTags(t *testing.T) { req := &types.ListArchivedWorkflowExecutionsRequest{ Domain: "test-domain", } tags := toListArchivedWorkflowExecutionsRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToListClosedWorkflowExecutionsRequestTags(t *testing.T) { req := &types.ListClosedWorkflowExecutionsRequest{ Domain: "test-domain", } tags := toListClosedWorkflowExecutionsRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToListOpenWorkflowExecutionsRequestTags(t *testing.T) { req := &types.ListOpenWorkflowExecutionsRequest{ Domain: "test-domain", } tags := toListOpenWorkflowExecutionsRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToRestartWorkflowExecutionRequestTags(t *testing.T) { req := &types.RestartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } tags := toRestartWorkflowExecutionRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToRespondActivityTaskFailedByIDRequestTags(t *testing.T) { req := &types.RespondActivityTaskFailedByIDRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", RunID: "test-run-id", } tags := toRespondActivityTaskFailedByIDRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToRespondActivityTaskCompletedByIDRequestTags(t *testing.T) { req := &types.RespondActivityTaskCompletedByIDRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", RunID: "test-run-id", } tags := toRespondActivityTaskCompletedByIDRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToRespondActivityTaskCanceledByIDRequestTags(t *testing.T) { req := &types.RespondActivityTaskCanceledByIDRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", RunID: "test-run-id", } tags := toRespondActivityTaskCanceledByIDRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToResetWorkflowExecutionRequestTags(t *testing.T) { req := &types.ResetWorkflowExecutionRequest{ Domain: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } tags := toResetWorkflowExecutionRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToResetStickyTaskListRequestTags(t *testing.T) { req := &types.ResetStickyTaskListRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } tags := toResetStickyTaskListRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToRequestCancelWorkflowExecutionRequestTags(t *testing.T) { req := &types.RequestCancelWorkflowExecutionRequest{ Domain: "test-domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } tags := toRequestCancelWorkflowExecutionRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToRefreshWorkflowTasksRequestTags(t *testing.T) { req := &types.RefreshWorkflowTasksRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } tags := toRefreshWorkflowTasksRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToRecordActivityTaskHeartbeatByIDRequestTags(t *testing.T) { req := &types.RecordActivityTaskHeartbeatByIDRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", RunID: "test-run-id", } tags := toRecordActivityTaskHeartbeatByIDRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToQueryWorkflowRequestTags(t *testing.T) { req := &types.QueryWorkflowRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } tags := toQueryWorkflowRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowRunID("test-run-id"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToPollForDecisionTaskRequestTags(t *testing.T) { kind := types.TaskListKindNormal req := &types.PollForDecisionTaskRequest{ Domain: "test-domain", TaskList: &types.TaskList{ Name: "test-task-list", Kind: &kind, }, } tags := toPollForDecisionTaskRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowTaskListName("test-task-list"), tag.WorkflowTaskListKind(int32(types.TaskListKindNormal)), } assert.ElementsMatch(t, expectedTags, tags) } func TestToListWorkflowExecutionsRequestTags(t *testing.T) { req := &types.ListWorkflowExecutionsRequest{ Domain: "test-domain", } tags := toListWorkflowExecutionsRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToPollForActivityTaskRequestTags(t *testing.T) { kind := types.TaskListKindNormal req := &types.PollForActivityTaskRequest{ Domain: "test-domain", TaskList: &types.TaskList{ Name: "test-task-list", Kind: &kind, }, } tags := toPollForActivityTaskRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowTaskListName("test-task-list"), tag.WorkflowTaskListKind(int32(types.TaskListKindNormal)), } assert.ElementsMatch(t, expectedTags, tags) } func TestToSignalWithStartWorkflowExecutionAsyncRequestTags(t *testing.T) { req := &types.SignalWithStartWorkflowExecutionAsyncRequest{ SignalWithStartWorkflowExecutionRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: "test-domain", WorkflowID: "test-workflow-id", WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, SignalName: "test-signal", }, } tags := toSignalWithStartWorkflowExecutionAsyncRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowID("test-workflow-id"), tag.WorkflowType("test-workflow-type"), tag.WorkflowSignalName("test-signal"), } assert.ElementsMatch(t, expectedTags, tags) } func TestToDescribeTaskListRequestTags(t *testing.T) { kind := types.TaskListKindNormal taskListType := types.TaskListTypeDecision req := &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{ Name: "test-task-list", Kind: &kind, }, TaskListType: &taskListType, } tags := toDescribeTaskListRequestTags(req) expectedTags := []tag.Tag{ tag.WorkflowDomainName("test-domain"), tag.WorkflowTaskListName("test-task-list"), tag.WorkflowTaskListType(int(taskListType)), tag.WorkflowTaskListKind(int32(types.TaskListKindNormal)), } assert.ElementsMatch(t, expectedTags, tags) } func TestHandleErr(t *testing.T) { tests := []struct { name string err error expectedErrType interface{} expectedErrMsg string expectedCounter string }{ { name: "BadRequestError", err: &types.BadRequestError{Message: "bad request"}, expectedErrType: &types.BadRequestError{}, expectedErrMsg: "bad request", expectedCounter: "test.cadence_err_bad_request_counter+", }, { name: "DomainNotActiveError", err: &types.DomainNotActiveError{Message: "domain not active"}, expectedErrType: &types.DomainNotActiveError{}, expectedErrMsg: "domain not active", expectedCounter: "test.cadence_err_bad_request_counter+", }, { name: "ServiceBusyError", err: &types.ServiceBusyError{Message: "service busy"}, expectedErrType: &types.ServiceBusyError{}, expectedErrMsg: "service busy", expectedCounter: "test.cadence_err_service_busy_counter+", }, { name: "EntityNotExistsError", err: &types.EntityNotExistsError{Message: "entity not exists"}, expectedErrType: &types.EntityNotExistsError{}, expectedErrMsg: "entity not exists", expectedCounter: "test.cadence_err_entity_not_exists_counter+", }, { name: "WorkflowExecutionAlreadyCompletedError", err: &types.WorkflowExecutionAlreadyCompletedError{Message: "workflow execution already completed"}, expectedErrType: &types.WorkflowExecutionAlreadyCompletedError{}, expectedErrMsg: "workflow execution already completed", expectedCounter: "test.cadence_err_workflow_execution_already_completed_counter+", }, { name: "WorkflowExecutionAlreadyStartedError", err: &types.WorkflowExecutionAlreadyStartedError{Message: "workflow execution already started"}, expectedErrType: &types.WorkflowExecutionAlreadyStartedError{}, expectedErrMsg: "workflow execution already started", expectedCounter: "test.cadence_err_execution_already_started_counter+", }, { name: "DomainAlreadyExistsError", err: &types.DomainAlreadyExistsError{Message: "domain already exists"}, expectedErrType: &types.DomainAlreadyExistsError{}, expectedErrMsg: "domain already exists", expectedCounter: "test.cadence_err_domain_already_exists_counter+", }, { name: "CancellationAlreadyRequestedError", err: &types.CancellationAlreadyRequestedError{Message: "cancellation already requested"}, expectedErrType: &types.CancellationAlreadyRequestedError{}, expectedErrMsg: "cancellation already requested", expectedCounter: "test.cadence_err_cancellation_already_requested_counter+", }, { name: "QueryFailedError", err: &types.QueryFailedError{Message: "query failed"}, expectedErrType: &types.QueryFailedError{}, expectedErrMsg: "query failed", expectedCounter: "test.cadence_err_query_failed_counter+", }, { name: "LimitExceededError", err: &types.LimitExceededError{Message: "limit exceeded"}, expectedErrType: &types.LimitExceededError{}, expectedErrMsg: "limit exceeded", expectedCounter: "test.cadence_err_limit_exceeded_counter+", }, { name: "ClientVersionNotSupportedError", err: &types.ClientVersionNotSupportedError{}, expectedErrType: &types.ClientVersionNotSupportedError{}, expectedErrMsg: "client version not supported", expectedCounter: "test.cadence_err_client_version_not_supported_counter+", }, { name: "YARPCDeadlineExceededError", err: yarpcerrors.Newf(yarpcerrors.CodeDeadlineExceeded, "deadline exceeded"), expectedErrType: &yarpcerrors.Status{}, expectedErrMsg: "deadline exceeded", expectedCounter: "test.cadence_err_context_timeout_counter+", }, { name: "ContextDeadlineExceeded", err: context.DeadlineExceeded, expectedErrType: context.DeadlineExceeded, expectedErrMsg: "context deadline exceeded", expectedCounter: "test.cadence_err_context_timeout_counter+", }, { name: "UncategorizedError", err: errors.New("unknown error"), expectedErrType: errors.New("unknown error"), expectedErrMsg: "unknown error", expectedCounter: "test.cadence_failures+", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { logger := testlogger.New(t) testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.Frontend, metrics.HistogramMigration{}) handler := &apiHandler{} err := handler.handleErr(tt.err, metricsClient.Scope(0), logger) assert.NotNil(t, err) assert.IsType(t, tt.expectedErrType, err) assert.Contains(t, err.Error(), tt.expectedErrMsg) }) } } ================================================ FILE: service/frontend/wrappers/ratelimited/api_generated.go ================================================ package ratelimited // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/ratelimited.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" "github.com/uber/cadence/service/frontend/validate" ) // apiHandler implements api.Handler interface instrumented with rate limiter. type apiHandler struct { wrapped api.Handler tokenSerializer common.TaskTokenSerializer domainCache cache.DomainCache userRateLimiter quotas.Policy workerRateLimiter quotas.Policy visibilityRateLimiter quotas.Policy asyncRateLimiter quotas.Policy maxWorkerPollDelay dynamicproperties.DurationPropertyFnWithDomainFilter callerBypass quotas.CallerBypass } // NewAPIHandler creates a new instance of Handler with ratelimiter. func NewAPIHandler( wrapped api.Handler, domainCache cache.DomainCache, userRateLimiter quotas.Policy, workerRateLimiter quotas.Policy, visibilityRateLimiter quotas.Policy, asyncRateLimiter quotas.Policy, maxWorkerPollDelay dynamicproperties.DurationPropertyFnWithDomainFilter, callerBypass quotas.CallerBypass, ) api.Handler { return &apiHandler{ wrapped: wrapped, tokenSerializer: common.NewJSONTaskTokenSerializer(), domainCache: domainCache, userRateLimiter: userRateLimiter, workerRateLimiter: workerRateLimiter, visibilityRateLimiter: visibilityRateLimiter, asyncRateLimiter: asyncRateLimiter, maxWorkerPollDelay: maxWorkerPollDelay, callerBypass: callerBypass, } } func (h *apiHandler) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest) (cp2 *types.CountWorkflowExecutionsResponse, err error) { if cp1 == nil { err = validate.ErrRequestNotSet return } if cp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeVisibility, cp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.CountWorkflowExecutions(ctx, cp1) } func (h *apiHandler) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest) (err error) { return h.wrapped.DeleteDomain(ctx, dp1) } func (h *apiHandler) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest) (err error) { return h.wrapped.DeprecateDomain(ctx, dp1) } func (h *apiHandler) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest) (dp2 *types.DescribeDomainResponse, err error) { return h.wrapped.DescribeDomain(ctx, dp1) } func (h *apiHandler) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest) (dp2 *types.DescribeTaskListResponse, err error) { if dp1 == nil { err = validate.ErrRequestNotSet return } if dp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, dp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.DescribeTaskList(ctx, dp1) } func (h *apiHandler) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { if dp1 == nil { err = validate.ErrRequestNotSet return } if dp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, dp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.DescribeWorkflowExecution(ctx, dp1) } func (h *apiHandler) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { if dp1 == nil { err = validate.ErrRequestNotSet return } if dp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, dp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.DiagnoseWorkflowExecution(ctx, dp1) } func (h *apiHandler) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest) (fp2 *types.FailoverDomainResponse, err error) { if fp1 == nil { err = validate.ErrRequestNotSet return } if fp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, fp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.FailoverDomain(ctx, fp1) } func (h *apiHandler) GetClusterInfo(ctx context.Context) (cp1 *types.ClusterInfo, err error) { return h.wrapped.GetClusterInfo(ctx) } func (h *apiHandler) GetSearchAttributes(ctx context.Context) (gp1 *types.GetSearchAttributesResponse, err error) { return h.wrapped.GetSearchAttributes(ctx) } func (h *apiHandler) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest) (gp2 *types.GetTaskListsByDomainResponse, err error) { if gp1 == nil { err = validate.ErrRequestNotSet return } if gp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, gp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.GetTaskListsByDomain(ctx, gp1) } func (h *apiHandler) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { if gp1 == nil { err = validate.ErrRequestNotSet return } if gp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, gp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.GetWorkflowExecutionHistory(ctx, gp1) } func (h *apiHandler) Health(ctx context.Context) (hp1 *types.HealthStatus, err error) { return h.wrapped.Health(ctx) } func (h *apiHandler) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { if lp1 == nil { err = validate.ErrRequestNotSet return } if lp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeVisibility, lp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.ListArchivedWorkflowExecutions(ctx, lp1) } func (h *apiHandler) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { if lp1 == nil { err = validate.ErrRequestNotSet return } if lp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeVisibility, lp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.ListClosedWorkflowExecutions(ctx, lp1) } func (h *apiHandler) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest) (lp2 *types.ListDomainsResponse, err error) { return h.wrapped.ListDomains(ctx, lp1) } func (h *apiHandler) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest) (lp2 *types.ListFailoverHistoryResponse, err error) { return h.wrapped.ListFailoverHistory(ctx, lp1) } func (h *apiHandler) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { if lp1 == nil { err = validate.ErrRequestNotSet return } if lp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeVisibility, lp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.ListOpenWorkflowExecutions(ctx, lp1) } func (h *apiHandler) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest) (lp2 *types.ListTaskListPartitionsResponse, err error) { if lp1 == nil { err = validate.ErrRequestNotSet return } if lp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, lp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.ListTaskListPartitions(ctx, lp1) } func (h *apiHandler) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest) (lp2 *types.ListWorkflowExecutionsResponse, err error) { if lp1 == nil { err = validate.ErrRequestNotSet return } if lp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeVisibility, lp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.ListWorkflowExecutions(ctx, lp1) } func (h *apiHandler) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest) (pp2 *types.PollForActivityTaskResponse, err error) { if pp1 == nil { err = validate.ErrRequestNotSet return } if pp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeWorkerPoll, pp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.PollForActivityTask(ctx, pp1) } func (h *apiHandler) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest) (pp2 *types.PollForDecisionTaskResponse, err error) { if pp1 == nil { err = validate.ErrRequestNotSet return } if pp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeWorkerPoll, pp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.PollForDecisionTask(ctx, pp1) } func (h *apiHandler) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest) (qp2 *types.QueryWorkflowResponse, err error) { if qp1 == nil { err = validate.ErrRequestNotSet return } if qp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, qp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.QueryWorkflow(ctx, qp1) } func (h *apiHandler) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.TaskToken == nil { err = validate.ErrTaskTokenNotSet return } token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } if token.DomainID == "" { err = validate.ErrDomainNotSet return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, domainName) return h.wrapped.RecordActivityTaskHeartbeat(ctx, rp1) } func (h *apiHandler) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, rp1.GetDomain()) return h.wrapped.RecordActivityTaskHeartbeatByID(ctx, rp1) } func (h *apiHandler) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest) (err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, rp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.RefreshWorkflowTasks(ctx, rp1) } func (h *apiHandler) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest) (err error) { return h.wrapped.RegisterDomain(ctx, rp1) } func (h *apiHandler) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest) (err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, rp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.RequestCancelWorkflowExecution(ctx, rp1) } func (h *apiHandler) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest) (rp2 *types.ResetStickyTaskListResponse, err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, rp1.GetDomain()) return h.wrapped.ResetStickyTaskList(ctx, rp1) } func (h *apiHandler) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest) (rp2 *types.ResetWorkflowExecutionResponse, err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, rp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.ResetWorkflowExecution(ctx, rp1) } func (h *apiHandler) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest) (err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.TaskToken == nil { err = validate.ErrTaskTokenNotSet return } token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } if token.DomainID == "" { err = validate.ErrDomainNotSet return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, domainName) return h.wrapped.RespondActivityTaskCanceled(ctx, rp1) } func (h *apiHandler) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest) (err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, rp1.GetDomain()) return h.wrapped.RespondActivityTaskCanceledByID(ctx, rp1) } func (h *apiHandler) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest) (err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.TaskToken == nil { err = validate.ErrTaskTokenNotSet return } token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } if token.DomainID == "" { err = validate.ErrDomainNotSet return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, domainName) return h.wrapped.RespondActivityTaskCompleted(ctx, rp1) } func (h *apiHandler) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest) (err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, rp1.GetDomain()) return h.wrapped.RespondActivityTaskCompletedByID(ctx, rp1) } func (h *apiHandler) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest) (err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.TaskToken == nil { err = validate.ErrTaskTokenNotSet return } token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } if token.DomainID == "" { err = validate.ErrDomainNotSet return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, domainName) return h.wrapped.RespondActivityTaskFailed(ctx, rp1) } func (h *apiHandler) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest) (err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, rp1.GetDomain()) return h.wrapped.RespondActivityTaskFailedByID(ctx, rp1) } func (h *apiHandler) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.TaskToken == nil { err = validate.ErrTaskTokenNotSet return } token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } if token.DomainID == "" { err = validate.ErrDomainNotSet return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, domainName) return h.wrapped.RespondDecisionTaskCompleted(ctx, rp1) } func (h *apiHandler) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest) (err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.TaskToken == nil { err = validate.ErrTaskTokenNotSet return } token, err := h.tokenSerializer.Deserialize(rp1.TaskToken) if err != nil { return } if token.DomainID == "" { err = validate.ErrDomainNotSet return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, domainName) return h.wrapped.RespondDecisionTaskFailed(ctx, rp1) } func (h *apiHandler) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest) (err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.TaskToken == nil { err = validate.ErrTaskTokenNotSet return } token, err := h.tokenSerializer.DeserializeQueryTaskToken(rp1.TaskToken) if err != nil { return } if token.DomainID == "" { err = validate.ErrDomainNotSet return } domainName, err := h.domainCache.GetDomainName(token.DomainID) if err != nil { return } // Count the request in the host RPS, // but we still accept it even if RPS is exceeded h.allowDomain(ctx, ratelimitTypeWorker, domainName) return h.wrapped.RespondQueryTaskCompleted(ctx, rp1) } func (h *apiHandler) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest) (rp2 *types.RestartWorkflowExecutionResponse, err error) { if rp1 == nil { err = validate.ErrRequestNotSet return } if rp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, rp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.RestartWorkflowExecution(ctx, rp1) } func (h *apiHandler) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest) (lp2 *types.ListWorkflowExecutionsResponse, err error) { if lp1 == nil { err = validate.ErrRequestNotSet return } if lp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeVisibility, lp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.ScanWorkflowExecutions(ctx, lp1) } func (h *apiHandler) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest) (sp2 *types.StartWorkflowExecutionResponse, err error) { if sp1 == nil { err = validate.ErrRequestNotSet return } if sp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, sp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.SignalWithStartWorkflowExecution(ctx, sp1) } func (h *apiHandler) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { if sp1 == nil { err = validate.ErrRequestNotSet return } if sp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeAsync, sp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.SignalWithStartWorkflowExecutionAsync(ctx, sp1) } func (h *apiHandler) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest) (err error) { if sp1 == nil { err = validate.ErrRequestNotSet return } if sp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, sp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.SignalWorkflowExecution(ctx, sp1) } func (h *apiHandler) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest) (sp2 *types.StartWorkflowExecutionResponse, err error) { if sp1 == nil { err = validate.ErrRequestNotSet return } if sp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, sp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.StartWorkflowExecution(ctx, sp1) } func (h *apiHandler) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { if sp1 == nil { err = validate.ErrRequestNotSet return } if sp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeAsync, sp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.StartWorkflowExecutionAsync(ctx, sp1) } func (h *apiHandler) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest) (err error) { if tp1 == nil { err = validate.ErrRequestNotSet return } if tp1.GetDomain() == "" { err = validate.ErrDomainNotSet return } if limitErr := h.allowDomain(ctx, ratelimitTypeUser, tp1.GetDomain()); limitErr != nil { err = limitErr return } return h.wrapped.TerminateWorkflowExecution(ctx, tp1) } func (h *apiHandler) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest) (up2 *types.UpdateDomainResponse, err error) { return h.wrapped.UpdateDomain(ctx, up1) } ================================================ FILE: service/frontend/wrappers/ratelimited/ratelimit.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ratelimited import ( "context" "time" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" ) // ratelimitType differentiates between the three categories of ratelimiters type ratelimitType int const ( ratelimitTypeUser ratelimitType = iota + 1 ratelimitTypeWorker ratelimitTypeWorkerPoll ratelimitTypeVisibility ratelimitTypeAsync ) var errRateLimited = types.ServiceBusyError{Message: "Too many outstanding requests to the cadence service"} func newErrRateLimited() error { return &errRateLimited } func (h *apiHandler) allowDomain(ctx context.Context, requestType ratelimitType, domain string) error { var policy quotas.Policy switch requestType { case ratelimitTypeUser: policy = h.userRateLimiter case ratelimitTypeWorker, ratelimitTypeWorkerPoll: policy = h.workerRateLimiter case ratelimitTypeVisibility: policy = h.visibilityRateLimiter case ratelimitTypeAsync: policy = h.asyncRateLimiter default: panic("coding error, unrecognized request ratelimit type value") } // If it is a poll request and there is a maxWorkerPollDelay configured, use Wait() // to potentially wait for a token. Otherwise, use Allow() for an immediate check if requestType == ratelimitTypeWorkerPoll { waitTime := h.maxWorkerPollDelay(domain) if waitTime > 0 { return h.waitForPolicy(ctx, waitTime, policy, domain) } } if !h.callerBypass.AllowPolicy(ctx, policy, quotas.Info{Domain: domain}) { return newErrRateLimited() } return nil } func (h *apiHandler) waitForPolicy(ctx context.Context, waitTime time.Duration, policy quotas.Policy, domain string) error { waitCtx, cancel := context.WithTimeout(ctx, waitTime) defer cancel() err := policy.Wait(waitCtx, quotas.Info{Domain: domain}) if err != nil { if ctx.Err() != nil { return ctx.Err() } waitCtxErr := waitCtx.Err() switch waitCtxErr { case nil: if h.callerBypass.ShouldBypass(ctx) { return nil } return newErrRateLimited() case context.DeadlineExceeded: // Race condition: context deadline hit right around wait completion if !h.callerBypass.AllowPolicy(ctx, policy, quotas.Info{Domain: domain}) { return newErrRateLimited() } return nil default: return waitCtxErr } } return nil } ================================================ FILE: service/frontend/wrappers/ratelimited/ratelimit_test.go ================================================ package ratelimited import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" ) const testDomain = "test-domain" func setupHandler(t *testing.T) *apiHandler { return setupHandlerWithDC(t, nil) } func setupHandlerWithDC(t *testing.T, dc *dynamicconfig.Collection) *apiHandler { ctrl := gomock.NewController(t) mockHandler := api.NewMockHandler(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) var callerBypass quotas.CallerBypass if dc != nil { callerBypass = quotas.NewCallerBypass(dc.GetListProperty(dynamicproperties.RateLimiterBypassCallerTypes)) } else { callerBypass = quotas.NewCallerBypass(nil) } return NewAPIHandler( mockHandler, mockDomainCache, &mockPolicy{}, // userRateLimiter &mockPolicy{}, // workerRateLimiter &mockPolicy{}, // visibilityRateLimiter &mockPolicy{}, // asyncRateLimiter func(domain string) time.Duration { return 0 }, // maxWorkerPollDelay callerBypass, ).(*apiHandler) } func TestAllowDomainRequestRouting(t *testing.T) { cases := []struct { name string requestType ratelimitType setupMock func(*apiHandler) expectedErr error }{ { name: "User request type uses user limiter with Allow", requestType: ratelimitTypeUser, setupMock: func(h *apiHandler) { h.userRateLimiter.(*mockPolicy).On("Allow", quotas.Info{Domain: testDomain}).Return(true).Once() }, expectedErr: nil, }, { name: "Worker request type uses worker limiter with Allow", requestType: ratelimitTypeWorker, setupMock: func(h *apiHandler) { h.workerRateLimiter.(*mockPolicy).On("Allow", quotas.Info{Domain: testDomain}).Return(true).Once() }, expectedErr: nil, }, { name: "Visibility request type uses visibility limiter with Allow", requestType: ratelimitTypeVisibility, setupMock: func(h *apiHandler) { h.visibilityRateLimiter.(*mockPolicy).On("Allow", quotas.Info{Domain: testDomain}).Return(true).Once() }, expectedErr: nil, }, { name: "WorkerPoll request with zero delay uses worker limiter with Allow", requestType: ratelimitTypeWorkerPoll, setupMock: func(h *apiHandler) { h.maxWorkerPollDelay = func(domain string) time.Duration { return 0 } h.workerRateLimiter.(*mockPolicy).On("Allow", quotas.Info{Domain: testDomain}).Return(true).Once() }, expectedErr: nil, }, { name: "WorkerPoll request with delay uses worker limiter with Wait", requestType: ratelimitTypeWorkerPoll, setupMock: func(h *apiHandler) { h.maxWorkerPollDelay = func(domain string) time.Duration { return 5 * time.Second } h.workerRateLimiter.(*mockPolicy).On("Wait", mock.Anything, quotas.Info{Domain: testDomain}).Return(nil).Once() }, expectedErr: nil, }, { name: "Allow blocking returns rate limit error", requestType: ratelimitTypeUser, setupMock: func(h *apiHandler) { h.userRateLimiter.(*mockPolicy).On("Allow", quotas.Info{Domain: testDomain}).Return(false).Once() }, expectedErr: newErrRateLimited(), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { handler := setupHandler(t) tc.setupMock(handler) err := handler.allowDomain(context.Background(), tc.requestType, testDomain) if tc.expectedErr != nil { assert.Error(t, err) if tc.expectedErr != nil { assert.Equal(t, tc.expectedErr, err) } } else { assert.NoError(t, err) } handler.userRateLimiter.(*mockPolicy).AssertExpectations(t) handler.workerRateLimiter.(*mockPolicy).AssertExpectations(t) handler.visibilityRateLimiter.(*mockPolicy).AssertExpectations(t) }) } } func TestHandlerRpcRouting(t *testing.T) { cases := []struct { name string operation func(*apiHandler) (interface{}, error) limiterSetup func(*apiHandler) }{ { name: "PollForActivityTask uses worker limiter with Allow when no delay is configured", operation: func(h *apiHandler) (interface{}, error) { return h.PollForActivityTask(context.Background(), &types.PollForActivityTaskRequest{Domain: testDomain}) }, limiterSetup: func(h *apiHandler) { h.maxWorkerPollDelay = func(domain string) time.Duration { return 0 } h.workerRateLimiter.(*mockPolicy).On("Allow", quotas.Info{Domain: testDomain}).Return(true).Once() h.wrapped.(*api.MockHandler).EXPECT().PollForActivityTask(gomock.Any(), gomock.Any()).Return(&types.PollForActivityTaskResponse{}, nil).Times(1) }, }, { name: "PollForActivityTask uses worker limiter with Wait when delay is configured", operation: func(h *apiHandler) (interface{}, error) { return h.PollForActivityTask(context.Background(), &types.PollForActivityTaskRequest{Domain: testDomain}) }, limiterSetup: func(h *apiHandler) { h.maxWorkerPollDelay = func(domain string) time.Duration { return 1 * time.Millisecond } h.workerRateLimiter.(*mockPolicy).On("Wait", mock.Anything, quotas.Info{Domain: testDomain}).Return(nil).Once() h.wrapped.(*api.MockHandler).EXPECT().PollForActivityTask(gomock.Any(), gomock.Any()).Return(&types.PollForActivityTaskResponse{}, nil).Times(1) }, }, { name: "CountWorkflowExecutions uses visibility limiter with Allow", operation: func(h *apiHandler) (interface{}, error) { return h.CountWorkflowExecutions(context.Background(), &types.CountWorkflowExecutionsRequest{Domain: testDomain}) }, limiterSetup: func(h *apiHandler) { h.visibilityRateLimiter.(*mockPolicy).On("Allow", quotas.Info{Domain: testDomain}).Return(true).Once() h.wrapped.(*api.MockHandler).EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.CountWorkflowExecutionsResponse{}, nil).Times(1) }, }, { name: "DescribeTaskList uses user limiter with Allow", operation: func(h *apiHandler) (interface{}, error) { return h.DescribeTaskList(context.Background(), &types.DescribeTaskListRequest{ Domain: testDomain, TaskList: &types.TaskList{Name: "test-tasklist"}, }) }, limiterSetup: func(h *apiHandler) { h.userRateLimiter.(*mockPolicy).On("Allow", quotas.Info{Domain: testDomain}).Return(true).Once() h.wrapped.(*api.MockHandler).EXPECT().DescribeTaskList(gomock.Any(), gomock.Any()).Return(&types.DescribeTaskListResponse{}, nil).Times(1) }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { handler := setupHandler(t) tc.limiterSetup(handler) resp, err := tc.operation(handler) assert.NoError(t, err) assert.NotNil(t, resp) handler.userRateLimiter.(*mockPolicy).AssertExpectations(t) handler.workerRateLimiter.(*mockPolicy).AssertExpectations(t) handler.visibilityRateLimiter.(*mockPolicy).AssertExpectations(t) }) } } type mockPolicy struct { mock.Mock } func (m *mockPolicy) Allow(info quotas.Info) bool { args := m.Called(info) return args.Bool(0) } func (m *mockPolicy) Wait(ctx context.Context, info quotas.Info) error { args := m.Called(ctx, info) return args.Error(0) } func TestCallerTypeBypass(t *testing.T) { tests := []struct { name string bypassCallerTypes []interface{} callerType types.CallerType rateLimitBlocks bool expectBypass bool }{ { name: "Bypass CLI caller when configured", bypassCallerTypes: []interface{}{"cli"}, callerType: types.CallerTypeCLI, rateLimitBlocks: true, expectBypass: true, }, { name: "Bypass UI caller when configured", bypassCallerTypes: []interface{}{"ui"}, callerType: types.CallerTypeUI, rateLimitBlocks: true, expectBypass: true, }, { name: "Don't bypass when caller type not in list", bypassCallerTypes: []interface{}{"cli"}, callerType: types.CallerTypeUI, rateLimitBlocks: true, expectBypass: false, }, { name: "Don't bypass with empty list", bypassCallerTypes: []interface{}{}, callerType: types.CallerTypeCLI, rateLimitBlocks: true, expectBypass: false, }, { name: "Multiple caller types in bypass list", bypassCallerTypes: []interface{}{"cli", "ui"}, callerType: types.CallerTypeUI, rateLimitBlocks: true, expectBypass: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { client := dynamicconfig.NewInMemoryClient() client.UpdateValue(dynamicproperties.RateLimiterBypassCallerTypes, tt.bypassCallerTypes) dc := dynamicconfig.NewCollection(client, testlogger.New(t)) handler := setupHandlerWithDC(t, dc) ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(tt.callerType)) if tt.rateLimitBlocks { handler.userRateLimiter.(*mockPolicy).On("Allow", quotas.Info{Domain: testDomain}).Return(false).Once() } else { handler.userRateLimiter.(*mockPolicy).On("Allow", quotas.Info{Domain: testDomain}).Return(true).Once() } err := handler.allowDomain(ctx, ratelimitTypeUser, testDomain) if tt.expectBypass { assert.NoError(t, err, "Expected bypass to allow request") } else { if tt.rateLimitBlocks { assert.Error(t, err, "Expected rate limit error") assert.Equal(t, newErrRateLimited(), err) } else { assert.NoError(t, err) } } handler.userRateLimiter.(*mockPolicy).AssertExpectations(t) }) } } func TestCallerTypeBypassWithWait(t *testing.T) { tests := []struct { name string bypassCallerTypes []interface{} callerType types.CallerType expectBypass bool }{ { name: "Bypass in Wait nil error path when configured", bypassCallerTypes: []interface{}{"cli"}, callerType: types.CallerTypeCLI, expectBypass: true, }, { name: "Don't bypass in Wait nil error path when not configured", bypassCallerTypes: []interface{}{}, callerType: types.CallerTypeCLI, expectBypass: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { client := dynamicconfig.NewInMemoryClient() client.UpdateValue(dynamicproperties.RateLimiterBypassCallerTypes, tt.bypassCallerTypes) dc := dynamicconfig.NewCollection(client, testlogger.New(t)) handler := setupHandlerWithDC(t, dc) handler.maxWorkerPollDelay = func(domain string) time.Duration { return 1 * time.Millisecond } ctx := types.ContextWithCallerInfo(context.Background(), types.NewCallerInfo(tt.callerType)) // Wait returns an error, but waitCtx.Err() is nil (case nil path) handler.workerRateLimiter.(*mockPolicy).On("Wait", mock.Anything, quotas.Info{Domain: testDomain}).Return(assert.AnError).Once() err := handler.allowDomain(ctx, ratelimitTypeWorkerPoll, testDomain) if tt.expectBypass { assert.NoError(t, err, "Expected bypass to allow request") } else { assert.Error(t, err, "Expected rate limit error") assert.Equal(t, newErrRateLimited(), err) } handler.workerRateLimiter.(*mockPolicy).AssertExpectations(t) }) } } ================================================ FILE: service/frontend/wrappers/thrift/admin_generated.go ================================================ package thrift // Code generated by gowrap. DO NOT EDIT. // template: ../../../templates/thrift.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/.gen/go/admin" "github.com/uber/cadence/.gen/go/replicator" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/types/mapper/thrift" ) func (g AdminHandler) AddSearchAttribute(ctx context.Context, Request *admin.AddSearchAttributeRequest) (err error) { err = g.h.AddSearchAttribute(ctx, thrift.ToAdminAddSearchAttributeRequest(Request)) return thrift.FromError(err) } func (g AdminHandler) CloseShard(ctx context.Context, Request *shared.CloseShardRequest) (err error) { err = g.h.CloseShard(ctx, thrift.ToAdminCloseShardRequest(Request)) return thrift.FromError(err) } func (g AdminHandler) DeleteWorkflow(ctx context.Context, Request *admin.AdminDeleteWorkflowRequest) (ap1 *admin.AdminDeleteWorkflowResponse, err error) { response, err := g.h.DeleteWorkflow(ctx, thrift.ToAdminDeleteWorkflowRequest(Request)) return thrift.FromAdminDeleteWorkflowResponse(response), thrift.FromError(err) } func (g AdminHandler) DescribeCluster(ctx context.Context) (dp1 *admin.DescribeClusterResponse, err error) { response, err := g.h.DescribeCluster(ctx) return thrift.FromAdminDescribeClusterResponse(response), thrift.FromError(err) } func (g AdminHandler) DescribeHistoryHost(ctx context.Context, Request *shared.DescribeHistoryHostRequest) (dp1 *shared.DescribeHistoryHostResponse, err error) { response, err := g.h.DescribeHistoryHost(ctx, thrift.ToAdminDescribeHistoryHostRequest(Request)) return thrift.FromAdminDescribeHistoryHostResponse(response), thrift.FromError(err) } func (g AdminHandler) DescribeQueue(ctx context.Context, Request *shared.DescribeQueueRequest) (dp1 *shared.DescribeQueueResponse, err error) { response, err := g.h.DescribeQueue(ctx, thrift.ToAdminDescribeQueueRequest(Request)) return thrift.FromAdminDescribeQueueResponse(response), thrift.FromError(err) } func (g AdminHandler) DescribeShardDistribution(ctx context.Context, Request *shared.DescribeShardDistributionRequest) (dp1 *shared.DescribeShardDistributionResponse, err error) { response, err := g.h.DescribeShardDistribution(ctx, thrift.ToAdminDescribeShardDistributionRequest(Request)) return thrift.FromAdminDescribeShardDistributionResponse(response), thrift.FromError(err) } func (g AdminHandler) DescribeWorkflowExecution(ctx context.Context, Request *admin.DescribeWorkflowExecutionRequest) (dp1 *admin.DescribeWorkflowExecutionResponse, err error) { response, err := g.h.DescribeWorkflowExecution(ctx, thrift.ToAdminDescribeWorkflowExecutionRequest(Request)) return thrift.FromAdminDescribeWorkflowExecutionResponse(response), thrift.FromError(err) } func (g AdminHandler) GetCrossClusterTasks(ctx context.Context, Request *shared.GetCrossClusterTasksRequest) (gp1 *shared.GetCrossClusterTasksResponse, err error) { response, err := g.h.GetCrossClusterTasks(ctx, thrift.ToAdminGetCrossClusterTasksRequest(Request)) return thrift.FromAdminGetCrossClusterTasksResponse(response), thrift.FromError(err) } func (g AdminHandler) GetDLQReplicationMessages(ctx context.Context, Request *replicator.GetDLQReplicationMessagesRequest) (gp1 *replicator.GetDLQReplicationMessagesResponse, err error) { response, err := g.h.GetDLQReplicationMessages(ctx, thrift.ToAdminGetDLQReplicationMessagesRequest(Request)) return thrift.FromAdminGetDLQReplicationMessagesResponse(response), thrift.FromError(err) } func (g AdminHandler) GetDomainAsyncWorkflowConfiguraton(ctx context.Context, Request *admin.GetDomainAsyncWorkflowConfiguratonRequest) (gp1 *admin.GetDomainAsyncWorkflowConfiguratonResponse, err error) { response, err := g.h.GetDomainAsyncWorkflowConfiguraton(ctx, thrift.ToAdminGetDomainAsyncWorkflowConfiguratonRequest(Request)) return thrift.FromAdminGetDomainAsyncWorkflowConfiguratonResponse(response), thrift.FromError(err) } func (g AdminHandler) GetDomainIsolationGroups(ctx context.Context, Request *admin.GetDomainIsolationGroupsRequest) (gp1 *admin.GetDomainIsolationGroupsResponse, err error) { response, err := g.h.GetDomainIsolationGroups(ctx, thrift.ToAdminGetDomainIsolationGroupsRequest(Request)) return thrift.FromAdminGetDomainIsolationGroupsResponse(response), thrift.FromError(err) } func (g AdminHandler) GetDomainReplicationMessages(ctx context.Context, Request *replicator.GetDomainReplicationMessagesRequest) (gp1 *replicator.GetDomainReplicationMessagesResponse, err error) { response, err := g.h.GetDomainReplicationMessages(ctx, thrift.ToAdminGetDomainReplicationMessagesRequest(Request)) return thrift.FromAdminGetDomainReplicationMessagesResponse(response), thrift.FromError(err) } func (g AdminHandler) GetDynamicConfig(ctx context.Context, Request *admin.GetDynamicConfigRequest) (gp1 *admin.GetDynamicConfigResponse, err error) { response, err := g.h.GetDynamicConfig(ctx, thrift.ToAdminGetDynamicConfigRequest(Request)) return thrift.FromAdminGetDynamicConfigResponse(response), thrift.FromError(err) } func (g AdminHandler) GetGlobalIsolationGroups(ctx context.Context, Request *admin.GetGlobalIsolationGroupsRequest) (gp1 *admin.GetGlobalIsolationGroupsResponse, err error) { response, err := g.h.GetGlobalIsolationGroups(ctx, thrift.ToAdminGetGlobalIsolationGroupsRequest(Request)) return thrift.FromAdminGetGlobalIsolationGroupsResponse(response), thrift.FromError(err) } func (g AdminHandler) GetReplicationMessages(ctx context.Context, Request *replicator.GetReplicationMessagesRequest) (gp1 *replicator.GetReplicationMessagesResponse, err error) { response, err := g.h.GetReplicationMessages(ctx, thrift.ToAdminGetReplicationMessagesRequest(Request)) return thrift.FromAdminGetReplicationMessagesResponse(response), thrift.FromError(err) } func (g AdminHandler) GetWorkflowExecutionRawHistoryV2(ctx context.Context, GetRequest *admin.GetWorkflowExecutionRawHistoryV2Request) (gp1 *admin.GetWorkflowExecutionRawHistoryV2Response, err error) { response, err := g.h.GetWorkflowExecutionRawHistoryV2(ctx, thrift.ToAdminGetWorkflowExecutionRawHistoryV2Request(GetRequest)) return thrift.FromAdminGetWorkflowExecutionRawHistoryV2Response(response), thrift.FromError(err) } func (g AdminHandler) ListDynamicConfig(ctx context.Context, Request *admin.ListDynamicConfigRequest) (lp1 *admin.ListDynamicConfigResponse, err error) { response, err := g.h.ListDynamicConfig(ctx, thrift.ToAdminListDynamicConfigRequest(Request)) return thrift.FromAdminListDynamicConfigResponse(response), thrift.FromError(err) } func (g AdminHandler) MaintainCorruptWorkflow(ctx context.Context, Request *admin.AdminMaintainWorkflowRequest) (ap1 *admin.AdminMaintainWorkflowResponse, err error) { response, err := g.h.MaintainCorruptWorkflow(ctx, thrift.ToAdminMaintainCorruptWorkflowRequest(Request)) return thrift.FromAdminMaintainCorruptWorkflowResponse(response), thrift.FromError(err) } func (g AdminHandler) MergeDLQMessages(ctx context.Context, Request *replicator.MergeDLQMessagesRequest) (mp1 *replicator.MergeDLQMessagesResponse, err error) { response, err := g.h.MergeDLQMessages(ctx, thrift.ToAdminMergeDLQMessagesRequest(Request)) return thrift.FromAdminMergeDLQMessagesResponse(response), thrift.FromError(err) } func (g AdminHandler) PurgeDLQMessages(ctx context.Context, Request *replicator.PurgeDLQMessagesRequest) (err error) { err = g.h.PurgeDLQMessages(ctx, thrift.ToAdminPurgeDLQMessagesRequest(Request)) return thrift.FromError(err) } func (g AdminHandler) ReadDLQMessages(ctx context.Context, Request *replicator.ReadDLQMessagesRequest) (rp1 *replicator.ReadDLQMessagesResponse, err error) { response, err := g.h.ReadDLQMessages(ctx, thrift.ToAdminReadDLQMessagesRequest(Request)) return thrift.FromAdminReadDLQMessagesResponse(response), thrift.FromError(err) } func (g AdminHandler) ReapplyEvents(ctx context.Context, ReapplyEventsRequest *shared.ReapplyEventsRequest) (err error) { err = g.h.ReapplyEvents(ctx, thrift.ToAdminReapplyEventsRequest(ReapplyEventsRequest)) return thrift.FromError(err) } func (g AdminHandler) RefreshWorkflowTasks(ctx context.Context, Request *shared.RefreshWorkflowTasksRequest) (err error) { err = g.h.RefreshWorkflowTasks(ctx, thrift.ToAdminRefreshWorkflowTasksRequest(Request)) return thrift.FromError(err) } func (g AdminHandler) RemoveTask(ctx context.Context, Request *shared.RemoveTaskRequest) (err error) { err = g.h.RemoveTask(ctx, thrift.ToAdminRemoveTaskRequest(Request)) return thrift.FromError(err) } func (g AdminHandler) ResendReplicationTasks(ctx context.Context, Request *admin.ResendReplicationTasksRequest) (err error) { err = g.h.ResendReplicationTasks(ctx, thrift.ToAdminResendReplicationTasksRequest(Request)) return thrift.FromError(err) } func (g AdminHandler) ResetQueue(ctx context.Context, Request *shared.ResetQueueRequest) (err error) { err = g.h.ResetQueue(ctx, thrift.ToAdminResetQueueRequest(Request)) return thrift.FromError(err) } func (g AdminHandler) RespondCrossClusterTasksCompleted(ctx context.Context, Request *shared.RespondCrossClusterTasksCompletedRequest) (rp1 *shared.RespondCrossClusterTasksCompletedResponse, err error) { response, err := g.h.RespondCrossClusterTasksCompleted(ctx, thrift.ToAdminRespondCrossClusterTasksCompletedRequest(Request)) return thrift.FromAdminRespondCrossClusterTasksCompletedResponse(response), thrift.FromError(err) } func (g AdminHandler) RestoreDynamicConfig(ctx context.Context, Request *admin.RestoreDynamicConfigRequest) (err error) { err = g.h.RestoreDynamicConfig(ctx, thrift.ToAdminRestoreDynamicConfigRequest(Request)) return thrift.FromError(err) } func (g AdminHandler) UpdateDomainAsyncWorkflowConfiguraton(ctx context.Context, Request *admin.UpdateDomainAsyncWorkflowConfiguratonRequest) (up1 *admin.UpdateDomainAsyncWorkflowConfiguratonResponse, err error) { response, err := g.h.UpdateDomainAsyncWorkflowConfiguraton(ctx, thrift.ToAdminUpdateDomainAsyncWorkflowConfiguratonRequest(Request)) return thrift.FromAdminUpdateDomainAsyncWorkflowConfiguratonResponse(response), thrift.FromError(err) } func (g AdminHandler) UpdateDomainIsolationGroups(ctx context.Context, Request *admin.UpdateDomainIsolationGroupsRequest) (up1 *admin.UpdateDomainIsolationGroupsResponse, err error) { response, err := g.h.UpdateDomainIsolationGroups(ctx, thrift.ToAdminUpdateDomainIsolationGroupsRequest(Request)) return thrift.FromAdminUpdateDomainIsolationGroupsResponse(response), thrift.FromError(err) } func (g AdminHandler) UpdateDynamicConfig(ctx context.Context, Request *admin.UpdateDynamicConfigRequest) (err error) { err = g.h.UpdateDynamicConfig(ctx, thrift.ToAdminUpdateDynamicConfigRequest(Request)) return thrift.FromError(err) } func (g AdminHandler) UpdateGlobalIsolationGroups(ctx context.Context, Request *admin.UpdateGlobalIsolationGroupsRequest) (up1 *admin.UpdateGlobalIsolationGroupsResponse, err error) { response, err := g.h.UpdateGlobalIsolationGroups(ctx, thrift.ToAdminUpdateGlobalIsolationGroupsRequest(Request)) return thrift.FromAdminUpdateGlobalIsolationGroupsResponse(response), thrift.FromError(err) } ================================================ FILE: service/frontend/wrappers/thrift/admin_handler_test.go ================================================ // Copyright (c) 2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package thrift import ( "context" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/.gen/go/admin" "github.com/uber/cadence/.gen/go/replicator" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" adminHandler "github.com/uber/cadence/service/frontend/admin" ) func TestAdminThriftHandler(t *testing.T) { ctrl := gomock.NewController(t) h := adminHandler.NewMockHandler(ctrl) th := NewAdminHandler(h) ctx := context.Background() internalErr := &types.InternalServiceError{Message: "test"} expectedErr := &shared.InternalServiceError{Message: "test"} t.Run("AddSearchAttribute", func(t *testing.T) { h.EXPECT().AddSearchAttribute(ctx, &types.AddSearchAttributeRequest{}).Return(internalErr).Times(1) err := th.AddSearchAttribute(ctx, &admin.AddSearchAttributeRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("CloseShard", func(t *testing.T) { h.EXPECT().CloseShard(ctx, &types.CloseShardRequest{}).Return(internalErr).Times(1) err := th.CloseShard(ctx, &shared.CloseShardRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("DescribeCluster", func(t *testing.T) { h.EXPECT().DescribeCluster(ctx).Return(&types.DescribeClusterResponse{}, internalErr).Times(1) resp, err := th.DescribeCluster(ctx) assert.Equal(t, admin.DescribeClusterResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("DescribeHistoryHost", func(t *testing.T) { h.EXPECT().DescribeHistoryHost(ctx, &types.DescribeHistoryHostRequest{}).Return(&types.DescribeHistoryHostResponse{}, internalErr).Times(1) resp, err := th.DescribeHistoryHost(ctx, &shared.DescribeHistoryHostRequest{}) assert.Equal(t, shared.DescribeHistoryHostResponse{NumberOfShards: common.Int32Ptr(0), ShardControllerStatus: common.StringPtr(""), Address: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("DescribeQueue", func(t *testing.T) { h.EXPECT().DescribeQueue(ctx, &types.DescribeQueueRequest{}).Return(&types.DescribeQueueResponse{}, internalErr).Times(1) resp, err := th.DescribeQueue(ctx, &shared.DescribeQueueRequest{}) assert.Equal(t, shared.DescribeQueueResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("DescribeWorkflowExecution", func(t *testing.T) { h.EXPECT().DescribeWorkflowExecution(ctx, &types.AdminDescribeWorkflowExecutionRequest{}).Return(nil, internalErr).Times(1) resp, err := th.DescribeWorkflowExecution(ctx, &admin.DescribeWorkflowExecutionRequest{}) assert.Nil(t, resp) assert.Equal(t, expectedErr, err) }) t.Run("GetDLQReplicationMessages", func(t *testing.T) { h.EXPECT().GetDLQReplicationMessages(ctx, &types.GetDLQReplicationMessagesRequest{}).Return(&types.GetDLQReplicationMessagesResponse{}, internalErr).Times(1) resp, err := th.GetDLQReplicationMessages(ctx, &replicator.GetDLQReplicationMessagesRequest{}) assert.Equal(t, replicator.GetDLQReplicationMessagesResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("GetDomainReplicationMessages", func(t *testing.T) { h.EXPECT().GetDomainReplicationMessages(ctx, &types.GetDomainReplicationMessagesRequest{}).Return(&types.GetDomainReplicationMessagesResponse{}, internalErr).Times(1) resp, err := th.GetDomainReplicationMessages(ctx, &replicator.GetDomainReplicationMessagesRequest{}) assert.Equal(t, replicator.GetDomainReplicationMessagesResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("GetReplicationMessages", func(t *testing.T) { h.EXPECT().GetReplicationMessages(ctx, &types.GetReplicationMessagesRequest{}).Return(&types.GetReplicationMessagesResponse{}, internalErr).Times(1) resp, err := th.GetReplicationMessages(ctx, &replicator.GetReplicationMessagesRequest{}) assert.Equal(t, replicator.GetReplicationMessagesResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("GetWorkflowExecutionRawHistoryV2", func(t *testing.T) { h.EXPECT().GetWorkflowExecutionRawHistoryV2(ctx, &types.GetWorkflowExecutionRawHistoryV2Request{}).Return(&types.GetWorkflowExecutionRawHistoryV2Response{}, internalErr).Times(1) resp, err := th.GetWorkflowExecutionRawHistoryV2(ctx, &admin.GetWorkflowExecutionRawHistoryV2Request{}) assert.Equal(t, admin.GetWorkflowExecutionRawHistoryV2Response{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("MergeDLQMessages", func(t *testing.T) { h.EXPECT().MergeDLQMessages(ctx, &types.MergeDLQMessagesRequest{}).Return(&types.MergeDLQMessagesResponse{}, internalErr).Times(1) resp, err := th.MergeDLQMessages(ctx, &replicator.MergeDLQMessagesRequest{}) assert.Equal(t, replicator.MergeDLQMessagesResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("PurgeDLQMessages", func(t *testing.T) { h.EXPECT().PurgeDLQMessages(ctx, &types.PurgeDLQMessagesRequest{}).Return(internalErr).Times(1) err := th.PurgeDLQMessages(ctx, &replicator.PurgeDLQMessagesRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("ReadDLQMessages", func(t *testing.T) { h.EXPECT().ReadDLQMessages(ctx, &types.ReadDLQMessagesRequest{}).Return(&types.ReadDLQMessagesResponse{}, internalErr).Times(1) resp, err := th.ReadDLQMessages(ctx, &replicator.ReadDLQMessagesRequest{}) assert.Equal(t, replicator.ReadDLQMessagesResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ReapplyEvents", func(t *testing.T) { h.EXPECT().ReapplyEvents(ctx, &types.ReapplyEventsRequest{}).Return(internalErr).Times(1) err := th.ReapplyEvents(ctx, &shared.ReapplyEventsRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RefreshWorkflowTasks", func(t *testing.T) { h.EXPECT().RefreshWorkflowTasks(ctx, &types.RefreshWorkflowTasksRequest{}).Return(internalErr).Times(1) err := th.RefreshWorkflowTasks(ctx, &shared.RefreshWorkflowTasksRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RemoveTask", func(t *testing.T) { h.EXPECT().RemoveTask(ctx, &types.RemoveTaskRequest{}).Return(internalErr).Times(1) err := th.RemoveTask(ctx, &shared.RemoveTaskRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("ResendReplicationTasks", func(t *testing.T) { h.EXPECT().ResendReplicationTasks(ctx, &types.ResendReplicationTasksRequest{}).Return(internalErr).Times(1) err := th.ResendReplicationTasks(ctx, &admin.ResendReplicationTasksRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("ResetQueue", func(t *testing.T) { h.EXPECT().ResetQueue(ctx, &types.ResetQueueRequest{}).Return(internalErr).Times(1) err := th.ResetQueue(ctx, &shared.ResetQueueRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("GetCrossClusterTasks", func(t *testing.T) { h.EXPECT().GetCrossClusterTasks(ctx, &types.GetCrossClusterTasksRequest{}).Return(&types.GetCrossClusterTasksResponse{}, internalErr).Times(1) resp, err := th.GetCrossClusterTasks(ctx, &shared.GetCrossClusterTasksRequest{}) assert.Equal(t, shared.GetCrossClusterTasksResponse{}, *resp) assert.Equal(t, expectedErr, err) }) } ================================================ FILE: service/frontend/wrappers/thrift/api_generated.go ================================================ package thrift // Code generated by gowrap. DO NOT EDIT. // template: ../../../templates/thrift.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/types/mapper/thrift" ) func (g APIHandler) CountWorkflowExecutions(ctx context.Context, CountRequest *shared.CountWorkflowExecutionsRequest) (cp1 *shared.CountWorkflowExecutionsResponse, err error) { response, err := g.h.CountWorkflowExecutions(ctx, thrift.ToCountWorkflowExecutionsRequest(CountRequest)) return thrift.FromCountWorkflowExecutionsResponse(response), thrift.FromError(err) } func (g APIHandler) DeleteDomain(ctx context.Context, DeleteRequest *shared.DeleteDomainRequest) (err error) { err = g.h.DeleteDomain(ctx, thrift.ToDeleteDomainRequest(DeleteRequest)) return thrift.FromError(err) } func (g APIHandler) DeprecateDomain(ctx context.Context, DeprecateRequest *shared.DeprecateDomainRequest) (err error) { err = g.h.DeprecateDomain(ctx, thrift.ToDeprecateDomainRequest(DeprecateRequest)) return thrift.FromError(err) } func (g APIHandler) DescribeDomain(ctx context.Context, DescribeRequest *shared.DescribeDomainRequest) (dp1 *shared.DescribeDomainResponse, err error) { response, err := g.h.DescribeDomain(ctx, thrift.ToDescribeDomainRequest(DescribeRequest)) return thrift.FromDescribeDomainResponse(response), thrift.FromError(err) } func (g APIHandler) DescribeTaskList(ctx context.Context, Request *shared.DescribeTaskListRequest) (dp1 *shared.DescribeTaskListResponse, err error) { response, err := g.h.DescribeTaskList(ctx, thrift.ToDescribeTaskListRequest(Request)) return thrift.FromDescribeTaskListResponse(response), thrift.FromError(err) } func (g APIHandler) DescribeWorkflowExecution(ctx context.Context, DescribeRequest *shared.DescribeWorkflowExecutionRequest) (dp1 *shared.DescribeWorkflowExecutionResponse, err error) { response, err := g.h.DescribeWorkflowExecution(ctx, thrift.ToDescribeWorkflowExecutionRequest(DescribeRequest)) return thrift.FromDescribeWorkflowExecutionResponse(response), thrift.FromError(err) } func (g APIHandler) DiagnoseWorkflowExecution(ctx context.Context, DiagnoseRequest *shared.DiagnoseWorkflowExecutionRequest) (dp1 *shared.DiagnoseWorkflowExecutionResponse, err error) { response, err := g.h.DiagnoseWorkflowExecution(ctx, thrift.ToDiagnoseWorkflowExecutionRequest(DiagnoseRequest)) return thrift.FromDiagnoseWorkflowExecutionResponse(response), thrift.FromError(err) } func (g APIHandler) FailoverDomain(ctx context.Context, FailoverRequest *shared.FailoverDomainRequest) (fp1 *shared.FailoverDomainResponse, err error) { response, err := g.h.FailoverDomain(ctx, thrift.ToFailoverDomainRequest(FailoverRequest)) return thrift.FromFailoverDomainResponse(response), thrift.FromError(err) } func (g APIHandler) GetClusterInfo(ctx context.Context) (cp1 *shared.ClusterInfo, err error) { response, err := g.h.GetClusterInfo(ctx) return thrift.FromGetClusterInfoResponse(response), thrift.FromError(err) } func (g APIHandler) GetSearchAttributes(ctx context.Context) (gp1 *shared.GetSearchAttributesResponse, err error) { response, err := g.h.GetSearchAttributes(ctx) return thrift.FromGetSearchAttributesResponse(response), thrift.FromError(err) } func (g APIHandler) GetTaskListsByDomain(ctx context.Context, Request *shared.GetTaskListsByDomainRequest) (gp1 *shared.GetTaskListsByDomainResponse, err error) { response, err := g.h.GetTaskListsByDomain(ctx, thrift.ToGetTaskListsByDomainRequest(Request)) return thrift.FromGetTaskListsByDomainResponse(response), thrift.FromError(err) } func (g APIHandler) GetWorkflowExecutionHistory(ctx context.Context, GetRequest *shared.GetWorkflowExecutionHistoryRequest) (gp1 *shared.GetWorkflowExecutionHistoryResponse, err error) { response, err := g.h.GetWorkflowExecutionHistory(ctx, thrift.ToGetWorkflowExecutionHistoryRequest(GetRequest)) return thrift.FromGetWorkflowExecutionHistoryResponse(response), thrift.FromError(err) } func (g APIHandler) ListArchivedWorkflowExecutions(ctx context.Context, ListRequest *shared.ListArchivedWorkflowExecutionsRequest) (lp1 *shared.ListArchivedWorkflowExecutionsResponse, err error) { response, err := g.h.ListArchivedWorkflowExecutions(ctx, thrift.ToListArchivedWorkflowExecutionsRequest(ListRequest)) return thrift.FromListArchivedWorkflowExecutionsResponse(response), thrift.FromError(err) } func (g APIHandler) ListClosedWorkflowExecutions(ctx context.Context, ListRequest *shared.ListClosedWorkflowExecutionsRequest) (lp1 *shared.ListClosedWorkflowExecutionsResponse, err error) { response, err := g.h.ListClosedWorkflowExecutions(ctx, thrift.ToListClosedWorkflowExecutionsRequest(ListRequest)) return thrift.FromListClosedWorkflowExecutionsResponse(response), thrift.FromError(err) } func (g APIHandler) ListDomains(ctx context.Context, ListRequest *shared.ListDomainsRequest) (lp1 *shared.ListDomainsResponse, err error) { response, err := g.h.ListDomains(ctx, thrift.ToListDomainsRequest(ListRequest)) return thrift.FromListDomainsResponse(response), thrift.FromError(err) } func (g APIHandler) ListFailoverHistory(ctx context.Context, ListRequest *shared.ListFailoverHistoryRequest) (lp1 *shared.ListFailoverHistoryResponse, err error) { response, err := g.h.ListFailoverHistory(ctx, thrift.ToListFailoverHistoryRequest(ListRequest)) return thrift.FromListFailoverHistoryResponse(response), thrift.FromError(err) } func (g APIHandler) ListOpenWorkflowExecutions(ctx context.Context, ListRequest *shared.ListOpenWorkflowExecutionsRequest) (lp1 *shared.ListOpenWorkflowExecutionsResponse, err error) { response, err := g.h.ListOpenWorkflowExecutions(ctx, thrift.ToListOpenWorkflowExecutionsRequest(ListRequest)) return thrift.FromListOpenWorkflowExecutionsResponse(response), thrift.FromError(err) } func (g APIHandler) ListTaskListPartitions(ctx context.Context, Request *shared.ListTaskListPartitionsRequest) (lp1 *shared.ListTaskListPartitionsResponse, err error) { response, err := g.h.ListTaskListPartitions(ctx, thrift.ToListTaskListPartitionsRequest(Request)) return thrift.FromListTaskListPartitionsResponse(response), thrift.FromError(err) } func (g APIHandler) ListWorkflowExecutions(ctx context.Context, ListRequest *shared.ListWorkflowExecutionsRequest) (lp1 *shared.ListWorkflowExecutionsResponse, err error) { response, err := g.h.ListWorkflowExecutions(ctx, thrift.ToListWorkflowExecutionsRequest(ListRequest)) return thrift.FromListWorkflowExecutionsResponse(response), thrift.FromError(err) } func (g APIHandler) PollForActivityTask(ctx context.Context, PollRequest *shared.PollForActivityTaskRequest) (pp1 *shared.PollForActivityTaskResponse, err error) { response, err := g.h.PollForActivityTask(ctx, thrift.ToPollForActivityTaskRequest(PollRequest)) return thrift.FromPollForActivityTaskResponse(response), thrift.FromError(err) } func (g APIHandler) PollForDecisionTask(ctx context.Context, PollRequest *shared.PollForDecisionTaskRequest) (pp1 *shared.PollForDecisionTaskResponse, err error) { response, err := g.h.PollForDecisionTask(ctx, thrift.ToPollForDecisionTaskRequest(PollRequest)) return thrift.FromPollForDecisionTaskResponse(response), thrift.FromError(err) } func (g APIHandler) QueryWorkflow(ctx context.Context, QueryRequest *shared.QueryWorkflowRequest) (qp1 *shared.QueryWorkflowResponse, err error) { response, err := g.h.QueryWorkflow(ctx, thrift.ToQueryWorkflowRequest(QueryRequest)) return thrift.FromQueryWorkflowResponse(response), thrift.FromError(err) } func (g APIHandler) RecordActivityTaskHeartbeat(ctx context.Context, HeartbeatRequest *shared.RecordActivityTaskHeartbeatRequest) (rp1 *shared.RecordActivityTaskHeartbeatResponse, err error) { response, err := g.h.RecordActivityTaskHeartbeat(ctx, thrift.ToRecordActivityTaskHeartbeatRequest(HeartbeatRequest)) return thrift.FromRecordActivityTaskHeartbeatResponse(response), thrift.FromError(err) } func (g APIHandler) RecordActivityTaskHeartbeatByID(ctx context.Context, HeartbeatRequest *shared.RecordActivityTaskHeartbeatByIDRequest) (rp1 *shared.RecordActivityTaskHeartbeatResponse, err error) { response, err := g.h.RecordActivityTaskHeartbeatByID(ctx, thrift.ToRecordActivityTaskHeartbeatByIDRequest(HeartbeatRequest)) return thrift.FromRecordActivityTaskHeartbeatByIDResponse(response), thrift.FromError(err) } func (g APIHandler) RefreshWorkflowTasks(ctx context.Context, Request *shared.RefreshWorkflowTasksRequest) (err error) { err = g.h.RefreshWorkflowTasks(ctx, thrift.ToRefreshWorkflowTasksRequest(Request)) return thrift.FromError(err) } func (g APIHandler) RegisterDomain(ctx context.Context, RegisterRequest *shared.RegisterDomainRequest) (err error) { err = g.h.RegisterDomain(ctx, thrift.ToRegisterDomainRequest(RegisterRequest)) return thrift.FromError(err) } func (g APIHandler) RequestCancelWorkflowExecution(ctx context.Context, CancelRequest *shared.RequestCancelWorkflowExecutionRequest) (err error) { err = g.h.RequestCancelWorkflowExecution(ctx, thrift.ToRequestCancelWorkflowExecutionRequest(CancelRequest)) return thrift.FromError(err) } func (g APIHandler) ResetStickyTaskList(ctx context.Context, ResetRequest *shared.ResetStickyTaskListRequest) (rp1 *shared.ResetStickyTaskListResponse, err error) { response, err := g.h.ResetStickyTaskList(ctx, thrift.ToResetStickyTaskListRequest(ResetRequest)) return thrift.FromResetStickyTaskListResponse(response), thrift.FromError(err) } func (g APIHandler) ResetWorkflowExecution(ctx context.Context, ResetRequest *shared.ResetWorkflowExecutionRequest) (rp1 *shared.ResetWorkflowExecutionResponse, err error) { response, err := g.h.ResetWorkflowExecution(ctx, thrift.ToResetWorkflowExecutionRequest(ResetRequest)) return thrift.FromResetWorkflowExecutionResponse(response), thrift.FromError(err) } func (g APIHandler) RespondActivityTaskCanceled(ctx context.Context, CanceledRequest *shared.RespondActivityTaskCanceledRequest) (err error) { err = g.h.RespondActivityTaskCanceled(ctx, thrift.ToRespondActivityTaskCanceledRequest(CanceledRequest)) return thrift.FromError(err) } func (g APIHandler) RespondActivityTaskCanceledByID(ctx context.Context, CanceledRequest *shared.RespondActivityTaskCanceledByIDRequest) (err error) { err = g.h.RespondActivityTaskCanceledByID(ctx, thrift.ToRespondActivityTaskCanceledByIDRequest(CanceledRequest)) return thrift.FromError(err) } func (g APIHandler) RespondActivityTaskCompleted(ctx context.Context, CompleteRequest *shared.RespondActivityTaskCompletedRequest) (err error) { err = g.h.RespondActivityTaskCompleted(ctx, thrift.ToRespondActivityTaskCompletedRequest(CompleteRequest)) return thrift.FromError(err) } func (g APIHandler) RespondActivityTaskCompletedByID(ctx context.Context, CompleteRequest *shared.RespondActivityTaskCompletedByIDRequest) (err error) { err = g.h.RespondActivityTaskCompletedByID(ctx, thrift.ToRespondActivityTaskCompletedByIDRequest(CompleteRequest)) return thrift.FromError(err) } func (g APIHandler) RespondActivityTaskFailed(ctx context.Context, FailRequest *shared.RespondActivityTaskFailedRequest) (err error) { err = g.h.RespondActivityTaskFailed(ctx, thrift.ToRespondActivityTaskFailedRequest(FailRequest)) return thrift.FromError(err) } func (g APIHandler) RespondActivityTaskFailedByID(ctx context.Context, FailRequest *shared.RespondActivityTaskFailedByIDRequest) (err error) { err = g.h.RespondActivityTaskFailedByID(ctx, thrift.ToRespondActivityTaskFailedByIDRequest(FailRequest)) return thrift.FromError(err) } func (g APIHandler) RespondDecisionTaskCompleted(ctx context.Context, CompleteRequest *shared.RespondDecisionTaskCompletedRequest) (rp1 *shared.RespondDecisionTaskCompletedResponse, err error) { response, err := g.h.RespondDecisionTaskCompleted(ctx, thrift.ToRespondDecisionTaskCompletedRequest(CompleteRequest)) return thrift.FromRespondDecisionTaskCompletedResponse(response), thrift.FromError(err) } func (g APIHandler) RespondDecisionTaskFailed(ctx context.Context, FailedRequest *shared.RespondDecisionTaskFailedRequest) (err error) { err = g.h.RespondDecisionTaskFailed(ctx, thrift.ToRespondDecisionTaskFailedRequest(FailedRequest)) return thrift.FromError(err) } func (g APIHandler) RespondQueryTaskCompleted(ctx context.Context, CompleteRequest *shared.RespondQueryTaskCompletedRequest) (err error) { err = g.h.RespondQueryTaskCompleted(ctx, thrift.ToRespondQueryTaskCompletedRequest(CompleteRequest)) return thrift.FromError(err) } func (g APIHandler) RestartWorkflowExecution(ctx context.Context, RestartRequest *shared.RestartWorkflowExecutionRequest) (rp1 *shared.RestartWorkflowExecutionResponse, err error) { response, err := g.h.RestartWorkflowExecution(ctx, thrift.ToRestartWorkflowExecutionRequest(RestartRequest)) return thrift.FromRestartWorkflowExecutionResponse(response), thrift.FromError(err) } func (g APIHandler) ScanWorkflowExecutions(ctx context.Context, ListRequest *shared.ListWorkflowExecutionsRequest) (lp1 *shared.ListWorkflowExecutionsResponse, err error) { response, err := g.h.ScanWorkflowExecutions(ctx, thrift.ToScanWorkflowExecutionsRequest(ListRequest)) return thrift.FromScanWorkflowExecutionsResponse(response), thrift.FromError(err) } func (g APIHandler) SignalWithStartWorkflowExecution(ctx context.Context, SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionRequest) (sp1 *shared.StartWorkflowExecutionResponse, err error) { response, err := g.h.SignalWithStartWorkflowExecution(ctx, thrift.ToSignalWithStartWorkflowExecutionRequest(SignalWithStartRequest)) return thrift.FromSignalWithStartWorkflowExecutionResponse(response), thrift.FromError(err) } func (g APIHandler) SignalWithStartWorkflowExecutionAsync(ctx context.Context, SignalWithStartRequest *shared.SignalWithStartWorkflowExecutionAsyncRequest) (sp1 *shared.SignalWithStartWorkflowExecutionAsyncResponse, err error) { response, err := g.h.SignalWithStartWorkflowExecutionAsync(ctx, thrift.ToSignalWithStartWorkflowExecutionAsyncRequest(SignalWithStartRequest)) return thrift.FromSignalWithStartWorkflowExecutionAsyncResponse(response), thrift.FromError(err) } func (g APIHandler) SignalWorkflowExecution(ctx context.Context, SignalRequest *shared.SignalWorkflowExecutionRequest) (err error) { err = g.h.SignalWorkflowExecution(ctx, thrift.ToSignalWorkflowExecutionRequest(SignalRequest)) return thrift.FromError(err) } func (g APIHandler) StartWorkflowExecution(ctx context.Context, StartRequest *shared.StartWorkflowExecutionRequest) (sp1 *shared.StartWorkflowExecutionResponse, err error) { response, err := g.h.StartWorkflowExecution(ctx, thrift.ToStartWorkflowExecutionRequest(StartRequest)) return thrift.FromStartWorkflowExecutionResponse(response), thrift.FromError(err) } func (g APIHandler) StartWorkflowExecutionAsync(ctx context.Context, StartRequest *shared.StartWorkflowExecutionAsyncRequest) (sp1 *shared.StartWorkflowExecutionAsyncResponse, err error) { response, err := g.h.StartWorkflowExecutionAsync(ctx, thrift.ToStartWorkflowExecutionAsyncRequest(StartRequest)) return thrift.FromStartWorkflowExecutionAsyncResponse(response), thrift.FromError(err) } func (g APIHandler) TerminateWorkflowExecution(ctx context.Context, TerminateRequest *shared.TerminateWorkflowExecutionRequest) (err error) { err = g.h.TerminateWorkflowExecution(ctx, thrift.ToTerminateWorkflowExecutionRequest(TerminateRequest)) return thrift.FromError(err) } func (g APIHandler) UpdateDomain(ctx context.Context, UpdateRequest *shared.UpdateDomainRequest) (up1 *shared.UpdateDomainResponse, err error) { response, err := g.h.UpdateDomain(ctx, thrift.ToUpdateDomainRequest(UpdateRequest)) return thrift.FromUpdateDomainResponse(response), thrift.FromError(err) } ================================================ FILE: service/frontend/wrappers/thrift/api_handler_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "context" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/.gen/go/health" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" ) func TestThriftHandler(t *testing.T) { ctrl := gomock.NewController(t) h := api.NewMockHandler(ctrl) th := NewAPIHandler(h) ctx := context.Background() internalErr := &types.InternalServiceError{Message: "test"} expectedErr := &shared.InternalServiceError{Message: "test"} t.Run("Health", func(t *testing.T) { h.EXPECT().Health(ctx).Return(&types.HealthStatus{}, internalErr).Times(1) resp, err := th.Health(ctx) assert.Equal(t, health.HealthStatus{Msg: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("CountWorkflowExecutions", func(t *testing.T) { h.EXPECT().CountWorkflowExecutions(ctx, &types.CountWorkflowExecutionsRequest{}).Return(&types.CountWorkflowExecutionsResponse{}, internalErr).Times(1) resp, err := th.CountWorkflowExecutions(ctx, &shared.CountWorkflowExecutionsRequest{}) assert.Equal(t, shared.CountWorkflowExecutionsResponse{Count: common.Int64Ptr(0)}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("DeprecateDomain", func(t *testing.T) { h.EXPECT().DeprecateDomain(ctx, &types.DeprecateDomainRequest{}).Return(internalErr).Times(1) err := th.DeprecateDomain(ctx, &shared.DeprecateDomainRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("DescribeDomain", func(t *testing.T) { h.EXPECT().DescribeDomain(ctx, &types.DescribeDomainRequest{}).Return(&types.DescribeDomainResponse{}, internalErr).Times(1) resp, err := th.DescribeDomain(ctx, &shared.DescribeDomainRequest{}) assert.Equal(t, shared.DescribeDomainResponse{IsGlobalDomain: common.BoolPtr(false), FailoverVersion: common.Int64Ptr(0)}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("DescribeTaskList", func(t *testing.T) { h.EXPECT().DescribeTaskList(ctx, &types.DescribeTaskListRequest{}).Return(&types.DescribeTaskListResponse{}, internalErr).Times(1) resp, err := th.DescribeTaskList(ctx, &shared.DescribeTaskListRequest{}) assert.Equal(t, shared.DescribeTaskListResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("DescribeWorkflowExecution", func(t *testing.T) { h.EXPECT().DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{}).Return(&types.DescribeWorkflowExecutionResponse{}, internalErr).Times(1) resp, err := th.DescribeWorkflowExecution(ctx, &shared.DescribeWorkflowExecutionRequest{}) assert.Equal(t, shared.DescribeWorkflowExecutionResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("GetClusterInfo", func(t *testing.T) { h.EXPECT().GetClusterInfo(ctx).Return(&types.ClusterInfo{}, internalErr).Times(1) resp, err := th.GetClusterInfo(ctx) assert.Equal(t, shared.ClusterInfo{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("GetSearchAttributes", func(t *testing.T) { h.EXPECT().GetSearchAttributes(ctx).Return(&types.GetSearchAttributesResponse{}, internalErr).Times(1) resp, err := th.GetSearchAttributes(ctx) assert.Equal(t, shared.GetSearchAttributesResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("GetWorkflowExecutionHistory", func(t *testing.T) { h.EXPECT().GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{}).Return(&types.GetWorkflowExecutionHistoryResponse{}, internalErr).Times(1) resp, err := th.GetWorkflowExecutionHistory(ctx, &shared.GetWorkflowExecutionHistoryRequest{}) assert.Equal(t, shared.GetWorkflowExecutionHistoryResponse{Archived: common.BoolPtr(false)}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ListArchivedWorkflowExecutions", func(t *testing.T) { h.EXPECT().ListArchivedWorkflowExecutions(ctx, &types.ListArchivedWorkflowExecutionsRequest{}).Return(&types.ListArchivedWorkflowExecutionsResponse{}, internalErr).Times(1) resp, err := th.ListArchivedWorkflowExecutions(ctx, &shared.ListArchivedWorkflowExecutionsRequest{}) assert.Equal(t, shared.ListArchivedWorkflowExecutionsResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ListClosedWorkflowExecutions", func(t *testing.T) { h.EXPECT().ListClosedWorkflowExecutions(ctx, &types.ListClosedWorkflowExecutionsRequest{}).Return(&types.ListClosedWorkflowExecutionsResponse{}, internalErr).Times(1) resp, err := th.ListClosedWorkflowExecutions(ctx, &shared.ListClosedWorkflowExecutionsRequest{}) assert.Equal(t, shared.ListClosedWorkflowExecutionsResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ListDomains", func(t *testing.T) { h.EXPECT().ListDomains(ctx, &types.ListDomainsRequest{}).Return(&types.ListDomainsResponse{}, internalErr).Times(1) resp, err := th.ListDomains(ctx, &shared.ListDomainsRequest{}) assert.Equal(t, shared.ListDomainsResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ListOpenWorkflowExecutions", func(t *testing.T) { h.EXPECT().ListOpenWorkflowExecutions(ctx, &types.ListOpenWorkflowExecutionsRequest{}).Return(&types.ListOpenWorkflowExecutionsResponse{}, internalErr).Times(1) resp, err := th.ListOpenWorkflowExecutions(ctx, &shared.ListOpenWorkflowExecutionsRequest{}) assert.Equal(t, shared.ListOpenWorkflowExecutionsResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ListTaskListPartitions", func(t *testing.T) { h.EXPECT().ListTaskListPartitions(ctx, &types.ListTaskListPartitionsRequest{}).Return(&types.ListTaskListPartitionsResponse{}, internalErr).Times(1) resp, err := th.ListTaskListPartitions(ctx, &shared.ListTaskListPartitionsRequest{}) assert.Equal(t, shared.ListTaskListPartitionsResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ListWorkflowExecutions", func(t *testing.T) { h.EXPECT().ListWorkflowExecutions(ctx, &types.ListWorkflowExecutionsRequest{}).Return(&types.ListWorkflowExecutionsResponse{}, internalErr).Times(1) resp, err := th.ListWorkflowExecutions(ctx, &shared.ListWorkflowExecutionsRequest{}) assert.Equal(t, shared.ListWorkflowExecutionsResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("PollForActivityTask", func(t *testing.T) { h.EXPECT().PollForActivityTask(ctx, &types.PollForActivityTaskRequest{}).Return(&types.PollForActivityTaskResponse{}, internalErr).Times(1) resp, err := th.PollForActivityTask(ctx, &shared.PollForActivityTaskRequest{}) assert.Equal(t, shared.PollForActivityTaskResponse{WorkflowDomain: common.StringPtr(""), ActivityId: common.StringPtr(""), Attempt: common.Int32Ptr(0)}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("PollForDecisionTask", func(t *testing.T) { h.EXPECT().PollForDecisionTask(ctx, &types.PollForDecisionTaskRequest{}).Return(&types.PollForDecisionTaskResponse{}, internalErr).Times(1) resp, err := th.PollForDecisionTask(ctx, &shared.PollForDecisionTaskRequest{}) assert.Equal(t, shared.PollForDecisionTaskResponse{StartedEventId: common.Int64Ptr(0), Attempt: common.Int64Ptr(0), BacklogCountHint: common.Int64Ptr(0), NextEventId: common.Int64Ptr(0), TotalHistoryBytes: common.Int64Ptr(0)}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("QueryWorkflow", func(t *testing.T) { h.EXPECT().QueryWorkflow(ctx, &types.QueryWorkflowRequest{}).Return(&types.QueryWorkflowResponse{}, internalErr).Times(1) resp, err := th.QueryWorkflow(ctx, &shared.QueryWorkflowRequest{}) assert.Equal(t, shared.QueryWorkflowResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RecordActivityTaskHeartbeat", func(t *testing.T) { h.EXPECT().RecordActivityTaskHeartbeat(ctx, &types.RecordActivityTaskHeartbeatRequest{}).Return(&types.RecordActivityTaskHeartbeatResponse{}, internalErr).Times(1) resp, err := th.RecordActivityTaskHeartbeat(ctx, &shared.RecordActivityTaskHeartbeatRequest{}) assert.Equal(t, shared.RecordActivityTaskHeartbeatResponse{CancelRequested: common.BoolPtr(false)}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RecordActivityTaskHeartbeatByID", func(t *testing.T) { h.EXPECT().RecordActivityTaskHeartbeatByID(ctx, &types.RecordActivityTaskHeartbeatByIDRequest{}).Return(&types.RecordActivityTaskHeartbeatResponse{}, internalErr).Times(1) resp, err := th.RecordActivityTaskHeartbeatByID(ctx, &shared.RecordActivityTaskHeartbeatByIDRequest{}) assert.Equal(t, shared.RecordActivityTaskHeartbeatResponse{CancelRequested: common.BoolPtr(false)}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RegisterDomain", func(t *testing.T) { h.EXPECT().RegisterDomain(ctx, &types.RegisterDomainRequest{}).Return(internalErr).Times(1) err := th.RegisterDomain(ctx, &shared.RegisterDomainRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RequestCancelWorkflowExecution", func(t *testing.T) { h.EXPECT().RequestCancelWorkflowExecution(ctx, &types.RequestCancelWorkflowExecutionRequest{}).Return(internalErr).Times(1) err := th.RequestCancelWorkflowExecution(ctx, &shared.RequestCancelWorkflowExecutionRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("ResetStickyTaskList", func(t *testing.T) { h.EXPECT().ResetStickyTaskList(ctx, &types.ResetStickyTaskListRequest{}).Return(&types.ResetStickyTaskListResponse{}, internalErr).Times(1) resp, err := th.ResetStickyTaskList(ctx, &shared.ResetStickyTaskListRequest{}) assert.Equal(t, shared.ResetStickyTaskListResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ResetWorkflowExecution", func(t *testing.T) { h.EXPECT().ResetWorkflowExecution(ctx, &types.ResetWorkflowExecutionRequest{}).Return(&types.ResetWorkflowExecutionResponse{}, internalErr).Times(1) resp, err := th.ResetWorkflowExecution(ctx, &shared.ResetWorkflowExecutionRequest{}) assert.Equal(t, shared.ResetWorkflowExecutionResponse{RunId: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RespondActivityTaskCanceled", func(t *testing.T) { h.EXPECT().RespondActivityTaskCanceled(ctx, &types.RespondActivityTaskCanceledRequest{}).Return(internalErr).Times(1) err := th.RespondActivityTaskCanceled(ctx, &shared.RespondActivityTaskCanceledRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RespondActivityTaskCanceledByID", func(t *testing.T) { h.EXPECT().RespondActivityTaskCanceledByID(ctx, &types.RespondActivityTaskCanceledByIDRequest{}).Return(internalErr).Times(1) err := th.RespondActivityTaskCanceledByID(ctx, &shared.RespondActivityTaskCanceledByIDRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RespondActivityTaskCompleted", func(t *testing.T) { h.EXPECT().RespondActivityTaskCompleted(ctx, &types.RespondActivityTaskCompletedRequest{}).Return(internalErr).Times(1) err := th.RespondActivityTaskCompleted(ctx, &shared.RespondActivityTaskCompletedRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RespondActivityTaskCompletedByID", func(t *testing.T) { h.EXPECT().RespondActivityTaskCompletedByID(ctx, &types.RespondActivityTaskCompletedByIDRequest{}).Return(internalErr).Times(1) err := th.RespondActivityTaskCompletedByID(ctx, &shared.RespondActivityTaskCompletedByIDRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RespondActivityTaskFailed", func(t *testing.T) { h.EXPECT().RespondActivityTaskFailed(ctx, &types.RespondActivityTaskFailedRequest{}).Return(internalErr).Times(1) err := th.RespondActivityTaskFailed(ctx, &shared.RespondActivityTaskFailedRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RespondActivityTaskFailedByID", func(t *testing.T) { h.EXPECT().RespondActivityTaskFailedByID(ctx, &types.RespondActivityTaskFailedByIDRequest{}).Return(internalErr).Times(1) err := th.RespondActivityTaskFailedByID(ctx, &shared.RespondActivityTaskFailedByIDRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RespondDecisionTaskCompleted", func(t *testing.T) { h.EXPECT().RespondDecisionTaskCompleted(ctx, &types.RespondDecisionTaskCompletedRequest{}).Return(&types.RespondDecisionTaskCompletedResponse{}, internalErr).Times(1) resp, err := th.RespondDecisionTaskCompleted(ctx, &shared.RespondDecisionTaskCompletedRequest{}) assert.Equal(t, shared.RespondDecisionTaskCompletedResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RespondDecisionTaskFailed", func(t *testing.T) { h.EXPECT().RespondDecisionTaskFailed(ctx, &types.RespondDecisionTaskFailedRequest{}).Return(internalErr).Times(1) err := th.RespondDecisionTaskFailed(ctx, &shared.RespondDecisionTaskFailedRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RespondQueryTaskCompleted", func(t *testing.T) { h.EXPECT().RespondQueryTaskCompleted(ctx, &types.RespondQueryTaskCompletedRequest{}).Return(internalErr).Times(1) err := th.RespondQueryTaskCompleted(ctx, &shared.RespondQueryTaskCompletedRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("ScanWorkflowExecutions", func(t *testing.T) { h.EXPECT().ScanWorkflowExecutions(ctx, &types.ListWorkflowExecutionsRequest{}).Return(&types.ListWorkflowExecutionsResponse{}, internalErr).Times(1) resp, err := th.ScanWorkflowExecutions(ctx, &shared.ListWorkflowExecutionsRequest{}) assert.Equal(t, shared.ListWorkflowExecutionsResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("SignalWithStartWorkflowExecution", func(t *testing.T) { h.EXPECT().SignalWithStartWorkflowExecution(ctx, &types.SignalWithStartWorkflowExecutionRequest{}).Return(&types.StartWorkflowExecutionResponse{}, internalErr).Times(1) resp, err := th.SignalWithStartWorkflowExecution(ctx, &shared.SignalWithStartWorkflowExecutionRequest{}) assert.Equal(t, shared.StartWorkflowExecutionResponse{RunId: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("SignalWorkflowExecution", func(t *testing.T) { h.EXPECT().SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{}).Return(internalErr).Times(1) err := th.SignalWorkflowExecution(ctx, &shared.SignalWorkflowExecutionRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("StartWorkflowExecution", func(t *testing.T) { h.EXPECT().StartWorkflowExecution(ctx, &types.StartWorkflowExecutionRequest{}).Return(&types.StartWorkflowExecutionResponse{}, internalErr).Times(1) resp, err := th.StartWorkflowExecution(ctx, &shared.StartWorkflowExecutionRequest{}) assert.Equal(t, shared.StartWorkflowExecutionResponse{RunId: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("TerminateWorkflowExecution", func(t *testing.T) { h.EXPECT().TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{}).Return(internalErr).Times(1) err := th.TerminateWorkflowExecution(ctx, &shared.TerminateWorkflowExecutionRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("UpdateDomain", func(t *testing.T) { h.EXPECT().UpdateDomain(ctx, &types.UpdateDomainRequest{}).Return(&types.UpdateDomainResponse{}, internalErr).Times(1) resp, err := th.UpdateDomain(ctx, &shared.UpdateDomainRequest{}) assert.Equal(t, shared.UpdateDomainResponse{IsGlobalDomain: common.BoolPtr(false), FailoverVersion: common.Int64Ptr(0)}, *resp) assert.Equal(t, expectedErr, err) }) } ================================================ FILE: service/frontend/wrappers/thrift/constructor.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/.gen/go/admin/adminserviceserver" "github.com/uber/cadence/.gen/go/cadence/workflowserviceserver" "github.com/uber/cadence/.gen/go/health" "github.com/uber/cadence/.gen/go/health/metaserver" "github.com/uber/cadence/common/types/mapper/thrift" "github.com/uber/cadence/service/frontend/admin" "github.com/uber/cadence/service/frontend/api" ) type ( AdminHandler struct { h admin.Handler } APIHandler struct { h api.Handler } ) func NewAdminHandler(h admin.Handler) AdminHandler { return AdminHandler{h} } func NewAPIHandler(h api.Handler) APIHandler { return APIHandler{h} } func (t AdminHandler) Register(dispatcher *yarpc.Dispatcher) { dispatcher.Register(adminserviceserver.New(t)) } func (t APIHandler) Register(dispatcher *yarpc.Dispatcher) { dispatcher.Register(workflowserviceserver.New(t)) dispatcher.Register(metaserver.New(t)) } func (t APIHandler) Health(ctx context.Context) (*health.HealthStatus, error) { response, err := t.h.Health(ctx) return thrift.FromHealthStatus(response), thrift.FromError(err) } ================================================ FILE: service/frontend/wrappers/versioncheck/api_generated.go ================================================ package versioncheck // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/versioncheck.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/api" "github.com/uber/cadence/service/frontend/config" ) type ( versionCheckHandler struct { frontendHandler api.Handler config *config.Config versionChecker client.VersionChecker } ) func NewAPIHandler( wfHandler api.Handler, config *config.Config, versionChecker client.VersionChecker, ) api.Handler { return &versionCheckHandler{ frontendHandler: wfHandler, config: config, versionChecker: versionChecker, } } func (h *versionCheckHandler) CountWorkflowExecutions(ctx context.Context, cp1 *types.CountWorkflowExecutionsRequest) (cp2 *types.CountWorkflowExecutionsResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.CountWorkflowExecutions(ctx, cp1) } func (h *versionCheckHandler) DeleteDomain(ctx context.Context, dp1 *types.DeleteDomainRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.DeleteDomain(ctx, dp1) } func (h *versionCheckHandler) DeprecateDomain(ctx context.Context, dp1 *types.DeprecateDomainRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.DeprecateDomain(ctx, dp1) } func (h *versionCheckHandler) DescribeDomain(ctx context.Context, dp1 *types.DescribeDomainRequest) (dp2 *types.DescribeDomainResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.DescribeDomain(ctx, dp1) } func (h *versionCheckHandler) DescribeTaskList(ctx context.Context, dp1 *types.DescribeTaskListRequest) (dp2 *types.DescribeTaskListResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.DescribeTaskList(ctx, dp1) } func (h *versionCheckHandler) DescribeWorkflowExecution(ctx context.Context, dp1 *types.DescribeWorkflowExecutionRequest) (dp2 *types.DescribeWorkflowExecutionResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.DescribeWorkflowExecution(ctx, dp1) } func (h *versionCheckHandler) DiagnoseWorkflowExecution(ctx context.Context, dp1 *types.DiagnoseWorkflowExecutionRequest) (dp2 *types.DiagnoseWorkflowExecutionResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.DiagnoseWorkflowExecution(ctx, dp1) } func (h *versionCheckHandler) FailoverDomain(ctx context.Context, fp1 *types.FailoverDomainRequest) (fp2 *types.FailoverDomainResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.FailoverDomain(ctx, fp1) } func (h *versionCheckHandler) GetClusterInfo(ctx context.Context) (cp1 *types.ClusterInfo, err error) { return h.frontendHandler.GetClusterInfo(ctx) } func (h *versionCheckHandler) GetSearchAttributes(ctx context.Context) (gp1 *types.GetSearchAttributesResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.GetSearchAttributes(ctx) } func (h *versionCheckHandler) GetTaskListsByDomain(ctx context.Context, gp1 *types.GetTaskListsByDomainRequest) (gp2 *types.GetTaskListsByDomainResponse, err error) { return h.frontendHandler.GetTaskListsByDomain(ctx, gp1) } func (h *versionCheckHandler) GetWorkflowExecutionHistory(ctx context.Context, gp1 *types.GetWorkflowExecutionHistoryRequest) (gp2 *types.GetWorkflowExecutionHistoryResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.GetWorkflowExecutionHistory(ctx, gp1) } func (h *versionCheckHandler) Health(ctx context.Context) (hp1 *types.HealthStatus, err error) { return h.frontendHandler.Health(ctx) } func (h *versionCheckHandler) ListArchivedWorkflowExecutions(ctx context.Context, lp1 *types.ListArchivedWorkflowExecutionsRequest) (lp2 *types.ListArchivedWorkflowExecutionsResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.ListArchivedWorkflowExecutions(ctx, lp1) } func (h *versionCheckHandler) ListClosedWorkflowExecutions(ctx context.Context, lp1 *types.ListClosedWorkflowExecutionsRequest) (lp2 *types.ListClosedWorkflowExecutionsResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.ListClosedWorkflowExecutions(ctx, lp1) } func (h *versionCheckHandler) ListDomains(ctx context.Context, lp1 *types.ListDomainsRequest) (lp2 *types.ListDomainsResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.ListDomains(ctx, lp1) } func (h *versionCheckHandler) ListFailoverHistory(ctx context.Context, lp1 *types.ListFailoverHistoryRequest) (lp2 *types.ListFailoverHistoryResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.ListFailoverHistory(ctx, lp1) } func (h *versionCheckHandler) ListOpenWorkflowExecutions(ctx context.Context, lp1 *types.ListOpenWorkflowExecutionsRequest) (lp2 *types.ListOpenWorkflowExecutionsResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.ListOpenWorkflowExecutions(ctx, lp1) } func (h *versionCheckHandler) ListTaskListPartitions(ctx context.Context, lp1 *types.ListTaskListPartitionsRequest) (lp2 *types.ListTaskListPartitionsResponse, err error) { return h.frontendHandler.ListTaskListPartitions(ctx, lp1) } func (h *versionCheckHandler) ListWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest) (lp2 *types.ListWorkflowExecutionsResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.ListWorkflowExecutions(ctx, lp1) } func (h *versionCheckHandler) PollForActivityTask(ctx context.Context, pp1 *types.PollForActivityTaskRequest) (pp2 *types.PollForActivityTaskResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.PollForActivityTask(ctx, pp1) } func (h *versionCheckHandler) PollForDecisionTask(ctx context.Context, pp1 *types.PollForDecisionTaskRequest) (pp2 *types.PollForDecisionTaskResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.PollForDecisionTask(ctx, pp1) } func (h *versionCheckHandler) QueryWorkflow(ctx context.Context, qp1 *types.QueryWorkflowRequest) (qp2 *types.QueryWorkflowResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.QueryWorkflow(ctx, qp1) } func (h *versionCheckHandler) RecordActivityTaskHeartbeat(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatRequest) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RecordActivityTaskHeartbeat(ctx, rp1) } func (h *versionCheckHandler) RecordActivityTaskHeartbeatByID(ctx context.Context, rp1 *types.RecordActivityTaskHeartbeatByIDRequest) (rp2 *types.RecordActivityTaskHeartbeatResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RecordActivityTaskHeartbeatByID(ctx, rp1) } func (h *versionCheckHandler) RefreshWorkflowTasks(ctx context.Context, rp1 *types.RefreshWorkflowTasksRequest) (err error) { return h.frontendHandler.RefreshWorkflowTasks(ctx, rp1) } func (h *versionCheckHandler) RegisterDomain(ctx context.Context, rp1 *types.RegisterDomainRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RegisterDomain(ctx, rp1) } func (h *versionCheckHandler) RequestCancelWorkflowExecution(ctx context.Context, rp1 *types.RequestCancelWorkflowExecutionRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RequestCancelWorkflowExecution(ctx, rp1) } func (h *versionCheckHandler) ResetStickyTaskList(ctx context.Context, rp1 *types.ResetStickyTaskListRequest) (rp2 *types.ResetStickyTaskListResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.ResetStickyTaskList(ctx, rp1) } func (h *versionCheckHandler) ResetWorkflowExecution(ctx context.Context, rp1 *types.ResetWorkflowExecutionRequest) (rp2 *types.ResetWorkflowExecutionResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.ResetWorkflowExecution(ctx, rp1) } func (h *versionCheckHandler) RespondActivityTaskCanceled(ctx context.Context, rp1 *types.RespondActivityTaskCanceledRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RespondActivityTaskCanceled(ctx, rp1) } func (h *versionCheckHandler) RespondActivityTaskCanceledByID(ctx context.Context, rp1 *types.RespondActivityTaskCanceledByIDRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RespondActivityTaskCanceledByID(ctx, rp1) } func (h *versionCheckHandler) RespondActivityTaskCompleted(ctx context.Context, rp1 *types.RespondActivityTaskCompletedRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RespondActivityTaskCompleted(ctx, rp1) } func (h *versionCheckHandler) RespondActivityTaskCompletedByID(ctx context.Context, rp1 *types.RespondActivityTaskCompletedByIDRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RespondActivityTaskCompletedByID(ctx, rp1) } func (h *versionCheckHandler) RespondActivityTaskFailed(ctx context.Context, rp1 *types.RespondActivityTaskFailedRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RespondActivityTaskFailed(ctx, rp1) } func (h *versionCheckHandler) RespondActivityTaskFailedByID(ctx context.Context, rp1 *types.RespondActivityTaskFailedByIDRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RespondActivityTaskFailedByID(ctx, rp1) } func (h *versionCheckHandler) RespondDecisionTaskCompleted(ctx context.Context, rp1 *types.RespondDecisionTaskCompletedRequest) (rp2 *types.RespondDecisionTaskCompletedResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RespondDecisionTaskCompleted(ctx, rp1) } func (h *versionCheckHandler) RespondDecisionTaskFailed(ctx context.Context, rp1 *types.RespondDecisionTaskFailedRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RespondDecisionTaskFailed(ctx, rp1) } func (h *versionCheckHandler) RespondQueryTaskCompleted(ctx context.Context, rp1 *types.RespondQueryTaskCompletedRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RespondQueryTaskCompleted(ctx, rp1) } func (h *versionCheckHandler) RestartWorkflowExecution(ctx context.Context, rp1 *types.RestartWorkflowExecutionRequest) (rp2 *types.RestartWorkflowExecutionResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.RestartWorkflowExecution(ctx, rp1) } func (h *versionCheckHandler) ScanWorkflowExecutions(ctx context.Context, lp1 *types.ListWorkflowExecutionsRequest) (lp2 *types.ListWorkflowExecutionsResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.ScanWorkflowExecutions(ctx, lp1) } func (h *versionCheckHandler) SignalWithStartWorkflowExecution(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionRequest) (sp2 *types.StartWorkflowExecutionResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.SignalWithStartWorkflowExecution(ctx, sp1) } func (h *versionCheckHandler) SignalWithStartWorkflowExecutionAsync(ctx context.Context, sp1 *types.SignalWithStartWorkflowExecutionAsyncRequest) (sp2 *types.SignalWithStartWorkflowExecutionAsyncResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.SignalWithStartWorkflowExecutionAsync(ctx, sp1) } func (h *versionCheckHandler) SignalWorkflowExecution(ctx context.Context, sp1 *types.SignalWorkflowExecutionRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.SignalWorkflowExecution(ctx, sp1) } func (h *versionCheckHandler) StartWorkflowExecution(ctx context.Context, sp1 *types.StartWorkflowExecutionRequest) (sp2 *types.StartWorkflowExecutionResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.StartWorkflowExecution(ctx, sp1) } func (h *versionCheckHandler) StartWorkflowExecutionAsync(ctx context.Context, sp1 *types.StartWorkflowExecutionAsyncRequest) (sp2 *types.StartWorkflowExecutionAsyncResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.StartWorkflowExecutionAsync(ctx, sp1) } func (h *versionCheckHandler) TerminateWorkflowExecution(ctx context.Context, tp1 *types.TerminateWorkflowExecutionRequest) (err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.TerminateWorkflowExecution(ctx, tp1) } func (h *versionCheckHandler) UpdateDomain(ctx context.Context, up1 *types.UpdateDomainRequest) (up2 *types.UpdateDomainResponse, err error) { err = h.versionChecker.ClientSupported(ctx, h.config.EnableClientVersionCheck()) if err != nil { return } return h.frontendHandler.UpdateDomain(ctx, up1) } ================================================ FILE: service/history/common/type.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package common import ( "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/events" ) type ( // NotifyTaskInfo defines the info of task notification NotifyTaskInfo struct { ExecutionInfo *persistence.WorkflowExecutionInfo Tasks []persistence.Task VersionHistories *persistence.VersionHistories Activities map[int64]*persistence.ActivityInfo History events.PersistedBlobs PersistenceError bool } ) ================================================ FILE: service/history/config/config.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" ) // Config represents configuration for cadence-history service type Config struct { NumberOfShards int IsAdvancedVisConfigExist bool RPS dynamicproperties.IntPropertyFn MaxIDLengthWarnLimit dynamicproperties.IntPropertyFn DomainNameMaxLength dynamicproperties.IntPropertyFnWithDomainFilter IdentityMaxLength dynamicproperties.IntPropertyFnWithDomainFilter WorkflowIDMaxLength dynamicproperties.IntPropertyFnWithDomainFilter SignalNameMaxLength dynamicproperties.IntPropertyFnWithDomainFilter WorkflowTypeMaxLength dynamicproperties.IntPropertyFnWithDomainFilter RequestIDMaxLength dynamicproperties.IntPropertyFnWithDomainFilter TaskListNameMaxLength dynamicproperties.IntPropertyFnWithDomainFilter ActivityIDMaxLength dynamicproperties.IntPropertyFnWithDomainFilter ActivityTypeMaxLength dynamicproperties.IntPropertyFnWithDomainFilter MarkerNameMaxLength dynamicproperties.IntPropertyFnWithDomainFilter TimerIDMaxLength dynamicproperties.IntPropertyFnWithDomainFilter PersistenceMaxQPS dynamicproperties.IntPropertyFn PersistenceGlobalMaxQPS dynamicproperties.IntPropertyFn EnableVisibilitySampling dynamicproperties.BoolPropertyFn EnableReadFromClosedExecutionV2 dynamicproperties.BoolPropertyFn VisibilityOpenMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter VisibilityClosedMaxQPS dynamicproperties.IntPropertyFnWithDomainFilter WriteVisibilityStoreName dynamicproperties.StringPropertyFn EmitShardDiffLog dynamicproperties.BoolPropertyFn MaxAutoResetPoints dynamicproperties.IntPropertyFnWithDomainFilter ThrottledLogRPS dynamicproperties.IntPropertyFn EnableStickyQuery dynamicproperties.BoolPropertyFnWithDomainFilter ShutdownDrainDuration dynamicproperties.DurationPropertyFn WorkflowDeletionJitterRange dynamicproperties.IntPropertyFnWithDomainFilter DeleteHistoryEventContextTimeout dynamicproperties.IntPropertyFn MaxResponseSize int // HistoryCache settings // Change of these configs require shard restart HistoryCacheInitialSize dynamicproperties.IntPropertyFn HistoryCacheMaxSize dynamicproperties.IntPropertyFn HistoryCacheTTL dynamicproperties.DurationPropertyFn EnableSizeBasedHistoryExecutionCache dynamicproperties.BoolPropertyFn ExecutionCacheMaxByteSize dynamicproperties.IntPropertyFn // EventsCache settings // Change of these configs require shard restart EventsCacheInitialCount dynamicproperties.IntPropertyFn EventsCacheMaxCount dynamicproperties.IntPropertyFn EventsCacheMaxSize dynamicproperties.IntPropertyFn EventsCacheTTL dynamicproperties.DurationPropertyFn EventsCacheGlobalEnable dynamicproperties.BoolPropertyFn EventsCacheGlobalInitialCount dynamicproperties.IntPropertyFn EventsCacheGlobalMaxCount dynamicproperties.IntPropertyFn EnableSizeBasedHistoryEventCache dynamicproperties.BoolPropertyFn // ShardController settings RangeSizeBits uint AcquireShardInterval dynamicproperties.DurationPropertyFn AcquireShardConcurrency dynamicproperties.IntPropertyFn // the artificial delay added to standby cluster's view of active cluster's time StandbyClusterDelay dynamicproperties.DurationPropertyFn StandbyTaskMissingEventsResendDelay dynamicproperties.DurationPropertyFn StandbyTaskMissingEventsDiscardDelay dynamicproperties.DurationPropertyFn // Task process settings TaskProcessRPS dynamicproperties.IntPropertyFnWithDomainFilter TaskSchedulerType dynamicproperties.IntPropertyFn TaskSchedulerWorkerCount dynamicproperties.IntPropertyFn TaskSchedulerQueueSize dynamicproperties.IntPropertyFn TaskSchedulerDispatcherCount dynamicproperties.IntPropertyFn TaskSchedulerRoundRobinWeights dynamicproperties.MapPropertyFn TaskSchedulerDomainRoundRobinWeights dynamicproperties.MapPropertyFnWithDomainFilter TaskSchedulerGlobalDomainRPS dynamicproperties.IntPropertyFnWithDomainFilter TaskSchedulerEnableRateLimiter dynamicproperties.BoolPropertyFn TaskSchedulerEnableRateLimiterShadowMode dynamicproperties.BoolPropertyFnWithDomainFilter TaskCriticalRetryCount dynamicproperties.IntPropertyFn ActiveTaskRedispatchInterval dynamicproperties.DurationPropertyFn StandbyTaskRedispatchInterval dynamicproperties.DurationPropertyFn StandbyTaskReReplicationContextTimeout dynamicproperties.DurationPropertyFnWithDomainIDFilter EnableDropStuckTaskByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter ResurrectionCheckMinDelay dynamicproperties.DurationPropertyFnWithDomainFilter EnableHierarchicalWeightedRoundRobinTaskScheduler dynamicproperties.BoolPropertyFn EnableTaskListAwareTaskSchedulerByDomain dynamicproperties.BoolPropertyFnWithDomainFilter // History Queue (v2) settings EnableTimerQueueV2 dynamicproperties.BoolPropertyFnWithShardIDFilter EnableTransferQueueV2 dynamicproperties.BoolPropertyFnWithShardIDFilter QueueMaxPendingTaskCount dynamicproperties.IntPropertyFn EnableTimerQueueV2PendingTaskCountAlert dynamicproperties.BoolPropertyFnWithShardIDFilter EnableTransferQueueV2PendingTaskCountAlert dynamicproperties.BoolPropertyFnWithShardIDFilter QueueCriticalPendingTaskCount dynamicproperties.IntPropertyFn QueueMaxVirtualQueueCount dynamicproperties.IntPropertyFn VirtualSliceForceAppendInterval dynamicproperties.DurationPropertyFn // QueueProcessor settings QueueProcessorEnableSplit dynamicproperties.BoolPropertyFn QueueProcessorSplitMaxLevel dynamicproperties.IntPropertyFn QueueProcessorEnableRandomSplitByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter QueueProcessorRandomSplitProbability dynamicproperties.FloatPropertyFn QueueProcessorEnablePendingTaskSplitByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter QueueProcessorPendingTaskSplitThreshold dynamicproperties.MapPropertyFn QueueProcessorEnableStuckTaskSplitByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter QueueProcessorStuckTaskSplitThreshold dynamicproperties.MapPropertyFn QueueProcessorSplitLookAheadDurationByDomainID dynamicproperties.DurationPropertyFnWithDomainIDFilter QueueProcessorPollBackoffInterval dynamicproperties.DurationPropertyFn QueueProcessorPollBackoffIntervalJitterCoefficient dynamicproperties.FloatPropertyFn QueueProcessorEnablePersistQueueStates dynamicproperties.BoolPropertyFn QueueProcessorEnableLoadQueueStates dynamicproperties.BoolPropertyFn QueueProcessorEnableGracefulSyncShutdown dynamicproperties.BoolPropertyFn // TimerQueueProcessor settings TimerTaskBatchSize dynamicproperties.IntPropertyFn TimerTaskDeleteBatchSize dynamicproperties.IntPropertyFn TimerProcessorGetFailureRetryCount dynamicproperties.IntPropertyFn TimerProcessorCompleteTimerFailureRetryCount dynamicproperties.IntPropertyFn TimerProcessorUpdateAckInterval dynamicproperties.DurationPropertyFn TimerProcessorUpdateAckIntervalJitterCoefficient dynamicproperties.FloatPropertyFn TimerProcessorCompleteTimerInterval dynamicproperties.DurationPropertyFn TimerProcessorFailoverMaxStartJitterInterval dynamicproperties.DurationPropertyFn TimerProcessorFailoverMaxPollRPS dynamicproperties.IntPropertyFn TimerProcessorMaxPollRPS dynamicproperties.IntPropertyFn TimerProcessorMaxPollInterval dynamicproperties.DurationPropertyFn TimerProcessorMaxPollIntervalJitterCoefficient dynamicproperties.FloatPropertyFn TimerProcessorSplitQueueInterval dynamicproperties.DurationPropertyFn TimerProcessorSplitQueueIntervalJitterCoefficient dynamicproperties.FloatPropertyFn TimerProcessorMaxRedispatchQueueSize dynamicproperties.IntPropertyFn TimerProcessorMaxTimeShift dynamicproperties.DurationPropertyFn TimerProcessorHistoryArchivalSizeLimit dynamicproperties.IntPropertyFn TimerProcessorArchivalTimeLimit dynamicproperties.DurationPropertyFn DisableTimerFailoverQueue dynamicproperties.BoolPropertyFn // TransferQueueProcessor settings TransferTaskBatchSize dynamicproperties.IntPropertyFn TransferTaskDeleteBatchSize dynamicproperties.IntPropertyFn TransferProcessorCompleteTransferFailureRetryCount dynamicproperties.IntPropertyFn TransferProcessorFailoverMaxStartJitterInterval dynamicproperties.DurationPropertyFn TransferProcessorFailoverMaxPollRPS dynamicproperties.IntPropertyFn TransferProcessorMaxPollRPS dynamicproperties.IntPropertyFn TransferProcessorMaxPollInterval dynamicproperties.DurationPropertyFn TransferProcessorMaxPollIntervalJitterCoefficient dynamicproperties.FloatPropertyFn TransferProcessorSplitQueueInterval dynamicproperties.DurationPropertyFn TransferProcessorSplitQueueIntervalJitterCoefficient dynamicproperties.FloatPropertyFn TransferProcessorUpdateAckInterval dynamicproperties.DurationPropertyFn TransferProcessorUpdateAckIntervalJitterCoefficient dynamicproperties.FloatPropertyFn TransferProcessorCompleteTransferInterval dynamicproperties.DurationPropertyFn TransferProcessorMaxRedispatchQueueSize dynamicproperties.IntPropertyFn TransferProcessorEnableValidator dynamicproperties.BoolPropertyFn TransferProcessorValidationInterval dynamicproperties.DurationPropertyFn TransferProcessorVisibilityArchivalTimeLimit dynamicproperties.DurationPropertyFn DisableTransferFailoverQueue dynamicproperties.BoolPropertyFn // ReplicatorQueueProcessor settings ReplicatorTaskDeleteBatchSize dynamicproperties.IntPropertyFn ReplicatorReadTaskMaxRetryCount dynamicproperties.IntPropertyFn ReplicatorProcessorFetchTasksBatchSize dynamicproperties.IntPropertyFnWithShardIDFilter ReplicatorProcessorMaxTaskBatchSize dynamicproperties.IntPropertyFnWithShardIDFilter ReplicatorProcessorMinTaskBatchSize dynamicproperties.IntPropertyFnWithShardIDFilter ReplicatorProcessorBatchSizeStepCount dynamicproperties.IntPropertyFnWithShardIDFilter ReplicatorUpperLatency dynamicproperties.DurationPropertyFn ReplicatorCacheCapacity dynamicproperties.IntPropertyFn ReplicatorCacheMaxSize dynamicproperties.IntPropertyFn ReplicationBudgetManagerMaxSizeBytes dynamicproperties.IntPropertyFn ReplicationBudgetManagerMaxSizeCount dynamicproperties.IntPropertyFn ReplicationBudgetManagerSoftCapThreshold dynamicproperties.FloatPropertyFn // System Limits MaximumBufferedEventsBatch dynamicproperties.IntPropertyFn MaximumSignalsPerExecution dynamicproperties.IntPropertyFnWithDomainFilter // ShardUpdateMinInterval the minimal time interval which the shard info can be updated ShardUpdateMinInterval dynamicproperties.DurationPropertyFn // ShardSyncMinInterval the minimal time interval which the shard info should be sync to remote ShardSyncMinInterval dynamicproperties.DurationPropertyFn ShardSyncTimerJitterCoefficient dynamicproperties.FloatPropertyFn // Time to hold a poll request before returning an empty response // right now only used by GetMutableState LongPollExpirationInterval dynamicproperties.DurationPropertyFnWithDomainFilter // encoding the history events EventEncodingType dynamicproperties.StringPropertyFnWithDomainFilter // whether or not using ParentClosePolicy EnableParentClosePolicy dynamicproperties.BoolPropertyFnWithDomainFilter // whether or not enable system workers for processing parent close policy task EnableParentClosePolicyWorker dynamicproperties.BoolPropertyFn // parent close policy will be processed by sys workers(if enabled) if // the number of children greater than or equal to this threshold ParentClosePolicyThreshold dynamicproperties.IntPropertyFnWithDomainFilter // the batch size of parent close policy processed by sys workers ParentClosePolicyBatchSize dynamicproperties.IntPropertyFnWithDomainFilter // total number of parentClosePolicy system workflows NumParentClosePolicySystemWorkflows dynamicproperties.IntPropertyFn // Archival settings NumArchiveSystemWorkflows dynamicproperties.IntPropertyFn ArchiveRequestRPS dynamicproperties.IntPropertyFn ArchiveInlineHistoryRPS dynamicproperties.IntPropertyFn ArchiveInlineHistoryGlobalRPS dynamicproperties.IntPropertyFn ArchiveInlineVisibilityRPS dynamicproperties.IntPropertyFn ArchiveInlineVisibilityGlobalRPS dynamicproperties.IntPropertyFn AllowArchivingIncompleteHistory dynamicproperties.BoolPropertyFn // Size limit related settings BlobSizeLimitError dynamicproperties.IntPropertyFnWithDomainFilter BlobSizeLimitWarn dynamicproperties.IntPropertyFnWithDomainFilter HistorySizeLimitError dynamicproperties.IntPropertyFnWithDomainFilter HistorySizeLimitWarn dynamicproperties.IntPropertyFnWithDomainFilter HistoryCountLimitError dynamicproperties.IntPropertyFnWithDomainFilter HistoryCountLimitWarn dynamicproperties.IntPropertyFnWithDomainFilter PendingActivitiesCountLimitError dynamicproperties.IntPropertyFn PendingActivitiesCountLimitWarn dynamicproperties.IntPropertyFn PendingActivityValidationEnabled dynamicproperties.BoolPropertyFn // ValidSearchAttributes is legal indexed keys that can be used in list APIs EnableQueryAttributeValidation dynamicproperties.BoolPropertyFn ValidSearchAttributes dynamicproperties.MapPropertyFn SearchAttributesNumberOfKeysLimit dynamicproperties.IntPropertyFnWithDomainFilter SearchAttributesSizeOfValueLimit dynamicproperties.IntPropertyFnWithDomainFilter SearchAttributesTotalSizeLimit dynamicproperties.IntPropertyFnWithDomainFilter SearchAttributesHiddenValueKeys dynamicproperties.MapPropertyFn // Decision settings // StickyTTL is to expire a sticky tasklist if no update more than this duration // TODO https://github.com/uber/cadence/issues/2357 StickyTTL dynamicproperties.DurationPropertyFnWithDomainFilter // DecisionHeartbeatTimeout is to timeout behavior of: RespondDecisionTaskComplete with ForceCreateNewDecisionTask == true without any decisions // So that decision will be scheduled to another worker(by clear stickyness) DecisionHeartbeatTimeout dynamicproperties.DurationPropertyFnWithDomainFilter // MaxDecisionStartToCloseSeconds is the StartToCloseSeconds for decision MaxDecisionStartToCloseSeconds dynamicproperties.IntPropertyFnWithDomainFilter DecisionRetryCriticalAttempts dynamicproperties.IntPropertyFn DecisionRetryMaxAttempts dynamicproperties.IntPropertyFnWithDomainFilter EnforceDecisionTaskAttempts dynamicproperties.BoolPropertyFnWithDomainFilter NormalDecisionScheduleToStartMaxAttempts dynamicproperties.IntPropertyFnWithDomainFilter NormalDecisionScheduleToStartTimeout dynamicproperties.DurationPropertyFnWithDomainFilter // The following is used by the new RPC replication stack ReplicationTaskFetcherParallelism dynamicproperties.IntPropertyFn ReplicationTaskFetcherAggregationInterval dynamicproperties.DurationPropertyFn ReplicationTaskFetcherTimerJitterCoefficient dynamicproperties.FloatPropertyFn ReplicationTaskFetcherErrorRetryWait dynamicproperties.DurationPropertyFn ReplicationTaskFetcherServiceBusyWait dynamicproperties.DurationPropertyFn ReplicationTaskProcessorErrorRetryWait dynamicproperties.DurationPropertyFnWithShardIDFilter ReplicationTaskProcessorErrorRetryMaxAttempts dynamicproperties.IntPropertyFnWithShardIDFilter ReplicationTaskProcessorErrorSecondRetryWait dynamicproperties.DurationPropertyFnWithShardIDFilter ReplicationTaskProcessorErrorSecondRetryMaxWait dynamicproperties.DurationPropertyFnWithShardIDFilter ReplicationTaskProcessorErrorSecondRetryExpiration dynamicproperties.DurationPropertyFnWithShardIDFilter ReplicationTaskProcessorNoTaskRetryWait dynamicproperties.DurationPropertyFnWithShardIDFilter ReplicationTaskProcessorCleanupInterval dynamicproperties.DurationPropertyFnWithShardIDFilter ReplicationTaskProcessorCleanupJitterCoefficient dynamicproperties.FloatPropertyFnWithShardIDFilter ReplicationTaskProcessorStartWait dynamicproperties.DurationPropertyFnWithShardIDFilter ReplicationTaskProcessorStartWaitJitterCoefficient dynamicproperties.FloatPropertyFnWithShardIDFilter ReplicationTaskProcessorHostQPS dynamicproperties.FloatPropertyFn ReplicationTaskProcessorShardQPS dynamicproperties.FloatPropertyFn ReplicationTaskGenerationQPS dynamicproperties.FloatPropertyFn EnableReplicationTaskGeneration dynamicproperties.BoolPropertyFnWithDomainIDAndWorkflowIDFilter EnableRecordWorkflowExecutionUninitialized dynamicproperties.BoolPropertyFnWithDomainFilter EnableCleanupOrphanedHistoryBranchOnWorkflowCreation dynamicproperties.BoolPropertyFnWithDomainFilter ReplicationTaskProcessorLatencyLogThreshold dynamicproperties.DurationPropertyFn // The following are used by the history workflowID cache WorkflowIDExternalRPS dynamicproperties.IntPropertyFnWithDomainFilter WorkflowIDInternalRPS dynamicproperties.IntPropertyFnWithDomainFilter // The following are used by consistent query EnableConsistentQuery dynamicproperties.BoolPropertyFn EnableConsistentQueryByDomain dynamicproperties.BoolPropertyFnWithDomainFilter MaxBufferedQueryCount dynamicproperties.IntPropertyFn // EnableContextHeaderInVisibility whether to enable indexing context header in visibility EnableContextHeaderInVisibility dynamicproperties.BoolPropertyFnWithDomainFilter EnableCrossClusterOperationsForDomain dynamicproperties.BoolPropertyFnWithDomainFilter // Data integrity check related config knobs MutableStateChecksumGenProbability dynamicproperties.IntPropertyFnWithDomainFilter MutableStateChecksumVerifyProbability dynamicproperties.IntPropertyFnWithDomainFilter MutableStateChecksumInvalidateBefore dynamicproperties.FloatPropertyFn EnableRetryForChecksumFailure dynamicproperties.BoolPropertyFnWithDomainFilter // History check for corruptions EnableHistoryCorruptionCheck dynamicproperties.BoolPropertyFnWithDomainFilter // Failover marker heartbeat NotifyFailoverMarkerInterval dynamicproperties.DurationPropertyFn NotifyFailoverMarkerTimerJitterCoefficient dynamicproperties.FloatPropertyFn EnableGracefulFailover dynamicproperties.BoolPropertyFn // Allows worker to dispatch activity tasks through local tunnel after decisions are made. This is an performance optimization to skip activity scheduling efforts. EnableActivityLocalDispatchByDomain dynamicproperties.BoolPropertyFnWithDomainFilter // Max # of activity tasks to dispatch to matching before creating transfer tasks. This is an performance optimization to skip activity scheduling efforts. MaxActivityCountDispatchByDomain dynamicproperties.IntPropertyFnWithDomainFilter ActivityMaxScheduleToStartTimeoutForRetry dynamicproperties.DurationPropertyFnWithDomainFilter // Debugging configurations EnableDebugMode bool // note that this value is initialized once on service start EnableTaskInfoLogByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter EnableTimerDebugLogByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter // Hotshard stuff SampleLoggingRate dynamicproperties.IntPropertyFn EnableShardIDMetrics dynamicproperties.BoolPropertyFn LargeShardHistorySizeMetricThreshold dynamicproperties.IntPropertyFn LargeShardHistoryEventMetricThreshold dynamicproperties.IntPropertyFn LargeShardHistoryBlobMetricThreshold dynamicproperties.IntPropertyFn EnableStrongIdempotency dynamicproperties.BoolPropertyFnWithDomainFilter EnableStrongIdempotencySanityCheck dynamicproperties.BoolPropertyFnWithDomainFilter // Global ratelimiter GlobalRatelimiterNewDataWeight dynamicproperties.FloatPropertyFn GlobalRatelimiterUpdateInterval dynamicproperties.DurationPropertyFn GlobalRatelimiterDecayAfter dynamicproperties.DurationPropertyFn GlobalRatelimiterGCAfter dynamicproperties.DurationPropertyFn // HostName for machine running the service HostName string } // New returns new service config with default values func New(dc *dynamicconfig.Collection, numberOfShards int, maxMessageSize int, isAdvancedVisConfigExist bool, hostname string) *Config { cfg := &Config{ NumberOfShards: numberOfShards, IsAdvancedVisConfigExist: isAdvancedVisConfigExist, RPS: dc.GetIntProperty(dynamicproperties.HistoryRPS), MaxIDLengthWarnLimit: dc.GetIntProperty(dynamicproperties.MaxIDLengthWarnLimit), DomainNameMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.DomainNameMaxLength), IdentityMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.IdentityMaxLength), WorkflowIDMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.WorkflowIDMaxLength), SignalNameMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.SignalNameMaxLength), WorkflowTypeMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.WorkflowTypeMaxLength), RequestIDMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.RequestIDMaxLength), TaskListNameMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.TaskListNameMaxLength), ActivityIDMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.ActivityIDMaxLength), ActivityTypeMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.ActivityTypeMaxLength), MarkerNameMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.MarkerNameMaxLength), TimerIDMaxLength: dc.GetIntPropertyFilteredByDomain(dynamicproperties.TimerIDMaxLength), PersistenceMaxQPS: dc.GetIntProperty(dynamicproperties.HistoryPersistenceMaxQPS), PersistenceGlobalMaxQPS: dc.GetIntProperty(dynamicproperties.HistoryPersistenceGlobalMaxQPS), ShutdownDrainDuration: dc.GetDurationProperty(dynamicproperties.HistoryShutdownDrainDuration), EnableVisibilitySampling: dc.GetBoolProperty(dynamicproperties.EnableVisibilitySampling), EnableReadFromClosedExecutionV2: dc.GetBoolProperty(dynamicproperties.EnableReadFromClosedExecutionV2), VisibilityOpenMaxQPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.HistoryVisibilityOpenMaxQPS), VisibilityClosedMaxQPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.HistoryVisibilityClosedMaxQPS), MaxAutoResetPoints: dc.GetIntPropertyFilteredByDomain(dynamicproperties.HistoryMaxAutoResetPoints), MaxDecisionStartToCloseSeconds: dc.GetIntPropertyFilteredByDomain(dynamicproperties.MaxDecisionStartToCloseSeconds), WriteVisibilityStoreName: dc.GetStringProperty(dynamicproperties.WriteVisibilityStoreName), EmitShardDiffLog: dc.GetBoolProperty(dynamicproperties.EmitShardDiffLog), HistoryCacheInitialSize: dc.GetIntProperty(dynamicproperties.HistoryCacheInitialSize), HistoryCacheMaxSize: dc.GetIntProperty(dynamicproperties.HistoryCacheMaxSize), ExecutionCacheMaxByteSize: dc.GetIntProperty(dynamicproperties.ExecutionCacheMaxByteSize), HistoryCacheTTL: dc.GetDurationProperty(dynamicproperties.HistoryCacheTTL), EnableSizeBasedHistoryExecutionCache: dc.GetBoolProperty(dynamicproperties.EnableSizeBasedHistoryExecutionCache), EventsCacheInitialCount: dc.GetIntProperty(dynamicproperties.EventsCacheInitialCount), EventsCacheMaxCount: dc.GetIntProperty(dynamicproperties.EventsCacheMaxCount), EventsCacheMaxSize: dc.GetIntProperty(dynamicproperties.EventsCacheMaxSize), EventsCacheTTL: dc.GetDurationProperty(dynamicproperties.EventsCacheTTL), EventsCacheGlobalEnable: dc.GetBoolProperty(dynamicproperties.EventsCacheGlobalEnable), EventsCacheGlobalInitialCount: dc.GetIntProperty(dynamicproperties.EventsCacheGlobalInitialCount), EventsCacheGlobalMaxCount: dc.GetIntProperty(dynamicproperties.EventsCacheGlobalMaxCount), EnableSizeBasedHistoryEventCache: dc.GetBoolProperty(dynamicproperties.EnableSizeBasedHistoryEventCache), RangeSizeBits: 20, // 20 bits for sequencer, 2^20 sequence number for any range AcquireShardInterval: dc.GetDurationProperty(dynamicproperties.AcquireShardInterval), AcquireShardConcurrency: dc.GetIntProperty(dynamicproperties.AcquireShardConcurrency), StandbyClusterDelay: dc.GetDurationProperty(dynamicproperties.StandbyClusterDelay), StandbyTaskMissingEventsResendDelay: dc.GetDurationProperty(dynamicproperties.StandbyTaskMissingEventsResendDelay), StandbyTaskMissingEventsDiscardDelay: dc.GetDurationProperty(dynamicproperties.StandbyTaskMissingEventsDiscardDelay), WorkflowDeletionJitterRange: dc.GetIntPropertyFilteredByDomain(dynamicproperties.WorkflowDeletionJitterRange), DeleteHistoryEventContextTimeout: dc.GetIntProperty(dynamicproperties.DeleteHistoryEventContextTimeout), MaxResponseSize: maxMessageSize, TaskProcessRPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.TaskProcessRPS), TaskSchedulerType: dc.GetIntProperty(dynamicproperties.TaskSchedulerType), TaskSchedulerWorkerCount: dc.GetIntProperty(dynamicproperties.TaskSchedulerWorkerCount), TaskSchedulerQueueSize: dc.GetIntProperty(dynamicproperties.TaskSchedulerQueueSize), TaskSchedulerDispatcherCount: dc.GetIntProperty(dynamicproperties.TaskSchedulerDispatcherCount), TaskSchedulerRoundRobinWeights: dc.GetMapProperty(dynamicproperties.TaskSchedulerRoundRobinWeights), TaskSchedulerDomainRoundRobinWeights: dc.GetMapPropertyFilteredByDomain(dynamicproperties.TaskSchedulerDomainRoundRobinWeights), TaskSchedulerGlobalDomainRPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.TaskSchedulerGlobalDomainRPS), TaskSchedulerEnableRateLimiter: dc.GetBoolProperty(dynamicproperties.TaskSchedulerEnableRateLimiter), TaskSchedulerEnableRateLimiterShadowMode: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.TaskSchedulerEnableRateLimiterShadowMode), TaskCriticalRetryCount: dc.GetIntProperty(dynamicproperties.TaskCriticalRetryCount), ActiveTaskRedispatchInterval: dc.GetDurationProperty(dynamicproperties.ActiveTaskRedispatchInterval), StandbyTaskRedispatchInterval: dc.GetDurationProperty(dynamicproperties.StandbyTaskRedispatchInterval), StandbyTaskReReplicationContextTimeout: dc.GetDurationPropertyFilteredByDomainID(dynamicproperties.StandbyTaskReReplicationContextTimeout), EnableDropStuckTaskByDomainID: dc.GetBoolPropertyFilteredByDomainID(dynamicproperties.EnableDropStuckTaskByDomainID), ResurrectionCheckMinDelay: dc.GetDurationPropertyFilteredByDomain(dynamicproperties.ResurrectionCheckMinDelay), EnableHierarchicalWeightedRoundRobinTaskScheduler: dc.GetBoolProperty(dynamicproperties.EnableHierarchicalWeightedRoundRobinTaskScheduler), EnableTaskListAwareTaskSchedulerByDomain: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableTaskListAwareTaskSchedulerByDomain), EnableTimerQueueV2: dc.GetBoolPropertyFilteredByShardID(dynamicproperties.EnableTimerQueueV2), EnableTransferQueueV2: dc.GetBoolPropertyFilteredByShardID(dynamicproperties.EnableTransferQueueV2), QueueMaxPendingTaskCount: dc.GetIntProperty(dynamicproperties.QueueMaxPendingTaskCount), EnableTimerQueueV2PendingTaskCountAlert: dc.GetBoolPropertyFilteredByShardID(dynamicproperties.EnableTimerQueueV2PendingTaskCountAlert), EnableTransferQueueV2PendingTaskCountAlert: dc.GetBoolPropertyFilteredByShardID(dynamicproperties.EnableTransferQueueV2PendingTaskCountAlert), QueueCriticalPendingTaskCount: dc.GetIntProperty(dynamicproperties.QueueCriticalPendingTaskCount), QueueMaxVirtualQueueCount: dc.GetIntProperty(dynamicproperties.QueueMaxVirtualQueueCount), VirtualSliceForceAppendInterval: dc.GetDurationProperty(dynamicproperties.VirtualSliceForceAppendInterval), QueueProcessorEnableSplit: dc.GetBoolProperty(dynamicproperties.QueueProcessorEnableSplit), QueueProcessorSplitMaxLevel: dc.GetIntProperty(dynamicproperties.QueueProcessorSplitMaxLevel), QueueProcessorEnableRandomSplitByDomainID: dc.GetBoolPropertyFilteredByDomainID(dynamicproperties.QueueProcessorEnableRandomSplitByDomainID), QueueProcessorRandomSplitProbability: dc.GetFloat64Property(dynamicproperties.QueueProcessorRandomSplitProbability), QueueProcessorEnablePendingTaskSplitByDomainID: dc.GetBoolPropertyFilteredByDomainID(dynamicproperties.QueueProcessorEnablePendingTaskSplitByDomainID), QueueProcessorPendingTaskSplitThreshold: dc.GetMapProperty(dynamicproperties.QueueProcessorPendingTaskSplitThreshold), QueueProcessorEnableStuckTaskSplitByDomainID: dc.GetBoolPropertyFilteredByDomainID(dynamicproperties.QueueProcessorEnableStuckTaskSplitByDomainID), QueueProcessorStuckTaskSplitThreshold: dc.GetMapProperty(dynamicproperties.QueueProcessorStuckTaskSplitThreshold), QueueProcessorSplitLookAheadDurationByDomainID: dc.GetDurationPropertyFilteredByDomainID(dynamicproperties.QueueProcessorSplitLookAheadDurationByDomainID), QueueProcessorPollBackoffInterval: dc.GetDurationProperty(dynamicproperties.QueueProcessorPollBackoffInterval), QueueProcessorPollBackoffIntervalJitterCoefficient: dc.GetFloat64Property(dynamicproperties.QueueProcessorPollBackoffIntervalJitterCoefficient), QueueProcessorEnablePersistQueueStates: dc.GetBoolProperty(dynamicproperties.QueueProcessorEnablePersistQueueStates), QueueProcessorEnableLoadQueueStates: dc.GetBoolProperty(dynamicproperties.QueueProcessorEnableLoadQueueStates), QueueProcessorEnableGracefulSyncShutdown: dc.GetBoolProperty(dynamicproperties.QueueProcessorEnableGracefulSyncShutdown), TimerTaskBatchSize: dc.GetIntProperty(dynamicproperties.TimerTaskBatchSize), TimerTaskDeleteBatchSize: dc.GetIntProperty(dynamicproperties.TimerTaskDeleteBatchSize), TimerProcessorGetFailureRetryCount: dc.GetIntProperty(dynamicproperties.TimerProcessorGetFailureRetryCount), TimerProcessorCompleteTimerFailureRetryCount: dc.GetIntProperty(dynamicproperties.TimerProcessorCompleteTimerFailureRetryCount), TimerProcessorUpdateAckInterval: dc.GetDurationProperty(dynamicproperties.TimerProcessorUpdateAckInterval), TimerProcessorUpdateAckIntervalJitterCoefficient: dc.GetFloat64Property(dynamicproperties.TimerProcessorUpdateAckIntervalJitterCoefficient), TimerProcessorCompleteTimerInterval: dc.GetDurationProperty(dynamicproperties.TimerProcessorCompleteTimerInterval), TimerProcessorFailoverMaxStartJitterInterval: dc.GetDurationProperty(dynamicproperties.TimerProcessorFailoverMaxStartJitterInterval), TimerProcessorFailoverMaxPollRPS: dc.GetIntProperty(dynamicproperties.TimerProcessorFailoverMaxPollRPS), TimerProcessorMaxPollRPS: dc.GetIntProperty(dynamicproperties.TimerProcessorMaxPollRPS), TimerProcessorMaxPollInterval: dc.GetDurationProperty(dynamicproperties.TimerProcessorMaxPollInterval), TimerProcessorMaxPollIntervalJitterCoefficient: dc.GetFloat64Property(dynamicproperties.TimerProcessorMaxPollIntervalJitterCoefficient), TimerProcessorSplitQueueInterval: dc.GetDurationProperty(dynamicproperties.TimerProcessorSplitQueueInterval), TimerProcessorSplitQueueIntervalJitterCoefficient: dc.GetFloat64Property(dynamicproperties.TimerProcessorSplitQueueIntervalJitterCoefficient), TimerProcessorMaxRedispatchQueueSize: dc.GetIntProperty(dynamicproperties.TimerProcessorMaxRedispatchQueueSize), TimerProcessorMaxTimeShift: dc.GetDurationProperty(dynamicproperties.TimerProcessorMaxTimeShift), TimerProcessorHistoryArchivalSizeLimit: dc.GetIntProperty(dynamicproperties.TimerProcessorHistoryArchivalSizeLimit), TimerProcessorArchivalTimeLimit: dc.GetDurationProperty(dynamicproperties.TimerProcessorArchivalTimeLimit), DisableTimerFailoverQueue: dc.GetBoolProperty(dynamicproperties.DisableTimerFailoverQueue), TransferTaskBatchSize: dc.GetIntProperty(dynamicproperties.TransferTaskBatchSize), TransferTaskDeleteBatchSize: dc.GetIntProperty(dynamicproperties.TransferTaskDeleteBatchSize), TransferProcessorFailoverMaxStartJitterInterval: dc.GetDurationProperty(dynamicproperties.TransferProcessorFailoverMaxStartJitterInterval), TransferProcessorFailoverMaxPollRPS: dc.GetIntProperty(dynamicproperties.TransferProcessorFailoverMaxPollRPS), TransferProcessorMaxPollRPS: dc.GetIntProperty(dynamicproperties.TransferProcessorMaxPollRPS), TransferProcessorCompleteTransferFailureRetryCount: dc.GetIntProperty(dynamicproperties.TransferProcessorCompleteTransferFailureRetryCount), TransferProcessorMaxPollInterval: dc.GetDurationProperty(dynamicproperties.TransferProcessorMaxPollInterval), TransferProcessorMaxPollIntervalJitterCoefficient: dc.GetFloat64Property(dynamicproperties.TransferProcessorMaxPollIntervalJitterCoefficient), TransferProcessorSplitQueueInterval: dc.GetDurationProperty(dynamicproperties.TransferProcessorSplitQueueInterval), TransferProcessorSplitQueueIntervalJitterCoefficient: dc.GetFloat64Property(dynamicproperties.TransferProcessorSplitQueueIntervalJitterCoefficient), TransferProcessorUpdateAckInterval: dc.GetDurationProperty(dynamicproperties.TransferProcessorUpdateAckInterval), TransferProcessorUpdateAckIntervalJitterCoefficient: dc.GetFloat64Property(dynamicproperties.TransferProcessorUpdateAckIntervalJitterCoefficient), TransferProcessorCompleteTransferInterval: dc.GetDurationProperty(dynamicproperties.TransferProcessorCompleteTransferInterval), TransferProcessorMaxRedispatchQueueSize: dc.GetIntProperty(dynamicproperties.TransferProcessorMaxRedispatchQueueSize), TransferProcessorEnableValidator: dc.GetBoolProperty(dynamicproperties.TransferProcessorEnableValidator), TransferProcessorValidationInterval: dc.GetDurationProperty(dynamicproperties.TransferProcessorValidationInterval), TransferProcessorVisibilityArchivalTimeLimit: dc.GetDurationProperty(dynamicproperties.TransferProcessorVisibilityArchivalTimeLimit), DisableTransferFailoverQueue: dc.GetBoolProperty(dynamicproperties.DisableTransferFailoverQueue), ReplicatorTaskDeleteBatchSize: dc.GetIntProperty(dynamicproperties.ReplicatorTaskDeleteBatchSize), ReplicatorReadTaskMaxRetryCount: dc.GetIntProperty(dynamicproperties.ReplicatorReadTaskMaxRetryCount), ReplicatorProcessorFetchTasksBatchSize: dc.GetIntPropertyFilteredByShardID(dynamicproperties.ReplicatorTaskBatchSize), ReplicatorProcessorMaxTaskBatchSize: dc.GetIntPropertyFilteredByShardID(dynamicproperties.ReplicatorMaxTaskBatchSize), ReplicatorProcessorMinTaskBatchSize: dc.GetIntPropertyFilteredByShardID(dynamicproperties.ReplicatorMinTaskBatchSize), ReplicatorProcessorBatchSizeStepCount: dc.GetIntPropertyFilteredByShardID(dynamicproperties.ReplicatorTaskBatchStepCount), ReplicatorUpperLatency: dc.GetDurationProperty(dynamicproperties.ReplicatorUpperLatency), ReplicatorCacheCapacity: dc.GetIntProperty(dynamicproperties.ReplicatorCacheCapacity), ReplicatorCacheMaxSize: dc.GetIntProperty(dynamicproperties.ReplicatorCacheMaxSize), ReplicationBudgetManagerMaxSizeBytes: dc.GetIntProperty(dynamicproperties.ReplicationBudgetManagerMaxSizeBytes), ReplicationBudgetManagerMaxSizeCount: dc.GetIntProperty(dynamicproperties.ReplicationBudgetManagerMaxSizeCount), ReplicationBudgetManagerSoftCapThreshold: dc.GetFloat64Property(dynamicproperties.ReplicationBudgetManagerSoftCapThreshold), MaximumBufferedEventsBatch: dc.GetIntProperty(dynamicproperties.MaximumBufferedEventsBatch), MaximumSignalsPerExecution: dc.GetIntPropertyFilteredByDomain(dynamicproperties.MaximumSignalsPerExecution), ShardUpdateMinInterval: dc.GetDurationProperty(dynamicproperties.ShardUpdateMinInterval), ShardSyncMinInterval: dc.GetDurationProperty(dynamicproperties.ShardSyncMinInterval), ShardSyncTimerJitterCoefficient: dc.GetFloat64Property(dynamicproperties.TransferProcessorMaxPollIntervalJitterCoefficient), // history client: client/history/client.go set the client timeout 30s LongPollExpirationInterval: dc.GetDurationPropertyFilteredByDomain(dynamicproperties.HistoryLongPollExpirationInterval), EventEncodingType: dc.GetStringPropertyFilteredByDomain(dynamicproperties.DefaultEventEncoding), EnableParentClosePolicy: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableParentClosePolicy), NumParentClosePolicySystemWorkflows: dc.GetIntProperty(dynamicproperties.NumParentClosePolicySystemWorkflows), EnableParentClosePolicyWorker: dc.GetBoolProperty(dynamicproperties.EnableParentClosePolicyWorker), ParentClosePolicyThreshold: dc.GetIntPropertyFilteredByDomain(dynamicproperties.ParentClosePolicyThreshold), ParentClosePolicyBatchSize: dc.GetIntPropertyFilteredByDomain(dynamicproperties.ParentClosePolicyBatchSize), NumArchiveSystemWorkflows: dc.GetIntProperty(dynamicproperties.NumArchiveSystemWorkflows), ArchiveRequestRPS: dc.GetIntProperty(dynamicproperties.ArchiveRequestRPS), ArchiveInlineHistoryRPS: dc.GetIntProperty(dynamicproperties.ArchiveInlineHistoryRPS), ArchiveInlineHistoryGlobalRPS: dc.GetIntProperty(dynamicproperties.ArchiveInlineHistoryGlobalRPS), ArchiveInlineVisibilityRPS: dc.GetIntProperty(dynamicproperties.ArchiveInlineVisibilityRPS), ArchiveInlineVisibilityGlobalRPS: dc.GetIntProperty(dynamicproperties.ArchiveInlineVisibilityGlobalRPS), AllowArchivingIncompleteHistory: dc.GetBoolProperty(dynamicproperties.AllowArchivingIncompleteHistory), BlobSizeLimitError: dc.GetIntPropertyFilteredByDomain(dynamicproperties.BlobSizeLimitError), BlobSizeLimitWarn: dc.GetIntPropertyFilteredByDomain(dynamicproperties.BlobSizeLimitWarn), HistorySizeLimitError: dc.GetIntPropertyFilteredByDomain(dynamicproperties.HistorySizeLimitError), HistorySizeLimitWarn: dc.GetIntPropertyFilteredByDomain(dynamicproperties.HistorySizeLimitWarn), HistoryCountLimitError: dc.GetIntPropertyFilteredByDomain(dynamicproperties.HistoryCountLimitError), HistoryCountLimitWarn: dc.GetIntPropertyFilteredByDomain(dynamicproperties.HistoryCountLimitWarn), PendingActivitiesCountLimitError: dc.GetIntProperty(dynamicproperties.PendingActivitiesCountLimitError), PendingActivitiesCountLimitWarn: dc.GetIntProperty(dynamicproperties.PendingActivitiesCountLimitWarn), PendingActivityValidationEnabled: dc.GetBoolProperty(dynamicproperties.EnablePendingActivityValidation), ThrottledLogRPS: dc.GetIntProperty(dynamicproperties.HistoryThrottledLogRPS), EnableStickyQuery: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableStickyQuery), EnableQueryAttributeValidation: dc.GetBoolProperty(dynamicproperties.EnableQueryAttributeValidation), ValidSearchAttributes: dc.GetMapProperty(dynamicproperties.ValidSearchAttributes), SearchAttributesNumberOfKeysLimit: dc.GetIntPropertyFilteredByDomain(dynamicproperties.SearchAttributesNumberOfKeysLimit), SearchAttributesSizeOfValueLimit: dc.GetIntPropertyFilteredByDomain(dynamicproperties.SearchAttributesSizeOfValueLimit), SearchAttributesTotalSizeLimit: dc.GetIntPropertyFilteredByDomain(dynamicproperties.SearchAttributesTotalSizeLimit), SearchAttributesHiddenValueKeys: dc.GetMapProperty(dynamicproperties.SearchAttributesHiddenValueKeys), StickyTTL: dc.GetDurationPropertyFilteredByDomain(dynamicproperties.StickyTTL), DecisionHeartbeatTimeout: dc.GetDurationPropertyFilteredByDomain(dynamicproperties.DecisionHeartbeatTimeout), DecisionRetryCriticalAttempts: dc.GetIntProperty(dynamicproperties.DecisionRetryCriticalAttempts), DecisionRetryMaxAttempts: dc.GetIntPropertyFilteredByDomain(dynamicproperties.DecisionRetryMaxAttempts), EnforceDecisionTaskAttempts: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnforceDecisionTaskAttempts), NormalDecisionScheduleToStartMaxAttempts: dc.GetIntPropertyFilteredByDomain(dynamicproperties.NormalDecisionScheduleToStartMaxAttempts), NormalDecisionScheduleToStartTimeout: dc.GetDurationPropertyFilteredByDomain(dynamicproperties.NormalDecisionScheduleToStartTimeout), ReplicationTaskFetcherParallelism: dc.GetIntProperty(dynamicproperties.ReplicationTaskFetcherParallelism), ReplicationTaskFetcherAggregationInterval: dc.GetDurationProperty(dynamicproperties.ReplicationTaskFetcherAggregationInterval), ReplicationTaskFetcherTimerJitterCoefficient: dc.GetFloat64Property(dynamicproperties.ReplicationTaskFetcherTimerJitterCoefficient), ReplicationTaskFetcherErrorRetryWait: dc.GetDurationProperty(dynamicproperties.ReplicationTaskFetcherErrorRetryWait), ReplicationTaskFetcherServiceBusyWait: dc.GetDurationProperty(dynamicproperties.ReplicationTaskFetcherServiceBusyWait), ReplicationTaskProcessorErrorRetryWait: dc.GetDurationPropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorErrorRetryWait), ReplicationTaskProcessorErrorRetryMaxAttempts: dc.GetIntPropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorErrorRetryMaxAttempts), ReplicationTaskProcessorErrorSecondRetryWait: dc.GetDurationPropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorErrorSecondRetryWait), ReplicationTaskProcessorErrorSecondRetryMaxWait: dc.GetDurationPropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorErrorSecondRetryMaxWait), ReplicationTaskProcessorErrorSecondRetryExpiration: dc.GetDurationPropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorErrorSecondRetryExpiration), ReplicationTaskProcessorNoTaskRetryWait: dc.GetDurationPropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorNoTaskInitialWait), ReplicationTaskProcessorCleanupInterval: dc.GetDurationPropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorCleanupInterval), ReplicationTaskProcessorCleanupJitterCoefficient: dc.GetFloat64PropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorCleanupJitterCoefficient), ReplicationTaskProcessorStartWait: dc.GetDurationPropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorStartWait), ReplicationTaskProcessorStartWaitJitterCoefficient: dc.GetFloat64PropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorStartWaitJitterCoefficient), ReplicationTaskProcessorHostQPS: dc.GetFloat64Property(dynamicproperties.ReplicationTaskProcessorHostQPS), ReplicationTaskProcessorShardQPS: dc.GetFloat64Property(dynamicproperties.ReplicationTaskProcessorShardQPS), ReplicationTaskGenerationQPS: dc.GetFloat64Property(dynamicproperties.ReplicationTaskGenerationQPS), EnableReplicationTaskGeneration: dc.GetBoolPropertyFilteredByDomainIDAndWorkflowID(dynamicproperties.EnableReplicationTaskGeneration), EnableRecordWorkflowExecutionUninitialized: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableRecordWorkflowExecutionUninitialized), EnableCleanupOrphanedHistoryBranchOnWorkflowCreation: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableCleanupOrphanedHistoryBranchOnWorkflowCreation), ReplicationTaskProcessorLatencyLogThreshold: dc.GetDurationProperty(dynamicproperties.ReplicationTaskProcessorLatencyLogThreshold), WorkflowIDExternalRPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.WorkflowIDExternalRPS), WorkflowIDInternalRPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.WorkflowIDInternalRPS), EnableConsistentQuery: dc.GetBoolProperty(dynamicproperties.EnableConsistentQuery), EnableConsistentQueryByDomain: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableConsistentQueryByDomain), EnableContextHeaderInVisibility: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableContextHeaderInVisibility), EnableCrossClusterOperationsForDomain: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableCrossClusterOperationsForDomain), MaxBufferedQueryCount: dc.GetIntProperty(dynamicproperties.MaxBufferedQueryCount), MutableStateChecksumGenProbability: dc.GetIntPropertyFilteredByDomain(dynamicproperties.MutableStateChecksumGenProbability), MutableStateChecksumVerifyProbability: dc.GetIntPropertyFilteredByDomain(dynamicproperties.MutableStateChecksumVerifyProbability), MutableStateChecksumInvalidateBefore: dc.GetFloat64Property(dynamicproperties.MutableStateChecksumInvalidateBefore), EnableRetryForChecksumFailure: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableRetryForChecksumFailure), EnableHistoryCorruptionCheck: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableHistoryCorruptionCheck), NotifyFailoverMarkerInterval: dc.GetDurationProperty(dynamicproperties.NotifyFailoverMarkerInterval), NotifyFailoverMarkerTimerJitterCoefficient: dc.GetFloat64Property(dynamicproperties.NotifyFailoverMarkerTimerJitterCoefficient), EnableGracefulFailover: dc.GetBoolProperty(dynamicproperties.EnableGracefulFailover), EnableActivityLocalDispatchByDomain: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableActivityLocalDispatchByDomain), MaxActivityCountDispatchByDomain: dc.GetIntPropertyFilteredByDomain(dynamicproperties.MaxActivityCountDispatchByDomain), ActivityMaxScheduleToStartTimeoutForRetry: dc.GetDurationPropertyFilteredByDomain(dynamicproperties.ActivityMaxScheduleToStartTimeoutForRetry), EnableDebugMode: dc.GetBoolProperty(dynamicproperties.EnableDebugMode)(), EnableTaskInfoLogByDomainID: dc.GetBoolPropertyFilteredByDomainID(dynamicproperties.HistoryEnableTaskInfoLogByDomainID), EnableTimerDebugLogByDomainID: dc.GetBoolPropertyFilteredByDomainID(dynamicproperties.EnableTimerDebugLogByDomainID), SampleLoggingRate: dc.GetIntProperty(dynamicproperties.SampleLoggingRate), EnableShardIDMetrics: dc.GetBoolProperty(dynamicproperties.EnableShardIDMetrics), LargeShardHistorySizeMetricThreshold: dc.GetIntProperty(dynamicproperties.LargeShardHistorySizeMetricThreshold), LargeShardHistoryEventMetricThreshold: dc.GetIntProperty(dynamicproperties.LargeShardHistoryEventMetricThreshold), LargeShardHistoryBlobMetricThreshold: dc.GetIntProperty(dynamicproperties.LargeShardHistoryBlobMetricThreshold), EnableStrongIdempotency: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableStrongIdempotency), EnableStrongIdempotencySanityCheck: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableStrongIdempotencySanityCheck), GlobalRatelimiterNewDataWeight: dc.GetFloat64Property(dynamicproperties.HistoryGlobalRatelimiterNewDataWeight), GlobalRatelimiterUpdateInterval: dc.GetDurationProperty(dynamicproperties.GlobalRatelimiterUpdateInterval), GlobalRatelimiterDecayAfter: dc.GetDurationProperty(dynamicproperties.HistoryGlobalRatelimiterDecayAfter), GlobalRatelimiterGCAfter: dc.GetDurationProperty(dynamicproperties.HistoryGlobalRatelimiterGCAfter), HostName: hostname, } return cfg } // NewForTest create new history service config for test func NewForTest() *Config { return NewForTestByShardNumber(1) } // NewForTestByShardNumber create new history service config for test func NewForTestByShardNumber(shardNumber int) *Config { panicIfErr := func(err error) { if err != nil { panic(err) } } inMem := dynamicconfig.NewInMemoryClient() panicIfErr(inMem.UpdateValue(dynamicproperties.HistoryLongPollExpirationInterval, 10*time.Second)) panicIfErr(inMem.UpdateValue(dynamicproperties.EnableConsistentQueryByDomain, true)) panicIfErr(inMem.UpdateValue(dynamicproperties.ReplicationTaskProcessorHostQPS, float64(10000))) panicIfErr(inMem.UpdateValue(dynamicproperties.ReplicationTaskProcessorCleanupInterval, 20*time.Millisecond)) panicIfErr(inMem.UpdateValue(dynamicproperties.ReplicatorTaskDeleteBatchSize, 50)) panicIfErr(inMem.UpdateValue(dynamicproperties.ShardSyncMinInterval, 20*time.Millisecond)) panicIfErr(inMem.UpdateValue(dynamicproperties.ReplicationTaskProcessorShardQPS, float64(10000))) panicIfErr(inMem.UpdateValue(dynamicproperties.ReplicationTaskProcessorStartWait, time.Nanosecond)) panicIfErr(inMem.UpdateValue(dynamicproperties.EnableActivityLocalDispatchByDomain, true)) panicIfErr(inMem.UpdateValue(dynamicproperties.MaxActivityCountDispatchByDomain, 0)) panicIfErr(inMem.UpdateValue(dynamicproperties.EnableCrossClusterOperationsForDomain, true)) panicIfErr(inMem.UpdateValue(dynamicproperties.NormalDecisionScheduleToStartMaxAttempts, 3)) panicIfErr(inMem.UpdateValue(dynamicproperties.EnablePendingActivityValidation, true)) panicIfErr(inMem.UpdateValue(dynamicproperties.QueueProcessorEnableGracefulSyncShutdown, true)) panicIfErr(inMem.UpdateValue(dynamicproperties.QueueProcessorSplitMaxLevel, 2)) panicIfErr(inMem.UpdateValue(dynamicproperties.QueueProcessorPendingTaskSplitThreshold, map[string]interface{}{ "0": 1000, "1": 5000, })) panicIfErr(inMem.UpdateValue(dynamicproperties.QueueProcessorStuckTaskSplitThreshold, map[string]interface{}{ "0": 10, "1": 50, })) panicIfErr(inMem.UpdateValue(dynamicproperties.QueueProcessorRandomSplitProbability, 0.5)) panicIfErr(inMem.UpdateValue(dynamicproperties.EnableStrongIdempotency, true)) dc := dynamicconfig.NewCollection(inMem, log.NewNoop()) config := New(dc, shardNumber, 1024*1024, false, "") // reduce the duration of long poll to increase test speed config.LongPollExpirationInterval = dc.GetDurationPropertyFilteredByDomain(dynamicproperties.HistoryLongPollExpirationInterval) config.EnableConsistentQueryByDomain = dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableConsistentQueryByDomain) config.ReplicationTaskProcessorHostQPS = dc.GetFloat64Property(dynamicproperties.ReplicationTaskProcessorHostQPS) config.ReplicationTaskProcessorCleanupInterval = dc.GetDurationPropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorCleanupInterval) config.ReplicatorTaskDeleteBatchSize = dc.GetIntProperty(dynamicproperties.ReplicatorTaskDeleteBatchSize) config.ShardSyncMinInterval = dc.GetDurationProperty(dynamicproperties.ShardSyncMinInterval) config.ReplicationTaskProcessorShardQPS = dc.GetFloat64Property(dynamicproperties.ReplicationTaskProcessorShardQPS) config.ReplicationTaskProcessorStartWait = dc.GetDurationPropertyFilteredByShardID(dynamicproperties.ReplicationTaskProcessorStartWait) config.EnableActivityLocalDispatchByDomain = dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableActivityLocalDispatchByDomain) config.MaxActivityCountDispatchByDomain = dc.GetIntPropertyFilteredByDomain(dynamicproperties.MaxActivityCountDispatchByDomain) config.EnableCrossClusterOperationsForDomain = dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableCrossClusterOperationsForDomain) config.NormalDecisionScheduleToStartMaxAttempts = dc.GetIntPropertyFilteredByDomain(dynamicproperties.NormalDecisionScheduleToStartMaxAttempts) config.NormalDecisionScheduleToStartTimeout = dc.GetDurationPropertyFilteredByDomain(dynamicproperties.NormalDecisionScheduleToStartTimeout) config.PendingActivityValidationEnabled = dc.GetBoolProperty(dynamicproperties.EnablePendingActivityValidation) config.QueueProcessorEnableGracefulSyncShutdown = dc.GetBoolProperty(dynamicproperties.QueueProcessorEnableGracefulSyncShutdown) config.QueueProcessorSplitMaxLevel = dc.GetIntProperty(dynamicproperties.QueueProcessorSplitMaxLevel) config.QueueProcessorPendingTaskSplitThreshold = dc.GetMapProperty(dynamicproperties.QueueProcessorPendingTaskSplitThreshold) config.QueueProcessorStuckTaskSplitThreshold = dc.GetMapProperty(dynamicproperties.QueueProcessorStuckTaskSplitThreshold) config.QueueProcessorRandomSplitProbability = dc.GetFloat64Property(dynamicproperties.QueueProcessorRandomSplitProbability) return config } // GetShardID return the corresponding shard ID for a given workflow ID func (config *Config) GetShardID(workflowID string) int { return common.WorkflowIDToHistoryShard(workflowID, config.NumberOfShards) } ================================================ FILE: service/history/config/config_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package config import ( "reflect" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) type configTestCase struct { key dynamicproperties.Key value interface{} } func TestNewConfig(t *testing.T) { hostname := "hostname" numberOfShards := 8192 maxMessageSize := 1024 isAdvancedVisConfigExist := true fields := map[string]configTestCase{ "NumberOfShards": {nil, numberOfShards}, "IsAdvancedVisConfigExist": {nil, isAdvancedVisConfigExist}, "RPS": {dynamicproperties.HistoryRPS, 1}, "MaxIDLengthWarnLimit": {dynamicproperties.MaxIDLengthWarnLimit, 2}, "DomainNameMaxLength": {dynamicproperties.DomainNameMaxLength, 3}, "IdentityMaxLength": {dynamicproperties.IdentityMaxLength, 4}, "WorkflowIDMaxLength": {dynamicproperties.WorkflowIDMaxLength, 5}, "SignalNameMaxLength": {dynamicproperties.SignalNameMaxLength, 6}, "WorkflowTypeMaxLength": {dynamicproperties.WorkflowTypeMaxLength, 7}, "RequestIDMaxLength": {dynamicproperties.RequestIDMaxLength, 8}, "TaskListNameMaxLength": {dynamicproperties.TaskListNameMaxLength, 9}, "ActivityIDMaxLength": {dynamicproperties.ActivityIDMaxLength, 10}, "ActivityTypeMaxLength": {dynamicproperties.ActivityTypeMaxLength, 11}, "MarkerNameMaxLength": {dynamicproperties.MarkerNameMaxLength, 12}, "TimerIDMaxLength": {dynamicproperties.TimerIDMaxLength, 13}, "PersistenceMaxQPS": {dynamicproperties.HistoryPersistenceMaxQPS, 14}, "PersistenceGlobalMaxQPS": {dynamicproperties.HistoryPersistenceGlobalMaxQPS, 15}, "EnableVisibilitySampling": {dynamicproperties.EnableVisibilitySampling, true}, "EnableReadFromClosedExecutionV2": {dynamicproperties.EnableReadFromClosedExecutionV2, true}, "VisibilityOpenMaxQPS": {dynamicproperties.HistoryVisibilityOpenMaxQPS, 16}, "VisibilityClosedMaxQPS": {dynamicproperties.HistoryVisibilityClosedMaxQPS, 17}, "WriteVisibilityStoreName": {dynamicproperties.WriteVisibilityStoreName, "es"}, "EmitShardDiffLog": {dynamicproperties.EmitShardDiffLog, true}, "MaxAutoResetPoints": {dynamicproperties.HistoryMaxAutoResetPoints, 18}, "ThrottledLogRPS": {dynamicproperties.HistoryThrottledLogRPS, 19}, "EnableStickyQuery": {dynamicproperties.EnableStickyQuery, true}, "ShutdownDrainDuration": {dynamicproperties.HistoryShutdownDrainDuration, time.Second}, "WorkflowDeletionJitterRange": {dynamicproperties.WorkflowDeletionJitterRange, 20}, "DeleteHistoryEventContextTimeout": {dynamicproperties.DeleteHistoryEventContextTimeout, 21}, "MaxResponseSize": {nil, maxMessageSize}, "HistoryCacheInitialSize": {dynamicproperties.HistoryCacheInitialSize, 22}, "HistoryCacheMaxSize": {dynamicproperties.HistoryCacheMaxSize, 23}, "HistoryCacheTTL": {dynamicproperties.HistoryCacheTTL, time.Second}, "EnableSizeBasedHistoryExecutionCache": {dynamicproperties.EnableSizeBasedHistoryExecutionCache, true}, "EventsCacheInitialCount": {dynamicproperties.EventsCacheInitialCount, 24}, "EventsCacheMaxCount": {dynamicproperties.EventsCacheMaxCount, 25}, "EventsCacheMaxSize": {dynamicproperties.EventsCacheMaxSize, 26}, "EventsCacheTTL": {dynamicproperties.EventsCacheTTL, time.Second}, "EventsCacheGlobalEnable": {dynamicproperties.EventsCacheGlobalEnable, true}, "EventsCacheGlobalInitialCount": {dynamicproperties.EventsCacheGlobalInitialCount, 27}, "EventsCacheGlobalMaxCount": {dynamicproperties.EventsCacheGlobalMaxCount, 28}, "EnableSizeBasedHistoryEventCache": {dynamicproperties.EnableSizeBasedHistoryEventCache, true}, "RangeSizeBits": {nil, uint(20)}, "AcquireShardInterval": {dynamicproperties.AcquireShardInterval, time.Second}, "AcquireShardConcurrency": {dynamicproperties.AcquireShardConcurrency, 29}, "StandbyClusterDelay": {dynamicproperties.StandbyClusterDelay, time.Second}, "StandbyTaskMissingEventsResendDelay": {dynamicproperties.StandbyTaskMissingEventsResendDelay, time.Second}, "StandbyTaskMissingEventsDiscardDelay": {dynamicproperties.StandbyTaskMissingEventsDiscardDelay, time.Second}, "TaskProcessRPS": {dynamicproperties.TaskProcessRPS, 30}, "TaskSchedulerType": {dynamicproperties.TaskSchedulerType, 31}, "TaskSchedulerWorkerCount": {dynamicproperties.TaskSchedulerWorkerCount, 32}, "TaskSchedulerQueueSize": {dynamicproperties.TaskSchedulerQueueSize, 34}, "TaskSchedulerDispatcherCount": {dynamicproperties.TaskSchedulerDispatcherCount, 35}, "TaskSchedulerRoundRobinWeights": {dynamicproperties.TaskSchedulerRoundRobinWeights, map[string]interface{}{"key": 1}}, "TaskSchedulerDomainRoundRobinWeights": {dynamicproperties.TaskSchedulerDomainRoundRobinWeights, map[string]interface{}{"key": 2}}, "TaskCriticalRetryCount": {dynamicproperties.TaskCriticalRetryCount, 37}, "ActiveTaskRedispatchInterval": {dynamicproperties.ActiveTaskRedispatchInterval, time.Second}, "StandbyTaskRedispatchInterval": {dynamicproperties.StandbyTaskRedispatchInterval, time.Second}, "StandbyTaskReReplicationContextTimeout": {dynamicproperties.StandbyTaskReReplicationContextTimeout, time.Second}, "EnableDropStuckTaskByDomainID": {dynamicproperties.EnableDropStuckTaskByDomainID, true}, "ResurrectionCheckMinDelay": {dynamicproperties.ResurrectionCheckMinDelay, time.Second}, "QueueProcessorEnableSplit": {dynamicproperties.QueueProcessorEnableSplit, true}, "QueueProcessorSplitMaxLevel": {dynamicproperties.QueueProcessorSplitMaxLevel, 38}, "QueueProcessorEnableRandomSplitByDomainID": {dynamicproperties.QueueProcessorEnableRandomSplitByDomainID, true}, "QueueProcessorRandomSplitProbability": {dynamicproperties.QueueProcessorRandomSplitProbability, 1.0}, "QueueProcessorEnablePendingTaskSplitByDomainID": {dynamicproperties.QueueProcessorEnablePendingTaskSplitByDomainID, true}, "QueueProcessorPendingTaskSplitThreshold": {dynamicproperties.QueueProcessorPendingTaskSplitThreshold, map[string]interface{}{"a": 100}}, "QueueProcessorEnableStuckTaskSplitByDomainID": {dynamicproperties.QueueProcessorEnableStuckTaskSplitByDomainID, true}, "QueueProcessorStuckTaskSplitThreshold": {dynamicproperties.QueueProcessorStuckTaskSplitThreshold, map[string]interface{}{"b": 1}}, "QueueProcessorSplitLookAheadDurationByDomainID": {dynamicproperties.QueueProcessorSplitLookAheadDurationByDomainID, time.Second}, "QueueProcessorPollBackoffInterval": {dynamicproperties.QueueProcessorPollBackoffInterval, time.Second}, "QueueProcessorPollBackoffIntervalJitterCoefficient": {dynamicproperties.QueueProcessorPollBackoffIntervalJitterCoefficient, 1.0}, "QueueProcessorEnableLoadQueueStates": {dynamicproperties.QueueProcessorEnableLoadQueueStates, true}, "QueueProcessorEnableGracefulSyncShutdown": {dynamicproperties.QueueProcessorEnableGracefulSyncShutdown, true}, "QueueProcessorEnablePersistQueueStates": {dynamicproperties.QueueProcessorEnablePersistQueueStates, true}, "TimerTaskBatchSize": {dynamicproperties.TimerTaskBatchSize, 39}, "TimerTaskDeleteBatchSize": {dynamicproperties.TimerTaskDeleteBatchSize, 40}, "TimerProcessorGetFailureRetryCount": {dynamicproperties.TimerProcessorGetFailureRetryCount, 41}, "TimerProcessorCompleteTimerFailureRetryCount": {dynamicproperties.TimerProcessorCompleteTimerFailureRetryCount, 42}, "TimerProcessorUpdateAckInterval": {dynamicproperties.TimerProcessorUpdateAckInterval, time.Second}, "TimerProcessorUpdateAckIntervalJitterCoefficient": {dynamicproperties.TimerProcessorUpdateAckIntervalJitterCoefficient, 2.0}, "TimerProcessorCompleteTimerInterval": {dynamicproperties.TimerProcessorCompleteTimerInterval, time.Second}, "TimerProcessorFailoverMaxStartJitterInterval": {dynamicproperties.TimerProcessorFailoverMaxStartJitterInterval, time.Second}, "TimerProcessorFailoverMaxPollRPS": {dynamicproperties.TimerProcessorFailoverMaxPollRPS, 43}, "TimerProcessorMaxPollRPS": {dynamicproperties.TimerProcessorMaxPollRPS, 44}, "TimerProcessorMaxPollInterval": {dynamicproperties.TimerProcessorMaxPollInterval, time.Second}, "TimerProcessorMaxPollIntervalJitterCoefficient": {dynamicproperties.TimerProcessorMaxPollIntervalJitterCoefficient, 3.0}, "TimerProcessorSplitQueueInterval": {dynamicproperties.TimerProcessorSplitQueueInterval, time.Second}, "TimerProcessorSplitQueueIntervalJitterCoefficient": {dynamicproperties.TimerProcessorSplitQueueIntervalJitterCoefficient, 4.0}, "TimerProcessorMaxRedispatchQueueSize": {dynamicproperties.TimerProcessorMaxRedispatchQueueSize, 45}, "TimerProcessorMaxTimeShift": {dynamicproperties.TimerProcessorMaxTimeShift, time.Second}, "TimerProcessorHistoryArchivalSizeLimit": {dynamicproperties.TimerProcessorHistoryArchivalSizeLimit, 46}, "TimerProcessorArchivalTimeLimit": {dynamicproperties.TimerProcessorArchivalTimeLimit, time.Second}, "TransferTaskBatchSize": {dynamicproperties.TransferTaskBatchSize, 47}, "TransferTaskDeleteBatchSize": {dynamicproperties.TransferTaskDeleteBatchSize, 48}, "TransferProcessorCompleteTransferFailureRetryCount": {dynamicproperties.TransferProcessorCompleteTransferFailureRetryCount, 49}, "TransferProcessorFailoverMaxStartJitterInterval": {dynamicproperties.TransferProcessorFailoverMaxStartJitterInterval, time.Second}, "TransferProcessorFailoverMaxPollRPS": {dynamicproperties.TransferProcessorFailoverMaxPollRPS, 50}, "TransferProcessorMaxPollRPS": {dynamicproperties.TransferProcessorMaxPollRPS, 51}, "TransferProcessorMaxPollInterval": {dynamicproperties.TransferProcessorMaxPollInterval, time.Second}, "TransferProcessorMaxPollIntervalJitterCoefficient": {dynamicproperties.TransferProcessorMaxPollIntervalJitterCoefficient, 8.0}, "TransferProcessorSplitQueueInterval": {dynamicproperties.TransferProcessorSplitQueueInterval, time.Second}, "TransferProcessorSplitQueueIntervalJitterCoefficient": {dynamicproperties.TransferProcessorSplitQueueIntervalJitterCoefficient, 6.0}, "TransferProcessorUpdateAckInterval": {dynamicproperties.TransferProcessorUpdateAckInterval, time.Second}, "TransferProcessorUpdateAckIntervalJitterCoefficient": {dynamicproperties.TransferProcessorUpdateAckIntervalJitterCoefficient, 7.0}, "TransferProcessorCompleteTransferInterval": {dynamicproperties.TransferProcessorCompleteTransferInterval, time.Second}, "TransferProcessorMaxRedispatchQueueSize": {dynamicproperties.TransferProcessorMaxRedispatchQueueSize, 52}, "TransferProcessorEnableValidator": {dynamicproperties.TransferProcessorEnableValidator, true}, "TransferProcessorValidationInterval": {dynamicproperties.TransferProcessorValidationInterval, time.Second}, "TransferProcessorVisibilityArchivalTimeLimit": {dynamicproperties.TransferProcessorVisibilityArchivalTimeLimit, time.Second}, "ReplicatorTaskDeleteBatchSize": {dynamicproperties.ReplicatorTaskDeleteBatchSize, 53}, "ReplicatorReadTaskMaxRetryCount": {dynamicproperties.ReplicatorReadTaskMaxRetryCount, 54}, "ReplicatorProcessorFetchTasksBatchSize": {dynamicproperties.ReplicatorTaskBatchSize, 55}, "ReplicatorProcessorMinTaskBatchSize": {dynamicproperties.ReplicatorMinTaskBatchSize, 1}, "ReplicatorProcessorMaxTaskBatchSize": {dynamicproperties.ReplicatorMaxTaskBatchSize, 1000}, "ReplicatorProcessorBatchSizeStepCount": {dynamicproperties.ReplicatorTaskBatchStepCount, 10}, "ReplicatorUpperLatency": {dynamicproperties.ReplicatorUpperLatency, time.Second}, "ReplicatorCacheCapacity": {dynamicproperties.ReplicatorCacheCapacity, 56}, "ReplicatorCacheMaxSize": {dynamicproperties.ReplicatorCacheMaxSize, 2000}, "ReplicationBudgetManagerMaxSizeBytes": {dynamicproperties.ReplicationBudgetManagerMaxSizeBytes, 0}, "ReplicationBudgetManagerMaxSizeCount": {dynamicproperties.ReplicationBudgetManagerMaxSizeCount, 0}, "ReplicationBudgetManagerSoftCapThreshold": {dynamicproperties.ReplicationBudgetManagerSoftCapThreshold, 1.0}, "MaximumBufferedEventsBatch": {dynamicproperties.MaximumBufferedEventsBatch, 59}, "MaximumSignalsPerExecution": {dynamicproperties.MaximumSignalsPerExecution, 60}, "ShardUpdateMinInterval": {dynamicproperties.ShardUpdateMinInterval, time.Second}, "ShardSyncMinInterval": {dynamicproperties.ShardSyncMinInterval, time.Second}, "ShardSyncTimerJitterCoefficient": {dynamicproperties.TransferProcessorMaxPollIntervalJitterCoefficient, 8.0}, "LongPollExpirationInterval": {dynamicproperties.HistoryLongPollExpirationInterval, time.Second}, "EventEncodingType": {dynamicproperties.DefaultEventEncoding, "eventEncodingType"}, "EnableParentClosePolicy": {dynamicproperties.EnableParentClosePolicy, true}, "EnableParentClosePolicyWorker": {dynamicproperties.EnableParentClosePolicyWorker, true}, "ParentClosePolicyThreshold": {dynamicproperties.ParentClosePolicyThreshold, 61}, "ParentClosePolicyBatchSize": {dynamicproperties.ParentClosePolicyBatchSize, 62}, "NumParentClosePolicySystemWorkflows": {dynamicproperties.NumParentClosePolicySystemWorkflows, 63}, "NumArchiveSystemWorkflows": {dynamicproperties.NumArchiveSystemWorkflows, 64}, "ArchiveRequestRPS": {dynamicproperties.ArchiveRequestRPS, 65}, "ArchiveInlineHistoryRPS": {dynamicproperties.ArchiveInlineHistoryRPS, 66}, "ArchiveInlineHistoryGlobalRPS": {dynamicproperties.ArchiveInlineHistoryGlobalRPS, 67}, "ArchiveInlineVisibilityRPS": {dynamicproperties.ArchiveInlineVisibilityRPS, 68}, "ArchiveInlineVisibilityGlobalRPS": {dynamicproperties.ArchiveInlineVisibilityGlobalRPS, 69}, "AllowArchivingIncompleteHistory": {dynamicproperties.AllowArchivingIncompleteHistory, true}, "BlobSizeLimitError": {dynamicproperties.BlobSizeLimitError, 70}, "BlobSizeLimitWarn": {dynamicproperties.BlobSizeLimitWarn, 71}, "HistorySizeLimitError": {dynamicproperties.HistorySizeLimitError, 72}, "HistorySizeLimitWarn": {dynamicproperties.HistorySizeLimitWarn, 73}, "HistoryCountLimitError": {dynamicproperties.HistoryCountLimitError, 74}, "HistoryCountLimitWarn": {dynamicproperties.HistoryCountLimitWarn, 75}, "PendingActivitiesCountLimitError": {dynamicproperties.PendingActivitiesCountLimitError, 76}, "PendingActivitiesCountLimitWarn": {dynamicproperties.PendingActivitiesCountLimitWarn, 77}, "PendingActivityValidationEnabled": {dynamicproperties.EnablePendingActivityValidation, true}, "EnableQueryAttributeValidation": {dynamicproperties.EnableQueryAttributeValidation, true}, "ValidSearchAttributes": {dynamicproperties.ValidSearchAttributes, map[string]interface{}{"key": 1}}, "SearchAttributesNumberOfKeysLimit": {dynamicproperties.SearchAttributesNumberOfKeysLimit, 78}, "SearchAttributesSizeOfValueLimit": {dynamicproperties.SearchAttributesSizeOfValueLimit, 79}, "SearchAttributesTotalSizeLimit": {dynamicproperties.SearchAttributesTotalSizeLimit, 80}, "StickyTTL": {dynamicproperties.StickyTTL, time.Second}, "DecisionHeartbeatTimeout": {dynamicproperties.DecisionHeartbeatTimeout, time.Second}, "MaxDecisionStartToCloseSeconds": {dynamicproperties.MaxDecisionStartToCloseSeconds, 81}, "DecisionRetryCriticalAttempts": {dynamicproperties.DecisionRetryCriticalAttempts, 82}, "DecisionRetryMaxAttempts": {dynamicproperties.DecisionRetryMaxAttempts, 83}, "EnforceDecisionTaskAttempts": {dynamicproperties.EnforceDecisionTaskAttempts, true}, "NormalDecisionScheduleToStartMaxAttempts": {dynamicproperties.NormalDecisionScheduleToStartMaxAttempts, 84}, "NormalDecisionScheduleToStartTimeout": {dynamicproperties.NormalDecisionScheduleToStartTimeout, time.Second}, "ReplicationTaskFetcherParallelism": {dynamicproperties.ReplicationTaskFetcherParallelism, 85}, "ReplicationTaskFetcherAggregationInterval": {dynamicproperties.ReplicationTaskFetcherAggregationInterval, time.Second}, "ReplicationTaskFetcherTimerJitterCoefficient": {dynamicproperties.ReplicationTaskFetcherTimerJitterCoefficient, 9.0}, "ReplicationTaskFetcherErrorRetryWait": {dynamicproperties.ReplicationTaskFetcherErrorRetryWait, time.Second}, "ReplicationTaskFetcherServiceBusyWait": {dynamicproperties.ReplicationTaskFetcherServiceBusyWait, time.Second}, "ReplicationTaskProcessorErrorRetryWait": {dynamicproperties.ReplicationTaskProcessorErrorRetryWait, time.Second}, "ReplicationTaskProcessorErrorRetryMaxAttempts": {dynamicproperties.ReplicationTaskProcessorErrorRetryMaxAttempts, 86}, "ReplicationTaskProcessorErrorSecondRetryWait": {dynamicproperties.ReplicationTaskProcessorErrorSecondRetryWait, time.Second}, "ReplicationTaskProcessorErrorSecondRetryExpiration": {dynamicproperties.ReplicationTaskProcessorErrorSecondRetryExpiration, time.Second}, "ReplicationTaskProcessorErrorSecondRetryMaxWait": {dynamicproperties.ReplicationTaskProcessorErrorSecondRetryMaxWait, time.Second}, "ReplicationTaskProcessorNoTaskRetryWait": {dynamicproperties.ReplicationTaskProcessorNoTaskInitialWait, time.Second}, "ReplicationTaskProcessorCleanupInterval": {dynamicproperties.ReplicationTaskProcessorCleanupInterval, time.Second}, "ReplicationTaskProcessorCleanupJitterCoefficient": {dynamicproperties.ReplicationTaskProcessorCleanupJitterCoefficient, 10.0}, "ReplicationTaskProcessorStartWait": {dynamicproperties.ReplicationTaskProcessorStartWait, time.Second}, "ReplicationTaskProcessorStartWaitJitterCoefficient": {dynamicproperties.ReplicationTaskProcessorStartWaitJitterCoefficient, 11.0}, "ReplicationTaskProcessorHostQPS": {dynamicproperties.ReplicationTaskProcessorHostQPS, 12.0}, "ReplicationTaskProcessorShardQPS": {dynamicproperties.ReplicationTaskProcessorShardQPS, 13.0}, "ReplicationTaskGenerationQPS": {dynamicproperties.ReplicationTaskGenerationQPS, 14.0}, "EnableReplicationTaskGeneration": {dynamicproperties.EnableReplicationTaskGeneration, true}, "EnableRecordWorkflowExecutionUninitialized": {dynamicproperties.EnableRecordWorkflowExecutionUninitialized, true}, "WorkflowIDExternalRPS": {dynamicproperties.WorkflowIDExternalRPS, 87}, "WorkflowIDInternalRPS": {dynamicproperties.WorkflowIDInternalRPS, 88}, "EnableConsistentQuery": {dynamicproperties.EnableConsistentQuery, true}, "EnableConsistentQueryByDomain": {dynamicproperties.EnableConsistentQueryByDomain, true}, "MaxBufferedQueryCount": {dynamicproperties.MaxBufferedQueryCount, 89}, "EnableContextHeaderInVisibility": {dynamicproperties.EnableContextHeaderInVisibility, true}, "EnableCrossClusterOperationsForDomain": {dynamicproperties.EnableCrossClusterOperationsForDomain, true}, "MutableStateChecksumGenProbability": {dynamicproperties.MutableStateChecksumGenProbability, 90}, "MutableStateChecksumVerifyProbability": {dynamicproperties.MutableStateChecksumVerifyProbability, 91}, "MutableStateChecksumInvalidateBefore": {dynamicproperties.MutableStateChecksumInvalidateBefore, 15.0}, "EnableRetryForChecksumFailure": {dynamicproperties.EnableRetryForChecksumFailure, true}, "EnableHistoryCorruptionCheck": {dynamicproperties.EnableHistoryCorruptionCheck, true}, "NotifyFailoverMarkerInterval": {dynamicproperties.NotifyFailoverMarkerInterval, time.Second}, "NotifyFailoverMarkerTimerJitterCoefficient": {dynamicproperties.NotifyFailoverMarkerTimerJitterCoefficient, 16.0}, "EnableGracefulFailover": {dynamicproperties.EnableGracefulFailover, true}, "EnableActivityLocalDispatchByDomain": {dynamicproperties.EnableActivityLocalDispatchByDomain, true}, "MaxActivityCountDispatchByDomain": {dynamicproperties.MaxActivityCountDispatchByDomain, 92}, "ActivityMaxScheduleToStartTimeoutForRetry": {dynamicproperties.ActivityMaxScheduleToStartTimeoutForRetry, time.Second}, "EnableDebugMode": {dynamicproperties.EnableDebugMode, true}, "EnableTaskInfoLogByDomainID": {dynamicproperties.HistoryEnableTaskInfoLogByDomainID, true}, "EnableTimerDebugLogByDomainID": {dynamicproperties.EnableTimerDebugLogByDomainID, true}, "SampleLoggingRate": {dynamicproperties.SampleLoggingRate, 93}, "EnableShardIDMetrics": {dynamicproperties.EnableShardIDMetrics, true}, "LargeShardHistorySizeMetricThreshold": {dynamicproperties.LargeShardHistorySizeMetricThreshold, 94}, "LargeShardHistoryEventMetricThreshold": {dynamicproperties.LargeShardHistoryEventMetricThreshold, 95}, "LargeShardHistoryBlobMetricThreshold": {dynamicproperties.LargeShardHistoryBlobMetricThreshold, 96}, "EnableStrongIdempotency": {dynamicproperties.EnableStrongIdempotency, true}, "EnableStrongIdempotencySanityCheck": {dynamicproperties.EnableStrongIdempotencySanityCheck, true}, "GlobalRatelimiterNewDataWeight": {dynamicproperties.HistoryGlobalRatelimiterNewDataWeight, 17.0}, "GlobalRatelimiterUpdateInterval": {dynamicproperties.GlobalRatelimiterUpdateInterval, time.Second}, "GlobalRatelimiterDecayAfter": {dynamicproperties.HistoryGlobalRatelimiterDecayAfter, time.Second}, "GlobalRatelimiterGCAfter": {dynamicproperties.HistoryGlobalRatelimiterGCAfter, time.Second}, "TaskSchedulerGlobalDomainRPS": {dynamicproperties.TaskSchedulerGlobalDomainRPS, 97}, "TaskSchedulerEnableRateLimiterShadowMode": {dynamicproperties.TaskSchedulerEnableRateLimiterShadowMode, false}, "TaskSchedulerEnableRateLimiter": {dynamicproperties.TaskSchedulerEnableRateLimiter, true}, "HostName": {nil, hostname}, "SearchAttributesHiddenValueKeys": {dynamicproperties.SearchAttributesHiddenValueKeys, map[string]interface{}{"CustomStringField": true}}, "ExecutionCacheMaxByteSize": {dynamicproperties.ExecutionCacheMaxByteSize, 98}, "DisableTransferFailoverQueue": {dynamicproperties.DisableTransferFailoverQueue, true}, "DisableTimerFailoverQueue": {dynamicproperties.DisableTimerFailoverQueue, true}, "EnableTransferQueueV2": {dynamicproperties.EnableTransferQueueV2, true}, "EnableTimerQueueV2": {dynamicproperties.EnableTimerQueueV2, true}, "QueueMaxPendingTaskCount": {dynamicproperties.QueueMaxPendingTaskCount, 99}, "EnableTimerQueueV2PendingTaskCountAlert": {dynamicproperties.EnableTimerQueueV2PendingTaskCountAlert, true}, "EnableTransferQueueV2PendingTaskCountAlert": {dynamicproperties.EnableTransferQueueV2PendingTaskCountAlert, true}, "QueueCriticalPendingTaskCount": {dynamicproperties.QueueCriticalPendingTaskCount, 100}, "QueueMaxVirtualQueueCount": {dynamicproperties.QueueMaxVirtualQueueCount, 101}, "VirtualSliceForceAppendInterval": {dynamicproperties.VirtualSliceForceAppendInterval, time.Second}, "ReplicationTaskProcessorLatencyLogThreshold": {dynamicproperties.ReplicationTaskProcessorLatencyLogThreshold, time.Duration(0)}, "EnableCleanupOrphanedHistoryBranchOnWorkflowCreation": {dynamicproperties.EnableCleanupOrphanedHistoryBranchOnWorkflowCreation, true}, "EnableHierarchicalWeightedRoundRobinTaskScheduler": {dynamicproperties.EnableHierarchicalWeightedRoundRobinTaskScheduler, true}, "EnableTaskListAwareTaskSchedulerByDomain": {dynamicproperties.EnableTaskListAwareTaskSchedulerByDomain, true}, } client := dynamicconfig.NewInMemoryClient() for fieldName, expected := range fields { if expected.key != nil { err := client.UpdateValue(expected.key, expected.value) if err != nil { t.Errorf("Failed to update config for %s: %s", fieldName, err) } } } dc := dynamicconfig.NewCollection(client, testlogger.New(t)) config := New(dc, numberOfShards, maxMessageSize, isAdvancedVisConfigExist, hostname) assertFieldsMatch(t, *config, fields) } func TestNewForTest(t *testing.T) { cfg := NewForTest() assert.NotNil(t, cfg) } func assertFieldsMatch(t *testing.T, config interface{}, fields map[string]configTestCase) { configType := reflect.ValueOf(config) for i := 0; i < configType.NumField(); i++ { f := configType.Field(i) fieldName := configType.Type().Field(i).Name if expected, ok := fields[fieldName]; ok { actual := getValue(&f) if f.Kind() == reflect.Slice { assert.ElementsMatch(t, expected.value, actual, "Incorrect value for field: %s", fieldName) } else { assert.Equal(t, expected.value, actual, "Incorrect value for field: %s", fieldName) } } else { t.Errorf("Unknown property on Config: %s", fieldName) } } } func getValue(f *reflect.Value) interface{} { switch f.Kind() { case reflect.Func: switch fn := f.Interface().(type) { case dynamicproperties.IntPropertyFn: return fn() case dynamicproperties.IntPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.IntPropertyFnWithTaskListInfoFilters: return fn("domain", "tasklist", int(types.TaskListTypeDecision)) case dynamicproperties.BoolPropertyFn: return fn() case dynamicproperties.BoolPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.BoolPropertyFnWithDomainIDFilter: return fn("domain") case dynamicproperties.BoolPropertyFnWithTaskListInfoFilters: return fn("domain", "tasklist", int(types.TaskListTypeDecision)) case dynamicproperties.DurationPropertyFn: return fn() case dynamicproperties.DurationPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.DurationPropertyFnWithTaskListInfoFilters: return fn("domain", "tasklist", int(types.TaskListTypeDecision)) case dynamicproperties.FloatPropertyFn: return fn() case dynamicproperties.MapPropertyFn: return fn() case dynamicproperties.StringPropertyFn: return fn() case dynamicproperties.DurationPropertyFnWithDomainIDFilter: return fn("domain") case dynamicproperties.IntPropertyFnWithShardIDFilter: return fn(0) case dynamicproperties.StringPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.DurationPropertyFnWithShardIDFilter: return fn(0) case dynamicproperties.FloatPropertyFnWithShardIDFilter: return fn(0) case dynamicproperties.BoolPropertyFnWithDomainIDAndWorkflowIDFilter: return fn("domain", "workflowID") case dynamicproperties.MapPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.BoolPropertyFnWithShardIDFilter: return fn(0) case func() []string: return fn() default: panic("Unable to handle type: " + f.Type().Name()) } default: return f.Interface() } } func isolationGroupsHelper() []string { return []string{"zone-1", "zone-2"} } ================================================ FILE: service/history/constants/constants.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package constants import "github.com/uber/cadence/common/types" var ( ErrDomainNotSet = &types.BadRequestError{Message: "Domain not set on request."} ErrWorkflowExecutionNotSet = &types.BadRequestError{Message: "WorkflowExecution not set on request."} ErrTaskListNotSet = &types.BadRequestError{Message: "Tasklist not set."} ErrRunIDNotValid = &types.BadRequestError{Message: "RunID is not valid UUID."} ErrWorkflowIDNotSet = &types.BadRequestError{Message: "WorkflowId is not set on request."} ErrSourceClusterNotSet = &types.BadRequestError{Message: "Source Cluster not set on request."} ErrTimestampNotSet = &types.BadRequestError{Message: "Timestamp not set on request."} ErrInvalidTaskType = &types.BadRequestError{Message: "Invalid task type"} ErrHistoryHostThrottle = &types.ServiceBusyError{Message: "History host rps exceeded"} ErrShuttingDown = &types.InternalServiceError{Message: "Shutting down"} ) ================================================ FILE: service/history/constants/test_constants.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package constants import ( "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) var ( // TestVersion is the workflow version for test TestVersion = cluster.TestCurrentClusterInitialFailoverVersion + (cluster.TestFailoverVersionIncrement * 5) // TestDomainID is the domainID for test TestDomainID = "deadbeef-0123-4567-890a-bcdef0123456" // TestActiveActiveDomainID is the active active domainID for test TestActiveActiveDomainID = "965c78b7-1f6f-4122-9ba7-af6b7a55b27f" // TestActiveActiveDomainName is the active active domain name for test TestActiveActiveDomainName = "active-active-domain" // TestDomainName is the domainName for test TestDomainName = "some random domain name" // TestRateLimitedDomainName is the domain name for testing task processing rate limits TestRateLimitedDomainName = "rate-limited-domain-0123-4567-890a-bcdef0123456" // TestRateLimitedDomainID is the domain ID for testing task processing rate limits TestRateLimitedDomainID = "rate-limited-domain" // TestUnknownDomainID is the domain ID for testing unknown domains TestUnknownDomainID = "unknown-domain-id" // TestWorkflowID is the workflowID for test TestWorkflowID = "random-workflow-id" // TestRunID is the workflow runID for test TestRunID = "0d00698f-08e1-4d36-a3e2-3bf109f5d2d6" // TestRequestID is the request ID for test TestRequestID = "143b22cd-dfac-4d59-9398-893f89d89df6" // CronSkip CronSkip = types.CronOverlapPolicySkipped // TestClusterMetadata is the cluster metadata for test TestClusterMetadata = cluster.GetTestClusterMetadata(true) // TestActiveClusterSelectionPolicy is the active cluster selection policy for test TestActiveClusterSelectionPolicy = types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, } // TestLocalDomainEntry is the local domain cache entry for test TestLocalDomainEntry = cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{ID: TestDomainID, Name: TestDomainName}, &persistence.DomainConfig{Retention: 1}, cluster.TestCurrentClusterName, ) // TestGlobalDomainEntry is the global domain cache entry for test TestGlobalDomainEntry = cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: TestDomainID, Name: TestDomainName}, &persistence.DomainConfig{ Retention: 1, VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "test:///visibility/archival", }, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, TestVersion, ) TestActiveActiveDomainEntry = cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: TestActiveActiveDomainID, Name: TestActiveActiveDomainName}, &persistence.DomainConfig{ Retention: 1, VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "test:///visibility/archival", }, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-east": { ActiveClusterName: cluster.TestCurrentClusterName, }, "us-west": { ActiveClusterName: cluster.TestAlternativeClusterName, }, }, }, }, }, }, TestVersion, ) // TestRateLimitedDomainEntry is the global domain cache entry for test TestRateLimitedDomainEntry = cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: TestRateLimitedDomainID, Name: TestRateLimitedDomainName}, &persistence.DomainConfig{ Retention: 1, VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "test:///visibility/archival", }, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, TestVersion, ) // TestGlobalParentDomainEntry is the global parent domain cache entry for test TestGlobalParentDomainEntry = cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: TestDomainID, Name: TestDomainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, TestVersion, ) // TestGlobalTargetDomainEntry is the global target domain cache entry for test TestGlobalTargetDomainEntry = cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: TestDomainID, Name: TestDomainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, TestVersion, ) // TestGlobalChildDomainEntry is the global child domain cache entry for test TestGlobalChildDomainEntry = cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: TestDomainID, Name: TestDomainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, TestVersion, ) // TestGlobalStandbyDomainEntry is the global standby domain cache entry for test TestGlobalStandbyDomainEntry = cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: TestDomainID, Name: TestDomainName}, &persistence.DomainConfig{ Retention: 1, VisibilityArchivalStatus: types.ArchivalStatusEnabled, VisibilityArchivalURI: "test:///visibility/archival", }, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, TestVersion, ) ) ================================================ FILE: service/history/decision/checker.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package decision import ( "fmt" "strings" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/elasticsearch/validator" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" ) type ( attrValidator struct { config *config.Config domainCache cache.DomainCache metricsClient metrics.Client logger log.Logger searchAttributesValidator *validator.SearchAttributesValidator } workflowSizeChecker struct { domainName string blobSizeLimitWarn int blobSizeLimitError int historySizeLimitWarn int historySizeLimitError int historyCountLimitWarn int historyCountLimitError int completedID int64 mutableState execution.MutableState executionStats *persistence.ExecutionStats metricsScope metrics.Scope logger log.Logger } ) func newAttrValidator( domainCache cache.DomainCache, metricsClient metrics.Client, config *config.Config, logger log.Logger, ) *attrValidator { return &attrValidator{ config: config, domainCache: domainCache, metricsClient: metricsClient, logger: logger, searchAttributesValidator: validator.NewSearchAttributesValidator( logger, config.EnableQueryAttributeValidation, config.ValidSearchAttributes, config.SearchAttributesNumberOfKeysLimit, config.SearchAttributesSizeOfValueLimit, config.SearchAttributesTotalSizeLimit, ), } } func newWorkflowSizeChecker( domainName string, blobSizeLimitWarn int, blobSizeLimitError int, historySizeLimitWarn int, historySizeLimitError int, historyCountLimitWarn int, historyCountLimitError int, completedID int64, mutableState execution.MutableState, executionStats *persistence.ExecutionStats, metricsScope metrics.Scope, logger log.Logger, ) *workflowSizeChecker { return &workflowSizeChecker{ domainName: domainName, blobSizeLimitWarn: blobSizeLimitWarn, blobSizeLimitError: blobSizeLimitError, historySizeLimitWarn: historySizeLimitWarn, historySizeLimitError: historySizeLimitError, historyCountLimitWarn: historyCountLimitWarn, historyCountLimitError: historyCountLimitError, completedID: completedID, mutableState: mutableState, executionStats: executionStats, metricsScope: metricsScope, logger: logger, } } func (c *workflowSizeChecker) failWorkflowIfBlobSizeExceedsLimit( decisionTypeTag metrics.Tag, blob []byte, message string, ) (bool, error) { executionInfo := c.mutableState.GetExecutionInfo() err := common.CheckEventBlobSizeLimit( len(blob), c.blobSizeLimitWarn, c.blobSizeLimitError, executionInfo.DomainID, c.domainName, executionInfo.WorkflowID, executionInfo.RunID, c.metricsScope.Tagged(decisionTypeTag), c.logger, tag.BlobSizeViolationOperation(decisionTypeTag.Value()), ) if err == nil { return false, nil } attributes := &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonDecisionBlobSizeExceedsLimit), Details: []byte(message), } if _, err := c.mutableState.AddFailWorkflowEvent(c.completedID, attributes); err != nil { return false, err } return true, nil } func (c *workflowSizeChecker) failWorkflowSizeExceedsLimit() (bool, error) { historyCount := int(c.mutableState.GetNextEventID()) - 1 historySize := int(c.executionStats.HistorySize) // metricsScope already has domainName and operation: "RespondDecisionTaskCompleted" c.metricsScope.RecordTimer(metrics.HistorySize, time.Duration(historySize)) c.metricsScope.RecordTimer(metrics.HistoryCount, time.Duration(historyCount)) if historySize > c.historySizeLimitError || historyCount > c.historyCountLimitError { executionInfo := c.mutableState.GetExecutionInfo() c.logger.Error("history size exceeds error limit.", tag.WorkflowDomainName(c.domainName), tag.WorkflowDomainID(executionInfo.DomainID), tag.WorkflowID(executionInfo.WorkflowID), tag.WorkflowRunID(executionInfo.RunID), tag.WorkflowHistorySize(historySize), tag.WorkflowEventCount(historyCount)) attributes := &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonSizeExceedsLimit), Details: []byte("Workflow history size / count exceeds limit."), } if _, err := c.mutableState.AddFailWorkflowEvent(c.completedID, attributes); err != nil { return false, err } return true, nil } if historySize > c.historySizeLimitWarn || historyCount > c.historyCountLimitWarn { executionInfo := c.mutableState.GetExecutionInfo() c.logger.Warn("history size exceeds warn limit.", tag.WorkflowDomainName(c.domainName), tag.WorkflowDomainID(executionInfo.DomainID), tag.WorkflowID(executionInfo.WorkflowID), tag.WorkflowRunID(executionInfo.RunID), tag.WorkflowHistorySize(historySize), tag.WorkflowEventCount(historyCount)) return false, nil } return false, nil } func (v *attrValidator) validateActivityScheduleAttributes( domainID string, targetDomainID string, attributes *types.ScheduleActivityTaskDecisionAttributes, executionInfo *persistence.WorkflowExecutionInfo, metricsScope metrics.ScopeIdx, ) error { if err := v.validateCrossDomainCall( domainID, targetDomainID, ); err != nil { return err } if attributes == nil { return &types.BadRequestError{Message: "ScheduleActivityTaskDecisionAttributes is not set on decision."} } taskList, err := v.validatedTaskList(attributes.TaskList, &types.TaskList{Name: executionInfo.TaskList, Kind: executionInfo.TaskListKind.Ptr()}, metricsScope, attributes.GetDomain()) if err != nil { return err } attributes.TaskList = taskList if attributes.GetActivityID() == "" { return &types.BadRequestError{Message: "ActivityId is not set on decision."} } if attributes.ActivityType == nil || attributes.ActivityType.GetName() == "" { return &types.BadRequestError{Message: "ActivityType is not set on decision."} } if err := common.ValidateRetryPolicy(attributes.RetryPolicy); err != nil { return err } idLengthWarnLimit := v.config.MaxIDLengthWarnLimit() if !common.IsValidIDLength( attributes.GetActivityID(), v.metricsClient.Scope(metricsScope), idLengthWarnLimit, v.config.ActivityIDMaxLength(attributes.GetDomain()), metrics.CadenceErrActivityIDExceededWarnLimit, attributes.GetDomain(), v.logger, tag.IDTypeActivityID) { return &types.BadRequestError{Message: "ActivityID exceeds length limit."} } if !common.IsValidIDLength( attributes.GetActivityType().GetName(), v.metricsClient.Scope(metricsScope), idLengthWarnLimit, v.config.ActivityTypeMaxLength(attributes.GetDomain()), metrics.CadenceErrActivityTypeExceededWarnLimit, attributes.GetDomain(), v.logger, tag.IDTypeActivityType) { return &types.BadRequestError{Message: "ActivityType exceeds length limit."} } if !common.IsValidIDLength( attributes.GetDomain(), v.metricsClient.Scope(metricsScope), idLengthWarnLimit, v.config.DomainNameMaxLength(attributes.GetDomain()), metrics.CadenceErrDomainNameExceededWarnLimit, attributes.GetDomain(), v.logger, tag.IDTypeDomainName) { return &types.BadRequestError{Message: "Domain exceeds length limit."} } // Only attempt to deduce and fill in unspecified timeouts only when all timeouts are non-negative. if attributes.GetScheduleToCloseTimeoutSeconds() < 0 || attributes.GetScheduleToStartTimeoutSeconds() < 0 || attributes.GetStartToCloseTimeoutSeconds() < 0 || attributes.GetHeartbeatTimeoutSeconds() < 0 { return &types.BadRequestError{Message: "A valid timeout may not be negative."} } wfTimeout := executionInfo.WorkflowTimeout // ensure activity timeout never larger than workflow timeout if attributes.GetScheduleToCloseTimeoutSeconds() > wfTimeout { attributes.ScheduleToCloseTimeoutSeconds = common.Int32Ptr(wfTimeout) } if attributes.GetScheduleToStartTimeoutSeconds() > wfTimeout { attributes.ScheduleToStartTimeoutSeconds = common.Int32Ptr(wfTimeout) } if attributes.GetStartToCloseTimeoutSeconds() > wfTimeout { attributes.StartToCloseTimeoutSeconds = common.Int32Ptr(wfTimeout) } if attributes.GetHeartbeatTimeoutSeconds() > wfTimeout { attributes.HeartbeatTimeoutSeconds = common.Int32Ptr(wfTimeout) } validScheduleToClose := attributes.GetScheduleToCloseTimeoutSeconds() > 0 validScheduleToStart := attributes.GetScheduleToStartTimeoutSeconds() > 0 validStartToClose := attributes.GetStartToCloseTimeoutSeconds() > 0 if validScheduleToClose { if !validScheduleToStart { attributes.ScheduleToStartTimeoutSeconds = common.Int32Ptr(attributes.GetScheduleToCloseTimeoutSeconds()) } if !validStartToClose { attributes.StartToCloseTimeoutSeconds = common.Int32Ptr(attributes.GetScheduleToCloseTimeoutSeconds()) } } else if validScheduleToStart && validStartToClose { attributes.ScheduleToCloseTimeoutSeconds = common.Int32Ptr(attributes.GetScheduleToStartTimeoutSeconds() + attributes.GetStartToCloseTimeoutSeconds()) if attributes.GetScheduleToCloseTimeoutSeconds() > wfTimeout { attributes.ScheduleToCloseTimeoutSeconds = common.Int32Ptr(wfTimeout) } } else { // Deduction failed as there's not enough information to fill in missing timeouts. return &types.BadRequestError{Message: "A valid ScheduleToCloseTimeout is not set on decision."} } // ensure activity's SCHEDULE_TO_START and SCHEDULE_TO_CLOSE is as long as expiration on retry policy // if SCHEDULE_TO_START timeout is retryable p := attributes.RetryPolicy if p != nil { isScheduleToStartRetryable := true scheduleToStartErrorReason := execution.TimerTypeToReason(execution.TimerTypeScheduleToStart) for _, reason := range p.GetNonRetriableErrorReasons() { if reason == scheduleToStartErrorReason { isScheduleToStartRetryable = false break } } expiration := p.GetExpirationIntervalInSeconds() if expiration == 0 || expiration > wfTimeout { expiration = wfTimeout } if isScheduleToStartRetryable { // If schedule to start timeout is retryable, we don't need to fail the activity and schedule // it again on the same tasklist, as it's a no-op). Extending schedule to start timeout to achieve // the same thing. // // Theoretically, we can extend schedule to start to be as long as the expiration time, // but if user specifies a very long expiration time and activity task got lost after the activity is // scheduled, workflow will be stuck for a long time. So here, we cap the schedule to start timeout // to a maximum value, so that when the activity task got lost, timeout can happen sooner and schedule // the activity again. domainName, _ := v.domainCache.GetDomainName(domainID) // if this call returns an error, we will just used the default value for max timeout maximumScheduleToStartTimeoutForRetryInSeconds := int32(v.config.ActivityMaxScheduleToStartTimeoutForRetry(domainName).Seconds()) scheduleToStartExpiration := min(expiration, maximumScheduleToStartTimeoutForRetryInSeconds) if attributes.GetScheduleToStartTimeoutSeconds() < scheduleToStartExpiration { attributes.ScheduleToStartTimeoutSeconds = common.Int32Ptr(scheduleToStartExpiration) } // TODO: uncomment the following code when the client side bug for calculating scheduleToClose deadline is fixed and // fully rolled out. Before that, we still need to extend scheduleToClose timeout to be as long as the expiration interval // // scheduleToCloseExpiration := common.MinInt32(expiration, scheduleToStartExpiration+attributes.GetStartToCloseTimeoutSeconds()) // if attributes.GetScheduleToCloseTimeoutSeconds() < scheduleToCloseExpiration { // attributes.ScheduleToCloseTimeoutSeconds = common.Int32Ptr(scheduleToCloseExpiration) // } } if attributes.GetScheduleToCloseTimeoutSeconds() < expiration { attributes.ScheduleToCloseTimeoutSeconds = common.Int32Ptr(expiration) } } return nil } func (v *attrValidator) validateTimerScheduleAttributes( attributes *types.StartTimerDecisionAttributes, metricsScope metrics.ScopeIdx, domain string, ) error { if attributes == nil { return &types.BadRequestError{Message: "StartTimerDecisionAttributes is not set on decision."} } if attributes.GetTimerID() == "" { return &types.BadRequestError{Message: "TimerId is not set on decision."} } if !common.IsValidIDLength( attributes.GetTimerID(), v.metricsClient.Scope(metricsScope), v.config.MaxIDLengthWarnLimit(), v.config.TimerIDMaxLength(domain), metrics.CadenceErrTimerIDExceededWarnLimit, domain, v.logger, tag.IDTypeTimerID) { return &types.BadRequestError{Message: "TimerId exceeds length limit."} } if attributes.GetStartToFireTimeoutSeconds() <= 0 { return &types.BadRequestError{ Message: fmt.Sprintf("Invalid StartToFireTimeoutSeconds: %v", attributes.GetStartToFireTimeoutSeconds()), } } return nil } func (v *attrValidator) validateActivityCancelAttributes( attributes *types.RequestCancelActivityTaskDecisionAttributes, metricsScope metrics.ScopeIdx, domain string, ) error { if attributes == nil { return &types.BadRequestError{Message: "RequestCancelActivityTaskDecisionAttributes is not set on decision."} } if attributes.GetActivityID() == "" { return &types.BadRequestError{Message: "ActivityId is not set on decision."} } if !common.IsValidIDLength( attributes.GetActivityID(), v.metricsClient.Scope(metricsScope), v.config.MaxIDLengthWarnLimit(), v.config.ActivityIDMaxLength(domain), metrics.CadenceErrActivityIDExceededWarnLimit, domain, v.logger, tag.IDTypeActivityID) { return &types.BadRequestError{Message: "ActivityId exceeds length limit."} } return nil } func (v *attrValidator) validateTimerCancelAttributes( attributes *types.CancelTimerDecisionAttributes, metricsScope metrics.ScopeIdx, domain string, ) error { if attributes == nil { return &types.BadRequestError{Message: "CancelTimerDecisionAttributes is not set on decision."} } if attributes.GetTimerID() == "" { return &types.BadRequestError{Message: "TimerId is not set on decision."} } if !common.IsValidIDLength( attributes.GetTimerID(), v.metricsClient.Scope(metricsScope), v.config.MaxIDLengthWarnLimit(), v.config.TimerIDMaxLength(domain), metrics.CadenceErrTimerIDExceededWarnLimit, domain, v.logger, tag.IDTypeTimerID) { return &types.BadRequestError{Message: "TimerId exceeds length limit."} } return nil } func (v *attrValidator) validateRecordMarkerAttributes( attributes *types.RecordMarkerDecisionAttributes, metricsScope metrics.ScopeIdx, domain string, ) error { if attributes == nil { return &types.BadRequestError{Message: "RecordMarkerDecisionAttributes is not set on decision."} } if attributes.GetMarkerName() == "" { return &types.BadRequestError{Message: "MarkerName is not set on decision."} } if !common.IsValidIDLength( attributes.GetMarkerName(), v.metricsClient.Scope(metricsScope), v.config.MaxIDLengthWarnLimit(), v.config.MarkerNameMaxLength(domain), metrics.CadenceErrMarkerNameExceededWarnLimit, domain, v.logger, tag.IDTypeMarkerName) { return &types.BadRequestError{Message: "MarkerName exceeds length limit."} } return nil } func (v *attrValidator) validateCompleteWorkflowExecutionAttributes( attributes *types.CompleteWorkflowExecutionDecisionAttributes, ) error { if attributes == nil { return &types.BadRequestError{Message: "CompleteWorkflowExecutionDecisionAttributes is not set on decision."} } return nil } func (v *attrValidator) validateFailWorkflowExecutionAttributes( attributes *types.FailWorkflowExecutionDecisionAttributes, ) error { if attributes == nil { return &types.BadRequestError{Message: "FailWorkflowExecutionDecisionAttributes is not set on decision."} } if attributes.Reason == nil { return &types.BadRequestError{Message: "Reason is not set on decision."} } return nil } func (v *attrValidator) validateCancelWorkflowExecutionAttributes( attributes *types.CancelWorkflowExecutionDecisionAttributes, ) error { if attributes == nil { return &types.BadRequestError{Message: "CancelWorkflowExecutionDecisionAttributes is not set on decision."} } return nil } func (v *attrValidator) validateCancelExternalWorkflowExecutionAttributes( domainID string, targetDomainID string, attributes *types.RequestCancelExternalWorkflowExecutionDecisionAttributes, metricsScope metrics.ScopeIdx, ) error { if err := v.validateCrossDomainCall( domainID, targetDomainID, ); err != nil { return err } if attributes == nil { return &types.BadRequestError{Message: "RequestCancelExternalWorkflowExecutionDecisionAttributes is not set on decision."} } if attributes.WorkflowID == "" { return &types.BadRequestError{Message: "WorkflowId is not set on decision."} } idLengthWarnLimit := v.config.MaxIDLengthWarnLimit() if !common.IsValidIDLength( attributes.GetDomain(), v.metricsClient.Scope(metricsScope), idLengthWarnLimit, v.config.DomainNameMaxLength(attributes.GetDomain()), metrics.CadenceErrDomainNameExceededWarnLimit, attributes.GetDomain(), v.logger, tag.IDTypeDomainName) { return &types.BadRequestError{Message: "Domain exceeds length limit."} } if !common.IsValidIDLength( attributes.GetWorkflowID(), v.metricsClient.Scope(metricsScope), idLengthWarnLimit, v.config.WorkflowIDMaxLength(attributes.GetDomain()), metrics.CadenceErrWorkflowIDExceededWarnLimit, attributes.GetDomain(), v.logger, tag.IDTypeWorkflowID) { return &types.BadRequestError{Message: "WorkflowId exceeds length limit."} } runID := attributes.GetRunID() if runID != "" && uuid.Parse(runID) == nil { return &types.BadRequestError{Message: "Invalid RunId set on decision."} } return nil } func (v *attrValidator) validateSignalExternalWorkflowExecutionAttributes( domainID string, targetDomainID string, attributes *types.SignalExternalWorkflowExecutionDecisionAttributes, metricsScope metrics.ScopeIdx, ) error { if err := v.validateCrossDomainCall( domainID, targetDomainID, ); err != nil { return err } if attributes == nil { return &types.BadRequestError{Message: "SignalExternalWorkflowExecutionDecisionAttributes is not set on decision."} } if attributes.Execution == nil { return &types.BadRequestError{Message: "Execution is nil on decision."} } if attributes.Execution.WorkflowID == "" { return &types.BadRequestError{Message: "WorkflowId is not set on decision."} } idLengthWarnLimit := v.config.MaxIDLengthWarnLimit() if !common.IsValidIDLength( attributes.GetDomain(), v.metricsClient.Scope(metricsScope), idLengthWarnLimit, v.config.DomainNameMaxLength(attributes.GetDomain()), metrics.CadenceErrDomainNameExceededWarnLimit, attributes.GetDomain(), v.logger, tag.IDTypeDomainName) { return &types.BadRequestError{Message: "Domain exceeds length limit."} } if !common.IsValidIDLength( attributes.Execution.GetWorkflowID(), v.metricsClient.Scope(metricsScope), idLengthWarnLimit, v.config.WorkflowIDMaxLength(attributes.GetDomain()), metrics.CadenceErrWorkflowIDExceededWarnLimit, attributes.GetDomain(), v.logger, tag.IDTypeWorkflowID) { return &types.BadRequestError{Message: "WorkflowId exceeds length limit."} } targetRunID := attributes.Execution.GetRunID() if targetRunID != "" && uuid.Parse(targetRunID) == nil { return &types.BadRequestError{Message: "Invalid RunId set on decision."} } if attributes.SignalName == "" { return &types.BadRequestError{Message: "SignalName is not set on decision."} } return nil } func (v *attrValidator) validateUpsertWorkflowSearchAttributes( domainName string, attributes *types.UpsertWorkflowSearchAttributesDecisionAttributes, ) error { if attributes == nil { return &types.BadRequestError{Message: "UpsertWorkflowSearchAttributesDecisionAttributes is not set on decision."} } if attributes.SearchAttributes == nil { return &types.BadRequestError{Message: "SearchAttributes is not set on decision."} } if len(attributes.GetSearchAttributes().GetIndexedFields()) == 0 { return &types.BadRequestError{Message: "IndexedFields is empty on decision."} } return v.searchAttributesValidator.ValidateSearchAttributes(attributes.GetSearchAttributes(), domainName) } func (v *attrValidator) validateContinueAsNewWorkflowExecutionAttributes( attributes *types.ContinueAsNewWorkflowExecutionDecisionAttributes, executionInfo *persistence.WorkflowExecutionInfo, metricsScope metrics.ScopeIdx, domain string, ) error { if attributes == nil { return &types.BadRequestError{Message: "ContinueAsNewWorkflowExecutionDecisionAttributes is not set on decision."} } // Inherit workflow type from previous execution if not provided on decision if attributes.WorkflowType == nil || attributes.WorkflowType.GetName() == "" { attributes.WorkflowType = &types.WorkflowType{Name: executionInfo.WorkflowTypeName} } if !common.IsValidIDLength( attributes.WorkflowType.GetName(), v.metricsClient.Scope(metricsScope), v.config.MaxIDLengthWarnLimit(), v.config.WorkflowTypeMaxLength(domain), metrics.CadenceErrWorkflowTypeExceededWarnLimit, domain, v.logger, tag.IDTypeWorkflowType) { return &types.BadRequestError{Message: "WorkflowType exceeds length limit."} } // Inherit Tasklist from previous execution if not provided on decision taskList, err := v.validatedTaskList(attributes.TaskList, &types.TaskList{Name: executionInfo.TaskList, Kind: executionInfo.TaskListKind.Ptr()}, metricsScope, domain) if err != nil { return err } attributes.TaskList = taskList // Inherit workflow timeout from previous execution if not provided on decision if attributes.GetExecutionStartToCloseTimeoutSeconds() <= 0 { attributes.ExecutionStartToCloseTimeoutSeconds = common.Int32Ptr(executionInfo.WorkflowTimeout) } // Inherit decision task timeout from previous execution if not provided on decision if attributes.GetTaskStartToCloseTimeoutSeconds() <= 0 { attributes.TaskStartToCloseTimeoutSeconds = common.Int32Ptr(executionInfo.DecisionStartToCloseTimeout) } // Check next run decision task delay if attributes.GetBackoffStartIntervalInSeconds() < 0 { return &types.BadRequestError{Message: "BackoffStartInterval is less than 0."} } domainName, err := v.domainCache.GetDomainName(executionInfo.DomainID) if err != nil { return err } return v.searchAttributesValidator.ValidateSearchAttributes(attributes.GetSearchAttributes(), domainName) } func (v *attrValidator) validateStartChildExecutionAttributes( domainID string, targetDomainID string, attributes *types.StartChildWorkflowExecutionDecisionAttributes, parentInfo *persistence.WorkflowExecutionInfo, metricsScope metrics.ScopeIdx, ) error { if err := v.validateCrossDomainCall( domainID, targetDomainID, ); err != nil { return err } if attributes == nil { return &types.BadRequestError{Message: "StartChildWorkflowExecutionDecisionAttributes is not set on decision."} } if attributes.GetWorkflowID() == "" { return &types.BadRequestError{Message: "Required field WorkflowID is not set on decision."} } if attributes.WorkflowType == nil || attributes.WorkflowType.GetName() == "" { return &types.BadRequestError{Message: "Required field WorkflowType is not set on decision."} } idLengthWarnLimit := v.config.MaxIDLengthWarnLimit() if !common.IsValidIDLength( attributes.GetDomain(), v.metricsClient.Scope(metricsScope), idLengthWarnLimit, v.config.DomainNameMaxLength(attributes.GetDomain()), metrics.CadenceErrDomainNameExceededWarnLimit, attributes.GetDomain(), v.logger, tag.IDTypeDomainName) { return &types.BadRequestError{Message: "Domain exceeds length limit."} } if !common.IsValidIDLength( attributes.GetWorkflowID(), v.metricsClient.Scope(metricsScope), idLengthWarnLimit, v.config.WorkflowIDMaxLength(attributes.GetDomain()), metrics.CadenceErrWorkflowIDExceededWarnLimit, attributes.GetDomain(), v.logger, tag.IDTypeWorkflowID) { return &types.BadRequestError{Message: "WorkflowId exceeds length limit."} } if !common.IsValidIDLength( attributes.WorkflowType.GetName(), v.metricsClient.Scope(metricsScope), idLengthWarnLimit, v.config.WorkflowTypeMaxLength(attributes.GetDomain()), metrics.CadenceErrWorkflowTypeExceededWarnLimit, attributes.GetDomain(), v.logger, tag.IDTypeWorkflowType) { return &types.BadRequestError{Message: "WorkflowType exceeds length limit."} } if err := common.ValidateRetryPolicy(attributes.RetryPolicy); err != nil { return err } if attributes.GetCronSchedule() != "" { if _, err := backoff.ValidateSchedule(attributes.GetCronSchedule()); err != nil { return err } } // Inherit tasklist from parent workflow execution if not provided on decision taskList, err := v.validatedTaskList(attributes.TaskList, &types.TaskList{Name: parentInfo.TaskList, Kind: parentInfo.TaskListKind.Ptr()}, metricsScope, attributes.GetDomain()) if err != nil { return err } attributes.TaskList = taskList // Inherit workflow timeout from parent workflow execution if not provided on decision if attributes.GetExecutionStartToCloseTimeoutSeconds() <= 0 { attributes.ExecutionStartToCloseTimeoutSeconds = common.Int32Ptr(parentInfo.WorkflowTimeout) } // Inherit decision task timeout from parent workflow execution if not provided on decision if attributes.GetTaskStartToCloseTimeoutSeconds() <= 0 { attributes.TaskStartToCloseTimeoutSeconds = common.Int32Ptr(parentInfo.DecisionStartToCloseTimeout) } return nil } func (v *attrValidator) validatedTaskList( taskList *types.TaskList, defaultVal *types.TaskList, metricsScope metrics.ScopeIdx, domain string, ) (*types.TaskList, error) { if taskList == nil { taskList = &types.TaskList{} } if taskList.GetName() == "" { if defaultVal.GetName() == "" { return taskList, &types.BadRequestError{Message: "missing task list name"} } taskList.Name = defaultVal.GetName() taskList.Kind = defaultVal.Kind return taskList, nil } if taskList.GetKind() == types.TaskListKindNormal && defaultVal.GetKind() == types.TaskListKindEphemeral { taskList.Kind = defaultVal.Kind } name := taskList.GetName() if !common.IsValidIDLength( name, v.metricsClient.Scope(metricsScope), v.config.MaxIDLengthWarnLimit(), v.config.TaskListNameMaxLength(domain), metrics.CadenceErrTaskListNameExceededWarnLimit, domain, v.logger, tag.IDTypeTaskListName) { return taskList, &types.BadRequestError{ Message: fmt.Sprintf("task list name exceeds length limit of %v", v.config.TaskListNameMaxLength(domain)), } } if strings.HasPrefix(name, constants.ReservedTaskListPrefix) { return taskList, &types.BadRequestError{ Message: fmt.Sprintf("task list name cannot start with reserved prefix %v", constants.ReservedTaskListPrefix), } } return taskList, nil } func (v *attrValidator) validateCrossDomainCall( sourceDomainID string, targetDomainID string, ) error { // same name, no check needed if sourceDomainID == targetDomainID { return nil } sourceDomainEntry, err := v.domainCache.GetDomainByID(sourceDomainID) if err != nil { return err } targetDomainEntry, err := v.domainCache.GetDomainByID(targetDomainID) if err != nil { return err } sourceClusters := sourceDomainEntry.GetReplicationConfig().Clusters targetClusters := targetDomainEntry.GetReplicationConfig().Clusters // both "local domain" // here a domain is "local domain" when: // - IsGlobalDomain() returns false // - domainCluster contains only one cluster // case 1 can be actually be combined with this case if len(sourceClusters) == 1 && len(targetClusters) == 1 { if sourceClusters[0].ClusterName == targetClusters[0].ClusterName { return nil } return v.createCrossDomainCallError(sourceDomainEntry, targetDomainEntry) } // both global domain with > 1 replication cluster // when code reaches here, at least one domain has more than one cluster if len(sourceClusters) == len(targetClusters) && v.config.EnableCrossClusterOperationsForDomain(sourceDomainEntry.GetInfo().Name) { // check if the source domain cluster matches those for the target domain for _, sourceCluster := range sourceClusters { found := false for _, targetCluster := range targetClusters { if sourceCluster.ClusterName == targetCluster.ClusterName { found = true break } } if !found { return v.createCrossDomainCallError(sourceDomainEntry, targetDomainEntry) } } return nil } return v.createCrossDomainCallError(sourceDomainEntry, targetDomainEntry) } func (v *attrValidator) createCrossDomainCallError( domainEntry *cache.DomainCacheEntry, targetDomainEntry *cache.DomainCacheEntry, ) error { return &types.BadRequestError{Message: fmt.Sprintf( "cannot make cross domain call between %v and %v", domainEntry.GetInfo().Name, targetDomainEntry.GetInfo().Name, )} } ================================================ FILE: service/history/decision/checker_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package decision import ( "sort" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "go.uber.org/zap/zaptest/observer" "golang.org/x/exp/maps" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/execution" ) type ( attrValidatorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockDomainCache *cache.MockDomainCache validator *attrValidator testDomainID string testTargetDomainID string testActivityMaxScheduleToStartTimeoutForRetryInSeconds int32 } ) func TestAttrValidatorSuite(t *testing.T) { s := new(attrValidatorSuite) suite.Run(t, s) } func (s *attrValidatorSuite) SetupSuite() { s.testDomainID = "test domain ID" s.testTargetDomainID = "test target domain ID" s.testActivityMaxScheduleToStartTimeoutForRetryInSeconds = 1800 } func (s *attrValidatorSuite) TearDownSuite() { } func (s *attrValidatorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockDomainCache = cache.NewMockDomainCache(s.controller) config := &config.Config{ MaxIDLengthWarnLimit: dynamicproperties.GetIntPropertyFn(128), DomainNameMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), IdentityMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), WorkflowIDMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), SignalNameMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), WorkflowTypeMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), RequestIDMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), TaskListNameMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), ActivityIDMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), ActivityTypeMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), MarkerNameMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), TimerIDMaxLength: dynamicproperties.GetIntPropertyFilteredByDomain(1000), ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(definition.GetDefaultIndexedKeys()), EnableQueryAttributeValidation: dynamicproperties.GetBoolPropertyFn(true), SearchAttributesNumberOfKeysLimit: dynamicproperties.GetIntPropertyFilteredByDomain(100), SearchAttributesSizeOfValueLimit: dynamicproperties.GetIntPropertyFilteredByDomain(2 * 1024), SearchAttributesTotalSizeLimit: dynamicproperties.GetIntPropertyFilteredByDomain(40 * 1024), ActivityMaxScheduleToStartTimeoutForRetry: dynamicproperties.GetDurationPropertyFnFilteredByDomain( time.Duration(s.testActivityMaxScheduleToStartTimeoutForRetryInSeconds) * time.Second, ), EnableCrossClusterOperationsForDomain: dynamicproperties.GetBoolPropertyFnFilteredByDomain(false), } s.validator = newAttrValidator( s.mockDomainCache, metrics.NewNoopMetricsClient(), config, log.NewNoop(), ) } func (s *attrValidatorSuite) TearDownTest() { s.controller.Finish() } func (s *attrValidatorSuite) TestValidateSignalExternalWorkflowExecutionAttributes() { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, cluster.TestCurrentClusterName, ) targetDomainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, cluster.TestCurrentClusterName, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).AnyTimes() var attributes *types.SignalExternalWorkflowExecutionDecisionAttributes err := s.validator.validateSignalExternalWorkflowExecutionAttributes(s.testDomainID, s.testTargetDomainID, attributes, metrics.HistoryRespondDecisionTaskCompletedScope) s.EqualError(err, "SignalExternalWorkflowExecutionDecisionAttributes is not set on decision.") attributes = &types.SignalExternalWorkflowExecutionDecisionAttributes{} err = s.validator.validateSignalExternalWorkflowExecutionAttributes(s.testDomainID, s.testTargetDomainID, attributes, metrics.HistoryRespondDecisionTaskCompletedScope) s.EqualError(err, "Execution is nil on decision.") attributes.Execution = &types.WorkflowExecution{} attributes.Execution.WorkflowID = "workflow-id" err = s.validator.validateSignalExternalWorkflowExecutionAttributes(s.testDomainID, s.testTargetDomainID, attributes, metrics.HistoryRespondDecisionTaskCompletedScope) s.EqualError(err, "SignalName is not set on decision.") attributes.Execution.RunID = "run-id" err = s.validator.validateSignalExternalWorkflowExecutionAttributes(s.testDomainID, s.testTargetDomainID, attributes, metrics.HistoryRespondDecisionTaskCompletedScope) s.EqualError(err, "Invalid RunId set on decision.") attributes.Execution.RunID = constants.TestRunID attributes.SignalName = "my signal name" err = s.validator.validateSignalExternalWorkflowExecutionAttributes(s.testDomainID, s.testTargetDomainID, attributes, metrics.HistoryRespondDecisionTaskCompletedScope) s.NoError(err) attributes.Input = []byte("test input") err = s.validator.validateSignalExternalWorkflowExecutionAttributes(s.testDomainID, s.testTargetDomainID, attributes, metrics.HistoryRespondDecisionTaskCompletedScope) s.NoError(err) } func (s *attrValidatorSuite) TestValidateUpsertWorkflowSearchAttributes() { domainName := "testDomain" var attributes *types.UpsertWorkflowSearchAttributesDecisionAttributes err := s.validator.validateUpsertWorkflowSearchAttributes(domainName, attributes) s.EqualError(err, "UpsertWorkflowSearchAttributesDecisionAttributes is not set on decision.") attributes = &types.UpsertWorkflowSearchAttributesDecisionAttributes{} err = s.validator.validateUpsertWorkflowSearchAttributes(domainName, attributes) s.EqualError(err, "SearchAttributes is not set on decision.") attributes.SearchAttributes = &types.SearchAttributes{} err = s.validator.validateUpsertWorkflowSearchAttributes(domainName, attributes) s.EqualError(err, "IndexedFields is empty on decision.") attributes.SearchAttributes.IndexedFields = map[string][]byte{"CustomKeywordField": []byte(`"bytes"`)} err = s.validator.validateUpsertWorkflowSearchAttributes(domainName, attributes) s.Nil(err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_LocalToLocal() { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, cluster.TestCurrentClusterName, ) targetDomainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, cluster.TestCurrentClusterName, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.Nil(err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_LocalToEffectiveLocal_SameCluster() { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, cluster.TestCurrentClusterName, ) targetDomainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}}, }, 1234, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.Nil(err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_LocalToEffectiveLocal_DiffCluster() { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, cluster.TestCurrentClusterName, ) targetDomainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestAlternativeClusterName}}, }, 1234, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.IsType(&types.BadRequestError{}, err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_LocalToGlobal() { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, cluster.TestCurrentClusterName, ) targetDomainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.IsType(&types.BadRequestError{}, err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_EffectiveLocalToLocal_SameCluster() { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}}, }, 1234, ) targetDomainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, cluster.TestCurrentClusterName, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.Nil(err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_EffectiveLocalToLocal_DiffCluster() { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestAlternativeClusterName}}, }, 1234, ) targetDomainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, cluster.TestCurrentClusterName, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.IsType(&types.BadRequestError{}, err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_EffectiveLocalToEffectiveLocal_SameCluster() { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}}, }, 1234, ) targetDomainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}}, }, 5678, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.Nil(err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_EffectiveLocalToEffectiveLocal_DiffCluster() { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}}, }, 1234, ) targetDomainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestAlternativeClusterName}}, }, 5678, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.IsType(&types.BadRequestError{}, err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_EffectiveLocalToGlobal() { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, }, }, 5678, ) targetDomainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.IsType(&types.BadRequestError{}, err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_GlobalToLocal() { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, ) targetDomainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, cluster.TestCurrentClusterName, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.IsType(&types.BadRequestError{}, err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_GlobalToEffectiveLocal() { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 5678, ) targetDomainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, }, }, 1234, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.IsType(&types.BadRequestError{}, err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_GlobalToGlobal_SameDomain() { targetDomainID := s.testDomainID err := s.validator.validateCrossDomainCall(s.testDomainID, targetDomainID) s.Nil(err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_GlobalToGlobal_DiffDomain_SameCluster() { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestAlternativeClusterName}, {ClusterName: cluster.TestCurrentClusterName}, }, }, 1234, ) targetDomainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(2) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(2) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.IsType(&types.BadRequestError{}, err) s.validator.config.EnableCrossClusterOperationsForDomain = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) err = s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.Nil(err) } func (s *attrValidatorSuite) TestValidateCrossDomainCall_GlobalToGlobal_DiffDomain_DiffCluster() { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestAlternativeClusterName}, {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: "cluster name for s.testDomainID"}, }, }, 1234, ) targetDomainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, {ClusterName: "cluster name for s.testTargetDomainID"}, }, }, 1234, ) s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateCrossDomainCall(s.testDomainID, s.testTargetDomainID) s.IsType(&types.BadRequestError{}, err) } func (s *attrValidatorSuite) TestValidateTaskListName() { taskList := func(name string) *types.TaskList { kind := types.TaskListKindNormal return &types.TaskList{Name: name, Kind: &kind} } ephemeralTasklist := func(name string) *types.TaskList { kind := types.TaskListKindEphemeral return &types.TaskList{Name: name, Kind: &kind} } testCases := []struct { defaultVal *types.TaskList input *types.TaskList output *types.TaskList isOutputErr bool }{ {taskList("tl-1"), nil, taskList("tl-1"), false}, {taskList(""), taskList("tl-1"), taskList("tl-1"), false}, {taskList("tl-1"), taskList("tl-1"), taskList("tl-1"), false}, {taskList(""), taskList("/tl-1"), taskList("/tl-1"), false}, {ephemeralTasklist("tl-1"), nil, ephemeralTasklist("tl-1"), false}, {ephemeralTasklist("tl-1"), taskList("tl-1"), ephemeralTasklist("tl-1"), false}, {ephemeralTasklist("tl-1"), taskList("tl-2"), ephemeralTasklist("tl-2"), false}, {ephemeralTasklist("tl-1"), taskList(""), ephemeralTasklist("tl-1"), false}, {taskList(""), taskList("/__cadence_sys"), taskList("/__cadence_sys"), false}, {taskList(""), nil, &types.TaskList{}, true}, {taskList(""), taskList(""), taskList(""), true}, {taskList(""), taskList(commonconstants.ReservedTaskListPrefix), taskList(commonconstants.ReservedTaskListPrefix), true}, {taskList("tl-1"), taskList(commonconstants.ReservedTaskListPrefix), taskList(commonconstants.ReservedTaskListPrefix), true}, {taskList(""), taskList(commonconstants.ReservedTaskListPrefix + "tl-1"), taskList(commonconstants.ReservedTaskListPrefix + "tl-1"), true}, {taskList("tl-1"), taskList(commonconstants.ReservedTaskListPrefix + "tl-1"), taskList(commonconstants.ReservedTaskListPrefix + "tl-1"), true}, } for _, tc := range testCases { key := tc.defaultVal.Name + "," + tc.defaultVal.Kind.String() + "#" if tc.input != nil { key += tc.input.GetName() } else { key += "nil" } s.Run(key, func() { output, err := s.validator.validatedTaskList(tc.input, tc.defaultVal, metrics.HistoryRespondDecisionTaskCompletedScope, "domain_name") if tc.isOutputErr { s.Error(err) } else { s.NoError(err) } s.EqualValues(tc.output, output) }) } } func (s *attrValidatorSuite) TestValidateActivityScheduleAttributes_NoRetryPolicy() { wfTimeout := int32(5) attributes := &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "some random activityID", ActivityType: &types.ActivityType{ Name: "some random activity type", }, Domain: s.testDomainID, TaskList: &types.TaskList{ Name: "some random task list", }, Input: []byte{1, 2, 3}, ScheduleToCloseTimeoutSeconds: nil, // not set ScheduleToStartTimeoutSeconds: common.Int32Ptr(3), StartToCloseTimeoutSeconds: common.Int32Ptr(3), // ScheduleToStart + StartToClose > wfTimeout HeartbeatTimeoutSeconds: common.Int32Ptr(10), // larger then wfTimeout } expectedAttributesAfterValidation := &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: attributes.ActivityID, ActivityType: attributes.ActivityType, Domain: attributes.Domain, TaskList: attributes.TaskList, Input: attributes.Input, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(wfTimeout), ScheduleToStartTimeoutSeconds: attributes.ScheduleToStartTimeoutSeconds, StartToCloseTimeoutSeconds: attributes.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: common.Int32Ptr(wfTimeout), } domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, cluster.TestCurrentClusterName, ) targetDomainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, cluster.TestCurrentClusterName, ) executionInfo := &persistence.WorkflowExecutionInfo{ WorkflowTimeout: wfTimeout, } s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateActivityScheduleAttributes( s.testDomainID, s.testTargetDomainID, attributes, executionInfo, metrics.HistoryRespondDecisionTaskCompletedScope, ) s.Nil(err) s.Equal(expectedAttributesAfterValidation, attributes) } func (s *attrValidatorSuite) TestValidateActivityScheduleAttributes_WithRetryPolicy_ScheduleToStartRetryable() { s.mockDomainCache.EXPECT().GetDomainName(s.testDomainID).Return("some random domain name", nil).Times(1) wfTimeout := int32(3000) attributes := &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "some random activityID", ActivityType: &types.ActivityType{ Name: "some random activity type", }, Domain: s.testDomainID, TaskList: &types.TaskList{ Name: "some random task list", }, Input: []byte{1, 2, 3}, ScheduleToCloseTimeoutSeconds: nil, // not set ScheduleToStartTimeoutSeconds: common.Int32Ptr(3), StartToCloseTimeoutSeconds: common.Int32Ptr(500), // extended ScheduleToStart + StartToClose > wfTimeout HeartbeatTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1.1, ExpirationIntervalInSeconds: s.testActivityMaxScheduleToStartTimeoutForRetryInSeconds + 1000, // larger than maximumScheduleToStartTimeoutForRetryInSeconds NonRetriableErrorReasons: []string{"non-retryable error"}, }, } expectedAttributesAfterValidation := &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: attributes.ActivityID, ActivityType: attributes.ActivityType, Domain: attributes.Domain, TaskList: attributes.TaskList, Input: attributes.Input, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(attributes.RetryPolicy.ExpirationIntervalInSeconds), ScheduleToStartTimeoutSeconds: common.Int32Ptr(s.testActivityMaxScheduleToStartTimeoutForRetryInSeconds), StartToCloseTimeoutSeconds: attributes.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: attributes.HeartbeatTimeoutSeconds, RetryPolicy: attributes.RetryPolicy, } domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, cluster.TestCurrentClusterName, ) targetDomainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, cluster.TestCurrentClusterName, ) executionInfo := &persistence.WorkflowExecutionInfo{ WorkflowTimeout: wfTimeout, } s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateActivityScheduleAttributes( s.testDomainID, s.testTargetDomainID, attributes, executionInfo, metrics.HistoryRespondDecisionTaskCompletedScope, ) s.Nil(err) s.Equal(expectedAttributesAfterValidation, attributes) } func (s *attrValidatorSuite) TestValidateActivityScheduleAttributes_WithRetryPolicy_ScheduleToStartNonRetryable() { wfTimeout := int32(1000) attributes := &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "some random activityID", ActivityType: &types.ActivityType{ Name: "some random activity type", }, Domain: s.testDomainID, TaskList: &types.TaskList{ Name: "some random task list", }, Input: []byte{1, 2, 3}, ScheduleToCloseTimeoutSeconds: nil, // not set ScheduleToStartTimeoutSeconds: common.Int32Ptr(3), StartToCloseTimeoutSeconds: common.Int32Ptr(500), // extended ScheduleToStart + StartToClose > wfTimeout HeartbeatTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1.1, ExpirationIntervalInSeconds: s.testActivityMaxScheduleToStartTimeoutForRetryInSeconds + 1000, // larger than wfTimeout and maximumScheduleToStartTimeoutForRetryInSeconds NonRetriableErrorReasons: []string{"cadenceInternal:Timeout SCHEDULE_TO_START"}, }, } expectedAttributesAfterValidation := &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: attributes.ActivityID, ActivityType: attributes.ActivityType, Domain: attributes.Domain, TaskList: attributes.TaskList, Input: attributes.Input, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(wfTimeout), ScheduleToStartTimeoutSeconds: attributes.ScheduleToStartTimeoutSeconds, StartToCloseTimeoutSeconds: attributes.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: attributes.HeartbeatTimeoutSeconds, RetryPolicy: attributes.RetryPolicy, } domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testDomainID}, nil, cluster.TestCurrentClusterName, ) targetDomainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: s.testTargetDomainID}, nil, cluster.TestCurrentClusterName, ) executionInfo := &persistence.WorkflowExecutionInfo{ WorkflowTimeout: wfTimeout, } s.mockDomainCache.EXPECT().GetDomainByID(s.testDomainID).Return(domainEntry, nil).Times(1) s.mockDomainCache.EXPECT().GetDomainByID(s.testTargetDomainID).Return(targetDomainEntry, nil).Times(1) err := s.validator.validateActivityScheduleAttributes( s.testDomainID, s.testTargetDomainID, attributes, executionInfo, metrics.HistoryRespondDecisionTaskCompletedScope, ) s.Nil(err) s.Equal(expectedAttributesAfterValidation, attributes) } const ( testDomainID = "test-domain-id" testDomainName = "test-domain" testWorkflowID = "test-workflow-id" testRunID = "test-run-id" ) func TestWorkflowSizeChecker_failWorkflowIfBlobSizeExceedsLimit(t *testing.T) { var ( testDecisionTag = metrics.DecisionTypeTag(types.DecisionTypeCompleteWorkflowExecution.String()) testEventID = int64(1) testMessage = "test" ) for name, tc := range map[string]struct { blobSizeLimitWarn int blobSizeLimitError int blob []byte assertLogsAndMetrics func(*testing.T, *observer.ObservedLogs, tally.TestScope) expectFail bool }{ "no errors": { blobSizeLimitWarn: 10, blobSizeLimitError: 20, blob: []byte("test"), assertLogsAndMetrics: func(t *testing.T, logs *observer.ObservedLogs, scope tally.TestScope) { assert.Empty(t, logs.All()) // ensure metrics with the size is emitted. timerData := maps.Values(scope.Snapshot().Timers()) assert.Len(t, timerData, 2) assert.Equal(t, "test.event_blob_size", timerData[0].Name()) }, }, "warn": { blobSizeLimitWarn: 10, blobSizeLimitError: 20, blob: []byte("should-warn"), assertLogsAndMetrics: func(t *testing.T, logs *observer.ObservedLogs, scope tally.TestScope) { logEntries := logs.All() require.Len(t, logEntries, 1) assert.Equal(t, "Blob size close to the limit.", logEntries[0].Message) }, }, "fail": { blobSizeLimitWarn: 5, blobSizeLimitError: 10, blob: []byte("should-fail"), assertLogsAndMetrics: func(t *testing.T, logs *observer.ObservedLogs, scope tally.TestScope) { logEntries := logs.All() require.Len(t, logEntries, 1) assert.Equal(t, "Blob size exceeds limit.", logEntries[0].Message) }, expectFail: true, }, } { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mutableState := execution.NewMockMutableState(ctrl) logger, logs := testlogger.NewObserved(t) metricsScope := tally.NewTestScope("test", nil) checker := &workflowSizeChecker{ blobSizeLimitWarn: tc.blobSizeLimitWarn, blobSizeLimitError: tc.blobSizeLimitError, completedID: testEventID, mutableState: mutableState, logger: logger, metricsScope: metrics.NewClient(metricsScope, metrics.History, metrics.HistogramMigration{}).Scope(metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DomainTag(testDomainName)), } mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }).Times(1) if tc.expectFail { mutableState.EXPECT().AddFailWorkflowEvent(testEventID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonDecisionBlobSizeExceedsLimit), Details: []byte(testMessage), }).Return(nil, nil).Times(1) } failed, err := checker.failWorkflowIfBlobSizeExceedsLimit(testDecisionTag, tc.blob, testMessage) require.NoError(t, err) if tc.assertLogsAndMetrics != nil { tc.assertLogsAndMetrics(t, logs, metricsScope) } assert.Equal(t, tc.expectFail, failed) }) } } func TestWorkflowSizeChecker_failWorkflowSizeExceedsLimit(t *testing.T) { var ( testEventID = int64(1) ) for name, tc := range map[string]struct { historyCount int historyCountLimitWarn int historyCountLimitError int historySize int historySizeLimitWarn int historySizeLimitError int noExecutionCall bool assertLogsAndMetrics func(*testing.T, *observer.ObservedLogs, tally.TestScope) expectFail bool }{ "no errors": { historyCount: 1, historyCountLimitWarn: 10, historyCountLimitError: 20, historySize: 1, historySizeLimitWarn: 10, historySizeLimitError: 20, noExecutionCall: true, assertLogsAndMetrics: func(t *testing.T, logs *observer.ObservedLogs, scope tally.TestScope) { assert.Empty(t, logs.All()) // ensure metrics with the size is emitted. timerData := maps.Values(scope.Snapshot().Timers()) assert.Len(t, timerData, 4) timerNames := make([]string, 0, 4) for _, timer := range timerData { timerNames = append(timerNames, timer.Name()) } sort.Strings(timerNames) // timers are duplicated for specific domain and domain: all assert.Equal(t, []string{"test.history_count", "test.history_count", "test.history_size", "test.history_size"}, timerNames) }, }, "count warn": { historyCount: 15, historyCountLimitWarn: 10, historyCountLimitError: 20, historySize: 1, historySizeLimitWarn: 10, historySizeLimitError: 20, assertLogsAndMetrics: func(t *testing.T, logs *observer.ObservedLogs, scope tally.TestScope) { logEntries := logs.All() require.Len(t, logEntries, 1) assert.Equal(t, "history size exceeds warn limit.", logEntries[0].Message) }, }, "count error": { historyCount: 25, historyCountLimitWarn: 10, historyCountLimitError: 20, historySize: 1, historySizeLimitWarn: 10, historySizeLimitError: 20, assertLogsAndMetrics: func(t *testing.T, logs *observer.ObservedLogs, scope tally.TestScope) { logEntries := logs.All() require.Len(t, logEntries, 1) assert.Equal(t, "history size exceeds error limit.", logEntries[0].Message) }, expectFail: true, }, "size warn": { historyCount: 1, historyCountLimitWarn: 10, historyCountLimitError: 20, historySize: 15, historySizeLimitWarn: 10, historySizeLimitError: 20, assertLogsAndMetrics: func(t *testing.T, logs *observer.ObservedLogs, scope tally.TestScope) { logEntries := logs.All() require.Len(t, logEntries, 1) assert.Equal(t, "history size exceeds warn limit.", logEntries[0].Message) }, }, "size error": { historyCount: 1, historyCountLimitWarn: 10, historyCountLimitError: 20, historySize: 25, historySizeLimitWarn: 10, historySizeLimitError: 20, assertLogsAndMetrics: func(t *testing.T, logs *observer.ObservedLogs, scope tally.TestScope) { logEntries := logs.All() require.Len(t, logEntries, 1) assert.Equal(t, "history size exceeds error limit.", logEntries[0].Message) }, expectFail: true, }, } { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) mutableState := execution.NewMockMutableState(ctrl) logger, logs := testlogger.NewObserved(t) metricsScope := tally.NewTestScope("test", nil) mutableState.EXPECT().GetNextEventID().Return(int64(tc.historyCount + 1)).Times(1) if !tc.noExecutionCall { mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }).Times(1) } if tc.expectFail { mutableState.EXPECT().AddFailWorkflowEvent(testEventID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonSizeExceedsLimit), Details: []byte("Workflow history size / count exceeds limit."), }).Return(nil, nil).Times(1) } checker := &workflowSizeChecker{ completedID: testEventID, historyCountLimitWarn: tc.historyCountLimitWarn, historyCountLimitError: tc.historyCountLimitError, historySizeLimitWarn: tc.historySizeLimitWarn, historySizeLimitError: tc.historySizeLimitError, mutableState: mutableState, executionStats: &persistence.ExecutionStats{ HistorySize: int64(tc.historySize), }, logger: logger, metricsScope: metrics.NewClient(metricsScope, metrics.History, metrics.HistogramMigration{}).Scope(metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DomainTag(testDomainName)), } failed, err := checker.failWorkflowSizeExceedsLimit() require.NoError(t, err) if tc.assertLogsAndMetrics != nil { tc.assertLogsAndMetrics(t, logs, metricsScope) } assert.Equal(t, tc.expectFail, failed) }) } } ================================================ FILE: service/history/decision/handler.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package decision import ( "context" "fmt" "time" "go.uber.org/yarpc" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/query" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/workflow" ) type ( // Handler contains decision business logic Handler interface { HandleDecisionTaskScheduled(context.Context, *types.ScheduleDecisionTaskRequest) error HandleDecisionTaskStarted(context.Context, *types.RecordDecisionTaskStartedRequest) (*types.RecordDecisionTaskStartedResponse, error) HandleDecisionTaskFailed(context.Context, *types.HistoryRespondDecisionTaskFailedRequest) error HandleDecisionTaskCompleted(context.Context, *types.HistoryRespondDecisionTaskCompletedRequest) (*types.HistoryRespondDecisionTaskCompletedResponse, error) // TODO also include the handle of decision timeout here } handlerImpl struct { config *config.Config shard shard.Context timeSource clock.TimeSource domainCache cache.DomainCache executionCache execution.Cache tokenSerializer common.TaskTokenSerializer metricsClient metrics.Client logger log.Logger throttledLogger log.Logger attrValidator *attrValidator versionChecker client.VersionChecker activeClusterManager activecluster.Manager } ) // NewHandler creates a new Handler for handling decision business logic func NewHandler( shard shard.Context, executionCache execution.Cache, tokenSerializer common.TaskTokenSerializer, ) Handler { config := shard.GetConfig() logger := shard.GetLogger().WithTags(tag.ComponentDecisionHandler) return &handlerImpl{ config: config, shard: shard, timeSource: shard.GetTimeSource(), domainCache: shard.GetDomainCache(), executionCache: executionCache, tokenSerializer: tokenSerializer, metricsClient: shard.GetMetricsClient(), logger: shard.GetLogger().WithTags(tag.ComponentDecisionHandler), activeClusterManager: shard.GetActiveClusterManager(), throttledLogger: shard.GetThrottledLogger().WithTags(tag.ComponentDecisionHandler), attrValidator: newAttrValidator( shard.GetDomainCache(), shard.GetMetricsClient(), config, logger, ), versionChecker: client.NewVersionChecker(), } } func (handler *handlerImpl) HandleDecisionTaskScheduled( ctx context.Context, req *types.ScheduleDecisionTaskRequest, ) error { domainEntry, err := handler.getActiveDomainByWorkflow(ctx, req.DomainUUID, req.WorkflowExecution.WorkflowID, req.WorkflowExecution.RunID) if err != nil { return err } domainID := domainEntry.GetInfo().ID workflowExecution := types.WorkflowExecution{ WorkflowID: req.WorkflowExecution.WorkflowID, RunID: req.WorkflowExecution.RunID, } return workflow.UpdateWithActionFunc( ctx, handler.logger, handler.executionCache, domainID, workflowExecution, handler.timeSource.Now(), func(context execution.Context, mutableState execution.MutableState) (*workflow.UpdateAction, error) { if !mutableState.IsWorkflowExecutionRunning() { return nil, workflow.ErrNotExists } if mutableState.HasProcessedOrPendingDecision() { return &workflow.UpdateAction{ Noop: true, }, nil } startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return nil, err } if err := mutableState.AddFirstDecisionTaskScheduled( startEvent, ); err != nil { return nil, err } return &workflow.UpdateAction{}, nil }, ) } func (handler *handlerImpl) HandleDecisionTaskStarted( ctx context.Context, req *types.RecordDecisionTaskStartedRequest, ) (*types.RecordDecisionTaskStartedResponse, error) { domainEntry, err := handler.getActiveDomainByWorkflow(ctx, req.DomainUUID, req.WorkflowExecution.WorkflowID, req.WorkflowExecution.RunID) if err != nil { return nil, err } domainID := domainEntry.GetInfo().ID workflowExecution := types.WorkflowExecution{ WorkflowID: req.WorkflowExecution.WorkflowID, RunID: req.WorkflowExecution.RunID, } scheduleID := req.GetScheduleID() requestID := req.GetRequestID() var resp *types.RecordDecisionTaskStartedResponse err = workflow.UpdateWithActionFunc( ctx, handler.logger, handler.executionCache, domainID, workflowExecution, handler.timeSource.Now(), func(context execution.Context, mutableState execution.MutableState) (*workflow.UpdateAction, error) { if !mutableState.IsWorkflowExecutionRunning() { return nil, workflow.ErrNotExists } decision, isRunning := mutableState.GetDecisionInfo(scheduleID) // First check to see if cache needs to be refreshed as we could potentially have stale workflow execution in // some extreme cassandra failure cases. if !isRunning && scheduleID >= mutableState.GetNextEventID() { handler.metricsClient.IncCounter(metrics.HistoryRecordDecisionTaskStartedScope, metrics.StaleMutableStateCounter) handler.logger.Error("Encounter stale mutable state in RecordDecisionTaskStarted", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(scheduleID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) // Reload workflow execution history // ErrStaleState will trigger updateWorkflowExecutionWithAction function to reload the mutable state return nil, workflow.ErrStaleState } // Check execution state to make sure task is in the list of outstanding tasks and it is not yet started. If // task is not outstanding than it is most probably a duplicate and complete the task. if !isRunning { // Looks like DecisionTask already completed as a result of another call. // It is OK to drop the task at this point. return nil, &types.EntityNotExistsError{Message: "Decision task not found."} } updateAction := &workflow.UpdateAction{} if decision.StartedID != constants.EmptyEventID { // If decision is started as part of the current request scope then return a positive response if decision.RequestID == requestID { resp, err = handler.createRecordDecisionTaskStartedResponse(domainID, mutableState, decision, req.PollRequest.GetIdentity()) if err != nil { return nil, err } updateAction.Noop = true return updateAction, nil } // Looks like DecisionTask already started as a result of another call. // It is OK to drop the task at this point. return nil, &types.EventAlreadyStartedError{Message: "Decision task already started."} } _, decision, err = mutableState.AddDecisionTaskStartedEvent(scheduleID, requestID, req.PollRequest) if err != nil { // Unable to add DecisionTaskStarted event to history return nil, &types.InternalServiceError{Message: "Unable to add DecisionTaskStarted event to history."} } resp, err = handler.createRecordDecisionTaskStartedResponse(domainID, mutableState, decision, req.PollRequest.GetIdentity()) if err != nil { return nil, err } return updateAction, nil }, ) if err != nil { return nil, err } return resp, nil } func (handler *handlerImpl) HandleDecisionTaskFailed( ctx context.Context, req *types.HistoryRespondDecisionTaskFailedRequest, ) (retError error) { request := req.FailedRequest token, err := handler.tokenSerializer.Deserialize(request.TaskToken) if err != nil { return workflow.ErrDeserializingToken } domainEntry, err := handler.getActiveDomainByWorkflow(ctx, req.DomainUUID, token.WorkflowID, token.RunID) if err != nil { return err } domainID := domainEntry.GetInfo().ID workflowExecution := types.WorkflowExecution{ WorkflowID: token.WorkflowID, RunID: token.RunID, } return workflow.UpdateWithAction(ctx, handler.logger, handler.executionCache, domainID, workflowExecution, true, handler.timeSource.Now(), func(context execution.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return workflow.ErrAlreadyCompleted } scheduleID := token.ScheduleID decision, isRunning := mutableState.GetDecisionInfo(scheduleID) if !isRunning || decision.Attempt != token.ScheduleAttempt || decision.StartedID == constants.EmptyEventID { return &types.EntityNotExistsError{Message: "Decision task not found."} } _, err := mutableState.AddDecisionTaskFailedEvent(decision.ScheduleID, decision.StartedID, request.GetCause(), request.Details, request.GetIdentity(), "", request.GetBinaryChecksum(), "", "", 0, "") return err }) } func (handler *handlerImpl) HandleDecisionTaskCompleted( ctx context.Context, req *types.HistoryRespondDecisionTaskCompletedRequest, ) (resp *types.HistoryRespondDecisionTaskCompletedResponse, retError error) { request := req.CompleteRequest token, err0 := handler.tokenSerializer.Deserialize(request.TaskToken) if err0 != nil { return nil, workflow.ErrDeserializingToken } domainEntry, err := handler.getActiveDomainByWorkflow(ctx, req.DomainUUID, token.WorkflowID, token.RunID) if err != nil { return nil, err } domainID := domainEntry.GetInfo().ID workflowExecution := types.WorkflowExecution{ WorkflowID: token.WorkflowID, RunID: token.RunID, } domainName := domainEntry.GetInfo().Name logger := handler.logger.WithTags( tag.WorkflowDomainName(domainName), tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(token.ScheduleID), ) scope := handler.metricsClient.Scope(metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DomainTag(domainName), metrics.WorkflowTypeTag(token.WorkflowType)) call := yarpc.CallFromContext(ctx) clientLibVersion := call.Header(common.LibraryVersionHeaderName) clientFeatureVersion := call.Header(common.FeatureVersionHeaderName) clientImpl := call.Header(common.ClientImplHeaderName) wfContext, release, err := handler.executionCache.GetOrCreateWorkflowExecution(ctx, domainID, workflowExecution) if err != nil { return nil, err } defer func() { release(retError) }() Update_History_Loop: for attempt := 0; attempt < workflow.ConditionalRetryCount; attempt++ { logger.Debug("Update_History_Loop attempt", tag.Attempt(int32(attempt))) msBuilder, err := wfContext.LoadWorkflowExecution(ctx) if err != nil { return nil, err } if !msBuilder.IsWorkflowExecutionRunning() { return nil, workflow.ErrAlreadyCompleted } executionStats, err := wfContext.LoadExecutionStats(ctx) if err != nil { return nil, err } executionInfo := msBuilder.GetExecutionInfo() currentDecision, isRunning := msBuilder.GetDecisionInfo(token.ScheduleID) // First check to see if cache needs to be refreshed as we could potentially have stale workflow execution in // some extreme cassandra failure cases. if !isRunning && token.ScheduleID >= msBuilder.GetNextEventID() { scope.IncCounter(metrics.StaleMutableStateCounter) logger.Error("Encounter stale mutable state in RespondDecisionTaskCompleted", tag.WorkflowNextEventID(msBuilder.GetNextEventID())) // Reload workflow execution history wfContext.Clear() continue Update_History_Loop } if !msBuilder.IsWorkflowExecutionRunning() || !isRunning || currentDecision.Attempt != token.ScheduleAttempt || currentDecision.StartedID == constants.EmptyEventID { logger.Debugf("Decision task not found. IsWorkflowExecutionRunning: %v, isRunning: %v, currentDecision.Attempt: %v, token.ScheduleAttempt: %v, currentDecision.StartID: %v", msBuilder.IsWorkflowExecutionRunning(), isRunning, getDecisionInfoAttempt(currentDecision), token.ScheduleAttempt, getDecisionInfoStartedID(currentDecision)) return nil, &types.EntityNotExistsError{Message: "Decision task not found."} } startedID := currentDecision.StartedID maxResetPoints := handler.config.MaxAutoResetPoints(domainEntry.GetInfo().Name) if msBuilder.GetExecutionInfo().AutoResetPoints != nil && maxResetPoints == len(msBuilder.GetExecutionInfo().AutoResetPoints.Points) { logger.Debugf("Max reset points %d is exceeded", maxResetPoints) scope.IncCounter(metrics.AutoResetPointsLimitExceededCounter) } decisionHeartbeating := request.GetForceCreateNewDecisionTask() && len(request.Decisions) == 0 var decisionHeartbeatTimeout bool var completedEvent *types.HistoryEvent if decisionHeartbeating { timeout := handler.config.DecisionHeartbeatTimeout(domainName) if currentDecision.OriginalScheduledTimestamp > 0 && handler.timeSource.Now().After(time.Unix(0, currentDecision.OriginalScheduledTimestamp).Add(timeout)) { decisionHeartbeatTimeout = true scope.IncCounter(metrics.DecisionHeartbeatTimeoutCounter) completedEvent, err = msBuilder.AddDecisionTaskTimedOutEvent(currentDecision.ScheduleID, currentDecision.StartedID) if err != nil { return nil, &types.InternalServiceError{Message: "Failed to add decision timeout event."} } msBuilder.ClearStickyness() } else { logger.Debug("Adding DecisionTaskCompletedEvent to mutable state for heartbeat") completedEvent, err = msBuilder.AddDecisionTaskCompletedEvent(token.ScheduleID, startedID, request, maxResetPoints) if err != nil { return nil, &types.InternalServiceError{Message: "Unable to add DecisionTaskCompleted event to history."} } } } else { completedEvent, err = msBuilder.AddDecisionTaskCompletedEvent(token.ScheduleID, startedID, request, maxResetPoints) if err != nil { return nil, &types.InternalServiceError{Message: "Unable to add DecisionTaskCompleted event to history."} } } var ( failDecision bool failCause types.DecisionTaskFailedCause failMessage string activityNotStartedCancelled bool continueAsNewBuilder execution.MutableState hasUnhandledEvents bool decisionResults []*decisionResult ) hasUnhandledEvents = msBuilder.HasBufferedEvents() if request.StickyAttributes == nil || request.StickyAttributes.WorkerTaskList == nil { scope.IncCounter(metrics.CompleteDecisionWithStickyDisabledCounter) executionInfo.StickyTaskList = "" executionInfo.StickyScheduleToStartTimeout = 0 } else { scope.IncCounter(metrics.CompleteDecisionWithStickyEnabledCounter) executionInfo.StickyTaskList = request.StickyAttributes.WorkerTaskList.GetName() executionInfo.StickyScheduleToStartTimeout = request.StickyAttributes.GetScheduleToStartTimeoutSeconds() } executionInfo.ClientLibraryVersion = clientLibVersion executionInfo.ClientFeatureVersion = clientFeatureVersion executionInfo.ClientImpl = clientImpl binChecksum := request.GetBinaryChecksum() if _, ok := domainEntry.GetConfig().BadBinaries.Binaries[binChecksum]; ok { failDecision = true failCause = types.DecisionTaskFailedCauseBadBinary failMessage = fmt.Sprintf("binary %v is already marked as bad deployment", binChecksum) } else { workflowSizeChecker := newWorkflowSizeChecker( domainName, handler.config.BlobSizeLimitWarn(domainName), handler.config.BlobSizeLimitError(domainName), handler.config.HistorySizeLimitWarn(domainName), handler.config.HistorySizeLimitError(domainName), handler.config.HistoryCountLimitWarn(domainName), handler.config.HistoryCountLimitError(domainName), completedEvent.ID, msBuilder, executionStats, handler.metricsClient.Scope(metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DomainTag(domainName)), handler.logger, ) decisionTaskHandler := newDecisionTaskHandler( request.GetIdentity(), completedEvent.ID, domainEntry, msBuilder, handler.attrValidator, workflowSizeChecker, handler.tokenSerializer, handler.logger, handler.domainCache, handler.metricsClient, handler.config, ) if decisionResults, err = decisionTaskHandler.handleDecisions( ctx, request.ExecutionContext, request.Decisions, ); err != nil { return nil, err } // set the vars used by following logic // further refactor should also clean up the vars used below failDecision = decisionTaskHandler.failDecision if failDecision { failCause = *decisionTaskHandler.failDecisionCause failMessage = *decisionTaskHandler.failMessage } // failMessage is not used by decisionTaskHandler activityNotStartedCancelled = decisionTaskHandler.activityNotStartedCancelled // continueAsNewTimerTasks is not used by decisionTaskHandler continueAsNewBuilder = decisionTaskHandler.continueAsNewBuilder hasUnhandledEvents = decisionTaskHandler.hasUnhandledEventsBeforeDecisions } if failDecision { scope.IncCounter(metrics.FailedDecisionsCounter) logger.Info("Failing the decision.", tag.WorkflowDecisionFailCause(int64(failCause))) msBuilder, err = handler.failDecisionHelper( ctx, wfContext, token.ScheduleID, startedID, failCause, []byte(failMessage), request, domainEntry) if err != nil { return nil, err } hasUnhandledEvents = true continueAsNewBuilder = nil } createNewDecisionTask := msBuilder.IsWorkflowExecutionRunning() && (hasUnhandledEvents || request.GetForceCreateNewDecisionTask() || activityNotStartedCancelled) logger.Debugf("createNewDecisionTask: %v, msBuilder.IsWorkflowExecutionRunning: %v, hasUnhandledEvents: %v, request.GetForceCreateNewDecisionTask: %v, activityNotStartedCancelled: %v", createNewDecisionTask, msBuilder.IsWorkflowExecutionRunning(), hasUnhandledEvents, request.GetForceCreateNewDecisionTask(), activityNotStartedCancelled) var newDecisionTaskScheduledID int64 if createNewDecisionTask { var newDecision *execution.DecisionInfo var err error if decisionHeartbeating && !decisionHeartbeatTimeout { newDecision, err = msBuilder.AddDecisionTaskScheduledEventAsHeartbeat( request.GetReturnNewDecisionTask(), currentDecision.OriginalScheduledTimestamp, ) } else { newDecision, err = msBuilder.AddDecisionTaskScheduledEvent( request.GetReturnNewDecisionTask(), ) } if err != nil { return nil, &types.InternalServiceError{Message: "Failed to add decision scheduled event."} } newDecisionTaskScheduledID = newDecision.ScheduleID // skip transfer task for decision if request asking to return new decision task if request.GetReturnNewDecisionTask() { logger.Debugf("Adding DecisionTaskStartedEvent to mutable state. new decision's ScheduleID: %d, TaskList: %s", newDecisionTaskScheduledID, newDecision.TaskList) // start the new decision task if request asked to do so // TODO: replace the poll request _, _, err := msBuilder.AddDecisionTaskStartedEvent(newDecision.ScheduleID, "request-from-RespondDecisionTaskCompleted", &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{Name: newDecision.TaskList}, Identity: request.Identity, }) if err != nil { return nil, err } } } // We apply the update to execution using optimistic concurrency. If it fails due to a conflict then reload // the history and try the operation again. var updateErr error if continueAsNewBuilder != nil { continueAsNewExecutionInfo := continueAsNewBuilder.GetExecutionInfo() logger.Debugf("Updating execution with continue as new info. new wfid: %s, runid: %s", continueAsNewExecutionInfo.WorkflowID, continueAsNewExecutionInfo.RunID) updateErr = wfContext.UpdateWorkflowExecutionWithNewAsActive( ctx, handler.shard.GetTimeSource().Now(), execution.NewContext( continueAsNewExecutionInfo.DomainID, types.WorkflowExecution{ WorkflowID: continueAsNewExecutionInfo.WorkflowID, RunID: continueAsNewExecutionInfo.RunID, }, handler.shard, handler.shard.GetExecutionManager(), handler.logger, ), continueAsNewBuilder, ) } else { handler.logger.Debug("HandleDecisionTaskCompleted calling UpdateWorkflowExecutionAsActive", tag.WorkflowID(msBuilder.GetExecutionInfo().WorkflowID)) updateErr = wfContext.UpdateWorkflowExecutionAsActive(ctx, handler.shard.GetTimeSource().Now()) } if updateErr != nil { if execution.IsConflictError(updateErr) { scope.IncCounter(metrics.ConcurrencyUpdateFailureCounter) logger.Error("Encounter conflict error while updating workflow execution", tag.WorkflowID(msBuilder.GetExecutionInfo().WorkflowID), tag.WorkflowRunID(msBuilder.GetExecutionInfo().RunID), tag.Error(updateErr), ) continue Update_History_Loop } // if updateErr resulted in TransactionSizeLimitError then fail workflow switch updateErr.(type) { case *persistence.TransactionSizeLimitError: // must reload mutable state because the first call to updateWorkflowExecutionWithContext or continueAsNewWorkflowExecution // clears mutable state if error is returned msBuilder, err = wfContext.LoadWorkflowExecution(ctx) if err != nil { return nil, err } eventBatchFirstEventID := msBuilder.GetNextEventID() if err := execution.TerminateWorkflow( msBuilder, eventBatchFirstEventID, common.FailureReasonTransactionSizeExceedsLimit, []byte(updateErr.Error()), execution.IdentityHistoryService, ); err != nil { return nil, err } handler.logger.Debug("HandleDecisionTaskCompleted calling UpdateWorkflowExecutionAsActive", tag.WorkflowID(msBuilder.GetExecutionInfo().WorkflowID)) if err := wfContext.UpdateWorkflowExecutionAsActive( ctx, handler.shard.GetTimeSource().Now(), ); err != nil { return nil, err } } return nil, updateErr } handler.handleBufferedQueries( msBuilder, clientImpl, clientFeatureVersion, req.GetCompleteRequest().GetQueryResults(), createNewDecisionTask, domainEntry, decisionHeartbeating) if decisionHeartbeatTimeout { // at this point, update is successful, but we still return an error to client so that the worker will give up this workflow return nil, &types.EntityNotExistsError{ Message: "decision heartbeat timeout", } } resp = &types.HistoryRespondDecisionTaskCompletedResponse{} if !msBuilder.IsWorkflowExecutionRunning() { // Workflow has been completed/terminated, so there is no need to dispatch more activity/decision tasks. return resp, nil } activitiesToDispatchLocally := make(map[string]*types.ActivityLocalDispatchInfo) for _, dr := range decisionResults { if dr.activityDispatchInfo != nil { activitiesToDispatchLocally[dr.activityDispatchInfo.ActivityID] = dr.activityDispatchInfo } } logger.Debugf("%d activities will be dispatched locally on the client side", len(activitiesToDispatchLocally)) resp.ActivitiesToDispatchLocally = activitiesToDispatchLocally if request.GetReturnNewDecisionTask() && createNewDecisionTask { decision, _ := msBuilder.GetDecisionInfo(newDecisionTaskScheduledID) resp.StartedResponse, err = handler.createRecordDecisionTaskStartedResponse(domainID, msBuilder, decision, request.GetIdentity()) if err != nil { return nil, err } // sticky is always enabled when worker request for new decision task from RespondDecisionTaskCompleted resp.StartedResponse.StickyExecutionEnabled = true } return resp, nil } return nil, workflow.ErrMaxAttemptsExceeded } func (handler *handlerImpl) createRecordDecisionTaskStartedResponse( domainID string, msBuilder execution.MutableState, decision *execution.DecisionInfo, identity string, ) (*types.RecordDecisionTaskStartedResponse, error) { response := &types.RecordDecisionTaskStartedResponse{} response.WorkflowType = msBuilder.GetWorkflowType() executionInfo := msBuilder.GetExecutionInfo() if executionInfo.LastProcessedEvent != constants.EmptyEventID { response.PreviousStartedEventID = common.Int64Ptr(executionInfo.LastProcessedEvent) } // Starting decision could result in different scheduleID if decision was transient and new new events came in // before it was started. response.ScheduledEventID = decision.ScheduleID response.StartedEventID = decision.StartedID // if we call IsStickyTaskListEnabled then it's possible that the decision is a // sticky decision but due to TTL check, the field becomes false // NOTE: it's possible that StickyTaskList is empty is even if the decision is scheduled // on a sticky tasklist since stickiness can be cleared at anytime by the ResetStickyTaskList API. // When this field is false, we will send full workflow history to client // (see createPollForDecisionTaskResponse in workflowHandler.go) // This is actually desired since if that API is called, it basically means the client side // cache has been cleared for the workflow and full history is needed by the client. Even if // client side still has the cache, client library is still able to handle the situation. response.StickyExecutionEnabled = msBuilder.GetExecutionInfo().StickyTaskList != "" response.NextEventID = msBuilder.GetNextEventID() response.Attempt = decision.Attempt response.WorkflowExecutionTaskList = &types.TaskList{ Name: executionInfo.TaskList, Kind: types.TaskListKindNormal.Ptr(), } response.ScheduledTimestamp = common.Int64Ptr(decision.ScheduledTimestamp) response.StartedTimestamp = common.Int64Ptr(decision.StartedTimestamp) if decision.Attempt > 0 { // This decision is retried from mutable state // Also return schedule and started which are not written to history yet scheduledEvent, startedEvent := msBuilder.CreateTransientDecisionEvents(decision, identity) response.DecisionInfo = &types.TransientDecisionInfo{} response.DecisionInfo.ScheduledEvent = scheduledEvent response.DecisionInfo.StartedEvent = startedEvent } currentBranchToken, err := msBuilder.GetCurrentBranchToken() if err != nil { return nil, err } response.BranchToken = currentBranchToken qr := msBuilder.GetQueryRegistry() buffered := qr.GetBufferedIDs() queries := make(map[string]*types.WorkflowQuery) for _, id := range buffered { input, err := qr.GetQueryInput(id) if err != nil { continue } queries[id] = input } response.Queries = queries response.HistorySize = msBuilder.GetHistorySize() return response, nil } func (handler *handlerImpl) handleBufferedQueries( msBuilder execution.MutableState, clientImpl string, clientFeatureVersion string, queryResults map[string]*types.WorkflowQueryResult, createNewDecisionTask bool, domainEntry *cache.DomainCacheEntry, decisionHeartbeating bool, ) { queryRegistry := msBuilder.GetQueryRegistry() if !queryRegistry.HasBufferedQuery() { return } domainID := domainEntry.GetInfo().ID domain := domainEntry.GetInfo().Name workflowID := msBuilder.GetExecutionInfo().WorkflowID runID := msBuilder.GetExecutionInfo().RunID scope := handler.metricsClient.Scope( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DomainTag(domainEntry.GetInfo().Name), metrics.DecisionTypeTag("ConsistentQuery")) // Consistent query requires both server and client worker support. If a consistent query was requested (meaning there are // buffered queries) but worker does not support consistent query then all buffered queries should be failed. versionErr := handler.versionChecker.SupportsConsistentQuery(clientImpl, clientFeatureVersion) // todo (David.Porter) remove the skip on version check for // clientImpl and clientFeatureVersion where they're nil // There's a bug, probably in matching somewhere which isn't // forwarding the client headers for version // info correctly making this call erroneously fail sometimes. // https://t3.uberinternal.com/browse/CDNC-8641 // So defaulting just this flow to fail-open in the absence of headers. if versionErr != nil && clientImpl != "" && clientFeatureVersion != "" { scope.IncCounter(metrics.WorkerNotSupportsConsistentQueryCount) failedTerminationState := &query.TerminationState{ TerminationType: query.TerminationTypeFailed, Failure: &types.BadRequestError{Message: versionErr.Error()}, } buffered := queryRegistry.GetBufferedIDs() handler.logger.Info( "failing query because worker does not support consistent query", tag.ClientImpl(clientImpl), tag.ClientFeatureVersion(clientFeatureVersion), tag.WorkflowDomainName(domain), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.Error(versionErr)) for _, id := range buffered { if err := queryRegistry.SetTerminationState(id, failedTerminationState); err != nil { handler.logger.Error( "failed to set query termination state to failed", tag.WorkflowDomainName(domain), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.QueryID(id), tag.Error(err)) scope.IncCounter(metrics.QueryRegistryInvalidStateCount) } } return } // if its a heartbeat decision it means local activities may still be running on the worker // which were started by an external event which happened before the query if decisionHeartbeating { return } sizeLimitError := handler.config.BlobSizeLimitError(domain) sizeLimitWarn := handler.config.BlobSizeLimitWarn(domain) // Complete or fail all queries we have results for for id, result := range queryResults { if err := common.CheckEventBlobSizeLimit( len(result.GetAnswer()), sizeLimitWarn, sizeLimitError, domainID, domain, workflowID, runID, scope, handler.logger, tag.BlobSizeViolationOperation("ConsistentQuery"), ); err != nil { handler.logger.Info("failing query because query result size is too large", tag.WorkflowDomainName(domain), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.QueryID(id), tag.Error(err)) failedTerminationState := &query.TerminationState{ TerminationType: query.TerminationTypeFailed, Failure: err, } if err := queryRegistry.SetTerminationState(id, failedTerminationState); err != nil { handler.logger.Error( "failed to set query termination state to failed", tag.WorkflowDomainName(domain), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.QueryID(id), tag.Error(err)) scope.IncCounter(metrics.QueryRegistryInvalidStateCount) } } else { completedTerminationState := &query.TerminationState{ TerminationType: query.TerminationTypeCompleted, QueryResult: result, } if err := queryRegistry.SetTerminationState(id, completedTerminationState); err != nil { handler.logger.Error( "failed to set query termination state to completed", tag.WorkflowDomainName(domain), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.QueryID(id), tag.Error(err)) scope.IncCounter(metrics.QueryRegistryInvalidStateCount) } } } // If no decision task was created then it means no buffered events came in during this decision task's handling. // This means all unanswered buffered queries can be dispatched directly through matching at this point. if !createNewDecisionTask { buffered := queryRegistry.GetBufferedIDs() for _, id := range buffered { unblockTerminationState := &query.TerminationState{ TerminationType: query.TerminationTypeUnblocked, } if err := queryRegistry.SetTerminationState(id, unblockTerminationState); err != nil { handler.logger.Error( "failed to set query termination state to unblocked", tag.WorkflowDomainName(domain), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.QueryID(id), tag.Error(err)) scope.IncCounter(metrics.QueryRegistryInvalidStateCount) } } } } func (handler *handlerImpl) failDecisionHelper( ctx context.Context, wfContext execution.Context, scheduleID int64, startedID int64, cause types.DecisionTaskFailedCause, details []byte, request *types.RespondDecisionTaskCompletedRequest, domainEntry *cache.DomainCacheEntry, ) (execution.MutableState, error) { // Clear any updates we have accumulated so far wfContext.Clear() // Reload workflow execution so we can apply the decision task failure event mutableState, err := wfContext.LoadWorkflowExecution(ctx) if err != nil { return nil, err } if _, err = mutableState.AddDecisionTaskFailedEvent( scheduleID, startedID, cause, details, request.GetIdentity(), "", request.GetBinaryChecksum(), "", "", 0, "", ); err != nil { return nil, err } domainName := domainEntry.GetInfo().Name maxAttempts := handler.config.DecisionRetryMaxAttempts(domainName) if maxAttempts > 0 && mutableState.GetExecutionInfo().DecisionAttempt > int64(maxAttempts) { message := fmt.Sprintf( "Decision attempt exceeds limit. Last decision failure cause and details: %v - %v", cause, details) executionInfo := mutableState.GetExecutionInfo() handler.logger.Error(message, tag.WorkflowDomainID(executionInfo.DomainID), tag.WorkflowID(executionInfo.WorkflowID), tag.WorkflowRunID(executionInfo.RunID)) handler.metricsClient.IncCounter(metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionRetriesExceededCounter) if _, err := mutableState.AddWorkflowExecutionTerminatedEvent( mutableState.GetNextEventID(), common.FailureReasonDecisionAttemptsExceedsLimit, []byte(message), execution.IdentityHistoryService, ); err != nil { return nil, err } } // Return new builder back to the caller for further updates return mutableState, nil } func (handler *handlerImpl) getActiveDomainByWorkflow(ctx context.Context, domainID, workflowID, runID string) (*cache.DomainCacheEntry, error) { activeClusterInfo, err := handler.activeClusterManager.GetActiveClusterInfoByWorkflow(ctx, domainID, workflowID, runID) if err != nil { return nil, err } domain, err := handler.domainCache.GetDomainByID(domainID) if err != nil { return nil, err } if activeClusterInfo.ActiveClusterName == handler.shard.GetClusterMetadata().GetCurrentClusterName() { return domain, nil } return nil, domain.NewDomainNotActiveError(handler.shard.GetClusterMetadata().GetCurrentClusterName(), activeClusterInfo.ActiveClusterName) } func getDecisionInfoAttempt(di *execution.DecisionInfo) int64 { if di == nil { return 0 } return di.Attempt } func getDecisionInfoStartedID(di *execution.DecisionInfo) int64 { if di == nil { return 0 } return di.StartedID } ================================================ FILE: service/history/decision/handler_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package decision import ( "context" "errors" "fmt" "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/query" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/workflow" ) const ( testInvalidDomainUUID = "some-invalid-UUID" testShardID = 1234 ) type ( DecisionHandlerSuite struct { *require.Assertions suite.Suite controller *gomock.Controller mockMutableState *execution.MockMutableState decisionHandler *handlerImpl queryRegistry query.Registry } ) func TestDecisionHandlerSuite(t *testing.T) { suite.Run(t, new(DecisionHandlerSuite)) } func (s *DecisionHandlerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.decisionHandler = &handlerImpl{ versionChecker: client.NewVersionChecker(), metricsClient: metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), config: config.NewForTest(), logger: testlogger.New(s.T()), timeSource: clock.NewRealTimeSource(), } s.queryRegistry = s.constructQueryRegistry(10) s.mockMutableState = execution.NewMockMutableState(s.controller) workflowInfo := &persistence.WorkflowExecutionInfo{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } s.mockMutableState.EXPECT().GetExecutionInfo().Return(workflowInfo).AnyTimes() } func (s *DecisionHandlerSuite) TearDownTest() { s.controller.Finish() } func (s *DecisionHandlerSuite) TestNewHandler() { shardContext := shard.NewMockContext(s.controller) tokenSerializer := common.NewMockTaskTokenSerializer(s.controller) shardContext.EXPECT().GetConfig().Times(1).Return(&config.Config{}) shardContext.EXPECT().GetLogger().Times(2).Return(testlogger.New(s.T())) shardContext.EXPECT().GetTimeSource().Times(1) shardContext.EXPECT().GetDomainCache().Times(2) shardContext.EXPECT().GetMetricsClient().Times(2) shardContext.EXPECT().GetThrottledLogger().Times(1).Return(testlogger.New(s.T())) shardContext.EXPECT().GetActiveClusterManager().Times(1).Return(activecluster.NewMockManager(s.controller)) h := NewHandler(shardContext, execution.NewMockCache(s.controller), tokenSerializer) s.NotNil(h) s.Equal("handlerImpl", reflect.ValueOf(h).Elem().Type().Name()) } func TestHandleDecisionTaskScheduled(t *testing.T) { tests := []struct { name string domainID string mutablestate *persistence.WorkflowMutableState isfirstDecision bool expectCalls func(ctrl *gomock.Controller, shardContext *shard.MockContext) expectErr bool }{ { name: "success", domainID: constants.TestDomainID, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{}, }, expectCalls: func(ctrl *gomock.Controller, shardContext *shard.MockContext) { shardContext.EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) }, expectErr: false, }, { name: "completed workflow", domainID: constants.TestDomainID, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ // WorkflowStateCompleted = 2 from persistence WorkflowExecutionInfo.IsRunning() State: 2, }, }, expectCalls: func(ctrl *gomock.Controller, shardContext *shard.MockContext) { shardContext.EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) }, expectErr: true, }, { name: "get start event failure", domainID: constants.TestDomainID, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ // execution has no event yet DecisionScheduleID: -23, LastProcessedEvent: -23, }, }, expectCalls: func(ctrl *gomock.Controller, shardContext *shard.MockContext) { eventsCache := events.NewMockCache(ctrl) shardContext.EXPECT().GetEventsCache().Times(1).Return(eventsCache) eventsCache.EXPECT(). GetEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Times(1). Return(nil, &persistence.TimeoutError{Msg: "failed to get start event: request timeout"}) shardContext.EXPECT().GetShardID().Return(testShardID).Times(1) }, expectErr: true, }, { name: "first decision task scheduled failure", domainID: constants.TestDomainID, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: -23, LastProcessedEvent: -23, }, BufferedEvents: append([]*types.HistoryEvent{}, &types.HistoryEvent{}), }, expectCalls: func(ctrl *gomock.Controller, shardContext *shard.MockContext) { eventsCache := events.NewMockCache(ctrl) shardContext.EXPECT().GetEventsCache().Times(1).Return(eventsCache) eventsCache.EXPECT(). GetEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Times(1). Return(&types.HistoryEvent{}, nil) shardContext.EXPECT().GetShardID().Return(testShardID).Times(1) shardContext.EXPECT().GenerateTaskIDs(gomock.Any()).Times(1).Return([]int64{}, errors.New("some random error to avoid going too deep in call stack unrelated to this unit")) }, expectErr: true, isfirstDecision: true, }, { name: "first decision task scheduled success", domainID: constants.TestDomainID, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: -23, LastProcessedEvent: -23, }, }, expectCalls: func(ctrl *gomock.Controller, shardContext *shard.MockContext) { eventsCache := events.NewMockCache(ctrl) shardContext.EXPECT().GetEventsCache().Times(1).Return(eventsCache) eventsCache.EXPECT(). GetEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Times(1). Return(&types.HistoryEvent{}, nil) shardContext.EXPECT().GetShardID().Return(testShardID).Times(1) shardContext.EXPECT().GenerateTaskIDs(gomock.Any()).Times(1).Return([]int64{}, errors.New("some random error to avoid going too deep in call stack unrelated to this unit")) }, expectErr: true, isfirstDecision: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctrl := gomock.NewController(t) request := &types.ScheduleDecisionTaskRequest{ DomainUUID: test.domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, IsFirstDecision: test.isfirstDecision, } decisionHandler := &handlerImpl{ config: config.NewForTest(), shard: shard.NewMockContext(ctrl), timeSource: clock.NewRealTimeSource(), metricsClient: metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), logger: testlogger.New(t), versionChecker: client.NewVersionChecker(), tokenSerializer: common.NewMockTaskTokenSerializer(ctrl), domainCache: cache.NewMockDomainCache(ctrl), activeClusterManager: activecluster.NewMockManager(ctrl), } expectCommonCalls(decisionHandler, test.domainID) expectDefaultDomainCache(decisionHandler, test.domainID) expectGetWorkflowExecution(decisionHandler, test.domainID, test.mutablestate) if test.expectCalls != nil { test.expectCalls(ctrl, decisionHandler.shard.(*shard.MockContext)) } decisionHandler.executionCache = execution.NewCache(decisionHandler.shard) err := decisionHandler.HandleDecisionTaskScheduled(context.Background(), request) assert.Equal(t, test.expectErr, err != nil) }) } } func TestHandleDecisionTaskFailed(t *testing.T) { taskToken := []byte("test-token") tests := []struct { name string domainID string mutablestate *persistence.WorkflowMutableState expectCalls func(ctrl *gomock.Controller, h *handlerImpl) expectErr bool expectNonDefaultDomainCache bool }{ { name: "failure to deserialize token", domainID: constants.TestDomainID, expectCalls: func(ctrl *gomock.Controller, h *handlerImpl) { h.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(taskToken).Return(nil, errors.New("unable to deserialize task token")) }, expectErr: true, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{}, }, expectNonDefaultDomainCache: true, }, { name: "success", domainID: constants.TestDomainID, expectCalls: func(ctrl *gomock.Controller, h *handlerImpl) { token := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } h.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(taskToken).Return(token, nil) h.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) h.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(gomock.Any()).Return([]int64{0}, nil) h.shard.(*shard.MockContext).EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }).Return(&persistence.AppendHistoryNodesResponse{}, nil) h.shard.(*shard.MockContext).EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil) h.shard.(*shard.MockContext).EXPECT().GetShardID().Return(testShardID) engine := engine.NewMockEngine(ctrl) h.shard.(*shard.MockContext).EXPECT().GetEngine().Times(3).Return(engine) engine.EXPECT().NotifyNewHistoryEvent(gomock.Any()) engine.EXPECT().NotifyNewTransferTasks(gomock.Any()) engine.EXPECT().NotifyNewTimerTasks(gomock.Any()) engine.EXPECT().NotifyNewReplicationTasks(gomock.Any()) }, expectErr: false, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{}, }, }, { name: "completed workflow", domainID: constants.TestDomainID, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ // WorkflowStateCompleted = 2 from persistence WorkflowExecutionInfo.IsRunning() State: 2, }, }, expectCalls: func(ctrl *gomock.Controller, h *handlerImpl) { token := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } h.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(taskToken).Return(token, nil) h.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) }, expectErr: true, }, { name: "decision task not found", domainID: constants.TestDomainID, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: 0, }, }, expectCalls: func(ctrl *gomock.Controller, h *handlerImpl) { token := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 1, } h.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(taskToken).Return(token, nil) h.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) }, expectErr: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctrl := gomock.NewController(t) request := &types.HistoryRespondDecisionTaskFailedRequest{ DomainUUID: test.domainID, FailedRequest: &types.RespondDecisionTaskFailedRequest{ TaskToken: taskToken, Cause: nil, Details: nil, }, } shardContext := shard.NewMockContext(ctrl) decisionHandler := &handlerImpl{ config: config.NewForTest(), shard: shardContext, timeSource: clock.NewRealTimeSource(), metricsClient: metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), logger: testlogger.New(t), versionChecker: client.NewVersionChecker(), tokenSerializer: common.NewMockTaskTokenSerializer(ctrl), domainCache: cache.NewMockDomainCache(ctrl), activeClusterManager: activecluster.NewMockManager(ctrl), } expectCommonCalls(decisionHandler, test.domainID) if !test.expectNonDefaultDomainCache { expectDefaultDomainCache(decisionHandler, test.domainID) } expectGetWorkflowExecution(decisionHandler, test.domainID, test.mutablestate) decisionHandler.executionCache = execution.NewCache(shardContext) if test.expectCalls != nil { test.expectCalls(ctrl, decisionHandler) } err := decisionHandler.HandleDecisionTaskFailed(context.Background(), request) assert.Equal(t, test.expectErr, err != nil) }) } } func TestHandleDecisionTaskStarted(t *testing.T) { testTaskListName := "some-tasklist-name" testWorkflowTypeName := "some-workflow-type-name" tests := []struct { name string domainID string mutablestate *persistence.WorkflowMutableState expectCalls func(ctrl *gomock.Controller, h *handlerImpl) expectErr error assertResponseBody func(t *testing.T, response *types.RecordDecisionTaskStartedResponse) }{ { name: "failure - decision task already started", domainID: constants.TestDomainID, expectCalls: func(ctrl *gomock.Controller, h *handlerImpl) { h.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) }, expectErr: &types.EventAlreadyStartedError{Message: "Decision task already started."}, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{}, }, }, { name: "failure - workflow completed", domainID: constants.TestDomainID, expectCalls: func(ctrl *gomock.Controller, h *handlerImpl) { h.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) }, expectErr: workflow.ErrNotExists, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: 2, // 2 == WorkflowStateCompleted }, }, }, { name: "failure - decision task already completed", domainID: constants.TestDomainID, expectCalls: func(ctrl *gomock.Controller, h *handlerImpl) { h.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) }, expectErr: &types.EntityNotExistsError{Message: "Decision task not found."}, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: 1, NextEventID: 2, }, }, }, { name: "failure - cached mutable state is stale", domainID: constants.TestDomainID, expectCalls: func(ctrl *gomock.Controller, h *handlerImpl) { // handler will attempt reloading mutable state at most 5 times // this test will fail all retries h.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(5).Return(events.NewMockCache(ctrl)) }, expectErr: workflow.ErrMaxAttemptsExceeded, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: 1, }, }, }, { name: "success", domainID: constants.TestDomainID, expectCalls: func(ctrl *gomock.Controller, h *handlerImpl) { h.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) }, expectErr: nil, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ NextEventID: 3, DecisionRequestID: "test-request-id", DecisionAttempt: 1, }, }, assertResponseBody: func(t *testing.T, resp *types.RecordDecisionTaskStartedResponse) { // expect test.mutablestate.ExecutionInfo.DecisionAttempt assert.Equal(t, int64(1), resp.DecisionInfo.ScheduledEvent.DecisionTaskScheduledEventAttributes.Attempt) }, }, { name: "success - decision startedID is empty", domainID: constants.TestDomainID, expectCalls: func(ctrl *gomock.Controller, h *handlerImpl) { h.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) h.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(gomock.Any()).Times(1).Return([]int64{0}, nil) h.shard.(*shard.MockContext).EXPECT(). AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}). Return(&persistence.AppendHistoryNodesResponse{}, nil) h.shard.(*shard.MockContext).EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil) h.shard.(*shard.MockContext).EXPECT().GetShardID().Return(testShardID) engine := engine.NewMockEngine(ctrl) h.shard.(*shard.MockContext).EXPECT().GetEngine().Times(3).Return(engine) engine.EXPECT().NotifyNewHistoryEvent(gomock.Any()) engine.EXPECT().NotifyNewTransferTasks(gomock.Any()) engine.EXPECT().NotifyNewTimerTasks(gomock.Any()) engine.EXPECT().NotifyNewReplicationTasks(gomock.Any()) }, expectErr: nil, mutablestate: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DecisionStartedID: -23, NextEventID: 2, WorkflowTypeName: testWorkflowTypeName, TaskList: testTaskListName, }, }, assertResponseBody: func(t *testing.T, resp *types.RecordDecisionTaskStartedResponse) { assert.Equal(t, testWorkflowTypeName, resp.WorkflowType.Name) assert.Equal(t, testTaskListName, resp.WorkflowExecutionTaskList.Name) assert.Equal(t, int64(0), resp.ScheduledEventID) assert.Equal(t, int64(3), resp.NextEventID) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctrl := gomock.NewController(t) request := &types.RecordDecisionTaskStartedRequest{ DomainUUID: test.domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, RequestID: "test-request-id", PollRequest: &types.PollForDecisionTaskRequest{ Domain: test.domainID, Identity: "test-identity", }, } shardContext := shard.NewMockContext(ctrl) decisionHandler := &handlerImpl{ config: config.NewForTest(), shard: shardContext, timeSource: clock.NewRealTimeSource(), metricsClient: metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), logger: testlogger.New(t), versionChecker: client.NewVersionChecker(), domainCache: cache.NewMockDomainCache(ctrl), activeClusterManager: activecluster.NewMockManager(ctrl), } expectCommonCalls(decisionHandler, test.domainID) expectDefaultDomainCache(decisionHandler, test.domainID) expectGetWorkflowExecution(decisionHandler, test.domainID, test.mutablestate) decisionHandler.executionCache = execution.NewCache(shardContext) if test.expectCalls != nil { test.expectCalls(ctrl, decisionHandler) } resp, err := decisionHandler.HandleDecisionTaskStarted(context.Background(), request) assert.Equal(t, test.expectErr, err) if err == nil { assert.NotNil(t, resp) assert.Equal(t, test.mutablestate.ExecutionInfo.DecisionScheduleID, resp.ScheduledEventID) assert.Equal(t, test.mutablestate.ExecutionInfo.DecisionStartedID, resp.StartedEventID) assert.Equal(t, test.mutablestate.ExecutionInfo.NextEventID, resp.NextEventID) assert.Equal(t, test.mutablestate.ExecutionInfo.TaskList, resp.WorkflowExecutionTaskList.Name) test.assertResponseBody(t, resp) } }) } } func TestHandleDecisionTaskCompleted(t *testing.T) { serializedTestToken := []byte("test-token") testTaskListName := "some-tasklist-name" testWorkflowTypeName := "some-workflow-type-name" tests := []struct { name string domainID string expectedErr error expectMockCalls func(ctrl *gomock.Controller, decisionHandler *handlerImpl) assertResponseBody func(t *testing.T, resp *types.HistoryRespondDecisionTaskCompletedResponse) mutableState *persistence.WorkflowMutableState request *types.HistoryRespondDecisionTaskCompletedRequest expectGetWorkflowExecution bool expectNonDefaultDomainCache bool }{ { name: "token deserialazation failure", domainID: constants.TestDomainID, expectedErr: workflow.ErrDeserializingToken, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(nil, errors.New("unable to deserialize task token")) }, expectNonDefaultDomainCache: true, expectGetWorkflowExecution: true, }, { name: "get or create wf execution failure", domainID: constants.TestDomainID, expectedErr: &types.BadRequestError{Message: "Can't load workflow execution. WorkflowId not set."}, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { taskToken := &common.TaskToken{ DomainID: constants.TestDomainID, // empty workflow ID to force decisionHandler.executionCache.GetOrCreateWorkflowExecution() failure } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(taskToken, nil) }, }, { name: "success", domainID: constants.TestDomainID, expectedErr: nil, expectGetWorkflowExecution: true, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 0, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Serialize(&common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, WorkflowType: testWorkflowTypeName, RunID: constants.TestRunID, ScheduleID: 1, ActivityID: "some-activity-id", ActivityType: "some-activity-name", }).Return(serializedTestToken, nil) eventsCache := events.NewMockCache(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(eventsCache) eventsCache.EXPECT().PutEvent(constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID, int64(1), gomock.Any()) decisionHandler.shard.(*shard.MockContext).EXPECT().GetShardID().Times(1).Return(testShardID) decisionHandler.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(4).Return([]int64{0, 1, 2, 3}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(6).Return([]int64{0, 1, 2, 3, 4, 5}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }).Return(&persistence.AppendHistoryNodesResponse{}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.UpdateWorkflowExecutionResponse{}, nil) engine := engine.NewMockEngine(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEngine().Return(engine).Times(3) engine.EXPECT().NotifyNewHistoryEvent(events.NewNotification(constants.TestDomainID, &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, 0, 5, 0, 1, 0, nil)) engine.EXPECT().NotifyNewTransferTasks(gomock.Any()) engine.EXPECT().NotifyNewTimerTasks(gomock.Any()) engine.EXPECT().NotifyNewReplicationTasks(gomock.Any()) decisionHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Times(1).Return(constants.TestLocalDomainEntry, nil) decisionHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainID(constants.TestDomainName).Times(1).Return(constants.TestDomainID, nil) }, mutableState: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ WorkflowTimeout: 600, AutoResetPoints: &types.ResetPoints{ Points: func() []*types.ResetPointInfo { if historyMaxResetPoints, ok := dynamicproperties.IntKeys[dynamicproperties.HistoryMaxAutoResetPoints]; ok { return make([]*types.ResetPointInfo, historyMaxResetPoints.DefaultValue) } return []*types.ResetPointInfo{} }(), }, WorkflowTypeName: testWorkflowTypeName, TaskList: testTaskListName, }, Checksum: checksum.Checksum{}, BufferedEvents: append([]*types.HistoryEvent{}, &types.HistoryEvent{}), ActivityInfos: make(map[int64]*persistence.ActivityInfo), }, assertResponseBody: func(t *testing.T, resp *types.HistoryRespondDecisionTaskCompletedResponse) { assert.True(t, resp.StartedResponse.StickyExecutionEnabled) assert.Equal(t, 1, len(resp.ActivitiesToDispatchLocally)) assert.Equal(t, testWorkflowTypeName, resp.StartedResponse.WorkflowType.Name) assert.Equal(t, int64(0), resp.StartedResponse.Attempt) assert.Equal(t, testTaskListName, resp.StartedResponse.WorkflowExecutionTaskList.Name) }, }, { name: "decision task failure", domainID: constants.TestDomainID, expectedErr: &types.InternalServiceError{Message: "add-decisiontask-failed-event operation failed"}, expectGetWorkflowExecution: true, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) eventsCache := events.NewMockCache(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(2).Return(eventsCache) decisionHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Times(1).Return(constants.TestLocalDomainEntry, nil) }, }, { name: "workflow completed", domainID: constants.TestDomainID, expectedErr: workflow.ErrAlreadyCompleted, expectGetWorkflowExecution: true, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) eventsCache := events.NewMockCache(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(eventsCache) }, mutableState: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ State: 2, }, }, }, { name: "decision task not found", domainID: constants.TestDomainID, expectedErr: &types.EntityNotExistsError{Message: "Decision task not found."}, expectGetWorkflowExecution: true, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleAttempt: 1, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) eventsCache := events.NewMockCache(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(eventsCache) }, }, { name: "decision heartbeat time out", domainID: constants.TestDomainID, expectedErr: &types.EntityNotExistsError{Message: "decision heartbeat timeout"}, expectGetWorkflowExecution: true, request: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: serializedTestToken, Decisions: []*types.Decision{}, ReturnNewDecisionTask: true, ForceCreateNewDecisionTask: true, StickyAttributes: &types.StickyExecutionAttributes{ WorkerTaskList: &types.TaskList{Name: testTaskListName}, ScheduleToStartTimeoutSeconds: func(i int32) *int32 { return &i }(10), }, }, }, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 0, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) eventsCache := events.NewMockCache(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(eventsCache) decisionHandler.shard.(*shard.MockContext).EXPECT().GetShardID().Times(1).Return(testShardID) decisionHandler.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(1).Return([]int64{0}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }).Return(&persistence.AppendHistoryNodesResponse{}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.UpdateWorkflowExecutionResponse{}, nil) engine := engine.NewMockEngine(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEngine().Return(engine).Times(3) engine.EXPECT().NotifyNewHistoryEvent(events.NewNotification(constants.TestDomainID, &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, 0, 1, 0, 1, 0, nil)) engine.EXPECT().NotifyNewTransferTasks(gomock.Any()) engine.EXPECT().NotifyNewTimerTasks(gomock.Any()) engine.EXPECT().NotifyNewReplicationTasks(gomock.Any()) }, mutableState: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DecisionOriginalScheduledTimestamp: 1, }, }, }, { name: "update continueAsNew info failure - execution size limit exceeded", domainID: constants.TestDomainID, expectedErr: execution.ErrWorkflowFinished, expectGetWorkflowExecution: true, request: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: serializedTestToken, Decisions: []*types.Decision{{ DecisionType: common.Ptr(types.DecisionTypeContinueAsNewWorkflowExecution), ContinueAsNewWorkflowExecutionDecisionAttributes: &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: &types.WorkflowType{Name: testWorkflowTypeName}, TaskList: &types.TaskList{Name: testTaskListName}, }, }}, ReturnNewDecisionTask: true, }, }, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 0, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) eventsCache := events.NewMockCache(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(3).Return(eventsCache) eventsCache.EXPECT().GetEvent(context.Background(), testShardID, constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID, commonconstants.FirstEventID, commonconstants.FirstEventID, nil).Return(&types.HistoryEvent{}, nil) eventsCache.EXPECT().PutEvent(constants.TestDomainID, constants.TestWorkflowID, gomock.Any(), int64(1), gomock.Any()).Times(2) decisionHandler.shard.(*shard.MockContext).EXPECT().GetShardID().Times(1).Return(testShardID) decisionHandler.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(2).Times(1).Return([]int64{0, 1}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, gomock.Any()).Return(nil, &persistence.TransactionSizeLimitError{Msg: fmt.Sprintf("transaction size exceeds limit")}) decisionHandler.shard.(*shard.MockContext).EXPECT().GetExecutionManager().Times(1) decisionHandler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil) }, mutableState: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DecisionOriginalScheduledTimestamp: 1, WorkflowTimeout: 100, }, }, }, { name: "update continueAsNew info failure - conflict error", domainID: constants.TestDomainID, expectedErr: workflow.ErrAlreadyCompleted, expectGetWorkflowExecution: true, request: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: serializedTestToken, Decisions: []*types.Decision{{ DecisionType: common.Ptr(types.DecisionTypeCompleteWorkflowExecution), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{Result: []byte{}}, }}, }, }, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) eventsCache := events.NewMockCache(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(3).Return(eventsCache) eventsCache.EXPECT().GetEvent(context.Background(), testShardID, constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID, commonconstants.FirstEventID, commonconstants.FirstEventID, nil). Return(&types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, nil).Times(3) eventsCache.EXPECT().PutEvent(constants.TestDomainID, constants.TestWorkflowID, gomock.Any(), int64(1), gomock.Any()).Times(2) decisionHandler.shard.(*shard.MockContext).EXPECT().GetShardID().Times(3).Return(testShardID) decisionHandler.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(2).Times(1).Return([]int64{0, 1}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, gomock.Any()).Return(nil, execution.NewConflictError(new(testing.T), errors.New("some random conflict error"))) decisionHandler.shard.(*shard.MockContext).EXPECT().GetExecutionManager().Times(1) decisionHandler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil) }, mutableState: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CronSchedule: "0 1 * * 1", // some random cron schedule }, }, }, { name: "update continueAsNew info failure - load execution", domainID: constants.TestDomainID, expectedErr: errors.New("some error occurred when loading workflow execution"), request: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: serializedTestToken, Decisions: []*types.Decision{{ DecisionType: common.Ptr(types.DecisionTypeFailWorkflowExecution), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ Reason: func(reason string) *string { return &reason }("some reason to fail workflow execution"), }, }}, }, }, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) eventsCache := events.NewMockCache(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(2).Return(eventsCache) eventsCache.EXPECT().GetEvent(context.Background(), testShardID, constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID, commonconstants.FirstEventID, commonconstants.FirstEventID, nil). Return(&types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, nil).Times(3) eventsCache.EXPECT().PutEvent(constants.TestDomainID, constants.TestWorkflowID, gomock.Any(), int64(1), gomock.Any()).Times(2) decisionHandler.shard.(*shard.MockContext).EXPECT().GetShardID().Times(3).Return(testShardID) decisionHandler.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(2).Times(1).Return([]int64{0, 1}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, gomock.Any()).Return(nil, &persistence.TransactionSizeLimitError{Msg: fmt.Sprintf("transaction size exceeds limit")}) decisionHandler.shard.(*shard.MockContext).EXPECT().GetExecutionManager().Times(1) decisionHandler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil) firstGetWfExecutionCall := decisionHandler.shard.(*shard.MockContext).EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()). Return(&persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, CronSchedule: "0 1 * * 1", // some random cron schedule }, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, }, nil) decisionHandler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil).AnyTimes() lastGetWfExecutionCall := decisionHandler.shard.(*shard.MockContext).EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, errors.New("some error occurred when loading workflow execution")) gomock.InOrder(firstGetWfExecutionCall, lastGetWfExecutionCall) }, }, { name: "update continueAsNew info failure - update execution", domainID: constants.TestDomainID, expectedErr: errors.New("some error updating workflow execution"), request: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: serializedTestToken, Decisions: []*types.Decision{{ DecisionType: common.Ptr(types.DecisionTypeFailWorkflowExecution), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ Reason: func(reason string) *string { return &reason }("some reason to fail workflow execution"), }, }}, }, }, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) eventsCache := events.NewMockCache(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(3).Return(eventsCache) eventsCache.EXPECT().GetEvent(context.Background(), testShardID, constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID, commonconstants.FirstEventID, commonconstants.FirstEventID, nil). Return(&types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, nil).Times(3) eventsCache.EXPECT().PutEvent(constants.TestDomainID, constants.TestWorkflowID, gomock.Any(), int64(1), gomock.Any()).Times(3) decisionHandler.shard.(*shard.MockContext).EXPECT().GetShardID().Times(3).Return(testShardID) decisionHandler.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(2).Times(2).Return([]int64{0, 1}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(1).Times(1).Return([]int64{0}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, gomock.Any()).Return(nil, &persistence.TransactionSizeLimitError{Msg: fmt.Sprintf("transaction size exceeds limit")}) decisionHandler.shard.(*shard.MockContext).EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, gomock.Any()).Return(&persistence.AppendHistoryNodesResponse{}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().GetExecutionManager().Times(1) decisionHandler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx interface{}, request interface{}) (*persistence.GetWorkflowExecutionResponse, error) { return &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, CronSchedule: "0 1 * * 1", // some random cron schedule }, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, }, nil }).Times(2) decisionHandler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil).AnyTimes() decisionHandler.shard.(*shard.MockContext).EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, errors.New("some error updating workflow execution")) engine := engine.NewMockEngine(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEngine().Return(engine).Times(2) engine.EXPECT().NotifyNewTransferTasks(gomock.Any()) engine.EXPECT().NotifyNewTimerTasks(gomock.Any()) engine.EXPECT().NotifyNewReplicationTasks(gomock.Any()) }, }, { name: "bad binaries", domainID: constants.TestDomainID, expectedErr: errors.New("some error updating continue as new info"), expectNonDefaultDomainCache: true, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { decisionHandler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: constants.TestClusterMetadata.GetCurrentClusterName()}, nil) deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) eventsCache := events.NewMockCache(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(3).Return(eventsCache) eventsCache.EXPECT().PutEvent(constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID, int64(0), gomock.Any()) decisionHandler.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(1).Return([]int64{0}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, gomock.Any()).Return(nil, errors.New("some error updating continue as new info")) domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName}, &persistence.DomainConfig{ Retention: 1, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{"test-binary-checksum": {Reason: "some reason"}}}, }, cluster.TestCurrentClusterName) decisionHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID).AnyTimes().Return(domainEntry, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()). Return(&persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, DecisionScheduleID: 1}, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, }, nil).Times(1) decisionHandler.shard.(*shard.MockContext).EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()). Return(&persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, }, nil).Times(1) decisionHandler.shard.(*shard.MockContext).EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()). Return(&persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, DecisionAttempt: 2}, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, }, nil).Times(1) decisionHandler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil).AnyTimes() }, request: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: serializedTestToken, BinaryChecksum: "test-binary-checksum", ForceCreateNewDecisionTask: true, ReturnNewDecisionTask: true, }, }, }, { name: "failure to load workflow execution - shard closed", domainID: constants.TestDomainID, expectedErr: errors.New("some random error"), expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Times(1).Return(nil, errors.New("some random error")) }, }, { name: "failure to load workflow execution stats", domainID: constants.TestDomainID, expectedErr: errors.New("some random error"), expectNonDefaultDomainCache: true, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { decisionHandler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: constants.TestClusterMetadata.GetCurrentClusterName()}, nil) deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) decisionHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID).Times(2).Return(constants.TestLocalDomainEntry, nil) decisionHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID).Times(1).Return(nil, errors.New("some random error")) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) expectGetWorkflowExecution(decisionHandler, constants.TestDomainID, nil) }, }, { name: "task handler fail decisions", domainID: constants.TestDomainID, expectedErr: &types.InternalServiceError{Message: "unable to change workflow state from 0 to 2, close status 3"}, expectGetWorkflowExecution: true, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 0, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) }, request: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: serializedTestToken, Decisions: []*types.Decision{{ DecisionType: common.Ptr(types.DecisionTypeCancelWorkflowExecution), CancelWorkflowExecutionDecisionAttributes: &types.CancelWorkflowExecutionDecisionAttributes{Details: []byte{}}, }}, }, }, }, { name: "Update_History_Loop max attempt exceeded", domainID: constants.TestDomainID, expectedErr: workflow.ErrMaxAttemptsExceeded, expectGetWorkflowExecution: true, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 2, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(5).Return(events.NewMockCache(ctrl)) }, }, { name: "success with decision heartbeat", domainID: constants.TestDomainID, expectedErr: nil, expectGetWorkflowExecution: true, request: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: serializedTestToken, Decisions: []*types.Decision{}, ReturnNewDecisionTask: true, ForceCreateNewDecisionTask: true, StickyAttributes: &types.StickyExecutionAttributes{ WorkerTaskList: &types.TaskList{Name: testTaskListName}, }, }, }, expectMockCalls: func(ctrl *gomock.Controller, decisionHandler *handlerImpl) { deserializedTestToken := &common.TaskToken{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, WorkflowType: testWorkflowTypeName, } decisionHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Deserialize(serializedTestToken).Return(deserializedTestToken, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEventsCache().Times(1).Return(events.NewMockCache(ctrl)) decisionHandler.shard.(*shard.MockContext).EXPECT().GetShardID().Times(1).Return(testShardID) decisionHandler.shard.(*shard.MockContext).EXPECT().GenerateTaskIDs(3).Return([]int64{0, 1, 2}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), constants.TestDomainID, types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }).Return(&persistence.AppendHistoryNodesResponse{}, nil) decisionHandler.shard.(*shard.MockContext).EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.UpdateWorkflowExecutionResponse{}, nil) engine := engine.NewMockEngine(ctrl) decisionHandler.shard.(*shard.MockContext).EXPECT().GetEngine().Return(engine).Times(3) engine.EXPECT().NotifyNewHistoryEvent(events.NewNotification(constants.TestDomainID, &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, 0, 3, 0, 1, 0, nil)) engine.EXPECT().NotifyNewTransferTasks(gomock.Any()) engine.EXPECT().NotifyNewTimerTasks(gomock.Any()) engine.EXPECT().NotifyNewReplicationTasks(gomock.Any()) }, assertResponseBody: func(t *testing.T, resp *types.HistoryRespondDecisionTaskCompletedResponse) { assert.True(t, resp.StartedResponse.StickyExecutionEnabled) assert.Equal(t, testWorkflowTypeName, resp.StartedResponse.WorkflowType.Name) assert.Equal(t, int64(0), resp.StartedResponse.Attempt) assert.Equal(t, testTaskListName, resp.StartedResponse.WorkflowExecutionTaskList.Name) }, mutableState: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ WorkflowTypeName: testWorkflowTypeName, TaskList: testTaskListName, }, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctrl := gomock.NewController(t) shard := shard.NewMockContext(ctrl) domainCache := cache.NewMockDomainCache(ctrl) handlerConfig := config.NewForTest() handlerConfig.MaxActivityCountDispatchByDomain = func(domain string) int { return 1 } // some value > 0 handlerConfig.EnableActivityLocalDispatchByDomain = func(domain string) bool { return true } handlerConfig.DecisionRetryMaxAttempts = func(domain string) int { return 1 } decisionHandler := &handlerImpl{ config: handlerConfig, shard: shard, timeSource: clock.NewMockedTimeSource(), domainCache: domainCache, metricsClient: metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), logger: testlogger.New(t), versionChecker: client.NewVersionChecker(), tokenSerializer: common.NewMockTaskTokenSerializer(ctrl), attrValidator: newAttrValidator(domainCache, metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), config.NewForTest(), testlogger.New(t)), activeClusterManager: activecluster.NewMockManager(ctrl), } expectCommonCalls(decisionHandler, test.domainID) if !test.expectNonDefaultDomainCache { expectDefaultDomainCache(decisionHandler, test.domainID) } if test.expectGetWorkflowExecution { expectGetWorkflowExecution(decisionHandler, test.domainID, test.mutableState) } decisionHandler.executionCache = execution.NewCache(shard) request := &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: test.domainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: serializedTestToken, Decisions: []*types.Decision{{ DecisionType: nil, ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "some-activity-id", ActivityType: &types.ActivityType{Name: "some-activity-name"}, Domain: constants.TestDomainName, TaskList: &types.TaskList{Name: testTaskListName}, ScheduleToCloseTimeoutSeconds: func(i int32) *int32 { return &i }(200), ScheduleToStartTimeoutSeconds: func(i int32) *int32 { return &i }(100), StartToCloseTimeoutSeconds: func(i int32) *int32 { return &i }(100), RequestLocalDispatch: true, }, }}, ReturnNewDecisionTask: true, }, } if test.expectMockCalls != nil { test.expectMockCalls(ctrl, decisionHandler) } if test.request != nil { request = test.request } resp, err := decisionHandler.HandleDecisionTaskCompleted(context.Background(), request) assert.Equal(t, test.expectedErr, err) if err != nil { assert.Nil(t, resp) } else { test.assertResponseBody(t, resp) } }) } } func (s *DecisionHandlerSuite) TestCreateRecordDecisionTaskStartedResponse() { tests := []struct { name string expectCalls func() expectedErr error indexes []string }{ { name: "success", expectCalls: func() { s.mockMutableState.EXPECT().GetWorkflowType().Return(&types.WorkflowType{}) s.mockMutableState.EXPECT().GetNextEventID().Return(int64(1)) s.mockMutableState.EXPECT().CreateTransientDecisionEvents(gomock.Any(), "test-identity").Return(&types.HistoryEvent{}, &types.HistoryEvent{}) s.mockMutableState.EXPECT().GetCurrentBranchToken().Return([]byte{}, nil) registry := query.NewMockRegistry(s.controller) s.mockMutableState.EXPECT().GetQueryRegistry().Return(registry) registry.EXPECT().GetBufferedIDs().Return([]string{"test-id", "test-id1", "test-id2"}) registry.EXPECT().GetQueryInput(gomock.Any()).Return(&types.WorkflowQuery{}, nil).Times(2) registry.EXPECT().GetQueryInput(gomock.Any()).Return(nil, &types.InternalServiceError{Message: "query does not exist"}) s.mockMutableState.EXPECT().GetHistorySize() }, expectedErr: nil, indexes: []string{"test-id", "test-id1"}, }, { name: "failure", expectCalls: func() { s.mockMutableState.EXPECT().GetWorkflowType().Return(&types.WorkflowType{}) s.mockMutableState.EXPECT().GetNextEventID().Return(int64(1)) s.mockMutableState.EXPECT().CreateTransientDecisionEvents(gomock.Any(), "test-identity").Return(&types.HistoryEvent{}, &types.HistoryEvent{}) s.mockMutableState.EXPECT().GetCurrentBranchToken().Return([]byte{}, &types.BadRequestError{Message: fmt.Sprintf("getting branch index: %d, available branch count: %d", 0, 0)}) }, expectedErr: &types.BadRequestError{Message: fmt.Sprintf("getting branch index: %d, available branch count: %d", 0, 0)}, }, } for _, test := range tests { s.Run(test.name, func() { decision := &execution.DecisionInfo{ ScheduleID: 1, StartedID: 2, RequestID: constants.TestRequestID, TaskList: "test-tasklist", Attempt: 1, } test.expectCalls() resp, err := s.decisionHandler.createRecordDecisionTaskStartedResponse(constants.TestDomainID, s.mockMutableState, decision, "test-identity") s.Equal(test.expectedErr, err) if err != nil { s.Nil(resp) } else { s.Equal(&types.HistoryEvent{}, resp.DecisionInfo.ScheduledEvent) s.Equal(&types.HistoryEvent{}, resp.DecisionInfo.StartedEvent) s.Equal([]byte{}, resp.BranchToken) for _, index := range test.indexes { _, ok := resp.Queries[index] s.True(ok) } } }) } } func (s *DecisionHandlerSuite) TestHandleBufferedQueries_ClientNotSupports() { s.mockMutableState.EXPECT().GetQueryRegistry().Return(s.queryRegistry) s.assertQueryCounts(s.queryRegistry, 10, 0, 0, 0) s.decisionHandler.handleBufferedQueries(s.mockMutableState, client.GoSDK, "0.0.0", nil, false, constants.TestGlobalDomainEntry, false) s.assertQueryCounts(s.queryRegistry, 0, 0, 0, 10) } func (s *DecisionHandlerSuite) TestHandleBufferedQueries_HeartbeatDecision() { s.mockMutableState.EXPECT().GetQueryRegistry().Return(s.queryRegistry) s.assertQueryCounts(s.queryRegistry, 10, 0, 0, 0) queryResults := s.constructQueryResults(s.queryRegistry.GetBufferedIDs()[0:5], 10) s.decisionHandler.handleBufferedQueries(s.mockMutableState, client.GoSDK, client.GoWorkerConsistentQueryVersion, queryResults, false, constants.TestGlobalDomainEntry, true) s.assertQueryCounts(s.queryRegistry, 10, 0, 0, 0) } func (s *DecisionHandlerSuite) TestHandleBufferedQueries_NewDecisionTask() { s.mockMutableState.EXPECT().GetQueryRegistry().Return(s.queryRegistry) s.assertQueryCounts(s.queryRegistry, 10, 0, 0, 0) queryResults := s.constructQueryResults(s.queryRegistry.GetBufferedIDs()[0:5], 10) s.decisionHandler.handleBufferedQueries(s.mockMutableState, client.GoSDK, client.GoWorkerConsistentQueryVersion, queryResults, true, constants.TestGlobalDomainEntry, false) s.assertQueryCounts(s.queryRegistry, 5, 5, 0, 0) } func (s *DecisionHandlerSuite) TestHandleBufferedQueries_NoNewDecisionTask() { s.mockMutableState.EXPECT().GetQueryRegistry().Return(s.queryRegistry) s.assertQueryCounts(s.queryRegistry, 10, 0, 0, 0) queryResults := s.constructQueryResults(s.queryRegistry.GetBufferedIDs()[0:5], 10) s.decisionHandler.handleBufferedQueries(s.mockMutableState, client.GoSDK, client.GoWorkerConsistentQueryVersion, queryResults, false, constants.TestGlobalDomainEntry, false) s.assertQueryCounts(s.queryRegistry, 0, 5, 5, 0) } func (s *DecisionHandlerSuite) TestHandleBufferedQueries_QueryTooLarge() { s.mockMutableState.EXPECT().GetQueryRegistry().Return(s.queryRegistry) s.assertQueryCounts(s.queryRegistry, 10, 0, 0, 0) bufferedIDs := s.queryRegistry.GetBufferedIDs() queryResults := s.constructQueryResults(bufferedIDs[0:5], 10) largeQueryResults := s.constructQueryResults(bufferedIDs[5:10], 10*1024*1024) for k, v := range largeQueryResults { queryResults[k] = v } s.decisionHandler.handleBufferedQueries(s.mockMutableState, client.GoSDK, client.GoWorkerConsistentQueryVersion, queryResults, false, constants.TestGlobalDomainEntry, false) s.assertQueryCounts(s.queryRegistry, 0, 5, 0, 5) } func (s *DecisionHandlerSuite) TestHandleBufferedQueries_QueryRegistryFailures() { tests := []struct { name string expectMockCalls func() assertCalls func(logs *observer.ObservedLogs) clientFeatureVersion string queryResults map[string]*types.WorkflowQueryResult }{ { name: "no buffered queries", expectMockCalls: func() { queryRegistry := query.NewMockRegistry(s.controller) s.mockMutableState.EXPECT().GetQueryRegistry().Return(queryRegistry) queryRegistry.EXPECT().HasBufferedQuery().Return(false) }, assertCalls: func(logs *observer.ObservedLogs) {}, }, { name: "set query termination state failed - client unsupported", expectMockCalls: func() { queryRegistry := query.NewMockRegistry(s.controller) s.mockMutableState.EXPECT().GetQueryRegistry().Return(queryRegistry) queryRegistry.EXPECT().HasBufferedQuery().Return(true) queryRegistry.EXPECT().GetBufferedIDs().Return([]string{"some-buffered-id"}) queryRegistry.EXPECT().SetTerminationState("some-buffered-id", gomock.Any()).Return(&types.InternalServiceError{Message: "query does not exist"}) }, assertCalls: func(logs *observer.ObservedLogs) { s.Equal(1, logs.FilterMessage("failed to set query termination state to failed").Len()) }, clientFeatureVersion: "0.0.0", }, { name: "set query termination state failed - query too large", expectMockCalls: func() { queryRegistry := query.NewMockRegistry(s.controller) s.mockMutableState.EXPECT().GetQueryRegistry().Return(queryRegistry) queryRegistry.EXPECT().HasBufferedQuery().Return(true) queryRegistry.EXPECT().GetBufferedIDs().Return([]string{"some-id"}) queryRegistry.EXPECT().SetTerminationState("some-id", gomock.Any()).Return(&types.InternalServiceError{Message: "query already in terminal state"}).Times(2) }, queryResults: s.constructQueryResults([]string{"some-id"}, 10*1024*1024), clientFeatureVersion: client.GoWorkerConsistentQueryVersion, assertCalls: func(logs *observer.ObservedLogs) { s.Equal(1, logs.FilterMessage("failed to set query termination state to failed").Len()) s.Equal(1, logs.FilterMessage("failed to set query termination state to unblocked").Len()) }, }, { name: "set query termination state unblocked", expectMockCalls: func() { queryRegistry := query.NewMockRegistry(s.controller) s.mockMutableState.EXPECT().GetQueryRegistry().Return(queryRegistry) queryRegistry.EXPECT().HasBufferedQuery().Return(true) queryRegistry.EXPECT().GetBufferedIDs().Return([]string{"some-buffered-id"}) queryRegistry.EXPECT().SetTerminationState("some-id", gomock.Any()).Return(&types.InternalServiceError{Message: "query does not exist"}) queryRegistry.EXPECT().SetTerminationState("some-buffered-id", gomock.Any()).Return(&types.InternalServiceError{Message: "query already in terminal state"}) }, clientFeatureVersion: client.GoWorkerConsistentQueryVersion, queryResults: map[string]*types.WorkflowQueryResult{"some-id": &types.WorkflowQueryResult{}}, assertCalls: func(logs *observer.ObservedLogs) { s.Equal(1, logs.FilterMessage("failed to set query termination state to completed").Len()) s.Equal(1, logs.FilterMessage("failed to set query termination state to unblocked").Len()) }, }, } for _, test := range tests { s.Run(test.name, func() { core, observedLogs := observer.New(zap.ErrorLevel) logger := zap.New(core) s.decisionHandler.logger = log.NewLogger(logger, log.WithSampleFunc(func(int) bool { return true })) test.expectMockCalls() s.decisionHandler.handleBufferedQueries(s.mockMutableState, client.GoSDK, test.clientFeatureVersion, test.queryResults, false, constants.TestGlobalDomainEntry, false) test.assertCalls(observedLogs) }) } } func (s *DecisionHandlerSuite) constructQueryResults(ids []string, resultSize int) map[string]*types.WorkflowQueryResult { results := make(map[string]*types.WorkflowQueryResult) for _, id := range ids { results[id] = &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeAnswered.Ptr(), Answer: make([]byte, resultSize), } } return results } func (s *DecisionHandlerSuite) constructQueryRegistry(numQueries int) query.Registry { queryRegistry := query.NewRegistry() for i := 0; i < numQueries; i++ { queryRegistry.BufferQuery(&types.WorkflowQuery{}) } return queryRegistry } func (s *DecisionHandlerSuite) assertQueryCounts(queryRegistry query.Registry, buffered, completed, unblocked, failed int) { s.Len(queryRegistry.GetBufferedIDs(), buffered) s.Len(queryRegistry.GetCompletedIDs(), completed) s.Len(queryRegistry.GetUnblockedIDs(), unblocked) s.Len(queryRegistry.GetFailedIDs(), failed) } func expectCommonCalls(handler *handlerImpl, domainID string) { handler.shard.(*shard.MockContext).EXPECT().GetConfig().AnyTimes().Return(handler.config) handler.shard.(*shard.MockContext).EXPECT().GetLogger().AnyTimes().Return(handler.logger) handler.shard.(*shard.MockContext).EXPECT().GetTimeSource().AnyTimes().Return(handler.timeSource) handler.shard.(*shard.MockContext).EXPECT().GetDomainCache().AnyTimes().Return(handler.domainCache) handler.shard.(*shard.MockContext).EXPECT().GetClusterMetadata().AnyTimes().Return(constants.TestClusterMetadata) handler.shard.(*shard.MockContext).EXPECT().GetMetricsClient().AnyTimes().Return(handler.metricsClient) handler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(domainID).AnyTimes().Return(constants.TestDomainName, nil) handler.shard.(*shard.MockContext).EXPECT().GetExecutionManager().Times(1) handler.shard.(*shard.MockContext).EXPECT().GetShardID().Return(testShardID).Times(1) handler.shard.(*shard.MockContext).EXPECT().GetActiveClusterManager().Return(handler.activeClusterManager).AnyTimes() } func expectGetWorkflowExecution(handler *handlerImpl, domainID string, state *persistence.WorkflowMutableState) { workflowExecutionResponse := &persistence.GetWorkflowExecutionResponse{ State: state, MutableStateStats: &persistence.MutableStateStats{}, } if state == nil { workflowExecutionResponse.State = &persistence.WorkflowMutableState{ExecutionInfo: &persistence.WorkflowExecutionInfo{}} } workflowExecutionResponse.State.ExecutionStats = &persistence.ExecutionStats{} workflowExecutionResponse.State.ExecutionInfo.DomainID = domainID workflowExecutionResponse.State.ExecutionInfo.WorkflowID = constants.TestWorkflowID workflowExecutionResponse.State.ExecutionInfo.RunID = constants.TestRunID handler.shard.(*shard.MockContext).EXPECT().GetWorkflowExecution(gomock.Any(), &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, DomainName: constants.TestDomainName, Execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, }).AnyTimes().Return(workflowExecutionResponse, nil) handler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), domainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil).AnyTimes() } func expectDefaultDomainCache(handler *handlerImpl, domainID string) { handler.activeClusterManager.(*activecluster.MockManager).EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), domainID, gomock.Any(), gomock.Any()).Return(&types.ActiveClusterInfo{ActiveClusterName: constants.TestClusterMetadata.GetCurrentClusterName()}, nil) handler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainByID(domainID).AnyTimes().Return(constants.TestLocalDomainEntry, nil) } ================================================ FILE: service/history/decision/task_handler.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package decision import ( "context" "fmt" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) const ( activityCancellationMsgActivityIDUnknown = "ACTIVITY_ID_UNKNOWN" activityCancellationMsgActivityNotStarted = "ACTIVITY_ID_NOT_STARTED" ) type ( attrValidationFn func() error taskHandlerImpl struct { identity string decisionTaskCompletedID int64 domainEntry *cache.DomainCacheEntry // internal state hasUnhandledEventsBeforeDecisions bool failDecision bool failDecisionCause *types.DecisionTaskFailedCause failMessage *string activityNotStartedCancelled bool continueAsNewBuilder execution.MutableState stopProcessing bool // should stop processing any more decisions mutableState execution.MutableState // validation attrValidator *attrValidator sizeLimitChecker *workflowSizeChecker tokenSerializer common.TaskTokenSerializer logger log.Logger domainCache cache.DomainCache metricsClient metrics.Client config *config.Config activityCountToDispatch int } decisionResult struct { activityDispatchInfo *types.ActivityLocalDispatchInfo } ) func newDecisionTaskHandler( identity string, decisionTaskCompletedID int64, domainEntry *cache.DomainCacheEntry, mutableState execution.MutableState, attrValidator *attrValidator, sizeLimitChecker *workflowSizeChecker, tokenSerializer common.TaskTokenSerializer, logger log.Logger, domainCache cache.DomainCache, metricsClient metrics.Client, config *config.Config, ) *taskHandlerImpl { return &taskHandlerImpl{ identity: identity, decisionTaskCompletedID: decisionTaskCompletedID, domainEntry: domainEntry, // internal state hasUnhandledEventsBeforeDecisions: mutableState.HasBufferedEvents(), failDecision: false, failDecisionCause: nil, failMessage: nil, activityNotStartedCancelled: false, continueAsNewBuilder: nil, stopProcessing: false, mutableState: mutableState, // validation attrValidator: attrValidator, sizeLimitChecker: sizeLimitChecker, tokenSerializer: tokenSerializer, logger: logger, domainCache: domainCache, metricsClient: metricsClient, config: config, activityCountToDispatch: config.MaxActivityCountDispatchByDomain(domainEntry.GetInfo().Name), } } func (handler *taskHandlerImpl) handleDecisions( ctx context.Context, executionContext []byte, decisions []*types.Decision, ) ([]*decisionResult, error) { // overall workflow size / count check failWorkflow, err := handler.sizeLimitChecker.failWorkflowSizeExceedsLimit() if err != nil || failWorkflow { return nil, err } var results []*decisionResult for _, decision := range decisions { result, err := handler.handleDecisionWithResult(ctx, decision) if err != nil || handler.stopProcessing { return nil, err } else if result != nil { results = append(results, result) } } handler.mutableState.GetExecutionInfo().ExecutionContext = executionContext return results, nil } func (handler *taskHandlerImpl) handleDecisionWithResult( ctx context.Context, decision *types.Decision, ) (*decisionResult, error) { switch decision.GetDecisionType() { case types.DecisionTypeScheduleActivityTask: return handler.handleDecisionScheduleActivity(ctx, decision.ScheduleActivityTaskDecisionAttributes) default: return nil, handler.handleDecision(ctx, decision) } } func (handler *taskHandlerImpl) handleDecision( ctx context.Context, decision *types.Decision, ) error { switch decision.GetDecisionType() { case types.DecisionTypeCompleteWorkflowExecution: return handler.handleDecisionCompleteWorkflow(ctx, decision.CompleteWorkflowExecutionDecisionAttributes) case types.DecisionTypeFailWorkflowExecution: return handler.handleDecisionFailWorkflow(ctx, decision.FailWorkflowExecutionDecisionAttributes) case types.DecisionTypeCancelWorkflowExecution: return handler.handleDecisionCancelWorkflow(ctx, decision.CancelWorkflowExecutionDecisionAttributes) case types.DecisionTypeStartTimer: return handler.handleDecisionStartTimer(ctx, decision.StartTimerDecisionAttributes) case types.DecisionTypeRequestCancelActivityTask: return handler.handleDecisionRequestCancelActivity(ctx, decision.RequestCancelActivityTaskDecisionAttributes) case types.DecisionTypeCancelTimer: return handler.handleDecisionCancelTimer(ctx, decision.CancelTimerDecisionAttributes) case types.DecisionTypeRecordMarker: return handler.handleDecisionRecordMarker(ctx, decision.RecordMarkerDecisionAttributes) case types.DecisionTypeRequestCancelExternalWorkflowExecution: return handler.handleDecisionRequestCancelExternalWorkflow(ctx, decision.RequestCancelExternalWorkflowExecutionDecisionAttributes) case types.DecisionTypeSignalExternalWorkflowExecution: return handler.handleDecisionSignalExternalWorkflow(ctx, decision.SignalExternalWorkflowExecutionDecisionAttributes) case types.DecisionTypeContinueAsNewWorkflowExecution: return handler.handleDecisionContinueAsNewWorkflow(ctx, decision.ContinueAsNewWorkflowExecutionDecisionAttributes) case types.DecisionTypeStartChildWorkflowExecution: return handler.handleDecisionStartChildWorkflow(ctx, decision.StartChildWorkflowExecutionDecisionAttributes) case types.DecisionTypeUpsertWorkflowSearchAttributes: return handler.handleDecisionUpsertWorkflowSearchAttributes(ctx, decision.UpsertWorkflowSearchAttributesDecisionAttributes) default: return &types.BadRequestError{Message: fmt.Sprintf("Unknown decision type: %v", decision.GetDecisionType())} } } func (handler *taskHandlerImpl) handleDecisionScheduleActivity( ctx context.Context, attr *types.ScheduleActivityTaskDecisionAttributes, ) (*decisionResult, error) { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeScheduleActivityCounter, ) executionInfo := handler.mutableState.GetExecutionInfo() domainID := executionInfo.DomainID targetDomainID := domainID if attr.GetDomain() != "" { targetDomainEntry, err := handler.domainCache.GetDomain(attr.GetDomain()) if err != nil { return nil, &types.InternalServiceError{ Message: fmt.Sprintf("Unable to schedule activity across domain %v.", attr.GetDomain()), } } targetDomainID = targetDomainEntry.GetInfo().ID } if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateActivityScheduleAttributes( domainID, targetDomainID, attr, executionInfo, metrics.HistoryRespondDecisionTaskCompletedScope, ) }, types.DecisionTaskFailedCauseBadScheduleActivityAttributes, ); err != nil || handler.stopProcessing { return nil, err } failWorkflow, err := handler.sizeLimitChecker.failWorkflowIfBlobSizeExceedsLimit( metrics.DecisionTypeTag(types.DecisionTypeScheduleActivityTask.String()), attr.Input, "ScheduleActivityTaskDecisionAttributes.Input exceeds size limit.", ) if err != nil || failWorkflow { handler.stopProcessing = true return nil, err } event, ai, activityDispatchInfo, dispatched, started, err := handler.mutableState.AddActivityTaskScheduledEvent( ctx, handler.decisionTaskCompletedID, attr, handler.activityCountToDispatch > 0) if dispatched { handler.activityCountToDispatch-- } switch err.(type) { case nil: if activityDispatchInfo != nil || started { if _, err1 := handler.mutableState.AddActivityTaskStartedEvent(ai, event.ID, uuid.New(), handler.identity); err1 != nil { return nil, err1 } if started { return nil, nil } token := &common.TaskToken{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, WorkflowType: executionInfo.WorkflowTypeName, RunID: executionInfo.RunID, ScheduleID: ai.ScheduleID, ScheduleAttempt: 0, ActivityID: ai.ActivityID, ActivityType: attr.ActivityType.GetName(), } activityDispatchInfo.TaskToken, err = handler.tokenSerializer.Serialize(token) if err != nil { return nil, workflow.ErrSerializingToken } activityDispatchInfo.ScheduledTimestamp = common.Int64Ptr(ai.ScheduledTime.UnixNano()) activityDispatchInfo.ScheduledTimestampOfThisAttempt = common.Int64Ptr(ai.ScheduledTime.UnixNano()) activityDispatchInfo.StartedTimestamp = common.Int64Ptr(ai.StartedTime.UnixNano()) return &decisionResult{activityDispatchInfo: activityDispatchInfo}, nil } return nil, nil case *types.BadRequestError: return nil, handler.handlerFailDecision( types.DecisionTaskFailedCauseScheduleActivityDuplicateID, "", ) case *types.InternalServiceError: // Check if this is ErrTooManyPendingActivities if err.Error() == execution.ErrTooManyPendingActivities.Error() { return nil, handler.handleFailWorkflowError(common.FailureReasonPendingActivityExceedsLimit, err.Error()) } return nil, err default: return nil, err } } // handleFailWorkflowError handles the certain types of error by failing the workflow func (handler *taskHandlerImpl) handleFailWorkflowError(failReason string, failDetails string) error { // Fail the workflow immediately instead of just returning the error handler.stopProcessing = true failAttributes := &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(failReason), Details: []byte(failDetails), } if _, failErr := handler.mutableState.AddFailWorkflowEvent( handler.decisionTaskCompletedID, failAttributes, ); failErr != nil { return failErr } return nil } func (handler *taskHandlerImpl) handleDecisionRequestCancelActivity( ctx context.Context, attr *types.RequestCancelActivityTaskDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeCancelActivityCounter, ) if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateActivityCancelAttributes( attr, metrics.HistoryRespondDecisionTaskCompletedScope, handler.domainEntry.GetInfo().Name) }, types.DecisionTaskFailedCauseBadRequestCancelActivityAttributes, ); err != nil || handler.stopProcessing { return err } activityID := attr.GetActivityID() actCancelReqEvent, ai, err := handler.mutableState.AddActivityTaskCancelRequestedEvent( handler.decisionTaskCompletedID, activityID, handler.identity, ) switch err.(type) { case nil: if ai.StartedID == constants.EmptyEventID { // We haven't started the activity yet, we can cancel the activity right away and // schedule a decision task to ensure the workflow makes progress. _, err = handler.mutableState.AddActivityTaskCanceledEvent( ai.ScheduleID, ai.StartedID, actCancelReqEvent.ID, []byte(activityCancellationMsgActivityNotStarted), handler.identity, ) if err != nil { return err } handler.activityNotStartedCancelled = true } return nil case *types.BadRequestError: _, err = handler.mutableState.AddRequestCancelActivityTaskFailedEvent( handler.decisionTaskCompletedID, activityID, activityCancellationMsgActivityIDUnknown, ) return err default: return err } } func (handler *taskHandlerImpl) handleDecisionStartTimer( ctx context.Context, attr *types.StartTimerDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeStartTimerCounter, ) if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateTimerScheduleAttributes( attr, metrics.HistoryRespondDecisionTaskCompletedScope, handler.domainEntry.GetInfo().Name) }, types.DecisionTaskFailedCauseBadStartTimerAttributes, ); err != nil || handler.stopProcessing { return err } _, _, err := handler.mutableState.AddTimerStartedEvent(handler.decisionTaskCompletedID, attr) switch err.(type) { case nil: return nil case *types.BadRequestError: return handler.handlerFailDecision( types.DecisionTaskFailedCauseStartTimerDuplicateID, "", ) default: return err } } func (handler *taskHandlerImpl) handleDecisionCompleteWorkflow( ctx context.Context, attr *types.CompleteWorkflowExecutionDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeCompleteWorkflowCounter, ) if handler.hasUnhandledEventsBeforeDecisions { return handler.handlerFailDecision( types.DecisionTaskFailedCauseUnhandledDecision, "cannot complete workflow, new pending decisions were scheduled while this decision was processing", ) } if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateCompleteWorkflowExecutionAttributes(attr) }, types.DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes, ); err != nil || handler.stopProcessing { return err } failWorkflow, err := handler.sizeLimitChecker.failWorkflowIfBlobSizeExceedsLimit( metrics.DecisionTypeTag(types.DecisionTypeCompleteWorkflowExecution.String()), attr.Result, "CompleteWorkflowExecutionDecisionAttributes.Result exceeds size limit.", ) if err != nil || failWorkflow { handler.stopProcessing = true return err } // If the decision has more than one completion event than just pick the first one if !handler.mutableState.IsWorkflowExecutionRunning() { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.MultipleCompletionDecisionsCounter, ) handler.logger.Warn( "Multiple completion decisions", tag.WorkflowDecisionType(int64(types.DecisionTypeCompleteWorkflowExecution)), tag.ErrorTypeMultipleCompletionDecisions, ) return nil } // event ID is not relevant isCanceled, _ := handler.mutableState.IsCancelRequested() // check if this is a cron workflow cronBackoff, err := handler.mutableState.GetCronBackoffDuration(ctx) if err != nil { handler.stopProcessing = true return err } if isCanceled || cronBackoff == backoff.NoBackoff { // canceled or not cron, so complete this workflow execution if _, err := handler.mutableState.AddCompletedWorkflowEvent(handler.decisionTaskCompletedID, attr); err != nil { return &types.InternalServiceError{Message: "Unable to add complete workflow event."} } return nil } // this is a cron workflow startEvent, err := handler.mutableState.GetStartEvent(ctx) if err != nil { return err } startAttributes := startEvent.WorkflowExecutionStartedEventAttributes return handler.retryCronContinueAsNew( ctx, startAttributes, int32(cronBackoff.Seconds()), types.ContinueAsNewInitiatorCronSchedule.Ptr(), nil, nil, attr.Result, ) } func (handler *taskHandlerImpl) handleDecisionFailWorkflow( ctx context.Context, attr *types.FailWorkflowExecutionDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeFailWorkflowCounter, ) if handler.hasUnhandledEventsBeforeDecisions { return handler.handlerFailDecision( types.DecisionTaskFailedCauseUnhandledDecision, "cannot complete workflow, new pending decisions were scheduled while this decision was processing", ) } if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateFailWorkflowExecutionAttributes(attr) }, types.DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes, ); err != nil || handler.stopProcessing { return err } failWorkflow, err := handler.sizeLimitChecker.failWorkflowIfBlobSizeExceedsLimit( metrics.DecisionTypeTag(types.DecisionTypeFailWorkflowExecution.String()), attr.Details, "FailWorkflowExecutionDecisionAttributes.Details exceeds size limit.", ) if err != nil || failWorkflow { handler.stopProcessing = true return err } // If the decision has more than one completion event than just pick the first one if !handler.mutableState.IsWorkflowExecutionRunning() { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.MultipleCompletionDecisionsCounter, ) handler.logger.Warn( "Multiple completion decisions", tag.WorkflowDecisionType(int64(types.DecisionTypeFailWorkflowExecution)), tag.ErrorTypeMultipleCompletionDecisions, ) return nil } if is, _ := handler.mutableState.IsCancelRequested(); is { // cancellation must be sticky, as it's telling things to stop. // this is particularly important for child workflows, as if they restart themselves after the parent // cancels its context, there is no way for the parent to cancel the new run. cancelAttrs := types.CancelWorkflowExecutionDecisionAttributes{ // TODO: serialize reason somehow, may deserve a new field / wrapped errors Details: attr.Details, } if _, err := handler.mutableState.AddWorkflowExecutionCanceledEvent(handler.decisionTaskCompletedID, &cancelAttrs); err != nil { return err } return nil } // below will check whether to do continue as new based on backoff & backoff or cron backoffInterval := handler.mutableState.GetRetryBackoffDuration(attr.GetReason()) continueAsNewInitiator := types.ContinueAsNewInitiatorRetryPolicy // first check the backoff retry if backoffInterval == backoff.NoBackoff { // if no backoff retry, set the backoffInterval using cron schedule backoffInterval, err = handler.mutableState.GetCronBackoffDuration(ctx) if err != nil { handler.stopProcessing = true return err } continueAsNewInitiator = types.ContinueAsNewInitiatorCronSchedule } // second check the backoff / cron schedule if backoffInterval == backoff.NoBackoff { // no retry or cron if _, err := handler.mutableState.AddFailWorkflowEvent(handler.decisionTaskCompletedID, attr); err != nil { return err } return nil } // this is a cron / backoff workflow startEvent, err := handler.mutableState.GetStartEvent(ctx) if err != nil { return err } startAttributes := startEvent.WorkflowExecutionStartedEventAttributes return handler.retryCronContinueAsNew( ctx, startAttributes, int32(backoffInterval.Seconds()), continueAsNewInitiator.Ptr(), attr.Reason, attr.Details, startAttributes.LastCompletionResult, ) } func (handler *taskHandlerImpl) handleDecisionCancelTimer( ctx context.Context, attr *types.CancelTimerDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeCancelTimerCounter, ) if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateTimerCancelAttributes( attr, metrics.HistoryRespondDecisionTaskCompletedScope, handler.domainEntry.GetInfo().Name) }, types.DecisionTaskFailedCauseBadCancelTimerAttributes, ); err != nil || handler.stopProcessing { return err } _, err := handler.mutableState.AddTimerCanceledEvent( handler.decisionTaskCompletedID, attr, handler.identity) switch err.(type) { case nil: // timer deletion is a success, we may have deleted a fired timer in // which case we should reset hasBufferedEvents // TODO deletion of timer fired event refreshing hasUnhandledEventsBeforeDecisions // is not entirely correct, since during these decisions processing, new event may appear handler.hasUnhandledEventsBeforeDecisions = handler.mutableState.HasBufferedEvents() return nil case *types.BadRequestError: _, err = handler.mutableState.AddCancelTimerFailedEvent( handler.decisionTaskCompletedID, attr, handler.identity, ) return err default: return err } } func (handler *taskHandlerImpl) handleDecisionCancelWorkflow( ctx context.Context, attr *types.CancelWorkflowExecutionDecisionAttributes, ) error { handler.metricsClient.IncCounter(metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeCancelWorkflowCounter) if handler.hasUnhandledEventsBeforeDecisions { return handler.handlerFailDecision( types.DecisionTaskFailedCauseUnhandledDecision, "cannot process cancellation, new pending decisions were scheduled while this decision was processing", ) } if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateCancelWorkflowExecutionAttributes(attr) }, types.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes, ); err != nil || handler.stopProcessing { return err } // If the decision has more than one completion event than just pick the first one if !handler.mutableState.IsWorkflowExecutionRunning() { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.MultipleCompletionDecisionsCounter, ) handler.logger.Warn( "Multiple completion decisions", tag.WorkflowDecisionType(int64(types.DecisionTypeCancelWorkflowExecution)), tag.ErrorTypeMultipleCompletionDecisions, ) return nil } _, err := handler.mutableState.AddWorkflowExecutionCanceledEvent(handler.decisionTaskCompletedID, attr) return err } func (handler *taskHandlerImpl) handleDecisionRequestCancelExternalWorkflow( ctx context.Context, attr *types.RequestCancelExternalWorkflowExecutionDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeCancelExternalWorkflowCounter, ) executionInfo := handler.mutableState.GetExecutionInfo() domainID := executionInfo.DomainID targetDomainID := domainID if attr.GetDomain() != "" { targetDomainEntry, err := handler.domainCache.GetDomain(attr.GetDomain()) if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("Unable to cancel workflow across domain: %v.", attr.GetDomain()), } } targetDomainID = targetDomainEntry.GetInfo().ID } if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateCancelExternalWorkflowExecutionAttributes( domainID, targetDomainID, attr, metrics.HistoryRespondDecisionTaskCompletedScope, ) }, types.DecisionTaskFailedCauseBadRequestCancelExternalWorkflowExecutionAttributes, ); err != nil || handler.stopProcessing { return err } cancelRequestID := uuid.New() _, _, err := handler.mutableState.AddRequestCancelExternalWorkflowExecutionInitiatedEvent( handler.decisionTaskCompletedID, cancelRequestID, attr, ) return err } func (handler *taskHandlerImpl) handleDecisionRecordMarker( ctx context.Context, attr *types.RecordMarkerDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeRecordMarkerCounter, ) if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateRecordMarkerAttributes( attr, metrics.HistoryRespondDecisionTaskCompletedScope, handler.domainEntry.GetInfo().Name) }, types.DecisionTaskFailedCauseBadRecordMarkerAttributes, ); err != nil || handler.stopProcessing { return err } failWorkflow, err := handler.sizeLimitChecker.failWorkflowIfBlobSizeExceedsLimit( metrics.DecisionTypeTag(types.DecisionTypeRecordMarker.String()), attr.Details, "RecordMarkerDecisionAttributes.Details exceeds size limit.", ) if err != nil || failWorkflow { handler.stopProcessing = true return err } _, err = handler.mutableState.AddRecordMarkerEvent(handler.decisionTaskCompletedID, attr) return err } func (handler *taskHandlerImpl) handleDecisionContinueAsNewWorkflow( ctx context.Context, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeContinueAsNewCounter, ) if handler.hasUnhandledEventsBeforeDecisions { return handler.handlerFailDecision( types.DecisionTaskFailedCauseUnhandledDecision, "cannot complete workflow, new pending decisions were scheduled while this decision was processing", ) } executionInfo := handler.mutableState.GetExecutionInfo() if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateContinueAsNewWorkflowExecutionAttributes( attr, executionInfo, metrics.HistoryRespondDecisionTaskCompletedScope, handler.domainEntry.GetInfo().Name, ) }, types.DecisionTaskFailedCauseBadContinueAsNewAttributes, ); err != nil || handler.stopProcessing { return err } failWorkflow, err := handler.sizeLimitChecker.failWorkflowIfBlobSizeExceedsLimit( metrics.DecisionTypeTag(types.DecisionTypeContinueAsNewWorkflowExecution.String()), attr.Input, "ContinueAsNewWorkflowExecutionDecisionAttributes. Input exceeds size limit.", ) if err != nil || failWorkflow { handler.stopProcessing = true return err } // If the decision has more than one completion event than just pick the first one if !handler.mutableState.IsWorkflowExecutionRunning() { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.MultipleCompletionDecisionsCounter, ) handler.logger.Warn( "Multiple completion decisions", tag.WorkflowDecisionType(int64(types.DecisionTypeContinueAsNewWorkflowExecution)), tag.ErrorTypeMultipleCompletionDecisions, ) return nil } if is, _ := handler.mutableState.IsCancelRequested(); is { // cancellation must be sticky, as it's telling things to stop. // this is particularly important for child workflows, as if they restart themselves after the parent // cancels its context, there is no way for the parent to cancel the new run. cancelAttrs := types.CancelWorkflowExecutionDecisionAttributes{ Details: nil, // TODO: serialize continue-as-new data somehow, may deserve a new field } if _, err := handler.mutableState.AddWorkflowExecutionCanceledEvent(handler.decisionTaskCompletedID, &cancelAttrs); err != nil { return err } return nil } // Extract parentDomainName so it can be passed down to next run of workflow execution var parentDomainName string if handler.mutableState.HasParentExecution() { parentDomainID := executionInfo.ParentDomainID parentDomainName, err = handler.domainCache.GetDomainName(parentDomainID) if err != nil { return err } } _, newStateBuilder, err := handler.mutableState.AddContinueAsNewEvent( ctx, handler.decisionTaskCompletedID, handler.decisionTaskCompletedID, parentDomainName, attr, ) if err != nil { return err } handler.continueAsNewBuilder = newStateBuilder return nil } func (handler *taskHandlerImpl) handleDecisionStartChildWorkflow( ctx context.Context, attr *types.StartChildWorkflowExecutionDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeChildWorkflowCounter, ) executionInfo := handler.mutableState.GetExecutionInfo() domainID := executionInfo.DomainID targetDomainID := domainID if attr.GetDomain() != "" { targetDomainEntry, err := handler.domainCache.GetDomain(attr.GetDomain()) if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("Unable to schedule child execution across domain %v.", attr.GetDomain()), } } targetDomainID = targetDomainEntry.GetInfo().ID } if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateStartChildExecutionAttributes( domainID, targetDomainID, attr, executionInfo, metrics.HistoryRespondDecisionTaskCompletedScope, ) }, types.DecisionTaskFailedCauseBadStartChildExecutionAttributes, ); err != nil || handler.stopProcessing { return err } failWorkflow, err := handler.sizeLimitChecker.failWorkflowIfBlobSizeExceedsLimit( metrics.DecisionTypeTag(types.DecisionTypeStartChildWorkflowExecution.String()), attr.Input, "StartChildWorkflowExecutionDecisionAttributes.Input exceeds size limit.", ) if err != nil || failWorkflow { handler.stopProcessing = true return err } enabled := handler.config.EnableParentClosePolicy(handler.domainEntry.GetInfo().Name) if attr.ParentClosePolicy == nil { // for old clients, this field is empty. If they enable the feature, make default as terminate if enabled { attr.ParentClosePolicy = types.ParentClosePolicyTerminate.Ptr() } else { attr.ParentClosePolicy = types.ParentClosePolicyAbandon.Ptr() } } else { // for domains that haven't enabled the feature yet, need to use Abandon for backward-compatibility if !enabled { attr.ParentClosePolicy = types.ParentClosePolicyAbandon.Ptr() } } requestID := uuid.New() _, _, err = handler.mutableState.AddStartChildWorkflowExecutionInitiatedEvent( handler.decisionTaskCompletedID, requestID, attr, ) return err } func (handler *taskHandlerImpl) handleDecisionSignalExternalWorkflow( ctx context.Context, attr *types.SignalExternalWorkflowExecutionDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeSignalExternalWorkflowCounter, ) executionInfo := handler.mutableState.GetExecutionInfo() domainID := executionInfo.DomainID targetDomainID := domainID if attr.GetDomain() != "" { targetDomainEntry, err := handler.domainCache.GetDomain(attr.GetDomain()) if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("Unable to signal workflow across domain: %v.", attr.GetDomain()), } } targetDomainID = targetDomainEntry.GetInfo().ID } if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateSignalExternalWorkflowExecutionAttributes( domainID, targetDomainID, attr, metrics.HistoryRespondDecisionTaskCompletedScope, ) }, types.DecisionTaskFailedCauseBadSignalWorkflowExecutionAttributes, ); err != nil || handler.stopProcessing { return err } failWorkflow, err := handler.sizeLimitChecker.failWorkflowIfBlobSizeExceedsLimit( metrics.DecisionTypeTag(types.DecisionTypeSignalExternalWorkflowExecution.String()), attr.Input, "SignalExternalWorkflowExecutionDecisionAttributes.Input exceeds size limit.", ) if err != nil || failWorkflow { handler.stopProcessing = true return err } signalRequestID := uuid.New() // for deduplicate _, _, err = handler.mutableState.AddSignalExternalWorkflowExecutionInitiatedEvent( handler.decisionTaskCompletedID, signalRequestID, attr, ) return err } func (handler *taskHandlerImpl) handleDecisionUpsertWorkflowSearchAttributes( ctx context.Context, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes, ) error { handler.metricsClient.IncCounter( metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeUpsertWorkflowSearchAttributesCounter, ) // get domain name executionInfo := handler.mutableState.GetExecutionInfo() domainID := executionInfo.DomainID domainName, err := handler.domainCache.GetDomainName(domainID) if err != nil { return &types.InternalServiceError{ Message: fmt.Sprintf("Unable to get domain for domainID: %v.", domainID), } } // valid search attributes for upsert if err := handler.validateDecisionAttr( func() error { return handler.attrValidator.validateUpsertWorkflowSearchAttributes( domainName, attr, ) }, types.DecisionTaskFailedCauseBadSearchAttributes, ); err != nil || handler.stopProcessing { return err } // blob size limit check failWorkflow, err := handler.sizeLimitChecker.failWorkflowIfBlobSizeExceedsLimit( metrics.DecisionTypeTag(types.DecisionTypeUpsertWorkflowSearchAttributes.String()), convertSearchAttributesToByteArray(attr.GetSearchAttributes().GetIndexedFields()), "UpsertWorkflowSearchAttributesDecisionAttributes exceeds size limit.", ) if err != nil || failWorkflow { handler.stopProcessing = true return err } _, err = handler.mutableState.AddUpsertWorkflowSearchAttributesEvent( handler.decisionTaskCompletedID, attr, ) return err } func convertSearchAttributesToByteArray(fields map[string][]byte) []byte { result := make([]byte, 0) for k, v := range fields { result = append(result, []byte(k)...) result = append(result, v...) } return result } func (handler *taskHandlerImpl) retryCronContinueAsNew( ctx context.Context, attr *types.WorkflowExecutionStartedEventAttributes, backoffInterval int32, continueAsNewIter *types.ContinueAsNewInitiator, failureReason *string, failureDetails []byte, lastCompletionResult []byte, ) error { continueAsNewAttributes := &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: attr.WorkflowType, TaskList: attr.TaskList, RetryPolicy: attr.RetryPolicy, Input: attr.Input, ExecutionStartToCloseTimeoutSeconds: attr.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: attr.TaskStartToCloseTimeoutSeconds, CronSchedule: attr.CronSchedule, BackoffStartIntervalInSeconds: common.Int32Ptr(backoffInterval), Initiator: continueAsNewIter, FailureReason: failureReason, FailureDetails: failureDetails, LastCompletionResult: lastCompletionResult, Header: attr.Header, Memo: attr.Memo, SearchAttributes: attr.SearchAttributes, JitterStartSeconds: attr.JitterStartSeconds, CronOverlapPolicy: attr.CronOverlapPolicy, ActiveClusterSelectionPolicy: attr.ActiveClusterSelectionPolicy, } _, newStateBuilder, err := handler.mutableState.AddContinueAsNewEvent( ctx, handler.decisionTaskCompletedID, handler.decisionTaskCompletedID, attr.GetParentWorkflowDomain(), continueAsNewAttributes, ) if err != nil { return err } handler.continueAsNewBuilder = newStateBuilder return nil } func (handler *taskHandlerImpl) validateDecisionAttr( validationFn attrValidationFn, failedCause types.DecisionTaskFailedCause, ) error { if err := validationFn(); err != nil { if _, ok := err.(*types.BadRequestError); ok { return handler.handlerFailDecision(failedCause, err.Error()) } return err } return nil } func (handler *taskHandlerImpl) handlerFailDecision( failedCause types.DecisionTaskFailedCause, failMessage string, ) error { handler.failDecision = true handler.failDecisionCause = failedCause.Ptr() handler.failMessage = common.StringPtr(failMessage) handler.stopProcessing = true return nil } ================================================ FILE: service/history/decision/task_handler_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package decision import ( "context" "errors" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) const ( testTaskCompletedID = int64(123) ) func TestHandleDecisionRequestCancelExternalWorkflow(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl) attributes *types.RequestCancelExternalWorkflowExecutionDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, err error) }{ { name: "success", attributes: &types.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, Control: nil, ChildWorkflowOnly: false, }, expectMockCalls: func(taskHandler *taskHandlerImpl) { taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, err error) { assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "internal service error", attributes: &types.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, Control: nil, ChildWorkflowOnly: false, }, expectMockCalls: func(taskHandler *taskHandlerImpl) { taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(nil, errors.New("some error getting domain cache")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, err error) { assert.Equal(t, &types.InternalServiceError{Message: "Unable to cancel workflow across domain: some random domain name."}, err) }, }, { name: "attributes validation failure", asserts: func(t *testing.T, taskHandler *taskHandlerImpl, err error) { assert.True(t, taskHandler.failDecision) assert.NotNil(t, taskHandler.failMessage) assert.Equal(t, func(i int32) *types.DecisionTaskFailedCause { cause := new(types.DecisionTaskFailedCause) *cause = types.DecisionTaskFailedCause(i) return cause // 9 is types.DecisionTaskFailedCause "BAD_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_ATTRIBUTES" }(9), taskHandler.failDecisionCause) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Times(1).Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddRequestCancelExternalWorkflowExecutionInitiatedEvent(testTaskCompletedID, gomock.Any(), test.attributes).AnyTimes() if test.expectMockCalls != nil { test.expectMockCalls(taskHandler) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeRequestCancelExternalWorkflowExecution), RequestCancelExternalWorkflowExecutionDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, err) }) } } func TestHandleDecisionRequestCancelActivity(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl) attributes *types.RequestCancelActivityTaskDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, err error) }{ { name: "success", attributes: &types.RequestCancelActivityTaskDecisionAttributes{ActivityID: testdata.ActivityID}, expectMockCalls: func(taskHandler *taskHandlerImpl) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskCancelRequestedEvent( testTaskCompletedID, testdata.ActivityID, testdata.Identity, ).Times(1).Return(&types.HistoryEvent{}, &persistence.ActivityInfo{StartedID: commonconstants.EmptyEventID}, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskCanceledEvent(int64(0), int64(-23), int64(0), []byte(activityCancellationMsgActivityNotStarted), testdata.Identity).Return(nil, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, err error) { assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "AddActivityTaskCanceledEvent failure", attributes: &types.RequestCancelActivityTaskDecisionAttributes{ActivityID: testdata.ActivityID}, expectMockCalls: func(taskHandler *taskHandlerImpl) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskCancelRequestedEvent( testTaskCompletedID, testdata.ActivityID, testdata.Identity, ).Times(1).Return(&types.HistoryEvent{}, &persistence.ActivityInfo{StartedID: commonconstants.EmptyEventID}, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskCanceledEvent(int64(0), int64(-23), int64(0), []byte(activityCancellationMsgActivityNotStarted), testdata.Identity).Return(nil, errors.New("some random error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, err error) { assert.Equal(t, errors.New("some random error"), err) }, }, { name: "AddRequestCancelActivityTaskFailedEvent failure", attributes: &types.RequestCancelActivityTaskDecisionAttributes{ActivityID: testdata.ActivityID}, expectMockCalls: func(taskHandler *taskHandlerImpl) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskCancelRequestedEvent( testTaskCompletedID, testdata.ActivityID, testdata.Identity, ).Times(1).Return(&types.HistoryEvent{}, &persistence.ActivityInfo{StartedID: commonconstants.EmptyEventID}, &types.BadRequestError{Message: "some types.BadRequestError error"}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddRequestCancelActivityTaskFailedEvent(testTaskCompletedID, testdata.ActivityID, activityCancellationMsgActivityIDUnknown).Return(nil, errors.New("some random error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, err error) { assert.Equal(t, errors.New("some random error"), err) }, }, { name: "default switch case error", attributes: &types.RequestCancelActivityTaskDecisionAttributes{ActivityID: testdata.ActivityID}, expectMockCalls: func(taskHandler *taskHandlerImpl) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskCancelRequestedEvent( testTaskCompletedID, testdata.ActivityID, testdata.Identity, ).Times(1).Return(&types.HistoryEvent{}, &persistence.ActivityInfo{StartedID: commonconstants.EmptyEventID}, errors.New("some default error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, err error) { assert.Equal(t, errors.New("some default error"), err) }, }, { name: "attributes validation failure", asserts: func(t *testing.T, taskHandler *taskHandlerImpl, err error) { assert.True(t, taskHandler.failDecision) assert.NotNil(t, taskHandler.failMessage) assert.Equal(t, func(i int32) *types.DecisionTaskFailedCause { cause := new(types.DecisionTaskFailedCause) *cause = types.DecisionTaskFailedCause(i) return cause // 2 is types.DecisionTaskFailedCause "BAD_REQUEST_CANCEL_ACTIVITY_ATTRIBUTES" }(2), taskHandler.failDecisionCause) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddRequestCancelExternalWorkflowExecutionInitiatedEvent(testTaskCompletedID, gomock.Any(), test.attributes).AnyTimes() if test.expectMockCalls != nil { test.expectMockCalls(taskHandler) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeRequestCancelActivityTask), RequestCancelActivityTaskDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, err) }) } } func TestHandleDecisionStartChildWorkflow(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes) attributes *types.StartChildWorkflowExecutionDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes, err error) }{ { name: "success - ParentClosePolicy enabled", attributes: &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, WorkflowType: &types.WorkflowType{Name: testdata.WorkflowTypeName}, TaskList: &types.TaskList{Name: testdata.TaskListName}, }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddStartChildWorkflowExecutionInitiatedEvent(testTaskCompletedID, gomock.Any(), attr).Times(1) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.ParentClosePolicyTerminate.Ptr(), attr.ParentClosePolicy) assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "success - ParentClosePolicy disabled", attributes: &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, WorkflowType: &types.WorkflowType{Name: testdata.WorkflowTypeName}, TaskList: &types.TaskList{Name: testdata.TaskListName}, }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes) { taskHandler.config.EnableParentClosePolicy = func(domain string) bool { return false } taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddStartChildWorkflowExecutionInitiatedEvent(testTaskCompletedID, gomock.Any(), attr).Times(1) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.ParentClosePolicyAbandon.Ptr(), attr.ParentClosePolicy) assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "success - ParentClosePolicy non nil", attributes: &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, WorkflowType: &types.WorkflowType{Name: testdata.WorkflowTypeName}, TaskList: &types.TaskList{Name: testdata.TaskListName}, ParentClosePolicy: new(types.ParentClosePolicy), }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes) { taskHandler.config.EnableParentClosePolicy = func(domain string) bool { return false } taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddStartChildWorkflowExecutionInitiatedEvent(testTaskCompletedID, gomock.Any(), attr).Times(1) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.ParentClosePolicyAbandon.Ptr(), attr.ParentClosePolicy) assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "internal service error", attributes: &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, WorkflowType: &types.WorkflowType{Name: testdata.WorkflowTypeName}, TaskList: &types.TaskList{Name: testdata.TaskListName}, }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes) { taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(nil, errors.New("some error getting domain cache")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, &types.InternalServiceError{Message: "Unable to schedule child execution across domain some random domain name."}, err) }, }, { name: "attributes validation failure", attributes: &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, WorkflowType: &types.WorkflowType{Name: testdata.WorkflowTypeName}, }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes) { taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes, err error) { assert.True(t, taskHandler.failDecision) assert.Equal(t, "missing task list name", *taskHandler.failMessage) assert.Equal(t, func(i int32) *types.DecisionTaskFailedCause { cause := new(types.DecisionTaskFailedCause) *cause = types.DecisionTaskFailedCause(i) return cause // 15 is types.DecisionTaskFailedCause "BAD_START_CHILD_EXECUTION_ATTRIBUTES" }(15), taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "size limit checker failure", attributes: &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, WorkflowType: &types.WorkflowType{Name: testdata.WorkflowTypeName}, TaskList: &types.TaskList{Name: testdata.TaskListName}, Input: []byte("input"), }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes) { taskHandler.sizeLimitChecker.blobSizeLimitError = 1 taskHandler.sizeLimitChecker.blobSizeLimitWarn = 2 taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(testTaskCompletedID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonDecisionBlobSizeExceedsLimit), Details: []byte("StartChildWorkflowExecutionDecisionAttributes.Input exceeds size limit."), }).Return(nil, errors.New("some error adding fail workflow event")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartChildWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, errors.New("some error adding fail workflow event"), err) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().AnyTimes().Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeStartChildWorkflowExecution), StartChildWorkflowExecutionDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, err) }) } } func TestHandleDecisionCancelTimer(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.CancelTimerDecisionAttributes) attributes *types.CancelTimerDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CancelTimerDecisionAttributes, err error) }{ { name: "success", attributes: &types.CancelTimerDecisionAttributes{TimerID: "test-timer-id"}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CancelTimerDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddTimerCanceledEvent(testTaskCompletedID, attr, taskHandler.identity) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().HasBufferedEvents() }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CancelTimerDecisionAttributes, err error) { assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "bad request error", attributes: &types.CancelTimerDecisionAttributes{TimerID: "test-timer-id"}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CancelTimerDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddTimerCanceledEvent(testTaskCompletedID, attr, taskHandler.identity).Return(nil, &types.BadRequestError{"some bad request error"}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddCancelTimerFailedEvent(testTaskCompletedID, attr, taskHandler.identity) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CancelTimerDecisionAttributes, err error) { assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "default error", attributes: &types.CancelTimerDecisionAttributes{TimerID: "test-timer-id"}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CancelTimerDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddTimerCanceledEvent(testTaskCompletedID, attr, taskHandler.identity).Return(nil, errors.New("some random error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CancelTimerDecisionAttributes, err error) { assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, errors.New("some random error"), err) }, }, { name: "attributes validation error", attributes: &types.CancelTimerDecisionAttributes{}, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CancelTimerDecisionAttributes, err error) { assert.True(t, taskHandler.failDecision) assert.Equal(t, "TimerId is not set on decision.", *taskHandler.failMessage) assert.Equal(t, func(i int32) *types.DecisionTaskFailedCause { cause := new(types.DecisionTaskFailedCause) *cause = types.DecisionTaskFailedCause(i) return cause }(4), taskHandler.failDecisionCause) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeCancelTimer), CancelTimerDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, err) }) } } func TestHandleDecisionSignalExternalWorkflow(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.SignalExternalWorkflowExecutionDecisionAttributes) attributes *types.SignalExternalWorkflowExecutionDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.SignalExternalWorkflowExecutionDecisionAttributes, err error) }{ { name: "success", attributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, Execution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, SignalName: testdata.SignalName, Input: []byte("some input data"), }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.SignalExternalWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Times(2).Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddSignalExternalWorkflowExecutionInitiatedEvent(testTaskCompletedID, gomock.Any(), attr) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.SignalExternalWorkflowExecutionDecisionAttributes, err error) { assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "internal service error", attributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, Execution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, SignalName: testdata.SignalName, Input: []byte("some input data"), }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.SignalExternalWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Times(1).Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(nil, errors.New("some error getting domain cache")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.SignalExternalWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, &types.InternalServiceError{Message: "Unable to signal workflow across domain: some random domain name."}, err) }, }, { name: "attributes validation failure", attributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, Execution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, Input: []byte("some input data"), }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.SignalExternalWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Times(1).Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.SignalExternalWorkflowExecutionDecisionAttributes, err error) { assert.True(t, taskHandler.failDecision) assert.Equal(t, "SignalName is not set on decision.", *taskHandler.failMessage) assert.Equal(t, func(i int32) *types.DecisionTaskFailedCause { cause := new(types.DecisionTaskFailedCause) *cause = types.DecisionTaskFailedCause(i) return cause // 14 is types.DecisionTaskFailedCause "BAD_SIGNAL_WORKFLOW_EXECUTION_ATTRIBUTES" }(14), taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "size limit checker failure", attributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, Execution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, SignalName: testdata.SignalName, Input: []byte("some input data"), }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.SignalExternalWorkflowExecutionDecisionAttributes) { taskHandler.sizeLimitChecker.blobSizeLimitError = 1 taskHandler.sizeLimitChecker.blobSizeLimitWarn = 2 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Times(2).Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(testTaskCompletedID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonDecisionBlobSizeExceedsLimit), Details: []byte("SignalExternalWorkflowExecutionDecisionAttributes.Input exceeds size limit."), }).Return(nil, errors.New("some error adding fail workflow event")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.SignalExternalWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, errors.New("some error adding fail workflow event"), err) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeSignalExternalWorkflowExecution), SignalExternalWorkflowExecutionDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, err) }) } } func TestHandleDecisionUpsertWorkflowSearchAttributes(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes) attributes *types.UpsertWorkflowSearchAttributesDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes, err error) }{ { name: "success", attributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: &types.SearchAttributes{IndexedFields: map[string][]byte{"some-key": []byte(`"some-value"`)}}, }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Times(2).Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddUpsertWorkflowSearchAttributesEvent(testTaskCompletedID, attr) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes, err error) { assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, nil, err) }, }, { name: "attributes validation failure", attributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: &types.SearchAttributes{IndexedFields: map[string][]byte{"some-key": []byte("some-value")}}, }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Times(1).Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes, err error) { assert.True(t, taskHandler.failDecision) assert.Equal(t, "some-value is not a valid search attribute value for key some-key", *taskHandler.failMessage) assert.Equal(t, func(i int32) *types.DecisionTaskFailedCause { cause := new(types.DecisionTaskFailedCause) *cause = types.DecisionTaskFailedCause(i) return cause // 22 is types.DecisionTaskFailedCause "BAD_SEARCH_ATTRIBUTES" }(22), taskHandler.failDecisionCause) }, }, { name: "size limit checker failure", attributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: &types.SearchAttributes{IndexedFields: map[string][]byte{"some-key": []byte(`"some-value"`)}}, }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes) { taskHandler.sizeLimitChecker.blobSizeLimitWarn = 2 taskHandler.sizeLimitChecker.blobSizeLimitError = 1 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Times(2).Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(testTaskCompletedID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonDecisionBlobSizeExceedsLimit), Details: []byte("UpsertWorkflowSearchAttributesDecisionAttributes exceeds size limit."), }).Return(nil, errors.New("some random error adding workflow event")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes, err error) { assert.False(t, taskHandler.failDecision) assert.Empty(t, taskHandler.failMessage) assert.Nil(t, taskHandler.failDecisionCause) assert.Equal(t, errors.New("some random error adding workflow event"), err) }, }, { name: "internal service error", attributes: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: &types.SearchAttributes{IndexedFields: map[string][]byte{"some-key": []byte(`"some-value"`)}}, }, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Times(1).Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return("", errors.New("some random error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.UpsertWorkflowSearchAttributesDecisionAttributes, err error) { assert.Equal(t, &types.InternalServiceError{Message: "Unable to get domain for domainID: deadbeef-0123-4567-890a-bcdef0123456."}, err) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeUpsertWorkflowSearchAttributes), UpsertWorkflowSearchAttributesDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, err) }) } } func TestHandleDecisionStartTimer(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.StartTimerDecisionAttributes) attributes *types.StartTimerDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartTimerDecisionAttributes, err error) }{ { name: "success", attributes: &types.StartTimerDecisionAttributes{TimerID: "test-timer-id"}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.StartTimerDecisionAttributes) { attr.StartToFireTimeoutSeconds = new(int64) *attr.StartToFireTimeoutSeconds = 60 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddTimerStartedEvent(testTaskCompletedID, attr) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartTimerDecisionAttributes, err error) { assert.Nil(t, err) }, }, { name: "attributes validation failure", attributes: &types.StartTimerDecisionAttributes{TimerID: "test-timer-id"}, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartTimerDecisionAttributes, err error) { assert.True(t, taskHandler.stopProcessing) }, }, { name: "bad request error", attributes: &types.StartTimerDecisionAttributes{TimerID: "test-timer-id"}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.StartTimerDecisionAttributes) { attr.StartToFireTimeoutSeconds = new(int64) *attr.StartToFireTimeoutSeconds = 60 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddTimerStartedEvent(testTaskCompletedID, attr).Return(nil, nil, &types.BadRequestError{Message: "some types.BadRequestError error"}) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartTimerDecisionAttributes, err error) { assert.Nil(t, err) assert.True(t, taskHandler.failDecision) assert.Equal(t, types.DecisionTaskFailedCauseStartTimerDuplicateID, *taskHandler.failDecisionCause) }, }, { name: "default case error", attributes: &types.StartTimerDecisionAttributes{TimerID: "test-timer-id"}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.StartTimerDecisionAttributes) { attr.StartToFireTimeoutSeconds = new(int64) *attr.StartToFireTimeoutSeconds = 60 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddTimerStartedEvent(testTaskCompletedID, attr).Return(nil, nil, errors.New("some random error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.StartTimerDecisionAttributes, err error) { assert.Equal(t, "some random error", err.Error()) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeStartTimer), StartTimerDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, err) }) } } func TestHandleDecisionCompleteWorkflow(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes) attributes *types.CompleteWorkflowExecutionDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes, err error) }{ { name: "handler has unhandled events", attributes: &types.CompleteWorkflowExecutionDecisionAttributes{}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes) { taskHandler.hasUnhandledEventsBeforeDecisions = true }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.DecisionTaskFailedCauseUnhandledDecision, *taskHandler.failDecisionCause) assert.Equal(t, "cannot complete workflow, new pending decisions were scheduled while this decision was processing", *taskHandler.failMessage) }, }, { name: "attributes validation failure", asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.DecisionTaskFailedCauseBadCompleteWorkflowExecutionAttributes, *taskHandler.failDecisionCause) }, }, { name: "blob size limit check failure", attributes: &types.CompleteWorkflowExecutionDecisionAttributes{Result: []byte("some-result")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes) { taskHandler.sizeLimitChecker.blobSizeLimitError = 5 taskHandler.sizeLimitChecker.blobSizeLimitWarn = 3 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(taskHandler.sizeLimitChecker.completedID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonDecisionBlobSizeExceedsLimit), Details: []byte("CompleteWorkflowExecutionDecisionAttributes.Result exceeds size limit."), }) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes, err error) { assert.True(t, taskHandler.stopProcessing) assert.Nil(t, err) }, }, { name: "workflow not running", attributes: &types.CompleteWorkflowExecutionDecisionAttributes{Result: []byte("some-result")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(false) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes, err error) { assert.Nil(t, err) }, }, { name: "failure to get cron duration", attributes: &types.CompleteWorkflowExecutionDecisionAttributes{Result: []byte("some-result")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested() taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetCronBackoffDuration(context.Background()).Return(time.Second, errors.New("some error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, "some error", err.Error()) }, }, { name: "internal service error cancel requested", attributes: &types.CompleteWorkflowExecutionDecisionAttributes{Result: []byte("some-result")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(true, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetCronBackoffDuration(context.Background()).Return(backoff.NoBackoff, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddCompletedWorkflowEvent(taskHandler.decisionTaskCompletedID, attr).Return(nil, errors.New("some error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, &types.InternalServiceError{Message: "Unable to add complete workflow event."}, err) }, }, { name: "cancel requested - sucess", attributes: &types.CompleteWorkflowExecutionDecisionAttributes{Result: []byte("some-result")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(true, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetCronBackoffDuration(context.Background()).Return(backoff.NoBackoff, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddCompletedWorkflowEvent(taskHandler.decisionTaskCompletedID, attr) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes, err error) { assert.Nil(t, err) }, }, { name: "GetStartedEvent failure on cron workflow", attributes: &types.CompleteWorkflowExecutionDecisionAttributes{Result: []byte("some-result")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested() taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetCronBackoffDuration(context.Background()) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetStartEvent(context.Background()).Return(nil, errors.New("some error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CompleteWorkflowExecutionDecisionAttributes, err error) { assert.NotNil(t, err) assert.Equal(t, "some error", err.Error()) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeCompleteWorkflowExecution), CompleteWorkflowExecutionDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, err) }) } } func TestHandleDecisionFailWorkflow(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) attributes *types.FailWorkflowExecutionDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) }{ { name: "handler has unhandled events", attributes: &types.FailWorkflowExecutionDecisionAttributes{}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) { taskHandler.hasUnhandledEventsBeforeDecisions = true }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.DecisionTaskFailedCauseUnhandledDecision, *taskHandler.failDecisionCause) assert.Equal(t, "cannot complete workflow, new pending decisions were scheduled while this decision was processing", *taskHandler.failMessage) }, }, { name: "attributes validation failure", asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.DecisionTaskFailedCauseBadFailWorkflowExecutionAttributes, *taskHandler.failDecisionCause) assert.Equal(t, "FailWorkflowExecutionDecisionAttributes is not set on decision.", *taskHandler.failMessage) }, }, { name: "blob size limit check failure", attributes: &types.FailWorkflowExecutionDecisionAttributes{Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) { attr.Reason = new(string) *attr.Reason = "some reason" taskHandler.sizeLimitChecker.blobSizeLimitWarn = 3 taskHandler.sizeLimitChecker.blobSizeLimitError = 5 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(taskHandler.sizeLimitChecker.completedID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonDecisionBlobSizeExceedsLimit), Details: []byte("FailWorkflowExecutionDecisionAttributes.Details exceeds size limit."), }) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.True(t, taskHandler.stopProcessing) assert.Nil(t, err) }, }, { name: "workflow not running", attributes: &types.FailWorkflowExecutionDecisionAttributes{Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) { attr.Reason = new(string) *attr.Reason = "some reason" taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(false) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.Nil(t, err) }, }, { name: "cancel requested - failure to add cancel event", attributes: &types.FailWorkflowExecutionDecisionAttributes{Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) { attr.Reason = new(string) *attr.Reason = "some reason" taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(true, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddWorkflowExecutionCanceledEvent(taskHandler.decisionTaskCompletedID, &types.CancelWorkflowExecutionDecisionAttributes{ Details: attr.Details, }).Return(nil, errors.New("some error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.NotNil(t, err) assert.Equal(t, "some error", err.Error()) }, }, { name: "cancel requested - success", attributes: &types.FailWorkflowExecutionDecisionAttributes{Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) { attr.Reason = new(string) *attr.Reason = "some reason" taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(true, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddWorkflowExecutionCanceledEvent(taskHandler.decisionTaskCompletedID, &types.CancelWorkflowExecutionDecisionAttributes{ Details: attr.Details, }) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.Nil(t, err) }, }, { name: "failure to get backoff duration on cron", attributes: &types.FailWorkflowExecutionDecisionAttributes{Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) { attr.Reason = new(string) *attr.Reason = "some reason" taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(false, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetRetryBackoffDuration(attr.GetReason()).Return(backoff.NoBackoff) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetCronBackoffDuration(context.Background()).Return(time.Second, errors.New("some error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.True(t, taskHandler.stopProcessing) assert.NotNil(t, err) assert.Equal(t, "some error", err.Error()) }, }, { name: "AddFailWorkflowEvent failure", attributes: &types.FailWorkflowExecutionDecisionAttributes{Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) { attr.Reason = new(string) *attr.Reason = "some reason" taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(false, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetRetryBackoffDuration(attr.GetReason()).Return(backoff.NoBackoff) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetCronBackoffDuration(context.Background()).Return(backoff.NoBackoff, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(taskHandler.decisionTaskCompletedID, attr).Return(nil, errors.New("some error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.NotNil(t, err) assert.Equal(t, "some error", err.Error()) }, }, { name: "cron workflow - success", attributes: &types.FailWorkflowExecutionDecisionAttributes{Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) { attr.Reason = new(string) *attr.Reason = "some reason" taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(false, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetRetryBackoffDuration(attr.GetReason()).Return(backoff.NoBackoff) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetCronBackoffDuration(context.Background()).Return(backoff.NoBackoff, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(taskHandler.decisionTaskCompletedID, attr) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.Nil(t, err) }, }, { name: "GetStartEvent failure", attributes: &types.FailWorkflowExecutionDecisionAttributes{Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) { attr.Reason = new(string) *attr.Reason = "some reason" taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(false, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetRetryBackoffDuration(attr.GetReason()) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetStartEvent(context.Background()).Return(nil, errors.New("some error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.NotNil(t, err) assert.Equal(t, "some error", err.Error()) }, }, { name: "success", attributes: &types.FailWorkflowExecutionDecisionAttributes{Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes) { attr.Reason = new(string) *attr.Reason = "some reason" taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(false, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetRetryBackoffDuration(attr.GetReason()) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetStartEvent(context.Background()).Return(&types.HistoryEvent{WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}}, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddContinueAsNewEvent(context.Background(), taskHandler.decisionTaskCompletedID, taskHandler.decisionTaskCompletedID, gomock.Any(), gomock.Any()) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.FailWorkflowExecutionDecisionAttributes, err error) { assert.Nil(t, err) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeFailWorkflowExecution), FailWorkflowExecutionDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, err) }) } } func TestHandleDecisionCancelWorkflow(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.CancelWorkflowExecutionDecisionAttributes) attributes *types.CancelWorkflowExecutionDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CancelWorkflowExecutionDecisionAttributes, err error) }{ { name: "handler has unhandled events", attributes: &types.CancelWorkflowExecutionDecisionAttributes{}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CancelWorkflowExecutionDecisionAttributes) { taskHandler.hasUnhandledEventsBeforeDecisions = true }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CancelWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.DecisionTaskFailedCauseUnhandledDecision, *taskHandler.failDecisionCause) assert.Equal(t, "cannot process cancellation, new pending decisions were scheduled while this decision was processing", *taskHandler.failMessage) }, }, { name: "attributes validation failure", asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CancelWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.DecisionTaskFailedCauseBadCancelWorkflowExecutionAttributes, *taskHandler.failDecisionCause) assert.Nil(t, err) assert.True(t, taskHandler.stopProcessing) }, }, { name: "workflow not running", attributes: &types.CancelWorkflowExecutionDecisionAttributes{Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CancelWorkflowExecutionDecisionAttributes) { taskHandler.metricsClient = new(mocks.Client) taskHandler.logger = log.NewMockLogger(gomock.NewController(t)) taskHandler.metricsClient.(*mocks.Client).On("IncCounter", metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeCancelWorkflowCounter) taskHandler.metricsClient.(*mocks.Client).On("IncCounter", metrics.HistoryRespondDecisionTaskCompletedScope, metrics.MultipleCompletionDecisionsCounter) taskHandler.logger.(*log.MockLogger).EXPECT().Warn("Multiple completion decisions", []tag.Tag{tag.WorkflowDecisionType(int64(types.DecisionTypeCancelWorkflowExecution)), tag.ErrorTypeMultipleCompletionDecisions}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(false) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CancelWorkflowExecutionDecisionAttributes, err error) { assert.Nil(t, err) }, }, { name: "success", attributes: &types.CancelWorkflowExecutionDecisionAttributes{}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.CancelWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddWorkflowExecutionCanceledEvent(taskHandler.decisionTaskCompletedID, attr) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.CancelWorkflowExecutionDecisionAttributes, err error) { assert.Nil(t, err) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeCancelWorkflowExecution), CancelWorkflowExecutionDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, err) }) } } func TestHandleDecisionRecordMarker(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.RecordMarkerDecisionAttributes) attributes *types.RecordMarkerDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.RecordMarkerDecisionAttributes, err error) }{ { name: "blob size limit check failure", attributes: &types.RecordMarkerDecisionAttributes{MarkerName: "some-marker", Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.RecordMarkerDecisionAttributes) { taskHandler.sizeLimitChecker.blobSizeLimitError = 5 taskHandler.sizeLimitChecker.blobSizeLimitWarn = 3 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(testTaskCompletedID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonDecisionBlobSizeExceedsLimit), Details: []byte("RecordMarkerDecisionAttributes.Details exceeds size limit."), }) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.RecordMarkerDecisionAttributes, err error) { assert.True(t, taskHandler.stopProcessing) assert.Nil(t, err) }, }, { name: "attributes validation failure", asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.RecordMarkerDecisionAttributes, err error) { assert.Equal(t, types.DecisionTaskFailedCauseBadRecordMarkerAttributes, *taskHandler.failDecisionCause) assert.Nil(t, err) assert.True(t, taskHandler.stopProcessing) }, }, { name: "success", attributes: &types.RecordMarkerDecisionAttributes{MarkerName: "some-marker", Details: []byte("some-details")}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.RecordMarkerDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddRecordMarkerEvent(taskHandler.decisionTaskCompletedID, attr) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.RecordMarkerDecisionAttributes, err error) { assert.Nil(t, err) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeRecordMarker), RecordMarkerDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, err) }) } } func TestHandleDecisionScheduleActivity(t *testing.T) { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{ID: testdata.DomainID, Name: testdata.DomainName}, &persistence.DomainConfig{ Retention: 1, BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{"test-binary-checksum": {Reason: "some reason"}}}, }, cluster.TestCurrentClusterName) executionInfo := &persistence.WorkflowExecutionInfo{ DomainID: testdata.DomainID, WorkflowID: testdata.WorkflowID, WorkflowTimeout: 100, } validAttr := &types.ScheduleActivityTaskDecisionAttributes{ Domain: testdata.DomainName, TaskList: &types.TaskList{Name: testdata.TaskListName}, ActivityID: "some-activity-id", ActivityType: &types.ActivityType{Name: testdata.ActivityTypeName}, ScheduleToCloseTimeoutSeconds: func(i int32) *int32 { return &i }(100), ScheduleToStartTimeoutSeconds: func(i int32) *int32 { return &i }(20), StartToCloseTimeoutSeconds: func(i int32) *int32 { return &i }(80), Input: []byte("some-input"), } tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) attributes *types.ScheduleActivityTaskDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) }{ { name: "internal service error", attributes: &types.ScheduleActivityTaskDecisionAttributes{Domain: testdata.DomainName}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(nil, errors.New("some radom error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.NotNil(t, err) assert.Nil(t, res) assert.Equal(t, &types.InternalServiceError{Message: fmt.Sprintf("Unable to schedule activity across domain %v.", attr.GetDomain())}, err) }, }, { name: "attributes validation failure", attributes: &types.ScheduleActivityTaskDecisionAttributes{Domain: testdata.DomainName}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.Nil(t, err) assert.Nil(t, res) assert.True(t, taskHandler.stopProcessing) }, }, { name: "blob size limit check failure", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.sizeLimitChecker.blobSizeLimitWarn = 3 taskHandler.sizeLimitChecker.blobSizeLimitError = 5 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(testTaskCompletedID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonDecisionBlobSizeExceedsLimit), Details: []byte("ScheduleActivityTaskDecisionAttributes.Input exceeds size limit."), }) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.Nil(t, err) assert.Nil(t, res) assert.True(t, taskHandler.stopProcessing) }, }, { name: "success - activity started", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskScheduledEvent(context.Background(), taskHandler.decisionTaskCompletedID, attr, taskHandler.activityCountToDispatch > 0). Return(&types.HistoryEvent{}, &persistence.ActivityInfo{}, &types.ActivityLocalDispatchInfo{}, true, true, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskStartedEvent(&persistence.ActivityInfo{}, int64(0), gomock.Any(), taskHandler.identity) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.Nil(t, err) assert.Nil(t, res) }, }, { name: "token serialization failure", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskScheduledEvent(context.Background(), taskHandler.decisionTaskCompletedID, attr, taskHandler.activityCountToDispatch > 0). Return(&types.HistoryEvent{}, &persistence.ActivityInfo{}, &types.ActivityLocalDispatchInfo{}, true, false, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskStartedEvent(&persistence.ActivityInfo{}, int64(0), gomock.Any(), taskHandler.identity) taskHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Serialize(&common.TaskToken{ DomainID: testdata.DomainID, WorkflowID: testdata.WorkflowID, ActivityType: testdata.ActivityTypeName, }).Return(nil, errors.New("some error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.Equal(t, workflow.ErrSerializingToken, err) assert.Nil(t, res) }, }, { name: "success - decisionResult non nil", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskScheduledEvent(context.Background(), taskHandler.decisionTaskCompletedID, attr, taskHandler.activityCountToDispatch > 0). Return(&types.HistoryEvent{}, &persistence.ActivityInfo{}, &types.ActivityLocalDispatchInfo{}, true, false, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskStartedEvent(&persistence.ActivityInfo{}, int64(0), gomock.Any(), taskHandler.identity) taskHandler.tokenSerializer.(*common.MockTaskTokenSerializer).EXPECT().Serialize(&common.TaskToken{ DomainID: testdata.DomainID, WorkflowID: testdata.WorkflowID, ActivityType: testdata.ActivityTypeName, }).Return([]byte("some-serialized-data"), nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.Nil(t, err) assert.NotNil(t, res) assert.Equal(t, []byte("some-serialized-data"), res.activityDispatchInfo.TaskToken) }, }, { name: "failure to add activity task started", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskScheduledEvent(context.Background(), taskHandler.decisionTaskCompletedID, attr, taskHandler.activityCountToDispatch > 0). Return(&types.HistoryEvent{}, &persistence.ActivityInfo{}, &types.ActivityLocalDispatchInfo{}, true, true, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskStartedEvent(&persistence.ActivityInfo{}, int64(0), gomock.Any(), taskHandler.identity).Return(nil, errors.New("some error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.NotNil(t, err) assert.Equal(t, "some error", err.Error()) assert.Nil(t, res) }, }, { name: "activity scheduled - nil activityDispatchInfo", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskScheduledEvent(context.Background(), taskHandler.decisionTaskCompletedID, attr, taskHandler.activityCountToDispatch > 0). Return(&types.HistoryEvent{}, &persistence.ActivityInfo{}, nil, true, false, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.Nil(t, err) assert.Nil(t, res) }, }, { name: "bad request error", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskScheduledEvent(context.Background(), taskHandler.decisionTaskCompletedID, attr, taskHandler.activityCountToDispatch > 0). Return(nil, nil, nil, false, false, &types.BadRequestError{ Message: "some bad request error", }) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.Nil(t, err) assert.Nil(t, res) }, }, { name: "default error case", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskScheduledEvent(context.Background(), taskHandler.decisionTaskCompletedID, attr, taskHandler.activityCountToDispatch > 0). Return(nil, nil, nil, false, false, errors.New("some default error")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.NotNil(t, err) assert.Equal(t, "some default error", err.Error()) assert.Nil(t, res) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeScheduleActivityTask), ScheduleActivityTaskDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) assert.NotNil(t, err) assert.Equal(t, &types.BadRequestError{Message: fmt.Sprintf("Unknown decision type: %v", decision.GetDecisionType())}, err) decisionRes, err := taskHandler.handleDecisionWithResult(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, decisionRes, err) }) } } func TestHandleDecisionContinueAsNewWorkflow(t *testing.T) { executionInfo := &persistence.WorkflowExecutionInfo{ DomainID: testdata.DomainID, WorkflowID: testdata.WorkflowID, WorkflowTimeout: 100, ParentDomainID: "parent-domain-id", } validAttr := &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: &types.WorkflowType{Name: testdata.WorkflowTypeName}, TaskList: &types.TaskList{Name: testdata.TaskListName}, Input: []byte("some-input"), ExecutionStartToCloseTimeoutSeconds: func(i int32) *int32 { return &i }(100), TaskStartToCloseTimeoutSeconds: func(i int32) *int32 { return &i }(80), BackoffStartIntervalInSeconds: new(int32), FailureReason: func(i string) *string { return &i }("some reason"), SearchAttributes: &types.SearchAttributes{IndexedFields: map[string][]byte{"some-key": []byte(`"some-value"`)}}, } tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes) attributes *types.ContinueAsNewWorkflowExecutionDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes, err error) }{ { name: "handler has unhandled events", attributes: &types.ContinueAsNewWorkflowExecutionDecisionAttributes{}, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes) { taskHandler.hasUnhandledEventsBeforeDecisions = true }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.DecisionTaskFailedCauseUnhandledDecision, *taskHandler.failDecisionCause) assert.Equal(t, "cannot complete workflow, new pending decisions were scheduled while this decision was processing", *taskHandler.failMessage) }, }, { name: "attributes validation failure", expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes, err error) { assert.Equal(t, types.DecisionTaskFailedCauseBadContinueAsNewAttributes, *taskHandler.failDecisionCause) assert.Equal(t, "ContinueAsNewWorkflowExecutionDecisionAttributes is not set on decision.", *taskHandler.failMessage) }, }, { name: "blob size limit check failure", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes) { taskHandler.sizeLimitChecker.blobSizeLimitWarn = 3 taskHandler.sizeLimitChecker.blobSizeLimitError = 5 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.attrValidator.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(testdata.DomainID).Return(testdata.DomainName, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(testTaskCompletedID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonDecisionBlobSizeExceedsLimit), Details: []byte("ContinueAsNewWorkflowExecutionDecisionAttributes. Input exceeds size limit."), }) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes, err error) { assert.True(t, taskHandler.stopProcessing) assert.NoError(t, err) }, }, { name: "workflow not running", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.attrValidator.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(testdata.DomainID).Return(testdata.DomainName, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(false) taskHandler.metricsClient = new(mocks.Client) taskHandler.logger = log.NewMockLogger(gomock.NewController(t)) taskHandler.metricsClient.(*mocks.Client).On("IncCounter", metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DecisionTypeContinueAsNewCounter) taskHandler.metricsClient.(*mocks.Client).On("IncCounter", metrics.HistoryRespondDecisionTaskCompletedScope, metrics.MultipleCompletionDecisionsCounter) taskHandler.logger.(*log.MockLogger).EXPECT().Warn("Multiple completion decisions", []tag.Tag{tag.WorkflowDecisionType(int64(types.DecisionTypeContinueAsNewWorkflowExecution)), tag.ErrorTypeMultipleCompletionDecisions}) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes, err error) { assert.Nil(t, err) }, }, { name: "cancel requested - failure to add cancel event", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.attrValidator.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(testdata.DomainID).Return(testdata.DomainName, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(true, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddWorkflowExecutionCanceledEvent(taskHandler.decisionTaskCompletedID, &types.CancelWorkflowExecutionDecisionAttributes{}).Return(nil, errors.New("some error adding execution canceled event")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes, err error) { assert.ErrorContains(t, err, "some error adding execution canceled event") }, }, { name: "cancel requested - success", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.attrValidator.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(testdata.DomainID).Return(testdata.DomainName, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(true, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddWorkflowExecutionCanceledEvent(taskHandler.decisionTaskCompletedID, &types.CancelWorkflowExecutionDecisionAttributes{}).Return(&types.HistoryEvent{}, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes, err error) { assert.Nil(t, err) }, }, { name: "failure to extract parent domain name", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.attrValidator.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(testdata.DomainID).Return(testdata.DomainName, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(false, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().HasParentExecution().Return(true) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(executionInfo.ParentDomainID).Return("", errors.New("some error getting parent domain name")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes, err error) { assert.ErrorContains(t, err, "some error getting parent domain name") }, }, { name: "failure to add continueAsNew event", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.attrValidator.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(testdata.DomainID).Return(testdata.DomainName, nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsWorkflowExecutionRunning().Return(true) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().IsCancelRequested().Return(false, "") taskHandler.mutableState.(*execution.MockMutableState).EXPECT().HasParentExecution().Return(true) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomainName(executionInfo.ParentDomainID).Return("parent-domain-name", nil) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddContinueAsNewEvent(context.Background(), taskHandler.decisionTaskCompletedID, taskHandler.decisionTaskCompletedID, "parent-domain-name", attr). Return(nil, nil, errors.New("some error adding continueAsNew event")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ContinueAsNewWorkflowExecutionDecisionAttributes, err error) { assert.ErrorContains(t, err, "some error adding continueAsNew event") }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } decision := &types.Decision{ DecisionType: common.Ptr(types.DecisionTypeContinueAsNewWorkflowExecution), ContinueAsNewWorkflowExecutionDecisionAttributes: test.attributes, } err := taskHandler.handleDecision(context.Background(), decision) test.asserts(t, taskHandler, test.attributes, err) }) } } func TestValidateAttributes(t *testing.T) { t.Run("failure", func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) validationFn := func() error { return errors.New("some validation error") } failedCause := new(types.DecisionTaskFailedCause) err := taskHandler.validateDecisionAttr(validationFn, *failedCause) assert.ErrorContains(t, err, "some validation error") }) } func TestHandleDecisions(t *testing.T) { t.Run("workflow size exceeds limit", func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) taskHandler.sizeLimitChecker.historyCountLimitError = 10 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetNextEventID().Return(int64(12)) // nextEventID - 1 > historyCountLimit of 10 taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent(taskHandler.sizeLimitChecker.completedID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: common.StringPtr(common.FailureReasonSizeExceedsLimit), Details: []byte("Workflow history size / count exceeds limit."), }).Return(nil, errors.New("some error adding fail workflow event")) res, err := taskHandler.handleDecisions(context.Background(), []byte{}, []*types.Decision{}) assert.Nil(t, res) assert.ErrorContains(t, err, "some error adding fail workflow event") }) } func newTaskHandlerForTest(t *testing.T) *taskHandlerImpl { ctrl := gomock.NewController(t) testLogger := testlogger.New(t) testConfig := config.NewForTest() testConfig.ValidSearchAttributes = func(opts ...dynamicproperties.FilterOption) map[string]interface{} { validSearchAttr := make(map[string]interface{}) validSearchAttr["some-key"] = types.IndexedValueTypeString return validSearchAttr } mockMutableState := execution.NewMockMutableState(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) workflowSizeChecker := newWorkflowSizeChecker( "testDomain", testConfig.BlobSizeLimitWarn(constants.TestDomainName), testConfig.BlobSizeLimitError(constants.TestDomainName), testConfig.HistorySizeLimitWarn(constants.TestDomainName), testConfig.HistorySizeLimitError(constants.TestDomainName), testConfig.HistoryCountLimitWarn(constants.TestDomainName), testConfig.HistoryCountLimitError(constants.TestDomainName), testTaskCompletedID, mockMutableState, &persistence.ExecutionStats{}, metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}).Scope(metrics.HistoryRespondDecisionTaskCompletedScope, metrics.DomainTag(constants.TestDomainName)), testLogger, ) mockMutableState.EXPECT().HasBufferedEvents().Return(false) taskHandler := newDecisionTaskHandler( testdata.Identity, testTaskCompletedID, constants.TestLocalDomainEntry, mockMutableState, newAttrValidator(mockDomainCache, metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), testConfig, testlogger.New(t)), workflowSizeChecker, common.NewMockTaskTokenSerializer(ctrl), testLogger, mockDomainCache, metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), testConfig, ) return taskHandler } func TestHandleFailWorkflowError(t *testing.T) { tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl) asserts func(t *testing.T, taskHandler *taskHandlerImpl, err error) }{ { name: "success - workflow fails due to too many pending activities", expectMockCalls: func(taskHandler *taskHandlerImpl) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent( taskHandler.decisionTaskCompletedID, gomock.Any(), ).Return(&types.HistoryEvent{}, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, err error) { assert.Nil(t, err) assert.True(t, taskHandler.stopProcessing) }, }, { name: "failure - AddFailWorkflowEvent returns error", expectMockCalls: func(taskHandler *taskHandlerImpl) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent( taskHandler.decisionTaskCompletedID, gomock.Any(), ).Return(nil, errors.New("failed to add fail workflow event")) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, err error) { assert.Error(t, err) assert.Equal(t, "failed to add fail workflow event", err.Error()) assert.True(t, taskHandler.stopProcessing) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler) } err := taskHandler.handleFailWorkflowError(common.FailureReasonPendingActivityExceedsLimit, execution.ErrTooManyPendingActivities.Error()) test.asserts(t, taskHandler, err) }) } } func TestHandleDecisionScheduleActivityWithTooManyPendingActivities(t *testing.T) { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{ID: testdata.DomainID, Name: testdata.DomainName}, &persistence.DomainConfig{ Retention: 1, }, cluster.TestCurrentClusterName) executionInfo := &persistence.WorkflowExecutionInfo{ DomainID: testdata.DomainID, WorkflowID: testdata.WorkflowID, WorkflowTimeout: 100, } validAttr := &types.ScheduleActivityTaskDecisionAttributes{ Domain: testdata.DomainName, TaskList: &types.TaskList{Name: testdata.TaskListName}, ActivityID: "some-activity-id", ActivityType: &types.ActivityType{Name: testdata.ActivityTypeName}, ScheduleToCloseTimeoutSeconds: func(i int32) *int32 { return &i }(100), ScheduleToStartTimeoutSeconds: func(i int32) *int32 { return &i }(20), StartToCloseTimeoutSeconds: func(i int32) *int32 { return &i }(80), Input: []byte("some-input"), } tests := []struct { name string expectMockCalls func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) attributes *types.ScheduleActivityTaskDecisionAttributes asserts func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) }{ { name: "ErrTooManyPendingActivities - workflow fails", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) // Mock AddActivityTaskScheduledEvent to return ErrTooManyPendingActivities taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskScheduledEvent( context.Background(), taskHandler.decisionTaskCompletedID, attr, taskHandler.activityCountToDispatch > 0, ).Return(nil, nil, nil, false, false, execution.ErrTooManyPendingActivities) taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddFailWorkflowEvent( taskHandler.decisionTaskCompletedID, gomock.Any(), ).Return(&types.HistoryEvent{}, nil) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.Nil(t, err) assert.Nil(t, res) assert.True(t, taskHandler.stopProcessing) }, }, { name: "Other InternalServiceError - passes through", attributes: validAttr, expectMockCalls: func(taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes) { taskHandler.mutableState.(*execution.MockMutableState).EXPECT().GetExecutionInfo().Return(executionInfo).Times(2) taskHandler.domainCache.(*cache.MockDomainCache).EXPECT().GetDomain(attr.GetDomain()).Return(domainEntry, nil) // Mock AddActivityTaskScheduledEvent to return different InternalServiceError taskHandler.mutableState.(*execution.MockMutableState).EXPECT().AddActivityTaskScheduledEvent( context.Background(), taskHandler.decisionTaskCompletedID, attr, taskHandler.activityCountToDispatch > 0, ).Return(nil, nil, nil, false, false, &types.InternalServiceError{Message: "Some other internal service error"}) }, asserts: func(t *testing.T, taskHandler *taskHandlerImpl, attr *types.ScheduleActivityTaskDecisionAttributes, res *decisionResult, err error) { assert.NotNil(t, err) assert.Equal(t, "Some other internal service error", err.Error()) assert.Nil(t, res) assert.False(t, taskHandler.stopProcessing) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { taskHandler := newTaskHandlerForTest(t) if test.expectMockCalls != nil { test.expectMockCalls(taskHandler, test.attributes) } res, err := taskHandler.handleDecisionScheduleActivity(context.Background(), test.attributes) test.asserts(t, taskHandler, test.attributes, res, err) }) } } ================================================ FILE: service/history/engine/engineimpl/describe_mutable_state.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "encoding/json" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" ) func (e *historyEngineImpl) DescribeMutableState( ctx context.Context, request *types.DescribeMutableStateRequest, ) (response *types.DescribeMutableStateResponse, retError error) { if err := common.ValidateDomainUUID(request.DomainUUID); err != nil { return nil, err } domainID := request.DomainUUID execution := types.WorkflowExecution{ WorkflowID: request.Execution.WorkflowID, RunID: request.Execution.RunID, } cacheCtx, dbCtx, release, cacheHit, err := e.executionCache.GetAndCreateWorkflowExecution( ctx, domainID, execution, ) if err != nil { return nil, err } defer func() { release(retError) }() response = &types.DescribeMutableStateResponse{} if cacheHit { if msb := cacheCtx.GetWorkflowExecution(); msb != nil { response.MutableStateInCache, err = e.toMutableStateJSON(msb) if err != nil { return nil, err } } } msb, err := dbCtx.LoadWorkflowExecution(ctx) if err != nil { return nil, err } response.MutableStateInDatabase, err = e.toMutableStateJSON(msb) if err != nil { return nil, err } return response, nil } func (e *historyEngineImpl) toMutableStateJSON(msb execution.MutableState) (string, error) { ms := msb.CopyToPersistence() jsonBytes, err := json.Marshal(ms) if err != nil { return "", err } return string(jsonBytes), nil } ================================================ FILE: service/history/engine/engineimpl/describe_mutable_state_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package engineimpl import ( "context" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/execution" ) func TestDescribeMutableState_Success(t *testing.T) { ctrl := gomock.NewController(t) cachedExecutionInfo := getWorkflowMutableState() dbExecutionInfo := getWorkflowMutableState() // Make sure the db is different from the cache, so we can verify the correct value // goes to the correct field in the response dbExecutionInfo.ExecutionInfo.DomainID = constants.TestDomainID + "other" // Cache context mock mutableStateFromCacheMock := execution.NewMockMutableState(ctrl) mutableStateFromCacheMock.EXPECT().CopyToPersistence().Return(cachedExecutionInfo) cacheContextMock := execution.NewMockContext(ctrl) cacheContextMock.EXPECT().GetWorkflowExecution().Return(mutableStateFromCacheMock) // DB context mock mutableStateFromDBMock := execution.NewMockMutableState(ctrl) mutableStateFromDBMock.EXPECT().CopyToPersistence().Return(dbExecutionInfo) dbContextMock := execution.NewMockContext(ctrl) dbContextMock.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(mutableStateFromDBMock, nil) releaseFunctionCalled := false cacheMock := execution.NewMockCache(ctrl) cacheMock.EXPECT(). GetAndCreateWorkflowExecution(gomock.Any(), constants.TestDomainID, *getExpectedWFExecution()). Return(cacheContextMock, dbContextMock, func(err error) { releaseFunctionCalled = true assert.NoError(t, err) }, true, nil) engine := &historyEngineImpl{ executionCache: cacheMock, } resp, err := engine.DescribeMutableState(context.TODO(), &types.DescribeMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: getExpectedWFExecution(), }) assert.NoError(t, err) expectedCachedExecutionInfo, err := json.Marshal(cachedExecutionInfo) require.NoError(t, err) expectedDBExecutionInfo, err := json.Marshal(dbExecutionInfo) require.NoError(t, err) // The response should contain the json representation of the execution info assert.Equal(t, string(expectedCachedExecutionInfo), resp.MutableStateInCache) assert.Equal(t, string(expectedDBExecutionInfo), resp.MutableStateInDatabase) // The release function should have been called assert.True(t, releaseFunctionCalled) } func TestDescribeMutableState_Error_UnknownDomain(t *testing.T) { engine := &historyEngineImpl{} _, err := engine.DescribeMutableState(context.TODO(), &types.DescribeMutableStateRequest{ DomainUUID: "This is not a uuid", }) assert.Error(t, err) assert.ErrorAs(t, err, new(*types.BadRequestError)) assert.ErrorContains(t, err, "Invalid domain UUID") } func TestDescribeMutableState_Error_GetAndCreateError(t *testing.T) { ctrl := gomock.NewController(t) cacheMock := execution.NewMockCache(ctrl) cacheMock.EXPECT(). GetAndCreateWorkflowExecution(gomock.Any(), constants.TestDomainID, *getExpectedWFExecution()). Return(nil, nil, nil, true, assert.AnError) engine := &historyEngineImpl{ executionCache: cacheMock, } _, err := engine.DescribeMutableState(context.TODO(), &types.DescribeMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: getExpectedWFExecution(), }) assert.Error(t, err) assert.ErrorIs(t, err, assert.AnError) } func TestDescribeMutableState_Error_LoadFromDBError(t *testing.T) { ctrl := gomock.NewController(t) // DB context mock returns error dbContextMock := execution.NewMockContext(ctrl) dbContextMock.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(nil, assert.AnError) releaseFunctionCalled := false cacheMock := execution.NewMockCache(ctrl) cacheMock.EXPECT(). GetAndCreateWorkflowExecution(gomock.Any(), constants.TestDomainID, *getExpectedWFExecution()). Return(nil, dbContextMock, func(err error) { releaseFunctionCalled = true assert.ErrorIs(t, err, assert.AnError) }, false, nil) engine := &historyEngineImpl{ executionCache: cacheMock, } _, err := engine.DescribeMutableState(context.TODO(), &types.DescribeMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: getExpectedWFExecution(), }) assert.ErrorIs(t, err, assert.AnError) // release function should have been called assert.True(t, releaseFunctionCalled) } // The content of this struct is not important for the purpose of this test, // we just want to check that the method returns the json representation of the struct func getWorkflowMutableState() *persistence.WorkflowMutableState { return &persistence.WorkflowMutableState{ ActivityInfos: map[int64]*persistence.ActivityInfo{}, TimerInfos: nil, ChildExecutionInfos: nil, RequestCancelInfos: nil, SignalInfos: nil, SignalRequestedIDs: nil, ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, ExecutionStats: nil, BufferedEvents: nil, VersionHistories: nil, ReplicationState: nil, Checksum: checksum.Checksum{}, } } func getExpectedWFExecution() *types.WorkflowExecution { return &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } } ================================================ FILE: service/history/engine/engineimpl/describe_queues.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "fmt" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/queue" ) func (e *historyEngineImpl) DescribeTransferQueue( ctx context.Context, clusterName string, ) (*types.DescribeQueueResponse, error) { return e.describeQueue(ctx, persistence.HistoryTaskCategoryTransfer, clusterName) } func (e *historyEngineImpl) DescribeTimerQueue( ctx context.Context, clusterName string, ) (*types.DescribeQueueResponse, error) { return e.describeQueue(ctx, persistence.HistoryTaskCategoryTimer, clusterName) } func (e *historyEngineImpl) describeQueue( ctx context.Context, category persistence.HistoryTaskCategory, clusterName string, ) (*types.DescribeQueueResponse, error) { queueProcessor, ok := e.queueProcessors[category] if !ok { return nil, fmt.Errorf("queue processor not found for category %v", category) } resp, err := queueProcessor.HandleAction(ctx, clusterName, queue.NewGetStateAction()) if err != nil { return nil, err } serializedStates := make([]string, 0, len(resp.GetStateActionResult.States)) for _, state := range resp.GetStateActionResult.States { serializedStates = append(serializedStates, e.serializeQueueState(state)) } return &types.DescribeQueueResponse{ ProcessingQueueStates: serializedStates, }, nil } func (e *historyEngineImpl) serializeQueueState( state queue.ProcessingQueueState, ) string { return fmt.Sprintf("%v", state) } ================================================ FILE: service/history/engine/engineimpl/describe_workflow_execution.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" ) // DescribeWorkflowExecution returns information about the specified workflow execution. func (e *historyEngineImpl) DescribeWorkflowExecution( ctx context.Context, request *types.HistoryDescribeWorkflowExecutionRequest, ) (retResp *types.DescribeWorkflowExecutionResponse, retError error) { if err := validateDescribeWorkflowExecutionRequest(request); err != nil { return nil, err } domainID := request.DomainUUID wfExecution := *request.Request.Execution wfContext, release, err0 := e.executionCache.GetOrCreateWorkflowExecution(ctx, domainID, wfExecution) if err0 != nil { return nil, err0 } defer func() { release(retError) }() mutableState, err1 := wfContext.LoadWorkflowExecution(ctx) if err1 != nil { return nil, err1 } // If history is corrupted, return an error to the end user if corrupted, err := e.checkForHistoryCorruptions(ctx, mutableState); err != nil { return nil, err } else if corrupted { return nil, &types.EntityNotExistsError{Message: "Workflow execution corrupted."} } domainCache := e.shard.GetDomainCache() result, err := createDescribeWorkflowExecutionResponse(ctx, mutableState, domainCache) if err != nil { return nil, err } return result, nil } // validateDescribeWorkflowExecutionRequest validates the input request func validateDescribeWorkflowExecutionRequest(request *types.HistoryDescribeWorkflowExecutionRequest) error { return common.ValidateDomainUUID(request.DomainUUID) } func createDescribeWorkflowExecutionResponse(ctx context.Context, mutableState execution.MutableState, domainCache cache.DomainCache) (*types.DescribeWorkflowExecutionResponse, error) { executionInfo := mutableState.GetExecutionInfo() executionConfiguration, err := mapWorkflowExecutionConfiguration(executionInfo) if err != nil { return nil, err } result := &types.DescribeWorkflowExecutionResponse{ ExecutionConfiguration: executionConfiguration, } // TODO: we need to consider adding execution time to mutable state // For now execution time will be calculated based on start time and cron schedule/retry policy // each time DescribeWorkflowExecution is called. startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return nil, err } var completionEvent *types.HistoryEvent if executionInfo.State == persistence.WorkflowStateCompleted { completionEvent, err = mutableState.GetCompletionEvent(ctx) if err != nil { return nil, err } } historyLength := mutableState.GetNextEventID() - constants.FirstEventID workflowExecutionInfo, err := mapWorkflowExecutionInfo(executionInfo, startEvent, domainCache, historyLength, completionEvent) if err != nil { return nil, err } result.WorkflowExecutionInfo = workflowExecutionInfo pendingActivityInfos := mutableState.GetPendingActivityInfos() for _, ai := range pendingActivityInfos { scheduledEvent, err := mutableState.GetActivityScheduledEvent(ctx, ai.ScheduleID) if err != nil { return nil, err } p := mapPendingActivityInfo(ai, scheduledEvent) result.PendingActivities = append(result.PendingActivities, p) } childExecutions := mutableState.GetPendingChildExecutionInfos() domainEntry := mutableState.GetDomainEntry() for _, childExecution := range childExecutions { pendingChild, err := mapPendingChildExecutionInfo(childExecution, domainEntry, domainCache) if err != nil { return nil, err } result.PendingChildren = append(result.PendingChildren, pendingChild) } if di, ok := mutableState.GetPendingDecision(); ok { result.PendingDecision = mapPendingDecisionInfo(di) } return result, nil } func mapWorkflowExecutionConfiguration(executionInfo *persistence.WorkflowExecutionInfo) (*types.WorkflowExecutionConfiguration, error) { return &types.WorkflowExecutionConfiguration{ TaskList: mapDecisionInfoToTaskList(executionInfo), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(executionInfo.WorkflowTimeout), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(executionInfo.DecisionStartToCloseTimeout), }, nil } func mapWorkflowExecutionInfo(executionInfo *persistence.WorkflowExecutionInfo, startEvent *types.HistoryEvent, domainCache cache.DomainCache, historyLength int64, completionEvent *types.HistoryEvent) (*types.WorkflowExecutionInfo, error) { result := &types.WorkflowExecutionInfo{ Execution: &types.WorkflowExecution{ WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskList: mapDecisionInfoToTaskList(executionInfo), Type: &types.WorkflowType{Name: executionInfo.WorkflowTypeName}, StartTime: common.Int64Ptr(executionInfo.StartTimestamp.UnixNano()), HistoryLength: historyLength, AutoResetPoints: executionInfo.AutoResetPoints, Memo: &types.Memo{Fields: executionInfo.CopyMemo()}, IsCron: len(executionInfo.CronSchedule) > 0, UpdateTime: common.Int64Ptr(executionInfo.LastUpdatedTimestamp.UnixNano()), SearchAttributes: &types.SearchAttributes{IndexedFields: executionInfo.CopySearchAttributes()}, PartitionConfig: executionInfo.CopyPartitionConfig(), CronOverlapPolicy: &executionInfo.CronOverlapPolicy, ActiveClusterSelectionPolicy: executionInfo.ActiveClusterSelectionPolicy, } backoffDuration := time.Duration(startEvent.GetWorkflowExecutionStartedEventAttributes().GetFirstDecisionTaskBackoffSeconds()) * time.Second result.ExecutionTime = common.Int64Ptr(result.GetStartTime() + backoffDuration.Nanoseconds()) if executionInfo.ParentRunID != "" { result.ParentExecution = &types.WorkflowExecution{ WorkflowID: executionInfo.ParentWorkflowID, RunID: executionInfo.ParentRunID, } result.ParentDomainID = common.StringPtr(executionInfo.ParentDomainID) result.ParentInitiatedID = common.Int64Ptr(executionInfo.InitiatedID) parentDomain, err := domainCache.GetDomainName(executionInfo.ParentDomainID) if err != nil { return nil, err } result.ParentDomain = common.StringPtr(parentDomain) } if executionInfo.State == persistence.WorkflowStateCompleted { result.CloseStatus = persistence.ToInternalWorkflowExecutionCloseStatus(executionInfo.CloseStatus) if completionEvent != nil { result.CloseTime = common.Int64Ptr(completionEvent.GetTimestamp()) } } return result, nil } func mapPendingActivityInfo(ai *persistence.ActivityInfo, activityScheduledEvent *types.HistoryEvent) *types.PendingActivityInfo { p := &types.PendingActivityInfo{ ActivityID: ai.ActivityID, ScheduleID: ai.ScheduleID, } state := types.PendingActivityStateScheduled if ai.CancelRequested { state = types.PendingActivityStateCancelRequested } else if ai.StartedID != constants.EmptyEventID { state = types.PendingActivityStateStarted } p.State = &state lastHeartbeatUnixNano := ai.LastHeartBeatUpdatedTime.UnixNano() if lastHeartbeatUnixNano > 0 { p.LastHeartbeatTimestamp = common.Int64Ptr(lastHeartbeatUnixNano) p.HeartbeatDetails = ai.Details } p.ActivityType = activityScheduledEvent.ActivityTaskScheduledEventAttributes.ActivityType if state == types.PendingActivityStateScheduled { p.ScheduledTimestamp = common.Int64Ptr(ai.ScheduledTime.UnixNano()) } else { p.LastStartedTimestamp = common.Int64Ptr(ai.StartedTime.UnixNano()) } if ai.HasRetryPolicy { p.Attempt = ai.Attempt if !ai.ExpirationTime.IsZero() { p.ExpirationTimestamp = common.Int64Ptr(ai.ExpirationTime.UnixNano()) } if ai.MaximumAttempts != 0 { p.MaximumAttempts = ai.MaximumAttempts } if ai.LastFailureReason != "" { p.LastFailureReason = common.StringPtr(ai.LastFailureReason) p.LastFailureDetails = ai.LastFailureDetails } if ai.LastWorkerIdentity != "" { p.LastWorkerIdentity = ai.LastWorkerIdentity } if ai.StartedIdentity != "" { p.StartedWorkerIdentity = ai.StartedIdentity } } return p } func mapPendingChildExecutionInfo(childExecution *persistence.ChildExecutionInfo, domainEntry *cache.DomainCacheEntry, domainCache cache.DomainCache) (*types.PendingChildExecutionInfo, error) { childDomainName, err := execution.GetChildExecutionDomainName( childExecution, domainCache, domainEntry, ) if err != nil { if !common.IsEntityNotExistsError(err) { return nil, err } // child domain already deleted, instead of failing the request, // return domainID instead since this field is only for information purpose childDomainName = childExecution.DomainID } return &types.PendingChildExecutionInfo{ Domain: childDomainName, WorkflowID: childExecution.StartedWorkflowID, RunID: childExecution.StartedRunID, WorkflowTypeName: childExecution.WorkflowTypeName, InitiatedID: childExecution.InitiatedID, ParentClosePolicy: &childExecution.ParentClosePolicy, }, nil } func mapPendingDecisionInfo(di *execution.DecisionInfo) *types.PendingDecisionInfo { pendingDecision := &types.PendingDecisionInfo{ State: types.PendingDecisionStateScheduled.Ptr(), ScheduledTimestamp: common.Int64Ptr(di.ScheduledTimestamp), Attempt: di.Attempt, OriginalScheduledTimestamp: common.Int64Ptr(di.OriginalScheduledTimestamp), ScheduleID: di.ScheduleID, } if di.StartedID != constants.EmptyEventID { pendingDecision.State = types.PendingDecisionStateStarted.Ptr() pendingDecision.StartedTimestamp = common.Int64Ptr(di.StartedTimestamp) } return pendingDecision } func mapDecisionInfoToTaskList(executionInfo *persistence.WorkflowExecutionInfo) *types.TaskList { return &types.TaskList{ Name: executionInfo.TaskList, Kind: executionInfo.TaskListKind.Ptr(), } } ================================================ FILE: service/history/engine/engineimpl/describe_workflow_execution_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package engineimpl import ( ctx "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" historyConstants "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) func TestDescribeWorkflowExecution(t *testing.T) { testDomainUUID := uuid.New() testCases := []struct { name string setupMocks func(*execution.MockCache, *execution.MockContext, *execution.MockMutableState, *shard.MockContext, string) enableCorruptionCheck bool expectError bool errorContains string }{ { name: "Success - happy path", setupMocks: func(mockExecutionCache *execution.MockCache, mockContext *execution.MockContext, mockMutableState *execution.MockMutableState, mockShard *shard.MockContext, domainUUID string) { wfExecution := types.WorkflowExecution{WorkflowID: "test-wf", RunID: "test-run"} mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), domainUUID, wfExecution).Return(mockContext, func(error) {}, nil) mockContext.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(mockMutableState, nil) // Mock for checkForHistoryCorruptions (GetDomainEntry() call) domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: "test-domain-name"}, &persistence.DomainConfig{}, "test-cluster", ) mockMutableState.EXPECT().GetDomainEntry().Return(domainEntry) // Mock for createDescribeWorkflowExecutionResponse mockShard.EXPECT().GetDomainCache().Return(cache.NewMockDomainCache(gomock.NewController(t))) mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ TaskList: "test-task-list", WorkflowTimeout: 1800, DecisionStartToCloseTimeout: 30, }) mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(&types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Int32Ptr(5), }, }, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(10)) mockMutableState.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{}) mockMutableState.EXPECT().GetPendingChildExecutionInfos().Return(map[int64]*persistence.ChildExecutionInfo{}) mockMutableState.EXPECT().GetDomainEntry().Return(domainEntry) mockMutableState.EXPECT().GetPendingDecision().Return(nil, false) }, enableCorruptionCheck: false, expectError: false, }, { name: "Error - execution cache GetOrCreateWorkflowExecution fails", setupMocks: func(mockExecutionCache *execution.MockCache, mockContext *execution.MockContext, mockMutableState *execution.MockMutableState, mockShard *shard.MockContext, domainUUID string) { execution := types.WorkflowExecution{WorkflowID: "test-wf", RunID: "test-run"} mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), domainUUID, execution).Return(nil, nil, &types.InternalServiceError{Message: "Cache error"}) }, enableCorruptionCheck: false, expectError: true, errorContains: "Cache error", }, { name: "Error - LoadWorkflowExecution fails", setupMocks: func(mockExecutionCache *execution.MockCache, mockContext *execution.MockContext, mockMutableState *execution.MockMutableState, mockShard *shard.MockContext, domainUUID string) { wfExecution := types.WorkflowExecution{WorkflowID: "test-wf", RunID: "test-run"} mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), domainUUID, wfExecution).Return(mockContext, func(error) {}, nil) mockContext.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(nil, &types.InternalServiceError{Message: "Load error"}) }, enableCorruptionCheck: false, expectError: true, errorContains: "Load error", }, { name: "Error - createDescribeWorkflowExecutionResponse fails", setupMocks: func(mockExecutionCache *execution.MockCache, mockContext *execution.MockContext, mockMutableState *execution.MockMutableState, mockShard *shard.MockContext, domainUUID string) { wfExecution := types.WorkflowExecution{WorkflowID: "test-wf", RunID: "test-run"} mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), domainUUID, wfExecution).Return(mockContext, func(error) {}, nil) mockContext.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(mockMutableState, nil) // Mock for checkForHistoryCorruptions (GetDomainEntry() call) domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: "test-domain-name"}, &persistence.DomainConfig{}, "test-cluster", ) mockMutableState.EXPECT().GetDomainEntry().Return(domainEntry) // Mock for createDescribeWorkflowExecutionResponse failure mockShard.EXPECT().GetDomainCache().Return(cache.NewMockDomainCache(gomock.NewController(t))) mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(nil, &types.InternalServiceError{Message: "Start event error"}) }, enableCorruptionCheck: false, expectError: true, errorContains: "Start event error", }, { name: "Error - history corruption detected (missing start event)", setupMocks: func(mockExecutionCache *execution.MockCache, mockContext *execution.MockContext, mockMutableState *execution.MockMutableState, mockShard *shard.MockContext, domainUUID string) { wfExecution := types.WorkflowExecution{WorkflowID: "test-wf", RunID: "test-run"} mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), domainUUID, wfExecution).Return(mockContext, func(error) {}, nil) mockContext.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(mockMutableState, nil) // Mock for checkForHistoryCorruptions domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: "test-domain-name"}, &persistence.DomainConfig{}, "test-cluster", ) mockMutableState.EXPECT().GetDomainEntry().Return(domainEntry) // This will trigger corruption detection mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(nil, execution.ErrMissingWorkflowStartEvent) // Mock GetExecutionInfo() call for corruption marking mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ WorkflowID: "test-wf", RunID: "test-run", WorkflowTypeName: "test-type", }) }, enableCorruptionCheck: true, expectError: true, errorContains: "Workflow execution corrupted.", }, { name: "Error - history corruption check fails with non-start-event error", setupMocks: func(mockExecutionCache *execution.MockCache, mockContext *execution.MockContext, mockMutableState *execution.MockMutableState, mockShard *shard.MockContext, domainUUID string) { wfExecution := types.WorkflowExecution{WorkflowID: "test-wf", RunID: "test-run"} mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), domainUUID, wfExecution).Return(mockContext, func(error) {}, nil) mockContext.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(mockMutableState, nil) // Mock for checkForHistoryCorruptions domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{Name: "test-domain-name"}, &persistence.DomainConfig{}, "test-cluster", ) mockMutableState.EXPECT().GetDomainEntry().Return(domainEntry) // This will trigger corruption check failure with different error mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(nil, &types.InternalServiceError{Message: "Database error"}) // Mock GetExecutionInfo() call for corruption marking mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ WorkflowID: "test-wf", RunID: "test-run", WorkflowTypeName: "test-type", }) }, enableCorruptionCheck: true, expectError: true, errorContains: "Database error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockExecutionCache := execution.NewMockCache(ctrl) mockContext := execution.NewMockContext(ctrl) mockMutableState := execution.NewMockMutableState(ctrl) mockShard := shard.NewMockContext(ctrl) tc.setupMocks(mockExecutionCache, mockContext, mockMutableState, mockShard, testDomainUUID) // Set up config with explicit corruption check setting cfg := &config.Config{} cfg.EnableHistoryCorruptionCheck = func(domain string) bool { return tc.enableCorruptionCheck } engine := &historyEngineImpl{ executionCache: mockExecutionCache, shard: mockShard, config: cfg, logger: testlogger.New(t), } result, err := engine.DescribeWorkflowExecution(ctx.Background(), &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: testDomainUUID, Request: &types.DescribeWorkflowExecutionRequest{ Domain: "test-domain-name", Execution: &types.WorkflowExecution{WorkflowID: "test-wf", RunID: "test-run"}, }, }) if tc.expectError { assert.Error(t, err) if tc.errorContains != "" { assert.Contains(t, err.Error(), tc.errorContains) } assert.Nil(t, result) } else { assert.NoError(t, err) assert.NotNil(t, result) } }) } } func TestCreateDescribeWorkflowExecutionResponse(t *testing.T) { testCases := []struct { name string setupMocks func(*execution.MockMutableState, *cache.MockDomainCache) expectError bool errorContains string verifyResult func(*testing.T, *types.DescribeWorkflowExecutionResponse) }{ { name: "Success - no pending activities, children, or decisions", setupMocks: func(mockMutableState *execution.MockMutableState, mockDomainCache *cache.MockDomainCache) { executionInfo := &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", TaskList: "test-task-list", WorkflowTimeout: 1800, DecisionStartToCloseTimeout: 30, WorkflowTypeName: "test-workflow-type", State: persistence.WorkflowStateRunning, } startEvent := &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Int32Ptr(5), }, } mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo) mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(startEvent, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(10)) mockMutableState.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{}) mockMutableState.EXPECT().GetPendingChildExecutionInfos().Return(map[int64]*persistence.ChildExecutionInfo{}) mockMutableState.EXPECT().GetDomainEntry().Return(&cache.DomainCacheEntry{}) mockMutableState.EXPECT().GetPendingDecision().Return(nil, false) }, expectError: false, verifyResult: func(t *testing.T, result *types.DescribeWorkflowExecutionResponse) { assert.NotNil(t, result.ExecutionConfiguration) assert.NotNil(t, result.WorkflowExecutionInfo) assert.Empty(t, result.PendingActivities) assert.Empty(t, result.PendingChildren) assert.Nil(t, result.PendingDecision) }, }, { name: "Error - GetStartEvent fails", setupMocks: func(mockMutableState *execution.MockMutableState, mockDomainCache *cache.MockDomainCache) { executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: "test-task-list", WorkflowTimeout: 1800, DecisionStartToCloseTimeout: 30, } mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo) mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(nil, &types.InternalServiceError{Message: "Failed to get start event"}) }, expectError: true, errorContains: "Failed to get start event", }, { name: "Error - GetCompletionEvent fails for completed workflow", setupMocks: func(mockMutableState *execution.MockMutableState, mockDomainCache *cache.MockDomainCache) { executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: "test-task-list", WorkflowTimeout: 1800, DecisionStartToCloseTimeout: 30, State: persistence.WorkflowStateCompleted, } startEvent := &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Int32Ptr(5), }, } mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo) mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(startEvent, nil) mockMutableState.EXPECT().GetCompletionEvent(gomock.Any()).Return(nil, &types.InternalServiceError{Message: "Failed to get completion event"}) }, expectError: true, errorContains: "Failed to get completion event", }, { name: "Error - mapWorkflowExecutionInfo fails due to domain cache error", setupMocks: func(mockMutableState *execution.MockMutableState, mockDomainCache *cache.MockDomainCache) { executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: "test-task-list", WorkflowTimeout: 1800, DecisionStartToCloseTimeout: 30, ParentDomainID: "parent-domain-id", ParentWorkflowID: "parent-workflow-id", ParentRunID: "parent-run-id", State: persistence.WorkflowStateRunning, } startEvent := &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Int32Ptr(5), }, } mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo) mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(startEvent, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(10)) mockDomainCache.EXPECT().GetDomainName("parent-domain-id").Return("", &types.InternalServiceError{Message: "Domain cache error"}) }, expectError: true, errorContains: "Domain cache error", }, { name: "Error - GetActivityScheduledEvent fails", setupMocks: func(mockMutableState *execution.MockMutableState, mockDomainCache *cache.MockDomainCache) { executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: "test-task-list", WorkflowTimeout: 1800, DecisionStartToCloseTimeout: 30, State: persistence.WorkflowStateRunning, } startEvent := &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Int32Ptr(5), }, } activityInfo := &persistence.ActivityInfo{ ScheduleID: 123, ActivityID: "test-activity", } mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo) mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(startEvent, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(10)) mockMutableState.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{123: activityInfo}) mockMutableState.EXPECT().GetActivityScheduledEvent(gomock.Any(), int64(123)).Return(nil, &types.InternalServiceError{Message: "Failed to get activity scheduled event"}) }, expectError: true, errorContains: "Failed to get activity scheduled event", }, { name: "Error - mapPendingChildExecutionInfo fails", setupMocks: func(mockMutableState *execution.MockMutableState, mockDomainCache *cache.MockDomainCache) { executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: "test-task-list", WorkflowTimeout: 1800, DecisionStartToCloseTimeout: 30, State: persistence.WorkflowStateRunning, } startEvent := &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Int32Ptr(5), }, } childExecutionInfo := &persistence.ChildExecutionInfo{ InitiatedID: 456, DomainID: "child-domain-id", StartedWorkflowID: "child-workflow-id", StartedRunID: "child-run-id", WorkflowTypeName: "child-workflow-type", } domainEntry := &cache.DomainCacheEntry{} mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo) mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(startEvent, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(10)) mockMutableState.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{}) mockMutableState.EXPECT().GetPendingChildExecutionInfos().Return(map[int64]*persistence.ChildExecutionInfo{456: childExecutionInfo}) mockMutableState.EXPECT().GetDomainEntry().Return(domainEntry) mockDomainCache.EXPECT().GetDomainName("child-domain-id").Return("", &types.InternalServiceError{Message: "Child domain cache error"}) }, expectError: true, errorContains: "Child domain cache error", }, { name: "Success - with pending activities, children, and decision", setupMocks: func(mockMutableState *execution.MockMutableState, mockDomainCache *cache.MockDomainCache) { executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: "test-task-list", WorkflowTimeout: 1800, DecisionStartToCloseTimeout: 30, State: persistence.WorkflowStateRunning, } startEvent := &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Int32Ptr(5), }, } activityInfo := &persistence.ActivityInfo{ ScheduleID: 123, ActivityID: "test-activity", } scheduledEvent := &types.HistoryEvent{ ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityType: &types.ActivityType{Name: "test-activity-type"}, }, } childExecutionInfo := &persistence.ChildExecutionInfo{ InitiatedID: 456, DomainID: "child-domain-id", StartedWorkflowID: "child-workflow-id", StartedRunID: "child-run-id", WorkflowTypeName: "child-workflow-type", } domainEntry := &cache.DomainCacheEntry{} decisionInfo := &execution.DecisionInfo{ ScheduleID: 789, StartedID: constants.EmptyEventID, } mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo) mockMutableState.EXPECT().GetStartEvent(gomock.Any()).Return(startEvent, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(10)) mockMutableState.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{123: activityInfo}) mockMutableState.EXPECT().GetActivityScheduledEvent(gomock.Any(), int64(123)).Return(scheduledEvent, nil) mockMutableState.EXPECT().GetPendingChildExecutionInfos().Return(map[int64]*persistence.ChildExecutionInfo{456: childExecutionInfo}) mockMutableState.EXPECT().GetDomainEntry().Return(domainEntry) mockDomainCache.EXPECT().GetDomainName("child-domain-id").Return("child-domain-name", nil) mockMutableState.EXPECT().GetPendingDecision().Return(decisionInfo, true) }, expectError: false, verifyResult: func(t *testing.T, result *types.DescribeWorkflowExecutionResponse) { assert.NotNil(t, result.ExecutionConfiguration) assert.NotNil(t, result.WorkflowExecutionInfo) assert.Len(t, result.PendingActivities, 1) assert.Len(t, result.PendingChildren, 1) assert.NotNil(t, result.PendingDecision) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockMutableState := execution.NewMockMutableState(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) tc.setupMocks(mockMutableState, mockDomainCache) result, err := createDescribeWorkflowExecutionResponse(ctx.Background(), mockMutableState, mockDomainCache) if tc.expectError { assert.Error(t, err) if tc.errorContains != "" { assert.Contains(t, err.Error(), tc.errorContains) } assert.Nil(t, result) } else { assert.NoError(t, err) assert.NotNil(t, result) if tc.verifyResult != nil { tc.verifyResult(t, result) } } }) } } func TestMapWorkflowExecutionConfiguration(t *testing.T) { testCases := []struct { name string executionInfo *persistence.WorkflowExecutionInfo expected *types.WorkflowExecutionConfiguration }{ { name: "Success - all fields present", executionInfo: &persistence.WorkflowExecutionInfo{ TaskList: "test-task-list", TaskListKind: types.TaskListKindSticky, WorkflowTimeout: 1800, DecisionStartToCloseTimeout: 30, }, expected: &types.WorkflowExecutionConfiguration{ TaskList: &types.TaskList{ Name: "test-task-list", Kind: types.TaskListKindSticky.Ptr(), }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1800), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(30), }, }, { name: "Success - zero values", executionInfo: &persistence.WorkflowExecutionInfo{}, expected: &types.WorkflowExecutionConfiguration{ TaskList: &types.TaskList{Name: "", Kind: types.TaskListKindNormal.Ptr()}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(0), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(0), }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result, err := mapWorkflowExecutionConfiguration(tc.executionInfo) assert.NoError(t, err) assert.Equal(t, tc.expected, result) }) } } // TODO: More test cases with different states of the workflow func TestMapWorkflowExecutionInfo(t *testing.T) { testCases := []struct { name string executionInfo *persistence.WorkflowExecutionInfo startEvent *types.HistoryEvent setupMock func(*cache.MockDomainCache) expectError bool expected *types.WorkflowExecutionInfo }{ { name: "Success - basic workflow with parent", executionInfo: &persistence.WorkflowExecutionInfo{ WorkflowID: "test-workflow-id", RunID: "test-run-id", TaskList: "test-task-list", TaskListKind: types.TaskListKindNormal, WorkflowTypeName: "test-workflow-type", StartTimestamp: time.Unix(0, 1000000), LastUpdatedTimestamp: time.Unix(0, 2000000), AutoResetPoints: &types.ResetPoints{Points: []*types.ResetPointInfo{}}, Memo: map[string][]byte{"key": []byte("value")}, SearchAttributes: map[string][]byte{"attr": []byte("val")}, PartitionConfig: map[string]string{"partition": "config"}, CronOverlapPolicy: historyConstants.CronSkip, ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{}, ParentWorkflowID: "parent-workflow-id", ParentRunID: "parent-run-id", ParentDomainID: "parent-domain-id", InitiatedID: 123, State: persistence.WorkflowStateCompleted, CloseStatus: persistence.WorkflowCloseStatusCompleted, }, startEvent: &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Int32Ptr(10), }, }, setupMock: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainName("parent-domain-id").Return("parent-domain-name", nil) }, expectError: false, expected: &types.WorkflowExecutionInfo{ Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, TaskList: &types.TaskList{ Name: "test-task-list", Kind: types.TaskListKindNormal.Ptr(), }, Type: &types.WorkflowType{Name: "test-workflow-type"}, StartTime: common.Int64Ptr(1000000), HistoryLength: 10, AutoResetPoints: &types.ResetPoints{Points: []*types.ResetPointInfo{}}, Memo: &types.Memo{Fields: map[string][]byte{"key": []byte("value")}}, IsCron: false, UpdateTime: common.Int64Ptr(2000000), SearchAttributes: &types.SearchAttributes{IndexedFields: map[string][]byte{"attr": []byte("val")}}, PartitionConfig: map[string]string{"partition": "config"}, CronOverlapPolicy: &historyConstants.CronSkip, ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{}, ExecutionTime: common.Int64Ptr(1000000 + (10 * time.Second).Nanoseconds()), ParentExecution: &types.WorkflowExecution{ WorkflowID: "parent-workflow-id", RunID: "parent-run-id", }, ParentDomainID: common.StringPtr("parent-domain-id"), ParentInitiatedID: common.Int64Ptr(123), ParentDomain: common.StringPtr("parent-domain-name"), CloseStatus: types.WorkflowExecutionCloseStatusCompleted.Ptr(), CloseTime: common.Int64Ptr(12345), }, }, { name: "Success - basic workflow without parent", executionInfo: &persistence.WorkflowExecutionInfo{ WorkflowID: "test-workflow-id", RunID: "test-run-id", TaskList: "test-task-list", TaskListKind: types.TaskListKindSticky, WorkflowTypeName: "test-workflow-type", StartTimestamp: time.Unix(0, 1000000), LastUpdatedTimestamp: time.Unix(0, 2000000), AutoResetPoints: &types.ResetPoints{Points: []*types.ResetPointInfo{}}, Memo: map[string][]byte{}, SearchAttributes: map[string][]byte{}, PartitionConfig: map[string]string{}, CronSchedule: "0 0 * * *", State: persistence.WorkflowStateRunning, }, startEvent: &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Int32Ptr(5), }, }, setupMock: func(mockDomainCache *cache.MockDomainCache) {}, expectError: false, expected: &types.WorkflowExecutionInfo{ Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, TaskList: &types.TaskList{ Name: "test-task-list", Kind: types.TaskListKindSticky.Ptr(), }, Type: &types.WorkflowType{Name: "test-workflow-type"}, StartTime: common.Int64Ptr(1000000), HistoryLength: 10, AutoResetPoints: &types.ResetPoints{Points: []*types.ResetPointInfo{}}, Memo: &types.Memo{Fields: map[string][]byte{}}, IsCron: true, UpdateTime: common.Int64Ptr(2000000), SearchAttributes: &types.SearchAttributes{IndexedFields: map[string][]byte{}}, PartitionConfig: map[string]string{}, CronOverlapPolicy: &historyConstants.CronSkip, ActiveClusterSelectionPolicy: nil, ExecutionTime: common.Int64Ptr(1000000 + (5 * time.Second).Nanoseconds()), }, }, { name: "Error - parent domain name lookup fails", executionInfo: &persistence.WorkflowExecutionInfo{ WorkflowID: "test-workflow-id", RunID: "test-run-id", TaskList: "test-task-list", TaskListKind: types.TaskListKindNormal, WorkflowTypeName: "test-workflow-type", StartTimestamp: time.Unix(0, 1000000), LastUpdatedTimestamp: time.Unix(0, 2000000), AutoResetPoints: &types.ResetPoints{Points: []*types.ResetPointInfo{}}, Memo: map[string][]byte{"key": []byte("value")}, SearchAttributes: map[string][]byte{"attr": []byte("val")}, PartitionConfig: map[string]string{"partition": "config"}, CronOverlapPolicy: historyConstants.CronSkip, ActiveClusterSelectionPolicy: &types.ActiveClusterSelectionPolicy{}, ParentWorkflowID: "parent-workflow-id", ParentRunID: "parent-run-id", ParentDomainID: "parent-domain-id", InitiatedID: 123, State: persistence.WorkflowStateRunning, }, startEvent: &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Int32Ptr(10), }, }, setupMock: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainName("parent-domain-id").Return("", &types.InternalServiceError{Message: "Domain lookup failed"}) }, expectError: true, expected: nil, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDomainCache := cache.NewMockDomainCache(ctrl) tc.setupMock(mockDomainCache) result, err := mapWorkflowExecutionInfo(tc.executionInfo, tc.startEvent, mockDomainCache, 10, &types.HistoryEvent{Timestamp: common.Int64Ptr(12345)}) if tc.expectError { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, result) } }) } } // TODO: More test cases for any other branches func TestMapPendingActivityInfo(t *testing.T) { testCases := []struct { name string activityInfo *persistence.ActivityInfo activityScheduledEvent *types.HistoryEvent expected *types.PendingActivityInfo }{ { name: "Success - started activity with retry policy", activityInfo: &persistence.ActivityInfo{ ActivityID: "test-activity-id", ScheduleID: 123, StartedID: 124, CancelRequested: false, StartedTime: time.Unix(0, 2001), LastHeartBeatUpdatedTime: time.Unix(0, 2000), Details: []byte("boom boom"), HasRetryPolicy: true, Attempt: 2002, MaximumAttempts: 2003, ExpirationTime: time.Unix(0, 2004), LastFailureReason: "failure reason", StartedIdentity: "StartedWorkerIdentity", LastWorkerIdentity: "LastWorkerIdentity", LastFailureDetails: []byte("failure details"), ScheduledTime: time.Unix(0, 1999), }, activityScheduledEvent: &types.HistoryEvent{ ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityType: &types.ActivityType{ Name: "test-activity-type", }, }, }, expected: &types.PendingActivityInfo{ ActivityID: "test-activity-id", ScheduleID: 123, State: types.PendingActivityStateStarted.Ptr(), HeartbeatDetails: []byte("boom boom"), LastHeartbeatTimestamp: common.Int64Ptr(2000), LastStartedTimestamp: common.Int64Ptr(2001), Attempt: 2002, MaximumAttempts: 2003, ExpirationTimestamp: common.Int64Ptr(2004), LastFailureReason: common.StringPtr("failure reason"), StartedWorkerIdentity: "StartedWorkerIdentity", LastWorkerIdentity: "LastWorkerIdentity", LastFailureDetails: []byte("failure details"), ActivityType: &types.ActivityType{ Name: "test-activity-type", }, }, }, { name: "Success - scheduled activity without retry policy", activityInfo: &persistence.ActivityInfo{ ActivityID: "test-activity-id-2", ScheduleID: 125, StartedID: constants.EmptyEventID, CancelRequested: false, ScheduledTime: time.Unix(0, 1999), HasRetryPolicy: false, }, activityScheduledEvent: &types.HistoryEvent{ ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityType: &types.ActivityType{ Name: "test-activity-type-2", }, }, }, expected: &types.PendingActivityInfo{ ActivityID: "test-activity-id-2", ScheduleID: 125, State: types.PendingActivityStateScheduled.Ptr(), ScheduledTimestamp: common.Int64Ptr(1999), ActivityType: &types.ActivityType{ Name: "test-activity-type-2", }, }, }, { name: "Success - cancel requested activity", activityInfo: &persistence.ActivityInfo{ ActivityID: "test-activity-id-3", ScheduleID: 126, StartedID: 127, CancelRequested: true, StartedTime: time.Unix(0, 2001), HasRetryPolicy: false, }, activityScheduledEvent: &types.HistoryEvent{ ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityType: &types.ActivityType{ Name: "test-activity-type-3", }, }, }, expected: &types.PendingActivityInfo{ ActivityID: "test-activity-id-3", ScheduleID: 126, State: types.PendingActivityStateCancelRequested.Ptr(), LastStartedTimestamp: common.Int64Ptr(2001), ActivityType: &types.ActivityType{ Name: "test-activity-type-3", }, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := mapPendingActivityInfo(tc.activityInfo, tc.activityScheduledEvent) assert.Equal(t, tc.expected, result) }) } } func TestMapPendingChildExecutionInfo(t *testing.T) { testCases := []struct { name string childExecution *persistence.ChildExecutionInfo domainEntry *cache.DomainCacheEntry setupMock func(*cache.MockDomainCache) expectError bool expected *types.PendingChildExecutionInfo }{ { name: "Success - child execution with DomainID", childExecution: &persistence.ChildExecutionInfo{ InitiatedID: 123, StartedWorkflowID: "child-workflow-id", StartedRunID: "child-run-id", DomainID: "child-domain-id", WorkflowTypeName: "child-workflow-type", ParentClosePolicy: types.ParentClosePolicyAbandon, }, domainEntry: &cache.DomainCacheEntry{}, setupMock: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainName("child-domain-id").Return("child-domain-name", nil) }, expectError: false, expected: &types.PendingChildExecutionInfo{ Domain: "child-domain-name", WorkflowID: "child-workflow-id", RunID: "child-run-id", WorkflowTypeName: "child-workflow-type", InitiatedID: 123, ParentClosePolicy: types.ParentClosePolicyAbandon.Ptr(), }, }, { name: "Success - child execution with deprecated domain name", childExecution: &persistence.ChildExecutionInfo{ InitiatedID: 456, StartedWorkflowID: "child-workflow-id-2", StartedRunID: "child-run-id-2", DomainID: "", // Empty DomainID DomainNameDEPRECATED: "deprecated-domain-name", WorkflowTypeName: "child-workflow-type-2", ParentClosePolicy: types.ParentClosePolicyTerminate, }, domainEntry: &cache.DomainCacheEntry{}, setupMock: func(mockDomainCache *cache.MockDomainCache) { // No mock expectations needed - uses deprecated domain name directly }, expectError: false, expected: &types.PendingChildExecutionInfo{ Domain: "deprecated-domain-name", WorkflowID: "child-workflow-id-2", RunID: "child-run-id-2", WorkflowTypeName: "child-workflow-type-2", InitiatedID: 456, ParentClosePolicy: types.ParentClosePolicyTerminate.Ptr(), }, }, { name: "Success - child execution using parent domain (fallback)", childExecution: &persistence.ChildExecutionInfo{ InitiatedID: 789, StartedWorkflowID: "child-workflow-id-3", StartedRunID: "child-run-id-3", DomainID: "", // Empty DomainID DomainNameDEPRECATED: "", // Empty deprecated name WorkflowTypeName: "child-workflow-type-3", ParentClosePolicy: types.ParentClosePolicyRequestCancel, }, domainEntry: cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{ ID: "parent-domain-id", Name: "parent-domain-fallback", }, &persistence.DomainConfig{}, "test-cluster", ), setupMock: func(mockDomainCache *cache.MockDomainCache) { // No mock expectations needed - uses parent domain entry directly }, expectError: false, expected: &types.PendingChildExecutionInfo{ Domain: "parent-domain-fallback", WorkflowID: "child-workflow-id-3", RunID: "child-run-id-3", WorkflowTypeName: "child-workflow-type-3", InitiatedID: 789, ParentClosePolicy: types.ParentClosePolicyRequestCancel.Ptr(), }, }, { name: "Success - domain not exists error (uses domainID as fallback)", childExecution: &persistence.ChildExecutionInfo{ InitiatedID: 999, StartedWorkflowID: "child-workflow-id-4", StartedRunID: "child-run-id-4", DomainID: "deleted-domain-id", WorkflowTypeName: "child-workflow-type-4", ParentClosePolicy: types.ParentClosePolicyAbandon, }, domainEntry: &cache.DomainCacheEntry{}, setupMock: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainName("deleted-domain-id").Return("", &types.EntityNotExistsError{Message: "Domain not found"}) }, expectError: false, expected: &types.PendingChildExecutionInfo{ Domain: "deleted-domain-id", // Falls back to DomainID WorkflowID: "child-workflow-id-4", RunID: "child-run-id-4", WorkflowTypeName: "child-workflow-type-4", InitiatedID: 999, ParentClosePolicy: types.ParentClosePolicyAbandon.Ptr(), }, }, { name: "Error - non-EntityNotExists error from domain cache", childExecution: &persistence.ChildExecutionInfo{ InitiatedID: 111, StartedWorkflowID: "child-workflow-id-5", StartedRunID: "child-run-id-5", DomainID: "error-domain-id", WorkflowTypeName: "child-workflow-type-5", ParentClosePolicy: types.ParentClosePolicyAbandon, }, domainEntry: &cache.DomainCacheEntry{}, setupMock: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainName("error-domain-id").Return("", &types.InternalServiceError{Message: "Internal error"}) }, expectError: true, expected: nil, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDomainCache := cache.NewMockDomainCache(ctrl) tc.setupMock(mockDomainCache) result, err := mapPendingChildExecutionInfo(tc.childExecution, tc.domainEntry, mockDomainCache) if tc.expectError { assert.Error(t, err) assert.Nil(t, result) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, result) } }) } } func TestMapPendingDecisionInfo(t *testing.T) { testCases := []struct { name string decisionInfo *execution.DecisionInfo expected *types.PendingDecisionInfo }{ { name: "Success - scheduled decision (not started)", decisionInfo: &execution.DecisionInfo{ Version: 1, ScheduleID: 100, StartedID: constants.EmptyEventID, // Not started RequestID: "request-id-1", DecisionTimeout: 30, TaskList: "decision-task-list", Attempt: 1, ScheduledTimestamp: 1234567890, StartedTimestamp: 0, // Not started OriginalScheduledTimestamp: 1234567890, }, expected: &types.PendingDecisionInfo{ State: types.PendingDecisionStateScheduled.Ptr(), ScheduledTimestamp: common.Int64Ptr(1234567890), Attempt: 1, OriginalScheduledTimestamp: common.Int64Ptr(1234567890), ScheduleID: 100, StartedTimestamp: nil, // Should not be set for scheduled decision }, }, { name: "Success - started decision", decisionInfo: &execution.DecisionInfo{ Version: 2, ScheduleID: 200, StartedID: 201, // Started RequestID: "request-id-2", DecisionTimeout: 60, TaskList: "decision-task-list-2", Attempt: 3, ScheduledTimestamp: 2345678901, StartedTimestamp: 2345678950, OriginalScheduledTimestamp: 2345678900, }, expected: &types.PendingDecisionInfo{ State: types.PendingDecisionStateStarted.Ptr(), ScheduledTimestamp: common.Int64Ptr(2345678901), StartedTimestamp: common.Int64Ptr(2345678950), Attempt: 3, OriginalScheduledTimestamp: common.Int64Ptr(2345678900), ScheduleID: 200, }, }, { name: "Success - decision with zero values", decisionInfo: &execution.DecisionInfo{ Version: 0, ScheduleID: 0, StartedID: constants.EmptyEventID, RequestID: "", DecisionTimeout: 0, TaskList: "", Attempt: 0, ScheduledTimestamp: 0, StartedTimestamp: 0, OriginalScheduledTimestamp: 0, }, expected: &types.PendingDecisionInfo{ State: types.PendingDecisionStateScheduled.Ptr(), ScheduledTimestamp: common.Int64Ptr(0), Attempt: 0, OriginalScheduledTimestamp: common.Int64Ptr(0), ScheduleID: 0, StartedTimestamp: nil, }, }, { name: "Success - decision with high attempt count", decisionInfo: &execution.DecisionInfo{ Version: 5, ScheduleID: 500, StartedID: 501, RequestID: "request-id-high-attempt", DecisionTimeout: 90, TaskList: "high-attempt-task-list", Attempt: 99, ScheduledTimestamp: 3456789012, StartedTimestamp: 3456789100, OriginalScheduledTimestamp: 3456789000, }, expected: &types.PendingDecisionInfo{ State: types.PendingDecisionStateStarted.Ptr(), ScheduledTimestamp: common.Int64Ptr(3456789012), StartedTimestamp: common.Int64Ptr(3456789100), Attempt: 99, OriginalScheduledTimestamp: common.Int64Ptr(3456789000), ScheduleID: 500, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := mapPendingDecisionInfo(tc.decisionInfo) assert.Equal(t, tc.expected, result) }) } } func TestValidateDescribeWorkflowExecutionRequest(t *testing.T) { testCases := []struct { name string request *types.HistoryDescribeWorkflowExecutionRequest expectError bool }{ { name: "Success - valid UUID", request: &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: uuid.New(), Request: &types.DescribeWorkflowExecutionRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, }, expectError: false, }, { name: "Error - empty UUID", request: &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: "", Request: &types.DescribeWorkflowExecutionRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, }, expectError: true, }, { name: "Error - invalid UUID format", request: &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: "not-a-valid-uuid", Request: &types.DescribeWorkflowExecutionRequest{ Domain: "test-domain", Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, }, expectError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := validateDescribeWorkflowExecutionRequest(tc.request) if tc.expectError { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } ================================================ FILE: service/history/engine/engineimpl/dlq_operations.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common/types" ) func (e *historyEngineImpl) CountDLQMessages(ctx context.Context, forceFetch bool) (map[string]int64, error) { return e.replicationDLQHandler.GetMessageCount(ctx, forceFetch) } func (e *historyEngineImpl) ReadDLQMessages( ctx context.Context, request *types.ReadDLQMessagesRequest, ) (*types.ReadDLQMessagesResponse, error) { tasks, taskInfo, token, err := e.replicationDLQHandler.ReadMessages( ctx, request.GetSourceCluster(), request.GetInclusiveEndMessageID(), int(request.GetMaximumPageSize()), request.GetNextPageToken(), ) if err != nil { return nil, err } return &types.ReadDLQMessagesResponse{ Type: request.GetType().Ptr(), ReplicationTasks: tasks, ReplicationTasksInfo: taskInfo, NextPageToken: token, }, nil } func (e *historyEngineImpl) PurgeDLQMessages( ctx context.Context, request *types.PurgeDLQMessagesRequest, ) error { return e.replicationDLQHandler.PurgeMessages( ctx, request.GetSourceCluster(), request.GetInclusiveEndMessageID(), ) } func (e *historyEngineImpl) MergeDLQMessages( ctx context.Context, request *types.MergeDLQMessagesRequest, ) (*types.MergeDLQMessagesResponse, error) { token, err := e.replicationDLQHandler.MergeMessages( ctx, request.GetSourceCluster(), request.GetInclusiveEndMessageID(), int(request.GetMaximumPageSize()), request.GetNextPageToken(), ) if err != nil { return nil, err } return &types.MergeDLQMessagesResponse{ NextPageToken: token, }, nil } ================================================ FILE: service/history/engine/engineimpl/get_replication_messages.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "encoding/json" "fmt" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func (e *historyEngineImpl) GetReplicationMessages( ctx context.Context, pollingCluster string, lastReadMessageID int64, ) (*types.ReplicationMessages, error) { scope := metrics.HistoryGetReplicationMessagesScope sw := e.metricsClient.StartTimer(scope, metrics.GetReplicationMessagesForShardLatency) defer sw.Stop() replicationMessages, err := e.replicationAckManager.GetTasks( ctx, pollingCluster, lastReadMessageID, ) if err != nil { e.logger.Error("Failed to retrieve replication messages.", tag.Error(err)) return nil, err } // Set cluster status for sync shard info replicationMessages.SyncShardStatus = &types.SyncShardStatus{ Timestamp: common.Int64Ptr(e.timeSource.Now().UnixNano()), } e.logger.Debug("Successfully fetched replication messages.", tag.Counter(len(replicationMessages.ReplicationTasks)), tag.ClusterName(pollingCluster)) if e.logger.DebugOn() { for _, task := range replicationMessages.ReplicationTasks { data, err := json.Marshal(task) if err != nil { e.logger.Error("Failed to marshal replication task.", tag.Error(err)) continue } e.logger.Debugf("Replication task: %s", string(data)) } } return replicationMessages, nil } func (e *historyEngineImpl) GetDLQReplicationMessages( ctx context.Context, taskInfos []*types.ReplicationTaskInfo, ) ([]*types.ReplicationTask, error) { scope := metrics.HistoryGetDLQReplicationMessagesScope sw := e.metricsClient.StartTimer(scope, metrics.GetDLQReplicationMessagesLatency) defer sw.Stop() tasks := make([]*types.ReplicationTask, 0, len(taskInfos)) for _, taskInfo := range taskInfos { t, err := convertToReplicationTask(taskInfo) if err != nil { e.logger.Error("Failed to convert replication task.", tag.Error(err)) return nil, err } task, err := e.replicationHydrator.Hydrate(ctx, t) if err != nil { e.logger.Error("Failed to fetch DLQ replication messages.", tag.Error(err)) return nil, err } if task != nil { tasks = append(tasks, task) } } return tasks, nil } func convertToReplicationTask(taskInfo *types.ReplicationTaskInfo) (persistence.Task, error) { switch taskInfo.TaskType { case persistence.ReplicationTaskTypeHistory: return &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: taskInfo.DomainID, WorkflowID: taskInfo.WorkflowID, RunID: taskInfo.RunID, }, TaskData: persistence.TaskData{ TaskID: taskInfo.TaskID, Version: taskInfo.Version, }, FirstEventID: taskInfo.FirstEventID, NextEventID: taskInfo.NextEventID, }, nil case persistence.ReplicationTaskTypeSyncActivity: return &persistence.SyncActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: taskInfo.DomainID, WorkflowID: taskInfo.WorkflowID, RunID: taskInfo.RunID, }, TaskData: persistence.TaskData{ TaskID: taskInfo.TaskID, Version: taskInfo.Version, }, ScheduledID: taskInfo.ScheduledID, }, nil case persistence.ReplicationTaskTypeFailoverMarker: return &persistence.FailoverMarkerTask{ DomainID: taskInfo.DomainID, TaskData: persistence.TaskData{ TaskID: taskInfo.TaskID, Version: taskInfo.Version, }, }, nil default: return nil, fmt.Errorf("unsupported task type: %v", taskInfo.TaskType) } } ================================================ FILE: service/history/engine/engineimpl/history_engine.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "errors" "fmt" "time" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/client/wrappers/retryable" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" cndc "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/decision" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/failover" "github.com/uber/cadence/service/history/ndc" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/replication" "github.com/uber/cadence/service/history/reset" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/workflow" "github.com/uber/cadence/service/worker/archiver" ) const ( defaultQueryFirstDecisionTaskWaitTime = time.Second queryFirstDecisionTaskCheckInterval = 200 * time.Millisecond contextLockTimeout = 500 * time.Millisecond longPollCompletionBuffer = 50 * time.Millisecond // TerminateIfRunningReason reason for terminateIfRunning TerminateIfRunningReason = "TerminateIfRunning Policy" // TerminateIfRunningDetailsTemplate details template for terminateIfRunning TerminateIfRunningDetailsTemplate = "New runID: %s" ) var ( errDomainDeprecated = &types.BadRequestError{Message: "Domain is deprecated."} ) type historyEngineImpl struct { currentClusterName string shard shard.Context timeSource clock.TimeSource decisionHandler decision.Handler clusterMetadata cluster.Metadata historyV2Mgr persistence.HistoryManager executionManager persistence.ExecutionManager visibilityMgr persistence.VisibilityManager queueProcessors map[persistence.HistoryTaskCategory]queue.Processor nDCReplicator ndc.HistoryReplicator nDCActivityReplicator ndc.ActivityReplicator historyEventNotifier events.Notifier tokenSerializer common.TaskTokenSerializer executionCache execution.Cache metricsClient metrics.Client logger log.Logger throttledLogger log.Logger activeClusterManager activecluster.Manager config *config.Config archivalClient archiver.Client workflowResetter reset.WorkflowResetter replicationTaskProcessors []replication.TaskProcessor replicationAckManager replication.TaskAckManager replicationTaskStore *replication.TaskStore replicationHydrator replication.TaskHydrator replicationMetricsEmitter *replication.MetricsEmitterImpl eventsReapplier ndc.EventsReapplier matchingClient matching.Client rawMatchingClient matching.Client clientChecker client.VersionChecker replicationDLQHandler replication.DLQHandler failoverMarkerNotifier failover.MarkerNotifier updateWithActionFn func( context.Context, log.Logger, execution.Cache, string, types.WorkflowExecution, bool, time.Time, func(wfContext execution.Context, mutableState execution.MutableState) error, ) error } var ( // FailedWorkflowCloseState is a set of failed workflow close states, used for start workflow policy // for start workflow execution API FailedWorkflowCloseState = map[int]bool{ persistence.WorkflowCloseStatusFailed: true, persistence.WorkflowCloseStatusCanceled: true, persistence.WorkflowCloseStatusTerminated: true, persistence.WorkflowCloseStatusTimedOut: true, } ) // NewEngineWithShardContext creates an instance of history engine func NewEngineWithShardContext( shard shard.Context, visibilityMgr persistence.VisibilityManager, matching matching.Client, historyEventNotifier events.Notifier, config *config.Config, replicationTaskFetchers replication.TaskFetchers, rawMatchingClient matching.Client, failoverCoordinator failover.Coordinator, queueFactories []queue.Factory, ) engine.Engine { currentClusterName := shard.GetService().GetClusterMetadata().GetCurrentClusterName() logger := shard.GetLogger() executionManager := shard.GetExecutionManager() historyV2Manager := shard.GetHistoryManager() executionCache := execution.NewCache(shard) failoverMarkerNotifier := failover.NewMarkerNotifier(shard, config, failoverCoordinator) replicationHydrator := replication.NewDeferredTaskHydrator(shard.GetShardID(), historyV2Manager, executionCache, shard.GetDomainCache()) replicationTaskStore := replication.NewTaskStore( shard.GetConfig(), shard.GetClusterMetadata(), shard.GetDomainCache(), shard.GetMetricsClient(), shard.GetLogger(), replicationHydrator, shard.GetReplicationBudgetManager(), shard.GetShardID(), shard.GetTimeSource(), ) replicationDynamicTaskBatchSizer := replication.NewDynamicTaskBatchSizer(shard.GetShardID(), logger, config, shard.GetMetricsClient()) replicationReader := replication.NewTaskReader(shard.GetShardID(), executionManager) historyEngImpl := &historyEngineImpl{ currentClusterName: currentClusterName, shard: shard, clusterMetadata: shard.GetClusterMetadata(), timeSource: shard.GetTimeSource(), historyV2Mgr: historyV2Manager, executionManager: executionManager, visibilityMgr: visibilityMgr, tokenSerializer: common.NewJSONTaskTokenSerializer(), executionCache: executionCache, logger: logger.WithTags(tag.ComponentHistoryEngine), throttledLogger: shard.GetThrottledLogger().WithTags(tag.ComponentHistoryEngine), activeClusterManager: shard.GetActiveClusterManager(), metricsClient: shard.GetMetricsClient(), historyEventNotifier: historyEventNotifier, config: config, workflowResetter: reset.NewWorkflowResetter( shard, executionCache, logger, ), matchingClient: matching, rawMatchingClient: rawMatchingClient, clientChecker: client.NewVersionChecker(), failoverMarkerNotifier: failoverMarkerNotifier, replicationHydrator: replicationHydrator, replicationAckManager: replication.NewTaskAckManager( shard.GetShardID(), shard, shard.GetMetricsClient(), shard.GetLogger(), replicationReader, replicationTaskStore, shard.GetTimeSource(), config, proto.ReplicationMessagesSize, replicationDynamicTaskBatchSizer, ), replicationTaskStore: replicationTaskStore, replicationMetricsEmitter: replication.NewMetricsEmitter( shard.GetShardID(), shard, replicationReader, shard.GetMetricsClient()), updateWithActionFn: workflow.UpdateWithAction, queueProcessors: make(map[persistence.HistoryTaskCategory]queue.Processor), } historyEngImpl.decisionHandler = decision.NewHandler( shard, historyEngImpl.executionCache, historyEngImpl.tokenSerializer, ) pRetry := persistence.NewPersistenceRetryer( shard.GetExecutionManager(), shard.GetHistoryManager(), common.CreatePersistenceRetryPolicy(), ) openExecutionCheck := invariant.NewConcreteExecutionExists(pRetry, shard.GetDomainCache()) for _, factory := range queueFactories { historyEngImpl.queueProcessors[factory.Category()] = factory.CreateQueue( shard, executionCache, openExecutionCheck, ) } historyEngImpl.eventsReapplier = ndc.NewEventsReapplier(shard.GetMetricsClient(), logger) historyEngImpl.nDCReplicator = ndc.NewHistoryReplicator( shard, executionCache, historyEngImpl.eventsReapplier, logger, ) historyEngImpl.nDCActivityReplicator = ndc.NewActivityReplicator( shard, executionCache, logger, ) var replicationTaskProcessors []replication.TaskProcessor replicationTaskExecutors := make(map[string]replication.TaskExecutor) // Intentionally use the raw client to create its own retry policy historyRawClient := shard.GetService().GetClientBean().GetHistoryClient() historyRetryableClient := retryable.NewHistoryClient( historyRawClient, common.CreateReplicationServiceBusyRetryPolicy(), common.IsServiceBusyError, ) resendFunc := func(ctx context.Context, request *types.ReplicateEventsV2Request) error { return historyRetryableClient.ReplicateEventsV2(ctx, request) } for _, replicationTaskFetcher := range replicationTaskFetchers.GetFetchers() { sourceCluster := replicationTaskFetcher.GetSourceCluster() historyResender := cndc.NewHistoryResender( shard.GetDomainCache(), shard.GetService().GetClientBean(), resendFunc, nil, openExecutionCheck, shard.GetLogger().WithTags(tag.ComponentReplicatorQueue, tag.ActiveClusterName(sourceCluster)), ) replicationTaskExecutor := replication.NewTaskExecutor( sourceCluster, shard, shard.GetDomainCache(), historyResender, historyEngImpl, shard.GetMetricsClient(), shard.GetLogger().WithTags(tag.ComponentReplicatorQueue, tag.ActiveClusterName(sourceCluster)), ) replicationTaskExecutors[sourceCluster] = replicationTaskExecutor replicationTaskProcessor := replication.NewTaskProcessor( shard, historyEngImpl, config, shard.GetMetricsClient(), replicationTaskFetcher, replicationTaskExecutor, shard.GetTimeSource(), ) replicationTaskProcessors = append(replicationTaskProcessors, replicationTaskProcessor) } historyEngImpl.replicationTaskProcessors = replicationTaskProcessors replicationMessageHandler := replication.NewDLQHandler(shard, replicationTaskExecutors) historyEngImpl.replicationDLQHandler = replicationMessageHandler shard.SetEngine(historyEngImpl) return historyEngImpl } // Start will spin up all the components needed to start serving this shard. // Make sure all the components are loaded lazily so start can return immediately. This is important because // ShardController calls start sequentially for all the shards for a given host during startup. func (e *historyEngineImpl) Start() { e.logger.Info("History engine state changed", tag.LifeCycleStarting) defer e.logger.Info("History engine state changed", tag.LifeCycleStarted) for _, processor := range e.queueProcessors { processor.Start() } e.replicationDLQHandler.Start() e.replicationMetricsEmitter.Start() // failover callback will try to create a failover queue processor to scan all inflight tasks // if domain needs to be failovered. However, in the multicursor queue logic, the scan range // can't be retrieved before the processor is started. If failover callback is registered // before queue processor is started, it may result in a deadline as to create the failover queue, // queue processor need to be started. e.registerDomainFailoverCallback() for _, replicationTaskProcessor := range e.replicationTaskProcessors { replicationTaskProcessor.Start() } if e.config.EnableGracefulFailover() { e.failoverMarkerNotifier.Start() } } // Stop the service. func (e *historyEngineImpl) Stop() { e.logger.Info("History engine state changed", tag.LifeCycleStopping) defer e.logger.Info("History engine state changed", tag.LifeCycleStopped) for _, processor := range e.queueProcessors { processor.Stop() } e.replicationDLQHandler.Stop() e.replicationMetricsEmitter.Stop() for _, replicationTaskProcessor := range e.replicationTaskProcessors { replicationTaskProcessor.Stop() } e.failoverMarkerNotifier.Stop() // unset the failover callback e.shard.GetDomainCache().UnregisterDomainChangeCallback(createShardNameFromShardID(e.shard.GetShardID())) } // ScheduleDecisionTask schedules a decision if no outstanding decision found func (e *historyEngineImpl) ScheduleDecisionTask(ctx context.Context, req *types.ScheduleDecisionTaskRequest) error { return e.decisionHandler.HandleDecisionTaskScheduled(ctx, req) } func (e *historyEngineImpl) ReplicateEventsV2(ctx context.Context, replicateRequest *types.ReplicateEventsV2Request) error { return e.nDCReplicator.ApplyEvents(ctx, replicateRequest) } func (e *historyEngineImpl) SyncShardStatus(ctx context.Context, request *types.SyncShardStatusRequest) error { clusterName := request.GetSourceCluster() now := time.Unix(0, request.GetTimestamp()) // here there are 3 main things // 1. update the view of remote cluster's shard time // 2. notify the timer gate in the timer queue standby processor // 3. notify the transfer (essentially a no op, just put it here so it looks symmetric) e.shard.SetCurrentTime(clusterName, now) for _, processor := range e.queueProcessors { processor.NotifyNewTask(clusterName, &hcommon.NotifyTaskInfo{Tasks: []persistence.Task{}}) } return nil } func (e *historyEngineImpl) SyncActivity(ctx context.Context, request *types.SyncActivityRequest) (retError error) { return e.nDCActivityReplicator.SyncActivity(ctx, request) } func (e *historyEngineImpl) newDomainNotActiveError( domainEntry *cache.DomainCacheEntry, failoverVersion int64, ) error { activeClusterName, err := e.shard.GetClusterMetadata().ClusterNameForFailoverVersion(failoverVersion) if err != nil { activeClusterName = "_unknown_" } return domainEntry.NewDomainNotActiveError(e.currentClusterName, activeClusterName) } func (e *historyEngineImpl) checkForHistoryCorruptions(ctx context.Context, mutableState execution.MutableState) (bool, error) { domainName := mutableState.GetDomainEntry().GetInfo().Name if !e.config.EnableHistoryCorruptionCheck(domainName) { return false, nil } // Ensure that we can obtain start event. Failing to do so means corrupted history or resurrected mutable state record. _, err := mutableState.GetStartEvent(ctx) if err != nil { info := mutableState.GetExecutionInfo() // Mark workflow as corrupted. So that new one can be restarted. info.State = persistence.WorkflowStateCorrupted e.logger.Error("history corruption check failed", tag.WorkflowDomainName(domainName), tag.WorkflowID(info.WorkflowID), tag.WorkflowRunID(info.RunID), tag.WorkflowType(info.WorkflowTypeName), tag.Error(err)) if errors.Is(err, execution.ErrMissingWorkflowStartEvent) { return true, nil } return false, err } return false, nil } func getScheduleID(activityID string, mutableState execution.MutableState) (int64, error) { if activityID == "" { return 0, &types.BadRequestError{Message: "Neither ActivityID nor ScheduleID is provided"} } activityInfo, ok := mutableState.GetActivityByActivityID(activityID) if !ok { return 0, &types.BadRequestError{Message: "Cannot locate Activity ScheduleID"} } return activityInfo.ScheduleID, nil } func (e *historyEngineImpl) getActiveDomainByWorkflow(ctx context.Context, domainID, workflowID, runID string) (*cache.DomainCacheEntry, error) { activeClusterInfo, err := e.activeClusterManager.GetActiveClusterInfoByWorkflow(ctx, domainID, workflowID, runID) if err != nil { return nil, err } domain, err := e.shard.GetDomainCache().GetDomainByID(domainID) if err != nil { return nil, err } if activeClusterInfo.ActiveClusterName == e.clusterMetadata.GetCurrentClusterName() { return domain, nil } return nil, domain.NewDomainNotActiveError(e.clusterMetadata.GetCurrentClusterName(), activeClusterInfo.ActiveClusterName) } func createShardNameFromShardID(shardID int) string { return fmt.Sprintf("history-engine-%d", shardID) } ================================================ FILE: service/history/engine/engineimpl/history_engine2_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "encoding/json" "errors" "reflect" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/decision" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/query" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/shard" test "github.com/uber/cadence/service/history/testing" "github.com/uber/cadence/service/history/workflow" ) type ( engine2Suite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockTxProcessor *queue.MockProcessor mockTimerProcessor *queue.MockProcessor mockEventsCache *events.MockCache mockDomainCache *cache.MockDomainCache historyEngine *historyEngineImpl mockExecutionMgr *mocks.ExecutionManager mockHistoryV2Mgr *mocks.HistoryV2Manager mockShardManager *mocks.ShardManager mockActiveClusterMgr *activecluster.MockManager config *config.Config logger log.Logger } ) func TestEngine2Suite(t *testing.T) { s := new(engine2Suite) suite.Run(t, s) } func (s *engine2Suite) SetupSuite() { s.config = config.NewForTest() } func (s *engine2Suite) TearDownSuite() { } func (s *engine2Suite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockTxProcessor = queue.NewMockProcessor(s.controller) s.mockTimerProcessor = queue.NewMockProcessor(s.controller) s.mockTxProcessor.EXPECT().NotifyNewTask(gomock.Any(), gomock.Any()).AnyTimes() s.mockTimerProcessor.EXPECT().NotifyNewTask(gomock.Any(), gomock.Any()).AnyTimes() s.mockShard = shard.NewTestContext( s.T(), s.controller, &p.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, s.config, ) s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockExecutionMgr = s.mockShard.Resource.ExecutionMgr s.mockHistoryV2Mgr = s.mockShard.Resource.HistoryMgr s.mockShardManager = s.mockShard.Resource.ShardMgr s.mockEventsCache = s.mockShard.MockEventsCache s.mockActiveClusterMgr = s.mockShard.Resource.ActiveClusterMgr testDomainEntry := cache.NewLocalDomainCacheEntryForTest( &p.DomainInfo{ID: constants.TestDomainID}, &p.DomainConfig{}, "", ) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(testDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(constants.TestDomainID, nil).AnyTimes() s.mockEventsCache.EXPECT().PutEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() s.logger = s.mockShard.GetLogger() executionCache := execution.NewCache(s.mockShard) h := &historyEngineImpl{ currentClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), shard: s.mockShard, clusterMetadata: s.mockShard.Resource.ClusterMetadata, executionManager: s.mockExecutionMgr, historyV2Mgr: s.mockHistoryV2Mgr, executionCache: executionCache, logger: s.logger, throttledLogger: s.logger, metricsClient: metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), tokenSerializer: common.NewJSONTaskTokenSerializer(), config: s.config, timeSource: s.mockShard.GetTimeSource(), historyEventNotifier: events.NewNotifier(clock.NewRealTimeSource(), metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), func(string) int { return 0 }), queueProcessors: map[p.HistoryTaskCategory]queue.Processor{ p.HistoryTaskCategoryTransfer: s.mockTxProcessor, p.HistoryTaskCategoryTimer: s.mockTimerProcessor, }, activeClusterManager: s.mockActiveClusterMgr, } s.mockShard.SetEngine(h) h.decisionHandler = decision.NewHandler(s.mockShard, h.executionCache, h.tokenSerializer) s.historyEngine = h } func (s *engine2Suite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *engine2Suite) TestRecordDecisionTaskStartedSuccessStickyExpired() { domainID := constants.TestDomainID we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" stickyTl := "stickyTaskList" identity := "testIdentity" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) executionInfo := msBuilder.GetExecutionInfo() executionInfo.StickyTaskList = stickyTl test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() request := types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &we, ScheduleID: 2, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: stickyTl, }, Identity: identity, }, } expectedResponse := types.RecordDecisionTaskStartedResponse{} expectedResponse.WorkflowType = msBuilder.GetWorkflowType() executionInfo = msBuilder.GetExecutionInfo() if executionInfo.LastProcessedEvent != commonconstants.EmptyEventID { expectedResponse.PreviousStartedEventID = common.Int64Ptr(executionInfo.LastProcessedEvent) } expectedResponse.ScheduledEventID = di.ScheduleID expectedResponse.StartedEventID = di.ScheduleID + 1 expectedResponse.StickyExecutionEnabled = false expectedResponse.NextEventID = msBuilder.GetNextEventID() + 1 expectedResponse.Attempt = di.Attempt expectedResponse.WorkflowExecutionTaskList = &types.TaskList{ Name: executionInfo.TaskList, Kind: types.TaskListKindNormal.Ptr(), } expectedResponse.BranchToken, _ = msBuilder.GetCurrentBranchToken() response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &request) s.Nil(err) s.NotNil(response) expectedResponse.StartedTimestamp = response.StartedTimestamp expectedResponse.ScheduledTimestamp = common.Int64Ptr(0) response.ScheduledTimestamp = common.Int64Ptr(0) expectedResponse.Queries = make(map[string]*types.WorkflowQuery) s.Equal(&expectedResponse, response) } func (s *engine2Suite) TestRecordDecisionTaskStartedSuccessStickyEnabled() { domainID := constants.TestDomainID we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" stickyTl := "stickyTaskList" identity := "testIdentity" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) executionInfo := msBuilder.GetExecutionInfo() executionInfo.LastUpdatedTimestamp = time.Now() executionInfo.StickyTaskList = stickyTl test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() request := types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &we, ScheduleID: 2, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: stickyTl, }, Identity: identity, }, } expectedResponse := types.RecordDecisionTaskStartedResponse{} expectedResponse.WorkflowType = msBuilder.GetWorkflowType() executionInfo = msBuilder.GetExecutionInfo() if executionInfo.LastProcessedEvent != commonconstants.EmptyEventID { expectedResponse.PreviousStartedEventID = common.Int64Ptr(executionInfo.LastProcessedEvent) } expectedResponse.ScheduledEventID = di.ScheduleID expectedResponse.StartedEventID = di.ScheduleID + 1 expectedResponse.StickyExecutionEnabled = true expectedResponse.NextEventID = msBuilder.GetNextEventID() + 1 expectedResponse.Attempt = di.Attempt expectedResponse.WorkflowExecutionTaskList = &types.TaskList{ Name: executionInfo.TaskList, Kind: types.TaskListKindNormal.Ptr(), } currentBranchTokken, err := msBuilder.GetCurrentBranchToken() s.NoError(err) expectedResponse.BranchToken = currentBranchTokken response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &request) s.Nil(err) s.NotNil(response) expectedResponse.StartedTimestamp = response.StartedTimestamp expectedResponse.ScheduledTimestamp = common.Int64Ptr(0) response.ScheduledTimestamp = common.Int64Ptr(0) expectedResponse.Queries = make(map[string]*types.WorkflowQuery) s.Equal(&expectedResponse, response) } func (s *engine2Suite) TestRecordDecisionTaskStartedIfNoExecution() { domainID := constants.TestDomainID workflowExecution := &types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) identity := "testIdentity" tl := "testTaskList" s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: workflowExecution, ScheduleID: 2, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(response) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engine2Suite) TestRecordDecisionTaskStartedIfGetExecutionFailed() { domainID := constants.TestDomainID workflowExecution := &types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) identity := "testIdentity" tl := "testTaskList" s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(nil, errors.New("FAILED")).Once() response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: workflowExecution, ScheduleID: 2, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(response) s.NotNil(err) s.EqualError(err, "FAILED") } func (s *engine2Suite) TestRecordDecisionTaskStartedIfTaskAlreadyStarted() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, true) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 2, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(response) s.NotNil(err) s.IsType(&types.EventAlreadyStartedError{}, err) s.logger.Error("RecordDecisionTaskStarted failed with", tag.Error(err)) } func (s *engine2Suite) TestRecordDecisionTaskStartedIfTaskAlreadyCompleted() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, true) test.AddDecisionTaskCompletedEvent(msBuilder, int64(2), int64(3), nil, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 2, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(response) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) s.logger.Error("RecordDecisionTaskStarted failed with", tag.Error(err)) } func (s *engine2Suite) TestRecordDecisionTaskStartedConflictOnUpdate() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(3) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, false) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.ConditionFailedError{}).Once() ms2 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse2 := &p.GetWorkflowExecutionResponse{State: ms2} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse2, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 2, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(err) s.NotNil(response) s.Equal("wType", response.WorkflowType.Name) s.True(response.PreviousStartedEventID == nil) s.Equal(int64(3), response.StartedEventID) } func (s *engine2Suite) TestRecordDecisionTaskRetrySameRequest() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" requestID := "testRecordDecisionTaskRetrySameRequestID" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(3) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, false) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.ConditionFailedError{}).Once() startedEventID := test.AddDecisionTaskStartedEventWithRequestID(msBuilder, int64(2), requestID, tl, identity) ms2 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse2 := &p.GetWorkflowExecutionResponse{State: ms2} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse2, nil).Once() response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 2, TaskID: 100, RequestID: requestID, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(err) s.NotNil(response) s.Equal("wType", response.WorkflowType.Name) s.True(response.PreviousStartedEventID == nil) s.Equal(startedEventID.ID, response.StartedEventID) } func (s *engine2Suite) TestRecordDecisionTaskRetryDifferentRequest() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" requestID := "testRecordDecisionTaskRetrySameRequestID" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(3) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, false) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.ConditionFailedError{}).Once() // Add event. test.AddDecisionTaskStartedEventWithRequestID(msBuilder, int64(2), "some_other_req", tl, identity) ms2 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse2 := &p.GetWorkflowExecutionResponse{State: ms2} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse2, nil).Once() response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 2, TaskID: 100, RequestID: requestID, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(response) s.NotNil(err) s.IsType(&types.EventAlreadyStartedError{}, err) s.logger.Info("Failed with error", tag.Error(err)) } func (s *engine2Suite) TestRecordDecisionTaskStartedMaxAttemptsExceeded() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(workflow.ConditionalRetryCount + 1) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, false) for i := 0; i < workflow.ConditionalRetryCount; i++ { ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() } s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Times( workflow.ConditionalRetryCount) s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.ConditionFailedError{}).Times(workflow.ConditionalRetryCount) response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 2, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.NotNil(err) s.Nil(response) s.Equal(workflow.ErrMaxAttemptsExceeded, err) } func (s *engine2Suite) TestRecordDecisionTaskSuccess() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(3) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, false) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() // load mutable state such that it already exists in memory when respond decision task is called // this enables us to set query registry on it ctx, release, err := s.historyEngine.executionCache.GetOrCreateWorkflowExecutionForBackground(constants.TestDomainID, workflowExecution) s.NoError(err) loadedMS, err := ctx.LoadWorkflowExecution(context.Background()) s.NoError(err) qr := query.NewRegistry() id1, _ := qr.BufferQuery(&types.WorkflowQuery{}) id2, _ := qr.BufferQuery(&types.WorkflowQuery{}) id3, _ := qr.BufferQuery(&types.WorkflowQuery{}) loadedMS.SetQueryRegistry(qr) release(nil) response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 2, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(err) s.NotNil(response) s.Equal("wType", response.WorkflowType.Name) s.True(response.PreviousStartedEventID == nil) s.Equal(int64(3), response.StartedEventID) expectedQueryMap := map[string]*types.WorkflowQuery{ id1: {}, id2: {}, id3: {}, } s.Equal(expectedQueryMap, response.Queries) } func (s *engine2Suite) TestRecordActivityTaskStartedIfNoExecution() { domainID := constants.TestDomainID workflowExecution := &types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) identity := "testIdentity" tl := "testTaskList" s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() response, err := s.historyEngine.RecordActivityTaskStarted(context.Background(), &types.RecordActivityTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: workflowExecution, ScheduleID: 5, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForActivityTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) if err != nil { s.logger.Error("Unexpected Error", tag.Error(err)) } s.Nil(response) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engine2Suite) TestRecordActivityTaskStartedSuccess() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, true) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, int64(2), int64(3), nil, identity) scheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &p.GetWorkflowExecutionResponse{State: ms1} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() s.mockEventsCache.EXPECT().GetEvent( gomock.Any(), gomock.Any(), domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID(), decisionCompletedEvent.ID, scheduledEvent.ID, gomock.Any(), ).Return(scheduledEvent, nil) response, err := s.historyEngine.RecordActivityTaskStarted(context.Background(), &types.RecordActivityTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 5, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForActivityTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(err) s.NotNil(response) s.Equal(scheduledEvent, response.ScheduledEvent) } func (s *engine2Suite) TestRecordActivityTaskStartedResurrected() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID} identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) timeSource := clock.NewMockedTimeSource() s.historyEngine.timeSource = timeSource msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, true) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, int64(2), int64(3), nil, identity) scheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, "activity1_id", "activity_type1", tl, []byte("input1"), 100, 10, 1, 5) // Use mutable state snapshot before start/completion of the activity (to indicate resurrected state) msSnapshot := execution.CreatePersistenceMutableState(s.T(), msBuilder) startedEvent := test.AddActivityTaskStartedEvent(msBuilder, scheduledEvent.ID, identity) test.AddActivityTaskCompletedEvent(msBuilder, scheduledEvent.ID, startedEvent.ID, nil, identity) // Use full history after the activity start/completion historySnapshot := msBuilder.GetHistoryBuilder().GetHistory() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&p.GetWorkflowExecutionResponse{State: msSnapshot}, nil).Once() s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.Anything).Return(&p.ReadHistoryBranchResponse{HistoryEvents: historySnapshot.Events}, nil).Once() // Expect that mutable state will be updated to delete resurrected activity s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.UpdateWorkflowExecutionRequest) bool { return len(request.UpdateWorkflowMutation.DeleteActivityInfos) == 1 })).Return(&p.UpdateWorkflowExecutionResponse{}, nil).Once() // Ensure enough time passed timeSource.Advance(time.Hour) _, err := s.historyEngine.RecordActivityTaskStarted(context.Background(), &types.RecordActivityTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: scheduledEvent.ID, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForActivityTaskRequest{TaskList: &types.TaskList{Name: tl}, Identity: identity}, }) s.Equal(err, workflow.ErrActivityTaskNotFound) } func (s *engine2Suite) TestRecordActivityTaskStartedStaleState() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(workflow.ConditionalRetryCount + 1) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, true) ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &p.GetWorkflowExecutionResponse{State: ms1} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Times(workflow.ConditionalRetryCount) response, err := s.historyEngine.RecordActivityTaskStarted(context.Background(), &types.RecordActivityTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 5, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForActivityTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Error(err) s.Nil(response) s.Equal(workflow.ErrMaxAttemptsExceeded, err) } func (s *engine2Suite) TestRecordActivityTaskStartedActivityNotPending() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, true) ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &p.GetWorkflowExecutionResponse{State: ms1} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() response, err := s.historyEngine.RecordActivityTaskStarted(context.Background(), &types.RecordActivityTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 3, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForActivityTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Error(err) s.Nil(response) s.Equal(workflow.ErrActivityTaskNotFound, err) } func (s *engine2Suite) TestRecordActivityTaskStartedActivityAlreadyStarted() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(6) activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, true) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, int64(2), int64(3), nil, identity) scheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &p.GetWorkflowExecutionResponse{State: ms1} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() s.mockEventsCache.EXPECT().GetEvent( gomock.Any(), gomock.Any(), domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID(), decisionCompletedEvent.ID, scheduledEvent.ID, gomock.Any(), ).Return(scheduledEvent, nil).Times(1) // start activity response, err := s.historyEngine.RecordActivityTaskStarted(context.Background(), &types.RecordActivityTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 5, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForActivityTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(err) s.NotNil(response) s.Equal(scheduledEvent, response.ScheduledEvent) // another request made with the same scheduleID and same requestID s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() s.mockEventsCache.EXPECT().GetEvent( gomock.Any(), gomock.Any(), domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID(), decisionCompletedEvent.ID, scheduledEvent.ID, gomock.Any(), ).Return(scheduledEvent, nil).Times(1) response, err = s.historyEngine.RecordActivityTaskStarted(context.Background(), &types.RecordActivityTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 5, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForActivityTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Nil(err) s.NotNil(response) s.Equal(scheduledEvent, response.ScheduledEvent) // another request made with the same scheduleID and different requestID s.mockEventsCache.EXPECT().GetEvent( gomock.Any(), gomock.Any(), domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID(), decisionCompletedEvent.ID, scheduledEvent.ID, gomock.Any(), ).Return(scheduledEvent, nil).Times(1) response, err = s.historyEngine.RecordActivityTaskStarted(context.Background(), &types.RecordActivityTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &workflowExecution, ScheduleID: 5, TaskID: 100, RequestID: "otherReqId", PollRequest: &types.PollForActivityTaskRequest{ TaskList: &types.TaskList{ Name: tl, }, Identity: identity, }, }) s.Error(err) s.Nil(response) s.Equal(&types.EventAlreadyStartedError{Message: "Activity task already started."}, err) } func (s *engine2Suite) TestRequestCancelWorkflowExecutionSuccess() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, false) ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &p.GetWorkflowExecutionResponse{State: ms1} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() err := s.historyEngine.RequestCancelWorkflowExecution(context.Background(), &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: domainID, CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, Identity: "identity", }, }) s.Nil(err) executionBuilder := s.getBuilder(domainID, workflowExecution) s.Equal(int64(4), executionBuilder.GetNextEventID()) } func (s *engine2Suite) TestRequestCancelWorkflowExecutionDuplicateRequestError() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, false) ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &p.GetWorkflowExecutionResponse{State: ms1} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.DuplicateRequestError{RunID: "test-run-id"}).Once() err := s.historyEngine.RequestCancelWorkflowExecution(context.Background(), &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: domainID, CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, Identity: "identity", }, }) s.Nil(err) } func (s *engine2Suite) TestRequestCancelWorkflowExecutionAlreadyCancelled_Success() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" cancelRequestID := "cancelrequestid" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, false) msBuilder.GetExecutionInfo().State = p.WorkflowStateCompleted msBuilder.GetExecutionInfo().CloseStatus = p.WorkflowCloseStatusCanceled msBuilder.GetExecutionInfo().CancelRequested = true msBuilder.GetExecutionInfo().CancelRequestID = cancelRequestID ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &p.GetWorkflowExecutionResponse{State: ms1} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() err := s.historyEngine.RequestCancelWorkflowExecution(context.Background(), &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: domainID, CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, Identity: "identity", RequestID: cancelRequestID, }, }) s.Nil(err) } func (s *engine2Suite) TestRequestCancelWorkflowExecutionAlreadyCancelled_Fail() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" cancelRequestID := "cancelrequestid" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, false) msBuilder.GetExecutionInfo().State = p.WorkflowStateCompleted msBuilder.GetExecutionInfo().CancelRequested = true msBuilder.GetExecutionInfo().CancelRequestID = cancelRequestID ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &p.GetWorkflowExecutionResponse{State: ms1} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() err := s.historyEngine.RequestCancelWorkflowExecution(context.Background(), &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: domainID, CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, Identity: "identity", RequestID: cancelRequestID + "xxx", }, }) s.NotNil(err) s.IsType(&types.WorkflowExecutionAlreadyCompletedError{}, err) } func (s *engine2Suite) TestRequestCancelWorkflowExecutionFail() { domainID := constants.TestDomainID workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" tl := "testTaskList" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) msBuilder := s.createExecutionStartedState(workflowExecution, tl, identity, false) msBuilder.GetExecutionInfo().State = p.WorkflowStateCompleted ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &p.GetWorkflowExecutionResponse{State: ms1} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() err := s.historyEngine.RequestCancelWorkflowExecution(context.Background(), &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: domainID, CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, }, Identity: "identity", }, }) s.NotNil(err) s.IsType(&types.WorkflowExecutionAlreadyCompletedError{}, err) } func (s *engine2Suite) createExecutionStartedState( we types.WorkflowExecution, tl, identity string, startDecision bool, ) execution.MutableState { msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, s.logger, we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) if startDecision { test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) } _ = msBuilder.SetHistoryTree(we.GetRunID()) return msBuilder } //nolint:unused //lint:ignore U1000 for printing within tests func (s *engine2Suite) printHistory(builder execution.MutableState) string { return thrift.FromHistory(builder.GetHistoryBuilder().GetHistory()).String() } func (s *engine2Suite) TestRespondDecisionTaskCompletedRecordMarkerDecision() { domainID := constants.TestDomainID we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: we.GetRunID(), ScheduleID: 2, }) identity := "testIdentity" markerDetails := []byte("marker details") markerName := "marker name" testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(3) msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeRecordMarker.Ptr(), RecordMarkerDecisionAttributes: &types.RecordMarkerDecisionAttributes{ MarkerName: markerName, Details: markerDetails, }, }} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() _, err := s.historyEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: domainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: nil, Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(domainID, we) s.Equal(int64(6), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(p.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) } func (s *engine2Suite) TestStartWorkflowExecution_BrandNew() { domainID := constants.TestDomainID workflowID := "workflowID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" partitionConfig := map[string]string{ "zone": "phx", } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) })).Return(&p.CreateWorkflowExecutionResponse{}, nil).Once() requestID := uuid.New() resp, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: requestID, }, PartitionConfig: partitionConfig, }) s.Nil(err) s.NotNil(resp.RunID) } func (s *engine2Suite) TestStartWorkflowExecution_BrandNew_DuplicateRequestError() { domainID := constants.TestDomainID workflowID := "workflowID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" partitionConfig := map[string]string{ "zone": "phx", } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) })).Return(nil, &p.DuplicateRequestError{RunID: "test-run-id"}).Once() requestID := uuid.New() resp, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: requestID, }, PartitionConfig: partitionConfig, }) s.NoError(err) s.Equal("test-run-id", resp.RunID) } func (s *engine2Suite) TestStartWorkflowExecution_BrandNew_DuplicateRequestError_TypeMismatch() { domainID := constants.TestDomainID workflowID := "workflowID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" partitionConfig := map[string]string{ "zone": "phx", } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockHistoryV2Mgr.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) })).Return(nil, &p.DuplicateRequestError{RequestType: p.WorkflowRequestTypeSignal, RunID: "test-run-id"}).Once() requestID := uuid.New() _, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: requestID, }, PartitionConfig: partitionConfig, }) s.Error(err) s.IsType(&p.DuplicateRequestError{}, err) } func (s *engine2Suite) TestStartWorkflowExecution_StillRunning_Dedup() { domainID := constants.TestDomainID workflowID := "workflowID" runID := "runID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" requestID := "requestID" lastWriteVersion := commonconstants.EmptyVersion testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.WorkflowExecutionAlreadyStartedError{ Msg: "random message", StartRequestID: requestID, RunID: runID, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, LastWriteVersion: lastWriteVersion, }).Once() resp, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: requestID, }, }) s.Nil(err) s.Equal(runID, resp.GetRunID()) } func (s *engine2Suite) TestStartWorkflowExecution_StillRunning_NonDeDup() { domainID := constants.TestDomainID workflowID := "workflowID" runID := "runID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" lastWriteVersion := commonconstants.EmptyVersion testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockHistoryV2Mgr.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.WorkflowExecutionAlreadyStartedError{ Msg: "random message", StartRequestID: "oldRequestID", RunID: runID, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, LastWriteVersion: lastWriteVersion, }).Once() resp, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: "newRequestID", }, }) if _, ok := err.(*types.WorkflowExecutionAlreadyStartedError); !ok { s.Fail("return err is not *types.WorkflowExecutionAlreadyStartedError") } s.Nil(resp) } func (s *engine2Suite) TestStartWorkflowExecution_NotRunning_PrevSuccess_DuplicateRequestError() { domainID := constants.TestDomainID workflowID := "workflowID" runID := "runID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" lastWriteVersion := commonconstants.EmptyVersion partitionConfig := map[string]string{ "zone": "phx", } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On( "CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return request.Mode == p.CreateWorkflowModeBrandNew && !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) }), ).Return(nil, &p.WorkflowExecutionAlreadyStartedError{ Msg: "random message", StartRequestID: "oldRequestID", RunID: runID, State: p.WorkflowStateCompleted, CloseStatus: p.WorkflowCloseStatusCompleted, LastWriteVersion: lastWriteVersion, }).Once() s.mockExecutionMgr.On( "CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return request.Mode == p.CreateWorkflowModeWorkflowIDReuse && request.PreviousRunID == runID && request.PreviousLastWriteVersion == lastWriteVersion && !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) }), ).Return(nil, &p.DuplicateRequestError{RequestType: p.WorkflowRequestTypeStart, RunID: "test-run-id"}).Once() resp, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: "newRequestID", WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), }, PartitionConfig: partitionConfig, }) s.Nil(err) s.Equal("test-run-id", resp.RunID) } func (s *engine2Suite) TestStartWorkflowExecution_NotRunning_PrevSuccess_DuplicateRequestError_TypeMismatch() { domainID := constants.TestDomainID workflowID := "workflowID" runID := "runID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" lastWriteVersion := commonconstants.EmptyVersion partitionConfig := map[string]string{ "zone": "phx", } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On( "CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return request.Mode == p.CreateWorkflowModeBrandNew && !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) }), ).Return(nil, &p.WorkflowExecutionAlreadyStartedError{ Msg: "random message", StartRequestID: "oldRequestID", RunID: runID, State: p.WorkflowStateCompleted, CloseStatus: p.WorkflowCloseStatusCompleted, LastWriteVersion: lastWriteVersion, }).Once() s.mockHistoryV2Mgr.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(nil).Once() s.mockExecutionMgr.On( "CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return request.Mode == p.CreateWorkflowModeWorkflowIDReuse && request.PreviousRunID == runID && request.PreviousLastWriteVersion == lastWriteVersion && !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) }), ).Return(nil, &p.DuplicateRequestError{RequestType: p.WorkflowRequestTypeSignal, RunID: "test-run-id"}).Once() _, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: "newRequestID", WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), }, PartitionConfig: partitionConfig, }) s.Error(err) s.IsType(&p.DuplicateRequestError{}, err) } func (s *engine2Suite) TestStartWorkflowExecution_NotRunning_PrevSuccess() { domainID := constants.TestDomainID workflowID := "workflowID" runID := "runID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" lastWriteVersion := commonconstants.EmptyVersion partitionConfig := map[string]string{ "zone": "phx", } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } options := []types.WorkflowIDReusePolicy{ types.WorkflowIDReusePolicyAllowDuplicateFailedOnly, types.WorkflowIDReusePolicyAllowDuplicate, types.WorkflowIDReusePolicyRejectDuplicate, } expectedErrs := []bool{true, false, true} s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Times(3) s.mockHistoryV2Mgr.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(nil).Times(2) s.mockExecutionMgr.On( "CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return request.Mode == p.CreateWorkflowModeBrandNew && !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) }), ).Return(nil, &p.WorkflowExecutionAlreadyStartedError{ Msg: "random message", StartRequestID: "oldRequestID", RunID: runID, State: p.WorkflowStateCompleted, CloseStatus: p.WorkflowCloseStatusCompleted, LastWriteVersion: lastWriteVersion, }).Times(len(expectedErrs)) for index, option := range options { s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) if !expectedErrs[index] { s.mockExecutionMgr.On( "CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return request.Mode == p.CreateWorkflowModeWorkflowIDReuse && request.PreviousRunID == runID && request.PreviousLastWriteVersion == lastWriteVersion && !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) }), ).Return(&p.CreateWorkflowExecutionResponse{}, nil).Once() } resp, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: "newRequestID", WorkflowIDReusePolicy: &option, }, PartitionConfig: partitionConfig, }) if expectedErrs[index] { if _, ok := err.(*types.WorkflowExecutionAlreadyStartedError); !ok { s.Fail("return err is not *types.WorkflowExecutionAlreadyStartedError") } s.Nil(resp) } else { s.Nil(err) s.NotNil(resp) } } } func (s *engine2Suite) TestStartWorkflowExecution_NotRunning_PrevFail() { domainID := constants.TestDomainID workflowID := "workflowID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" lastWriteVersion := commonconstants.EmptyVersion partitionConfig := map[string]string{ "zone": "phx", } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } options := []types.WorkflowIDReusePolicy{ types.WorkflowIDReusePolicyAllowDuplicateFailedOnly, types.WorkflowIDReusePolicyAllowDuplicate, types.WorkflowIDReusePolicyRejectDuplicate, } expectedErrs := []bool{false, false, true} closeStates := []int{ p.WorkflowCloseStatusFailed, p.WorkflowCloseStatusCanceled, p.WorkflowCloseStatusTerminated, p.WorkflowCloseStatusTimedOut, } runIDs := []string{"1", "2", "3", "4"} for i, closeState := range closeStates { s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Times(3) s.mockHistoryV2Mgr.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(nil).Times(1) s.mockExecutionMgr.On( "CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return request.Mode == p.CreateWorkflowModeBrandNew && !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) }), ).Return(nil, &p.WorkflowExecutionAlreadyStartedError{ Msg: "random message", StartRequestID: "oldRequestID", RunID: runIDs[i], State: p.WorkflowStateCompleted, CloseStatus: closeState, LastWriteVersion: lastWriteVersion, }).Times(len(expectedErrs)) for j, option := range options { s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil) if !expectedErrs[j] { s.mockExecutionMgr.On( "CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return request.Mode == p.CreateWorkflowModeWorkflowIDReuse && request.PreviousRunID == runIDs[i] && request.PreviousLastWriteVersion == lastWriteVersion && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) }), ).Return(&p.CreateWorkflowExecutionResponse{}, nil).Once() } resp, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: "newRequestID", WorkflowIDReusePolicy: &option, }, PartitionConfig: partitionConfig, }) if expectedErrs[j] { if _, ok := err.(*types.WorkflowExecutionAlreadyStartedError); !ok { s.Fail("return err is not *types.WorkflowExecutionAlreadyStartedError") } s.Nil(resp) } else { s.Nil(err) s.NotNil(resp) } } } } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_JustSignal() { domainID := constants.TestDomainID workflowID := "wId" runID := constants.TestRunID identity := "testIdentity" signalName := "my signal name" input := []byte("test input") testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, Identity: identity, SignalName: signalName, Input: input, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), runID, constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} gceResponse := &p.GetCurrentExecutionResponse{RunID: runID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(err) s.Equal(runID, resp.GetRunID()) } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_JustSignal_DuplicateRequestError() { domainID := constants.TestDomainID workflowID := "wId" runID := constants.TestRunID identity := "testIdentity" signalName := "my signal name" input := []byte("test input") testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, Identity: identity, SignalName: signalName, Input: input, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), runID, constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} gceResponse := &p.GetCurrentExecutionResponse{RunID: runID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.DuplicateRequestError{RequestType: p.WorkflowRequestTypeSignal, RunID: "test-run-id"}).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(err) s.Equal("test-run-id", resp.GetRunID()) } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_JustSignal_DuplicateRequestError_TypeMismatch() { domainID := constants.TestDomainID workflowID := "wId" runID := constants.TestRunID identity := "testIdentity" signalName := "my signal name" input := []byte("test input") testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, Identity: identity, SignalName: signalName, Input: input, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), runID, constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} gceResponse := &p.GetCurrentExecutionResponse{RunID: runID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.DuplicateRequestError{RequestType: p.WorkflowRequestTypeStart, RunID: "test-run-id"}).Once() _, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Error(err) s.IsType(&p.DuplicateRequestError{}, err) } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_WorkflowNotExist() { domainID := constants.TestDomainID workflowID := "wId" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") requestID := uuid.New() partitionConfig := map[string]string{ "zone": "phx", } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, }, PartitionConfig: partitionConfig, } notExistErr := &types.EntityNotExistsError{Message: "Workflow not exist"} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(nil, notExistErr).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) })).Return(&p.CreateWorkflowExecutionResponse{}, nil).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(err) s.NotNil(resp.GetRunID()) } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_WorkflowNotExist_DuplicateRequestError() { domainID := constants.TestDomainID workflowID := "wId" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") requestID := uuid.New() partitionConfig := map[string]string{ "zone": "phx", } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, }, PartitionConfig: partitionConfig, } notExistErr := &types.EntityNotExistsError{Message: "Workflow not exist"} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(nil, notExistErr).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) })).Return(nil, &p.DuplicateRequestError{RunID: "test-run-id"}).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(err) s.Equal("test-run-id", resp.GetRunID()) } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_WorkflowNotExist_DuplicateRequestError_TypeMismatch() { domainID := constants.TestDomainID workflowID := "wId" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") requestID := uuid.New() partitionConfig := map[string]string{ "zone": "phx", } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, }, PartitionConfig: partitionConfig, } notExistErr := &types.EntityNotExistsError{Message: "Workflow not exist"} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(nil, notExistErr).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockHistoryV2Mgr.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) })).Return(nil, &p.DuplicateRequestError{RequestType: p.WorkflowRequestTypeCancel, RunID: "test-run-id"}).Once() _, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Error(err) s.IsType(&p.DuplicateRequestError{}, err) } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_CreateTimeout() { domainID := constants.TestDomainID workflowID := "wId" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") requestID := uuid.New() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, }, } notExistErr := &types.EntityNotExistsError{Message: "Workflow not exist"} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(nil, notExistErr).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.TimeoutError{}).Once() s.mockShardManager.On("UpdateShard", mock.Anything, mock.Anything).Return(nil).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.True(p.IsTimeoutError(err)) s.NotNil(resp.GetRunID()) } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_WorkflowNotRunning() { domainID := constants.TestDomainID workflowID := "wId" runID := constants.TestRunID workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") requestID := uuid.New() partitionConfig := map[string]string{ "zone": "phx", } policy := types.WorkflowIDReusePolicyAllowDuplicate testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, WorkflowIDReusePolicy: &policy, }, PartitionConfig: partitionConfig, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), runID, constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) ms.ExecutionInfo.State = p.WorkflowStateCompleted gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} gceResponse := &p.GetCurrentExecutionResponse{RunID: runID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) })).Return(&p.CreateWorkflowExecutionResponse{}, nil).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(err) s.NotNil(resp.GetRunID()) s.NotEqual(runID, resp.GetRunID()) } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_Start_DuplicateRequests() { domainID := constants.TestDomainID workflowID := "wId" runID := constants.TestRunID workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") requestID := "testRequestID" policy := types.WorkflowIDReusePolicyAllowDuplicate testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, WorkflowIDReusePolicy: &policy, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), runID, constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) ms.ExecutionInfo.State = p.WorkflowStateCompleted gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} gceResponse := &p.GetCurrentExecutionResponse{RunID: runID} workflowAlreadyStartedErr := &p.WorkflowExecutionAlreadyStartedError{ Msg: "random message", StartRequestID: requestID, // use same requestID RunID: runID, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, LastWriteVersion: commonconstants.EmptyVersion, } s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, workflowAlreadyStartedErr).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(err) s.NotNil(resp.GetRunID()) s.Equal(runID, resp.GetRunID()) } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_Start_DuplicateRequestError() { domainID := constants.TestDomainID workflowID := "wId" runID := constants.TestRunID workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") requestID := "testRequestID" policy := types.WorkflowIDReusePolicyAllowDuplicate testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, WorkflowIDReusePolicy: &policy, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), runID, constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) ms.ExecutionInfo.State = p.WorkflowStateCompleted gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} gceResponse := &p.GetCurrentExecutionResponse{RunID: runID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &p.DuplicateRequestError{RunID: "test-run-id"}).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(err) s.Equal("test-run-id", resp.GetRunID()) } func (s *engine2Suite) TestSignalWithStartWorkflowExecution_Start_WorkflowAlreadyStarted() { domainID := constants.TestDomainID workflowID := "wId" runID := constants.TestRunID workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") requestID := "testRequestID" policy := types.WorkflowIDReusePolicyAllowDuplicate testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, WorkflowIDReusePolicy: &policy, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), runID, constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) ms.ExecutionInfo.State = p.WorkflowStateCompleted gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} gceResponse := &p.GetCurrentExecutionResponse{RunID: runID} workflowAlreadyStartedErr := &p.WorkflowExecutionAlreadyStartedError{ Msg: "random message", StartRequestID: "new request ID", RunID: runID, State: p.WorkflowStateRunning, CloseStatus: p.WorkflowCloseStatusNone, LastWriteVersion: commonconstants.EmptyVersion, } s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockHistoryV2Mgr.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, workflowAlreadyStartedErr).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(resp) s.NotNil(err) } func (s *engine2Suite) TestTerminateWorkflowExecution_Success() { } func (s *engine2Suite) TestNewChildContext() { ctx := context.Background() childCtx, childCancel := s.historyEngine.newChildContext(ctx) defer childCancel() _, ok := childCtx.Deadline() s.True(ok) ctx, cancel := context.WithTimeout(ctx, time.Hour) defer cancel() childCtx, childCancel = s.historyEngine.newChildContext(ctx) defer childCancel() deadline, ok := childCtx.Deadline() s.True(ok) s.True(time.Until(deadline) < 10*time.Minute) } func (s *engine2Suite) getBuilder(domainID string, we types.WorkflowExecution) execution.MutableState { context, release, err := s.historyEngine.executionCache.GetOrCreateWorkflowExecutionForBackground(domainID, we) if err != nil { return nil } defer release(nil) return context.GetWorkflowExecution() } ================================================ FILE: service/history/engine/engineimpl/history_engine3_eventsv2_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "reflect" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/decision" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/shard" test "github.com/uber/cadence/service/history/testing" ) type ( engine3Suite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockTxProcessor *queue.MockProcessor mockTimerProcessor *queue.MockProcessor mockEventsCache *events.MockCache mockDomainCache *cache.MockDomainCache historyEngine *historyEngineImpl mockExecutionMgr *mocks.ExecutionManager mockHistoryV2Mgr *mocks.HistoryV2Manager config *config.Config logger log.Logger } ) func TestEngine3Suite(t *testing.T) { s := new(engine3Suite) suite.Run(t, s) } func (s *engine3Suite) SetupSuite() { s.config = config.NewForTest() } func (s *engine3Suite) TearDownSuite() { } func (s *engine3Suite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockTxProcessor = queue.NewMockProcessor(s.controller) s.mockTimerProcessor = queue.NewMockProcessor(s.controller) s.mockTxProcessor.EXPECT().NotifyNewTask(gomock.Any(), gomock.Any()).AnyTimes() s.mockTimerProcessor.EXPECT().NotifyNewTask(gomock.Any(), gomock.Any()).AnyTimes() s.mockShard = shard.NewTestContext( s.T(), s.controller, &p.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, s.config, ) s.mockExecutionMgr = s.mockShard.Resource.ExecutionMgr s.mockHistoryV2Mgr = s.mockShard.Resource.HistoryMgr s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockEventsCache = s.mockShard.MockEventsCache s.mockEventsCache.EXPECT().PutEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() s.logger = s.mockShard.GetLogger() h := &historyEngineImpl{ currentClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), shard: s.mockShard, clusterMetadata: s.mockShard.Resource.ClusterMetadata, executionManager: s.mockExecutionMgr, historyV2Mgr: s.mockHistoryV2Mgr, executionCache: execution.NewCache(s.mockShard), logger: s.logger, throttledLogger: s.logger, metricsClient: metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), tokenSerializer: common.NewJSONTaskTokenSerializer(), config: s.config, timeSource: s.mockShard.GetTimeSource(), historyEventNotifier: events.NewNotifier(clock.NewRealTimeSource(), metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), func(string) int { return 0 }), queueProcessors: map[p.HistoryTaskCategory]queue.Processor{ p.HistoryTaskCategoryTransfer: s.mockTxProcessor, p.HistoryTaskCategoryTimer: s.mockTimerProcessor, }, activeClusterManager: s.mockShard.Resource.ActiveClusterMgr, } s.mockShard.SetEngine(h) h.decisionHandler = decision.NewHandler(s.mockShard, h.executionCache, h.tokenSerializer) s.historyEngine = h } func (s *engine3Suite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *engine3Suite) TestRecordDecisionTaskStartedSuccessStickyEnabled() { testDomainEntry := cache.NewLocalDomainCacheEntryForTest( &p.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName}, &p.DomainConfig{Retention: 1}, "", ) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(testDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(constants.TestDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(testDomainEntry, nil).AnyTimes() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: testDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(2) domainID := constants.TestDomainID we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" stickyTl := "stickyTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) executionInfo := msBuilder.GetExecutionInfo() executionInfo.LastUpdatedTimestamp = time.Now() executionInfo.StickyTaskList = stickyTl test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() request := types.RecordDecisionTaskStartedRequest{ DomainUUID: domainID, WorkflowExecution: &we, ScheduleID: 2, TaskID: 100, RequestID: "reqId", PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: stickyTl, }, Identity: identity, }, } expectedResponse := types.RecordDecisionTaskStartedResponse{} expectedResponse.WorkflowType = msBuilder.GetWorkflowType() executionInfo = msBuilder.GetExecutionInfo() if executionInfo.LastProcessedEvent != commonconstants.EmptyEventID { expectedResponse.PreviousStartedEventID = common.Int64Ptr(executionInfo.LastProcessedEvent) } expectedResponse.ScheduledEventID = di.ScheduleID expectedResponse.StartedEventID = di.ScheduleID + 1 expectedResponse.StickyExecutionEnabled = true expectedResponse.NextEventID = msBuilder.GetNextEventID() + 1 expectedResponse.Attempt = di.Attempt expectedResponse.WorkflowExecutionTaskList = &types.TaskList{ Name: executionInfo.TaskList, Kind: types.TaskListKindNormal.Ptr(), } expectedResponse.BranchToken = msBuilder.GetExecutionInfo().BranchToken response, err := s.historyEngine.RecordDecisionTaskStarted(context.Background(), &request) s.Nil(err) s.NotNil(response) expectedResponse.StartedTimestamp = response.StartedTimestamp expectedResponse.ScheduledTimestamp = response.ScheduledTimestamp expectedResponse.Queries = make(map[string]*types.WorkflowQuery) s.Equal(&expectedResponse, response) } func (s *engine3Suite) TestStartWorkflowExecution_BrandNew() { testDomainEntry := cache.NewLocalDomainCacheEntryForTest( &p.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName}, &p.DomainConfig{Retention: 1}, "", ) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(testDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(constants.TestDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(testDomainEntry, nil).AnyTimes() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() domainID := constants.TestDomainID workflowID := "workflowID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" partitionConfig := map[string]string{ "zone": "phx", } s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig, partitionConfig) })).Return(&p.CreateWorkflowExecutionResponse{}, nil).Once() requestID := uuid.New() resp, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: requestID, }, PartitionConfig: partitionConfig, }) s.Nil(err) s.NotNil(resp.RunID) } func (s *engine3Suite) TestStartWorkflowExecution_DeprecatedDomain() { domainID := constants.TestDomainID workflowID := "workflowID" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" requestID := uuid.New() testDomainEntry := cache.NewLocalDomainCacheEntryForTest( &p.DomainInfo{ID: domainID, Name: constants.TestDomainName, Status: p.DomainStatusDeprecated}, &p.DomainConfig{Retention: 1}, "", ) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(testDomainEntry, nil) _, err := s.historyEngine.StartWorkflowExecution(context.Background(), &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: domainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, RequestID: requestID, }, }) s.IsType(&types.BadRequestError{}, err) } func (s *engine3Suite) TestSignalWithStartWorkflowExecution_JustSignal() { testDomainEntry := cache.NewLocalDomainCacheEntryForTest( &p.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName}, &p.DomainConfig{Retention: 1}, "", ) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(testDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(constants.TestDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(testDomainEntry, nil).AnyTimes() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: testDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: testDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() domainID := constants.TestDomainID workflowID := "wId" runID := constants.TestRunID identity := "testIdentity" signalName := "my signal name" input := []byte("test input") sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, Identity: identity, SignalName: signalName, Input: input, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.historyEngine.shard, testlogger.New(s.Suite.T()), runID, constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &p.GetWorkflowExecutionResponse{State: ms} gceResponse := &p.GetCurrentExecutionResponse{RunID: runID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&p.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &p.MutableStateUpdateSessionStats{}, }, nil).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(err) s.Equal(runID, resp.GetRunID()) } func (s *engine3Suite) TestSignalWithStartWorkflowExecution_WorkflowNotExist() { testDomainEntry := cache.NewLocalDomainCacheEntryForTest( &p.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName}, &p.DomainConfig{Retention: 1}, "", ) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(testDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(constants.TestDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(testDomainEntry, nil).AnyTimes() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() domainID := constants.TestDomainID workflowID := "wId" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") requestID := uuid.New() partitionConfig := map[string]string{ "zone": "phx", } sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, }, PartitionConfig: partitionConfig, } notExistErr := &types.EntityNotExistsError{Message: "Workflow not exist"} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(nil, notExistErr).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&p.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.MatchedBy(func(request *p.CreateWorkflowExecutionRequest) bool { return !request.NewWorkflowSnapshot.ExecutionInfo.StartTimestamp.IsZero() && reflect.DeepEqual(partitionConfig, request.NewWorkflowSnapshot.ExecutionInfo.PartitionConfig) })).Return(&p.CreateWorkflowExecutionResponse{}, nil).Once() resp, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(err) s.NotNil(resp.GetRunID()) } func (s *engine3Suite) TestSignalWithStartWorkflowExecution_DeprecatedDomain() { domainID := constants.TestDomainID workflowID := "wId" workflowType := "workflowType" taskList := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") requestID := uuid.New() testDomainEntry := cache.NewLocalDomainCacheEntryForTest( &p.DomainInfo{ID: domainID, Name: constants.TestDomainName, Status: p.DomainStatusDeprecated}, &p.DomainConfig{Retention: 1}, "", ) sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, }, } s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(testDomainEntry, nil) _, err := s.historyEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.IsType(&types.BadRequestError{}, err) } func (s *engine3Suite) TestSignalWorkflowExecution_DeprecatedDomain() { we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } identity := "testIdentity" signalName := "my signal name" input := []byte("test input") signalRequest := &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ Domain: constants.TestDomainID, WorkflowExecution: &we, Identity: identity, SignalName: signalName, Input: input, }, } testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.historyEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) testDomainEntry := cache.NewLocalDomainCacheEntryForTest( &p.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName, Status: p.DomainStatusDeprecated}, &p.DomainConfig{Retention: 1}, "", ) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(testDomainEntry, nil) err := s.historyEngine.SignalWorkflowExecution(context.Background(), signalRequest) s.IsType(&types.BadRequestError{}, err) } ================================================ FILE: service/history/engine/engineimpl/history_engine_start_test.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "testing" "go.uber.org/goleak" "github.com/uber/cadence/service/history/engine/testdata" ) func TestHistoryEngineStartStop(t *testing.T) { t.Cleanup(func() { goleak.VerifyNone(t) }) eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) eft.Engine.Start() eft.Engine.Stop() } ================================================ FILE: service/history/engine/engineimpl/history_engine_test.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "encoding/json" "errors" "fmt" "sync" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "go.uber.org/yarpc/api/encoding" "go.uber.org/yarpc/api/transport" hclient "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" cc "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/decision" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/ndc" "github.com/uber/cadence/service/history/query" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/reset" "github.com/uber/cadence/service/history/shard" test "github.com/uber/cadence/service/history/testing" "github.com/uber/cadence/service/history/workflow" ) type ( engineSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockTxProcessor *queue.MockProcessor mockTimerProcessor *queue.MockProcessor mockDomainCache *cache.MockDomainCache mockHistoryClient *hclient.MockClient mockMatchingClient *matching.MockClient mockEventsReapplier *ndc.MockEventsReapplier mockWorkflowResetter *reset.MockWorkflowResetter mockHistoryEngine *historyEngineImpl mockExecutionMgr *mocks.ExecutionManager mockHistoryV2Mgr *mocks.HistoryV2Manager mockShardManager *mocks.ShardManager eventsCache events.Cache config *config.Config } ) func TestEngineSuite(t *testing.T) { s := new(engineSuite) suite.Run(t, s) } func (s *engineSuite) SetupSuite() { s.config = config.NewForTest() } func (s *engineSuite) TearDownSuite() { } func (s *engineSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockTxProcessor = queue.NewMockProcessor(s.controller) s.mockTimerProcessor = queue.NewMockProcessor(s.controller) s.mockEventsReapplier = ndc.NewMockEventsReapplier(s.controller) s.mockWorkflowResetter = reset.NewMockWorkflowResetter(s.controller) s.mockTxProcessor.EXPECT().NotifyNewTask(gomock.Any(), gomock.Any()).AnyTimes() s.mockTimerProcessor.EXPECT().NotifyNewTask(gomock.Any(), gomock.Any()).AnyTimes() s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ RangeID: 1, TransferAckLevel: 0, }, s.config, ) s.eventsCache = events.NewCache( s.mockShard.GetShardID(), s.mockShard.GetHistoryManager(), s.config, s.mockShard.GetLogger(), s.mockShard.GetMetricsClient(), s.mockDomainCache, ) s.mockShard.SetEventsCache(s.eventsCache) s.mockMatchingClient = s.mockShard.Resource.MatchingClient s.mockHistoryClient = s.mockShard.Resource.HistoryClient s.mockExecutionMgr = s.mockShard.Resource.ExecutionMgr s.mockHistoryV2Mgr = s.mockShard.Resource.HistoryMgr s.mockShardManager = s.mockShard.Resource.ShardMgr s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestLocalDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(constants.TestActiveActiveDomainID).Return(constants.TestActiveActiveDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(constants.TestActiveActiveDomainID).Return(constants.TestDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainID(constants.TestDomainName).Return(constants.TestDomainID, nil).AnyTimes() historyEventNotifier := events.NewNotifier( clock.NewRealTimeSource(), s.mockShard.Resource.MetricsClient, func(workflowID string) int { return len(workflowID) }, ) h := &historyEngineImpl{ currentClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), shard: s.mockShard, timeSource: s.mockShard.GetTimeSource(), clusterMetadata: s.mockShard.Resource.ClusterMetadata, executionManager: s.mockExecutionMgr, historyV2Mgr: s.mockHistoryV2Mgr, executionCache: execution.NewCache(s.mockShard), logger: s.mockShard.GetLogger(), metricsClient: s.mockShard.GetMetricsClient(), tokenSerializer: common.NewJSONTaskTokenSerializer(), historyEventNotifier: historyEventNotifier, config: config.NewForTest(), queueProcessors: map[persistence.HistoryTaskCategory]queue.Processor{ persistence.HistoryTaskCategoryTransfer: s.mockTxProcessor, persistence.HistoryTaskCategoryTimer: s.mockTimerProcessor, }, clientChecker: cc.NewVersionChecker(), eventsReapplier: s.mockEventsReapplier, workflowResetter: s.mockWorkflowResetter, activeClusterManager: s.mockShard.Resource.ActiveClusterMgr, } s.mockShard.SetEngine(h) h.decisionHandler = decision.NewHandler(s.mockShard, h.executionCache, h.tokenSerializer) h.historyEventNotifier.Start() s.mockHistoryEngine = h } func (s *engineSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) s.mockHistoryEngine.historyEventNotifier.Stop() } func (s *engineSuite) TestGetMutableStateSync() { ctx := context.Background() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-get-workflow-execution-event-id", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) msBuilder.SetVersionHistories(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("token"), Items: []*persistence.VersionHistoryItem{ { EventID: 2, Version: 1, }, }, }, }, }) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} // right now the next event ID is 4 s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() // test get the next event ID instantly response, err := s.mockHistoryEngine.GetMutableState(ctx, &types.GetMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: &workflowExecution, }) s.Nil(err) s.Equal(int64(4), response.GetNextEventID()) } func (s *engineSuite) TestGetMutableState_IntestRunID() { ctx := context.Background() execution := types.WorkflowExecution{ WorkflowID: "test-get-workflow-execution-event-id", RunID: "run-id-not-valid-uuid", } _, err := s.mockHistoryEngine.GetMutableState(ctx, &types.GetMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: &execution, }) s.Equal(constants.ErrRunIDNotValid, err) } func (s *engineSuite) TestGetMutableState_EmptyRunID() { ctx := context.Background() execution := types.WorkflowExecution{ WorkflowID: "test-get-workflow-execution-event-id", } s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() _, err := s.mockHistoryEngine.GetMutableState(ctx, &types.GetMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: &execution, }) s.Equal(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestGetMutableState_NoVersionHistories() { ctx := context.Background() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-get-workflow-execution-event-id", RunID: constants.TestRunID, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) // simulate having no version histories s.Require().NoError(msBuilder.SetVersionHistories(nil)) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() // return immediately, since the expected next event ID appears mutableState, err := s.mockHistoryEngine.GetMutableState(ctx, &types.GetMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: &workflowExecution, // we request for the specific history version, but there are no history versions for the execution VersionHistoryItem: &types.VersionHistoryItem{ EventID: 1, Version: 1, }, }) s.ErrorContains(err, "version histories do not exist") s.Nil(mutableState) } func (s *engineSuite) TestGetMutableStateLongPoll() { ctx := context.Background() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-get-workflow-execution-event-id", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) msBuilder.SetVersionHistories(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("token"), Items: []*persistence.VersionHistoryItem{ { EventID: 2, Version: 1, }, }, }, }, }) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} // right now the next event ID is 4 s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() // test long poll on next event ID change waitGroup := &sync.WaitGroup{} waitGroup.Add(1) asycWorkflowUpdate := func(delay time.Duration) { taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, ScheduleID: 2, }) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() timer := time.NewTimer(delay) <-timer.C _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.Nil(err) waitGroup.Done() // right now the next event ID is 5 } // return immediately, since the expected next event ID appears response, err := s.mockHistoryEngine.GetMutableState(ctx, &types.GetMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: &workflowExecution, ExpectedNextEventID: 3, VersionHistoryItem: &types.VersionHistoryItem{ EventID: 1, Version: 1, }, }) s.Nil(err) s.Equal(int64(4), response.NextEventID) // long poll, new event happen before long poll timeout go asycWorkflowUpdate(time.Second * 2) start := time.Now() pollResponse, err := s.mockHistoryEngine.PollMutableState(ctx, &types.PollMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: &workflowExecution, ExpectedNextEventID: 4, VersionHistoryItem: &types.VersionHistoryItem{ EventID: 1, Version: 1, }, }) s.True(time.Now().After(start.Add(time.Second * 1))) s.Nil(err) s.Equal(int64(5), pollResponse.GetNextEventID()) waitGroup.Wait() } func (s *engineSuite) TestGetMutableStateLongPoll_CurrentBranchChanged() { ctx := context.Background() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-get-workflow-execution-event-id", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) msBuilder.SetVersionHistories(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("token"), Items: []*persistence.VersionHistoryItem{ { EventID: 2, Version: 1, }, }, }, }, }) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} // right now the next event ID is 4 s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() // test long poll on next event ID change asyncBranchTokenUpdate := func(delay time.Duration) { timer := time.NewTimer(delay) <-timer.C newExecution := &types.WorkflowExecution{ WorkflowID: workflowExecution.WorkflowID, RunID: workflowExecution.RunID, } s.mockHistoryEngine.historyEventNotifier.NotifyNewHistoryEvent(events.NewNotification( "constants.TestDomainID", newExecution, int64(1), int64(4), int64(1), persistence.WorkflowStateCreated, persistence.WorkflowCloseStatusNone, nil)) } // return immediately, since the expected next event ID appears response0, err := s.mockHistoryEngine.GetMutableState(ctx, &types.GetMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: &workflowExecution, ExpectedNextEventID: 3, }) s.Nil(err) s.Equal(int64(4), response0.GetNextEventID()) // long poll, new event happen before long poll timeout go asyncBranchTokenUpdate(time.Second * 2) start := time.Now() response1, err := s.mockHistoryEngine.GetMutableState(ctx, &types.GetMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: &workflowExecution, ExpectedNextEventID: 10, }) s.True(time.Now().After(start.Add(time.Second * 1))) s.Nil(err) s.Equal(response0.GetCurrentBranchToken(), response1.GetCurrentBranchToken()) } func (s *engineSuite) TestGetMutableStateLongPollTimeout() { ctx := context.Background() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-get-workflow-execution-event-id", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) msBuilder.SetVersionHistories(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("token"), Items: []*persistence.VersionHistoryItem{ { EventID: 2, Version: 1, }, }, }, }, }) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} // right now the next event ID is 4 s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() // long poll, no event happen after long poll timeout response, err := s.mockHistoryEngine.GetMutableState(ctx, &types.GetMutableStateRequest{ DomainUUID: constants.TestDomainID, Execution: &workflowExecution, ExpectedNextEventID: 4, }) s.Nil(err) s.Equal(int64(4), response.GetNextEventID()) } func (s *engineSuite) TestQueryWorkflow_RejectBasedOnNotEnabled() { s.mockHistoryEngine.config.EnableConsistentQueryByDomain = dynamicproperties.GetBoolPropertyFnFilteredByDomain(false) request := &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestDomainID, Request: &types.QueryWorkflowRequest{ QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }, } resp, err := s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.Nil(resp) s.Equal(workflow.ErrConsistentQueryNotEnabled, err) s.mockHistoryEngine.config.EnableConsistentQueryByDomain = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) s.mockHistoryEngine.config.EnableConsistentQuery = dynamicproperties.GetBoolPropertyFn(false) resp, err = s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.Nil(resp) s.Equal(workflow.ErrConsistentQueryNotEnabled, err) } func (s *engineSuite) TestQueryWorkflow_RejectBasedOnCompleted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "TestQueryWorkflow_RejectBasedOnCompleted", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) event := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) di.StartedID = event.ID event = test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, di.StartedID, nil, "some random identity") test.AddCompleteWorkflowEvent(msBuilder, event.ID, nil) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() request := &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestDomainID, Request: &types.QueryWorkflowRequest{ Execution: &workflowExecution, Query: &types.WorkflowQuery{}, QueryRejectCondition: types.QueryRejectConditionNotOpen.Ptr(), }, } resp, err := s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.NoError(err) s.Nil(resp.GetResponse().QueryResult) s.NotNil(resp.GetResponse().QueryRejected) s.Equal(types.WorkflowExecutionCloseStatusCompleted.Ptr(), resp.GetResponse().GetQueryRejected().CloseStatus) } func (s *engineSuite) TestQueryWorkflow_RejectBasedOnFailed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "TestQueryWorkflow_RejectBasedOnFailed", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) event := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) di.StartedID = event.ID event = test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, di.StartedID, nil, "some random identity") test.AddFailWorkflowEvent(msBuilder, event.ID, "failure reason", []byte{1, 2, 3}) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() request := &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestDomainID, Request: &types.QueryWorkflowRequest{ Execution: &workflowExecution, Query: &types.WorkflowQuery{}, QueryRejectCondition: types.QueryRejectConditionNotOpen.Ptr(), }, } resp, err := s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.NoError(err) s.Nil(resp.GetResponse().QueryResult) s.NotNil(resp.GetResponse().QueryRejected) s.Equal(types.WorkflowExecutionCloseStatusFailed.Ptr(), resp.GetResponse().GetQueryRejected().CloseStatus) request = &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestDomainID, Request: &types.QueryWorkflowRequest{ Execution: &workflowExecution, Query: &types.WorkflowQuery{}, QueryRejectCondition: types.QueryRejectConditionNotCompletedCleanly.Ptr(), }, } resp, err = s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.NoError(err) s.Nil(resp.GetResponse().QueryResult) s.NotNil(resp.GetResponse().QueryRejected) s.Equal(types.WorkflowExecutionCloseStatusFailed.Ptr(), resp.GetResponse().GetQueryRejected().CloseStatus) } func (s *engineSuite) TestQueryWorkflow_FirstDecisionNotCompleted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "TestQueryWorkflow_FirstDecisionNotCompleted", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() request := &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestDomainID, Request: &types.QueryWorkflowRequest{ Execution: &workflowExecution, Query: &types.WorkflowQuery{}, }, } resp, err := s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.Equal(workflow.ErrQueryWorkflowBeforeFirstDecision, err) s.Nil(resp) } func (s *engineSuite) TestQueryWorkflow_DirectlyThroughMatching() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "TestQueryWorkflow_DirectlyThroughMatching", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) startedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, startedEvent.ID, nil, identity) di = test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() s.mockMatchingClient.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()).Return(&types.MatchingQueryWorkflowResponse{QueryResult: []byte{1, 2, 3}}, nil) s.mockHistoryEngine.matchingClient = s.mockMatchingClient request := &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestDomainID, Request: &types.QueryWorkflowRequest{ Execution: &workflowExecution, Query: &types.WorkflowQuery{}, // since workflow is open this filter does not reject query QueryRejectCondition: types.QueryRejectConditionNotOpen.Ptr(), QueryConsistencyLevel: types.QueryConsistencyLevelEventual.Ptr(), }, } resp, err := s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.NoError(err) s.NotNil(resp.GetResponse().QueryResult) s.Nil(resp.GetResponse().QueryRejected) s.Equal([]byte{1, 2, 3}, resp.GetResponse().GetQueryResult()) } func (s *engineSuite) TestQueryWorkflow_DecisionTaskDispatch_Timeout() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "TestQueryWorkflow_DecisionTaskDispatch_Timeout", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) startedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, startedEvent.ID, nil, identity) di = test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() request := &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestDomainID, Request: &types.QueryWorkflowRequest{ Execution: &workflowExecution, Query: &types.WorkflowQuery{}, // since workflow is open this filter does not reject query QueryRejectCondition: types.QueryRejectConditionNotOpen.Ptr(), QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }, } wg := &sync.WaitGroup{} wg.Add(1) go func() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) defer cancel() resp, err := s.mockHistoryEngine.QueryWorkflow(ctx, request) s.Error(err) s.Nil(resp) wg.Done() }() <-time.After(time.Second) builder := s.getBuilder(constants.TestDomainID, workflowExecution) s.NotNil(builder) qr := builder.GetQueryRegistry() s.True(qr.HasBufferedQuery()) s.False(qr.HasCompletedQuery()) s.False(qr.HasUnblockedQuery()) s.False(qr.HasFailedQuery()) wg.Wait() s.False(qr.HasBufferedQuery()) s.False(qr.HasCompletedQuery()) s.False(qr.HasUnblockedQuery()) s.False(qr.HasFailedQuery()) } func (s *engineSuite) TestQueryWorkflow_ConsistentQueryBufferFull() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "TestQueryWorkflow_ConsistentQueryBufferFull", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) startedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, startedEvent.ID, nil, identity) di = test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() // buffer query so that when types.QueryWorkflow is called buffer is already full ctx, release, err := s.mockHistoryEngine.executionCache.GetOrCreateWorkflowExecutionForBackground(constants.TestDomainID, workflowExecution) s.NoError(err) loadedMS, err := ctx.LoadWorkflowExecution(context.Background()) s.NoError(err) qr := query.NewRegistry() qr.BufferQuery(&types.WorkflowQuery{}) loadedMS.SetQueryRegistry(qr) release(nil) request := &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestDomainID, Request: &types.QueryWorkflowRequest{ Execution: &workflowExecution, Query: &types.WorkflowQuery{}, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }, } resp, err := s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.Nil(resp) s.Equal(workflow.ErrConsistentQueryBufferExceeded, err) } func (s *engineSuite) TestQueryWorkflow_DecisionTaskDispatch_Complete() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "TestQueryWorkflow_DecisionTaskDispatch_Complete", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) startedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, startedEvent.ID, nil, identity) di = test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() waitGroup := &sync.WaitGroup{} waitGroup.Add(1) asyncQueryUpdate := func(delay time.Duration, answer []byte) { defer waitGroup.Done() <-time.After(delay) builder := s.getBuilder(constants.TestDomainID, workflowExecution) s.NotNil(builder) qr := builder.GetQueryRegistry() buffered := qr.GetBufferedIDs() for _, id := range buffered { resultType := types.QueryResultTypeAnswered completedTerminationState := &query.TerminationState{ TerminationType: query.TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{ ResultType: &resultType, Answer: answer, }, } err := qr.SetTerminationState(id, completedTerminationState) s.NoError(err) state, err := qr.GetTerminationState(id) s.NoError(err) s.Equal(query.TerminationTypeCompleted, state.TerminationType) } } request := &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestDomainID, Request: &types.QueryWorkflowRequest{ Execution: &workflowExecution, Query: &types.WorkflowQuery{}, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }, } go asyncQueryUpdate(time.Second*2, []byte{1, 2, 3}) start := time.Now() resp, err := s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.True(time.Now().After(start.Add(time.Second))) s.NoError(err) s.Equal([]byte{1, 2, 3}, resp.GetResponse().GetQueryResult()) builder := s.getBuilder(constants.TestDomainID, workflowExecution) s.NotNil(builder) qr := builder.GetQueryRegistry() s.False(qr.HasBufferedQuery()) s.False(qr.HasCompletedQuery()) waitGroup.Wait() } func (s *engineSuite) TestQueryWorkflow_DecisionTaskDispatch_Complete_ActiveActive() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestActiveActiveDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestActiveActiveDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "TestQueryWorkflow_DecisionTaskDispatch_Complete_ActiveActive", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestActiveActiveDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, &constants.TestActiveClusterSelectionPolicy) di := test.AddDecisionTaskScheduledEvent(msBuilder) startedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, startedEvent.ID, nil, identity) di = test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() waitGroup := &sync.WaitGroup{} waitGroup.Add(1) asyncQueryUpdate := func(delay time.Duration, answer []byte) { defer waitGroup.Done() <-time.After(delay) builder := s.getBuilder(constants.TestActiveActiveDomainID, workflowExecution) s.NotNil(builder) qr := builder.GetQueryRegistry() buffered := qr.GetBufferedIDs() for _, id := range buffered { resultType := types.QueryResultTypeAnswered completedTerminationState := &query.TerminationState{ TerminationType: query.TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{ ResultType: &resultType, Answer: answer, }, } err := qr.SetTerminationState(id, completedTerminationState) s.NoError(err) state, err := qr.GetTerminationState(id) s.NoError(err) s.Equal(query.TerminationTypeCompleted, state.TerminationType) } } request := &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestActiveActiveDomainID, Request: &types.QueryWorkflowRequest{ Execution: &workflowExecution, Query: &types.WorkflowQuery{}, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }, } go asyncQueryUpdate(time.Second*2, []byte{1, 2, 3}) start := time.Now() resp, err := s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.True(time.Now().After(start.Add(time.Second))) s.NoError(err) s.Equal([]byte{1, 2, 3}, resp.GetResponse().GetQueryResult()) builder := s.getBuilder(constants.TestActiveActiveDomainID, workflowExecution) s.NotNil(builder) qr := builder.GetQueryRegistry() s.False(qr.HasBufferedQuery()) s.False(qr.HasCompletedQuery()) waitGroup.Wait() } func (s *engineSuite) TestQueryWorkflow_DecisionTaskDispatch_Unblocked() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "TestQueryWorkflow_DecisionTaskDispatch_Unblocked", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) startedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, startedEvent.ID, nil, identity) di = test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tasklist, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gweResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gweResponse, nil).Once() s.mockMatchingClient.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()).Return(&types.MatchingQueryWorkflowResponse{QueryResult: []byte{1, 2, 3}}, nil) s.mockHistoryEngine.matchingClient = s.mockMatchingClient waitGroup := &sync.WaitGroup{} waitGroup.Add(1) asyncQueryUpdate := func(delay time.Duration, answer []byte) { defer waitGroup.Done() <-time.After(delay) builder := s.getBuilder(constants.TestDomainID, workflowExecution) s.NotNil(builder) qr := builder.GetQueryRegistry() buffered := qr.GetBufferedIDs() for _, id := range buffered { s.NoError(qr.SetTerminationState(id, &query.TerminationState{TerminationType: query.TerminationTypeUnblocked})) state, err := qr.GetTerminationState(id) s.NoError(err) s.Equal(query.TerminationTypeUnblocked, state.TerminationType) } } request := &types.HistoryQueryWorkflowRequest{ DomainUUID: constants.TestDomainID, Request: &types.QueryWorkflowRequest{ Execution: &workflowExecution, Query: &types.WorkflowQuery{}, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }, } go asyncQueryUpdate(time.Second*2, []byte{1, 2, 3}) start := time.Now() resp, err := s.mockHistoryEngine.QueryWorkflow(context.Background(), request) s.True(time.Now().After(start.Add(time.Second))) s.NoError(err) s.Equal([]byte{1, 2, 3}, resp.GetResponse().GetQueryResult()) builder := s.getBuilder(constants.TestDomainID, workflowExecution) s.NotNil(builder) qr := builder.GetQueryRegistry() s.False(qr.HasBufferedQuery()) s.False(qr.HasCompletedQuery()) s.False(qr.HasUnblockedQuery()) waitGroup.Wait() } func (s *engineSuite) TestRespondDecisionTaskCompletedInvalidToken() { invalidToken, _ := json.Marshal("bad token") identity := "testIdentity" _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: invalidToken, Decisions: nil, ExecutionContext: nil, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.BadRequestError{}, err) } func (s *engineSuite) TestRespondDecisionTaskCompletedIfNoExecution() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockHistoryEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).Times(1) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: constants.TestRunID, ScheduleID: 2, }) identity := "testIdentity" s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondDecisionTaskCompletedIfGetExecutionFailed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockHistoryEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: constants.TestRunID, ScheduleID: 2, }) identity := "testIdentity" s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(nil, errors.New("FAILED")).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.EqualError(err, "FAILED") } func (s *engineSuite) TestRespondDecisionTaskCompletedUpdateExecutionFailed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, errors.New("FAILED")).Once() s.mockShardManager.On("UpdateShard", mock.Anything, mock.Anything).Return(nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.NotNil(err) s.EqualError(err, "FAILED") } func (s *engineSuite) TestRespondDecisionTaskCompletedIfTaskCompleted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) startedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, startedEvent.ID, nil, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondDecisionTaskCompletedIfTaskNotStarted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondDecisionTaskCompletedConflictOnUpdate() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" executionContext := []byte("context") activity1ID := "activity1" activity1Type := "activity_type1" activity1Input := []byte("input1") activity1Result := []byte("activity1_result") activity2ID := "activity2" activity2Type := "activity_type2" activity2Input := []byte("input2") activity2Result := []byte("activity2_result") activity3ID := "activity3" activity3Type := "activity_type3" activity3Input := []byte("input3") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di1 := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent1 := test.AddDecisionTaskStartedEvent(msBuilder, di1.ScheduleID, tl, identity) decisionCompletedEvent1 := test.AddDecisionTaskCompletedEvent(msBuilder, di1.ScheduleID, decisionStartedEvent1.ID, nil, identity) activity1ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity1ID, activity1Type, tl, activity1Input, 100, 10, 1, 5) activity2ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity2ID, activity2Type, tl, activity2Input, 100, 10, 1, 5) activity1StartedEvent := test.AddActivityTaskStartedEvent(msBuilder, activity1ScheduledEvent.ID, identity) activity2StartedEvent := test.AddActivityTaskStartedEvent(msBuilder, activity2ScheduledEvent.ID, identity) test.AddActivityTaskCompletedEvent(msBuilder, activity1ScheduledEvent.ID, activity1StartedEvent.ID, activity1Result, identity) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent2 := test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: we.GetRunID(), ScheduleID: di2.ScheduleID, }) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: activity3ID, ActivityType: &types.ActivityType{Name: activity3Type}, TaskList: &types.TaskList{Name: tl}, Input: activity3Input, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} test.AddActivityTaskCompletedEvent(msBuilder, activity2ScheduledEvent.ID, activity2StartedEvent.ID, activity2Result, identity) ms2 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse2 := &persistence.GetWorkflowExecutionResponse{State: ms2} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, &persistence.ConditionFailedError{}).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse2, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) s.Equal(int64(16), ms2.ExecutionInfo.NextEventID) s.Equal(decisionStartedEvent2.ID, ms2.ExecutionInfo.LastProcessedEvent) s.Equal(executionContext, ms2.ExecutionInfo.ExecutionContext) executionBuilder := s.getBuilder(constants.TestDomainID, we) activity3Attributes := s.getActivityScheduledEvent(executionBuilder, 13).ActivityTaskScheduledEventAttributes s.Equal(activity3ID, activity3Attributes.ActivityID) s.Equal(activity3Type, activity3Attributes.ActivityType.Name) s.Equal(int64(12), activity3Attributes.DecisionTaskCompletedEventID) s.Equal(tl, activity3Attributes.TaskList.Name) s.Equal(activity3Input, activity3Attributes.Input) s.Equal(int32(100), *activity3Attributes.ScheduleToCloseTimeoutSeconds) s.Equal(int32(10), *activity3Attributes.ScheduleToStartTimeoutSeconds) s.Equal(int32(50), *activity3Attributes.StartToCloseTimeoutSeconds) s.Equal(int32(5), *activity3Attributes.HeartbeatTimeoutSeconds) di, ok := executionBuilder.GetDecisionInfo(15) s.True(ok) s.Equal(int32(100), di.DecisionTimeout) } func (s *engineSuite) TestValidateSignalRequest() { workflowType := "testType" input := []byte("input") startRequest := &types.StartWorkflowExecutionRequest{ WorkflowID: "ID", WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: "taskptr"}, Input: input, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(10), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "identity", } err := s.mockHistoryEngine.validateStartWorkflowExecutionRequest(startRequest, 0) s.Error(err, "startRequest doesn't have request id, it should error out") } func (s *engineSuite) TestRespondDecisionTaskCompletedMaxAttemptsExceeded() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") input := []byte("input") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "activity1", ActivityType: &types.ActivityType{Name: "activity_type1"}, TaskList: &types.TaskList{Name: tl}, Input: input, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }} for i := 0; i < workflow.ConditionalRetryCount; i++ { ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, &persistence.ConditionFailedError{}).Once() } _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.NotNil(err) s.Equal(workflow.ErrMaxAttemptsExceeded, err) } func (s *engineSuite) TestRespondDecisionTaskCompletedCompleteWorkflowFailed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" executionContext := []byte("context") activity1ID := "activity1" activity1Type := "activity_type1" activity1Input := []byte("input1") activity1Result := []byte("activity1_result") activity2ID := "activity2" activity2Type := "activity_type2" activity2Input := []byte("input2") activity2Result := []byte("activity2_result") workflowResult := []byte("workflow result") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 25, 200, identity, nil) di1 := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent1 := test.AddDecisionTaskStartedEvent(msBuilder, di1.ScheduleID, tl, identity) decisionCompletedEvent1 := test.AddDecisionTaskCompletedEvent(msBuilder, di1.ScheduleID, decisionStartedEvent1.ID, nil, identity) activity1ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity1ID, activity1Type, tl, activity1Input, 100, 10, 1, 5) activity2ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity2ID, activity2Type, tl, activity2Input, 100, 10, 1, 5) activity1StartedEvent := test.AddActivityTaskStartedEvent(msBuilder, activity1ScheduledEvent.ID, identity) activity2StartedEvent := test.AddActivityTaskStartedEvent(msBuilder, activity2ScheduledEvent.ID, identity) test.AddActivityTaskCompletedEvent(msBuilder, activity1ScheduledEvent.ID, activity1StartedEvent.ID, activity1Result, identity) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) test.AddActivityTaskCompletedEvent(msBuilder, activity2ScheduledEvent.ID, activity2StartedEvent.ID, activity2Result, identity) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: di2.ScheduleID, }) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: workflowResult, }, }} for i := 0; i < 2; i++ { ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() } s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(15), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(decisionStartedEvent1.ID, executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Empty(executionBuilder.GetExecutionInfo().ExecutionContext) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di3, ok := executionBuilder.GetDecisionInfo(executionBuilder.GetExecutionInfo().NextEventID - 1) s.True(ok) s.Equal(executionBuilder.GetExecutionInfo().NextEventID-1, di3.ScheduleID) s.Equal(int64(0), di3.Attempt) } func (s *engineSuite) TestRespondDecisionTaskCompletedFailWorkflowFailed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" executionContext := []byte("context") activity1ID := "activity1" activity1Type := "activity_type1" activity1Input := []byte("input1") activity1Result := []byte("activity1_result") activity2ID := "activity2" activity2Type := "activity_type2" activity2Input := []byte("input2") activity2Result := []byte("activity2_result") reason := "workflow fail reason" details := []byte("workflow fail details") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 25, 200, identity, nil) di1 := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent1 := test.AddDecisionTaskStartedEvent(msBuilder, di1.ScheduleID, tl, identity) decisionCompletedEvent1 := test.AddDecisionTaskCompletedEvent(msBuilder, di1.ScheduleID, decisionStartedEvent1.ID, nil, identity) activity1ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity1ID, activity1Type, tl, activity1Input, 100, 10, 1, 5) activity2ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity2ID, activity2Type, tl, activity2Input, 100, 10, 1, 5) activity1StartedEvent := test.AddActivityTaskStartedEvent(msBuilder, activity1ScheduledEvent.ID, identity) activity2StartedEvent := test.AddActivityTaskStartedEvent(msBuilder, activity2ScheduledEvent.ID, identity) test.AddActivityTaskCompletedEvent(msBuilder, activity1ScheduledEvent.ID, activity1StartedEvent.ID, activity1Result, identity) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) test.AddActivityTaskCompletedEvent(msBuilder, activity2ScheduledEvent.ID, activity2StartedEvent.ID, activity2Result, identity) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: di2.ScheduleID, }) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeFailWorkflowExecution.Ptr(), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ Reason: &reason, Details: details, }, }} for i := 0; i < 2; i++ { ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() } s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(15), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(decisionStartedEvent1.ID, executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Empty(executionBuilder.GetExecutionInfo().ExecutionContext) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di3, ok := executionBuilder.GetDecisionInfo(executionBuilder.GetExecutionInfo().NextEventID - 1) s.True(ok) s.Equal(executionBuilder.GetExecutionInfo().NextEventID-1, di3.ScheduleID) s.Equal(int64(0), di3.Attempt) } func (s *engineSuite) TestRespondDecisionTaskCompletedBadDecisionAttributes() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" executionContext := []byte("context") activity1ID := "activity1" activity1Type := "activity_type1" activity1Input := []byte("input1") activity1Result := []byte("activity1_result") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 25, 200, identity, nil) di1 := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent1 := test.AddDecisionTaskStartedEvent(msBuilder, di1.ScheduleID, tl, identity) decisionCompletedEvent1 := test.AddDecisionTaskCompletedEvent(msBuilder, di1.ScheduleID, decisionStartedEvent1.ID, nil, identity) activity1ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity1ID, activity1Type, tl, activity1Input, 100, 10, 1, 5) activity1StartedEvent := test.AddActivityTaskStartedEvent(msBuilder, activity1ScheduledEvent.ID, identity) test.AddActivityTaskCompletedEvent(msBuilder, activity1ScheduledEvent.ID, activity1StartedEvent.ID, activity1Result, identity) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: di2.ScheduleID, }) // Decision with nil attributes decisions := []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), }} gwmsResponse1 := &persistence.GetWorkflowExecutionResponse{State: execution.CreatePersistenceMutableState(s.T(), msBuilder)} gwmsResponse2 := &persistence.GetWorkflowExecutionResponse{State: execution.CreatePersistenceMutableState(s.T(), msBuilder)} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse2, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil, ).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err) } // This test unit tests the activity schedule timeout validation logic of HistoryEngine's RespondDecisionTaskComplete function. // An scheduled activity decision has 3 timeouts: ScheduleToClose, ScheduleToStart and StartToClose. // This test verifies that when either ScheduleToClose or ScheduleToStart and StartToClose are specified, // HistoryEngine's validateActivityScheduleAttribute will deduce the missing timeout and fill it in // instead of returning a BadRequest error and only when all three are missing should a BadRequest be returned. func (s *engineSuite) TestRespondDecisionTaskCompletedSingleActivityScheduledAttribute() { workflowTimeout := int32(100) testIterationVariables := []struct { scheduleToClose *int32 scheduleToStart *int32 startToClose *int32 heartbeat *int32 expectedScheduleToClose int32 expectedScheduleToStart int32 expectedStartToClose int32 expectDecisionFail bool }{ // No ScheduleToClose timeout, will use ScheduleToStart + StartToClose {nil, common.Int32Ptr(3), common.Int32Ptr(7), nil, 3 + 7, 3, 7, false}, // Has ScheduleToClose timeout but not ScheduleToStart or StartToClose, // will use ScheduleToClose for ScheduleToStart and StartToClose {common.Int32Ptr(7), nil, nil, nil, 7, 7, 7, false}, // No ScheduleToClose timeout, ScheduleToStart or StartToClose, expect error return {nil, nil, nil, nil, 0, 0, 0, true}, // Negative ScheduleToClose, expect error return {common.Int32Ptr(-1), nil, nil, nil, 0, 0, 0, true}, // Negative ScheduleToStart, expect error return {nil, common.Int32Ptr(-1), nil, nil, 0, 0, 0, true}, // Negative StartToClose, expect error return {nil, nil, common.Int32Ptr(-1), nil, 0, 0, 0, true}, // Negative HeartBeat, expect error return {nil, nil, nil, common.Int32Ptr(-1), 0, 0, 0, true}, // Use workflow timeout {common.Int32Ptr(workflowTimeout), nil, nil, nil, workflowTimeout, workflowTimeout, workflowTimeout, false}, // Timeout larger than workflow timeout {common.Int32Ptr(workflowTimeout + 1), nil, nil, nil, workflowTimeout, workflowTimeout, workflowTimeout, false}, {nil, common.Int32Ptr(workflowTimeout + 1), nil, nil, 0, 0, 0, true}, {nil, nil, common.Int32Ptr(workflowTimeout + 1), nil, 0, 0, 0, true}, {nil, nil, nil, common.Int32Ptr(workflowTimeout + 1), 0, 0, 0, true}, // No ScheduleToClose timeout, will use ScheduleToStart + StartToClose, but exceed limit {nil, common.Int32Ptr(workflowTimeout), common.Int32Ptr(10), nil, workflowTimeout, workflowTimeout, 10, false}, } for _, iVar := range testIterationVariables { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: we.GetRunID(), ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") input := []byte("input") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), workflowTimeout, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "activity1", ActivityType: &types.ActivityType{Name: "activity_type1"}, TaskList: &types.TaskList{Name: tl}, Input: input, ScheduleToCloseTimeoutSeconds: iVar.scheduleToClose, ScheduleToStartTimeoutSeconds: iVar.scheduleToStart, StartToCloseTimeoutSeconds: iVar.startToClose, HeartbeatTimeoutSeconds: iVar.heartbeat, }, }} gwmsResponse1 := &persistence.GetWorkflowExecutionResponse{State: execution.CreatePersistenceMutableState(s.T(), msBuilder)} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() if iVar.expectDecisionFail { gwmsResponse2 := &persistence.GetWorkflowExecutionResponse{State: execution.CreatePersistenceMutableState(s.T(), msBuilder)} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse2, nil).Once() } s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) if !iVar.expectDecisionFail { s.Equal(int64(6), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) activity1Attributes := s.getActivityScheduledEvent(executionBuilder, int64(5)).ActivityTaskScheduledEventAttributes s.Equal(iVar.expectedScheduleToClose, activity1Attributes.GetScheduleToCloseTimeoutSeconds()) s.Equal(iVar.expectedScheduleToStart, activity1Attributes.GetScheduleToStartTimeoutSeconds()) s.Equal(iVar.expectedStartToClose, activity1Attributes.GetStartToCloseTimeoutSeconds()) } else { s.Equal(int64(5), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(commonconstants.EmptyEventID, executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) } s.TearDownTest() s.SetupTest() } } func (s *engineSuite) TestRespondDecisionTaskCompletedBadBinary() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() domainID := uuid.New() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: we.GetRunID(), ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: constants.TestDomainName}, &persistence.DomainConfig{ Retention: 2, BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "test-bad-binary": {}, }, }, }, cluster.TestCurrentClusterName, ) msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), domainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) var decisions []*types.Decision gwmsResponse1 := &persistence.GetWorkflowExecutionResponse{State: execution.CreatePersistenceMutableState(s.T(), msBuilder)} gwmsResponse2 := &persistence.GetWorkflowExecutionResponse{State: execution.CreatePersistenceMutableState(s.T(), msBuilder)} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse2, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(constants.TestDomainName, nil).AnyTimes() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: domainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, BinaryChecksum: "test-bad-binary", }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(domainID, we) s.Equal(int64(5), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(commonconstants.EmptyEventID, executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Empty(executionBuilder.GetExecutionInfo().ExecutionContext) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestRespondDecisionTaskCompletedSingleActivityScheduledDecision() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: we.GetRunID(), ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") input := []byte("input") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), ScheduleActivityTaskDecisionAttributes: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "activity1", ActivityType: &types.ActivityType{Name: "activity_type1"}, TaskList: &types.TaskList{Name: tl}, Input: input, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(10), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(5), }, }} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(6), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(executionContext, executionBuilder.GetExecutionInfo().ExecutionContext) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) activity1Attributes := s.getActivityScheduledEvent(executionBuilder, int64(5)).ActivityTaskScheduledEventAttributes s.Equal("activity1", activity1Attributes.ActivityID) s.Equal("activity_type1", activity1Attributes.ActivityType.Name) s.Equal(int64(4), activity1Attributes.DecisionTaskCompletedEventID) s.Equal(tl, activity1Attributes.TaskList.Name) s.Equal(input, activity1Attributes.Input) s.Equal(int32(100), *activity1Attributes.ScheduleToCloseTimeoutSeconds) s.Equal(int32(10), *activity1Attributes.ScheduleToStartTimeoutSeconds) s.Equal(int32(50), *activity1Attributes.StartToCloseTimeoutSeconds) s.Equal(int32(5), *activity1Attributes.HeartbeatTimeoutSeconds) } func (s *engineSuite) TestRespondDecisionTaskCompleted_DecisionHeartbeatTimeout() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) msBuilder.GetExecutionInfo().DecisionOriginalScheduledTimestamp = time.Now().Add(-time.Hour).UnixNano() decisions := []*types.Decision{} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ ForceCreateNewDecisionTask: true, TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Error(err, "decision heartbeat timeout") } func (s *engineSuite) TestRespondDecisionTaskCompleted_DecisionHeartbeatNotTimeout() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) msBuilder.GetExecutionInfo().DecisionOriginalScheduledTimestamp = time.Now().Add(-time.Minute).UnixNano() decisions := []*types.Decision{} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ ForceCreateNewDecisionTask: true, TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err) } func (s *engineSuite) TestRespondDecisionTaskCompleted_DecisionHeartbeatNotTimeout_ZeroOrignalScheduledTime() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) msBuilder.GetExecutionInfo().DecisionOriginalScheduledTimestamp = 0 decisions := []*types.Decision{} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ ForceCreateNewDecisionTask: true, TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err) } func (s *engineSuite) TestRespondDecisionTaskCompletedCompleteWorkflowSuccess() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") workflowResult := []byte("success") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: workflowResult, }, }} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(6), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(executionContext, executionBuilder.GetExecutionInfo().ExecutionContext) s.Equal(persistence.WorkflowStateCompleted, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestRespondDecisionTaskCompletedFailWorkflowSuccess() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") details := []byte("fail workflow details") reason := "fail workflow reason" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeFailWorkflowExecution.Ptr(), FailWorkflowExecutionDecisionAttributes: &types.FailWorkflowExecutionDecisionAttributes{ Reason: &reason, Details: details, }, }} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(6), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(executionContext, executionBuilder.GetExecutionInfo().ExecutionContext) s.Equal(persistence.WorkflowStateCompleted, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestRespondDecisionTaskCompletedSignalExternalWorkflowSuccess() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeSignalExternalWorkflowExecution.Ptr(), SignalExternalWorkflowExecutionDecisionAttributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, Execution: &types.WorkflowExecution{ WorkflowID: we.WorkflowID, RunID: we.RunID, }, SignalName: "signal", Input: []byte("test input"), }, }} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(6), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(executionContext, executionBuilder.GetExecutionInfo().ExecutionContext) } func (s *engineSuite) TestRespondDecisionTaskCompletedStartChildWorkflowWithAbandonPolicy() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) abandon := types.ParentClosePolicyAbandon decisions := []*types.Decision{{ DecisionType: types.DecisionTypeStartChildWorkflowExecution.Ptr(), StartChildWorkflowExecutionDecisionAttributes: &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: "child-workflow-id", WorkflowType: &types.WorkflowType{ Name: "child-workflow-type", }, ParentClosePolicy: &abandon, }, }} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(6), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(executionContext, executionBuilder.GetExecutionInfo().ExecutionContext) s.Equal(int(1), len(executionBuilder.GetPendingChildExecutionInfos())) var childID int64 for c := range executionBuilder.GetPendingChildExecutionInfos() { childID = c break } s.Equal("child-workflow-id", executionBuilder.GetPendingChildExecutionInfos()[childID].StartedWorkflowID) s.Equal(types.ParentClosePolicyAbandon, executionBuilder.GetPendingChildExecutionInfos()[childID].ParentClosePolicy) } func (s *engineSuite) TestRespondDecisionTaskCompletedStartChildWorkflowWithTerminatePolicy() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) terminate := types.ParentClosePolicyTerminate decisions := []*types.Decision{{ DecisionType: types.DecisionTypeStartChildWorkflowExecution.Ptr(), StartChildWorkflowExecutionDecisionAttributes: &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: "child-workflow-id", WorkflowType: &types.WorkflowType{ Name: "child-workflow-type", }, ParentClosePolicy: &terminate, }, }} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(6), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(executionContext, executionBuilder.GetExecutionInfo().ExecutionContext) s.Equal(int(1), len(executionBuilder.GetPendingChildExecutionInfos())) var childID int64 for c := range executionBuilder.GetPendingChildExecutionInfos() { childID = c break } s.Equal("child-workflow-id", executionBuilder.GetPendingChildExecutionInfos()[childID].StartedWorkflowID) s.Equal(types.ParentClosePolicyTerminate, executionBuilder.GetPendingChildExecutionInfos()[childID].ParentClosePolicy) } func (s *engineSuite) TestRespondDecisionTaskCompletedSignalExternalWorkflowFailed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: "invalid run id", } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.GetWorkflowID(), RunID: we.GetRunID(), ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeSignalExternalWorkflowExecution.Ptr(), SignalExternalWorkflowExecutionDecisionAttributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainID, Execution: &types.WorkflowExecution{ WorkflowID: we.WorkflowID, RunID: we.RunID, }, SignalName: "signal", Input: []byte("test input"), }, }} _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.EqualError(err, "RunID is not valid UUID.") } func (s *engineSuite) TestRespondDecisionTaskCompletedSignalExternalWorkflowFailed_UnKnownDomain() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" executionContext := []byte("context") foreignDomain := "unknown domain" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeSignalExternalWorkflowExecution.Ptr(), SignalExternalWorkflowExecutionDecisionAttributes: &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: foreignDomain, Execution: &types.WorkflowExecution{ WorkflowID: we.WorkflowID, RunID: we.RunID, }, SignalName: "signal", Input: []byte("test input"), }, }} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockDomainCache.EXPECT().GetDomain(foreignDomain).Return( nil, errors.New("get foreign domain error"), ).Times(1) _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: executionContext, Identity: identity, }, }) s.NotNil(err) } func (s *engineSuite) TestRespondActivityTaskCompletedInvalidToken() { invalidToken, _ := json.Marshal("bad token") identity := "testIdentity" err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: invalidToken, Result: nil, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.BadRequestError{}, err) } func (s *engineSuite) TestRespondActivityTaskCompletedIfNoExecution() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockHistoryEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: constants.TestRunID, ScheduleID: 2, }) identity := "testIdentity" s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondActivityTaskCompletedIfNoRunID() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockHistoryEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", ScheduleID: 2, }) identity := "testIdentity" s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondActivityTaskCompletedIfGetExecutionFailed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockHistoryEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: constants.TestRunID, ScheduleID: 2, }) identity := "testIdentity" s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(nil, errors.New("FAILED")).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.EqualError(err, "FAILED") } func (s *engineSuite) TestRespondActivityTaskCompletedIfNoAIdProvided() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", ScheduleID: commonconstants.EmptyEventID, }) msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), constants.TestRunID, constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: constants.TestRunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.EqualError(err, "Neither ActivityID nor ScheduleID is provided") } func (s *engineSuite) TestRespondActivityTaskCompletedIfNotFound() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", ScheduleID: commonconstants.EmptyEventID, ActivityID: "aid", }) workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), constants.TestRunID, constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: constants.TestRunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.Error(err) } func (s *engineSuite) TestRespondActivityTaskCompletedUpdateExecutionFailed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") activityResult := []byte("activity result") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, errors.New("FAILED")).Once() s.mockShardManager.On("UpdateShard", mock.Anything, mock.Anything).Return(nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Result: activityResult, Identity: identity, }, }) s.EqualError(err, "FAILED") } func (s *engineSuite) TestRespondActivityTaskCompletedIfTaskCompleted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") activityResult := []byte("activity result") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) activityStartedEvent := test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) test.AddActivityTaskCompletedEvent(msBuilder, activityScheduledEvent.ID, activityStartedEvent.ID, activityResult, identity) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Result: activityResult, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondActivityTaskCompletedIfTaskNotStarted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") activityResult := []byte("activity result") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 200, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Result: activityResult, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondActivityTaskCompletedConflictOnUpdate() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activity1ID := "activity1" activity1Type := "activity_type1" activity1Input := []byte("input1") activity1Result := []byte("activity1_result") activity2ID := "activity2" activity2Type := "activity_type2" activity2Input := []byte("input2") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent1 := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent1 := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent1.ID, nil, identity) activity1ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity1ID, activity1Type, tl, activity1Input, 100, 10, 1, 5) activity2ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity2ID, activity2Type, tl, activity2Input, 100, 10, 1, 5) test.AddActivityTaskStartedEvent(msBuilder, activity1ScheduledEvent.ID, identity) test.AddActivityTaskStartedEvent(msBuilder, activity2ScheduledEvent.ID, identity) ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &persistence.GetWorkflowExecutionResponse{State: ms1} ms2 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse2 := &persistence.GetWorkflowExecutionResponse{State: ms2} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, &persistence.ConditionFailedError{}).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse2, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Result: activity1Result, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(11), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di, ok := executionBuilder.GetDecisionInfo(int64(10)) s.True(ok) s.Equal(int32(100), di.DecisionTimeout) s.Equal(int64(10), di.ScheduleID) s.Equal(commonconstants.EmptyEventID, di.StartedID) } func (s *engineSuite) TestRespondActivityTaskCompletedMaxAttemptsExceeded() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") activityResult := []byte("activity result") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) for i := 0; i < workflow.ConditionalRetryCount; i++ { ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, &persistence.ConditionFailedError{}).Once() } err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Result: activityResult, Identity: identity, }, }) s.Equal(workflow.ErrMaxAttemptsExceeded, err) } func (s *engineSuite) TestRespondActivityTaskCompletedSuccess() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") activityResult := []byte("activity result") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Result: activityResult, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(9), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di, ok := executionBuilder.GetDecisionInfo(int64(8)) s.True(ok) s.Equal(int32(100), di.DecisionTimeout) s.Equal(int64(8), di.ScheduleID) s.Equal(commonconstants.EmptyEventID, di.StartedID) } func (s *engineSuite) TestRespondActivityTaskCompletedByIdSuccess() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") activityResult := []byte("activity result") taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, ScheduleID: commonconstants.EmptyEventID, ActivityID: activityID, }) msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) decisionScheduledEvent := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, decisionScheduledEvent.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, decisionScheduledEvent.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: we.RunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCompleted(context.Background(), &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: taskToken, Result: activityResult, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(9), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di, ok := executionBuilder.GetDecisionInfo(int64(8)) s.True(ok) s.Equal(int32(100), di.DecisionTimeout) s.Equal(int64(8), di.ScheduleID) s.Equal(commonconstants.EmptyEventID, di.StartedID) } func (s *engineSuite) TestRespondActivityTaskFailedInvalidToken() { invalidToken, _ := json.Marshal("bad token") identity := "testIdentity" err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: invalidToken, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.BadRequestError{}, err) } func (s *engineSuite) TestRespondActivityTaskFailedIfNoExecution() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockHistoryEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: constants.TestRunID, ScheduleID: 2, }) identity := "testIdentity" s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondActivityTaskFailedIfNoRunID() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockHistoryEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", ScheduleID: 2, }) identity := "testIdentity" s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondActivityTaskFailedIfGetExecutionFailed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockHistoryEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: constants.TestRunID, ScheduleID: 2, }) identity := "testIdentity" s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(nil, errors.New("FAILED")).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.EqualError(err, "FAILED") } func (s *engineSuite) TestRespondActivityTaskFailededIfNoAIdProvided() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", ScheduleID: commonconstants.EmptyEventID, }) workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), constants.TestRunID, constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: constants.TestRunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.EqualError(err, "Neither ActivityID nor ScheduleID is provided") } func (s *engineSuite) TestRespondActivityTaskFailededIfNotFound() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", ScheduleID: commonconstants.EmptyEventID, ActivityID: "aid", }) workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), constants.TestRunID, constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: constants.TestRunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.Error(err) } func (s *engineSuite) TestRespondActivityTaskFailedUpdateExecutionFailed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, errors.New("FAILED")).Once() s.mockShardManager.On("UpdateShard", mock.Anything, mock.Anything).Return(nil).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.EqualError(err, "FAILED") } func (s *engineSuite) TestRespondActivityTaskFailedIfTaskCompleted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") failReason := "fail reason" details := []byte("fail details") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) activityStartedEvent := test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) test.AddActivityTaskFailedEvent(msBuilder, activityScheduledEvent.ID, activityStartedEvent.ID, failReason, details, identity) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Reason: &failReason, Details: details, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondActivityTaskFailedIfTaskNotStarted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondActivityTaskFailedConflictOnUpdate() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activity1ID := "activity1" activity1Type := "activity_type1" activity1Input := []byte("input1") failReason := "fail reason" details := []byte("fail details.") activity2ID := "activity2" activity2Type := "activity_type2" activity2Input := []byte("input2") activity2Result := []byte("activity2_result") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 25, 25, identity, nil) di1 := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent1 := test.AddDecisionTaskStartedEvent(msBuilder, di1.ScheduleID, tl, identity) decisionCompletedEvent1 := test.AddDecisionTaskCompletedEvent(msBuilder, di1.ScheduleID, decisionStartedEvent1.ID, nil, identity) activity1ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity1ID, activity1Type, tl, activity1Input, 100, 10, 1, 5) activity2ScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent1.ID, activity2ID, activity2Type, tl, activity2Input, 100, 10, 1, 5) test.AddActivityTaskStartedEvent(msBuilder, activity1ScheduledEvent.ID, identity) activity2StartedEvent := test.AddActivityTaskStartedEvent(msBuilder, activity2ScheduledEvent.ID, identity) ms1 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse1 := &persistence.GetWorkflowExecutionResponse{State: ms1} test.AddActivityTaskCompletedEvent(msBuilder, activity2ScheduledEvent.ID, activity2StartedEvent.ID, activity2Result, identity) test.AddDecisionTaskScheduledEvent(msBuilder) ms2 := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse2 := &persistence.GetWorkflowExecutionResponse{State: ms2} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse1, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, &persistence.ConditionFailedError{}).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse2, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Reason: &failReason, Details: details, Identity: identity, }, }) s.Nil(err, s.printHistory(msBuilder)) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(12), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di, ok := executionBuilder.GetDecisionInfo(int64(10)) s.True(ok) s.Equal(int32(25), di.DecisionTimeout) s.Equal(int64(10), di.ScheduleID) s.Equal(commonconstants.EmptyEventID, di.StartedID) } func (s *engineSuite) TestRespondActivityTaskFailedMaxAttemptsExceeded() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) for i := 0; i < workflow.ConditionalRetryCount; i++ { ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, &persistence.ConditionFailedError{}).Once() } err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Identity: identity, }, }) s.Equal(workflow.ErrMaxAttemptsExceeded, err) } func (s *engineSuite) TestRespondActivityTaskFailedSuccess() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") failReason := "failed" failDetails := []byte("fail details.") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Reason: &failReason, Details: failDetails, Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(9), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di, ok := executionBuilder.GetDecisionInfo(int64(8)) s.True(ok) s.Equal(int32(100), di.DecisionTimeout) s.Equal(int64(8), di.ScheduleID) s.Equal(commonconstants.EmptyEventID, di.StartedID) } func (s *engineSuite) TestRespondActivityTaskFailedByIDSuccess() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") failReason := "failed" failDetails := []byte("fail details.") taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, ScheduleID: commonconstants.EmptyEventID, ActivityID: activityID, }) msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) decisionScheduledEvent := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, decisionScheduledEvent.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, decisionScheduledEvent.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 5) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: we.RunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskFailed(context.Background(), &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: constants.TestDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: taskToken, Reason: &failReason, Details: failDetails, Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(9), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di, ok := executionBuilder.GetDecisionInfo(int64(8)) s.True(ok) s.Equal(int32(100), di.DecisionTimeout) s.Equal(int64(8), di.ScheduleID) s.Equal(commonconstants.EmptyEventID, di.StartedID) } func (s *engineSuite) TestRecordActivityTaskHeartBeatSuccess_NoTimer() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 0) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) // No HeartBeat timer running. ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() detais := []byte("details") _, err := s.mockHistoryEngine.RecordActivityTaskHeartbeat(context.Background(), &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: constants.TestDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: taskToken, Identity: identity, Details: detais, }, }) s.Nil(err) } func (s *engineSuite) TestRecordActivityTaskHeartBeatSuccess_TimerRunning() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 1) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} // HeartBeat timer running. s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() detais := []byte("details") _, err := s.mockHistoryEngine.RecordActivityTaskHeartbeat(context.Background(), &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: constants.TestDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: taskToken, Identity: identity, Details: detais, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(7), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestRecordActivityTaskHeartBeatByIDSuccess() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: commonconstants.EmptyEventID, ActivityID: activityID, }) msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 0) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) // No HeartBeat timer running. ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() detais := []byte("details") _, err := s.mockHistoryEngine.RecordActivityTaskHeartbeat(context.Background(), &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: constants.TestDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: taskToken, Identity: identity, Details: detais, }, }) s.Nil(err) } func (s *engineSuite) TestRespondActivityTaskCanceled_Scheduled() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 1) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCanceled(context.Background(), &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: constants.TestDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: taskToken, Identity: identity, Details: []byte("details"), }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondActivityTaskCanceled_Started() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 5, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 1) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) _, _, err := msBuilder.AddActivityTaskCancelRequestedEvent(decisionCompletedEvent.ID, activityID, identity) s.Nil(err) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err = s.mockHistoryEngine.RespondActivityTaskCanceled(context.Background(), &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: constants.TestDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: taskToken, Identity: identity, Details: []byte("details"), }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(10), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di, ok := executionBuilder.GetDecisionInfo(int64(9)) s.True(ok) s.Equal(int32(100), di.DecisionTimeout) s.Equal(int64(9), di.ScheduleID) s.Equal(commonconstants.EmptyEventID, di.StartedID) } func (s *engineSuite) TestRespondActivityTaskCanceledByID_Started() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, ScheduleID: commonconstants.EmptyEventID, ActivityID: activityID, }) msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) decisionScheduledEvent := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, decisionScheduledEvent.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, decisionScheduledEvent.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 1) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) _, _, err := msBuilder.AddActivityTaskCancelRequestedEvent(decisionCompletedEvent.ID, activityID, identity) s.Nil(err) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: we.RunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err = s.mockHistoryEngine.RespondActivityTaskCanceled(context.Background(), &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: constants.TestDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: taskToken, Identity: identity, Details: []byte("details"), }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(10), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di, ok := executionBuilder.GetDecisionInfo(int64(9)) s.True(ok) s.Equal(int32(100), di.DecisionTimeout) s.Equal(int64(9), di.ScheduleID) s.Equal(commonconstants.EmptyEventID, di.StartedID) } func (s *engineSuite) TestRespondActivityTaskCanceledIfNoRunID() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockHistoryEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil) taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", ScheduleID: 2, }) identity := "testIdentity" s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() err := s.mockHistoryEngine.RespondActivityTaskCanceled(context.Background(), &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: constants.TestDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: taskToken, Identity: identity, }, }) s.NotNil(err) s.IsType(&types.EntityNotExistsError{}, err) } func (s *engineSuite) TestRespondActivityTaskCanceledIfNoAIdProvided() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", ScheduleID: commonconstants.EmptyEventID, }) identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), constants.TestRunID, constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: constants.TestRunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCanceled(context.Background(), &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: constants.TestDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: taskToken, Identity: identity, }, }) s.EqualError(err, "Neither ActivityID nor ScheduleID is provided") } func (s *engineSuite) TestRespondActivityTaskCanceledIfNotFound() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", ScheduleID: commonconstants.EmptyEventID, ActivityID: "aid", }) identity := "testIdentity" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), constants.TestRunID, constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: constants.TestRunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.RespondActivityTaskCanceled(context.Background(), &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: constants.TestDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: taskToken, Identity: identity, }, }) s.Error(err) } func (s *engineSuite) TestRequestCancel_RespondDecisionTaskCompleted_NotScheduled() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" activityID := "activity1_id" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: activityID, }, }} ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(7), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestRequestCancel_RespondDecisionTaskCompleted_Scheduled() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 6, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 1) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} decisions := []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: activityID, }, }} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(12), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(7), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di2, ok := executionBuilder.GetDecisionInfo(executionBuilder.GetExecutionInfo().NextEventID - 1) s.True(ok) s.Equal(executionBuilder.GetExecutionInfo().NextEventID-1, di2.ScheduleID) s.Equal(int64(0), di2.Attempt) } func (s *engineSuite) TestRequestCancel_RespondDecisionTaskCompleted_Started() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 7, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 0) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} decisions := []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: activityID, }, }} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(11), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(8), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestRequestCancel_RespondDecisionTaskCompleted_Completed() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 6, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") workflowResult := []byte("workflow result") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 0) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) decisions := []*types.Decision{ { DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: activityID, }, }, { DecisionType: types.DecisionTypeCompleteWorkflowExecution.Ptr(), CompleteWorkflowExecutionDecisionAttributes: &types.CompleteWorkflowExecutionDecisionAttributes{ Result: workflowResult, }, }, } ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(11), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(7), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateCompleted, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestRequestCancel_RespondDecisionTaskCompleted_NoHeartBeat() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 7, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 0) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} decisions := []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: activityID, }, }} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(11), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(8), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) // Try recording activity heartbeat s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() activityTaskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: we.GetRunID(), ScheduleID: 5, }) hbResponse, err := s.mockHistoryEngine.RecordActivityTaskHeartbeat(context.Background(), &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: constants.TestDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: activityTaskToken, Identity: identity, Details: []byte("details"), }, }) s.Nil(err) s.NotNil(hbResponse) s.True(hbResponse.CancelRequested) // Try cancelling the request. s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err = s.mockHistoryEngine.RespondActivityTaskCanceled(context.Background(), &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: constants.TestDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: activityTaskToken, Identity: identity, Details: []byte("details"), }, }) s.Nil(err) executionBuilder = s.getBuilder(constants.TestDomainID, we) s.Equal(int64(13), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(8), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestRequestCancel_RespondDecisionTaskCompleted_Success() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 7, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 1) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} decisions := []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: activityID, }, }} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(11), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(8), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) // Try recording activity heartbeat s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() activityTaskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: we.GetRunID(), ScheduleID: 5, }) hbResponse, err := s.mockHistoryEngine.RecordActivityTaskHeartbeat(context.Background(), &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: constants.TestDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: activityTaskToken, Identity: identity, Details: []byte("details"), }, }) s.Nil(err) s.NotNil(hbResponse) s.True(hbResponse.CancelRequested) // Try cancelling the request. s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err = s.mockHistoryEngine.RespondActivityTaskCanceled(context.Background(), &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: constants.TestDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: activityTaskToken, Identity: identity, Details: []byte("details"), }, }) s.Nil(err) executionBuilder = s.getBuilder(constants.TestDomainID, we) s.Equal(int64(13), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(8), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestRequestCancel_RespondDecisionTaskCompleted_SuccessWithQueries() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 7, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 1) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} decisions := []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: activityID, }, }} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() // load mutable state such that it already exists in memory when respond decision task is called // this enables us to set query registry on it ctx, release, err := s.mockHistoryEngine.executionCache.GetOrCreateWorkflowExecutionForBackground(constants.TestDomainID, we) s.NoError(err) loadedMS, err := ctx.LoadWorkflowExecution(context.Background()) s.NoError(err) qr := query.NewRegistry() id1, _ := qr.BufferQuery(&types.WorkflowQuery{}) id2, _ := qr.BufferQuery(&types.WorkflowQuery{}) id3, _ := qr.BufferQuery(&types.WorkflowQuery{}) loadedMS.SetQueryRegistry(qr) release(nil) result1 := &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeAnswered.Ptr(), Answer: []byte{1, 2, 3}, } result2 := &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeFailed.Ptr(), ErrorMessage: "error reason", } queryResults := map[string]*types.WorkflowQueryResult{ id1: result1, id2: result2, } _, err = s.mockHistoryEngine.RespondDecisionTaskCompleted(s.constructCallContext(cc.GoWorkerConsistentQueryVersion), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, QueryResults: queryResults, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(11), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(8), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) s.Len(qr.GetCompletedIDs(), 2) completed1, err := qr.GetTerminationState(id1) s.NoError(err) s.Equal(result1, completed1.QueryResult) s.Equal(query.TerminationTypeCompleted, completed1.TerminationType) completed2, err := qr.GetTerminationState(id2) s.NoError(err) s.Equal(result2, completed2.QueryResult) s.Equal(query.TerminationTypeCompleted, completed2.TerminationType) s.Len(qr.GetBufferedIDs(), 0) s.Len(qr.GetFailedIDs(), 0) s.Len(qr.GetUnblockedIDs(), 1) unblocked1, err := qr.GetTerminationState(id3) s.NoError(err) s.Nil(unblocked1.QueryResult) s.Equal(query.TerminationTypeUnblocked, unblocked1.TerminationType) // Try recording activity heartbeat s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() activityTaskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: "wId", RunID: we.GetRunID(), ScheduleID: 5, }) hbResponse, err := s.mockHistoryEngine.RecordActivityTaskHeartbeat(context.Background(), &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: constants.TestDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: activityTaskToken, Identity: identity, Details: []byte("details"), }, }) s.Nil(err) s.NotNil(hbResponse) s.True(hbResponse.CancelRequested) // Try cancelling the request. s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err = s.mockHistoryEngine.RespondActivityTaskCanceled(context.Background(), &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: constants.TestDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: activityTaskToken, Identity: identity, Details: []byte("details"), }, }) s.Nil(err) executionBuilder = s.getBuilder(constants.TestDomainID, we) s.Equal(int64(13), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(8), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestRequestCancel_RespondDecisionTaskCompleted_SuccessWithConsistentQueriesUnsupported() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 7, }) identity := "testIdentity" activityID := "activity1_id" activityType := "activity_type1" activityInput := []byte("input1") msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) activityScheduledEvent, _ := test.AddActivityTaskScheduledEvent(msBuilder, decisionCompletedEvent.ID, activityID, activityType, tl, activityInput, 100, 10, 1, 1) test.AddActivityTaskStartedEvent(msBuilder, activityScheduledEvent.ID, identity) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} decisions := []*types.Decision{{ DecisionType: types.DecisionTypeRequestCancelActivityTask.Ptr(), RequestCancelActivityTaskDecisionAttributes: &types.RequestCancelActivityTaskDecisionAttributes{ ActivityID: activityID, }, }} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() // load mutable state such that it already exists in memory when respond decision task is called // this enables us to set query registry on it ctx, release, err := s.mockHistoryEngine.executionCache.GetOrCreateWorkflowExecutionForBackground(constants.TestDomainID, we) s.NoError(err) loadedMS, err := ctx.LoadWorkflowExecution(context.Background()) s.NoError(err) qr := query.NewRegistry() qr.BufferQuery(&types.WorkflowQuery{}) qr.BufferQuery(&types.WorkflowQuery{}) qr.BufferQuery(&types.WorkflowQuery{}) loadedMS.SetQueryRegistry(qr) release(nil) _, err = s.mockHistoryEngine.RespondDecisionTaskCompleted(s.constructCallContext("0.0.0"), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(11), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(8), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) s.Len(qr.GetBufferedIDs(), 0) s.Len(qr.GetCompletedIDs(), 0) s.Len(qr.GetUnblockedIDs(), 0) s.Len(qr.GetFailedIDs(), 3) } func (s *engineSuite) constructCallContext(featureVersion string) context.Context { ctx := context.Background() ctx, call := encoding.NewInboundCall(ctx) err := call.ReadFromRequest(&transport.Request{ Headers: transport.NewHeaders().With(common.ClientImplHeaderName, cc.GoSDK).With(common.FeatureVersionHeaderName, featureVersion), }) s.NoError(err) return ctx } func (s *engineSuite) TestStarTimer_DuplicateTimerID() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" timerID := "t1" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} decisions := []*types.Decision{{ DecisionType: types.DecisionTypeStartTimer.Ptr(), StartTimerDecisionAttributes: &types.StartTimerDecisionAttributes{ TimerID: timerID, StartToFireTimeoutSeconds: common.Int64Ptr(1), }, }} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) // Try to add the same timer ID again. di2 := test.AddDecisionTaskScheduledEvent(executionBuilder) test.AddDecisionTaskStartedEvent(executionBuilder, di2.ScheduleID, tl, identity) taskToken2, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: di2.ScheduleID, }) ms2 := execution.CreatePersistenceMutableState(s.T(), executionBuilder) gwmsResponse2 := &persistence.GetWorkflowExecutionResponse{State: ms2} decisionFailedEvent := false s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse2, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Run(func(arguments mock.Arguments) { req := arguments.Get(1).(*persistence.AppendHistoryNodesRequest) decTaskIndex := len(req.Events) - 1 if decTaskIndex >= 0 && *req.Events[decTaskIndex].EventType == types.EventTypeDecisionTaskFailed { decisionFailedEvent = true } }).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken2, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) s.True(decisionFailedEvent) executionBuilder = s.getBuilder(constants.TestDomainID, we) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.True(executionBuilder.HasPendingDecision()) di3, ok := executionBuilder.GetDecisionInfo(executionBuilder.GetExecutionInfo().NextEventID) s.True(ok, "DI.ScheduleID: %v, ScheduleID: %v, StartedID: %v", di2.ScheduleID, executionBuilder.GetExecutionInfo().DecisionScheduleID, executionBuilder.GetExecutionInfo().DecisionStartedID) s.Equal(executionBuilder.GetExecutionInfo().NextEventID, di3.ScheduleID) s.Equal(int64(1), di3.Attempt) } func (s *engineSuite) TestUserTimer_RespondDecisionTaskCompleted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 6, }) identity := "testIdentity" timerID := "t1" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) // Verify cancel timer with a start event. test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) test.AddTimerStartedEvent(msBuilder, decisionCompletedEvent.ID, timerID, 10) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} decisions := []*types.Decision{{ DecisionType: types.DecisionTypeCancelTimer.Ptr(), CancelTimerDecisionAttributes: &types.CancelTimerDecisionAttributes{ TimerID: timerID, }, }} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(10), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(7), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestCancelTimer_RespondDecisionTaskCompleted_NoStartTimer() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 2, }) identity := "testIdentity" timerID := "t1" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) // Verify cancel timer with a start event. test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} decisions := []*types.Decision{{ DecisionType: types.DecisionTypeCancelTimer.Ptr(), CancelTimerDecisionAttributes: &types.CancelTimerDecisionAttributes{ TimerID: timerID, }, }} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err := s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(6), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(3), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) } func (s *engineSuite) TestCancelTimer_RespondDecisionTaskCompleted_TimerFired() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" taskToken, _ := json.Marshal(&common.TaskToken{ WorkflowID: we.WorkflowID, RunID: we.RunID, ScheduleID: 6, }) identity := "testIdentity" timerID := "t1" msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) // Verify cancel timer with a start event. test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tl, []byte("input"), 100, 100, identity, nil) di := test.AddDecisionTaskScheduledEvent(msBuilder) decisionStartedEvent := test.AddDecisionTaskStartedEvent(msBuilder, di.ScheduleID, tl, identity) decisionCompletedEvent := test.AddDecisionTaskCompletedEvent(msBuilder, di.ScheduleID, decisionStartedEvent.ID, nil, identity) test.AddTimerStartedEvent(msBuilder, decisionCompletedEvent.ID, timerID, 10) di2 := test.AddDecisionTaskScheduledEvent(msBuilder) test.AddDecisionTaskStartedEvent(msBuilder, di2.ScheduleID, tl, identity) test.AddTimerFiredEvent(msBuilder, timerID) _, _, err := msBuilder.CloseTransactionAsMutation(time.Now(), execution.TransactionPolicyActive) s.Nil(err) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.True(len(gwmsResponse.State.BufferedEvents) > 0) decisions := []*types.Decision{{ DecisionType: types.DecisionTypeCancelTimer.Ptr(), CancelTimerDecisionAttributes: &types.CancelTimerDecisionAttributes{ TimerID: timerID, }, }} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(input *persistence.UpdateWorkflowExecutionRequest) bool { // need to check whether the buffered events are cleared s.True(input.UpdateWorkflowMutation.ClearBufferedEvents) return true })).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.mockHistoryEngine.RespondDecisionTaskCompleted(context.Background(), &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: constants.TestDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: taskToken, Decisions: decisions, ExecutionContext: []byte("context"), Identity: identity, }, }) s.Nil(err) executionBuilder := s.getBuilder(constants.TestDomainID, we) s.Equal(int64(10), executionBuilder.GetExecutionInfo().NextEventID) s.Equal(int64(7), executionBuilder.GetExecutionInfo().LastProcessedEvent) s.Equal(persistence.WorkflowStateRunning, executionBuilder.GetExecutionInfo().State) s.False(executionBuilder.HasPendingDecision()) s.False(executionBuilder.HasBufferedEvents()) } func (s *engineSuite) TestSignalWorkflowExecution() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") signalRequest := &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ Domain: constants.TestDomainID, WorkflowExecution: &we, Identity: identity, SignalName: signalName, Input: input, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tasklist, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) ms.ExecutionInfo.DomainID = constants.TestDomainID gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err := s.mockHistoryEngine.SignalWorkflowExecution(context.Background(), signalRequest) s.Nil(err) } // Test signal decision by adding request ID func (s *engineSuite) TestSignalWorkflowExecution_DuplicateRequest_WorkflowOpen() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" signalName := "my signal name 2" input := []byte("test input 2") requestID := uuid.New() signalRequest := &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ Domain: constants.TestDomainID, WorkflowExecution: &we, Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tasklist, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) // assume duplicate request id ms.SignalRequestedIDs = make(map[string]struct{}) ms.SignalRequestedIDs[requestID] = struct{}{} ms.ExecutionInfo.DomainID = constants.TestDomainID gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.SignalWorkflowExecution(context.Background(), signalRequest) s.Nil(err) } func (s *engineSuite) TestSignalWorkflowExecution_DuplicateRequest_WorkflowCompleted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" signalName := "my signal name 2" input := []byte("test input 2") requestID := uuid.New() signalRequest := &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ Domain: constants.TestDomainID, WorkflowExecution: &we, Identity: identity, SignalName: signalName, Input: input, RequestID: requestID, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tasklist, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) // assume duplicate request id ms.SignalRequestedIDs = make(map[string]struct{}) ms.SignalRequestedIDs[requestID] = struct{}{} ms.ExecutionInfo.DomainID = constants.TestDomainID ms.ExecutionInfo.State = persistence.WorkflowStateCompleted gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.SignalWorkflowExecution(context.Background(), signalRequest) s.Nil(err) } func (s *engineSuite) TestSignalWorkflowExecution_WorkflowCompleted() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") signalRequest := &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ Domain: constants.TestDomainID, WorkflowExecution: we, Identity: identity, SignalName: signalName, Input: input, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, *we, "wType", tasklist, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) ms.ExecutionInfo.State = persistence.WorkflowStateCompleted gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() err := s.mockHistoryEngine.SignalWorkflowExecution(context.Background(), signalRequest) s.EqualError(err, "workflow execution already completed") } func (s *engineSuite) TestSignalWorkflowExecution_DelayStart_NoDecisionScheduled() { // 1. Setup Cluster Info testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() // 2. Setup Request Data we := types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") signalRequest := &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ Domain: constants.TestDomainID, WorkflowExecution: &we, Identity: identity, SignalName: signalName, Input: input, }, } // 3. Build Mutable State - simulates DelayStart by NOT adding a decision task msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) // Only add start event, no decision task scheduled — simulates DelayStart waiting test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tasklist, []byte("input"), 100, 200, identity, nil) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) ms.ExecutionInfo.DomainID = constants.TestDomainID gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} var updateReq *persistence.UpdateWorkflowExecutionRequest // 4. Setup Mocks - capture the update request to verify DecisionScheduleID s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { req, _ := args.Get(1).(*persistence.UpdateWorkflowExecutionRequest) updateReq = req }). Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil). Once() // 5. Run the Signal Call err := s.mockHistoryEngine.SignalWorkflowExecution(context.Background(), signalRequest) // 6. Assertions s.Nil(err) s.NotNil(updateReq) s.NotNil(updateReq.UpdateWorkflowMutation) s.NotNil(updateReq.UpdateWorkflowMutation.ExecutionInfo) // Verify that NO decision was scheduled (ID is empty) because workflow hasn't processed first decision s.Equal(commonconstants.EmptyEventID, updateReq.UpdateWorkflowMutation.ExecutionInfo.DecisionScheduleID) } func (s *engineSuite) TestSignalWorkflowExecution_AfterFirstDecision_DecisionScheduled() { // Test that signals DO schedule a decision when workflow has already processed its first decision testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() we := types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" signalName := "my signal name" input := []byte("test input") signalRequest := &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ Domain: constants.TestDomainID, WorkflowExecution: &we, Identity: identity, SignalName: signalName, Input: input, }, } // Build mutable state with a pending decision (proves HasProcessedOrPendingDecision returns true) msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), we.GetRunID(), constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", tasklist, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) ms.ExecutionInfo.DomainID = constants.TestDomainID gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} var updateReq *persistence.UpdateWorkflowExecutionRequest s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { req, _ := args.Get(1).(*persistence.UpdateWorkflowExecutionRequest) updateReq = req }). Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil). Once() err := s.mockHistoryEngine.SignalWorkflowExecution(context.Background(), signalRequest) s.Nil(err) s.NotNil(updateReq) s.NotNil(updateReq.UpdateWorkflowMutation) s.NotNil(updateReq.UpdateWorkflowMutation.ExecutionInfo) // Verify that a decision WAS scheduled because workflow has processed first decision s.NotEqual(commonconstants.EmptyEventID, updateReq.UpdateWorkflowMutation.ExecutionInfo.DecisionScheduleID) } func (s *engineSuite) TestSignalWithStartWorkflowExecution_DelayStart_NoDecisionScheduled() { // Test SignalWithStart on existing workflow waiting for DelayStart - should NOT schedule decision testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() domainID := constants.TestDomainID workflowID := "wId" runID := constants.TestRunID identity := "testIdentity" signalName := "my signal name" input := []byte("test input") sRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: domainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: domainID, WorkflowID: workflowID, Identity: identity, SignalName: signalName, Input: input, }, } // Build mutable state simulating DelayStart - no decision task msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), runID, constants.TestLocalDomainEntry, ) we := types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, } // Add workflow started event but no decision task test.AddWorkflowExecutionStartedEvent(msBuilder, we, "wType", "testTaskList", []byte("input"), 100, 200, identity, nil) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: runID} var updateReq *persistence.UpdateWorkflowExecutionRequest s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { req, _ := args.Get(1).(*persistence.UpdateWorkflowExecutionRequest) updateReq = req }). Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil). Once() resp, err := s.mockHistoryEngine.SignalWithStartWorkflowExecution(context.Background(), sRequest) s.Nil(err) s.Equal(runID, resp.GetRunID()) s.NotNil(updateReq) s.NotNil(updateReq.UpdateWorkflowMutation) s.NotNil(updateReq.UpdateWorkflowMutation.ExecutionInfo) // Verify that NO decision was scheduled because workflow hasn't processed first decision s.Equal(commonconstants.EmptyEventID, updateReq.UpdateWorkflowMutation.ExecutionInfo.DecisionScheduleID) } func (s *engineSuite) TestRemoveSignalMutableState() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: s.mockHistoryEngine.clusterMetadata.GetCurrentClusterName(), FailoverVersion: 0, } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tasklist := "testTaskList" identity := "testIdentity" requestID := uuid.New() removeRequest := &types.RemoveSignalMutableStateRequest{ DomainUUID: constants.TestDomainID, WorkflowExecution: &workflowExecution, RequestID: requestID, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), constants.TestRunID, constants.TestLocalDomainEntry, ) test.AddWorkflowExecutionStartedEvent(msBuilder, workflowExecution, "wType", tasklist, []byte("input"), 100, 200, identity, nil) test.AddDecisionTaskScheduledEvent(msBuilder) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) ms.ExecutionInfo.DomainID = constants.TestDomainID gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() err := s.mockHistoryEngine.RemoveSignalMutableState(context.Background(), removeRequest) s.Nil(err) } func (s *engineSuite) TestReapplyEvents_ReturnSuccess() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-reapply", RunID: constants.TestRunID, } history := []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), Version: 1, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: constants.TestRunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockEventsReapplier.EXPECT().ReapplyEvents(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) err := s.mockHistoryEngine.ReapplyEvents( context.Background(), constants.TestDomainID, workflowExecution.WorkflowID, workflowExecution.RunID, history, ) s.NoError(err) } func (s *engineSuite) TestReapplyEvents_IgnoreSameVersionEvents() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-reapply-same-version", RunID: constants.TestRunID, } history := []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeTimerStarted.Ptr(), Version: commonconstants.EmptyVersion, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: constants.TestRunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockEventsReapplier.EXPECT().ReapplyEvents(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) err := s.mockHistoryEngine.ReapplyEvents( context.Background(), constants.TestDomainID, workflowExecution.WorkflowID, workflowExecution.RunID, history, ) s.NoError(err) } func (s *engineSuite) TestReapplyEvents_ResetWorkflow() { testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestLocalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestLocalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testActiveClusterInfo, nil).AnyTimes() workflowExecution := types.WorkflowExecution{ WorkflowID: "test-reapply-reset-workflow", RunID: constants.TestRunID, } history := []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), Version: 100, }, } msBuilder := execution.NewMutableStateBuilderWithEventV2( s.mockHistoryEngine.shard, testlogger.New(s.Suite.T()), workflowExecution.GetRunID(), constants.TestLocalDomainEntry, ) ms := execution.CreatePersistenceMutableState(s.T(), msBuilder) ms.ExecutionInfo.State = persistence.WorkflowStateCompleted ms.ExecutionInfo.LastProcessedEvent = 1 token, err := msBuilder.GetCurrentBranchToken() s.NoError(err) item := persistence.NewVersionHistoryItem(1, 1) versionHistory := persistence.NewVersionHistory(token, []*persistence.VersionHistoryItem{item}) ms.VersionHistories = persistence.NewVersionHistories(versionHistory) gwmsResponse := &persistence.GetWorkflowExecutionResponse{State: ms} gceResponse := &persistence.GetCurrentExecutionResponse{RunID: constants.TestRunID} s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(gceResponse, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(gwmsResponse, nil).Once() s.mockEventsReapplier.EXPECT().ReapplyEvents(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) s.mockWorkflowResetter.EXPECT().ResetWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(nil).Times(1) err = s.mockHistoryEngine.ReapplyEvents( context.Background(), constants.TestDomainID, workflowExecution.WorkflowID, workflowExecution.RunID, history, ) s.NoError(err) } func (s *engineSuite) getBuilder(testDomainID string, we types.WorkflowExecution) execution.MutableState { context, release, err := s.mockHistoryEngine.executionCache.GetOrCreateWorkflowExecutionForBackground(testDomainID, we) if err != nil { return nil } defer release(nil) return context.GetWorkflowExecution() } func (s *engineSuite) getActivityScheduledEvent( msBuilder execution.MutableState, scheduleID int64, ) *types.HistoryEvent { event, _ := msBuilder.GetActivityScheduledEvent(context.Background(), scheduleID) return event } func (s *engineSuite) printHistory(builder execution.MutableState) string { return thrift.FromHistory(builder.GetHistoryBuilder().GetHistory()).String() } func TestRecordChildExecutionCompleted(t *testing.T) { testCases := []struct { name string request *types.RecordChildExecutionCompletedRequest mockSetup func(*execution.MockMutableState) wantErr bool assertErr func(t *testing.T, err error) }{ { name: "workflow not running", request: &types.RecordChildExecutionCompletedRequest{ DomainUUID: "58f7998e-9c00-4827-bbd3-6a891b3ca0ca", InitiatedID: 1, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "4353ddce-ca34-4c78-9785-dc0b83af4bbc", }, CompletedExecution: &types.WorkflowExecution{ WorkflowID: "wid1", RunID: "312b2440-2859-4e50-a59f-d92a300a072d", }, }, mockSetup: func(ms *execution.MockMutableState) { ms.EXPECT().IsWorkflowExecutionRunning().Return(false) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, workflow.ErrNotExists, err) }, }, { name: "stale mutable state - not containing initiated event", request: &types.RecordChildExecutionCompletedRequest{ DomainUUID: "58f7998e-9c00-4827-bbd3-6a891b3ca0ca", InitiatedID: 10, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "4353ddce-ca34-4c78-9785-dc0b83af4bbc", }, CompletedExecution: &types.WorkflowExecution{ WorkflowID: "wid1", RunID: "312b2440-2859-4e50-a59f-d92a300a072d", }, }, mockSetup: func(ms *execution.MockMutableState) { ms.EXPECT().IsWorkflowExecutionRunning().Return(true) ms.EXPECT().GetChildExecutionInfo(int64(10)).Return(nil, false) ms.EXPECT().GetNextEventID().Return(int64(10)).Times(2) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, workflow.ErrStaleState, err) }, }, { name: "pending child execution not found", request: &types.RecordChildExecutionCompletedRequest{ DomainUUID: "58f7998e-9c00-4827-bbd3-6a891b3ca0ca", InitiatedID: 10, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "4353ddce-ca34-4c78-9785-dc0b83af4bbc", }, CompletedExecution: &types.WorkflowExecution{ WorkflowID: "wid1", RunID: "312b2440-2859-4e50-a59f-d92a300a072d", }, }, mockSetup: func(ms *execution.MockMutableState) { ms.EXPECT().IsWorkflowExecutionRunning().Return(true) ms.EXPECT().GetChildExecutionInfo(int64(10)).Return(nil, false) ms.EXPECT().GetNextEventID().Return(int64(11)).Times(1) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &types.EntityNotExistsError{Message: "Pending child execution not found."}, err) }, }, { name: "stale mutable state - not containing started event", request: &types.RecordChildExecutionCompletedRequest{ DomainUUID: "58f7998e-9c00-4827-bbd3-6a891b3ca0ca", InitiatedID: 10, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "4353ddce-ca34-4c78-9785-dc0b83af4bbc", }, CompletedExecution: &types.WorkflowExecution{ WorkflowID: "wid1", RunID: "312b2440-2859-4e50-a59f-d92a300a072d", }, }, mockSetup: func(ms *execution.MockMutableState) { ms.EXPECT().IsWorkflowExecutionRunning().Return(true) ms.EXPECT().GetChildExecutionInfo(int64(10)).Return(&persistence.ChildExecutionInfo{StartedID: commonconstants.EmptyEventID}, true) ms.EXPECT().GetNextEventID().Return(int64(11)).Times(1) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, workflow.ErrStaleState, err) }, }, { name: "pending child execution workflowID mismatch", request: &types.RecordChildExecutionCompletedRequest{ DomainUUID: "58f7998e-9c00-4827-bbd3-6a891b3ca0ca", InitiatedID: 10, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "4353ddce-ca34-4c78-9785-dc0b83af4bbc", }, CompletedExecution: &types.WorkflowExecution{ WorkflowID: "wid1", RunID: "312b2440-2859-4e50-a59f-d92a300a072d", }, StartedID: 11, }, mockSetup: func(ms *execution.MockMutableState) { ms.EXPECT().IsWorkflowExecutionRunning().Return(true) ms.EXPECT().GetChildExecutionInfo(int64(10)).Return(&persistence.ChildExecutionInfo{StartedID: 11, StartedWorkflowID: "wid0"}, true) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &types.EntityNotExistsError{Message: "Pending child execution workflowID mismatch."}, err) }, }, { name: "success - child workflow completed", request: &types.RecordChildExecutionCompletedRequest{ DomainUUID: "58f7998e-9c00-4827-bbd3-6a891b3ca0ca", InitiatedID: 10, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "4353ddce-ca34-4c78-9785-dc0b83af4bbc", }, CompletedExecution: &types.WorkflowExecution{ WorkflowID: "wid1", RunID: "312b2440-2859-4e50-a59f-d92a300a072d", }, CompletionEvent: &types.HistoryEvent{ EventType: types.EventTypeWorkflowExecutionCompleted.Ptr(), WorkflowExecutionCompletedEventAttributes: &types.WorkflowExecutionCompletedEventAttributes{ Result: []byte("success"), }, }, StartedID: 11, }, mockSetup: func(ms *execution.MockMutableState) { ms.EXPECT().IsWorkflowExecutionRunning().Return(true) ms.EXPECT().GetChildExecutionInfo(int64(10)).Return(&persistence.ChildExecutionInfo{StartedID: 11, StartedWorkflowID: "wid1"}, true) ms.EXPECT().AddChildWorkflowExecutionCompletedEvent(int64(10), gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockShard := shard.NewTestContext(t, ctrl, &persistence.ShardInfo{}, config.NewForTest()) mockDomainCache := mockShard.Resource.DomainCache mockActiveClusterManager := mockShard.Resource.ActiveClusterMgr mockActiveClusterManager.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterInfo{ActiveClusterName: mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: 0}, nil).Times(1) testDomainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{ID: constants.TestDomainID}, &persistence.DomainConfig{}, "", ) mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(testDomainEntry, nil).AnyTimes() mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(constants.TestDomainID, nil).AnyTimes() ms := execution.NewMockMutableState(ctrl) tc.mockSetup(ms) historyEngine := &historyEngineImpl{ shard: mockShard, clusterMetadata: mockShard.GetClusterMetadata(), timeSource: mockShard.GetTimeSource(), metricsClient: metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), logger: mockShard.GetLogger(), updateWithActionFn: func(_ context.Context, _ log.Logger, _ execution.Cache, _ string, _ types.WorkflowExecution, _ bool, _ time.Time, actionFn func(wfContext execution.Context, mutableState execution.MutableState) error) error { return actionFn(nil, ms) }, activeClusterManager: mockActiveClusterManager, } err := historyEngine.RecordChildExecutionCompleted(context.Background(), tc.request) if tc.wantErr { tc.assertErr(t, err) } else { assert.NoError(t, err) } }) } } func Test_createShardNameFromShardID(t *testing.T) { shardID := 1 shardName := createShardNameFromShardID(shardID) assert.Equal(t, fmt.Sprintf("history-engine-%d", shardID), shardName) } ================================================ FILE: service/history/engine/engineimpl/notify_tasks.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "errors" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/replication" ) func (e *historyEngineImpl) NotifyNewHistoryEvent(event *events.Notification) { e.historyEventNotifier.NotifyNewHistoryEvent(event) } func (e *historyEngineImpl) NotifyNewTransferTasks(info *hcommon.NotifyTaskInfo) { if len(info.Tasks) == 0 { return } task := info.Tasks[0] clusterName, err := e.shard.GetClusterMetadata().ClusterNameForFailoverVersion(task.GetVersion()) if err == nil { transferProcessor, ok := e.queueProcessors[persistence.HistoryTaskCategoryTransfer] if !ok { e.logger.Error("transfer processor not found", tag.Error(err)) return } transferProcessor.NotifyNewTask(clusterName, info) } } func (e *historyEngineImpl) NotifyNewTimerTasks(info *hcommon.NotifyTaskInfo) { if len(info.Tasks) == 0 { return } task := info.Tasks[0] clusterName, err := e.shard.GetClusterMetadata().ClusterNameForFailoverVersion(task.GetVersion()) if err == nil { timerProcessor, ok := e.queueProcessors[persistence.HistoryTaskCategoryTimer] if !ok { e.logger.Error("timer processor not found", tag.Error(err)) return } timerProcessor.NotifyNewTask(clusterName, info) } } func (e *historyEngineImpl) NotifyNewReplicationTasks(info *hcommon.NotifyTaskInfo) { for _, task := range info.Tasks { hTask, err := hydrateReplicationTask(task, info.ExecutionInfo, info.VersionHistories, info.Activities, info.History) if err != nil { e.logger.Error("failed to preemptively hydrate replication task", tag.Error(err)) continue } e.replicationTaskStore.Put(hTask) } } func hydrateReplicationTask( task persistence.Task, exec *persistence.WorkflowExecutionInfo, versionHistories *persistence.VersionHistories, activities map[int64]*persistence.ActivityInfo, history events.PersistedBlobs, ) (*types.ReplicationTask, error) { info := persistence.ReplicationTaskInfo{ DomainID: exec.DomainID, WorkflowID: exec.WorkflowID, RunID: exec.RunID, TaskType: task.GetTaskType(), CreationTime: task.GetVisibilityTimestamp().UnixNano(), TaskID: task.GetTaskID(), Version: task.GetVersion(), } switch t := task.(type) { case *persistence.HistoryReplicationTask: info.BranchToken = t.BranchToken info.NewRunBranchToken = t.NewRunBranchToken info.FirstEventID = t.FirstEventID info.NextEventID = t.NextEventID case *persistence.SyncActivityTask: info.ScheduledID = t.ScheduledID case *persistence.FailoverMarkerTask: // No specific fields, but supported default: return nil, errors.New("unknown replication task") } hydrator := replication.NewImmediateTaskHydrator( exec.IsRunning(), versionHistories, activities, history.Find(info.BranchToken, info.FirstEventID), history.Find(info.NewRunBranchToken, constants.FirstEventID), ) return hydrator.Hydrate(context.Background(), task) } ================================================ FILE: service/history/engine/engineimpl/poll_mutable_state.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "fmt" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // GetMutableState retrieves the mutable state of the workflow execution func (e *historyEngineImpl) GetMutableState(ctx context.Context, request *types.GetMutableStateRequest) (*types.GetMutableStateResponse, error) { return e.getMutableStateOrLongPoll(ctx, request) } // PollMutableState retrieves the mutable state of the workflow execution with long polling func (e *historyEngineImpl) PollMutableState(ctx context.Context, request *types.PollMutableStateRequest) (*types.PollMutableStateResponse, error) { response, err := e.getMutableStateOrLongPoll(ctx, &types.GetMutableStateRequest{ DomainUUID: request.DomainUUID, Execution: request.Execution, ExpectedNextEventID: request.ExpectedNextEventID, CurrentBranchToken: request.CurrentBranchToken, VersionHistoryItem: request.GetVersionHistoryItem(), }) if err != nil { return nil, e.updateEntityNotExistsErrorOnPassiveCluster(ctx, err, request.GetDomainUUID(), request.Execution) } return &types.PollMutableStateResponse{ Execution: response.Execution, WorkflowType: response.WorkflowType, NextEventID: response.NextEventID, PreviousStartedEventID: response.PreviousStartedEventID, LastFirstEventID: response.LastFirstEventID, TaskList: response.TaskList, StickyTaskList: response.StickyTaskList, ClientLibraryVersion: response.ClientLibraryVersion, ClientFeatureVersion: response.ClientFeatureVersion, ClientImpl: response.ClientImpl, StickyTaskListScheduleToStartTimeout: response.StickyTaskListScheduleToStartTimeout, CurrentBranchToken: response.CurrentBranchToken, VersionHistories: response.VersionHistories, WorkflowState: response.WorkflowState, WorkflowCloseState: response.WorkflowCloseState, }, nil } func (e *historyEngineImpl) getMutableState( ctx context.Context, domainID string, execution types.WorkflowExecution, ) (retResp *types.GetMutableStateResponse, retError error) { wfContext, release, retError := e.executionCache.GetOrCreateWorkflowExecution(ctx, domainID, execution) if retError != nil { return } defer func() { release(retError) }() mutableState, retError := wfContext.LoadWorkflowExecution(ctx) if retError != nil { return } currentBranchToken, err := mutableState.GetCurrentBranchToken() if err != nil { return nil, err } executionInfo := mutableState.GetExecutionInfo() execution.RunID = wfContext.GetExecution().RunID workflowState, workflowCloseState := mutableState.GetWorkflowStateCloseStatus() retResp = &types.GetMutableStateResponse{ Execution: &execution, WorkflowType: &types.WorkflowType{Name: executionInfo.WorkflowTypeName}, LastFirstEventID: mutableState.GetLastFirstEventID(), NextEventID: mutableState.GetNextEventID(), PreviousStartedEventID: common.Int64Ptr(mutableState.GetPreviousStartedEventID()), TaskList: &types.TaskList{Name: executionInfo.TaskList}, StickyTaskList: &types.TaskList{Name: executionInfo.StickyTaskList, Kind: types.TaskListKindSticky.Ptr()}, ClientLibraryVersion: executionInfo.ClientLibraryVersion, ClientFeatureVersion: executionInfo.ClientFeatureVersion, ClientImpl: executionInfo.ClientImpl, IsWorkflowRunning: mutableState.IsWorkflowExecutionRunning(), StickyTaskListScheduleToStartTimeout: common.Int32Ptr(executionInfo.StickyScheduleToStartTimeout), CurrentBranchToken: currentBranchToken, WorkflowState: common.Int32Ptr(int32(workflowState)), WorkflowCloseState: common.Int32Ptr(int32(workflowCloseState)), IsStickyTaskListEnabled: mutableState.IsStickyTaskListEnabled(), HistorySize: mutableState.GetHistorySize(), } versionHistories := mutableState.GetVersionHistories() if versionHistories != nil { retResp.VersionHistories = versionHistories.ToInternalType() } return } func (e *historyEngineImpl) updateEntityNotExistsErrorOnPassiveCluster(ctx context.Context, err error, domainID string, execution *types.WorkflowExecution) error { switch err.(type) { case *types.EntityNotExistsError: activeClusterInfo, activeClusterErr := e.activeClusterManager.GetActiveClusterInfoByWorkflow(ctx, domainID, execution.GetWorkflowID(), execution.GetRunID()) if activeClusterErr != nil { return err // return original error if could not access active cluster manager } if activeClusterInfo.ActiveClusterName != e.clusterMetadata.GetCurrentClusterName() { return &types.EntityNotExistsError{ Message: "Workflow execution not found in non-active cluster", ActiveCluster: activeClusterInfo.ActiveClusterName, CurrentCluster: e.clusterMetadata.GetCurrentClusterName(), } } } return err } func (e *historyEngineImpl) getMutableStateWithCurrentVersionHistory( ctx context.Context, domainID string, execution types.WorkflowExecution, ) (*types.GetMutableStateResponse, *persistence.VersionHistory, error) { mutableState, err := e.getMutableState(ctx, domainID, execution) if err != nil { return nil, nil, err } // must be corrupted workflow if mutableState.GetVersionHistories() == nil { e.logger.Warn("version histories do not exist") return nil, nil, &types.InternalDataInconsistencyError{Message: "version histories do not exist"} } currentVersionHistory, err := persistence.NewVersionHistoriesFromInternalType( mutableState.GetVersionHistories(), ).GetCurrentVersionHistory() if err != nil { return nil, nil, fmt.Errorf("unable to get version history while looking up response for mutable state: %w", err) } return mutableState, currentVersionHistory, err } func (e *historyEngineImpl) getMutableStateOrLongPoll( ctx context.Context, request *types.GetMutableStateRequest, ) (*types.GetMutableStateResponse, error) { if err := common.ValidateDomainUUID(request.DomainUUID); err != nil { return nil, err } domainID := request.DomainUUID execution := types.WorkflowExecution{ WorkflowID: request.Execution.WorkflowID, RunID: request.Execution.RunID, } mutableState, currentVersionHistory, err := e.getMutableStateWithCurrentVersionHistory(ctx, domainID, execution) if err != nil { return nil, err } if request.VersionHistoryItem == nil { lastVersionHistoryItem, err := currentVersionHistory.GetLastItem() if err != nil { return nil, err } request.VersionHistoryItem = lastVersionHistoryItem.ToInternalType() } // Use the latest event id + event version as the branch identifier. This pair is unique across clusters. // We return the full version histories. Callers need to fetch the last version history item from current branch // and use the last version history item in following calls. if !currentVersionHistory.ContainsItem(persistence.NewVersionHistoryItemFromInternalType(request.VersionHistoryItem)) { e.logger.Warn("current version history and requested one are different - found on first check", tag.Dynamic("current-version-history", currentVersionHistory), tag.Dynamic("requested-version-history", request.VersionHistoryItem), ) return nil, &types.CurrentBranchChangedError{ Message: "current branch token and request branch token doesn't match", CurrentBranchToken: mutableState.CurrentBranchToken} } // set the run id in case query the current running workflow execution.RunID = mutableState.Execution.RunID // expectedNextEventID is 0 when caller want to get the current next event ID without blocking expectedNextEventID := constants.FirstEventID if request.ExpectedNextEventID != 0 { expectedNextEventID = request.GetExpectedNextEventID() } if expectedNextEventID < mutableState.GetNextEventID() || !mutableState.GetIsWorkflowRunning() { return mutableState, nil } // if caller decide to long poll on workflow execution // and the event ID we are looking for is smaller than current next event ID return e.longPollForEventID(ctx, expectedNextEventID, domainID, execution, request.VersionHistoryItem) } func (e *historyEngineImpl) longPollForEventID( ctx context.Context, expectedNextEventID int64, domainID string, execution types.WorkflowExecution, versionHistoryItem *types.VersionHistoryItem, ) (*types.GetMutableStateResponse, error) { wfIdentifier := definition.NewWorkflowIdentifier(domainID, execution.GetWorkflowID(), execution.GetRunID()) subscriberID, channel, err := e.historyEventNotifier.WatchHistoryEvent(wfIdentifier) if err != nil { return nil, err } defer e.historyEventNotifier.UnwatchHistoryEvent(wfIdentifier, subscriberID) //nolint:errcheck // check again in case the next event ID is updated before we subscribed mutableState, currentVersionHistory, err := e.getMutableStateWithCurrentVersionHistory(ctx, domainID, execution) if err != nil { return nil, err } if !currentVersionHistory.ContainsItem(persistence.NewVersionHistoryItemFromInternalType(versionHistoryItem)) { e.logger.Warn("current version history and requested one are different - found on checking mutableState", tag.Dynamic("current-version-history", currentVersionHistory), tag.Dynamic("requested-version-history", versionHistoryItem), ) return nil, &types.CurrentBranchChangedError{ Message: "current branch token and request branch token doesn't match", CurrentBranchToken: mutableState.CurrentBranchToken, } } if expectedNextEventID < mutableState.GetNextEventID() || !mutableState.GetIsWorkflowRunning() { return mutableState, nil } domainName, err := e.shard.GetDomainCache().GetDomainName(domainID) if err != nil { return nil, err } expirationInterval := e.shard.GetConfig().LongPollExpirationInterval(domainName) if deadline, ok := ctx.Deadline(); ok { remainingTime := deadline.Sub(e.shard.GetTimeSource().Now()) // Here we return a safeguard error, to ensure that older clients are not stuck in long poll loop until context fully expires. // Otherwise, it results in multiple additional requests being made that returns empty responses. // Newer clients will not make request with too small timeout remaining. if remainingTime < longPollCompletionBuffer { return nil, context.DeadlineExceeded } // longPollCompletionBuffer is here to leave some room to finish current request without its timeout. expirationInterval = min( expirationInterval, remainingTime-longPollCompletionBuffer, ) } if expirationInterval <= 0 { return mutableState, nil } timer := time.NewTimer(expirationInterval) defer timer.Stop() for { select { case event := <-channel: mutableState.LastFirstEventID = event.LastFirstEventID mutableState.NextEventID = event.NextEventID mutableState.IsWorkflowRunning = event.WorkflowCloseState == persistence.WorkflowCloseStatusNone mutableState.PreviousStartedEventID = common.Int64Ptr(event.PreviousStartedEventID) mutableState.WorkflowState = common.Int32Ptr(int32(event.WorkflowState)) mutableState.WorkflowCloseState = common.Int32Ptr(int32(event.WorkflowCloseState)) currentVersionHistoryOnPoll, err := event.VersionHistories.GetCurrentVersionHistory() if err != nil { return nil, fmt.Errorf("unexpected error getting current version history while polling for mutable state: %w", err) } if !currentVersionHistoryOnPoll.ContainsItem(persistence.NewVersionHistoryItemFromInternalType(versionHistoryItem)) { e.logger.Warn("current version history and requested one are different", tag.Dynamic("current-version-history", currentVersionHistoryOnPoll), tag.Dynamic("requested-version-history", versionHistoryItem), ) return nil, &types.CurrentBranchChangedError{ Message: "current and requested version histories don't match - changed while polling", CurrentBranchToken: mutableState.CurrentBranchToken, } } if expectedNextEventID < mutableState.GetNextEventID() || !mutableState.GetIsWorkflowRunning() { return mutableState, nil } case <-timer.C: return mutableState, nil } } } ================================================ FILE: service/history/engine/engineimpl/query_workflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "strings" "time" "go.uber.org/yarpc/yarpcerrors" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/query" "github.com/uber/cadence/service/history/workflow" ) func (e *historyEngineImpl) QueryWorkflow( ctx context.Context, request *types.HistoryQueryWorkflowRequest, ) (retResp *types.HistoryQueryWorkflowResponse, retErr error) { scope := e.metricsClient.Scope(metrics.HistoryQueryWorkflowScope).Tagged(metrics.DomainTag(request.GetRequest().GetDomain())) shardMetricScope := e.metricsClient.Scope(metrics.HistoryQueryWorkflowScope, metrics.ShardIDTag(e.shard.GetShardID())) consistentQueryEnabled := e.config.EnableConsistentQuery() && e.config.EnableConsistentQueryByDomain(request.GetRequest().GetDomain()) if request.GetRequest().GetQueryConsistencyLevel() == types.QueryConsistencyLevelStrong { if !consistentQueryEnabled { return nil, workflow.ErrConsistentQueryNotEnabled } shardMetricScope.IncCounter(metrics.ConsistentQueryPerShard) e.logger.SampleInfo("History QueryWorkflow called with QueryConsistencyLevelStrong", e.config.SampleLoggingRate(), tag.ShardID(e.shard.GetShardID()), tag.WorkflowID(request.GetRequest().Execution.WorkflowID), tag.WorkflowDomainName(request.GetRequest().Domain)) } execution := *request.GetRequest().GetExecution() mutableStateResp, err := e.getMutableState(ctx, request.GetDomainUUID(), execution) if err != nil { return nil, err } req := request.GetRequest() if !mutableStateResp.GetIsWorkflowRunning() && req.QueryRejectCondition != nil { notOpenReject := req.GetQueryRejectCondition() == types.QueryRejectConditionNotOpen closeStatus := mutableStateResp.GetWorkflowCloseState() notCompletedCleanlyReject := req.GetQueryRejectCondition() == types.QueryRejectConditionNotCompletedCleanly && closeStatus != persistence.WorkflowCloseStatusCompleted if notOpenReject || notCompletedCleanlyReject { return &types.HistoryQueryWorkflowResponse{ Response: &types.QueryWorkflowResponse{ QueryRejected: &types.QueryRejected{ CloseStatus: persistence.ToInternalWorkflowExecutionCloseStatus(int(closeStatus)), }, }, }, nil } } // query cannot be processed unless at least one decision task has finished // if first decision task has not finished wait for up to a second for it to complete queryFirstDecisionTaskWaitTime := defaultQueryFirstDecisionTaskWaitTime ctxDeadline, ok := ctx.Deadline() if ok { ctxWaitTime := time.Until(ctxDeadline) - time.Second if ctxWaitTime > queryFirstDecisionTaskWaitTime { queryFirstDecisionTaskWaitTime = ctxWaitTime } } deadline := time.Now().Add(queryFirstDecisionTaskWaitTime) for mutableStateResp.GetPreviousStartedEventID() <= 0 && time.Now().Before(deadline) { time.Sleep(queryFirstDecisionTaskCheckInterval) mutableStateResp, err = e.getMutableState(ctx, request.GetDomainUUID(), execution) if err != nil { return nil, err } } if mutableStateResp.GetPreviousStartedEventID() <= 0 { scope.IncCounter(metrics.QueryBeforeFirstDecisionCount) return nil, workflow.ErrQueryWorkflowBeforeFirstDecision } de, err := e.shard.GetDomainCache().GetDomainByID(request.GetDomainUUID()) if err != nil { return nil, err } wfContext, release, err := e.executionCache.GetOrCreateWorkflowExecution(ctx, request.GetDomainUUID(), execution) if err != nil { return nil, err } defer func() { release(retErr) }() mutableState, err := wfContext.LoadWorkflowExecution(ctx) if err != nil { return nil, err } // If history is corrupted, query will be rejected if corrupted, err := e.checkForHistoryCorruptions(ctx, mutableState); err != nil { return nil, err } else if corrupted { return nil, &types.EntityNotExistsError{Message: "Workflow execution corrupted."} } // There are two ways in which queries get dispatched to decider. First, queries can be dispatched on decision tasks. // These decision tasks potentially contain new events and queries. The events are treated as coming before the query in time. // The second way in which queries are dispatched to decider is directly through matching; in this approach queries can be // dispatched to decider immediately even if there are outstanding events that came before the query. The following logic // is used to determine if a query can be safely dispatched directly through matching or if given the desired consistency // level must be dispatched on a decision task. There are four cases in which a query can be dispatched directly through // matching safely, without violating the desired consistency level: // 1. the domain is not active, in this case history is immutable so a query dispatched at any time is consistent // 2. the workflow is not running, whenever a workflow is not running dispatching query directly is consistent // 3. the client requested eventual consistency, in this case there are no consistency requirements so dispatching directly through matching is safe // 4. if there is no pending or started decision it means no events came before query arrived, so its safe to dispatch directly isActive := false clusterAttribute := mutableState.GetExecutionInfo().ActiveClusterSelectionPolicy.GetClusterAttribute() activeClusterInfo, exists := de.GetActiveClusterInfoByClusterAttribute(clusterAttribute) if !exists { // TODO: this is only possible if the domain metadata doesn't have the cluster attribute, should we return an error instead? e.logger.Warn("Failed to get active cluster info by cluster attribute, default to active", tag.Dynamic("clusterAttribute", clusterAttribute)) isActive = true } else { isActive = activeClusterInfo.ActiveClusterName == e.clusterMetadata.GetCurrentClusterName() } safeToDispatchDirectly := !isActive || !mutableState.IsWorkflowExecutionRunning() || req.GetQueryConsistencyLevel() == types.QueryConsistencyLevelEventual || (!mutableState.HasPendingDecision() && !mutableState.HasInFlightDecision()) if safeToDispatchDirectly { release(nil) msResp, err := e.getMutableState(ctx, request.GetDomainUUID(), execution) if err != nil { return nil, err } req.Execution.RunID = msResp.Execution.RunID return e.queryDirectlyThroughMatching(ctx, msResp, request.GetDomainUUID(), req, scope, isActive) } // If we get here it means query could not be dispatched through matching directly, so it must block // until either an result has been obtained on a decision task response or until it is safe to dispatch directly through matching. sw := scope.StartTimer(metrics.DecisionTaskQueryLatency) defer sw.Stop() queryReg := mutableState.GetQueryRegistry() if len(queryReg.GetBufferedIDs()) >= e.config.MaxBufferedQueryCount() { scope.IncCounter(metrics.QueryBufferExceededCount) return nil, workflow.ErrConsistentQueryBufferExceeded } queryID, termCh := queryReg.BufferQuery(req.GetQuery()) defer queryReg.RemoveQuery(queryID) release(nil) select { case <-termCh: state, err := queryReg.GetTerminationState(queryID) if err != nil { scope.IncCounter(metrics.QueryRegistryInvalidStateCount) return nil, err } switch state.TerminationType { case query.TerminationTypeCompleted: result := state.QueryResult switch result.GetResultType() { case types.QueryResultTypeAnswered: return &types.HistoryQueryWorkflowResponse{ Response: &types.QueryWorkflowResponse{ QueryResult: result.GetAnswer(), }, }, nil case types.QueryResultTypeFailed: return nil, &types.QueryFailedError{Message: result.GetErrorMessage()} default: scope.IncCounter(metrics.QueryRegistryInvalidStateCount) return nil, workflow.ErrQueryEnteredInvalidState } case query.TerminationTypeUnblocked: msResp, err := e.getMutableState(ctx, request.GetDomainUUID(), execution) if err != nil { return nil, err } req.Execution.RunID = msResp.Execution.RunID return e.queryDirectlyThroughMatching(ctx, msResp, request.GetDomainUUID(), req, scope, isActive) case query.TerminationTypeFailed: return nil, state.Failure default: scope.IncCounter(metrics.QueryRegistryInvalidStateCount) return nil, workflow.ErrQueryEnteredInvalidState } case <-ctx.Done(): scope.IncCounter(metrics.ConsistentQueryTimeoutCount) return nil, ctx.Err() } } func (e *historyEngineImpl) queryDirectlyThroughMatching( ctx context.Context, msResp *types.GetMutableStateResponse, domainID string, queryRequest *types.QueryWorkflowRequest, scope metrics.Scope, isActive bool, ) (*types.HistoryQueryWorkflowResponse, error) { sw := scope.StartTimer(metrics.DirectQueryDispatchLatency) defer sw.Stop() // Sticky task list is not very useful in the standby cluster because the decider cache is // not updated by dispatching tasks to it (it is only updated in the case of query). // Additionally on the standby side we are not even able to clear sticky. // Stickiness might be outdated if the customer did a restart of their nodes causing a query // dispatched on the standby side on sticky to hang. We decided it made sense to simply not attempt // query on sticky task list at all on the passive side. supportsStickyQuery := e.clientChecker.SupportsStickyQuery(msResp.GetClientImpl(), msResp.GetClientFeatureVersion()) == nil if msResp.GetIsStickyTaskListEnabled() && len(msResp.GetStickyTaskList().GetName()) != 0 && supportsStickyQuery && e.config.EnableStickyQuery(queryRequest.GetDomain()) && isActive { stickyMatchingRequest := &types.MatchingQueryWorkflowRequest{ DomainUUID: domainID, QueryRequest: queryRequest, TaskList: msResp.GetStickyTaskList(), } // using a clean new context in case customer provide a context which has // a really short deadline, causing we clear the stickiness stickyContext, cancel := context.WithTimeout(context.Background(), time.Duration(msResp.GetStickyTaskListScheduleToStartTimeout())*time.Second) stickyStopWatch := scope.StartTimer(metrics.DirectQueryDispatchStickyLatency) matchingResp, err := e.rawMatchingClient.QueryWorkflow(stickyContext, stickyMatchingRequest) stickyStopWatch.Stop() cancel() if err == nil { scope.IncCounter(metrics.DirectQueryDispatchStickySuccessCount) return &types.HistoryQueryWorkflowResponse{Response: &types.QueryWorkflowResponse{QueryResult: matchingResp.GetQueryResult(), QueryRejected: matchingResp.GetQueryRejected()}}, nil } switch v := err.(type) { case *types.StickyWorkerUnavailableError: case *yarpcerrors.Status: if v.Code() != yarpcerrors.CodeDeadlineExceeded { e.logger.Error("query directly though matching on sticky failed, will not attempt query on non-sticky", tag.WorkflowDomainName(queryRequest.GetDomain()), tag.WorkflowID(queryRequest.Execution.GetWorkflowID()), tag.WorkflowRunID(queryRequest.Execution.GetRunID()), tag.WorkflowQueryType(queryRequest.Query.GetQueryType()), tag.Error(err)) return nil, err } default: e.logger.Error("query directly though matching on sticky failed, will not attempt query on non-sticky", tag.WorkflowDomainName(queryRequest.GetDomain()), tag.WorkflowID(queryRequest.Execution.GetWorkflowID()), tag.WorkflowRunID(queryRequest.Execution.GetRunID()), tag.WorkflowQueryType(queryRequest.Query.GetQueryType()), tag.Error(err)) return nil, err } if msResp.GetIsWorkflowRunning() { e.logger.Info("query direct through matching failed on sticky, clearing sticky before attempting on non-sticky", tag.WorkflowDomainName(queryRequest.GetDomain()), tag.WorkflowID(queryRequest.Execution.GetWorkflowID()), tag.WorkflowRunID(queryRequest.Execution.GetRunID()), tag.WorkflowQueryType(queryRequest.Query.GetQueryType()), tag.Error(err)) resetContext, cancel := context.WithTimeout(context.Background(), 5*time.Second) clearStickinessStopWatch := scope.StartTimer(metrics.DirectQueryDispatchClearStickinessLatency) _, err := e.ResetStickyTaskList(resetContext, &types.HistoryResetStickyTaskListRequest{ DomainUUID: domainID, Execution: queryRequest.GetExecution(), }) clearStickinessStopWatch.Stop() cancel() if err != nil && err != workflow.ErrAlreadyCompleted && err != workflow.ErrNotExists { return nil, err } scope.IncCounter(metrics.DirectQueryDispatchClearStickinessSuccessCount) } } if err := common.IsValidContext(ctx); err != nil { e.logger.Info("query context timed out before query on non-sticky task list could be attempted", tag.WorkflowDomainName(queryRequest.GetDomain()), tag.WorkflowID(queryRequest.Execution.GetWorkflowID()), tag.WorkflowRunID(queryRequest.Execution.GetRunID()), tag.WorkflowQueryType(queryRequest.Query.GetQueryType())) scope.IncCounter(metrics.DirectQueryDispatchTimeoutBeforeNonStickyCount) return nil, err } e.logger.Debug("query directly through matching on sticky timed out, attempting to query on non-sticky", tag.WorkflowDomainName(queryRequest.GetDomain()), tag.WorkflowID(queryRequest.Execution.GetWorkflowID()), tag.WorkflowRunID(queryRequest.Execution.GetRunID()), tag.WorkflowQueryType(queryRequest.Query.GetQueryType()), tag.WorkflowTaskListName(msResp.GetStickyTaskList().GetName()), tag.WorkflowNextEventID(msResp.GetNextEventID())) nonStickyMatchingRequest := &types.MatchingQueryWorkflowRequest{ DomainUUID: domainID, QueryRequest: queryRequest, TaskList: msResp.TaskList, } nonStickyStopWatch := scope.StartTimer(metrics.DirectQueryDispatchNonStickyLatency) matchingResp, err := e.matchingClient.QueryWorkflow(ctx, nonStickyMatchingRequest) nonStickyStopWatch.Stop() if err != nil { if strings.Contains(err.Error(), "unknown queryType") { e.logger.Info("user calls for unsupported query type", tag.WorkflowDomainName(queryRequest.GetDomain()), tag.WorkflowID(queryRequest.Execution.GetWorkflowID()), tag.WorkflowRunID(queryRequest.Execution.GetRunID()), tag.WorkflowQueryType(queryRequest.Query.GetQueryType()), tag.Error(err)) } else { e.logger.Error("query directly though matching on non-sticky failed", tag.WorkflowDomainName(queryRequest.GetDomain()), tag.WorkflowID(queryRequest.Execution.GetWorkflowID()), tag.WorkflowRunID(queryRequest.Execution.GetRunID()), tag.WorkflowQueryType(queryRequest.Query.GetQueryType()), tag.Error(err)) } return nil, err } scope.IncCounter(metrics.DirectQueryDispatchNonStickySuccessCount) return &types.HistoryQueryWorkflowResponse{Response: &types.QueryWorkflowResponse{QueryResult: matchingResp.GetQueryResult(), QueryRejected: matchingResp.GetQueryRejected()}}, err } ================================================ FILE: service/history/engine/engineimpl/reapply_events.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/pborman/uuid" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/ndc" "github.com/uber/cadence/service/history/workflow" ) func (e *historyEngineImpl) ReapplyEvents( ctx context.Context, domainUUID string, workflowID string, runID string, reapplyEvents []*types.HistoryEvent, ) error { domainEntry, err := e.getActiveDomainByWorkflow(ctx, domainUUID, workflowID, runID) if err != nil { switch { // TODO(active-active): should we remove this check for active-active domain? // It was introduced in https://github.com/cadence-workflow/cadence/pull/3502 case domainEntry != nil && domainEntry.IsDomainPendingActive(): return nil default: return err } } domainID := domainEntry.GetInfo().ID // remove run id from the execution so that reapply events to the current run currentExecution := types.WorkflowExecution{ WorkflowID: workflowID, } return workflow.UpdateWithActionFunc( ctx, e.logger, e.executionCache, domainID, currentExecution, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) (*workflow.UpdateAction, error) { // Filter out reapply event from the same cluster toReapplyEvents := make([]*types.HistoryEvent, 0, len(reapplyEvents)) lastWriteVersion, err := mutableState.GetLastWriteVersion() if err != nil { return nil, err } for _, event := range reapplyEvents { if event.Version == lastWriteVersion { // The reapply is from the same cluster. Ignoring. continue } dedupResource := definition.NewEventReappliedID(runID, event.ID, event.Version) if mutableState.IsResourceDuplicated(dedupResource) { // already apply the signal continue } toReapplyEvents = append(toReapplyEvents, event) } if len(toReapplyEvents) == 0 { return &workflow.UpdateAction{ Noop: true, }, nil } if !mutableState.IsWorkflowExecutionRunning() { // need to reset target workflow (which is also the current workflow) // to accept events to be reapplied baseRunID := mutableState.GetExecutionInfo().RunID resetRunID := uuid.New() baseRebuildLastEventID := mutableState.GetPreviousStartedEventID() // TODO when https://github.com/uber/cadence/issues/2420 is finished, remove this block, // since cannot reapply event to a finished workflow which had no decisions started if baseRebuildLastEventID == constants.EmptyEventID { e.logger.Warn("cannot reapply event to a finished workflow", tag.WorkflowDomainID(domainID), tag.WorkflowID(currentExecution.GetWorkflowID()), ) e.metricsClient.IncCounter(metrics.HistoryReapplyEventsScope, metrics.EventReapplySkippedCount) return &workflow.UpdateAction{Noop: true}, nil } baseVersionHistories := mutableState.GetVersionHistories() if baseVersionHistories == nil { return nil, execution.ErrMissingVersionHistories } baseCurrentVersionHistory, err := baseVersionHistories.GetCurrentVersionHistory() if err != nil { return nil, err } baseRebuildLastEventVersion, err := baseCurrentVersionHistory.GetEventVersion(baseRebuildLastEventID) if err != nil { return nil, err } baseCurrentBranchToken := baseCurrentVersionHistory.GetBranchToken() baseNextEventID := mutableState.GetNextEventID() if err = e.workflowResetter.ResetWorkflow( ctx, domainID, workflowID, baseRunID, baseCurrentBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, baseNextEventID, resetRunID, uuid.New(), execution.NewWorkflow( ctx, e.shard.GetClusterMetadata(), wfContext, mutableState, execution.NoopReleaseFn, e.logger, ), ndc.EventsReapplicationResetWorkflowReason, toReapplyEvents, false, ); err != nil { return nil, err } return &workflow.UpdateAction{ Noop: true, }, nil } postActions := &workflow.UpdateAction{ CreateDecision: true, } // Do not create decision task when the workflow is cron and the cron has not been started yet if mutableState.GetExecutionInfo().CronSchedule != "" && !mutableState.HasProcessedOrPendingDecision() { postActions.CreateDecision = false } reappliedEvents, err := e.eventsReapplier.ReapplyEvents( ctx, mutableState, toReapplyEvents, runID, ) if err != nil { e.logger.Error("failed to re-apply stale events", tag.Error(err)) return nil, &types.InternalServiceError{Message: "unable to re-apply stale events"} } if len(reappliedEvents) == 0 { return &workflow.UpdateAction{ Noop: true, }, nil } return postActions, nil }, ) } ================================================ FILE: service/history/engine/engineimpl/record_activity_task_started.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) func (e *historyEngineImpl) RecordActivityTaskStarted( ctx context.Context, request *types.RecordActivityTaskStartedRequest, ) (*types.RecordActivityTaskStartedResponse, error) { domainEntry, err := e.getActiveDomainByWorkflow(ctx, request.DomainUUID, request.WorkflowExecution.WorkflowID, request.WorkflowExecution.RunID) if err != nil { return nil, err } domainInfo := domainEntry.GetInfo() domainID := domainInfo.ID domainName := domainInfo.Name workflowExecution := types.WorkflowExecution{ WorkflowID: request.WorkflowExecution.WorkflowID, RunID: request.WorkflowExecution.RunID, } var resurrectError error response := &types.RecordActivityTaskStartedResponse{} err = workflow.UpdateWithAction(ctx, e.logger, e.executionCache, domainID, workflowExecution, false, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return workflow.ErrNotExists } scheduleID := request.GetScheduleID() requestID := request.GetRequestID() ai, isRunning := mutableState.GetActivityInfo(scheduleID) // RecordActivityTaskStarted is already past scheduleToClose timeout. // If at this point pending activity is still in mutable state it may be resurrected. // Otherwise it would be completed or timed out already. if isRunning && e.timeSource.Now().After(ai.ScheduledTime.Add(time.Duration(ai.ScheduleToCloseTimeout)*time.Second)) { resurrectedActivities, err := execution.GetResurrectedActivities(ctx, e.shard, mutableState) if err != nil { e.logger.Error("Activity resurrection check failed", tag.Error(err)) return err } if _, ok := resurrectedActivities[scheduleID]; ok { // found activity resurrection domainName := mutableState.GetDomainEntry().GetInfo().Name e.metricsClient.IncCounter(metrics.HistoryRecordActivityTaskStartedScope, metrics.ActivityResurrectionCounter) e.logger.Error("Encounter resurrected activity, skip", tag.WorkflowDomainName(domainName), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(scheduleID), ) // remove resurrected activity from mutable state if err := mutableState.DeleteActivity(scheduleID); err != nil { return err } // save resurrection error but return nil here, so that mutable state would get updated in DB resurrectError = workflow.ErrActivityTaskNotFound return nil } } // First check to see if cache needs to be refreshed as we could potentially have stale workflow execution in // some extreme cassandra failure cases. if !isRunning && scheduleID >= mutableState.GetNextEventID() { e.metricsClient.IncCounter(metrics.HistoryRecordActivityTaskStartedScope, metrics.StaleMutableStateCounter) e.logger.Error("Encounter stale mutable state in RecordActivityTaskStarted", tag.WorkflowDomainName(domainName), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(scheduleID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) return workflow.ErrStaleState } // Check execution state to make sure task is in the list of outstanding tasks and it is not yet started. If // task is not outstanding than it is most probably a duplicate and complete the task. if !isRunning { // Looks like ActivityTask already completed as a result of another call. // It is OK to drop the task at this point. e.logger.Debug("Potentially duplicate task.", tag.TaskID(request.GetTaskID()), tag.WorkflowScheduleID(scheduleID), tag.TaskType(persistence.TransferTaskTypeActivityTask)) return workflow.ErrActivityTaskNotFound } scheduledEvent, err := mutableState.GetActivityScheduledEvent(ctx, scheduleID) if err != nil { return err } response.ScheduledEvent = scheduledEvent response.ScheduledTimestampOfThisAttempt = common.Int64Ptr(ai.ScheduledTime.UnixNano()) response.Attempt = int64(ai.Attempt) response.HeartbeatDetails = ai.Details response.WorkflowType = mutableState.GetWorkflowType() response.WorkflowDomain = domainName if ai.StartedID != constants.EmptyEventID { // If activity is started as part of the current request scope then return a positive response if ai.RequestID == requestID { response.StartedTimestamp = common.Int64Ptr(ai.StartedTime.UnixNano()) return nil } // Looks like ActivityTask already started as a result of another call. // It is OK to drop the task at this point. e.logger.Debug("Potentially duplicate task.", tag.TaskID(request.GetTaskID()), tag.WorkflowScheduleID(scheduleID), tag.TaskType(persistence.TransferTaskTypeActivityTask)) return &types.EventAlreadyStartedError{Message: "Activity task already started."} } if _, err := mutableState.AddActivityTaskStartedEvent( ai, scheduleID, requestID, request.PollRequest.GetIdentity(), ); err != nil { return err } response.StartedTimestamp = common.Int64Ptr(ai.StartedTime.UnixNano()) return nil }) if err != nil { return nil, err } if resurrectError != nil { return nil, resurrectError } return response, err } ================================================ FILE: service/history/engine/engineimpl/record_child_execution_completed.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) // RecordChildExecutionCompleted records the completion of child execution into parent execution history func (e *historyEngineImpl) RecordChildExecutionCompleted( ctx context.Context, completionRequest *types.RecordChildExecutionCompletedRequest, ) error { domainEntry, err := e.getActiveDomainByWorkflow(ctx, completionRequest.DomainUUID, completionRequest.WorkflowExecution.GetWorkflowID(), completionRequest.WorkflowExecution.GetRunID()) if err != nil { return err } domainID := domainEntry.GetInfo().ID workflowExecution := types.WorkflowExecution{ WorkflowID: completionRequest.WorkflowExecution.GetWorkflowID(), RunID: completionRequest.WorkflowExecution.GetRunID(), } return e.updateWithActionFn(ctx, e.logger, e.executionCache, domainID, workflowExecution, true, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return workflow.ErrNotExists } initiatedID := completionRequest.InitiatedID startedID := completionRequest.StartedID completedExecution := completionRequest.CompletedExecution completionEvent := completionRequest.CompletionEvent // Check mutable state to make sure child execution is in pending child executions ci, isRunning := mutableState.GetChildExecutionInfo(initiatedID) if !isRunning { if initiatedID >= mutableState.GetNextEventID() { e.metricsClient.IncCounter(metrics.HistoryRecordChildExecutionCompletedScope, metrics.StaleMutableStateCounter) e.logger.Error("Encounter stale mutable state in RecordChildExecutionCompleted", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowInitiatedID(initiatedID), tag.WorkflowStartedID(startedID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) return workflow.ErrStaleState } // This can happen if the child execution is already completed, so it's deleted from mutable state // We must return a non-retryable error to avoid infinite retries return &types.EntityNotExistsError{Message: "Pending child execution not found."} } if ci.StartedID == constants.EmptyEventID { // This means that the child execution is initiated but not started in the view of the parent workflow, // but it receives a notification on child workflow completion. // This can happen if the parent workflow data is stale (due to shard movement), some bad guy is spoiling our API, or there is a bug. e.metricsClient.IncCounter(metrics.HistoryRecordChildExecutionCompletedScope, metrics.StaleMutableStateCounter) e.logger.Error("Encounter stale mutable state in RecordChildExecutionCompleted", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowInitiatedID(initiatedID), tag.WorkflowStartedID(startedID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) return workflow.ErrStaleState } if ci.StartedWorkflowID != completedExecution.GetWorkflowID() { return &types.EntityNotExistsError{Message: "Pending child execution workflowID mismatch."} } switch *completionEvent.EventType { case types.EventTypeWorkflowExecutionCompleted: attributes := completionEvent.WorkflowExecutionCompletedEventAttributes _, err = mutableState.AddChildWorkflowExecutionCompletedEvent(initiatedID, completedExecution, attributes) case types.EventTypeWorkflowExecutionFailed: attributes := completionEvent.WorkflowExecutionFailedEventAttributes _, err = mutableState.AddChildWorkflowExecutionFailedEvent(initiatedID, completedExecution, attributes) case types.EventTypeWorkflowExecutionCanceled: attributes := completionEvent.WorkflowExecutionCanceledEventAttributes _, err = mutableState.AddChildWorkflowExecutionCanceledEvent(initiatedID, completedExecution, attributes) case types.EventTypeWorkflowExecutionTerminated: attributes := completionEvent.WorkflowExecutionTerminatedEventAttributes _, err = mutableState.AddChildWorkflowExecutionTerminatedEvent(initiatedID, completedExecution, attributes) case types.EventTypeWorkflowExecutionTimedOut: attributes := completionEvent.WorkflowExecutionTimedOutEventAttributes _, err = mutableState.AddChildWorkflowExecutionTimedOutEvent(initiatedID, completedExecution, attributes) } return err }) } ================================================ FILE: service/history/engine/engineimpl/record_decision_task_started.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common/types" ) // RecordDecisionTaskStarted starts a decision func (e *historyEngineImpl) RecordDecisionTaskStarted(ctx context.Context, request *types.RecordDecisionTaskStartedRequest) (*types.RecordDecisionTaskStartedResponse, error) { return e.decisionHandler.HandleDecisionTaskStarted(ctx, request) } ================================================ FILE: service/history/engine/engineimpl/refresh_workflow_tasks.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" ) func (e *historyEngineImpl) RefreshWorkflowTasks( ctx context.Context, domainUUID string, workflowExecution types.WorkflowExecution, ) (retError error) { domainEntry, err := e.shard.GetDomainCache().GetDomainByID(domainUUID) if err != nil { return err } domainID := domainEntry.GetInfo().ID wfContext, release, err := e.executionCache.GetOrCreateWorkflowExecution(ctx, domainID, workflowExecution) if err != nil { return err } defer func() { release(retError) }() mutableState, err := wfContext.LoadWorkflowExecution(ctx) if err != nil { return err } mutableStateTaskRefresher := execution.NewMutableStateTaskRefresher( e.shard.GetConfig(), e.shard.GetClusterMetadata(), e.shard.GetDomainCache(), e.shard.GetEventsCache(), e.shard.GetShardID(), e.logger, ) err = mutableStateTaskRefresher.RefreshTasks(ctx, mutableState.GetExecutionInfo().StartTimestamp, mutableState) if err != nil { return err } err = wfContext.UpdateWorkflowExecutionTasks(ctx, e.shard.GetTimeSource().Now()) if err != nil { return err } return nil } ================================================ FILE: service/history/engine/engineimpl/refresh_workflow_tasks_test.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "errors" "testing" "github.com/stretchr/testify/mock" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine/testdata" ) func TestRefreshWorkflowTasks(t *testing.T) { tests := []struct { name string execution types.WorkflowExecution getWFExecErr error readHistBranchErr error updateWFExecErr error wantErr bool }{ { name: "runid is not uuid", execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: "not-a-uuid", }, wantErr: true, }, { name: "failed to get workflow execution", getWFExecErr: errors.New("some random error"), execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, wantErr: true, }, { name: "failed to get workflow start event", readHistBranchErr: errors.New("some random error"), execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, wantErr: true, }, { name: "failed to update workflow execution", // returning TimeoutError because it doesn't get retried updateWFExecErr: &persistence.TimeoutError{}, execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, wantErr: true, }, { name: "success", execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, wantErr: false, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) eft.Engine.Start() defer eft.Engine.Stop() // GetWorkflowExecution prep getExecReq := &persistence.GetWorkflowExecutionRequest{ DomainID: constants.TestDomainID, Execution: tc.execution, DomainName: constants.TestDomainName, RangeID: 1, } getExecResp := &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("GetWorkflowExecution", mock.Anything, getExecReq). Return(getExecResp, tc.getWFExecErr). Once() eft.ShardCtx.Resource.ActiveClusterMgr. EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, tc.execution.WorkflowID, tc.execution.RunID). Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil).MaxTimes(1) // ReadHistoryBranch prep historyBranchResp := &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ // first event. { ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, }, } historyMgr := eft.ShardCtx.Resource.HistoryMgr historyMgr. On("ReadHistoryBranch", mock.Anything, mock.Anything). Return(historyBranchResp, tc.readHistBranchErr). Once() // UpdateWorkflowExecution prep var gotUpdateExecReq *persistence.UpdateWorkflowExecutionRequest updateExecResp := &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { var ok bool gotUpdateExecReq, ok = args.Get(1).(*persistence.UpdateWorkflowExecutionRequest) if !ok { t.Fatalf("failed to cast input to *persistence.UpdateWorkflowExecutionRequest, type is %T", args.Get(1)) } }). Return(updateExecResp, tc.updateWFExecErr). Once() // UpdateShard prep. this is needed to update the shard's rangeID for failure cases. eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) // Call RefreshWorkflowTasks err := eft.Engine.RefreshWorkflowTasks( context.Background(), constants.TestDomainID, tc.execution, ) // Error validations if (err != nil) != tc.wantErr { t.Fatalf("RefreshWorkflowTasks() error = %v, wantErr %v", err, tc.wantErr) } if err != nil { return } // UpdateWorkflowExecutionRequest validations if gotUpdateExecReq == nil { t.Fatal("UpdateWorkflowExecutionRequest is nil") } if gotUpdateExecReq.RangeID != 1 { t.Errorf("got RangeID %v, want 1", gotUpdateExecReq.RangeID) } if gotUpdateExecReq.DomainName != constants.TestDomainName { t.Errorf("got DomainName %v, want %v", gotUpdateExecReq.DomainName, constants.TestDomainName) } if gotUpdateExecReq.Mode != persistence.UpdateWorkflowModeIgnoreCurrent { t.Errorf("got Mode %v, want %v", gotUpdateExecReq.Mode, persistence.UpdateWorkflowModeIgnoreCurrent) } if len(gotUpdateExecReq.UpdateWorkflowMutation.TasksByCategory[persistence.HistoryTaskCategoryTimer]) != 2 { t.Errorf("got %v TimerTasks, want 2", len(gotUpdateExecReq.UpdateWorkflowMutation.TasksByCategory[persistence.HistoryTaskCategoryTimer])) } else { timer0, ok := gotUpdateExecReq.UpdateWorkflowMutation.TasksByCategory[persistence.HistoryTaskCategoryTimer][0].(*persistence.WorkflowTimeoutTask) if !ok { t.Fatalf("failed to cast TimerTask[0] to *persistence.WorkflowTimeoutTask, type is %T", timer0) } timer1, ok := gotUpdateExecReq.UpdateWorkflowMutation.TasksByCategory[persistence.HistoryTaskCategoryTimer][1].(*persistence.DecisionTimeoutTask) if !ok { t.Fatalf("failed to cast TimerTask[0] to *persistence.WorkflowTimeoutTask, type is %T", timer1) } } }) } } ================================================ FILE: service/history/engine/engineimpl/register_domain_failover_callback.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "sort" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" hcommon "github.com/uber/cadence/service/history/common" ) func (e *historyEngineImpl) registerDomainFailoverCallback() { // NOTE: READ BEFORE MODIFICATION // // Tasks, e.g. transfer tasks and timer tasks, are created when holding the shard lock // meaning tasks -> release of shard lock // // Domain change notification follows the following steps, order matters // 1. lock all task processing. // 2. domain changes visible to everyone (Note: lock of task processing prevents task processing logic seeing the domain changes). // 3. failover min and max task levels are calculated, then update to shard. // 4. failover start & task processing unlock & shard domain version notification update. (order does not matter for this discussion) // // The above guarantees that task created during the failover will be processed. // If the task is created after domain change: // then active processor will handle it. (simple case) // If the task is created before domain change: // task -> release of shard lock // failover min / max task levels calculated & updated to shard (using shard lock) -> failover start // above 2 guarantees that failover start is after persistence of the task. initialNotificationVersion := e.shard.GetDomainNotificationVersion() catchUpFn := func(domainCache cache.DomainCache, prepareCallback cache.PrepareCallbackFn, callback cache.CallbackFn) { // this section is trying to make the shard catch up with domain changes domains := cache.DomainCacheEntries{} for _, domain := range domainCache.GetAllDomain() { domains = append(domains, domain) } // we must notify the change in an ordered fashion // since history shard has to update the shard info // with the domain change version. sort.Sort(domains) var updatedEntries []*cache.DomainCacheEntry var domainNames []string for _, domain := range domains { if domain.GetNotificationVersion() >= initialNotificationVersion { updatedEntries = append(updatedEntries, domain) domainNames = append(domainNames, domain.GetInfo().Name) } } if len(updatedEntries) > 0 { e.logger.Info("catchUpFn calling prepareCallback and callback", tag.Dynamic("domain-names", domainNames)) prepareCallback() callback(updatedEntries) } } // first set the failover callback e.shard.GetDomainCache().RegisterDomainChangeCallback( createShardNameFromShardID(e.shard.GetShardID()), catchUpFn, e.lockTaskProcessingForDomainUpdate, e.domainChangeCB, ) } func (e *historyEngineImpl) domainChangeCB(nextDomains []*cache.DomainCacheEntry) { defer func() { e.unlockProcessingForDomainUpdate() }() if len(nextDomains) == 0 { return } shardNotificationVersion := e.shard.GetDomainNotificationVersion() failoverActivePassiveDomainIDs := map[string]struct{}{} failoverActiveActiveDomainIDs := map[string]struct{}{} for _, nextDomain := range nextDomains { domainFailoverNotificationVersion := nextDomain.GetFailoverNotificationVersion() domainActiveCluster := nextDomain.GetReplicationConfig().ActiveClusterName if !nextDomain.IsGlobalDomain() { continue } if !nextDomain.GetReplicationConfig().IsActiveActive() && domainFailoverNotificationVersion >= shardNotificationVersion && domainActiveCluster == e.currentClusterName { failoverActivePassiveDomainIDs[nextDomain.GetInfo().ID] = struct{}{} } if nextDomain.GetReplicationConfig().IsActiveActive() && domainFailoverNotificationVersion >= shardNotificationVersion && nextDomain.IsActiveIn(e.currentClusterName) { failoverActiveActiveDomainIDs[nextDomain.GetInfo().ID] = struct{}{} } } if len(failoverActivePassiveDomainIDs) > 0 { e.logger.Info("Domain Failover Start.", tag.WorkflowDomainIDs(failoverActivePassiveDomainIDs)) // Failover queues are not created for active-active domains. Will revisit after new queue framework implementation. for _, processor := range e.queueProcessors { processor.FailoverDomain(failoverActivePassiveDomainIDs) } } if len(failoverActiveActiveDomainIDs) > 0 { e.logger.Info("Active-Active Domain updated", tag.WorkflowDomainIDs(failoverActiveActiveDomainIDs)) } // Notify queues for any domain update. (active-passive and active-active) if len(failoverActivePassiveDomainIDs) > 0 || len(failoverActiveActiveDomainIDs) > 0 { e.notifyQueues() } failoverMarkerTasks := e.generateGracefulFailoverTasksForDomainUpdateCallback(shardNotificationVersion, nextDomains) // This is a debug metric e.metricsClient.IncCounter(metrics.FailoverMarkerScope, metrics.HistoryFailoverCallbackCount) if len(failoverMarkerTasks) > 0 { if err := e.shard.ReplicateFailoverMarkers( context.Background(), failoverMarkerTasks, ); err != nil { e.logger.Error("Failed to insert failover marker to replication queue.", tag.Error(err)) e.metricsClient.IncCounter(metrics.FailoverMarkerScope, metrics.FailoverMarkerInsertFailure) // fail this failover callback and it retries on next domain cache refresh return } } //nolint:errcheck e.shard.UpdateDomainNotificationVersion(nextDomains[len(nextDomains)-1].GetNotificationVersion() + 1) } func (e *historyEngineImpl) notifyQueues() { now := e.shard.GetTimeSource().Now() // the fake tasks will not be actually used, we just need to make sure // its length > 0 and has correct timestamp, to trigger a db scan fakeDecisionTask := []persistence.Task{&persistence.DecisionTask{}} fakeDecisionTimeoutTask := []persistence.Task{&persistence.DecisionTimeoutTask{TaskData: persistence.TaskData{VisibilityTimestamp: now}}} transferProcessor, ok := e.queueProcessors[persistence.HistoryTaskCategoryTransfer] if !ok { e.logger.Error("transfer processor not found") return } transferProcessor.NotifyNewTask(e.currentClusterName, &hcommon.NotifyTaskInfo{Tasks: fakeDecisionTask}) timerProcessor, ok := e.queueProcessors[persistence.HistoryTaskCategoryTimer] if !ok { e.logger.Error("timer processor not found") return } timerProcessor.NotifyNewTask(e.currentClusterName, &hcommon.NotifyTaskInfo{Tasks: fakeDecisionTimeoutTask}) } func (e *historyEngineImpl) generateGracefulFailoverTasksForDomainUpdateCallback(shardNotificationVersion int64, nextDomains []*cache.DomainCacheEntry) []*persistence.FailoverMarkerTask { // handle graceful failover on active to passive // make sure task processor failover the domain before inserting the failover marker failoverMarkerTasks := []*persistence.FailoverMarkerTask{} for _, nextDomain := range nextDomains { if nextDomain.GetReplicationConfig().IsActiveActive() { // Currently it's unclear whether graceful failover is working for active-passive domains. We don't use it in practice. // Don't try to make it work for active-active domains until we determine we need it. // We may potentially retire existing graceful failover implementation and provide "sync replication" instead. continue } domainFailoverNotificationVersion := nextDomain.GetFailoverNotificationVersion() domainActiveCluster := nextDomain.GetReplicationConfig().ActiveClusterName previousFailoverVersion := nextDomain.GetPreviousFailoverVersion() previousClusterName, err := e.clusterMetadata.ClusterNameForFailoverVersion(previousFailoverVersion) if err != nil && previousFailoverVersion != constants.InitialPreviousFailoverVersion { e.logger.Error("Failed to handle graceful failover", tag.WorkflowDomainID(nextDomain.GetInfo().ID), tag.Error(err)) continue } if nextDomain.IsGlobalDomain() && domainFailoverNotificationVersion >= shardNotificationVersion && domainActiveCluster != e.currentClusterName && previousFailoverVersion != constants.InitialPreviousFailoverVersion && previousClusterName == e.currentClusterName { // the visibility timestamp will be set in shard context failoverMarkerTasks = append(failoverMarkerTasks, &persistence.FailoverMarkerTask{ TaskData: persistence.TaskData{ Version: nextDomain.GetFailoverVersion(), }, DomainID: nextDomain.GetInfo().ID, }) // This is a debug metric e.metricsClient.IncCounter(metrics.FailoverMarkerScope, metrics.FailoverMarkerCallbackCount) } } return failoverMarkerTasks } func (e *historyEngineImpl) lockTaskProcessingForDomainUpdate() { e.logger.Debug("Locking processing for domain update") for _, processor := range e.queueProcessors { processor.LockTaskProcessing() } } func (e *historyEngineImpl) unlockProcessingForDomainUpdate() { e.logger.Debug("Unlocking processing for failover") for _, processor := range e.queueProcessors { processor.UnlockTaskProcessing() } } func (e *historyEngineImpl) failoverPredicate(shardNotificationVersion int64, nextDomain *cache.DomainCacheEntry, action func()) { domainFailoverNotificationVersion := nextDomain.GetFailoverNotificationVersion() domainActiveCluster := nextDomain.GetReplicationConfig().ActiveClusterName if nextDomain.IsGlobalDomain() && !nextDomain.GetReplicationConfig().IsActiveActive() && domainFailoverNotificationVersion >= shardNotificationVersion && domainActiveCluster == e.currentClusterName { action() } } ================================================ FILE: service/history/engine/engineimpl/register_domain_failover_callback_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package engineimpl import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) func TestGenerateFailoverTasksForDomainCallback(t *testing.T) { domainInfo := persistence.DomainInfo{ ID: "83b48dab-68cb-4f73-8752-c75d9271977f", Name: "samples-domain", OwnerEmail: "test4@test.com", Data: map[string]string{}, } domainConfig := persistence.DomainConfig{ Retention: 3, EmitMetric: true, HistoryArchivalURI: "file:///tmp/cadence_archival/development", VisibilityArchivalURI: "file:///tmp/cadence_vis_archival/development", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, } replicationConfig := persistence.DomainReplicationConfig{ ActiveClusterName: "cluster0", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "cluster0"}, {ClusterName: "cluster1"}, {ClusterName: "cluster2"}, }, } t1 := cache.NewDomainCacheEntryForTest(&domainInfo, &domainConfig, true, &replicationConfig, 1, nil, 2, constants.InitialPreviousFailoverVersion, 15, ) t2 := cache.NewDomainCacheEntryForTest(&domainInfo, &domainConfig, true, &replicationConfig, 10, nil, 3, constants.InitialPreviousFailoverVersion, 15, ) t3 := cache.NewDomainCacheEntryForTest(&domainInfo, &domainConfig, true, &persistence.DomainReplicationConfig{ ActiveClusterName: "cluster1", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "cluster0"}, {ClusterName: "cluster1"}, {ClusterName: "cluster2"}, }, }, 10, common.Ptr(time.Now().Add(time.Second).UnixNano()), 4, 1, 15, ) nonFailoverDomainUpdateT1 := []*cache.DomainCacheEntry{ t1, } failoverUpdateT2 := []*cache.DomainCacheEntry{ t2, } failoverUpdateT3 := []*cache.DomainCacheEntry{ t3, } tests := map[string]struct { domainUpdates []*cache.DomainCacheEntry currentNotificationVersion int64 expectedRes []*persistence.FailoverMarkerTask expectedErr error }{ "non-failover domain update. This should generate no tasks as there's no failover taking place": { domainUpdates: nonFailoverDomainUpdateT1, currentNotificationVersion: 1, expectedRes: []*persistence.FailoverMarkerTask{}, }, "non-graceful failover domain update. This should generate no tasks as there's no failover taking place": { domainUpdates: failoverUpdateT2, currentNotificationVersion: 2, expectedRes: []*persistence.FailoverMarkerTask{ // no failoverMarker tasks are created for non-graceful failovers, they just occur immediately }, }, "graceful failover domain update. This should generate failover marker tasks": { domainUpdates: failoverUpdateT3, currentNotificationVersion: 3, expectedRes: []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{ Version: 10, TaskID: 0, VisibilityTimestamp: time.Time{}, }, DomainID: "83b48dab-68cb-4f73-8752-c75d9271977f", }, }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { cluster := cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 10, PrimaryClusterName: "cluster0", CurrentClusterName: "cluster0", ClusterGroup: map[string]config.ClusterInformation{ "cluster0": config.ClusterInformation{ Enabled: true, InitialFailoverVersion: 1, }, "cluster1": config.ClusterInformation{ Enabled: true, InitialFailoverVersion: 0, }, "cluster2": config.ClusterInformation{ Enabled: true, InitialFailoverVersion: 2, }, }, }, func(string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), ) he := historyEngineImpl{ logger: log.NewNoop(), clusterMetadata: cluster, currentClusterName: "cluster0", metricsClient: metrics.NewNoopMetricsClient(), } res := he.generateGracefulFailoverTasksForDomainUpdateCallback(td.currentNotificationVersion, td.domainUpdates) assert.Equal(t, td.expectedRes, res) }) } } func TestDomainCallback(t *testing.T) { // This is a reference set of data from a series of steps, showing a data dump // from cluster0 of the domain failover values, counters and guards and how they work: // ---- Initial starting position -- active in cluster0 // // Cluster0 has initialFailoverVersion of 1 // Cluster1 has initialFailoverVersion of 0 // Cluster2 has initialFailoverVersion of 2 // failover increments are 10 // ----------- t0 - initial starting position - the domain is initially active in cluster0 // domains_partition | 0 // name | samples-domain // config | {retention: 3, emit_metric: True, archival_bucket: '', archival_status: 0, bad_binaries: 0x590d000a0b0c0000000000, bad_binaries_encoding: 'thriftrw', history_archival_status: 1, history_archival_uri: 'file:///tmp/cadence_archival/development', visibility_archival_status: 1, visibility_archival_uri: 'file:///tmp/cadence_vis_archival/development', isolation_groups: 0x5900, isolation_groups_encoding: 'thriftrw', async_workflow_config: 0x5902000a000b0014000000000b001e0000000000, async_workflow_config_encoding: 'thriftrw'} // config_version | 0 // domain | {id: ad1002f2-f87d-4128-afce-6d320c8871e7, name: 'samples-domain', status: 0, description: '', owner_email: '', data: null} // failover_end_time | 0 // failover_notification_version | 0 // failover_version | 1 // is_global_domain | True // last_updated_time | 1729031237843277000 // notification_version | 1 // previous_failover_version | -1 // replication_config | {active_cluster_name: 'cluster0', clusters: [{cluster_name: 'cluster0'}, {cluster_name: 'cluster1'}, {cluster_name: 'cluster2'}]} // ------------ t1 domain update - no failover // domains_partition | 0 // name | samples-domain // config | {retention: 3, emit_metric: True, archival_bucket: '', archival_status: 0, bad_binaries: 0x590d000a0b0c0000000000, bad_binaries_encoding: 'thriftrw', history_archival_status: 1, history_archival_uri: 'file:///tmp/cadence_archival/development', visibility_archival_status: 1, visibility_archival_uri: 'file:///tmp/cadence_vis_archival/development', isolation_groups: 0x5900, isolation_groups_encoding: 'thriftrw', async_workflow_config: 0x5902000a000b0014000000000b001e0000000000, async_workflow_config_encoding: 'thriftrw'} // config_version | 1 // domain | {id: ad1002f2-f87d-4128-afce-6d320c8871e7, name: 'samples-domain', status: 0, description: '', owner_email: 'some-email@email.com', data: {}} // failover_end_time | 0 // failover_notification_version | 0 // failover_version | 1 // is_global_domain | True // last_updated_time | 1729031406189428000 // notification_version | 2 // previous_failover_version | -1 // replication_config | {active_cluster_name: 'cluster0', clusters: [{cluster_name: 'cluster0'}, {cluster_name: 'cluster1'}, {cluster_name: 'cluster2'}]} // ------------- t2 Failover to cluster2 - non-graceful // domains_partition | 0 // name | samples-domain // config | {retention: 3, emit_metric: True, archival_bucket: '', archival_status: 0, bad_binaries: 0x590d000a0b0c0000000000, bad_binaries_encoding: 'thriftrw', history_archival_status: 1, history_archival_uri: 'file:///tmp/cadence_archival/development', visibility_archival_status: 1, visibility_archival_uri: 'file:///tmp/cadence_vis_archival/development', isolation_groups: 0x5900, isolation_groups_encoding: 'thriftrw', async_workflow_config: 0x5902000a000b0014000000000b001e0000000000, async_workflow_config_encoding: 'thriftrw'} // config_version | 1 // domain | {id: ad1002f2-f87d-4128-afce-6d320c8871e7, name: 'samples-domain', status: 0, description: '', owner_email: 'some-email@email.com', data: {'FailoverHistory': '[{"eventTime":"2024-10-15T15:31:52.533009-07:00","fromCluster":"cluster0","toCluster":"cluster2","failoverType":"Force"}]'}} // failover_end_time | 0 // failover_notification_version | 3 // failover_version | 2 // is_global_domain | True // last_updated_time | 1729031512533009000 // notification_version | 3 // previous_failover_version | -1 // replication_config | {active_cluster_name: 'cluster2', clusters: [{cluster_name: 'cluster0'}, {cluster_name: 'cluster1'}, {cluster_name: 'cluster2'}]} // ------------ t3 Failback to cluster0 - graceful // domains_partition | 0 // name | samples-domain // config | {retention: 3, emit_metric: True, archival_bucket: '', archival_status: 0, bad_binaries: 0x590d000a0b0c0000000000, bad_binaries_encoding: 'thriftrw', history_archival_status: 1, history_archival_uri: 'file:///tmp/cadence_archival/development', visibility_archival_status: 1, visibility_archival_uri: 'file:///tmp/cadence_vis_archival/development', isolation_groups: 0x5900, isolation_groups_encoding: 'thriftrw', async_workflow_config: 0x5902000a000b0014000000000b001e0000000000, async_workflow_config_encoding: 'thriftrw'} // config_version | 1 // domain | {id: ad1002f2-f87d-4128-afce-6d320c8871e7, name: 'samples-domain', status: 0, description: '', owner_email: 'some-email@email.com', data: {'FailoverHistory': '[{"eventTime":"2024-10-15T15:33:39.83016-07:00","fromCluster":"cluster2","toCluster":"cluster0","failoverType":"Grace"},{"eventTime":"2024-10-15T15:31:52.533009-07:00","fromCluster":"cluster0","toCluster":"cluster2","failoverType":"Force"}]'}} // failover_end_time | 0 // failover_notification_version | 4 // failover_version | 11 // is_global_domain | True // last_updated_time | 0 // notification_version | 5 // previous_failover_version | 0 // replication_config | {active_cluster_name: 'cluster0', clusters: [{cluster_name: 'cluster0'}, {cluster_name: 'cluster1'}, {cluster_name: 'cluster2'}]} // ------------ t4 Failback to cluster2 - graceful // domains_partition | 0 // name | samples-domain // config | {retention: 3, emit_metric: True, archival_bucket: '', archival_status: 0, bad_binaries: 0x590d000a0b0c0000000000, bad_binaries_encoding: 'thriftrw', history_archival_status: 1, history_archival_uri: 'file:///tmp/cadence_archival/development', visibility_archival_status: 1, visibility_archival_uri: 'file:///tmp/cadence_vis_archival/development', isolation_groups: 0x5900, isolation_groups_encoding: 'thriftrw', async_workflow_config: 0x5902000a000b0014000000000b001e0000000000, async_workflow_config_encoding: 'thriftrw'} // config_version | 1 // domain | {id: ad1002f2-f87d-4128-afce-6d320c8871e7, name: 'samples-domain', status: 0, description: '', owner_email: 'some-email@email.com', data: {'FailoverHistory': '[{"eventTime":"2024-10-15T15:47:56.967632-07:00","fromCluster":"cluster0","toCluster":"cluster2","failoverType":"Grace"}]'}} // failover_end_time | 0 // failover_notification_version | 5 // failover_version | 12 // is_global_domain | True // last_updated_time | 0 // notification_version | 6 // previous_failover_version | 0 // replication_config | {active_cluster_name: 'cluster2', clusters: [{cluster_name: 'cluster0'}, {cluster_name: 'cluster1'}, {cluster_name: 'cluster2'}]} clusters := []*persistence.ClusterReplicationConfig{ {ClusterName: "cluster0"}, {ClusterName: "cluster1"}, {ClusterName: "cluster2"}, } domainInfo := persistence.DomainInfo{ ID: "83b48dab-68cb-4f73-8752-c75d9271977f", Name: "samples-domain", OwnerEmail: "test4@test.com", Data: map[string]string{}, } domainConfig := persistence.DomainConfig{ Retention: 3, EmitMetric: true, HistoryArchivalURI: "file:///tmp/cadence_archival/development", VisibilityArchivalURI: "file:///tmp/cadence_vis_archival/development", BadBinaries: types.BadBinaries{Binaries: map[string]*types.BadBinaryInfo{}}, IsolationGroups: types.IsolationGroupConfiguration{}, } replicationConfig := persistence.DomainReplicationConfig{ ActiveClusterName: "cluster0", Clusters: clusters, } // failover_version | 1 // is_global_domain | True // last_updated_time | 1729031237843277000 // notification_version | 1 // previous_failover_version | -1 // A domain update, no failover though // failover_end_time | 0 // failover_notification_version | 0 // failover_version | 1 // is_global_domain | True // last_updated_time | 1729031406189428000 // notification_version | 2 // previous_failover_version | -1 t1 := cache.NewDomainCacheEntryForTest(&domainInfo, &domainConfig, true, &replicationConfig, 1, nil, 0, constants.InitialPreviousFailoverVersion, 2, ) // failover to cluster2 // failover_end_time | 0 // failover_notification_version | 3 // failover_version | 2 // is_global_domain | True // last_updated_time | 1729031512533009000 // notification_version | 3 // previous_failover_version | -1 t2 := cache.NewDomainCacheEntryForTest(&domainInfo, &domainConfig, true, &persistence.DomainReplicationConfig{ ActiveClusterName: "cluster2", Clusters: clusters, }, 2, nil, 3, constants.InitialPreviousFailoverVersion, 3, ) // Graceful failover to cluster0 again (remember cluster0 is initialFailover version 1) // failover_end_time | 0 // failover_notification_version | 4 // failover_version | 11 // is_global_domain | True // last_updated_time | 0 // notification_version | 5 // previous_failover_version | 0 t3 := cache.NewDomainCacheEntryForTest(&domainInfo, &domainConfig, true, &persistence.DomainReplicationConfig{ ActiveClusterName: "cluster0", Clusters: clusters, }, 11, common.Ptr(time.Now().Add(time.Second).UnixNano()), 4, 0, 5, // this is 4 on all the other clusters, but on cluster0 its 5 because // graceful does two events it seems to increment it twice just locally ) nonFailoverDomainUpdateT1 := []*cache.DomainCacheEntry{ t1, } failoverUpdateT2 := []*cache.DomainCacheEntry{ t2, } failoverUpdateT3 := []*cache.DomainCacheEntry{ t3, } failoverUpdateActiveActive := []*cache.DomainCacheEntry{ cache.NewDomainCacheEntryForTest(&domainInfo, &domainConfig, true, &persistence.DomainReplicationConfig{ Clusters: clusters, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region0": { ActiveClusterName: "cluster0", FailoverVersion: 1, }, "region1": { ActiveClusterName: "cluster1", FailoverVersion: 2, }, }, }, }, }, }, -1, nil, 4, 5, 5, ), } invalidDomainUpdate := []*cache.DomainCacheEntry{ cache.NewDomainCacheEntryForTest(&domainInfo, &domainConfig, true, &persistence.DomainReplicationConfig{ ActiveClusterName: "cluster0", Clusters: clusters, }, 11, common.Ptr(time.Now().Add(time.Second).UnixNano()), 4, 5, // not a valid initial failover vrsion 5, ), } timeSource := clock.NewMockedTimeSource() tests := map[string]struct { domainUpdates []*cache.DomainCacheEntry asCluster string affordances func( shardCtx *shard.MockContext, txProcessor *queue.MockProcessor, timerProcessor *queue.MockProcessor, taskProcessor *task.MockProcessor, ) expectedErr error }{ "t1 - non-failover domain update. - from the point of view of cluster 0": { domainUpdates: nonFailoverDomainUpdateT1, asCluster: "cluster0", affordances: func( shardCtx *shard.MockContext, txProcessor *queue.MockProcessor, timerProcessor *queue.MockProcessor, taskProcessor *task.MockProcessor) { shardCtx.EXPECT().GetDomainNotificationVersion().Return(int64(1)) shardCtx.EXPECT().UpdateDomainNotificationVersion(int64(3)) txProcessor.EXPECT().UnlockTaskProcessing() timerProcessor.EXPECT().UnlockTaskProcessing() }, }, "t1 -> t2: non-graceful failover domain update. cluster0 POV": { domainUpdates: failoverUpdateT2, asCluster: "cluster0", affordances: func( shardCtx *shard.MockContext, txProcessor *queue.MockProcessor, timerProcessor *queue.MockProcessor, taskProcessor *task.MockProcessor) { shardCtx.EXPECT().GetDomainNotificationVersion().Return(int64(1)) shardCtx.EXPECT().UpdateDomainNotificationVersion(int64(4)) txProcessor.EXPECT().UnlockTaskProcessing() timerProcessor.EXPECT().UnlockTaskProcessing() }, }, "t1 -> t2: non-graceful failover domain update. - cluster2 POV": { domainUpdates: failoverUpdateT2, asCluster: "cluster2", affordances: func( shardCtx *shard.MockContext, txProcessor *queue.MockProcessor, timerProcessor *queue.MockProcessor, taskProcessor *task.MockProcessor) { shardCtx.EXPECT().GetDomainNotificationVersion().Return(int64(1)) shardCtx.EXPECT().GetTimeSource().Return(timeSource) shardCtx.EXPECT().UpdateDomainNotificationVersion(int64(4)) txProcessor.EXPECT().UnlockTaskProcessing() txProcessor.EXPECT().FailoverDomain(gomock.Any()) txProcessor.EXPECT().NotifyNewTask("cluster2", gomock.Any()) timerProcessor.EXPECT().UnlockTaskProcessing() timerProcessor.EXPECT().FailoverDomain(gomock.Any()) timerProcessor.EXPECT().NotifyNewTask("cluster2", gomock.Any()) }, }, "t2 -> t3: graceful failover domain update. cluster0 POV": { domainUpdates: failoverUpdateT3, asCluster: "cluster0", affordances: func( shardCtx *shard.MockContext, txProcessor *queue.MockProcessor, timerProcessor *queue.MockProcessor, taskProcessor *task.MockProcessor) { shardCtx.EXPECT().GetDomainNotificationVersion().Return(int64(1)) shardCtx.EXPECT().UpdateDomainNotificationVersion(int64(6)) shardCtx.EXPECT().GetTimeSource().Return(timeSource) txProcessor.EXPECT().UnlockTaskProcessing() txProcessor.EXPECT().FailoverDomain(map[string]struct{}{"83b48dab-68cb-4f73-8752-c75d9271977f": struct{}{}}) txProcessor.EXPECT().NotifyNewTask("cluster0", gomock.Any()) timerProcessor.EXPECT().UnlockTaskProcessing() timerProcessor.EXPECT().FailoverDomain(map[string]struct{}{"83b48dab-68cb-4f73-8752-c75d9271977f": struct{}{}}) timerProcessor.EXPECT().NotifyNewTask("cluster0", gomock.Any()) }, }, "t2 -> t3: graceful failover domain update. - cluster2 POV": { domainUpdates: failoverUpdateT3, asCluster: "cluster2", affordances: func( shardCtx *shard.MockContext, txProcessor *queue.MockProcessor, timerProcessor *queue.MockProcessor, taskProcessor *task.MockProcessor) { shardCtx.EXPECT().GetDomainNotificationVersion().Return(int64(1)) shardCtx.EXPECT().UpdateDomainNotificationVersion(int64(6)) txProcessor.EXPECT().UnlockTaskProcessing() timerProcessor.EXPECT().UnlockTaskProcessing() }, }, "graceful failover domain update. cluster1 POV": { domainUpdates: failoverUpdateT3, asCluster: "cluster1", affordances: func( shardCtx *shard.MockContext, txProcessor *queue.MockProcessor, timerProcessor *queue.MockProcessor, taskProcessor *task.MockProcessor) { shardCtx.EXPECT().GetDomainNotificationVersion().Return(int64(1)) shardCtx.EXPECT().UpdateDomainNotificationVersion(int64(6)) shardCtx.EXPECT().ReplicateFailoverMarkers(gomock.Any(), []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{ Version: 11, }, DomainID: "83b48dab-68cb-4f73-8752-c75d9271977f", }, }) txProcessor.EXPECT().UnlockTaskProcessing() timerProcessor.EXPECT().UnlockTaskProcessing() }, }, "graceful failover domain update. cluster1 POV - error on replication": { domainUpdates: failoverUpdateT3, asCluster: "cluster1", affordances: func( shardCtx *shard.MockContext, txProcessor *queue.MockProcessor, timerProcessor *queue.MockProcessor, taskProcessor *task.MockProcessor) { shardCtx.EXPECT().GetDomainNotificationVersion().Return(int64(1)) shardCtx.EXPECT().ReplicateFailoverMarkers(gomock.Any(), []*persistence.FailoverMarkerTask{ { TaskData: persistence.TaskData{ Version: 11, }, DomainID: "83b48dab-68cb-4f73-8752-c75d9271977f", }, }).Return(assert.AnError) txProcessor.EXPECT().UnlockTaskProcessing() timerProcessor.EXPECT().UnlockTaskProcessing() }, }, "invalid failover version": { domainUpdates: invalidDomainUpdate, asCluster: "cluster1", affordances: func( shardCtx *shard.MockContext, txProcessor *queue.MockProcessor, timerProcessor *queue.MockProcessor, taskProcessor *task.MockProcessor) { shardCtx.EXPECT().GetDomainNotificationVersion().Return(int64(1)) shardCtx.EXPECT().UpdateDomainNotificationVersion(int64(6)) txProcessor.EXPECT().UnlockTaskProcessing() timerProcessor.EXPECT().UnlockTaskProcessing() }, }, "active active domain failover update. cluster0 POV": { domainUpdates: failoverUpdateActiveActive, asCluster: "cluster0", affordances: func( shardCtx *shard.MockContext, txProcessor *queue.MockProcessor, timerProcessor *queue.MockProcessor, taskProcessor *task.MockProcessor) { shardCtx.EXPECT().GetDomainNotificationVersion().Return(int64(1)) shardCtx.EXPECT().GetTimeSource().Return(timeSource) shardCtx.EXPECT().UpdateDomainNotificationVersion(int64(6)) txProcessor.EXPECT().UnlockTaskProcessing() txProcessor.EXPECT().NotifyNewTask("cluster0", gomock.Any()) timerProcessor.EXPECT().UnlockTaskProcessing() timerProcessor.EXPECT().NotifyNewTask("cluster0", gomock.Any()) }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { cluster := cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 10, PrimaryClusterName: "cluster0", CurrentClusterName: "cluster0", ClusterGroup: map[string]config.ClusterInformation{ "cluster0": config.ClusterInformation{ Enabled: true, InitialFailoverVersion: 1, }, "cluster1": config.ClusterInformation{ Enabled: true, InitialFailoverVersion: 0, }, "cluster2": config.ClusterInformation{ Enabled: true, InitialFailoverVersion: 2, }, }, }, func(string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), ) ctrl := gomock.NewController(t) timeProcessor := queue.NewMockProcessor(ctrl) queueTaskProcessor := task.NewMockProcessor(ctrl) txProcessor := queue.NewMockProcessor(ctrl) shardCtx := shard.NewMockContext(ctrl) td.affordances(shardCtx, txProcessor, timeProcessor, queueTaskProcessor) he := historyEngineImpl{ logger: log.NewNoop(), clusterMetadata: cluster, currentClusterName: td.asCluster, metricsClient: metrics.NewNoopMetricsClient(), queueProcessors: map[persistence.HistoryTaskCategory]queue.Processor{ persistence.HistoryTaskCategoryTransfer: txProcessor, persistence.HistoryTaskCategoryTimer: timeProcessor, }, shard: shardCtx, } he.domainChangeCB(td.domainUpdates) }) } } func TestDomainLocking(t *testing.T) { cluster := cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 10, PrimaryClusterName: "cluster0", CurrentClusterName: "cluster0", ClusterGroup: map[string]config.ClusterInformation{ "cluster0": config.ClusterInformation{ Enabled: true, InitialFailoverVersion: 1, }, }, }, func(string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), ) ctrl := gomock.NewController(t) timeProcessor := queue.NewMockProcessor(ctrl) txProcessor := queue.NewMockProcessor(ctrl) shardCtx := shard.NewMockContext(ctrl) timeProcessor.EXPECT().LockTaskProcessing() timeProcessor.EXPECT().UnlockTaskProcessing() txProcessor.EXPECT().LockTaskProcessing() txProcessor.EXPECT().UnlockTaskProcessing() he := historyEngineImpl{ logger: log.NewNoop(), clusterMetadata: cluster, currentClusterName: "cluster0", metricsClient: metrics.NewNoopMetricsClient(), queueProcessors: map[persistence.HistoryTaskCategory]queue.Processor{ persistence.HistoryTaskCategoryTransfer: txProcessor, persistence.HistoryTaskCategoryTimer: timeProcessor, }, shard: shardCtx, } he.lockTaskProcessingForDomainUpdate() he.unlockProcessingForDomainUpdate() } func TestHistoryEngine_registerDomainFailoverCallback_ClosureBehavior(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockShard := shard.NewMockContext(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) // Define the initial shard notification version initialShardVersion := int64(5) mockShard.EXPECT().GetDomainNotificationVersion().Return(initialShardVersion) mockShard.EXPECT().GetShardID().Return(456).Times(1) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) // Capture the registered catchUpFn var registeredCatchUpFn func(cache.DomainCache, cache.PrepareCallbackFn, cache.CallbackFn) mockDomainCache.EXPECT().RegisterDomainChangeCallback( createShardNameFromShardID(456), // id of the callback gomock.Any(), // catchUpFn gomock.Any(), // lockTaskProcessingForDomainUpdate gomock.Any(), // domainChangeCB ).Do(func(_ string, catchUpFn, _, _ interface{}) { if fn, ok := catchUpFn.(cache.CatchUpFn); ok { registeredCatchUpFn = fn } else { t.Fatalf("Failed to convert catchUpFn to cache.CatchUpFn: got type %T", catchUpFn) } }).Times(1) cluster := cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 10, PrimaryClusterName: "cluster0", CurrentClusterName: "cluster0", ClusterGroup: map[string]config.ClusterInformation{ "cluster0": config.ClusterInformation{ Enabled: true, InitialFailoverVersion: 1, }, "cluster1": config.ClusterInformation{ Enabled: true, InitialFailoverVersion: 0, }, "cluster2": config.ClusterInformation{ Enabled: true, InitialFailoverVersion: 2, }, }, }, func(string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), ) // Create the history engine instance engine := &historyEngineImpl{ shard: mockShard, logger: log.NewNoop(), clusterMetadata: cluster, // Or a specific mock if needed currentClusterName: "cluster0", // Or a specific value metricsClient: metrics.NewNoopMetricsClient(), } // Call the method under test to register the callback engine.registerDomainFailoverCallback() // --- Test the behavior of the registered catchUpFn --- t.Run("catchUpFn - No updated domains", func(t *testing.T) { mockDomainCache.EXPECT().GetAllDomain().Return(map[string]*cache.DomainCacheEntry{ "uuid-domain1": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "uuid-domain1", Name: "domain1"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "A"}, 0, nil, 0, 0, 1, ), "uuid-domain2": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "uuid-domain2", Name: "domain2"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "A"}, 0, nil, 0, 0, 4, ), }) prepareCalled := false callbackCalled := false prepare := func() { prepareCalled = true } callback := func([]*cache.DomainCacheEntry) { callbackCalled = true } if registeredCatchUpFn != nil { registeredCatchUpFn(mockDomainCache, prepare, callback) assert.False(t, prepareCalled, "prepareCallback should not be called") assert.False(t, callbackCalled, "callback should not be called") } else { assert.Fail(t, "catchUpFn was not registered") } }) t.Run("catchUpFn - Some updated domains", func(t *testing.T) { mockDomainCache.EXPECT().GetAllDomain().Return(map[string]*cache.DomainCacheEntry{ "uuid-domain1": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "uuid-domain1", Name: "domain1"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "A"}, 0, nil, 0, 0, 7, ), "uuid-domain2": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "uuid-domain2", Name: "domain2"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "A"}, 0, nil, 0, 0, 4, ), }) prepareCalled := false callbackCalled := false var actualUpdatedEntries []*cache.DomainCacheEntry prepare := func() { prepareCalled = true } callback := func(entries []*cache.DomainCacheEntry) { callbackCalled = true actualUpdatedEntries = entries } if registeredCatchUpFn != nil { registeredCatchUpFn(mockDomainCache, prepare, callback) assert.True(t, prepareCalled, "prepareCallback should be called") assert.True(t, callbackCalled, "callback should be called") assert.Len(t, actualUpdatedEntries, 1, "should have one updated entry") assert.Equal(t, int64(7), actualUpdatedEntries[0].GetNotificationVersion()) } else { assert.Fail(t, "catchUpFn was not registered") } }) t.Run("catchUpFn - All domains should be updated", func(t *testing.T) { mockDomainCache.EXPECT().GetAllDomain().Return(map[string]*cache.DomainCacheEntry{ "uuid-domain1": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "uuid-domain1", Name: "domain1"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "A"}, 0, nil, 0, 0, 7, ), "uuid-domain2": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "uuid-domain2", Name: "domain2"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "A"}, 0, nil, 0, 0, 5, ), }) prepareCalled := false callbackCalled := false var actualUpdatedEntries []*cache.DomainCacheEntry prepare := func() { prepareCalled = true } callback := func(entries []*cache.DomainCacheEntry) { callbackCalled = true actualUpdatedEntries = entries } if registeredCatchUpFn != nil { registeredCatchUpFn(mockDomainCache, prepare, callback) assert.True(t, prepareCalled, "prepareCallback should be called") assert.True(t, callbackCalled, "callback should be called") assert.Len(t, actualUpdatedEntries, 2, "should have two updated entries") assert.Equal(t, int64(5), actualUpdatedEntries[0].GetNotificationVersion()) assert.Equal(t, int64(7), actualUpdatedEntries[1].GetNotificationVersion()) } else { assert.Fail(t, "catchUpFn was not registered") } }) t.Run("catchUpFn - Empty domain cache", func(t *testing.T) { mockDomainCache.EXPECT().GetAllDomain().Return(map[string]*cache.DomainCacheEntry{}) prepareCalled := false callbackCalled := false prepare := func() { prepareCalled = true } callback := func([]*cache.DomainCacheEntry) { callbackCalled = true } if registeredCatchUpFn != nil { registeredCatchUpFn(mockDomainCache, prepare, callback) assert.False(t, prepareCalled, "prepareCallback should not be called") assert.False(t, callbackCalled, "callback should not be called") } else { assert.Fail(t, "catchUpFn was not registered") } }) } ================================================ FILE: service/history/engine/engineimpl/remove_signal_mutable_state.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) // RemoveSignalMutableState remove the signal request id in signal_requested for deduplicate func (e *historyEngineImpl) RemoveSignalMutableState( ctx context.Context, request *types.RemoveSignalMutableStateRequest, ) error { domainEntry, err := e.getActiveDomainByWorkflow(ctx, request.DomainUUID, request.WorkflowExecution.GetWorkflowID(), request.WorkflowExecution.GetRunID()) if err != nil { return err } domainID := domainEntry.GetInfo().ID workflowExecution := types.WorkflowExecution{ WorkflowID: request.WorkflowExecution.WorkflowID, RunID: request.WorkflowExecution.RunID, } return workflow.UpdateWithAction(ctx, e.logger, e.executionCache, domainID, workflowExecution, false, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return workflow.ErrNotExists } mutableState.DeleteSignalRequested(request.GetRequestID()) return nil }) } ================================================ FILE: service/history/engine/engineimpl/request_cancel_workflow_execution.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) // RequestCancelWorkflowExecution records request cancellation event for workflow execution func (e *historyEngineImpl) RequestCancelWorkflowExecution( ctx context.Context, req *types.HistoryRequestCancelWorkflowExecutionRequest, ) error { request := req.CancelRequest parentExecution := req.ExternalWorkflowExecution childWorkflowOnly := req.GetChildWorkflowOnly() workflowExecution := types.WorkflowExecution{ WorkflowID: request.WorkflowExecution.WorkflowID, } // If firstExecutionRunID is set on the request always try to cancel currently running execution if request.GetFirstExecutionRunID() == "" { workflowExecution.RunID = request.WorkflowExecution.RunID } domainEntry, err := e.getActiveDomainByWorkflow(ctx, req.DomainUUID, workflowExecution.WorkflowID, workflowExecution.RunID) if err != nil { return err } domainID := domainEntry.GetInfo().ID return workflow.UpdateCurrentWithActionFunc(ctx, e.logger, e.executionCache, e.executionManager, domainID, e.shard.GetDomainCache(), workflowExecution, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) (*workflow.UpdateAction, error) { isCancelRequested, cancelRequestID := mutableState.IsCancelRequested() if !mutableState.IsWorkflowExecutionRunning() { _, closeStatus := mutableState.GetWorkflowStateCloseStatus() if isCancelRequested && closeStatus == persistence.WorkflowCloseStatusCanceled { cancelRequest := req.CancelRequest if cancelRequest.RequestID != "" && cancelRequest.RequestID == cancelRequestID { return &workflow.UpdateAction{Noop: true}, nil } } return nil, workflow.ErrAlreadyCompleted } executionInfo := mutableState.GetExecutionInfo() if request.GetFirstExecutionRunID() != "" { firstRunID := executionInfo.FirstExecutionRunID if firstRunID == "" { // This is needed for backwards compatibility. Workflow execution create with Cadence release v0.25.0 or earlier // does not have FirstExecutionRunID stored as part of mutable state. If this is not set then load it from // workflow execution started event. startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return nil, err } firstRunID = startEvent.GetWorkflowExecutionStartedEventAttributes().GetFirstExecutionRunID() } if request.GetFirstExecutionRunID() != firstRunID { return nil, &types.EntityNotExistsError{Message: "Workflow execution not found"} } } if childWorkflowOnly { parentWorkflowID := executionInfo.ParentWorkflowID parentRunID := executionInfo.ParentRunID if parentExecution.GetWorkflowID() != parentWorkflowID || parentExecution.GetRunID() != parentRunID { return nil, workflow.ErrParentMismatch } } if isCancelRequested { cancelRequest := req.CancelRequest if cancelRequest.RequestID != "" && cancelRequest.RequestID == cancelRequestID { return workflow.UpdateWithNewDecision, nil } // if we consider workflow cancellation idempotent, then this error is redundant // this error maybe useful if this API is invoked by external, not decision from transfer queue return nil, workflow.ErrCancellationAlreadyRequested } if _, err := mutableState.AddWorkflowExecutionCancelRequestedEvent(req.CancelRequest.Cause, req); err != nil { return nil, &types.InternalServiceError{Message: "Unable to cancel workflow execution."} } return workflow.UpdateWithNewDecision, nil }) } ================================================ FILE: service/history/engine/engineimpl/reset_queues.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "fmt" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/queue" ) func (e *historyEngineImpl) ResetTransferQueue( ctx context.Context, clusterName string, ) error { transferProcessor, ok := e.queueProcessors[persistence.HistoryTaskCategoryTransfer] if !ok { return fmt.Errorf("transfer processor not found") } _, err := transferProcessor.HandleAction(ctx, clusterName, queue.NewResetAction()) return err } func (e *historyEngineImpl) ResetTimerQueue( ctx context.Context, clusterName string, ) error { timerProcessor, ok := e.queueProcessors[persistence.HistoryTaskCategoryTimer] if !ok { return fmt.Errorf("timer processor not found") } _, err := timerProcessor.HandleAction(ctx, clusterName, queue.NewResetAction()) return err } ================================================ FILE: service/history/engine/engineimpl/reset_sticky_tasklist.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) // ResetStickyTaskList reset the volatile information in mutable state of a given types. // Volatile information are the information related to client, such as: // 1. StickyTaskList // 2. StickyScheduleToStartTimeout // 3. ClientLibraryVersion // 4. ClientFeatureVersion // 5. ClientImpl func (e *historyEngineImpl) ResetStickyTaskList( ctx context.Context, resetRequest *types.HistoryResetStickyTaskListRequest, ) (*types.HistoryResetStickyTaskListResponse, error) { if err := common.ValidateDomainUUID(resetRequest.DomainUUID); err != nil { return nil, err } domainID := resetRequest.DomainUUID err := workflow.UpdateWithAction(ctx, e.logger, e.executionCache, domainID, *resetRequest.Execution, false, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return workflow.ErrAlreadyCompleted } mutableState.ClearStickyness() return nil }, ) if err != nil { return nil, err } return &types.HistoryResetStickyTaskListResponse{}, nil } ================================================ FILE: service/history/engine/engineimpl/reset_sticky_tasklist_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package engineimpl import ( ctx "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine/testdata" "github.com/uber/cadence/service/history/workflow" ) func TestResetStickyTaskList(t *testing.T) { execution := &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } cases := []struct { name string request *types.HistoryResetStickyTaskListRequest init func(engine *testdata.EngineForTest) assertions func(engine *testdata.EngineForTest) expectedErr error }{ { name: "Invalid Domain", request: &types.HistoryResetStickyTaskListRequest{ DomainUUID: "", Execution: execution, }, expectedErr: &types.BadRequestError{Message: "Missing domain UUID."}, }, { name: "Completed Workflow", request: &types.HistoryResetStickyTaskListRequest{ DomainUUID: constants.TestDomainID, Execution: execution, }, init: func(engine *testdata.EngineForTest) { engine.ShardCtx.Resource.ExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.GetWorkflowExecutionRequest) bool { return req.Execution == *execution })).Return(&persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: execution.WorkflowID, RunID: execution.RunID, State: persistence.WorkflowStateCompleted, }, ExecutionStats: &persistence.ExecutionStats{}, Checksum: checksum.Checksum{}, }, }, nil) engine.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, execution.WorkflowID, execution.RunID). Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil).Times(1) }, expectedErr: workflow.ErrAlreadyCompleted, }, { name: "Success", request: &types.HistoryResetStickyTaskListRequest{ DomainUUID: constants.TestDomainID, Execution: execution, }, init: func(engine *testdata.EngineForTest) { engine.ShardCtx.Resource.ExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.GetWorkflowExecutionRequest) bool { return req.Execution == *execution })).Return(&persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: execution.WorkflowID, RunID: execution.RunID, StickyTaskList: "CLEAR ME PLEASE", }, ExecutionStats: &persistence.ExecutionStats{}, Checksum: checksum.Checksum{}, }, }, nil) engine.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, execution.WorkflowID, execution.RunID). Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil).Times(1) engine.ShardCtx.Resource.ExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.UpdateWorkflowExecutionRequest) bool { return req.UpdateWorkflowMutation.ExecutionInfo.WorkflowID == execution.WorkflowID && req.UpdateWorkflowMutation.ExecutionInfo.RunID == execution.RunID && req.UpdateWorkflowMutation.ExecutionInfo.StickyTaskList == "" })).Return(&persistence.UpdateWorkflowExecutionResponse{}, nil) }, }, } for _, testCase := range cases { t.Run(testCase.name, func(t *testing.T) { eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) if testCase.init != nil { testCase.init(eft) } eft.Engine.Start() result, err := eft.Engine.ResetStickyTaskList(ctx.Background(), testCase.request) if testCase.assertions != nil { testCase.assertions(eft) } eft.Engine.Stop() if testCase.expectedErr == nil { assert.NoError(t, err) assert.Equal(t, &types.HistoryResetStickyTaskListResponse{}, result) } else { assert.Equal(t, testCase.expectedErr, err) assert.Nil(t, result) } }) } } ================================================ FILE: service/history/engine/engineimpl/reset_workflow_execution.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "fmt" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" persistenceutils "github.com/uber/cadence/common/persistence/persistence-utils" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" ) const ( DefaultPageSize = 100 ) func (e *historyEngineImpl) ResetWorkflowExecution( ctx context.Context, resetRequest *types.HistoryResetWorkflowExecutionRequest, ) (response *types.ResetWorkflowExecutionResponse, retError error) { request := resetRequest.ResetRequest domainID := resetRequest.GetDomainUUID() workflowID := request.WorkflowExecution.GetWorkflowID() baseRunID := request.WorkflowExecution.GetRunID() baseContext, baseReleaseFn, err := e.executionCache.GetOrCreateWorkflowExecution( ctx, domainID, types.WorkflowExecution{ WorkflowID: workflowID, RunID: baseRunID, }, ) if err != nil { return nil, err } defer func() { baseReleaseFn(retError) }() baseMutableState, err := baseContext.LoadWorkflowExecution(ctx) if err != nil { return nil, err } if ok := baseMutableState.HasProcessedOrPendingDecision(); !ok { return nil, &types.BadRequestError{ Message: "Cannot reset workflow without a decision task schedule.", } } if request.GetDecisionFinishEventID() <= constants.FirstEventID || request.GetDecisionFinishEventID() > baseMutableState.GetNextEventID() { return nil, &types.BadRequestError{ Message: "Decision finish ID must be > 1 && <= workflow next event ID.", } } domainName, err := e.shard.GetDomainCache().GetDomainName(domainID) if err != nil { return nil, err } // also load the current run of the workflow, it can be different from the base runID resp, err := e.executionManager.GetCurrentExecution(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: request.WorkflowExecution.GetWorkflowID(), DomainName: domainName, }) if err != nil { return nil, err } currentRunID := resp.RunID var currentContext execution.Context var currentMutableState execution.MutableState var currentReleaseFn execution.ReleaseFunc if currentRunID == baseRunID { currentContext = baseContext currentMutableState = baseMutableState } else { currentContext, currentReleaseFn, err = e.executionCache.GetOrCreateWorkflowExecution( ctx, domainID, types.WorkflowExecution{ WorkflowID: workflowID, RunID: currentRunID, }, ) if err != nil { return nil, err } defer func() { currentReleaseFn(retError) }() currentMutableState, err = currentContext.LoadWorkflowExecution(ctx) if err != nil { return nil, err } } // dedup by requestID if currentMutableState.GetExecutionInfo().CreateRequestID == request.GetRequestID() { e.logger.Info("Duplicated reset request", tag.WorkflowID(workflowID), tag.WorkflowRunID(currentRunID), tag.WorkflowDomainID(domainID)) return &types.ResetWorkflowExecutionResponse{ RunID: currentRunID, }, nil } resetRunID := uuid.New() baseRebuildLastEventID := request.GetDecisionFinishEventID() - 1 baseVersionHistories := baseMutableState.GetVersionHistories() baseCurrentBranchToken, err := baseMutableState.GetCurrentBranchToken() if err != nil { return nil, err } baseRebuildLastEventVersion := baseMutableState.GetCurrentVersion() baseNextEventID := baseMutableState.GetNextEventID() if baseVersionHistories != nil { baseCurrentVersionHistory, err := baseVersionHistories.GetCurrentVersionHistory() if err != nil { return nil, err } baseRebuildLastEventVersion, err = baseCurrentVersionHistory.GetEventVersion(baseRebuildLastEventID) if err != nil { return nil, err } baseCurrentBranchToken = baseCurrentVersionHistory.GetBranchToken() } // for reset workflow execution requests, the caller provides the decision finish event ID. // must validate the event ID to ensure it is a valid reset point err = e.validateResetPointForResetWorkflowExecutionRequest(ctx, request, baseCurrentBranchToken, domainID) if err != nil { return nil, err } if err := e.workflowResetter.ResetWorkflow( ctx, domainID, workflowID, baseRunID, baseCurrentBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, baseNextEventID, resetRunID, request.GetRequestID(), execution.NewWorkflow( ctx, e.shard.GetClusterMetadata(), currentContext, currentMutableState, currentReleaseFn, e.logger, ), request.GetReason(), nil, request.GetSkipSignalReapply(), ); err != nil { if t, ok := persistence.AsDuplicateRequestError(err); ok { if t.RequestType == persistence.WorkflowRequestTypeReset { return &types.ResetWorkflowExecutionResponse{ RunID: t.RunID, }, nil } e.logger.Error("A bug is detected for idempotency improvement", tag.Dynamic("request-type", t.RequestType)) return nil, t } return nil, err } return &types.ResetWorkflowExecutionResponse{ RunID: resetRunID, }, nil } func (e *historyEngineImpl) validateResetPointForResetWorkflowExecutionRequest( ctx context.Context, request *types.ResetWorkflowExecutionRequest, baseCurrentBranchToken []byte, domainID string, ) error { iter := collection.NewPagingIterator(e.getPaginationFn( ctx, constants.FirstEventID, request.GetDecisionFinishEventID()+1, baseCurrentBranchToken, domainID, )) if !iter.HasNext() { return fmt.Errorf("workflow has corrupted or missing history") } var events []*types.HistoryEvent for iter.HasNext() { batch, err := iter.Next() if err != nil { return err } events = batch.(*types.History).Events // get the batch of events that contains the reset event and exit the loop if events[len(events)-1].ID >= request.GetDecisionFinishEventID() { break } } return checkResetEventType(events, request.GetDecisionFinishEventID()) } func (e *historyEngineImpl) getPaginationFn( ctx context.Context, firstEventID int64, nextEventID int64, branchToken []byte, domainID string, ) collection.PaginationFn { return func(paginationToken []byte) ([]interface{}, []byte, error) { _, historyBatches, token, _, err := persistenceutils.PaginateHistory( ctx, e.historyV2Mgr, true, branchToken, firstEventID, nextEventID, paginationToken, DefaultPageSize, common.IntPtr(e.shard.GetShardID()), domainID, e.shard.GetDomainCache(), ) if err != nil { return nil, nil, err } var paginateItems []interface{} for _, history := range historyBatches { paginateItems = append(paginateItems, history) } return paginateItems, token, nil } } // checkResetEventType checks the type of the reset event and only allows specific types to be resettable func checkResetEventType(events []*types.HistoryEvent, resetEventID int64) error { for _, event := range events { if event.ID == resetEventID { switch *event.EventType { case types.EventTypeDecisionTaskStarted: return nil case types.EventTypeDecisionTaskTimedOut: return nil case types.EventTypeDecisionTaskFailed: return nil case types.EventTypeDecisionTaskCompleted: return nil default: return &types.BadRequestError{ Message: fmt.Sprintf("reset event must be of type DecisionTaskStarted, DecisionTaskTimedOut, DecisionTaskFailed, or DecisionTaskCompleted. Attempting to reset on event type: %v", event.EventType.String()), } } } } return fmt.Errorf("reset event ID %v not found in the history events", resetEventID) } ================================================ FILE: service/history/engine/engineimpl/reset_workflow_execution_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package engineimpl import ( ctx "context" "fmt" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine/testdata" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/reset" ) var ( testRequestID = "this is a test request" testRequestReason = "Test reason" testRequestSkipSignalReapply = true latestRunID = constants.TestRunID latestExecution = &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: latestRunID} previousRunID = "bbbbbeef-0123-4567-890a-bcdef0123456" previousExecution = &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: previousRunID} version = int64(12) branchToken = []byte("other random branch token") partitionConfig = map[string]string{ "userid": uuid.New(), } firstEventID = commonconstants.FirstEventID workflowEvents = []*types.HistoryEvent{ { ID: 1, Version: version, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: "some random workflow type"}, TaskList: &types.TaskList{Name: "some random workflow type"}, Input: []byte("some random input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(123), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(233), Identity: "some random identity", PartitionConfig: partitionConfig, }, }, { ID: 2, Version: version, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, }, { ID: 3, Version: version, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 2, }, }, { ID: 4, Version: version, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 2, StartedEventID: 3, }, }, { ID: 5, Version: version, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityID: "1", }, }, { ID: 6, Version: version, EventType: types.EventTypeActivityTaskStarted.Ptr(), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 5, }, }, { ID: 7, Version: version, EventType: types.EventTypeActivityTaskCompleted.Ptr(), ActivityTaskCompletedEventAttributes: &types.ActivityTaskCompletedEventAttributes{ ScheduledEventID: 5, StartedEventID: 6, }, }, { ID: 8, Version: version, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, }, { ID: 9, Version: version, EventType: types.EventTypeDecisionTaskTimedOut.Ptr(), DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: 8, }, }, { ID: 10, Version: version, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: "some random workflow type"}, }, }, { ID: 11, Version: version, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 10, }, }, { ID: 12, Version: version, EventType: types.EventTypeDecisionTaskFailed.Ptr(), DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ ScheduledEventID: 10, StartedEventID: 11, }, }, { ID: 13, Version: version, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, }, { ID: 14, Version: version, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 13, }, }, { ID: 15, Version: version, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 13, StartedEventID: 14, }, }, { ID: 16, Version: version, EventType: types.EventTypeTimerStarted.Ptr(), TimerStartedEventAttributes: &types.TimerStartedEventAttributes{}, }, { ID: 17, Version: version, EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{}, }, { ID: 18, Version: version, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{}, }, { ID: 19, Version: version, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{}, }, { ID: 20, Version: version, EventType: types.EventTypeActivityTaskStarted.Ptr(), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 19, }, }, { ID: 21, Version: version, EventType: types.EventTypeActivityTaskFailed.Ptr(), ActivityTaskCompletedEventAttributes: &types.ActivityTaskCompletedEventAttributes{ ScheduledEventID: 19, StartedEventID: 20, }, }, { ID: 22, Version: version, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, }, { ID: 23, Version: version, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: 22, }, }, { ID: 24, Version: version, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 22, StartedEventID: 23, }, }, { ID: 25, Version: version, EventType: types.EventTypeWorkflowExecutionCompleted.Ptr(), WorkflowExecutionCompletedEventAttributes: &types.WorkflowExecutionCompletedEventAttributes{}, }, } history = []testHistoryEvents{ { Events: workflowEvents[:2], Size: 1, }, { Events: workflowEvents[2:3], Size: 2, }, { Events: workflowEvents[3:6], Size: 3, }, { Events: workflowEvents[6:8], Size: 4, }, { Events: workflowEvents[8:10], Size: 5, }, { Events: workflowEvents[10:11], Size: 6, }, { Events: workflowEvents[11:13], Size: 7, }, { Events: workflowEvents[13:14], Size: 8, }, { Events: workflowEvents[14:16], Size: 9, }, { Events: workflowEvents[16:19], Size: 10, }, { Events: workflowEvents[19:22], Size: 11, }, { Events: workflowEvents[22:23], Size: 12, }, { Events: workflowEvents[23:24], Size: 13, }, { Events: workflowEvents[24:25], Size: 14, }, } ) type ( InitFn func(t *testing.T, engine *testdata.EngineForTest) testHistoryEvents struct { Events []*types.HistoryEvent Size int } ) func TestResetWorkflowExecution(t *testing.T) { cases := []struct { name string request *types.HistoryResetWorkflowExecutionRequest init []InitFn expectedErr error expected *types.ResetWorkflowExecutionResponse }{ { name: "No processed or pending decision", request: resetExecutionRequest(latestExecution, 100), init: []InitFn{ withCurrentExecution(latestExecution), withState(latestExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, DecisionScheduleID: commonconstants.EmptyEventID, LastProcessedEvent: commonconstants.EmptyEventID, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 1}, }), withActiveClusterInfo(constants.TestDomainID, latestExecution, &types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}), }, expectedErr: &types.BadRequestError{Message: "Cannot reset workflow without a decision task schedule."}, }, { name: "Invalid DecisionFinishEventId", request: resetExecutionRequest(latestExecution, 100), init: []InitFn{ withCurrentExecution(latestExecution), withState(latestExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, NextEventID: 26, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 1}, }), withActiveClusterInfo(constants.TestDomainID, latestExecution, &types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}), }, expectedErr: &types.BadRequestError{Message: "Decision finish ID must be > 1 && <= workflow next event ID."}, }, { name: "Duplicate Request", request: resetExecutionRequest(latestExecution, 24), init: []InitFn{ withCurrentExecution(latestExecution), withState(latestExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, NextEventID: 26, CreateRequestID: testRequestID, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 1}, }), withActiveClusterInfo(constants.TestDomainID, latestExecution, &types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}), }, expected: &types.ResetWorkflowExecutionResponse{ RunID: latestRunID, }, }, { name: "Success", request: resetExecutionRequest(latestExecution, 24), init: []InitFn{ withCurrentExecution(latestExecution), withState(latestExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, NextEventID: 26, BranchToken: branchToken, }, ReplicationState: &persistence.ReplicationState{ CurrentVersion: version, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 1}, }), withActiveClusterInfo(constants.TestDomainID, latestExecution, &types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}), withHistoryPagination(branchToken, 24), func(t *testing.T, engine *testdata.EngineForTest) { ctrl := gomock.NewController(t) mockResetter := reset.NewMockWorkflowResetter(ctrl) engine.Engine.(*historyEngineImpl).workflowResetter = mockResetter mockResetter.EXPECT().ResetWorkflow( gomock.Any(), // Context gomock.Eq(constants.TestDomainID), gomock.Eq(constants.TestWorkflowID), gomock.Eq(latestExecution.RunID), gomock.Eq(branchToken), gomock.Eq(int64(24)-1), // Request.DecisionFinishEventID - 1 gomock.Eq(version), // CurrentVersion gomock.Eq(int64(26)), // NextEventID gomock.Any(), // random uuid gomock.Eq(testRequestID), &workflowMatcher{latestExecution}, gomock.Eq(testRequestReason), gomock.Nil(), gomock.Eq(testRequestSkipSignalReapply), ).Return(nil).Times(1) }, }, // Can't assert on the result because the runID is random }, { name: "Success using version histories started in current cluster", // This corresponds to VersionHistories.Histories.Items.EventID request: resetExecutionRequest(latestExecution, 24), init: []InitFn{ withCurrentExecution(latestExecution), withState(latestExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, NextEventID: 26, BranchToken: branchToken, }, VersionHistories: &persistence.VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("some other branch token"), }, { BranchToken: branchToken, Items: []*persistence.VersionHistoryItem{ { EventID: 1, Version: 1000, // current cluster }, { EventID: 23, Version: 1001, }, { EventID: 24, Version: 1002, }, }, }, }, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 1}, }), withActiveClusterInfo(constants.TestDomainID, latestExecution, &types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}), withHistoryPagination(branchToken, 24), func(t *testing.T, engine *testdata.EngineForTest) { ctrl := gomock.NewController(t) mockResetter := reset.NewMockWorkflowResetter(ctrl) engine.Engine.(*historyEngineImpl).workflowResetter = mockResetter mockResetter.EXPECT().ResetWorkflow( gomock.Any(), // Context gomock.Eq(constants.TestDomainID), gomock.Eq(constants.TestWorkflowID), gomock.Eq(latestExecution.RunID), gomock.Eq(branchToken), //VersionHistories.Histories.BranchToken gomock.Eq(int64(23)), // Request.DecisionFinishEventID - 1 gomock.Eq(int64(1001)), // VersionHistories.Histories.Items.Version gomock.Eq(int64(26)), // NextEventID gomock.Any(), // random uuid gomock.Eq(testRequestID), &workflowMatcher{latestExecution}, gomock.Eq(testRequestReason), gomock.Nil(), gomock.Eq(testRequestSkipSignalReapply), ).Return(nil).Times(1) }, }, // Can't assert on the result because the runID is random }, { name: "Success using previous version", request: resetExecutionRequest(previousExecution, 24), init: []InitFn{ withCurrentExecution(latestExecution), withState(latestExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, }, ReplicationState: &persistence.ReplicationState{ CurrentVersion: 1, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 1}, }), withActiveClusterInfo(constants.TestDomainID, latestExecution, &types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}), withState(previousExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, NextEventID: 26, BranchToken: branchToken, }, ReplicationState: &persistence.ReplicationState{ CurrentVersion: version, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 1}, }), withActiveClusterInfo(constants.TestDomainID, latestExecution, &types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}), withHistoryPagination(branchToken, 24), func(t *testing.T, engine *testdata.EngineForTest) { ctrl := gomock.NewController(t) mockResetter := reset.NewMockWorkflowResetter(ctrl) engine.Engine.(*historyEngineImpl).workflowResetter = mockResetter mockResetter.EXPECT().ResetWorkflow( gomock.Any(), // Context gomock.Eq(constants.TestDomainID), gomock.Eq(constants.TestWorkflowID), gomock.Eq(previousExecution.RunID), gomock.Eq(branchToken), gomock.Eq(int64(23)), // Request.DecisionFinishEventID - 1 gomock.Eq(version), // CurrentVersion gomock.Eq(int64(26)), // NextEventID gomock.Any(), // random uuid gomock.Eq(testRequestID), &workflowMatcher{latestExecution}, gomock.Eq(testRequestReason), gomock.Nil(), gomock.Eq(testRequestSkipSignalReapply), ).Return(nil).Times(1) }, }, // Can't assert on the result because the runID is random }, { name: "Persistence Duplicate Request", request: resetExecutionRequest(latestExecution, 24), init: []InitFn{ withCurrentExecution(latestExecution), withState(latestExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, NextEventID: 26, BranchToken: branchToken, }, ReplicationState: &persistence.ReplicationState{ CurrentVersion: version, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 1}, }), withActiveClusterInfo(constants.TestDomainID, latestExecution, &types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}), withHistoryPagination(branchToken, 24), func(t *testing.T, engine *testdata.EngineForTest) { ctrl := gomock.NewController(t) mockResetter := reset.NewMockWorkflowResetter(ctrl) engine.Engine.(*historyEngineImpl).workflowResetter = mockResetter mockResetter.EXPECT().ResetWorkflow( gomock.Any(), // Context gomock.Eq(constants.TestDomainID), gomock.Eq(constants.TestWorkflowID), gomock.Eq(latestExecution.RunID), gomock.Eq(branchToken), gomock.Eq(int64(23)), // Request.DecisionFinishEventID - 1 gomock.Eq(version), // CurrentVersion gomock.Eq(int64(26)), // NextEventID gomock.Any(), // random uuid gomock.Eq(testRequestID), &workflowMatcher{latestExecution}, gomock.Eq(testRequestReason), gomock.Nil(), gomock.Eq(testRequestSkipSignalReapply), ).Return(&persistence.DuplicateRequestError{ RequestType: persistence.WorkflowRequestTypeReset, RunID: "errorID", }).Times(1) }, }, expected: &types.ResetWorkflowExecutionResponse{ RunID: "errorID", }, }, { name: "Persistence Duplicate Request Bug", request: resetExecutionRequest(latestExecution, 24), init: []InitFn{ withCurrentExecution(latestExecution), withState(latestExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, NextEventID: 26, BranchToken: branchToken, }, ReplicationState: &persistence.ReplicationState{ CurrentVersion: version, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 1}, }), withActiveClusterInfo(constants.TestDomainID, latestExecution, &types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}), withHistoryPagination(branchToken, 24), func(t *testing.T, engine *testdata.EngineForTest) { ctrl := gomock.NewController(t) mockResetter := reset.NewMockWorkflowResetter(ctrl) engine.Engine.(*historyEngineImpl).workflowResetter = mockResetter mockResetter.EXPECT().ResetWorkflow( gomock.Any(), // Context gomock.Eq(constants.TestDomainID), gomock.Eq(constants.TestWorkflowID), gomock.Eq(latestExecution.RunID), gomock.Eq(branchToken), gomock.Eq(int64(23)), // Request.DecisionFinishEventID - 1 gomock.Eq(version), // CurrentVersion gomock.Eq(int64(26)), // NextEventID gomock.Any(), // random uuid gomock.Eq(testRequestID), &workflowMatcher{latestExecution}, gomock.Eq(testRequestReason), gomock.Nil(), gomock.Eq(testRequestSkipSignalReapply), ).Return(&persistence.DuplicateRequestError{ RequestType: persistence.WorkflowRequestTypeStart, RunID: "errorID", }).Times(1) }, }, expectedErr: &persistence.DuplicateRequestError{ RequestType: persistence.WorkflowRequestTypeStart, RunID: "errorID", }, }, { name: "Reset returns Err", request: resetExecutionRequest(latestExecution, 24), init: []InitFn{ withCurrentExecution(latestExecution), withState(latestExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, NextEventID: 26, BranchToken: branchToken, }, ReplicationState: &persistence.ReplicationState{ CurrentVersion: version, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 1}, }), withActiveClusterInfo(constants.TestDomainID, latestExecution, &types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}), withHistoryPagination(branchToken, 24), func(t *testing.T, engine *testdata.EngineForTest) { ctrl := gomock.NewController(t) mockResetter := reset.NewMockWorkflowResetter(ctrl) engine.Engine.(*historyEngineImpl).workflowResetter = mockResetter mockResetter.EXPECT().ResetWorkflow( gomock.Any(), // Context gomock.Eq(constants.TestDomainID), gomock.Eq(constants.TestWorkflowID), gomock.Eq(latestExecution.RunID), gomock.Eq(branchToken), gomock.Eq(int64(23)), // Request.DecisionFinishEventID - 1 gomock.Eq(version), // CurrentVersion gomock.Eq(int64(26)), // NextEventID gomock.Any(), // random uuid gomock.Eq(testRequestID), &workflowMatcher{latestExecution}, gomock.Eq(testRequestReason), gomock.Nil(), gomock.Eq(testRequestSkipSignalReapply), ).Return(&types.BadRequestError{ Message: "didn't work", }).Times(1) }, }, expectedErr: &types.BadRequestError{ Message: "didn't work", }, }, } for _, testCase := range cases { t.Run(testCase.name, func(t *testing.T) { eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) for _, setup := range testCase.init { setup(t, eft) } eft.Engine.Start() result, err := eft.Engine.ResetWorkflowExecution(ctx.Background(), testCase.request) eft.Engine.Stop() if testCase.expectedErr == nil { if assert.NotNil(t, result) { assert.NotEmpty(t, result.RunID) } assert.NoError(t, err) } else { assert.Nil(t, result) assert.Equal(t, testCase.expectedErr, err) } }) } } func TestResetWorkflowExecution_ResetPointsValidation(t *testing.T) { testCases := []struct { name string resetEventID int64 setupMock func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) err error }{ { name: "error - reset on decision task scheduled", resetEventID: 10, setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) {}, err: &types.BadRequestError{ Message: fmt.Sprintf("reset event must be of type DecisionTaskStarted, DecisionTaskTimedOut, DecisionTaskFailed, or DecisionTaskCompleted. Attempting to reset on event type: %v", workflowEvents[9].EventType.String()), }, }, { name: "error - reset on activity task scheduled", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) {}, resetEventID: 5, err: &types.BadRequestError{ Message: fmt.Sprintf("reset event must be of type DecisionTaskStarted, DecisionTaskTimedOut, DecisionTaskFailed, or DecisionTaskCompleted. Attempting to reset on event type: %v", workflowEvents[4].EventType.String()), }, }, { name: "error - reset on activity task started", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) {}, resetEventID: 6, err: &types.BadRequestError{ Message: fmt.Sprintf("reset event must be of type DecisionTaskStarted, DecisionTaskTimedOut, DecisionTaskFailed, or DecisionTaskCompleted. Attempting to reset on event type: %v", workflowEvents[5].EventType.String()), }, }, { name: "error - reset on activity task completed", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) {}, resetEventID: 7, err: &types.BadRequestError{ Message: fmt.Sprintf("reset event must be of type DecisionTaskStarted, DecisionTaskTimedOut, DecisionTaskFailed, or DecisionTaskCompleted. Attempting to reset on event type: %v", workflowEvents[6].EventType.String()), }, }, { name: "error - reset on timer started", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) {}, resetEventID: 16, err: &types.BadRequestError{ Message: fmt.Sprintf("reset event must be of type DecisionTaskStarted, DecisionTaskTimedOut, DecisionTaskFailed, or DecisionTaskCompleted. Attempting to reset on event type: %v", workflowEvents[15].EventType.String()), }, }, { name: "error - reset on timer fired", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) {}, resetEventID: 17, err: &types.BadRequestError{ Message: fmt.Sprintf("reset event must be of type DecisionTaskStarted, DecisionTaskTimedOut, DecisionTaskFailed, or DecisionTaskCompleted. Attempting to reset on event type: %v", workflowEvents[16].EventType.String()), }, }, { name: "error - reset on workflow execution signaled", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) {}, resetEventID: 18, err: &types.BadRequestError{ Message: fmt.Sprintf("reset event must be of type DecisionTaskStarted, DecisionTaskTimedOut, DecisionTaskFailed, or DecisionTaskCompleted. Attempting to reset on event type: %v", workflowEvents[17].EventType.String()), }, }, { name: "error - reset on activity task failed", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) {}, resetEventID: 21, err: &types.BadRequestError{ Message: fmt.Sprintf("reset event must be of type DecisionTaskStarted, DecisionTaskTimedOut, DecisionTaskFailed, or DecisionTaskCompleted. Attempting to reset on event type: %v", workflowEvents[20].EventType.String()), }, }, { name: "error - reset on workflow execution completed", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) {}, resetEventID: 25, err: &types.BadRequestError{ Message: fmt.Sprintf("reset event must be of type DecisionTaskStarted, DecisionTaskTimedOut, DecisionTaskFailed, or DecisionTaskCompleted. Attempting to reset on event type: %v", workflowEvents[24].EventType.String()), }, }, { name: "success - reset on decision task started", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) { mockResetter.EXPECT().ResetWorkflow( gomock.Any(), // Context gomock.Eq(constants.TestDomainID), gomock.Eq(constants.TestWorkflowID), gomock.Eq(latestExecution.RunID), gomock.Eq(branchToken), gomock.Eq(resetEventID-1), // Request.DecisionFinishEventID - 1 gomock.Eq(version), // CurrentVersion gomock.Eq(int64(26)), // NextEventID gomock.Any(), // random uuid gomock.Eq(testRequestID), &workflowMatcher{latestExecution}, gomock.Eq(testRequestReason), gomock.Nil(), gomock.Eq(testRequestSkipSignalReapply), ).Return(nil).Times(1) }, resetEventID: 23, err: nil, }, { name: "success - reset on decision task timed out", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) { mockResetter.EXPECT().ResetWorkflow( gomock.Any(), // Context gomock.Eq(constants.TestDomainID), gomock.Eq(constants.TestWorkflowID), gomock.Eq(latestExecution.RunID), gomock.Eq(branchToken), gomock.Eq(resetEventID-1), // Request.DecisionFinishEventID - 1 gomock.Eq(version), // CurrentVersion gomock.Eq(int64(26)), // NextEventID gomock.Any(), // random uuid gomock.Eq(testRequestID), &workflowMatcher{latestExecution}, gomock.Eq(testRequestReason), gomock.Nil(), gomock.Eq(testRequestSkipSignalReapply), ).Return(nil).Times(1) }, resetEventID: 9, err: nil, }, { name: "success - reset on decision task failed", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) { mockResetter.EXPECT().ResetWorkflow( gomock.Any(), // Context gomock.Eq(constants.TestDomainID), gomock.Eq(constants.TestWorkflowID), gomock.Eq(latestExecution.RunID), gomock.Eq(branchToken), gomock.Eq(resetEventID-1), // Request.DecisionFinishEventID - 1 gomock.Eq(version), // CurrentVersion gomock.Eq(int64(26)), // NextEventID gomock.Any(), // random uuid gomock.Eq(testRequestID), &workflowMatcher{latestExecution}, gomock.Eq(testRequestReason), gomock.Nil(), gomock.Eq(testRequestSkipSignalReapply), ).Return(nil).Times(1) }, resetEventID: 12, err: nil, }, { name: "success - reset on decision task completed", setupMock: func(mockResetter *reset.MockWorkflowResetter, resetEventID int64) { mockResetter.EXPECT().ResetWorkflow( gomock.Any(), // Context gomock.Eq(constants.TestDomainID), gomock.Eq(constants.TestWorkflowID), gomock.Eq(latestExecution.RunID), gomock.Eq(branchToken), gomock.Eq(resetEventID-1), // Request.DecisionFinishEventID - 1 gomock.Eq(version), // CurrentVersion gomock.Eq(int64(26)), // NextEventID gomock.Any(), // random uuid gomock.Eq(testRequestID), &workflowMatcher{latestExecution}, gomock.Eq(testRequestReason), gomock.Nil(), gomock.Eq(testRequestSkipSignalReapply), ).Return(nil).Times(1) }, resetEventID: 24, err: nil, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) pageToken := []byte("some random pagination token") eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) request := resetExecutionRequest(latestExecution, int(tc.resetEventID)) mockResetter := reset.NewMockWorkflowResetter(ctrl) eft.Engine.(*historyEngineImpl).workflowResetter = mockResetter eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, latestExecution.WorkflowID, latestExecution.RunID).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil) tc.setupMock(mockResetter, tc.resetEventID) withHistoryPagination(pageToken, tc.resetEventID)(t, eft) withState(latestExecution, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: latestRunID, DecisionScheduleID: commonconstants.EmptyEventID, LastProcessedEvent: 25, NextEventID: 26, }, ExecutionStats: &persistence.ExecutionStats{HistorySize: 100}, VersionHistories: &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: branchToken, Items: []*persistence.VersionHistoryItem{ { EventID: 25, Version: version, }, }, }, }, }, })(t, eft) withCurrentExecution(latestExecution)(t, eft) eft.Engine.Start() result, err := eft.Engine.ResetWorkflowExecution(ctx.Background(), request) eft.Engine.Stop() if tc.err != nil { assert.Error(t, err) assert.EqualError(t, err, tc.err.Error()) } else { assert.NoError(t, err) assert.NotNil(t, result) } }) } } func withHistoryPagination(pageToken []byte, resetEventID int64) InitFn { return func(_ *testing.T, engine *testdata.EngineForTest) { counter := int64(0) historySize := 0 for index, historyBatch := range history { token := pageToken if index == 0 { token = nil } counter += int64(len(historyBatch.Events)) if counter >= resetEventID { pageToken = nil } engine.ShardCtx.GetHistoryManager().(*mocks.HistoryV2Manager).On("ReadHistoryBranchByBatch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: resetEventID + 1, // Rebuild adds 1 to nextEventID PageSize: DefaultPageSize, NextPageToken: token, ShardID: common.IntPtr(engine.ShardCtx.GetShardID()), DomainName: constants.TestDomainName, }).Return(&persistence.ReadHistoryBranchByBatchResponse{ History: []*types.History{{Events: historyBatch.Events}}, NextPageToken: pageToken, Size: historyBatch.Size, }, nil).Once() historySize += historyBatch.Size if counter >= resetEventID { break } } } } func withCurrentExecution(execution *types.WorkflowExecution) InitFn { return func(_ *testing.T, engine *testdata.EngineForTest) { engine.ShardCtx.Resource.ExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(&persistence.GetCurrentExecutionResponse{ StartRequestID: "CurrentExecutionStartRequestID", RunID: execution.RunID, // Other fields don't matter }, nil) } } func withState(execution *types.WorkflowExecution, state *persistence.WorkflowMutableState) InitFn { return func(_ *testing.T, engine *testdata.EngineForTest) { engine.ShardCtx.Resource.ExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.GetWorkflowExecutionRequest) bool { return req.Execution == *execution })).Return(&persistence.GetWorkflowExecutionResponse{ State: state, }, nil) } } func withActiveClusterInfo(domainID string, execution *types.WorkflowExecution, activeClusterInfo *types.ActiveClusterInfo) InitFn { return func(_ *testing.T, engine *testdata.EngineForTest) { engine.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), domainID, execution.WorkflowID, execution.RunID).Return(activeClusterInfo, nil) } } func resetExecutionRequest(execution *types.WorkflowExecution, decisionFinishEventID int) *types.HistoryResetWorkflowExecutionRequest { return &types.HistoryResetWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, ResetRequest: &types.ResetWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowExecution: execution, Reason: testRequestReason, DecisionFinishEventID: int64(decisionFinishEventID), RequestID: testRequestID, SkipSignalReapply: testRequestSkipSignalReapply, }, } } type workflowMatcher struct { execution *types.WorkflowExecution } func (m *workflowMatcher) Matches(obj interface{}) bool { if ex, ok := obj.(execution.Workflow); ok { executionInfo := ex.GetMutableState().GetExecutionInfo() return executionInfo.WorkflowID == m.execution.WorkflowID && executionInfo.RunID == m.execution.RunID } return false } func (m *workflowMatcher) String() string { return fmt.Sprintf("Workflow with WorkflowID %s and RunID %s", m.execution.WorkflowID, m.execution.RunID) } ================================================ FILE: service/history/engine/engineimpl/respond_activity_task_canceled.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) // RespondActivityTaskCanceled completes an activity task failure. func (e *historyEngineImpl) RespondActivityTaskCanceled( ctx context.Context, req *types.HistoryRespondActivityTaskCanceledRequest, ) error { request := req.CancelRequest token, err0 := e.tokenSerializer.Deserialize(request.TaskToken) if err0 != nil { return workflow.ErrDeserializingToken } domainEntry, err := e.getActiveDomainByWorkflow(ctx, req.DomainUUID, token.WorkflowID, token.RunID) if err != nil { return err } domainID := domainEntry.GetInfo().ID domainName := domainEntry.GetInfo().Name workflowExecution := types.WorkflowExecution{ WorkflowID: token.WorkflowID, RunID: token.RunID, } var activityStartedTime time.Time var taskList string err = workflow.UpdateWithAction(ctx, e.logger, e.executionCache, domainID, workflowExecution, true, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return workflow.ErrAlreadyCompleted } scheduleID := token.ScheduleID if scheduleID == constants.EmptyEventID { // client call CompleteActivityById, so get scheduleID by activityID scheduleID, err0 = getScheduleID(token.ActivityID, mutableState) if err0 != nil { return err0 } } ai, isRunning := mutableState.GetActivityInfo(scheduleID) // First check to see if cache needs to be refreshed as we could potentially have stale workflow execution in // some extreme cassandra failure cases. if !isRunning && scheduleID >= mutableState.GetNextEventID() { e.metricsClient.IncCounter(metrics.HistoryRespondActivityTaskCanceledScope, metrics.StaleMutableStateCounter) e.logger.Error("Encounter stale mutable state in RecordActivityTaskCanceled", tag.WorkflowDomainName(domainName), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(scheduleID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) return workflow.ErrStaleState } if !isRunning || ai.StartedID == constants.EmptyEventID || (token.ScheduleID != constants.EmptyEventID && token.ScheduleAttempt != int64(ai.Attempt)) { return workflow.ErrActivityTaskNotFound } if _, err := mutableState.AddActivityTaskCanceledEvent( scheduleID, ai.StartedID, ai.CancelRequestID, request.Details, request.Identity); err != nil { // Unable to add ActivityTaskCanceled event to history return &types.InternalServiceError{Message: "Unable to add ActivityTaskCanceled event to history."} } activityStartedTime = ai.StartedTime taskList = ai.TaskList return nil }) if err == nil && !activityStartedTime.IsZero() { scope := e.metricsClient.Scope(metrics.HistoryClientRespondActivityTaskCanceledScope). Tagged( metrics.DomainTag(domainName), metrics.WorkflowTypeTag(token.WorkflowType), metrics.ActivityTypeTag(token.ActivityType), metrics.TaskListTag(taskList), ) scope.RecordTimer(metrics.ActivityE2ELatency, time.Since(activityStartedTime)) } return err } ================================================ FILE: service/history/engine/engineimpl/respond_activity_task_completed.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "fmt" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) // RespondActivityTaskCompleted completes an activity task. func (e *historyEngineImpl) RespondActivityTaskCompleted( ctx context.Context, req *types.HistoryRespondActivityTaskCompletedRequest, ) error { request := req.CompleteRequest token, err0 := e.tokenSerializer.Deserialize(request.TaskToken) if err0 != nil { return workflow.ErrDeserializingToken } domainEntry, err := e.getActiveDomainByWorkflow(ctx, req.DomainUUID, token.WorkflowID, token.RunID) if err != nil { return err } domainID := domainEntry.GetInfo().ID domainName := domainEntry.GetInfo().Name workflowExecution := types.WorkflowExecution{ WorkflowID: token.WorkflowID, RunID: token.RunID, } var activityStartedTime time.Time var taskList string err = workflow.UpdateWithAction(ctx, e.logger, e.executionCache, domainID, workflowExecution, true, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return workflow.ErrAlreadyCompleted } scheduleID := token.ScheduleID if scheduleID == constants.EmptyEventID { // client call CompleteActivityById, so get scheduleID by activityID scheduleID, err0 = getScheduleID(token.ActivityID, mutableState) if err0 != nil { return err0 } } ai, isRunning := mutableState.GetActivityInfo(scheduleID) // First check to see if cache needs to be refreshed as we could potentially have stale workflow execution in // some extreme cassandra failure cases. if !isRunning && scheduleID >= mutableState.GetNextEventID() { e.metricsClient.IncCounter(metrics.HistoryRespondActivityTaskCompletedScope, metrics.StaleMutableStateCounter) e.logger.Error("Encounter stale mutable state in RecordActivityTaskCompleted", tag.WorkflowDomainName(domainName), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(scheduleID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) return workflow.ErrStaleState } if !isRunning || ai.StartedID == constants.EmptyEventID || (token.ScheduleID != constants.EmptyEventID && token.ScheduleAttempt != int64(ai.Attempt)) { e.logger.Warn(fmt.Sprintf( "Encounter non existing activity in RecordActivityTaskCompleted: isRunning: %t, ai: %#v, token: %#v.", isRunning, ai, token), tag.WorkflowDomainName(domainName), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(scheduleID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) return workflow.ErrActivityTaskNotFound } if _, err := mutableState.AddActivityTaskCompletedEvent(scheduleID, ai.StartedID, request); err != nil { // Unable to add ActivityTaskCompleted event to history return &types.InternalServiceError{Message: "Unable to add ActivityTaskCompleted event to history."} } activityStartedTime = ai.StartedTime taskList = ai.TaskList return nil }) if err == nil && !activityStartedTime.IsZero() { scope := e.metricsClient.Scope(metrics.HistoryRespondActivityTaskCompletedScope). Tagged( metrics.DomainTag(domainName), metrics.WorkflowTypeTag(token.WorkflowType), metrics.ActivityTypeTag(token.ActivityType), metrics.TaskListTag(taskList), ) scope.RecordTimer(metrics.ActivityE2ELatency, time.Since(activityStartedTime)) } return err } ================================================ FILE: service/history/engine/engineimpl/respond_activity_task_failed.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "fmt" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) // RespondActivityTaskFailed completes an activity task failure. func (e *historyEngineImpl) RespondActivityTaskFailed( ctx context.Context, req *types.HistoryRespondActivityTaskFailedRequest, ) error { request := req.FailedRequest token, err0 := e.tokenSerializer.Deserialize(request.TaskToken) if err0 != nil { return workflow.ErrDeserializingToken } domainEntry, err := e.getActiveDomainByWorkflow(ctx, req.DomainUUID, token.WorkflowID, token.RunID) if err != nil { return err } domainID := domainEntry.GetInfo().ID domainName := domainEntry.GetInfo().Name workflowExecution := types.WorkflowExecution{ WorkflowID: token.WorkflowID, RunID: token.RunID, } var activityStartedTime time.Time var taskList string err = workflow.UpdateWithActionFunc( ctx, e.logger, e.executionCache, domainID, workflowExecution, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) (*workflow.UpdateAction, error) { if !mutableState.IsWorkflowExecutionRunning() { return nil, workflow.ErrAlreadyCompleted } scheduleID := token.ScheduleID if scheduleID == constants.EmptyEventID { // client call CompleteActivityById, so get scheduleID by activityID scheduleID, err0 = getScheduleID(token.ActivityID, mutableState) if err0 != nil { return nil, err0 } } ai, isRunning := mutableState.GetActivityInfo(scheduleID) // First check to see if cache needs to be refreshed as we could potentially have stale workflow execution in // some extreme cassandra failure cases. if !isRunning && scheduleID >= mutableState.GetNextEventID() { e.metricsClient.IncCounter(metrics.HistoryRespondActivityTaskFailedScope, metrics.StaleMutableStateCounter) e.logger.Error("Encounter stale mutable state in RecordActivityTaskFailed", tag.WorkflowDomainName(domainName), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(scheduleID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) return nil, workflow.ErrStaleState } if !isRunning || ai.StartedID == constants.EmptyEventID || (token.ScheduleID != constants.EmptyEventID && token.ScheduleAttempt != int64(ai.Attempt)) { e.logger.Warn(fmt.Sprintf( "Encounter non existing activity in RecordActivityTaskFailed: isRunning: %t, ai: %#v, token: %#v.", isRunning, ai, token), tag.WorkflowDomainName(domainName), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(scheduleID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) return nil, workflow.ErrActivityTaskNotFound } postActions := &workflow.UpdateAction{} ok, err := mutableState.RetryActivity(ai, req.FailedRequest.GetReason(), req.FailedRequest.GetDetails()) if err != nil { return nil, err } if !ok { // no more retry, and we want to record the failure event if _, err := mutableState.AddActivityTaskFailedEvent(scheduleID, ai.StartedID, request); err != nil { // Unable to add ActivityTaskFailed event to history return nil, &types.InternalServiceError{Message: "Unable to add ActivityTaskFailed event to history."} } postActions.CreateDecision = true } activityStartedTime = ai.StartedTime taskList = ai.TaskList return postActions, nil }, ) if err == nil && !activityStartedTime.IsZero() { scope := e.metricsClient.Scope(metrics.HistoryRespondActivityTaskFailedScope). Tagged( metrics.DomainTag(domainName), metrics.WorkflowTypeTag(token.WorkflowType), metrics.ActivityTypeTag(token.ActivityType), metrics.TaskListTag(taskList), ) scope.RecordTimer(metrics.ActivityE2ELatency, time.Since(activityStartedTime)) } return err } ================================================ FILE: service/history/engine/engineimpl/respond_activity_task_heartbeat.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "fmt" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) // RecordActivityTaskHeartbeat records an heartbeat for a task. // This method can be used for two purposes. // - For reporting liveness of the activity. // - For reporting progress of the activity, this can be done even if the liveness is not configured. func (e *historyEngineImpl) RecordActivityTaskHeartbeat( ctx context.Context, req *types.HistoryRecordActivityTaskHeartbeatRequest, ) (*types.RecordActivityTaskHeartbeatResponse, error) { request := req.HeartbeatRequest token, err0 := e.tokenSerializer.Deserialize(request.TaskToken) if err0 != nil { return nil, workflow.ErrDeserializingToken } domainEntry, err := e.getActiveDomainByWorkflow(ctx, req.DomainUUID, token.WorkflowID, token.RunID) if err != nil { return nil, err } domainID := domainEntry.GetInfo().ID workflowExecution := types.WorkflowExecution{ WorkflowID: token.WorkflowID, RunID: token.RunID, } var cancelRequested bool err = workflow.UpdateWithAction(ctx, e.logger, e.executionCache, domainID, workflowExecution, false, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { e.logger.Debug("Heartbeat failed") return workflow.ErrAlreadyCompleted } scheduleID := token.ScheduleID if scheduleID == constants.EmptyEventID { // client call RecordActivityHeartbeatByID, so get scheduleID by activityID scheduleID, err0 = getScheduleID(token.ActivityID, mutableState) if err0 != nil { return err0 } } ai, isRunning := mutableState.GetActivityInfo(scheduleID) // First check to see if cache needs to be refreshed as we could potentially have stale workflow execution in // some extreme cassandra failure cases. if !isRunning && scheduleID >= mutableState.GetNextEventID() { e.metricsClient.IncCounter(metrics.HistoryRecordActivityTaskHeartbeatScope, metrics.StaleMutableStateCounter) e.logger.Error("Encounter stale mutable state in RecordActivityTaskHeartbeat", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(scheduleID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) return workflow.ErrStaleState } if !isRunning || ai.StartedID == constants.EmptyEventID || (token.ScheduleID != constants.EmptyEventID && token.ScheduleAttempt != int64(ai.Attempt)) { e.logger.Warn(fmt.Sprintf( "Encounter non existing activity in RecordActivityTaskHeartbeat: isRunning: %t, ai: %#v, token: %#v.", isRunning, ai, token), tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowScheduleID(scheduleID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) return workflow.ErrActivityTaskNotFound } cancelRequested = ai.CancelRequested e.logger.Debug(fmt.Sprintf("Activity HeartBeat: scheduleEventID: %v, ActivityInfo: %+v, CancelRequested: %v", scheduleID, ai, cancelRequested)) // Save progress and last HB reported time. mutableState.UpdateActivityProgress(ai, request) return nil }) if err != nil { return &types.RecordActivityTaskHeartbeatResponse{}, err } return &types.RecordActivityTaskHeartbeatResponse{CancelRequested: cancelRequested}, nil } ================================================ FILE: service/history/engine/engineimpl/respond_decision_task_completed.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common/types" ) // RespondDecisionTaskCompleted completes a decision task func (e *historyEngineImpl) RespondDecisionTaskCompleted(ctx context.Context, req *types.HistoryRespondDecisionTaskCompletedRequest) (*types.HistoryRespondDecisionTaskCompletedResponse, error) { return e.decisionHandler.HandleDecisionTaskCompleted(ctx, req) } ================================================ FILE: service/history/engine/engineimpl/respond_decision_task_failed.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common/types" ) // RespondDecisionTaskFailed fails a decision func (e *historyEngineImpl) RespondDecisionTaskFailed(ctx context.Context, req *types.HistoryRespondDecisionTaskFailedRequest) error { return e.decisionHandler.HandleDecisionTaskFailed(ctx, req) } ================================================ FILE: service/history/engine/engineimpl/signal_workflow_execution.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) func (e *historyEngineImpl) SignalWorkflowExecution( ctx context.Context, signalRequest *types.HistorySignalWorkflowExecutionRequest, ) error { request := signalRequest.SignalRequest workflowExecution := types.WorkflowExecution{ WorkflowID: request.WorkflowExecution.WorkflowID, RunID: request.WorkflowExecution.RunID, } domainEntry, err := e.getActiveDomainByWorkflow(ctx, signalRequest.DomainUUID, workflowExecution.WorkflowID, workflowExecution.RunID) if err != nil { return err } if domainEntry.GetInfo().Status != persistence.DomainStatusRegistered { return errDomainDeprecated } domainID := domainEntry.GetInfo().ID parentExecution := signalRequest.ExternalWorkflowExecution childWorkflowOnly := signalRequest.GetChildWorkflowOnly() return workflow.UpdateCurrentWithActionFunc( ctx, e.logger, e.executionCache, e.executionManager, domainID, e.shard.GetDomainCache(), workflowExecution, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) (*workflow.UpdateAction, error) { // first deduplicate by request id for signal decision // this is done before workflow running check so that already completed error // won't be returned for duplicated signals even if the workflow is closed. if requestID := request.GetRequestID(); requestID != "" { if mutableState.IsSignalRequested(requestID) { return &workflow.UpdateAction{ Noop: true, CreateDecision: false, }, nil } } if !mutableState.IsWorkflowExecutionRunning() { return nil, workflow.ErrAlreadyCompleted } // If history is corrupted, signal will be rejected if corrupted, err := e.checkForHistoryCorruptions(ctx, mutableState); err != nil { return nil, err } else if corrupted { return nil, &types.EntityNotExistsError{Message: "Workflow execution corrupted."} } executionInfo := mutableState.GetExecutionInfo() createDecisionTask := true if !mutableState.HasProcessedOrPendingDecision() { createDecisionTask = false } maxAllowedSignals := e.config.MaximumSignalsPerExecution(domainEntry.GetInfo().Name) if maxAllowedSignals > 0 && int(executionInfo.SignalCount) >= maxAllowedSignals { e.logger.Info("Execution limit reached for maximum signals", tag.WorkflowSignalCount(executionInfo.SignalCount), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowDomainID(domainID)) return nil, workflow.ErrSignalsLimitExceeded } if childWorkflowOnly { parentWorkflowID := executionInfo.ParentWorkflowID parentRunID := executionInfo.ParentRunID if parentExecution.GetWorkflowID() != parentWorkflowID || parentExecution.GetRunID() != parentRunID { return nil, workflow.ErrParentMismatch } } if requestID := request.GetRequestID(); requestID != "" { mutableState.AddSignalRequested(requestID) } if _, err := mutableState.AddWorkflowExecutionSignaled( request.GetSignalName(), request.GetInput(), request.GetIdentity(), request.GetRequestID(), ); err != nil { return nil, &types.InternalServiceError{Message: "Unable to signal workflow execution."} } return &workflow.UpdateAction{ Noop: false, CreateDecision: createDecisionTask, }, nil }) } ================================================ FILE: service/history/engine/engineimpl/start_workflow_execution.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "errors" "fmt" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) var errClusterAttributeNotFound = &types.BadRequestError{Message: "Cannot start workflow with a cluster attribute that is not found in the domain's metadata."} // for startWorkflowHelper be reused by signalWithStart type signalWithStartArg struct { signalWithStartRequest *types.HistorySignalWithStartWorkflowExecutionRequest prevMutableState execution.MutableState } // StartWorkflowExecution starts a workflow execution func (e *historyEngineImpl) StartWorkflowExecution( ctx context.Context, startRequest *types.HistoryStartWorkflowExecutionRequest, ) (resp *types.StartWorkflowExecutionResponse, retError error) { domainEntry, err := e.shard.GetDomainCache().GetDomainByID(startRequest.DomainUUID) if err != nil { return nil, err } resp, workflowExecution, historyBlob, err := e.startWorkflowHelper( ctx, startRequest, domainEntry, metrics.HistoryStartWorkflowExecutionScope, nil) if err != nil { e.handleCreateWorkflowExecutionFailureCleanup(ctx, startRequest, domainEntry, workflowExecution, historyBlob, false, err, ) return nil, err } return resp, nil } func (e *historyEngineImpl) startWorkflowHelper( ctx context.Context, startRequest *types.HistoryStartWorkflowExecutionRequest, domainEntry *cache.DomainCacheEntry, metricsScope metrics.ScopeIdx, sigWithStartArg *signalWithStartArg, ) (_ *types.StartWorkflowExecutionResponse, _ *types.WorkflowExecution, _ *events.PersistedBlob, retError error) { if domainEntry.GetInfo().Status != persistence.DomainStatusRegistered { return nil, nil, nil, errDomainDeprecated } request := startRequest.StartRequest err := e.validateStartWorkflowExecutionRequest(request, metricsScope) if err != nil { return nil, nil, nil, err } e.overrideTaskStartToCloseTimeoutSeconds(domainEntry, request, metricsScope) workflowID := request.GetWorkflowID() domainID := domainEntry.GetInfo().ID domain := domainEntry.GetInfo().Name // grab the current context as a lock, nothing more // use a smaller context timeout to get the lock childCtx, childCancel := e.newChildContext(ctx) defer childCancel() _, currentRelease, err := e.executionCache.GetOrCreateCurrentWorkflowExecution( childCtx, domainID, workflowID, ) if err != nil { if err == context.DeadlineExceeded { return nil, nil, nil, workflow.ErrConcurrentStartRequest } return nil, nil, nil, err } defer func() { currentRelease(retError) }() workflowExecution := &types.WorkflowExecution{ WorkflowID: workflowID, RunID: uuid.New(), } curMutableState, err := e.createMutableState(ctx, domainEntry, workflowExecution.GetRunID(), startRequest) if err != nil { return nil, nil, nil, err } // preprocess for signalWithStart var prevMutableState execution.MutableState var signalWithStartRequest *types.HistorySignalWithStartWorkflowExecutionRequest isSignalWithStart := sigWithStartArg != nil if isSignalWithStart { prevMutableState = sigWithStartArg.prevMutableState signalWithStartRequest = sigWithStartArg.signalWithStartRequest } if prevMutableState != nil { prevLastWriteVersion, err := prevMutableState.GetLastWriteVersion() if err != nil { return nil, nil, nil, err } if prevLastWriteVersion > curMutableState.GetCurrentVersion() { policy, err := e.shard.GetActiveClusterManager().GetActiveClusterSelectionPolicyForWorkflow(ctx, domainID, workflowID, prevMutableState.GetExecutionInfo().RunID) if err != nil { return nil, nil, nil, err } if policy.Equals(request.ActiveClusterSelectionPolicy) { return nil, nil, nil, e.newDomainNotActiveError( domainEntry, prevLastWriteVersion, ) } } err = e.applyWorkflowIDReusePolicyForSigWithStart( prevMutableState.GetExecutionInfo(), *workflowExecution, request.GetWorkflowIDReusePolicy(), ) if err != nil { return nil, nil, nil, err } } else if e.shard.GetConfig().EnableRecordWorkflowExecutionUninitialized(domainEntry.GetInfo().Name) && e.visibilityMgr != nil { uninitializedRequest := &persistence.RecordWorkflowExecutionUninitializedRequest{ DomainUUID: domainID, Domain: domain, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: workflowExecution.RunID, }, WorkflowTypeName: request.WorkflowType.Name, UpdateTimestamp: e.shard.GetTimeSource().Now().UnixNano(), ShardID: int64(e.shard.GetShardID()), } if err := e.visibilityMgr.RecordWorkflowExecutionUninitialized(ctx, uninitializedRequest); err != nil { e.logger.Error("Failed to record uninitialized workflow execution", tag.Error(err)) } } err = e.addStartEventsAndTasks( curMutableState, *workflowExecution, startRequest, signalWithStartRequest, ) if err != nil { if e.shard.GetConfig().EnableRecordWorkflowExecutionUninitialized(domainEntry.GetInfo().Name) && e.visibilityMgr != nil { // delete the uninitialized workflow execution record since it failed to start the workflow // uninitialized record is used to find wfs that didn't make a progress or stuck during the start process if errVisibility := e.visibilityMgr.DeleteWorkflowExecution(ctx, &persistence.VisibilityDeleteWorkflowExecutionRequest{ DomainID: domainID, Domain: domain, RunID: workflowExecution.RunID, WorkflowID: workflowID, }); errVisibility != nil { e.logger.Error("Failed to delete uninitialized workflow execution record", tag.Error(errVisibility)) } } return nil, nil, nil, err } wfContext := execution.NewContext(domainID, *workflowExecution, e.shard, e.executionManager, e.logger) newWorkflow, newWorkflowEventsSeq, err := curMutableState.CloseTransactionAsSnapshot( e.timeSource.Now(), execution.TransactionPolicyActive, ) if err != nil { return nil, nil, nil, err } b, err := wfContext.PersistStartWorkflowBatchEvents(ctx, newWorkflowEventsSeq[0]) if err != nil { return nil, workflowExecution, nil, err } historyBlob := &b // create as brand new createMode := persistence.CreateWorkflowModeBrandNew prevRunID := "" prevLastWriteVersion := int64(0) // overwrite in case of signalWithStart if prevMutableState != nil { createMode = persistence.CreateWorkflowModeWorkflowIDReuse info := prevMutableState.GetExecutionInfo() // For corrupted workflows use ContinueAsNew mode. // WorkflowIDReuse mode require workflows to be in completed state, which is not necessarily true for corrupted workflows. if info.State == persistence.WorkflowStateCorrupted { createMode = persistence.CreateWorkflowModeContinueAsNew } prevRunID = info.RunID prevLastWriteVersion, err = prevMutableState.GetLastWriteVersion() if err != nil { return nil, workflowExecution, historyBlob, err } } err = wfContext.CreateWorkflowExecution( ctx, newWorkflow, *historyBlob, createMode, prevRunID, prevLastWriteVersion, persistence.CreateWorkflowRequestModeNew, ) if t, ok := persistence.AsDuplicateRequestError(err); ok { if t.RequestType == persistence.WorkflowRequestTypeStart || (isSignalWithStart && t.RequestType == persistence.WorkflowRequestTypeSignal) { return &types.StartWorkflowExecutionResponse{ RunID: t.RunID, }, workflowExecution, historyBlob, nil } e.logger.Error("A bug is detected for idempotency improvement", tag.Dynamic("request-type", t.RequestType)) return nil, workflowExecution, historyBlob, t } // handle already started error if t, ok := err.(*persistence.WorkflowExecutionAlreadyStartedError); ok { if t.StartRequestID == request.GetRequestID() { return &types.StartWorkflowExecutionResponse{ RunID: t.RunID, }, workflowExecution, historyBlob, nil } if isSignalWithStart { e.logger.Warn("signal-with-start might have left an orphaned history branch", tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.Dynamic("debug-startRequest", startRequest), tag.Dynamic("debug-historyBlob", historyBlob), tag.Dynamic("debug-domainEntry", domainEntry), tag.Dynamic("debug-workflowExecution", workflowExecution), tag.Error(err)) return nil, workflowExecution, historyBlob, err } if curMutableState.GetCurrentVersion() < t.LastWriteVersion { policy, err := e.shard.GetActiveClusterManager().GetActiveClusterSelectionPolicyForWorkflow(ctx, domainID, workflowID, t.RunID) if err != nil { return nil, workflowExecution, historyBlob, err } if policy.Equals(request.ActiveClusterSelectionPolicy) { return nil, workflowExecution, historyBlob, e.newDomainNotActiveError( domainEntry, t.LastWriteVersion, ) } } prevRunID = t.RunID if shouldTerminateAndStart(startRequest, t.State) { runningWFCtx, err := workflow.LoadOnce(ctx, e.executionCache, domainID, workflowID, prevRunID) if err != nil { return nil, workflowExecution, historyBlob, err } defer func() { runningWFCtx.GetReleaseFn()(retError) }() resp, err := e.terminateAndStartWorkflow( ctx, runningWFCtx, *workflowExecution, domainEntry, domainID, startRequest, nil, ) switch err.(type) { // By the time we try to terminate the workflow, it was already terminated // So continue as if we didn't need to terminate it in the first place case *types.WorkflowExecutionAlreadyCompletedError: e.shard.GetLogger().Warn("Workflow completed while trying to terminate, will continue starting workflow", tag.Error(err)) default: return resp, workflowExecution, historyBlob, err } } if err = e.applyWorkflowIDReusePolicyHelper( t.StartRequestID, prevRunID, t.State, t.CloseStatus, *workflowExecution, startRequest.StartRequest.GetWorkflowIDReusePolicy(), ); err != nil { return nil, workflowExecution, historyBlob, err } // create as ID reuse createMode = persistence.CreateWorkflowModeWorkflowIDReuse err = wfContext.CreateWorkflowExecution( ctx, newWorkflow, *historyBlob, createMode, prevRunID, t.LastWriteVersion, persistence.CreateWorkflowRequestModeNew, ) if t, ok := persistence.AsDuplicateRequestError(err); ok { if t.RequestType == persistence.WorkflowRequestTypeStart || (isSignalWithStart && t.RequestType == persistence.WorkflowRequestTypeSignal) { return &types.StartWorkflowExecutionResponse{ RunID: t.RunID, }, workflowExecution, historyBlob, nil } e.logger.Error("A bug is detected for idempotency improvement", tag.Dynamic("request-type", t.RequestType)) return nil, workflowExecution, historyBlob, t } } if err != nil { return nil, workflowExecution, historyBlob, err } return &types.StartWorkflowExecutionResponse{ RunID: workflowExecution.RunID, }, workflowExecution, historyBlob, nil } func (e *historyEngineImpl) handleCreateWorkflowExecutionFailureCleanup( ctx context.Context, startRequest *types.HistoryStartWorkflowExecutionRequest, domainEntry *cache.DomainCacheEntry, workflowExecution *types.WorkflowExecution, historyBlob *events.PersistedBlob, isSignalWithStart bool, err error, ) { if workflowExecution == nil || historyBlob == nil { // expected behaviour, errors caused by validation will not have a workflow execution return } // we cannot know if the workflow has succeeded, so the safest thing to do is // to do nothing. Deleting the history risks breaking a valid execution and prevrenting it from being restarted if persistence.IsTimeoutError(err) { e.logger.Warn("timeout error detected when creating execution. If the excution was not created successfully this has probably left some orphaned history branch that needs cleaning up", tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.Error(err)) e.metricsClient.IncCounter(metrics.HistoryEngineScope, metrics.WorkflowCreationFailedCleanupHaltedTimeoutCount) return } // this is the set of errors for which we condider safe to history branch created // earlier when starting the workflow. It does not include any // errors where the state of the workflow is not known (like timeouts, DB internal errors) isKnownFailureRequiringCleanup := errors.As(err, new(*types.WorkflowExecutionAlreadyStartedError)) || errors.As(err, new(*types.ShardOwnershipLostError)) || errors.As(err, new(*persistence.CurrentWorkflowConditionFailedError)) || errors.As(err, new(*persistence.ConditionFailedError)) || errors.As(err, new(*persistence.ShardAlreadyExistError)) || errors.As(err, new(*persistence.WorkflowExecutionAlreadyStartedError)) || errors.As(err, new(*types.ShardOwnershipLostError)) || errors.As(err, new(*types.WorkflowExecutionAlreadyStartedError)) || errors.As(err, new(*types.WorkflowExecutionAlreadyCompletedError)) || errors.As(err, new(*persistence.ShardOwnershipLostError)) || errors.As(err, new(*persistence.DuplicateRequestError)) if !isKnownFailureRequiringCleanup { e.logger.Warn("unknown failure detected when creating execution. If the excution was not created successfully this has probably left some orphaned history branch that needs cleaning up", tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.Error(err)) e.metricsClient.IncCounter(metrics.HistoryEngineScope, metrics.WorkflowCreationFailedCleanupUnknownCount) return } if !e.shard.GetConfig().EnableCleanupOrphanedHistoryBranchOnWorkflowCreation(domainEntry.GetInfo().Name) { e.logger.Warn("cleanup of orphaned history branch is disabled, but possible orphaned history branch was detected", tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.Dynamic("debug-isSignalWithStart", isSignalWithStart), tag.Error(err), ) return } if startRequest == nil || domainEntry == nil || workflowExecution.WorkflowID == "" || workflowExecution.RunID == "" { e.logger.Error("some parameters are missing in handleCreateWorkflowExecutionFailureCleanup. This is a bug", tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.Dynamic("debug-startRequest", startRequest), tag.Dynamic("debug-historyBlob", historyBlob), tag.Dynamic("debug-domainEntry", domainEntry), tag.Dynamic("debug-workflowExecution", workflowExecution), tag.Error(err)) return } // The key here is to delete the additational branch, since it has just been created earlier in this call // and is not used. However, we must be careful, there may be other existing branches that we must not touch e.logger.Info("Deleting orphaned history branch during cleanup after identified failure during creation", tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.Dynamic("debug-isSignalWithStart", isSignalWithStart), tag.Error(err)) cleanupErr := e.shard.GetHistoryManager().DeleteHistoryBranch(ctx, &persistence.DeleteHistoryBranchRequest{ BranchToken: historyBlob.BranchToken, ShardID: common.IntPtr(e.shard.GetShardID()), DomainName: domainEntry.GetInfo().Name, }) if cleanupErr != nil { e.logger.Warn("Failed to cleanup orphaned history branch", tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.Error(cleanupErr)) e.metricsClient.IncCounter(metrics.HistoryEngineScope, metrics.WorkflowCreationFailedCleanupFailureCount) return } e.metricsClient.IncCounter(metrics.HistoryEngineScope, metrics.WorkflowCreationFailedCleanupSuccessCount) if e.shard.GetConfig().EnableRecordWorkflowExecutionUninitialized(domainEntry.GetInfo().Name) && e.visibilityMgr != nil { // delete the uninitialized workflow execution record since it failed to start the workflow // uninitialized record is used to find wfs that didn't make a progress or stuck during the start process if errVisibility := e.visibilityMgr.DeleteWorkflowExecution(ctx, &persistence.VisibilityDeleteWorkflowExecutionRequest{ DomainID: domainEntry.GetInfo().ID, Domain: domainEntry.GetInfo().Name, RunID: workflowExecution.RunID, WorkflowID: workflowExecution.WorkflowID, }); errVisibility != nil { e.logger.Warn("Failed to delete uninitialized workflow execution record after failed CreateWorkflowExecution", tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.Error(errVisibility)) } } } // TODO(active-active): Review this method for active-active domains func (e *historyEngineImpl) SignalWithStartWorkflowExecution( ctx context.Context, signalWithStartRequest *types.HistorySignalWithStartWorkflowExecutionRequest, ) (retResp *types.StartWorkflowExecutionResponse, retError error) { domainEntry, err := e.shard.GetDomainCache().GetDomainByID(signalWithStartRequest.DomainUUID) if err != nil { return nil, err } if domainEntry.GetInfo().Status != persistence.DomainStatusRegistered { return nil, errDomainDeprecated } domainID := domainEntry.GetInfo().ID sRequest := signalWithStartRequest.SignalWithStartRequest workflowExecution := types.WorkflowExecution{ WorkflowID: sRequest.WorkflowID, } var prevMutableState execution.MutableState attempt := 0 wfContext, release, err0 := e.executionCache.GetOrCreateWorkflowExecution(ctx, domainID, workflowExecution) if err0 == nil { defer func() { release(retError) }() Just_Signal_Loop: for ; attempt < workflow.ConditionalRetryCount; attempt++ { // workflow not exist, will create workflow then signal mutableState, err1 := wfContext.LoadWorkflowExecution(ctx) if err1 != nil { if _, ok := err1.(*types.EntityNotExistsError); ok { break } return nil, err1 } if mutableState.IsSignalRequested(sRequest.GetRequestID()) { return &types.StartWorkflowExecutionResponse{RunID: wfContext.GetExecution().RunID}, nil } // workflow exist but not running, will restart workflow then signal if !mutableState.IsWorkflowExecutionRunning() { prevMutableState = mutableState break } // workflow exists but history is corrupted, will restart workflow then signal if corrupted, err := e.checkForHistoryCorruptions(ctx, mutableState); err != nil { return nil, err } else if corrupted { prevMutableState = mutableState break } // workflow is running, if policy is TerminateIfRunning, terminate current run then signalWithStart if sRequest.GetWorkflowIDReusePolicy() == types.WorkflowIDReusePolicyTerminateIfRunning { workflowExecution.RunID = uuid.New() runningWFCtx := workflow.NewContext(wfContext, release, mutableState) resp, errTerm := e.terminateAndStartWorkflow( ctx, runningWFCtx, workflowExecution, domainEntry, domainID, nil, signalWithStartRequest, ) // By the time we try to terminate the workflow, it was already terminated // So continue as if we didn't need to terminate it in the first place if _, ok := errTerm.(*types.WorkflowExecutionAlreadyCompletedError); !ok { return resp, errTerm } } executionInfo := mutableState.GetExecutionInfo() maxAllowedSignals := e.config.MaximumSignalsPerExecution(domainEntry.GetInfo().Name) if maxAllowedSignals > 0 && int(executionInfo.SignalCount) >= maxAllowedSignals { e.logger.Info("Execution limit reached for maximum signals", tag.WorkflowSignalCount(executionInfo.SignalCount), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(workflowExecution.GetRunID()), tag.WorkflowDomainID(domainID)) return nil, workflow.ErrSignalsLimitExceeded } requestID := sRequest.GetRequestID() if requestID != "" { mutableState.AddSignalRequested(requestID) } if _, err := mutableState.AddWorkflowExecutionSignaled( sRequest.GetSignalName(), sRequest.GetSignalInput(), sRequest.GetIdentity(), sRequest.GetRequestID(), ); err != nil { return nil, &types.InternalServiceError{Message: "Unable to signal workflow execution."} } // Create a transfer task to schedule a decision task // Do not schedule if the workflow hasn't processed its first decision yet // (e.g. waiting for DelayStart or Cron timer) if !mutableState.HasPendingDecision() && mutableState.HasProcessedOrPendingDecision() { _, err := mutableState.AddDecisionTaskScheduledEvent(false) if err != nil { return nil, &types.InternalServiceError{Message: "Failed to add decision scheduled event."} } } // We apply the update to execution using optimistic concurrency. If it fails due to a conflict then reload // the history and try the operation again. e.logger.Debugf("SignalWithStartWorkflowExecution calling UpdateWorkflowExecutionAsActive for wfID %s", workflowExecution.GetWorkflowID(), ) if err := wfContext.UpdateWorkflowExecutionAsActive(ctx, e.shard.GetTimeSource().Now()); err != nil { if t, ok := persistence.AsDuplicateRequestError(err); ok { if t.RequestType == persistence.WorkflowRequestTypeSignal { return &types.StartWorkflowExecutionResponse{RunID: t.RunID}, nil } e.logger.Error("A bug is detected for idempotency improvement", tag.Dynamic("request-type", t.RequestType)) return nil, t } if execution.IsConflictError(err) { continue Just_Signal_Loop } return nil, err } return &types.StartWorkflowExecutionResponse{RunID: wfContext.GetExecution().RunID}, nil } // end for Just_Signal_Loop if attempt == workflow.ConditionalRetryCount { return nil, workflow.ErrMaxAttemptsExceeded } } else { if _, ok := err0.(*types.EntityNotExistsError); !ok { return nil, err0 } // workflow not exist, will create workflow then signal } // Start workflow and signal startRequest, err := getStartRequest(domainID, sRequest, signalWithStartRequest.PartitionConfig) if err != nil { return nil, err } sigWithStartArg := &signalWithStartArg{ signalWithStartRequest: signalWithStartRequest, prevMutableState: prevMutableState, } resp, createdWFExecution, historyBlob, err := e.startWorkflowHelper( ctx, startRequest, domainEntry, metrics.HistorySignalWithStartWorkflowExecutionScope, sigWithStartArg, ) if err != nil { e.handleCreateWorkflowExecutionFailureCleanup(ctx, startRequest, domainEntry, createdWFExecution, historyBlob, true, err, ) return nil, err } return resp, nil } func getStartRequest( domainID string, request *types.SignalWithStartWorkflowExecutionRequest, partitionConfig map[string]string, ) (*types.HistoryStartWorkflowExecutionRequest, error) { req := &types.StartWorkflowExecutionRequest{ Domain: request.Domain, WorkflowID: request.WorkflowID, WorkflowType: request.WorkflowType, TaskList: request.TaskList, Input: request.Input, ExecutionStartToCloseTimeoutSeconds: request.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: request.TaskStartToCloseTimeoutSeconds, Identity: request.Identity, RequestID: request.RequestID, WorkflowIDReusePolicy: request.WorkflowIDReusePolicy, RetryPolicy: request.RetryPolicy, CronSchedule: request.CronSchedule, CronOverlapPolicy: request.CronOverlapPolicy, Memo: request.Memo, SearchAttributes: request.SearchAttributes, Header: request.Header, DelayStartSeconds: request.DelayStartSeconds, JitterStartSeconds: request.JitterStartSeconds, FirstRunAtTimeStamp: request.FirstRunAtTimestamp, ActiveClusterSelectionPolicy: request.ActiveClusterSelectionPolicy, } return common.CreateHistoryStartWorkflowRequest(domainID, req, time.Now(), partitionConfig) } func shouldTerminateAndStart(startRequest *types.HistoryStartWorkflowExecutionRequest, state int) bool { return startRequest.StartRequest.GetWorkflowIDReusePolicy() == types.WorkflowIDReusePolicyTerminateIfRunning && persistence.IsWorkflowRunning(state) } func (e *historyEngineImpl) validateStartWorkflowExecutionRequest(request *types.StartWorkflowExecutionRequest, metricsScope metrics.ScopeIdx) error { if len(request.GetRequestID()) == 0 { return &types.BadRequestError{Message: "Missing request ID."} } if request.ExecutionStartToCloseTimeoutSeconds == nil || request.GetExecutionStartToCloseTimeoutSeconds() <= 0 { return &types.BadRequestError{Message: "Missing or invalid ExecutionStartToCloseTimeoutSeconds."} } if request.TaskStartToCloseTimeoutSeconds == nil || request.GetTaskStartToCloseTimeoutSeconds() <= 0 { return &types.BadRequestError{Message: "Missing or invalid TaskStartToCloseTimeoutSeconds."} } if request.TaskList == nil || request.TaskList.GetName() == "" { return &types.BadRequestError{Message: "Missing Tasklist."} } if request.WorkflowType == nil || request.WorkflowType.GetName() == "" { return &types.BadRequestError{Message: "Missing WorkflowType."} } if !common.IsValidIDLength( request.GetDomain(), e.metricsClient.Scope(metricsScope), e.config.MaxIDLengthWarnLimit(), e.config.DomainNameMaxLength(request.GetDomain()), metrics.CadenceErrDomainNameExceededWarnLimit, request.GetDomain(), e.logger, tag.IDTypeDomainName) { return &types.BadRequestError{Message: "Domain exceeds length limit."} } if !common.IsValidIDLength( request.GetWorkflowID(), e.metricsClient.Scope(metricsScope), e.config.MaxIDLengthWarnLimit(), e.config.WorkflowIDMaxLength(request.GetDomain()), metrics.CadenceErrWorkflowIDExceededWarnLimit, request.GetDomain(), e.logger, tag.IDTypeWorkflowID) { return &types.BadRequestError{Message: "WorkflowId exceeds length limit."} } if !common.IsValidIDLength( request.TaskList.GetName(), e.metricsClient.Scope(metricsScope), e.config.MaxIDLengthWarnLimit(), e.config.TaskListNameMaxLength(request.GetDomain()), metrics.CadenceErrTaskListNameExceededWarnLimit, request.GetDomain(), e.logger, tag.IDTypeTaskListName) { return &types.BadRequestError{Message: "TaskList exceeds length limit."} } if !common.IsValidIDLength( request.WorkflowType.GetName(), e.metricsClient.Scope(metricsScope), e.config.MaxIDLengthWarnLimit(), e.config.WorkflowTypeMaxLength(request.GetDomain()), metrics.CadenceErrWorkflowTypeExceededWarnLimit, request.GetDomain(), e.logger, tag.IDTypeWorkflowType) { return &types.BadRequestError{Message: "WorkflowType exceeds length limit."} } return common.ValidateRetryPolicy(request.RetryPolicy) } func (e *historyEngineImpl) overrideTaskStartToCloseTimeoutSeconds( domainEntry *cache.DomainCacheEntry, request *types.StartWorkflowExecutionRequest, metricsScope metrics.ScopeIdx, ) { domainName := domainEntry.GetInfo().Name maxDecisionStartToCloseTimeoutSeconds := int32(e.config.MaxDecisionStartToCloseSeconds(domainName)) taskStartToCloseTimeoutSecs := request.GetTaskStartToCloseTimeoutSeconds() taskStartToCloseTimeoutSecs = min(taskStartToCloseTimeoutSecs, maxDecisionStartToCloseTimeoutSeconds) taskStartToCloseTimeoutSecs = min(taskStartToCloseTimeoutSecs, request.GetExecutionStartToCloseTimeoutSeconds()) if taskStartToCloseTimeoutSecs != request.GetTaskStartToCloseTimeoutSeconds() { request.TaskStartToCloseTimeoutSeconds = &taskStartToCloseTimeoutSecs e.metricsClient.Scope( metricsScope, metrics.DomainTag(domainName), ).IncCounter(metrics.DecisionStartToCloseTimeoutOverrideCount) } } // terminate running workflow then start a new run in one transaction func (e *historyEngineImpl) terminateAndStartWorkflow( ctx context.Context, runningWFCtx workflow.Context, workflowExecution types.WorkflowExecution, domainEntry *cache.DomainCacheEntry, domainID string, startRequest *types.HistoryStartWorkflowExecutionRequest, signalWithStartRequest *types.HistorySignalWithStartWorkflowExecutionRequest, ) (*types.StartWorkflowExecutionResponse, error) { runningMutableState := runningWFCtx.GetMutableState() var err error if signalWithStartRequest != nil { startRequest, err = getStartRequest(domainID, signalWithStartRequest.SignalWithStartRequest, signalWithStartRequest.PartitionConfig) if err != nil { return nil, err } } activeCluster, err := e.clusterMetadata.ClusterNameForFailoverVersion(runningMutableState.GetCurrentVersion()) if err != nil { return nil, err } if activeCluster != e.currentClusterName { if runningMutableState.GetExecutionInfo().ActiveClusterSelectionPolicy.Equals(startRequest.StartRequest.ActiveClusterSelectionPolicy) { return nil, e.newDomainNotActiveError(domainEntry, runningMutableState.GetCurrentVersion()) } // TODO(active-active): This is a short-term fix to handle this special case, because we don't have a way to terminate the existing workflow in a different cluster and start a new workflow in the current cluster // atomically in one transaction. We'll review this when we have time to implement a better solution. return nil, &types.BadRequestError{Message: "Cannot terminate the existing workflow and start a new workflow because it is active in a different cluster with a different active cluster selection policy."} } UpdateWorkflowLoop: for attempt := 0; attempt < workflow.ConditionalRetryCount; attempt++ { if !runningMutableState.IsWorkflowExecutionRunning() { return nil, workflow.ErrAlreadyCompleted } if err := execution.TerminateWorkflow( runningMutableState, runningMutableState.GetNextEventID(), TerminateIfRunningReason, getTerminateIfRunningDetails(workflowExecution.GetRunID()), execution.IdentityHistoryService, ); err != nil { if err == workflow.ErrStaleState { // Handler detected that cached workflow mutable could potentially be stale // Reload workflow execution history runningWFCtx.GetContext().Clear() if attempt != workflow.ConditionalRetryCount-1 { _, err = runningWFCtx.ReloadMutableState(ctx) if err != nil { return nil, err } } continue UpdateWorkflowLoop } return nil, err } // new mutable state newMutableState, err := e.createMutableState(ctx, domainEntry, workflowExecution.GetRunID(), startRequest) if err != nil { return nil, err } err = e.addStartEventsAndTasks( newMutableState, workflowExecution, startRequest, signalWithStartRequest, ) if err != nil { return nil, err } updateErr := runningWFCtx.GetContext().UpdateWorkflowExecutionWithNewAsActive( ctx, e.timeSource.Now(), execution.NewContext( domainID, workflowExecution, e.shard, e.shard.GetExecutionManager(), e.logger, ), newMutableState, ) if updateErr != nil { if execution.IsConflictError(updateErr) { e.metricsClient.IncCounter(metrics.HistoryStartWorkflowExecutionScope, metrics.ConcurrencyUpdateFailureCounter) continue UpdateWorkflowLoop } return nil, updateErr } break UpdateWorkflowLoop } return &types.StartWorkflowExecutionResponse{ RunID: workflowExecution.RunID, }, nil } func (e *historyEngineImpl) addStartEventsAndTasks( mutableState execution.MutableState, workflowExecution types.WorkflowExecution, startRequest *types.HistoryStartWorkflowExecutionRequest, signalWithStartRequest *types.HistorySignalWithStartWorkflowExecutionRequest, ) error { // Add WF start event startEvent, err := mutableState.AddWorkflowExecutionStartedEvent( workflowExecution, startRequest, ) if err != nil { return &types.InternalServiceError{ Message: "Failed to add workflow execution started event.", } } if signalWithStartRequest != nil { // Add signal event sRequest := signalWithStartRequest.SignalWithStartRequest if sRequest.GetRequestID() != "" { mutableState.AddSignalRequested(sRequest.GetRequestID()) } _, err := mutableState.AddWorkflowExecutionSignaled( sRequest.GetSignalName(), sRequest.GetSignalInput(), sRequest.GetIdentity(), sRequest.GetRequestID(), ) if err != nil { return &types.InternalServiceError{Message: "Failed to add workflow execution signaled event."} } } // Generate first decision task event if not child WF and no first decision task backoff return e.generateFirstDecisionTask( mutableState, startRequest.ParentExecutionInfo, startEvent, ) } func getTerminateIfRunningDetails(newRunID string) []byte { return []byte(fmt.Sprintf(TerminateIfRunningDetailsTemplate, newRunID)) } func (e *historyEngineImpl) applyWorkflowIDReusePolicyForSigWithStart( prevExecutionInfo *persistence.WorkflowExecutionInfo, execution types.WorkflowExecution, wfIDReusePolicy types.WorkflowIDReusePolicy, ) error { prevStartRequestID := prevExecutionInfo.CreateRequestID prevRunID := prevExecutionInfo.RunID prevState := prevExecutionInfo.State prevCloseState := prevExecutionInfo.CloseStatus return e.applyWorkflowIDReusePolicyHelper( prevStartRequestID, prevRunID, prevState, prevCloseState, execution, wfIDReusePolicy, ) } func (e *historyEngineImpl) applyWorkflowIDReusePolicyHelper( prevStartRequestID, prevRunID string, prevState int, prevCloseState int, execution types.WorkflowExecution, wfIDReusePolicy types.WorkflowIDReusePolicy, ) error { // here we know some information about the prev workflow, i.e. either running right now // or has history check if the workflow is finished switch prevState { case persistence.WorkflowStateCreated, persistence.WorkflowStateRunning: msg := "Workflow execution is already running. WorkflowId: %v, RunId: %v." return getWorkflowAlreadyStartedError(msg, prevStartRequestID, execution.GetWorkflowID(), prevRunID) case persistence.WorkflowStateCompleted: // previous workflow completed, proceed case persistence.WorkflowStateCorrupted: // ignore workflow ID reuse policy for corrupted workflows, treat as they do not exist return nil default: // persistence.WorkflowStateZombie or unknown type return &types.InternalServiceError{Message: fmt.Sprintf("Failed to process workflow, workflow has invalid state: %v.", prevState)} } switch wfIDReusePolicy { case types.WorkflowIDReusePolicyAllowDuplicateFailedOnly: if _, ok := FailedWorkflowCloseState[prevCloseState]; !ok { msg := "Workflow execution already finished successfully. WorkflowId: %v, RunId: %v. Workflow ID reuse policy: allow duplicate workflow ID if last run failed." return getWorkflowAlreadyStartedError(msg, prevStartRequestID, execution.GetWorkflowID(), prevRunID) } case types.WorkflowIDReusePolicyAllowDuplicate, types.WorkflowIDReusePolicyTerminateIfRunning: // no check need here case types.WorkflowIDReusePolicyRejectDuplicate: msg := "Workflow execution already finished. WorkflowId: %v, RunId: %v. Workflow ID reuse policy: reject duplicate workflow ID." return getWorkflowAlreadyStartedError(msg, prevStartRequestID, execution.GetWorkflowID(), prevRunID) default: return &types.InternalServiceError{Message: "Failed to process start workflow reuse policy."} } return nil } func getWorkflowAlreadyStartedError(errMsg string, createRequestID string, workflowID string, runID string) error { return &types.WorkflowExecutionAlreadyStartedError{ Message: fmt.Sprintf(errMsg, workflowID, runID), StartRequestID: createRequestID, RunID: runID, } } func (e *historyEngineImpl) newChildContext( parentCtx context.Context, ) (context.Context, context.CancelFunc) { ctxTimeout := contextLockTimeout if deadline, ok := parentCtx.Deadline(); ok { now := e.shard.GetTimeSource().Now() parentTimeout := deadline.Sub(now) if parentTimeout > 0 && parentTimeout < contextLockTimeout { ctxTimeout = parentTimeout } } return context.WithTimeout(context.Background(), ctxTimeout) } func (e *historyEngineImpl) createMutableState( ctx context.Context, domainEntry *cache.DomainCacheEntry, runID string, startRequest *types.HistoryStartWorkflowExecutionRequest, ) (execution.MutableState, error) { activeClusterInfo, err := e.shard.GetActiveClusterManager().GetActiveClusterInfoByClusterAttribute(ctx, domainEntry.GetInfo().ID, startRequest.StartRequest.ActiveClusterSelectionPolicy.GetClusterAttribute()) if err != nil { var errNotFound *activecluster.ClusterAttributeNotFoundError if !errors.As(err, &errNotFound) { // unexpected error return nil, err } e.logger.Warn("Failed to get active cluster info by cluster attribute, falling back to domain-level active cluster info", tag.Error(err)) return nil, errClusterAttributeNotFound } if activeClusterInfo.ActiveClusterName != e.currentClusterName { return nil, e.newDomainNotActiveError(domainEntry, activeClusterInfo.FailoverVersion) } newMutableState := execution.NewMutableStateBuilderWithVersionHistories( e.shard, e.logger, domainEntry, activeClusterInfo.FailoverVersion, ) if err := newMutableState.SetHistoryTree(runID); err != nil { return nil, err } return newMutableState, nil } func (e *historyEngineImpl) generateFirstDecisionTask( mutableState execution.MutableState, parentInfo *types.ParentExecutionInfo, startEvent *types.HistoryEvent, ) error { if parentInfo == nil { // DecisionTask is only created when it is not a Child Workflow and no backoff is needed if err := mutableState.AddFirstDecisionTaskScheduled( startEvent, ); err != nil { return err } } return nil } ================================================ FILE: service/history/engine/engineimpl/start_workflow_execution_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package engineimpl import ( "context" "errors" "testing" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine/testdata" "github.com/uber/cadence/service/history/events" ) func TestStartWorkflowExecution(t *testing.T) { tests := []struct { name string request *types.HistoryStartWorkflowExecutionRequest setupMocks func(*testing.T, *testdata.EngineForTest) wantErr bool }{ { name: "start workflow execution success", request: &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{Name: "workflow-type"}, TaskList: &types.TaskList{ Name: "default-task-list", }, Input: []byte("workflow input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), // 1 hour TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), // 10 seconds Identity: "workflow-starter", RequestID: "request-id-for-start", RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 2.0, MaximumIntervalInSeconds: 10, MaximumAttempts: 5, ExpirationIntervalInSeconds: 3600, // 1 hour }, Memo: &types.Memo{ Fields: map[string][]byte{ "key1": []byte("value1"), }, }, SearchAttributes: &types.SearchAttributes{ IndexedFields: map[string][]byte{ "CustomKeywordField": []byte("test"), }, }, }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { domainEntry := &cache.DomainCacheEntry{} eft.ShardCtx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainEntry, nil).AnyTimes() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, nil).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) eft.ShardCtx.Resource.ExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.CreateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() historyBranchResp := &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, }, } historyMgr := eft.ShardCtx.Resource.HistoryMgr historyMgr. On("ReadHistoryBranch", mock.Anything, mock.Anything). Return(historyBranchResp, nil). Once() eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() }, wantErr: false, }, { name: "failed to get workflow execution", request: &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowID: "workflow-id", Input: []byte("workflow input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), // 1 hour TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), // 10 seconds Identity: "workflow-starter", RequestID: "request-id-for-start", WorkflowType: &types.WorkflowType{Name: "workflow-type"}, }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { domainEntry := &cache.DomainCacheEntry{} eft.ShardCtx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainEntry, nil).AnyTimes() eft.ShardCtx.Resource.ExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, errors.New("internal error")).Once() }, wantErr: true, }, { name: "prev mutable state version conflict", request: &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowID: "workflow-id", Input: []byte("workflow input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), // 1 hour TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), TaskList: &types.TaskList{ Name: "default-task-list", }, Identity: "workflow-starter", RequestID: "request-id-for-start", WorkflowType: &types.WorkflowType{Name: "workflow-type"}, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { domainEntry := &cache.DomainCacheEntry{} eft.ShardCtx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainEntry, nil).AnyTimes() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, nil).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) eft.ShardCtx.Resource.ExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, errors.New("version conflict")).Once() eft.ShardCtx.Resource.ExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, errors.New("internal error")).Once() eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() }, wantErr: true, }, { name: "workflow ID reuse - terminate if running", request: &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowID: "workflow-id", Input: []byte("workflow input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), // 1 hour TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), TaskList: &types.TaskList{ Name: "default-task-list", }, Identity: "workflow-starter", RequestID: "request-id-for-start", WorkflowType: &types.WorkflowType{Name: "workflow-type"}, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyTerminateIfRunning.Ptr(), }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { domainEntry := &cache.DomainCacheEntry{} eft.ShardCtx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainEntry, nil).AnyTimes() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, nil).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) // Simulate the termination and recreation process eft.ShardCtx.Resource.ExecutionMgr.On("TerminateWorkflowExecution", mock.Anything, mock.Anything).Return(nil).Once() eft.ShardCtx.Resource.ExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.CreateWorkflowExecutionResponse{}, nil).Once() eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() }, wantErr: false, }, { name: "workflow ID reuse policy - reject duplicate", request: &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{Name: "workflow-type"}, TaskList: &types.TaskList{Name: "default-task-list"}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), // 1 hour TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), // 10 seconds Identity: "workflow-starter", RequestID: "request-id-for-start", WorkflowIDReusePolicy: types.WorkflowIDReusePolicyRejectDuplicate.Ptr(), }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { domainEntry := &cache.DomainCacheEntry{} eft.ShardCtx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainEntry, nil).AnyTimes() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, nil).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil).AnyTimes() eft.ShardCtx.Resource.ExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &persistence.WorkflowExecutionAlreadyStartedError{ StartRequestID: "existing-request-id", RunID: "existing-run-id", }).Once() eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() historyV2Mgr.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(nil).Once() }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) eft.Engine.Start() defer eft.Engine.Stop() tc.setupMocks(t, eft) _, err := eft.Engine.StartWorkflowExecution(context.Background(), tc.request) if (err != nil) != tc.wantErr { t.Fatalf("%s: StartWorkflowExecution() error = %v, wantErr %v", tc.name, err, tc.wantErr) } }) } } func TestStartWorkflowExecution_OrphanedHistoryCleanup(t *testing.T) { tests := []struct { name string request *types.HistoryStartWorkflowExecutionRequest setupMocks func(*testing.T, *testdata.EngineForTest) enableCleanupFlag bool expectHistoryCleanup bool wantErr bool }{ { name: "cleanup orphaned history on WorkflowExecutionAlreadyStartedError with flag enabled", request: &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{Name: "workflow-type"}, TaskList: &types.TaskList{Name: "default-task-list"}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "workflow-starter", RequestID: "request-id", }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { domainEntry := &cache.DomainCacheEntry{} eft.ShardCtx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainEntry, nil).AnyTimes() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, nil).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) var capturedBranchToken []byte historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Run(func(args mock.Arguments) { req := args.Get(1).(*persistence.AppendHistoryNodesRequest) capturedBranchToken = req.BranchToken }). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() eft.ShardCtx.Resource.ExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything). Return(nil, &persistence.WorkflowExecutionAlreadyStartedError{ StartRequestID: "different-request-id", RunID: "existing-run-id", State: persistence.WorkflowStateCompleted, }).Once() historyV2Mgr.On("DeleteHistoryBranch", mock.Anything, mock.MatchedBy(func(req *persistence.DeleteHistoryBranchRequest) bool { return assert.Equal(t, capturedBranchToken, req.BranchToken) && assert.Equal(t, constants.TestDomainName, req.DomainName) })). Return(nil).Once() }, enableCleanupFlag: true, expectHistoryCleanup: true, wantErr: true, }, { name: "no cleanup when flag disabled on WorkflowExecutionAlreadyStartedError", request: &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{Name: "workflow-type"}, TaskList: &types.TaskList{Name: "default-task-list"}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "workflow-starter", RequestID: "request-id", }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { domainEntry := &cache.DomainCacheEntry{} eft.ShardCtx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainEntry, nil).AnyTimes() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, nil).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() eft.ShardCtx.Resource.ExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything). Return(nil, &persistence.WorkflowExecutionAlreadyStartedError{ StartRequestID: "different-request-id", RunID: "existing-run-id", State: persistence.WorkflowStateCompleted, }).Once() }, enableCleanupFlag: false, expectHistoryCleanup: false, wantErr: true, }, { name: "no cleanup on DuplicateRequestError with WorkflowRequestTypeStart (returns success)", request: &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, StartRequest: &types.StartWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{Name: "workflow-type"}, TaskList: &types.TaskList{Name: "default-task-list"}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "workflow-starter", RequestID: "request-id", }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { domainEntry := &cache.DomainCacheEntry{} eft.ShardCtx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainEntry, nil).AnyTimes() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, nil).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) eft.ShardCtx.Resource.ExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything). Return(nil, &types.EntityNotExistsError{}).Once() historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() eft.ShardCtx.Resource.ExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything). Return(nil, &persistence.DuplicateRequestError{ RequestType: persistence.WorkflowRequestTypeStart, RunID: "existing-run-id", }).Once() // No DeleteHistoryBranch mock - cleanup doesn't happen for this case }, enableCleanupFlag: true, expectHistoryCleanup: false, // Cleanup doesn't happen because success is returned wantErr: false, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) eft.Engine.Start() defer eft.Engine.Stop() eft.ShardCtx.Resource.ShardMgr.On("UpdateShard", mock.Anything, mock.Anything).Return(nil) eft.ShardCtx.GetConfig().EnableCleanupOrphanedHistoryBranchOnWorkflowCreation = dynamicproperties.GetBoolPropertyFnFilteredByDomain(tc.enableCleanupFlag) tc.setupMocks(t, eft) _, err := eft.Engine.StartWorkflowExecution(context.Background(), tc.request) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } if tc.expectHistoryCleanup { eft.ShardCtx.Resource.HistoryMgr.AssertCalled(t, "DeleteHistoryBranch", mock.Anything, mock.MatchedBy(func(req *persistence.DeleteHistoryBranchRequest) bool { return req.BranchToken != nil })) } else { eft.ShardCtx.Resource.HistoryMgr.AssertNotCalled(t, "DeleteHistoryBranch", mock.Anything, mock.AnythingOfType("*persistence.DeleteHistoryBranchRequest")) } }) } } func TestSignalWithStartWorkflowExecution(t *testing.T) { tests := []struct { name string setupMocks func(*testing.T, *testdata.EngineForTest) request *types.HistorySignalWithStartWorkflowExecutionRequest wantErr bool }{ { name: "signal and start workflow successfully", request: &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowID: "workflow-id", WorkflowType: &types.WorkflowType{Name: "workflow-type"}, SignalName: "signal-name", ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), // 1 hour TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), TaskList: &types.TaskList{ Name: "default-task-list", }, RequestID: "request-id-for-start", SignalInput: []byte("signal-input"), Identity: "tester", }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { domainEntry := &cache.DomainCacheEntry{} eft.ShardCtx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainEntry, nil).AnyTimes() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, nil).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) // Mock GetCurrentExecution to simulate a non-existent current execution getCurrentExecReq := &persistence.GetCurrentExecutionRequest{ DomainID: constants.TestDomainID, WorkflowID: "workflow-id", DomainName: constants.TestDomainName, } getCurrentExecResp := &persistence.GetCurrentExecutionResponse{ RunID: "", // No current run ID indicates no current execution State: persistence.WorkflowStateCompleted, CloseStatus: persistence.WorkflowCloseStatusCompleted, } eft.ShardCtx.Resource.ExecutionMgr.On("GetCurrentExecution", mock.Anything, getCurrentExecReq).Return(getCurrentExecResp, &types.EntityNotExistsError{}).Once() eft.ShardCtx.Resource.ExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.CreateWorkflowExecutionResponse{}, nil) eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() }, wantErr: false, }, { name: "terminate existing and start new workflow", request: &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, WorkflowType: &types.WorkflowType{Name: "workflow-type"}, SignalName: "signal-name", ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(3600), // 1 hour TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), TaskList: &types.TaskList{ Name: "default-task-list", }, RequestID: "request-id-for-start", SignalInput: []byte("signal-input"), Identity: "tester", WorkflowIDReusePolicy: (*types.WorkflowIDReusePolicy)(common.Int32Ptr(3)), }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { domainEntry := &cache.DomainCacheEntry{} eft.ShardCtx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainEntry, nil).AnyTimes() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), constants.TestDomainID, nil).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) // Simulate current workflow execution is running getCurrentExecReq := &persistence.GetCurrentExecutionRequest{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, DomainName: constants.TestDomainName, } getCurrentExecResp := &persistence.GetCurrentExecutionResponse{ RunID: constants.TestRunID, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, } eft.ShardCtx.Resource.ExecutionMgr.On("GetCurrentExecution", mock.Anything, getCurrentExecReq).Return(getCurrentExecResp, nil).Once() getExecReq := &persistence.GetWorkflowExecutionRequest{ DomainID: constants.TestDomainID, Execution: types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, DomainName: constants.TestDomainName, RangeID: 1, } getExecResp := &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("GetWorkflowExecution", mock.Anything, getExecReq). Return(getExecResp, nil). Once() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil).AnyTimes() var _ *persistence.UpdateWorkflowExecutionRequest updateExecResp := &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { var ok bool _, ok = args.Get(1).(*persistence.UpdateWorkflowExecutionRequest) if !ok { t.Fatalf("failed to cast input to *persistence.UpdateWorkflowExecutionRequest, type is %T", args.Get(1)) } }). Return(updateExecResp, nil). Once() // Expect termination of the current workflow eft.ShardCtx.Resource.ExecutionMgr.On("TerminateWorkflowExecution", mock.Anything, mock.Anything).Return(nil).Once() // Expect creation of a new workflow execution eft.ShardCtx.Resource.ExecutionMgr.On("CreateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.CreateWorkflowExecutionResponse{}, nil).Once() // Mocking additional interactions required by the workflow context and execution eft.ShardCtx.Resource.ShardMgr.On("UpdateShard", mock.Anything, mock.Anything).Return(nil) historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")).Return(&persistence.AppendHistoryNodesResponse{}, nil) }, wantErr: false, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) eft.Engine.Start() defer eft.Engine.Stop() tc.setupMocks(t, eft) response, err := eft.Engine.SignalWithStartWorkflowExecution(context.Background(), tc.request) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.NotNil(t, response) } }) } } func TestCreateMutableState(t *testing.T) { tests := []struct { name string domainEntry *cache.DomainCacheEntry mockFn func(ac *activecluster.MockManager) wantErr bool wantVersion int64 wantErrMessage string }{ { name: "create mutable state successfully, failover version is looked up from active cluster manager", domainEntry: getDomainCacheEntry( 0, &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster1", FailoverVersion: 0, }, "us-east": { ActiveClusterName: "cluster2", FailoverVersion: 2, }, }, }, }, }), mockFn: func(ac *activecluster.MockManager) { ac.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ActiveClusterInfo{ ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: 125, }, nil) }, wantVersion: 125, }, { name: "failed to create mutable state, current cluster is not the active cluster", domainEntry: getDomainCacheEntry( 0, &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster1", FailoverVersion: 0, }, "us-east": { ActiveClusterName: "cluster2", FailoverVersion: 2, }, }, }, }, }), mockFn: func(ac *activecluster.MockManager) { ac.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ActiveClusterInfo{ ActiveClusterName: cluster.TestAlternativeClusterName, FailoverVersion: 125, }, nil) }, wantErr: true, wantErrMessage: "is active in cluster(s): [cluster1 cluster2]", }, { name: "failed to create mutable state. GetActiveClusterInfoByClusterAttribute failed", domainEntry: getDomainCacheEntry( 0, &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster1", FailoverVersion: 0, }, "us-east": { ActiveClusterName: "cluster2", FailoverVersion: 2, }, }, }, }, }), mockFn: func(ac *activecluster.MockManager) { ac.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, errors.New("some error")) }, wantErr: true, wantErrMessage: "some error", }, { name: "failed to create mutable state, cluster attribute not found", domainEntry: getDomainCacheEntry( 0, &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster1", FailoverVersion: 0, }, "us-east": { ActiveClusterName: "cluster2", FailoverVersion: 2, }, }, }, }, }), mockFn: func(ac *activecluster.MockManager) { ac.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, &activecluster.ClusterAttributeNotFoundError{}) }, wantErr: true, wantErrMessage: "Cannot start workflow with a cluster attribute that is not found in the domain's metadata.", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) eft.Engine.Start() defer eft.Engine.Stop() engine := eft.Engine.(*historyEngineImpl) if tc.mockFn != nil { tc.mockFn(eft.ShardCtx.Resource.ActiveClusterMgr) } mutableState, err := engine.createMutableState( context.Background(), tc.domainEntry, "rid", &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, ) if tc.wantErr { assert.Error(t, err) assert.Contains(t, err.Error(), tc.wantErrMessage) } else { assert.NoError(t, err) assert.NotNil(t, mutableState) } if err != nil { return } gotVer := mutableState.GetCurrentVersion() assert.Equal(t, tc.wantVersion, gotVer) }) } } func TestHandleCreateWorkflowExecutionFailureCleanup(t *testing.T) { // Known error types that should trigger cleanup knownCleanupError := &persistence.WorkflowExecutionAlreadyStartedError{ Msg: "workflow already started", } tests := []struct { name string enableCleanupFlag bool enableUninitializedRecordFlag bool err error workflowExecution *types.WorkflowExecution historyBlob []byte startRequest *types.HistoryStartWorkflowExecutionRequest setupMocks func(*testdata.EngineForTest) expectDeleteHistoryBranch bool expectDeleteVisibility bool }{ { name: "workflowExecution is nil - returns early", enableCleanupFlag: true, err: knownCleanupError, workflowExecution: nil, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) {}, expectDeleteHistoryBranch: false, expectDeleteVisibility: false, }, { name: "historyBlob is nil - returns early", enableCleanupFlag: true, err: knownCleanupError, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "run-id", }, historyBlob: nil, startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) {}, expectDeleteHistoryBranch: false, expectDeleteVisibility: false, }, { name: "timeout error - returns early without cleanup", enableCleanupFlag: true, err: &persistence.TimeoutError{Msg: "timeout"}, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "run-id", }, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) {}, expectDeleteHistoryBranch: false, expectDeleteVisibility: false, }, { name: "unknown error - returns early without cleanup", enableCleanupFlag: true, err: errors.New("some unknown error"), workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "run-id", }, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) {}, expectDeleteHistoryBranch: false, expectDeleteVisibility: false, }, { name: "cleanup disabled - returns early", enableCleanupFlag: false, err: knownCleanupError, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "run-id", }, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) {}, expectDeleteHistoryBranch: false, expectDeleteVisibility: false, }, { name: "startRequest is nil - returns early with bug log", enableCleanupFlag: true, err: knownCleanupError, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "run-id", }, historyBlob: []byte("branch-token"), startRequest: nil, setupMocks: func(eft *testdata.EngineForTest) {}, expectDeleteHistoryBranch: false, expectDeleteVisibility: false, }, { name: "workflowID is empty - returns early with bug log", enableCleanupFlag: true, err: knownCleanupError, workflowExecution: &types.WorkflowExecution{ WorkflowID: "", RunID: "run-id", }, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) {}, expectDeleteHistoryBranch: false, expectDeleteVisibility: false, }, { name: "runID is empty - returns early with bug log", enableCleanupFlag: true, err: knownCleanupError, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "", }, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) {}, expectDeleteHistoryBranch: false, expectDeleteVisibility: false, }, { name: "cleanup path with WorkflowExecutionAlreadyStartedError - deletes history branch successfully", enableCleanupFlag: true, err: knownCleanupError, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "run-id", }, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) { eft.ShardCtx.Resource.HistoryMgr.On("DeleteHistoryBranch", mock.Anything, mock.MatchedBy(func(req *persistence.DeleteHistoryBranchRequest) bool { return string(req.BranchToken) == "branch-token" && req.DomainName == constants.TestDomainName })).Return(nil).Once() }, expectDeleteHistoryBranch: true, expectDeleteVisibility: false, }, { name: "cleanup path with DuplicateRequestError - deletes history branch successfully", enableCleanupFlag: true, err: &persistence.DuplicateRequestError{ RequestType: persistence.WorkflowRequestTypeStart, RunID: "existing-run-id", }, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "run-id", }, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) { eft.ShardCtx.Resource.HistoryMgr.On("DeleteHistoryBranch", mock.Anything, mock.AnythingOfType("*persistence.DeleteHistoryBranchRequest")). Return(nil).Once() }, expectDeleteHistoryBranch: true, expectDeleteVisibility: false, }, { name: "cleanup path - delete history branch fails gracefully", enableCleanupFlag: true, err: knownCleanupError, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "run-id", }, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) { eft.ShardCtx.Resource.HistoryMgr.On("DeleteHistoryBranch", mock.Anything, mock.AnythingOfType("*persistence.DeleteHistoryBranchRequest")). Return(errors.New("delete failed")).Once() }, expectDeleteHistoryBranch: true, expectDeleteVisibility: false, }, { name: "cleanup path with visibility - deletes both history and visibility", enableCleanupFlag: true, enableUninitializedRecordFlag: true, err: knownCleanupError, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "run-id", }, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) { eft.ShardCtx.Resource.HistoryMgr.On("DeleteHistoryBranch", mock.Anything, mock.AnythingOfType("*persistence.DeleteHistoryBranchRequest")). Return(nil).Once() eft.ShardCtx.Resource.VisibilityMgr.On("DeleteWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.VisibilityDeleteWorkflowExecutionRequest) bool { return req.WorkflowID == "wf-id" && req.RunID == "run-id" && req.Domain == constants.TestDomainName })).Return(nil).Once() }, expectDeleteHistoryBranch: true, expectDeleteVisibility: true, }, { name: "cleanup path with visibility - visibility delete fails gracefully", enableCleanupFlag: true, enableUninitializedRecordFlag: true, err: knownCleanupError, workflowExecution: &types.WorkflowExecution{ WorkflowID: "wf-id", RunID: "run-id", }, historyBlob: []byte("branch-token"), startRequest: &types.HistoryStartWorkflowExecutionRequest{ StartRequest: &types.StartWorkflowExecutionRequest{}, }, setupMocks: func(eft *testdata.EngineForTest) { eft.ShardCtx.Resource.HistoryMgr.On("DeleteHistoryBranch", mock.Anything, mock.AnythingOfType("*persistence.DeleteHistoryBranchRequest")). Return(nil).Once() eft.ShardCtx.Resource.VisibilityMgr.On("DeleteWorkflowExecution", mock.Anything, mock.AnythingOfType("*persistence.VisibilityDeleteWorkflowExecutionRequest")). Return(errors.New("visibility delete failed")).Once() }, expectDeleteHistoryBranch: true, expectDeleteVisibility: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) eft.Engine.Start() defer eft.Engine.Stop() // Configure flags eft.ShardCtx.GetConfig().EnableCleanupOrphanedHistoryBranchOnWorkflowCreation = dynamicproperties.GetBoolPropertyFnFilteredByDomain(tc.enableCleanupFlag) eft.ShardCtx.GetConfig().EnableRecordWorkflowExecutionUninitialized = dynamicproperties.GetBoolPropertyFnFilteredByDomain(tc.enableUninitializedRecordFlag) // Setup mocks tc.setupMocks(eft) // Get the engine implementation engine := eft.Engine.(*historyEngineImpl) // Create domain entry domainEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ ID: constants.TestDomainID, Name: constants.TestDomainName, }, nil, true, nil, 0, nil, 1, 1, 1, ) // Create history blob if provided var historyBlob *events.PersistedBlob if tc.historyBlob != nil { historyBlob = &events.PersistedBlob{ BranchToken: tc.historyBlob, } } // Call the function engine.handleCreateWorkflowExecutionFailureCleanup( context.Background(), tc.startRequest, domainEntry, tc.workflowExecution, historyBlob, false, tc.err, ) // Assert mocks if tc.expectDeleteHistoryBranch { eft.ShardCtx.Resource.HistoryMgr.AssertCalled(t, "DeleteHistoryBranch", mock.Anything, mock.AnythingOfType("*persistence.DeleteHistoryBranchRequest")) } else { eft.ShardCtx.Resource.HistoryMgr.AssertNotCalled(t, "DeleteHistoryBranch", mock.Anything, mock.AnythingOfType("*persistence.DeleteHistoryBranchRequest")) } if tc.expectDeleteVisibility { eft.ShardCtx.Resource.VisibilityMgr.AssertCalled(t, "DeleteWorkflowExecution", mock.Anything, mock.AnythingOfType("*persistence.VisibilityDeleteWorkflowExecutionRequest")) } else if tc.enableUninitializedRecordFlag { // Only assert not called if the flag was enabled (otherwise it wouldn't be called anyway) eft.ShardCtx.Resource.VisibilityMgr.AssertNotCalled(t, "DeleteWorkflowExecution", mock.Anything, mock.AnythingOfType("*persistence.VisibilityDeleteWorkflowExecutionRequest")) } }) } } func getDomainCacheEntry(domainFailoverVersion int64, cfg *types.ActiveClusters) *cache.DomainCacheEntry { // only thing we care in domain cache entry is the active clusters config return cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ ID: "domain-id", }, nil, true, &persistence.DomainReplicationConfig{ ActiveClusters: cfg, ActiveClusterName: "cluster0", }, domainFailoverVersion, nil, 1, 1, 1, ) } ================================================ FILE: service/history/engine/engineimpl/terminate_workflow_execution.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package engineimpl import ( "context" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/workflow" ) func (e *historyEngineImpl) TerminateWorkflowExecution( ctx context.Context, terminateRequest *types.HistoryTerminateWorkflowExecutionRequest, ) error { request := terminateRequest.TerminateRequest parentExecution := terminateRequest.ExternalWorkflowExecution childWorkflowOnly := terminateRequest.GetChildWorkflowOnly() workflowExecution := types.WorkflowExecution{ WorkflowID: request.WorkflowExecution.WorkflowID, } // If firstExecutionRunID is set on the request always try to cancel currently running execution if request.GetFirstExecutionRunID() == "" { workflowExecution.RunID = request.WorkflowExecution.RunID } domainEntry, err := e.getActiveDomainByWorkflow(ctx, terminateRequest.DomainUUID, workflowExecution.WorkflowID, workflowExecution.RunID) if err != nil { return err } domainID := domainEntry.GetInfo().ID return workflow.UpdateCurrentWithActionFunc( ctx, e.logger, e.executionCache, e.executionManager, domainID, e.shard.GetDomainCache(), workflowExecution, e.timeSource.Now(), func(wfContext execution.Context, mutableState execution.MutableState) (*workflow.UpdateAction, error) { if !mutableState.IsWorkflowExecutionRunning() { return nil, workflow.ErrAlreadyCompleted } executionInfo := mutableState.GetExecutionInfo() if request.GetFirstExecutionRunID() != "" { firstRunID := executionInfo.FirstExecutionRunID if firstRunID == "" { // This is needed for backwards compatibility. Workflow execution create with Cadence release v0.25.0 or earlier // does not have FirstExecutionRunID stored as part of mutable state. If this is not set then load it from // workflow execution started event. startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return nil, err } firstRunID = startEvent.GetWorkflowExecutionStartedEventAttributes().GetFirstExecutionRunID() } if request.GetFirstExecutionRunID() != firstRunID { return nil, &types.EntityNotExistsError{Message: "Workflow execution not found"} } } if childWorkflowOnly { parentWorkflowID := executionInfo.ParentWorkflowID parentRunID := executionInfo.ParentRunID if parentExecution.GetWorkflowID() != parentWorkflowID || parentExecution.GetRunID() != parentRunID { return nil, workflow.ErrParentMismatch } } eventBatchFirstEventID := mutableState.GetNextEventID() return workflow.UpdateWithoutDecision, execution.TerminateWorkflow( mutableState, eventBatchFirstEventID, request.GetReason(), request.GetDetails(), request.GetIdentity(), ) }) } ================================================ FILE: service/history/engine/engineimpl/terminate_workflow_execution_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package engineimpl import ( "context" "errors" "testing" "github.com/stretchr/testify/mock" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine/testdata" ) func TestTerminateWorkflowExecution(t *testing.T) { tests := []struct { name string terminationRequest types.HistoryTerminateWorkflowExecutionRequest setupMocks func(*testing.T, *testdata.EngineForTest) wantErr bool }{ { name: "domain is not active", terminationRequest: types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, ChildWorkflowOnly: true, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, Reason: "Test termination", Identity: "testRunner", // Specifically testing child workflow scenario }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: "aaa"}, nil) }, wantErr: true, }, { name: "runid is not uuid", terminationRequest: types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: "not-a-uuid"}, }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, "not-a-uuid").Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) eft.ShardCtx.Resource.ExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything). Return(nil, errors.New("invalid UUID")).Once() }, wantErr: true, }, { name: "failed to get workflow execution", terminationRequest: types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) getExecReq := &persistence.GetWorkflowExecutionRequest{ DomainID: constants.TestDomainID, Execution: types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, DomainName: constants.TestDomainName, RangeID: 1, } eft.ShardCtx.Resource.ExecutionMgr.On("GetWorkflowExecution", mock.Anything, getExecReq). Return(nil, errors.New("some random error")).Once() }, wantErr: true, }, { name: "child workflow parent mismatch", terminationRequest: types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, ChildWorkflowOnly: true, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, Reason: "Test termination", Identity: "testRunner", // Specifically testing child workflow scenario }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { // Mock the retrieval of the workflow execution details getExecReq := &persistence.GetWorkflowExecutionRequest{ DomainID: constants.TestDomainID, Execution: types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, DomainName: constants.TestDomainName, RangeID: 1, } getExecResp := &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ParentWorkflowID: "other-parent-id", ParentRunID: "other-parent-runid", }, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("GetWorkflowExecution", mock.Anything, getExecReq). Return(getExecResp, nil).Once() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil).AnyTimes() // Mock the retrieval of the workflow's history branch historyBranchResp := &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, }, } historyMgr := eft.ShardCtx.Resource.HistoryMgr historyMgr. On("ReadHistoryBranch", mock.Anything, mock.Anything). Return(historyBranchResp, nil). Once() // Mock the update of the workflow execution var _ *persistence.UpdateWorkflowExecutionRequest updateExecResp := &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Return(updateExecResp, &types.EntityNotExistsError{Message: "Workflow execution not found due to parent mismatch"}). Once() // Mock the update of the shard's range ID eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) // Mock appending history nodes historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() }, wantErr: true, }, { name: "valid first execution run ID", terminationRequest: types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, Reason: "Test termination", Identity: "testRunner", FirstExecutionRunID: "", }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { getExecReq := &persistence.GetWorkflowExecutionRequest{ DomainID: constants.TestDomainID, Execution: types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, DomainName: constants.TestDomainName, RangeID: 1, } getExecResp := &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, FirstExecutionRunID: "", }, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("GetWorkflowExecution", mock.Anything, getExecReq). Return(getExecResp, nil).Once() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil).AnyTimes() eft.ShardCtx.Resource.ExecutionMgr. On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Return(&persistence.UpdateWorkflowExecutionResponse{}, nil). Once() // Mock GetCurrentExecution call getCurrentExecReq := &persistence.GetCurrentExecutionRequest{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, DomainName: constants.TestDomainName, } getCurrentExecResp := &persistence.GetCurrentExecutionResponse{ RunID: constants.TestRunID, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, } eft.ShardCtx.Resource.ExecutionMgr. On("GetCurrentExecution", mock.Anything, getCurrentExecReq). Return(getCurrentExecResp, nil).Once() // Mock the retrieval of the workflow's history branch historyBranchResp := &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstExecutionRunID: "fetched-first-run-id", // The ID to be fetched }, }, }, } historyMgr := eft.ShardCtx.Resource.HistoryMgr historyMgr. On("ReadHistoryBranch", mock.Anything, mock.Anything). Return(historyBranchResp, nil). Once() eft.ShardCtx.Resource.HistoryMgr. On("ReadHistoryBranch", mock.Anything, mock.Anything). Return(historyBranchResp, nil). Once() // Mock the update of the workflow execution var _ *persistence.UpdateWorkflowExecutionRequest updateExecResp := &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Return(updateExecResp, &types.EntityNotExistsError{Message: "Workflow execution not found due to parent mismatch"}). Once() // Mock the update of the shard's range ID eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) // Mock appending history nodes historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() }, wantErr: false, }, { name: "successful termination of a running workflow", terminationRequest: types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, Reason: "Test termination", Identity: "testRunner", }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { getExecReq := &persistence.GetWorkflowExecutionRequest{ DomainID: constants.TestDomainID, Execution: types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, DomainName: constants.TestDomainName, RangeID: 1, } getExecResp := &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("GetWorkflowExecution", mock.Anything, getExecReq). Return(getExecResp, nil). Once() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil).AnyTimes() // ReadHistoryBranch prep historyBranchResp := &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ // first event. { ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, }, } historyMgr := eft.ShardCtx.Resource.HistoryMgr historyMgr. On("ReadHistoryBranch", mock.Anything, mock.Anything). Return(historyBranchResp, nil). Once() var _ *persistence.UpdateWorkflowExecutionRequest updateExecResp := &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { var ok bool _, ok = args.Get(1).(*persistence.UpdateWorkflowExecutionRequest) if !ok { t.Fatalf("failed to cast input to *persistence.UpdateWorkflowExecutionRequest, type is %T", args.Get(1)) } }). Return(updateExecResp, nil). Once() // UpdateShard prep. this is needed to update the shard's rangeID for failure cases. eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() // Adjust the return values based on your test case needs }, wantErr: false, }, { name: "first execution run ID matches", terminationRequest: types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, Reason: "Test termination", Identity: "testRunner", FirstExecutionRunID: "matching-first-run-id", }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { getExecReq := &persistence.GetWorkflowExecutionRequest{ DomainID: constants.TestDomainID, Execution: types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, DomainName: constants.TestDomainName, RangeID: 1, } getExecResp := &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, FirstExecutionRunID: "matching-first-run-id", }, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("GetWorkflowExecution", mock.Anything, getExecReq). Return(getExecResp, nil).Once() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, "").Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil).AnyTimes() getCurrentExecReq := &persistence.GetCurrentExecutionRequest{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, DomainName: constants.TestDomainName, } getCurrentExecResp := &persistence.GetCurrentExecutionResponse{ RunID: constants.TestRunID, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, } eft.ShardCtx.Resource.ExecutionMgr. On("GetCurrentExecution", mock.Anything, getCurrentExecReq). Return(getCurrentExecResp, nil).Once() historyMgr := eft.ShardCtx.Resource.HistoryMgr historyMgr. On("ReadHistoryBranch", mock.Anything, mock.Anything). Return(&persistence.ReadHistoryBranchResponse{}, nil). Once() updateExecResp := &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Return(updateExecResp, nil).Once() eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() }, wantErr: false, }, { name: "first execution run ID does not match", terminationRequest: types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, Reason: "Test termination", Identity: "testRunner", FirstExecutionRunID: "non-matching-first-run-id", }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { getExecReq := &persistence.GetWorkflowExecutionRequest{ DomainID: constants.TestDomainID, Execution: types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, DomainName: constants.TestDomainName, RangeID: 1, } getExecResp := &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, FirstExecutionRunID: "matching-first-run-id", }, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("GetWorkflowExecution", mock.Anything, getExecReq). Return(getExecResp, nil).Once() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, "").Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil).AnyTimes() getCurrentExecReq := &persistence.GetCurrentExecutionRequest{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, DomainName: constants.TestDomainName, } getCurrentExecResp := &persistence.GetCurrentExecutionResponse{ RunID: constants.TestRunID, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, } eft.ShardCtx.Resource.ExecutionMgr. On("GetCurrentExecution", mock.Anything, getCurrentExecReq). Return(getCurrentExecResp, nil).Once() historyMgr := eft.ShardCtx.Resource.HistoryMgr historyMgr. On("ReadHistoryBranch", mock.Anything, mock.Anything). Return(&persistence.ReadHistoryBranchResponse{}, nil). Once() eft.ShardCtx.Resource.ExecutionMgr. On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Return(&persistence.UpdateWorkflowExecutionResponse{}, nil).Once() }, wantErr: true, }, { name: "load first execution run ID from start event", terminationRequest: types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, Reason: "Test termination", Identity: "testRunner", FirstExecutionRunID: "fetched-first-run-id", }, }, setupMocks: func(t *testing.T, eft *testdata.EngineForTest) { getExecReq := &persistence.GetWorkflowExecutionRequest{ DomainID: constants.TestDomainID, Execution: types.WorkflowExecution{WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID}, DomainName: constants.TestDomainName, RangeID: 1, } getExecResp := &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, FirstExecutionRunID: "", }, ExecutionStats: &persistence.ExecutionStats{}, }, MutableStateStats: &persistence.MutableStateStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("GetWorkflowExecution", mock.Anything, getExecReq). Return(getExecResp, nil).Once() eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, "").Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) eft.ShardCtx.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil).AnyTimes() getCurrentExecReq := &persistence.GetCurrentExecutionRequest{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, DomainName: constants.TestDomainName, } getCurrentExecResp := &persistence.GetCurrentExecutionResponse{ RunID: constants.TestRunID, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, } eft.ShardCtx.Resource.ExecutionMgr. On("GetCurrentExecution", mock.Anything, getCurrentExecReq). Return(getCurrentExecResp, nil).Once() historyBranchResp := &persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ { ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstExecutionRunID: "fetched-first-run-id", }, }, }, } historyMgr := eft.ShardCtx.Resource.HistoryMgr historyMgr. On("ReadHistoryBranch", mock.Anything, mock.Anything). Return(historyBranchResp, nil). Once() updateExecResp := &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}, } eft.ShardCtx.Resource.ExecutionMgr. On("UpdateWorkflowExecution", mock.Anything, mock.Anything). Return(updateExecResp, nil).Once() eft.ShardCtx.Resource.ShardMgr. On("UpdateShard", mock.Anything, mock.Anything). Return(nil) historyV2Mgr := eft.ShardCtx.Resource.HistoryMgr historyV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.AnythingOfType("*persistence.AppendHistoryNodesRequest")). Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() }, wantErr: false, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { eft := testdata.NewEngineForTest(t, NewEngineWithShardContext) eft.Engine.Start() defer eft.Engine.Stop() tc.setupMocks(t, eft) err := eft.Engine.TerminateWorkflowExecution( context.Background(), &tc.terminationRequest, ) if (err != nil) != tc.wantErr { t.Fatalf("TerminateWorkflowExecution() error = %v, wantErr %v", err, tc.wantErr) } }) } } ================================================ FILE: service/history/engine/interface.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go package engine import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/events" ) type ( // Engine represents an interface for managing workflow execution history. Engine interface { common.Daemon StartWorkflowExecution(ctx context.Context, request *types.HistoryStartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) GetMutableState(ctx context.Context, request *types.GetMutableStateRequest) (*types.GetMutableStateResponse, error) PollMutableState(ctx context.Context, request *types.PollMutableStateRequest) (*types.PollMutableStateResponse, error) DescribeMutableState(ctx context.Context, request *types.DescribeMutableStateRequest) (*types.DescribeMutableStateResponse, error) ResetStickyTaskList(ctx context.Context, resetRequest *types.HistoryResetStickyTaskListRequest) (*types.HistoryResetStickyTaskListResponse, error) DescribeWorkflowExecution(ctx context.Context, request *types.HistoryDescribeWorkflowExecutionRequest) (*types.DescribeWorkflowExecutionResponse, error) RecordDecisionTaskStarted(ctx context.Context, request *types.RecordDecisionTaskStartedRequest) (*types.RecordDecisionTaskStartedResponse, error) RecordActivityTaskStarted(ctx context.Context, request *types.RecordActivityTaskStartedRequest) (*types.RecordActivityTaskStartedResponse, error) RespondDecisionTaskCompleted(ctx context.Context, request *types.HistoryRespondDecisionTaskCompletedRequest) (*types.HistoryRespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed(ctx context.Context, request *types.HistoryRespondDecisionTaskFailedRequest) error RespondActivityTaskCompleted(ctx context.Context, request *types.HistoryRespondActivityTaskCompletedRequest) error RespondActivityTaskFailed(ctx context.Context, request *types.HistoryRespondActivityTaskFailedRequest) error RespondActivityTaskCanceled(ctx context.Context, request *types.HistoryRespondActivityTaskCanceledRequest) error RecordActivityTaskHeartbeat(ctx context.Context, request *types.HistoryRecordActivityTaskHeartbeatRequest) (*types.RecordActivityTaskHeartbeatResponse, error) RequestCancelWorkflowExecution(ctx context.Context, request *types.HistoryRequestCancelWorkflowExecutionRequest) error SignalWorkflowExecution(ctx context.Context, request *types.HistorySignalWorkflowExecutionRequest) error SignalWithStartWorkflowExecution(ctx context.Context, request *types.HistorySignalWithStartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) RemoveSignalMutableState(ctx context.Context, request *types.RemoveSignalMutableStateRequest) error TerminateWorkflowExecution(ctx context.Context, request *types.HistoryTerminateWorkflowExecutionRequest) error ResetWorkflowExecution(ctx context.Context, request *types.HistoryResetWorkflowExecutionRequest) (*types.ResetWorkflowExecutionResponse, error) ScheduleDecisionTask(ctx context.Context, request *types.ScheduleDecisionTaskRequest) error RecordChildExecutionCompleted(ctx context.Context, request *types.RecordChildExecutionCompletedRequest) error ReplicateEventsV2(ctx context.Context, request *types.ReplicateEventsV2Request) error SyncShardStatus(ctx context.Context, request *types.SyncShardStatusRequest) error SyncActivity(ctx context.Context, request *types.SyncActivityRequest) error GetReplicationMessages(ctx context.Context, pollingCluster string, lastReadMessageID int64) (*types.ReplicationMessages, error) GetDLQReplicationMessages(ctx context.Context, taskInfos []*types.ReplicationTaskInfo) ([]*types.ReplicationTask, error) QueryWorkflow(ctx context.Context, request *types.HistoryQueryWorkflowRequest) (*types.HistoryQueryWorkflowResponse, error) ReapplyEvents(ctx context.Context, domainUUID string, workflowID string, runID string, events []*types.HistoryEvent) error CountDLQMessages(ctx context.Context, forceFetch bool) (map[string]int64, error) ReadDLQMessages(ctx context.Context, messagesRequest *types.ReadDLQMessagesRequest) (*types.ReadDLQMessagesResponse, error) PurgeDLQMessages(ctx context.Context, messagesRequest *types.PurgeDLQMessagesRequest) error MergeDLQMessages(ctx context.Context, messagesRequest *types.MergeDLQMessagesRequest) (*types.MergeDLQMessagesResponse, error) RefreshWorkflowTasks(ctx context.Context, domainUUID string, execution types.WorkflowExecution) error ResetTransferQueue(ctx context.Context, clusterName string) error ResetTimerQueue(ctx context.Context, clusterName string) error DescribeTransferQueue(ctx context.Context, clusterName string) (*types.DescribeQueueResponse, error) DescribeTimerQueue(ctx context.Context, clusterName string) (*types.DescribeQueueResponse, error) NotifyNewHistoryEvent(event *events.Notification) NotifyNewTransferTasks(info *hcommon.NotifyTaskInfo) NotifyNewTimerTasks(info *hcommon.NotifyTaskInfo) NotifyNewReplicationTasks(info *hcommon.NotifyTaskInfo) } ) ================================================ FILE: service/history/engine/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package engine -source interface.go -destination interface_mock.go // // Package engine is a generated GoMock package. package engine import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" common "github.com/uber/cadence/service/history/common" events "github.com/uber/cadence/service/history/events" ) // MockEngine is a mock of Engine interface. type MockEngine struct { ctrl *gomock.Controller recorder *MockEngineMockRecorder isgomock struct{} } // MockEngineMockRecorder is the mock recorder for MockEngine. type MockEngineMockRecorder struct { mock *MockEngine } // NewMockEngine creates a new mock instance. func NewMockEngine(ctrl *gomock.Controller) *MockEngine { mock := &MockEngine{ctrl: ctrl} mock.recorder = &MockEngineMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockEngine) EXPECT() *MockEngineMockRecorder { return m.recorder } // CountDLQMessages mocks base method. func (m *MockEngine) CountDLQMessages(ctx context.Context, forceFetch bool) (map[string]int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CountDLQMessages", ctx, forceFetch) ret0, _ := ret[0].(map[string]int64) ret1, _ := ret[1].(error) return ret0, ret1 } // CountDLQMessages indicates an expected call of CountDLQMessages. func (mr *MockEngineMockRecorder) CountDLQMessages(ctx, forceFetch any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountDLQMessages", reflect.TypeOf((*MockEngine)(nil).CountDLQMessages), ctx, forceFetch) } // DescribeMutableState mocks base method. func (m *MockEngine) DescribeMutableState(ctx context.Context, request *types.DescribeMutableStateRequest) (*types.DescribeMutableStateResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeMutableState", ctx, request) ret0, _ := ret[0].(*types.DescribeMutableStateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeMutableState indicates an expected call of DescribeMutableState. func (mr *MockEngineMockRecorder) DescribeMutableState(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeMutableState", reflect.TypeOf((*MockEngine)(nil).DescribeMutableState), ctx, request) } // DescribeTimerQueue mocks base method. func (m *MockEngine) DescribeTimerQueue(ctx context.Context, clusterName string) (*types.DescribeQueueResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeTimerQueue", ctx, clusterName) ret0, _ := ret[0].(*types.DescribeQueueResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeTimerQueue indicates an expected call of DescribeTimerQueue. func (mr *MockEngineMockRecorder) DescribeTimerQueue(ctx, clusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTimerQueue", reflect.TypeOf((*MockEngine)(nil).DescribeTimerQueue), ctx, clusterName) } // DescribeTransferQueue mocks base method. func (m *MockEngine) DescribeTransferQueue(ctx context.Context, clusterName string) (*types.DescribeQueueResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeTransferQueue", ctx, clusterName) ret0, _ := ret[0].(*types.DescribeQueueResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeTransferQueue indicates an expected call of DescribeTransferQueue. func (mr *MockEngineMockRecorder) DescribeTransferQueue(ctx, clusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTransferQueue", reflect.TypeOf((*MockEngine)(nil).DescribeTransferQueue), ctx, clusterName) } // DescribeWorkflowExecution mocks base method. func (m *MockEngine) DescribeWorkflowExecution(ctx context.Context, request *types.HistoryDescribeWorkflowExecutionRequest) (*types.DescribeWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeWorkflowExecution", ctx, request) ret0, _ := ret[0].(*types.DescribeWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeWorkflowExecution indicates an expected call of DescribeWorkflowExecution. func (mr *MockEngineMockRecorder) DescribeWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeWorkflowExecution", reflect.TypeOf((*MockEngine)(nil).DescribeWorkflowExecution), ctx, request) } // GetDLQReplicationMessages mocks base method. func (m *MockEngine) GetDLQReplicationMessages(ctx context.Context, taskInfos []*types.ReplicationTaskInfo) ([]*types.ReplicationTask, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDLQReplicationMessages", ctx, taskInfos) ret0, _ := ret[0].([]*types.ReplicationTask) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDLQReplicationMessages indicates an expected call of GetDLQReplicationMessages. func (mr *MockEngineMockRecorder) GetDLQReplicationMessages(ctx, taskInfos any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDLQReplicationMessages", reflect.TypeOf((*MockEngine)(nil).GetDLQReplicationMessages), ctx, taskInfos) } // GetMutableState mocks base method. func (m *MockEngine) GetMutableState(ctx context.Context, request *types.GetMutableStateRequest) (*types.GetMutableStateResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMutableState", ctx, request) ret0, _ := ret[0].(*types.GetMutableStateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMutableState indicates an expected call of GetMutableState. func (mr *MockEngineMockRecorder) GetMutableState(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMutableState", reflect.TypeOf((*MockEngine)(nil).GetMutableState), ctx, request) } // GetReplicationMessages mocks base method. func (m *MockEngine) GetReplicationMessages(ctx context.Context, pollingCluster string, lastReadMessageID int64) (*types.ReplicationMessages, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReplicationMessages", ctx, pollingCluster, lastReadMessageID) ret0, _ := ret[0].(*types.ReplicationMessages) ret1, _ := ret[1].(error) return ret0, ret1 } // GetReplicationMessages indicates an expected call of GetReplicationMessages. func (mr *MockEngineMockRecorder) GetReplicationMessages(ctx, pollingCluster, lastReadMessageID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationMessages", reflect.TypeOf((*MockEngine)(nil).GetReplicationMessages), ctx, pollingCluster, lastReadMessageID) } // MergeDLQMessages mocks base method. func (m *MockEngine) MergeDLQMessages(ctx context.Context, messagesRequest *types.MergeDLQMessagesRequest) (*types.MergeDLQMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MergeDLQMessages", ctx, messagesRequest) ret0, _ := ret[0].(*types.MergeDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MergeDLQMessages indicates an expected call of MergeDLQMessages. func (mr *MockEngineMockRecorder) MergeDLQMessages(ctx, messagesRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MergeDLQMessages", reflect.TypeOf((*MockEngine)(nil).MergeDLQMessages), ctx, messagesRequest) } // NotifyNewHistoryEvent mocks base method. func (m *MockEngine) NotifyNewHistoryEvent(event *events.Notification) { m.ctrl.T.Helper() m.ctrl.Call(m, "NotifyNewHistoryEvent", event) } // NotifyNewHistoryEvent indicates an expected call of NotifyNewHistoryEvent. func (mr *MockEngineMockRecorder) NotifyNewHistoryEvent(event any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyNewHistoryEvent", reflect.TypeOf((*MockEngine)(nil).NotifyNewHistoryEvent), event) } // NotifyNewReplicationTasks mocks base method. func (m *MockEngine) NotifyNewReplicationTasks(info *common.NotifyTaskInfo) { m.ctrl.T.Helper() m.ctrl.Call(m, "NotifyNewReplicationTasks", info) } // NotifyNewReplicationTasks indicates an expected call of NotifyNewReplicationTasks. func (mr *MockEngineMockRecorder) NotifyNewReplicationTasks(info any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyNewReplicationTasks", reflect.TypeOf((*MockEngine)(nil).NotifyNewReplicationTasks), info) } // NotifyNewTimerTasks mocks base method. func (m *MockEngine) NotifyNewTimerTasks(info *common.NotifyTaskInfo) { m.ctrl.T.Helper() m.ctrl.Call(m, "NotifyNewTimerTasks", info) } // NotifyNewTimerTasks indicates an expected call of NotifyNewTimerTasks. func (mr *MockEngineMockRecorder) NotifyNewTimerTasks(info any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyNewTimerTasks", reflect.TypeOf((*MockEngine)(nil).NotifyNewTimerTasks), info) } // NotifyNewTransferTasks mocks base method. func (m *MockEngine) NotifyNewTransferTasks(info *common.NotifyTaskInfo) { m.ctrl.T.Helper() m.ctrl.Call(m, "NotifyNewTransferTasks", info) } // NotifyNewTransferTasks indicates an expected call of NotifyNewTransferTasks. func (mr *MockEngineMockRecorder) NotifyNewTransferTasks(info any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyNewTransferTasks", reflect.TypeOf((*MockEngine)(nil).NotifyNewTransferTasks), info) } // PollMutableState mocks base method. func (m *MockEngine) PollMutableState(ctx context.Context, request *types.PollMutableStateRequest) (*types.PollMutableStateResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PollMutableState", ctx, request) ret0, _ := ret[0].(*types.PollMutableStateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollMutableState indicates an expected call of PollMutableState. func (mr *MockEngineMockRecorder) PollMutableState(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollMutableState", reflect.TypeOf((*MockEngine)(nil).PollMutableState), ctx, request) } // PurgeDLQMessages mocks base method. func (m *MockEngine) PurgeDLQMessages(ctx context.Context, messagesRequest *types.PurgeDLQMessagesRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PurgeDLQMessages", ctx, messagesRequest) ret0, _ := ret[0].(error) return ret0 } // PurgeDLQMessages indicates an expected call of PurgeDLQMessages. func (mr *MockEngineMockRecorder) PurgeDLQMessages(ctx, messagesRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PurgeDLQMessages", reflect.TypeOf((*MockEngine)(nil).PurgeDLQMessages), ctx, messagesRequest) } // QueryWorkflow mocks base method. func (m *MockEngine) QueryWorkflow(ctx context.Context, request *types.HistoryQueryWorkflowRequest) (*types.HistoryQueryWorkflowResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryWorkflow", ctx, request) ret0, _ := ret[0].(*types.HistoryQueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // QueryWorkflow indicates an expected call of QueryWorkflow. func (mr *MockEngineMockRecorder) QueryWorkflow(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockEngine)(nil).QueryWorkflow), ctx, request) } // ReadDLQMessages mocks base method. func (m *MockEngine) ReadDLQMessages(ctx context.Context, messagesRequest *types.ReadDLQMessagesRequest) (*types.ReadDLQMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadDLQMessages", ctx, messagesRequest) ret0, _ := ret[0].(*types.ReadDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadDLQMessages indicates an expected call of ReadDLQMessages. func (mr *MockEngineMockRecorder) ReadDLQMessages(ctx, messagesRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDLQMessages", reflect.TypeOf((*MockEngine)(nil).ReadDLQMessages), ctx, messagesRequest) } // ReapplyEvents mocks base method. func (m *MockEngine) ReapplyEvents(ctx context.Context, domainUUID, workflowID, runID string, events []*types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReapplyEvents", ctx, domainUUID, workflowID, runID, events) ret0, _ := ret[0].(error) return ret0 } // ReapplyEvents indicates an expected call of ReapplyEvents. func (mr *MockEngineMockRecorder) ReapplyEvents(ctx, domainUUID, workflowID, runID, events any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReapplyEvents", reflect.TypeOf((*MockEngine)(nil).ReapplyEvents), ctx, domainUUID, workflowID, runID, events) } // RecordActivityTaskHeartbeat mocks base method. func (m *MockEngine) RecordActivityTaskHeartbeat(ctx context.Context, request *types.HistoryRecordActivityTaskHeartbeatRequest) (*types.RecordActivityTaskHeartbeatResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordActivityTaskHeartbeat", ctx, request) ret0, _ := ret[0].(*types.RecordActivityTaskHeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordActivityTaskHeartbeat indicates an expected call of RecordActivityTaskHeartbeat. func (mr *MockEngineMockRecorder) RecordActivityTaskHeartbeat(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordActivityTaskHeartbeat", reflect.TypeOf((*MockEngine)(nil).RecordActivityTaskHeartbeat), ctx, request) } // RecordActivityTaskStarted mocks base method. func (m *MockEngine) RecordActivityTaskStarted(ctx context.Context, request *types.RecordActivityTaskStartedRequest) (*types.RecordActivityTaskStartedResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordActivityTaskStarted", ctx, request) ret0, _ := ret[0].(*types.RecordActivityTaskStartedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordActivityTaskStarted indicates an expected call of RecordActivityTaskStarted. func (mr *MockEngineMockRecorder) RecordActivityTaskStarted(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordActivityTaskStarted", reflect.TypeOf((*MockEngine)(nil).RecordActivityTaskStarted), ctx, request) } // RecordChildExecutionCompleted mocks base method. func (m *MockEngine) RecordChildExecutionCompleted(ctx context.Context, request *types.RecordChildExecutionCompletedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordChildExecutionCompleted", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RecordChildExecutionCompleted indicates an expected call of RecordChildExecutionCompleted. func (mr *MockEngineMockRecorder) RecordChildExecutionCompleted(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordChildExecutionCompleted", reflect.TypeOf((*MockEngine)(nil).RecordChildExecutionCompleted), ctx, request) } // RecordDecisionTaskStarted mocks base method. func (m *MockEngine) RecordDecisionTaskStarted(ctx context.Context, request *types.RecordDecisionTaskStartedRequest) (*types.RecordDecisionTaskStartedResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordDecisionTaskStarted", ctx, request) ret0, _ := ret[0].(*types.RecordDecisionTaskStartedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordDecisionTaskStarted indicates an expected call of RecordDecisionTaskStarted. func (mr *MockEngineMockRecorder) RecordDecisionTaskStarted(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordDecisionTaskStarted", reflect.TypeOf((*MockEngine)(nil).RecordDecisionTaskStarted), ctx, request) } // RefreshWorkflowTasks mocks base method. func (m *MockEngine) RefreshWorkflowTasks(ctx context.Context, domainUUID string, execution types.WorkflowExecution) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RefreshWorkflowTasks", ctx, domainUUID, execution) ret0, _ := ret[0].(error) return ret0 } // RefreshWorkflowTasks indicates an expected call of RefreshWorkflowTasks. func (mr *MockEngineMockRecorder) RefreshWorkflowTasks(ctx, domainUUID, execution any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshWorkflowTasks", reflect.TypeOf((*MockEngine)(nil).RefreshWorkflowTasks), ctx, domainUUID, execution) } // RemoveSignalMutableState mocks base method. func (m *MockEngine) RemoveSignalMutableState(ctx context.Context, request *types.RemoveSignalMutableStateRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoveSignalMutableState", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RemoveSignalMutableState indicates an expected call of RemoveSignalMutableState. func (mr *MockEngineMockRecorder) RemoveSignalMutableState(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveSignalMutableState", reflect.TypeOf((*MockEngine)(nil).RemoveSignalMutableState), ctx, request) } // ReplicateEventsV2 mocks base method. func (m *MockEngine) ReplicateEventsV2(ctx context.Context, request *types.ReplicateEventsV2Request) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateEventsV2", ctx, request) ret0, _ := ret[0].(error) return ret0 } // ReplicateEventsV2 indicates an expected call of ReplicateEventsV2. func (mr *MockEngineMockRecorder) ReplicateEventsV2(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateEventsV2", reflect.TypeOf((*MockEngine)(nil).ReplicateEventsV2), ctx, request) } // RequestCancelWorkflowExecution mocks base method. func (m *MockEngine) RequestCancelWorkflowExecution(ctx context.Context, request *types.HistoryRequestCancelWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RequestCancelWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RequestCancelWorkflowExecution indicates an expected call of RequestCancelWorkflowExecution. func (mr *MockEngineMockRecorder) RequestCancelWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestCancelWorkflowExecution", reflect.TypeOf((*MockEngine)(nil).RequestCancelWorkflowExecution), ctx, request) } // ResetStickyTaskList mocks base method. func (m *MockEngine) ResetStickyTaskList(ctx context.Context, resetRequest *types.HistoryResetStickyTaskListRequest) (*types.HistoryResetStickyTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetStickyTaskList", ctx, resetRequest) ret0, _ := ret[0].(*types.HistoryResetStickyTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetStickyTaskList indicates an expected call of ResetStickyTaskList. func (mr *MockEngineMockRecorder) ResetStickyTaskList(ctx, resetRequest any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetStickyTaskList", reflect.TypeOf((*MockEngine)(nil).ResetStickyTaskList), ctx, resetRequest) } // ResetTimerQueue mocks base method. func (m *MockEngine) ResetTimerQueue(ctx context.Context, clusterName string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetTimerQueue", ctx, clusterName) ret0, _ := ret[0].(error) return ret0 } // ResetTimerQueue indicates an expected call of ResetTimerQueue. func (mr *MockEngineMockRecorder) ResetTimerQueue(ctx, clusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimerQueue", reflect.TypeOf((*MockEngine)(nil).ResetTimerQueue), ctx, clusterName) } // ResetTransferQueue mocks base method. func (m *MockEngine) ResetTransferQueue(ctx context.Context, clusterName string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetTransferQueue", ctx, clusterName) ret0, _ := ret[0].(error) return ret0 } // ResetTransferQueue indicates an expected call of ResetTransferQueue. func (mr *MockEngineMockRecorder) ResetTransferQueue(ctx, clusterName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTransferQueue", reflect.TypeOf((*MockEngine)(nil).ResetTransferQueue), ctx, clusterName) } // ResetWorkflowExecution mocks base method. func (m *MockEngine) ResetWorkflowExecution(ctx context.Context, request *types.HistoryResetWorkflowExecutionRequest) (*types.ResetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetWorkflowExecution", ctx, request) ret0, _ := ret[0].(*types.ResetWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetWorkflowExecution indicates an expected call of ResetWorkflowExecution. func (mr *MockEngineMockRecorder) ResetWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetWorkflowExecution", reflect.TypeOf((*MockEngine)(nil).ResetWorkflowExecution), ctx, request) } // RespondActivityTaskCanceled mocks base method. func (m *MockEngine) RespondActivityTaskCanceled(ctx context.Context, request *types.HistoryRespondActivityTaskCanceledRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskCanceled", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCanceled indicates an expected call of RespondActivityTaskCanceled. func (mr *MockEngineMockRecorder) RespondActivityTaskCanceled(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCanceled", reflect.TypeOf((*MockEngine)(nil).RespondActivityTaskCanceled), ctx, request) } // RespondActivityTaskCompleted mocks base method. func (m *MockEngine) RespondActivityTaskCompleted(ctx context.Context, request *types.HistoryRespondActivityTaskCompletedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskCompleted", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCompleted indicates an expected call of RespondActivityTaskCompleted. func (mr *MockEngineMockRecorder) RespondActivityTaskCompleted(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCompleted", reflect.TypeOf((*MockEngine)(nil).RespondActivityTaskCompleted), ctx, request) } // RespondActivityTaskFailed mocks base method. func (m *MockEngine) RespondActivityTaskFailed(ctx context.Context, request *types.HistoryRespondActivityTaskFailedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskFailed", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskFailed indicates an expected call of RespondActivityTaskFailed. func (mr *MockEngineMockRecorder) RespondActivityTaskFailed(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskFailed", reflect.TypeOf((*MockEngine)(nil).RespondActivityTaskFailed), ctx, request) } // RespondDecisionTaskCompleted mocks base method. func (m *MockEngine) RespondDecisionTaskCompleted(ctx context.Context, request *types.HistoryRespondDecisionTaskCompletedRequest) (*types.HistoryRespondDecisionTaskCompletedResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondDecisionTaskCompleted", ctx, request) ret0, _ := ret[0].(*types.HistoryRespondDecisionTaskCompletedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RespondDecisionTaskCompleted indicates an expected call of RespondDecisionTaskCompleted. func (mr *MockEngineMockRecorder) RespondDecisionTaskCompleted(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondDecisionTaskCompleted", reflect.TypeOf((*MockEngine)(nil).RespondDecisionTaskCompleted), ctx, request) } // RespondDecisionTaskFailed mocks base method. func (m *MockEngine) RespondDecisionTaskFailed(ctx context.Context, request *types.HistoryRespondDecisionTaskFailedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondDecisionTaskFailed", ctx, request) ret0, _ := ret[0].(error) return ret0 } // RespondDecisionTaskFailed indicates an expected call of RespondDecisionTaskFailed. func (mr *MockEngineMockRecorder) RespondDecisionTaskFailed(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondDecisionTaskFailed", reflect.TypeOf((*MockEngine)(nil).RespondDecisionTaskFailed), ctx, request) } // ScheduleDecisionTask mocks base method. func (m *MockEngine) ScheduleDecisionTask(ctx context.Context, request *types.ScheduleDecisionTaskRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ScheduleDecisionTask", ctx, request) ret0, _ := ret[0].(error) return ret0 } // ScheduleDecisionTask indicates an expected call of ScheduleDecisionTask. func (mr *MockEngineMockRecorder) ScheduleDecisionTask(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduleDecisionTask", reflect.TypeOf((*MockEngine)(nil).ScheduleDecisionTask), ctx, request) } // SignalWithStartWorkflowExecution mocks base method. func (m *MockEngine) SignalWithStartWorkflowExecution(ctx context.Context, request *types.HistorySignalWithStartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignalWithStartWorkflowExecution", ctx, request) ret0, _ := ret[0].(*types.StartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SignalWithStartWorkflowExecution indicates an expected call of SignalWithStartWorkflowExecution. func (mr *MockEngineMockRecorder) SignalWithStartWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWithStartWorkflowExecution", reflect.TypeOf((*MockEngine)(nil).SignalWithStartWorkflowExecution), ctx, request) } // SignalWorkflowExecution mocks base method. func (m *MockEngine) SignalWorkflowExecution(ctx context.Context, request *types.HistorySignalWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignalWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // SignalWorkflowExecution indicates an expected call of SignalWorkflowExecution. func (mr *MockEngineMockRecorder) SignalWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWorkflowExecution", reflect.TypeOf((*MockEngine)(nil).SignalWorkflowExecution), ctx, request) } // Start mocks base method. func (m *MockEngine) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockEngineMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockEngine)(nil).Start)) } // StartWorkflowExecution mocks base method. func (m *MockEngine) StartWorkflowExecution(ctx context.Context, request *types.HistoryStartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StartWorkflowExecution", ctx, request) ret0, _ := ret[0].(*types.StartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // StartWorkflowExecution indicates an expected call of StartWorkflowExecution. func (mr *MockEngineMockRecorder) StartWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartWorkflowExecution", reflect.TypeOf((*MockEngine)(nil).StartWorkflowExecution), ctx, request) } // Stop mocks base method. func (m *MockEngine) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockEngineMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockEngine)(nil).Stop)) } // SyncActivity mocks base method. func (m *MockEngine) SyncActivity(ctx context.Context, request *types.SyncActivityRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SyncActivity", ctx, request) ret0, _ := ret[0].(error) return ret0 } // SyncActivity indicates an expected call of SyncActivity. func (mr *MockEngineMockRecorder) SyncActivity(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncActivity", reflect.TypeOf((*MockEngine)(nil).SyncActivity), ctx, request) } // SyncShardStatus mocks base method. func (m *MockEngine) SyncShardStatus(ctx context.Context, request *types.SyncShardStatusRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SyncShardStatus", ctx, request) ret0, _ := ret[0].(error) return ret0 } // SyncShardStatus indicates an expected call of SyncShardStatus. func (mr *MockEngineMockRecorder) SyncShardStatus(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncShardStatus", reflect.TypeOf((*MockEngine)(nil).SyncShardStatus), ctx, request) } // TerminateWorkflowExecution mocks base method. func (m *MockEngine) TerminateWorkflowExecution(ctx context.Context, request *types.HistoryTerminateWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TerminateWorkflowExecution", ctx, request) ret0, _ := ret[0].(error) return ret0 } // TerminateWorkflowExecution indicates an expected call of TerminateWorkflowExecution. func (mr *MockEngineMockRecorder) TerminateWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TerminateWorkflowExecution", reflect.TypeOf((*MockEngine)(nil).TerminateWorkflowExecution), ctx, request) } ================================================ FILE: service/history/engine/testdata/engine_for_tests.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package testdata import ( "testing" // client library cannot change from old gomock "github.com/stretchr/testify/mock" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/failover" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/replication" "github.com/uber/cadence/service/history/shard" ) type EngineForTest struct { Engine engine.Engine // Add mocks or other fields here ShardCtx *shard.TestContext } // NewEngineFn is defined as an alias for engineimpl.NewEngineWithShardContext to avoid circular dependency type NewEngineFn func( shard shard.Context, visibilityMgr persistence.VisibilityManager, matching matching.Client, historyEventNotifier events.Notifier, config *config.Config, replicationTaskFetchers replication.TaskFetchers, rawMatchingClient matching.Client, failoverCoordinator failover.Coordinator, queueFactories []queue.Factory, ) engine.Engine func NewEngineForTest(t *testing.T, newEngineFn NewEngineFn) *EngineForTest { t.Helper() controller := gomock.NewController(t) historyCfg := config.NewForTest() shardCtx := shard.NewTestContext( t, controller, &persistence.ShardInfo{ RangeID: 1, TransferAckLevel: 0, }, historyCfg, ) domainCache := shardCtx.Resource.DomainCache domainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestLocalDomainEntry, nil).AnyTimes() domainCache.EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).AnyTimes() domainCache.EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestLocalDomainEntry, nil).AnyTimes() domainCache.EXPECT().GetDomainID(constants.TestDomainName).Return(constants.TestDomainID, nil).AnyTimes() domainCache.EXPECT().RegisterDomainChangeCallback(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() domainCache.EXPECT().UnregisterDomainChangeCallback(gomock.Any()).Times(1) executionMgr := shardCtx.Resource.ExecutionMgr // RangeCompleteReplicationTask is called by taskProcessorImpl's background loop executionMgr. On("RangeCompleteHistoryTask", mock.Anything, mock.Anything). Return(&persistence.RangeCompleteHistoryTaskResponse{}, nil) membershipResolver := shardCtx.Resource.MembershipResolver membershipResolver.EXPECT().MemberCount(gomock.Any()).Return(1, nil).AnyTimes() eventsCache := events.NewCache( shardCtx.GetShardID(), shardCtx.GetHistoryManager(), historyCfg, shardCtx.GetLogger(), shardCtx.GetMetricsClient(), domainCache, ) shardCtx.SetEventsCache(eventsCache) historyEventNotifier := events.NewNotifier( clock.NewRealTimeSource(), shardCtx.Resource.MetricsClient, func(workflowID string) int { return len(workflowID) }, ) replicatonTaskFetchers := replication.NewMockTaskFetchers(controller) replicationTaskFetcher := replication.NewMockTaskFetcher(controller) // TODO: this should probably return another cluster name, not current replicationTaskFetcher.EXPECT().GetSourceCluster().Return(constants.TestClusterMetadata.GetCurrentClusterName()).AnyTimes() replicationTaskFetcher.EXPECT().GetRateLimiter().Return(quotas.NewDynamicRateLimiter(func() float64 { return 100 })).AnyTimes() replicationTaskFetcher.EXPECT().GetRequestChan(gomock.Any()).Return(nil).AnyTimes() replicatonTaskFetchers.EXPECT().GetFetchers().Return([]replication.TaskFetcher{replicationTaskFetcher}).AnyTimes() failoverCoordinator := failover.NewMockCoordinator(controller) timerQueueFactory := queue.NewMockFactory(controller) timerQueueFactory.EXPECT().Category().Return(persistence.HistoryTaskCategoryTimer).AnyTimes() timerQProcessor := queue.NewMockProcessor(controller) timerQProcessor.EXPECT().Start().Return().Times(1) timerQProcessor.EXPECT().NotifyNewTask(gomock.Any(), gomock.Any()).Return().AnyTimes() timerQProcessor.EXPECT().Stop().Return().Times(1) timerQueueFactory.EXPECT().CreateQueue(gomock.Any(), gomock.Any(), gomock.Any()).Return(timerQProcessor).Times(1) transferQueueFactory := queue.NewMockFactory(controller) transferQueueFactory.EXPECT().Category().Return(persistence.HistoryTaskCategoryTransfer).AnyTimes() transferQProcessor := queue.NewMockProcessor(controller) transferQProcessor.EXPECT().Start().Return().Times(1) transferQProcessor.EXPECT().NotifyNewTask(gomock.Any(), gomock.Any()).Return().AnyTimes() transferQProcessor.EXPECT().Stop().Return().Times(1) transferQueueFactory.EXPECT().CreateQueue(gomock.Any(), gomock.Any(), gomock.Any()).Return(transferQProcessor).Times(1) queueFactories := []queue.Factory{timerQueueFactory, transferQueueFactory} engine := newEngineFn( shardCtx, shardCtx.Resource.VisibilityMgr, shardCtx.Resource.MatchingClient, historyEventNotifier, historyCfg, replicatonTaskFetchers, shardCtx.Resource.MatchingClient, failoverCoordinator, queueFactories, ) shardCtx.SetEngine(engine) historyEventNotifier.Start() t.Cleanup(historyEventNotifier.Stop) return &EngineForTest{ Engine: engine, ShardCtx: shardCtx, } } ================================================ FILE: service/history/events/blob.go ================================================ // The MIT License (MIT) // Copyright (c) 2022 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package events import ( "bytes" "github.com/uber/cadence/common/persistence" ) type ( // PersistedBlob is a wrapper on persistence.DataBlob with additional field indicating what was persisted. // Additional fields are used as an identification key among other blobs. PersistedBlob struct { persistence.DataBlob BranchToken []byte FirstEventID int64 } // PersistedBlobs is a slice of PersistedBlob PersistedBlobs []PersistedBlob ) // Find searches for persisted event blob. Returns nil when not found. func (blobs PersistedBlobs) Find(branchToken []byte, firstEventID int64) *persistence.DataBlob { // Linear search is ok here, as we will only have 1-2 persisted blobs per transaction for _, blob := range blobs { if bytes.Equal(blob.BranchToken, branchToken) && blob.FirstEventID == firstEventID { return &blob.DataBlob } } return nil } ================================================ FILE: service/history/events/blob_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2022 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package events import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/persistence" ) func TestPersistedBlobs_Find(t *testing.T) { blob1 := persistence.DataBlob{Data: []byte{1, 2, 3}} blob2 := persistence.DataBlob{Data: []byte{4, 5, 6}} blob3 := persistence.DataBlob{Data: []byte{7, 8, 9}} branchA := []byte{11, 11, 11} branchB := []byte{22, 22, 22} persistedBlobs := PersistedBlobs{ PersistedBlob{BranchToken: branchA, FirstEventID: 100, DataBlob: blob1}, PersistedBlob{BranchToken: branchA, FirstEventID: 105, DataBlob: blob2}, PersistedBlob{BranchToken: branchB, FirstEventID: 100, DataBlob: blob3}, } assert.Equal(t, blob1, *persistedBlobs.Find(branchA, 100)) assert.Equal(t, blob2, *persistedBlobs.Find(branchA, 105)) assert.Equal(t, blob3, *persistedBlobs.Find(branchB, 100)) assert.Nil(t, persistedBlobs.Find(branchB, 105)) assert.Nil(t, persistedBlobs.Find([]byte{99}, 100)) } ================================================ FILE: service/history/events/cache.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination cache_mock.go package events import ( "context" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" ) type ( // Cache caches workflow history event Cache interface { GetEvent( ctx context.Context, shardID int, domainID string, workflowID string, runID string, firstEventID int64, eventID int64, branchToken []byte, ) (*types.HistoryEvent, error) PutEvent( domainID string, workflowID string, runID string, eventID int64, event *types.HistoryEvent, ) } cacheImpl struct { cache.Cache domainCache cache.DomainCache historyManager persistence.HistoryManager disabled bool logger log.Logger metricsClient metrics.Client shardID *int } eventKey struct { domainID string workflowID string runID string eventID int64 } ) var ( errEventNotFoundInBatch = &types.EntityNotExistsError{Message: "History event not found within expected batch"} ) var _ Cache = (*cacheImpl)(nil) // NewGlobalCache creates a new global events cache func NewGlobalCache( initialCount int, maxCount int, ttl time.Duration, historyManager persistence.HistoryManager, logger log.Logger, metricsClient metrics.Client, enableSizeBasedCache dynamicproperties.BoolPropertyFn, maxSize dynamicproperties.IntPropertyFn, domainCache cache.DomainCache, ) Cache { return newCacheWithOption( nil, initialCount, maxCount, ttl, historyManager, false, logger, metricsClient, enableSizeBasedCache, maxSize, domainCache, ) } // NewCache creates a new events cache func NewCache( shardID int, historyManager persistence.HistoryManager, config *config.Config, logger log.Logger, metricsClient metrics.Client, domainCache cache.DomainCache, ) Cache { return newCacheWithOption( &shardID, config.EventsCacheInitialCount(), config.EventsCacheMaxCount(), config.EventsCacheTTL(), historyManager, false, logger, metricsClient, config.EnableSizeBasedHistoryEventCache, config.EventsCacheMaxSize, domainCache, ) } func newCacheWithOption( shardID *int, initialCount int, maxCount int, ttl time.Duration, historyManager persistence.HistoryManager, disabled bool, logger log.Logger, metricsClient metrics.Client, enableSizeBasedCache dynamicproperties.BoolPropertyFn, maxSize dynamicproperties.IntPropertyFn, domainCache cache.DomainCache, ) *cacheImpl { opts := &cache.Options{} opts.InitialCapacity = initialCount opts.TTL = ttl opts.MaxCount = maxCount opts.MetricsScope = metricsClient.Scope(metrics.EventsCacheGetEventScope) if shardID != nil { opts.MetricsScope = opts.MetricsScope.Tagged(metrics.ShardIDTag(*shardID)) } opts.MaxSize = maxSize opts.Logger = logger.WithTags(tag.ComponentEventsCache) opts.IsSizeBased = enableSizeBasedCache return &cacheImpl{ Cache: cache.New(opts), domainCache: domainCache, historyManager: historyManager, disabled: disabled, logger: logger.WithTags(tag.ComponentEventsCache), metricsClient: metricsClient, shardID: shardID, } } func newEventKey( domainID, workflowID, runID string, eventID int64, ) eventKey { return eventKey{ domainID: domainID, workflowID: workflowID, runID: runID, eventID: eventID, } } func (e *cacheImpl) GetEvent( ctx context.Context, shardID int, domainID string, workflowID string, runID string, firstEventID int64, eventID int64, branchToken []byte, ) (*types.HistoryEvent, error) { e.metricsClient.IncCounter(metrics.EventsCacheGetEventScope, metrics.CacheRequests) sw := e.metricsClient.StartTimer(metrics.EventsCacheGetEventScope, metrics.CacheLatency) defer sw.Stop() key := newEventKey(domainID, workflowID, runID, eventID) // Test hook for disabling cache if !e.disabled { event, cacheHit := e.Cache.Get(key).(*types.HistoryEvent) if cacheHit { return event, nil } } e.metricsClient.IncCounter(metrics.EventsCacheGetEventScope, metrics.CacheMissCounter) event, err := e.getHistoryEventFromStore(ctx, firstEventID, eventID, branchToken, shardID, domainID) if err != nil { e.metricsClient.IncCounter(metrics.EventsCacheGetEventScope, metrics.CacheFailures) e.logger.Error("EventsCache unable to retrieve event from store", tag.Error(err), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.WorkflowDomainID(domainID), tag.WorkflowEventID(eventID)) return nil, err } e.Put(key, event) return event, nil } func (e *cacheImpl) PutEvent( domainID, workflowID, runID string, eventID int64, event *types.HistoryEvent, ) { e.metricsClient.IncCounter(metrics.EventsCachePutEventScope, metrics.CacheRequests) sw := e.metricsClient.StartTimer(metrics.EventsCachePutEventScope, metrics.CacheLatency) defer sw.Stop() key := newEventKey(domainID, workflowID, runID, eventID) e.Put(key, event) } func (e *cacheImpl) getHistoryEventFromStore( ctx context.Context, firstEventID int64, eventID int64, branchToken []byte, shardID int, domainID string, ) (*types.HistoryEvent, error) { e.metricsClient.IncCounter(metrics.EventsCacheGetFromStoreScope, metrics.CacheRequests) sw := e.metricsClient.StartTimer(metrics.EventsCacheGetFromStoreScope, metrics.CacheLatency) defer sw.Stop() var historyEvents []*types.HistoryEvent domainName, err := e.domainCache.GetDomainName(domainID) if err != nil { return nil, err } response, err := e.historyManager.ReadHistoryBranch(ctx, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: eventID + 1, PageSize: 1, NextPageToken: nil, ShardID: common.IntPtr(shardID), DomainName: domainName, }) if err != nil { e.metricsClient.IncCounter(metrics.EventsCacheGetFromStoreScope, metrics.CacheFailures) return nil, err } historyEvents = response.HistoryEvents // find history event from batch and return back single event to caller for _, e := range historyEvents { if e.ID == eventID { return e, nil } } return nil, errEventNotFoundInBatch } ================================================ FILE: service/history/events/cache_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: cache.go // // Generated by this command: // // mockgen -package events -source cache.go -destination cache_mock.go // // Package events is a generated GoMock package. package events import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockCache is a mock of Cache interface. type MockCache struct { ctrl *gomock.Controller recorder *MockCacheMockRecorder isgomock struct{} } // MockCacheMockRecorder is the mock recorder for MockCache. type MockCacheMockRecorder struct { mock *MockCache } // NewMockCache creates a new mock instance. func NewMockCache(ctrl *gomock.Controller) *MockCache { mock := &MockCache{ctrl: ctrl} mock.recorder = &MockCacheMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCache) EXPECT() *MockCacheMockRecorder { return m.recorder } // GetEvent mocks base method. func (m *MockCache) GetEvent(ctx context.Context, shardID int, domainID, workflowID, runID string, firstEventID, eventID int64, branchToken []byte) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEvent", ctx, shardID, domainID, workflowID, runID, firstEventID, eventID, branchToken) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // GetEvent indicates an expected call of GetEvent. func (mr *MockCacheMockRecorder) GetEvent(ctx, shardID, domainID, workflowID, runID, firstEventID, eventID, branchToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEvent", reflect.TypeOf((*MockCache)(nil).GetEvent), ctx, shardID, domainID, workflowID, runID, firstEventID, eventID, branchToken) } // PutEvent mocks base method. func (m *MockCache) PutEvent(domainID, workflowID, runID string, eventID int64, event *types.HistoryEvent) { m.ctrl.T.Helper() m.ctrl.Call(m, "PutEvent", domainID, workflowID, runID, eventID, event) } // PutEvent indicates an expected call of PutEvent. func (mr *MockCacheMockRecorder) PutEvent(domainID, workflowID, runID, eventID, event any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutEvent", reflect.TypeOf((*MockCache)(nil).PutEvent), domainID, workflowID, runID, eventID, event) } ================================================ FILE: service/history/events/cache_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package events import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( eventsCacheSuite struct { suite.Suite *require.Assertions logger log.Logger mockHistoryManager *mocks.HistoryV2Manager cache *cacheImpl ctrl *gomock.Controller domainCache *cache.MockDomainCache } ) func TestEventsCacheSuite(t *testing.T) { s := new(eventsCacheSuite) suite.Run(t, s) } func (s *eventsCacheSuite) SetupSuite() { } func (s *eventsCacheSuite) TearDownSuite() { } func (s *eventsCacheSuite) SetupTest() { s.Assertions = require.New(s.T()) s.logger = testlogger.New(s.Suite.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) s.mockHistoryManager = &mocks.HistoryV2Manager{} s.ctrl = gomock.NewController(s.T()) s.domainCache = cache.NewMockDomainCache(s.ctrl) s.cache = s.newTestEventsCache() } func (s *eventsCacheSuite) TearDownTest() { s.mockHistoryManager.AssertExpectations(s.T()) s.ctrl.Finish() } func (s *eventsCacheSuite) newTestEventsCache() *cacheImpl { return newCacheWithOption(common.IntPtr(10), 16, 32, time.Minute, s.mockHistoryManager, false, s.logger, metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), dynamicproperties.GetBoolPropertyFn(false), dynamicproperties.GetIntPropertyFn(1000), s.domainCache) } func (s *eventsCacheSuite) TestEventsCacheHitSuccess() { domainID := "events-cache-hit-success-domain" workflowID := "events-cache-hit-success-workflow-id" runID := "events-cache-hit-success-run-id" eventID := int64(23) event := &types.HistoryEvent{ ID: eventID, EventType: types.EventTypeActivityTaskStarted.Ptr(), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{}, } s.cache.PutEvent(domainID, workflowID, runID, eventID, event) actualEvent, err := s.cache.GetEvent(context.Background(), 0, domainID, workflowID, runID, eventID, eventID, nil) s.Nil(err) s.Equal(event, actualEvent) } func (s *eventsCacheSuite) TestEventsCacheMissMultiEventsBatchV2Success() { domainID := "events-cache-miss-multi-events-batch-v2-success-domain" workflowID := "events-cache-miss-multi-events-batch-v2-success-workflow-id" runID := "events-cache-miss-multi-events-batch-v2-success-run-id" domainName := "events-cache-miss-multi-events-batch-v2-success-domainName" event1 := &types.HistoryEvent{ ID: 11, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{}, } event2 := &types.HistoryEvent{ ID: 12, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{}, } event3 := &types.HistoryEvent{ ID: 13, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{}, } event4 := &types.HistoryEvent{ ID: 14, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{}, } event5 := &types.HistoryEvent{ ID: 15, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{}, } event6 := &types.HistoryEvent{ ID: 16, EventType: types.EventTypeActivityTaskScheduled.Ptr(), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{}, } shardID := common.IntPtr(10) s.mockHistoryManager.On("ReadHistoryBranch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: []byte("store_token"), MinEventID: event1.ID, MaxEventID: event6.ID + 1, PageSize: 1, NextPageToken: nil, ShardID: shardID, DomainName: domainName, }).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{event1, event2, event3, event4, event5, event6}, NextPageToken: nil, LastFirstEventID: event1.ID, }, nil) s.domainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() s.cache.PutEvent(domainID, workflowID, runID, event2.ID, event2) actualEvent, err := s.cache.GetEvent(context.Background(), *shardID, domainID, workflowID, runID, event1.ID, event6.ID, []byte("store_token")) s.Nil(err) s.Equal(event6, actualEvent) } func (s *eventsCacheSuite) TestEventsCacheMissV2Failure() { domainID := "events-cache-miss-failure-domain" workflowID := "events-cache-miss-failure-workflow-id" runID := "events-cache-miss-failure-run-id" shardID := common.IntPtr(10) domainName := "events-cache-miss-failure-domainName" expectedErr := errors.New("persistence call failed") s.mockHistoryManager.On("ReadHistoryBranch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: []byte("store_token"), MinEventID: int64(11), MaxEventID: int64(15), PageSize: 1, NextPageToken: nil, ShardID: shardID, DomainName: domainName, }).Return(nil, expectedErr) s.domainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() actualEvent, err := s.cache.GetEvent(context.Background(), *shardID, domainID, workflowID, runID, int64(11), int64(14), []byte("store_token")) s.Nil(actualEvent) s.Equal(expectedErr, err) } func (s *eventsCacheSuite) TestEventsCacheDisableSuccess() { domainID := "events-cache-disable-success-domain" workflowID := "events-cache-disable-success-workflow-id" runID := "events-cache-disable-success-run-id" shardID := common.IntPtr(10) domainName := "events-cache-disable-success-domainName" event1 := &types.HistoryEvent{ ID: 23, EventType: types.EventTypeActivityTaskStarted.Ptr(), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{}, } event2 := &types.HistoryEvent{ ID: 32, EventType: types.EventTypeActivityTaskStarted.Ptr(), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{}, } s.mockHistoryManager.On("ReadHistoryBranch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: []byte("store_token"), MinEventID: event2.ID, MaxEventID: event2.ID + 1, PageSize: 1, NextPageToken: nil, ShardID: shardID, DomainName: domainName, }).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{event2}, NextPageToken: nil, LastFirstEventID: event2.ID, }, nil) s.domainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() s.cache.PutEvent(domainID, workflowID, runID, event1.ID, event1) s.cache.PutEvent(domainID, workflowID, runID, event2.ID, event2) s.cache.disabled = true actualEvent, err := s.cache.GetEvent(context.Background(), *shardID, domainID, workflowID, runID, event2.ID, event2.ID, []byte("store_token")) s.Nil(err) s.Equal(event2, actualEvent) } ================================================ FILE: service/history/events/notifier.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package events import ( "sync/atomic" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const ( eventsChanSize = 1000 ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination notifier_mock.go -self_package github.com/uber/cadence/service/history/events type ( // Notifier is a pub-sub for sending and receiving notifications on new history events Notifier interface { common.Daemon NotifyNewHistoryEvent(event *Notification) WatchHistoryEvent(identifier definition.WorkflowIdentifier) (string, chan *Notification, error) UnwatchHistoryEvent(identifier definition.WorkflowIdentifier, subscriberID string) error } // Notification is the notification for new history events Notification struct { ID definition.WorkflowIdentifier LastFirstEventID int64 NextEventID int64 PreviousStartedEventID int64 Timestamp time.Time WorkflowState int WorkflowCloseState int VersionHistories *persistence.VersionHistories } notifierImpl struct { timeSource clock.TimeSource metrics metrics.Client // internal status indicator status int32 // stop signal channel closeChan chan bool // this channel will never close eventsChan chan *Notification // function which calculate the shard ID from given workflow ID workflowIDToShardID func(string) int // concurrent map with key workflowIdentifier, value map[string]chan *Notification. // the reason for the second map being non thread safe: // 1. expected number of subscriber per workflow is low, i.e. < 5 // 2. update to this map is already guarded by GetAndDo API provided by ConcurrentTxMap eventsPubsubs collection.ConcurrentTxMap } ) var _ Notifier = (*notifierImpl)(nil) // NewNotification creates a new history event notification func NewNotification( domainID string, workflowExecution *types.WorkflowExecution, lastFirstEventID int64, nextEventID int64, previousStartedEventID int64, workflowState int, workflowCloseState int, versionHistories *persistence.VersionHistories, ) *Notification { return &Notification{ ID: definition.NewWorkflowIdentifier( domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID(), ), LastFirstEventID: lastFirstEventID, NextEventID: nextEventID, PreviousStartedEventID: previousStartedEventID, WorkflowState: workflowState, WorkflowCloseState: workflowCloseState, VersionHistories: versionHistories, } } // NewNotifier creates a new history event notifier func NewNotifier( timeSource clock.TimeSource, metrics metrics.Client, workflowIDToShardID func(string) int, ) Notifier { hashFn := func(key interface{}) uint32 { notification, ok := key.(Notification) if !ok { return 0 } return uint32(workflowIDToShardID(notification.ID.WorkflowID)) } return ¬ifierImpl{ timeSource: timeSource, metrics: metrics, status: common.DaemonStatusInitialized, closeChan: make(chan bool), eventsChan: make(chan *Notification, eventsChanSize), workflowIDToShardID: workflowIDToShardID, eventsPubsubs: collection.NewShardedConcurrentTxMap(1024, hashFn), } } func (notifier *notifierImpl) WatchHistoryEvent( identifier definition.WorkflowIdentifier) (string, chan *Notification, error) { channel := make(chan *Notification, 1) subscriberID := uuid.New() subscribers := map[string]chan *Notification{ subscriberID: channel, } _, _, err := notifier.eventsPubsubs.PutOrDo(identifier, subscribers, func(key interface{}, value interface{}) error { subscribers := value.(map[string]chan *Notification) if _, ok := subscribers[subscriberID]; ok { // UUID collision return &types.InternalServiceError{ Message: "Unable to watch on workflow execution.", } } subscribers[subscriberID] = channel return nil }) if err != nil { return "", nil, err } return subscriberID, channel, nil } func (notifier *notifierImpl) UnwatchHistoryEvent( identifier definition.WorkflowIdentifier, subscriberID string) error { success := true notifier.eventsPubsubs.RemoveIf(identifier, func(key interface{}, value interface{}) bool { subscribers := value.(map[string]chan *Notification) if _, ok := subscribers[subscriberID]; !ok { // cannot find the subscribe ID, which means there is a bug success = false } else { delete(subscribers, subscriberID) } return len(subscribers) == 0 }) if !success { // cannot find the subscribe ID, which means there is a bug return &types.InternalServiceError{ Message: "Unable to unwatch on workflow execution.", } } return nil } func (notifier *notifierImpl) dispatchHistoryEventNotification(event *Notification) { identifier := event.ID timer := notifier.metrics.StartTimer(metrics.HistoryEventNotificationScope, metrics.HistoryEventNotificationFanoutLatency) defer timer.Stop() notifier.eventsPubsubs.GetAndDo(identifier, func(key interface{}, value interface{}) error { //nolint:errcheck subscribers := value.(map[string]chan *Notification) for _, channel := range subscribers { select { case channel <- event: default: // in case the channel is already filled with message // this should NOT happen, unless there is a bug or high load } } return nil }) } func (notifier *notifierImpl) enqueueHistoryEventNotification(event *Notification) { // set the timestamp just before enqueuing the event event.Timestamp = notifier.timeSource.Now() select { case notifier.eventsChan <- event: default: // in case the channel is already filled with message // this can be caused by high load notifier.metrics.IncCounter(metrics.HistoryEventNotificationScope, metrics.HistoryEventNotificationFailDeliveryCount) } } func (notifier *notifierImpl) dequeueHistoryEventNotifications() { for { // send out metrics about the current number of messages in flight notifier.metrics.UpdateGauge(metrics.HistoryEventNotificationScope, metrics.HistoryEventNotificationInFlightMessageGauge, float64(len(notifier.eventsChan))) select { case event := <-notifier.eventsChan: // send out metrics about message processing delay timeelapsed := time.Since(event.Timestamp) notifier.metrics.RecordTimer(metrics.HistoryEventNotificationScope, metrics.HistoryEventNotificationQueueingLatency, timeelapsed) notifier.dispatchHistoryEventNotification(event) case <-notifier.closeChan: // shutdown return } } } func (notifier *notifierImpl) Start() { if !atomic.CompareAndSwapInt32(¬ifier.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } go notifier.dequeueHistoryEventNotifications() } func (notifier *notifierImpl) Stop() { if !atomic.CompareAndSwapInt32(¬ifier.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(notifier.closeChan) } func (notifier *notifierImpl) NotifyNewHistoryEvent(event *Notification) { notifier.enqueueHistoryEventNotification(event) } ================================================ FILE: service/history/events/notifier_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: notifier.go // // Generated by this command: // // mockgen -package events -source notifier.go -destination notifier_mock.go -self_package github.com/uber/cadence/service/history/events // // Package events is a generated GoMock package. package events import ( reflect "reflect" gomock "go.uber.org/mock/gomock" definition "github.com/uber/cadence/common/definition" ) // MockNotifier is a mock of Notifier interface. type MockNotifier struct { ctrl *gomock.Controller recorder *MockNotifierMockRecorder isgomock struct{} } // MockNotifierMockRecorder is the mock recorder for MockNotifier. type MockNotifierMockRecorder struct { mock *MockNotifier } // NewMockNotifier creates a new mock instance. func NewMockNotifier(ctrl *gomock.Controller) *MockNotifier { mock := &MockNotifier{ctrl: ctrl} mock.recorder = &MockNotifierMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockNotifier) EXPECT() *MockNotifierMockRecorder { return m.recorder } // NotifyNewHistoryEvent mocks base method. func (m *MockNotifier) NotifyNewHistoryEvent(event *Notification) { m.ctrl.T.Helper() m.ctrl.Call(m, "NotifyNewHistoryEvent", event) } // NotifyNewHistoryEvent indicates an expected call of NotifyNewHistoryEvent. func (mr *MockNotifierMockRecorder) NotifyNewHistoryEvent(event any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyNewHistoryEvent", reflect.TypeOf((*MockNotifier)(nil).NotifyNewHistoryEvent), event) } // Start mocks base method. func (m *MockNotifier) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockNotifierMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockNotifier)(nil).Start)) } // Stop mocks base method. func (m *MockNotifier) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockNotifierMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockNotifier)(nil).Stop)) } // UnwatchHistoryEvent mocks base method. func (m *MockNotifier) UnwatchHistoryEvent(identifier definition.WorkflowIdentifier, subscriberID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UnwatchHistoryEvent", identifier, subscriberID) ret0, _ := ret[0].(error) return ret0 } // UnwatchHistoryEvent indicates an expected call of UnwatchHistoryEvent. func (mr *MockNotifierMockRecorder) UnwatchHistoryEvent(identifier, subscriberID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnwatchHistoryEvent", reflect.TypeOf((*MockNotifier)(nil).UnwatchHistoryEvent), identifier, subscriberID) } // WatchHistoryEvent mocks base method. func (m *MockNotifier) WatchHistoryEvent(identifier definition.WorkflowIdentifier) (string, chan *Notification, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WatchHistoryEvent", identifier) ret0, _ := ret[0].(string) ret1, _ := ret[1].(chan *Notification) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // WatchHistoryEvent indicates an expected call of WatchHistoryEvent. func (mr *MockNotifierMockRecorder) WatchHistoryEvent(identifier any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WatchHistoryEvent", reflect.TypeOf((*MockNotifier)(nil).WatchHistoryEvent), identifier) } ================================================ FILE: service/history/events/notifier_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package events import ( "sync" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( notifierSuite struct { suite.Suite *require.Assertions historyEventNotifier Notifier } ) func TestHistoryEventNotifierSuite(t *testing.T) { s := new(notifierSuite) suite.Run(t, s) } func (s *notifierSuite) SetupSuite() { } func (s *notifierSuite) TearDownSuite() { } func (s *notifierSuite) SetupTest() { s.Assertions = require.New(s.T()) s.historyEventNotifier = NewNotifier( clock.NewRealTimeSource(), metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), func(workflowID string) int { return len(workflowID) }, ) s.historyEventNotifier.Start() } func (s *notifierSuite) TearDownTest() { s.historyEventNotifier.Stop() } func (s *notifierSuite) TestSingleSubscriberWatchingEvents() { domainID := "domain ID" execution := &types.WorkflowExecution{ WorkflowID: "workflow ID", RunID: "run ID", } lastFirstEventID := int64(3) previousStartedEventID := int64(5) nextEventID := int64(18) workflowState := persistence.WorkflowStateCreated workflowCloseState := persistence.WorkflowCloseStatusNone versionHistory := persistence.VersionHistories{} historyEvent := NewNotification( domainID, execution, lastFirstEventID, nextEventID, previousStartedEventID, workflowState, workflowCloseState, &versionHistory, ) timerChan := time.NewTimer(time.Second * 2).C subscriberID, channel, err := s.historyEventNotifier.WatchHistoryEvent(definition.NewWorkflowIdentifier(domainID, execution.GetWorkflowID(), execution.GetRunID())) s.Nil(err) go func() { <-timerChan s.historyEventNotifier.NotifyNewHistoryEvent(historyEvent) }() msg := <-channel s.Equal(historyEvent, msg) err = s.historyEventNotifier.UnwatchHistoryEvent(definition.NewWorkflowIdentifier(domainID, execution.GetWorkflowID(), execution.GetRunID()), subscriberID) s.Nil(err) } func (s *notifierSuite) TestMultipleSubscriberWatchingEvents() { domainID := "domain ID" execution := &types.WorkflowExecution{ WorkflowID: "workflow ID", RunID: "run ID", } lastFirstEventID := int64(3) previousStartedEventID := int64(5) nextEventID := int64(18) workflowState := persistence.WorkflowStateCreated workflowCloseState := persistence.WorkflowCloseStatusNone versionHistories := &persistence.VersionHistories{} historyEvent := NewNotification(domainID, execution, lastFirstEventID, nextEventID, previousStartedEventID, workflowState, workflowCloseState, versionHistories) timerChan := time.NewTimer(time.Second * 5).C subscriberCount := 100 waitGroup := sync.WaitGroup{} waitGroup.Add(subscriberCount) watchFunc := func() { subscriberID, channel, err := s.historyEventNotifier.WatchHistoryEvent(definition.NewWorkflowIdentifier(domainID, execution.GetWorkflowID(), execution.GetRunID())) s.Nil(err) timeourChan := time.NewTimer(time.Second * 10).C select { case msg := <-channel: s.Equal(historyEvent, msg) case <-timeourChan: s.Fail("subscribe to new events timeout") } err = s.historyEventNotifier.UnwatchHistoryEvent(definition.NewWorkflowIdentifier(domainID, execution.GetWorkflowID(), execution.GetRunID()), subscriberID) s.Nil(err) waitGroup.Done() } for count := 0; count < subscriberCount; count++ { go watchFunc() } <-timerChan s.historyEventNotifier.NotifyNewHistoryEvent(historyEvent) waitGroup.Wait() } ================================================ FILE: service/history/execution/cache.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination cache_mock.go package execution import ( "context" "sync/atomic" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" ) // Cache is a cache that holds workflow execution context type Cache interface { cache.Cache // GetOrCreateCurrentWorkflowExecution gets or creates workflow execution context for the current run GetOrCreateCurrentWorkflowExecution( ctx context.Context, domainID string, workflowID string, ) (Context, ReleaseFunc, error) // GetAndCreateWorkflowExecution is for analyzing mutableState, it will try getting Context from cache // and also load from database GetAndCreateWorkflowExecution( ctx context.Context, domainID string, execution types.WorkflowExecution, ) (Context, Context, ReleaseFunc, bool, error) // GetOrCreateWorkflowExecutionForBackground gets or creates workflow execution context with background context GetOrCreateWorkflowExecutionForBackground( domainID string, execution types.WorkflowExecution, ) (Context, ReleaseFunc, error) // GetOrCreateWorkflowExecutionWithTimeout gets or creates workflow execution context with timeout GetOrCreateWorkflowExecutionWithTimeout( domainID string, execution types.WorkflowExecution, timeout time.Duration, ) (Context, ReleaseFunc, error) // GetOrCreateWorkflowExecution gets or creates workflow execution context GetOrCreateWorkflowExecution( ctx context.Context, domainID string, execution types.WorkflowExecution, ) (Context, ReleaseFunc, error) } type ( // ReleaseFunc releases workflow execution context ReleaseFunc func(err error) // Cache caches workflow execution context cacheImpl struct { cache.Cache shard shard.Context executionManager persistence.ExecutionManager disabled bool logger log.Logger metricsClient metrics.Client config *config.Config } ) var ( // NoopReleaseFn is an no-op implementation for the ReleaseFunc type NoopReleaseFn ReleaseFunc = func(err error) {} ) const ( cacheNotReleased int32 = 0 cacheReleased int32 = 1 ) // NewCache creates a new workflow execution context cache func NewCache(shard shard.Context) Cache { opts := &cache.Options{} config := shard.GetConfig() opts.InitialCapacity = config.HistoryCacheInitialSize() opts.TTL = config.HistoryCacheTTL() opts.Pin = true opts.MaxCount = config.HistoryCacheMaxSize() opts.MetricsScope = shard.GetMetricsClient().Scope(metrics.HistoryExecutionCacheScope).Tagged(metrics.ShardIDTag(shard.GetShardID())) opts.Logger = shard.GetLogger().WithTags(tag.ComponentHistoryCache) opts.IsSizeBased = config.EnableSizeBasedHistoryExecutionCache opts.MaxSize = config.ExecutionCacheMaxByteSize return &cacheImpl{ Cache: cache.New(opts), shard: shard, executionManager: shard.GetExecutionManager(), logger: shard.GetLogger().WithTags(tag.ComponentHistoryCache), metricsClient: shard.GetMetricsClient(), config: config, } } // GetOrCreateCurrentWorkflowExecution gets or creates workflow execution context for the current run func (c *cacheImpl) GetOrCreateCurrentWorkflowExecution( ctx context.Context, domainID string, workflowID string, ) (Context, ReleaseFunc, error) { scope := metrics.HistoryCacheGetOrCreateCurrentScope c.metricsClient.IncCounter(scope, metrics.CacheRequests) sw := c.metricsClient.StartTimer(scope, metrics.CacheLatency) defer sw.Stop() // using empty run ID as current workflow run ID runID := "" execution := types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, } return c.getOrCreateWorkflowExecutionInternal( ctx, domainID, execution, scope, true, ) } // GetAndCreateWorkflowExecution is for analyzing mutableState, it will try getting Context from cache // and also load from database func (c *cacheImpl) GetAndCreateWorkflowExecution( ctx context.Context, domainID string, execution types.WorkflowExecution, ) (Context, Context, ReleaseFunc, bool, error) { scope := metrics.HistoryCacheGetAndCreateScope c.metricsClient.IncCounter(scope, metrics.CacheRequests) sw := c.metricsClient.StartTimer(scope, metrics.CacheLatency) defer sw.Stop() if err := c.validateWorkflowExecutionInfo(ctx, domainID, &execution); err != nil { c.metricsClient.IncCounter(scope, metrics.CacheFailures) return nil, nil, nil, false, err } key := definition.NewWorkflowIdentifier(domainID, execution.GetWorkflowID(), execution.GetRunID()) contextFromCache, cacheHit := c.Get(key).(Context) // TODO This will create a closure on every request. // Consider revisiting this if it causes too much GC activity releaseFunc := NoopReleaseFn // If cache hit, we need to lock the cache to prevent race condition if cacheHit { if err := contextFromCache.Lock(ctx); err != nil { // ctx is done before lock can be acquired c.Release(key) c.metricsClient.IncCounter(metrics.HistoryCacheGetAndCreateScope, metrics.CacheFailures) c.metricsClient.IncCounter(metrics.HistoryCacheGetAndCreateScope, metrics.AcquireLockFailedCounter) return nil, nil, nil, false, err } releaseFunc = c.makeReleaseFunc(key, contextFromCache, false) } else { c.metricsClient.IncCounter(metrics.HistoryCacheGetAndCreateScope, metrics.CacheMissCounter) } // Note, the one loaded from DB is not put into cache and don't affect any behavior contextFromDB := NewContext(domainID, execution, c.shard, c.executionManager, c.logger) return contextFromCache, contextFromDB, releaseFunc, cacheHit, nil } // GetOrCreateWorkflowExecutionForBackground gets or creates workflow execution context with background context // currently only used in tests func (c *cacheImpl) GetOrCreateWorkflowExecutionForBackground( domainID string, execution types.WorkflowExecution, ) (Context, ReleaseFunc, error) { return c.GetOrCreateWorkflowExecution(context.Background(), domainID, execution) } // GetOrCreateWorkflowExecutionWithTimeout gets or creates workflow execution context with timeout func (c *cacheImpl) GetOrCreateWorkflowExecutionWithTimeout( domainID string, execution types.WorkflowExecution, timeout time.Duration, ) (Context, ReleaseFunc, error) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() return c.GetOrCreateWorkflowExecution(ctx, domainID, execution) } // GetOrCreateWorkflowExecution gets or creates workflow execution context func (c *cacheImpl) GetOrCreateWorkflowExecution( ctx context.Context, domainID string, execution types.WorkflowExecution, ) (Context, ReleaseFunc, error) { scope := metrics.HistoryCacheGetOrCreateScope c.metricsClient.IncCounter(scope, metrics.CacheRequests) sw := c.metricsClient.StartTimer(scope, metrics.CacheLatency) defer sw.Stop() if err := c.validateWorkflowExecutionInfo(ctx, domainID, &execution); err != nil { c.metricsClient.IncCounter(scope, metrics.CacheFailures) return nil, nil, err } return c.getOrCreateWorkflowExecutionInternal( ctx, domainID, execution, scope, false, ) } func (c *cacheImpl) getOrCreateWorkflowExecutionInternal( ctx context.Context, domainID string, execution types.WorkflowExecution, scope metrics.ScopeIdx, forceClearContext bool, ) (Context, ReleaseFunc, error) { // Test hook for disabling the cache if c.disabled { return NewContext(domainID, execution, c.shard, c.executionManager, c.logger), NoopReleaseFn, nil } key := definition.NewWorkflowIdentifier(domainID, execution.GetWorkflowID(), execution.GetRunID()) workflowCtx, cacheHit := c.Get(key).(Context) if !cacheHit { c.metricsClient.IncCounter(scope, metrics.CacheMissCounter) // Let's create the workflow execution workflowCtx workflowCtx = NewContext(domainID, execution, c.shard, c.executionManager, c.logger) elem, err := c.PutIfNotExist(key, workflowCtx) if err != nil { c.metricsClient.IncCounter(scope, metrics.CacheFailures) return nil, nil, err } workflowCtx = elem.(Context) } // TODO This will create a closure on every request. // Consider revisiting this if it causes too much GC activity releaseFunc := c.makeReleaseFunc(key, workflowCtx, forceClearContext) if err := workflowCtx.Lock(ctx); err != nil { // ctx is done before lock can be acquired c.Release(key) c.metricsClient.IncCounter(scope, metrics.CacheFailures) c.metricsClient.IncCounter(scope, metrics.AcquireLockFailedCounter) return nil, nil, err } return workflowCtx, releaseFunc, nil } func (c *cacheImpl) validateWorkflowExecutionInfo( ctx context.Context, domainID string, execution *types.WorkflowExecution, ) error { if execution.GetWorkflowID() == "" { return &types.BadRequestError{Message: "Can't load workflow execution. WorkflowId not set."} } domainName, err := c.shard.GetDomainCache().GetDomainName(domainID) if err != nil { return err } // RunID is not provided, lets try to retrieve the RunID for current active execution if execution.GetRunID() == "" { response, err := c.getCurrentExecutionWithRetry(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: execution.GetWorkflowID(), DomainName: domainName, }) if err != nil { return err } execution.RunID = response.RunID } else if uuid.Parse(execution.GetRunID()) == nil { // immediately return if invalid runID return &types.BadRequestError{Message: "RunID is not valid UUID."} } return nil } func (c *cacheImpl) makeReleaseFunc( key definition.WorkflowIdentifier, context Context, forceClearContext bool, ) func(error) { status := cacheNotReleased return func(err error) { defer func() { if atomic.CompareAndSwapInt32(&status, cacheNotReleased, cacheReleased) { if rec := recover(); rec != nil { context.Clear() context.Unlock() c.Release(key) panic(rec) } else { if err != nil || forceClearContext { // TODO see issue #668, there are certain type or errors which can bypass the clear context.Clear() } context.Unlock() c.Release(key) } } }() } } func (c *cacheImpl) getCurrentExecutionWithRetry( ctx context.Context, request *persistence.GetCurrentExecutionRequest, ) (*persistence.GetCurrentExecutionResponse, error) { c.metricsClient.IncCounter(metrics.HistoryCacheGetCurrentExecutionScope, metrics.CacheRequests) sw := c.metricsClient.StartTimer(metrics.HistoryCacheGetCurrentExecutionScope, metrics.CacheLatency) defer sw.Stop() var response *persistence.GetCurrentExecutionResponse op := func(ctx context.Context) error { var err error response, err = c.executionManager.GetCurrentExecution(ctx, request) return err } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(common.CreatePersistenceRetryPolicy()), backoff.WithRetryableError(persistence.IsTransientError), ) err := throttleRetry.Do(ctx, op) if err != nil { c.metricsClient.IncCounter(metrics.HistoryCacheGetCurrentExecutionScope, metrics.CacheFailures) return nil, err } return response, nil } ================================================ FILE: service/history/execution/cache_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: cache.go // // Generated by this command: // // mockgen -package execution -source cache.go -destination cache_mock.go // // Package execution is a generated GoMock package. package execution import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" cache "github.com/uber/cadence/common/cache" types "github.com/uber/cadence/common/types" ) // MockCache is a mock of Cache interface. type MockCache struct { ctrl *gomock.Controller recorder *MockCacheMockRecorder isgomock struct{} } // MockCacheMockRecorder is the mock recorder for MockCache. type MockCacheMockRecorder struct { mock *MockCache } // NewMockCache creates a new mock instance. func NewMockCache(ctrl *gomock.Controller) *MockCache { mock := &MockCache{ctrl: ctrl} mock.recorder = &MockCacheMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCache) EXPECT() *MockCacheMockRecorder { return m.recorder } // Delete mocks base method. func (m *MockCache) Delete(key any) { m.ctrl.T.Helper() m.ctrl.Call(m, "Delete", key) } // Delete indicates an expected call of Delete. func (mr *MockCacheMockRecorder) Delete(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCache)(nil).Delete), key) } // Get mocks base method. func (m *MockCache) Get(key any) any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", key) ret0, _ := ret[0].(any) return ret0 } // Get indicates an expected call of Get. func (mr *MockCacheMockRecorder) Get(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCache)(nil).Get), key) } // GetAndCreateWorkflowExecution mocks base method. func (m *MockCache) GetAndCreateWorkflowExecution(ctx context.Context, domainID string, execution types.WorkflowExecution) (Context, Context, ReleaseFunc, bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAndCreateWorkflowExecution", ctx, domainID, execution) ret0, _ := ret[0].(Context) ret1, _ := ret[1].(Context) ret2, _ := ret[2].(ReleaseFunc) ret3, _ := ret[3].(bool) ret4, _ := ret[4].(error) return ret0, ret1, ret2, ret3, ret4 } // GetAndCreateWorkflowExecution indicates an expected call of GetAndCreateWorkflowExecution. func (mr *MockCacheMockRecorder) GetAndCreateWorkflowExecution(ctx, domainID, execution any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAndCreateWorkflowExecution", reflect.TypeOf((*MockCache)(nil).GetAndCreateWorkflowExecution), ctx, domainID, execution) } // GetOrCreateCurrentWorkflowExecution mocks base method. func (m *MockCache) GetOrCreateCurrentWorkflowExecution(ctx context.Context, domainID, workflowID string) (Context, ReleaseFunc, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrCreateCurrentWorkflowExecution", ctx, domainID, workflowID) ret0, _ := ret[0].(Context) ret1, _ := ret[1].(ReleaseFunc) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // GetOrCreateCurrentWorkflowExecution indicates an expected call of GetOrCreateCurrentWorkflowExecution. func (mr *MockCacheMockRecorder) GetOrCreateCurrentWorkflowExecution(ctx, domainID, workflowID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrCreateCurrentWorkflowExecution", reflect.TypeOf((*MockCache)(nil).GetOrCreateCurrentWorkflowExecution), ctx, domainID, workflowID) } // GetOrCreateWorkflowExecution mocks base method. func (m *MockCache) GetOrCreateWorkflowExecution(ctx context.Context, domainID string, execution types.WorkflowExecution) (Context, ReleaseFunc, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrCreateWorkflowExecution", ctx, domainID, execution) ret0, _ := ret[0].(Context) ret1, _ := ret[1].(ReleaseFunc) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // GetOrCreateWorkflowExecution indicates an expected call of GetOrCreateWorkflowExecution. func (mr *MockCacheMockRecorder) GetOrCreateWorkflowExecution(ctx, domainID, execution any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrCreateWorkflowExecution", reflect.TypeOf((*MockCache)(nil).GetOrCreateWorkflowExecution), ctx, domainID, execution) } // GetOrCreateWorkflowExecutionForBackground mocks base method. func (m *MockCache) GetOrCreateWorkflowExecutionForBackground(domainID string, execution types.WorkflowExecution) (Context, ReleaseFunc, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrCreateWorkflowExecutionForBackground", domainID, execution) ret0, _ := ret[0].(Context) ret1, _ := ret[1].(ReleaseFunc) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // GetOrCreateWorkflowExecutionForBackground indicates an expected call of GetOrCreateWorkflowExecutionForBackground. func (mr *MockCacheMockRecorder) GetOrCreateWorkflowExecutionForBackground(domainID, execution any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrCreateWorkflowExecutionForBackground", reflect.TypeOf((*MockCache)(nil).GetOrCreateWorkflowExecutionForBackground), domainID, execution) } // GetOrCreateWorkflowExecutionWithTimeout mocks base method. func (m *MockCache) GetOrCreateWorkflowExecutionWithTimeout(domainID string, execution types.WorkflowExecution, timeout time.Duration) (Context, ReleaseFunc, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrCreateWorkflowExecutionWithTimeout", domainID, execution, timeout) ret0, _ := ret[0].(Context) ret1, _ := ret[1].(ReleaseFunc) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // GetOrCreateWorkflowExecutionWithTimeout indicates an expected call of GetOrCreateWorkflowExecutionWithTimeout. func (mr *MockCacheMockRecorder) GetOrCreateWorkflowExecutionWithTimeout(domainID, execution, timeout any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrCreateWorkflowExecutionWithTimeout", reflect.TypeOf((*MockCache)(nil).GetOrCreateWorkflowExecutionWithTimeout), domainID, execution, timeout) } // Iterator mocks base method. func (m *MockCache) Iterator() cache.Iterator { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Iterator") ret0, _ := ret[0].(cache.Iterator) return ret0 } // Iterator indicates an expected call of Iterator. func (mr *MockCacheMockRecorder) Iterator() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Iterator", reflect.TypeOf((*MockCache)(nil).Iterator)) } // Put mocks base method. func (m *MockCache) Put(key, value any) any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Put", key, value) ret0, _ := ret[0].(any) return ret0 } // Put indicates an expected call of Put. func (mr *MockCacheMockRecorder) Put(key, value any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockCache)(nil).Put), key, value) } // PutIfNotExist mocks base method. func (m *MockCache) PutIfNotExist(key, value any) (any, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PutIfNotExist", key, value) ret0, _ := ret[0].(any) ret1, _ := ret[1].(error) return ret0, ret1 } // PutIfNotExist indicates an expected call of PutIfNotExist. func (mr *MockCacheMockRecorder) PutIfNotExist(key, value any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutIfNotExist", reflect.TypeOf((*MockCache)(nil).PutIfNotExist), key, value) } // Release mocks base method. func (m *MockCache) Release(key any) { m.ctrl.T.Helper() m.ctrl.Call(m, "Release", key) } // Release indicates an expected call of Release. func (mr *MockCacheMockRecorder) Release(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Release", reflect.TypeOf((*MockCache)(nil).Release), key) } // Size mocks base method. func (m *MockCache) Size() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Size") ret0, _ := ret[0].(int) return ret0 } // Size indicates an expected call of Size. func (mr *MockCacheMockRecorder) Size() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Size", reflect.TypeOf((*MockCache)(nil).Size)) } ================================================ FILE: service/history/execution/cache_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "context" "errors" "reflect" "sync" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/shard" ) type ( historyCacheSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext cache Cache } ) func TestHistoryCacheSuite(t *testing.T) { s := new(historyCacheSuite) suite.Run(t, s) } func (s *historyCacheSuite) SetupSuite() { } func (s *historyCacheSuite) TearDownSuite() { } func (s *historyCacheSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) } func (s *historyCacheSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *historyCacheSuite) TestHistoryCacheBasic() { s.cache = NewCache(s.mockShard) domainID := "test_domain_id" domainName := "test_domain_name" execution1 := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: uuid.New(), } s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() mockMS1 := NewMockMutableState(s.controller) ctx, release, err := s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, execution1) s.Nil(err) ctx.(*contextImpl).mutableState = mockMS1 release(nil) ctx, release, err = s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, execution1) s.Nil(err) s.Equal(mockMS1, ctx.(*contextImpl).mutableState) release(nil) execution2 := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: uuid.New(), } ctx, release, err = s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, execution2) s.Nil(err) s.NotEqual(mockMS1, ctx.(*contextImpl).mutableState) release(nil) } func (s *historyCacheSuite) TestHistoryCachePinning() { s.mockShard.GetConfig().HistoryCacheMaxSize = dynamicproperties.GetIntPropertyFn(1) domainID := "test_domain_id" s.cache = NewCache(s.mockShard) we := types.WorkflowExecution{ WorkflowID: "wf-cache-test-pinning", RunID: uuid.New(), } s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test_domain_name", nil).AnyTimes() ctx, release, err := s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, we) s.Nil(err) we2 := types.WorkflowExecution{ WorkflowID: "wf-cache-test-pinning", RunID: uuid.New(), } // Cache is full because ctx is pinned, should get an error now _, _, err2 := s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, we2) s.NotNil(err2) // Now release the ctx, this should unpin it. release(err2) _, release2, err3 := s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, we2) s.Nil(err3) release2(err3) // Old ctx should be evicted. newContext, release, err4 := s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, we) s.Nil(err4) s.False(ctx == newContext) release(err4) } func (s *historyCacheSuite) TestHistoryCacheClear() { s.mockShard.GetConfig().HistoryCacheMaxSize = dynamicproperties.GetIntPropertyFn(20) domainID := "test_domain_id" s.cache = NewCache(s.mockShard) we := types.WorkflowExecution{ WorkflowID: "wf-cache-test-clear", RunID: uuid.New(), } s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test_domain_name", nil).AnyTimes() ctx, release, err := s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, we) s.Nil(err) // since we are just testing whether the release function will clear the cache // all we need is a fake msBuilder ctx.(*contextImpl).mutableState = &mutableStateBuilder{} release(nil) // since last time, the release function receive a nil error // the ms builder will not be cleared ctx, release, err = s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, we) s.Nil(err) s.NotNil(ctx.(*contextImpl).mutableState) release(errors.New("some random error message")) // since last time, the release function receive a non-nil error // the ms builder will be cleared ctx, release, err = s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, we) s.Nil(err) s.Nil(ctx.(*contextImpl).mutableState) release(nil) } func (s *historyCacheSuite) TestHistoryCacheConcurrentAccess() { s.mockShard.GetConfig().HistoryCacheMaxSize = dynamicproperties.GetIntPropertyFn(20) domainID := "test_domain_id" s.cache = NewCache(s.mockShard) we := types.WorkflowExecution{ WorkflowID: "wf-cache-test-pinning", RunID: uuid.New(), } coroutineCount := 50 waitGroup := &sync.WaitGroup{} stopChan := make(chan struct{}) testFn := func() { <-stopChan ctx, release, err := s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, we) s.Nil(err) // since each time the builder is reset to nil s.Nil(ctx.(*contextImpl).mutableState) // since we are just testing whether the release function will clear the cache // all we need is a fake msBuilder ctx.(*contextImpl).mutableState = &mutableStateBuilder{} release(errors.New("some random error message")) waitGroup.Done() } s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-name", nil).AnyTimes() for i := 0; i < coroutineCount; i++ { waitGroup.Add(1) go testFn() } close(stopChan) waitGroup.Wait() ctx, release, err := s.cache.GetOrCreateWorkflowExecutionForBackground(domainID, we) s.Nil(err) // since we are just testing whether the release function will clear the cache // all we need is a fake msBuilder s.Nil(ctx.(*contextImpl).mutableState) release(nil) } func (s *historyCacheSuite) TestGetOrCreateCurrentWorkflowExecution() { tests := []struct { name string mockSetup func(c *cacheImpl, mockContext *MockContext, ctx context.Context) disabled bool cacheCtxNil bool noOpReleaseFn bool err error }{ { name: "success - cache enabled - cache miss", mockSetup: func(_ *cacheImpl, _ *MockContext, _ context.Context) {}, }, { name: "success - cache disabled", mockSetup: func(_ *cacheImpl, _ *MockContext, _ context.Context) {}, disabled: true, noOpReleaseFn: true, }, { name: "success - cache enabled - cache hit", mockSetup: func(c *cacheImpl, mockContext *MockContext, ctx context.Context) { mockContext.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() key := definition.NewWorkflowIdentifier(constants.TestDomainID, constants.TestWorkflowID, "") _, _ = c.Cache.PutIfNotExist(key, mockContext) mockContext.EXPECT().Lock(ctx).Return(nil).Times(1) }, }, { name: "error - cache enabled - cache hit error on lock", mockSetup: func(cache *cacheImpl, mockContext *MockContext, ctx context.Context) { mockContext.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() key := definition.NewWorkflowIdentifier(constants.TestDomainID, constants.TestWorkflowID, "") _, _ = cache.Cache.PutIfNotExist(key, mockContext) mockContext.EXPECT().Lock(ctx).Return(errors.New("test-error")).Times(1) }, cacheCtxNil: true, err: errors.New("test-error"), }, } for _, tt := range tests { s.Run(tt.name, func() { c := &cacheImpl{ Cache: cache.New(&cache.Options{ InitialCapacity: s.mockShard.GetConfig().HistoryCacheInitialSize(), TTL: s.mockShard.GetConfig().HistoryCacheTTL(), Pin: true, MaxCount: s.mockShard.GetConfig().HistoryCacheMaxSize(), Logger: s.mockShard.GetLogger().WithTags(tag.ComponentHistoryCache), }), shard: s.mockShard, executionManager: s.mockShard.GetExecutionManager(), logger: s.mockShard.GetLogger().WithTags(tag.ComponentHistoryCache), metricsClient: s.mockShard.GetMetricsClient(), config: s.mockShard.GetConfig(), } mockContext := NewMockContext(s.controller) c.disabled = tt.disabled ctx := context.Background() tt.mockSetup(c, mockContext, ctx) cacheCtx, releaseFunc, err := c.GetOrCreateCurrentWorkflowExecution(ctx, constants.TestDomainID, constants.TestWorkflowID) if tt.cacheCtxNil { s.Nil(cacheCtx) } else { s.NotNil(cacheCtx) } if tt.noOpReleaseFn { s.True(reflect.ValueOf(NoopReleaseFn).Pointer() == reflect.ValueOf(releaseFunc).Pointer()) } if tt.err != nil { s.Error(err) s.Equal(tt.err, err) s.Nil(releaseFunc) } else { s.NoError(err) s.NotNil(releaseFunc) } }) } } func (s *historyCacheSuite) TestGetOrCreateWorkflowExecution() { tests := []struct { name string workflowID string runID string cacheCtxNil bool mockSetup func(mockShard *shard.TestContext) err error }{ { name: "error - empty workflow ID", workflowID: "", runID: constants.TestRunID, mockSetup: func(_ *shard.TestContext) {}, cacheCtxNil: true, err: &types.BadRequestError{Message: "Can't load workflow execution. WorkflowId not set."}, }, { name: "error - get domain cache error", workflowID: constants.TestWorkflowID, runID: constants.TestRunID, mockSetup: func(mockShard *shard.TestContext) { mockShard.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return("", errors.New("test-error")).Times(1) }, cacheCtxNil: true, err: errors.New("test-error"), }, { name: "error - not valid runID", mockSetup: func(mockShard *shard.TestContext) { mockShard.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).Times(1) }, workflowID: constants.TestWorkflowID, runID: "invalid-run-id", cacheCtxNil: true, err: &types.BadRequestError{Message: "RunID is not valid UUID."}, }, { name: "error - empty runID retry failed", mockSetup: func(mockShard *shard.TestContext) { mockShard.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).Times(1) req := &persistence.GetCurrentExecutionRequest{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, DomainName: constants.TestDomainName, } mockShard.GetExecutionManager().(*mocks.ExecutionManager).On("GetCurrentExecution", mock.Anything, req).Return(nil, errors.New("test-error")).Times(1) }, workflowID: constants.TestWorkflowID, runID: "", cacheCtxNil: true, err: errors.New("test-error"), }, { name: "success - runID provided", mockSetup: func(mockShard *shard.TestContext) { mockShard.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).Times(1) }, workflowID: constants.TestWorkflowID, runID: constants.TestRunID, err: nil, }, { name: "success - runID not provided", mockSetup: func(mockShard *shard.TestContext) { mockShard.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).Times(1) req := &persistence.GetCurrentExecutionRequest{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, DomainName: constants.TestDomainName, } resp := &persistence.GetCurrentExecutionResponse{ RunID: constants.TestRunID, } mockShard.GetExecutionManager().(*mocks.ExecutionManager).On("GetCurrentExecution", mock.Anything, req).Return(resp, nil).Times(1) }, workflowID: constants.TestWorkflowID, runID: "", err: nil, }, } for _, tt := range tests { s.Run(tt.name, func() { s.cache = NewCache(s.mockShard) ctx := context.Background() tt.mockSetup(s.mockShard) cacheCtx, releaseFunc, err := s.cache.GetOrCreateWorkflowExecution(ctx, constants.TestDomainID, types.WorkflowExecution{WorkflowID: tt.workflowID, RunID: tt.runID}) if tt.cacheCtxNil { s.Nil(cacheCtx) } else { s.NotNil(cacheCtx) } if tt.err != nil { s.Error(err) s.Equal(tt.err, err) s.Nil(releaseFunc) } else { s.NoError(err) s.NotNil(releaseFunc) } }) } } func (s *historyCacheSuite) TestGetAndCreateWorkflowExecution() { ctx := context.Background() tests := []struct { name string domainID string execution types.WorkflowExecution mockSetup func(mockShard *shard.TestContext, mockContext *MockContext, c *cacheImpl) cacheHit bool cacheCtxNil bool err error }{ { name: "error - could not validate workflow execution info", domainID: constants.TestDomainID, execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, mockSetup: func(mockShard *shard.TestContext, _ *MockContext, _ *cacheImpl) { mockShard.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return("", errors.New("validate-error")).Times(1) }, cacheHit: false, cacheCtxNil: true, err: errors.New("validate-error"), }, { name: "success - cache miss", domainID: constants.TestDomainID, execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, mockSetup: func(mockShard *shard.TestContext, _ *MockContext, _ *cacheImpl) { mockShard.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).Times(1) }, cacheHit: false, cacheCtxNil: true, err: nil, }, { name: "error - cache hit error on lock", domainID: constants.TestDomainID, execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, mockSetup: func(mockShard *shard.TestContext, mockContext *MockContext, c *cacheImpl) { mockContext.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() mockShard.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).Times(1) key := definition.NewWorkflowIdentifier(constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID) _, _ = c.Cache.PutIfNotExist(key, mockContext) mockContext.EXPECT().Lock(ctx).Return(errors.New("lock-error")).Times(1) }, cacheHit: false, cacheCtxNil: true, err: errors.New("lock-error"), }, { name: "success - cache hit", domainID: constants.TestDomainID, execution: types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, mockSetup: func(mockShard *shard.TestContext, mockContext *MockContext, c *cacheImpl) { mockContext.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() mockShard.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).Times(1) key := definition.NewWorkflowIdentifier(constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID) _, _ = c.Cache.PutIfNotExist(key, mockContext) mockContext.EXPECT().Lock(ctx).Return(nil).Times(1) }, cacheHit: true, }, } for _, tt := range tests { s.Run(tt.name, func() { s.cache = NewCache(s.mockShard) mockContext := NewMockContext(s.controller) tt.mockSetup(s.mockShard, mockContext, s.cache.(*cacheImpl)) cacheCtx, dbCtx, release, cacheHit, err := s.cache.GetAndCreateWorkflowExecution(ctx, tt.domainID, tt.execution) s.Equal(tt.cacheHit, cacheHit) if tt.cacheCtxNil { s.Nil(cacheCtx) } else { s.NotNil(cacheCtx) s.Equal(mockContext, cacheCtx) } if tt.err != nil { s.Error(err) s.Equal(tt.err, err) s.Nil(dbCtx) s.Nil(release) } else { s.NoError(err) s.NotNil(dbCtx) s.NotNil(release) } }) } } func (s *historyCacheSuite) TestGetOrCreateWorkflowExecutionWithTimeout() { s.cache = NewCache(s.mockShard) workflowExecution := types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } newCtx := NewContext(constants.TestDomainID, workflowExecution, s.mockShard, s.mockShard.GetExecutionManager(), s.mockShard.GetLogger()) s.mockShard.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).Times(1) key := definition.NewWorkflowIdentifier(constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID) _, _ = s.cache.(*cacheImpl).Cache.PutIfNotExist(key, newCtx) // getting the lock to guarantee that the context will time out _ = newCtx.Lock(context.Background()) defer newCtx.Unlock() cacheCtx, release, err := s.cache.GetOrCreateWorkflowExecutionWithTimeout(constants.TestDomainID, workflowExecution, 0) s.Nil(cacheCtx) s.Nil(release) s.Error(err) s.Equal(context.DeadlineExceeded, err) } ================================================ FILE: service/history/execution/checksum.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "fmt" "slices" checksumgen "github.com/uber/cadence/.gen/go/checksum" "github.com/uber/cadence/common" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/types/mapper/thrift" ) const ( mutableStateChecksumPayloadV1 = 1 ) func generateMutableStateChecksum(ms MutableState) (checksum.Checksum, error) { payload := newMutableStateChecksumPayload(ms) csum, err := checksum.GenerateCRC32(payload, mutableStateChecksumPayloadV1) if err != nil { return checksum.Checksum{}, err } return csum, nil } func verifyMutableStateChecksum( ms MutableState, csum checksum.Checksum, ) error { if csum.Version != mutableStateChecksumPayloadV1 { return fmt.Errorf("invalid checksum payload version %v", csum.Version) } payload := newMutableStateChecksumPayload(ms) return checksum.Verify(payload, csum) } func newMutableStateChecksumPayload(ms MutableState) *checksumgen.MutableStateChecksumPayload { executionInfo := ms.GetExecutionInfo() payload := &checksumgen.MutableStateChecksumPayload{ CancelRequested: common.BoolPtr(executionInfo.CancelRequested), State: common.Int16Ptr(int16(executionInfo.State)), LastFirstEventID: common.Int64Ptr(executionInfo.LastFirstEventID), NextEventID: common.Int64Ptr(executionInfo.NextEventID), LastProcessedEventID: common.Int64Ptr(executionInfo.LastProcessedEvent), SignalCount: common.Int64Ptr(int64(executionInfo.SignalCount)), DecisionAttempt: common.Int32Ptr(int32(executionInfo.DecisionAttempt)), DecisionScheduledID: common.Int64Ptr(executionInfo.DecisionScheduleID), DecisionStartedID: common.Int64Ptr(executionInfo.DecisionStartedID), DecisionVersion: common.Int64Ptr(executionInfo.DecisionVersion), StickyTaskListName: common.StringPtr(executionInfo.StickyTaskList), } versionHistories := ms.GetVersionHistories() if versionHistories != nil { payload.VersionHistories = thrift.FromVersionHistories(versionHistories.ToInternalType()) } // for each of the pendingXXX ids below, sorting is needed to guarantee that // same serialized bytes can be generated during verification pendingTimerIDs := make([]int64, 0, len(ms.GetPendingTimerInfos())) for _, ti := range ms.GetPendingTimerInfos() { pendingTimerIDs = append(pendingTimerIDs, ti.StartedID) } slices.Sort(pendingTimerIDs) payload.PendingTimerStartedIDs = pendingTimerIDs pendingActivityIDs := make([]int64, 0, len(ms.GetPendingActivityInfos())) for id := range ms.GetPendingActivityInfos() { pendingActivityIDs = append(pendingActivityIDs, id) } slices.Sort(pendingActivityIDs) payload.PendingActivityScheduledIDs = pendingActivityIDs pendingChildIDs := make([]int64, 0, len(ms.GetPendingChildExecutionInfos())) for id := range ms.GetPendingChildExecutionInfos() { pendingChildIDs = append(pendingChildIDs, id) } slices.Sort(pendingChildIDs) payload.PendingChildInitiatedIDs = pendingChildIDs signalIDs := make([]int64, 0, len(ms.GetPendingSignalExternalInfos())) for id := range ms.GetPendingSignalExternalInfos() { signalIDs = append(signalIDs, id) } slices.Sort(signalIDs) payload.PendingSignalInitiatedIDs = signalIDs requestCancelIDs := make([]int64, 0, len(ms.GetPendingRequestCancelExternalInfos())) for id := range ms.GetPendingRequestCancelExternalInfos() { requestCancelIDs = append(requestCancelIDs, id) } slices.Sort(requestCancelIDs) payload.PendingReqCancelInitiatedIDs = requestCancelIDs return payload } ================================================ FILE: service/history/execution/context.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination context_mock.go -self_package github.com/uber/cadence/service/history/execution package execution import ( "context" "errors" "fmt" "strings" "testing" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/locks" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/shard" ) const ( defaultRemoteCallTimeout = 30 * time.Second checksumErrorRetryCount = 3 maxLockDuration = 1 * time.Second ) type conflictError struct { Cause error } func (e *conflictError) Error() string { return fmt.Sprintf("conditional update failed: %v", e.Cause) } func (e *conflictError) Unwrap() error { return e.Cause } // NewConflictError is only public because it used in workflow/util_test.go // TODO: refactor those tests func NewConflictError(_ *testing.T, cause error) error { return &conflictError{cause} } // IsConflictError checks whether a conflict has occurred while updating a workflow execution func IsConflictError(err error) bool { var e *conflictError return errors.As(err, &e) } type ( // Context is the processing context for all operations on workflow execution Context interface { GetDomainName() string GetDomainID() string GetExecution() *types.WorkflowExecution GetWorkflowExecution() MutableState SetWorkflowExecution(mutableState MutableState) LoadWorkflowExecution(ctx context.Context) (MutableState, error) LoadWorkflowExecutionWithTaskVersion(ctx context.Context, incomingVersion int64) (MutableState, error) LoadExecutionStats(ctx context.Context) (*persistence.ExecutionStats, error) Clear() Lock(ctx context.Context) error Unlock() GetHistorySize() int64 SetHistorySize(size int64) ReapplyEvents( eventBatches []*persistence.WorkflowEvents, ) error PersistStartWorkflowBatchEvents( ctx context.Context, workflowEvents *persistence.WorkflowEvents, ) (events.PersistedBlob, error) PersistNonStartWorkflowBatchEvents( ctx context.Context, workflowEvents *persistence.WorkflowEvents, ) (events.PersistedBlob, error) CreateWorkflowExecution( ctx context.Context, newWorkflow *persistence.WorkflowSnapshot, persistedHistory events.PersistedBlob, createMode persistence.CreateWorkflowMode, prevRunID string, // TODO(active-active): only passing prevLastWriteVersion might not be enough for active-active workflows, we may consider passing ActiveClusterSelectionPolicy as well // and include ActiveClusterSelectionPolicy in conditional update of current execution record prevLastWriteVersion int64, workflowRequestMode persistence.CreateWorkflowRequestMode, ) error ConflictResolveWorkflowExecution( ctx context.Context, now time.Time, conflictResolveMode persistence.ConflictResolveWorkflowMode, resetMutableState MutableState, newContext Context, newMutableState MutableState, currentContext Context, currentMutableState MutableState, currentTransactionPolicy *TransactionPolicy, ) error UpdateWorkflowExecutionAsActive( ctx context.Context, now time.Time, ) error UpdateWorkflowExecutionWithNewAsActive( ctx context.Context, now time.Time, newContext Context, newMutableState MutableState, ) error UpdateWorkflowExecutionAsPassive( ctx context.Context, now time.Time, ) error UpdateWorkflowExecutionWithNewAsPassive( ctx context.Context, now time.Time, newContext Context, newMutableState MutableState, ) error UpdateWorkflowExecutionWithNew( ctx context.Context, now time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentWorkflowTransactionPolicy TransactionPolicy, newWorkflowTransactionPolicy *TransactionPolicy, workflowRequestMode persistence.CreateWorkflowRequestMode, ) error UpdateWorkflowExecutionTasks( ctx context.Context, now time.Time, ) error cache.Sizeable } ) type ( contextImpl struct { domainID string workflowExecution types.WorkflowExecution shard shard.Context executionManager persistence.ExecutionManager logger log.Logger metricsClient metrics.Client mutex locks.Mutex lockTime time.Time maxLockDuration time.Duration mutableState MutableState stats *persistence.ExecutionStats appendHistoryNodesFn func(context.Context, string, types.WorkflowExecution, *persistence.AppendHistoryNodesRequest) (*persistence.AppendHistoryNodesResponse, error) persistStartWorkflowBatchEventsFn func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) persistNonStartWorkflowBatchEventsFn func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) getWorkflowExecutionFn func(context.Context, *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) createWorkflowExecutionFn func(context.Context, *persistence.CreateWorkflowExecutionRequest) (*persistence.CreateWorkflowExecutionResponse, error) updateWorkflowExecutionFn func(context.Context, *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) updateWorkflowExecutionWithNewFn func(context.Context, time.Time, persistence.UpdateWorkflowMode, Context, MutableState, TransactionPolicy, *TransactionPolicy, persistence.CreateWorkflowRequestMode) error notifyTasksFromWorkflowSnapshotFn func(*persistence.WorkflowSnapshot, events.PersistedBlobs, bool) notifyTasksFromWorkflowMutationFn func(*persistence.WorkflowMutation, events.PersistedBlobs, bool) emitSessionUpdateStatsFn func(string, *persistence.MutableStateUpdateSessionStats) emitWorkflowHistoryStatsFn func(string, int, int) emitWorkflowCompletionStatsFn func(string, string, string, string, string, *types.HistoryEvent) mergeContinueAsNewReplicationTasksFn func(persistence.UpdateWorkflowMode, *persistence.WorkflowMutation, *persistence.WorkflowSnapshot) error updateWorkflowExecutionEventReapplyFn func(persistence.UpdateWorkflowMode, []*persistence.WorkflowEvents, []*persistence.WorkflowEvents) error conflictResolveEventReapplyFn func(persistence.ConflictResolveWorkflowMode, []*persistence.WorkflowEvents, []*persistence.WorkflowEvents) error emitLargeWorkflowShardIDStatsFn func(int64, int64, int64, int64) emitWorkflowExecutionStatsFn func(string, *persistence.MutableStateStats, int64) createMutableStateFn func(shard.Context, log.Logger, *cache.DomainCacheEntry) MutableState } ) var _ Context = (*contextImpl)(nil) // NewContext creates a new workflow execution context func NewContext( domainID string, execution types.WorkflowExecution, shard shard.Context, executionManager persistence.ExecutionManager, logger log.Logger, ) Context { logger = logger.WithTags(tag.WorkflowDomainID(domainID), tag.WorkflowID(execution.GetWorkflowID()), tag.WorkflowRunID(execution.GetRunID())) ctx := &contextImpl{ domainID: domainID, workflowExecution: execution, shard: shard, executionManager: executionManager, logger: logger, metricsClient: shard.GetMetricsClient(), mutex: locks.NewMutex(), maxLockDuration: maxLockDuration, stats: &persistence.ExecutionStats{ HistorySize: 0, }, appendHistoryNodesFn: func(ctx context.Context, domainID string, workflowExecution types.WorkflowExecution, request *persistence.AppendHistoryNodesRequest) (*persistence.AppendHistoryNodesResponse, error) { return appendHistoryV2EventsWithRetry(ctx, shard, common.CreatePersistenceRetryPolicy(), domainID, workflowExecution, request) }, getWorkflowExecutionFn: func(ctx context.Context, request *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) { return getWorkflowExecutionWithRetry(ctx, shard, logger.Helper(), common.CreatePersistenceRetryPolicy(), request) }, createWorkflowExecutionFn: func(ctx context.Context, request *persistence.CreateWorkflowExecutionRequest) (*persistence.CreateWorkflowExecutionResponse, error) { return createWorkflowExecutionWithRetry(ctx, shard, logger.Helper(), common.CreatePersistenceRetryPolicy(), request) }, updateWorkflowExecutionFn: func(ctx context.Context, request *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) { return updateWorkflowExecutionWithRetry(ctx, shard, logger.Helper(), common.CreatePersistenceRetryPolicy(), request) }, notifyTasksFromWorkflowSnapshotFn: func(snapshot *persistence.WorkflowSnapshot, blobs events.PersistedBlobs, persistentError bool) { notifyTasksFromWorkflowSnapshot(shard.GetEngine(), snapshot, blobs, persistentError) }, notifyTasksFromWorkflowMutationFn: func(snapshot *persistence.WorkflowMutation, blobs events.PersistedBlobs, persistentError bool) { notifyTasksFromWorkflowMutation(shard.GetEngine(), snapshot, blobs, persistentError) }, emitSessionUpdateStatsFn: func(domainName string, stats *persistence.MutableStateUpdateSessionStats) { emitSessionUpdateStats(shard.GetMetricsClient(), domainName, stats) }, emitWorkflowHistoryStatsFn: func(domainName string, historySize int, eventCount int) { emitWorkflowHistoryStats(shard.GetMetricsClient(), domainName, historySize, eventCount) }, emitWorkflowCompletionStatsFn: func(domainName, workflowType, workflowID, runID, taskList string, event *types.HistoryEvent) { emitWorkflowCompletionStats(shard.GetMetricsClient(), logger.Helper(), domainName, workflowType, workflowID, runID, taskList, event) }, emitWorkflowExecutionStatsFn: func(domainName string, stats *persistence.MutableStateStats, historySize int64) { emitWorkflowExecutionStats(shard.GetMetricsClient(), domainName, stats, historySize) }, mergeContinueAsNewReplicationTasksFn: func(updateMode persistence.UpdateWorkflowMode, mutation *persistence.WorkflowMutation, snapshot *persistence.WorkflowSnapshot) error { return mergeContinueAsNewReplicationTasks(logger, updateMode, mutation, snapshot) }, createMutableStateFn: NewMutableStateBuilder, } ctx.persistStartWorkflowBatchEventsFn = ctx.PersistStartWorkflowBatchEvents ctx.persistNonStartWorkflowBatchEventsFn = ctx.PersistNonStartWorkflowBatchEvents ctx.updateWorkflowExecutionEventReapplyFn = ctx.updateWorkflowExecutionEventReapply ctx.conflictResolveEventReapplyFn = ctx.conflictResolveEventReapply ctx.emitLargeWorkflowShardIDStatsFn = ctx.emitLargeWorkflowShardIDStats ctx.updateWorkflowExecutionWithNewFn = ctx.UpdateWorkflowExecutionWithNew return ctx } func (c *contextImpl) Lock(ctx context.Context) error { err := c.mutex.Lock(ctx) if err != nil { return err } c.lockTime = time.Now() return nil } func (c *contextImpl) Unlock() { defer c.mutex.Unlock() if c.lockTime.IsZero() { // skip logging if the lock is never acquired return } elapsed := time.Since(c.lockTime) c.metricsClient.RecordTimer(metrics.WorkflowContextScope, metrics.WorkflowContextLockLatency, elapsed) if elapsed > c.maxLockDuration { c.maxLockDuration = elapsed c.logger.Info("workflow context lock is released. this is logged only when it's longer than maxLockDuration", tag.WorkflowContextLockLatency(elapsed)) } } func (c *contextImpl) Clear() { c.metricsClient.IncCounter(metrics.WorkflowContextScope, metrics.WorkflowContextCleared) c.mutableState = nil c.stats = &persistence.ExecutionStats{ HistorySize: 0, } } func (c *contextImpl) GetDomainID() string { return c.domainID } func (c *contextImpl) GetExecution() *types.WorkflowExecution { return &c.workflowExecution } func (c *contextImpl) GetDomainName() string { domainName, err := c.shard.GetDomainCache().GetDomainName(c.domainID) if err != nil { return "" } return domainName } func (c *contextImpl) GetHistorySize() int64 { return c.stats.HistorySize } func (c *contextImpl) SetHistorySize(size int64) { c.stats.HistorySize = size if c.mutableState != nil { c.mutableState.SetHistorySize(size) } } func (c *contextImpl) LoadExecutionStats( ctx context.Context, ) (*persistence.ExecutionStats, error) { _, err := c.LoadWorkflowExecution(ctx) if err != nil { return nil, err } return c.stats, nil } func isChecksumError(err error) bool { if err == nil { return false } return strings.Contains(err.Error(), "checksum mismatch error") } func (c *contextImpl) LoadWorkflowExecutionWithTaskVersion( ctx context.Context, incomingVersion int64, ) (MutableState, error) { domainEntry, err := c.shard.GetDomainCache().GetDomainByID(c.domainID) if err != nil { return nil, err } if c.mutableState == nil { var response *persistence.GetWorkflowExecutionResponse for i := 0; i < checksumErrorRetryCount; i++ { response, err = c.getWorkflowExecutionFn(ctx, &persistence.GetWorkflowExecutionRequest{ DomainID: c.domainID, Execution: c.workflowExecution, DomainName: domainEntry.GetInfo().Name, }) if err != nil { return nil, err } c.mutableState = c.createMutableStateFn(c.shard, c.logger, domainEntry) err = c.mutableState.Load(ctx, response.State) if err == nil { break } else if !isChecksumError(err) { c.logger.Error("failed to load mutable state", tag.Error(err)) break } // backoff before retry c.shard.GetTimeSource().Sleep(time.Millisecond * 100) } if isChecksumError(err) { c.metricsClient.IncCounter(metrics.WorkflowContextScope, metrics.StaleMutableStateCounter) c.logger.Error("encounter stale mutable state after retry", tag.Error(err)) } c.stats = response.State.ExecutionStats // finally emit execution and session stats c.emitWorkflowExecutionStatsFn(domainEntry.GetInfo().Name, response.MutableStateStats, c.stats.HistorySize) } flushBeforeReady, err := c.mutableState.StartTransaction(ctx, domainEntry, incomingVersion) if err != nil { return nil, err } if !flushBeforeReady { return c.mutableState, nil } c.logger.Debug("LoadWorkflowExecutionWithTaskVersion calling UpdateWorkflowExecutionAsActive", tag.WorkflowID(c.workflowExecution.GetWorkflowID())) if err = c.UpdateWorkflowExecutionAsActive(ctx, c.shard.GetTimeSource().Now()); err != nil { return nil, err } flushBeforeReady, err = c.mutableState.StartTransaction(ctx, domainEntry, incomingVersion) if err != nil { return nil, err } if flushBeforeReady { return nil, &types.InternalServiceError{ Message: "workflowExecutionContext counter flushBeforeReady status after loading mutable state from DB", } } return c.mutableState, nil } // GetWorkflowExecution should only be used in tests func (c *contextImpl) GetWorkflowExecution() MutableState { return c.mutableState } // SetWorkflowExecution should only be used in tests func (c *contextImpl) SetWorkflowExecution(mutableState MutableState) { c.mutableState = mutableState } func (c *contextImpl) LoadWorkflowExecution( ctx context.Context, ) (MutableState, error) { // Use empty version to skip incoming task version validation return c.LoadWorkflowExecutionWithTaskVersion(ctx, constants.EmptyVersion) } func (c *contextImpl) CreateWorkflowExecution( ctx context.Context, newWorkflow *persistence.WorkflowSnapshot, persistedHistory events.PersistedBlob, createMode persistence.CreateWorkflowMode, prevRunID string, prevLastWriteVersion int64, workflowRequestMode persistence.CreateWorkflowRequestMode, ) (retError error) { defer func() { if retError != nil { c.Clear() } }() domain, errorDomainName := c.shard.GetDomainCache().GetDomainName(c.domainID) if errorDomainName != nil { return errorDomainName } err := validateWorkflowRequestsAndMode(newWorkflow.WorkflowRequests, workflowRequestMode) if err != nil { if c.shard.GetConfig().EnableStrongIdempotencySanityCheck(domain) { return err } c.logger.Error("workflow requests and mode validation error", tag.Error(err)) } createRequest := &persistence.CreateWorkflowExecutionRequest{ // workflow create mode & prev run ID & version Mode: createMode, PreviousRunID: prevRunID, PreviousLastWriteVersion: prevLastWriteVersion, NewWorkflowSnapshot: *newWorkflow, WorkflowRequestMode: workflowRequestMode, DomainName: domain, } historySize := int64(len(persistedHistory.Data)) historySize += c.GetHistorySize() c.SetHistorySize(historySize) createRequest.NewWorkflowSnapshot.ExecutionStats = &persistence.ExecutionStats{ HistorySize: historySize, } resp, err := c.createWorkflowExecutionFn(ctx, createRequest) if err != nil { if isOperationPossiblySuccessfulError(err) { c.notifyTasksFromWorkflowSnapshotFn(newWorkflow, events.PersistedBlobs{persistedHistory}, true) } return err } c.notifyTasksFromWorkflowSnapshotFn(newWorkflow, events.PersistedBlobs{persistedHistory}, false) // finally emit session stats c.emitSessionUpdateStatsFn(domain, resp.MutableStateUpdateSessionStats) return nil } func (c *contextImpl) ConflictResolveWorkflowExecution( ctx context.Context, now time.Time, conflictResolveMode persistence.ConflictResolveWorkflowMode, resetMutableState MutableState, newContext Context, newMutableState MutableState, currentContext Context, currentMutableState MutableState, currentTransactionPolicy *TransactionPolicy, ) (retError error) { defer func() { if retError != nil { c.Clear() } }() resetWorkflow, resetWorkflowEventsSeq, err := resetMutableState.CloseTransactionAsSnapshot(now, TransactionPolicyPassive) if err != nil { return err } domain, errorDomainName := c.shard.GetDomainCache().GetDomainName(c.domainID) if errorDomainName != nil { return errorDomainName } var persistedBlobs events.PersistedBlobs resetHistorySize := c.GetHistorySize() for _, workflowEvents := range resetWorkflowEventsSeq { blob, err := c.persistNonStartWorkflowBatchEventsFn(ctx, workflowEvents) if err != nil { return err } resetHistorySize += int64(len(blob.Data)) persistedBlobs = append(persistedBlobs, blob) } c.SetHistorySize(resetHistorySize) resetWorkflow.ExecutionStats = &persistence.ExecutionStats{ HistorySize: resetHistorySize, } var newWorkflow *persistence.WorkflowSnapshot var newWorkflowEventsSeq []*persistence.WorkflowEvents if newContext != nil && newMutableState != nil { defer func() { if retError != nil { newContext.Clear() } }() newWorkflow, newWorkflowEventsSeq, err = newMutableState.CloseTransactionAsSnapshot(now, TransactionPolicyPassive) if err != nil { return err } if len(resetWorkflow.WorkflowRequests) != 0 && len(newWorkflow.WorkflowRequests) != 0 { if c.shard.GetConfig().EnableStrongIdempotencySanityCheck(domain) { return &types.InternalServiceError{Message: "workflow requests are only expected to be generated from either reset workflow or continue-as-new workflow for ConflictResolveWorkflowExecution"} } c.logger.Error("workflow requests are only expected to be generated from either reset workflow or continue-as-new workflow for ConflictResolveWorkflowExecution", tag.Number(int64(len(resetWorkflow.WorkflowRequests))), tag.NextNumber(int64(len(newWorkflow.WorkflowRequests)))) } newWorkflowSizeSize := newContext.GetHistorySize() startEvents := newWorkflowEventsSeq[0] blob, err := c.persistStartWorkflowBatchEventsFn(ctx, startEvents) if err != nil { return err } newWorkflowSizeSize += int64(len(blob.Data)) newContext.SetHistorySize(newWorkflowSizeSize) newWorkflow.ExecutionStats = &persistence.ExecutionStats{ HistorySize: newWorkflowSizeSize, } persistedBlobs = append(persistedBlobs, blob) } var currentWorkflow *persistence.WorkflowMutation var currentWorkflowEventsSeq []*persistence.WorkflowEvents if currentContext != nil && currentMutableState != nil && currentTransactionPolicy != nil { defer func() { if retError != nil { currentContext.Clear() } }() c.logger.Debug("ConflictResolveWorkflowExecution calling CloseTransactionAsMutation", tag.WorkflowID(c.workflowExecution.GetWorkflowID()), tag.Dynamic("policy", *currentTransactionPolicy)) currentWorkflow, currentWorkflowEventsSeq, err = currentMutableState.CloseTransactionAsMutation(now, *currentTransactionPolicy) if err != nil { return err } if len(currentWorkflow.WorkflowRequests) != 0 { if c.shard.GetConfig().EnableStrongIdempotencySanityCheck(domain) { return &types.InternalServiceError{Message: "workflow requests are not expected from current workflow for ConflictResolveWorkflowExecution"} } c.logger.Error("workflow requests are not expected from current workflow for ConflictResolveWorkflowExecution", tag.Counter(len(currentWorkflow.WorkflowRequests))) } currentWorkflowSize := currentContext.GetHistorySize() for _, workflowEvents := range currentWorkflowEventsSeq { blob, err := c.persistNonStartWorkflowBatchEventsFn(ctx, workflowEvents) if err != nil { return err } currentWorkflowSize += int64(len(blob.Data)) persistedBlobs = append(persistedBlobs, blob) } currentContext.SetHistorySize(currentWorkflowSize) currentWorkflow.ExecutionStats = &persistence.ExecutionStats{ HistorySize: currentWorkflowSize, } } if err := c.conflictResolveEventReapplyFn( conflictResolveMode, resetWorkflowEventsSeq, newWorkflowEventsSeq, // current workflow events will not participate in the events reapplication ); err != nil { return err } resp, err := c.shard.ConflictResolveWorkflowExecution(ctx, &persistence.ConflictResolveWorkflowExecutionRequest{ // RangeID , this is set by shard context Mode: conflictResolveMode, ResetWorkflowSnapshot: *resetWorkflow, NewWorkflowSnapshot: newWorkflow, CurrentWorkflowMutation: currentWorkflow, WorkflowRequestMode: persistence.CreateWorkflowRequestModeReplicated, // Encoding, this is set by shard context DomainName: domain, }) if err != nil { if isOperationPossiblySuccessfulError(err) { c.notifyTasksFromWorkflowSnapshotFn(resetWorkflow, persistedBlobs, true) c.notifyTasksFromWorkflowSnapshotFn(newWorkflow, persistedBlobs, true) c.notifyTasksFromWorkflowMutationFn(currentWorkflow, persistedBlobs, true) } return err } workflowState, workflowCloseState := resetMutableState.GetWorkflowStateCloseStatus() // Current branch changed and notify the watchers c.shard.GetEngine().NotifyNewHistoryEvent(events.NewNotification( c.domainID, &c.workflowExecution, resetMutableState.GetLastFirstEventID(), resetMutableState.GetNextEventID(), resetMutableState.GetPreviousStartedEventID(), workflowState, workflowCloseState, resetMutableState.GetVersionHistories().Duplicate(), )) c.notifyTasksFromWorkflowSnapshotFn(resetWorkflow, persistedBlobs, false) c.notifyTasksFromWorkflowSnapshotFn(newWorkflow, persistedBlobs, false) c.notifyTasksFromWorkflowMutationFn(currentWorkflow, persistedBlobs, false) // finally emit session stats c.emitWorkflowHistoryStatsFn(domain, int(c.stats.HistorySize), int(resetMutableState.GetNextEventID()-1)) c.emitSessionUpdateStatsFn(domain, resp.MutableStateUpdateSessionStats) // emit workflow completion stats if any if resetWorkflow.ExecutionInfo.State == persistence.WorkflowStateCompleted { if event, err := resetMutableState.GetCompletionEvent(ctx); err == nil { workflowType := resetWorkflow.ExecutionInfo.WorkflowTypeName taskList := resetWorkflow.ExecutionInfo.TaskList c.emitWorkflowCompletionStatsFn(domain, workflowType, c.workflowExecution.GetWorkflowID(), c.workflowExecution.GetRunID(), taskList, event) } } return nil } func (c *contextImpl) UpdateWorkflowExecutionAsActive( ctx context.Context, now time.Time, ) error { c.logger.Debug("UpdateWorkflowExecutionAsActive calling UpdateWorkflowExecutionWithNew", tag.WorkflowID(c.workflowExecution.GetWorkflowID()), tag.Dynamic("current policy", TransactionPolicyActive), tag.Dynamic("new policy", nil), ) return c.updateWorkflowExecutionWithNewFn(ctx, now, persistence.UpdateWorkflowModeUpdateCurrent, nil, nil, TransactionPolicyActive, nil, persistence.CreateWorkflowRequestModeNew) } func (c *contextImpl) UpdateWorkflowExecutionWithNewAsActive( ctx context.Context, now time.Time, newContext Context, newMutableState MutableState, ) error { c.logger.Debug("UpdateWorkflowExecutionWithNewAsActive calling UpdateWorkflowExecutionWithNew", tag.WorkflowID(c.workflowExecution.GetWorkflowID()), tag.Dynamic("current policy", TransactionPolicyActive), tag.Dynamic("new policy", TransactionPolicyActive), ) return c.updateWorkflowExecutionWithNewFn(ctx, now, persistence.UpdateWorkflowModeUpdateCurrent, newContext, newMutableState, TransactionPolicyActive, TransactionPolicyActive.Ptr(), persistence.CreateWorkflowRequestModeNew) } func (c *contextImpl) UpdateWorkflowExecutionAsPassive( ctx context.Context, now time.Time, ) error { c.logger.Debug("UpdateWorkflowExecutionAsPassive calling UpdateWorkflowExecutionWithNew", tag.WorkflowID(c.workflowExecution.GetWorkflowID()), tag.Dynamic("current policy", TransactionPolicyPassive), tag.Dynamic("new policy", nil), ) return c.updateWorkflowExecutionWithNewFn(ctx, now, persistence.UpdateWorkflowModeUpdateCurrent, nil, nil, TransactionPolicyPassive, nil, persistence.CreateWorkflowRequestModeReplicated) } func (c *contextImpl) UpdateWorkflowExecutionWithNewAsPassive( ctx context.Context, now time.Time, newContext Context, newMutableState MutableState, ) error { c.logger.Debug("UpdateWorkflowExecutionWithNewAsPassive calling UpdateWorkflowExecutionWithNew", tag.WorkflowID(c.workflowExecution.GetWorkflowID()), tag.Dynamic("current policy", TransactionPolicyPassive), tag.Dynamic("new policy", TransactionPolicyPassive), ) return c.updateWorkflowExecutionWithNewFn(ctx, now, persistence.UpdateWorkflowModeUpdateCurrent, newContext, newMutableState, TransactionPolicyPassive, TransactionPolicyPassive.Ptr(), persistence.CreateWorkflowRequestModeReplicated) } func (c *contextImpl) UpdateWorkflowExecutionTasks( ctx context.Context, now time.Time, ) (retError error) { defer func() { if retError != nil { c.Clear() } }() c.logger.Debug("UpdateWorkflowExecutionTask calling CloseTransactionAsMutation", tag.WorkflowID(c.workflowExecution.GetWorkflowID())) currentWorkflow, currentWorkflowEventsSeq, err := c.mutableState.CloseTransactionAsMutation(now, TransactionPolicyPassive) if err != nil { return err } if len(currentWorkflowEventsSeq) != 0 { return &types.InternalServiceError{ Message: "UpdateWorkflowExecutionTask can only be used for persisting new workflow tasks, but found new history events", } } domainName, errorDomainName := c.shard.GetDomainCache().GetDomainName(c.domainID) if errorDomainName != nil { return errorDomainName } if len(currentWorkflow.WorkflowRequests) != 0 { if c.shard.GetConfig().EnableStrongIdempotencySanityCheck(domainName) { return &types.InternalServiceError{Message: "UpdateWorkflowExecutionTask can only be used for persisting new workflow tasks, but found new workflow requests"} } c.logger.Error("UpdateWorkflowExecutionTask can only be used for persisting new workflow tasks, but found new workflow requests", tag.Counter(len(currentWorkflow.WorkflowRequests))) } currentWorkflow.ExecutionStats = &persistence.ExecutionStats{ HistorySize: c.GetHistorySize(), } resp, err := c.updateWorkflowExecutionFn(ctx, &persistence.UpdateWorkflowExecutionRequest{ // RangeID , this is set by shard context Mode: persistence.UpdateWorkflowModeIgnoreCurrent, UpdateWorkflowMutation: *currentWorkflow, // Encoding, this is set by shard context DomainName: domainName, }) if err != nil { if isOperationPossiblySuccessfulError(err) { c.notifyTasksFromWorkflowMutationFn(currentWorkflow, nil, true) } return err } // notify current workflow tasks c.notifyTasksFromWorkflowMutationFn(currentWorkflow, nil, false) c.emitSessionUpdateStatsFn(domainName, resp.MutableStateUpdateSessionStats) return nil } func (c *contextImpl) UpdateWorkflowExecutionWithNew( ctx context.Context, now time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentWorkflowTransactionPolicy TransactionPolicy, newWorkflowTransactionPolicy *TransactionPolicy, workflowRequestMode persistence.CreateWorkflowRequestMode, ) (retError error) { defer func() { if retError != nil { c.Clear() } }() c.logger.Debug("UpdateWorkflowExecutionWithNew calling CloseTransactionAsMutation", tag.WorkflowID(c.workflowExecution.GetWorkflowID()), tag.Dynamic("policy", currentWorkflowTransactionPolicy)) currentWorkflow, currentWorkflowEventsSeq, err := c.mutableState.CloseTransactionAsMutation(now, currentWorkflowTransactionPolicy) if err != nil { return err } domain, errorDomainName := c.shard.GetDomainCache().GetDomainName(c.domainID) if errorDomainName != nil { return errorDomainName } err = validateWorkflowRequestsAndMode(currentWorkflow.WorkflowRequests, workflowRequestMode) if err != nil { if c.shard.GetConfig().EnableStrongIdempotencySanityCheck(domain) { return err } c.logger.Error("workflow requests and mode validation error", tag.Error(err)) } var persistedBlobs events.PersistedBlobs currentWorkflowSize := c.GetHistorySize() oldWorkflowSize := currentWorkflowSize currentWorkflowHistoryCount := c.mutableState.GetNextEventID() - 1 oldWorkflowHistoryCount := currentWorkflowHistoryCount for _, workflowEvents := range currentWorkflowEventsSeq { blob, err := c.persistNonStartWorkflowBatchEventsFn(ctx, workflowEvents) if err != nil { return err } currentWorkflowHistoryCount += int64(len(workflowEvents.Events)) currentWorkflowSize += int64(len(blob.Data)) persistedBlobs = append(persistedBlobs, blob) } c.SetHistorySize(currentWorkflowSize) currentWorkflow.ExecutionStats = &persistence.ExecutionStats{ HistorySize: currentWorkflowSize, } var newWorkflow *persistence.WorkflowSnapshot var newWorkflowEventsSeq []*persistence.WorkflowEvents if newContext != nil && newMutableState != nil && newWorkflowTransactionPolicy != nil { defer func() { if retError != nil { newContext.Clear() } }() newWorkflow, newWorkflowEventsSeq, err = newMutableState.CloseTransactionAsSnapshot( now, *newWorkflowTransactionPolicy, ) if err != nil { return err } if len(newWorkflow.WorkflowRequests) != 0 && len(currentWorkflow.WorkflowRequests) != 0 { if c.shard.GetConfig().EnableStrongIdempotencySanityCheck(domain) { return &types.InternalServiceError{Message: "workflow requests are only expected to be generated from one workflow for UpdateWorkflowExecution"} } c.logger.Error("workflow requests are only expected to be generated from one workflow for UpdateWorkflowExecution", tag.Number(int64(len(currentWorkflow.WorkflowRequests))), tag.NextNumber(int64(len(newWorkflow.WorkflowRequests)))) } err := validateWorkflowRequestsAndMode(newWorkflow.WorkflowRequests, workflowRequestMode) if err != nil { if c.shard.GetConfig().EnableStrongIdempotencySanityCheck(domain) { return err } c.logger.Error("workflow requests and mode validation error", tag.Error(err)) } newWorkflowSizeSize := newContext.GetHistorySize() startEvents := newWorkflowEventsSeq[0] firstEventID := startEvents.Events[0].ID var blob events.PersistedBlob if firstEventID == constants.FirstEventID { blob, err = c.persistStartWorkflowBatchEventsFn(ctx, startEvents) if err != nil { return err } } else { // NOTE: This is the case for reset workflow, reset workflow already inserted a branch record blob, err = c.persistNonStartWorkflowBatchEventsFn(ctx, startEvents) if err != nil { return err } } persistedBlobs = append(persistedBlobs, blob) newWorkflowSizeSize += int64(len(blob.Data)) newContext.SetHistorySize(newWorkflowSizeSize) newWorkflow.ExecutionStats = &persistence.ExecutionStats{ HistorySize: newWorkflowSizeSize, } } if err := c.mergeContinueAsNewReplicationTasksFn(updateMode, currentWorkflow, newWorkflow); err != nil { return err } if err := c.updateWorkflowExecutionEventReapplyFn(updateMode, currentWorkflowEventsSeq, newWorkflowEventsSeq); err != nil { return err } resp, err := c.updateWorkflowExecutionFn(ctx, &persistence.UpdateWorkflowExecutionRequest{ // RangeID , this is set by shard context Mode: updateMode, UpdateWorkflowMutation: *currentWorkflow, NewWorkflowSnapshot: newWorkflow, WorkflowRequestMode: workflowRequestMode, // Encoding, this is set by shard context DomainName: domain, }) if err != nil { if isOperationPossiblySuccessfulError(err) { c.notifyTasksFromWorkflowMutationFn(currentWorkflow, persistedBlobs, true) c.notifyTasksFromWorkflowSnapshotFn(newWorkflow, persistedBlobs, true) } return err } // for any change in the workflow, send a event workflowState, workflowCloseState := c.mutableState.GetWorkflowStateCloseStatus() c.shard.GetEngine().NotifyNewHistoryEvent(events.NewNotification( c.domainID, &c.workflowExecution, c.mutableState.GetLastFirstEventID(), c.mutableState.GetNextEventID(), c.mutableState.GetPreviousStartedEventID(), workflowState, workflowCloseState, c.mutableState.GetVersionHistories().Duplicate(), )) // notify current workflow tasks c.notifyTasksFromWorkflowMutationFn(currentWorkflow, persistedBlobs, false) // notify new workflow tasks c.notifyTasksFromWorkflowSnapshotFn(newWorkflow, persistedBlobs, false) // finally emit session stats c.emitWorkflowHistoryStatsFn(domain, int(c.stats.HistorySize), int(c.mutableState.GetNextEventID()-1)) c.emitSessionUpdateStatsFn(domain, resp.MutableStateUpdateSessionStats) c.emitLargeWorkflowShardIDStatsFn(currentWorkflowSize-oldWorkflowSize, oldWorkflowHistoryCount, oldWorkflowSize, currentWorkflowHistoryCount) // emit workflow completion stats if any if currentWorkflow.ExecutionInfo.State == persistence.WorkflowStateCompleted { if event, err := c.mutableState.GetCompletionEvent(ctx); err == nil { workflowType := currentWorkflow.ExecutionInfo.WorkflowTypeName taskList := currentWorkflow.ExecutionInfo.TaskList c.emitWorkflowCompletionStatsFn(domain, workflowType, c.workflowExecution.GetWorkflowID(), c.workflowExecution.GetRunID(), taskList, event) } } return nil } func notifyTasksFromWorkflowSnapshot( engine engine.Engine, workflowSnapShot *persistence.WorkflowSnapshot, history events.PersistedBlobs, persistenceError bool, ) { if workflowSnapShot == nil { return } notifyTasks( engine, workflowSnapShot.ExecutionInfo, workflowSnapShot.VersionHistories, workflowSnapShot.ActivityInfos, workflowSnapShot.TasksByCategory, history, persistenceError, ) } func notifyTasksFromWorkflowMutation( engine engine.Engine, workflowMutation *persistence.WorkflowMutation, history events.PersistedBlobs, persistenceError bool, ) { if workflowMutation == nil { return } notifyTasks( engine, workflowMutation.ExecutionInfo, workflowMutation.VersionHistories, workflowMutation.UpsertActivityInfos, workflowMutation.TasksByCategory, history, persistenceError, ) } func activityInfosToMap(ais []*persistence.ActivityInfo) map[int64]*persistence.ActivityInfo { m := make(map[int64]*persistence.ActivityInfo, len(ais)) for _, ai := range ais { m[ai.ScheduleID] = ai } return m } func notifyTasks( engine engine.Engine, executionInfo *persistence.WorkflowExecutionInfo, versionHistories *persistence.VersionHistories, activities []*persistence.ActivityInfo, tasksByCategory map[persistence.HistoryTaskCategory][]persistence.Task, history events.PersistedBlobs, persistenceError bool, ) { transferTaskInfo := &hcommon.NotifyTaskInfo{ ExecutionInfo: executionInfo, Tasks: tasksByCategory[persistence.HistoryTaskCategoryTransfer], PersistenceError: persistenceError, } timerTaskInfo := &hcommon.NotifyTaskInfo{ ExecutionInfo: executionInfo, Tasks: tasksByCategory[persistence.HistoryTaskCategoryTimer], PersistenceError: persistenceError, } replicationTaskInfo := &hcommon.NotifyTaskInfo{ ExecutionInfo: executionInfo, Tasks: tasksByCategory[persistence.HistoryTaskCategoryReplication], VersionHistories: versionHistories, Activities: activityInfosToMap(activities), History: history, PersistenceError: persistenceError, } // TODO: unify these methods engine.NotifyNewTransferTasks(transferTaskInfo) engine.NotifyNewTimerTasks(timerTaskInfo) engine.NotifyNewReplicationTasks(replicationTaskInfo) } func mergeContinueAsNewReplicationTasks( logger log.Logger, updateMode persistence.UpdateWorkflowMode, currentWorkflowMutation *persistence.WorkflowMutation, newWorkflowSnapshot *persistence.WorkflowSnapshot, ) error { if currentWorkflowMutation.ExecutionInfo.CloseStatus != persistence.WorkflowCloseStatusContinuedAsNew { return nil } else if updateMode == persistence.UpdateWorkflowModeBypassCurrent && newWorkflowSnapshot == nil { // update current workflow as zombie & continue as new without new zombie workflow // this case can be valid if new workflow is already created by resend return nil } // current workflow is doing continue as new // it is possible that continue as new is done as part of passive logic if len(currentWorkflowMutation.TasksByCategory[persistence.HistoryTaskCategoryReplication]) == 0 { logger.Debug("mergeContinueAsNewReplicationTasks: no replication task", tag.WorkflowDomainID(currentWorkflowMutation.ExecutionInfo.DomainID), tag.WorkflowID(currentWorkflowMutation.ExecutionInfo.WorkflowID), tag.WorkflowRunID(currentWorkflowMutation.ExecutionInfo.RunID), ) return nil } if newWorkflowSnapshot == nil || len(newWorkflowSnapshot.TasksByCategory[persistence.HistoryTaskCategoryReplication]) != 1 { return &types.InternalServiceError{ Message: "unable to find replication task from new workflow for continue as new replication", } } // merge the new run first event batch replication task // to current event batch replication task newRunTask := newWorkflowSnapshot.TasksByCategory[persistence.HistoryTaskCategoryReplication][0].(*persistence.HistoryReplicationTask) newWorkflowSnapshot.TasksByCategory[persistence.HistoryTaskCategoryReplication] = nil newRunBranchToken := newRunTask.BranchToken taskUpdated := false for _, replicationTask := range currentWorkflowMutation.TasksByCategory[persistence.HistoryTaskCategoryReplication] { if task, ok := replicationTask.(*persistence.HistoryReplicationTask); ok { taskUpdated = true task.NewRunBranchToken = newRunBranchToken logger.Debug("mergeContinueAsNewReplicationTasks: updated replication task", tag.WorkflowDomainID(currentWorkflowMutation.ExecutionInfo.DomainID), tag.WorkflowID(currentWorkflowMutation.ExecutionInfo.WorkflowID), tag.WorkflowRunID(currentWorkflowMutation.ExecutionInfo.RunID), tag.Dynamic("taskid", task.TaskID), tag.Dynamic("version", task.Version), tag.Dynamic("visibility_ts", task.VisibilityTimestamp.Format(time.RFC3339)), ) } } if !taskUpdated { return &types.InternalServiceError{ Message: "unable to find replication task from current workflow for continue as new replication", } } return nil } func (c *contextImpl) PersistStartWorkflowBatchEvents( ctx context.Context, workflowEvents *persistence.WorkflowEvents, ) (events.PersistedBlob, error) { if len(workflowEvents.Events) == 0 { return events.PersistedBlob{}, &types.InternalServiceError{ Message: "cannot persist first workflow events with empty events", } } domainID := workflowEvents.DomainID domainName, err := c.shard.GetDomainCache().GetDomainName(domainID) if err != nil { return events.PersistedBlob{}, err } workflowID := workflowEvents.WorkflowID runID := workflowEvents.RunID execution := types.WorkflowExecution{ WorkflowID: workflowEvents.WorkflowID, RunID: workflowEvents.RunID, } resp, err := c.appendHistoryNodesFn( ctx, domainID, execution, &persistence.AppendHistoryNodesRequest{ IsNewBranch: true, Info: persistence.BuildHistoryGarbageCleanupInfo(domainID, workflowID, runID), BranchToken: workflowEvents.BranchToken, Events: workflowEvents.Events, DomainName: domainName, // TransactionID is set by shard context }, ) if err != nil { return events.PersistedBlob{}, err } return events.PersistedBlob{ DataBlob: resp.DataBlob, BranchToken: workflowEvents.BranchToken, FirstEventID: workflowEvents.Events[0].ID, }, nil } func (c *contextImpl) PersistNonStartWorkflowBatchEvents( ctx context.Context, workflowEvents *persistence.WorkflowEvents, ) (events.PersistedBlob, error) { if len(workflowEvents.Events) == 0 { return events.PersistedBlob{}, nil // allow update workflow without events } domainID := workflowEvents.DomainID domainName, err := c.shard.GetDomainCache().GetDomainName(domainID) if err != nil { return events.PersistedBlob{}, err } execution := types.WorkflowExecution{ WorkflowID: workflowEvents.WorkflowID, RunID: workflowEvents.RunID, } resp, err := c.appendHistoryNodesFn( ctx, domainID, execution, &persistence.AppendHistoryNodesRequest{ IsNewBranch: false, BranchToken: workflowEvents.BranchToken, Events: workflowEvents.Events, DomainName: domainName, // TransactionID is set by shard context }, ) if err != nil { return events.PersistedBlob{}, err } return events.PersistedBlob{ DataBlob: resp.DataBlob, BranchToken: workflowEvents.BranchToken, FirstEventID: workflowEvents.Events[0].ID, }, nil } func appendHistoryV2EventsWithRetry( ctx context.Context, shardContext shard.Context, retryPolicy backoff.RetryPolicy, domainID string, execution types.WorkflowExecution, request *persistence.AppendHistoryNodesRequest, ) (*persistence.AppendHistoryNodesResponse, error) { var resp *persistence.AppendHistoryNodesResponse op := func(ctx context.Context) error { var err error resp, err = shardContext.AppendHistoryV2Events(ctx, request, domainID, execution) return err } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(persistence.IsTransientError), ) err := throttleRetry.Do(ctx, op) return resp, err } func createWorkflowExecutionWithRetry( ctx context.Context, shardContext shard.Context, logger log.Logger, retryPolicy backoff.RetryPolicy, request *persistence.CreateWorkflowExecutionRequest, ) (*persistence.CreateWorkflowExecutionResponse, error) { logger = logger.Helper() var resp *persistence.CreateWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = shardContext.CreateWorkflowExecution(ctx, request) return err } isRetryable := func(err error) bool { if _, ok := err.(*persistence.TimeoutError); ok { // TODO: is timeout error retryable for create workflow? // if we treat it as retryable, user may receive workflowAlreadyRunning error // on the first start workflow execution request. return false } return persistence.IsTransientError(err) } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(isRetryable), ) err := throttleRetry.Do(ctx, op) switch err.(type) { case nil: return resp, nil case *persistence.WorkflowExecutionAlreadyStartedError: // it is possible that workflow already exists and caller need to apply // workflow ID reuse policy return nil, err default: logger.Error( "Persistent store operation failure", tag.StoreOperationCreateWorkflowExecution, tag.Error(err), ) return nil, err } } func getWorkflowExecutionWithRetry( ctx context.Context, shardContext shard.Context, logger log.Logger, retryPolicy backoff.RetryPolicy, request *persistence.GetWorkflowExecutionRequest, ) (*persistence.GetWorkflowExecutionResponse, error) { logger.Helper() var resp *persistence.GetWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = shardContext.GetWorkflowExecution(ctx, request) return err } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(persistence.IsTransientError), ) err := throttleRetry.Do(ctx, op) switch err.(type) { case nil: return resp, nil case *types.EntityNotExistsError: // it is possible that workflow does not exists return nil, err default: // If error is shard closed, only log error if shard has been closed for a while, // otherwise always log var shardClosedError *shard.ErrShardClosed if !errors.As(err, &shardClosedError) || shardContext.GetTimeSource().Since(shardClosedError.ClosedAt) > shard.TimeBeforeShardClosedIsError { logger.Error("Persistent fetch operation failure", tag.StoreOperationGetWorkflowExecution, tag.Error(err)) } return nil, err } } func updateWorkflowExecutionWithRetry( ctx context.Context, shardContext shard.Context, logger log.Logger, retryPolicy backoff.RetryPolicy, request *persistence.UpdateWorkflowExecutionRequest, ) (*persistence.UpdateWorkflowExecutionResponse, error) { logger.Helper() var resp *persistence.UpdateWorkflowExecutionResponse op := func(ctx context.Context) error { var err error resp, err = shardContext.UpdateWorkflowExecution(ctx, request) return err } // Preparation for the task Validation. // metricsClient := c.shard.GetMetricsClient() // domainCache := c.shard.GetDomainCache() // executionManager := c.shard.GetExecutionManager() // historymanager := c.shard.GetHistoryManager() // zapLogger, _ := zap.NewProduction() // checker, _ := taskvalidator.NewWfChecker(zapLogger, metricsClient, domainCache, executionManager, historymanager) isRetryable := func(err error) bool { if _, ok := err.(*persistence.TimeoutError); ok { // timeout error is not retryable for update workflow execution return false } return persistence.IsTransientError(err) } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(isRetryable), ) err := throttleRetry.Do(ctx, op) switch err.(type) { case nil: return resp, nil case *persistence.ConditionFailedError: return nil, &conflictError{err} default: logger.Error( "Persistent store operation failure", tag.StoreOperationUpdateWorkflowExecution, tag.Error(err), tag.Number(request.UpdateWorkflowMutation.Condition), ) // TODO: Call the Task Validation here so that it happens whenever an error happen during Update. // err1 := checker.WorkflowCheckforValidation( // ctx, // c.workflowExecution.GetWorkflowID(), // c.domainID, // c.GetDomainName(), // c.workflowExecution.GetRunID(), // ) // if err1 != nil { // return nil, err1 // } return nil, err } } func (c *contextImpl) updateWorkflowExecutionEventReapply( updateMode persistence.UpdateWorkflowMode, eventBatch1 []*persistence.WorkflowEvents, eventBatch2 []*persistence.WorkflowEvents, ) error { if updateMode != persistence.UpdateWorkflowModeBypassCurrent { return nil } var eventBatches []*persistence.WorkflowEvents eventBatches = append(eventBatches, eventBatch1...) eventBatches = append(eventBatches, eventBatch2...) return c.ReapplyEvents(eventBatches) } func (c *contextImpl) conflictResolveEventReapply( conflictResolveMode persistence.ConflictResolveWorkflowMode, eventBatch1 []*persistence.WorkflowEvents, eventBatch2 []*persistence.WorkflowEvents, ) error { if conflictResolveMode != persistence.ConflictResolveWorkflowModeBypassCurrent { return nil } var eventBatches []*persistence.WorkflowEvents eventBatches = append(eventBatches, eventBatch1...) eventBatches = append(eventBatches, eventBatch2...) return c.ReapplyEvents(eventBatches) } func (c *contextImpl) ReapplyEvents( eventBatches []*persistence.WorkflowEvents, ) error { // NOTE: this function should only be used to workflow which is // not the caller, or otherwise deadlock will appear if len(eventBatches) == 0 { return nil } domainID := eventBatches[0].DomainID workflowID := eventBatches[0].WorkflowID runID := eventBatches[0].RunID domainCache := c.shard.GetDomainCache() domainEntry, err := domainCache.GetDomainByID(domainID) if err != nil { return err } if domainEntry.IsDomainPendingActive() { return nil } var reapplyEvents []*types.HistoryEvent for _, events := range eventBatches { if events.DomainID != domainID || events.WorkflowID != workflowID { return &types.InternalServiceError{ Message: "workflowExecutionContext encounter mismatch domainID / workflowID in events reapplication.", } } for _, event := range events.Events { switch event.GetEventType() { case types.EventTypeWorkflowExecutionSignaled: reapplyEvents = append(reapplyEvents, event) } } } if len(reapplyEvents) == 0 { return nil } ctx, cancel := context.WithTimeout(context.Background(), defaultRemoteCallTimeout) defer cancel() activeClusterInfo, err := c.shard.GetActiveClusterManager().GetActiveClusterInfoByWorkflow(ctx, domainID, workflowID, runID) if err != nil { return err } if activeClusterInfo.ActiveClusterName == c.shard.GetClusterMetadata().GetCurrentClusterName() { return c.shard.GetEngine().ReapplyEvents( ctx, domainID, workflowID, runID, reapplyEvents, ) } // Reapply events only reapply to the current run. // The run id is only used for reapply event de-duplication execution := &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, } clientBean := c.shard.GetService().GetClientBean() serializer := c.shard.GetService().GetPayloadSerializer() // The active cluster of the domain is the same as current cluster. // Use the history from the same cluster to reapply events reapplyEventsDataBlob, err := serializer.SerializeBatchEvents( reapplyEvents, constants.EncodingTypeThriftRW, ) if err != nil { return err } // The active cluster of the domain is differ from the current cluster // Use frontend client to route this request to the active cluster // Reapplication only happens in active cluster sourceCluster, err := clientBean.GetRemoteAdminClient(activeClusterInfo.ActiveClusterName) if err != nil { return &types.InternalServiceError{ Message: err.Error(), } } return sourceCluster.ReapplyEvents( ctx, &types.ReapplyEventsRequest{ DomainName: domainEntry.GetInfo().Name, WorkflowExecution: execution, Events: reapplyEventsDataBlob.ToInternal(), }, ) } func (c *contextImpl) ByteSize() uint64 { var size int // Estimate size of strings size += len(c.domainID) size += len(c.workflowExecution.GetWorkflowID()) + len(c.workflowExecution.GetRunID()) size += 3 * constants.StringSizeOverheadBytes size += 3 * 8 // logger size += 512 // MetricsClient estimation size += 256 // ExecutionManager estimation size += 8 // Mutex size += 1024 // Mutable-state estimation size += 8 // stats pointer size += 18 * 8 // 18 function pointers with 8 bytes each return uint64(size) } func isOperationPossiblySuccessfulError(err error) bool { switch err.(type) { case nil: return false case *types.WorkflowExecutionAlreadyStartedError, *persistence.WorkflowExecutionAlreadyStartedError, *persistence.CurrentWorkflowConditionFailedError, *persistence.ConditionFailedError, *types.ServiceBusyError, *types.LimitExceededError, *persistence.ShardOwnershipLostError: return false case *persistence.TimeoutError: return true default: return !IsConflictError(err) } } func validateWorkflowRequestsAndMode(requests []*persistence.WorkflowRequest, mode persistence.CreateWorkflowRequestMode) error { if mode != persistence.CreateWorkflowRequestModeNew { return nil } if len(requests) > 2 { return &types.InternalServiceError{Message: "too many workflow request entities generated from a single API request"} } else if len(requests) == 2 { // SignalWithStartWorkflow API can generate 2 workflow requests if (requests[0].RequestType == persistence.WorkflowRequestTypeStart && requests[1].RequestType == persistence.WorkflowRequestTypeSignal) || (requests[1].RequestType == persistence.WorkflowRequestTypeStart && requests[0].RequestType == persistence.WorkflowRequestTypeSignal) { return nil } return &types.InternalServiceError{Message: "too many workflow request entities generated from a single API request"} } return nil } ================================================ FILE: service/history/execution/context_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: context.go // // Generated by this command: // // mockgen -package execution -source context.go -destination context_mock.go -self_package github.com/uber/cadence/service/history/execution // // Package execution is a generated GoMock package. package execution import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" types "github.com/uber/cadence/common/types" events "github.com/uber/cadence/service/history/events" ) // MockContext is a mock of Context interface. type MockContext struct { ctrl *gomock.Controller recorder *MockContextMockRecorder isgomock struct{} } // MockContextMockRecorder is the mock recorder for MockContext. type MockContextMockRecorder struct { mock *MockContext } // NewMockContext creates a new mock instance. func NewMockContext(ctrl *gomock.Controller) *MockContext { mock := &MockContext{ctrl: ctrl} mock.recorder = &MockContextMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockContext) EXPECT() *MockContextMockRecorder { return m.recorder } // ByteSize mocks base method. func (m *MockContext) ByteSize() uint64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByteSize") ret0, _ := ret[0].(uint64) return ret0 } // ByteSize indicates an expected call of ByteSize. func (mr *MockContextMockRecorder) ByteSize() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByteSize", reflect.TypeOf((*MockContext)(nil).ByteSize)) } // Clear mocks base method. func (m *MockContext) Clear() { m.ctrl.T.Helper() m.ctrl.Call(m, "Clear") } // Clear indicates an expected call of Clear. func (mr *MockContextMockRecorder) Clear() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockContext)(nil).Clear)) } // ConflictResolveWorkflowExecution mocks base method. func (m *MockContext) ConflictResolveWorkflowExecution(ctx context.Context, now time.Time, conflictResolveMode persistence.ConflictResolveWorkflowMode, resetMutableState MutableState, newContext Context, newMutableState MutableState, currentContext Context, currentMutableState MutableState, currentTransactionPolicy *TransactionPolicy) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConflictResolveWorkflowExecution", ctx, now, conflictResolveMode, resetMutableState, newContext, newMutableState, currentContext, currentMutableState, currentTransactionPolicy) ret0, _ := ret[0].(error) return ret0 } // ConflictResolveWorkflowExecution indicates an expected call of ConflictResolveWorkflowExecution. func (mr *MockContextMockRecorder) ConflictResolveWorkflowExecution(ctx, now, conflictResolveMode, resetMutableState, newContext, newMutableState, currentContext, currentMutableState, currentTransactionPolicy any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConflictResolveWorkflowExecution", reflect.TypeOf((*MockContext)(nil).ConflictResolveWorkflowExecution), ctx, now, conflictResolveMode, resetMutableState, newContext, newMutableState, currentContext, currentMutableState, currentTransactionPolicy) } // CreateWorkflowExecution mocks base method. func (m *MockContext) CreateWorkflowExecution(ctx context.Context, newWorkflow *persistence.WorkflowSnapshot, persistedHistory events.PersistedBlob, createMode persistence.CreateWorkflowMode, prevRunID string, prevLastWriteVersion int64, workflowRequestMode persistence.CreateWorkflowRequestMode) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateWorkflowExecution", ctx, newWorkflow, persistedHistory, createMode, prevRunID, prevLastWriteVersion, workflowRequestMode) ret0, _ := ret[0].(error) return ret0 } // CreateWorkflowExecution indicates an expected call of CreateWorkflowExecution. func (mr *MockContextMockRecorder) CreateWorkflowExecution(ctx, newWorkflow, persistedHistory, createMode, prevRunID, prevLastWriteVersion, workflowRequestMode any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateWorkflowExecution", reflect.TypeOf((*MockContext)(nil).CreateWorkflowExecution), ctx, newWorkflow, persistedHistory, createMode, prevRunID, prevLastWriteVersion, workflowRequestMode) } // GetDomainID mocks base method. func (m *MockContext) GetDomainID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainID") ret0, _ := ret[0].(string) return ret0 } // GetDomainID indicates an expected call of GetDomainID. func (mr *MockContextMockRecorder) GetDomainID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainID", reflect.TypeOf((*MockContext)(nil).GetDomainID)) } // GetDomainName mocks base method. func (m *MockContext) GetDomainName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainName") ret0, _ := ret[0].(string) return ret0 } // GetDomainName indicates an expected call of GetDomainName. func (mr *MockContextMockRecorder) GetDomainName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainName", reflect.TypeOf((*MockContext)(nil).GetDomainName)) } // GetExecution mocks base method. func (m *MockContext) GetExecution() *types.WorkflowExecution { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetExecution") ret0, _ := ret[0].(*types.WorkflowExecution) return ret0 } // GetExecution indicates an expected call of GetExecution. func (mr *MockContextMockRecorder) GetExecution() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExecution", reflect.TypeOf((*MockContext)(nil).GetExecution)) } // GetHistorySize mocks base method. func (m *MockContext) GetHistorySize() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistorySize") ret0, _ := ret[0].(int64) return ret0 } // GetHistorySize indicates an expected call of GetHistorySize. func (mr *MockContextMockRecorder) GetHistorySize() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistorySize", reflect.TypeOf((*MockContext)(nil).GetHistorySize)) } // GetWorkflowExecution mocks base method. func (m *MockContext) GetWorkflowExecution() MutableState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowExecution") ret0, _ := ret[0].(MutableState) return ret0 } // GetWorkflowExecution indicates an expected call of GetWorkflowExecution. func (mr *MockContextMockRecorder) GetWorkflowExecution() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowExecution", reflect.TypeOf((*MockContext)(nil).GetWorkflowExecution)) } // LoadExecutionStats mocks base method. func (m *MockContext) LoadExecutionStats(ctx context.Context) (*persistence.ExecutionStats, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LoadExecutionStats", ctx) ret0, _ := ret[0].(*persistence.ExecutionStats) ret1, _ := ret[1].(error) return ret0, ret1 } // LoadExecutionStats indicates an expected call of LoadExecutionStats. func (mr *MockContextMockRecorder) LoadExecutionStats(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadExecutionStats", reflect.TypeOf((*MockContext)(nil).LoadExecutionStats), ctx) } // LoadWorkflowExecution mocks base method. func (m *MockContext) LoadWorkflowExecution(ctx context.Context) (MutableState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LoadWorkflowExecution", ctx) ret0, _ := ret[0].(MutableState) ret1, _ := ret[1].(error) return ret0, ret1 } // LoadWorkflowExecution indicates an expected call of LoadWorkflowExecution. func (mr *MockContextMockRecorder) LoadWorkflowExecution(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWorkflowExecution", reflect.TypeOf((*MockContext)(nil).LoadWorkflowExecution), ctx) } // LoadWorkflowExecutionWithTaskVersion mocks base method. func (m *MockContext) LoadWorkflowExecutionWithTaskVersion(ctx context.Context, incomingVersion int64) (MutableState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LoadWorkflowExecutionWithTaskVersion", ctx, incomingVersion) ret0, _ := ret[0].(MutableState) ret1, _ := ret[1].(error) return ret0, ret1 } // LoadWorkflowExecutionWithTaskVersion indicates an expected call of LoadWorkflowExecutionWithTaskVersion. func (mr *MockContextMockRecorder) LoadWorkflowExecutionWithTaskVersion(ctx, incomingVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWorkflowExecutionWithTaskVersion", reflect.TypeOf((*MockContext)(nil).LoadWorkflowExecutionWithTaskVersion), ctx, incomingVersion) } // Lock mocks base method. func (m *MockContext) Lock(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Lock", ctx) ret0, _ := ret[0].(error) return ret0 } // Lock indicates an expected call of Lock. func (mr *MockContextMockRecorder) Lock(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lock", reflect.TypeOf((*MockContext)(nil).Lock), ctx) } // PersistNonStartWorkflowBatchEvents mocks base method. func (m *MockContext) PersistNonStartWorkflowBatchEvents(ctx context.Context, workflowEvents *persistence.WorkflowEvents) (events.PersistedBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PersistNonStartWorkflowBatchEvents", ctx, workflowEvents) ret0, _ := ret[0].(events.PersistedBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // PersistNonStartWorkflowBatchEvents indicates an expected call of PersistNonStartWorkflowBatchEvents. func (mr *MockContextMockRecorder) PersistNonStartWorkflowBatchEvents(ctx, workflowEvents any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PersistNonStartWorkflowBatchEvents", reflect.TypeOf((*MockContext)(nil).PersistNonStartWorkflowBatchEvents), ctx, workflowEvents) } // PersistStartWorkflowBatchEvents mocks base method. func (m *MockContext) PersistStartWorkflowBatchEvents(ctx context.Context, workflowEvents *persistence.WorkflowEvents) (events.PersistedBlob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PersistStartWorkflowBatchEvents", ctx, workflowEvents) ret0, _ := ret[0].(events.PersistedBlob) ret1, _ := ret[1].(error) return ret0, ret1 } // PersistStartWorkflowBatchEvents indicates an expected call of PersistStartWorkflowBatchEvents. func (mr *MockContextMockRecorder) PersistStartWorkflowBatchEvents(ctx, workflowEvents any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PersistStartWorkflowBatchEvents", reflect.TypeOf((*MockContext)(nil).PersistStartWorkflowBatchEvents), ctx, workflowEvents) } // ReapplyEvents mocks base method. func (m *MockContext) ReapplyEvents(eventBatches []*persistence.WorkflowEvents) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReapplyEvents", eventBatches) ret0, _ := ret[0].(error) return ret0 } // ReapplyEvents indicates an expected call of ReapplyEvents. func (mr *MockContextMockRecorder) ReapplyEvents(eventBatches any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReapplyEvents", reflect.TypeOf((*MockContext)(nil).ReapplyEvents), eventBatches) } // SetHistorySize mocks base method. func (m *MockContext) SetHistorySize(size int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetHistorySize", size) } // SetHistorySize indicates an expected call of SetHistorySize. func (mr *MockContextMockRecorder) SetHistorySize(size any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHistorySize", reflect.TypeOf((*MockContext)(nil).SetHistorySize), size) } // SetWorkflowExecution mocks base method. func (m *MockContext) SetWorkflowExecution(mutableState MutableState) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetWorkflowExecution", mutableState) } // SetWorkflowExecution indicates an expected call of SetWorkflowExecution. func (mr *MockContextMockRecorder) SetWorkflowExecution(mutableState any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWorkflowExecution", reflect.TypeOf((*MockContext)(nil).SetWorkflowExecution), mutableState) } // Unlock mocks base method. func (m *MockContext) Unlock() { m.ctrl.T.Helper() m.ctrl.Call(m, "Unlock") } // Unlock indicates an expected call of Unlock. func (mr *MockContextMockRecorder) Unlock() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unlock", reflect.TypeOf((*MockContext)(nil).Unlock)) } // UpdateWorkflowExecutionAsActive mocks base method. func (m *MockContext) UpdateWorkflowExecutionAsActive(ctx context.Context, now time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecutionAsActive", ctx, now) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowExecutionAsActive indicates an expected call of UpdateWorkflowExecutionAsActive. func (mr *MockContextMockRecorder) UpdateWorkflowExecutionAsActive(ctx, now any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecutionAsActive", reflect.TypeOf((*MockContext)(nil).UpdateWorkflowExecutionAsActive), ctx, now) } // UpdateWorkflowExecutionAsPassive mocks base method. func (m *MockContext) UpdateWorkflowExecutionAsPassive(ctx context.Context, now time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecutionAsPassive", ctx, now) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowExecutionAsPassive indicates an expected call of UpdateWorkflowExecutionAsPassive. func (mr *MockContextMockRecorder) UpdateWorkflowExecutionAsPassive(ctx, now any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecutionAsPassive", reflect.TypeOf((*MockContext)(nil).UpdateWorkflowExecutionAsPassive), ctx, now) } // UpdateWorkflowExecutionTasks mocks base method. func (m *MockContext) UpdateWorkflowExecutionTasks(ctx context.Context, now time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecutionTasks", ctx, now) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowExecutionTasks indicates an expected call of UpdateWorkflowExecutionTasks. func (mr *MockContextMockRecorder) UpdateWorkflowExecutionTasks(ctx, now any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecutionTasks", reflect.TypeOf((*MockContext)(nil).UpdateWorkflowExecutionTasks), ctx, now) } // UpdateWorkflowExecutionWithNew mocks base method. func (m *MockContext) UpdateWorkflowExecutionWithNew(ctx context.Context, now time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentWorkflowTransactionPolicy TransactionPolicy, newWorkflowTransactionPolicy *TransactionPolicy, workflowRequestMode persistence.CreateWorkflowRequestMode) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecutionWithNew", ctx, now, updateMode, newContext, newMutableState, currentWorkflowTransactionPolicy, newWorkflowTransactionPolicy, workflowRequestMode) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowExecutionWithNew indicates an expected call of UpdateWorkflowExecutionWithNew. func (mr *MockContextMockRecorder) UpdateWorkflowExecutionWithNew(ctx, now, updateMode, newContext, newMutableState, currentWorkflowTransactionPolicy, newWorkflowTransactionPolicy, workflowRequestMode any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecutionWithNew", reflect.TypeOf((*MockContext)(nil).UpdateWorkflowExecutionWithNew), ctx, now, updateMode, newContext, newMutableState, currentWorkflowTransactionPolicy, newWorkflowTransactionPolicy, workflowRequestMode) } // UpdateWorkflowExecutionWithNewAsActive mocks base method. func (m *MockContext) UpdateWorkflowExecutionWithNewAsActive(ctx context.Context, now time.Time, newContext Context, newMutableState MutableState) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecutionWithNewAsActive", ctx, now, newContext, newMutableState) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowExecutionWithNewAsActive indicates an expected call of UpdateWorkflowExecutionWithNewAsActive. func (mr *MockContextMockRecorder) UpdateWorkflowExecutionWithNewAsActive(ctx, now, newContext, newMutableState any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecutionWithNewAsActive", reflect.TypeOf((*MockContext)(nil).UpdateWorkflowExecutionWithNewAsActive), ctx, now, newContext, newMutableState) } // UpdateWorkflowExecutionWithNewAsPassive mocks base method. func (m *MockContext) UpdateWorkflowExecutionWithNewAsPassive(ctx context.Context, now time.Time, newContext Context, newMutableState MutableState) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecutionWithNewAsPassive", ctx, now, newContext, newMutableState) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowExecutionWithNewAsPassive indicates an expected call of UpdateWorkflowExecutionWithNewAsPassive. func (mr *MockContextMockRecorder) UpdateWorkflowExecutionWithNewAsPassive(ctx, now, newContext, newMutableState any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecutionWithNewAsPassive", reflect.TypeOf((*MockContext)(nil).UpdateWorkflowExecutionWithNewAsPassive), ctx, now, newContext, newMutableState) } ================================================ FILE: service/history/execution/context_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/resource" "github.com/uber/cadence/service/history/shard" ) func TestIsOperationPossiblySuccessfulError(t *testing.T) { assert.False(t, isOperationPossiblySuccessfulError(nil)) assert.False(t, isOperationPossiblySuccessfulError(&types.WorkflowExecutionAlreadyStartedError{})) assert.False(t, isOperationPossiblySuccessfulError(&persistence.WorkflowExecutionAlreadyStartedError{})) assert.False(t, isOperationPossiblySuccessfulError(&persistence.CurrentWorkflowConditionFailedError{})) assert.False(t, isOperationPossiblySuccessfulError(&persistence.ConditionFailedError{})) assert.False(t, isOperationPossiblySuccessfulError(&types.ServiceBusyError{})) assert.False(t, isOperationPossiblySuccessfulError(&types.LimitExceededError{})) assert.False(t, isOperationPossiblySuccessfulError(&persistence.ShardOwnershipLostError{})) assert.True(t, isOperationPossiblySuccessfulError(&persistence.TimeoutError{})) assert.False(t, isOperationPossiblySuccessfulError(NewConflictError(t, &persistence.ConditionFailedError{}))) assert.True(t, isOperationPossiblySuccessfulError(context.DeadlineExceeded)) } func TestMergeContinueAsNewReplicationTasks(t *testing.T) { testCases := []struct { name string updateMode persistence.UpdateWorkflowMode currentWorkflowMutation *persistence.WorkflowMutation newWorkflowSnapshot *persistence.WorkflowSnapshot wantErr bool assertErr func(*testing.T, error) }{ { name: "current workflow does not continue as new", currentWorkflowMutation: &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusCompleted, }, }, wantErr: false, }, { name: "update workflow as zombie and continue as new without new zombie workflow", currentWorkflowMutation: &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusContinuedAsNew, }, }, updateMode: persistence.UpdateWorkflowModeBypassCurrent, wantErr: false, }, { name: "continue as new on the passive side", currentWorkflowMutation: &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusContinuedAsNew, }, }, updateMode: persistence.UpdateWorkflowModeUpdateCurrent, wantErr: false, }, { name: "continue as new on the active side, but new workflow is not provided", currentWorkflowMutation: &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusContinuedAsNew, }, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryReplication: { &persistence.HistoryReplicationTask{}, }, }, }, updateMode: persistence.UpdateWorkflowModeUpdateCurrent, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, &types.InternalServiceError{}, err) assert.Contains(t, err.Error(), "unable to find replication task from new workflow for continue as new replication") }, }, { name: "continue as new on the active side, but new workflow has no replication task", currentWorkflowMutation: &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusContinuedAsNew, }, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryReplication: { &persistence.HistoryReplicationTask{}, }, }, }, newWorkflowSnapshot: &persistence.WorkflowSnapshot{}, updateMode: persistence.UpdateWorkflowModeUpdateCurrent, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, &types.InternalServiceError{}, err) assert.Contains(t, err.Error(), "unable to find replication task from new workflow for continue as new replication") }, }, { name: "continue as new on the active side, but current workflow has no history replication task", currentWorkflowMutation: &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusContinuedAsNew, }, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryReplication: { &persistence.SyncActivityTask{}, }, }, }, newWorkflowSnapshot: &persistence.WorkflowSnapshot{ TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryReplication: { &persistence.HistoryReplicationTask{}, }, }, }, updateMode: persistence.UpdateWorkflowModeUpdateCurrent, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, &types.InternalServiceError{}, err) assert.Contains(t, err.Error(), "unable to find replication task from current workflow for continue as new replication") }, }, { name: "continue as new on the active side", currentWorkflowMutation: &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusContinuedAsNew, }, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryReplication: { &persistence.HistoryReplicationTask{}, }, }, }, newWorkflowSnapshot: &persistence.WorkflowSnapshot{ TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryReplication: { &persistence.HistoryReplicationTask{}, }, }, }, updateMode: persistence.UpdateWorkflowModeUpdateCurrent, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { logger := log.NewNoop() err := mergeContinueAsNewReplicationTasks(logger, tc.updateMode, tc.currentWorkflowMutation, tc.newWorkflowSnapshot) if tc.wantErr { assert.Error(t, err) if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err) } }) } } func TestNotifyTasksFromWorkflowSnapshot(t *testing.T) { testCases := []struct { name string workflowSnapShot *persistence.WorkflowSnapshot history events.PersistedBlobs persistenceError bool mockSetup func(*engine.MockEngine) }{ { name: "Success case", workflowSnapShot: &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, VersionHistories: &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte{1, 2, 3}, }, }, }, ActivityInfos: []*persistence.ActivityInfo{ { Version: 1, ScheduleID: 11, }, }, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: { &persistence.ActivityTask{ TaskList: "test-tl", }, }, persistence.HistoryTaskCategoryTimer: { &persistence.ActivityTimeoutTask{ Attempt: 10, }, }, persistence.HistoryTaskCategoryReplication: { &persistence.HistoryReplicationTask{ FirstEventID: 1, NextEventID: 10, }, }, }, }, history: events.PersistedBlobs{ events.PersistedBlob{}, }, persistenceError: true, mockSetup: func(mockEngine *engine.MockEngine) { mockEngine.EXPECT().NotifyNewTransferTasks(&hcommon.NotifyTaskInfo{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, Tasks: []persistence.Task{ &persistence.ActivityTask{ TaskList: "test-tl", }, }, PersistenceError: true, }) mockEngine.EXPECT().NotifyNewTimerTasks(&hcommon.NotifyTaskInfo{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, Tasks: []persistence.Task{ &persistence.ActivityTimeoutTask{ Attempt: 10, }, }, PersistenceError: true, }) mockEngine.EXPECT().NotifyNewReplicationTasks(&hcommon.NotifyTaskInfo{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, Tasks: []persistence.Task{ &persistence.HistoryReplicationTask{ FirstEventID: 1, NextEventID: 10, }, }, VersionHistories: &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte{1, 2, 3}, }, }, }, Activities: map[int64]*persistence.ActivityInfo{ 11: { Version: 1, ScheduleID: 11, }, }, History: events.PersistedBlobs{ events.PersistedBlob{}, }, PersistenceError: true, }) }, }, { name: "nil snapshot", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockEngine := engine.NewMockEngine(mockCtrl) if tc.mockSetup != nil { tc.mockSetup(mockEngine) } notifyTasksFromWorkflowSnapshot(mockEngine, tc.workflowSnapShot, tc.history, tc.persistenceError) }) } } func TestNotifyTasksFromWorkflowMutation(t *testing.T) { testCases := []struct { name string workflowMutation *persistence.WorkflowMutation history events.PersistedBlobs persistenceError bool mockSetup func(*engine.MockEngine) }{ { name: "Success case", workflowMutation: &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, VersionHistories: &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte{1, 2, 3}, }, }, }, UpsertActivityInfos: []*persistence.ActivityInfo{ { Version: 1, ScheduleID: 11, }, }, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: { &persistence.ActivityTask{ TaskList: "test-tl", }, }, persistence.HistoryTaskCategoryTimer: { &persistence.ActivityTimeoutTask{ Attempt: 10, }, }, persistence.HistoryTaskCategoryReplication: { &persistence.HistoryReplicationTask{ FirstEventID: 1, NextEventID: 10, }, }, }, }, history: events.PersistedBlobs{ events.PersistedBlob{}, }, persistenceError: true, mockSetup: func(mockEngine *engine.MockEngine) { mockEngine.EXPECT().NotifyNewTransferTasks(&hcommon.NotifyTaskInfo{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, Tasks: []persistence.Task{ &persistence.ActivityTask{ TaskList: "test-tl", }, }, PersistenceError: true, }) mockEngine.EXPECT().NotifyNewTimerTasks(&hcommon.NotifyTaskInfo{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, Tasks: []persistence.Task{ &persistence.ActivityTimeoutTask{ Attempt: 10, }, }, PersistenceError: true, }) mockEngine.EXPECT().NotifyNewReplicationTasks(&hcommon.NotifyTaskInfo{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, Tasks: []persistence.Task{ &persistence.HistoryReplicationTask{ FirstEventID: 1, NextEventID: 10, }, }, VersionHistories: &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte{1, 2, 3}, }, }, }, Activities: map[int64]*persistence.ActivityInfo{ 11: { Version: 1, ScheduleID: 11, }, }, History: events.PersistedBlobs{ events.PersistedBlob{}, }, PersistenceError: true, }) }, }, { name: "nil mutation", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockEngine := engine.NewMockEngine(mockCtrl) if tc.mockSetup != nil { tc.mockSetup(mockEngine) } notifyTasksFromWorkflowMutation(mockEngine, tc.workflowMutation, tc.history, tc.persistenceError) }) } } func TestActivityInfosToMap(t *testing.T) { testCases := []struct { name string activities []*persistence.ActivityInfo want map[int64]*persistence.ActivityInfo }{ { name: "non-empty", activities: []*persistence.ActivityInfo{ { Version: 1, ScheduleID: 11, }, { Version: 2, ScheduleID: 12, }, }, want: map[int64]*persistence.ActivityInfo{ 11: { Version: 1, ScheduleID: 11, }, 12: { Version: 2, ScheduleID: 12, }, }, }, { name: "empty slice", activities: []*persistence.ActivityInfo{}, want: map[int64]*persistence.ActivityInfo{}, }, { name: "nil slice", want: map[int64]*persistence.ActivityInfo{}, }, } for _, tc := range testCases { assert.Equal(t, tc.want, activityInfosToMap(tc.activities)) } } func TestCreateWorkflowExecutionWithRetry(t *testing.T) { testCases := []struct { name string request *persistence.CreateWorkflowExecutionRequest mockSetup func(*shard.MockContext) want *persistence.CreateWorkflowExecutionResponse wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", request: &persistence.CreateWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().CreateWorkflowExecution(gomock.Any(), &persistence.CreateWorkflowExecutionRequest{ RangeID: 100, }).Return(&persistence.CreateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, nil) }, want: &persistence.CreateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, wantErr: false, }, { name: "workflow already started error", request: &persistence.CreateWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().CreateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &persistence.WorkflowExecutionAlreadyStartedError{}) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, err, &persistence.WorkflowExecutionAlreadyStartedError{}) }, }, { name: "timeout error", request: &persistence.CreateWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().CreateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &persistence.TimeoutError{}) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, err, &persistence.TimeoutError{}) }, }, { name: "retry succeeds", request: &persistence.CreateWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().CreateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &types.ServiceBusyError{}) mockShard.EXPECT().CreateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.CreateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, nil) }, want: &persistence.CreateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) policy := backoff.NewExponentialRetryPolicy(time.Millisecond) policy.SetMaximumAttempts(1) if tc.mockSetup != nil { tc.mockSetup(mockShard) } resp, err := createWorkflowExecutionWithRetry(context.Background(), mockShard, testlogger.New(t), policy, tc.request) if tc.wantErr { assert.Error(t, err) if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err) assert.Equal(t, tc.want, resp) } }) } } func TestUpdateWorkflowExecutionWithRetry(t *testing.T) { testCases := []struct { name string request *persistence.UpdateWorkflowExecutionRequest mockSetup func(*shard.MockContext) want *persistence.UpdateWorkflowExecutionResponse wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", request: &persistence.UpdateWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().UpdateWorkflowExecution(gomock.Any(), &persistence.UpdateWorkflowExecutionRequest{ RangeID: 100, }).Return(&persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, nil) }, want: &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, wantErr: false, }, { name: "condition failed error", request: &persistence.UpdateWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &persistence.ConditionFailedError{}) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, err, &conflictError{}) }, }, { name: "timeout error", request: &persistence.UpdateWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &persistence.TimeoutError{}) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, err, &persistence.TimeoutError{}) }, }, { name: "retry succeeds", request: &persistence.UpdateWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &types.ServiceBusyError{}) mockShard.EXPECT().UpdateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, nil) }, want: &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) policy := backoff.NewExponentialRetryPolicy(time.Millisecond) policy.SetMaximumAttempts(1) if tc.mockSetup != nil { tc.mockSetup(mockShard) } resp, err := updateWorkflowExecutionWithRetry(context.Background(), mockShard, testlogger.New(t), policy, tc.request) if tc.wantErr { assert.Error(t, err) if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err) assert.Equal(t, tc.want, resp) } }) } } func TestAppendHistoryV2EventsWithRetry(t *testing.T) { testCases := []struct { name string domainID string execution types.WorkflowExecution request *persistence.AppendHistoryNodesRequest mockSetup func(*shard.MockContext) want *persistence.AppendHistoryNodesResponse wantErr bool }{ { name: "Success case", domainID: "test-domain-id", execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, request: &persistence.AppendHistoryNodesRequest{ IsNewBranch: true, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().AppendHistoryV2Events(gomock.Any(), &persistence.AppendHistoryNodesRequest{ IsNewBranch: true, }, "test-domain-id", types.WorkflowExecution{WorkflowID: "test-workflow-id", RunID: "test-run-id"}).Return(&persistence.AppendHistoryNodesResponse{ DataBlob: persistence.DataBlob{}, }, nil) }, want: &persistence.AppendHistoryNodesResponse{ DataBlob: persistence.DataBlob{}, }, wantErr: false, }, { name: "retry success", domainID: "test-domain-id", execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, request: &persistence.AppendHistoryNodesRequest{ IsNewBranch: true, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &types.ServiceBusyError{}) mockShard.EXPECT().AppendHistoryV2Events(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&persistence.AppendHistoryNodesResponse{ DataBlob: persistence.DataBlob{}, }, nil) }, want: &persistence.AppendHistoryNodesResponse{ DataBlob: persistence.DataBlob{}, }, wantErr: false, }, { name: "non retryable error", domainID: "test-domain-id", execution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, request: &persistence.AppendHistoryNodesRequest{ IsNewBranch: true, }, mockSetup: func(mockShard *shard.MockContext) { mockShard.EXPECT().AppendHistoryV2Events(gomock.Any(), &persistence.AppendHistoryNodesRequest{ IsNewBranch: true, }, "test-domain-id", types.WorkflowExecution{WorkflowID: "test-workflow-id", RunID: "test-run-id"}).Return(nil, errors.New("some error")) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) policy := backoff.NewExponentialRetryPolicy(time.Millisecond) policy.SetMaximumAttempts(1) if tc.mockSetup != nil { tc.mockSetup(mockShard) } resp, err := appendHistoryV2EventsWithRetry(context.Background(), mockShard, policy, tc.domainID, tc.execution, tc.request) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.want, resp) } }) } } func TestPersistStartWorkflowBatchEvents(t *testing.T) { testCases := []struct { name string workflowEvents *persistence.WorkflowEvents mockSetup func(*shard.MockContext, *cache.MockDomainCache) mockAppendHistoryNodesFn func(context.Context, string, types.WorkflowExecution, *persistence.AppendHistoryNodesRequest) (*persistence.AppendHistoryNodesResponse, error) wantErr bool want events.PersistedBlob assertErr func(*testing.T, error) }{ { name: "empty events", workflowEvents: &persistence.WorkflowEvents{}, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, err, &types.InternalServiceError{}) assert.Contains(t, err.Error(), "cannot persist first workflow events with empty events") }, }, { name: "failed to get domain name", workflowEvents: &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: 1, }, }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("", errors.New("some error")) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, err, errors.New("some error")) }, }, { name: "failed to append history nodes", workflowEvents: &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: 1, }, }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) }, mockAppendHistoryNodesFn: func(context.Context, string, types.WorkflowExecution, *persistence.AppendHistoryNodesRequest) (*persistence.AppendHistoryNodesResponse, error) { return nil, errors.New("some error") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, err, errors.New("some error")) }, }, { name: "success", workflowEvents: &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) }, mockAppendHistoryNodesFn: func(ctx context.Context, domainID string, execution types.WorkflowExecution, req *persistence.AppendHistoryNodesRequest) (*persistence.AppendHistoryNodesResponse, error) { assert.Equal(t, &persistence.AppendHistoryNodesRequest{ IsNewBranch: true, Info: "::", BranchToken: []byte{1, 2, 3}, Events: []*types.HistoryEvent{ { ID: 1, }, }, DomainName: "test-domain", }, req) return &persistence.AppendHistoryNodesResponse{ DataBlob: persistence.DataBlob{ Data: []byte("123"), }, }, nil }, want: events.PersistedBlob{ DataBlob: persistence.DataBlob{ Data: []byte("123"), }, BranchToken: []byte{1, 2, 3}, FirstEventID: 1, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockDomainCache := cache.NewMockDomainCache(mockCtrl) if tc.mockSetup != nil { tc.mockSetup(mockShard, mockDomainCache) } ctx := &contextImpl{ shard: mockShard, } if tc.mockAppendHistoryNodesFn != nil { ctx.appendHistoryNodesFn = tc.mockAppendHistoryNodesFn } got, err := ctx.PersistStartWorkflowBatchEvents(context.Background(), tc.workflowEvents) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.want, got) } }) } } func TestPersistNonStartWorkflowBatchEvents(t *testing.T) { testCases := []struct { name string workflowEvents *persistence.WorkflowEvents mockSetup func(*shard.MockContext, *cache.MockDomainCache) mockAppendHistoryNodesFn func(context.Context, string, types.WorkflowExecution, *persistence.AppendHistoryNodesRequest) (*persistence.AppendHistoryNodesResponse, error) wantErr bool want events.PersistedBlob assertErr func(*testing.T, error) }{ { name: "empty events", workflowEvents: &persistence.WorkflowEvents{}, wantErr: false, want: events.PersistedBlob{}, }, { name: "failed to get domain name", workflowEvents: &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: 1, }, }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("", errors.New("some error")) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, err, errors.New("some error")) }, }, { name: "failed to append history nodes", workflowEvents: &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: 1, }, }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) }, mockAppendHistoryNodesFn: func(context.Context, string, types.WorkflowExecution, *persistence.AppendHistoryNodesRequest) (*persistence.AppendHistoryNodesResponse, error) { return nil, errors.New("some error") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, err, errors.New("some error")) }, }, { name: "success", workflowEvents: &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) }, mockAppendHistoryNodesFn: func(ctx context.Context, domainID string, execution types.WorkflowExecution, req *persistence.AppendHistoryNodesRequest) (*persistence.AppendHistoryNodesResponse, error) { assert.Equal(t, &persistence.AppendHistoryNodesRequest{ IsNewBranch: false, BranchToken: []byte{1, 2, 3}, Events: []*types.HistoryEvent{ { ID: 1, }, }, DomainName: "test-domain", }, req) return &persistence.AppendHistoryNodesResponse{ DataBlob: persistence.DataBlob{ Data: []byte("123"), }, }, nil }, want: events.PersistedBlob{ DataBlob: persistence.DataBlob{ Data: []byte("123"), }, BranchToken: []byte{1, 2, 3}, FirstEventID: 1, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockDomainCache := cache.NewMockDomainCache(mockCtrl) if tc.mockSetup != nil { tc.mockSetup(mockShard, mockDomainCache) } ctx := &contextImpl{ shard: mockShard, } if tc.mockAppendHistoryNodesFn != nil { ctx.appendHistoryNodesFn = tc.mockAppendHistoryNodesFn } got, err := ctx.PersistNonStartWorkflowBatchEvents(context.Background(), tc.workflowEvents) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.want, got) } }) } } func TestCreateWorkflowExecution(t *testing.T) { testCases := []struct { name string newWorkflow *persistence.WorkflowSnapshot history events.PersistedBlob createMode persistence.CreateWorkflowMode prevRunID string prevLastWriteVersion int64 createWorkflowRequestMode persistence.CreateWorkflowRequestMode mockCreateWorkflowExecutionFn func(context.Context, *persistence.CreateWorkflowExecutionRequest) (*persistence.CreateWorkflowExecutionResponse, error) mockNotifyTasksFromWorkflowSnapshotFn func(*persistence.WorkflowSnapshot, events.PersistedBlobs, bool) mockEmitSessionUpdateStatsFn func(string, *persistence.MutableStateUpdateSessionStats) wantErr bool }{ { name: "failed to create workflow execution with possibly success error", newWorkflow: &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, history: events.PersistedBlob{ DataBlob: persistence.DataBlob{ Data: []byte("123"), }, BranchToken: []byte{1, 2, 3}, FirstEventID: 1, }, createMode: persistence.CreateWorkflowModeContinueAsNew, prevRunID: "test-prev-run-id", prevLastWriteVersion: 123, createWorkflowRequestMode: persistence.CreateWorkflowRequestModeReplicated, mockCreateWorkflowExecutionFn: func(context.Context, *persistence.CreateWorkflowExecutionRequest) (*persistence.CreateWorkflowExecutionResponse, error) { return nil, &types.InternalServiceError{} }, mockNotifyTasksFromWorkflowSnapshotFn: func(_ *persistence.WorkflowSnapshot, _ events.PersistedBlobs, persistenceError bool) { assert.Equal(t, true, persistenceError) }, wantErr: true, }, { name: "failed to validate workflow requests", newWorkflow: &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, WorkflowRequests: []*persistence.WorkflowRequest{{}, {}}, }, history: events.PersistedBlob{ DataBlob: persistence.DataBlob{ Data: []byte("123"), }, BranchToken: []byte{1, 2, 3}, FirstEventID: 1, }, createMode: persistence.CreateWorkflowModeContinueAsNew, prevRunID: "test-prev-run-id", prevLastWriteVersion: 123, createWorkflowRequestMode: persistence.CreateWorkflowRequestModeNew, wantErr: true, }, { name: "success", newWorkflow: &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, history: events.PersistedBlob{ DataBlob: persistence.DataBlob{ Data: []byte("123"), }, BranchToken: []byte{1, 2, 3}, FirstEventID: 1, }, createMode: persistence.CreateWorkflowModeContinueAsNew, prevRunID: "test-prev-run-id", prevLastWriteVersion: 123, createWorkflowRequestMode: persistence.CreateWorkflowRequestModeReplicated, mockCreateWorkflowExecutionFn: func(ctx context.Context, req *persistence.CreateWorkflowExecutionRequest) (*persistence.CreateWorkflowExecutionResponse, error) { assert.Equal(t, &persistence.CreateWorkflowExecutionRequest{ Mode: persistence.CreateWorkflowModeContinueAsNew, PreviousRunID: "test-prev-run-id", PreviousLastWriteVersion: 123, NewWorkflowSnapshot: persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 3, }, }, WorkflowRequestMode: persistence.CreateWorkflowRequestModeReplicated, DomainName: "test-domain", }, req) return &persistence.CreateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, nil }, mockNotifyTasksFromWorkflowSnapshotFn: func(newWorkflow *persistence.WorkflowSnapshot, history events.PersistedBlobs, persistenceError bool) { assert.Equal(t, &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, newWorkflow) assert.Equal(t, events.PersistedBlobs{ { DataBlob: persistence.DataBlob{ Data: []byte("123"), }, BranchToken: []byte{1, 2, 3}, FirstEventID: 1, }, }, history) assert.Equal(t, false, persistenceError) }, mockEmitSessionUpdateStatsFn: func(domainName string, stats *persistence.MutableStateUpdateSessionStats) { assert.Equal(t, "test-domain", domainName) assert.Equal(t, &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, stats) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockShard.EXPECT().GetConfig().Return(&config.Config{ EnableStrongIdempotencySanityCheck: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), }).AnyTimes() mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) ctx := &contextImpl{ logger: testlogger.New(t), shard: mockShard, stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), } if tc.mockCreateWorkflowExecutionFn != nil { ctx.createWorkflowExecutionFn = tc.mockCreateWorkflowExecutionFn } if tc.mockNotifyTasksFromWorkflowSnapshotFn != nil { ctx.notifyTasksFromWorkflowSnapshotFn = tc.mockNotifyTasksFromWorkflowSnapshotFn } if tc.mockEmitSessionUpdateStatsFn != nil { ctx.emitSessionUpdateStatsFn = tc.mockEmitSessionUpdateStatsFn } err := ctx.CreateWorkflowExecution(context.Background(), tc.newWorkflow, tc.history, tc.createMode, tc.prevRunID, tc.prevLastWriteVersion, tc.createWorkflowRequestMode) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestUpdateWorkflowExecutionTasks(t *testing.T) { testCases := []struct { name string mockSetup func(*shard.MockContext, *cache.MockDomainCache, *MockMutableState) mockUpdateWorkflowExecutionFn func(context.Context, *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) mockNotifyTasksFromWorkflowMutationFn func(*persistence.WorkflowMutation, events.PersistedBlobs, bool) mockEmitSessionUpdateStatsFn func(string, *persistence.MutableStateUpdateSessionStats) wantErr bool assertErr func(*testing.T, error) }{ { name: "CloseTransactionAsMutation failed", mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState) { mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(nil, nil, errors.New("some error")) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "found unexpected new events", mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState) { mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{{}}, nil) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, &types.InternalServiceError{}, err) }, }, { name: "found unexpected workflow requests", mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState) { mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{ WorkflowRequests: []*persistence.WorkflowRequest{{}}, }, []*persistence.WorkflowEvents{}, nil) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockShard.EXPECT().GetConfig().Return(&config.Config{ EnableStrongIdempotencySanityCheck: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), }) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &types.InternalServiceError{Message: "UpdateWorkflowExecutionTask can only be used for persisting new workflow tasks, but found new workflow requests"}, err) }, }, { name: "domain cache error", mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState) { mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{}, nil) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("", errors.New("some error")) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "update workflow failed with possibly success error", mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState) { mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{}, nil) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) }, mockUpdateWorkflowExecutionFn: func(_ context.Context, request *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) { return nil, &types.InternalServiceError{} }, mockNotifyTasksFromWorkflowMutationFn: func(_ *persistence.WorkflowMutation, _ events.PersistedBlobs, persistenceError bool) { assert.Equal(t, true, persistenceError, "case: update workflow failed with possibly success error") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, &types.InternalServiceError{}, err) }, }, { name: "success", mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState) { mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, []*persistence.WorkflowEvents{}, nil) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) }, mockUpdateWorkflowExecutionFn: func(_ context.Context, request *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) { assert.Equal(t, &persistence.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, ExecutionStats: &persistence.ExecutionStats{}, }, Mode: persistence.UpdateWorkflowModeIgnoreCurrent, DomainName: "test-domain", }, request, "case: success") return &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, nil }, mockNotifyTasksFromWorkflowMutationFn: func(mutation *persistence.WorkflowMutation, history events.PersistedBlobs, persistenceError bool) { assert.Equal(t, &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, ExecutionStats: &persistence.ExecutionStats{}, }, mutation, "case: success") assert.Nil(t, history, "case: success") assert.Equal(t, false, persistenceError, "case: success") }, mockEmitSessionUpdateStatsFn: func(domainName string, stats *persistence.MutableStateUpdateSessionStats) { assert.Equal(t, "test-domain", domainName, "case: success") assert.Equal(t, &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, stats, "case: success") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockMutableState := NewMockMutableState(mockCtrl) if tc.mockSetup != nil { tc.mockSetup(mockShard, mockDomainCache, mockMutableState) } ctx := &contextImpl{ logger: testlogger.New(t), shard: mockShard, mutableState: mockMutableState, stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), } if tc.mockUpdateWorkflowExecutionFn != nil { ctx.updateWorkflowExecutionFn = tc.mockUpdateWorkflowExecutionFn } if tc.mockNotifyTasksFromWorkflowMutationFn != nil { ctx.notifyTasksFromWorkflowMutationFn = tc.mockNotifyTasksFromWorkflowMutationFn } if tc.mockEmitSessionUpdateStatsFn != nil { ctx.emitSessionUpdateStatsFn = tc.mockEmitSessionUpdateStatsFn } err := ctx.UpdateWorkflowExecutionTasks(context.Background(), time.Unix(0, 0)) if tc.wantErr { assert.Error(t, err) if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err) } }) } } func TestUpdateWorkflowExecutionWithNew(t *testing.T) { testCases := []struct { name string updateMode persistence.UpdateWorkflowMode newContext Context currentWorkflowTransactionPolicy TransactionPolicy newWorkflowTransactionPolicy *TransactionPolicy workflowRequestMode persistence.CreateWorkflowRequestMode mockSetup func(*shard.MockContext, *cache.MockDomainCache, *MockMutableState, *MockMutableState, *engine.MockEngine) mockPersistNonStartWorkflowBatchEventsFn func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) mockPersistStartWorkflowBatchEventsFn func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) mockUpdateWorkflowExecutionFn func(context.Context, *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) mockNotifyTasksFromWorkflowMutationFn func(*persistence.WorkflowMutation, events.PersistedBlobs, bool) mockNotifyTasksFromWorkflowSnapshotFn func(*persistence.WorkflowSnapshot, events.PersistedBlobs, bool) mockEmitSessionUpdateStatsFn func(string, *persistence.MutableStateUpdateSessionStats) mockEmitWorkflowHistoryStatsFn func(string, int, int) mockEmitLargeWorkflowShardIDStatsFn func(int64, int64, int64, int64) mockEmitWorkflowCompletionStatsFn func(string, string, string, string, string, *types.HistoryEvent) mockMergeContinueAsNewReplicationTasksFn func(persistence.UpdateWorkflowMode, *persistence.WorkflowMutation, *persistence.WorkflowSnapshot) error mockUpdateWorkflowExecutionEventReapplyFn func(persistence.UpdateWorkflowMode, []*persistence.WorkflowEvents, []*persistence.WorkflowEvents) error wantErr bool assertErr func(*testing.T, error) }{ { name: "CloseTransactionAsMutation failed", currentWorkflowTransactionPolicy: TransactionPolicyPassive, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(nil, nil, errors.New("some error")) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "PersistNonStartWorkflowBatchEvents failed", currentWorkflowTransactionPolicy: TransactionPolicyPassive, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(11)) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, errors.New("some error") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "CloseTransactionAsSnapshot failed", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive, newWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(11)) mockMutableState.EXPECT().SetHistorySize(gomock.Any()) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(nil, nil, errors.New("some error")) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "both current workflow and new workflow generate workflow requests", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive, newWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockShard.EXPECT().GetConfig().Return(&config.Config{ EnableStrongIdempotencySanityCheck: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), }) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{ WorkflowRequests: []*persistence.WorkflowRequest{{}}, }, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(11)) mockMutableState.EXPECT().SetHistorySize(gomock.Any()) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{ WorkflowRequests: []*persistence.WorkflowRequest{{}}, }, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &types.InternalServiceError{Message: "workflow requests are only expected to be generated from one workflow for UpdateWorkflowExecution"}, err) }, }, { name: "mergeContinueAsNewReplicationTasks failed", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive, newWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(11)) mockMutableState.EXPECT().SetHistorySize(gomock.Any()) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockPersistStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockMergeContinueAsNewReplicationTasksFn: func(persistence.UpdateWorkflowMode, *persistence.WorkflowMutation, *persistence.WorkflowSnapshot) error { return errors.New("some error") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "updateWorkflowExecutionEventReapply failed", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive, newWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(11)) mockMutableState.EXPECT().SetHistorySize(gomock.Any()) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockPersistStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockMergeContinueAsNewReplicationTasksFn: func(persistence.UpdateWorkflowMode, *persistence.WorkflowMutation, *persistence.WorkflowSnapshot) error { return nil }, mockUpdateWorkflowExecutionEventReapplyFn: func(persistence.UpdateWorkflowMode, []*persistence.WorkflowEvents, []*persistence.WorkflowEvents) error { return errors.New("some error") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "updateWorkflowExecution failed", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive, newWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockMutableState.EXPECT().GetNextEventID().Return(int64(11)) mockMutableState.EXPECT().SetHistorySize(gomock.Any()) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockPersistStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockMergeContinueAsNewReplicationTasksFn: func(persistence.UpdateWorkflowMode, *persistence.WorkflowMutation, *persistence.WorkflowSnapshot) error { return nil }, mockUpdateWorkflowExecutionEventReapplyFn: func(persistence.UpdateWorkflowMode, []*persistence.WorkflowEvents, []*persistence.WorkflowEvents) error { return nil }, mockUpdateWorkflowExecutionFn: func(context.Context, *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) { return nil, errors.New("some error") }, mockNotifyTasksFromWorkflowMutationFn: func(_ *persistence.WorkflowMutation, _ events.PersistedBlobs, persistenceError bool) { assert.Equal(t, true, persistenceError, "case: updateWorkflowExecution failed") }, mockNotifyTasksFromWorkflowSnapshotFn: func(_ *persistence.WorkflowSnapshot, _ events.PersistedBlobs, persistenceError bool) { assert.Equal(t, true, persistenceError, "case: updateWorkflowExecution failed") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "success", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, updateMode: persistence.UpdateWorkflowModeUpdateCurrent, currentWorkflowTransactionPolicy: TransactionPolicyActive, newWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), workflowRequestMode: persistence.CreateWorkflowRequestModeReplicated, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), TransactionPolicyActive).Return(&persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", State: persistence.WorkflowStateCompleted, }, }, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 2, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockMutableState.EXPECT().GetVersionHistories().Return(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("branchtoken"), Items: []*persistence.VersionHistoryItem{ { EventID: 1, Version: 1, }, }, }, }, }) mockMutableState.EXPECT().GetNextEventID().Return(int64(11)) mockMutableState.EXPECT().SetHistorySize(int64(5)) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), TransactionPolicyActive).Return(&persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id2", }, }, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(persistence.WorkflowStateCompleted, persistence.WorkflowCloseStatusCompleted) mockShard.EXPECT().GetEngine().Return(mockEngine) mockEngine.EXPECT().NotifyNewHistoryEvent(gomock.Any()) mockMutableState.EXPECT().GetLastFirstEventID().Return(int64(1)) mockMutableState.EXPECT().GetNextEventID().Return(int64(10)) mockMutableState.EXPECT().GetPreviousStartedEventID().Return(int64(12)) mockMutableState.EXPECT().GetNextEventID().Return(int64(20)) mockMutableState.EXPECT().GetCompletionEvent(gomock.Any()).Return(&types.HistoryEvent{ ID: 123, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(_ context.Context, history *persistence.WorkflowEvents) (events.PersistedBlob, error) { assert.Equal(t, &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: 2, }, }, BranchToken: []byte{1, 2, 3}, }, history, "case: success") return events.PersistedBlob{ DataBlob: persistence.DataBlob{ Data: []byte{1, 2, 3, 4, 5}, }, }, nil }, mockPersistStartWorkflowBatchEventsFn: func(_ context.Context, history *persistence.WorkflowEvents) (events.PersistedBlob, error) { assert.Equal(t, &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, history, "case: success") return events.PersistedBlob{ DataBlob: persistence.DataBlob{ Data: []byte{4, 5}, }, }, nil }, mockMergeContinueAsNewReplicationTasksFn: func(updateMode persistence.UpdateWorkflowMode, currentWorkflow *persistence.WorkflowMutation, newWorkflow *persistence.WorkflowSnapshot) error { assert.Equal(t, persistence.UpdateWorkflowModeUpdateCurrent, updateMode) assert.Equal(t, &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", State: persistence.WorkflowStateCompleted, }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 5, }, }, currentWorkflow, "case: success") assert.Equal(t, &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id2", }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 2, }, }, newWorkflow, "case: success") return nil }, mockUpdateWorkflowExecutionEventReapplyFn: func(updateMode persistence.UpdateWorkflowMode, currentEvents []*persistence.WorkflowEvents, newEvents []*persistence.WorkflowEvents) error { assert.Equal(t, persistence.UpdateWorkflowModeUpdateCurrent, updateMode) assert.Equal(t, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 2, }, }, BranchToken: []byte{1, 2, 3}, }, }, currentEvents, "case: success") assert.Equal(t, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, newEvents, "case: success") return nil }, mockUpdateWorkflowExecutionFn: func(_ context.Context, req *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) { assert.Equal(t, &persistence.UpdateWorkflowExecutionRequest{ Mode: persistence.UpdateWorkflowModeUpdateCurrent, UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", State: persistence.WorkflowStateCompleted, }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 5, }, }, NewWorkflowSnapshot: &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id2", }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 2, }, }, WorkflowRequestMode: persistence.CreateWorkflowRequestModeReplicated, DomainName: "test-domain", }, req, "case: success") return &persistence.UpdateWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, nil }, mockNotifyTasksFromWorkflowMutationFn: func(currentWorkflow *persistence.WorkflowMutation, currentEvents events.PersistedBlobs, persistenceError bool) { assert.Equal(t, &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", State: persistence.WorkflowStateCompleted, }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 5, }, }, currentWorkflow, "case: success") assert.Equal(t, events.PersistedBlobs{ { DataBlob: persistence.DataBlob{ Data: []byte{1, 2, 3, 4, 5}, }, }, { DataBlob: persistence.DataBlob{ Data: []byte{4, 5}, }, }, }, currentEvents, "case: success") assert.Equal(t, false, persistenceError) }, mockNotifyTasksFromWorkflowSnapshotFn: func(newWorkflow *persistence.WorkflowSnapshot, newEvents events.PersistedBlobs, persistenceError bool) { assert.Equal(t, &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id2", }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 2, }, }, newWorkflow, "case: success") assert.Equal(t, events.PersistedBlobs{ { DataBlob: persistence.DataBlob{ Data: []byte{1, 2, 3, 4, 5}, }, }, { DataBlob: persistence.DataBlob{ Data: []byte{4, 5}, }, }, }, newEvents, "case: success") assert.Equal(t, false, persistenceError, "case: success") }, mockEmitWorkflowHistoryStatsFn: func(domainName string, size int, count int) { assert.Equal(t, 5, size, "case: success") assert.Equal(t, 19, count, "case: success") }, mockEmitSessionUpdateStatsFn: func(domainName string, stats *persistence.MutableStateUpdateSessionStats) { assert.Equal(t, &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, stats, "case: success") }, mockEmitLargeWorkflowShardIDStatsFn: func(blobSize int64, oldHistoryCount int64, oldHistorySize int64, newHistoryCount int64) { assert.Equal(t, int64(5), blobSize, "case: success") assert.Equal(t, int64(10), oldHistoryCount, "case: success") assert.Equal(t, int64(0), oldHistorySize, "case: success") assert.Equal(t, int64(11), newHistoryCount, "case: success") }, mockEmitWorkflowCompletionStatsFn: func(domainName string, workflowType string, workflowID string, runID string, taskList string, lastEvent *types.HistoryEvent) { assert.Equal(t, &types.HistoryEvent{ ID: 123, }, lastEvent, "case: success") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockMutableState := NewMockMutableState(mockCtrl) mockNewMutableState := NewMockMutableState(mockCtrl) mockEngine := engine.NewMockEngine(mockCtrl) if tc.mockSetup != nil { tc.mockSetup(mockShard, mockDomainCache, mockMutableState, mockNewMutableState, mockEngine) } ctx := &contextImpl{ logger: testlogger.New(t), shard: mockShard, mutableState: mockMutableState, stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), persistNonStartWorkflowBatchEventsFn: tc.mockPersistNonStartWorkflowBatchEventsFn, persistStartWorkflowBatchEventsFn: tc.mockPersistStartWorkflowBatchEventsFn, updateWorkflowExecutionFn: tc.mockUpdateWorkflowExecutionFn, notifyTasksFromWorkflowMutationFn: tc.mockNotifyTasksFromWorkflowMutationFn, notifyTasksFromWorkflowSnapshotFn: tc.mockNotifyTasksFromWorkflowSnapshotFn, emitSessionUpdateStatsFn: tc.mockEmitSessionUpdateStatsFn, emitWorkflowHistoryStatsFn: tc.mockEmitWorkflowHistoryStatsFn, mergeContinueAsNewReplicationTasksFn: tc.mockMergeContinueAsNewReplicationTasksFn, updateWorkflowExecutionEventReapplyFn: tc.mockUpdateWorkflowExecutionEventReapplyFn, emitLargeWorkflowShardIDStatsFn: tc.mockEmitLargeWorkflowShardIDStatsFn, emitWorkflowCompletionStatsFn: tc.mockEmitWorkflowCompletionStatsFn, } err := ctx.UpdateWorkflowExecutionWithNew(context.Background(), time.Unix(0, 0), tc.updateMode, tc.newContext, mockNewMutableState, tc.currentWorkflowTransactionPolicy, tc.newWorkflowTransactionPolicy, tc.workflowRequestMode) if tc.wantErr { assert.Error(t, err) if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err) } }) } } func TestConflictResolveWorkflowExecution(t *testing.T) { testCases := []struct { name string conflictResolveMode persistence.ConflictResolveWorkflowMode newContext Context currentContext Context currentWorkflowTransactionPolicy *TransactionPolicy mockSetup func(*shard.MockContext, *cache.MockDomainCache, *MockMutableState, *MockMutableState, *MockMutableState, *engine.MockEngine) mockPersistNonStartWorkflowBatchEventsFn func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) mockPersistStartWorkflowBatchEventsFn func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) mockUpdateWorkflowExecutionFn func(context.Context, *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) mockNotifyTasksFromWorkflowMutationFn func(*persistence.WorkflowMutation, events.PersistedBlobs, bool) mockNotifyTasksFromWorkflowSnapshotFn func(*persistence.WorkflowSnapshot, events.PersistedBlobs, bool) mockEmitSessionUpdateStatsFn func(string, *persistence.MutableStateUpdateSessionStats) mockEmitWorkflowHistoryStatsFn func(string, int, int) mockEmitLargeWorkflowShardIDStatsFn func(int64, int64, int64, int64) mockEmitWorkflowCompletionStatsFn func(string, string, string, string, string, *types.HistoryEvent) mockMergeContinueAsNewReplicationTasksFn func(persistence.UpdateWorkflowMode, *persistence.WorkflowMutation, *persistence.WorkflowSnapshot) error mockConflictResolveWorkflowExecutionEventReapplyFn func(persistence.ConflictResolveWorkflowMode, []*persistence.WorkflowEvents, []*persistence.WorkflowEvents) error wantErr bool assertErr func(*testing.T, error) }{ { name: "resetMutableState CloseTransactionAsSnapshot failed", mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(nil, nil, errors.New("some error")) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "persistNonStartWorkflowEvents failed", mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, errors.New("some error") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "newMutableState CloseTransactionAsSnapshot failed", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(nil, nil, errors.New("some error")) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "both reset workflow and new workflow generate workflow requests", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockShard.EXPECT().GetConfig().Return(&config.Config{ EnableStrongIdempotencySanityCheck: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), }) mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{ WorkflowRequests: []*persistence.WorkflowRequest{ { RequestType: persistence.WorkflowRequestTypeStart, RequestID: "test", Version: 1, }, }, }, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{ WorkflowRequests: []*persistence.WorkflowRequest{ { RequestType: persistence.WorkflowRequestTypeStart, RequestID: "test", Version: 1, }, }, }, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &types.InternalServiceError{Message: "workflow requests are only expected to be generated from either reset workflow or continue-as-new workflow for ConflictResolveWorkflowExecution"}, err) }, }, { name: "persistStartWorkflowEvents failed", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockPersistStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, errors.New("some error") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "currentMutableState CloseTransactionAsMutation failed", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(nil, nil, errors.New("some error")) }, mockPersistNonStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockPersistStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "currentMutableState generates workflow requests", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockShard.EXPECT().GetConfig().Return(&config.Config{ EnableStrongIdempotencySanityCheck: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), }) mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{ WorkflowRequests: []*persistence.WorkflowRequest{ { RequestType: persistence.WorkflowRequestTypeStart, RequestID: "test", Version: 1, }, }, }, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 2, }, }, BranchToken: []byte{5, 6}, }, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(_ context.Context, history *persistence.WorkflowEvents) (events.PersistedBlob, error) { if history.BranchToken[0] == 1 { return events.PersistedBlob{}, nil } return events.PersistedBlob{}, nil }, mockPersistStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, &types.InternalServiceError{Message: "workflow requests are not expected from current workflow for ConflictResolveWorkflowExecution"}, err) }, }, { name: "currentMutableState persistNonStartWorkflowEvents failed", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 2, }, }, BranchToken: []byte{5, 6}, }, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(_ context.Context, history *persistence.WorkflowEvents) (events.PersistedBlob, error) { if history.BranchToken[0] == 1 { return events.PersistedBlob{}, nil } return events.PersistedBlob{}, errors.New("some error") }, mockPersistStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "conflictResolveEventReapply failed", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 2, }, }, BranchToken: []byte{5, 6}, }, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(_ context.Context, history *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockPersistStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockConflictResolveWorkflowExecutionEventReapplyFn: func(persistence.ConflictResolveWorkflowMode, []*persistence.WorkflowEvents, []*persistence.WorkflowEvents) error { return errors.New("some error") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "ConflictResolveWorkflowExecution failed", newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{}, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 2, }, }, BranchToken: []byte{5, 6}, }, }, nil) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockShard.EXPECT().ConflictResolveWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, errors.New("some error")) }, mockPersistNonStartWorkflowBatchEventsFn: func(_ context.Context, history *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockPersistStartWorkflowBatchEventsFn: func(context.Context, *persistence.WorkflowEvents) (events.PersistedBlob, error) { return events.PersistedBlob{}, nil }, mockConflictResolveWorkflowExecutionEventReapplyFn: func(persistence.ConflictResolveWorkflowMode, []*persistence.WorkflowEvents, []*persistence.WorkflowEvents) error { return nil }, mockNotifyTasksFromWorkflowMutationFn: func(currentWorkflow *persistence.WorkflowMutation, currentEvents events.PersistedBlobs, persistenceError bool) { assert.Equal(t, true, persistenceError, "case: ConflictResolveWorkflowExecution failed") }, mockNotifyTasksFromWorkflowSnapshotFn: func(newWorkflow *persistence.WorkflowSnapshot, newEvents events.PersistedBlobs, persistenceError bool) { assert.Equal(t, true, persistenceError, "case: ConflictResolveWorkflowExecution failed") }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "ConflictResolveWorkflowExecution success", conflictResolveMode: persistence.ConflictResolveWorkflowModeUpdateCurrent, newContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentContext: &contextImpl{ stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), }, currentWorkflowTransactionPolicy: TransactionPolicyActive.Ptr(), mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResetMutableState *MockMutableState, mockNewMutableState *MockMutableState, mockMutableState *MockMutableState, mockEngine *engine.MockEngine) { mockResetMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", State: persistence.WorkflowStateCompleted, }, }, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, nil) mockNewMutableState.EXPECT().CloseTransactionAsSnapshot(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id2", }, }, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, nil) mockMutableState.EXPECT().CloseTransactionAsMutation(gomock.Any(), gomock.Any()).Return(&persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id0", }, }, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 2, }, }, BranchToken: []byte{5, 6}, }, }, nil) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) mockShard.EXPECT().ConflictResolveWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.ConflictResolveWorkflowExecutionResponse{ MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, }, nil) mockResetMutableState.EXPECT().GetVersionHistories().Return(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("123"), Items: []*persistence.VersionHistoryItem{ { EventID: 1, Version: 1, }, }, }, }, }) mockResetMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(persistence.WorkflowStateCompleted, persistence.WorkflowCloseStatusCompleted) mockShard.EXPECT().GetEngine().Return(mockEngine) mockEngine.EXPECT().NotifyNewHistoryEvent(gomock.Any()) mockResetMutableState.EXPECT().GetLastFirstEventID().Return(int64(123)) mockResetMutableState.EXPECT().GetNextEventID().Return(int64(456)) mockResetMutableState.EXPECT().GetPreviousStartedEventID().Return(int64(789)) mockResetMutableState.EXPECT().GetNextEventID().Return(int64(1111)) mockResetMutableState.EXPECT().GetCompletionEvent(gomock.Any()).Return(&types.HistoryEvent{ ID: 123, }, nil) }, mockPersistNonStartWorkflowBatchEventsFn: func(_ context.Context, history *persistence.WorkflowEvents) (events.PersistedBlob, error) { if history.BranchToken[0] == 1 { assert.Equal(t, &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, history, "case: success") return events.PersistedBlob{ DataBlob: persistence.DataBlob{ Data: []byte{1, 2, 3, 4, 5}, }, }, nil } assert.Equal(t, &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: 2, }, }, BranchToken: []byte{5, 6}, }, history, "case: success") return events.PersistedBlob{ DataBlob: persistence.DataBlob{ Data: []byte{1, 2}, }, }, nil }, mockPersistStartWorkflowBatchEventsFn: func(_ context.Context, history *persistence.WorkflowEvents) (events.PersistedBlob, error) { assert.Equal(t, &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, history, "case: success") return events.PersistedBlob{ DataBlob: persistence.DataBlob{ Data: []byte{3, 2}, }, }, nil }, mockConflictResolveWorkflowExecutionEventReapplyFn: func(mode persistence.ConflictResolveWorkflowMode, resetEvents []*persistence.WorkflowEvents, newEvents []*persistence.WorkflowEvents) error { assert.Equal(t, persistence.ConflictResolveWorkflowModeUpdateCurrent, mode, "case: success") assert.Equal(t, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: 1, }, }, BranchToken: []byte{1, 2, 3}, }, }, resetEvents, "case: success") assert.Equal(t, []*persistence.WorkflowEvents{ { Events: []*types.HistoryEvent{ { ID: constants.FirstEventID, }, }, BranchToken: []byte{4}, }, }, newEvents, "case: success") return nil }, mockNotifyTasksFromWorkflowMutationFn: func(currentWorkflow *persistence.WorkflowMutation, currentEvents events.PersistedBlobs, persistenceError bool) { assert.Equal(t, &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id0", }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 2, }, }, currentWorkflow, "case: success") assert.Equal(t, events.PersistedBlobs{ { DataBlob: persistence.DataBlob{ Data: []byte{1, 2, 3, 4, 5}, }, }, { DataBlob: persistence.DataBlob{ Data: []byte{3, 2}, }, }, { DataBlob: persistence.DataBlob{ Data: []byte{1, 2}, }, }, }, currentEvents, "case: success") assert.Equal(t, false, persistenceError, "case: success") }, mockNotifyTasksFromWorkflowSnapshotFn: func(newWorkflow *persistence.WorkflowSnapshot, newEvents events.PersistedBlobs, persistenceError bool) { if newWorkflow.ExecutionInfo.RunID == "test-run-id" { assert.Equal(t, &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", State: persistence.WorkflowStateCompleted, }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 5, }, }, newWorkflow, "case: success") } else { assert.Equal(t, &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id2", }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 2, }, }, newWorkflow, "case: success") } assert.Equal(t, events.PersistedBlobs{ { DataBlob: persistence.DataBlob{ Data: []byte{1, 2, 3, 4, 5}, }, }, { DataBlob: persistence.DataBlob{ Data: []byte{3, 2}, }, }, { DataBlob: persistence.DataBlob{ Data: []byte{1, 2}, }, }, }, newEvents, "case: success") assert.Equal(t, false, persistenceError, "case: success") }, mockEmitWorkflowHistoryStatsFn: func(domainName string, size int, count int) { assert.Equal(t, 5, size, "case: success") assert.Equal(t, 1110, count, "case: success") }, mockEmitSessionUpdateStatsFn: func(domainName string, stats *persistence.MutableStateUpdateSessionStats) { assert.Equal(t, &persistence.MutableStateUpdateSessionStats{ MutableStateSize: 123, }, stats, "case: success") }, mockEmitWorkflowCompletionStatsFn: func(domainName string, workflowType string, workflowID string, runID string, taskList string, lastEvent *types.HistoryEvent) { assert.Equal(t, &types.HistoryEvent{ ID: 123, }, lastEvent, "case: success") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockResetMutableState := NewMockMutableState(mockCtrl) mockMutableState := NewMockMutableState(mockCtrl) mockNewMutableState := NewMockMutableState(mockCtrl) mockEngine := engine.NewMockEngine(mockCtrl) if tc.mockSetup != nil { tc.mockSetup(mockShard, mockDomainCache, mockResetMutableState, mockNewMutableState, mockMutableState, mockEngine) } ctx := &contextImpl{ logger: testlogger.New(t), shard: mockShard, stats: &persistence.ExecutionStats{}, metricsClient: metrics.NewNoopMetricsClient(), persistNonStartWorkflowBatchEventsFn: tc.mockPersistNonStartWorkflowBatchEventsFn, persistStartWorkflowBatchEventsFn: tc.mockPersistStartWorkflowBatchEventsFn, updateWorkflowExecutionFn: tc.mockUpdateWorkflowExecutionFn, notifyTasksFromWorkflowMutationFn: tc.mockNotifyTasksFromWorkflowMutationFn, notifyTasksFromWorkflowSnapshotFn: tc.mockNotifyTasksFromWorkflowSnapshotFn, emitSessionUpdateStatsFn: tc.mockEmitSessionUpdateStatsFn, emitWorkflowHistoryStatsFn: tc.mockEmitWorkflowHistoryStatsFn, mergeContinueAsNewReplicationTasksFn: tc.mockMergeContinueAsNewReplicationTasksFn, conflictResolveEventReapplyFn: tc.mockConflictResolveWorkflowExecutionEventReapplyFn, emitLargeWorkflowShardIDStatsFn: tc.mockEmitLargeWorkflowShardIDStatsFn, emitWorkflowCompletionStatsFn: tc.mockEmitWorkflowCompletionStatsFn, } err := ctx.ConflictResolveWorkflowExecution(context.Background(), time.Unix(0, 0), tc.conflictResolveMode, mockResetMutableState, tc.newContext, mockNewMutableState, tc.currentContext, mockMutableState, tc.currentWorkflowTransactionPolicy) if tc.wantErr { assert.Error(t, err) if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err) } }) } } func TestReapplyEvents(t *testing.T) { testCases := []struct { name string eventBatches []*persistence.WorkflowEvents mockSetup func(*shard.MockContext, *cache.MockDomainCache, *resource.Test, *engine.MockEngine, *activecluster.MockManager) wantErr bool }{ { name: "empty input", eventBatches: []*persistence.WorkflowEvents{}, wantErr: false, }, { name: "domain cache error", eventBatches: []*persistence.WorkflowEvents{ { DomainID: "test-domain-id", }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, _ *resource.Test, _ *engine.MockEngine, _ *activecluster.MockManager) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID("test-domain-id").Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "domain is pending active", eventBatches: []*persistence.WorkflowEvents{ { DomainID: "test-domain-id", }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, _ *resource.Test, _ *engine.MockEngine, _ *activecluster.MockManager) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID("test-domain-id").Return(cache.NewDomainCacheEntryForTest(nil, nil, true, nil, 0, common.Ptr(int64(1)), 0, 0, 0), nil) }, wantErr: false, }, { name: "domainID/workflowID mismatch", eventBatches: []*persistence.WorkflowEvents{ { DomainID: "test-domain-id", }, { DomainID: "test-domain-id2", }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, _ *resource.Test, _ *engine.MockEngine, _ *activecluster.MockManager) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID("test-domain-id").Return(cache.NewDomainCacheEntryForTest(nil, nil, true, nil, 0, nil, 0, 0, 0), nil) }, wantErr: true, }, { name: "no signal events", eventBatches: []*persistence.WorkflowEvents{ { DomainID: "test-domain-id", }, { DomainID: "test-domain-id", }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, _ *resource.Test, _ *engine.MockEngine, _ *activecluster.MockManager) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID("test-domain-id").Return(cache.NewDomainCacheEntryForTest(nil, nil, true, nil, 0, nil, 0, 0, 0), nil) }, wantErr: false, }, { name: "lookup workflow error", eventBatches: []*persistence.WorkflowEvents{ { DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", Events: []*types.HistoryEvent{ { EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), }, }, }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResource *resource.Test, mockEngine *engine.MockEngine, mockActiveClusterManager *activecluster.MockManager) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID("test-domain-id").Return( cache.NewGlobalDomainCacheEntryForTest(nil, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: 1, }, }, }, }, }, }, 0), nil) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager) mockActiveClusterManager.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id").Return( nil, errors.New("some error")) }, wantErr: true, }, { name: "success - active-active apply to current cluster", eventBatches: []*persistence.WorkflowEvents{ { DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", Events: []*types.HistoryEvent{ { EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), }, }, }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResource *resource.Test, mockEngine *engine.MockEngine, mockActiveClusterManager *activecluster.MockManager) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID("test-domain-id").Return( cache.NewGlobalDomainCacheEntryForTest(nil, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: 1, }, }, }, }, }, }, 0), nil) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager) mockActiveClusterManager.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id").Return( &types.ActiveClusterInfo{ ActiveClusterName: cluster.TestCurrentClusterName, }, nil) mockShard.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata) mockShard.EXPECT().GetEngine().Return(mockEngine) mockEngine.EXPECT().ReapplyEvents(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id", []*types.HistoryEvent{ { EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), }, }).Return(nil) }, }, { name: "success - active-active apply to remote cluster", eventBatches: []*persistence.WorkflowEvents{ { DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", Events: []*types.HistoryEvent{ { EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), }, }, }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResource *resource.Test, mockEngine *engine.MockEngine, mockActiveClusterManager *activecluster.MockManager) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID("test-domain-id").Return( cache.NewGlobalDomainCacheEntryForTest(&persistence.DomainInfo{Name: "test-domain"}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: cluster.TestCurrentClusterName, FailoverVersion: 1, }, }, }, }, }, }, 0), nil) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager) mockActiveClusterManager.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id").Return( &types.ActiveClusterInfo{ ActiveClusterName: cluster.TestAlternativeClusterName, }, nil) mockShard.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata) mockShard.EXPECT().GetService().Return(mockResource).Times(2) mockResource.RemoteAdminClient.EXPECT().ReapplyEvents(gomock.Any(), gomock.Any()).Return(nil) }, }, { name: "success - apply to current cluster", eventBatches: []*persistence.WorkflowEvents{ { DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", Events: []*types.HistoryEvent{ { EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), }, }, }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, _ *resource.Test, mockEngine *engine.MockEngine, mockActiveClusterManager *activecluster.MockManager) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID("test-domain-id").Return(cache.NewGlobalDomainCacheEntryForTest(nil, nil, &persistence.DomainReplicationConfig{ActiveClusterName: cluster.TestCurrentClusterName}, 0), nil) mockShard.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager) mockActiveClusterManager.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id").Return( &types.ActiveClusterInfo{ ActiveClusterName: cluster.TestCurrentClusterName, }, nil) mockShard.EXPECT().GetEngine().Return(mockEngine) mockEngine.EXPECT().ReapplyEvents(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id", []*types.HistoryEvent{ { EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), }, }).Return(nil) }, wantErr: false, }, { name: "success - apply to remote cluster", eventBatches: []*persistence.WorkflowEvents{ { DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", Events: []*types.HistoryEvent{ { EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), }, }, }, }, mockSetup: func(mockShard *shard.MockContext, mockDomainCache *cache.MockDomainCache, mockResource *resource.Test, mockEngine *engine.MockEngine, mockActiveClusterManager *activecluster.MockManager) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID("test-domain-id").Return(cache.NewGlobalDomainCacheEntryForTest(&persistence.DomainInfo{Name: "test-domain"}, nil, &persistence.DomainReplicationConfig{ActiveClusterName: cluster.TestAlternativeClusterName}, 0), nil) mockShard.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata) mockShard.EXPECT().GetService().Return(mockResource).Times(2) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager) mockActiveClusterManager.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), "test-domain-id", "test-workflow-id", "test-run-id").Return( &types.ActiveClusterInfo{ ActiveClusterName: cluster.TestAlternativeClusterName, }, nil) mockResource.RemoteAdminClient.EXPECT().ReapplyEvents(gomock.Any(), gomock.Any()).Return(nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockEngine := engine.NewMockEngine(mockCtrl) mockActiveClusterManager := activecluster.NewMockManager(mockCtrl) testResource := resource.NewTest(t, mockCtrl, metrics.Common) if tc.mockSetup != nil { tc.mockSetup(mockShard, mockDomainCache, testResource, mockEngine, mockActiveClusterManager) } ctx := &contextImpl{ shard: mockShard, } err := ctx.ReapplyEvents(tc.eventBatches) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestGetWorkflowExecutionWithRetry(t *testing.T) { testCases := []struct { name string request *persistence.GetWorkflowExecutionRequest mockSetup func(*shard.MockContext, *log.MockLogger, clock.MockedTimeSource) want *persistence.GetWorkflowExecutionResponse wantErr bool assertErr func(*testing.T, error) }{ { name: "Success case", request: &persistence.GetWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext, mockLogger *log.MockLogger, timeSource clock.MockedTimeSource) { mockShard.EXPECT().GetWorkflowExecution(gomock.Any(), &persistence.GetWorkflowExecutionRequest{ RangeID: 100, }).Return(&persistence.GetWorkflowExecutionResponse{ MutableStateStats: &persistence.MutableStateStats{ MutableStateSize: 123, }, }, nil) }, want: &persistence.GetWorkflowExecutionResponse{ MutableStateStats: &persistence.MutableStateStats{ MutableStateSize: 123, }, }, wantErr: false, }, { name: "entity not exists error", request: &persistence.GetWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext, mockLogger *log.MockLogger, timeSource clock.MockedTimeSource) { mockShard.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &types.EntityNotExistsError{}) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.IsType(t, err, &types.EntityNotExistsError{}) }, }, { name: "shard closed error recent", request: &persistence.GetWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext, mockLogger *log.MockLogger, timeSource clock.MockedTimeSource) { mockShard.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &shard.ErrShardClosed{ ClosedAt: timeSource.Now().Add(-shard.TimeBeforeShardClosedIsError / 2), }) // We do _not_ expect a log call }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.ErrorAs(t, err, new(*shard.ErrShardClosed)) }, }, { name: "shard closed error", request: &persistence.GetWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext, mockLogger *log.MockLogger, timeSource clock.MockedTimeSource) { err := &shard.ErrShardClosed{ ClosedAt: timeSource.Now().Add(-shard.TimeBeforeShardClosedIsError * 2), } mockShard.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, err) expectLog(mockLogger, err) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.ErrorAs(t, err, new(*shard.ErrShardClosed)) }, }, { name: "non retryable error", request: &persistence.GetWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext, mockLogger *log.MockLogger, timeSource clock.MockedTimeSource) { mockShard.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, errors.New("some error")) expectLog(mockLogger, errors.New("some error")) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, errors.New("some error"), err) }, }, { name: "retry succeeds", request: &persistence.GetWorkflowExecutionRequest{ RangeID: 100, }, mockSetup: func(mockShard *shard.MockContext, mockLogger *log.MockLogger, timeSource clock.MockedTimeSource) { mockShard.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &types.ServiceBusyError{}) mockShard.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()).Return(&persistence.GetWorkflowExecutionResponse{ MutableStateStats: &persistence.MutableStateStats{ MutableStateSize: 123, }, }, nil) }, want: &persistence.GetWorkflowExecutionResponse{ MutableStateStats: &persistence.MutableStateStats{ MutableStateSize: 123, }, }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockLogger := log.NewMockLogger(gomock.NewController(t)) mockLogger.EXPECT().Helper().Return(mockLogger) timeSource := clock.NewMockedTimeSource() mockShard.EXPECT().GetTimeSource().Return(timeSource).AnyTimes() policy := backoff.NewExponentialRetryPolicy(time.Millisecond) policy.SetMaximumAttempts(1) if tc.mockSetup != nil { tc.mockSetup(mockShard, mockLogger, timeSource) } resp, err := getWorkflowExecutionWithRetry(context.Background(), mockShard, mockLogger, policy, tc.request) if tc.wantErr { assert.Error(t, err) if tc.assertErr != nil { tc.assertErr(t, err) } } else { assert.NoError(t, err) assert.Equal(t, tc.want, resp) } }) } } func expectLog(mockLogger *log.MockLogger, err error) *gomock.Call { return mockLogger.EXPECT().Error( "Persistent fetch operation failure", []tag.Tag{ tag.StoreOperationGetWorkflowExecution, tag.Error(err), }) } func TestLoadWorkflowExecutionWithTaskVersion(t *testing.T) { testCases := []struct { name string mockSetup func(*shard.MockContext, *MockMutableState, *cache.MockDomainCache) mockGetWorkflowExecutionFn func(context.Context, *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) mockEmitWorkflowExecutionStatsFn func(string, *persistence.MutableStateStats, int64) mockUpdateWorkflowExecutionWithNewFn func(context.Context, time.Time, persistence.UpdateWorkflowMode, Context, MutableState, TransactionPolicy, *TransactionPolicy, persistence.CreateWorkflowRequestMode) error wantErr bool }{ { name: "domain cache failed", mockSetup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "getWorkflowExecutionFn failed", mockSetup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ Name: "test-domain", }, nil, true, nil, 0, nil, 0, 0, 0), nil) }, mockGetWorkflowExecutionFn: func(context.Context, *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) { return nil, errors.New("some error") }, wantErr: true, }, { name: "StartTransaction failed", mockSetup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ Name: "test-domain", }, nil, true, nil, 0, nil, 0, 0, 0), nil) mockMutableState.EXPECT().Load(gomock.Any(), gomock.Any()).Return(errors.New("some error")) mockMutableState.EXPECT().StartTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, errors.New("some error")) }, mockGetWorkflowExecutionFn: func(context.Context, *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) { return &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 123, }, }, }, nil }, mockEmitWorkflowExecutionStatsFn: func(domainName string, stats *persistence.MutableStateStats, size int64) { assert.Equal(t, "test-domain", domainName) assert.Equal(t, int64(123), size) }, wantErr: true, }, { name: "do not need to flush", mockSetup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ Name: "test-domain", }, nil, true, nil, 0, nil, 0, 0, 0), nil) mockMutableState.EXPECT().Load(gomock.Any(), gomock.Any()).Return(errors.New("some error")) mockMutableState.EXPECT().StartTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil) }, mockGetWorkflowExecutionFn: func(context.Context, *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) { return &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 123, }, }, }, nil }, mockEmitWorkflowExecutionStatsFn: func(domainName string, stats *persistence.MutableStateStats, size int64) { assert.Equal(t, "test-domain", domainName) assert.Equal(t, int64(123), size) }, wantErr: false, }, { name: "flush buffered events", mockSetup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, mockDomainCache *cache.MockDomainCache) { mockShard.EXPECT().GetDomainCache().Return(mockDomainCache) mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ Name: "test-domain", }, nil, true, nil, 0, nil, 0, 0, 0), nil) mockMutableState.EXPECT().Load(gomock.Any(), gomock.Any()).Return(errors.New("some error")) mockMutableState.EXPECT().StartTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return(true, nil) mockMutableState.EXPECT().StartTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil) mockShard.EXPECT().GetTimeSource().Return(clock.NewMockedTimeSource()) }, mockGetWorkflowExecutionFn: func(context.Context, *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) { return &persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", }, ExecutionStats: &persistence.ExecutionStats{ HistorySize: 123, }, }, }, nil }, mockEmitWorkflowExecutionStatsFn: func(domainName string, stats *persistence.MutableStateStats, size int64) { assert.Equal(t, "test-domain", domainName) assert.Equal(t, int64(123), size) }, mockUpdateWorkflowExecutionWithNewFn: func(context.Context, time.Time, persistence.UpdateWorkflowMode, Context, MutableState, TransactionPolicy, *TransactionPolicy, persistence.CreateWorkflowRequestMode) error { return nil }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockMutableState := NewMockMutableState(mockCtrl) if tc.mockSetup != nil { tc.mockSetup(mockShard, mockMutableState, mockDomainCache) } ctx := &contextImpl{ shard: mockShard, logger: testlogger.New(t), createMutableStateFn: func(shard.Context, log.Logger, *cache.DomainCacheEntry) MutableState { return mockMutableState }, getWorkflowExecutionFn: tc.mockGetWorkflowExecutionFn, emitWorkflowExecutionStatsFn: tc.mockEmitWorkflowExecutionStatsFn, updateWorkflowExecutionWithNewFn: tc.mockUpdateWorkflowExecutionWithNewFn, } got, err := ctx.LoadWorkflowExecutionWithTaskVersion(context.Background(), 123) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, mockMutableState, got) } }) } } func TestUpdateWorkflowExecutionAsActive(t *testing.T) { testCases := []struct { name string mockUpdateWorkflowExecutionWithNewFn func(context.Context, time.Time, persistence.UpdateWorkflowMode, Context, MutableState, TransactionPolicy, *TransactionPolicy, persistence.CreateWorkflowRequestMode) error wantErr bool }{ { name: "success", mockUpdateWorkflowExecutionWithNewFn: func(_ context.Context, _ time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentPolicy TransactionPolicy, newPolicy *TransactionPolicy, workflowRequestMode persistence.CreateWorkflowRequestMode) error { assert.Equal(t, persistence.UpdateWorkflowModeUpdateCurrent, updateMode) assert.Nil(t, newContext) assert.Nil(t, newMutableState) assert.Equal(t, TransactionPolicyActive, currentPolicy) assert.Nil(t, newPolicy) assert.Equal(t, persistence.CreateWorkflowRequestModeNew, workflowRequestMode) return nil }, wantErr: false, }, { name: "error case", mockUpdateWorkflowExecutionWithNewFn: func(_ context.Context, _ time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentPolicy TransactionPolicy, newPolicy *TransactionPolicy, _ persistence.CreateWorkflowRequestMode) error { return errors.New("some error") }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctx := &contextImpl{ updateWorkflowExecutionWithNewFn: tc.mockUpdateWorkflowExecutionWithNewFn, logger: testlogger.New(t), workflowExecution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } err := ctx.UpdateWorkflowExecutionAsActive(context.Background(), time.Now()) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestUpdateWorkflowExecutionWithNewAsActive(t *testing.T) { testCases := []struct { name string mockUpdateWorkflowExecutionWithNewFn func(context.Context, time.Time, persistence.UpdateWorkflowMode, Context, MutableState, TransactionPolicy, *TransactionPolicy, persistence.CreateWorkflowRequestMode) error wantErr bool }{ { name: "success", mockUpdateWorkflowExecutionWithNewFn: func(_ context.Context, _ time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentPolicy TransactionPolicy, newPolicy *TransactionPolicy, workflowRequestMode persistence.CreateWorkflowRequestMode) error { assert.Equal(t, persistence.UpdateWorkflowModeUpdateCurrent, updateMode) assert.NotNil(t, newContext) assert.NotNil(t, newMutableState) assert.Equal(t, TransactionPolicyActive, currentPolicy) assert.Equal(t, TransactionPolicyActive.Ptr(), newPolicy) assert.Equal(t, persistence.CreateWorkflowRequestModeNew, workflowRequestMode) return nil }, wantErr: false, }, { name: "error case", mockUpdateWorkflowExecutionWithNewFn: func(_ context.Context, _ time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentPolicy TransactionPolicy, newPolicy *TransactionPolicy, _ persistence.CreateWorkflowRequestMode) error { return errors.New("some error") }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctx := &contextImpl{ updateWorkflowExecutionWithNewFn: tc.mockUpdateWorkflowExecutionWithNewFn, logger: testlogger.New(t), workflowExecution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } err := ctx.UpdateWorkflowExecutionWithNewAsActive(context.Background(), time.Now(), &contextImpl{}, &mutableStateBuilder{}) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestUpdateWorkflowExecutionAsPassive(t *testing.T) { testCases := []struct { name string mockUpdateWorkflowExecutionWithNewFn func(context.Context, time.Time, persistence.UpdateWorkflowMode, Context, MutableState, TransactionPolicy, *TransactionPolicy, persistence.CreateWorkflowRequestMode) error wantErr bool }{ { name: "success", mockUpdateWorkflowExecutionWithNewFn: func(_ context.Context, _ time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentPolicy TransactionPolicy, newPolicy *TransactionPolicy, workflowRequestMode persistence.CreateWorkflowRequestMode) error { assert.Equal(t, persistence.UpdateWorkflowModeUpdateCurrent, updateMode) assert.Nil(t, newContext) assert.Nil(t, newMutableState) assert.Equal(t, TransactionPolicyPassive, currentPolicy) assert.Nil(t, newPolicy) assert.Equal(t, persistence.CreateWorkflowRequestModeReplicated, workflowRequestMode) return nil }, wantErr: false, }, { name: "error case", mockUpdateWorkflowExecutionWithNewFn: func(_ context.Context, _ time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentPolicy TransactionPolicy, newPolicy *TransactionPolicy, _ persistence.CreateWorkflowRequestMode) error { return errors.New("some error") }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctx := &contextImpl{ updateWorkflowExecutionWithNewFn: tc.mockUpdateWorkflowExecutionWithNewFn, logger: testlogger.New(t), workflowExecution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } err := ctx.UpdateWorkflowExecutionAsPassive(context.Background(), time.Now()) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestUpdateWorkflowExecutionWithNewAsPassive(t *testing.T) { testCases := []struct { name string mockUpdateWorkflowExecutionWithNewFn func(context.Context, time.Time, persistence.UpdateWorkflowMode, Context, MutableState, TransactionPolicy, *TransactionPolicy, persistence.CreateWorkflowRequestMode) error wantErr bool }{ { name: "success", mockUpdateWorkflowExecutionWithNewFn: func(_ context.Context, _ time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentPolicy TransactionPolicy, newPolicy *TransactionPolicy, workflowRequestMode persistence.CreateWorkflowRequestMode) error { assert.Equal(t, persistence.UpdateWorkflowModeUpdateCurrent, updateMode) assert.NotNil(t, newContext) assert.NotNil(t, newMutableState) assert.Equal(t, TransactionPolicyPassive, currentPolicy) assert.Equal(t, TransactionPolicyPassive.Ptr(), newPolicy) assert.Equal(t, persistence.CreateWorkflowRequestModeReplicated, workflowRequestMode) return nil }, wantErr: false, }, { name: "error case", mockUpdateWorkflowExecutionWithNewFn: func(_ context.Context, _ time.Time, updateMode persistence.UpdateWorkflowMode, newContext Context, newMutableState MutableState, currentPolicy TransactionPolicy, newPolicy *TransactionPolicy, _ persistence.CreateWorkflowRequestMode) error { return errors.New("some error") }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctx := &contextImpl{ updateWorkflowExecutionWithNewFn: tc.mockUpdateWorkflowExecutionWithNewFn, logger: testlogger.New(t), workflowExecution: types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, } err := ctx.UpdateWorkflowExecutionWithNewAsPassive(context.Background(), time.Now(), &contextImpl{}, &mutableStateBuilder{}) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestValidateWorkflowRequestsAndMode(t *testing.T) { testCases := []struct { name string requests []*persistence.WorkflowRequest mode persistence.CreateWorkflowRequestMode wantErr bool }{ { name: "Success - replicated mode", mode: persistence.CreateWorkflowRequestModeReplicated, wantErr: false, }, { name: "Success - new mode", requests: []*persistence.WorkflowRequest{ { RequestType: persistence.WorkflowRequestTypeCancel, RequestID: "abc", Version: 1, }, }, mode: persistence.CreateWorkflowRequestModeNew, wantErr: false, }, { name: "Success - new mode, signal with start", requests: []*persistence.WorkflowRequest{ { RequestType: persistence.WorkflowRequestTypeSignal, RequestID: "abc", Version: 1, }, { RequestType: persistence.WorkflowRequestTypeStart, RequestID: "abc", Version: 1, }, }, mode: persistence.CreateWorkflowRequestModeNew, wantErr: false, }, { name: "too many requests", requests: []*persistence.WorkflowRequest{ { RequestType: persistence.WorkflowRequestTypeSignal, RequestID: "abc", Version: 1, }, { RequestType: persistence.WorkflowRequestTypeSignal, RequestID: "abc", Version: 1, }, }, mode: persistence.CreateWorkflowRequestModeNew, wantErr: true, }, { name: "too many requests", requests: []*persistence.WorkflowRequest{ { RequestType: persistence.WorkflowRequestTypeSignal, RequestID: "abc", Version: 1, }, { RequestType: persistence.WorkflowRequestTypeSignal, RequestID: "abc", Version: 1, }, { RequestType: persistence.WorkflowRequestTypeSignal, RequestID: "abc", Version: 1, }, }, mode: persistence.CreateWorkflowRequestModeNew, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := validateWorkflowRequestsAndMode(tc.requests, tc.mode) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } ================================================ FILE: service/history/execution/context_util.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func (c *contextImpl) emitLargeWorkflowShardIDStats(blobSize int64, oldHistoryCount int64, oldHistorySize int64, newHistoryCount int64) { if c.shard.GetConfig().EnableShardIDMetrics() { shardID := c.shard.GetShardID() blobSizeWarn := min(int64(c.shard.GetConfig().LargeShardHistoryBlobMetricThreshold()), int64(c.shard.GetConfig().BlobSizeLimitWarn(c.GetDomainName()))) // check if blob size is larger than threshold in Dynamic config if so alert on it every time if blobSize > blobSizeWarn { c.logger.SampleInfo("Workflow writing a large blob", c.shard.GetConfig().SampleLoggingRate(), tag.WorkflowDomainName(c.GetDomainName()), tag.WorkflowID(c.workflowExecution.GetWorkflowID()), tag.ShardID(c.shard.GetShardID()), tag.WorkflowRunID(c.workflowExecution.GetRunID())) c.metricsClient.Scope(metrics.LargeExecutionBlobShardScope, metrics.ShardIDTag(shardID), metrics.DomainTag(c.GetDomainName())).IncCounter(metrics.LargeHistoryBlobCount) } historyCountWarn := min(int64(c.shard.GetConfig().LargeShardHistoryEventMetricThreshold()), int64(c.shard.GetConfig().HistoryCountLimitWarn(c.GetDomainName()))) // check if the new history count is greater than our threshold and only count/log it once when it passes it // this seems to double count and I can't figure out why but should be ok to get a rough idea and identify bad actors if oldHistoryCount < historyCountWarn && newHistoryCount >= historyCountWarn { c.logger.Warn("Workflow history event count is reaching dangerous levels", tag.WorkflowDomainName(c.GetDomainName()), tag.WorkflowID(c.workflowExecution.GetWorkflowID()), tag.ShardID(c.shard.GetShardID()), tag.WorkflowRunID(c.workflowExecution.GetRunID())) c.metricsClient.Scope(metrics.LargeExecutionCountShardScope, metrics.ShardIDTag(shardID), metrics.DomainTag(c.GetDomainName())).IncCounter(metrics.LargeHistoryEventCount) } historySizeWarn := min(int64(c.shard.GetConfig().LargeShardHistorySizeMetricThreshold()), int64(c.shard.GetConfig().HistorySizeLimitWarn(c.GetDomainName()))) // check if the new history size is greater than our threshold and only count/log it once when it passes it if oldHistorySize < historySizeWarn && c.stats.HistorySize >= historySizeWarn { c.logger.Warn("Workflow history event size is reaching dangerous levels", tag.WorkflowDomainName(c.GetDomainName()), tag.WorkflowID(c.workflowExecution.GetWorkflowID()), tag.ShardID(c.shard.GetShardID()), tag.WorkflowRunID(c.workflowExecution.GetRunID())) c.metricsClient.Scope(metrics.LargeExecutionSizeShardScope, metrics.ShardIDTag(shardID), metrics.DomainTag(c.GetDomainName())).IncCounter(metrics.LargeHistorySizeCount) } } } func emitWorkflowHistoryStats( metricsClient metrics.Client, domainName string, historySize int, historyCount int, ) { sizeScope := metricsClient.Scope(metrics.ExecutionSizeStatsScope, metrics.DomainTag(domainName)) countScope := metricsClient.Scope(metrics.ExecutionCountStatsScope, metrics.DomainTag(domainName)) sizeScope.RecordTimer(metrics.HistorySize, time.Duration(historySize)) countScope.RecordTimer(metrics.HistoryCount, time.Duration(historyCount)) } func emitWorkflowExecutionStats( metricsClient metrics.Client, domainName string, stats *persistence.MutableStateStats, executionInfoHistorySize int64, ) { if stats == nil { return } sizeScope := metricsClient.Scope(metrics.ExecutionSizeStatsScope, metrics.DomainTag(domainName)) countScope := metricsClient.Scope(metrics.ExecutionCountStatsScope, metrics.DomainTag(domainName)) sizeScope.RecordTimer(metrics.HistorySize, time.Duration(executionInfoHistorySize)) sizeScope.RecordTimer(metrics.MutableStateSize, time.Duration(stats.MutableStateSize)) sizeScope.RecordTimer(metrics.ExecutionInfoSize, time.Duration(stats.MutableStateSize)) sizeScope.RecordTimer(metrics.ActivityInfoSize, time.Duration(stats.ActivityInfoSize)) sizeScope.RecordTimer(metrics.TimerInfoSize, time.Duration(stats.TimerInfoSize)) sizeScope.RecordTimer(metrics.ChildInfoSize, time.Duration(stats.ChildInfoSize)) sizeScope.RecordTimer(metrics.SignalInfoSize, time.Duration(stats.SignalInfoSize)) sizeScope.RecordTimer(metrics.BufferedEventsSize, time.Duration(stats.BufferedEventsSize)) countScope.RecordTimer(metrics.ActivityInfoCount, time.Duration(stats.ActivityInfoCount)) countScope.RecordTimer(metrics.TimerInfoCount, time.Duration(stats.TimerInfoCount)) countScope.RecordTimer(metrics.ChildInfoCount, time.Duration(stats.ChildInfoCount)) countScope.RecordTimer(metrics.SignalInfoCount, time.Duration(stats.SignalInfoCount)) countScope.RecordTimer(metrics.RequestCancelInfoCount, time.Duration(stats.RequestCancelInfoCount)) countScope.RecordTimer(metrics.BufferedEventsCount, time.Duration(stats.BufferedEventsCount)) } func emitSessionUpdateStats( metricsClient metrics.Client, domainName string, stats *persistence.MutableStateUpdateSessionStats, ) { if stats == nil { return } sizeScope := metricsClient.Scope(metrics.SessionSizeStatsScope, metrics.DomainTag(domainName)) countScope := metricsClient.Scope(metrics.SessionCountStatsScope, metrics.DomainTag(domainName)) sizeScope.RecordTimer(metrics.MutableStateSize, time.Duration(stats.MutableStateSize)) sizeScope.RecordTimer(metrics.ExecutionInfoSize, time.Duration(stats.ExecutionInfoSize)) sizeScope.RecordTimer(metrics.ActivityInfoSize, time.Duration(stats.ActivityInfoSize)) sizeScope.RecordTimer(metrics.TimerInfoSize, time.Duration(stats.TimerInfoSize)) sizeScope.RecordTimer(metrics.ChildInfoSize, time.Duration(stats.ChildInfoSize)) sizeScope.RecordTimer(metrics.SignalInfoSize, time.Duration(stats.SignalInfoSize)) sizeScope.RecordTimer(metrics.BufferedEventsSize, time.Duration(stats.BufferedEventsSize)) countScope.RecordTimer(metrics.ActivityInfoCount, time.Duration(stats.ActivityInfoCount)) countScope.RecordTimer(metrics.TimerInfoCount, time.Duration(stats.TimerInfoCount)) countScope.RecordTimer(metrics.ChildInfoCount, time.Duration(stats.ChildInfoCount)) countScope.RecordTimer(metrics.SignalInfoCount, time.Duration(stats.SignalInfoCount)) countScope.RecordTimer(metrics.RequestCancelInfoCount, time.Duration(stats.RequestCancelInfoCount)) countScope.RecordTimer(metrics.DeleteActivityInfoCount, time.Duration(stats.DeleteActivityInfoCount)) countScope.RecordTimer(metrics.DeleteTimerInfoCount, time.Duration(stats.DeleteTimerInfoCount)) countScope.RecordTimer(metrics.DeleteChildInfoCount, time.Duration(stats.DeleteChildInfoCount)) countScope.RecordTimer(metrics.DeleteSignalInfoCount, time.Duration(stats.DeleteSignalInfoCount)) countScope.RecordTimer(metrics.DeleteRequestCancelInfoCount, time.Duration(stats.DeleteRequestCancelInfoCount)) countScope.RecordTimer(metrics.TransferTasksCount, time.Duration(stats.TaskCountByCategory[persistence.HistoryTaskCategoryTransfer])) countScope.RecordTimer(metrics.TimerTasksCount, time.Duration(stats.TaskCountByCategory[persistence.HistoryTaskCategoryTimer])) countScope.RecordTimer(metrics.ReplicationTasksCount, time.Duration(stats.TaskCountByCategory[persistence.HistoryTaskCategoryReplication])) countScope.IncCounter(metrics.UpdateWorkflowExecutionCount) } func emitWorkflowCompletionStats( metricsClient metrics.Client, logger log.Logger, domainName string, workflowType string, workflowID string, runID string, taskList string, event *types.HistoryEvent, ) { if event.EventType == nil { return } scope := metricsClient.Scope( metrics.WorkflowCompletionStatsScope, metrics.DomainTag(domainName), metrics.WorkflowTypeTag(workflowType), metrics.TaskListTag(taskList), ) switch *event.EventType { case types.EventTypeWorkflowExecutionCompleted: scope.IncCounter(metrics.WorkflowSuccessCount) case types.EventTypeWorkflowExecutionCanceled: scope.IncCounter(metrics.WorkflowCancelCount) case types.EventTypeWorkflowExecutionFailed: scope.IncCounter(metrics.WorkflowFailedCount) case types.EventTypeWorkflowExecutionTimedOut: scope.IncCounter(metrics.WorkflowTimeoutCount) logger.Info("workflow execution timed out", tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.WorkflowDomainName(domainName), ) case types.EventTypeWorkflowExecutionTerminated: scope.IncCounter(metrics.WorkflowTerminateCount) logger.Info("workflow terminated", tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.WorkflowDomainName(domainName), ) case types.EventTypeWorkflowExecutionContinuedAsNew: scope.IncCounter(metrics.WorkflowContinuedAsNew) logger.Debug("workflow continued as new", tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.WorkflowDomainName(domainName), ) default: scope.IncCounter(metrics.WorkflowCompletedUnknownType) logger.Warn("Workflow completed with an unknown event type", tag.WorkflowEventType(event.EventType.String()), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.WorkflowDomainName(domainName), ) } } ================================================ FILE: service/history/execution/context_util_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" ) const ( _testDomain = "testDomain" ) func createTestConfig() *config.Config { return &config.Config{ EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(true), LargeShardHistoryBlobMetricThreshold: dynamicproperties.GetIntPropertyFn(1024 * 1024 * 5), // 5 MB BlobSizeLimitWarn: func(domainName string) int { return 1024 * 1024 * 5 }, // 5 MB LargeShardHistoryEventMetricThreshold: dynamicproperties.GetIntPropertyFn(1500), HistoryCountLimitWarn: func(domainName string) int { return 1500 }, LargeShardHistorySizeMetricThreshold: dynamicproperties.GetIntPropertyFn(1024 * 1024 * 2), // 2 MB HistorySizeLimitWarn: func(domainName string) int { return 1024 * 1024 * 2 }, // 2 MB SampleLoggingRate: dynamicproperties.GetIntPropertyFn(100), } } func TestEmitLargeWorkflowShardIDStats(t *testing.T) { tests := []struct { name string blobSize int64 oldHistoryCount int64 oldHistorySize int64 newHistoryCount int64 shardConfig *config.Config stats *persistence.ExecutionStats loggerExpectations func(logger *log.MockLogger) assertMetrics func(t *testing.T, snapshot tally.Snapshot) }{ { name: "Blob size exceeds threshold", blobSize: 1024 * 1024 * 10, // 10 MB oldHistoryCount: 1000, oldHistorySize: 1024 * 500, // 0.5 MB newHistoryCount: 100, stats: &persistence.ExecutionStats{ HistorySize: 0, }, shardConfig: createTestConfig(), loggerExpectations: func(logger *log.MockLogger) { logger.EXPECT().SampleInfo("Workflow writing a large blob", 100, gomock.Any()) }, assertMetrics: func(t *testing.T, snapshot tally.Snapshot) { countersSnapshot := snapshot.Counters() require.Contains(t, countersSnapshot, "test.large_history_blob_count+domain=testDomain,operation=LargeExecutionBlobShard,shard_id=1") assert.Equal(t, int64(1), countersSnapshot["test.large_history_blob_count+domain=testDomain,operation=LargeExecutionBlobShard,shard_id=1"].Value()) }, }, { name: "History old size and old count already exceeds threshold", blobSize: 1024 * 1024 * 10, // 10 MB oldHistoryCount: 1500, oldHistorySize: 1024 * 1024 * 2, // 0.5 MB newHistoryCount: 2000, stats: &persistence.ExecutionStats{ HistorySize: 1024 * 1024 * 3, }, shardConfig: createTestConfig(), loggerExpectations: func(logger *log.MockLogger) { logger.EXPECT().SampleInfo("Workflow writing a large blob", 100, gomock.Any()) }, assertMetrics: func(t *testing.T, snapshot tally.Snapshot) { countersSnapshot := snapshot.Counters() require.NotContains(t, countersSnapshot, "test.large_history_event_count+domain=testDomain,operation=LargeExecutionCountShard,shard_id=1") require.NotContains(t, countersSnapshot, "test.large_history_size_count+domain=testDomain,operation=LargeExecutionSizeShard,shard_id=1") }, }, { name: "History old size and old count already exceeds threshold", blobSize: 1024 * 1024 * 10, // 10 MB oldHistoryCount: 1500, oldHistorySize: 1024 * 1024 * 2, // 0.5 MB newHistoryCount: 2000, stats: &persistence.ExecutionStats{ HistorySize: 1024 * 1024 * 3, }, shardConfig: createTestConfig(), loggerExpectations: func(logger *log.MockLogger) { logger.EXPECT().SampleInfo("Workflow writing a large blob", 100, gomock.Any()) }, assertMetrics: func(t *testing.T, snapshot tally.Snapshot) { countersSnapshot := snapshot.Counters() require.NotContains(t, countersSnapshot, "test.large_history_event_count+domain=testDomain,operation=LargeExecutionCountShard,shard_id=1") require.NotContains(t, countersSnapshot, "test.large_history_size_count+domain=testDomain,operation=LargeExecutionSizeShard,shard_id=1") }, }, { name: "History count and size within threshold", blobSize: 1024 * 500, // 0.5 MB oldHistoryCount: 500, oldHistorySize: 1024 * 1024, // 1 MB newHistoryCount: 800, stats: &persistence.ExecutionStats{}, shardConfig: createTestConfig(), }, { name: "Metrics disabled", blobSize: 1024 * 1024 * 10, // 10 MB oldHistoryCount: 1000, oldHistorySize: 1024 * 1024 * 2, // 2 MB newHistoryCount: 2000, shardConfig: &config.Config{ EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(false), }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockShard := shard.NewMockContext(mockCtrl) metricScope := tally.NewTestScope("test", make(map[string]string)) mockMetricsClient := metrics.NewClient(metricScope, metrics.History, metrics.HistogramMigration{}) mockLogger := log.NewMockLogger(gomock.NewController(t)) mockDomainCache := cache.NewMockDomainCache(mockCtrl) context := &contextImpl{ shard: mockShard, metricsClient: mockMetricsClient, logger: mockLogger, stats: tc.stats, } mockShard.EXPECT().GetShardID().Return(1).AnyTimes() mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).AnyTimes() mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(_testDomain, nil).AnyTimes() mockShard.EXPECT().GetConfig().Return(tc.shardConfig).AnyTimes() if tc.loggerExpectations != nil { tc.loggerExpectations(mockLogger) } context.emitLargeWorkflowShardIDStats(tc.blobSize, tc.oldHistoryCount, tc.oldHistorySize, tc.newHistoryCount) if tc.assertMetrics != nil { tc.assertMetrics(t, metricScope.Snapshot()) } }) } } func TestEmitWorkflowHistoryStats(t *testing.T) { mockMetricsClient := new(mocks.Client) mockScope := new(mocks.Scope) mockMetricsClient.On("Scope", mock.Anything, mock.Anything).Return(mockScope) mockScope.On("RecordTimer", mock.Anything, mock.Anything).Return() domainName := "testDomain" historySize := 2048 historyCount := 150 emitWorkflowHistoryStats(mockMetricsClient, domainName, historySize, historyCount) mockMetricsClient.AssertExpectations(t) mockScope.AssertExpectations(t) } func TestEmitWorkflowExecutionStats(t *testing.T) { tests := []struct { name string domainName string stats *persistence.MutableStateStats historySize int64 expectCalls bool }{ { name: "With valid stats", domainName: "testDomain", stats: &persistence.MutableStateStats{ MutableStateSize: 1024, ActivityInfoSize: 256, TimerInfoSize: 128, }, historySize: 2048, expectCalls: true, }, { name: "Nil stats", domainName: "testDomain", stats: nil, historySize: 2048, expectCalls: false, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { mockMetricsClient := new(mocks.Client) mockScope := new(mocks.Scope) if tc.expectCalls { mockMetricsClient.On("Scope", metrics.ExecutionSizeStatsScope, mock.Anything).Return(mockScope) mockMetricsClient.On("Scope", metrics.ExecutionCountStatsScope, mock.Anything).Return(mockScope) mockScope.On("RecordTimer", mock.AnythingOfType("metrics.MetricIdx"), mock.AnythingOfType("time.Duration")).Return().Times(14) } else { mockScope.AssertNotCalled(t, "RecordTimer") } emitWorkflowExecutionStats(mockMetricsClient, tc.domainName, tc.stats, tc.historySize) mockMetricsClient.AssertExpectations(t) mockScope.AssertExpectations(t) }) } } ================================================ FILE: service/history/execution/history_builder.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // HistoryBuilder builds and stores workflow history events HistoryBuilder struct { transientHistory []*types.HistoryEvent history []*types.HistoryEvent msBuilder MutableState } ) // NewHistoryBuilder creates a new history builder func NewHistoryBuilder(msBuilder MutableState) *HistoryBuilder { return &HistoryBuilder{ transientHistory: []*types.HistoryEvent{}, history: []*types.HistoryEvent{}, msBuilder: msBuilder, } } // NewHistoryBuilderFromEvents creates a new history builder based on the given workflow history events func NewHistoryBuilderFromEvents(history []*types.HistoryEvent, msBuilder MutableState) *HistoryBuilder { return &HistoryBuilder{ history: history, msBuilder: msBuilder, } } // AddWorkflowExecutionStartedEvent adds WorkflowExecutionStarted event to history // originalRunID is the runID when the WorkflowExecutionStarted event is written // firstRunID is the very first runID along the chain of ContinueAsNew and Reset func (b *HistoryBuilder) AddWorkflowExecutionStartedEvent( startRequest *types.HistoryStartWorkflowExecutionRequest, previousExecution *persistence.WorkflowExecutionInfo, firstRunID string, originalRunID string, firstScheduledTime time.Time, ) *types.HistoryEvent { var prevRunID string var resetPoints *types.ResetPoints if previousExecution != nil { prevRunID = previousExecution.RunID resetPoints = previousExecution.AutoResetPoints } request := startRequest.StartRequest event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeWorkflowExecutionStarted) var scheduledTime *time.Time if request.CronSchedule != "" { // first scheduled time is only necessary for cron workflows. scheduledTime = &firstScheduledTime } attributes := &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: request.WorkflowType, TaskList: request.TaskList, Header: request.Header, Input: request.Input, ExecutionStartToCloseTimeoutSeconds: request.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: request.TaskStartToCloseTimeoutSeconds, ContinuedExecutionRunID: prevRunID, PrevAutoResetPoints: resetPoints, Identity: request.Identity, RetryPolicy: request.RetryPolicy, Attempt: startRequest.GetAttempt(), ExpirationTimestamp: startRequest.ExpirationTimestamp, CronSchedule: request.CronSchedule, CronOverlapPolicy: request.CronOverlapPolicy, LastCompletionResult: startRequest.LastCompletionResult, ContinuedFailureReason: startRequest.ContinuedFailureReason, ContinuedFailureDetails: startRequest.ContinuedFailureDetails, Initiator: startRequest.ContinueAsNewInitiator, FirstDecisionTaskBackoffSeconds: startRequest.FirstDecisionTaskBackoffSeconds, FirstExecutionRunID: firstRunID, FirstScheduleTime: scheduledTime, OriginalExecutionRunID: originalRunID, Memo: request.Memo, SearchAttributes: request.SearchAttributes, JitterStartSeconds: request.JitterStartSeconds, PartitionConfig: startRequest.PartitionConfig, RequestID: request.RequestID, ActiveClusterSelectionPolicy: request.ActiveClusterSelectionPolicy, } if parentInfo := startRequest.ParentExecutionInfo; parentInfo != nil { attributes.ParentWorkflowDomainID = &parentInfo.DomainUUID attributes.ParentWorkflowDomain = &parentInfo.Domain attributes.ParentWorkflowExecution = parentInfo.Execution attributes.ParentInitiatedEventID = &parentInfo.InitiatedID } event.WorkflowExecutionStartedEventAttributes = attributes return b.addEventToHistory(event) } // AddDecisionTaskScheduledEvent adds DecisionTaskScheduled event to history func (b *HistoryBuilder) AddDecisionTaskScheduledEvent(taskList string, startToCloseTimeoutSeconds int32, attempt int64) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeDecisionTaskScheduled) event.DecisionTaskScheduledEventAttributes = getDecisionTaskScheduledEventAttributes(taskList, startToCloseTimeoutSeconds, attempt) return b.addEventToHistory(event) } // AddTransientDecisionTaskScheduledEvent adds transient DecisionTaskScheduled event func (b *HistoryBuilder) AddTransientDecisionTaskScheduledEvent(taskList string, startToCloseTimeoutSeconds int32, attempt int64, timestamp int64) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEventWithTimestamp(types.EventTypeDecisionTaskScheduled, timestamp) event.DecisionTaskScheduledEventAttributes = getDecisionTaskScheduledEventAttributes(taskList, startToCloseTimeoutSeconds, attempt) return b.addTransientEvent(event) } // AddDecisionTaskStartedEvent adds DecisionTaskStarted event to history func (b *HistoryBuilder) AddDecisionTaskStartedEvent(scheduleEventID int64, requestID string, identity string) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeDecisionTaskStarted) event.DecisionTaskStartedEventAttributes = getDecisionTaskStartedEventAttributes(scheduleEventID, requestID, identity) return b.addEventToHistory(event) } // AddTransientDecisionTaskStartedEvent adds transient DecisionTaskStarted event func (b *HistoryBuilder) AddTransientDecisionTaskStartedEvent(scheduleEventID int64, requestID string, identity string, timestamp int64) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEventWithTimestamp(types.EventTypeDecisionTaskStarted, timestamp) event.DecisionTaskStartedEventAttributes = getDecisionTaskStartedEventAttributes(scheduleEventID, requestID, identity) return b.addTransientEvent(event) } // AddDecisionTaskCompletedEvent adds DecisionTaskCompleted event to history func (b *HistoryBuilder) AddDecisionTaskCompletedEvent(scheduleEventID, StartedEventID int64, request *types.RespondDecisionTaskCompletedRequest) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeDecisionTaskCompleted) event.DecisionTaskCompletedEventAttributes = &types.DecisionTaskCompletedEventAttributes{ ExecutionContext: request.ExecutionContext, ScheduledEventID: scheduleEventID, StartedEventID: StartedEventID, Identity: request.Identity, BinaryChecksum: request.BinaryChecksum, } return b.addEventToHistory(event) } // AddDecisionTaskTimedOutEvent adds DecisionTaskTimedOut event to history func (b *HistoryBuilder) AddDecisionTaskTimedOutEvent( scheduleEventID int64, startedEventID int64, timeoutType types.TimeoutType, baseRunID string, newRunID string, forkEventVersion int64, reason string, cause types.DecisionTaskTimedOutCause, resetRequestID string, ) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeDecisionTaskTimedOut) event.DecisionTaskTimedOutEventAttributes = &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: scheduleEventID, StartedEventID: startedEventID, TimeoutType: timeoutType.Ptr(), BaseRunID: baseRunID, NewRunID: newRunID, ForkEventVersion: forkEventVersion, Reason: reason, Cause: cause.Ptr(), RequestID: resetRequestID, } return b.addEventToHistory(event) } // AddDecisionTaskFailedEvent adds DecisionTaskFailed event to history func (b *HistoryBuilder) AddDecisionTaskFailedEvent(attr types.DecisionTaskFailedEventAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeDecisionTaskFailed) event.DecisionTaskFailedEventAttributes = &attr return b.addEventToHistory(event) } // AddActivityTaskScheduledEvent adds ActivityTaskScheduled event to history func (b *HistoryBuilder) AddActivityTaskScheduledEvent(decisionCompletedEventID int64, attributes *types.ScheduleActivityTaskDecisionAttributes) *types.HistoryEvent { var domain *string if attributes.Domain != "" { // for backward compatibility // old releases will encounter issues if Domain field is a pointer to an empty string. domain = common.StringPtr(attributes.Domain) } event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeActivityTaskScheduled) event.ActivityTaskScheduledEventAttributes = &types.ActivityTaskScheduledEventAttributes{ ActivityID: attributes.ActivityID, ActivityType: attributes.ActivityType, TaskList: attributes.TaskList, Header: attributes.Header, Input: attributes.Input, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(common.Int32Default(attributes.ScheduleToCloseTimeoutSeconds)), ScheduleToStartTimeoutSeconds: common.Int32Ptr(common.Int32Default(attributes.ScheduleToStartTimeoutSeconds)), StartToCloseTimeoutSeconds: common.Int32Ptr(common.Int32Default(attributes.StartToCloseTimeoutSeconds)), HeartbeatTimeoutSeconds: common.Int32Ptr(common.Int32Default(attributes.HeartbeatTimeoutSeconds)), DecisionTaskCompletedEventID: decisionCompletedEventID, RetryPolicy: attributes.RetryPolicy, Domain: domain, } return b.addEventToHistory(event) } // AddActivityTaskStartedEvent adds ActivityTaskStarted event to history func (b *HistoryBuilder) AddActivityTaskStartedEvent( scheduleEventID int64, attempt int32, requestID string, identity string, lastFailureReason string, lastFailureDetails []byte, ) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeActivityTaskStarted) event.ActivityTaskStartedEventAttributes = &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: scheduleEventID, Attempt: attempt, Identity: identity, RequestID: requestID, LastFailureReason: common.StringPtr(lastFailureReason), LastFailureDetails: lastFailureDetails, } return b.addEventToHistory(event) } // AddActivityTaskCompletedEvent adds ActivityTaskCompleted event to history func (b *HistoryBuilder) AddActivityTaskCompletedEvent(scheduleEventID, startedEventID int64, request *types.RespondActivityTaskCompletedRequest) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeActivityTaskCompleted) event.ActivityTaskCompletedEventAttributes = &types.ActivityTaskCompletedEventAttributes{ Result: request.Result, ScheduledEventID: scheduleEventID, StartedEventID: startedEventID, Identity: request.Identity, } return b.addEventToHistory(event) } // AddActivityTaskFailedEvent adds ActivityTaskFailed event to history func (b *HistoryBuilder) AddActivityTaskFailedEvent(scheduleEventID, StartedEventID int64, request *types.RespondActivityTaskFailedRequest) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeActivityTaskFailed) event.ActivityTaskFailedEventAttributes = &types.ActivityTaskFailedEventAttributes{ Reason: common.StringPtr(common.StringDefault(request.Reason)), Details: request.Details, ScheduledEventID: scheduleEventID, StartedEventID: StartedEventID, Identity: request.Identity, } return b.addEventToHistory(event) } // AddActivityTaskTimedOutEvent adds ActivityTaskTimedOut event to history func (b *HistoryBuilder) AddActivityTaskTimedOutEvent( scheduleEventID, startedEventID int64, timeoutType types.TimeoutType, lastHeartBeatDetails []byte, lastFailureReason string, lastFailureDetail []byte, ) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeActivityTaskTimedOut) event.ActivityTaskTimedOutEventAttributes = &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: scheduleEventID, StartedEventID: startedEventID, TimeoutType: &timeoutType, Details: lastHeartBeatDetails, LastFailureReason: common.StringPtr(lastFailureReason), LastFailureDetails: lastFailureDetail, } return b.addEventToHistory(event) } // AddCompletedWorkflowEvent adds WorkflowExecutionCompleted event to history func (b *HistoryBuilder) AddCompletedWorkflowEvent(decisionCompletedEventID int64, attributes *types.CompleteWorkflowExecutionDecisionAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeWorkflowExecutionCompleted) event.WorkflowExecutionCompletedEventAttributes = &types.WorkflowExecutionCompletedEventAttributes{ Result: attributes.Result, DecisionTaskCompletedEventID: decisionCompletedEventID, } return b.addEventToHistory(event) } // AddFailWorkflowEvent adds WorkflowExecutionFailed event to history func (b *HistoryBuilder) AddFailWorkflowEvent(decisionCompletedEventID int64, attributes *types.FailWorkflowExecutionDecisionAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeWorkflowExecutionFailed) event.WorkflowExecutionFailedEventAttributes = &types.WorkflowExecutionFailedEventAttributes{ Reason: common.StringPtr(common.StringDefault(attributes.Reason)), Details: attributes.Details, DecisionTaskCompletedEventID: decisionCompletedEventID, } return b.addEventToHistory(event) } // AddTimeoutWorkflowEvent adds WorkflowExecutionTimedout event to history func (b *HistoryBuilder) AddTimeoutWorkflowEvent() *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeWorkflowExecutionTimedOut) event.WorkflowExecutionTimedOutEventAttributes = &types.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: types.TimeoutTypeStartToClose.Ptr(), } return b.addEventToHistory(event) } // AddWorkflowExecutionTerminatedEvent add WorkflowExecutionTerminated event to history func (b *HistoryBuilder) AddWorkflowExecutionTerminatedEvent( reason string, details []byte, identity string, ) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeWorkflowExecutionTerminated) event.WorkflowExecutionTerminatedEventAttributes = &types.WorkflowExecutionTerminatedEventAttributes{ Reason: reason, Details: details, Identity: identity, } return b.addEventToHistory(event) } // AddContinuedAsNewEvent adds WorkflowExecutionContinuedAsNew event to history func (b *HistoryBuilder) AddContinuedAsNewEvent(decisionCompletedEventID int64, newRunID string, attributes *types.ContinueAsNewWorkflowExecutionDecisionAttributes) *types.HistoryEvent { initiator := attributes.Initiator if initiator == nil { initiator = types.ContinueAsNewInitiatorDecider.Ptr() } event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeWorkflowExecutionContinuedAsNew) event.WorkflowExecutionContinuedAsNewEventAttributes = &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: newRunID, WorkflowType: attributes.WorkflowType, TaskList: attributes.TaskList, Header: attributes.Header, Input: attributes.Input, ExecutionStartToCloseTimeoutSeconds: attributes.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: attributes.TaskStartToCloseTimeoutSeconds, DecisionTaskCompletedEventID: decisionCompletedEventID, BackoffStartIntervalInSeconds: common.Int32Ptr(attributes.GetBackoffStartIntervalInSeconds()), Initiator: initiator, FailureReason: attributes.FailureReason, FailureDetails: attributes.FailureDetails, LastCompletionResult: attributes.LastCompletionResult, Memo: attributes.Memo, SearchAttributes: attributes.SearchAttributes, JitterStartSeconds: attributes.JitterStartSeconds, CronOverlapPolicy: attributes.CronOverlapPolicy, ActiveClusterSelectionPolicy: attributes.ActiveClusterSelectionPolicy, } return b.addEventToHistory(event) } // AddTimerStartedEvent adds TimerStart event to history func (b *HistoryBuilder) AddTimerStartedEvent(decisionCompletedEventID int64, request *types.StartTimerDecisionAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeTimerStarted) event.TimerStartedEventAttributes = &types.TimerStartedEventAttributes{ TimerID: request.TimerID, StartToFireTimeoutSeconds: request.StartToFireTimeoutSeconds, DecisionTaskCompletedEventID: decisionCompletedEventID, } return b.addEventToHistory(event) } // AddTimerFiredEvent adds TimerFired event to history func (b *HistoryBuilder) AddTimerFiredEvent( StartedEventID int64, TimerID string, ) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeTimerFired) event.TimerFiredEventAttributes = &types.TimerFiredEventAttributes{ TimerID: TimerID, StartedEventID: StartedEventID, } return b.addEventToHistory(event) } // AddActivityTaskCancelRequestedEvent add ActivityTaskCancelRequested event to history func (b *HistoryBuilder) AddActivityTaskCancelRequestedEvent(decisionCompletedEventID int64, activityID string) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeActivityTaskCancelRequested) event.ActivityTaskCancelRequestedEventAttributes = &types.ActivityTaskCancelRequestedEventAttributes{ ActivityID: activityID, DecisionTaskCompletedEventID: decisionCompletedEventID, } return b.addEventToHistory(event) } // AddRequestCancelActivityTaskFailedEvent add RequestCancelActivityTaskFailed event to history func (b *HistoryBuilder) AddRequestCancelActivityTaskFailedEvent(decisionCompletedEventID int64, activityID string, cause string) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeRequestCancelActivityTaskFailed) event.RequestCancelActivityTaskFailedEventAttributes = &types.RequestCancelActivityTaskFailedEventAttributes{ ActivityID: activityID, DecisionTaskCompletedEventID: decisionCompletedEventID, Cause: cause, } return b.addEventToHistory(event) } // AddActivityTaskCanceledEvent adds ActivityTaskCanceled event to history func (b *HistoryBuilder) AddActivityTaskCanceledEvent(scheduleEventID, startedEventID int64, latestCancelRequestedEventID int64, details []byte, identity string) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeActivityTaskCanceled) event.ActivityTaskCanceledEventAttributes = &types.ActivityTaskCanceledEventAttributes{ ScheduledEventID: scheduleEventID, StartedEventID: startedEventID, LatestCancelRequestedEventID: latestCancelRequestedEventID, Details: details, Identity: identity, } return b.addEventToHistory(event) } // AddTimerCanceledEvent adds TimerCanceled event to history func (b *HistoryBuilder) AddTimerCanceledEvent(startedEventID int64, decisionTaskCompletedEventID int64, timerID string, identity string) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeTimerCanceled) event.TimerCanceledEventAttributes = &types.TimerCanceledEventAttributes{ StartedEventID: startedEventID, DecisionTaskCompletedEventID: decisionTaskCompletedEventID, TimerID: timerID, Identity: identity, } return b.addEventToHistory(event) } // AddCancelTimerFailedEvent adds CancelTimerFailed event to history func (b *HistoryBuilder) AddCancelTimerFailedEvent(timerID string, decisionTaskCompletedEventID int64, cause string, identity string) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeCancelTimerFailed) event.CancelTimerFailedEventAttributes = &types.CancelTimerFailedEventAttributes{ TimerID: timerID, DecisionTaskCompletedEventID: decisionTaskCompletedEventID, Cause: cause, Identity: identity, } return b.addEventToHistory(event) } // AddWorkflowExecutionCancelRequestedEvent adds WorkflowExecutionCancelRequested event to history func (b *HistoryBuilder) AddWorkflowExecutionCancelRequestedEvent( cause string, request *types.HistoryRequestCancelWorkflowExecutionRequest, ) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeWorkflowExecutionCancelRequested) event.WorkflowExecutionCancelRequestedEventAttributes = &types.WorkflowExecutionCancelRequestedEventAttributes{ Cause: cause, Identity: request.CancelRequest.Identity, ExternalInitiatedEventID: request.ExternalInitiatedEventID, ExternalWorkflowExecution: request.ExternalWorkflowExecution, RequestID: request.CancelRequest.RequestID, } return b.addEventToHistory(event) } // AddWorkflowExecutionCanceledEvent adds WorkflowExecutionCanceled event to history func (b *HistoryBuilder) AddWorkflowExecutionCanceledEvent(decisionTaskCompletedEventID int64, attributes *types.CancelWorkflowExecutionDecisionAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeWorkflowExecutionCanceled) event.WorkflowExecutionCanceledEventAttributes = &types.WorkflowExecutionCanceledEventAttributes{ DecisionTaskCompletedEventID: decisionTaskCompletedEventID, Details: attributes.Details, } return b.addEventToHistory(event) } // AddRequestCancelExternalWorkflowExecutionInitiatedEvent adds RequestCancelExternalWorkflowExecutionInitiated event to history func (b *HistoryBuilder) AddRequestCancelExternalWorkflowExecutionInitiatedEvent(decisionTaskCompletedEventID int64, request *types.RequestCancelExternalWorkflowExecutionDecisionAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeRequestCancelExternalWorkflowExecutionInitiated) event.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes = &types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventID: decisionTaskCompletedEventID, Domain: request.Domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: request.WorkflowID, RunID: request.RunID, }, Control: request.Control, ChildWorkflowOnly: request.ChildWorkflowOnly, } return b.addEventToHistory(event) } // AddRequestCancelExternalWorkflowExecutionFailedEvent adds RequestCancelExternalWorkflowExecutionFailed event to history func (b *HistoryBuilder) AddRequestCancelExternalWorkflowExecutionFailedEvent(decisionTaskCompletedEventID, initiatedEventID int64, domain, workflowID, runID string, cause types.CancelExternalWorkflowExecutionFailedCause) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeRequestCancelExternalWorkflowExecutionFailed) event.RequestCancelExternalWorkflowExecutionFailedEventAttributes = &types.RequestCancelExternalWorkflowExecutionFailedEventAttributes{ DecisionTaskCompletedEventID: decisionTaskCompletedEventID, InitiatedEventID: initiatedEventID, Domain: domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Cause: cause.Ptr(), } return b.addEventToHistory(event) } // AddExternalWorkflowExecutionCancelRequested adds ExternalWorkflowExecutionCancelRequested event to history func (b *HistoryBuilder) AddExternalWorkflowExecutionCancelRequested(initiatedEventID int64, domain, workflowID, runID string) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeExternalWorkflowExecutionCancelRequested) event.ExternalWorkflowExecutionCancelRequestedEventAttributes = &types.ExternalWorkflowExecutionCancelRequestedEventAttributes{ InitiatedEventID: initiatedEventID, Domain: domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, } return b.addEventToHistory(event) } // AddSignalExternalWorkflowExecutionInitiatedEvent adds SignalExternalWorkflowExecutionInitiated event to history func (b *HistoryBuilder) AddSignalExternalWorkflowExecutionInitiatedEvent(decisionTaskCompletedEventID int64, attributes *types.SignalExternalWorkflowExecutionDecisionAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeSignalExternalWorkflowExecutionInitiated) event.SignalExternalWorkflowExecutionInitiatedEventAttributes = &types.SignalExternalWorkflowExecutionInitiatedEventAttributes{ DecisionTaskCompletedEventID: decisionTaskCompletedEventID, Domain: attributes.Domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: attributes.Execution.WorkflowID, RunID: attributes.Execution.RunID, }, SignalName: attributes.GetSignalName(), Input: attributes.Input, Control: attributes.Control, ChildWorkflowOnly: attributes.ChildWorkflowOnly, } return b.addEventToHistory(event) } // AddUpsertWorkflowSearchAttributesEvent adds UpsertWorkflowSearchAttributes event to history func (b *HistoryBuilder) AddUpsertWorkflowSearchAttributesEvent( decisionTaskCompletedEventID int64, attributes *types.UpsertWorkflowSearchAttributesDecisionAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeUpsertWorkflowSearchAttributes) event.UpsertWorkflowSearchAttributesEventAttributes = &types.UpsertWorkflowSearchAttributesEventAttributes{ DecisionTaskCompletedEventID: decisionTaskCompletedEventID, SearchAttributes: attributes.GetSearchAttributes(), } return b.addEventToHistory(event) } // AddSignalExternalWorkflowExecutionFailedEvent adds SignalExternalWorkflowExecutionFailed event to history func (b *HistoryBuilder) AddSignalExternalWorkflowExecutionFailedEvent(decisionTaskCompletedEventID, initiatedEventID int64, domain, workflowID, runID string, control []byte, cause types.SignalExternalWorkflowExecutionFailedCause) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeSignalExternalWorkflowExecutionFailed) event.SignalExternalWorkflowExecutionFailedEventAttributes = &types.SignalExternalWorkflowExecutionFailedEventAttributes{ DecisionTaskCompletedEventID: decisionTaskCompletedEventID, InitiatedEventID: initiatedEventID, Domain: domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Cause: cause.Ptr(), Control: control, } return b.addEventToHistory(event) } // AddExternalWorkflowExecutionSignaled adds ExternalWorkflowExecutionSignaled event to history func (b *HistoryBuilder) AddExternalWorkflowExecutionSignaled(initiatedEventID int64, domain, workflowID, runID string, control []byte) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeExternalWorkflowExecutionSignaled) event.ExternalWorkflowExecutionSignaledEventAttributes = &types.ExternalWorkflowExecutionSignaledEventAttributes{ InitiatedEventID: initiatedEventID, Domain: domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Control: control, } return b.addEventToHistory(event) } // AddMarkerRecordedEvent adds MarkerRecorded event to history func (b *HistoryBuilder) AddMarkerRecordedEvent(decisionCompletedEventID int64, attributes *types.RecordMarkerDecisionAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeMarkerRecorded) event.MarkerRecordedEventAttributes = &types.MarkerRecordedEventAttributes{ MarkerName: attributes.MarkerName, Details: attributes.Details, DecisionTaskCompletedEventID: decisionCompletedEventID, Header: attributes.Header, } return b.addEventToHistory(event) } // AddWorkflowExecutionSignaledEvent adds WorkflowExecutionSignaled event to history func (b *HistoryBuilder) AddWorkflowExecutionSignaledEvent( signalName string, input []byte, identity string, requestID string, ) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeWorkflowExecutionSignaled) event.WorkflowExecutionSignaledEventAttributes = &types.WorkflowExecutionSignaledEventAttributes{ SignalName: signalName, Input: input, Identity: identity, RequestID: requestID, } return b.addEventToHistory(event) } // AddStartChildWorkflowExecutionInitiatedEvent adds ChildWorkflowExecutionInitiated event to history func (b *HistoryBuilder) AddStartChildWorkflowExecutionInitiatedEvent( decisionCompletedEventID int64, attributes *types.StartChildWorkflowExecutionDecisionAttributes, targetDomainName string, ) *types.HistoryEvent { domain := attributes.Domain if domain == "" { domain = targetDomainName } event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeStartChildWorkflowExecutionInitiated) event.StartChildWorkflowExecutionInitiatedEventAttributes = &types.StartChildWorkflowExecutionInitiatedEventAttributes{ Domain: domain, WorkflowID: attributes.WorkflowID, WorkflowType: attributes.WorkflowType, TaskList: attributes.TaskList, Header: attributes.Header, Input: attributes.Input, ExecutionStartToCloseTimeoutSeconds: attributes.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: attributes.TaskStartToCloseTimeoutSeconds, Control: attributes.Control, DecisionTaskCompletedEventID: decisionCompletedEventID, WorkflowIDReusePolicy: attributes.WorkflowIDReusePolicy, RetryPolicy: attributes.RetryPolicy, CronSchedule: attributes.CronSchedule, Memo: attributes.Memo, SearchAttributes: attributes.SearchAttributes, ParentClosePolicy: attributes.GetParentClosePolicy().Ptr(), CronOverlapPolicy: attributes.CronOverlapPolicy, ActiveClusterSelectionPolicy: attributes.ActiveClusterSelectionPolicy, } return b.addEventToHistory(event) } // AddChildWorkflowExecutionStartedEvent adds ChildWorkflowExecutionStarted event to history func (b *HistoryBuilder) AddChildWorkflowExecutionStartedEvent( domain string, execution *types.WorkflowExecution, workflowType *types.WorkflowType, initiatedID int64, header *types.Header, ) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionStarted) event.ChildWorkflowExecutionStartedEventAttributes = &types.ChildWorkflowExecutionStartedEventAttributes{ Domain: domain, WorkflowExecution: execution, WorkflowType: workflowType, InitiatedEventID: initiatedID, Header: header, } return b.addEventToHistory(event) } // AddStartChildWorkflowExecutionFailedEvent adds ChildWorkflowExecutionFailed event to history func (b *HistoryBuilder) AddStartChildWorkflowExecutionFailedEvent(initiatedID int64, cause types.ChildWorkflowExecutionFailedCause, initiatedEventAttributes *types.StartChildWorkflowExecutionInitiatedEventAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeStartChildWorkflowExecutionFailed) event.StartChildWorkflowExecutionFailedEventAttributes = &types.StartChildWorkflowExecutionFailedEventAttributes{ Domain: initiatedEventAttributes.Domain, WorkflowID: initiatedEventAttributes.WorkflowID, WorkflowType: initiatedEventAttributes.WorkflowType, InitiatedEventID: initiatedID, DecisionTaskCompletedEventID: initiatedEventAttributes.DecisionTaskCompletedEventID, Control: initiatedEventAttributes.Control, Cause: cause.Ptr(), } return b.addEventToHistory(event) } // AddChildWorkflowExecutionCompletedEvent adds ChildWorkflowExecutionCompleted event to history func (b *HistoryBuilder) AddChildWorkflowExecutionCompletedEvent(domain string, execution *types.WorkflowExecution, workflowType *types.WorkflowType, initiatedID, startedID int64, completedAttributes *types.WorkflowExecutionCompletedEventAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionCompleted) event.ChildWorkflowExecutionCompletedEventAttributes = &types.ChildWorkflowExecutionCompletedEventAttributes{ Domain: domain, WorkflowExecution: execution, WorkflowType: workflowType, InitiatedEventID: initiatedID, StartedEventID: startedID, Result: completedAttributes.Result, } return b.addEventToHistory(event) } // AddChildWorkflowExecutionFailedEvent adds ChildWorkflowExecutionFailed event to history func (b *HistoryBuilder) AddChildWorkflowExecutionFailedEvent(domain string, execution *types.WorkflowExecution, workflowType *types.WorkflowType, initiatedID, startedID int64, failedAttributes *types.WorkflowExecutionFailedEventAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionFailed) event.ChildWorkflowExecutionFailedEventAttributes = &types.ChildWorkflowExecutionFailedEventAttributes{ Domain: domain, WorkflowExecution: execution, WorkflowType: workflowType, InitiatedEventID: initiatedID, StartedEventID: startedID, Reason: common.StringPtr(common.StringDefault(failedAttributes.Reason)), Details: failedAttributes.Details, } return b.addEventToHistory(event) } // AddChildWorkflowExecutionCanceledEvent adds ChildWorkflowExecutionCanceled event to history func (b *HistoryBuilder) AddChildWorkflowExecutionCanceledEvent(domain string, execution *types.WorkflowExecution, workflowType *types.WorkflowType, initiatedID, startedID int64, canceledAttributes *types.WorkflowExecutionCanceledEventAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionCanceled) event.ChildWorkflowExecutionCanceledEventAttributes = &types.ChildWorkflowExecutionCanceledEventAttributes{ Domain: domain, WorkflowExecution: execution, WorkflowType: workflowType, InitiatedEventID: initiatedID, StartedEventID: startedID, Details: canceledAttributes.Details, } return b.addEventToHistory(event) } // AddChildWorkflowExecutionTerminatedEvent adds ChildWorkflowExecutionTerminated event to history func (b *HistoryBuilder) AddChildWorkflowExecutionTerminatedEvent(domain string, execution *types.WorkflowExecution, workflowType *types.WorkflowType, initiatedID, startedID int64, terminatedAttributes *types.WorkflowExecutionTerminatedEventAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionTerminated) event.ChildWorkflowExecutionTerminatedEventAttributes = &types.ChildWorkflowExecutionTerminatedEventAttributes{ Domain: domain, WorkflowExecution: execution, WorkflowType: workflowType, InitiatedEventID: initiatedID, StartedEventID: startedID, } return b.addEventToHistory(event) } // AddChildWorkflowExecutionTimedOutEvent adds ChildWorkflowExecutionTimedOut event to history func (b *HistoryBuilder) AddChildWorkflowExecutionTimedOutEvent(domain string, execution *types.WorkflowExecution, workflowType *types.WorkflowType, initiatedID, startedID int64, timedOutAttributes *types.WorkflowExecutionTimedOutEventAttributes) *types.HistoryEvent { event := b.msBuilder.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionTimedOut) event.ChildWorkflowExecutionTimedOutEventAttributes = &types.ChildWorkflowExecutionTimedOutEventAttributes{ Domain: domain, TimeoutType: timedOutAttributes.TimeoutType, WorkflowExecution: execution, WorkflowType: workflowType, InitiatedEventID: initiatedID, StartedEventID: startedID, } return b.addEventToHistory(event) } func (b *HistoryBuilder) addEventToHistory(event *types.HistoryEvent) *types.HistoryEvent { b.history = append(b.history, event) return event } func (b *HistoryBuilder) addTransientEvent(event *types.HistoryEvent) *types.HistoryEvent { b.transientHistory = append(b.transientHistory, event) return event } func newDecisionTaskScheduledEventWithInfo(eventID, timestamp int64, taskList string, startToCloseTimeoutSeconds int32, attempt int64) *types.HistoryEvent { event := createNewHistoryEvent(eventID, types.EventTypeDecisionTaskScheduled, timestamp) event.DecisionTaskScheduledEventAttributes = getDecisionTaskScheduledEventAttributes(taskList, startToCloseTimeoutSeconds, attempt) return event } func newDecisionTaskStartedEventWithInfo(eventID, timestamp int64, scheduledEventID int64, requestID string, identity string) *types.HistoryEvent { event := createNewHistoryEvent(eventID, types.EventTypeDecisionTaskStarted, timestamp) event.DecisionTaskStartedEventAttributes = getDecisionTaskStartedEventAttributes(scheduledEventID, requestID, identity) return event } func createNewHistoryEvent(eventID int64, eventType types.EventType, timestamp int64) *types.HistoryEvent { return &types.HistoryEvent{ ID: eventID, Timestamp: common.Int64Ptr(timestamp), EventType: eventType.Ptr(), } } func getDecisionTaskScheduledEventAttributes(taskList string, startToCloseTimeoutSeconds int32, attempt int64) *types.DecisionTaskScheduledEventAttributes { return &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{ Name: taskList, }, StartToCloseTimeoutSeconds: common.Int32Ptr(startToCloseTimeoutSeconds), Attempt: attempt, } } func getDecisionTaskStartedEventAttributes(scheduledEventID int64, requestID string, identity string) *types.DecisionTaskStartedEventAttributes { return &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: scheduledEventID, Identity: identity, RequestID: requestID, } } // GetHistory gets workflow history stored inside history builder func (b *HistoryBuilder) GetHistory() *types.History { history := types.History{Events: b.history} return &history } ================================================ FILE: service/history/execution/history_builder_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/shard" ) type ( historyBuilderSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockEventsCache *events.MockCache mockDomainCache *cache.MockDomainCache domainID string domainName string domainEntry *cache.DomainCacheEntry targetDomainID string targetDomainName string targetDomainEntry *cache.DomainCacheEntry msBuilder MutableState builder *HistoryBuilder logger log.Logger } ) func TestHistoryBuilderSuite(t *testing.T) { s := new(historyBuilderSuite) suite.Run(t, s) } func (s *historyBuilderSuite) SetupTest() { // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.logger = log.NewNoop() s.domainEntry = constants.TestLocalDomainEntry s.domainID = s.domainEntry.GetInfo().ID s.domainName = s.domainEntry.GetInfo().Name s.targetDomainEntry = constants.TestGlobalTargetDomainEntry s.targetDomainID = s.targetDomainEntry.GetInfo().ID s.targetDomainName = s.targetDomainEntry.GetInfo().Name s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockEventsCache = s.mockShard.MockEventsCache s.mockDomainCache.EXPECT().GetDomainID(s.domainName).Return(s.domainID, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(s.domainID).Return(s.domainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(s.targetDomainName).Return(s.targetDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainID(s.targetDomainName).Return(s.targetDomainID, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(s.targetDomainID).Return(s.targetDomainEntry, nil).AnyTimes() s.mockEventsCache.EXPECT().PutEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() s.msBuilder = NewMutableStateBuilder(s.mockShard, s.logger, s.domainEntry) s.builder = NewHistoryBuilder(s.msBuilder) } func (s *historyBuilderSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *historyBuilderSuite) TestHistoryBuilderDynamicSuccess() { id := "dynamic-historybuilder-success-test-workflow-id" rid := "dynamic-historybuilder-success-test-run-id" wt := "dynamic-historybuilder-success-type" tl := "dynamic-historybuilder-success-tasklist" identity := "dynamic-historybuilder-success-worker" input := []byte("dynamic-historybuilder-success-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) di := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di, 2, tl, taskTimeout) s.Equal(int64(3), s.getNextEventID()) di0, decisionRunning0 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning0) s.Equal(commonconstants.EmptyEventID, di0.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionStartedEvent := s.addDecisionTaskStartedEvent(2, tl, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent, 3, 2, identity) s.Equal(int64(4), s.getNextEventID()) di1, decisionRunning1 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning1) s.NotNil(di1) decisionStartedID1 := di1.StartedID s.Equal(int64(3), decisionStartedID1) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionContext := []byte("dynamic-historybuilder-success-context") decisionCompletedEvent := s.addDecisionTaskCompletedEvent(2, 3, decisionContext, identity) s.validateDecisionTaskCompletedEvent(decisionCompletedEvent, 4, 2, 3, decisionContext, identity) s.Equal(int64(5), s.getNextEventID()) _, decisionRunning2 := s.msBuilder.GetDecisionInfo(2) s.False(decisionRunning2) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activityTaskList := "dynamic-historybuilder-success-activity-tasklist" activityTimeout := int32(60) queueTimeout := int32(20) hearbeatTimeout := int32(10) activity1ID := "activity1" activity1Type := "dynamic-historybuilder-success-activity1-type" activity1Domain := "" activity1Input := []byte("dynamic-historybuilder-success-activity1-input") activity1Result := []byte("dynamic-historybuilder-success-activity1-result") activity1ScheduledEvent, _, activityDispatchInfo := s.addActivityTaskScheduledEvent(4, activity1ID, activity1Type, activity1Domain, activityTaskList, activity1Input, activityTimeout, queueTimeout, hearbeatTimeout, nil, false) s.validateActivityTaskScheduledEvent(activity1ScheduledEvent, 5, 4, activity1ID, activity1Type, activity1Domain, activityTaskList, activity1Input, activityTimeout, queueTimeout, hearbeatTimeout, activityDispatchInfo, false) s.Equal(int64(6), s.getNextEventID()) ai0, activity1Running0 := s.msBuilder.GetActivityInfo(5) s.True(activity1Running0) s.Equal(commonconstants.EmptyEventID, ai0.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity2ID := "activity2" activity2Type := "dynamic-historybuilder-success-activity2-type" activity2Domain := "" activity2Input := []byte("dynamic-historybuilder-success-activity2-input") activity2Reason := "dynamic-historybuilder-success-activity2-failed" activity2Details := []byte("dynamic-historybuilder-success-activity2-callstack") activity2ScheduledEvent, _, activityDispatchInfo := s.addActivityTaskScheduledEvent(4, activity2ID, activity2Type, activity2Domain, activityTaskList, activity2Input, activityTimeout, queueTimeout, hearbeatTimeout, nil, false) s.validateActivityTaskScheduledEvent(activity2ScheduledEvent, 6, 4, activity2ID, activity2Type, activity2Domain, activityTaskList, activity2Input, activityTimeout, queueTimeout, hearbeatTimeout, activityDispatchInfo, false) s.Equal(int64(7), s.getNextEventID()) ai2, activity2Running0 := s.msBuilder.GetActivityInfo(6) s.True(activity2Running0) s.Equal(commonconstants.EmptyEventID, ai2.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity3ID := "activity3" activity3Type := "dynamic-historybuilder-success-activity3-type" activity3Domain := s.targetDomainName activity3Input := []byte("dynamic-historybuilder-success-activity3-input") activity3RetryPolicy := &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 3, MaximumIntervalInSeconds: 1, NonRetriableErrorReasons: []string{"bad-bug"}, BackoffCoefficient: 1, ExpirationIntervalInSeconds: 100, } activity3ScheduledEvent, _, activityDispatchInfo := s.addActivityTaskScheduledEvent(4, activity3ID, activity3Type, activity3Domain, activityTaskList, activity3Input, activityTimeout, queueTimeout, hearbeatTimeout, activity3RetryPolicy, false) s.validateActivityTaskScheduledEvent(activity3ScheduledEvent, 7, 4, activity3ID, activity3Type, activity3Domain, activityTaskList, activity3Input, activityTimeout, queueTimeout, hearbeatTimeout, activityDispatchInfo, false) s.Equal(int64(8), s.getNextEventID()) ai2, activity3Running0 := s.msBuilder.GetActivityInfo(6) s.True(activity3Running0) s.Equal(commonconstants.EmptyEventID, ai2.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity4ID := "activity4" activity4Type := "dynamic-historybuilder-success-activity4-type" activity4Domain := s.targetDomainName activity4Input := []byte("dynamic-historybuilder-success-activity4-input") activity4Result := []byte("dynamic-historybuilder-success-activity4-result") activity4ScheduledEvent, _, activityDispatchInfo := s.addActivityTaskScheduledEvent(4, activity4ID, activity4Type, activity4Domain, activityTaskList, activity4Input, activityTimeout, queueTimeout, hearbeatTimeout, nil, true) s.validateActivityTaskScheduledEvent(activity4ScheduledEvent, 8, 4, activity4ID, activity4Type, activity4Domain, activityTaskList, activity4Input, activityTimeout, queueTimeout, hearbeatTimeout, activityDispatchInfo, true) s.Equal(int64(9), s.getNextEventID()) ai4, activity4Running0 := s.msBuilder.GetActivityInfo(8) s.True(activity4Running0) s.Equal(commonconstants.EmptyEventID, ai4.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity5ID := "activity5" activity5Type := "dynamic-historybuilder-success-activity5-type" activity5Domain := s.targetDomainName activity5Input := []byte("dynamic-historybuilder-success-activity5-input") activity5RetryPolicy := &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 3, MaximumIntervalInSeconds: 1, NonRetriableErrorReasons: []string{"bad-bug"}, BackoffCoefficient: 1, ExpirationIntervalInSeconds: 100, } activity5ScheduledEvent, _, activityDispatchInfo := s.addActivityTaskScheduledEvent(4, activity5ID, activity5Type, activity5Domain, activityTaskList, activity5Input, activityTimeout, queueTimeout, hearbeatTimeout, activity5RetryPolicy, true) s.validateActivityTaskScheduledEvent(activity5ScheduledEvent, 9, 4, activity5ID, activity5Type, activity5Domain, activityTaskList, activity5Input, activityTimeout, queueTimeout, hearbeatTimeout, activityDispatchInfo, true) s.Equal(int64(10), s.getNextEventID()) ai5, activity5Running0 := s.msBuilder.GetActivityInfo(9) s.True(activity5Running0) s.Equal(commonconstants.EmptyEventID, ai5.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activityStartedEvent := s.addActivityTaskStartedEvent(5, activityTaskList, identity) s.validateActivityTaskStartedEvent(activityStartedEvent, commonconstants.BufferedEventID, 5, identity, 0, "", nil) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateActivityTaskStartedEvent(activityStartedEvent, 10, 5, identity, 0, "", nil) s.Equal(int64(11), s.getNextEventID()) ai3, activity1Running1 := s.msBuilder.GetActivityInfo(5) s.True(activity1Running1) s.Equal(int64(10), ai3.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activityCompletedEvent := s.addActivityTaskCompletedEvent(5, 10, activity1Result, identity) s.validateActivityTaskCompletedEvent(activityCompletedEvent, commonconstants.BufferedEventID, 5, 10, activity1Result, identity) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateActivityTaskCompletedEvent(activityCompletedEvent, 11, 5, 10, activity1Result, identity) s.Equal(int64(12), s.getNextEventID()) _, activity1Running2 := s.msBuilder.GetActivityInfo(5) s.False(activity1Running2) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) di2 := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di2, 12, tl, taskTimeout) s.Equal(int64(13), s.getNextEventID()) di3, decisionRunning3 := s.msBuilder.GetDecisionInfo(12) s.True(decisionRunning3) s.Equal(commonconstants.EmptyEventID, di3.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity2StartedEvent := s.addActivityTaskStartedEvent(6, activityTaskList, identity) s.validateActivityTaskStartedEvent(activity2StartedEvent, commonconstants.BufferedEventID, 6, identity, 0, "", nil) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateActivityTaskStartedEvent(activity2StartedEvent, 13, 6, identity, 0, "", nil) s.Equal(int64(14), s.getNextEventID()) ai4, activity2Running1 := s.msBuilder.GetActivityInfo(6) s.True(activity2Running1) s.Equal(int64(13), ai4.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity2FailedEvent := s.addActivityTaskFailedEvent(6, 13, activity2Reason, activity2Details, identity) s.validateActivityTaskFailedEvent(activity2FailedEvent, commonconstants.BufferedEventID, 6, 13, activity2Reason, activity2Details, identity) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateActivityTaskFailedEvent(activity2FailedEvent, 14, 6, 13, activity2Reason, activity2Details, identity) s.Equal(int64(15), s.getNextEventID()) _, activity2Running3 := s.msBuilder.GetActivityInfo(6) s.False(activity2Running3) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity3StartedEvent := s.addActivityTaskStartedEvent(7, activityTaskList, identity) s.validateTransientActivityTaskStartedEvent(activity3StartedEvent, commonconstants.TransientEventID, 7, identity) s.Equal(int64(15), s.getNextEventID()) ai5, activity3Running1 := s.msBuilder.GetActivityInfo(7) s.True(activity3Running1) s.Equal(commonconstants.TransientEventID, ai5.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity3Reason := "dynamic-historybuilder-success-activity3-failed" activity3Details := []byte("dynamic-historybuilder-success-activity3-callstack") s.msBuilder.RetryActivity(ai5, activity3Reason, activity3Details) ai6, activity3Running2 := s.msBuilder.GetActivityInfo(7) s.Equal(activity3Reason, ai6.LastFailureReason) s.Equal(activity3Details, ai6.LastFailureDetails) s.True(activity3Running2) activity3StartedEvent2 := s.addActivityTaskStartedEvent(7, activityTaskList, identity) s.validateTransientActivityTaskStartedEvent(activity3StartedEvent2, commonconstants.TransientEventID, 7, identity) s.Equal(int64(15), s.getNextEventID()) ai7, activity3Running3 := s.msBuilder.GetActivityInfo(7) s.True(activity3Running3) s.Equal(commonconstants.TransientEventID, ai7.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity3Result := []byte("dynamic-historybuilder-success-activity1-result") activity3CompletedEvent := s.addActivityTaskCompletedEvent(7, commonconstants.TransientEventID, activity3Result, identity) s.validateActivityTaskCompletedEvent(activity3CompletedEvent, commonconstants.BufferedEventID, 7, commonconstants.TransientEventID, activity3Result, identity) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateActivityTaskCompletedEvent(activity3CompletedEvent, 16, 7, 15, activity3Result, identity) s.Equal(int64(17), s.getNextEventID()) ai8, activity3Running4 := s.msBuilder.GetActivityInfo(7) s.Nil(ai8) s.False(activity3Running4) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity4StartedEvent := s.addActivityTaskStartedEvent(8, activityTaskList, identity) s.validateActivityTaskStartedEvent(activity4StartedEvent, commonconstants.BufferedEventID, 8, identity, 0, "", nil) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateActivityTaskStartedEvent(activity4StartedEvent, 17, 8, identity, 0, "", nil) s.Equal(int64(18), s.getNextEventID()) ai4, activity4Running1 := s.msBuilder.GetActivityInfo(8) s.True(activity4Running1) s.Equal(int64(17), ai4.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity4CompletedEvent := s.addActivityTaskCompletedEvent(8, 17, activity4Result, identity) s.validateActivityTaskCompletedEvent(activity4CompletedEvent, commonconstants.BufferedEventID, 8, 17, activity4Result, identity) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateActivityTaskCompletedEvent(activity4CompletedEvent, 18, 8, 17, activity4Result, identity) s.Equal(int64(19), s.getNextEventID()) _, activity4Running2 := s.msBuilder.GetActivityInfo(8) s.False(activity4Running2) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity5StartedEvent := s.addActivityTaskStartedEvent(9, activityTaskList, identity) s.validateTransientActivityTaskStartedEvent(activity5StartedEvent, commonconstants.TransientEventID, 9, identity) s.Equal(int64(19), s.getNextEventID()) ai5, activity5Running1 := s.msBuilder.GetActivityInfo(9) s.True(activity5Running1) s.Equal(commonconstants.TransientEventID, ai5.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity5Reason := "dynamic-historybuilder-success-activity5-failed" activity5Details := []byte("dynamic-historybuilder-success-activity5-callstack") s.msBuilder.RetryActivity(ai5, activity5Reason, activity5Details) ai6, activity5Running2 := s.msBuilder.GetActivityInfo(9) s.Equal(activity5Reason, ai6.LastFailureReason) s.Equal(activity5Details, ai6.LastFailureDetails) s.True(activity5Running2) activity5StartedEvent2 := s.addActivityTaskStartedEvent(9, activityTaskList, identity) s.validateTransientActivityTaskStartedEvent(activity5StartedEvent2, commonconstants.TransientEventID, 9, identity) s.Equal(int64(19), s.getNextEventID()) ai7, activity5Running3 := s.msBuilder.GetActivityInfo(9) s.True(activity5Running3) s.Equal(commonconstants.TransientEventID, ai7.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activity5Result := []byte("dynamic-historybuilder-success-activity5-result") activity5CompletedEvent := s.addActivityTaskCompletedEvent(9, commonconstants.TransientEventID, activity5Result, identity) s.validateActivityTaskCompletedEvent(activity5CompletedEvent, commonconstants.BufferedEventID, 9, commonconstants.TransientEventID, activity5Result, identity) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateActivityTaskCompletedEvent(activity5CompletedEvent, 20, 9, 19, activity5Result, identity) s.Equal(int64(21), s.getNextEventID()) ai8, activity5Running4 := s.msBuilder.GetActivityInfo(9) s.Nil(ai8) s.False(activity5Running4) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) // Verify the last ActivityTaskStartedEvent which should show the error from the first attempt historyEvents := s.msBuilder.GetHistoryBuilder().GetHistory().GetEvents() s.Len(historyEvents, 20) s.validateActivityTaskStartedEvent(historyEvents[14], 15, 7, identity, 1, activity3Reason, activity3Details) markerDetails := []byte("dynamic-historybuilder-success-marker-details") markerHeaderField1 := []byte("dynamic-historybuilder-success-marker-header1") markerHeaderField2 := []byte("dynamic-historybuilder-success-marker-header2") markerHeader := map[string][]byte{ "name1": markerHeaderField1, "name2": markerHeaderField2, } markerEvent := s.addMarkerRecordedEvent(4, "testMarker", markerDetails, &markerHeader) s.validateMarkerRecordedEvent(markerEvent, 21, 4, "testMarker", markerDetails, &markerHeader) s.Nil(s.msBuilder.FlushBufferedEvents()) s.Equal(int64(22), s.getNextEventID()) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) } func (s *historyBuilderSuite) TestHistoryBuilderWorkflowStartFailures() { id := "historybuilder-workflowstart-failures-test-workflow-id" rid := "historybuilder-workflowstart-failures-test-run-id" wt := "historybuilder-workflowstart-failures-type" tl := "historybuilder-workflowstart-failures-tasklist" identity := "historybuilder-workflowstart-failures-worker" input := []byte("historybuilder-workflowstart-failures-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) di := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di, 2, tl, taskTimeout) s.Equal(int64(3), s.getNextEventID()) di0, decisionRunning0 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning0) s.Equal(commonconstants.EmptyEventID, di0.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) _, err := s.msBuilder.AddWorkflowExecutionStartedEvent( we, &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: s.domainID, StartRequest: &types.StartWorkflowExecutionRequest{ WorkflowID: we.WorkflowID, WorkflowType: &types.WorkflowType{Name: wt}, TaskList: &types.TaskList{Name: tl}, Input: input, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(execTimeout), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(taskTimeout), Identity: identity, }, }) s.NotNil(err) s.Equal(int64(3), s.getNextEventID(), s.printHistory()) di1, decisionRunning1 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning1) s.Equal(commonconstants.EmptyEventID, di1.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) } func (s *historyBuilderSuite) TestHistoryBuilderDecisionScheduledFailures() { id := "historybuilder-decisionscheduled-failures-test-workflow-id" rid := "historybuilder-decisionscheduled-failures-test-run-id" wt := "historybuilder-decisionscheduled-failures-type" tl := "historybuilder-decisionscheduled-failures-tasklist" identity := "historybuilder-decisionscheduled-failures-worker" input := []byte("historybuilder-decisionscheduled-failures-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) di := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di, 2, tl, taskTimeout) s.Equal(int64(3), s.getNextEventID()) di0, decisionRunning0 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning0) s.Equal(commonconstants.EmptyEventID, di0.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) _, err := s.msBuilder.AddDecisionTaskScheduledEvent(false) s.NotNil(err) s.Equal(int64(3), s.getNextEventID()) di1, decisionRunning1 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning1) s.Equal(commonconstants.EmptyEventID, di1.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) } func (s *historyBuilderSuite) TestHistoryBuilderDecisionScheduledTimedout() { id := "historybuilder-decisionscheduled-failures-test-workflow-id" rid := "historybuilder-decisionscheduled-failures-test-run-id" wt := "historybuilder-decisionscheduled-failures-type" tl := "historybuilder-decisionscheduled-failures-tasklist" identity := "historybuilder-decisionscheduled-failures-worker" input := []byte("historybuilder-decisionscheduled-failures-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) di := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di, 2, tl, taskTimeout) s.Equal(int64(3), s.getNextEventID()) di0, decisionRunning0 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning0) s.Equal(commonconstants.EmptyEventID, di0.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionTimeoutEvent := s.addDecisionTaskTimedOutEvent(2) s.NotNil(decisionTimeoutEvent) s.validateDecisionTaskTimedoutEvent(decisionTimeoutEvent, 3, 2, 0, types.TimeoutTypeScheduleToStart, "", "", commonconstants.EmptyVersion, "", types.DecisionTaskTimedOutCauseTimeout) s.Equal(int64(4), s.getNextEventID()) di2 := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di2, 4, tl, taskTimeout) s.Equal(int64(4), s.getNextEventID()) di1, decisionRunning1 := s.msBuilder.GetDecisionInfo(4) s.True(decisionRunning1) s.Equal(commonconstants.EmptyEventID, di1.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionTimeoutEvent = s.addDecisionTaskTimedOutEvent(4) s.Nil(decisionTimeoutEvent) s.Equal(int64(4), s.getNextEventID()) } func (s *historyBuilderSuite) TestHistoryBuilderDecisionStartedTimedout() { id := "historybuilder-decisionscheduled-failures-test-workflow-id" rid := "historybuilder-decisionscheduled-failures-test-run-id" wt := "historybuilder-decisionscheduled-failures-type" tl := "historybuilder-decisionscheduled-failures-tasklist" identity := "historybuilder-decisionscheduled-failures-worker" input := []byte("historybuilder-decisionscheduled-failures-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) di := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di, 2, tl, taskTimeout) s.Equal(int64(3), s.getNextEventID()) di0, decisionRunning0 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning0) s.Equal(commonconstants.EmptyEventID, di0.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionStartedEvent := s.addDecisionTaskStartedEvent(2, tl, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent, 3, 2, identity) s.Equal(int64(4), s.getNextEventID()) decisionInfo, decisionRunning := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(int64(3), decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionTimeoutEvent := s.addDecisionTaskStartedTimedOutEvent(2, 3) s.NotNil(decisionTimeoutEvent) s.validateDecisionTaskTimedoutEvent(decisionTimeoutEvent, 4, 2, 3, types.TimeoutTypeStartToClose, "", "", commonconstants.EmptyVersion, "", types.DecisionTaskTimedOutCauseTimeout) s.Equal(int64(5), s.getNextEventID()) di2 := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di2, 5, tl, taskTimeout) s.Equal(int64(5), s.getNextEventID()) di1, decisionRunning1 := s.msBuilder.GetDecisionInfo(5) s.True(decisionRunning1) s.Equal(int64(1), di1.Attempt) s.Equal(commonconstants.EmptyEventID, di1.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionStartedEvent = s.addDecisionTaskStartedEvent(5, tl, identity) s.Nil(decisionStartedEvent) s.Equal(int64(5), s.getNextEventID()) decisionTimeoutEvent = s.addDecisionTaskStartedTimedOutEvent(5, 6) s.Equal(int64(5), s.getNextEventID()) } func (s *historyBuilderSuite) TestHistoryBuilderDecisionStartedFailures() { id := "historybuilder-decisionstarted-failures-test-workflow-id" rid := "historybuilder-decisionstarted-failures-test-run-id" wt := "historybuilder-decisionstarted-failures-type" tl := "historybuilder-decisionstarted-failures-tasklist" identity := "historybuilder-decisionstarted-failures-worker" input := []byte("historybuilder-decisionstarted-failures-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) _, _, err := s.msBuilder.AddDecisionTaskStartedEvent(2, uuid.New(), &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{Name: tl}, Identity: identity, }) s.NotNil(err) s.Equal(int64(2), s.getNextEventID()) _, decisionRunning1 := s.msBuilder.GetDecisionInfo(2) s.False(decisionRunning1) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) di := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di, 2, tl, taskTimeout) s.Equal(int64(3), s.getNextEventID()) di0, decisionRunning0 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning0) s.Equal(commonconstants.EmptyEventID, di0.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) _, _, err = s.msBuilder.AddDecisionTaskStartedEvent(100, uuid.New(), &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{Name: tl}, Identity: identity, }) s.NotNil(err) s.Equal(int64(3), s.getNextEventID()) di2, decisionRunning2 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning2) s.Equal(commonconstants.EmptyEventID, di2.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionStartedEvent2 := s.addDecisionTaskStartedEvent(2, tl, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent2, 3, 2, identity) s.Equal(int64(4), s.getNextEventID()) di3, decisionRunning3 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning3) s.Equal(int64(3), di3.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) } func (s *historyBuilderSuite) TestHistoryBuilderFlushBufferedEvents() { id := "flush-buffered-events-test-workflow-id" rid := "flush-buffered-events-test-run-id" wt := "flush-buffered-events-type" tl := "flush-buffered-events-tasklist" identity := "flush-buffered-events-worker" input := []byte("flush-buffered-events-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } // 1 execution started workflowStartedEvent := s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) // 2 decision scheduled di := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di, 2, tl, taskTimeout) s.Equal(int64(3), s.getNextEventID()) di0, decisionRunning0 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning0) s.Equal(commonconstants.EmptyEventID, di0.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) // 3 decision started decisionStartedEvent := s.addDecisionTaskStartedEvent(2, tl, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent, 3, 2, identity) s.Equal(int64(4), s.getNextEventID()) di1, decisionRunning1 := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning1) s.NotNil(di1) decisionStartedID1 := di1.StartedID s.Equal(int64(3), decisionStartedID1) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) // 4 decision completed decisionContext := []byte("flush-buffered-events-context") decisionCompletedEvent := s.addDecisionTaskCompletedEvent(2, 3, decisionContext, identity) s.validateDecisionTaskCompletedEvent(decisionCompletedEvent, 4, 2, 3, decisionContext, identity) s.Equal(int64(5), s.getNextEventID()) _, decisionRunning2 := s.msBuilder.GetDecisionInfo(2) s.False(decisionRunning2) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) activityTaskList := "flush-buffered-events-activity-tasklist" activityTimeout := int32(60) queueTimeout := int32(20) hearbeatTimeout := int32(10) // 5 activity1 scheduled activity1ID := "activity1" activity1Type := "flush-buffered-events-activity1-type" activity1Domain := "" activity1Input := []byte("flush-buffered-events-activity1-input") activity1Result := []byte("flush-buffered-events-activity1-result") activity1ScheduledEvent, _, activityDispatchInfo := s.addActivityTaskScheduledEvent(4, activity1ID, activity1Type, activity1Domain, activityTaskList, activity1Input, activityTimeout, queueTimeout, hearbeatTimeout, nil, false) s.validateActivityTaskScheduledEvent(activity1ScheduledEvent, 5, 4, activity1ID, activity1Type, activity1Domain, activityTaskList, activity1Input, activityTimeout, queueTimeout, hearbeatTimeout, activityDispatchInfo, false) s.Equal(int64(6), s.getNextEventID()) ai0, activity1Running0 := s.msBuilder.GetActivityInfo(5) s.True(activity1Running0) s.Equal(commonconstants.EmptyEventID, ai0.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) // 6 activity 2 scheduled activity2ID := "activity2" activity2Type := "flush-buffered-events-activity2-type" activity2Domain := s.targetDomainName activity2Input := []byte("flush-buffered-events-activity2-input") activity2ScheduledEvent, _, activityDispatchInfo := s.addActivityTaskScheduledEvent(4, activity2ID, activity2Type, activity2Domain, activityTaskList, activity2Input, activityTimeout, queueTimeout, hearbeatTimeout, nil, false) s.validateActivityTaskScheduledEvent(activity2ScheduledEvent, 6, 4, activity2ID, activity2Type, activity2Domain, activityTaskList, activity2Input, activityTimeout, queueTimeout, hearbeatTimeout, activityDispatchInfo, false) s.Equal(int64(7), s.getNextEventID()) ai2, activity2Running0 := s.msBuilder.GetActivityInfo(6) s.True(activity2Running0) s.Equal(commonconstants.EmptyEventID, ai2.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) // 7 activity1 started activityStartedEvent := s.addActivityTaskStartedEvent(5, activityTaskList, identity) s.validateActivityTaskStartedEvent(activityStartedEvent, commonconstants.BufferedEventID, 5, identity, 0, "", nil) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateActivityTaskStartedEvent(activityStartedEvent, 7, 5, identity, 0, "", nil) s.Equal(int64(8), s.getNextEventID()) ai3, activity1Running1 := s.msBuilder.GetActivityInfo(5) s.True(activity1Running1) s.Equal(int64(7), ai3.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) // 8 activity1 completed activityCompletedEvent := s.addActivityTaskCompletedEvent(5, 7, activity1Result, identity) s.validateActivityTaskCompletedEvent(activityCompletedEvent, commonconstants.BufferedEventID, 5, 7, activity1Result, identity) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateActivityTaskCompletedEvent(activityCompletedEvent, 8, 5, 7, activity1Result, identity) s.Equal(int64(9), s.getNextEventID()) _, activity1Running2 := s.msBuilder.GetActivityInfo(5) s.False(activity1Running2) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) // 9 decision2 scheduled di2 := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(di2, 9, tl, taskTimeout) s.Equal(int64(10), s.getNextEventID()) di3, decisionRunning3 := s.msBuilder.GetDecisionInfo(9) s.True(decisionRunning3) s.Equal(commonconstants.EmptyEventID, di3.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) // 10 decision2 started decision2StartedEvent := s.addDecisionTaskStartedEvent(9, tl, identity) s.validateDecisionTaskStartedEvent(decision2StartedEvent, 10, 9, identity) s.Equal(int64(11), s.getNextEventID()) di2, decision2Running := s.msBuilder.GetDecisionInfo(9) s.True(decision2Running) s.NotNil(di2) decision2StartedID := di2.StartedID s.Equal(int64(10), decision2StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) // 11 (buffered) activity2 started activity2StartedEvent := s.addActivityTaskStartedEvent(6, activityTaskList, identity) s.validateActivityTaskStartedEvent(activity2StartedEvent, commonconstants.BufferedEventID, 6, identity, 0, "", nil) s.Equal(int64(11), s.getNextEventID()) ai4, activity2Running := s.msBuilder.GetActivityInfo(6) s.True(activity2Running) s.Equal(commonconstants.BufferedEventID, ai4.StartedID) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) // 12 (buffered) activity2 failed activity2Reason := "flush-buffered-events-activity2-failed" activity2Details := []byte("flush-buffered-events-activity2-callstack") activity2FailedEvent := s.addActivityTaskFailedEvent(6, commonconstants.BufferedEventID, activity2Reason, activity2Details, identity) s.validateActivityTaskFailedEvent(activity2FailedEvent, commonconstants.BufferedEventID, 6, commonconstants.BufferedEventID, activity2Reason, activity2Details, identity) s.Equal(int64(11), s.getNextEventID()) _, activity2Running2 := s.msBuilder.GetActivityInfo(6) s.False(activity2Running2) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) // 13 (eventId will be 11) decision completed decision2Context := []byte("flush-buffered-events-context") decision2CompletedEvent := s.addDecisionTaskCompletedEvent(9, 10, decision2Context, identity) s.validateDecisionTaskCompletedEvent(decision2CompletedEvent, 11, 9, 10, decision2Context, identity) s.Equal(int64(11), decision2CompletedEvent.ID) s.Equal(int64(12), s.getNextEventID()) _, decision2Running2 := s.msBuilder.GetDecisionInfo(2) s.False(decision2Running2) s.Equal(int64(10), s.getPreviousDecisionStartedEventID()) // flush buffered events. 12: Activity2Started, 13: Activity2Failed s.NoError(s.msBuilder.FlushBufferedEvents()) s.Equal(int64(14), s.getNextEventID()) activity2StartedEvent2 := s.msBuilder.GetHistoryBuilder().history[11] s.Equal(int64(12), activity2StartedEvent2.ID) s.Equal(types.EventTypeActivityTaskStarted, activity2StartedEvent2.GetEventType()) activity2FailedEvent2 := s.msBuilder.GetHistoryBuilder().history[12] s.Equal(int64(13), activity2FailedEvent2.ID) s.Equal(types.EventTypeActivityTaskFailed, activity2FailedEvent2.GetEventType()) s.Equal(int64(12), activity2FailedEvent2.ActivityTaskFailedEventAttributes.GetStartedEventID()) } func (s *historyBuilderSuite) TestHistoryBuilderWorkflowCancellationRequested() { workflowType := "some random workflow type" tasklist := "some random tasklist" identity := "some random identity" input := []byte("some random workflow input") execTimeout := int32(60) taskTimeout := int32(10) workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: uuid.New(), } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(workflowExecution, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) decisionInfo := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(decisionInfo, 2, tasklist, taskTimeout) s.Equal(int64(3), s.getNextEventID()) decisionInfo, decisionRunning := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(commonconstants.EmptyEventID, decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionStartedEvent := s.addDecisionTaskStartedEvent(2, tasklist, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent, 3, 2, identity) s.Equal(int64(4), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(int64(3), decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionContext := []byte("some random decision context") decisionCompletedEvent := s.addDecisionTaskCompletedEvent(2, 3, decisionContext, identity) s.validateDecisionTaskCompletedEvent(decisionCompletedEvent, 4, 2, 3, decisionContext, identity) s.Equal(int64(5), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(2) s.False(decisionRunning) s.Nil(decisionInfo) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) targetExecution := types.WorkflowExecution{ WorkflowID: "some random target workflow ID", RunID: "some random target run ID", } cancellationChildWorkflowOnly := true cancellationInitiatedEvent := s.addRequestCancelExternalWorkflowExecutionInitiatedEvent( 4, s.targetDomainName, targetExecution, cancellationChildWorkflowOnly, ) s.validateRequestCancelExternalWorkflowExecutionInitiatedEvent( cancellationInitiatedEvent, 5, 4, s.targetDomainName, targetExecution, cancellationChildWorkflowOnly, ) s.Equal(int64(6), s.getNextEventID()) cancellationRequestedEvent := s.addExternalWorkflowExecutionCancelRequested( 5, s.targetDomainName, targetExecution.GetWorkflowID(), targetExecution.GetRunID(), ) s.validateExternalWorkflowExecutionCancelRequested(cancellationRequestedEvent, commonconstants.BufferedEventID, 5, s.targetDomainName, targetExecution) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateExternalWorkflowExecutionCancelRequested(cancellationRequestedEvent, 6, 5, s.targetDomainName, targetExecution) s.Equal(int64(7), s.getNextEventID()) } func (s *historyBuilderSuite) TestHistoryBuilderWorkflowCancellationFailed() { workflowType := "some random workflow type" tasklist := "some random tasklist" identity := "some random identity" input := []byte("some random workflow input") execTimeout := int32(60) taskTimeout := int32(10) workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: uuid.New(), } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(workflowExecution, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) decisionInfo := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(decisionInfo, 2, tasklist, taskTimeout) s.Equal(int64(3), s.getNextEventID()) decisionInfo, decisionRunning := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(commonconstants.EmptyEventID, decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionStartedEvent := s.addDecisionTaskStartedEvent(2, tasklist, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent, 3, 2, identity) s.Equal(int64(4), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(int64(3), decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionContext := []byte("some random decision context") decisionCompletedEvent := s.addDecisionTaskCompletedEvent(2, 3, decisionContext, identity) s.validateDecisionTaskCompletedEvent(decisionCompletedEvent, 4, 2, 3, decisionContext, identity) s.Equal(int64(5), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(2) s.False(decisionRunning) s.Nil(decisionInfo) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) targetExecution := types.WorkflowExecution{ WorkflowID: "some random target workflow ID", RunID: "some random target run ID", } cancellationChildWorkflowOnly := true cancellationFailedCause := types.CancelExternalWorkflowExecutionFailedCause(59) cancellationInitiatedEvent := s.addRequestCancelExternalWorkflowExecutionInitiatedEvent( 4, s.targetDomainName, targetExecution, cancellationChildWorkflowOnly, ) s.validateRequestCancelExternalWorkflowExecutionInitiatedEvent( cancellationInitiatedEvent, 5, 4, s.targetDomainName, targetExecution, cancellationChildWorkflowOnly, ) s.Equal(int64(6), s.getNextEventID()) cancellationRequestedEvent := s.addRequestCancelExternalWorkflowExecutionFailedEvent( 4, 5, s.targetDomainName, targetExecution.GetWorkflowID(), targetExecution.GetRunID(), cancellationFailedCause, ) s.validateRequestCancelExternalWorkflowExecutionFailedEvent( cancellationRequestedEvent, commonconstants.BufferedEventID, 4, 5, s.targetDomainName, targetExecution, cancellationFailedCause, ) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateRequestCancelExternalWorkflowExecutionFailedEvent( cancellationRequestedEvent, 6, 4, 5, s.targetDomainName, targetExecution, cancellationFailedCause, ) s.Equal(int64(7), s.getNextEventID()) } func (s *historyBuilderSuite) TestHistoryBuilder_DecisionTaskResetTimedOut() { workflowType := "some random workflow type" tasklist := "some random tasklist" identity := "some random identity" input := []byte("some random workflow input") execTimeout := int32(60) taskTimeout := int32(10) workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: uuid.New(), } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(workflowExecution, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) decisionInfo := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(decisionInfo, 2, tasklist, taskTimeout) s.Equal(int64(3), s.getNextEventID()) decisionInfo, decisionRunning := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(commonconstants.EmptyEventID, decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionStartedEvent := s.addDecisionTaskStartedEvent(2, tasklist, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent, 3, 2, identity) s.Equal(int64(4), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(int64(3), decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) baseRunID := uuid.New() newRunID := uuid.New() forkVersion := int64(10) resetRequestID := uuid.New() decisionFailedEvent, err := s.msBuilder.AddDecisionTaskFailedEvent(2, 3, types.DecisionTaskFailedCauseResetWorkflow, nil, identity, "", "", baseRunID, newRunID, forkVersion, resetRequestID) s.NoError(err) s.NotNil(decisionFailedEvent.GetDecisionTaskFailedEventAttributes()) s.Equal(baseRunID, decisionFailedEvent.GetDecisionTaskFailedEventAttributes().GetBaseRunID()) s.Equal(newRunID, decisionFailedEvent.GetDecisionTaskFailedEventAttributes().GetNewRunID()) s.Equal(forkVersion, decisionFailedEvent.GetDecisionTaskFailedEventAttributes().GetForkEventVersion()) s.Equal(resetRequestID, decisionFailedEvent.GetDecisionTaskFailedEventAttributes().GetRequestID()) s.Equal(int64(5), s.getNextEventID()) decisionInfo = s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(decisionInfo, 5, tasklist, taskTimeout) s.Equal(int64(6), s.getNextEventID()) decisionStartedEvent = s.addDecisionTaskStartedEvent(5, tasklist, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent, 6, 5, identity) s.Equal(int64(7), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(5) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(5), decisionInfo.ScheduleID) s.Equal(int64(6), decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionFailedEvent, err = s.msBuilder.AddDecisionTaskFailedEvent(5, 6, types.DecisionTaskFailedCauseResetWorkflow, nil, identity, "", "", baseRunID, newRunID, forkVersion, resetRequestID) s.NoError(err) s.NotNil(decisionFailedEvent.GetDecisionTaskFailedEventAttributes()) s.Equal(baseRunID, decisionFailedEvent.GetDecisionTaskFailedEventAttributes().GetBaseRunID()) s.Equal(newRunID, decisionFailedEvent.GetDecisionTaskFailedEventAttributes().GetNewRunID()) s.Equal(forkVersion, decisionFailedEvent.GetDecisionTaskFailedEventAttributes().GetForkEventVersion()) s.Equal(resetRequestID, decisionFailedEvent.GetDecisionTaskFailedEventAttributes().GetRequestID()) s.Equal(int64(8), s.getNextEventID()) } func (s *historyBuilderSuite) TestHistoryBuilder_DecisionTaskResetFailed() { workflowType := "some random workflow type" tasklist := "some random tasklist" identity := "some random identity" input := []byte("some random workflow input") execTimeout := int32(60) taskTimeout := int32(10) workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: uuid.New(), } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(workflowExecution, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) decisionInfo := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(decisionInfo, 2, tasklist, taskTimeout) s.Equal(int64(3), s.getNextEventID()) decisionInfo, decisionRunning := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(commonconstants.EmptyEventID, decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionStartedEvent := s.addDecisionTaskStartedEvent(2, tasklist, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent, 3, 2, identity) s.Equal(int64(4), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(int64(3), decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) baseRunID := uuid.New() newRunID := uuid.New() forkVersion := int64(10) resetRequestID := uuid.New() decisionTimedOutEvent := s.addDecisionTaskResetTimedOutEvent(2, baseRunID, newRunID, forkVersion, resetRequestID) s.NotNil(decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes()) s.Equal(int64(2), decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes().GetScheduledEventID()) s.Equal(baseRunID, decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes().GetBaseRunID()) s.Equal(newRunID, decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes().GetNewRunID()) s.Equal(forkVersion, decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes().GetForkEventVersion()) s.Equal(resetRequestID, decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes().GetRequestID()) s.Equal(int64(5), s.getNextEventID()) decisionInfo = s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(decisionInfo, 5, tasklist, taskTimeout) s.Equal(int64(6), s.getNextEventID()) decisionTimedOutEvent = s.addDecisionTaskResetTimedOutEvent(5, baseRunID, newRunID, forkVersion, resetRequestID) s.NotNil(decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes()) s.Equal(int64(5), decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes().GetScheduledEventID()) s.Equal(baseRunID, decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes().GetBaseRunID()) s.Equal(newRunID, decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes().GetNewRunID()) s.Equal(forkVersion, decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes().GetForkEventVersion()) s.Equal(resetRequestID, decisionTimedOutEvent.GetDecisionTaskTimedOutEventAttributes().GetRequestID()) s.Equal(int64(7), s.getNextEventID()) } func (s *historyBuilderSuite) TestHistoryBuilderWorkflowExternalCancellationRequested() { workflowType := "some random workflow type" tasklist := "some random tasklist" identity := "some random identity" input := []byte("some random workflow input") execTimeout := int32(60) taskTimeout := int32(10) workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: uuid.New(), } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(workflowExecution, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) decisionInfo := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(decisionInfo, 2, tasklist, taskTimeout) s.Equal(int64(3), s.getNextEventID()) decisionInfo, decisionRunning := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(commonconstants.EmptyEventID, decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionStartedEvent := s.addDecisionTaskStartedEvent(2, tasklist, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent, 3, 2, identity) s.Equal(int64(4), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(int64(3), decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionContext := []byte("some random decision context") decisionCompletedEvent := s.addDecisionTaskCompletedEvent(2, 3, decisionContext, identity) s.validateDecisionTaskCompletedEvent(decisionCompletedEvent, 4, 2, 3, decisionContext, identity) s.Equal(int64(5), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(2) s.False(decisionRunning) s.Nil(decisionInfo) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) cause := "cancel workflow" req := &types.HistoryRequestCancelWorkflowExecutionRequest{ CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Identity: "identity", RequestID: "b071cbe8-3a95-4223-a8ac-f308a42db383", }, } cancellationEvent := s.addWorkflowExecutionCancelRequestedEvent(cause, req) s.validateWorkflowExecutionCancelRequestedEvent(cancellationEvent, commonconstants.BufferedEventID, "cancel workflow", "identity", nil, types.WorkflowExecution{}, "b071cbe8-3a95-4223-a8ac-f308a42db383") s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateWorkflowExecutionCancelRequestedEvent(cancellationEvent, 5, "cancel workflow", "identity", nil, types.WorkflowExecution{}, "b071cbe8-3a95-4223-a8ac-f308a42db383") s.Equal(int64(6), s.getNextEventID()) _, exists := s.msBuilder.(*mutableStateBuilder).workflowRequests[persistence.WorkflowRequest{ RequestID: "b071cbe8-3a95-4223-a8ac-f308a42db383", RequestType: persistence.WorkflowRequestTypeCancel, Version: s.msBuilder.GetCurrentVersion(), }] s.True(exists) } func (s *historyBuilderSuite) TestHistoryBuilderWorkflowExternalSignaled() { workflowType := "some random workflow type" tasklist := "some random tasklist" identity := "some random identity" input := []byte("some random workflow input") execTimeout := int32(60) taskTimeout := int32(10) workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: uuid.New(), } partitionConfig := map[string]string{ "zone": "dca1", } workflowStartedEvent := s.addWorkflowExecutionStartedEvent(workflowExecution, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.validateWorkflowExecutionStartedEvent(workflowStartedEvent, workflowType, tasklist, input, execTimeout, taskTimeout, identity, partitionConfig) s.Equal(int64(2), s.getNextEventID()) decisionInfo := s.addDecisionTaskScheduledEvent() s.validateDecisionTaskScheduledEvent(decisionInfo, 2, tasklist, taskTimeout) s.Equal(int64(3), s.getNextEventID()) decisionInfo, decisionRunning := s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(commonconstants.EmptyEventID, decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionStartedEvent := s.addDecisionTaskStartedEvent(2, tasklist, identity) s.validateDecisionTaskStartedEvent(decisionStartedEvent, 3, 2, identity) s.Equal(int64(4), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(2) s.True(decisionRunning) s.NotNil(decisionInfo) s.Equal(int64(2), decisionInfo.ScheduleID) s.Equal(int64(3), decisionInfo.StartedID) s.Equal(commonconstants.EmptyEventID, s.getPreviousDecisionStartedEventID()) decisionContext := []byte("some random decision context") decisionCompletedEvent := s.addDecisionTaskCompletedEvent(2, 3, decisionContext, identity) s.validateDecisionTaskCompletedEvent(decisionCompletedEvent, 4, 2, 3, decisionContext, identity) s.Equal(int64(5), s.getNextEventID()) decisionInfo, decisionRunning = s.msBuilder.GetDecisionInfo(2) s.False(decisionRunning) s.Nil(decisionInfo) s.Equal(int64(3), s.getPreviousDecisionStartedEventID()) signalName := "test-signal" signalInput := []byte("input") signalIdentity := "id" requestID := "3b8d0ec2-e1ff-4f61-915b-1ffca831361e" signalEvent := s.addWorkflowExecutionSignaledEvent(signalName, signalInput, signalIdentity, requestID) s.validateWorkflowExecutionSignaledEvent(signalEvent, commonconstants.BufferedEventID, "test-signal", []byte("input"), "id", "3b8d0ec2-e1ff-4f61-915b-1ffca831361e") s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateWorkflowExecutionSignaledEvent(signalEvent, 5, "test-signal", []byte("input"), "id", "3b8d0ec2-e1ff-4f61-915b-1ffca831361e") s.Equal(int64(6), s.getNextEventID()) _, exists := s.msBuilder.(*mutableStateBuilder).workflowRequests[persistence.WorkflowRequest{ RequestID: "3b8d0ec2-e1ff-4f61-915b-1ffca831361e", RequestType: persistence.WorkflowRequestTypeSignal, Version: s.msBuilder.GetCurrentVersion(), }] s.True(exists) } func (s *historyBuilderSuite) TestHistoryBuilderActivityTaskTimedOut() { id := "historybuilder-workflow-id" rid := "historybuilder-test-run-id" wt := "historybuilder-type" tl := "historybuilder-tasklist" identity := "historybuilder-worker" input := []byte("historybuilder-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } // 1 s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) // 2 s.addDecisionTaskScheduledEvent() // 3 s.addDecisionTaskStartedEvent(2, tl, identity) decisionContext := []byte("historybuilder-context") // 4 s.addDecisionTaskCompletedEvent(2, 3, decisionContext, identity) activityID := "activity1" activityType := "activity1-type" activityDomain := "" activityInput := []byte("activity1-input") activityTimeout := int32(60) queueTimeout := int32(20) hearbeatTimeout := int32(10) // 5 s.addActivityTaskScheduledEvent(4, activityID, activityType, activityDomain, tl, activityInput, activityTimeout, queueTimeout, hearbeatTimeout, nil, false) // 6 s.addActivityTaskStartedEvent(5, tl, identity) s.Nil(s.msBuilder.FlushBufferedEvents()) timeoutType := types.TimeoutTypeHeartbeat lastHeartbeat := []byte("last-heartbeat") // 7 timedOut := s.addActivityTaskTimedOutEvent(5, 6, timeoutType, lastHeartbeat) s.validateActivityTaskTimedOutEvent(timedOut, commonconstants.BufferedEventID, 5, 6, timeoutType, lastHeartbeat, "", nil) // 8 s.addDecisionTaskScheduledEvent() // 9 s.addDecisionTaskStartedEvent(8, tl, identity) // 10 s.addDecisionTaskCompletedEvent(8, 9, decisionContext, identity) failReason := "activity failed :(" failDetails := []byte("huge failure") // 11 failed := s.addWorkflowExecutionFailed(10, &types.FailWorkflowExecutionDecisionAttributes{ Reason: &failReason, Details: failDetails, }) s.validateWorkflowExecutionFailed(failed, 11, 10, failReason, failDetails) } func (s *historyBuilderSuite) TestHistoryBuilderTimeoutWorkflow() { id := "historybuilder-workflow-id" rid := "historybuilder-test-run-id" wt := "historybuilder-type" tl := "historybuilder-tasklist" identity := "historybuilder-worker" input := []byte("historybuilder-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } // 1 s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) // 2 timedOut := s.addWorkflowExecutionTimedOut(1) s.validateWorkflowExecutionTimedOut(timedOut, 2) } func (s *historyBuilderSuite) TestHistoryBuilderCompleteWorkflow() { id := "historybuilder-workflow-id" rid := "historybuilder-test-run-id" wt := "historybuilder-type" tl := "historybuilder-tasklist" identity := "historybuilder-worker" input := []byte("historybuilder-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } // 1 s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) // 2 s.addDecisionTaskScheduledEvent() // 3 s.addDecisionTaskStartedEvent(2, tl, identity) decisionContext := []byte("historybuilder-context") // 4 s.addDecisionTaskCompletedEvent(2, 3, decisionContext, identity) decisionResult := []byte("great success") // 5 event := s.addWorkflowExecutionCompleted(4, &types.CompleteWorkflowExecutionDecisionAttributes{ Result: decisionResult, }) s.validateWorkflowExecutionCompleted(event, 5, 4, decisionResult) } func (s *historyBuilderSuite) TestHistoryBuilderTerminateWorkflow() { id := "historybuilder-workflow-id" rid := "historybuilder-test-run-id" wt := "historybuilder-type" tl := "historybuilder-tasklist" identity := "historybuilder-worker" input := []byte("historybuilder-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } // 1 s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) // 2 terminated := s.addWorkflowExecutionTerminated(1, "reason", []byte("a really good reason"), identity) s.validateWorkflowExecutionTerminated(terminated, 2, "reason", []byte("a really good reason"), identity) } func (s *historyBuilderSuite) TestHistoryBuilderCancelWorkflow() { id := "historybuilder-workflow-id" rid := "historybuilder-test-run-id" wt := "historybuilder-type" tl := "historybuilder-tasklist" identity := "historybuilder-worker" input := []byte("historybuilder-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } // 1 s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) req := &types.HistoryRequestCancelWorkflowExecutionRequest{ CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Identity: "identity", RequestID: "b071cbe8-3a95-4223-a8ac-f308a42db383", }, } // 2 s.addWorkflowExecutionCancelRequestedEvent("because", req) // 3 s.addDecisionTaskScheduledEvent() // 4 s.addDecisionTaskStartedEvent(3, tl, identity) // 5 s.addDecisionTaskCompletedEvent(3, 4, []byte("context"), identity) // 6 event := s.addWorkflowExecutionCanceled(5, &types.CancelWorkflowExecutionDecisionAttributes{ Details: []byte("details"), }) s.validateWorkflowExecutionCanceled(event, 6, 5, []byte("details")) } func (s *historyBuilderSuite) TestHistoryBuilderTimers() { id := "historybuilder-workflow-id" rid := "historybuilder-test-run-id" wt := "historybuilder-type" tl := "historybuilder-tasklist" identity := "historybuilder-worker" input := []byte("historybuilder-input") execTimeout := int32(60) taskTimeout := int32(10) we := types.WorkflowExecution{ WorkflowID: id, RunID: rid, } partitionConfig := map[string]string{ "zone": "dca1", } // 1 s.addWorkflowExecutionStartedEvent(we, wt, tl, input, execTimeout, taskTimeout, identity, partitionConfig) // 2 s.addDecisionTaskScheduledEvent() // 3 s.addDecisionTaskStartedEvent(2, tl, identity) // 4 s.addDecisionTaskCompletedEvent(2, 3, []byte("context"), identity) // 5 timer1Started := s.addTimerStartedEvent(4, &types.StartTimerDecisionAttributes{ TimerID: "timer1", StartToFireTimeoutSeconds: common.Int64Ptr(int64(30)), }) // 6 timer2Started := s.addTimerStartedEvent(4, &types.StartTimerDecisionAttributes{ TimerID: "timer2", StartToFireTimeoutSeconds: common.Int64Ptr(int64(45)), }) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateTimerStartedEvent(timer1Started, 5, 4, 30, "timer1") s.validateTimerStartedEvent(timer2Started, 6, 4, 45, "timer2") // 7 timerFired := s.addTimerFiredEvent("timer1") s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateTimerFiredEvent(timerFired, 7, 5, "timer1") // 8 s.addDecisionTaskScheduledEvent() // 9 s.addDecisionTaskStartedEvent(8, tl, identity) // 10 s.addDecisionTaskCompletedEvent(8, 9, []byte("context"), identity) // 11 // Cancel timer2 cancel := s.addTimerCanceledEvent(10, &types.CancelTimerDecisionAttributes{ TimerID: "timer2", }, identity) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateTimerCanceledEvent(cancel, 11, 6, 10, "timer2", identity) // 12 // Fail to cancel a timer that doesn't exist cancelFailed := s.addTimerCancelFailedEvent(10, &types.CancelTimerDecisionAttributes{ TimerID: "not-a-real-timer", }, identity) s.Nil(s.msBuilder.FlushBufferedEvents()) s.validateTimerCancelFailedEvent(cancelFailed, 12, 10, "not-a-real-timer", identity) } func (s *historyBuilderSuite) getNextEventID() int64 { return s.msBuilder.GetExecutionInfo().NextEventID } func (s *historyBuilderSuite) getPreviousDecisionStartedEventID() int64 { return s.msBuilder.GetExecutionInfo().LastProcessedEvent } func (s *historyBuilderSuite) addWorkflowExecutionStartedEvent(we types.WorkflowExecution, workflowType, taskList string, input []byte, executionStartToCloseTimeout, taskStartToCloseTimeout int32, identity string, partitionConfig map[string]string) *types.HistoryEvent { request := &types.StartWorkflowExecutionRequest{ WorkflowID: we.WorkflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, Input: input, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(executionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(taskStartToCloseTimeout), Identity: identity, } event, err := s.msBuilder.AddWorkflowExecutionStartedEvent( we, &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: s.domainID, StartRequest: request, PartitionConfig: partitionConfig, }, ) s.Nil(err) return event } func (s *historyBuilderSuite) addWorkflowExecutionTimedOut(firstEvent int64) *types.HistoryEvent { event, err := s.msBuilder.AddTimeoutWorkflowEvent(firstEvent) s.Nil(err) return event } func (s *historyBuilderSuite) addWorkflowExecutionFailed(decisionCompletedEventID int64, attributes *types.FailWorkflowExecutionDecisionAttributes) *types.HistoryEvent { event, err := s.msBuilder.AddFailWorkflowEvent(decisionCompletedEventID, attributes) s.Nil(err) return event } func (s *historyBuilderSuite) addWorkflowExecutionCompleted(decisionCompletedEventID int64, attributes *types.CompleteWorkflowExecutionDecisionAttributes) *types.HistoryEvent { event, err := s.msBuilder.AddCompletedWorkflowEvent(decisionCompletedEventID, attributes) s.Nil(err) return event } func (s *historyBuilderSuite) addWorkflowExecutionTerminated(firstEventID int64, reason string, details []byte, identity string) *types.HistoryEvent { event, err := s.msBuilder.AddWorkflowExecutionTerminatedEvent(firstEventID, reason, details, identity) s.Nil(err) return event } func (s *historyBuilderSuite) addWorkflowExecutionCanceled(decisionTaskCompletedEventID int64, attributes *types.CancelWorkflowExecutionDecisionAttributes) *types.HistoryEvent { event, err := s.msBuilder.AddWorkflowExecutionCanceledEvent(decisionTaskCompletedEventID, attributes) s.Nil(err) return event } func (s *historyBuilderSuite) addDecisionTaskScheduledEvent() *DecisionInfo { di, err := s.msBuilder.AddDecisionTaskScheduledEvent(false) s.Nil(err) return di } func (s *historyBuilderSuite) addDecisionTaskStartedEvent( scheduleID int64, taskList string, identity string, ) *types.HistoryEvent { event, _, err := s.msBuilder.AddDecisionTaskStartedEvent(scheduleID, uuid.New(), &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{Name: taskList}, Identity: identity, }) s.Nil(err) return event } func (s *historyBuilderSuite) addDecisionTaskCompletedEvent( scheduleID int64, startedID int64, context []byte, identity string, ) *types.HistoryEvent { event, err := s.msBuilder.AddDecisionTaskCompletedEvent(scheduleID, startedID, &types.RespondDecisionTaskCompletedRequest{ ExecutionContext: context, Identity: identity, }, commonconstants.DefaultHistoryMaxAutoResetPoints) s.Nil(err) return event } func (s *historyBuilderSuite) addDecisionTaskTimedOutEvent( scheduleID int64, ) *types.HistoryEvent { event, err := s.msBuilder.AddDecisionTaskScheduleToStartTimeoutEvent(scheduleID) s.Nil(err) return event } func (s *historyBuilderSuite) addDecisionTaskStartedTimedOutEvent( scheduleID int64, startID int64, ) *types.HistoryEvent { event, err := s.msBuilder.AddDecisionTaskTimedOutEvent(scheduleID, startID) s.Nil(err) return event } func (s *historyBuilderSuite) addDecisionTaskResetTimedOutEvent( scheduleID int64, baseRunID string, newRunID string, newRunVersion int64, resetRequestID string, ) *types.HistoryEvent { event, err := s.msBuilder.AddDecisionTaskResetTimeoutEvent( scheduleID, baseRunID, newRunID, newRunVersion, "", resetRequestID, ) s.Nil(err) return event } func (s *historyBuilderSuite) addTimerStartedEvent(decisionCompletedEventID int64, request *types.StartTimerDecisionAttributes) *types.HistoryEvent { event, _, err := s.msBuilder.AddTimerStartedEvent(decisionCompletedEventID, request) s.Nil(err) return event } func (s *historyBuilderSuite) addTimerFiredEvent(timerID string) *types.HistoryEvent { event, err := s.msBuilder.AddTimerFiredEvent(timerID) s.Nil(err) return event } func (s *historyBuilderSuite) addTimerCanceledEvent(decisionCompletedEventID int64, attributes *types.CancelTimerDecisionAttributes, identity string) *types.HistoryEvent { event, err := s.msBuilder.AddTimerCanceledEvent(decisionCompletedEventID, attributes, identity) s.Nil(err) return event } func (s *historyBuilderSuite) addTimerCancelFailedEvent(decisionCompletedEventID int64, attributes *types.CancelTimerDecisionAttributes, identity string) *types.HistoryEvent { event, err := s.msBuilder.AddCancelTimerFailedEvent(decisionCompletedEventID, attributes, identity) s.Nil(err) return event } func (s *historyBuilderSuite) addActivityTaskScheduledEvent( decisionCompletedID int64, activityID, activityType, domain, taskList string, input []byte, timeout, queueTimeout, hearbeatTimeout int32, retryPolicy *types.RetryPolicy, requestLocalDispatch bool, ) (*types.HistoryEvent, *persistence.ActivityInfo, *types.ActivityLocalDispatchInfo) { event, ai, activityDispatchInfo, _, _, err := s.msBuilder.AddActivityTaskScheduledEvent(nil, decisionCompletedID, &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: activityID, ActivityType: &types.ActivityType{Name: activityType}, Domain: domain, TaskList: &types.TaskList{Name: taskList}, Input: input, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(timeout), ScheduleToStartTimeoutSeconds: common.Int32Ptr(queueTimeout), HeartbeatTimeoutSeconds: common.Int32Ptr(hearbeatTimeout), StartToCloseTimeoutSeconds: common.Int32Ptr(1), RetryPolicy: retryPolicy, RequestLocalDispatch: requestLocalDispatch, }, false, ) s.Nil(err) if domain == "" { s.Equal(s.domainID, ai.DomainID) } else { s.Equal(s.targetDomainID, ai.DomainID) } return event, ai, activityDispatchInfo } func (s *historyBuilderSuite) addActivityTaskStartedEvent(scheduleID int64, taskList, identity string) *types.HistoryEvent { ai, _ := s.msBuilder.GetActivityInfo(scheduleID) event, err := s.msBuilder.AddActivityTaskStartedEvent(ai, scheduleID, uuid.New(), identity) s.Nil(err) return event } func (s *historyBuilderSuite) addActivityTaskCompletedEvent(scheduleID, startedID int64, result []byte, identity string) *types.HistoryEvent { event, err := s.msBuilder.AddActivityTaskCompletedEvent(scheduleID, startedID, &types.RespondActivityTaskCompletedRequest{ Result: result, Identity: identity, }) s.Nil(err) return event } func (s *historyBuilderSuite) addActivityTaskFailedEvent(scheduleID, startedID int64, reason string, details []byte, identity string) *types.HistoryEvent { event, err := s.msBuilder.AddActivityTaskFailedEvent(scheduleID, startedID, &types.RespondActivityTaskFailedRequest{ Reason: common.StringPtr(reason), Details: details, Identity: identity, }) s.Nil(err) return event } func (s *historyBuilderSuite) addActivityTaskTimedOutEvent(scheduleEventID, startedEventID int64, timeoutType types.TimeoutType, lastHeartBeatDetails []byte) *types.HistoryEvent { event, err := s.msBuilder.AddActivityTaskTimedOutEvent(scheduleEventID, startedEventID, timeoutType, lastHeartBeatDetails) s.Nil(err) return event } func (s *historyBuilderSuite) addMarkerRecordedEvent(decisionCompletedEventID int64, markerName string, details []byte, header *map[string][]byte) *types.HistoryEvent { fields := make(map[string][]byte) if header != nil { for name, value := range *header { fields[name] = value } } event, err := s.msBuilder.AddRecordMarkerEvent(decisionCompletedEventID, &types.RecordMarkerDecisionAttributes{ MarkerName: markerName, Details: details, Header: &types.Header{ Fields: fields, }, }) s.Nil(err) return event } func (s *historyBuilderSuite) addRequestCancelExternalWorkflowExecutionInitiatedEvent( decisionCompletedEventID int64, targetDomain string, targetExecution types.WorkflowExecution, childWorkflowOnly bool) *types.HistoryEvent { event, _, err := s.msBuilder.AddRequestCancelExternalWorkflowExecutionInitiatedEvent( decisionCompletedEventID, uuid.New(), &types.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: targetDomain, WorkflowID: targetExecution.WorkflowID, RunID: targetExecution.RunID, ChildWorkflowOnly: childWorkflowOnly, }, ) s.Nil(err) return event } func (s *historyBuilderSuite) addExternalWorkflowExecutionCancelRequested( initiatedID int64, domain, workflowID, runID string) *types.HistoryEvent { event, err := s.msBuilder.AddExternalWorkflowExecutionCancelRequested( initiatedID, domain, workflowID, runID, ) s.Nil(err) return event } func (s *historyBuilderSuite) addRequestCancelExternalWorkflowExecutionFailedEvent( decisionTaskCompletedEventID, initiatedID int64, domain, workflowID, runID string, cause types.CancelExternalWorkflowExecutionFailedCause) *types.HistoryEvent { event, err := s.msBuilder.AddRequestCancelExternalWorkflowExecutionFailedEvent( decisionTaskCompletedEventID, initiatedID, domain, workflowID, runID, cause, ) s.Nil(err) return event } func (s *historyBuilderSuite) addWorkflowExecutionCancelRequestedEvent( cause string, request *types.HistoryRequestCancelWorkflowExecutionRequest, ) *types.HistoryEvent { event, err := s.msBuilder.AddWorkflowExecutionCancelRequestedEvent(cause, request) s.Nil(err) return event } func (s *historyBuilderSuite) addWorkflowExecutionSignaledEvent( signalName string, input []byte, identity string, requestID string, ) *types.HistoryEvent { event, err := s.msBuilder.AddWorkflowExecutionSignaled(signalName, input, identity, requestID) s.Nil(err) return event } func (s *historyBuilderSuite) validateWorkflowExecutionStartedEvent(event *types.HistoryEvent, workflowType, taskList string, input []byte, executionStartToCloseTimeout, taskStartToCloseTimeout int32, identity string, partitionConfig map[string]string) { s.NotNil(event) s.Equal(types.EventTypeWorkflowExecutionStarted, *event.EventType) s.Equal(commonconstants.FirstEventID, event.ID) attributes := event.WorkflowExecutionStartedEventAttributes s.NotNil(attributes) s.Equal(workflowType, attributes.WorkflowType.Name) s.Equal(taskList, attributes.TaskList.Name) s.Equal(input, attributes.Input) s.Equal(executionStartToCloseTimeout, *attributes.ExecutionStartToCloseTimeoutSeconds) s.Equal(taskStartToCloseTimeout, *attributes.TaskStartToCloseTimeoutSeconds) s.Equal(identity, attributes.Identity) s.Equal(partitionConfig, attributes.PartitionConfig) if attributes.CronSchedule == "" { s.Nil(attributes.FirstScheduleTime) } else { s.NotNil(attributes.FirstScheduleTime) } } func (s *historyBuilderSuite) validateWorkflowExecutionFailed(event *types.HistoryEvent, eventID, decisionCompletedID int64, failReason string, failDetail []byte) { s.NotNil(event) s.Equal(types.EventTypeWorkflowExecutionFailed, *event.EventType) s.Equal(eventID, event.ID) attributes := event.WorkflowExecutionFailedEventAttributes s.NotNil(attributes) s.Equal(decisionCompletedID, attributes.DecisionTaskCompletedEventID) s.Equal(failReason, *attributes.Reason) s.Equal(failDetail, attributes.Details) } func (s *historyBuilderSuite) validateWorkflowExecutionTimedOut(event *types.HistoryEvent, eventID int64) { s.NotNil(event) s.Equal(types.EventTypeWorkflowExecutionTimedOut, *event.EventType) s.Equal(eventID, event.ID) attributes := event.WorkflowExecutionTimedOutEventAttributes s.NotNil(attributes) s.Equal(types.TimeoutTypeStartToClose, *attributes.TimeoutType) } func (s *historyBuilderSuite) validateWorkflowExecutionCompleted(event *types.HistoryEvent, eventID, decisionCompletedID int64, result []byte) { s.NotNil(event) s.Equal(types.EventTypeWorkflowExecutionCompleted, *event.EventType) s.Equal(eventID, event.ID) attributes := event.WorkflowExecutionCompletedEventAttributes s.NotNil(attributes) s.Equal(decisionCompletedID, attributes.DecisionTaskCompletedEventID) s.Equal(result, attributes.Result) } func (s *historyBuilderSuite) validateWorkflowExecutionTerminated(event *types.HistoryEvent, eventID int64, reason string, details []byte, identity string) { s.NotNil(event) s.Equal(types.EventTypeWorkflowExecutionTerminated, *event.EventType) s.Equal(eventID, event.ID) attributes := event.WorkflowExecutionTerminatedEventAttributes s.NotNil(attributes) s.Equal(reason, attributes.Reason) s.Equal(details, attributes.Details) s.Equal(identity, attributes.Identity) } func (s *historyBuilderSuite) validateWorkflowExecutionCanceled(event *types.HistoryEvent, eventID, decisionTaskCompletedID int64, details []byte) { s.NotNil(event) s.Equal(types.EventTypeWorkflowExecutionCanceled, *event.EventType) s.Equal(eventID, event.ID) attributes := event.WorkflowExecutionCanceledEventAttributes s.NotNil(attributes) s.Equal(decisionTaskCompletedID, attributes.DecisionTaskCompletedEventID) s.Equal(details, attributes.Details) } func (s *historyBuilderSuite) validateDecisionTaskScheduledEvent(di *DecisionInfo, eventID int64, taskList string, timeout int32) { s.NotNil(di) s.Equal(eventID, di.ScheduleID) s.Equal(taskList, di.TaskList) } func (s *historyBuilderSuite) validateDecisionTaskStartedEvent(event *types.HistoryEvent, eventID, scheduleID int64, identity string) { s.NotNil(event) s.Equal(types.EventTypeDecisionTaskStarted, *event.EventType) s.Equal(eventID, event.ID) attributes := event.DecisionTaskStartedEventAttributes s.NotNil(attributes) s.Equal(scheduleID, attributes.ScheduledEventID) s.Equal(identity, attributes.Identity) } func (s *historyBuilderSuite) validateDecisionTaskCompletedEvent(event *types.HistoryEvent, eventID, scheduleID, startedID int64, context []byte, identity string) { s.NotNil(event) s.Equal(types.EventTypeDecisionTaskCompleted, *event.EventType) s.Equal(eventID, event.ID) attributes := event.DecisionTaskCompletedEventAttributes s.NotNil(attributes) s.Equal(scheduleID, attributes.ScheduledEventID) s.Equal(startedID, attributes.StartedEventID) s.Equal(context, attributes.ExecutionContext) s.Equal(identity, attributes.Identity) } func (s *historyBuilderSuite) validateDecisionTaskTimedoutEvent( event *types.HistoryEvent, eventID int64, scheduleEventID int64, startedEventID int64, timeoutType types.TimeoutType, baseRunID string, newRunID string, forkEventVersion int64, reason string, cause types.DecisionTaskTimedOutCause, ) { s.NotNil(event) s.Equal(types.EventTypeDecisionTaskTimedOut, *event.EventType) s.Equal(eventID, event.ID) attributes := event.DecisionTaskTimedOutEventAttributes s.NotNil(attributes) s.Equal(scheduleEventID, attributes.ScheduledEventID) s.Equal(startedEventID, attributes.StartedEventID) s.Equal(timeoutType, *attributes.TimeoutType) s.Equal(baseRunID, attributes.BaseRunID) s.Equal(newRunID, attributes.NewRunID) s.Equal(forkEventVersion, attributes.ForkEventVersion) s.Equal(reason, attributes.Reason) s.Equal(cause, *attributes.Cause) } func (s *historyBuilderSuite) validateActivityTaskScheduledEvent( event *types.HistoryEvent, eventID, decisionID int64, activityID, activityType, domain, taskList string, input []byte, timeout, queueTimeout, hearbeatTimeout int32, activityDispatchInfo *types.ActivityLocalDispatchInfo, requestLocalDispatch bool, ) { s.NotNil(event) s.Equal(types.EventTypeActivityTaskScheduled, *event.EventType) s.Equal(eventID, event.ID) attributes := event.ActivityTaskScheduledEventAttributes s.NotNil(attributes) s.Equal(decisionID, attributes.DecisionTaskCompletedEventID) s.Equal(activityID, attributes.ActivityID) s.Equal(activityType, attributes.ActivityType.Name) s.Equal(taskList, attributes.TaskList.Name) s.Equal(input, attributes.Input) s.Equal(timeout, *attributes.ScheduleToCloseTimeoutSeconds) s.Equal(queueTimeout, *attributes.ScheduleToStartTimeoutSeconds) s.Equal(hearbeatTimeout, *attributes.HeartbeatTimeoutSeconds) if domain != "" { s.Equal(domain, *attributes.Domain) } else { s.Nil(attributes.Domain) } if requestLocalDispatch { s.NotNil(activityDispatchInfo) } else { s.Nil(activityDispatchInfo) } } func (s *historyBuilderSuite) validateActivityTaskStartedEvent(event *types.HistoryEvent, eventID, scheduleID int64, identity string, attempt int64, lastFailureReason string, lastFailureDetails []byte) { s.NotNil(event) s.Equal(types.EventTypeActivityTaskStarted, *event.EventType) s.Equal(eventID, event.ID) attributes := event.ActivityTaskStartedEventAttributes s.NotNil(attributes) s.Equal(scheduleID, attributes.ScheduledEventID) s.Equal(identity, attributes.Identity) s.Equal(lastFailureReason, *attributes.LastFailureReason) s.Equal(lastFailureDetails, attributes.LastFailureDetails) } func (s *historyBuilderSuite) validateTransientActivityTaskStartedEvent(event *types.HistoryEvent, eventID, scheduleID int64, identity string) { s.Nil(event) ai, ok := s.msBuilder.GetPendingActivityInfos()[scheduleID] s.True(ok) s.NotNil(ai) s.Equal(scheduleID, ai.ScheduleID) s.Equal(identity, ai.StartedIdentity) } func (s *historyBuilderSuite) validateActivityTaskCompletedEvent(event *types.HistoryEvent, eventID, scheduleID, startedID int64, result []byte, identity string) { s.NotNil(event) s.Equal(types.EventTypeActivityTaskCompleted, *event.EventType) s.Equal(eventID, event.ID) attributes := event.ActivityTaskCompletedEventAttributes s.NotNil(attributes) s.Equal(scheduleID, attributes.ScheduledEventID) s.Equal(startedID, attributes.StartedEventID) s.Equal(result, attributes.Result) s.Equal(identity, attributes.Identity) } func (s *historyBuilderSuite) validateActivityTaskFailedEvent(event *types.HistoryEvent, eventID, scheduleID, startedID int64, reason string, details []byte, identity string) { s.NotNil(event) s.Equal(types.EventTypeActivityTaskFailed, *event.EventType) s.Equal(eventID, event.ID) attributes := event.ActivityTaskFailedEventAttributes s.NotNil(attributes) s.Equal(scheduleID, attributes.ScheduledEventID) s.Equal(startedID, attributes.StartedEventID) s.Equal(reason, *attributes.Reason) s.Equal(details, attributes.Details) s.Equal(identity, attributes.Identity) } func (s *historyBuilderSuite) validateActivityTaskTimedOutEvent(event *types.HistoryEvent, eventID, scheduleID, startedID int64, timeoutType types.TimeoutType, lastHeartBeatDetails []byte, lastFailureReason string, lastFailureDetails []byte) { s.NotNil(event) s.Equal(types.EventTypeActivityTaskTimedOut, *event.EventType) s.Equal(eventID, event.ID) attributes := event.ActivityTaskTimedOutEventAttributes s.NotNil(attributes) s.Equal(scheduleID, attributes.ScheduledEventID) s.Equal(startedID, attributes.StartedEventID) s.Equal(timeoutType, *attributes.TimeoutType) s.Equal(lastHeartBeatDetails, attributes.Details) s.Equal(lastFailureReason, *attributes.LastFailureReason) s.Equal(lastFailureDetails, attributes.LastFailureDetails) } func (s *historyBuilderSuite) validateMarkerRecordedEvent( event *types.HistoryEvent, eventID, decisionTaskCompletedEventID int64, markerName string, details []byte, header *map[string][]byte) { s.NotNil(event) s.Equal(types.EventTypeMarkerRecorded, *event.EventType) s.Equal(eventID, event.ID) attributes := event.MarkerRecordedEventAttributes s.NotNil(attributes) s.Equal(decisionTaskCompletedEventID, attributes.DecisionTaskCompletedEventID) s.Equal(markerName, attributes.GetMarkerName()) s.Equal(details, attributes.Details) if header != nil { for name, value := range attributes.Header.Fields { s.Equal((*header)[name], value) } } else { s.Nil(attributes.Header) } } func (s *historyBuilderSuite) validateRequestCancelExternalWorkflowExecutionInitiatedEvent( event *types.HistoryEvent, eventID, decisionTaskCompletedEventID int64, domain string, execution types.WorkflowExecution, childWorkflowOnly bool) { s.NotNil(event) s.Equal(types.EventTypeRequestCancelExternalWorkflowExecutionInitiated, *event.EventType) s.Equal(eventID, event.ID) attributes := event.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes s.NotNil(attributes) s.Equal(decisionTaskCompletedEventID, attributes.DecisionTaskCompletedEventID) s.Equal(domain, attributes.GetDomain()) s.Equal(execution.GetWorkflowID(), attributes.WorkflowExecution.GetWorkflowID()) s.Equal(execution.GetRunID(), attributes.WorkflowExecution.GetRunID()) s.Equal(childWorkflowOnly, attributes.ChildWorkflowOnly) } func (s *historyBuilderSuite) validateExternalWorkflowExecutionCancelRequested( event *types.HistoryEvent, eventID, initiatedEventID int64, domain string, execution types.WorkflowExecution) { s.NotNil(event) s.Equal(types.EventTypeExternalWorkflowExecutionCancelRequested, *event.EventType) s.Equal(eventID, event.ID) attributes := event.ExternalWorkflowExecutionCancelRequestedEventAttributes s.NotNil(attributes) s.Equal(initiatedEventID, attributes.GetInitiatedEventID()) s.Equal(domain, attributes.GetDomain()) s.Equal(execution.GetWorkflowID(), attributes.WorkflowExecution.GetWorkflowID()) s.Equal(execution.GetRunID(), attributes.WorkflowExecution.GetRunID()) } func (s *historyBuilderSuite) validateRequestCancelExternalWorkflowExecutionFailedEvent( event *types.HistoryEvent, eventID, decisionTaskCompletedEventID, initiatedEventID int64, domain string, execution types.WorkflowExecution, cause types.CancelExternalWorkflowExecutionFailedCause) { s.NotNil(event) s.Equal(types.EventTypeRequestCancelExternalWorkflowExecutionFailed, *event.EventType) s.Equal(eventID, event.ID) attributes := event.RequestCancelExternalWorkflowExecutionFailedEventAttributes s.NotNil(attributes) s.Equal(decisionTaskCompletedEventID, attributes.GetDecisionTaskCompletedEventID()) s.Equal(initiatedEventID, attributes.GetInitiatedEventID()) s.Equal(domain, attributes.GetDomain()) s.Equal(execution.GetWorkflowID(), attributes.WorkflowExecution.GetWorkflowID()) s.Equal(execution.GetRunID(), attributes.WorkflowExecution.GetRunID()) s.Equal(cause, *attributes.Cause) } func (s *historyBuilderSuite) validateWorkflowExecutionCancelRequestedEvent( event *types.HistoryEvent, eventID int64, cause string, identity string, externalInitiatedEventID *int64, execution types.WorkflowExecution, requestID string, ) { s.NotNil(event) s.Equal(types.EventTypeWorkflowExecutionCancelRequested, *event.EventType) s.Equal(eventID, event.ID) attributes := event.WorkflowExecutionCancelRequestedEventAttributes s.NotNil(attributes) s.Equal(cause, attributes.Cause) s.Equal(identity, attributes.Identity) s.Equal(externalInitiatedEventID, attributes.ExternalInitiatedEventID) s.Equal(execution.GetWorkflowID(), attributes.ExternalWorkflowExecution.GetWorkflowID()) s.Equal(execution.GetRunID(), attributes.ExternalWorkflowExecution.GetRunID()) s.Equal(requestID, attributes.RequestID) } func (s *historyBuilderSuite) validateWorkflowExecutionSignaledEvent( event *types.HistoryEvent, eventID int64, signalName string, input []byte, identity string, requestID string, ) { s.NotNil(event) s.Equal(types.EventTypeWorkflowExecutionSignaled, *event.EventType) s.Equal(eventID, event.ID) attributes := event.WorkflowExecutionSignaledEventAttributes s.Equal(signalName, attributes.SignalName) s.Equal(input, attributes.Input) s.Equal(identity, attributes.Identity) s.Equal(requestID, attributes.RequestID) } func (s *historyBuilderSuite) validateTimerStartedEvent(event *types.HistoryEvent, eventID, decisionTaskCompleted, startToFireTimeoutSeconds int64, timerID string) { s.NotNil(event) s.Equal(types.EventTypeTimerStarted, *event.EventType) s.Equal(eventID, event.ID) attributes := event.TimerStartedEventAttributes s.NotNil(attributes) s.Equal(timerID, attributes.TimerID) s.Equal(decisionTaskCompleted, attributes.DecisionTaskCompletedEventID) s.Equal(startToFireTimeoutSeconds, *attributes.StartToFireTimeoutSeconds) } func (s *historyBuilderSuite) validateTimerFiredEvent(event *types.HistoryEvent, eventID, startedEventID int64, timerID string) { s.NotNil(event) s.Equal(types.EventTypeTimerFired, *event.EventType) s.Equal(eventID, event.ID) attributes := event.TimerFiredEventAttributes s.NotNil(attributes) s.Equal(timerID, attributes.TimerID) s.Equal(startedEventID, attributes.StartedEventID) } func (s *historyBuilderSuite) validateTimerCanceledEvent(event *types.HistoryEvent, eventID, startedEventID, decisionTaskCompletedID int64, timerID, identity string) { s.NotNil(event) s.Equal(types.EventTypeTimerCanceled, *event.EventType) s.Equal(eventID, event.ID) attributes := event.TimerCanceledEventAttributes s.NotNil(attributes) s.Equal(timerID, attributes.TimerID) s.Equal(startedEventID, attributes.StartedEventID) s.Equal(decisionTaskCompletedID, attributes.DecisionTaskCompletedEventID) s.Equal(identity, attributes.Identity) } func (s *historyBuilderSuite) validateTimerCancelFailedEvent(event *types.HistoryEvent, eventID, decisionTaskCompletedID int64, timerID, identity string) { s.NotNil(event) s.Equal(types.EventTypeCancelTimerFailed, *event.EventType) s.Equal(eventID, event.ID) attributes := event.CancelTimerFailedEventAttributes s.NotNil(attributes) s.Equal(timerID, attributes.TimerID) s.Equal(decisionTaskCompletedID, attributes.DecisionTaskCompletedEventID) s.Equal(identity, attributes.Identity) s.Equal(timerCancellationMsgTimerIDUnknown, attributes.Cause) } func (s *historyBuilderSuite) printHistory() string { return thrift.FromHistory(s.builder.GetHistory()).String() } ================================================ FILE: service/history/execution/integrity.go ================================================ // Copyright (c) 2022 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/collection" persistenceutils "github.com/uber/cadence/common/persistence/persistence-utils" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/shard" ) // GetResurrectedTimers returns a set of timers (timer IDs) that were resurrected. // Meaning timers that are still pending in mutable state, but were already completed based on event history. func GetResurrectedTimers( ctx context.Context, shard shard.Context, mutableState MutableState, ) (map[string]struct{}, error) { // 1. check if there is any pending timer pendingTimerInfos := mutableState.GetPendingTimerInfos() if len(pendingTimerInfos) == 0 { return map[string]struct{}{}, nil } // 2. scan history from the beginning and see if any // TimerFiredEvent or TimerCancelledEvent matches pending timer // NOTE: We can't read from the middle of events branch, because // we don't know the last txn id of previous event from the middle. // Reading from the middle could get invalid nodes with invalid txn ids. resurrectedTimer := make(map[string]struct{}) branchToken, err := mutableState.GetCurrentBranchToken() if err != nil { return nil, err } domainID := mutableState.GetExecutionInfo().DomainID iter := collection.NewPagingIterator(getHistoryPaginationFn( ctx, shard, 1, mutableState.GetNextEventID(), branchToken, domainID, )) for iter.HasNext() { item, err := iter.Next() if err != nil { return nil, err } event := item.(*types.HistoryEvent) var timerID string switch event.GetEventType() { case types.EventTypeTimerFired: timerID = event.TimerFiredEventAttributes.TimerID case types.EventTypeTimerCanceled: timerID = event.TimerCanceledEventAttributes.TimerID } if _, ok := pendingTimerInfos[timerID]; ok && timerID != "" { resurrectedTimer[timerID] = struct{}{} } } return resurrectedTimer, nil } // GetResurrectedActivities returns a set of activities (schedule IDs) that were resurrected. // Meaning activities that are still pending in mutable state, but were already completed based on event history. func GetResurrectedActivities( ctx context.Context, shard shard.Context, mutableState MutableState, ) (map[int64]struct{}, error) { // 1. check if there is any pending activity pendingActivityInfos := mutableState.GetPendingActivityInfos() if len(pendingActivityInfos) == 0 { return map[int64]struct{}{}, nil } // 2. scan history from the beginning and see if any // activity termination events matches pending activity // NOTE: We can't read from the middle of events branch, because // we don't know the last txn id of previous event from the middle. // Reading from the middle could get invalid nodes with invalid txn ids. resurrectedActivity := make(map[int64]struct{}) branchToken, err := mutableState.GetCurrentBranchToken() if err != nil { return nil, err } domainID := mutableState.GetExecutionInfo().DomainID iter := collection.NewPagingIterator(getHistoryPaginationFn( ctx, shard, 1, mutableState.GetNextEventID(), branchToken, domainID, )) for iter.HasNext() { item, err := iter.Next() if err != nil { return nil, err } event := item.(*types.HistoryEvent) var scheduledID int64 switch event.GetEventType() { case types.EventTypeActivityTaskCompleted: scheduledID = event.ActivityTaskCompletedEventAttributes.ScheduledEventID case types.EventTypeActivityTaskFailed: scheduledID = event.ActivityTaskFailedEventAttributes.ScheduledEventID case types.EventTypeActivityTaskTimedOut: scheduledID = event.ActivityTaskTimedOutEventAttributes.ScheduledEventID case types.EventTypeActivityTaskCanceled: scheduledID = event.ActivityTaskCanceledEventAttributes.ScheduledEventID } if _, ok := pendingActivityInfos[scheduledID]; ok && scheduledID != 0 { resurrectedActivity[scheduledID] = struct{}{} } } return resurrectedActivity, nil } func getHistoryPaginationFn( ctx context.Context, shard shard.Context, firstEventID int64, nextEventID int64, branchToken []byte, domainID string, ) collection.PaginationFn { domainCache := shard.GetDomainCache() return func(token []byte) ([]interface{}, []byte, error) { historyEvents, _, token, _, err := persistenceutils.PaginateHistory( ctx, shard.GetHistoryManager(), false, branchToken, firstEventID, nextEventID, token, NDCDefaultPageSize, common.IntPtr(shard.GetShardID()), domainID, domainCache, ) if err != nil { return nil, nil, err } var items []interface{} for _, event := range historyEvents { items = append(items, event) } return items, token, nil } } ================================================ FILE: service/history/execution/integrity_test.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/shard" ) func TestGetResurrectedTimers(t *testing.T) { tests := []struct { name string setup func(mockShard *shard.MockContext, mockMutableState *MockMutableState, domainCache *cache.MockDomainCache, manager *persistence.MockHistoryManager) want map[string]struct{} wantErr bool }{ { name: "No pending timers", setup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, domainCache *cache.MockDomainCache, manager *persistence.MockHistoryManager) { mockMutableState.EXPECT().GetPendingTimerInfos().Return(map[string]*persistence.TimerInfo{}).Times(1) }, want: map[string]struct{}{}, }, { name: "Timers with no corresponding events", setup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, domainCache *cache.MockDomainCache, manager *persistence.MockHistoryManager) { mockMutableState.EXPECT().GetPendingTimerInfos().Return(map[string]*persistence.TimerInfo{ "timer1": { TimerID: "timer1", ExpiryTime: clock.NewRealTimeSource().Now().Add(10 * time.Minute), }, }).Times(1) mockMutableState.EXPECT().GetCurrentBranchToken().Return([]byte("branchToken"), nil).Times(1) mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "testDomain"}).Times(1) mockMutableState.EXPECT().GetNextEventID().Return(int64(10)).Times(1) mockShard.EXPECT().GetHistoryManager().Return(manager).Times(1) manager.EXPECT().GetHistoryTree(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTreeResponse{ Branches: []*workflow.HistoryBranch{ {TreeID: common.StringPtr("treeID1"), BranchID: common.StringPtr("branchID1"), Ancestors: []*workflow.HistoryBranchRange{}}, }, }, nil).AnyTimes() manager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{}, }, nil).Times(1) mockShard.EXPECT().GetShardID().Return(1).Times(1) mockShard.EXPECT().GetDomainCache().Return(domainCache).Times(1) domainCache.EXPECT().GetDomainName("testDomain").Return("Test Domain", nil).Times(1) }, want: map[string]struct{}{}, }, { name: "Error on fetching branch token", setup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, domainCache *cache.MockDomainCache, manager *persistence.MockHistoryManager) { mockMutableState.EXPECT().GetPendingTimerInfos().Return(map[string]*persistence.TimerInfo{"timer1": { TimerID: "timer1", ExpiryTime: clock.NewRealTimeSource().Now().Add(10 * time.Minute), }}).Times(1) mockMutableState.EXPECT().GetCurrentBranchToken().Return(nil, errors.New("error fetching token")).Times(1) }, wantErr: true, }, { name: "Processing multiple events", setup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, domainCache *cache.MockDomainCache, manager *persistence.MockHistoryManager) { mockMutableState.EXPECT().GetPendingTimerInfos().Return(map[string]*persistence.TimerInfo{ "timer1": {TimerID: "timer1", ExpiryTime: clock.NewRealTimeSource().Now().Add(10 * time.Minute)}, "timer2": {TimerID: "timer2", ExpiryTime: clock.NewRealTimeSource().Now().Add(10 * time.Minute)}, }).Times(1) mockMutableState.EXPECT().GetCurrentBranchToken().Return(nil, nil).Times(1) mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "testDomain"}).Times(1) mockMutableState.EXPECT().GetNextEventID().Return(int64(10)).Times(1) mockShard.EXPECT().GetShardID().Return(1).Times(1) mockShard.EXPECT().GetDomainCache().Return(domainCache).Times(1) domainCache.EXPECT().GetDomainName("testDomain").Return("testDomain", nil).Times(1) eventTypeTimerFired := types.EventTypeTimerFired eventTypeTimerCanceled := types.EventTypeTimerCanceled events := []*types.HistoryEvent{ {EventType: &eventTypeTimerFired, TimerFiredEventAttributes: &types.TimerFiredEventAttributes{TimerID: "timer1"}}, {EventType: &eventTypeTimerCanceled, TimerCanceledEventAttributes: &types.TimerCanceledEventAttributes{TimerID: "timer2"}}, } mockShard.EXPECT().GetHistoryManager().Return(manager).Times(1) manager.EXPECT().GetHistoryTree(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTreeResponse{ Branches: []*workflow.HistoryBranch{ {TreeID: common.StringPtr("treeID"), BranchID: common.StringPtr("branchID")}, }}, nil).AnyTimes() manager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: events, }, nil).Times(1) }, want: map[string]struct{}{"timer1": {}, "timer2": {}}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockMutableState := NewMockMutableState(mockCtrl) mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockHistoryManager := persistence.NewMockHistoryManager(mockCtrl) tc.setup(mockShard, mockMutableState, mockDomainCache, mockHistoryManager) ctx := context.Background() resurrectedTimers, err := GetResurrectedTimers(ctx, mockShard, mockMutableState) if tc.wantErr { assert.Error(t, err, "GetResurrectedTimers() should have returned an error") } else { assert.NoError(t, err, "GetResurrectedTimers() should not have returned an error") assert.Equal(t, tc.want, resurrectedTimers, "Mismatch in expected and actual resurrected timers") } }) } } func TestGetResurrectedActivities(t *testing.T) { tests := []struct { name string setup func(mockShard *shard.MockContext, mockMutableState *MockMutableState, mockHistoryManager *persistence.MockHistoryManager, mockDomainCache *cache.MockDomainCache) want map[int64]struct{} wantErr bool }{ { name: "No pending activities", setup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, mockHistoryManager *persistence.MockHistoryManager, mockDomainCache *cache.MockDomainCache) { mockMutableState.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{}).Times(1) }, want: map[int64]struct{}{}, }, { name: "With pending activities and matching events", setup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, mockHistoryManager *persistence.MockHistoryManager, mockDomainCache *cache.MockDomainCache) { pendingActivities := map[int64]*persistence.ActivityInfo{ 1: {ScheduleID: 1}, 2: {ScheduleID: 2}, 3: {ScheduleID: 3}, 4: {ScheduleID: 4}, } mockMutableState.EXPECT().GetPendingActivityInfos().Return(pendingActivities).Times(1) mockMutableState.EXPECT().GetCurrentBranchToken().Return([]byte("branchToken"), nil).Times(1) mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "testDomain"}).Times(1) mockMutableState.EXPECT().GetNextEventID().Return(int64(10)).Times(1) taskCompleted := types.EventTypeActivityTaskCompleted taskFailed := types.EventTypeActivityTaskFailed taskTimedOut := types.EventTypeActivityTaskTimedOut taskCanceled := types.EventTypeActivityTaskCanceled events := []*types.HistoryEvent{ {EventType: &taskCompleted, ActivityTaskCompletedEventAttributes: &types.ActivityTaskCompletedEventAttributes{ScheduledEventID: 1}}, {EventType: &taskFailed, ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ScheduledEventID: 2}}, {EventType: &taskTimedOut, ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ScheduledEventID: 3}}, {EventType: &taskCanceled, ActivityTaskCanceledEventAttributes: &types.ActivityTaskCanceledEventAttributes{ScheduledEventID: 4}}, } mockShard.EXPECT().GetShardID().Return(1).Times(1) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).Times(1) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("testDomain", nil).Times(1) mockShard.EXPECT().GetHistoryManager().Return(mockHistoryManager).Times(1) mockHistoryManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: events, }, nil).AnyTimes() }, want: map[int64]struct{}{1: {}, 2: {}, 3: {}, 4: {}}, }, { name: "Error fetching branch token", setup: func(mockShard *shard.MockContext, mockMutableState *MockMutableState, mockHistoryManager *persistence.MockHistoryManager, mockDomainCache *cache.MockDomainCache) { mockMutableState.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{1: {ScheduleID: 1}}).Times(1) mockMutableState.EXPECT().GetCurrentBranchToken().Return(nil, errors.New("error fetching token")).Times(1) }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockShard := shard.NewMockContext(mockCtrl) mockMutableState := NewMockMutableState(mockCtrl) mockHistoryManager := persistence.NewMockHistoryManager(mockCtrl) mockDomainCache := cache.NewMockDomainCache(mockCtrl) tc.setup(mockShard, mockMutableState, mockHistoryManager, mockDomainCache) ctx := context.Background() got, err := GetResurrectedActivities(ctx, mockShard, mockMutableState) if tc.wantErr { assert.Error(t, err, "GetResurrectedActivities() should have returned an error") } else { assert.NoError(t, err, "GetResurrectedActivities() should not have returned an error") assert.Equal(t, tc.want, got, "Mismatch in expected and actual resurrected activities") } }) } } ================================================ FILE: service/history/execution/mutable_state.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination mutable_state_mock.go -self_package github.com/uber/cadence/service/history/execution package execution import ( "context" "time" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/query" ) type ( // DecisionInfo should be part of persistence layer DecisionInfo struct { Version int64 ScheduleID int64 StartedID int64 RequestID string DecisionTimeout int32 TaskList string // This is only needed to communicate tasklist used after AddDecisionTaskScheduledEvent Attempt int64 // Scheduled and Started timestamps are useful for transient decision: when transient decision finally completes, // use these timestamp to create scheduled/started events. // Also used for recording latency metrics ScheduledTimestamp int64 StartedTimestamp int64 // OriginalScheduledTimestamp is to record the first scheduled decision during decision heartbeat. // Client may heartbeat decision by RespondDecisionTaskComplete with ForceCreateNewDecisionTask == true // In this case, OriginalScheduledTimestamp won't change. Then when current time - OriginalScheduledTimestamp exceeds // some threshold, server can interrupt the heartbeat by enforcing to timeout the decision. OriginalScheduledTimestamp int64 } // MutableState contains the current workflow execution state MutableState interface { AddActivityTaskCancelRequestedEvent(int64, string, string) (*types.HistoryEvent, *persistence.ActivityInfo, error) AddActivityTaskCanceledEvent(int64, int64, int64, []uint8, string) (*types.HistoryEvent, error) AddActivityTaskCompletedEvent(int64, int64, *types.RespondActivityTaskCompletedRequest) (*types.HistoryEvent, error) AddActivityTaskFailedEvent(int64, int64, *types.RespondActivityTaskFailedRequest) (*types.HistoryEvent, error) AddActivityTaskScheduledEvent(context.Context, int64, *types.ScheduleActivityTaskDecisionAttributes, bool) (*types.HistoryEvent, *persistence.ActivityInfo, *types.ActivityLocalDispatchInfo, bool, bool, error) AddActivityTaskStartedEvent(*persistence.ActivityInfo, int64, string, string) (*types.HistoryEvent, error) AddActivityTaskTimedOutEvent(int64, int64, types.TimeoutType, []uint8) (*types.HistoryEvent, error) AddCancelTimerFailedEvent(int64, *types.CancelTimerDecisionAttributes, string) (*types.HistoryEvent, error) AddChildWorkflowExecutionCanceledEvent(int64, *types.WorkflowExecution, *types.WorkflowExecutionCanceledEventAttributes) (*types.HistoryEvent, error) AddChildWorkflowExecutionCompletedEvent(int64, *types.WorkflowExecution, *types.WorkflowExecutionCompletedEventAttributes) (*types.HistoryEvent, error) AddChildWorkflowExecutionFailedEvent(int64, *types.WorkflowExecution, *types.WorkflowExecutionFailedEventAttributes) (*types.HistoryEvent, error) AddChildWorkflowExecutionStartedEvent(string, *types.WorkflowExecution, *types.WorkflowType, int64, *types.Header) (*types.HistoryEvent, error) AddChildWorkflowExecutionTerminatedEvent(int64, *types.WorkflowExecution, *types.WorkflowExecutionTerminatedEventAttributes) (*types.HistoryEvent, error) AddChildWorkflowExecutionTimedOutEvent(int64, *types.WorkflowExecution, *types.WorkflowExecutionTimedOutEventAttributes) (*types.HistoryEvent, error) AddCompletedWorkflowEvent(int64, *types.CompleteWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, error) AddContinueAsNewEvent(context.Context, int64, int64, string, *types.ContinueAsNewWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, MutableState, error) AddDecisionTaskCompletedEvent(int64, int64, *types.RespondDecisionTaskCompletedRequest, int) (*types.HistoryEvent, error) AddDecisionTaskFailedEvent(scheduleEventID int64, startedEventID int64, cause types.DecisionTaskFailedCause, details []byte, identity, reason, binChecksum, baseRunID, newRunID string, forkEventVersion int64, resetRequestID string) (*types.HistoryEvent, error) AddDecisionTaskScheduleToStartTimeoutEvent(int64) (*types.HistoryEvent, error) AddDecisionTaskResetTimeoutEvent(int64, string, string, int64, string, string) (*types.HistoryEvent, error) AddFirstDecisionTaskScheduled(*types.HistoryEvent) error AddDecisionTaskScheduledEvent(bypassTaskGeneration bool) (*DecisionInfo, error) AddDecisionTaskScheduledEventAsHeartbeat(bypassTaskGeneration bool, originalScheduledTimestamp int64) (*DecisionInfo, error) AddDecisionTaskStartedEvent(int64, string, *types.PollForDecisionTaskRequest) (*types.HistoryEvent, *DecisionInfo, error) AddDecisionTaskTimedOutEvent(int64, int64) (*types.HistoryEvent, error) AddExternalWorkflowExecutionCancelRequested(int64, string, string, string) (*types.HistoryEvent, error) AddExternalWorkflowExecutionSignaled(int64, string, string, string, []uint8) (*types.HistoryEvent, error) AddFailWorkflowEvent(int64, *types.FailWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, error) AddRecordMarkerEvent(int64, *types.RecordMarkerDecisionAttributes) (*types.HistoryEvent, error) AddRequestCancelActivityTaskFailedEvent(int64, string, string) (*types.HistoryEvent, error) AddRequestCancelExternalWorkflowExecutionFailedEvent(int64, int64, string, string, string, types.CancelExternalWorkflowExecutionFailedCause) (*types.HistoryEvent, error) AddRequestCancelExternalWorkflowExecutionInitiatedEvent(int64, string, *types.RequestCancelExternalWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, *persistence.RequestCancelInfo, error) AddSignalExternalWorkflowExecutionFailedEvent(int64, int64, string, string, string, []uint8, types.SignalExternalWorkflowExecutionFailedCause) (*types.HistoryEvent, error) AddSignalExternalWorkflowExecutionInitiatedEvent(int64, string, *types.SignalExternalWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, *persistence.SignalInfo, error) AddSignalRequested(requestID string) AddStartChildWorkflowExecutionFailedEvent(int64, types.ChildWorkflowExecutionFailedCause, *types.StartChildWorkflowExecutionInitiatedEventAttributes) (*types.HistoryEvent, error) AddStartChildWorkflowExecutionInitiatedEvent(int64, string, *types.StartChildWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, *persistence.ChildExecutionInfo, error) AddTimeoutWorkflowEvent(int64) (*types.HistoryEvent, error) AddTimerCanceledEvent(int64, *types.CancelTimerDecisionAttributes, string) (*types.HistoryEvent, error) AddTimerFiredEvent(string) (*types.HistoryEvent, error) AddTimerStartedEvent(int64, *types.StartTimerDecisionAttributes) (*types.HistoryEvent, *persistence.TimerInfo, error) AddUpsertWorkflowSearchAttributesEvent(int64, *types.UpsertWorkflowSearchAttributesDecisionAttributes) (*types.HistoryEvent, error) AddWorkflowExecutionCancelRequestedEvent(string, *types.HistoryRequestCancelWorkflowExecutionRequest) (*types.HistoryEvent, error) AddWorkflowExecutionCanceledEvent(int64, *types.CancelWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, error) AddWorkflowExecutionSignaled(signalName string, input []byte, identity string, reqeustID string) (*types.HistoryEvent, error) AddWorkflowExecutionStartedEvent(types.WorkflowExecution, *types.HistoryStartWorkflowExecutionRequest) (*types.HistoryEvent, error) AddWorkflowExecutionTerminatedEvent(firstEventID int64, reason string, details []byte, identity string) (*types.HistoryEvent, error) ClearStickyness() CheckResettable() error CopyToPersistence() *persistence.WorkflowMutableState RetryActivity(ai *persistence.ActivityInfo, failureReason string, failureDetails []byte) (bool, error) CreateNewHistoryEvent(eventType types.EventType) *types.HistoryEvent CreateNewHistoryEventWithTimestamp(eventType types.EventType, timestamp int64) *types.HistoryEvent CreateTransientDecisionEvents(di *DecisionInfo, identity string) (*types.HistoryEvent, *types.HistoryEvent) DeleteDecision() DeleteUserTimer(timerID string) error DeleteActivity(scheduleEventID int64) error DeleteSignalRequested(requestID string) FailDecision(bool) FlushBufferedEvents() error GetActivityByActivityID(string) (*persistence.ActivityInfo, bool) GetActivityInfo(int64) (*persistence.ActivityInfo, bool) GetActivityScheduledEvent(context.Context, int64) (*types.HistoryEvent, error) GetChildExecutionInfo(int64) (*persistence.ChildExecutionInfo, bool) GetChildExecutionInitiatedEvent(context.Context, int64) (*types.HistoryEvent, error) GetCompletionEvent(context.Context) (*types.HistoryEvent, error) GetDecisionInfo(int64) (*DecisionInfo, bool) GetDecisionScheduleToStartTimeout() time.Duration GetDomainEntry() *cache.DomainCacheEntry GetStartEvent(context.Context) (*types.HistoryEvent, error) GetCurrentBranchToken() ([]byte, error) GetVersionHistories() *persistence.VersionHistories GetCurrentVersion() int64 GetExecutionInfo() *persistence.WorkflowExecutionInfo GetHistoryBuilder() *HistoryBuilder GetInFlightDecision() (*DecisionInfo, bool) GetPendingDecision() (*DecisionInfo, bool) GetLastFirstEventID() int64 GetLastWriteVersion() (int64, error) GetNextEventID() int64 GetPreviousStartedEventID() int64 GetPendingActivityInfos() map[int64]*persistence.ActivityInfo GetPendingTimerInfos() map[string]*persistence.TimerInfo GetPendingChildExecutionInfos() map[int64]*persistence.ChildExecutionInfo GetPendingRequestCancelExternalInfos() map[int64]*persistence.RequestCancelInfo GetPendingSignalExternalInfos() map[int64]*persistence.SignalInfo GetRequestCancelInfo(int64) (*persistence.RequestCancelInfo, bool) GetRetryBackoffDuration(errReason string) time.Duration GetCronBackoffDuration(context.Context) (time.Duration, error) GetSignalInfo(int64) (*persistence.SignalInfo, bool) GetStartVersion() (int64, error) GetUserTimerInfoByEventID(int64) (*persistence.TimerInfo, bool) GetUserTimerInfo(string) (*persistence.TimerInfo, bool) GetWorkflowType() *types.WorkflowType GetWorkflowStateCloseStatus() (int, int) GetQueryRegistry() query.Registry SetQueryRegistry(query.Registry) HasBufferedEvents() bool HasInFlightDecision() bool HasParentExecution() bool HasPendingDecision() bool HasProcessedOrPendingDecision() bool IsCancelRequested() (bool, string) IsCurrentWorkflowGuaranteed() bool IsSignalRequested(requestID string) bool IsStickyTaskListEnabled() bool IsWorkflowExecutionRunning() bool IsWorkflowCompleted() bool IsResourceDuplicated(resourceDedupKey definition.DeduplicationID) bool UpdateDuplicatedResource(resourceDedupKey definition.DeduplicationID) Load(context.Context, *persistence.WorkflowMutableState) error ReplicateActivityInfo(*types.SyncActivityRequest, bool) error ReplicateActivityTaskCancelRequestedEvent(*types.HistoryEvent) error ReplicateActivityTaskCanceledEvent(*types.HistoryEvent) error ReplicateActivityTaskCompletedEvent(*types.HistoryEvent) error ReplicateActivityTaskFailedEvent(*types.HistoryEvent) error ReplicateActivityTaskScheduledEvent(int64, *types.HistoryEvent, bool) (*persistence.ActivityInfo, error) ReplicateActivityTaskStartedEvent(*types.HistoryEvent) error ReplicateActivityTaskTimedOutEvent(*types.HistoryEvent) error ReplicateChildWorkflowExecutionCanceledEvent(*types.HistoryEvent) error ReplicateChildWorkflowExecutionCompletedEvent(*types.HistoryEvent) error ReplicateChildWorkflowExecutionFailedEvent(*types.HistoryEvent) error ReplicateChildWorkflowExecutionStartedEvent(*types.HistoryEvent) error ReplicateChildWorkflowExecutionTerminatedEvent(*types.HistoryEvent) error ReplicateChildWorkflowExecutionTimedOutEvent(*types.HistoryEvent) error ReplicateDecisionTaskCompletedEvent(*types.HistoryEvent) error ReplicateDecisionTaskFailedEvent(*types.HistoryEvent) error ReplicateDecisionTaskScheduledEvent(int64, int64, string, int32, int64, int64, int64, bool) (*DecisionInfo, error) ReplicateDecisionTaskStartedEvent(*DecisionInfo, int64, int64, int64, string, int64) (*DecisionInfo, error) ReplicateDecisionTaskTimedOutEvent(*types.HistoryEvent) error ReplicateExternalWorkflowExecutionCancelRequested(*types.HistoryEvent) error ReplicateExternalWorkflowExecutionSignaled(*types.HistoryEvent) error ReplicateRequestCancelExternalWorkflowExecutionFailedEvent(*types.HistoryEvent) error ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent(int64, *types.HistoryEvent, string) (*persistence.RequestCancelInfo, error) ReplicateSignalExternalWorkflowExecutionFailedEvent(*types.HistoryEvent) error ReplicateSignalExternalWorkflowExecutionInitiatedEvent(int64, *types.HistoryEvent, string) (*persistence.SignalInfo, error) ReplicateStartChildWorkflowExecutionFailedEvent(*types.HistoryEvent) error ReplicateStartChildWorkflowExecutionInitiatedEvent(int64, *types.HistoryEvent, string) (*persistence.ChildExecutionInfo, error) ReplicateTimerCanceledEvent(*types.HistoryEvent) error ReplicateTimerFiredEvent(*types.HistoryEvent) error ReplicateTimerStartedEvent(*types.HistoryEvent) (*persistence.TimerInfo, error) ReplicateTransientDecisionTaskScheduled() error ReplicateUpsertWorkflowSearchAttributesEvent(*types.HistoryEvent) error ReplicateWorkflowExecutionCancelRequestedEvent(*types.HistoryEvent) error ReplicateWorkflowExecutionCanceledEvent(int64, *types.HistoryEvent) error ReplicateWorkflowExecutionCompletedEvent(int64, *types.HistoryEvent) error ReplicateWorkflowExecutionContinuedAsNewEvent(int64, string, *types.HistoryEvent) error ReplicateWorkflowExecutionFailedEvent(int64, *types.HistoryEvent) error ReplicateWorkflowExecutionSignaled(*types.HistoryEvent) error ReplicateWorkflowExecutionStartedEvent(*string, types.WorkflowExecution, string, *types.HistoryEvent, bool) error ReplicateWorkflowExecutionTerminatedEvent(int64, *types.HistoryEvent) error ReplicateWorkflowExecutionTimedoutEvent(int64, *types.HistoryEvent) error SetCurrentBranchToken(branchToken []byte) error SetHistoryBuilder(hBuilder *HistoryBuilder) SetHistoryTree(treeID string) error SetVersionHistories(*persistence.VersionHistories) error UpdateActivity(*persistence.ActivityInfo) error UpdateActivityProgress(ai *persistence.ActivityInfo, request *types.RecordActivityTaskHeartbeatRequest) UpdateDecision(*DecisionInfo) UpdateUserTimer(*persistence.TimerInfo) error UpdateCurrentVersion(version int64, forceUpdate bool) error UpdateWorkflowStateCloseStatus(state int, closeStatus int) error AddTransferTasks(transferTasks ...persistence.Task) AddTimerTasks(timerTasks ...persistence.Task) GetTransferTasks() []persistence.Task GetTimerTasks() []persistence.Task DeleteTransferTasks() DeleteTimerTasks() SetUpdateCondition(int64) GetUpdateCondition() int64 StartTransaction(ctx context.Context, entry *cache.DomainCacheEntry, incomingTaskVersion int64) (bool, error) CloseTransactionAsMutation(now time.Time, transactionPolicy TransactionPolicy) (*persistence.WorkflowMutation, []*persistence.WorkflowEvents, error) CloseTransactionAsSnapshot(now time.Time, transactionPolicy TransactionPolicy) (*persistence.WorkflowSnapshot, []*persistence.WorkflowEvents, error) GetHistorySize() int64 SetHistorySize(size int64) cache.Sizeable } ) ================================================ FILE: service/history/execution/mutable_state_builder.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "context" "fmt" "math/rand" "runtime/debug" "time" "github.com/pborman/uuid" "golang.org/x/exp/maps" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/query" "github.com/uber/cadence/service/history/shard" ) const ( mutableStateInvalidHistoryActionMsg = "invalid history builder state for action" mutableStateInvalidHistoryActionMsgTemplate = mutableStateInvalidHistoryActionMsg + ": %v" timerCancellationMsgTimerIDUnknown = "TIMER_ID_UNKNOWN" ) var ( // ErrWorkflowFinished indicates trying to mutate mutable state after workflow finished ErrWorkflowFinished = &types.InternalServiceError{Message: "invalid mutable state action: mutation after finish"} // ErrMissingTimerInfo indicates missing timer info ErrMissingTimerInfo = &types.InternalServiceError{Message: "unable to get timer info"} // ErrMissingActivityInfo indicates missing activity info ErrMissingActivityInfo = &types.InternalServiceError{Message: "unable to get activity info"} // ErrMissingChildWorkflowInfo indicates missing child workflow info ErrMissingChildWorkflowInfo = &types.InternalServiceError{Message: "unable to get child workflow info"} // ErrMissingWorkflowStartEvent indicates missing workflow start event ErrMissingWorkflowStartEvent = &types.InternalServiceError{Message: "unable to get workflow start event"} // ErrMissingWorkflowCompletionEvent indicates missing workflow completion event ErrMissingWorkflowCompletionEvent = &types.InternalServiceError{Message: "unable to get workflow completion event"} // ErrMissingActivityScheduledEvent indicates missing workflow activity scheduled event ErrMissingActivityScheduledEvent = &types.InternalServiceError{Message: "unable to get activity scheduled event"} // ErrMissingChildWorkflowInitiatedEvent indicates missing child workflow initiated event ErrMissingChildWorkflowInitiatedEvent = &types.InternalServiceError{Message: "unable to get child workflow initiated event"} // ErrEventsAfterWorkflowFinish is the error indicating server error trying to write events after workflow finish event ErrEventsAfterWorkflowFinish = &types.InternalServiceError{Message: "error validating last event being workflow finish event"} // ErrMissingVersionHistories is the error indicating cadence failed to process 2dc workflow type. ErrMissingVersionHistories = &types.BadRequestError{Message: "versionHistories is empty, which is required for NDC feature. It's probably from deprecated 2dc workflows"} // ErrTooManyPendingActivities is the error that currently there are too many pending activities in the workflow ErrTooManyPendingActivities = &types.InternalServiceError{Message: "Too many pending activities"} ) type ( mutableStateBuilder struct { pendingActivityInfoIDs map[int64]*persistence.ActivityInfo // Schedule Event ID -> Activity Info. pendingActivityIDToEventID map[string]int64 // Activity ID -> Schedule Event ID of the activity. updateActivityInfos map[int64]*persistence.ActivityInfo // Modified activities from last update. deleteActivityInfos map[int64]struct{} // Deleted activities from last update. syncActivityTasks map[int64]struct{} // Activity to be sync to remote pendingTimerInfoIDs map[string]*persistence.TimerInfo // User Timer ID -> Timer Info. pendingTimerEventIDToID map[int64]string // User Timer Start Event ID -> User Timer ID. updateTimerInfos map[string]*persistence.TimerInfo // Modified timers from last update. deleteTimerInfos map[string]struct{} // Deleted timers from last update. pendingChildExecutionInfoIDs map[int64]*persistence.ChildExecutionInfo // Initiated Event ID -> Child Execution Info updateChildExecutionInfos map[int64]*persistence.ChildExecutionInfo // Modified ChildExecution Infos since last update deleteChildExecutionInfos map[int64]struct{} // Deleted ChildExecution Infos since last update pendingRequestCancelInfoIDs map[int64]*persistence.RequestCancelInfo // Initiated Event ID -> RequestCancelInfo updateRequestCancelInfos map[int64]*persistence.RequestCancelInfo // Modified RequestCancel Infos since last update, for persistence update deleteRequestCancelInfos map[int64]struct{} // Deleted RequestCancel Infos since last update, for persistence update pendingSignalInfoIDs map[int64]*persistence.SignalInfo // Initiated Event ID -> SignalInfo updateSignalInfos map[int64]*persistence.SignalInfo // Modified SignalInfo since last update deleteSignalInfos map[int64]struct{} // Deleted SignalInfos since last update pendingSignalRequestedIDs map[string]struct{} // Set of signaled requestIds updateSignalRequestedIDs map[string]struct{} // Set of signaled requestIds since last update deleteSignalRequestedIDs map[string]struct{} // Deleted signaled requestIds bufferedEvents []*types.HistoryEvent // buffered history events that are already persisted updateBufferedEvents []*types.HistoryEvent // buffered history events that needs to be persisted clearBufferedEvents bool // delete buffered events from persistence // This section includes Workflow Execution Info parameters like StartTimestamp, // which are only visible after the workflow has begun. // However, there are other parameters such as LastEventTimestamp, // which are updated as the execution progresses through various cycles. // It's common to encounter null values for these timestamps initially, // as they are populated once the update cycles are initiated. executionInfo *persistence.WorkflowExecutionInfo // Workflow mutable state info. versionHistories *persistence.VersionHistories // TODO: remove this struct after all 2DC workflows complete replicationState *persistence.ReplicationState hBuilder *HistoryBuilder // in memory only attributes // indicate the current version currentVersion int64 // indicates whether there are buffered events in persistence hasBufferedEventsInDB bool // indicates the workflow state in DB, can be used to calculate // whether this workflow is pointed by current workflow record stateInDB int // indicates the next event ID in DB, for conditional update nextEventIDInDB int64 // domain entry contains a snapshot of domain // NOTE: do not use the failover version inside, use currentVersion above domainEntry *cache.DomainCacheEntry // record if a event has been applied to mutable state // TODO: persist this to db appliedEvents map[string]struct{} insertTransferTasks []persistence.Task insertReplicationTasks []persistence.Task insertTimerTasks []persistence.Task workflowRequests map[persistence.WorkflowRequest]struct{} // do not rely on this, this is only updated on // Load() and closeTransactionXXX methods. So when // a transaction is in progress, this value will be // wrong. This exist primarily for visibility via CLI checksum checksum.Checksum taskGenerator MutableStateTaskGenerator decisionTaskManager mutableStateDecisionTaskManager queryRegistry query.Registry shard shard.Context clusterMetadata cluster.Metadata eventsCache events.Cache config *config.Config timeSource clock.TimeSource logger log.Logger metricsClient metrics.Client pendingActivityWarningSent bool executionStats *persistence.ExecutionStats } ) var _ MutableState = (*mutableStateBuilder)(nil) // NewMutableStateBuilder creates a new workflow mutable state builder func NewMutableStateBuilder( shard shard.Context, logger log.Logger, domainEntry *cache.DomainCacheEntry, ) MutableState { return newMutableStateBuilder(shard, logger, domainEntry, constants.EmptyVersion) } func newMutableStateBuilder( shard shard.Context, logger log.Logger, domainEntry *cache.DomainCacheEntry, currentVersion int64, ) *mutableStateBuilder { s := &mutableStateBuilder{ updateActivityInfos: make(map[int64]*persistence.ActivityInfo), pendingActivityInfoIDs: make(map[int64]*persistence.ActivityInfo), pendingActivityIDToEventID: make(map[string]int64), deleteActivityInfos: make(map[int64]struct{}), syncActivityTasks: make(map[int64]struct{}), pendingTimerInfoIDs: make(map[string]*persistence.TimerInfo), pendingTimerEventIDToID: make(map[int64]string), updateTimerInfos: make(map[string]*persistence.TimerInfo), deleteTimerInfos: make(map[string]struct{}), updateChildExecutionInfos: make(map[int64]*persistence.ChildExecutionInfo), pendingChildExecutionInfoIDs: make(map[int64]*persistence.ChildExecutionInfo), deleteChildExecutionInfos: make(map[int64]struct{}), updateRequestCancelInfos: make(map[int64]*persistence.RequestCancelInfo), pendingRequestCancelInfoIDs: make(map[int64]*persistence.RequestCancelInfo), deleteRequestCancelInfos: make(map[int64]struct{}), updateSignalInfos: make(map[int64]*persistence.SignalInfo), pendingSignalInfoIDs: make(map[int64]*persistence.SignalInfo), deleteSignalInfos: make(map[int64]struct{}), updateSignalRequestedIDs: make(map[string]struct{}), pendingSignalRequestedIDs: make(map[string]struct{}), deleteSignalRequestedIDs: make(map[string]struct{}), currentVersion: currentVersion, hasBufferedEventsInDB: false, stateInDB: persistence.WorkflowStateVoid, nextEventIDInDB: 0, domainEntry: domainEntry, appliedEvents: make(map[string]struct{}), queryRegistry: query.NewRegistry(), shard: shard, clusterMetadata: shard.GetClusterMetadata(), eventsCache: shard.GetEventsCache(), config: shard.GetConfig(), timeSource: shard.GetTimeSource(), logger: logger, metricsClient: shard.GetMetricsClient(), workflowRequests: make(map[persistence.WorkflowRequest]struct{}), } s.executionInfo = &persistence.WorkflowExecutionInfo{ DecisionVersion: constants.EmptyVersion, DecisionScheduleID: constants.EmptyEventID, DecisionStartedID: constants.EmptyEventID, DecisionRequestID: constants.EmptyUUID, DecisionTimeout: 0, NextEventID: constants.FirstEventID, State: persistence.WorkflowStateCreated, CloseStatus: persistence.WorkflowCloseStatusNone, LastProcessedEvent: constants.EmptyEventID, CronOverlapPolicy: types.CronOverlapPolicySkipped, } s.hBuilder = NewHistoryBuilder(s) s.taskGenerator = NewMutableStateTaskGenerator(shard.GetLogger(), shard.GetClusterMetadata(), shard.GetDomainCache(), s) s.decisionTaskManager = newMutableStateDecisionTaskManager(s) s.executionStats = &persistence.ExecutionStats{} return s } // NewMutableStateBuilderWithVersionHistories creates mutable state builder with version history initialized // NOTE: currentVersion should be the failover version of the workflow, which is derived from domain metadata and // the active cluster selection policy of the workflow. For passive workflows, the currentVersion will be overridden by the event version, // so the input doesn't matter for them. func NewMutableStateBuilderWithVersionHistories( shard shard.Context, logger log.Logger, domainEntry *cache.DomainCacheEntry, currentVersion int64, ) MutableState { s := newMutableStateBuilder(shard, logger, domainEntry, currentVersion) s.versionHistories = persistence.NewVersionHistories(&persistence.VersionHistory{}) return s } // NewMutableStateBuilderWithEventV2 is used only in test func NewMutableStateBuilderWithEventV2( shard shard.Context, logger log.Logger, runID string, domainEntry *cache.DomainCacheEntry, ) MutableState { msBuilder := NewMutableStateBuilder(shard, logger, domainEntry) _ = msBuilder.SetHistoryTree(runID) return msBuilder } // NewMutableStateBuilderWithVersionHistoriesWithEventV2 is used only in test func NewMutableStateBuilderWithVersionHistoriesWithEventV2( shard shard.Context, logger log.Logger, version int64, runID string, domainEntry *cache.DomainCacheEntry, ) MutableState { msBuilder := NewMutableStateBuilderWithVersionHistories(shard, logger, domainEntry, domainEntry.GetFailoverVersion()) err := msBuilder.UpdateCurrentVersion(version, false) if err != nil { logger.Error("update current version error", tag.Error(err)) } _ = msBuilder.SetHistoryTree(runID) return msBuilder } // Creates a shallow copy, not safe to use if the copied struct is mutated func (e *mutableStateBuilder) CopyToPersistence() *persistence.WorkflowMutableState { state := &persistence.WorkflowMutableState{} state.ActivityInfos = e.pendingActivityInfoIDs state.TimerInfos = e.pendingTimerInfoIDs state.ChildExecutionInfos = e.pendingChildExecutionInfoIDs state.RequestCancelInfos = e.pendingRequestCancelInfoIDs state.SignalInfos = e.pendingSignalInfoIDs state.SignalRequestedIDs = e.pendingSignalRequestedIDs state.ExecutionInfo = e.executionInfo state.BufferedEvents = e.bufferedEvents state.VersionHistories = e.versionHistories state.Checksum = e.checksum state.ReplicationState = e.replicationState state.ExecutionStats = e.executionStats return state } func (e *mutableStateBuilder) Load( ctx context.Context, state *persistence.WorkflowMutableState, ) error { e.pendingActivityInfoIDs = state.ActivityInfos for _, activityInfo := range state.ActivityInfos { e.pendingActivityIDToEventID[activityInfo.ActivityID] = activityInfo.ScheduleID } e.pendingTimerInfoIDs = state.TimerInfos for _, timerInfo := range state.TimerInfos { e.pendingTimerEventIDToID[timerInfo.StartedID] = timerInfo.TimerID } e.pendingChildExecutionInfoIDs = state.ChildExecutionInfos e.pendingRequestCancelInfoIDs = state.RequestCancelInfos e.pendingSignalInfoIDs = state.SignalInfos e.pendingSignalRequestedIDs = state.SignalRequestedIDs e.executionInfo = state.ExecutionInfo e.bufferedEvents = e.reorderAndFilterDuplicateEvents(state.BufferedEvents, "load") e.currentVersion = constants.EmptyVersion e.hasBufferedEventsInDB = len(e.bufferedEvents) > 0 e.stateInDB = state.ExecutionInfo.State e.nextEventIDInDB = state.ExecutionInfo.NextEventID e.versionHistories = state.VersionHistories // TODO: remove this after all 2DC workflows complete e.replicationState = state.ReplicationState e.checksum = state.Checksum e.executionStats = state.ExecutionStats e.fillForBackwardsCompatibility() if len(state.Checksum.Value) > 0 { switch { case e.shouldInvalidateChecksum(): e.checksum = checksum.Checksum{} e.metricsClient.IncCounter(metrics.WorkflowContextScope, metrics.MutableStateChecksumInvalidated) case e.shouldVerifyChecksum(): if err := verifyMutableStateChecksum(e, state.Checksum); err != nil { // we ignore checksum verification errors for now until this // feature is tested and/or we have mechanisms in place to deal // with these types of errors e.metricsClient.IncCounter(metrics.WorkflowContextScope, metrics.MutableStateChecksumMismatch) e.logError("mutable state checksum mismatch", tag.WorkflowNextEventID(e.executionInfo.NextEventID), tag.WorkflowScheduleID(e.executionInfo.DecisionScheduleID), tag.WorkflowStartedID(e.executionInfo.DecisionStartedID), tag.Dynamic("timerIDs", maps.Keys(e.pendingTimerInfoIDs)), tag.Dynamic("activityIDs", maps.Keys(e.pendingActivityInfoIDs)), tag.Dynamic("childIDs", maps.Keys(e.pendingChildExecutionInfoIDs)), tag.Dynamic("signalIDs", maps.Keys(e.pendingSignalInfoIDs)), tag.Dynamic("cancelIDs", maps.Keys(e.pendingRequestCancelInfoIDs)), tag.Error(err)) if e.enableChecksumFailureRetry() { return err } } } } return nil } func (e *mutableStateBuilder) fillForBackwardsCompatibility() { // With https://github.com/uber/cadence/pull/4601 newly introduced DomainID may not be set for older workflows. // Here we will fill its value based on previously used domain name. for _, info := range e.pendingChildExecutionInfoIDs { if info.DomainID == "" && info.DomainNameDEPRECATED != "" { domainID, err := e.shard.GetDomainCache().GetDomainID(info.DomainNameDEPRECATED) if err != nil { e.logError("failed to fill domainId for pending child executions", tag.Error(err)) } info.DomainID = domainID } } } func (e *mutableStateBuilder) GetCurrentBranchToken() ([]byte, error) { if e.versionHistories != nil { currentVersionHistory, err := e.versionHistories.GetCurrentVersionHistory() if err != nil { return nil, err } return currentVersionHistory.GetBranchToken(), nil } return e.executionInfo.BranchToken, nil } func (e *mutableStateBuilder) GetVersionHistories() *persistence.VersionHistories { return e.versionHistories } // set treeID/historyBranches func (e *mutableStateBuilder) SetHistoryTree( treeID string, ) error { initialBranchToken, err := persistence.NewHistoryBranchToken(treeID) if err != nil { return err } return e.SetCurrentBranchToken(initialBranchToken) } func (e *mutableStateBuilder) SetCurrentBranchToken( branchToken []byte, ) error { exeInfo := e.GetExecutionInfo() if e.versionHistories == nil { exeInfo.BranchToken = branchToken return nil } currentVersionHistory, err := e.versionHistories.GetCurrentVersionHistory() if err != nil { return err } return currentVersionHistory.SetBranchToken(branchToken) } func (e *mutableStateBuilder) SetVersionHistories( versionHistories *persistence.VersionHistories, ) error { e.versionHistories = versionHistories return nil } func (e *mutableStateBuilder) GetHistoryBuilder() *HistoryBuilder { return e.hBuilder } func (e *mutableStateBuilder) SetHistoryBuilder(hBuilder *HistoryBuilder) { e.hBuilder = hBuilder } func (e *mutableStateBuilder) GetExecutionInfo() *persistence.WorkflowExecutionInfo { return e.executionInfo } func (e *mutableStateBuilder) FlushBufferedEvents() error { // put new events into 2 buckets: // 1) if the event was added while there was in-flight decision, then put it in buffered bucket // 2) otherwise, put it in committed bucket var newBufferedEvents []*types.HistoryEvent var newCommittedEvents []*types.HistoryEvent for _, event := range e.hBuilder.history { if event.ID == constants.BufferedEventID { newBufferedEvents = append(newBufferedEvents, event) } else { newCommittedEvents = append(newCommittedEvents, event) } } // no decision in-flight, flush all buffered events to committed bucket if !e.HasInFlightDecision() { var allBufferedEvents []*types.HistoryEvent // flush persisted buffered events if len(e.bufferedEvents) > 0 { allBufferedEvents = append(allBufferedEvents, e.bufferedEvents...) e.bufferedEvents = nil } if e.hasBufferedEventsInDB { e.clearBufferedEvents = true } // flush pending buffered events allBufferedEvents = append(allBufferedEvents, e.updateBufferedEvents...) e.updateBufferedEvents = nil // Resolve issues with persistence duplicating or reordering buffered events reorderedEvents := e.reorderAndFilterDuplicateEvents(allBufferedEvents, "flush") // Put back all the reordered buffer events at the end if len(reorderedEvents) > 0 { newCommittedEvents = append(newCommittedEvents, reorderedEvents...) } // flush new buffered events newCommittedEvents = append(newCommittedEvents, newBufferedEvents...) newBufferedEvents = nil } newCommittedEvents = e.trimEventsAfterWorkflowClose(newCommittedEvents) e.hBuilder.history = newCommittedEvents // make sure all new committed events have correct EventID e.assignEventIDToBufferedEvents() if err := e.assignTaskIDToEvents(); err != nil { return err } // if decision is not closed yet, and there are new buffered events, then put those to the pending buffer if e.HasInFlightDecision() && len(newBufferedEvents) > 0 { e.updateBufferedEvents = newBufferedEvents } return nil } func (e *mutableStateBuilder) UpdateCurrentVersion( version int64, forceUpdate bool, ) error { before := e.currentVersion defer func() { e.logger.Debugf("UpdateCurrentVersion for domain %s, wfID %v, version before: %v, version after: %v, forceUpdate: %v", e.executionInfo.DomainID, e.executionInfo.WorkflowID, before, e.currentVersion, forceUpdate) }() if state, _ := e.GetWorkflowStateCloseStatus(); state == persistence.WorkflowStateCompleted { // always set current version to last write version when workflow is completed lastWriteVersion, err := e.GetLastWriteVersion() if err != nil { return err } e.currentVersion = lastWriteVersion return nil } if e.versionHistories != nil { versionHistory, err := e.versionHistories.GetCurrentVersionHistory() if err != nil { return err } if !versionHistory.IsEmpty() { // this make sure current version >= last write version versionHistoryItem, err := versionHistory.GetLastItem() if err != nil { return err } e.currentVersion = versionHistoryItem.Version } if version > e.currentVersion || forceUpdate { e.currentVersion = version } return nil } e.currentVersion = constants.EmptyVersion return nil } // GetCurrentVersion indicates which cluster this workflow is considered active. func (e *mutableStateBuilder) GetCurrentVersion() int64 { // Legacy TODO: remove this after all 2DC workflows complete if e.replicationState != nil { e.logger.Debugf("GetCurrentVersion replicationState.CurrentVersion=%v", e.replicationState.CurrentVersion) return e.replicationState.CurrentVersion } if e.versionHistories != nil { e.logger.Debugf("GetCurrentVersion versionHistories.CurrentVersion=%v", e.currentVersion) return e.currentVersion } e.logger.Debugf("GetCurrentVersion returning empty version=%v", constants.EmptyVersion) return constants.EmptyVersion } // TODO: Check all usages of this method and address active-active case if needed. func (e *mutableStateBuilder) GetStartVersion() (int64, error) { if e.versionHistories != nil { versionHistory, err := e.versionHistories.GetCurrentVersionHistory() if err != nil { return 0, err } firstItem, err := versionHistory.GetFirstItem() if err != nil { return 0, err } return firstItem.Version, nil } return constants.EmptyVersion, nil } func (e *mutableStateBuilder) GetLastWriteVersion() (int64, error) { // TODO: remove this after all 2DC workflows complete if e.replicationState != nil { return e.replicationState.LastWriteVersion, nil } if e.versionHistories != nil { versionHistory, err := e.versionHistories.GetCurrentVersionHistory() if err != nil { return 0, err } lastItem, err := versionHistory.GetLastItem() if err != nil { return 0, err } return lastItem.Version, nil } return constants.EmptyVersion, nil } func (e *mutableStateBuilder) checkAndClearTimerFiredEvent( timerID string, ) *types.HistoryEvent { var timerEvent *types.HistoryEvent e.bufferedEvents, timerEvent = checkAndClearTimerFiredEvent(e.bufferedEvents, timerID) if timerEvent != nil { return timerEvent } e.updateBufferedEvents, timerEvent = checkAndClearTimerFiredEvent(e.updateBufferedEvents, timerID) if timerEvent != nil { return timerEvent } e.hBuilder.history, timerEvent = checkAndClearTimerFiredEvent(e.hBuilder.history, timerID) return timerEvent } func (e *mutableStateBuilder) trimEventsAfterWorkflowClose( input []*types.HistoryEvent, ) []*types.HistoryEvent { if len(input) == 0 { return input } nextIndex := 0 loop: for _, event := range input { nextIndex++ switch event.GetEventType() { case types.EventTypeWorkflowExecutionCompleted, types.EventTypeWorkflowExecutionFailed, types.EventTypeWorkflowExecutionTimedOut, types.EventTypeWorkflowExecutionTerminated, types.EventTypeWorkflowExecutionContinuedAsNew, types.EventTypeWorkflowExecutionCanceled: break loop } } return input[0:nextIndex] } func (e *mutableStateBuilder) assignEventIDToBufferedEvents() { newCommittedEvents := e.hBuilder.history scheduledIDToStartedID := make(map[int64]int64) for _, event := range newCommittedEvents { if event.ID != constants.BufferedEventID { continue } eventID := e.executionInfo.NextEventID event.ID = eventID e.executionInfo.IncreaseNextEventID() switch event.GetEventType() { case types.EventTypeActivityTaskStarted: attributes := event.ActivityTaskStartedEventAttributes scheduledID := attributes.GetScheduledEventID() scheduledIDToStartedID[scheduledID] = eventID if ai, ok := e.GetActivityInfo(scheduledID); ok { ai.StartedID = eventID e.updateActivityInfos[ai.ScheduleID] = ai } case types.EventTypeChildWorkflowExecutionStarted: attributes := event.ChildWorkflowExecutionStartedEventAttributes initiatedID := attributes.GetInitiatedEventID() scheduledIDToStartedID[initiatedID] = eventID if ci, ok := e.GetChildExecutionInfo(initiatedID); ok { ci.StartedID = eventID e.updateChildExecutionInfos[ci.InitiatedID] = ci } case types.EventTypeActivityTaskCompleted: attributes := event.ActivityTaskCompletedEventAttributes if startedID, ok := scheduledIDToStartedID[attributes.GetScheduledEventID()]; ok { attributes.StartedEventID = startedID } case types.EventTypeActivityTaskFailed: attributes := event.ActivityTaskFailedEventAttributes if startedID, ok := scheduledIDToStartedID[attributes.GetScheduledEventID()]; ok { attributes.StartedEventID = startedID } case types.EventTypeActivityTaskTimedOut: attributes := event.ActivityTaskTimedOutEventAttributes if startedID, ok := scheduledIDToStartedID[attributes.GetScheduledEventID()]; ok { attributes.StartedEventID = startedID } case types.EventTypeActivityTaskCanceled: attributes := event.ActivityTaskCanceledEventAttributes if startedID, ok := scheduledIDToStartedID[attributes.GetScheduledEventID()]; ok { attributes.StartedEventID = startedID } case types.EventTypeChildWorkflowExecutionCompleted: attributes := event.ChildWorkflowExecutionCompletedEventAttributes if startedID, ok := scheduledIDToStartedID[attributes.GetInitiatedEventID()]; ok { attributes.StartedEventID = startedID } case types.EventTypeChildWorkflowExecutionFailed: attributes := event.ChildWorkflowExecutionFailedEventAttributes if startedID, ok := scheduledIDToStartedID[attributes.GetInitiatedEventID()]; ok { attributes.StartedEventID = startedID } case types.EventTypeChildWorkflowExecutionTimedOut: attributes := event.ChildWorkflowExecutionTimedOutEventAttributes if startedID, ok := scheduledIDToStartedID[attributes.GetInitiatedEventID()]; ok { attributes.StartedEventID = startedID } case types.EventTypeChildWorkflowExecutionCanceled: attributes := event.ChildWorkflowExecutionCanceledEventAttributes if startedID, ok := scheduledIDToStartedID[attributes.GetInitiatedEventID()]; ok { attributes.StartedEventID = startedID } case types.EventTypeChildWorkflowExecutionTerminated: attributes := event.ChildWorkflowExecutionTerminatedEventAttributes if startedID, ok := scheduledIDToStartedID[attributes.GetInitiatedEventID()]; ok { attributes.StartedEventID = startedID } } } } func (e *mutableStateBuilder) assignTaskIDToEvents() error { // assign task IDs to all history events // first transient events numTaskIDs := len(e.hBuilder.transientHistory) if numTaskIDs > 0 { taskIDs, err := e.shard.GenerateTaskIDs(numTaskIDs) if err != nil { return err } for index, event := range e.hBuilder.transientHistory { if event.TaskID == constants.EmptyEventTaskID { taskID := taskIDs[index] event.TaskID = taskID e.executionInfo.LastEventTaskID = taskID } } } // then normal events numTaskIDs = len(e.hBuilder.history) if numTaskIDs > 0 { taskIDs, err := e.shard.GenerateTaskIDs(numTaskIDs) if err != nil { return err } for index, event := range e.hBuilder.history { if event.TaskID == constants.EmptyEventTaskID { taskID := taskIDs[index] event.TaskID = taskID e.executionInfo.LastEventTaskID = taskID } } } return nil } func (e *mutableStateBuilder) IsCurrentWorkflowGuaranteed() bool { // stateInDB is used like a bloom filter: // // 1. stateInDB being created / running meaning that this workflow must be the current // workflow (assuming there is no rebuild of mutable state). // 2. stateInDB being completed does not guarantee this workflow being the current workflow // 3. stateInDB being zombie guarantees this workflow not being the current workflow // 4. stateInDB cannot be void, void is only possible when mutable state is just initialized switch e.stateInDB { case persistence.WorkflowStateVoid: return false case persistence.WorkflowStateCreated: return true case persistence.WorkflowStateRunning: return true case persistence.WorkflowStateCompleted: return false case persistence.WorkflowStateZombie: return false case persistence.WorkflowStateCorrupted: return false default: panic(fmt.Sprintf("unknown workflow state: %v", e.executionInfo.State)) } } func (e *mutableStateBuilder) GetDomainEntry() *cache.DomainCacheEntry { return e.domainEntry } func (e *mutableStateBuilder) IsStickyTaskListEnabled() bool { if e.executionInfo.StickyTaskList == "" { return false } ttl := e.config.StickyTTL(e.GetDomainEntry().GetInfo().Name) return !e.timeSource.Now().After(e.executionInfo.LastUpdatedTimestamp.Add(ttl)) } func (e *mutableStateBuilder) CreateNewHistoryEvent( eventType types.EventType, ) *types.HistoryEvent { return e.CreateNewHistoryEventWithTimestamp(eventType, e.timeSource.Now().UnixNano()) } func (e *mutableStateBuilder) CreateNewHistoryEventWithTimestamp( eventType types.EventType, timestamp int64, ) *types.HistoryEvent { eventID := e.executionInfo.NextEventID if e.shouldBufferEvent(eventType) { eventID = constants.BufferedEventID } else { // only increase NextEventID if event is not buffered e.executionInfo.IncreaseNextEventID() } ts := common.Int64Ptr(timestamp) historyEvent := &types.HistoryEvent{} historyEvent.ID = eventID historyEvent.Timestamp = ts historyEvent.EventType = &eventType historyEvent.Version = e.GetCurrentVersion() historyEvent.TaskID = constants.EmptyEventTaskID return historyEvent } func (e *mutableStateBuilder) shouldBufferEvent( eventType types.EventType, ) bool { switch eventType { case // do not buffer for workflow state change types.EventTypeWorkflowExecutionStarted, types.EventTypeWorkflowExecutionCompleted, types.EventTypeWorkflowExecutionFailed, types.EventTypeWorkflowExecutionTimedOut, types.EventTypeWorkflowExecutionTerminated, types.EventTypeWorkflowExecutionContinuedAsNew, types.EventTypeWorkflowExecutionCanceled: return false case // decision event should not be buffered types.EventTypeDecisionTaskScheduled, types.EventTypeDecisionTaskStarted, types.EventTypeDecisionTaskCompleted, types.EventTypeDecisionTaskFailed, types.EventTypeDecisionTaskTimedOut: return false case // events generated directly from decisions should not be buffered // workflow complete, failed, cancelled and continue-as-new events are duplication of above // just put is here for reference // types.EventTypeWorkflowExecutionCompleted, // types.EventTypeWorkflowExecutionFailed, // types.EventTypeWorkflowExecutionCanceled, // types.EventTypeWorkflowExecutionContinuedAsNew, types.EventTypeActivityTaskScheduled, types.EventTypeActivityTaskCancelRequested, types.EventTypeTimerStarted, // DecisionTypeCancelTimer is an exception. This decision will be mapped // to either types.EventTypeTimerCanceled, or types.EventTypeCancelTimerFailed. // So both should not be buffered. Ref: historyEngine, search for "types.DecisionTypeCancelTimer" types.EventTypeTimerCanceled, types.EventTypeCancelTimerFailed, types.EventTypeRequestCancelExternalWorkflowExecutionInitiated, types.EventTypeMarkerRecorded, types.EventTypeStartChildWorkflowExecutionInitiated, types.EventTypeSignalExternalWorkflowExecutionInitiated, types.EventTypeUpsertWorkflowSearchAttributes: // do not buffer event if event is directly generated from a corresponding decision // sanity check there is no decision on the fly if e.HasInFlightDecision() { msg := fmt.Sprintf("history mutable state is processing event: %v while there is decision pending. "+ "domainID: %v, workflow ID: %v, run ID: %v.", eventType, e.executionInfo.DomainID, e.executionInfo.WorkflowID, e.executionInfo.RunID) panic(msg) } return false default: return true } } func (e *mutableStateBuilder) GetWorkflowType() *types.WorkflowType { wType := &types.WorkflowType{} wType.Name = e.executionInfo.WorkflowTypeName return wType } func (e *mutableStateBuilder) GetQueryRegistry() query.Registry { return e.queryRegistry } func (e *mutableStateBuilder) SetQueryRegistry(queryRegistry query.Registry) { e.queryRegistry = queryRegistry } func (e *mutableStateBuilder) GetRetryBackoffDuration( errReason string, ) time.Duration { info := e.executionInfo if !info.HasRetryPolicy { return backoff.NoBackoff } return getBackoffInterval( e.timeSource.Now(), info.ExpirationTime, info.Attempt, info.MaximumAttempts, info.InitialInterval, info.MaximumInterval, info.BackoffCoefficient, errReason, info.NonRetriableErrors, ) } func (e *mutableStateBuilder) GetCronBackoffDuration( ctx context.Context, ) (time.Duration, error) { info := e.executionInfo if len(info.CronSchedule) == 0 { return backoff.NoBackoff, nil } sched, err := backoff.ValidateSchedule(info.CronSchedule) if err != nil { return backoff.NoBackoff, err } // TODO: decide if we can add execution time in execution info. executionTime := e.executionInfo.StartTimestamp // This only call when doing ContinueAsNew. At this point, the workflow should have a start event workflowStartEvent, err := e.GetStartEvent(ctx) if err != nil { e.logError("unable to find workflow start event", tag.ErrorTypeInvalidHistoryAction) return backoff.NoBackoff, err } firstDecisionTaskBackoff := time.Duration(workflowStartEvent.GetWorkflowExecutionStartedEventAttributes().GetFirstDecisionTaskBackoffSeconds()) * time.Second executionTime = executionTime.Add(firstDecisionTaskBackoff) jitterStartSeconds := workflowStartEvent.GetWorkflowExecutionStartedEventAttributes().GetJitterStartSeconds() return backoff.GetBackoffForNextSchedule(sched, executionTime, e.timeSource.Now(), jitterStartSeconds, info.CronOverlapPolicy) } // GetStartEvent retrieves the workflow start event from mutable state func (e *mutableStateBuilder) GetStartEvent( ctx context.Context, ) (*types.HistoryEvent, error) { currentBranchToken, err := e.GetCurrentBranchToken() if err != nil { return nil, err } startEvent, err := e.eventsCache.GetEvent( ctx, e.shard.GetShardID(), e.executionInfo.DomainID, e.executionInfo.WorkflowID, e.executionInfo.RunID, constants.FirstEventID, constants.FirstEventID, currentBranchToken, ) if err != nil { // do not return the original error // since original error can be of type entity not exists // which can cause task processing side to fail silently // However, if the error is a persistence transient error, // we return the original error, because we fail to get // the event because of failure from database if persistence.IsTransientError(err) { return nil, err } return nil, ErrMissingWorkflowStartEvent } return startEvent, nil } // DeletePendingRequestCancel deletes details about a RequestCancelInfo. func (e *mutableStateBuilder) DeletePendingRequestCancel( initiatedEventID int64, ) error { if _, ok := e.pendingRequestCancelInfoIDs[initiatedEventID]; ok { delete(e.pendingRequestCancelInfoIDs, initiatedEventID) } else { e.logError( fmt.Sprintf("unable to find request cancel external workflow event ID: %v in mutable state", initiatedEventID), tag.ErrorTypeInvalidMutableStateAction, ) // log data inconsistency instead of returning an error e.logDataInconsistency() } delete(e.updateRequestCancelInfos, initiatedEventID) e.deleteRequestCancelInfos[initiatedEventID] = struct{}{} return nil } func (e *mutableStateBuilder) writeEventToCache( event *types.HistoryEvent, ) { // For start event: store it within events cache so the recordWorkflowStarted transfer task doesn't need to // load it from database // For completion event: store it within events cache so we can communicate the result to parent execution // during the processing of DeleteTransferTask without loading this event from database e.eventsCache.PutEvent( e.executionInfo.DomainID, e.executionInfo.WorkflowID, e.executionInfo.RunID, event.ID, event, ) } func (e *mutableStateBuilder) HasParentExecution() bool { return e.executionInfo.ParentDomainID != "" && e.executionInfo.ParentWorkflowID != "" } // GetUserTimerInfoByEventID gives details about a user timer. func (e *mutableStateBuilder) GetUserTimerInfoByEventID( startEventID int64, ) (*persistence.TimerInfo, bool) { timerID, ok := e.pendingTimerEventIDToID[startEventID] if !ok { return nil, false } return e.GetUserTimerInfo(timerID) } func (e *mutableStateBuilder) GetPendingRequestCancelExternalInfos() map[int64]*persistence.RequestCancelInfo { return e.pendingRequestCancelInfoIDs } func (e *mutableStateBuilder) HasBufferedEvents() bool { if len(e.bufferedEvents) > 0 || len(e.updateBufferedEvents) > 0 { return true } for _, event := range e.hBuilder.history { if event.ID == constants.BufferedEventID { return true } } return false } func (e *mutableStateBuilder) ClearStickyness() { e.executionInfo.StickyTaskList = "" e.executionInfo.StickyScheduleToStartTimeout = 0 e.executionInfo.ClientLibraryVersion = "" e.executionInfo.ClientFeatureVersion = "" e.executionInfo.ClientImpl = "" } // GetLastFirstEventID returns last first event ID // first event ID is the ID of a batch of events in a single history events record func (e *mutableStateBuilder) GetLastFirstEventID() int64 { return e.executionInfo.LastFirstEventID } // GetNextEventID returns next event ID func (e *mutableStateBuilder) GetNextEventID() int64 { return e.executionInfo.NextEventID } // GetPreviousStartedEventID returns last started decision task event ID func (e *mutableStateBuilder) GetPreviousStartedEventID() int64 { return e.executionInfo.LastProcessedEvent } func (e *mutableStateBuilder) IsWorkflowExecutionRunning() bool { return e.executionInfo.IsRunning() } func (e *mutableStateBuilder) AddUpsertWorkflowSearchAttributesEvent( decisionCompletedEventID int64, request *types.UpsertWorkflowSearchAttributesDecisionAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionUpsertWorkflowSearchAttributes if err := e.checkMutability(opTag); err != nil { return nil, err } event := e.hBuilder.AddUpsertWorkflowSearchAttributesEvent(decisionCompletedEventID, request) if err := e.ReplicateUpsertWorkflowSearchAttributesEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateUpsertWorkflowSearchAttributesEvent( event *types.HistoryEvent, ) error { upsertSearchAttr := event.UpsertWorkflowSearchAttributesEventAttributes.GetSearchAttributes().GetIndexedFields() currentSearchAttr := e.GetExecutionInfo().SearchAttributes e.executionInfo.SearchAttributes = mergeMapOfByteArray(currentSearchAttr, upsertSearchAttr) return e.taskGenerator.GenerateWorkflowSearchAttrTasks() } func (e *mutableStateBuilder) AddRecordMarkerEvent( decisionCompletedEventID int64, attributes *types.RecordMarkerDecisionAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowRecordMarker if err := e.checkMutability(opTag); err != nil { return nil, err } return e.hBuilder.AddMarkerRecordedEvent(decisionCompletedEventID, attributes), nil } func (e *mutableStateBuilder) AddWorkflowExecutionTerminatedEvent( firstEventID int64, reason string, details []byte, identity string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowTerminated if err := e.checkMutability(opTag); err != nil { return nil, err } event := e.hBuilder.AddWorkflowExecutionTerminatedEvent(reason, details, identity) if err := e.ReplicateWorkflowExecutionTerminatedEvent(firstEventID, event); err != nil { return nil, err } domainName := e.GetDomainEntry().GetInfo().Name e.logger.Info( "Workflow execution terminated.", tag.WorkflowDomainName(domainName), tag.WorkflowID(e.GetExecutionInfo().WorkflowID), tag.WorkflowRunID(e.GetExecutionInfo().RunID), tag.WorkflowTerminationReason(reason), ) scopeWithDomainTag := e.metricsClient.Scope(metrics.HistoryTerminateWorkflowExecutionScope). Tagged(metrics.DomainTag(domainName)). Tagged(metrics.WorkflowTerminationReasonTag(reason)) scopeWithDomainTag.IncCounter(metrics.WorkflowTerminateCounterPerDomain) return event, nil } func (e *mutableStateBuilder) ReplicateWorkflowExecutionTerminatedEvent( firstEventID int64, event *types.HistoryEvent, ) error { if err := e.UpdateWorkflowStateCloseStatus( persistence.WorkflowStateCompleted, persistence.WorkflowCloseStatusTerminated, ); err != nil { return err } e.executionInfo.CompletionEventBatchID = firstEventID // Used when completion event needs to be loaded from database e.ClearStickyness() e.writeEventToCache(event) return e.taskGenerator.GenerateWorkflowCloseTasks(event, e.config.WorkflowDeletionJitterRange(e.domainEntry.GetInfo().Name)) } func (e *mutableStateBuilder) AddContinueAsNewEvent( ctx context.Context, firstEventID int64, decisionCompletedEventID int64, parentDomainName string, attributes *types.ContinueAsNewWorkflowExecutionDecisionAttributes, ) (*types.HistoryEvent, MutableState, error) { opTag := tag.WorkflowActionWorkflowContinueAsNew if err := e.checkMutability(opTag); err != nil { return nil, nil, err } var err error newRunID := uuid.New() newExecution := types.WorkflowExecution{ WorkflowID: e.executionInfo.WorkflowID, RunID: newRunID, } // Extract ParentExecutionInfo from current run so it can be passed down to the next var parentInfo *types.ParentExecutionInfo if e.HasParentExecution() { parentInfo = &types.ParentExecutionInfo{ DomainUUID: e.executionInfo.ParentDomainID, Domain: parentDomainName, Execution: &types.WorkflowExecution{ WorkflowID: e.executionInfo.ParentWorkflowID, RunID: e.executionInfo.ParentRunID, }, InitiatedID: e.executionInfo.InitiatedID, } } continueAsNewEvent := e.hBuilder.AddContinuedAsNewEvent(decisionCompletedEventID, newRunID, attributes) currentStartEvent, err := e.GetStartEvent(ctx) if err != nil { return nil, nil, err } firstRunID := e.executionInfo.FirstExecutionRunID // This is needed for backwards compatibility. Workflow execution create with Cadence release v0.25.0 or earlier // does not have FirstExecutionRunID stored as part of mutable state. If this is not set then load it from // workflow execution started event. if len(firstRunID) == 0 { firstRunID = currentStartEvent.GetWorkflowExecutionStartedEventAttributes().GetFirstExecutionRunID() } firstScheduleTime := currentStartEvent.GetWorkflowExecutionStartedEventAttributes().GetFirstScheduledTime() domainID := e.domainEntry.GetInfo().ID activeClusterInfo, err := e.shard.GetActiveClusterManager().GetActiveClusterInfoByClusterAttribute(ctx, domainID, attributes.ActiveClusterSelectionPolicy.GetClusterAttribute()) if err != nil { return nil, nil, err } if e.logger.DebugOn() { e.logger.Debug("mutableStateBuilder.AddContinueAsNewEvent created newStateBuilder", tag.WorkflowDomainID(domainID), tag.WorkflowID(e.executionInfo.WorkflowID), tag.WorkflowRunID(e.executionInfo.RunID), tag.WorkflowRunID(newRunID), tag.CurrentVersion(e.currentVersion), tag.Dynamic("activecluster-sel-policy", attributes.ActiveClusterSelectionPolicy), tag.Dynamic("activecluster-info", activeClusterInfo), ) } // TODO: improve type casting newStateBuilder := NewMutableStateBuilderWithVersionHistories( e.shard, e.logger, e.domainEntry, activeClusterInfo.FailoverVersion, ).(*mutableStateBuilder) if _, err = newStateBuilder.addWorkflowExecutionStartedEventForContinueAsNew( parentInfo, newExecution, e, attributes, firstRunID, firstScheduleTime, ); err != nil { return nil, nil, &types.InternalServiceError{Message: "Failed to add workflow execution started event."} } if err = e.ReplicateWorkflowExecutionContinuedAsNewEvent( firstEventID, domainID, continueAsNewEvent, ); err != nil { return nil, nil, err } return continueAsNewEvent, newStateBuilder, nil } func rolloverAutoResetPointsWithExpiringTime( resetPoints *types.ResetPoints, prevRunID string, nowNano int64, domainRetentionDays int32, ) *types.ResetPoints { if resetPoints == nil || resetPoints.Points == nil { return resetPoints } newPoints := make([]*types.ResetPointInfo, 0, len(resetPoints.Points)) expiringTimeNano := nowNano + int64(time.Duration(domainRetentionDays)*time.Hour*24) for _, rp := range resetPoints.Points { if rp.GetRunID() == prevRunID { rp.ExpiringTimeNano = common.Int64Ptr(expiringTimeNano) } newPoints = append(newPoints, rp) } return &types.ResetPoints{ Points: newPoints, } } func (e *mutableStateBuilder) ReplicateWorkflowExecutionContinuedAsNewEvent( firstEventID int64, domainID string, continueAsNewEvent *types.HistoryEvent, ) error { if err := e.UpdateWorkflowStateCloseStatus( persistence.WorkflowStateCompleted, persistence.WorkflowCloseStatusContinuedAsNew, ); err != nil { return err } e.executionInfo.CompletionEventBatchID = firstEventID // Used when completion event needs to be loaded from database e.ClearStickyness() e.writeEventToCache(continueAsNewEvent) return e.taskGenerator.GenerateWorkflowCloseTasks(continueAsNewEvent, e.config.WorkflowDeletionJitterRange(e.domainEntry.GetInfo().Name)) } // TODO mutable state should generate corresponding transfer / timer tasks according to // updates accumulated, while currently all transfer / timer tasks are managed manually // TODO convert AddTransferTasks to prepareTransferTasks func (e *mutableStateBuilder) AddTransferTasks( transferTasks ...persistence.Task, ) { e.insertTransferTasks = append(e.insertTransferTasks, transferTasks...) } // TODO convert AddTimerTasks to prepareTimerTasks func (e *mutableStateBuilder) AddTimerTasks( timerTasks ...persistence.Task, ) { e.insertTimerTasks = append(e.insertTimerTasks, timerTasks...) } func (e *mutableStateBuilder) GetTransferTasks() []persistence.Task { return e.insertTransferTasks } func (e *mutableStateBuilder) GetTimerTasks() []persistence.Task { return e.insertTimerTasks } func (e *mutableStateBuilder) DeleteTransferTasks() { e.insertTransferTasks = nil } func (e *mutableStateBuilder) DeleteTimerTasks() { e.insertTimerTasks = nil } func (e *mutableStateBuilder) SetUpdateCondition( nextEventIDInDB int64, ) { e.nextEventIDInDB = nextEventIDInDB } func (e *mutableStateBuilder) GetUpdateCondition() int64 { return e.nextEventIDInDB } func (e *mutableStateBuilder) GetWorkflowStateCloseStatus() (int, int) { executionInfo := e.executionInfo return executionInfo.State, executionInfo.CloseStatus } func (e *mutableStateBuilder) UpdateWorkflowStateCloseStatus( state int, closeStatus int, ) error { return e.executionInfo.UpdateWorkflowStateCloseStatus(state, closeStatus) } func (e *mutableStateBuilder) StartTransaction( ctx context.Context, domainEntry *cache.DomainCacheEntry, incomingTaskVersion int64, ) (bool, error) { activeClusterInfo, err := e.shard.GetActiveClusterManager().GetActiveClusterInfoByWorkflow(ctx, e.executionInfo.DomainID, e.executionInfo.WorkflowID, e.executionInfo.RunID) if err != nil { return false, err } if e.logger.DebugOn() { e.logger.Debugf("StartTransaction calling UpdateCurrentVersion for domain %s, wfID %v, incomingTaskVersion %v, version %v, stacktrace %v", domainEntry.GetInfo().Name, e.executionInfo.WorkflowID, incomingTaskVersion, activeClusterInfo.FailoverVersion, string(debug.Stack())) } if err := e.UpdateCurrentVersion(activeClusterInfo.FailoverVersion, false); err != nil { return false, err } return e.startTransactionHandleDecisionFailover(incomingTaskVersion) } func (e *mutableStateBuilder) CloseTransactionAsMutation( now time.Time, transactionPolicy TransactionPolicy, ) (*persistence.WorkflowMutation, []*persistence.WorkflowEvents, error) { if err := e.prepareCloseTransaction( transactionPolicy, ); err != nil { return nil, nil, err } workflowEventsSeq, err := e.prepareEventsAndReplicationTasks(transactionPolicy) if err != nil { return nil, nil, err } if len(workflowEventsSeq) > 0 { lastEvents := workflowEventsSeq[len(workflowEventsSeq)-1].Events firstEvent := lastEvents[0] lastEvent := lastEvents[len(lastEvents)-1] e.updateWithLastFirstEvent(firstEvent) if err := e.updateWithLastWriteEvent( lastEvent, transactionPolicy, ); err != nil { return nil, nil, err } } // update last update time e.executionInfo.LastUpdatedTimestamp = now // we generate checksum here based on the assumption that the returned // snapshot object is considered immutable. As of this writing, the only // code that modifies the returned object lives inside workflowExecutionContext.resetWorkflowExecution // currently, the updates done inside workflowExecutionContext.resetWorkflowExecution doesn't // impact the checksum calculation checksum := e.generateChecksum() workflowMutation := &persistence.WorkflowMutation{ ExecutionInfo: e.executionInfo, VersionHistories: e.versionHistories, UpsertActivityInfos: maps.Values(e.updateActivityInfos), DeleteActivityInfos: maps.Keys(e.deleteActivityInfos), UpsertTimerInfos: maps.Values(e.updateTimerInfos), DeleteTimerInfos: maps.Keys(e.deleteTimerInfos), UpsertChildExecutionInfos: maps.Values(e.updateChildExecutionInfos), DeleteChildExecutionInfos: maps.Keys(e.deleteChildExecutionInfos), UpsertRequestCancelInfos: maps.Values(e.updateRequestCancelInfos), DeleteRequestCancelInfos: maps.Keys(e.deleteRequestCancelInfos), UpsertSignalInfos: maps.Values(e.updateSignalInfos), DeleteSignalInfos: maps.Keys(e.deleteSignalInfos), UpsertSignalRequestedIDs: maps.Keys(e.updateSignalRequestedIDs), DeleteSignalRequestedIDs: maps.Keys(e.deleteSignalRequestedIDs), NewBufferedEvents: e.updateBufferedEvents, ClearBufferedEvents: e.clearBufferedEvents, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: e.insertTransferTasks, persistence.HistoryTaskCategoryReplication: e.insertReplicationTasks, persistence.HistoryTaskCategoryTimer: e.insertTimerTasks, }, WorkflowRequests: convertWorkflowRequests(e.workflowRequests), Condition: e.nextEventIDInDB, Checksum: checksum, } e.checksum = checksum if err := e.cleanupTransaction(); err != nil { return nil, nil, err } return workflowMutation, workflowEventsSeq, nil } func (e *mutableStateBuilder) CloseTransactionAsSnapshot( now time.Time, transactionPolicy TransactionPolicy, ) (*persistence.WorkflowSnapshot, []*persistence.WorkflowEvents, error) { if err := e.prepareCloseTransaction( transactionPolicy, ); err != nil { return nil, nil, err } workflowEventsSeq, err := e.prepareEventsAndReplicationTasks(transactionPolicy) if err != nil { return nil, nil, err } if len(workflowEventsSeq) > 1 { return nil, nil, &types.InternalServiceError{ Message: "cannot generate workflow snapshot with transient events", } } if len(e.bufferedEvents) > 0 { // TODO do we need the functionality to generate snapshot with buffered events? return nil, nil, &types.InternalServiceError{ Message: "cannot generate workflow snapshot with buffered events", } } if len(workflowEventsSeq) > 0 { lastEvents := workflowEventsSeq[len(workflowEventsSeq)-1].Events firstEvent := lastEvents[0] lastEvent := lastEvents[len(lastEvents)-1] e.updateWithLastFirstEvent(firstEvent) if err := e.updateWithLastWriteEvent( lastEvent, transactionPolicy, ); err != nil { return nil, nil, err } } // update last update time e.executionInfo.LastUpdatedTimestamp = now // we generate checksum here based on the assumption that the returned // snapshot object is considered immutable. As of this writing, the only // code that modifies the returned object lives inside workflowExecutionContext.resetWorkflowExecution // currently, the updates done inside workflowExecutionContext.resetWorkflowExecution doesn't // impact the checksum calculation checksum := e.generateChecksum() workflowSnapshot := &persistence.WorkflowSnapshot{ ExecutionInfo: e.executionInfo, VersionHistories: e.versionHistories, ActivityInfos: maps.Values(e.pendingActivityInfoIDs), TimerInfos: maps.Values(e.pendingTimerInfoIDs), ChildExecutionInfos: maps.Values(e.pendingChildExecutionInfoIDs), RequestCancelInfos: maps.Values(e.pendingRequestCancelInfoIDs), SignalInfos: maps.Values(e.pendingSignalInfoIDs), SignalRequestedIDs: maps.Keys(e.pendingSignalRequestedIDs), TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: e.insertTransferTasks, persistence.HistoryTaskCategoryReplication: e.insertReplicationTasks, persistence.HistoryTaskCategoryTimer: e.insertTimerTasks, }, WorkflowRequests: convertWorkflowRequests(e.workflowRequests), Condition: e.nextEventIDInDB, Checksum: checksum, } e.checksum = checksum if err := e.cleanupTransaction(); err != nil { return nil, nil, err } return workflowSnapshot, workflowEventsSeq, nil } func (e *mutableStateBuilder) IsResourceDuplicated( resourceDedupKey definition.DeduplicationID, ) bool { id := definition.GenerateDeduplicationKey(resourceDedupKey) _, duplicated := e.appliedEvents[id] return duplicated } func (e *mutableStateBuilder) UpdateDuplicatedResource( resourceDedupKey definition.DeduplicationID, ) { id := definition.GenerateDeduplicationKey(resourceDedupKey) e.appliedEvents[id] = struct{}{} } func (e *mutableStateBuilder) GetHistorySize() int64 { return e.executionStats.HistorySize } func (e *mutableStateBuilder) SetHistorySize(size int64) { e.executionStats.HistorySize = size } func (e *mutableStateBuilder) ByteSize() uint64 { // TODO: To be implemented return 0 } func (e *mutableStateBuilder) prepareCloseTransaction( transactionPolicy TransactionPolicy, ) error { if err := e.closeTransactionWithPolicyCheck( transactionPolicy, ); err != nil { return err } if err := e.closeTransactionHandleBufferedEventsLimit( transactionPolicy, ); err != nil { return err } if err := e.closeTransactionHandleWorkflowReset( transactionPolicy, ); err != nil { return err } // flushing buffered events should happen at very last if transactionPolicy == TransactionPolicyActive { if err := e.FlushBufferedEvents(); err != nil { return err } } // NOTE: this function must be the last call // since we only generate at most one activity & user timer, // regardless of how many activity & user timer created // so the calculation must be at the very end return e.closeTransactionHandleActivityUserTimerTasks() } func (e *mutableStateBuilder) cleanupTransaction() error { // Clear all updates to prepare for the next session e.hBuilder = NewHistoryBuilder(e) e.updateActivityInfos = make(map[int64]*persistence.ActivityInfo) e.deleteActivityInfos = make(map[int64]struct{}) e.syncActivityTasks = make(map[int64]struct{}) e.updateTimerInfos = make(map[string]*persistence.TimerInfo) e.deleteTimerInfos = make(map[string]struct{}) e.updateChildExecutionInfos = make(map[int64]*persistence.ChildExecutionInfo) e.deleteChildExecutionInfos = make(map[int64]struct{}) e.updateRequestCancelInfos = make(map[int64]*persistence.RequestCancelInfo) e.deleteRequestCancelInfos = make(map[int64]struct{}) e.updateSignalInfos = make(map[int64]*persistence.SignalInfo) e.deleteSignalInfos = make(map[int64]struct{}) e.updateSignalRequestedIDs = make(map[string]struct{}) e.deleteSignalRequestedIDs = make(map[string]struct{}) e.clearBufferedEvents = false if e.updateBufferedEvents != nil { e.bufferedEvents = append(e.bufferedEvents, e.updateBufferedEvents...) e.updateBufferedEvents = nil } e.hasBufferedEventsInDB = len(e.bufferedEvents) > 0 e.stateInDB = e.executionInfo.State e.nextEventIDInDB = e.GetNextEventID() e.insertTransferTasks = nil e.insertReplicationTasks = nil e.insertTimerTasks = nil e.workflowRequests = make(map[persistence.WorkflowRequest]struct{}) return nil } func (e *mutableStateBuilder) prepareEventsAndReplicationTasks( transactionPolicy TransactionPolicy, ) ([]*persistence.WorkflowEvents, error) { currentBranchToken, err := e.GetCurrentBranchToken() if err != nil { return nil, err } var workflowEventsSeq []*persistence.WorkflowEvents if len(e.hBuilder.transientHistory) != 0 { workflowEventsSeq = append(workflowEventsSeq, &persistence.WorkflowEvents{ DomainID: e.executionInfo.DomainID, WorkflowID: e.executionInfo.WorkflowID, RunID: e.executionInfo.RunID, BranchToken: currentBranchToken, Events: e.hBuilder.transientHistory, }) } if len(e.hBuilder.history) != 0 { workflowEventsSeq = append(workflowEventsSeq, &persistence.WorkflowEvents{ DomainID: e.executionInfo.DomainID, WorkflowID: e.executionInfo.WorkflowID, RunID: e.executionInfo.RunID, BranchToken: currentBranchToken, Events: e.hBuilder.history, }) } if err := e.validateNoEventsAfterWorkflowFinish( transactionPolicy, e.hBuilder.history, ); err != nil { return nil, err } for _, workflowEvents := range workflowEventsSeq { replicationTasks, err := e.eventsToReplicationTask(transactionPolicy, workflowEvents.Events) if err != nil { return nil, err } e.insertReplicationTasks = append( e.insertReplicationTasks, replicationTasks..., ) } e.insertReplicationTasks = append( e.insertReplicationTasks, e.syncActivityToReplicationTask(transactionPolicy)..., ) if transactionPolicy == TransactionPolicyPassive && len(e.insertReplicationTasks) > 0 { return nil, &types.InternalServiceError{ Message: "should not generate replication task when close transaction as passive", } } return workflowEventsSeq, nil } func (e *mutableStateBuilder) eventsToReplicationTask( transactionPolicy TransactionPolicy, events []*types.HistoryEvent, ) ([]persistence.Task, error) { if transactionPolicy == TransactionPolicyPassive || !e.canReplicateEvents() || len(events) == 0 { return emptyTasks, nil } firstEvent := events[0] lastEvent := events[len(events)-1] version := firstEvent.Version // Check all the events in the transaction belongs to the same cluster sourceCluster, err := e.clusterMetadata.ClusterNameForFailoverVersion(version) if err != nil { return nil, err } currentCluster := e.clusterMetadata.GetCurrentClusterName() if currentCluster != sourceCluster { return nil, &types.InternalServiceError{ Message: "mutableStateBuilder encounter contradicting version & transaction policy", } } currentBranchToken, err := e.GetCurrentBranchToken() if err != nil { return nil, err } // the visibility timestamp will be set in shard context replicationTask := &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: e.executionInfo.DomainID, WorkflowID: e.executionInfo.WorkflowID, RunID: e.executionInfo.RunID, }, TaskData: persistence.TaskData{ Version: firstEvent.Version, }, FirstEventID: firstEvent.ID, NextEventID: lastEvent.ID + 1, BranchToken: currentBranchToken, NewRunBranchToken: nil, } e.logger.Debugf("eventsToReplicationTask returning replicationTask. Version: %v, FirstEventID: %v, NextEventID: %v, SourceCluster: %v, CurrentCluster: %v", replicationTask.Version, replicationTask.FirstEventID, replicationTask.NextEventID, sourceCluster, currentCluster, ) return []persistence.Task{replicationTask}, nil } func (e *mutableStateBuilder) syncActivityToReplicationTask( transactionPolicy TransactionPolicy, ) []persistence.Task { if transactionPolicy == TransactionPolicyPassive || !e.canReplicateEvents() { return emptyTasks } return convertSyncActivityInfos( e.executionInfo, e.pendingActivityInfoIDs, e.syncActivityTasks, ) } func (e *mutableStateBuilder) updateWithLastWriteEvent( lastEvent *types.HistoryEvent, transactionPolicy TransactionPolicy, ) error { if transactionPolicy == TransactionPolicyPassive { // already handled in state builder return nil } e.GetExecutionInfo().LastEventTaskID = lastEvent.TaskID if e.versionHistories != nil { currentVersionHistory, err := e.versionHistories.GetCurrentVersionHistory() if err != nil { return err } if err := currentVersionHistory.AddOrUpdateItem(persistence.NewVersionHistoryItem( lastEvent.ID, lastEvent.Version, )); err != nil { return err } } return nil } func (e *mutableStateBuilder) updateWithLastFirstEvent( lastFirstEvent *types.HistoryEvent, ) { e.GetExecutionInfo().SetLastFirstEventID(lastFirstEvent.ID) } func (e *mutableStateBuilder) canReplicateEvents() bool { if e.domainEntry.GetReplicationPolicy() == cache.ReplicationPolicyOneCluster { return false } // ReplicationPolicyMultiCluster domainID := e.domainEntry.GetInfo().ID workflowID := e.GetExecutionInfo().WorkflowID return e.shard.GetConfig().EnableReplicationTaskGeneration(domainID, workflowID) } // validateNoEventsAfterWorkflowFinish perform check on history event batch // NOTE: do not apply this check on every batch, since transient // decision && workflow finish will be broken (the first batch) func (e *mutableStateBuilder) validateNoEventsAfterWorkflowFinish( transactionPolicy TransactionPolicy, events []*types.HistoryEvent, ) error { if transactionPolicy == TransactionPolicyPassive || len(events) == 0 { return nil } // only do check if workflow is finished if e.GetExecutionInfo().State != persistence.WorkflowStateCompleted { return nil } // workflow close // this will perform check on the last event of last batch // NOTE: do not apply this check on every batch, since transient // decision && workflow finish will be broken (the first batch) lastEvent := events[len(events)-1] switch lastEvent.GetEventType() { case types.EventTypeWorkflowExecutionCompleted, types.EventTypeWorkflowExecutionFailed, types.EventTypeWorkflowExecutionTimedOut, types.EventTypeWorkflowExecutionTerminated, types.EventTypeWorkflowExecutionContinuedAsNew, types.EventTypeWorkflowExecutionCanceled: return nil default: executionInfo := e.GetExecutionInfo() e.logError( "encounter case where events appears after workflow finish.", tag.WorkflowDomainID(executionInfo.DomainID), tag.WorkflowID(executionInfo.WorkflowID), tag.WorkflowRunID(executionInfo.RunID), ) return ErrEventsAfterWorkflowFinish } } func (e *mutableStateBuilder) startTransactionHandleDecisionFailover( incomingTaskVersion int64, ) (bool, error) { if !e.IsWorkflowExecutionRunning() || !e.canReplicateEvents() { return false, nil } // NOTE: // the main idea here is to guarantee that once there is a decision task started // all events ending in the buffer should have the same version // Handling mutable state turn from standby to active, while having a decision on the fly decision, ok := e.GetInFlightDecision() if !ok || decision.Version >= e.GetCurrentVersion() { // no pending decision, no buffered events // or decision has higher / equal version return false, nil } lastWriteVersion, err := e.GetLastWriteVersion() if err != nil { return false, err } if lastWriteVersion != decision.Version { return false, &types.InternalServiceError{Message: fmt.Sprintf( "mutableStateBuilder encounter mismatch version, decision: %v, last write version %v", decision.Version, lastWriteVersion, )} } lastWriteSourceCluster, err := e.clusterMetadata.ClusterNameForFailoverVersion(lastWriteVersion) if err != nil { return false, err } currentVersion := e.GetCurrentVersion() currentVersionCluster, err := e.clusterMetadata.ClusterNameForFailoverVersion(currentVersion) if err != nil { return false, err } currentCluster := e.clusterMetadata.GetCurrentClusterName() // there are 5 cases for version changes (based on version from domain cache) // NOTE: domain cache version change may occur after seeing events with higher version // meaning that the flush buffer logic in NDC branch manager should be kept. // // 1. active -> passive => fail decision & flush buffer using last write version // 2. active -> active => fail decision & flush buffer using last write version // 3. passive -> active => fail decision using current version, no buffered events // 4. passive -> passive => no buffered events, since always passive, nothing to be done // 5. special case: current cluster is passive. Due to some reason, the history generated by the current cluster // is missing and the missing history replicate back from remote cluster via resending approach => nothing to do e.logger.Debugf("startTransactionHandleDecisionFailover incomingTaskVersion %v, lastWriteVersion %v, currentVersion %v, currentCluster %v, lastWriteSourceCluster %v, currentVersionCluster %v", incomingTaskVersion, lastWriteVersion, currentVersion, currentCluster, lastWriteSourceCluster, currentVersionCluster, ) // handle case 5 incomingTaskSourceCluster, err := e.clusterMetadata.ClusterNameForFailoverVersion(incomingTaskVersion) if err != nil { return false, err } if incomingTaskVersion != constants.EmptyVersion && currentVersionCluster != currentCluster && incomingTaskSourceCluster == currentCluster { return false, nil } // handle case 4 if lastWriteSourceCluster != currentCluster && currentVersionCluster != currentCluster { // do a sanity check on buffered events if e.HasBufferedEvents() { return false, &types.InternalServiceError{ Message: "mutableStateBuilder encounter previous passive workflow with buffered events", } } return false, nil } // handle case 1 & 2 var flushBufferVersion = lastWriteVersion // handle case 3 if lastWriteSourceCluster != currentCluster && currentVersionCluster == currentCluster { // do a sanity check on buffered events if e.HasBufferedEvents() { return false, &types.InternalServiceError{ Message: "mutableStateBuilder encounter previous passive workflow with buffered events", } } flushBufferVersion = currentVersion } // this workflow was previous active (whether it has buffered events or not), // the in flight decision must be failed to guarantee all events within same // event batch shard the same version e.logger.Debugf("startTransactionHandleDecisionFailover calling UpdateCurrentVersion for domain %s, wfID %v, flushBufferVersion %v", e.executionInfo.DomainID, e.executionInfo.WorkflowID, flushBufferVersion) if err := e.UpdateCurrentVersion(flushBufferVersion, true); err != nil { return false, err } // we have a decision with buffered events on the fly with a lower version, fail it if err := FailDecision( e, decision, types.DecisionTaskFailedCauseFailoverCloseDecision, ); err != nil { return false, err } err = ScheduleDecision(e) if err != nil { return false, err } return true, nil } func (e *mutableStateBuilder) closeTransactionWithPolicyCheck( transactionPolicy TransactionPolicy, ) error { if transactionPolicy == TransactionPolicyPassive || !e.canReplicateEvents() { return nil } activeCluster, err := e.clusterMetadata.ClusterNameForFailoverVersion(e.GetCurrentVersion()) if err != nil { return err } currentCluster := e.clusterMetadata.GetCurrentClusterName() if activeCluster != currentCluster { e.logger.Debugf("closeTransactionWithPolicyCheck activeCluster != currentCluster, activeCluster=%v, currentCluster=%v, e.GetCurrentVersion()=%v", activeCluster, currentCluster, e.GetCurrentVersion()) return e.domainEntry.NewDomainNotActiveError(currentCluster, activeCluster) } return nil } func (e *mutableStateBuilder) closeTransactionHandleBufferedEventsLimit( transactionPolicy TransactionPolicy, ) error { if transactionPolicy == TransactionPolicyPassive || !e.IsWorkflowExecutionRunning() { return nil } if len(e.bufferedEvents) < e.config.MaximumBufferedEventsBatch() { return nil } // Handling buffered events size issue if decision, ok := e.GetInFlightDecision(); ok { // we have a decision on the fly with a lower version, fail it if err := FailDecision( e, decision, types.DecisionTaskFailedCauseForceCloseDecision, ); err != nil { return err } err := ScheduleDecision(e) if err != nil { return err } } return nil } func (e *mutableStateBuilder) closeTransactionHandleWorkflowReset( transactionPolicy TransactionPolicy, ) error { if transactionPolicy == TransactionPolicyPassive || !e.IsWorkflowExecutionRunning() { return nil } // compare with bad client binary checksum and schedule a reset task // only schedule reset task if current doesn't have childWFs. // TODO: This will be removed once our reset allows childWFs if len(e.GetPendingChildExecutionInfos()) != 0 { return nil } executionInfo := e.GetExecutionInfo() domainEntry, err := e.shard.GetDomainCache().GetDomainByID(executionInfo.DomainID) if err != nil { return err } if _, pt := FindAutoResetPoint( e.timeSource, &domainEntry.GetConfig().BadBinaries, e.GetExecutionInfo().AutoResetPoints, ); pt != nil { if err := e.taskGenerator.GenerateWorkflowResetTasks(); err != nil { return err } e.logInfo("Auto-Reset task is scheduled", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.WorkflowID(executionInfo.WorkflowID), tag.WorkflowRunID(executionInfo.RunID), tag.WorkflowResetBaseRunID(pt.GetRunID()), tag.WorkflowEventID(pt.GetFirstDecisionCompletedID()), tag.WorkflowBinaryChecksum(pt.GetBinaryChecksum()), ) } return nil } func (e *mutableStateBuilder) closeTransactionHandleActivityUserTimerTasks() error { if !e.IsWorkflowExecutionRunning() { return nil } if err := e.taskGenerator.GenerateActivityTimerTasks(); err != nil { return err } return e.taskGenerator.GenerateUserTimerTasks() } func (e *mutableStateBuilder) checkMutability( actionTag tag.Tag, ) error { if !e.IsWorkflowExecutionRunning() { e.logWarn( mutableStateInvalidHistoryActionMsg, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowState(e.executionInfo.State), actionTag, ) return ErrWorkflowFinished } return nil } func (e *mutableStateBuilder) insertWorkflowRequest(request persistence.WorkflowRequest) { if e.domainEntry != nil && e.config.EnableStrongIdempotency(e.domainEntry.GetInfo().Name) && request.RequestID != "" { if _, ok := e.workflowRequests[request]; ok { e.logWarn("error encountering duplicate request", tag.WorkflowRequestID(request.RequestID)) } e.workflowRequests[request] = struct{}{} } } func (e *mutableStateBuilder) generateChecksum() checksum.Checksum { if !e.shouldGenerateChecksum() { return checksum.Checksum{} } csum, err := generateMutableStateChecksum(e) if err != nil { e.logWarn("error generating mutableState checksum", tag.Error(err)) return checksum.Checksum{} } return csum } func (e *mutableStateBuilder) shouldGenerateChecksum() bool { if e.domainEntry == nil { return false } return rand.Intn(100) < e.config.MutableStateChecksumGenProbability(e.domainEntry.GetInfo().Name) } func (e *mutableStateBuilder) shouldVerifyChecksum() bool { if e.domainEntry == nil { return false } return rand.Intn(100) < e.config.MutableStateChecksumVerifyProbability(e.domainEntry.GetInfo().Name) } func (e *mutableStateBuilder) enableChecksumFailureRetry() bool { if e.domainEntry == nil { return false } return e.config.EnableRetryForChecksumFailure(e.domainEntry.GetInfo().Name) } func (e *mutableStateBuilder) shouldInvalidateChecksum() bool { invalidateBeforeEpochSecs := int64(e.config.MutableStateChecksumInvalidateBefore()) if invalidateBeforeEpochSecs > 0 { invalidateBefore := time.Unix(invalidateBeforeEpochSecs, 0) return e.executionInfo.LastUpdatedTimestamp.Before(invalidateBefore) } return false } func (e *mutableStateBuilder) createInternalServerError( actionTag tag.Tag, ) error { return &types.InternalServiceError{Message: actionTag.Field().String + " operation failed"} } func (e *mutableStateBuilder) createCallerError( actionTag tag.Tag, ) error { return &types.BadRequestError{ Message: fmt.Sprintf(mutableStateInvalidHistoryActionMsgTemplate, actionTag.Field().String), } } func (e *mutableStateBuilder) unixNanoToTime( timestampNanos int64, ) time.Time { return time.Unix(0, timestampNanos) } func (e *mutableStateBuilder) logInfo(msg string, tags ...tag.Tag) { if e == nil { return } if e.executionInfo != nil { tags = append(tags, tag.WorkflowID(e.executionInfo.WorkflowID)) tags = append(tags, tag.WorkflowRunID(e.executionInfo.RunID)) tags = append(tags, tag.WorkflowDomainID(e.executionInfo.DomainID)) } e.logger.Info(msg, tags...) } func (e *mutableStateBuilder) logWarn(msg string, tags ...tag.Tag) { if e == nil { return } if e.executionInfo != nil { tags = append(tags, tag.WorkflowID(e.executionInfo.WorkflowID)) tags = append(tags, tag.WorkflowRunID(e.executionInfo.RunID)) tags = append(tags, tag.WorkflowDomainID(e.executionInfo.DomainID)) } e.logger.Warn(msg, tags...) } func (e *mutableStateBuilder) logError(msg string, tags ...tag.Tag) { if e == nil { return } if e.executionInfo != nil { tags = append(tags, tag.WorkflowID(e.executionInfo.WorkflowID)) tags = append(tags, tag.WorkflowRunID(e.executionInfo.RunID)) tags = append(tags, tag.WorkflowDomainID(e.executionInfo.DomainID)) } e.logger.Error(msg, tags...) } func (e *mutableStateBuilder) logDataInconsistency() { domainID := e.executionInfo.DomainID workflowID := e.executionInfo.WorkflowID runID := e.executionInfo.RunID e.metricsClient.Scope(metrics.WorkflowContextScope).IncCounter(metrics.DataInconsistentCounter) e.logger.Error("encounter mutable state data inconsistency", tag.WorkflowDomainID(domainID), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), ) } func (e *mutableStateBuilder) reorderAndFilterDuplicateEvents(events []*types.HistoryEvent, source string) []*types.HistoryEvent { type eventUniquenessParams struct { eventType types.EventType scheduledEventID int64 attempt int32 startedEventID int64 } activityTaskUniqueEvents := make(map[eventUniquenessParams]struct{}) checkEventUniqueness := func(event *types.HistoryEvent) bool { var uniqueEventParams eventUniquenessParams var scheduledEventID int64 switch event.GetEventType() { case types.EventTypeActivityTaskStarted: scheduledEventID = event.ActivityTaskStartedEventAttributes.GetScheduledEventID() uniqueEventParams = eventUniquenessParams{ eventType: event.GetEventType(), scheduledEventID: scheduledEventID, attempt: event.ActivityTaskStartedEventAttributes.Attempt, } case types.EventTypeActivityTaskCompleted: scheduledEventID = event.ActivityTaskCompletedEventAttributes.GetScheduledEventID() uniqueEventParams = eventUniquenessParams{ eventType: event.GetEventType(), scheduledEventID: scheduledEventID, startedEventID: event.ActivityTaskCompletedEventAttributes.GetStartedEventID(), } case types.EventTypeActivityTaskFailed: scheduledEventID = event.ActivityTaskFailedEventAttributes.GetScheduledEventID() uniqueEventParams = eventUniquenessParams{ eventType: event.GetEventType(), scheduledEventID: scheduledEventID, startedEventID: event.ActivityTaskFailedEventAttributes.GetStartedEventID(), } case types.EventTypeActivityTaskCanceled: scheduledEventID = event.ActivityTaskCanceledEventAttributes.GetScheduledEventID() uniqueEventParams = eventUniquenessParams{ eventType: event.GetEventType(), scheduledEventID: scheduledEventID, startedEventID: event.ActivityTaskCanceledEventAttributes.StartedEventID, } case types.EventTypeActivityTaskTimedOut: scheduledEventID = event.ActivityTaskTimedOutEventAttributes.GetScheduledEventID() uniqueEventParams = eventUniquenessParams{ eventType: event.GetEventType(), scheduledEventID: scheduledEventID, startedEventID: event.ActivityTaskTimedOutEventAttributes.StartedEventID, } case types.EventTypeChildWorkflowExecutionStarted: scheduledEventID = event.ChildWorkflowExecutionStartedEventAttributes.InitiatedEventID uniqueEventParams = eventUniquenessParams{ eventType: event.GetEventType(), scheduledEventID: scheduledEventID, } case types.EventTypeChildWorkflowExecutionCompleted: scheduledEventID = event.ChildWorkflowExecutionCompletedEventAttributes.InitiatedEventID uniqueEventParams = eventUniquenessParams{ eventType: event.GetEventType(), scheduledEventID: scheduledEventID, startedEventID: event.ChildWorkflowExecutionCompletedEventAttributes.StartedEventID, } case types.EventTypeChildWorkflowExecutionFailed: scheduledEventID = event.ChildWorkflowExecutionFailedEventAttributes.InitiatedEventID uniqueEventParams = eventUniquenessParams{ eventType: event.GetEventType(), scheduledEventID: scheduledEventID, startedEventID: event.ChildWorkflowExecutionFailedEventAttributes.StartedEventID, } case types.EventTypeChildWorkflowExecutionTimedOut: scheduledEventID = event.ChildWorkflowExecutionTimedOutEventAttributes.InitiatedEventID uniqueEventParams = eventUniquenessParams{ eventType: event.GetEventType(), scheduledEventID: scheduledEventID, startedEventID: event.ChildWorkflowExecutionTimedOutEventAttributes.StartedEventID, } case types.EventTypeChildWorkflowExecutionCanceled: scheduledEventID = event.ChildWorkflowExecutionCanceledEventAttributes.InitiatedEventID uniqueEventParams = eventUniquenessParams{ eventType: event.GetEventType(), scheduledEventID: scheduledEventID, startedEventID: event.ChildWorkflowExecutionCanceledEventAttributes.StartedEventID, } default: return true } if _, ok := activityTaskUniqueEvents[uniqueEventParams]; ok { e.logger.Error("Duplicate event found", tag.WorkflowDomainName(e.GetDomainEntry().GetInfo().Name), tag.WorkflowID(e.GetExecutionInfo().WorkflowID), tag.WorkflowRunID(e.GetExecutionInfo().RunID), tag.WorkflowScheduleID(scheduledEventID), tag.WorkflowEventType(event.GetEventType().String()), tag.Dynamic("duplication-source", source), ) e.metricsClient.IncCounter(metrics.HistoryFlushBufferedEventsScope, metrics.DuplicateActivityTaskEventCounter) return false } activityTaskUniqueEvents[uniqueEventParams] = struct{}{} return true } var headEvents []*types.HistoryEvent var tailEvents []*types.HistoryEvent // Sometimes we see buffered events are out of order when read back from database. This is mostly not an issue // except in the Activity case where ActivityStarted and ActivityCompleted gets out of order. The following code // is added to reorder buffered events to guarantee all activity completion events will always be processed at the end. for _, event := range events { // We sometimes see duplicate events if unique := checkEventUniqueness(event); !unique { continue } switch event.GetEventType() { case types.EventTypeActivityTaskCompleted, types.EventTypeActivityTaskFailed, types.EventTypeActivityTaskCanceled, types.EventTypeActivityTaskTimedOut: tailEvents = append(tailEvents, event) case types.EventTypeChildWorkflowExecutionCompleted, types.EventTypeChildWorkflowExecutionFailed, types.EventTypeChildWorkflowExecutionCanceled, types.EventTypeChildWorkflowExecutionTimedOut, types.EventTypeChildWorkflowExecutionTerminated: tailEvents = append(tailEvents, event) default: headEvents = append(headEvents, event) } } return append(headEvents, tailEvents...) } func mergeMapOfByteArray( current map[string][]byte, upsert map[string][]byte, ) map[string][]byte { if current == nil { current = make(map[string][]byte) } for k, v := range upsert { current[k] = v } return current } ================================================ FILE: service/history/execution/mutable_state_builder_add_continue_as_new_event_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "context" "errors" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/events" shardCtx "github.com/uber/cadence/service/history/shard" ) func TestAddContinueAsNewEvent(t *testing.T) { firstEventID := int64(15) decisionCompletedEventID := int64(15) domainID := "5391dbea-5b30-4323-82ca-e1c95339bb3e" domainFailoverVersion := int64(1) ts0 := int64(123450) ts1 := int64(123451) ts2 := int64(123452) ts3 := int64(123453) shardID := 123 domainEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID}, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{}, domainFailoverVersion, nil, 0, 0, 0, ) domainEntryActiveActive := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID}, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region0": { ActiveClusterName: "cluster0", }, "region1": { ActiveClusterName: "cluster1", }, }, }, }, }, }, -1, // failover version is not used for active-active domain nil, 0, 0, 0, ) // the mutable state builder confusingly both returns a new builder with this fuction // as well as mutating its internal state, making it difficult to test repeatedly, since // the supplied inputs are muted per invocation. Wrapping them in a factor here to allow // for tests to be independent createStartingExecutionInfo := func() *persistence.WorkflowExecutionInfo { return &persistence.WorkflowExecutionInfo{ DomainID: "5391dbea-5b30-4323-82ca-e1c95339bb3e", WorkflowID: "helloworld_b4db8bd0-74b7-4250-ade7-ac72a1efb171", RunID: "5adce5c5-b7b2-4418-9bf0-4207303f6343", FirstExecutionRunID: "5adce5c5-b7b2-4418-9bf0-4207303f6343", InitiatedID: -7, TaskList: "helloWorldGroup", WorkflowTypeName: "helloWorldWorkflow", WorkflowTimeout: 60, DecisionStartToCloseTimeout: 60, State: 1, LastFirstEventID: 14, LastEventTaskID: 15728673, NextEventID: 16, LastProcessedEvent: 14, StartTimestamp: time.Unix(0, ts0), LastUpdatedTimestamp: time.Unix(0, ts2), CreateRequestID: "b086d62c-dd2b-4bbc-9143-5940516acbfe", DecisionVersion: -24, DecisionScheduleID: -23, DecisionStartedID: -23, DecisionRequestID: "emptyUuid", DecisionOriginalScheduledTimestamp: 1709872131542474000, StickyTaskList: "david-porter-DVFG73D710:04be47fa-2381-469f-b2ea-1253271ad116", StickyScheduleToStartTimeout: 5, ClientLibraryVersion: "0.18.4", ClientFeatureVersion: "1.7.0", ClientImpl: "uber-go", AutoResetPoints: &types.ResetPoints{ Points: []*types.ResetPointInfo{{ BinaryChecksum: "6df03bf5110d681667852a8456519536", RunID: "5adce5c5-b7b2-4418-9bf0-4207303f6343", FirstDecisionCompletedID: 4, CreatedTimeNano: common.Ptr(int64(ts1)), Resettable: true, }}, }, SearchAttributes: map[string][]uint8{"BinaryChecksums": {91, 34, 54, 100, 102, 48, 51, 98, 102, 53, 49, 49, 48, 100, 54, 56, 49, 54, 54, 55, 56, 53, 50, 97, 56, 52, 53, 54, 53, 49, 57, 53, 51, 54, 34, 93}}} } createValidStartingHistory := func(version int64) []*types.HistoryEvent { return []*types.HistoryEvent{{ ID: 15, Timestamp: common.Ptr(int64(1709872131580456000)), EventType: common.Ptr(types.EventTypeDecisionTaskCompleted), Version: version, TaskID: -1234, DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 13, StartedEventID: 14, Identity: "27368@david-porter-DVFG73D710@helloWorldGroup@6027e9ee-048e-4f67-8d88-27883c496901", BinaryChecksum: "6df03bf5110d681667852a8456519536", }, }} } createFetchedHistory := func(version int64) *types.HistoryEvent { return &types.HistoryEvent{ ID: 1, Timestamp: common.Ptr(int64(1709938156435726000)), EventType: common.Ptr(types.EventTypeWorkflowExecutionStarted), Version: version, TaskID: 17826364, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: "helloWorldWorkflow"}, TaskList: &types.TaskList{Name: "helloWorldGroup"}, Input: []uint8{110, 117, 108, 108, 10}, ExecutionStartToCloseTimeoutSeconds: common.Ptr(int32(60)), TaskStartToCloseTimeoutSeconds: common.Ptr(int32(60)), ContinuedExecutionRunID: "96892ca6-975a-44b1-9726-cdb63acd8cda", OriginalExecutionRunID: "befc5b41-fb06-4a99-bec2-91c3e98b17d7", FirstExecutionRunID: "bcdee7e4-cb21-4bbb-a8d1-43da79e3d252", PrevAutoResetPoints: &types.ResetPoints{Points: []*types.ResetPointInfo{ { BinaryChecksum: "6df03bf5110d681667852a8456519536", RunID: "bcdee7e4-cb21-4bbb-a8d1-43da79e3d252", FirstDecisionCompletedID: 4, CreatedTimeNano: common.Ptr(int64(1709938002170829000)), ExpiringTimeNano: common.Ptr(int64(1710197212347858000)), Resettable: true, }, }}, Header: &types.Header{}, }, } } expectedEndingReturnExecutionStateFn := func(version int64) *persistence.WorkflowExecutionInfo { return &persistence.WorkflowExecutionInfo{ DomainID: "5391dbea-5b30-4323-82ca-e1c95339bb3e", WorkflowID: "helloworld_b4db8bd0-74b7-4250-ade7-ac72a1efb171", RunID: "a run id", FirstExecutionRunID: "5adce5c5-b7b2-4418-9bf0-4207303f6343", InitiatedID: -23, TaskList: "helloWorldGroup", TaskListKind: types.TaskListKindNormal, WorkflowTypeName: "helloWorldWorkflow", WorkflowTimeout: 60, DecisionStartToCloseTimeout: 60, State: 1, LastFirstEventID: 1, NextEventID: 3, LastProcessedEvent: -23, StartTimestamp: time.Unix(0, ts3), CreateRequestID: "4630bf04-5c64-41bf-92d9-576db2d535cb", DecisionVersion: version, DecisionScheduleID: 2, DecisionStartedID: -23, DecisionRequestID: "emptyUuid", DecisionTimeout: 60, DecisionScheduledTimestamp: ts3, DecisionOriginalScheduledTimestamp: ts3, AutoResetPoints: &types.ResetPoints{ Points: []*types.ResetPointInfo{{ BinaryChecksum: "6df03bf5110d681667852a8456519536", RunID: "5adce5c5-b7b2-4418-9bf0-4207303f6343", FirstDecisionCompletedID: 4, CreatedTimeNano: common.Ptr(int64(ts1)), ExpiringTimeNano: common.Ptr(int64(ts3)), Resettable: true, }}, }, } } expectedEndingReturnHistoryStateFn := func(version int64) []*types.HistoryEvent { return []*types.HistoryEvent{ { ID: 1, Timestamp: common.Ptr(int64(ts3)), EventType: common.Ptr(types.EventTypeWorkflowExecutionStarted), Version: version, TaskID: -1234, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{ Name: "helloWorldWorkflow", }, TaskList: &types.TaskList{Name: "helloWorldGroup", Kind: types.TaskListKindNormal.Ptr()}, Input: []uint8{110, 117, 108, 108, 10}, ExecutionStartToCloseTimeoutSeconds: common.Ptr(int32(60)), TaskStartToCloseTimeoutSeconds: common.Ptr(int32(60)), ContinuedExecutionRunID: "5adce5c5-b7b2-4418-9bf0-4207303f6343", OriginalExecutionRunID: "a run id", FirstExecutionRunID: "5adce5c5-b7b2-4418-9bf0-4207303f6343", PrevAutoResetPoints: &types.ResetPoints{Points: []*types.ResetPointInfo{{ BinaryChecksum: "6df03bf5110d681667852a8456519536", RunID: "5adce5c5-b7b2-4418-9bf0-4207303f6343", FirstDecisionCompletedID: 4, CreatedTimeNano: common.Ptr(int64(ts1)), ExpiringTimeNano: common.Ptr(int64(ts3)), Resettable: true, }}}, Header: nil, }, }, { ID: 2, Timestamp: common.Ptr(int64(ts3)), EventType: common.Ptr(types.EventTypeDecisionTaskScheduled), Version: version, TaskID: -1234, DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: "helloWorldGroup"}, StartToCloseTimeoutSeconds: common.Ptr(int32(60)), }, }, } } tests := map[string]struct { domainEntry *cache.DomainCacheEntry startingState *persistence.WorkflowExecutionInfo // history is a substruct of current state, but because they're both // pointing to each other, they're assembled at the test start startingHistory []*types.HistoryEvent // expectations historyManagerAffordance func(historyManager *persistence.MockHistoryManager) taskgeneratorAffordance func(taskGenerator *MockMutableStateTaskGenerator, msb *mutableStateBuilder) actClMgrAffordance func(actClMgr *activecluster.MockManager) expectedReturnedState *persistence.WorkflowExecutionInfo // this is returned expectedReturnedHistory []*types.HistoryEvent expectedErr error }{ "a continue-as-new event with no errors": { domainEntry: domainEntry, startingState: createStartingExecutionInfo(), startingHistory: createValidStartingHistory(domainFailoverVersion), actClMgrAffordance: func(actClMgr *activecluster.MockManager) { actClMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterInfo{ FailoverVersion: domainFailoverVersion, }, nil) }, // when it goes to fetch the starting event historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) { historyManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ createFetchedHistory(domainFailoverVersion), }, }, nil) }, taskgeneratorAffordance: func(taskGenerator *MockMutableStateTaskGenerator, msb *mutableStateBuilder) { taskGenerator.EXPECT().GenerateWorkflowCloseTasks(gomock.Any(), msb.config.WorkflowDeletionJitterRange("domain")) }, expectedReturnedState: expectedEndingReturnExecutionStateFn(1), expectedReturnedHistory: expectedEndingReturnHistoryStateFn(1), }, "a continue-as-new event with no errors - active-active domain": { domainEntry: domainEntryActiveActive, startingState: createStartingExecutionInfo(), startingHistory: createValidStartingHistory(1), actClMgrAffordance: func(actClMgr *activecluster.MockManager) { actClMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterInfo{ FailoverVersion: 2, // this version will be used by new mutable state builder for new tasks }, nil) }, historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) { historyManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ createFetchedHistory(2), }, }, nil) }, taskgeneratorAffordance: func(taskGenerator *MockMutableStateTaskGenerator, msb *mutableStateBuilder) { taskGenerator.EXPECT().GenerateWorkflowCloseTasks(gomock.Any(), msb.config.WorkflowDeletionJitterRange("domain")) }, expectedReturnedState: expectedEndingReturnExecutionStateFn(2), expectedReturnedHistory: expectedEndingReturnHistoryStateFn(2), }, "a continue-as-new with failure to get the history event": { domainEntry: domainEntry, startingState: createStartingExecutionInfo(), startingHistory: createValidStartingHistory(domainFailoverVersion), historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) { historyManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(nil, errors.New("an error")) }, expectedErr: errors.New("an error"), }, "a continue-as-new with errors in replicating": { domainEntry: domainEntry, startingState: createStartingExecutionInfo(), startingHistory: createValidStartingHistory(domainFailoverVersion), actClMgrAffordance: func(actClMgr *activecluster.MockManager) { actClMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ActiveClusterInfo{ FailoverVersion: domainFailoverVersion, }, nil) }, historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) { historyManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ createFetchedHistory(domainFailoverVersion), }, }, nil) }, taskgeneratorAffordance: func(taskGenerator *MockMutableStateTaskGenerator, msb *mutableStateBuilder) { taskGenerator.EXPECT().GenerateWorkflowCloseTasks(gomock.Any(), msb.config.WorkflowDeletionJitterRange("domain")).Return(errors.New("an error")) }, expectedErr: errors.New("an error"), }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) logger := log.NewNoop() msb := &mutableStateBuilder{ domainEntry: td.domainEntry, executionInfo: td.startingState, logger: logger, config: config.NewForTest(), currentVersion: domainFailoverVersion, } actClMgr := activecluster.NewMockManager(ctrl) shardContext := shardCtx.NewMockContext(ctrl) shardContext.EXPECT().GetLogger().Return(logger).AnyTimes() shardContext.EXPECT().GetActiveClusterManager().Return(actClMgr).AnyTimes() historyManager := persistence.NewMockHistoryManager(ctrl) domainCache := cache.NewMockDomainCache(ctrl) taskGenerator := NewMockMutableStateTaskGenerator(ctrl) msb.timeSource = clock.NewMockedTimeSourceAt(time.Unix(0, ts3)) msb.eventsCache = events.NewCache(shardID, historyManager, config.NewForTest(), logger, metrics.NewNoopMetricsClient(), domainCache) msb.shard = shardContext msb.executionInfo = td.startingState msb.hBuilder = &HistoryBuilder{ history: td.startingHistory, msBuilder: msb, } msb.taskGenerator = taskGenerator domainCache.EXPECT().GetDomainName(gomock.Any()).Return("domain", nil).AnyTimes() shardContext.EXPECT().GetShardID().Return(123).AnyTimes() shardContext.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).AnyTimes() shardContext.EXPECT().GetEventsCache().Return(msb.eventsCache).AnyTimes() shardContext.EXPECT().GetConfig().Return(msb.config).AnyTimes() shardContext.EXPECT().GetTimeSource().Return(msb.timeSource).AnyTimes() shardContext.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).AnyTimes() shardContext.EXPECT().GetDomainCache().Return(domainCache).AnyTimes() if td.historyManagerAffordance != nil { td.historyManagerAffordance(historyManager) } if td.taskgeneratorAffordance != nil { td.taskgeneratorAffordance(taskGenerator, msb) } if td.actClMgrAffordance != nil { td.actClMgrAffordance(actClMgr) } _, returnedBuilder, err := msb.AddContinueAsNewEvent(context.Background(), firstEventID, decisionCompletedEventID, "", &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), WorkflowType: &types.WorkflowType{ Name: "helloWorldWorkflow", }, TaskList: &types.TaskList{ Name: "helloWorldGroup", }, Input: []uint8{110, 117, 108, 108, 10}, }) if td.expectedErr != nil { assert.ErrorAs(t, err, &td.expectedErr) return } resultExecutionInfo := returnedBuilder.GetExecutionInfo() assert.Empty(t, cmp.Diff(td.expectedReturnedState, resultExecutionInfo, // these are generated nondeterministically, with a plain guid generator // todo(david): make this mockable cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "OriginalExecutionRunID"), cmpopts.IgnoreFields(types.WorkflowExecution{}, "RunID"), cmpopts.IgnoreFields(persistence.WorkflowExecutionInfo{}, "RunID", "CreateRequestID"), )) assert.Empty(t, cmp.Diff(td.expectedReturnedHistory, returnedBuilder.GetHistoryBuilder().history, cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "OriginalExecutionRunID"), cmpopts.IgnoreFields(types.WorkflowExecutionStartedEventAttributes{}, "RequestID")), ) }) } } ================================================ FILE: service/history/execution/mutable_state_builder_methods_activity.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "context" "fmt" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // GetActivityInfo gives details about an activity that is currently in progress. func (e *mutableStateBuilder) GetActivityInfo( scheduleEventID int64, ) (*persistence.ActivityInfo, bool) { ai, ok := e.pendingActivityInfoIDs[scheduleEventID] return ai, ok } func (e *mutableStateBuilder) GetPendingActivityInfos() map[int64]*persistence.ActivityInfo { return e.pendingActivityInfoIDs } // GetActivityByActivityID gives details about an activity that is currently in progress. func (e *mutableStateBuilder) GetActivityByActivityID( activityID string, ) (*persistence.ActivityInfo, bool) { eventID, ok := e.pendingActivityIDToEventID[activityID] if !ok { return nil, false } return e.GetActivityInfo(eventID) } func (e *mutableStateBuilder) UpdateActivityProgress( ai *persistence.ActivityInfo, request *types.RecordActivityTaskHeartbeatRequest, ) { ai.Version = e.GetCurrentVersion() ai.Details = request.Details ai.LastHeartBeatUpdatedTime = e.timeSource.Now() e.updateActivityInfos[ai.ScheduleID] = ai e.syncActivityTasks[ai.ScheduleID] = struct{}{} } // ReplicateActivityInfo replicate the necessary activity information func (e *mutableStateBuilder) ReplicateActivityInfo( request *types.SyncActivityRequest, resetActivityTimerTaskStatus bool, ) error { ai, ok := e.pendingActivityInfoIDs[request.GetScheduledID()] if !ok { e.logError( fmt.Sprintf("unable to find activity event ID: %v in mutable state", request.GetScheduledID()), tag.ErrorTypeInvalidMutableStateAction, ) return ErrMissingActivityInfo } ai.Version = request.GetVersion() ai.ScheduledTime = time.Unix(0, request.GetScheduledTime()) ai.StartedID = request.GetStartedID() ai.LastHeartBeatUpdatedTime = time.Unix(0, request.GetLastHeartbeatTime()) if ai.StartedID == constants.EmptyEventID { ai.StartedTime = time.Time{} } else { ai.StartedTime = time.Unix(0, request.GetStartedTime()) } ai.Details = request.GetDetails() ai.Attempt = request.GetAttempt() ai.LastFailureReason = request.GetLastFailureReason() ai.LastWorkerIdentity = request.GetLastWorkerIdentity() ai.LastFailureDetails = request.GetLastFailureDetails() if resetActivityTimerTaskStatus { ai.TimerTaskStatus = TimerTaskStatusNone } e.updateActivityInfos[ai.ScheduleID] = ai return nil } // UpdateActivity updates an activity func (e *mutableStateBuilder) UpdateActivity( ai *persistence.ActivityInfo, ) error { if _, ok := e.pendingActivityInfoIDs[ai.ScheduleID]; !ok { e.logError( fmt.Sprintf("unable to find activity ID: %v in mutable state", ai.ActivityID), tag.ErrorTypeInvalidMutableStateAction, ) return ErrMissingActivityInfo } e.pendingActivityInfoIDs[ai.ScheduleID] = ai e.updateActivityInfos[ai.ScheduleID] = ai return nil } // DeleteActivity deletes details about an activity. func (e *mutableStateBuilder) DeleteActivity( scheduleEventID int64, ) error { if activityInfo, ok := e.pendingActivityInfoIDs[scheduleEventID]; ok { delete(e.pendingActivityInfoIDs, scheduleEventID) if _, ok = e.pendingActivityIDToEventID[activityInfo.ActivityID]; ok { delete(e.pendingActivityIDToEventID, activityInfo.ActivityID) } else { e.logError( fmt.Sprintf("unable to find activity ID: %v in mutable state", activityInfo.ActivityID), tag.ErrorTypeInvalidMutableStateAction, ) // log data inconsistency instead of returning an error e.logDataInconsistency() } } else { e.logError( fmt.Sprintf("unable to find activity event id: %v in mutable state", scheduleEventID), tag.ErrorTypeInvalidMutableStateAction, ) // log data inconsistency instead of returning an error e.logDataInconsistency() } delete(e.updateActivityInfos, scheduleEventID) e.deleteActivityInfos[scheduleEventID] = struct{}{} return nil } func (e *mutableStateBuilder) GetActivityScheduledEvent( ctx context.Context, scheduleEventID int64, ) (*types.HistoryEvent, error) { ai, ok := e.pendingActivityInfoIDs[scheduleEventID] if !ok { return nil, ErrMissingActivityInfo } // Needed for backward compatibility reason if ai.ScheduledEvent != nil { return ai.ScheduledEvent, nil } currentBranchToken, err := e.GetCurrentBranchToken() if err != nil { return nil, err } scheduledEvent, err := e.eventsCache.GetEvent( ctx, e.shard.GetShardID(), e.executionInfo.DomainID, e.executionInfo.WorkflowID, e.executionInfo.RunID, ai.ScheduledEventBatchID, ai.ScheduleID, currentBranchToken, ) if err != nil { // do not return the original error // since original error can be of type entity not exists // which can cause task processing side to fail silently // However, if the error is a persistence transient error, // we return the original error, because we fail to get // the event because of failure from database if persistence.IsTransientError(err) { return nil, err } return nil, ErrMissingActivityScheduledEvent } return scheduledEvent, nil } func (e *mutableStateBuilder) AddActivityTaskScheduledEvent( ctx context.Context, decisionCompletedEventID int64, attributes *types.ScheduleActivityTaskDecisionAttributes, dispatch bool, ) (*types.HistoryEvent, *persistence.ActivityInfo, *types.ActivityLocalDispatchInfo, bool, bool, error) { opTag := tag.WorkflowActionActivityTaskScheduled if err := e.checkMutability(opTag); err != nil { return nil, nil, nil, false, false, err } _, ok := e.GetActivityByActivityID(attributes.GetActivityID()) if ok { e.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction) return nil, nil, nil, false, false, e.createCallerError(opTag) } pendingActivitiesCount := len(e.pendingActivityInfoIDs) if pendingActivitiesCount >= e.config.PendingActivitiesCountLimitError() { e.logger.Error("Pending activity count exceeds error limit", tag.WorkflowDomainName(e.GetDomainEntry().GetInfo().Name), tag.WorkflowID(e.executionInfo.WorkflowID), tag.WorkflowRunID(e.executionInfo.RunID), tag.Number(int64(pendingActivitiesCount))) if e.config.PendingActivityValidationEnabled() { return nil, nil, nil, false, false, ErrTooManyPendingActivities } } else if pendingActivitiesCount >= e.config.PendingActivitiesCountLimitWarn() && !e.pendingActivityWarningSent { e.logger.Warn("Pending activity count exceeds warn limit", tag.WorkflowDomainName(e.GetDomainEntry().GetInfo().Name), tag.WorkflowID(e.executionInfo.WorkflowID), tag.WorkflowRunID(e.executionInfo.RunID), tag.Number(int64(pendingActivitiesCount))) e.pendingActivityWarningSent = true } event := e.hBuilder.AddActivityTaskScheduledEvent(decisionCompletedEventID, attributes) // Write the event to cache only on active cluster for processing on activity started or retried e.eventsCache.PutEvent( e.executionInfo.DomainID, e.executionInfo.WorkflowID, e.executionInfo.RunID, event.ID, event, ) ai, err := e.ReplicateActivityTaskScheduledEvent(decisionCompletedEventID, event, true) if err != nil { return nil, nil, nil, false, false, err } activityStartedScope := e.metricsClient.Scope(metrics.HistoryRecordActivityTaskStartedScope) if e.config.EnableActivityLocalDispatchByDomain(e.domainEntry.GetInfo().Name) && attributes.RequestLocalDispatch { activityStartedScope.IncCounter(metrics.CadenceRequests) return event, ai, &types.ActivityLocalDispatchInfo{ActivityID: ai.ActivityID}, false, false, nil } started := false if dispatch { started = e.tryDispatchActivityTask(ctx, event, ai) } if started { activityStartedScope.IncCounter(metrics.CadenceRequests) return event, ai, nil, true, true, nil } if err := e.taskGenerator.GenerateActivityTransferTasks(event); err != nil { return nil, nil, nil, dispatch, false, err } return event, ai, nil, dispatch, false, err } func (e *mutableStateBuilder) tryDispatchActivityTask( ctx context.Context, scheduledEvent *types.HistoryEvent, ai *persistence.ActivityInfo, ) bool { taggedScope := e.metricsClient.Scope(metrics.HistoryScheduleDecisionTaskScope).Tagged( metrics.DomainTag(e.domainEntry.GetInfo().Name), metrics.WorkflowTypeTag(e.GetWorkflowType().Name), metrics.TaskListTag(ai.TaskList)) taggedScope.IncCounter(metrics.DecisionTypeScheduleActivityDispatchCounter) _, err := e.shard.GetService().GetMatchingClient().AddActivityTask(ctx, &types.AddActivityTaskRequest{ DomainUUID: e.executionInfo.DomainID, SourceDomainUUID: e.domainEntry.GetInfo().ID, Execution: &types.WorkflowExecution{ WorkflowID: e.executionInfo.WorkflowID, RunID: e.executionInfo.RunID, }, TaskList: &types.TaskList{Name: ai.TaskList}, ScheduleID: scheduledEvent.ID, ScheduleToStartTimeoutSeconds: common.Int32Ptr(ai.ScheduleToStartTimeout), ActivityTaskDispatchInfo: &types.ActivityTaskDispatchInfo{ ScheduledEvent: scheduledEvent, StartedTimestamp: common.Int64Ptr(e.timeSource.Now().UnixNano()), WorkflowType: e.GetWorkflowType(), WorkflowDomain: e.GetDomainEntry().GetInfo().Name, ScheduledTimestampOfThisAttempt: common.Int64Ptr(ai.ScheduledTime.UnixNano()), }, PartitionConfig: e.executionInfo.PartitionConfig, }) if err == nil { taggedScope.IncCounter(metrics.DecisionTypeScheduleActivityDispatchSucceedCounter) return true } return false } func (e *mutableStateBuilder) ReplicateActivityTaskScheduledEvent( firstEventID int64, event *types.HistoryEvent, skipTaskGeneration bool, ) (*persistence.ActivityInfo, error) { attributes := event.ActivityTaskScheduledEventAttributes targetDomainID := e.executionInfo.DomainID if attributes.GetDomain() != "" { var err error targetDomainID, err = e.shard.GetDomainCache().GetDomainID(attributes.GetDomain()) if err != nil { return nil, err } } scheduleEventID := event.ID scheduleToCloseTimeout := attributes.GetScheduleToCloseTimeoutSeconds() ai := &persistence.ActivityInfo{ Version: event.Version, ScheduleID: scheduleEventID, ScheduledEventBatchID: firstEventID, ScheduledTime: time.Unix(0, event.GetTimestamp()), StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: attributes.ActivityID, DomainID: targetDomainID, ScheduleToStartTimeout: attributes.GetScheduleToStartTimeoutSeconds(), ScheduleToCloseTimeout: scheduleToCloseTimeout, StartToCloseTimeout: attributes.GetStartToCloseTimeoutSeconds(), HeartbeatTimeout: attributes.GetHeartbeatTimeoutSeconds(), CancelRequested: false, CancelRequestID: constants.EmptyEventID, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusNone, TaskList: attributes.TaskList.GetName(), TaskListKind: attributes.TaskList.GetKind(), HasRetryPolicy: attributes.RetryPolicy != nil, } if ai.HasRetryPolicy { ai.InitialInterval = attributes.RetryPolicy.GetInitialIntervalInSeconds() ai.BackoffCoefficient = attributes.RetryPolicy.GetBackoffCoefficient() ai.MaximumInterval = attributes.RetryPolicy.GetMaximumIntervalInSeconds() ai.MaximumAttempts = attributes.RetryPolicy.GetMaximumAttempts() ai.NonRetriableErrors = attributes.RetryPolicy.NonRetriableErrorReasons if attributes.RetryPolicy.GetExpirationIntervalInSeconds() != 0 { ai.ExpirationTime = ai.ScheduledTime.Add(time.Duration(attributes.RetryPolicy.GetExpirationIntervalInSeconds()) * time.Second) } } e.pendingActivityInfoIDs[scheduleEventID] = ai e.pendingActivityIDToEventID[ai.ActivityID] = scheduleEventID e.updateActivityInfos[ai.ScheduleID] = ai if !skipTaskGeneration { return ai, e.taskGenerator.GenerateActivityTransferTasks(event) } return ai, nil } func (e *mutableStateBuilder) addTransientActivityStartedEvent( scheduleEventID int64, ) error { ai, ok := e.GetActivityInfo(scheduleEventID) if !ok || ai.StartedID != constants.TransientEventID { return nil } // activity task was started (as transient event), we need to add it now. event := e.hBuilder.AddActivityTaskStartedEvent(scheduleEventID, ai.Attempt, ai.RequestID, ai.StartedIdentity, ai.LastFailureReason, ai.LastFailureDetails) if !ai.StartedTime.IsZero() { // overwrite started event time to the one recorded in ActivityInfo event.Timestamp = common.Int64Ptr(ai.StartedTime.UnixNano()) } return e.ReplicateActivityTaskStartedEvent(event) } func (e *mutableStateBuilder) AddActivityTaskStartedEvent( ai *persistence.ActivityInfo, scheduleEventID int64, requestID string, identity string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionActivityTaskStarted if err := e.checkMutability(opTag); err != nil { return nil, err } if !ai.HasRetryPolicy { event := e.hBuilder.AddActivityTaskStartedEvent(scheduleEventID, ai.Attempt, requestID, identity, ai.LastFailureReason, ai.LastFailureDetails) if err := e.ReplicateActivityTaskStartedEvent(event); err != nil { return nil, err } return event, nil } // we might need to retry, so do not append started event just yet, // instead update mutable state and will record started event when activity task is closed ai.Version = e.GetCurrentVersion() ai.StartedID = constants.TransientEventID ai.RequestID = requestID ai.StartedTime = e.timeSource.Now() ai.LastHeartBeatUpdatedTime = ai.StartedTime ai.StartedIdentity = identity if err := e.UpdateActivity(ai); err != nil { return nil, err } e.syncActivityTasks[ai.ScheduleID] = struct{}{} return nil, nil } func (e *mutableStateBuilder) ReplicateActivityTaskStartedEvent( event *types.HistoryEvent, ) error { attributes := event.ActivityTaskStartedEventAttributes scheduleID := attributes.GetScheduledEventID() ai, ok := e.GetActivityInfo(scheduleID) if !ok { e.logError( fmt.Sprintf("unable to find activity event id: %v in mutable state", scheduleID), tag.ErrorTypeInvalidMutableStateAction, ) return ErrMissingActivityInfo } ai.Version = event.Version ai.StartedID = event.ID ai.RequestID = attributes.GetRequestID() ai.StartedTime = time.Unix(0, event.GetTimestamp()) ai.LastHeartBeatUpdatedTime = ai.StartedTime e.updateActivityInfos[ai.ScheduleID] = ai return nil } func (e *mutableStateBuilder) AddActivityTaskCompletedEvent( scheduleEventID int64, startedEventID int64, request *types.RespondActivityTaskCompletedRequest, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionActivityTaskCompleted if err := e.checkMutability(opTag); err != nil { return nil, err } if ai, ok := e.GetActivityInfo(scheduleEventID); !ok || ai.StartedID != startedEventID { e.logger.Warn( mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(ok), tag.WorkflowScheduleID(scheduleEventID), tag.WorkflowStartedID(startedEventID)) return nil, e.createInternalServerError(opTag) } if err := e.addTransientActivityStartedEvent(scheduleEventID); err != nil { return nil, err } event := e.hBuilder.AddActivityTaskCompletedEvent(scheduleEventID, startedEventID, request) if err := e.ReplicateActivityTaskCompletedEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateActivityTaskCompletedEvent( event *types.HistoryEvent, ) error { attributes := event.ActivityTaskCompletedEventAttributes scheduleID := attributes.GetScheduledEventID() return e.DeleteActivity(scheduleID) } func (e *mutableStateBuilder) AddActivityTaskFailedEvent( scheduleEventID int64, startedEventID int64, request *types.RespondActivityTaskFailedRequest, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionActivityTaskFailed if err := e.checkMutability(opTag); err != nil { return nil, err } if ai, ok := e.GetActivityInfo(scheduleEventID); !ok || ai.StartedID != startedEventID { e.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(ok), tag.WorkflowScheduleID(scheduleEventID), tag.WorkflowStartedID(startedEventID)) return nil, e.createInternalServerError(opTag) } if err := e.addTransientActivityStartedEvent(scheduleEventID); err != nil { return nil, err } event := e.hBuilder.AddActivityTaskFailedEvent(scheduleEventID, startedEventID, request) if err := e.ReplicateActivityTaskFailedEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateActivityTaskFailedEvent( event *types.HistoryEvent, ) error { attributes := event.ActivityTaskFailedEventAttributes scheduleID := attributes.GetScheduledEventID() return e.DeleteActivity(scheduleID) } func (e *mutableStateBuilder) AddActivityTaskTimedOutEvent( scheduleEventID int64, startedEventID int64, timeoutType types.TimeoutType, lastHeartBeatDetails []byte, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionActivityTaskTimedOut if err := e.checkMutability(opTag); err != nil { return nil, err } ai, ok := e.GetActivityInfo(scheduleEventID) if !ok || ai.StartedID != startedEventID || ((timeoutType == types.TimeoutTypeStartToClose || timeoutType == types.TimeoutTypeHeartbeat) && ai.StartedID == constants.EmptyEventID) { e.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(ok), tag.WorkflowScheduleID(scheduleEventID), tag.WorkflowStartedID(startedEventID), tag.WorkflowTimeoutType(int64(timeoutType))) return nil, e.createInternalServerError(opTag) } if err := e.addTransientActivityStartedEvent(scheduleEventID); err != nil { return nil, err } event := e.hBuilder.AddActivityTaskTimedOutEvent(scheduleEventID, startedEventID, timeoutType, lastHeartBeatDetails, ai.LastFailureReason, ai.LastFailureDetails) if err := e.ReplicateActivityTaskTimedOutEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateActivityTaskTimedOutEvent( event *types.HistoryEvent, ) error { attributes := event.ActivityTaskTimedOutEventAttributes scheduleID := attributes.GetScheduledEventID() return e.DeleteActivity(scheduleID) } func (e *mutableStateBuilder) AddActivityTaskCancelRequestedEvent( decisionCompletedEventID int64, activityID string, identity string, ) (*types.HistoryEvent, *persistence.ActivityInfo, error) { opTag := tag.WorkflowActionActivityTaskCancelRequested if err := e.checkMutability(opTag); err != nil { return nil, nil, err } // we need to add the cancel request event even if activity not in mutable state // if activity not in mutable state or already cancel requested, // we do not need to call the replication function actCancelReqEvent := e.hBuilder.AddActivityTaskCancelRequestedEvent(decisionCompletedEventID, activityID) ai, ok := e.GetActivityByActivityID(activityID) if !ok || ai.CancelRequested { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(ok), tag.WorkflowActivityID(activityID)) return nil, nil, e.createCallerError(opTag) } if err := e.ReplicateActivityTaskCancelRequestedEvent(actCancelReqEvent); err != nil { return nil, nil, err } return actCancelReqEvent, ai, nil } func (e *mutableStateBuilder) ReplicateActivityTaskCancelRequestedEvent( event *types.HistoryEvent, ) error { attributes := event.ActivityTaskCancelRequestedEventAttributes activityID := attributes.GetActivityID() ai, ok := e.GetActivityByActivityID(activityID) if !ok { // On active side, if the ActivityTaskCancelRequested is invalid, it will created a RequestCancelActivityTaskFailed // Passive will rely on active side logic return nil } ai.Version = event.Version // - We have the activity dispatched to worker. // - The activity might not be heartbeating, but the activity can still call RecordActivityHeartBeat() // to see cancellation while reporting progress of the activity. ai.CancelRequested = true ai.CancelRequestID = event.ID e.updateActivityInfos[ai.ScheduleID] = ai return nil } func (e *mutableStateBuilder) AddRequestCancelActivityTaskFailedEvent( decisionCompletedEventID int64, activityID string, cause string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionActivityTaskCancelFailed if err := e.checkMutability(opTag); err != nil { return nil, err } return e.hBuilder.AddRequestCancelActivityTaskFailedEvent(decisionCompletedEventID, activityID, cause), nil } func (e *mutableStateBuilder) AddActivityTaskCanceledEvent( scheduleEventID int64, startedEventID int64, latestCancelRequestedEventID int64, details []byte, identity string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionActivityTaskCanceled if err := e.checkMutability(opTag); err != nil { return nil, err } ai, ok := e.GetActivityInfo(scheduleEventID) if !ok || ai.StartedID != startedEventID { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowScheduleID(scheduleEventID)) return nil, e.createInternalServerError(opTag) } // Verify cancel request as well. if !ai.CancelRequested { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowScheduleID(scheduleEventID), tag.WorkflowActivityID(ai.ActivityID), tag.WorkflowStartedID(ai.StartedID)) return nil, e.createInternalServerError(opTag) } if err := e.addTransientActivityStartedEvent(scheduleEventID); err != nil { return nil, err } event := e.hBuilder.AddActivityTaskCanceledEvent(scheduleEventID, startedEventID, latestCancelRequestedEventID, details, identity) if err := e.ReplicateActivityTaskCanceledEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateActivityTaskCanceledEvent( event *types.HistoryEvent, ) error { attributes := event.ActivityTaskCanceledEventAttributes scheduleID := attributes.GetScheduledEventID() return e.DeleteActivity(scheduleID) } func (e *mutableStateBuilder) RetryActivity( ai *persistence.ActivityInfo, failureReason string, failureDetails []byte, ) (bool, error) { opTag := tag.WorkflowActionActivityTaskRetry if err := e.checkMutability(opTag); err != nil { return false, err } if !ai.HasRetryPolicy || ai.CancelRequested { return false, nil } now := e.timeSource.Now() backoffInterval := getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, failureReason, ai.NonRetriableErrors, ) if backoffInterval == backoff.NoBackoff { return false, nil } // a retry is needed, update activity info for next retry ai.Version = e.GetCurrentVersion() ai.Attempt++ ai.ScheduledTime = now.Add(backoffInterval) // update to next schedule time ai.StartedID = constants.EmptyEventID ai.RequestID = "" ai.StartedTime = time.Time{} ai.TimerTaskStatus = TimerTaskStatusNone ai.LastFailureReason = failureReason ai.LastWorkerIdentity = ai.StartedIdentity ai.LastFailureDetails = failureDetails if err := e.taskGenerator.GenerateActivityRetryTasks( ai.ScheduleID, ); err != nil { return false, err } e.updateActivityInfos[ai.ScheduleID] = ai e.syncActivityTasks[ai.ScheduleID] = struct{}{} return true, nil } ================================================ FILE: service/history/execution/mutable_state_builder_methods_activity_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/shard" ) var currentTime = time.Unix(0, 1) func testMutableStateBuilder(t *testing.T) *mutableStateBuilder { ctrl := gomock.NewController(t) mockShard := shard.NewTestContext( t, ctrl, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) mockShard.Resource.TimeSource = clock.NewMockedTimeSourceAt(currentTime) // set the checksum probabilities to 100% for exercising during test mockShard.GetConfig().MutableStateChecksumGenProbability = func(domain string) int { return 100 } mockShard.GetConfig().MutableStateChecksumVerifyProbability = func(domain string) int { return 100 } mockShard.GetConfig().EnableRetryForChecksumFailure = func(domain string) bool { return true } logger := log.NewNoop() mockShard.Resource.MatchingClient.EXPECT().AddActivityTask(gomock.Any(), gomock.Any()).Return(&types.AddActivityTaskResponse{}, nil).AnyTimes() mockShard.Resource.DomainCache.EXPECT().GetDomainID(constants.TestDomainName).Return(constants.TestDomainID, nil).AnyTimes() mockShard.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(&cache.DomainCacheEntry{}, nil).AnyTimes() return newMutableStateBuilder(mockShard, logger, constants.TestLocalDomainEntry, constants.TestLocalDomainEntry.GetFailoverVersion()) } func Test__AddActivityTaskScheduledEvent(t *testing.T) { activityType := &types.ActivityType{Name: "activityType"} retryPolicy := &types.RetryPolicy{ InitialIntervalInSeconds: 11, BackoffCoefficient: 12, MaximumIntervalInSeconds: 13, MaximumAttempts: 14, NonRetriableErrorReasons: []string{"no retries please"}, ExpirationIntervalInSeconds: 15, } header := &types.Header{Fields: map[string][]byte{ "key": []byte("value"), }} cases := []struct { name string workflowTaskList *types.TaskList attr *types.ScheduleActivityTaskDecisionAttributes dispatch bool expectedAttributes *types.ActivityTaskScheduledEventAttributes expectedInfo *persistence.ActivityInfo expectedDispatch *types.ActivityLocalDispatchInfo expectedDispatched bool expectedStarted bool expectedError error }{ { name: "success", workflowTaskList: &types.TaskList{Name: "taskList", Kind: types.TaskListKindNormal.Ptr()}, attr: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "activityID", ActivityType: activityType, Domain: constants.TestDomainName, TaskList: &types.TaskList{Name: "taskList"}, Input: []byte("input"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(1), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(3), HeartbeatTimeoutSeconds: common.Int32Ptr(4), RetryPolicy: retryPolicy, Header: header, RequestLocalDispatch: false, }, expectedAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityID: "activityID", ActivityType: activityType, Domain: &constants.TestDomainName, TaskList: &types.TaskList{Name: "taskList"}, Input: []byte("input"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(1), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(3), HeartbeatTimeoutSeconds: common.Int32Ptr(4), DecisionTaskCompletedEventID: 0, RetryPolicy: retryPolicy, Header: header, }, expectedInfo: &persistence.ActivityInfo{ Version: commonconstants.EmptyVersion, ScheduleID: 1, ScheduledEventBatchID: 0, ScheduledTime: currentTime, StartedID: commonconstants.EmptyEventID, DomainID: constants.TestDomainID, ActivityID: "activityID", ScheduleToCloseTimeout: 1, ScheduleToStartTimeout: 2, StartToCloseTimeout: 3, HeartbeatTimeout: 4, CancelRequestID: commonconstants.EmptyEventID, TaskList: "taskList", TaskListKind: types.TaskListKindNormal, HasRetryPolicy: true, InitialInterval: retryPolicy.InitialIntervalInSeconds, BackoffCoefficient: retryPolicy.BackoffCoefficient, MaximumInterval: retryPolicy.MaximumIntervalInSeconds, ExpirationTime: currentTime.Add(time.Duration(retryPolicy.ExpirationIntervalInSeconds) * time.Second), MaximumAttempts: retryPolicy.MaximumAttempts, NonRetriableErrors: retryPolicy.NonRetriableErrorReasons, }, }, { name: "success - normal workflow with ephemeral activity", workflowTaskList: &types.TaskList{Name: "taskList", Kind: types.TaskListKindNormal.Ptr()}, attr: &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "activityID", ActivityType: activityType, Domain: constants.TestDomainName, TaskList: &types.TaskList{Name: "taskList", Kind: types.TaskListKindEphemeral.Ptr()}, Input: []byte("input"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(1), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(3), HeartbeatTimeoutSeconds: common.Int32Ptr(4), RetryPolicy: retryPolicy, Header: header, RequestLocalDispatch: false, }, expectedAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityID: "activityID", ActivityType: activityType, Domain: &constants.TestDomainName, TaskList: &types.TaskList{Name: "taskList", Kind: types.TaskListKindEphemeral.Ptr()}, Input: []byte("input"), ScheduleToCloseTimeoutSeconds: common.Int32Ptr(1), ScheduleToStartTimeoutSeconds: common.Int32Ptr(2), StartToCloseTimeoutSeconds: common.Int32Ptr(3), HeartbeatTimeoutSeconds: common.Int32Ptr(4), DecisionTaskCompletedEventID: 0, RetryPolicy: retryPolicy, Header: header, }, expectedInfo: &persistence.ActivityInfo{ Version: commonconstants.EmptyVersion, ScheduleID: 1, ScheduledEventBatchID: 0, ScheduledTime: currentTime, StartedID: commonconstants.EmptyEventID, DomainID: constants.TestDomainID, ActivityID: "activityID", ScheduleToCloseTimeout: 1, ScheduleToStartTimeout: 2, StartToCloseTimeout: 3, HeartbeatTimeout: 4, CancelRequestID: commonconstants.EmptyEventID, TaskList: "taskList", TaskListKind: types.TaskListKindEphemeral, HasRetryPolicy: true, InitialInterval: retryPolicy.InitialIntervalInSeconds, BackoffCoefficient: retryPolicy.BackoffCoefficient, MaximumInterval: retryPolicy.MaximumIntervalInSeconds, ExpirationTime: currentTime.Add(time.Duration(retryPolicy.ExpirationIntervalInSeconds) * time.Second), MaximumAttempts: retryPolicy.MaximumAttempts, NonRetriableErrors: retryPolicy.NonRetriableErrorReasons, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { mb := testMutableStateBuilder(t) mockEventsCache := mb.shard.GetEventsCache().(*events.MockCache) mockEventsCache.EXPECT().PutEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) if tc.workflowTaskList != nil { mb.executionInfo.TaskList = tc.workflowTaskList.Name mb.executionInfo.TaskListKind = tc.workflowTaskList.GetKind() } ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() event, activityInfo, dispatchInfo, dispatched, started, err := mb.AddActivityTaskScheduledEvent(ctx, 0, tc.attr, tc.dispatch) if tc.expectedError != nil { assert.ErrorIs(t, err, tc.expectedError) assert.Nil(t, event) assert.Nil(t, activityInfo) assert.Nil(t, dispatchInfo) } else { assert.NoError(t, err) assert.Equal(t, types.EventTypeActivityTaskScheduled.Ptr(), event.EventType) assert.Equal(t, tc.expectedAttributes, event.ActivityTaskScheduledEventAttributes) assert.Equal(t, tc.expectedInfo, activityInfo) assert.Equal(t, tc.expectedDispatch, dispatchInfo) } assert.Equal(t, tc.expectedDispatched, dispatched) assert.Equal(t, tc.expectedStarted, started) }) } } func Test__UpdateActivityProgress(t *testing.T) { mb := testMutableStateBuilder(t) ai := &persistence.ActivityInfo{ Version: 1, ScheduleID: 1, } request := &types.RecordActivityTaskHeartbeatRequest{ TaskToken: nil, Details: []byte{10, 0}, Identity: "", } assert.Equal(t, int64(1), ai.Version) mb.UpdateActivityProgress(ai, request) assert.Equal(t, commonconstants.EmptyVersion, ai.Version) assert.Equal(t, request.Details, ai.Details) assert.Equal(t, ai, mb.updateActivityInfos[ai.ScheduleID]) assert.NotNil(t, mb.syncActivityTasks[ai.ScheduleID]) } func Test__ReplicateActivityInfo(t *testing.T) { mb := testMutableStateBuilder(t) now := time.Now() nowUnix := now.UnixNano() request := &types.SyncActivityRequest{ ScheduledID: 1, Version: 1, ScheduledTime: &nowUnix, LastHeartbeatTime: &nowUnix, } ai := &persistence.ActivityInfo{} err := mb.ReplicateActivityInfo(request, true) assert.Error(t, err) assert.Equal(t, ErrMissingActivityInfo, err) mb.pendingActivityInfoIDs[request.ScheduledID] = ai err = mb.ReplicateActivityInfo(request, true) assert.NoError(t, err) assert.Equal(t, int64(1), ai.Version) assert.Equal(t, now.UTC(), ai.ScheduledTime.UTC()) assert.Equal(t, request.StartedID, ai.StartedID) assert.Equal(t, now.UTC(), ai.LastHeartBeatUpdatedTime.UTC()) } func Test__UpdateActivity(t *testing.T) { mb := testMutableStateBuilder(t) ai := &persistence.ActivityInfo{ScheduleID: 1} t.Run("error missing activity info", func(t *testing.T) { err := mb.UpdateActivity(ai) assert.Error(t, err) assert.Equal(t, ErrMissingActivityInfo, err) }) t.Run("update success", func(t *testing.T) { mb.pendingActivityInfoIDs[1] = ai err := mb.UpdateActivity(ai) assert.NoError(t, err) assert.Equal(t, ai, mb.updateActivityInfos[1]) }) } func Test__GetActivityScheduledEvent(t *testing.T) { mb := testMutableStateBuilder(t) ai := &persistence.ActivityInfo{ ScheduleID: 1, ScheduledEvent: &types.HistoryEvent{}, } t.Run("error missing activity info", func(t *testing.T) { _, err := mb.GetActivityScheduledEvent(context.Background(), ai.ScheduleID) assert.Error(t, err) assert.Equal(t, ErrMissingActivityInfo, err) }) t.Run("scheduled event from activity info", func(t *testing.T) { mb.pendingActivityInfoIDs[1] = ai result, err := mb.GetActivityScheduledEvent(context.Background(), ai.ScheduleID) assert.NoError(t, err) assert.Equal(t, ai.ScheduledEvent, result) }) t.Run("scheduled event from events cache", func(t *testing.T) { mockEventsCache := mb.shard.GetEventsCache().(*events.MockCache) mockEventsCache.EXPECT().GetEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.HistoryEvent{}, nil) mb.pendingActivityInfoIDs[1] = &persistence.ActivityInfo{ ScheduleID: 1, } result, err := mb.GetActivityScheduledEvent(context.Background(), ai.ScheduleID) assert.NoError(t, err) assert.Equal(t, &types.HistoryEvent{}, result) }) t.Run("error missing scheduled event", func(t *testing.T) { mockEventsCache := mb.shard.GetEventsCache().(*events.MockCache) mockEventsCache.EXPECT().GetEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, assert.AnError) mb.pendingActivityInfoIDs[1] = &persistence.ActivityInfo{ ScheduleID: 1, } _, err := mb.GetActivityScheduledEvent(context.Background(), ai.ScheduleID) assert.Error(t, err) assert.Equal(t, ErrMissingActivityScheduledEvent, err) }) } func Test__AddActivityTaskCompletedEvent(t *testing.T) { mb := testMutableStateBuilder(t) request := &types.RespondActivityTaskCompletedRequest{ TaskToken: nil, Result: nil, Identity: "", } t.Run("error workflow finished", func(t *testing.T) { mbCompleted := testMutableStateBuilder(t) mbCompleted.executionInfo.State = persistence.WorkflowStateCompleted _, err := mbCompleted.AddActivityTaskCompletedEvent(1, 1, request) assert.Error(t, err) assert.Equal(t, ErrWorkflowFinished, err) }) t.Run("error getting activity info", func(t *testing.T) { _, err := mb.AddActivityTaskCompletedEvent(1, 1, request) assert.Error(t, err) assert.Equal(t, "add-activitytask-completed-event operation failed", err.Error()) }) t.Run("success", func(t *testing.T) { ai := &persistence.ActivityInfo{ ScheduleID: 1, ActivityID: "1", ScheduledEvent: &types.HistoryEvent{}, StartedID: 1, } mb.pendingActivityInfoIDs[1] = ai mb.pendingActivityIDToEventID["1"] = 1 mb.updateActivityInfos[1] = ai mb.hBuilder = NewHistoryBuilder(mb) event, err := mb.AddActivityTaskCompletedEvent(1, 1, request) assert.NoError(t, err) assert.Equal(t, int64(1), event.ActivityTaskCompletedEventAttributes.ScheduledEventID) }) } func Test__tryDispatchActivityTask(t *testing.T) { mb := testMutableStateBuilder(t) event := &types.HistoryEvent{} ai := &persistence.ActivityInfo{} result := mb.tryDispatchActivityTask(context.Background(), event, ai) assert.True(t, result) } func Test__ReplicateActivityTaskCanceledEvent(t *testing.T) { mb := testMutableStateBuilder(t) event := &types.HistoryEvent{ EventType: types.EventTypeActivityTaskCanceled.Ptr(), ActivityTaskCanceledEventAttributes: &types.ActivityTaskCanceledEventAttributes{ ScheduledEventID: 1, }, } ai := &persistence.ActivityInfo{ ActivityID: "1", } mb.pendingActivityInfoIDs[1] = ai mb.pendingActivityIDToEventID["1"] = 1 err := mb.ReplicateActivityTaskCanceledEvent(event) assert.NoError(t, err) _, ok := mb.pendingActivityInfoIDs[1] assert.False(t, ok) _, ok = mb.pendingActivityIDToEventID["1"] assert.False(t, ok) } func Test__ReplicateActivityTaskCancelRequestedEvent(t *testing.T) { mb := testMutableStateBuilder(t) event := &types.HistoryEvent{ EventType: types.EventTypeActivityTaskCanceled.Ptr(), ActivityTaskCancelRequestedEventAttributes: &types.ActivityTaskCancelRequestedEventAttributes{ ActivityID: "1", }, } ai := &persistence.ActivityInfo{ ActivityID: "1", ScheduleID: 1, } mb.pendingActivityInfoIDs[1] = ai mb.pendingActivityIDToEventID["1"] = 1 err := mb.ReplicateActivityTaskCancelRequestedEvent(event) assert.NoError(t, err) assert.Equal(t, ai, mb.updateActivityInfos[1]) } func Test__ReplicateActivityTaskTimedOutEvent(t *testing.T) { mb := testMutableStateBuilder(t) event := &types.HistoryEvent{ EventType: types.EventTypeActivityTaskTimedOut.Ptr(), ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 1, }, } ai := &persistence.ActivityInfo{ ActivityID: "1", } mb.pendingActivityInfoIDs[1] = ai mb.pendingActivityIDToEventID["1"] = 1 err := mb.ReplicateActivityTaskTimedOutEvent(event) assert.NoError(t, err) _, ok := mb.pendingActivityInfoIDs[1] assert.False(t, ok) _, ok = mb.pendingActivityIDToEventID["1"] assert.False(t, ok) } func Test__ReplicateActivityTaskFailedEvent(t *testing.T) { mb := testMutableStateBuilder(t) event := &types.HistoryEvent{ EventType: types.EventTypeActivityTaskFailed.Ptr(), ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ ScheduledEventID: 1, }, } ai := &persistence.ActivityInfo{ ActivityID: "1", } mb.pendingActivityInfoIDs[1] = ai mb.pendingActivityIDToEventID["1"] = 1 err := mb.ReplicateActivityTaskFailedEvent(event) assert.NoError(t, err) _, ok := mb.pendingActivityInfoIDs[1] assert.False(t, ok) _, ok = mb.pendingActivityIDToEventID["1"] assert.False(t, ok) } func Test__ReplicateActivityTaskCompletedEvent(t *testing.T) { mb := testMutableStateBuilder(t) event := &types.HistoryEvent{ EventType: types.EventTypeActivityTaskCompleted.Ptr(), ActivityTaskCompletedEventAttributes: &types.ActivityTaskCompletedEventAttributes{ ScheduledEventID: 1, }, } ai := &persistence.ActivityInfo{ ActivityID: "1", } mb.pendingActivityInfoIDs[1] = ai mb.pendingActivityIDToEventID["1"] = 1 err := mb.ReplicateActivityTaskCompletedEvent(event) assert.NoError(t, err) _, ok := mb.pendingActivityInfoIDs[1] assert.False(t, ok) _, ok = mb.pendingActivityIDToEventID["1"] assert.False(t, ok) } func Test__AddActivityTaskCanceledEvent(t *testing.T) { t.Run("error workflow finished", func(t *testing.T) { mbCompleted := testMutableStateBuilder(t) mbCompleted.executionInfo.State = persistence.WorkflowStateCompleted _, err := mbCompleted.AddActivityTaskCanceledEvent(1, 1, 1, []byte{10}, "test") assert.Error(t, err) assert.Equal(t, ErrWorkflowFinished, err) }) t.Run("error getting activity info", func(t *testing.T) { mb := testMutableStateBuilder(t) _, err := mb.AddActivityTaskCanceledEvent(1, 1, 1, []byte{10}, "test") assert.Error(t, err) assert.Equal(t, "add-activitytask-canceled-event operation failed", err.Error()) }) t.Run("error cancel not requested", func(t *testing.T) { mb := testMutableStateBuilder(t) mb.hBuilder = NewHistoryBuilder(mb) ai := &persistence.ActivityInfo{ StartedID: 1, CancelRequested: false, StartedTime: time.Now(), } mb.pendingActivityInfoIDs[1] = ai _, err := mb.AddActivityTaskCanceledEvent(1, 1, 1, []byte{10}, "test") assert.Error(t, err) }) t.Run("success", func(t *testing.T) { mb := testMutableStateBuilder(t) mb.hBuilder = NewHistoryBuilder(mb) ai := &persistence.ActivityInfo{ StartedID: 1, CancelRequested: true, StartedTime: time.Now(), } mb.pendingActivityInfoIDs[1] = ai event, err := mb.AddActivityTaskCanceledEvent(1, 1, 1, []byte{10}, "test") assert.NoError(t, err) assert.Equal(t, int64(1), event.ActivityTaskCanceledEventAttributes.ScheduledEventID) assert.Equal(t, "test", event.ActivityTaskCanceledEventAttributes.Identity) }) } func Test__AddRequestCancelActivityTaskFailedEvent(t *testing.T) { t.Run("error workflow finished", func(t *testing.T) { mbCompleted := testMutableStateBuilder(t) mbCompleted.executionInfo.State = persistence.WorkflowStateCompleted _, err := mbCompleted.AddRequestCancelActivityTaskFailedEvent(1, "1", "test") assert.Error(t, err) assert.Equal(t, ErrWorkflowFinished, err) }) t.Run("success", func(t *testing.T) { mb := testMutableStateBuilder(t) mb.hBuilder = NewHistoryBuilder(mb) event, err := mb.AddRequestCancelActivityTaskFailedEvent(1, "1", "test") assert.NoError(t, err) assert.Equal(t, int64(1), event.RequestCancelActivityTaskFailedEventAttributes.DecisionTaskCompletedEventID) assert.Equal(t, "test", event.RequestCancelActivityTaskFailedEventAttributes.Cause) }) } func Test__AddActivityTaskCancelRequestedEvent(t *testing.T) { t.Run("error workflow finished", func(t *testing.T) { mbCompleted := testMutableStateBuilder(t) mbCompleted.executionInfo.State = persistence.WorkflowStateCompleted _, _, err := mbCompleted.AddActivityTaskCancelRequestedEvent(1, "1", "test") assert.Error(t, err) assert.Equal(t, ErrWorkflowFinished, err) }) t.Run("error getting activity info", func(t *testing.T) { mb := testMutableStateBuilder(t) mb.hBuilder = NewHistoryBuilder(mb) _, _, err := mb.AddActivityTaskCancelRequestedEvent(1, "1", "test") assert.Error(t, err) assert.Equal(t, "invalid history builder state for action: add-activitytask-cancel-requested-event", err.Error()) }) t.Run("success", func(t *testing.T) { mb := testMutableStateBuilder(t) mb.hBuilder = NewHistoryBuilder(mb) ai := &persistence.ActivityInfo{ StartedID: 1, StartedTime: time.Now(), } mb.pendingActivityInfoIDs[1] = ai mb.pendingActivityIDToEventID["1"] = 1 event, ai, err := mb.AddActivityTaskCancelRequestedEvent(1, "1", "test") assert.NoError(t, err) assert.Equal(t, "1", event.ActivityTaskCancelRequestedEventAttributes.ActivityID) }) } func Test__AddActivityTaskTimedOutEvent(t *testing.T) { t.Run("error workflow finished", func(t *testing.T) { mbCompleted := testMutableStateBuilder(t) mbCompleted.executionInfo.State = persistence.WorkflowStateCompleted _, err := mbCompleted.AddActivityTaskTimedOutEvent(1, 1, types.TimeoutTypeHeartbeat, []byte{10}) assert.Error(t, err) assert.Equal(t, ErrWorkflowFinished, err) }) t.Run("error getting activity info", func(t *testing.T) { mb := testMutableStateBuilder(t) _, err := mb.AddActivityTaskTimedOutEvent(1, 1, types.TimeoutTypeHeartbeat, []byte{10}) assert.Error(t, err) assert.Equal(t, "add-activitytask-timed-event operation failed", err.Error()) }) t.Run("success", func(t *testing.T) { mb := testMutableStateBuilder(t) ai := &persistence.ActivityInfo{ ScheduleID: 1, ActivityID: "1", ScheduledEvent: &types.HistoryEvent{}, StartedID: 1, } mb.pendingActivityInfoIDs[1] = ai mb.hBuilder = NewHistoryBuilder(mb) event, err := mb.AddActivityTaskTimedOutEvent(1, 1, types.TimeoutTypeHeartbeat, []byte{10}) assert.NoError(t, err) assert.Equal(t, int64(1), event.ActivityTaskTimedOutEventAttributes.ScheduledEventID) }) } func Test__AddActivityTaskFailedEvent(t *testing.T) { t.Run("error workflow finished", func(t *testing.T) { mbCompleted := testMutableStateBuilder(t) mbCompleted.executionInfo.State = persistence.WorkflowStateCompleted _, err := mbCompleted.AddActivityTaskFailedEvent(1, 1, &types.RespondActivityTaskFailedRequest{}) assert.Error(t, err) assert.Equal(t, ErrWorkflowFinished, err) }) t.Run("error getting activity info", func(t *testing.T) { mb := testMutableStateBuilder(t) _, err := mb.AddActivityTaskFailedEvent(1, 1, &types.RespondActivityTaskFailedRequest{}) assert.Error(t, err) assert.Equal(t, "add-activitytask-failed-event operation failed", err.Error()) }) t.Run("success", func(t *testing.T) { mb := testMutableStateBuilder(t) ai := &persistence.ActivityInfo{ ScheduleID: 1, ActivityID: "1", ScheduledEvent: &types.HistoryEvent{}, StartedID: 1, } mb.pendingActivityInfoIDs[1] = ai mb.hBuilder = NewHistoryBuilder(mb) event, err := mb.AddActivityTaskFailedEvent(1, 1, &types.RespondActivityTaskFailedRequest{ Identity: "test", Details: make([]byte, 10), }) assert.NoError(t, err) assert.Equal(t, int64(1), event.ActivityTaskFailedEventAttributes.ScheduledEventID) }) } ================================================ FILE: service/history/execution/mutable_state_builder_methods_cancellation.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func (e *mutableStateBuilder) IsCancelRequested() (bool, string) { if e.executionInfo.CancelRequested { return e.executionInfo.CancelRequested, e.executionInfo.CancelRequestID } return false, "" } // GetRequestCancelInfo gives details about a request cancellation that is currently in progress. func (e *mutableStateBuilder) GetRequestCancelInfo( initiatedEventID int64, ) (*persistence.RequestCancelInfo, bool) { ri, ok := e.pendingRequestCancelInfoIDs[initiatedEventID] return ri, ok } func (e *mutableStateBuilder) AddWorkflowExecutionCancelRequestedEvent( cause string, request *types.HistoryRequestCancelWorkflowExecutionRequest, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowCancelRequested if err := e.checkMutability(opTag); err != nil { return nil, err } if e.executionInfo.CancelRequested { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowState(e.executionInfo.State), tag.Bool(e.executionInfo.CancelRequested), tag.Key(e.executionInfo.CancelRequestID), ) return nil, e.createInternalServerError(opTag) } event := e.hBuilder.AddWorkflowExecutionCancelRequestedEvent(cause, request) if err := e.ReplicateWorkflowExecutionCancelRequestedEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateWorkflowExecutionCancelRequestedEvent( event *types.HistoryEvent, ) error { e.executionInfo.CancelRequested = true requestID := event.WorkflowExecutionCancelRequestedEventAttributes.RequestID e.executionInfo.CancelRequestID = requestID e.insertWorkflowRequest(persistence.WorkflowRequest{ RequestID: requestID, Version: event.Version, RequestType: persistence.WorkflowRequestTypeCancel, }) return nil } func (e *mutableStateBuilder) AddWorkflowExecutionCanceledEvent( decisionTaskCompletedEventID int64, attributes *types.CancelWorkflowExecutionDecisionAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowCanceled if err := e.checkMutability(opTag); err != nil { return nil, err } event := e.hBuilder.AddWorkflowExecutionCanceledEvent(decisionTaskCompletedEventID, attributes) if err := e.ReplicateWorkflowExecutionCanceledEvent(decisionTaskCompletedEventID, event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateWorkflowExecutionCanceledEvent( firstEventID int64, event *types.HistoryEvent, ) error { if err := e.UpdateWorkflowStateCloseStatus( persistence.WorkflowStateCompleted, persistence.WorkflowCloseStatusCanceled, ); err != nil { return err } e.executionInfo.CompletionEventBatchID = firstEventID // Used when completion event needs to be loaded from database e.ClearStickyness() e.writeEventToCache(event) return e.taskGenerator.GenerateWorkflowCloseTasks(event, e.config.WorkflowDeletionJitterRange(e.domainEntry.GetInfo().Name)) } func (e *mutableStateBuilder) AddRequestCancelExternalWorkflowExecutionInitiatedEvent( decisionCompletedEventID int64, cancelRequestID string, request *types.RequestCancelExternalWorkflowExecutionDecisionAttributes, ) (*types.HistoryEvent, *persistence.RequestCancelInfo, error) { opTag := tag.WorkflowActionExternalWorkflowCancelInitiated if err := e.checkMutability(opTag); err != nil { return nil, nil, err } event := e.hBuilder.AddRequestCancelExternalWorkflowExecutionInitiatedEvent(decisionCompletedEventID, request) rci, err := e.ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent(decisionCompletedEventID, event, cancelRequestID) if err != nil { return nil, nil, err } return event, rci, nil } func (e *mutableStateBuilder) ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent( firstEventID int64, event *types.HistoryEvent, cancelRequestID string, ) (*persistence.RequestCancelInfo, error) { // TODO: Evaluate if we need cancelRequestID also part of history event initiatedEventID := event.ID rci := &persistence.RequestCancelInfo{ Version: event.Version, InitiatedEventBatchID: firstEventID, InitiatedID: initiatedEventID, CancelRequestID: cancelRequestID, } e.pendingRequestCancelInfoIDs[rci.InitiatedID] = rci e.updateRequestCancelInfos[rci.InitiatedID] = rci return rci, e.taskGenerator.GenerateRequestCancelExternalTasks(event) } func (e *mutableStateBuilder) AddExternalWorkflowExecutionCancelRequested( initiatedID int64, domain string, workflowID string, runID string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionExternalWorkflowCancelRequested if err := e.checkMutability(opTag); err != nil { return nil, err } _, ok := e.GetRequestCancelInfo(initiatedID) if !ok { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } event := e.hBuilder.AddExternalWorkflowExecutionCancelRequested(initiatedID, domain, workflowID, runID) if err := e.ReplicateExternalWorkflowExecutionCancelRequested(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateExternalWorkflowExecutionCancelRequested( event *types.HistoryEvent, ) error { initiatedID := event.ExternalWorkflowExecutionCancelRequestedEventAttributes.GetInitiatedEventID() return e.DeletePendingRequestCancel(initiatedID) } func (e *mutableStateBuilder) AddRequestCancelExternalWorkflowExecutionFailedEvent( decisionTaskCompletedEventID int64, initiatedID int64, domain string, workflowID string, runID string, cause types.CancelExternalWorkflowExecutionFailedCause, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionExternalWorkflowCancelFailed if err := e.checkMutability(opTag); err != nil { return nil, err } _, ok := e.GetRequestCancelInfo(initiatedID) if !ok { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } event := e.hBuilder.AddRequestCancelExternalWorkflowExecutionFailedEvent(decisionTaskCompletedEventID, initiatedID, domain, workflowID, runID, cause) if err := e.ReplicateRequestCancelExternalWorkflowExecutionFailedEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateRequestCancelExternalWorkflowExecutionFailedEvent( event *types.HistoryEvent, ) error { initiatedID := event.RequestCancelExternalWorkflowExecutionFailedEventAttributes.GetInitiatedEventID() return e.DeletePendingRequestCancel(initiatedID) } ================================================ FILE: service/history/execution/mutable_state_builder_methods_child_workflow.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "context" "fmt" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // GetChildExecutionInfo gives details about a child execution that is currently in progress. func (e *mutableStateBuilder) GetChildExecutionInfo( initiatedEventID int64, ) (*persistence.ChildExecutionInfo, bool) { ci, ok := e.pendingChildExecutionInfoIDs[initiatedEventID] return ci, ok } // GetChildExecutionInitiatedEvent reads out the ChildExecutionInitiatedEvent from mutable state for in-progress child // executions func (e *mutableStateBuilder) GetChildExecutionInitiatedEvent( ctx context.Context, initiatedEventID int64, ) (*types.HistoryEvent, error) { ci, ok := e.pendingChildExecutionInfoIDs[initiatedEventID] if !ok { return nil, ErrMissingChildWorkflowInfo } // Needed for backward compatibility reason if ci.InitiatedEvent != nil { return ci.InitiatedEvent, nil } currentBranchToken, err := e.GetCurrentBranchToken() if err != nil { return nil, err } initiatedEvent, err := e.eventsCache.GetEvent( ctx, e.shard.GetShardID(), e.executionInfo.DomainID, e.executionInfo.WorkflowID, e.executionInfo.RunID, ci.InitiatedEventBatchID, ci.InitiatedID, currentBranchToken, ) if err != nil { // do not return the original error // since original error can be of type entity not exists // which can cause task processing side to fail silently // However, if the error is a persistence transient error, // we return the original error, because we fail to get // the event because of failure from database if persistence.IsTransientError(err) { return nil, err } return nil, ErrMissingChildWorkflowInitiatedEvent } return initiatedEvent, nil } func (e *mutableStateBuilder) GetPendingChildExecutionInfos() map[int64]*persistence.ChildExecutionInfo { return e.pendingChildExecutionInfoIDs } // DeletePendingChildExecution deletes details about a ChildExecutionInfo. func (e *mutableStateBuilder) DeletePendingChildExecution( initiatedEventID int64, ) error { if _, ok := e.pendingChildExecutionInfoIDs[initiatedEventID]; ok { delete(e.pendingChildExecutionInfoIDs, initiatedEventID) } else { e.logError( fmt.Sprintf("unable to find child workflow event ID: %v in mutable state", initiatedEventID), tag.ErrorTypeInvalidMutableStateAction, ) // log data inconsistency instead of returning an error e.logDataInconsistency() } delete(e.updateChildExecutionInfos, initiatedEventID) e.deleteChildExecutionInfos[initiatedEventID] = struct{}{} return nil } func (e *mutableStateBuilder) AddStartChildWorkflowExecutionInitiatedEvent( decisionCompletedEventID int64, createRequestID string, attributes *types.StartChildWorkflowExecutionDecisionAttributes, ) (*types.HistoryEvent, *persistence.ChildExecutionInfo, error) { opTag := tag.WorkflowActionChildWorkflowInitiated if err := e.checkMutability(opTag); err != nil { return nil, nil, err } event := e.hBuilder.AddStartChildWorkflowExecutionInitiatedEvent( decisionCompletedEventID, attributes, e.GetDomainEntry().GetInfo().Name, ) // Write the event to cache only on active cluster e.eventsCache.PutEvent(e.executionInfo.DomainID, e.executionInfo.WorkflowID, e.executionInfo.RunID, event.ID, event) ci, err := e.ReplicateStartChildWorkflowExecutionInitiatedEvent(decisionCompletedEventID, event, createRequestID) if err != nil { return nil, nil, err } return event, ci, nil } func (e *mutableStateBuilder) ReplicateStartChildWorkflowExecutionInitiatedEvent( firstEventID int64, event *types.HistoryEvent, createRequestID string, ) (*persistence.ChildExecutionInfo, error) { initiatedEventID := event.ID attributes := event.StartChildWorkflowExecutionInitiatedEventAttributes domainID := e.GetExecutionInfo().DomainID if domainName := attributes.GetDomain(); domainName != "" { // domainName may still be empty if two cadence clusters are running different versions var err error domainID, err = e.shard.GetDomainCache().GetDomainID(domainName) if err != nil { return nil, err } } ci := &persistence.ChildExecutionInfo{ Version: event.Version, InitiatedID: initiatedEventID, InitiatedEventBatchID: firstEventID, StartedID: constants.EmptyEventID, StartedWorkflowID: attributes.GetWorkflowID(), CreateRequestID: createRequestID, DomainID: domainID, // DomainName field is being deprecated // DomainName: attributes.GetDomain(), WorkflowTypeName: attributes.GetWorkflowType().GetName(), ParentClosePolicy: attributes.GetParentClosePolicy(), } e.pendingChildExecutionInfoIDs[ci.InitiatedID] = ci e.updateChildExecutionInfos[ci.InitiatedID] = ci return ci, e.taskGenerator.GenerateChildWorkflowTasks(event) } func (e *mutableStateBuilder) AddChildWorkflowExecutionStartedEvent( domain string, execution *types.WorkflowExecution, workflowType *types.WorkflowType, initiatedID int64, header *types.Header, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionChildWorkflowStarted if err := e.checkMutability(opTag); err != nil { return nil, err } ci, ok := e.GetChildExecutionInfo(initiatedID) if !ok || ci.StartedID != constants.EmptyEventID { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(ok), tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } event := e.hBuilder.AddChildWorkflowExecutionStartedEvent(domain, execution, workflowType, initiatedID, header) if err := e.ReplicateChildWorkflowExecutionStartedEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateChildWorkflowExecutionStartedEvent( event *types.HistoryEvent, ) error { attributes := event.ChildWorkflowExecutionStartedEventAttributes initiatedID := attributes.GetInitiatedEventID() ci, ok := e.GetChildExecutionInfo(initiatedID) if !ok { e.logError( "Unable to find child workflow", tag.ErrorTypeInvalidMutableStateAction, tag.WorkflowEventID(e.GetNextEventID()), tag.WorkflowInitiatedID(initiatedID), ) return ErrMissingChildWorkflowInfo } ci.StartedID = event.ID ci.StartedRunID = attributes.GetWorkflowExecution().GetRunID() e.updateChildExecutionInfos[ci.InitiatedID] = ci return nil } func (e *mutableStateBuilder) AddStartChildWorkflowExecutionFailedEvent( initiatedID int64, cause types.ChildWorkflowExecutionFailedCause, initiatedEventAttributes *types.StartChildWorkflowExecutionInitiatedEventAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionChildWorkflowInitiationFailed if err := e.checkMutability(opTag); err != nil { return nil, err } ci, ok := e.GetChildExecutionInfo(initiatedID) if !ok || ci.StartedID != constants.EmptyEventID { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(ok), tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } event := e.hBuilder.AddStartChildWorkflowExecutionFailedEvent(initiatedID, cause, initiatedEventAttributes) if err := e.ReplicateStartChildWorkflowExecutionFailedEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateStartChildWorkflowExecutionFailedEvent( event *types.HistoryEvent, ) error { attributes := event.StartChildWorkflowExecutionFailedEventAttributes initiatedID := attributes.GetInitiatedEventID() return e.DeletePendingChildExecution(initiatedID) } func (e *mutableStateBuilder) AddChildWorkflowExecutionCompletedEvent( initiatedID int64, childExecution *types.WorkflowExecution, attributes *types.WorkflowExecutionCompletedEventAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionChildWorkflowCompleted if err := e.checkMutability(opTag); err != nil { return nil, err } ci, ok := e.GetChildExecutionInfo(initiatedID) if !ok || ci.StartedID == constants.EmptyEventID { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(ok), tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } childDomainName, err := GetChildExecutionDomainName(ci, e.shard.GetDomainCache(), e.GetDomainEntry()) if err != nil { return nil, err } workflowType := &types.WorkflowType{ Name: ci.WorkflowTypeName, } event := e.hBuilder.AddChildWorkflowExecutionCompletedEvent( childDomainName, childExecution, workflowType, ci.InitiatedID, ci.StartedID, attributes, ) if err := e.ReplicateChildWorkflowExecutionCompletedEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateChildWorkflowExecutionCompletedEvent( event *types.HistoryEvent, ) error { attributes := event.ChildWorkflowExecutionCompletedEventAttributes initiatedID := attributes.GetInitiatedEventID() return e.DeletePendingChildExecution(initiatedID) } func (e *mutableStateBuilder) AddChildWorkflowExecutionFailedEvent( initiatedID int64, childExecution *types.WorkflowExecution, attributes *types.WorkflowExecutionFailedEventAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionChildWorkflowFailed if err := e.checkMutability(opTag); err != nil { return nil, err } ci, ok := e.GetChildExecutionInfo(initiatedID) if !ok || ci.StartedID == constants.EmptyEventID { e.logWarn(mutableStateInvalidHistoryActionMsg, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(!ok), tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } domainName, err := GetChildExecutionDomainName(ci, e.shard.GetDomainCache(), e.GetDomainEntry()) if err != nil { return nil, err } workflowType := &types.WorkflowType{ Name: ci.WorkflowTypeName, } event := e.hBuilder.AddChildWorkflowExecutionFailedEvent( domainName, childExecution, workflowType, ci.InitiatedID, ci.StartedID, attributes, ) if err := e.ReplicateChildWorkflowExecutionFailedEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateChildWorkflowExecutionFailedEvent( event *types.HistoryEvent, ) error { attributes := event.ChildWorkflowExecutionFailedEventAttributes initiatedID := attributes.GetInitiatedEventID() return e.DeletePendingChildExecution(initiatedID) } func (e *mutableStateBuilder) AddChildWorkflowExecutionCanceledEvent( initiatedID int64, childExecution *types.WorkflowExecution, attributes *types.WorkflowExecutionCanceledEventAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionChildWorkflowCanceled if err := e.checkMutability(opTag); err != nil { return nil, err } ci, ok := e.GetChildExecutionInfo(initiatedID) if !ok || ci.StartedID == constants.EmptyEventID { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(ok), tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } domainName, err := GetChildExecutionDomainName(ci, e.shard.GetDomainCache(), e.GetDomainEntry()) if err != nil { return nil, err } workflowType := &types.WorkflowType{ Name: ci.WorkflowTypeName, } event := e.hBuilder.AddChildWorkflowExecutionCanceledEvent( domainName, childExecution, workflowType, ci.InitiatedID, ci.StartedID, attributes, ) if err := e.ReplicateChildWorkflowExecutionCanceledEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateChildWorkflowExecutionCanceledEvent( event *types.HistoryEvent, ) error { attributes := event.ChildWorkflowExecutionCanceledEventAttributes initiatedID := attributes.GetInitiatedEventID() return e.DeletePendingChildExecution(initiatedID) } func (e *mutableStateBuilder) AddChildWorkflowExecutionTerminatedEvent( initiatedID int64, childExecution *types.WorkflowExecution, attributes *types.WorkflowExecutionTerminatedEventAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionChildWorkflowTerminated if err := e.checkMutability(opTag); err != nil { return nil, err } ci, ok := e.GetChildExecutionInfo(initiatedID) if !ok || ci.StartedID == constants.EmptyEventID { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(ok), tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } domainName, err := GetChildExecutionDomainName(ci, e.shard.GetDomainCache(), e.GetDomainEntry()) if err != nil { return nil, err } workflowType := &types.WorkflowType{ Name: ci.WorkflowTypeName, } event := e.hBuilder.AddChildWorkflowExecutionTerminatedEvent( domainName, childExecution, workflowType, ci.InitiatedID, ci.StartedID, attributes, ) if err := e.ReplicateChildWorkflowExecutionTerminatedEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateChildWorkflowExecutionTerminatedEvent( event *types.HistoryEvent, ) error { attributes := event.ChildWorkflowExecutionTerminatedEventAttributes initiatedID := attributes.GetInitiatedEventID() return e.DeletePendingChildExecution(initiatedID) } func (e *mutableStateBuilder) AddChildWorkflowExecutionTimedOutEvent( initiatedID int64, childExecution *types.WorkflowExecution, attributes *types.WorkflowExecutionTimedOutEventAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionChildWorkflowTimedOut if err := e.checkMutability(opTag); err != nil { return nil, err } ci, ok := e.GetChildExecutionInfo(initiatedID) if !ok || ci.StartedID == constants.EmptyEventID { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.Bool(ok), tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } domainName, err := GetChildExecutionDomainName(ci, e.shard.GetDomainCache(), e.GetDomainEntry()) if err != nil { return nil, err } workflowType := &types.WorkflowType{ Name: ci.WorkflowTypeName, } event := e.hBuilder.AddChildWorkflowExecutionTimedOutEvent( domainName, childExecution, workflowType, ci.InitiatedID, ci.StartedID, attributes, ) if err := e.ReplicateChildWorkflowExecutionTimedOutEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateChildWorkflowExecutionTimedOutEvent( event *types.HistoryEvent, ) error { attributes := event.ChildWorkflowExecutionTimedOutEventAttributes initiatedID := attributes.GetInitiatedEventID() return e.DeletePendingChildExecution(initiatedID) } ================================================ FILE: service/history/execution/mutable_state_builder_methods_child_workflow_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/shard" ) var ( currentBranchToken = []byte("branchToken") testShardID = 42 ) func TestGetChildExecutionInfo(t *testing.T) { childInfo := &persistence.ChildExecutionInfo{ DomainID: constants.TestDomainID, } ctx := createShardCtx(t) m := loadMutableState(t, ctx, &persistence.WorkflowMutableState{ ExecutionInfo: standardExecutionInfo(), ChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{ 1: childInfo, }}) result, ok := m.GetChildExecutionInfo(1) assert.True(t, ok) assert.Equal(t, result, childInfo) } func TestGetChildExecutionInitiatedEvent(t *testing.T) { initiatedEventID := int64(1) sampleEvent := &types.HistoryEvent{ ID: 2, } cases := []struct { name string eventCacheAffordance func(e *events.MockCache) childInfo *persistence.ChildExecutionInfo expected *types.HistoryEvent expectedErr error }{ { name: "Missing Child Workflow Info", expectedErr: ErrMissingChildWorkflowInfo, }, { name: "Event stored on info", childInfo: &persistence.ChildExecutionInfo{ InitiatedEvent: sampleEvent, }, expected: sampleEvent, }, { name: "Get from EventCache", childInfo: &persistence.ChildExecutionInfo{ InitiatedEventBatchID: 10, InitiatedID: 11, }, eventCacheAffordance: func(e *events.MockCache) { e.EXPECT().GetEvent( gomock.Any(), // context testShardID, constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID, int64(10), // InitiatedEventBatchID int64(11), // InitiatedID currentBranchToken, ).Return(sampleEvent, nil) }, expected: sampleEvent, }, { name: "Error from EventCache", childInfo: &persistence.ChildExecutionInfo{ InitiatedEventBatchID: 10, InitiatedID: 11, }, eventCacheAffordance: func(e *events.MockCache) { e.EXPECT().GetEvent( gomock.Any(), // context testShardID, constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID, int64(10), // InitiatedEventBatchID int64(11), // InitiatedID currentBranchToken, ).Return(nil, &types.AccessDeniedError{Message: "Oh no!"}) }, expectedErr: ErrMissingChildWorkflowInitiatedEvent, }, } for _, testCase := range cases { t.Run(testCase.name, func(t *testing.T) { ctx := createShardCtx(t) if testCase.eventCacheAffordance != nil { testCase.eventCacheAffordance(ctx.MockEventsCache) } childExecutionInfo := map[int64]*persistence.ChildExecutionInfo{} if testCase.childInfo != nil { childExecutionInfo[initiatedEventID] = testCase.childInfo } m := loadMutableState(t, ctx, &persistence.WorkflowMutableState{ ExecutionInfo: standardExecutionInfo(), ChildExecutionInfos: childExecutionInfo, }) result, err := m.GetChildExecutionInitiatedEvent(context.Background(), initiatedEventID) assert.Equal(t, testCase.expected, result) assert.Equal(t, testCase.expectedErr, err) }) } } func TestGetPendingChildExecutionInfos(t *testing.T) { childExecutionInfos := map[int64]*persistence.ChildExecutionInfo{ 1: { DomainID: constants.TestDomainID, }, } ctx := createShardCtx(t) m := loadMutableState(t, ctx, &persistence.WorkflowMutableState{ ExecutionInfo: standardExecutionInfo(), ChildExecutionInfos: childExecutionInfos, }) assert.Equal(t, childExecutionInfos, m.GetPendingChildExecutionInfos()) } func TestDeletePendingChildExecution(t *testing.T) { cases := []struct { name string childInfo map[int64]*persistence.ChildExecutionInfo toDelete int64 }{ { name: "DeletePresent", childInfo: map[int64]*persistence.ChildExecutionInfo{ 1: { DomainID: constants.TestDomainID, }, }, toDelete: 1, }, { name: "DeleteNotFound", childInfo: map[int64]*persistence.ChildExecutionInfo{ 1: { DomainID: constants.TestDomainID, }, }, toDelete: 2, }, } for _, testCase := range cases { t.Run(testCase.name, func(t *testing.T) { ctx := createShardCtx(t) m := loadMutableState(t, ctx, &persistence.WorkflowMutableState{ ExecutionInfo: standardExecutionInfo(), ChildExecutionInfos: testCase.childInfo, }) err := m.DeletePendingChildExecution(testCase.toDelete) // Even the failure case doesn't return an error assert.NoError(t, err) _, hasChild := m.GetPendingChildExecutionInfos()[testCase.toDelete] _, deletedChild := m.deleteChildExecutionInfos[testCase.toDelete] assert.Equal(t, false, hasChild) assert.Equal(t, true, deletedChild) }) } } func TestAddChildEvents(t *testing.T) { parentWfID := "parent" parentRunID := "1d00698f-08e1-4d36-a3e2-3bf109f5d2d6" decisionID := int64(0) requestID := "request" decisionAttributes := &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, WorkflowType: &types.WorkflowType{Name: "type"}, TaskList: &types.TaskList{}, Input: []byte("input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(2), ParentClosePolicy: types.ParentClosePolicyAbandon.Ptr(), Control: []byte("control"), WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), RetryPolicy: nil, CronSchedule: "", Header: &types.Header{Fields: map[string][]byte{"headerKey": []byte("headerValue")}}, Memo: &types.Memo{Fields: map[string][]byte{"key": []byte("value")}}, SearchAttributes: &types.SearchAttributes{IndexedFields: map[string][]byte{"indexed": []byte("field")}}, } initiatedAttributes := &types.StartChildWorkflowExecutionInitiatedEventAttributes{ Domain: decisionAttributes.Domain, WorkflowID: decisionAttributes.WorkflowID, WorkflowType: decisionAttributes.WorkflowType, TaskList: decisionAttributes.TaskList, Input: decisionAttributes.Input, ExecutionStartToCloseTimeoutSeconds: decisionAttributes.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: decisionAttributes.TaskStartToCloseTimeoutSeconds, ParentClosePolicy: decisionAttributes.ParentClosePolicy, Control: decisionAttributes.Control, DecisionTaskCompletedEventID: decisionID, WorkflowIDReusePolicy: decisionAttributes.WorkflowIDReusePolicy, RetryPolicy: decisionAttributes.RetryPolicy, CronSchedule: decisionAttributes.CronSchedule, Header: decisionAttributes.Header, Memo: decisionAttributes.Memo, SearchAttributes: decisionAttributes.SearchAttributes, } childInitiatedID := int64(1) childStartedID := int64(3) nextEventID := int64(2) currentTimestamp := int64(1_000_001) childExecution := &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } childType := &types.WorkflowType{Name: "type"} cases := []struct { name string childInfo map[int64]*persistence.ChildExecutionInfo wfState int mockAllowances func(ctx *shard.TestContext) eventFn func(builder *mutableStateBuilder) (*types.HistoryEvent, error) assertFn func(t *testing.T, builder *mutableStateBuilder) expectedEvent *types.HistoryEvent expectedErr error }{ { name: "StartChildWorkflowExecutionInitiated", mockAllowances: func(ctx *shard.TestContext) { ctx.MockEventsCache.EXPECT().PutEvent(constants.TestDomainID, parentWfID, parentRunID, nextEventID, gomock.Any()) }, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { event, _, e := builder.AddStartChildWorkflowExecutionInitiatedEvent(decisionID, requestID, decisionAttributes) return event, e }, expectedEvent: &types.HistoryEvent{ ID: nextEventID, Timestamp: ¤tTimestamp, EventType: types.EventTypeStartChildWorkflowExecutionInitiated.Ptr(), Version: commonconstants.EmptyVersion, TaskID: commonconstants.EmptyEventTaskID, StartChildWorkflowExecutionInitiatedEventAttributes: initiatedAttributes, }, }, { name: "StartChildWorkflowExecutionInitiated: not mutable", wfState: persistence.WorkflowStateCompleted, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { event, _, e := builder.AddStartChildWorkflowExecutionInitiatedEvent(decisionID, requestID, decisionAttributes) return event, e }, expectedErr: ErrWorkflowFinished, }, { name: "ChildWorkflowExecutionStarted", childInfo: map[int64]*persistence.ChildExecutionInfo{ childInitiatedID: { StartedID: commonconstants.EmptyEventID, }, }, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionStartedEvent(constants.TestDomainID, &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, &types.WorkflowType{Name: "type"}, childInitiatedID, nil) }, expectedEvent: &types.HistoryEvent{ ID: commonconstants.BufferedEventID, Timestamp: ¤tTimestamp, EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), Version: commonconstants.EmptyVersion, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ Domain: constants.TestDomainID, InitiatedEventID: childInitiatedID, WorkflowExecution: childExecution, WorkflowType: childType, Header: nil, }, }, }, { name: "ChildWorkflowExecutionStarted: not mutable", wfState: persistence.WorkflowStateCompleted, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionStartedEvent(constants.TestDomainID, &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, &types.WorkflowType{Name: "type"}, childInitiatedID, nil) }, expectedErr: ErrWorkflowFinished, }, { name: "ChildWorkflowExecutionStarted: missing child info", eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionStartedEvent(constants.TestDomainID, childExecution, childType, childInitiatedID, nil) }, expectedErr: &types.InternalServiceError{Message: tag.WorkflowActionChildWorkflowStarted.Field().String + " operation failed"}, }, { name: "StartChildWorkflowExecutionFailed", childInfo: map[int64]*persistence.ChildExecutionInfo{ childInitiatedID: { StartedID: commonconstants.EmptyEventID, }, }, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddStartChildWorkflowExecutionFailedEvent(childInitiatedID, types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning, initiatedAttributes) }, expectedEvent: &types.HistoryEvent{ ID: commonconstants.BufferedEventID, Timestamp: ¤tTimestamp, EventType: types.EventTypeStartChildWorkflowExecutionFailed.Ptr(), Version: commonconstants.EmptyVersion, TaskID: commonconstants.EmptyEventTaskID, StartChildWorkflowExecutionFailedEventAttributes: &types.StartChildWorkflowExecutionFailedEventAttributes{ Domain: constants.TestDomainName, WorkflowID: constants.TestWorkflowID, WorkflowType: childType, Cause: types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning.Ptr(), Control: []byte("control"), InitiatedEventID: childInitiatedID, DecisionTaskCompletedEventID: decisionID, }, }, }, { name: "StartChildWorkflowExecutionFailed: not mutable", wfState: persistence.WorkflowStateCompleted, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddStartChildWorkflowExecutionFailedEvent(childInitiatedID, types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning, initiatedAttributes) }, expectedErr: ErrWorkflowFinished, }, { name: "StartChildWorkflowExecutionFailed: missing child info", eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddStartChildWorkflowExecutionFailedEvent(childInitiatedID, types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning, initiatedAttributes) }, expectedErr: &types.InternalServiceError{Message: tag.WorkflowActionChildWorkflowInitiationFailed.Field().String + " operation failed"}, }, { name: "ChildWorkflowExecutionCompleted", childInfo: map[int64]*persistence.ChildExecutionInfo{ childInitiatedID: { InitiatedID: childInitiatedID, StartedID: childStartedID, DomainID: constants.TestDomainID, WorkflowTypeName: childType.Name, }, }, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionCompletedEvent(childInitiatedID, childExecution, &types.WorkflowExecutionCompletedEventAttributes{ Result: []byte("result"), DecisionTaskCompletedEventID: 10, // Unused }) }, expectedEvent: &types.HistoryEvent{ ID: commonconstants.BufferedEventID, Timestamp: ¤tTimestamp, EventType: types.EventTypeChildWorkflowExecutionCompleted.Ptr(), Version: commonconstants.EmptyVersion, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionCompletedEventAttributes: &types.ChildWorkflowExecutionCompletedEventAttributes{ Result: []byte("result"), Domain: constants.TestDomainName, WorkflowExecution: childExecution, WorkflowType: childType, InitiatedEventID: childInitiatedID, StartedEventID: 3, }, }, }, { name: "ChildWorkflowExecutionCompleted: not mutable", wfState: persistence.WorkflowStateCompleted, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionCompletedEvent(childInitiatedID, childExecution, &types.WorkflowExecutionCompletedEventAttributes{ Result: []byte("result"), DecisionTaskCompletedEventID: 10, // Unused }) }, expectedErr: ErrWorkflowFinished, }, { name: "ChildWorkflowExecutionCompleted: missing child info", childInfo: map[int64]*persistence.ChildExecutionInfo{}, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionCompletedEvent(childInitiatedID, childExecution, &types.WorkflowExecutionCompletedEventAttributes{ Result: []byte("result"), DecisionTaskCompletedEventID: 10, // Unused }) }, expectedErr: &types.InternalServiceError{Message: tag.WorkflowActionChildWorkflowCompleted.Field().String + " operation failed"}, }, { name: "ChildWorkflowExecutionFailed", childInfo: map[int64]*persistence.ChildExecutionInfo{ childInitiatedID: { InitiatedID: childInitiatedID, StartedID: childStartedID, DomainID: constants.TestDomainID, WorkflowTypeName: childType.Name, }, }, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionFailedEvent(childInitiatedID, childExecution, &types.WorkflowExecutionFailedEventAttributes{ Reason: common.StringPtr("failed"), DecisionTaskCompletedEventID: 10, // Unused Details: []byte("details"), }) }, expectedEvent: &types.HistoryEvent{ ID: commonconstants.BufferedEventID, Timestamp: ¤tTimestamp, EventType: types.EventTypeChildWorkflowExecutionFailed.Ptr(), Version: commonconstants.EmptyVersion, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionFailedEventAttributes: &types.ChildWorkflowExecutionFailedEventAttributes{ Reason: common.StringPtr("failed"), Details: []byte("details"), Domain: constants.TestDomainName, WorkflowExecution: childExecution, WorkflowType: childType, InitiatedEventID: childInitiatedID, StartedEventID: childStartedID, }, }, }, { name: "ChildWorkflowExecutionFailed: not mutable", wfState: persistence.WorkflowStateCompleted, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionFailedEvent(childInitiatedID, childExecution, &types.WorkflowExecutionFailedEventAttributes{ Reason: common.StringPtr("failed"), DecisionTaskCompletedEventID: 10, // Unused Details: []byte("details"), }) }, expectedErr: ErrWorkflowFinished, }, { name: "ChildWorkflowExecutionFailed: missing child info", eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionFailedEvent(childInitiatedID, childExecution, &types.WorkflowExecutionFailedEventAttributes{ Reason: common.StringPtr("failed"), DecisionTaskCompletedEventID: 10, // Unused Details: []byte("details"), }) }, expectedErr: &types.InternalServiceError{Message: tag.WorkflowActionChildWorkflowFailed.Field().String + " operation failed"}, }, { name: "ChildWorkflowExecutionCanceled", childInfo: map[int64]*persistence.ChildExecutionInfo{ childInitiatedID: { InitiatedID: childInitiatedID, StartedID: childStartedID, DomainID: constants.TestDomainID, WorkflowTypeName: childType.Name, }, }, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionCanceledEvent(childInitiatedID, childExecution, &types.WorkflowExecutionCanceledEventAttributes{ Details: []byte("details"), DecisionTaskCompletedEventID: 10, // Unused }) }, expectedEvent: &types.HistoryEvent{ ID: commonconstants.BufferedEventID, Timestamp: ¤tTimestamp, EventType: types.EventTypeChildWorkflowExecutionCanceled.Ptr(), Version: commonconstants.EmptyVersion, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionCanceledEventAttributes: &types.ChildWorkflowExecutionCanceledEventAttributes{ Details: []byte("details"), Domain: constants.TestDomainName, WorkflowExecution: childExecution, WorkflowType: childType, InitiatedEventID: childInitiatedID, StartedEventID: childStartedID, }, }, }, { name: "ChildWorkflowExecutionCanceled: not mutable", wfState: persistence.WorkflowStateCompleted, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionCanceledEvent(childInitiatedID, childExecution, &types.WorkflowExecutionCanceledEventAttributes{ Details: []byte("details"), DecisionTaskCompletedEventID: 10, // Unused }) }, expectedErr: ErrWorkflowFinished, }, { name: "ChildWorkflowExecutionCanceled: missing child info", eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionCanceledEvent(childInitiatedID, childExecution, &types.WorkflowExecutionCanceledEventAttributes{ Details: []byte("details"), DecisionTaskCompletedEventID: 10, // Unused }) }, expectedErr: &types.InternalServiceError{Message: tag.WorkflowActionChildWorkflowCanceled.Field().String + " operation failed"}, }, { name: "ChildWorkflowExecutionTerminated", childInfo: map[int64]*persistence.ChildExecutionInfo{ childInitiatedID: { InitiatedID: childInitiatedID, StartedID: childStartedID, DomainID: constants.TestDomainID, WorkflowTypeName: childType.Name, }, }, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { // Attributes are entirely unused return builder.AddChildWorkflowExecutionTerminatedEvent(childInitiatedID, childExecution, &types.WorkflowExecutionTerminatedEventAttributes{ Reason: "reason", Details: []byte("details"), Identity: "identity", }) }, expectedEvent: &types.HistoryEvent{ ID: commonconstants.BufferedEventID, Timestamp: ¤tTimestamp, EventType: types.EventTypeChildWorkflowExecutionTerminated.Ptr(), Version: commonconstants.EmptyVersion, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionTerminatedEventAttributes: &types.ChildWorkflowExecutionTerminatedEventAttributes{ Domain: constants.TestDomainName, WorkflowExecution: childExecution, WorkflowType: childType, InitiatedEventID: childInitiatedID, StartedEventID: childStartedID, }, }, }, { name: "ChildWorkflowExecutionTerminated: not mutable", wfState: persistence.WorkflowStateCompleted, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { // Attributes are entirely unused return builder.AddChildWorkflowExecutionTerminatedEvent(childInitiatedID, childExecution, &types.WorkflowExecutionTerminatedEventAttributes{ Reason: "reason", Details: []byte("details"), Identity: "identity", }) }, expectedErr: ErrWorkflowFinished, }, { name: "ChildWorkflowExecutionTerminated: missing child info", eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { // Attributes are entirely unused return builder.AddChildWorkflowExecutionTerminatedEvent(childInitiatedID, childExecution, &types.WorkflowExecutionTerminatedEventAttributes{ Reason: "reason", Details: []byte("details"), Identity: "identity", }) }, expectedErr: &types.InternalServiceError{Message: tag.WorkflowActionChildWorkflowTerminated.Field().String + " operation failed"}, }, { name: "ChildWorkflowExecutionTimedOut", childInfo: map[int64]*persistence.ChildExecutionInfo{ childInitiatedID: { InitiatedID: childInitiatedID, StartedID: childStartedID, DomainID: constants.TestDomainID, WorkflowTypeName: childType.Name, }, }, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionTimedOutEvent(childInitiatedID, childExecution, &types.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }) }, expectedEvent: &types.HistoryEvent{ ID: commonconstants.BufferedEventID, Timestamp: ¤tTimestamp, EventType: types.EventTypeChildWorkflowExecutionTimedOut.Ptr(), Version: commonconstants.EmptyVersion, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionTimedOutEventAttributes: &types.ChildWorkflowExecutionTimedOutEventAttributes{ Domain: constants.TestDomainName, WorkflowExecution: childExecution, WorkflowType: childType, InitiatedEventID: childInitiatedID, StartedEventID: childStartedID, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, }, { name: "ChildWorkflowExecutionTimedOut: not mutable", wfState: persistence.WorkflowStateCompleted, eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionTimedOutEvent(childInitiatedID, childExecution, &types.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }) }, expectedErr: ErrWorkflowFinished, }, { name: "ChildWorkflowExecutionTimedOut: missing child info", eventFn: func(builder *mutableStateBuilder) (*types.HistoryEvent, error) { return builder.AddChildWorkflowExecutionTimedOutEvent(childInitiatedID, childExecution, &types.WorkflowExecutionTimedOutEventAttributes{ TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }) }, expectedErr: &types.InternalServiceError{Message: tag.WorkflowActionChildWorkflowTimedOut.Field().String + " operation failed"}, }, } for _, testCase := range cases { t.Run(testCase.name, func(t *testing.T) { ctx := createShardCtx(t) ctx.Resource.TimeSource = clock.NewMockedTimeSourceAt(time.Unix(0, currentTimestamp)) if testCase.mockAllowances != nil { testCase.mockAllowances(ctx) } childExecutionInfo := testCase.childInfo if childExecutionInfo == nil { childExecutionInfo = map[int64]*persistence.ChildExecutionInfo{} } m := loadMutableState(t, ctx, &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: parentWfID, RunID: parentRunID, WorkflowTypeName: "type", BranchToken: currentBranchToken, NextEventID: nextEventID, State: testCase.wfState, }, ChildExecutionInfos: childExecutionInfo, }) event, err := testCase.eventFn(m) assert.Equal(t, testCase.expectedEvent, event) assert.Equal(t, testCase.expectedErr, err) }) } } // MutableState is all about mutating, avoid reusing objects func standardExecutionInfo() *persistence.WorkflowExecutionInfo { return &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, WorkflowTypeName: "type", BranchToken: currentBranchToken, } } func createShardCtx(t *testing.T) *shard.TestContext { return shard.NewTestContext( t, gomock.NewController(t), &persistence.ShardInfo{ ShardID: testShardID, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) } func loadMutableState(t *testing.T, ctx *shard.TestContext, state *persistence.WorkflowMutableState) *mutableStateBuilder { domain := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName}, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{}, 1, nil, 0, 0, 0, ) ctx.Resource.DomainCache.EXPECT().GetDomainID(constants.TestDomainName).AnyTimes() ctx.Resource.DomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(domain, nil).AnyTimes() ctx.Resource.DomainCache.EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).AnyTimes() m := newMutableStateBuilder(ctx, log.NewNoop(), domain, domain.GetFailoverVersion(), ) err := m.Load(context.Background(), state) assert.NoError(t, err) return m } ================================================ FILE: service/history/execution/mutable_state_builder_methods_completed.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "context" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func (e *mutableStateBuilder) IsWorkflowCompleted() bool { return e.executionInfo.State == persistence.WorkflowStateCompleted } func (e *mutableStateBuilder) AddCompletedWorkflowEvent( decisionCompletedEventID int64, attributes *types.CompleteWorkflowExecutionDecisionAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowCompleted if err := e.checkMutability(opTag); err != nil { return nil, err } event := e.hBuilder.AddCompletedWorkflowEvent(decisionCompletedEventID, attributes) if err := e.ReplicateWorkflowExecutionCompletedEvent(decisionCompletedEventID, event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateWorkflowExecutionCompletedEvent( firstEventID int64, event *types.HistoryEvent, ) error { if err := e.UpdateWorkflowStateCloseStatus( persistence.WorkflowStateCompleted, persistence.WorkflowCloseStatusCompleted, ); err != nil { return err } e.executionInfo.CompletionEventBatchID = firstEventID // Used when completion event needs to be loaded from database e.ClearStickyness() e.writeEventToCache(event) return e.taskGenerator.GenerateWorkflowCloseTasks(event, e.config.WorkflowDeletionJitterRange(e.domainEntry.GetInfo().Name)) } func (e *mutableStateBuilder) AddFailWorkflowEvent( decisionCompletedEventID int64, attributes *types.FailWorkflowExecutionDecisionAttributes, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowFailed if err := e.checkMutability(opTag); err != nil { return nil, err } event := e.hBuilder.AddFailWorkflowEvent(decisionCompletedEventID, attributes) if err := e.ReplicateWorkflowExecutionFailedEvent(decisionCompletedEventID, event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateWorkflowExecutionFailedEvent( firstEventID int64, event *types.HistoryEvent, ) error { if err := e.UpdateWorkflowStateCloseStatus( persistence.WorkflowStateCompleted, persistence.WorkflowCloseStatusFailed, ); err != nil { return err } e.executionInfo.CompletionEventBatchID = firstEventID // Used when completion event needs to be loaded from database e.ClearStickyness() e.writeEventToCache(event) return e.taskGenerator.GenerateWorkflowCloseTasks(event, e.config.WorkflowDeletionJitterRange(e.domainEntry.GetInfo().Name)) } // GetCompletionEvent retrieves the workflow completion event from mutable state func (e *mutableStateBuilder) GetCompletionEvent( ctx context.Context, ) (*types.HistoryEvent, error) { if e.executionInfo.State != persistence.WorkflowStateCompleted { return nil, ErrMissingWorkflowCompletionEvent } // Needed for backward compatibility reason if e.executionInfo.CompletionEvent != nil { return e.executionInfo.CompletionEvent, nil } // Needed for backward compatibility reason if e.executionInfo.CompletionEventBatchID == constants.EmptyEventID { return nil, ErrMissingWorkflowCompletionEvent } currentBranchToken, err := e.GetCurrentBranchToken() if err != nil { return nil, err } // Completion EventID is always one less than NextEventID after workflow is completed completionEventID := e.executionInfo.NextEventID - 1 firstEventID := e.executionInfo.CompletionEventBatchID completionEvent, err := e.eventsCache.GetEvent( ctx, e.shard.GetShardID(), e.executionInfo.DomainID, e.executionInfo.WorkflowID, e.executionInfo.RunID, firstEventID, completionEventID, currentBranchToken, ) if err != nil { // do not return the original error // since original error can be of type entity not exists // which can cause task processing side to fail silently // However, if the error is a persistence transient error, // we return the original error, because we fail to get // the event because of failure from database if persistence.IsTransientError(err) { return nil, err } return nil, ErrMissingWorkflowCompletionEvent } return completionEvent, nil } ================================================ FILE: service/history/execution/mutable_state_builder_methods_decision.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "encoding/json" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) // GetDecisionInfo returns details about the in-progress decision task func (e *mutableStateBuilder) GetDecisionInfo( scheduleEventID int64, ) (*DecisionInfo, bool) { return e.decisionTaskManager.GetDecisionInfo(scheduleEventID) } // UpdateDecision updates a decision task. func (e *mutableStateBuilder) UpdateDecision( decision *DecisionInfo, ) { e.decisionTaskManager.UpdateDecision(decision) } // DeleteDecision deletes a decision task. func (e *mutableStateBuilder) DeleteDecision() { e.decisionTaskManager.DeleteDecision() } func (e *mutableStateBuilder) FailDecision( incrementAttempt bool, ) { e.decisionTaskManager.FailDecision(incrementAttempt) } func (e *mutableStateBuilder) HasProcessedOrPendingDecision() bool { return e.decisionTaskManager.HasProcessedOrPendingDecision() } func (e *mutableStateBuilder) HasPendingDecision() bool { return e.decisionTaskManager.HasPendingDecision() } func (e *mutableStateBuilder) GetPendingDecision() (*DecisionInfo, bool) { return e.decisionTaskManager.GetPendingDecision() } func (e *mutableStateBuilder) HasInFlightDecision() bool { return e.decisionTaskManager.HasInFlightDecision() } func (e *mutableStateBuilder) GetInFlightDecision() (*DecisionInfo, bool) { return e.decisionTaskManager.GetInFlightDecision() } func (e *mutableStateBuilder) GetDecisionScheduleToStartTimeout() time.Duration { return e.decisionTaskManager.GetDecisionScheduleToStartTimeout() } func (e *mutableStateBuilder) AddFirstDecisionTaskScheduled( startEvent *types.HistoryEvent, ) error { opTag := tag.WorkflowActionDecisionTaskScheduled if err := e.checkMutability(opTag); err != nil { return err } return e.decisionTaskManager.AddFirstDecisionTaskScheduled(startEvent) } func (e *mutableStateBuilder) AddDecisionTaskScheduledEvent( bypassTaskGeneration bool, ) (*DecisionInfo, error) { opTag := tag.WorkflowActionDecisionTaskScheduled if err := e.checkMutability(opTag); err != nil { return nil, err } return e.decisionTaskManager.AddDecisionTaskScheduledEvent(bypassTaskGeneration) } // originalScheduledTimestamp is to record the first scheduled decision during decision heartbeat. func (e *mutableStateBuilder) AddDecisionTaskScheduledEventAsHeartbeat( bypassTaskGeneration bool, originalScheduledTimestamp int64, ) (*DecisionInfo, error) { opTag := tag.WorkflowActionDecisionTaskScheduled if err := e.checkMutability(opTag); err != nil { return nil, err } return e.decisionTaskManager.AddDecisionTaskScheduledEventAsHeartbeat(bypassTaskGeneration, originalScheduledTimestamp) } func (e *mutableStateBuilder) ReplicateTransientDecisionTaskScheduled() error { return e.decisionTaskManager.ReplicateTransientDecisionTaskScheduled() } func (e *mutableStateBuilder) ReplicateDecisionTaskScheduledEvent( version int64, scheduleID int64, taskList string, startToCloseTimeoutSeconds int32, attempt int64, scheduleTimestamp int64, originalScheduledTimestamp int64, bypassTaskGeneration bool, ) (*DecisionInfo, error) { return e.decisionTaskManager.ReplicateDecisionTaskScheduledEvent(version, scheduleID, taskList, startToCloseTimeoutSeconds, attempt, scheduleTimestamp, originalScheduledTimestamp, bypassTaskGeneration) } func (e *mutableStateBuilder) AddDecisionTaskStartedEvent( scheduleEventID int64, requestID string, request *types.PollForDecisionTaskRequest, ) (*types.HistoryEvent, *DecisionInfo, error) { opTag := tag.WorkflowActionDecisionTaskStarted if err := e.checkMutability(opTag); err != nil { return nil, nil, err } return e.decisionTaskManager.AddDecisionTaskStartedEvent(scheduleEventID, requestID, request) } func (e *mutableStateBuilder) ReplicateDecisionTaskStartedEvent( decision *DecisionInfo, version int64, scheduleID int64, startedID int64, requestID string, timestamp int64, ) (*DecisionInfo, error) { return e.decisionTaskManager.ReplicateDecisionTaskStartedEvent(decision, version, scheduleID, startedID, requestID, timestamp) } func (e *mutableStateBuilder) CreateTransientDecisionEvents( decision *DecisionInfo, identity string, ) (*types.HistoryEvent, *types.HistoryEvent) { return e.decisionTaskManager.CreateTransientDecisionEvents(decision, identity) } // TODO: we will release the restriction when reset API allow those pending func (e *mutableStateBuilder) CheckResettable() error { if len(e.GetPendingChildExecutionInfos()) > 0 { return &types.BadRequestError{ Message: "it is not allowed resetting to a point that workflow has pending child types.", } } if len(e.GetPendingRequestCancelExternalInfos()) > 0 { return &types.BadRequestError{ Message: "it is not allowed resetting to a point that workflow has pending request cancel.", } } if len(e.GetPendingSignalExternalInfos()) > 0 { return &types.BadRequestError{ Message: "it is not allowed resetting to a point that workflow has pending signals to send.", } } return nil } func (e *mutableStateBuilder) AddDecisionTaskCompletedEvent( scheduleEventID int64, startedEventID int64, request *types.RespondDecisionTaskCompletedRequest, maxResetPoints int, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionDecisionTaskCompleted if err := e.checkMutability(opTag); err != nil { return nil, err } return e.decisionTaskManager.AddDecisionTaskCompletedEvent(scheduleEventID, startedEventID, request, maxResetPoints) } func (e *mutableStateBuilder) ReplicateDecisionTaskCompletedEvent( event *types.HistoryEvent, ) error { return e.decisionTaskManager.ReplicateDecisionTaskCompletedEvent(event) } func (e *mutableStateBuilder) AddDecisionTaskTimedOutEvent( scheduleEventID int64, startedEventID int64, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionDecisionTaskTimedOut if err := e.checkMutability(opTag); err != nil { return nil, err } return e.decisionTaskManager.AddDecisionTaskTimedOutEvent(scheduleEventID, startedEventID) } func (e *mutableStateBuilder) ReplicateDecisionTaskTimedOutEvent( event *types.HistoryEvent, ) error { return e.decisionTaskManager.ReplicateDecisionTaskTimedOutEvent(event) } func (e *mutableStateBuilder) AddDecisionTaskScheduleToStartTimeoutEvent( scheduleEventID int64, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionDecisionTaskTimedOut if err := e.checkMutability(opTag); err != nil { return nil, err } return e.decisionTaskManager.AddDecisionTaskScheduleToStartTimeoutEvent(scheduleEventID) } func (e *mutableStateBuilder) AddDecisionTaskResetTimeoutEvent( scheduleEventID int64, baseRunID string, newRunID string, forkEventVersion int64, reason string, resetRequestID string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionDecisionTaskTimedOut if err := e.checkMutability(opTag); err != nil { return nil, err } return e.decisionTaskManager.AddDecisionTaskResetTimeoutEvent( scheduleEventID, baseRunID, newRunID, forkEventVersion, reason, resetRequestID, ) } func (e *mutableStateBuilder) AddDecisionTaskFailedEvent( scheduleEventID int64, startedEventID int64, cause types.DecisionTaskFailedCause, details []byte, identity string, reason string, binChecksum string, baseRunID string, newRunID string, forkEventVersion int64, resetRequestID string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionDecisionTaskFailed if err := e.checkMutability(opTag); err != nil { return nil, err } return e.decisionTaskManager.AddDecisionTaskFailedEvent( scheduleEventID, startedEventID, cause, details, identity, reason, binChecksum, baseRunID, newRunID, forkEventVersion, resetRequestID, ) } func (e *mutableStateBuilder) ReplicateDecisionTaskFailedEvent(event *types.HistoryEvent) error { return e.decisionTaskManager.ReplicateDecisionTaskFailedEvent(event) } // add BinaryCheckSum for the first decisionTaskCompletedID for auto-reset func (e *mutableStateBuilder) addBinaryCheckSumIfNotExists( event *types.HistoryEvent, maxResetPoints int, ) error { binChecksum := event.GetDecisionTaskCompletedEventAttributes().GetBinaryChecksum() if len(binChecksum) == 0 { return nil } exeInfo := e.executionInfo var currResetPoints []*types.ResetPointInfo if exeInfo.AutoResetPoints != nil && exeInfo.AutoResetPoints.Points != nil { currResetPoints = e.executionInfo.AutoResetPoints.Points } else { currResetPoints = make([]*types.ResetPointInfo, 0, 1) } // List of all recent binary checksums associated with the types. var recentBinaryChecksums []string for _, rp := range currResetPoints { recentBinaryChecksums = append(recentBinaryChecksums, rp.GetBinaryChecksum()) if rp.GetBinaryChecksum() == binChecksum { // this checksum already exists return nil } } recentBinaryChecksums, currResetPoints = trimBinaryChecksums(recentBinaryChecksums, currResetPoints, maxResetPoints) // Adding current version of the binary checksum. recentBinaryChecksums = append(recentBinaryChecksums, binChecksum) resettable := true err := e.CheckResettable() if err != nil { resettable = false } info := &types.ResetPointInfo{ BinaryChecksum: binChecksum, RunID: exeInfo.RunID, FirstDecisionCompletedID: event.ID, CreatedTimeNano: common.Int64Ptr(e.timeSource.Now().UnixNano()), Resettable: resettable, } currResetPoints = append(currResetPoints, info) exeInfo.AutoResetPoints = &types.ResetPoints{ Points: currResetPoints, } bytes, err := json.Marshal(recentBinaryChecksums) if err != nil { return err } if exeInfo.SearchAttributes == nil { exeInfo.SearchAttributes = make(map[string][]byte) } exeInfo.SearchAttributes[definition.BinaryChecksums] = bytes if common.IsAdvancedVisibilityWritingEnabled(e.shard.GetConfig().WriteVisibilityStoreName(), e.shard.GetConfig().IsAdvancedVisConfigExist) { return e.taskGenerator.GenerateWorkflowSearchAttrTasks() } return nil } ================================================ FILE: service/history/execution/mutable_state_builder_methods_decision_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "encoding/json" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" ) func CreateDecisionInfo() *DecisionInfo { return &DecisionInfo{ Version: 123, ScheduleID: 123, StartedID: 123, RequestID: "123", DecisionTimeout: 123, StartedTimestamp: 123, ScheduledTimestamp: 123, OriginalScheduledTimestamp: 123, } } func TestGetDecisionInfoMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } scheduleEventID := int64(123) rets := CreateDecisionInfo() decisionTaskManager.EXPECT().GetDecisionInfo(scheduleEventID).Return(rets, true).Times(1) decisionInfo, ok := builder.GetDecisionInfo(scheduleEventID) assert.Equal(t, rets, decisionInfo) assert.True(t, ok) } func TestUpdateDecisionMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } decision := CreateDecisionInfo() decisionTaskManager.EXPECT().UpdateDecision(decision).Times(1) builder.UpdateDecision(decision) } func TestDeleteDecisionMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } decisionTaskManager.EXPECT().DeleteDecision().Times(1) builder.DeleteDecision() } func TestFailDecisionMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } decisionTaskManager.EXPECT().FailDecision(true).Times(1) builder.FailDecision(true) } func TestHasProcessedOrPendingDecisionMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } decisionTaskManager.EXPECT().HasProcessedOrPendingDecision().Return(true).Times(1) hasProcessedOrPendingDecision := builder.HasProcessedOrPendingDecision() assert.True(t, hasProcessedOrPendingDecision) } func TestHasPendingDecisionMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } decisionTaskManager.EXPECT().HasPendingDecision().Return(true).Times(1) hasPendingDecision := builder.HasPendingDecision() assert.True(t, hasPendingDecision) } func TestGetPendingDecisionMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } rets := CreateDecisionInfo() decisionTaskManager.EXPECT().GetPendingDecision().Return(rets, true).Times(1) pendingDecision, ok := builder.GetPendingDecision() assert.Equal(t, rets, pendingDecision) assert.True(t, ok) } func TestHasInFlightDecisionMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } decisionTaskManager.EXPECT().HasInFlightDecision().Return(true).Times(1) hasInFlightDecision := builder.HasInFlightDecision() assert.True(t, hasInFlightDecision) } func TestGetInFlightDecisionMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } rets := CreateDecisionInfo() decisionTaskManager.EXPECT().GetInFlightDecision().Return(rets, true).Times(1) inFlightDecision, ok := builder.GetInFlightDecision() assert.Equal(t, rets, inFlightDecision) assert.True(t, ok) } func TestGetDecisionScheduleToStartTimeoutMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } rets := time.Duration(123) decisionTaskManager.EXPECT().GetDecisionScheduleToStartTimeout().Return(rets).Times(1) timeout := builder.GetDecisionScheduleToStartTimeout() assert.Equal(t, rets, timeout) } func TestAddFirstDecisionTaskScheduledMutableStateBuilder(t *testing.T) { tests := []struct { name string executionInfo *persistence.WorkflowExecutionInfo wantErr bool }{ { name: "success", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, { name: "error due to state", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, executionInfo: tc.executionInfo, logger: log.NewNoop(), } startEvent := &types.HistoryEvent{} if !tc.wantErr { decisionTaskManager.EXPECT().AddFirstDecisionTaskScheduled(startEvent).Return(nil).Times(1) } err := builder.AddFirstDecisionTaskScheduled(startEvent) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestAddDecisionTaskScheduledEventMutableStateBuilder(t *testing.T) { tests := []struct { name string executionInfo *persistence.WorkflowExecutionInfo wantErr bool }{ { name: "success", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, { name: "error due to state", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, executionInfo: tc.executionInfo, logger: log.NewNoop(), } rets := CreateDecisionInfo() if !tc.wantErr { decisionTaskManager.EXPECT().AddDecisionTaskScheduledEvent(true).Return(rets, nil).Times(1) } decisionInfo, err := builder.AddDecisionTaskScheduledEvent(true) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, rets, decisionInfo) } }) } } func TestAddDecisionTaskScheduledEventAsHeartbeatMutableStateBuilder(t *testing.T) { tests := []struct { name string executionInfo *persistence.WorkflowExecutionInfo wantErr bool }{ { name: "success", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, { name: "error due to state", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, executionInfo: tc.executionInfo, logger: log.NewNoop(), } rets := CreateDecisionInfo() originalScheduledTimestamp := int64(1) if !tc.wantErr { decisionTaskManager.EXPECT().AddDecisionTaskScheduledEventAsHeartbeat(true, originalScheduledTimestamp).Return(rets, nil).Times(1) } decisionInfo, err := builder.AddDecisionTaskScheduledEventAsHeartbeat(true, originalScheduledTimestamp) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, rets, decisionInfo) } }) } } func TestReplicateTransientDecisionTaskScheduledMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } decisionTaskManager.EXPECT().ReplicateTransientDecisionTaskScheduled().Return(nil).Times(1) err := builder.ReplicateTransientDecisionTaskScheduled() assert.NoError(t, err) } func TestReplicateDecisionTaskScheduledEventMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } var version, scheduleID, attempt, scheduleTimestamp, originalScheduledTimestamp int64 = 1, 2, 3, 4, 5 startToCloseTimeoutSeconds := int32(6) taskList := "taskList" rets := CreateDecisionInfo() decisionTaskManager.EXPECT().ReplicateDecisionTaskScheduledEvent(version, scheduleID, taskList, startToCloseTimeoutSeconds, attempt, scheduleTimestamp, originalScheduledTimestamp, true).Return(rets, nil).Times(1) decisionInfo, err := builder.ReplicateDecisionTaskScheduledEvent(version, scheduleID, taskList, startToCloseTimeoutSeconds, attempt, scheduleTimestamp, originalScheduledTimestamp, true) assert.NoError(t, err) assert.Equal(t, rets, decisionInfo) } func TestAddDecisionTaskStartedEventMutableStateBuilder(t *testing.T) { tests := []struct { name string request *types.PollForDecisionTaskRequest executionInfo *persistence.WorkflowExecutionInfo wantErr bool }{ { name: "success", request: &types.PollForDecisionTaskRequest{}, executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, { name: "error due to state", request: &types.PollForDecisionTaskRequest{}, executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, executionInfo: tc.executionInfo, logger: log.NewNoop(), } scheduleEventID := int64(123) requestID := "requestID" rets0 := &types.HistoryEvent{} rets1 := CreateDecisionInfo() if !tc.wantErr { decisionTaskManager.EXPECT().AddDecisionTaskStartedEvent(scheduleEventID, requestID, tc.request).Return(rets0, rets1, nil).Times(1) } historyEvent, decisionInfo, err := builder.AddDecisionTaskStartedEvent(scheduleEventID, requestID, tc.request) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, rets0, historyEvent) assert.Equal(t, rets1, decisionInfo) } }) } } func TestReplicateDecisionTaskStartedEventMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } var version, scheduleID, startedID, timestamp int64 = 1, 2, 3, 4 requestID := "requestID" rets := CreateDecisionInfo() decisionTaskManager.EXPECT().ReplicateDecisionTaskStartedEvent(rets, version, scheduleID, startedID, requestID, timestamp).Return(rets, nil).Times(1) decisionInfo, err := builder.ReplicateDecisionTaskStartedEvent(rets, version, scheduleID, startedID, requestID, timestamp) assert.NoError(t, err) assert.Equal(t, rets, decisionInfo) } func TestCreateTransientDecisionEventsMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } decision := CreateDecisionInfo() identity := "identity" rets := &types.HistoryEvent{} decisionTaskManager.EXPECT().CreateTransientDecisionEvents(decision, identity).Return(rets, rets).Times(1) historyEvent0, historyEvent1 := builder.CreateTransientDecisionEvents(decision, identity) assert.Equal(t, rets, historyEvent0) assert.Equal(t, rets, historyEvent1) } func TestCheckResettableMutableStateBuilder(t *testing.T) { tests := []struct { name string pendingChildExecutionInfoIDs map[int64]*persistence.ChildExecutionInfo pendingRequestCancelInfoIDs map[int64]*persistence.RequestCancelInfo pendingSignalInfoIDs map[int64]*persistence.SignalInfo wantErr bool errMessage string }{ { name: "success", }, { name: "error due to pending child execution", pendingChildExecutionInfoIDs: map[int64]*persistence.ChildExecutionInfo{ 1: {}, }, wantErr: true, errMessage: "it is not allowed resetting to a point that workflow has pending child types.", }, { name: "error due to pending request cancel external", pendingRequestCancelInfoIDs: map[int64]*persistence.RequestCancelInfo{ 1: {}, }, wantErr: true, errMessage: "it is not allowed resetting to a point that workflow has pending request cancel.", }, { name: "error due to pending signal external", pendingSignalInfoIDs: map[int64]*persistence.SignalInfo{ 1: {}, }, wantErr: true, errMessage: "it is not allowed resetting to a point that workflow has pending signals to send.", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { builder := &mutableStateBuilder{ pendingChildExecutionInfoIDs: tc.pendingChildExecutionInfoIDs, pendingRequestCancelInfoIDs: tc.pendingRequestCancelInfoIDs, pendingSignalInfoIDs: tc.pendingSignalInfoIDs, } err := builder.CheckResettable() if tc.wantErr { assert.Error(t, err) assert.Equal(t, tc.errMessage, err.Error()) } else { assert.NoError(t, err) } }) } } func TestAddDecisionTaskCompletedEventMutableStateBuilder(t *testing.T) { tests := []struct { name string executionInfo *persistence.WorkflowExecutionInfo wantErr bool }{ { name: "success", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, { name: "error due to state", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, executionInfo: tc.executionInfo, logger: log.NewNoop(), } var scheduleEventID, startedEventID int64 = 123, 234 request := &types.RespondDecisionTaskCompletedRequest{} maxResetPoints := 1 rets := &types.HistoryEvent{} if !tc.wantErr { decisionTaskManager.EXPECT().AddDecisionTaskCompletedEvent(scheduleEventID, startedEventID, request, maxResetPoints).Return(rets, nil).Times(1) } historyEvent, err := builder.AddDecisionTaskCompletedEvent(scheduleEventID, startedEventID, request, maxResetPoints) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, rets, historyEvent) } }) } } func TestReplicateDecisionTaskCompletedEventMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } event := &types.HistoryEvent{} decisionTaskManager.EXPECT().ReplicateDecisionTaskCompletedEvent(event).Return(nil).Times(1) err := builder.ReplicateDecisionTaskCompletedEvent(event) assert.NoError(t, err) } func TestAddDecisionTaskTimedOutEventMutableStateBuilder(t *testing.T) { tests := []struct { name string executionInfo *persistence.WorkflowExecutionInfo wantErr bool }{ { name: "success", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, { name: "error due to state", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, executionInfo: tc.executionInfo, logger: log.NewNoop(), } var scheduleEventID, startedEventID int64 = 123, 234 rets := &types.HistoryEvent{} if !tc.wantErr { decisionTaskManager.EXPECT().AddDecisionTaskTimedOutEvent(scheduleEventID, startedEventID).Return(rets, nil).Times(1) } historyEvent, err := builder.AddDecisionTaskTimedOutEvent(scheduleEventID, startedEventID) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, rets, historyEvent) } }) } } func TestReplicateDecisionTaskTimedOutEventMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } event := &types.HistoryEvent{} decisionTaskManager.EXPECT().ReplicateDecisionTaskTimedOutEvent(event).Return(nil).Times(1) err := builder.ReplicateDecisionTaskTimedOutEvent(event) assert.NoError(t, err) } func TestAddDecisionTaskScheduleToStartTimeoutEventMutableStateBuilder(t *testing.T) { tests := []struct { name string executionInfo *persistence.WorkflowExecutionInfo wantErr bool }{ { name: "success", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, { name: "error due to state", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, executionInfo: tc.executionInfo, logger: log.NewNoop(), } scheduleEventID := int64(123) rets := &types.HistoryEvent{} if !tc.wantErr { decisionTaskManager.EXPECT().AddDecisionTaskScheduleToStartTimeoutEvent(scheduleEventID).Return(rets, nil).Times(1) } historyEvent, err := builder.AddDecisionTaskScheduleToStartTimeoutEvent(scheduleEventID) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, rets, historyEvent) } }) } } func TestAddDecisionTaskResetTimeoutEventMutableStateBuilder(t *testing.T) { tests := []struct { name string executionInfo *persistence.WorkflowExecutionInfo wantErr bool }{ { name: "success", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, { name: "error due to state", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, executionInfo: tc.executionInfo, logger: log.NewNoop(), } var scheduleEventID, forkEventVersion int64 = 123, 1 baserRunID, newRunID, reason, resetRequestID := "baseRunID", "newRunID", "reason", "resetRequestID" rets := &types.HistoryEvent{} if !tc.wantErr { decisionTaskManager.EXPECT().AddDecisionTaskResetTimeoutEvent(scheduleEventID, baserRunID, newRunID, forkEventVersion, reason, resetRequestID).Return(rets, nil).Times(1) } historyEvent, err := builder.AddDecisionTaskResetTimeoutEvent(scheduleEventID, baserRunID, newRunID, forkEventVersion, reason, resetRequestID) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, rets, historyEvent) } }) } } func TestAddDecisionTaskFailedEventMutableStateBuilder(t *testing.T) { tests := []struct { name string executionInfo *persistence.WorkflowExecutionInfo wantErr bool }{ { name: "success", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCreated, }, }, { name: "error due to state", executionInfo: &persistence.WorkflowExecutionInfo{ State: persistence.WorkflowStateCompleted, }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, executionInfo: tc.executionInfo, logger: log.NewNoop(), } var scheduleEventID, startedEventID, forkEventVersion int64 = 123, 234, 1 cause := types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure details := []byte("details") identity, reason, binChecksum, baseRunID, newRunID, resetRequestID := "identity", "reason", "checksum", "baseRunID", "newRunID", "resetRequestID" rets := &types.HistoryEvent{} if !tc.wantErr { decisionTaskManager.EXPECT().AddDecisionTaskFailedEvent(scheduleEventID, startedEventID, cause, details, identity, reason, binChecksum, baseRunID, newRunID, forkEventVersion, resetRequestID).Return(rets, nil).Times(1) } historyEvent, err := builder.AddDecisionTaskFailedEvent(scheduleEventID, startedEventID, cause, details, identity, reason, binChecksum, baseRunID, newRunID, forkEventVersion, resetRequestID) if tc.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, rets, historyEvent) } }) } } func TestReplicateDecisionTaskFailedEventMutableStateBuilder(t *testing.T) { decisionTaskManager := NewMockmutableStateDecisionTaskManager(gomock.NewController(t)) builder := &mutableStateBuilder{ decisionTaskManager: decisionTaskManager, } event := &types.HistoryEvent{} decisionTaskManager.EXPECT().ReplicateDecisionTaskFailedEvent(event).Return(nil).Times(1) err := builder.ReplicateDecisionTaskFailedEvent(event) assert.NoError(t, err) } func TestAddBinaryCheckSumIfNotExistsMutableStateBuilder(t *testing.T) { timeNow := time.Now() runID := "runID" checkSum := "checkSum" eventID := int64(1) tests := []struct { name string decisionTaskCompletedEventAttributes *types.DecisionTaskCompletedEventAttributes autoResetPoints *types.ResetPoints shardConfig *config.Config pendingChildExecutionInfoIDs map[int64]*persistence.ChildExecutionInfo wantWorkFlowExecutionInfo *persistence.WorkflowExecutionInfo }{ { name: "binaryChecksum not added due to empty binChecksum", wantWorkFlowExecutionInfo: &persistence.WorkflowExecutionInfo{ RunID: runID, }, }, { name: "binaryChecksum not added due to existing checksum with AutoResetPoints and AutoResetPoints.Points in executionInfo", decisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ BinaryChecksum: checkSum, }, autoResetPoints: &types.ResetPoints{ Points: []*types.ResetPointInfo{ {BinaryChecksum: checkSum}, }, }, wantWorkFlowExecutionInfo: &persistence.WorkflowExecutionInfo{ RunID: runID, AutoResetPoints: &types.ResetPoints{ Points: []*types.ResetPointInfo{ {BinaryChecksum: checkSum}, }, }, }, }, { name: "success with existing distinct autoResetPoints", decisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ BinaryChecksum: checkSum, }, autoResetPoints: &types.ResetPoints{ Points: []*types.ResetPointInfo{ {BinaryChecksum: "anotherCheckSum"}, {BinaryChecksum: "toBeTrimmed"}, }, }, shardConfig: &config.Config{ WriteVisibilityStoreName: dynamicproperties.GetStringPropertyFn("off"), }, wantWorkFlowExecutionInfo: &persistence.WorkflowExecutionInfo{ RunID: runID, AutoResetPoints: &types.ResetPoints{ Points: []*types.ResetPointInfo{ { BinaryChecksum: checkSum, RunID: runID, FirstDecisionCompletedID: eventID, CreatedTimeNano: common.Int64Ptr(timeNow.UnixNano()), Resettable: true, }, }, }, SearchAttributes: map[string][]byte{ definition.BinaryChecksums: func() []byte { bytes, _ := json.Marshal([]string{checkSum}) return bytes }(), }, }, }, { name: "success with AdvancedVisibilityWritingEnabled", decisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ BinaryChecksum: checkSum, }, shardConfig: &config.Config{ WriteVisibilityStoreName: dynamicproperties.GetStringPropertyFn("es"), IsAdvancedVisConfigExist: true, }, wantWorkFlowExecutionInfo: &persistence.WorkflowExecutionInfo{ RunID: runID, AutoResetPoints: &types.ResetPoints{ Points: []*types.ResetPointInfo{ { BinaryChecksum: checkSum, RunID: runID, FirstDecisionCompletedID: eventID, CreatedTimeNano: common.Int64Ptr(timeNow.UnixNano()), Resettable: true, }, }, }, SearchAttributes: map[string][]byte{ definition.BinaryChecksums: func() []byte { bytes, _ := json.Marshal([]string{checkSum}) return bytes }(), }, }, }, { name: "success with CheckResettable error", decisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ BinaryChecksum: checkSum, }, shardConfig: &config.Config{ WriteVisibilityStoreName: dynamicproperties.GetStringPropertyFn("off"), }, pendingChildExecutionInfoIDs: map[int64]*persistence.ChildExecutionInfo{ 1: {}, }, wantWorkFlowExecutionInfo: &persistence.WorkflowExecutionInfo{ RunID: runID, AutoResetPoints: &types.ResetPoints{ Points: []*types.ResetPointInfo{ { BinaryChecksum: checkSum, RunID: runID, FirstDecisionCompletedID: eventID, CreatedTimeNano: common.Int64Ptr(timeNow.UnixNano()), }, }, }, SearchAttributes: map[string][]byte{ definition.BinaryChecksums: func() []byte { bytes, _ := json.Marshal([]string{checkSum}) return bytes }(), }, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) builder := &mutableStateBuilder{ logger: log.NewNoop(), executionInfo: &persistence.WorkflowExecutionInfo{ AutoResetPoints: tc.autoResetPoints, RunID: runID, }, timeSource: clock.NewMockedTimeSourceAt(timeNow), pendingChildExecutionInfoIDs: tc.pendingChildExecutionInfoIDs, shard: shard.NewMockContext(ctrl), taskGenerator: NewMockMutableStateTaskGenerator(ctrl), } event := &types.HistoryEvent{ ID: eventID, DecisionTaskCompletedEventAttributes: tc.decisionTaskCompletedEventAttributes, } maxResetPoints := 1 if tc.shardConfig != nil { builder.shard.(*shard.MockContext).EXPECT().GetConfig().Return(tc.shardConfig).Times(2) } if tc.shardConfig != nil && tc.shardConfig.IsAdvancedVisConfigExist { builder.taskGenerator.(*MockMutableStateTaskGenerator).EXPECT().GenerateWorkflowSearchAttrTasks().Return(nil).Times(1) } err := builder.addBinaryCheckSumIfNotExists(event, maxResetPoints) assert.NoError(t, err) if diff := cmp.Diff(tc.wantWorkFlowExecutionInfo, builder.executionInfo); diff != "" { t.Fatalf("Mismatch (-want +got):\n%s", diff) } }) } } ================================================ FILE: service/history/execution/mutable_state_builder_methods_signal.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "fmt" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func (e *mutableStateBuilder) IsSignalRequested( requestID string, ) bool { if _, ok := e.pendingSignalRequestedIDs[requestID]; ok { return true } return false } // GetSignalInfo get details about a signal request that is currently in progress. func (e *mutableStateBuilder) GetSignalInfo( initiatedEventID int64, ) (*persistence.SignalInfo, bool) { ri, ok := e.pendingSignalInfoIDs[initiatedEventID] return ri, ok } func (e *mutableStateBuilder) GetPendingSignalExternalInfos() map[int64]*persistence.SignalInfo { return e.pendingSignalInfoIDs } func (e *mutableStateBuilder) AddWorkflowExecutionSignaled( signalName string, input []byte, identity string, requestID string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowSignaled if err := e.checkMutability(opTag); err != nil { return nil, err } event := e.hBuilder.AddWorkflowExecutionSignaledEvent(signalName, input, identity, requestID) if err := e.ReplicateWorkflowExecutionSignaled(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateWorkflowExecutionSignaled( event *types.HistoryEvent, ) error { // Increment signal count in mutable state for this workflow execution e.executionInfo.SignalCount++ e.insertWorkflowRequest(persistence.WorkflowRequest{ RequestID: event.WorkflowExecutionSignaledEventAttributes.RequestID, Version: event.Version, RequestType: persistence.WorkflowRequestTypeSignal, }) return nil } func (e *mutableStateBuilder) AddExternalWorkflowExecutionSignaled( initiatedID int64, domain string, workflowID string, runID string, control []byte, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionExternalWorkflowSignalRequested if err := e.checkMutability(opTag); err != nil { return nil, err } _, ok := e.GetSignalInfo(initiatedID) if !ok { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } event := e.hBuilder.AddExternalWorkflowExecutionSignaled(initiatedID, domain, workflowID, runID, control) if err := e.ReplicateExternalWorkflowExecutionSignaled(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateExternalWorkflowExecutionSignaled( event *types.HistoryEvent, ) error { initiatedID := event.ExternalWorkflowExecutionSignaledEventAttributes.GetInitiatedEventID() return e.DeletePendingSignal(initiatedID) } func (e *mutableStateBuilder) AddSignalExternalWorkflowExecutionFailedEvent( decisionTaskCompletedEventID int64, initiatedID int64, domain string, workflowID string, runID string, control []byte, cause types.SignalExternalWorkflowExecutionFailedCause, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionExternalWorkflowSignalFailed if err := e.checkMutability(opTag); err != nil { return nil, err } _, ok := e.GetSignalInfo(initiatedID) if !ok { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowInitiatedID(initiatedID)) return nil, e.createInternalServerError(opTag) } event := e.hBuilder.AddSignalExternalWorkflowExecutionFailedEvent(decisionTaskCompletedEventID, initiatedID, domain, workflowID, runID, control, cause) if err := e.ReplicateSignalExternalWorkflowExecutionFailedEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateSignalExternalWorkflowExecutionFailedEvent( event *types.HistoryEvent, ) error { initiatedID := event.SignalExternalWorkflowExecutionFailedEventAttributes.GetInitiatedEventID() return e.DeletePendingSignal(initiatedID) } func (e *mutableStateBuilder) AddSignalRequested( requestID string, ) { if e.pendingSignalRequestedIDs == nil { e.pendingSignalRequestedIDs = make(map[string]struct{}) } if e.updateSignalRequestedIDs == nil { e.updateSignalRequestedIDs = make(map[string]struct{}) } e.pendingSignalRequestedIDs[requestID] = struct{}{} // add requestID to set e.updateSignalRequestedIDs[requestID] = struct{}{} } func (e *mutableStateBuilder) DeleteSignalRequested( requestID string, ) { delete(e.pendingSignalRequestedIDs, requestID) delete(e.updateSignalRequestedIDs, requestID) e.deleteSignalRequestedIDs[requestID] = struct{}{} } func (e *mutableStateBuilder) AddSignalExternalWorkflowExecutionInitiatedEvent( decisionCompletedEventID int64, signalRequestID string, request *types.SignalExternalWorkflowExecutionDecisionAttributes, ) (*types.HistoryEvent, *persistence.SignalInfo, error) { opTag := tag.WorkflowActionExternalWorkflowSignalInitiated if err := e.checkMutability(opTag); err != nil { return nil, nil, err } event := e.hBuilder.AddSignalExternalWorkflowExecutionInitiatedEvent(decisionCompletedEventID, request) si, err := e.ReplicateSignalExternalWorkflowExecutionInitiatedEvent(decisionCompletedEventID, event, signalRequestID) if err != nil { return nil, nil, err } return event, si, nil } func (e *mutableStateBuilder) ReplicateSignalExternalWorkflowExecutionInitiatedEvent( firstEventID int64, event *types.HistoryEvent, signalRequestID string, ) (*persistence.SignalInfo, error) { // TODO: Consider also writing signalRequestID to history event initiatedEventID := event.ID attributes := event.SignalExternalWorkflowExecutionInitiatedEventAttributes si := &persistence.SignalInfo{ Version: event.Version, InitiatedEventBatchID: firstEventID, InitiatedID: initiatedEventID, SignalRequestID: signalRequestID, SignalName: attributes.GetSignalName(), Input: attributes.Input, Control: attributes.Control, } e.pendingSignalInfoIDs[si.InitiatedID] = si e.updateSignalInfos[si.InitiatedID] = si return si, e.taskGenerator.GenerateSignalExternalTasks(event) } // DeletePendingSignal deletes details about a SignalInfo func (e *mutableStateBuilder) DeletePendingSignal( initiatedEventID int64, ) error { if _, ok := e.pendingSignalInfoIDs[initiatedEventID]; ok { delete(e.pendingSignalInfoIDs, initiatedEventID) } else { e.logError( fmt.Sprintf("unable to find signal external workflow event ID: %v in mutable state", initiatedEventID), tag.ErrorTypeInvalidMutableStateAction, ) // log data inconsistency instead of returning an error e.logDataInconsistency() } delete(e.updateSignalInfos, initiatedEventID) e.deleteSignalInfos[initiatedEventID] = struct{}{} return nil } ================================================ FILE: service/history/execution/mutable_state_builder_methods_signal_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" ) func Test__IsSignalRequested(t *testing.T) { requestID := "101" t.Run("signal not found", func(t *testing.T) { mb := testMutableStateBuilder(t) result := mb.IsSignalRequested(requestID) assert.False(t, result) }) t.Run("signal found", func(t *testing.T) { mb := testMutableStateBuilder(t) mb.pendingSignalRequestedIDs[requestID] = struct{}{} result := mb.IsSignalRequested(requestID) assert.True(t, result) }) } func Test__GetSignalInfo(t *testing.T) { initiatedEventID := int64(1) info := &persistence.SignalInfo{ InitiatedID: 1, SignalRequestID: "101", } t.Run("signal not found", func(t *testing.T) { mb := testMutableStateBuilder(t) _, ok := mb.GetSignalInfo(initiatedEventID) assert.False(t, ok) }) t.Run("signal found", func(t *testing.T) { mb := testMutableStateBuilder(t) mb.pendingSignalInfoIDs[initiatedEventID] = info result, ok := mb.GetSignalInfo(initiatedEventID) assert.True(t, ok) assert.Equal(t, info, result) }) } func Test__ReplicateExternalWorkflowExecutionSignaled(t *testing.T) { mb := testMutableStateBuilder(t) event := &types.HistoryEvent{ ExternalWorkflowExecutionSignaledEventAttributes: &types.ExternalWorkflowExecutionSignaledEventAttributes{ InitiatedEventID: 1, }, } info := &persistence.SignalInfo{ InitiatedID: 1, SignalRequestID: "101", } mb.pendingSignalInfoIDs[int64(1)] = info mb.updateSignalInfos[int64(1)] = info err := mb.ReplicateExternalWorkflowExecutionSignaled(event) assert.NoError(t, err) assert.NotNil(t, mb.deleteSignalInfos[int64(1)]) _, ok := mb.pendingSignalInfoIDs[int64(1)] assert.False(t, ok) _, ok = mb.updateSignalInfos[int64(1)] assert.False(t, ok) } func Test__ReplicateSignalExternalWorkflowExecutionFailedEvent(t *testing.T) { mb := testMutableStateBuilder(t) event := &types.HistoryEvent{ SignalExternalWorkflowExecutionFailedEventAttributes: &types.SignalExternalWorkflowExecutionFailedEventAttributes{ InitiatedEventID: 1, }, } info := &persistence.SignalInfo{ InitiatedID: 1, SignalRequestID: "101", } mb.pendingSignalInfoIDs[int64(1)] = info mb.updateSignalInfos[int64(1)] = info err := mb.ReplicateSignalExternalWorkflowExecutionFailedEvent(event) assert.NoError(t, err) assert.NotNil(t, mb.deleteSignalInfos[int64(1)]) _, ok := mb.pendingSignalInfoIDs[int64(1)] assert.False(t, ok) _, ok = mb.updateSignalInfos[int64(1)] assert.False(t, ok) } func Test__AddSignalRequested(t *testing.T) { mb := testMutableStateBuilder(t) requestID := "101" mb.pendingSignalRequestedIDs = nil mb.updateSignalRequestedIDs = nil mb.AddSignalRequested(requestID) assert.NotNil(t, mb.pendingSignalRequestedIDs[requestID]) assert.NotNil(t, mb.updateSignalRequestedIDs[requestID]) } func Test__DeleteSignalRequested(t *testing.T) { mb := testMutableStateBuilder(t) requestID := "101" mb.pendingSignalRequestedIDs[requestID] = struct{}{} mb.updateSignalRequestedIDs[requestID] = struct{}{} mb.DeleteSignalRequested(requestID) assert.NotNil(t, mb.deleteSignalRequestedIDs[requestID]) } func Test__AddExternalWorkflowExecutionSignaled(t *testing.T) { t.Run("error workflow finished", func(t *testing.T) { mbCompleted := testMutableStateBuilder(t) mbCompleted.executionInfo.State = persistence.WorkflowStateCompleted _, err := mbCompleted.AddExternalWorkflowExecutionSignaled(1, "test-domain", "wid", "rid", []byte{10}) assert.Error(t, err) assert.Equal(t, ErrWorkflowFinished, err) }) t.Run("error getting signal info", func(t *testing.T) { mb := testMutableStateBuilder(t) _, err := mb.AddExternalWorkflowExecutionSignaled(1, "test-domain", "wid", "rid", []byte{10}) assert.Error(t, err) assert.Equal(t, "add-externalworkflow-signal-requested-event operation failed", err.Error()) }) t.Run("success", func(t *testing.T) { mb := testMutableStateBuilder(t) si := &persistence.SignalInfo{ InitiatedID: 1, } mb.pendingSignalInfoIDs[1] = si mb.hBuilder = NewHistoryBuilder(mb) event, err := mb.AddExternalWorkflowExecutionSignaled(1, "test-domain", "wid", "rid", []byte{10}) assert.NoError(t, err) assert.Equal(t, int64(1), event.ExternalWorkflowExecutionSignaledEventAttributes.GetInitiatedEventID()) }) } func Test__AddSignalExternalWorkflowExecutionFailedEvent(t *testing.T) { t.Run("error workflow finished", func(t *testing.T) { mbCompleted := testMutableStateBuilder(t) mbCompleted.executionInfo.State = persistence.WorkflowStateCompleted _, err := mbCompleted.AddSignalExternalWorkflowExecutionFailedEvent(1, 1, "test-domain", "wid", "rid", []byte{10}, types.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted) assert.Error(t, err) assert.Equal(t, ErrWorkflowFinished, err) }) t.Run("error getting signal info", func(t *testing.T) { mb := testMutableStateBuilder(t) _, err := mb.AddSignalExternalWorkflowExecutionFailedEvent(1, 1, "test-domain", "wid", "rid", []byte{10}, types.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted) assert.Error(t, err) assert.Equal(t, "add-externalworkflow-signal-failed-event operation failed", err.Error()) }) t.Run("success", func(t *testing.T) { mb := testMutableStateBuilder(t) si := &persistence.SignalInfo{ InitiatedID: 1, } mb.pendingSignalInfoIDs[1] = si mb.hBuilder = NewHistoryBuilder(mb) event, err := mb.AddSignalExternalWorkflowExecutionFailedEvent(1, 1, "test-domain", "wid", "rid", []byte{10}, types.SignalExternalWorkflowExecutionFailedCauseWorkflowAlreadyCompleted) assert.NoError(t, err) assert.Equal(t, int64(1), event.SignalExternalWorkflowExecutionFailedEventAttributes.GetInitiatedEventID()) }) } func Test__AddSignalExternalWorkflowExecutionInitiatedEvent(t *testing.T) { request := &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: constants.TestDomainName, Execution: &types.WorkflowExecution{ WorkflowID: "wid", RunID: "rid", }, SignalName: "test-signal", Input: make([]byte, 0), } t.Run("error workflow finished", func(t *testing.T) { mbCompleted := testMutableStateBuilder(t) mbCompleted.executionInfo.State = persistence.WorkflowStateCompleted _, _, err := mbCompleted.AddSignalExternalWorkflowExecutionInitiatedEvent(1, "101", request) assert.Error(t, err) assert.Equal(t, ErrWorkflowFinished, err) }) t.Run("success", func(t *testing.T) { mb := testMutableStateBuilder(t) mb.executionInfo = &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: "wid", RunID: "rid", } mb.hBuilder = NewHistoryBuilder(mb) event, si, err := mb.AddSignalExternalWorkflowExecutionInitiatedEvent(1, "101", request) assert.NoError(t, err) assert.Equal(t, request.Execution, event.SignalExternalWorkflowExecutionInitiatedEventAttributes.GetWorkflowExecution()) assert.Equal(t, "101", si.SignalRequestID) }) } ================================================ FILE: service/history/execution/mutable_state_builder_methods_started.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func (e *mutableStateBuilder) addWorkflowExecutionStartedEventForContinueAsNew( parentExecutionInfo *types.ParentExecutionInfo, execution types.WorkflowExecution, previousExecutionState MutableState, attributes *types.ContinueAsNewWorkflowExecutionDecisionAttributes, firstRunID string, firstScheduledTime time.Time, ) (*types.HistoryEvent, error) { previousExecutionInfo := previousExecutionState.GetExecutionInfo() tl := &types.TaskList{ Name: previousExecutionInfo.TaskList, Kind: previousExecutionInfo.TaskListKind.Ptr(), } // ContinueAsNew can change the name, not the kind if attributes.TaskList != nil { tl.Name = attributes.TaskList.Name } workflowType := previousExecutionInfo.WorkflowTypeName if attributes.WorkflowType != nil { workflowType = attributes.WorkflowType.GetName() } wType := &types.WorkflowType{} wType.Name = workflowType decisionTimeout := previousExecutionInfo.DecisionStartToCloseTimeout if attributes.TaskStartToCloseTimeoutSeconds != nil { decisionTimeout = attributes.GetTaskStartToCloseTimeoutSeconds() } createRequest := &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: e.domainEntry.GetInfo().Name, WorkflowID: execution.WorkflowID, TaskList: tl, WorkflowType: wType, TaskStartToCloseTimeoutSeconds: common.Int32Ptr(decisionTimeout), ExecutionStartToCloseTimeoutSeconds: attributes.ExecutionStartToCloseTimeoutSeconds, Input: attributes.Input, Header: attributes.Header, RetryPolicy: attributes.RetryPolicy, CronSchedule: attributes.CronSchedule, Memo: attributes.Memo, SearchAttributes: attributes.SearchAttributes, JitterStartSeconds: attributes.JitterStartSeconds, CronOverlapPolicy: attributes.CronOverlapPolicy, ActiveClusterSelectionPolicy: attributes.ActiveClusterSelectionPolicy, } req := &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: e.domainEntry.GetInfo().ID, StartRequest: createRequest, ParentExecutionInfo: parentExecutionInfo, LastCompletionResult: attributes.LastCompletionResult, ContinuedFailureReason: attributes.FailureReason, ContinuedFailureDetails: attributes.FailureDetails, ContinueAsNewInitiator: attributes.Initiator, FirstDecisionTaskBackoffSeconds: attributes.BackoffStartIntervalInSeconds, PartitionConfig: previousExecutionInfo.PartitionConfig, } // if ContinueAsNew as Cron or decider, recalculate the expiration timestamp and set attempts to 0 req.Attempt = 0 if attributes.RetryPolicy != nil && attributes.RetryPolicy.GetExpirationIntervalInSeconds() > 0 { // expirationTime calculates from first decision task schedule to the end of the workflow expirationInSeconds := attributes.RetryPolicy.GetExpirationIntervalInSeconds() + req.GetFirstDecisionTaskBackoffSeconds() expirationTime := e.timeSource.Now().Add(time.Second * time.Duration(expirationInSeconds)) req.ExpirationTimestamp = common.Int64Ptr(expirationTime.UnixNano()) } // if ContinueAsNew as retry use the same expiration timestamp and increment attempts from previous execution state if attributes.GetInitiator() == types.ContinueAsNewInitiatorRetryPolicy { req.Attempt = previousExecutionState.GetExecutionInfo().Attempt + 1 expirationTime := previousExecutionState.GetExecutionInfo().ExpirationTime if !expirationTime.IsZero() { req.ExpirationTimestamp = common.Int64Ptr(expirationTime.UnixNano()) } } // History event only has domainName so domainID has to be passed in explicitly to update the mutable state var parentDomainID *string if parentExecutionInfo != nil { parentDomainID = &parentExecutionInfo.DomainUUID } event := e.hBuilder.AddWorkflowExecutionStartedEvent(req, previousExecutionInfo, firstRunID, execution.GetRunID(), firstScheduledTime) if err := e.ReplicateWorkflowExecutionStartedEvent( parentDomainID, execution, createRequest.GetRequestID(), event, false, ); err != nil { return nil, err } if err := e.SetHistoryTree(e.GetExecutionInfo().RunID); err != nil { return nil, err } if err := e.AddFirstDecisionTaskScheduled( event, ); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) AddWorkflowExecutionStartedEvent( execution types.WorkflowExecution, startRequest *types.HistoryStartWorkflowExecutionRequest, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowStarted if err := e.checkMutability(opTag); err != nil { return nil, err } request := startRequest.StartRequest eventID := e.GetNextEventID() if eventID != constants.FirstEventID { e.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(eventID), tag.ErrorTypeInvalidHistoryAction) return nil, e.createInternalServerError(opTag) } event := e.hBuilder.AddWorkflowExecutionStartedEvent(startRequest, nil, execution.GetRunID(), execution.GetRunID(), time.Now()) var parentDomainID *string if startRequest.ParentExecutionInfo != nil { parentDomainID = &startRequest.ParentExecutionInfo.DomainUUID } if err := e.ReplicateWorkflowExecutionStartedEvent( parentDomainID, execution, request.GetRequestID(), event, false); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateWorkflowExecutionStartedEvent( parentDomainID *string, execution types.WorkflowExecution, requestID string, startEvent *types.HistoryEvent, generateDelayedDecisionTasks bool, ) error { event := startEvent.WorkflowExecutionStartedEventAttributes if event.GetRequestID() != "" { // prefer requestID from history event, ideally we should remove the requestID parameter // removing it may or may not be backward compatible, so keep it now requestID = event.GetRequestID() } e.executionInfo.CreateRequestID = requestID e.insertWorkflowRequest(persistence.WorkflowRequest{ RequestID: requestID, Version: startEvent.Version, RequestType: persistence.WorkflowRequestTypeStart, }) e.executionInfo.DomainID = e.domainEntry.GetInfo().ID e.executionInfo.WorkflowID = execution.GetWorkflowID() e.executionInfo.RunID = execution.GetRunID() e.executionInfo.FirstExecutionRunID = event.GetFirstExecutionRunID() if e.executionInfo.FirstExecutionRunID == "" { e.executionInfo.FirstExecutionRunID = execution.GetRunID() } e.executionInfo.TaskList = event.TaskList.GetName() e.executionInfo.TaskListKind = event.TaskList.GetKind() e.executionInfo.WorkflowTypeName = event.WorkflowType.GetName() e.executionInfo.WorkflowTimeout = event.GetExecutionStartToCloseTimeoutSeconds() e.executionInfo.DecisionStartToCloseTimeout = event.GetTaskStartToCloseTimeoutSeconds() e.executionInfo.StartTimestamp = e.unixNanoToTime(startEvent.GetTimestamp()) if err := e.UpdateWorkflowStateCloseStatus( persistence.WorkflowStateCreated, persistence.WorkflowCloseStatusNone, ); err != nil { return err } e.executionInfo.LastProcessedEvent = constants.EmptyEventID e.executionInfo.LastFirstEventID = startEvent.ID e.executionInfo.DecisionVersion = constants.EmptyVersion e.executionInfo.DecisionScheduleID = constants.EmptyEventID e.executionInfo.DecisionStartedID = constants.EmptyEventID e.executionInfo.DecisionRequestID = constants.EmptyUUID e.executionInfo.DecisionTimeout = 0 e.executionInfo.CronSchedule = event.GetCronSchedule() if event.CronOverlapPolicy != nil { e.executionInfo.CronOverlapPolicy = *event.CronOverlapPolicy } if parentDomainID != nil { e.executionInfo.ParentDomainID = *parentDomainID } if event.ParentWorkflowExecution != nil { e.executionInfo.ParentWorkflowID = event.ParentWorkflowExecution.GetWorkflowID() e.executionInfo.ParentRunID = event.ParentWorkflowExecution.GetRunID() } if event.ParentInitiatedEventID != nil { e.executionInfo.InitiatedID = event.GetParentInitiatedEventID() } else { e.executionInfo.InitiatedID = constants.EmptyEventID } if event.CronOverlapPolicy != nil { e.executionInfo.CronOverlapPolicy = *event.CronOverlapPolicy } e.executionInfo.Attempt = event.GetAttempt() if event.GetExpirationTimestamp() != 0 { e.executionInfo.ExpirationTime = time.Unix(0, event.GetExpirationTimestamp()) } if event.RetryPolicy != nil { e.executionInfo.HasRetryPolicy = true e.executionInfo.BackoffCoefficient = event.RetryPolicy.GetBackoffCoefficient() e.executionInfo.ExpirationSeconds = event.RetryPolicy.GetExpirationIntervalInSeconds() e.executionInfo.InitialInterval = event.RetryPolicy.GetInitialIntervalInSeconds() e.executionInfo.MaximumAttempts = event.RetryPolicy.GetMaximumAttempts() e.executionInfo.MaximumInterval = event.RetryPolicy.GetMaximumIntervalInSeconds() e.executionInfo.NonRetriableErrors = event.RetryPolicy.NonRetriableErrorReasons } e.executionInfo.AutoResetPoints = rolloverAutoResetPointsWithExpiringTime( event.GetPrevAutoResetPoints(), event.GetContinuedExecutionRunID(), startEvent.GetTimestamp(), e.domainEntry.GetRetentionDays(e.executionInfo.WorkflowID), ) if event.Memo != nil { e.executionInfo.Memo = event.Memo.GetFields() } if event.SearchAttributes != nil { e.executionInfo.SearchAttributes = event.SearchAttributes.GetIndexedFields() } e.executionInfo.PartitionConfig = event.PartitionConfig e.executionInfo.ActiveClusterSelectionPolicy = event.ActiveClusterSelectionPolicy e.writeEventToCache(startEvent) if err := e.taskGenerator.GenerateWorkflowStartTasks(e.unixNanoToTime(startEvent.GetTimestamp()), startEvent); err != nil { return err } if err := e.taskGenerator.GenerateRecordWorkflowStartedTasks(startEvent); err != nil { return err } if generateDelayedDecisionTasks && event.GetFirstDecisionTaskBackoffSeconds() > 0 { if err := e.taskGenerator.GenerateDelayedDecisionTasks(startEvent); err != nil { return err } } return nil } ================================================ FILE: service/history/execution/mutable_state_builder_methods_timedout.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func (e *mutableStateBuilder) AddTimeoutWorkflowEvent( firstEventID int64, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionWorkflowTimeout if err := e.checkMutability(opTag); err != nil { return nil, err } event := e.hBuilder.AddTimeoutWorkflowEvent() if err := e.ReplicateWorkflowExecutionTimedoutEvent(firstEventID, event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateWorkflowExecutionTimedoutEvent( firstEventID int64, event *types.HistoryEvent, ) error { if err := e.UpdateWorkflowStateCloseStatus( persistence.WorkflowStateCompleted, persistence.WorkflowCloseStatusTimedOut, ); err != nil { return err } e.executionInfo.CompletionEventBatchID = firstEventID // Used when completion event needs to be loaded from database e.ClearStickyness() e.writeEventToCache(event) return e.taskGenerator.GenerateWorkflowCloseTasks(event, e.config.WorkflowDeletionJitterRange(e.domainEntry.GetInfo().Name)) } ================================================ FILE: service/history/execution/mutable_state_builder_methods_timer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "fmt" "time" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func (e *mutableStateBuilder) GetPendingTimerInfos() map[string]*persistence.TimerInfo { return e.pendingTimerInfoIDs } func (e *mutableStateBuilder) AddTimerStartedEvent( decisionCompletedEventID int64, request *types.StartTimerDecisionAttributes, ) (*types.HistoryEvent, *persistence.TimerInfo, error) { opTag := tag.WorkflowActionTimerStarted if err := e.checkMutability(opTag); err != nil { return nil, nil, err } timerID := request.GetTimerID() _, ok := e.GetUserTimerInfo(timerID) if ok { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowTimerID(timerID)) return nil, nil, e.createCallerError(opTag) } event := e.hBuilder.AddTimerStartedEvent(decisionCompletedEventID, request) ti, err := e.ReplicateTimerStartedEvent(event) if err != nil { return nil, nil, err } return event, ti, err } func (e *mutableStateBuilder) ReplicateTimerStartedEvent( event *types.HistoryEvent, ) (*persistence.TimerInfo, error) { attributes := event.TimerStartedEventAttributes timerID := attributes.GetTimerID() startToFireTimeout := attributes.GetStartToFireTimeoutSeconds() fireTimeout := time.Duration(startToFireTimeout) * time.Second // TODO: Time skew need to be taken in to account. expiryTime := time.Unix(0, event.GetTimestamp()).Add(fireTimeout) // should use the event time, not now ti := &persistence.TimerInfo{ Version: event.Version, TimerID: timerID, ExpiryTime: expiryTime, StartedID: event.ID, TaskStatus: TimerTaskStatusNone, } e.pendingTimerInfoIDs[ti.TimerID] = ti e.pendingTimerEventIDToID[ti.StartedID] = ti.TimerID e.updateTimerInfos[ti.TimerID] = ti return ti, nil } func (e *mutableStateBuilder) AddTimerFiredEvent( timerID string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionTimerFired if err := e.checkMutability(opTag); err != nil { return nil, err } timerInfo, ok := e.GetUserTimerInfo(timerID) if !ok { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowTimerID(timerID)) return nil, e.createInternalServerError(opTag) } // Timer is running. event := e.hBuilder.AddTimerFiredEvent(timerInfo.StartedID, timerID) if err := e.ReplicateTimerFiredEvent(event); err != nil { return nil, err } return event, nil } func (e *mutableStateBuilder) ReplicateTimerFiredEvent( event *types.HistoryEvent, ) error { attributes := event.TimerFiredEventAttributes timerID := attributes.GetTimerID() return e.DeleteUserTimer(timerID) } func (e *mutableStateBuilder) AddTimerCanceledEvent( decisionCompletedEventID int64, attributes *types.CancelTimerDecisionAttributes, identity string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionTimerCanceled if err := e.checkMutability(opTag); err != nil { return nil, err } var timerStartedID int64 timerID := attributes.GetTimerID() ti, ok := e.GetUserTimerInfo(timerID) if !ok { // if timer is not running then check if it has fired in the mutable state. // If so clear the timer from the mutable state. We need to check both the // bufferedEvents and the history builder timerFiredEvent := e.checkAndClearTimerFiredEvent(timerID) if timerFiredEvent == nil { e.logWarn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(e.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowTimerID(timerID)) return nil, e.createCallerError(opTag) } timerStartedID = timerFiredEvent.TimerFiredEventAttributes.GetStartedEventID() } else { timerStartedID = ti.StartedID } // Timer is running. event := e.hBuilder.AddTimerCanceledEvent(timerStartedID, decisionCompletedEventID, timerID, identity) if ok { if err := e.ReplicateTimerCanceledEvent(event); err != nil { return nil, err } } return event, nil } func (e *mutableStateBuilder) ReplicateTimerCanceledEvent( event *types.HistoryEvent, ) error { attributes := event.TimerCanceledEventAttributes timerID := attributes.GetTimerID() return e.DeleteUserTimer(timerID) } func (e *mutableStateBuilder) AddCancelTimerFailedEvent( decisionCompletedEventID int64, attributes *types.CancelTimerDecisionAttributes, identity string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionTimerCancelFailed if err := e.checkMutability(opTag); err != nil { return nil, err } // No Operation: We couldn't cancel it probably TIMER_ID_UNKNOWN timerID := attributes.GetTimerID() return e.hBuilder.AddCancelTimerFailedEvent(timerID, decisionCompletedEventID, timerCancellationMsgTimerIDUnknown, identity), nil } // GetUserTimerInfo gives details about a user timer. func (e *mutableStateBuilder) GetUserTimerInfo( timerID string, ) (*persistence.TimerInfo, bool) { timerInfo, ok := e.pendingTimerInfoIDs[timerID] return timerInfo, ok } // UpdateUserTimer updates the user timer in progress. func (e *mutableStateBuilder) UpdateUserTimer( ti *persistence.TimerInfo, ) error { timerID, ok := e.pendingTimerEventIDToID[ti.StartedID] if !ok { e.logError( fmt.Sprintf("unable to find timer event ID: %v in mutable state", ti.StartedID), tag.ErrorTypeInvalidMutableStateAction, ) return ErrMissingTimerInfo } if _, ok := e.pendingTimerInfoIDs[timerID]; !ok { e.logError( fmt.Sprintf("unable to find timer ID: %v in mutable state", timerID), tag.ErrorTypeInvalidMutableStateAction, ) return ErrMissingTimerInfo } e.pendingTimerInfoIDs[ti.TimerID] = ti e.updateTimerInfos[ti.TimerID] = ti return nil } // DeleteUserTimer deletes an user timer. func (e *mutableStateBuilder) DeleteUserTimer( timerID string, ) error { if timerInfo, ok := e.pendingTimerInfoIDs[timerID]; ok { delete(e.pendingTimerInfoIDs, timerID) if _, ok = e.pendingTimerEventIDToID[timerInfo.StartedID]; ok { delete(e.pendingTimerEventIDToID, timerInfo.StartedID) } else { e.logError( fmt.Sprintf("unable to find timer event ID: %v in mutable state", timerID), tag.ErrorTypeInvalidMutableStateAction, ) // log data inconsistency instead of returning an error e.logDataInconsistency() } } else { e.logError( fmt.Sprintf("unable to find timer ID: %v in mutable state", timerID), tag.ErrorTypeInvalidMutableStateAction, ) // log data inconsistency instead of returning an error e.logDataInconsistency() } delete(e.updateTimerInfos, timerID) e.deleteTimerInfos[timerID] = struct{}{} return nil } func checkAndClearTimerFiredEvent( events []*types.HistoryEvent, timerID string, ) ([]*types.HistoryEvent, *types.HistoryEvent) { // go over all history events. if we find a timer fired event for the given // timerID, clear it timerFiredIdx := -1 for idx, event := range events { if event.GetEventType() == types.EventTypeTimerFired && event.GetTimerFiredEventAttributes().GetTimerID() == timerID { timerFiredIdx = idx break } } if timerFiredIdx == -1 { return events, nil } timerEvent := events[timerFiredIdx] return append(events[:timerFiredIdx], events[timerFiredIdx+1:]...), timerEvent } ================================================ FILE: service/history/execution/mutable_state_builder_methods_timer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func Test__checkAndClearTimerFiredEvent(t *testing.T) { t.Run("no timer fired event to clear", func(t *testing.T) { events := []*types.HistoryEvent{{ ID: 1, Timestamp: nil, EventType: types.EventTypeActivityTaskScheduled.Ptr(), }} remainingEvents, timerEvent := checkAndClearTimerFiredEvent(events, "1") assert.Nil(t, timerEvent) assert.Equal(t, len(events), len(remainingEvents)) }) t.Run("timer fired event cleared", func(t *testing.T) { timerEvent := &types.HistoryEvent{ ID: 2, Timestamp: nil, EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "101", StartedEventID: 0, }, } events := []*types.HistoryEvent{{ ID: 1, Timestamp: nil, EventType: types.EventTypeActivityTaskScheduled.Ptr(), }, timerEvent, } remainingEvents, clearedEvent := checkAndClearTimerFiredEvent(events, timerEvent.TimerFiredEventAttributes.TimerID) assert.NotNil(t, timerEvent) assert.Equal(t, timerEvent, clearedEvent) assert.Equal(t, len(events)-1, len(remainingEvents)) }) } func Test__DeleteUserTimer(t *testing.T) { mb := testMutableStateBuilder(t) ti := &persistence.TimerInfo{ TimerID: "101", StartedID: 1, } mb.pendingTimerInfoIDs[ti.TimerID] = ti mb.pendingTimerEventIDToID[ti.StartedID] = ti.TimerID err := mb.DeleteUserTimer(ti.TimerID) assert.NoError(t, err) } func Test__UpdateUserTimer(t *testing.T) { mb := testMutableStateBuilder(t) ti := &persistence.TimerInfo{ TimerID: "101", StartedID: 1, } t.Run("missing timer info", func(t *testing.T) { err := mb.UpdateUserTimer(ti) assert.Error(t, err) assert.Equal(t, ErrMissingTimerInfo, err) mb.pendingTimerEventIDToID[ti.StartedID] = ti.TimerID err = mb.UpdateUserTimer(ti) assert.Error(t, err) assert.Equal(t, ErrMissingTimerInfo, err) }) t.Run("success", func(t *testing.T) { mb.pendingTimerInfoIDs[ti.TimerID] = ti mb.pendingTimerEventIDToID[ti.StartedID] = ti.TimerID err := mb.UpdateUserTimer(ti) assert.NoError(t, err) }) } func Test__GetUserTimerInfo(t *testing.T) { mb := testMutableStateBuilder(t) ti := &persistence.TimerInfo{ TimerID: "101", StartedID: 1, } mb.pendingTimerInfoIDs[ti.TimerID] = ti info, ok := mb.GetUserTimerInfo(ti.TimerID) assert.Equal(t, ti, info) assert.True(t, ok) } func Test__ReplicateTimerCanceledEvent(t *testing.T) { mb := testMutableStateBuilder(t) timerEvent := &types.HistoryEvent{ ID: 1, Timestamp: nil, EventType: types.EventTypeTimerCanceled.Ptr(), TimerCanceledEventAttributes: &types.TimerCanceledEventAttributes{ TimerID: "101", StartedEventID: 0, }, } ti := &persistence.TimerInfo{ TimerID: "101", StartedID: 1, } mb.pendingTimerInfoIDs[ti.TimerID] = ti mb.pendingTimerEventIDToID[ti.StartedID] = ti.TimerID err := mb.ReplicateTimerCanceledEvent(timerEvent) assert.NoError(t, err) } func Test__ReplicateTimerFiredEvent(t *testing.T) { mb := testMutableStateBuilder(t) timerEvent := &types.HistoryEvent{ ID: 1, Timestamp: nil, EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "101", StartedEventID: 0, }, } ti := &persistence.TimerInfo{ TimerID: "101", StartedID: 1, } mb.pendingTimerInfoIDs[ti.TimerID] = ti mb.pendingTimerEventIDToID[ti.StartedID] = ti.TimerID err := mb.ReplicateTimerFiredEvent(timerEvent) assert.NoError(t, err) } func Test__ReplicateTimerStartedEvent(t *testing.T) { mb := testMutableStateBuilder(t) startToFireTimeoutSeconds := int64(5) now := time.Now() nowUnix := now.UnixNano() timerEvent := &types.HistoryEvent{ ID: 1, Version: 0, Timestamp: &nowUnix, EventType: types.EventTypeTimerStarted.Ptr(), TimerStartedEventAttributes: &types.TimerStartedEventAttributes{ TimerID: "101", StartToFireTimeoutSeconds: &startToFireTimeoutSeconds, }, } expectedTimerInfo := &persistence.TimerInfo{ Version: 0, TimerID: "101", StartedID: 1, ExpiryTime: now.Add(time.Second * time.Duration(int64(5))), } ti, err := mb.ReplicateTimerStartedEvent(timerEvent) assert.NoError(t, err) assert.Equal(t, expectedTimerInfo.ExpiryTime.UTC(), ti.ExpiryTime.UTC()) assert.Equal(t, expectedTimerInfo.TimerID, ti.TimerID) } func Test__GetPendingTimerInfos(t *testing.T) { mb := testMutableStateBuilder(t) pendingTimerInfo := map[string]*persistence.TimerInfo{ "101": { Version: 0, TimerID: "101", StartedID: 1, }, } mb.pendingTimerInfoIDs = pendingTimerInfo result := mb.GetPendingTimerInfos() assert.Equal(t, pendingTimerInfo, result) } ================================================ FILE: service/history/execution/mutable_state_builder_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "context" "errors" "math/rand" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/checksum" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" commonConfig "github.com/uber/cadence/common/config" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/testing/testdatagen" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/query" "github.com/uber/cadence/service/history/shard" shardCtx "github.com/uber/cadence/service/history/shard" ) type ( mutableStateSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockEventsCache *events.MockCache msBuilder *mutableStateBuilder logger log.Logger testScope tally.TestScope } ) func TestMutableStateSuite(t *testing.T) { s := new(mutableStateSuite) suite.Run(t, s) } func (s *mutableStateSuite) SetupSuite() { } func (s *mutableStateSuite) TearDownSuite() { } func (s *mutableStateSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) // set the checksum probabilities to 100% for exercising during test s.mockShard.GetConfig().MutableStateChecksumGenProbability = func(domain string) int { return 100 } s.mockShard.GetConfig().MutableStateChecksumVerifyProbability = func(domain string) int { return 100 } s.mockShard.GetConfig().EnableRetryForChecksumFailure = func(domain string) bool { return true } s.mockEventsCache = s.mockShard.GetEventsCache().(*events.MockCache) s.testScope = s.mockShard.Resource.MetricsScope s.logger = s.mockShard.GetLogger() s.mockShard.Resource.DomainCache.EXPECT().GetDomainID(constants.TestDomainName).Return(constants.TestDomainID, nil).AnyTimes() s.msBuilder = newMutableStateBuilder(s.mockShard, s.logger, constants.TestLocalDomainEntry, constants.TestLocalDomainEntry.GetFailoverVersion()) } func (s *mutableStateSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *mutableStateSuite) TestErrorReturnedWhenSchedulingTooManyPendingActivities() { for i := 0; i < s.msBuilder.config.PendingActivitiesCountLimitError(); i++ { s.msBuilder.pendingActivityInfoIDs[int64(i)] = &persistence.ActivityInfo{} } _, _, _, _, _, err := s.msBuilder.AddActivityTaskScheduledEvent(nil, 1, &types.ScheduleActivityTaskDecisionAttributes{}, false) assert.Equal(s.T(), "Too many pending activities", err.Error()) } func (s *mutableStateSuite) TestTransientDecisionCompletionFirstBatchReplicated_ReplicateDecisionCompleted() { version := int64(12) runID := uuid.New() s.msBuilder = NewMutableStateBuilderWithVersionHistoriesWithEventV2( s.mockShard, s.logger, version, runID, constants.TestGlobalDomainEntry, ).(*mutableStateBuilder) newDecisionScheduleEvent, newDecisionStartedEvent := s.prepareTransientDecisionCompletionFirstBatchReplicated(version, runID) newDecisionCompletedEvent := &types.HistoryEvent{ Version: version, ID: newDecisionStartedEvent.ID + 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: newDecisionScheduleEvent.ID, StartedEventID: newDecisionStartedEvent.ID, Identity: "some random identity", }, } err := s.msBuilder.ReplicateDecisionTaskCompletedEvent(newDecisionCompletedEvent) s.NoError(err) s.Equal(0, len(s.msBuilder.GetHistoryBuilder().transientHistory)) s.Equal(0, len(s.msBuilder.GetHistoryBuilder().history)) } func (s *mutableStateSuite) TestTransientDecisionCompletionFirstBatchReplicated_FailoverDecisionTimeout() { version := int64(12) runID := uuid.New() s.msBuilder = NewMutableStateBuilderWithVersionHistoriesWithEventV2( s.mockShard, s.logger, version, runID, constants.TestGlobalDomainEntry, ).(*mutableStateBuilder) newDecisionScheduleEvent, newDecisionStartedEvent := s.prepareTransientDecisionCompletionFirstBatchReplicated(version, runID) s.NotNil(s.msBuilder.AddDecisionTaskTimedOutEvent(newDecisionScheduleEvent.ID, newDecisionStartedEvent.ID)) s.Equal(0, len(s.msBuilder.GetHistoryBuilder().transientHistory)) s.Equal(1, len(s.msBuilder.GetHistoryBuilder().history)) } func (s *mutableStateSuite) TestTransientDecisionCompletionFirstBatchReplicated_FailoverDecisionFailed() { version := int64(12) runID := uuid.New() s.msBuilder = NewMutableStateBuilderWithVersionHistoriesWithEventV2( s.mockShard, s.logger, version, runID, constants.TestGlobalDomainEntry, ).(*mutableStateBuilder) newDecisionScheduleEvent, newDecisionStartedEvent := s.prepareTransientDecisionCompletionFirstBatchReplicated(version, runID) s.NotNil(s.msBuilder.AddDecisionTaskFailedEvent( newDecisionScheduleEvent.ID, newDecisionStartedEvent.ID, types.DecisionTaskFailedCauseWorkflowWorkerUnhandledFailure, []byte("some random decision failure details"), "some random decision failure identity", "", "", "", "", 0, "", )) s.Equal(0, len(s.msBuilder.GetHistoryBuilder().transientHistory)) s.Equal(1, len(s.msBuilder.GetHistoryBuilder().history)) } func (s *mutableStateSuite) TestShouldBufferEvent() { // workflow status events will be assign event ID immediately workflowEvents := map[types.EventType]bool{ types.EventTypeWorkflowExecutionStarted: true, types.EventTypeWorkflowExecutionCompleted: true, types.EventTypeWorkflowExecutionFailed: true, types.EventTypeWorkflowExecutionTimedOut: true, types.EventTypeWorkflowExecutionTerminated: true, types.EventTypeWorkflowExecutionContinuedAsNew: true, types.EventTypeWorkflowExecutionCanceled: true, } // decision events will be assign event ID immediately decisionTaskEvents := map[types.EventType]bool{ types.EventTypeDecisionTaskScheduled: true, types.EventTypeDecisionTaskStarted: true, types.EventTypeDecisionTaskCompleted: true, types.EventTypeDecisionTaskFailed: true, types.EventTypeDecisionTaskTimedOut: true, } // events corresponding to decisions from client will be assign event ID immediately decisionEvents := map[types.EventType]bool{ types.EventTypeWorkflowExecutionCompleted: true, types.EventTypeWorkflowExecutionFailed: true, types.EventTypeWorkflowExecutionCanceled: true, types.EventTypeWorkflowExecutionContinuedAsNew: true, types.EventTypeActivityTaskScheduled: true, types.EventTypeActivityTaskCancelRequested: true, types.EventTypeTimerStarted: true, types.EventTypeTimerCanceled: true, types.EventTypeCancelTimerFailed: true, types.EventTypeRequestCancelExternalWorkflowExecutionInitiated: true, types.EventTypeMarkerRecorded: true, types.EventTypeStartChildWorkflowExecutionInitiated: true, types.EventTypeSignalExternalWorkflowExecutionInitiated: true, types.EventTypeUpsertWorkflowSearchAttributes: true, } // other events will not be assign event ID immediately otherEvents := map[types.EventType]bool{} OtherEventsLoop: for _, eventType := range types.EventTypeValues() { if _, ok := workflowEvents[eventType]; ok { continue OtherEventsLoop } if _, ok := decisionTaskEvents[eventType]; ok { continue OtherEventsLoop } if _, ok := decisionEvents[eventType]; ok { continue OtherEventsLoop } otherEvents[eventType] = true } // test workflowEvents, decisionTaskEvents, decisionEvents will return true for eventType := range workflowEvents { s.False(s.msBuilder.shouldBufferEvent(eventType)) } for eventType := range decisionTaskEvents { s.False(s.msBuilder.shouldBufferEvent(eventType)) } for eventType := range decisionEvents { s.False(s.msBuilder.shouldBufferEvent(eventType)) } // other events will return false for eventType := range otherEvents { s.True(s.msBuilder.shouldBufferEvent(eventType)) } // +1 is because DecisionTypeCancelTimer will be mapped // to either types.EventTypeTimerCanceled, or types.EventTypeCancelTimerFailed. s.Equal(len(types.DecisionTypeValues())+1, len(decisionEvents), "This assertaion will be broken a new decision is added and no corresponding logic added to shouldBufferEvent()") } func (s *mutableStateSuite) TestReorderEvents() { domainID := constants.TestDomainID we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" activityID := "activity_id" activityResult := []byte("activity_result") info := &persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: we.GetWorkflowID(), RunID: we.GetRunID(), TaskList: tl, WorkflowTypeName: "wType", WorkflowTimeout: 200, DecisionStartToCloseTimeout: 100, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, NextEventID: int64(8), LastProcessedEvent: int64(3), LastUpdatedTimestamp: time.Now(), DecisionVersion: commonconstants.EmptyVersion, DecisionScheduleID: commonconstants.EmptyEventID, DecisionStartedID: commonconstants.EmptyEventID, DecisionTimeout: 100, } activityInfos := map[int64]*persistence.ActivityInfo{ 5: { Version: int64(1), ScheduleID: int64(5), ScheduledTime: time.Now(), StartedID: commonconstants.EmptyEventID, StartedTime: time.Now(), ActivityID: activityID, ScheduleToStartTimeout: 100, ScheduleToCloseTimeout: 200, StartToCloseTimeout: 300, HeartbeatTimeout: 50, }, } bufferedEvents := []*types.HistoryEvent{ { ID: commonconstants.BufferedEventID, EventType: types.EventTypeActivityTaskCompleted.Ptr(), Version: 1, ActivityTaskCompletedEventAttributes: &types.ActivityTaskCompletedEventAttributes{ Result: []byte(activityResult), ScheduledEventID: 5, StartedEventID: commonconstants.BufferedEventID, }, }, { ID: commonconstants.BufferedEventID, EventType: types.EventTypeActivityTaskStarted.Ptr(), Version: 1, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 5, }, }, } dbState := &persistence.WorkflowMutableState{ ExecutionInfo: info, ActivityInfos: activityInfos, BufferedEvents: bufferedEvents, } err := s.msBuilder.Load(context.Background(), dbState) s.Nil(err) s.Equal(types.EventTypeActivityTaskStarted, s.msBuilder.bufferedEvents[0].GetEventType()) s.Equal(int64(-123), s.msBuilder.bufferedEvents[0].ID) s.Equal(int64(5), s.msBuilder.bufferedEvents[0].ActivityTaskStartedEventAttributes.GetScheduledEventID()) s.Equal(types.EventTypeActivityTaskCompleted, s.msBuilder.bufferedEvents[1].GetEventType()) s.Equal(int64(-123), s.msBuilder.bufferedEvents[1].ID) s.Equal(int64(-123), s.msBuilder.bufferedEvents[1].ActivityTaskCompletedEventAttributes.GetStartedEventID()) s.Equal(int64(5), s.msBuilder.bufferedEvents[1].ActivityTaskCompletedEventAttributes.GetScheduledEventID()) err = s.msBuilder.FlushBufferedEvents() s.Nil(err) s.Equal(types.EventTypeActivityTaskStarted, s.msBuilder.hBuilder.history[0].GetEventType()) s.Equal(int64(8), s.msBuilder.hBuilder.history[0].ID) s.Equal(int64(5), s.msBuilder.hBuilder.history[0].ActivityTaskStartedEventAttributes.GetScheduledEventID()) s.Equal(types.EventTypeActivityTaskCompleted, s.msBuilder.hBuilder.history[1].GetEventType()) s.Equal(int64(9), s.msBuilder.hBuilder.history[1].ID) s.Equal(int64(8), s.msBuilder.hBuilder.history[1].ActivityTaskCompletedEventAttributes.GetStartedEventID()) s.Equal(int64(5), s.msBuilder.hBuilder.history[1].ActivityTaskCompletedEventAttributes.GetScheduledEventID()) } func (s *mutableStateSuite) TestChecksum() { testCases := []struct { name string enableBufferedEvents bool closeTxFunc func(ms *mutableStateBuilder) (checksum.Checksum, error) }{ { name: "closeTransactionAsSnapshot", closeTxFunc: func(ms *mutableStateBuilder) (checksum.Checksum, error) { snapshot, _, err := ms.CloseTransactionAsSnapshot(time.Now(), TransactionPolicyPassive) if err != nil { return checksum.Checksum{}, err } return snapshot.Checksum, err }, }, { name: "closeTransactionAsMutation", enableBufferedEvents: true, closeTxFunc: func(ms *mutableStateBuilder) (checksum.Checksum, error) { mutation, _, err := ms.CloseTransactionAsMutation(time.Now(), TransactionPolicyPassive) if err != nil { return checksum.Checksum{}, err } return mutation.Checksum, err }, }, } loadErrorsFunc := func() int64 { counter := s.testScope.Snapshot().Counters()["test.mutable_state_checksum_mismatch+operation=WorkflowContext"] if counter != nil { return counter.Value() } return 0 } var loadErrors int64 for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { dbState := buildWorkflowMutableState() if !tc.enableBufferedEvents { dbState.BufferedEvents = nil } // create mutable state and verify checksum is generated on close loadErrors = loadErrorsFunc() s.msBuilder.Load(context.Background(), dbState) s.Equal(loadErrors, loadErrorsFunc()) // no errors expected s.EqualValues(dbState.Checksum, s.msBuilder.checksum) s.msBuilder.domainEntry = s.newDomainCacheEntry() csum, err := tc.closeTxFunc(s.msBuilder) s.Nil(err) s.NotNil(csum.Value) s.Equal(checksum.FlavorIEEECRC32OverThriftBinary, csum.Flavor) s.Equal(mutableStateChecksumPayloadV1, csum.Version) s.EqualValues(csum, s.msBuilder.checksum) // verify checksum is verified on Load dbState.Checksum = csum err = s.msBuilder.Load(context.Background(), dbState) s.NoError(err) s.Equal(loadErrors, loadErrorsFunc()) // generate checksum again and verify its the same csum, err = tc.closeTxFunc(s.msBuilder) s.Nil(err) s.NotNil(csum.Value) s.Equal(dbState.Checksum.Value, csum.Value) // modify checksum and verify Load fails dbState.Checksum.Value[0]++ err = s.msBuilder.Load(context.Background(), dbState) s.Error(err) s.Equal(loadErrors+1, loadErrorsFunc()) s.EqualValues(dbState.Checksum, s.msBuilder.checksum) // test checksum is invalidated loadErrors = loadErrorsFunc() s.mockShard.GetConfig().MutableStateChecksumInvalidateBefore = func(...dynamicproperties.FilterOption) float64 { return float64((s.msBuilder.executionInfo.LastUpdatedTimestamp.UnixNano() / int64(time.Second)) + 1) } err = s.msBuilder.Load(context.Background(), dbState) s.NoError(err) s.Equal(loadErrors, loadErrorsFunc()) s.EqualValues(checksum.Checksum{}, s.msBuilder.checksum) // revert the config value for the next test case s.mockShard.GetConfig().MutableStateChecksumInvalidateBefore = func(...dynamicproperties.FilterOption) float64 { return float64(0) } }) } } func (s *mutableStateSuite) TestChecksumProbabilities() { for _, prob := range []int{0, 100} { s.mockShard.GetConfig().MutableStateChecksumGenProbability = func(domain string) int { return prob } s.mockShard.GetConfig().MutableStateChecksumVerifyProbability = func(domain string) int { return prob } for i := 0; i < 100; i++ { shouldGenerate := s.msBuilder.shouldGenerateChecksum() shouldVerify := s.msBuilder.shouldVerifyChecksum() s.Equal(prob == 100, shouldGenerate) s.Equal(prob == 100, shouldVerify) } } } func (s *mutableStateSuite) TestChecksumShouldInvalidate() { s.mockShard.GetConfig().MutableStateChecksumInvalidateBefore = func(...dynamicproperties.FilterOption) float64 { return 0 } s.False(s.msBuilder.shouldInvalidateChecksum()) s.msBuilder.executionInfo.LastUpdatedTimestamp = time.Now() s.mockShard.GetConfig().MutableStateChecksumInvalidateBefore = func(...dynamicproperties.FilterOption) float64 { return float64((s.msBuilder.executionInfo.LastUpdatedTimestamp.UnixNano() / int64(time.Second)) + 1) } s.True(s.msBuilder.shouldInvalidateChecksum()) s.mockShard.GetConfig().MutableStateChecksumInvalidateBefore = func(...dynamicproperties.FilterOption) float64 { return float64((s.msBuilder.executionInfo.LastUpdatedTimestamp.UnixNano() / int64(time.Second)) - 1) } s.False(s.msBuilder.shouldInvalidateChecksum()) } func (s *mutableStateSuite) TestTrimEvents() { var input []*types.HistoryEvent output := s.msBuilder.trimEventsAfterWorkflowClose(input) s.Equal(input, output) input = []*types.HistoryEvent{} output = s.msBuilder.trimEventsAfterWorkflowClose(input) s.Equal(input, output) input = []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskCanceled.Ptr(), }, { EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), }, } output = s.msBuilder.trimEventsAfterWorkflowClose(input) s.Equal(input, output) input = []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskCanceled.Ptr(), }, { EventType: types.EventTypeWorkflowExecutionCompleted.Ptr(), }, } output = s.msBuilder.trimEventsAfterWorkflowClose(input) s.Equal(input, output) input = []*types.HistoryEvent{ { EventType: types.EventTypeWorkflowExecutionCompleted.Ptr(), }, { EventType: types.EventTypeActivityTaskCanceled.Ptr(), }, } output = s.msBuilder.trimEventsAfterWorkflowClose(input) s.Equal([]*types.HistoryEvent{ { EventType: types.EventTypeWorkflowExecutionCompleted.Ptr(), }, }, output) } func (s *mutableStateSuite) TestMergeMapOfByteArray() { var currentMap map[string][]byte var newMap map[string][]byte resultMap := mergeMapOfByteArray(currentMap, newMap) s.Equal(make(map[string][]byte), resultMap) newMap = map[string][]byte{"key": []byte("val")} resultMap = mergeMapOfByteArray(currentMap, newMap) s.Equal(newMap, resultMap) currentMap = map[string][]byte{"number": []byte("1")} resultMap = mergeMapOfByteArray(currentMap, newMap) s.Equal(2, len(resultMap)) } func (s *mutableStateSuite) TestEventReapplied() { runID := uuid.New() eventID := int64(1) version := int64(2) dedupResource := definition.NewEventReappliedID(runID, eventID, version) isReapplied := s.msBuilder.IsResourceDuplicated(dedupResource) s.False(isReapplied) s.msBuilder.UpdateDuplicatedResource(dedupResource) isReapplied = s.msBuilder.IsResourceDuplicated(dedupResource) s.True(isReapplied) } func (s *mutableStateSuite) TestTransientDecisionTaskSchedule_CurrentVersionChanged() { version := int64(2000) runID := uuid.New() s.msBuilder = NewMutableStateBuilderWithVersionHistoriesWithEventV2( s.mockShard, s.logger, version, runID, constants.TestGlobalDomainEntry, ).(*mutableStateBuilder) decisionScheduleEvent, decisionStartedEvent := s.prepareTransientDecisionCompletionFirstBatchReplicated(version, runID) decisionFailedEvent := &types.HistoryEvent{ Version: version, ID: 3, Timestamp: common.Int64Ptr(time.Now().UnixNano()), EventType: types.EventTypeDecisionTaskFailed.Ptr(), DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ ScheduledEventID: decisionScheduleEvent.ID, StartedEventID: decisionStartedEvent.ID, }, } err := s.msBuilder.ReplicateDecisionTaskFailedEvent(decisionFailedEvent) s.NoError(err) err = s.msBuilder.UpdateCurrentVersion(version+1, true) s.NoError(err) versionHistories := s.msBuilder.GetVersionHistories() versionHistory, err := versionHistories.GetCurrentVersionHistory() s.NoError(err) versionHistory.AddOrUpdateItem(&persistence.VersionHistoryItem{ EventID: 3, Version: version, }) now := time.Now() di, err := s.msBuilder.AddDecisionTaskScheduledEventAsHeartbeat(true, now.UnixNano()) s.NoError(err) s.NotNil(di) s.Equal(int64(0), s.msBuilder.GetExecutionInfo().DecisionAttempt) s.Equal(0, len(s.msBuilder.GetHistoryBuilder().transientHistory)) s.Equal(1, len(s.msBuilder.GetHistoryBuilder().history)) } func (s *mutableStateSuite) TestTransientDecisionTaskStart_CurrentVersionChanged() { version := int64(2000) runID := uuid.New() s.msBuilder = NewMutableStateBuilderWithVersionHistoriesWithEventV2( s.mockShard, s.logger, version, runID, constants.TestGlobalDomainEntry, ).(*mutableStateBuilder) decisionScheduleEvent, decisionStartedEvent := s.prepareTransientDecisionCompletionFirstBatchReplicated(version, runID) decisionFailedEvent := &types.HistoryEvent{ Version: version, ID: 3, Timestamp: common.Int64Ptr(time.Now().UnixNano()), EventType: types.EventTypeDecisionTaskFailed.Ptr(), DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ ScheduledEventID: decisionScheduleEvent.ID, StartedEventID: decisionStartedEvent.ID, }, } err := s.msBuilder.ReplicateDecisionTaskFailedEvent(decisionFailedEvent) s.NoError(err) decisionScheduleID := int64(4) now := time.Now() tasklist := "some random tasklist" decisionTimeoutSecond := int32(11) decisionAttempt := int64(2) newDecisionScheduleEvent := &types.HistoryEvent{ Version: version, ID: decisionScheduleID, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(decisionTimeoutSecond), Attempt: decisionAttempt, }, } di, err := s.msBuilder.ReplicateDecisionTaskScheduledEvent( newDecisionScheduleEvent.Version, newDecisionScheduleEvent.ID, newDecisionScheduleEvent.DecisionTaskScheduledEventAttributes.TaskList.GetName(), newDecisionScheduleEvent.DecisionTaskScheduledEventAttributes.GetStartToCloseTimeoutSeconds(), newDecisionScheduleEvent.DecisionTaskScheduledEventAttributes.GetAttempt(), 0, 0, false, ) s.NoError(err) s.NotNil(di) err = s.msBuilder.UpdateCurrentVersion(version+1, true) s.NoError(err) versionHistories := s.msBuilder.GetVersionHistories() versionHistory, err := versionHistories.GetCurrentVersionHistory() s.NoError(err) versionHistory.AddOrUpdateItem(&persistence.VersionHistoryItem{ EventID: 3, Version: version, }) _, _, err = s.msBuilder.AddDecisionTaskStartedEvent( decisionScheduleID, uuid.New(), &types.PollForDecisionTaskRequest{ Identity: IdentityHistoryService, }, ) s.NoError(err) s.Equal(0, len(s.msBuilder.GetHistoryBuilder().transientHistory)) s.Equal(2, len(s.msBuilder.GetHistoryBuilder().history)) } func (s *mutableStateSuite) prepareTransientDecisionCompletionFirstBatchReplicated(version int64, runID string) (*types.HistoryEvent, *types.HistoryEvent) { domainID := constants.TestDomainID execution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: runID, } now := time.Now() workflowType := "some random workflow type" tasklist := "some random tasklist" workflowTimeoutSecond := int32(222) decisionTimeoutSecond := int32(11) decisionAttempt := int64(0) partitionConfig := map[string]string{ "zone": "dca", } eventID := int64(1) workflowStartEvent := &types.HistoryEvent{ Version: version, ID: eventID, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: tasklist}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(workflowTimeoutSecond), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(decisionTimeoutSecond), PartitionConfig: partitionConfig, }, } eventID++ decisionScheduleEvent := &types.HistoryEvent{ Version: version, ID: eventID, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(decisionTimeoutSecond), Attempt: decisionAttempt, }, } eventID++ decisionStartedEvent := &types.HistoryEvent{ Version: version, ID: eventID, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: decisionScheduleEvent.ID, RequestID: uuid.New(), }, } eventID++ decisionFailedEvent := &types.HistoryEvent{ Version: version, ID: eventID, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeDecisionTaskFailed.Ptr(), DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ ScheduledEventID: decisionScheduleEvent.ID, StartedEventID: decisionStartedEvent.ID, }, } eventID++ s.mockEventsCache.EXPECT().PutEvent( domainID, execution.GetWorkflowID(), execution.GetRunID(), workflowStartEvent.ID, workflowStartEvent, ).Times(1) err := s.msBuilder.ReplicateWorkflowExecutionStartedEvent( nil, execution, uuid.New(), workflowStartEvent, false, ) s.Nil(err) // setup transient decision di, err := s.msBuilder.ReplicateDecisionTaskScheduledEvent( decisionScheduleEvent.Version, decisionScheduleEvent.ID, decisionScheduleEvent.DecisionTaskScheduledEventAttributes.TaskList.GetName(), decisionScheduleEvent.DecisionTaskScheduledEventAttributes.GetStartToCloseTimeoutSeconds(), decisionScheduleEvent.DecisionTaskScheduledEventAttributes.GetAttempt(), 0, 0, false, ) s.Nil(err) s.NotNil(di) di, err = s.msBuilder.ReplicateDecisionTaskStartedEvent(nil, decisionStartedEvent.Version, decisionScheduleEvent.ID, decisionStartedEvent.ID, decisionStartedEvent.DecisionTaskStartedEventAttributes.GetRequestID(), decisionStartedEvent.GetTimestamp(), ) s.Nil(err) s.NotNil(di) err = s.msBuilder.ReplicateDecisionTaskFailedEvent(decisionFailedEvent) s.Nil(err) decisionAttempt = int64(123) newDecisionScheduleEvent := &types.HistoryEvent{ Version: version, ID: eventID, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(decisionTimeoutSecond), Attempt: decisionAttempt, }, } eventID++ newDecisionStartedEvent := &types.HistoryEvent{ Version: version, ID: eventID, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: decisionScheduleEvent.ID, RequestID: uuid.New(), }, } eventID++ //nolint:ineffassign di, err = s.msBuilder.ReplicateDecisionTaskScheduledEvent( newDecisionScheduleEvent.Version, newDecisionScheduleEvent.ID, newDecisionScheduleEvent.DecisionTaskScheduledEventAttributes.TaskList.GetName(), newDecisionScheduleEvent.DecisionTaskScheduledEventAttributes.GetStartToCloseTimeoutSeconds(), newDecisionScheduleEvent.DecisionTaskScheduledEventAttributes.GetAttempt(), 0, 0, false, ) s.Nil(err) s.NotNil(di) di, err = s.msBuilder.ReplicateDecisionTaskStartedEvent(nil, newDecisionStartedEvent.Version, newDecisionScheduleEvent.ID, newDecisionStartedEvent.ID, newDecisionStartedEvent.DecisionTaskStartedEventAttributes.GetRequestID(), newDecisionStartedEvent.GetTimestamp(), ) s.Nil(err) s.NotNil(di) return newDecisionScheduleEvent, newDecisionStartedEvent } func (s *mutableStateSuite) TestLoad_BackwardsCompatibility() { mutableState := buildWorkflowMutableState() s.msBuilder.Load(context.Background(), mutableState) s.Equal(constants.TestDomainID, s.msBuilder.pendingChildExecutionInfoIDs[81].DomainID) } func (s *mutableStateSuite) TestUpdateCurrentVersion_WorkflowOpen() { mutableState := buildWorkflowMutableState() s.msBuilder.Load(context.Background(), mutableState) s.Equal(commonconstants.EmptyVersion, s.msBuilder.GetCurrentVersion()) version := int64(2000) s.msBuilder.UpdateCurrentVersion(version, false) s.Equal(version, s.msBuilder.GetCurrentVersion()) } func (s *mutableStateSuite) TestUpdateCurrentVersion_WorkflowClosed() { mutableState := buildWorkflowMutableState() mutableState.ExecutionInfo.State = persistence.WorkflowStateCompleted mutableState.ExecutionInfo.CloseStatus = persistence.WorkflowCloseStatusCompleted s.msBuilder.Load(context.Background(), mutableState) s.Equal(commonconstants.EmptyVersion, s.msBuilder.GetCurrentVersion()) versionHistory, err := mutableState.VersionHistories.GetCurrentVersionHistory() s.NoError(err) lastItem, err := versionHistory.GetLastItem() s.NoError(err) lastWriteVersion := lastItem.Version version := int64(2000) s.msBuilder.UpdateCurrentVersion(version, false) s.Equal(lastWriteVersion, s.msBuilder.GetCurrentVersion()) } func (s *mutableStateSuite) newDomainCacheEntry() *cache.DomainCacheEntry { return cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "mutableStateTest"}, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{}, 1, nil, 0, 0, 0, ) } func buildWorkflowMutableState() *persistence.WorkflowMutableState { domainID := constants.TestDomainID we := types.WorkflowExecution{ WorkflowID: "wId", RunID: constants.TestRunID, } tl := "testTaskList" failoverVersion := int64(300) partitionConfig := map[string]string{ "zone": "phx", } info := &persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: we.GetWorkflowID(), RunID: we.GetRunID(), TaskList: tl, WorkflowTypeName: "wType", WorkflowTimeout: 200, DecisionStartToCloseTimeout: 100, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, NextEventID: int64(101), LastProcessedEvent: int64(99), LastUpdatedTimestamp: time.Now(), DecisionVersion: failoverVersion, DecisionScheduleID: commonconstants.EmptyEventID, DecisionStartedID: commonconstants.EmptyEventID, DecisionTimeout: 100, PartitionConfig: partitionConfig, } activityInfos := map[int64]*persistence.ActivityInfo{ 5: { Version: failoverVersion, ScheduleID: int64(5), ScheduledTime: time.Now(), StartedID: commonconstants.EmptyEventID, StartedTime: time.Now(), ActivityID: "activityID_5", ScheduleToStartTimeout: 100, ScheduleToCloseTimeout: 200, StartToCloseTimeout: 300, HeartbeatTimeout: 50, }, } timerInfos := map[string]*persistence.TimerInfo{ "25": { Version: failoverVersion, TimerID: "25", StartedID: 85, ExpiryTime: time.Now().Add(time.Hour), }, } childInfos := map[int64]*persistence.ChildExecutionInfo{ 80: { Version: failoverVersion, InitiatedID: 80, InitiatedEventBatchID: 20, InitiatedEvent: &types.HistoryEvent{}, StartedID: commonconstants.EmptyEventID, CreateRequestID: uuid.New(), DomainID: constants.TestDomainID, WorkflowTypeName: "code.uber.internal/test/foobar", }, 81: { Version: failoverVersion, InitiatedID: 80, InitiatedEventBatchID: 20, InitiatedEvent: &types.HistoryEvent{}, StartedID: commonconstants.EmptyEventID, CreateRequestID: uuid.New(), DomainNameDEPRECATED: constants.TestDomainName, WorkflowTypeName: "code.uber.internal/test/foobar", }, } signalInfos := map[int64]*persistence.SignalInfo{ 75: { Version: failoverVersion, InitiatedID: 75, InitiatedEventBatchID: 17, SignalRequestID: uuid.New(), SignalName: "test-signal-75", Input: []byte("signal-input-75"), }, } signalRequestIDs := map[string]struct{}{ uuid.New(): {}, } bufferedEvents := []*types.HistoryEvent{ { ID: commonconstants.BufferedEventID, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), Version: failoverVersion, WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "test-signal-buffered", Input: []byte("test-signal-buffered-input"), }, }, } versionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("token#1"), Items: []*persistence.VersionHistoryItem{ {EventID: 1, Version: 300}, }, }, }, } return &persistence.WorkflowMutableState{ ExecutionInfo: info, ActivityInfos: activityInfos, TimerInfos: timerInfos, ChildExecutionInfos: childInfos, SignalInfos: signalInfos, SignalRequestedIDs: signalRequestIDs, BufferedEvents: bufferedEvents, VersionHistories: versionHistories, } } func TestNewMutableStateBuilderWithEventV2(t *testing.T) { ctrl := gomock.NewController(t) mockShard := shard.NewTestContext( t, ctrl, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) domainCache := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "mutableStateTest"}, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{}, 1, nil, 0, 0, 0, ) NewMutableStateBuilderWithEventV2(mockShard, log.NewNoop(), "A82146B5-7A5C-4660-9195-E80E5161EC56", domainCache) } var ( domainID = "A6338800-D143-4FEF-8A49-9BBB31386C5F" wfID = "879A361B-B435-491D-8A3B-ACF3BAD30F4B" runID = "81DFCB6B-ACD4-46D1-89C2-804388203880" ts1 = int64(1234) shardID = 123 ) // Guiding real data example: ie: // `select execution from executions where run_id = ALLOW FILTERING;` // // executions.execution { // domainID: "A6338800-D143-4FEF-8A49-9BBB31386C5F", // wfID: "879A361B-B435-491D-8A3B-ACF3BAD30F4B", // runID: "81DFCB6B-ACD4-46D1-89C2-804388203880", // initiated_id: -7, // completion_event: null, // state: 2, // close_status: 1, // next_event_id: 12, // last_processed_event: 9, // decision_schedule_id: -23, // decision_started_id: -23, // last_first_event_id: 10, // decision_version: -24, // completion_event_batch_id: 10, // last_event_task_id: 4194328, // } var exampleMutableStateForClosedWF = &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{ WorkflowID: wfID, DomainID: domainID, RunID: runID, NextEventID: 12, State: persistence.WorkflowStateCompleted, CompletionEventBatchID: 10, BranchToken: []byte("branch-token"), }, } var exampleCompletionEvent = &types.HistoryEvent{ ID: 11, TaskID: 4194328, Version: 1, Timestamp: &ts1, WorkflowExecutionCompletedEventAttributes: &types.WorkflowExecutionCompletedEventAttributes{ Result: []byte("some random workflow completion result"), DecisionTaskCompletedEventID: 10, }, } var exampleStartEvent = &types.HistoryEvent{ ID: 1, TaskID: 4194328, Version: 1, Timestamp: &ts1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: "workflow-type"}, TaskList: &types.TaskList{Name: "tasklist"}, Input: []byte("some random workflow input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), OriginalExecutionRunID: runID, Identity: "123@some-hostname@@uuid", }, } func TestGetCompletionEvent(t *testing.T) { tests := map[string]struct { currentState *mutableStateBuilder historyManagerAffordance func(historyManager *persistence.MockHistoryManager) expectedResult *types.HistoryEvent expectedErr error }{ "Getting a completed event from a normal, completed workflow - taken from a real example": { currentState: exampleMutableStateForClosedWF, historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) { historyManager.EXPECT().ReadHistoryBranch(gomock.Any(), &persistence.ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), MinEventID: 10, MaxEventID: 12, // nextEventID +1 PageSize: 1, NextPageToken: nil, ShardID: common.IntPtr(shardID), DomainName: "domain", }).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ exampleCompletionEvent, }, }, nil) }, expectedResult: exampleCompletionEvent, }, "An unexpected error while fetchhing history, such as not found err": { currentState: &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{ WorkflowID: wfID, DomainID: domainID, RunID: runID, NextEventID: 12, State: persistence.WorkflowStateCompleted, CompletionEventBatchID: 10, BranchToken: []byte("branch-token"), }, }, historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) { historyManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(nil, errors.New("a transient random error")) }, expectedResult: nil, expectedErr: &types.InternalServiceError{Message: "unable to get workflow completion event"}, }, "A 'transient' internal service error, this should be returned to the caller": { currentState: &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{ WorkflowID: wfID, DomainID: domainID, RunID: runID, NextEventID: 12, State: persistence.WorkflowStateCompleted, CompletionEventBatchID: 10, BranchToken: []byte("branch-token"), }, }, historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) { historyManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()). Return(nil, &types.InternalServiceError{Message: "an err"}) }, expectedResult: nil, expectedErr: &types.InternalServiceError{Message: "an err"}, }, "initial validation: An invalid starting mutable state should return an error": { currentState: &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{}, }, historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) {}, expectedResult: nil, expectedErr: &types.InternalServiceError{Message: "unable to get workflow completion event"}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) shardContext := shardCtx.NewMockContext(ctrl) shardContext.EXPECT().GetShardID().Return(123).AnyTimes() // this isn't called on a few of the validation failures historyManager := persistence.NewMockHistoryManager(ctrl) td.historyManagerAffordance(historyManager) domainCache := cache.NewMockDomainCache(ctrl) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("domain", nil).AnyTimes() // this isn't called on validation td.currentState.eventsCache = events.NewCache(shardID, historyManager, config.NewForTest(), log.NewNoop(), metrics.NewNoopMetricsClient(), domainCache) td.currentState.shard = shardContext res, err := td.currentState.GetCompletionEvent(context.Background()) assert.Equal(t, td.expectedResult, res) if td.expectedErr != nil { assert.ErrorAs(t, td.expectedErr, &err) } }) } } func TestGetStartEvent(t *testing.T) { tests := map[string]struct { currentState *mutableStateBuilder historyManagerAffordance func(historyManager *persistence.MockHistoryManager) expectedResult *types.HistoryEvent expectedErr error }{ "Getting a start event from a normal, completed workflow - taken from a real example": { currentState: exampleMutableStateForClosedWF, historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) { historyManager.EXPECT().ReadHistoryBranch(gomock.Any(), &persistence.ReadHistoryBranchRequest{ BranchToken: []byte("branch-token"), MinEventID: 1, MaxEventID: 2, PageSize: 1, NextPageToken: nil, ShardID: common.IntPtr(shardID), DomainName: "domain", }).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{ exampleStartEvent, }, }, nil) }, expectedResult: exampleStartEvent, }, "Getting a start event but hitting an error when reaching into history": { currentState: exampleMutableStateForClosedWF, historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) { historyManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(nil, errors.New("an error")) }, expectedErr: types.InternalServiceError{Message: "unable to get workflow start event"}, }, "Getting a start event but hitting a 'transient' error when reaching into history. This should be passed back up the call stack": { currentState: exampleMutableStateForClosedWF, historyManagerAffordance: func(historyManager *persistence.MockHistoryManager) { historyManager.EXPECT().ReadHistoryBranch(gomock.Any(), gomock.Any()).Return(nil, &types.InternalServiceError{Message: "an error"}) }, expectedErr: types.InternalServiceError{Message: "an error"}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) shardContext := shardCtx.NewMockContext(ctrl) shardContext.EXPECT().GetShardID().Return(123).AnyTimes() // this isn't called on a few of the validation failures historyManager := persistence.NewMockHistoryManager(ctrl) td.historyManagerAffordance(historyManager) domainCache := cache.NewMockDomainCache(ctrl) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("domain", nil).AnyTimes() // this isn't called on validation td.currentState.eventsCache = events.NewCache(shardID, historyManager, config.NewForTest(), log.NewNoop(), metrics.NewNoopMetricsClient(), domainCache) td.currentState.shard = shardContext res, err := td.currentState.GetStartEvent(context.Background()) assert.Equal(t, td.expectedResult, res) if td.expectedErr != nil { assert.ErrorAs(t, err, &td.expectedErr) } }) } } func TestLoggingNilAndInvalidHandling(t *testing.T) { gen := testdatagen.New(t) executionInfo := persistence.WorkflowExecutionInfo{} gen.Fuzz(&executionInfo) msb := mutableStateBuilder{ executionInfo: &executionInfo, logger: log.NewNoop(), metricsClient: metrics.NewNoopMetricsClient(), } msbInvalid := mutableStateBuilder{logger: log.NewNoop()} assert.NotPanics(t, func() { msbInvalid.logWarn("test", tag.WorkflowDomainID("test")) msbInvalid.logError("test", tag.WorkflowDomainID("test")) msbInvalid.logInfo("test", tag.WorkflowDomainID("test")) msb.logWarn("test", tag.WorkflowDomainID("test")) msb.logError("test", tag.WorkflowDomainID("test")) msb.logInfo("test", tag.WorkflowDomainID("test")) msb.logDataInconsistency() }) } func TestAssignEventIDToBufferedEvents(t *testing.T) { tests := map[string]struct { startingEventID int64 pendingActivityInfo map[int64]*persistence.ActivityInfo pendingChildExecutionInfoIDs map[int64]*persistence.ChildExecutionInfo startingHistoryEntries []*types.HistoryEvent expectedUpdateActivityInfos map[int64]*persistence.ActivityInfo expectedEndingHistoryEntries []*types.HistoryEvent expectedNextEventID int64 expectedUpdateChildExecutionInfos map[int64]*persistence.ChildExecutionInfo }{ "Timer Fired - this should increment the nextevent ID counter": { startingEventID: 12, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeTimerFired.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "1", StartedEventID: 11, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeTimerFired.Ptr(), ID: 12, TaskID: commonconstants.EmptyEventTaskID, TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "1", StartedEventID: 11, }, }, }, expectedNextEventID: 13, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, "Activity completed and started - this should update any buffered activities": { startingEventID: 6, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeDecisionTaskCompleted.Ptr(), ID: 4, TaskID: commonconstants.EmptyEventTaskID, DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 2, StartedEventID: 3, }, }, { EventType: types.EventTypeActivityTaskScheduled.Ptr(), ID: 5, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityID: "0", }, }, { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 5, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeDecisionTaskCompleted.Ptr(), ID: 4, TaskID: commonconstants.EmptyEventTaskID, DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: 2, StartedEventID: 3, }, }, { EventType: types.EventTypeActivityTaskScheduled.Ptr(), ID: 5, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityID: "0", }, }, { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 5, }, }, }, expectedNextEventID: 7, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, "Activity task started and a pending activity is updated - this should be put to the updatedActivityInfos map with all the other counters incremented": { startingEventID: 6, pendingActivityInfo: map[int64]*persistence.ActivityInfo{ 5: { ScheduleID: 5, StartedID: 6, }, }, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 5, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 5, }, }, }, expectedNextEventID: 7, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{ 5: { ScheduleID: 5, StartedID: 6, }, }, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, "Activity task started and then completed": { startingEventID: 6, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 3456, }, }, { EventType: types.EventTypeActivityTaskCompleted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskCompletedEventAttributes: &types.ActivityTaskCompletedEventAttributes{ StartedEventID: 4567, ScheduledEventID: 3456, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 3456, }, }, { EventType: types.EventTypeActivityTaskCompleted.Ptr(), ID: 7, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskCompletedEventAttributes: &types.ActivityTaskCompletedEventAttributes{ StartedEventID: 6, ScheduledEventID: 3456, }, }, }, expectedNextEventID: 8, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, "Activity task started and then Cancelled": { startingEventID: 6, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 3456, }, }, { EventType: types.EventTypeActivityTaskCanceled.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskCanceledEventAttributes: &types.ActivityTaskCanceledEventAttributes{ StartedEventID: 123, ScheduledEventID: 3456, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 3456, }, }, { EventType: types.EventTypeActivityTaskCanceled.Ptr(), ID: 7, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskCanceledEventAttributes: &types.ActivityTaskCanceledEventAttributes{ StartedEventID: 6, ScheduledEventID: 3456, }, }, }, expectedNextEventID: 8, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, "Activity task started and then failed": { startingEventID: 6, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 3456, }, }, { EventType: types.EventTypeActivityTaskFailed.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ StartedEventID: 123, ScheduledEventID: 3456, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 3456, }, }, { EventType: types.EventTypeActivityTaskFailed.Ptr(), ID: 7, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ StartedEventID: 6, ScheduledEventID: 3456, }, }, }, expectedNextEventID: 8, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, "Activity task started and then timed out": { startingEventID: 6, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 3456, }, }, { EventType: types.EventTypeActivityTaskTimedOut.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ StartedEventID: 123, ScheduledEventID: 3456, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeActivityTaskStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 3456, }, }, { EventType: types.EventTypeActivityTaskTimedOut.Ptr(), ID: 7, TaskID: commonconstants.EmptyEventTaskID, ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ StartedEventID: 6, ScheduledEventID: 3456, }, }, }, expectedNextEventID: 8, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, "Child workflow scheduled and then completed": { startingEventID: 6, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 123, }, }, { EventType: types.EventTypeChildWorkflowExecutionCompleted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionCompletedEventAttributes: &types.ChildWorkflowExecutionCompletedEventAttributes{ StartedEventID: 123, InitiatedEventID: 2345, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 123, }, }, { EventType: types.EventTypeChildWorkflowExecutionCompleted.Ptr(), ID: 7, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionCompletedEventAttributes: &types.ChildWorkflowExecutionCompletedEventAttributes{ StartedEventID: 123, InitiatedEventID: 2345, }, }, }, expectedNextEventID: 8, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, "Child workflow scheduled and then Cancelled - where there is a pending execution that requires an update": { startingEventID: 6, pendingChildExecutionInfoIDs: map[int64]*persistence.ChildExecutionInfo{ 123: { InitiatedID: 321, }, }, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 123, }, }, { EventType: types.EventTypeChildWorkflowExecutionCanceled.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionCanceledEventAttributes: &types.ChildWorkflowExecutionCanceledEventAttributes{ StartedEventID: 321, InitiatedEventID: 123, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 123, }, }, { EventType: types.EventTypeChildWorkflowExecutionCanceled.Ptr(), ID: 7, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionCanceledEventAttributes: &types.ChildWorkflowExecutionCanceledEventAttributes{ StartedEventID: 6, InitiatedEventID: 123, }, }, }, expectedNextEventID: 8, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{ 321: { InitiatedID: 321, StartedID: 6, }, }, }, "Child workflow scheduled and then Failed": { startingEventID: 6, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 123, }, }, { EventType: types.EventTypeChildWorkflowExecutionFailed.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionFailedEventAttributes: &types.ChildWorkflowExecutionFailedEventAttributes{ StartedEventID: 123, InitiatedEventID: 2345, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 123, }, }, { EventType: types.EventTypeChildWorkflowExecutionFailed.Ptr().Ptr(), ID: 7, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionFailedEventAttributes: &types.ChildWorkflowExecutionFailedEventAttributes{ StartedEventID: 123, InitiatedEventID: 2345, }, }, }, expectedNextEventID: 8, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, "Child workflow scheduled and then Timed out": { startingEventID: 6, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 123, }, }, { EventType: types.EventTypeChildWorkflowExecutionTimedOut.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionTimedOutEventAttributes: &types.ChildWorkflowExecutionTimedOutEventAttributes{ StartedEventID: 123, InitiatedEventID: 2345, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 123, }, }, { EventType: types.EventTypeChildWorkflowExecutionTimedOut.Ptr(), ID: 7, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionTimedOutEventAttributes: &types.ChildWorkflowExecutionTimedOutEventAttributes{ StartedEventID: 123, InitiatedEventID: 2345, }, }, }, expectedNextEventID: 8, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, "Child workflow scheduled and then Terminated": { startingEventID: 6, startingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 123, }, }, { EventType: types.EventTypeChildWorkflowExecutionTerminated.Ptr(), ID: commonconstants.BufferedEventID, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionTerminatedEventAttributes: &types.ChildWorkflowExecutionTerminatedEventAttributes{ StartedEventID: 123, InitiatedEventID: 2345, }, }, }, expectedEndingHistoryEntries: []*types.HistoryEvent{ { EventType: types.EventTypeChildWorkflowExecutionStarted.Ptr(), ID: 6, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 123, }, }, { EventType: types.EventTypeChildWorkflowExecutionTerminated.Ptr(), ID: 7, TaskID: commonconstants.EmptyEventTaskID, ChildWorkflowExecutionTerminatedEventAttributes: &types.ChildWorkflowExecutionTerminatedEventAttributes{ StartedEventID: 123, InitiatedEventID: 2345, }, }, }, expectedNextEventID: 8, expectedUpdateActivityInfos: map[int64]*persistence.ActivityInfo{}, expectedUpdateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { msb := &mutableStateBuilder{ pendingChildExecutionInfoIDs: td.pendingChildExecutionInfoIDs, pendingActivityInfoIDs: td.pendingActivityInfo, executionInfo: &persistence.WorkflowExecutionInfo{ NextEventID: td.startingEventID, }, hBuilder: &HistoryBuilder{ history: td.startingHistoryEntries, }, updateActivityInfos: make(map[int64]*persistence.ActivityInfo), updateChildExecutionInfos: make(map[int64]*persistence.ChildExecutionInfo), } msb.assignEventIDToBufferedEvents() assert.Equal(t, td.expectedEndingHistoryEntries, msb.hBuilder.history) assert.Equal(t, td.expectedNextEventID, msb.executionInfo.NextEventID) assert.Equal(t, td.expectedUpdateActivityInfos, msb.updateActivityInfos) assert.Equal(t, td.expectedUpdateChildExecutionInfos, msb.updateChildExecutionInfos) }) } } // This is only for passing the coverage func TestLog(t *testing.T) { var e *mutableStateBuilder assert.NotPanics(t, func() { e.logInfo("a") }) assert.NotPanics(t, func() { e.logWarn("a") }) assert.NotPanics(t, func() { e.logError("a") }) } func TestMutableStateBuilder_CopyToPersistence_roundtrip(t *testing.T) { for i := 0; i <= 100; i++ { ctrl := gomock.NewController(t) seed := int64(rand.Int()) fuzzer := testdatagen.NewWithNilChance(t, seed, 0) execution := &persistence.WorkflowMutableState{} fuzzer.Fuzz(&execution) // checksum is a calculated value, zero it out because // it'll be overwridden during the constructor setup execution.Checksum = checksum.Checksum{} shardContext := shard.NewMockContext(ctrl) mockCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return("some-domain-id", nil).AnyTimes() shardContext.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata).Times(2) shardContext.EXPECT().GetEventsCache().Return(mockCache) shardContext.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 2, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, MutableStateChecksumInvalidateBefore: dynamicproperties.GetFloatPropertyFn(10), MutableStateChecksumVerifyProbability: dynamicproperties.GetIntPropertyFilteredByDomain(0.0), HostName: "test-host", }).Times(1) shardContext.EXPECT().GetTimeSource().Return(clock.NewMockedTimeSource()) shardContext.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()) shardContext.EXPECT().GetDomainCache().Return(mockDomainCache).AnyTimes() shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() activeClusterManager := activecluster.NewMockManager(ctrl) shardContext.EXPECT().GetActiveClusterManager().Return(activeClusterManager).AnyTimes() msb := newMutableStateBuilder(shardContext, log.NewNoop(), constants.TestGlobalDomainEntry, constants.TestGlobalDomainEntry.GetFailoverVersion()) msb.Load(context.Background(), execution) out := msb.CopyToPersistence() assert.Equal(t, execution.ActivityInfos, out.ActivityInfos, "activityinfos mismatch") assert.Equal(t, execution.TimerInfos, out.TimerInfos, "timerinfos mismatch") assert.Equal(t, execution.ChildExecutionInfos, out.ChildExecutionInfos, "child executino info mismatches") assert.Equal(t, execution.RequestCancelInfos, out.RequestCancelInfos, "request cancellantion info mismatches") assert.Equal(t, execution.SignalInfos, out.SignalInfos, "signal info mismatches") assert.Equal(t, execution.SignalRequestedIDs, out.SignalRequestedIDs, "signal request ids mismaches") assert.Equal(t, execution.ExecutionInfo, out.ExecutionInfo, "execution info mismatches") assert.Equal(t, execution.BufferedEvents, out.BufferedEvents, "buffered events mismatch") assert.Equal(t, execution.VersionHistories, out.VersionHistories, "version histories") assert.Equal(t, execution.Checksum, out.Checksum, "checksum mismatch") assert.Equal(t, execution.ReplicationState, out.ReplicationState, "replication state mismatch") assert.Equal(t, execution.ExecutionStats, out.ExecutionStats, "execution stats mismatch") assert.Equal(t, execution, out) } } func TestMutableStateBuilder_closeTransactionHandleWorkflowReset(t *testing.T) { t1 := time.Unix(123, 0) now := time.Unix(500, 0) badBinaryID := "bad-binary-id" mockDomainEntryWithBadBinary := cache.NewLocalDomainCacheEntryForTest(&persistence.DomainInfo{Name: "domain"}, &persistence.DomainConfig{ BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ badBinaryID: &types.BadBinaryInfo{ Reason: "some-reason", Operator: "", CreatedTimeNano: common.Ptr(t1.UnixNano()), }, }, }, }, "cluster0") mockDomainEntryWithoutBadBinary := cache.NewLocalDomainCacheEntryForTest(nil, &persistence.DomainConfig{ BadBinaries: types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{}, }, }, "cluster0") tests := map[string]struct { policyIn TransactionPolicy shardContextExpectations func(mockCache *events.MockCache, shard *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) mutableStateBuilderStartingState func(m *mutableStateBuilder) expectedEndState func(t *testing.T, m *mutableStateBuilder) expectedErr error }{ "a workflow with reset point which is running - the expectation is that this should be able to successfully find the domain to reset and add a transfer task": { policyIn: TransactionPolicyActive, mutableStateBuilderStartingState: func(m *mutableStateBuilder) { // the workflow's running m.executionInfo = &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusNone, DomainID: "some-domain-id", WorkflowID: "wf-id", AutoResetPoints: &types.ResetPoints{ Points: []*types.ResetPointInfo{ { BinaryChecksum: badBinaryID, RunID: "", FirstDecisionCompletedID: 0, CreatedTimeNano: common.Ptr(t1.UnixNano()), ExpiringTimeNano: nil, Resettable: true, }, }, }, } }, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GetDomainCache().Return(mockDomainCache).Times(1) mockDomainCache.EXPECT().GetDomainByID("some-domain-id").Return(mockDomainEntryWithBadBinary, nil) }, expectedEndState: func(t *testing.T, m *mutableStateBuilder) { assert.Equal(t, []persistence.Task{ &persistence.ResetWorkflowTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some-domain-id", WorkflowID: "wf-id", }, TaskData: persistence.TaskData{ Version: commonconstants.EmptyVersion, }, }, }, m.insertTransferTasks) }, }, "a workflow with reset point which is running for a domain without a bad binary - the expectation is this will not add any transfer tasks": { policyIn: TransactionPolicyActive, mutableStateBuilderStartingState: func(m *mutableStateBuilder) { // the workflow's running m.executionInfo = &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusNone, DomainID: "some-domain-id", WorkflowID: "wf-id", AutoResetPoints: &types.ResetPoints{ Points: []*types.ResetPointInfo{ { BinaryChecksum: badBinaryID, RunID: "", FirstDecisionCompletedID: 0, CreatedTimeNano: common.Ptr(t1.UnixNano()), ExpiringTimeNano: nil, Resettable: true, }, }, }, } }, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GetDomainCache().Return(mockDomainCache).Times(1) mockDomainCache.EXPECT().GetDomainByID("some-domain-id").Return(mockDomainEntryWithoutBadBinary, nil) }, expectedEndState: func(t *testing.T, m *mutableStateBuilder) { assert.Equal(t, []persistence.Task(nil), m.insertTransferTasks) }, }, "a workflow withithout auto-reset point which is running for a domain": { policyIn: TransactionPolicyActive, mutableStateBuilderStartingState: func(m *mutableStateBuilder) { // the workflow's running m.executionInfo = &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusNone, DomainID: "some-domain-id", WorkflowID: "wf-id", } }, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GetDomainCache().Return(mockDomainCache).Times(1) mockDomainCache.EXPECT().GetDomainByID("some-domain-id").Return(mockDomainEntryWithoutBadBinary, nil) }, expectedEndState: func(t *testing.T, m *mutableStateBuilder) { assert.Equal(t, []persistence.Task(nil), m.insertTransferTasks) }, }, "a workflow with reset point which is running but which has child workflows": { policyIn: TransactionPolicyActive, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { }, mutableStateBuilderStartingState: func(m *mutableStateBuilder) { // the workflow's running m.executionInfo = &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusNone, } // there's some child workflow that's due to be updated m.pendingChildExecutionInfoIDs = map[int64]*persistence.ChildExecutionInfo{ 1: &persistence.ChildExecutionInfo{}, } }, expectedEndState: func(t *testing.T, m *mutableStateBuilder) { assert.Equal(t, []persistence.Task(nil), m.insertTransferTasks) }, }, "Transaction policy passive - no expected resets": { policyIn: TransactionPolicyPassive, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { }, mutableStateBuilderStartingState: func(m *mutableStateBuilder) { // the workflow's running m.executionInfo = &persistence.WorkflowExecutionInfo{ CloseStatus: persistence.WorkflowCloseStatusNone, } // there's some child workflow that's due to be updated m.pendingChildExecutionInfoIDs = map[int64]*persistence.ChildExecutionInfo{ 1: &persistence.ChildExecutionInfo{}, } }, expectedEndState: func(t *testing.T, m *mutableStateBuilder) { assert.Equal(t, []persistence.Task(nil), m.insertTransferTasks) }, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) shardContext := shard.NewMockContext(ctrl) mockCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() td.shardContextExpectations(mockCache, shardContext, mockDomainCache) nowClock := clock.NewMockedTimeSourceAt(now) msb := createMSBWithMocks(mockCache, shardContext, mockDomainCache, nil) td.mutableStateBuilderStartingState(msb) msb.timeSource = nowClock err := msb.closeTransactionHandleWorkflowReset(td.policyIn) assert.Equal(t, td.expectedErr, err) td.expectedEndState(t, msb) }) } } func TestMutableStateBuilder_GetVersionHistoriesStart(t *testing.T) { tests := map[string]struct { mutableStateBuilderStartingState func(m *mutableStateBuilder) expectedVersion int64 expectedErr error }{ "A mutable state with version history": { mutableStateBuilderStartingState: func(m *mutableStateBuilder) { m.versionHistories = &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("branch-token1"), Items: []*persistence.VersionHistoryItem{ { EventID: 100, Version: 23, }, { EventID: 401, Version: 424, }, }, }, { BranchToken: []byte("branch-token1"), Items: []*persistence.VersionHistoryItem{ { EventID: 200, Version: 123, }, { EventID: 201, Version: 124, }, }, }, }, } }, expectedVersion: 23, }, "invalid / partial version history ": { mutableStateBuilderStartingState: func(m *mutableStateBuilder) { m.versionHistories = &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("branch-token1"), Items: []*persistence.VersionHistoryItem{}, }, { BranchToken: []byte("branch-token2"), Items: []*persistence.VersionHistoryItem{}, }, }, } }, expectedErr: &types.BadRequestError{Message: "version history is empty."}, expectedVersion: 0, }, "invalid / partial version history - branch not available": { mutableStateBuilderStartingState: func(m *mutableStateBuilder) { m.versionHistories = &persistence.VersionHistories{ CurrentVersionHistoryIndex: 10, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("branch-token1"), Items: []*persistence.VersionHistoryItem{}, }, { BranchToken: []byte("branch-token1"), Items: []*persistence.VersionHistoryItem{}, }, }, } }, expectedErr: &types.BadRequestError{Message: "getting branch index: 10, available branch count: 2"}, expectedVersion: 0, }, "nil version history": { mutableStateBuilderStartingState: func(m *mutableStateBuilder) { }, expectedVersion: commonconstants.EmptyVersion, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) shardContext := shard.NewMockContext(ctrl) shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() mockCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) msb := createMSBWithMocks(mockCache, shardContext, mockDomainCache, nil) td.mutableStateBuilderStartingState(msb) res, err := msb.GetStartVersion() assert.Equal(t, td.expectedErr, err) assert.Equal(t, td.expectedVersion, res) }) } } func TestIsCurrentWorkflowGuaranteed(t *testing.T) { tests := []struct { name string stateInDB int expectedResult bool }{ { name: "Workflow is created", stateInDB: persistence.WorkflowStateCreated, expectedResult: true, }, { name: "Workflow is running", stateInDB: persistence.WorkflowStateRunning, expectedResult: true, }, { name: "Workflow is completed", stateInDB: persistence.WorkflowStateCompleted, expectedResult: false, }, { name: "Workflow is zombie", stateInDB: persistence.WorkflowStateZombie, expectedResult: false, }, { name: "Workflow is void", stateInDB: persistence.WorkflowStateVoid, expectedResult: false, }, { name: "Workflow is corrupted", stateInDB: persistence.WorkflowStateCorrupted, expectedResult: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { msb := mutableStateBuilder{} msb.stateInDB = tt.stateInDB result := msb.IsCurrentWorkflowGuaranteed() assert.Equal(t, tt.expectedResult, result) }) } assert.Panics(t, func() { msb := mutableStateBuilder{} msb.stateInDB = 123 msb.IsCurrentWorkflowGuaranteed() }) } // this is a pretty poor test, the actual logic is better tested in the // unit tests for getBackoffInterval() func TestGetRetryBackoffDuration(t *testing.T) { tests := []struct { name string retryPolicy *persistence.WorkflowExecutionInfo errorReason string expectedBackoff time.Duration }{ { name: "NoRetryPolicy", retryPolicy: &persistence.WorkflowExecutionInfo{ HasRetryPolicy: false, }, errorReason: "some error reason", expectedBackoff: backoff.NoBackoff, }, { name: "WithRetryPolicy", retryPolicy: &persistence.WorkflowExecutionInfo{ HasRetryPolicy: true, ExpirationTime: time.Now().Add(time.Hour), Attempt: 1, MaximumAttempts: 5, BackoffCoefficient: 2.0, InitialInterval: 12, NonRetriableErrors: []string{"non-retriable-error"}, }, errorReason: "some error reason", expectedBackoff: 24 * time.Second, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t1 := time.Unix(1730247795, 0) msb := mutableStateBuilder{} msb.executionInfo = tt.retryPolicy msb.timeSource = clock.NewMockedTimeSourceAt(t1) duration := msb.GetRetryBackoffDuration(tt.errorReason) assert.Equal(t, tt.expectedBackoff, duration) }) } } func TestGetCronRetryBackoffDuration(t *testing.T) { t1 := time.Unix(1730247795, 0) sampleVersionHistory := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("branch-token1"), Items: []*persistence.VersionHistoryItem{ { EventID: 100, Version: 23, }, { EventID: 401, Version: 424, }, }, }, { BranchToken: []byte("branch-token1"), Items: []*persistence.VersionHistoryItem{ { EventID: 200, Version: 123, }, { EventID: 201, Version: 124, }, }, }, }, } startEvent := &types.HistoryEvent{ ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, } tests := map[string]struct { startingExecutionInfo *persistence.WorkflowExecutionInfo expectedErr bool shardContextExpectations func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) expectedBackoff time.Duration }{ "with simple, valid cron schedule": { startingExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "domain-id", CronSchedule: "* * * * *", RunID: "run-id", WorkflowID: "wid", StartTimestamp: t1, }, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GetShardID().Return(12) mockCache.EXPECT().GetEvent(gomock.Any(), 12, "domain-id", "wid", "run-id", int64(1), int64(1), []byte("branch-token1")).Return(startEvent, nil) }, expectedBackoff: 45 * time.Second, }, "with no cron schedule": { startingExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "domain-id", RunID: "run-id", WorkflowID: "wid", StartTimestamp: t1, }, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { }, expectedBackoff: backoff.NoBackoff, }, "with invalid start event": { startingExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "domain-id", RunID: "run-id", WorkflowID: "wid", CronSchedule: "* * * * *", StartTimestamp: t1, }, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GetShardID().Return(12) mockCache.EXPECT().GetEvent(gomock.Any(), 12, "domain-id", "wid", "run-id", int64(1), int64(1), []byte("branch-token1")).Return(nil, assert.AnError) }, expectedBackoff: backoff.NoBackoff, expectedErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) shardContext := shard.NewMockContext(ctrl) shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() mockCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) td.shardContextExpectations(mockCache, shardContext, mockDomainCache) msb := createMSBWithMocks(mockCache, shardContext, mockDomainCache, nil) msb.executionInfo = td.startingExecutionInfo msb.versionHistories = sampleVersionHistory msb.timeSource = clock.NewMockedTimeSourceAt(t1) duration, err := msb.GetCronBackoffDuration(context.Background()) assert.Equal(t, td.expectedBackoff, duration) if td.expectedErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestStartTransactionHandleFailover(t *testing.T) { tests := map[string]struct { incomingTaskVersion int64 currentVersion int64 decisionManagerAffordance func(m *MockmutableStateDecisionTaskManager) expectFlushBeforeReady bool expectedErr bool }{ "Failing over from cluster2 to cluster1 - passive -> passive: There's an inflight decision, but it's from an earlier version": { incomingTaskVersion: 10, currentVersion: 2, decisionManagerAffordance: func(m *MockmutableStateDecisionTaskManager) { m.EXPECT().GetInFlightDecision().Return(&DecisionInfo{ Version: 2, }, true) }, expectFlushBeforeReady: false, }, // todo: David.porter - look a bit more into why this could occur and write a better description // about what the intent is, because this is a unit test without a clear intent or outcome. // At the time of writing this test I believe this is a migration case, but I'm not 100% sure and // need to do some runtime debugging. "empty version": { incomingTaskVersion: commonconstants.EmptyVersion, currentVersion: 2, decisionManagerAffordance: func(m *MockmutableStateDecisionTaskManager) { m.EXPECT().GetInFlightDecision().Return(&DecisionInfo{ Version: 2, }, true) }, expectFlushBeforeReady: false, }, "active -> passive - when there's an inflight decision from an earlier version": { incomingTaskVersion: 12, currentVersion: 11, decisionManagerAffordance: func(m *MockmutableStateDecisionTaskManager) { m.EXPECT().GetInFlightDecision().Return(&DecisionInfo{ Version: 2, StartedID: 123, ScheduleID: 124, RequestID: "requestID", }, true) m.EXPECT().AddDecisionTaskFailedEvent(int64(124), int64(123), types.DecisionTaskFailedCauseFailoverCloseDecision, gomock.Any(), "history-service", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) m.EXPECT().HasInFlightDecision().Return(true) m.EXPECT().HasInFlightDecision().Return(true) m.EXPECT().HasPendingDecision().Return(true) }, expectFlushBeforeReady: true, }, "There's a decision for for the same level as the failover version": { incomingTaskVersion: 10, currentVersion: 10, decisionManagerAffordance: func(m *MockmutableStateDecisionTaskManager) { m.EXPECT().GetInFlightDecision().Return(&DecisionInfo{ Version: 1, }, true) }, expectFlushBeforeReady: false, expectedErr: true, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) clusterMetadata := cluster.NewMetadata( commonConfig.ClusterGroupMetadata{ FailoverVersionIncrement: 10, PrimaryClusterName: "cluster0", CurrentClusterName: "cluster0", ClusterGroup: map[string]commonConfig.ClusterInformation{ "cluster0": { Enabled: true, InitialFailoverVersion: 1, }, "cluster1": { Enabled: true, InitialFailoverVersion: 0, }, "cluster2": { Enabled: true, InitialFailoverVersion: 2, }, }, }, func(string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), ) shardContext := shardCtx.NewMockContext(ctrl) shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() activeClusterManager := activecluster.NewMockManager(ctrl) shardContext.EXPECT().GetActiveClusterManager().Return(activeClusterManager).AnyTimes() decisionManager := NewMockmutableStateDecisionTaskManager(ctrl) td.decisionManagerAffordance(decisionManager) shardContext.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 3, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, MutableStateChecksumInvalidateBefore: dynamicproperties.GetFloatPropertyFn(10), MutableStateChecksumVerifyProbability: dynamicproperties.GetIntPropertyFilteredByDomain(0.0), EnableReplicationTaskGeneration: func(_ string, _ string) bool { return true }, HostName: "test-host", }).Times(1) domainEntry := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ ID: "domain-id", Name: "domain", }, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{ ActiveClusterName: "cluster0", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "cluster0"}, {ClusterName: "cluster1"}, {ClusterName: "cluster2"}, }, }, 0, nil, 0, 0, 0) msb := mutableStateBuilder{ decisionTaskManager: decisionManager, shard: shardContext, domainEntry: domainEntry, clusterMetadata: clusterMetadata, currentVersion: td.currentVersion, versionHistories: &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte("token"), Items: []*persistence.VersionHistoryItem{ { EventID: 3, Version: 10, }, { EventID: 2, Version: 2, }, }, }, }, }, executionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "domainID", WorkflowID: "workflowID", RunID: "some-example-run", FirstExecutionRunID: "", ParentDomainID: "", ParentWorkflowID: "", ParentRunID: "", InitiatedID: 0, CompletionEventBatchID: 0, CompletionEvent: nil, State: 0, CloseStatus: 0, LastFirstEventID: 0, LastEventTaskID: 0, NextEventID: 0, LastProcessedEvent: 0, }, logger: testlogger.New(t), } msb.hBuilder = NewHistoryBuilder(&msb) flushBeforeReady, err := msb.startTransactionHandleDecisionFailover(td.incomingTaskVersion) if td.expectedErr { assert.Error(t, err) } else { assert.NoError(t, err) } assert.Equal(t, td.expectFlushBeforeReady, flushBeforeReady) }) } } func TestSimpleGetters(t *testing.T) { msb := createMSB(t) assert.Equal(t, msb.versionHistories, msb.GetVersionHistories()) branchToken, err := msb.GetCurrentBranchToken() assert.Equal(t, msb.versionHistories.Histories[0].BranchToken, branchToken) assert.NoError(t, err) assert.Equal(t, msb.currentVersion, msb.GetCurrentVersion()) assert.Equal(t, msb.domainEntry, msb.GetDomainEntry()) assert.Equal(t, msb.executionInfo, msb.GetExecutionInfo()) assert.Equal(t, msb.hBuilder, msb.GetHistoryBuilder()) assert.Equal(t, msb.executionStats.HistorySize, msb.GetHistorySize()) assert.Equal(t, msb.executionInfo.LastFirstEventID, msb.GetLastFirstEventID()) lastWriteVersion, err := msb.GetLastWriteVersion() item, err := msb.versionHistories.Histories[0].GetLastItem() assert.NoError(t, err) assert.Equal(t, item.Version, lastWriteVersion) assert.Equal(t, msb.executionInfo.NextEventID, msb.GetNextEventID()) assert.Equal(t, msb.pendingRequestCancelInfoIDs, msb.GetPendingRequestCancelExternalInfos()) assert.Equal(t, msb.executionInfo.LastProcessedEvent, msb.GetPreviousStartedEventID()) assert.Equal(t, msb.queryRegistry, msb.GetQueryRegistry()) startVersion, err := msb.GetStartVersion() assert.NoError(t, err) assert.Equal(t, msb.versionHistories.Histories[0].Items[0].Version, startVersion) assert.Equal(t, msb.insertTimerTasks, msb.GetTimerTasks()) assert.Equal(t, msb.insertTransferTasks, msb.GetTransferTasks()) assert.Equal(t, msb.nextEventIDInDB, msb.GetUpdateCondition()) assert.Equal(t, msb.versionHistories, msb.GetVersionHistories()) state, closeStatus := msb.GetWorkflowStateCloseStatus() assert.Equal(t, msb.executionInfo.CloseStatus, closeStatus) assert.Equal(t, msb.executionInfo.State, state) assert.Equal(t, &types.WorkflowType{Name: msb.executionInfo.WorkflowTypeName}, msb.GetWorkflowType()) pendingActivityInfo, activityInfoIsPresent := msb.GetActivityInfo(1232) assert.Equal(t, msb.pendingActivityInfoIDs[1232], pendingActivityInfo) assert.True(t, activityInfoIsPresent) assert.Equal(t, msb.pendingActivityInfoIDs, msb.GetPendingActivityInfos()) pendingRequestCancelledInfo, ok := msb.GetRequestCancelInfo(13) assert.Equal(t, msb.pendingRequestCancelInfoIDs[13], pendingRequestCancelledInfo) assert.True(t, ok) pendingChildExecutions, ok := msb.GetChildExecutionInfo(1) assert.Equal(t, msb.pendingChildExecutionInfoIDs[1], pendingChildExecutions) assert.True(t, ok) } func TestMutableState_IsCurrentWorkflowGuaranteed(t *testing.T) { tests := map[string]struct { state int expected bool }{ "created": { state: persistence.WorkflowStateCreated, expected: true, }, "running": { state: persistence.WorkflowStateCreated, expected: true, }, "completed": { state: persistence.WorkflowStateCompleted, expected: false, }, "void": { state: persistence.WorkflowStateVoid, expected: false, }, "zombie state": { state: persistence.WorkflowStateZombie, expected: false, }, "corrupted state": { state: persistence.WorkflowStateCorrupted, expected: false, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { msb := mutableStateBuilder{ stateInDB: td.state, logger: testlogger.New(t), } assert.Equal(t, td.expected, msb.IsCurrentWorkflowGuaranteed()) }) } } func createMSB(t *testing.T) mutableStateBuilder { sampleDomain := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ID: "domain-id", Name: "domain"}, &persistence.DomainConfig{}, true, nil, 0, nil, 0, 0, 0) return mutableStateBuilder{ pendingActivityInfoIDs: map[int64]*persistence.ActivityInfo{ 1232: &persistence.ActivityInfo{ActivityID: "activityID"}, }, pendingActivityIDToEventID: map[string]int64{ "activityID": 6, }, updateActivityInfos: map[int64]*persistence.ActivityInfo{ 7: &persistence.ActivityInfo{DomainID: "domainID"}, }, deleteActivityInfos: map[int64]struct{}{ 8: struct{}{}, }, syncActivityTasks: map[int64]struct{}{}, pendingTimerInfoIDs: map[string]*persistence.TimerInfo{ "testdata-pendingTimerInfoIDs": &persistence.TimerInfo{ Version: 1, TimerID: "1232", }, }, pendingTimerEventIDToID: map[int64]string{}, updateTimerInfos: map[string]*persistence.TimerInfo{ "testdata-updatedtimerinfos": &persistence.TimerInfo{ Version: 1, TimerID: "1232", }, }, deleteTimerInfos: map[string]struct{}{}, pendingChildExecutionInfoIDs: map[int64]*persistence.ChildExecutionInfo{ 1: &persistence.ChildExecutionInfo{ WorkflowTypeName: "sample-workflow", }, }, updateChildExecutionInfos: map[int64]*persistence.ChildExecutionInfo{ 8: &persistence.ChildExecutionInfo{DomainID: "updateChildInfosDomainID"}, }, deleteChildExecutionInfos: map[int64]struct{}{ 12: struct{}{}, }, pendingRequestCancelInfoIDs: map[int64]*persistence.RequestCancelInfo{ 13: &persistence.RequestCancelInfo{InitiatedID: 16}, }, updateRequestCancelInfos: map[int64]*persistence.RequestCancelInfo{}, deleteRequestCancelInfos: map[int64]struct{}{ 15: struct{}{}, }, pendingSignalInfoIDs: map[int64]*persistence.SignalInfo{}, updateSignalInfos: map[int64]*persistence.SignalInfo{}, deleteSignalInfos: map[int64]struct{}{}, pendingSignalRequestedIDs: map[string]struct{}{}, updateSignalRequestedIDs: map[string]struct{}{}, deleteSignalRequestedIDs: map[string]struct{}{}, bufferedEvents: []*types.HistoryEvent{}, updateBufferedEvents: []*types.HistoryEvent{}, clearBufferedEvents: false, executionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "d9cbf563-3056-4387-b2ac-5fddd868fe4d", WorkflowID: "53fc235c-093e-4b15-9d9d-045e61354b91", RunID: "a2901718-ac12-443e-873d-b100f45d55d8", FirstExecutionRunID: "a2901718-ac12-443e-873d-b100f45d55d8", InitiatedID: -7, TaskList: "tl", WorkflowTypeName: "test", WorkflowTimeout: 600000000, DecisionStartToCloseTimeout: 10, State: 1, LastFirstEventID: 1, LastEventTaskID: 2097153, NextEventID: 3, LastProcessedEvent: -23, StartTimestamp: time.Date(2024, 10, 21, 20, 58, 1, 275000000, time.UTC), LastUpdatedTimestamp: time.Date(2024, 10, 21, 20, 58, 1, 275000000, time.UTC), CreateRequestID: "f33ee669-9ff6-4221-a2b0-feb2959667b8", DecisionVersion: 1, DecisionScheduleID: 2, DecisionStartedID: -23, DecisionRequestID: "emptyUuid", DecisionTimeout: 10, DecisionScheduledTimestamp: 1729544281275414000, DecisionOriginalScheduledTimestamp: 1729544281275414000, AutoResetPoints: &types.ResetPoints{}, }, versionHistories: &persistence.VersionHistories{ Histories: []*persistence.VersionHistory{ { BranchToken: []byte("a branch token"), Items: []*persistence.VersionHistoryItem{{ EventID: 2, Version: 1, }}, }, }, }, currentVersion: int64(-24), hasBufferedEventsInDB: false, stateInDB: int(1), nextEventIDInDB: int64(3), domainEntry: sampleDomain, appliedEvents: map[string]struct{}{}, insertTransferTasks: []persistence.Task{ &persistence.DecisionTask{ TargetDomainID: "decsion task", }, }, insertReplicationTasks: []persistence.Task{}, insertTimerTasks: []persistence.Task{ &persistence.ActivityRetryTimerTask{ TaskData: persistence.TaskData{}, EventID: 123, Attempt: 4, }, }, workflowRequests: map[persistence.WorkflowRequest]struct{}{}, checksum: checksum.Checksum{}, executionStats: &persistence.ExecutionStats{HistorySize: 403}, queryRegistry: query.NewRegistry(), logger: testlogger.New(t), } } func TestMutableStateBuilder_GetTransferTasks(t *testing.T) { msb := &mutableStateBuilder{ insertTransferTasks: []persistence.Task{ &persistence.ActivityTask{}, &persistence.DecisionTask{}, }, } tasks := msb.GetTransferTasks() assert.Equal(t, 2, len(tasks)) assert.IsType(t, &persistence.ActivityTask{}, tasks[0]) assert.IsType(t, &persistence.DecisionTask{}, tasks[1]) } func TestMutableStateBuilder_GetTimerTasks(t *testing.T) { msb := &mutableStateBuilder{ insertTimerTasks: []persistence.Task{ &persistence.UserTimerTask{}, }, } tasks := msb.GetTimerTasks() assert.Equal(t, 1, len(tasks)) assert.IsType(t, &persistence.UserTimerTask{}, tasks[0]) } func TestMutableStateBuilder_DeleteTransferTasks(t *testing.T) { msb := &mutableStateBuilder{ insertTransferTasks: []persistence.Task{ &persistence.ActivityTask{}, }, } msb.DeleteTransferTasks() assert.Nil(t, msb.insertTransferTasks) } func TestMutableStateBuilder_DeleteTimerTasks(t *testing.T) { msb := &mutableStateBuilder{ insertTimerTasks: []persistence.Task{ &persistence.UserTimerTask{}, }, } msb.DeleteTimerTasks() assert.Nil(t, msb.insertTimerTasks) } func TestMutableStateBuilder_SetUpdateCondition(t *testing.T) { msb := &mutableStateBuilder{} msb.SetUpdateCondition(123) assert.Equal(t, int64(123), msb.nextEventIDInDB) } func TestMutableStateBuilder_GetUpdateCondition(t *testing.T) { msb := &mutableStateBuilder{ nextEventIDInDB: 123, } assert.Equal(t, int64(123), msb.GetUpdateCondition()) } func TestCheckAndClearTimerFiredEvent(t *testing.T) { tests := []struct { name string timerID string bufferedEvents []*types.HistoryEvent updateBufferedEvents []*types.HistoryEvent history []*types.HistoryEvent expectedTimerEvent *types.HistoryEvent expectedBufferedEvents []*types.HistoryEvent expectedUpdateBufferedEvents []*types.HistoryEvent expectedHistory []*types.HistoryEvent }{ { name: "TimerFiredEventInBufferedEvents", timerID: "timer1", bufferedEvents: []*types.HistoryEvent{ { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer1", }, }, { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer2", }, }, }, updateBufferedEvents: []*types.HistoryEvent{}, history: []*types.HistoryEvent{}, expectedTimerEvent: &types.HistoryEvent{ EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer1", }, }, expectedBufferedEvents: []*types.HistoryEvent{ { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer2", }, }, }, expectedUpdateBufferedEvents: []*types.HistoryEvent{}, expectedHistory: []*types.HistoryEvent{}, }, { name: "TimerFiredEventInUpdateBufferedEvents", timerID: "timer2", bufferedEvents: []*types.HistoryEvent{}, updateBufferedEvents: []*types.HistoryEvent{ { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer1", }, }, { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer2", }, }, { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer3", }, }, }, history: []*types.HistoryEvent{}, expectedTimerEvent: &types.HistoryEvent{ EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer2", }, }, expectedBufferedEvents: []*types.HistoryEvent{}, expectedUpdateBufferedEvents: []*types.HistoryEvent{ { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer1", }, }, { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer3", }, }, }, expectedHistory: []*types.HistoryEvent{}, }, { name: "TimerFiredEventInHistory", timerID: "timer3", bufferedEvents: []*types.HistoryEvent{}, updateBufferedEvents: []*types.HistoryEvent{}, history: []*types.HistoryEvent{ { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer1", }, }, { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer3", }, }, }, expectedTimerEvent: &types.HistoryEvent{ EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer3", }, }, expectedBufferedEvents: []*types.HistoryEvent{}, expectedUpdateBufferedEvents: []*types.HistoryEvent{}, expectedHistory: []*types.HistoryEvent{ { EventType: types.EventTypeTimerFired.Ptr(), TimerFiredEventAttributes: &types.TimerFiredEventAttributes{ TimerID: "timer1", }, }, }, }, { name: "NoTimerFiredEvent", timerID: "timer4", bufferedEvents: []*types.HistoryEvent{}, updateBufferedEvents: []*types.HistoryEvent{}, history: []*types.HistoryEvent{}, expectedTimerEvent: nil, expectedBufferedEvents: []*types.HistoryEvent{}, expectedUpdateBufferedEvents: []*types.HistoryEvent{}, expectedHistory: []*types.HistoryEvent{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { msb := &mutableStateBuilder{ bufferedEvents: tt.bufferedEvents, updateBufferedEvents: tt.updateBufferedEvents, hBuilder: &HistoryBuilder{history: tt.history}, } timerEvent := msb.checkAndClearTimerFiredEvent(tt.timerID) assert.Equal(t, tt.expectedTimerEvent, timerEvent) assert.Equal(t, tt.expectedBufferedEvents, msb.bufferedEvents) assert.Equal(t, tt.expectedUpdateBufferedEvents, msb.updateBufferedEvents) assert.Equal(t, tt.expectedHistory, msb.hBuilder.history) }) } } func TestAssignTaskIDToTransientHistoryEvents(t *testing.T) { tests := map[string]struct { transientHistory []*types.HistoryEvent taskID int64 shardContextExpectations func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) expectedEvents []*types.HistoryEvent expectedErr error }{ "AssignTaskIDToSingleEvent - transient": { transientHistory: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: commonconstants.EmptyEventTaskID, }, }, taskID: 123, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GenerateTaskIDs(1).Return([]int64{123}, nil).Times(1) }, expectedEvents: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: 123, }, }, }, "AssignTaskIDToMultipleEvents - transient": { transientHistory: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: commonconstants.EmptyEventTaskID, }, { ID: 2, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), TaskID: commonconstants.EmptyEventTaskID, }, }, taskID: 456, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GenerateTaskIDs(2).Return([]int64{123, 124}, nil).Times(1) }, expectedEvents: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: 123, }, { ID: 2, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), TaskID: 124, }, }, }, "NoEvents - transient events": { transientHistory: []*types.HistoryEvent{}, taskID: 789, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { }, expectedEvents: []*types.HistoryEvent{}, }, "error returned": { transientHistory: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: commonconstants.EmptyEventTaskID, }, }, taskID: 456, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GenerateTaskIDs(1).Return(nil, assert.AnError).Times(1) }, expectedEvents: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: commonconstants.EmptyEventTaskID, }, }, expectedErr: assert.AnError, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) shardContext := shard.NewMockContext(ctrl) shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() mockCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) td.shardContextExpectations(mockCache, shardContext, mockDomainCache) msb := createMSBWithMocks(mockCache, shardContext, mockDomainCache, nil) msb.hBuilder.transientHistory = td.transientHistory err := msb.assignTaskIDToEvents() assert.Equal(t, td.expectedEvents, msb.hBuilder.transientHistory) assert.Equal(t, td.expectedErr, err) }) } } func TestAssignTaskIDToHistoryEvents(t *testing.T) { tests := map[string]struct { history []*types.HistoryEvent taskID int64 shardContextExpectations func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) expectedEvents []*types.HistoryEvent expectedErr error }{ "AssignTaskIDToSingleEvent": { history: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: commonconstants.EmptyEventTaskID, }, }, taskID: 123, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GenerateTaskIDs(1).Return([]int64{123}, nil).Times(1) }, expectedEvents: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: 123, }, }, }, "AssignTaskIDToMultipleEvents": { history: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: commonconstants.EmptyEventTaskID, }, { ID: 2, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), TaskID: commonconstants.EmptyEventTaskID, }, }, taskID: 456, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GenerateTaskIDs(2).Return([]int64{123, 124}, nil).Times(1) }, expectedEvents: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: 123, }, { ID: 2, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), TaskID: 124, }, }, }, "NoEvents - transient events": { history: []*types.HistoryEvent{}, taskID: 789, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { }, expectedEvents: []*types.HistoryEvent{}, }, "error returned": { history: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: commonconstants.EmptyEventTaskID, }, }, taskID: 456, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GenerateTaskIDs(1).Return(nil, assert.AnError).Times(1) }, expectedEvents: []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), TaskID: commonconstants.EmptyEventTaskID, }, }, expectedErr: assert.AnError, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) shardContext := shard.NewMockContext(ctrl) shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() mockCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) td.shardContextExpectations(mockCache, shardContext, mockDomainCache) msb := createMSBWithMocks(mockCache, shardContext, mockDomainCache, nil) msb.hBuilder.history = td.history err := msb.assignTaskIDToEvents() assert.Equal(t, td.expectedEvents, msb.hBuilder.history) assert.Equal(t, td.expectedErr, err) }) } } func TestAddUpsertWorkflowSearchAttributesEvent(t *testing.T) { now := time.Unix(1730353941, 0) tests := map[string]struct { decisionCompletedEventID int64 request *types.UpsertWorkflowSearchAttributesDecisionAttributes mutableStateBuilderSetup func(m *mutableStateBuilder) expectedEvent *types.HistoryEvent expectedErr error }{ "successful upsert": { decisionCompletedEventID: 123, request: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: &types.SearchAttributes{ IndexedFields: map[string][]byte{ "CustomKeywordField": []byte("keyword"), }, }, }, mutableStateBuilderSetup: func(m *mutableStateBuilder) { }, expectedEvent: &types.HistoryEvent{ ID: 1, EventType: types.EventTypeUpsertWorkflowSearchAttributes.Ptr(), UpsertWorkflowSearchAttributesEventAttributes: &types.UpsertWorkflowSearchAttributesEventAttributes{ DecisionTaskCompletedEventID: 123, SearchAttributes: &types.SearchAttributes{ IndexedFields: map[string][]byte{ "CustomKeywordField": []byte("keyword"), }, }, }, TaskID: commonconstants.EmptyEventTaskID, Version: commonconstants.EmptyVersion, Timestamp: common.Ptr(now.UnixNano()), }, expectedErr: nil, }, "mutability check fails": { decisionCompletedEventID: 123, request: &types.UpsertWorkflowSearchAttributesDecisionAttributes{ SearchAttributes: &types.SearchAttributes{ IndexedFields: map[string][]byte{ "CustomKeywordField": []byte("keyword"), }, }, }, mutableStateBuilderSetup: func(m *mutableStateBuilder) { m.executionInfo.State = persistence.WorkflowStateCompleted }, expectedEvent: nil, expectedErr: &types.InternalServiceError{Message: "invalid mutable state action: mutation after finish"}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) shardContext := shard.NewMockContext(ctrl) shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() mockCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) nowClock := clock.NewMockedTimeSourceAt(now) msb := createMSBWithMocks(mockCache, shardContext, mockDomainCache, nil) msb.hBuilder = &HistoryBuilder{ history: []*types.HistoryEvent{}, msBuilder: msb, } td.mutableStateBuilderSetup(msb) msb.timeSource = nowClock event, err := msb.AddUpsertWorkflowSearchAttributesEvent(td.decisionCompletedEventID, td.request) assert.Equal(t, td.expectedEvent, event) assert.Equal(t, td.expectedErr, err) }) } } func TestCloseTransactionAsMutation(t *testing.T) { now := time.Unix(500, 0) mockDomain := cache.NewLocalDomainCacheEntryForTest(&persistence.DomainInfo{Name: "domain"}, &persistence.DomainConfig{ BadBinaries: types.BadBinaries{}, }, "cluster0") tests := map[string]struct { mutableStateSetup func(ms *mutableStateBuilder) shardContextExpectations func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) transactionPolicy TransactionPolicy expectedMutation *persistence.WorkflowMutation expectedEvent []*persistence.WorkflowEvents expectedErr error }{ "no buffered events": { mutableStateSetup: func(ms *mutableStateBuilder) { ms.executionInfo.DomainID = "some-domain-id" ms.executionInfo.NextEventID = 10 ms.executionInfo.LastProcessedEvent = 5 ms.executionInfo.State = persistence.WorkflowStateRunning ms.executionInfo.CloseStatus = persistence.WorkflowCloseStatusNone }, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 2, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, MutableStateChecksumInvalidateBefore: dynamicproperties.GetFloatPropertyFn(10), MutableStateChecksumVerifyProbability: dynamicproperties.GetIntPropertyFilteredByDomain(0.0), HostName: "test-host", EnableReplicationTaskGeneration: func(string, string) bool { return true }, MaximumBufferedEventsBatch: func(...dynamicproperties.FilterOption) int { return 100 }, }).Times(2) shardContext.EXPECT().GetDomainCache().Return(mockDomainCache).Times(1) mockDomainCache.EXPECT().GetDomainByID("some-domain-id").Return(mockDomain, nil) }, expectedMutation: &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "some-domain-id", NextEventID: 10, LastProcessedEvent: 5, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, LastUpdatedTimestamp: now, DecisionVersion: commonconstants.EmptyVersion, DecisionScheduleID: commonconstants.EmptyEventID, DecisionRequestID: commonconstants.EmptyUUID, DecisionStartedID: commonconstants.EmptyEventID, }, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: nil, persistence.HistoryTaskCategoryTimer: nil, persistence.HistoryTaskCategoryReplication: nil, }, UpsertActivityInfos: []*persistence.ActivityInfo{}, DeleteActivityInfos: []int64{}, UpsertTimerInfos: []*persistence.TimerInfo{}, DeleteTimerInfos: []string{}, UpsertChildExecutionInfos: []*persistence.ChildExecutionInfo{}, UpsertRequestCancelInfos: []*persistence.RequestCancelInfo{}, DeleteRequestCancelInfos: []int64{}, UpsertSignalInfos: []*persistence.SignalInfo{}, DeleteSignalInfos: []int64{}, UpsertSignalRequestedIDs: []string{}, DeleteSignalRequestedIDs: []string{}, DeleteChildExecutionInfos: []int64{}, WorkflowRequests: []*persistence.WorkflowRequest{}, Condition: 0, }, expectedEvent: nil, expectedErr: nil, }, "with buffered events": { mutableStateSetup: func(ms *mutableStateBuilder) { ms.executionInfo.DomainID = "some-domain-id" ms.executionInfo.NextEventID = 10 ms.executionInfo.LastProcessedEvent = 5 ms.executionInfo.State = persistence.WorkflowStateRunning ms.executionInfo.CloseStatus = persistence.WorkflowCloseStatusNone ms.bufferedEvents = []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), }, } }, shardContextExpectations: func(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache) { shardContext.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 2, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, MutableStateChecksumInvalidateBefore: dynamicproperties.GetFloatPropertyFn(10), MutableStateChecksumVerifyProbability: dynamicproperties.GetIntPropertyFilteredByDomain(0.0), HostName: "test-host", EnableReplicationTaskGeneration: func(string, string) bool { return true }, MaximumBufferedEventsBatch: func(...dynamicproperties.FilterOption) int { return 100 }, }).Times(3) shardContext.EXPECT().GenerateTaskIDs(1).Return([]int64{123}, nil).Times(1) shardContext.EXPECT().GetDomainCache().Return(mockDomainCache).Times(1) mockDomainCache.EXPECT().GetDomainByID("some-domain-id").Return(mockDomain, nil) }, expectedMutation: &persistence.WorkflowMutation{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "some-domain-id", NextEventID: 10, LastProcessedEvent: 5, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, LastUpdatedTimestamp: now, DecisionVersion: commonconstants.EmptyVersion, DecisionScheduleID: commonconstants.EmptyEventID, DecisionRequestID: commonconstants.EmptyUUID, DecisionStartedID: commonconstants.EmptyEventID, LastFirstEventID: 1, }, TasksByCategory: map[persistence.HistoryTaskCategory][]persistence.Task{ persistence.HistoryTaskCategoryTransfer: nil, persistence.HistoryTaskCategoryTimer: nil, persistence.HistoryTaskCategoryReplication: []persistence.Task{ &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some-domain-id", WorkflowID: "", RunID: "", }, FirstEventID: 1, NextEventID: 2, TaskData: persistence.TaskData{ Version: 0, TaskID: 0, VisibilityTimestamp: time.Time{}, }, }, }, }, UpsertActivityInfos: []*persistence.ActivityInfo{}, DeleteActivityInfos: []int64{}, UpsertTimerInfos: []*persistence.TimerInfo{}, DeleteTimerInfos: []string{}, UpsertChildExecutionInfos: []*persistence.ChildExecutionInfo{}, UpsertRequestCancelInfos: []*persistence.RequestCancelInfo{}, DeleteRequestCancelInfos: []int64{}, UpsertSignalInfos: []*persistence.SignalInfo{}, DeleteSignalInfos: []int64{}, UpsertSignalRequestedIDs: []string{}, DeleteSignalRequestedIDs: []string{}, DeleteChildExecutionInfos: []int64{}, WorkflowRequests: []*persistence.WorkflowRequest{}, Condition: 0, }, expectedEvent: []*persistence.WorkflowEvents{ { DomainID: "some-domain-id", Events: []*types.HistoryEvent{{ ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr()}, }, }, }, expectedErr: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) shardContext := shard.NewMockContext(ctrl) shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() activeClusterManager := activecluster.NewMockManager(ctrl) shardContext.EXPECT().GetActiveClusterManager().Return(activeClusterManager).AnyTimes() mockCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) ms := createMSBWithMocks(mockCache, shardContext, mockDomainCache, nil) td.mutableStateSetup(ms) td.shardContextExpectations(mockCache, shardContext, mockDomainCache) mutation, workflowEvents, err := ms.CloseTransactionAsMutation(now, td.transactionPolicy) assert.Equal(t, td.expectedMutation, mutation) assert.Equal(t, td.expectedEvent, workflowEvents) assert.Equal(t, td.expectedErr, err) }) } } func Test__reorderAndFilterDuplicateEvents(t *testing.T) { testCases := []struct { name string buildEvents func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) assertions func(*testing.T, *observer.ObservedLogs) }{ { name: "no duplicates", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskStarted) event1.ActivityTaskStartedEventAttributes = &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 1, Attempt: 1, } event2 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskCompleted) event2.ActivityTaskCompletedEventAttributes = &types.ActivityTaskCompletedEventAttributes{ ScheduledEventID: 1, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1, event2} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 0, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "activity started event duplicated", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskStarted) event1.ActivityTaskStartedEventAttributes = &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 1, Attempt: 1, } event2 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskStarted) event2.ActivityTaskStartedEventAttributes = &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 1, Attempt: 1, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 1, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "activty completed event duplicated", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskCompleted) event1.ActivityTaskCompletedEventAttributes = &types.ActivityTaskCompletedEventAttributes{ ScheduledEventID: 1, } event2 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskCompleted) event2.ActivityTaskCompletedEventAttributes = &types.ActivityTaskCompletedEventAttributes{ ScheduledEventID: 1, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 1, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "activity canceled event duplicated", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskCanceled) event1.ActivityTaskCanceledEventAttributes = &types.ActivityTaskCanceledEventAttributes{ ScheduledEventID: 1, } event2 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskCanceled) event2.ActivityTaskCanceledEventAttributes = &types.ActivityTaskCanceledEventAttributes{ ScheduledEventID: 1, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 1, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "activity failed event duplicated", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskFailed) event1.ActivityTaskFailedEventAttributes = &types.ActivityTaskFailedEventAttributes{ ScheduledEventID: 1, } event2 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskFailed) event2.ActivityTaskFailedEventAttributes = &types.ActivityTaskFailedEventAttributes{ ScheduledEventID: 1, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 1, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "activity timed out event duplicated", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskTimedOut) event1.ActivityTaskTimedOutEventAttributes = &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 1, } event2 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskTimedOut) event2.ActivityTaskTimedOutEventAttributes = &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 1, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 1, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "child workflow started event duplicated", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionStarted) event1.ChildWorkflowExecutionStartedEventAttributes = &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 1, } event2 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionStarted) event2.ChildWorkflowExecutionStartedEventAttributes = &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 1, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 1, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "child workflow completed event duplicated", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionCompleted) event1.ChildWorkflowExecutionCompletedEventAttributes = &types.ChildWorkflowExecutionCompletedEventAttributes{ InitiatedEventID: 1, StartedEventID: 2, } event2 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionCompleted) event2.ChildWorkflowExecutionCompletedEventAttributes = &types.ChildWorkflowExecutionCompletedEventAttributes{ InitiatedEventID: 1, StartedEventID: 2, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 1, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "child workflow failed event duplicated", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionFailed) event1.ChildWorkflowExecutionFailedEventAttributes = &types.ChildWorkflowExecutionFailedEventAttributes{ InitiatedEventID: 1, StartedEventID: 2, } event2 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionFailed) event2.ChildWorkflowExecutionFailedEventAttributes = &types.ChildWorkflowExecutionFailedEventAttributes{ InitiatedEventID: 1, StartedEventID: 2, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 1, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "child workflow timed out event duplicated", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionTimedOut) event1.ChildWorkflowExecutionTimedOutEventAttributes = &types.ChildWorkflowExecutionTimedOutEventAttributes{ InitiatedEventID: 1, StartedEventID: 2, } event2 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionTimedOut) event2.ChildWorkflowExecutionTimedOutEventAttributes = &types.ChildWorkflowExecutionTimedOutEventAttributes{ InitiatedEventID: 1, StartedEventID: 2, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 1, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "child workflow canceled event duplicated", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionCanceled) event1.ChildWorkflowExecutionCanceledEventAttributes = &types.ChildWorkflowExecutionCanceledEventAttributes{ InitiatedEventID: 1, StartedEventID: 2, } event2 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionCanceled) event2.ChildWorkflowExecutionCanceledEventAttributes = &types.ChildWorkflowExecutionCanceledEventAttributes{ InitiatedEventID: 1, StartedEventID: 2, } return []*types.HistoryEvent{event1, event2}, []*types.HistoryEvent{event1} }, assertions: func(t *testing.T, logs *observer.ObservedLogs) { assert.Equal(t, 1, logs.FilterMessage("Duplicate event found").Len()) }, }, { name: "reorder events", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskStarted) event1.ActivityTaskStartedEventAttributes = &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 1, Attempt: 1, } event2 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskTimedOut) event2.ActivityTaskTimedOutEventAttributes = &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 1, } return []*types.HistoryEvent{event2, event1}, []*types.HistoryEvent{event1, event2} }, }, { name: "reorder all event types", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskStarted) event1.ActivityTaskStartedEventAttributes = &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 1, Attempt: 1, } event2 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskCompleted) event2.ActivityTaskCompletedEventAttributes = &types.ActivityTaskCompletedEventAttributes{ ScheduledEventID: 1, } event3 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskFailed) event3.ActivityTaskFailedEventAttributes = &types.ActivityTaskFailedEventAttributes{ ScheduledEventID: 1, } event4 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskCanceled) event4.ActivityTaskCanceledEventAttributes = &types.ActivityTaskCanceledEventAttributes{ ScheduledEventID: 1, } event5 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskTimedOut) event5.ActivityTaskTimedOutEventAttributes = &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 1, } event6 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionCompleted) event6.ChildWorkflowExecutionCompletedEventAttributes = &types.ChildWorkflowExecutionCompletedEventAttributes{ StartedEventID: 1, } event7 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionFailed) event7.ChildWorkflowExecutionFailedEventAttributes = &types.ChildWorkflowExecutionFailedEventAttributes{ StartedEventID: 1, } event8 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionCanceled) event8.ChildWorkflowExecutionCanceledEventAttributes = &types.ChildWorkflowExecutionCanceledEventAttributes{ StartedEventID: 1, } event9 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionTimedOut) event9.ChildWorkflowExecutionTimedOutEventAttributes = &types.ChildWorkflowExecutionTimedOutEventAttributes{ StartedEventID: 1, } event10 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionTerminated) event10.ChildWorkflowExecutionTerminatedEventAttributes = &types.ChildWorkflowExecutionTerminatedEventAttributes{ StartedEventID: 1, } return []*types.HistoryEvent{event2, event3, event4, event5, event6, event7, event8, event9, event10, event1}, []*types.HistoryEvent{event1, event2, event3, event4, event5, event6, event7, event8, event9, event10} }, }, { name: "reorder and remove duplicate events", buildEvents: func(msb *mutableStateBuilder) ([]*types.HistoryEvent, []*types.HistoryEvent) { event1 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskStarted) event1.ActivityTaskStartedEventAttributes = &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 1, Attempt: 1, } event2 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskTimedOut) event2.ActivityTaskTimedOutEventAttributes = &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 1, } event3 := msb.CreateNewHistoryEvent(types.EventTypeActivityTaskTimedOut) event3.ActivityTaskTimedOutEventAttributes = &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 1, } event4 := msb.CreateNewHistoryEvent(types.EventTypeStartChildWorkflowExecutionInitiated) event4.StartChildWorkflowExecutionInitiatedEventAttributes = &types.StartChildWorkflowExecutionInitiatedEventAttributes{} event5 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionStarted) event5.ChildWorkflowExecutionStartedEventAttributes = &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 4, } event6 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionStarted) event6.ChildWorkflowExecutionStartedEventAttributes = &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 4, } event7 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionCompleted) event7.ChildWorkflowExecutionCompletedEventAttributes = &types.ChildWorkflowExecutionCompletedEventAttributes{ InitiatedEventID: 4, StartedEventID: 5, } event8 := msb.CreateNewHistoryEvent(types.EventTypeChildWorkflowExecutionCompleted) event8.ChildWorkflowExecutionCompletedEventAttributes = &types.ChildWorkflowExecutionCompletedEventAttributes{ InitiatedEventID: 4, StartedEventID: 5, } return []*types.HistoryEvent{event2, event1, event3, event4, event5, event6, event7, event8}, []*types.HistoryEvent{event1, event4, event5, event2, event7} }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() core, observedLogs := observer.New(zap.WarnLevel) mockCache := events.NewMockCache(ctrl) shardContext := shard.NewMockContext(ctrl) shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() mockDomainCache := cache.NewMockDomainCache(ctrl) msb := createMSBWithMocks(mockCache, shardContext, mockDomainCache, nil) msb.logger = log.NewLogger(zap.New(core)) msb.executionInfo.DomainID = "some-domain-id" msb.executionInfo.WorkflowID = "some-workflow-id" msb.executionInfo.RunID = "some-run-id" allEvents, nonDuplicate := tc.buildEvents(msb) result := msb.reorderAndFilterDuplicateEvents(allEvents, "test") assert.Equal(t, nonDuplicate, result) if tc.assertions != nil { tc.assertions(t, observedLogs) } }) } } func createMSBWithMocks(mockCache *events.MockCache, shardContext *shardCtx.MockContext, mockDomainCache *cache.MockDomainCache, domainEntry *cache.DomainCacheEntry) *mutableStateBuilder { // the MSB constructor calls a bunch of endpoints on the mocks, so // put them in here as a set of fixed expectations so the actual mocking // code can just make expectations on the calls on the returned MSB object // and not get cluttered with constructor calls shardContext.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata).Times(2) shardContext.EXPECT().GetEventsCache().Return(mockCache) shardContext.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 2, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, MutableStateChecksumInvalidateBefore: dynamicproperties.GetFloatPropertyFn(10), MutableStateChecksumVerifyProbability: dynamicproperties.GetIntPropertyFilteredByDomain(0.0), MutableStateChecksumGenProbability: dynamicproperties.GetIntPropertyFilteredByDomain(0.0), HostName: "test-host", EnableReplicationTaskGeneration: func(string, string) bool { return true }, MaximumBufferedEventsBatch: func(...dynamicproperties.FilterOption) int { return 100 }, }).Times(1) shardContext.EXPECT().GetTimeSource().Return(clock.NewMockedTimeSource()) shardContext.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()) shardContext.EXPECT().GetDomainCache().Return(mockDomainCache).Times(1) if domainEntry == nil { domainEntry = constants.TestGlobalDomainEntry } msb := newMutableStateBuilder(shardContext, log.NewNoop(), domainEntry, domainEntry.GetFailoverVersion()) return msb } func TestLoad_ActiveActive(t *testing.T) { domainID := "test-domain-id" workflowID := "test-workflow-id" runID := "test-run-id" // Create domain entries for different test scenarios activeActiveDomainEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ ID: domainID, Name: "test-domain", }, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "cityID": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "seattle": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, "sydney": { ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, }, "regionID": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region0": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, "region1": { ActiveClusterName: "cluster1", FailoverVersion: 200, }, "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, }, }, }, }, }, 1, nil, 0, 0, 0, ) nonActiveActiveDomainEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ ID: domainID, Name: "test-domain", }, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{ ActiveClusterName: "cluster0", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "cluster0"}, }, }, 1, nil, 0, 0, 0, ) // Create base mutable state for testing baseMutableState := buildWorkflowMutableState() baseMutableState.ExecutionInfo = &persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: persistence.WorkflowStateRunning, } // Create base mutable state for testing (without version histories) baseMutableStateNoVersionHistory := buildWorkflowMutableState() baseMutableStateNoVersionHistory.ExecutionInfo = &persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: persistence.WorkflowStateRunning, } baseMutableStateNoVersionHistory.VersionHistories = nil tests := map[string]struct { domainEntry *cache.DomainCacheEntry mutableState *persistence.WorkflowMutableState activeClusterManagerAffordance func(activeClusterManager *activecluster.MockManager) expectedCurrentVersion int64 expectedErr error }{ "Non-active-active domain": { domainEntry: nonActiveActiveDomainEntry, mutableState: baseMutableState, activeClusterManagerAffordance: func(activeClusterManager *activecluster.MockManager) { }, expectedCurrentVersion: commonconstants.EmptyVersion, expectedErr: nil, }, "Active-active domain with nil version history - should return EmptyVersion": { domainEntry: activeActiveDomainEntry, mutableState: baseMutableStateNoVersionHistory, activeClusterManagerAffordance: func(activeClusterManager *activecluster.MockManager) { }, expectedCurrentVersion: commonconstants.EmptyVersion, // GetCurrentVersion returns EmptyVersion when versionHistories is nil expectedErr: nil, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) // Setup mocks shardContext := shard.NewMockContext(ctrl) mockCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) activeClusterManager := activecluster.NewMockManager(ctrl) // Set up shard context expectations shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() shardContext.EXPECT().GetActiveClusterManager().Return(activeClusterManager).AnyTimes() msb := createMSBWithMocks(mockCache, shardContext, mockDomainCache, td.domainEntry) // modify expectation from .Times(1) to .AnyTimes() for the load function shardContext.EXPECT().GetDomainCache().Return(mockDomainCache).AnyTimes() // Set up domain cache expectations mockDomainCache.EXPECT().GetDomainID(gomock.Any()).Return("some-domain-id", nil).AnyTimes() // Set up active cluster manager expectations based on test case td.activeClusterManagerAffordance(activeClusterManager) // Execute Load function err := msb.Load(context.Background(), td.mutableState) // Verify results if td.expectedErr != nil { require.Error(t, err) assert.Equal(t, td.expectedErr.Error(), err.Error()) } else { assert.NoError(t, err) assert.Equal(t, td.expectedCurrentVersion, msb.GetCurrentVersion()) } }) } } func TestStartTransaction(t *testing.T) { domainID := constants.TestDomainID workflowID := "test-workflow" runID := constants.TestRunID // Create domain entries for different test scenarios regularDomainEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ ID: domainID, Name: "test-domain", }, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{ ActiveClusterName: "cluster0", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: "cluster0"}, }, }, 123, // failover version nil, 0, 0, 0, ) activeActiveDomainEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ ID: domainID, Name: "test-aa-domain", }, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{ ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region0": { ActiveClusterName: "cluster0", FailoverVersion: 100, }, "region1": { ActiveClusterName: "cluster1", FailoverVersion: 200, }, }, }, }, }, }, 456, // failover version nil, 0, 0, 0, ) testCases := map[string]struct { domainEntry *cache.DomainCacheEntry setupActiveClusterManager func(activeClusterManager *activecluster.MockManager) setupMutableStateBuilder func(msBuilder *mutableStateBuilder) incomingTaskVersion int64 expectedFlushDecision bool expectedVersion int64 expectedError bool expectedErrorContains string }{ "it should successfully update the failover version": { domainEntry: regularDomainEntry, setupActiveClusterManager: func(activeClusterManager *activecluster.MockManager) { activeClusterManager.EXPECT().GetActiveClusterInfoByWorkflow( gomock.Any(), domainID, workflowID, runID, ).Return(&types.ActiveClusterInfo{ FailoverVersion: int64(123), }, nil).Times(1) }, setupMutableStateBuilder: func(msBuilder *mutableStateBuilder) {}, incomingTaskVersion: int64(100), expectedFlushDecision: false, expectedVersion: 123, expectedError: false, }, "when the domain is active-active domain it should update the failover version": { domainEntry: activeActiveDomainEntry, setupActiveClusterManager: func(activeClusterManager *activecluster.MockManager) { activeClusterManager.EXPECT().GetActiveClusterInfoByWorkflow( gomock.Any(), domainID, workflowID, runID, ).Return(&types.ActiveClusterInfo{ FailoverVersion: int64(999), }, nil).Times(1) }, setupMutableStateBuilder: func(msBuilder *mutableStateBuilder) {}, incomingTaskVersion: int64(200), expectedFlushDecision: false, expectedVersion: 999, expectedError: false, }, "when the domain is active-active and workflow lookup fails it should return an error": { domainEntry: activeActiveDomainEntry, setupActiveClusterManager: func(activeClusterManager *activecluster.MockManager) { activeClusterManager.EXPECT().GetActiveClusterInfoByWorkflow( gomock.Any(), domainID, workflowID, runID, ).Return(nil, errors.New("cluster lookup failed")).Times(1) }, setupMutableStateBuilder: func(msBuilder *mutableStateBuilder) {}, incomingTaskVersion: int64(300), expectedFlushDecision: false, expectedVersion: 0, // version won't be set due to error expectedError: true, expectedErrorContains: "cluster lookup failed", }, "when unable to update current version it should return an error": { domainEntry: regularDomainEntry, setupActiveClusterManager: func(activeClusterManager *activecluster.MockManager) { activeClusterManager.EXPECT().GetActiveClusterInfoByWorkflow( gomock.Any(), domainID, workflowID, runID, ).Return(&types.ActiveClusterInfo{ FailoverVersion: int64(123), }, nil).Times(1) }, setupMutableStateBuilder: func(msBuilder *mutableStateBuilder) { // Create empty version histories to trigger GetCurrentVersionHistory error msBuilder.versionHistories = &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{}, } }, incomingTaskVersion: int64(400), expectedFlushDecision: false, expectedVersion: 0, // version won't be updated due to error expectedError: true, expectedErrorContains: "getting branch index", }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Setup mocks shardContext := shard.NewMockContext(ctrl) mockCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) activeClusterManager := activecluster.NewMockManager(ctrl) // Set up shard context expectations shardContext.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() shardContext.EXPECT().GetActiveClusterManager().Return(activeClusterManager).AnyTimes() // Create mutable state builder with mocks msBuilder := createMSBWithMocks(mockCache, shardContext, mockDomainCache, tc.domainEntry) // Override shard context expectations for multiple calls shardContext.EXPECT().GetDomainCache().Return(mockDomainCache).AnyTimes() shardContext.EXPECT().GetConfig().Return(&config.Config{ HostName: "test-host", EnableReplicationTaskGeneration: func(string, string) bool { return true }, MaximumBufferedEventsBatch: func(...dynamicproperties.FilterOption) int { return 100 }, }).AnyTimes() // Set up basic execution info msBuilder.executionInfo = &persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, State: persistence.WorkflowStateRunning, NextEventID: int64(5), } // Set up version histories for UdpateCurrentVersion msBuilder.versionHistories = persistence.NewVersionHistories(&persistence.VersionHistory{ BranchToken: []byte("test-branch-token"), Items: []*persistence.VersionHistoryItem{ {EventID: 1, Version: 1}, }, }) // Apply test-specific setup tc.setupActiveClusterManager(activeClusterManager) tc.setupMutableStateBuilder(msBuilder) // Execute the function under test flushDecision, err := msBuilder.StartTransaction(context.Background(), tc.domainEntry, tc.incomingTaskVersion) // Verify results if tc.expectedError { assert.Error(t, err) if tc.expectedErrorContains != "" { assert.Contains(t, err.Error(), tc.expectedErrorContains) } assert.Equal(t, tc.expectedFlushDecision, flushDecision) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedFlushDecision, flushDecision) // Verify domain entry was updated assert.Equal(t, tc.domainEntry, msBuilder.domainEntry) // Verify current version was set to expected version assert.Equal(t, tc.expectedVersion, msBuilder.GetCurrentVersion()) } }) } } ================================================ FILE: service/history/execution/mutable_state_decision_task_manager.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination mutable_state_decision_task_manager_mock.go -self_package github.com/uber/cadence/service/history/execution package execution import ( "fmt" "runtime/debug" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( mutableStateDecisionTaskManager interface { ReplicateDecisionTaskScheduledEvent( version int64, scheduleID int64, taskList string, startToCloseTimeoutSeconds int32, attempt int64, scheduleTimestamp int64, originalScheduledTimestamp int64, bypassTaskGeneration bool, ) (*DecisionInfo, error) ReplicateTransientDecisionTaskScheduled() error ReplicateDecisionTaskStartedEvent( decision *DecisionInfo, version int64, scheduleID int64, startedID int64, requestID string, timestamp int64, ) (*DecisionInfo, error) ReplicateDecisionTaskCompletedEvent(event *types.HistoryEvent) error ReplicateDecisionTaskFailedEvent(*types.HistoryEvent) error ReplicateDecisionTaskTimedOutEvent(*types.HistoryEvent) error AddDecisionTaskScheduleToStartTimeoutEvent(scheduleEventID int64) (*types.HistoryEvent, error) AddDecisionTaskScheduledEventAsHeartbeat( bypassTaskGeneration bool, originalScheduledTimestamp int64, ) (*DecisionInfo, error) AddDecisionTaskScheduledEvent(bypassTaskGeneration bool) (*DecisionInfo, error) AddFirstDecisionTaskScheduled(startEvent *types.HistoryEvent) error AddDecisionTaskStartedEvent( scheduleEventID int64, requestID string, request *types.PollForDecisionTaskRequest, ) (*types.HistoryEvent, *DecisionInfo, error) AddDecisionTaskCompletedEvent( scheduleEventID int64, startedEventID int64, request *types.RespondDecisionTaskCompletedRequest, maxResetPoints int, ) (*types.HistoryEvent, error) AddDecisionTaskFailedEvent( scheduleEventID int64, startedEventID int64, cause types.DecisionTaskFailedCause, details []byte, identity string, reason string, binChecksum string, baseRunID string, newRunID string, forkEventVersion int64, resetRequestID string, ) (*types.HistoryEvent, error) AddDecisionTaskTimedOutEvent(scheduleEventID int64, startedEventID int64) (*types.HistoryEvent, error) AddDecisionTaskResetTimeoutEvent( scheduleEventID int64, baseRunID string, newRunID string, forkEventVersion int64, reason string, resetRequestID string, ) (*types.HistoryEvent, error) FailDecision(incrementAttempt bool) DeleteDecision() UpdateDecision(decision *DecisionInfo) HasPendingDecision() bool GetPendingDecision() (*DecisionInfo, bool) HasInFlightDecision() bool GetInFlightDecision() (*DecisionInfo, bool) HasProcessedOrPendingDecision() bool GetDecisionInfo(scheduleEventID int64) (*DecisionInfo, bool) GetDecisionScheduleToStartTimeout() time.Duration CreateTransientDecisionEvents(decision *DecisionInfo, identity string) (*types.HistoryEvent, *types.HistoryEvent) } mutableStateDecisionTaskManagerImpl struct { msb *mutableStateBuilder } ) func newMutableStateDecisionTaskManager(msb *mutableStateBuilder) mutableStateDecisionTaskManager { return &mutableStateDecisionTaskManagerImpl{ msb: msb, } } func (m *mutableStateDecisionTaskManagerImpl) ReplicateDecisionTaskScheduledEvent( version int64, scheduleID int64, taskList string, startToCloseTimeoutSeconds int32, attempt int64, scheduleTimestamp int64, originalScheduledTimestamp int64, bypassTaskGeneration bool, ) (*DecisionInfo, error) { // set workflow state to running, since decision is scheduled // NOTE: for zombie workflow, should not change the state state, _ := m.msb.GetWorkflowStateCloseStatus() if state != persistence.WorkflowStateZombie { if err := m.msb.UpdateWorkflowStateCloseStatus( persistence.WorkflowStateRunning, persistence.WorkflowCloseStatusNone, ); err != nil { return nil, err } } decision := &DecisionInfo{ Version: version, ScheduleID: scheduleID, StartedID: constants.EmptyEventID, RequestID: constants.EmptyUUID, DecisionTimeout: startToCloseTimeoutSeconds, TaskList: taskList, Attempt: attempt, ScheduledTimestamp: scheduleTimestamp, StartedTimestamp: 0, OriginalScheduledTimestamp: originalScheduledTimestamp, } m.UpdateDecision(decision) if !bypassTaskGeneration { if err := m.msb.taskGenerator.GenerateDecisionScheduleTasks(decision.ScheduleID); err != nil { return nil, err } } return decision, nil } func (m *mutableStateDecisionTaskManagerImpl) ReplicateTransientDecisionTaskScheduled() error { if m.HasPendingDecision() || m.msb.GetExecutionInfo().DecisionAttempt == 0 { return nil } // the schedule ID for this decision is guaranteed to be wrong // since the next event ID is assigned at the very end of when // all events are applied for replication. // this is OK // 1. if a failover happen just after this transient decision, // AddDecisionTaskStartedEvent will handle the correction of schedule ID // and set the attempt to 0 // 2. if no failover happen during the life time of this transient decision // then ReplicateDecisionTaskScheduledEvent will overwrite everything // including the decision schedule ID decision := &DecisionInfo{ Version: m.msb.GetCurrentVersion(), ScheduleID: m.msb.GetNextEventID(), StartedID: constants.EmptyEventID, RequestID: constants.EmptyUUID, DecisionTimeout: m.msb.GetExecutionInfo().DecisionStartToCloseTimeout, TaskList: m.msb.GetExecutionInfo().TaskList, Attempt: m.msb.GetExecutionInfo().DecisionAttempt, ScheduledTimestamp: m.msb.timeSource.Now().UnixNano(), StartedTimestamp: 0, } m.UpdateDecision(decision) return m.msb.taskGenerator.GenerateDecisionScheduleTasks(decision.ScheduleID) } func (m *mutableStateDecisionTaskManagerImpl) ReplicateDecisionTaskStartedEvent( decision *DecisionInfo, version int64, scheduleID int64, startedID int64, requestID string, timestamp int64, ) (*DecisionInfo, error) { // Replicator calls it with a nil decision info, and it is safe to always lookup the decision in this case as it // does not have to deal with transient decision case. var ok bool if decision == nil { decision, ok = m.GetDecisionInfo(scheduleID) if !ok { return nil, errors.NewInternalFailureError(fmt.Sprintf("unable to find decision: %v", scheduleID)) } // setting decision attempt to 0 for decision task replication // this mainly handles transient decision completion // for transient decision, active side will write 2 batch in a "transaction" // 1. decision task scheduled & decision task started // 2. decision task completed & other events // since we need to treat each individual event batch as one transaction // certain "magic" needs to be done, i.e. setting attempt to 0 so // if first batch is replicated, but not the second one, decision can be correctly timed out decision.Attempt = 0 } // Update mutable decision state decision = &DecisionInfo{ Version: version, ScheduleID: scheduleID, StartedID: startedID, RequestID: requestID, DecisionTimeout: decision.DecisionTimeout, Attempt: decision.Attempt, StartedTimestamp: timestamp, ScheduledTimestamp: decision.ScheduledTimestamp, TaskList: decision.TaskList, OriginalScheduledTimestamp: decision.OriginalScheduledTimestamp, } m.UpdateDecision(decision) return decision, m.msb.taskGenerator.GenerateDecisionStartTasks(scheduleID) } func (m *mutableStateDecisionTaskManagerImpl) ReplicateDecisionTaskCompletedEvent( event *types.HistoryEvent, ) error { m.beforeAddDecisionTaskCompletedEvent() maxResetPoints := constants.DefaultHistoryMaxAutoResetPoints // use default when it is not set in the config if m.msb.GetDomainEntry() != nil && m.msb.GetDomainEntry().GetInfo() != nil && m.msb.config != nil { domainName := m.msb.GetDomainEntry().GetInfo().Name maxResetPoints = m.msb.config.MaxAutoResetPoints(domainName) } return m.afterAddDecisionTaskCompletedEvent(event, maxResetPoints) } func (m *mutableStateDecisionTaskManagerImpl) ReplicateDecisionTaskFailedEvent(event *types.HistoryEvent) error { if event != nil && event.DecisionTaskFailedEventAttributes.GetCause() == types.DecisionTaskFailedCauseResetWorkflow { m.msb.insertWorkflowRequest(persistence.WorkflowRequest{ RequestID: event.DecisionTaskFailedEventAttributes.RequestID, Version: event.Version, RequestType: persistence.WorkflowRequestTypeReset, }) } m.FailDecision(true) return nil } func (m *mutableStateDecisionTaskManagerImpl) ReplicateDecisionTaskTimedOutEvent( event *types.HistoryEvent, ) error { timeoutType := event.DecisionTaskTimedOutEventAttributes.GetTimeoutType() incrementAttempt := true // Do not increment decision attempt in the case of sticky scheduleToStart timeout to // prevent creating next decision as transient // Note: this is just best effort, stickiness can be cleared before the timer fires, // and we can't tell is the decision that is having scheduleToStart timeout is sticky // or not. if timeoutType == types.TimeoutTypeScheduleToStart && m.msb.executionInfo.StickyTaskList != "" { incrementAttempt = false } if event.DecisionTaskTimedOutEventAttributes.GetCause() == types.DecisionTaskTimedOutCauseReset { m.msb.insertWorkflowRequest(persistence.WorkflowRequest{ RequestID: event.DecisionTaskTimedOutEventAttributes.GetRequestID(), Version: event.Version, RequestType: persistence.WorkflowRequestTypeReset, }) } m.FailDecision(incrementAttempt) return nil } func (m *mutableStateDecisionTaskManagerImpl) AddDecisionTaskScheduleToStartTimeoutEvent( scheduleEventID int64, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionDecisionTaskTimedOut if m.msb.executionInfo.DecisionScheduleID != scheduleEventID || m.msb.executionInfo.DecisionStartedID > 0 { m.msb.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(m.msb.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowScheduleID(scheduleEventID), ) return nil, m.msb.createInternalServerError(opTag) } var event *types.HistoryEvent // stickyness will be cleared in ReplicateDecisionTaskTimedOutEvent // Avoid creating new history events when decisions are continuously timing out if m.msb.executionInfo.DecisionAttempt == 0 { event = m.msb.hBuilder.AddDecisionTaskTimedOutEvent( scheduleEventID, 0, types.TimeoutTypeScheduleToStart, "", "", constants.EmptyVersion, "", types.DecisionTaskTimedOutCauseTimeout, "", ) if err := m.ReplicateDecisionTaskTimedOutEvent(event); err != nil { return nil, err } } else { if err := m.ReplicateDecisionTaskTimedOutEvent(&types.HistoryEvent{ DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ TimeoutType: types.TimeoutTypeScheduleToStart.Ptr(), }, }); err != nil { return nil, err } } return event, nil } func (m *mutableStateDecisionTaskManagerImpl) AddDecisionTaskResetTimeoutEvent( scheduleEventID int64, baseRunID string, newRunID string, forkEventVersion int64, reason string, resetRequestID string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionDecisionTaskTimedOut if m.msb.executionInfo.DecisionScheduleID != scheduleEventID { m.msb.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(m.msb.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowScheduleID(scheduleEventID), ) return nil, m.msb.createInternalServerError(opTag) } event := m.msb.hBuilder.AddDecisionTaskTimedOutEvent( scheduleEventID, 0, types.TimeoutTypeScheduleToStart, baseRunID, newRunID, forkEventVersion, reason, types.DecisionTaskTimedOutCauseReset, resetRequestID, ) if err := m.ReplicateDecisionTaskTimedOutEvent(event); err != nil { return nil, err } // always clear decision attempt for reset m.msb.executionInfo.DecisionAttempt = 0 return event, nil } // originalScheduledTimestamp is to record the first scheduled decision during decision heartbeat. func (m *mutableStateDecisionTaskManagerImpl) AddDecisionTaskScheduledEventAsHeartbeat( bypassTaskGeneration bool, originalScheduledTimestamp int64, ) (*DecisionInfo, error) { opTag := tag.WorkflowActionDecisionTaskScheduled if m.HasPendingDecision() { m.msb.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(m.msb.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowScheduleID(m.msb.executionInfo.DecisionScheduleID)) return nil, m.msb.createInternalServerError(opTag) } // Tasklist and decision timeout should already be set from workflow execution started event taskList := m.msb.executionInfo.TaskList if m.msb.IsStickyTaskListEnabled() { taskList = m.msb.executionInfo.StickyTaskList } else { // It can be because stickyness has expired due to StickyTTL config // In that case we need to clear stickyness so that the LastUpdateTimestamp is not corrupted. // In other cases, clearing stickyness shouldn't hurt anything. // TODO: https://github.com/uber/cadence/issues/2357: // if we can use a new field(LastDecisionUpdateTimestamp), then we could get rid of it. m.msb.ClearStickyness() } startToCloseTimeoutSeconds := m.msb.executionInfo.DecisionStartToCloseTimeout // Flush any buffered events before creating the decision, otherwise it will result in invalid IDs for transient // decision and will cause in timeout processing to not work for transient decisions if m.msb.HasBufferedEvents() { // if creating a decision and in the mean time events are flushed from buffered events // than this decision cannot be a transient decision m.msb.executionInfo.DecisionAttempt = 0 if err := m.msb.FlushBufferedEvents(); err != nil { return nil, err } } var newDecisionEvent *types.HistoryEvent scheduleID := m.msb.GetNextEventID() // we will generate the schedule event later for repeatedly failing decisions // Avoid creating new history events when decisions are continuously failing scheduleTime := m.msb.timeSource.Now().UnixNano() useNonTransientDecision := m.shouldUpdateLastWriteVersion() if m.msb.executionInfo.DecisionAttempt == 0 || useNonTransientDecision { newDecisionEvent = m.msb.hBuilder.AddDecisionTaskScheduledEvent( taskList, startToCloseTimeoutSeconds, m.msb.executionInfo.DecisionAttempt) scheduleID = newDecisionEvent.ID scheduleTime = newDecisionEvent.GetTimestamp() m.msb.executionInfo.DecisionAttempt = 0 } return m.ReplicateDecisionTaskScheduledEvent( m.msb.GetCurrentVersion(), scheduleID, taskList, startToCloseTimeoutSeconds, m.msb.executionInfo.DecisionAttempt, scheduleTime, originalScheduledTimestamp, bypassTaskGeneration, ) } func (m *mutableStateDecisionTaskManagerImpl) AddDecisionTaskScheduledEvent( bypassTaskGeneration bool, ) (*DecisionInfo, error) { return m.AddDecisionTaskScheduledEventAsHeartbeat(bypassTaskGeneration, m.msb.timeSource.Now().UnixNano()) } func (m *mutableStateDecisionTaskManagerImpl) AddFirstDecisionTaskScheduled( startEvent *types.HistoryEvent, ) error { // handle first decision case, i.e. possible delayed decision // // below handles the following cases: // 1. if not continue as new & if workflow has no parent // -> schedule decision & schedule delayed decision // 2. if not continue as new & if workflow has parent // -> this function should not be called during workflow start, but should be called as // part of schedule decision in 2 phase commit // // if continue as new // 1. whether has parent workflow or not // -> schedule decision & schedule delayed decision // startAttr := startEvent.WorkflowExecutionStartedEventAttributes decisionBackoffDuration := time.Duration(startAttr.GetFirstDecisionTaskBackoffSeconds()) * time.Second var err error if decisionBackoffDuration != 0 { if err = m.msb.taskGenerator.GenerateDelayedDecisionTasks( startEvent, ); err != nil { return err } } else { if _, err = m.AddDecisionTaskScheduledEvent( false, ); err != nil { return err } } return nil } func (m *mutableStateDecisionTaskManagerImpl) AddDecisionTaskStartedEvent( scheduleEventID int64, requestID string, request *types.PollForDecisionTaskRequest, ) (*types.HistoryEvent, *DecisionInfo, error) { opTag := tag.WorkflowActionDecisionTaskStarted decision, ok := m.GetDecisionInfo(scheduleEventID) if !ok || decision.StartedID != constants.EmptyEventID { m.msb.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(m.msb.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowScheduleID(scheduleEventID)) return nil, nil, m.msb.createInternalServerError(opTag) } var event *types.HistoryEvent scheduleID := decision.ScheduleID startedID := scheduleID + 1 tasklist := request.TaskList.GetName() startTime := m.msb.timeSource.Now().UnixNano() useNonTransientDecision := m.shouldUpdateLastWriteVersion() // First check to see if new events came since transient decision was scheduled if decision.Attempt > 0 && (decision.ScheduleID != m.msb.GetNextEventID() || useNonTransientDecision) { // Also create a new DecisionTaskScheduledEvent since new events came in when it was scheduled scheduleEvent := m.msb.hBuilder.AddDecisionTaskScheduledEvent(tasklist, decision.DecisionTimeout, 0) scheduleID = scheduleEvent.ID decision.Attempt = 0 } // Avoid creating new history events when decisions are continuously failing if decision.Attempt == 0 { // Now create DecisionTaskStartedEvent event = m.msb.hBuilder.AddDecisionTaskStartedEvent(scheduleID, requestID, request.GetIdentity()) startedID = event.ID startTime = event.GetTimestamp() } decision, err := m.ReplicateDecisionTaskStartedEvent(decision, m.msb.GetCurrentVersion(), scheduleID, startedID, requestID, startTime) return event, decision, err } func (m *mutableStateDecisionTaskManagerImpl) AddDecisionTaskCompletedEvent( scheduleEventID int64, startedEventID int64, request *types.RespondDecisionTaskCompletedRequest, maxResetPoints int, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionDecisionTaskCompleted decision, ok := m.GetDecisionInfo(scheduleEventID) if !ok || decision.StartedID != startedEventID { m.msb.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(m.msb.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowScheduleID(scheduleEventID), tag.WorkflowStartedID(startedEventID)) return nil, m.msb.createInternalServerError(opTag) } m.beforeAddDecisionTaskCompletedEvent() if decision.Attempt > 0 { // Create corresponding DecisionTaskSchedule and DecisionTaskStarted events for decisions we have been retrying scheduledEvent := m.msb.hBuilder.AddTransientDecisionTaskScheduledEvent(m.msb.executionInfo.TaskList, decision.DecisionTimeout, decision.Attempt, decision.ScheduledTimestamp) startedEvent := m.msb.hBuilder.AddTransientDecisionTaskStartedEvent(scheduledEvent.ID, decision.RequestID, request.GetIdentity(), decision.StartedTimestamp) startedEventID = startedEvent.ID } // Now write the completed event event := m.msb.hBuilder.AddDecisionTaskCompletedEvent(scheduleEventID, startedEventID, request) err := m.afterAddDecisionTaskCompletedEvent(event, maxResetPoints) if err != nil { return nil, err } return event, nil } func (m *mutableStateDecisionTaskManagerImpl) AddDecisionTaskFailedEvent( scheduleEventID int64, startedEventID int64, cause types.DecisionTaskFailedCause, details []byte, identity string, reason string, binChecksum string, baseRunID string, newRunID string, forkEventVersion int64, resetRequestID string, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionDecisionTaskFailed attr := types.DecisionTaskFailedEventAttributes{ ScheduledEventID: scheduleEventID, StartedEventID: startedEventID, Cause: cause.Ptr(), Details: details, Identity: identity, Reason: common.StringPtr(reason), BinaryChecksum: binChecksum, BaseRunID: baseRunID, NewRunID: newRunID, ForkEventVersion: forkEventVersion, RequestID: resetRequestID, } dt, ok := m.GetDecisionInfo(scheduleEventID) if !ok || dt.StartedID != startedEventID { m.msb.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(m.msb.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowScheduleID(scheduleEventID), tag.WorkflowStartedID(startedEventID)) return nil, m.msb.createInternalServerError(opTag) } var event *types.HistoryEvent // Only emit DecisionTaskFailedEvent for the very first time if dt.Attempt == 0 { event = m.msb.hBuilder.AddDecisionTaskFailedEvent(attr) } if err := m.ReplicateDecisionTaskFailedEvent(event); err != nil { return nil, err } // always clear decision attempt for reset if cause == types.DecisionTaskFailedCauseResetWorkflow || cause == types.DecisionTaskFailedCauseFailoverCloseDecision { m.msb.executionInfo.DecisionAttempt = 0 } return event, nil } func (m *mutableStateDecisionTaskManagerImpl) AddDecisionTaskTimedOutEvent( scheduleEventID int64, startedEventID int64, ) (*types.HistoryEvent, error) { opTag := tag.WorkflowActionDecisionTaskTimedOut dt, ok := m.GetDecisionInfo(scheduleEventID) if !ok || dt.StartedID != startedEventID { m.msb.logger.Warn(mutableStateInvalidHistoryActionMsg, opTag, tag.WorkflowEventID(m.msb.GetNextEventID()), tag.ErrorTypeInvalidHistoryAction, tag.WorkflowScheduleID(scheduleEventID), tag.WorkflowStartedID(startedEventID)) return nil, m.msb.createInternalServerError(opTag) } event := &types.HistoryEvent{ DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, } // Avoid creating new history events when decisions are continuously timing out if dt.Attempt == 0 { event = m.msb.hBuilder.AddDecisionTaskTimedOutEvent( scheduleEventID, startedEventID, types.TimeoutTypeStartToClose, "", "", constants.EmptyVersion, "", types.DecisionTaskTimedOutCauseTimeout, "", ) } if err := m.ReplicateDecisionTaskTimedOutEvent(event); err != nil { return nil, err } return event, nil } func (m *mutableStateDecisionTaskManagerImpl) FailDecision( incrementAttempt bool, ) { // Clear stickiness whenever decision fails m.msb.ClearStickyness() failDecisionInfo := &DecisionInfo{ Version: constants.EmptyVersion, ScheduleID: constants.EmptyEventID, StartedID: constants.EmptyEventID, RequestID: constants.EmptyUUID, DecisionTimeout: 0, StartedTimestamp: 0, TaskList: "", OriginalScheduledTimestamp: 0, } if incrementAttempt { failDecisionInfo.Attempt = m.msb.executionInfo.DecisionAttempt + 1 failDecisionInfo.ScheduledTimestamp = m.msb.timeSource.Now().UnixNano() if failDecisionInfo.Attempt >= int64(m.msb.shard.GetConfig().DecisionRetryCriticalAttempts()) { domainName := m.msb.GetDomainEntry().GetInfo().Name domainTag := metrics.DomainTag(domainName) m.msb.metricsClient.Scope(metrics.WorkflowContextScope, domainTag).RecordTimer(metrics.DecisionAttemptTimer, time.Duration(failDecisionInfo.Attempt)) m.msb.logger.Warn("Critical error processing decision task, retrying.", tag.WorkflowDomainName(m.msb.GetDomainEntry().GetInfo().Name), tag.WorkflowID(m.msb.GetExecutionInfo().WorkflowID), tag.WorkflowRunID(m.msb.GetExecutionInfo().RunID), ) } } m.UpdateDecision(failDecisionInfo) } // DeleteDecision deletes a decision task. func (m *mutableStateDecisionTaskManagerImpl) DeleteDecision() { resetDecisionInfo := &DecisionInfo{ Version: constants.EmptyVersion, ScheduleID: constants.EmptyEventID, StartedID: constants.EmptyEventID, RequestID: constants.EmptyUUID, DecisionTimeout: 0, Attempt: 0, StartedTimestamp: 0, ScheduledTimestamp: 0, TaskList: "", // Keep the last original scheduled timestamp, so that AddDecisionAsHeartbeat can continue with it. OriginalScheduledTimestamp: m.getDecisionInfo().OriginalScheduledTimestamp, } m.UpdateDecision(resetDecisionInfo) } // UpdateDecision updates a decision task. func (m *mutableStateDecisionTaskManagerImpl) UpdateDecision( decision *DecisionInfo, ) { m.msb.executionInfo.DecisionVersion = decision.Version m.msb.executionInfo.DecisionScheduleID = decision.ScheduleID m.msb.executionInfo.DecisionStartedID = decision.StartedID m.msb.executionInfo.DecisionRequestID = decision.RequestID m.msb.executionInfo.DecisionTimeout = decision.DecisionTimeout m.msb.executionInfo.DecisionAttempt = decision.Attempt m.msb.executionInfo.DecisionStartedTimestamp = decision.StartedTimestamp m.msb.executionInfo.DecisionScheduledTimestamp = decision.ScheduledTimestamp m.msb.executionInfo.DecisionOriginalScheduledTimestamp = decision.OriginalScheduledTimestamp // NOTE: do not update tasklist in execution info if m.msb.logger.DebugOn() { m.msb.logger.Debugf( "Decision Updated: {Schedule: %v, Started: %v, ID: %v, Timeout: %v, Attempt: %v, Timestamp: %v}, Stacktrace: %v", decision.ScheduleID, decision.StartedID, decision.RequestID, decision.DecisionTimeout, decision.Attempt, decision.StartedTimestamp, debug.Stack(), ) } } func (m *mutableStateDecisionTaskManagerImpl) HasPendingDecision() bool { return m.msb.executionInfo.DecisionScheduleID != constants.EmptyEventID } func (m *mutableStateDecisionTaskManagerImpl) GetPendingDecision() (*DecisionInfo, bool) { if m.msb.executionInfo.DecisionScheduleID == constants.EmptyEventID { return nil, false } decision := m.getDecisionInfo() return decision, true } func (m *mutableStateDecisionTaskManagerImpl) HasInFlightDecision() bool { return m.msb.executionInfo.DecisionStartedID > 0 } func (m *mutableStateDecisionTaskManagerImpl) GetInFlightDecision() (*DecisionInfo, bool) { if m.msb.executionInfo.DecisionScheduleID == constants.EmptyEventID || m.msb.executionInfo.DecisionStartedID == constants.EmptyEventID { return nil, false } decision := m.getDecisionInfo() return decision, true } func (m *mutableStateDecisionTaskManagerImpl) HasProcessedOrPendingDecision() bool { return m.HasPendingDecision() || m.msb.GetPreviousStartedEventID() != constants.EmptyEventID } // GetDecisionInfo returns details about the in-progress decision task func (m *mutableStateDecisionTaskManagerImpl) GetDecisionInfo( scheduleEventID int64, ) (*DecisionInfo, bool) { decision := m.getDecisionInfo() if scheduleEventID == decision.ScheduleID { return decision, true } return nil, false } func (m *mutableStateDecisionTaskManagerImpl) GetDecisionScheduleToStartTimeout() time.Duration { // we should not call IsStickyTaskListEnabled which may be false // if sticky TTL has expired // NOTE: this function is called in the same mutable state transaction as the one generating the decision task // we stickiness won't be cleared between creating the decision and getting the timeout if m.msb.executionInfo.StickyTaskList != "" { return time.Duration( m.msb.executionInfo.StickyScheduleToStartTimeout, ) * time.Second } domainName := m.msb.GetDomainEntry().GetInfo().Name if m.msb.executionInfo.DecisionAttempt < int64(m.msb.config.NormalDecisionScheduleToStartMaxAttempts(domainName)) { return m.msb.config.NormalDecisionScheduleToStartTimeout(domainName) } return 0 } func (m *mutableStateDecisionTaskManagerImpl) CreateTransientDecisionEvents( decision *DecisionInfo, identity string, ) (*types.HistoryEvent, *types.HistoryEvent) { tasklist := m.msb.executionInfo.TaskList scheduledEvent := newDecisionTaskScheduledEventWithInfo( decision.ScheduleID, decision.ScheduledTimestamp, tasklist, decision.DecisionTimeout, decision.Attempt, ) startedEvent := newDecisionTaskStartedEventWithInfo( decision.StartedID, decision.StartedTimestamp, decision.ScheduleID, decision.RequestID, identity, ) return scheduledEvent, startedEvent } func (m *mutableStateDecisionTaskManagerImpl) getDecisionInfo() *DecisionInfo { taskList := m.msb.executionInfo.TaskList if m.msb.executionInfo.StickyTaskList != "" { taskList = m.msb.executionInfo.StickyTaskList } return &DecisionInfo{ Version: m.msb.executionInfo.DecisionVersion, ScheduleID: m.msb.executionInfo.DecisionScheduleID, StartedID: m.msb.executionInfo.DecisionStartedID, RequestID: m.msb.executionInfo.DecisionRequestID, DecisionTimeout: m.msb.executionInfo.DecisionTimeout, Attempt: m.msb.executionInfo.DecisionAttempt, StartedTimestamp: m.msb.executionInfo.DecisionStartedTimestamp, ScheduledTimestamp: m.msb.executionInfo.DecisionScheduledTimestamp, TaskList: taskList, OriginalScheduledTimestamp: m.msb.executionInfo.DecisionOriginalScheduledTimestamp, } } func (m *mutableStateDecisionTaskManagerImpl) beforeAddDecisionTaskCompletedEvent() { // Make sure to delete decision before adding events. Otherwise they are buffered rather than getting appended m.DeleteDecision() } func (m *mutableStateDecisionTaskManagerImpl) afterAddDecisionTaskCompletedEvent( event *types.HistoryEvent, maxResetPoints int, ) error { m.msb.executionInfo.LastProcessedEvent = event.GetDecisionTaskCompletedEventAttributes().GetStartedEventID() return m.msb.addBinaryCheckSumIfNotExists(event, maxResetPoints) } func (m *mutableStateDecisionTaskManagerImpl) shouldUpdateLastWriteVersion() bool { currentVersion := m.msb.GetCurrentVersion() lastWriteVersion, err := m.msb.GetLastWriteVersion() if err != nil { // The error is version history has no item. This is expected for the first batch of a workflow. return false } return currentVersion != lastWriteVersion } ================================================ FILE: service/history/execution/mutable_state_decision_task_manager_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: mutable_state_decision_task_manager.go // // Generated by this command: // // mockgen -package execution -source mutable_state_decision_task_manager.go -destination mutable_state_decision_task_manager_mock.go -self_package github.com/uber/cadence/service/history/execution // // Package execution is a generated GoMock package. package execution import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockmutableStateDecisionTaskManager is a mock of mutableStateDecisionTaskManager interface. type MockmutableStateDecisionTaskManager struct { ctrl *gomock.Controller recorder *MockmutableStateDecisionTaskManagerMockRecorder isgomock struct{} } // MockmutableStateDecisionTaskManagerMockRecorder is the mock recorder for MockmutableStateDecisionTaskManager. type MockmutableStateDecisionTaskManagerMockRecorder struct { mock *MockmutableStateDecisionTaskManager } // NewMockmutableStateDecisionTaskManager creates a new mock instance. func NewMockmutableStateDecisionTaskManager(ctrl *gomock.Controller) *MockmutableStateDecisionTaskManager { mock := &MockmutableStateDecisionTaskManager{ctrl: ctrl} mock.recorder = &MockmutableStateDecisionTaskManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockmutableStateDecisionTaskManager) EXPECT() *MockmutableStateDecisionTaskManagerMockRecorder { return m.recorder } // AddDecisionTaskCompletedEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) AddDecisionTaskCompletedEvent(scheduleEventID, startedEventID int64, request *types.RespondDecisionTaskCompletedRequest, maxResetPoints int) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskCompletedEvent", scheduleEventID, startedEventID, request, maxResetPoints) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskCompletedEvent indicates an expected call of AddDecisionTaskCompletedEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) AddDecisionTaskCompletedEvent(scheduleEventID, startedEventID, request, maxResetPoints any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskCompletedEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).AddDecisionTaskCompletedEvent), scheduleEventID, startedEventID, request, maxResetPoints) } // AddDecisionTaskFailedEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) AddDecisionTaskFailedEvent(scheduleEventID, startedEventID int64, cause types.DecisionTaskFailedCause, details []byte, identity, reason, binChecksum, baseRunID, newRunID string, forkEventVersion int64, resetRequestID string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskFailedEvent", scheduleEventID, startedEventID, cause, details, identity, reason, binChecksum, baseRunID, newRunID, forkEventVersion, resetRequestID) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskFailedEvent indicates an expected call of AddDecisionTaskFailedEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) AddDecisionTaskFailedEvent(scheduleEventID, startedEventID, cause, details, identity, reason, binChecksum, baseRunID, newRunID, forkEventVersion, resetRequestID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskFailedEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).AddDecisionTaskFailedEvent), scheduleEventID, startedEventID, cause, details, identity, reason, binChecksum, baseRunID, newRunID, forkEventVersion, resetRequestID) } // AddDecisionTaskResetTimeoutEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) AddDecisionTaskResetTimeoutEvent(scheduleEventID int64, baseRunID, newRunID string, forkEventVersion int64, reason, resetRequestID string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskResetTimeoutEvent", scheduleEventID, baseRunID, newRunID, forkEventVersion, reason, resetRequestID) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskResetTimeoutEvent indicates an expected call of AddDecisionTaskResetTimeoutEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) AddDecisionTaskResetTimeoutEvent(scheduleEventID, baseRunID, newRunID, forkEventVersion, reason, resetRequestID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskResetTimeoutEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).AddDecisionTaskResetTimeoutEvent), scheduleEventID, baseRunID, newRunID, forkEventVersion, reason, resetRequestID) } // AddDecisionTaskScheduleToStartTimeoutEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) AddDecisionTaskScheduleToStartTimeoutEvent(scheduleEventID int64) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskScheduleToStartTimeoutEvent", scheduleEventID) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskScheduleToStartTimeoutEvent indicates an expected call of AddDecisionTaskScheduleToStartTimeoutEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) AddDecisionTaskScheduleToStartTimeoutEvent(scheduleEventID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskScheduleToStartTimeoutEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).AddDecisionTaskScheduleToStartTimeoutEvent), scheduleEventID) } // AddDecisionTaskScheduledEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) AddDecisionTaskScheduledEvent(bypassTaskGeneration bool) (*DecisionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskScheduledEvent", bypassTaskGeneration) ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskScheduledEvent indicates an expected call of AddDecisionTaskScheduledEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) AddDecisionTaskScheduledEvent(bypassTaskGeneration any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskScheduledEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).AddDecisionTaskScheduledEvent), bypassTaskGeneration) } // AddDecisionTaskScheduledEventAsHeartbeat mocks base method. func (m *MockmutableStateDecisionTaskManager) AddDecisionTaskScheduledEventAsHeartbeat(bypassTaskGeneration bool, originalScheduledTimestamp int64) (*DecisionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskScheduledEventAsHeartbeat", bypassTaskGeneration, originalScheduledTimestamp) ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskScheduledEventAsHeartbeat indicates an expected call of AddDecisionTaskScheduledEventAsHeartbeat. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) AddDecisionTaskScheduledEventAsHeartbeat(bypassTaskGeneration, originalScheduledTimestamp any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskScheduledEventAsHeartbeat", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).AddDecisionTaskScheduledEventAsHeartbeat), bypassTaskGeneration, originalScheduledTimestamp) } // AddDecisionTaskStartedEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) AddDecisionTaskStartedEvent(scheduleEventID int64, requestID string, request *types.PollForDecisionTaskRequest) (*types.HistoryEvent, *DecisionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskStartedEvent", scheduleEventID, requestID, request) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(*DecisionInfo) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // AddDecisionTaskStartedEvent indicates an expected call of AddDecisionTaskStartedEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) AddDecisionTaskStartedEvent(scheduleEventID, requestID, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskStartedEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).AddDecisionTaskStartedEvent), scheduleEventID, requestID, request) } // AddDecisionTaskTimedOutEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) AddDecisionTaskTimedOutEvent(scheduleEventID, startedEventID int64) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskTimedOutEvent", scheduleEventID, startedEventID) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskTimedOutEvent indicates an expected call of AddDecisionTaskTimedOutEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) AddDecisionTaskTimedOutEvent(scheduleEventID, startedEventID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskTimedOutEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).AddDecisionTaskTimedOutEvent), scheduleEventID, startedEventID) } // AddFirstDecisionTaskScheduled mocks base method. func (m *MockmutableStateDecisionTaskManager) AddFirstDecisionTaskScheduled(startEvent *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddFirstDecisionTaskScheduled", startEvent) ret0, _ := ret[0].(error) return ret0 } // AddFirstDecisionTaskScheduled indicates an expected call of AddFirstDecisionTaskScheduled. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) AddFirstDecisionTaskScheduled(startEvent any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFirstDecisionTaskScheduled", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).AddFirstDecisionTaskScheduled), startEvent) } // CreateTransientDecisionEvents mocks base method. func (m *MockmutableStateDecisionTaskManager) CreateTransientDecisionEvents(decision *DecisionInfo, identity string) (*types.HistoryEvent, *types.HistoryEvent) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateTransientDecisionEvents", decision, identity) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(*types.HistoryEvent) return ret0, ret1 } // CreateTransientDecisionEvents indicates an expected call of CreateTransientDecisionEvents. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) CreateTransientDecisionEvents(decision, identity any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTransientDecisionEvents", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).CreateTransientDecisionEvents), decision, identity) } // DeleteDecision mocks base method. func (m *MockmutableStateDecisionTaskManager) DeleteDecision() { m.ctrl.T.Helper() m.ctrl.Call(m, "DeleteDecision") } // DeleteDecision indicates an expected call of DeleteDecision. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) DeleteDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDecision", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).DeleteDecision)) } // FailDecision mocks base method. func (m *MockmutableStateDecisionTaskManager) FailDecision(incrementAttempt bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "FailDecision", incrementAttempt) } // FailDecision indicates an expected call of FailDecision. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) FailDecision(incrementAttempt any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FailDecision", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).FailDecision), incrementAttempt) } // GetDecisionInfo mocks base method. func (m *MockmutableStateDecisionTaskManager) GetDecisionInfo(scheduleEventID int64) (*DecisionInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDecisionInfo", scheduleEventID) ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetDecisionInfo indicates an expected call of GetDecisionInfo. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) GetDecisionInfo(scheduleEventID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDecisionInfo", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).GetDecisionInfo), scheduleEventID) } // GetDecisionScheduleToStartTimeout mocks base method. func (m *MockmutableStateDecisionTaskManager) GetDecisionScheduleToStartTimeout() time.Duration { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDecisionScheduleToStartTimeout") ret0, _ := ret[0].(time.Duration) return ret0 } // GetDecisionScheduleToStartTimeout indicates an expected call of GetDecisionScheduleToStartTimeout. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) GetDecisionScheduleToStartTimeout() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDecisionScheduleToStartTimeout", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).GetDecisionScheduleToStartTimeout)) } // GetInFlightDecision mocks base method. func (m *MockmutableStateDecisionTaskManager) GetInFlightDecision() (*DecisionInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetInFlightDecision") ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetInFlightDecision indicates an expected call of GetInFlightDecision. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) GetInFlightDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInFlightDecision", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).GetInFlightDecision)) } // GetPendingDecision mocks base method. func (m *MockmutableStateDecisionTaskManager) GetPendingDecision() (*DecisionInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPendingDecision") ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetPendingDecision indicates an expected call of GetPendingDecision. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) GetPendingDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingDecision", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).GetPendingDecision)) } // HasInFlightDecision mocks base method. func (m *MockmutableStateDecisionTaskManager) HasInFlightDecision() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasInFlightDecision") ret0, _ := ret[0].(bool) return ret0 } // HasInFlightDecision indicates an expected call of HasInFlightDecision. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) HasInFlightDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasInFlightDecision", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).HasInFlightDecision)) } // HasPendingDecision mocks base method. func (m *MockmutableStateDecisionTaskManager) HasPendingDecision() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasPendingDecision") ret0, _ := ret[0].(bool) return ret0 } // HasPendingDecision indicates an expected call of HasPendingDecision. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) HasPendingDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasPendingDecision", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).HasPendingDecision)) } // HasProcessedOrPendingDecision mocks base method. func (m *MockmutableStateDecisionTaskManager) HasProcessedOrPendingDecision() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasProcessedOrPendingDecision") ret0, _ := ret[0].(bool) return ret0 } // HasProcessedOrPendingDecision indicates an expected call of HasProcessedOrPendingDecision. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) HasProcessedOrPendingDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasProcessedOrPendingDecision", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).HasProcessedOrPendingDecision)) } // ReplicateDecisionTaskCompletedEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) ReplicateDecisionTaskCompletedEvent(event *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateDecisionTaskCompletedEvent", event) ret0, _ := ret[0].(error) return ret0 } // ReplicateDecisionTaskCompletedEvent indicates an expected call of ReplicateDecisionTaskCompletedEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) ReplicateDecisionTaskCompletedEvent(event any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateDecisionTaskCompletedEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).ReplicateDecisionTaskCompletedEvent), event) } // ReplicateDecisionTaskFailedEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) ReplicateDecisionTaskFailedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateDecisionTaskFailedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateDecisionTaskFailedEvent indicates an expected call of ReplicateDecisionTaskFailedEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) ReplicateDecisionTaskFailedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateDecisionTaskFailedEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).ReplicateDecisionTaskFailedEvent), arg0) } // ReplicateDecisionTaskScheduledEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) ReplicateDecisionTaskScheduledEvent(version, scheduleID int64, taskList string, startToCloseTimeoutSeconds int32, attempt, scheduleTimestamp, originalScheduledTimestamp int64, bypassTaskGeneration bool) (*DecisionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateDecisionTaskScheduledEvent", version, scheduleID, taskList, startToCloseTimeoutSeconds, attempt, scheduleTimestamp, originalScheduledTimestamp, bypassTaskGeneration) ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicateDecisionTaskScheduledEvent indicates an expected call of ReplicateDecisionTaskScheduledEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) ReplicateDecisionTaskScheduledEvent(version, scheduleID, taskList, startToCloseTimeoutSeconds, attempt, scheduleTimestamp, originalScheduledTimestamp, bypassTaskGeneration any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateDecisionTaskScheduledEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).ReplicateDecisionTaskScheduledEvent), version, scheduleID, taskList, startToCloseTimeoutSeconds, attempt, scheduleTimestamp, originalScheduledTimestamp, bypassTaskGeneration) } // ReplicateDecisionTaskStartedEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) ReplicateDecisionTaskStartedEvent(decision *DecisionInfo, version, scheduleID, startedID int64, requestID string, timestamp int64) (*DecisionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateDecisionTaskStartedEvent", decision, version, scheduleID, startedID, requestID, timestamp) ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicateDecisionTaskStartedEvent indicates an expected call of ReplicateDecisionTaskStartedEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) ReplicateDecisionTaskStartedEvent(decision, version, scheduleID, startedID, requestID, timestamp any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateDecisionTaskStartedEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).ReplicateDecisionTaskStartedEvent), decision, version, scheduleID, startedID, requestID, timestamp) } // ReplicateDecisionTaskTimedOutEvent mocks base method. func (m *MockmutableStateDecisionTaskManager) ReplicateDecisionTaskTimedOutEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateDecisionTaskTimedOutEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateDecisionTaskTimedOutEvent indicates an expected call of ReplicateDecisionTaskTimedOutEvent. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) ReplicateDecisionTaskTimedOutEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateDecisionTaskTimedOutEvent", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).ReplicateDecisionTaskTimedOutEvent), arg0) } // ReplicateTransientDecisionTaskScheduled mocks base method. func (m *MockmutableStateDecisionTaskManager) ReplicateTransientDecisionTaskScheduled() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateTransientDecisionTaskScheduled") ret0, _ := ret[0].(error) return ret0 } // ReplicateTransientDecisionTaskScheduled indicates an expected call of ReplicateTransientDecisionTaskScheduled. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) ReplicateTransientDecisionTaskScheduled() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateTransientDecisionTaskScheduled", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).ReplicateTransientDecisionTaskScheduled)) } // UpdateDecision mocks base method. func (m *MockmutableStateDecisionTaskManager) UpdateDecision(decision *DecisionInfo) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdateDecision", decision) } // UpdateDecision indicates an expected call of UpdateDecision. func (mr *MockmutableStateDecisionTaskManagerMockRecorder) UpdateDecision(decision any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDecision", reflect.TypeOf((*MockmutableStateDecisionTaskManager)(nil).UpdateDecision), decision) } ================================================ FILE: service/history/execution/mutable_state_decision_task_manager_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "errors" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" "github.com/uber/cadence/common/clock" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/shard" ) func TestReplicateDecisionTaskCompletedEvent(t *testing.T) { mockShard := shard.NewTestContext( t, gomock.NewController(t), &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) mockShard.GetConfig().MutableStateChecksumGenProbability = func(domain string) int { return 100 } mockShard.GetConfig().MutableStateChecksumVerifyProbability = func(domain string) int { return 100 } logger := mockShard.GetLogger() mockShard.Resource.DomainCache.EXPECT().GetDomainID(constants.TestDomainName).Return(constants.TestDomainID, nil).AnyTimes() m := &mutableStateDecisionTaskManagerImpl{ msb: newMutableStateBuilder(mockShard, logger, constants.TestLocalDomainEntry, constants.TestLocalDomainEntry.GetFailoverVersion()), } eventType := types.EventTypeActivityTaskCompleted e := &types.HistoryEvent{ ID: 1, EventType: &eventType, } err := m.ReplicateDecisionTaskCompletedEvent(e) assert.NoError(t, err) // test when domainEntry is missed m.msb.domainEntry = nil err = m.ReplicateDecisionTaskCompletedEvent(e) assert.NoError(t, err) // test when config is nil m.msb = newMutableStateBuilder(mockShard, logger, constants.TestLocalDomainEntry, constants.TestLocalDomainEntry.GetFailoverVersion()) m.msb.config = nil err = m.ReplicateDecisionTaskCompletedEvent(e) assert.NoError(t, err) } func TestReplicateDecisionTaskScheduledEvent(t *testing.T) { version := int64(123) scheduleID := int64(1) taskList := "task-list" startToCloseTimeoutSeconds := int32(100) attempt := int64(1) scheduleTimestamp := int64(1) originalScheduledTimestamp := int64(0) bypassTaskGeneration := false tests := []struct { name string assertions func(t *testing.T, info *DecisionInfo, err error, logs *observer.ObservedLogs) expectations func(mgr *mutableStateDecisionTaskManagerImpl) newMsb func(t *testing.T) *mutableStateBuilder }{ { name: "success", newMsb: func(t *testing.T) *mutableStateBuilder { return &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{ State: 0, // persistence.WorkflowStateCreated }, taskGenerator: NewMockMutableStateTaskGenerator(gomock.NewController(t)), } }, expectations: func(mgr *mutableStateDecisionTaskManagerImpl) { mgr.msb.taskGenerator.(*MockMutableStateTaskGenerator).EXPECT().GenerateDecisionScheduleTasks(scheduleID) }, assertions: func(t *testing.T, info *DecisionInfo, err error, observedLogs *observer.ObservedLogs) { require.NoError(t, err) assert.Equal(t, version, info.Version) assert.Equal(t, scheduleID, info.ScheduleID) assert.Equal(t, taskList, info.TaskList) assert.Equal(t, attempt, info.Attempt) assert.Equal(t, scheduleTimestamp, info.ScheduledTimestamp) assert.Equal(t, originalScheduledTimestamp, info.OriginalScheduledTimestamp) assert.Equal(t, 1, observedLogs.FilterMessageSnippet(fmt.Sprintf( "Decision Updated: {Schedule: %v, Started: %v, ID: %v, Timeout: %v, Attempt: %v, Timestamp: %v}", scheduleID, commonconstants.EmptyEventID, commonconstants.EmptyUUID, startToCloseTimeoutSeconds, attempt, 0, )).Len()) }, }, { name: "UpdateWorkflowStateCloseStatus failure", newMsb: func(t *testing.T) *mutableStateBuilder { return &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{ State: 2, // persistence.WorkflowStateCompleted }, taskGenerator: NewMockMutableStateTaskGenerator(gomock.NewController(t)), } }, assertions: func(t *testing.T, info *DecisionInfo, err error, observedLogs *observer.ObservedLogs) { assert.ErrorContains(t, err, "unable to change workflow state from 2 to 1, close status 0") assert.Nil(t, info) }, }, { name: "GenerateDecisionScheduleTasks failure", newMsb: func(t *testing.T) *mutableStateBuilder { return &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{ State: 0, // persistence.WorkflowStateCreated }, taskGenerator: NewMockMutableStateTaskGenerator(gomock.NewController(t)), } }, expectations: func(mgr *mutableStateDecisionTaskManagerImpl) { mgr.msb.taskGenerator.(*MockMutableStateTaskGenerator).EXPECT().GenerateDecisionScheduleTasks(scheduleID).Return(errors.New("some error")) }, assertions: func(t *testing.T, info *DecisionInfo, err error, observedLogs *observer.ObservedLogs) { assert.ErrorContains(t, err, "some error") assert.Nil(t, info) assert.Equal(t, 1, observedLogs.FilterMessageSnippet(fmt.Sprintf( "Decision Updated: {Schedule: %v, Started: %v, ID: %v, Timeout: %v, Attempt: %v, Timestamp: %v}", scheduleID, commonconstants.EmptyEventID, commonconstants.EmptyUUID, startToCloseTimeoutSeconds, attempt, 0, )).Len()) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{msb: test.newMsb(t)} core, observedLogs := observer.New(zap.DebugLevel) m.msb.logger = log.NewLogger(zap.New(core)) if test.expectations != nil { test.expectations(m) } info, err := m.ReplicateDecisionTaskScheduledEvent(version, scheduleID, taskList, startToCloseTimeoutSeconds, attempt, scheduleTimestamp, originalScheduledTimestamp, bypassTaskGeneration) test.assertions(t, info, err, observedLogs) }) } } func TestReplicateTransientDecisionTaskScheduled(t *testing.T) { tests := []struct { name string expectations func(mgr *mutableStateDecisionTaskManagerImpl) newMsb func(t *testing.T) *mutableStateBuilder assertions func(t *testing.T, err error, logs *observer.ObservedLogs) }{ { name: "success - decision updated", newMsb: func(t *testing.T) *mutableStateBuilder { return &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: commonconstants.EmptyEventID, DecisionAttempt: 1, }, taskGenerator: NewMockMutableStateTaskGenerator(gomock.NewController(t)), timeSource: clock.NewMockedTimeSource(), } }, expectations: func(mgr *mutableStateDecisionTaskManagerImpl) { mgr.msb.taskGenerator.(*MockMutableStateTaskGenerator).EXPECT().GenerateDecisionScheduleTasks(int64(0)) }, assertions: func(t *testing.T, err error, observedLogs *observer.ObservedLogs) { require.NoError(t, err) assert.Equal(t, 1, observedLogs.FilterMessageSnippet(fmt.Sprintf( "Decision Updated: {Schedule: %v, Started: %v, ID: %v, Timeout: %v, Attempt: %v, Timestamp: %v}", 0, commonconstants.EmptyEventID, commonconstants.EmptyUUID, 0, 1, 0)).Len()) }, }, { name: "success - decision need no update", newMsb: func(t *testing.T) *mutableStateBuilder { return &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: 0, // pending decisions DecisionAttempt: 0, }, taskGenerator: NewMockMutableStateTaskGenerator(gomock.NewController(t)), timeSource: clock.NewMockedTimeSource(), } }, assertions: func(t *testing.T, err error, observedLogs *observer.ObservedLogs) { require.NoError(t, err) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{msb: test.newMsb(t)} core, observedLogs := observer.New(zap.DebugLevel) m.msb.logger = log.NewLogger(zap.New(core)) if test.expectations != nil { test.expectations(m) } err := m.ReplicateTransientDecisionTaskScheduled() test.assertions(t, err, observedLogs) }) } } func TestCreateTransientDecisionEvents(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{ msb: &mutableStateBuilder{executionInfo: &persistence.WorkflowExecutionInfo{TaskList: "some-task-list"}}, } decision := &DecisionInfo{ ScheduleID: 0, StartedID: 1, RequestID: "some-requestID", DecisionTimeout: 100, Attempt: 1, ScheduledTimestamp: 10, StartedTimestamp: 10, OriginalScheduledTimestamp: 10, } scheduledEvent, startedEvent := m.CreateTransientDecisionEvents(decision, "identity") require.NotNil(t, scheduledEvent) assert.Equal(t, decision.ScheduleID, scheduledEvent.ID) assert.Equal(t, &decision.ScheduledTimestamp, scheduledEvent.Timestamp) assert.Equal(t, m.msb.executionInfo.TaskList, scheduledEvent.DecisionTaskScheduledEventAttributes.TaskList.Name) assert.Equal(t, &decision.DecisionTimeout, scheduledEvent.DecisionTaskScheduledEventAttributes.StartToCloseTimeoutSeconds) assert.Equal(t, decision.Attempt, scheduledEvent.DecisionTaskScheduledEventAttributes.Attempt) require.NotNil(t, startedEvent) assert.Equal(t, decision.StartedID, startedEvent.ID) assert.Equal(t, &decision.StartedTimestamp, startedEvent.Timestamp) assert.Equal(t, decision.ScheduleID, startedEvent.DecisionTaskStartedEventAttributes.ScheduledEventID) assert.Equal(t, decision.RequestID, startedEvent.DecisionTaskStartedEventAttributes.RequestID) assert.Equal(t, "identity", startedEvent.DecisionTaskStartedEventAttributes.Identity) } func TestGetDecisionScheduleToStartTimeout(t *testing.T) { t.Run("sticky taskList", func(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{ msb: &mutableStateBuilder{executionInfo: &persistence.WorkflowExecutionInfo{ StickyTaskList: "some-sticky-task-list", StickyScheduleToStartTimeout: 100, }}, } duration := m.GetDecisionScheduleToStartTimeout() assert.Equal(t, time.Duration(m.msb.executionInfo.StickyScheduleToStartTimeout)*time.Second, duration) }) } func TestHasProcessedOrPendingDecision(t *testing.T) { t.Run("true", func(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{ msb: &mutableStateBuilder{executionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: 0, // has pending decisions }}, } ok := m.HasProcessedOrPendingDecision() assert.True(t, ok) }) t.Run("false", func(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{ msb: &mutableStateBuilder{executionInfo: &persistence.WorkflowExecutionInfo{ // has no pending decisions DecisionScheduleID: commonconstants.EmptyEventID, LastProcessedEvent: commonconstants.EmptyEventID, }}, } ok := m.HasProcessedOrPendingDecision() assert.False(t, ok) }) } func TestGetInFlightDecision(t *testing.T) { t.Run("success", func(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{ msb: &mutableStateBuilder{executionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: 0, DecisionStartedID: 1, StickyTaskList: "some-sticky-task-list", }}, } decision, ok := m.GetInFlightDecision() require.NotNil(t, decision) assert.True(t, ok) assert.Equal(t, m.msb.executionInfo.DecisionVersion, decision.Version) assert.Equal(t, m.msb.executionInfo.DecisionScheduleID, decision.ScheduleID) assert.Equal(t, m.msb.executionInfo.DecisionStartedID, decision.StartedID) assert.Equal(t, m.msb.executionInfo.DecisionRequestID, decision.RequestID) assert.Equal(t, int64(m.msb.executionInfo.Attempt), decision.Attempt) assert.Equal(t, m.msb.executionInfo.DecisionStartedTimestamp, decision.StartedTimestamp) assert.Equal(t, m.msb.executionInfo.DecisionScheduledTimestamp, decision.ScheduledTimestamp) assert.Equal(t, m.msb.executionInfo.StickyTaskList, decision.TaskList) }) t.Run("failure", func(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{ msb: &mutableStateBuilder{executionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: commonconstants.EmptyEventID, DecisionStartedID: commonconstants.EmptyEventID, }}, } decision, value := m.GetInFlightDecision() require.Nil(t, decision) assert.False(t, value) }) } func TestGetPendingDecision(t *testing.T) { t.Run("success", func(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{ msb: &mutableStateBuilder{executionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: 0, DecisionStartedID: 1, StickyTaskList: "some-sticky-task-list", }}, } decision, ok := m.GetPendingDecision() require.NotNil(t, decision) assert.True(t, ok) assert.Equal(t, m.msb.executionInfo.DecisionVersion, decision.Version) assert.Equal(t, m.msb.executionInfo.DecisionScheduleID, decision.ScheduleID) assert.Equal(t, m.msb.executionInfo.DecisionStartedID, decision.StartedID) assert.Equal(t, m.msb.executionInfo.DecisionRequestID, decision.RequestID) assert.Equal(t, int64(m.msb.executionInfo.Attempt), decision.Attempt) assert.Equal(t, m.msb.executionInfo.DecisionStartedTimestamp, decision.StartedTimestamp) assert.Equal(t, m.msb.executionInfo.DecisionScheduledTimestamp, decision.ScheduledTimestamp) assert.Equal(t, m.msb.executionInfo.StickyTaskList, decision.TaskList) }) t.Run("failure", func(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{ msb: &mutableStateBuilder{executionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: commonconstants.EmptyEventID, }}, } decision, value := m.GetPendingDecision() require.Nil(t, decision) assert.False(t, value) }) } func TestReplicateDecisionTaskStartedEvent(t *testing.T) { var version int64 = 123 var scheduleID int64 = 1 var startedID int64 = 2 requestID := "some-request-id" var timeStamp int64 = 1 var originalTimeStamp int64 = 1 t.Run("success", func(t *testing.T) { core, observedLogs := observer.New(zap.DebugLevel) m := &mutableStateDecisionTaskManagerImpl{ msb: &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: scheduleID, DecisionVersion: version, DecisionStartedID: startedID, DecisionRequestID: requestID, DecisionStartedTimestamp: timeStamp, DecisionOriginalScheduledTimestamp: originalTimeStamp, TaskList: "some-taskList", }, taskGenerator: NewMockMutableStateTaskGenerator(gomock.NewController(t)), timeSource: clock.NewMockedTimeSource(), logger: log.NewLogger(zap.New(core)), }, } var decision *DecisionInfo m.msb.taskGenerator.(*MockMutableStateTaskGenerator).EXPECT().GenerateDecisionStartTasks(scheduleID) result, err := m.ReplicateDecisionTaskStartedEvent(decision, version, scheduleID, startedID, requestID, timeStamp) require.NoError(t, err) require.NotNil(t, result) assert.Equal(t, 1, observedLogs.FilterMessageSnippet(fmt.Sprintf( "Decision Updated: {Schedule: %v, Started: %v, ID: %v, Timeout: %v, Attempt: %v, Timestamp: %v}", scheduleID, startedID, requestID, 0, m.msb.executionInfo.Attempt, timeStamp)).Len()) assert.Equal(t, version, result.Version) assert.Equal(t, scheduleID, result.ScheduleID) assert.Equal(t, startedID, result.StartedID) assert.Equal(t, int64(m.msb.executionInfo.Attempt), result.Attempt) assert.Equal(t, requestID, result.RequestID) assert.Equal(t, m.msb.executionInfo.TaskList, result.TaskList) assert.Equal(t, timeStamp, result.StartedTimestamp) assert.Equal(t, m.msb.executionInfo.DecisionOriginalScheduledTimestamp, result.OriginalScheduledTimestamp) }) t.Run("failure", func(t *testing.T) { m := &mutableStateDecisionTaskManagerImpl{ msb: &mutableStateBuilder{ executionInfo: &persistence.WorkflowExecutionInfo{ DecisionScheduleID: commonconstants.EmptyEventID, DecisionAttempt: 1, }, }, } var decision *DecisionInfo result, err := m.ReplicateDecisionTaskStartedEvent(decision, version, scheduleID, startedID, requestID, timeStamp) assert.ErrorContains(t, err, fmt.Sprintf("unable to find decision: %v", scheduleID)) require.Nil(t, result) }) } ================================================ FILE: service/history/execution/mutable_state_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: mutable_state.go // // Generated by this command: // // mockgen -package execution -source mutable_state.go -destination mutable_state_mock.go -self_package github.com/uber/cadence/service/history/execution // // Package execution is a generated GoMock package. package execution import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" cache "github.com/uber/cadence/common/cache" definition "github.com/uber/cadence/common/definition" persistence "github.com/uber/cadence/common/persistence" types "github.com/uber/cadence/common/types" query "github.com/uber/cadence/service/history/query" ) // MockMutableState is a mock of MutableState interface. type MockMutableState struct { ctrl *gomock.Controller recorder *MockMutableStateMockRecorder isgomock struct{} } // MockMutableStateMockRecorder is the mock recorder for MockMutableState. type MockMutableStateMockRecorder struct { mock *MockMutableState } // NewMockMutableState creates a new mock instance. func NewMockMutableState(ctrl *gomock.Controller) *MockMutableState { mock := &MockMutableState{ctrl: ctrl} mock.recorder = &MockMutableStateMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMutableState) EXPECT() *MockMutableStateMockRecorder { return m.recorder } // AddActivityTaskCancelRequestedEvent mocks base method. func (m *MockMutableState) AddActivityTaskCancelRequestedEvent(arg0 int64, arg1, arg2 string) (*types.HistoryEvent, *persistence.ActivityInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddActivityTaskCancelRequestedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(*persistence.ActivityInfo) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // AddActivityTaskCancelRequestedEvent indicates an expected call of AddActivityTaskCancelRequestedEvent. func (mr *MockMutableStateMockRecorder) AddActivityTaskCancelRequestedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActivityTaskCancelRequestedEvent", reflect.TypeOf((*MockMutableState)(nil).AddActivityTaskCancelRequestedEvent), arg0, arg1, arg2) } // AddActivityTaskCanceledEvent mocks base method. func (m *MockMutableState) AddActivityTaskCanceledEvent(arg0, arg1, arg2 int64, arg3 []uint8, arg4 string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddActivityTaskCanceledEvent", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddActivityTaskCanceledEvent indicates an expected call of AddActivityTaskCanceledEvent. func (mr *MockMutableStateMockRecorder) AddActivityTaskCanceledEvent(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActivityTaskCanceledEvent", reflect.TypeOf((*MockMutableState)(nil).AddActivityTaskCanceledEvent), arg0, arg1, arg2, arg3, arg4) } // AddActivityTaskCompletedEvent mocks base method. func (m *MockMutableState) AddActivityTaskCompletedEvent(arg0, arg1 int64, arg2 *types.RespondActivityTaskCompletedRequest) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddActivityTaskCompletedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddActivityTaskCompletedEvent indicates an expected call of AddActivityTaskCompletedEvent. func (mr *MockMutableStateMockRecorder) AddActivityTaskCompletedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActivityTaskCompletedEvent", reflect.TypeOf((*MockMutableState)(nil).AddActivityTaskCompletedEvent), arg0, arg1, arg2) } // AddActivityTaskFailedEvent mocks base method. func (m *MockMutableState) AddActivityTaskFailedEvent(arg0, arg1 int64, arg2 *types.RespondActivityTaskFailedRequest) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddActivityTaskFailedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddActivityTaskFailedEvent indicates an expected call of AddActivityTaskFailedEvent. func (mr *MockMutableStateMockRecorder) AddActivityTaskFailedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActivityTaskFailedEvent", reflect.TypeOf((*MockMutableState)(nil).AddActivityTaskFailedEvent), arg0, arg1, arg2) } // AddActivityTaskScheduledEvent mocks base method. func (m *MockMutableState) AddActivityTaskScheduledEvent(arg0 context.Context, arg1 int64, arg2 *types.ScheduleActivityTaskDecisionAttributes, arg3 bool) (*types.HistoryEvent, *persistence.ActivityInfo, *types.ActivityLocalDispatchInfo, bool, bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddActivityTaskScheduledEvent", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(*persistence.ActivityInfo) ret2, _ := ret[2].(*types.ActivityLocalDispatchInfo) ret3, _ := ret[3].(bool) ret4, _ := ret[4].(bool) ret5, _ := ret[5].(error) return ret0, ret1, ret2, ret3, ret4, ret5 } // AddActivityTaskScheduledEvent indicates an expected call of AddActivityTaskScheduledEvent. func (mr *MockMutableStateMockRecorder) AddActivityTaskScheduledEvent(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActivityTaskScheduledEvent", reflect.TypeOf((*MockMutableState)(nil).AddActivityTaskScheduledEvent), arg0, arg1, arg2, arg3) } // AddActivityTaskStartedEvent mocks base method. func (m *MockMutableState) AddActivityTaskStartedEvent(arg0 *persistence.ActivityInfo, arg1 int64, arg2, arg3 string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddActivityTaskStartedEvent", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddActivityTaskStartedEvent indicates an expected call of AddActivityTaskStartedEvent. func (mr *MockMutableStateMockRecorder) AddActivityTaskStartedEvent(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActivityTaskStartedEvent", reflect.TypeOf((*MockMutableState)(nil).AddActivityTaskStartedEvent), arg0, arg1, arg2, arg3) } // AddActivityTaskTimedOutEvent mocks base method. func (m *MockMutableState) AddActivityTaskTimedOutEvent(arg0, arg1 int64, arg2 types.TimeoutType, arg3 []uint8) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddActivityTaskTimedOutEvent", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddActivityTaskTimedOutEvent indicates an expected call of AddActivityTaskTimedOutEvent. func (mr *MockMutableStateMockRecorder) AddActivityTaskTimedOutEvent(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActivityTaskTimedOutEvent", reflect.TypeOf((*MockMutableState)(nil).AddActivityTaskTimedOutEvent), arg0, arg1, arg2, arg3) } // AddCancelTimerFailedEvent mocks base method. func (m *MockMutableState) AddCancelTimerFailedEvent(arg0 int64, arg1 *types.CancelTimerDecisionAttributes, arg2 string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddCancelTimerFailedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddCancelTimerFailedEvent indicates an expected call of AddCancelTimerFailedEvent. func (mr *MockMutableStateMockRecorder) AddCancelTimerFailedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddCancelTimerFailedEvent", reflect.TypeOf((*MockMutableState)(nil).AddCancelTimerFailedEvent), arg0, arg1, arg2) } // AddChildWorkflowExecutionCanceledEvent mocks base method. func (m *MockMutableState) AddChildWorkflowExecutionCanceledEvent(arg0 int64, arg1 *types.WorkflowExecution, arg2 *types.WorkflowExecutionCanceledEventAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddChildWorkflowExecutionCanceledEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddChildWorkflowExecutionCanceledEvent indicates an expected call of AddChildWorkflowExecutionCanceledEvent. func (mr *MockMutableStateMockRecorder) AddChildWorkflowExecutionCanceledEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChildWorkflowExecutionCanceledEvent", reflect.TypeOf((*MockMutableState)(nil).AddChildWorkflowExecutionCanceledEvent), arg0, arg1, arg2) } // AddChildWorkflowExecutionCompletedEvent mocks base method. func (m *MockMutableState) AddChildWorkflowExecutionCompletedEvent(arg0 int64, arg1 *types.WorkflowExecution, arg2 *types.WorkflowExecutionCompletedEventAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddChildWorkflowExecutionCompletedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddChildWorkflowExecutionCompletedEvent indicates an expected call of AddChildWorkflowExecutionCompletedEvent. func (mr *MockMutableStateMockRecorder) AddChildWorkflowExecutionCompletedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChildWorkflowExecutionCompletedEvent", reflect.TypeOf((*MockMutableState)(nil).AddChildWorkflowExecutionCompletedEvent), arg0, arg1, arg2) } // AddChildWorkflowExecutionFailedEvent mocks base method. func (m *MockMutableState) AddChildWorkflowExecutionFailedEvent(arg0 int64, arg1 *types.WorkflowExecution, arg2 *types.WorkflowExecutionFailedEventAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddChildWorkflowExecutionFailedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddChildWorkflowExecutionFailedEvent indicates an expected call of AddChildWorkflowExecutionFailedEvent. func (mr *MockMutableStateMockRecorder) AddChildWorkflowExecutionFailedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChildWorkflowExecutionFailedEvent", reflect.TypeOf((*MockMutableState)(nil).AddChildWorkflowExecutionFailedEvent), arg0, arg1, arg2) } // AddChildWorkflowExecutionStartedEvent mocks base method. func (m *MockMutableState) AddChildWorkflowExecutionStartedEvent(arg0 string, arg1 *types.WorkflowExecution, arg2 *types.WorkflowType, arg3 int64, arg4 *types.Header) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddChildWorkflowExecutionStartedEvent", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddChildWorkflowExecutionStartedEvent indicates an expected call of AddChildWorkflowExecutionStartedEvent. func (mr *MockMutableStateMockRecorder) AddChildWorkflowExecutionStartedEvent(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChildWorkflowExecutionStartedEvent", reflect.TypeOf((*MockMutableState)(nil).AddChildWorkflowExecutionStartedEvent), arg0, arg1, arg2, arg3, arg4) } // AddChildWorkflowExecutionTerminatedEvent mocks base method. func (m *MockMutableState) AddChildWorkflowExecutionTerminatedEvent(arg0 int64, arg1 *types.WorkflowExecution, arg2 *types.WorkflowExecutionTerminatedEventAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddChildWorkflowExecutionTerminatedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddChildWorkflowExecutionTerminatedEvent indicates an expected call of AddChildWorkflowExecutionTerminatedEvent. func (mr *MockMutableStateMockRecorder) AddChildWorkflowExecutionTerminatedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChildWorkflowExecutionTerminatedEvent", reflect.TypeOf((*MockMutableState)(nil).AddChildWorkflowExecutionTerminatedEvent), arg0, arg1, arg2) } // AddChildWorkflowExecutionTimedOutEvent mocks base method. func (m *MockMutableState) AddChildWorkflowExecutionTimedOutEvent(arg0 int64, arg1 *types.WorkflowExecution, arg2 *types.WorkflowExecutionTimedOutEventAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddChildWorkflowExecutionTimedOutEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddChildWorkflowExecutionTimedOutEvent indicates an expected call of AddChildWorkflowExecutionTimedOutEvent. func (mr *MockMutableStateMockRecorder) AddChildWorkflowExecutionTimedOutEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddChildWorkflowExecutionTimedOutEvent", reflect.TypeOf((*MockMutableState)(nil).AddChildWorkflowExecutionTimedOutEvent), arg0, arg1, arg2) } // AddCompletedWorkflowEvent mocks base method. func (m *MockMutableState) AddCompletedWorkflowEvent(arg0 int64, arg1 *types.CompleteWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddCompletedWorkflowEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddCompletedWorkflowEvent indicates an expected call of AddCompletedWorkflowEvent. func (mr *MockMutableStateMockRecorder) AddCompletedWorkflowEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddCompletedWorkflowEvent", reflect.TypeOf((*MockMutableState)(nil).AddCompletedWorkflowEvent), arg0, arg1) } // AddContinueAsNewEvent mocks base method. func (m *MockMutableState) AddContinueAsNewEvent(arg0 context.Context, arg1, arg2 int64, arg3 string, arg4 *types.ContinueAsNewWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, MutableState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddContinueAsNewEvent", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(MutableState) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // AddContinueAsNewEvent indicates an expected call of AddContinueAsNewEvent. func (mr *MockMutableStateMockRecorder) AddContinueAsNewEvent(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddContinueAsNewEvent", reflect.TypeOf((*MockMutableState)(nil).AddContinueAsNewEvent), arg0, arg1, arg2, arg3, arg4) } // AddDecisionTaskCompletedEvent mocks base method. func (m *MockMutableState) AddDecisionTaskCompletedEvent(arg0, arg1 int64, arg2 *types.RespondDecisionTaskCompletedRequest, arg3 int) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskCompletedEvent", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskCompletedEvent indicates an expected call of AddDecisionTaskCompletedEvent. func (mr *MockMutableStateMockRecorder) AddDecisionTaskCompletedEvent(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskCompletedEvent", reflect.TypeOf((*MockMutableState)(nil).AddDecisionTaskCompletedEvent), arg0, arg1, arg2, arg3) } // AddDecisionTaskFailedEvent mocks base method. func (m *MockMutableState) AddDecisionTaskFailedEvent(scheduleEventID, startedEventID int64, cause types.DecisionTaskFailedCause, details []byte, identity, reason, binChecksum, baseRunID, newRunID string, forkEventVersion int64, resetRequestID string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskFailedEvent", scheduleEventID, startedEventID, cause, details, identity, reason, binChecksum, baseRunID, newRunID, forkEventVersion, resetRequestID) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskFailedEvent indicates an expected call of AddDecisionTaskFailedEvent. func (mr *MockMutableStateMockRecorder) AddDecisionTaskFailedEvent(scheduleEventID, startedEventID, cause, details, identity, reason, binChecksum, baseRunID, newRunID, forkEventVersion, resetRequestID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskFailedEvent", reflect.TypeOf((*MockMutableState)(nil).AddDecisionTaskFailedEvent), scheduleEventID, startedEventID, cause, details, identity, reason, binChecksum, baseRunID, newRunID, forkEventVersion, resetRequestID) } // AddDecisionTaskResetTimeoutEvent mocks base method. func (m *MockMutableState) AddDecisionTaskResetTimeoutEvent(arg0 int64, arg1, arg2 string, arg3 int64, arg4, arg5 string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskResetTimeoutEvent", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskResetTimeoutEvent indicates an expected call of AddDecisionTaskResetTimeoutEvent. func (mr *MockMutableStateMockRecorder) AddDecisionTaskResetTimeoutEvent(arg0, arg1, arg2, arg3, arg4, arg5 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskResetTimeoutEvent", reflect.TypeOf((*MockMutableState)(nil).AddDecisionTaskResetTimeoutEvent), arg0, arg1, arg2, arg3, arg4, arg5) } // AddDecisionTaskScheduleToStartTimeoutEvent mocks base method. func (m *MockMutableState) AddDecisionTaskScheduleToStartTimeoutEvent(arg0 int64) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskScheduleToStartTimeoutEvent", arg0) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskScheduleToStartTimeoutEvent indicates an expected call of AddDecisionTaskScheduleToStartTimeoutEvent. func (mr *MockMutableStateMockRecorder) AddDecisionTaskScheduleToStartTimeoutEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskScheduleToStartTimeoutEvent", reflect.TypeOf((*MockMutableState)(nil).AddDecisionTaskScheduleToStartTimeoutEvent), arg0) } // AddDecisionTaskScheduledEvent mocks base method. func (m *MockMutableState) AddDecisionTaskScheduledEvent(bypassTaskGeneration bool) (*DecisionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskScheduledEvent", bypassTaskGeneration) ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskScheduledEvent indicates an expected call of AddDecisionTaskScheduledEvent. func (mr *MockMutableStateMockRecorder) AddDecisionTaskScheduledEvent(bypassTaskGeneration any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskScheduledEvent", reflect.TypeOf((*MockMutableState)(nil).AddDecisionTaskScheduledEvent), bypassTaskGeneration) } // AddDecisionTaskScheduledEventAsHeartbeat mocks base method. func (m *MockMutableState) AddDecisionTaskScheduledEventAsHeartbeat(bypassTaskGeneration bool, originalScheduledTimestamp int64) (*DecisionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskScheduledEventAsHeartbeat", bypassTaskGeneration, originalScheduledTimestamp) ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskScheduledEventAsHeartbeat indicates an expected call of AddDecisionTaskScheduledEventAsHeartbeat. func (mr *MockMutableStateMockRecorder) AddDecisionTaskScheduledEventAsHeartbeat(bypassTaskGeneration, originalScheduledTimestamp any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskScheduledEventAsHeartbeat", reflect.TypeOf((*MockMutableState)(nil).AddDecisionTaskScheduledEventAsHeartbeat), bypassTaskGeneration, originalScheduledTimestamp) } // AddDecisionTaskStartedEvent mocks base method. func (m *MockMutableState) AddDecisionTaskStartedEvent(arg0 int64, arg1 string, arg2 *types.PollForDecisionTaskRequest) (*types.HistoryEvent, *DecisionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskStartedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(*DecisionInfo) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // AddDecisionTaskStartedEvent indicates an expected call of AddDecisionTaskStartedEvent. func (mr *MockMutableStateMockRecorder) AddDecisionTaskStartedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskStartedEvent", reflect.TypeOf((*MockMutableState)(nil).AddDecisionTaskStartedEvent), arg0, arg1, arg2) } // AddDecisionTaskTimedOutEvent mocks base method. func (m *MockMutableState) AddDecisionTaskTimedOutEvent(arg0, arg1 int64) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTaskTimedOutEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTaskTimedOutEvent indicates an expected call of AddDecisionTaskTimedOutEvent. func (mr *MockMutableStateMockRecorder) AddDecisionTaskTimedOutEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTaskTimedOutEvent", reflect.TypeOf((*MockMutableState)(nil).AddDecisionTaskTimedOutEvent), arg0, arg1) } // AddExternalWorkflowExecutionCancelRequested mocks base method. func (m *MockMutableState) AddExternalWorkflowExecutionCancelRequested(arg0 int64, arg1, arg2, arg3 string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddExternalWorkflowExecutionCancelRequested", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddExternalWorkflowExecutionCancelRequested indicates an expected call of AddExternalWorkflowExecutionCancelRequested. func (mr *MockMutableStateMockRecorder) AddExternalWorkflowExecutionCancelRequested(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddExternalWorkflowExecutionCancelRequested", reflect.TypeOf((*MockMutableState)(nil).AddExternalWorkflowExecutionCancelRequested), arg0, arg1, arg2, arg3) } // AddExternalWorkflowExecutionSignaled mocks base method. func (m *MockMutableState) AddExternalWorkflowExecutionSignaled(arg0 int64, arg1, arg2, arg3 string, arg4 []uint8) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddExternalWorkflowExecutionSignaled", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddExternalWorkflowExecutionSignaled indicates an expected call of AddExternalWorkflowExecutionSignaled. func (mr *MockMutableStateMockRecorder) AddExternalWorkflowExecutionSignaled(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddExternalWorkflowExecutionSignaled", reflect.TypeOf((*MockMutableState)(nil).AddExternalWorkflowExecutionSignaled), arg0, arg1, arg2, arg3, arg4) } // AddFailWorkflowEvent mocks base method. func (m *MockMutableState) AddFailWorkflowEvent(arg0 int64, arg1 *types.FailWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddFailWorkflowEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddFailWorkflowEvent indicates an expected call of AddFailWorkflowEvent. func (mr *MockMutableStateMockRecorder) AddFailWorkflowEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFailWorkflowEvent", reflect.TypeOf((*MockMutableState)(nil).AddFailWorkflowEvent), arg0, arg1) } // AddFirstDecisionTaskScheduled mocks base method. func (m *MockMutableState) AddFirstDecisionTaskScheduled(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddFirstDecisionTaskScheduled", arg0) ret0, _ := ret[0].(error) return ret0 } // AddFirstDecisionTaskScheduled indicates an expected call of AddFirstDecisionTaskScheduled. func (mr *MockMutableStateMockRecorder) AddFirstDecisionTaskScheduled(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFirstDecisionTaskScheduled", reflect.TypeOf((*MockMutableState)(nil).AddFirstDecisionTaskScheduled), arg0) } // AddRecordMarkerEvent mocks base method. func (m *MockMutableState) AddRecordMarkerEvent(arg0 int64, arg1 *types.RecordMarkerDecisionAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddRecordMarkerEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddRecordMarkerEvent indicates an expected call of AddRecordMarkerEvent. func (mr *MockMutableStateMockRecorder) AddRecordMarkerEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRecordMarkerEvent", reflect.TypeOf((*MockMutableState)(nil).AddRecordMarkerEvent), arg0, arg1) } // AddRequestCancelActivityTaskFailedEvent mocks base method. func (m *MockMutableState) AddRequestCancelActivityTaskFailedEvent(arg0 int64, arg1, arg2 string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddRequestCancelActivityTaskFailedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddRequestCancelActivityTaskFailedEvent indicates an expected call of AddRequestCancelActivityTaskFailedEvent. func (mr *MockMutableStateMockRecorder) AddRequestCancelActivityTaskFailedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRequestCancelActivityTaskFailedEvent", reflect.TypeOf((*MockMutableState)(nil).AddRequestCancelActivityTaskFailedEvent), arg0, arg1, arg2) } // AddRequestCancelExternalWorkflowExecutionFailedEvent mocks base method. func (m *MockMutableState) AddRequestCancelExternalWorkflowExecutionFailedEvent(arg0, arg1 int64, arg2, arg3, arg4 string, arg5 types.CancelExternalWorkflowExecutionFailedCause) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddRequestCancelExternalWorkflowExecutionFailedEvent", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddRequestCancelExternalWorkflowExecutionFailedEvent indicates an expected call of AddRequestCancelExternalWorkflowExecutionFailedEvent. func (mr *MockMutableStateMockRecorder) AddRequestCancelExternalWorkflowExecutionFailedEvent(arg0, arg1, arg2, arg3, arg4, arg5 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRequestCancelExternalWorkflowExecutionFailedEvent", reflect.TypeOf((*MockMutableState)(nil).AddRequestCancelExternalWorkflowExecutionFailedEvent), arg0, arg1, arg2, arg3, arg4, arg5) } // AddRequestCancelExternalWorkflowExecutionInitiatedEvent mocks base method. func (m *MockMutableState) AddRequestCancelExternalWorkflowExecutionInitiatedEvent(arg0 int64, arg1 string, arg2 *types.RequestCancelExternalWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, *persistence.RequestCancelInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddRequestCancelExternalWorkflowExecutionInitiatedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(*persistence.RequestCancelInfo) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // AddRequestCancelExternalWorkflowExecutionInitiatedEvent indicates an expected call of AddRequestCancelExternalWorkflowExecutionInitiatedEvent. func (mr *MockMutableStateMockRecorder) AddRequestCancelExternalWorkflowExecutionInitiatedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRequestCancelExternalWorkflowExecutionInitiatedEvent", reflect.TypeOf((*MockMutableState)(nil).AddRequestCancelExternalWorkflowExecutionInitiatedEvent), arg0, arg1, arg2) } // AddSignalExternalWorkflowExecutionFailedEvent mocks base method. func (m *MockMutableState) AddSignalExternalWorkflowExecutionFailedEvent(arg0, arg1 int64, arg2, arg3, arg4 string, arg5 []uint8, arg6 types.SignalExternalWorkflowExecutionFailedCause) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddSignalExternalWorkflowExecutionFailedEvent", arg0, arg1, arg2, arg3, arg4, arg5, arg6) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddSignalExternalWorkflowExecutionFailedEvent indicates an expected call of AddSignalExternalWorkflowExecutionFailedEvent. func (mr *MockMutableStateMockRecorder) AddSignalExternalWorkflowExecutionFailedEvent(arg0, arg1, arg2, arg3, arg4, arg5, arg6 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSignalExternalWorkflowExecutionFailedEvent", reflect.TypeOf((*MockMutableState)(nil).AddSignalExternalWorkflowExecutionFailedEvent), arg0, arg1, arg2, arg3, arg4, arg5, arg6) } // AddSignalExternalWorkflowExecutionInitiatedEvent mocks base method. func (m *MockMutableState) AddSignalExternalWorkflowExecutionInitiatedEvent(arg0 int64, arg1 string, arg2 *types.SignalExternalWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, *persistence.SignalInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddSignalExternalWorkflowExecutionInitiatedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(*persistence.SignalInfo) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // AddSignalExternalWorkflowExecutionInitiatedEvent indicates an expected call of AddSignalExternalWorkflowExecutionInitiatedEvent. func (mr *MockMutableStateMockRecorder) AddSignalExternalWorkflowExecutionInitiatedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSignalExternalWorkflowExecutionInitiatedEvent", reflect.TypeOf((*MockMutableState)(nil).AddSignalExternalWorkflowExecutionInitiatedEvent), arg0, arg1, arg2) } // AddSignalRequested mocks base method. func (m *MockMutableState) AddSignalRequested(requestID string) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddSignalRequested", requestID) } // AddSignalRequested indicates an expected call of AddSignalRequested. func (mr *MockMutableStateMockRecorder) AddSignalRequested(requestID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSignalRequested", reflect.TypeOf((*MockMutableState)(nil).AddSignalRequested), requestID) } // AddStartChildWorkflowExecutionFailedEvent mocks base method. func (m *MockMutableState) AddStartChildWorkflowExecutionFailedEvent(arg0 int64, arg1 types.ChildWorkflowExecutionFailedCause, arg2 *types.StartChildWorkflowExecutionInitiatedEventAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddStartChildWorkflowExecutionFailedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddStartChildWorkflowExecutionFailedEvent indicates an expected call of AddStartChildWorkflowExecutionFailedEvent. func (mr *MockMutableStateMockRecorder) AddStartChildWorkflowExecutionFailedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddStartChildWorkflowExecutionFailedEvent", reflect.TypeOf((*MockMutableState)(nil).AddStartChildWorkflowExecutionFailedEvent), arg0, arg1, arg2) } // AddStartChildWorkflowExecutionInitiatedEvent mocks base method. func (m *MockMutableState) AddStartChildWorkflowExecutionInitiatedEvent(arg0 int64, arg1 string, arg2 *types.StartChildWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, *persistence.ChildExecutionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddStartChildWorkflowExecutionInitiatedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(*persistence.ChildExecutionInfo) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // AddStartChildWorkflowExecutionInitiatedEvent indicates an expected call of AddStartChildWorkflowExecutionInitiatedEvent. func (mr *MockMutableStateMockRecorder) AddStartChildWorkflowExecutionInitiatedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddStartChildWorkflowExecutionInitiatedEvent", reflect.TypeOf((*MockMutableState)(nil).AddStartChildWorkflowExecutionInitiatedEvent), arg0, arg1, arg2) } // AddTimeoutWorkflowEvent mocks base method. func (m *MockMutableState) AddTimeoutWorkflowEvent(arg0 int64) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddTimeoutWorkflowEvent", arg0) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddTimeoutWorkflowEvent indicates an expected call of AddTimeoutWorkflowEvent. func (mr *MockMutableStateMockRecorder) AddTimeoutWorkflowEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTimeoutWorkflowEvent", reflect.TypeOf((*MockMutableState)(nil).AddTimeoutWorkflowEvent), arg0) } // AddTimerCanceledEvent mocks base method. func (m *MockMutableState) AddTimerCanceledEvent(arg0 int64, arg1 *types.CancelTimerDecisionAttributes, arg2 string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddTimerCanceledEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddTimerCanceledEvent indicates an expected call of AddTimerCanceledEvent. func (mr *MockMutableStateMockRecorder) AddTimerCanceledEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTimerCanceledEvent", reflect.TypeOf((*MockMutableState)(nil).AddTimerCanceledEvent), arg0, arg1, arg2) } // AddTimerFiredEvent mocks base method. func (m *MockMutableState) AddTimerFiredEvent(arg0 string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddTimerFiredEvent", arg0) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddTimerFiredEvent indicates an expected call of AddTimerFiredEvent. func (mr *MockMutableStateMockRecorder) AddTimerFiredEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTimerFiredEvent", reflect.TypeOf((*MockMutableState)(nil).AddTimerFiredEvent), arg0) } // AddTimerStartedEvent mocks base method. func (m *MockMutableState) AddTimerStartedEvent(arg0 int64, arg1 *types.StartTimerDecisionAttributes) (*types.HistoryEvent, *persistence.TimerInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddTimerStartedEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(*persistence.TimerInfo) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // AddTimerStartedEvent indicates an expected call of AddTimerStartedEvent. func (mr *MockMutableStateMockRecorder) AddTimerStartedEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTimerStartedEvent", reflect.TypeOf((*MockMutableState)(nil).AddTimerStartedEvent), arg0, arg1) } // AddTimerTasks mocks base method. func (m *MockMutableState) AddTimerTasks(timerTasks ...persistence.Task) { m.ctrl.T.Helper() varargs := []any{} for _, a := range timerTasks { varargs = append(varargs, a) } m.ctrl.Call(m, "AddTimerTasks", varargs...) } // AddTimerTasks indicates an expected call of AddTimerTasks. func (mr *MockMutableStateMockRecorder) AddTimerTasks(timerTasks ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTimerTasks", reflect.TypeOf((*MockMutableState)(nil).AddTimerTasks), timerTasks...) } // AddTransferTasks mocks base method. func (m *MockMutableState) AddTransferTasks(transferTasks ...persistence.Task) { m.ctrl.T.Helper() varargs := []any{} for _, a := range transferTasks { varargs = append(varargs, a) } m.ctrl.Call(m, "AddTransferTasks", varargs...) } // AddTransferTasks indicates an expected call of AddTransferTasks. func (mr *MockMutableStateMockRecorder) AddTransferTasks(transferTasks ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTransferTasks", reflect.TypeOf((*MockMutableState)(nil).AddTransferTasks), transferTasks...) } // AddUpsertWorkflowSearchAttributesEvent mocks base method. func (m *MockMutableState) AddUpsertWorkflowSearchAttributesEvent(arg0 int64, arg1 *types.UpsertWorkflowSearchAttributesDecisionAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddUpsertWorkflowSearchAttributesEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddUpsertWorkflowSearchAttributesEvent indicates an expected call of AddUpsertWorkflowSearchAttributesEvent. func (mr *MockMutableStateMockRecorder) AddUpsertWorkflowSearchAttributesEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUpsertWorkflowSearchAttributesEvent", reflect.TypeOf((*MockMutableState)(nil).AddUpsertWorkflowSearchAttributesEvent), arg0, arg1) } // AddWorkflowExecutionCancelRequestedEvent mocks base method. func (m *MockMutableState) AddWorkflowExecutionCancelRequestedEvent(arg0 string, arg1 *types.HistoryRequestCancelWorkflowExecutionRequest) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddWorkflowExecutionCancelRequestedEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddWorkflowExecutionCancelRequestedEvent indicates an expected call of AddWorkflowExecutionCancelRequestedEvent. func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionCancelRequestedEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionCancelRequestedEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionCancelRequestedEvent), arg0, arg1) } // AddWorkflowExecutionCanceledEvent mocks base method. func (m *MockMutableState) AddWorkflowExecutionCanceledEvent(arg0 int64, arg1 *types.CancelWorkflowExecutionDecisionAttributes) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddWorkflowExecutionCanceledEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddWorkflowExecutionCanceledEvent indicates an expected call of AddWorkflowExecutionCanceledEvent. func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionCanceledEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionCanceledEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionCanceledEvent), arg0, arg1) } // AddWorkflowExecutionSignaled mocks base method. func (m *MockMutableState) AddWorkflowExecutionSignaled(signalName string, input []byte, identity, reqeustID string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddWorkflowExecutionSignaled", signalName, input, identity, reqeustID) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddWorkflowExecutionSignaled indicates an expected call of AddWorkflowExecutionSignaled. func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionSignaled(signalName, input, identity, reqeustID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionSignaled", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionSignaled), signalName, input, identity, reqeustID) } // AddWorkflowExecutionStartedEvent mocks base method. func (m *MockMutableState) AddWorkflowExecutionStartedEvent(arg0 types.WorkflowExecution, arg1 *types.HistoryStartWorkflowExecutionRequest) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddWorkflowExecutionStartedEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddWorkflowExecutionStartedEvent indicates an expected call of AddWorkflowExecutionStartedEvent. func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionStartedEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionStartedEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionStartedEvent), arg0, arg1) } // AddWorkflowExecutionTerminatedEvent mocks base method. func (m *MockMutableState) AddWorkflowExecutionTerminatedEvent(firstEventID int64, reason string, details []byte, identity string) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddWorkflowExecutionTerminatedEvent", firstEventID, reason, details, identity) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // AddWorkflowExecutionTerminatedEvent indicates an expected call of AddWorkflowExecutionTerminatedEvent. func (mr *MockMutableStateMockRecorder) AddWorkflowExecutionTerminatedEvent(firstEventID, reason, details, identity any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWorkflowExecutionTerminatedEvent", reflect.TypeOf((*MockMutableState)(nil).AddWorkflowExecutionTerminatedEvent), firstEventID, reason, details, identity) } // ByteSize mocks base method. func (m *MockMutableState) ByteSize() uint64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByteSize") ret0, _ := ret[0].(uint64) return ret0 } // ByteSize indicates an expected call of ByteSize. func (mr *MockMutableStateMockRecorder) ByteSize() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByteSize", reflect.TypeOf((*MockMutableState)(nil).ByteSize)) } // CheckResettable mocks base method. func (m *MockMutableState) CheckResettable() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CheckResettable") ret0, _ := ret[0].(error) return ret0 } // CheckResettable indicates an expected call of CheckResettable. func (mr *MockMutableStateMockRecorder) CheckResettable() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckResettable", reflect.TypeOf((*MockMutableState)(nil).CheckResettable)) } // ClearStickyness mocks base method. func (m *MockMutableState) ClearStickyness() { m.ctrl.T.Helper() m.ctrl.Call(m, "ClearStickyness") } // ClearStickyness indicates an expected call of ClearStickyness. func (mr *MockMutableStateMockRecorder) ClearStickyness() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearStickyness", reflect.TypeOf((*MockMutableState)(nil).ClearStickyness)) } // CloseTransactionAsMutation mocks base method. func (m *MockMutableState) CloseTransactionAsMutation(now time.Time, transactionPolicy TransactionPolicy) (*persistence.WorkflowMutation, []*persistence.WorkflowEvents, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CloseTransactionAsMutation", now, transactionPolicy) ret0, _ := ret[0].(*persistence.WorkflowMutation) ret1, _ := ret[1].([]*persistence.WorkflowEvents) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // CloseTransactionAsMutation indicates an expected call of CloseTransactionAsMutation. func (mr *MockMutableStateMockRecorder) CloseTransactionAsMutation(now, transactionPolicy any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseTransactionAsMutation", reflect.TypeOf((*MockMutableState)(nil).CloseTransactionAsMutation), now, transactionPolicy) } // CloseTransactionAsSnapshot mocks base method. func (m *MockMutableState) CloseTransactionAsSnapshot(now time.Time, transactionPolicy TransactionPolicy) (*persistence.WorkflowSnapshot, []*persistence.WorkflowEvents, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CloseTransactionAsSnapshot", now, transactionPolicy) ret0, _ := ret[0].(*persistence.WorkflowSnapshot) ret1, _ := ret[1].([]*persistence.WorkflowEvents) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // CloseTransactionAsSnapshot indicates an expected call of CloseTransactionAsSnapshot. func (mr *MockMutableStateMockRecorder) CloseTransactionAsSnapshot(now, transactionPolicy any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseTransactionAsSnapshot", reflect.TypeOf((*MockMutableState)(nil).CloseTransactionAsSnapshot), now, transactionPolicy) } // CopyToPersistence mocks base method. func (m *MockMutableState) CopyToPersistence() *persistence.WorkflowMutableState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CopyToPersistence") ret0, _ := ret[0].(*persistence.WorkflowMutableState) return ret0 } // CopyToPersistence indicates an expected call of CopyToPersistence. func (mr *MockMutableStateMockRecorder) CopyToPersistence() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CopyToPersistence", reflect.TypeOf((*MockMutableState)(nil).CopyToPersistence)) } // CreateNewHistoryEvent mocks base method. func (m *MockMutableState) CreateNewHistoryEvent(eventType types.EventType) *types.HistoryEvent { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateNewHistoryEvent", eventType) ret0, _ := ret[0].(*types.HistoryEvent) return ret0 } // CreateNewHistoryEvent indicates an expected call of CreateNewHistoryEvent. func (mr *MockMutableStateMockRecorder) CreateNewHistoryEvent(eventType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewHistoryEvent", reflect.TypeOf((*MockMutableState)(nil).CreateNewHistoryEvent), eventType) } // CreateNewHistoryEventWithTimestamp mocks base method. func (m *MockMutableState) CreateNewHistoryEventWithTimestamp(eventType types.EventType, timestamp int64) *types.HistoryEvent { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateNewHistoryEventWithTimestamp", eventType, timestamp) ret0, _ := ret[0].(*types.HistoryEvent) return ret0 } // CreateNewHistoryEventWithTimestamp indicates an expected call of CreateNewHistoryEventWithTimestamp. func (mr *MockMutableStateMockRecorder) CreateNewHistoryEventWithTimestamp(eventType, timestamp any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewHistoryEventWithTimestamp", reflect.TypeOf((*MockMutableState)(nil).CreateNewHistoryEventWithTimestamp), eventType, timestamp) } // CreateTransientDecisionEvents mocks base method. func (m *MockMutableState) CreateTransientDecisionEvents(di *DecisionInfo, identity string) (*types.HistoryEvent, *types.HistoryEvent) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateTransientDecisionEvents", di, identity) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(*types.HistoryEvent) return ret0, ret1 } // CreateTransientDecisionEvents indicates an expected call of CreateTransientDecisionEvents. func (mr *MockMutableStateMockRecorder) CreateTransientDecisionEvents(di, identity any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTransientDecisionEvents", reflect.TypeOf((*MockMutableState)(nil).CreateTransientDecisionEvents), di, identity) } // DeleteActivity mocks base method. func (m *MockMutableState) DeleteActivity(scheduleEventID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteActivity", scheduleEventID) ret0, _ := ret[0].(error) return ret0 } // DeleteActivity indicates an expected call of DeleteActivity. func (mr *MockMutableStateMockRecorder) DeleteActivity(scheduleEventID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteActivity", reflect.TypeOf((*MockMutableState)(nil).DeleteActivity), scheduleEventID) } // DeleteDecision mocks base method. func (m *MockMutableState) DeleteDecision() { m.ctrl.T.Helper() m.ctrl.Call(m, "DeleteDecision") } // DeleteDecision indicates an expected call of DeleteDecision. func (mr *MockMutableStateMockRecorder) DeleteDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDecision", reflect.TypeOf((*MockMutableState)(nil).DeleteDecision)) } // DeleteSignalRequested mocks base method. func (m *MockMutableState) DeleteSignalRequested(requestID string) { m.ctrl.T.Helper() m.ctrl.Call(m, "DeleteSignalRequested", requestID) } // DeleteSignalRequested indicates an expected call of DeleteSignalRequested. func (mr *MockMutableStateMockRecorder) DeleteSignalRequested(requestID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSignalRequested", reflect.TypeOf((*MockMutableState)(nil).DeleteSignalRequested), requestID) } // DeleteTimerTasks mocks base method. func (m *MockMutableState) DeleteTimerTasks() { m.ctrl.T.Helper() m.ctrl.Call(m, "DeleteTimerTasks") } // DeleteTimerTasks indicates an expected call of DeleteTimerTasks. func (mr *MockMutableStateMockRecorder) DeleteTimerTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTimerTasks", reflect.TypeOf((*MockMutableState)(nil).DeleteTimerTasks)) } // DeleteTransferTasks mocks base method. func (m *MockMutableState) DeleteTransferTasks() { m.ctrl.T.Helper() m.ctrl.Call(m, "DeleteTransferTasks") } // DeleteTransferTasks indicates an expected call of DeleteTransferTasks. func (mr *MockMutableStateMockRecorder) DeleteTransferTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTransferTasks", reflect.TypeOf((*MockMutableState)(nil).DeleteTransferTasks)) } // DeleteUserTimer mocks base method. func (m *MockMutableState) DeleteUserTimer(timerID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteUserTimer", timerID) ret0, _ := ret[0].(error) return ret0 } // DeleteUserTimer indicates an expected call of DeleteUserTimer. func (mr *MockMutableStateMockRecorder) DeleteUserTimer(timerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUserTimer", reflect.TypeOf((*MockMutableState)(nil).DeleteUserTimer), timerID) } // FailDecision mocks base method. func (m *MockMutableState) FailDecision(arg0 bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "FailDecision", arg0) } // FailDecision indicates an expected call of FailDecision. func (mr *MockMutableStateMockRecorder) FailDecision(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FailDecision", reflect.TypeOf((*MockMutableState)(nil).FailDecision), arg0) } // FlushBufferedEvents mocks base method. func (m *MockMutableState) FlushBufferedEvents() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FlushBufferedEvents") ret0, _ := ret[0].(error) return ret0 } // FlushBufferedEvents indicates an expected call of FlushBufferedEvents. func (mr *MockMutableStateMockRecorder) FlushBufferedEvents() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushBufferedEvents", reflect.TypeOf((*MockMutableState)(nil).FlushBufferedEvents)) } // GetActivityByActivityID mocks base method. func (m *MockMutableState) GetActivityByActivityID(arg0 string) (*persistence.ActivityInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActivityByActivityID", arg0) ret0, _ := ret[0].(*persistence.ActivityInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetActivityByActivityID indicates an expected call of GetActivityByActivityID. func (mr *MockMutableStateMockRecorder) GetActivityByActivityID(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActivityByActivityID", reflect.TypeOf((*MockMutableState)(nil).GetActivityByActivityID), arg0) } // GetActivityInfo mocks base method. func (m *MockMutableState) GetActivityInfo(arg0 int64) (*persistence.ActivityInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActivityInfo", arg0) ret0, _ := ret[0].(*persistence.ActivityInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetActivityInfo indicates an expected call of GetActivityInfo. func (mr *MockMutableStateMockRecorder) GetActivityInfo(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActivityInfo", reflect.TypeOf((*MockMutableState)(nil).GetActivityInfo), arg0) } // GetActivityScheduledEvent mocks base method. func (m *MockMutableState) GetActivityScheduledEvent(arg0 context.Context, arg1 int64) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActivityScheduledEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // GetActivityScheduledEvent indicates an expected call of GetActivityScheduledEvent. func (mr *MockMutableStateMockRecorder) GetActivityScheduledEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActivityScheduledEvent", reflect.TypeOf((*MockMutableState)(nil).GetActivityScheduledEvent), arg0, arg1) } // GetChildExecutionInfo mocks base method. func (m *MockMutableState) GetChildExecutionInfo(arg0 int64) (*persistence.ChildExecutionInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetChildExecutionInfo", arg0) ret0, _ := ret[0].(*persistence.ChildExecutionInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetChildExecutionInfo indicates an expected call of GetChildExecutionInfo. func (mr *MockMutableStateMockRecorder) GetChildExecutionInfo(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChildExecutionInfo", reflect.TypeOf((*MockMutableState)(nil).GetChildExecutionInfo), arg0) } // GetChildExecutionInitiatedEvent mocks base method. func (m *MockMutableState) GetChildExecutionInitiatedEvent(arg0 context.Context, arg1 int64) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetChildExecutionInitiatedEvent", arg0, arg1) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // GetChildExecutionInitiatedEvent indicates an expected call of GetChildExecutionInitiatedEvent. func (mr *MockMutableStateMockRecorder) GetChildExecutionInitiatedEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChildExecutionInitiatedEvent", reflect.TypeOf((*MockMutableState)(nil).GetChildExecutionInitiatedEvent), arg0, arg1) } // GetCompletionEvent mocks base method. func (m *MockMutableState) GetCompletionEvent(arg0 context.Context) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCompletionEvent", arg0) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCompletionEvent indicates an expected call of GetCompletionEvent. func (mr *MockMutableStateMockRecorder) GetCompletionEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCompletionEvent", reflect.TypeOf((*MockMutableState)(nil).GetCompletionEvent), arg0) } // GetCronBackoffDuration mocks base method. func (m *MockMutableState) GetCronBackoffDuration(arg0 context.Context) (time.Duration, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCronBackoffDuration", arg0) ret0, _ := ret[0].(time.Duration) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCronBackoffDuration indicates an expected call of GetCronBackoffDuration. func (mr *MockMutableStateMockRecorder) GetCronBackoffDuration(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCronBackoffDuration", reflect.TypeOf((*MockMutableState)(nil).GetCronBackoffDuration), arg0) } // GetCurrentBranchToken mocks base method. func (m *MockMutableState) GetCurrentBranchToken() ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCurrentBranchToken") ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentBranchToken indicates an expected call of GetCurrentBranchToken. func (mr *MockMutableStateMockRecorder) GetCurrentBranchToken() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentBranchToken", reflect.TypeOf((*MockMutableState)(nil).GetCurrentBranchToken)) } // GetCurrentVersion mocks base method. func (m *MockMutableState) GetCurrentVersion() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCurrentVersion") ret0, _ := ret[0].(int64) return ret0 } // GetCurrentVersion indicates an expected call of GetCurrentVersion. func (mr *MockMutableStateMockRecorder) GetCurrentVersion() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentVersion", reflect.TypeOf((*MockMutableState)(nil).GetCurrentVersion)) } // GetDecisionInfo mocks base method. func (m *MockMutableState) GetDecisionInfo(arg0 int64) (*DecisionInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDecisionInfo", arg0) ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetDecisionInfo indicates an expected call of GetDecisionInfo. func (mr *MockMutableStateMockRecorder) GetDecisionInfo(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDecisionInfo", reflect.TypeOf((*MockMutableState)(nil).GetDecisionInfo), arg0) } // GetDecisionScheduleToStartTimeout mocks base method. func (m *MockMutableState) GetDecisionScheduleToStartTimeout() time.Duration { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDecisionScheduleToStartTimeout") ret0, _ := ret[0].(time.Duration) return ret0 } // GetDecisionScheduleToStartTimeout indicates an expected call of GetDecisionScheduleToStartTimeout. func (mr *MockMutableStateMockRecorder) GetDecisionScheduleToStartTimeout() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDecisionScheduleToStartTimeout", reflect.TypeOf((*MockMutableState)(nil).GetDecisionScheduleToStartTimeout)) } // GetDomainEntry mocks base method. func (m *MockMutableState) GetDomainEntry() *cache.DomainCacheEntry { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainEntry") ret0, _ := ret[0].(*cache.DomainCacheEntry) return ret0 } // GetDomainEntry indicates an expected call of GetDomainEntry. func (mr *MockMutableStateMockRecorder) GetDomainEntry() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainEntry", reflect.TypeOf((*MockMutableState)(nil).GetDomainEntry)) } // GetExecutionInfo mocks base method. func (m *MockMutableState) GetExecutionInfo() *persistence.WorkflowExecutionInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetExecutionInfo") ret0, _ := ret[0].(*persistence.WorkflowExecutionInfo) return ret0 } // GetExecutionInfo indicates an expected call of GetExecutionInfo. func (mr *MockMutableStateMockRecorder) GetExecutionInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExecutionInfo", reflect.TypeOf((*MockMutableState)(nil).GetExecutionInfo)) } // GetHistoryBuilder mocks base method. func (m *MockMutableState) GetHistoryBuilder() *HistoryBuilder { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryBuilder") ret0, _ := ret[0].(*HistoryBuilder) return ret0 } // GetHistoryBuilder indicates an expected call of GetHistoryBuilder. func (mr *MockMutableStateMockRecorder) GetHistoryBuilder() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryBuilder", reflect.TypeOf((*MockMutableState)(nil).GetHistoryBuilder)) } // GetHistorySize mocks base method. func (m *MockMutableState) GetHistorySize() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistorySize") ret0, _ := ret[0].(int64) return ret0 } // GetHistorySize indicates an expected call of GetHistorySize. func (mr *MockMutableStateMockRecorder) GetHistorySize() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistorySize", reflect.TypeOf((*MockMutableState)(nil).GetHistorySize)) } // GetInFlightDecision mocks base method. func (m *MockMutableState) GetInFlightDecision() (*DecisionInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetInFlightDecision") ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetInFlightDecision indicates an expected call of GetInFlightDecision. func (mr *MockMutableStateMockRecorder) GetInFlightDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInFlightDecision", reflect.TypeOf((*MockMutableState)(nil).GetInFlightDecision)) } // GetLastFirstEventID mocks base method. func (m *MockMutableState) GetLastFirstEventID() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLastFirstEventID") ret0, _ := ret[0].(int64) return ret0 } // GetLastFirstEventID indicates an expected call of GetLastFirstEventID. func (mr *MockMutableStateMockRecorder) GetLastFirstEventID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastFirstEventID", reflect.TypeOf((*MockMutableState)(nil).GetLastFirstEventID)) } // GetLastWriteVersion mocks base method. func (m *MockMutableState) GetLastWriteVersion() (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLastWriteVersion") ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLastWriteVersion indicates an expected call of GetLastWriteVersion. func (mr *MockMutableStateMockRecorder) GetLastWriteVersion() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastWriteVersion", reflect.TypeOf((*MockMutableState)(nil).GetLastWriteVersion)) } // GetNextEventID mocks base method. func (m *MockMutableState) GetNextEventID() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNextEventID") ret0, _ := ret[0].(int64) return ret0 } // GetNextEventID indicates an expected call of GetNextEventID. func (mr *MockMutableStateMockRecorder) GetNextEventID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNextEventID", reflect.TypeOf((*MockMutableState)(nil).GetNextEventID)) } // GetPendingActivityInfos mocks base method. func (m *MockMutableState) GetPendingActivityInfos() map[int64]*persistence.ActivityInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPendingActivityInfos") ret0, _ := ret[0].(map[int64]*persistence.ActivityInfo) return ret0 } // GetPendingActivityInfos indicates an expected call of GetPendingActivityInfos. func (mr *MockMutableStateMockRecorder) GetPendingActivityInfos() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingActivityInfos", reflect.TypeOf((*MockMutableState)(nil).GetPendingActivityInfos)) } // GetPendingChildExecutionInfos mocks base method. func (m *MockMutableState) GetPendingChildExecutionInfos() map[int64]*persistence.ChildExecutionInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPendingChildExecutionInfos") ret0, _ := ret[0].(map[int64]*persistence.ChildExecutionInfo) return ret0 } // GetPendingChildExecutionInfos indicates an expected call of GetPendingChildExecutionInfos. func (mr *MockMutableStateMockRecorder) GetPendingChildExecutionInfos() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingChildExecutionInfos", reflect.TypeOf((*MockMutableState)(nil).GetPendingChildExecutionInfos)) } // GetPendingDecision mocks base method. func (m *MockMutableState) GetPendingDecision() (*DecisionInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPendingDecision") ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetPendingDecision indicates an expected call of GetPendingDecision. func (mr *MockMutableStateMockRecorder) GetPendingDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingDecision", reflect.TypeOf((*MockMutableState)(nil).GetPendingDecision)) } // GetPendingRequestCancelExternalInfos mocks base method. func (m *MockMutableState) GetPendingRequestCancelExternalInfos() map[int64]*persistence.RequestCancelInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPendingRequestCancelExternalInfos") ret0, _ := ret[0].(map[int64]*persistence.RequestCancelInfo) return ret0 } // GetPendingRequestCancelExternalInfos indicates an expected call of GetPendingRequestCancelExternalInfos. func (mr *MockMutableStateMockRecorder) GetPendingRequestCancelExternalInfos() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingRequestCancelExternalInfos", reflect.TypeOf((*MockMutableState)(nil).GetPendingRequestCancelExternalInfos)) } // GetPendingSignalExternalInfos mocks base method. func (m *MockMutableState) GetPendingSignalExternalInfos() map[int64]*persistence.SignalInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPendingSignalExternalInfos") ret0, _ := ret[0].(map[int64]*persistence.SignalInfo) return ret0 } // GetPendingSignalExternalInfos indicates an expected call of GetPendingSignalExternalInfos. func (mr *MockMutableStateMockRecorder) GetPendingSignalExternalInfos() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingSignalExternalInfos", reflect.TypeOf((*MockMutableState)(nil).GetPendingSignalExternalInfos)) } // GetPendingTimerInfos mocks base method. func (m *MockMutableState) GetPendingTimerInfos() map[string]*persistence.TimerInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPendingTimerInfos") ret0, _ := ret[0].(map[string]*persistence.TimerInfo) return ret0 } // GetPendingTimerInfos indicates an expected call of GetPendingTimerInfos. func (mr *MockMutableStateMockRecorder) GetPendingTimerInfos() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingTimerInfos", reflect.TypeOf((*MockMutableState)(nil).GetPendingTimerInfos)) } // GetPreviousStartedEventID mocks base method. func (m *MockMutableState) GetPreviousStartedEventID() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPreviousStartedEventID") ret0, _ := ret[0].(int64) return ret0 } // GetPreviousStartedEventID indicates an expected call of GetPreviousStartedEventID. func (mr *MockMutableStateMockRecorder) GetPreviousStartedEventID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPreviousStartedEventID", reflect.TypeOf((*MockMutableState)(nil).GetPreviousStartedEventID)) } // GetQueryRegistry mocks base method. func (m *MockMutableState) GetQueryRegistry() query.Registry { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueryRegistry") ret0, _ := ret[0].(query.Registry) return ret0 } // GetQueryRegistry indicates an expected call of GetQueryRegistry. func (mr *MockMutableStateMockRecorder) GetQueryRegistry() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueryRegistry", reflect.TypeOf((*MockMutableState)(nil).GetQueryRegistry)) } // GetRequestCancelInfo mocks base method. func (m *MockMutableState) GetRequestCancelInfo(arg0 int64) (*persistence.RequestCancelInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRequestCancelInfo", arg0) ret0, _ := ret[0].(*persistence.RequestCancelInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetRequestCancelInfo indicates an expected call of GetRequestCancelInfo. func (mr *MockMutableStateMockRecorder) GetRequestCancelInfo(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRequestCancelInfo", reflect.TypeOf((*MockMutableState)(nil).GetRequestCancelInfo), arg0) } // GetRetryBackoffDuration mocks base method. func (m *MockMutableState) GetRetryBackoffDuration(errReason string) time.Duration { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRetryBackoffDuration", errReason) ret0, _ := ret[0].(time.Duration) return ret0 } // GetRetryBackoffDuration indicates an expected call of GetRetryBackoffDuration. func (mr *MockMutableStateMockRecorder) GetRetryBackoffDuration(errReason any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRetryBackoffDuration", reflect.TypeOf((*MockMutableState)(nil).GetRetryBackoffDuration), errReason) } // GetSignalInfo mocks base method. func (m *MockMutableState) GetSignalInfo(arg0 int64) (*persistence.SignalInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSignalInfo", arg0) ret0, _ := ret[0].(*persistence.SignalInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetSignalInfo indicates an expected call of GetSignalInfo. func (mr *MockMutableStateMockRecorder) GetSignalInfo(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSignalInfo", reflect.TypeOf((*MockMutableState)(nil).GetSignalInfo), arg0) } // GetStartEvent mocks base method. func (m *MockMutableState) GetStartEvent(arg0 context.Context) (*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetStartEvent", arg0) ret0, _ := ret[0].(*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStartEvent indicates an expected call of GetStartEvent. func (mr *MockMutableStateMockRecorder) GetStartEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStartEvent", reflect.TypeOf((*MockMutableState)(nil).GetStartEvent), arg0) } // GetStartVersion mocks base method. func (m *MockMutableState) GetStartVersion() (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetStartVersion") ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStartVersion indicates an expected call of GetStartVersion. func (mr *MockMutableStateMockRecorder) GetStartVersion() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStartVersion", reflect.TypeOf((*MockMutableState)(nil).GetStartVersion)) } // GetTimerTasks mocks base method. func (m *MockMutableState) GetTimerTasks() []persistence.Task { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTimerTasks") ret0, _ := ret[0].([]persistence.Task) return ret0 } // GetTimerTasks indicates an expected call of GetTimerTasks. func (mr *MockMutableStateMockRecorder) GetTimerTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimerTasks", reflect.TypeOf((*MockMutableState)(nil).GetTimerTasks)) } // GetTransferTasks mocks base method. func (m *MockMutableState) GetTransferTasks() []persistence.Task { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTransferTasks") ret0, _ := ret[0].([]persistence.Task) return ret0 } // GetTransferTasks indicates an expected call of GetTransferTasks. func (mr *MockMutableStateMockRecorder) GetTransferTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransferTasks", reflect.TypeOf((*MockMutableState)(nil).GetTransferTasks)) } // GetUpdateCondition mocks base method. func (m *MockMutableState) GetUpdateCondition() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUpdateCondition") ret0, _ := ret[0].(int64) return ret0 } // GetUpdateCondition indicates an expected call of GetUpdateCondition. func (mr *MockMutableStateMockRecorder) GetUpdateCondition() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUpdateCondition", reflect.TypeOf((*MockMutableState)(nil).GetUpdateCondition)) } // GetUserTimerInfo mocks base method. func (m *MockMutableState) GetUserTimerInfo(arg0 string) (*persistence.TimerInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUserTimerInfo", arg0) ret0, _ := ret[0].(*persistence.TimerInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetUserTimerInfo indicates an expected call of GetUserTimerInfo. func (mr *MockMutableStateMockRecorder) GetUserTimerInfo(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserTimerInfo", reflect.TypeOf((*MockMutableState)(nil).GetUserTimerInfo), arg0) } // GetUserTimerInfoByEventID mocks base method. func (m *MockMutableState) GetUserTimerInfoByEventID(arg0 int64) (*persistence.TimerInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUserTimerInfoByEventID", arg0) ret0, _ := ret[0].(*persistence.TimerInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetUserTimerInfoByEventID indicates an expected call of GetUserTimerInfoByEventID. func (mr *MockMutableStateMockRecorder) GetUserTimerInfoByEventID(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserTimerInfoByEventID", reflect.TypeOf((*MockMutableState)(nil).GetUserTimerInfoByEventID), arg0) } // GetVersionHistories mocks base method. func (m *MockMutableState) GetVersionHistories() *persistence.VersionHistories { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVersionHistories") ret0, _ := ret[0].(*persistence.VersionHistories) return ret0 } // GetVersionHistories indicates an expected call of GetVersionHistories. func (mr *MockMutableStateMockRecorder) GetVersionHistories() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVersionHistories", reflect.TypeOf((*MockMutableState)(nil).GetVersionHistories)) } // GetWorkflowStateCloseStatus mocks base method. func (m *MockMutableState) GetWorkflowStateCloseStatus() (int, int) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowStateCloseStatus") ret0, _ := ret[0].(int) ret1, _ := ret[1].(int) return ret0, ret1 } // GetWorkflowStateCloseStatus indicates an expected call of GetWorkflowStateCloseStatus. func (mr *MockMutableStateMockRecorder) GetWorkflowStateCloseStatus() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowStateCloseStatus", reflect.TypeOf((*MockMutableState)(nil).GetWorkflowStateCloseStatus)) } // GetWorkflowType mocks base method. func (m *MockMutableState) GetWorkflowType() *types.WorkflowType { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowType") ret0, _ := ret[0].(*types.WorkflowType) return ret0 } // GetWorkflowType indicates an expected call of GetWorkflowType. func (mr *MockMutableStateMockRecorder) GetWorkflowType() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowType", reflect.TypeOf((*MockMutableState)(nil).GetWorkflowType)) } // HasBufferedEvents mocks base method. func (m *MockMutableState) HasBufferedEvents() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasBufferedEvents") ret0, _ := ret[0].(bool) return ret0 } // HasBufferedEvents indicates an expected call of HasBufferedEvents. func (mr *MockMutableStateMockRecorder) HasBufferedEvents() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasBufferedEvents", reflect.TypeOf((*MockMutableState)(nil).HasBufferedEvents)) } // HasInFlightDecision mocks base method. func (m *MockMutableState) HasInFlightDecision() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasInFlightDecision") ret0, _ := ret[0].(bool) return ret0 } // HasInFlightDecision indicates an expected call of HasInFlightDecision. func (mr *MockMutableStateMockRecorder) HasInFlightDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasInFlightDecision", reflect.TypeOf((*MockMutableState)(nil).HasInFlightDecision)) } // HasParentExecution mocks base method. func (m *MockMutableState) HasParentExecution() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasParentExecution") ret0, _ := ret[0].(bool) return ret0 } // HasParentExecution indicates an expected call of HasParentExecution. func (mr *MockMutableStateMockRecorder) HasParentExecution() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasParentExecution", reflect.TypeOf((*MockMutableState)(nil).HasParentExecution)) } // HasPendingDecision mocks base method. func (m *MockMutableState) HasPendingDecision() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasPendingDecision") ret0, _ := ret[0].(bool) return ret0 } // HasPendingDecision indicates an expected call of HasPendingDecision. func (mr *MockMutableStateMockRecorder) HasPendingDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasPendingDecision", reflect.TypeOf((*MockMutableState)(nil).HasPendingDecision)) } // HasProcessedOrPendingDecision mocks base method. func (m *MockMutableState) HasProcessedOrPendingDecision() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasProcessedOrPendingDecision") ret0, _ := ret[0].(bool) return ret0 } // HasProcessedOrPendingDecision indicates an expected call of HasProcessedOrPendingDecision. func (mr *MockMutableStateMockRecorder) HasProcessedOrPendingDecision() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasProcessedOrPendingDecision", reflect.TypeOf((*MockMutableState)(nil).HasProcessedOrPendingDecision)) } // IsCancelRequested mocks base method. func (m *MockMutableState) IsCancelRequested() (bool, string) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsCancelRequested") ret0, _ := ret[0].(bool) ret1, _ := ret[1].(string) return ret0, ret1 } // IsCancelRequested indicates an expected call of IsCancelRequested. func (mr *MockMutableStateMockRecorder) IsCancelRequested() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsCancelRequested", reflect.TypeOf((*MockMutableState)(nil).IsCancelRequested)) } // IsCurrentWorkflowGuaranteed mocks base method. func (m *MockMutableState) IsCurrentWorkflowGuaranteed() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsCurrentWorkflowGuaranteed") ret0, _ := ret[0].(bool) return ret0 } // IsCurrentWorkflowGuaranteed indicates an expected call of IsCurrentWorkflowGuaranteed. func (mr *MockMutableStateMockRecorder) IsCurrentWorkflowGuaranteed() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsCurrentWorkflowGuaranteed", reflect.TypeOf((*MockMutableState)(nil).IsCurrentWorkflowGuaranteed)) } // IsResourceDuplicated mocks base method. func (m *MockMutableState) IsResourceDuplicated(resourceDedupKey definition.DeduplicationID) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsResourceDuplicated", resourceDedupKey) ret0, _ := ret[0].(bool) return ret0 } // IsResourceDuplicated indicates an expected call of IsResourceDuplicated. func (mr *MockMutableStateMockRecorder) IsResourceDuplicated(resourceDedupKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsResourceDuplicated", reflect.TypeOf((*MockMutableState)(nil).IsResourceDuplicated), resourceDedupKey) } // IsSignalRequested mocks base method. func (m *MockMutableState) IsSignalRequested(requestID string) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsSignalRequested", requestID) ret0, _ := ret[0].(bool) return ret0 } // IsSignalRequested indicates an expected call of IsSignalRequested. func (mr *MockMutableStateMockRecorder) IsSignalRequested(requestID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSignalRequested", reflect.TypeOf((*MockMutableState)(nil).IsSignalRequested), requestID) } // IsStickyTaskListEnabled mocks base method. func (m *MockMutableState) IsStickyTaskListEnabled() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsStickyTaskListEnabled") ret0, _ := ret[0].(bool) return ret0 } // IsStickyTaskListEnabled indicates an expected call of IsStickyTaskListEnabled. func (mr *MockMutableStateMockRecorder) IsStickyTaskListEnabled() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsStickyTaskListEnabled", reflect.TypeOf((*MockMutableState)(nil).IsStickyTaskListEnabled)) } // IsWorkflowCompleted mocks base method. func (m *MockMutableState) IsWorkflowCompleted() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsWorkflowCompleted") ret0, _ := ret[0].(bool) return ret0 } // IsWorkflowCompleted indicates an expected call of IsWorkflowCompleted. func (mr *MockMutableStateMockRecorder) IsWorkflowCompleted() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWorkflowCompleted", reflect.TypeOf((*MockMutableState)(nil).IsWorkflowCompleted)) } // IsWorkflowExecutionRunning mocks base method. func (m *MockMutableState) IsWorkflowExecutionRunning() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsWorkflowExecutionRunning") ret0, _ := ret[0].(bool) return ret0 } // IsWorkflowExecutionRunning indicates an expected call of IsWorkflowExecutionRunning. func (mr *MockMutableStateMockRecorder) IsWorkflowExecutionRunning() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWorkflowExecutionRunning", reflect.TypeOf((*MockMutableState)(nil).IsWorkflowExecutionRunning)) } // Load mocks base method. func (m *MockMutableState) Load(arg0 context.Context, arg1 *persistence.WorkflowMutableState) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Load", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // Load indicates an expected call of Load. func (mr *MockMutableStateMockRecorder) Load(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Load", reflect.TypeOf((*MockMutableState)(nil).Load), arg0, arg1) } // ReplicateActivityInfo mocks base method. func (m *MockMutableState) ReplicateActivityInfo(arg0 *types.SyncActivityRequest, arg1 bool) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateActivityInfo", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ReplicateActivityInfo indicates an expected call of ReplicateActivityInfo. func (mr *MockMutableStateMockRecorder) ReplicateActivityInfo(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateActivityInfo", reflect.TypeOf((*MockMutableState)(nil).ReplicateActivityInfo), arg0, arg1) } // ReplicateActivityTaskCancelRequestedEvent mocks base method. func (m *MockMutableState) ReplicateActivityTaskCancelRequestedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateActivityTaskCancelRequestedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateActivityTaskCancelRequestedEvent indicates an expected call of ReplicateActivityTaskCancelRequestedEvent. func (mr *MockMutableStateMockRecorder) ReplicateActivityTaskCancelRequestedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateActivityTaskCancelRequestedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateActivityTaskCancelRequestedEvent), arg0) } // ReplicateActivityTaskCanceledEvent mocks base method. func (m *MockMutableState) ReplicateActivityTaskCanceledEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateActivityTaskCanceledEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateActivityTaskCanceledEvent indicates an expected call of ReplicateActivityTaskCanceledEvent. func (mr *MockMutableStateMockRecorder) ReplicateActivityTaskCanceledEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateActivityTaskCanceledEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateActivityTaskCanceledEvent), arg0) } // ReplicateActivityTaskCompletedEvent mocks base method. func (m *MockMutableState) ReplicateActivityTaskCompletedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateActivityTaskCompletedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateActivityTaskCompletedEvent indicates an expected call of ReplicateActivityTaskCompletedEvent. func (mr *MockMutableStateMockRecorder) ReplicateActivityTaskCompletedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateActivityTaskCompletedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateActivityTaskCompletedEvent), arg0) } // ReplicateActivityTaskFailedEvent mocks base method. func (m *MockMutableState) ReplicateActivityTaskFailedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateActivityTaskFailedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateActivityTaskFailedEvent indicates an expected call of ReplicateActivityTaskFailedEvent. func (mr *MockMutableStateMockRecorder) ReplicateActivityTaskFailedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateActivityTaskFailedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateActivityTaskFailedEvent), arg0) } // ReplicateActivityTaskScheduledEvent mocks base method. func (m *MockMutableState) ReplicateActivityTaskScheduledEvent(arg0 int64, arg1 *types.HistoryEvent, arg2 bool) (*persistence.ActivityInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateActivityTaskScheduledEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*persistence.ActivityInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicateActivityTaskScheduledEvent indicates an expected call of ReplicateActivityTaskScheduledEvent. func (mr *MockMutableStateMockRecorder) ReplicateActivityTaskScheduledEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateActivityTaskScheduledEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateActivityTaskScheduledEvent), arg0, arg1, arg2) } // ReplicateActivityTaskStartedEvent mocks base method. func (m *MockMutableState) ReplicateActivityTaskStartedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateActivityTaskStartedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateActivityTaskStartedEvent indicates an expected call of ReplicateActivityTaskStartedEvent. func (mr *MockMutableStateMockRecorder) ReplicateActivityTaskStartedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateActivityTaskStartedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateActivityTaskStartedEvent), arg0) } // ReplicateActivityTaskTimedOutEvent mocks base method. func (m *MockMutableState) ReplicateActivityTaskTimedOutEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateActivityTaskTimedOutEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateActivityTaskTimedOutEvent indicates an expected call of ReplicateActivityTaskTimedOutEvent. func (mr *MockMutableStateMockRecorder) ReplicateActivityTaskTimedOutEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateActivityTaskTimedOutEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateActivityTaskTimedOutEvent), arg0) } // ReplicateChildWorkflowExecutionCanceledEvent mocks base method. func (m *MockMutableState) ReplicateChildWorkflowExecutionCanceledEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateChildWorkflowExecutionCanceledEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateChildWorkflowExecutionCanceledEvent indicates an expected call of ReplicateChildWorkflowExecutionCanceledEvent. func (mr *MockMutableStateMockRecorder) ReplicateChildWorkflowExecutionCanceledEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateChildWorkflowExecutionCanceledEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateChildWorkflowExecutionCanceledEvent), arg0) } // ReplicateChildWorkflowExecutionCompletedEvent mocks base method. func (m *MockMutableState) ReplicateChildWorkflowExecutionCompletedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateChildWorkflowExecutionCompletedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateChildWorkflowExecutionCompletedEvent indicates an expected call of ReplicateChildWorkflowExecutionCompletedEvent. func (mr *MockMutableStateMockRecorder) ReplicateChildWorkflowExecutionCompletedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateChildWorkflowExecutionCompletedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateChildWorkflowExecutionCompletedEvent), arg0) } // ReplicateChildWorkflowExecutionFailedEvent mocks base method. func (m *MockMutableState) ReplicateChildWorkflowExecutionFailedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateChildWorkflowExecutionFailedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateChildWorkflowExecutionFailedEvent indicates an expected call of ReplicateChildWorkflowExecutionFailedEvent. func (mr *MockMutableStateMockRecorder) ReplicateChildWorkflowExecutionFailedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateChildWorkflowExecutionFailedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateChildWorkflowExecutionFailedEvent), arg0) } // ReplicateChildWorkflowExecutionStartedEvent mocks base method. func (m *MockMutableState) ReplicateChildWorkflowExecutionStartedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateChildWorkflowExecutionStartedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateChildWorkflowExecutionStartedEvent indicates an expected call of ReplicateChildWorkflowExecutionStartedEvent. func (mr *MockMutableStateMockRecorder) ReplicateChildWorkflowExecutionStartedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateChildWorkflowExecutionStartedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateChildWorkflowExecutionStartedEvent), arg0) } // ReplicateChildWorkflowExecutionTerminatedEvent mocks base method. func (m *MockMutableState) ReplicateChildWorkflowExecutionTerminatedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateChildWorkflowExecutionTerminatedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateChildWorkflowExecutionTerminatedEvent indicates an expected call of ReplicateChildWorkflowExecutionTerminatedEvent. func (mr *MockMutableStateMockRecorder) ReplicateChildWorkflowExecutionTerminatedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateChildWorkflowExecutionTerminatedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateChildWorkflowExecutionTerminatedEvent), arg0) } // ReplicateChildWorkflowExecutionTimedOutEvent mocks base method. func (m *MockMutableState) ReplicateChildWorkflowExecutionTimedOutEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateChildWorkflowExecutionTimedOutEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateChildWorkflowExecutionTimedOutEvent indicates an expected call of ReplicateChildWorkflowExecutionTimedOutEvent. func (mr *MockMutableStateMockRecorder) ReplicateChildWorkflowExecutionTimedOutEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateChildWorkflowExecutionTimedOutEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateChildWorkflowExecutionTimedOutEvent), arg0) } // ReplicateDecisionTaskCompletedEvent mocks base method. func (m *MockMutableState) ReplicateDecisionTaskCompletedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateDecisionTaskCompletedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateDecisionTaskCompletedEvent indicates an expected call of ReplicateDecisionTaskCompletedEvent. func (mr *MockMutableStateMockRecorder) ReplicateDecisionTaskCompletedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateDecisionTaskCompletedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateDecisionTaskCompletedEvent), arg0) } // ReplicateDecisionTaskFailedEvent mocks base method. func (m *MockMutableState) ReplicateDecisionTaskFailedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateDecisionTaskFailedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateDecisionTaskFailedEvent indicates an expected call of ReplicateDecisionTaskFailedEvent. func (mr *MockMutableStateMockRecorder) ReplicateDecisionTaskFailedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateDecisionTaskFailedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateDecisionTaskFailedEvent), arg0) } // ReplicateDecisionTaskScheduledEvent mocks base method. func (m *MockMutableState) ReplicateDecisionTaskScheduledEvent(arg0, arg1 int64, arg2 string, arg3 int32, arg4, arg5, arg6 int64, arg7 bool) (*DecisionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateDecisionTaskScheduledEvent", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicateDecisionTaskScheduledEvent indicates an expected call of ReplicateDecisionTaskScheduledEvent. func (mr *MockMutableStateMockRecorder) ReplicateDecisionTaskScheduledEvent(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateDecisionTaskScheduledEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateDecisionTaskScheduledEvent), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) } // ReplicateDecisionTaskStartedEvent mocks base method. func (m *MockMutableState) ReplicateDecisionTaskStartedEvent(arg0 *DecisionInfo, arg1, arg2, arg3 int64, arg4 string, arg5 int64) (*DecisionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateDecisionTaskStartedEvent", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*DecisionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicateDecisionTaskStartedEvent indicates an expected call of ReplicateDecisionTaskStartedEvent. func (mr *MockMutableStateMockRecorder) ReplicateDecisionTaskStartedEvent(arg0, arg1, arg2, arg3, arg4, arg5 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateDecisionTaskStartedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateDecisionTaskStartedEvent), arg0, arg1, arg2, arg3, arg4, arg5) } // ReplicateDecisionTaskTimedOutEvent mocks base method. func (m *MockMutableState) ReplicateDecisionTaskTimedOutEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateDecisionTaskTimedOutEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateDecisionTaskTimedOutEvent indicates an expected call of ReplicateDecisionTaskTimedOutEvent. func (mr *MockMutableStateMockRecorder) ReplicateDecisionTaskTimedOutEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateDecisionTaskTimedOutEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateDecisionTaskTimedOutEvent), arg0) } // ReplicateExternalWorkflowExecutionCancelRequested mocks base method. func (m *MockMutableState) ReplicateExternalWorkflowExecutionCancelRequested(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateExternalWorkflowExecutionCancelRequested", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateExternalWorkflowExecutionCancelRequested indicates an expected call of ReplicateExternalWorkflowExecutionCancelRequested. func (mr *MockMutableStateMockRecorder) ReplicateExternalWorkflowExecutionCancelRequested(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateExternalWorkflowExecutionCancelRequested", reflect.TypeOf((*MockMutableState)(nil).ReplicateExternalWorkflowExecutionCancelRequested), arg0) } // ReplicateExternalWorkflowExecutionSignaled mocks base method. func (m *MockMutableState) ReplicateExternalWorkflowExecutionSignaled(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateExternalWorkflowExecutionSignaled", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateExternalWorkflowExecutionSignaled indicates an expected call of ReplicateExternalWorkflowExecutionSignaled. func (mr *MockMutableStateMockRecorder) ReplicateExternalWorkflowExecutionSignaled(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateExternalWorkflowExecutionSignaled", reflect.TypeOf((*MockMutableState)(nil).ReplicateExternalWorkflowExecutionSignaled), arg0) } // ReplicateRequestCancelExternalWorkflowExecutionFailedEvent mocks base method. func (m *MockMutableState) ReplicateRequestCancelExternalWorkflowExecutionFailedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateRequestCancelExternalWorkflowExecutionFailedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateRequestCancelExternalWorkflowExecutionFailedEvent indicates an expected call of ReplicateRequestCancelExternalWorkflowExecutionFailedEvent. func (mr *MockMutableStateMockRecorder) ReplicateRequestCancelExternalWorkflowExecutionFailedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateRequestCancelExternalWorkflowExecutionFailedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateRequestCancelExternalWorkflowExecutionFailedEvent), arg0) } // ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent mocks base method. func (m *MockMutableState) ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent(arg0 int64, arg1 *types.HistoryEvent, arg2 string) (*persistence.RequestCancelInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*persistence.RequestCancelInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent indicates an expected call of ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent. func (mr *MockMutableStateMockRecorder) ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent), arg0, arg1, arg2) } // ReplicateSignalExternalWorkflowExecutionFailedEvent mocks base method. func (m *MockMutableState) ReplicateSignalExternalWorkflowExecutionFailedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateSignalExternalWorkflowExecutionFailedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateSignalExternalWorkflowExecutionFailedEvent indicates an expected call of ReplicateSignalExternalWorkflowExecutionFailedEvent. func (mr *MockMutableStateMockRecorder) ReplicateSignalExternalWorkflowExecutionFailedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateSignalExternalWorkflowExecutionFailedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateSignalExternalWorkflowExecutionFailedEvent), arg0) } // ReplicateSignalExternalWorkflowExecutionInitiatedEvent mocks base method. func (m *MockMutableState) ReplicateSignalExternalWorkflowExecutionInitiatedEvent(arg0 int64, arg1 *types.HistoryEvent, arg2 string) (*persistence.SignalInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateSignalExternalWorkflowExecutionInitiatedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*persistence.SignalInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicateSignalExternalWorkflowExecutionInitiatedEvent indicates an expected call of ReplicateSignalExternalWorkflowExecutionInitiatedEvent. func (mr *MockMutableStateMockRecorder) ReplicateSignalExternalWorkflowExecutionInitiatedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateSignalExternalWorkflowExecutionInitiatedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateSignalExternalWorkflowExecutionInitiatedEvent), arg0, arg1, arg2) } // ReplicateStartChildWorkflowExecutionFailedEvent mocks base method. func (m *MockMutableState) ReplicateStartChildWorkflowExecutionFailedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateStartChildWorkflowExecutionFailedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateStartChildWorkflowExecutionFailedEvent indicates an expected call of ReplicateStartChildWorkflowExecutionFailedEvent. func (mr *MockMutableStateMockRecorder) ReplicateStartChildWorkflowExecutionFailedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateStartChildWorkflowExecutionFailedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateStartChildWorkflowExecutionFailedEvent), arg0) } // ReplicateStartChildWorkflowExecutionInitiatedEvent mocks base method. func (m *MockMutableState) ReplicateStartChildWorkflowExecutionInitiatedEvent(arg0 int64, arg1 *types.HistoryEvent, arg2 string) (*persistence.ChildExecutionInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateStartChildWorkflowExecutionInitiatedEvent", arg0, arg1, arg2) ret0, _ := ret[0].(*persistence.ChildExecutionInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicateStartChildWorkflowExecutionInitiatedEvent indicates an expected call of ReplicateStartChildWorkflowExecutionInitiatedEvent. func (mr *MockMutableStateMockRecorder) ReplicateStartChildWorkflowExecutionInitiatedEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateStartChildWorkflowExecutionInitiatedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateStartChildWorkflowExecutionInitiatedEvent), arg0, arg1, arg2) } // ReplicateTimerCanceledEvent mocks base method. func (m *MockMutableState) ReplicateTimerCanceledEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateTimerCanceledEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateTimerCanceledEvent indicates an expected call of ReplicateTimerCanceledEvent. func (mr *MockMutableStateMockRecorder) ReplicateTimerCanceledEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateTimerCanceledEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateTimerCanceledEvent), arg0) } // ReplicateTimerFiredEvent mocks base method. func (m *MockMutableState) ReplicateTimerFiredEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateTimerFiredEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateTimerFiredEvent indicates an expected call of ReplicateTimerFiredEvent. func (mr *MockMutableStateMockRecorder) ReplicateTimerFiredEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateTimerFiredEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateTimerFiredEvent), arg0) } // ReplicateTimerStartedEvent mocks base method. func (m *MockMutableState) ReplicateTimerStartedEvent(arg0 *types.HistoryEvent) (*persistence.TimerInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateTimerStartedEvent", arg0) ret0, _ := ret[0].(*persistence.TimerInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReplicateTimerStartedEvent indicates an expected call of ReplicateTimerStartedEvent. func (mr *MockMutableStateMockRecorder) ReplicateTimerStartedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateTimerStartedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateTimerStartedEvent), arg0) } // ReplicateTransientDecisionTaskScheduled mocks base method. func (m *MockMutableState) ReplicateTransientDecisionTaskScheduled() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateTransientDecisionTaskScheduled") ret0, _ := ret[0].(error) return ret0 } // ReplicateTransientDecisionTaskScheduled indicates an expected call of ReplicateTransientDecisionTaskScheduled. func (mr *MockMutableStateMockRecorder) ReplicateTransientDecisionTaskScheduled() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateTransientDecisionTaskScheduled", reflect.TypeOf((*MockMutableState)(nil).ReplicateTransientDecisionTaskScheduled)) } // ReplicateUpsertWorkflowSearchAttributesEvent mocks base method. func (m *MockMutableState) ReplicateUpsertWorkflowSearchAttributesEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateUpsertWorkflowSearchAttributesEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateUpsertWorkflowSearchAttributesEvent indicates an expected call of ReplicateUpsertWorkflowSearchAttributesEvent. func (mr *MockMutableStateMockRecorder) ReplicateUpsertWorkflowSearchAttributesEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateUpsertWorkflowSearchAttributesEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateUpsertWorkflowSearchAttributesEvent), arg0) } // ReplicateWorkflowExecutionCancelRequestedEvent mocks base method. func (m *MockMutableState) ReplicateWorkflowExecutionCancelRequestedEvent(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateWorkflowExecutionCancelRequestedEvent", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateWorkflowExecutionCancelRequestedEvent indicates an expected call of ReplicateWorkflowExecutionCancelRequestedEvent. func (mr *MockMutableStateMockRecorder) ReplicateWorkflowExecutionCancelRequestedEvent(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateWorkflowExecutionCancelRequestedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateWorkflowExecutionCancelRequestedEvent), arg0) } // ReplicateWorkflowExecutionCanceledEvent mocks base method. func (m *MockMutableState) ReplicateWorkflowExecutionCanceledEvent(arg0 int64, arg1 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateWorkflowExecutionCanceledEvent", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ReplicateWorkflowExecutionCanceledEvent indicates an expected call of ReplicateWorkflowExecutionCanceledEvent. func (mr *MockMutableStateMockRecorder) ReplicateWorkflowExecutionCanceledEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateWorkflowExecutionCanceledEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateWorkflowExecutionCanceledEvent), arg0, arg1) } // ReplicateWorkflowExecutionCompletedEvent mocks base method. func (m *MockMutableState) ReplicateWorkflowExecutionCompletedEvent(arg0 int64, arg1 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateWorkflowExecutionCompletedEvent", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ReplicateWorkflowExecutionCompletedEvent indicates an expected call of ReplicateWorkflowExecutionCompletedEvent. func (mr *MockMutableStateMockRecorder) ReplicateWorkflowExecutionCompletedEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateWorkflowExecutionCompletedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateWorkflowExecutionCompletedEvent), arg0, arg1) } // ReplicateWorkflowExecutionContinuedAsNewEvent mocks base method. func (m *MockMutableState) ReplicateWorkflowExecutionContinuedAsNewEvent(arg0 int64, arg1 string, arg2 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateWorkflowExecutionContinuedAsNewEvent", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // ReplicateWorkflowExecutionContinuedAsNewEvent indicates an expected call of ReplicateWorkflowExecutionContinuedAsNewEvent. func (mr *MockMutableStateMockRecorder) ReplicateWorkflowExecutionContinuedAsNewEvent(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateWorkflowExecutionContinuedAsNewEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateWorkflowExecutionContinuedAsNewEvent), arg0, arg1, arg2) } // ReplicateWorkflowExecutionFailedEvent mocks base method. func (m *MockMutableState) ReplicateWorkflowExecutionFailedEvent(arg0 int64, arg1 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateWorkflowExecutionFailedEvent", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ReplicateWorkflowExecutionFailedEvent indicates an expected call of ReplicateWorkflowExecutionFailedEvent. func (mr *MockMutableStateMockRecorder) ReplicateWorkflowExecutionFailedEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateWorkflowExecutionFailedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateWorkflowExecutionFailedEvent), arg0, arg1) } // ReplicateWorkflowExecutionSignaled mocks base method. func (m *MockMutableState) ReplicateWorkflowExecutionSignaled(arg0 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateWorkflowExecutionSignaled", arg0) ret0, _ := ret[0].(error) return ret0 } // ReplicateWorkflowExecutionSignaled indicates an expected call of ReplicateWorkflowExecutionSignaled. func (mr *MockMutableStateMockRecorder) ReplicateWorkflowExecutionSignaled(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateWorkflowExecutionSignaled", reflect.TypeOf((*MockMutableState)(nil).ReplicateWorkflowExecutionSignaled), arg0) } // ReplicateWorkflowExecutionStartedEvent mocks base method. func (m *MockMutableState) ReplicateWorkflowExecutionStartedEvent(arg0 *string, arg1 types.WorkflowExecution, arg2 string, arg3 *types.HistoryEvent, arg4 bool) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateWorkflowExecutionStartedEvent", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(error) return ret0 } // ReplicateWorkflowExecutionStartedEvent indicates an expected call of ReplicateWorkflowExecutionStartedEvent. func (mr *MockMutableStateMockRecorder) ReplicateWorkflowExecutionStartedEvent(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateWorkflowExecutionStartedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateWorkflowExecutionStartedEvent), arg0, arg1, arg2, arg3, arg4) } // ReplicateWorkflowExecutionTerminatedEvent mocks base method. func (m *MockMutableState) ReplicateWorkflowExecutionTerminatedEvent(arg0 int64, arg1 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateWorkflowExecutionTerminatedEvent", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ReplicateWorkflowExecutionTerminatedEvent indicates an expected call of ReplicateWorkflowExecutionTerminatedEvent. func (mr *MockMutableStateMockRecorder) ReplicateWorkflowExecutionTerminatedEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateWorkflowExecutionTerminatedEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateWorkflowExecutionTerminatedEvent), arg0, arg1) } // ReplicateWorkflowExecutionTimedoutEvent mocks base method. func (m *MockMutableState) ReplicateWorkflowExecutionTimedoutEvent(arg0 int64, arg1 *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateWorkflowExecutionTimedoutEvent", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ReplicateWorkflowExecutionTimedoutEvent indicates an expected call of ReplicateWorkflowExecutionTimedoutEvent. func (mr *MockMutableStateMockRecorder) ReplicateWorkflowExecutionTimedoutEvent(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateWorkflowExecutionTimedoutEvent", reflect.TypeOf((*MockMutableState)(nil).ReplicateWorkflowExecutionTimedoutEvent), arg0, arg1) } // RetryActivity mocks base method. func (m *MockMutableState) RetryActivity(ai *persistence.ActivityInfo, failureReason string, failureDetails []byte) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RetryActivity", ai, failureReason, failureDetails) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // RetryActivity indicates an expected call of RetryActivity. func (mr *MockMutableStateMockRecorder) RetryActivity(ai, failureReason, failureDetails any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetryActivity", reflect.TypeOf((*MockMutableState)(nil).RetryActivity), ai, failureReason, failureDetails) } // SetCurrentBranchToken mocks base method. func (m *MockMutableState) SetCurrentBranchToken(branchToken []byte) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetCurrentBranchToken", branchToken) ret0, _ := ret[0].(error) return ret0 } // SetCurrentBranchToken indicates an expected call of SetCurrentBranchToken. func (mr *MockMutableStateMockRecorder) SetCurrentBranchToken(branchToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCurrentBranchToken", reflect.TypeOf((*MockMutableState)(nil).SetCurrentBranchToken), branchToken) } // SetHistoryBuilder mocks base method. func (m *MockMutableState) SetHistoryBuilder(hBuilder *HistoryBuilder) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetHistoryBuilder", hBuilder) } // SetHistoryBuilder indicates an expected call of SetHistoryBuilder. func (mr *MockMutableStateMockRecorder) SetHistoryBuilder(hBuilder any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHistoryBuilder", reflect.TypeOf((*MockMutableState)(nil).SetHistoryBuilder), hBuilder) } // SetHistorySize mocks base method. func (m *MockMutableState) SetHistorySize(size int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetHistorySize", size) } // SetHistorySize indicates an expected call of SetHistorySize. func (mr *MockMutableStateMockRecorder) SetHistorySize(size any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHistorySize", reflect.TypeOf((*MockMutableState)(nil).SetHistorySize), size) } // SetHistoryTree mocks base method. func (m *MockMutableState) SetHistoryTree(treeID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetHistoryTree", treeID) ret0, _ := ret[0].(error) return ret0 } // SetHistoryTree indicates an expected call of SetHistoryTree. func (mr *MockMutableStateMockRecorder) SetHistoryTree(treeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHistoryTree", reflect.TypeOf((*MockMutableState)(nil).SetHistoryTree), treeID) } // SetQueryRegistry mocks base method. func (m *MockMutableState) SetQueryRegistry(arg0 query.Registry) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetQueryRegistry", arg0) } // SetQueryRegistry indicates an expected call of SetQueryRegistry. func (mr *MockMutableStateMockRecorder) SetQueryRegistry(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetQueryRegistry", reflect.TypeOf((*MockMutableState)(nil).SetQueryRegistry), arg0) } // SetUpdateCondition mocks base method. func (m *MockMutableState) SetUpdateCondition(arg0 int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetUpdateCondition", arg0) } // SetUpdateCondition indicates an expected call of SetUpdateCondition. func (mr *MockMutableStateMockRecorder) SetUpdateCondition(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUpdateCondition", reflect.TypeOf((*MockMutableState)(nil).SetUpdateCondition), arg0) } // SetVersionHistories mocks base method. func (m *MockMutableState) SetVersionHistories(arg0 *persistence.VersionHistories) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetVersionHistories", arg0) ret0, _ := ret[0].(error) return ret0 } // SetVersionHistories indicates an expected call of SetVersionHistories. func (mr *MockMutableStateMockRecorder) SetVersionHistories(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVersionHistories", reflect.TypeOf((*MockMutableState)(nil).SetVersionHistories), arg0) } // StartTransaction mocks base method. func (m *MockMutableState) StartTransaction(ctx context.Context, entry *cache.DomainCacheEntry, incomingTaskVersion int64) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StartTransaction", ctx, entry, incomingTaskVersion) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // StartTransaction indicates an expected call of StartTransaction. func (mr *MockMutableStateMockRecorder) StartTransaction(ctx, entry, incomingTaskVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartTransaction", reflect.TypeOf((*MockMutableState)(nil).StartTransaction), ctx, entry, incomingTaskVersion) } // UpdateActivity mocks base method. func (m *MockMutableState) UpdateActivity(arg0 *persistence.ActivityInfo) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateActivity", arg0) ret0, _ := ret[0].(error) return ret0 } // UpdateActivity indicates an expected call of UpdateActivity. func (mr *MockMutableStateMockRecorder) UpdateActivity(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateActivity", reflect.TypeOf((*MockMutableState)(nil).UpdateActivity), arg0) } // UpdateActivityProgress mocks base method. func (m *MockMutableState) UpdateActivityProgress(ai *persistence.ActivityInfo, request *types.RecordActivityTaskHeartbeatRequest) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdateActivityProgress", ai, request) } // UpdateActivityProgress indicates an expected call of UpdateActivityProgress. func (mr *MockMutableStateMockRecorder) UpdateActivityProgress(ai, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateActivityProgress", reflect.TypeOf((*MockMutableState)(nil).UpdateActivityProgress), ai, request) } // UpdateCurrentVersion mocks base method. func (m *MockMutableState) UpdateCurrentVersion(version int64, forceUpdate bool) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateCurrentVersion", version, forceUpdate) ret0, _ := ret[0].(error) return ret0 } // UpdateCurrentVersion indicates an expected call of UpdateCurrentVersion. func (mr *MockMutableStateMockRecorder) UpdateCurrentVersion(version, forceUpdate any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCurrentVersion", reflect.TypeOf((*MockMutableState)(nil).UpdateCurrentVersion), version, forceUpdate) } // UpdateDecision mocks base method. func (m *MockMutableState) UpdateDecision(arg0 *DecisionInfo) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdateDecision", arg0) } // UpdateDecision indicates an expected call of UpdateDecision. func (mr *MockMutableStateMockRecorder) UpdateDecision(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDecision", reflect.TypeOf((*MockMutableState)(nil).UpdateDecision), arg0) } // UpdateDuplicatedResource mocks base method. func (m *MockMutableState) UpdateDuplicatedResource(resourceDedupKey definition.DeduplicationID) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdateDuplicatedResource", resourceDedupKey) } // UpdateDuplicatedResource indicates an expected call of UpdateDuplicatedResource. func (mr *MockMutableStateMockRecorder) UpdateDuplicatedResource(resourceDedupKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDuplicatedResource", reflect.TypeOf((*MockMutableState)(nil).UpdateDuplicatedResource), resourceDedupKey) } // UpdateUserTimer mocks base method. func (m *MockMutableState) UpdateUserTimer(arg0 *persistence.TimerInfo) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateUserTimer", arg0) ret0, _ := ret[0].(error) return ret0 } // UpdateUserTimer indicates an expected call of UpdateUserTimer. func (mr *MockMutableStateMockRecorder) UpdateUserTimer(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserTimer", reflect.TypeOf((*MockMutableState)(nil).UpdateUserTimer), arg0) } // UpdateWorkflowStateCloseStatus mocks base method. func (m *MockMutableState) UpdateWorkflowStateCloseStatus(state, closeStatus int) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowStateCloseStatus", state, closeStatus) ret0, _ := ret[0].(error) return ret0 } // UpdateWorkflowStateCloseStatus indicates an expected call of UpdateWorkflowStateCloseStatus. func (mr *MockMutableStateMockRecorder) UpdateWorkflowStateCloseStatus(state, closeStatus any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowStateCloseStatus", reflect.TypeOf((*MockMutableState)(nil).UpdateWorkflowStateCloseStatus), state, closeStatus) } ================================================ FILE: service/history/execution/mutable_state_task_generator.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination mutable_state_task_generator_mock.go -self_package github.com/uber/cadence/service/history/execution package execution import ( "fmt" "math" "math/rand" "time" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // MutableStateTaskGenerator generates workflow transfer and timer tasks MutableStateTaskGenerator interface { // for workflow reset startTime should be the reset time instead of // the startEvent time, so that workflow timeout timestamp can be // re-calculated. GenerateWorkflowStartTasks( startTime time.Time, startEvent *types.HistoryEvent, ) error GenerateWorkflowCloseTasks( closeEvent *types.HistoryEvent, workflowDeletionTaskJitterRange int, ) error GenerateRecordWorkflowStartedTasks( startEvent *types.HistoryEvent, ) error GenerateDelayedDecisionTasks( startEvent *types.HistoryEvent, ) error GenerateDecisionScheduleTasks( decisionScheduleID int64, ) error GenerateDecisionStartTasks( decisionScheduleID int64, ) error GenerateActivityTransferTasks( event *types.HistoryEvent, ) error GenerateActivityRetryTasks( activityScheduleID int64, ) error GenerateChildWorkflowTasks( event *types.HistoryEvent, ) error GenerateRequestCancelExternalTasks( event *types.HistoryEvent, ) error GenerateSignalExternalTasks( event *types.HistoryEvent, ) error GenerateWorkflowSearchAttrTasks() error GenerateWorkflowResetTasks() error // these 2 APIs should only be called when mutable state transaction is being closed GenerateActivityTimerTasks() error GenerateUserTimerTasks() error } mutableStateTaskGeneratorImpl struct { logger log.Logger clusterMetadata cluster.Metadata domainCache cache.DomainCache mutableState MutableState } ) const ( defaultWorkflowRetentionInDays int32 = 1 defaultInitIntervalForDecisionRetry = 1 * time.Minute defaultMaxIntervalForDecisionRetry = 5 * time.Minute defaultJitterCoefficient = 0.2 ) var _ MutableStateTaskGenerator = (*mutableStateTaskGeneratorImpl)(nil) // NewMutableStateTaskGenerator creates a new task generator for mutable state func NewMutableStateTaskGenerator( logger log.Logger, clusterMetadata cluster.Metadata, domainCache cache.DomainCache, mutableState MutableState, ) MutableStateTaskGenerator { return &mutableStateTaskGeneratorImpl{ logger: logger, clusterMetadata: clusterMetadata, domainCache: domainCache, mutableState: mutableState, } } func (r *mutableStateTaskGeneratorImpl) GenerateWorkflowStartTasks( startTime time.Time, startEvent *types.HistoryEvent, ) error { attr := startEvent.WorkflowExecutionStartedEventAttributes firstDecisionDelayDuration := time.Duration(attr.GetFirstDecisionTaskBackoffSeconds()) * time.Second executionInfo := r.mutableState.GetExecutionInfo() startVersion := startEvent.Version workflowTimeoutDuration := time.Duration(executionInfo.WorkflowTimeout) * time.Second workflowTimeoutTimestamp := startTime.Add(workflowTimeoutDuration + firstDecisionDelayDuration) // ensure that the first attempt does not time out early based on retry policy timeout if attr.Attempt > 0 && !executionInfo.ExpirationTime.IsZero() && workflowTimeoutTimestamp.After(executionInfo.ExpirationTime) { workflowTimeoutTimestamp = executionInfo.ExpirationTime } r.mutableState.AddTimerTasks(&persistence.WorkflowTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: workflowTimeoutTimestamp, Version: startVersion, }, TaskList: executionInfo.TaskList, }) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateWorkflowCloseTasks( closeEvent *types.HistoryEvent, workflowDeletionTaskJitterRange int, ) error { executionInfo := r.mutableState.GetExecutionInfo() taskList := executionInfo.TaskList r.mutableState.AddTransferTasks(&persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID and VisibilityTimestamp are set by shard context Version: closeEvent.Version, }, TaskList: taskList, }) retentionInDays := defaultWorkflowRetentionInDays domainEntry, err := r.domainCache.GetDomainByID(executionInfo.DomainID) switch err.(type) { case nil: retentionInDays = domainEntry.GetRetentionDays(executionInfo.WorkflowID) case *types.EntityNotExistsError: // domain is not accessible, use default value above default: return err } closeTimestamp := time.Unix(0, closeEvent.GetTimestamp()) retentionDuration := (time.Duration(retentionInDays) * time.Hour * 24) if workflowDeletionTaskJitterRange > 1 { retentionDuration += time.Duration(rand.Intn(workflowDeletionTaskJitterRange*60)) * time.Second } r.logger.Debug("GenerateWorkflowCloseTasks", tag.WorkflowID(executionInfo.WorkflowID), tag.WorkflowRunID(executionInfo.RunID), tag.WorkflowDomainID(executionInfo.DomainID), tag.Timestamp(closeTimestamp), ) r.mutableState.AddTimerTasks(&persistence.DeleteHistoryEventTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: closeTimestamp.Add(retentionDuration), Version: closeEvent.Version, }, TaskList: taskList, }) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateDelayedDecisionTasks( startEvent *types.HistoryEvent, ) error { startVersion := startEvent.Version startTimestamp := time.Unix(0, startEvent.GetTimestamp()) startAttr := startEvent.WorkflowExecutionStartedEventAttributes decisionBackoffDuration := time.Duration(startAttr.GetFirstDecisionTaskBackoffSeconds()) * time.Second executionTimestamp := startTimestamp.Add(decisionBackoffDuration) // noParentWorkflow case firstDecisionDelayType := persistence.WorkflowBackoffTimeoutTypeCron // continue as new case if startAttr.Initiator != nil { switch startAttr.GetInitiator() { case types.ContinueAsNewInitiatorRetryPolicy: firstDecisionDelayType = persistence.WorkflowBackoffTimeoutTypeRetry case types.ContinueAsNewInitiatorCronSchedule: firstDecisionDelayType = persistence.WorkflowBackoffTimeoutTypeCron case types.ContinueAsNewInitiatorDecider: return &types.InternalServiceError{ Message: "encounter continue as new iterator & first decision delay not 0", } default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown iterator retry policy: %v", startAttr.GetInitiator()), } } } executionInfo := r.mutableState.GetExecutionInfo() r.mutableState.AddTimerTasks(&persistence.WorkflowBackoffTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: executionTimestamp, Version: startVersion, }, TimeoutType: firstDecisionDelayType, TaskList: executionInfo.TaskList, }) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateRecordWorkflowStartedTasks( startEvent *types.HistoryEvent, ) error { startVersion := startEvent.Version executionInfo := r.mutableState.GetExecutionInfo() r.mutableState.AddTransferTasks(&persistence.RecordWorkflowStartedTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID and VisibilityTimestamp are set by shard context Version: startVersion, }, TaskList: executionInfo.TaskList, }) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateDecisionScheduleTasks( decisionScheduleID int64, ) error { executionInfo := r.mutableState.GetExecutionInfo() decision, ok := r.mutableState.GetDecisionInfo( decisionScheduleID, ) if !ok { return &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending decision: %v", decisionScheduleID), } } originalTaskList := executionInfo.TaskList r.mutableState.AddTransferTasks(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID and VisibilityTimestamp are set by shard context Version: decision.Version, }, TargetDomainID: executionInfo.DomainID, TaskList: decision.TaskList, ScheduleID: decision.ScheduleID, OriginalTaskList: originalTaskList, OriginalTaskListKind: executionInfo.TaskListKind, }) if scheduleToStartTimeout := r.mutableState.GetDecisionScheduleToStartTimeout(); scheduleToStartTimeout != 0 { scheduledTime := time.Unix(0, decision.ScheduledTimestamp) r.mutableState.AddTimerTasks(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: scheduledTime.Add(scheduleToStartTimeout), Version: decision.Version, }, TimeoutType: int(TimerTypeScheduleToStart), EventID: decision.ScheduleID, ScheduleAttempt: decision.Attempt, TaskList: originalTaskList, }) } return nil } func (r *mutableStateTaskGeneratorImpl) GenerateDecisionStartTasks( decisionScheduleID int64, ) error { decision, ok := r.mutableState.GetDecisionInfo( decisionScheduleID, ) if !ok { return &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending decision: %v", decisionScheduleID), } } startedTime := time.Unix(0, decision.StartedTimestamp) startToCloseTimeout := time.Duration( decision.DecisionTimeout, ) * time.Second executionInfo := r.mutableState.GetExecutionInfo() // schedule timer exponentially if decision keeps failing if decision.Attempt > 1 { defaultStartToCloseTimeout := executionInfo.DecisionStartToCloseTimeout startToCloseTimeout = getNextDecisionTimeout(decision.Attempt, time.Duration(defaultStartToCloseTimeout)*time.Second) decision.DecisionTimeout = int32(startToCloseTimeout.Seconds()) // override decision timeout r.mutableState.UpdateDecision(decision) } r.mutableState.AddTimerTasks(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: startedTime.Add(startToCloseTimeout), Version: decision.Version, }, TimeoutType: int(TimerTypeStartToClose), EventID: decision.ScheduleID, ScheduleAttempt: decision.Attempt, TaskList: executionInfo.TaskList, }) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateActivityTransferTasks( event *types.HistoryEvent, ) error { attr := event.ActivityTaskScheduledEventAttributes activityScheduleID := event.ID activityInfo, ok := r.mutableState.GetActivityInfo(activityScheduleID) if !ok { return &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending activity: %v", activityScheduleID), } } var targetDomainID string var err error if activityInfo.DomainID != "" { targetDomainID = activityInfo.DomainID } else { // TODO remove this block after Mar, 1th, 2020 // previously, DomainID in activity info is not used, so need to get // schedule event from DB checking whether activity to be scheduled // belongs to this domain targetDomainID, err = r.getTargetDomainID(attr.GetDomain()) if err != nil { return err } } executionInfo := r.mutableState.GetExecutionInfo() r.mutableState.AddTransferTasks(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID and VisibilityTimestamp are set by shard context Version: activityInfo.Version, }, TargetDomainID: targetDomainID, TaskList: activityInfo.TaskList, ScheduleID: activityInfo.ScheduleID, }) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateActivityRetryTasks( activityScheduleID int64, ) error { ai, ok := r.mutableState.GetActivityInfo(activityScheduleID) if !ok { return &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending activity: %v", activityScheduleID), } } executionInfo := r.mutableState.GetExecutionInfo() r.mutableState.AddTimerTasks(&persistence.ActivityRetryTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID is set by shard Version: ai.Version, VisibilityTimestamp: ai.ScheduledTime, }, EventID: ai.ScheduleID, Attempt: int64(ai.Attempt), TaskList: ai.TaskList, }) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateChildWorkflowTasks( event *types.HistoryEvent, ) error { childWorkflowScheduleID := event.ID childWorkflowInfo, ok := r.mutableState.GetChildExecutionInfo(childWorkflowScheduleID) if !ok { return &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending child workflow: %v", childWorkflowScheduleID), } } msbDomainID := r.mutableState.GetDomainEntry().GetInfo().ID targetDomainID := childWorkflowInfo.DomainID if childWorkflowInfo.DomainID == "" { targetDomainID = msbDomainID } err := r.validateChildWorkflowParameters(msbDomainID, targetDomainID) if err != nil { return err } executionInfo := r.mutableState.GetExecutionInfo() startChildExecutionTask := &persistence.StartChildExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID and VisibilityTimestamp are set by shard context Version: childWorkflowInfo.Version, }, TargetDomainID: targetDomainID, TargetWorkflowID: childWorkflowInfo.StartedWorkflowID, InitiatedID: childWorkflowInfo.InitiatedID, TaskList: executionInfo.TaskList, } r.mutableState.AddTransferTasks(startChildExecutionTask) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateRequestCancelExternalTasks( event *types.HistoryEvent, ) error { attr := event.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes scheduleID := event.ID version := event.Version targetDomainName := attr.GetDomain() targetWorkflowID := attr.GetWorkflowExecution().GetWorkflowID() targetRunID := attr.GetWorkflowExecution().GetRunID() targetChildOnly := attr.GetChildWorkflowOnly() _, ok := r.mutableState.GetRequestCancelInfo(scheduleID) if !ok { return &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending request cancel external workflow: %v", scheduleID), } } targetDomainID, err := r.getTargetDomainID(targetDomainName) if err != nil { return err } executionInfo := r.mutableState.GetExecutionInfo() cancelExecutionTask := &persistence.CancelExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID and VisibilityTimestamp are set by shard context Version: version, }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: targetChildOnly, InitiatedID: scheduleID, TaskList: executionInfo.TaskList, } r.mutableState.AddTransferTasks(cancelExecutionTask) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateSignalExternalTasks( event *types.HistoryEvent, ) error { attr := event.SignalExternalWorkflowExecutionInitiatedEventAttributes scheduleID := event.ID version := event.Version targetDomainName := attr.GetDomain() targetWorkflowID := attr.GetWorkflowExecution().GetWorkflowID() targetRunID := attr.GetWorkflowExecution().GetRunID() targetChildOnly := attr.GetChildWorkflowOnly() _, ok := r.mutableState.GetSignalInfo(scheduleID) if !ok { return &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending signal external workflow: %v", scheduleID), } } targetDomainID, err := r.getTargetDomainID(targetDomainName) if err != nil { return err } executionInfo := r.mutableState.GetExecutionInfo() signalExecutionTask := &persistence.SignalExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID and VisibilityTimestamp are set by shard context Version: version, }, TargetDomainID: targetDomainID, TargetWorkflowID: targetWorkflowID, TargetRunID: targetRunID, TargetChildWorkflowOnly: targetChildOnly, InitiatedID: scheduleID, TaskList: executionInfo.TaskList, } r.mutableState.AddTransferTasks(signalExecutionTask) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateWorkflowSearchAttrTasks() error { currentVersion := r.mutableState.GetCurrentVersion() executionInfo := r.mutableState.GetExecutionInfo() r.mutableState.AddTransferTasks(&persistence.UpsertWorkflowSearchAttributesTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID and VisibilityTimestamp are set by shard context Version: currentVersion, // task processing does not check this version }, TaskList: executionInfo.TaskList, }) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateWorkflowResetTasks() error { currentVersion := r.mutableState.GetCurrentVersion() executionInfo := r.mutableState.GetExecutionInfo() r.mutableState.AddTransferTasks(&persistence.ResetWorkflowTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID and VisibilityTimestamp are set by shard context Version: currentVersion, }, TaskList: executionInfo.TaskList, }) return nil } func (r *mutableStateTaskGeneratorImpl) GenerateActivityTimerTasks() error { _, err := NewTimerSequence(r.mutableState).CreateNextActivityTimer() return err } func (r *mutableStateTaskGeneratorImpl) GenerateUserTimerTasks() error { _, err := NewTimerSequence(r.mutableState).CreateNextUserTimer() return err } func (r *mutableStateTaskGeneratorImpl) getTargetDomainID( targetDomainName string, ) (string, error) { if targetDomainName != "" { return r.domainCache.GetDomainID(targetDomainName) } return r.mutableState.GetExecutionInfo().DomainID, nil } func (r *mutableStateTaskGeneratorImpl) validateChildWorkflowParameters(msbDomainID string, targetDomainID string) error { // standard case if msbDomainID == targetDomainID { return nil } thisDomain := r.mutableState.GetDomainEntry() targetDomain, err := r.domainCache.GetDomainByID(targetDomainID) if err != nil { return fmt.Errorf("cannot get target domain for child workflow: %w", err) } // Generally, cross-domain calls are not allowed for launching child workflows in global domains due to // the fact that we have removed cross-cluster calls (due to their overhead and limited use). // // There is a limited exception for local domains, which do not suffer from this problem can be // handled as an exception where the transfer task may be picked up by another domain in-cluster // without risk of the child workflow may end up in a different cluster. if thisDomain.IsGlobalDomain() || targetDomain.IsGlobalDomain() { return &types.BadRequestError{ Message: fmt.Sprintf("The child workflow is "+ "trying to use domain %s but it's running in domain %s. "+ "Cross-cluster and cross domain child workflows are not supported for global domains", targetDomainID, msbDomainID), } } return nil } func getNextDecisionTimeout(attempt int64, defaultStartToCloseTimeout time.Duration) time.Duration { if attempt <= 1 { return defaultStartToCloseTimeout } nextInterval := float64(defaultInitIntervalForDecisionRetry) * math.Pow(2, float64(attempt-2)) nextInterval = math.Min(nextInterval, float64(defaultMaxIntervalForDecisionRetry)) jitterPortion := int(defaultJitterCoefficient * nextInterval) if jitterPortion < 1 { jitterPortion = 1 } nextInterval = nextInterval*(1-defaultJitterCoefficient) + float64(rand.Intn(jitterPortion)) return time.Duration(nextInterval) } ================================================ FILE: service/history/execution/mutable_state_task_generator_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: mutable_state_task_generator.go // // Generated by this command: // // mockgen -package execution -source mutable_state_task_generator.go -destination mutable_state_task_generator_mock.go -self_package github.com/uber/cadence/service/history/execution // // Package execution is a generated GoMock package. package execution import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockMutableStateTaskGenerator is a mock of MutableStateTaskGenerator interface. type MockMutableStateTaskGenerator struct { ctrl *gomock.Controller recorder *MockMutableStateTaskGeneratorMockRecorder isgomock struct{} } // MockMutableStateTaskGeneratorMockRecorder is the mock recorder for MockMutableStateTaskGenerator. type MockMutableStateTaskGeneratorMockRecorder struct { mock *MockMutableStateTaskGenerator } // NewMockMutableStateTaskGenerator creates a new mock instance. func NewMockMutableStateTaskGenerator(ctrl *gomock.Controller) *MockMutableStateTaskGenerator { mock := &MockMutableStateTaskGenerator{ctrl: ctrl} mock.recorder = &MockMutableStateTaskGeneratorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMutableStateTaskGenerator) EXPECT() *MockMutableStateTaskGeneratorMockRecorder { return m.recorder } // GenerateActivityRetryTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateActivityRetryTasks(activityScheduleID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateActivityRetryTasks", activityScheduleID) ret0, _ := ret[0].(error) return ret0 } // GenerateActivityRetryTasks indicates an expected call of GenerateActivityRetryTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateActivityRetryTasks(activityScheduleID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateActivityRetryTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateActivityRetryTasks), activityScheduleID) } // GenerateActivityTimerTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateActivityTimerTasks() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateActivityTimerTasks") ret0, _ := ret[0].(error) return ret0 } // GenerateActivityTimerTasks indicates an expected call of GenerateActivityTimerTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateActivityTimerTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateActivityTimerTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateActivityTimerTasks)) } // GenerateActivityTransferTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateActivityTransferTasks(event *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateActivityTransferTasks", event) ret0, _ := ret[0].(error) return ret0 } // GenerateActivityTransferTasks indicates an expected call of GenerateActivityTransferTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateActivityTransferTasks(event any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateActivityTransferTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateActivityTransferTasks), event) } // GenerateChildWorkflowTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateChildWorkflowTasks(event *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateChildWorkflowTasks", event) ret0, _ := ret[0].(error) return ret0 } // GenerateChildWorkflowTasks indicates an expected call of GenerateChildWorkflowTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateChildWorkflowTasks(event any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateChildWorkflowTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateChildWorkflowTasks), event) } // GenerateDecisionScheduleTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateDecisionScheduleTasks(decisionScheduleID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateDecisionScheduleTasks", decisionScheduleID) ret0, _ := ret[0].(error) return ret0 } // GenerateDecisionScheduleTasks indicates an expected call of GenerateDecisionScheduleTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateDecisionScheduleTasks(decisionScheduleID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateDecisionScheduleTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateDecisionScheduleTasks), decisionScheduleID) } // GenerateDecisionStartTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateDecisionStartTasks(decisionScheduleID int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateDecisionStartTasks", decisionScheduleID) ret0, _ := ret[0].(error) return ret0 } // GenerateDecisionStartTasks indicates an expected call of GenerateDecisionStartTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateDecisionStartTasks(decisionScheduleID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateDecisionStartTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateDecisionStartTasks), decisionScheduleID) } // GenerateDelayedDecisionTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateDelayedDecisionTasks(startEvent *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateDelayedDecisionTasks", startEvent) ret0, _ := ret[0].(error) return ret0 } // GenerateDelayedDecisionTasks indicates an expected call of GenerateDelayedDecisionTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateDelayedDecisionTasks(startEvent any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateDelayedDecisionTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateDelayedDecisionTasks), startEvent) } // GenerateRecordWorkflowStartedTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateRecordWorkflowStartedTasks(startEvent *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateRecordWorkflowStartedTasks", startEvent) ret0, _ := ret[0].(error) return ret0 } // GenerateRecordWorkflowStartedTasks indicates an expected call of GenerateRecordWorkflowStartedTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateRecordWorkflowStartedTasks(startEvent any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateRecordWorkflowStartedTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateRecordWorkflowStartedTasks), startEvent) } // GenerateRequestCancelExternalTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateRequestCancelExternalTasks(event *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateRequestCancelExternalTasks", event) ret0, _ := ret[0].(error) return ret0 } // GenerateRequestCancelExternalTasks indicates an expected call of GenerateRequestCancelExternalTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateRequestCancelExternalTasks(event any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateRequestCancelExternalTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateRequestCancelExternalTasks), event) } // GenerateSignalExternalTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateSignalExternalTasks(event *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateSignalExternalTasks", event) ret0, _ := ret[0].(error) return ret0 } // GenerateSignalExternalTasks indicates an expected call of GenerateSignalExternalTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateSignalExternalTasks(event any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateSignalExternalTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateSignalExternalTasks), event) } // GenerateUserTimerTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateUserTimerTasks() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateUserTimerTasks") ret0, _ := ret[0].(error) return ret0 } // GenerateUserTimerTasks indicates an expected call of GenerateUserTimerTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateUserTimerTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateUserTimerTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateUserTimerTasks)) } // GenerateWorkflowCloseTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateWorkflowCloseTasks(closeEvent *types.HistoryEvent, workflowDeletionTaskJitterRange int) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateWorkflowCloseTasks", closeEvent, workflowDeletionTaskJitterRange) ret0, _ := ret[0].(error) return ret0 } // GenerateWorkflowCloseTasks indicates an expected call of GenerateWorkflowCloseTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateWorkflowCloseTasks(closeEvent, workflowDeletionTaskJitterRange any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateWorkflowCloseTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateWorkflowCloseTasks), closeEvent, workflowDeletionTaskJitterRange) } // GenerateWorkflowResetTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateWorkflowResetTasks() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateWorkflowResetTasks") ret0, _ := ret[0].(error) return ret0 } // GenerateWorkflowResetTasks indicates an expected call of GenerateWorkflowResetTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateWorkflowResetTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateWorkflowResetTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateWorkflowResetTasks)) } // GenerateWorkflowSearchAttrTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateWorkflowSearchAttrTasks() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateWorkflowSearchAttrTasks") ret0, _ := ret[0].(error) return ret0 } // GenerateWorkflowSearchAttrTasks indicates an expected call of GenerateWorkflowSearchAttrTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateWorkflowSearchAttrTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateWorkflowSearchAttrTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateWorkflowSearchAttrTasks)) } // GenerateWorkflowStartTasks mocks base method. func (m *MockMutableStateTaskGenerator) GenerateWorkflowStartTasks(startTime time.Time, startEvent *types.HistoryEvent) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateWorkflowStartTasks", startTime, startEvent) ret0, _ := ret[0].(error) return ret0 } // GenerateWorkflowStartTasks indicates an expected call of GenerateWorkflowStartTasks. func (mr *MockMutableStateTaskGeneratorMockRecorder) GenerateWorkflowStartTasks(startTime, startEvent any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateWorkflowStartTasks", reflect.TypeOf((*MockMutableStateTaskGenerator)(nil).GenerateWorkflowStartTasks), startTime, startEvent) } ================================================ FILE: service/history/execution/mutable_state_task_generator_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "errors" "fmt" "math/rand" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" ) type ( mutableStateTaskGeneratorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockDomainCache *cache.MockDomainCache mockMutableState *MockMutableState taskGenerator *mutableStateTaskGeneratorImpl } ) func TestMutableStateTaskGeneratorSuite(t *testing.T) { s := new(mutableStateTaskGeneratorSuite) suite.Run(t, s) } func (s *mutableStateTaskGeneratorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockDomainCache = cache.NewMockDomainCache(s.controller) s.mockMutableState = NewMockMutableState(s.controller) s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil).AnyTimes() s.taskGenerator = NewMutableStateTaskGenerator( log.NewNoop(), constants.TestClusterMetadata, s.mockDomainCache, s.mockMutableState, ).(*mutableStateTaskGeneratorImpl) } func (s *mutableStateTaskGeneratorSuite) TearDownTest() { s.controller.Finish() } func (s *mutableStateTaskGeneratorSuite) TestGenerateWorkflowCloseTasks_JitteredDeletion() { now := time.Now() version := int64(123) closeEvent := &types.HistoryEvent{ EventType: types.EventTypeWorkflowExecutionCompleted.Ptr(), Timestamp: common.Int64Ptr(now.UnixNano()), Version: version, } domainEntry, err := s.mockDomainCache.GetDomainByID(constants.TestDomainID) s.NoError(err) retention := time.Duration(domainEntry.GetRetentionDays(constants.TestWorkflowID)) * time.Hour * 24 testCases := GenerateWorkflowCloseTasksTestCases(retention, closeEvent, now) for _, tc := range testCases { // create new mockMutableState so can we can setup separete mock for each test case mockMutableState := NewMockMutableState(s.controller) taskGenerator := NewMutableStateTaskGenerator( log.NewNoop(), constants.TestClusterMetadata, s.mockDomainCache, mockMutableState, ) var transferTasks []persistence.Task var timerTasks []persistence.Task mockMutableState.EXPECT().GetDomainEntry().Return(domainEntry).AnyTimes() mockMutableState.EXPECT().AddTransferTasks(gomock.Any()).Do(func(tasks ...persistence.Task) { transferTasks = tasks }).MaxTimes(1) mockMutableState.EXPECT().AddTimerTasks(gomock.Any()).Do(func(tasks ...persistence.Task) { timerTasks = tasks }).MaxTimes(1) tc.setupFn(mockMutableState) err := taskGenerator.GenerateWorkflowCloseTasks(closeEvent, 60) s.NoError(err) actualGeneratedTasks := transferTasks for _, task := range actualGeneratedTasks { // force set visibility timestamp since that field is not assigned // for transfer and cross cluster in GenerateWorkflowCloseTasks // it will be set by shard context // set it to now so that we can easily test if other fields are equal task.SetVisibilityTimestamp(now) } for _, task := range timerTasks { // force set timer tasks because with jittering the timertask visibility time stamp // is not consistent for each run. // as long as code doesn't break during generation we should be ok task.SetVisibilityTimestamp(time.Unix(0, closeEvent.GetTimestamp()).Add(retention)) } actualGeneratedTasks = append(actualGeneratedTasks, timerTasks...) s.Equal(tc.generatedTasks, actualGeneratedTasks) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateWorkflowCloseTasks() { now := time.Now() version := int64(123) closeEvent := &types.HistoryEvent{ EventType: types.EventTypeWorkflowExecutionCompleted.Ptr(), Timestamp: common.Int64Ptr(now.UnixNano()), Version: version, } domainEntry, err := s.mockDomainCache.GetDomainByID(constants.TestDomainID) s.NoError(err) retention := time.Duration(domainEntry.GetRetentionDays(constants.TestWorkflowID)) * time.Hour * 24 testCases := GenerateWorkflowCloseTasksTestCases(retention, closeEvent, now) for _, tc := range testCases { // create new mockMutableState so can we can setup separete mock for each test case mockMutableState := NewMockMutableState(s.controller) taskGenerator := NewMutableStateTaskGenerator( log.NewNoop(), constants.TestClusterMetadata, s.mockDomainCache, mockMutableState, ) var transferTasks []persistence.Task var timerTasks []persistence.Task mockMutableState.EXPECT().GetDomainEntry().Return(domainEntry).AnyTimes() mockMutableState.EXPECT().AddTransferTasks(gomock.Any()).Do(func(tasks ...persistence.Task) { transferTasks = tasks }).MaxTimes(1) mockMutableState.EXPECT().AddTimerTasks(gomock.Any()).Do(func(tasks ...persistence.Task) { timerTasks = tasks }).MaxTimes(1) tc.setupFn(mockMutableState) err := taskGenerator.GenerateWorkflowCloseTasks(closeEvent, 1) s.NoError(err) actualGeneratedTasks := transferTasks for _, task := range actualGeneratedTasks { // force set visibility timestamp since that field is not assigned // for transfer and cross cluster in GenerateWorkflowCloseTasks // it will be set by shard context // set it to now so that we can easily test if other fields are equal task.SetVisibilityTimestamp(now) } actualGeneratedTasks = append(actualGeneratedTasks, timerTasks...) s.Equal(tc.generatedTasks, actualGeneratedTasks) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateWorkflowCloseTasks_NotActive() { closeEvent := &types.HistoryEvent{ Version: constants.TestVersion, Timestamp: common.Ptr(time.Unix(1719224698, 0).UnixNano()), } s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "some-domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }).Times(1) domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{}, &persistence.DomainConfig{ Retention: defaultWorkflowRetentionInDays, }, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1, ) s.mockDomainCache.EXPECT().GetDomainByID("some-domain-id").Return(domainEntry, nil).Times(1) var transferTasks []persistence.Task transferTasks = append(transferTasks, &persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some-domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ Version: constants.TestVersion, }, TaskList: "task-list", }) s.mockMutableState.EXPECT().AddTransferTasks(transferTasks).Times(1) expectedDeletionTS := time.Unix(0, closeEvent.GetTimestamp()). Add(time.Duration(defaultWorkflowRetentionInDays) * time.Hour * 24) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.DeleteHistoryEventTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some-domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: expectedDeletionTS, Version: closeEvent.Version, }, TaskList: "task-list", }) err := s.taskGenerator.GenerateWorkflowCloseTasks(closeEvent, 1) s.NoError(err) } func (s *mutableStateTaskGeneratorSuite) TestGenerateFromTransferTask() { now := time.Now() testCases := []struct { transferTask *persistence.TransferTaskInfo expectError bool }{ { transferTask: &persistence.TransferTaskInfo{ TaskType: persistence.TransferTaskTypeActivityTask, }, expectError: true, }, { transferTask: &persistence.TransferTaskInfo{ TaskType: persistence.TransferTaskTypeCancelExecution, TargetDomainID: constants.TestDomainID, TargetWorkflowID: constants.TestWorkflowID, TargetRunID: constants.TestRunID, TargetChildWorkflowOnly: false, ScheduleID: int64(123), }, expectError: false, }, { transferTask: &persistence.TransferTaskInfo{ TaskType: persistence.TransferTaskTypeSignalExecution, TargetDomainID: constants.TestDomainID, TargetWorkflowID: constants.TestWorkflowID, TargetRunID: constants.TestRunID, TargetChildWorkflowOnly: false, ScheduleID: int64(123), }, expectError: false, }, { transferTask: &persistence.TransferTaskInfo{ TaskType: persistence.TransferTaskTypeStartChildExecution, TargetDomainID: constants.TestDomainID, TargetWorkflowID: constants.TestWorkflowID, ScheduleID: int64(123), }, expectError: false, }, } for _, tc := range testCases { if !tc.expectError { tc.transferTask.Version = int64(101) tc.transferTask.VisibilityTimestamp = now } } } func (s *mutableStateTaskGeneratorSuite) TestGetNextDecisionTimeout() { defaultStartToCloseTimeout := 10 * time.Second expectedResult := []time.Duration{ defaultStartToCloseTimeout, defaultStartToCloseTimeout, defaultInitIntervalForDecisionRetry, defaultInitIntervalForDecisionRetry * 2, defaultInitIntervalForDecisionRetry * 4, defaultMaxIntervalForDecisionRetry, defaultMaxIntervalForDecisionRetry, defaultMaxIntervalForDecisionRetry, } for i := 0; i < len(expectedResult); i++ { next := getNextDecisionTimeout(int64(i), defaultStartToCloseTimeout) expected := expectedResult[i] min, max := getNextBackoffRange(expected) s.True(next >= min, "NextBackoff too low: actual: %v, expected: %v", next, expected) s.True(next <= max, "NextBackoff too high: actual: %v, expected: %v", next, expected) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateWorkflowStartTasks() { startTime := time.Now() expirationTime := startTime.Add(5 * time.Second) testCases := []struct { name string startEvent *types.HistoryEvent workflowTimeout int32 visibilityTimestamp time.Time }{ { name: "Success case - first attempt", startEvent: &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{Attempt: 0}, Version: constants.TestVersion, }, workflowTimeout: 10, visibilityTimestamp: startTime.Add(time.Duration(10) * time.Second), }, { name: "Success case - second attempt and workflowTimeoutTimestamp before expirationTime", startEvent: &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{Attempt: 1}, Version: constants.TestVersion, }, workflowTimeout: 1, visibilityTimestamp: startTime.Add(time.Duration(1) * time.Second), }, { name: "Success case - second attempt and workflowTimeoutTimestamp after expirationTime", startEvent: &types.HistoryEvent{ WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{Attempt: 1}, Version: constants.TestVersion, }, workflowTimeout: 6, visibilityTimestamp: expirationTime, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{WorkflowTimeout: tc.workflowTimeout, ExpirationTime: expirationTime, TaskList: "task-list"}).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.WorkflowTimeoutTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: tc.visibilityTimestamp, Version: tc.startEvent.Version, }, TaskList: "task-list", }).Times(1) err := s.taskGenerator.GenerateWorkflowStartTasks(startTime, tc.startEvent) s.NoError(err) }) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateDelayedDecisionTasks() { timestamp := common.Int64Ptr(time.Now().UnixNano()) firstDecisionTaskBackoffSeconds := common.Int32Ptr(1) testCases := []struct { name string startEvent *types.HistoryEvent setupMock func() err error }{ { name: "Success case - nil initiator", startEvent: &types.HistoryEvent{ Version: constants.TestVersion, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ Initiator: nil, FirstDecisionTaskBackoffSeconds: firstDecisionTaskBackoffSeconds, }, Timestamp: timestamp, }, setupMock: func() { s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.WorkflowBackoffTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Unix(0, *timestamp).Add(time.Duration(*firstDecisionTaskBackoffSeconds) * time.Second), Version: constants.TestVersion, }, TimeoutType: persistence.WorkflowBackoffTimeoutTypeCron, TaskList: "task-list", }).Times(1) }, }, { name: "Success case - retry policy initiator", startEvent: &types.HistoryEvent{ Version: constants.TestVersion, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ Initiator: types.ContinueAsNewInitiatorRetryPolicy.Ptr(), FirstDecisionTaskBackoffSeconds: firstDecisionTaskBackoffSeconds, }, Timestamp: timestamp, }, setupMock: func() { s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.WorkflowBackoffTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Unix(0, *timestamp).Add(time.Duration(*firstDecisionTaskBackoffSeconds) * time.Second), Version: constants.TestVersion, }, TimeoutType: persistence.WorkflowBackoffTimeoutTypeRetry, TaskList: "task-list", }).Times(1) }, }, { name: "Success case - cron initiator", startEvent: &types.HistoryEvent{ Version: constants.TestVersion, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ Initiator: types.ContinueAsNewInitiatorCronSchedule.Ptr(), FirstDecisionTaskBackoffSeconds: firstDecisionTaskBackoffSeconds, }, Timestamp: timestamp, }, setupMock: func() { s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.WorkflowBackoffTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Unix(0, *timestamp).Add(time.Duration(*firstDecisionTaskBackoffSeconds) * time.Second), Version: constants.TestVersion, }, TimeoutType: persistence.WorkflowBackoffTimeoutTypeCron, TaskList: "task-list", }).Times(1) }, }, { name: "Error case - decider initiator", startEvent: &types.HistoryEvent{ Version: constants.TestVersion, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ Initiator: types.ContinueAsNewInitiatorDecider.Ptr(), FirstDecisionTaskBackoffSeconds: firstDecisionTaskBackoffSeconds, }, Timestamp: timestamp, }, setupMock: func() {}, err: &types.InternalServiceError{Message: "encounter continue as new iterator & first decision delay not 0"}, }, { name: "Error case - unknown initiator", startEvent: &types.HistoryEvent{ Version: constants.TestVersion, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ Initiator: types.ContinueAsNewInitiator(3).Ptr(), FirstDecisionTaskBackoffSeconds: firstDecisionTaskBackoffSeconds, }, Timestamp: timestamp, }, setupMock: func() {}, err: &types.InternalServiceError{Message: fmt.Sprintf("unknown iterator retry policy: %v", types.ContinueAsNewInitiator(3).Ptr())}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMock() err := s.taskGenerator.GenerateDelayedDecisionTasks(tc.startEvent) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateRecordWorkflowStartedTasks() { startEvent := &types.HistoryEvent{ Version: constants.TestVersion, } s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }).Times(1) s.mockMutableState.EXPECT().AddTransferTasks(&persistence.RecordWorkflowStartedTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ Version: startEvent.Version, }, TaskList: "task-list", }).Times(1) err := s.taskGenerator.GenerateRecordWorkflowStartedTasks(startEvent) s.NoError(err) } func (s *mutableStateTaskGeneratorSuite) TestGenerateDecisionScheduleTasks() { decisionScheduleID := int64(123) executionInfo := &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, TaskList: "task-list", TaskListKind: types.TaskListKindEphemeral, } decision := &DecisionInfo{ TaskList: "taskList", ScheduleID: 123, Version: constants.TestVersion, ScheduledTimestamp: time.Now().UnixNano(), Attempt: 1, } testCases := []struct { name string setupMock func() err error }{ { name: "Success case - scheduleToStartTimeout 0", setupMock: func() { s.mockMutableState.EXPECT().GetDecisionInfo(decisionScheduleID).Return(decision, true).Times(1) s.mockMutableState.EXPECT().AddTransferTasks(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, TaskData: persistence.TaskData{ Version: decision.Version, }, TargetDomainID: executionInfo.DomainID, TaskList: decision.TaskList, ScheduleID: decision.ScheduleID, OriginalTaskList: "task-list", OriginalTaskListKind: types.TaskListKindEphemeral, }).Times(1) s.mockMutableState.EXPECT().GetDecisionScheduleToStartTimeout().Return(time.Duration(0)).Times(1) }, }, { name: "Success case - scheduleToStartTimeout not 0", setupMock: func() { s.mockMutableState.EXPECT().GetDecisionInfo(decisionScheduleID).Return(decision, true).Times(1) s.mockMutableState.EXPECT().AddTransferTasks(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, TaskData: persistence.TaskData{ Version: decision.Version, }, TargetDomainID: executionInfo.DomainID, TaskList: decision.TaskList, ScheduleID: decision.ScheduleID, OriginalTaskList: "task-list", OriginalTaskListKind: types.TaskListKindEphemeral, }).Times(1) scheduleToStartTimeout := time.Duration(1) s.mockMutableState.EXPECT().GetDecisionScheduleToStartTimeout().Return(scheduleToStartTimeout).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Unix(0, decision.ScheduledTimestamp).Add(scheduleToStartTimeout), Version: decision.Version, }, TimeoutType: int(TimerTypeScheduleToStart), EventID: decision.ScheduleID, ScheduleAttempt: decision.Attempt, TaskList: "task-list", }).Times(1) }, }, { name: "Error case - GetDecisionInfo error", setupMock: func() { s.mockMutableState.EXPECT().GetDecisionInfo(decisionScheduleID).Return(nil, false).Times(1) }, err: &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending decision: %v", decisionScheduleID), }, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { s.mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo).Times(1) tc.setupMock() err := s.taskGenerator.GenerateDecisionScheduleTasks(decisionScheduleID) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateDecisionStartTasks() { seed := int64(1) rand.Seed(seed) decisionScheduleID := int64(123) getDecision := func() *DecisionInfo { return &DecisionInfo{ Version: constants.TestVersion, ScheduleID: 123, StartedTimestamp: time.Now().UnixNano(), } } testCases := []struct { name string setupMock func() err error }{ { name: "Success case - attempt greater than 1", setupMock: func() { decision := getDecision() decision.Attempt = 2 s.mockMutableState.EXPECT().GetDecisionInfo(decisionScheduleID).Return(decision, true).Times(1) defaultStartToCloseTimeout := int32(1) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", DecisionStartToCloseTimeout: defaultStartToCloseTimeout, TaskList: "task-list", }).Times(1) startToCloseTimeout := getNextDecisionTimeout(decision.Attempt, time.Duration(defaultStartToCloseTimeout)*time.Second) decision.DecisionTimeout = int32(startToCloseTimeout.Seconds()) s.mockMutableState.EXPECT().UpdateDecision(decision).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Unix(0, decision.StartedTimestamp).Add(startToCloseTimeout), Version: decision.Version, }, TimeoutType: int(TimerTypeStartToClose), EventID: decision.ScheduleID, ScheduleAttempt: decision.Attempt, TaskList: "task-list", }) rand.Seed(seed) }, }, { name: "Success case - attempt less or equal to 1", setupMock: func() { decision := getDecision() decision.DecisionTimeout = 1 s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }) s.mockMutableState.EXPECT().GetDecisionInfo(decisionScheduleID).Return(decision, true).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Unix(0, decision.StartedTimestamp).Add(time.Duration(decision.DecisionTimeout) * time.Second), Version: decision.Version, }, TimeoutType: int(TimerTypeStartToClose), EventID: decision.ScheduleID, ScheduleAttempt: decision.Attempt, TaskList: "task-list", }) }, }, { name: "Error case - GetDecisionInfo error", setupMock: func() { s.mockMutableState.EXPECT().GetDecisionInfo(decisionScheduleID).Return(nil, false).Times(1) }, err: &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending decision: %v", decisionScheduleID), }, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMock() err := s.taskGenerator.GenerateDecisionStartTasks(decisionScheduleID) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateActivityTransferTasks() { domain := constants.TestDomainName event := &types.HistoryEvent{ ID: 123, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ Domain: &domain, }, } getActivityInfo := func() *persistence.ActivityInfo { return &persistence.ActivityInfo{ Version: constants.TestVersion, TaskList: "taskList", ScheduleID: 456, } } testCases := []struct { name string setupMock func() err error }{ { name: "Success case - DomainID is not empty", setupMock: func() { activityInfo := getActivityInfo() activityInfo.DomainID = constants.TestDomainID s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }) s.mockMutableState.EXPECT().GetActivityInfo(event.ID).Return(activityInfo, true).Times(1) s.mockMutableState.EXPECT().AddTransferTasks(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ Version: activityInfo.Version, }, TargetDomainID: activityInfo.DomainID, TaskList: activityInfo.TaskList, ScheduleID: activityInfo.ScheduleID, }).Times(1) }, }, { name: "Success case - DomainID is empty", setupMock: func() { activityInfo := getActivityInfo() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }) s.mockMutableState.EXPECT().GetActivityInfo(event.ID).Return(activityInfo, true).Times(1) s.mockDomainCache.EXPECT().GetDomainID(event.ActivityTaskScheduledEventAttributes.GetDomain()).Return(event.ActivityTaskScheduledEventAttributes.GetDomain(), nil).Times(1) s.mockMutableState.EXPECT().AddTransferTasks(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ Version: activityInfo.Version, }, TargetDomainID: event.ActivityTaskScheduledEventAttributes.GetDomain(), TaskList: activityInfo.TaskList, ScheduleID: activityInfo.ScheduleID, }).Times(1) }, }, { name: "Error case - GetActivityInfo error", setupMock: func() { s.mockMutableState.EXPECT().GetActivityInfo(event.ID).Return(nil, false).Times(1) }, err: &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending activity: %v", event.ID), }, }, { name: "Error case - getTargetDomainID error", setupMock: func() { activityInfo := getActivityInfo() s.mockMutableState.EXPECT().GetActivityInfo(event.ID).Return(activityInfo, true).Times(1) s.mockDomainCache.EXPECT().GetDomainID(event.ActivityTaskScheduledEventAttributes.GetDomain()). Return("", errors.New("get-target-domain-id-error")).Times(1) }, err: errors.New("get-target-domain-id-error"), }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMock() err := s.taskGenerator.GenerateActivityTransferTasks(event) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateActivityRetryTasks() { activityScheduleID := int64(123) testCases := []struct { name string setupMock func() err error }{ { name: "Success case", setupMock: func() { ai := &persistence.ActivityInfo{ Version: constants.TestVersion, ScheduledTime: time.Now(), ScheduleID: activityScheduleID, Attempt: 1, TaskList: "task-list2", } s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }) s.mockMutableState.EXPECT().GetActivityInfo(activityScheduleID).Return(ai, true).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.ActivityRetryTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ Version: ai.Version, VisibilityTimestamp: ai.ScheduledTime, }, EventID: ai.ScheduleID, Attempt: int64(ai.Attempt), TaskList: "task-list2", }).Times(1) }, }, { name: "Error case - GetActivityInfo error", setupMock: func() { s.mockMutableState.EXPECT().GetActivityInfo(activityScheduleID).Return(nil, false).Times(1) }, err: &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending activity: %v", activityScheduleID), }, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMock() err := s.taskGenerator.GenerateActivityRetryTasks(activityScheduleID) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateChildWorkflowTasks() { eventID := int64(123) getChildWorkflowInfo := func() *persistence.ChildExecutionInfo { return &persistence.ChildExecutionInfo{ Version: constants.TestVersion, InitiatedID: 123, StartedWorkflowID: constants.TestWorkflowID, } } parentDomain := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ ID: constants.TestDomainID, Name: constants.TestDomainName, }, &persistence.DomainConfig{}, nil, 0, ) testCases := []struct { name string setupMock func() domainName string err error }{ { name: "Success case - targetDomainID is not empty and isCrossClusterTask is false", setupMock: func() { childWorkflowInfo := getChildWorkflowInfo() childWorkflowInfo.DomainID = constants.TestDomainID s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }) s.mockMutableState.EXPECT().GetDomainEntry().Return(parentDomain).Times(1) s.mockMutableState.EXPECT().GetChildExecutionInfo(eventID).Return(childWorkflowInfo, true).Times(1) s.mockMutableState.EXPECT().AddTransferTasks(&persistence.StartChildExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ Version: childWorkflowInfo.Version, }, TargetDomainID: childWorkflowInfo.DomainID, TargetWorkflowID: childWorkflowInfo.StartedWorkflowID, InitiatedID: childWorkflowInfo.InitiatedID, TaskList: "task-list", }).Times(1) }, }, { name: "Success case - targetDomainID is empty - the expectations is that this should default to the existing parent workflow's domain", domainName: "", setupMock: func() { childWorkflowInfo := getChildWorkflowInfo() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }) s.mockMutableState.EXPECT().GetDomainEntry().Return(parentDomain).Times(1) s.mockMutableState.EXPECT().GetChildExecutionInfo(eventID).Return(childWorkflowInfo, true).Times(1) s.mockMutableState.EXPECT().AddTransferTasks(&persistence.StartChildExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ Version: childWorkflowInfo.Version, }, TargetDomainID: constants.TestDomainID, TargetWorkflowID: childWorkflowInfo.StartedWorkflowID, InitiatedID: childWorkflowInfo.InitiatedID, TaskList: "task-list", }).Times(1) }, }, { name: "targetDomainID different to child's - this is invalid and should be an error", domainName: "child-domain-B", setupMock: func() { childWorkflowInfo := &persistence.ChildExecutionInfo{ Version: constants.TestVersion, InitiatedID: 123, StartedWorkflowID: constants.TestWorkflowID, DomainID: "child-domain-B", } cacheEntry := cache.NewGlobalDomainCacheEntryForTest(nil, nil, nil, 0) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cacheEntry, nil).Times(1) s.mockMutableState.EXPECT().GetChildExecutionInfo(eventID).Return(childWorkflowInfo, true).Times(1) s.mockMutableState.EXPECT().GetDomainEntry().Return(parentDomain).Times(2) }, err: &types.BadRequestError{ Message: "The child workflow is trying to use domain child-domain-B but it's running in domain deadbeef-0123-4567-890a-bcdef0123456. Cross-cluster and cross domain child workflows are not supported for global domains", }, }, { name: "Error case - GetChildExecutionInfo error", setupMock: func() { s.mockMutableState.EXPECT().GetChildExecutionInfo(eventID).Return(nil, false).Times(1) }, err: &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending child workflow: %v", eventID), }, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMock() event := &types.HistoryEvent{ StartChildWorkflowExecutionInitiatedEventAttributes: &types.StartChildWorkflowExecutionInitiatedEventAttributes{ Domain: tc.domainName, }, ID: eventID, } err := s.taskGenerator.GenerateChildWorkflowTasks(event) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateRequestCancelExternalTasks() { event := &types.HistoryEvent{ RequestCancelExternalWorkflowExecutionInitiatedEventAttributes: &types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, }, ID: 123, Version: constants.TestVersion, } testCases := []struct { name string setupMock func() err error }{ { name: "Error case - GetRequestCancelInfo error", setupMock: func() { s.mockMutableState.EXPECT().GetRequestCancelInfo(event.ID).Return(nil, false).Times(1) }, err: &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending request cancel external workflow: %v", event.ID), }, }, { name: "Error case - getTargetDomainID error", setupMock: func() { s.mockMutableState.EXPECT().GetRequestCancelInfo(event.ID).Return(&persistence.RequestCancelInfo{}, true).Times(1) s.mockDomainCache.EXPECT().GetDomainID(event.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes.GetDomain()). Return("", errors.New("get-target-domain-id-error")).Times(1) }, err: errors.New("get-target-domain-id-error"), }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMock() err := s.taskGenerator.GenerateRequestCancelExternalTasks(event) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateSignalExternalTasks() { event := &types.HistoryEvent{ SignalExternalWorkflowExecutionInitiatedEventAttributes: &types.SignalExternalWorkflowExecutionInitiatedEventAttributes{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, }, ID: 123, Version: constants.TestVersion, } testCases := []struct { name string setupMock func() err error }{ { name: "Success case", setupMock: func() { targetDomainID := "target-domain-id" s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", TaskList: "task-list", }) s.mockMutableState.EXPECT().GetSignalInfo(event.ID).Return(nil, true).Times(1) s.mockDomainCache.EXPECT().GetDomainID(event.SignalExternalWorkflowExecutionInitiatedEventAttributes.GetDomain()). Return(targetDomainID, nil).Times(1) s.mockMutableState.EXPECT().AddTransferTasks(&persistence.SignalExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "rid", }, TaskData: persistence.TaskData{ Version: event.Version, }, TargetDomainID: targetDomainID, TargetWorkflowID: event.SignalExternalWorkflowExecutionInitiatedEventAttributes.WorkflowExecution.GetWorkflowID(), TargetRunID: event.SignalExternalWorkflowExecutionInitiatedEventAttributes.WorkflowExecution.GetRunID(), InitiatedID: event.ID, TaskList: "task-list", }).Times(1) }, }, { name: "Error case - GetSignalInfo error", setupMock: func() { s.mockMutableState.EXPECT().GetSignalInfo(event.ID).Return(nil, false).Times(1) }, err: &types.InternalServiceError{ Message: fmt.Sprintf("it could be a bug, cannot get pending signal external workflow: %v", event.ID), }, }, { name: "Error case - getTargetDomainID error", setupMock: func() { s.mockMutableState.EXPECT().GetSignalInfo(event.ID).Return(&persistence.SignalInfo{}, true).Times(1) s.mockDomainCache.EXPECT().GetDomainID(event.SignalExternalWorkflowExecutionInitiatedEventAttributes.GetDomain()). Return("", errors.New("get-target-domain-id-error")).Times(1) }, err: errors.New("get-target-domain-id-error"), }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMock() err := s.taskGenerator.GenerateSignalExternalTasks(event) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *mutableStateTaskGeneratorSuite) TestGenerateWorkflowSearchAttrTasks() { version := int64(123) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id", TaskList: "task-list", }).Times(1) s.mockMutableState.EXPECT().GetCurrentVersion().Return(version).Times(1) s.mockMutableState.EXPECT().AddTransferTasks(&persistence.UpsertWorkflowSearchAttributesTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id", }, TaskData: persistence.TaskData{ Version: version, }, TaskList: "task-list", }).Times(1) err := s.taskGenerator.GenerateWorkflowSearchAttrTasks() s.NoError(err) } func (s *mutableStateTaskGeneratorSuite) TestGenerateWorkflowResetTasks() { version := int64(123) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id", TaskList: "task-list", }).Times(1) s.mockMutableState.EXPECT().GetCurrentVersion().Return(version).Times(1) s.mockMutableState.EXPECT().AddTransferTasks(&persistence.ResetWorkflowTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id", }, TaskData: persistence.TaskData{ Version: version, }, TaskList: "task-list", }).Times(1) err := s.taskGenerator.GenerateWorkflowResetTasks() s.NoError(err) } func (s *mutableStateTaskGeneratorSuite) TestGenerateActivityTimerTasks() { s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(nil).Times(1) err := s.taskGenerator.GenerateActivityTimerTasks() s.NoError(err) } func (s *mutableStateTaskGeneratorSuite) TestGenerateUserTimerTasks() { s.mockMutableState.EXPECT().GetPendingTimerInfos().Return(nil).Times(1) err := s.taskGenerator.GenerateUserTimerTasks() s.NoError(err) } func (s *mutableStateTaskGeneratorSuite) TestGetTargetDomainID() { testCases := []struct { name string setupMock func(targetDomainName string, domainID string) targetDomainName string domainID string err error }{ { name: "Success case - empty targetDomainName", setupMock: func(targetDomainName string, domainID string) { s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: domainID}).Times(1) }, domainID: constants.TestDomainID, targetDomainName: "", }, { name: "Success case - targetDomainName is not empty", setupMock: func(targetDomainName string, domainID string) { s.mockDomainCache.EXPECT().GetDomainID(targetDomainName).Return(domainID, nil).Times(1) }, domainID: constants.TestDomainID, targetDomainName: constants.TestDomainName, }, { name: "Error case - GetDomainID error", setupMock: func(targetDomainName string, domainID string) { s.mockDomainCache.EXPECT().GetDomainID(targetDomainName).Return("", errors.New("get-domain-id-error")).Times(1) }, targetDomainName: constants.TestDomainName, err: errors.New("get-domain-id-error"), }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMock(tc.targetDomainName, tc.domainID) targetDomainID, err := s.taskGenerator.getTargetDomainID(tc.targetDomainName) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) s.Equal(tc.domainID, targetDomainID) } }) } } func getNextBackoffRange(duration time.Duration) (time.Duration, time.Duration) { rangeMin := time.Duration((1 - defaultJitterCoefficient) * float64(duration)) return rangeMin, duration } func TestValidationOfChildWorkflowParameters(t *testing.T) { g1 := cache.NewGlobalDomainCacheEntryForTest(&persistence.DomainInfo{ID: "g1", Name: "g1"}, nil, nil, 0) g2 := cache.NewGlobalDomainCacheEntryForTest(&persistence.DomainInfo{ID: "g2", Name: "g2"}, nil, nil, 0) l1 := cache.NewLocalDomainCacheEntryForTest(&persistence.DomainInfo{ID: "l1", Name: "l1"}, nil, "") l2 := cache.NewLocalDomainCacheEntryForTest(&persistence.DomainInfo{ID: "l2", Name: "l2"}, nil, "") tests := map[string]struct { thisDomain *cache.DomainCacheEntry childWorkflow *cache.DomainCacheEntry setupCache func(mockCache *cache.MockDomainCache) expectedError error }{ "Normal case: a child workflow running from the same domain as the parent shouldn't see any errors": { thisDomain: g1, childWorkflow: g1, setupCache: func(mockCache *cache.MockDomainCache) {}, expectedError: nil, }, "local domains, cross-domain call": { thisDomain: l1, childWorkflow: l2, setupCache: func(cache *cache.MockDomainCache) { cache.EXPECT().GetDomainByID(l2.GetInfo().ID).Return(l2, nil).Times(1) }, expectedError: nil, }, "Global domains cross domain call. This is not permitted": { thisDomain: g1, childWorkflow: g2, setupCache: func(cache *cache.MockDomainCache) { cache.EXPECT().GetDomainByID(g2.GetInfo().ID).Return(g2, nil).Times(1) }, expectedError: &types.BadRequestError{Message: "The child workflow is trying to use domain g2 but it's running in domain g1. Cross-cluster and cross domain child workflows are not supported for global domains"}, }, "Global domains cross domain call 2. This is not permitted": { thisDomain: l1, childWorkflow: g2, setupCache: func(cache *cache.MockDomainCache) { cache.EXPECT().GetDomainByID(g2.GetInfo().ID).Return(g2, nil).Times(1) }, expectedError: &types.BadRequestError{Message: "The child workflow is trying to use domain g2 but it's running in domain l1. Cross-cluster and cross domain child workflows are not supported for global domains"}, }, "Global domains cross domain call 3. This is not permitted": { thisDomain: g1, childWorkflow: l2, setupCache: func(cache *cache.MockDomainCache) { cache.EXPECT().GetDomainByID(l2.GetInfo().ID).Return(g2, nil).Times(1) }, expectedError: &types.BadRequestError{Message: "The child workflow is trying to use domain l2 but it's running in domain g1. Cross-cluster and cross domain child workflows are not supported for global domains"}, }, } for name, td := range tests { t.Run(name, func(t *testing.T) { msb := mutableStateBuilder{ domainEntry: td.thisDomain, } ctrl := gomock.NewController(t) domainCache := cache.NewMockDomainCache(ctrl) td.setupCache(domainCache) mstb := mutableStateTaskGeneratorImpl{ mutableState: &msb, domainCache: domainCache, } err := mstb.validateChildWorkflowParameters(td.thisDomain.GetInfo().ID, td.childWorkflow.GetInfo().ID) assert.Equal(t, td.expectedError, err) }) } } func GenerateWorkflowCloseTasksTestCases(retention time.Duration, closeEvent *types.HistoryEvent, now time.Time) []struct { setupFn func(mockMutableState *MockMutableState) generatedTasks []persistence.Task } { version := int64(123) return []struct { setupFn func(mockMutableState *MockMutableState) generatedTasks []persistence.Task }{ { // no parent, no children setupFn: func(mockMutableState *MockMutableState) { mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, TaskList: "task-list", }).AnyTimes() mockMutableState.EXPECT().HasParentExecution().Return(false).AnyTimes() mockMutableState.EXPECT().GetPendingChildExecutionInfos().Return(nil).AnyTimes() }, generatedTasks: []persistence.Task{ &persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, TaskData: persistence.TaskData{ VisibilityTimestamp: now, Version: version, }, TaskList: "task-list", }, &persistence.DeleteHistoryEventTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Unix(0, closeEvent.GetTimestamp()).Add(retention), Version: version, }, TaskList: "task-list", }, }, }, { // parent and children all active in current cluster setupFn: func(mockMutableState *MockMutableState) { mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ParentDomainID: constants.TestDomainID, ParentWorkflowID: "parent workflowID", ParentRunID: "parent runID", InitiatedID: 101, CloseStatus: persistence.WorkflowCloseStatusCompleted, TaskList: "task-list", }).AnyTimes() mockMutableState.EXPECT().HasParentExecution().Return(true).AnyTimes() mockMutableState.EXPECT().GetPendingChildExecutionInfos().Return(map[int64]*persistence.ChildExecutionInfo{ 102: {DomainID: constants.TestDomainID, ParentClosePolicy: types.ParentClosePolicyTerminate}, 103: {DomainID: constants.TestDomainID, ParentClosePolicy: types.ParentClosePolicyAbandon}, }).AnyTimes() }, generatedTasks: []persistence.Task{ &persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, TaskData: persistence.TaskData{ VisibilityTimestamp: now, Version: version, }, TaskList: "task-list", }, &persistence.DeleteHistoryEventTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Unix(0, closeEvent.GetTimestamp()).Add(retention), Version: version, }, TaskList: "task-list", }, }, }, { // no parent, no children setupFn: func(mockMutableState *MockMutableState) { mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, TaskList: "task-list", }).AnyTimes() mockMutableState.EXPECT().HasParentExecution().Return(false).AnyTimes() mockMutableState.EXPECT().GetPendingChildExecutionInfos().Return(nil).AnyTimes() }, generatedTasks: []persistence.Task{ &persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, TaskData: persistence.TaskData{ VisibilityTimestamp: now, Version: version, }, TaskList: "task-list", }, &persistence.DeleteHistoryEventTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Unix(0, closeEvent.GetTimestamp()).Add(retention), Version: version, }, TaskList: "task-list", }, }, }, } } ================================================ FILE: service/history/execution/mutable_state_task_refresher.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination mutable_state_task_refresher_mock.go -self_package github.com/uber/cadence/service/history/execution package execution import ( "context" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/events" ) var emptyTasks = []persistence.Task{} type ( // MutableStateTaskRefresher refreshes workflow transfer and timer tasks MutableStateTaskRefresher interface { RefreshTasks(ctx context.Context, startTime time.Time, mutableState MutableState) error } mutableStateTaskRefresherImpl struct { config *config.Config clusterMetadata cluster.Metadata domainCache cache.DomainCache eventsCache events.Cache shardID int logger log.Logger newMutableStateTaskGeneratorFn func(log.Logger, cluster.Metadata, cache.DomainCache, MutableState) MutableStateTaskGenerator refreshTasksForWorkflowStartFn func(context.Context, time.Time, MutableState, MutableStateTaskGenerator) error refreshTasksForWorkflowCloseFn func(context.Context, MutableState, MutableStateTaskGenerator, int) error refreshTasksForRecordWorkflowStartedFn func(context.Context, MutableState, MutableStateTaskGenerator) error refreshTasksForDecisionFn func(context.Context, MutableState, MutableStateTaskGenerator) error refreshTasksForActivityFn func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache, func(MutableState) TimerSequence) error refreshTasksForTimerFn func(context.Context, MutableState, MutableStateTaskGenerator, func(MutableState) TimerSequence) error refreshTasksForChildWorkflowFn func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache) error refreshTasksForRequestCancelExternalWorkflowFn func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache) error refreshTasksForSignalExternalWorkflowFn func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache) error refreshTasksForWorkflowSearchAttrFn func(context.Context, MutableState, MutableStateTaskGenerator) error } ) // NewMutableStateTaskRefresher creates a new task refresher for mutable state func NewMutableStateTaskRefresher( config *config.Config, clusterMetadata cluster.Metadata, domainCache cache.DomainCache, eventsCache events.Cache, shardID int, logger log.Logger, ) MutableStateTaskRefresher { return &mutableStateTaskRefresherImpl{ config: config, clusterMetadata: clusterMetadata, domainCache: domainCache, eventsCache: eventsCache, shardID: shardID, logger: logger, newMutableStateTaskGeneratorFn: NewMutableStateTaskGenerator, refreshTasksForWorkflowStartFn: refreshTasksForWorkflowStart, refreshTasksForWorkflowCloseFn: refreshTasksForWorkflowClose, refreshTasksForRecordWorkflowStartedFn: refreshTasksForRecordWorkflowStarted, refreshTasksForDecisionFn: refreshTasksForDecision, refreshTasksForActivityFn: refreshTasksForActivity, refreshTasksForTimerFn: refreshTasksForTimer, refreshTasksForChildWorkflowFn: refreshTasksForChildWorkflow, refreshTasksForRequestCancelExternalWorkflowFn: refreshTasksForRequestCancelExternalWorkflow, refreshTasksForSignalExternalWorkflowFn: refreshTasksForSignalExternalWorkflow, refreshTasksForWorkflowSearchAttrFn: refreshTasksForWorkflowSearchAttr, } } func (r *mutableStateTaskRefresherImpl) RefreshTasks( ctx context.Context, startTime time.Time, mutableState MutableState, ) error { taskGenerator := r.newMutableStateTaskGeneratorFn( r.logger, r.clusterMetadata, r.domainCache, mutableState, ) if err := r.refreshTasksForWorkflowStartFn( ctx, startTime, mutableState, taskGenerator, ); err != nil { return err } if err := r.refreshTasksForWorkflowCloseFn( ctx, mutableState, taskGenerator, r.config.WorkflowDeletionJitterRange(mutableState.GetDomainEntry().GetInfo().Name), ); err != nil { return err } if err := r.refreshTasksForRecordWorkflowStartedFn( ctx, mutableState, taskGenerator, ); err != nil { return err } if err := r.refreshTasksForDecisionFn( ctx, mutableState, taskGenerator, ); err != nil { return err } if err := r.refreshTasksForActivityFn( ctx, mutableState, taskGenerator, r.shardID, r.eventsCache, NewTimerSequence, ); err != nil { return err } if err := r.refreshTasksForTimerFn( ctx, mutableState, taskGenerator, NewTimerSequence, ); err != nil { return err } if err := r.refreshTasksForChildWorkflowFn( ctx, mutableState, taskGenerator, r.shardID, r.eventsCache, ); err != nil { return err } if err := r.refreshTasksForRequestCancelExternalWorkflowFn( ctx, mutableState, taskGenerator, r.shardID, r.eventsCache, ); err != nil { return err } if err := r.refreshTasksForSignalExternalWorkflowFn( ctx, mutableState, taskGenerator, r.shardID, r.eventsCache, ); err != nil { return err } if common.IsAdvancedVisibilityWritingEnabled(r.config.WriteVisibilityStoreName(), r.config.IsAdvancedVisConfigExist) { if err := r.refreshTasksForWorkflowSearchAttrFn( ctx, mutableState, taskGenerator, ); err != nil { return err } } return nil } func refreshTasksForWorkflowStart( ctx context.Context, startTime time.Time, mutableState MutableState, taskGenerator MutableStateTaskGenerator, ) error { startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return err } if err := taskGenerator.GenerateWorkflowStartTasks( startTime, startEvent, ); err != nil { return err } startAttr := startEvent.WorkflowExecutionStartedEventAttributes if !mutableState.HasProcessedOrPendingDecision() && startAttr.GetFirstDecisionTaskBackoffSeconds() > 0 { if err := taskGenerator.GenerateDelayedDecisionTasks( startEvent, ); err != nil { return err } } return nil } func refreshTasksForWorkflowClose( ctx context.Context, mutableState MutableState, taskGenerator MutableStateTaskGenerator, workflowDeletionTaskJitterRange int, ) error { executionInfo := mutableState.GetExecutionInfo() if executionInfo.CloseStatus != persistence.WorkflowCloseStatusNone { closeEvent, err := mutableState.GetCompletionEvent(ctx) if err != nil { return err } return taskGenerator.GenerateWorkflowCloseTasks( closeEvent, workflowDeletionTaskJitterRange, ) } return nil } func refreshTasksForRecordWorkflowStarted( ctx context.Context, mutableState MutableState, taskGenerator MutableStateTaskGenerator, ) error { executionInfo := mutableState.GetExecutionInfo() if executionInfo.CloseStatus == persistence.WorkflowCloseStatusNone { startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return err } return taskGenerator.GenerateRecordWorkflowStartedTasks( startEvent, ) } return nil } func refreshTasksForDecision( ctx context.Context, mutableState MutableState, taskGenerator MutableStateTaskGenerator, ) error { if !mutableState.HasPendingDecision() { // no decision task at all return nil } decision, ok := mutableState.GetPendingDecision() if !ok { return &types.InternalServiceError{Message: "it could be a bug, cannot get pending decision"} } // decision already started if decision.StartedID != constants.EmptyEventID { return taskGenerator.GenerateDecisionStartTasks( decision.ScheduleID, ) } // decision only scheduled return taskGenerator.GenerateDecisionScheduleTasks( decision.ScheduleID, ) } func refreshTasksForActivity( ctx context.Context, mutableState MutableState, taskGenerator MutableStateTaskGenerator, shardID int, eventsCache events.Cache, newTimerSequenceFn func(MutableState) TimerSequence, ) error { currentBranchToken, err := mutableState.GetCurrentBranchToken() if err != nil { return err } executionInfo := mutableState.GetExecutionInfo() pendingActivityInfos := mutableState.GetPendingActivityInfos() for _, activityInfo := range pendingActivityInfos { // clear all activity timer task mask for later activity timer task re-generation activityInfo.TimerTaskStatus = TimerTaskStatusNone // need to update activity timer task mask for which task is generated if err := mutableState.UpdateActivity( activityInfo, ); err != nil { return err } if activityInfo.StartedID != constants.EmptyEventID { continue } scheduleEvent, err := eventsCache.GetEvent( ctx, shardID, executionInfo.DomainID, executionInfo.WorkflowID, executionInfo.RunID, activityInfo.ScheduledEventBatchID, activityInfo.ScheduleID, currentBranchToken, ) if err != nil { return err } if err := taskGenerator.GenerateActivityTransferTasks( scheduleEvent, ); err != nil { return err } } if _, err := newTimerSequenceFn( mutableState, ).CreateNextActivityTimer(); err != nil { return err } return nil } func refreshTasksForTimer( ctx context.Context, mutableState MutableState, taskGenerator MutableStateTaskGenerator, newTimerSequenceFn func(MutableState) TimerSequence, ) error { pendingTimerInfos := mutableState.GetPendingTimerInfos() for _, timerInfo := range pendingTimerInfos { // clear all timer task mask for later timer task re-generation timerInfo.TaskStatus = TimerTaskStatusNone // need to update user timer task mask for which task is generated if err := mutableState.UpdateUserTimer( timerInfo, ); err != nil { return err } } if _, err := newTimerSequenceFn(mutableState).CreateNextUserTimer(); err != nil { return err } return nil } func refreshTasksForChildWorkflow( ctx context.Context, mutableState MutableState, taskGenerator MutableStateTaskGenerator, shardID int, eventsCache events.Cache, ) error { currentBranchToken, err := mutableState.GetCurrentBranchToken() if err != nil { return err } executionInfo := mutableState.GetExecutionInfo() pendingChildWorkflowInfos := mutableState.GetPendingChildExecutionInfos() for _, childWorkflowInfo := range pendingChildWorkflowInfos { if childWorkflowInfo.StartedID != constants.EmptyEventID { continue } scheduleEvent, err := eventsCache.GetEvent( ctx, shardID, executionInfo.DomainID, executionInfo.WorkflowID, executionInfo.RunID, childWorkflowInfo.InitiatedEventBatchID, childWorkflowInfo.InitiatedID, currentBranchToken, ) if err != nil { return err } if err := taskGenerator.GenerateChildWorkflowTasks( scheduleEvent, ); err != nil { return err } } return nil } func refreshTasksForRequestCancelExternalWorkflow( ctx context.Context, mutableState MutableState, taskGenerator MutableStateTaskGenerator, shardID int, eventsCache events.Cache, ) error { currentBranchToken, err := mutableState.GetCurrentBranchToken() if err != nil { return err } executionInfo := mutableState.GetExecutionInfo() pendingRequestCancelInfos := mutableState.GetPendingRequestCancelExternalInfos() for _, requestCancelInfo := range pendingRequestCancelInfos { initiateEvent, err := eventsCache.GetEvent( ctx, shardID, executionInfo.DomainID, executionInfo.WorkflowID, executionInfo.RunID, requestCancelInfo.InitiatedEventBatchID, requestCancelInfo.InitiatedID, currentBranchToken, ) if err != nil { return err } if err := taskGenerator.GenerateRequestCancelExternalTasks( initiateEvent, ); err != nil { return err } } return nil } func refreshTasksForSignalExternalWorkflow( ctx context.Context, mutableState MutableState, taskGenerator MutableStateTaskGenerator, shardID int, eventsCache events.Cache, ) error { currentBranchToken, err := mutableState.GetCurrentBranchToken() if err != nil { return err } executionInfo := mutableState.GetExecutionInfo() pendingSignalInfos := mutableState.GetPendingSignalExternalInfos() for _, signalInfo := range pendingSignalInfos { initiateEvent, err := eventsCache.GetEvent( ctx, shardID, executionInfo.DomainID, executionInfo.WorkflowID, executionInfo.RunID, signalInfo.InitiatedEventBatchID, signalInfo.InitiatedID, currentBranchToken, ) if err != nil { return err } if err := taskGenerator.GenerateSignalExternalTasks( initiateEvent, ); err != nil { return err } } return nil } func refreshTasksForWorkflowSearchAttr( ctx context.Context, mutableState MutableState, taskGenerator MutableStateTaskGenerator, ) error { return taskGenerator.GenerateWorkflowSearchAttrTasks() } ================================================ FILE: service/history/execution/mutable_state_task_refresher_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: mutable_state_task_refresher.go // // Generated by this command: // // mockgen -package execution -source mutable_state_task_refresher.go -destination mutable_state_task_refresher_mock.go -self_package github.com/uber/cadence/service/history/execution // // Package execution is a generated GoMock package. package execution import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" ) // MockMutableStateTaskRefresher is a mock of MutableStateTaskRefresher interface. type MockMutableStateTaskRefresher struct { ctrl *gomock.Controller recorder *MockMutableStateTaskRefresherMockRecorder isgomock struct{} } // MockMutableStateTaskRefresherMockRecorder is the mock recorder for MockMutableStateTaskRefresher. type MockMutableStateTaskRefresherMockRecorder struct { mock *MockMutableStateTaskRefresher } // NewMockMutableStateTaskRefresher creates a new mock instance. func NewMockMutableStateTaskRefresher(ctrl *gomock.Controller) *MockMutableStateTaskRefresher { mock := &MockMutableStateTaskRefresher{ctrl: ctrl} mock.recorder = &MockMutableStateTaskRefresherMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMutableStateTaskRefresher) EXPECT() *MockMutableStateTaskRefresherMockRecorder { return m.recorder } // RefreshTasks mocks base method. func (m *MockMutableStateTaskRefresher) RefreshTasks(ctx context.Context, startTime time.Time, mutableState MutableState) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RefreshTasks", ctx, startTime, mutableState) ret0, _ := ret[0].(error) return ret0 } // RefreshTasks indicates an expected call of RefreshTasks. func (mr *MockMutableStateTaskRefresherMockRecorder) RefreshTasks(ctx, startTime, mutableState any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTasks", reflect.TypeOf((*MockMutableStateTaskRefresher)(nil).RefreshTasks), ctx, startTime, mutableState) } ================================================ FILE: service/history/execution/mutable_state_task_refresher_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "context" "errors" "testing" "time" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/events" ) func TestRefreshTasksForWorkflowStart(t *testing.T) { testCases := []struct { name string mockSetup func(*MockMutableState, *MockMutableStateTaskGenerator) wantErr bool }{ { name: "failed to get start event", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().GetStartEvent(gomock.Any()).Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to generate start tasks", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().GetStartEvent(gomock.Any()).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateWorkflowStartTasks(gomock.Any(), &types.HistoryEvent{ID: 1}).Return(errors.New("some error")) }, wantErr: true, }, { name: "failed to generate delayed decision tasks", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { startEvent := &types.HistoryEvent{ ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Ptr[int32](10), }, } ms.EXPECT().GetStartEvent(gomock.Any()).Return(startEvent, nil) mtg.EXPECT().GenerateWorkflowStartTasks(gomock.Any(), gomock.Any()).Return(nil) ms.EXPECT().HasProcessedOrPendingDecision().Return(false) mtg.EXPECT().GenerateDelayedDecisionTasks(startEvent).Return(errors.New("some error")) }, wantErr: true, }, { name: "success", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { startEvent := &types.HistoryEvent{ ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ FirstDecisionTaskBackoffSeconds: common.Ptr[int32](10), }, } ms.EXPECT().GetStartEvent(gomock.Any()).Return(startEvent, nil) mtg.EXPECT().GenerateWorkflowStartTasks(gomock.Any(), gomock.Any()).Return(nil) ms.EXPECT().HasProcessedOrPendingDecision().Return(false) mtg.EXPECT().GenerateDelayedDecisionTasks(startEvent).Return(nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) ms := NewMockMutableState(ctrl) mtg := NewMockMutableStateTaskGenerator(ctrl) tc.mockSetup(ms, mtg) err := refreshTasksForWorkflowStart(context.Background(), time.Now(), ms, mtg) if (err != nil) != tc.wantErr { t.Errorf("refreshTasksForWorkflowStart err = %v, wantErr %v", err, tc.wantErr) } }) } } func TestRefreshTasksForWorkflowClose(t *testing.T) { testCases := []struct { name string mockSetup func(*MockMutableState, *MockMutableStateTaskGenerator) wantErr bool }{ { name: "failed to get completion event", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{CloseStatus: persistence.WorkflowCloseStatusCompleted}) ms.EXPECT().GetCompletionEvent(gomock.Any()).Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to generate close tasks", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{CloseStatus: persistence.WorkflowCloseStatusCompleted}) ms.EXPECT().GetCompletionEvent(gomock.Any()).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateWorkflowCloseTasks(&types.HistoryEvent{ID: 1}, gomock.Any()).Return(errors.New("some error")) }, wantErr: true, }, { name: "success - open workflow", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{CloseStatus: persistence.WorkflowCloseStatusNone}) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) ms := NewMockMutableState(ctrl) mtg := NewMockMutableStateTaskGenerator(ctrl) tc.mockSetup(ms, mtg) err := refreshTasksForWorkflowClose(context.Background(), ms, mtg, 100) if (err != nil) != tc.wantErr { t.Errorf("refreshTasksForWorkflowClose err = %v, wantErr %v", err, tc.wantErr) } }) } } func TestRefreshTasksForRecordWorkflowStarted(t *testing.T) { testCases := []struct { name string mockSetup func(*MockMutableState, *MockMutableStateTaskGenerator) wantErr bool }{ { name: "failed to get start event", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{CloseStatus: persistence.WorkflowCloseStatusNone}) ms.EXPECT().GetStartEvent(gomock.Any()).Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to generate record started tasks", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().GetStartEvent(gomock.Any()).Return(&types.HistoryEvent{ID: 1}, nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{CloseStatus: persistence.WorkflowCloseStatusNone}) mtg.EXPECT().GenerateRecordWorkflowStartedTasks(&types.HistoryEvent{ID: 1}).Return(errors.New("some error")) }, wantErr: true, }, { name: "success - closed workflow", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{CloseStatus: persistence.WorkflowCloseStatusCompleted}) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) ms := NewMockMutableState(ctrl) mtg := NewMockMutableStateTaskGenerator(ctrl) tc.mockSetup(ms, mtg) err := refreshTasksForRecordWorkflowStarted(context.Background(), ms, mtg) if (err != nil) != tc.wantErr { t.Errorf("refreshTasksForRecordWorkflowStarted err = %v, wantErr %v", err, tc.wantErr) } }) } } func TestRefreshTasksForDecision(t *testing.T) { testCases := []struct { name string mockSetup func(*MockMutableState, *MockMutableStateTaskGenerator) wantErr bool }{ { name: "success - no pending decision", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().HasPendingDecision().Return(false) }, wantErr: false, }, { name: "bug - cannot get pending decision", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().HasPendingDecision().Return(true) ms.EXPECT().GetPendingDecision().Return(nil, false) }, wantErr: true, }, { name: "success - generate decision started task", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().HasPendingDecision().Return(true) ms.EXPECT().GetPendingDecision().Return(&DecisionInfo{ScheduleID: 2, StartedID: 3}, true) mtg.EXPECT().GenerateDecisionStartTasks(int64(2)).Return(nil) }, wantErr: false, }, { name: "success - generate decision started task", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator) { ms.EXPECT().HasPendingDecision().Return(true) ms.EXPECT().GetPendingDecision().Return(&DecisionInfo{ScheduleID: 2, StartedID: constants.EmptyEventID}, true) mtg.EXPECT().GenerateDecisionScheduleTasks(int64(2)).Return(nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) ms := NewMockMutableState(ctrl) mtg := NewMockMutableStateTaskGenerator(ctrl) tc.mockSetup(ms, mtg) err := refreshTasksForDecision(context.Background(), ms, mtg) if (err != nil) != tc.wantErr { t.Errorf("refreshTasksForDecision err = %v, wantErr %v", err, tc.wantErr) } }) } } func TestRefreshTasksForActivity(t *testing.T) { testCases := []struct { name string mockSetup func(*MockMutableState, *MockMutableStateTaskGenerator, *events.MockCache, *MockTimerSequence) wantErr bool }{ { name: "failed to get current branch token", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache, mt *MockTimerSequence) { ms.EXPECT().GetCurrentBranchToken().Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to update activity", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache, mt *MockTimerSequence) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}) ms.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{1: {Version: 1, TimerTaskStatus: TimerTaskStatusCreated}}) ms.EXPECT().UpdateActivity(&persistence.ActivityInfo{Version: 1, TimerTaskStatus: TimerTaskStatusNone}).Return(errors.New("some error")) }, wantErr: true, }, { name: "failed to get event", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache, mt *MockTimerSequence) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{1: {Version: 1, TimerTaskStatus: TimerTaskStatusCreated, ScheduledEventBatchID: 11, ScheduleID: 12, StartedID: constants.EmptyEventID}}) ms.EXPECT().UpdateActivity(&persistence.ActivityInfo{Version: 1, TimerTaskStatus: TimerTaskStatusNone, ScheduledEventBatchID: 11, ScheduleID: 12, StartedID: constants.EmptyEventID}).Return(nil) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(11), int64(12), []byte("token")).Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to generate activity tasks", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache, mt *MockTimerSequence) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{1: {Version: 1, TimerTaskStatus: TimerTaskStatusCreated, ScheduledEventBatchID: 11, ScheduleID: 12, StartedID: constants.EmptyEventID}}) ms.EXPECT().UpdateActivity(&persistence.ActivityInfo{Version: 1, TimerTaskStatus: TimerTaskStatusNone, ScheduledEventBatchID: 11, ScheduleID: 12, StartedID: constants.EmptyEventID}).Return(nil) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(11), int64(12), []byte("token")).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateActivityTransferTasks(&types.HistoryEvent{ID: 1}).Return(errors.New("some error")) }, wantErr: true, }, { name: "failed to create activity timer", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache, mt *MockTimerSequence) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{1: {Version: 1, TimerTaskStatus: TimerTaskStatusCreated, ScheduledEventBatchID: 11, ScheduleID: 12, StartedID: constants.EmptyEventID}}) ms.EXPECT().UpdateActivity(&persistence.ActivityInfo{Version: 1, TimerTaskStatus: TimerTaskStatusNone, ScheduledEventBatchID: 11, ScheduleID: 12, StartedID: constants.EmptyEventID}).Return(nil) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(11), int64(12), []byte("token")).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateActivityTransferTasks(&types.HistoryEvent{ID: 1}).Return(nil) mt.EXPECT().CreateNextActivityTimer().Return(false, errors.New("some error")) }, wantErr: true, }, { name: "success", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache, mt *MockTimerSequence) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{10: {Version: 0, StartedID: 10}, 1: {Version: 1, TimerTaskStatus: TimerTaskStatusCreated, ScheduledEventBatchID: 11, ScheduleID: 12, StartedID: constants.EmptyEventID}}) ms.EXPECT().UpdateActivity(&persistence.ActivityInfo{Version: 0, StartedID: 10, TimerTaskStatus: TimerTaskStatusNone}).Return(nil) ms.EXPECT().UpdateActivity(&persistence.ActivityInfo{Version: 1, TimerTaskStatus: TimerTaskStatusNone, ScheduledEventBatchID: 11, ScheduleID: 12, StartedID: constants.EmptyEventID}).Return(nil) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(11), int64(12), []byte("token")).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateActivityTransferTasks(&types.HistoryEvent{ID: 1}).Return(nil) mt.EXPECT().CreateNextActivityTimer().Return(true, nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) ms := NewMockMutableState(ctrl) mtg := NewMockMutableStateTaskGenerator(ctrl) mc := events.NewMockCache(ctrl) mt := NewMockTimerSequence(ctrl) tc.mockSetup(ms, mtg, mc, mt) err := refreshTasksForActivity(context.Background(), ms, mtg, 1, mc, func(MutableState) TimerSequence { return mt }) if (err != nil) != tc.wantErr { t.Errorf("refreshTasksForActivity err = %v, wantErr %v", err, tc.wantErr) } }) } } func TestRefreshTasksForTimer(t *testing.T) { testCases := []struct { name string mockSetup func(*MockMutableState, *MockMutableStateTaskGenerator, *MockTimerSequence) wantErr bool }{ { name: "failed to update user timer", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mt *MockTimerSequence) { ms.EXPECT().GetPendingTimerInfos().Return(map[string]*persistence.TimerInfo{"0": &persistence.TimerInfo{}}) ms.EXPECT().UpdateUserTimer(gomock.Any()).Return(errors.New("some error")) }, wantErr: true, }, { name: "failed to create user timer", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mt *MockTimerSequence) { ms.EXPECT().GetPendingTimerInfos().Return(map[string]*persistence.TimerInfo{"0": &persistence.TimerInfo{}}) ms.EXPECT().UpdateUserTimer(gomock.Any()).Return(nil) mt.EXPECT().CreateNextUserTimer().Return(false, errors.New("some error")) }, wantErr: true, }, { name: "success", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mt *MockTimerSequence) { ms.EXPECT().GetPendingTimerInfos().Return(map[string]*persistence.TimerInfo{"0": &persistence.TimerInfo{}}) ms.EXPECT().UpdateUserTimer(gomock.Any()).Return(nil) mt.EXPECT().CreateNextUserTimer().Return(false, nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) ms := NewMockMutableState(ctrl) mtg := NewMockMutableStateTaskGenerator(ctrl) mt := NewMockTimerSequence(ctrl) tc.mockSetup(ms, mtg, mt) err := refreshTasksForTimer(context.Background(), ms, mtg, func(MutableState) TimerSequence { return mt }) if (err != nil) != tc.wantErr { t.Errorf("refreshTasksForTimer err = %v, wantErr %v", err, tc.wantErr) } }) } } func TestRefreshTasksForChildWorkflow(t *testing.T) { testCases := []struct { name string mockSetup func(*MockMutableState, *MockMutableStateTaskGenerator, *events.MockCache) wantErr bool }{ { name: "failed to get current branch token", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to get event", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingChildExecutionInfos().Return(map[int64]*persistence.ChildExecutionInfo{1: {InitiatedEventBatchID: 1, InitiatedID: 2, StartedID: constants.EmptyEventID, Version: 1}}) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(1), int64(2), []byte("token")).Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to generate child workflow tasks", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingChildExecutionInfos().Return(map[int64]*persistence.ChildExecutionInfo{1: {InitiatedEventBatchID: 1, InitiatedID: 2, StartedID: constants.EmptyEventID, Version: 1}}) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(1), int64(2), []byte("token")).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateChildWorkflowTasks(&types.HistoryEvent{ID: 1}).Return(errors.New("some error")) }, wantErr: true, }, { name: "success", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingChildExecutionInfos().Return(map[int64]*persistence.ChildExecutionInfo{1: {InitiatedEventBatchID: 1, InitiatedID: 2, StartedID: constants.EmptyEventID, Version: 1}, 11: {StartedID: 12}}) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(1), int64(2), []byte("token")).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateChildWorkflowTasks(&types.HistoryEvent{ID: 1}).Return(nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) ms := NewMockMutableState(ctrl) mtg := NewMockMutableStateTaskGenerator(ctrl) mc := events.NewMockCache(ctrl) tc.mockSetup(ms, mtg, mc) err := refreshTasksForChildWorkflow(context.Background(), ms, mtg, 1, mc) if (err != nil) != tc.wantErr { t.Errorf("refreshTasksForChildWorkflow err = %v, wantErr %v", err, tc.wantErr) } }) } } func TestRefreshTasksForRequestCancelExternalWorkflow(t *testing.T) { testCases := []struct { name string mockSetup func(*MockMutableState, *MockMutableStateTaskGenerator, *events.MockCache) wantErr bool }{ { name: "failed to get current branch token", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to get event", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingRequestCancelExternalInfos().Return(map[int64]*persistence.RequestCancelInfo{1: {InitiatedEventBatchID: 1, InitiatedID: 2, Version: 1}}) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(1), int64(2), []byte("token")).Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to generate child workflow tasks", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingRequestCancelExternalInfos().Return(map[int64]*persistence.RequestCancelInfo{1: {InitiatedEventBatchID: 1, InitiatedID: 2, Version: 1}}) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(1), int64(2), []byte("token")).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateRequestCancelExternalTasks(&types.HistoryEvent{ID: 1}).Return(errors.New("some error")) }, wantErr: true, }, { name: "success", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingRequestCancelExternalInfos().Return(map[int64]*persistence.RequestCancelInfo{1: {InitiatedEventBatchID: 1, InitiatedID: 2, Version: 1}}) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(1), int64(2), []byte("token")).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateRequestCancelExternalTasks(&types.HistoryEvent{ID: 1}).Return(nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) ms := NewMockMutableState(ctrl) mtg := NewMockMutableStateTaskGenerator(ctrl) mc := events.NewMockCache(ctrl) tc.mockSetup(ms, mtg, mc) err := refreshTasksForRequestCancelExternalWorkflow(context.Background(), ms, mtg, 1, mc) if (err != nil) != tc.wantErr { t.Errorf("refreshTasksForRequestCancelExternalWorkflow err = %v, wantErr %v", err, tc.wantErr) } }) } } func TestRefreshTasksForSignalExternalWorkflow(t *testing.T) { testCases := []struct { name string mockSetup func(*MockMutableState, *MockMutableStateTaskGenerator, *events.MockCache) wantErr bool }{ { name: "failed to get current branch token", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to get event", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingSignalExternalInfos().Return(map[int64]*persistence.SignalInfo{1: {InitiatedEventBatchID: 1, InitiatedID: 2, Version: 1}}) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(1), int64(2), []byte("token")).Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "failed to generate child workflow tasks", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingSignalExternalInfos().Return(map[int64]*persistence.SignalInfo{1: {InitiatedEventBatchID: 1, InitiatedID: 2, Version: 1}}) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(1), int64(2), []byte("token")).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateSignalExternalTasks(&types.HistoryEvent{ID: 1}).Return(errors.New("some error")) }, wantErr: true, }, { name: "success", mockSetup: func(ms *MockMutableState, mtg *MockMutableStateTaskGenerator, mc *events.MockCache) { ms.EXPECT().GetCurrentBranchToken().Return([]byte("token"), nil) ms.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id"}) ms.EXPECT().GetPendingSignalExternalInfos().Return(map[int64]*persistence.SignalInfo{1: {InitiatedEventBatchID: 1, InitiatedID: 2, Version: 1}}) mc.EXPECT().GetEvent(gomock.Any(), gomock.Any(), "domain-id", "wf-id", "run-id", int64(1), int64(2), []byte("token")).Return(&types.HistoryEvent{ID: 1}, nil) mtg.EXPECT().GenerateSignalExternalTasks(&types.HistoryEvent{ID: 1}).Return(nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) ms := NewMockMutableState(ctrl) mtg := NewMockMutableStateTaskGenerator(ctrl) mc := events.NewMockCache(ctrl) tc.mockSetup(ms, mtg, mc) err := refreshTasksForSignalExternalWorkflow(context.Background(), ms, mtg, 1, mc) if (err != nil) != tc.wantErr { t.Errorf("refreshTasksForSignalExternalWorkflow err = %v, wantErr %v", err, tc.wantErr) } }) } } func TestRefreshTasksForWorkflowSearchAttr(t *testing.T) { testCases := []struct { name string mockSetup func(*MockMutableStateTaskGenerator) wantErr bool }{ { name: "failed to generate workflow search attribute tasks", mockSetup: func(mtg *MockMutableStateTaskGenerator) { mtg.EXPECT().GenerateWorkflowSearchAttrTasks().Return(errors.New("some error")) }, wantErr: true, }, { name: "success", mockSetup: func(mtg *MockMutableStateTaskGenerator) { mtg.EXPECT().GenerateWorkflowSearchAttrTasks().Return(nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mtg := NewMockMutableStateTaskGenerator(ctrl) tc.mockSetup(mtg) err := refreshTasksForWorkflowSearchAttr(context.Background(), nil, mtg) if (err != nil) != tc.wantErr { t.Errorf("refreshTasksForWorkflowSearchAttr err = %v, wantErr %v", err, tc.wantErr) } }) } } func TestRefreshTasks(t *testing.T) { testCases := []struct { name string refreshTasksForWorkflowStartFn func(context.Context, time.Time, MutableState, MutableStateTaskGenerator) error refreshTasksForWorkflowCloseFn func(context.Context, MutableState, MutableStateTaskGenerator, int) error refreshTasksForRecordWorkflowStartedFn func(context.Context, MutableState, MutableStateTaskGenerator) error refreshTasksForDecisionFn func(context.Context, MutableState, MutableStateTaskGenerator) error refreshTasksForActivityFn func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache, func(MutableState) TimerSequence) error refreshTasksForTimerFn func(context.Context, MutableState, MutableStateTaskGenerator, func(MutableState) TimerSequence) error refreshTasksForChildWorkflowFn func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache) error refreshTasksForRequestCancelExternalWorkflowFn func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache) error refreshTasksForSignalExternalWorkflowFn func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache) error refreshTasksForWorkflowSearchAttrFn func(context.Context, MutableState, MutableStateTaskGenerator) error wantErr bool }{ { name: "success", refreshTasksForWorkflowStartFn: func(context.Context, time.Time, MutableState, MutableStateTaskGenerator) error { return nil }, refreshTasksForWorkflowCloseFn: func(context.Context, MutableState, MutableStateTaskGenerator, int) error { return nil }, refreshTasksForRecordWorkflowStartedFn: func(context.Context, MutableState, MutableStateTaskGenerator) error { return nil }, refreshTasksForDecisionFn: func(context.Context, MutableState, MutableStateTaskGenerator) error { return nil }, refreshTasksForActivityFn: func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache, func(MutableState) TimerSequence) error { return nil }, refreshTasksForTimerFn: func(context.Context, MutableState, MutableStateTaskGenerator, func(MutableState) TimerSequence) error { return nil }, refreshTasksForChildWorkflowFn: func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache) error { return nil }, refreshTasksForRequestCancelExternalWorkflowFn: func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache) error { return nil }, refreshTasksForSignalExternalWorkflowFn: func(context.Context, MutableState, MutableStateTaskGenerator, int, events.Cache) error { return nil }, refreshTasksForWorkflowSearchAttrFn: func(context.Context, MutableState, MutableStateTaskGenerator) error { return nil }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) ms := NewMockMutableState(ctrl) mtg := NewMockMutableStateTaskGenerator(ctrl) ms.EXPECT().GetDomainEntry().Return(cache.NewLocalDomainCacheEntryForTest(&persistence.DomainInfo{ID: "domain-id"}, nil, "test")).AnyTimes() refresher := &mutableStateTaskRefresherImpl{ config: &config.Config{ WriteVisibilityStoreName: dynamicproperties.GetStringPropertyFn(constants.VisibilityModeES), WorkflowDeletionJitterRange: dynamicproperties.GetIntPropertyFilteredByDomain(1), IsAdvancedVisConfigExist: true, }, newMutableStateTaskGeneratorFn: func(log.Logger, cluster.Metadata, cache.DomainCache, MutableState) MutableStateTaskGenerator { return mtg }, refreshTasksForWorkflowStartFn: tc.refreshTasksForWorkflowStartFn, refreshTasksForWorkflowCloseFn: tc.refreshTasksForWorkflowCloseFn, refreshTasksForRecordWorkflowStartedFn: tc.refreshTasksForRecordWorkflowStartedFn, refreshTasksForDecisionFn: tc.refreshTasksForDecisionFn, refreshTasksForActivityFn: tc.refreshTasksForActivityFn, refreshTasksForTimerFn: tc.refreshTasksForTimerFn, refreshTasksForChildWorkflowFn: tc.refreshTasksForChildWorkflowFn, refreshTasksForRequestCancelExternalWorkflowFn: tc.refreshTasksForRequestCancelExternalWorkflowFn, refreshTasksForSignalExternalWorkflowFn: tc.refreshTasksForSignalExternalWorkflowFn, refreshTasksForWorkflowSearchAttrFn: tc.refreshTasksForWorkflowSearchAttrFn, } err := refresher.RefreshTasks(context.Background(), time.Now(), ms) if (err != nil) != tc.wantErr { t.Errorf("RefreshTasks err = %v, wantErr %v", err, tc.wantErr) } }) } } ================================================ FILE: service/history/execution/mutable_state_util.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // TransactionPolicy is the policy used for updating workflow execution TransactionPolicy int ) const ( // TransactionPolicyActive updates workflow execution as active TransactionPolicyActive TransactionPolicy = 0 // TransactionPolicyPassive updates workflow execution as passive TransactionPolicyPassive TransactionPolicy = 1 ) // Ptr returns a pointer to the current transaction policy func (policy TransactionPolicy) Ptr() *TransactionPolicy { return &policy } func convertSyncActivityInfos( executionInfo *persistence.WorkflowExecutionInfo, activityInfos map[int64]*persistence.ActivityInfo, inputs map[int64]struct{}, ) []persistence.Task { outputs := make([]persistence.Task, 0, len(inputs)) for item := range inputs { activityInfo, ok := activityInfos[item] if ok { // the visibility timestamp will be set in shard context outputs = append(outputs, &persistence.SyncActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ Version: activityInfo.Version, }, ScheduledID: activityInfo.ScheduleID, }) } } return outputs } func convertWorkflowRequests(inputs map[persistence.WorkflowRequest]struct{}) []*persistence.WorkflowRequest { outputs := make([]*persistence.WorkflowRequest, 0, len(inputs)) for key := range inputs { key := key // TODO: remove this trick once we upgrade go to 1.22 outputs = append(outputs, &key) } return outputs } // FailDecision fails the current decision task func FailDecision( mutableState MutableState, decision *DecisionInfo, decisionFailureCause types.DecisionTaskFailedCause, ) error { if _, err := mutableState.AddDecisionTaskFailedEvent( decision.ScheduleID, decision.StartedID, decisionFailureCause, nil, IdentityHistoryService, "", "", "", "", 0, "", ); err != nil { return err } return mutableState.FlushBufferedEvents() } // ScheduleDecision schedules a new decision task func ScheduleDecision( mutableState MutableState, ) error { if mutableState.HasPendingDecision() { return nil } _, err := mutableState.AddDecisionTaskScheduledEvent(false) if err != nil { return &types.InternalServiceError{Message: "Failed to add decision scheduled event."} } return nil } // FindAutoResetPoint returns the auto reset point func FindAutoResetPoint( timeSource clock.TimeSource, badBinaries *types.BadBinaries, autoResetPoints *types.ResetPoints, ) (string, *types.ResetPointInfo) { if badBinaries == nil || badBinaries.Binaries == nil || autoResetPoints == nil || autoResetPoints.Points == nil { return "", nil } nowNano := timeSource.Now().UnixNano() for _, p := range autoResetPoints.Points { bin, ok := badBinaries.Binaries[p.GetBinaryChecksum()] if ok && p.GetResettable() { if p.GetExpiringTimeNano() > 0 && nowNano > p.GetExpiringTimeNano() { // reset point has expired and we may already deleted the history continue } return bin.GetReason(), p } } return "", nil } // GetChildExecutionDomainName gets domain name for the child workflow // NOTE: DomainName in ChildExecutionInfo is being deprecated, and // we should always use DomainID field instead. // this function exists for backward compatibility reason func GetChildExecutionDomainName( childInfo *persistence.ChildExecutionInfo, domainCache cache.DomainCache, parentDomainEntry *cache.DomainCacheEntry, ) (string, error) { if childInfo.DomainID != "" { return domainCache.GetDomainName(childInfo.DomainID) } if childInfo.DomainNameDEPRECATED != "" { return childInfo.DomainNameDEPRECATED, nil } return parentDomainEntry.GetInfo().Name, nil } // GetChildExecutionDomainID gets domainID for the child workflow // NOTE: DomainName in ChildExecutionInfo is being deprecated, and // we should always use DomainID field instead. // this function exists for backward compatibility reason func GetChildExecutionDomainID( childInfo *persistence.ChildExecutionInfo, domainCache cache.DomainCache, parentDomainEntry *cache.DomainCacheEntry, ) (string, error) { if childInfo.DomainID != "" { return childInfo.DomainID, nil } if childInfo.DomainNameDEPRECATED != "" { return domainCache.GetDomainID(childInfo.DomainNameDEPRECATED) } return parentDomainEntry.GetInfo().ID, nil } func trimBinaryChecksums(recentBinaryChecksums []string, currResetPoints []*types.ResetPointInfo, maxResetPoints int) ([]string, []*types.ResetPointInfo) { numResetPoints := len(currResetPoints) if numResetPoints >= maxResetPoints { // If exceeding the max limit, do rotation by taking the oldest ones out. // startIndex plus one here because it needs to make space for the new binary checksum for the current run startIndex := numResetPoints - maxResetPoints + 1 currResetPoints = currResetPoints[startIndex:] recentBinaryChecksums = recentBinaryChecksums[startIndex:] } return recentBinaryChecksums, currResetPoints } ================================================ FILE: service/history/execution/mutable_state_util_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/testing/testdatagen/idlfuzzedtestdata" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/testdata" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/shard" ) func TestCopyActivityInfo(t *testing.T) { t.Run("test CopyActivityInfo Mapping", func(t *testing.T) { f := idlfuzzedtestdata.NewFuzzerWithIDLTypes(t) d1 := persistence.ActivityInfo{} f.Fuzz(&d1) d2 := CopyActivityInfo(t, &d1) assert.Equal(t, &d1, d2) }) } func TestCopyWorkflowExecutionInfo(t *testing.T) { t.Run("test ExecutionInfo Mapping", func(t *testing.T) { f := idlfuzzedtestdata.NewFuzzerWithIDLTypes(t) d1 := persistence.WorkflowExecutionInfo{} f.Fuzz(&d1) d2 := CopyWorkflowExecutionInfo(t, &d1) assert.Equal(t, &d1, d2) }) } func TestCopyTimerInfoMapping(t *testing.T) { t.Run("test Timer info Mapping", func(t *testing.T) { f := idlfuzzedtestdata.NewFuzzerWithIDLTypes(t) d1 := persistence.TimerInfo{} f.Fuzz(&d1) d2 := CopyTimerInfo(t, &d1) assert.Equal(t, &d1, d2) }) } func TestChildWorkflowMapping(t *testing.T) { t.Run("test child workflwo info Mapping", func(t *testing.T) { f := idlfuzzedtestdata.NewFuzzerWithIDLTypes(t) d1 := persistence.ChildExecutionInfo{} f.Fuzz(&d1) d2 := CopyChildInfo(t, &d1) assert.Equal(t, &d1, d2) }) } func TestCopySignalInfo(t *testing.T) { t.Run("test signal info Mapping", func(t *testing.T) { f := idlfuzzedtestdata.NewFuzzerWithIDLTypes(t) d1 := persistence.SignalInfo{} f.Fuzz(&d1) d2 := CopySignalInfo(t, &d1) assert.Equal(t, &d1, d2) }) } func TestCopyCancellationInfo(t *testing.T) { t.Run("test signal info Mapping", func(t *testing.T) { f := idlfuzzedtestdata.NewFuzzerWithIDLTypes(t) d1 := persistence.RequestCancelInfo{} f.Fuzz(&d1) d2 := CopyCancellationInfo(t, &d1) assert.Equal(t, &d1, d2) }) } func TestFindAutoResetPoint(t *testing.T) { timeSource := clock.NewRealTimeSource() // case 1: nil _, pt := FindAutoResetPoint(timeSource, nil, nil) assert.Nil(t, pt) // case 2: empty _, pt = FindAutoResetPoint(timeSource, &types.BadBinaries{}, &types.ResetPoints{}) assert.Nil(t, pt) pt0 := &types.ResetPointInfo{ BinaryChecksum: "abc", Resettable: true, } pt1 := &types.ResetPointInfo{ BinaryChecksum: "def", Resettable: true, } pt3 := &types.ResetPointInfo{ BinaryChecksum: "ghi", Resettable: false, } expiredNowNano := time.Now().UnixNano() - int64(time.Hour) notExpiredNowNano := time.Now().UnixNano() + int64(time.Hour) pt4 := &types.ResetPointInfo{ BinaryChecksum: "expired", Resettable: true, ExpiringTimeNano: common.Int64Ptr(expiredNowNano), } pt5 := &types.ResetPointInfo{ BinaryChecksum: "notExpired", Resettable: true, ExpiringTimeNano: common.Int64Ptr(notExpiredNowNano), } // case 3: two intersection _, pt = FindAutoResetPoint(timeSource, &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "abc": {}, "def": {}, }, }, &types.ResetPoints{ Points: []*types.ResetPointInfo{ pt0, pt1, pt3, }, }) assert.Equal(t, pt, pt0) // case 4: one intersection _, pt = FindAutoResetPoint(timeSource, &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "none": {}, "def": {}, "expired": {}, }, }, &types.ResetPoints{ Points: []*types.ResetPointInfo{ pt4, pt0, pt1, pt3, }, }) assert.Equal(t, pt, pt1) // case 4: no intersection _, pt = FindAutoResetPoint(timeSource, &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "none1": {}, "none2": {}, }, }, &types.ResetPoints{ Points: []*types.ResetPointInfo{ pt0, pt1, pt3, }, }) assert.Nil(t, pt) // case 5: not resettable _, pt = FindAutoResetPoint(timeSource, &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "none1": {}, "ghi": {}, }, }, &types.ResetPoints{ Points: []*types.ResetPointInfo{ pt0, pt1, pt3, }, }) assert.Nil(t, pt) // case 6: one intersection of expired _, pt = FindAutoResetPoint(timeSource, &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "none": {}, "expired": {}, }, }, &types.ResetPoints{ Points: []*types.ResetPointInfo{ pt0, pt1, pt3, pt4, pt5, }, }) assert.Nil(t, pt) // case 7: one intersection of not expired _, pt = FindAutoResetPoint(timeSource, &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "none": {}, "notExpired": {}, }, }, &types.ResetPoints{ Points: []*types.ResetPointInfo{ pt0, pt1, pt3, pt4, pt5, }, }) assert.Equal(t, pt, pt5) } func TestTrimBinaryChecksums(t *testing.T) { testCases := []struct { name string maxResetPoints int expected []string }{ { name: "not reach limit", maxResetPoints: 6, expected: []string{"checksum1", "checksum2", "checksum3", "checksum4", "checksum5"}, }, { name: "reach at limit", maxResetPoints: 5, expected: []string{"checksum2", "checksum3", "checksum4", "checksum5"}, }, { name: "exceeds limit", maxResetPoints: 2, expected: []string{"checksum5"}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { currResetPoints := []*types.ResetPointInfo{ { BinaryChecksum: "checksum1", RunID: "run1", }, { BinaryChecksum: "checksum2", RunID: "run2", }, { BinaryChecksum: "checksum3", RunID: "run3", }, { BinaryChecksum: "checksum4", RunID: "run4", }, { BinaryChecksum: "checksum5", RunID: "run5", }, } var recentBinaryChecksums []string for _, rp := range currResetPoints { recentBinaryChecksums = append(recentBinaryChecksums, rp.GetBinaryChecksum()) } recentBinaryChecksums, currResetPoints = trimBinaryChecksums(recentBinaryChecksums, currResetPoints, tc.maxResetPoints) assert.Equal(t, tc.expected, recentBinaryChecksums) assert.Equal(t, len(tc.expected), len(currResetPoints)) }) } // test empty case currResetPoints := make([]*types.ResetPointInfo, 0, 1) var recentBinaryChecksums []string for _, rp := range currResetPoints { recentBinaryChecksums = append(recentBinaryChecksums, rp.GetBinaryChecksum()) } defer func() { if r := recover(); r != nil { t.Errorf("The function panicked: %v", r) } }() trimedBinaryChecksums, trimedResetPoints := trimBinaryChecksums(recentBinaryChecksums, currResetPoints, 2) assert.Equal(t, recentBinaryChecksums, trimedBinaryChecksums) assert.Equal(t, currResetPoints, trimedResetPoints) } func TestConvertWorkflowRequests(t *testing.T) { inputs := map[persistence.WorkflowRequest]struct{}{} inputs[persistence.WorkflowRequest{RequestID: "aaa", Version: 1, RequestType: persistence.WorkflowRequestTypeStart}] = struct{}{} inputs[persistence.WorkflowRequest{RequestID: "aaa", Version: 1, RequestType: persistence.WorkflowRequestTypeSignal}] = struct{}{} expectedOutputs := []*persistence.WorkflowRequest{ { RequestID: "aaa", Version: 1, RequestType: persistence.WorkflowRequestTypeStart, }, { RequestID: "aaa", Version: 1, RequestType: persistence.WorkflowRequestTypeSignal, }, } assert.ElementsMatch(t, expectedOutputs, convertWorkflowRequests(inputs)) } func TestCreatePersistenceMutableState(t *testing.T) { ctrl := gomock.NewController(t) mockShardContext := shard.NewMockContext(ctrl) logger := testlogger.New(t) mockEventsCache := events.NewMockCache(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) mockShardContext.EXPECT().GetClusterMetadata().Return(constants.TestClusterMetadata).Times(2) mockShardContext.EXPECT().GetEventsCache().Return(mockEventsCache) mockShardContext.EXPECT().GetConfig().Return(config.NewForTest()) mockShardContext.EXPECT().GetTimeSource().Return(clock.NewMockedTimeSource()) mockShardContext.EXPECT().GetMetricsClient().Return(metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{})) mockShardContext.EXPECT().GetDomainCache().Return(mockDomainCache) mockShardContext.EXPECT().GetLogger().Return(logger).AnyTimes() builder := newMutableStateBuilder(mockShardContext, logger, constants.TestLocalDomainEntry, constants.TestLocalDomainEntry.GetFailoverVersion()) builder.pendingActivityInfoIDs[0] = &persistence.ActivityInfo{} builder.pendingTimerInfoIDs["some-key"] = &persistence.TimerInfo{} builder.pendingSignalInfoIDs[0] = &persistence.SignalInfo{} builder.pendingChildExecutionInfoIDs[0] = &persistence.ChildExecutionInfo{} builder.bufferedEvents = []*types.HistoryEvent{{}} builder.updateBufferedEvents = []*types.HistoryEvent{{}} builder.versionHistories = &persistence.VersionHistories{CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{}} builder.pendingRequestCancelInfoIDs[0] = &persistence.RequestCancelInfo{} builder.decisionTaskManager.(*mutableStateDecisionTaskManagerImpl).HasInFlightDecision() builder.executionInfo.DecisionStartedID = 1 mutableState := CreatePersistenceMutableState(t, builder) assert.NotNil(t, mutableState) assert.Equal(t, builder.executionInfo, mutableState.ExecutionInfo) assert.Equal(t, builder.pendingActivityInfoIDs, mutableState.ActivityInfos) assert.Equal(t, builder.pendingSignalInfoIDs, mutableState.SignalInfos) assert.Equal(t, builder.pendingTimerInfoIDs, mutableState.TimerInfos) assert.Equal(t, builder.pendingRequestCancelInfoIDs, mutableState.RequestCancelInfos) assert.Equal(t, len(builder.bufferedEvents)+len(builder.updateBufferedEvents), len(mutableState.BufferedEvents)) } func TestGetChildExecutionDomainName(t *testing.T) { t.Run("nonempty domain ID", func(t *testing.T) { childInfo := &persistence.ChildExecutionInfo{DomainID: testdata.DomainID} mockDomainCache := cache.NewMockDomainCache(gomock.NewController(t)) expected := testdata.DomainName mockDomainCache.EXPECT().GetDomainName(childInfo.DomainID).Return(expected, nil) name, err := GetChildExecutionDomainName(childInfo, mockDomainCache, constants.TestLocalDomainEntry) require.NoError(t, err) assert.Equal(t, expected, name) }) t.Run("nonempty domain name", func(t *testing.T) { childInfo := &persistence.ChildExecutionInfo{DomainNameDEPRECATED: testdata.DomainName} parentDomainEntry := constants.TestLocalDomainEntry mockDomainCache := cache.NewMockDomainCache(gomock.NewController(t)) name, err := GetChildExecutionDomainName(childInfo, mockDomainCache, parentDomainEntry) require.NoError(t, err) assert.Equal(t, testdata.DomainName, name) }) t.Run("empty childInfo", func(t *testing.T) { childInfo := &persistence.ChildExecutionInfo{} parentDomainEntry := constants.TestLocalDomainEntry mockDomainCache := cache.NewMockDomainCache(gomock.NewController(t)) name, err := GetChildExecutionDomainName(childInfo, mockDomainCache, parentDomainEntry) require.NoError(t, err) assert.Equal(t, parentDomainEntry.GetInfo().Name, name) }) } func TestGetChildExecutionDomainID(t *testing.T) { t.Run("nonempty domain ID", func(t *testing.T) { childInfo := &persistence.ChildExecutionInfo{DomainID: testdata.DomainID} mockDomainCache := cache.NewMockDomainCache(gomock.NewController(t)) name, err := GetChildExecutionDomainID(childInfo, mockDomainCache, constants.TestLocalDomainEntry) require.NoError(t, err) assert.Equal(t, testdata.DomainID, name) }) t.Run("nonempty domain name", func(t *testing.T) { childInfo := &persistence.ChildExecutionInfo{DomainNameDEPRECATED: testdata.DomainName} parentDomainEntry := constants.TestLocalDomainEntry mockDomainCache := cache.NewMockDomainCache(gomock.NewController(t)) mockDomainCache.EXPECT().GetDomainID(testdata.DomainName).Return(testdata.DomainID, nil) name, err := GetChildExecutionDomainID(childInfo, mockDomainCache, parentDomainEntry) require.NoError(t, err) assert.Equal(t, testdata.DomainID, name) }) t.Run("empty childInfo", func(t *testing.T) { childInfo := &persistence.ChildExecutionInfo{} parentDomainEntry := constants.TestLocalDomainEntry mockDomainCache := cache.NewMockDomainCache(gomock.NewController(t)) name, err := GetChildExecutionDomainID(childInfo, mockDomainCache, parentDomainEntry) require.NoError(t, err) assert.Equal(t, parentDomainEntry.GetInfo().ID, name) }) } func TestGetChildExecutionDomainEntry(t *testing.T) { t.Run("nonempty domain ID", func(t *testing.T) { childInfo := &persistence.ChildExecutionInfo{DomainID: testdata.DomainID} mockDomainCache := cache.NewMockDomainCache(gomock.NewController(t)) expected := &cache.DomainCacheEntry{} mockDomainCache.EXPECT().GetDomainByID(childInfo.DomainID).Return(expected, nil) entry, err := GetChildExecutionDomainEntry(t, childInfo, mockDomainCache, constants.TestLocalDomainEntry) require.NoError(t, err) assert.Equal(t, expected, entry) }) t.Run("nonempty domain name", func(t *testing.T) { childInfo := &persistence.ChildExecutionInfo{DomainNameDEPRECATED: testdata.DomainName} parentDomainEntry := constants.TestLocalDomainEntry mockDomainCache := cache.NewMockDomainCache(gomock.NewController(t)) expected := &cache.DomainCacheEntry{} mockDomainCache.EXPECT().GetDomain(childInfo.DomainNameDEPRECATED).Return(expected, nil) entry, err := GetChildExecutionDomainEntry(t, childInfo, mockDomainCache, parentDomainEntry) require.NoError(t, err) assert.Equal(t, expected, entry) }) t.Run("empty childInfo", func(t *testing.T) { childInfo := &persistence.ChildExecutionInfo{} parentDomainEntry := constants.TestLocalDomainEntry mockDomainCache := cache.NewMockDomainCache(gomock.NewController(t)) entry, err := GetChildExecutionDomainEntry(t, childInfo, mockDomainCache, parentDomainEntry) require.NoError(t, err) assert.Equal(t, parentDomainEntry, entry) }) } func TestConvert(t *testing.T) { t.Run("convertSyncActivityInfos", func(t *testing.T) { executionInfo := &persistence.WorkflowExecutionInfo{ DomainID: "some-domain-id", WorkflowID: "some-workflow-id", RunID: "some-run-id", } activityInfos := map[int64]*persistence.ActivityInfo{1: {Version: 1, ScheduleID: 1}} inputs := map[int64]struct{}{1: {}} outputs := convertSyncActivityInfos(executionInfo, activityInfos, inputs) assert.NotNil(t, outputs) assert.Equal(t, 1, len(outputs)) assert.Equal(t, int64(1), outputs[0].(*persistence.SyncActivityTask).ScheduledID) assert.Equal(t, int64(1), outputs[0].GetVersion()) assert.Equal(t, executionInfo.DomainID, outputs[0].(*persistence.SyncActivityTask).DomainID) assert.Equal(t, executionInfo.WorkflowID, outputs[0].(*persistence.SyncActivityTask).WorkflowID) assert.Equal(t, executionInfo.RunID, outputs[0].(*persistence.SyncActivityTask).RunID) }) } func TestScheduleDecision(t *testing.T) { t.Run("mutable state has pending decision", func(t *testing.T) { mockMutableState := NewMockMutableState(gomock.NewController(t)) mockMutableState.EXPECT().HasPendingDecision().Return(true) err := ScheduleDecision(mockMutableState) require.NoError(t, err) }) t.Run("internal service error", func(t *testing.T) { mockMutableState := NewMockMutableState(gomock.NewController(t)) mockMutableState.EXPECT().HasPendingDecision().Return(false) mockMutableState.EXPECT().AddDecisionTaskScheduledEvent(false).Return(nil, errors.New("some error")) err := ScheduleDecision(mockMutableState) assert.NotNil(t, err) assert.Equal(t, "Failed to add decision scheduled event.", err.Error()) }) t.Run("success", func(t *testing.T) { mockMutableState := NewMockMutableState(gomock.NewController(t)) mockMutableState.EXPECT().HasPendingDecision().Return(false) mockMutableState.EXPECT().AddDecisionTaskScheduledEvent(false).Return(nil, nil) err := ScheduleDecision(mockMutableState) require.NoError(t, err) }) } func TestFailDecision(t *testing.T) { t.Run("success", func(t *testing.T) { mockMutableState := NewMockMutableState(gomock.NewController(t)) decision := &DecisionInfo{} failureCause := new(types.DecisionTaskFailedCause) mockMutableState.EXPECT().AddDecisionTaskFailedEvent( decision.ScheduleID, decision.StartedID, *failureCause, nil, IdentityHistoryService, "", "", "", "", int64(0), "", ).Return(nil, nil) mockMutableState.EXPECT().FlushBufferedEvents() // only on success err := FailDecision(mockMutableState, decision, *failureCause) require.NoError(t, err) }) t.Run("failure", func(t *testing.T) { mockMutableState := NewMockMutableState(gomock.NewController(t)) decision := &DecisionInfo{} failureCause := new(types.DecisionTaskFailedCause) mockMutableState.EXPECT().AddDecisionTaskFailedEvent( decision.ScheduleID, decision.StartedID, *failureCause, nil, IdentityHistoryService, "", "", "", "", int64(0), "", ).Return(nil, errors.New("some error adding failed event")) err := FailDecision(mockMutableState, decision, *failureCause) assert.NotNil(t, err) assert.Equal(t, "some error adding failed event", err.Error()) }) } ================================================ FILE: service/history/execution/mutable_state_util_test_helpers.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package execution import ( "encoding/json" "testing" "golang.org/x/exp/slices" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // CreatePersistenceMutableState creates a persistence mutable state based on the its in-memory version func CreatePersistenceMutableState(t *testing.T, ms MutableState) *persistence.WorkflowMutableState { builder := ms.(*mutableStateBuilder) builder.FlushBufferedEvents() //nolint:errcheck info := CopyWorkflowExecutionInfo(t, builder.GetExecutionInfo()) stats := &persistence.ExecutionStats{} activityInfos := make(map[int64]*persistence.ActivityInfo) for id, info := range builder.GetPendingActivityInfos() { activityInfos[id] = CopyActivityInfo(t, info) } timerInfos := make(map[string]*persistence.TimerInfo) for id, info := range builder.GetPendingTimerInfos() { timerInfos[id] = CopyTimerInfo(t, info) } cancellationInfos := make(map[int64]*persistence.RequestCancelInfo) for id, info := range builder.GetPendingRequestCancelExternalInfos() { cancellationInfos[id] = CopyCancellationInfo(t, info) } signalInfos := make(map[int64]*persistence.SignalInfo) for id, info := range builder.GetPendingSignalExternalInfos() { signalInfos[id] = CopySignalInfo(t, info) } childInfos := make(map[int64]*persistence.ChildExecutionInfo) for id, info := range builder.GetPendingChildExecutionInfos() { childInfos[id] = CopyChildInfo(t, info) } builder.FlushBufferedEvents() //nolint:errcheck var bufferedEvents []*types.HistoryEvent if len(builder.bufferedEvents) > 0 { bufferedEvents = append(bufferedEvents, builder.bufferedEvents...) } if len(builder.updateBufferedEvents) > 0 { bufferedEvents = append(bufferedEvents, builder.updateBufferedEvents...) } var versionHistories *persistence.VersionHistories if ms.GetVersionHistories() != nil { versionHistories = ms.GetVersionHistories().Duplicate() } return &persistence.WorkflowMutableState{ ExecutionInfo: info, ExecutionStats: stats, ActivityInfos: activityInfos, TimerInfos: timerInfos, BufferedEvents: bufferedEvents, SignalInfos: signalInfos, RequestCancelInfos: cancellationInfos, ChildExecutionInfos: childInfos, VersionHistories: versionHistories, } } // CopyWorkflowExecutionInfo copies WorkflowExecutionInfo func CopyWorkflowExecutionInfo(t *testing.T, sourceInfo *persistence.WorkflowExecutionInfo) *persistence.WorkflowExecutionInfo { return &persistence.WorkflowExecutionInfo{ DomainID: sourceInfo.DomainID, WorkflowID: sourceInfo.WorkflowID, RunID: sourceInfo.RunID, FirstExecutionRunID: sourceInfo.FirstExecutionRunID, ParentDomainID: sourceInfo.ParentDomainID, ParentWorkflowID: sourceInfo.ParentWorkflowID, ParentRunID: sourceInfo.ParentRunID, IsCron: sourceInfo.IsCron, InitiatedID: sourceInfo.InitiatedID, CompletionEventBatchID: sourceInfo.CompletionEventBatchID, CompletionEvent: sourceInfo.CompletionEvent, TaskList: sourceInfo.TaskList, TaskListKind: sourceInfo.TaskListKind, StickyTaskList: sourceInfo.StickyTaskList, StickyScheduleToStartTimeout: sourceInfo.StickyScheduleToStartTimeout, WorkflowTypeName: sourceInfo.WorkflowTypeName, WorkflowTimeout: sourceInfo.WorkflowTimeout, DecisionStartToCloseTimeout: sourceInfo.DecisionStartToCloseTimeout, ExecutionContext: sourceInfo.ExecutionContext, State: sourceInfo.State, CloseStatus: sourceInfo.CloseStatus, LastFirstEventID: sourceInfo.LastFirstEventID, LastEventTaskID: sourceInfo.LastEventTaskID, NextEventID: sourceInfo.NextEventID, LastProcessedEvent: sourceInfo.LastProcessedEvent, StartTimestamp: sourceInfo.StartTimestamp, LastUpdatedTimestamp: sourceInfo.LastUpdatedTimestamp, CreateRequestID: sourceInfo.CreateRequestID, SignalCount: sourceInfo.SignalCount, DecisionVersion: sourceInfo.DecisionVersion, DecisionScheduleID: sourceInfo.DecisionScheduleID, DecisionStartedID: sourceInfo.DecisionStartedID, DecisionRequestID: sourceInfo.DecisionRequestID, DecisionTimeout: sourceInfo.DecisionTimeout, DecisionAttempt: sourceInfo.DecisionAttempt, DecisionScheduledTimestamp: sourceInfo.DecisionScheduledTimestamp, DecisionStartedTimestamp: sourceInfo.DecisionStartedTimestamp, DecisionOriginalScheduledTimestamp: sourceInfo.DecisionOriginalScheduledTimestamp, CancelRequested: sourceInfo.CancelRequested, CancelRequestID: sourceInfo.CancelRequestID, CronSchedule: sourceInfo.CronSchedule, ClientLibraryVersion: sourceInfo.ClientLibraryVersion, ClientFeatureVersion: sourceInfo.ClientFeatureVersion, ClientImpl: sourceInfo.ClientImpl, AutoResetPoints: sourceInfo.AutoResetPoints, Memo: sourceInfo.Memo, SearchAttributes: sourceInfo.SearchAttributes, PartitionConfig: sourceInfo.PartitionConfig, ExecutionStatus: sourceInfo.ExecutionStatus, ScheduledExecutionTimestamp: sourceInfo.ScheduledExecutionTimestamp, Attempt: sourceInfo.Attempt, HasRetryPolicy: sourceInfo.HasRetryPolicy, InitialInterval: sourceInfo.InitialInterval, BackoffCoefficient: sourceInfo.BackoffCoefficient, MaximumInterval: sourceInfo.MaximumInterval, ExpirationTime: sourceInfo.ExpirationTime, MaximumAttempts: sourceInfo.MaximumAttempts, NonRetriableErrors: sourceInfo.NonRetriableErrors, BranchToken: sourceInfo.BranchToken, ExpirationSeconds: sourceInfo.ExpirationSeconds, CronOverlapPolicy: sourceInfo.CronOverlapPolicy, ActiveClusterSelectionPolicy: sourceInfo.ActiveClusterSelectionPolicy, } } // CopyActivityInfo copies ActivityInfo func CopyActivityInfo(t *testing.T, sourceInfo *persistence.ActivityInfo) *persistence.ActivityInfo { details := slices.Clone(sourceInfo.Details) return &persistence.ActivityInfo{ Version: sourceInfo.Version, ScheduleID: sourceInfo.ScheduleID, ScheduledEventBatchID: sourceInfo.ScheduledEventBatchID, ScheduledEvent: deepCopyHistoryEvent(t, sourceInfo.ScheduledEvent), StartedID: sourceInfo.StartedID, StartedEvent: deepCopyHistoryEvent(t, sourceInfo.StartedEvent), ActivityID: sourceInfo.ActivityID, RequestID: sourceInfo.RequestID, Details: details, ScheduledTime: sourceInfo.ScheduledTime, StartedTime: sourceInfo.StartedTime, ScheduleToStartTimeout: sourceInfo.ScheduleToStartTimeout, ScheduleToCloseTimeout: sourceInfo.ScheduleToCloseTimeout, StartToCloseTimeout: sourceInfo.StartToCloseTimeout, HeartbeatTimeout: sourceInfo.HeartbeatTimeout, LastHeartBeatUpdatedTime: sourceInfo.LastHeartBeatUpdatedTime, CancelRequested: sourceInfo.CancelRequested, CancelRequestID: sourceInfo.CancelRequestID, TimerTaskStatus: sourceInfo.TimerTaskStatus, Attempt: sourceInfo.Attempt, DomainID: sourceInfo.DomainID, StartedIdentity: sourceInfo.StartedIdentity, TaskList: sourceInfo.TaskList, TaskListKind: sourceInfo.TaskListKind, HasRetryPolicy: sourceInfo.HasRetryPolicy, InitialInterval: sourceInfo.InitialInterval, BackoffCoefficient: sourceInfo.BackoffCoefficient, MaximumInterval: sourceInfo.MaximumInterval, ExpirationTime: sourceInfo.ExpirationTime, MaximumAttempts: sourceInfo.MaximumAttempts, NonRetriableErrors: sourceInfo.NonRetriableErrors, LastFailureReason: sourceInfo.LastFailureReason, LastWorkerIdentity: sourceInfo.LastWorkerIdentity, LastFailureDetails: sourceInfo.LastFailureDetails, // Not written to database - This is used only for deduping heartbeat timer creation LastHeartbeatTimeoutVisibilityInSeconds: sourceInfo.LastHeartbeatTimeoutVisibilityInSeconds, } } // CopyTimerInfo copies TimerInfo func CopyTimerInfo(t *testing.T, sourceInfo *persistence.TimerInfo) *persistence.TimerInfo { return &persistence.TimerInfo{ Version: sourceInfo.Version, TimerID: sourceInfo.TimerID, StartedID: sourceInfo.StartedID, ExpiryTime: sourceInfo.ExpiryTime, TaskStatus: sourceInfo.TaskStatus, } } // CopyCancellationInfo copies RequestCancelInfo func CopyCancellationInfo(t *testing.T, sourceInfo *persistence.RequestCancelInfo) *persistence.RequestCancelInfo { return &persistence.RequestCancelInfo{ Version: sourceInfo.Version, InitiatedID: sourceInfo.InitiatedID, InitiatedEventBatchID: sourceInfo.InitiatedEventBatchID, CancelRequestID: sourceInfo.CancelRequestID, } } // CopySignalInfo copies SignalInfo func CopySignalInfo(t *testing.T, sourceInfo *persistence.SignalInfo) *persistence.SignalInfo { return &persistence.SignalInfo{ Version: sourceInfo.Version, InitiatedEventBatchID: sourceInfo.InitiatedEventBatchID, InitiatedID: sourceInfo.InitiatedID, SignalRequestID: sourceInfo.SignalRequestID, SignalName: sourceInfo.SignalName, Input: slices.Clone(sourceInfo.Input), Control: slices.Clone(sourceInfo.Control), } } // CopyChildInfo copies ChildExecutionInfo func CopyChildInfo(t *testing.T, sourceInfo *persistence.ChildExecutionInfo) *persistence.ChildExecutionInfo { return &persistence.ChildExecutionInfo{ Version: sourceInfo.Version, InitiatedID: sourceInfo.InitiatedID, InitiatedEventBatchID: sourceInfo.InitiatedEventBatchID, StartedID: sourceInfo.StartedID, StartedWorkflowID: sourceInfo.StartedWorkflowID, StartedRunID: sourceInfo.StartedRunID, CreateRequestID: sourceInfo.CreateRequestID, DomainID: sourceInfo.DomainID, DomainNameDEPRECATED: sourceInfo.DomainNameDEPRECATED, WorkflowTypeName: sourceInfo.WorkflowTypeName, ParentClosePolicy: sourceInfo.ParentClosePolicy, InitiatedEvent: deepCopyHistoryEvent(t, sourceInfo.InitiatedEvent), StartedEvent: deepCopyHistoryEvent(t, sourceInfo.StartedEvent), } } func deepCopyHistoryEvent(t *testing.T, e *types.HistoryEvent) *types.HistoryEvent { if e == nil { return nil } bytes, err := json.Marshal(e) if err != nil { panic(err) } var copy types.HistoryEvent err = json.Unmarshal(bytes, ©) if err != nil { panic(err) } return © } // GetChildExecutionDomainEntry get domain entry for the child workflow // NOTE: DomainName in ChildExecutionInfo is being deprecated, and // we should always use DomainID field instead. // this function exists for backward compatibility reason func GetChildExecutionDomainEntry( t *testing.T, childInfo *persistence.ChildExecutionInfo, domainCache cache.DomainCache, parentDomainEntry *cache.DomainCacheEntry, ) (*cache.DomainCacheEntry, error) { if childInfo.DomainID != "" { return domainCache.GetDomainByID(childInfo.DomainID) } if childInfo.DomainNameDEPRECATED != "" { return domainCache.GetDomain(childInfo.DomainNameDEPRECATED) } return parentDomainEntry, nil } ================================================ FILE: service/history/execution/retry.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "math" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" ) func getBackoffInterval( now time.Time, expirationTime time.Time, currAttempt int32, maxAttempts int32, initInterval int32, maxInterval int32, backoffCoefficient float64, failureReason string, nonRetriableErrors []string, ) time.Duration { if maxAttempts == 0 && expirationTime.IsZero() { return backoff.NoBackoff } if maxAttempts > 0 && currAttempt >= maxAttempts-1 { // currAttempt starts from 0. // MaximumAttempts is the total attempts, including initial (non-retry) attempt. return backoff.NoBackoff } nextInterval := int64(float64(initInterval) * math.Pow(backoffCoefficient, float64(currAttempt))) if nextInterval <= 0 { // math.Pow() could overflow if maxInterval > 0 { nextInterval = int64(maxInterval) } else { return backoff.NoBackoff } } if maxInterval > 0 && nextInterval > int64(maxInterval) { // cap next interval to MaxInterval nextInterval = int64(maxInterval) } backoffInterval := time.Duration(nextInterval) * time.Second nextScheduleTime := now.Add(backoffInterval) if !expirationTime.IsZero() && nextScheduleTime.After(expirationTime) { return backoff.NoBackoff } // make sure we don't retry size exceeded error reasons. Note that FailureReasonFailureDetailsExceedsLimit is retryable. if failureReason == common.FailureReasonCancelDetailsExceedsLimit || failureReason == common.FailureReasonCompleteResultExceedsLimit || failureReason == common.FailureReasonHeartbeatExceedsLimit || failureReason == common.FailureReasonDecisionBlobSizeExceedsLimit { return backoff.NoBackoff } // check if error is non-retriable for _, er := range nonRetriableErrors { if er == failureReason { return backoff.NoBackoff } } return backoffInterval } ================================================ FILE: service/history/execution/retry_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/persistence" ) func Test_NextRetry2(t *testing.T) { // a := assert.New(t) now, _ := time.Parse(time.RFC3339, "2018-04-13T16:08:08+00:00") reason := "good-reason" identity := "some-worker-identity" // no retry without retry policy ai := &persistence.ActivityInfo{ ScheduleToStartTimeout: int32((30 * time.Minute).Seconds()), ScheduleToCloseTimeout: int32((30 * time.Minute).Seconds()), StartToCloseTimeout: int32((30 * time.Minute).Seconds()), HasRetryPolicy: true, NonRetriableErrors: []string{"bad-reason", "ugly-reason"}, StartedIdentity: identity, MaximumAttempts: 0, InitialInterval: 60, BackoffCoefficient: 1, MaximumInterval: 6000, ExpirationTime: now.Add(86400 * time.Second), Attempt: 5, } dur := getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, ) t.Logf("dur: %v", dur) } func Test_NextRetry(t *testing.T) { a := assert.New(t) now, _ := time.Parse(time.RFC3339, "2018-04-13T16:08:08+00:00") reason := "good-reason" identity := "some-worker-identity" // no retry without retry policy ai := &persistence.ActivityInfo{ ScheduleToStartTimeout: 5, ScheduleToCloseTimeout: 30, StartToCloseTimeout: 25, HasRetryPolicy: false, NonRetriableErrors: []string{"bad-reason", "ugly-reason"}, StartedIdentity: identity, } a.Equal(backoff.NoBackoff, getBackoffInterval( clock.NewRealTimeSource().Now(), ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) // no retry if cancel requested ai.HasRetryPolicy = true ai.CancelRequested = true a.Equal(backoff.NoBackoff, getBackoffInterval( clock.NewRealTimeSource().Now(), ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) // no retry if both MaximumAttempts and ExpirationTime are not set ai.CancelRequested = false a.Equal(backoff.NoBackoff, getBackoffInterval( clock.NewRealTimeSource().Now(), ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) // no retry if MaximumAttempts is 1 (for initial attempt) ai.InitialInterval = 1 ai.MaximumAttempts = 1 a.Equal(backoff.NoBackoff, getBackoffInterval( clock.NewRealTimeSource().Now(), ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) // backoff retry, intervals: 1s, 2s, 4s, 8s. ai.MaximumAttempts = 5 ai.BackoffCoefficient = 2 a.Equal(time.Second, getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) ai.Attempt++ a.Equal(time.Second*2, getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) ai.Attempt++ a.Equal(time.Second*4, getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) ai.Attempt++ // test non-retriable error reason = "bad-reason" a.Equal(backoff.NoBackoff, getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) reason = "good-reason" a.Equal(time.Second*8, getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) ai.Attempt++ // no retry as max attempt reached a.Equal(ai.MaximumAttempts-1, ai.Attempt) a.Equal(backoff.NoBackoff, getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) // increase max attempts, with max interval cap at 10s ai.MaximumAttempts = 6 ai.MaximumInterval = 10 a.Equal(time.Second*10, getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) ai.Attempt++ // no retry because expiration time before next interval ai.MaximumAttempts = 8 ai.ExpirationTime = now.Add(time.Second * 5) a.Equal(backoff.NoBackoff, getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) // extend expiration, next interval should be 10s ai.ExpirationTime = now.Add(time.Minute) a.Equal(time.Second*10, getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) ai.Attempt++ // with big max retry, math.Pow() could overflow, verify that it uses the MaxInterval ai.Attempt = 64 ai.MaximumAttempts = 100 a.Equal(time.Second*10, getBackoffInterval( now, ai.ExpirationTime, ai.Attempt, ai.MaximumAttempts, ai.InitialInterval, ai.MaximumInterval, ai.BackoffCoefficient, reason, ai.NonRetriableErrors, )) ai.Attempt++ } ================================================ FILE: service/history/execution/state_builder.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination state_builder_mock.go -self_package github.com/uber/cadence/service/history/execution package execution import ( "time" "github.com/pborman/uuid" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/shard" ) type ( // StateBuilder is the mutable state builder StateBuilder interface { ApplyEvents( domainID string, requestID string, workflowExecution types.WorkflowExecution, history []*types.HistoryEvent, newRunHistory []*types.HistoryEvent, ) (MutableState, error) GetMutableState() MutableState } stateBuilderImpl struct { shard shard.Context clusterMetadata cluster.Metadata domainCache cache.DomainCache logger log.Logger mutableState MutableState } ) const ( errMessageHistorySizeZero = "encounter history size being zero" ) var _ StateBuilder = (*stateBuilderImpl)(nil) // NewStateBuilder creates a state builder func NewStateBuilder( shard shard.Context, logger log.Logger, mutableState MutableState, ) StateBuilder { return &stateBuilderImpl{ shard: shard, clusterMetadata: shard.GetService().GetClusterMetadata(), domainCache: shard.GetDomainCache(), logger: logger, mutableState: mutableState, } } func (b *stateBuilderImpl) ApplyEvents( domainID string, requestID string, workflowExecution types.WorkflowExecution, history []*types.HistoryEvent, newRunHistory []*types.HistoryEvent, ) (MutableState, error) { if len(history) == 0 { return nil, errors.NewInternalFailureError(errMessageHistorySizeZero) } firstEvent := history[0] lastEvent := history[len(history)-1] var newRunMutableStateBuilder MutableState b.logger.Debugf("stateBuilderImpl Applying events for domain %s, wfID %v, first event [id:%v, version:%v], last event [id:%v, version:%v]", domainID, workflowExecution.WorkflowID, firstEvent.ID, firstEvent.Version, lastEvent.ID, lastEvent.Version) // need to clear the stickiness since workflow turned to passive b.mutableState.ClearStickyness() historyLength := len(history) for i, event := range history { b.logger.Debugf("stateBuilderImpl Applying event %v of %v. Calling UpdateCurrentVersion for domain %s, wfID %v, event [id:%v, version:%v]", i+1, historyLength, domainID, workflowExecution.WorkflowID, event.ID, event.Version) // NOTE: stateBuilder is also being used in the active side if err := b.mutableState.UpdateCurrentVersion(event.Version, true); err != nil { return nil, err } versionHistories := b.mutableState.GetVersionHistories() if versionHistories == nil { return nil, ErrMissingVersionHistories } versionHistory, err := versionHistories.GetCurrentVersionHistory() if err != nil { return nil, err } if err := versionHistory.AddOrUpdateItem(persistence.NewVersionHistoryItem( event.ID, event.Version, )); err != nil { return nil, err } b.mutableState.GetExecutionInfo().LastEventTaskID = event.TaskID switch event.GetEventType() { case types.EventTypeWorkflowExecutionStarted: attributes := event.WorkflowExecutionStartedEventAttributes var parentDomainID *string // If ParentWorkflowDomainID is present use it, otherwise fallback to ParentWorkflowDomain // as ParentWorkflowDomainID will not be present on older histories. if attributes.ParentWorkflowDomainID != nil { parentDomainID = attributes.ParentWorkflowDomainID } else if attributes.GetParentWorkflowDomain() != "" { parentDomainEntry, err := b.domainCache.GetDomain( attributes.GetParentWorkflowDomain(), ) if err != nil { return nil, err } parentDomainID = &parentDomainEntry.GetInfo().ID } b.logger.Debug("stateBuilderImpl calling ReplicateWorkflowExecutionStartedEvent", tag.WorkflowDomainID(domainID), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.Dynamic("attributes.activecluster-sel-policy-nil", attributes.ActiveClusterSelectionPolicy == nil), ) if err := b.mutableState.ReplicateWorkflowExecutionStartedEvent( parentDomainID, workflowExecution, requestID, event, true, ); err != nil { return nil, err } if err := b.mutableState.SetHistoryTree( workflowExecution.GetRunID(), ); err != nil { return nil, err } case types.EventTypeDecisionTaskScheduled: attributes := event.DecisionTaskScheduledEventAttributes // use event.GetTimestamp() as DecisionOriginalScheduledTimestamp, because the heartbeat is not happening here. _, err := b.mutableState.ReplicateDecisionTaskScheduledEvent( event.Version, event.ID, attributes.TaskList.GetName(), attributes.GetStartToCloseTimeoutSeconds(), attributes.GetAttempt(), event.GetTimestamp(), event.GetTimestamp(), false, ) if err != nil { return nil, err } case types.EventTypeDecisionTaskStarted: attributes := event.DecisionTaskStartedEventAttributes _, err := b.mutableState.ReplicateDecisionTaskStartedEvent( nil, event.Version, attributes.GetScheduledEventID(), event.ID, attributes.GetRequestID(), event.GetTimestamp(), ) if err != nil { return nil, err } case types.EventTypeDecisionTaskCompleted: if err := b.mutableState.ReplicateDecisionTaskCompletedEvent( event, ); err != nil { return nil, err } case types.EventTypeDecisionTaskTimedOut: if err := b.mutableState.ReplicateDecisionTaskTimedOutEvent( event, ); err != nil { return nil, err } // this is for transient decision err := b.mutableState.ReplicateTransientDecisionTaskScheduled() if err != nil { return nil, err } case types.EventTypeDecisionTaskFailed: if err := b.mutableState.ReplicateDecisionTaskFailedEvent(event); err != nil { return nil, err } // this is for transient decision err := b.mutableState.ReplicateTransientDecisionTaskScheduled() if err != nil { return nil, err } case types.EventTypeActivityTaskScheduled: if _, err := b.mutableState.ReplicateActivityTaskScheduledEvent( firstEvent.ID, event, false, ); err != nil { return nil, err } case types.EventTypeActivityTaskStarted: if err := b.mutableState.ReplicateActivityTaskStartedEvent( event, ); err != nil { return nil, err } case types.EventTypeActivityTaskCompleted: if err := b.mutableState.ReplicateActivityTaskCompletedEvent( event, ); err != nil { return nil, err } case types.EventTypeActivityTaskFailed: if err := b.mutableState.ReplicateActivityTaskFailedEvent( event, ); err != nil { return nil, err } case types.EventTypeActivityTaskTimedOut: if err := b.mutableState.ReplicateActivityTaskTimedOutEvent( event, ); err != nil { return nil, err } case types.EventTypeActivityTaskCancelRequested: if err := b.mutableState.ReplicateActivityTaskCancelRequestedEvent( event, ); err != nil { return nil, err } case types.EventTypeActivityTaskCanceled: if err := b.mutableState.ReplicateActivityTaskCanceledEvent( event, ); err != nil { return nil, err } case types.EventTypeRequestCancelActivityTaskFailed: // No mutable state action is needed case types.EventTypeTimerStarted: if _, err := b.mutableState.ReplicateTimerStartedEvent( event, ); err != nil { return nil, err } case types.EventTypeTimerFired: if err := b.mutableState.ReplicateTimerFiredEvent( event, ); err != nil { return nil, err } case types.EventTypeTimerCanceled: if err := b.mutableState.ReplicateTimerCanceledEvent( event, ); err != nil { return nil, err } case types.EventTypeCancelTimerFailed: // no mutable state action is needed case types.EventTypeStartChildWorkflowExecutionInitiated: if _, err := b.mutableState.ReplicateStartChildWorkflowExecutionInitiatedEvent( firstEvent.ID, event, // create a new request ID which is used by transfer queue processor // if domain is failed over at this point uuid.New(), ); err != nil { return nil, err } case types.EventTypeStartChildWorkflowExecutionFailed: if err := b.mutableState.ReplicateStartChildWorkflowExecutionFailedEvent( event, ); err != nil { return nil, err } case types.EventTypeChildWorkflowExecutionStarted: if err := b.mutableState.ReplicateChildWorkflowExecutionStartedEvent( event, ); err != nil { return nil, err } case types.EventTypeChildWorkflowExecutionCompleted: if err := b.mutableState.ReplicateChildWorkflowExecutionCompletedEvent( event, ); err != nil { return nil, err } case types.EventTypeChildWorkflowExecutionFailed: if err := b.mutableState.ReplicateChildWorkflowExecutionFailedEvent( event, ); err != nil { return nil, err } case types.EventTypeChildWorkflowExecutionCanceled: if err := b.mutableState.ReplicateChildWorkflowExecutionCanceledEvent( event, ); err != nil { return nil, err } case types.EventTypeChildWorkflowExecutionTimedOut: if err := b.mutableState.ReplicateChildWorkflowExecutionTimedOutEvent( event, ); err != nil { return nil, err } case types.EventTypeChildWorkflowExecutionTerminated: if err := b.mutableState.ReplicateChildWorkflowExecutionTerminatedEvent( event, ); err != nil { return nil, err } case types.EventTypeRequestCancelExternalWorkflowExecutionInitiated: if _, err := b.mutableState.ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent( firstEvent.ID, event, // create a new request ID which is used by transfer queue processor // if domain is failed over at this point uuid.New(), ); err != nil { return nil, err } case types.EventTypeRequestCancelExternalWorkflowExecutionFailed: if err := b.mutableState.ReplicateRequestCancelExternalWorkflowExecutionFailedEvent( event, ); err != nil { return nil, err } case types.EventTypeExternalWorkflowExecutionCancelRequested: if err := b.mutableState.ReplicateExternalWorkflowExecutionCancelRequested( event, ); err != nil { return nil, err } case types.EventTypeSignalExternalWorkflowExecutionInitiated: // Create a new request ID which is used by transfer queue processor if domain is failed over at this point signalRequestID := uuid.New() if _, err := b.mutableState.ReplicateSignalExternalWorkflowExecutionInitiatedEvent( firstEvent.ID, event, signalRequestID, ); err != nil { return nil, err } case types.EventTypeSignalExternalWorkflowExecutionFailed: if err := b.mutableState.ReplicateSignalExternalWorkflowExecutionFailedEvent( event, ); err != nil { return nil, err } case types.EventTypeExternalWorkflowExecutionSignaled: if err := b.mutableState.ReplicateExternalWorkflowExecutionSignaled( event, ); err != nil { return nil, err } case types.EventTypeMarkerRecorded: // No mutable state action is needed case types.EventTypeWorkflowExecutionSignaled: if err := b.mutableState.ReplicateWorkflowExecutionSignaled( event, ); err != nil { return nil, err } case types.EventTypeWorkflowExecutionCancelRequested: if err := b.mutableState.ReplicateWorkflowExecutionCancelRequestedEvent( event, ); err != nil { return nil, err } case types.EventTypeUpsertWorkflowSearchAttributes: if err := b.mutableState.ReplicateUpsertWorkflowSearchAttributesEvent( event, ); err != nil { return nil, err } case types.EventTypeWorkflowExecutionCompleted: if err := b.mutableState.ReplicateWorkflowExecutionCompletedEvent( firstEvent.ID, event, ); err != nil { return nil, err } case types.EventTypeWorkflowExecutionFailed: if err := b.mutableState.ReplicateWorkflowExecutionFailedEvent( firstEvent.ID, event, ); err != nil { return nil, err } case types.EventTypeWorkflowExecutionTimedOut: if err := b.mutableState.ReplicateWorkflowExecutionTimedoutEvent( firstEvent.ID, event, ); err != nil { return nil, err } case types.EventTypeWorkflowExecutionCanceled: if err := b.mutableState.ReplicateWorkflowExecutionCanceledEvent( firstEvent.ID, event, ); err != nil { return nil, err } case types.EventTypeWorkflowExecutionTerminated: if err := b.mutableState.ReplicateWorkflowExecutionTerminatedEvent( firstEvent.ID, event, ); err != nil { return nil, err } case types.EventTypeWorkflowExecutionContinuedAsNew: // The length of newRunHistory can be zero in resend case if len(newRunHistory) != 0 { domainEntry := b.mutableState.GetDomainEntry() newRunMutableStateBuilder = NewMutableStateBuilderWithVersionHistories( b.shard, b.logger, domainEntry, constants.EmptyVersion, ) newRunStateBuilder := NewStateBuilder(b.shard, b.logger, newRunMutableStateBuilder) newRunID := event.WorkflowExecutionContinuedAsNewEventAttributes.GetNewExecutionRunID() newExecution := types.WorkflowExecution{ WorkflowID: workflowExecution.WorkflowID, RunID: newRunID, } _, err := newRunStateBuilder.ApplyEvents( domainID, uuid.New(), newExecution, newRunHistory, nil, ) if err != nil { return nil, err } } b.logger.Debug("stateBuilderImpl calling ReplicateWorkflowExecutionContinuedAsNewEvent", tag.WorkflowDomainID(domainID), tag.WorkflowID(workflowExecution.WorkflowID), tag.WorkflowRunID(workflowExecution.RunID), tag.Dynamic("newRunHistorySize", len(newRunHistory)), tag.Dynamic("activecluster-sel-policy-nil", event.WorkflowExecutionContinuedAsNewEventAttributes.ActiveClusterSelectionPolicy == nil), ) err := b.mutableState.ReplicateWorkflowExecutionContinuedAsNewEvent( firstEvent.ID, domainID, event, ) if err != nil { return nil, err } default: return nil, &types.BadRequestError{Message: "Unknown event type"} } b.logger.Debugf("Applied event %v of %v for domain %s, wfID %v, event [id:%v, version:%v]", i+1, historyLength, domainID, workflowExecution.WorkflowID, event.ID, event.Version) } b.mutableState.GetExecutionInfo().SetLastFirstEventID(firstEvent.ID) b.mutableState.GetExecutionInfo().SetNextEventID(lastEvent.ID + 1) b.mutableState.SetHistoryBuilder(NewHistoryBuilderFromEvents(history, b.mutableState)) return newRunMutableStateBuilder, nil } func (b *stateBuilderImpl) GetMutableState() MutableState { return b.mutableState } func (b *stateBuilderImpl) unixNanoToTime( unixNano int64, ) time.Time { return time.Unix(0, unixNano) } ================================================ FILE: service/history/execution/state_builder_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: state_builder.go // // Generated by this command: // // mockgen -package execution -source state_builder.go -destination state_builder_mock.go -self_package github.com/uber/cadence/service/history/execution // // Package execution is a generated GoMock package. package execution import ( reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockStateBuilder is a mock of StateBuilder interface. type MockStateBuilder struct { ctrl *gomock.Controller recorder *MockStateBuilderMockRecorder isgomock struct{} } // MockStateBuilderMockRecorder is the mock recorder for MockStateBuilder. type MockStateBuilderMockRecorder struct { mock *MockStateBuilder } // NewMockStateBuilder creates a new mock instance. func NewMockStateBuilder(ctrl *gomock.Controller) *MockStateBuilder { mock := &MockStateBuilder{ctrl: ctrl} mock.recorder = &MockStateBuilderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStateBuilder) EXPECT() *MockStateBuilderMockRecorder { return m.recorder } // ApplyEvents mocks base method. func (m *MockStateBuilder) ApplyEvents(domainID, requestID string, workflowExecution types.WorkflowExecution, history, newRunHistory []*types.HistoryEvent) (MutableState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ApplyEvents", domainID, requestID, workflowExecution, history, newRunHistory) ret0, _ := ret[0].(MutableState) ret1, _ := ret[1].(error) return ret0, ret1 } // ApplyEvents indicates an expected call of ApplyEvents. func (mr *MockStateBuilderMockRecorder) ApplyEvents(domainID, requestID, workflowExecution, history, newRunHistory any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyEvents", reflect.TypeOf((*MockStateBuilder)(nil).ApplyEvents), domainID, requestID, workflowExecution, history, newRunHistory) } // GetMutableState mocks base method. func (m *MockStateBuilder) GetMutableState() MutableState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMutableState") ret0, _ := ret[0].(MutableState) return ret0 } // GetMutableState indicates an expected call of GetMutableState. func (mr *MockStateBuilderMockRecorder) GetMutableState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMutableState", reflect.TypeOf((*MockStateBuilder)(nil).GetMutableState)) } ================================================ FILE: service/history/execution/state_builder_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/shard" ) type ( stateBuilderSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockEventsCache *events.MockCache mockDomainCache *cache.MockDomainCache mockMutableState *MockMutableState logger log.Logger sourceCluster string stateBuilder *stateBuilderImpl } ) func TestStateBuilderSuite(t *testing.T) { s := new(stateBuilderSuite) suite.Run(t, s) } func (s *stateBuilderSuite) SetupSuite() { } func (s *stateBuilderSuite) TearDownSuite() { } func (s *stateBuilderSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockMutableState = NewMockMutableState(s.controller) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockEventsCache = s.mockShard.MockEventsCache s.mockEventsCache.EXPECT().PutEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() s.logger = s.mockShard.GetLogger() s.mockMutableState.EXPECT().GetVersionHistories().Return(persistence.NewVersionHistories(&persistence.VersionHistory{})).AnyTimes() s.stateBuilder = NewStateBuilder( s.mockShard, s.logger, s.mockMutableState, ).(*stateBuilderImpl) s.sourceCluster = "some random source cluster" } func (s *stateBuilderSuite) TearDownTest() { s.stateBuilder = nil s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *stateBuilderSuite) mockUpdateVersion(events ...*types.HistoryEvent) { for _, event := range events { s.mockMutableState.EXPECT().UpdateCurrentVersion(event.Version, true).Times(1) } s.mockMutableState.EXPECT().SetHistoryBuilder(NewHistoryBuilderFromEvents(events, s.mockMutableState)).Times(1) } func (s *stateBuilderSuite) toHistory(events ...*types.HistoryEvent) []*types.HistoryEvent { return events } // workflow operations func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionStarted_NoCronSchedule() { cronSchedule := "" version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } executionInfo := &persistence.WorkflowExecutionInfo{ WorkflowTimeout: 100, CronSchedule: cronSchedule, } now := time.Now() evenType := types.EventTypeWorkflowExecutionStarted startWorkflowAttribute := &types.WorkflowExecutionStartedEventAttributes{ ParentWorkflowDomain: common.StringPtr(constants.TestDomainName), } event := &types.HistoryEvent{ Version: version, ID: 1, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, WorkflowExecutionStartedEventAttributes: startWorkflowAttribute, } s.mockDomainCache.EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestGlobalParentDomainEntry, nil).Times(1) s.mockMutableState.EXPECT().ReplicateWorkflowExecutionStartedEvent(&constants.TestDomainID, workflowExecution, requestID, event, true).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) s.mockMutableState.EXPECT().SetHistoryTree(constants.TestRunID).Return(nil).Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionStarted_WithCronSchedule() { cronSchedule := "* * * * *" parsedSchedule, err := backoff.ValidateSchedule(cronSchedule) require.NoError(s.T(), err) version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } executionInfo := &persistence.WorkflowExecutionInfo{ WorkflowTimeout: 100, CronSchedule: cronSchedule, } now := time.Now() evenType := types.EventTypeWorkflowExecutionStarted next, err := backoff.GetBackoffForNextSchedule(parsedSchedule, now, now, 0, types.CronOverlapPolicySkipped) require.NoError(s.T(), err) startWorkflowAttribute := &types.WorkflowExecutionStartedEventAttributes{ ParentWorkflowDomainID: common.StringPtr(constants.TestDomainID), ParentWorkflowDomain: common.StringPtr(constants.TestDomainName), Initiator: types.ContinueAsNewInitiatorCronSchedule.Ptr(), FirstDecisionTaskBackoffSeconds: common.Int32Ptr( int32(next.Seconds()), ), } event := &types.HistoryEvent{ Version: version, ID: 1, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, WorkflowExecutionStartedEventAttributes: startWorkflowAttribute, } s.mockDomainCache.EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestGlobalParentDomainEntry, nil).Times(0) s.mockMutableState.EXPECT().ReplicateWorkflowExecutionStartedEvent(&constants.TestDomainID, workflowExecution, requestID, event, true).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) s.mockMutableState.EXPECT().SetHistoryTree(constants.TestRunID).Return(nil).Times(1) _, err = s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionTimedOut() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeWorkflowExecutionTimedOut event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, WorkflowExecutionTimedOutEventAttributes: &types.WorkflowExecutionTimedOutEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateWorkflowExecutionTimedoutEvent(event.ID, event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().GetDomainEntry().Return(constants.TestGlobalDomainEntry).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionTerminated() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeWorkflowExecutionTerminated event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, WorkflowExecutionTerminatedEventAttributes: &types.WorkflowExecutionTerminatedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateWorkflowExecutionTerminatedEvent(event.ID, event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().GetDomainEntry().Return(constants.TestGlobalDomainEntry).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionFailed() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeWorkflowExecutionFailed event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, WorkflowExecutionFailedEventAttributes: &types.WorkflowExecutionFailedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateWorkflowExecutionFailedEvent(event.ID, event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().GetDomainEntry().Return(constants.TestGlobalDomainEntry).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionCompleted() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeWorkflowExecutionCompleted event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, WorkflowExecutionCompletedEventAttributes: &types.WorkflowExecutionCompletedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateWorkflowExecutionCompletedEvent(event.ID, event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().GetDomainEntry().Return(constants.TestGlobalDomainEntry).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionCanceled() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeWorkflowExecutionCanceled event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, WorkflowExecutionCanceledEventAttributes: &types.WorkflowExecutionCanceledEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateWorkflowExecutionCanceledEvent(event.ID, event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetDomainEntry().Return(constants.TestGlobalDomainEntry).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionContinuedAsNew() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } parentWorkflowID := "some random parent workflow ID" parentRunID := uuid.New() parentInitiatedEventID := int64(144) now := time.Now() tasklist := "some random tasklist" workflowType := "some random workflow type" workflowTimeoutSecond := int32(110) decisionTimeoutSecond := int32(11) newRunID := uuid.New() continueAsNewEvent := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeWorkflowExecutionContinuedAsNew.Ptr(), WorkflowExecutionContinuedAsNewEventAttributes: &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: newRunID, }, } newRunStartedEvent := &types.HistoryEvent{ Version: version, ID: 1, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ ParentWorkflowDomainID: common.StringPtr(constants.TestDomainID), ParentWorkflowDomain: common.StringPtr(constants.TestDomainName), ParentWorkflowExecution: &types.WorkflowExecution{ WorkflowID: parentWorkflowID, RunID: parentRunID, }, ParentInitiatedEventID: common.Int64Ptr(parentInitiatedEventID), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(workflowTimeoutSecond), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(decisionTimeoutSecond), TaskList: &types.TaskList{Name: tasklist}, WorkflowType: &types.WorkflowType{Name: workflowType}, }, } newRunSignalEvent := &types.HistoryEvent{ Version: version, ID: 2, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some random signal name", Input: []byte("some random signal input"), Identity: "some random identity", }, } newRunDecisionAttempt := int64(123) newRunDecisionEvent := &types.HistoryEvent{ Version: version, ID: 3, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(decisionTimeoutSecond), Attempt: newRunDecisionAttempt, }, } newRunEvents := []*types.HistoryEvent{ newRunStartedEvent, newRunSignalEvent, newRunDecisionEvent, } s.mockMutableState.EXPECT().ReplicateWorkflowExecutionContinuedAsNewEvent( continueAsNewEvent.ID, constants.TestDomainID, continueAsNewEvent, ).Return(nil).Times(1) s.mockMutableState.EXPECT().GetDomainEntry().Return(constants.TestGlobalDomainEntry).AnyTimes() s.mockUpdateVersion(continueAsNewEvent) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) // new workflow domain s.mockDomainCache.EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestGlobalParentDomainEntry, nil).AnyTimes() newRunStateBuilder, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(continueAsNewEvent), newRunEvents) s.Nil(err) s.NotNil(newRunStateBuilder) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionContinuedAsNew_EmptyNewRunHistory() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() newRunID := uuid.New() continueAsNewEvent := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: types.EventTypeWorkflowExecutionContinuedAsNew.Ptr(), WorkflowExecutionContinuedAsNewEventAttributes: &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: newRunID, }, } s.mockMutableState.EXPECT().ReplicateWorkflowExecutionContinuedAsNewEvent( continueAsNewEvent.ID, constants.TestDomainID, continueAsNewEvent, ).Return(nil).Times(1) s.mockMutableState.EXPECT().GetDomainEntry().Return(constants.TestGlobalDomainEntry).AnyTimes() s.mockUpdateVersion(continueAsNewEvent) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) // new workflow domain s.mockDomainCache.EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestGlobalParentDomainEntry, nil).AnyTimes() newRunStateBuilder, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(continueAsNewEvent), nil) s.Nil(err) s.Nil(newRunStateBuilder) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionSignaled() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeWorkflowExecutionSignaled event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{}, } s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ReplicateWorkflowExecutionSignaled(event).Return(nil).Times(1) s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeWorkflowExecutionCancelRequested() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeWorkflowExecutionCancelRequested event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, WorkflowExecutionCancelRequestedEventAttributes: &types.WorkflowExecutionCancelRequestedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateWorkflowExecutionCancelRequestedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeUpsertWorkflowSearchAttributes() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeUpsertWorkflowSearchAttributes event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, UpsertWorkflowSearchAttributesEventAttributes: &types.UpsertWorkflowSearchAttributesEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateUpsertWorkflowSearchAttributesEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeMarkerRecorded() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeMarkerRecorded event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, MarkerRecordedEventAttributes: &types.MarkerRecordedEventAttributes{}, } s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } // decision operations func (s *stateBuilderSuite) TestApplyEvents_EventTypeDecisionTaskScheduled() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() tasklist := "some random tasklist" timeoutSecond := int32(11) evenType := types.EventTypeDecisionTaskScheduled decisionAttempt := int64(111) event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ TaskList: &types.TaskList{Name: tasklist}, StartToCloseTimeoutSeconds: common.Int32Ptr(timeoutSecond), Attempt: decisionAttempt, }, } di := &DecisionInfo{ Version: event.Version, ScheduleID: event.ID, StartedID: commonconstants.EmptyEventID, RequestID: commonconstants.EmptyUUID, DecisionTimeout: timeoutSecond, TaskList: tasklist, Attempt: decisionAttempt, } executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: tasklist, } s.mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo).AnyTimes() s.mockMutableState.EXPECT().ReplicateDecisionTaskScheduledEvent( event.Version, event.ID, tasklist, timeoutSecond, decisionAttempt, event.GetTimestamp(), event.GetTimestamp(), false, ).Return(di, nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeDecisionTaskStarted() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() tasklist := "some random tasklist" timeoutSecond := int32(11) scheduleID := int64(111) decisionRequestID := uuid.New() evenType := types.EventTypeDecisionTaskStarted event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{ ScheduledEventID: scheduleID, RequestID: decisionRequestID, }, } di := &DecisionInfo{ Version: event.Version, ScheduleID: scheduleID, StartedID: event.ID, RequestID: decisionRequestID, DecisionTimeout: timeoutSecond, TaskList: tasklist, Attempt: 0, } s.mockMutableState.EXPECT().ReplicateDecisionTaskStartedEvent( (*DecisionInfo)(nil), event.Version, scheduleID, event.ID, decisionRequestID, event.GetTimestamp(), ).Return(di, nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeDecisionTaskTimedOut() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() scheduleID := int64(12) startedID := int64(28) evenType := types.EventTypeDecisionTaskTimedOut event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: scheduleID, StartedEventID: startedID, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, } s.mockMutableState.EXPECT().ReplicateDecisionTaskTimedOutEvent(event).Return(nil).Times(1) tasklist := "some random tasklist" executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: tasklist, } s.mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo).AnyTimes() s.mockMutableState.EXPECT().ReplicateTransientDecisionTaskScheduled().Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeDecisionTaskFailed() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() scheduleID := int64(12) startedID := int64(28) evenType := types.EventTypeDecisionTaskFailed event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ ScheduledEventID: scheduleID, StartedEventID: startedID, }, } s.mockMutableState.EXPECT().ReplicateDecisionTaskFailedEvent(event).Return(nil).Times(1) tasklist := "some random tasklist" executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: tasklist, } s.mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo).AnyTimes() s.mockMutableState.EXPECT().ReplicateTransientDecisionTaskScheduled().Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeDecisionTaskCompleted() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() scheduleID := int64(12) startedID := int64(28) evenType := types.EventTypeDecisionTaskCompleted event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ ScheduledEventID: scheduleID, StartedEventID: startedID, }, } s.mockMutableState.EXPECT().ReplicateDecisionTaskCompletedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } // user timer operations func (s *stateBuilderSuite) TestApplyEvents_EventTypeTimerStarted() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() timerID := "timer ID" timeoutSecond := int64(10) evenType := types.EventTypeTimerStarted event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, TimerStartedEventAttributes: &types.TimerStartedEventAttributes{ TimerID: timerID, StartToFireTimeoutSeconds: common.Int64Ptr(timeoutSecond), }, } ti := &persistence.TimerInfo{ Version: event.Version, TimerID: timerID, ExpiryTime: time.Unix(0, event.GetTimestamp()).Add(time.Duration(timeoutSecond) * time.Second), StartedID: event.ID, TaskStatus: TimerTaskStatusNone, } s.mockMutableState.EXPECT().ReplicateTimerStartedEvent(event).Return(ti, nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() // assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // need to be refreshed each time s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeTimerFired() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeTimerFired event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, TimerFiredEventAttributes: &types.TimerFiredEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateTimerFiredEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() // assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // need to be refreshed each time s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeCancelTimerFailed() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeCancelTimerFailed event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, CancelTimerFailedEventAttributes: &types.CancelTimerFailedEventAttributes{}, } s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() // assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // need to be refreshed each time s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeTimerCanceled() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeTimerCanceled event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, TimerCanceledEventAttributes: &types.TimerCanceledEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateTimerCanceledEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() // assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // need to be refreshed each time s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } // activity operations func (s *stateBuilderSuite) TestApplyEvents_EventTypeActivityTaskScheduled() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() activityID := "activity ID" tasklist := "some random tasklist" timeoutSecond := int32(10) evenType := types.EventTypeActivityTaskScheduled event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{}, } ai := &persistence.ActivityInfo{ Version: event.Version, ScheduleID: event.ID, ScheduledEventBatchID: event.ID, ScheduledEvent: event, ScheduledTime: time.Unix(0, event.GetTimestamp()), StartedID: commonconstants.EmptyEventID, StartedTime: time.Time{}, ActivityID: activityID, ScheduleToStartTimeout: timeoutSecond, ScheduleToCloseTimeout: timeoutSecond, StartToCloseTimeout: timeoutSecond, HeartbeatTimeout: timeoutSecond, CancelRequested: false, CancelRequestID: commonconstants.EmptyEventID, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusNone, TaskList: tasklist, } executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: tasklist, } s.mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo).AnyTimes() s.mockMutableState.EXPECT().ReplicateActivityTaskScheduledEvent(event.ID, event, false).Return(ai, nil).Times(1) s.mockUpdateVersion(event) // assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // need to be refreshed each time s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeActivityTaskStarted() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() tasklist := "some random tasklist" evenType := types.EventTypeActivityTaskScheduled scheduledEvent := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{}, } evenType = types.EventTypeActivityTaskStarted startedEvent := &types.HistoryEvent{ Version: version, ID: scheduledEvent.ID + 1, Timestamp: common.Int64Ptr(scheduledEvent.GetTimestamp() + 1000), EventType: &evenType, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{}, } executionInfo := &persistence.WorkflowExecutionInfo{ TaskList: tasklist, } s.mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo).AnyTimes() s.mockMutableState.EXPECT().ReplicateActivityTaskStartedEvent(startedEvent).Return(nil).Times(1) s.mockUpdateVersion(startedEvent) // assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // need to be refreshed each time s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(startedEvent), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeActivityTaskTimedOut() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeActivityTaskTimedOut event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateActivityTaskTimedOutEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() // assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // need to be refreshed each time// assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // // need to be refreshed each time s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeActivityTaskFailed() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeActivityTaskFailed event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateActivityTaskFailedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() // assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // need to be refreshed each time s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeActivityTaskCompleted() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeActivityTaskCompleted event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ActivityTaskCompletedEventAttributes: &types.ActivityTaskCompletedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateActivityTaskCompletedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() // assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // need to be refreshed each time s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeActivityTaskCancelRequested() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeActivityTaskCancelRequested event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ActivityTaskCancelRequestedEventAttributes: &types.ActivityTaskCancelRequestedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateActivityTaskCancelRequestedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeRequestCancelActivityTaskFailed() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeRequestCancelActivityTaskFailed event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, RequestCancelActivityTaskFailedEventAttributes: &types.RequestCancelActivityTaskFailedEventAttributes{}, } s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeActivityTaskCanceled() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeActivityTaskCanceled event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ActivityTaskCanceledEventAttributes: &types.ActivityTaskCanceledEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateActivityTaskCanceledEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() // assertion on timer generated is in `mockUpdateVersion` function, since activity / user timer // need to be refreshed each time s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } // child workflow operations func (s *stateBuilderSuite) TestApplyEvents_EventTypeStartChildWorkflowExecutionInitiated() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } targetWorkflowID := "some random target workflow ID" now := time.Now() createRequestID := uuid.New() evenType := types.EventTypeStartChildWorkflowExecutionInitiated event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, StartChildWorkflowExecutionInitiatedEventAttributes: &types.StartChildWorkflowExecutionInitiatedEventAttributes{ Domain: constants.TestDomainName, WorkflowID: targetWorkflowID, }, } ci := &persistence.ChildExecutionInfo{ Version: event.Version, InitiatedID: event.ID, InitiatedEventBatchID: event.ID, StartedID: commonconstants.EmptyEventID, CreateRequestID: createRequestID, DomainID: constants.TestDomainID, DomainNameDEPRECATED: constants.TestDomainName, } // the create request ID is generated inside, cannot assert equal s.mockMutableState.EXPECT().ReplicateStartChildWorkflowExecutionInitiatedEvent( event.ID, event, gomock.Any(), ).Return(ci, nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeStartChildWorkflowExecutionFailed() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeStartChildWorkflowExecutionFailed event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, StartChildWorkflowExecutionFailedEventAttributes: &types.StartChildWorkflowExecutionFailedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateStartChildWorkflowExecutionFailedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeChildWorkflowExecutionStarted() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeChildWorkflowExecutionStarted event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateChildWorkflowExecutionStartedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeChildWorkflowExecutionTimedOut() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeChildWorkflowExecutionTimedOut event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ChildWorkflowExecutionTimedOutEventAttributes: &types.ChildWorkflowExecutionTimedOutEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateChildWorkflowExecutionTimedOutEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeChildWorkflowExecutionTerminated() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeChildWorkflowExecutionTerminated event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ChildWorkflowExecutionTerminatedEventAttributes: &types.ChildWorkflowExecutionTerminatedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateChildWorkflowExecutionTerminatedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeChildWorkflowExecutionFailed() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeChildWorkflowExecutionFailed event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ChildWorkflowExecutionFailedEventAttributes: &types.ChildWorkflowExecutionFailedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateChildWorkflowExecutionFailedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeChildWorkflowExecutionCompleted() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeChildWorkflowExecutionCompleted event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ChildWorkflowExecutionCompletedEventAttributes: &types.ChildWorkflowExecutionCompletedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateChildWorkflowExecutionCompletedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } // cancel external workflow operations func (s *stateBuilderSuite) TestApplyEvents_EventTypeRequestCancelExternalWorkflowExecutionInitiated() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } targetWorkflowID := "some random target workflow ID" targetRunID := uuid.New() childWorkflowOnly := true now := time.Now() cancellationRequestID := uuid.New() control := []byte("some random control") evenType := types.EventTypeRequestCancelExternalWorkflowExecutionInitiated event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, RequestCancelExternalWorkflowExecutionInitiatedEventAttributes: &types.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: targetWorkflowID, RunID: targetRunID, }, ChildWorkflowOnly: childWorkflowOnly, Control: control, }, } rci := &persistence.RequestCancelInfo{ Version: event.Version, InitiatedID: event.ID, CancelRequestID: cancellationRequestID, } // the cancellation request ID is generated inside, cannot assert equal s.mockMutableState.EXPECT().ReplicateRequestCancelExternalWorkflowExecutionInitiatedEvent( event.ID, event, gomock.Any(), ).Return(rci, nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeRequestCancelExternalWorkflowExecutionFailed() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeRequestCancelExternalWorkflowExecutionFailed event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, RequestCancelExternalWorkflowExecutionFailedEventAttributes: &types.RequestCancelExternalWorkflowExecutionFailedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateRequestCancelExternalWorkflowExecutionFailedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeExternalWorkflowExecutionCancelRequested() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeExternalWorkflowExecutionCancelRequested event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ExternalWorkflowExecutionCancelRequestedEventAttributes: &types.ExternalWorkflowExecutionCancelRequestedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateExternalWorkflowExecutionCancelRequested(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeChildWorkflowExecutionCanceled() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeChildWorkflowExecutionCanceled event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ChildWorkflowExecutionCanceledEventAttributes: &types.ChildWorkflowExecutionCanceledEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateChildWorkflowExecutionCanceledEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } // signal external workflow operations func (s *stateBuilderSuite) TestApplyEvents_EventTypeSignalExternalWorkflowExecutionInitiated() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } targetWorkflowID := "some random target workflow ID" targetRunID := uuid.New() childWorkflowOnly := true now := time.Now() signalRequestID := uuid.New() signalName := "some random signal name" signalInput := []byte("some random signal input") control := []byte("some random control") evenType := types.EventTypeSignalExternalWorkflowExecutionInitiated event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, SignalExternalWorkflowExecutionInitiatedEventAttributes: &types.SignalExternalWorkflowExecutionInitiatedEventAttributes{ Domain: constants.TestDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: targetWorkflowID, RunID: targetRunID, }, SignalName: signalName, Input: signalInput, ChildWorkflowOnly: childWorkflowOnly, }, } si := &persistence.SignalInfo{ Version: event.Version, InitiatedID: event.ID, SignalRequestID: signalRequestID, SignalName: signalName, Input: signalInput, Control: control, } // the cancellation request ID is generated inside, cannot assert equal s.mockMutableState.EXPECT().ReplicateSignalExternalWorkflowExecutionInitiatedEvent( event.ID, event, gomock.Any(), ).Return(si, nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeSignalExternalWorkflowExecutionFailed() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeSignalExternalWorkflowExecutionFailed event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, SignalExternalWorkflowExecutionFailedEventAttributes: &types.SignalExternalWorkflowExecutionFailedEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateSignalExternalWorkflowExecutionFailedEvent(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEvents_EventTypeExternalWorkflowExecutionSignaled() { version := int64(1) requestID := uuid.New() workflowExecution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: constants.TestRunID, } now := time.Now() evenType := types.EventTypeExternalWorkflowExecutionSignaled event := &types.HistoryEvent{ Version: version, ID: 130, Timestamp: common.Int64Ptr(now.UnixNano()), EventType: &evenType, ExternalWorkflowExecutionSignaledEventAttributes: &types.ExternalWorkflowExecutionSignaledEventAttributes{}, } s.mockMutableState.EXPECT().ReplicateExternalWorkflowExecutionSignaled(event).Return(nil).Times(1) s.mockUpdateVersion(event) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().ClearStickyness().Times(1) _, err := s.stateBuilder.ApplyEvents(constants.TestDomainID, requestID, workflowExecution, s.toHistory(event), nil) s.Nil(err) } func (s *stateBuilderSuite) TestApplyEventsNewEventsNotHandled() { eventTypes := types.EventTypeValues() s.Equal(42, len(eventTypes), "If you see this error, you are adding new event type. "+ "Before updating the number to make this test pass, please make sure you update stateBuilderImpl.ApplyEvents method "+ "to handle the new decision type. Otherwise cross dc will not work on the new event.") } ================================================ FILE: service/history/execution/state_rebuilder.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination state_rebuilder_mock.go -self_package github.com/uber/cadence/service/history/execution package execution import ( "context" "fmt" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" persistenceutils "github.com/uber/cadence/common/persistence/persistence-utils" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/shard" ) const ( // NDCDefaultPageSize is the default pagination size for ndc NDCDefaultPageSize = 100 ) type ( // StateRebuilder is a mutable state builder to ndc state rebuild StateRebuilder interface { Rebuild( ctx context.Context, now time.Time, baseWorkflowIdentifier definition.WorkflowIdentifier, baseBranchToken []byte, baseLastEventID int64, baseLastEventVersion int64, targetWorkflowIdentifier definition.WorkflowIdentifier, targetBranchFn func() ([]byte, error), requestID string, ) (MutableState, int64, error) } stateRebuilderImpl struct { shard shard.Context domainCache cache.DomainCache clusterMetadata cluster.Metadata historyV2Mgr persistence.HistoryManager taskRefresher MutableStateTaskRefresher rebuiltHistorySize int64 logger log.Logger } ) var _ StateRebuilder = (*stateRebuilderImpl)(nil) // NewStateRebuilder creates a state rebuilder func NewStateRebuilder( shard shard.Context, logger log.Logger, ) StateRebuilder { return &stateRebuilderImpl{ shard: shard, domainCache: shard.GetDomainCache(), clusterMetadata: shard.GetService().GetClusterMetadata(), historyV2Mgr: shard.GetHistoryManager(), taskRefresher: NewMutableStateTaskRefresher( shard.GetConfig(), shard.GetClusterMetadata(), shard.GetDomainCache(), shard.GetEventsCache(), shard.GetShardID(), logger, ), rebuiltHistorySize: 0, logger: logger, } } func (r *stateRebuilderImpl) Rebuild( ctx context.Context, now time.Time, baseWorkflowIdentifier definition.WorkflowIdentifier, baseBranchToken []byte, baseLastEventID int64, baseLastEventVersion int64, targetWorkflowIdentifier definition.WorkflowIdentifier, targetBranchFn func() ([]byte, error), requestID string, ) (MutableState, int64, error) { iter := collection.NewPagingIterator(r.getPaginationFn( ctx, constants.FirstEventID, baseLastEventID+1, baseBranchToken, targetWorkflowIdentifier.DomainID, )) domainEntry, err := r.domainCache.GetDomainByID(targetWorkflowIdentifier.DomainID) if err != nil { return nil, 0, err } // Corrupt data handling if !iter.HasNext() { return nil, 0, fmt.Errorf("Attempting to build history state but the iterator has found no history") } // need to specially handling the first batch, to initialize mutable state & state builder batch, err := iter.Next() if err != nil { return nil, 0, err } firstEventBatch := batch.(*types.History).Events rebuiltMutableState, stateBuilder := r.initializeBuilders( domainEntry, ) if err := r.applyEvents(targetWorkflowIdentifier, stateBuilder, firstEventBatch, requestID); err != nil { return nil, 0, err } for iter.HasNext() { batch, err := iter.Next() if err != nil { return nil, 0, err } events := batch.(*types.History).Events if err := r.applyEvents(targetWorkflowIdentifier, stateBuilder, events, requestID); err != nil { return nil, 0, err } } rebuildVersionHistories := rebuiltMutableState.GetVersionHistories() if rebuildVersionHistories != nil { currentVersionHistory, err := rebuildVersionHistories.GetCurrentVersionHistory() if err != nil { return nil, 0, err } lastItem, err := currentVersionHistory.GetLastItem() if err != nil { return nil, 0, err } if !lastItem.Equals(persistence.NewVersionHistoryItem( baseLastEventID, baseLastEventVersion, )) { return nil, 0, &types.BadRequestError{Message: fmt.Sprintf( "nDCStateRebuilder unable to rebuild mutable state to event ID: %v, version: %v, "+ "baseLastEventID + baseLastEventVersion is not the same as the last event of the last "+ "batch, event ID: %v, version :%v ,typically because of attemptting to rebuild to a middle of a batch", baseLastEventID, baseLastEventVersion, lastItem.EventID, lastItem.Version, )} } } targetBranchToken, err := targetBranchFn() if err != nil { return nil, 0, err } if err := rebuiltMutableState.SetCurrentBranchToken(targetBranchToken); err != nil { return nil, 0, err } // close rebuilt mutable state transaction clearing all generated tasks, workflow requests, etc. _, _, err = rebuiltMutableState.CloseTransactionAsSnapshot(now, TransactionPolicyPassive) if err != nil { return nil, 0, err } // refresh tasks to be generated if err := r.taskRefresher.RefreshTasks(ctx, now, rebuiltMutableState); err != nil { return nil, 0, err } // mutable state rebuild should use the same time stamp rebuiltMutableState.GetExecutionInfo().StartTimestamp = now return rebuiltMutableState, r.rebuiltHistorySize, nil } func (r *stateRebuilderImpl) initializeBuilders( domainEntry *cache.DomainCacheEntry, ) (MutableState, StateBuilder) { resetMutableStateBuilder := NewMutableStateBuilderWithVersionHistories( r.shard, r.logger, domainEntry, constants.EmptyVersion, ) stateBuilder := NewStateBuilder( r.shard, r.logger, resetMutableStateBuilder, ) return resetMutableStateBuilder, stateBuilder } func (r *stateRebuilderImpl) applyEvents( workflowIdentifier definition.WorkflowIdentifier, stateBuilder StateBuilder, events []*types.HistoryEvent, requestID string, ) error { _, err := stateBuilder.ApplyEvents( workflowIdentifier.DomainID, requestID, types.WorkflowExecution{ WorkflowID: workflowIdentifier.WorkflowID, RunID: workflowIdentifier.RunID, }, events, nil, // no new run history when rebuilding mutable state ) if err != nil { r.logger.Error("nDCStateRebuilder unable to rebuild mutable state.", tag.Error(err)) } return err } func (r *stateRebuilderImpl) getPaginationFn( ctx context.Context, firstEventID int64, nextEventID int64, branchToken []byte, domainID string, ) collection.PaginationFn { return func(paginationToken []byte) ([]interface{}, []byte, error) { _, historyBatches, token, size, err := persistenceutils.PaginateHistory( ctx, r.historyV2Mgr, true, branchToken, firstEventID, nextEventID, paginationToken, NDCDefaultPageSize, common.IntPtr(r.shard.GetShardID()), domainID, r.domainCache, ) if err != nil { return nil, nil, err } r.rebuiltHistorySize += int64(size) var paginateItems []interface{} for _, history := range historyBatches { paginateItems = append(paginateItems, history) } return paginateItems, token, nil } } ================================================ FILE: service/history/execution/state_rebuilder_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: state_rebuilder.go // // Generated by this command: // // mockgen -package execution -source state_rebuilder.go -destination state_rebuilder_mock.go -self_package github.com/uber/cadence/service/history/execution // // Package execution is a generated GoMock package. package execution import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" definition "github.com/uber/cadence/common/definition" ) // MockStateRebuilder is a mock of StateRebuilder interface. type MockStateRebuilder struct { ctrl *gomock.Controller recorder *MockStateRebuilderMockRecorder isgomock struct{} } // MockStateRebuilderMockRecorder is the mock recorder for MockStateRebuilder. type MockStateRebuilderMockRecorder struct { mock *MockStateRebuilder } // NewMockStateRebuilder creates a new mock instance. func NewMockStateRebuilder(ctrl *gomock.Controller) *MockStateRebuilder { mock := &MockStateRebuilder{ctrl: ctrl} mock.recorder = &MockStateRebuilderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStateRebuilder) EXPECT() *MockStateRebuilderMockRecorder { return m.recorder } // Rebuild mocks base method. func (m *MockStateRebuilder) Rebuild(ctx context.Context, now time.Time, baseWorkflowIdentifier definition.WorkflowIdentifier, baseBranchToken []byte, baseLastEventID, baseLastEventVersion int64, targetWorkflowIdentifier definition.WorkflowIdentifier, targetBranchFn func() ([]byte, error), requestID string) (MutableState, int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Rebuild", ctx, now, baseWorkflowIdentifier, baseBranchToken, baseLastEventID, baseLastEventVersion, targetWorkflowIdentifier, targetBranchFn, requestID) ret0, _ := ret[0].(MutableState) ret1, _ := ret[1].(int64) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // Rebuild indicates an expected call of Rebuild. func (mr *MockStateRebuilderMockRecorder) Rebuild(ctx, now, baseWorkflowIdentifier, baseBranchToken, baseLastEventID, baseLastEventVersion, targetWorkflowIdentifier, targetBranchFn, requestID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rebuild", reflect.TypeOf((*MockStateRebuilder)(nil).Rebuild), ctx, now, baseWorkflowIdentifier, baseBranchToken, baseLastEventID, baseLastEventVersion, targetWorkflowIdentifier, targetBranchFn, requestID) } ================================================ FILE: service/history/execution/state_rebuilder_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/collection" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/shard" ) type ( stateRebuilderSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockEventsCache *events.MockCache mockTaskRefresher *MockMutableStateTaskRefresher mockDomainCache *cache.MockDomainCache mockActiveClusterManager *activecluster.MockManager mockHistoryV2Mgr *mocks.HistoryV2Manager logger log.Logger domainID string workflowID string runID string nDCStateRebuilder *stateRebuilderImpl } ) func TestStateRebuilderSuite(t *testing.T) { s := new(stateRebuilderSuite) suite.Run(t, s) } func (s *stateRebuilderSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockTaskRefresher = NewMockMutableStateTaskRefresher(s.controller) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.mockHistoryV2Mgr = s.mockShard.Resource.HistoryMgr s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockEventsCache = s.mockShard.MockEventsCache s.mockEventsCache.EXPECT().PutEvent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() s.logger = s.mockShard.GetLogger() s.workflowID = "some random workflow ID" s.runID = uuid.New() s.nDCStateRebuilder = NewStateRebuilder( s.mockShard, s.logger, ).(*stateRebuilderImpl) s.nDCStateRebuilder.taskRefresher = s.mockTaskRefresher } func (s *stateRebuilderSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *stateRebuilderSuite) TestInitializeBuilders() { mutableState, stateBuilder := s.nDCStateRebuilder.initializeBuilders(constants.TestGlobalDomainEntry) s.NotNil(mutableState) s.NotNil(stateBuilder) s.NotNil(mutableState.GetVersionHistories()) } func (s *stateRebuilderSuite) TestApplyEvents() { requestID := uuid.New() events := []*types.HistoryEvent{ { ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, }, { ID: 2, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{}, }, } workflowIdentifier := definition.NewWorkflowIdentifier(s.domainID, s.workflowID, s.runID) mockStateBuilder := NewMockStateBuilder(s.controller) mockStateBuilder.EXPECT().ApplyEvents( s.domainID, requestID, types.WorkflowExecution{ WorkflowID: s.workflowID, RunID: s.runID, }, events, []*types.HistoryEvent(nil), ).Return(nil, nil).Times(1) err := s.nDCStateRebuilder.applyEvents(workflowIdentifier, mockStateBuilder, events, requestID) s.NoError(err) } func (s *stateRebuilderSuite) TestPagination() { firstEventID := commonconstants.FirstEventID nextEventID := int64(101) branchToken := []byte("some random branch token") domainName := "some random domain name" event1 := &types.HistoryEvent{ ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, } event2 := &types.HistoryEvent{ ID: 2, DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, } event3 := &types.HistoryEvent{ ID: 3, DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{}, } event4 := &types.HistoryEvent{ ID: 4, DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{}, } event5 := &types.HistoryEvent{ ID: 5, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{}, } history1 := []*types.History{{Events: []*types.HistoryEvent{event1, event2, event3}}} history2 := []*types.History{{Events: []*types.HistoryEvent{event4, event5}}} history := append(history1, history2...) pageToken := []byte("some random token") s.mockDomainCache.EXPECT().GetDomainName(s.domainID).Return(domainName, nil).AnyTimes() s.mockHistoryV2Mgr.On("ReadHistoryBranchByBatch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: NDCDefaultPageSize, NextPageToken: nil, ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }).Return(&persistence.ReadHistoryBranchByBatchResponse{ History: history1, NextPageToken: pageToken, Size: 12345, }, nil).Once() s.mockHistoryV2Mgr.On("ReadHistoryBranchByBatch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: NDCDefaultPageSize, NextPageToken: pageToken, ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }).Return(&persistence.ReadHistoryBranchByBatchResponse{ History: history2, NextPageToken: nil, Size: 67890, }, nil).Once() paginationFn := s.nDCStateRebuilder.getPaginationFn(context.Background(), firstEventID, nextEventID, branchToken, s.domainID) iter := collection.NewPagingIterator(paginationFn) result := []*types.History{} for iter.HasNext() { item, err := iter.Next() s.NoError(err) result = append(result, item.(*types.History)) } s.Equal(history, result) } func (s *stateRebuilderSuite) TestRebuild() { requestID := uuid.New() version := int64(12) lastEventID := int64(2) branchToken := []byte("other random branch token") targetBranchToken := []byte("some other random branch token") now := time.Now() partitionConfig := map[string]string{ "userid": uuid.New(), } targetDomainID := uuid.New() targetDomainName := "other random domain name" targetWorkflowID := "other random workflow ID" targetRunID := uuid.New() firstEventID := commonconstants.FirstEventID nextEventID := lastEventID + 1 events1 := []*types.HistoryEvent{{ ID: 1, Version: version, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: "some random workflow type"}, TaskList: &types.TaskList{Name: "some random workflow type"}, Input: []byte("some random input"), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(123), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(233), Identity: "some random identity", PartitionConfig: partitionConfig, }, }} events2 := []*types.HistoryEvent{{ ID: 2, Version: version, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some random signal name", Input: []byte("some random signal input"), Identity: "some random identity", }, }} history1 := []*types.History{{Events: events1}} history2 := []*types.History{{Events: events2}} pageToken := []byte("some random pagination token") historySize1 := 12345 historySize2 := 67890 s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(targetDomainName, nil).AnyTimes() s.mockHistoryV2Mgr.On("ReadHistoryBranchByBatch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: NDCDefaultPageSize, NextPageToken: nil, ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: targetDomainName, }).Return(&persistence.ReadHistoryBranchByBatchResponse{ History: history1, NextPageToken: pageToken, Size: historySize1, }, nil).Once() s.mockHistoryV2Mgr.On("ReadHistoryBranchByBatch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: NDCDefaultPageSize, NextPageToken: pageToken, ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: targetDomainName, }).Return(&persistence.ReadHistoryBranchByBatchResponse{ History: history2, NextPageToken: nil, Size: historySize2, }, nil).Once() s.mockDomainCache.EXPECT().GetDomainByID(targetDomainID).Return(cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: targetDomainID, Name: targetDomainName}, &persistence.DomainConfig{}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, ), nil).AnyTimes() s.mockTaskRefresher.EXPECT().RefreshTasks(gomock.Any(), now, gomock.Any()).Return(nil).Times(1) targetBranchTokenFn := func() ([]byte, error) { return targetBranchToken, nil } rebuildMutableState, rebuiltHistorySize, err := s.nDCStateRebuilder.Rebuild( context.Background(), now, definition.NewWorkflowIdentifier(s.domainID, s.workflowID, s.runID), branchToken, lastEventID, version, definition.NewWorkflowIdentifier(targetDomainID, targetWorkflowID, targetRunID), targetBranchTokenFn, requestID, ) s.NoError(err) s.NotNil(rebuildMutableState) rebuildExecutionInfo := rebuildMutableState.GetExecutionInfo() s.Equal(targetDomainID, rebuildExecutionInfo.DomainID) s.Equal(targetWorkflowID, rebuildExecutionInfo.WorkflowID) s.Equal(targetRunID, rebuildExecutionInfo.RunID) s.Equal(partitionConfig, rebuildExecutionInfo.PartitionConfig) s.Equal(int64(historySize1+historySize2), rebuiltHistorySize) s.Equal(persistence.NewVersionHistories( persistence.NewVersionHistory( targetBranchToken, []*persistence.VersionHistoryItem{persistence.NewVersionHistoryItem(lastEventID, version)}, ), ), rebuildMutableState.GetVersionHistories()) s.Equal(rebuildMutableState.GetExecutionInfo().StartTimestamp, now) } func (s *stateRebuilderSuite) TestInvalidStateHandling() { requestID := uuid.New() version := int64(12) lastEventID := int64(2) branchToken := []byte("other random branch token") targetBranchToken := []byte("some other random branch token") now := time.Now() targetDomainID := uuid.New() targetDomainName := "other random domain name" targetWorkflowID := "other random workflow ID" targetRunID := uuid.New() s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(targetDomainName, nil).AnyTimes() s.mockHistoryV2Mgr.On("ReadHistoryBranchByBatch", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() s.mockDomainCache.EXPECT().GetDomainByID(targetDomainID).Return(cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: targetDomainID, Name: targetDomainName}, &persistence.DomainConfig{}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, ), nil).AnyTimes() targetBranchTokenFn := func() ([]byte, error) { return targetBranchToken, nil } s.nDCStateRebuilder.Rebuild( context.Background(), now, definition.NewWorkflowIdentifier(s.domainID, s.workflowID, s.runID), branchToken, lastEventID, version, definition.NewWorkflowIdentifier(targetDomainID, targetWorkflowID, targetRunID), targetBranchTokenFn, requestID, ) } ================================================ FILE: service/history/execution/timer_sequence.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination timer_sequence_mock.go -self_package github.com/uber/cadence/service/history/execution package execution import ( "fmt" "sort" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // TimerType indicates timer type TimerType int32 ) const ( // TimerTypeStartToClose is the timer type for activity startToClose timer TimerTypeStartToClose = TimerType(types.TimeoutTypeStartToClose) // TimerTypeScheduleToStart is the timer type for activity scheduleToStart timer TimerTypeScheduleToStart = TimerType(types.TimeoutTypeScheduleToStart) // TimerTypeScheduleToClose is the timer type for activity scheduleToClose timer TimerTypeScheduleToClose = TimerType(types.TimeoutTypeScheduleToClose) // TimerTypeHeartbeat is the timer type for activity heartbeat timer TimerTypeHeartbeat = TimerType(types.TimeoutTypeHeartbeat) ) const ( // TimerTaskStatusNone indicates activity / user timer task has not been created TimerTaskStatusNone = iota // TimerTaskStatusCreated indicates user timer task has been created TimerTaskStatusCreated ) const ( // TimerTaskStatusCreatedStartToClose indicates activity startToClose timer has been created TimerTaskStatusCreatedStartToClose = 1 << iota // TimerTaskStatusCreatedScheduleToStart indicates activity scheduleToStart timer has been created TimerTaskStatusCreatedScheduleToStart // TimerTaskStatusCreatedScheduleToClose indicates activity scheduleToClose timer has been created TimerTaskStatusCreatedScheduleToClose // TimerTaskStatusCreatedHeartbeat indicates activity heartbeat timer has been created TimerTaskStatusCreatedHeartbeat ) type ( // TimerSequenceID describes user / activity timer and defines an order among timers TimerSequenceID struct { EventID int64 Timestamp time.Time TimerType TimerType TimerCreated bool Attempt int32 } // TimerSequenceIDs is a list of TimerSequenceID TimerSequenceIDs []TimerSequenceID // TimerSequence manages user / activity timer TimerSequence interface { IsExpired(referenceTime time.Time, TimerSequenceID TimerSequenceID) (time.Duration, bool) CreateNextUserTimer() (bool, error) CreateNextActivityTimer() (bool, error) LoadAndSortUserTimers() []TimerSequenceID LoadAndSortActivityTimers() []TimerSequenceID } timerSequenceImpl struct { mutableState MutableState } ) var _ TimerSequence = (*timerSequenceImpl)(nil) // NewTimerSequence creates a new timer sequence func NewTimerSequence( mutableState MutableState, ) TimerSequence { return &timerSequenceImpl{ mutableState: mutableState, } } func (t *timerSequenceImpl) IsExpired( referenceTime time.Time, TimerSequenceID TimerSequenceID, ) (time.Duration, bool) { // Cassandra timestamp resolution is in millisecond // here we do the check in terms of second resolution. timerFireTimeInSecond := TimerSequenceID.Timestamp.Unix() referenceTimeInSecond := referenceTime.Unix() if timerFireTimeInSecond <= referenceTimeInSecond { return time.Duration(referenceTimeInSecond-timerFireTimeInSecond) * time.Second, true } return 0, false } func (t *timerSequenceImpl) CreateNextUserTimer() (bool, error) { sequenceIDs := t.LoadAndSortUserTimers() if len(sequenceIDs) == 0 { return false, nil } firstTimerTask := sequenceIDs[0] // timer has already been created if firstTimerTask.TimerCreated { return false, nil } timerInfo, ok := t.mutableState.GetUserTimerInfoByEventID(firstTimerTask.EventID) if !ok { return false, &types.InternalServiceError{ Message: fmt.Sprintf("unable to load activity info %v", firstTimerTask.EventID), } } // mark timer task mask as indication that timer task is generated // here TaskID is misleading attr, should be called timer created flag or something timerInfo.TaskStatus = TimerTaskStatusCreated if err := t.mutableState.UpdateUserTimer(timerInfo); err != nil { return false, err } executionInfo := t.mutableState.GetExecutionInfo() t.mutableState.AddTimerTasks(&persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: firstTimerTask.Timestamp, Version: t.mutableState.GetCurrentVersion(), }, EventID: firstTimerTask.EventID, TaskList: executionInfo.TaskList, }) return true, nil } func (t *timerSequenceImpl) CreateNextActivityTimer() (bool, error) { sequenceIDs := t.LoadAndSortActivityTimers() if len(sequenceIDs) == 0 { return false, nil } firstTimerTask := sequenceIDs[0] // timer has already been created if firstTimerTask.TimerCreated { return false, nil } activityInfo, ok := t.mutableState.GetActivityInfo(firstTimerTask.EventID) if !ok { return false, &types.InternalServiceError{ Message: fmt.Sprintf("unable to load activity info %v", firstTimerTask.EventID), } } // mark timer task mask as indication that timer task is generated activityInfo.TimerTaskStatus |= TimerTypeToTimerMask(firstTimerTask.TimerType) if firstTimerTask.TimerType == TimerTypeHeartbeat { activityInfo.LastHeartbeatTimeoutVisibilityInSeconds = firstTimerTask.Timestamp.Unix() } if err := t.mutableState.UpdateActivity(activityInfo); err != nil { return false, err } executionInfo := t.mutableState.GetExecutionInfo() t.mutableState.AddTimerTasks(&persistence.ActivityTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: firstTimerTask.Timestamp, Version: t.mutableState.GetCurrentVersion(), }, TimeoutType: int(firstTimerTask.TimerType), EventID: firstTimerTask.EventID, Attempt: int64(firstTimerTask.Attempt), TaskList: activityInfo.TaskList, }) return true, nil } func (t *timerSequenceImpl) LoadAndSortUserTimers() []TimerSequenceID { pendingTimers := t.mutableState.GetPendingTimerInfos() timers := make(TimerSequenceIDs, 0, len(pendingTimers)) for _, timerInfo := range pendingTimers { if sequenceID := t.getUserTimerTimeout( timerInfo, ); sequenceID != nil { timers = append(timers, *sequenceID) } } sort.Sort(timers) return timers } func (t *timerSequenceImpl) LoadAndSortActivityTimers() []TimerSequenceID { // there can be 4 timer per activity // see TimerType pendingActivities := t.mutableState.GetPendingActivityInfos() activityTimers := make(TimerSequenceIDs, 0, len(pendingActivities)*4) for _, activityInfo := range pendingActivities { if sequenceID := t.getActivityScheduleToCloseTimeout( activityInfo, ); sequenceID != nil { activityTimers = append(activityTimers, *sequenceID) } if sequenceID := t.getActivityScheduleToStartTimeout( activityInfo, ); sequenceID != nil { activityTimers = append(activityTimers, *sequenceID) } if sequenceID := t.getActivityStartToCloseTimeout( activityInfo, ); sequenceID != nil { activityTimers = append(activityTimers, *sequenceID) } if sequenceID := t.getActivityHeartbeatTimeout( activityInfo, ); sequenceID != nil { activityTimers = append(activityTimers, *sequenceID) } } sort.Sort(activityTimers) return activityTimers } func (t *timerSequenceImpl) getUserTimerTimeout( timerInfo *persistence.TimerInfo, ) *TimerSequenceID { return &TimerSequenceID{ EventID: timerInfo.StartedID, Timestamp: timerInfo.ExpiryTime, TimerType: TimerTypeStartToClose, TimerCreated: timerInfo.TaskStatus == TimerTaskStatusCreated, Attempt: 0, } } func (t *timerSequenceImpl) getActivityScheduleToStartTimeout( activityInfo *persistence.ActivityInfo, ) *TimerSequenceID { // activity is not scheduled yet, probably due to retry & backoff if activityInfo.ScheduleID == constants.EmptyEventID { return nil } // activity is already started if activityInfo.StartedID != constants.EmptyEventID { return nil } startTimeout := activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToStartTimeout) * time.Second, ) return &TimerSequenceID{ EventID: activityInfo.ScheduleID, Timestamp: startTimeout, TimerType: TimerTypeScheduleToStart, TimerCreated: (activityInfo.TimerTaskStatus & TimerTaskStatusCreatedScheduleToStart) > 0, Attempt: activityInfo.Attempt, } } func (t *timerSequenceImpl) getActivityScheduleToCloseTimeout( activityInfo *persistence.ActivityInfo, ) *TimerSequenceID { // activity is not scheduled yet, probably due to retry & backoff if activityInfo.ScheduleID == constants.EmptyEventID { return nil } closeTimeout := activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToCloseTimeout) * time.Second, ) return &TimerSequenceID{ EventID: activityInfo.ScheduleID, Timestamp: closeTimeout, TimerType: TimerTypeScheduleToClose, TimerCreated: (activityInfo.TimerTaskStatus & TimerTaskStatusCreatedScheduleToClose) > 0, Attempt: activityInfo.Attempt, } } func (t *timerSequenceImpl) getActivityStartToCloseTimeout( activityInfo *persistence.ActivityInfo, ) *TimerSequenceID { // activity is not scheduled yet, probably due to retry & backoff if activityInfo.ScheduleID == constants.EmptyEventID { return nil } // activity is not started yet if activityInfo.StartedID == constants.EmptyEventID { return nil } closeTimeout := activityInfo.StartedTime.Add( time.Duration(activityInfo.StartToCloseTimeout) * time.Second, ) return &TimerSequenceID{ EventID: activityInfo.ScheduleID, Timestamp: closeTimeout, TimerType: TimerTypeStartToClose, TimerCreated: (activityInfo.TimerTaskStatus & TimerTaskStatusCreatedStartToClose) > 0, Attempt: activityInfo.Attempt, } } func (t *timerSequenceImpl) getActivityHeartbeatTimeout( activityInfo *persistence.ActivityInfo, ) *TimerSequenceID { // activity is not scheduled yet, probably due to retry & backoff if activityInfo.ScheduleID == constants.EmptyEventID { return nil } // activity is not started yet if activityInfo.StartedID == constants.EmptyEventID { return nil } // not heartbeat timeout configured if activityInfo.HeartbeatTimeout <= 0 { return nil } // use the latest time as last heartbeat time lastHeartbeat := activityInfo.StartedTime if activityInfo.LastHeartBeatUpdatedTime.After(lastHeartbeat) { lastHeartbeat = activityInfo.LastHeartBeatUpdatedTime } heartbeatTimeout := lastHeartbeat.Add( time.Duration(activityInfo.HeartbeatTimeout) * time.Second, ) return &TimerSequenceID{ EventID: activityInfo.ScheduleID, Timestamp: heartbeatTimeout, TimerType: TimerTypeHeartbeat, TimerCreated: (activityInfo.TimerTaskStatus & TimerTaskStatusCreatedHeartbeat) > 0, Attempt: activityInfo.Attempt, } } // TimerTypeToTimerMask converts TimerType into the TimerTaskStatus flag func TimerTypeToTimerMask( TimerType TimerType, ) int32 { switch TimerType { case TimerTypeStartToClose: return TimerTaskStatusCreatedStartToClose case TimerTypeScheduleToStart: return TimerTaskStatusCreatedScheduleToStart case TimerTypeScheduleToClose: return TimerTaskStatusCreatedScheduleToClose case TimerTypeHeartbeat: return TimerTaskStatusCreatedHeartbeat default: panic("invalid timeout type") } } // TimerTypeToInternal converts TimeType to its internal representation func TimerTypeToInternal( TimerType TimerType, ) types.TimeoutType { switch TimerType { case TimerTypeStartToClose: return types.TimeoutTypeStartToClose case TimerTypeScheduleToStart: return types.TimeoutTypeScheduleToStart case TimerTypeScheduleToClose: return types.TimeoutTypeScheduleToClose case TimerTypeHeartbeat: return types.TimeoutTypeHeartbeat default: panic(fmt.Sprintf("invalid timer type: %v", TimerType)) } } // TimerTypeFromInternal gets TimerType from internal type func TimerTypeFromInternal( TimerType types.TimeoutType, ) TimerType { switch TimerType { case types.TimeoutTypeStartToClose: return TimerTypeStartToClose case types.TimeoutTypeScheduleToStart: return TimerTypeScheduleToStart case types.TimeoutTypeScheduleToClose: return TimerTypeScheduleToClose case types.TimeoutTypeHeartbeat: return TimerTypeHeartbeat default: panic(fmt.Sprintf("invalid timeout type: %v", TimerType)) } } // TimerTypeToReason creates timeout reason based on the TimeType func TimerTypeToReason( timerType TimerType, ) string { return fmt.Sprintf("cadenceInternal:Timeout %v", TimerTypeToInternal(timerType)) } // Len implements sort.Interface func (s TimerSequenceIDs) Len() int { return len(s) } // Swap implements sort.Interface. func (s TimerSequenceIDs) Swap( this int, that int, ) { s[this], s[that] = s[that], s[this] } // Less implements sort.Interface func (s TimerSequenceIDs) Less( this int, that int, ) bool { thisSequenceID := s[this] thatSequenceID := s[that] // order: timeout time, event ID, timeout type if thisSequenceID.Timestamp.Before(thatSequenceID.Timestamp) { return true } else if thisSequenceID.Timestamp.After(thatSequenceID.Timestamp) { return false } // timeout time are the same if thisSequenceID.EventID < thatSequenceID.EventID { return true } else if thisSequenceID.EventID > thatSequenceID.EventID { return false } // timeout time & event ID are the same if thisSequenceID.TimerType < thatSequenceID.TimerType { return true } else if thisSequenceID.TimerType > thatSequenceID.TimerType { return false } // thisSequenceID && thatSequenceID are the same return true } ================================================ FILE: service/history/execution/timer_sequence_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: timer_sequence.go // // Generated by this command: // // mockgen -package execution -source timer_sequence.go -destination timer_sequence_mock.go -self_package github.com/uber/cadence/service/history/execution // // Package execution is a generated GoMock package. package execution import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" ) // MockTimerSequence is a mock of TimerSequence interface. type MockTimerSequence struct { ctrl *gomock.Controller recorder *MockTimerSequenceMockRecorder isgomock struct{} } // MockTimerSequenceMockRecorder is the mock recorder for MockTimerSequence. type MockTimerSequenceMockRecorder struct { mock *MockTimerSequence } // NewMockTimerSequence creates a new mock instance. func NewMockTimerSequence(ctrl *gomock.Controller) *MockTimerSequence { mock := &MockTimerSequence{ctrl: ctrl} mock.recorder = &MockTimerSequenceMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTimerSequence) EXPECT() *MockTimerSequenceMockRecorder { return m.recorder } // CreateNextActivityTimer mocks base method. func (m *MockTimerSequence) CreateNextActivityTimer() (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateNextActivityTimer") ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateNextActivityTimer indicates an expected call of CreateNextActivityTimer. func (mr *MockTimerSequenceMockRecorder) CreateNextActivityTimer() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNextActivityTimer", reflect.TypeOf((*MockTimerSequence)(nil).CreateNextActivityTimer)) } // CreateNextUserTimer mocks base method. func (m *MockTimerSequence) CreateNextUserTimer() (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateNextUserTimer") ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateNextUserTimer indicates an expected call of CreateNextUserTimer. func (mr *MockTimerSequenceMockRecorder) CreateNextUserTimer() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNextUserTimer", reflect.TypeOf((*MockTimerSequence)(nil).CreateNextUserTimer)) } // IsExpired mocks base method. func (m *MockTimerSequence) IsExpired(referenceTime time.Time, TimerSequenceID TimerSequenceID) (time.Duration, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsExpired", referenceTime, TimerSequenceID) ret0, _ := ret[0].(time.Duration) ret1, _ := ret[1].(bool) return ret0, ret1 } // IsExpired indicates an expected call of IsExpired. func (mr *MockTimerSequenceMockRecorder) IsExpired(referenceTime, TimerSequenceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsExpired", reflect.TypeOf((*MockTimerSequence)(nil).IsExpired), referenceTime, TimerSequenceID) } // LoadAndSortActivityTimers mocks base method. func (m *MockTimerSequence) LoadAndSortActivityTimers() []TimerSequenceID { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LoadAndSortActivityTimers") ret0, _ := ret[0].([]TimerSequenceID) return ret0 } // LoadAndSortActivityTimers indicates an expected call of LoadAndSortActivityTimers. func (mr *MockTimerSequenceMockRecorder) LoadAndSortActivityTimers() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadAndSortActivityTimers", reflect.TypeOf((*MockTimerSequence)(nil).LoadAndSortActivityTimers)) } // LoadAndSortUserTimers mocks base method. func (m *MockTimerSequence) LoadAndSortUserTimers() []TimerSequenceID { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LoadAndSortUserTimers") ret0, _ := ret[0].([]TimerSequenceID) return ret0 } // LoadAndSortUserTimers indicates an expected call of LoadAndSortUserTimers. func (mr *MockTimerSequenceMockRecorder) LoadAndSortUserTimers() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadAndSortUserTimers", reflect.TypeOf((*MockTimerSequence)(nil).LoadAndSortUserTimers)) } ================================================ FILE: service/history/execution/timer_sequence_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( timerSequenceSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockMutableState *MockMutableState timerSequence *timerSequenceImpl } ) func TestTimerSequenceSuite(t *testing.T) { s := new(timerSequenceSuite) suite.Run(t, s) } func (s *timerSequenceSuite) SetupSuite() { } func (s *timerSequenceSuite) TearDownSuite() { } func (s *timerSequenceSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockMutableState = NewMockMutableState(s.controller) s.timerSequence = NewTimerSequence(s.mockMutableState).(*timerSequenceImpl) } func (s *timerSequenceSuite) TearDownTest() { s.controller.Finish() } func (s *timerSequenceSuite) TestCreateNextUserTimer_AlreadyCreated() { now := time.Now() timerInfo := &persistence.TimerInfo{ Version: 123, TimerID: "some random timer ID", StartedID: 456, ExpiryTime: now.Add(100 * time.Second), TaskStatus: TimerTaskStatusCreated, } timerInfos := map[string]*persistence.TimerInfo{timerInfo.TimerID: timerInfo} s.mockMutableState.EXPECT().GetPendingTimerInfos().Return(timerInfos).Times(1) modified, err := s.timerSequence.CreateNextUserTimer() s.NoError(err) s.False(modified) } func (s *timerSequenceSuite) TestCreateNextUserTimer_NotCreated() { now := time.Now() currentVersion := int64(999) timerInfo := &persistence.TimerInfo{ Version: 123, TimerID: "some random timer ID", StartedID: 456, ExpiryTime: now.Add(100 * time.Second), TaskStatus: TimerTaskStatusNone, } timerInfos := map[string]*persistence.TimerInfo{timerInfo.TimerID: timerInfo} s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id", TaskList: "task-list", }).Times(1) s.mockMutableState.EXPECT().GetPendingTimerInfos().Return(timerInfos).Times(1) s.mockMutableState.EXPECT().GetUserTimerInfoByEventID(timerInfo.StartedID).Return(timerInfo, true).Times(1) var timerInfoUpdated = *timerInfo // make a copy timerInfoUpdated.TaskStatus = TimerTaskStatusCreated s.mockMutableState.EXPECT().UpdateUserTimer(&timerInfoUpdated).Return(nil).Times(1) s.mockMutableState.EXPECT().GetCurrentVersion().Return(currentVersion).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id", }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: timerInfo.ExpiryTime, Version: currentVersion, }, EventID: timerInfo.StartedID, TaskList: "task-list", }).Times(1) modified, err := s.timerSequence.CreateNextUserTimer() s.NoError(err) s.True(modified) } func (s *timerSequenceSuite) TestCreateNextActivityTimer_AlreadyCreated() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 1, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusCreatedScheduleToClose | TimerTaskStatusCreatedScheduleToStart, Attempt: 12, TaskList: "task-list", } activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) modified, err := s.timerSequence.CreateNextActivityTimer() s.NoError(err) s.False(modified) } func (s *timerSequenceSuite) TestCreateNextActivityTimer_NotCreated() { now := time.Now() currentVersion := int64(999) activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 1, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusNone, Attempt: 12, TaskList: "task-list", } activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id", TaskList: "task-list2", }).Times(1) s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) s.mockMutableState.EXPECT().GetActivityInfo(activityInfo.ScheduleID).Return(activityInfo, true).Times(1) var activityInfoUpdated = *activityInfo // make a copy activityInfoUpdated.TimerTaskStatus = TimerTaskStatusCreatedScheduleToStart s.mockMutableState.EXPECT().UpdateActivity(&activityInfoUpdated).Return(nil).Times(1) s.mockMutableState.EXPECT().GetCurrentVersion().Return(currentVersion).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.ActivityTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id", }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToStartTimeout) * time.Second, ), Version: currentVersion, }, TimeoutType: int(types.TimeoutTypeScheduleToStart), EventID: activityInfo.ScheduleID, Attempt: int64(activityInfo.Attempt), TaskList: "task-list", }).Times(1) modified, err := s.timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) } func (s *timerSequenceSuite) TestCreateNextActivityTimer_HeartbeatTimer() { now := time.Now() currentVersion := int64(999) activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 1, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusNone, Attempt: 12, TaskList: "task-list", } activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id", TaskList: "task-list2", }).Times(1) s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) s.mockMutableState.EXPECT().GetActivityInfo(activityInfo.ScheduleID).Return(activityInfo, true).Times(1) taskVisibilityTimestamp := activityInfo.StartedTime.Add( time.Duration(activityInfo.HeartbeatTimeout) * time.Second, ) var activityInfoUpdated = *activityInfo // make a copy activityInfoUpdated.TimerTaskStatus = TimerTaskStatusCreatedHeartbeat activityInfoUpdated.LastHeartbeatTimeoutVisibilityInSeconds = taskVisibilityTimestamp.Unix() s.mockMutableState.EXPECT().UpdateActivity(&activityInfoUpdated).Return(nil).Times(1) s.mockMutableState.EXPECT().GetCurrentVersion().Return(currentVersion).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(&persistence.ActivityTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain-id", WorkflowID: "wf-id", RunID: "run-id", }, TaskData: persistence.TaskData{ // TaskID is set by shard VisibilityTimestamp: taskVisibilityTimestamp, Version: currentVersion, }, TimeoutType: int(types.TimeoutTypeHeartbeat), EventID: activityInfo.ScheduleID, Attempt: int64(activityInfo.Attempt), TaskList: "task-list", }).Times(1) modified, err := s.timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) } func (s *timerSequenceSuite) TestLoadAndSortUserTimers_None() { timerInfos := map[string]*persistence.TimerInfo{} s.mockMutableState.EXPECT().GetPendingTimerInfos().Return(timerInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortUserTimers() s.Empty(TimerSequenceIDs) } func (s *timerSequenceSuite) TestLoadAndSortUserTimers_One() { now := time.Now() timerInfo := &persistence.TimerInfo{ Version: 123, TimerID: "some random timer ID", StartedID: 456, ExpiryTime: now.Add(100 * time.Second), TaskStatus: TimerTaskStatusCreated, } timerInfos := map[string]*persistence.TimerInfo{timerInfo.TimerID: timerInfo} s.mockMutableState.EXPECT().GetPendingTimerInfos().Return(timerInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortUserTimers() s.Equal([]TimerSequenceID{{ EventID: timerInfo.StartedID, Timestamp: timerInfo.ExpiryTime, TimerType: TimerTypeStartToClose, TimerCreated: true, Attempt: 0, }}, TimerSequenceIDs) } func (s *timerSequenceSuite) TestLoadAndSortUserTimers_Multiple() { now := time.Now() timerInfo1 := &persistence.TimerInfo{ Version: 123, TimerID: "some random timer ID", StartedID: 456, ExpiryTime: now.Add(100 * time.Second), TaskStatus: TimerTaskStatusCreated, } timerInfo2 := &persistence.TimerInfo{ Version: 1234, TimerID: "other random timer ID", StartedID: 4567, ExpiryTime: now.Add(200 * time.Second), TaskStatus: TimerTaskStatusNone, } timerInfos := map[string]*persistence.TimerInfo{ timerInfo1.TimerID: timerInfo1, timerInfo2.TimerID: timerInfo2, } s.mockMutableState.EXPECT().GetPendingTimerInfos().Return(timerInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortUserTimers() s.Equal([]TimerSequenceID{ { EventID: timerInfo1.StartedID, Timestamp: timerInfo1.ExpiryTime, TimerType: TimerTypeStartToClose, TimerCreated: true, Attempt: 0, }, { EventID: timerInfo2.StartedID, Timestamp: timerInfo2.ExpiryTime, TimerType: TimerTypeStartToClose, TimerCreated: false, Attempt: 0, }, }, TimerSequenceIDs) } func (s *timerSequenceSuite) TestLoadAndSortActivityTimers_None() { activityInfos := map[int64]*persistence.ActivityInfo{} s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortActivityTimers() s.Empty(TimerSequenceIDs) } func (s *timerSequenceSuite) TestLoadAndSortActivityTimers_One_NotScheduled() { activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: constants.EmptyEventID, ScheduledTime: time.Time{}, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 1, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusNone, } activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortActivityTimers() s.Empty(TimerSequenceIDs) } func (s *timerSequenceSuite) TestLoadAndSortActivityTimers_One_Scheduled_NotStarted() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 1, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusCreatedScheduleToClose | TimerTaskStatusCreatedScheduleToStart, Attempt: 12, } activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortActivityTimers() s.Equal([]TimerSequenceID{ { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToStartTimeout) * time.Second, ), TimerType: TimerTypeScheduleToStart, TimerCreated: true, Attempt: activityInfo.Attempt, }, { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToCloseTimeout) * time.Second, ), TimerType: TimerTypeScheduleToClose, TimerCreated: true, Attempt: activityInfo.Attempt, }, }, TimerSequenceIDs) } func (s *timerSequenceSuite) TestLoadAndSortActivityTimers_One_Scheduled_Started_WithHeartbeatTimeout() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 1, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusCreatedScheduleToClose | TimerTaskStatusCreatedStartToClose | TimerTaskStatusCreatedHeartbeat, Attempt: 12, } activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortActivityTimers() s.Equal([]TimerSequenceID{ { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.StartedTime.Add( time.Duration(activityInfo.HeartbeatTimeout) * time.Second, ), TimerType: TimerTypeHeartbeat, TimerCreated: true, Attempt: activityInfo.Attempt, }, { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.StartedTime.Add( time.Duration(activityInfo.StartToCloseTimeout) * time.Second, ), TimerType: TimerTypeStartToClose, TimerCreated: true, Attempt: activityInfo.Attempt, }, { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToCloseTimeout) * time.Second, ), TimerType: TimerTypeScheduleToClose, TimerCreated: true, Attempt: activityInfo.Attempt, }, }, TimerSequenceIDs) } func (s *timerSequenceSuite) TestLoadAndSortActivityTimers_One_Scheduled_Started_WithoutHeartbeatTimeout() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusCreatedScheduleToClose | TimerTaskStatusCreatedStartToClose, Attempt: 12, } activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortActivityTimers() s.Equal([]TimerSequenceID{ { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.StartedTime.Add( time.Duration(activityInfo.StartToCloseTimeout) * time.Second, ), TimerType: TimerTypeStartToClose, TimerCreated: true, Attempt: activityInfo.Attempt, }, { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToCloseTimeout) * time.Second, ), TimerType: TimerTypeScheduleToClose, TimerCreated: true, Attempt: activityInfo.Attempt, }, }, TimerSequenceIDs) } func (s *timerSequenceSuite) TestLoadAndSortActivityTimers_One_Scheduled_Started_Heartbeated_WithHeartbeatTimeout() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 1, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusCreatedScheduleToClose | TimerTaskStatusCreatedStartToClose | TimerTaskStatusCreatedHeartbeat, Attempt: 12, } activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortActivityTimers() s.Equal([]TimerSequenceID{ { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.LastHeartBeatUpdatedTime.Add( time.Duration(activityInfo.HeartbeatTimeout) * time.Second, ), TimerType: TimerTypeHeartbeat, TimerCreated: true, Attempt: activityInfo.Attempt, }, { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.StartedTime.Add( time.Duration(activityInfo.StartToCloseTimeout) * time.Second, ), TimerType: TimerTypeStartToClose, TimerCreated: true, Attempt: activityInfo.Attempt, }, { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToCloseTimeout) * time.Second, ), TimerType: TimerTypeScheduleToClose, TimerCreated: true, Attempt: activityInfo.Attempt, }, }, TimerSequenceIDs) } func (s *timerSequenceSuite) TestLoadAndSortActivityTimers_One_Scheduled_Started_Heartbeated_WithoutHeartbeatTimeout() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusCreatedScheduleToClose | TimerTaskStatusCreatedStartToClose, Attempt: 12, } activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortActivityTimers() s.Equal([]TimerSequenceID{ { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.StartedTime.Add( time.Duration(activityInfo.StartToCloseTimeout) * time.Second, ), TimerType: TimerTypeStartToClose, TimerCreated: true, Attempt: activityInfo.Attempt, }, { EventID: activityInfo.ScheduleID, Timestamp: activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToCloseTimeout) * time.Second, ), TimerType: TimerTypeScheduleToClose, TimerCreated: true, Attempt: activityInfo.Attempt, }, }, TimerSequenceIDs) } func (s *timerSequenceSuite) TestLoadAndSortActivityTimers_Multiple() { now := time.Now() activityInfo1 := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusNone, Attempt: 12, } activityInfo2 := &persistence.ActivityInfo{ Version: 123, ScheduleID: 2345, ScheduledTime: now, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "other random activity ID", ScheduleToStartTimeout: 11, ScheduleToCloseTimeout: 1001, StartToCloseTimeout: 101, HeartbeatTimeout: 6, LastHeartBeatUpdatedTime: now.Add(800 * time.Millisecond), TimerTaskStatus: TimerTaskStatusNone, Attempt: 21, } activityInfos := map[int64]*persistence.ActivityInfo{ activityInfo1.ScheduleID: activityInfo1, activityInfo2.ScheduleID: activityInfo2, } s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).Times(1) TimerSequenceIDs := s.timerSequence.LoadAndSortActivityTimers() s.Equal([]TimerSequenceID{ { EventID: activityInfo2.ScheduleID, Timestamp: activityInfo2.ScheduledTime.Add( time.Duration(activityInfo2.ScheduleToStartTimeout) * time.Second, ), TimerType: TimerTypeScheduleToStart, TimerCreated: false, Attempt: activityInfo2.Attempt, }, { EventID: activityInfo1.ScheduleID, Timestamp: activityInfo1.StartedTime.Add( time.Duration(activityInfo1.StartToCloseTimeout) * time.Second, ), TimerType: TimerTypeStartToClose, TimerCreated: false, Attempt: activityInfo1.Attempt, }, { EventID: activityInfo1.ScheduleID, Timestamp: activityInfo1.ScheduledTime.Add( time.Duration(activityInfo1.ScheduleToCloseTimeout) * time.Second, ), TimerType: TimerTypeScheduleToClose, TimerCreated: false, Attempt: activityInfo1.Attempt, }, { EventID: activityInfo2.ScheduleID, Timestamp: activityInfo2.ScheduledTime.Add( time.Duration(activityInfo2.ScheduleToCloseTimeout) * time.Second, ), TimerType: TimerTypeScheduleToClose, TimerCreated: false, Attempt: activityInfo2.Attempt, }, }, TimerSequenceIDs) } func (s *timerSequenceSuite) TestGetUserTimerTimeout() { now := time.Now() timerInfo := &persistence.TimerInfo{ Version: 123, TimerID: "some random timer ID", StartedID: 456, ExpiryTime: now.Add(100 * time.Second), TaskStatus: TimerTaskStatusCreated, } expectedTimerSequence := &TimerSequenceID{ EventID: timerInfo.StartedID, Timestamp: timerInfo.ExpiryTime, TimerType: TimerTypeStartToClose, TimerCreated: true, Attempt: 0, } timerSequence := s.timerSequence.getUserTimerTimeout(timerInfo) s.Equal(expectedTimerSequence, timerSequence) timerInfo.TaskStatus = TimerTaskStatusNone expectedTimerSequence.TimerCreated = false timerSequence = s.timerSequence.getUserTimerTimeout(timerInfo) s.Equal(expectedTimerSequence, timerSequence) } func (s *timerSequenceSuite) TestGetActivityScheduleToStartTimeout_NotScheduled() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: constants.EmptyEventID, ScheduledTime: time.Time{}, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusNone, Attempt: 12, } timerSequence := s.timerSequence.getActivityScheduleToStartTimeout(activityInfo) s.Empty(timerSequence) } func (s *timerSequenceSuite) TestGetActivityScheduleToStartTimeout_Scheduled_NotStarted() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusCreatedScheduleToStart, Attempt: 12, } expectedTimerSequence := &TimerSequenceID{ EventID: activityInfo.ScheduleID, Timestamp: activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToStartTimeout) * time.Second, ), TimerType: TimerTypeScheduleToStart, TimerCreated: true, Attempt: 12, } timerSequence := s.timerSequence.getActivityScheduleToStartTimeout(activityInfo) s.Equal(expectedTimerSequence, timerSequence) activityInfo.TimerTaskStatus = TimerTaskStatusNone expectedTimerSequence.TimerCreated = false timerSequence = s.timerSequence.getActivityScheduleToStartTimeout(activityInfo) s.Equal(expectedTimerSequence, timerSequence) } func (s *timerSequenceSuite) TestGetActivityScheduleToStartTimeout_Scheduled_Started() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Second), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusCreatedScheduleToStart, Attempt: 12, } timerSequence := s.timerSequence.getActivityScheduleToStartTimeout(activityInfo) s.Empty(timerSequence) activityInfo.TimerTaskStatus = TimerTaskStatusNone timerSequence = s.timerSequence.getActivityScheduleToStartTimeout(activityInfo) s.Empty(timerSequence) } func (s *timerSequenceSuite) TestGetActivityScheduleToCloseTimeout_NotScheduled() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: constants.EmptyEventID, ScheduledTime: time.Time{}, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusNone, Attempt: 12, } timerSequence := s.timerSequence.getActivityScheduleToCloseTimeout(activityInfo) s.Empty(timerSequence) } func (s *timerSequenceSuite) TestGetActivityScheduleToCloseTimeout_Scheduled() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusCreatedScheduleToClose, Attempt: 12, } expectedTimerSequence := &TimerSequenceID{ EventID: activityInfo.ScheduleID, Timestamp: activityInfo.ScheduledTime.Add( time.Duration(activityInfo.ScheduleToCloseTimeout) * time.Second, ), TimerType: TimerTypeScheduleToClose, TimerCreated: true, Attempt: 12, } timerSequence := s.timerSequence.getActivityScheduleToCloseTimeout(activityInfo) s.Equal(expectedTimerSequence, timerSequence) activityInfo.TimerTaskStatus = TimerTaskStatusNone expectedTimerSequence.TimerCreated = false timerSequence = s.timerSequence.getActivityScheduleToCloseTimeout(activityInfo) s.Equal(expectedTimerSequence, timerSequence) } func (s *timerSequenceSuite) TestGetActivityStartToCloseTimeout_NotStarted() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusNone, Attempt: 12, } timerSequence := s.timerSequence.getActivityStartToCloseTimeout(activityInfo) s.Empty(timerSequence) } func (s *timerSequenceSuite) TestGetActivityStartToCloseTimeout_Started() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusCreatedStartToClose, Attempt: 12, } expectedTimerSequence := &TimerSequenceID{ EventID: activityInfo.ScheduleID, Timestamp: activityInfo.StartedTime.Add( time.Duration(activityInfo.StartToCloseTimeout) * time.Second, ), TimerType: TimerTypeStartToClose, TimerCreated: true, Attempt: 12, } timerSequence := s.timerSequence.getActivityStartToCloseTimeout(activityInfo) s.Equal(expectedTimerSequence, timerSequence) activityInfo.TimerTaskStatus = TimerTaskStatusNone expectedTimerSequence.TimerCreated = false timerSequence = s.timerSequence.getActivityStartToCloseTimeout(activityInfo) s.Equal(expectedTimerSequence, timerSequence) } func (s *timerSequenceSuite) TestGetActivityHeartbeatTimeout_WithHeartbeat_NotStarted() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 1, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusNone, Attempt: 12, } timerSequence := s.timerSequence.getActivityHeartbeatTimeout(activityInfo) s.Empty(timerSequence) } func (s *timerSequenceSuite) TestGetActivityHeartbeatTimeout_WithHeartbeat_Started_NoHeartbeat() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 1, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusCreatedHeartbeat, Attempt: 12, } expectedTimerSequence := &TimerSequenceID{ EventID: activityInfo.ScheduleID, Timestamp: activityInfo.StartedTime.Add( time.Duration(activityInfo.HeartbeatTimeout) * time.Second, ), TimerType: TimerTypeHeartbeat, TimerCreated: true, Attempt: 12, } timerSequence := s.timerSequence.getActivityHeartbeatTimeout(activityInfo) s.Equal(expectedTimerSequence, timerSequence) activityInfo.TimerTaskStatus = TimerTaskStatusNone expectedTimerSequence.TimerCreated = false timerSequence = s.timerSequence.getActivityHeartbeatTimeout(activityInfo) s.Equal(expectedTimerSequence, timerSequence) } func (s *timerSequenceSuite) TestGetActivityHeartbeatTimeout_WithHeartbeat_Started_Heartbeated() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 1, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusCreatedHeartbeat, Attempt: 12, } expectedTimerSequence := &TimerSequenceID{ EventID: activityInfo.ScheduleID, Timestamp: activityInfo.LastHeartBeatUpdatedTime.Add( time.Duration(activityInfo.HeartbeatTimeout) * time.Second, ), TimerType: TimerTypeHeartbeat, TimerCreated: true, Attempt: 12, } timerSequence := s.timerSequence.getActivityHeartbeatTimeout(activityInfo) s.Equal(expectedTimerSequence, timerSequence) activityInfo.TimerTaskStatus = TimerTaskStatusNone expectedTimerSequence.TimerCreated = false timerSequence = s.timerSequence.getActivityHeartbeatTimeout(activityInfo) s.Equal(expectedTimerSequence, timerSequence) } func (s *timerSequenceSuite) TestGetActivityHeartbeatTimeout_WithoutHeartbeat_NotStarted() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: constants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusNone, Attempt: 12, } timerSequence := s.timerSequence.getActivityHeartbeatTimeout(activityInfo) s.Empty(timerSequence) } func (s *timerSequenceSuite) TestGetActivityHeartbeatTimeout_WithoutHeartbeat_Started_NoHeartbeat() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: TimerTaskStatusCreatedHeartbeat, Attempt: 12, } timerSequence := s.timerSequence.getActivityHeartbeatTimeout(activityInfo) s.Empty(timerSequence) activityInfo.TimerTaskStatus = TimerTaskStatusNone timerSequence = s.timerSequence.getActivityHeartbeatTimeout(activityInfo) s.Empty(timerSequence) } func (s *timerSequenceSuite) TestGetActivityHeartbeatTimeout_WithoutHeartbeat_Started_Heartbeated() { now := time.Now() activityInfo := &persistence.ActivityInfo{ Version: 123, ScheduleID: 234, ScheduledTime: now, StartedID: 345, StartedTime: now.Add(200 * time.Millisecond), ActivityID: "some random activity ID", ScheduleToStartTimeout: 10, ScheduleToCloseTimeout: 1000, StartToCloseTimeout: 100, HeartbeatTimeout: 0, LastHeartBeatUpdatedTime: now.Add(400 * time.Millisecond), TimerTaskStatus: TimerTaskStatusCreatedHeartbeat, Attempt: 12, } timerSequence := s.timerSequence.getActivityHeartbeatTimeout(activityInfo) s.Empty(timerSequence) activityInfo.TimerTaskStatus = TimerTaskStatusNone timerSequence = s.timerSequence.getActivityHeartbeatTimeout(activityInfo) s.Empty(timerSequence) } func (s *timerSequenceSuite) TestConversion() { s.Equal(types.TimeoutTypeStartToClose, TimerTypeToInternal(TimerTypeStartToClose)) s.Equal(types.TimeoutTypeScheduleToStart, TimerTypeToInternal(TimerTypeScheduleToStart)) s.Equal(types.TimeoutTypeScheduleToClose, TimerTypeToInternal(TimerTypeScheduleToClose)) s.Equal(types.TimeoutTypeHeartbeat, TimerTypeToInternal(TimerTypeHeartbeat)) s.Equal(TimerTypeFromInternal(types.TimeoutTypeStartToClose), TimerTypeStartToClose) s.Equal(TimerTypeFromInternal(types.TimeoutTypeScheduleToStart), TimerTypeScheduleToStart) s.Equal(TimerTypeFromInternal(types.TimeoutTypeScheduleToClose), TimerTypeScheduleToClose) s.Equal(TimerTypeFromInternal(types.TimeoutTypeHeartbeat), TimerTypeHeartbeat) s.Equal(int32(TimerTaskStatusCreatedStartToClose), TimerTypeToTimerMask(TimerTypeStartToClose)) s.Equal(int32(TimerTaskStatusCreatedScheduleToStart), TimerTypeToTimerMask(TimerTypeScheduleToStart)) s.Equal(int32(TimerTaskStatusCreatedScheduleToClose), TimerTypeToTimerMask(TimerTypeScheduleToClose)) s.Equal(int32(TimerTaskStatusCreatedHeartbeat), TimerTypeToTimerMask(TimerTypeHeartbeat)) s.Equal(TimerTaskStatusNone, 0) s.Equal(TimerTaskStatusCreated, 1) s.Equal(TimerTaskStatusCreatedStartToClose, 1) s.Equal(TimerTaskStatusCreatedScheduleToStart, 2) s.Equal(TimerTaskStatusCreatedScheduleToClose, 4) s.Equal(TimerTaskStatusCreatedHeartbeat, 8) } func (s *timerSequenceSuite) TestLess_CompareTime() { now := time.Now() timerSequenceID1 := TimerSequenceID{ EventID: 123, Timestamp: now, TimerType: TimerTypeHeartbeat, TimerCreated: true, Attempt: 12, } timerSequenceID2 := TimerSequenceID{ EventID: 123, Timestamp: now.Add(time.Second), TimerType: TimerTypeHeartbeat, TimerCreated: true, Attempt: 12, } TimerSequenceIDs := TimerSequenceIDs([]TimerSequenceID{timerSequenceID1, timerSequenceID2}) s.True(TimerSequenceIDs.Less(0, 1)) s.False(TimerSequenceIDs.Less(1, 0)) } func (s *timerSequenceSuite) TestLess_CompareEventID() { now := time.Now() timerSequenceID1 := TimerSequenceID{ EventID: 122, Timestamp: now, TimerType: TimerTypeHeartbeat, TimerCreated: true, Attempt: 12, } timerSequenceID2 := TimerSequenceID{ EventID: 123, Timestamp: now, TimerType: TimerTypeHeartbeat, TimerCreated: true, Attempt: 12, } TimerSequenceIDs := TimerSequenceIDs([]TimerSequenceID{timerSequenceID1, timerSequenceID2}) s.True(TimerSequenceIDs.Less(0, 1)) s.False(TimerSequenceIDs.Less(1, 0)) } func (s *timerSequenceSuite) TestLess_CompareType() { now := time.Now() timerSequenceID1 := TimerSequenceID{ EventID: 123, Timestamp: now, TimerType: TimerTypeScheduleToClose, TimerCreated: true, Attempt: 12, } timerSequenceID2 := TimerSequenceID{ EventID: 123, Timestamp: now, TimerType: TimerTypeHeartbeat, TimerCreated: true, Attempt: 12, } TimerSequenceIDs := TimerSequenceIDs([]TimerSequenceID{timerSequenceID1, timerSequenceID2}) s.True(TimerSequenceIDs.Less(0, 1)) s.False(TimerSequenceIDs.Less(1, 0)) } ================================================ FILE: service/history/execution/workflow.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination workflow_mock.go -self_package github.com/uber/cadence/service/history/execution package execution import ( "context" "fmt" "time" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) const ( // IdentityHistoryService is the service role identity IdentityHistoryService = "history-service" // WorkflowTerminationIdentity is the component which decides to terminate the workflow WorkflowTerminationIdentity = "worker-service" // WorkflowTerminationReason is the reason for terminating workflow due to version conflict WorkflowTerminationReason = "Terminate Workflow Due To Version Conflict." ) type ( // Workflow is the interface for NDC workflow Workflow interface { GetContext() Context GetMutableState() MutableState GetReleaseFn() ReleaseFunc GetVectorClock() (WorkflowVectorClock, error) HappensAfter(that Workflow) (bool, error) Revive() error SuppressBy(incomingWorkflow Workflow) (TransactionPolicy, error) FlushBufferedEvents() error } workflowImpl struct { logger log.Logger clusterMetadata cluster.Metadata ctx context.Context context Context mutableState MutableState releaseFn ReleaseFunc } WorkflowVectorClock struct { ActiveClusterSelectionPolicy *types.ActiveClusterSelectionPolicy LastWriteVersion int64 LastEventTaskID int64 StartTimestamp time.Time RunID string } ) // NewWorkflow creates a new NDC workflow func NewWorkflow( ctx context.Context, clusterMetadata cluster.Metadata, context Context, mutableState MutableState, releaseFn ReleaseFunc, logger log.Logger, ) Workflow { return &workflowImpl{ ctx: ctx, clusterMetadata: clusterMetadata, logger: logger, context: context, mutableState: mutableState, releaseFn: releaseFn, } } func (r *workflowImpl) GetContext() Context { return r.context } func (r *workflowImpl) GetMutableState() MutableState { return r.mutableState } func (r *workflowImpl) GetReleaseFn() ReleaseFunc { return r.releaseFn } func (r *workflowImpl) GetVectorClock() (WorkflowVectorClock, error) { lastWriteVersion, err := r.mutableState.GetLastWriteVersion() if err != nil { return WorkflowVectorClock{}, err } executionInfo := r.mutableState.GetExecutionInfo() return WorkflowVectorClock{ ActiveClusterSelectionPolicy: executionInfo.ActiveClusterSelectionPolicy, LastWriteVersion: lastWriteVersion, LastEventTaskID: executionInfo.LastEventTaskID, StartTimestamp: executionInfo.StartTimestamp, RunID: executionInfo.RunID, }, nil } func (r *workflowImpl) HappensAfter( that Workflow, ) (bool, error) { thisVectorClock, err := r.GetVectorClock() if err != nil { return false, err } thatVectorClock, err := that.GetVectorClock() if err != nil { return false, err } return workflowHappensAfter( thisVectorClock, thatVectorClock, ), nil } func (r *workflowImpl) Revive() error { state, _ := r.mutableState.GetWorkflowStateCloseStatus() if state != persistence.WorkflowStateZombie { return nil } else if state == persistence.WorkflowStateCompleted { // workflow already finished return nil } // workflow is in zombie state, need to set the state correctly accordingly state = persistence.WorkflowStateCreated if r.mutableState.HasProcessedOrPendingDecision() { state = persistence.WorkflowStateRunning } return r.mutableState.UpdateWorkflowStateCloseStatus( state, persistence.WorkflowCloseStatusNone, ) } func (r *workflowImpl) SuppressBy( incomingWorkflow Workflow, ) (TransactionPolicy, error) { // NOTE: READ BEFORE MODIFICATION // // if the workflow to be suppressed has last write version being local active // then use active logic to terminate this workflow // if the workflow to be suppressed has last write version being remote active // then turn this workflow into a zombie currentVectorClock, err := r.GetVectorClock() if err != nil { return TransactionPolicyActive, err } incomingVectorClock, err := incomingWorkflow.GetVectorClock() if err != nil { return TransactionPolicyActive, err } if workflowHappensAfter( currentVectorClock, incomingVectorClock, ) { return TransactionPolicyActive, &types.InternalServiceError{ Message: "nDCWorkflow cannot suppress workflow by older workflow", } } // if workflow is in zombie or finished state, keep as is if !r.mutableState.IsWorkflowExecutionRunning() { return TransactionPolicyPassive, nil } lastWriteCluster, err := r.clusterMetadata.ClusterNameForFailoverVersion(currentVectorClock.LastWriteVersion) if err != nil { return TransactionPolicyActive, err } currentCluster := r.clusterMetadata.GetCurrentClusterName() if currentCluster == lastWriteCluster { return TransactionPolicyActive, r.terminateWorkflow(currentVectorClock.LastWriteVersion, incomingVectorClock.LastWriteVersion, WorkflowTerminationReason) } return TransactionPolicyPassive, r.zombiefyWorkflow() } func (r *workflowImpl) FlushBufferedEvents() error { if !r.mutableState.IsWorkflowExecutionRunning() { return nil } if !r.mutableState.HasBufferedEvents() { return nil } currentVectorClock, err := r.GetVectorClock() if err != nil { return err } lastWriteCluster, err := r.clusterMetadata.ClusterNameForFailoverVersion(currentVectorClock.LastWriteVersion) if err != nil { // TODO: add a test for this return err } currentCluster := r.clusterMetadata.GetCurrentClusterName() if lastWriteCluster != currentCluster { // TODO: add a test for this return &types.InternalServiceError{ Message: "nDCWorkflow encounter workflow with buffered events but last write not from current cluster", } } return r.failDecision(currentVectorClock.LastWriteVersion, true) } func (r *workflowImpl) failDecision( lastWriteVersion int64, scheduleNewDecision bool, ) error { // do not persist the change right now, NDC requires transaction r.logger.Debugf("failDecision calling UpdateCurrentVersion for domain %s, wfID %v, lastWriteVersion %v", r.mutableState.GetExecutionInfo().DomainID, r.mutableState.GetExecutionInfo().WorkflowID, lastWriteVersion) if err := r.mutableState.UpdateCurrentVersion(lastWriteVersion, true); err != nil { return err } decision, ok := r.mutableState.GetInFlightDecision() if !ok { return nil } if err := FailDecision(r.mutableState, decision, types.DecisionTaskFailedCauseFailoverCloseDecision); err != nil { return err } if scheduleNewDecision { return ScheduleDecision(r.mutableState) } return nil } func (r *workflowImpl) terminateWorkflow( lastWriteVersion int64, incomingLastWriteVersion int64, terminationReason string, ) error { eventBatchFirstEventID := r.GetMutableState().GetNextEventID() if err := r.failDecision(lastWriteVersion, false); err != nil { return err } // do not persist the change right now, NDC requires transaction r.logger.Debugf("terminateWorkflow calling UpdateCurrentVersion for domain %s, wfID %v, lastWriteVersion %v", r.mutableState.GetExecutionInfo().DomainID, r.mutableState.GetExecutionInfo().WorkflowID, lastWriteVersion) if err := r.mutableState.UpdateCurrentVersion(lastWriteVersion, true); err != nil { return err } _, err := r.mutableState.AddWorkflowExecutionTerminatedEvent( eventBatchFirstEventID, terminationReason, []byte(fmt.Sprintf("terminated by version: %v", incomingLastWriteVersion)), WorkflowTerminationIdentity, ) return err } func (r *workflowImpl) zombiefyWorkflow() error { return r.mutableState.GetExecutionInfo().UpdateWorkflowStateCloseStatus( persistence.WorkflowStateZombie, persistence.WorkflowCloseStatusNone, ) } // Conflict resolution for workflow replication requires determining which workflow event is "newer." // This decision must be made deterministically across all clusters without coordination, // ensuring that each cluster independently resolves conflicts to the same final state. // // Active-Passive Domains: // - These domains do not use active cluster selection policies. // - The event with the larger failover version is considered newer, since the failover version is // a monotonically increasing logical clock for the domain. // - After a failover, any event with a higher failover version wins conflict resolution. // - Conflicts between events with the same failover version should not occur, as they originate // from the same active cluster. In case of a tie, the replication task event ID is used as a tiebreaker. // // Active-Active Domains (Same Selection Policy): // - Treated the same as active-passive domains. // - The event generated after failover (larger failover version) wins. // // Active-Active Domains (Different Selection Policies): // - These represent workflows started concurrently in different active clusters. // - The event with the larger start timestamp is considered newer. // - Clock skew between clusters is expected to be small, making this a reasonable rule. // - In case of a tie on start time, the RunID is used as a final tiebreaker. func workflowHappensAfter( thisVectorClock WorkflowVectorClock, thatVectorClock WorkflowVectorClock, ) bool { if !thisVectorClock.ActiveClusterSelectionPolicy.Equals(thatVectorClock.ActiveClusterSelectionPolicy) { if !thisVectorClock.StartTimestamp.Equal(thatVectorClock.StartTimestamp) { return thatVectorClock.StartTimestamp.Before(thisVectorClock.StartTimestamp) } return thisVectorClock.RunID > thatVectorClock.RunID } if thisVectorClock.LastWriteVersion != thatVectorClock.LastWriteVersion { return thisVectorClock.LastWriteVersion > thatVectorClock.LastWriteVersion } // thisVectorClock.LastWriteVersion == thatVectorClock.LastWriteVersion return thisVectorClock.LastEventTaskID > thatVectorClock.LastEventTaskID } ================================================ FILE: service/history/execution/workflow_execution_util.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import "github.com/uber/cadence/common/types" // TerminateWorkflow is a helper function to terminate workflow func TerminateWorkflow( mutableState MutableState, eventBatchFirstEventID int64, terminateReason string, terminateDetails []byte, terminateIdentity string, ) error { if decision, ok := mutableState.GetInFlightDecision(); ok { if err := FailDecision( mutableState, decision, types.DecisionTaskFailedCauseForceCloseDecision, ); err != nil { return err } } _, err := mutableState.AddWorkflowExecutionTerminatedEvent( eventBatchFirstEventID, terminateReason, terminateDetails, terminateIdentity, ) return err } ================================================ FILE: service/history/execution/workflow_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: workflow.go // // Generated by this command: // // mockgen -package execution -source workflow.go -destination workflow_mock.go -self_package github.com/uber/cadence/service/history/execution // // Package execution is a generated GoMock package. package execution import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockWorkflow is a mock of Workflow interface. type MockWorkflow struct { ctrl *gomock.Controller recorder *MockWorkflowMockRecorder isgomock struct{} } // MockWorkflowMockRecorder is the mock recorder for MockWorkflow. type MockWorkflowMockRecorder struct { mock *MockWorkflow } // NewMockWorkflow creates a new mock instance. func NewMockWorkflow(ctrl *gomock.Controller) *MockWorkflow { mock := &MockWorkflow{ctrl: ctrl} mock.recorder = &MockWorkflowMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWorkflow) EXPECT() *MockWorkflowMockRecorder { return m.recorder } // FlushBufferedEvents mocks base method. func (m *MockWorkflow) FlushBufferedEvents() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FlushBufferedEvents") ret0, _ := ret[0].(error) return ret0 } // FlushBufferedEvents indicates an expected call of FlushBufferedEvents. func (mr *MockWorkflowMockRecorder) FlushBufferedEvents() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushBufferedEvents", reflect.TypeOf((*MockWorkflow)(nil).FlushBufferedEvents)) } // GetContext mocks base method. func (m *MockWorkflow) GetContext() Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetContext") ret0, _ := ret[0].(Context) return ret0 } // GetContext indicates an expected call of GetContext. func (mr *MockWorkflowMockRecorder) GetContext() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContext", reflect.TypeOf((*MockWorkflow)(nil).GetContext)) } // GetMutableState mocks base method. func (m *MockWorkflow) GetMutableState() MutableState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMutableState") ret0, _ := ret[0].(MutableState) return ret0 } // GetMutableState indicates an expected call of GetMutableState. func (mr *MockWorkflowMockRecorder) GetMutableState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMutableState", reflect.TypeOf((*MockWorkflow)(nil).GetMutableState)) } // GetReleaseFn mocks base method. func (m *MockWorkflow) GetReleaseFn() ReleaseFunc { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReleaseFn") ret0, _ := ret[0].(ReleaseFunc) return ret0 } // GetReleaseFn indicates an expected call of GetReleaseFn. func (mr *MockWorkflowMockRecorder) GetReleaseFn() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReleaseFn", reflect.TypeOf((*MockWorkflow)(nil).GetReleaseFn)) } // GetVectorClock mocks base method. func (m *MockWorkflow) GetVectorClock() (WorkflowVectorClock, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVectorClock") ret0, _ := ret[0].(WorkflowVectorClock) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVectorClock indicates an expected call of GetVectorClock. func (mr *MockWorkflowMockRecorder) GetVectorClock() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVectorClock", reflect.TypeOf((*MockWorkflow)(nil).GetVectorClock)) } // HappensAfter mocks base method. func (m *MockWorkflow) HappensAfter(that Workflow) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HappensAfter", that) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // HappensAfter indicates an expected call of HappensAfter. func (mr *MockWorkflowMockRecorder) HappensAfter(that any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HappensAfter", reflect.TypeOf((*MockWorkflow)(nil).HappensAfter), that) } // Revive mocks base method. func (m *MockWorkflow) Revive() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Revive") ret0, _ := ret[0].(error) return ret0 } // Revive indicates an expected call of Revive. func (mr *MockWorkflowMockRecorder) Revive() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Revive", reflect.TypeOf((*MockWorkflow)(nil).Revive)) } // SuppressBy mocks base method. func (m *MockWorkflow) SuppressBy(incomingWorkflow Workflow) (TransactionPolicy, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SuppressBy", incomingWorkflow) ret0, _ := ret[0].(TransactionPolicy) ret1, _ := ret[1].(error) return ret0, ret1 } // SuppressBy indicates an expected call of SuppressBy. func (mr *MockWorkflowMockRecorder) SuppressBy(incomingWorkflow any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SuppressBy", reflect.TypeOf((*MockWorkflow)(nil).SuppressBy), incomingWorkflow) } ================================================ FILE: service/history/execution/workflow_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package execution import ( "context" "reflect" "runtime" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( workflowSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockContext *MockContext mockMutableState *MockMutableState domainID string domainName string workflowID string runID string } ) func TestWorkflowSuite(t *testing.T) { s := new(workflowSuite) suite.Run(t, s) } func (s *workflowSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockContext = NewMockContext(s.controller) s.mockMutableState = NewMockMutableState(s.controller) s.domainID = uuid.New() s.domainName = "domain-name" s.workflowID = "some random workflow ID" s.runID = uuid.New() } func (s *workflowSuite) TearDownTest() { s.controller.Finish() } func (s *workflowSuite) TestGetMethods() { lastEventTaskID := int64(144) lastEventVersion := int64(12) startTimestamp := time.Now() activeClusterSelectionPolicy := &types.ActiveClusterSelectionPolicy{ ActiveClusterSelectionStrategy: types.ActiveClusterSelectionStrategyRegionSticky.Ptr(), StickyRegion: "region-1", } s.mockMutableState.EXPECT().GetLastWriteVersion().Return(lastEventVersion, nil).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, LastEventTaskID: lastEventTaskID, StartTimestamp: startTimestamp, ActiveClusterSelectionPolicy: activeClusterSelectionPolicy, }).AnyTimes() nDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, s.mockContext, s.mockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) s.Equal(s.mockContext, nDCWorkflow.GetContext()) s.Equal(s.mockMutableState, nDCWorkflow.GetMutableState()) // NOTE golang does not seem to let people compare functions, easily // link: https://github.com/stretchr/testify/issues/182 // this is a hack to compare 2 functions, being the same expectedReleaseFn := runtime.FuncForPC(reflect.ValueOf(NoopReleaseFn).Pointer()).Name() actualReleaseFn := runtime.FuncForPC(reflect.ValueOf(nDCWorkflow.GetReleaseFn()).Pointer()).Name() s.Equal(expectedReleaseFn, actualReleaseFn) vectorClock, err := nDCWorkflow.GetVectorClock() s.NoError(err) expectedVectorClock := WorkflowVectorClock{ ActiveClusterSelectionPolicy: activeClusterSelectionPolicy, LastWriteVersion: lastEventVersion, LastEventTaskID: lastEventTaskID, StartTimestamp: startTimestamp, RunID: s.runID, } s.Equal(expectedVectorClock, vectorClock) } func (s *workflowSuite) TestSuppressWorkflowBy_Error() { nDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, s.mockContext, s.mockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) incomingMockContext := NewMockContext(s.controller) incomingMockMutableState := NewMockMutableState(s.controller) incomingNDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, incomingMockContext, incomingMockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) // cannot suppress by older workflow lastEventTaskID := int64(144) lastEventVersion := int64(12) s.mockMutableState.EXPECT().GetLastWriteVersion().Return(lastEventVersion, nil).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, LastEventTaskID: lastEventTaskID, }).AnyTimes() incomingRunID := uuid.New() incomingLastEventTaskID := int64(144) incomingLastEventVersion := lastEventVersion - 1 incomingMockMutableState.EXPECT().GetLastWriteVersion().Return(incomingLastEventVersion, nil).AnyTimes() incomingMockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: incomingRunID, LastEventTaskID: incomingLastEventTaskID, }).AnyTimes() _, err := nDCWorkflow.SuppressBy(incomingNDCWorkflow) s.Error(err) } func (s *workflowSuite) TestSuppressWorkflowBy_Terminate() { lastEventID := int64(2) lastEventTaskID := int64(144) lastEventVersion := cluster.TestCurrentClusterInitialFailoverVersion s.mockMutableState.EXPECT().GetNextEventID().Return(lastEventID + 1).AnyTimes() s.mockMutableState.EXPECT().GetLastWriteVersion().Return(lastEventVersion, nil).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, LastEventTaskID: lastEventTaskID, }).AnyTimes() nDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, s.mockContext, s.mockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) incomingRunID := uuid.New() incomingLastEventTaskID := int64(144) incomingLastEventVersion := lastEventVersion + 1 incomingMockContext := NewMockContext(s.controller) incomingMockMutableState := NewMockMutableState(s.controller) incomingNDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, incomingMockContext, incomingMockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) incomingMockMutableState.EXPECT().GetLastWriteVersion().Return(incomingLastEventVersion, nil).AnyTimes() incomingMockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: incomingRunID, LastEventTaskID: incomingLastEventTaskID, }).AnyTimes() s.mockMutableState.EXPECT().UpdateCurrentVersion(lastEventVersion, true).Return(nil).AnyTimes() inFlightDecision := &DecisionInfo{ Version: 1234, ScheduleID: 5678, StartedID: 9012, } s.mockMutableState.EXPECT().GetInFlightDecision().Return(inFlightDecision, true).Times(1) s.mockMutableState.EXPECT().AddDecisionTaskFailedEvent( inFlightDecision.ScheduleID, inFlightDecision.StartedID, types.DecisionTaskFailedCauseFailoverCloseDecision, []byte(nil), IdentityHistoryService, "", "", "", "", int64(0), "", ).Return(&types.HistoryEvent{}, nil).Times(1) s.mockMutableState.EXPECT().FlushBufferedEvents().Return(nil).Times(1) s.mockMutableState.EXPECT().AddWorkflowExecutionTerminatedEvent( lastEventID+1, WorkflowTerminationReason, gomock.Any(), WorkflowTerminationIdentity, ).Return(&types.HistoryEvent{}, nil).Times(1) // if workflow is in zombie or finished state, keep as is s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(false).Times(1) policy, err := nDCWorkflow.SuppressBy(incomingNDCWorkflow) s.NoError(err) s.Equal(TransactionPolicyPassive, policy) s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).Times(1) policy, err = nDCWorkflow.SuppressBy(incomingNDCWorkflow) s.NoError(err) s.Equal(TransactionPolicyActive, policy) } func (s *workflowSuite) TestSuppressWorkflowBy_Zombiefy() { lastEventTaskID := int64(144) lastEventVersion := cluster.TestAlternativeClusterInitialFailoverVersion s.mockMutableState.EXPECT().GetLastWriteVersion().Return(lastEventVersion, nil).AnyTimes() executionInfo := &persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, LastEventTaskID: lastEventTaskID, State: persistence.WorkflowStateRunning, CloseStatus: persistence.WorkflowCloseStatusNone, } s.mockMutableState.EXPECT().GetExecutionInfo().Return(executionInfo).AnyTimes() nDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, s.mockContext, s.mockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) incomingRunID := uuid.New() incomingLastEventTaskID := int64(144) incomingLastEventVersion := lastEventVersion + 1 incomingMockContext := NewMockContext(s.controller) incomingMockMutableState := NewMockMutableState(s.controller) incomingNDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, incomingMockContext, incomingMockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) incomingMockMutableState.EXPECT().GetLastWriteVersion().Return(incomingLastEventVersion, nil).AnyTimes() incomingMockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: incomingRunID, LastEventTaskID: incomingLastEventTaskID, }).AnyTimes() // if workflow is in zombie or finished state, keep as is s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(false).Times(1) policy, err := nDCWorkflow.SuppressBy(incomingNDCWorkflow) s.NoError(err) s.Equal(TransactionPolicyPassive, policy) s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).Times(1) policy, err = nDCWorkflow.SuppressBy(incomingNDCWorkflow) s.NoError(err) s.Equal(TransactionPolicyPassive, policy) s.Equal(persistence.WorkflowStateZombie, executionInfo.State) s.Equal(persistence.WorkflowCloseStatusNone, executionInfo.CloseStatus) } func (s *workflowSuite) TestRevive_Zombie_Error() { s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(persistence.WorkflowStateZombie, persistence.WorkflowCloseStatusNone).Times(1) s.mockMutableState.EXPECT().HasProcessedOrPendingDecision().Return(true).Times(1) s.mockMutableState.EXPECT().UpdateWorkflowStateCloseStatus(persistence.WorkflowStateRunning, persistence.WorkflowCloseStatusNone).Return(&types.InternalServiceError{Message: "error"}).Times(1) nDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, s.mockContext, s.mockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) err := nDCWorkflow.Revive() s.Error(err) } func (s *workflowSuite) TestRevive_Zombie_Success() { s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(persistence.WorkflowStateZombie, persistence.WorkflowCloseStatusNone).Times(1) s.mockMutableState.EXPECT().HasProcessedOrPendingDecision().Return(true).Times(1) s.mockMutableState.EXPECT().UpdateWorkflowStateCloseStatus(persistence.WorkflowStateRunning, persistence.WorkflowCloseStatusNone).Return(nil).Times(1) nDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, s.mockContext, s.mockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) err := nDCWorkflow.Revive() s.NoError(err) } func (s *workflowSuite) TestRevive_NonZombie_Success() { s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(persistence.WorkflowStateCompleted, persistence.WorkflowCloseStatusNone).Times(1) nDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, s.mockContext, s.mockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) err := nDCWorkflow.Revive() s.NoError(err) } func (s *workflowSuite) TestFlushBufferedEvents_Success() { lastWriteVersion := cluster.TestCurrentClusterInitialFailoverVersion lastEventTaskID := int64(144) decision := &DecisionInfo{ ScheduleID: 1, StartedID: 2, } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true) s.mockMutableState.EXPECT().HasBufferedEvents().Return(true) s.mockMutableState.EXPECT().GetLastWriteVersion().Return(lastWriteVersion, nil) s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{LastEventTaskID: lastEventTaskID}).AnyTimes() s.mockMutableState.EXPECT().UpdateCurrentVersion(lastWriteVersion, true).Return(nil) s.mockMutableState.EXPECT().GetInFlightDecision().Return(decision, true) s.mockMutableState.EXPECT().AddDecisionTaskFailedEvent(decision.ScheduleID, decision.StartedID, types.DecisionTaskFailedCauseFailoverCloseDecision, nil, IdentityHistoryService, "", "", "", "", int64(0), "").Return(&types.HistoryEvent{}, nil) s.mockMutableState.EXPECT().FlushBufferedEvents().Return(nil) s.mockMutableState.EXPECT().HasPendingDecision().Return(false) s.mockMutableState.EXPECT().AddDecisionTaskScheduledEvent(false).Return(&DecisionInfo{}, nil) nDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, s.mockContext, s.mockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) err := nDCWorkflow.FlushBufferedEvents() s.NoError(err) } func (s *workflowSuite) TestFlushBufferedEvents_NoBuffer_Success() { s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true) s.mockMutableState.EXPECT().HasBufferedEvents().Return(false) nDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, s.mockContext, s.mockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) err := nDCWorkflow.FlushBufferedEvents() s.NoError(err) } func (s *workflowSuite) TestFlushBufferedEvents_NoDecision_Success() { lastWriteVersion := cluster.TestCurrentClusterInitialFailoverVersion lastEventTaskID := int64(144) s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true) s.mockMutableState.EXPECT().HasBufferedEvents().Return(true) s.mockMutableState.EXPECT().GetLastWriteVersion().Return(lastWriteVersion, nil) s.mockMutableState.EXPECT().GetExecutionInfo().Return( &persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, LastEventTaskID: lastEventTaskID, }, ).AnyTimes() s.mockMutableState.EXPECT().UpdateCurrentVersion(lastWriteVersion, true).Return(nil) s.mockMutableState.EXPECT().GetInFlightDecision().Return(nil, false) nDCWorkflow := NewWorkflow( context.Background(), cluster.TestActiveClusterMetadata, s.mockContext, s.mockMutableState, NoopReleaseFn, testlogger.New(s.T()), ) err := nDCWorkflow.FlushBufferedEvents() s.NoError(err) } func TestWorkflowHappensAfter(t *testing.T) { baseTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.FixedZone("UTC-8", -8*60*60)) utcBaseTime := baseTime.UTC() laterTime := baseTime.Add(time.Hour) // Helper function to create ActiveClusterSelectionPolicy createPolicy := func(scope, name string) *types.ActiveClusterSelectionPolicy { return &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: scope, Name: name, }, } } // Helper function to create WorkflowVectorClock createVectorClock := func(policy *types.ActiveClusterSelectionPolicy, lastWriteVersion, lastEventTaskID int64, startTime time.Time, runID string) WorkflowVectorClock { return WorkflowVectorClock{ ActiveClusterSelectionPolicy: policy, LastWriteVersion: lastWriteVersion, LastEventTaskID: lastEventTaskID, StartTimestamp: startTime, RunID: runID, } } policy1 := createPolicy("region", "region-1") policy2 := createPolicy("region", "region-2") tests := []struct { name string thisVectorClock WorkflowVectorClock thatVectorClock WorkflowVectorClock expected bool }{ { name: "Different policies - this happens after based on start timestamp", thisVectorClock: createVectorClock(policy1, 100, 10, laterTime, "run-1"), thatVectorClock: createVectorClock(policy2, 200, 20, baseTime, "run-2"), expected: true, }, { name: "Different policies - same start timestamp - this happens after based on RunID", thisVectorClock: createVectorClock(policy1, 100, 10, baseTime, "run-z"), thatVectorClock: createVectorClock(policy2, 200, 20, baseTime, "run-a"), expected: true, }, { name: "Different policies - same start timestamp in different time zone - this happens after based on RunID", thisVectorClock: createVectorClock(policy1, 100, 10, baseTime, "run-z"), thatVectorClock: createVectorClock(policy2, 200, 20, utcBaseTime, "run-a"), expected: true, }, { name: "Same policies - this happens after based on LastWriteVersion", thisVectorClock: createVectorClock(policy1, 200, 10, baseTime, "run-1"), thatVectorClock: createVectorClock(policy1, 100, 20, baseTime, "run-2"), expected: true, }, { name: "Same policies and LastWriteVersion - this happens after based on LastEventTaskID", thisVectorClock: createVectorClock(policy1, 100, 20, baseTime, "run-1"), thatVectorClock: createVectorClock(policy1, 100, 10, baseTime, "run-2"), expected: true, }, { name: "Nil policies - this happens after based on LastWriteVersion", thisVectorClock: createVectorClock(nil, 200, 10, laterTime, "run-1"), thatVectorClock: createVectorClock(nil, 100, 20, baseTime, "run-2"), expected: true, }, { name: "One nil policy, one non-nil - this happens after based on start timestamp", thisVectorClock: createVectorClock(nil, 100, 10, laterTime, "run-1"), thatVectorClock: createVectorClock(policy1, 200, 20, baseTime, "run-2"), expected: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := workflowHappensAfter(tt.thisVectorClock, tt.thatVectorClock) assert.Equal(t, tt.expected, result, "workflowHappensAfter result mismatch for test case: %s", tt.name) }) } } ================================================ FILE: service/history/failover/coordinator.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination coordinator_mock.go -self_package github.com/uber/cadence/service/history/failover package failover import ( ctx "context" "sync" "sync/atomic" "time" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" ) const ( notificationChanBufferSize = 1000 receiveChanBufferSize = 1000 cleanupMarkerInterval = 30 * time.Minute invalidMarkerDuration = 1 * time.Hour updateDomainRetryInitialInterval = 50 * time.Millisecond updateDomainRetryCoefficient = 2.0 updateDomainMaxRetry = 2 ) var ( errRecordNotFound = &types.EntityNotExistsError{Message: "Graceful failover record not found in shard coordinator"} ) type ( // Coordinator manages the failover markers on sending and receiving Coordinator interface { common.Daemon NotifyFailoverMarkers(shardID int32, markers []*types.FailoverMarkerAttributes) ReceiveFailoverMarkers(shardIDs []int32, marker *types.FailoverMarkerAttributes) GetFailoverInfo(domainID string) (*types.GetFailoverInfoResponse, error) } coordinatorImpl struct { status int32 notificationChan chan *notificationRequest receiveChan chan *receiveRequest shutdownChan chan struct{} retryPolicy backoff.RetryPolicy recorderLock sync.Mutex recorder map[string]*failoverRecord domainManager persistence.DomainManager historyClient history.Client config *config.Config timeSource clock.TimeSource domainCache cache.DomainCache scope metrics.Scope logger log.Logger } notificationRequest struct { shardID int32 markers []*types.FailoverMarkerAttributes } receiveRequest struct { shardIDs []int32 marker *types.FailoverMarkerAttributes } failoverRecord struct { failoverVersion int64 shards map[int32]struct{} lastUpdatedTime time.Time } ) // NewCoordinator initialize a failover coordinator func NewCoordinator( domainManager persistence.DomainManager, historyClient history.Client, timeSource clock.TimeSource, domainCache cache.DomainCache, config *config.Config, metricsClient metrics.Client, logger log.Logger, ) Coordinator { retryPolicy := backoff.NewExponentialRetryPolicy(updateDomainRetryInitialInterval) retryPolicy.SetBackoffCoefficient(updateDomainRetryCoefficient) retryPolicy.SetMaximumAttempts(updateDomainMaxRetry) return &coordinatorImpl{ status: common.DaemonStatusInitialized, recorder: make(map[string]*failoverRecord), notificationChan: make(chan *notificationRequest, notificationChanBufferSize), receiveChan: make(chan *receiveRequest, receiveChanBufferSize), shutdownChan: make(chan struct{}), retryPolicy: retryPolicy, domainManager: domainManager, historyClient: historyClient, timeSource: timeSource, domainCache: domainCache, config: config, scope: metricsClient.Scope(metrics.FailoverMarkerScope), logger: logger.WithTags(tag.ComponentFailoverCoordinator), } } func (c *coordinatorImpl) Start() { if !atomic.CompareAndSwapInt32( &c.status, common.DaemonStatusInitialized, common.DaemonStatusStarted, ) { return } go c.receiveFailoverMarkersLoop() go c.notifyFailoverMarkerLoop() c.logger.Info("Coordinator state changed", tag.LifeCycleStarted) } func (c *coordinatorImpl) Stop() { if !atomic.CompareAndSwapInt32( &c.status, common.DaemonStatusStarted, common.DaemonStatusStopped, ) { return } close(c.shutdownChan) c.logger.Info("Coordinator state changed", tag.LifeCycleStopped) } func (c *coordinatorImpl) NotifyFailoverMarkers( shardID int32, markers []*types.FailoverMarkerAttributes, ) { c.notificationChan <- ¬ificationRequest{ shardID: shardID, markers: markers, } } func (c *coordinatorImpl) ReceiveFailoverMarkers( shardIDs []int32, marker *types.FailoverMarkerAttributes, ) { c.receiveChan <- &receiveRequest{ shardIDs: shardIDs, marker: marker, } } func (c *coordinatorImpl) GetFailoverInfo( domainID string, ) (*types.GetFailoverInfoResponse, error) { c.recorderLock.Lock() defer c.recorderLock.Unlock() record, ok := c.recorder[domainID] if !ok { return nil, errRecordNotFound } var pendingShards []int32 for i := 0; i < c.config.NumberOfShards; i++ { if _, ok := record.shards[int32(i)]; !ok { pendingShards = append(pendingShards, int32(i)) } } return &types.GetFailoverInfoResponse{ CompletedShardCount: int32(len(record.shards)), PendingShards: pendingShards, }, nil } func (c *coordinatorImpl) receiveFailoverMarkersLoop() { ticker := time.NewTicker(cleanupMarkerInterval) defer ticker.Stop() for { select { case <-c.shutdownChan: return case <-ticker.C: c.cleanupInvalidMarkers() case request := <-c.receiveChan: c.handleFailoverMarkers(request) } } } func (c *coordinatorImpl) notifyFailoverMarkerLoop() { timer := time.NewTimer(backoff.JitDuration( c.config.NotifyFailoverMarkerInterval(), c.config.NotifyFailoverMarkerTimerJitterCoefficient(), )) defer timer.Stop() requestByMarker := make(map[types.FailoverMarkerAttributes]*receiveRequest) for { select { case <-c.shutdownChan: return case notificationReq := <-c.notificationChan: // if there is a shard movement happen, it is fine to have duplicate shard ID in the request // The receiver side will de-dup the shard IDs. See: handleFailoverMarkers aggregateNotificationRequests(notificationReq, requestByMarker) case <-timer.C: if err := c.notifyRemoteCoordinator(requestByMarker); err == nil { requestByMarker = make(map[types.FailoverMarkerAttributes]*receiveRequest) } timer.Reset(backoff.JitDuration( c.config.NotifyFailoverMarkerInterval(), c.config.NotifyFailoverMarkerTimerJitterCoefficient(), )) } } } func (c *coordinatorImpl) handleFailoverMarkers( request *receiveRequest, ) { c.recorderLock.Lock() defer c.recorderLock.Unlock() marker := request.marker domainID := marker.GetDomainID() if record, ok := c.recorder[domainID]; ok { // if the local failover version is smaller than the new received marker, // it means there is another failover happened and the local one should be invalid. if record.failoverVersion < marker.GetFailoverVersion() { delete(c.recorder, domainID) } // if the local failover version is larger than the new received marker, // ignore the incoming marker if record.failoverVersion > marker.GetFailoverVersion() { return } } if _, ok := c.recorder[domainID]; !ok { // initialize the failover record c.recorder[marker.GetDomainID()] = &failoverRecord{ failoverVersion: marker.GetFailoverVersion(), shards: make(map[int32]struct{}), } } record := c.recorder[domainID] record.lastUpdatedTime = c.timeSource.Now() for _, shardID := range request.shardIDs { record.shards[shardID] = struct{}{} } domainName, err := c.domainCache.GetDomainName(domainID) if err != nil { c.logger.Error("Coordinator failed to get domain after receiving all failover markers", tag.WorkflowDomainID(domainID), tag.Error(err), ) c.scope.Tagged(metrics.DomainTag(domainName)).IncCounter(metrics.CadenceFailures) return } if len(record.shards) == c.config.NumberOfShards { if err := domain.CleanPendingActiveState( c.domainManager, domainID, record.failoverVersion, c.retryPolicy, ); err != nil { c.logger.Error("Coordinator failed to update domain after receiving all failover markers", tag.WorkflowDomainID(domainID), tag.Error(err), ) c.scope.IncCounter(metrics.CadenceFailures) return } delete(c.recorder, domainID) now := c.timeSource.Now() c.scope.Tagged( metrics.DomainTag(domainName), ).RecordTimer( metrics.GracefulFailoverLatency, now.Sub(time.Unix(0, marker.GetCreationTime())), ) c.logger.Info("Updated domain from pending-active to active", tag.WorkflowDomainName(domainName), tag.FailoverVersion(marker.FailoverVersion), ) } else { c.scope.Tagged( metrics.DomainTag(domainName), ).RecordTimer( metrics.FailoverMarkerCount, time.Duration(len(record.shards)), ) } } func (c *coordinatorImpl) cleanupInvalidMarkers() { c.recorderLock.Lock() defer c.recorderLock.Unlock() for domainID, record := range c.recorder { if c.timeSource.Now().Sub(record.lastUpdatedTime) > invalidMarkerDuration { delete(c.recorder, domainID) } } } func (c *coordinatorImpl) notifyRemoteCoordinator( requestByMarker map[types.FailoverMarkerAttributes]*receiveRequest, ) error { if len(requestByMarker) > 0 { var tokens []*types.FailoverMarkerToken for _, request := range requestByMarker { tokens = append(tokens, &types.FailoverMarkerToken{ ShardIDs: request.shardIDs, FailoverMarker: request.marker, }) } err := c.historyClient.NotifyFailoverMarkers( ctx.Background(), &types.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: tokens, }, ) if err != nil { c.scope.IncCounter(metrics.FailoverMarkerNotificationFailure) c.logger.Error("Failed to notify failover markers", tag.Error(err)) return err } } return nil } func aggregateNotificationRequests( request *notificationRequest, requestByMarker map[types.FailoverMarkerAttributes]*receiveRequest, ) { for _, marker := range request.markers { markerMask := types.FailoverMarkerAttributes{ DomainID: marker.DomainID, FailoverVersion: marker.FailoverVersion, } if _, ok := requestByMarker[markerMask]; !ok { requestByMarker[markerMask] = &receiveRequest{ shardIDs: []int32{}, marker: marker, } } req := requestByMarker[markerMask] req.shardIDs = append(req.shardIDs, request.shardID) } } ================================================ FILE: service/history/failover/coordinator_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: coordinator.go // // Generated by this command: // // mockgen -package failover -source coordinator.go -destination coordinator_mock.go -self_package github.com/uber/cadence/service/history/failover // // Package failover is a generated GoMock package. package failover import ( reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockCoordinator is a mock of Coordinator interface. type MockCoordinator struct { ctrl *gomock.Controller recorder *MockCoordinatorMockRecorder isgomock struct{} } // MockCoordinatorMockRecorder is the mock recorder for MockCoordinator. type MockCoordinatorMockRecorder struct { mock *MockCoordinator } // NewMockCoordinator creates a new mock instance. func NewMockCoordinator(ctrl *gomock.Controller) *MockCoordinator { mock := &MockCoordinator{ctrl: ctrl} mock.recorder = &MockCoordinatorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCoordinator) EXPECT() *MockCoordinatorMockRecorder { return m.recorder } // GetFailoverInfo mocks base method. func (m *MockCoordinator) GetFailoverInfo(domainID string) (*types.GetFailoverInfoResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFailoverInfo", domainID) ret0, _ := ret[0].(*types.GetFailoverInfoResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFailoverInfo indicates an expected call of GetFailoverInfo. func (mr *MockCoordinatorMockRecorder) GetFailoverInfo(domainID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFailoverInfo", reflect.TypeOf((*MockCoordinator)(nil).GetFailoverInfo), domainID) } // NotifyFailoverMarkers mocks base method. func (m *MockCoordinator) NotifyFailoverMarkers(shardID int32, markers []*types.FailoverMarkerAttributes) { m.ctrl.T.Helper() m.ctrl.Call(m, "NotifyFailoverMarkers", shardID, markers) } // NotifyFailoverMarkers indicates an expected call of NotifyFailoverMarkers. func (mr *MockCoordinatorMockRecorder) NotifyFailoverMarkers(shardID, markers any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyFailoverMarkers", reflect.TypeOf((*MockCoordinator)(nil).NotifyFailoverMarkers), shardID, markers) } // ReceiveFailoverMarkers mocks base method. func (m *MockCoordinator) ReceiveFailoverMarkers(shardIDs []int32, marker *types.FailoverMarkerAttributes) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceiveFailoverMarkers", shardIDs, marker) } // ReceiveFailoverMarkers indicates an expected call of ReceiveFailoverMarkers. func (mr *MockCoordinatorMockRecorder) ReceiveFailoverMarkers(shardIDs, marker any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveFailoverMarkers", reflect.TypeOf((*MockCoordinator)(nil).ReceiveFailoverMarkers), shardIDs, marker) } // Start mocks base method. func (m *MockCoordinator) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockCoordinatorMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockCoordinator)(nil).Start)) } // Stop mocks base method. func (m *MockCoordinator) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockCoordinatorMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockCoordinator)(nil).Stop)) } ================================================ FILE: service/history/failover/coordinator_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package failover import ( "context" "fmt" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" mmocks "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" ) type ( coordinatorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockResource *resource.Test mockMetadataManager *mmocks.MetadataManager historyClient *history.MockClient config *config.Config coordinator *coordinatorImpl } ) func TestCoordinatorSuite(t *testing.T) { s := new(coordinatorSuite) suite.Run(t, s) } func (s *coordinatorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.History) s.mockMetadataManager = s.mockResource.MetadataMgr s.historyClient = s.mockResource.HistoryClient s.config = config.NewForTest() s.config.NumberOfShards = 2 s.config.NotifyFailoverMarkerInterval = dynamicproperties.GetDurationPropertyFn(10 * time.Millisecond) s.config.NotifyFailoverMarkerTimerJitterCoefficient = dynamicproperties.GetFloatPropertyFn(0.01) s.mockResource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test", nil).AnyTimes() s.coordinator = NewCoordinator( s.mockMetadataManager, s.historyClient, s.mockResource.GetTimeSource(), s.mockResource.GetDomainCache(), s.config, s.mockResource.GetMetricsClient(), s.mockResource.GetLogger(), ).(*coordinatorImpl) } func (s *coordinatorSuite) TearDownTest() { s.controller.Finish() s.mockResource.Finish(s.T()) s.coordinator.Stop() s.mockMetadataManager.AssertExpectations(s.T()) } func (s *coordinatorSuite) TestNotifyFailoverMarkers() { doneCh := make(chan struct{}) attributes := &types.FailoverMarkerAttributes{ DomainID: uuid.New(), FailoverVersion: 1, CreationTime: common.Int64Ptr(1), } s.historyClient.EXPECT().NotifyFailoverMarkers( context.Background(), &types.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: []*types.FailoverMarkerToken{ { ShardIDs: []int32{1, 2}, FailoverMarker: attributes, }, }, }, ).DoAndReturn(func(ctx context.Context, request *types.NotifyFailoverMarkersRequest, opts ...yarpc.CallOption) error { close(doneCh) return nil }).Times(1) s.coordinator.NotifyFailoverMarkers( 1, []*types.FailoverMarkerAttributes{attributes}, ) s.coordinator.NotifyFailoverMarkers( 2, []*types.FailoverMarkerAttributes{attributes}, ) s.coordinator.Start() <-doneCh } func (s *coordinatorSuite) TestNotifyRemoteCoordinator_Empty() { requestByMarker := make(map[types.FailoverMarkerAttributes]*receiveRequest) s.historyClient.EXPECT().NotifyFailoverMarkers(context.Background(), gomock.Any()).Times(0) err := s.coordinator.notifyRemoteCoordinator(requestByMarker) s.NoError(err) } func (s *coordinatorSuite) TestNotifyRemoteCoordinator() { requestByMarker := make(map[types.FailoverMarkerAttributes]*receiveRequest) attributes := types.FailoverMarkerAttributes{ DomainID: uuid.New(), FailoverVersion: 1, CreationTime: common.Int64Ptr(1), } requestByMarker[attributes] = &receiveRequest{ shardIDs: []int32{1, 2, 3}, marker: &attributes, } s.historyClient.EXPECT().NotifyFailoverMarkers( context.Background(), &types.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: []*types.FailoverMarkerToken{ { ShardIDs: []int32{1, 2, 3}, FailoverMarker: &attributes, }, }, }, ).Return(nil).Times(1) err := s.coordinator.notifyRemoteCoordinator(requestByMarker) s.NoError(err) s.Equal(1, len(requestByMarker)) } func (s *coordinatorSuite) TestNotifyRemoteCoordinator_Error() { requestByMarker := make(map[types.FailoverMarkerAttributes]*receiveRequest) attributes := types.FailoverMarkerAttributes{ DomainID: uuid.New(), FailoverVersion: 1, CreationTime: common.Int64Ptr(1), } requestByMarker[attributes] = &receiveRequest{ shardIDs: []int32{1, 2, 3}, marker: &attributes, } s.historyClient.EXPECT().NotifyFailoverMarkers( context.Background(), &types.NotifyFailoverMarkersRequest{ FailoverMarkerTokens: []*types.FailoverMarkerToken{ { ShardIDs: []int32{1, 2, 3}, FailoverMarker: &attributes, }, }, }, ).Return(fmt.Errorf("test")).Times(1) err := s.coordinator.notifyRemoteCoordinator(requestByMarker) s.Error(err) } func (s *coordinatorSuite) TestAggregateNotificationRequests() { requestByMarker := make(map[types.FailoverMarkerAttributes]*receiveRequest) attributes1 := types.FailoverMarkerAttributes{ DomainID: uuid.New(), FailoverVersion: 1, CreationTime: common.Int64Ptr(1), } attributes2 := types.FailoverMarkerAttributes{ DomainID: uuid.New(), FailoverVersion: 2, CreationTime: common.Int64Ptr(2), } request1 := ¬ificationRequest{ shardID: 1, markers: []*types.FailoverMarkerAttributes{&attributes1}, } aggregateNotificationRequests(request1, requestByMarker) request2 := ¬ificationRequest{ shardID: 2, markers: []*types.FailoverMarkerAttributes{&attributes1}, } aggregateNotificationRequests(request2, requestByMarker) request3 := ¬ificationRequest{ shardID: 3, markers: []*types.FailoverMarkerAttributes{&attributes1, &attributes2}, } aggregateNotificationRequests(request3, requestByMarker) attributes1.CreationTime = nil attributes2.CreationTime = nil s.Equal([]int32{1, 2, 3}, requestByMarker[attributes1].shardIDs) s.Equal([]int32{3}, requestByMarker[attributes2].shardIDs) } func (s *coordinatorSuite) TestHandleFailoverMarkers_DeleteExpiredFailoverMarker() { domainID := uuid.New() attributes1 := &types.FailoverMarkerAttributes{ DomainID: domainID, FailoverVersion: 1, CreationTime: common.Int64Ptr(1), } attributes2 := &types.FailoverMarkerAttributes{ DomainID: domainID, FailoverVersion: 2, CreationTime: common.Int64Ptr(1), } request1 := &receiveRequest{ shardIDs: []int32{1}, marker: attributes1, } request2 := &receiveRequest{ shardIDs: []int32{2}, marker: attributes2, } s.coordinator.handleFailoverMarkers(request1) s.coordinator.handleFailoverMarkers(request2) s.Equal(1, len(s.coordinator.recorder)) } func (s *coordinatorSuite) TestHandleFailoverMarkers_IgnoreExpiredFailoverMarker() { domainID := uuid.New() attributes1 := &types.FailoverMarkerAttributes{ DomainID: domainID, FailoverVersion: 1, CreationTime: common.Int64Ptr(1), } attributes2 := &types.FailoverMarkerAttributes{ DomainID: domainID, FailoverVersion: 2, CreationTime: common.Int64Ptr(1), } request1 := &receiveRequest{ shardIDs: []int32{1}, marker: attributes1, } request2 := &receiveRequest{ shardIDs: []int32{2}, marker: attributes2, } s.coordinator.handleFailoverMarkers(request2) s.coordinator.handleFailoverMarkers(request1) s.Equal(1, len(s.coordinator.recorder)) } func (s *coordinatorSuite) TestHandleFailoverMarkers_CleanPendingActiveState_Success() { domainID := uuid.New() attributes1 := &types.FailoverMarkerAttributes{ DomainID: domainID, FailoverVersion: 2, CreationTime: common.Int64Ptr(1), } attributes2 := &types.FailoverMarkerAttributes{ DomainID: domainID, FailoverVersion: 2, CreationTime: common.Int64Ptr(1), } request1 := &receiveRequest{ shardIDs: []int32{1}, marker: attributes1, } request2 := &receiveRequest{ shardIDs: []int32{2}, marker: attributes2, } info := &persistence.DomainInfo{ ID: domainID, Name: uuid.New(), Status: persistence.DomainStatusRegistered, Description: "some random description", OwnerEmail: "some random email", Data: nil, } domainConfig := &persistence.DomainConfig{ Retention: 1, EmitMetric: true, } replicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ { "active", }, }, } s.mockMetadataManager.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: 1, }, nil) s.mockMetadataManager.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{ ID: domainID, }).Return(&persistence.GetDomainResponse{ Info: info, Config: domainConfig, ReplicationConfig: replicationConfig, IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 2, FailoverEndTime: common.Int64Ptr(1), NotificationVersion: 1, }, nil).Times(1) s.mockMetadataManager.On("UpdateDomain", mock.Anything, &persistence.UpdateDomainRequest{ Info: info, Config: domainConfig, ReplicationConfig: replicationConfig, ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 2, FailoverEndTime: nil, NotificationVersion: 1, }).Return(nil).Times(1) s.coordinator.handleFailoverMarkers(request1) s.coordinator.handleFailoverMarkers(request2) s.Equal(0, len(s.coordinator.recorder)) } func (s *coordinatorSuite) TestHandleFailoverMarkers_CleanPendingActiveState_Error() { domainID := uuid.New() attributes1 := &types.FailoverMarkerAttributes{ DomainID: domainID, FailoverVersion: 2, CreationTime: common.Int64Ptr(1), } attributes2 := &types.FailoverMarkerAttributes{ DomainID: domainID, FailoverVersion: 2, CreationTime: common.Int64Ptr(1), } request1 := &receiveRequest{ shardIDs: []int32{1}, marker: attributes1, } request2 := &receiveRequest{ shardIDs: []int32{2}, marker: attributes2, } info := &persistence.DomainInfo{ ID: domainID, Name: uuid.New(), Status: persistence.DomainStatusRegistered, Description: "some random description", OwnerEmail: "some random email", Data: nil, } domainConfig := &persistence.DomainConfig{ Retention: 1, EmitMetric: true, } replicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: "active", Clusters: []*persistence.ClusterReplicationConfig{ { "active", }, }, } s.mockMetadataManager.On("GetMetadata", mock.Anything).Return(&persistence.GetMetadataResponse{ NotificationVersion: 1, }, nil) s.mockMetadataManager.On("GetDomain", mock.Anything, &persistence.GetDomainRequest{ ID: domainID, }).Return(&persistence.GetDomainResponse{ Info: info, Config: domainConfig, ReplicationConfig: replicationConfig, IsGlobalDomain: true, ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 2, FailoverEndTime: common.Int64Ptr(1), NotificationVersion: 1, }, nil).Times(1) s.mockMetadataManager.On("UpdateDomain", mock.Anything, &persistence.UpdateDomainRequest{ Info: info, Config: domainConfig, ReplicationConfig: replicationConfig, ConfigVersion: 1, FailoverVersion: 2, FailoverNotificationVersion: 2, FailoverEndTime: nil, NotificationVersion: 1, }).Return(fmt.Errorf("test error")).Times(3) s.coordinator.handleFailoverMarkers(request1) s.coordinator.handleFailoverMarkers(request2) s.Equal(1, len(s.coordinator.recorder)) } func (s *coordinatorSuite) TestGetFailoverInfo_Success() { domainID := uuid.New() // Add failover marker attributes := &types.FailoverMarkerAttributes{ DomainID: domainID, FailoverVersion: 2, CreationTime: common.Int64Ptr(1), } request := &receiveRequest{ shardIDs: []int32{1}, marker: attributes, } s.coordinator.handleFailoverMarkers(request) resp, err := s.coordinator.GetFailoverInfo(domainID) s.NoError(err) s.Equal(int32(1), resp.GetCompletedShardCount()) s.Contains(resp.GetPendingShards(), int32(0)) } func (s *coordinatorSuite) TestGetFailoverInfo_DomainIDNotFound_Error() { domainID := uuid.New() resp, err := s.coordinator.GetFailoverInfo(domainID) s.Nil(resp) s.Error(err) } ================================================ FILE: service/history/failover/marker_notifier.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination marker_notifier_mock.go -self_package github.com/uber/cadence/service/history/failover package failover import ( "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" ) type ( // MarkerNotifier notifies failover markers to the remote failover coordinator MarkerNotifier interface { common.Daemon } markerNotifierImpl struct { status int32 shutdownCh chan struct{} shard shard.Context config *config.Config failoverCoordinator Coordinator logger log.Logger metrics metrics.Client } ) // NewMarkerNotifier creates a new instance of failover marker notifier func NewMarkerNotifier( shard shard.Context, config *config.Config, failoverCoordinator Coordinator, ) MarkerNotifier { return &markerNotifierImpl{ status: common.DaemonStatusInitialized, shutdownCh: make(chan struct{}, 1), shard: shard, config: config, failoverCoordinator: failoverCoordinator, logger: shard.GetLogger().WithTags(tag.ComponentFailoverMarkerNotifier), metrics: shard.GetMetricsClient(), } } func (m *markerNotifierImpl) Start() { if !atomic.CompareAndSwapInt32( &m.status, common.DaemonStatusInitialized, common.DaemonStatusStarted, ) { return } go m.notifyPendingFailoverMarker() m.logger.Info("Marker notifier state changed", tag.LifeCycleStarted) } func (m *markerNotifierImpl) Stop() { if !atomic.CompareAndSwapInt32( &m.status, common.DaemonStatusStarted, common.DaemonStatusStopped, ) { return } close(m.shutdownCh) m.logger.Info("Marker notifier state changed", tag.LifeCycleStopped) } func (m *markerNotifierImpl) notifyPendingFailoverMarker() { ticker := time.NewTicker(m.config.NotifyFailoverMarkerInterval()) defer ticker.Stop() for { select { case <-m.shutdownCh: return case <-ticker.C: markers, err := m.shard.ValidateAndUpdateFailoverMarkers() if err != nil { m.metrics.IncCounter(metrics.FailoverMarkerScope, metrics.FailoverMarkerUpdateShardFailure) m.logger.Error("Failed to update pending failover markers in shard info.", tag.Error(err)) } if len(markers) > 0 { m.failoverCoordinator.NotifyFailoverMarkers(int32(m.shard.GetShardID()), markers) } } } } ================================================ FILE: service/history/failover/marker_notifier_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: marker_notifier.go // // Generated by this command: // // mockgen -package failover -source marker_notifier.go -destination marker_notifier_mock.go -self_package github.com/uber/cadence/service/history/failover // // Package failover is a generated GoMock package. package failover import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockMarkerNotifier is a mock of MarkerNotifier interface. type MockMarkerNotifier struct { ctrl *gomock.Controller recorder *MockMarkerNotifierMockRecorder isgomock struct{} } // MockMarkerNotifierMockRecorder is the mock recorder for MockMarkerNotifier. type MockMarkerNotifierMockRecorder struct { mock *MockMarkerNotifier } // NewMockMarkerNotifier creates a new mock instance. func NewMockMarkerNotifier(ctrl *gomock.Controller) *MockMarkerNotifier { mock := &MockMarkerNotifier{ctrl: ctrl} mock.recorder = &MockMarkerNotifierMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMarkerNotifier) EXPECT() *MockMarkerNotifierMockRecorder { return m.recorder } // Start mocks base method. func (m *MockMarkerNotifier) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockMarkerNotifierMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockMarkerNotifier)(nil).Start)) } // Stop mocks base method. func (m *MockMarkerNotifier) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockMarkerNotifierMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockMarkerNotifier)(nil).Stop)) } ================================================ FILE: service/history/failover/marker_notifier_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package failover import ( "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" ) type ( markerNotifierSuite struct { suite.Suite *require.Assertions controller *gomock.Controller coordinator *MockCoordinator mockShard *shard.TestContext mockDomainCache *cache.MockDomainCache clusterMetadata cluster.Metadata markerNotifier *markerNotifierImpl } ) func TestMarkerNotifierSuite(t *testing.T) { s := new(markerNotifierSuite) suite.Run(t, s) } func (s *markerNotifierSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) config := config.NewForTest() config.NotifyFailoverMarkerInterval = dynamicproperties.GetDurationPropertyFn(time.Millisecond) s.coordinator = NewMockCoordinator(s.controller) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config, ) s.clusterMetadata = s.mockShard.Resource.ClusterMetadata mockShardManager := s.mockShard.Resource.ShardMgr mockShardManager.On("UpdateShard", mock.Anything, mock.Anything).Return(nil) s.mockDomainCache = s.mockShard.Resource.DomainCache s.markerNotifier = NewMarkerNotifier( s.mockShard, config, s.coordinator, ).(*markerNotifierImpl) } func (s *markerNotifierSuite) TearDownTest() { s.controller.Finish() s.markerNotifier.Stop() } func (s *markerNotifierSuite) TestNotifyPendingFailoverMarker_Shutdown() { close(s.markerNotifier.shutdownCh) s.coordinator.EXPECT().NotifyFailoverMarkers(gomock.Any(), gomock.Any()).Times(0) s.markerNotifier.notifyPendingFailoverMarker() } func (s *markerNotifierSuite) TestNotifyPendingFailoverMarker() { domainID := uuid.New() info := &persistence.DomainInfo{ ID: domainID, Name: domainID, Status: persistence.DomainStatusRegistered, Description: "some random description", OwnerEmail: "some random email", Data: nil, } domainConfig := &persistence.DomainConfig{ Retention: 1, EmitMetric: true, } replicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: s.clusterMetadata.GetCurrentClusterName(), Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: s.clusterMetadata.GetCurrentClusterName(), }, }, } endTime := common.Int64Ptr(time.Now().UnixNano()) domainEntry := cache.NewDomainCacheEntryForTest( info, domainConfig, true, replicationConfig, 1, endTime, 0, 0, 0, ) s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil).AnyTimes() task := &types.FailoverMarkerAttributes{ DomainID: domainID, FailoverVersion: 1, CreationTime: common.Int64Ptr(1), } tasks := []*types.FailoverMarkerAttributes{task} respCh := make(chan error, 1) err := s.mockShard.AddingPendingFailoverMarker(task) s.NoError(err) count := 0 s.coordinator.EXPECT().NotifyFailoverMarkers( int32(s.mockShard.GetShardID()), tasks, ).Do( func( shardID int32, markers []*types.FailoverMarkerAttributes, ) { if count == 0 { count++ respCh <- nil } if count == 1 { close(s.markerNotifier.shutdownCh) } }, ) s.markerNotifier.notifyPendingFailoverMarker() } ================================================ FILE: service/history/handler/handler.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package handler import ( "context" "errors" "fmt" "slices" "sync" "sync/atomic" "time" "github.com/pborman/uuid" "go.uber.org/yarpc/yarpcerrors" "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/quotas/global/algorithm" "github.com/uber/cadence/common/quotas/global/rpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/engine/engineimpl" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/failover" "github.com/uber/cadence/service/history/lookup" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/queuev2" "github.com/uber/cadence/service/history/replication" "github.com/uber/cadence/service/history/resource" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" "github.com/uber/cadence/service/history/workflowcache" ) const ( shardOwnershipTransferDelay = 5 * time.Second ) type ( // handlerImpl is an implementation for history service independent of wire protocol handlerImpl struct { resource.Resource shuttingDown int32 controller shard.Controller tokenSerializer common.TaskTokenSerializer startWG sync.WaitGroup config *config.Config historyEventNotifier events.Notifier rateLimiter quotas.Limiter replicationTaskFetchers replication.TaskFetchers queueTaskProcessor task.Processor failoverCoordinator failover.Coordinator workflowIDCache workflowcache.WFCache ratelimitAggregator algorithm.RequestWeighted queueFactories []queue.Factory replicationBudgetManager cache.Manager } ) var _ Handler = (*handlerImpl)(nil) var _ shard.EngineFactory = (*handlerImpl)(nil) // NewHandler creates a thrift handler for the history service func NewHandler( resource resource.Resource, config *config.Config, wfCache workflowcache.WFCache, ) Handler { handler := &handlerImpl{ Resource: resource, config: config, tokenSerializer: common.NewJSONTaskTokenSerializer(), rateLimiter: quotas.NewDynamicRateLimiter(config.RPS.AsFloat64()), workflowIDCache: wfCache, ratelimitAggregator: resource.GetRatelimiterAlgorithm(), } // prevent us from trying to serve requests before shard controller is started and ready handler.startWG.Add(1) return handler } // Start starts the handler func (h *handlerImpl) Start() { var err error h.replicationTaskFetchers, err = replication.NewTaskFetchers( h.GetLogger(), h.config, h.GetClusterMetadata(), h.GetClientBean(), h.GetMetricsClient(), ) if err != nil { h.GetLogger().Fatal("Creating replication task fetchers failed", tag.Error(err)) } h.replicationTaskFetchers.Start() taskPriorityAssigner := task.NewPriorityAssigner( h.GetClusterMetadata().GetCurrentClusterName(), h.GetDomainCache(), h.GetActiveClusterManager(), h.GetLogger(), h.GetMetricsClient(), h.config, ) h.replicationBudgetManager = cache.NewBudgetManager( "replication-budget-manager", h.config.ReplicationBudgetManagerMaxSizeBytes, h.config.ReplicationBudgetManagerMaxSizeCount, cache.AdmissionOptimistic, 0, h.GetMetricsClient().Scope(metrics.ReplicatorCacheManagerScope, metrics.HostTag(h.config.HostName)), h.GetLogger(), h.config.ReplicationBudgetManagerSoftCapThreshold, ) h.controller = shard.NewShardController( h.Resource, h, h.config, h.replicationBudgetManager, ) var taskProcessor task.Processor taskProcessor, err = task.NewProcessor( taskPriorityAssigner, h.config, h.GetLogger(), h.GetMetricsClient(), h.GetTimeSource(), h.GetDomainCache(), ) if err != nil { h.GetLogger().Fatal("Creating priority task processor failed", tag.Error(err)) } taskRateLimiter := task.NewRateLimiter( h.GetLogger(), h.GetMetricsClient(), h.GetDomainCache(), h.config, h.controller, ) h.queueTaskProcessor = task.NewRateLimitedProcessor(taskProcessor, taskRateLimiter) h.queueTaskProcessor.Start() h.queueFactories = []queue.Factory{ queuev2.NewTransferQueueFactory( h.queueTaskProcessor, h.GetArchiverClient(), h.workflowIDCache, ), queuev2.NewTimerQueueFactory( h.queueTaskProcessor, h.GetArchiverClient(), ), } h.historyEventNotifier = events.NewNotifier(h.GetTimeSource(), h.GetMetricsClient(), h.config.GetShardID) // events notifier must starts before controller h.historyEventNotifier.Start() h.failoverCoordinator = failover.NewCoordinator( h.GetDomainManager(), h.GetHistoryClient(), h.GetTimeSource(), h.GetDomainCache(), h.config, h.GetMetricsClient(), h.GetLogger(), ) if h.config.EnableGracefulFailover() { h.failoverCoordinator.Start() } h.controller.Start() h.startWG.Done() } // Stop stops the handler func (h *handlerImpl) Stop() { h.prepareToShutDown() if h.replicationBudgetManager != nil { h.replicationBudgetManager.Stop() } h.replicationTaskFetchers.Stop() h.controller.Stop() h.queueTaskProcessor.Stop() h.historyEventNotifier.Stop() h.failoverCoordinator.Stop() } // PrepareToStop starts graceful traffic drain in preparation for shutdown func (h *handlerImpl) PrepareToStop(remainingTime time.Duration) time.Duration { h.GetLogger().Info("ShutdownHandler: Initiating shardController shutdown") h.controller.PrepareToStop() h.GetLogger().Info("ShutdownHandler: Waiting for traffic to drain") remainingTime = common.SleepWithMinDuration(shardOwnershipTransferDelay, remainingTime) h.GetLogger().Info("ShutdownHandler: No longer taking rpc requests") h.prepareToShutDown() return remainingTime } func (h *handlerImpl) prepareToShutDown() { atomic.StoreInt32(&h.shuttingDown, 1) } func (h *handlerImpl) isShuttingDown() bool { return atomic.LoadInt32(&h.shuttingDown) != 0 } // CreateEngine is implementation for HistoryEngineFactory used for creating the engine instance for shard func (h *handlerImpl) CreateEngine( shardContext shard.Context, ) engine.Engine { return engineimpl.NewEngineWithShardContext( shardContext, h.GetVisibilityManager(), h.GetMatchingClient(), h.historyEventNotifier, h.config, h.replicationTaskFetchers, h.GetMatchingRawClient(), h.failoverCoordinator, h.queueFactories, ) } // Health is for health check func (h *handlerImpl) Health(ctx context.Context) (*types.HealthStatus, error) { h.startWG.Wait() h.GetLogger().Debug("History health check endpoint reached.") hs := &types.HealthStatus{Ok: true, Msg: "OK"} return hs, nil } // RecordActivityTaskHeartbeat - Record Activity Task Heart beat. func (h *handlerImpl) RecordActivityTaskHeartbeat( ctx context.Context, wrappedRequest *types.HistoryRecordActivityTaskHeartbeatRequest, ) (resp *types.RecordActivityTaskHeartbeatResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRecordActivityTaskHeartbeatScope) defer sw.Stop() domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } heartbeatRequest := wrappedRequest.HeartbeatRequest token, err0 := h.tokenSerializer.Deserialize(heartbeatRequest.TaskToken) if err0 != nil { err0 = &types.BadRequestError{Message: fmt.Sprintf("Error deserializing task token. Error: %v", err0)} return nil, h.error(err0, scope, domainID, "", "") } err0 = validateTaskToken(token) if err0 != nil { return nil, h.error(err0, scope, domainID, "", "") } workflowID := token.WorkflowID engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, "") } response, err2 := engine.RecordActivityTaskHeartbeat(ctx, wrappedRequest) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, "") } return response, nil } // RecordActivityTaskStarted - Record Activity Task started. func (h *handlerImpl) RecordActivityTaskStarted( ctx context.Context, recordRequest *types.RecordActivityTaskStartedRequest, ) (resp *types.RecordActivityTaskStartedResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRecordActivityTaskStartedScope) defer sw.Stop() domainID := recordRequest.GetDomainUUID() workflowExecution := recordRequest.WorkflowExecution workflowID := workflowExecution.GetWorkflowID() h.emitInfoOrDebugLog( domainID, "RecordActivityTaskStarted", tag.WorkflowDomainID(domainID), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(recordRequest.WorkflowExecution.RunID), tag.WorkflowScheduleID(recordRequest.GetScheduleID()), ) if recordRequest.GetDomainUUID() == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, workflowID, "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, workflowID, "") } engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, "") } response, err2 := engine.RecordActivityTaskStarted(ctx, recordRequest) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, "") } return response, nil } // RecordDecisionTaskStarted - Record Decision Task started. func (h *handlerImpl) RecordDecisionTaskStarted( ctx context.Context, recordRequest *types.RecordDecisionTaskStartedRequest, ) (resp *types.RecordDecisionTaskStartedResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRecordDecisionTaskStartedScope) defer sw.Stop() domainID := recordRequest.GetDomainUUID() workflowExecution := recordRequest.WorkflowExecution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() h.emitInfoOrDebugLog( domainID, "RecordDecisionTaskStarted", tag.WorkflowDomainID(domainID), tag.WorkflowID(workflowExecution.GetWorkflowID()), tag.WorkflowRunID(recordRequest.WorkflowExecution.RunID), tag.WorkflowScheduleID(recordRequest.GetScheduleID()), ) if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, workflowID, runID) } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, workflowID, runID) } if recordRequest.PollRequest == nil || recordRequest.PollRequest.TaskList.GetName() == "" { return nil, h.error(constants.ErrTaskListNotSet, scope, domainID, workflowID, runID) } engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { h.GetLogger().Error("RecordDecisionTaskStarted failed.", tag.Error(err1), tag.WorkflowID(recordRequest.WorkflowExecution.GetWorkflowID()), tag.WorkflowRunID(runID), tag.WorkflowDomainName(domainID), tag.WorkflowRunID(recordRequest.WorkflowExecution.GetRunID()), tag.WorkflowScheduleID(recordRequest.GetScheduleID()), ) return nil, h.error(err1, scope, domainID, workflowID, runID) } response, err2 := engine.RecordDecisionTaskStarted(ctx, recordRequest) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, runID) } return response, nil } // RespondActivityTaskCompleted - records completion of an activity task func (h *handlerImpl) RespondActivityTaskCompleted( ctx context.Context, wrappedRequest *types.HistoryRespondActivityTaskCompletedRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRespondActivityTaskCompletedScope) defer sw.Stop() domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } completeRequest := wrappedRequest.CompleteRequest token, err0 := h.tokenSerializer.Deserialize(completeRequest.TaskToken) if err0 != nil { err0 = &types.BadRequestError{Message: fmt.Sprintf("Error deserializing task token. Error: %v", err0)} return h.error(err0, scope, domainID, "", "") } err0 = validateTaskToken(token) if err0 != nil { return h.error(err0, scope, domainID, "", "") } workflowID := token.WorkflowID runID := token.RunID engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.RespondActivityTaskCompleted(ctx, wrappedRequest) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // RespondActivityTaskFailed - records failure of an activity task func (h *handlerImpl) RespondActivityTaskFailed( ctx context.Context, wrappedRequest *types.HistoryRespondActivityTaskFailedRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRespondActivityTaskFailedScope) defer sw.Stop() domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } failRequest := wrappedRequest.FailedRequest token, err0 := h.tokenSerializer.Deserialize(failRequest.TaskToken) if err0 != nil { err0 = &types.BadRequestError{Message: fmt.Sprintf("Error deserializing task token. Error: %v", err0)} return h.error(err0, scope, domainID, "", "") } err0 = validateTaskToken(token) if err0 != nil { return h.error(err0, scope, domainID, "", "") } workflowID := token.WorkflowID runID := token.RunID engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.RespondActivityTaskFailed(ctx, wrappedRequest) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // RespondActivityTaskCanceled - records failure of an activity task func (h *handlerImpl) RespondActivityTaskCanceled( ctx context.Context, wrappedRequest *types.HistoryRespondActivityTaskCanceledRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRespondActivityTaskCanceledScope) defer sw.Stop() domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } cancelRequest := wrappedRequest.CancelRequest token, err0 := h.tokenSerializer.Deserialize(cancelRequest.TaskToken) if err0 != nil { err0 = &types.BadRequestError{Message: fmt.Sprintf("Error deserializing task token. Error: %v", err0)} return h.error(err0, scope, domainID, "", "") } err0 = validateTaskToken(token) if err0 != nil { return h.error(err0, scope, domainID, "", "") } workflowID := token.WorkflowID runID := token.RunID engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.RespondActivityTaskCanceled(ctx, wrappedRequest) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // RespondDecisionTaskCompleted - records completion of a decision task func (h *handlerImpl) RespondDecisionTaskCompleted( ctx context.Context, wrappedRequest *types.HistoryRespondDecisionTaskCompletedRequest, ) (resp *types.HistoryRespondDecisionTaskCompletedResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRespondDecisionTaskCompletedScope) defer sw.Stop() domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } completeRequest := wrappedRequest.CompleteRequest if len(completeRequest.Decisions) == 0 { scope.IncCounter(metrics.EmptyCompletionDecisionsCounter) } token, err0 := h.tokenSerializer.Deserialize(completeRequest.TaskToken) if err0 != nil { err0 = &types.BadRequestError{Message: fmt.Sprintf("Error deserializing task token. Error: %v", err0)} return nil, h.error(err0, scope, domainID, "", "") } h.GetLogger().Debug(fmt.Sprintf("RespondDecisionTaskCompleted. DomainID: %v, WorkflowID: %v, RunID: %v, ScheduleID: %v", token.DomainID, token.WorkflowID, token.RunID, token.ScheduleID)) err0 = validateTaskToken(token) if err0 != nil { return nil, h.error(err0, scope, domainID, "", "") } workflowID := token.WorkflowID runID := token.RunID engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, runID) } response, err2 := engine.RespondDecisionTaskCompleted(ctx, wrappedRequest) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, runID) } return response, nil } // RespondDecisionTaskFailed - failed response to decision task func (h *handlerImpl) RespondDecisionTaskFailed( ctx context.Context, wrappedRequest *types.HistoryRespondDecisionTaskFailedRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRespondDecisionTaskFailedScope) defer sw.Stop() domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } failedRequest := wrappedRequest.FailedRequest token, err0 := h.tokenSerializer.Deserialize(failedRequest.TaskToken) if err0 != nil { err0 = &types.BadRequestError{Message: fmt.Sprintf("Error deserializing task token. Error: %v", err0)} return h.error(err0, scope, domainID, "", "") } h.GetLogger().Debug(fmt.Sprintf("RespondDecisionTaskFailed. DomainID: %v, WorkflowID: %v, RunID: %v, ScheduleID: %v", token.DomainID, token.WorkflowID, token.RunID, token.ScheduleID)) if failedRequest != nil && failedRequest.GetCause() == types.DecisionTaskFailedCauseUnhandledDecision { h.GetLogger().Info("Non-Deterministic Error", tag.WorkflowDomainID(token.DomainID), tag.WorkflowID(token.WorkflowID), tag.WorkflowRunID(token.RunID)) domainName, err := h.GetDomainCache().GetDomainName(token.DomainID) var domainTag metrics.Tag if err == nil { domainTag = metrics.DomainTag(domainName) } else { domainTag = metrics.DomainUnknownTag() } scope.Tagged(domainTag).IncCounter(metrics.CadenceErrNonDeterministicCounter) } err0 = validateTaskToken(token) if err0 != nil { return h.error(err0, scope, domainID, "", "") } workflowID := token.WorkflowID runID := token.RunID engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.RespondDecisionTaskFailed(ctx, wrappedRequest) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // StartWorkflowExecution - creates a new workflow execution func (h *handlerImpl) StartWorkflowExecution( ctx context.Context, wrappedRequest *types.HistoryStartWorkflowExecutionRequest, ) (resp *types.StartWorkflowExecutionResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryStartWorkflowExecutionScope) defer sw.Stop() domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } startRequest := wrappedRequest.StartRequest workflowID := startRequest.GetWorkflowID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, "") } response, err2 := engine.StartWorkflowExecution(ctx, wrappedRequest) runID := response.GetRunID() if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, runID) } return response, nil } // DescribeHistoryHost returns information about the internal states of a history host func (h *handlerImpl) DescribeHistoryHost( ctx context.Context, request *types.DescribeHistoryHostRequest, ) (resp *types.DescribeHistoryHostResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() numOfItemsInCacheByID, numOfItemsInCacheByName := h.GetDomainCache().GetCacheSize() status := "" switch h.controller.Status() { case common.DaemonStatusInitialized: status = "initialized" case common.DaemonStatusStarted: status = "started" case common.DaemonStatusStopped: status = "stopped" } resp = &types.DescribeHistoryHostResponse{ NumberOfShards: int32(h.controller.NumShards()), ShardIDs: h.controller.ShardIDs(), DomainCache: &types.DomainCacheInfo{ NumOfItemsInCacheByID: numOfItemsInCacheByID, NumOfItemsInCacheByName: numOfItemsInCacheByName, }, ShardControllerStatus: status, Address: h.GetHostInfo().GetAddress(), } return resp, nil } // RemoveTask returns information about the internal states of a history host func (h *handlerImpl) RemoveTask( ctx context.Context, request *types.RemoveTaskRequest, ) (retError error) { executionMgr, err := h.GetExecutionManager(int(request.GetShardID())) if err != nil { return err } switch taskType := commonconstants.TaskType(request.GetType()); taskType { case commonconstants.TaskTypeTransfer: return executionMgr.CompleteHistoryTask(ctx, &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, TaskKey: persistence.NewImmediateTaskKey(request.GetTaskID()), }) case commonconstants.TaskTypeTimer: return executionMgr.CompleteHistoryTask(ctx, &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, TaskKey: persistence.NewHistoryTaskKey(time.Unix(0, request.GetVisibilityTimestamp()), request.GetTaskID()), }) case commonconstants.TaskTypeReplication: return executionMgr.CompleteHistoryTask(ctx, &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, TaskKey: persistence.NewImmediateTaskKey(request.GetTaskID()), }) default: return constants.ErrInvalidTaskType } } // CloseShard closes a shard hosted by this instance func (h *handlerImpl) CloseShard( ctx context.Context, request *types.CloseShardRequest, ) (retError error) { h.controller.RemoveEngineForShard(int(request.GetShardID())) return nil } // ResetQueue resets processing queue states func (h *handlerImpl) ResetQueue( ctx context.Context, request *types.ResetQueueRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryResetQueueScope) defer sw.Stop() engine, err := h.controller.GetEngineForShard(int(request.GetShardID())) if err != nil { return h.error(err, scope, "", "", "") } switch taskType := commonconstants.TaskType(request.GetType()); taskType { case commonconstants.TaskTypeTransfer: err = engine.ResetTransferQueue(ctx, request.GetClusterName()) case commonconstants.TaskTypeTimer: err = engine.ResetTimerQueue(ctx, request.GetClusterName()) default: err = constants.ErrInvalidTaskType } if err != nil { return h.error(err, scope, "", "", "") } return nil } // DescribeQueue describes processing queue states func (h *handlerImpl) DescribeQueue( ctx context.Context, request *types.DescribeQueueRequest, ) (resp *types.DescribeQueueResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryDescribeQueueScope) defer sw.Stop() engine, err := h.controller.GetEngineForShard(int(request.GetShardID())) if err != nil { return nil, h.error(err, scope, "", "", "") } switch taskType := commonconstants.TaskType(request.GetType()); taskType { case commonconstants.TaskTypeTransfer: resp, err = engine.DescribeTransferQueue(ctx, request.GetClusterName()) case commonconstants.TaskTypeTimer: resp, err = engine.DescribeTimerQueue(ctx, request.GetClusterName()) default: err = constants.ErrInvalidTaskType } if err != nil { return nil, h.error(err, scope, "", "", "") } return resp, nil } // DescribeMutableState - returns the internal analysis of workflow execution state func (h *handlerImpl) DescribeMutableState( ctx context.Context, request *types.DescribeMutableStateRequest, ) (resp *types.DescribeMutableStateResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryDescribeMutabelStateScope) defer sw.Stop() domainID := request.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } workflowExecution := request.Execution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, runID) } resp, err2 := engine.DescribeMutableState(ctx, request) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, runID) } return resp, nil } // GetMutableState - returns the id of the next event in the execution's history func (h *handlerImpl) GetMutableState( ctx context.Context, getRequest *types.GetMutableStateRequest, ) (resp *types.GetMutableStateResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryGetMutableStateScope) defer sw.Stop() domainID := getRequest.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } workflowExecution := getRequest.Execution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetWorkflowID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, runID) } resp, err2 := engine.GetMutableState(ctx, getRequest) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, runID) } return resp, nil } // PollMutableState - returns the id of the next event in the execution's history func (h *handlerImpl) PollMutableState( ctx context.Context, getRequest *types.PollMutableStateRequest, ) (resp *types.PollMutableStateResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryPollMutableStateScope) defer sw.Stop() domainID := getRequest.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } workflowExecution := getRequest.Execution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, runID) } resp, err2 := engine.PollMutableState(ctx, getRequest) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, runID) } return resp, nil } // DescribeWorkflowExecution returns information about the specified workflow execution. func (h *handlerImpl) DescribeWorkflowExecution( ctx context.Context, request *types.HistoryDescribeWorkflowExecutionRequest, ) (resp *types.DescribeWorkflowExecutionResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryDescribeWorkflowExecutionScope) defer sw.Stop() domainID := request.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } workflowExecution := request.Request.Execution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, runID) } resp, err2 := engine.DescribeWorkflowExecution(ctx, request) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, runID) } return resp, nil } // RequestCancelWorkflowExecution - requests cancellation of a workflow func (h *handlerImpl) RequestCancelWorkflowExecution( ctx context.Context, request *types.HistoryRequestCancelWorkflowExecutionRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRequestCancelWorkflowExecutionScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } domainID := request.GetDomainUUID() if domainID == "" || request.CancelRequest.GetDomain() == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } cancelRequest := request.CancelRequest h.GetLogger().Debug(fmt.Sprintf("RequestCancelWorkflowExecution. DomainID: %v/%v, WorkflowID: %v, RunID: %v.", cancelRequest.GetDomain(), request.GetDomainUUID(), cancelRequest.WorkflowExecution.GetWorkflowID(), cancelRequest.WorkflowExecution.GetRunID())) workflowID := cancelRequest.WorkflowExecution.GetWorkflowID() runID := cancelRequest.WorkflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.RequestCancelWorkflowExecution(ctx, request) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // SignalWorkflowExecution is used to send a signal event to running workflow execution. This results in // WorkflowExecutionSignaled event recorded in the history and a decision task being created for the execution. func (h *handlerImpl) SignalWorkflowExecution( ctx context.Context, wrappedRequest *types.HistorySignalWorkflowExecutionRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistorySignalWorkflowExecutionScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } workflowExecution := wrappedRequest.SignalRequest.WorkflowExecution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.SignalWorkflowExecution(ctx, wrappedRequest) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // SignalWithStartWorkflowExecution is used to ensure sending a signal event to a workflow execution. // If workflow is running, this results in WorkflowExecutionSignaled event recorded in the history // and a decision task being created for the execution. // If workflow is not running or not found, this results in WorkflowExecutionStarted and WorkflowExecutionSignaled // event recorded in history, and a decision task being created for the execution func (h *handlerImpl) SignalWithStartWorkflowExecution( ctx context.Context, wrappedRequest *types.HistorySignalWithStartWorkflowExecutionRequest, ) (resp *types.StartWorkflowExecutionResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistorySignalWithStartWorkflowExecutionScope) defer sw.Stop() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } signalWithStartRequest := wrappedRequest.SignalWithStartRequest workflowID := signalWithStartRequest.GetWorkflowID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, "") } resp, err2 := engine.SignalWithStartWorkflowExecution(ctx, wrappedRequest) if err2 == nil { return resp, nil } // Two simultaneous SignalWithStart requests might try to start a workflow at the same time. // This can result in one of the requests failing with one of two possible errors: // 1) If it is a brand new WF ID, one of the requests can fail with WorkflowExecutionAlreadyStartedError // (createMode is persistence.CreateWorkflowModeBrandNew) // 2) If it an already existing WF ID, one of the requests can fail with a CurrentWorkflowConditionFailedError // (createMode is persisetence.CreateWorkflowModeWorkflowIDReuse) // If either error occurs, just go ahead and retry. It should succeed on the subsequent attempt. var e1 *persistence.WorkflowExecutionAlreadyStartedError var e2 *persistence.CurrentWorkflowConditionFailedError if !errors.As(err2, &e1) && !errors.As(err2, &e2) { return nil, h.error(err2, scope, domainID, workflowID, resp.GetRunID()) } resp, err2 = engine.SignalWithStartWorkflowExecution(ctx, wrappedRequest) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, resp.GetRunID()) } return resp, nil } // RemoveSignalMutableState is used to remove a signal request ID that was previously recorded. This is currently // used to clean execution info when signal decision finished. func (h *handlerImpl) RemoveSignalMutableState( ctx context.Context, wrappedRequest *types.RemoveSignalMutableStateRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRemoveSignalMutableStateScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } workflowExecution := wrappedRequest.WorkflowExecution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.RemoveSignalMutableState(ctx, wrappedRequest) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // TerminateWorkflowExecution terminates an existing workflow execution by recording WorkflowExecutionTerminated event // in the history and immediately terminating the execution instance. func (h *handlerImpl) TerminateWorkflowExecution( ctx context.Context, wrappedRequest *types.HistoryTerminateWorkflowExecutionRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryTerminateWorkflowExecutionScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } workflowExecution := wrappedRequest.TerminateRequest.WorkflowExecution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.TerminateWorkflowExecution(ctx, wrappedRequest) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // ResetWorkflowExecution reset an existing workflow execution // in the history and immediately terminating the execution instance. func (h *handlerImpl) ResetWorkflowExecution( ctx context.Context, wrappedRequest *types.HistoryResetWorkflowExecutionRequest, ) (resp *types.ResetWorkflowExecutionResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryResetWorkflowExecutionScope) defer sw.Stop() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } domainID := wrappedRequest.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } workflowExecution := wrappedRequest.ResetRequest.WorkflowExecution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, runID) } resp, err2 := engine.ResetWorkflowExecution(ctx, wrappedRequest) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, runID) } return resp, nil } // QueryWorkflow queries a types. func (h *handlerImpl) QueryWorkflow( ctx context.Context, request *types.HistoryQueryWorkflowRequest, ) (resp *types.HistoryQueryWorkflowResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryQueryWorkflowScope) defer sw.Stop() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } domainID := request.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } workflowID := request.GetRequest().GetExecution().GetWorkflowID() runID := request.GetRequest().GetExecution().GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return nil, h.error(err1, scope, domainID, workflowID, runID) } resp, err2 := engine.QueryWorkflow(ctx, request) if err2 != nil { return nil, h.error(err2, scope, domainID, workflowID, runID) } return resp, nil } // ScheduleDecisionTask is used for creating a decision task for already started workflow execution. This is mainly // used by transfer queue processor during the processing of StartChildWorkflowExecution task, where it first starts // child execution without creating the decision task and then calls this API after updating the mutable state of // parent execution. func (h *handlerImpl) ScheduleDecisionTask( ctx context.Context, request *types.ScheduleDecisionTaskRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryScheduleDecisionTaskScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } domainID := request.GetDomainUUID() if domainID == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } if request.WorkflowExecution == nil { return h.error(constants.ErrWorkflowExecutionNotSet, scope, domainID, "", "") } workflowExecution := request.WorkflowExecution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.ScheduleDecisionTask(ctx, request) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // RecordChildExecutionCompleted is used for reporting the completion of child workflow execution to parent. // This is mainly called by transfer queue processor during the processing of DeleteExecution task. func (h *handlerImpl) RecordChildExecutionCompleted( ctx context.Context, request *types.RecordChildExecutionCompletedRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRecordChildExecutionCompletedScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } domainID := request.GetDomainUUID() if domainID == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } if request.WorkflowExecution == nil { return h.error(constants.ErrWorkflowExecutionNotSet, scope, domainID, "", "") } workflowExecution := request.WorkflowExecution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.RecordChildExecutionCompleted(ctx, request) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // ResetStickyTaskList reset the volatile information in mutable state of a given types. // Volatile information are the information related to client, such as: // 1. StickyTaskList // 2. StickyScheduleToStartTimeout // 3. ClientLibraryVersion // 4. ClientFeatureVersion // 5. ClientImpl func (h *handlerImpl) ResetStickyTaskList( ctx context.Context, resetRequest *types.HistoryResetStickyTaskListRequest, ) (resp *types.HistoryResetStickyTaskListResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryResetStickyTaskListScope) defer sw.Stop() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } domainID := resetRequest.GetDomainUUID() if domainID == "" { return nil, h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return nil, h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } workflowID := resetRequest.Execution.GetWorkflowID() runID := resetRequest.Execution.GetRunID() engine, err := h.controller.GetEngine(workflowID) if err != nil { return nil, h.error(err, scope, domainID, workflowID, runID) } resp, err = engine.ResetStickyTaskList(ctx, resetRequest) if err != nil { return nil, h.error(err, scope, domainID, workflowID, runID) } return resp, nil } // ReplicateEventsV2 is called by processor to replicate history events for passive domains func (h *handlerImpl) ReplicateEventsV2( ctx context.Context, replicateRequest *types.ReplicateEventsV2Request, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() if h.isShuttingDown() { return constants.ErrShuttingDown } scope, sw := h.startRequestProfile(ctx, metrics.HistoryReplicateEventsV2Scope) defer sw.Stop() domainID := replicateRequest.GetDomainUUID() if domainID == "" { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } workflowExecution := replicateRequest.WorkflowExecution workflowID := workflowExecution.GetWorkflowID() runID := workflowExecution.GetRunID() engine, err1 := h.controller.GetEngine(workflowID) if err1 != nil { return h.error(err1, scope, domainID, workflowID, runID) } err2 := engine.ReplicateEventsV2(ctx, replicateRequest) if err2 != nil { return h.error(err2, scope, domainID, workflowID, runID) } return nil } // SyncShardStatus is called by processor to sync history shard information from another cluster func (h *handlerImpl) SyncShardStatus( ctx context.Context, syncShardStatusRequest *types.SyncShardStatusRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistorySyncShardStatusScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, "", "", "") } if syncShardStatusRequest.SourceCluster == "" { return h.error(constants.ErrSourceClusterNotSet, scope, "", "", "") } if syncShardStatusRequest.Timestamp == nil { return h.error(constants.ErrTimestampNotSet, scope, "", "", "") } // shard ID is already provided in the request engine, err := h.controller.GetEngineForShard(int(syncShardStatusRequest.GetShardID())) if err != nil { return h.error(err, scope, "", "", "") } err = engine.SyncShardStatus(ctx, syncShardStatusRequest) if err != nil { return h.error(err, scope, "", "", "") } return nil } // SyncActivity is called by processor to sync activity func (h *handlerImpl) SyncActivity( ctx context.Context, syncActivityRequest *types.SyncActivityRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistorySyncActivityScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } domainID := syncActivityRequest.GetDomainID() if syncActivityRequest.DomainID == "" || uuid.Parse(syncActivityRequest.GetDomainID()) == nil { return h.error(constants.ErrDomainNotSet, scope, domainID, "", "") } if ok := h.rateLimiter.Allow(); !ok { return h.error(constants.ErrHistoryHostThrottle, scope, domainID, "", "") } if syncActivityRequest.WorkflowID == "" { return h.error(constants.ErrWorkflowIDNotSet, scope, domainID, "", "") } if syncActivityRequest.RunID == "" || uuid.Parse(syncActivityRequest.GetRunID()) == nil { return h.error(constants.ErrRunIDNotValid, scope, domainID, "", "") } workflowID := syncActivityRequest.GetWorkflowID() runID := syncActivityRequest.GetRunID() engine, err := h.controller.GetEngine(workflowID) if err != nil { return h.error(err, scope, domainID, workflowID, runID) } err = engine.SyncActivity(ctx, syncActivityRequest) if err != nil { return h.error(err, scope, domainID, workflowID, runID) } return nil } // GetReplicationMessages is called by remote peers to get replicated messages for cross DC replication func (h *handlerImpl) GetReplicationMessages( ctx context.Context, request *types.GetReplicationMessagesRequest, ) (resp *types.GetReplicationMessagesResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() h.GetLogger().Debug("Received GetReplicationMessages call.") metricsScope, sw := h.startRequestProfile(ctx, metrics.HistoryGetReplicationMessagesScope) defer sw.Stop() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } msgs := h.getReplicationShardMessages(ctx, request) response := h.buildGetReplicationMessagesResponse(metricsScope, msgs) h.GetLogger().Debug("GetReplicationMessages succeeded.") return response, nil } // getReplicationShardMessages gets replication messages from all the shards of the request // it queries the replication tasks from each shard in parallel func (h *handlerImpl) getReplicationShardMessages( ctx context.Context, request *types.GetReplicationMessagesRequest, ) []replicationShardMessages { var ( wg sync.WaitGroup mx sync.Mutex results = make([]replicationShardMessages, 0, len(request.Tokens)) ) wg.Add(len(request.Tokens)) for _, token := range request.Tokens { go func(token *types.ReplicationToken) { defer wg.Done() engine, err := h.controller.GetEngineForShard(int(token.GetShardID())) if err != nil { h.GetLogger().Warn("History engine not found for shard", tag.Error(err)) return } msgs, err := engine.GetReplicationMessages( ctx, request.GetClusterName(), token.GetLastRetrievedMessageID(), ) if err != nil { h.GetLogger().Warn("Failed to get replication tasks for shard", tag.Error(err)) return } mx.Lock() defer mx.Unlock() results = append(results, replicationShardMessages{ ReplicationMessages: msgs, shardID: token.GetShardID(), size: proto.FromReplicationMessages(msgs).Size(), earliestCreationTime: msgs.GetEarliestCreationTime(), }) }(token) } wg.Wait() return results } // buildGetReplicationMessagesResponse builds a new GetReplicationMessagesResponse from shard results // The response can be partial if the total size of the response exceeds the max size. // In this case, responses with oldest replication tasks will be returned func (h *handlerImpl) buildGetReplicationMessagesResponse(metricsScope metrics.Scope, msgs []replicationShardMessages) *types.GetReplicationMessagesResponse { // Shards with large messages can cause the response to exceed the max size. // In this case, we need to skip some shard messages to make sure the result response size is within the limit. // To prevent a replication lag in the future, we should return the messages with the oldest replication task. // So we sort the shard messages by the earliest creation time of the replication task. // If the earliest creation time is the same, we compare the size of the message. // This will sure that shards with the oldest replication tasks will be processed first. sortReplicationShardMessages(msgs) var ( responseSize = 0 maxResponseSize = h.config.MaxResponseSize messagesByShard = make(map[int32]*types.ReplicationMessages, len(msgs)) ) for _, m := range msgs { if (responseSize + m.size) >= maxResponseSize { metricsScope.Tagged(metrics.ShardIDTag(int(m.shardID))).IncCounter(metrics.ReplicationMessageTooLargePerShard) // Log shards that did not fit for debugging purposes h.GetLogger().Warn("Replication messages did not fit in the response (history host)", tag.ShardID(int(m.shardID)), tag.ResponseSize(m.size), tag.ResponseTotalSize(responseSize), tag.ResponseMaxSize(maxResponseSize), ) continue } responseSize += m.size messagesByShard[m.shardID] = m.ReplicationMessages } return &types.GetReplicationMessagesResponse{MessagesByShard: messagesByShard} } // replicationShardMessages wraps types.ReplicationMessages // and contains some metadata of the ReplicationMessages type replicationShardMessages struct { *types.ReplicationMessages // shardID of the ReplicationMessages shardID int32 // size of proto payload of ReplicationMessages size int // earliestCreationTime of ReplicationMessages earliestCreationTime *int64 } // sortReplicationShardMessages sorts the peer responses by the earliest creation time of the replication tasks func sortReplicationShardMessages(msgs []replicationShardMessages) { slices.SortStableFunc(msgs, cmpReplicationShardMessages) } // cmpReplicationShardMessages compares // two replicationShardMessages objects by earliest creation time // it can be used as a comparison func for slices.SortStableFunc // if a's or b's earliestCreationTime is nil, slices.SortStableFunc will put them to the end of a slice // otherwise it will compare the earliestCreationTime of the replication tasks // if earliestCreationTime is equal, it will compare the size of the response func cmpReplicationShardMessages(a, b replicationShardMessages) int { // a > b if a.earliestCreationTime == nil { return 1 } // a < b if b.earliestCreationTime == nil { return -1 } // if both are not nil, compare the creation time if *a.earliestCreationTime < *b.earliestCreationTime { return -1 } if *a.earliestCreationTime > *b.earliestCreationTime { return 1 } // if both equal, compare the size if a.size < b.size { return -1 } if a.size > b.size { return 1 } return 0 } // GetDLQReplicationMessages is called by remote peers to get replicated messages for DLQ merging func (h *handlerImpl) GetDLQReplicationMessages( ctx context.Context, request *types.GetDLQReplicationMessagesRequest, ) (resp *types.GetDLQReplicationMessagesResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() _, sw := h.startRequestProfile(ctx, metrics.HistoryGetDLQReplicationMessagesScope) defer sw.Stop() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } taskInfoPerExecution := map[definition.WorkflowIdentifier][]*types.ReplicationTaskInfo{} // do batch based on workflow ID and run ID for _, taskInfo := range request.GetTaskInfos() { identity := definition.NewWorkflowIdentifier( taskInfo.GetDomainID(), taskInfo.GetWorkflowID(), taskInfo.GetRunID(), ) if _, ok := taskInfoPerExecution[identity]; !ok { taskInfoPerExecution[identity] = []*types.ReplicationTaskInfo{} } taskInfoPerExecution[identity] = append(taskInfoPerExecution[identity], taskInfo) } var wg sync.WaitGroup wg.Add(len(taskInfoPerExecution)) tasksChan := make(chan *types.ReplicationTask, len(request.GetTaskInfos())) handleTaskInfoPerExecution := func(taskInfos []*types.ReplicationTaskInfo) { defer wg.Done() if len(taskInfos) == 0 { return } engine, err := h.controller.GetEngine( taskInfos[0].GetWorkflowID(), ) if err != nil { h.GetLogger().Warn("History engine not found for workflow ID.", tag.Error(err)) return } tasks, err := engine.GetDLQReplicationMessages( ctx, taskInfos, ) if err != nil { h.GetLogger().Error("Failed to get dlq replication tasks.", tag.Error(err)) return } for _, task := range tasks { tasksChan <- task } } for _, replicationTaskInfos := range taskInfoPerExecution { go handleTaskInfoPerExecution(replicationTaskInfos) } wg.Wait() close(tasksChan) replicationTasks := make([]*types.ReplicationTask, 0, len(tasksChan)) for task := range tasksChan { replicationTasks = append(replicationTasks, task) } return &types.GetDLQReplicationMessagesResponse{ ReplicationTasks: replicationTasks, }, nil } // ReapplyEvents applies stale events to the current workflow and the current run func (h *handlerImpl) ReapplyEvents( ctx context.Context, request *types.HistoryReapplyEventsRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryReapplyEventsScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } domainID := request.GetDomainUUID() workflowID := request.GetRequest().GetWorkflowExecution().GetWorkflowID() runID := request.GetRequest().GetWorkflowExecution().GetRunID() engine, err := h.controller.GetEngine(workflowID) if err != nil { return h.error(err, scope, domainID, workflowID, runID) } // deserialize history event object historyEvents, err := h.GetPayloadSerializer().DeserializeBatchEvents(&persistence.DataBlob{ Encoding: commonconstants.EncodingTypeThriftRW, Data: request.GetRequest().GetEvents().GetData(), }) if err != nil { return h.error(err, scope, domainID, workflowID, runID) } execution := request.GetRequest().GetWorkflowExecution() if err := engine.ReapplyEvents( ctx, request.GetDomainUUID(), execution.GetWorkflowID(), execution.GetRunID(), historyEvents, ); err != nil { return h.error(err, scope, domainID, workflowID, runID) } return nil } func (h *handlerImpl) CountDLQMessages( ctx context.Context, request *types.CountDLQMessagesRequest, ) (resp *types.HistoryCountDLQMessagesResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryCountDLQMessagesScope) defer sw.Stop() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } g := &errgroup.Group{} var mu sync.Mutex entries := map[types.HistoryDLQCountKey]int64{} for _, shardID := range h.controller.ShardIDs() { shardID := shardID g.Go(func() (e error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &e) }() engine, err := h.controller.GetEngineForShard(int(shardID)) if err != nil { return fmt.Errorf("dlq count for shard %d: %w", shardID, err) } counts, err := engine.CountDLQMessages(ctx, request.ForceFetch) if err != nil { return fmt.Errorf("dlq count for shard %d: %w", shardID, err) } mu.Lock() defer mu.Unlock() for sourceCluster, count := range counts { key := types.HistoryDLQCountKey{ShardID: shardID, SourceCluster: sourceCluster} entries[key] = count } return nil }) } err := g.Wait() return &types.HistoryCountDLQMessagesResponse{Entries: entries}, h.error(err, scope, "", "", "") } // ReadDLQMessages reads replication DLQ messages func (h *handlerImpl) ReadDLQMessages( ctx context.Context, request *types.ReadDLQMessagesRequest, ) (resp *types.ReadDLQMessagesResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryReadDLQMessagesScope) defer sw.Stop() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } engine, err := h.controller.GetEngineForShard(int(request.GetShardID())) if err != nil { return nil, h.error(err, scope, "", "", "") } return engine.ReadDLQMessages(ctx, request) } // PurgeDLQMessages deletes replication DLQ messages func (h *handlerImpl) PurgeDLQMessages( ctx context.Context, request *types.PurgeDLQMessagesRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryPurgeDLQMessagesScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } engine, err := h.controller.GetEngineForShard(int(request.GetShardID())) if err != nil { return h.error(err, scope, "", "", "") } return engine.PurgeDLQMessages(ctx, request) } // MergeDLQMessages reads and applies replication DLQ messages func (h *handlerImpl) MergeDLQMessages( ctx context.Context, request *types.MergeDLQMessagesRequest, ) (resp *types.MergeDLQMessagesResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } scope, sw := h.startRequestProfile(ctx, metrics.HistoryMergeDLQMessagesScope) defer sw.Stop() engine, err := h.controller.GetEngineForShard(int(request.GetShardID())) if err != nil { return nil, h.error(err, scope, "", "", "") } return engine.MergeDLQMessages(ctx, request) } // RefreshWorkflowTasks refreshes all the tasks of a workflow func (h *handlerImpl) RefreshWorkflowTasks( ctx context.Context, request *types.HistoryRefreshWorkflowTasksRequest) (retError error) { scope, sw := h.startRequestProfile(ctx, metrics.HistoryRefreshWorkflowTasksScope) defer sw.Stop() if h.isShuttingDown() { return constants.ErrShuttingDown } domainID := request.DomainUIID execution := request.GetRequest().GetExecution() workflowID := execution.GetWorkflowID() runID := execution.GetWorkflowID() engine, err := h.controller.GetEngine(workflowID) if err != nil { return h.error(err, scope, domainID, workflowID, runID) } err = engine.RefreshWorkflowTasks( ctx, domainID, types.WorkflowExecution{ WorkflowID: execution.WorkflowID, RunID: execution.RunID, }, ) if err != nil { return h.error(err, scope, domainID, workflowID, runID) } return nil } // NotifyFailoverMarkers sends the failover markers to failover coordinator. // The coordinator decides when the failover finishes based on received failover marker. func (h *handlerImpl) NotifyFailoverMarkers( ctx context.Context, request *types.NotifyFailoverMarkersRequest, ) (retError error) { _, sw := h.startRequestProfile(ctx, metrics.HistoryNotifyFailoverMarkersScope) defer sw.Stop() for _, token := range request.GetFailoverMarkerTokens() { marker := token.GetFailoverMarker() h.GetLogger().Debug("Handling failover maker", tag.WorkflowDomainID(marker.GetDomainID())) h.failoverCoordinator.ReceiveFailoverMarkers(token.GetShardIDs(), token.GetFailoverMarker()) } return nil } func (h *handlerImpl) GetCrossClusterTasks( ctx context.Context, request *types.GetCrossClusterTasksRequest, ) (resp *types.GetCrossClusterTasksResponse, retError error) { return nil, types.BadRequestError{Message: "The cross-cluster feature has been deprecated."} } func (h *handlerImpl) RespondCrossClusterTasksCompleted( ctx context.Context, request *types.RespondCrossClusterTasksCompletedRequest, ) (resp *types.RespondCrossClusterTasksCompletedResponse, retError error) { return nil, types.BadRequestError{Message: "The cross-cluster feature has been deprecated"} } func (h *handlerImpl) GetFailoverInfo( ctx context.Context, request *types.GetFailoverInfoRequest, ) (resp *types.GetFailoverInfoResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryGetFailoverInfoScope) defer sw.Stop() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } resp, err := h.failoverCoordinator.GetFailoverInfo(request.GetDomainID()) if err != nil { return nil, h.error(err, scope, request.GetDomainID(), "", "") } return resp, nil } func (h *handlerImpl) RatelimitUpdate( ctx context.Context, request *types.RatelimitUpdateRequest, ) (_ *types.RatelimitUpdateResponse, retError error) { defer func() { log.CapturePanic(recover(), h.GetLogger(), &retError) }() h.startWG.Wait() scope, sw := h.startRequestProfile(ctx, metrics.HistoryRatelimitUpdateScope) defer sw.Stop() if h.isShuttingDown() { return nil, constants.ErrShuttingDown } // for now there is just one ratelimit-rpc type and one algorithm that makes use of it. // unpack the arg and pass it to the aggregator. // in the future, this should select the algorithm by the Any.ValueType, via a registry of some kind. arg, err := rpc.AnyToAggregatorUpdate(request.Any) if err != nil { return nil, h.error(fmt.Errorf("failed to map data to args: %w", err), scope, "", "", "") } err = h.ratelimitAggregator.Update(arg) if err != nil { return nil, h.error(fmt.Errorf("failed to update ratelimits: %w", err), scope, "", "", "") } // collect the response data and pack it into an Any for the response. // like unpacking, this will eventually be handled by the registry above. // // "_" is ignoring "used RPS" data here. it is likely useful for being friendlier // to brief, bursty-but-within-limits load, but that has not yet been built. weights, err := h.ratelimitAggregator.HostUsage(arg.ID, maps.Keys(arg.Load)) if err != nil { return nil, h.error(fmt.Errorf("failed to retrieve updated weights: %w", err), scope, "", "", "") } resAny, err := rpc.AggregatorWeightsToAny(weights) if err != nil { return nil, h.error(fmt.Errorf("failed to Any-package response: %w", err), scope, "", "", "") } return &types.RatelimitUpdateResponse{ Any: resAny, }, nil } // convertError is a helper method to convert ShardOwnershipLostError from persistence layer returned by various // HistoryEngine API calls to ShardOwnershipLost error return by HistoryService for client to be redirected to the // correct shard. func (h *handlerImpl) convertError(err error) error { switch err := err.(type) { case *persistence.ShardOwnershipLostError: info, err2 := lookup.HistoryServerByShardID(h.GetMembershipResolver(), err.ShardID) if err2 != nil { return shard.CreateShardOwnershipLostError(h.GetHostInfo(), membership.HostInfo{}) } return shard.CreateShardOwnershipLostError(h.GetHostInfo(), info) case *persistence.WorkflowExecutionAlreadyStartedError: return &types.InternalServiceError{Message: err.Msg} case *persistence.CurrentWorkflowConditionFailedError: return &types.InternalServiceError{Message: err.Msg} case *persistence.TimeoutError: return &types.InternalServiceError{Message: err.Msg} case *persistence.TransactionSizeLimitError: return &types.BadRequestError{Message: err.Msg} } return err } func (h *handlerImpl) updateErrorMetric( scope metrics.Scope, domainID string, workflowID string, runID string, err error, ) { logger := h.GetLogger().Helper() var yarpcE *yarpcerrors.Status var shardOwnershipLostError *types.ShardOwnershipLostError var eventAlreadyStartedError *types.EventAlreadyStartedError var badRequestError *types.BadRequestError var domainNotActiveError *types.DomainNotActiveError var workflowExecutionAlreadyStartedError *types.WorkflowExecutionAlreadyStartedError var entityNotExistsError *types.EntityNotExistsError var workflowExecutionAlreadyCompletedError *types.WorkflowExecutionAlreadyCompletedError var cancellationAlreadyRequestedError *types.CancellationAlreadyRequestedError var limitExceededError *types.LimitExceededError var retryTaskV2Error *types.RetryTaskV2Error var serviceBusyError *types.ServiceBusyError var internalServiceError *types.InternalServiceError var queryFailedError *types.QueryFailedError if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { scope.IncCounter(metrics.CadenceErrContextTimeoutCounter) return } if errors.As(err, &shardOwnershipLostError) { scope.IncCounter(metrics.CadenceErrShardOwnershipLostCounter) } else if errors.As(err, &eventAlreadyStartedError) { scope.IncCounter(metrics.CadenceErrEventAlreadyStartedCounter) } else if errors.As(err, &badRequestError) { scope.IncCounter(metrics.CadenceErrBadRequestCounter) } else if errors.As(err, &domainNotActiveError) { scope.IncCounter(metrics.CadenceErrDomainNotActiveCounter) } else if errors.As(err, &workflowExecutionAlreadyStartedError) { scope.IncCounter(metrics.CadenceErrExecutionAlreadyStartedCounter) } else if errors.As(err, &entityNotExistsError) { scope.IncCounter(metrics.CadenceErrEntityNotExistsCounter) } else if errors.As(err, &workflowExecutionAlreadyCompletedError) { scope.IncCounter(metrics.CadenceErrWorkflowExecutionAlreadyCompletedCounter) } else if errors.As(err, &cancellationAlreadyRequestedError) { scope.IncCounter(metrics.CadenceErrCancellationAlreadyRequestedCounter) } else if errors.As(err, &limitExceededError) { scope.IncCounter(metrics.CadenceErrLimitExceededCounter) } else if errors.As(err, &retryTaskV2Error) { scope.IncCounter(metrics.CadenceErrRetryTaskCounter) } else if errors.As(err, &serviceBusyError) { scope.IncCounter(metrics.CadenceErrServiceBusyCounter) } else if errors.As(err, &queryFailedError) { scope.IncCounter(metrics.CadenceErrQueryFailedCounter) } else if errors.As(err, &yarpcE) { if yarpcE.Code() == yarpcerrors.CodeDeadlineExceeded { scope.IncCounter(metrics.CadenceErrContextTimeoutCounter) } scope.IncCounter(metrics.CadenceFailures) } else if errors.As(err, &internalServiceError) { scope.IncCounter(metrics.CadenceFailures) logger.Error("Internal service error", tag.Error(err), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.WorkflowDomainID(domainID)) } else { // Default / unknown error fallback scope.IncCounter(metrics.CadenceFailures) logger.Error("Uncategorized error", tag.Error(err), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.WorkflowDomainID(domainID)) } } func (h *handlerImpl) error( err error, scope metrics.Scope, domainID string, workflowID string, runID string, ) error { err = h.convertError(err) h.updateErrorMetric(scope, domainID, workflowID, runID, err) return err } func (h *handlerImpl) emitInfoOrDebugLog( domainID string, msg string, tags ...tag.Tag, ) { if h.config.EnableDebugMode && h.config.EnableTaskInfoLogByDomainID(domainID) { h.GetLogger().Info(msg, tags...) } else { h.GetLogger().Debug(msg, tags...) } } func (h *handlerImpl) startRequestProfile(ctx context.Context, scope metrics.ScopeIdx) (metrics.Scope, metrics.Stopwatch) { metricsScope := h.GetMetricsClient().Scope(scope, metrics.GetContextTags(ctx)...) metricsScope.IncCounter(metrics.CadenceRequests) sw := metricsScope.StartTimer(metrics.CadenceLatency) return metricsScope, sw } func validateTaskToken(token *common.TaskToken) error { if token.WorkflowID == "" { return constants.ErrWorkflowIDNotSet } if token.RunID != "" && uuid.Parse(token.RunID) == nil { return constants.ErrRunIDNotValid } return nil } ================================================ FILE: service/history/handler/handler_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package handler import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/goleak" "go.uber.org/mock/gomock" "go.uber.org/yarpc/yarpcerrors" "github.com/uber/cadence/common" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/quotas/global/algorithm" "github.com/uber/cadence/common/quotas/global/rpc" "github.com/uber/cadence/common/quotas/global/shared" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/failover" "github.com/uber/cadence/service/history/resource" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/workflowcache" ) const ( testWorkflowID = "test-workflow-id" testWorkflowRunID = "test-workflow-run-id" testDomainID = "BF80C53A-ED56-4DD9-84EB-BE9AD4E45867" testValidUUID = "FCD00931-EBD4-4028-B67E-4DE624641255" ) type ( handlerSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockResource *resource.Test mockShardController *shard.MockController mockEngine *engine.MockEngine mockWFCache *workflowcache.MockWFCache mockTokenSerializer *common.MockTaskTokenSerializer mockHistoryEventNotifier *events.MockNotifier mockRatelimiter *quotas.MockLimiter mockFailoverCoordinator *failover.MockCoordinator handler *handlerImpl } ) func TestHandlerSuite(t *testing.T) { s := new(handlerSuite) suite.Run(t, s) } func (s *handlerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.History) s.mockResource.Logger = testlogger.New(s.Suite.T()) s.mockShardController = shard.NewMockController(s.controller) s.mockEngine = engine.NewMockEngine(s.controller) s.mockWFCache = workflowcache.NewMockWFCache(s.controller) s.mockFailoverCoordinator = failover.NewMockCoordinator(s.controller) s.handler = NewHandler(s.mockResource, config.NewForTest(), s.mockWFCache).(*handlerImpl) s.handler.controller = s.mockShardController s.mockTokenSerializer = common.NewMockTaskTokenSerializer(s.controller) s.mockRatelimiter = quotas.NewMockLimiter(s.controller) s.handler.rateLimiter = s.mockRatelimiter s.handler.tokenSerializer = s.mockTokenSerializer s.handler.startWG.Done() } func (s *handlerSuite) TearDownTest() { s.controller.Finish() } func (s *handlerSuite) TestHealth() { hs, err := s.handler.Health(context.Background()) s.NoError(err) s.Equal(&types.HealthStatus{Ok: true, Msg: "OK"}, hs) } func (s *handlerSuite) TestRecordActivityTaskHeartbeat() { testInput := map[string]struct { caseName string input *types.HistoryRecordActivityTaskHeartbeatRequest expected *types.RecordActivityTaskHeartbeatResponse expectedError bool }{ "valid input": { caseName: "valid input", input: &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: testDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: []byte("task-token"), }, }, expected: &types.RecordActivityTaskHeartbeatResponse{CancelRequested: false}, expectedError: false, }, "empty domainID": { caseName: "empty domainID", input: &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: "", }, expected: nil, expectedError: true, }, "ratelimit exceeded": { caseName: "ratelimit exceeded", input: &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: testDomainID, }, expected: nil, expectedError: true, }, "token deserialization error": { caseName: "token deserialization error", input: &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: testDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: []byte("task-token"), }, }, expected: nil, expectedError: true, }, "invalid task token": { caseName: "invalid task token", input: &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: testDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: []byte("task-token"), }, }, expected: nil, expectedError: true, }, "get engine error": { caseName: "get engine error", input: &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: testDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: []byte("task-token"), }, }, expected: nil, expectedError: true, }, "engine error": { caseName: "engine error", input: &types.HistoryRecordActivityTaskHeartbeatRequest{ DomainUUID: testDomainID, HeartbeatRequest: &types.RecordActivityTaskHeartbeatRequest{ TaskToken: []byte("task-token"), }, }, expected: nil, expectedError: true, }, } for name, input := range testInput { s.Run(name, func() { switch input.caseName { case "valid input": s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), input.input).Return(input.expected, nil).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) case "empty domainID": case "ratelimit exceeded": s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) case "token deserialization error": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(nil, errors.New("some random error")).Times(1) case "invalid task token": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: "", RunID: "", }, nil).Times(1) case "get engine error": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) case "engine error": s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), input.input).Return(nil, errors.New("error")).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) } response, err := s.handler.RecordActivityTaskHeartbeat(context.Background(), input.input) s.Equal(input.expected, response) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestRecordActivityTaskStarted() { testInput := map[string]struct { caseName string input *types.RecordActivityTaskStartedRequest expected *types.RecordActivityTaskStartedResponse expectedError bool }{ "valid input": { caseName: "valid input", input: &types.RecordActivityTaskStartedRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, expected: &types.RecordActivityTaskStartedResponse{Attempt: 1}, expectedError: false, }, "empty domainID": { caseName: "empty domainID", input: &types.RecordActivityTaskStartedRequest{ DomainUUID: "", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, expected: nil, expectedError: true, }, "ratelimit exceeded": { caseName: "ratelimit exceeded", input: &types.RecordActivityTaskStartedRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, expected: nil, expectedError: true, }, "get engine error": { caseName: "get engine error", input: &types.RecordActivityTaskStartedRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, expected: nil, expectedError: true, }, "engine error": { caseName: "engine error", input: &types.RecordActivityTaskStartedRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, expected: nil, expectedError: true, }, } for name, input := range testInput { s.Run(name, func() { switch input.caseName { case "valid input": s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RecordActivityTaskStarted(gomock.Any(), input.input).Return(input.expected, nil).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) case "empty domainID": case "ratelimit exceeded": s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) case "get engine error": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) case "engine error": s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RecordActivityTaskStarted(gomock.Any(), input.input).Return(nil, errors.New("error")).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) } response, err := s.handler.RecordActivityTaskStarted(context.Background(), input.input) s.Equal(input.expected, response) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestRecordDecisionTaskStarted() { testInput := map[string]struct { input *types.RecordDecisionTaskStartedRequest expected *types.RecordDecisionTaskStartedResponse expectedError bool }{ "valid input": { input: &types.RecordDecisionTaskStartedRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: "test-task-list", }, }, }, expected: &types.RecordDecisionTaskStartedResponse{ WorkflowType: &types.WorkflowType{ Name: "test-workflow-type", }, Attempt: 1, }, expectedError: false, }, "empty domainID": { input: &types.RecordDecisionTaskStartedRequest{ DomainUUID: "", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, expected: nil, expectedError: true, }, "ratelimit exceeded": { input: &types.RecordDecisionTaskStartedRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: "test-task-list", }, }, }, expected: nil, expectedError: true, }, "get engine error": { input: &types.RecordDecisionTaskStartedRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: "test-task-list", }, }, }, expected: nil, expectedError: true, }, "engine error": { input: &types.RecordDecisionTaskStartedRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: "test-task-list", }, }, }, expected: nil, expectedError: true, }, "engine error with ShardOwnershipLost": { input: &types.RecordDecisionTaskStartedRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: "test-task-list", }, }, }, expected: nil, expectedError: true, }, "empty poll request": { input: &types.RecordDecisionTaskStartedRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, expected: nil, expectedError: true, }, } for name, input := range testInput { s.Run(name, func() { switch name { case "valid input": s.mockShardController.EXPECT().GetEngine(gomock.Any()).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RecordDecisionTaskStarted(gomock.Any(), input.input).Return(input.expected, nil).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) case "empty domainID": case "ratelimit exceeded": s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) case "get engine error": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) case "engine error": s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RecordDecisionTaskStarted(gomock.Any(), input.input).Return(nil, errors.New("error")).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) case "engine error with ShardOwnershipLost": s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().RecordDecisionTaskStarted(gomock.Any(), input.input).Return(nil, &persistence.ShardOwnershipLostError{ShardID: 123}).Times(1) s.mockResource.MembershipResolver.EXPECT().Lookup(service.History, string(rune(123))) case "empty poll request": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) } response, err := s.handler.RecordDecisionTaskStarted(context.Background(), input.input) s.Equal(input.expected, response) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestRespondActivityTaskCompleted() { testInput := map[string]struct { caseName string input *types.HistoryRespondActivityTaskCompletedRequest expectedError bool }{ "valid input": { caseName: "valid input", input: &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: testDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: []byte("task-token"), Result: []byte("result"), Identity: "identity", }, }, expectedError: false, }, "empty domainID": { caseName: "empty domainID", input: &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: "", }, expectedError: true, }, "ratelimit exceeded": { caseName: "ratelimit exceeded", input: &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: testDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: []byte("task-token"), Result: []byte("result"), Identity: "identity", }, }, expectedError: true, }, "token deserialization error": { caseName: "token deserialization error", input: &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: testDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: []byte("task-token"), Result: []byte("result"), Identity: "identity", }, }, expectedError: true, }, "invalid task token": { caseName: "invalid task token", input: &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: testDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: []byte("task-token"), Result: []byte("result"), Identity: "identity", }, }, expectedError: true, }, "get engine error": { caseName: "get engine error", input: &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: testDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: []byte("task-token"), Result: []byte("result"), Identity: "identity", }, }, expectedError: true, }, "engine error": { caseName: "engine error", input: &types.HistoryRespondActivityTaskCompletedRequest{ DomainUUID: testDomainID, CompleteRequest: &types.RespondActivityTaskCompletedRequest{ TaskToken: []byte("task-token"), Result: []byte("result"), Identity: "identity", }, }, expectedError: true, }, } for name, input := range testInput { s.Run(name, func() { switch input.caseName { case "valid input": s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondActivityTaskCompleted(gomock.Any(), input.input).Return(nil).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) case "empty domainID": case "ratelimit exceeded": s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) case "token deserialization error": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(nil, errors.New("some random error")).Times(1) case "invalid task token": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: "", RunID: "", }, nil).Times(1) case "get engine error": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) case "engine error": s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondActivityTaskCompleted(gomock.Any(), input.input).Return(errors.New("error")).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) } err := s.handler.RespondActivityTaskCompleted(context.Background(), input.input) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestRespondActivityTaskFailed() { testInput := map[string]struct { caseName string input *types.HistoryRespondActivityTaskFailedRequest expectedError bool }{ "valid input": { caseName: "valid input", input: &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: testDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: false, }, "empty domainID": { caseName: "empty domainID", input: &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: "", }, expectedError: true, }, "ratelimit exceeded": { caseName: "ratelimit exceeded", input: &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: testDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: true, }, "token deserialization error": { caseName: "token deserialization error", input: &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: testDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: true, }, "invalid task token": { caseName: "invalid task token", input: &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: testDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: true, }, "get engine error": { caseName: "get engine error", input: &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: testDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: true, }, "engine error": { caseName: "engine error", input: &types.HistoryRespondActivityTaskFailedRequest{ DomainUUID: testDomainID, FailedRequest: &types.RespondActivityTaskFailedRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: true, }, } for name, input := range testInput { s.Run(name, func() { switch input.caseName { case "valid input": s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondActivityTaskFailed(gomock.Any(), input.input).Return(nil).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) case "empty domainID": case "ratelimit exceeded": s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) case "token deserialization error": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(nil, errors.New("some random error")).Times(1) case "invalid task token": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: "", RunID: "", }, nil).Times(1) case "get engine error": s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) case "engine error": s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondActivityTaskFailed(gomock.Any(), input.input).Return(errors.New("error")).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) } err := s.handler.RespondActivityTaskFailed(context.Background(), input.input) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestRespondActivityTaskCanceled() { validInput := &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: testDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, } testInput := map[string]struct { input *types.HistoryRespondActivityTaskCanceledRequest expectedError bool mockFn func() }{ "valid input": { input: validInput, expectedError: false, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondActivityTaskCanceled(gomock.Any(), validInput).Return(nil).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) }, }, "empty domainID": { input: &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: testDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "token deserialization error": { input: &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: testDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(nil, errors.New("some random error")).Times(1) }, }, "invalid task token": { input: &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: testDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: "", RunID: "", }, nil).Times(1) }, }, "get engine error": { input: &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: testDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "engine error": { input: &types.HistoryRespondActivityTaskCanceledRequest{ DomainUUID: testDomainID, CancelRequest: &types.RespondActivityTaskCanceledRequest{ TaskToken: []byte("task-token"), Details: []byte("Details"), Identity: "identity", }, }, expectedError: true, mockFn: func() { s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondActivityTaskCanceled(gomock.Any(), validInput).Return(errors.New("error")).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.RespondActivityTaskCanceled(context.Background(), input.input) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestRespondDecisionTaskCompleted() { validReq := &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: testDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: []byte("task-token"), Decisions: []*types.Decision{ { DecisionType: types.DecisionTypeScheduleActivityTask.Ptr(), }, }, ExecutionContext: nil, Identity: "identity", }, } validResp := &types.HistoryRespondDecisionTaskCompletedResponse{ StartedResponse: &types.RecordDecisionTaskStartedResponse{ WorkflowType: &types.WorkflowType{}, }, } testInput := map[string]struct { input *types.HistoryRespondDecisionTaskCompletedRequest expectedError bool mockFn func() }{ "valid input": { input: validReq, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), validReq).Return(validResp, nil).Times(1) }, }, "empty domainID": { input: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validReq, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "token deserialization error": { input: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: testDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: []byte("task-token"), Decisions: []*types.Decision{}, }, }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(nil, errors.New("some random error")).Times(1) }, }, "invalid task token": { input: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: testDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: []byte("task-token"), Decisions: []*types.Decision{}, }, }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: "", RunID: "", }, nil).Times(1) }, }, "get engine error": { input: &types.HistoryRespondDecisionTaskCompletedRequest{ DomainUUID: testDomainID, CompleteRequest: &types.RespondDecisionTaskCompletedRequest{ TaskToken: []byte("task-token"), Decisions: []*types.Decision{}, }, }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "engine error": { input: validReq, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondDecisionTaskCompleted(gomock.Any(), validReq).Return(nil, errors.New("error")).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.RespondDecisionTaskCompleted(context.Background(), input.input) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestRespondDecisionTaskFailed() { validInput := &types.HistoryRespondDecisionTaskFailedRequest{ DomainUUID: testDomainID, FailedRequest: &types.RespondDecisionTaskFailedRequest{ TaskToken: []byte("task-token"), Cause: types.DecisionTaskFailedCauseBadBinary.Ptr(), Details: []byte("Details"), Identity: "identity", }, } specialInput := &types.HistoryRespondDecisionTaskFailedRequest{ DomainUUID: testDomainID, FailedRequest: &types.RespondDecisionTaskFailedRequest{ TaskToken: []byte("task-token"), Cause: types.DecisionTaskFailedCauseUnhandledDecision.Ptr(), Details: []byte("Details"), Identity: "identity", }, } testInput := map[string]struct { input *types.HistoryRespondDecisionTaskFailedRequest expectedError bool mockFn func() }{ "valid input": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondDecisionTaskFailed(gomock.Any(), validInput).Return(nil).Times(1) }, }, "empty domainID": { input: &types.HistoryRespondDecisionTaskFailedRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "token deserialization error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(nil, errors.New("some random error")).Times(1) }, }, "invalid task token": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: "", RunID: "", }, nil).Times(1) }, }, "get engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondDecisionTaskFailed(gomock.Any(), validInput).Return(errors.New("error")).Times(1) }, }, "special domain": { input: specialInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockResource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("name", nil).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondDecisionTaskFailed(gomock.Any(), specialInput).Return(nil).Times(1) }, }, "special domain2": { input: specialInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockTokenSerializer.EXPECT().Deserialize(gomock.Any()).Return(&common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, nil).Times(1) s.mockResource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("", errors.New("error")).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RespondDecisionTaskFailed(gomock.Any(), specialInput).Return(nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.RespondDecisionTaskFailed(context.Background(), input.input) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestDescribeHistoryHost() { request := &types.DescribeHistoryHostRequest{ HostAddress: common.StringPtr("test"), } mockStatus := map[string]int32{ "initialized": 0, "started": 1, "stopped": 2, } for status, value := range mockStatus { s.mockResource.DomainCache.EXPECT().GetCacheSize().Return(int64(2), int64(3)).Times(1) s.mockShardController.EXPECT().Status().Return(value).Times(1) s.mockShardController.EXPECT().NumShards().Return(1) s.mockShardController.EXPECT().ShardIDs().Return([]int32{0}) resp, err := s.handler.DescribeHistoryHost(context.Background(), request) s.NoError(err) s.Equal(resp.DomainCache, &types.DomainCacheInfo{ NumOfItemsInCacheByID: 2, NumOfItemsInCacheByName: 3, }) s.Equal(resp.ShardControllerStatus, status) } } func (s *handlerSuite) TestRemoveTask() { now := time.Now() testInput := map[string]struct { request *types.RemoveTaskRequest expectedError bool mockFn func() }{ "transfer task": { request: &types.RemoveTaskRequest{ ShardID: 0, Type: common.Int32Ptr(int32(commonconstants.TaskTypeTransfer)), TaskID: int64(1), }, expectedError: false, mockFn: func() { s.mockResource.ExecutionMgr.On("CompleteHistoryTask", mock.Anything, &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, TaskKey: persistence.NewImmediateTaskKey(1), }).Return(nil).Once() }, }, "timer task": { request: &types.RemoveTaskRequest{ ShardID: 0, Type: common.Int32Ptr(int32(commonconstants.TaskTypeTimer)), TaskID: int64(1), VisibilityTimestamp: common.Int64Ptr(int64(now.UnixNano())), }, expectedError: false, mockFn: func() { s.mockResource.ExecutionMgr.On("CompleteHistoryTask", mock.Anything, &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, TaskKey: persistence.NewHistoryTaskKey(time.Unix(0, int64(now.UnixNano())), 1), }).Return(nil).Once() }, }, "replication task": { request: &types.RemoveTaskRequest{ ShardID: 0, Type: common.Int32Ptr(int32(commonconstants.TaskTypeReplication)), TaskID: int64(1), }, expectedError: false, mockFn: func() { s.mockResource.ExecutionMgr.On("CompleteHistoryTask", mock.Anything, &persistence.CompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, TaskKey: persistence.NewImmediateTaskKey(1), }).Return(nil).Once() }, }, "invalid": { request: &types.RemoveTaskRequest{ ShardID: 0, Type: common.Int32Ptr(int32(100)), }, expectedError: true, mockFn: func() {}, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.RemoveTask(context.Background(), input.request) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestCloseShard() { request := &types.CloseShardRequest{ ShardID: 0, } s.mockShardController.EXPECT().RemoveEngineForShard(0).Return().Times(1) err := s.handler.CloseShard(context.Background(), request) s.NoError(err) } func (s *handlerSuite) TestResetQueue() { testInput := map[string]struct { request *types.ResetQueueRequest expectedError bool mockFn func() }{ "getEngine error": { request: &types.ResetQueueRequest{ ShardID: 0, }, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(0).Return(nil, errors.New("error")).Times(1) }, }, "transfer task": { request: &types.ResetQueueRequest{ ShardID: 0, Type: common.Int32Ptr(int32(commonconstants.TaskTypeTransfer)), }, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(0).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ResetTransferQueue(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, }, "timer task": { request: &types.ResetQueueRequest{ ShardID: 0, Type: common.Int32Ptr(int32(commonconstants.TaskTypeTimer)), }, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(0).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ResetTimerQueue(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, }, "invalid task": { request: &types.ResetQueueRequest{ ShardID: 0, Type: common.Int32Ptr(int32(100)), }, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(0).Return(s.mockEngine, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.ResetQueue(context.Background(), input.request) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestDescribeQueue() { testInput := map[string]struct { request *types.DescribeQueueRequest expectedError bool mockFn func() }{ "getEngine error": { request: &types.DescribeQueueRequest{ ShardID: 0, }, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(0).Return(nil, errors.New("error")).Times(1) }, }, "transfer task": { request: &types.DescribeQueueRequest{ ShardID: 0, Type: common.Int32Ptr(int32(commonconstants.TaskTypeTransfer)), }, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(0).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().DescribeTransferQueue(gomock.Any(), gomock.Any()).Return(&types.DescribeQueueResponse{}, nil).Times(1) }, }, "timer task": { request: &types.DescribeQueueRequest{ ShardID: 0, Type: common.Int32Ptr(int32(commonconstants.TaskTypeTimer)), }, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(0).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().DescribeTimerQueue(gomock.Any(), gomock.Any()).Return(&types.DescribeQueueResponse{}, nil).Times(1) }, }, "invalid task": { request: &types.DescribeQueueRequest{ Type: common.Int32Ptr(int32(100)), }, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(0).Return(s.mockEngine, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.DescribeQueue(context.Background(), input.request) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestDescribeMutableState() { validInput := &types.DescribeMutableStateRequest{ DomainUUID: testDomainID, Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, } testInput := map[string]struct { request *types.DescribeMutableStateRequest expectedError bool mockFn func() }{ "empty domainID": { request: &types.DescribeMutableStateRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "getEngine error": { request: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "getMutableState error": { request: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().DescribeMutableState(gomock.Any(), validInput).Return(nil, errors.New("error")).Times(1) }, }, "success": { request: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().DescribeMutableState(gomock.Any(), validInput).Return(&types.DescribeMutableStateResponse{}, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.DescribeMutableState(context.Background(), input.request) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestGetMutableState() { validInput := &types.GetMutableStateRequest{ DomainUUID: testDomainID, Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, } testInput := map[string]struct { request *types.GetMutableStateRequest expectedError bool mockFn func() }{ "empty domainID": { request: &types.GetMutableStateRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit": { request: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "getEngine error": { request: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "getMutableState error": { request: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetMutableState(gomock.Any(), validInput).Return(nil, errors.New("error")).Times(1) }, }, "success": { request: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetMutableState(gomock.Any(), validInput).Return(&types.GetMutableStateResponse{}, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.GetMutableState(context.Background(), input.request) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestPollMutableState() { validInput := &types.PollMutableStateRequest{ DomainUUID: testDomainID, Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, } testInput := map[string]struct { request *types.PollMutableStateRequest expectedError bool mockFn func() }{ "empty domainID": { request: &types.PollMutableStateRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit": { request: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "getEngine error": { request: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "getMutableState error": { request: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().PollMutableState(gomock.Any(), validInput).Return(nil, errors.New("error")).Times(1) }, }, "success": { request: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().PollMutableState(gomock.Any(), validInput).Return(&types.PollMutableStateResponse{}, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.PollMutableState(context.Background(), input.request) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestDescribeWorkflowExecution() { validInput := &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: testDomainID, Request: &types.DescribeWorkflowExecutionRequest{ Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, } testInput := map[string]struct { input *types.HistoryDescribeWorkflowExecutionRequest expectedError bool mockFn func() }{ "valid input": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().DescribeWorkflowExecution(gomock.Any(), validInput).Return(&types.DescribeWorkflowExecutionResponse{}, nil).Times(1) }, }, "empty domainID": { input: &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "get engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().DescribeWorkflowExecution(gomock.Any(), validInput).Return(nil, errors.New("error")).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.DescribeWorkflowExecution(context.Background(), input.input) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestRequestCancelWorkflowExecution() { validInput := &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: testDomainID, CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Domain: "domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, } testInput := map[string]struct { input *types.HistoryRequestCancelWorkflowExecutionRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "valid input": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), validInput).Return(nil).Times(1) }, }, "empty domainID": { input: &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "get engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), validInput).Return(errors.New("error")).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.RequestCancelWorkflowExecution(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestSignalWorkflowExecution() { validInput := &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: testDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ Domain: "domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, } testInput := map[string]struct { input *types.HistorySignalWorkflowExecutionRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "valid input": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().SignalWorkflowExecution(gomock.Any(), validInput).Return(nil).Times(1) }, }, "empty domainID": { input: &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "get engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().SignalWorkflowExecution(gomock.Any(), validInput).Return(errors.New("error")).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.SignalWorkflowExecution(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestSignalWithStartWorkflowExecution() { validInput := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: testDomainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{ WorkflowID: testWorkflowID, }, } testInput := map[string]struct { input *types.HistorySignalWithStartWorkflowExecutionRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "valid input": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), validInput).Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) }, }, "empty domainID": { input: &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "get engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), validInput).Return(nil, errors.New("error")).Times(1) }, }, "special engine error and retry failure": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), validInput).Return(nil, &persistence.WorkflowExecutionAlreadyStartedError{}).Times(1) s.mockEngine.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), validInput).Return(nil, errors.New("error")).Times(1) }, }, "special engine error and retry success": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), validInput).Return(nil, &persistence.CurrentWorkflowConditionFailedError{}).Times(1) s.mockEngine.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), validInput).Return(&types.StartWorkflowExecutionResponse{}, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.SignalWithStartWorkflowExecution(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestRemoveSignalMutableState() { validInput := &types.RemoveSignalMutableStateRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, } testInput := map[string]struct { input *types.RemoveSignalMutableStateRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "valid input": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RemoveSignalMutableState(gomock.Any(), validInput).Return(nil).Times(1) }, }, "empty domainID": { input: &types.RemoveSignalMutableStateRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "get engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RemoveSignalMutableState(gomock.Any(), validInput).Return(errors.New("error")).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.RemoveSignalMutableState(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestTerminateWorkflowExecution() { validInput := &types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: testDomainID, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ Domain: "domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, } testInput := map[string]struct { input *types.HistoryTerminateWorkflowExecutionRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "valid input": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().TerminateWorkflowExecution(gomock.Any(), validInput).Return(nil).Times(1) }, }, "empty domainID": { input: &types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "get engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().TerminateWorkflowExecution(gomock.Any(), validInput).Return(errors.New("error")).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.TerminateWorkflowExecution(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestResetWorkflowExecution() { validInput := &types.HistoryResetWorkflowExecutionRequest{ DomainUUID: testDomainID, ResetRequest: &types.ResetWorkflowExecutionRequest{ Domain: "domain", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, Reason: "test", DecisionFinishEventID: 1, }, } testInput := map[string]struct { input *types.HistoryResetWorkflowExecutionRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "valid input": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ResetWorkflowExecution(gomock.Any(), validInput).Return(&types.ResetWorkflowExecutionResponse{}, nil).Times(1) }, }, "empty domainID": { input: &types.HistoryResetWorkflowExecutionRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "get engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ResetWorkflowExecution(gomock.Any(), validInput).Return(nil, errors.New("error")).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.ResetWorkflowExecution(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestQueryWorkflow() { validInput := &types.HistoryQueryWorkflowRequest{ DomainUUID: testDomainID, Request: &types.QueryWorkflowRequest{ Domain: "domain", Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }, } testInput := map[string]struct { input *types.HistoryQueryWorkflowRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "empty domainID": { input: &types.HistoryQueryWorkflowRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "getEngine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "queryWorkflow error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().QueryWorkflow(gomock.Any(), validInput).Return(nil, errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().QueryWorkflow(gomock.Any(), validInput).Return(&types.HistoryQueryWorkflowResponse{}, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.QueryWorkflow(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestScheduleDecisionTask() { validInput := &types.ScheduleDecisionTaskRequest{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, } testInput := map[string]struct { input *types.ScheduleDecisionTaskRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "empty domainID": { input: &types.ScheduleDecisionTaskRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "getEngine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "scheduleDecisionTask error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ScheduleDecisionTask(gomock.Any(), validInput).Return(errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ScheduleDecisionTask(gomock.Any(), validInput).Return(nil).Times(1) }, }, "empty execution": { input: &types.ScheduleDecisionTaskRequest{ DomainUUID: testDomainID, }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.ScheduleDecisionTask(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestRecordChildExecutionCompleted() { validInput := &types.RecordChildExecutionCompletedRequest{ DomainUUID: testDomainID, InitiatedID: 1, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, } testInput := map[string]struct { input *types.RecordChildExecutionCompletedRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "empty domainID": { input: &types.RecordChildExecutionCompletedRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "getEngine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "recordChildExecutionCompleted error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RecordChildExecutionCompleted(gomock.Any(), validInput).Return(errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RecordChildExecutionCompleted(gomock.Any(), validInput).Return(nil).Times(1) }, }, "empty execution": { input: &types.RecordChildExecutionCompletedRequest{ DomainUUID: testDomainID, }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.RecordChildExecutionCompleted(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestResetStickyTaskList() { validInput := &types.HistoryResetStickyTaskListRequest{ DomainUUID: testDomainID, Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, } testInput := map[string]struct { input *types.HistoryResetStickyTaskListRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "empty domainID": { input: &types.HistoryResetStickyTaskListRequest{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "getEngine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "resetStickyTaskList error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ResetStickyTaskList(gomock.Any(), validInput).Return(nil, errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ResetStickyTaskList(gomock.Any(), validInput).Return(&types.HistoryResetStickyTaskListResponse{}, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.ResetStickyTaskList(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestReplicateEventsV2() { validInput := &types.ReplicateEventsV2Request{ DomainUUID: testDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, VersionHistoryItems: []*types.VersionHistoryItem{ { EventID: 1, Version: 1, }, }, Events: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte{1, 2, 3}, }, } testInput := map[string]struct { input *types.ReplicateEventsV2Request expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "empty domainID": { input: &types.ReplicateEventsV2Request{ DomainUUID: "", }, expectedError: true, mockFn: func() {}, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "getEngine error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "replicateEventsV2 error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ReplicateEventsV2(gomock.Any(), validInput).Return(errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ReplicateEventsV2(gomock.Any(), validInput).Return(nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.ReplicateEventsV2(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestSyncShardStatus() { validInput := &types.SyncShardStatusRequest{ SourceCluster: "test", ShardID: 1, Timestamp: common.Int64Ptr(time.Now().UnixNano()), } testInput := map[string]struct { input *types.SyncShardStatusRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "get shard engine": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngineForShard(int(validInput.ShardID)).Return(nil, errors.New("error")).Times(1) }, }, "syncShardStatus error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngineForShard(int(validInput.ShardID)).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().SyncShardStatus(gomock.Any(), validInput).Return(errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngineForShard(int(validInput.ShardID)).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().SyncShardStatus(gomock.Any(), validInput).Return(nil).Times(1) }, }, "empty sourceCluster": { input: &types.SyncShardStatusRequest{ SourceCluster: "", }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) }, }, "missing timestamp": { input: &types.SyncShardStatusRequest{ SourceCluster: "test", }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.SyncShardStatus(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestSyncActivity() { validInput := &types.SyncActivityRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testValidUUID, Version: 1, ScheduledID: 1, Details: []byte{1, 2, 3}, } testInput := map[string]struct { input *types.SyncActivityRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "ratelimit exceeded": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(false).Times(1) }, }, "empty domainID": { input: &types.SyncActivityRequest{ DomainID: "", }, expectedError: true, mockFn: func() {}, }, "empty workflowID": { input: &types.SyncActivityRequest{ DomainID: testDomainID, WorkflowID: "", }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) }, }, "empty runID": { input: &types.SyncActivityRequest{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: "", }, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) }, }, "cannot get engine": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "syncActivity error": { input: validInput, expectedError: true, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().SyncActivity(gomock.Any(), validInput).Return(errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().SyncActivity(gomock.Any(), validInput).Return(nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.SyncActivity(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestGetReplicationMessages() { validInput := &types.GetReplicationMessagesRequest{ ClusterName: "test", Tokens: []*types.ReplicationToken{ { ShardID: 1, LastRetrievedMessageID: 1, }, { ShardID: 2, LastRetrievedMessageID: 2, }, }, } testInput := map[string]struct { input *types.GetReplicationMessagesRequest mockFn func() expectedError bool expectedResponse *types.GetReplicationMessagesResponse }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, expectedResponse: nil, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(int(validInput.Tokens[0].ShardID)).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetReplicationMessages(gomock.Any(), validInput.ClusterName, validInput.Tokens[0].LastRetrievedMessageID).Return(&types.ReplicationMessages{}, nil).Times(1) s.mockShardController.EXPECT().GetEngineForShard(int(validInput.Tokens[1].ShardID)).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetReplicationMessages(gomock.Any(), validInput.ClusterName, validInput.Tokens[1].LastRetrievedMessageID).Return(&types.ReplicationMessages{}, nil).Times(1) }, expectedResponse: &types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{ 1: {}, 2: {}, }, }, }, "cannot get engine and cannot get task": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(int(validInput.Tokens[0].ShardID)).Return(nil, errors.New("errors")).Times(1) s.mockShardController.EXPECT().GetEngineForShard(int(validInput.Tokens[1].ShardID)).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetReplicationMessages(gomock.Any(), validInput.ClusterName, validInput.Tokens[1].LastRetrievedMessageID).Return(nil, errors.New("errors")).Times(1) }, expectedResponse: &types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{}, }, }, "maxSize exceeds": { input: validInput, expectedError: false, mockFn: func() { s.handler.config.MaxResponseSize = 0 s.mockShardController.EXPECT().GetEngineForShard(int(validInput.Tokens[0].ShardID)).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetReplicationMessages(gomock.Any(), validInput.ClusterName, validInput.Tokens[0].LastRetrievedMessageID).Return(&types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeHistory.Ptr(), }, { TaskType: types.ReplicationTaskTypeHistory.Ptr(), }, }, }, nil).Times(1) s.mockShardController.EXPECT().GetEngineForShard(int(validInput.Tokens[1].ShardID)).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetReplicationMessages(gomock.Any(), validInput.ClusterName, validInput.Tokens[1].LastRetrievedMessageID).Return(&types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeHistory.Ptr(), }, { TaskType: types.ReplicationTaskTypeHistory.Ptr(), }, }, }, nil).Times(1) }, expectedResponse: &types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{}, }, }, "only first shard in response": { input: validInput, expectedError: false, mockFn: func() { firstShardMessages := &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeHistory.Ptr(), CreationTime: common.Int64Ptr(1000), }, { TaskType: types.ReplicationTaskTypeHistory.Ptr(), CreationTime: common.Int64Ptr(1000), }, }, } secondShardMessages := &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeHistory.Ptr(), CreationTime: common.Int64Ptr(100), }, { TaskType: types.ReplicationTaskTypeHistory.Ptr(), CreationTime: common.Int64Ptr(100), }, }, } // we want to allow only the second shard messages to be returned s.handler.config.MaxResponseSize = proto.FromReplicationMessages(secondShardMessages).Size() + 1 s.mockShardController.EXPECT().GetEngineForShard(int(validInput.Tokens[0].ShardID)).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetReplicationMessages(gomock.Any(), validInput.ClusterName, validInput.Tokens[0].LastRetrievedMessageID).Return(firstShardMessages, nil).Times(1) s.mockShardController.EXPECT().GetEngineForShard(int(validInput.Tokens[1].ShardID)).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetReplicationMessages(gomock.Any(), validInput.ClusterName, validInput.Tokens[1].LastRetrievedMessageID).Return(secondShardMessages, nil).Times(1) }, expectedResponse: &types.GetReplicationMessagesResponse{ MessagesByShard: map[int32]*types.ReplicationMessages{ // second shard is older than first shard // so it should only return the second shard messages // because the first shard messages will exceed the max response size 2: { ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeHistory.Ptr(), CreationTime: common.Int64Ptr(100), }, { TaskType: types.ReplicationTaskTypeHistory.Ptr(), CreationTime: common.Int64Ptr(100), }, }, }, }, }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.GetReplicationMessages(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.Equal(input.expectedResponse, resp) s.NoError(err) } goleak.VerifyNone(s.T()) }) } } func (s *handlerSuite) TestGetDLQReplicationMessages() { validInput := &types.GetDLQReplicationMessagesRequest{ TaskInfos: []*types.ReplicationTaskInfo{ { DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, } mockResp := make([]*types.ReplicationTask, 0, 10) mockResp = append(mockResp, &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistory.Ptr(), }) mockEmptyResp := make([]*types.ReplicationTask, 0) testInput := map[string]struct { input *types.GetDLQReplicationMessagesRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngine(gomock.Any()).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetDLQReplicationMessages(gomock.Any(), gomock.Any()).Return(mockResp, nil).Times(1) }, }, "cannot get engine": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngine(gomock.Any()).Return(nil, errors.New("error")).Times(1) }, }, "cannot get task": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngine(gomock.Any()).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetDLQReplicationMessages(gomock.Any(), gomock.Any()).Return(nil, errors.New("error")).Times(1) }, }, "empty task response": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngine(gomock.Any()).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().GetDLQReplicationMessages(gomock.Any(), gomock.Any()).Return(mockEmptyResp, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.GetDLQReplicationMessages(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } goleak.VerifyNone(s.T()) }) } } func (s *handlerSuite) TestReapplyEvents() { validInput := &types.HistoryReapplyEventsRequest{ DomainUUID: testDomainID, Request: &types.ReapplyEventsRequest{ WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, Events: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte{}, }, }, } testInput := map[string]struct { input *types.HistoryReapplyEventsRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "cannot get engine": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "cannot get serialized": { input: &types.HistoryReapplyEventsRequest{ DomainUUID: testDomainID, Request: &types.ReapplyEventsRequest{ WorkflowExecution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, Events: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte{1, 2, 3, 4}, }, }, }, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) }, }, "reapplyEvents error": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ReapplyEvents(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ReapplyEvents(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.ReapplyEvents(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestCountDLQMessages() { validInput := &types.CountDLQMessagesRequest{ ForceFetch: true, } testInput := map[string]struct { input *types.CountDLQMessagesRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "cannot get engine": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().ShardIDs().Return([]int32{0}).Times(1) s.mockShardController.EXPECT().GetEngineForShard(gomock.Any()).Return(nil, errors.New("error")).Times(1) }, }, "countDLQMessages error": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().ShardIDs().Return([]int32{0}).Times(1) s.mockShardController.EXPECT().GetEngineForShard(gomock.Any()).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().CountDLQMessages(gomock.Any(), gomock.Any()).Return(map[string]int64{}, errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().ShardIDs().Return([]int32{0}).Times(1) s.mockShardController.EXPECT().GetEngineForShard(gomock.Any()).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().CountDLQMessages(gomock.Any(), gomock.Any()).Return(map[string]int64{ "test": 1, "test2": 2, }, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() _, err := s.handler.CountDLQMessages(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestReadDLQMessages() { validInput := &types.ReadDLQMessagesRequest{ ShardID: 1, } testInput := map[string]struct { input *types.ReadDLQMessagesRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "get shard engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(1).Return(nil, errors.New("error")).Times(1) }, }, "readDLQMessages error": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(1).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ReadDLQMessages(gomock.Any(), gomock.Any()).Return(nil, errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { resp := &types.ReadDLQMessagesResponse{} s.mockShardController.EXPECT().GetEngineForShard(1).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().ReadDLQMessages(gomock.Any(), gomock.Any()).Return(resp, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() resp, err := s.handler.ReadDLQMessages(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Nil(resp) s.Error(err) } else { s.NotNil(resp) s.NoError(err) } }) } } func (s *handlerSuite) TestPurgeDLQMessages() { validInput := &types.PurgeDLQMessagesRequest{ ShardID: 1, } testInput := map[string]struct { input *types.PurgeDLQMessagesRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "get shard engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(1).Return(nil, errors.New("error")).Times(1) }, }, "purgeDLQMessages error": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(1).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().PurgeDLQMessages(gomock.Any(), gomock.Any()).Return(errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(1).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().PurgeDLQMessages(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.PurgeDLQMessages(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestMergeDLQMessages() { validInput := &types.MergeDLQMessagesRequest{ ShardID: 1, } testInput := map[string]struct { input *types.MergeDLQMessagesRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "get shard engine error": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(1).Return(nil, errors.New("error")).Times(1) }, }, "mergeDLQMessages error": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(1).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().MergeDLQMessages(gomock.Any(), gomock.Any()).Return(nil, errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngineForShard(1).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().MergeDLQMessages(gomock.Any(), gomock.Any()).Return(&types.MergeDLQMessagesResponse{}, nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() _, err := s.handler.MergeDLQMessages(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestRefreshWorkflowTasks() { validInput := &types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: testDomainID, Request: &types.RefreshWorkflowTasksRequest{ Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, }, } testInput := map[string]struct { input *types.HistoryRefreshWorkflowTasksRequest expectedError bool mockFn func() }{ "shutting down": { input: validInput, expectedError: true, mockFn: func() { s.handler.shuttingDown = int32(1) }, }, "cannot get engine": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(nil, errors.New("error")).Times(1) }, }, "refreshWorkflowTasks error": { input: validInput, expectedError: true, mockFn: func() { s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RefreshWorkflowTasks(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("error")).Times(1) }, }, "success": { input: validInput, expectedError: false, mockFn: func() { s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockEngine.EXPECT().RefreshWorkflowTasks(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) }, }, } for name, input := range testInput { s.Run(name, func() { input.mockFn() err := s.handler.RefreshWorkflowTasks(context.Background(), input.input) s.handler.shuttingDown = int32(0) if input.expectedError { s.Error(err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestStartWorkflowExecution() { request := &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: testDomainID, StartRequest: &types.StartWorkflowExecutionRequest{ WorkflowID: testWorkflowID, }, } expectedResponse := &types.StartWorkflowExecutionResponse{ RunID: testWorkflowRunID, } s.mockShardController.EXPECT().GetEngine(testWorkflowID).Return(s.mockEngine, nil).Times(1) s.mockRatelimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(expectedResponse, nil).Times(1) response, err := s.handler.StartWorkflowExecution(context.Background(), request) s.Equal(expectedResponse, response) s.Nil(err) } func (s *handlerSuite) TestEmitInfoOrDebugLog() { // test emitInfoOrDebugLog s.mockResource.Logger = testlogger.New(s.Suite.T()) s.handler.emitInfoOrDebugLog("domain1", "test log") } func (s *handlerSuite) TestValidateTaskToken() { testInput := map[string]struct { taskToken *common.TaskToken expectedError error }{ "valid task token": { taskToken: &common.TaskToken{ WorkflowID: testWorkflowID, RunID: testValidUUID, }, expectedError: nil, }, "empty workflow id": { taskToken: &common.TaskToken{ WorkflowID: "", }, expectedError: constants.ErrWorkflowIDNotSet, }, "invalid run id": { taskToken: &common.TaskToken{ WorkflowID: testWorkflowID, RunID: "invalid", }, expectedError: constants.ErrRunIDNotValid, }, } for name, input := range testInput { s.Run(name, func() { err := validateTaskToken(input.taskToken) s.Equal(input.expectedError, err) }) } } func (s *handlerSuite) TestCorrectUseOfErrorHandling() { tests := map[string]struct { input error expectation func(scope *mocks.Scope) }{ "A deadline exceeded error": { input: context.DeadlineExceeded, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrContextTimeoutCounter).Once() }, }, "A cancelled error": { input: context.Canceled, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrContextTimeoutCounter).Once() }, }, "A shard ownership lost error": { input: &types.ShardOwnershipLostError{ Message: "something is lost", Owner: "owner", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrShardOwnershipLostCounter).Once() }, }, "a workflow is already started": { input: &types.EventAlreadyStartedError{ Message: "workflow already running", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrEventAlreadyStartedCounter).Once() }, }, "a bad request": { input: &types.BadRequestError{ Message: "bad request", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrBadRequestCounter).Once() }, }, "domain is not active": { input: &types.DomainNotActiveError{ Message: "domain not active", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrDomainNotActiveCounter).Once() }, }, "workflow is already started err": { input: &types.WorkflowExecutionAlreadyStartedError{ Message: "bad already started", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrExecutionAlreadyStartedCounter).Once() }, }, "does not exist": { input: &types.EntityNotExistsError{ Message: "the workflow doesn't exist", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrEntityNotExistsCounter).Once() }, }, "already completed": { input: &types.WorkflowExecutionAlreadyCompletedError{ Message: "the workflow is done", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrWorkflowExecutionAlreadyCompletedCounter).Once() }, }, "Cancellation already requested": { input: &types.CancellationAlreadyRequestedError{ Message: "the workflow is cancelled already", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrCancellationAlreadyRequestedCounter).Once() }, }, "rate-limits": { input: &types.LimitExceededError{ Message: "limits exceeded", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrLimitExceededCounter).Once() }, }, "retry tasks": { input: &types.RetryTaskV2Error{ Message: "limits exceeded", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrRetryTaskCounter).Once() }, }, "service busy error": { input: &types.ServiceBusyError{ Message: "limits exceeded - service is busy", }, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrServiceBusyCounter).Once() }, }, "deadline exceeded": { input: yarpcerrors.DeadlineExceededErrorf("some deadline exceeded err"), expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceErrContextTimeoutCounter).Once() scope.Mock.On("IncCounter", metrics.CadenceFailures).Once() }, }, "internal error": { input: types.InternalServiceError{Message: "internal error"}, expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceFailures).Once() }, }, "uncategorized error": { input: errors.New("some random error"), expectation: func(scope *mocks.Scope) { scope.Mock.On("IncCounter", metrics.CadenceFailures).Once() }, }, } for name, td := range tests { s.Run(name, func() { scope := mocks.Scope{} td.expectation(&scope) h := handlerImpl{ Resource: resource.NewTest(s.T(), gomock.NewController(s.T()), 0), } h.error(td.input, &scope, "some-domain", "some-wf", "some-run") // we're doing the args assertion in the On, so using mock.Anything to avoid having to duplicate this // a wrong metric being emitted will fail the mock.On() expectation. This will catch missing calls scope.Mock.AssertCalled(s.T(), "IncCounter", mock.Anything) }) } } func (s *handlerSuite) TestConvertError() { testCases := []struct { name string input error expected error }{ { name: "workflow already started error", input: &persistence.WorkflowExecutionAlreadyStartedError{ Msg: "workflow already started", }, expected: &types.InternalServiceError{ Message: "workflow already started", }, }, { name: "current workflow condition failed error", input: &persistence.CurrentWorkflowConditionFailedError{ Msg: "current workflow condition failed", }, expected: &types.InternalServiceError{ Message: "current workflow condition failed", }, }, { name: "persistence timeout error", input: &persistence.TimeoutError{ Msg: "persistence timeout", }, expected: &types.InternalServiceError{ Message: "persistence timeout", }, }, { name: "transaction size limit error", input: &persistence.TransactionSizeLimitError{ Msg: "transaction size limit", }, expected: &types.BadRequestError{ Message: "transaction size limit", }, }, { name: "shard ownership lost error", input: &persistence.ShardOwnershipLostError{ ShardID: 1, }, expected: &types.ShardOwnershipLostError{ Owner: "127.0.0.1:1234", Message: "Shard is not owned by host: test_host", }, }, } for _, tc := range testCases { s.mockResource.MembershipResolver.EXPECT().Lookup(gomock.Any(), gomock.Any()).Return(membership.NewHostInfo("127.0.0.1:1234"), nil).AnyTimes() err := s.handler.convertError(tc.input) s.Equal(tc.expected, err, tc.name) } } func TestRatelimitUpdate(t *testing.T) { ctrl := gomock.NewController(t) res := resource.NewMockResource(ctrl) res.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).AnyTimes() res.EXPECT().GetLogger().Return(testlogger.New(t)).AnyTimes() update, err := rpc.TestUpdateToAny(t, "testhost", time.Second, map[shared.GlobalKey]rpc.Calls{ "test:domain-user-limit": { Allowed: 10, Rejected: 5, }, }) require.NoError(t, err) alg, err := algorithm.New( metrics.NewNoopMetricsClient(), testlogger.New(t), algorithm.Config{ NewDataWeight: func(opts ...dynamicproperties.FilterOption) float64 { return 0.5 }, UpdateInterval: func(opts ...dynamicproperties.FilterOption) time.Duration { return 3 * time.Second }, DecayAfter: func(opts ...dynamicproperties.FilterOption) time.Duration { return 6 * time.Second }, GcAfter: func(opts ...dynamicproperties.FilterOption) time.Duration { return time.Minute }, }, ) require.NoError(t, err) h := &handlerImpl{ Resource: res, ratelimitAggregator: alg, } resp, err := h.RatelimitUpdate(context.Background(), &types.RatelimitUpdateRequest{ Any: update, }) require.NoError(t, err) w, err := rpc.TestAnyToWeights(t, resp.Any) require.NoError(t, err) assert.Equalf(t, map[shared.GlobalKey]rpc.UpdateEntry{ "test:domain-user-limit": { Weight: 1, // re 10 vs 15: used RPS only tracks accepted. // // this way we don't consider any incorrectly-rejected requests // when calculating our new limits. UsedRPS: 10, }, }, w, "unexpected weights returned from aggregator or serialization. if values differ in a reasonable way, possibly aggregator behavior changed?", ) } func Test_cmpReplicationShardMessages(t *testing.T) { for name, c := range map[string]struct { a, b replicationShardMessages want int }{ "a time is nil, b is empty": { a: replicationShardMessages{earliestCreationTime: nil}, want: 1, }, "a time is not nil, b is empty": { a: replicationShardMessages{earliestCreationTime: common.Int64Ptr(10)}, want: -1, }, "a time is not nil, b time is nil": { a: replicationShardMessages{earliestCreationTime: common.Int64Ptr(10)}, b: replicationShardMessages{earliestCreationTime: nil}, want: -1, }, "a time less b time": { a: replicationShardMessages{earliestCreationTime: common.Int64Ptr(10)}, b: replicationShardMessages{earliestCreationTime: common.Int64Ptr(20)}, want: -1, }, "a time greater b time": { a: replicationShardMessages{earliestCreationTime: common.Int64Ptr(20)}, b: replicationShardMessages{earliestCreationTime: common.Int64Ptr(10)}, want: 1, }, "a size less b size": { a: replicationShardMessages{earliestCreationTime: common.Int64Ptr(10), size: 10}, b: replicationShardMessages{earliestCreationTime: common.Int64Ptr(10), size: 20}, want: -1, }, "a size greater b size": { a: replicationShardMessages{earliestCreationTime: common.Int64Ptr(10), size: 20}, b: replicationShardMessages{earliestCreationTime: common.Int64Ptr(10), size: 10}, want: 1, }, "a equal b": { a: replicationShardMessages{earliestCreationTime: common.Int64Ptr(10)}, b: replicationShardMessages{earliestCreationTime: common.Int64Ptr(10)}, want: 0, }, } { t.Run(name, func(t *testing.T) { assert.Equal(t, c.want, cmpReplicationShardMessages(c.a, c.b)) }) } } func Test_sortReplicationShardMessages(t *testing.T) { for name, c := range map[string]struct { msgs []replicationShardMessages want []replicationShardMessages }{ "empty": {}, "multiple nil, non nil earliestCreationTime": { msgs: []replicationShardMessages{ {earliestCreationTime: nil}, {earliestCreationTime: nil}, {earliestCreationTime: common.Int64Ptr(20)}, {earliestCreationTime: common.Int64Ptr(10)}, }, want: []replicationShardMessages{ {earliestCreationTime: common.Int64Ptr(10)}, {earliestCreationTime: common.Int64Ptr(20)}, {earliestCreationTime: nil}, {earliestCreationTime: nil}, }, }, "multiple nil, non nil same earliestCreationTime, different size": { msgs: []replicationShardMessages{ {earliestCreationTime: nil}, {earliestCreationTime: nil}, {earliestCreationTime: common.Int64Ptr(100), size: 50}, {earliestCreationTime: common.Int64Ptr(100), size: 30}, {earliestCreationTime: common.Int64Ptr(20)}, }, want: []replicationShardMessages{ {earliestCreationTime: common.Int64Ptr(20)}, {earliestCreationTime: common.Int64Ptr(100), size: 30}, {earliestCreationTime: common.Int64Ptr(100), size: 50}, {earliestCreationTime: nil}, {earliestCreationTime: nil}, }, }, } { t.Run(name, func(t *testing.T) { sortReplicationShardMessages(c.msgs) assert.Equal(t, c.want, c.msgs) }) } } ================================================ FILE: service/history/handler/interface.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -package handler github.com/uber/cadence/service/history/handler Handler //go:generate gowrap gen -g -p . -i Handler -t ../../templates/grpc.tmpl -o ../wrappers/grpc/grpc_handler_generated.go -v handler=GRPC -v package=historyv1 -v path=github.com/uber/cadence/.gen/proto/history/v1 -v prefix=History //go:generate gowrap gen -g -p ../../../.gen/go/history/historyserviceserver -i Interface -t ../../templates/thrift.tmpl -o ../wrappers/thrift/thrift_handler_generated.go -v handler=Thrift -v prefix=History package handler import ( "context" "time" "github.com/uber/cadence/common/types" ) // Handler interface for history service type Handler interface { // Do not use embeded methods, otherwise, we got the following error from gowrap // and we only get this error from history/interface.go, not sure why // failed to parse interface declaration: Daemon: target declaration not found // service/history/interface.go:22: running "gowrap": exit status 1 // common.Daemon Start() Stop() PrepareToStop(time.Duration) time.Duration Health(context.Context) (*types.HealthStatus, error) CloseShard(context.Context, *types.CloseShardRequest) error DescribeHistoryHost(context.Context, *types.DescribeHistoryHostRequest) (*types.DescribeHistoryHostResponse, error) DescribeMutableState(context.Context, *types.DescribeMutableStateRequest) (*types.DescribeMutableStateResponse, error) DescribeQueue(context.Context, *types.DescribeQueueRequest) (*types.DescribeQueueResponse, error) DescribeWorkflowExecution(context.Context, *types.HistoryDescribeWorkflowExecutionRequest) (*types.DescribeWorkflowExecutionResponse, error) GetCrossClusterTasks(context.Context, *types.GetCrossClusterTasksRequest) (*types.GetCrossClusterTasksResponse, error) CountDLQMessages(context.Context, *types.CountDLQMessagesRequest) (*types.HistoryCountDLQMessagesResponse, error) GetDLQReplicationMessages(context.Context, *types.GetDLQReplicationMessagesRequest) (*types.GetDLQReplicationMessagesResponse, error) GetMutableState(context.Context, *types.GetMutableStateRequest) (*types.GetMutableStateResponse, error) GetReplicationMessages(context.Context, *types.GetReplicationMessagesRequest) (*types.GetReplicationMessagesResponse, error) MergeDLQMessages(context.Context, *types.MergeDLQMessagesRequest) (*types.MergeDLQMessagesResponse, error) NotifyFailoverMarkers(context.Context, *types.NotifyFailoverMarkersRequest) error PollMutableState(context.Context, *types.PollMutableStateRequest) (*types.PollMutableStateResponse, error) PurgeDLQMessages(context.Context, *types.PurgeDLQMessagesRequest) error QueryWorkflow(context.Context, *types.HistoryQueryWorkflowRequest) (*types.HistoryQueryWorkflowResponse, error) ReadDLQMessages(context.Context, *types.ReadDLQMessagesRequest) (*types.ReadDLQMessagesResponse, error) ReapplyEvents(context.Context, *types.HistoryReapplyEventsRequest) error RecordActivityTaskHeartbeat(context.Context, *types.HistoryRecordActivityTaskHeartbeatRequest) (*types.RecordActivityTaskHeartbeatResponse, error) RecordActivityTaskStarted(context.Context, *types.RecordActivityTaskStartedRequest) (*types.RecordActivityTaskStartedResponse, error) RecordChildExecutionCompleted(context.Context, *types.RecordChildExecutionCompletedRequest) error RecordDecisionTaskStarted(context.Context, *types.RecordDecisionTaskStartedRequest) (*types.RecordDecisionTaskStartedResponse, error) RefreshWorkflowTasks(context.Context, *types.HistoryRefreshWorkflowTasksRequest) error RemoveSignalMutableState(context.Context, *types.RemoveSignalMutableStateRequest) error RemoveTask(context.Context, *types.RemoveTaskRequest) error ReplicateEventsV2(context.Context, *types.ReplicateEventsV2Request) error RequestCancelWorkflowExecution(context.Context, *types.HistoryRequestCancelWorkflowExecutionRequest) error ResetQueue(context.Context, *types.ResetQueueRequest) error ResetStickyTaskList(context.Context, *types.HistoryResetStickyTaskListRequest) (*types.HistoryResetStickyTaskListResponse, error) ResetWorkflowExecution(context.Context, *types.HistoryResetWorkflowExecutionRequest) (*types.ResetWorkflowExecutionResponse, error) RespondActivityTaskCanceled(context.Context, *types.HistoryRespondActivityTaskCanceledRequest) error RespondActivityTaskCompleted(context.Context, *types.HistoryRespondActivityTaskCompletedRequest) error RespondActivityTaskFailed(context.Context, *types.HistoryRespondActivityTaskFailedRequest) error RespondCrossClusterTasksCompleted(context.Context, *types.RespondCrossClusterTasksCompletedRequest) (*types.RespondCrossClusterTasksCompletedResponse, error) RespondDecisionTaskCompleted(context.Context, *types.HistoryRespondDecisionTaskCompletedRequest) (*types.HistoryRespondDecisionTaskCompletedResponse, error) RespondDecisionTaskFailed(context.Context, *types.HistoryRespondDecisionTaskFailedRequest) error ScheduleDecisionTask(context.Context, *types.ScheduleDecisionTaskRequest) error SignalWithStartWorkflowExecution(context.Context, *types.HistorySignalWithStartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) SignalWorkflowExecution(context.Context, *types.HistorySignalWorkflowExecutionRequest) error StartWorkflowExecution(context.Context, *types.HistoryStartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) SyncActivity(context.Context, *types.SyncActivityRequest) error SyncShardStatus(context.Context, *types.SyncShardStatusRequest) error TerminateWorkflowExecution(context.Context, *types.HistoryTerminateWorkflowExecutionRequest) error GetFailoverInfo(context.Context, *types.GetFailoverInfoRequest) (*types.GetFailoverInfoResponse, error) RatelimitUpdate(context.Context, *types.RatelimitUpdateRequest) (*types.RatelimitUpdateResponse, error) } ================================================ FILE: service/history/handler/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package handler -source interface.go -destination interface_mock.go -package handler github.com/uber/cadence/service/history/handler Handler // // Package handler is a generated GoMock package. package handler import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockHandler is a mock of Handler interface. type MockHandler struct { ctrl *gomock.Controller recorder *MockHandlerMockRecorder isgomock struct{} } // MockHandlerMockRecorder is the mock recorder for MockHandler. type MockHandlerMockRecorder struct { mock *MockHandler } // NewMockHandler creates a new mock instance. func NewMockHandler(ctrl *gomock.Controller) *MockHandler { mock := &MockHandler{ctrl: ctrl} mock.recorder = &MockHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHandler) EXPECT() *MockHandlerMockRecorder { return m.recorder } // CloseShard mocks base method. func (m *MockHandler) CloseShard(arg0 context.Context, arg1 *types.CloseShardRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CloseShard", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // CloseShard indicates an expected call of CloseShard. func (mr *MockHandlerMockRecorder) CloseShard(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseShard", reflect.TypeOf((*MockHandler)(nil).CloseShard), arg0, arg1) } // CountDLQMessages mocks base method. func (m *MockHandler) CountDLQMessages(arg0 context.Context, arg1 *types.CountDLQMessagesRequest) (*types.HistoryCountDLQMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CountDLQMessages", arg0, arg1) ret0, _ := ret[0].(*types.HistoryCountDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CountDLQMessages indicates an expected call of CountDLQMessages. func (mr *MockHandlerMockRecorder) CountDLQMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountDLQMessages", reflect.TypeOf((*MockHandler)(nil).CountDLQMessages), arg0, arg1) } // DescribeHistoryHost mocks base method. func (m *MockHandler) DescribeHistoryHost(arg0 context.Context, arg1 *types.DescribeHistoryHostRequest) (*types.DescribeHistoryHostResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeHistoryHost", arg0, arg1) ret0, _ := ret[0].(*types.DescribeHistoryHostResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeHistoryHost indicates an expected call of DescribeHistoryHost. func (mr *MockHandlerMockRecorder) DescribeHistoryHost(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeHistoryHost", reflect.TypeOf((*MockHandler)(nil).DescribeHistoryHost), arg0, arg1) } // DescribeMutableState mocks base method. func (m *MockHandler) DescribeMutableState(arg0 context.Context, arg1 *types.DescribeMutableStateRequest) (*types.DescribeMutableStateResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeMutableState", arg0, arg1) ret0, _ := ret[0].(*types.DescribeMutableStateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeMutableState indicates an expected call of DescribeMutableState. func (mr *MockHandlerMockRecorder) DescribeMutableState(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeMutableState", reflect.TypeOf((*MockHandler)(nil).DescribeMutableState), arg0, arg1) } // DescribeQueue mocks base method. func (m *MockHandler) DescribeQueue(arg0 context.Context, arg1 *types.DescribeQueueRequest) (*types.DescribeQueueResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeQueue", arg0, arg1) ret0, _ := ret[0].(*types.DescribeQueueResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeQueue indicates an expected call of DescribeQueue. func (mr *MockHandlerMockRecorder) DescribeQueue(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeQueue", reflect.TypeOf((*MockHandler)(nil).DescribeQueue), arg0, arg1) } // DescribeWorkflowExecution mocks base method. func (m *MockHandler) DescribeWorkflowExecution(arg0 context.Context, arg1 *types.HistoryDescribeWorkflowExecutionRequest) (*types.DescribeWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.DescribeWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeWorkflowExecution indicates an expected call of DescribeWorkflowExecution. func (mr *MockHandlerMockRecorder) DescribeWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).DescribeWorkflowExecution), arg0, arg1) } // GetCrossClusterTasks mocks base method. func (m *MockHandler) GetCrossClusterTasks(arg0 context.Context, arg1 *types.GetCrossClusterTasksRequest) (*types.GetCrossClusterTasksResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCrossClusterTasks", arg0, arg1) ret0, _ := ret[0].(*types.GetCrossClusterTasksResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCrossClusterTasks indicates an expected call of GetCrossClusterTasks. func (mr *MockHandlerMockRecorder) GetCrossClusterTasks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCrossClusterTasks", reflect.TypeOf((*MockHandler)(nil).GetCrossClusterTasks), arg0, arg1) } // GetDLQReplicationMessages mocks base method. func (m *MockHandler) GetDLQReplicationMessages(arg0 context.Context, arg1 *types.GetDLQReplicationMessagesRequest) (*types.GetDLQReplicationMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDLQReplicationMessages", arg0, arg1) ret0, _ := ret[0].(*types.GetDLQReplicationMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDLQReplicationMessages indicates an expected call of GetDLQReplicationMessages. func (mr *MockHandlerMockRecorder) GetDLQReplicationMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDLQReplicationMessages", reflect.TypeOf((*MockHandler)(nil).GetDLQReplicationMessages), arg0, arg1) } // GetFailoverInfo mocks base method. func (m *MockHandler) GetFailoverInfo(arg0 context.Context, arg1 *types.GetFailoverInfoRequest) (*types.GetFailoverInfoResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFailoverInfo", arg0, arg1) ret0, _ := ret[0].(*types.GetFailoverInfoResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFailoverInfo indicates an expected call of GetFailoverInfo. func (mr *MockHandlerMockRecorder) GetFailoverInfo(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFailoverInfo", reflect.TypeOf((*MockHandler)(nil).GetFailoverInfo), arg0, arg1) } // GetMutableState mocks base method. func (m *MockHandler) GetMutableState(arg0 context.Context, arg1 *types.GetMutableStateRequest) (*types.GetMutableStateResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMutableState", arg0, arg1) ret0, _ := ret[0].(*types.GetMutableStateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMutableState indicates an expected call of GetMutableState. func (mr *MockHandlerMockRecorder) GetMutableState(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMutableState", reflect.TypeOf((*MockHandler)(nil).GetMutableState), arg0, arg1) } // GetReplicationMessages mocks base method. func (m *MockHandler) GetReplicationMessages(arg0 context.Context, arg1 *types.GetReplicationMessagesRequest) (*types.GetReplicationMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReplicationMessages", arg0, arg1) ret0, _ := ret[0].(*types.GetReplicationMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetReplicationMessages indicates an expected call of GetReplicationMessages. func (mr *MockHandlerMockRecorder) GetReplicationMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationMessages", reflect.TypeOf((*MockHandler)(nil).GetReplicationMessages), arg0, arg1) } // Health mocks base method. func (m *MockHandler) Health(arg0 context.Context) (*types.HealthStatus, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Health", arg0) ret0, _ := ret[0].(*types.HealthStatus) ret1, _ := ret[1].(error) return ret0, ret1 } // Health indicates an expected call of Health. func (mr *MockHandlerMockRecorder) Health(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockHandler)(nil).Health), arg0) } // MergeDLQMessages mocks base method. func (m *MockHandler) MergeDLQMessages(arg0 context.Context, arg1 *types.MergeDLQMessagesRequest) (*types.MergeDLQMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MergeDLQMessages", arg0, arg1) ret0, _ := ret[0].(*types.MergeDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MergeDLQMessages indicates an expected call of MergeDLQMessages. func (mr *MockHandlerMockRecorder) MergeDLQMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MergeDLQMessages", reflect.TypeOf((*MockHandler)(nil).MergeDLQMessages), arg0, arg1) } // NotifyFailoverMarkers mocks base method. func (m *MockHandler) NotifyFailoverMarkers(arg0 context.Context, arg1 *types.NotifyFailoverMarkersRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NotifyFailoverMarkers", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // NotifyFailoverMarkers indicates an expected call of NotifyFailoverMarkers. func (mr *MockHandlerMockRecorder) NotifyFailoverMarkers(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyFailoverMarkers", reflect.TypeOf((*MockHandler)(nil).NotifyFailoverMarkers), arg0, arg1) } // PollMutableState mocks base method. func (m *MockHandler) PollMutableState(arg0 context.Context, arg1 *types.PollMutableStateRequest) (*types.PollMutableStateResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PollMutableState", arg0, arg1) ret0, _ := ret[0].(*types.PollMutableStateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollMutableState indicates an expected call of PollMutableState. func (mr *MockHandlerMockRecorder) PollMutableState(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollMutableState", reflect.TypeOf((*MockHandler)(nil).PollMutableState), arg0, arg1) } // PrepareToStop mocks base method. func (m *MockHandler) PrepareToStop(arg0 time.Duration) time.Duration { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PrepareToStop", arg0) ret0, _ := ret[0].(time.Duration) return ret0 } // PrepareToStop indicates an expected call of PrepareToStop. func (mr *MockHandlerMockRecorder) PrepareToStop(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrepareToStop", reflect.TypeOf((*MockHandler)(nil).PrepareToStop), arg0) } // PurgeDLQMessages mocks base method. func (m *MockHandler) PurgeDLQMessages(arg0 context.Context, arg1 *types.PurgeDLQMessagesRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PurgeDLQMessages", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // PurgeDLQMessages indicates an expected call of PurgeDLQMessages. func (mr *MockHandlerMockRecorder) PurgeDLQMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PurgeDLQMessages", reflect.TypeOf((*MockHandler)(nil).PurgeDLQMessages), arg0, arg1) } // QueryWorkflow mocks base method. func (m *MockHandler) QueryWorkflow(arg0 context.Context, arg1 *types.HistoryQueryWorkflowRequest) (*types.HistoryQueryWorkflowResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryWorkflow", arg0, arg1) ret0, _ := ret[0].(*types.HistoryQueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // QueryWorkflow indicates an expected call of QueryWorkflow. func (mr *MockHandlerMockRecorder) QueryWorkflow(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockHandler)(nil).QueryWorkflow), arg0, arg1) } // RatelimitUpdate mocks base method. func (m *MockHandler) RatelimitUpdate(arg0 context.Context, arg1 *types.RatelimitUpdateRequest) (*types.RatelimitUpdateResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RatelimitUpdate", arg0, arg1) ret0, _ := ret[0].(*types.RatelimitUpdateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RatelimitUpdate indicates an expected call of RatelimitUpdate. func (mr *MockHandlerMockRecorder) RatelimitUpdate(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RatelimitUpdate", reflect.TypeOf((*MockHandler)(nil).RatelimitUpdate), arg0, arg1) } // ReadDLQMessages mocks base method. func (m *MockHandler) ReadDLQMessages(arg0 context.Context, arg1 *types.ReadDLQMessagesRequest) (*types.ReadDLQMessagesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadDLQMessages", arg0, arg1) ret0, _ := ret[0].(*types.ReadDLQMessagesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadDLQMessages indicates an expected call of ReadDLQMessages. func (mr *MockHandlerMockRecorder) ReadDLQMessages(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDLQMessages", reflect.TypeOf((*MockHandler)(nil).ReadDLQMessages), arg0, arg1) } // ReapplyEvents mocks base method. func (m *MockHandler) ReapplyEvents(arg0 context.Context, arg1 *types.HistoryReapplyEventsRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReapplyEvents", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ReapplyEvents indicates an expected call of ReapplyEvents. func (mr *MockHandlerMockRecorder) ReapplyEvents(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReapplyEvents", reflect.TypeOf((*MockHandler)(nil).ReapplyEvents), arg0, arg1) } // RecordActivityTaskHeartbeat mocks base method. func (m *MockHandler) RecordActivityTaskHeartbeat(arg0 context.Context, arg1 *types.HistoryRecordActivityTaskHeartbeatRequest) (*types.RecordActivityTaskHeartbeatResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordActivityTaskHeartbeat", arg0, arg1) ret0, _ := ret[0].(*types.RecordActivityTaskHeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordActivityTaskHeartbeat indicates an expected call of RecordActivityTaskHeartbeat. func (mr *MockHandlerMockRecorder) RecordActivityTaskHeartbeat(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordActivityTaskHeartbeat", reflect.TypeOf((*MockHandler)(nil).RecordActivityTaskHeartbeat), arg0, arg1) } // RecordActivityTaskStarted mocks base method. func (m *MockHandler) RecordActivityTaskStarted(arg0 context.Context, arg1 *types.RecordActivityTaskStartedRequest) (*types.RecordActivityTaskStartedResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordActivityTaskStarted", arg0, arg1) ret0, _ := ret[0].(*types.RecordActivityTaskStartedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordActivityTaskStarted indicates an expected call of RecordActivityTaskStarted. func (mr *MockHandlerMockRecorder) RecordActivityTaskStarted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordActivityTaskStarted", reflect.TypeOf((*MockHandler)(nil).RecordActivityTaskStarted), arg0, arg1) } // RecordChildExecutionCompleted mocks base method. func (m *MockHandler) RecordChildExecutionCompleted(arg0 context.Context, arg1 *types.RecordChildExecutionCompletedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordChildExecutionCompleted", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RecordChildExecutionCompleted indicates an expected call of RecordChildExecutionCompleted. func (mr *MockHandlerMockRecorder) RecordChildExecutionCompleted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordChildExecutionCompleted", reflect.TypeOf((*MockHandler)(nil).RecordChildExecutionCompleted), arg0, arg1) } // RecordDecisionTaskStarted mocks base method. func (m *MockHandler) RecordDecisionTaskStarted(arg0 context.Context, arg1 *types.RecordDecisionTaskStartedRequest) (*types.RecordDecisionTaskStartedResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordDecisionTaskStarted", arg0, arg1) ret0, _ := ret[0].(*types.RecordDecisionTaskStartedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RecordDecisionTaskStarted indicates an expected call of RecordDecisionTaskStarted. func (mr *MockHandlerMockRecorder) RecordDecisionTaskStarted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordDecisionTaskStarted", reflect.TypeOf((*MockHandler)(nil).RecordDecisionTaskStarted), arg0, arg1) } // RefreshWorkflowTasks mocks base method. func (m *MockHandler) RefreshWorkflowTasks(arg0 context.Context, arg1 *types.HistoryRefreshWorkflowTasksRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RefreshWorkflowTasks", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RefreshWorkflowTasks indicates an expected call of RefreshWorkflowTasks. func (mr *MockHandlerMockRecorder) RefreshWorkflowTasks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshWorkflowTasks", reflect.TypeOf((*MockHandler)(nil).RefreshWorkflowTasks), arg0, arg1) } // RemoveSignalMutableState mocks base method. func (m *MockHandler) RemoveSignalMutableState(arg0 context.Context, arg1 *types.RemoveSignalMutableStateRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoveSignalMutableState", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RemoveSignalMutableState indicates an expected call of RemoveSignalMutableState. func (mr *MockHandlerMockRecorder) RemoveSignalMutableState(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveSignalMutableState", reflect.TypeOf((*MockHandler)(nil).RemoveSignalMutableState), arg0, arg1) } // RemoveTask mocks base method. func (m *MockHandler) RemoveTask(arg0 context.Context, arg1 *types.RemoveTaskRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoveTask", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RemoveTask indicates an expected call of RemoveTask. func (mr *MockHandlerMockRecorder) RemoveTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveTask", reflect.TypeOf((*MockHandler)(nil).RemoveTask), arg0, arg1) } // ReplicateEventsV2 mocks base method. func (m *MockHandler) ReplicateEventsV2(arg0 context.Context, arg1 *types.ReplicateEventsV2Request) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateEventsV2", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ReplicateEventsV2 indicates an expected call of ReplicateEventsV2. func (mr *MockHandlerMockRecorder) ReplicateEventsV2(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateEventsV2", reflect.TypeOf((*MockHandler)(nil).ReplicateEventsV2), arg0, arg1) } // RequestCancelWorkflowExecution mocks base method. func (m *MockHandler) RequestCancelWorkflowExecution(arg0 context.Context, arg1 *types.HistoryRequestCancelWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RequestCancelWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RequestCancelWorkflowExecution indicates an expected call of RequestCancelWorkflowExecution. func (mr *MockHandlerMockRecorder) RequestCancelWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestCancelWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).RequestCancelWorkflowExecution), arg0, arg1) } // ResetQueue mocks base method. func (m *MockHandler) ResetQueue(arg0 context.Context, arg1 *types.ResetQueueRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetQueue", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ResetQueue indicates an expected call of ResetQueue. func (mr *MockHandlerMockRecorder) ResetQueue(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetQueue", reflect.TypeOf((*MockHandler)(nil).ResetQueue), arg0, arg1) } // ResetStickyTaskList mocks base method. func (m *MockHandler) ResetStickyTaskList(arg0 context.Context, arg1 *types.HistoryResetStickyTaskListRequest) (*types.HistoryResetStickyTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetStickyTaskList", arg0, arg1) ret0, _ := ret[0].(*types.HistoryResetStickyTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetStickyTaskList indicates an expected call of ResetStickyTaskList. func (mr *MockHandlerMockRecorder) ResetStickyTaskList(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetStickyTaskList", reflect.TypeOf((*MockHandler)(nil).ResetStickyTaskList), arg0, arg1) } // ResetWorkflowExecution mocks base method. func (m *MockHandler) ResetWorkflowExecution(arg0 context.Context, arg1 *types.HistoryResetWorkflowExecutionRequest) (*types.ResetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.ResetWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetWorkflowExecution indicates an expected call of ResetWorkflowExecution. func (mr *MockHandlerMockRecorder) ResetWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).ResetWorkflowExecution), arg0, arg1) } // RespondActivityTaskCanceled mocks base method. func (m *MockHandler) RespondActivityTaskCanceled(arg0 context.Context, arg1 *types.HistoryRespondActivityTaskCanceledRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskCanceled", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCanceled indicates an expected call of RespondActivityTaskCanceled. func (mr *MockHandlerMockRecorder) RespondActivityTaskCanceled(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCanceled", reflect.TypeOf((*MockHandler)(nil).RespondActivityTaskCanceled), arg0, arg1) } // RespondActivityTaskCompleted mocks base method. func (m *MockHandler) RespondActivityTaskCompleted(arg0 context.Context, arg1 *types.HistoryRespondActivityTaskCompletedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskCompleted", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskCompleted indicates an expected call of RespondActivityTaskCompleted. func (mr *MockHandlerMockRecorder) RespondActivityTaskCompleted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskCompleted", reflect.TypeOf((*MockHandler)(nil).RespondActivityTaskCompleted), arg0, arg1) } // RespondActivityTaskFailed mocks base method. func (m *MockHandler) RespondActivityTaskFailed(arg0 context.Context, arg1 *types.HistoryRespondActivityTaskFailedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondActivityTaskFailed", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondActivityTaskFailed indicates an expected call of RespondActivityTaskFailed. func (mr *MockHandlerMockRecorder) RespondActivityTaskFailed(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondActivityTaskFailed", reflect.TypeOf((*MockHandler)(nil).RespondActivityTaskFailed), arg0, arg1) } // RespondCrossClusterTasksCompleted mocks base method. func (m *MockHandler) RespondCrossClusterTasksCompleted(arg0 context.Context, arg1 *types.RespondCrossClusterTasksCompletedRequest) (*types.RespondCrossClusterTasksCompletedResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondCrossClusterTasksCompleted", arg0, arg1) ret0, _ := ret[0].(*types.RespondCrossClusterTasksCompletedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RespondCrossClusterTasksCompleted indicates an expected call of RespondCrossClusterTasksCompleted. func (mr *MockHandlerMockRecorder) RespondCrossClusterTasksCompleted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondCrossClusterTasksCompleted", reflect.TypeOf((*MockHandler)(nil).RespondCrossClusterTasksCompleted), arg0, arg1) } // RespondDecisionTaskCompleted mocks base method. func (m *MockHandler) RespondDecisionTaskCompleted(arg0 context.Context, arg1 *types.HistoryRespondDecisionTaskCompletedRequest) (*types.HistoryRespondDecisionTaskCompletedResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondDecisionTaskCompleted", arg0, arg1) ret0, _ := ret[0].(*types.HistoryRespondDecisionTaskCompletedResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RespondDecisionTaskCompleted indicates an expected call of RespondDecisionTaskCompleted. func (mr *MockHandlerMockRecorder) RespondDecisionTaskCompleted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondDecisionTaskCompleted", reflect.TypeOf((*MockHandler)(nil).RespondDecisionTaskCompleted), arg0, arg1) } // RespondDecisionTaskFailed mocks base method. func (m *MockHandler) RespondDecisionTaskFailed(arg0 context.Context, arg1 *types.HistoryRespondDecisionTaskFailedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondDecisionTaskFailed", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondDecisionTaskFailed indicates an expected call of RespondDecisionTaskFailed. func (mr *MockHandlerMockRecorder) RespondDecisionTaskFailed(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondDecisionTaskFailed", reflect.TypeOf((*MockHandler)(nil).RespondDecisionTaskFailed), arg0, arg1) } // ScheduleDecisionTask mocks base method. func (m *MockHandler) ScheduleDecisionTask(arg0 context.Context, arg1 *types.ScheduleDecisionTaskRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ScheduleDecisionTask", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ScheduleDecisionTask indicates an expected call of ScheduleDecisionTask. func (mr *MockHandlerMockRecorder) ScheduleDecisionTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduleDecisionTask", reflect.TypeOf((*MockHandler)(nil).ScheduleDecisionTask), arg0, arg1) } // SignalWithStartWorkflowExecution mocks base method. func (m *MockHandler) SignalWithStartWorkflowExecution(arg0 context.Context, arg1 *types.HistorySignalWithStartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignalWithStartWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.StartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SignalWithStartWorkflowExecution indicates an expected call of SignalWithStartWorkflowExecution. func (mr *MockHandlerMockRecorder) SignalWithStartWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWithStartWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).SignalWithStartWorkflowExecution), arg0, arg1) } // SignalWorkflowExecution mocks base method. func (m *MockHandler) SignalWorkflowExecution(arg0 context.Context, arg1 *types.HistorySignalWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignalWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // SignalWorkflowExecution indicates an expected call of SignalWorkflowExecution. func (mr *MockHandlerMockRecorder) SignalWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).SignalWorkflowExecution), arg0, arg1) } // Start mocks base method. func (m *MockHandler) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockHandlerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockHandler)(nil).Start)) } // StartWorkflowExecution mocks base method. func (m *MockHandler) StartWorkflowExecution(arg0 context.Context, arg1 *types.HistoryStartWorkflowExecutionRequest) (*types.StartWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StartWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(*types.StartWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // StartWorkflowExecution indicates an expected call of StartWorkflowExecution. func (mr *MockHandlerMockRecorder) StartWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).StartWorkflowExecution), arg0, arg1) } // Stop mocks base method. func (m *MockHandler) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockHandlerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockHandler)(nil).Stop)) } // SyncActivity mocks base method. func (m *MockHandler) SyncActivity(arg0 context.Context, arg1 *types.SyncActivityRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SyncActivity", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // SyncActivity indicates an expected call of SyncActivity. func (mr *MockHandlerMockRecorder) SyncActivity(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncActivity", reflect.TypeOf((*MockHandler)(nil).SyncActivity), arg0, arg1) } // SyncShardStatus mocks base method. func (m *MockHandler) SyncShardStatus(arg0 context.Context, arg1 *types.SyncShardStatusRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SyncShardStatus", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // SyncShardStatus indicates an expected call of SyncShardStatus. func (mr *MockHandlerMockRecorder) SyncShardStatus(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncShardStatus", reflect.TypeOf((*MockHandler)(nil).SyncShardStatus), arg0, arg1) } // TerminateWorkflowExecution mocks base method. func (m *MockHandler) TerminateWorkflowExecution(arg0 context.Context, arg1 *types.HistoryTerminateWorkflowExecutionRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TerminateWorkflowExecution", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // TerminateWorkflowExecution indicates an expected call of TerminateWorkflowExecution. func (mr *MockHandlerMockRecorder) TerminateWorkflowExecution(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TerminateWorkflowExecution", reflect.TypeOf((*MockHandler)(nil).TerminateWorkflowExecution), arg0, arg1) } ================================================ FILE: service/history/lookup/lookup.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package lookup import ( "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" ) // HistoryServerByShardID calls resolver.Lookup with key based on provided shardID func HistoryServerByShardID(resolver membership.Resolver, shardID int) (membership.HostInfo, error) { return resolver.Lookup(service.History, string(rune(shardID))) } ================================================ FILE: service/history/lookup/lookup_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package lookup import ( "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" ) func TestHistoryServerByShardID_Succeeds(t *testing.T) { ctrl := gomock.NewController(t) mockResolver := membership.NewMockResolver(ctrl) mockResolver.EXPECT().Lookup(service.History, string(rune(65))). Return(membership.NewHostInfo("127.0.0.1:1234"), nil) host, err := HistoryServerByShardID(mockResolver, 65) require.NoError(t, err) assert.Equal(t, "127.0.0.1:1234", host.GetAddress()) } func TestHistoryServerByShardID_PreservesError(t *testing.T) { lookupError := errors.New("lookup failed") ctrl := gomock.NewController(t) mockResolver := membership.NewMockResolver(ctrl) mockResolver.EXPECT().Lookup(service.History, gomock.Any()). Return(membership.HostInfo{}, lookupError) host, err := HistoryServerByShardID(mockResolver, 65) assert.Equal(t, lookupError, err, "error should not be modified") assert.Empty(t, host) } ================================================ FILE: service/history/ndc/activity_replicator.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination activity_replicator_mock.go package ndc import ( ctx "context" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) const ( resendMissingEventMessage = "Resend missed sync activity events" resendHigherVersionMessage = "Resend sync activity events due to a higher version received" errRetrySyncActivityMsg = "retry on applying sync activity" ) type ( // ActivityReplicator handles sync activity process ActivityReplicator interface { SyncActivity( ctx ctx.Context, request *types.SyncActivityRequest, ) error } activityReplicatorImpl struct { executionCache execution.Cache clusterMetadata cluster.Metadata logger log.Logger } ) var _ ActivityReplicator = (*activityReplicatorImpl)(nil) // NewActivityReplicator creates activity replicator func NewActivityReplicator( shard shard.Context, executionCache execution.Cache, logger log.Logger, ) ActivityReplicator { return &activityReplicatorImpl{ executionCache: executionCache, clusterMetadata: shard.GetService().GetClusterMetadata(), logger: logger.WithTags(tag.ComponentHistoryReplicator), } } func (r *activityReplicatorImpl) SyncActivity( ctx ctx.Context, request *types.SyncActivityRequest, ) (retError error) { // sync activity info will only be sent from active side, when // 1. activity has retry policy and activity got started // 2. activity heart beat // no sync activity task will be sent when active side fail / timeout activity, // since standby side does not have activity retry timer domainID := request.GetDomainID() workflowExecution := types.WorkflowExecution{ WorkflowID: request.WorkflowID, RunID: request.RunID, } context, release, err := r.executionCache.GetOrCreateWorkflowExecution(ctx, domainID, workflowExecution) if err != nil { // for get workflow execution context, with valid run id // err will not be of type EntityNotExistsError return err } defer func() { release(retError) }() mutableState, err := context.LoadWorkflowExecution(ctx) if err != nil { if _, ok := err.(*types.EntityNotExistsError); !ok { return err } // this can happen if the workflow start event and this sync activity task are out of order // or the target workflow is long gone // the safe solution to this is to throw away the sync activity task // or otherwise, worker attempt will exceeds limit and put this message to DLQ return nil } version := request.GetVersion() scheduleID := request.GetScheduledID() shouldApply, err := r.shouldApplySyncActivity( domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID(), scheduleID, version, mutableState, request.GetVersionHistory(), ) if err != nil { return err } if !shouldApply { return nil } ai, ok := mutableState.GetActivityInfo(scheduleID) if !ok { // this should not retry, can be caused by out of order delivery // since the activity is already finished return nil } if ai.Version > request.GetVersion() { // this should not retry, can be caused by failover or reset return nil } if ai.Version == request.GetVersion() { if ai.Attempt > request.GetAttempt() { // this should not retry, can be caused by failover or reset return nil } if ai.Attempt == request.GetAttempt() { lastHeartbeatTime := time.Unix(0, request.GetLastHeartbeatTime()) if ai.LastHeartBeatUpdatedTime.After(lastHeartbeatTime) { // this should not retry, can be caused by out of order delivery return nil } // version equal & attempt equal & last heartbeat after existing heartbeat // should update activity } // version equal & attempt larger then existing, should update activity } // version larger then existing, should update activity // calculate whether to reset the activity timer task status bits // reset timer task status bits if // 1. same source cluster & attempt changes // 2. different source cluster resetActivityTimerTaskStatus := false if !r.clusterMetadata.IsVersionFromSameCluster(request.GetVersion(), ai.Version) { resetActivityTimerTaskStatus = true } else if ai.Attempt < request.GetAttempt() { resetActivityTimerTaskStatus = true } err = mutableState.ReplicateActivityInfo(request, resetActivityTimerTaskStatus) if err != nil { return err } // see whether we need to refresh the activity timer eventTime := request.GetScheduledTime() if eventTime < request.GetStartedTime() { eventTime = request.GetStartedTime() } if eventTime < request.GetLastHeartbeatTime() { eventTime = request.GetLastHeartbeatTime() } // passive logic need to explicitly call create timer now := time.Unix(0, eventTime) if _, err := execution.NewTimerSequence( mutableState, ).CreateNextActivityTimer(); err != nil { return err } updateMode := persistence.UpdateWorkflowModeUpdateCurrent if state, _ := mutableState.GetWorkflowStateCloseStatus(); state == persistence.WorkflowStateZombie { updateMode = persistence.UpdateWorkflowModeBypassCurrent } r.logger.Debugf("SyncActivity calling UpdateWorkflowExecutionWithNew for wfID %s, updateMode %v, current policy %v, new policy %v", workflowExecution.GetWorkflowID(), updateMode, execution.TransactionPolicyPassive, nil, ) return context.UpdateWorkflowExecutionWithNew( ctx, now, updateMode, nil, // no new workflow nil, // no new workflow execution.TransactionPolicyPassive, nil, persistence.CreateWorkflowRequestModeReplicated, ) } func (r *activityReplicatorImpl) shouldApplySyncActivity( domainID string, workflowID string, runID string, scheduleID int64, activityVersion int64, mutableState execution.MutableState, incomingRawVersionHistory *types.VersionHistory, ) (bool, error) { if mutableState.GetVersionHistories() != nil { if state, _ := mutableState.GetWorkflowStateCloseStatus(); state == persistence.WorkflowStateCompleted { return false, nil } currentVersionHistory, err := mutableState.GetVersionHistories().GetCurrentVersionHistory() if err != nil { return false, err } lastLocalItem, err := currentVersionHistory.GetLastItem() if err != nil { return false, err } incomingVersionHistory := persistence.NewVersionHistoryFromInternalType(incomingRawVersionHistory) lastIncomingItem, err := incomingVersionHistory.GetLastItem() if err != nil { return false, err } lcaItem, err := currentVersionHistory.FindLCAItem(incomingVersionHistory) if err != nil { return false, err } // case 1: local version history is superset of incoming version history // or incoming version history is superset of local version history // resend the missing event if local version history doesn't have the schedule event // case 2: local version history and incoming version history diverged // case 2-1: local version history has the higher version and discard the incoming event // case 2-2: incoming version history has the higher version and resend the missing incoming events if currentVersionHistory.IsLCAAppendable(lcaItem) || incomingVersionHistory.IsLCAAppendable(lcaItem) { // case 1 if scheduleID > lcaItem.EventID { return false, newNDCRetryTaskErrorWithHint( resendMissingEventMessage, domainID, workflowID, runID, common.Int64Ptr(lcaItem.EventID), common.Int64Ptr(lcaItem.Version), nil, nil, ) } } else { // case 2 if lastIncomingItem.Version < lastLocalItem.Version { // case 2-1 return false, nil } else if lastIncomingItem.Version > lastLocalItem.Version { // case 2-2 return false, newNDCRetryTaskErrorWithHint( resendHigherVersionMessage, domainID, workflowID, runID, common.Int64Ptr(lcaItem.EventID), common.Int64Ptr(lcaItem.Version), nil, nil, ) } } } else { return false, &types.InternalServiceError{Message: "The workflow version histories is corrupted."} } return true, nil } ================================================ FILE: service/history/ndc/activity_replicator_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: activity_replicator.go // // Generated by this command: // // mockgen -package ndc -source activity_replicator.go -destination activity_replicator_mock.go // // Package ndc is a generated GoMock package. package ndc import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockActivityReplicator is a mock of ActivityReplicator interface. type MockActivityReplicator struct { ctrl *gomock.Controller recorder *MockActivityReplicatorMockRecorder isgomock struct{} } // MockActivityReplicatorMockRecorder is the mock recorder for MockActivityReplicator. type MockActivityReplicatorMockRecorder struct { mock *MockActivityReplicator } // NewMockActivityReplicator creates a new mock instance. func NewMockActivityReplicator(ctrl *gomock.Controller) *MockActivityReplicator { mock := &MockActivityReplicator{ctrl: ctrl} mock.recorder = &MockActivityReplicatorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockActivityReplicator) EXPECT() *MockActivityReplicatorMockRecorder { return m.recorder } // SyncActivity mocks base method. func (m *MockActivityReplicator) SyncActivity(ctx context.Context, request *types.SyncActivityRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SyncActivity", ctx, request) ret0, _ := ret[0].(error) return ret0 } // SyncActivity indicates an expected call of SyncActivity. func (mr *MockActivityReplicatorMockRecorder) SyncActivity(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncActivity", reflect.TypeOf((*MockActivityReplicator)(nil).SyncActivity), ctx, request) } ================================================ FILE: service/history/ndc/activity_replicator_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( ctx "context" "errors" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) type ( activityReplicatorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockEngine *engine.MockEngine mockDomainCache *cache.MockDomainCache mockMutableState *execution.MockMutableState mockExecutionMgr *mocks.ExecutionManager logger log.Logger executionCache execution.Cache activityReplicator ActivityReplicator } ) func TestActivityReplicatorSuite(t *testing.T) { s := new(activityReplicatorSuite) suite.Run(t, s) } func (s *activityReplicatorSuite) SetupSuite() { } func (s *activityReplicatorSuite) TearDownSuite() { } func (s *activityReplicatorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockMutableState = execution.NewMockMutableState(s.controller) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockExecutionMgr = s.mockShard.Resource.ExecutionMgr s.logger = s.mockShard.GetLogger() s.executionCache = execution.NewCache(s.mockShard) s.mockEngine = engine.NewMockEngine(s.controller) s.mockEngine.EXPECT().NotifyNewHistoryEvent(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewTransferTasks(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewTimerTasks(gomock.Any()).AnyTimes() s.mockShard.SetEngine(s.mockEngine) s.activityReplicator = NewActivityReplicator( s.mockShard, s.executionCache, s.logger, ) } func (s *activityReplicatorSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *activityReplicatorSuite) TestSyncActivity_WorkflowNotFound() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() version := int64(100) request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, } s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, DomainName: domainName, RangeID: 1, }).Return(nil, &types.EntityNotExistsError{}) s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, version, ), nil, ).AnyTimes() err := s.activityReplicator.SyncActivity(ctx.Background(), request) s.Nil(err) } func (s *activityReplicatorSuite) TestSyncActivity_WorkflowClosed() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() version := int64(100) key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().AnyTimes() context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(false).AnyTimes() versionHistories := &persistence.VersionHistories{} s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(persistence.WorkflowStateCompleted, persistence.WorkflowCloseStatusCompleted) s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, version, ), nil, ).AnyTimes() err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Nil(err) } func (s *activityReplicatorSuite) TestSyncActivity_IncomingScheduleIDLarger_IncomingVersionSmaller() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() scheduleID := int64(144) version := int64(100) lastWriteVersion := version + 100 nextEventID := scheduleID - 10 versionHistoryItem0 := persistence.NewVersionHistoryItem(1, 1) versionHistoryItem1 := persistence.NewVersionHistoryItem(scheduleID, version) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ versionHistoryItem0, versionHistoryItem1, }) versionHistories := persistence.NewVersionHistories(versionHistory) key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().AnyTimes() context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) versionHistoryItem2 := persistence.NewVersionHistoryItem(scheduleID+1, version-1) versionHistory2 := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ versionHistoryItem0, versionHistoryItem2, }) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, VersionHistory: versionHistory2.ToInternalType(), } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() s.mockMutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(1, 0).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, lastWriteVersion, ), nil, ).AnyTimes() err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Nil(err) } func (s *activityReplicatorSuite) TestSyncActivity_IncomingScheduleIDLarger_IncomingVersionLarger() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() scheduleID := int64(144) version := int64(100) lastWriteVersion := version - 100 nextEventID := scheduleID - 10 key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().AnyTimes() context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() s.mockMutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() s.mockMutableState.EXPECT().GetLastWriteVersion().Return(lastWriteVersion, nil).AnyTimes() var versionHistories *persistence.VersionHistories s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, lastWriteVersion, ), nil, ).AnyTimes() _ = s.activityReplicator.SyncActivity(ctx.Background(), request) } func (s *activityReplicatorSuite) TestSyncActivity_VersionHistories_IncomingVersionSmaller_DiscardTask() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() scheduleID := int64(144) version := int64(99) lastWriteVersion := version - 100 incomingVersionHistory := persistence.VersionHistory{ BranchToken: []byte{}, Items: []*persistence.VersionHistoryItem{ { EventID: scheduleID - 1, Version: version - 1, }, { EventID: scheduleID, Version: version, }, }, } key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().AnyTimes() context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, VersionHistory: incomingVersionHistory.ToInternalType(), } localVersionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte{}, Items: []*persistence.VersionHistoryItem{ { EventID: scheduleID - 1, Version: version - 1, }, { EventID: scheduleID + 1, Version: version + 1, }, }, }, }, } s.mockMutableState.EXPECT().GetVersionHistories().Return(localVersionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(persistence.WorkflowStateRunning, persistence.WorkflowCloseStatusNone) s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, lastWriteVersion, ), nil, ).AnyTimes() err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Nil(err) } func (s *activityReplicatorSuite) TestSyncActivity_DifferentVersionHistories_IncomingVersionLarger_ReturnRetryError() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() scheduleID := int64(144) version := int64(100) lastWriteVersion := version - 100 incomingVersionHistory := persistence.VersionHistory{ BranchToken: []byte{}, Items: []*persistence.VersionHistoryItem{ { EventID: 50, Version: 2, }, { EventID: scheduleID, Version: version, }, }, } key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().AnyTimes() context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, VersionHistory: incomingVersionHistory.ToInternalType(), } localVersionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte{}, Items: []*persistence.VersionHistoryItem{ { EventID: 100, Version: 2, }, }, }, }, } s.mockMutableState.EXPECT().GetVersionHistories().Return(localVersionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(persistence.WorkflowStateRunning, persistence.WorkflowCloseStatusNone) s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, lastWriteVersion, ), nil, ).AnyTimes() err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Equal(newNDCRetryTaskErrorWithHint( resendHigherVersionMessage, domainID, workflowID, runID, common.Int64Ptr(50), common.Int64Ptr(2), nil, nil, ), err, ) } func (s *activityReplicatorSuite) TestSyncActivity_VersionHistories_IncomingScheduleIDLarger_ReturnRetryError() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() scheduleID := int64(99) version := int64(100) lastWriteVersion := version - 100 incomingVersionHistory := persistence.VersionHistory{ BranchToken: []byte{}, Items: []*persistence.VersionHistoryItem{ { EventID: 50, Version: 2, }, { EventID: scheduleID, Version: version, }, { EventID: scheduleID + 100, Version: version + 100, }, }, } key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().AnyTimes() context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, VersionHistory: incomingVersionHistory.ToInternalType(), } localVersionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte{}, Items: []*persistence.VersionHistoryItem{ { EventID: scheduleID - 10, Version: version, }, }, }, }, } s.mockMutableState.EXPECT().GetVersionHistories().Return(localVersionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(persistence.WorkflowStateRunning, persistence.WorkflowCloseStatusNone) s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, lastWriteVersion, ), nil, ).AnyTimes() err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Equal(newNDCRetryTaskErrorWithHint( resendMissingEventMessage, domainID, workflowID, runID, common.Int64Ptr(scheduleID-10), common.Int64Ptr(version), nil, nil, ), err, ) } func (s *activityReplicatorSuite) TestSyncActivity_VersionHistories_SameScheduleID() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() scheduleID := int64(99) version := int64(100) lastWriteVersion := version - 100 incomingVersionHistory := persistence.VersionHistory{ BranchToken: []byte{}, Items: []*persistence.VersionHistoryItem{ { EventID: 50, Version: 2, }, { EventID: scheduleID, Version: version, }, }, } key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().AnyTimes() context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, VersionHistory: incomingVersionHistory.ToInternalType(), } localVersionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte{}, Items: []*persistence.VersionHistoryItem{ { EventID: scheduleID, Version: version, }, }, }, }, } s.mockMutableState.EXPECT().GetVersionHistories().Return(localVersionHistories).AnyTimes() s.mockMutableState.EXPECT().GetActivityInfo(scheduleID).Return(nil, false).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus(). Return(persistence.WorkflowStateCreated, persistence.WorkflowCloseStatusNone).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, lastWriteVersion, ), nil, ).AnyTimes() err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Nil(err) } func (s *activityReplicatorSuite) TestSyncActivity_VersionHistories_LocalVersionHistoryWin() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() scheduleID := int64(99) version := int64(100) lastWriteVersion := version - 100 incomingVersionHistory := persistence.VersionHistory{ BranchToken: []byte{}, Items: []*persistence.VersionHistoryItem{ { EventID: scheduleID, Version: version, }, }, } key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().AnyTimes() context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, VersionHistory: incomingVersionHistory.ToInternalType(), } localVersionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: []byte{}, Items: []*persistence.VersionHistoryItem{ { EventID: scheduleID, Version: version, }, { EventID: scheduleID + 1, Version: version + 1, }, }, }, }, } s.mockMutableState.EXPECT().GetVersionHistories().Return(localVersionHistories).AnyTimes() s.mockMutableState.EXPECT().GetActivityInfo(scheduleID).Return(nil, false).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus(). Return(persistence.WorkflowStateCreated, persistence.WorkflowCloseStatusNone).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, lastWriteVersion, ), nil, ).AnyTimes() err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Nil(err) } func (s *activityReplicatorSuite) TestSyncActivity_ActivityCompleted() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() scheduleID := int64(144) version := int64(100) lastWriteVersion := version nextEventID := scheduleID + 10 versionHistoryItem := persistence.NewVersionHistoryItem(scheduleID, version) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ versionHistoryItem, }) versionHistories := persistence.NewVersionHistories(versionHistory) key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().AnyTimes() context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, VersionHistory: versionHistory.ToInternalType(), } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() s.mockMutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(1, 0).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, lastWriteVersion, ), nil, ).AnyTimes() s.mockMutableState.EXPECT().GetActivityInfo(scheduleID).Return(nil, false).AnyTimes() err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Nil(err) } func (s *activityReplicatorSuite) TestSyncActivity_ActivityRunning_LocalActivityVersionLarger() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() scheduleID := int64(144) version := int64(100) lastWriteVersion := version + 10 nextEventID := scheduleID + 10 versionHistoryItem := persistence.NewVersionHistoryItem(scheduleID, version) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ versionHistoryItem, }) versionHistories := persistence.NewVersionHistories(versionHistory) key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().AnyTimes() context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, VersionHistory: versionHistory.ToInternalType(), } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() s.mockMutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(1, 0).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, lastWriteVersion, ), nil, ).AnyTimes() s.mockMutableState.EXPECT().GetActivityInfo(scheduleID).Return(&persistence.ActivityInfo{ Version: lastWriteVersion - 1, }, true).AnyTimes() err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Nil(err) } func (s *activityReplicatorSuite) TestSyncActivity_ActivityRunning_Update_SameVersionSameAttempt() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() version := int64(100) scheduleID := int64(144) scheduledTime := time.Now() startedID := scheduleID + 1 startedTime := scheduledTime.Add(time.Minute) heartBeatUpdatedTime := startedTime.Add(time.Minute) attempt := int32(0) details := []byte("some random activity heartbeat progress") nextEventID := scheduleID + 10 versionHistoryItem := persistence.NewVersionHistoryItem(scheduleID, version) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ versionHistoryItem, }) versionHistories := persistence.NewVersionHistories(versionHistory) key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().Times(1) context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, ScheduledTime: common.Int64Ptr(scheduledTime.UnixNano()), StartedID: startedID, StartedTime: common.Int64Ptr(startedTime.UnixNano()), Attempt: attempt, LastHeartbeatTime: common.Int64Ptr(heartBeatUpdatedTime.UnixNano()), Details: details, VersionHistory: versionHistory.ToInternalType(), } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() s.mockMutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(1, 0).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, version, ), nil, ).AnyTimes() activityInfo := &persistence.ActivityInfo{ Version: version, ScheduleID: scheduleID, Attempt: attempt, } s.mockMutableState.EXPECT().GetActivityInfo(scheduleID).Return(activityInfo, true).AnyTimes() expectedErr := errors.New("this is error is used to by pass lots of mocking") s.mockMutableState.EXPECT().ReplicateActivityInfo(request, false).Return(expectedErr).Times(1) err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Equal(expectedErr, err) } func (s *activityReplicatorSuite) TestSyncActivity_ActivityRunning_Update_SameVersionLargerAttempt() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() version := int64(100) scheduleID := int64(144) scheduledTime := time.Now() startedID := scheduleID + 1 startedTime := scheduledTime.Add(time.Minute) heartBeatUpdatedTime := startedTime.Add(time.Minute) attempt := int32(100) details := []byte("some random activity heartbeat progress") nextEventID := scheduleID + 10 versionHistoryItem := persistence.NewVersionHistoryItem(scheduleID, version) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ versionHistoryItem, }) versionHistories := persistence.NewVersionHistories(versionHistory) key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().Times(1) context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, ScheduledTime: common.Int64Ptr(scheduledTime.UnixNano()), StartedID: startedID, StartedTime: common.Int64Ptr(startedTime.UnixNano()), Attempt: attempt, LastHeartbeatTime: common.Int64Ptr(heartBeatUpdatedTime.UnixNano()), Details: details, VersionHistory: versionHistory.ToInternalType(), } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() s.mockMutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(1, 0).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, version, ), nil, ).AnyTimes() activityInfo := &persistence.ActivityInfo{ Version: version, ScheduleID: scheduleID, Attempt: attempt - 1, } s.mockMutableState.EXPECT().GetActivityInfo(scheduleID).Return(activityInfo, true).AnyTimes() expectedErr := errors.New("this is error is used to by pass lots of mocking") s.mockMutableState.EXPECT().ReplicateActivityInfo(request, true).Return(expectedErr).Times(1) err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Equal(expectedErr, err) } func (s *activityReplicatorSuite) TestSyncActivity_ActivityRunning_Update_LargerVersion() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() version := int64(100) scheduleID := int64(144) scheduledTime := time.Now() startedID := scheduleID + 1 startedTime := scheduledTime.Add(time.Minute) heartBeatUpdatedTime := startedTime.Add(time.Minute) attempt := int32(100) details := []byte("some random activity heartbeat progress") nextEventID := scheduleID + 10 versionHistoryItem := persistence.NewVersionHistoryItem(scheduleID, version) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ versionHistoryItem, }) versionHistories := persistence.NewVersionHistories(versionHistory) key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().Clear().Times(1) context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, ScheduledTime: common.Int64Ptr(scheduledTime.UnixNano()), StartedID: startedID, StartedTime: common.Int64Ptr(startedTime.UnixNano()), Attempt: attempt, LastHeartbeatTime: common.Int64Ptr(heartBeatUpdatedTime.UnixNano()), Details: details, VersionHistory: versionHistory.ToInternalType(), } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() s.mockMutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(1, 0).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, version, ), nil, ).AnyTimes() activityInfo := &persistence.ActivityInfo{ Version: version - 1, ScheduleID: scheduleID, Attempt: attempt + 1, } s.mockMutableState.EXPECT().GetActivityInfo(scheduleID).Return(activityInfo, true).AnyTimes() expectedErr := errors.New("this is error is used to by pass lots of mocking") s.mockMutableState.EXPECT().ReplicateActivityInfo(request, true).Return(expectedErr).Times(1) err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.Equal(expectedErr, err) } func (s *activityReplicatorSuite) TestSyncActivity_ActivityRunning() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() version := int64(100) scheduleID := int64(144) scheduledTime := time.Now() startedID := scheduleID + 1 startedTime := scheduledTime.Add(time.Minute) heartBeatUpdatedTime := startedTime.Add(time.Minute) attempt := int32(100) details := []byte("some random activity heartbeat progress") nextEventID := scheduleID + 10 versionHistoryItem := persistence.NewVersionHistoryItem(scheduleID, version) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ versionHistoryItem, }) versionHistories := persistence.NewVersionHistories(versionHistory) key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, ScheduledTime: common.Int64Ptr(scheduledTime.UnixNano()), StartedID: startedID, StartedTime: common.Int64Ptr(startedTime.UnixNano()), Attempt: attempt, LastHeartbeatTime: common.Int64Ptr(heartBeatUpdatedTime.UnixNano()), Details: details, VersionHistory: versionHistory.ToInternalType(), } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() s.mockMutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(1, 0).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, version, ), nil, ).AnyTimes() activityInfo := &persistence.ActivityInfo{ Version: version - 1, ScheduleID: scheduleID, Attempt: attempt + 1, } s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }) s.mockMutableState.EXPECT().GetActivityInfo(scheduleID).Return(activityInfo, true).AnyTimes() activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).AnyTimes() s.mockMutableState.EXPECT().ReplicateActivityInfo(request, true).Return(nil).Times(1) s.mockMutableState.EXPECT().UpdateActivity(activityInfo).Return(nil).Times(1) s.mockMutableState.EXPECT().GetCurrentVersion().Return(int64(1)).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(gomock.Any()).Times(1) now := time.Unix(0, request.GetLastHeartbeatTime()) context.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeUpdateCurrent, nil, nil, execution.TransactionPolicyPassive, nil, persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.NoError(err) } func (s *activityReplicatorSuite) TestSyncActivity_ActivityRunning_ZombieWorkflow() { domainName := "some random domain name" domainID := constants.TestDomainID workflowID := "some random workflow ID" runID := uuid.New() version := int64(100) scheduleID := int64(144) scheduledTime := time.Now() startedID := scheduleID + 1 startedTime := scheduledTime.Add(time.Minute) heartBeatUpdatedTime := startedTime.Add(time.Minute) attempt := int32(100) details := []byte("some random activity heartbeat progress") nextEventID := scheduleID + 10 versionHistoryItem := persistence.NewVersionHistoryItem(scheduleID, version) versionHistory := persistence.NewVersionHistory([]byte{}, []*persistence.VersionHistoryItem{ versionHistoryItem, }) versionHistories := persistence.NewVersionHistories(versionHistory) key := definition.NewWorkflowIdentifier(domainID, workflowID, runID) context := execution.NewMockContext(s.controller) context.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(s.mockMutableState, nil).Times(1) context.EXPECT().Lock(gomock.Any()).Return(nil) context.EXPECT().Unlock().Times(1) context.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() _, err := s.executionCache.PutIfNotExist(key, context) s.NoError(err) s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Version: version, ScheduledID: scheduleID, ScheduledTime: common.Int64Ptr(scheduledTime.UnixNano()), StartedID: startedID, StartedTime: common.Int64Ptr(startedTime.UnixNano()), Attempt: attempt, LastHeartbeatTime: common.Int64Ptr(heartBeatUpdatedTime.UnixNano()), Details: details, VersionHistory: versionHistory.ToInternalType(), } s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() s.mockMutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetWorkflowStateCloseStatus().Return(3, 0).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(domainID).Return( cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: domainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, version, ), nil, ).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }) activityInfo := &persistence.ActivityInfo{ Version: version - 1, ScheduleID: scheduleID, Attempt: attempt + 1, } s.mockMutableState.EXPECT().GetActivityInfo(scheduleID).Return(activityInfo, true).AnyTimes() activityInfos := map[int64]*persistence.ActivityInfo{activityInfo.ScheduleID: activityInfo} s.mockMutableState.EXPECT().GetPendingActivityInfos().Return(activityInfos).AnyTimes() s.mockMutableState.EXPECT().ReplicateActivityInfo(request, true).Return(nil).Times(1) s.mockMutableState.EXPECT().UpdateActivity(activityInfo).Return(nil).Times(1) s.mockMutableState.EXPECT().GetCurrentVersion().Return(int64(1)).Times(1) s.mockMutableState.EXPECT().AddTimerTasks(gomock.Any()).Times(1) now := time.Unix(0, request.GetLastHeartbeatTime()) context.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeBypassCurrent, nil, nil, execution.TransactionPolicyPassive, nil, persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err = s.activityReplicator.SyncActivity(ctx.Background(), request) s.NoError(err) } ================================================ FILE: service/history/ndc/branch_manager.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination branch_manager_mock.go package ndc import ( "context" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) const ( outOfOrderDeliveryMessage = "Resend events due to out of order delivery" ) type ( branchManager interface { prepareVersionHistory( ctx context.Context, incomingVersionHistory *persistence.VersionHistory, incomingFirstEventID int64, incomingFirstEventVersion int64, ) (bool, int, error) } branchManagerImpl struct { shard shard.Context clusterMetadata cluster.Metadata historyV2Mgr persistence.HistoryManager context execution.Context mutableState execution.MutableState logger log.Logger } ) var _ branchManager = (*branchManagerImpl)(nil) func newBranchManager( shard shard.Context, context execution.Context, mutableState execution.MutableState, logger log.Logger, ) branchManager { return &branchManagerImpl{ shard: shard, clusterMetadata: shard.GetService().GetClusterMetadata(), historyV2Mgr: shard.GetHistoryManager(), context: context, mutableState: mutableState, logger: logger, } } func (r *branchManagerImpl) prepareVersionHistory( ctx context.Context, incomingVersionHistory *persistence.VersionHistory, incomingFirstEventID int64, incomingFirstEventVersion int64, ) (bool, int, error) { versionHistoryIndex, lcaVersionHistoryItem, err := r.flushBufferedEvents(ctx, incomingVersionHistory) if err != nil { return false, 0, err } localVersionHistories := r.mutableState.GetVersionHistories() if localVersionHistories == nil { return false, 0, execution.ErrMissingVersionHistories } versionHistory, err := localVersionHistories.GetVersionHistory(versionHistoryIndex) if err != nil { return false, 0, err } // if can directly append to a branch if versionHistory.IsLCAAppendable(lcaVersionHistoryItem) { doContinue, err := r.verifyEventsOrder( ctx, versionHistory, incomingFirstEventID, incomingFirstEventVersion, ) if err != nil { return false, 0, err } return doContinue, versionHistoryIndex, nil } newVersionHistory, err := versionHistory.DuplicateUntilLCAItem(lcaVersionHistoryItem) if err != nil { return false, 0, err } // if cannot directly append to the new branch to be created doContinue, err := r.verifyEventsOrder( ctx, newVersionHistory, incomingFirstEventID, incomingFirstEventVersion, ) if err != nil || !doContinue { return false, 0, err } newVersionHistoryIndex, err := r.createNewBranch( ctx, versionHistory.GetBranchToken(), lcaVersionHistoryItem.EventID, newVersionHistory, ) if err != nil { return false, 0, err } return true, newVersionHistoryIndex, nil } func (r *branchManagerImpl) flushBufferedEvents( ctx context.Context, incomingVersionHistory *persistence.VersionHistory, ) (int, *persistence.VersionHistoryItem, error) { localVersionHistories := r.mutableState.GetVersionHistories() if localVersionHistories == nil { return 0, nil, execution.ErrMissingVersionHistories } versionHistoryIndex, lcaVersionHistoryItem, err := localVersionHistories.FindLCAVersionHistoryIndexAndItem( incomingVersionHistory, ) if err != nil { return 0, nil, err } // check whether there are buffered events, if so, flush it // NOTE: buffered events does not show in version history or next event id if !r.mutableState.HasBufferedEvents() { return versionHistoryIndex, lcaVersionHistoryItem, nil } targetWorkflow := execution.NewWorkflow( ctx, r.clusterMetadata, r.context, r.mutableState, execution.NoopReleaseFn, r.logger, ) if err := targetWorkflow.FlushBufferedEvents(); err != nil { return 0, nil, err } // the workflow must be updated as active, to send out replication tasks r.logger.Debug("flushBufferedEvents calling UpdateWorkflowExecutionAsActive", tag.WorkflowID(r.mutableState.GetExecutionInfo().WorkflowID)) if err := targetWorkflow.GetContext().UpdateWorkflowExecutionAsActive( ctx, r.shard.GetTimeSource().Now(), ); err != nil { return 0, nil, err } r.context = targetWorkflow.GetContext() r.mutableState = targetWorkflow.GetMutableState() localVersionHistories = r.mutableState.GetVersionHistories() return localVersionHistories.FindLCAVersionHistoryIndexAndItem(incomingVersionHistory) } func (r *branchManagerImpl) verifyEventsOrder( ctx context.Context, localVersionHistory *persistence.VersionHistory, incomingFirstEventID int64, incomingFirstEventVersion int64, ) (bool, error) { lastVersionHistoryItem, err := localVersionHistory.GetLastItem() if err != nil { return false, err } nextEventID := lastVersionHistoryItem.EventID + 1 if incomingFirstEventID < nextEventID { // duplicate replication task return false, nil } if incomingFirstEventID > nextEventID { executionInfo := r.mutableState.GetExecutionInfo() return false, newNDCRetryTaskErrorWithHint( outOfOrderDeliveryMessage, executionInfo.DomainID, executionInfo.WorkflowID, executionInfo.RunID, common.Int64Ptr(lastVersionHistoryItem.EventID), common.Int64Ptr(lastVersionHistoryItem.Version), common.Int64Ptr(incomingFirstEventID), common.Int64Ptr(incomingFirstEventVersion)) } // task.getFirstEvent().GetEventId() == nextEventID return true, nil } func (r *branchManagerImpl) createNewBranch( ctx context.Context, baseBranchToken []byte, baseBranchLastEventID int64, newVersionHistory *persistence.VersionHistory, ) (newVersionHistoryIndex int, retError error) { shardID := r.shard.GetShardID() executionInfo := r.mutableState.GetExecutionInfo() domainID := executionInfo.DomainID workflowID := executionInfo.WorkflowID domainName, err := r.shard.GetDomainCache().GetDomainName(domainID) if err != nil { return 0, err } resp, err := r.historyV2Mgr.ForkHistoryBranch(ctx, &persistence.ForkHistoryBranchRequest{ ForkBranchToken: baseBranchToken, ForkNodeID: baseBranchLastEventID + 1, Info: persistence.BuildHistoryGarbageCleanupInfo(domainID, workflowID, uuid.New()), ShardID: common.IntPtr(shardID), DomainName: domainName, }) if err != nil { return 0, err } if err := newVersionHistory.SetBranchToken(resp.NewBranchToken); err != nil { return 0, err } versionHistory := r.mutableState.GetVersionHistories() if versionHistory == nil { return 0, execution.ErrMissingVersionHistories } branchChanged, newIndex, err := versionHistory.AddVersionHistory( newVersionHistory, ) if err != nil { return 0, err } if branchChanged { return 0, &types.BadRequestError{ Message: "nDCBranchMgr encounter branch change during conflict resolution", } } return newIndex, nil } ================================================ FILE: service/history/ndc/branch_manager_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: branch_manager.go // // Generated by this command: // // mockgen -package ndc -source branch_manager.go -destination branch_manager_mock.go // // Package ndc is a generated GoMock package. package ndc import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" ) // MockbranchManager is a mock of branchManager interface. type MockbranchManager struct { ctrl *gomock.Controller recorder *MockbranchManagerMockRecorder isgomock struct{} } // MockbranchManagerMockRecorder is the mock recorder for MockbranchManager. type MockbranchManagerMockRecorder struct { mock *MockbranchManager } // NewMockbranchManager creates a new mock instance. func NewMockbranchManager(ctrl *gomock.Controller) *MockbranchManager { mock := &MockbranchManager{ctrl: ctrl} mock.recorder = &MockbranchManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockbranchManager) EXPECT() *MockbranchManagerMockRecorder { return m.recorder } // prepareVersionHistory mocks base method. func (m *MockbranchManager) prepareVersionHistory(ctx context.Context, incomingVersionHistory *persistence.VersionHistory, incomingFirstEventID, incomingFirstEventVersion int64) (bool, int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "prepareVersionHistory", ctx, incomingVersionHistory, incomingFirstEventID, incomingFirstEventVersion) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(int) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // prepareVersionHistory indicates an expected call of prepareVersionHistory. func (mr *MockbranchManagerMockRecorder) prepareVersionHistory(ctx, incomingVersionHistory, incomingFirstEventID, incomingFirstEventVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "prepareVersionHistory", reflect.TypeOf((*MockbranchManager)(nil).prepareVersionHistory), ctx, incomingVersionHistory, incomingFirstEventID, incomingFirstEventVersion) } ================================================ FILE: service/history/ndc/branch_manager_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( ctx "context" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) type ( branchManagerSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockContext *execution.MockContext mockMutableState *execution.MockMutableState mockHistoryV2Manager *mocks.HistoryV2Manager logger log.Logger branchIndex int domainID string workflowID string runID string branchManager *branchManagerImpl } ) func TestBranchManagerSuite(t *testing.T) { s := new(branchManagerSuite) suite.Run(t, s) } func (s *branchManagerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockContext = execution.NewMockContext(s.controller) s.mockMutableState = execution.NewMockMutableState(s.controller) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.mockHistoryV2Manager = s.mockShard.Resource.HistoryMgr s.logger = s.mockShard.GetLogger() s.domainID = uuid.New() s.workflowID = "some random workflow ID" s.runID = uuid.New() s.branchIndex = 0 s.branchManager = newBranchManager( s.mockShard, s.mockContext, s.mockMutableState, s.logger, ).(*branchManagerImpl) } func (s *branchManagerSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *branchManagerSuite) TestCreateNewBranch() { baseBranchToken := []byte("some random base branch token") baseBranchLCAEventVersion := int64(200) baseBranchLCAEventID := int64(1394) baseBranchLastEventVersion := int64(400) baseBranchLastEventID := int64(2333) domainName := "test-domain-name" versionHistory := persistence.NewVersionHistory(baseBranchToken, []*persistence.VersionHistoryItem{ persistence.NewVersionHistoryItem(10, 0), persistence.NewVersionHistoryItem(50, 100), persistence.NewVersionHistoryItem(baseBranchLCAEventID, baseBranchLCAEventVersion), persistence.NewVersionHistoryItem(baseBranchLastEventID, baseBranchLastEventVersion), }) versionHistories := persistence.NewVersionHistories(versionHistory) newBranchToken := []byte("some random new branch token") newVersionHistory, err := versionHistory.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(baseBranchLCAEventID, baseBranchLCAEventVersion), ) s.NoError(err) s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, }).AnyTimes() s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() s.mockHistoryV2Manager.On("ForkHistoryBranch", mock.Anything, mock.MatchedBy(func(input *persistence.ForkHistoryBranchRequest) bool { input.Info = "" s.Equal(&persistence.ForkHistoryBranchRequest{ ForkBranchToken: baseBranchToken, ForkNodeID: baseBranchLCAEventID + 1, Info: "", ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }, input) return true })).Return(&persistence.ForkHistoryBranchResponse{ NewBranchToken: newBranchToken, }, nil).Once() newIndex, err := s.branchManager.createNewBranch(ctx.Background(), baseBranchToken, baseBranchLCAEventID, newVersionHistory) s.Nil(err) s.Equal(1, newIndex) compareVersionHistory, err := versionHistory.DuplicateUntilLCAItem( persistence.NewVersionHistoryItem(baseBranchLCAEventID, baseBranchLCAEventVersion), ) s.NoError(err) s.NoError(compareVersionHistory.SetBranchToken(newBranchToken)) newVersionHistory, err = versionHistories.GetVersionHistory(newIndex) s.NoError(err) s.True(compareVersionHistory.Equals(newVersionHistory)) } func (s *branchManagerSuite) TestFlushBufferedEvents() { lastWriteVersion := int64(300) versionHistory := persistence.NewVersionHistory([]byte("some random base branch token"), []*persistence.VersionHistoryItem{ persistence.NewVersionHistoryItem(10, 0), persistence.NewVersionHistoryItem(50, 100), persistence.NewVersionHistoryItem(100, 200), persistence.NewVersionHistoryItem(150, 300), }) versionHistories := persistence.NewVersionHistories(versionHistory) incomingVersionHistory := versionHistory.Duplicate() err := incomingVersionHistory.AddOrUpdateItem( persistence.NewVersionHistoryItem(200, 300), ) s.NoError(err) s.mockMutableState.EXPECT().GetLastWriteVersion().Return(lastWriteVersion, nil).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().HasBufferedEvents().Return(true).AnyTimes() s.mockMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() s.mockMutableState.EXPECT().UpdateCurrentVersion(lastWriteVersion, true).Return(nil).Times(1) decisionInfo := &execution.DecisionInfo{ ScheduleID: 1234, StartedID: 2345, } s.mockMutableState.EXPECT().GetInFlightDecision().Return(decisionInfo, true).Times(1) // GetExecutionInfo's return value is not used by this test s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() s.mockMutableState.EXPECT().AddDecisionTaskFailedEvent( decisionInfo.ScheduleID, decisionInfo.StartedID, types.DecisionTaskFailedCauseFailoverCloseDecision, []byte(nil), execution.IdentityHistoryService, "", "", "", "", int64(0), "", ).Return(&types.HistoryEvent{}, nil).Times(1) s.mockMutableState.EXPECT().FlushBufferedEvents().Return(nil).Times(1) s.mockMutableState.EXPECT().HasPendingDecision().Return(false).Times(1) s.mockMutableState.EXPECT().AddDecisionTaskScheduledEvent(false).Return(&execution.DecisionInfo{}, nil).Times(1) s.mockContext.EXPECT().UpdateWorkflowExecutionAsActive(gomock.Any(), gomock.Any()).Return(nil).Times(1) ctx := ctx.Background() _, _, err = s.branchManager.flushBufferedEvents(ctx, incomingVersionHistory) s.NoError(err) } func (s *branchManagerSuite) TestPrepareVersionHistory_BranchAppendable_NoMissingEventInBetween() { versionHistory := persistence.NewVersionHistory([]byte("some random base branch token"), []*persistence.VersionHistoryItem{ persistence.NewVersionHistoryItem(10, 0), persistence.NewVersionHistoryItem(50, 100), persistence.NewVersionHistoryItem(100, 200), persistence.NewVersionHistoryItem(150, 300), }) versionHistories := persistence.NewVersionHistories(versionHistory) incomingVersionHistory := versionHistory.Duplicate() err := incomingVersionHistory.AddOrUpdateItem( persistence.NewVersionHistoryItem(200, 300), ) s.NoError(err) s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().HasBufferedEvents().Return(false).AnyTimes() doContinue, index, err := s.branchManager.prepareVersionHistory( ctx.Background(), incomingVersionHistory, 150+1, 300) s.NoError(err) s.True(doContinue) s.Equal(0, index) } func (s *branchManagerSuite) TestPrepareVersionHistory_BranchAppendable_MissingEventInBetween() { versionHistory := persistence.NewVersionHistory([]byte("some random base branch token"), []*persistence.VersionHistoryItem{ persistence.NewVersionHistoryItem(10, 0), persistence.NewVersionHistoryItem(50, 100), persistence.NewVersionHistoryItem(100, 200), persistence.NewVersionHistoryItem(150, 300), }) versionHistories := persistence.NewVersionHistories(versionHistory) incomingVersionHistory := versionHistory.Duplicate() incomingFirstEventVersionHistoryItem := persistence.NewVersionHistoryItem(200, 300) err := incomingVersionHistory.AddOrUpdateItem( incomingFirstEventVersionHistoryItem, ) s.NoError(err) execution := &persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, } s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().HasBufferedEvents().Return(false).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(execution).AnyTimes() _, _, err = s.branchManager.prepareVersionHistory( ctx.Background(), incomingVersionHistory, 150+2, 300) s.IsType(&types.RetryTaskV2Error{}, err) } func (s *branchManagerSuite) TestPrepareVersionHistory_BranchNotAppendable_NoMissingEventInBetween() { baseBranchToken := []byte("some random base branch token") baseBranchLCAEventID := int64(85) baseBranchLCAEventVersion := int64(200) domainName := "test-domainName" versionHistory := persistence.NewVersionHistory(baseBranchToken, []*persistence.VersionHistoryItem{ persistence.NewVersionHistoryItem(10, 0), persistence.NewVersionHistoryItem(50, 100), persistence.NewVersionHistoryItem(baseBranchLCAEventID+10, baseBranchLCAEventVersion), persistence.NewVersionHistoryItem(150, 300), }) versionHistories := persistence.NewVersionHistories(versionHistory) incomingVersionHistory := persistence.NewVersionHistory(nil, []*persistence.VersionHistoryItem{ persistence.NewVersionHistoryItem(10, 0), persistence.NewVersionHistoryItem(50, 100), persistence.NewVersionHistoryItem(baseBranchLCAEventID, baseBranchLCAEventVersion), persistence.NewVersionHistoryItem(200, 400), }) newBranchToken := []byte("some random new branch token") s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().HasBufferedEvents().Return(false).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, }).AnyTimes() s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() s.mockHistoryV2Manager.On("ForkHistoryBranch", mock.Anything, mock.MatchedBy(func(input *persistence.ForkHistoryBranchRequest) bool { input.Info = "" s.Equal(&persistence.ForkHistoryBranchRequest{ ForkBranchToken: baseBranchToken, ForkNodeID: baseBranchLCAEventID + 1, Info: "", ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }, input) return true })).Return(&persistence.ForkHistoryBranchResponse{ NewBranchToken: newBranchToken, }, nil).Once() doContinue, index, err := s.branchManager.prepareVersionHistory( ctx.Background(), incomingVersionHistory, baseBranchLCAEventID+1, baseBranchLCAEventVersion, ) s.NoError(err) s.True(doContinue) s.Equal(1, index) } func (s *branchManagerSuite) TestPrepareVersionHistory_BranchNotAppendable_MissingEventInBetween() { baseBranchToken := []byte("some random base branch token") baseBranchLCAEventID := int64(85) baseBranchLCAEventVersion := int64(200) baseBranchLastEventID := int64(150) baseBranchLastEventVersion := int64(300) versionHistory := persistence.NewVersionHistory(baseBranchToken, []*persistence.VersionHistoryItem{ persistence.NewVersionHistoryItem(10, 0), persistence.NewVersionHistoryItem(50, 100), persistence.NewVersionHistoryItem(baseBranchLCAEventID+10, baseBranchLCAEventVersion), persistence.NewVersionHistoryItem(baseBranchLastEventID, baseBranchLastEventVersion), }) versionHistories := persistence.NewVersionHistories(versionHistory) incomingVersionHistory := persistence.NewVersionHistory(nil, []*persistence.VersionHistoryItem{ persistence.NewVersionHistoryItem(10, 0), persistence.NewVersionHistoryItem(50, 100), persistence.NewVersionHistoryItem(baseBranchLCAEventID, baseBranchLCAEventVersion), persistence.NewVersionHistoryItem(200, 400), }) execution := &persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, } s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().HasBufferedEvents().Return(false).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(execution).AnyTimes() _, _, err := s.branchManager.prepareVersionHistory( ctx.Background(), incomingVersionHistory, baseBranchLCAEventID+2, baseBranchLCAEventVersion, ) s.IsType(&types.RetryTaskV2Error{}, err) } ================================================ FILE: service/history/ndc/conflict_resolver.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination conflict_resolver_mock.go package ndc import ( ctx "context" "github.com/pborman/uuid" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) type ( conflictResolver interface { prepareMutableState( ctx ctx.Context, branchIndex int, incomingVersion int64, ) (execution.MutableState, bool, error) } conflictResolverImpl struct { shard shard.Context stateRebuilder execution.StateRebuilder context execution.Context mutableState execution.MutableState logger log.Logger } ) var _ conflictResolver = (*conflictResolverImpl)(nil) func newConflictResolver( shard shard.Context, context execution.Context, mutableState execution.MutableState, logger log.Logger, ) conflictResolver { return &conflictResolverImpl{ shard: shard, stateRebuilder: execution.NewStateRebuilder(shard, logger), context: context, mutableState: mutableState, logger: logger, } } func (r *conflictResolverImpl) prepareMutableState( ctx ctx.Context, branchIndex int, incomingVersion int64, ) (execution.MutableState, bool, error) { versionHistories := r.mutableState.GetVersionHistories() if versionHistories == nil { return nil, false, execution.ErrMissingVersionHistories } currentVersionHistoryIndex := versionHistories.GetCurrentVersionHistoryIndex() // replication task to be applied to current branch if branchIndex == currentVersionHistoryIndex { return r.mutableState, false, nil } currentVersionHistory, err := versionHistories.GetVersionHistory(currentVersionHistoryIndex) if err != nil { return nil, false, err } currentLastItem, err := currentVersionHistory.GetLastItem() if err != nil { return nil, false, err } // mutable state does not need rebuild if incomingVersion < currentLastItem.Version { return r.mutableState, false, nil } if incomingVersion == currentLastItem.Version { return nil, false, &types.BadRequestError{ Message: "nDCConflictResolver encounter replication task version == current branch last write version", } } // task.getVersion() > currentLastItem // incoming replication task, after application, will become the current branch // (because higher version wins), we need to rebuild the mutable state for that rebuiltMutableState, err := r.rebuild(ctx, branchIndex, uuid.New()) if err != nil { return nil, false, err } return rebuiltMutableState, true, nil } func (r *conflictResolverImpl) rebuild( ctx ctx.Context, branchIndex int, requestID string, ) (execution.MutableState, error) { versionHistories := r.mutableState.GetVersionHistories() if versionHistories == nil { return nil, execution.ErrMissingVersionHistories } replayVersionHistory, err := versionHistories.GetVersionHistory(branchIndex) if err != nil { return nil, err } lastItem, err := replayVersionHistory.GetLastItem() if err != nil { return nil, err } executionInfo := r.mutableState.GetExecutionInfo() workflowIdentifier := definition.NewWorkflowIdentifier( executionInfo.DomainID, executionInfo.WorkflowID, executionInfo.RunID, ) rebuildMutableState, rebuiltHistorySize, err := r.stateRebuilder.Rebuild( ctx, executionInfo.StartTimestamp, workflowIdentifier, replayVersionHistory.GetBranchToken(), lastItem.EventID, lastItem.Version, workflowIdentifier, func() ([]byte, error) { return replayVersionHistory.GetBranchToken(), nil }, requestID, ) if err != nil { return nil, err } // after rebuilt verification rebuildVersionHistories := rebuildMutableState.GetVersionHistories() if rebuildVersionHistories == nil { return nil, execution.ErrMissingVersionHistories } rebuildVersionHistory, err := rebuildVersionHistories.GetCurrentVersionHistory() if err != nil { return nil, err } if !rebuildVersionHistory.Equals(replayVersionHistory) { return nil, &types.InternalServiceError{ Message: "nDCConflictResolver encounter mismatch version history after rebuild", } } // set the current branch index to target branch index // set the version history back // // caller can use the IsRebuilt function in VersionHistories // telling whether mutable state is rebuilt, before apply new history events if err := versionHistories.SetCurrentVersionHistoryIndex(branchIndex); err != nil { return nil, err } if err = rebuildMutableState.SetVersionHistories(versionHistories); err != nil { return nil, err } // set the update condition from original mutable state rebuildMutableState.SetUpdateCondition(r.mutableState.GetUpdateCondition()) r.context.Clear() r.context.SetHistorySize(rebuiltHistorySize) return rebuildMutableState, nil } ================================================ FILE: service/history/ndc/conflict_resolver_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: conflict_resolver.go // // Generated by this command: // // mockgen -package ndc -source conflict_resolver.go -destination conflict_resolver_mock.go // // Package ndc is a generated GoMock package. package ndc import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" execution "github.com/uber/cadence/service/history/execution" ) // MockconflictResolver is a mock of conflictResolver interface. type MockconflictResolver struct { ctrl *gomock.Controller recorder *MockconflictResolverMockRecorder isgomock struct{} } // MockconflictResolverMockRecorder is the mock recorder for MockconflictResolver. type MockconflictResolverMockRecorder struct { mock *MockconflictResolver } // NewMockconflictResolver creates a new mock instance. func NewMockconflictResolver(ctrl *gomock.Controller) *MockconflictResolver { mock := &MockconflictResolver{ctrl: ctrl} mock.recorder = &MockconflictResolverMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockconflictResolver) EXPECT() *MockconflictResolverMockRecorder { return m.recorder } // prepareMutableState mocks base method. func (m *MockconflictResolver) prepareMutableState(ctx context.Context, branchIndex int, incomingVersion int64) (execution.MutableState, bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "prepareMutableState", ctx, branchIndex, incomingVersion) ret0, _ := ret[0].(execution.MutableState) ret1, _ := ret[1].(bool) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // prepareMutableState indicates an expected call of prepareMutableState. func (mr *MockconflictResolverMockRecorder) prepareMutableState(ctx, branchIndex, incomingVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "prepareMutableState", reflect.TypeOf((*MockconflictResolver)(nil).prepareMutableState), ctx, branchIndex, incomingVersion) } ================================================ FILE: service/history/ndc/conflict_resolver_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( ctx "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "golang.org/x/net/context" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) type ( conflictResolverSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockContext *execution.MockContext mockMutableState *execution.MockMutableState mockStateBuilder *execution.MockStateRebuilder logger log.Logger domainID string domainName string workflowID string runID string nDCConflictResolver *conflictResolverImpl } ) func TestConflictResolverSuite(t *testing.T) { s := new(conflictResolverSuite) suite.Run(t, s) } func (s *conflictResolverSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockContext = execution.NewMockContext(s.controller) s.mockMutableState = execution.NewMockMutableState(s.controller) s.mockStateBuilder = execution.NewMockStateRebuilder(s.controller) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.logger = s.mockShard.GetLogger() s.domainID = uuid.New() s.domainName = "some random domain name" s.workflowID = "some random workflow ID" s.runID = uuid.New() s.nDCConflictResolver = newConflictResolver( s.mockShard, s.mockContext, s.mockMutableState, s.logger, ).(*conflictResolverImpl) s.nDCConflictResolver.stateRebuilder = s.mockStateBuilder } func (s *conflictResolverSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *conflictResolverSuite) TestRebuild() { ctx := ctx.Background() updateCondition := int64(59) requestID := uuid.New() version := int64(12) historySize := int64(12345) branchToken0 := []byte("some random branch token") lastEventID0 := int64(5) versionHistory0 := persistence.NewVersionHistory( branchToken0, []*persistence.VersionHistoryItem{persistence.NewVersionHistoryItem(lastEventID0, version)}, ) branchToken1 := []byte("other random branch token") lastEventID1 := int64(2) versionHistory1 := persistence.NewVersionHistory( branchToken1, []*persistence.VersionHistoryItem{persistence.NewVersionHistoryItem(lastEventID1, version)}, ) versionHistories := persistence.NewVersionHistories(versionHistory0) _, _, err := versionHistories.AddVersionHistory(versionHistory1) s.NoError(err) s.mockMutableState.EXPECT().GetUpdateCondition().Return(updateCondition).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, }).AnyTimes() workflowIdentifier := definition.NewWorkflowIdentifier( s.domainID, s.workflowID, s.runID, ) mockRebuildMutableState := execution.NewMockMutableState(s.controller) mockRebuildMutableState.EXPECT().GetVersionHistories().Return( persistence.NewVersionHistories( persistence.NewVersionHistory( branchToken1, []*persistence.VersionHistoryItem{persistence.NewVersionHistoryItem(lastEventID1, version)}, ), ), ).Times(1) mockRebuildMutableState.EXPECT().SetVersionHistories(versionHistories).Return(nil).Times(1) mockRebuildMutableState.EXPECT().SetUpdateCondition(updateCondition).Times(1) s.mockStateBuilder.EXPECT().Rebuild( ctx, gomock.Any(), workflowIdentifier, branchToken1, lastEventID1, version, workflowIdentifier, gomock.Any(), requestID, ).DoAndReturn(func(ctx context.Context, now time.Time, baseWorkflowIdentifier definition.WorkflowIdentifier, baseBranchToken []byte, baseRebuildLastEventID int64, baseRebuildLastEventVersion int64, targetWorkflowIdentifier definition.WorkflowIdentifier, targetBranchFn func() ([]byte, error), requestID string) (execution.MutableState, int64, error) { targetBranchToken, err := targetBranchFn() s.NoError(err) s.Equal(branchToken1, targetBranchToken) return mockRebuildMutableState, historySize, nil }).Times(1) s.mockContext.EXPECT().Clear().Times(1) s.mockContext.EXPECT().SetHistorySize(historySize).Times(1) rebuiltMutableState, err := s.nDCConflictResolver.rebuild(ctx, 1, requestID) s.NoError(err) s.NotNil(rebuiltMutableState) s.Equal(1, versionHistories.GetCurrentVersionHistoryIndex()) } func (s *conflictResolverSuite) TestPrepareMutableState_NoRebuild() { branchToken := []byte("some random branch token") lastEventID := int64(2) version := int64(12) versionHistoryItem := persistence.NewVersionHistoryItem(lastEventID, version) versionHistory := persistence.NewVersionHistory( branchToken, []*persistence.VersionHistoryItem{versionHistoryItem}, ) versionHistories := persistence.NewVersionHistories(versionHistory) s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() rebuiltMutableState, isRebuilt, err := s.nDCConflictResolver.prepareMutableState(ctx.Background(), 0, version) s.NoError(err) s.False(isRebuilt) s.Equal(s.mockMutableState, rebuiltMutableState) } func (s *conflictResolverSuite) TestPrepareMutableState_Rebuild() { ctx := ctx.Background() updateCondition := int64(59) version := int64(12) incomingVersion := version + 1 historySize := int64(12345) // current branch branchToken0 := []byte("some random branch token") lastEventID0 := int64(2) versionHistoryItem0 := persistence.NewVersionHistoryItem(lastEventID0, version) versionHistory0 := persistence.NewVersionHistory( branchToken0, []*persistence.VersionHistoryItem{versionHistoryItem0}, ) // stale branch, used for rebuild branchToken1 := []byte("other random branch token") lastEventID1 := lastEventID0 - 1 versionHistoryItem1 := persistence.NewVersionHistoryItem(lastEventID1, version) versionHistory1 := persistence.NewVersionHistory( branchToken1, []*persistence.VersionHistoryItem{versionHistoryItem1}, ) versionHistories := persistence.NewVersionHistories(versionHistory0) _, _, err := versionHistories.AddVersionHistory(versionHistory1) s.Nil(err) s.mockMutableState.EXPECT().GetUpdateCondition().Return(updateCondition).AnyTimes() s.mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.runID, }).AnyTimes() workflowIdentifier := definition.NewWorkflowIdentifier( s.domainID, s.workflowID, s.runID, ) mockRebuildMutableState := execution.NewMockMutableState(s.controller) mockRebuildMutableState.EXPECT().GetVersionHistories().Return( persistence.NewVersionHistories( persistence.NewVersionHistory( branchToken1, []*persistence.VersionHistoryItem{persistence.NewVersionHistoryItem(lastEventID1, version)}, ), ), ).Times(1) mockRebuildMutableState.EXPECT().SetVersionHistories(versionHistories).Return(nil).Times(1) mockRebuildMutableState.EXPECT().SetUpdateCondition(updateCondition).Times(1) s.mockStateBuilder.EXPECT().Rebuild( ctx, gomock.Any(), workflowIdentifier, branchToken1, lastEventID1, version, workflowIdentifier, gomock.Any(), gomock.Any(), ).DoAndReturn(func(ctx context.Context, now time.Time, baseWorkflowIdentifier definition.WorkflowIdentifier, baseBranchToken []byte, baseRebuildLastEventID int64, baseRebuildLastEventVersion int64, targetWorkflowIdentifier definition.WorkflowIdentifier, targetBranchFn func() ([]byte, error), requestID string) (execution.MutableState, int64, error) { targetBranchToken, err := targetBranchFn() s.NoError(err) s.Equal(branchToken1, targetBranchToken) return mockRebuildMutableState, historySize, nil }).Times(1) s.mockContext.EXPECT().Clear().Times(1) s.mockContext.EXPECT().SetHistorySize(int64(historySize)).Times(1) rebuiltMutableState, isRebuilt, err := s.nDCConflictResolver.prepareMutableState(ctx, 1, incomingVersion) s.NoError(err) s.NotNil(rebuiltMutableState) s.True(isRebuilt) } ================================================ FILE: service/history/ndc/events_reapplier.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination events_reapplier_mock.go package ndc import ( ctx "context" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" ) type ( // EventsReapplier handles event re-application EventsReapplier interface { ReapplyEvents( ctx ctx.Context, msBuilder execution.MutableState, historyEvents []*types.HistoryEvent, runID string, ) ([]*types.HistoryEvent, error) } eventsReapplierImpl struct { metricsClient metrics.Client logger log.Logger } ) var _ EventsReapplier = (*eventsReapplierImpl)(nil) // NewEventsReapplier creates events reapplier func NewEventsReapplier( metricsClient metrics.Client, logger log.Logger, ) EventsReapplier { return &eventsReapplierImpl{ metricsClient: metricsClient, logger: logger, } } func (r *eventsReapplierImpl) ReapplyEvents( ctx ctx.Context, msBuilder execution.MutableState, historyEvents []*types.HistoryEvent, runID string, ) ([]*types.HistoryEvent, error) { var reappliedEvents []*types.HistoryEvent for _, event := range historyEvents { switch event.GetEventType() { case types.EventTypeWorkflowExecutionSignaled: dedupResource := definition.NewEventReappliedID(runID, event.ID, event.Version) if msBuilder.IsResourceDuplicated(dedupResource) { // skip already applied event continue } reappliedEvents = append(reappliedEvents, event) } } if len(reappliedEvents) == 0 { return nil, nil } // sanity check workflow still running if !msBuilder.IsWorkflowExecutionRunning() { return nil, &types.InternalServiceError{ Message: "unable to reapply events to closed workflow.", } } for _, event := range reappliedEvents { signal := event.GetWorkflowExecutionSignaledEventAttributes() if _, err := msBuilder.AddWorkflowExecutionSignaled( signal.GetSignalName(), signal.GetInput(), signal.GetIdentity(), "", // Do not set requestID for requests reapplied, because they have already been applied previously ); err != nil { return nil, err } deDupResource := definition.NewEventReappliedID(runID, event.ID, event.Version) msBuilder.UpdateDuplicatedResource(deDupResource) } return reappliedEvents, nil } ================================================ FILE: service/history/ndc/events_reapplier_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: events_reapplier.go // // Generated by this command: // // mockgen -package ndc -source events_reapplier.go -destination events_reapplier_mock.go // // Package ndc is a generated GoMock package. package ndc import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" execution "github.com/uber/cadence/service/history/execution" ) // MockEventsReapplier is a mock of EventsReapplier interface. type MockEventsReapplier struct { ctrl *gomock.Controller recorder *MockEventsReapplierMockRecorder isgomock struct{} } // MockEventsReapplierMockRecorder is the mock recorder for MockEventsReapplier. type MockEventsReapplierMockRecorder struct { mock *MockEventsReapplier } // NewMockEventsReapplier creates a new mock instance. func NewMockEventsReapplier(ctrl *gomock.Controller) *MockEventsReapplier { mock := &MockEventsReapplier{ctrl: ctrl} mock.recorder = &MockEventsReapplierMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockEventsReapplier) EXPECT() *MockEventsReapplierMockRecorder { return m.recorder } // ReapplyEvents mocks base method. func (m *MockEventsReapplier) ReapplyEvents(ctx context.Context, msBuilder execution.MutableState, historyEvents []*types.HistoryEvent, runID string) ([]*types.HistoryEvent, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReapplyEvents", ctx, msBuilder, historyEvents, runID) ret0, _ := ret[0].([]*types.HistoryEvent) ret1, _ := ret[1].(error) return ret0, ret1 } // ReapplyEvents indicates an expected call of ReapplyEvents. func (mr *MockEventsReapplierMockRecorder) ReapplyEvents(ctx, msBuilder, historyEvents, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReapplyEvents", reflect.TypeOf((*MockEventsReapplier)(nil).ReapplyEvents), ctx, msBuilder, historyEvents, runID) } ================================================ FILE: service/history/ndc/events_reapplier_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( "context" "fmt" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" ) type ( eventReapplicationSuite struct { suite.Suite *require.Assertions controller *gomock.Controller reapplication EventsReapplier } ) func TestEventReapplicationSuite(t *testing.T) { s := new(eventReapplicationSuite) suite.Run(t, s) } func (s *eventReapplicationSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) logger := testlogger.New(s.Suite.T()) metricsClient := metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) s.reapplication = NewEventsReapplier( metricsClient, logger, ) } func (s *eventReapplicationSuite) TearDownTest() { s.controller.Finish() } func (s *eventReapplicationSuite) TestReapplyEvents_AppliedEvent() { runID := uuid.New() workflowExecution := &persistence.WorkflowExecutionInfo{ DomainID: uuid.New(), } event := &types.HistoryEvent{ ID: 1, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ Identity: "test", SignalName: "signal", Input: []byte{}, RequestID: "b90794a5-e9f4-4f41-9ebf-38aeedefd4ef", }, } attr := event.WorkflowExecutionSignaledEventAttributes msBuilderCurrent := execution.NewMockMutableState(s.controller) msBuilderCurrent.EXPECT().IsWorkflowExecutionRunning().Return(true) msBuilderCurrent.EXPECT().GetLastWriteVersion().Return(int64(1), nil).AnyTimes() msBuilderCurrent.EXPECT().GetExecutionInfo().Return(workflowExecution).AnyTimes() msBuilderCurrent.EXPECT().AddWorkflowExecutionSignaled( attr.GetSignalName(), attr.GetInput(), attr.GetIdentity(), "", ).Return(event, nil).Times(1) dedupResource := definition.NewEventReappliedID(runID, event.ID, event.Version) msBuilderCurrent.EXPECT().IsResourceDuplicated(dedupResource).Return(false).Times(1) msBuilderCurrent.EXPECT().UpdateDuplicatedResource(dedupResource).Times(1) events := []*types.HistoryEvent{ {EventType: types.EventTypeWorkflowExecutionStarted.Ptr()}, event, } appliedEvent, err := s.reapplication.ReapplyEvents(context.Background(), msBuilderCurrent, events, runID) s.NoError(err) s.Equal(1, len(appliedEvent)) } func (s *eventReapplicationSuite) TestReapplyEvents_Noop() { runID := uuid.New() event := &types.HistoryEvent{ ID: 1, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ Identity: "test", SignalName: "signal", Input: []byte{}, }, } msBuilderCurrent := execution.NewMockMutableState(s.controller) dedupResource := definition.NewEventReappliedID(runID, event.ID, event.Version) msBuilderCurrent.EXPECT().IsResourceDuplicated(dedupResource).Return(true).Times(1) events := []*types.HistoryEvent{ {EventType: types.EventTypeWorkflowExecutionStarted.Ptr()}, event, } appliedEvent, err := s.reapplication.ReapplyEvents(context.Background(), msBuilderCurrent, events, runID) s.NoError(err) s.Equal(0, len(appliedEvent)) } func (s *eventReapplicationSuite) TestReapplyEvents_PartialAppliedEvent() { runID := uuid.New() workflowExecution := &persistence.WorkflowExecutionInfo{ DomainID: uuid.New(), } event1 := &types.HistoryEvent{ ID: 1, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ Identity: "test", SignalName: "signal", Input: []byte{}, RequestID: "3eb0594e-82dd-4335-8284-855c99d61c74", }, } event2 := &types.HistoryEvent{ ID: 2, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ Identity: "test", SignalName: "signal", Input: []byte{}, RequestID: "2d2cae90-1ae4-4bcb-99a8-30b1cce64e3e", }, } attr1 := event1.WorkflowExecutionSignaledEventAttributes msBuilderCurrent := execution.NewMockMutableState(s.controller) msBuilderCurrent.EXPECT().IsWorkflowExecutionRunning().Return(true) msBuilderCurrent.EXPECT().GetLastWriteVersion().Return(int64(1), nil).AnyTimes() msBuilderCurrent.EXPECT().GetExecutionInfo().Return(workflowExecution).AnyTimes() msBuilderCurrent.EXPECT().AddWorkflowExecutionSignaled( attr1.GetSignalName(), attr1.GetInput(), attr1.GetIdentity(), "", ).Return(event1, nil).Times(1) dedupResource1 := definition.NewEventReappliedID(runID, event1.ID, event1.Version) msBuilderCurrent.EXPECT().IsResourceDuplicated(dedupResource1).Return(false).Times(1) dedupResource2 := definition.NewEventReappliedID(runID, event2.ID, event2.Version) msBuilderCurrent.EXPECT().IsResourceDuplicated(dedupResource2).Return(true).Times(1) msBuilderCurrent.EXPECT().UpdateDuplicatedResource(dedupResource1).Times(1) events := []*types.HistoryEvent{ {EventType: types.EventTypeWorkflowExecutionStarted.Ptr()}, event1, event2, } appliedEvent, err := s.reapplication.ReapplyEvents(context.Background(), msBuilderCurrent, events, runID) s.NoError(err) s.Equal(1, len(appliedEvent)) } func (s *eventReapplicationSuite) TestReapplyEvents_Error() { runID := uuid.New() workflowExecution := &persistence.WorkflowExecutionInfo{ DomainID: uuid.New(), } event := &types.HistoryEvent{ ID: 1, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ Identity: "test", SignalName: "signal", Input: []byte{}, RequestID: "1ece6551-27ac-4a9f-a086-5a780dea10f7", }, } attr := event.WorkflowExecutionSignaledEventAttributes msBuilderCurrent := execution.NewMockMutableState(s.controller) msBuilderCurrent.EXPECT().IsWorkflowExecutionRunning().Return(true) msBuilderCurrent.EXPECT().GetLastWriteVersion().Return(int64(1), nil).AnyTimes() msBuilderCurrent.EXPECT().GetExecutionInfo().Return(workflowExecution).AnyTimes() msBuilderCurrent.EXPECT().AddWorkflowExecutionSignaled( attr.GetSignalName(), attr.GetInput(), attr.GetIdentity(), "", ).Return(nil, fmt.Errorf("test")).Times(1) dedupResource := definition.NewEventReappliedID(runID, event.ID, event.Version) msBuilderCurrent.EXPECT().IsResourceDuplicated(dedupResource).Return(false).Times(1) events := []*types.HistoryEvent{ {EventType: types.EventTypeWorkflowExecutionStarted.Ptr()}, event, } appliedEvent, err := s.reapplication.ReapplyEvents(context.Background(), msBuilderCurrent, events, runID) s.Error(err) s.Equal(0, len(appliedEvent)) } ================================================ FILE: service/history/ndc/existing_workflow_transaction_manager.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination existing_workflow_transaction_manager_mock.go package ndc import ( ctx "context" "fmt" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" ) type ( transactionManagerForExistingWorkflow interface { dispatchForExistingWorkflow( ctx ctx.Context, now time.Time, isWorkflowRebuilt bool, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error } transactionManagerForExistingWorkflowImpl struct { transactionManager transactionManager logger log.Logger } ) var _ transactionManagerForExistingWorkflow = (*transactionManagerForExistingWorkflowImpl)(nil) func newTransactionManagerForExistingWorkflow( transactionManager transactionManager, logger log.Logger, ) transactionManagerForExistingWorkflow { return &transactionManagerForExistingWorkflowImpl{ transactionManager: transactionManager, logger: logger, } } func (r *transactionManagerForExistingWorkflowImpl) dispatchForExistingWorkflow( ctx ctx.Context, now time.Time, isWorkflowRebuilt bool, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error { // NOTE: this function does NOT mutate current workflow, target workflow or new workflow, // workflow mutation is done in methods within executeTransaction function // this is a performance optimization so most update does not need to // check whether target workflow is current workflow by calling DB API if !isWorkflowRebuilt && targetWorkflow.GetMutableState().IsCurrentWorkflowGuaranteed() { // NOTE: if target workflow is rebuilt, then IsCurrentWorkflowGuaranteed is not trustworthy // update to current record, since target workflow is pointed by current record return r.dispatchWorkflowUpdateAsCurrent( ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow, ) } targetExecutionInfo := targetWorkflow.GetMutableState().GetExecutionInfo() domainID := targetExecutionInfo.DomainID workflowID := targetExecutionInfo.WorkflowID targetRunID := targetExecutionInfo.RunID // the target workflow is rebuilt // we need to check the current workflow execution currentRunID, err := r.transactionManager.getCurrentWorkflowRunID( ctx, domainID, workflowID, ) if err != nil { return err } if currentRunID == "" { // this means a bug in our code or DB is inconsistent... return &types.InternalServiceError{ Message: "nDCTransactionManager: unable to locate current workflow during update", } } if currentRunID == targetRunID { if !isWorkflowRebuilt { return &types.InternalServiceError{ Message: "nDCTransactionManager: encounter workflow not rebuilt & current workflow not guaranteed", } } // update to current record, since target workflow is pointed by current record return r.dispatchWorkflowUpdateAsCurrent( ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow, ) } // there exists a current workflow, need additional check currentWorkflow, err := r.transactionManager.loadNDCWorkflow( ctx, domainID, workflowID, currentRunID, ) if err != nil { return err } targetWorkflowIsNewer, err := targetWorkflow.HappensAfter(currentWorkflow) if err != nil { return err } if !targetWorkflowIsNewer { // target workflow is older than current workflow, need to suppress the target workflow return r.dispatchWorkflowUpdateAsZombie( ctx, now, isWorkflowRebuilt, currentWorkflow, targetWorkflow, newWorkflow, ) } // isWorkflowRebuilt is irrelevant here, because the DB API to be used // will set target workflow using snapshot return r.executeTransaction( ctx, now, transactionPolicySuppressCurrentAndUpdateAsCurrent, currentWorkflow, targetWorkflow, newWorkflow, ) } func (r *transactionManagerForExistingWorkflowImpl) dispatchWorkflowUpdateAsCurrent( ctx ctx.Context, now time.Time, isWorkflowRebuilt bool, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error { if !isWorkflowRebuilt { return r.executeTransaction( ctx, now, transactionPolicyUpdateAsCurrent, nil, targetWorkflow, newWorkflow, ) } return r.executeTransaction( ctx, now, transactionPolicyConflictResolveAsCurrent, nil, targetWorkflow, newWorkflow, ) } func (r *transactionManagerForExistingWorkflowImpl) dispatchWorkflowUpdateAsZombie( ctx ctx.Context, now time.Time, isWorkflowRebuilt bool, currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error { if !isWorkflowRebuilt { return r.executeTransaction( ctx, now, transactionPolicyUpdateAsZombie, currentWorkflow, targetWorkflow, newWorkflow, ) } return r.executeTransaction( ctx, now, transactionPolicyConflictResolveAsZombie, currentWorkflow, targetWorkflow, newWorkflow, ) } func (r *transactionManagerForExistingWorkflowImpl) updateAsCurrent( ctx ctx.Context, now time.Time, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error { if newWorkflow == nil { return targetWorkflow.GetContext().UpdateWorkflowExecutionAsPassive(ctx, now) } return targetWorkflow.GetContext().UpdateWorkflowExecutionWithNewAsPassive( ctx, now, newWorkflow.GetContext(), newWorkflow.GetMutableState(), ) } func (r *transactionManagerForExistingWorkflowImpl) updateAsZombie( ctx ctx.Context, now time.Time, currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error { targetPolicy, err := targetWorkflow.SuppressBy( currentWorkflow, ) if err != nil { return err } if targetPolicy != execution.TransactionPolicyPassive { return &types.InternalServiceError{ Message: "nDCTransactionManagerForExistingWorkflow updateAsZombie encounter target workflow policy not being passive", } } var newContext execution.Context var newMutableState execution.MutableState var newTransactionPolicy *execution.TransactionPolicy if newWorkflow != nil { newWorkflowPolicy, err := newWorkflow.SuppressBy( currentWorkflow, ) if err != nil { return err } if newWorkflowPolicy != execution.TransactionPolicyPassive { return &types.InternalServiceError{ Message: "nDCTransactionManagerForExistingWorkflow updateAsZombie encounter new workflow policy not being passive", } } // sanity check if new workflow is already created // since workflow resend can have already created the new workflow newExecutionInfo := newWorkflow.GetMutableState().GetExecutionInfo() newWorkflowExists, err := r.transactionManager.checkWorkflowExists( ctx, newExecutionInfo.DomainID, newExecutionInfo.WorkflowID, newExecutionInfo.RunID, ) if err != nil { return err } if newWorkflowExists { // new workflow already exists, do not create again newContext = nil newMutableState = nil newTransactionPolicy = nil } else { // new workflow does not exists, continue newContext = newWorkflow.GetContext() newMutableState = newWorkflow.GetMutableState() newTransactionPolicy = execution.TransactionPolicyPassive.Ptr() } } // release lock on current workflow, since current cluster maybe the active cluster // and events maybe reapplied to current workflow currentWorkflow.GetReleaseFn()(nil) currentWorkflow = nil r.logger.Debugf("updateAsZombie calling UpdateWorkflowExecutionWithNew for wfID %s, current policy %v, new policy %v", targetWorkflow.GetMutableState().GetExecutionInfo().WorkflowID, execution.TransactionPolicyPassive, newTransactionPolicy, ) return targetWorkflow.GetContext().UpdateWorkflowExecutionWithNew( ctx, now, persistence.UpdateWorkflowModeBypassCurrent, newContext, newMutableState, execution.TransactionPolicyPassive, newTransactionPolicy, persistence.CreateWorkflowRequestModeReplicated, ) } func (r *transactionManagerForExistingWorkflowImpl) suppressCurrentAndUpdateAsCurrent( ctx ctx.Context, now time.Time, currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error { var err error currentWorkflowPolicy := execution.TransactionPolicyPassive if currentWorkflow.GetMutableState().IsWorkflowExecutionRunning() { currentWorkflowPolicy, err = currentWorkflow.SuppressBy( targetWorkflow, ) if err != nil { return err } } if err := targetWorkflow.Revive(); err != nil { return err } var newContext execution.Context var newMutableState execution.MutableState if newWorkflow != nil { newContext = newWorkflow.GetContext() newMutableState = newWorkflow.GetMutableState() if err := newWorkflow.Revive(); err != nil { return err } } return targetWorkflow.GetContext().ConflictResolveWorkflowExecution( ctx, now, persistence.ConflictResolveWorkflowModeUpdateCurrent, targetWorkflow.GetMutableState(), newContext, newMutableState, currentWorkflow.GetContext(), currentWorkflow.GetMutableState(), currentWorkflowPolicy.Ptr(), ) } func (r *transactionManagerForExistingWorkflowImpl) conflictResolveAsCurrent( ctx ctx.Context, now time.Time, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error { var newContext execution.Context var newMutableState execution.MutableState if newWorkflow != nil { newContext = newWorkflow.GetContext() newMutableState = newWorkflow.GetMutableState() } return targetWorkflow.GetContext().ConflictResolveWorkflowExecution( ctx, now, persistence.ConflictResolveWorkflowModeUpdateCurrent, targetWorkflow.GetMutableState(), newContext, newMutableState, nil, nil, nil, ) } func (r *transactionManagerForExistingWorkflowImpl) conflictResolveAsZombie( ctx ctx.Context, now time.Time, currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error { targetWorkflowPolicy, err := targetWorkflow.SuppressBy( currentWorkflow, ) if err != nil { return err } if targetWorkflowPolicy != execution.TransactionPolicyPassive { return &types.InternalServiceError{ Message: "nDCTransactionManagerForExistingWorkflow conflictResolveAsZombie encounter target workflow policy not being passive", } } var newContext execution.Context var newMutableState execution.MutableState if newWorkflow != nil { newWorkflowPolicy, err := newWorkflow.SuppressBy( currentWorkflow, ) if err != nil { return err } if newWorkflowPolicy != execution.TransactionPolicyPassive { return &types.InternalServiceError{ Message: "nDCTransactionManagerForExistingWorkflow conflictResolveAsZombie encounter new workflow policy not being passive", } } // sanity check if new workflow is already created // since workflow resend can have already created the new workflow newExecutionInfo := newWorkflow.GetMutableState().GetExecutionInfo() newWorkflowExists, err := r.transactionManager.checkWorkflowExists( ctx, newExecutionInfo.DomainID, newExecutionInfo.WorkflowID, newExecutionInfo.RunID, ) if err != nil { return err } if newWorkflowExists { // new workflow already exists, do not create again newContext = nil newMutableState = nil } else { // new workflow does not exists, continue newContext = newWorkflow.GetContext() newMutableState = newWorkflow.GetMutableState() } } // release lock on current workflow, since current cluster maybe the active cluster // and events maybe reapplied to current workflow currentWorkflow.GetReleaseFn()(nil) currentWorkflow = nil return targetWorkflow.GetContext().ConflictResolveWorkflowExecution( ctx, now, persistence.ConflictResolveWorkflowModeBypassCurrent, targetWorkflow.GetMutableState(), newContext, newMutableState, nil, nil, nil, ) } func (r *transactionManagerForExistingWorkflowImpl) executeTransaction( ctx ctx.Context, now time.Time, transactionPolicy transactionPolicy, currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) (retError error) { defer func() { if rec := recover(); rec != nil { r.cleanupTransaction(currentWorkflow, targetWorkflow, newWorkflow, errPanic) panic(rec) } else { r.cleanupTransaction(currentWorkflow, targetWorkflow, newWorkflow, retError) } }() switch transactionPolicy { case transactionPolicyUpdateAsCurrent: return r.updateAsCurrent( ctx, now, targetWorkflow, newWorkflow, ) case transactionPolicyUpdateAsZombie: return r.updateAsZombie( ctx, now, currentWorkflow, targetWorkflow, newWorkflow, ) case transactionPolicySuppressCurrentAndUpdateAsCurrent: return r.suppressCurrentAndUpdateAsCurrent( ctx, now, currentWorkflow, targetWorkflow, newWorkflow, ) case transactionPolicyConflictResolveAsCurrent: return r.conflictResolveAsCurrent( ctx, now, targetWorkflow, newWorkflow, ) case transactionPolicyConflictResolveAsZombie: return r.conflictResolveAsZombie( ctx, now, currentWorkflow, targetWorkflow, newWorkflow, ) default: return &types.InternalServiceError{ Message: fmt.Sprintf("nDCTransactionManager: encounter unknown transaction type: %v", transactionPolicy), } } } func (r *transactionManagerForExistingWorkflowImpl) cleanupTransaction( currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, err error, ) { if currentWorkflow != nil { currentWorkflow.GetReleaseFn()(err) } if targetWorkflow != nil { targetWorkflow.GetReleaseFn()(err) } if newWorkflow != nil { newWorkflow.GetReleaseFn()(err) } } ================================================ FILE: service/history/ndc/existing_workflow_transaction_manager_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: existing_workflow_transaction_manager.go // // Generated by this command: // // mockgen -package ndc -source existing_workflow_transaction_manager.go -destination existing_workflow_transaction_manager_mock.go // // Package ndc is a generated GoMock package. package ndc import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" execution "github.com/uber/cadence/service/history/execution" ) // MocktransactionManagerForExistingWorkflow is a mock of transactionManagerForExistingWorkflow interface. type MocktransactionManagerForExistingWorkflow struct { ctrl *gomock.Controller recorder *MocktransactionManagerForExistingWorkflowMockRecorder isgomock struct{} } // MocktransactionManagerForExistingWorkflowMockRecorder is the mock recorder for MocktransactionManagerForExistingWorkflow. type MocktransactionManagerForExistingWorkflowMockRecorder struct { mock *MocktransactionManagerForExistingWorkflow } // NewMocktransactionManagerForExistingWorkflow creates a new mock instance. func NewMocktransactionManagerForExistingWorkflow(ctrl *gomock.Controller) *MocktransactionManagerForExistingWorkflow { mock := &MocktransactionManagerForExistingWorkflow{ctrl: ctrl} mock.recorder = &MocktransactionManagerForExistingWorkflowMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MocktransactionManagerForExistingWorkflow) EXPECT() *MocktransactionManagerForExistingWorkflowMockRecorder { return m.recorder } // dispatchForExistingWorkflow mocks base method. func (m *MocktransactionManagerForExistingWorkflow) dispatchForExistingWorkflow(ctx context.Context, now time.Time, isWorkflowRebuilt bool, targetWorkflow, newWorkflow execution.Workflow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "dispatchForExistingWorkflow", ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) ret0, _ := ret[0].(error) return ret0 } // dispatchForExistingWorkflow indicates an expected call of dispatchForExistingWorkflow. func (mr *MocktransactionManagerForExistingWorkflowMockRecorder) dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "dispatchForExistingWorkflow", reflect.TypeOf((*MocktransactionManagerForExistingWorkflow)(nil).dispatchForExistingWorkflow), ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) } ================================================ FILE: service/history/ndc/existing_workflow_transaction_manager_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( ctx "context" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/execution" ) type ( transactionManagerForExistingWorkflowSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockTransactionMgr *MocktransactionManager updateMgr *transactionManagerForExistingWorkflowImpl } ) func TestTransactionMgrForExistingWorkflowSuite(t *testing.T) { s := new(transactionManagerForExistingWorkflowSuite) suite.Run(t, s) } func (s *transactionManagerForExistingWorkflowSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockTransactionMgr = NewMocktransactionManager(s.controller) s.updateMgr = newTransactionManagerForExistingWorkflow( s.mockTransactionMgr, testlogger.New(s.T()), ).(*transactionManagerForExistingWorkflowImpl) } func (s *transactionManagerForExistingWorkflowSuite) TearDownTest() { s.controller.Finish() } func (s *transactionManagerForExistingWorkflowSuite) TestDispatchForExistingWorkflow_NoRebuild_CurrentWorkflowGuaranteed() { ctx := ctx.Background() now := time.Now() isWorkflowRebuilt := false targetReleaseCalled := false newReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() newWorkflow := execution.NewMockWorkflow(s.controller) newContext := execution.NewMockContext(s.controller) newMutableState := execution.NewMockMutableState(s.controller) var newReleaseFn execution.ReleaseFunc = func(error) { newReleaseCalled = true } newWorkflow.EXPECT().GetContext().Return(newContext).AnyTimes() newWorkflow.EXPECT().GetMutableState().Return(newMutableState).AnyTimes() newWorkflow.EXPECT().GetReleaseFn().Return(newReleaseFn).AnyTimes() targetMutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(true).AnyTimes() targetContext.EXPECT().UpdateWorkflowExecutionWithNewAsPassive( gomock.Any(), now, newContext, newMutableState, ).Return(nil).Times(1) err := s.updateMgr.dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(newReleaseCalled) } func (s *transactionManagerForExistingWorkflowSuite) TestDispatchForExistingWorkflow_NoRebuild_CurrentWorkflowNotGuaranteed_IsCurrent() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" isWorkflowRebuilt := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() newWorkflow := execution.NewMockWorkflow(s.controller) targetMutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(false).AnyTimes() targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() s.mockTransactionMgr.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(targetRunID, nil).Times(1) err := s.updateMgr.dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.Error(err) } func (s *transactionManagerForExistingWorkflowSuite) TestDispatchForExistingWorkflow_NoRebuild_CurrentWorkflowNotGuaranteed_NotCurrent_CurrentRunning_UpdateAsCurrent() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" currentRunID := "other random runID" isWorkflowRebuilt := false targetReleaseCalled := false newReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() newWorkflow := execution.NewMockWorkflow(s.controller) newContext := execution.NewMockContext(s.controller) newMutableState := execution.NewMockMutableState(s.controller) var newReleaseFn execution.ReleaseFunc = func(error) { newReleaseCalled = true } newWorkflow.EXPECT().GetContext().Return(newContext).AnyTimes() newWorkflow.EXPECT().GetMutableState().Return(newMutableState).AnyTimes() newWorkflow.EXPECT().GetReleaseFn().Return(newReleaseFn).AnyTimes() newWorkflow.EXPECT().Revive().Return(nil).Times(1) currentWorkflow := execution.NewMockWorkflow(s.controller) currentContext := execution.NewMockContext(s.controller) currentMutableState := execution.NewMockMutableState(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetContext().Return(currentContext).AnyTimes() currentWorkflow.EXPECT().GetMutableState().Return(currentMutableState).AnyTimes() currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetMutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(false).AnyTimes() targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() s.mockTransactionMgr.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionMgr.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(true, nil) currentWorkflowPolicy := execution.TransactionPolicyPassive currentMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() currentWorkflow.EXPECT().SuppressBy(targetWorkflow).Return(currentWorkflowPolicy, nil).Times(1) targetWorkflow.EXPECT().Revive().Return(nil).Times(1) targetContext.EXPECT().ConflictResolveWorkflowExecution( gomock.Any(), now, persistence.ConflictResolveWorkflowModeUpdateCurrent, targetMutableState, newContext, newMutableState, currentContext, currentMutableState, currentWorkflowPolicy.Ptr(), ).Return(nil).Times(1) err := s.updateMgr.dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(newReleaseCalled) s.True(currentReleaseCalled) } func (s *transactionManagerForExistingWorkflowSuite) TestDispatchForExistingWorkflow_NoRebuild_CurrentWorkflowNotGuaranteed_NotCurrent_CurrentComplete_UpdateAsCurrent() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" currentRunID := "other random runID" isWorkflowRebuilt := false targetReleaseCalled := false newReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() newWorkflow := execution.NewMockWorkflow(s.controller) newContext := execution.NewMockContext(s.controller) newMutableState := execution.NewMockMutableState(s.controller) var newReleaseFn execution.ReleaseFunc = func(error) { newReleaseCalled = true } newWorkflow.EXPECT().GetContext().Return(newContext).AnyTimes() newWorkflow.EXPECT().GetMutableState().Return(newMutableState).AnyTimes() newWorkflow.EXPECT().GetReleaseFn().Return(newReleaseFn).AnyTimes() newWorkflow.EXPECT().Revive().Return(nil).Times(1) currentWorkflow := execution.NewMockWorkflow(s.controller) currentContext := execution.NewMockContext(s.controller) currentMutableState := execution.NewMockMutableState(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetContext().Return(currentContext).AnyTimes() currentWorkflow.EXPECT().GetMutableState().Return(currentMutableState).AnyTimes() currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetMutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(false).AnyTimes() targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() s.mockTransactionMgr.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionMgr.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(true, nil) currentWorkflowPolicy := execution.TransactionPolicyPassive currentMutableState.EXPECT().IsWorkflowExecutionRunning().Return(false).AnyTimes() currentWorkflow.EXPECT().SuppressBy(targetWorkflow).Return(currentWorkflowPolicy, nil).Times(0) targetWorkflow.EXPECT().Revive().Return(nil).Times(1) targetContext.EXPECT().ConflictResolveWorkflowExecution( gomock.Any(), now, persistence.ConflictResolveWorkflowModeUpdateCurrent, targetMutableState, newContext, newMutableState, currentContext, currentMutableState, currentWorkflowPolicy.Ptr(), ).Return(nil).Times(1) err := s.updateMgr.dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(newReleaseCalled) s.True(currentReleaseCalled) } func (s *transactionManagerForExistingWorkflowSuite) TestDispatchForExistingWorkflow_NoRebuild_CurrentWorkflowNotGuaranteed_NotCurrent_UpdateAsZombie_NewRunDoesNotExists() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" newRunID := "some random new run ID" currentRunID := "other random runID" isWorkflowRebuilt := false targetReleaseCalled := false newReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() newWorkflow := execution.NewMockWorkflow(s.controller) newContext := execution.NewMockContext(s.controller) newMutableState := execution.NewMockMutableState(s.controller) var newReleaseFn execution.ReleaseFunc = func(error) { newReleaseCalled = true } newWorkflow.EXPECT().GetContext().Return(newContext).AnyTimes() newWorkflow.EXPECT().GetMutableState().Return(newMutableState).AnyTimes() newWorkflow.EXPECT().GetReleaseFn().Return(newReleaseFn).AnyTimes() currentWorkflow := execution.NewMockWorkflow(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetMutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(false).AnyTimes() targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() newMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: newRunID, }).AnyTimes() s.mockTransactionMgr.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionMgr.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) s.mockTransactionMgr.EXPECT().checkWorkflowExists(ctx, domainID, workflowID, newRunID).Return(false, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(false, nil) targetWorkflow.EXPECT().SuppressBy(currentWorkflow).Return(execution.TransactionPolicyPassive, nil).Times(1) newWorkflow.EXPECT().SuppressBy(currentWorkflow).Return(execution.TransactionPolicyPassive, nil).Times(1) targetContext.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeBypassCurrent, newContext, newMutableState, execution.TransactionPolicyPassive, execution.TransactionPolicyPassive.Ptr(), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.updateMgr.dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(newReleaseCalled) s.True(currentReleaseCalled) } func (s *transactionManagerForExistingWorkflowSuite) TestDispatchForExistingWorkflow_NoRebuild_CurrentWorkflowNotGuaranteed_NotCurrent_UpdateAsZombie_NewRunDoesExists() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" newRunID := "some random new run ID" currentRunID := "other random runID" isWorkflowRebuilt := false targetReleaseCalled := false newReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() newWorkflow := execution.NewMockWorkflow(s.controller) newContext := execution.NewMockContext(s.controller) newMutableState := execution.NewMockMutableState(s.controller) var newReleaseFn execution.ReleaseFunc = func(error) { newReleaseCalled = true } newWorkflow.EXPECT().GetContext().Return(newContext).AnyTimes() newWorkflow.EXPECT().GetMutableState().Return(newMutableState).AnyTimes() newWorkflow.EXPECT().GetReleaseFn().Return(newReleaseFn).AnyTimes() currentWorkflow := execution.NewMockWorkflow(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetMutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(false).AnyTimes() targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() newMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: newRunID, }).AnyTimes() s.mockTransactionMgr.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionMgr.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) s.mockTransactionMgr.EXPECT().checkWorkflowExists(ctx, domainID, workflowID, newRunID).Return(true, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(false, nil) targetWorkflow.EXPECT().SuppressBy(currentWorkflow).Return(execution.TransactionPolicyPassive, nil).Times(1) newWorkflow.EXPECT().SuppressBy(currentWorkflow).Return(execution.TransactionPolicyPassive, nil).Times(1) targetContext.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeBypassCurrent, (execution.Context)(nil), (execution.MutableState)(nil), execution.TransactionPolicyPassive, (*execution.TransactionPolicy)(nil), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.updateMgr.dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(newReleaseCalled) s.True(currentReleaseCalled) } func (s *transactionManagerForExistingWorkflowSuite) TestDispatchForExistingWorkflow_Rebuild_IsCurrent() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" isWorkflowRebuilt := true targetReleaseCalled := false newReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() newWorkflow := execution.NewMockWorkflow(s.controller) newContext := execution.NewMockContext(s.controller) newMutableState := execution.NewMockMutableState(s.controller) var newReleaseFn execution.ReleaseFunc = func(error) { newReleaseCalled = true } newWorkflow.EXPECT().GetContext().Return(newContext).AnyTimes() newWorkflow.EXPECT().GetMutableState().Return(newMutableState).AnyTimes() newWorkflow.EXPECT().GetReleaseFn().Return(newReleaseFn).AnyTimes() targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() s.mockTransactionMgr.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(targetRunID, nil).Times(1) targetContext.EXPECT().ConflictResolveWorkflowExecution( gomock.Any(), now, persistence.ConflictResolveWorkflowModeUpdateCurrent, targetMutableState, newContext, newMutableState, (execution.Context)(nil), (execution.MutableState)(nil), (*execution.TransactionPolicy)(nil), ).Return(nil).Times(1) err := s.updateMgr.dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(newReleaseCalled) } func (s *transactionManagerForExistingWorkflowSuite) TestDispatchForExistingWorkflow_Rebuild_NotCurrent_UpdateAsCurrent() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" currentRunID := "other random runID" isWorkflowRebuilt := true targetReleaseCalled := false newReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() newWorkflow := execution.NewMockWorkflow(s.controller) newContext := execution.NewMockContext(s.controller) newMutableState := execution.NewMockMutableState(s.controller) var newReleaseFn execution.ReleaseFunc = func(error) { newReleaseCalled = true } newWorkflow.EXPECT().GetContext().Return(newContext).AnyTimes() newWorkflow.EXPECT().GetMutableState().Return(newMutableState).AnyTimes() newWorkflow.EXPECT().GetReleaseFn().Return(newReleaseFn).AnyTimes() newWorkflow.EXPECT().Revive().Return(nil).Times(1) currentWorkflow := execution.NewMockWorkflow(s.controller) currentContext := execution.NewMockContext(s.controller) currentMutableState := execution.NewMockMutableState(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetContext().Return(currentContext).AnyTimes() currentWorkflow.EXPECT().GetMutableState().Return(currentMutableState).AnyTimes() currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() s.mockTransactionMgr.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionMgr.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(true, nil) currentWorkflowPolicy := execution.TransactionPolicyActive currentMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() currentWorkflow.EXPECT().SuppressBy(targetWorkflow).Return(currentWorkflowPolicy, nil).Times(1) targetWorkflow.EXPECT().Revive().Return(nil).Times(1) targetContext.EXPECT().ConflictResolveWorkflowExecution( gomock.Any(), now, persistence.ConflictResolveWorkflowModeUpdateCurrent, targetMutableState, newContext, newMutableState, currentContext, currentMutableState, currentWorkflowPolicy.Ptr(), ).Return(nil).Times(1) err := s.updateMgr.dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(newReleaseCalled) s.True(currentReleaseCalled) } func (s *transactionManagerForExistingWorkflowSuite) TestDispatchForExistingWorkflow_Rebuild_NotCurrent_UpdateAsZombie_NewRunDoesNotExists() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" newRunID := "some random new run ID" currentRunID := "other random runID" isWorkflowRebuilt := true targetReleaseCalled := false newReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() newWorkflow := execution.NewMockWorkflow(s.controller) newContext := execution.NewMockContext(s.controller) newMutableState := execution.NewMockMutableState(s.controller) var newReleaseFn execution.ReleaseFunc = func(error) { newReleaseCalled = true } newWorkflow.EXPECT().GetContext().Return(newContext).AnyTimes() newWorkflow.EXPECT().GetMutableState().Return(newMutableState).AnyTimes() newWorkflow.EXPECT().GetReleaseFn().Return(newReleaseFn).AnyTimes() currentWorkflow := execution.NewMockWorkflow(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() newMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: newRunID, }).AnyTimes() s.mockTransactionMgr.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionMgr.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) s.mockTransactionMgr.EXPECT().checkWorkflowExists(ctx, domainID, workflowID, newRunID).Return(false, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(false, nil) targetWorkflow.EXPECT().SuppressBy(currentWorkflow).Return(execution.TransactionPolicyPassive, nil).Times(1) newWorkflow.EXPECT().SuppressBy(currentWorkflow).Return(execution.TransactionPolicyPassive, nil).Times(1) targetContext.EXPECT().ConflictResolveWorkflowExecution( gomock.Any(), now, persistence.ConflictResolveWorkflowModeBypassCurrent, targetMutableState, newContext, newMutableState, (execution.Context)(nil), (execution.MutableState)(nil), (*execution.TransactionPolicy)(nil), ).Return(nil).Times(1) err := s.updateMgr.dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(newReleaseCalled) s.True(currentReleaseCalled) } func (s *transactionManagerForExistingWorkflowSuite) TestDispatchForExistingWorkflow_Rebuild_NotCurrent_UpdateAsZombie_NewRunDoesExists() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" newRunID := "some random new run ID" currentRunID := "other random runID" isWorkflowRebuilt := true targetReleaseCalled := false newReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() newWorkflow := execution.NewMockWorkflow(s.controller) newContext := execution.NewMockContext(s.controller) newMutableState := execution.NewMockMutableState(s.controller) var newReleaseFn execution.ReleaseFunc = func(error) { newReleaseCalled = true } newWorkflow.EXPECT().GetContext().Return(newContext).AnyTimes() newWorkflow.EXPECT().GetMutableState().Return(newMutableState).AnyTimes() newWorkflow.EXPECT().GetReleaseFn().Return(newReleaseFn).AnyTimes() currentWorkflow := execution.NewMockWorkflow(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() newMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: newRunID, }).AnyTimes() s.mockTransactionMgr.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionMgr.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) s.mockTransactionMgr.EXPECT().checkWorkflowExists(ctx, domainID, workflowID, newRunID).Return(true, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(false, nil) targetWorkflow.EXPECT().SuppressBy(currentWorkflow).Return(execution.TransactionPolicyPassive, nil).Times(1) newWorkflow.EXPECT().SuppressBy(currentWorkflow).Return(execution.TransactionPolicyPassive, nil).Times(1) targetContext.EXPECT().ConflictResolveWorkflowExecution( gomock.Any(), now, persistence.ConflictResolveWorkflowModeBypassCurrent, targetMutableState, (execution.Context)(nil), (execution.MutableState)(nil), (execution.Context)(nil), (execution.MutableState)(nil), (*execution.TransactionPolicy)(nil), ).Return(nil).Times(1) err := s.updateMgr.dispatchForExistingWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(newReleaseCalled) s.True(currentReleaseCalled) } ================================================ FILE: service/history/ndc/history_replicator.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( ctx "context" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) const ( mutableStateMissingMessage = "Resend events due to missing mutable state" ) type ( // HistoryReplicator handles history replication task HistoryReplicator interface { ApplyEvents( ctx ctx.Context, request *types.ReplicateEventsV2Request, ) error } historyReplicatorImpl struct { shard shard.Context clusterMetadata cluster.Metadata historyV2Manager persistence.HistoryManager historySerializer persistence.PayloadSerializer metricsClient metrics.Client domainCache cache.DomainCache executionCache execution.Cache eventsReapplier EventsReapplier transactionManager transactionManager logger log.Logger newBranchManagerFn newBranchManagerFn newConflictResolverFn newConflictResolverFn newWorkflowResetterFn newWorkflowResetterFn newStateBuilderFn newStateBuilderFn newMutableStateFn newMutableStateFn // refactored functions for a better testability newReplicationTaskFn newReplicationTaskFn applyStartEventsFn applyStartEventsFn applyNonStartEventsPrepareBranchFn applyNonStartEventsPrepareBranchFn applyNonStartEventsPrepareMutableStateFn applyNonStartEventsPrepareMutableStateFn applyNonStartEventsToCurrentBranchFn applyNonStartEventsToCurrentBranchFn applyNonStartEventsToNoneCurrentBranchFn applyNonStartEventsToNoneCurrentBranchFn applyNonStartEventsToNoneCurrentBranchWithContinueAsNewFn applyNonStartEventsToNoneCurrentBranchWithContinueAsNewFn applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNewFn applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNewFn applyNonStartEventsMissingMutableStateFn applyNonStartEventsMissingMutableStateFn applyNonStartEventsResetWorkflowFn applyNonStartEventsResetWorkflowFn } newStateBuilderFn func( mutableState execution.MutableState, logger log.Logger) execution.StateBuilder newMutableStateFn func( domainEntry *cache.DomainCacheEntry, logger log.Logger, ) execution.MutableState newBranchManagerFn func( context execution.Context, mutableState execution.MutableState, logger log.Logger, ) branchManager newConflictResolverFn func( context execution.Context, mutableState execution.MutableState, logger log.Logger, ) conflictResolver newWorkflowResetterFn func( domainID string, workflowID string, baseRunID string, newContext execution.Context, newRunID string, logger log.Logger, ) WorkflowResetter newReplicationTaskFn func( clusterMetadata cluster.Metadata, historySerializer persistence.PayloadSerializer, taskStartTime time.Time, logger log.Logger, request *types.ReplicateEventsV2Request, ) (replicationTask, error) applyStartEventsFn func( ctx ctx.Context, context execution.Context, releaseFn execution.ReleaseFunc, task replicationTask, domainCache cache.DomainCache, newMutableState newMutableStateFn, newStateBuilder newStateBuilderFn, transactionManager transactionManager, logger log.Logger, shard shard.Context, clusterMetadata cluster.Metadata, ) (retError error) applyNonStartEventsPrepareBranchFn func( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newBranchManager newBranchManagerFn, ) (bool, int, error) applyNonStartEventsPrepareMutableStateFn func( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) applyNonStartEventsToCurrentBranchFn func( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, isRebuilt bool, releaseFn execution.ReleaseFunc, task replicationTask, newStateBuilder newStateBuilderFn, clusterMetadata cluster.Metadata, shard shard.Context, logger log.Logger, transactionManager transactionManager, ) error applyNonStartEventsToNoneCurrentBranchFn func( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, releaseFn execution.ReleaseFunc, task replicationTask, r *historyReplicatorImpl, logger log.Logger, ) error applyNonStartEventsToNoneCurrentBranchWithContinueAsNewFn func( ctx ctx.Context, context execution.Context, releaseFn execution.ReleaseFunc, task replicationTask, r *historyReplicatorImpl, ) error applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNewFn func( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, releaseFn execution.ReleaseFunc, task replicationTask, transactionManager transactionManager, clusterMetadata cluster.Metadata, shard shard.Context, logger log.Logger, ) error applyNonStartEventsMissingMutableStateFn func( ctx ctx.Context, newContext execution.Context, task replicationTask, newWorkflowResetter newWorkflowResetterFn, ) (execution.MutableState, error) applyNonStartEventsResetWorkflowFn func( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newStateBuilder newStateBuilderFn, transactionManager transactionManager, clusterMetadata cluster.Metadata, logger log.Logger, shard shard.Context, ) error ) var _ HistoryReplicator = (*historyReplicatorImpl)(nil) var errPanic = errors.NewInternalFailureError("encounter panic") // NewHistoryReplicator creates history replicator func NewHistoryReplicator( shard shard.Context, executionCache execution.Cache, eventsReapplier EventsReapplier, logger log.Logger, ) HistoryReplicator { transactionManager := newTransactionManager(shard, executionCache, eventsReapplier, logger) replicator := &historyReplicatorImpl{ shard: shard, clusterMetadata: shard.GetService().GetClusterMetadata(), historyV2Manager: shard.GetHistoryManager(), historySerializer: persistence.NewPayloadSerializer(), metricsClient: shard.GetMetricsClient(), domainCache: shard.GetDomainCache(), executionCache: executionCache, transactionManager: transactionManager, eventsReapplier: eventsReapplier, logger: logger.WithTags(tag.ComponentHistoryReplicator), newBranchManagerFn: func( context execution.Context, mutableState execution.MutableState, logger log.Logger, ) branchManager { return newBranchManager(shard, context, mutableState, logger) }, newConflictResolverFn: func( context execution.Context, mutableState execution.MutableState, logger log.Logger, ) conflictResolver { return newConflictResolver(shard, context, mutableState, logger) }, newWorkflowResetterFn: func( domainID string, workflowID string, baseRunID string, newContext execution.Context, newRunID string, logger log.Logger, ) WorkflowResetter { return NewWorkflowResetter(shard, transactionManager, domainID, workflowID, baseRunID, newContext, newRunID, logger) }, newStateBuilderFn: func( state execution.MutableState, logger log.Logger, ) execution.StateBuilder { return execution.NewStateBuilder( shard, logger, state, ) }, newMutableStateFn: func( domainEntry *cache.DomainCacheEntry, logger log.Logger, ) execution.MutableState { return execution.NewMutableStateBuilderWithVersionHistories( shard, logger, domainEntry, constants.EmptyVersion, ) }, newReplicationTaskFn: newReplicationTask, applyStartEventsFn: applyStartEvents, applyNonStartEventsPrepareBranchFn: applyNonStartEventsPrepareBranch, applyNonStartEventsPrepareMutableStateFn: applyNonStartEventsPrepareMutableState, applyNonStartEventsToCurrentBranchFn: applyNonStartEventsToCurrentBranch, applyNonStartEventsToNoneCurrentBranchFn: applyNonStartEventsToNoneCurrentBranch, applyNonStartEventsToNoneCurrentBranchWithContinueAsNewFn: applyNonStartEventsToNoneCurrentBranchWithContinueAsNew, applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNewFn: applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNew, applyNonStartEventsMissingMutableStateFn: applyNonStartEventsMissingMutableState, applyNonStartEventsResetWorkflowFn: applyNonStartEventsResetWorkflow, } return replicator } func (r *historyReplicatorImpl) ApplyEvents( ctx ctx.Context, request *types.ReplicateEventsV2Request, ) (retError error) { startTime := time.Now() task, err := r.newReplicationTaskFn( r.clusterMetadata, r.historySerializer, startTime, r.logger, request, ) if err != nil { return err } return r.applyEvents(ctx, task) } func (r *historyReplicatorImpl) applyEvents( ctx ctx.Context, task replicationTask, ) (retError error) { context, releaseFn, err := r.executionCache.GetOrCreateWorkflowExecution( ctx, task.getDomainID(), *task.getExecution(), ) if err != nil { // for get workflow execution context, with valid run id // err will not be of type EntityNotExistsError return err } defer func() { if rec := recover(); rec != nil { releaseFn(errPanic) panic(rec) } else { releaseFn(retError) } }() switch task.getFirstEvent().GetEventType() { case types.EventTypeWorkflowExecutionStarted: return r.applyStartEventsFn(ctx, context, releaseFn, task, r.domainCache, r.newMutableStateFn, r.newStateBuilderFn, r.transactionManager, r.logger, r.shard, r.clusterMetadata) default: // apply events, other than simple start workflow execution // the continue as new + start workflow execution combination will also be processed here mutableState, err := context.LoadWorkflowExecutionWithTaskVersion(ctx, task.getVersion()) switch err.(type) { case nil: // Sanity check to make only 3DC mutable state here if mutableState.GetVersionHistories() == nil { return execution.ErrMissingVersionHistories } doContinue, branchIndex, err := r.applyNonStartEventsPrepareBranchFn(ctx, context, mutableState, task, r.newBranchManagerFn) if err != nil { return err } else if !doContinue { r.metricsClient.IncCounter(metrics.ReplicateHistoryEventsScope, metrics.DuplicateReplicationEventsCounter) return nil } mutableState, isRebuilt, err := r.applyNonStartEventsPrepareMutableStateFn(ctx, context, mutableState, branchIndex, task, r.newConflictResolverFn) if err != nil { return err } if mutableState.GetVersionHistories().GetCurrentVersionHistoryIndex() == branchIndex { return r.applyNonStartEventsToCurrentBranchFn(ctx, context, mutableState, isRebuilt, releaseFn, task, r.newStateBuilderFn, r.clusterMetadata, r.shard, r.logger, r.transactionManager) } // passed in r because there's a recursive call within applyNonStartEventsToNoneCurrentBranchWithContinueAsNew return r.applyNonStartEventsToNoneCurrentBranchFn(ctx, context, mutableState, branchIndex, releaseFn, task, r, r.logger) case *types.EntityNotExistsError: // mutable state not created, check if is workflow reset mutableState, err := r.applyNonStartEventsMissingMutableStateFn(ctx, context, task, r.newWorkflowResetterFn) if err != nil { return err } return r.applyNonStartEventsResetWorkflowFn(ctx, context, mutableState, task, r.newStateBuilderFn, r.transactionManager, r.clusterMetadata, r.logger, r.shard) default: // unable to get mutable state, return err so we can retry the task later return err } } } func applyStartEvents( ctx ctx.Context, context execution.Context, releaseFn execution.ReleaseFunc, task replicationTask, domainCache cache.DomainCache, newMutableState newMutableStateFn, newStateBuilder newStateBuilderFn, transactionManager transactionManager, logger log.Logger, shard shard.Context, clusterMetadata cluster.Metadata, ) (retError error) { domainEntry, err := domainCache.GetDomainByID(task.getDomainID()) if err != nil { return err } requestID := uuid.New() // requestID used for start workflow execution request. This is not on the history event. // since it's replicated from the other cluster, we don't care the active cluster policy, because the failover version will be updated after ApplyEvents mutableState := newMutableState(domainEntry, task.getLogger()) stateBuilder := newStateBuilder(mutableState, task.getLogger()) // use state builder for workflow mutable state mutation _, err = stateBuilder.ApplyEvents( task.getDomainID(), requestID, *task.getExecution(), task.getEvents(), task.getNewEvents(), ) if err != nil { task.getLogger().Error( "nDCHistoryReplicator unable to apply events when applyStartEvents", tag.Error(err), ) return err } err = transactionManager.createWorkflow( ctx, task.getEventTime(), execution.NewWorkflow( ctx, clusterMetadata, context, mutableState, releaseFn, logger, ), ) if err != nil { task.getLogger().Error( "nDCHistoryReplicator unable to create workflow when applyStartEvents", tag.Error(err), ) } else { notify(task.getSourceCluster(), task.getEventTime(), logger, shard, clusterMetadata) } return err } func applyNonStartEventsPrepareBranch( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newBranchManager newBranchManagerFn, ) (bool, int, error) { incomingVersionHistory := task.getVersionHistory() branchManager := newBranchManager(context, mutableState, task.getLogger()) doContinue, versionHistoryIndex, err := branchManager.prepareVersionHistory( ctx, incomingVersionHistory, task.getFirstEvent().ID, task.getFirstEvent().Version, ) switch err.(type) { case nil: return doContinue, versionHistoryIndex, nil case *types.RetryTaskV2Error: // replication message can arrive out of order // do not log return false, 0, err default: task.getLogger().Error( "nDCHistoryReplicator unable to prepare version history when applyNonStartEventsPrepareBranch", tag.Error(err), ) return false, 0, err } } func applyNonStartEventsPrepareMutableState( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { incomingVersion := task.getVersion() conflictResolver := newConflictResolver(context, mutableState, task.getLogger()) mutableState, isRebuilt, err := conflictResolver.prepareMutableState( ctx, branchIndex, incomingVersion, ) if err != nil { task.getLogger().Error( "nDCHistoryReplicator unable to prepare mutable state when applyNonStartEventsPrepareMutableState", tag.Error(err), ) } return mutableState, isRebuilt, err } func applyNonStartEventsToCurrentBranch( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, isRebuilt bool, releaseFn execution.ReleaseFunc, task replicationTask, newStateBuilder newStateBuilderFn, clusterMetadata cluster.Metadata, shard shard.Context, logger log.Logger, transactionManager transactionManager, ) error { requestID := uuid.New() // requestID used for start workflow execution request. This is not on the history event. stateBuilder := newStateBuilder(mutableState, task.getLogger()) newMutableState, err := stateBuilder.ApplyEvents( task.getDomainID(), requestID, *task.getExecution(), task.getEvents(), task.getNewEvents(), ) if err != nil { task.getLogger().Error( "nDCHistoryReplicator unable to apply events when applyNonStartEventsToCurrentBranch", tag.Error(err), ) return err } targetWorkflow := execution.NewWorkflow( ctx, clusterMetadata, context, mutableState, releaseFn, logger, ) var newWorkflow execution.Workflow if newMutableState != nil { newExecutionInfo := newMutableState.GetExecutionInfo() newContext := execution.NewContext( newExecutionInfo.DomainID, types.WorkflowExecution{ WorkflowID: newExecutionInfo.WorkflowID, RunID: newExecutionInfo.RunID, }, shard, shard.GetExecutionManager(), logger, ) newWorkflow = execution.NewWorkflow( ctx, clusterMetadata, newContext, newMutableState, execution.NoopReleaseFn, logger, ) } err = transactionManager.updateWorkflow( ctx, task.getEventTime(), isRebuilt, targetWorkflow, newWorkflow, ) if err != nil { task.getLogger().Error( "nDCHistoryReplicator unable to update workflow when applyNonStartEventsToCurrentBranch", tag.Error(err), ) } else { notify(task.getSourceCluster(), task.getEventTime(), logger, shard, clusterMetadata) } return err } func applyNonStartEventsToNoneCurrentBranch( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, releaseFn execution.ReleaseFunc, task replicationTask, r *historyReplicatorImpl, logger log.Logger, ) error { if len(task.getNewEvents()) != 0 { return r.applyNonStartEventsToNoneCurrentBranchWithContinueAsNewFn( ctx, context, releaseFn, task, r, ) } return r.applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNewFn( ctx, context, mutableState, branchIndex, releaseFn, task, r.transactionManager, r.clusterMetadata, r.shard, logger, ) } func applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNew( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, releaseFn execution.ReleaseFunc, task replicationTask, transactionManager transactionManager, clusterMetadata cluster.Metadata, shard shard.Context, logger log.Logger, ) error { versionHistoryItem := persistence.NewVersionHistoryItem( task.getLastEvent().ID, task.getLastEvent().Version, ) versionHistories := mutableState.GetVersionHistories() if versionHistories == nil { return execution.ErrMissingVersionHistories } versionHistory, err := versionHistories.GetVersionHistory(branchIndex) if err != nil { return err } if err = versionHistory.AddOrUpdateItem(versionHistoryItem); err != nil { return err } err = transactionManager.backfillWorkflow( ctx, task.getEventTime(), execution.NewWorkflow( ctx, clusterMetadata, context, mutableState, releaseFn, logger, ), &persistence.WorkflowEvents{ DomainID: task.getDomainID(), WorkflowID: task.getExecution().GetWorkflowID(), RunID: task.getExecution().GetRunID(), BranchToken: versionHistory.GetBranchToken(), Events: task.getEvents(), }, ) if err != nil { task.getLogger().Error( "nDCHistoryReplicator unable to backfill workflow when applyNonStartEventsToNoneCurrentBranch", tag.Error(err), ) return err } return nil } func applyNonStartEventsToNoneCurrentBranchWithContinueAsNew( ctx ctx.Context, context execution.Context, releaseFn execution.ReleaseFunc, task replicationTask, r *historyReplicatorImpl, ) error { // workflow backfill to non current branch with continue as new // first, release target workflow lock & create the new workflow as zombie // NOTE: need to release target workflow due to target workflow // can potentially be the current workflow causing deadlock // 1. clear all in memory changes & release target workflow lock // 2. apply new workflow first // 3. apply target workflow // step 1 context.Clear() releaseFn(nil) // step 2 startTime := time.Now() task, newTask, err := task.splitTask(startTime) if err != nil { return err } if err := r.applyEvents(ctx, newTask); err != nil { newTask.getLogger().Error( "nDCHistoryReplicator unable to create new workflow when applyNonStartEventsToNoneCurrentBranchWithContinueAsNew", tag.Error(err), ) return err } // step 3 if err := r.applyEvents(ctx, task); err != nil { newTask.getLogger().Error( "nDCHistoryReplicator unable to create target workflow when applyNonStartEventsToNoneCurrentBranchWithContinueAsNew", tag.Error(err), ) return err } return nil } func applyNonStartEventsMissingMutableState( ctx ctx.Context, newContext execution.Context, task replicationTask, newWorkflowResetter newWorkflowResetterFn, ) (execution.MutableState, error) { // for non reset workflow execution replication task, just do re-replication if !task.isWorkflowReset() { firstEvent := task.getFirstEvent() return nil, newNDCRetryTaskErrorWithHint( mutableStateMissingMessage, task.getDomainID(), task.getWorkflowID(), task.getRunID(), nil, nil, common.Int64Ptr(firstEvent.ID), common.Int64Ptr(firstEvent.Version), ) } decisionTaskEvent := task.getFirstEvent() baseEventID := decisionTaskEvent.ID - 1 baseRunID, newRunID, baseEventVersion, _ := task.getWorkflowResetMetadata() workflowResetter := newWorkflowResetter( task.getDomainID(), task.getWorkflowID(), baseRunID, newContext, newRunID, task.getLogger(), ) resetMutableState, err := workflowResetter.ResetWorkflow( ctx, task.getEventTime(), baseEventID, baseEventVersion, task.getFirstEvent().ID, task.getVersion(), ) if err != nil { task.getLogger().Error( "nDCHistoryReplicator unable to reset workflow when applyNonStartEventsMissingMutableState", tag.Error(err), ) return nil, err } return resetMutableState, nil } func applyNonStartEventsResetWorkflow( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newStateBuilder newStateBuilderFn, transactionManager transactionManager, clusterMetadata cluster.Metadata, logger log.Logger, shard shard.Context, ) error { requestID := uuid.New() // requestID used for start workflow execution request. This is not on the history event. stateBuilder := newStateBuilder(mutableState, task.getLogger()) _, err := stateBuilder.ApplyEvents( task.getDomainID(), requestID, *task.getExecution(), task.getEvents(), task.getNewEvents(), ) if err != nil { task.getLogger().Error( "nDCHistoryReplicator unable to apply events when applyNonStartEventsResetWorkflow", tag.Error(err), ) return err } targetWorkflow := execution.NewWorkflow( ctx, clusterMetadata, context, mutableState, execution.NoopReleaseFn, logger, ) err = transactionManager.createWorkflow( ctx, task.getEventTime(), targetWorkflow, ) if err != nil { task.getLogger().Error( "nDCHistoryReplicator unable to create workflow when applyNonStartEventsResetWorkflow", tag.Error(err), ) } else { notify(task.getSourceCluster(), task.getEventTime(), logger, shard, clusterMetadata) } return err } func notify( clusterName string, now time.Time, logger log.Logger, shard shard.Context, clusterMetadata cluster.Metadata, ) { if clusterName == clusterMetadata.GetCurrentClusterName() { // this is a valid use case for testing, but not for production logger.Warn("nDCHistoryReplicator applying events generated by current cluster") return } now = now.Add(-shard.GetConfig().StandbyClusterDelay()) shard.SetCurrentTime(clusterName, now) logger.Debugf("History replicator setting current time to: %v for clusterName %v", now, clusterName) } func newNDCRetryTaskErrorWithHint( message string, domainID string, workflowID string, runID string, startEventID *int64, startEventVersion *int64, endEventID *int64, endEventVersion *int64, ) error { return &types.RetryTaskV2Error{ Message: message, DomainID: domainID, WorkflowID: workflowID, RunID: runID, StartEventID: startEventID, StartEventVersion: startEventVersion, EndEventID: endEventID, EndEventVersion: endEventVersion, } } ================================================ FILE: service/history/ndc/history_replicator_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ndc import ( ctx "context" "fmt" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" commonConfig "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/resource" "github.com/uber/cadence/service/history/shard" ) var ( testShardID = 1234 ) func createTestHistoryReplicator(t *testing.T, domainID string) historyReplicatorImpl { ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockShard.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 0, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, HistoryCacheInitialSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheMaxSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheTTL: dynamicproperties.GetDurationPropertyFn(10), HostName: "test-host", EnableSizeBasedHistoryExecutionCache: dynamicproperties.GetBoolPropertyFn(false), }).Times(1) // before going into NewHistoryReplicator mockExecutionManager := persistence.NewMockExecutionManager(ctrl) mockShard.EXPECT().GetExecutionManager().Return(mockExecutionManager).AnyTimes() mockShard.EXPECT().GetLogger().Return(log.NewNoop()).AnyTimes() mockShard.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).AnyTimes() mockShard.EXPECT().GetLogger().Return(log.NewNoop()).AnyTimes() mockShard.EXPECT().GetShardID().Return(testShardID).AnyTimes() testExecutionCache := execution.NewCache(mockShard) mockEventsReapplier := NewMockEventsReapplier(ctrl) // going into NewHistoryReplicator -> newTransactionManager() clusterMetadata := cluster.Metadata{} mockShard.EXPECT().GetClusterMetadata().Return(clusterMetadata).Times(2) mockActiveClusterManager := activecluster.NewMockManager(ctrl) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager).Times(2) mockHistoryManager := persistence.NewMockHistoryManager(ctrl) mockShard.EXPECT().GetHistoryManager().Return(mockHistoryManager).Times(3) mockHistoryResource := resource.NewMockResource(ctrl) mockShard.EXPECT().GetService().Return(mockHistoryResource).Times(2) mockPayloadSerializer := persistence.NewMockPayloadSerializer(ctrl) mockHistoryResource.EXPECT().GetPayloadSerializer().Return(mockPayloadSerializer).Times(1) // going into NewHistoryReplicator -> newTransactionManager -> reset.NewWorkflowResetter mockDomainCache := cache.NewMockDomainCache(ctrl) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).Times(2) // going back to NewHistoryReplicator mockHistoryResource.EXPECT().GetClusterMetadata().Return(clusterMetadata).Times(1) replicator := NewHistoryReplicator(mockShard, testExecutionCache, mockEventsReapplier, log.NewNoop()) replicatorImpl := replicator.(*historyReplicatorImpl) return *replicatorImpl } func TestNewHistoryReplicator(t *testing.T) { assert.NotNil(t, createTestHistoryReplicator(t, uuid.New())) } func TestNewHistoryReplicator_newBranchManager(t *testing.T) { ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockShard.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 0, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, HistoryCacheInitialSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheMaxSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheTTL: dynamicproperties.GetDurationPropertyFn(10), HostName: "test-host", EnableSizeBasedHistoryExecutionCache: dynamicproperties.GetBoolPropertyFn(false), }).Times(1) // before going into NewHistoryReplicator mockExecutionManager := persistence.NewMockExecutionManager(ctrl) mockShard.EXPECT().GetExecutionManager().Return(mockExecutionManager).Times(1) mockShard.EXPECT().GetLogger().Return(log.NewNoop()).AnyTimes() mockShard.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).AnyTimes() mockShard.EXPECT().GetShardID().Return(testShardID).AnyTimes() testExecutionCache := execution.NewCache(mockShard) mockEventsReapplier := NewMockEventsReapplier(ctrl) // going into NewHistoryReplicator -> newTransactionManager() mockShard.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).Times(2) mockActiveClusterManager := activecluster.NewMockManager(ctrl) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager).Times(2) mockHistoryManager := persistence.NewMockHistoryManager(ctrl) mockShard.EXPECT().GetHistoryManager().Return(mockHistoryManager).Times(4) mockHistoryResource := resource.NewMockResource(ctrl) mockShard.EXPECT().GetService().Return(mockHistoryResource).Times(3) mockPayloadSerializer := persistence.NewMockPayloadSerializer(ctrl) mockHistoryResource.EXPECT().GetPayloadSerializer().Return(mockPayloadSerializer).Times(1) // going into NewHistoryReplicator -> newTransactionManager -> reset.NewWorkflowResetter mockDomainCache := cache.NewMockDomainCache(ctrl) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).Times(2) // going back to NewHistoryReplicator mockHistoryResource.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).Times(2) testReplicator := NewHistoryReplicator(mockShard, testExecutionCache, mockEventsReapplier, log.NewNoop()) testReplicatorImpl := testReplicator.(*historyReplicatorImpl) // test newBranchManagerFn function in history replicator mockExecutionContext := execution.NewMockContext(ctrl) mockExecutionMutableState := execution.NewMockMutableState(ctrl) assert.NotNil(t, testReplicatorImpl.newBranchManagerFn(mockExecutionContext, mockExecutionMutableState, log.NewNoop())) } func TestNewHistoryReplicator_newConflictResolver(t *testing.T) { ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockShard.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 0, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, HistoryCacheInitialSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheMaxSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheTTL: dynamicproperties.GetDurationPropertyFn(10), HostName: "test-host", EnableSizeBasedHistoryExecutionCache: dynamicproperties.GetBoolPropertyFn(false), }).Times(2) // before going into NewHistoryReplicator mockExecutionManager := persistence.NewMockExecutionManager(ctrl) mockShard.EXPECT().GetExecutionManager().Return(mockExecutionManager).Times(1) mockShard.EXPECT().GetLogger().Return(log.NewNoop()).AnyTimes() mockShard.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).AnyTimes() mockShard.EXPECT().GetShardID().Return(testShardID).AnyTimes() testExecutionCache := execution.NewCache(mockShard) mockEventsReapplier := NewMockEventsReapplier(ctrl) // going into NewHistoryReplicator -> newTransactionManager() mockShard.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).Times(3) mockActiveClusterManager := activecluster.NewMockManager(ctrl) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager).Times(2) mockHistoryManager := persistence.NewMockHistoryManager(ctrl) mockShard.EXPECT().GetHistoryManager().Return(mockHistoryManager).Times(4) mockHistoryResource := resource.NewMockResource(ctrl) mockShard.EXPECT().GetService().Return(mockHistoryResource).Times(3) mockPayloadSerializer := persistence.NewMockPayloadSerializer(ctrl) mockHistoryResource.EXPECT().GetPayloadSerializer().Return(mockPayloadSerializer).Times(1) // going into NewHistoryReplicator -> newTransactionManager -> reset.NewWorkflowResetter mockDomainCache := cache.NewMockDomainCache(ctrl) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).Times(4) // going back to NewHistoryReplicator mockHistoryResource.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).Times(2) testReplicator := NewHistoryReplicator(mockShard, testExecutionCache, mockEventsReapplier, log.NewNoop()) testReplicatorImpl := testReplicator.(*historyReplicatorImpl) // test newConflictResolverFn function in history replicator mockEventsCache := events.NewMockCache(ctrl) mockShard.EXPECT().GetEventsCache().Return(mockEventsCache).Times(1) mockExecutionContext := execution.NewMockContext(ctrl) mockExecutionMutableState := execution.NewMockMutableState(ctrl) assert.NotNil(t, testReplicatorImpl.newConflictResolverFn(mockExecutionContext, mockExecutionMutableState, log.NewNoop())) } func TestNewHistoryReplicator_newWorkflowResetter(t *testing.T) { ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockShard.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 0, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, HistoryCacheInitialSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheMaxSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheTTL: dynamicproperties.GetDurationPropertyFn(10), HostName: "test-host", EnableSizeBasedHistoryExecutionCache: dynamicproperties.GetBoolPropertyFn(false), }).Times(2) // before going into NewHistoryReplicator mockExecutionManager := persistence.NewMockExecutionManager(ctrl) mockShard.EXPECT().GetExecutionManager().Return(mockExecutionManager).Times(1) mockShard.EXPECT().GetLogger().Return(log.NewNoop()).AnyTimes() mockShard.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).AnyTimes() mockShard.EXPECT().GetShardID().Return(testShardID).AnyTimes() testExecutionCache := execution.NewCache(mockShard) mockEventsReapplier := NewMockEventsReapplier(ctrl) // going into NewHistoryReplicator -> newTransactionManager() mockShard.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).Times(3) mockActiveClusterManager := activecluster.NewMockManager(ctrl) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager).Times(2) mockHistoryManager := persistence.NewMockHistoryManager(ctrl) mockShard.EXPECT().GetHistoryManager().Return(mockHistoryManager).Times(5) mockHistoryResource := resource.NewMockResource(ctrl) mockShard.EXPECT().GetService().Return(mockHistoryResource).Times(3) mockPayloadSerializer := persistence.NewMockPayloadSerializer(ctrl) mockHistoryResource.EXPECT().GetPayloadSerializer().Return(mockPayloadSerializer).Times(1) // going into NewHistoryReplicator -> newTransactionManager -> reset.NewWorkflowResetter mockDomainCache := cache.NewMockDomainCache(ctrl) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).Times(4) // going back to NewHistoryReplicator mockHistoryResource.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).Times(2) testReplicator := NewHistoryReplicator(mockShard, testExecutionCache, mockEventsReapplier, log.NewNoop()) testReplicatorImpl := testReplicator.(*historyReplicatorImpl) // test newWorkflowResetterFn function in history replicator mockEventsCache := events.NewMockCache(ctrl) mockShard.EXPECT().GetEventsCache().Return(mockEventsCache).Times(1) mockShard.EXPECT().GetShardID().Return(testShardID).AnyTimes() mockExecutionContext := execution.NewMockContext(ctrl) assert.NotNil(t, testReplicatorImpl.newWorkflowResetterFn( "test-domain-id", "test-workflow-id", "test-base-run-id", mockExecutionContext, "test-run-id", log.NewNoop(), )) } func TestNewHistoryReplicator_newStateBuilder(t *testing.T) { ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockShard.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 0, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, HistoryCacheInitialSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheMaxSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheTTL: dynamicproperties.GetDurationPropertyFn(10), HostName: "test-host", EnableSizeBasedHistoryExecutionCache: dynamicproperties.GetBoolPropertyFn(false), }).Times(1) // before going into NewHistoryReplicator mockExecutionManager := persistence.NewMockExecutionManager(ctrl) mockShard.EXPECT().GetExecutionManager().Return(mockExecutionManager).Times(1) mockShard.EXPECT().GetLogger().Return(log.NewNoop()).AnyTimes() mockShard.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).AnyTimes() mockShard.EXPECT().GetShardID().Return(testShardID).AnyTimes() testExecutionCache := execution.NewCache(mockShard) mockEventsReapplier := NewMockEventsReapplier(ctrl) // going into NewHistoryReplicator -> newTransactionManager() mockShard.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).Times(2) mockActiveClusterManager := activecluster.NewMockManager(ctrl) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager).Times(2) mockHistoryManager := persistence.NewMockHistoryManager(ctrl) mockShard.EXPECT().GetHistoryManager().Return(mockHistoryManager).Times(3) mockHistoryResource := resource.NewMockResource(ctrl) mockShard.EXPECT().GetService().Return(mockHistoryResource).Times(3) mockPayloadSerializer := persistence.NewMockPayloadSerializer(ctrl) mockHistoryResource.EXPECT().GetPayloadSerializer().Return(mockPayloadSerializer).Times(1) // going into NewHistoryReplicator -> newTransactionManager -> reset.NewWorkflowResetter mockDomainCache := cache.NewMockDomainCache(ctrl) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).Times(3) // going back to NewHistoryReplicator mockHistoryResource.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).Times(2) testReplicator := NewHistoryReplicator(mockShard, testExecutionCache, mockEventsReapplier, log.NewNoop()) testReplicatorImpl := testReplicator.(*historyReplicatorImpl) // test newStateBuilderFn function in history replicator mockExecutionMutableState := execution.NewMockMutableState(ctrl) assert.NotNil(t, testReplicatorImpl.newStateBuilderFn(mockExecutionMutableState, log.NewNoop())) } func TestNewHistoryReplicator_newMutableState(t *testing.T) { ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockShard.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 0, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, HistoryCacheInitialSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheMaxSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheTTL: dynamicproperties.GetDurationPropertyFn(10), HostName: "test-host", EnableSizeBasedHistoryExecutionCache: dynamicproperties.GetBoolPropertyFn(false), }).Times(2) // before going into NewHistoryReplicator mockExecutionManager := persistence.NewMockExecutionManager(ctrl) mockShard.EXPECT().GetExecutionManager().Return(mockExecutionManager).Times(1) mockShard.EXPECT().GetLogger().Return(log.NewNoop()).AnyTimes() mockShard.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).AnyTimes() mockShard.EXPECT().GetShardID().Return(testShardID).AnyTimes() testExecutionCache := execution.NewCache(mockShard) mockEventsReapplier := NewMockEventsReapplier(ctrl) // going into NewHistoryReplicator -> newTransactionManager() mockShard.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).Times(4) mockActiveClusterManager := activecluster.NewMockManager(ctrl) mockShard.EXPECT().GetActiveClusterManager().Return(mockActiveClusterManager).Times(2) mockHistoryManager := persistence.NewMockHistoryManager(ctrl) mockShard.EXPECT().GetHistoryManager().Return(mockHistoryManager).Times(3) mockHistoryResource := resource.NewMockResource(ctrl) mockShard.EXPECT().GetService().Return(mockHistoryResource).Times(2) mockPayloadSerializer := persistence.NewMockPayloadSerializer(ctrl) mockHistoryResource.EXPECT().GetPayloadSerializer().Return(mockPayloadSerializer).Times(1) // going into NewHistoryReplicator -> newTransactionManager -> reset.NewWorkflowResetter mockDomainCache := cache.NewMockDomainCache(ctrl) mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).Times(3) // going back to NewHistoryReplicator mockHistoryResource.EXPECT().GetClusterMetadata().Return(cluster.Metadata{}).Times(1) testReplicator := NewHistoryReplicator(mockShard, testExecutionCache, mockEventsReapplier, log.NewNoop()) testReplicatorImpl := testReplicator.(*historyReplicatorImpl) // test newMutableStateFn function in history replicator deadline := int64(0) mockShard.EXPECT().GetTimeSource().Return(clock.NewMockedTimeSource()).Times(1) mockEventsCache := events.NewMockCache(ctrl) mockShard.EXPECT().GetEventsCache().Return(mockEventsCache).Times(1) mockDomainCacheEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "test-domain"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "test-active-cluster"}, 0, &deadline, 0, 0, 0, ) assert.NotNil(t, testReplicatorImpl.newMutableStateFn(mockDomainCacheEntry, log.NewNoop())) } func TestApplyEvents(t *testing.T) { replicator := createTestHistoryReplicator(t, uuid.New()) replicator.newReplicationTaskFn = func( clusterMetadata cluster.Metadata, historySerializer persistence.PayloadSerializer, taskStartTime time.Time, logger log.Logger, request *types.ReplicateEventsV2Request, ) (replicationTask, error) { return nil, nil } // Intentionally panic result. Will test applyEvents function seperately assert.Panics(t, func() { replicator.ApplyEvents(nil, nil) }) } func Test_applyEvents_EventTypeWorkflowExecutionStarted(t *testing.T) { workflowExecutionStartedType := types.EventTypeWorkflowExecutionStarted tests := map[string]struct { mockExecutionCacheAffordance func(mockExecutionCache *execution.MockCache) mockReplicationTaskAffordance func(mockReplicationTask *MockreplicationTask) expectError error }{ "Case1: success case": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, func(err error) {}, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ EventType: &workflowExecutionStartedType, }).Times(1) }, expectError: nil, }, "Case2: error case": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, func(err error) {}, fmt.Errorf("test error")).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) }, expectError: fmt.Errorf("test error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) // mock objects replicator := createTestHistoryReplicator(t, uuid.New()) mockReplicationTask := NewMockreplicationTask(ctrl) mockExecutionCache := execution.NewMockCache(ctrl) replicator.executionCache = mockExecutionCache // mock functions test.mockReplicationTaskAffordance(mockReplicationTask) test.mockExecutionCacheAffordance(mockExecutionCache) // parameter functions affordance replicator.applyStartEventsFn = func( ctx ctx.Context, context execution.Context, releaseFn execution.ReleaseFunc, task replicationTask, domainCache cache.DomainCache, newMutableState newMutableStateFn, newStateBuilder newStateBuilderFn, transactionManager transactionManager, logger log.Logger, shard shard.Context, clusterMetadata cluster.Metadata, ) error { return nil } assert.Equal(t, replicator.applyEvents(ctx.Background(), mockReplicationTask), test.expectError) }) } } func Test_applyEvents_defaultCase_noErrorBranch(t *testing.T) { workflowExecutionType := types.EventTypeWorkflowExecutionCompleted tests := map[string]struct { mockExecutionCacheAffordance func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) mockReplicationTaskAffordance func(mockReplicationTask *MockreplicationTask) mockExecutionContextAffordance func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) mockMutableStateAffordance func(mockExecutionMutableState *execution.MockMutableState) mockApplyNonStartEventsPrepareBranchFn func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newBranchManager newBranchManagerFn, ) (bool, int, error) applyNonStartEventsPrepareMutableStateFnAffordance func(mockExecutionMutableState *execution.MockMutableState) func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) expectError error }{ "Case1: case nil with GetVersionHistories is nil": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockExecutionContext, func(err error) {}, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ EventType: &workflowExecutionType, }).Times(1) mockReplicationTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockExecutionContextAffordance: func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) { mockExecutionContext.EXPECT().LoadWorkflowExecutionWithTaskVersion(gomock.Any(), gomock.Any()). Return(mockExecutionMutableState, nil).Times(1) }, mockMutableStateAffordance: func(mockExecutionMutableState *execution.MockMutableState) { mockExecutionMutableState.EXPECT().GetVersionHistories().Return(nil).Times(1) }, mockApplyNonStartEventsPrepareBranchFn: func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newBranchManager newBranchManagerFn, ) (bool, int, error) { return false, 0, nil }, applyNonStartEventsPrepareMutableStateFnAffordance: func(mockExecutionMutableState *execution.MockMutableState) func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { fn := func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { return nil, false, fmt.Errorf("test error") } return fn }, expectError: execution.ErrMissingVersionHistories, }, "Case2: case nil with applyNonStartEventsPrepareBranchFn error": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockExecutionContext, func(err error) {}, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ EventType: &workflowExecutionType, }).Times(1) mockReplicationTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockExecutionContextAffordance: func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) { mockExecutionContext.EXPECT().LoadWorkflowExecutionWithTaskVersion(gomock.Any(), gomock.Any()). Return(mockExecutionMutableState, nil).Times(1) }, mockMutableStateAffordance: func(mockExecutionMutableState *execution.MockMutableState) { mockExecutionMutableState.EXPECT().GetVersionHistories().Return(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: nil, }).Times(1) }, mockApplyNonStartEventsPrepareBranchFn: func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newBranchManager newBranchManagerFn, ) (bool, int, error) { return false, 0, fmt.Errorf("test error") }, applyNonStartEventsPrepareMutableStateFnAffordance: func(mockExecutionMutableState *execution.MockMutableState) func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { fn := func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { return nil, false, fmt.Errorf("test error") } return fn }, expectError: fmt.Errorf("test error"), }, "Case3: case nil with applyNonStartEventsPrepareBranchFn no error and doContinue is false": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockExecutionContext, func(err error) {}, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ EventType: &workflowExecutionType, }).Times(1) mockReplicationTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockExecutionContextAffordance: func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) { mockExecutionContext.EXPECT().LoadWorkflowExecutionWithTaskVersion(gomock.Any(), gomock.Any()). Return(mockExecutionMutableState, nil).Times(1) }, mockMutableStateAffordance: func(mockExecutionMutableState *execution.MockMutableState) { mockExecutionMutableState.EXPECT().GetVersionHistories().Return(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: nil, }).Times(1) }, mockApplyNonStartEventsPrepareBranchFn: func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newBranchManager newBranchManagerFn, ) (bool, int, error) { return false, 0, nil }, applyNonStartEventsPrepareMutableStateFnAffordance: func(mockExecutionMutableState *execution.MockMutableState) func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { fn := func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { return nil, false, fmt.Errorf("test error") } return fn }, expectError: nil, }, "Case4: case nil with applyNonStartEventsPrepareMutableStateFn error": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockExecutionContext, func(err error) {}, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ EventType: &workflowExecutionType, }).Times(1) mockReplicationTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockExecutionContextAffordance: func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) { mockExecutionContext.EXPECT().LoadWorkflowExecutionWithTaskVersion(gomock.Any(), gomock.Any()). Return(mockExecutionMutableState, nil).Times(1) }, mockMutableStateAffordance: func(mockExecutionMutableState *execution.MockMutableState) { mockExecutionMutableState.EXPECT().GetVersionHistories().Return(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: nil, }).Times(1) }, mockApplyNonStartEventsPrepareBranchFn: func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newBranchManager newBranchManagerFn, ) (bool, int, error) { return true, 5, nil }, applyNonStartEventsPrepareMutableStateFnAffordance: func(mockExecutionMutableState *execution.MockMutableState) func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { fn := func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { assert.Equal(t, branchIndex, 5) return nil, false, fmt.Errorf("test error") } return fn }, expectError: fmt.Errorf("test error"), }, "Case5: case nil with CurrentVersionHistoryIndex() == branchIndex": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockExecutionContext, func(err error) {}, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ EventType: &workflowExecutionType, }).Times(1) mockReplicationTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockExecutionContextAffordance: func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) { mockExecutionContext.EXPECT().LoadWorkflowExecutionWithTaskVersion(gomock.Any(), gomock.Any()). Return(mockExecutionMutableState, nil).Times(1) }, mockMutableStateAffordance: func(mockExecutionMutableState *execution.MockMutableState) { mockExecutionMutableState.EXPECT().GetVersionHistories().Return(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: nil, }).Times(2) }, mockApplyNonStartEventsPrepareBranchFn: func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newBranchManager newBranchManagerFn, ) (bool, int, error) { return true, 1, nil }, applyNonStartEventsPrepareMutableStateFnAffordance: func(mockExecutionMutableState *execution.MockMutableState) func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { fn := func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { assert.Equal(t, branchIndex, 1) return mockExecutionMutableState, false, nil } return fn }, expectError: nil, }, "Case6: case nil with CurrentVersionHistoryIndex() != branchIndex": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockExecutionContext, func(err error) {}, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ EventType: &workflowExecutionType, }).Times(1) mockReplicationTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockExecutionContextAffordance: func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) { mockExecutionContext.EXPECT().LoadWorkflowExecutionWithTaskVersion(gomock.Any(), gomock.Any()). Return(mockExecutionMutableState, nil).Times(1) }, mockMutableStateAffordance: func(mockExecutionMutableState *execution.MockMutableState) { mockExecutionMutableState.EXPECT().GetVersionHistories().Return(&persistence.VersionHistories{ CurrentVersionHistoryIndex: 1, Histories: nil, }).Times(2) }, mockApplyNonStartEventsPrepareBranchFn: func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newBranchManager newBranchManagerFn, ) (bool, int, error) { return true, 2, nil }, applyNonStartEventsPrepareMutableStateFnAffordance: func(mockExecutionMutableState *execution.MockMutableState) func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { fn := func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, task replicationTask, newConflictResolver newConflictResolverFn, ) (execution.MutableState, bool, error) { assert.Equal(t, branchIndex, 2) return mockExecutionMutableState, false, nil } return fn }, expectError: nil, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) // mock objects replicator := createTestHistoryReplicator(t, uuid.New()) mockReplicationTask := NewMockreplicationTask(ctrl) mockExecutionCache := execution.NewMockCache(ctrl) mockExecutionContext := execution.NewMockContext(ctrl) mockExecutionMutableState := execution.NewMockMutableState(ctrl) mockMetricsClient := metrics.NewNoopMetricsClient() replicator.executionCache = mockExecutionCache replicator.metricsClient = mockMetricsClient // mock functions test.mockReplicationTaskAffordance(mockReplicationTask) test.mockExecutionCacheAffordance(mockExecutionCache, mockExecutionContext) test.mockExecutionContextAffordance(mockExecutionContext, mockExecutionMutableState) test.mockMutableStateAffordance(mockExecutionMutableState) // parameter functions affordance replicator.applyNonStartEventsPrepareBranchFn = test.mockApplyNonStartEventsPrepareBranchFn replicator.applyNonStartEventsPrepareMutableStateFn = test.applyNonStartEventsPrepareMutableStateFnAffordance(mockExecutionMutableState) replicator.applyNonStartEventsToCurrentBranchFn = func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, isRebuilt bool, releaseFn execution.ReleaseFunc, task replicationTask, newStateBuilder newStateBuilderFn, clusterMetadata cluster.Metadata, shard shard.Context, logger log.Logger, transactionManager transactionManager, ) error { return nil } replicator.applyNonStartEventsToNoneCurrentBranchFn = func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, releaseFn execution.ReleaseFunc, task replicationTask, r *historyReplicatorImpl, logger log.Logger, ) error { return nil } assert.Equal(t, replicator.applyEvents(ctx.Background(), mockReplicationTask), test.expectError) }) } } func Test_applyEvents_defaultCase_errorAndDefault(t *testing.T) { workflowExecutionType := types.EventTypeWorkflowExecutionCompleted tests := map[string]struct { mockExecutionCacheAffordance func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) mockReplicationTaskAffordance func(mockReplicationTask *MockreplicationTask) mockExecutionContextAffordance func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) mockMutableStateAffordance func(mockExecutionMutableState *execution.MockMutableState) mockApplyNonStartEventsMissingMutableStateFn func(ctx ctx.Context, newContext execution.Context, task replicationTask, newWorkflowResetter newWorkflowResetterFn, ) (execution.MutableState, error) expectError error }{ "Case1-1: case EntityNotExistsError + applyNonStartEventsMissingMutableStateFn error": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockExecutionContext, func(err error) {}, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ EventType: &workflowExecutionType, }).Times(1) mockReplicationTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockExecutionContextAffordance: func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) { mockExecutionContext.EXPECT().LoadWorkflowExecutionWithTaskVersion(gomock.Any(), gomock.Any()). Return(mockExecutionMutableState, &types.EntityNotExistsError{}).Times(1) }, mockMutableStateAffordance: func(mockExecutionMutableState *execution.MockMutableState) { return }, mockApplyNonStartEventsMissingMutableStateFn: func(ctx ctx.Context, newContext execution.Context, task replicationTask, newWorkflowResetter newWorkflowResetterFn, ) (execution.MutableState, error) { return nil, fmt.Errorf("test error") }, expectError: fmt.Errorf("test error"), }, "Case1-2: case EntityNotExistsError": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockExecutionContext, func(err error) {}, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ EventType: &workflowExecutionType, }).Times(1) mockReplicationTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockExecutionContextAffordance: func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) { mockExecutionContext.EXPECT().LoadWorkflowExecutionWithTaskVersion(gomock.Any(), gomock.Any()). Return(mockExecutionMutableState, &types.EntityNotExistsError{}).Times(1) }, mockMutableStateAffordance: func(mockExecutionMutableState *execution.MockMutableState) { return }, mockApplyNonStartEventsMissingMutableStateFn: func(ctx ctx.Context, newContext execution.Context, task replicationTask, newWorkflowResetter newWorkflowResetterFn, ) (execution.MutableState, error) { return nil, nil }, expectError: nil, }, "Case1-3: case other errors": { mockExecutionCacheAffordance: func(mockExecutionCache *execution.MockCache, mockExecutionContext *execution.MockContext) { mockExecutionCache.EXPECT().GetOrCreateWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()). Return(mockExecutionContext, func(err error) {}, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ EventType: &workflowExecutionType, }).Times(1) mockReplicationTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockExecutionContextAffordance: func(mockExecutionContext *execution.MockContext, mockExecutionMutableState *execution.MockMutableState) { mockExecutionContext.EXPECT().LoadWorkflowExecutionWithTaskVersion(gomock.Any(), gomock.Any()). Return(mockExecutionMutableState, fmt.Errorf("test-error")).Times(1) }, mockMutableStateAffordance: func(mockExecutionMutableState *execution.MockMutableState) { return }, mockApplyNonStartEventsMissingMutableStateFn: func(ctx ctx.Context, newContext execution.Context, task replicationTask, newWorkflowResetter newWorkflowResetterFn, ) (execution.MutableState, error) { return nil, nil }, expectError: fmt.Errorf("test-error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) // mock objects replicator := createTestHistoryReplicator(t, uuid.New()) mockReplicationTask := NewMockreplicationTask(ctrl) mockExecutionCache := execution.NewMockCache(ctrl) mockExecutionContext := execution.NewMockContext(ctrl) mockExecutionMutableState := execution.NewMockMutableState(ctrl) replicator.executionCache = mockExecutionCache // mock functions test.mockReplicationTaskAffordance(mockReplicationTask) test.mockExecutionCacheAffordance(mockExecutionCache, mockExecutionContext) test.mockExecutionContextAffordance(mockExecutionContext, mockExecutionMutableState) test.mockMutableStateAffordance(mockExecutionMutableState) // parameter functions affordance replicator.applyNonStartEventsMissingMutableStateFn = test.mockApplyNonStartEventsMissingMutableStateFn replicator.applyNonStartEventsResetWorkflowFn = func(ctx ctx.Context, context execution.Context, mutableState execution.MutableState, task replicationTask, newStateBuilder newStateBuilderFn, transactionManager transactionManager, clusterMetadata cluster.Metadata, logger log.Logger, shard shard.Context, ) error { return nil } assert.Equal(t, replicator.applyEvents(ctx.Background(), mockReplicationTask), test.expectError) }) } } func Test_applyStartEvents(t *testing.T) { tests := map[string]struct { mockDomainCacheAffordance func(mockDomainCache *cache.MockDomainCache) mockStateBuilderAffordance func(mockStateBuilder *execution.MockStateBuilder) mockReplicationTaskAffordance func(mockReplicationTask *MockreplicationTask) mockTransactionManagerAffordance func(mockTransactionManager *MocktransactionManager) mockShardContextAffordance func(mockShardContext *shard.MockContext) expectError error }{ "Case1: success case with no errors": { mockDomainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainByID(gomock.Any()). Return(cache.NewGlobalDomainCacheEntryForTest(nil, nil, nil, 1), nil).Times(1) }, mockStateBuilderAffordance: func(mockStateBuilder *execution.MockStateBuilder) { mockStateBuilder.EXPECT().ApplyEvents( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(nil, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getLogger().Return(log.NewNoop()).Times(2) mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(2) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getEvents().Return(nil).Times(1) mockReplicationTask.EXPECT().getNewEvents().Return(nil).Times(1) mockReplicationTask.EXPECT().getEventTime().Return(time.Now()).Times(2) mockReplicationTask.EXPECT().getSourceCluster().Return("test-source-cluster").Times(1) }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) { mockTransactionManager.EXPECT().createWorkflow(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) }, mockShardContextAffordance: func(mockShard *shard.MockContext) { mockShard.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 0, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, HistoryCacheInitialSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheMaxSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheTTL: dynamicproperties.GetDurationPropertyFn(10), HostName: "test-host", StandbyClusterDelay: dynamicproperties.GetDurationPropertyFn(10), EnableSizeBasedHistoryExecutionCache: dynamicproperties.GetBoolPropertyFn(false), }).Times(1) mockShard.EXPECT().SetCurrentTime(gomock.Any(), gomock.Any()).Times(1) }, expectError: nil, }, "Case2: error when GetDomainByID fails": { mockDomainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainByID(gomock.Any()). Return(nil, fmt.Errorf("test error")).Times(1) }, mockStateBuilderAffordance: func(mockStateBuilder *execution.MockStateBuilder) {}, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) {}, mockShardContextAffordance: func(mockShard *shard.MockContext) {}, expectError: fmt.Errorf("test error"), }, "Case3: error during createWorkflow": { mockDomainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainByID(gomock.Any()). Return(cache.NewGlobalDomainCacheEntryForTest(nil, nil, nil, 1), nil).Times(1) }, mockStateBuilderAffordance: func(mockStateBuilder *execution.MockStateBuilder) { mockStateBuilder.EXPECT().ApplyEvents( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(nil, nil).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getLogger().Return(log.NewNoop()).Times(3) mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(2) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getEvents().Return(nil).Times(1) mockReplicationTask.EXPECT().getNewEvents().Return(nil).Times(1) mockReplicationTask.EXPECT().getEventTime().Return(time.Now()).Times(1) }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) { mockTransactionManager.EXPECT().createWorkflow(gomock.Any(), gomock.Any(), gomock.Any()). Return(fmt.Errorf("test error")).Times(1) }, mockShardContextAffordance: func(mockShard *shard.MockContext) {}, expectError: fmt.Errorf("test error"), }, "Case4: error when calling stateBuilder.ApplyEvents": { mockDomainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainByID(gomock.Any()). Return(cache.NewGlobalDomainCacheEntryForTest(nil, nil, nil, 1), nil).Times(1) }, mockStateBuilderAffordance: func(mockStateBuilder *execution.MockStateBuilder) { mockStateBuilder.EXPECT().ApplyEvents( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(nil, fmt.Errorf("test error")).Times(1) }, mockReplicationTaskAffordance: func(mockReplicationTask *MockreplicationTask) { mockReplicationTask.EXPECT().getLogger().Return(log.NewNoop()).Times(3) mockReplicationTask.EXPECT().getDomainID().Return("test-domain-id").Times(2) mockReplicationTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockReplicationTask.EXPECT().getEvents().Return(nil).Times(1) mockReplicationTask.EXPECT().getNewEvents().Return(nil).Times(1) }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) {}, mockShardContextAffordance: func(mockShard *shard.MockContext) {}, expectError: fmt.Errorf("test error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Mock objects mockDomainCache := cache.NewMockDomainCache(ctrl) mockExecutionContext := execution.NewMockContext(ctrl) mockMutableState := execution.NewMockMutableState(ctrl) mockStateBuilder := execution.NewMockStateBuilder(ctrl) mockReplicationTask := NewMockreplicationTask(ctrl) mockTransactionManager := NewMocktransactionManager(ctrl) mockShard := shard.NewMockContext(ctrl) logger := log.NewNoop() // Mock affordances test.mockDomainCacheAffordance(mockDomainCache) test.mockStateBuilderAffordance(mockStateBuilder) test.mockReplicationTaskAffordance(mockReplicationTask) test.mockTransactionManagerAffordance(mockTransactionManager) test.mockShardContextAffordance(mockShard) // Mock functions mockNewMutableStateFn := func( domainEntry *cache.DomainCacheEntry, logger log.Logger, ) execution.MutableState { return mockMutableState } mockNewStateBuilderFn := func( state execution.MutableState, logger log.Logger, ) execution.StateBuilder { return mockStateBuilder } // Call the function under test err := applyStartEvents(ctx.Background(), mockExecutionContext, func(err error) {}, mockReplicationTask, mockDomainCache, mockNewMutableStateFn, mockNewStateBuilderFn, mockTransactionManager, logger, mockShard, cluster.Metadata{}) // Assertions // can't change it to ErrorIs since ErrorIs need error chains assert.Equal(t, test.expectError, err) }) } } func Test_applyNonStartEventsPrepareBranch(t *testing.T) { tests := map[string]struct { mockTaskAffordance func(mockTask *MockreplicationTask) mockBranchManagerAffordance func(mockBranchManager *MockbranchManager) mockNewBranchManagerFn func(context execution.Context, mutableState execution.MutableState, logger log.Logger) branchManager expectDoContinue bool expectVersionHistoryIndex int expectError error }{ "Case1: success case with no errors": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getVersionHistory().Return(&persistence.VersionHistory{}).Times(1) mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(1) mockTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ ID: 1, Version: 1, }).Times(2) }, mockBranchManagerAffordance: func(mockBranchManager *MockbranchManager) { mockBranchManager.EXPECT().prepareVersionHistory( gomock.Any(), gomock.Any(), int64(1), int64(1), ).Return(true, 5, nil).Times(1) }, mockNewBranchManagerFn: func(context execution.Context, mutableState execution.MutableState, logger log.Logger) branchManager { mockBranchManager := NewMockbranchManager(gomock.NewController(t)) mockBranchManager.EXPECT().prepareVersionHistory(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(true, 5, nil).Times(1) return mockBranchManager }, expectDoContinue: true, expectVersionHistoryIndex: 5, expectError: nil, }, "Case2: RetryTaskV2Error case": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getVersionHistory().Return(&persistence.VersionHistory{}).Times(1) mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(1) mockTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ ID: 1, Version: 1, }).Times(2) }, mockBranchManagerAffordance: func(mockBranchManager *MockbranchManager) { mockBranchManager.EXPECT().prepareVersionHistory( gomock.Any(), gomock.Any(), int64(1), int64(1), ).Return(false, 0, &types.RetryTaskV2Error{}).Times(1) }, mockNewBranchManagerFn: func(context execution.Context, mutableState execution.MutableState, logger log.Logger) branchManager { mockBranchManager := NewMockbranchManager(gomock.NewController(t)) mockBranchManager.EXPECT().prepareVersionHistory(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(false, 0, &types.RetryTaskV2Error{}).Times(1) return mockBranchManager }, expectDoContinue: false, expectVersionHistoryIndex: 0, expectError: &types.RetryTaskV2Error{}, }, "Case3: unknown error case": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getVersionHistory().Return(&persistence.VersionHistory{}).Times(1) mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(2) mockTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ ID: 1, Version: 1, }).Times(2) }, mockBranchManagerAffordance: func(mockBranchManager *MockbranchManager) { mockBranchManager.EXPECT().prepareVersionHistory( gomock.Any(), gomock.Any(), int64(1), int64(1), ).Return(false, 0, fmt.Errorf("test error")).Times(1) }, mockNewBranchManagerFn: func(context execution.Context, mutableState execution.MutableState, logger log.Logger) branchManager { mockBranchManager := NewMockbranchManager(gomock.NewController(t)) mockBranchManager.EXPECT().prepareVersionHistory(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(false, 0, fmt.Errorf("test error")).Times(1) return mockBranchManager }, expectDoContinue: false, expectVersionHistoryIndex: 0, expectError: fmt.Errorf("test error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Mock objects mockTask := NewMockreplicationTask(ctrl) mockExecutionContext := execution.NewMockContext(ctrl) mockMutableState := execution.NewMockMutableState(ctrl) // Mock affordances test.mockTaskAffordance(mockTask) mockBranchManager := NewMockbranchManager(ctrl) test.mockBranchManagerAffordance(mockBranchManager) // Mock newBranchManagerFn newBranchManagerFn := func(context execution.Context, mutableState execution.MutableState, logger log.Logger) branchManager { return mockBranchManager } // Call the function under test doContinue, versionHistoryIndex, err := applyNonStartEventsPrepareBranch(ctx.Background(), mockExecutionContext, mockMutableState, mockTask, newBranchManagerFn) // Assertions assert.Equal(t, test.expectDoContinue, doContinue) assert.Equal(t, test.expectVersionHistoryIndex, versionHistoryIndex) assert.Equal(t, test.expectError, err) }) } } func Test_applyNonStartEventsPrepareMutableState(t *testing.T) { tests := map[string]struct { mockTaskAffordance func(mockTask *MockreplicationTask) mockConflictResolverAffordance func(mockConflictResolver *MockconflictResolver) mockNewConflictResolverFn func(context execution.Context, mutableState execution.MutableState, logger log.Logger) conflictResolver expectMutableState execution.MutableState expectIsRebuilt bool expectError error }{ "Case1: success case with no errors": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getVersion().Return(int64(1)).Times(1) mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(1) }, mockConflictResolverAffordance: func(mockConflictResolver *MockconflictResolver) { mockMutableState := execution.NewMockMutableState(gomock.NewController(t)) // Define mockMutableState mockConflictResolver.EXPECT().prepareMutableState( gomock.Any(), 5, // branchIndex int64(1), // incomingVersion ).Return(mockMutableState, true, nil).Times(1) }, mockNewConflictResolverFn: func(context execution.Context, mutableState execution.MutableState, logger log.Logger) conflictResolver { mockConflictResolver := NewMockconflictResolver(gomock.NewController(t)) return mockConflictResolver }, expectMutableState: execution.NewMockMutableState(gomock.NewController(t)), // Use same mock for expectations expectIsRebuilt: true, expectError: nil, }, "Case2: error during prepareMutableState": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getVersion().Return(int64(1)).Times(1) mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(2) // Error logging case }, mockConflictResolverAffordance: func(mockConflictResolver *MockconflictResolver) { mockConflictResolver.EXPECT().prepareMutableState( gomock.Any(), 5, // branchIndex int64(1), // incomingVersion ).Return(nil, false, fmt.Errorf("test error")).Times(1) }, mockNewConflictResolverFn: func(context execution.Context, mutableState execution.MutableState, logger log.Logger) conflictResolver { mockConflictResolver := NewMockconflictResolver(gomock.NewController(t)) return mockConflictResolver }, expectMutableState: nil, expectIsRebuilt: false, expectError: fmt.Errorf("test error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Mock objects mockTask := NewMockreplicationTask(ctrl) mockExecutionContext := execution.NewMockContext(ctrl) mockMutableState := execution.NewMockMutableState(ctrl) // Define mockMutableState // Mock affordances test.mockTaskAffordance(mockTask) mockConflictResolver := NewMockconflictResolver(ctrl) test.mockConflictResolverAffordance(mockConflictResolver) // Mock newConflictResolverFn newConflictResolverFn := func(context execution.Context, mutableState execution.MutableState, logger log.Logger) conflictResolver { return mockConflictResolver } // Call the function under test mutableState, isRebuilt, err := applyNonStartEventsPrepareMutableState(ctx.Background(), mockExecutionContext, mockMutableState, 5, mockTask, newConflictResolverFn) // Assertions assert.Equal(t, test.expectMutableState, mutableState) assert.Equal(t, test.expectIsRebuilt, isRebuilt) assert.Equal(t, test.expectError, err) }) } } func Test_applyNonStartEventsToCurrentBranch(t *testing.T) { tests := map[string]struct { mockTaskAffordance func(mockTask *MockreplicationTask) mockStateBuilderAffordance func(mockStateBuilder *execution.MockStateBuilder, mockMutableState *execution.MockMutableState) mockTransactionManagerAffordance func(mockTransactionManager *MocktransactionManager) mockShardAffordance func(mockShard *shard.MockContext) mockNewStateBuilderFn func(mutableState execution.MutableState, logger log.Logger) execution.StateBuilder expectError error }{ "Case1: success case with no errors": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(1) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockTask.EXPECT().getEvents().Return(nil).Times(1) mockTask.EXPECT().getNewEvents().Return(nil).Times(1) mockTask.EXPECT().getEventTime().Return(time.Now()).Times(2) mockTask.EXPECT().getSourceCluster().Return("test-source-cluster").Times(1) }, mockStateBuilderAffordance: func(mockStateBuilder *execution.MockStateBuilder, mockMutableState *execution.MockMutableState) { mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ WorkflowID: "test-workflow-id", RunID: "test-run-id", DomainID: "test-domain-id", }).Times(1) mockStateBuilder.EXPECT().ApplyEvents( "test-domain-id", gomock.Any(), types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, nil, nil, ).Return(mockMutableState, nil).Times(1) }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) { mockTransactionManager.EXPECT().updateWorkflow( gomock.Any(), gomock.Any(), true, gomock.Any(), gomock.Any(), ).Return(nil).Times(1) }, mockShardAffordance: func(mockShard *shard.MockContext) { mockShard.EXPECT().GetExecutionManager().Return(nil).Times(1) mockShard.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).Times(1) mockShard.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 0, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, HistoryCacheInitialSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheMaxSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheTTL: dynamicproperties.GetDurationPropertyFn(10), HostName: "test-host", StandbyClusterDelay: dynamicproperties.GetDurationPropertyFn(10), EnableSizeBasedHistoryExecutionCache: dynamicproperties.GetBoolPropertyFn(false), }).Times(1) mockShard.EXPECT().SetCurrentTime(gomock.Any(), gomock.Any()).Times(1) }, mockNewStateBuilderFn: func(mutableState execution.MutableState, logger log.Logger) execution.StateBuilder { mockStateBuilder := execution.NewMockStateBuilder(gomock.NewController(t)) return mockStateBuilder }, expectError: nil, }, "Case2: error during ApplyEvents": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(2) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockTask.EXPECT().getEvents().Return(nil).Times(1) mockTask.EXPECT().getNewEvents().Return(nil).Times(1) }, mockStateBuilderAffordance: func(mockStateBuilder *execution.MockStateBuilder, mockMutableState *execution.MockMutableState) { mockStateBuilder.EXPECT().ApplyEvents( "test-domain-id", gomock.Any(), types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, nil, nil, ).Return(nil, fmt.Errorf("test error")).Times(1) }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) {}, mockShardAffordance: func(mockShard *shard.MockContext) {}, mockNewStateBuilderFn: func(mutableState execution.MutableState, logger log.Logger) execution.StateBuilder { mockStateBuilder := execution.NewMockStateBuilder(gomock.NewController(t)) return mockStateBuilder }, expectError: fmt.Errorf("test error"), }, "Case3: error during updateWorkflow": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(2) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockTask.EXPECT().getEvents().Return(nil).Times(1) mockTask.EXPECT().getNewEvents().Return(nil).Times(1) mockTask.EXPECT().getEventTime().Return(time.Now()).Times(1) }, mockStateBuilderAffordance: func(mockStateBuilder *execution.MockStateBuilder, mockMutableState *execution.MockMutableState) { mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ WorkflowID: "test-workflow-id", RunID: "test-run-id", DomainID: "test-domain-id", }).Times(1) mockStateBuilder.EXPECT().ApplyEvents( "test-domain-id", gomock.Any(), types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, nil, nil, ).Return(mockMutableState, nil).Times(1) }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) { mockTransactionManager.EXPECT().updateWorkflow( gomock.Any(), gomock.Any(), true, gomock.Any(), gomock.Any(), ).Return(fmt.Errorf("test update error")).Times(1) }, mockShardAffordance: func(mockShard *shard.MockContext) { mockShard.EXPECT().GetExecutionManager().Return(nil).Times(1) mockShard.EXPECT().GetMetricsClient().Return(metrics.NewNoopMetricsClient()).Times(1) }, mockNewStateBuilderFn: func(mutableState execution.MutableState, logger log.Logger) execution.StateBuilder { mockStateBuilder := execution.NewMockStateBuilder(gomock.NewController(t)) return mockStateBuilder }, expectError: fmt.Errorf("test update error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Mock objects mockTask := NewMockreplicationTask(ctrl) mockExecutionContext := execution.NewMockContext(ctrl) mockMutableState := execution.NewMockMutableState(ctrl) mockTransactionManager := NewMocktransactionManager(ctrl) mockShard := shard.NewMockContext(ctrl) logger := log.NewNoop() // Mock affordances test.mockTaskAffordance(mockTask) mockStateBuilder := execution.NewMockStateBuilder(ctrl) test.mockStateBuilderAffordance(mockStateBuilder, mockMutableState) test.mockTransactionManagerAffordance(mockTransactionManager) test.mockShardAffordance(mockShard) // Mock newStateBuilderFn newStateBuilderFn := func(mutableState execution.MutableState, logger log.Logger) execution.StateBuilder { return mockStateBuilder } // Call the function under test err := applyNonStartEventsToCurrentBranch(ctx.Background(), mockExecutionContext, mockMutableState, true, func(error) {}, mockTask, newStateBuilderFn, cluster.Metadata{}, mockShard, logger, mockTransactionManager) // Assertions assert.Equal(t, test.expectError, err) }) } } func Test_applyNonStartEventsToNoneCurrentBranch(t *testing.T) { tests := map[string]struct { mockTaskAffordance func(mockTask *MockreplicationTask) mockApplyNonStartEventsWithContinueAsNewAffordance func(replicator *historyReplicatorImpl) mockApplyNonStartEventsWithoutContinueAsNewAffordance func(replicator *historyReplicatorImpl) expectError error }{ "Case1: with NewEvents, should call applyNonStartEventsToNoneCurrentBranchWithContinueAsNew": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getNewEvents().Return([]*types.HistoryEvent{{}}).Times(1) }, mockApplyNonStartEventsWithContinueAsNewAffordance: func(replicator *historyReplicatorImpl) { replicator.applyNonStartEventsToNoneCurrentBranchWithContinueAsNewFn = func( ctx ctx.Context, context execution.Context, releaseFn execution.ReleaseFunc, task replicationTask, r *historyReplicatorImpl, ) error { return nil } }, mockApplyNonStartEventsWithoutContinueAsNewAffordance: func(replicator *historyReplicatorImpl) {}, expectError: nil, }, "Case2: without NewEvents, should call applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNew": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getNewEvents().Return(nil).Times(1) }, mockApplyNonStartEventsWithContinueAsNewAffordance: func(replicator *historyReplicatorImpl) {}, mockApplyNonStartEventsWithoutContinueAsNewAffordance: func(replicator *historyReplicatorImpl) { replicator.applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNewFn = func( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, releaseFn execution.ReleaseFunc, task replicationTask, transactionManager transactionManager, clusterMetadata cluster.Metadata, shard shard.Context, logger log.Logger, ) error { return nil } }, expectError: nil, }, "Case3: error case for applyNonStartEventsToNoneCurrentBranchWithContinueAsNew": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getNewEvents().Return([]*types.HistoryEvent{{}}).Times(1) }, mockApplyNonStartEventsWithContinueAsNewAffordance: func(replicator *historyReplicatorImpl) { replicator.applyNonStartEventsToNoneCurrentBranchWithContinueAsNewFn = func( ctx ctx.Context, context execution.Context, releaseFn execution.ReleaseFunc, task replicationTask, r *historyReplicatorImpl, ) error { return fmt.Errorf("test error") } }, mockApplyNonStartEventsWithoutContinueAsNewAffordance: func(replicator *historyReplicatorImpl) {}, expectError: fmt.Errorf("test error"), }, "Case4: error case for applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNew": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getNewEvents().Return(nil).Times(1) }, mockApplyNonStartEventsWithContinueAsNewAffordance: func(replicator *historyReplicatorImpl) {}, mockApplyNonStartEventsWithoutContinueAsNewAffordance: func(replicator *historyReplicatorImpl) { replicator.applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNewFn = func( ctx ctx.Context, context execution.Context, mutableState execution.MutableState, branchIndex int, releaseFn execution.ReleaseFunc, task replicationTask, transactionManager transactionManager, clusterMetadata cluster.Metadata, shard shard.Context, logger log.Logger, ) error { return fmt.Errorf("test error") } }, expectError: fmt.Errorf("test error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Mock objects mockTask := NewMockreplicationTask(ctrl) mockExecutionContext := execution.NewMockContext(ctrl) mockMutableState := execution.NewMockMutableState(ctrl) mockReleaseFn := func(error) {} // Create the replicator using createTestHistoryReplicator replicator := createTestHistoryReplicator(t, uuid.New()) // Mock affordances test.mockTaskAffordance(mockTask) test.mockApplyNonStartEventsWithContinueAsNewAffordance(&replicator) test.mockApplyNonStartEventsWithoutContinueAsNewAffordance(&replicator) // Call the function under test err := applyNonStartEventsToNoneCurrentBranch(ctx.Background(), mockExecutionContext, mockMutableState, 1, mockReleaseFn, mockTask, &replicator, replicator.logger) // Assertions assert.Equal(t, test.expectError, err) }) } } func Test_applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNew(t *testing.T) { tests := map[string]struct { mockTaskAffordance func(mockTask *MockreplicationTask) mockMutableStateAffordance func(mockMutableState *execution.MockMutableState) *persistence.VersionHistories mockTransactionManagerAffordance func(mockTransactionManager *MocktransactionManager) expectError error }{ "Case1: success case with no errors": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getLastEvent().Return(&types.HistoryEvent{ ID: 1, Version: 1, }).Times(2) mockTask.EXPECT().getEventTime().Return(time.Now()).Times(1) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(2) mockTask.EXPECT().getEvents().Return(nil).Times(1) }, mockMutableStateAffordance: func(mockMutableState *execution.MockMutableState) *persistence.VersionHistories { // Create a VersionHistory and VersionHistories structure versionHistory := &persistence.VersionHistory{ BranchToken: []byte("branch-token"), Items: []*persistence.VersionHistoryItem{}, } versionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{versionHistory}, } mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).Times(1) return versionHistories }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) { mockTransactionManager.EXPECT().backfillWorkflow( gomock.Any(), gomock.Any(), gomock.Any(), &persistence.WorkflowEvents{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", BranchToken: []byte("branch-token"), Events: nil, }, ).Return(nil).Times(1) }, expectError: nil, }, "Case2: missing version histories": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getLastEvent().Return(&types.HistoryEvent{ ID: 1, Version: 1, }).Times(2) }, mockMutableStateAffordance: func(mockMutableState *execution.MockMutableState) *persistence.VersionHistories { mockMutableState.EXPECT().GetVersionHistories().Return(nil).Times(1) return nil }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) {}, expectError: execution.ErrMissingVersionHistories, }, "Case3: error when adding or updating version history item": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getLastEvent().Return(&types.HistoryEvent{ ID: 1, Version: 1, }).Times(2) }, mockMutableStateAffordance: func(mockMutableState *execution.MockMutableState) *persistence.VersionHistories { // Create a VersionHistory and VersionHistories structure with a higher event ID and version versionHistory := &persistence.VersionHistory{ BranchToken: []byte("branch-token"), Items: []*persistence.VersionHistoryItem{ { EventID: 2, // Higher event ID Version: 2, // Higher version }, }, } versionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{versionHistory}, } mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).Times(1) return versionHistories }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) {}, expectError: &types.BadRequestError{Message: "cannot update version history with a lower version 1. Last version: 2"}, }, "Case4: error during backfill workflow": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getLastEvent().Return(&types.HistoryEvent{ ID: 1, Version: 1, }).Times(2) mockTask.EXPECT().getEventTime().Return(time.Now()).Times(1) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(2) mockTask.EXPECT().getEvents().Return(nil).Times(1) mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(1) }, mockMutableStateAffordance: func(mockMutableState *execution.MockMutableState) *persistence.VersionHistories { versionHistory := &persistence.VersionHistory{ BranchToken: []byte("branch-token"), Items: []*persistence.VersionHistoryItem{}, } versionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{versionHistory}, } mockMutableState.EXPECT().GetVersionHistories().Return(versionHistories).Times(1) return versionHistories }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) { mockTransactionManager.EXPECT().backfillWorkflow( gomock.Any(), gomock.Any(), gomock.Any(), &persistence.WorkflowEvents{ DomainID: "test-domain-id", WorkflowID: "test-workflow-id", RunID: "test-run-id", BranchToken: []byte("branch-token"), Events: nil, }, ).Return(fmt.Errorf("backfill error")).Times(1) }, expectError: fmt.Errorf("backfill error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Mock objects mockTask := NewMockreplicationTask(ctrl) mockExecutionContext := execution.NewMockContext(ctrl) mockMutableState := execution.NewMockMutableState(ctrl) mockTransactionManager := NewMocktransactionManager(ctrl) mockReleaseFn := func(error) {} // Mock affordances test.mockMutableStateAffordance(mockMutableState) test.mockTaskAffordance(mockTask) test.mockTransactionManagerAffordance(mockTransactionManager) mockShard := shard.NewMockContext(ctrl) // Call the function under test err := applyNonStartEventsToNoneCurrentBranchWithoutContinueAsNew( ctx.Background(), mockExecutionContext, mockMutableState, 0, // Ensure branchIndex is valid mockReleaseFn, mockTask, mockTransactionManager, cluster.Metadata{}, mockShard, testlogger.New(t), ) // Assertions assert.Equal(t, test.expectError, err) }) } } func Test_applyNonStartEventsMissingMutableState(t *testing.T) { tests := map[string]struct { mockTaskAffordance func(mockTask *MockreplicationTask) mockWorkflowResetterAffordance func(mockWorkflowResetter *MockWorkflowResetter) mockNewWorkflowResetterFn func(mockTask *MockreplicationTask, mockWorkflowResetter *MockWorkflowResetter) newWorkflowResetterFn expectMutableState execution.MutableState validateError func(t *testing.T, err error) }{ "Case1: non-reset workflow, should return retry task error": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().isWorkflowReset().Return(false).Times(1) mockTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ ID: 10, Version: 1, }).Times(1) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getWorkflowID().Return("test-workflow-id").Times(1) mockTask.EXPECT().getRunID().Return("test-run-id").Times(1) }, mockWorkflowResetterAffordance: func(mockWorkflowResetter *MockWorkflowResetter) {}, mockNewWorkflowResetterFn: func(mockTask *MockreplicationTask, mockWorkflowResetter *MockWorkflowResetter) newWorkflowResetterFn { return nil }, expectMutableState: nil, validateError: func(t *testing.T, err error) { assert.IsType(t, &types.RetryTaskV2Error{}, err) retryError := err.(*types.RetryTaskV2Error) assert.Equal(t, "Resend events due to missing mutable state", retryError.Message) assert.Equal(t, "test-domain-id", retryError.DomainID) assert.Equal(t, "test-workflow-id", retryError.WorkflowID) assert.Equal(t, "test-run-id", retryError.RunID) }, }, "Case2: reset workflow, should return reset mutable state": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().isWorkflowReset().Return(true).Times(1) mockTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ ID: 10, Version: 1, }).Times(2) mockTask.EXPECT().getWorkflowResetMetadata().Return("base-run-id", "new-run-id", int64(1), false).Times(1) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getWorkflowID().Return("test-workflow-id").Times(1) mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(1) mockTask.EXPECT().getEventTime().Return(time.Now()).Times(1) mockTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockWorkflowResetterAffordance: func(mockWorkflowResetter *MockWorkflowResetter) { mockMutableState := execution.NewMockMutableState(gomock.NewController(t)) // Ensure consistent MutableState mockWorkflowResetter.EXPECT().ResetWorkflow( gomock.Any(), gomock.Any(), int64(9), int64(1), int64(10), int64(1), ).Return(mockMutableState, nil).Times(1) }, mockNewWorkflowResetterFn: func(mockTask *MockreplicationTask, mockWorkflowResetter *MockWorkflowResetter) newWorkflowResetterFn { // Return the already created mockWorkflowResetter return func(domainID, workflowID, baseRunID string, newContext execution.Context, newRunID string, logger log.Logger) WorkflowResetter { return mockWorkflowResetter } }, expectMutableState: execution.NewMockMutableState(gomock.NewController(t)), // Match returned value validateError: func(t *testing.T, err error) { assert.NoError(t, err) }, }, "Case3: error during workflow reset": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().isWorkflowReset().Return(true).Times(1) mockTask.EXPECT().getFirstEvent().Return(&types.HistoryEvent{ ID: 10, Version: 1, }).Times(2) mockTask.EXPECT().getWorkflowResetMetadata().Return("base-run-id", "new-run-id", int64(1), false).Times(1) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getWorkflowID().Return("test-workflow-id").Times(1) mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(2) mockTask.EXPECT().getEventTime().Return(time.Now()).Times(1) mockTask.EXPECT().getVersion().Return(int64(1)).Times(1) }, mockWorkflowResetterAffordance: func(mockWorkflowResetter *MockWorkflowResetter) { mockWorkflowResetter.EXPECT().ResetWorkflow( gomock.Any(), gomock.Any(), int64(9), int64(1), int64(10), int64(1), ).Return(nil, fmt.Errorf("reset error")).Times(1) }, mockNewWorkflowResetterFn: func(mockTask *MockreplicationTask, mockWorkflowResetter *MockWorkflowResetter) newWorkflowResetterFn { // Return the already created mockWorkflowResetter return func(domainID, workflowID, baseRunID string, newContext execution.Context, newRunID string, logger log.Logger) WorkflowResetter { return mockWorkflowResetter } }, expectMutableState: nil, validateError: func(t *testing.T, err error) { assert.EqualError(t, err, "reset error") }, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Mock objects mockTask := NewMockreplicationTask(ctrl) mockExecutionContext := execution.NewMockContext(ctrl) mockWorkflowResetter := NewMockWorkflowResetter(ctrl) // Mock affordances test.mockTaskAffordance(mockTask) test.mockWorkflowResetterAffordance(mockWorkflowResetter) newWorkflowResetterFn := test.mockNewWorkflowResetterFn(mockTask, mockWorkflowResetter) // Call the function under test mutableState, err := applyNonStartEventsMissingMutableState( ctx.Background(), mockExecutionContext, mockTask, newWorkflowResetterFn, ) // Assertions assert.Equal(t, test.expectMutableState, mutableState) test.validateError(t, err) }) } } func Test_applyNonStartEventsResetWorkflow(t *testing.T) { tests := map[string]struct { mockTaskAffordance func(mockTask *MockreplicationTask) mockStateBuilderAffordance func(mockStateBuilder *execution.MockStateBuilder) mockTransactionManagerAffordance func(mockTransactionManager *MocktransactionManager) mockShardContextAffordance func(mockShard *shard.MockContext) expectError error }{ "Case1: success case with no errors": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(1) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockTask.EXPECT().getEvents().Return(nil).Times(1) mockTask.EXPECT().getNewEvents().Return(nil).Times(1) mockTask.EXPECT().getEventTime().Return(time.Now()).Times(2) mockTask.EXPECT().getSourceCluster().Return("test-source-cluster").Times(1) }, mockStateBuilderAffordance: func(mockStateBuilder *execution.MockStateBuilder) { mockStateBuilder.EXPECT().ApplyEvents( "test-domain-id", gomock.Any(), types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, nil, nil, ).Return(nil, nil).Times(1) }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) { mockTransactionManager.EXPECT().createWorkflow( gomock.Any(), gomock.Any(), gomock.Any(), ).Return(nil).Times(1) }, mockShardContextAffordance: func(mockShard *shard.MockContext) { mockShard.EXPECT().GetConfig().Return(&config.Config{ NumberOfShards: 0, IsAdvancedVisConfigExist: false, MaxResponseSize: 0, HistoryCacheInitialSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheMaxSize: dynamicproperties.GetIntPropertyFn(10), HistoryCacheTTL: dynamicproperties.GetDurationPropertyFn(10), HostName: "test-host", StandbyClusterDelay: dynamicproperties.GetDurationPropertyFn(10), EnableSizeBasedHistoryExecutionCache: dynamicproperties.GetBoolPropertyFn(false), }).Times(1) mockShard.EXPECT().SetCurrentTime(gomock.Any(), gomock.Any()).Times(1) }, expectError: nil, }, "Case2: error during ApplyEvents": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(2) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockTask.EXPECT().getEvents().Return(nil).Times(1) mockTask.EXPECT().getNewEvents().Return(nil).Times(1) }, mockStateBuilderAffordance: func(mockStateBuilder *execution.MockStateBuilder) { mockStateBuilder.EXPECT().ApplyEvents( "test-domain-id", gomock.Any(), types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, nil, nil, ).Return(nil, fmt.Errorf("applyEvents error")).Times(1) }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) {}, mockShardContextAffordance: func(mockShard *shard.MockContext) {}, expectError: fmt.Errorf("applyEvents error"), }, "Case3: error during createWorkflow": { mockTaskAffordance: func(mockTask *MockreplicationTask) { mockTask.EXPECT().getLogger().Return(log.NewNoop()).Times(2) mockTask.EXPECT().getDomainID().Return("test-domain-id").Times(1) mockTask.EXPECT().getExecution().Return(&types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }).Times(1) mockTask.EXPECT().getEvents().Return(nil).Times(1) mockTask.EXPECT().getNewEvents().Return(nil).Times(1) mockTask.EXPECT().getEventTime().Return(time.Now()).Times(1) }, mockStateBuilderAffordance: func(mockStateBuilder *execution.MockStateBuilder) { mockStateBuilder.EXPECT().ApplyEvents( "test-domain-id", gomock.Any(), types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, nil, nil, ).Return(nil, nil).Times(1) }, mockTransactionManagerAffordance: func(mockTransactionManager *MocktransactionManager) { mockTransactionManager.EXPECT().createWorkflow( gomock.Any(), gomock.Any(), gomock.Any(), ).Return(fmt.Errorf("createWorkflow error")).Times(1) }, mockShardContextAffordance: func(mockShard *shard.MockContext) {}, expectError: fmt.Errorf("createWorkflow error"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Mock objects mockTask := NewMockreplicationTask(ctrl) mockExecutionContext := execution.NewMockContext(ctrl) mockMutableState := execution.NewMockMutableState(ctrl) mockStateBuilder := execution.NewMockStateBuilder(ctrl) mockTransactionManager := NewMocktransactionManager(ctrl) mockShard := shard.NewMockContext(ctrl) logger := log.NewNoop() // Mock affordances test.mockTaskAffordance(mockTask) test.mockStateBuilderAffordance(mockStateBuilder) test.mockTransactionManagerAffordance(mockTransactionManager) test.mockShardContextAffordance(mockShard) // Mock functions mockNewStateBuilderFn := func(mutableState execution.MutableState, logger log.Logger) execution.StateBuilder { return mockStateBuilder } // Call the function under test err := applyNonStartEventsResetWorkflow( ctx.Background(), mockExecutionContext, mockMutableState, mockTask, mockNewStateBuilderFn, mockTransactionManager, cluster.Metadata{}, logger, mockShard, ) // Assertions assert.Equal(t, test.expectError, err) }) } } func Test_notify(t *testing.T) { tests := map[string]struct { clusterName string now time.Time currentClusterName string primaryClusterName string expectSetCurrentTime bool }{ "Case1: event from current cluster, should log a warning": { clusterName: "current-cluster", now: time.Now(), currentClusterName: "current-cluster", primaryClusterName: "primary-cluster", expectSetCurrentTime: false, }, "Case2: event from different cluster, should update shard time": { clusterName: "other-cluster", now: time.Now(), currentClusterName: "current-cluster", primaryClusterName: "primary-cluster", expectSetCurrentTime: true, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Create ClusterInformation instances clusterGroup := map[string]commonConfig.ClusterInformation{ "current-cluster": { Enabled: true, InitialFailoverVersion: 1, RPCName: "current-cluster-rpc", RPCAddress: "127.0.0.1:8080", RPCTransport: "tchannel", }, "other-cluster": { Enabled: true, InitialFailoverVersion: 2, RPCName: "other-cluster-rpc", RPCAddress: "127.0.0.1:8081", RPCTransport: "grpc", }, } // Create Metadata instance clusterMetadata := cluster.NewMetadata( commonConfig.ClusterGroupMetadata{ FailoverVersionIncrement: 1, PrimaryClusterName: test.primaryClusterName, CurrentClusterName: test.currentClusterName, ClusterGroup: clusterGroup, }, dynamicproperties.GetBoolPropertyFnFilteredByDomain(false), metrics.NewNoopMetricsClient(), log.NewNoop(), ) // Mock Shard Context mockShard := shard.NewMockContext(ctrl) if test.expectSetCurrentTime { mockShard.EXPECT().GetConfig().Return(&config.Config{ StandbyClusterDelay: dynamicproperties.GetDurationPropertyFn(5 * time.Minute), }).Times(1) mockShard.EXPECT().SetCurrentTime(test.clusterName, gomock.Any()).Times(1) } // Use Noop logger logger := log.NewNoop() // Call the function under test notify( test.clusterName, test.now, logger, mockShard, clusterMetadata, ) }) } } ================================================ FILE: service/history/ndc/new_workflow_transaction_mamanger_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: new_workflow_transaction_manager.go // // Generated by this command: // // mockgen -package ndc -source new_workflow_transaction_manager.go -destination new_workflow_transaction_mamanger_mock.go // // Package ndc is a generated GoMock package. package ndc import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" execution "github.com/uber/cadence/service/history/execution" ) // MocktransactionManagerForNewWorkflow is a mock of transactionManagerForNewWorkflow interface. type MocktransactionManagerForNewWorkflow struct { ctrl *gomock.Controller recorder *MocktransactionManagerForNewWorkflowMockRecorder isgomock struct{} } // MocktransactionManagerForNewWorkflowMockRecorder is the mock recorder for MocktransactionManagerForNewWorkflow. type MocktransactionManagerForNewWorkflowMockRecorder struct { mock *MocktransactionManagerForNewWorkflow } // NewMocktransactionManagerForNewWorkflow creates a new mock instance. func NewMocktransactionManagerForNewWorkflow(ctrl *gomock.Controller) *MocktransactionManagerForNewWorkflow { mock := &MocktransactionManagerForNewWorkflow{ctrl: ctrl} mock.recorder = &MocktransactionManagerForNewWorkflowMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MocktransactionManagerForNewWorkflow) EXPECT() *MocktransactionManagerForNewWorkflowMockRecorder { return m.recorder } // dispatchForNewWorkflow mocks base method. func (m *MocktransactionManagerForNewWorkflow) dispatchForNewWorkflow(ctx context.Context, now time.Time, targetWorkflow execution.Workflow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "dispatchForNewWorkflow", ctx, now, targetWorkflow) ret0, _ := ret[0].(error) return ret0 } // dispatchForNewWorkflow indicates an expected call of dispatchForNewWorkflow. func (mr *MocktransactionManagerForNewWorkflowMockRecorder) dispatchForNewWorkflow(ctx, now, targetWorkflow any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "dispatchForNewWorkflow", reflect.TypeOf((*MocktransactionManagerForNewWorkflow)(nil).dispatchForNewWorkflow), ctx, now, targetWorkflow) } ================================================ FILE: service/history/ndc/new_workflow_transaction_manager.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination new_workflow_transaction_mamanger_mock.go package ndc import ( ctx "context" "fmt" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" ) type ( transactionManagerForNewWorkflow interface { dispatchForNewWorkflow( ctx ctx.Context, now time.Time, targetWorkflow execution.Workflow, ) error } transactionManagerForNewWorkflowImpl struct { transactionManager transactionManager logger log.Logger } ) var _ transactionManagerForNewWorkflow = (*transactionManagerForNewWorkflowImpl)(nil) func newTransactionManagerForNewWorkflow( transactionManager transactionManager, logger log.Logger, ) transactionManagerForNewWorkflow { return &transactionManagerForNewWorkflowImpl{ transactionManager: transactionManager, logger: logger, } } func (r *transactionManagerForNewWorkflowImpl) dispatchForNewWorkflow( ctx ctx.Context, now time.Time, targetWorkflow execution.Workflow, ) error { // NOTE: this function does NOT mutate current workflow or target workflow, // workflow mutation is done in methods within executeTransaction function targetExecutionInfo := targetWorkflow.GetMutableState().GetExecutionInfo() domainID := targetExecutionInfo.DomainID workflowID := targetExecutionInfo.WorkflowID targetRunID := targetExecutionInfo.RunID // we need to check the current workflow execution currentRunID, err := r.transactionManager.getCurrentWorkflowRunID( ctx, domainID, workflowID, ) if err != nil || currentRunID == targetRunID { // error out or workflow already created return err } if currentRunID == "" { // current record does not exists return r.executeTransaction( ctx, now, transactionPolicyCreateAsCurrent, nil, targetWorkflow, ) } // there exists a current workflow, need additional check currentWorkflow, err := r.transactionManager.loadNDCWorkflow( ctx, domainID, workflowID, currentRunID, ) if err != nil { return err } targetWorkflowIsNewer, err := targetWorkflow.HappensAfter(currentWorkflow) if err != nil { return err } if !targetWorkflowIsNewer { // target workflow is older than current workflow, need to suppress the target workflow return r.executeTransaction( ctx, now, transactionPolicyCreateAsZombie, currentWorkflow, targetWorkflow, ) } // target workflow is newer than current workflow if !currentWorkflow.GetMutableState().IsWorkflowExecutionRunning() { // current workflow is completed // proceed to create workflow return r.executeTransaction( ctx, now, transactionPolicyCreateAsCurrent, currentWorkflow, targetWorkflow, ) } // current workflow is still running, need to suppress the current workflow return r.executeTransaction( ctx, now, transactionPolicySuppressCurrentAndCreateAsCurrent, currentWorkflow, targetWorkflow, ) } func (r *transactionManagerForNewWorkflowImpl) createAsCurrent( ctx ctx.Context, now time.Time, currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, ) error { targetWorkflowSnapshot, targetWorkflowEventsSeq, err := targetWorkflow.GetMutableState().CloseTransactionAsSnapshot( now, execution.TransactionPolicyPassive, ) if err != nil { return err } var targetWorkflowHistoryBlob events.PersistedBlob if len(targetWorkflowEventsSeq[0].Events) > 0 { if targetWorkflowEventsSeq[0].Events[0].GetEventType() == types.EventTypeWorkflowExecutionStarted { targetWorkflowHistoryBlob, err = targetWorkflow.GetContext().PersistStartWorkflowBatchEvents( ctx, targetWorkflowEventsSeq[0], ) } else { // reset workflows fall into else branch targetWorkflowHistoryBlob, err = targetWorkflow.GetContext().PersistNonStartWorkflowBatchEvents( ctx, targetWorkflowEventsSeq[0], ) } } if err != nil { return err } // target workflow to be created as current if currentWorkflow != nil { // current workflow exists, need to do compare and swap createMode := persistence.CreateWorkflowModeWorkflowIDReuse prevRunID := currentWorkflow.GetMutableState().GetExecutionInfo().RunID prevVectorClock, err := currentWorkflow.GetVectorClock() if err != nil { return err } return targetWorkflow.GetContext().CreateWorkflowExecution( ctx, targetWorkflowSnapshot, targetWorkflowHistoryBlob, createMode, prevRunID, prevVectorClock.LastWriteVersion, persistence.CreateWorkflowRequestModeReplicated, ) } // current workflow does not exists, create as brand new createMode := persistence.CreateWorkflowModeBrandNew prevRunID := "" prevLastWriteVersion := int64(0) return targetWorkflow.GetContext().CreateWorkflowExecution( ctx, targetWorkflowSnapshot, targetWorkflowHistoryBlob, createMode, prevRunID, prevLastWriteVersion, persistence.CreateWorkflowRequestModeReplicated, ) } func (r *transactionManagerForNewWorkflowImpl) createAsZombie( ctx ctx.Context, now time.Time, currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, ) error { targetWorkflowPolicy, err := targetWorkflow.SuppressBy( currentWorkflow, ) if err != nil { return err } if targetWorkflowPolicy != execution.TransactionPolicyPassive { return &types.InternalServiceError{ Message: "nDCTransactionManagerForNewWorkflow createAsZombie encounter target workflow policy not being passive", } } targetWorkflowSnapshot, targetWorkflowEventsSeq, err := targetWorkflow.GetMutableState().CloseTransactionAsSnapshot( now, targetWorkflowPolicy, ) if err != nil { return err } var targetWorkflowHistoryBlob events.PersistedBlob if len(targetWorkflowEventsSeq[0].Events) > 0 { if targetWorkflowEventsSeq[0].Events[0].GetEventType() == types.EventTypeWorkflowExecutionStarted { targetWorkflowHistoryBlob, err = targetWorkflow.GetContext().PersistStartWorkflowBatchEvents( ctx, targetWorkflowEventsSeq[0], ) } else { // reset workflows fall into else branch targetWorkflowHistoryBlob, err = targetWorkflow.GetContext().PersistNonStartWorkflowBatchEvents( ctx, targetWorkflowEventsSeq[0], ) } } if err != nil { return err } // release lock on current workflow, since current cluster maybe the active cluster // and events maybe reapplied to current workflow // TODO: add functional test for this case. currentWorkflow.GetReleaseFn()(nil) currentWorkflow = nil if err := targetWorkflow.GetContext().ReapplyEvents( targetWorkflowEventsSeq, ); err != nil { return err } createMode := persistence.CreateWorkflowModeZombie prevRunID := "" prevLastWriteVersion := int64(0) err = targetWorkflow.GetContext().CreateWorkflowExecution( ctx, targetWorkflowSnapshot, targetWorkflowHistoryBlob, createMode, prevRunID, prevLastWriteVersion, persistence.CreateWorkflowRequestModeReplicated, ) switch err.(type) { case nil: return nil case *persistence.WorkflowExecutionAlreadyStartedError: // workflow already created return nil default: return err } } func (r *transactionManagerForNewWorkflowImpl) suppressCurrentAndCreateAsCurrent( ctx ctx.Context, now time.Time, currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, ) error { currentWorkflowPolicy, err := currentWorkflow.SuppressBy( targetWorkflow, ) if err != nil { return err } if err := targetWorkflow.Revive(); err != nil { return err } r.logger.Debugf("suppressCurrentAndCreateAsCurrent calling UpdateWorkflowExecutionWithNew for wfID %s, current policy %v, new policy %v", currentWorkflow.GetMutableState().GetExecutionInfo().WorkflowID, currentWorkflowPolicy, execution.TransactionPolicyPassive, ) return currentWorkflow.GetContext().UpdateWorkflowExecutionWithNew( ctx, now, persistence.UpdateWorkflowModeUpdateCurrent, targetWorkflow.GetContext(), targetWorkflow.GetMutableState(), currentWorkflowPolicy, execution.TransactionPolicyPassive.Ptr(), persistence.CreateWorkflowRequestModeReplicated, ) } func (r *transactionManagerForNewWorkflowImpl) executeTransaction( ctx ctx.Context, now time.Time, transactionPolicy transactionPolicy, currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, ) (retError error) { defer func() { if rec := recover(); rec != nil { r.cleanupTransaction(currentWorkflow, targetWorkflow, errPanic) panic(rec) } else { r.cleanupTransaction(currentWorkflow, targetWorkflow, retError) } }() switch transactionPolicy { case transactionPolicyCreateAsCurrent: return r.createAsCurrent( ctx, now, currentWorkflow, targetWorkflow, ) case transactionPolicyCreateAsZombie: return r.createAsZombie( ctx, now, currentWorkflow, targetWorkflow, ) case transactionPolicySuppressCurrentAndCreateAsCurrent: return r.suppressCurrentAndCreateAsCurrent( ctx, now, currentWorkflow, targetWorkflow, ) default: return &types.InternalServiceError{ Message: fmt.Sprintf("nDCTransactionManager: encounter unknown transaction type: %v", transactionPolicy), } } } func (r *transactionManagerForNewWorkflowImpl) cleanupTransaction( currentWorkflow execution.Workflow, targetWorkflow execution.Workflow, err error, ) { if currentWorkflow != nil { currentWorkflow.GetReleaseFn()(err) } if targetWorkflow != nil { targetWorkflow.GetReleaseFn()(err) } } ================================================ FILE: service/history/ndc/new_workflow_transaction_manager_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( ctx "context" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" ) type ( transactionManagerForNewWorkflowSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockTransactionManager *MocktransactionManager createManager *transactionManagerForNewWorkflowImpl } ) func TestTransactionManagerForNewWorkflowSuite(t *testing.T) { s := new(transactionManagerForNewWorkflowSuite) suite.Run(t, s) } func (s *transactionManagerForNewWorkflowSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockTransactionManager = NewMocktransactionManager(s.controller) s.createManager = newTransactionManagerForNewWorkflow( s.mockTransactionManager, testlogger.New(s.T()), ).(*transactionManagerForNewWorkflowImpl) } func (s *transactionManagerForNewWorkflowSuite) TearDownTest() { s.controller.Finish() } func (s *transactionManagerForNewWorkflowSuite) TestDispatchForNewWorkflow_Dup() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" runID := "some random run ID" workflow := execution.NewMockWorkflow(s.controller) mutableState := execution.NewMockMutableState(s.controller) workflow.EXPECT().GetMutableState().Return(mutableState).AnyTimes() mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).AnyTimes() s.mockTransactionManager.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(runID, nil).Times(1) err := s.createManager.dispatchForNewWorkflow(ctx, now, workflow) s.NoError(err) } func (s *transactionManagerForNewWorkflowSuite) TestDispatchForNewWorkflow_BrandNew() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" runID := "some random run ID" releaseCalled := false workflow := execution.NewMockWorkflow(s.controller) context := execution.NewMockContext(s.controller) mutableState := execution.NewMockMutableState(s.controller) var releaseFn execution.ReleaseFunc = func(error) { releaseCalled = true } workflow.EXPECT().GetContext().Return(context).AnyTimes() workflow.EXPECT().GetMutableState().Return(mutableState).AnyTimes() workflow.EXPECT().GetReleaseFn().Return(releaseFn).AnyTimes() workflowSnapshot := &persistence.WorkflowSnapshot{} workflowStartedType := types.EventTypeWorkflowExecutionStarted workflowEventsSeq := []*persistence.WorkflowEvents{ &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { EventType: &workflowStartedType, }, }, }, } workflowHistory := events.PersistedBlob{DataBlob: persistence.DataBlob{Data: make([]byte, 12345)}} mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).AnyTimes() mutableState.EXPECT().CloseTransactionAsSnapshot(now, execution.TransactionPolicyPassive).Return( workflowSnapshot, workflowEventsSeq, nil, ).Times(1) s.mockTransactionManager.EXPECT().getCurrentWorkflowRunID( ctx, domainID, workflowID, ).Return("", nil).Times(1) context.EXPECT().PersistStartWorkflowBatchEvents( gomock.Any(), workflowEventsSeq[0], ).Return(workflowHistory, nil).Times(1) context.EXPECT().CreateWorkflowExecution( gomock.Any(), workflowSnapshot, workflowHistory, persistence.CreateWorkflowModeBrandNew, "", int64(0), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.createManager.dispatchForNewWorkflow(ctx, now, workflow) s.NoError(err) s.True(releaseCalled) } func (s *transactionManagerForNewWorkflowSuite) TestDispatchForNewWorkflow_CreateAsCurrent() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" currentRunID := "other random runID" currentLastWriteVersion := int64(4321) targetReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() currentWorkflow := execution.NewMockWorkflow(s.controller) currentMutableState := execution.NewMockMutableState(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetMutableState().Return(currentMutableState).AnyTimes() currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetWorkflowSnapshot := &persistence.WorkflowSnapshot{} workflowNonStartedType := types.EventTypeDecisionTaskScheduled // non workflow started event targetWorkflowEventsSeq := []*persistence.WorkflowEvents{ &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { EventType: &workflowNonStartedType, }, }, }, } targetWorkflowHistory := events.PersistedBlob{DataBlob: persistence.DataBlob{Data: make([]byte, 12345)}} targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() targetMutableState.EXPECT().CloseTransactionAsSnapshot(now, execution.TransactionPolicyPassive).Return( targetWorkflowSnapshot, targetWorkflowEventsSeq, nil, ).Times(1) s.mockTransactionManager.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionManager.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(true, nil) currentMutableState.EXPECT().IsWorkflowExecutionRunning().Return(false).AnyTimes() currentMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: currentRunID, }).AnyTimes() currentWorkflow.EXPECT().GetVectorClock().Return(execution.WorkflowVectorClock{ LastWriteVersion: currentLastWriteVersion, }, nil) targetContext.EXPECT().PersistNonStartWorkflowBatchEvents( gomock.Any(), targetWorkflowEventsSeq[0], ).Return(targetWorkflowHistory, nil).Times(1) targetContext.EXPECT().CreateWorkflowExecution( gomock.Any(), targetWorkflowSnapshot, targetWorkflowHistory, persistence.CreateWorkflowModeWorkflowIDReuse, currentRunID, currentLastWriteVersion, persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.createManager.dispatchForNewWorkflow(ctx, now, targetWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(currentReleaseCalled) } func (s *transactionManagerForNewWorkflowSuite) TestDispatchForNewWorkflow_CreateAsZombie() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" currentRunID := "other random runID" targetReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() currentWorkflow := execution.NewMockWorkflow(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetWorkflowSnapshot := &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, }, } workflowStartedType := types.EventTypeWorkflowExecutionStarted targetWorkflowEventsSeq := []*persistence.WorkflowEvents{ &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { EventType: &workflowStartedType, }, }, }, } targetWorkflowHistory := events.PersistedBlob{DataBlob: persistence.DataBlob{Data: make([]byte, 12345)}} targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() targetMutableState.EXPECT().CloseTransactionAsSnapshot(now, execution.TransactionPolicyPassive).Return( targetWorkflowSnapshot, targetWorkflowEventsSeq, nil, ).Times(1) s.mockTransactionManager.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionManager.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(false, nil) targetWorkflow.EXPECT().SuppressBy(currentWorkflow).Return(execution.TransactionPolicyPassive, nil).Times(1) targetContext.EXPECT().PersistStartWorkflowBatchEvents( gomock.Any(), targetWorkflowEventsSeq[0], ).Return(targetWorkflowHistory, nil).Times(1) targetContext.EXPECT().CreateWorkflowExecution( gomock.Any(), targetWorkflowSnapshot, targetWorkflowHistory, persistence.CreateWorkflowModeZombie, "", int64(0), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) targetContext.EXPECT().ReapplyEvents(targetWorkflowEventsSeq).Return(nil).Times(1) err := s.createManager.dispatchForNewWorkflow(ctx, now, targetWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(currentReleaseCalled) } func (s *transactionManagerForNewWorkflowSuite) TestDispatchForNewWorkflow_CreateAsZombie_Dedup() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" currentRunID := "other random runID" targetReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() currentWorkflow := execution.NewMockWorkflow(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetWorkflowSnapshot := &persistence.WorkflowSnapshot{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, }, } workflowNonStartedType := types.EventTypeDecisionTaskScheduled // non workflow started event targetWorkflowEventsSeq := []*persistence.WorkflowEvents{ &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{ { EventType: &workflowNonStartedType, }, }, }, } targetWorkflowHistory := events.PersistedBlob{DataBlob: persistence.DataBlob{Data: make([]byte, 12345)}} targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() targetMutableState.EXPECT().CloseTransactionAsSnapshot(now, execution.TransactionPolicyPassive).Return( targetWorkflowSnapshot, targetWorkflowEventsSeq, nil, ).Times(1) s.mockTransactionManager.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionManager.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(false, nil) targetWorkflow.EXPECT().SuppressBy(currentWorkflow).Return(execution.TransactionPolicyPassive, nil).Times(1) targetContext.EXPECT().PersistNonStartWorkflowBatchEvents( gomock.Any(), targetWorkflowEventsSeq[0], ).Return(targetWorkflowHistory, nil).Times(1) targetContext.EXPECT().CreateWorkflowExecution( gomock.Any(), targetWorkflowSnapshot, targetWorkflowHistory, persistence.CreateWorkflowModeZombie, "", int64(0), persistence.CreateWorkflowRequestModeReplicated, ).Return(&persistence.WorkflowExecutionAlreadyStartedError{}).Times(1) targetContext.EXPECT().ReapplyEvents(targetWorkflowEventsSeq).Return(nil).Times(1) err := s.createManager.dispatchForNewWorkflow(ctx, now, targetWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(currentReleaseCalled) } func (s *transactionManagerForNewWorkflowSuite) TestDispatchForNewWorkflow_SuppressCurrentAndCreateAsCurrent() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" targetRunID := "some random run ID" currentRunID := "other random runID" targetReleaseCalled := false currentReleaseCalled := false targetWorkflow := execution.NewMockWorkflow(s.controller) targetContext := execution.NewMockContext(s.controller) targetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { targetReleaseCalled = true } targetWorkflow.EXPECT().GetContext().Return(targetContext).AnyTimes() targetWorkflow.EXPECT().GetMutableState().Return(targetMutableState).AnyTimes() targetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() currentWorkflow := execution.NewMockWorkflow(s.controller) currentContext := execution.NewMockContext(s.controller) currentMutableState := execution.NewMockMutableState(s.controller) currentMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, }).AnyTimes() var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetContext().Return(currentContext).AnyTimes() currentWorkflow.EXPECT().GetMutableState().Return(currentMutableState).AnyTimes() currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() targetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: targetRunID, }).AnyTimes() s.mockTransactionManager.EXPECT().getCurrentWorkflowRunID(ctx, domainID, workflowID).Return(currentRunID, nil).Times(1) s.mockTransactionManager.EXPECT().loadNDCWorkflow(ctx, domainID, workflowID, currentRunID).Return(currentWorkflow, nil).Times(1) targetWorkflow.EXPECT().HappensAfter(currentWorkflow).Return(true, nil) currentMutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() currentWorkflowPolicy := execution.TransactionPolicyActive currentWorkflow.EXPECT().SuppressBy(targetWorkflow).Return(currentWorkflowPolicy, nil).Times(1) targetWorkflow.EXPECT().Revive().Return(nil).Times(1) currentContext.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeUpdateCurrent, targetContext, targetMutableState, currentWorkflowPolicy, execution.TransactionPolicyPassive.Ptr(), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.createManager.dispatchForNewWorkflow(ctx, now, targetWorkflow) s.NoError(err) s.True(targetReleaseCalled) s.True(currentReleaseCalled) } ================================================ FILE: service/history/ndc/replication_task.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination replication_task_mock.go -self_package github.com/uber/cadence/service/history/ndc package ndc import ( "time" "github.com/pborman/uuid" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( replicationTask interface { getDomainID() string getExecution() *types.WorkflowExecution getWorkflowID() string getRunID() string getEventTime() time.Time getFirstEvent() *types.HistoryEvent getLastEvent() *types.HistoryEvent getVersion() int64 getSourceCluster() string getEvents() []*types.HistoryEvent getNewEvents() []*types.HistoryEvent getLogger() log.Logger getVersionHistory() *persistence.VersionHistory isWorkflowReset() bool getWorkflowResetMetadata() (string, string, int64, bool) splitTask(taskStartTime time.Time) (replicationTask, replicationTask, error) } replicationTaskImpl struct { sourceCluster string domainID string execution *types.WorkflowExecution version int64 firstEvent *types.HistoryEvent lastEvent *types.HistoryEvent eventTime time.Time events []*types.HistoryEvent newEvents []*types.HistoryEvent versionHistory *persistence.VersionHistory startTime time.Time logger log.Logger } ) var ( // ErrInvalidDomainID is returned if domain ID is invalid ErrInvalidDomainID = &types.BadRequestError{Message: "invalid domain ID"} // ErrInvalidExecution is returned if execution is invalid ErrInvalidExecution = &types.BadRequestError{Message: "invalid execution"} // ErrInvalidRunID is returned if run ID is invalid ErrInvalidRunID = &types.BadRequestError{Message: "invalid run ID"} // ErrEventIDMismatch is returned if event ID mis-matched ErrEventIDMismatch = &types.BadRequestError{Message: "event ID mismatch"} // ErrEventVersionMismatch is returned if event version mis-matched ErrEventVersionMismatch = &types.BadRequestError{Message: "event version mismatch"} // ErrNoNewRunHistory is returned if there is no new run history ErrNoNewRunHistory = &types.BadRequestError{Message: "no new run history events"} // ErrLastEventIsNotContinueAsNew is returned if the last event is not continue as new ErrLastEventIsNotContinueAsNew = &types.BadRequestError{Message: "last event is not continue as new"} // ErrEmptyHistoryRawEventBatch indicate that one single batch of history raw events is of size 0 ErrEmptyHistoryRawEventBatch = &types.BadRequestError{Message: "encounter empty history batch"} ) func newReplicationTask( clusterMetadata cluster.Metadata, historySerializer persistence.PayloadSerializer, taskStartTime time.Time, logger log.Logger, request *types.ReplicateEventsV2Request, ) (replicationTask, error) { events, newEvents, err := validateReplicateEventsRequest( historySerializer, request, ) if err != nil { return nil, err } domainID := request.GetDomainUUID() execution := request.WorkflowExecution versionHistory := &types.VersionHistory{ BranchToken: nil, Items: request.VersionHistoryItems, } firstEvent := events[0] lastEvent := events[len(events)-1] version := firstEvent.Version sourceCluster, err := clusterMetadata.ClusterNameForFailoverVersion(version) if err != nil { return nil, err } eventTime := int64(0) for _, event := range events { if event.GetTimestamp() > eventTime { eventTime = event.GetTimestamp() } } for _, event := range newEvents { if event.GetTimestamp() > eventTime { eventTime = event.GetTimestamp() } } logger = logger.WithTags( tag.WorkflowID(execution.GetWorkflowID()), tag.WorkflowRunID(execution.GetRunID()), tag.SourceCluster(sourceCluster), tag.IncomingVersion(version), tag.WorkflowFirstEventID(firstEvent.ID), tag.WorkflowNextEventID(lastEvent.ID+1), ) return &replicationTaskImpl{ sourceCluster: sourceCluster, domainID: domainID, execution: execution, version: version, firstEvent: firstEvent, lastEvent: lastEvent, eventTime: time.Unix(0, eventTime), events: events, newEvents: newEvents, versionHistory: persistence.NewVersionHistoryFromInternalType(versionHistory), startTime: taskStartTime, logger: logger, }, nil } func (t *replicationTaskImpl) getDomainID() string { return t.domainID } func (t *replicationTaskImpl) getExecution() *types.WorkflowExecution { return t.execution } func (t *replicationTaskImpl) getWorkflowID() string { return t.execution.GetWorkflowID() } func (t *replicationTaskImpl) getRunID() string { return t.execution.GetRunID() } func (t *replicationTaskImpl) getEventTime() time.Time { return t.eventTime } func (t *replicationTaskImpl) getFirstEvent() *types.HistoryEvent { return t.firstEvent } func (t *replicationTaskImpl) getLastEvent() *types.HistoryEvent { return t.lastEvent } func (t *replicationTaskImpl) getVersion() int64 { return t.version } func (t *replicationTaskImpl) getSourceCluster() string { return t.sourceCluster } func (t *replicationTaskImpl) getEvents() []*types.HistoryEvent { return t.events } func (t *replicationTaskImpl) getNewEvents() []*types.HistoryEvent { return t.newEvents } func (t *replicationTaskImpl) getLogger() log.Logger { return t.logger } func (t *replicationTaskImpl) getVersionHistory() *persistence.VersionHistory { return t.versionHistory } func (t *replicationTaskImpl) isWorkflowReset() bool { baseRunID, newRunID, baseEventVersion, isReset := t.getWorkflowResetMetadata() return len(baseRunID) > 0 && baseEventVersion != constants.EmptyVersion && len(newRunID) > 0 && isReset } func (t *replicationTaskImpl) getWorkflowResetMetadata() (string, string, int64, bool) { var baseRunID string var newRunID string var baseEventVersion = constants.EmptyVersion var isReset bool switch t.getFirstEvent().GetEventType() { case types.EventTypeDecisionTaskFailed: decisionTaskFailedEvent := t.getFirstEvent() attr := decisionTaskFailedEvent.GetDecisionTaskFailedEventAttributes() baseRunID = attr.GetBaseRunID() baseEventVersion = attr.GetForkEventVersion() newRunID = attr.GetNewRunID() if attr.GetCause() == types.DecisionTaskFailedCauseResetWorkflow { isReset = true } case types.EventTypeDecisionTaskTimedOut: decisionTaskTimedOutEvent := t.getFirstEvent() attr := decisionTaskTimedOutEvent.GetDecisionTaskTimedOutEventAttributes() baseRunID = attr.GetBaseRunID() baseEventVersion = attr.GetForkEventVersion() newRunID = attr.GetNewRunID() if attr.GetCause() == types.DecisionTaskTimedOutCauseReset { isReset = true } } return baseRunID, newRunID, baseEventVersion, isReset } func (t *replicationTaskImpl) splitTask( taskStartTime time.Time, ) (replicationTask, replicationTask, error) { if len(t.newEvents) == 0 { return nil, nil, ErrNoNewRunHistory } newHistoryEvents := t.newEvents if t.getLastEvent().GetEventType() != types.EventTypeWorkflowExecutionContinuedAsNew || t.getLastEvent().WorkflowExecutionContinuedAsNewEventAttributes == nil { return nil, nil, ErrLastEventIsNotContinueAsNew } newRunID := t.getLastEvent().WorkflowExecutionContinuedAsNewEventAttributes.GetNewExecutionRunID() newFirstEvent := newHistoryEvents[0] newLastEvent := newHistoryEvents[len(newHistoryEvents)-1] newEventTime := int64(0) for _, event := range newHistoryEvents { if event.GetTimestamp() > newEventTime { newEventTime = event.GetTimestamp() } } newVersionHistory := persistence.NewVersionHistoryFromInternalType(&types.VersionHistory{ BranchToken: nil, Items: []*types.VersionHistoryItem{{ EventID: newLastEvent.ID, Version: newLastEvent.Version, }}, }) logger := t.logger.WithTags( tag.WorkflowID(t.getExecution().GetWorkflowID()), tag.WorkflowRunID(newRunID), tag.SourceCluster(t.sourceCluster), tag.IncomingVersion(t.version), tag.WorkflowFirstEventID(newFirstEvent.ID), tag.WorkflowNextEventID(newLastEvent.ID+1), ) newRunTask := &replicationTaskImpl{ sourceCluster: t.sourceCluster, domainID: t.domainID, execution: &types.WorkflowExecution{ WorkflowID: t.execution.WorkflowID, RunID: newRunID, }, version: t.version, firstEvent: newFirstEvent, lastEvent: newLastEvent, eventTime: time.Unix(0, newEventTime), events: newHistoryEvents, newEvents: []*types.HistoryEvent{}, versionHistory: newVersionHistory, startTime: taskStartTime, logger: logger, } t.newEvents = nil return t, newRunTask, nil } func validateReplicateEventsRequest( historySerializer persistence.PayloadSerializer, request *types.ReplicateEventsV2Request, ) ([]*types.HistoryEvent, []*types.HistoryEvent, error) { // TODO add validation on version history if valid := validateUUID(request.GetDomainUUID()); !valid { return nil, nil, ErrInvalidDomainID } if request.WorkflowExecution == nil { return nil, nil, ErrInvalidExecution } if valid := validateUUID(request.WorkflowExecution.GetRunID()); !valid { return nil, nil, ErrInvalidRunID } events, err := deserializeBlob(historySerializer, request.Events) if err != nil { return nil, nil, err } if len(events) == 0 { return nil, nil, ErrEmptyHistoryRawEventBatch } version, err := validateEvents(events) if err != nil { return nil, nil, err } if request.NewRunEvents == nil { return events, nil, nil } newRunEvents, err := deserializeBlob(historySerializer, request.NewRunEvents) if err != nil { return nil, nil, err } newRunVersion, err := validateEvents(newRunEvents) if err != nil { return nil, nil, err } if version != newRunVersion { return nil, nil, ErrEventVersionMismatch } return events, newRunEvents, nil } func validateUUID(input string) bool { return uuid.Parse(input) != nil } func validateEvents(events []*types.HistoryEvent) (int64, error) { firstEvent := events[0] firstEventID := firstEvent.ID version := firstEvent.Version for index, event := range events { if event.ID != firstEventID+int64(index) { return 0, ErrEventIDMismatch } if event.Version != version { return 0, ErrEventVersionMismatch } } return version, nil } func deserializeBlob( historySerializer persistence.PayloadSerializer, blob *types.DataBlob, ) ([]*types.HistoryEvent, error) { if blob == nil { return nil, nil } internalEvents, err := historySerializer.DeserializeBatchEvents(&persistence.DataBlob{ Encoding: constants.EncodingTypeThriftRW, Data: blob.Data, }) return internalEvents, err } ================================================ FILE: service/history/ndc/replication_task_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: replication_task.go // // Generated by this command: // // mockgen -package ndc -source replication_task.go -destination replication_task_mock.go -self_package github.com/uber/cadence/service/history/ndc // // Package ndc is a generated GoMock package. package ndc import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" log "github.com/uber/cadence/common/log" persistence "github.com/uber/cadence/common/persistence" types "github.com/uber/cadence/common/types" ) // MockreplicationTask is a mock of replicationTask interface. type MockreplicationTask struct { ctrl *gomock.Controller recorder *MockreplicationTaskMockRecorder isgomock struct{} } // MockreplicationTaskMockRecorder is the mock recorder for MockreplicationTask. type MockreplicationTaskMockRecorder struct { mock *MockreplicationTask } // NewMockreplicationTask creates a new mock instance. func NewMockreplicationTask(ctrl *gomock.Controller) *MockreplicationTask { mock := &MockreplicationTask{ctrl: ctrl} mock.recorder = &MockreplicationTaskMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockreplicationTask) EXPECT() *MockreplicationTaskMockRecorder { return m.recorder } // getDomainID mocks base method. func (m *MockreplicationTask) getDomainID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getDomainID") ret0, _ := ret[0].(string) return ret0 } // getDomainID indicates an expected call of getDomainID. func (mr *MockreplicationTaskMockRecorder) getDomainID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getDomainID", reflect.TypeOf((*MockreplicationTask)(nil).getDomainID)) } // getEventTime mocks base method. func (m *MockreplicationTask) getEventTime() time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getEventTime") ret0, _ := ret[0].(time.Time) return ret0 } // getEventTime indicates an expected call of getEventTime. func (mr *MockreplicationTaskMockRecorder) getEventTime() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getEventTime", reflect.TypeOf((*MockreplicationTask)(nil).getEventTime)) } // getEvents mocks base method. func (m *MockreplicationTask) getEvents() []*types.HistoryEvent { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getEvents") ret0, _ := ret[0].([]*types.HistoryEvent) return ret0 } // getEvents indicates an expected call of getEvents. func (mr *MockreplicationTaskMockRecorder) getEvents() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getEvents", reflect.TypeOf((*MockreplicationTask)(nil).getEvents)) } // getExecution mocks base method. func (m *MockreplicationTask) getExecution() *types.WorkflowExecution { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getExecution") ret0, _ := ret[0].(*types.WorkflowExecution) return ret0 } // getExecution indicates an expected call of getExecution. func (mr *MockreplicationTaskMockRecorder) getExecution() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getExecution", reflect.TypeOf((*MockreplicationTask)(nil).getExecution)) } // getFirstEvent mocks base method. func (m *MockreplicationTask) getFirstEvent() *types.HistoryEvent { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getFirstEvent") ret0, _ := ret[0].(*types.HistoryEvent) return ret0 } // getFirstEvent indicates an expected call of getFirstEvent. func (mr *MockreplicationTaskMockRecorder) getFirstEvent() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getFirstEvent", reflect.TypeOf((*MockreplicationTask)(nil).getFirstEvent)) } // getLastEvent mocks base method. func (m *MockreplicationTask) getLastEvent() *types.HistoryEvent { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getLastEvent") ret0, _ := ret[0].(*types.HistoryEvent) return ret0 } // getLastEvent indicates an expected call of getLastEvent. func (mr *MockreplicationTaskMockRecorder) getLastEvent() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getLastEvent", reflect.TypeOf((*MockreplicationTask)(nil).getLastEvent)) } // getLogger mocks base method. func (m *MockreplicationTask) getLogger() log.Logger { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getLogger") ret0, _ := ret[0].(log.Logger) return ret0 } // getLogger indicates an expected call of getLogger. func (mr *MockreplicationTaskMockRecorder) getLogger() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getLogger", reflect.TypeOf((*MockreplicationTask)(nil).getLogger)) } // getNewEvents mocks base method. func (m *MockreplicationTask) getNewEvents() []*types.HistoryEvent { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getNewEvents") ret0, _ := ret[0].([]*types.HistoryEvent) return ret0 } // getNewEvents indicates an expected call of getNewEvents. func (mr *MockreplicationTaskMockRecorder) getNewEvents() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getNewEvents", reflect.TypeOf((*MockreplicationTask)(nil).getNewEvents)) } // getRunID mocks base method. func (m *MockreplicationTask) getRunID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getRunID") ret0, _ := ret[0].(string) return ret0 } // getRunID indicates an expected call of getRunID. func (mr *MockreplicationTaskMockRecorder) getRunID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getRunID", reflect.TypeOf((*MockreplicationTask)(nil).getRunID)) } // getSourceCluster mocks base method. func (m *MockreplicationTask) getSourceCluster() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getSourceCluster") ret0, _ := ret[0].(string) return ret0 } // getSourceCluster indicates an expected call of getSourceCluster. func (mr *MockreplicationTaskMockRecorder) getSourceCluster() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getSourceCluster", reflect.TypeOf((*MockreplicationTask)(nil).getSourceCluster)) } // getVersion mocks base method. func (m *MockreplicationTask) getVersion() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getVersion") ret0, _ := ret[0].(int64) return ret0 } // getVersion indicates an expected call of getVersion. func (mr *MockreplicationTaskMockRecorder) getVersion() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getVersion", reflect.TypeOf((*MockreplicationTask)(nil).getVersion)) } // getVersionHistory mocks base method. func (m *MockreplicationTask) getVersionHistory() *persistence.VersionHistory { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getVersionHistory") ret0, _ := ret[0].(*persistence.VersionHistory) return ret0 } // getVersionHistory indicates an expected call of getVersionHistory. func (mr *MockreplicationTaskMockRecorder) getVersionHistory() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getVersionHistory", reflect.TypeOf((*MockreplicationTask)(nil).getVersionHistory)) } // getWorkflowID mocks base method. func (m *MockreplicationTask) getWorkflowID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getWorkflowID") ret0, _ := ret[0].(string) return ret0 } // getWorkflowID indicates an expected call of getWorkflowID. func (mr *MockreplicationTaskMockRecorder) getWorkflowID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getWorkflowID", reflect.TypeOf((*MockreplicationTask)(nil).getWorkflowID)) } // getWorkflowResetMetadata mocks base method. func (m *MockreplicationTask) getWorkflowResetMetadata() (string, string, int64, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getWorkflowResetMetadata") ret0, _ := ret[0].(string) ret1, _ := ret[1].(string) ret2, _ := ret[2].(int64) ret3, _ := ret[3].(bool) return ret0, ret1, ret2, ret3 } // getWorkflowResetMetadata indicates an expected call of getWorkflowResetMetadata. func (mr *MockreplicationTaskMockRecorder) getWorkflowResetMetadata() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getWorkflowResetMetadata", reflect.TypeOf((*MockreplicationTask)(nil).getWorkflowResetMetadata)) } // isWorkflowReset mocks base method. func (m *MockreplicationTask) isWorkflowReset() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "isWorkflowReset") ret0, _ := ret[0].(bool) return ret0 } // isWorkflowReset indicates an expected call of isWorkflowReset. func (mr *MockreplicationTaskMockRecorder) isWorkflowReset() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "isWorkflowReset", reflect.TypeOf((*MockreplicationTask)(nil).isWorkflowReset)) } // splitTask mocks base method. func (m *MockreplicationTask) splitTask(taskStartTime time.Time) (replicationTask, replicationTask, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "splitTask", taskStartTime) ret0, _ := ret[0].(replicationTask) ret1, _ := ret[1].(replicationTask) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // splitTask indicates an expected call of splitTask. func (mr *MockreplicationTaskMockRecorder) splitTask(taskStartTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "splitTask", reflect.TypeOf((*MockreplicationTask)(nil).splitTask), taskStartTime) } ================================================ FILE: service/history/ndc/replication_task_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ndc import ( "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestReplicationTaskResetEvent(t *testing.T) { taskStartTime := time.Now() domainID := uuid.New() workflowID := uuid.New() runID := uuid.New() logger := testlogger.New(t) clusterMetadata := cluster.GetTestClusterMetadata(true) historySerializer := persistence.NewPayloadSerializer() versionHistoryItems := []*types.VersionHistoryItem{} versionHistoryItems = append(versionHistoryItems, persistence.NewVersionHistoryItem(3, 0).ToInternalType()) eventType := types.EventTypeDecisionTaskFailed resetCause := types.DecisionTaskFailedCauseResetWorkflow event := &types.HistoryEvent{ ID: 3, EventType: &eventType, Version: 0, DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ BaseRunID: uuid.New(), ForkEventVersion: 0, NewRunID: runID, Cause: &resetCause, }, } events := []*types.HistoryEvent{} events = append(events, event) eventsBlob, err := historySerializer.SerializeBatchEvents(events, constants.EncodingTypeThriftRW) require.NoError(t, err) request := &types.ReplicateEventsV2Request{ DomainUUID: domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, VersionHistoryItems: versionHistoryItems, Events: eventsBlob.ToInternal(), NewRunEvents: nil, } task, err := newReplicationTask(clusterMetadata, historySerializer, taskStartTime, logger, request) require.NoError(t, err) assert.True(t, task.isWorkflowReset()) } func TestNewReplicationTask(t *testing.T) { encodingType1 := types.EncodingTypeJSON encodingType2 := types.EncodingTypeThriftRW historyEvent1 := &types.HistoryEvent{ Version: 0, } historyEvent2 := &types.HistoryEvent{ Version: 1, } historyEvents := []*types.HistoryEvent{historyEvent1, historyEvent2} serializer := persistence.NewPayloadSerializer() serializedEvents, err := serializer.SerializeBatchEvents(historyEvents, constants.EncodingTypeThriftRW) assert.NoError(t, err) historyEvent3 := &types.HistoryEvent{ ID: 0, Version: 2, } historyEvent4 := &types.HistoryEvent{ ID: 1, Version: 3, } historyEvents2 := []*types.HistoryEvent{historyEvent3} serializedEvents2, err := serializer.SerializeBatchEvents(historyEvents2, constants.EncodingTypeThriftRW) assert.NoError(t, err) historyEvents3 := []*types.HistoryEvent{historyEvent3, historyEvent4} serializedEvents3, err := serializer.SerializeBatchEvents(historyEvents3, constants.EncodingTypeThriftRW) assert.NoError(t, err) historyEvents4 := []*types.HistoryEvent{historyEvent4} serializedEvents4, err := serializer.SerializeBatchEvents(historyEvents4, constants.EncodingTypeThriftRW) assert.NoError(t, err) tests := map[string]struct { request *types.ReplicateEventsV2Request eventsAffordance func() []*types.HistoryEvent expectedErrorMsg string }{ "Case1: empty case": { request: &types.ReplicateEventsV2Request{}, expectedErrorMsg: "invalid domain ID", }, "Case2: nil case": { request: nil, expectedErrorMsg: "invalid domain ID", }, "Case3-1: fail case with invalid domain id": { request: &types.ReplicateEventsV2Request{ DomainUUID: "12345678-1234-5678-9012-", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, VersionHistoryItems: nil, Events: nil, NewRunEvents: nil, }, expectedErrorMsg: "invalid domain ID", }, "Case3-2: fail case in validateReplicateEventsRequest with invalid run id": { request: &types.ReplicateEventsV2Request{ DomainUUID: "12345678-1234-5678-9012-123456789011", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, VersionHistoryItems: nil, Events: nil, NewRunEvents: nil, }, expectedErrorMsg: "invalid run ID", }, "Case3-3: fail case in validateReplicateEventsRequest with invalid workflow execution": { request: &types.ReplicateEventsV2Request{ DomainUUID: "12345678-1234-5678-9012-123456789011", WorkflowExecution: nil, VersionHistoryItems: nil, Events: nil, NewRunEvents: nil, }, expectedErrorMsg: "invalid execution", }, "Case3-4: fail case in validateReplicateEventsRequest with event is empty": { request: &types.ReplicateEventsV2Request{ DomainUUID: "12345678-1234-5678-9012-123456789011", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "12345678-1234-5678-9012-123456789012", }, VersionHistoryItems: nil, Events: nil, NewRunEvents: nil, }, expectedErrorMsg: "encounter empty history batch", }, "Case3-5: fail case in validateReplicateEventsRequest with DeserializeBatchEvents error": { request: &types.ReplicateEventsV2Request{ DomainUUID: "12345678-1234-5678-9012-123456789011", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "12345678-1234-5678-9012-123456789012", }, VersionHistoryItems: nil, Events: &types.DataBlob{ EncodingType: &encodingType1, Data: []byte("test-data"), }, NewRunEvents: nil, }, expectedErrorMsg: "cadence deserialization error: DeserializeBatchEvents encoding: \"thriftrw\", error: Invalid binary encoding version.", }, "Case3-6: fail case in validateReplicateEventsRequest with event ID mismatch": { request: &types.ReplicateEventsV2Request{ DomainUUID: "12345678-1234-5678-9012-123456789011", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "12345678-1234-5678-9012-123456789012", }, VersionHistoryItems: nil, Events: &types.DataBlob{ EncodingType: &encodingType2, Data: serializedEvents.Data, }, NewRunEvents: nil, }, expectedErrorMsg: "event ID mismatch", }, "Case3-7: fail case in validateReplicateEventsRequest with event version mismatch": { request: &types.ReplicateEventsV2Request{ DomainUUID: "12345678-1234-5678-9012-123456789011", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "12345678-1234-5678-9012-123456789012", }, VersionHistoryItems: nil, Events: &types.DataBlob{ EncodingType: &encodingType2, Data: serializedEvents2.Data, }, NewRunEvents: &types.DataBlob{ EncodingType: &encodingType2, Data: serializedEvents3.Data, }, }, expectedErrorMsg: "event version mismatch", }, "Case3-8: fail case in validateReplicateEventsRequest with ErrEventVersionMismatch": { request: &types.ReplicateEventsV2Request{ DomainUUID: "12345678-1234-5678-9012-123456789011", WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "12345678-1234-5678-9012-123456789012", }, VersionHistoryItems: nil, Events: &types.DataBlob{ EncodingType: &encodingType2, Data: serializedEvents2.Data, }, NewRunEvents: &types.DataBlob{ EncodingType: &encodingType2, Data: serializedEvents4.Data, }, }, expectedErrorMsg: "event version mismatch", }, } for name, test := range tests { t.Run(name, func(t *testing.T) { domainID := "" if test.request != nil { domainID = test.request.DomainUUID } replicator := createTestHistoryReplicator(t, domainID) _, err := newReplicationTask( replicator.clusterMetadata, replicator.historySerializer, time.Now(), replicator.logger, test.request, ) assert.Equal(t, test.expectedErrorMsg, err.Error()) }) } } func Test_getWorkflowResetMetadata(t *testing.T) { tests := map[string]struct { mockTaskAffordance func(mockTask *replicationTaskImpl) expectBaseRunID string expectNewRunID string expectBaseEventVersion int64 expectIsReset bool }{ "Case1: DecisionTaskFailed with reset workflow": { mockTaskAffordance: func(mockTask *replicationTaskImpl) { decisionTaskFailedEvent := &types.HistoryEvent{ EventType: types.EventTypeDecisionTaskFailed.Ptr(), DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ BaseRunID: "base-run-id", ForkEventVersion: 1, NewRunID: "new-run-id", Cause: types.DecisionTaskFailedCauseResetWorkflow.Ptr(), }, } mockTask.firstEvent = decisionTaskFailedEvent }, expectBaseRunID: "base-run-id", expectNewRunID: "new-run-id", expectBaseEventVersion: 1, expectIsReset: true, }, "Case2: DecisionTaskTimedOut with reset workflow": { mockTaskAffordance: func(mockTask *replicationTaskImpl) { decisionTaskTimedOutEvent := &types.HistoryEvent{ EventType: types.EventTypeDecisionTaskTimedOut.Ptr(), DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ BaseRunID: "base-run-id-timedout", ForkEventVersion: 2, NewRunID: "new-run-id-timedout", Cause: types.DecisionTaskTimedOutCauseReset.Ptr(), }, } mockTask.firstEvent = decisionTaskTimedOutEvent }, expectBaseRunID: "base-run-id-timedout", expectNewRunID: "new-run-id-timedout", expectBaseEventVersion: 2, expectIsReset: true, }, "Case3: DecisionTaskFailed without reset workflow": { mockTaskAffordance: func(mockTask *replicationTaskImpl) { decisionTaskFailedEvent := &types.HistoryEvent{ EventType: types.EventTypeDecisionTaskFailed.Ptr(), DecisionTaskFailedEventAttributes: &types.DecisionTaskFailedEventAttributes{ BaseRunID: "base-run-id", ForkEventVersion: 1, NewRunID: "new-run-id", Cause: types.DecisionTaskFailedCause.Ptr(0), }, } mockTask.firstEvent = decisionTaskFailedEvent }, expectBaseRunID: "base-run-id", expectNewRunID: "new-run-id", expectBaseEventVersion: 1, expectIsReset: false, }, "Case4: DecisionTaskTimedOut without reset workflow": { mockTaskAffordance: func(mockTask *replicationTaskImpl) { decisionTaskTimedOutEvent := &types.HistoryEvent{ EventType: types.EventTypeDecisionTaskTimedOut.Ptr(), DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ BaseRunID: "base-run-id-timedout", ForkEventVersion: 2, NewRunID: "new-run-id-timedout", Cause: types.DecisionTaskTimedOutCause.Ptr(0), }, } mockTask.firstEvent = decisionTaskTimedOutEvent }, expectBaseRunID: "base-run-id-timedout", expectNewRunID: "new-run-id-timedout", expectBaseEventVersion: 2, expectIsReset: false, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // Create a mock task mockTask := &replicationTaskImpl{} // Apply test case mock behavior test.mockTaskAffordance(mockTask) // Call the method under test baseRunID, newRunID, baseEventVersion, isReset := mockTask.getWorkflowResetMetadata() // Assertions assert.Equal(t, test.expectBaseRunID, baseRunID) assert.Equal(t, test.expectNewRunID, newRunID) assert.Equal(t, test.expectBaseEventVersion, baseEventVersion) assert.Equal(t, test.expectIsReset, isReset) }) } } func Test_splitTask(t *testing.T) { tests := map[string]struct { mockTaskAffordance func(mockTask *replicationTaskImpl) taskStartTime time.Time expectedNewRunTask bool expectedError error }{ "Case1: success case with valid newEvents and continuedAsNew event": { mockTaskAffordance: func(mockTask *replicationTaskImpl) { timestamp1 := time.Now().UnixNano() timestamp2 := time.Now().Add(1 * time.Second).UnixNano() mockTask.newEvents = []*types.HistoryEvent{ {ID: 1, Version: 1, Timestamp: ×tamp1}, {ID: 2, Version: 1, Timestamp: ×tamp2}, } mockTask.firstEvent = &types.HistoryEvent{ EventType: types.EventTypeWorkflowExecutionContinuedAsNew.Ptr(), WorkflowExecutionContinuedAsNewEventAttributes: &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: "new-run-id", }, } mockTask.lastEvent = &types.HistoryEvent{ EventType: types.EventTypeWorkflowExecutionContinuedAsNew.Ptr(), ID: 2, Version: 1, WorkflowExecutionContinuedAsNewEventAttributes: &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: "new-run-id", }, } // Initialize execution to avoid nil pointer error mockTask.execution = &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", } // Assign a logger mockTask.logger = log.NewNoop() }, taskStartTime: time.Now(), expectedNewRunTask: true, expectedError: nil, }, "Case2: error when no newEvents": { mockTaskAffordance: func(mockTask *replicationTaskImpl) { mockTask.newEvents = nil // Initialize execution to avoid nil pointer error mockTask.execution = &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", } mockTask.logger = log.NewNoop() // Assign a logger }, taskStartTime: time.Now(), expectedNewRunTask: false, expectedError: ErrNoNewRunHistory, }, "Case3: error when lastEvent is not continuedAsNew": { mockTaskAffordance: func(mockTask *replicationTaskImpl) { timestamp1 := time.Now().UnixNano() mockTask.newEvents = []*types.HistoryEvent{ {ID: 1, Version: 1, Timestamp: ×tamp1}, } mockTask.lastEvent = &types.HistoryEvent{ EventType: types.EventTypeWorkflowExecutionCompleted.Ptr(), // Not continuedAsNew ID: 1, Version: 1, } // Initialize execution to avoid nil pointer error mockTask.execution = &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", } mockTask.logger = log.NewNoop() // Assign a logger }, taskStartTime: time.Now(), expectedNewRunTask: false, expectedError: ErrLastEventIsNotContinueAsNew, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // Create a mock task mockTask := &replicationTaskImpl{} // Apply the mock behavior based on the test case test.mockTaskAffordance(mockTask) // Call the method under test _, newRunTask, err := mockTask.splitTask(test.taskStartTime) // Assertions if test.expectedError != nil { assert.Equal(t, test.expectedError, err) assert.Nil(t, newRunTask) } else { assert.NoError(t, err) assert.NotNil(t, newRunTask) assert.Equal(t, "new-run-id", newRunTask.getRunID()) } }) } } func Test_replicationTaskImpl_Getters(t *testing.T) { // Setup some common values for testing workflowExecution := &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", } historyEvents := []*types.HistoryEvent{ {ID: 1, Version: 1}, {ID: 2, Version: 1}, } newHistoryEvents := []*types.HistoryEvent{ {ID: 3, Version: 1}, {ID: 4, Version: 1}, } logger := log.NewNoop() versionHistory := persistence.NewVersionHistory(nil, []*persistence.VersionHistoryItem{ {EventID: 1, Version: 1}, }) task := &replicationTaskImpl{ domainID: "test-domain-id", execution: workflowExecution, version: 123, sourceCluster: "test-cluster", eventTime: time.Now(), events: historyEvents, newEvents: newHistoryEvents, logger: logger, versionHistory: versionHistory, } tests := map[string]struct { testFunc func() interface{} expectedResult interface{} }{ "getDomainID": { testFunc: func() interface{} { return task.getDomainID() }, expectedResult: "test-domain-id", }, "getWorkflowID": { testFunc: func() interface{} { return task.getWorkflowID() }, expectedResult: "test-workflow-id", }, "getRunID": { testFunc: func() interface{} { return task.getRunID() }, expectedResult: "test-run-id", }, "getEventTime": { testFunc: func() interface{} { return task.getEventTime() }, expectedResult: task.eventTime, }, "getVersion": { testFunc: func() interface{} { return task.getVersion() }, expectedResult: int64(123), }, "getSourceCluster": { testFunc: func() interface{} { return task.getSourceCluster() }, expectedResult: "test-cluster", }, "getEvents": { testFunc: func() interface{} { return task.getEvents() }, expectedResult: historyEvents, }, "getNewEvents": { testFunc: func() interface{} { return task.getNewEvents() }, expectedResult: newHistoryEvents, }, "getLogger": { testFunc: func() interface{} { return task.getLogger() }, expectedResult: logger, }, "getVersionHistory": { testFunc: func() interface{} { return task.getVersionHistory() }, expectedResult: versionHistory, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { assert.Equal(t, test.expectedResult, test.testFunc()) }) } } ================================================ FILE: service/history/ndc/transaction_manager.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination transaction_manager_mock.go package ndc import ( "context" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/reset" "github.com/uber/cadence/service/history/shard" ) // NOTE: terminology // // 1. currentWorkflow means current running / closed workflow // pointed by the current record in DB // // 2. targetWorkflow means the workflow to be replicated // pointed by the replication task // // 3. newWorkflow means the workflow to be replicated, as part of continue as new // pointed by the replication task // // 4. if target workflow and current workflow are the same // then target workflow is set, current workflow is nil // // 5. suppress a workflow means turn a workflow into a zombie // or terminate a workflow // Cases to be handled by this file: // // create path (there will be only current branch) // 1. create as current -> transactionPolicyCreateAsCurrent // 2. create as zombie -> transactionPolicyCreateAsZombie // // create path (there will be only current branch) + suppress current // 1. create as current & suppress current -> transactionPolicySuppressCurrentAndCreateAsCurrent // // update to current branch path // 1. update as current -> transactionPolicyUpdateAsCurrent // 2. update as current & new created as current -> transactionPolicyUpdateAsCurrent // 3. update as zombie -> transactionPolicyUpdateAsZombie // 4. update as zombie & new created as zombie -> transactionPolicyUpdateAsZombie // // backfill to non current branch path // 1. backfill as is // 2. backfill as is & new created as zombie // // conflict resolve path // 1. conflict resolve as current -> transactionPolicyConflictResolveAsCurrent // 2. conflict resolve as zombie -> transactionPolicyConflictResolveAsZombie // // conflict resolve path + suppress current // 1. update from zombie to current & suppress current -> transactionPolicySuppressCurrentAndUpdateAsCurrent // 2. update from zombie to current & new created as current & suppress current -> transactionPolicySuppressCurrentAndUpdateAsCurrent type transactionPolicy int const ( transactionPolicyCreateAsCurrent transactionPolicy = iota transactionPolicyCreateAsZombie transactionPolicySuppressCurrentAndCreateAsCurrent transactionPolicyUpdateAsCurrent transactionPolicyUpdateAsZombie transactionPolicyConflictResolveAsCurrent transactionPolicyConflictResolveAsZombie transactionPolicySuppressCurrentAndUpdateAsCurrent // EventsReapplicationResetWorkflowReason is the reason for reset workflow during reapplication EventsReapplicationResetWorkflowReason = "events-reapplication" ) type ( transactionManager interface { createWorkflow( ctx context.Context, now time.Time, targetWorkflow execution.Workflow, ) error updateWorkflow( ctx context.Context, now time.Time, isWorkflowRebuilt bool, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error backfillWorkflow( ctx context.Context, now time.Time, targetWorkflow execution.Workflow, targetWorkflowEvents *persistence.WorkflowEvents, ) error checkWorkflowExists( ctx context.Context, domainID string, workflowID string, runID string, ) (bool, error) getCurrentWorkflowRunID( ctx context.Context, domainID string, workflowID string, ) (string, error) loadNDCWorkflow( ctx context.Context, domainID string, workflowID string, runID string, ) (execution.Workflow, error) } transactionManagerImpl struct { shard shard.Context executionCache execution.Cache clusterMetadata cluster.Metadata activeClusterManager activecluster.Manager historyV2Manager persistence.HistoryManager serializer persistence.PayloadSerializer metricsClient metrics.Client workflowResetter reset.WorkflowResetter eventsReapplier EventsReapplier logger log.Logger createManager transactionManagerForNewWorkflow updateManager transactionManagerForExistingWorkflow } ) var _ transactionManager = (*transactionManagerImpl)(nil) func newTransactionManager( shard shard.Context, executionCache execution.Cache, eventsReapplier EventsReapplier, logger log.Logger, ) *transactionManagerImpl { transactionManager := &transactionManagerImpl{ shard: shard, executionCache: executionCache, clusterMetadata: shard.GetClusterMetadata(), activeClusterManager: shard.GetActiveClusterManager(), historyV2Manager: shard.GetHistoryManager(), serializer: shard.GetService().GetPayloadSerializer(), metricsClient: shard.GetMetricsClient(), workflowResetter: reset.NewWorkflowResetter( shard, executionCache, logger, ), eventsReapplier: eventsReapplier, logger: logger.WithTags(tag.ComponentHistoryReplicator), createManager: nil, updateManager: nil, } transactionManager.createManager = newTransactionManagerForNewWorkflow(transactionManager, logger) transactionManager.updateManager = newTransactionManagerForExistingWorkflow(transactionManager, logger) return transactionManager } func (r *transactionManagerImpl) createWorkflow( ctx context.Context, now time.Time, targetWorkflow execution.Workflow, ) error { return r.createManager.dispatchForNewWorkflow( ctx, now, targetWorkflow, ) } func (r *transactionManagerImpl) updateWorkflow( ctx context.Context, now time.Time, isWorkflowRebuilt bool, targetWorkflow execution.Workflow, newWorkflow execution.Workflow, ) error { return r.updateManager.dispatchForExistingWorkflow( ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow, ) } func (r *transactionManagerImpl) backfillWorkflow( ctx context.Context, now time.Time, targetWorkflow execution.Workflow, targetWorkflowEvents *persistence.WorkflowEvents, ) (retError error) { defer func() { if rec := recover(); rec != nil { targetWorkflow.GetReleaseFn()(errPanic) panic(rec) } else { targetWorkflow.GetReleaseFn()(retError) } }() if _, err := targetWorkflow.GetContext().PersistNonStartWorkflowBatchEvents( ctx, targetWorkflowEvents, ); err != nil { return err } updateMode, transactionPolicy, err := r.backfillWorkflowEventsReapply( ctx, targetWorkflow, targetWorkflowEvents, ) if err != nil { return err } r.logger.Debugf("backfillWorkflowEventsReapply calling UpdateWorkflowExecutionWithNew for wfID %s, updateMode %v, current policy %v, new policy %v", targetWorkflow.GetMutableState().GetExecutionInfo().WorkflowID, updateMode, transactionPolicy, nil, ) return targetWorkflow.GetContext().UpdateWorkflowExecutionWithNew( ctx, now, updateMode, nil, nil, transactionPolicy, nil, persistence.CreateWorkflowRequestModeReplicated, ) } func (r *transactionManagerImpl) backfillWorkflowEventsReapply( ctx context.Context, targetWorkflow execution.Workflow, targetWorkflowEvents *persistence.WorkflowEvents, ) (persistence.UpdateWorkflowMode, execution.TransactionPolicy, error) { isCurrentWorkflow, err := r.isWorkflowCurrent(ctx, targetWorkflow) if err != nil { return 0, execution.TransactionPolicyActive, err } isWorkflowRunning := targetWorkflow.GetMutableState().IsWorkflowExecutionRunning() activeClusterSelectionPolicy := targetWorkflow.GetMutableState().GetExecutionInfo().ActiveClusterSelectionPolicy activeClusterInfo, err := r.activeClusterManager.GetActiveClusterInfoByClusterAttribute(ctx, targetWorkflow.GetMutableState().GetExecutionInfo().DomainID, activeClusterSelectionPolicy.GetClusterAttribute()) if err != nil { return 0, execution.TransactionPolicyActive, err } targetWorkflowActiveCluster := activeClusterInfo.ActiveClusterName currentCluster := r.clusterMetadata.GetCurrentClusterName() isActiveCluster := targetWorkflowActiveCluster == currentCluster // workflow events reapplication // we need to handle 3 cases // 1. target workflow is self & self being current & active // a. workflow still running -> just reapply // b. workflow closed -> reset current workflow & reapply // 2. anything not case 1 -> find the current & active workflow to reapply // case 1 if isCurrentWorkflow && isActiveCluster { // case 1.a if isWorkflowRunning { if _, err := r.eventsReapplier.ReapplyEvents( ctx, targetWorkflow.GetMutableState(), targetWorkflowEvents.Events, targetWorkflow.GetMutableState().GetExecutionInfo().RunID, ); err != nil { return 0, execution.TransactionPolicyActive, err } return persistence.UpdateWorkflowModeUpdateCurrent, execution.TransactionPolicyActive, nil } // case 1.b // need to reset target workflow (which is also the current workflow) // to accept events to be reapplied baseMutableState := targetWorkflow.GetMutableState() domainID := baseMutableState.GetExecutionInfo().DomainID workflowID := baseMutableState.GetExecutionInfo().WorkflowID baseRunID := baseMutableState.GetExecutionInfo().RunID resetRunID := uuid.New() baseRebuildLastEventID := baseMutableState.GetPreviousStartedEventID() // TODO when https://github.com/uber/cadence/issues/2420 is finished, remove this block, // since cannot reapply event to a finished workflow which had no decisions started if baseRebuildLastEventID == constants.EmptyEventID { r.logger.Warn("cannot reapply event to a finished workflow", tag.WorkflowDomainID(domainID), tag.WorkflowID(workflowID), ) r.metricsClient.IncCounter(metrics.HistoryReapplyEventsScope, metrics.EventReapplySkippedCount) return persistence.UpdateWorkflowModeBypassCurrent, execution.TransactionPolicyPassive, nil } baseVersionHistories := baseMutableState.GetVersionHistories() if baseVersionHistories == nil { return 0, execution.TransactionPolicyActive, execution.ErrMissingVersionHistories } baseCurrentVersionHistory, err := baseVersionHistories.GetCurrentVersionHistory() if err != nil { return 0, execution.TransactionPolicyActive, err } baseRebuildLastEventVersion, err := baseCurrentVersionHistory.GetEventVersion(baseRebuildLastEventID) if err != nil { return 0, execution.TransactionPolicyActive, err } baseCurrentBranchToken := baseCurrentVersionHistory.GetBranchToken() baseNextEventID := baseMutableState.GetNextEventID() if err = r.workflowResetter.ResetWorkflow( ctx, domainID, workflowID, baseRunID, baseCurrentBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, baseNextEventID, resetRunID, uuid.New(), targetWorkflow, EventsReapplicationResetWorkflowReason, targetWorkflowEvents.Events, false, ); err != nil { return 0, execution.TransactionPolicyActive, err } // after the reset of target workflow (current workflow) with additional events to be reapplied // target workflow is no longer the current workflow return persistence.UpdateWorkflowModeBypassCurrent, execution.TransactionPolicyPassive, nil } // case 2 // find the current & active workflow to reapply if err := targetWorkflow.GetContext().ReapplyEvents( []*persistence.WorkflowEvents{targetWorkflowEvents}, ); err != nil { return 0, execution.TransactionPolicyActive, err } if isCurrentWorkflow { return persistence.UpdateWorkflowModeUpdateCurrent, execution.TransactionPolicyPassive, nil } return persistence.UpdateWorkflowModeBypassCurrent, execution.TransactionPolicyPassive, nil } func (r *transactionManagerImpl) checkWorkflowExists( ctx context.Context, domainID string, workflowID string, runID string, ) (bool, error) { domainName, errorDomainName := r.shard.GetDomainCache().GetDomainName(domainID) if errorDomainName != nil { return false, errorDomainName } _, err := r.shard.GetWorkflowExecution( ctx, &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, DomainName: domainName, }, ) switch err.(type) { case nil: return true, nil case *types.EntityNotExistsError: return false, nil default: return false, err } } func (r *transactionManagerImpl) getCurrentWorkflowRunID( ctx context.Context, domainID string, workflowID string, ) (string, error) { domainName, errorDomainName := r.shard.GetDomainCache().GetDomainName(domainID) if errorDomainName != nil { return "", errorDomainName } resp, err := r.shard.GetExecutionManager().GetCurrentExecution( ctx, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, DomainName: domainName, }, ) switch err.(type) { case nil: return resp.RunID, nil case *types.EntityNotExistsError: return "", nil default: return "", err } } func (r *transactionManagerImpl) loadNDCWorkflow( ctx context.Context, domainID string, workflowID string, runID string, ) (execution.Workflow, error) { // we need to check the current workflow execution context, release, err := r.executionCache.GetOrCreateWorkflowExecution( ctx, domainID, types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, ) if err != nil { return nil, err } msBuilder, err := context.LoadWorkflowExecution(ctx) if err != nil { // no matter what error happen, we need to retry release(err) return nil, err } return execution.NewWorkflow(ctx, r.clusterMetadata, context, msBuilder, release, r.logger), nil } func (r *transactionManagerImpl) isWorkflowCurrent( ctx context.Context, targetWorkflow execution.Workflow, ) (bool, error) { // since we are not rebuilding the mutable state (when doing backfill) then we // can trust the result from IsCurrentWorkflowGuaranteed if targetWorkflow.GetMutableState().IsCurrentWorkflowGuaranteed() { return true, nil } // target workflow is not guaranteed to be current workflow, do additional check executionInfo := targetWorkflow.GetMutableState().GetExecutionInfo() domainID := executionInfo.DomainID workflowID := executionInfo.WorkflowID runID := executionInfo.RunID currentRunID, err := r.getCurrentWorkflowRunID( ctx, domainID, workflowID, ) if err != nil { return false, err } return currentRunID == runID, nil } ================================================ FILE: service/history/ndc/transaction_manager_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: transaction_manager.go // // Generated by this command: // // mockgen -package ndc -source transaction_manager.go -destination transaction_manager_mock.go // // Package ndc is a generated GoMock package. package ndc import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" execution "github.com/uber/cadence/service/history/execution" ) // MocktransactionManager is a mock of transactionManager interface. type MocktransactionManager struct { ctrl *gomock.Controller recorder *MocktransactionManagerMockRecorder isgomock struct{} } // MocktransactionManagerMockRecorder is the mock recorder for MocktransactionManager. type MocktransactionManagerMockRecorder struct { mock *MocktransactionManager } // NewMocktransactionManager creates a new mock instance. func NewMocktransactionManager(ctrl *gomock.Controller) *MocktransactionManager { mock := &MocktransactionManager{ctrl: ctrl} mock.recorder = &MocktransactionManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MocktransactionManager) EXPECT() *MocktransactionManagerMockRecorder { return m.recorder } // backfillWorkflow mocks base method. func (m *MocktransactionManager) backfillWorkflow(ctx context.Context, now time.Time, targetWorkflow execution.Workflow, targetWorkflowEvents *persistence.WorkflowEvents) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "backfillWorkflow", ctx, now, targetWorkflow, targetWorkflowEvents) ret0, _ := ret[0].(error) return ret0 } // backfillWorkflow indicates an expected call of backfillWorkflow. func (mr *MocktransactionManagerMockRecorder) backfillWorkflow(ctx, now, targetWorkflow, targetWorkflowEvents any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "backfillWorkflow", reflect.TypeOf((*MocktransactionManager)(nil).backfillWorkflow), ctx, now, targetWorkflow, targetWorkflowEvents) } // checkWorkflowExists mocks base method. func (m *MocktransactionManager) checkWorkflowExists(ctx context.Context, domainID, workflowID, runID string) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "checkWorkflowExists", ctx, domainID, workflowID, runID) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // checkWorkflowExists indicates an expected call of checkWorkflowExists. func (mr *MocktransactionManagerMockRecorder) checkWorkflowExists(ctx, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "checkWorkflowExists", reflect.TypeOf((*MocktransactionManager)(nil).checkWorkflowExists), ctx, domainID, workflowID, runID) } // createWorkflow mocks base method. func (m *MocktransactionManager) createWorkflow(ctx context.Context, now time.Time, targetWorkflow execution.Workflow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "createWorkflow", ctx, now, targetWorkflow) ret0, _ := ret[0].(error) return ret0 } // createWorkflow indicates an expected call of createWorkflow. func (mr *MocktransactionManagerMockRecorder) createWorkflow(ctx, now, targetWorkflow any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "createWorkflow", reflect.TypeOf((*MocktransactionManager)(nil).createWorkflow), ctx, now, targetWorkflow) } // getCurrentWorkflowRunID mocks base method. func (m *MocktransactionManager) getCurrentWorkflowRunID(ctx context.Context, domainID, workflowID string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getCurrentWorkflowRunID", ctx, domainID, workflowID) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // getCurrentWorkflowRunID indicates an expected call of getCurrentWorkflowRunID. func (mr *MocktransactionManagerMockRecorder) getCurrentWorkflowRunID(ctx, domainID, workflowID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getCurrentWorkflowRunID", reflect.TypeOf((*MocktransactionManager)(nil).getCurrentWorkflowRunID), ctx, domainID, workflowID) } // loadNDCWorkflow mocks base method. func (m *MocktransactionManager) loadNDCWorkflow(ctx context.Context, domainID, workflowID, runID string) (execution.Workflow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "loadNDCWorkflow", ctx, domainID, workflowID, runID) ret0, _ := ret[0].(execution.Workflow) ret1, _ := ret[1].(error) return ret0, ret1 } // loadNDCWorkflow indicates an expected call of loadNDCWorkflow. func (mr *MocktransactionManagerMockRecorder) loadNDCWorkflow(ctx, domainID, workflowID, runID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "loadNDCWorkflow", reflect.TypeOf((*MocktransactionManager)(nil).loadNDCWorkflow), ctx, domainID, workflowID, runID) } // updateWorkflow mocks base method. func (m *MocktransactionManager) updateWorkflow(ctx context.Context, now time.Time, isWorkflowRebuilt bool, targetWorkflow, newWorkflow execution.Workflow) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "updateWorkflow", ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) ret0, _ := ret[0].(error) return ret0 } // updateWorkflow indicates an expected call of updateWorkflow. func (mr *MocktransactionManagerMockRecorder) updateWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "updateWorkflow", reflect.TypeOf((*MocktransactionManager)(nil).updateWorkflow), ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) } ================================================ FILE: service/history/ndc/transaction_manager_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( ctx "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/reset" "github.com/uber/cadence/service/history/shard" ) type ( transactionManagerSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockCreateManager *MocktransactionManagerForNewWorkflow mockUpdateManager *MocktransactionManagerForExistingWorkflow mockEventsReapplier *MockEventsReapplier mockWorkflowResetter *reset.MockWorkflowResetter mockExecutionManager *mocks.ExecutionManager logger log.Logger domainEntry *cache.DomainCacheEntry transactionManager *transactionManagerImpl } ) func TestTransactionManagerSuite(t *testing.T) { s := new(transactionManagerSuite) suite.Run(t, s) } func (s *transactionManagerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockCreateManager = NewMocktransactionManagerForNewWorkflow(s.controller) s.mockUpdateManager = NewMocktransactionManagerForExistingWorkflow(s.controller) s.mockEventsReapplier = NewMockEventsReapplier(s.controller) s.mockWorkflowResetter = reset.NewMockWorkflowResetter(s.controller) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.mockExecutionManager = s.mockShard.Resource.ExecutionMgr s.logger = s.mockShard.GetLogger() s.domainEntry = constants.TestGlobalDomainEntry // Setup mock for GetActiveClusterInfoByClusterAttribute to avoid errors mockActiveClusterManager := s.mockShard.Resource.ActiveClusterMgr mockActiveClusterManager.EXPECT().GetActiveClusterInfoByClusterAttribute( gomock.Any(), gomock.Any(), gomock.Any(), ).Return(&types.ActiveClusterInfo{ ActiveClusterName: s.mockShard.GetClusterMetadata().GetCurrentClusterName(), FailoverVersion: int64(0), }, nil).AnyTimes() s.transactionManager = newTransactionManager(s.mockShard, execution.NewCache(s.mockShard), s.mockEventsReapplier, s.logger) s.transactionManager.createManager = s.mockCreateManager s.transactionManager.updateManager = s.mockUpdateManager s.transactionManager.workflowResetter = s.mockWorkflowResetter } func (s *transactionManagerSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *transactionManagerSuite) TestCreateWorkflow() { ctx := ctx.Background() now := time.Now() targetWorkflow := execution.NewMockWorkflow(s.controller) s.mockCreateManager.EXPECT().dispatchForNewWorkflow( ctx, now, targetWorkflow, ).Return(nil).Times(1) err := s.transactionManager.createWorkflow(ctx, now, targetWorkflow) s.NoError(err) } func (s *transactionManagerSuite) TestUpdateWorkflow() { ctx := ctx.Background() now := time.Now() isWorkflowRebuilt := true targetWorkflow := execution.NewMockWorkflow(s.controller) newWorkflow := execution.NewMockWorkflow(s.controller) s.mockUpdateManager.EXPECT().dispatchForExistingWorkflow( ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow, ).Return(nil).Times(1) err := s.transactionManager.updateWorkflow(ctx, now, isWorkflowRebuilt, targetWorkflow, newWorkflow) s.NoError(err) } func (s *transactionManagerSuite) TestBackfillWorkflow_CurrentWorkflow_Active_Open() { ctx := ctx.Background() now := time.Now() releaseCalled := false runID := uuid.New() workflow := execution.NewMockWorkflow(s.controller) context := execution.NewMockContext(s.controller) mutableState := execution.NewMockMutableState(s.controller) var releaseFn execution.ReleaseFunc = func(error) { releaseCalled = true } workflowEvents := &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{{ID: 1}}, } workflow.EXPECT().GetContext().Return(context).AnyTimes() workflow.EXPECT().GetMutableState().Return(mutableState).AnyTimes() workflow.EXPECT().GetReleaseFn().Return(releaseFn).AnyTimes() s.mockEventsReapplier.EXPECT().ReapplyEvents(ctx, mutableState, workflowEvents.Events, runID).Return(workflowEvents.Events, nil).Times(1) mutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(true).AnyTimes() mutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() mutableState.EXPECT().GetDomainEntry().Return(s.domainEntry).AnyTimes() mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{RunID: runID}).AnyTimes() context.EXPECT().PersistNonStartWorkflowBatchEvents(gomock.Any(), workflowEvents).Return(events.PersistedBlob{}, nil).Times(1) context.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeUpdateCurrent, nil, nil, execution.TransactionPolicyActive, (*execution.TransactionPolicy)(nil), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.transactionManager.backfillWorkflow(ctx, now, workflow, workflowEvents) s.NoError(err) s.True(releaseCalled) } func (s *transactionManagerSuite) TestBackfillWorkflow_CurrentWorkflow_Active_Closed() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" runID := "some random run ID" domainName := "some random domainName" lastDecisionTaskStartedEventID := int64(9999) nextEventID := lastDecisionTaskStartedEventID * 2 lastDecisionTaskStartedVersion := s.domainEntry.GetFailoverVersion() versionHistory := persistence.NewVersionHistory([]byte("branch token"), []*persistence.VersionHistoryItem{ {EventID: lastDecisionTaskStartedEventID, Version: lastDecisionTaskStartedVersion}, }) histories := persistence.NewVersionHistories(versionHistory) releaseCalled := false workflow := execution.NewMockWorkflow(s.controller) context := execution.NewMockContext(s.controller) mutableState := execution.NewMockMutableState(s.controller) var releaseFn execution.ReleaseFunc = func(error) { releaseCalled = true } workflowEvents := &persistence.WorkflowEvents{} workflow.EXPECT().GetContext().Return(context).AnyTimes() workflow.EXPECT().GetMutableState().Return(mutableState).AnyTimes() workflow.EXPECT().GetReleaseFn().Return(releaseFn).AnyTimes() mutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(false).AnyTimes() mutableState.EXPECT().IsWorkflowExecutionRunning().Return(false).AnyTimes() mutableState.EXPECT().GetDomainEntry().Return(s.domainEntry).AnyTimes() mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).AnyTimes() mutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() mutableState.EXPECT().GetPreviousStartedEventID().Return(lastDecisionTaskStartedEventID).Times(1) mutableState.EXPECT().GetVersionHistories().Return(histories).Times(1) s.mockWorkflowResetter.EXPECT().ResetWorkflow( ctx, domainID, workflowID, runID, versionHistory.GetBranchToken(), lastDecisionTaskStartedEventID, lastDecisionTaskStartedVersion, nextEventID, gomock.Any(), gomock.Any(), workflow, EventsReapplicationResetWorkflowReason, workflowEvents.Events, false, ).Return(nil).Times(1) s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() s.mockExecutionManager.On("GetCurrentExecution", mock.Anything, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, DomainName: domainName, }).Return(&persistence.GetCurrentExecutionResponse{RunID: runID}, nil).Once() context.EXPECT().PersistNonStartWorkflowBatchEvents(gomock.Any(), workflowEvents).Return(events.PersistedBlob{}, nil).Times(1) context.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeBypassCurrent, nil, nil, execution.TransactionPolicyPassive, (*execution.TransactionPolicy)(nil), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.transactionManager.backfillWorkflow(ctx, now, workflow, workflowEvents) s.NoError(err) s.True(releaseCalled) } func (s *transactionManagerSuite) TestBackfillWorkflow_CurrentWorkflow_Passive_Open() { ctx := ctx.Background() now := time.Now() releaseCalled := false workflow := execution.NewMockWorkflow(s.controller) context := execution.NewMockContext(s.controller) mutableState := execution.NewMockMutableState(s.controller) var releaseFn execution.ReleaseFunc = func(error) { releaseCalled = true } workflowEvents := &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{{ID: 1}}, } workflow.EXPECT().GetContext().Return(context).AnyTimes() workflow.EXPECT().GetMutableState().Return(mutableState).AnyTimes() workflow.EXPECT().GetReleaseFn().Return(releaseFn).AnyTimes() s.transactionManager.clusterMetadata = cluster.TestPassiveClusterMetadata mutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(true).AnyTimes() mutableState.EXPECT().IsWorkflowExecutionRunning().Return(true).AnyTimes() mutableState.EXPECT().GetDomainEntry().Return(s.domainEntry).AnyTimes() mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() context.EXPECT().ReapplyEvents([]*persistence.WorkflowEvents{workflowEvents}).Times(1) context.EXPECT().PersistNonStartWorkflowBatchEvents(gomock.Any(), workflowEvents).Return(events.PersistedBlob{}, nil).Times(1) context.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeUpdateCurrent, nil, nil, execution.TransactionPolicyPassive, (*execution.TransactionPolicy)(nil), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.transactionManager.backfillWorkflow(ctx, now, workflow, workflowEvents) s.NoError(err) s.True(releaseCalled) } func (s *transactionManagerSuite) TestBackfillWorkflow_CurrentWorkflow_Passive_Closed() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" runID := "some random run ID" domainName := "some random domainName" releaseCalled := false workflow := execution.NewMockWorkflow(s.controller) context := execution.NewMockContext(s.controller) mutableState := execution.NewMockMutableState(s.controller) var releaseFn execution.ReleaseFunc = func(error) { releaseCalled = true } workflowEvents := &persistence.WorkflowEvents{} workflow.EXPECT().GetContext().Return(context).AnyTimes() workflow.EXPECT().GetMutableState().Return(mutableState).AnyTimes() workflow.EXPECT().GetReleaseFn().Return(releaseFn).AnyTimes() s.transactionManager.clusterMetadata = cluster.TestPassiveClusterMetadata mutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(false).AnyTimes() mutableState.EXPECT().IsWorkflowExecutionRunning().Return(false).AnyTimes() mutableState.EXPECT().GetDomainEntry().Return(s.domainEntry).AnyTimes() mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).AnyTimes() s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() s.mockExecutionManager.On("GetCurrentExecution", mock.Anything, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, DomainName: domainName, }).Return(&persistence.GetCurrentExecutionResponse{RunID: runID}, nil).Once() context.EXPECT().ReapplyEvents([]*persistence.WorkflowEvents{workflowEvents}).Times(1) context.EXPECT().PersistNonStartWorkflowBatchEvents(gomock.Any(), workflowEvents).Return(events.PersistedBlob{}, nil).Times(1) context.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeUpdateCurrent, nil, nil, execution.TransactionPolicyPassive, (*execution.TransactionPolicy)(nil), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.transactionManager.backfillWorkflow(ctx, now, workflow, workflowEvents) s.NoError(err) s.True(releaseCalled) } func (s *transactionManagerSuite) TestBackfillWorkflow_NotCurrentWorkflow_Active() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" runID := "some random run ID" currentRunID := "other random run ID" domainName := "some random domainName" releaseCalled := false workflow := execution.NewMockWorkflow(s.controller) context := execution.NewMockContext(s.controller) mutableState := execution.NewMockMutableState(s.controller) var releaseFn execution.ReleaseFunc = func(error) { releaseCalled = true } workflowEvents := &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{{ EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), }}, DomainID: domainID, WorkflowID: workflowID, } workflow.EXPECT().GetContext().Return(context).AnyTimes() workflow.EXPECT().GetMutableState().Return(mutableState).AnyTimes() workflow.EXPECT().GetReleaseFn().Return(releaseFn).AnyTimes() mutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(false).AnyTimes() mutableState.EXPECT().IsWorkflowExecutionRunning().Return(false).AnyTimes() mutableState.EXPECT().GetDomainEntry().Return(s.domainEntry).AnyTimes() mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).AnyTimes() s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() s.mockExecutionManager.On("GetCurrentExecution", mock.Anything, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, DomainName: domainName, }).Return(&persistence.GetCurrentExecutionResponse{RunID: currentRunID}, nil).Once() context.EXPECT().ReapplyEvents([]*persistence.WorkflowEvents{workflowEvents}).Times(1) context.EXPECT().PersistNonStartWorkflowBatchEvents(gomock.Any(), workflowEvents).Return(events.PersistedBlob{}, nil).Times(1) context.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeBypassCurrent, nil, nil, execution.TransactionPolicyPassive, (*execution.TransactionPolicy)(nil), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.transactionManager.backfillWorkflow(ctx, now, workflow, workflowEvents) s.NoError(err) s.True(releaseCalled) } func (s *transactionManagerSuite) TestBackfillWorkflow_NotCurrentWorkflow_Passive() { ctx := ctx.Background() now := time.Now() domainID := "some random domain ID" workflowID := "some random workflow ID" runID := "some random run ID" currentRunID := "other random run ID" domainName := "some random domainName" releaseCalled := false workflow := execution.NewMockWorkflow(s.controller) context := execution.NewMockContext(s.controller) mutableState := execution.NewMockMutableState(s.controller) var releaseFn execution.ReleaseFunc = func(error) { releaseCalled = true } workflowEvents := &persistence.WorkflowEvents{ Events: []*types.HistoryEvent{{ EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), }}, DomainID: domainID, WorkflowID: workflowID, } workflow.EXPECT().GetContext().Return(context).AnyTimes() workflow.EXPECT().GetMutableState().Return(mutableState).AnyTimes() workflow.EXPECT().GetReleaseFn().Return(releaseFn).AnyTimes() mutableState.EXPECT().IsCurrentWorkflowGuaranteed().Return(false).AnyTimes() mutableState.EXPECT().IsWorkflowExecutionRunning().Return(false).AnyTimes() mutableState.EXPECT().GetDomainEntry().Return(s.domainEntry).AnyTimes() mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }).AnyTimes() s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() s.mockExecutionManager.On("GetCurrentExecution", mock.Anything, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, DomainName: domainName, }).Return(&persistence.GetCurrentExecutionResponse{RunID: currentRunID}, nil).Once() context.EXPECT().ReapplyEvents([]*persistence.WorkflowEvents{workflowEvents}).Times(1) context.EXPECT().PersistNonStartWorkflowBatchEvents(gomock.Any(), workflowEvents).Return(events.PersistedBlob{}, nil).Times(1) context.EXPECT().UpdateWorkflowExecutionWithNew( gomock.Any(), now, persistence.UpdateWorkflowModeBypassCurrent, nil, nil, execution.TransactionPolicyPassive, (*execution.TransactionPolicy)(nil), persistence.CreateWorkflowRequestModeReplicated, ).Return(nil).Times(1) err := s.transactionManager.backfillWorkflow(ctx, now, workflow, workflowEvents) s.NoError(err) s.True(releaseCalled) } func (s *transactionManagerSuite) TestCheckWorkflowExists_DoesNotExists() { ctx := ctx.Background() domainID := "some random domain ID" workflowID := "some random workflow ID" runID := "some random run ID" domainName := "some random domainName" s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil) s.mockExecutionManager.On("GetWorkflowExecution", mock.Anything, &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, DomainName: domainName, RangeID: 1, }).Return(nil, &types.EntityNotExistsError{}).Once() exists, err := s.transactionManager.checkWorkflowExists(ctx, domainID, workflowID, runID) s.NoError(err) s.False(exists) } func (s *transactionManagerSuite) TestCheckWorkflowExists_DoesExists() { ctx := ctx.Background() domainID := "some random domain ID" workflowID := "some random workflow ID" runID := "some random run ID" domainName := "some random Domain Name" s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() s.mockExecutionManager.On("GetWorkflowExecution", mock.Anything, &persistence.GetWorkflowExecutionRequest{ DomainID: domainID, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, DomainName: domainName, RangeID: 1, }).Return(&persistence.GetWorkflowExecutionResponse{}, nil).Once() exists, err := s.transactionManager.checkWorkflowExists(ctx, domainID, workflowID, runID) s.NoError(err) s.True(exists) } func (s *transactionManagerSuite) TestGetWorkflowCurrentRunID_Missing() { ctx := ctx.Background() domainID := "some random domain ID" workflowID := "some random workflow ID" domainName := "some random domainName" s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() s.mockExecutionManager.On("GetCurrentExecution", mock.Anything, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, DomainName: domainName, }).Return(nil, &types.EntityNotExistsError{}).Once() currentRunID, err := s.transactionManager.getCurrentWorkflowRunID(ctx, domainID, workflowID) s.NoError(err) s.Equal("", currentRunID) } func (s *transactionManagerSuite) TestGetWorkflowCurrentRunID_Exists() { ctx := ctx.Background() domainID := "some random domain ID" workflowID := "some random workflow ID" runID := "some random run ID" domainName := "some random domainName" s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() s.mockExecutionManager.On("GetCurrentExecution", mock.Anything, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, DomainName: domainName, }).Return(&persistence.GetCurrentExecutionResponse{RunID: runID}, nil).Once() currentRunID, err := s.transactionManager.getCurrentWorkflowRunID(ctx, domainID, workflowID) s.NoError(err) s.Equal(runID, currentRunID) } ================================================ FILE: service/history/ndc/workflow_resetter.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination workflow_resetter_mock.go package ndc import ( "context" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) const ( resendOnResetWorkflowMessage = "Resend events due to reset workflow" ) type ( // WorkflowResetter handles workflow reset for NDC WorkflowResetter interface { ResetWorkflow( ctx context.Context, now time.Time, baseLastEventID int64, baseLastEventVersion int64, incomingFirstEventID int64, incomingFirstEventVersion int64, ) (execution.MutableState, error) } workflowResetterImpl struct { shard shard.Context transactionManager transactionManager historyV2Manager persistence.HistoryManager stateRebuilder execution.StateRebuilder domainID string workflowID string baseRunID string newContext execution.Context newRunID string logger log.Logger } ) var _ WorkflowResetter = (*workflowResetterImpl)(nil) // NewWorkflowResetter creates workflow resetter func NewWorkflowResetter( shard shard.Context, transactionManager transactionManager, domainID string, workflowID string, baseRunID string, newContext execution.Context, newRunID string, logger log.Logger, ) WorkflowResetter { return &workflowResetterImpl{ shard: shard, transactionManager: transactionManager, historyV2Manager: shard.GetHistoryManager(), stateRebuilder: execution.NewStateRebuilder(shard, logger), domainID: domainID, workflowID: workflowID, baseRunID: baseRunID, newContext: newContext, newRunID: newRunID, logger: logger, } } func (r *workflowResetterImpl) ResetWorkflow( ctx context.Context, now time.Time, baseLastEventID int64, baseLastEventVersion int64, incomingFirstEventID int64, incomingFirstEventVersion int64, ) (execution.MutableState, error) { baseBranchToken, err := r.getBaseBranchToken( ctx, baseLastEventID, baseLastEventVersion, incomingFirstEventID, incomingFirstEventVersion, ) if err != nil { return nil, err } resetBranchTokenFn := func() ([]byte, error) { resetBranchToken, err := r.getResetBranchToken(ctx, baseBranchToken, baseLastEventID) return resetBranchToken, err } requestID := uuid.New() rebuildMutableState, rebuiltHistorySize, err := r.stateRebuilder.Rebuild( ctx, now, definition.NewWorkflowIdentifier( r.domainID, r.workflowID, r.baseRunID, ), baseBranchToken, baseLastEventID, baseLastEventVersion, definition.NewWorkflowIdentifier( r.domainID, r.workflowID, r.newRunID, ), resetBranchTokenFn, requestID, ) if err != nil { return nil, err } r.newContext.Clear() r.newContext.SetHistorySize(rebuiltHistorySize) return rebuildMutableState, nil } func (r *workflowResetterImpl) getBaseBranchToken( ctx context.Context, baseLastEventID int64, baseLastEventVersion int64, incomingFirstEventID int64, incomingFirstEventVersion int64, ) (baseBranchToken []byte, retError error) { baseWorkflow, err := r.transactionManager.loadNDCWorkflow( ctx, r.domainID, r.workflowID, r.baseRunID, ) if err != nil { return nil, err } defer func() { baseWorkflow.GetReleaseFn()(retError) }() mutableState := baseWorkflow.GetMutableState() branchToken, err := mutableState.GetCurrentBranchToken() if err != nil { return nil, err } baseVersionHistories := mutableState.GetVersionHistories() if baseVersionHistories != nil { _, baseVersionHistory, err := baseVersionHistories.FindFirstVersionHistoryByItem( persistence.NewVersionHistoryItem(baseLastEventID, baseLastEventVersion), ) if err != nil { // the base event and incoming event are from different branch // only re-replicate the gap on the incoming branch // the base branch event will eventually arrived return nil, newNDCRetryTaskErrorWithHint( resendOnResetWorkflowMessage, r.domainID, r.workflowID, r.newRunID, nil, nil, common.Int64Ptr(incomingFirstEventID), common.Int64Ptr(incomingFirstEventVersion), ) } branchToken = baseVersionHistory.GetBranchToken() } return branchToken, nil } func (r *workflowResetterImpl) getResetBranchToken( ctx context.Context, baseBranchToken []byte, baseLastEventID int64, ) ([]byte, error) { // fork a new history branch shardID := r.shard.GetShardID() domainName, err := r.shard.GetDomainCache().GetDomainName(r.domainID) if err != nil { return nil, err } resp, err := r.historyV2Manager.ForkHistoryBranch( ctx, &persistence.ForkHistoryBranchRequest{ ForkBranchToken: baseBranchToken, ForkNodeID: baseLastEventID + 1, Info: persistence.BuildHistoryGarbageCleanupInfo(r.domainID, r.workflowID, r.newRunID), ShardID: common.IntPtr(shardID), DomainName: domainName, }, ) if err != nil { return nil, err } return resp.NewBranchToken, nil } ================================================ FILE: service/history/ndc/workflow_resetter_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: workflow_resetter.go // // Generated by this command: // // mockgen -package ndc -source workflow_resetter.go -destination workflow_resetter_mock.go // // Package ndc is a generated GoMock package. package ndc import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" execution "github.com/uber/cadence/service/history/execution" ) // MockWorkflowResetter is a mock of WorkflowResetter interface. type MockWorkflowResetter struct { ctrl *gomock.Controller recorder *MockWorkflowResetterMockRecorder isgomock struct{} } // MockWorkflowResetterMockRecorder is the mock recorder for MockWorkflowResetter. type MockWorkflowResetterMockRecorder struct { mock *MockWorkflowResetter } // NewMockWorkflowResetter creates a new mock instance. func NewMockWorkflowResetter(ctrl *gomock.Controller) *MockWorkflowResetter { mock := &MockWorkflowResetter{ctrl: ctrl} mock.recorder = &MockWorkflowResetterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWorkflowResetter) EXPECT() *MockWorkflowResetterMockRecorder { return m.recorder } // ResetWorkflow mocks base method. func (m *MockWorkflowResetter) ResetWorkflow(ctx context.Context, now time.Time, baseLastEventID, baseLastEventVersion, incomingFirstEventID, incomingFirstEventVersion int64) (execution.MutableState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetWorkflow", ctx, now, baseLastEventID, baseLastEventVersion, incomingFirstEventID, incomingFirstEventVersion) ret0, _ := ret[0].(execution.MutableState) ret1, _ := ret[1].(error) return ret0, ret1 } // ResetWorkflow indicates an expected call of ResetWorkflow. func (mr *MockWorkflowResetterMockRecorder) ResetWorkflow(ctx, now, baseLastEventID, baseLastEventVersion, incomingFirstEventID, incomingFirstEventVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetWorkflow", reflect.TypeOf((*MockWorkflowResetter)(nil).ResetWorkflow), ctx, now, baseLastEventID, baseLastEventVersion, incomingFirstEventID, incomingFirstEventVersion) } ================================================ FILE: service/history/ndc/workflow_resetter_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package ndc import ( ctx "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "golang.org/x/net/context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) type ( workflowResetterSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockBaseMutableState *execution.MockMutableState mockRebuiltMutableState *execution.MockMutableState mockTransactionMgr *MocktransactionManager mockStateRebuilder *execution.MockStateRebuilder logger log.Logger mockHistoryV2Mgr *mocks.HistoryV2Manager domainID string domainName string workflowID string baseRunID string newContext execution.Context newRunID string workflowResetter *workflowResetterImpl } ) func TestWorkflowResetterSuite(t *testing.T) { s := new(workflowResetterSuite) suite.Run(t, s) } func (s *workflowResetterSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockBaseMutableState = execution.NewMockMutableState(s.controller) s.mockRebuiltMutableState = execution.NewMockMutableState(s.controller) s.mockTransactionMgr = NewMocktransactionManager(s.controller) s.mockStateRebuilder = execution.NewMockStateRebuilder(s.controller) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.mockHistoryV2Mgr = s.mockShard.Resource.HistoryMgr s.logger = s.mockShard.GetLogger() s.domainID = uuid.New() s.domainName = "some random domain name" s.workflowID = "some random workflow ID" s.baseRunID = uuid.New() s.newContext = execution.NewContext( s.domainID, types.WorkflowExecution{ WorkflowID: s.workflowID, RunID: s.newRunID, }, s.mockShard, nil, s.logger, ) s.newRunID = uuid.New() s.workflowResetter = NewWorkflowResetter( s.mockShard, s.mockTransactionMgr, s.domainID, s.workflowID, s.baseRunID, s.newContext, s.newRunID, s.logger, ).(*workflowResetterImpl) s.workflowResetter.stateRebuilder = s.mockStateRebuilder } func (s *workflowResetterSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *workflowResetterSuite) TestResetWorkflow_NoError() { ctx := ctx.Background() now := time.Now() branchToken := []byte("some random branch token") lastEventID := int64(500) version := int64(123) versionHistory := persistence.NewVersionHistory( branchToken, []*persistence.VersionHistoryItem{persistence.NewVersionHistoryItem(lastEventID, version)}, ) versionHistories := persistence.NewVersionHistories(versionHistory) baseEventID := lastEventID - 100 baseVersion := version incomingFirstEventID := baseEventID + 12 incomingVersion := baseVersion + 3 rebuiltHistorySize := int64(9999) resetBranchToken := []byte("other random branch token") domainName := "test-domainName" s.mockBaseMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockBaseMutableState.EXPECT().GetCurrentBranchToken().Return(branchToken, nil).AnyTimes() mockBaseWorkflowReleaseFnCalled := false mockBaseWorkflowReleaseFn := func(err error) { mockBaseWorkflowReleaseFnCalled = true } mockBaseWorkflow := execution.NewMockWorkflow(s.controller) mockBaseWorkflow.EXPECT().GetMutableState().Return(s.mockBaseMutableState).AnyTimes() mockBaseWorkflow.EXPECT().GetReleaseFn().Return(mockBaseWorkflowReleaseFn).Times(1) s.mockTransactionMgr.EXPECT().loadNDCWorkflow( ctx, s.domainID, s.workflowID, s.baseRunID, ).Return(mockBaseWorkflow, nil).Times(1) s.mockStateRebuilder.EXPECT().Rebuild( ctx, now, definition.NewWorkflowIdentifier( s.domainID, s.workflowID, s.baseRunID, ), branchToken, baseEventID, baseVersion, definition.NewWorkflowIdentifier( s.domainID, s.workflowID, s.newRunID, ), gomock.Any(), gomock.Any(), ).DoAndReturn(func(ctx context.Context, now time.Time, baseWorkflowIdentifier definition.WorkflowIdentifier, baseBranchToken []byte, baseRebuildLastEventID int64, baseRebuildLastEventVersion int64, targetWorkflowIdentifier definition.WorkflowIdentifier, targetBranchFn func() ([]byte, error), requestID string) (execution.MutableState, int64, error) { targetBranchToken, err := targetBranchFn() s.NoError(err) s.Equal(resetBranchToken, targetBranchToken) return s.mockRebuiltMutableState, rebuiltHistorySize, nil }).Times(1) s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() s.mockHistoryV2Mgr.On("ForkHistoryBranch", mock.Anything, &persistence.ForkHistoryBranchRequest{ ForkBranchToken: branchToken, ForkNodeID: baseEventID + 1, Info: persistence.BuildHistoryGarbageCleanupInfo(s.domainID, s.workflowID, s.newRunID), ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }).Return(&persistence.ForkHistoryBranchResponse{NewBranchToken: resetBranchToken}, nil).Times(1) rebuiltMutableState, err := s.workflowResetter.ResetWorkflow( ctx, now, baseEventID, baseVersion, incomingFirstEventID, incomingVersion, ) s.NoError(err) s.Equal(s.mockRebuiltMutableState, rebuiltMutableState) s.Equal(s.newContext.GetHistorySize(), rebuiltHistorySize) s.True(mockBaseWorkflowReleaseFnCalled) } func (s *workflowResetterSuite) TestResetWorkflow_Error() { ctx := ctx.Background() now := time.Now() branchToken := []byte("some random branch token") lastEventID := int64(500) version := int64(123) versionHistory := persistence.NewVersionHistory( branchToken, []*persistence.VersionHistoryItem{persistence.NewVersionHistoryItem(lastEventID, version)}, ) versionHistories := persistence.NewVersionHistories(versionHistory) baseEventID := lastEventID + 100 baseVersion := version incomingFirstEventID := baseEventID + 12 incomingFirstEventVersion := baseVersion + 3 s.mockBaseMutableState.EXPECT().GetVersionHistories().Return(versionHistories).AnyTimes() s.mockBaseMutableState.EXPECT().GetCurrentBranchToken().Return(branchToken, nil).AnyTimes() mockBaseWorkflowReleaseFn := func(err error) { } mockBaseWorkflow := execution.NewMockWorkflow(s.controller) mockBaseWorkflow.EXPECT().GetMutableState().Return(s.mockBaseMutableState).AnyTimes() mockBaseWorkflow.EXPECT().GetReleaseFn().Return(mockBaseWorkflowReleaseFn).Times(1) s.mockTransactionMgr.EXPECT().loadNDCWorkflow( ctx, s.domainID, s.workflowID, s.baseRunID, ).Return(mockBaseWorkflow, nil).Times(1) rebuiltMutableState, err := s.workflowResetter.ResetWorkflow( ctx, now, baseEventID, baseVersion, incomingFirstEventID, incomingFirstEventVersion, ) s.Error(err) s.IsType(&types.RetryTaskV2Error{}, err) s.Nil(rebuiltMutableState) retryErr, isRetryError := err.(*types.RetryTaskV2Error) s.True(isRetryError) expectedErr := &types.RetryTaskV2Error{ Message: resendOnResetWorkflowMessage, DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.newRunID, EndEventID: common.Int64Ptr(incomingFirstEventID), EndEventVersion: common.Int64Ptr(incomingFirstEventVersion), } s.Equal(retryErr, expectedErr) } ================================================ FILE: service/history/query/query.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query import ( "sync/atomic" "github.com/pborman/uuid" "github.com/uber/cadence/common/types" ) const ( // TerminationTypeCompleted means a query reaches its termination state because it has been completed TerminationTypeCompleted TerminationType = iota // TerminationTypeUnblocked means a query reaches its termination state because it has been unblocked TerminationTypeUnblocked // TerminationTypeFailed means a query reaches its termination state because it has failed TerminationTypeFailed ) var ( errTerminationStateInvalid = &types.InternalServiceError{Message: "query termination state invalid"} errAlreadyInTerminalState = &types.InternalServiceError{Message: "query already in terminal state"} errQueryNotInTerminalState = &types.InternalServiceError{Message: "query not in terminal state"} ) type ( // TerminationType is the type of a query's termination state TerminationType int // TerminationState describes a query's termination state TerminationState struct { TerminationType TerminationType QueryResult *types.WorkflowQueryResult Failure error } query interface { getQueryID() string getQueryTermCh() <-chan struct{} getQueryInput() *types.WorkflowQuery getTerminationState() (*TerminationState, error) setTerminationState(*TerminationState) error } queryImpl struct { id string queryInput *types.WorkflowQuery termCh chan struct{} terminationState atomic.Value } ) func newQuery(queryInput *types.WorkflowQuery) query { return &queryImpl{ id: uuid.New(), queryInput: queryInput, termCh: make(chan struct{}), } } func (q *queryImpl) getQueryID() string { return q.id } func (q *queryImpl) getQueryTermCh() <-chan struct{} { return q.termCh } func (q *queryImpl) getQueryInput() *types.WorkflowQuery { return q.queryInput } func (q *queryImpl) getTerminationState() (*TerminationState, error) { ts := q.terminationState.Load() if ts == nil { return nil, errQueryNotInTerminalState } return ts.(*TerminationState), nil } func (q *queryImpl) setTerminationState(terminationState *TerminationState) error { if err := q.validateTerminationState(terminationState); err != nil { return err } currTerminationState, _ := q.getTerminationState() if currTerminationState != nil { return errAlreadyInTerminalState } q.terminationState.Store(terminationState) close(q.termCh) return nil } func (q *queryImpl) validateTerminationState( terminationState *TerminationState, ) error { if terminationState == nil { return errTerminationStateInvalid } switch terminationState.TerminationType { case TerminationTypeCompleted: if terminationState.QueryResult == nil || terminationState.Failure != nil { return errTerminationStateInvalid } queryResult := terminationState.QueryResult validAnswered := queryResult.GetResultType() == types.QueryResultTypeAnswered && queryResult.Answer != nil && queryResult.ErrorMessage == "" validFailed := queryResult.GetResultType() == types.QueryResultTypeFailed && queryResult.Answer == nil && queryResult.ErrorMessage != "" if !validAnswered && !validFailed { return errTerminationStateInvalid } return nil case TerminationTypeUnblocked: if terminationState.QueryResult != nil || terminationState.Failure != nil { return errTerminationStateInvalid } return nil case TerminationTypeFailed: if terminationState.QueryResult != nil || terminationState.Failure == nil { return errTerminationStateInvalid } return nil default: return errTerminationStateInvalid } } ================================================ FILE: service/history/query/query_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query import ( "errors" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/types" ) type QuerySuite struct { *require.Assertions suite.Suite } func TestQuerySuite(t *testing.T) { suite.Run(t, new(QuerySuite)) } func (s *QuerySuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *QuerySuite) TestValidateTerminationState() { testCases := []struct { ts *TerminationState expectErr bool }{ { ts: nil, expectErr: true, }, { ts: &TerminationState{ TerminationType: TerminationTypeCompleted, }, expectErr: true, }, { ts: &TerminationState{ TerminationType: TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{}, Failure: errors.New("err"), }, expectErr: true, }, { ts: &TerminationState{ TerminationType: TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeAnswered.Ptr(), }, }, expectErr: true, }, { ts: &TerminationState{ TerminationType: TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeAnswered.Ptr(), Answer: []byte{1, 2, 3}, ErrorMessage: "err", }, }, expectErr: true, }, { ts: &TerminationState{ TerminationType: TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeFailed.Ptr(), Answer: []byte{1, 2, 3}, }, }, expectErr: true, }, { ts: &TerminationState{ TerminationType: TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeFailed.Ptr(), ErrorMessage: "err", }, }, expectErr: false, }, { ts: &TerminationState{ TerminationType: TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeAnswered.Ptr(), Answer: []byte{1, 2, 3}, }, }, expectErr: false, }, { ts: &TerminationState{ TerminationType: TerminationTypeUnblocked, QueryResult: &types.WorkflowQueryResult{}, }, expectErr: true, }, { ts: &TerminationState{ TerminationType: TerminationTypeUnblocked, Failure: errors.New("err"), }, expectErr: true, }, { ts: &TerminationState{ TerminationType: TerminationTypeUnblocked, }, expectErr: false, }, { ts: &TerminationState{ TerminationType: TerminationTypeFailed, }, expectErr: true, }, { ts: &TerminationState{ TerminationType: TerminationTypeFailed, QueryResult: &types.WorkflowQueryResult{}, }, expectErr: true, }, { ts: &TerminationState{ TerminationType: TerminationTypeFailed, Failure: errors.New("err"), }, expectErr: false, }, } queryImpl := &queryImpl{} for _, tc := range testCases { if tc.expectErr { s.Error(queryImpl.validateTerminationState(tc.ts)) } else { s.NoError(queryImpl.validateTerminationState(tc.ts)) } } } func (s *QuerySuite) TestTerminationState_Failed() { failedTerminationState := &TerminationState{ TerminationType: TerminationTypeFailed, Failure: errors.New("err"), } s.testSetTerminationState(failedTerminationState) } func (s *QuerySuite) TestTerminationState_Completed() { answeredTerminationState := &TerminationState{ TerminationType: TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeAnswered.Ptr(), Answer: []byte{1, 2, 3}, }, } s.testSetTerminationState(answeredTerminationState) } func (s *QuerySuite) TestTerminationState_Unblocked() { unblockedTerminationState := &TerminationState{ TerminationType: TerminationTypeUnblocked, } s.testSetTerminationState(unblockedTerminationState) } func (s *QuerySuite) testSetTerminationState(terminationState *TerminationState) { query := newQuery(nil) ts, err := query.getTerminationState() s.Equal(errQueryNotInTerminalState, err) s.Nil(ts) s.False(closed(query.getQueryTermCh())) s.Equal(errTerminationStateInvalid, query.setTerminationState(nil)) s.NoError(query.setTerminationState(terminationState)) s.True(closed(query.getQueryTermCh())) actualTerminationState, err := query.getTerminationState() s.NoError(err) s.assertTerminationStateEqual(terminationState, actualTerminationState) } func (s *QuerySuite) assertTerminationStateEqual(expected *TerminationState, actual *TerminationState) { s.Equal(expected.TerminationType, actual.TerminationType) if expected.Failure != nil { s.Equal(expected.Failure.Error(), actual.Failure.Error()) } if expected.QueryResult != nil { s.Equal(expected.QueryResult, actual.QueryResult) } } func closed(ch <-chan struct{}) bool { select { case <-ch: return true default: return false } } ================================================ FILE: service/history/query/registry.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination registry_mock.go -self_package github.com/uber/cadence/service/history/query package query import ( "sync" "github.com/uber/cadence/common/types" ) var ( errQueryNotExists = &types.InternalServiceError{Message: "query does not exist"} ) type ( // Registry manages all the queries for a workflow Registry interface { HasBufferedQuery() bool GetBufferedIDs() []string HasCompletedQuery() bool GetCompletedIDs() []string HasUnblockedQuery() bool GetUnblockedIDs() []string HasFailedQuery() bool GetFailedIDs() []string GetQueryTermCh(string) (<-chan struct{}, error) GetQueryInput(string) (*types.WorkflowQuery, error) GetTerminationState(string) (*TerminationState, error) BufferQuery(queryInput *types.WorkflowQuery) (string, <-chan struct{}) SetTerminationState(string, *TerminationState) error RemoveQuery(id string) } registryImpl struct { sync.RWMutex buffered map[string]query completed map[string]query unblocked map[string]query failed map[string]query } ) // NewRegistry creates a new query registry func NewRegistry() Registry { return ®istryImpl{ buffered: make(map[string]query), completed: make(map[string]query), unblocked: make(map[string]query), failed: make(map[string]query), } } func (r *registryImpl) HasBufferedQuery() bool { r.RLock() defer r.RUnlock() return len(r.buffered) > 0 } func (r *registryImpl) GetBufferedIDs() []string { r.RLock() defer r.RUnlock() return r.getIDs(r.buffered) } func (r *registryImpl) HasCompletedQuery() bool { r.RLock() defer r.RUnlock() return len(r.completed) > 0 } func (r *registryImpl) GetCompletedIDs() []string { r.RLock() defer r.RUnlock() return r.getIDs(r.completed) } func (r *registryImpl) HasUnblockedQuery() bool { r.RLock() defer r.RUnlock() return len(r.unblocked) > 0 } func (r *registryImpl) GetUnblockedIDs() []string { r.RLock() defer r.RUnlock() return r.getIDs(r.unblocked) } func (r *registryImpl) HasFailedQuery() bool { r.RLock() defer r.RUnlock() return len(r.failed) > 0 } func (r *registryImpl) GetFailedIDs() []string { r.RLock() defer r.RUnlock() return r.getIDs(r.failed) } func (r *registryImpl) GetQueryTermCh(id string) (<-chan struct{}, error) { r.RLock() defer r.RUnlock() q, err := r.getQueryNoLock(id) if err != nil { return nil, err } return q.getQueryTermCh(), nil } func (r *registryImpl) GetQueryInput(id string) (*types.WorkflowQuery, error) { r.RLock() defer r.RUnlock() q, err := r.getQueryNoLock(id) if err != nil { return nil, err } return q.getQueryInput(), nil } func (r *registryImpl) GetTerminationState(id string) (*TerminationState, error) { r.RLock() defer r.RUnlock() q, err := r.getQueryNoLock(id) if err != nil { return nil, err } return q.getTerminationState() } func (r *registryImpl) BufferQuery(queryInput *types.WorkflowQuery) (string, <-chan struct{}) { r.Lock() defer r.Unlock() q := newQuery(queryInput) id := q.getQueryID() r.buffered[id] = q return id, q.getQueryTermCh() } func (r *registryImpl) SetTerminationState(id string, TerminationState *TerminationState) error { r.Lock() defer r.Unlock() q, ok := r.buffered[id] if !ok { return errQueryNotExists } if err := q.setTerminationState(TerminationState); err != nil { return err } delete(r.buffered, id) switch TerminationState.TerminationType { case TerminationTypeCompleted: r.completed[id] = q case TerminationTypeUnblocked: r.unblocked[id] = q case TerminationTypeFailed: r.failed[id] = q } return nil } func (r *registryImpl) RemoveQuery(id string) { r.Lock() defer r.Unlock() delete(r.buffered, id) delete(r.completed, id) delete(r.unblocked, id) delete(r.failed, id) } func (r *registryImpl) getQueryNoLock(id string) (query, error) { if q, ok := r.buffered[id]; ok { return q, nil } if q, ok := r.completed[id]; ok { return q, nil } if q, ok := r.unblocked[id]; ok { return q, nil } if q, ok := r.failed[id]; ok { return q, nil } return nil, errQueryNotExists } func (r *registryImpl) getIDs(m map[string]query) []string { result := make([]string, len(m)) index := 0 for id := range m { result[index] = id index++ } return result } ================================================ FILE: service/history/query/registry_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: registry.go // // Generated by this command: // // mockgen -package query -source registry.go -destination registry_mock.go -self_package github.com/uber/cadence/service/history/query // // Package query is a generated GoMock package. package query import ( reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockRegistry is a mock of Registry interface. type MockRegistry struct { ctrl *gomock.Controller recorder *MockRegistryMockRecorder isgomock struct{} } // MockRegistryMockRecorder is the mock recorder for MockRegistry. type MockRegistryMockRecorder struct { mock *MockRegistry } // NewMockRegistry creates a new mock instance. func NewMockRegistry(ctrl *gomock.Controller) *MockRegistry { mock := &MockRegistry{ctrl: ctrl} mock.recorder = &MockRegistryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockRegistry) EXPECT() *MockRegistryMockRecorder { return m.recorder } // BufferQuery mocks base method. func (m *MockRegistry) BufferQuery(queryInput *types.WorkflowQuery) (string, <-chan struct{}) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BufferQuery", queryInput) ret0, _ := ret[0].(string) ret1, _ := ret[1].(<-chan struct{}) return ret0, ret1 } // BufferQuery indicates an expected call of BufferQuery. func (mr *MockRegistryMockRecorder) BufferQuery(queryInput any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BufferQuery", reflect.TypeOf((*MockRegistry)(nil).BufferQuery), queryInput) } // GetBufferedIDs mocks base method. func (m *MockRegistry) GetBufferedIDs() []string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBufferedIDs") ret0, _ := ret[0].([]string) return ret0 } // GetBufferedIDs indicates an expected call of GetBufferedIDs. func (mr *MockRegistryMockRecorder) GetBufferedIDs() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBufferedIDs", reflect.TypeOf((*MockRegistry)(nil).GetBufferedIDs)) } // GetCompletedIDs mocks base method. func (m *MockRegistry) GetCompletedIDs() []string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCompletedIDs") ret0, _ := ret[0].([]string) return ret0 } // GetCompletedIDs indicates an expected call of GetCompletedIDs. func (mr *MockRegistryMockRecorder) GetCompletedIDs() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCompletedIDs", reflect.TypeOf((*MockRegistry)(nil).GetCompletedIDs)) } // GetFailedIDs mocks base method. func (m *MockRegistry) GetFailedIDs() []string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFailedIDs") ret0, _ := ret[0].([]string) return ret0 } // GetFailedIDs indicates an expected call of GetFailedIDs. func (mr *MockRegistryMockRecorder) GetFailedIDs() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFailedIDs", reflect.TypeOf((*MockRegistry)(nil).GetFailedIDs)) } // GetQueryInput mocks base method. func (m *MockRegistry) GetQueryInput(arg0 string) (*types.WorkflowQuery, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueryInput", arg0) ret0, _ := ret[0].(*types.WorkflowQuery) ret1, _ := ret[1].(error) return ret0, ret1 } // GetQueryInput indicates an expected call of GetQueryInput. func (mr *MockRegistryMockRecorder) GetQueryInput(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueryInput", reflect.TypeOf((*MockRegistry)(nil).GetQueryInput), arg0) } // GetQueryTermCh mocks base method. func (m *MockRegistry) GetQueryTermCh(arg0 string) (<-chan struct{}, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueryTermCh", arg0) ret0, _ := ret[0].(<-chan struct{}) ret1, _ := ret[1].(error) return ret0, ret1 } // GetQueryTermCh indicates an expected call of GetQueryTermCh. func (mr *MockRegistryMockRecorder) GetQueryTermCh(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueryTermCh", reflect.TypeOf((*MockRegistry)(nil).GetQueryTermCh), arg0) } // GetTerminationState mocks base method. func (m *MockRegistry) GetTerminationState(arg0 string) (*TerminationState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTerminationState", arg0) ret0, _ := ret[0].(*TerminationState) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTerminationState indicates an expected call of GetTerminationState. func (mr *MockRegistryMockRecorder) GetTerminationState(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTerminationState", reflect.TypeOf((*MockRegistry)(nil).GetTerminationState), arg0) } // GetUnblockedIDs mocks base method. func (m *MockRegistry) GetUnblockedIDs() []string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUnblockedIDs") ret0, _ := ret[0].([]string) return ret0 } // GetUnblockedIDs indicates an expected call of GetUnblockedIDs. func (mr *MockRegistryMockRecorder) GetUnblockedIDs() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnblockedIDs", reflect.TypeOf((*MockRegistry)(nil).GetUnblockedIDs)) } // HasBufferedQuery mocks base method. func (m *MockRegistry) HasBufferedQuery() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasBufferedQuery") ret0, _ := ret[0].(bool) return ret0 } // HasBufferedQuery indicates an expected call of HasBufferedQuery. func (mr *MockRegistryMockRecorder) HasBufferedQuery() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasBufferedQuery", reflect.TypeOf((*MockRegistry)(nil).HasBufferedQuery)) } // HasCompletedQuery mocks base method. func (m *MockRegistry) HasCompletedQuery() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasCompletedQuery") ret0, _ := ret[0].(bool) return ret0 } // HasCompletedQuery indicates an expected call of HasCompletedQuery. func (mr *MockRegistryMockRecorder) HasCompletedQuery() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasCompletedQuery", reflect.TypeOf((*MockRegistry)(nil).HasCompletedQuery)) } // HasFailedQuery mocks base method. func (m *MockRegistry) HasFailedQuery() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasFailedQuery") ret0, _ := ret[0].(bool) return ret0 } // HasFailedQuery indicates an expected call of HasFailedQuery. func (mr *MockRegistryMockRecorder) HasFailedQuery() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasFailedQuery", reflect.TypeOf((*MockRegistry)(nil).HasFailedQuery)) } // HasUnblockedQuery mocks base method. func (m *MockRegistry) HasUnblockedQuery() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasUnblockedQuery") ret0, _ := ret[0].(bool) return ret0 } // HasUnblockedQuery indicates an expected call of HasUnblockedQuery. func (mr *MockRegistryMockRecorder) HasUnblockedQuery() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasUnblockedQuery", reflect.TypeOf((*MockRegistry)(nil).HasUnblockedQuery)) } // RemoveQuery mocks base method. func (m *MockRegistry) RemoveQuery(id string) { m.ctrl.T.Helper() m.ctrl.Call(m, "RemoveQuery", id) } // RemoveQuery indicates an expected call of RemoveQuery. func (mr *MockRegistryMockRecorder) RemoveQuery(id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveQuery", reflect.TypeOf((*MockRegistry)(nil).RemoveQuery), id) } // SetTerminationState mocks base method. func (m *MockRegistry) SetTerminationState(arg0 string, arg1 *TerminationState) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetTerminationState", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // SetTerminationState indicates an expected call of SetTerminationState. func (mr *MockRegistryMockRecorder) SetTerminationState(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTerminationState", reflect.TypeOf((*MockRegistry)(nil).SetTerminationState), arg0, arg1) } ================================================ FILE: service/history/query/registry_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package query import ( "errors" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/types" ) type QueryRegistrySuite struct { suite.Suite *require.Assertions } func TestQueryRegistrySuite(t *testing.T) { suite.Run(t, new(QueryRegistrySuite)) } func (s *QueryRegistrySuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *QueryRegistrySuite) TestQueryRegistry() { qr := NewRegistry() ids := make([]string, 100) termChans := make([]<-chan struct{}, 100) for i := 0; i < 100; i++ { ids[i], termChans[i] = qr.BufferQuery(&types.WorkflowQuery{}) } s.assertBufferedState(qr, ids...) s.assertHasQueries(qr, true, false, false, false) s.assertQuerySizes(qr, 100, 0, 0, 0) s.assertChanState(false, termChans...) for i := 0; i < 25; i++ { err := qr.SetTerminationState(ids[i], &TerminationState{ TerminationType: TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{ ResultType: types.QueryResultTypeAnswered.Ptr(), Answer: []byte{1, 2, 3}, }, }) s.NoError(err) } s.assertCompletedState(qr, ids[0:25]...) s.assertBufferedState(qr, ids[25:]...) s.assertHasQueries(qr, true, true, false, false) s.assertQuerySizes(qr, 75, 25, 0, 0) s.assertChanState(true, termChans[0:25]...) s.assertChanState(false, termChans[25:]...) for i := 25; i < 50; i++ { err := qr.SetTerminationState(ids[i], &TerminationState{ TerminationType: TerminationTypeUnblocked, }) s.NoError(err) } s.assertCompletedState(qr, ids[0:25]...) s.assertUnblockedState(qr, ids[25:50]...) s.assertBufferedState(qr, ids[50:]...) s.assertHasQueries(qr, true, true, true, false) s.assertQuerySizes(qr, 50, 25, 25, 0) s.assertChanState(true, termChans[0:50]...) s.assertChanState(false, termChans[50:]...) for i := 50; i < 75; i++ { err := qr.SetTerminationState(ids[i], &TerminationState{ TerminationType: TerminationTypeFailed, Failure: errors.New("err"), }) s.NoError(err) } s.assertCompletedState(qr, ids[0:25]...) s.assertUnblockedState(qr, ids[25:50]...) s.assertFailedState(qr, ids[50:75]...) s.assertBufferedState(qr, ids[75:]...) s.assertHasQueries(qr, true, true, true, true) s.assertQuerySizes(qr, 25, 25, 25, 25) s.assertChanState(true, termChans[0:75]...) s.assertChanState(false, termChans[75:]...) for i := 0; i < 75; i++ { switch i % 3 { case 0: s.Equal(errQueryNotExists, qr.SetTerminationState(ids[i], &TerminationState{ TerminationType: TerminationTypeCompleted, QueryResult: &types.WorkflowQueryResult{}, })) case 1: s.Equal(errQueryNotExists, qr.SetTerminationState(ids[i], &TerminationState{ TerminationType: TerminationTypeUnblocked, })) case 2: s.Equal(errQueryNotExists, qr.SetTerminationState(ids[i], &TerminationState{ TerminationType: TerminationTypeFailed, Failure: errors.New("err"), })) } } s.assertCompletedState(qr, ids[0:25]...) s.assertUnblockedState(qr, ids[25:50]...) s.assertFailedState(qr, ids[50:75]...) s.assertBufferedState(qr, ids[75:]...) s.assertHasQueries(qr, true, true, true, true) s.assertQuerySizes(qr, 25, 25, 25, 25) s.assertChanState(true, termChans[0:75]...) s.assertChanState(false, termChans[75:]...) for i := 0; i < 25; i++ { qr.RemoveQuery(ids[i]) s.assertHasQueries(qr, true, i < 24, true, true) s.assertQuerySizes(qr, 25, 25-i-1, 25, 25) } for i := 25; i < 50; i++ { qr.RemoveQuery(ids[i]) s.assertHasQueries(qr, true, false, i < 49, true) s.assertQuerySizes(qr, 25, 0, 50-i-1, 25) } for i := 50; i < 75; i++ { qr.RemoveQuery(ids[i]) s.assertHasQueries(qr, true, false, false, i < 74) s.assertQuerySizes(qr, 25, 0, 0, 75-i-1) } for i := 75; i < 100; i++ { qr.RemoveQuery(ids[i]) s.assertHasQueries(qr, i < 99, false, false, false) s.assertQuerySizes(qr, 100-i-1, 0, 0, 0) } s.assertChanState(true, termChans[0:75]...) s.assertChanState(false, termChans[75:]...) } func (s *QueryRegistrySuite) assertBufferedState(qr Registry, ids ...string) { for _, id := range ids { termCh, err := qr.GetQueryTermCh(id) s.NoError(err) s.False(closed(termCh)) input, err := qr.GetQueryInput(id) s.NoError(err) s.NotNil(input) termState, err := qr.GetTerminationState(id) s.Equal(errQueryNotInTerminalState, err) s.Nil(termState) } } func (s *QueryRegistrySuite) assertCompletedState(qr Registry, ids ...string) { for _, id := range ids { termCh, err := qr.GetQueryTermCh(id) s.NoError(err) s.True(closed(termCh)) input, err := qr.GetQueryInput(id) s.NoError(err) s.NotNil(input) termState, err := qr.GetTerminationState(id) s.NoError(err) s.NotNil(termState) s.Equal(TerminationTypeCompleted, termState.TerminationType) s.NotNil(termState.QueryResult) s.Nil(termState.Failure) } } func (s *QueryRegistrySuite) assertUnblockedState(qr Registry, ids ...string) { for _, id := range ids { termCh, err := qr.GetQueryTermCh(id) s.NoError(err) s.True(closed(termCh)) input, err := qr.GetQueryInput(id) s.NoError(err) s.NotNil(input) termState, err := qr.GetTerminationState(id) s.NoError(err) s.NotNil(termState) s.Equal(TerminationTypeUnblocked, termState.TerminationType) s.Nil(termState.QueryResult) s.Nil(termState.Failure) } } func (s *QueryRegistrySuite) assertFailedState(qr Registry, ids ...string) { for _, id := range ids { termCh, err := qr.GetQueryTermCh(id) s.NoError(err) s.True(closed(termCh)) input, err := qr.GetQueryInput(id) s.NoError(err) s.NotNil(input) termState, err := qr.GetTerminationState(id) s.NoError(err) s.NotNil(termState) s.Equal(TerminationTypeFailed, termState.TerminationType) s.Nil(termState.QueryResult) s.NotNil(termState.Failure) } } func (s *QueryRegistrySuite) assertHasQueries(qr Registry, buffered, completed, unblocked, failed bool) { s.Equal(buffered, qr.HasBufferedQuery()) s.Equal(completed, qr.HasCompletedQuery()) s.Equal(unblocked, qr.HasUnblockedQuery()) s.Equal(failed, qr.HasFailedQuery()) } func (s *QueryRegistrySuite) assertQuerySizes(qr Registry, buffered, completed, unblocked, failed int) { s.Len(qr.GetBufferedIDs(), buffered) s.Len(qr.GetCompletedIDs(), completed) s.Len(qr.GetUnblockedIDs(), unblocked) s.Len(qr.GetFailedIDs(), failed) } func (s *QueryRegistrySuite) assertChanState(expectedClosed bool, chans ...<-chan struct{}) { for _, ch := range chans { s.Equal(expectedClosed, closed(ch)) } } ================================================ FILE: service/history/queue/action.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package queue import "github.com/uber/cadence/common/types" type ( // ActionType specifies the type of the Action ActionType int // Action specifies the Action should be performed Action struct { ActionType ActionType ResetActionAttributes *ResetActionAttributes GetStateActionAttributes *GetStateActionAttributes GetTasksAttributes *GetTasksAttributes UpdateTaskAttributes *UpdateTasksAttributes // add attributes for other action types here } // ActionResult is the result for performing an Action ActionResult struct { ActionType ActionType ResetActionResult *ResetActionResult GetStateActionResult *GetStateActionResult GetTasksResult *GetTasksResult UpdateTaskResult *UpdateTasksResult } // ResetActionAttributes contains the parameter for performing Reset Action ResetActionAttributes struct{} // ResetActionResult is the result for performing Reset Action ResetActionResult struct{} // GetStateActionAttributes contains the parameter for performing GetState Action GetStateActionAttributes struct{} // GetStateActionResult is the result for performing GetState Action GetStateActionResult struct { States []ProcessingQueueState } // GetTasksAttributes contains the parameter to get tasks GetTasksAttributes struct{} // GetTasksResult is the result for performing GetTasks Action GetTasksResult struct { TaskRequests []*types.CrossClusterTaskRequest } // UpdateTasksAttributes contains the parameter to update task UpdateTasksAttributes struct { TaskResponses []*types.CrossClusterTaskResponse } // UpdateTasksResult is the result for performing UpdateTask Action UpdateTasksResult struct { } ) const ( // ActionTypeReset is the ActionType for reseting processing queue states ActionTypeReset ActionType = iota + 1 // ActionTypeGetState is the ActionType for reading processing queue states ActionTypeGetState // ActionTypeGetTasks is the ActionType for get cross cluster tasks ActionTypeGetTasks // ActionTypeUpdateTask is the ActionType to update outstanding task ActionTypeUpdateTask // add more ActionType here ) // NewResetAction creates a new action for reseting processing queue states func NewResetAction() *Action { return &Action{ ActionType: ActionTypeReset, ResetActionAttributes: &ResetActionAttributes{}, } } // NewGetStateAction reads all processing queue states in the processor func NewGetStateAction() *Action { return &Action{ ActionType: ActionTypeGetState, GetStateActionAttributes: &GetStateActionAttributes{}, } } // NewGetTasksAction creates a queue action for fetching cross cluster tasks func NewGetTasksAction() *Action { return &Action{ ActionType: ActionTypeGetTasks, GetTasksAttributes: &GetTasksAttributes{}, } } // NewUpdateTasksAction creates a queue action for responding cross cluster task // processing results func NewUpdateTasksAction( taskResponses []*types.CrossClusterTaskResponse, ) *Action { return &Action{ ActionType: ActionTypeUpdateTask, UpdateTaskAttributes: &UpdateTasksAttributes{ TaskResponses: taskResponses, }, } } ================================================ FILE: service/history/queue/constants.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import "time" const ( defaultProcessingQueueLevel = 0 // gracefulShutdownTimeout is the the hardcoded timeout for queue components to wrap up shutting down. // This is not ideal because we should have a top level deadline for shutdown propagating down to all components. gracefulShutdownTimeout = time.Minute ) ================================================ FILE: service/history/queue/domain_filter.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package queue // NewDomainFilter creates a new domain filter func NewDomainFilter( domainIDs map[string]struct{}, reverseMatch bool, ) DomainFilter { if domainIDs == nil { domainIDs = make(map[string]struct{}) } return DomainFilter{ DomainIDs: domainIDs, ReverseMatch: reverseMatch, } } // Filter returns true if domainID is in the domainID set specified by the filter func (f DomainFilter) Filter(domainID string) bool { _, ok := f.DomainIDs[domainID] if f.ReverseMatch { ok = !ok } return ok } // Include adds more domainIDs to the domainID set specified by the filter func (f DomainFilter) Include(domainIDs map[string]struct{}) DomainFilter { filter := f.copy() for domainID := range domainIDs { if !filter.ReverseMatch { filter.DomainIDs[domainID] = struct{}{} } else { delete(filter.DomainIDs, domainID) } } return filter } // Exclude removes domainIDs from the domainID set specified by the filter func (f DomainFilter) Exclude(domainIDs map[string]struct{}) DomainFilter { filter := f.copy() for domainID := range domainIDs { if !filter.ReverseMatch { delete(filter.DomainIDs, domainID) } else { filter.DomainIDs[domainID] = struct{}{} } } return filter } // Merge merges the domainID sets specified by two domain filters func (f DomainFilter) Merge(f2 DomainFilter) DomainFilter { // case 1: ReverseMatch field is false for both filters if !f.ReverseMatch && !f2.ReverseMatch { // union the domainIDs field filter := f.copy() for domainID := range f2.DomainIDs { filter.DomainIDs[domainID] = struct{}{} } return filter } // for the following three cases, ReverseMatch field is always true // case 2: ReverseMatch field is true for both filters if f.ReverseMatch && f2.ReverseMatch { // intersect the domainIDs field filter := DomainFilter{ DomainIDs: make(map[string]struct{}), ReverseMatch: true, } for domainID := range f.DomainIDs { if _, ok := f2.DomainIDs[domainID]; ok { filter.DomainIDs[domainID] = struct{}{} } } return filter } // case 3, 4: one of the filters has ReverseMatch equals true var filter DomainFilter var includeDomainIDs map[string]struct{} if f.ReverseMatch { filter = f.copy() includeDomainIDs = f2.DomainIDs } else { filter = f2.copy() includeDomainIDs = f.DomainIDs } for domainID := range includeDomainIDs { delete(filter.DomainIDs, domainID) } return filter } func (f DomainFilter) copy() DomainFilter { domainIDs := make(map[string]struct{}) for domainID := range f.DomainIDs { domainIDs[domainID] = struct{}{} } return NewDomainFilter(domainIDs, f.ReverseMatch) } func covertToDomainIDSet( domainIDs []string, ) map[string]struct{} { domainIDsMap := make(map[string]struct{}) for _, domainID := range domainIDs { domainIDsMap[domainID] = struct{}{} } return domainIDsMap } ================================================ FILE: service/history/queue/domain_filter_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package queue import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type ( domainFilterSuite struct { suite.Suite *require.Assertions } ) func TestDomainFilterSuite(t *testing.T) { s := new(domainFilterSuite) suite.Run(t, s) } func (s *domainFilterSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *domainFilterSuite) TestDomainFilter_Filter() { testCases := []struct { domainIDs map[string]struct{} reverseMatch bool testDomainIDs []string expectedResult []bool }{ { domainIDs: nil, reverseMatch: false, testDomainIDs: []string{"some random domain"}, expectedResult: []bool{false}, }, { domainIDs: covertToDomainIDSet([]string{"testDomain1", "testDomain2"}), reverseMatch: false, testDomainIDs: []string{"testDomain1", "some random domain"}, expectedResult: []bool{true, false}, }, { domainIDs: covertToDomainIDSet([]string{}), reverseMatch: true, testDomainIDs: []string{"any domainID"}, expectedResult: []bool{true}, }, { domainIDs: covertToDomainIDSet([]string{"testDomain1", "testDomain2"}), reverseMatch: true, testDomainIDs: []string{"testDomain1", "some random domain"}, expectedResult: []bool{false, true}, }, } for _, tc := range testCases { filter := NewDomainFilter(tc.domainIDs, tc.reverseMatch) for i, testDomain := range tc.testDomainIDs { result := filter.Filter(testDomain) s.Equal(tc.expectedResult[i], result) } } } func (s *domainFilterSuite) TestDomainFilter_Include() { testCases := []struct { domainIDs map[string]struct{} reverseMatch bool newDomainIDs map[string]struct{} expectedDomainIDs map[string]struct{} }{ { domainIDs: covertToDomainIDSet([]string{"testDomain1", "testDomain2"}), reverseMatch: false, newDomainIDs: covertToDomainIDSet([]string{"testDomain2", "testDomain3"}), expectedDomainIDs: covertToDomainIDSet([]string{"testDomain1", "testDomain2", "testDomain3"}), }, { domainIDs: covertToDomainIDSet([]string{"testDomain1", "testDomain2"}), reverseMatch: true, newDomainIDs: covertToDomainIDSet([]string{"testDomain2", "testDomain3"}), expectedDomainIDs: covertToDomainIDSet([]string{"testDomain1"}), }, } for _, tc := range testCases { baseFilter := NewDomainFilter(tc.domainIDs, tc.reverseMatch) newFilter := baseFilter.Include(tc.newDomainIDs) // check if the base filter got modified s.Equal(tc.domainIDs, baseFilter.DomainIDs) s.Equal(tc.reverseMatch, baseFilter.ReverseMatch) s.Equal(tc.expectedDomainIDs, newFilter.DomainIDs) s.Equal(tc.reverseMatch, newFilter.ReverseMatch) } } func (s *domainFilterSuite) TestDomainFilter_Exclude() { testCases := []struct { domainIDs map[string]struct{} reverseMatch bool newDomainIDs map[string]struct{} expectedDomainIDs map[string]struct{} }{ { domainIDs: covertToDomainIDSet([]string{"testDomain1", "testDomain2"}), reverseMatch: false, newDomainIDs: covertToDomainIDSet([]string{"testDomain2", "testDomain3"}), expectedDomainIDs: covertToDomainIDSet([]string{"testDomain1"}), }, { domainIDs: covertToDomainIDSet([]string{"testDomain1", "testDomain2"}), reverseMatch: true, newDomainIDs: covertToDomainIDSet([]string{"testDomain2", "testDomain3"}), expectedDomainIDs: covertToDomainIDSet([]string{"testDomain1", "testDomain2", "testDomain3"}), }, } for _, tc := range testCases { baseFilter := NewDomainFilter(tc.domainIDs, tc.reverseMatch) newFilter := baseFilter.Exclude(tc.newDomainIDs) // check if the base filter got modified s.Equal(tc.domainIDs, baseFilter.DomainIDs) s.Equal(tc.reverseMatch, baseFilter.ReverseMatch) s.Equal(tc.expectedDomainIDs, newFilter.DomainIDs) s.Equal(tc.reverseMatch, newFilter.ReverseMatch) } } func (s *domainFilterSuite) TestDomainFilter_Merge() { testCases := []struct { domainIDs []map[string]struct{} reverseMatch []bool expectedDomainIDs map[string]struct{} expectedReverseMatch bool }{ { domainIDs: []map[string]struct{}{ covertToDomainIDSet([]string{"testDomain1", "testDomain2"}), covertToDomainIDSet([]string{"testDomain2", "testDomain3"}), }, reverseMatch: []bool{false, false}, expectedDomainIDs: covertToDomainIDSet([]string{"testDomain1", "testDomain2", "testDomain3"}), expectedReverseMatch: false, }, { domainIDs: []map[string]struct{}{ covertToDomainIDSet([]string{"testDomain1", "testDomain2"}), covertToDomainIDSet([]string{"testDomain2", "testDomain3"}), }, reverseMatch: []bool{true, true}, expectedDomainIDs: covertToDomainIDSet([]string{"testDomain2"}), expectedReverseMatch: true, }, { domainIDs: []map[string]struct{}{ covertToDomainIDSet([]string{"testDomain1", "testDomain2"}), covertToDomainIDSet([]string{"testDomain2", "testDomain3"}), }, reverseMatch: []bool{true, false}, expectedDomainIDs: covertToDomainIDSet([]string{"testDomain1"}), expectedReverseMatch: true, }, { domainIDs: []map[string]struct{}{ covertToDomainIDSet([]string{"testDomain1", "testDomain2"}), covertToDomainIDSet([]string{"testDomain2", "testDomain3"}), }, reverseMatch: []bool{false, true}, expectedDomainIDs: covertToDomainIDSet([]string{"testDomain3"}), expectedReverseMatch: true, }, } for _, tc := range testCases { var filters []DomainFilter for i, domainIDs := range tc.domainIDs { filters = append(filters, NewDomainFilter(domainIDs, tc.reverseMatch[i])) } s.NotEmpty(filters) mergedFilter := filters[0] for _, f := range filters[1:] { mergedFilter = mergedFilter.Merge(f) } s.Equal(tc.expectedDomainIDs, mergedFilter.DomainIDs) s.Equal(tc.expectedReverseMatch, mergedFilter.ReverseMatch) } } ================================================ FILE: service/history/queue/factory.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/reset" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" "github.com/uber/cadence/service/history/workflowcache" "github.com/uber/cadence/service/worker/archiver" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination factory_mock.go -self_package github.com/uber/cadence/service/history/queue type ( Factory interface { Category() persistence.HistoryTaskCategory CreateQueue(shard.Context, execution.Cache, invariant.Invariant) Processor } transferQueueFactory struct { taskProcessor task.Processor archivalClient archiver.Client wfIDCache workflowcache.WFCache } timerQueueFactory struct { taskProcessor task.Processor archivalClient archiver.Client } ) func NewTransferQueueFactory( taskProcessor task.Processor, archivalClient archiver.Client, wfIDCache workflowcache.WFCache, ) Factory { return &transferQueueFactory{ taskProcessor: taskProcessor, archivalClient: archivalClient, wfIDCache: wfIDCache, } } func (f *transferQueueFactory) Category() persistence.HistoryTaskCategory { return persistence.HistoryTaskCategoryTransfer } func (f *transferQueueFactory) CreateQueue( shard shard.Context, executionCache execution.Cache, openExecutionCheck invariant.Invariant, ) Processor { workflowResetter := reset.NewWorkflowResetter(shard, executionCache, shard.GetLogger()) return NewTransferQueueProcessor( shard, f.taskProcessor, executionCache, workflowResetter, f.archivalClient, openExecutionCheck, f.wfIDCache, ) } func (f *timerQueueFactory) Category() persistence.HistoryTaskCategory { return persistence.HistoryTaskCategoryTimer } func NewTimerQueueFactory( taskProcessor task.Processor, archivalClient archiver.Client, ) Factory { return &timerQueueFactory{ taskProcessor: taskProcessor, archivalClient: archivalClient, } } func (f *timerQueueFactory) CreateQueue( shard shard.Context, executionCache execution.Cache, openExecutionCheck invariant.Invariant, ) Processor { return NewTimerQueueProcessor( shard, f.taskProcessor, executionCache, f.archivalClient, openExecutionCheck, ) } ================================================ FILE: service/history/queue/factory_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: factory.go // // Generated by this command: // // mockgen -package queue -source factory.go -destination factory_mock.go -self_package github.com/uber/cadence/service/history/queue // // Package queue is a generated GoMock package. package queue import ( reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" invariant "github.com/uber/cadence/common/reconciliation/invariant" execution "github.com/uber/cadence/service/history/execution" shard "github.com/uber/cadence/service/history/shard" ) // MockFactory is a mock of Factory interface. type MockFactory struct { ctrl *gomock.Controller recorder *MockFactoryMockRecorder isgomock struct{} } // MockFactoryMockRecorder is the mock recorder for MockFactory. type MockFactoryMockRecorder struct { mock *MockFactory } // NewMockFactory creates a new mock instance. func NewMockFactory(ctrl *gomock.Controller) *MockFactory { mock := &MockFactory{ctrl: ctrl} mock.recorder = &MockFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFactory) EXPECT() *MockFactoryMockRecorder { return m.recorder } // Category mocks base method. func (m *MockFactory) Category() persistence.HistoryTaskCategory { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Category") ret0, _ := ret[0].(persistence.HistoryTaskCategory) return ret0 } // Category indicates an expected call of Category. func (mr *MockFactoryMockRecorder) Category() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Category", reflect.TypeOf((*MockFactory)(nil).Category)) } // CreateQueue mocks base method. func (m *MockFactory) CreateQueue(arg0 shard.Context, arg1 execution.Cache, arg2 invariant.Invariant) Processor { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateQueue", arg0, arg1, arg2) ret0, _ := ret[0].(Processor) return ret0 } // CreateQueue indicates an expected call of CreateQueue. func (mr *MockFactoryMockRecorder) CreateQueue(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateQueue", reflect.TypeOf((*MockFactory)(nil).CreateQueue), arg0, arg1, arg2) } ================================================ FILE: service/history/queue/factory_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "testing" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" "github.com/uber/cadence/service/history/workflowcache" "github.com/uber/cadence/service/worker/archiver" ) func TestTransferQueueFactory(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShard := shard.NewTestContext( t, ctrl, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest()) defer mockShard.Finish(t) mockProcessor := task.NewMockProcessor(ctrl) mockArchiver := archiver.NewMockClient(ctrl) mockInvariant := invariant.NewMockInvariant(ctrl) mockWorkflowCache := workflowcache.NewMockWFCache(ctrl) f := NewTransferQueueFactory(mockProcessor, mockArchiver, mockWorkflowCache) processor := f.CreateQueue(mockShard, execution.NewCache(mockShard), mockInvariant) if processor == nil { t.Error("NewTransferQueueProcessor returned nil") } } func TestTimerQueueFactory(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShard := shard.NewTestContext( t, ctrl, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest()) defer mockShard.Finish(t) mockProcessor := task.NewMockProcessor(ctrl) mockArchiver := archiver.NewMockClient(ctrl) mockInvariant := invariant.NewMockInvariant(ctrl) f := NewTimerQueueFactory(mockProcessor, mockArchiver) processor := f.CreateQueue(mockShard, execution.NewCache(mockShard), mockInvariant) if processor == nil { t.Error("NewTimerQueueProcessor returned nil") } } ================================================ FILE: service/history/queue/interface.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/service/history/queue package queue import ( "context" "github.com/uber/cadence/common" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/task" ) type ( // DomainFilter filters domain DomainFilter struct { DomainIDs map[string]struct{} // by default, a DomainFilter matches domains listed in the Domains field // if reverseMatch is true then the DomainFilter matches domains that are // not in the Domains field. ReverseMatch bool } // ProcessingQueueState indicates the scope of a task processing queue and its current progress ProcessingQueueState interface { Level() int AckLevel() task.Key ReadLevel() task.Key MaxLevel() task.Key DomainFilter() DomainFilter } // ProcessingQueue is responsible for keeping track of the state of tasks // within the scope defined by its state; it can also be split into multiple // ProcessingQueues with non-overlapping scope or be merged with another // ProcessingQueue ProcessingQueue interface { State() ProcessingQueueState Split(ProcessingQueueSplitPolicy) []ProcessingQueue Merge(ProcessingQueue) []ProcessingQueue AddTasks(map[task.Key]task.Task, task.Key) GetTask(task.Key) (task.Task, error) GetTasks() []task.Task UpdateAckLevel() (task.Key, int) // return new ack level and number of pending tasks // TODO: add Offload() method } // ProcessingQueueSplitPolicy determines if one ProcessingQueue should be split // into multiple ProcessingQueues ProcessingQueueSplitPolicy interface { Evaluate(ProcessingQueue) []ProcessingQueueState } // ProcessingQueueCollection manages a list of non-overlapping ProcessingQueues // and keep track of the current active ProcessingQueue ProcessingQueueCollection interface { Level() int Queues() []ProcessingQueue ActiveQueue() ProcessingQueue AddTasks(map[task.Key]task.Task, task.Key) GetTask(task.Key) (task.Task, error) GetTasks() []task.Task UpdateAckLevels() (task.Key, int) // return min of all new ack levels and number of total pending tasks Split(ProcessingQueueSplitPolicy) []ProcessingQueue Merge([]ProcessingQueue) // TODO: add Offload() method } // Processor is the interface for task queue processor Processor interface { common.Daemon FailoverDomain(domainIDs map[string]struct{}) NotifyNewTask(clusterName string, info *hcommon.NotifyTaskInfo) HandleAction(ctx context.Context, clusterName string, action *Action) (*ActionResult, error) LockTaskProcessing() UnlockTaskProcessing() } ) ================================================ FILE: service/history/queue/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package queue -source interface.go -destination interface_mock.go -self_package github.com/uber/cadence/service/history/queue // // Package queue is a generated GoMock package. package queue import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" common "github.com/uber/cadence/service/history/common" task "github.com/uber/cadence/service/history/task" ) // MockProcessingQueueState is a mock of ProcessingQueueState interface. type MockProcessingQueueState struct { ctrl *gomock.Controller recorder *MockProcessingQueueStateMockRecorder isgomock struct{} } // MockProcessingQueueStateMockRecorder is the mock recorder for MockProcessingQueueState. type MockProcessingQueueStateMockRecorder struct { mock *MockProcessingQueueState } // NewMockProcessingQueueState creates a new mock instance. func NewMockProcessingQueueState(ctrl *gomock.Controller) *MockProcessingQueueState { mock := &MockProcessingQueueState{ctrl: ctrl} mock.recorder = &MockProcessingQueueStateMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProcessingQueueState) EXPECT() *MockProcessingQueueStateMockRecorder { return m.recorder } // AckLevel mocks base method. func (m *MockProcessingQueueState) AckLevel() task.Key { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AckLevel") ret0, _ := ret[0].(task.Key) return ret0 } // AckLevel indicates an expected call of AckLevel. func (mr *MockProcessingQueueStateMockRecorder) AckLevel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AckLevel", reflect.TypeOf((*MockProcessingQueueState)(nil).AckLevel)) } // DomainFilter mocks base method. func (m *MockProcessingQueueState) DomainFilter() DomainFilter { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DomainFilter") ret0, _ := ret[0].(DomainFilter) return ret0 } // DomainFilter indicates an expected call of DomainFilter. func (mr *MockProcessingQueueStateMockRecorder) DomainFilter() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DomainFilter", reflect.TypeOf((*MockProcessingQueueState)(nil).DomainFilter)) } // Level mocks base method. func (m *MockProcessingQueueState) Level() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Level") ret0, _ := ret[0].(int) return ret0 } // Level indicates an expected call of Level. func (mr *MockProcessingQueueStateMockRecorder) Level() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Level", reflect.TypeOf((*MockProcessingQueueState)(nil).Level)) } // MaxLevel mocks base method. func (m *MockProcessingQueueState) MaxLevel() task.Key { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MaxLevel") ret0, _ := ret[0].(task.Key) return ret0 } // MaxLevel indicates an expected call of MaxLevel. func (mr *MockProcessingQueueStateMockRecorder) MaxLevel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxLevel", reflect.TypeOf((*MockProcessingQueueState)(nil).MaxLevel)) } // ReadLevel mocks base method. func (m *MockProcessingQueueState) ReadLevel() task.Key { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadLevel") ret0, _ := ret[0].(task.Key) return ret0 } // ReadLevel indicates an expected call of ReadLevel. func (mr *MockProcessingQueueStateMockRecorder) ReadLevel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadLevel", reflect.TypeOf((*MockProcessingQueueState)(nil).ReadLevel)) } // MockProcessingQueue is a mock of ProcessingQueue interface. type MockProcessingQueue struct { ctrl *gomock.Controller recorder *MockProcessingQueueMockRecorder isgomock struct{} } // MockProcessingQueueMockRecorder is the mock recorder for MockProcessingQueue. type MockProcessingQueueMockRecorder struct { mock *MockProcessingQueue } // NewMockProcessingQueue creates a new mock instance. func NewMockProcessingQueue(ctrl *gomock.Controller) *MockProcessingQueue { mock := &MockProcessingQueue{ctrl: ctrl} mock.recorder = &MockProcessingQueueMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProcessingQueue) EXPECT() *MockProcessingQueueMockRecorder { return m.recorder } // AddTasks mocks base method. func (m *MockProcessingQueue) AddTasks(arg0 map[task.Key]task.Task, arg1 task.Key) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddTasks", arg0, arg1) } // AddTasks indicates an expected call of AddTasks. func (mr *MockProcessingQueueMockRecorder) AddTasks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTasks", reflect.TypeOf((*MockProcessingQueue)(nil).AddTasks), arg0, arg1) } // GetTask mocks base method. func (m *MockProcessingQueue) GetTask(arg0 task.Key) (task.Task, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTask", arg0) ret0, _ := ret[0].(task.Task) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTask indicates an expected call of GetTask. func (mr *MockProcessingQueueMockRecorder) GetTask(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTask", reflect.TypeOf((*MockProcessingQueue)(nil).GetTask), arg0) } // GetTasks mocks base method. func (m *MockProcessingQueue) GetTasks() []task.Task { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasks") ret0, _ := ret[0].([]task.Task) return ret0 } // GetTasks indicates an expected call of GetTasks. func (mr *MockProcessingQueueMockRecorder) GetTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasks", reflect.TypeOf((*MockProcessingQueue)(nil).GetTasks)) } // Merge mocks base method. func (m *MockProcessingQueue) Merge(arg0 ProcessingQueue) []ProcessingQueue { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Merge", arg0) ret0, _ := ret[0].([]ProcessingQueue) return ret0 } // Merge indicates an expected call of Merge. func (mr *MockProcessingQueueMockRecorder) Merge(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Merge", reflect.TypeOf((*MockProcessingQueue)(nil).Merge), arg0) } // Split mocks base method. func (m *MockProcessingQueue) Split(arg0 ProcessingQueueSplitPolicy) []ProcessingQueue { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Split", arg0) ret0, _ := ret[0].([]ProcessingQueue) return ret0 } // Split indicates an expected call of Split. func (mr *MockProcessingQueueMockRecorder) Split(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Split", reflect.TypeOf((*MockProcessingQueue)(nil).Split), arg0) } // State mocks base method. func (m *MockProcessingQueue) State() ProcessingQueueState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "State") ret0, _ := ret[0].(ProcessingQueueState) return ret0 } // State indicates an expected call of State. func (mr *MockProcessingQueueMockRecorder) State() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "State", reflect.TypeOf((*MockProcessingQueue)(nil).State)) } // UpdateAckLevel mocks base method. func (m *MockProcessingQueue) UpdateAckLevel() (task.Key, int) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAckLevel") ret0, _ := ret[0].(task.Key) ret1, _ := ret[1].(int) return ret0, ret1 } // UpdateAckLevel indicates an expected call of UpdateAckLevel. func (mr *MockProcessingQueueMockRecorder) UpdateAckLevel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAckLevel", reflect.TypeOf((*MockProcessingQueue)(nil).UpdateAckLevel)) } // MockProcessingQueueSplitPolicy is a mock of ProcessingQueueSplitPolicy interface. type MockProcessingQueueSplitPolicy struct { ctrl *gomock.Controller recorder *MockProcessingQueueSplitPolicyMockRecorder isgomock struct{} } // MockProcessingQueueSplitPolicyMockRecorder is the mock recorder for MockProcessingQueueSplitPolicy. type MockProcessingQueueSplitPolicyMockRecorder struct { mock *MockProcessingQueueSplitPolicy } // NewMockProcessingQueueSplitPolicy creates a new mock instance. func NewMockProcessingQueueSplitPolicy(ctrl *gomock.Controller) *MockProcessingQueueSplitPolicy { mock := &MockProcessingQueueSplitPolicy{ctrl: ctrl} mock.recorder = &MockProcessingQueueSplitPolicyMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProcessingQueueSplitPolicy) EXPECT() *MockProcessingQueueSplitPolicyMockRecorder { return m.recorder } // Evaluate mocks base method. func (m *MockProcessingQueueSplitPolicy) Evaluate(arg0 ProcessingQueue) []ProcessingQueueState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Evaluate", arg0) ret0, _ := ret[0].([]ProcessingQueueState) return ret0 } // Evaluate indicates an expected call of Evaluate. func (mr *MockProcessingQueueSplitPolicyMockRecorder) Evaluate(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Evaluate", reflect.TypeOf((*MockProcessingQueueSplitPolicy)(nil).Evaluate), arg0) } // MockProcessingQueueCollection is a mock of ProcessingQueueCollection interface. type MockProcessingQueueCollection struct { ctrl *gomock.Controller recorder *MockProcessingQueueCollectionMockRecorder isgomock struct{} } // MockProcessingQueueCollectionMockRecorder is the mock recorder for MockProcessingQueueCollection. type MockProcessingQueueCollectionMockRecorder struct { mock *MockProcessingQueueCollection } // NewMockProcessingQueueCollection creates a new mock instance. func NewMockProcessingQueueCollection(ctrl *gomock.Controller) *MockProcessingQueueCollection { mock := &MockProcessingQueueCollection{ctrl: ctrl} mock.recorder = &MockProcessingQueueCollectionMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProcessingQueueCollection) EXPECT() *MockProcessingQueueCollectionMockRecorder { return m.recorder } // ActiveQueue mocks base method. func (m *MockProcessingQueueCollection) ActiveQueue() ProcessingQueue { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ActiveQueue") ret0, _ := ret[0].(ProcessingQueue) return ret0 } // ActiveQueue indicates an expected call of ActiveQueue. func (mr *MockProcessingQueueCollectionMockRecorder) ActiveQueue() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActiveQueue", reflect.TypeOf((*MockProcessingQueueCollection)(nil).ActiveQueue)) } // AddTasks mocks base method. func (m *MockProcessingQueueCollection) AddTasks(arg0 map[task.Key]task.Task, arg1 task.Key) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddTasks", arg0, arg1) } // AddTasks indicates an expected call of AddTasks. func (mr *MockProcessingQueueCollectionMockRecorder) AddTasks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTasks", reflect.TypeOf((*MockProcessingQueueCollection)(nil).AddTasks), arg0, arg1) } // GetTask mocks base method. func (m *MockProcessingQueueCollection) GetTask(arg0 task.Key) (task.Task, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTask", arg0) ret0, _ := ret[0].(task.Task) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTask indicates an expected call of GetTask. func (mr *MockProcessingQueueCollectionMockRecorder) GetTask(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTask", reflect.TypeOf((*MockProcessingQueueCollection)(nil).GetTask), arg0) } // GetTasks mocks base method. func (m *MockProcessingQueueCollection) GetTasks() []task.Task { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasks") ret0, _ := ret[0].([]task.Task) return ret0 } // GetTasks indicates an expected call of GetTasks. func (mr *MockProcessingQueueCollectionMockRecorder) GetTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasks", reflect.TypeOf((*MockProcessingQueueCollection)(nil).GetTasks)) } // Level mocks base method. func (m *MockProcessingQueueCollection) Level() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Level") ret0, _ := ret[0].(int) return ret0 } // Level indicates an expected call of Level. func (mr *MockProcessingQueueCollectionMockRecorder) Level() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Level", reflect.TypeOf((*MockProcessingQueueCollection)(nil).Level)) } // Merge mocks base method. func (m *MockProcessingQueueCollection) Merge(arg0 []ProcessingQueue) { m.ctrl.T.Helper() m.ctrl.Call(m, "Merge", arg0) } // Merge indicates an expected call of Merge. func (mr *MockProcessingQueueCollectionMockRecorder) Merge(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Merge", reflect.TypeOf((*MockProcessingQueueCollection)(nil).Merge), arg0) } // Queues mocks base method. func (m *MockProcessingQueueCollection) Queues() []ProcessingQueue { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Queues") ret0, _ := ret[0].([]ProcessingQueue) return ret0 } // Queues indicates an expected call of Queues. func (mr *MockProcessingQueueCollectionMockRecorder) Queues() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Queues", reflect.TypeOf((*MockProcessingQueueCollection)(nil).Queues)) } // Split mocks base method. func (m *MockProcessingQueueCollection) Split(arg0 ProcessingQueueSplitPolicy) []ProcessingQueue { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Split", arg0) ret0, _ := ret[0].([]ProcessingQueue) return ret0 } // Split indicates an expected call of Split. func (mr *MockProcessingQueueCollectionMockRecorder) Split(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Split", reflect.TypeOf((*MockProcessingQueueCollection)(nil).Split), arg0) } // UpdateAckLevels mocks base method. func (m *MockProcessingQueueCollection) UpdateAckLevels() (task.Key, int) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAckLevels") ret0, _ := ret[0].(task.Key) ret1, _ := ret[1].(int) return ret0, ret1 } // UpdateAckLevels indicates an expected call of UpdateAckLevels. func (mr *MockProcessingQueueCollectionMockRecorder) UpdateAckLevels() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAckLevels", reflect.TypeOf((*MockProcessingQueueCollection)(nil).UpdateAckLevels)) } // MockProcessor is a mock of Processor interface. type MockProcessor struct { ctrl *gomock.Controller recorder *MockProcessorMockRecorder isgomock struct{} } // MockProcessorMockRecorder is the mock recorder for MockProcessor. type MockProcessorMockRecorder struct { mock *MockProcessor } // NewMockProcessor creates a new mock instance. func NewMockProcessor(ctrl *gomock.Controller) *MockProcessor { mock := &MockProcessor{ctrl: ctrl} mock.recorder = &MockProcessorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProcessor) EXPECT() *MockProcessorMockRecorder { return m.recorder } // FailoverDomain mocks base method. func (m *MockProcessor) FailoverDomain(domainIDs map[string]struct{}) { m.ctrl.T.Helper() m.ctrl.Call(m, "FailoverDomain", domainIDs) } // FailoverDomain indicates an expected call of FailoverDomain. func (mr *MockProcessorMockRecorder) FailoverDomain(domainIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FailoverDomain", reflect.TypeOf((*MockProcessor)(nil).FailoverDomain), domainIDs) } // HandleAction mocks base method. func (m *MockProcessor) HandleAction(ctx context.Context, clusterName string, action *Action) (*ActionResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleAction", ctx, clusterName, action) ret0, _ := ret[0].(*ActionResult) ret1, _ := ret[1].(error) return ret0, ret1 } // HandleAction indicates an expected call of HandleAction. func (mr *MockProcessorMockRecorder) HandleAction(ctx, clusterName, action any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleAction", reflect.TypeOf((*MockProcessor)(nil).HandleAction), ctx, clusterName, action) } // LockTaskProcessing mocks base method. func (m *MockProcessor) LockTaskProcessing() { m.ctrl.T.Helper() m.ctrl.Call(m, "LockTaskProcessing") } // LockTaskProcessing indicates an expected call of LockTaskProcessing. func (mr *MockProcessorMockRecorder) LockTaskProcessing() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockTaskProcessing", reflect.TypeOf((*MockProcessor)(nil).LockTaskProcessing)) } // NotifyNewTask mocks base method. func (m *MockProcessor) NotifyNewTask(clusterName string, info *common.NotifyTaskInfo) { m.ctrl.T.Helper() m.ctrl.Call(m, "NotifyNewTask", clusterName, info) } // NotifyNewTask indicates an expected call of NotifyNewTask. func (mr *MockProcessorMockRecorder) NotifyNewTask(clusterName, info any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyNewTask", reflect.TypeOf((*MockProcessor)(nil).NotifyNewTask), clusterName, info) } // Start mocks base method. func (m *MockProcessor) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockProcessorMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockProcessor)(nil).Start)) } // Stop mocks base method. func (m *MockProcessor) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockProcessorMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockProcessor)(nil).Stop)) } // UnlockTaskProcessing mocks base method. func (m *MockProcessor) UnlockTaskProcessing() { m.ctrl.T.Helper() m.ctrl.Call(m, "UnlockTaskProcessing") } // UnlockTaskProcessing indicates an expected call of UnlockTaskProcessing. func (mr *MockProcessorMockRecorder) UnlockTaskProcessing() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnlockTaskProcessing", reflect.TypeOf((*MockProcessor)(nil).UnlockTaskProcessing)) } ================================================ FILE: service/history/queue/main_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "os" "testing" "go.uber.org/goleak" ) func TestMain(m *testing.M) { defer goleak.VerifyTestMain(m) os.Exit(m.Run()) } ================================================ FILE: service/history/queue/processing_queue.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package queue import ( "errors" "fmt" "sort" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" t "github.com/uber/cadence/common/task" "github.com/uber/cadence/service/history/task" ) var ( errTaskNotFound = errors.New("task not found") ) type ( processingQueueImpl struct { state *processingQueueStateImpl outstandingTasks map[task.Key]task.Task logger log.Logger metricsClient metrics.Client // TODO: emit metrics } ) // NewProcessingQueue creates a new processing queue based on its state func NewProcessingQueue( state ProcessingQueueState, logger log.Logger, metricsClient metrics.Client, ) ProcessingQueue { return newProcessingQueue( state, nil, logger, metricsClient, ) } func newProcessingQueue( state ProcessingQueueState, outstandingTasks map[task.Key]task.Task, logger log.Logger, metricsClient metrics.Client, ) *processingQueueImpl { if outstandingTasks == nil { outstandingTasks = make(map[task.Key]task.Task) } queue := &processingQueueImpl{ outstandingTasks: outstandingTasks, logger: logger, metricsClient: metricsClient, } // convert state to *processingQueueStateImpl type so that // queue implementation can change the state value if stateImpl, ok := state.(*processingQueueStateImpl); ok { queue.state = stateImpl } else { queue.state = copyQueueState(state) } if queue.state.readLevel.Less(queue.state.ackLevel) { logger.Fatal("ack level larger than readlevel when creating processing queue", tag.Error( fmt.Errorf("ack level: %v, read level: %v", queue.state.ackLevel, queue.state.readLevel), )) } return queue } func (q *processingQueueImpl) State() ProcessingQueueState { return q.state } func (q *processingQueueImpl) Split(policy ProcessingQueueSplitPolicy) []ProcessingQueue { newQueueStates := policy.Evaluate(q) if len(newQueueStates) == 0 { // no need to split, return self return []ProcessingQueue{q} } return splitProcessingQueue([]*processingQueueImpl{q}, newQueueStates, q.logger, q.metricsClient) } func (q *processingQueueImpl) Merge(queue ProcessingQueue) []ProcessingQueue { q1, q2 := q, queue.(*processingQueueImpl) if q1.State().Level() != q2.State().Level() { errMsg := "Processing queue encountered a queue from different level during merge" q.logger.Error(errMsg, tag.Error( fmt.Errorf("current queue level: %v, incoming queue level: %v", q1.state.level, q2.state.level), )) panic(errMsg) } if !q1.state.ackLevel.Less(q2.state.maxLevel) || !q2.state.ackLevel.Less(q1.state.maxLevel) { // one queue's ackLevel is larger or equal than the other one's maxLevel // this means there's no overlap between two queues return []ProcessingQueue{q1, q2} } // generate new queue states for merged queues newQueueStates := []ProcessingQueueState{} if !taskKeyEquals(q1.state.ackLevel, q2.state.ackLevel) { if q2.state.ackLevel.Less(q1.state.ackLevel) { q1, q2 = q2, q1 } newQueueStates = append(newQueueStates, newProcessingQueueState( q1.state.level, q1.state.ackLevel, minTaskKey(q1.state.readLevel, q2.state.ackLevel), q2.state.ackLevel, q1.state.domainFilter.copy(), )) } if !taskKeyEquals(q1.state.maxLevel, q2.state.maxLevel) { if q1.state.maxLevel.Less(q2.state.maxLevel) { q1, q2 = q2, q1 } newQueueStates = append(newQueueStates, newProcessingQueueState( q1.state.level, q2.state.maxLevel, maxTaskKey(q1.state.readLevel, q2.state.maxLevel), q1.state.maxLevel, q1.state.domainFilter.copy(), )) } overlappingQueueAckLevel := maxTaskKey(q1.state.ackLevel, q2.state.ackLevel) newQueueStates = append(newQueueStates, newProcessingQueueState( q1.state.level, overlappingQueueAckLevel, maxTaskKey(minTaskKey(q1.state.readLevel, q2.state.readLevel), overlappingQueueAckLevel), minTaskKey(q1.state.maxLevel, q2.state.maxLevel), q1.state.domainFilter.Merge(q2.state.domainFilter), )) for _, state := range newQueueStates { if state.ReadLevel().Less(state.AckLevel()) || state.MaxLevel().Less(state.ReadLevel()) { q.logger.Fatal("invalid processing queue merge result", tag.Error( fmt.Errorf("q1: %v, q2: %v, merge result: %v", q1.state, q2.state, newQueueStates), )) } } return splitProcessingQueue([]*processingQueueImpl{q1, q2}, newQueueStates, q.logger, q.metricsClient) } func (q *processingQueueImpl) AddTasks(tasks map[task.Key]task.Task, newReadLevel task.Key) { if newReadLevel.Less(q.state.readLevel) { q.logger.Fatal("processing queue read level moved backward", tag.Error( fmt.Errorf("current read level: %v, new read level: %v", q.state.readLevel, newReadLevel), )) } for key, task := range tasks { if _, loaded := q.outstandingTasks[key]; loaded { // TODO: this means the task has been submitted before, we should mark the task state accordingly and // do not submit this task again in transfer/timer queue processor base q.logger.Debug(fmt.Sprintf("Skipping task: %+v. DomainID: %v, WorkflowID: %v, RunID: %v, Type: %v", key, task.GetDomainID(), task.GetWorkflowID(), task.GetRunID(), task.GetTaskType())) continue } if !taskBelongsToProcessQueue(q.state, key, task) { errMsg := "Processing queue encountered a task doesn't belong to its scope" q.logger.Error(errMsg, tag.Error( fmt.Errorf("processing queue state: %+v, key: %+v, task: %+v", q.state, key, task), )) continue } q.outstandingTasks[key] = task } q.state.readLevel = newReadLevel } func (q *processingQueueImpl) GetTask(key task.Key) (task.Task, error) { if task, ok := q.outstandingTasks[key]; ok { return task, nil } return nil, errTaskNotFound } func (q *processingQueueImpl) GetTasks() []task.Task { var outstandingTask []task.Task for _, task := range q.outstandingTasks { outstandingTask = append(outstandingTask, task) } return outstandingTask } func (q *processingQueueImpl) UpdateAckLevel() (task.Key, int) { keys := make([]task.Key, 0, len(q.outstandingTasks)) for key := range q.outstandingTasks { keys = append(keys, key) } sort.Slice(keys, func(i, j int) bool { return keys[i].Less(keys[j]) }) var idx int var key task.Key for idx, key = range keys { if q.state.readLevel.Less(key) { // this can happen as during merge read level can move backward. // besides that, for timer task key, readLevel is expected to be less than task key // as the taskID for read level is always 0. This means we can potentially buffer // more timer tasks in memory. If this becomes a problem, we can change this logic. break } if q.outstandingTasks[key].State() != t.TaskStateAcked { break } q.state.ackLevel = key delete(q.outstandingTasks, key) } // The following loop attempts to delete tasks beyond ack level but has already been acked. // To ensure ack level can be advanced as quick as possible, for a series of acked tasks, // we still keep the last one in memory so that when previous pending tasks are acked, ack level // can be advanced without wait for other tasks. // Also only delete tasks less than read level as the sequence beyond read level may change. // // As an example, say currently we have 9 tasks: 1 2 3 4 5 6 7 9 10. Ack level is 0, read level is 7, // task 1 2 6 10 is pending and the rest have been acked. // We can delete task 3 4 but not 5 as otherwise even if task 1 and 2 were acked later, ack level would // at most be 2 until task 6 is acked, while ideally it should be 5. // We also can't delete task 7, because it's possible that task 8 will be loaded later. If task 7 got deleted, // ack level can only be advanced to 6 instead of 7. for idx < len(keys)-1 && keys[idx].Less(q.state.readLevel) { if q.outstandingTasks[keys[idx]].State() == t.TaskStateAcked && q.outstandingTasks[keys[idx+1]].State() == t.TaskStateAcked { delete(q.outstandingTasks, keys[idx]) } idx++ } if len(q.outstandingTasks) == 0 { q.state.ackLevel = q.state.readLevel } if timerKey, ok := q.state.ackLevel.(timerTaskKey); ok { q.state.ackLevel = newTimerTaskKey(timerKey.visibilityTimestamp, 0) } if q.state.readLevel.Less(q.state.ackLevel) { q.logger.Fatal("ack level moved beyond read level", tag.Error( fmt.Errorf("processing queue state: %v", q.state), )) } return q.state.ackLevel, len(q.outstandingTasks) } func splitProcessingQueue( queues []*processingQueueImpl, newQueueStates []ProcessingQueueState, logger log.Logger, metricsClient metrics.Client, ) []ProcessingQueue { newQueueTasks := make([]map[task.Key]task.Task, 0, len(newQueueStates)) for i := 0; i != len(newQueueStates); i++ { newQueueTasks = append(newQueueTasks, make(map[task.Key]task.Task)) } for _, queue := range queues { SplitTaskLoop: for key, task := range queue.outstandingTasks { for i, state := range newQueueStates { if taskBelongsToProcessQueue(state, key, task) { newQueueTasks[i][key] = task continue SplitTaskLoop } } // if code reaches there it means the task doesn't belongs to any new queue. // there's must be a bug in the code for generating the newQueueStates // log error, skip the split and return current queues as result currentQueues := make([]ProcessingQueue, 0, len(newQueueStates)) currentQueueStates := make([]ProcessingQueueState, 0, len(newQueueStates)) for _, q := range queues { currentQueues = append(currentQueues, q) currentQueueStates = append(currentQueueStates, queue.State()) } logger.Error("Processing queue encountered an error during split or merge.", tag.Error( fmt.Errorf("current queue state: %+v, new queue state: %+v", currentQueueStates, newQueueStates), )) return currentQueues } } newQueues := make([]ProcessingQueue, 0, len(newQueueStates)) for i, state := range newQueueStates { queue := newProcessingQueue( state, newQueueTasks[i], logger, metricsClient, ) newQueues = append(newQueues, queue) } return newQueues } func taskBelongsToProcessQueue(state ProcessingQueueState, key task.Key, task task.Task) bool { return state.DomainFilter().Filter(task.GetDomainID()) && state.AckLevel().Less(key) && !state.MaxLevel().Less(key) } func taskKeyEquals(key1 task.Key, key2 task.Key) bool { return !key1.Less(key2) && !key2.Less(key1) } func minTaskKey(key1 task.Key, key2 task.Key) task.Key { if key1.Less(key2) { return key1 } return key2 } func maxTaskKey(key1 task.Key, key2 task.Key) task.Key { if key1.Less(key2) { return key2 } return key1 } ================================================ FILE: service/history/queue/processing_queue_collection.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "fmt" "sort" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/history/task" ) type processingQueueCollection struct { level int queues []ProcessingQueue activeQueue ProcessingQueue } // NewProcessingQueueCollection creates a new collection for non-overlapping queues func NewProcessingQueueCollection(level int, queues []ProcessingQueue) ProcessingQueueCollection { sortProcessingQueue(queues) queueCollection := &processingQueueCollection{ level: level, queues: queues, } queueCollection.resetActiveQueue() return queueCollection } func newProcessingQueueCollections( processingQueueStates []ProcessingQueueState, logger log.Logger, metricsClient metrics.Client, ) []ProcessingQueueCollection { processingQueuesMap := make(map[int][]ProcessingQueue) // level -> state for _, queueState := range processingQueueStates { processingQueuesMap[queueState.Level()] = append(processingQueuesMap[queueState.Level()], NewProcessingQueue( queueState, logger, metricsClient, )) } processingQueueCollections := make([]ProcessingQueueCollection, 0, len(processingQueuesMap)) for level, queues := range processingQueuesMap { processingQueueCollections = append(processingQueueCollections, NewProcessingQueueCollection( level, queues, )) } return processingQueueCollections } func (c *processingQueueCollection) Level() int { return c.level } func (c *processingQueueCollection) Queues() []ProcessingQueue { return c.queues } func (c *processingQueueCollection) ActiveQueue() ProcessingQueue { return c.activeQueue } func (c *processingQueueCollection) AddTasks(tasks map[task.Key]task.Task, newReadLevel task.Key) { activeQueue := c.ActiveQueue() activeQueue.AddTasks(tasks, newReadLevel) if taskKeyEquals(activeQueue.State().ReadLevel(), activeQueue.State().MaxLevel()) { c.resetActiveQueue() } } func (c *processingQueueCollection) GetTask(key task.Key) (task.Task, error) { for _, queue := range c.Queues() { if task, err := queue.GetTask(key); err == nil { return task, nil } } return nil, errTaskNotFound } func (c *processingQueueCollection) GetTasks() []task.Task { var outstandingTask []task.Task for _, queue := range c.Queues() { outstandingTask = append(outstandingTask, queue.GetTasks()...) } return outstandingTask } func (c *processingQueueCollection) UpdateAckLevels() (task.Key, int) { if len(c.queues) == 0 { return nil, 0 } remainingQueues := make([]ProcessingQueue, 0, len(c.queues)) totalPendingTasks := 0 var minAckLevel task.Key for _, queue := range c.queues { ackLevel, numPendingTasks := queue.UpdateAckLevel() if taskKeyEquals(ackLevel, queue.State().MaxLevel()) { continue } remainingQueues = append(remainingQueues, queue) totalPendingTasks += numPendingTasks if minAckLevel == nil { minAckLevel = ackLevel } else { minAckLevel = minTaskKey(minAckLevel, ackLevel) } } c.queues = remainingQueues return minAckLevel, totalPendingTasks } func (c *processingQueueCollection) Split(policy ProcessingQueueSplitPolicy) []ProcessingQueue { if len(c.queues) == 0 { return nil } newQueues := make([]ProcessingQueue, 0, len(c.queues)) nextLevelQueues := []ProcessingQueue{} for _, queue := range c.queues { splitQueues := queue.Split(policy) sortProcessingQueue(splitQueues) for _, splitQueue := range splitQueues { if splitQueue.State().Level() != c.level { nextLevelQueues = append(nextLevelQueues, splitQueue) } else { newQueues = append(newQueues, splitQueue) } } } c.queues = newQueues c.resetActiveQueue() return nextLevelQueues } func (c *processingQueueCollection) Merge(incomingQueues []ProcessingQueue) { sortProcessingQueue(incomingQueues) newQueues := make([]ProcessingQueue, 0, len(c.queues)+len(incomingQueues)) currentQueueIdx := 0 incomingQueueIdx := 0 for incomingQueueIdx < len(incomingQueues) && currentQueueIdx < len(c.queues) { mergedQueues := c.queues[currentQueueIdx].Merge(incomingQueues[incomingQueueIdx]) sortProcessingQueue(mergedQueues) newQueues = append(newQueues, mergedQueues[:len(mergedQueues)-1]...) lastMergedQueue := mergedQueues[len(mergedQueues)-1] overlapWithCurrentQueue := currentQueueIdx+1 != len(c.queues) && c.queues[currentQueueIdx+1].State().AckLevel().Less(lastMergedQueue.State().MaxLevel()) overlapWithIncomingQueue := incomingQueueIdx+1 != len(incomingQueues) && incomingQueues[incomingQueueIdx+1].State().AckLevel().Less(lastMergedQueue.State().MaxLevel()) if !overlapWithCurrentQueue && !overlapWithIncomingQueue { newQueues = append(newQueues, lastMergedQueue) incomingQueueIdx++ currentQueueIdx++ } else if overlapWithCurrentQueue { incomingQueues[incomingQueueIdx] = lastMergedQueue currentQueueIdx++ } else { c.queues[currentQueueIdx] = lastMergedQueue incomingQueueIdx++ } } if incomingQueueIdx < len(incomingQueues) { newQueues = append(newQueues, incomingQueues[incomingQueueIdx:]...) } if currentQueueIdx < len(c.queues) { newQueues = append(newQueues, c.queues[currentQueueIdx:]...) } c.queues = newQueues // make sure the result is ordered and disjoint for idx := 0; idx < len(c.queues)-1; idx++ { if c.queues[idx+1].State().AckLevel().Less(c.queues[idx].State().MaxLevel()) { errMsg := "" for _, q := range c.queues { errMsg += fmt.Sprintf("%v ", q) } panic("invalid processing queue merge result: " + errMsg) } } c.resetActiveQueue() } func (c *processingQueueCollection) resetActiveQueue() { for _, queue := range c.queues { if !taskKeyEquals(queue.State().ReadLevel(), queue.State().MaxLevel()) { c.activeQueue = queue return } } c.activeQueue = nil } func sortProcessingQueue(queues []ProcessingQueue) { sort.Slice(queues, func(i, j int) bool { if queues[i].State().Level() == queues[j].State().Level() { return queues[i].State().AckLevel().Less(queues[j].State().AckLevel()) } return queues[i].State().Level() < queues[j].State().Level() }) } ================================================ FILE: service/history/queue/processing_queue_collection_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/service/history/task" ) type ( processingQueueCollectionSuite struct { suite.Suite *require.Assertions controller *gomock.Controller level int } ) func TestProcessingQueueCollectionSuite(t *testing.T) { s := new(processingQueueCollectionSuite) suite.Run(t, s) } func (s *processingQueueCollectionSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.level = 0 } func (s *processingQueueCollectionSuite) TearDownTest() { s.controller.Finish() } func (s *processingQueueCollectionSuite) TestNewCollection_EmptyQueues() { queueCollection := NewProcessingQueueCollection(s.level, nil).(*processingQueueCollection) s.Nil(queueCollection.ActiveQueue()) } func (s *processingQueueCollectionSuite) TestNewCollection_OutOfOrderQueues() { totalQueues := 4 mockQueues := []ProcessingQueue{} for i := 0; i != totalQueues; i++ { mockQueues = append(mockQueues, NewMockProcessingQueue(s.controller)) } mockQueues[0].(*MockProcessingQueue).EXPECT().State().Return(newProcessingQueueState( s.level, testKey{ID: 20}, testKey{ID: 25}, testKey{ID: 30}, DomainFilter{}, )).AnyTimes() mockQueues[1].(*MockProcessingQueue).EXPECT().State().Return(newProcessingQueueState( s.level, testKey{ID: 3}, testKey{ID: 10}, testKey{ID: 10}, DomainFilter{}, )).AnyTimes() mockQueues[2].(*MockProcessingQueue).EXPECT().State().Return(newProcessingQueueState( s.level, testKey{ID: 30}, testKey{ID: 30}, testKey{ID: 40}, DomainFilter{}, )).AnyTimes() mockQueues[3].(*MockProcessingQueue).EXPECT().State().Return(newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 20}, testKey{ID: 20}, DomainFilter{}, )).AnyTimes() expectedActiveQueue := mockQueues[0] queueCollection := NewProcessingQueueCollection(s.level, mockQueues).(*processingQueueCollection) s.Equal(expectedActiveQueue.State(), queueCollection.ActiveQueue().State()) s.True(s.isQueuesSorted(queueCollection.queues)) } func (s *processingQueueCollectionSuite) TestAddTasks_ReadNotFinished() { totalQueues := 4 currentActiveIdx := 1 newReadLevel := testKey{ID: 9} mockQueues := []*MockProcessingQueue{} for i := 0; i != totalQueues; i++ { mockQueues = append(mockQueues, NewMockProcessingQueue(s.controller)) } mockQueues[currentActiveIdx].EXPECT().AddTasks(gomock.Any(), newReadLevel).Times(1) mockQueues[currentActiveIdx].EXPECT().State().Return(newProcessingQueueState( s.level, testKey{ID: 3}, newReadLevel, testKey{ID: 10}, DomainFilter{}, )).AnyTimes() queueCollection := s.newTestProcessingQueueCollection(s.level, mockQueues) queueCollection.activeQueue = mockQueues[currentActiveIdx] queueCollection.AddTasks(map[task.Key]task.Task{}, newReadLevel) s.Equal(mockQueues[currentActiveIdx].State(), queueCollection.ActiveQueue().State()) } func (s *processingQueueCollectionSuite) TestAddTask_ReadFinished() { totalQueues := 4 currentActiveIdx := 1 newReadLevel := testKey{ID: 10} mockQueues := []*MockProcessingQueue{} for i := 0; i != totalQueues; i++ { mockQueues = append(mockQueues, NewMockProcessingQueue(s.controller)) } mockQueues[currentActiveIdx].EXPECT().AddTasks(gomock.Any(), newReadLevel).Times(1) for i := 0; i != totalQueues; i++ { mockQueues[i].EXPECT().State().Return(newProcessingQueueState( s.level, testKey{ID: 3}, newReadLevel, testKey{ID: 10}, DomainFilter{}, )).AnyTimes() } queueCollection := s.newTestProcessingQueueCollection(s.level, mockQueues) queueCollection.activeQueue = mockQueues[currentActiveIdx] queueCollection.AddTasks(map[task.Key]task.Task{}, newReadLevel) s.Nil(queueCollection.ActiveQueue()) } func (s *processingQueueCollectionSuite) TestGetTask_Success() { totalQueues := 4 currentActiveIdx := 1 mockTask := task.NewMockTask(s.controller) mockQueues := []*MockProcessingQueue{} for i := 0; i != totalQueues; i++ { mockQueues = append(mockQueues, NewMockProcessingQueue(s.controller)) } mockQueues[0].EXPECT().GetTask(gomock.Any()).Return(mockTask, nil).Times(1) for i := 1; i != totalQueues; i++ { mockQueues[i].EXPECT().GetTask(gomock.Any()).Return(nil, errTaskNotFound).AnyTimes() } queueCollection := s.newTestProcessingQueueCollection(s.level, mockQueues) queueCollection.activeQueue = mockQueues[currentActiveIdx] newTask, err := queueCollection.GetTask(testKey{ID: 1}) s.NoError(err) s.Equal(mockTask, newTask) } func (s *processingQueueCollectionSuite) TestGetTask_NoTaskFound_Fail() { totalQueues := 4 currentActiveIdx := 1 mockQueues := []*MockProcessingQueue{} for i := 0; i != totalQueues; i++ { mockQueues = append(mockQueues, NewMockProcessingQueue(s.controller)) } for i := 0; i != totalQueues; i++ { mockQueues[i].EXPECT().GetTask(gomock.Any()).Return(nil, errTaskNotFound).AnyTimes() } queueCollection := s.newTestProcessingQueueCollection(s.level, mockQueues) queueCollection.activeQueue = mockQueues[currentActiveIdx] newTask, err := queueCollection.GetTask(testKey{ID: 1}) s.Error(err) s.Nil(newTask) } func (s *processingQueueCollectionSuite) TestGetTasks_Success() { totalQueues := 4 currentActiveIdx := 1 mockQueues := []*MockProcessingQueue{} mockTasks := []task.Task{} for i := 0; i != totalQueues; i++ { mockQueues = append(mockQueues, NewMockProcessingQueue(s.controller)) mockTasks = append(mockTasks, task.NewMockTask(s.controller)) } for i := 0; i != totalQueues; i++ { mockQueues[i].EXPECT().GetTasks().Return([]task.Task{mockTasks[i]}).Times(1) } queueCollection := s.newTestProcessingQueueCollection(s.level, mockQueues) queueCollection.activeQueue = mockQueues[currentActiveIdx] newTasks := queueCollection.GetTasks() s.Equal(mockTasks, newTasks) } func (s *processingQueueCollectionSuite) TestGetTasks_EmptyTask() { totalQueues := 4 currentActiveIdx := 1 mockQueues := []*MockProcessingQueue{} for i := 0; i != totalQueues; i++ { mockQueues = append(mockQueues, NewMockProcessingQueue(s.controller)) } for i := 0; i != totalQueues; i++ { mockQueues[i].EXPECT().GetTasks().Return([]task.Task{}).Times(1) } queueCollection := s.newTestProcessingQueueCollection(s.level, mockQueues) queueCollection.activeQueue = mockQueues[currentActiveIdx] newTasks := queueCollection.GetTasks() s.Equal(0, len(newTasks)) } func (s *processingQueueCollectionSuite) TestUpdateAckLevels() { totalQueues := 5 currentActiveIdx := 1 mockQueues := []*MockProcessingQueue{} for i := 0; i != totalQueues; i++ { mockQueues = append(mockQueues, NewMockProcessingQueue(s.controller)) } finishedQueueIdx := map[int]struct{}{0: {}, 2: {}, 3: {}} for i := 0; i != totalQueues; i++ { if _, ok := finishedQueueIdx[i]; ok { mockQueues[i].EXPECT().UpdateAckLevel().Return(testKey{ID: i}, 0).Times(1) mockQueues[i].EXPECT().State().Return(newProcessingQueueState( s.level, testKey{ID: i}, testKey{ID: i}, testKey{ID: i}, DomainFilter{}, )).AnyTimes() } else { mockQueues[i].EXPECT().UpdateAckLevel().Return(testKey{ID: i - i}, 1).Times(1) mockQueues[i].EXPECT().State().Return(newProcessingQueueState( s.level, testKey{ID: i - 1}, testKey{ID: i}, testKey{ID: i}, DomainFilter{}, )).AnyTimes() } } expectedActiveQueue := mockQueues[1] queueCollection := s.newTestProcessingQueueCollection(s.level, mockQueues) queueCollection.activeQueue = mockQueues[currentActiveIdx] ackLevel, totalPendingTasks := queueCollection.UpdateAckLevels() s.Equal(testKey{ID: 0}, ackLevel) s.Equal(totalQueues-len(finishedQueueIdx), totalPendingTasks) s.Len(queueCollection.queues, totalQueues-len(finishedQueueIdx)) s.Equal(expectedActiveQueue.State(), queueCollection.ActiveQueue().State()) } func (s *processingQueueCollectionSuite) TestSplit() { testCases := []struct { currentQueueStates []ProcessingQueueState splitResults [][]ProcessingQueueState expectedActiveQueueState ProcessingQueueState expectedNewQueueStates []ProcessingQueueState expectedNextLevelQueueStates []ProcessingQueueState }{ {}, // empty queue collection { currentQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain2": {}}}, ), }, splitResults: [][]ProcessingQueueState{ { newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), newProcessingQueueState( s.level+1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), }, }, expectedActiveQueueState: newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), expectedNewQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), }, expectedNextLevelQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level+1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), }, }, { currentQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 10}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain2": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 15}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain2": {}, "domain3": {}}}, ), }, splitResults: [][]ProcessingQueueState{ { newProcessingQueueState( s.level+1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 5}, testKey{ID: 10}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain2": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), }, { newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 15}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain2": {}, "domain3": {}}}, ), }, }, expectedActiveQueueState: newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 15}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain2": {}, "domain3": {}}}, ), expectedNewQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 5}, testKey{ID: 10}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain2": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 15}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain2": {}, "domain3": {}}}, ), }, expectedNextLevelQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level+1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), }, }, } for _, tc := range testCases { mockQueues := []ProcessingQueue{} for idx, queueState := range tc.currentQueueStates { mockQueue := NewMockProcessingQueue(s.controller) mockQueue.EXPECT().State().Return(queueState).AnyTimes() splitMockQueues := []ProcessingQueue{} for _, splitQueueState := range tc.splitResults[idx] { splitMockQueue := NewMockProcessingQueue(s.controller) splitMockQueue.EXPECT().State().Return(splitQueueState).AnyTimes() splitMockQueues = append(splitMockQueues, splitMockQueue) } mockQueue.EXPECT().Split(gomock.Any()).Return(splitMockQueues) mockQueues = append(mockQueues, mockQueue) } queueCollection := NewProcessingQueueCollection(s.level, mockQueues).(*processingQueueCollection) nextLevelQueues := queueCollection.Split(NewMockProcessingQueueSplitPolicy(s.controller)) if tc.expectedActiveQueueState != nil { s.Equal(tc.expectedActiveQueueState, queueCollection.ActiveQueue().State()) } for idx, expectedState := range tc.expectedNewQueueStates { s.Equal(expectedState, queueCollection.queues[idx].State()) } for idx, expectedState := range tc.expectedNextLevelQueueStates { s.Equal(expectedState, nextLevelQueues[idx].State()) } } } func (s *processingQueueCollectionSuite) TestMerge() { testCases := []struct { currentQueueStates []ProcessingQueueState incomingQueueStates []ProcessingQueueState expectedActiveQueueState ProcessingQueueState expectedNewQueueStates []ProcessingQueueState }{ {}, // empty queue collection { currentQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 20}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), }, incomingQueueStates: nil, expectedActiveQueueState: nil, expectedNewQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 20}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), }, }, { currentQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 20}, testKey{ID: 25}, testKey{ID: 30}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), }, incomingQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 20}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), }, expectedActiveQueueState: newProcessingQueueState( s.level, testKey{ID: 20}, testKey{ID: 25}, testKey{ID: 30}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), expectedNewQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 20}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 20}, testKey{ID: 25}, testKey{ID: 30}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), }, }, { currentQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 10}, testKey{ID: 50}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), }, incomingQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 20}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), }, expectedActiveQueueState: newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 10}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain2": {}}}, ), expectedNewQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 10}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 10}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain2": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 20}, testKey{ID: 20}, testKey{ID: 50}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), }, }, { currentQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 20}, testKey{ID: 20}, testKey{ID: 30}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 60}, testKey{ID: 75}, testKey{ID: 70}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), }, incomingQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 8}, testKey{ID: 35}, testKey{ID: 50}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 80}, testKey{ID: 90}, testKey{ID: 100}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), }, expectedActiveQueueState: newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 8}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), expectedNewQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 8}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 8}, testKey{ID: 8}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 20}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 20}, testKey{ID: 20}, testKey{ID: 30}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}, "domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 30}, testKey{ID: 35}, testKey{ID: 50}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 60}, testKey{ID: 75}, testKey{ID: 70}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 80}, testKey{ID: 90}, testKey{ID: 100}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), }, }, { currentQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 15}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 30}, testKey{ID: 40}, testKey{ID: 50}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 60}, testKey{ID: 65}, testKey{ID: 70}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}}}, ), }, incomingQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 15}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 18}, testKey{ID: 18}, testKey{ID: 100}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), }, expectedActiveQueueState: newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), expectedNewQueueStates: []ProcessingQueueState{ newProcessingQueueState( s.level, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 10}, testKey{ID: 10}, testKey{ID: 15}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 15}, testKey{ID: 15}, testKey{ID: 18}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 18}, testKey{ID: 18}, testKey{ID: 20}, DomainFilter{DomainIDs: map[string]struct{}{"domain1": {}, "domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 20}, testKey{ID: 20}, testKey{ID: 30}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 30}, testKey{ID: 30}, testKey{ID: 50}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}, "domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 50}, testKey{ID: 50}, testKey{ID: 60}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 60}, testKey{ID: 60}, testKey{ID: 70}, DomainFilter{DomainIDs: map[string]struct{}{"domain2": {}, "domain3": {}}}, ), newProcessingQueueState( s.level, testKey{ID: 70}, testKey{ID: 70}, testKey{ID: 100}, DomainFilter{DomainIDs: map[string]struct{}{"domain3": {}}}, ), }, }, } for _, tc := range testCases { queues := []ProcessingQueue{} for _, queueState := range tc.currentQueueStates { queue := NewProcessingQueue(queueState, nil, nil) queues = append(queues, queue) } incomingQueues := []ProcessingQueue{} for _, queueState := range tc.incomingQueueStates { incomingQueue := NewProcessingQueue(queueState, nil, nil) incomingQueues = append(incomingQueues, incomingQueue) } queueCollection := NewProcessingQueueCollection(s.level, queues).(*processingQueueCollection) queueCollection.Merge(incomingQueues) if tc.expectedActiveQueueState != nil { s.Equal(tc.expectedActiveQueueState, queueCollection.ActiveQueue().State()) } for idx, expectedState := range tc.expectedNewQueueStates { s.Equal(expectedState, queueCollection.queues[idx].State()) } } } func (s *processingQueueCollectionSuite) isQueuesSorted( queues []ProcessingQueue, ) bool { if len(queues) <= 1 { return true } for i := 0; i != len(queues)-1; i++ { if !queues[i].State().AckLevel().Less(queues[i+1].State().AckLevel()) || queues[i+1].State().AckLevel().Less(queues[i].State().MaxLevel()) { return false } } return true } func (s *processingQueueCollectionSuite) newTestProcessingQueueCollection( level int, mockQueues []*MockProcessingQueue, ) *processingQueueCollection { queues := make([]ProcessingQueue, 0, len(mockQueues)) for _, mockQueue := range mockQueues { queues = append(queues, mockQueue) } return &processingQueueCollection{ level: level, queues: queues, } } ================================================ FILE: service/history/queue/processing_queue_state.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package queue import ( "fmt" "github.com/uber/cadence/service/history/task" ) type processingQueueStateImpl struct { level int ackLevel task.Key readLevel task.Key maxLevel task.Key domainFilter DomainFilter } // NewProcessingQueueState creates a new state instance for processing queue // readLevel will be set to the same value as ackLevel func NewProcessingQueueState( level int, ackLevel task.Key, maxLevel task.Key, domainFilter DomainFilter, ) ProcessingQueueState { return newProcessingQueueState( level, ackLevel, ackLevel, maxLevel, domainFilter, ) } func newProcessingQueueState( level int, ackLevel task.Key, readLevel task.Key, maxLevel task.Key, domainFilter DomainFilter, ) *processingQueueStateImpl { return &processingQueueStateImpl{ level: level, ackLevel: ackLevel, readLevel: readLevel, maxLevel: maxLevel, domainFilter: domainFilter, } } func (s *processingQueueStateImpl) Level() int { return s.level } func (s *processingQueueStateImpl) MaxLevel() task.Key { return s.maxLevel } func (s *processingQueueStateImpl) AckLevel() task.Key { return s.ackLevel } func (s *processingQueueStateImpl) ReadLevel() task.Key { return s.readLevel } func (s *processingQueueStateImpl) DomainFilter() DomainFilter { return s.domainFilter } func (s *processingQueueStateImpl) String() string { return fmt.Sprintf("&{level: %+v, ackLevel: %+v, readLevel: %+v, maxLevel: %+v, domainFilter: %+v}", s.level, s.ackLevel, s.readLevel, s.maxLevel, s.domainFilter, ) } func copyQueueState(state ProcessingQueueState) *processingQueueStateImpl { return newProcessingQueueState( state.Level(), state.AckLevel(), state.ReadLevel(), state.MaxLevel(), state.DomainFilter(), ) } ================================================ FILE: service/history/queue/processing_queue_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package queue import ( "sort" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" t "github.com/uber/cadence/common/task" "github.com/uber/cadence/service/history/task" ) type ( processingQueueSuite struct { suite.Suite *require.Assertions controller *gomock.Controller logger log.Logger metricsClient metrics.Client } testKey struct { ID int } ) func TestProcessingQueueSuite(t *testing.T) { s := new(processingQueueSuite) suite.Run(t, s) } func (s *processingQueueSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.logger = testlogger.New(s.Suite.T()) s.metricsClient = metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) } func (s *processingQueueSuite) TearDownTest() { s.controller.Finish() } func (s *processingQueueSuite) TestAddTasks() { ackLevel := testKey{ID: 1} maxLevel := testKey{ID: 10} taskKeys := []task.Key{ testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 5}, testKey{ID: 9}, } tasks := make(map[task.Key]task.Task) for _, key := range taskKeys { mockTask := task.NewMockTask(s.controller) mockTask.EXPECT().GetDomainID().Return("some random domainID").AnyTimes() mockTask.EXPECT().GetWorkflowID().Return("some random workflowID").AnyTimes() mockTask.EXPECT().GetRunID().Return("some random runID").AnyTimes() mockTask.EXPECT().GetTaskType().Return(0).AnyTimes() tasks[key] = mockTask } queue := s.newTestProcessingQueue( 0, ackLevel, ackLevel, maxLevel, NewDomainFilter(nil, true), make(map[task.Key]task.Task), ) newReadLevel := testKey{ID: 10} queue.AddTasks(tasks, newReadLevel) s.Len(queue.outstandingTasks, len(taskKeys)) s.Equal(newReadLevel, queue.state.readLevel) // add the same set of tasks again, should have no effect queue.AddTasks(tasks, newReadLevel) s.Len(queue.outstandingTasks, len(taskKeys)) s.Equal(newReadLevel, queue.state.readLevel) } func (s *processingQueueSuite) TestGetTask_TaskNotFound_Error() { ackLevel := testKey{ID: 1} maxLevel := testKey{ID: 10} taskKeys := []task.Key{ testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 5}, testKey{ID: 9}, } tasks := make(map[task.Key]task.Task) for _, key := range taskKeys { mockTask := task.NewMockTask(s.controller) mockTask.EXPECT().GetDomainID().Return("some random domainID").AnyTimes() mockTask.EXPECT().GetWorkflowID().Return("some random workflowID").AnyTimes() mockTask.EXPECT().GetRunID().Return("some random runID").AnyTimes() mockTask.EXPECT().GetTaskType().Return(0).AnyTimes() tasks[key] = mockTask } queue := s.newTestProcessingQueue( 0, ackLevel, ackLevel, maxLevel, NewDomainFilter(nil, true), make(map[task.Key]task.Task), ) newReadLevel := testKey{ID: 10} queue.AddTasks(tasks, newReadLevel) s.Len(queue.outstandingTasks, len(taskKeys)) task, err := queue.GetTask(testKey{ID: 100}) s.Nil(task) s.Error(err) } func (s *processingQueueSuite) TestGetTask_Success() { ackLevel := testKey{ID: 1} maxLevel := testKey{ID: 10} taskKeys := []task.Key{ testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 5}, testKey{ID: 9}, } tasks := make(map[task.Key]task.Task) for _, key := range taskKeys { mockTask := task.NewMockTask(s.controller) mockTask.EXPECT().GetDomainID().Return("some random domainID").AnyTimes() mockTask.EXPECT().GetWorkflowID().Return("some random workflowID").AnyTimes() mockTask.EXPECT().GetRunID().Return("some random runID").AnyTimes() mockTask.EXPECT().GetTaskType().Return(0).AnyTimes() tasks[key] = mockTask } queue := s.newTestProcessingQueue( 0, ackLevel, ackLevel, maxLevel, NewDomainFilter(nil, true), make(map[task.Key]task.Task), ) newReadLevel := testKey{ID: 10} queue.AddTasks(tasks, newReadLevel) s.Len(queue.outstandingTasks, len(taskKeys)) task, err := queue.GetTask(testKey{ID: 3}) s.NoError(err) s.Equal(tasks[testKey{ID: 3}], task) } func (s *processingQueueSuite) TestGetTasks_Success() { ackLevel := testKey{ID: 1} maxLevel := testKey{ID: 10} taskKeys := []task.Key{ testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 5}, testKey{ID: 9}, } tasks := make(map[task.Key]task.Task) for _, key := range taskKeys { mockTask := task.NewMockTask(s.controller) mockTask.EXPECT().GetTaskID().Return(int64(key.(testKey).ID)).AnyTimes() mockTask.EXPECT().GetDomainID().Return("some random domainID").AnyTimes() mockTask.EXPECT().GetWorkflowID().Return("some random workflowID").AnyTimes() mockTask.EXPECT().GetRunID().Return("some random runID").AnyTimes() mockTask.EXPECT().GetTaskType().Return(0).AnyTimes() tasks[key] = mockTask } queue := s.newTestProcessingQueue( 0, ackLevel, ackLevel, maxLevel, NewDomainFilter(nil, true), make(map[task.Key]task.Task), ) newReadLevel := testKey{ID: 10} queue.AddTasks(tasks, newReadLevel) s.Len(queue.outstandingTasks, len(taskKeys)) outstandingTasks := queue.GetTasks() for _, outstandingTask := range outstandingTasks { task := tasks[testKey{ID: int(outstandingTask.GetTaskID())}] s.Equal(outstandingTask, task) } } func (s *processingQueueSuite) TestGetTasks_EmptyResult() { ackLevel := testKey{ID: 1} maxLevel := testKey{ID: 10} queue := s.newTestProcessingQueue( 0, ackLevel, ackLevel, maxLevel, NewDomainFilter(nil, true), make(map[task.Key]task.Task), ) outstandingTasks := queue.GetTasks() s.Equal(0, len(outstandingTasks)) } func (s *processingQueueSuite) TestUpdateAckLevel_WithPendingTasks() { ackLevel := testKey{ID: 1} maxLevel := testKey{ID: 10} taskKeys := []task.Key{ testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 5}, testKey{ID: 8}, testKey{ID: 10}, } taskStates := []t.State{ t.TaskStateAcked, t.TaskStateAcked, t.TaskStatePending, t.TaskStateAcked, t.TaskStatePending, } tasks := make(map[task.Key]task.Task) for i, key := range taskKeys { task := task.NewMockTask(s.controller) task.EXPECT().State().Return(taskStates[i]).AnyTimes() tasks[key] = task } queue := s.newTestProcessingQueue( 0, ackLevel, taskKeys[len(taskKeys)-1], maxLevel, NewDomainFilter(nil, true), tasks, ) newAckLevel, pendingTasks := queue.UpdateAckLevel() s.Equal(testKey{ID: 3}, newAckLevel) s.Equal(testKey{ID: 3}, queue.state.ackLevel) s.Equal(3, pendingTasks) } func (s *processingQueueSuite) TestUpdateAckLevel_NoPendingTasks() { ackLevel := testKey{ID: 1} readLevel := testKey{ID: 9} maxLevel := testKey{ID: 10} taskKeys := []task.Key{ testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 5}, testKey{ID: 8}, } taskStates := []t.State{ t.TaskStateAcked, t.TaskStateAcked, t.TaskStateAcked, t.TaskStateAcked, } tasks := make(map[task.Key]task.Task) for i, key := range taskKeys { task := task.NewMockTask(s.controller) task.EXPECT().State().Return(taskStates[i]).AnyTimes() tasks[key] = task } queue := s.newTestProcessingQueue( 0, ackLevel, readLevel, maxLevel, NewDomainFilter(nil, true), tasks, ) newAckLevel, pendingTasks := queue.UpdateAckLevel() s.Equal(readLevel, newAckLevel) s.Equal(readLevel, queue.state.ackLevel) s.Equal(0, pendingTasks) } func (s *processingQueueSuite) TestUpdateAckLevel_TaskKeyLargerThanReadLevel() { ackLevel := testKey{ID: 1} readLevel := testKey{ID: 5} maxLevel := testKey{ID: 10} taskKeys := []task.Key{ testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 5}, testKey{ID: 8}, testKey{ID: 9}, } taskStates := []t.State{ t.TaskStateAcked, t.TaskStateAcked, t.TaskStateAcked, t.TaskStateAcked, t.TaskStatePending, } tasks := make(map[task.Key]task.Task) for i, key := range taskKeys { task := task.NewMockTask(s.controller) task.EXPECT().State().Return(taskStates[i]).AnyTimes() tasks[key] = task } queue := s.newTestProcessingQueue( 0, ackLevel, readLevel, maxLevel, NewDomainFilter(nil, true), tasks, ) newAckLevel, pendingTasks := queue.UpdateAckLevel() s.Equal(readLevel, newAckLevel) s.Equal(readLevel, queue.state.ackLevel) s.Equal(2, pendingTasks) } func (s *processingQueueSuite) TestUpdateAckLevel_DeleteTaskBeyondAckLevel() { ackLevel := testKey{ID: 0} readLevel := testKey{ID: 7} maxLevel := testKey{ID: 10} taskKeys := []task.Key{ testKey{ID: 1}, testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 4}, testKey{ID: 5}, testKey{ID: 6}, testKey{ID: 7}, testKey{ID: 9}, testKey{ID: 10}, } taskStates := []t.State{ t.TaskStateAcked, t.TaskStatePending, t.TaskStateAcked, t.TaskStateAcked, t.TaskStateAcked, t.TaskStatePending, t.TaskStateAcked, t.TaskStateAcked, t.TaskStatePending, } tasks := make(map[task.Key]task.Task) for i, key := range taskKeys { task := task.NewMockTask(s.controller) task.EXPECT().State().Return(taskStates[i]).AnyTimes() tasks[key] = task } queue := s.newTestProcessingQueue( 0, ackLevel, readLevel, maxLevel, NewDomainFilter(nil, true), tasks, ) newAckLevel, pendingTasks := queue.UpdateAckLevel() s.Equal(testKey{ID: 1}, newAckLevel) s.Equal(testKey{ID: 1}, queue.state.ackLevel) s.Equal(6, pendingTasks) // 2 5 6 7 9 10 } func (s *processingQueueSuite) TestSplit() { testCases := []struct { queue *processingQueueImpl policyResult []ProcessingQueueState expectedNewQueues []*processingQueueImpl }{ { // test 1: no split needed queue: s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 3}, testKey{ID: 5}, NewDomainFilter(nil, true), s.newMockTasksForDomain( []task.Key{testKey{ID: 1}, testKey{ID: 2}, testKey{ID: 3}}, []string{"testDomain1", "testDomain1", "testDomain2"}, ), ), policyResult: nil, expectedNewQueues: []*processingQueueImpl{ s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 3}, testKey{ID: 5}, NewDomainFilter(nil, true), map[task.Key]task.Task{ testKey{ID: 1}: task.NewMockTask(s.controller), testKey{ID: 2}: task.NewMockTask(s.controller), testKey{ID: 3}: task.NewMockTask(s.controller), }, ), }, }, { // test 2: split two domains to another level, doesn't change range queue: s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}, "testDomain2": {}, "testDomain3": {}}, ReverseMatch: false, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 1}, testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 5}}, []string{"testDomain1", "testDomain1", "testDomain2", "testDomain3"}, ), ), policyResult: []ProcessingQueueState{ newProcessingQueueState( 1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}, "testDomain3": {}}, ReverseMatch: false, }, ), newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, ), }, expectedNewQueues: []*processingQueueImpl{ s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 1}: task.NewMockTask(s.controller), testKey{ID: 2}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}, "testDomain3": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 3}: task.NewMockTask(s.controller), testKey{ID: 5}: task.NewMockTask(s.controller), }, ), }, }, { // test 3: split into multiple new levels, while keeping the existing range queue: s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: make(map[string]struct{}), ReverseMatch: true, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 1}, testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 5}}, []string{"testDomain1", "testDomain1", "testDomain2", "testDomain3"}, ), ), policyResult: []ProcessingQueueState{ newProcessingQueueState( 1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}}, ReverseMatch: false, }, ), newProcessingQueueState( 2, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain3": {}}, ReverseMatch: false, }, ), newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}, "testDomain3": {}}, ReverseMatch: true, }, ), }, expectedNewQueues: []*processingQueueImpl{ s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}, "testDomain3": {}}, ReverseMatch: true, }, map[task.Key]task.Task{ testKey{ID: 1}: task.NewMockTask(s.controller), testKey{ID: 2}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 3}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 2, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain3": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 5}: task.NewMockTask(s.controller), }, ), }, }, { // test 4: change the queue range queue: s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 7}, testKey{ID: 10}, DomainFilter{ DomainIDs: make(map[string]struct{}), ReverseMatch: true, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 1}, testKey{ID: 2}, testKey{ID: 3}, testKey{ID: 5}, testKey{ID: 6}, testKey{ID: 7}}, []string{"testDomain1", "testDomain1", "testDomain2", "testDomain3", "testDomain1", "testDomain3"}, ), ), policyResult: []ProcessingQueueState{ newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}, "testDomain3": {}}, ReverseMatch: true, }, ), newProcessingQueueState( 0, testKey{ID: 5}, testKey{ID: 7}, testKey{ID: 10}, DomainFilter{ DomainIDs: make(map[string]struct{}), ReverseMatch: true, }, ), newProcessingQueueState( 1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}, "testDomain3": {}}, ReverseMatch: false, }, ), }, expectedNewQueues: []*processingQueueImpl{ s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}, "testDomain3": {}}, ReverseMatch: true, }, map[task.Key]task.Task{ testKey{ID: 1}: task.NewMockTask(s.controller), testKey{ID: 2}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 0, testKey{ID: 5}, testKey{ID: 7}, testKey{ID: 10}, DomainFilter{ DomainIDs: make(map[string]struct{}), ReverseMatch: true, }, map[task.Key]task.Task{ testKey{ID: 6}: task.NewMockTask(s.controller), testKey{ID: 7}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}, "testDomain3": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 3}: task.NewMockTask(s.controller), testKey{ID: 5}: task.NewMockTask(s.controller), }, ), }, }, } for _, tc := range testCases { mockPolicy := NewMockProcessingQueueSplitPolicy(s.controller) mockPolicy.EXPECT().Evaluate(ProcessingQueue(tc.queue)).Return(tc.policyResult).Times(1) newQueues := tc.queue.Split(mockPolicy) s.assertQueuesEqual(tc.expectedNewQueues, newQueues) } } func (s *processingQueueSuite) TestMerge() { testCases := []struct { queue1 *processingQueueImpl queue2 *processingQueueImpl expectedNewQueues []*processingQueueImpl }{ { // test 1: no overlap in range queue1: s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 1}, testKey{ID: 10}, NewDomainFilter(nil, true), s.newMockTasksForDomain( []task.Key{testKey{ID: 1}}, []string{"testDomain1"}, ), ), queue2: s.newTestProcessingQueue( 0, testKey{ID: 10}, testKey{ID: 50}, testKey{ID: 100}, NewDomainFilter(nil, true), s.newMockTasksForDomain( []task.Key{testKey{ID: 50}}, []string{"testDomain2"}, ), ), expectedNewQueues: []*processingQueueImpl{ s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 1}, testKey{ID: 10}, NewDomainFilter(nil, true), map[task.Key]task.Task{ testKey{ID: 1}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 0, testKey{ID: 10}, testKey{ID: 50}, testKey{ID: 100}, NewDomainFilter(nil, true), map[task.Key]task.Task{ testKey{ID: 50}: task.NewMockTask(s.controller), }, ), }, }, { // test 2: same ack level queue1: s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 7}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 1}, testKey{ID: 4}, testKey{ID: 7}}, []string{"testDomain1", "testDomain1", "testDomain1"}, ), ), queue2: s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 3}, testKey{ID: 5}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}}, ReverseMatch: false, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 2}, testKey{ID: 3}}, []string{"testDomain2", "testDomain2"}, ), ), expectedNewQueues: []*processingQueueImpl{ s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 3}, testKey{ID: 5}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 1}: task.NewMockTask(s.controller), testKey{ID: 2}: task.NewMockTask(s.controller), testKey{ID: 3}: task.NewMockTask(s.controller), testKey{ID: 4}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 0, testKey{ID: 5}, testKey{ID: 7}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 7}: task.NewMockTask(s.controller), }, ), }, }, { // test 3: same max level queue1: s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 7}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 1}, testKey{ID: 4}, testKey{ID: 7}}, []string{"testDomain1", "testDomain1", "testDomain1"}, ), ), queue2: s.newTestProcessingQueue( 0, testKey{ID: 5}, testKey{ID: 9}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}}, ReverseMatch: false, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 6}, testKey{ID: 8}, testKey{ID: 9}}, []string{"testDomain2", "testDomain2", "testDomain2"}, ), ), expectedNewQueues: []*processingQueueImpl{ s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 1}: task.NewMockTask(s.controller), testKey{ID: 4}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 0, testKey{ID: 5}, testKey{ID: 7}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 6}: task.NewMockTask(s.controller), testKey{ID: 7}: task.NewMockTask(s.controller), testKey{ID: 8}: task.NewMockTask(s.controller), testKey{ID: 9}: task.NewMockTask(s.controller), }, ), }, }, { // test 4: one queue contain another queue1: s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 7}, testKey{ID: 20}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 1}, testKey{ID: 4}, testKey{ID: 7}}, []string{"testDomain1", "testDomain1", "testDomain1"}, ), ), queue2: s.newTestProcessingQueue( 0, testKey{ID: 5}, testKey{ID: 9}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}}, ReverseMatch: false, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 6}, testKey{ID: 8}, testKey{ID: 9}}, []string{"testDomain2", "testDomain2", "testDomain2"}, ), ), expectedNewQueues: []*processingQueueImpl{ s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 1}: task.NewMockTask(s.controller), testKey{ID: 4}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 0, testKey{ID: 5}, testKey{ID: 7}, testKey{ID: 10}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 6}: task.NewMockTask(s.controller), testKey{ID: 7}: task.NewMockTask(s.controller), testKey{ID: 8}: task.NewMockTask(s.controller), testKey{ID: 9}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 0, testKey{ID: 10}, testKey{ID: 10}, testKey{ID: 20}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, map[task.Key]task.Task{}, ), }, }, { // test 5: general case queue1: s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 3}, testKey{ID: 15}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 1}, testKey{ID: 3}}, []string{"testDomain1", "testDomain1"}, ), ), queue2: s.newTestProcessingQueue( 0, testKey{ID: 5}, testKey{ID: 17}, testKey{ID: 20}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}}, ReverseMatch: false, }, s.newMockTasksForDomain( []task.Key{testKey{ID: 6}, testKey{ID: 8}, testKey{ID: 9}, testKey{ID: 17}}, []string{"testDomain2", "testDomain2", "testDomain2", "testDomain2"}, ), ), expectedNewQueues: []*processingQueueImpl{ s.newTestProcessingQueue( 0, testKey{ID: 0}, testKey{ID: 3}, testKey{ID: 5}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 1}: task.NewMockTask(s.controller), testKey{ID: 3}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 0, testKey{ID: 5}, testKey{ID: 5}, testKey{ID: 15}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 6}: task.NewMockTask(s.controller), testKey{ID: 8}: task.NewMockTask(s.controller), testKey{ID: 9}: task.NewMockTask(s.controller), }, ), s.newTestProcessingQueue( 0, testKey{ID: 15}, testKey{ID: 17}, testKey{ID: 20}, DomainFilter{ DomainIDs: map[string]struct{}{"testDomain2": {}}, ReverseMatch: false, }, map[task.Key]task.Task{ testKey{ID: 17}: task.NewMockTask(s.controller), }, ), }, }, } for _, tc := range testCases { queue1 := s.copyProcessingQueue(tc.queue1) queue2 := s.copyProcessingQueue(tc.queue2) s.assertQueuesEqual(tc.expectedNewQueues, queue1.Merge(queue2)) queue1 = s.copyProcessingQueue(tc.queue1) queue2 = s.copyProcessingQueue(tc.queue2) s.assertQueuesEqual(tc.expectedNewQueues, queue2.Merge(queue1)) } } func (s *processingQueueSuite) assertQueuesEqual( expectedQueues []*processingQueueImpl, actual []ProcessingQueue, ) { s.Equal(len(expectedQueues), len(actual)) actualQueues := make([]*processingQueueImpl, 0, len(actual)) for _, queue := range actual { actualQueues = append(actualQueues, queue.(*processingQueueImpl)) } compFn := func(q1, q2 *processingQueueImpl) bool { if taskKeyEquals(q1.state.ackLevel, q2.state.ackLevel) { return q1.state.level < q2.state.level } return q1.state.ackLevel.Less(q2.state.ackLevel) } sort.Slice(expectedQueues, func(i, j int) bool { return compFn(expectedQueues[i], expectedQueues[j]) }) sort.Slice(actualQueues, func(i, j int) bool { return compFn(actualQueues[i], actualQueues[j]) }) for i := 0; i != len(expectedQueues); i++ { s.assertQueueEqual(expectedQueues[i], actualQueues[i]) } } func (s *processingQueueSuite) assertQueueEqual( expected *processingQueueImpl, actual *processingQueueImpl, ) { s.Equal(expected.state, actual.state) s.Equal(len(expected.outstandingTasks), len(actual.outstandingTasks)) expectedKeys := make([]task.Key, 0, len(expected.outstandingTasks)) for key := range expected.outstandingTasks { expectedKeys = append(expectedKeys, key) } actualKeys := make([]task.Key, 0, len(actual.outstandingTasks)) for key := range actual.outstandingTasks { actualKeys = append(actualKeys, key) } sort.Slice(expectedKeys, func(i, j int) bool { return expectedKeys[i].Less(expectedKeys[j]) }) sort.Slice(actualKeys, func(i, j int) bool { return actualKeys[i].Less(actualKeys[j]) }) for i := 0; i != len(expectedKeys); i++ { s.True(taskKeyEquals(expectedKeys[i], actualKeys[i])) } } func (s *processingQueueSuite) copyProcessingQueue( queue *processingQueueImpl, ) *processingQueueImpl { tasks := make(map[task.Key]task.Task) for key, task := range queue.outstandingTasks { tasks[key] = task } return s.newTestProcessingQueue( queue.state.level, queue.state.ackLevel, queue.state.readLevel, queue.state.maxLevel, queue.state.domainFilter.copy(), tasks, ) } func (s *processingQueueSuite) newTestProcessingQueue( level int, ackLevel task.Key, readLevel task.Key, maxLevel task.Key, domainFilter DomainFilter, outstandingTasks map[task.Key]task.Task, ) *processingQueueImpl { return newProcessingQueue( &processingQueueStateImpl{ level: level, ackLevel: ackLevel, readLevel: readLevel, maxLevel: maxLevel, domainFilter: domainFilter, }, outstandingTasks, s.logger, s.metricsClient, ) } func (s *processingQueueSuite) newMockTasksForDomain( keys []task.Key, domainID []string, ) map[task.Key]task.Task { tasks := make(map[task.Key]task.Task) s.Equal(len(keys), len(domainID)) for i := 0; i != len(keys); i++ { mockTask := task.NewMockTask(s.controller) mockTask.EXPECT().GetDomainID().Return(domainID[i]).AnyTimes() tasks[keys[i]] = mockTask } return tasks } func (k testKey) Less(key task.Key) bool { return k.ID < key.(testKey).ID } ================================================ FILE: service/history/queue/processor_base.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "context" "fmt" "sync" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) const ( warnPendingTasks = 2000 ) type ( updateMaxReadLevelFn func() task.Key updateClusterAckLevelFn func(task.Key) error // TODO: deprecate this in favor of updateProcessingQueueStatesFn updateProcessingQueueStatesFn func([]ProcessingQueueState) error queueShutdownFn func() error actionNotification struct { ctx context.Context action *Action resultNotificationCh chan actionResultNotification } actionResultNotification struct { result *ActionResult err error } processorBase struct { shard shard.Context taskProcessor task.Processor redispatcher task.Redispatcher options *queueProcessorOptions updateMaxReadLevel updateMaxReadLevelFn updateClusterAckLevel updateClusterAckLevelFn updateProcessingQueueStates updateProcessingQueueStatesFn queueShutdown queueShutdownFn logger log.Logger metricsClient metrics.Client metricsScope metrics.Scope rateLimiter quotas.Limiter status int32 shutdownWG sync.WaitGroup shutdownCh chan struct{} actionNotifyCh chan actionNotification processingQueueCollections []ProcessingQueueCollection } ) func newProcessorBase( shard shard.Context, processingQueueStates []ProcessingQueueState, taskProcessor task.Processor, options *queueProcessorOptions, updateMaxReadLevel updateMaxReadLevelFn, updateClusterAckLevel updateClusterAckLevelFn, updateProcessingQueueStates updateProcessingQueueStatesFn, queueShutdown queueShutdownFn, logger log.Logger, metricsClient metrics.Client, ) *processorBase { metricsScope := metricsClient.Scope(options.MetricScope).Tagged(metrics.ShardIDTag(shard.GetShardID())) return &processorBase{ shard: shard, taskProcessor: taskProcessor, redispatcher: task.NewRedispatcher( taskProcessor, shard.GetTimeSource(), &task.RedispatcherOptions{ TaskRedispatchInterval: options.RedispatchInterval, }, logger, metricsScope, ), options: options, updateMaxReadLevel: updateMaxReadLevel, updateClusterAckLevel: updateClusterAckLevel, updateProcessingQueueStates: updateProcessingQueueStates, queueShutdown: queueShutdown, logger: logger, metricsClient: metricsClient, metricsScope: metricsScope, rateLimiter: quotas.NewDynamicRateLimiter(options.MaxPollRPS.AsFloat64()), status: common.DaemonStatusInitialized, shutdownCh: make(chan struct{}), actionNotifyCh: make(chan actionNotification), processingQueueCollections: newProcessingQueueCollections(processingQueueStates, logger, metricsClient), } } func (p *processorBase) updateAckLevel() (bool, task.Key, error) { p.metricsScope.IncCounter(metrics.AckLevelUpdateCounter) var minAckLevel task.Key totalPengingTasks := 0 for _, queueCollection := range p.processingQueueCollections { ackLevel, numPendingTasks := queueCollection.UpdateAckLevels() if ackLevel == nil { // ack level may be nil if the queueCollection doesn't contain any processing queue // after updating ack levels continue } totalPengingTasks += numPendingTasks if minAckLevel == nil { minAckLevel = ackLevel } else { minAckLevel = minTaskKey(minAckLevel, ackLevel) } } if minAckLevel == nil { // note that only failover processor will meet this condition err := p.queueShutdown() if err != nil { p.logger.Error("Error shutdown queue", tag.Error(err)) // return error so that shutdown callback can be retried return false, nil, err } return true, nil, nil } if totalPengingTasks > warnPendingTasks { p.logger.Warn("Too many pending tasks.", tag.Number(int64(totalPengingTasks))) } // TODO: consider move pendingTasksTime metrics from shardInfoScope to queue processor scope p.metricsClient.RecordTimer(metrics.ShardInfoScope, getPendingTasksMetricIdx(p.options.MetricScope), time.Duration(totalPengingTasks)) if p.options.EnablePersistQueueStates() && p.updateProcessingQueueStates != nil { states := p.getProcessingQueueStates().GetStateActionResult.States if err := p.updateProcessingQueueStates(states); err != nil { p.logger.Error("Error persisting processing queue states", tag.Error(err), tag.OperationFailed) p.metricsScope.IncCounter(metrics.AckLevelUpdateFailedCounter) return false, minAckLevel, err } } else { if err := p.updateClusterAckLevel(minAckLevel); err != nil { p.logger.Error("Error updating ack level for shard", tag.Error(err), tag.OperationFailed) p.metricsScope.IncCounter(metrics.AckLevelUpdateFailedCounter) return false, minAckLevel, err } } return false, minAckLevel, nil } func (p *processorBase) initializeSplitPolicy(lookAheadFunc lookAheadFunc) ProcessingQueueSplitPolicy { if !p.options.EnableSplit() { return nil } // note the order of policies matters, check the comment for aggregated split policy var policies []ProcessingQueueSplitPolicy maxNewQueueLevel := p.options.SplitMaxLevel() pendingTaskThresholds, err := dynamicproperties.ConvertDynamicConfigMapPropertyToIntMap(p.options.PendingTaskSplitThreshold()) if err != nil { p.logger.Error("Failed to convert pending task threshold", tag.Error(err)) } else { policies = append(policies, NewPendingTaskSplitPolicy( pendingTaskThresholds, p.options.EnablePendingTaskSplitByDomainID, lookAheadFunc, maxNewQueueLevel, p.logger, p.metricsScope, )) } taskAttemptThresholds, err := dynamicproperties.ConvertDynamicConfigMapPropertyToIntMap(p.options.StuckTaskSplitThreshold()) if err != nil { p.logger.Error("Failed to convert stuck task threshold", tag.Error(err)) } else { policies = append(policies, NewStuckTaskSplitPolicy( taskAttemptThresholds, p.options.EnableStuckTaskSplitByDomainID, maxNewQueueLevel, p.logger, p.metricsScope, )) } randomSplitProbability := p.options.RandomSplitProbability() if randomSplitProbability != float64(0) { policies = append(policies, NewRandomSplitPolicy( randomSplitProbability, p.options.EnableRandomSplitByDomainID, maxNewQueueLevel, lookAheadFunc, p.logger, p.metricsScope, )) } if len(policies) == 0 { return nil } return NewAggregatedSplitPolicy(policies...) } func (p *processorBase) splitProcessingQueueCollection(splitPolicy ProcessingQueueSplitPolicy, upsertPollTimeFn func(int, time.Time)) { defer p.emitProcessingQueueMetrics() if splitPolicy == nil { return } newQueuesMap := make(map[int][][]ProcessingQueue) for _, queueCollection := range p.processingQueueCollections { currentNewQueuesMap := make(map[int][]ProcessingQueue) newQueues := queueCollection.Split(splitPolicy) for _, newQueue := range newQueues { newQueueLevel := newQueue.State().Level() currentNewQueuesMap[newQueueLevel] = append(currentNewQueuesMap[newQueueLevel], newQueue) } for newQueueLevel, queues := range currentNewQueuesMap { newQueuesMap[newQueueLevel] = append(newQueuesMap[newQueueLevel], queues) } } for _, queueCollection := range p.processingQueueCollections { if queuesList, ok := newQueuesMap[queueCollection.Level()]; ok { for _, queues := range queuesList { queueCollection.Merge(queues) } } delete(newQueuesMap, queueCollection.Level()) } for level, newQueuesList := range newQueuesMap { newQueueCollection := NewProcessingQueueCollection( level, []ProcessingQueue{}, ) for _, newQueues := range newQueuesList { newQueueCollection.Merge(newQueues) } p.processingQueueCollections = append(p.processingQueueCollections, newQueueCollection) delete(newQueuesMap, level) } // there can be new queue collections created or new queues added to an existing collection for _, queueCollections := range p.processingQueueCollections { upsertPollTimeFn(queueCollections.Level(), time.Time{}) } } func (p *processorBase) emitProcessingQueueMetrics() { numProcessingQueues := 0 maxProcessingQueueLevel := 0 for _, queueCollection := range p.processingQueueCollections { size := len(queueCollection.Queues()) numProcessingQueues += size if size != 0 && queueCollection.Level() > maxProcessingQueueLevel { maxProcessingQueueLevel = queueCollection.Level() } } p.metricsScope.RecordTimer(metrics.ProcessingQueueNumTimer, time.Duration(numProcessingQueues)) p.metricsScope.RecordTimer(metrics.ProcessingQueueMaxLevelTimer, time.Duration(maxProcessingQueueLevel)) } func (p *processorBase) addAction(ctx context.Context, action *Action) (chan actionResultNotification, bool) { resultNotificationCh := make(chan actionResultNotification, 1) if ctx == nil { ctx = context.Background() } select { case p.actionNotifyCh <- actionNotification{ ctx: ctx, action: action, resultNotificationCh: resultNotificationCh, }: return resultNotificationCh, true case <-p.shutdownCh: close(resultNotificationCh) return nil, false case <-ctx.Done(): close(resultNotificationCh) return nil, false } } func (p *processorBase) handleActionNotification(notification actionNotification, postActionFn func()) { var result *ActionResult var err error switch notification.action.ActionType { case ActionTypeReset: result, err = p.resetProcessingQueueStates() case ActionTypeGetState: result = p.getProcessingQueueStates() default: err = fmt.Errorf("unknown queue action type: %v", notification.action.ActionType) } notification.resultNotificationCh <- actionResultNotification{ result: result, err: err, } close(notification.resultNotificationCh) if err == nil { // only run post action when the action complete successfully postActionFn() } } func (p *processorBase) resetProcessingQueueStates() (*ActionResult, error) { var minAckLevel task.Key for _, queueCollection := range p.processingQueueCollections { ackLevel, _ := queueCollection.UpdateAckLevels() if ackLevel == nil { // ack level may be nil if the queueCollection doesn't contain any processing queue // after updating ack levels continue } if minAckLevel == nil { minAckLevel = ackLevel } else { minAckLevel = minTaskKey(minAckLevel, ackLevel) } } if minAckLevel == nil { // reset queue can't be invoked for failover queue, so if this happens, there's must be a // bug in the queue split implementation p.logger.Fatal("unable to find minAckLevel during reset", tag.Value(p.processingQueueCollections)) } var maxReadLevel task.Key switch p.options.MetricScope { case metrics.TransferActiveQueueProcessorScope, metrics.TransferStandbyQueueProcessorScope: maxReadLevel = maximumTransferTaskKey case metrics.TimerActiveQueueProcessorScope, metrics.TimerStandbyQueueProcessorScope: maxReadLevel = maximumTimerTaskKey } p.processingQueueCollections = newProcessingQueueCollections( []ProcessingQueueState{ NewProcessingQueueState( defaultProcessingQueueLevel, minAckLevel, maxReadLevel, NewDomainFilter(nil, true), ), }, p.logger, p.metricsClient, ) return &ActionResult{ ActionType: ActionTypeReset, ResetActionResult: &ResetActionResult{}, }, nil } func (p *processorBase) getProcessingQueueStates() *ActionResult { var queueStates []ProcessingQueueState for _, queueCollection := range p.processingQueueCollections { for _, queue := range queueCollection.Queues() { queueStates = append(queueStates, copyQueueState(queue.State())) } } return &ActionResult{ ActionType: ActionTypeGetState, GetStateActionResult: &GetStateActionResult{ States: queueStates, }, } } func (p *processorBase) submitTask(task task.Task) (bool, error) { submitted, err := p.taskProcessor.TrySubmit(task) if err != nil { select { case <-p.shutdownCh: // if error is due to shard shutdown return false, err default: // otherwise it might be error from domain cache etc, add // the task to redispatch queue so that it can be retried p.logger.Error("Failed to submit task", tag.Error(err)) } } if err != nil || !submitted { p.redispatcher.AddTask(task) return false, nil } return true, nil } func getPendingTasksMetricIdx(scopeIdx metrics.ScopeIdx) metrics.MetricIdx { switch scopeIdx { case metrics.TimerActiveQueueProcessorScope: return metrics.ShardInfoTimerActivePendingTasksTimer case metrics.TimerStandbyQueueProcessorScope: return metrics.ShardInfoTimerStandbyPendingTasksTimer case metrics.TransferActiveQueueProcessorScope: return metrics.ShardInfoTransferActivePendingTasksTimer case metrics.TransferStandbyQueueProcessorScope: return metrics.ShardInfoTransferStandbyPendingTasksTimer case metrics.CrossClusterQueueProcessorScope: return metrics.ShardInfoCrossClusterPendingTasksTimer case metrics.ReplicatorQueueProcessorScope: return metrics.ShardInfoReplicationPendingTasksTimer default: panic("unknown queue processor metric scope") } } ================================================ FILE: service/history/queue/processor_base_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "sort" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) type ( processorBaseSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockTaskProcessor *task.MockProcessor logger log.Logger metricsClient metrics.Client metricsScope metrics.Scope } ) func TestProcessorBaseSuite(t *testing.T) { s := new(processorBaseSuite) suite.Run(t, s) } func (s *processorBaseSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.mockTaskProcessor = task.NewMockProcessor(s.controller) s.logger = testlogger.New(s.Suite.T()) s.metricsClient = metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) s.metricsScope = s.metricsClient.Scope(metrics.TransferQueueProcessorScope) } func (s *processorBaseSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *processorBaseSuite) TestSplitQueue() { mockQueueSplitPolicy := NewMockProcessingQueueSplitPolicy(s.controller) processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( 0, newTransferTaskKey(0), newTransferTaskKey(100), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, true), ), NewProcessingQueueState( 1, newTransferTaskKey(0), newTransferTaskKey(100), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), NewProcessingQueueState( 0, newTransferTaskKey(100), newTransferTaskKey(1000), NewDomainFilter(map[string]struct{}{}, true), ), } mockQueueSplitPolicy.EXPECT().Evaluate(NewProcessingQueue(processingQueueStates[0], s.logger, s.metricsClient)).Return(nil).Times(1) mockQueueSplitPolicy.EXPECT().Evaluate(NewProcessingQueue(processingQueueStates[1], s.logger, s.metricsClient)).Return([]ProcessingQueueState{ NewProcessingQueueState( 2, newTransferTaskKey(0), newTransferTaskKey(100), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), }).Times(1) mockQueueSplitPolicy.EXPECT().Evaluate(NewProcessingQueue(processingQueueStates[2], s.logger, s.metricsClient)).Return([]ProcessingQueueState{ NewProcessingQueueState( 0, newTransferTaskKey(100), newTransferTaskKey(1000), NewDomainFilter(map[string]struct{}{"testDomain1": {}, "testDomain2": {}, "testDomain3": {}}, false), ), NewProcessingQueueState( 1, newTransferTaskKey(100), newTransferTaskKey(1000), NewDomainFilter(map[string]struct{}{"testDomain2": {}}, false), ), NewProcessingQueueState( 2, newTransferTaskKey(100), newTransferTaskKey(1000), NewDomainFilter(map[string]struct{}{"testDomain3": {}}, false), ), }).Times(1) processorBase := s.newTestProcessorBase( processingQueueStates, nil, nil, nil, nil, ) nextPollTime := make(map[int]time.Time) processorBase.splitProcessingQueueCollection( mockQueueSplitPolicy, func(level int, pollTime time.Time) { nextPollTime[level] = pollTime }, ) processingQueueCollections := processorBase.processingQueueCollections sort.Slice(processingQueueCollections, func(i, j int) bool { return processingQueueCollections[i].Level() < processingQueueCollections[j].Level() }) s.Len(processingQueueCollections, 3) s.Len(processingQueueCollections[0].Queues(), 2) s.Len(processingQueueCollections[1].Queues(), 1) s.Len(processingQueueCollections[2].Queues(), 2) for idx := 1; idx != len(processingQueueCollections)-1; idx++ { s.Less( processingQueueCollections[idx-1].Level(), processingQueueCollections[idx].Level(), ) } s.Len(nextPollTime, 3) for _, nextPollTime := range nextPollTime { s.Zero(nextPollTime) } } func (s *processorBaseSuite) TestUpdateAckLevel_Transfer_ProcessedFinished() { processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( 2, newTransferTaskKey(100), newTransferTaskKey(100), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), NewProcessingQueueState( 0, newTransferTaskKey(1000), newTransferTaskKey(1000), NewDomainFilter(map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, true), ), } queueShutdown := false queueShutdownFn := func() error { queueShutdown = true return nil } processorBase := s.newTestProcessorBase( processingQueueStates, nil, nil, nil, queueShutdownFn, ) processFinished, ackLevel, err := processorBase.updateAckLevel() s.NoError(err) s.Nil(ackLevel) s.True(processFinished) s.True(queueShutdown) } func (s *processorBaseSuite) TestUpdateAckLevel_Tranfer_ProcessNotFinished() { processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( 2, newTransferTaskKey(5), newTransferTaskKey(100), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), NewProcessingQueueState( 1, newTransferTaskKey(2), newTransferTaskKey(100), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), NewProcessingQueueState( 0, newTransferTaskKey(100), newTransferTaskKey(1000), NewDomainFilter(map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, true), ), } updateAckLevel := int64(0) updateTransferAckLevelFn := func(ackLevel task.Key) error { updateAckLevel = ackLevel.(transferTaskKey).taskID return nil } processorBase := s.newTestProcessorBase( processingQueueStates, nil, updateTransferAckLevelFn, nil, nil, ) processFinished, ackLevel, err := processorBase.updateAckLevel() s.NoError(err) s.False(processFinished) s.Equal(int64(2), updateAckLevel) s.Equal(int64(2), ackLevel.(transferTaskKey).taskID) } func (s *processorBaseSuite) TestUpdateAckLevel_Timer_UpdateAckLevel() { now := time.Now() processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( 2, newTimerTaskKey(now.Add(-5*time.Second), 0), newTimerTaskKey(now, 0), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), NewProcessingQueueState( 1, newTimerTaskKey(now.Add(-3*time.Second), 0), newTimerTaskKey(now.Add(5*time.Second), 0), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), NewProcessingQueueState( 0, newTimerTaskKey(now.Add(-1*time.Second), 0), newTimerTaskKey(now.Add(100*time.Second), 0), NewDomainFilter(map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, true), ), } updateAckLevel := time.Time{} updateTransferAckLevelFn := func(ackLevel task.Key) error { updateAckLevel = ackLevel.(timerTaskKey).visibilityTimestamp return nil } timerQueueProcessBase := s.newTestProcessorBase(processingQueueStates, nil, updateTransferAckLevelFn, nil, nil) timerQueueProcessBase.options.EnablePersistQueueStates = dynamicproperties.GetBoolPropertyFn(true) processFinished, ackLevel, err := timerQueueProcessBase.updateAckLevel() s.NoError(err) s.False(processFinished) s.Equal(now.Add(-5*time.Second), updateAckLevel) s.Equal(now.Add(-5*time.Second), ackLevel.(timerTaskKey).visibilityTimestamp) } func (s *processorBaseSuite) TestUpdateAckLevel_Timer_UpdateQueueStates() { now := time.Now() processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( 2, newTimerTaskKey(now.Add(-5*time.Second), 0), newTimerTaskKey(now, 0), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), NewProcessingQueueState( 1, newTimerTaskKey(now.Add(-3*time.Second), 0), newTimerTaskKey(now.Add(5*time.Second), 0), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), NewProcessingQueueState( 0, newTimerTaskKey(now.Add(-1*time.Second), 0), newTimerTaskKey(now.Add(100*time.Second), 0), NewDomainFilter(map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, true), ), } var pState []*types.ProcessingQueueState updateProcessingQueueStates := func(states []ProcessingQueueState) error { pState = convertToPersistenceTimerProcessingQueueStates(states) return nil } timerQueueProcessBase := s.newTestProcessorBase(processingQueueStates, nil, nil, updateProcessingQueueStates, nil) timerQueueProcessBase.options.EnablePersistQueueStates = dynamicproperties.GetBoolPropertyFn(true) processFinished, ackLevel, err := timerQueueProcessBase.updateAckLevel() s.NoError(err) s.False(processFinished) s.Equal(len(processingQueueStates), len(pState)) s.Equal(now.Add(-5*time.Second), ackLevel.(timerTaskKey).visibilityTimestamp) } func (s *processorBaseSuite) TestInitializeSplitPolicy_Disabled() { processorBase := s.newTestProcessorBase( nil, nil, nil, nil, nil, ) splitPolicy := processorBase.initializeSplitPolicy(nil) s.Nil(splitPolicy, "got non-nil split policy, want nil because it's disabled") } func (s *processorBaseSuite) TestInitializeSplitPolicy_Enabled() { processorBase := s.newTestProcessorBase(nil, nil, nil, nil, nil) processorBase.options.EnableSplit = dynamicproperties.GetBoolPropertyFn(true) splitPolicy := processorBase.initializeSplitPolicy(nil) s.NotNil(splitPolicy, "got nil split policy, want non-nil") aggPolicy, ok := splitPolicy.(*aggregatedSplitPolicy) s.True(ok, "got %T, want *aggregatedSplitPolicy", splitPolicy) s.Equal(3, len(aggPolicy.policies), "got %v policies, want 3: pending task policy, stuck task policy and random split policy", len(aggPolicy.policies)) } func (s *processorBaseSuite) TestResetProcessingQueueStates() { processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( 0, newTransferTaskKey(0), newTransferTaskKey(100), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, true), ), NewProcessingQueueState( 1, newTransferTaskKey(0), newTransferTaskKey(100), NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), NewProcessingQueueState( 0, newTransferTaskKey(100), newTransferTaskKey(1000), NewDomainFilter(map[string]struct{}{}, true), ), } processorBase := s.newTestProcessorBase(processingQueueStates, nil, nil, nil, nil) res, err := processorBase.resetProcessingQueueStates() s.NoError(err, "no error expected") s.Equal(ActionTypeReset, res.ActionType, "got action type %v, want %v", res.ActionType, ActionTypeReset) } func (s *processorBaseSuite) newTestProcessorBase( processingQueueStates []ProcessingQueueState, updateMaxReadLevel updateMaxReadLevelFn, updateClusterAckLevel updateClusterAckLevelFn, updateProcessingQueueStates updateProcessingQueueStatesFn, queueShutdown queueShutdownFn, ) *processorBase { return newProcessorBase( s.mockShard, processingQueueStates, s.mockTaskProcessor, newTransferQueueProcessorOptions(s.mockShard.GetConfig(), true, false), updateMaxReadLevel, updateClusterAckLevel, updateProcessingQueueStates, queueShutdown, s.logger, s.metricsClient, ) } ================================================ FILE: service/history/queue/processor_options.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" ) type queueProcessorOptions struct { BatchSize dynamicproperties.IntPropertyFn DeleteBatchSize dynamicproperties.IntPropertyFn MaxPollRPS dynamicproperties.IntPropertyFn MaxPollInterval dynamicproperties.DurationPropertyFn MaxPollIntervalJitterCoefficient dynamicproperties.FloatPropertyFn UpdateAckInterval dynamicproperties.DurationPropertyFn UpdateAckIntervalJitterCoefficient dynamicproperties.FloatPropertyFn RedispatchInterval dynamicproperties.DurationPropertyFn MaxRedispatchQueueSize dynamicproperties.IntPropertyFn MaxStartJitterInterval dynamicproperties.DurationPropertyFn SplitQueueInterval dynamicproperties.DurationPropertyFn SplitQueueIntervalJitterCoefficient dynamicproperties.FloatPropertyFn EnableSplit dynamicproperties.BoolPropertyFn SplitMaxLevel dynamicproperties.IntPropertyFn EnableRandomSplitByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter RandomSplitProbability dynamicproperties.FloatPropertyFn EnablePendingTaskSplitByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter PendingTaskSplitThreshold dynamicproperties.MapPropertyFn EnableStuckTaskSplitByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter StuckTaskSplitThreshold dynamicproperties.MapPropertyFn SplitLookAheadDurationByDomainID dynamicproperties.DurationPropertyFnWithDomainIDFilter PollBackoffInterval dynamicproperties.DurationPropertyFn PollBackoffIntervalJitterCoefficient dynamicproperties.FloatPropertyFn EnablePersistQueueStates dynamicproperties.BoolPropertyFn EnableLoadQueueStates dynamicproperties.BoolPropertyFn EnableGracefulSyncShutdown dynamicproperties.BoolPropertyFn EnableValidator dynamicproperties.BoolPropertyFn ValidationInterval dynamicproperties.DurationPropertyFn // MaxPendingTaskSize is used in cross cluster queue to limit the pending task count MaxPendingTaskSize dynamicproperties.IntPropertyFn MetricScope metrics.ScopeIdx } ================================================ FILE: service/history/queue/queue_processor_util.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) func convertToPersistenceTransferProcessingQueueStates(states []ProcessingQueueState) []*types.ProcessingQueueState { pStates := make([]*types.ProcessingQueueState, 0, len(states)) for _, state := range states { pStates = append(pStates, &types.ProcessingQueueState{ Level: common.Int32Ptr(int32(state.Level())), AckLevel: common.Int64Ptr(state.AckLevel().(transferTaskKey).taskID), MaxLevel: common.Int64Ptr(state.MaxLevel().(transferTaskKey).taskID), DomainFilter: convertToPersistenceDomainFilter(state.DomainFilter()), }) } return pStates } func convertFromPersistenceTransferProcessingQueueStates(pStates []*types.ProcessingQueueState) []ProcessingQueueState { states := make([]ProcessingQueueState, 0, len(pStates)) for _, pState := range pStates { states = append(states, NewProcessingQueueState( int(pState.GetLevel()), newTransferTaskKey(pState.GetAckLevel()), newTransferTaskKey(pState.GetMaxLevel()), convertFromPersistenceDomainFilter(pState.DomainFilter), )) } return states } func convertToPersistenceTimerProcessingQueueStates(states []ProcessingQueueState) []*types.ProcessingQueueState { pStates := make([]*types.ProcessingQueueState, 0, len(states)) for _, state := range states { pStates = append(pStates, &types.ProcessingQueueState{ Level: common.Int32Ptr(int32(state.Level())), AckLevel: common.Int64Ptr(state.AckLevel().(timerTaskKey).visibilityTimestamp.UnixNano()), MaxLevel: common.Int64Ptr(state.MaxLevel().(timerTaskKey).visibilityTimestamp.UnixNano()), DomainFilter: convertToPersistenceDomainFilter(state.DomainFilter()), }) } return pStates } func convertFromPersistenceTimerProcessingQueueStates(pStates []*types.ProcessingQueueState) []ProcessingQueueState { states := make([]ProcessingQueueState, 0, len(pStates)) for _, pState := range pStates { states = append(states, NewProcessingQueueState( int(pState.GetLevel()), newTimerTaskKey(time.Unix(0, pState.GetAckLevel()), 0), newTimerTaskKey(time.Unix(0, pState.GetMaxLevel()), 0), convertFromPersistenceDomainFilter(pState.DomainFilter), )) } return states } func convertToPersistenceDomainFilter(domainFilter DomainFilter) *types.DomainFilter { domainIDs := make([]string, 0, len(domainFilter.DomainIDs)) for domainID := range domainFilter.DomainIDs { domainIDs = append(domainIDs, domainID) } return &types.DomainFilter{ DomainIDs: domainIDs, ReverseMatch: domainFilter.ReverseMatch, } } func convertFromPersistenceDomainFilter(domainFilter *types.DomainFilter) DomainFilter { domainIDs := make(map[string]struct{}) for _, domainID := range domainFilter.DomainIDs { domainIDs[domainID] = struct{}{} } return NewDomainFilter(domainIDs, domainFilter.GetReverseMatch()) } func validateProcessingQueueStates(pStates []*types.ProcessingQueueState, ackLevel interface{}) bool { if len(pStates) == 0 { return false } minAckLevel := pStates[0].GetAckLevel() for _, pState := range pStates { minAckLevel = min(minAckLevel, pState.GetAckLevel()) } switch ackLevel := ackLevel.(type) { case int64: return minAckLevel == ackLevel case time.Time: return minAckLevel == ackLevel.UnixNano() default: return false } } ================================================ FILE: service/history/queue/queue_processor_util_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) type ( queueProcessorUtilSuite struct { suite.Suite *require.Assertions } ) func TestQueueProcessorUtilSuite(t *testing.T) { s := new(queueProcessorUtilSuite) suite.Run(t, s) } func (s *queueProcessorUtilSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *queueProcessorUtilSuite) TestConvertToPersistenceTransferProcessingQueueStates() { levels := []int{0, 1} ackLevels := []int64{123, 456} maxLevels := []int64{456, 789} domainFilters := []DomainFilter{ NewDomainFilter(nil, true), NewDomainFilter(map[string]struct{}{"domain 1": {}, "domain 2": {}}, false), } states := []ProcessingQueueState{} for i := 0; i != len(levels); i++ { states = append(states, NewProcessingQueueState( levels[i], newTransferTaskKey(ackLevels[i]), newTransferTaskKey(maxLevels[i]), domainFilters[i], )) } pStates := convertToPersistenceTransferProcessingQueueStates(states) s.Equal(len(states), len(pStates)) for i := 0; i != len(states); i++ { s.assertProcessingQueueStateEqual(states[i], pStates[i]) } } func (s *queueProcessorUtilSuite) TestConvertFromPersistenceTransferProcessingQueueStates() { levels := []int32{0, 1} ackLevels := []int64{123, 456} maxLevels := []int64{456, 789} domainFilters := []*types.DomainFilter{ { DomainIDs: nil, ReverseMatch: true, }, { DomainIDs: []string{"domain 1", "domain 2"}, ReverseMatch: false, }, } pStates := []*types.ProcessingQueueState{} for i := 0; i != len(levels); i++ { pStates = append(pStates, &types.ProcessingQueueState{ Level: common.Int32Ptr(levels[i]), AckLevel: common.Int64Ptr(ackLevels[i]), MaxLevel: common.Int64Ptr(maxLevels[i]), DomainFilter: domainFilters[i], }) } states := convertFromPersistenceTransferProcessingQueueStates(pStates) s.Equal(len(pStates), len(states)) for i := 0; i != len(pStates); i++ { s.assertProcessingQueueStateEqual(states[i], pStates[i]) } } func (s *queueProcessorUtilSuite) TestConvertToPersistenceTimerProcessingQueueStates() { levels := []int{0, 1} ackLevels := []time.Time{time.Now(), time.Now().Add(-time.Minute)} maxLevels := []time.Time{time.Now().Add(time.Hour), time.Now().Add(time.Nanosecond)} domainFilters := []DomainFilter{ NewDomainFilter(nil, true), NewDomainFilter(map[string]struct{}{"domain 1": {}, "domain 2": {}}, false), } states := []ProcessingQueueState{} for i := 0; i != len(levels); i++ { states = append(states, NewProcessingQueueState( levels[i], newTimerTaskKey(ackLevels[i], 0), newTimerTaskKey(maxLevels[i], 0), domainFilters[i], )) } pStates := convertToPersistenceTimerProcessingQueueStates(states) s.Equal(len(states), len(pStates)) for i := 0; i != len(states); i++ { s.assertProcessingQueueStateEqual(states[i], pStates[i]) } } func (s *queueProcessorUtilSuite) TestConvertFromPersistenceTimerProcessingQueueStates() { levels := []int32{0, 1} ackLevels := []time.Time{time.Now(), time.Now().Add(-time.Minute)} maxLevels := []time.Time{time.Now().Add(time.Hour), time.Now().Add(time.Nanosecond)} domainFilters := []*types.DomainFilter{ { DomainIDs: nil, ReverseMatch: true, }, { DomainIDs: []string{"domain 1", "domain 2"}, ReverseMatch: false, }, } pStates := []*types.ProcessingQueueState{} for i := 0; i != len(levels); i++ { pStates = append(pStates, &types.ProcessingQueueState{ Level: common.Int32Ptr(levels[i]), AckLevel: common.Int64Ptr(ackLevels[i].UnixNano()), MaxLevel: common.Int64Ptr(maxLevels[i].UnixNano()), DomainFilter: domainFilters[i], }) } states := convertFromPersistenceTimerProcessingQueueStates(pStates) s.Equal(len(pStates), len(states)) for i := 0; i != len(pStates); i++ { s.assertProcessingQueueStateEqual(states[i], pStates[i]) } } func (s *queueProcessorUtilSuite) assertProcessingQueueStateEqual( state ProcessingQueueState, pState *types.ProcessingQueueState, ) { var ackLevel, maxLevel int64 switch taskKey := state.AckLevel().(type) { case transferTaskKey: ackLevel = taskKey.taskID case timerTaskKey: ackLevel = taskKey.visibilityTimestamp.UnixNano() s.Zero(taskKey.taskID) } switch taskKey := state.MaxLevel().(type) { case transferTaskKey: maxLevel = taskKey.taskID case timerTaskKey: maxLevel = taskKey.visibilityTimestamp.UnixNano() s.Zero(taskKey.taskID) } s.Equal(state.Level(), int(pState.GetLevel())) s.Equal(ackLevel, pState.GetAckLevel()) s.Equal(maxLevel, pState.GetMaxLevel()) s.assertDomainFilterEqual(state.DomainFilter(), pState.GetDomainFilter()) } func (s *queueProcessorUtilSuite) TestValidateProcessingQueueStates_Fail() { ackLevels := []int64{123, 456} pStates := []*types.ProcessingQueueState{} for i := 0; i != len(ackLevels); i++ { pStates = append(pStates, &types.ProcessingQueueState{ Level: nil, AckLevel: common.Int64Ptr(ackLevels[i]), MaxLevel: nil, DomainFilter: nil, }) } isValid := validateProcessingQueueStates(pStates, 789) s.False(isValid) } func (s *queueProcessorUtilSuite) TestValidateProcessingQueueStates_Success() { ackLevels := []time.Time{time.Now(), time.Now().Add(-time.Minute)} pStates := []*types.ProcessingQueueState{} for i := 0; i != len(ackLevels); i++ { pStates = append(pStates, &types.ProcessingQueueState{ Level: nil, AckLevel: common.Int64Ptr(ackLevels[i].UnixNano()), MaxLevel: nil, DomainFilter: nil, }) } isValid := validateProcessingQueueStates(pStates, ackLevels[1]) s.True(isValid) } func (s *queueProcessorUtilSuite) assertDomainFilterEqual( domainFilter DomainFilter, pDomainFilter *types.DomainFilter, ) { s.Equal(domainFilter.ReverseMatch, pDomainFilter.GetReverseMatch()) s.Equal(len(domainFilter.DomainIDs), len(pDomainFilter.GetDomainIDs())) for _, domainID := range pDomainFilter.GetDomainIDs() { _, ok := domainFilter.DomainIDs[domainID] s.True(ok) } } ================================================ FILE: service/history/queue/split_policy.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package queue import ( "fmt" "math/rand" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" t "github.com/uber/cadence/common/task" "github.com/uber/cadence/service/history/task" ) const ( policyTypePendingTask int = iota + 1 policyTypeStuckTask policyTypeSelectedDomain policyTypeRandom ) type ( lookAheadFunc func(task.Key, string) task.Key pendingTaskSplitPolicy struct { pendingTaskThreshold map[int]int // queue level -> threshold enabledByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter maxNewQueueLevel int lookAheadFunc lookAheadFunc logger log.Logger metricsScope metrics.Scope } stuckTaskSplitPolicy struct { attemptThreshold map[int]int // queue level -> threshold enabledByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter maxNewQueueLevel int logger log.Logger metricsScope metrics.Scope } selectedDomainSplitPolicy struct { domainIDs map[string]struct{} newQueueLevel int logger log.Logger metricsScope metrics.Scope } randomSplitPolicy struct { splitProbability float64 enabledByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter maxNewQueueLevel int lookAheadFunc lookAheadFunc logger log.Logger metricsScope metrics.Scope } aggregatedSplitPolicy struct { policies []ProcessingQueueSplitPolicy } ) // NewPendingTaskSplitPolicy creates a new processing queue split policy // based on the number of pending tasks func NewPendingTaskSplitPolicy( pendingTaskThreshold map[int]int, enabledByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter, lookAheadFunc lookAheadFunc, maxNewQueueLevel int, logger log.Logger, metricsScope metrics.Scope, ) ProcessingQueueSplitPolicy { return &pendingTaskSplitPolicy{ pendingTaskThreshold: pendingTaskThreshold, enabledByDomainID: enabledByDomainID, lookAheadFunc: lookAheadFunc, maxNewQueueLevel: maxNewQueueLevel, logger: logger, metricsScope: metricsScope, } } // NewStuckTaskSplitPolicy creates a new processing queue split policy // based on the number of task attempts tasks func NewStuckTaskSplitPolicy( attemptThreshold map[int]int, enabledByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter, maxNewQueueLevel int, logger log.Logger, metricsScope metrics.Scope, ) ProcessingQueueSplitPolicy { return &stuckTaskSplitPolicy{ attemptThreshold: attemptThreshold, enabledByDomainID: enabledByDomainID, maxNewQueueLevel: maxNewQueueLevel, logger: logger, metricsScope: metricsScope, } } // NewSelectedDomainSplitPolicy creates a new processing queue split policy // that splits out specific domainIDs func NewSelectedDomainSplitPolicy( domainIDs map[string]struct{}, newQueueLevel int, logger log.Logger, metricsScope metrics.Scope, ) ProcessingQueueSplitPolicy { return &selectedDomainSplitPolicy{ domainIDs: domainIDs, newQueueLevel: newQueueLevel, logger: logger, metricsScope: metricsScope, } } // NewRandomSplitPolicy creates a split policy that will randomly split one // or more domains into a new processing queue func NewRandomSplitPolicy( splitProbability float64, enabledByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter, maxNewQueueLevel int, lookAheadFunc lookAheadFunc, logger log.Logger, metricsScope metrics.Scope, ) ProcessingQueueSplitPolicy { return &randomSplitPolicy{ splitProbability: splitProbability, enabledByDomainID: enabledByDomainID, maxNewQueueLevel: maxNewQueueLevel, lookAheadFunc: lookAheadFunc, logger: logger, metricsScope: metricsScope, } } // NewAggregatedSplitPolicy creates a new processing queue split policy // that which combines other policies. Policies are evaluated in the order // they passed in, and if one policy returns an non-empty result, that result // will be returned as is and policies after that one will not be evaluated func NewAggregatedSplitPolicy(policies ...ProcessingQueueSplitPolicy) ProcessingQueueSplitPolicy { return &aggregatedSplitPolicy{ policies: policies, } } func (p *pendingTaskSplitPolicy) Evaluate(queue ProcessingQueue) []ProcessingQueueState { queueImpl := queue.(*processingQueueImpl) if queueImpl.state.level == p.maxNewQueueLevel { // already reaches max level, skip splitting return nil } threshold, ok := p.pendingTaskThreshold[queueImpl.state.level] if !ok { // no threshold specified for the level, skip splitting return nil } pendingTasksPerDomain := make(map[string]int) // domainID -> # of pending tasks for _, task := range queueImpl.outstandingTasks { if task.State() != t.TaskStateAcked { pendingTasksPerDomain[task.GetDomainID()]++ } } domainToSplit := make(map[string]struct{}) for domainID, pendingTasks := range pendingTasksPerDomain { if pendingTasks > threshold && p.enabledByDomainID(domainID) { domainToSplit[domainID] = struct{}{} } } if len(domainToSplit) == 0 { return nil } newQueueLevel := queueImpl.state.level + 1 // split stuck tasks to current level + 1 p.logger.Info("Split processing queue", tag.QueueLevel(newQueueLevel), tag.PreviousQueueLevel(queueImpl.state.level), tag.WorkflowDomainIDs(domainToSplit), tag.QueueSplitPolicyType(policyTypePendingTask), ) p.metricsScope.IncCounter(metrics.ProcessingQueuePendingTaskSplitCounter) return splitQueueHelper( queueImpl, domainToSplit, newQueueLevel, p.lookAheadFunc, ) } func (p *stuckTaskSplitPolicy) Evaluate(queue ProcessingQueue) []ProcessingQueueState { queueImpl := queue.(*processingQueueImpl) if queueImpl.state.level == p.maxNewQueueLevel { // already reaches max level, skip splitting return nil } threshold, ok := p.attemptThreshold[queueImpl.state.level] if !ok { // no threshold specified for the level, skip splitting return nil } domainToSplit := make(map[string]struct{}) for _, task := range queueImpl.outstandingTasks { domainID := task.GetDomainID() attempt := task.GetAttempt() if attempt > threshold && p.enabledByDomainID(domainID) { domainToSplit[domainID] = struct{}{} } } if len(domainToSplit) == 0 { return nil } newQueueLevel := queueImpl.state.level + 1 // split stuck tasks to current level + 1 p.logger.Info("Split processing queue", tag.QueueLevel(newQueueLevel), tag.PreviousQueueLevel(queueImpl.state.level), tag.WorkflowDomainIDs(domainToSplit), tag.QueueSplitPolicyType(policyTypeStuckTask), ) p.metricsScope.IncCounter(metrics.ProcessingQueueStuckTaskSplitCounter) return splitQueueHelper( queueImpl, domainToSplit, newQueueLevel, nil, // no need to look ahead ) } func (p *selectedDomainSplitPolicy) Evaluate(queue ProcessingQueue) []ProcessingQueueState { domainBelongsToQueue := false currentQueueState := queue.State() currentDomainFilter := currentQueueState.DomainFilter() for domainID := range p.domainIDs { if currentDomainFilter.Filter(domainID) { domainBelongsToQueue = true break } } if !domainBelongsToQueue { // no split needed return nil } p.logger.Info("Split processing queue", tag.QueueLevel(currentQueueState.Level()), tag.PreviousQueueLevel(p.newQueueLevel), tag.WorkflowDomainIDs(p.domainIDs), tag.QueueSplitPolicyType(policyTypeSelectedDomain), ) p.metricsScope.IncCounter(metrics.ProcessingQueueSelectedDomainSplitCounter) return []ProcessingQueueState{ newProcessingQueueState( currentQueueState.Level(), currentQueueState.AckLevel(), currentQueueState.ReadLevel(), currentQueueState.MaxLevel(), currentDomainFilter.Exclude(p.domainIDs), ), newProcessingQueueState( p.newQueueLevel, currentQueueState.AckLevel(), currentQueueState.ReadLevel(), currentQueueState.MaxLevel(), // make a copy here so that it won't be accidentally modified when p.domainID is changed NewDomainFilter(p.domainIDs, false).copy(), ), } } func (p *randomSplitPolicy) Evaluate(queue ProcessingQueue) []ProcessingQueueState { queueImpl := queue.(*processingQueueImpl) if queueImpl.state.level == p.maxNewQueueLevel { // already reaches max level, skip splitting return nil } domainIDs := make(map[string]struct{}) for _, task := range queueImpl.outstandingTasks { domainIDs[task.GetDomainID()] = struct{}{} } if len(domainIDs) == 0 { return nil } domainToSplit := make(map[string]struct{}) for domainID := range domainIDs { if !p.enabledByDomainID(domainID) { continue } if !shouldSplit(p.splitProbability) { continue } domainToSplit[domainID] = struct{}{} } if len(domainToSplit) == 0 { return nil } newQueueLevel := queueImpl.state.level + 1 // split stuck tasks to current level + 1 p.logger.Info("Split processing queue", tag.QueueLevel(newQueueLevel), tag.PreviousQueueLevel(queueImpl.state.level), tag.WorkflowDomainIDs(domainToSplit), tag.QueueSplitPolicyType(policyTypeRandom), ) p.metricsScope.IncCounter(metrics.ProcessingQueueRandomSplitCounter) return splitQueueHelper( queueImpl, domainToSplit, newQueueLevel, p.lookAheadFunc, ) } func (p *aggregatedSplitPolicy) Evaluate(queue ProcessingQueue) []ProcessingQueueState { for _, policy := range p.policies { newStates := policy.Evaluate(queue) if len(newStates) != 0 { return newStates } } return nil } // splitQueueHelper assumes domainToSplit is not empty func splitQueueHelper( queueImpl *processingQueueImpl, domainToSplit map[string]struct{}, newQueueLevel int, lookAheadFunc lookAheadFunc, ) []ProcessingQueueState { newMaxLevel := queueImpl.state.readLevel if lookAheadFunc != nil { for domainID := range domainToSplit { newMaxLevel = maxTaskKey(newMaxLevel, lookAheadFunc(queueImpl.state.readLevel, domainID)) } } if queueImpl.state.maxLevel.Less(newMaxLevel) { newMaxLevel = queueImpl.state.maxLevel } newQueueStates := []ProcessingQueueState{ newProcessingQueueState( newQueueLevel, queueImpl.state.ackLevel, queueImpl.state.readLevel, newMaxLevel, NewDomainFilter(domainToSplit, false), ), } excludedDomainFilter := queueImpl.state.domainFilter.Exclude(domainToSplit) if excludedDomainFilter.ReverseMatch || len(excludedDomainFilter.DomainIDs) != 0 { // this means the new domain filter still matches at least one domain newQueueStates = append(newQueueStates, newProcessingQueueState( queueImpl.state.level, queueImpl.state.ackLevel, queueImpl.state.readLevel, newMaxLevel, excludedDomainFilter, )) } if !taskKeyEquals(newMaxLevel, queueImpl.state.maxLevel) { newQueueStates = append(newQueueStates, newProcessingQueueState( queueImpl.state.level, newMaxLevel, newMaxLevel, queueImpl.state.maxLevel, queueImpl.state.domainFilter.copy(), )) } for _, state := range newQueueStates { if state.ReadLevel().Less(state.AckLevel()) || state.MaxLevel().Less(state.ReadLevel()) { panic(fmt.Sprintf("invalid processing queue split result: %v, state before split: %v, newMaxLevel: %v", state, queueImpl.state, newMaxLevel)) } } return newQueueStates } func shouldSplit(probability float64) bool { if probability <= 0 { return false } if probability >= 1.0 { return true } return rand.Intn(int(1.0/probability)) == 0 } ================================================ FILE: service/history/queue/split_policy_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package queue import ( "sort" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" t "github.com/uber/cadence/common/task" "github.com/uber/cadence/service/history/task" ) type ( splitPolicySuite struct { suite.Suite *require.Assertions controller *gomock.Controller logger log.Logger metricsScope metrics.Scope } ) func TestSplitPolicySuite(t *testing.T) { s := new(splitPolicySuite) suite.Run(t, s) } func (s *splitPolicySuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.logger = testlogger.New(s.Suite.T()) s.metricsScope = metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}).Scope(metrics.TimerQueueProcessorScope) } func (s *splitPolicySuite) TearDownTest() { s.controller.Finish() } func (s *splitPolicySuite) TestPendingTaskSplitPolicy() { maxNewQueueLevel := 3 pendingTaskThreshold := map[int]int{ 0: 10, 1: 100, 2: 1000, 3: 10000, } lookAheadTasks := 5 lookAheadFunc := func(key task.Key, _ string) task.Key { currentID := key.(testKey).ID return testKey{ID: currentID + lookAheadTasks} } pendingTaskSplitPolicy := NewPendingTaskSplitPolicy( pendingTaskThreshold, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), lookAheadFunc, maxNewQueueLevel, s.logger, s.metricsScope, ) testCases := []struct { currentState ProcessingQueueState numPendingTasksPerDomain map[string]int // domainID -> number of pending tasks expectedNewStates []ProcessingQueueState }{ { currentState: newProcessingQueueState( 101, // a level which has no threshold specified testKey{ID: 0}, testKey{ID: 1000}, testKey{ID: 10000}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), numPendingTasksPerDomain: map[string]int{ "testDomain1": 1000, }, expectedNewStates: nil, }, { currentState: newProcessingQueueState( 3, // maxNewQueueLevel testKey{ID: 0}, testKey{ID: 100000}, testKey{ID: 1000000}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), numPendingTasksPerDomain: map[string]int{ "testDomain2": 100000, }, expectedNewStates: nil, }, { currentState: newProcessingQueueState( 1, testKey{ID: 0}, testKey{ID: 198}, testKey{ID: 200}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), numPendingTasksPerDomain: map[string]int{ "testDomain1": 99, "testDomain2": 99, }, expectedNewStates: nil, }, { currentState: newProcessingQueueState( 2, testKey{ID: 0}, testKey{ID: 2002}, testKey{ID: 2002 + lookAheadTasks + 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), numPendingTasksPerDomain: map[string]int{ "testDomain1": 1001, "testDomain2": 1001, }, expectedNewStates: []ProcessingQueueState{ newProcessingQueueState( 3, testKey{ID: 0}, testKey{ID: 2002}, testKey{ID: 2002 + lookAheadTasks}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), newProcessingQueueState( 2, testKey{ID: 2002 + lookAheadTasks}, testKey{ID: 2002 + lookAheadTasks}, testKey{ID: 2002 + lookAheadTasks + 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), }, }, { currentState: newProcessingQueueState( 2, testKey{ID: 0}, testKey{ID: 1001}, testKey{ID: 1001 + lookAheadTasks}, NewDomainFilter( map[string]struct{}{"testDomain1": {}}, false, ), ), numPendingTasksPerDomain: map[string]int{ "testDomain1": 1001, }, expectedNewStates: []ProcessingQueueState{ newProcessingQueueState( 3, testKey{ID: 0}, testKey{ID: 1001}, testKey{ID: 1001 + lookAheadTasks}, NewDomainFilter( map[string]struct{}{"testDomain1": {}}, false, ), ), }, }, { currentState: newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 109}, testKey{ID: 109 + lookAheadTasks + 100}, NewDomainFilter( map[string]struct{}{"testDomain1": {}}, true, ), ), numPendingTasksPerDomain: map[string]int{ "testDomain2": 9, "testDomain3": 100, }, expectedNewStates: []ProcessingQueueState{ newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 109}, testKey{ID: 109 + lookAheadTasks}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain3": {}}, true, ), ), newProcessingQueueState( 0, testKey{ID: 109 + lookAheadTasks}, testKey{ID: 109 + lookAheadTasks}, testKey{ID: 109 + lookAheadTasks + 100}, NewDomainFilter( map[string]struct{}{"testDomain1": {}}, true, ), ), newProcessingQueueState( 1, testKey{ID: 0}, testKey{ID: 109}, testKey{ID: 109 + lookAheadTasks}, NewDomainFilter( map[string]struct{}{"testDomain3": {}}, false, ), ), }, }, } for _, tc := range testCases { outstandingTasks := make(map[task.Key]task.Task) for domainID, numPendingTasks := range tc.numPendingTasksPerDomain { for i := 0; i != numPendingTasks; i++ { mockTask := task.NewMockTask(s.controller) mockTask.EXPECT().GetDomainID().Return(domainID).MaxTimes(1) mockTask.EXPECT().State().Return(t.TaskStatePending).MaxTimes(1) outstandingTasks[task.NewMockKey(s.controller)] = mockTask } } queue := newProcessingQueue( tc.currentState, outstandingTasks, nil, nil, ) s.assertQueueStatesEqual(tc.expectedNewStates, pendingTaskSplitPolicy.Evaluate(queue)) } } func (s *splitPolicySuite) TestStuckTaskSplitPolicy() { maxNewQueueLevel := 3 attemptThreshold := map[int]int{ 0: 10, 1: 100, 2: 1000, 3: 10000, } stuckTaskSplitPolicy := NewStuckTaskSplitPolicy( attemptThreshold, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), maxNewQueueLevel, s.logger, s.metricsScope, ) testCases := []struct { currentState ProcessingQueueState pendingTaskAttempts map[string][]int // domainID -> list of task attempts expectedNewStates []ProcessingQueueState }{ { currentState: newProcessingQueueState( 101, // a level which has no threshold specified testKey{ID: 0}, testKey{ID: 3}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), pendingTaskAttempts: map[string][]int{ "testDomain1": {1, 1000, 10000}, }, expectedNewStates: nil, }, { currentState: newProcessingQueueState( 3, // maxNewQueueLevel testKey{ID: 0}, testKey{ID: 1}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), pendingTaskAttempts: map[string][]int{ "testDomain2": {100000}, }, expectedNewStates: nil, }, { currentState: newProcessingQueueState( 1, testKey{ID: 0}, testKey{ID: 4}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), pendingTaskAttempts: map[string][]int{ "testDomain1": {0, 99}, "testDomain2": {0, 99}, }, expectedNewStates: nil, }, { currentState: newProcessingQueueState( 2, testKey{ID: 0}, testKey{ID: 4}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), pendingTaskAttempts: map[string][]int{ "testDomain1": {0, 99, 1001}, "testDomain2": {1001}, }, expectedNewStates: []ProcessingQueueState{ newProcessingQueueState( 3, testKey{ID: 0}, testKey{ID: 4}, testKey{ID: 4}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), newProcessingQueueState( 2, testKey{ID: 4}, testKey{ID: 4}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), }, }, { currentState: newProcessingQueueState( 2, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, NewDomainFilter( map[string]struct{}{"testDomain1": {}}, false, ), ), pendingTaskAttempts: map[string][]int{ "testDomain1": {0, 1, 99, 1001, 0}, }, expectedNewStates: []ProcessingQueueState{ newProcessingQueueState( 3, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, NewDomainFilter( map[string]struct{}{"testDomain1": {}}, false, ), ), }, }, { currentState: newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}}, true, ), ), pendingTaskAttempts: map[string][]int{ "testDomain2": {0, 1, 9}, "testDomain3": {1, 100}, }, expectedNewStates: []ProcessingQueueState{ newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain3": {}}, true, ), ), newProcessingQueueState( 0, testKey{ID: 5}, testKey{ID: 5}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}}, true, ), ), newProcessingQueueState( 1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 5}, NewDomainFilter( map[string]struct{}{"testDomain3": {}}, false, ), ), }, }, } for _, tc := range testCases { outstandingTasks := make(map[task.Key]task.Task) for domainID, taskAttempts := range tc.pendingTaskAttempts { for _, attempt := range taskAttempts { mockTask := task.NewMockTask(s.controller) mockTask.EXPECT().GetDomainID().Return(domainID).MaxTimes(1) mockTask.EXPECT().GetAttempt().Return(attempt).MaxTimes(1) outstandingTasks[task.NewMockKey(s.controller)] = mockTask } } queue := newProcessingQueue( tc.currentState, outstandingTasks, nil, nil, ) s.assertQueueStatesEqual(tc.expectedNewStates, stuckTaskSplitPolicy.Evaluate(queue)) } } func (s *splitPolicySuite) TestSelectedDomainSplitPolicy() { newQueueLevel := 123 testCases := []struct { currentState ProcessingQueueState domainToSplit map[string]struct{} expectedNewStates []ProcessingQueueState }{ { currentState: newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 0}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), domainToSplit: map[string]struct{}{"testDomain3": {}, "testDomain4": {}}, expectedNewStates: nil, }, { currentState: newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, true, ), ), domainToSplit: map[string]struct{}{"testDomain3": {}}, expectedNewStates: []ProcessingQueueState{ newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}, "testDomain3": {}}, true, ), ), newProcessingQueueState( newQueueLevel, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain3": {}}, false, ), ), }, }, } for _, tc := range testCases { queue := NewProcessingQueue(tc.currentState, nil, nil) splitPolicy := NewSelectedDomainSplitPolicy(tc.domainToSplit, newQueueLevel, s.logger, s.metricsScope) s.assertQueueStatesEqual(tc.expectedNewStates, splitPolicy.Evaluate(queue)) } } func (s *splitPolicySuite) TestRandomSplitPolicy() { maxNewQueueLevel := 3 lookAheadFunc := func(key task.Key, _ string) task.Key { currentID := key.(testKey).ID return testKey{ID: currentID + 10} } testCases := []struct { currentState ProcessingQueueState splitProbability float64 numPendingTasksPerDomain map[string]int // domainID -> number of pending tasks expectedNewStates []ProcessingQueueState }{ { currentState: newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 0}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), splitProbability: 0, numPendingTasksPerDomain: nil, expectedNewStates: nil, }, { currentState: newProcessingQueueState( 3, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}, "testDomain2": {}}, false, ), ), splitProbability: 1, numPendingTasksPerDomain: map[string]int{ "testDomain1": 2, "testDomain2": 3, }, expectedNewStates: nil, }, { currentState: newProcessingQueueState( 0, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}}, false, ), ), splitProbability: 1, numPendingTasksPerDomain: map[string]int{ "testDomain1": 5, }, expectedNewStates: []ProcessingQueueState{ newProcessingQueueState( 1, testKey{ID: 0}, testKey{ID: 5}, testKey{ID: 10}, NewDomainFilter( map[string]struct{}{"testDomain1": {}}, false, ), ), }, }, } for _, tc := range testCases { outstandingTasks := make(map[task.Key]task.Task) for domainID, numPendingTasks := range tc.numPendingTasksPerDomain { for i := 0; i != numPendingTasks; i++ { mockTask := task.NewMockTask(s.controller) mockTask.EXPECT().GetDomainID().Return(domainID).MaxTimes(1) outstandingTasks[task.NewMockKey(s.controller)] = mockTask } } queue := newProcessingQueue( tc.currentState, outstandingTasks, nil, nil, ) splitPolicy := NewRandomSplitPolicy( tc.splitProbability, dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), maxNewQueueLevel, lookAheadFunc, s.logger, s.metricsScope, ) s.assertQueueStatesEqual(tc.expectedNewStates, splitPolicy.Evaluate(queue)) } } func (s *splitPolicySuite) TestAggregatedSplitPolicy() { expectedNewStates := []ProcessingQueueState{ NewMockProcessingQueueState(s.controller), NewMockProcessingQueueState(s.controller), NewMockProcessingQueueState(s.controller), } mockProcessingQueue := NewMockProcessingQueue(s.controller) totalPolicyNum := 4 policyEvaluationResults := [][]ProcessingQueueState{ nil, {}, expectedNewStates, } mockSplitPolicies := []ProcessingQueueSplitPolicy{} for i := 0; i != totalPolicyNum; i++ { mockSplitPolicy := NewMockProcessingQueueSplitPolicy(s.controller) if i < len(policyEvaluationResults) { mockSplitPolicy.EXPECT().Evaluate(mockProcessingQueue).Return(policyEvaluationResults[i]).Times(1) } mockSplitPolicies = append(mockSplitPolicies, mockSplitPolicy) } aggregatedSplitPolicy := NewAggregatedSplitPolicy(mockSplitPolicies...) s.Equal(expectedNewStates, aggregatedSplitPolicy.Evaluate(mockProcessingQueue)) } func (s *splitPolicySuite) assertQueueStatesEqual( expected []ProcessingQueueState, actual []ProcessingQueueState, ) { s.Equal(len(expected), len(actual)) if len(expected) == 0 { return } compFn := func(s1, s2 ProcessingQueueState) bool { if taskKeyEquals(s1.AckLevel(), s2.AckLevel()) { return s1.Level() < s2.Level() } return s1.AckLevel().Less(s2.AckLevel()) } sort.Slice(expected, func(i, j int) bool { return compFn(expected[i], expected[j]) }) sort.Slice(actual, func(i, j int) bool { return compFn(actual[i], actual[j]) }) for i := 0; i != len(expected); i++ { s.Equal(expected[i], actual[i]) } } ================================================ FILE: service/history/queue/task_allocator.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package queue import ( "context" "encoding/json" "errors" "runtime/debug" "sync" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/shard" htask "github.com/uber/cadence/service/history/task" ) type ( // TaskAllocator verifies if a task should be processed or not TaskAllocator interface { VerifyActiveTask(domainID, wfID, rID string, task interface{}) (bool, error) VerifyFailoverActiveTask(targetDomainIDs map[string]struct{}, domainID, wfID, rID string, task interface{}) (bool, error) VerifyStandbyTask(standbyCluster string, domainID, wfID, rID string, task interface{}) (bool, error) Lock() Unlock() } taskAllocatorImpl struct { currentClusterName string shard shard.Context domainCache cache.DomainCache activeClusterMgr activecluster.Manager logger log.Logger locker sync.RWMutex } ) // NewTaskAllocator create a new task allocator func NewTaskAllocator(shard shard.Context) TaskAllocator { return &taskAllocatorImpl{ currentClusterName: shard.GetService().GetClusterMetadata().GetCurrentClusterName(), shard: shard, domainCache: shard.GetDomainCache(), activeClusterMgr: shard.GetActiveClusterManager(), logger: shard.GetLogger(), } } // VerifyActiveTask, will return true if task activeness check is successful func (t *taskAllocatorImpl) VerifyActiveTask(domainID, wfID, rID string, task interface{}) (bool, error) { return t.verifyTaskActiveness(t.currentClusterName, domainID, wfID, rID, task, false) } // VerifyFailoverActiveTask, will return true if task activeness check is successful func (t *taskAllocatorImpl) VerifyFailoverActiveTask(targetDomainIDs map[string]struct{}, domainID, wfID, rID string, task interface{}) (bool, error) { _, ok := targetDomainIDs[domainID] if !ok { return false, nil } return t.verifyTaskActiveness("", domainID, wfID, rID, task, true) } // VerifyStandbyTask, will return true if task standbyness check is successful func (t *taskAllocatorImpl) VerifyStandbyTask(standbyCluster string, domainID, wfID, rID string, task interface{}) (bool, error) { return t.verifyTaskActiveness(standbyCluster, domainID, wfID, rID, task, true) } // verifyTaskActiveness verifies if a task should be processed or not based on domain's state // - If failed to fetch the domain, it returns (false, err) indicating the task should be retried // - If domain is not found, it returns (false, nil) indicating the task should be skipped // - If domain is local, return (!skipLocalDomain, nil) indicating the task should be skipped if skipLocalDomain is true // - If domain is pending active, it returns (false, ErrTaskPendingActive) indicating the task should be retried // - If domain is active in the given cluster, it returns (true, nil) indicating the task should be processed // Special case: if it's a failover queue (cluster == ""), it returns (true, nil) indicating the task should be processed in any cluster func (t *taskAllocatorImpl) verifyTaskActiveness(cluster string, domainID, wfID, rID string, task interface{}, skipLocalDomain bool) (b bool, e error) { if t.logger.DebugOn() { defer func() { taskString := "nil" if task != nil { data, err := json.Marshal(task) if err != nil { t.logger.Error("Failed to marshal task.", tag.Error(err)) taskString = "nil" } else { taskString = string(data) } } t.logger.Debugf("verifyTaskActiveness returning (%v, %v) for cluster %s, domainID %s, wfID %s, rID %s, task %s, stacktrace %s", b, e, cluster, domainID, wfID, rID, taskString, string(debug.Stack()), ) }() } t.locker.RLock() defer t.locker.RUnlock() domainEntry, err := t.domainCache.GetDomainByID(domainID) if err != nil { // it is possible that the domain is deleted // we should treat that domain as not active if _, ok := err.(*types.EntityNotExistsError); !ok { t.logger.Warn("Failed to get domain from cache", tag.WorkflowDomainID(domainID), tag.Error(err)) return false, err } t.logger.Warn("Cannot find domain, default to not process task.", tag.WorkflowDomainID(domainID), tag.Value(task)) return false, nil } // handle local domain if !domainEntry.IsGlobalDomain() { // only active in domain's cluster but should be skipped if skipLocalDomain is true return !skipLocalDomain && t.currentClusterName == cluster, nil } // return error for pending active domain so the task can be retried if err := t.checkDomainPendingActive( domainEntry, domainID, task, ); err != nil { return false, err } if cluster == "" { // failover queue task. Revisit this logic. It's copied from previous implementation return true, nil } // handle active-active domain if domainEntry.GetReplicationConfig().IsActiveActive() { resp, err := t.activeClusterMgr.GetActiveClusterInfoByWorkflow(context.Background(), domainID, wfID, rID) if err != nil { t.logger.Warn("Failed to lookup active cluster", tag.WorkflowDomainID(domainID), tag.WorkflowID(wfID), tag.WorkflowRunID(rID), tag.Error(err), ) return false, err } if resp.ActiveClusterName != cluster { t.logger.Debug("Skip task because workflow is not active on the given cluster", tag.WorkflowID(wfID), tag.WorkflowDomainID(domainID), tag.ClusterName(cluster), ) return false, nil } t.logger.Debug("Active cluster for given task", tag.WorkflowDomainID(domainID), tag.WorkflowID(wfID), tag.WorkflowRunID(rID), tag.ClusterName(resp.ActiveClusterName), ) return true, nil } // handle active-passive domain if domainEntry.GetReplicationConfig().ActiveClusterName != cluster { t.logger.Debug("Domain is not active in the given cluster, skip task.", tag.WorkflowDomainID(domainID), tag.WorkflowID(wfID), tag.WorkflowRunID(rID), tag.ClusterName(cluster), ) return false, nil } return true, nil } func (t *taskAllocatorImpl) checkDomainPendingActive(domainEntry *cache.DomainCacheEntry, taskDomainID string, task interface{}) error { if domainEntry.IsGlobalDomain() && domainEntry.GetFailoverEndTime() != nil { // the domain is pending active, pause on processing this task t.logger.Debug("Domain is not in pending active, skip task.", tag.WorkflowDomainID(taskDomainID), tag.Value(task)) return htask.ErrTaskPendingActive } return nil } // Lock block all task allocation func (t *taskAllocatorImpl) Lock() { t.locker.Lock() } // Unlock resume the task allocator func (t *taskAllocatorImpl) Unlock() { t.locker.Unlock() } // isDomainNotRegistered checks either if domain does not exist or is in deprecated or deleted status func isDomainNotRegistered(shard shard.Context, domainID string) (bool, error) { domainEntry, err := shard.GetDomainCache().GetDomainByID(domainID) if err != nil { // error in finding a domain return false, err } info := domainEntry.GetInfo() if info == nil { return false, errors.New("domain info is nil in cache") } return info.Status == persistence.DomainStatusDeprecated || info.Status == persistence.DomainStatusDeleted, nil } ================================================ FILE: service/history/queue/task_allocator_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "errors" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/shard" htask "github.com/uber/cadence/service/history/task" ) func TestVerifyActiveTask(t *testing.T) { domainID := "testDomainID" task := "testTask" tests := []struct { name string setupMocks func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) expectedResult bool expectedErrorString string }{ { name: "Failed to get domain from cache, non-EntityNotExistsError", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(nil, errors.New("some error")) }, expectedResult: false, expectedErrorString: "some error", }, { name: "Domain not found, EntityNotExistsError", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(nil, &types.EntityNotExistsError{}) }, expectedResult: false, }, { name: "Domain is global and not active in current cluster", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: domainID + "name"}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: "otherCluster", }, 1, ) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, expectedResult: false, }, { name: "Domain is global and pending active in current cluster", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { endtime := int64(1) domainEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: domainID + "name"}, nil, true, &persistence.DomainReplicationConfig{ ActiveClusterName: "currentCluster", }, 1, &endtime, 1, 1, 1, ) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, expectedResult: false, expectedErrorString: "the domain is pending-active", }, { name: "Domain is global and active in current cluster", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{Name: domainID + "name"}, nil, &persistence.DomainReplicationConfig{ ActiveClusterName: "currentCluster", }, 1, ) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, expectedResult: true, }, { name: "Domain is active-active mode, task is not active in current cluster so should be skipped", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := activeActiveDomainEntry() mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) activeClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ActiveClusterInfo{ ActiveClusterName: "another-cluster", }, nil) }, expectedResult: false, }, { name: "Domain is active-active mode, task is active in current cluster so should be processed", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := activeActiveDomainEntry() mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) activeClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ActiveClusterInfo{ ActiveClusterName: "currentCluster", }, nil) }, expectedResult: true, }, { name: "Domain is active-active mode, activeness lookup returns error", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := activeActiveDomainEntry() mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) activeClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, errors.New("some error")) }, expectedResult: false, expectedErrorString: "some error", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) // Create mocks mockShard := shard.NewMockContext(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) // Setup mock shard to return mock domain cache and logger mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).AnyTimes() mockShard.EXPECT().GetService().Return(nil).AnyTimes() // Adjust based on your implementation activeClusterMgr := activecluster.NewMockManager(ctrl) // Create the task allocator allocator := &taskAllocatorImpl{ currentClusterName: "currentCluster", shard: mockShard, domainCache: mockDomainCache, logger: log.NewNoop(), activeClusterMgr: activeClusterMgr, } tt.setupMocks(mockDomainCache, activeClusterMgr) result, err := allocator.VerifyActiveTask(domainID, "wfid", "rid", task) assert.Equal(t, tt.expectedResult, result) if tt.expectedErrorString != "" { assert.Contains(t, err.Error(), tt.expectedErrorString) } else { assert.NoError(t, err) } }) } } func TestVerifyFailoverActiveTask(t *testing.T) { domainID := "testDomainID" task := "testTask" tests := []struct { name string targetDomainIDs map[string]struct{} setupMocks func(mockDomainCache *cache.MockDomainCache) expectedResult bool expectedError error expectedErrorString string }{ { name: "Domain not in targetDomainIDs", targetDomainIDs: map[string]struct{}{ "someOtherDomainID": {}, }, setupMocks: func(mockDomainCache *cache.MockDomainCache) { // No mocks needed since the domain is not in targetDomainIDs }, expectedResult: false, expectedError: nil, }, { name: "Domain in targetDomainIDs, GetDomainByID returns non-EntityNotExistsError", targetDomainIDs: map[string]struct{}{ domainID: {}, }, setupMocks: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(nil, errors.New("some error")) }, expectedResult: false, expectedError: errors.New("some error"), expectedErrorString: "some error", }, { name: "Domain in targetDomainIDs, GetDomainByID returns EntityNotExistsError", targetDomainIDs: map[string]struct{}{ domainID: {}, }, setupMocks: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(nil, &types.EntityNotExistsError{}) }, expectedResult: false, expectedError: nil, }, { name: "Domain in targetDomainIDs, domain is pending active", targetDomainIDs: map[string]struct{}{ domainID: {}, }, setupMocks: func(mockDomainCache *cache.MockDomainCache) { // Set up a domainEntry that is global and has a non-nil FailoverEndTime endtime := int64(1) domainEntry := cache.NewDomainCacheEntryForTest( nil, nil, true, // IsGlobalDomain &persistence.DomainReplicationConfig{}, 1, &endtime, // FailoverEndTime is non-nil 1, 1, 1, ) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, expectedResult: false, expectedError: htask.ErrTaskPendingActive, }, { name: "Domain in targetDomainIDs, checkDomainPendingActive returns nil", targetDomainIDs: map[string]struct{}{ domainID: {}, }, setupMocks: func(mockDomainCache *cache.MockDomainCache) { // Set up a domainEntry that is global and has a nil FailoverEndTime domainEntry := cache.NewDomainCacheEntryForTest( nil, nil, true, // IsGlobalDomain &persistence.DomainReplicationConfig{}, 1, nil, // FailoverEndTime is nil 1, 1, 1, ) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, expectedResult: true, expectedError: nil, }, { name: "Domain is local, should be skipped", targetDomainIDs: map[string]struct{}{ domainID: {}, }, setupMocks: func(mockDomainCache *cache.MockDomainCache) { // Set up a domainEntry that is global and has a nil FailoverEndTime domainEntry := cache.NewDomainCacheEntryForTest( nil, nil, false, // IsGlobalDomain &persistence.DomainReplicationConfig{}, 1, nil, 1, 1, 1, ) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, expectedResult: false, expectedError: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) // Create mocks mockShard := shard.NewMockContext(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) // Setup mock shard to return mock domain cache and logger mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).AnyTimes() mockShard.EXPECT().GetService().Return(nil).AnyTimes() // Adjust based on your implementation // Create the task allocator allocator := &taskAllocatorImpl{ currentClusterName: "currentCluster", shard: mockShard, domainCache: mockDomainCache, logger: log.NewNoop(), } tt.setupMocks(mockDomainCache) result, err := allocator.VerifyFailoverActiveTask(tt.targetDomainIDs, domainID, "wfid", "rid", task) assert.Equal(t, tt.expectedResult, result) if tt.expectedError != nil { assert.Equal(t, tt.expectedError, err) } else if tt.expectedErrorString != "" { assert.Contains(t, err.Error(), tt.expectedErrorString) } else { assert.NoError(t, err) } }) } } func TestVerifyStandbyTask(t *testing.T) { domainID := "testDomainID" task := "testTask" tests := []struct { name string setupMocks func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) standbyCluster string expectedResult bool expectedError error expectedErrorString string }{ { name: "GetDomainByID returns non-EntityNotExistsError", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(nil, errors.New("some error")) }, standbyCluster: "standbyCluster", expectedResult: false, expectedErrorString: "some error", }, { name: "GetDomainByID returns EntityNotExistsError", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(nil, &types.EntityNotExistsError{}) }, standbyCluster: "standbyCluster", expectedResult: false, expectedError: nil, }, { name: "Domain is not global", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := cache.NewLocalDomainCacheEntryForTest( &persistence.DomainInfo{}, &persistence.DomainConfig{}, "", ) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, standbyCluster: "standbyCluster", expectedResult: false, expectedError: nil, }, { name: "Domain is global but not standby (active cluster name does not match standbyCluster)", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{}, &persistence.DomainConfig{}, &persistence.DomainReplicationConfig{ ActiveClusterName: "anotherCluster", }, 0, ) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, standbyCluster: "standbyCluster", expectedResult: false, expectedError: nil, }, { name: "Domain is global and standby, but checkDomainPendingActive returns error", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { endTime := time.Now().Add(time.Hour).UnixNano() domainEntry := cache.NewDomainCacheEntryForTest(nil, nil, true, &persistence.DomainReplicationConfig{ ActiveClusterName: "currentCluster", }, 1, &endTime, 1, 1, 1) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, standbyCluster: "currentCluster", expectedResult: false, expectedErrorString: htask.ErrTaskPendingActive.Error(), }, { name: "Domain is global and standby, checkDomainPendingActive returns nil", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{}, &persistence.DomainConfig{}, &persistence.DomainReplicationConfig{ ActiveClusterName: "standbyCluster", }, 0, // FailoverEndTime is zero ) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, standbyCluster: "standbyCluster", expectedResult: true, expectedError: nil, }, { name: "Domain is local, should be skipped", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { // Set up a domainEntry that is global and has a nil FailoverEndTime domainEntry := cache.NewDomainCacheEntryForTest( nil, nil, false, // IsGlobalDomain &persistence.DomainReplicationConfig{}, 1, nil, 1, 1, 1, ) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) }, standbyCluster: "standbyCluster", expectedResult: false, expectedError: nil, }, { name: "Domain is active-active mode, task is not active in standby cluster so should be skipped", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := activeActiveDomainEntry() mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) activeClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ActiveClusterInfo{ ActiveClusterName: "another-cluster", }, nil) }, standbyCluster: "standbyCluster", expectedResult: false, expectedError: nil, }, { name: "Domain is active-active mode, task is active in standby cluster so should be processed", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := activeActiveDomainEntry() mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) activeClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ActiveClusterInfo{ ActiveClusterName: "standbyCluster", }, nil) }, standbyCluster: "standbyCluster", expectedResult: true, expectedError: nil, }, { name: "Domain is active-active mode, activeness lookup returns error", setupMocks: func(mockDomainCache *cache.MockDomainCache, activeClusterMgr *activecluster.MockManager) { domainEntry := activeActiveDomainEntry() mockDomainCache.EXPECT().GetDomainByID(domainID).Return(domainEntry, nil) activeClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, errors.New("some error")) }, standbyCluster: "standbyCluster", expectedResult: false, expectedError: errors.New("some error"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) // Create mocks mockShard := shard.NewMockContext(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) // Setup mock shard to return mock domain cache and logger mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).AnyTimes() mockShard.EXPECT().GetService().Return(nil).AnyTimes() // Adjust based on your implementation activeClusterMgr := activecluster.NewMockManager(ctrl) // Create the task allocator allocator := &taskAllocatorImpl{ currentClusterName: "currentCluster", shard: mockShard, domainCache: mockDomainCache, logger: log.NewNoop(), activeClusterMgr: activeClusterMgr, } tt.setupMocks(mockDomainCache, activeClusterMgr) result, err := allocator.VerifyStandbyTask(tt.standbyCluster, domainID, "wfid", "rid", task) assert.Equal(t, tt.expectedResult, result) if tt.expectedError != nil { assert.Equal(t, tt.expectedError, err) } else if tt.expectedErrorString != "" { assert.Contains(t, err.Error(), tt.expectedErrorString) } else { assert.NoError(t, err) } }) } } func TestIsDomainNotRegistered(t *testing.T) { tests := []struct { name string domainID string mockFn func(mockDomainCache *cache.MockDomainCache) expectedErrorString string }{ { name: "domainID return error", mockFn: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomainByID("").Return(nil, fmt.Errorf("testError")) }, domainID: "", expectedErrorString: "testError", }, { name: "cannot get info", domainID: "testDomainID", mockFn: func(mockDomainCache *cache.MockDomainCache) { domainEntry := cache.NewDomainCacheEntryForTest(nil, nil, false, nil, 0, nil, 0, 0, 0) mockDomainCache.EXPECT().GetDomainByID("testDomainID").Return(domainEntry, nil) }, expectedErrorString: "domain info is nil in cache", }, { name: "domain is deprecated", domainID: "testDomainID", mockFn: func(mockDomainCache *cache.MockDomainCache) { domainEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Status: persistence.DomainStatusDeprecated}, nil, false, nil, 0, nil, 0, 0, 0) mockDomainCache.EXPECT().GetDomainByID("testDomainID").Return(domainEntry, nil) }, expectedErrorString: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) // Create mocks mockShard := shard.NewMockContext(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) // Setup mock shard to return mock domain cache and logger mockShard.EXPECT().GetDomainCache().Return(mockDomainCache).AnyTimes() mockShard.EXPECT().GetService().Return(nil).AnyTimes() // Adjust based on your implementation tt.mockFn(mockDomainCache) res, err := isDomainNotRegistered(mockShard, tt.domainID) if tt.expectedErrorString != "" { assert.ErrorContains(t, err, tt.expectedErrorString) assert.False(t, res) } else { assert.NoError(t, err) assert.True(t, res) } }) } } func TestLockUnlock(t *testing.T) { // basic validation to ensure lock/unlock doesn't panic and get blocked allocator := &taskAllocatorImpl{} allocator.Lock() defer allocator.Unlock() } func activeActiveDomainEntry() *cache.DomainCacheEntry { // The specific versions are not important, as long as it's active-active. // Having a DomainReplicationConfig with non-zero length ActiveClusters is enough to make it active-active. return cache.NewDomainCacheEntryForTest( nil, nil, true, &persistence.DomainReplicationConfig{ // Populate ActiveClusters to make it active-active ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 1, }, }, }, }, }, }, 1, nil, 1, 1, 1, ) } ================================================ FILE: service/history/queue/timer_queue_active_processor.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) func newTimerQueueActiveProcessor( clusterName string, shard shard.Context, taskProcessor task.Processor, taskAllocator TaskAllocator, taskExecutor task.Executor, logger log.Logger, ) *timerQueueProcessorBase { config := shard.GetConfig() options := newTimerQueueProcessorOptions(config, true, false) logger = logger.WithTags(tag.ClusterName(clusterName)) taskFilter := func(timer persistence.Task) (bool, error) { if timer.GetTaskCategory() != persistence.HistoryTaskCategoryTimer { return false, errUnexpectedQueueTask } if notRegistered, err := isDomainNotRegistered(shard, timer.GetDomainID()); notRegistered && err == nil { // Allow deletion tasks for deprecated domains if timer.GetTaskType() == persistence.TaskTypeDeleteHistoryEvent { return true, nil } logger.Info("Domain is not in registered status, skip task in active timer queue.", tag.WorkflowDomainID(timer.GetDomainID()), tag.Value(timer)) return false, nil } return taskAllocator.VerifyActiveTask(timer.GetDomainID(), timer.GetWorkflowID(), timer.GetRunID(), timer) } updateMaxReadLevel := func() task.Key { return newTimerTaskKey(shard.UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryTimer, clusterName).GetScheduledTime(), 0) } updateClusterAckLevel := func(ackLevel task.Key) error { return shard.UpdateQueueClusterAckLevel(persistence.HistoryTaskCategoryTimer, clusterName, persistence.NewHistoryTaskKey(ackLevel.(timerTaskKey).visibilityTimestamp, 0)) } updateProcessingQueueStates := func(states []ProcessingQueueState) error { pStates := convertToPersistenceTimerProcessingQueueStates(states) return shard.UpdateTimerProcessingQueueStates(clusterName, pStates) } queueShutdown := func() error { return nil } return newTimerQueueProcessorBase( clusterName, shard, loadTimerProcessingQueueStates(clusterName, shard, options, logger), taskProcessor, clock.NewTimerGate(shard.GetTimeSource()), options, updateMaxReadLevel, updateClusterAckLevel, updateProcessingQueueStates, queueShutdown, taskFilter, taskExecutor, logger, shard.GetMetricsClient(), ) } ================================================ FILE: service/history/queue/timer_queue_failover_processor.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "time" "github.com/pborman/uuid" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) func newTimerQueueFailoverProcessor( standbyClusterName string, shardContext shard.Context, taskProcessor task.Processor, taskAllocator TaskAllocator, taskExecutor task.Executor, logger log.Logger, minLevel, maxLevel time.Time, domainIDs map[string]struct{}, ) (updateClusterAckLevelFn, *timerQueueProcessorBase) { config := shardContext.GetConfig() options := newTimerQueueProcessorOptions(config, true, true) currentClusterName := shardContext.GetService().GetClusterMetadata().GetCurrentClusterName() failoverStartTime := shardContext.GetTimeSource().Now() failoverUUID := uuid.New() logger = logger.WithTags( tag.ClusterName(currentClusterName), tag.WorkflowDomainIDs(domainIDs), tag.FailoverMsg("from: "+standbyClusterName), ) taskFilter := func(timer persistence.Task) (bool, error) { if timer.GetTaskCategory() != persistence.HistoryTaskCategoryTimer { return false, errUnexpectedQueueTask } if notRegistered, err := isDomainNotRegistered(shardContext, timer.GetDomainID()); notRegistered && err == nil { // Allow deletion tasks for deprecated domains if timer.GetTaskType() == persistence.TaskTypeDeleteHistoryEvent { return true, nil } logger.Info("Domain is not in registered status, skip task in failover timer queue.", tag.WorkflowDomainID(timer.GetDomainID()), tag.Value(timer)) return false, nil } return taskAllocator.VerifyFailoverActiveTask(domainIDs, timer.GetDomainID(), timer.GetWorkflowID(), timer.GetRunID(), timer) } maxReadLevelTaskKey := newTimerTaskKey(maxLevel, 0) updateMaxReadLevel := func() task.Key { return maxReadLevelTaskKey // this is a const } updateClusterAckLevel := func(ackLevel task.Key) error { return shardContext.UpdateFailoverLevel( persistence.HistoryTaskCategoryTimer, failoverUUID, persistence.FailoverLevel{ StartTime: failoverStartTime, MinLevel: persistence.NewHistoryTaskKey(minLevel, 0), CurrentLevel: persistence.NewHistoryTaskKey(ackLevel.(timerTaskKey).visibilityTimestamp, 0), MaxLevel: persistence.NewHistoryTaskKey(maxLevel, 0), DomainIDs: domainIDs, }, ) } queueShutdown := func() error { return shardContext.DeleteFailoverLevel( persistence.HistoryTaskCategoryTimer, failoverUUID, ) } processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( defaultProcessingQueueLevel, newTimerTaskKey(minLevel, 0), maxReadLevelTaskKey, NewDomainFilter(domainIDs, false), ), } return updateClusterAckLevel, newTimerQueueProcessorBase( currentClusterName, // should use current cluster's time when doing domain failover shardContext, processingQueueStates, taskProcessor, clock.NewTimerGate(shardContext.GetTimeSource()), options, updateMaxReadLevel, updateClusterAckLevel, nil, queueShutdown, taskFilter, taskExecutor, logger, shardContext.GetMetricsClient(), ) } ================================================ FILE: service/history/queue/timer_queue_processor.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "context" "errors" "fmt" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/types" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" "github.com/uber/cadence/service/worker/archiver" ) type timerQueueProcessor struct { shard shard.Context historyEngine engine.Engine taskProcessor task.Processor config *config.Config currentClusterName string metricsClient metrics.Client logger log.Logger status int32 shutdownChan chan struct{} shutdownWG sync.WaitGroup ackLevel time.Time taskAllocator TaskAllocator activeTaskExecutor task.Executor activeQueueProcessor *timerQueueProcessorBase standbyQueueProcessors map[string]*timerQueueProcessorBase standbyTaskExecutors []task.Executor standbyQueueTimerGates map[string]clock.EventTimerGate failoverQueueProcessors []*timerQueueProcessorBase } // NewTimerQueueProcessor creates a new timer QueueProcessor func NewTimerQueueProcessor( shard shard.Context, taskProcessor task.Processor, executionCache execution.Cache, archivalClient archiver.Client, executionCheck invariant.Invariant, ) Processor { logger := shard.GetLogger().WithTags(tag.ComponentTimerQueue) currentClusterName := shard.GetClusterMetadata().GetCurrentClusterName() config := shard.GetConfig() taskAllocator := NewTaskAllocator(shard) activeLogger := logger.WithTags(tag.QueueTypeActive) activeTaskExecutor := task.NewTimerActiveTaskExecutor( shard, archivalClient, executionCache, activeLogger, shard.GetMetricsClient(), config, ) activeQueueProcessor := newTimerQueueActiveProcessor( currentClusterName, shard, taskProcessor, taskAllocator, activeTaskExecutor, activeLogger, ) standbyTaskExecutors := make([]task.Executor, 0, len(shard.GetClusterMetadata().GetRemoteClusterInfo())) standbyQueueProcessors := make(map[string]*timerQueueProcessorBase) standbyQueueTimerGates := make(map[string]clock.EventTimerGate) for clusterName := range shard.GetClusterMetadata().GetRemoteClusterInfo() { historyResender := ndc.NewHistoryResender( shard.GetDomainCache(), shard.GetService().GetClientBean(), func(ctx context.Context, request *types.ReplicateEventsV2Request) error { return shard.GetEngine().ReplicateEventsV2(ctx, request) }, config.StandbyTaskReReplicationContextTimeout, executionCheck, shard.GetLogger(), ) standByLogger := logger.WithTags(tag.QueueTypeStandby, tag.ActiveClusterName(clusterName)) standbyTaskExecutor := task.NewTimerStandbyTaskExecutor( shard, archivalClient, executionCache, historyResender, standByLogger, shard.GetMetricsClient(), clusterName, config, ) standbyTaskExecutors = append(standbyTaskExecutors, standbyTaskExecutor) standbyQueueProcessors[clusterName], standbyQueueTimerGates[clusterName] = newTimerQueueStandbyProcessor( clusterName, shard, taskProcessor, taskAllocator, standbyTaskExecutor, standByLogger, ) } return &timerQueueProcessor{ shard: shard, taskProcessor: taskProcessor, config: config, currentClusterName: currentClusterName, metricsClient: shard.GetMetricsClient(), logger: logger, status: common.DaemonStatusInitialized, shutdownChan: make(chan struct{}), ackLevel: shard.GetQueueAckLevel(persistence.HistoryTaskCategoryTimer).GetScheduledTime(), taskAllocator: taskAllocator, activeTaskExecutor: activeTaskExecutor, activeQueueProcessor: activeQueueProcessor, standbyQueueProcessors: standbyQueueProcessors, standbyQueueTimerGates: standbyQueueTimerGates, standbyTaskExecutors: standbyTaskExecutors, } } func (t *timerQueueProcessor) Start() { if !atomic.CompareAndSwapInt32(&t.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } t.logger.Info("Starting timer queue processor") defer t.logger.Info("Timer queue processor started") t.activeQueueProcessor.Start() for _, standbyQueueProcessor := range t.standbyQueueProcessors { standbyQueueProcessor.Start() } t.shutdownWG.Add(1) go t.completeTimerLoop() } func (t *timerQueueProcessor) Stop() { if !atomic.CompareAndSwapInt32(&t.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } if !t.shard.GetConfig().QueueProcessorEnableGracefulSyncShutdown() { t.logger.Info("Stopping timer queue processor non-gracefully") defer t.logger.Info("Timer queue processor stopped non-gracefully") t.activeQueueProcessor.Stop() // stop active executor after queue processor t.activeTaskExecutor.Stop() for _, standbyQueueProcessor := range t.standbyQueueProcessors { standbyQueueProcessor.Stop() } // stop standby executors after queue processors for _, standbyTaskExecutor := range t.standbyTaskExecutors { standbyTaskExecutor.Stop() } close(t.shutdownChan) if !common.AwaitWaitGroup(&t.shutdownWG, time.Minute) { t.logger.Warn("timerQueueProcessor timed out on shut down", tag.LifeCycleStopTimedout) } return } t.logger.Info("Stopping timer queue processor gracefully") defer t.logger.Info("Timer queue processor stopped gracefully") // close the shutdown channel first so processor pumps drains tasks // and then stop the processors close(t.shutdownChan) if !common.AwaitWaitGroup(&t.shutdownWG, gracefulShutdownTimeout) { t.logger.Warn("transferQueueProcessor timed out on shut down", tag.LifeCycleStopTimedout) } t.activeQueueProcessor.Stop() for _, standbyQueueProcessor := range t.standbyQueueProcessors { standbyQueueProcessor.Stop() } // stop standby executors after queue processors for _, standbyTaskExecutor := range t.standbyTaskExecutors { standbyTaskExecutor.Stop() } if len(t.failoverQueueProcessors) > 0 { t.logger.Info("Shutting down failover timer queues", tag.Counter(len(t.failoverQueueProcessors))) for _, failoverQueueProcessor := range t.failoverQueueProcessors { failoverQueueProcessor.Stop() } } t.activeTaskExecutor.Stop() } func (t *timerQueueProcessor) NotifyNewTask(clusterName string, info *hcommon.NotifyTaskInfo) { if clusterName == t.currentClusterName { t.activeQueueProcessor.notifyNewTimers(info.Tasks) return } standbyQueueProcessor, ok := t.standbyQueueProcessors[clusterName] if !ok { panic(fmt.Sprintf("Cannot find standby timer processor for %s.", clusterName)) } standbyQueueTimerGate, ok := t.standbyQueueTimerGates[clusterName] if !ok { panic(fmt.Sprintf("Cannot find standby timer gate for %s.", clusterName)) } curTime := t.shard.GetCurrentTime(clusterName) standbyQueueTimerGate.SetCurrentTime(curTime) t.logger.Debug("Current time for standby queue timergate is updated", tag.ClusterName(clusterName), tag.Timestamp(curTime)) standbyQueueProcessor.notifyNewTimers(info.Tasks) } func (t *timerQueueProcessor) FailoverDomain(domainIDs map[string]struct{}) { if t.shard.GetConfig().DisableTimerFailoverQueue() { return } // Failover queue is used to scan all inflight tasks, if queue processor is not // started, there's no inflight task and we don't need to create a failover processor. // Also the HandleAction will be blocked if queue processor processing loop is not running. if atomic.LoadInt32(&t.status) != common.DaemonStatusStarted { return } minLevel := t.shard.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryTimer, t.currentClusterName).GetScheduledTime() standbyClusterName := t.currentClusterName for clusterName := range t.shard.GetClusterMetadata().GetEnabledClusterInfo() { ackLevel := t.shard.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryTimer, clusterName).GetScheduledTime() if ackLevel.Before(minLevel) { minLevel = ackLevel standbyClusterName = clusterName } } if standbyClusterName != t.currentClusterName { t.logger.Debugf("Timer queue failover will use minLevel: %v from standbyClusterName: %s", minLevel, standbyClusterName) } else { t.logger.Debugf("Timer queue failover will use minLevel: %v from current cluster: %s", minLevel, t.currentClusterName) } maxReadLevel := time.Time{} actionResult, err := t.HandleAction(context.Background(), t.currentClusterName, NewGetStateAction()) if err != nil { t.logger.Error("Timer failover failed while getting queue states", tag.WorkflowDomainIDs(domainIDs), tag.Error(err)) if err == errProcessorShutdown { // processor/shard already shutdown, we don't need to create failover queue processor return } // other errors should never be returned for GetStateAction panic(fmt.Sprintf("unknown error for GetStateAction: %v", err)) } var maxReadLevelQueueLevel int for _, queueState := range actionResult.GetStateActionResult.States { queueReadLevel := queueState.ReadLevel().(timerTaskKey).visibilityTimestamp if maxReadLevel.Before(queueReadLevel) { maxReadLevel = queueReadLevel maxReadLevelQueueLevel = queueState.Level() } } if !maxReadLevel.IsZero() { t.logger.Debugf("Timer queue failover will use maxReadLevel: %v from queue at level: %v", maxReadLevel, maxReadLevelQueueLevel) } // TODO: Below Add call has no effect, understand the underlying intent and fix it. maxReadLevel.Add(1 * time.Millisecond) t.logger.Info("Timer Failover Triggered", tag.WorkflowDomainIDs(domainIDs), tag.MinLevel(minLevel.UnixNano()), tag.MaxLevel(maxReadLevel.UnixNano()), ) updateClusterAckLevelFn, failoverQueueProcessor := newTimerQueueFailoverProcessor( standbyClusterName, t.shard, t.taskProcessor, t.taskAllocator, t.activeTaskExecutor, t.logger, minLevel, maxReadLevel, domainIDs, ) // NOTE: READ REF BEFORE MODIFICATION // ref: historyEngine.go registerDomainFailoverCallback function err = updateClusterAckLevelFn(newTimerTaskKey(minLevel, 0)) if err != nil { t.logger.Error("Error update shard ack level", tag.Error(err)) } // Failover queue processors are started on the fly when domains are failed over. // Failover queue processors will be stopped when the timer queue instance is stopped (due to restart or shard movement). // This means the failover queue processor might not finish its job. // There is no mechanism to re-start ongoing failover queue processors in the new shard owner. t.failoverQueueProcessors = append(t.failoverQueueProcessors, failoverQueueProcessor) failoverQueueProcessor.Start() } func (t *timerQueueProcessor) HandleAction(ctx context.Context, clusterName string, action *Action) (*ActionResult, error) { var resultNotificationCh chan actionResultNotification var added bool if clusterName == t.currentClusterName { resultNotificationCh, added = t.activeQueueProcessor.addAction(ctx, action) } else { found := false for standbyClusterName, standbyProcessor := range t.standbyQueueProcessors { if clusterName == standbyClusterName { resultNotificationCh, added = standbyProcessor.addAction(ctx, action) found = true break } } if !found { return nil, fmt.Errorf("unknown cluster name: %v", clusterName) } } if !added { if ctxErr := ctx.Err(); ctxErr != nil { return nil, ctxErr } return nil, errProcessorShutdown } select { case resultNotification := <-resultNotificationCh: return resultNotification.result, resultNotification.err case <-t.shutdownChan: return nil, errProcessorShutdown case <-ctx.Done(): return nil, ctx.Err() } } func (t *timerQueueProcessor) LockTaskProcessing() { t.logger.Debug("Timer queue processor locking task processing") t.taskAllocator.Lock() } func (t *timerQueueProcessor) UnlockTaskProcessing() { t.logger.Debug("Timer queue processor unlocking task processing") t.taskAllocator.Unlock() } func (t *timerQueueProcessor) drain() { if !t.shard.GetConfig().QueueProcessorEnableGracefulSyncShutdown() { if err := t.completeTimer(context.Background()); err != nil { t.logger.Error("Failed to complete timer task during drain", tag.Error(err)) } return } // when graceful shutdown is enabled for queue processor, use a context with timeout ctx, cancel := context.WithTimeout(context.Background(), gracefulShutdownTimeout) defer cancel() if err := t.completeTimer(ctx); err != nil { t.logger.Error("Failed to complete timer task during drain", tag.Error(err)) } } func (t *timerQueueProcessor) completeTimerLoop() { defer t.shutdownWG.Done() t.logger.Info("Timer queue processor completeTimerLoop") defer t.logger.Info("Timer queue processor completeTimerLoop completed") completeTimer := time.NewTimer(t.config.TimerProcessorCompleteTimerInterval()) defer completeTimer.Stop() // Create a retryTimer once, and reset it as needed retryTimer := time.NewTimer(0) defer retryTimer.Stop() // Stop it immediately because we don't want it to fire initially if !retryTimer.Stop() { <-retryTimer.C } for { select { case <-t.shutdownChan: t.drain() return case <-completeTimer.C: for attempt := 0; attempt < t.config.TimerProcessorCompleteTimerFailureRetryCount(); attempt++ { err := t.completeTimer(context.Background()) if err == nil { break } t.logger.Error("Failed to complete timer task", tag.Error(err), tag.Attempt(int32(attempt))) var errShardClosed *shard.ErrShardClosed if errors.As(err, &errShardClosed) { if !t.shard.GetConfig().QueueProcessorEnableGracefulSyncShutdown() { go t.Stop() return } t.Stop() return } // Reset the retryTimer for the delay between attempts // TODO: the first retry has 0 backoff, revisit it to see if it's expected retryDuration := time.Duration(attempt*100) * time.Millisecond retryTimer.Reset(retryDuration) select { case <-t.shutdownChan: t.drain() return case <-retryTimer.C: // do nothing. retry loop will continue } } completeTimer.Reset(t.config.TimerProcessorCompleteTimerInterval()) } } } func (t *timerQueueProcessor) completeTimer(ctx context.Context) error { newAckLevel := maximumTimerTaskKey actionResult, err := t.HandleAction(ctx, t.currentClusterName, NewGetStateAction()) if err != nil { return err } for _, queueState := range actionResult.GetStateActionResult.States { newAckLevel = minTaskKey(newAckLevel, queueState.AckLevel()) } for standbyClusterName := range t.standbyQueueProcessors { actionResult, err := t.HandleAction(ctx, standbyClusterName, NewGetStateAction()) if err != nil { return err } for _, queueState := range actionResult.GetStateActionResult.States { newAckLevel = minTaskKey(newAckLevel, queueState.AckLevel()) } } for _, failoverInfo := range t.shard.GetAllFailoverLevels(persistence.HistoryTaskCategoryTimer) { failoverLevel := newTimerTaskKey(failoverInfo.MinLevel.GetScheduledTime(), 0) newAckLevel = minTaskKey(newAckLevel, failoverLevel) } if newAckLevel == maximumTimerTaskKey { panic("Unable to get timer queue processor ack level") } newAckLevelTimestamp := newAckLevel.(timerTaskKey).visibilityTimestamp if !t.ackLevel.Before(newAckLevelTimestamp) { t.logger.Debugf("Skipping timer task completion because new ack level %v is not before ack level %v", newAckLevelTimestamp, t.ackLevel) return nil } t.logger.Debugf("Start completing timer task from: %v, to %v", t.ackLevel, newAckLevelTimestamp) t.metricsClient.Scope(metrics.TimerQueueProcessorScope). Tagged(metrics.ShardIDTag(t.shard.GetShardID())). IncCounter(metrics.TaskBatchCompleteCounter) totalDeleted := 0 for { pageSize := t.config.TimerTaskDeleteBatchSize() resp, err := t.shard.GetExecutionManager().RangeCompleteHistoryTask(ctx, &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(t.ackLevel, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(newAckLevelTimestamp, 0), PageSize: pageSize, }) if err != nil { return err } totalDeleted += resp.TasksCompleted t.logger.Debug("Timer task batch deletion", tag.Dynamic("page-size", pageSize), tag.Dynamic("total-deleted", totalDeleted)) if !persistence.HasMoreRowsToDelete(resp.TasksCompleted, pageSize) { break } } t.ackLevel = newAckLevelTimestamp return t.shard.UpdateQueueAckLevel(persistence.HistoryTaskCategoryTimer, persistence.NewHistoryTaskKey(t.ackLevel, 0)) } func loadTimerProcessingQueueStates( clusterName string, shard shard.Context, options *queueProcessorOptions, logger log.Logger, ) []ProcessingQueueState { ackLevel := shard.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryTimer, clusterName).GetScheduledTime() if options.EnableLoadQueueStates() { pStates := shard.GetTimerProcessingQueueStates(clusterName) if validateProcessingQueueStates(pStates, ackLevel) { return convertFromPersistenceTimerProcessingQueueStates(pStates) } logger.Error("Incompatible processing queue states and ackLevel", tag.Value(pStates), tag.ShardTimerAcks(ackLevel), ) } return []ProcessingQueueState{ NewProcessingQueueState( defaultProcessingQueueLevel, newTimerTaskKey(ackLevel, 0), maximumTimerTaskKey, NewDomainFilter(nil, true), ), } } ================================================ FILE: service/history/queue/timer_queue_processor_base.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "context" "errors" "fmt" "math" "math/rand" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) var ( maximumTimerTaskKey = newTimerTaskKey( time.Unix(0, math.MaxInt64), 0, ) timerTaskOperationRetryPolicy = common.CreatePersistenceRetryPolicy() ) type ( timerTaskKey struct { visibilityTimestamp time.Time taskID int64 } timeTaskReadProgress struct { currentQueue ProcessingQueue readLevel task.Key maxReadLevel task.Key nextPageToken []byte } timerQueueProcessorBase struct { *processorBase taskInitializer task.Initializer clusterName string pollTimeLock sync.Mutex backoffTimer map[int]*time.Timer nextPollTime map[int]time.Time timerGate clock.TimerGate // timer notification newTimerCh chan struct{} newTimeLock sync.Mutex newTime time.Time processingQueueReadProgress map[int]timeTaskReadProgress updateAckLevelFn func() (bool, task.Key, error) splitProcessingQueueCollectionFn func(splitPolicy ProcessingQueueSplitPolicy, upsertPollTimeFn func(int, time.Time)) } filteredTimerTasksResponse struct { timerTasks []persistence.Task lookAheadTimestamp time.Time nextPageToken []byte } ) func newTimerQueueProcessorBase( clusterName string, shard shard.Context, processingQueueStates []ProcessingQueueState, taskProcessor task.Processor, timerGate clock.TimerGate, options *queueProcessorOptions, updateMaxReadLevel updateMaxReadLevelFn, updateClusterAckLevel updateClusterAckLevelFn, updateProcessingQueueStates updateProcessingQueueStatesFn, queueShutdown queueShutdownFn, taskFilter task.Filter, taskExecutor task.Executor, logger log.Logger, metricsClient metrics.Client, ) *timerQueueProcessorBase { processorBase := newProcessorBase( shard, processingQueueStates, taskProcessor, options, updateMaxReadLevel, updateClusterAckLevel, updateProcessingQueueStates, queueShutdown, logger.WithTags(tag.ComponentTimerQueue), metricsClient, ) queueType := task.QueueTypeActiveTimer if options.MetricScope == metrics.TimerStandbyQueueProcessorScope { queueType = task.QueueTypeStandbyTimer } t := &timerQueueProcessorBase{ processorBase: processorBase, taskInitializer: func(taskInfo persistence.Task) task.Task { return task.NewHistoryTask( shard, taskInfo, queueType, task.InitializeLoggerForTask(shard.GetShardID(), taskInfo, logger), taskFilter, taskExecutor, taskProcessor, processorBase.redispatcher, shard.GetConfig().TaskCriticalRetryCount, ) }, clusterName: clusterName, backoffTimer: make(map[int]*time.Timer), nextPollTime: make(map[int]time.Time), timerGate: timerGate, newTimerCh: make(chan struct{}, 1), processingQueueReadProgress: make(map[int]timeTaskReadProgress), } t.updateAckLevelFn = t.updateAckLevel t.splitProcessingQueueCollectionFn = t.splitProcessingQueueCollection return t } func (t *timerQueueProcessorBase) Start() { if !atomic.CompareAndSwapInt32(&t.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } t.logger.Info("Timer queue processor state changed", tag.LifeCycleStarting) defer t.logger.Info("Timer queue processor state changed", tag.LifeCycleStarted) t.redispatcher.Start() newPollTime := time.Time{} if startJitter := t.options.MaxStartJitterInterval(); startJitter > 0 { now := t.shard.GetTimeSource().Now() newPollTime = now.Add(time.Duration(rand.Int63n(int64(startJitter)))) } for _, queueCollections := range t.processingQueueCollections { t.upsertPollTime(queueCollections.Level(), newPollTime) } t.shutdownWG.Add(1) go t.processorPump() } // Edge Case: Stop doesn't stop TimerGate if timerQueueProcessorBase is only initiliazed without starting // As a result, TimerGate needs to be stopped separately // One way to fix this is to make sure TimerGate doesn't start daemon loop on initilization and requires explicit Start func (t *timerQueueProcessorBase) Stop() { if !atomic.CompareAndSwapInt32(&t.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } t.logger.Info("Timer queue processor state changed", tag.LifeCycleStopping) t.timerGate.Stop() close(t.shutdownCh) t.pollTimeLock.Lock() for _, timer := range t.backoffTimer { timer.Stop() } t.pollTimeLock.Unlock() if success := common.AwaitWaitGroup(&t.shutdownWG, gracefulShutdownTimeout); !success { t.logger.Warn("timerQueueProcessorBase timed out on shut down", tag.LifeCycleStopTimedout) } t.redispatcher.Stop() t.logger.Info("Timer queue processor state changed", tag.LifeCycleStopped) } func (t *timerQueueProcessorBase) processorPump() { defer t.shutdownWG.Done() updateAckTimer := time.NewTimer(backoff.JitDuration(t.options.UpdateAckInterval(), t.options.UpdateAckIntervalJitterCoefficient())) defer updateAckTimer.Stop() splitQueueTimer := time.NewTimer(backoff.JitDuration(t.options.SplitQueueInterval(), t.options.SplitQueueIntervalJitterCoefficient())) defer splitQueueTimer.Stop() for { select { case <-t.shutdownCh: return case <-t.timerGate.Chan(): t.updateTimerGates() case <-updateAckTimer.C: if stopPump := t.handleAckLevelUpdate(updateAckTimer); stopPump { if !t.options.EnableGracefulSyncShutdown() { go t.Stop() return } t.Stop() return } case <-t.newTimerCh: t.handleNewTimer() case <-splitQueueTimer.C: t.splitQueue(splitQueueTimer) case notification := <-t.actionNotifyCh: t.handleActionNotification(notification) } } } func (t *timerQueueProcessorBase) processQueueCollections(levels map[int]struct{}) { for _, queueCollection := range t.processingQueueCollections { level := queueCollection.Level() if _, ok := levels[level]; !ok { continue } activeQueue := queueCollection.ActiveQueue() if activeQueue == nil { // process for this queue collection has finished // it's possible that new queue will be added to this collection later though, // pollTime will be updated after split/merge t.logger.Debug("Active queue is nil for timer queue at this level", tag.QueueLevel(level)) continue } t.upsertPollTime(level, t.shard.GetCurrentTime(t.clusterName).Add(backoff.JitDuration( t.options.MaxPollInterval(), t.options.MaxPollIntervalJitterCoefficient(), ))) var nextPageToken []byte readLevel := activeQueue.State().ReadLevel() maxReadLevel := minTaskKey(activeQueue.State().MaxLevel(), t.updateMaxReadLevel()) domainFilter := activeQueue.State().DomainFilter() if progress, ok := t.processingQueueReadProgress[level]; ok { if progress.currentQueue == activeQueue { readLevel = progress.readLevel maxReadLevel = progress.maxReadLevel nextPageToken = progress.nextPageToken } delete(t.processingQueueReadProgress, level) } if !readLevel.Less(maxReadLevel) { // notify timer gate about the min time t.upsertPollTime(level, readLevel.(timerTaskKey).visibilityTimestamp) t.logger.Debug("Skipping processing timer queue at this level because readLevel >= maxReadLevel", tag.QueueLevel(level)) continue } ctx, cancel := context.WithTimeout(context.Background(), loadQueueTaskThrottleRetryDelay) if err := t.rateLimiter.Wait(ctx); err != nil { cancel() if level == defaultProcessingQueueLevel { t.upsertPollTime(level, time.Time{}) } else { t.setupBackoffTimer(level) } continue } cancel() resp, err := t.readAndFilterTasks(readLevel, maxReadLevel, nextPageToken) if err != nil { t.logger.Error("Processor unable to retrieve tasks", tag.Error(err)) t.upsertPollTime(level, time.Time{}) // re-enqueue the event continue } tasks := make(map[task.Key]task.Task) taskChFull := false submittedCount := 0 now := t.shard.GetTimeSource().Now() for _, taskInfo := range resp.timerTasks { if !domainFilter.Filter(taskInfo.GetDomainID()) { continue } if persistence.IsTaskCorrupted(taskInfo) { t.logger.Error("Processing queue encountered a corrupted task", tag.Dynamic("task", taskInfo)) t.metricsScope.IncCounter(metrics.CorruptedHistoryTaskCounter) continue } task := t.taskInitializer(taskInfo) tasks[newTimerTaskKey(taskInfo.GetVisibilityTimestamp(), taskInfo.GetTaskID())] = task // shard level metrics for the duration between a task being written to a queue and being fetched from it t.metricsScope.RecordHistogramDuration(metrics.TaskEnqueueToFetchLatency, now.Sub(taskInfo.GetVisibilityTimestamp())) submitted, err := t.submitTask(task) if err != nil { // only err here is due to the fact that processor has been shutdown // return instead of continue return } taskChFull = taskChFull || !submitted if submitted { submittedCount++ } } t.logger.Debugf("Submitted %d timer tasks successfully out of %d tasks", submittedCount, len(resp.timerTasks)) var newReadLevel task.Key if len(resp.nextPageToken) == 0 { newReadLevel = maxReadLevel if !resp.lookAheadTimestamp.IsZero() { // lookAheadTask may exist only when nextPageToken is empty // notice that lookAheadTask.VisibilityTimestamp may be larger than shard max read level, // which means new tasks can be generated before that timestamp. This issue is solved by // upsertPollTime whenever there are new tasks lookAheadTimestamp := resp.lookAheadTimestamp t.upsertPollTime(level, lookAheadTimestamp) newReadLevel = minTaskKey(newReadLevel, newTimerTaskKey(lookAheadTimestamp, 0)) t.logger.Debugf("nextPageToken is empty for timer queue at level %d so setting newReadLevel to max(lookAheadTask.timestamp: %v, maxReadLevel: %v)", level, lookAheadTimestamp, maxReadLevel) } else { // else we have no idea when the next poll should happen // rely on notifyNewTask to trigger the next poll even for non-default queue. // another option for non-default queue is that we can setup a backoff timer to check back later t.logger.Debugf("nextPageToken is empty for timer queue at level %d and there' no lookAheadTask. setting readLevel to maxReadLevel: %v", level, maxReadLevel) } } else { // more tasks should be loaded for this processing queue // record the current progress and update the poll time if level == defaultProcessingQueueLevel || !taskChFull { t.logger.Debugf("upserting poll time for timer queue at level %d because nextPageToken is not empty and !taskChFull", level) t.upsertPollTime(level, time.Time{}) } else { t.logger.Debugf("setting up backoff timer for timer queue at level %d because nextPageToken is not empty and taskChFull", level) t.setupBackoffTimer(level) } t.processingQueueReadProgress[level] = timeTaskReadProgress{ currentQueue: activeQueue, readLevel: readLevel, maxReadLevel: maxReadLevel, nextPageToken: resp.nextPageToken, } if len(resp.timerTasks) > 0 { newReadLevel = newTimerTaskKey(resp.timerTasks[len(resp.timerTasks)-1].GetVisibilityTimestamp(), 0) } } queueCollection.AddTasks(tasks, newReadLevel) } } // splitQueue splits the processing queue collection based on some policy // and resets the timer with jitter for next run func (t *timerQueueProcessorBase) splitQueue(splitQueueTimer *time.Timer) { splitPolicy := t.initializeSplitPolicy( func(key task.Key, domainID string) task.Key { return newTimerTaskKey( key.(timerTaskKey).visibilityTimestamp.Add( t.options.SplitLookAheadDurationByDomainID(domainID), ), 0, ) }, ) t.splitProcessingQueueCollectionFn(splitPolicy, t.upsertPollTime) splitQueueTimer.Reset(backoff.JitDuration( t.options.SplitQueueInterval(), t.options.SplitQueueIntervalJitterCoefficient(), )) } // handleAckLevelUpdate updates ack level and resets timer with jitter. // returns true if processing should be terminated func (t *timerQueueProcessorBase) handleAckLevelUpdate(updateAckTimer *time.Timer) bool { processFinished, _, err := t.updateAckLevelFn() var errShardClosed *shard.ErrShardClosed if errors.As(err, &errShardClosed) || (err == nil && processFinished) { return true } updateAckTimer.Reset(backoff.JitDuration( t.options.UpdateAckInterval(), t.options.UpdateAckIntervalJitterCoefficient(), )) return false } func (t *timerQueueProcessorBase) updateTimerGates() { maxRedispatchQueueSize := t.options.MaxRedispatchQueueSize() if t.redispatcher.Size() > maxRedispatchQueueSize { t.redispatcher.Redispatch(maxRedispatchQueueSize) if t.redispatcher.Size() > maxRedispatchQueueSize { // if redispatcher still has a large number of tasks // this only happens when system is under very high load // we should backoff here instead of keeping submitting tasks to task processor // don't call t.timerGate.Update(time.Now() + loadQueueTaskThrottleRetryDelay) as the time in // standby timer processor is not real time and is managed separately time.Sleep(backoff.JitDuration( t.options.PollBackoffInterval(), t.options.PollBackoffIntervalJitterCoefficient(), )) } t.timerGate.Update(time.Time{}) return } t.pollTimeLock.Lock() levels := make(map[int]struct{}) now := t.shard.GetCurrentTime(t.clusterName) for level, pollTime := range t.nextPollTime { if !now.Before(pollTime) { levels[level] = struct{}{} delete(t.nextPollTime, level) } else { t.timerGate.Update(pollTime) } } t.pollTimeLock.Unlock() t.processQueueCollections(levels) } func (t *timerQueueProcessorBase) handleNewTimer() { t.newTimeLock.Lock() newTime := t.newTime t.newTime = time.Time{} t.newTimeLock.Unlock() // New Timer has arrived. t.metricsScope.IncCounter(metrics.NewTimerNotifyCounter) // notify all queue collections as they are waiting for the notification when there's // no more task to process. For non-default queue, we choose to do periodic polling // in the future, then we don't need to notify them. for _, queueCollection := range t.processingQueueCollections { t.upsertPollTime(queueCollection.Level(), newTime) } } func (t *timerQueueProcessorBase) handleActionNotification(notification actionNotification) { t.processorBase.handleActionNotification(notification, func() { switch notification.action.ActionType { case ActionTypeReset: t.upsertPollTime(defaultProcessingQueueLevel, time.Time{}) } }) } func (t *timerQueueProcessorBase) readAndFilterTasks(readLevel, maxReadLevel task.Key, nextPageToken []byte) (*filteredTimerTasksResponse, error) { resp, err := t.getTimerTasks(readLevel, maxReadLevel, nextPageToken, t.options.BatchSize()) if err != nil { return nil, err } var lookAheadTimestamp time.Time filteredTasks := []persistence.Task{} for _, timerTask := range resp.Tasks { if !t.isProcessNow(timerTask.GetVisibilityTimestamp()) { // found the first task that is not ready to be processed yet. // reset NextPageToken so we can load more tasks starting from this lookAheadTask next time. lookAheadTimestamp = timerTask.GetVisibilityTimestamp() resp.NextPageToken = nil break } filteredTasks = append(filteredTasks, timerTask) } if len(resp.NextPageToken) == 0 && lookAheadTimestamp.IsZero() { // only look ahead within the processing queue boundary lookAheadTask, err := t.readLookAheadTask(maxReadLevel, maximumTimerTaskKey) if err != nil { // we don't know if look ahead task exists or not, but we know if it exists, // it's visibility timestamp is larger than or equal to maxReadLevel. // so, create a fake look ahead task so another load can be triggered at that time. lookAheadTimestamp = maxReadLevel.(timerTaskKey).visibilityTimestamp } else if lookAheadTask != nil { lookAheadTimestamp = lookAheadTask.GetVisibilityTimestamp() } } t.logger.Debugf("readAndFilterTasks returning %d tasks and lookAheadTimestamp: %#v for readLevel: %#v, maxReadLevel: %#v", len(filteredTasks), lookAheadTimestamp, readLevel, maxReadLevel) return &filteredTimerTasksResponse{ timerTasks: filteredTasks, lookAheadTimestamp: lookAheadTimestamp, nextPageToken: resp.NextPageToken, }, nil } func (t *timerQueueProcessorBase) readLookAheadTask(lookAheadStartLevel task.Key, lookAheadMaxLevel task.Key) (persistence.Task, error) { resp, err := t.getTimerTasks(lookAheadStartLevel, lookAheadMaxLevel, nil, 1) if err != nil { return nil, err } if len(resp.Tasks) == 1 { return resp.Tasks[0], nil } return nil, nil } func (t *timerQueueProcessorBase) getTimerTasks(readLevel, maxReadLevel task.Key, nextPageToken []byte, batchSize int) (*persistence.GetHistoryTasksResponse, error) { request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(readLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: batchSize, NextPageToken: nextPageToken, } var response *persistence.GetHistoryTasksResponse op := func(ctx context.Context) error { var err error response, err = t.shard.GetExecutionManager().GetHistoryTasks(ctx, request) return err } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(timerTaskOperationRetryPolicy), backoff.WithRetryableError(func(err error) bool { return true }), ) err := throttleRetry.Do(context.Background(), op) if err != nil { return nil, err } return response, nil } func (t *timerQueueProcessorBase) isProcessNow(expiryTime time.Time) bool { if expiryTime.IsZero() { // return true, but somewhere probably have bug creating empty timerTask. t.logger.Warn("Timer task has timestamp zero") } return !t.shard.GetCurrentTime(t.clusterName).Before(expiryTime) } func (t *timerQueueProcessorBase) notifyNewTimers(timerTasks []persistence.Task) { if len(timerTasks) == 0 { return } isActive := t.options.MetricScope == metrics.TimerActiveQueueProcessorScope minNewTime := timerTasks[0].GetVisibilityTimestamp() shardIDTag := metrics.ShardIDTag(t.shard.GetShardID()) for _, timerTask := range timerTasks { ts := timerTask.GetVisibilityTimestamp() if ts.Before(minNewTime) { minNewTime = ts } taskScopeIdx := task.GetTimerTaskMetricScope( timerTask.GetTaskType(), isActive, ) t.metricsClient.Scope(taskScopeIdx).Tagged(shardIDTag).IncCounter(metrics.NewTimerNotifyCounter) } t.notifyNewTimer(minNewTime) } func (t *timerQueueProcessorBase) notifyNewTimer(newTime time.Time) { t.newTimeLock.Lock() defer t.newTimeLock.Unlock() if t.newTime.IsZero() || newTime.Before(t.newTime) { t.logger.Debugf("Updating newTime from %v to %v", t.newTime, newTime) t.newTime = newTime select { case t.newTimerCh <- struct{}{}: // Notified about new time. default: // Channel "full" -> drop and move on, this will happen only if service is in high load. } } } func (t *timerQueueProcessorBase) upsertPollTime(level int, newPollTime time.Time) { t.pollTimeLock.Lock() defer t.pollTimeLock.Unlock() if _, ok := t.backoffTimer[level]; ok { // honor existing backoff timer t.logger.Debugf("Skipping upsertPollTime for timer queue at level %d because there's a backoff timer", level) return } currentPollTime, ok := t.nextPollTime[level] if !ok || newPollTime.Before(currentPollTime) { t.logger.Debugf("Updating poll timer for timer queue at level %d. CurrentPollTime: %v, newPollTime: %v", level, currentPollTime, newPollTime) t.nextPollTime[level] = newPollTime t.timerGate.Update(newPollTime) return } t.logger.Debugf("Skipping upsertPollTime for level %d because currentPollTime %v is before newPollTime %v", level, currentPollTime, newPollTime) } // setupBackoffTimer will trigger a poll for the specified processing queue collection // after a certain period of (real) time. This means for standby timer, even if the cluster time // has not been updated, the poll will still be triggered when the timer fired. Use this function // for delaying the load for processing queue. If a poll should be triggered immediately // use upsertPollTime. func (t *timerQueueProcessorBase) setupBackoffTimer(level int) { t.pollTimeLock.Lock() defer t.pollTimeLock.Unlock() if _, ok := t.backoffTimer[level]; ok { // honor existing backoff timer return } t.metricsScope.IncCounter(metrics.ProcessingQueueThrottledCounter) t.logger.Info("Throttled processing queue", tag.QueueLevel(level)) backoffDuration := backoff.JitDuration( t.options.PollBackoffInterval(), t.options.PollBackoffIntervalJitterCoefficient(), ) t.backoffTimer[level] = time.AfterFunc(backoffDuration, func() { select { case <-t.shutdownCh: return default: } t.pollTimeLock.Lock() defer t.pollTimeLock.Unlock() t.nextPollTime[level] = time.Time{} t.timerGate.Update(time.Time{}) delete(t.backoffTimer, level) }) } func newTimerTaskKey(visibilityTimestamp time.Time, taskID int64) task.Key { return timerTaskKey{ visibilityTimestamp: visibilityTimestamp, taskID: taskID, } } func (k timerTaskKey) Less( key task.Key, ) bool { timerKey := key.(timerTaskKey) if k.visibilityTimestamp.Equal(timerKey.visibilityTimestamp) { return k.taskID < timerKey.taskID } return k.visibilityTimestamp.Before(timerKey.visibilityTimestamp) } func (k timerTaskKey) String() string { return fmt.Sprintf("{visibilityTimestamp: %v, taskID: %v}", k.visibilityTimestamp, k.taskID) } func newTimerQueueProcessorOptions( config *config.Config, isActive bool, isFailover bool, ) *queueProcessorOptions { options := &queueProcessorOptions{ BatchSize: config.TimerTaskBatchSize, DeleteBatchSize: config.TimerTaskDeleteBatchSize, MaxPollRPS: config.TimerProcessorMaxPollRPS, MaxPollInterval: config.TimerProcessorMaxPollInterval, MaxPollIntervalJitterCoefficient: config.TimerProcessorMaxPollIntervalJitterCoefficient, UpdateAckInterval: config.TimerProcessorUpdateAckInterval, UpdateAckIntervalJitterCoefficient: config.TimerProcessorUpdateAckIntervalJitterCoefficient, MaxRedispatchQueueSize: config.TimerProcessorMaxRedispatchQueueSize, SplitQueueInterval: config.TimerProcessorSplitQueueInterval, SplitQueueIntervalJitterCoefficient: config.TimerProcessorSplitQueueIntervalJitterCoefficient, PollBackoffInterval: config.QueueProcessorPollBackoffInterval, PollBackoffIntervalJitterCoefficient: config.QueueProcessorPollBackoffIntervalJitterCoefficient, EnableGracefulSyncShutdown: config.QueueProcessorEnableGracefulSyncShutdown, } if isFailover { // disable queue split for failover processor options.EnableSplit = dynamicproperties.GetBoolPropertyFn(false) // disable persist and load processing queue states for failover processor as it will never be split options.EnablePersistQueueStates = dynamicproperties.GetBoolPropertyFn(false) options.EnableLoadQueueStates = dynamicproperties.GetBoolPropertyFn(false) options.MaxStartJitterInterval = config.TimerProcessorFailoverMaxStartJitterInterval } else { options.EnableSplit = config.QueueProcessorEnableSplit options.SplitMaxLevel = config.QueueProcessorSplitMaxLevel options.EnableRandomSplitByDomainID = config.QueueProcessorEnableRandomSplitByDomainID options.RandomSplitProbability = config.QueueProcessorRandomSplitProbability options.EnablePendingTaskSplitByDomainID = config.QueueProcessorEnablePendingTaskSplitByDomainID options.PendingTaskSplitThreshold = config.QueueProcessorPendingTaskSplitThreshold options.EnableStuckTaskSplitByDomainID = config.QueueProcessorEnableStuckTaskSplitByDomainID options.StuckTaskSplitThreshold = config.QueueProcessorStuckTaskSplitThreshold options.SplitLookAheadDurationByDomainID = config.QueueProcessorSplitLookAheadDurationByDomainID options.EnablePersistQueueStates = config.QueueProcessorEnablePersistQueueStates options.EnableLoadQueueStates = config.QueueProcessorEnableLoadQueueStates options.MaxStartJitterInterval = dynamicproperties.GetDurationPropertyFn(0) } if isActive { options.MetricScope = metrics.TimerActiveQueueProcessorScope options.RedispatchInterval = config.ActiveTaskRedispatchInterval } else { options.MetricScope = metrics.TimerStandbyQueueProcessorScope options.RedispatchInterval = config.StandbyTaskRedispatchInterval } return options } ================================================ FILE: service/history/queue/timer_queue_processor_base_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "errors" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) type ( timerQueueProcessorBaseSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockTaskProcessor *task.MockProcessor mockQueueSplitPolicy *MockProcessingQueueSplitPolicy clusterName string logger log.Logger metricsClient metrics.Client metricsScope metrics.Scope } ) func TestTimerQueueProcessorBaseSuite(t *testing.T) { s := new(timerQueueProcessorBaseSuite) suite.Run(t, s) } func (s *timerQueueProcessorBaseSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return(constants.TestDomainName, nil).AnyTimes() s.mockQueueSplitPolicy = NewMockProcessingQueueSplitPolicy(s.controller) s.mockTaskProcessor = task.NewMockProcessor(s.controller) s.clusterName = cluster.TestCurrentClusterName s.logger = testlogger.New(s.Suite.T()) s.metricsClient = metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) s.metricsScope = s.metricsClient.Scope(metrics.TimerQueueProcessorScope) } func (s *timerQueueProcessorBaseSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) defer goleak.VerifyNone(s.T()) } func (s *timerQueueProcessorBaseSuite) TestIsProcessNow() { timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(nil, nil, nil, nil, nil) defer done() s.True(timerQueueProcessBase.isProcessNow(time.Time{})) now := s.mockShard.GetCurrentTime(s.clusterName) s.True(timerQueueProcessBase.isProcessNow(now)) timeBefore := now.Add(-10 * time.Second) s.True(timerQueueProcessBase.isProcessNow(timeBefore)) timeAfter := now.Add(10 * time.Second) s.False(timerQueueProcessBase.isProcessNow(timeAfter)) } func (s *timerQueueProcessorBaseSuite) TestGetTimerTasks_More() { readLevel := newTimerTaskKey(time.Now().Add(-10*time.Second), 0) maxReadLevel := newTimerTaskKey(time.Now().Add(10*time.Second), 0) batchSize := 10 request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(readLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: batchSize, NextPageToken: []byte("some random input next page token"), } response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now().Add(-5 * time.Second), TaskID: int64(59), Version: int64(1), }, EventID: int64(28), }, }, NextPageToken: []byte("some random output next page token"), } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(nil, nil, nil, nil, nil) defer done() got, err := timerQueueProcessBase.getTimerTasks(readLevel, maxReadLevel, request.NextPageToken, batchSize) s.Nil(err) s.Equal(response.Tasks, got.Tasks) s.Equal(response.NextPageToken, got.NextPageToken) } func (s *timerQueueProcessorBaseSuite) TestGetTimerTasks_NoMore() { readLevel := newTimerTaskKey(time.Now().Add(-10*time.Second), 0) maxReadLevel := newTimerTaskKey(time.Now().Add(10*time.Second), 0) batchSize := 10 request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(readLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: batchSize, NextPageToken: nil, } response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now().Add(-5 * time.Second), TaskID: int64(59), Version: int64(1), }, EventID: int64(28), }, }, NextPageToken: nil, } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(nil, nil, nil, nil, nil) defer done() got, err := timerQueueProcessBase.getTimerTasks(readLevel, maxReadLevel, request.NextPageToken, batchSize) s.Nil(err) s.Equal(response.Tasks, got.Tasks) s.Empty(got.NextPageToken) } func (s *timerQueueProcessorBaseSuite) TestReadLookAheadTask() { shardMaxReadLevel := s.mockShard.UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryTimer, s.clusterName).GetScheduledTime() readLevel := newTimerTaskKey(shardMaxReadLevel, 0) maxReadLevel := newTimerTaskKey(shardMaxReadLevel.Add(10*time.Second), 0) request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(readLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: 1, NextPageToken: nil, } response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: shardMaxReadLevel, TaskID: int64(59), Version: int64(79), }, EventID: int64(28), }, }, NextPageToken: []byte("some random next page token"), } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(nil, nil, nil, nil, nil) defer done() lookAheadTask, err := timerQueueProcessBase.readLookAheadTask(readLevel, maxReadLevel) s.Nil(err) s.Equal(response.Tasks[0], lookAheadTask) } func (s *timerQueueProcessorBaseSuite) TestReadAndFilterTasks_NoLookAhead_NoNextPage() { readLevel := newTimerTaskKey(time.Now().Add(-10*time.Second), 0) maxReadLevel := newTimerTaskKey(time.Now().Add(1*time.Second), 0) request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(readLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: s.mockShard.GetConfig().TimerTaskBatchSize(), NextPageToken: []byte("some random input next page token"), } lookAheadRequest := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maximumTimerTaskKey.(timerTaskKey).visibilityTimestamp, 0), PageSize: 1, NextPageToken: nil, } response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now().Add(-5 * time.Second), TaskID: int64(59), Version: int64(1), }, EventID: int64(28), }, }, NextPageToken: nil, } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() mockExecutionMgr.On("GetHistoryTasks", mock.Anything, lookAheadRequest).Return(&persistence.GetHistoryTasksResponse{}, nil).Once() timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(nil, nil, nil, nil, nil) defer done() got, err := timerQueueProcessBase.readAndFilterTasks(readLevel, maxReadLevel, request.NextPageToken) s.Nil(err) s.Equal(response.Tasks, got.timerTasks) s.Zero(got.lookAheadTimestamp) s.Nil(got.nextPageToken) } func (s *timerQueueProcessorBaseSuite) TestReadAndFilterTasks_NoLookAhead_HasNextPage() { readLevel := newTimerTaskKey(time.Now().Add(-10*time.Second), 0) maxReadLevel := newTimerTaskKey(time.Now().Add(1*time.Second), 0) request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(readLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: s.mockShard.GetConfig().TimerTaskBatchSize(), NextPageToken: []byte("some random input next page token"), } response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now().Add(-5 * time.Second), TaskID: int64(59), Version: int64(1), }, EventID: int64(28), }, }, NextPageToken: []byte("some random next page token"), } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(nil, nil, nil, nil, nil) defer done() got, err := timerQueueProcessBase.readAndFilterTasks(readLevel, maxReadLevel, request.NextPageToken) s.Nil(err) s.Equal(response.Tasks, got.timerTasks) s.Zero(got.lookAheadTimestamp) s.Equal(response.NextPageToken, got.nextPageToken) } func (s *timerQueueProcessorBaseSuite) TestReadAndFilterTasks_HasLookAhead_NoNextPage() { readLevel := newTimerTaskKey(time.Now().Add(-10*time.Second), 0) maxReadLevel := newTimerTaskKey(time.Now().Add(1*time.Second), 0) request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(readLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: s.mockShard.GetConfig().TimerTaskBatchSize(), NextPageToken: []byte("some random input next page token"), } response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now().Add(-5 * time.Second), }, EventID: int64(28), }, &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now().Add(500 * time.Millisecond), }, EventID: int64(28), }, }, NextPageToken: nil, } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(nil, nil, nil, nil, nil) defer done() got, err := timerQueueProcessBase.readAndFilterTasks(readLevel, maxReadLevel, request.NextPageToken) s.Nil(err) s.Equal([]persistence.Task{response.Tasks[0]}, got.timerTasks) s.Equal(response.Tasks[1].GetVisibilityTimestamp(), got.lookAheadTimestamp) s.Nil(got.nextPageToken) } func (s *timerQueueProcessorBaseSuite) TestReadAndFilterTasks_HasLookAhead_HasNextPage() { readLevel := newTimerTaskKey(time.Now().Add(-10*time.Second), 0) maxReadLevel := newTimerTaskKey(time.Now().Add(1*time.Second), 0) request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(readLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: s.mockShard.GetConfig().TimerTaskBatchSize(), NextPageToken: []byte("some random input next page token"), } response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now().Add(-5 * time.Second), }, EventID: int64(28), }, &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now().Add(500 * time.Millisecond), }, EventID: int64(28), }, }, NextPageToken: []byte("some random next page token"), } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(nil, nil, nil, nil, nil) defer done() got, err := timerQueueProcessBase.readAndFilterTasks(readLevel, maxReadLevel, request.NextPageToken) s.Nil(err) s.Equal([]persistence.Task{response.Tasks[0]}, got.timerTasks) s.Equal(response.Tasks[1].GetVisibilityTimestamp(), got.lookAheadTimestamp) s.Equal(response.NextPageToken, got.nextPageToken) } func (s *timerQueueProcessorBaseSuite) TestReadAndFilterTasks_LookAheadFailed_NoNextPage() { readLevel := newTimerTaskKey(time.Now().Add(-10*time.Second), 0) maxReadLevel := newTimerTaskKey(time.Now().Add(1*time.Second), 0) request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(readLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: s.mockShard.GetConfig().TimerTaskBatchSize(), NextPageToken: []byte("some random input next page token"), } lookAheadRequest := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(maxReadLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maximumTimerTaskKey.(timerTaskKey).visibilityTimestamp, 0), PageSize: 1, NextPageToken: nil, } response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now().Add(-5 * time.Second), TaskID: int64(59), Version: int64(1), }, EventID: int64(28), }, &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: time.Now().Add(-500 * time.Second), TaskID: int64(59), Version: int64(1), }, EventID: int64(28), }, }, NextPageToken: nil, } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() mockExecutionMgr.On("GetHistoryTasks", mock.Anything, lookAheadRequest).Return(nil, errors.New("some random error")).Times(0) timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(nil, nil, nil, nil, nil) defer done() got, err := timerQueueProcessBase.readAndFilterTasks(readLevel, maxReadLevel, request.NextPageToken) s.Nil(err) s.Equal(response.Tasks, got.timerTasks) s.Equal(maxReadLevel.(timerTaskKey).visibilityTimestamp, got.lookAheadTimestamp) s.Nil(got.nextPageToken) } func (s *timerQueueProcessorBaseSuite) TestNotifyNewTimes() { timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(nil, nil, nil, nil, nil) defer done() // assert the initial state s.True(timerQueueProcessBase.newTime.IsZero()) select { case <-timerQueueProcessBase.newTimerCh: default: } now := time.Now() timerQueueProcessBase.notifyNewTimers([]persistence.Task{ &persistence.UserTimerTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: now.Add(5 * time.Second), TaskID: int64(59), }, EventID: int64(28), }, &persistence.UserTimerTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: now.Add(1 * time.Second), TaskID: int64(59), }, EventID: int64(28), }, }) select { case <-timerQueueProcessBase.newTimerCh: s.Equal(now.Add(1*time.Second), timerQueueProcessBase.newTime) default: s.Fail("should notify new timer") } timerQueueProcessBase.notifyNewTimers([]persistence.Task{ &persistence.UserTimerTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: now.Add(10 * time.Second), TaskID: int64(59), }, EventID: int64(28), }, }) select { case <-timerQueueProcessBase.newTimerCh: s.Fail("should not notify new timer") default: s.Equal(now.Add(1*time.Second), timerQueueProcessBase.newTime) } } func (s *timerQueueProcessorBaseSuite) TestProcessQueueCollections_SkipRead() { now := time.Now() queueLevel := 0 shardMaxReadLevel := newTimerTaskKey(now, 0) ackLevel := newTimerTaskKey(now.Add(50*time.Millisecond), 0) maxLevel := newTimerTaskKey(now.Add(10*time.Second), 0) processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( queueLevel, ackLevel, maxLevel, NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), } updateMaxReadLevel := func() task.Key { return shardMaxReadLevel } timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(processingQueueStates, updateMaxReadLevel, nil, nil, nil) defer done() timerQueueProcessBase.processQueueCollections(map[int]struct{}{queueLevel: {}}) s.Len(timerQueueProcessBase.processingQueueCollections, 1) s.Len(timerQueueProcessBase.processingQueueCollections[0].Queues(), 1) activeQueue := timerQueueProcessBase.processingQueueCollections[0].ActiveQueue() s.NotNil(activeQueue) s.Equal(ackLevel, activeQueue.State().AckLevel()) s.Equal(ackLevel, activeQueue.State().ReadLevel()) s.Equal(maxLevel, activeQueue.State().MaxLevel()) s.Empty(timerQueueProcessBase.processingQueueReadProgress) s.Empty(timerQueueProcessBase.backoffTimer) time.Sleep(100 * time.Millisecond) s.True(timerQueueProcessBase.nextPollTime[queueLevel].Before(s.mockShard.GetTimeSource().Now())) select { case <-timerQueueProcessBase.timerGate.Chan(): default: s.Fail("timer gate should fire") } } func (s *timerQueueProcessorBaseSuite) TestProcessBatch_HasNextPage() { now := time.Now() queueLevel := 0 ackLevel := newTimerTaskKey(now.Add(-5*time.Second), 0) shardMaxReadLevel := newTimerTaskKey(now.Add(1*time.Second), 0) maxLevel := newTimerTaskKey(now.Add(10*time.Second), 0) processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( queueLevel, ackLevel, maxLevel, NewDomainFilter(map[string]struct{}{"excludedDomain": {}}, true), ), } updateMaxReadLevel := func() task.Key { return shardMaxReadLevel } request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(ackLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(shardMaxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: s.mockShard.GetConfig().TimerTaskBatchSize(), NextPageToken: nil, } response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: now.Add(-3 * time.Second), TaskID: int64(59), Version: int64(1), }, EventID: int64(28), }, &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "excludedDomain", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: now.Add(-2 * time.Second), TaskID: int64(60), Version: int64(1), }, EventID: int64(28), }, }, NextPageToken: []byte("some random next page token"), } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() s.mockTaskProcessor.EXPECT().TrySubmit(gomock.Any()).Return(true, nil).AnyTimes() timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(processingQueueStates, updateMaxReadLevel, nil, nil, nil) defer done() timerQueueProcessBase.processQueueCollections(map[int]struct{}{queueLevel: {}}) s.Len(timerQueueProcessBase.processingQueueCollections, 1) s.Len(timerQueueProcessBase.processingQueueCollections[0].Queues(), 1) activeQueue := timerQueueProcessBase.processingQueueCollections[0].ActiveQueue() s.NotNil(activeQueue) s.Equal(ackLevel, activeQueue.State().AckLevel()) s.Equal(newTimerTaskKey(response.Tasks[1].GetVisibilityTimestamp(), 0), activeQueue.State().ReadLevel()) s.Equal(maxLevel, activeQueue.State().MaxLevel()) s.Len(activeQueue.(*processingQueueImpl).outstandingTasks, 1) s.Len(timerQueueProcessBase.processingQueueReadProgress, 1) s.Equal(timeTaskReadProgress{ currentQueue: activeQueue, readLevel: ackLevel, maxReadLevel: shardMaxReadLevel, nextPageToken: response.NextPageToken, }, timerQueueProcessBase.processingQueueReadProgress[0]) s.True(timerQueueProcessBase.nextPollTime[queueLevel].IsZero()) s.Empty(timerQueueProcessBase.backoffTimer) time.Sleep(100 * time.Millisecond) select { case <-timerQueueProcessBase.timerGate.Chan(): default: s.Fail("timer gate should fire") } } func (s *timerQueueProcessorBaseSuite) TestProcessBatch_NoNextPage_HasLookAhead() { now := time.Now() queueLevel := 0 ackLevel := newTimerTaskKey(now.Add(-5*time.Second), 0) shardMaxReadLevel := newTimerTaskKey(now.Add(1*time.Second), 0) maxLevel := newTimerTaskKey(now.Add(10*time.Second), 0) processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( queueLevel, ackLevel, maxLevel, NewDomainFilter(map[string]struct{}{"excludedDomain": {}}, true), ), } updateMaxReadLevel := func() task.Key { return shardMaxReadLevel } requestNextPageToken := []byte("some random input next page token") request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(ackLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(shardMaxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: s.mockShard.GetConfig().TimerTaskBatchSize(), NextPageToken: requestNextPageToken, } lookAheadTaskTimestamp := now.Add(50 * time.Millisecond) response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: now.Add(-3 * time.Second), TaskID: int64(59), Version: int64(1), }, EventID: int64(28), }, &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "excludedDomain", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: lookAheadTaskTimestamp, TaskID: int64(60), Version: int64(1), }, EventID: int64(28), }, }, NextPageToken: nil, } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() s.mockTaskProcessor.EXPECT().TrySubmit(gomock.Any()).Return(true, nil).AnyTimes() timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(processingQueueStates, updateMaxReadLevel, nil, nil, nil) defer done() timerQueueProcessBase.processingQueueReadProgress[0] = timeTaskReadProgress{ currentQueue: timerQueueProcessBase.processingQueueCollections[0].ActiveQueue(), readLevel: ackLevel, maxReadLevel: shardMaxReadLevel, nextPageToken: requestNextPageToken, } timerQueueProcessBase.processQueueCollections(map[int]struct{}{queueLevel: {}}) s.Len(timerQueueProcessBase.processingQueueCollections, 1) s.Len(timerQueueProcessBase.processingQueueCollections[0].Queues(), 1) activeQueue := timerQueueProcessBase.processingQueueCollections[0].ActiveQueue() s.NotNil(activeQueue) s.Equal(ackLevel, activeQueue.State().AckLevel()) s.Equal(newTimerTaskKey(lookAheadTaskTimestamp, 0), activeQueue.State().ReadLevel()) s.Equal(maxLevel, activeQueue.State().MaxLevel()) s.Len(activeQueue.(*processingQueueImpl).outstandingTasks, 1) s.Empty(timerQueueProcessBase.processingQueueReadProgress) s.Empty(timerQueueProcessBase.backoffTimer) s.Equal(lookAheadTaskTimestamp, timerQueueProcessBase.nextPollTime[queueLevel]) time.Sleep(100 * time.Millisecond) select { case <-timerQueueProcessBase.timerGate.Chan(): default: s.Fail("timer gate should fire") } } func (s *timerQueueProcessorBaseSuite) TestProcessBatch_NoNextPage_NoLookAhead() { now := time.Now() queueLevel := 0 ackLevel := newTimerTaskKey(now.Add(-5*time.Second), 0) shardMaxReadLevel := newTimerTaskKey(now.Add(1*time.Second), 0) maxLevel := newTimerTaskKey(now.Add(10*time.Second), 0) processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( queueLevel, ackLevel, maxLevel, NewDomainFilter(map[string]struct{}{"excludedDomain": {}}, true), ), } updateMaxReadLevel := func() task.Key { return shardMaxReadLevel } requestNextPageToken := []byte("some random input next page token") request := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(ackLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(shardMaxReadLevel.(timerTaskKey).visibilityTimestamp, 0), PageSize: s.mockShard.GetConfig().TimerTaskBatchSize(), NextPageToken: requestNextPageToken, } lookAheadRequest := &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(shardMaxReadLevel.(timerTaskKey).visibilityTimestamp, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(maximumTimerTaskKey.(timerTaskKey).visibilityTimestamp, 0), PageSize: 1, NextPageToken: nil, } response := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "some random domain ID", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: now.Add(-3 * time.Second), TaskID: int64(59), Version: int64(1), }, EventID: int64(28), }, &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "excludedDomain", WorkflowID: "some random workflow ID", RunID: uuid.New(), }, TaskData: persistence.TaskData{ VisibilityTimestamp: now.Add(-2 * time.Second), TaskID: int64(60), Version: int64(1), }, EventID: int64(28), }, }, NextPageToken: nil, } mockExecutionMgr := s.mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetHistoryTasks", mock.Anything, request).Return(response, nil).Once() mockExecutionMgr.On("GetHistoryTasks", mock.Anything, lookAheadRequest).Return(&persistence.GetHistoryTasksResponse{}, nil).Once() s.mockTaskProcessor.EXPECT().TrySubmit(gomock.Any()).Return(true, nil).AnyTimes() timerQueueProcessBase, done := s.newTestTimerQueueProcessorBase(processingQueueStates, updateMaxReadLevel, nil, nil, nil) defer done() timerQueueProcessBase.processingQueueReadProgress[0] = timeTaskReadProgress{ currentQueue: timerQueueProcessBase.processingQueueCollections[0].ActiveQueue(), readLevel: ackLevel, maxReadLevel: shardMaxReadLevel, nextPageToken: requestNextPageToken, } timerQueueProcessBase.processQueueCollections(map[int]struct{}{queueLevel: {}}) s.Len(timerQueueProcessBase.processingQueueCollections, 1) s.Len(timerQueueProcessBase.processingQueueCollections[0].Queues(), 1) activeQueue := timerQueueProcessBase.processingQueueCollections[0].ActiveQueue() s.NotNil(activeQueue) s.Equal(ackLevel, activeQueue.State().AckLevel()) s.Equal(shardMaxReadLevel, activeQueue.State().ReadLevel()) s.Equal(maxLevel, activeQueue.State().MaxLevel()) s.Len(activeQueue.(*processingQueueImpl).outstandingTasks, 1) s.Empty(timerQueueProcessBase.processingQueueReadProgress) _, ok := timerQueueProcessBase.nextPollTime[queueLevel] s.True(ok) // this is the poll time for max poll interval time.Sleep(100 * time.Millisecond) select { case <-timerQueueProcessBase.timerGate.Chan(): s.Fail("timer gate should not fire") default: } } func (s *timerQueueProcessorBaseSuite) TestTimerProcessorPump_HandleAckLevelUpdate() { now := time.Now() queueLevel := 0 ackLevel := newTimerTaskKey(now.Add(50*time.Millisecond), 0) maxLevel := newTimerTaskKey(now.Add(10*time.Second), 0) processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( queueLevel, ackLevel, maxLevel, NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), } updateMaxReadLevel := func() task.Key { return newTimerTaskKey(now, 0) } timerQueueProcessBase, _ := s.newTestTimerQueueProcessorBase(processingQueueStates, updateMaxReadLevel, nil, nil, nil) timerQueueProcessBase.options.UpdateAckInterval = dynamicproperties.GetDurationPropertyFn(1 * time.Millisecond) updatedCh := make(chan struct{}, 1) timerQueueProcessBase.updateAckLevelFn = func() (bool, task.Key, error) { updatedCh <- struct{}{} return false, nil, nil } timerQueueProcessBase.Start() defer timerQueueProcessBase.Stop() select { case <-updatedCh: return case <-time.After(100 * time.Millisecond): s.Fail("Ack level update not called") } } func (s *timerQueueProcessorBaseSuite) TestTimerProcessorPump_SplitQueue() { now := time.Now() queueLevel := 0 ackLevel := newTimerTaskKey(now.Add(50*time.Millisecond), 0) maxLevel := newTimerTaskKey(now.Add(10*time.Second), 0) processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( queueLevel, ackLevel, maxLevel, NewDomainFilter(map[string]struct{}{"testDomain1": {}}, false), ), } updateMaxReadLevel := func() task.Key { return newTimerTaskKey(now, 0) } timerQueueProcessBase, _ := s.newTestTimerQueueProcessorBase(processingQueueStates, updateMaxReadLevel, nil, nil, nil) timerQueueProcessBase.options.SplitQueueInterval = dynamicproperties.GetDurationPropertyFn(1 * time.Millisecond) splittedCh := make(chan struct{}, 1) timerQueueProcessBase.splitProcessingQueueCollectionFn = func(splitPolicy ProcessingQueueSplitPolicy, upsertPollTimeFn func(int, time.Time)) { splittedCh <- struct{}{} } timerQueueProcessBase.Start() defer timerQueueProcessBase.Stop() select { case <-splittedCh: return case <-time.After(100 * time.Millisecond): s.Fail("splitProcessingQueueCollectionFn not called") } } func (s *timerQueueProcessorBaseSuite) newTestTimerQueueProcessorBase( processingQueueStates []ProcessingQueueState, updateMaxReadLevel updateMaxReadLevelFn, updateClusterAckLevel updateClusterAckLevelFn, updateProcessingQueueStates updateProcessingQueueStatesFn, queueShutdown queueShutdownFn, ) (*timerQueueProcessorBase, func()) { timerGate := clock.NewTimerGate(s.mockShard.GetTimeSource()) return newTimerQueueProcessorBase( s.clusterName, s.mockShard, processingQueueStates, s.mockTaskProcessor, timerGate, newTimerQueueProcessorOptions(s.mockShard.GetConfig(), true, false), updateMaxReadLevel, updateClusterAckLevel, updateProcessingQueueStates, queueShutdown, nil, nil, s.logger, s.metricsClient, ), func() { timerGate.Stop() } } ================================================ FILE: service/history/queue/timer_queue_standby_processor.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) func newTimerQueueStandbyProcessor( clusterName string, shard shard.Context, taskProcessor task.Processor, taskAllocator TaskAllocator, taskExecutor task.Executor, logger log.Logger, ) (*timerQueueProcessorBase, clock.EventTimerGate) { config := shard.GetConfig() options := newTimerQueueProcessorOptions(config, false, false) logger = logger.WithTags(tag.ClusterName(clusterName)) taskFilter := func(timer persistence.Task) (bool, error) { if timer.GetTaskCategory() != persistence.HistoryTaskCategoryTimer { return false, errUnexpectedQueueTask } if notRegistered, err := isDomainNotRegistered(shard, timer.GetDomainID()); notRegistered && err == nil { // Allow deletion tasks for deprecated domains if timer.GetTaskType() == persistence.TaskTypeDeleteHistoryEvent { return true, nil } logger.Info("Domain is not in registered status, skip task in standby timer queue.", tag.WorkflowDomainID(timer.GetDomainID()), tag.Value(timer)) return false, nil } if timer.GetTaskType() == persistence.TaskTypeWorkflowTimeout || timer.GetTaskType() == persistence.TaskTypeDeleteHistoryEvent { domainEntry, err := shard.GetDomainCache().GetDomainByID(timer.GetDomainID()) if err == nil { if domainEntry.HasReplicationCluster(clusterName) { // guarantee the processing of workflow execution history deletion return true, nil } } else { if _, ok := err.(*types.EntityNotExistsError); !ok { // retry the task if failed to find the domain logger.Warn("Cannot find domain", tag.WorkflowDomainID(timer.GetDomainID())) return false, err } logger.Warn("Cannot find domain, default to not process task.", tag.WorkflowDomainID(timer.GetDomainID()), tag.Value(timer)) return false, nil } } return taskAllocator.VerifyStandbyTask(clusterName, timer.GetDomainID(), timer.GetWorkflowID(), timer.GetRunID(), timer) } updateMaxReadLevel := func() task.Key { return newTimerTaskKey(shard.UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryTimer, clusterName).GetScheduledTime(), 0) } updateClusterAckLevel := func(ackLevel task.Key) error { return shard.UpdateQueueClusterAckLevel(persistence.HistoryTaskCategoryTimer, clusterName, persistence.NewHistoryTaskKey(ackLevel.(timerTaskKey).visibilityTimestamp, 0)) } updateProcessingQueueStates := func(states []ProcessingQueueState) error { pStates := convertToPersistenceTimerProcessingQueueStates(states) return shard.UpdateTimerProcessingQueueStates(clusterName, pStates) } queueShutdown := func() error { return nil } remoteTimerGate := clock.NewEventTimerGate(shard.GetCurrentTime(clusterName)) return newTimerQueueProcessorBase( clusterName, shard, loadTimerProcessingQueueStates(clusterName, shard, options, logger), taskProcessor, remoteTimerGate, options, updateMaxReadLevel, updateClusterAckLevel, updateProcessingQueueStates, queueShutdown, taskFilter, taskExecutor, logger, shard.GetMetricsClient(), ), remoteTimerGate } ================================================ FILE: service/history/queue/transfer_queue_processor.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "context" "errors" "fmt" "math" "sync" "sync/atomic" "time" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/types" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/reset" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" "github.com/uber/cadence/service/history/workflowcache" "github.com/uber/cadence/service/worker/archiver" ) var ( errUnexpectedQueueTask = errors.New("unexpected queue task") errProcessorShutdown = errors.New("queue processor has been shutdown") maximumTransferTaskKey = newTransferTaskKey(math.MaxInt64) ) type transferQueueProcessor struct { shard shard.Context historyEngine engine.Engine taskProcessor task.Processor config *config.Config currentClusterName string metricsClient metrics.Client logger log.Logger status int32 shutdownChan chan struct{} shutdownWG sync.WaitGroup ackLevel int64 taskAllocator TaskAllocator activeTaskExecutor task.Executor activeQueueProcessor *transferQueueProcessorBase standbyQueueProcessors map[string]*transferQueueProcessorBase failoverQueueProcessors []*transferQueueProcessorBase } // NewTransferQueueProcessor creates a new transfer QueueProcessor func NewTransferQueueProcessor( shard shard.Context, taskProcessor task.Processor, executionCache execution.Cache, workflowResetter reset.WorkflowResetter, archivalClient archiver.Client, executionCheck invariant.Invariant, wfIDCache workflowcache.WFCache, ) Processor { logger := shard.GetLogger().WithTags(tag.ComponentTransferQueue) currentClusterName := shard.GetClusterMetadata().GetCurrentClusterName() config := shard.GetConfig() taskAllocator := NewTaskAllocator(shard) activeLogger := logger.WithTags(tag.QueueTypeActive) activeTaskExecutor := task.NewTransferActiveTaskExecutor( shard, archivalClient, executionCache, workflowResetter, activeLogger, config, wfIDCache, ) activeQueueProcessor := newTransferQueueActiveProcessor( shard, taskProcessor, taskAllocator, activeTaskExecutor, activeLogger, ) standbyQueueProcessors := make(map[string]*transferQueueProcessorBase) for clusterName := range shard.GetClusterMetadata().GetRemoteClusterInfo() { historyResender := ndc.NewHistoryResender( shard.GetDomainCache(), shard.GetService().GetClientBean(), func(ctx context.Context, request *types.ReplicateEventsV2Request) error { return shard.GetEngine().ReplicateEventsV2(ctx, request) }, config.StandbyTaskReReplicationContextTimeout, executionCheck, shard.GetLogger(), ) standByLogger := logger.WithTags(tag.QueueTypeStandby, tag.ActiveClusterName(clusterName)) standbyTaskExecutor := task.NewTransferStandbyTaskExecutor( shard, archivalClient, executionCache, historyResender, standByLogger, clusterName, config, ) standbyQueueProcessors[clusterName] = newTransferQueueStandbyProcessor( clusterName, shard, taskProcessor, taskAllocator, standbyTaskExecutor, standByLogger, ) } return &transferQueueProcessor{ shard: shard, taskProcessor: taskProcessor, config: config, currentClusterName: currentClusterName, metricsClient: shard.GetMetricsClient(), logger: logger, status: common.DaemonStatusInitialized, shutdownChan: make(chan struct{}), ackLevel: shard.GetQueueAckLevel(persistence.HistoryTaskCategoryTransfer).GetTaskID(), taskAllocator: taskAllocator, activeTaskExecutor: activeTaskExecutor, activeQueueProcessor: activeQueueProcessor, standbyQueueProcessors: standbyQueueProcessors, } } func (t *transferQueueProcessor) Start() { if !atomic.CompareAndSwapInt32(&t.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } t.logger.Info("Starting transfer queue processor") defer t.logger.Info("Transfer queue processor started") t.activeQueueProcessor.Start() for _, standbyQueueProcessor := range t.standbyQueueProcessors { standbyQueueProcessor.Start() } t.shutdownWG.Add(1) go t.completeTransferLoop() } func (t *transferQueueProcessor) Stop() { if !atomic.CompareAndSwapInt32(&t.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } if !t.shard.GetConfig().QueueProcessorEnableGracefulSyncShutdown() { t.logger.Info("Stopping transfer queue processor non-gracefully") defer t.logger.Info("Transfer queue processor stopped non-gracefully") t.activeQueueProcessor.Stop() for _, standbyQueueProcessor := range t.standbyQueueProcessors { standbyQueueProcessor.Stop() } close(t.shutdownChan) if !common.AwaitWaitGroup(&t.shutdownWG, time.Minute) { t.logger.Warn("transferQueueProcessor timed out on shut down", tag.LifeCycleStopTimedout) } return } t.logger.Info("Stopping transfer queue processor gracefully") defer t.logger.Info("Transfer queue processor stopped gracefully") // close the shutdown channel so processor pump goroutine drains tasks and then stop the processors close(t.shutdownChan) if !common.AwaitWaitGroup(&t.shutdownWG, gracefulShutdownTimeout) { t.logger.Warn("transferQueueProcessor timed out on shut down", tag.LifeCycleStopTimedout) } t.activeQueueProcessor.Stop() for _, standbyQueueProcessor := range t.standbyQueueProcessors { standbyQueueProcessor.Stop() } if len(t.failoverQueueProcessors) > 0 { t.logger.Info("Shutting down failover transfer queues", tag.Counter(len(t.failoverQueueProcessors))) for _, failoverQueueProcessor := range t.failoverQueueProcessors { failoverQueueProcessor.Stop() } } } func (t *transferQueueProcessor) NotifyNewTask(clusterName string, info *hcommon.NotifyTaskInfo) { if len(info.Tasks) == 0 { return } if clusterName == t.currentClusterName { t.activeQueueProcessor.notifyNewTask(info) return } standbyQueueProcessor, ok := t.standbyQueueProcessors[clusterName] if !ok { panic(fmt.Sprintf("Cannot find transfer processor for %s.", clusterName)) } standbyQueueProcessor.notifyNewTask(info) } func (t *transferQueueProcessor) FailoverDomain(domainIDs map[string]struct{}) { if t.shard.GetConfig().DisableTransferFailoverQueue() { return } // Failover queue is used to scan all inflight tasks, if queue processor is not // started, there's no inflight task and we don't need to create a failover processor. // Also the HandleAction will be blocked if queue processor processing loop is not running. if atomic.LoadInt32(&t.status) != common.DaemonStatusStarted { return } minLevel := t.shard.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryTransfer, t.currentClusterName).GetTaskID() standbyClusterName := t.currentClusterName for clusterName := range t.shard.GetClusterMetadata().GetEnabledClusterInfo() { ackLevel := t.shard.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryTransfer, clusterName).GetTaskID() if ackLevel < minLevel { minLevel = ackLevel standbyClusterName = clusterName } } maxReadLevel := int64(0) actionResult, err := t.HandleAction(context.Background(), t.currentClusterName, NewGetStateAction()) if err != nil { t.logger.Error("Transfer Failover Failed", tag.WorkflowDomainIDs(domainIDs), tag.Error(err)) if err == errProcessorShutdown { // processor/shard already shutdown, we don't need to create failover queue processor return } // other errors should never be returned for GetStateAction panic(fmt.Sprintf("unknown error for GetStateAction: %v", err)) } for _, queueState := range actionResult.GetStateActionResult.States { queueReadLevel := queueState.ReadLevel().(transferTaskKey).taskID if maxReadLevel < queueReadLevel { maxReadLevel = queueReadLevel } } // maxReadLevel is exclusive, so add 1 maxReadLevel++ t.logger.Info("Transfer Failover Triggered", tag.WorkflowDomainIDs(domainIDs), tag.MinLevel(minLevel), tag.MaxLevel(maxReadLevel)) updateShardAckLevel, failoverQueueProcessor := newTransferQueueFailoverProcessor( t.shard, t.taskProcessor, t.taskAllocator, t.activeTaskExecutor, t.logger, minLevel, maxReadLevel, domainIDs, standbyClusterName, ) // NOTE: READ REF BEFORE MODIFICATION // ref: historyEngine.go registerDomainFailoverCallback function err = updateShardAckLevel(newTransferTaskKey(minLevel)) if err != nil { t.logger.Error("Error update shard ack level", tag.Error(err)) } // Failover queue processors are started on the fly when domains are failed over. // Failover queue processors will be stopped when the transfer queue instance is stopped (due to restart or shard movement). // This means the failover queue processor might not finish its job. // There is no mechanism to re-start ongoing failover queue processors in the new shard owner. t.failoverQueueProcessors = append(t.failoverQueueProcessors, failoverQueueProcessor) failoverQueueProcessor.Start() } func (t *transferQueueProcessor) HandleAction( ctx context.Context, clusterName string, action *Action, ) (*ActionResult, error) { var resultNotificationCh chan actionResultNotification var added bool if clusterName == t.currentClusterName { resultNotificationCh, added = t.activeQueueProcessor.addAction(ctx, action) } else { found := false for standbyClusterName, standbyProcessor := range t.standbyQueueProcessors { if clusterName == standbyClusterName { resultNotificationCh, added = standbyProcessor.addAction(ctx, action) found = true break } } if !found { return nil, fmt.Errorf("unknown cluster name: %v", clusterName) } } if !added { if ctxErr := ctx.Err(); ctxErr != nil { return nil, ctxErr } return nil, errProcessorShutdown } select { case resultNotification := <-resultNotificationCh: return resultNotification.result, resultNotification.err case <-t.shutdownChan: return nil, errProcessorShutdown case <-ctx.Done(): return nil, ctx.Err() } } func (t *transferQueueProcessor) LockTaskProcessing() { t.logger.Debug("Transfer queue processor locking task processing") t.taskAllocator.Lock() } func (t *transferQueueProcessor) UnlockTaskProcessing() { t.logger.Debug("Transfer queue processor unlocking task processing") t.taskAllocator.Unlock() } func (t *transferQueueProcessor) drain() { // before shutdown, make sure the ack level is up to date if err := t.completeTransfer(); err != nil { t.logger.Error("Failed to complete transfer task during shutdown", tag.Error(err)) } } func (t *transferQueueProcessor) completeTransferLoop() { defer t.shutdownWG.Done() t.logger.Info("Transfer queue processor completeTransferLoop") defer t.logger.Info("Transfer queue processor completeTransferLoop completed") completeTimer := time.NewTimer(t.config.TransferProcessorCompleteTransferInterval()) defer completeTimer.Stop() // Create a retryTimer once, and reset it as needed retryTimer := time.NewTimer(0) defer retryTimer.Stop() // Stop it immediately because we don't want it to fire initially if !retryTimer.Stop() { <-retryTimer.C } for { select { case <-t.shutdownChan: t.drain() return case <-completeTimer.C: for attempt := 0; attempt < t.config.TransferProcessorCompleteTransferFailureRetryCount(); attempt++ { err := t.completeTransfer() if err == nil { break } t.logger.Error("Failed to complete transfer task", tag.Error(err), tag.Attempt(int32(attempt))) var errShardClosed *shard.ErrShardClosed if errors.As(err, &errShardClosed) { // shard closed, trigger shutdown and bail out if !t.shard.GetConfig().QueueProcessorEnableGracefulSyncShutdown() { go t.Stop() return } t.Stop() return } // Reset the retryTimer for the delay between attempts // TODO: the first retry has 0 backoff, revisit it to see if it's expected retryDuration := time.Duration(attempt*100) * time.Millisecond retryTimer.Reset(retryDuration) select { case <-t.shutdownChan: t.drain() return case <-retryTimer.C: // do nothing. retry loop will continue } } completeTimer.Reset(t.config.TransferProcessorCompleteTransferInterval()) } } } func (t *transferQueueProcessor) completeTransfer() error { newAckLevel := maximumTransferTaskKey actionResult, err := t.HandleAction(context.Background(), t.currentClusterName, NewGetStateAction()) if err != nil { return err } for _, queueState := range actionResult.GetStateActionResult.States { newAckLevel = minTaskKey(newAckLevel, queueState.AckLevel()) } for standbyClusterName := range t.standbyQueueProcessors { actionResult, err := t.HandleAction(context.Background(), standbyClusterName, NewGetStateAction()) if err != nil { return err } for _, queueState := range actionResult.GetStateActionResult.States { newAckLevel = minTaskKey(newAckLevel, queueState.AckLevel()) } } for _, failoverInfo := range t.shard.GetAllFailoverLevels(persistence.HistoryTaskCategoryTransfer) { failoverLevel := newTransferTaskKey(failoverInfo.MinLevel.GetTaskID()) if newAckLevel == nil { newAckLevel = failoverLevel } else { newAckLevel = minTaskKey(newAckLevel, failoverLevel) } } if newAckLevel == nil { panic("Unable to get transfer queue processor ack level") } newAckLevelTaskID := newAckLevel.(transferTaskKey).taskID t.logger.Debugf("Start completing transfer task from: %v, to %v.", t.ackLevel, newAckLevelTaskID) if t.ackLevel >= newAckLevelTaskID { return nil } t.metricsClient.Scope(metrics.TransferQueueProcessorScope). Tagged(metrics.ShardIDTag(t.shard.GetShardID())). IncCounter(metrics.TaskBatchCompleteCounter) for { pageSize := t.config.TransferTaskDeleteBatchSize() // we're switching from exclusive begin/end to inclusive min/exclusive max, // so we need to adjust the taskID, for example, if the original range is (1, 10], // the new range should be [2, 11), so we add 1 to both the min and max taskID resp, err := t.shard.GetExecutionManager().RangeCompleteHistoryTask(context.Background(), &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(t.ackLevel + 1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(newAckLevelTaskID + 1), PageSize: pageSize, }) if err != nil { return err } if !persistence.HasMoreRowsToDelete(resp.TasksCompleted, pageSize) { break } } t.ackLevel = newAckLevelTaskID return t.shard.UpdateQueueAckLevel(persistence.HistoryTaskCategoryTransfer, persistence.NewImmediateTaskKey(newAckLevelTaskID)) } func newTransferQueueActiveProcessor( shard shard.Context, taskProcessor task.Processor, taskAllocator TaskAllocator, taskExecutor task.Executor, logger log.Logger, ) *transferQueueProcessorBase { config := shard.GetConfig() options := newTransferQueueProcessorOptions(config, true, false) currentClusterName := shard.GetClusterMetadata().GetCurrentClusterName() logger = logger.WithTags(tag.ClusterName(currentClusterName)) taskFilter := func(task persistence.Task) (bool, error) { if task.GetTaskCategory() != persistence.HistoryTaskCategoryTransfer { return false, errUnexpectedQueueTask } if notRegistered, err := isDomainNotRegistered(shard, task.GetDomainID()); notRegistered && err == nil { logger.Info("Domain is not in registered status, skip task in active transfer queue.", tag.WorkflowDomainID(task.GetDomainID()), tag.Value(task)) return false, nil } return taskAllocator.VerifyActiveTask(task.GetDomainID(), task.GetWorkflowID(), task.GetRunID(), task) } updateMaxReadLevel := func() task.Key { return newTransferTaskKey(shard.UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryTransfer, currentClusterName).GetTaskID()) } updateClusterAckLevel := func(ackLevel task.Key) error { taskID := ackLevel.(transferTaskKey).taskID return shard.UpdateQueueClusterAckLevel(persistence.HistoryTaskCategoryTransfer, currentClusterName, persistence.NewImmediateTaskKey(taskID)) } updateProcessingQueueStates := func(states []ProcessingQueueState) error { pStates := convertToPersistenceTransferProcessingQueueStates(states) return shard.UpdateTransferProcessingQueueStates(currentClusterName, pStates) } queueShutdown := func() error { return nil } return newTransferQueueProcessorBase( shard, loadTransferProcessingQueueStates(currentClusterName, shard, options, logger), taskProcessor, options, updateMaxReadLevel, updateClusterAckLevel, updateProcessingQueueStates, queueShutdown, taskFilter, taskExecutor, logger, shard.GetMetricsClient(), ) } func newTransferQueueStandbyProcessor( clusterName string, shard shard.Context, taskProcessor task.Processor, taskAllocator TaskAllocator, taskExecutor task.Executor, logger log.Logger, ) *transferQueueProcessorBase { config := shard.GetConfig() options := newTransferQueueProcessorOptions(config, false, false) logger = logger.WithTags(tag.ClusterName(clusterName)) taskFilter := func(task persistence.Task) (bool, error) { if task.GetTaskCategory() != persistence.HistoryTaskCategoryTransfer { return false, errUnexpectedQueueTask } if notRegistered, err := isDomainNotRegistered(shard, task.GetDomainID()); notRegistered && err == nil { logger.Info("Domain is not in registered status, skip task in standby transfer queue.", tag.WorkflowDomainID(task.GetDomainID()), tag.Value(task)) return false, nil } if task.GetTaskType() == persistence.TransferTaskTypeCloseExecution || task.GetTaskType() == persistence.TransferTaskTypeRecordWorkflowClosed { domainEntry, err := shard.GetDomainCache().GetDomainByID(task.GetDomainID()) if err == nil { if domainEntry.HasReplicationCluster(clusterName) { // guarantee the processing of workflow execution close return true, nil } } else { if _, ok := err.(*types.EntityNotExistsError); !ok { // retry the task if failed to find the domain logger.Warn("Cannot find domain", tag.WorkflowDomainID(task.GetDomainID())) return false, err } logger.Warn("Cannot find domain, default to not process task.", tag.WorkflowDomainID(task.GetDomainID()), tag.Value(task)) return false, nil } } return taskAllocator.VerifyStandbyTask(clusterName, task.GetDomainID(), task.GetWorkflowID(), task.GetRunID(), task) } updateMaxReadLevel := func() task.Key { return newTransferTaskKey(shard.UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryTransfer, clusterName).GetTaskID()) } updateClusterAckLevel := func(ackLevel task.Key) error { taskID := ackLevel.(transferTaskKey).taskID return shard.UpdateQueueClusterAckLevel(persistence.HistoryTaskCategoryTransfer, clusterName, persistence.NewImmediateTaskKey(taskID)) } updateProcessingQueueStates := func(states []ProcessingQueueState) error { pStates := convertToPersistenceTransferProcessingQueueStates(states) return shard.UpdateTransferProcessingQueueStates(clusterName, pStates) } queueShutdown := func() error { return nil } return newTransferQueueProcessorBase( shard, loadTransferProcessingQueueStates(clusterName, shard, options, logger), taskProcessor, options, updateMaxReadLevel, updateClusterAckLevel, updateProcessingQueueStates, queueShutdown, taskFilter, taskExecutor, logger, shard.GetMetricsClient(), ) } func newTransferQueueFailoverProcessor( shardContext shard.Context, taskProcessor task.Processor, taskAllocator TaskAllocator, taskExecutor task.Executor, logger log.Logger, minLevel, maxLevel int64, domainIDs map[string]struct{}, standbyClusterName string, ) (updateClusterAckLevelFn, *transferQueueProcessorBase) { config := shardContext.GetConfig() options := newTransferQueueProcessorOptions(config, true, true) currentClusterName := shardContext.GetService().GetClusterMetadata().GetCurrentClusterName() failoverUUID := uuid.New() logger = logger.WithTags( tag.ClusterName(currentClusterName), tag.WorkflowDomainIDs(domainIDs), tag.FailoverMsg("from: "+standbyClusterName), ) taskFilter := func(task persistence.Task) (bool, error) { if task.GetTaskCategory() != persistence.HistoryTaskCategoryTransfer { return false, errUnexpectedQueueTask } if notRegistered, err := isDomainNotRegistered(shardContext, task.GetDomainID()); notRegistered && err == nil { logger.Info("Domain is not in registered status, skip task in failover transfer queue.", tag.WorkflowDomainID(task.GetDomainID()), tag.Value(task)) return false, nil } return taskAllocator.VerifyFailoverActiveTask(domainIDs, task.GetDomainID(), task.GetWorkflowID(), task.GetRunID(), task) } maxReadLevelTaskKey := newTransferTaskKey(maxLevel) updateMaxReadLevel := func() task.Key { return maxReadLevelTaskKey // this is a const } updateClusterAckLevel := func(ackLevel task.Key) error { taskID := ackLevel.(transferTaskKey).taskID return shardContext.UpdateFailoverLevel( persistence.HistoryTaskCategoryTransfer, failoverUUID, persistence.FailoverLevel{ StartTime: shardContext.GetTimeSource().Now(), MinLevel: persistence.NewImmediateTaskKey(minLevel), CurrentLevel: persistence.NewImmediateTaskKey(taskID), MaxLevel: persistence.NewImmediateTaskKey(maxLevel), DomainIDs: domainIDs, }, ) } queueShutdown := func() error { return shardContext.DeleteFailoverLevel( persistence.HistoryTaskCategoryTransfer, failoverUUID, ) } processingQueueStates := []ProcessingQueueState{ NewProcessingQueueState( defaultProcessingQueueLevel, newTransferTaskKey(minLevel), maxReadLevelTaskKey, NewDomainFilter(domainIDs, false), ), } return updateClusterAckLevel, newTransferQueueProcessorBase( shardContext, processingQueueStates, taskProcessor, options, updateMaxReadLevel, updateClusterAckLevel, nil, queueShutdown, taskFilter, taskExecutor, logger, shardContext.GetMetricsClient(), ) } func loadTransferProcessingQueueStates( clusterName string, shard shard.Context, options *queueProcessorOptions, logger log.Logger, ) []ProcessingQueueState { ackLevel := shard.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryTransfer, clusterName).GetTaskID() if options.EnableLoadQueueStates() { pStates := shard.GetTransferProcessingQueueStates(clusterName) if validateProcessingQueueStates(pStates, ackLevel) { return convertFromPersistenceTransferProcessingQueueStates(pStates) } logger.Error("Incompatible processing queue states and ackLevel", tag.Value(pStates), tag.ShardTransferAcks(ackLevel), ) } // LoadQueueStates is disabled or sanity check failed // fallback to use ackLevel return []ProcessingQueueState{ NewProcessingQueueState( defaultProcessingQueueLevel, newTransferTaskKey(ackLevel), maximumTransferTaskKey, NewDomainFilter(nil, true), ), } } ================================================ FILE: service/history/queue/transfer_queue_processor_base.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "context" "errors" "math/rand" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) const ( numTasksEstimationDecay = 0.6 ) var ( loadQueueTaskThrottleRetryDelay = 5 * time.Second persistenceOperationRetryPolicy = common.CreatePersistenceRetryPolicy() ) type ( transferTaskKey struct { taskID int64 } transferQueueProcessorBase struct { *processorBase taskInitializer task.Initializer notifyCh chan struct{} processCh chan struct{} // for managing if a processing queue collection should be processed startJitterTimer *time.Timer processingLock sync.Mutex backoffTimer map[int]*time.Timer shouldProcess map[int]bool // for estimating the look ahead taskID during split lastSplitTime time.Time lastMaxReadLevel int64 estimatedTasksPerMinute int64 // for validating if the queue failed to load any tasks validator *transferQueueValidator processQueueCollectionsFn func() updateAckLevelFn func() (bool, task.Key, error) } ) func newTransferQueueProcessorBase( shard shard.Context, processingQueueStates []ProcessingQueueState, taskProcessor task.Processor, options *queueProcessorOptions, updateMaxReadLevel updateMaxReadLevelFn, updateClusterAckLevel updateClusterAckLevelFn, updateProcessingQueueStates updateProcessingQueueStatesFn, queueShutdown queueShutdownFn, taskFilter task.Filter, taskExecutor task.Executor, logger log.Logger, metricsClient metrics.Client, ) *transferQueueProcessorBase { processorBase := newProcessorBase( shard, processingQueueStates, taskProcessor, options, updateMaxReadLevel, updateClusterAckLevel, updateProcessingQueueStates, queueShutdown, logger.WithTags(tag.ComponentTransferQueue), metricsClient, ) queueType := task.QueueTypeActiveTransfer if options.MetricScope == metrics.TransferStandbyQueueProcessorScope { queueType = task.QueueTypeStandbyTransfer } transferQueueProcessorBase := &transferQueueProcessorBase{ processorBase: processorBase, taskInitializer: func(taskInfo persistence.Task) task.Task { return task.NewHistoryTask( shard, taskInfo, queueType, task.InitializeLoggerForTask(shard.GetShardID(), taskInfo, logger), taskFilter, taskExecutor, taskProcessor, processorBase.redispatcher, shard.GetConfig().TaskCriticalRetryCount, ) }, notifyCh: make(chan struct{}, 1), processCh: make(chan struct{}, 1), backoffTimer: make(map[int]*time.Timer), shouldProcess: make(map[int]bool), lastSplitTime: time.Time{}, lastMaxReadLevel: 0, } transferQueueProcessorBase.processQueueCollectionsFn = transferQueueProcessorBase.processQueueCollections transferQueueProcessorBase.updateAckLevelFn = transferQueueProcessorBase.updateAckLevel if shard.GetConfig().EnableDebugMode && options.EnableValidator() { transferQueueProcessorBase.validator = newTransferQueueValidator( transferQueueProcessorBase, options.ValidationInterval, logger, processorBase.metricsScope, ) } return transferQueueProcessorBase } func (t *transferQueueProcessorBase) Start() { if !atomic.CompareAndSwapInt32(&t.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } t.logger.Info("Transfer queue processor state changed", tag.LifeCycleStarting) defer t.logger.Info("Transfer queue processor state changed", tag.LifeCycleStarted) t.redispatcher.Start() // trigger an initial (maybe delayed) load of tasks if startJitter := t.options.MaxStartJitterInterval(); startJitter > 0 { t.startJitterTimer = time.AfterFunc( time.Duration(rand.Int63n(int64(startJitter))), func() { t.notifyAllQueueCollections() }, ) } else { t.notifyAllQueueCollections() } t.shutdownWG.Add(1) go t.processorPump() } func (t *transferQueueProcessorBase) Stop() { if !atomic.CompareAndSwapInt32(&t.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } t.logger.Info("Transfer queue processor state changed", tag.LifeCycleStopping) close(t.shutdownCh) if t.startJitterTimer != nil { t.startJitterTimer.Stop() } t.processingLock.Lock() for _, timer := range t.backoffTimer { timer.Stop() } for level := range t.shouldProcess { t.shouldProcess[level] = false } t.processingLock.Unlock() if success := common.AwaitWaitGroup(&t.shutdownWG, gracefulShutdownTimeout); !success { t.logger.Warn("transferQueueProcessorBase timed out on shut down", tag.LifeCycleStopTimedout) } t.redispatcher.Stop() t.logger.Info("Transfer queue processor state changed", tag.LifeCycleStopped) } func (t *transferQueueProcessorBase) notifyNewTask(info *hcommon.NotifyTaskInfo) { select { case t.notifyCh <- struct{}{}: default: } if info.ExecutionInfo != nil && t.validator != nil { // executionInfo will be nil when notifyNewTask is called to trigger a scan, for example during domain failover or sync shard. t.validator.addTasks(info) } } func (t *transferQueueProcessorBase) readyForProcess(level int) { t.processingLock.Lock() defer t.processingLock.Unlock() if _, ok := t.backoffTimer[level]; ok { // current level is being throttled return } t.shouldProcess[level] = true // trigger the actual processing select { case t.processCh <- struct{}{}: default: } } func (t *transferQueueProcessorBase) setupBackoffTimer(level int) { t.processingLock.Lock() defer t.processingLock.Unlock() if _, ok := t.backoffTimer[level]; ok { // there's an existing backoff timer, no-op // this case should not happen return } t.metricsScope.IncCounter(metrics.ProcessingQueueThrottledCounter) t.logger.Info("Throttled processing queue", tag.QueueLevel(level)) backoffDuration := backoff.JitDuration( t.options.PollBackoffInterval(), t.options.PollBackoffIntervalJitterCoefficient(), ) t.backoffTimer[level] = time.AfterFunc(backoffDuration, func() { select { case <-t.shutdownCh: return default: } t.processingLock.Lock() defer t.processingLock.Unlock() t.shouldProcess[level] = true delete(t.backoffTimer, level) // trigger the actual processing select { case t.processCh <- struct{}{}: default: } }) } func (t *transferQueueProcessorBase) processorPump() { defer t.shutdownWG.Done() updateAckTimer := time.NewTimer(backoff.JitDuration( t.options.UpdateAckInterval(), t.options.UpdateAckIntervalJitterCoefficient(), )) defer updateAckTimer.Stop() splitQueueTimer := time.NewTimer(backoff.JitDuration( t.options.SplitQueueInterval(), t.options.SplitQueueIntervalJitterCoefficient(), )) defer splitQueueTimer.Stop() maxPollTimer := time.NewTimer(backoff.JitDuration( t.options.MaxPollInterval(), t.options.MaxPollIntervalJitterCoefficient(), )) defer maxPollTimer.Stop() for { select { case <-t.shutdownCh: return case <-t.notifyCh: // notify all queue collections as they are waiting for the notification when there's // no more task to process. For non-default queue, if we choose to do periodic polling // in the future, then we don't need to notify them. t.notifyAllQueueCollections() case <-maxPollTimer.C: t.notifyAllQueueCollections() maxPollTimer.Reset(backoff.JitDuration( t.options.MaxPollInterval(), t.options.MaxPollIntervalJitterCoefficient(), )) case <-t.processCh: maxRedispatchQueueSize := t.options.MaxRedispatchQueueSize() if redispathSize := t.redispatcher.Size(); redispathSize > maxRedispatchQueueSize { t.logger.Debugf("Transfer queue has too many pending tasks in re-dispatch queue: %v > maxRedispatchQueueSize: %v, block loading tasks from persistence", redispathSize, maxRedispatchQueueSize) t.redispatcher.Redispatch(maxRedispatchQueueSize) if redispathSize := t.redispatcher.Size(); redispathSize > maxRedispatchQueueSize { // if redispatcher still has a large number of tasks // this only happens when system is under very high load // we should backoff here instead of keeping submitting tasks to task processor t.logger.Debugf("Transfer queue still has too many pending tasks in re-dispatch queue: %v > maxRedispatchQueueSize: %v, backing off for %v", redispathSize, maxRedispatchQueueSize, t.options.PollBackoffInterval()) time.Sleep(backoff.JitDuration( t.options.PollBackoffInterval(), t.options.PollBackoffIntervalJitterCoefficient(), )) } // re-enqueue the event to see if we need keep re-dispatching or load new tasks from persistence select { case t.processCh <- struct{}{}: default: } } else { t.processQueueCollectionsFn() } case <-updateAckTimer.C: processFinished, _, err := t.updateAckLevelFn() var errShardClosed *shard.ErrShardClosed if errors.As(err, &errShardClosed) || (err == nil && processFinished) { if !t.options.EnableGracefulSyncShutdown() { go t.Stop() return } t.Stop() return } updateAckTimer.Reset(backoff.JitDuration( t.options.UpdateAckInterval(), t.options.UpdateAckIntervalJitterCoefficient(), )) case <-splitQueueTimer.C: t.splitQueue() splitQueueTimer.Reset(backoff.JitDuration( t.options.SplitQueueInterval(), t.options.SplitQueueIntervalJitterCoefficient(), )) case notification := <-t.actionNotifyCh: t.handleActionNotification(notification) } } } func (t *transferQueueProcessorBase) notifyAllQueueCollections() { for _, queueCollection := range t.processingQueueCollections { t.readyForProcess(queueCollection.Level()) } } func (t *transferQueueProcessorBase) processQueueCollections() { for _, queueCollection := range t.processingQueueCollections { level := queueCollection.Level() t.processingLock.Lock() if shouldProcess, ok := t.shouldProcess[level]; !ok || !shouldProcess { t.processingLock.Unlock() continue } t.shouldProcess[level] = false t.processingLock.Unlock() activeQueue := queueCollection.ActiveQueue() if activeQueue == nil { // process for this queue collection has finished // it's possible that new queue will be added to this collection later though, // pollTime will be updated after split/merge continue } readLevel := activeQueue.State().ReadLevel() maxReadLevel := minTaskKey(activeQueue.State().MaxLevel(), t.updateMaxReadLevel()) domainFilter := activeQueue.State().DomainFilter() if !readLevel.Less(maxReadLevel) { // no task need to be processed for now, wait for new task notification // note that if taskID for new task is still less than readLevel, the notification // will just be a no-op and there's no DB requests. continue } ctx, cancel := context.WithTimeout(context.Background(), loadQueueTaskThrottleRetryDelay) if err := t.rateLimiter.Wait(ctx); err != nil { cancel() if level != defaultProcessingQueueLevel { t.setupBackoffTimer(level) } else { t.readyForProcess(level) } continue } cancel() transferTaskInfos, more, err := t.readTasks(readLevel, maxReadLevel) if err != nil { t.logger.Error("Processor unable to retrieve tasks", tag.Error(err)) t.readyForProcess(level) // re-enqueue the event continue } t.logger.Debug("load transfer tasks from database", tag.ReadLevel(readLevel.(transferTaskKey).taskID), tag.MaxLevel(maxReadLevel.(transferTaskKey).taskID), tag.Counter(len(transferTaskInfos))) tasks := make(map[task.Key]task.Task) taskChFull := false now := t.shard.GetTimeSource().Now() for _, taskInfo := range transferTaskInfos { if !domainFilter.Filter(taskInfo.GetDomainID()) { t.logger.Debug("transfer task filtered", tag.TaskID(taskInfo.GetTaskID())) continue } if persistence.IsTaskCorrupted(taskInfo) { t.logger.Error("Processing queue encountered a corrupted task", tag.Dynamic("task", taskInfo)) t.metricsScope.IncCounter(metrics.CorruptedHistoryTaskCounter) continue } task := t.taskInitializer(taskInfo) tasks[newTransferTaskKey(taskInfo.GetTaskID())] = task t.metricsScope.RecordHistogramDuration(metrics.TaskEnqueueToFetchLatency, now.Sub(taskInfo.GetVisibilityTimestamp())) submitted, err := t.submitTask(task) if err != nil { // only err here is due to the fact that processor has been shutdown // return instead of continue return } taskChFull = taskChFull || !submitted } var newReadLevel task.Key if !more { newReadLevel = maxReadLevel } else { newReadLevel = newTransferTaskKey(transferTaskInfos[len(transferTaskInfos)-1].GetTaskID()) } queueCollection.AddTasks(tasks, newReadLevel) if t.validator != nil { t.logger.Debug("ack transfer tasks", tag.ReadLevel(readLevel.(transferTaskKey).taskID), tag.MaxLevel(newReadLevel.(transferTaskKey).taskID), tag.Counter(len(tasks))) t.validator.ackTasks(level, readLevel, newReadLevel, tasks) } newActiveQueue := queueCollection.ActiveQueue() if more || (newActiveQueue != nil && newActiveQueue != activeQueue) { // more tasks for the current active queue or the active queue has changed if level != defaultProcessingQueueLevel && taskChFull { t.setupBackoffTimer(level) } else { t.readyForProcess(level) } } // else it means we don't have tasks to process for now // wait for new task notification // another option for non-default queue is that we can setup a backoff timer to check back later } } func (t *transferQueueProcessorBase) splitQueue() { currentTime := t.shard.GetTimeSource().Now() currentMaxReadLevel := t.updateMaxReadLevel().(transferTaskKey).taskID defer func() { t.lastSplitTime = currentTime t.lastMaxReadLevel = currentMaxReadLevel }() if currentMaxReadLevel-t.lastMaxReadLevel < 2<<(t.shard.GetConfig().RangeSizeBits-1) { // only update the estimation when rangeID is not renewed // note the threshold here is only an estimation. If the read level increased too much // we will drop that data point. numTasksPerMinute := (currentMaxReadLevel - t.lastMaxReadLevel) / int64(currentTime.Sub(t.lastSplitTime).Seconds()) * int64(time.Minute.Seconds()) if t.estimatedTasksPerMinute == 0 { // set the initial value for the estimation t.estimatedTasksPerMinute = numTasksPerMinute } else { t.estimatedTasksPerMinute = int64(numTasksEstimationDecay*float64(t.estimatedTasksPerMinute) + (1-numTasksEstimationDecay)*float64(numTasksPerMinute)) } } if t.lastSplitTime.IsZero() || t.estimatedTasksPerMinute == 0 { // skip the split as we can't estimate the look ahead taskID return } splitPolicy := t.initializeSplitPolicy( func(key task.Key, domainID string) task.Key { totalLookAhead := t.estimatedTasksPerMinute * int64(t.options.SplitLookAheadDurationByDomainID(domainID).Minutes()) // ensure the above calculation doesn't overflow and cap the maximun look ahead interval totalLookAhead = max(min(totalLookAhead, 2<= newAckLevelTaskID": { ackLevel: 10, mockSetup: func(mockShard *shard.TestContext) {}, err: nil, }, "error - ackLevel < newAckLevelTaskID - RangeCompleteTransferTask error": { ackLevel: 1, mockSetup: func(mockShard *shard.TestContext) { mockShard.Resource.ExecutionMgr.On("RangeCompleteHistoryTask", mock.Anything, mock.Anything). Return(&persistence.RangeCompleteHistoryTaskResponse{}, assert.AnError).Once() }, err: assert.AnError, }, "success - ackLevel < newAckLevelTaskID": { ackLevel: 1, mockSetup: func(mockShard *shard.TestContext) { mockShard.Resource.ExecutionMgr.On("RangeCompleteHistoryTask", mock.Anything, mock.Anything). Return(&persistence.RangeCompleteHistoryTaskResponse{}, nil).Once() mockShard.GetShardManager().(*mocks.ShardManager).On("UpdateShard", mock.Anything, mock.Anything).Return(nil).Once() }, }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() processor.ackLevel = tt.ackLevel tt.mockSetup(processor.shard.(*shard.TestContext)) defer processor.Stop() processor.Start() err := processor.completeTransfer() if tt.err != nil { assert.Error(t, err) assert.ErrorIs(t, err, tt.err) } else { assert.NoError(t, err) } }) } } func TestTransferQueueProcessor_completeTransferLoop(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() processor.config.TransferProcessorCompleteTransferInterval = dynamicproperties.GetDurationPropertyFn(10 * time.Millisecond) processor.activeQueueProcessor.Start() for _, standbyQueueProcessor := range processor.standbyQueueProcessors { standbyQueueProcessor.Start() } processor.shard.(*shard.TestContext).Resource.ExecutionMgr.On("RangeCompleteHistoryTask", mock.Anything, mock.Anything). Return(&persistence.RangeCompleteHistoryTaskResponse{}, nil) processor.shard.(*shard.TestContext).GetShardManager().(*mocks.ShardManager).On("UpdateShard", mock.Anything, mock.Anything).Return(nil) processor.shutdownWG.Add(1) go func() { time.Sleep(200 * time.Millisecond) processor.activeQueueProcessor.Stop() for _, standbyQueueProcessor := range processor.standbyQueueProcessors { standbyQueueProcessor.Stop() } close(processor.shutdownChan) common.AwaitWaitGroup(&processor.shutdownWG, time.Minute) }() processor.completeTransferLoop() } func TestTransferQueueProcessor_completeTransferLoop_ErrShardClosed(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() processor.config.TransferProcessorCompleteTransferInterval = dynamicproperties.GetDurationPropertyFn(30 * time.Millisecond) processor.activeQueueProcessor.Start() for _, standbyQueueProcessor := range processor.standbyQueueProcessors { standbyQueueProcessor.Start() } processor.shard.(*shard.TestContext).Resource.ExecutionMgr.On("RangeCompleteHistoryTask", mock.Anything, mock.Anything). Return(&persistence.RangeCompleteHistoryTaskResponse{}, &shard.ErrShardClosed{}).Once() processor.shutdownWG.Add(1) go func() { time.Sleep(50 * time.Millisecond) processor.activeQueueProcessor.Stop() for _, standbyQueueProcessor := range processor.standbyQueueProcessors { standbyQueueProcessor.Stop() } close(processor.shutdownChan) common.AwaitWaitGroup(&processor.shutdownWG, time.Minute) }() processor.completeTransferLoop() } func TestTransferQueueProcessor_completeTransferLoop_ErrShardClosedNotGraceful(t *testing.T) { cfg := config.NewForTest() cfg.QueueProcessorEnableGracefulSyncShutdown = dynamicproperties.GetBoolPropertyFn(false) ctrl, processor := setupTransferQueueProcessor(t, cfg) defer ctrl.Finish() processor.config.TransferProcessorCompleteTransferInterval = dynamicproperties.GetDurationPropertyFn(30 * time.Millisecond) processor.activeQueueProcessor.Start() for _, standbyQueueProcessor := range processor.standbyQueueProcessors { standbyQueueProcessor.Start() } processor.shard.(*shard.TestContext).Resource.ExecutionMgr.On("RangeCompleteHistoryTask", mock.Anything, mock.Anything). Return(&persistence.RangeCompleteHistoryTaskResponse{}, &shard.ErrShardClosed{}).Once() processor.shutdownWG.Add(1) go func() { time.Sleep(50 * time.Millisecond) processor.activeQueueProcessor.Stop() for _, standbyQueueProcessor := range processor.standbyQueueProcessors { standbyQueueProcessor.Stop() } close(processor.shutdownChan) common.AwaitWaitGroup(&processor.shutdownWG, time.Minute) }() processor.completeTransferLoop() } func TestTransferQueueProcessor_completeTransferLoop_OtherError(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() processor.config.TransferProcessorCompleteTransferInterval = dynamicproperties.GetDurationPropertyFn(30 * time.Millisecond) processor.activeQueueProcessor.Start() for _, standbyQueueProcessor := range processor.standbyQueueProcessors { standbyQueueProcessor.Start() } processor.shard.(*shard.TestContext).Resource.ExecutionMgr.On("RangeCompleteHistoryTask", mock.Anything, mock.Anything). Return(&persistence.RangeCompleteHistoryTaskResponse{}, assert.AnError) processor.shutdownWG.Add(1) go func() { time.Sleep(50 * time.Millisecond) processor.activeQueueProcessor.Stop() for _, standbyQueueProcessor := range processor.standbyQueueProcessors { standbyQueueProcessor.Stop() } close(processor.shutdownChan) common.AwaitWaitGroup(&processor.shutdownWG, time.Minute) }() processor.completeTransferLoop() } func Test_transferQueueActiveProcessor_taskFilter(t *testing.T) { tests := map[string]struct { mockSetup func(*shard.TestContext) task persistence.Task err error }{ "noop - domain not registered": { mockSetup: func(testContext *shard.TestContext) { cacheEntry := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{Status: persistence.DomainStatusDeprecated}, nil, true, nil, 1, nil, 0, 0, 0) testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID). Return(cacheEntry, nil).Times(1) }, task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, }, }, err: nil, }, "taskFilter success": { mockSetup: func(testContext *shard.TestContext) { testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID). Return(constants.TestDomainName, nil).Times(1) cacheEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Status: persistence.DomainStatusRegistered}, nil, true, &persistence.DomainReplicationConfig{ ActiveClusterName: constants.TestClusterMetadata.GetCurrentClusterName(), }, 1, nil, 0, 0, 0) testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID). Return(cacheEntry, nil).Times(2) }, task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, }, }, // Error to Execute only. Since taskImpl is not exported, and the filter is a private field, had to use the Execute method to execute the filter function. // The filter returned no error err: &types.BadRequestError{Message: "Can't load workflow execution. WorkflowId not set."}, }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() tt.mockSetup(processor.shard.(*shard.TestContext)) err := processor.activeQueueProcessor.taskInitializer(tt.task).Execute() if tt.err != nil { assert.Error(t, err) assert.ErrorContains(t, err, tt.err.Error()) } else { assert.NoError(t, err) } }) } } func Test_transferQueueActiveProcessor_updateClusterAckLevel(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() taskID := int64(11) key := transferTaskKey{ taskID: taskID, } processor.shard.(*shard.TestContext).GetShardManager().(*mocks.ShardManager).On("UpdateShard", mock.Anything, mock.Anything).Return(nil).Once() err := processor.activeQueueProcessor.processorBase.updateClusterAckLevel(key) assert.NoError(t, err) assert.Equal(t, taskID, processor.shard.(*shard.TestContext).ShardInfo().ClusterTransferAckLevel[constants.TestClusterMetadata.GetCurrentClusterName()]) } func Test_transferQueueActiveProcessor_updateProcessingQueueStates(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() taskID := int64(11) key := transferTaskKey{ taskID: taskID, } state := NewProcessingQueueState(12, key, key, DomainFilter{}) states := []ProcessingQueueState{state} processor.shard.(*shard.TestContext).GetShardManager().(*mocks.ShardManager).On("UpdateShard", mock.Anything, mock.Anything).Return(nil).Once() err := processor.activeQueueProcessor.processorBase.updateProcessingQueueStates(states) assert.NoError(t, err) assert.Equal(t, taskID, processor.shard.(*shard.TestContext).ShardInfo().ClusterTransferAckLevel[constants.TestClusterMetadata.GetCurrentClusterName()]) assert.Equal(t, 1, len(processor.shard.(*shard.TestContext).ShardInfo().TransferProcessingQueueStates.StatesByCluster[constants.TestClusterMetadata.GetCurrentClusterName()])) assert.Equal(t, int32(state.Level()), *processor.shard.(*shard.TestContext).ShardInfo().TransferProcessingQueueStates.StatesByCluster[constants.TestClusterMetadata.GetCurrentClusterName()][0].Level) } func Test_transferQueueActiveProcessor_queueShutdown(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() err := processor.activeQueueProcessor.queueShutdown() assert.NoError(t, err) } func Test_transferQueueStandbyProcessor_taskFilter(t *testing.T) { tests := map[string]struct { mockSetup func(*shard.TestContext) task persistence.Task err error }{ "noop - domain not registered": { mockSetup: func(testContext *shard.TestContext) { cacheEntry := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{Status: persistence.DomainStatusDeprecated}, nil, true, nil, 1, nil, 0, 0, 0) testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID). Return(cacheEntry, nil).Times(1) }, task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, }, }, err: nil, }, "no error - TransferTaskTypeCloseExecution or TransferTaskTypeRecordWorkflowClosed": { mockSetup: func(testContext *shard.TestContext) { testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainName(constants.TestDomainID). Return(constants.TestDomainName, nil).Times(1) cacheEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Status: persistence.DomainStatusRegistered}, nil, true, &persistence.DomainReplicationConfig{ ActiveClusterName: constants.TestClusterMetadata.GetCurrentClusterName(), Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: constants.TestClusterMetadata.GetCurrentClusterName()}, {ClusterName: "standby"}, }, }, 1, nil, 0, 0, 0) testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID). Return(cacheEntry, nil).Times(2) }, task: &persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, }, }, // Error to Execute only. Since taskImpl is not exported, and the filter is a private field, had to use the Execute method to execute the filter function. // The filter returned no error err: &types.BadRequestError{Message: "Can't load workflow execution. WorkflowId not set."}, }, "error - TransferTaskTypeCloseExecution or TransferTaskTypeRecordWorkflowClosed - cannot find domain - retry": { mockSetup: func(testContext *shard.TestContext) { testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID). Return(nil, assert.AnError).Times(2) }, task: &persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, }, }, err: assert.AnError, }, "noop - TransferTaskTypeCloseExecution or TransferTaskTypeRecordWorkflowClosed - EntityNotExistsError": { mockSetup: func(testContext *shard.TestContext) { testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID). Return(nil, &types.EntityNotExistsError{Message: "domain doesn't exist"}).Times(2) }, task: &persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, }, }, err: nil, }, "taskFilter success": { mockSetup: func(testContext *shard.TestContext) { cacheEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Status: persistence.DomainStatusRegistered}, nil, true, &persistence.DomainReplicationConfig{ ActiveClusterName: constants.TestClusterMetadata.GetCurrentClusterName(), }, 1, nil, 0, 0, 0) testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID). Return(cacheEntry, nil).Times(2) }, task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, }, }, err: nil, }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() tt.mockSetup(processor.shard.(*shard.TestContext)) err := processor.standbyQueueProcessors["standby"].taskInitializer(tt.task).Execute() if tt.err != nil { assert.Error(t, err) assert.ErrorContains(t, err, tt.err.Error()) } else { assert.NoError(t, err) } }) } } func Test_transferQueueStandbyProcessor_updateClusterAckLevel(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() taskID := int64(11) key := transferTaskKey{ taskID: taskID, } processor.shard.(*shard.TestContext).GetShardManager().(*mocks.ShardManager).On("UpdateShard", mock.Anything, mock.Anything).Return(nil).Once() err := processor.standbyQueueProcessors["standby"].processorBase.updateClusterAckLevel(key) assert.NoError(t, err) assert.Equal(t, taskID, processor.shard.(*shard.TestContext).ShardInfo().ClusterTransferAckLevel["standby"]) } func Test_transferQueueStandbyProcessor_updateProcessingQueueStates(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() taskID := int64(11) key := transferTaskKey{ taskID: taskID, } state := NewProcessingQueueState(12, key, key, DomainFilter{}) states := []ProcessingQueueState{state} processor.shard.(*shard.TestContext).GetShardManager().(*mocks.ShardManager).On("UpdateShard", mock.Anything, mock.Anything).Return(nil).Once() err := processor.standbyQueueProcessors["standby"].processorBase.updateProcessingQueueStates(states) assert.NoError(t, err) assert.Equal(t, taskID, processor.shard.(*shard.TestContext).ShardInfo().ClusterTransferAckLevel["standby"]) assert.Equal(t, 1, len(processor.shard.(*shard.TestContext).ShardInfo().TransferProcessingQueueStates.StatesByCluster["standby"])) assert.Equal(t, int32(state.Level()), *processor.shard.(*shard.TestContext).ShardInfo().TransferProcessingQueueStates.StatesByCluster["standby"][0].Level) } func Test_transferQueueStandbyProcessor_queueShutdown(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() err := processor.standbyQueueProcessors["standby"].queueShutdown() assert.NoError(t, err) } func Test_transferQueueFailoverProcessor_taskFilter(t *testing.T) { tests := map[string]struct { mockSetup func(*shard.TestContext) task persistence.Task err error }{ "noop - domain not registered": { mockSetup: func(testContext *shard.TestContext) { cacheEntry := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{Status: persistence.DomainStatusDeprecated}, nil, true, nil, 1, nil, 0, 0, 0) testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID). Return(cacheEntry, nil).Times(1) }, task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, }, }, err: nil, }, "taskFilter success": { mockSetup: func(testContext *shard.TestContext) { cacheEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Status: persistence.DomainStatusRegistered}, nil, true, &persistence.DomainReplicationConfig{ ActiveClusterName: constants.TestClusterMetadata.GetCurrentClusterName(), }, 1, nil, 0, 0, 0) testContext.GetDomainCache().(*cache.MockDomainCache).EXPECT().GetDomainByID(constants.TestDomainID). Return(cacheEntry, nil).Times(1) }, task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, }, }, err: nil, }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() tt.mockSetup(processor.shard.(*shard.TestContext)) domainIDs := map[string]struct{}{"standby": {}} _, failoverQueueProcessor := newTransferQueueFailoverProcessor( processor.shard, processor.taskProcessor, processor.taskAllocator, processor.activeTaskExecutor, processor.logger, 0, 10, domainIDs, constants.TestClusterMetadata.GetCurrentClusterName(), ) err := failoverQueueProcessor.taskInitializer(tt.task).Execute() if tt.err != nil { assert.Error(t, err) assert.ErrorContains(t, err, tt.err.Error()) } else { assert.NoError(t, err) } }) } } func Test_transferQueueFailoverProcessor_updateClusterAckLevel(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() taskID := int64(11) key := transferTaskKey{ taskID: taskID, } domainIDs := map[string]struct{}{"standby": {}} updateClusterAckLevel, _ := newTransferQueueFailoverProcessor( processor.shard, processor.taskProcessor, processor.taskAllocator, processor.activeTaskExecutor, processor.logger, 0, 10, domainIDs, constants.TestClusterMetadata.GetCurrentClusterName(), ) err := updateClusterAckLevel(key) assert.NoError(t, err) } func Test_transferQueueFailoverProcessor_queueShutdown(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() domainIDs := map[string]struct{}{"standby": {}} _, failoverQueueProcessor := newTransferQueueFailoverProcessor( processor.shard, processor.taskProcessor, processor.taskAllocator, processor.activeTaskExecutor, processor.logger, 0, 10, domainIDs, constants.TestClusterMetadata.GetCurrentClusterName(), ) err := failoverQueueProcessor.queueShutdown() assert.NoError(t, err) } func Test_loadTransferProcessingQueueStates(t *testing.T) { tests := map[string]struct { enableLoadQueueStates bool clusterName string taskID func(testContext *shard.TestContext) int64 }{ "load queue states true": { enableLoadQueueStates: true, clusterName: constants.TestClusterMetadata.GetCurrentClusterName(), taskID: func(testContext *shard.TestContext) int64 { return testContext.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryTransfer, constants.TestClusterMetadata.GetCurrentClusterName()).GetTaskID() }, }, "load queue states false": { enableLoadQueueStates: false, clusterName: "standby", taskID: func(testContext *shard.TestContext) int64 { return testContext.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryTransfer, "standby").GetTaskID() }, }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { ctrl, processor := setupTransferQueueProcessor(t, nil) defer ctrl.Finish() opts := &queueProcessorOptions{ EnableLoadQueueStates: func(opts ...dynamicproperties.FilterOption) bool { return tt.enableLoadQueueStates }, } pqs := loadTransferProcessingQueueStates(tt.clusterName, processor.shard.(*shard.TestContext), opts, processor.shard.(*shard.TestContext).GetLogger()) assert.NotNil(t, pqs) assert.Equal(t, 1, len(pqs)) assert.Equal(t, tt.taskID(processor.shard.(*shard.TestContext)), pqs[0].AckLevel().(transferTaskKey).taskID) }) } } ================================================ FILE: service/history/queue/transfer_queue_validator.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "fmt" "strings" "sync" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/task" ) const ( defaultMaxPendingTasksSize = 5000 ) type ( pendingTaskInfo struct { executionInfo *persistence.WorkflowExecutionInfo task persistence.Task potentialFalsePositive bool } transferQueueValidator struct { sync.Mutex processor *transferQueueProcessorBase timeSource clock.TimeSource logger log.Logger metricsScope metrics.Scope pendingTaskInfos map[int64]pendingTaskInfo maxReadLevels map[int]task.Key minReadTaskID int64 lastValidateTime time.Time validationInterval dynamicproperties.DurationPropertyFn } ) func newTransferQueueValidator( processor *transferQueueProcessorBase, validationInterval dynamicproperties.DurationPropertyFn, logger log.Logger, metricsScope metrics.Scope, ) *transferQueueValidator { timeSource := processor.shard.GetTimeSource() return &transferQueueValidator{ processor: processor, timeSource: timeSource, logger: logger, metricsScope: metricsScope, pendingTaskInfos: make(map[int64]pendingTaskInfo), maxReadLevels: make(map[int]task.Key), minReadTaskID: 0, lastValidateTime: timeSource.Now(), validationInterval: validationInterval, } } func (v *transferQueueValidator) addTasks(info *hcommon.NotifyTaskInfo) { v.Lock() defer v.Unlock() numTaskToAdd := len(info.Tasks) if numTaskToAdd+len(v.pendingTaskInfos) > defaultMaxPendingTasksSize { numTaskToAdd = defaultMaxPendingTasksSize - len(v.pendingTaskInfos) var taskDump strings.Builder droppedTasks := info.Tasks[numTaskToAdd:] for _, task := range droppedTasks { taskDump.WriteString(fmt.Sprintf("%+v\n", task)) } v.logger.Warn( "Too many pending transfer tasks, dropping new tasks", tag.WorkflowDomainID(info.ExecutionInfo.DomainID), tag.WorkflowID(info.ExecutionInfo.WorkflowID), tag.WorkflowRunID(info.ExecutionInfo.RunID), tag.Key("dropped-transfer-tasks"), tag.Value(taskDump.String()), ) v.metricsScope.AddCounter(metrics.QueueValidatorDropTaskCounter, int64(len(droppedTasks))) } for _, task := range info.Tasks[:numTaskToAdd] { // It is possible that a task is acked before it is added to the validator // In that case, the lost task could be a potential false positive case potentialFalsePositive := info.PersistenceError || task.GetTaskID() <= v.minReadTaskID v.pendingTaskInfos[task.GetTaskID()] = pendingTaskInfo{ executionInfo: info.ExecutionInfo, task: task, potentialFalsePositive: potentialFalsePositive, } } } func (v *transferQueueValidator) ackTasks( queueLevel int, readLevel task.Key, maxReadLevel task.Key, loadedTasks map[task.Key]task.Task, ) { v.Lock() defer v.Unlock() for _, task := range loadedTasks { // note that loadedTasks will contain tasks not in pendingTaskInfos // either due to the retries when updating mutable state or the fact that we // have two processors for the same queue in DB delete(v.pendingTaskInfos, task.GetTaskID()) } if queueLevel == defaultProcessingQueueLevel { if expectedReadLevel, ok := v.maxReadLevels[queueLevel]; ok && expectedReadLevel.Less(readLevel) { // TODO: implement an event logger for queue processor and dump all events when this validation fails. v.logger.Error("Transfer queue processor load request is not continuous") v.metricsScope.IncCounter(metrics.QueueValidatorInvalidLoadCounter) } } v.maxReadLevels[queueLevel] = maxReadLevel if v.timeSource.Now().After(v.lastValidateTime.Add(v.validationInterval())) { v.validatePendingTasks() v.lastValidateTime = v.timeSource.Now() } } func (v *transferQueueValidator) validatePendingTasks() { v.metricsScope.IncCounter(metrics.QueueValidatorValidationCounter) // first find the minimal read level across all processing queue levels minReadLevel := maximumTransferTaskKey for _, queueCollection := range v.processor.processingQueueCollections { if activeQueue := queueCollection.ActiveQueue(); activeQueue != nil { minReadLevel = minTaskKey(minReadLevel, activeQueue.State().ReadLevel()) } } // all pending tasks with taskID <= minReadLevel will never be loaded, // log those tasks, emit metrics, and delete them from pending tasks // // NOTE: this may contain false positives as when the persistence operation for // updating workflow execution times out, task notification will still be sent, // but those tasks may not be persisted. // // As a result, when lost task metric is emitted, first check if there's corresponding // persistence operation errors. minReadTaskID := minReadLevel.(transferTaskKey).taskID for taskID, taskInfo := range v.pendingTaskInfos { if taskID <= minReadTaskID { v.logger.Error("Failed to load transfer task", tag.TaskID(taskID), tag.TaskVisibilityTimestamp(taskInfo.task.GetVisibilityTimestamp().UnixNano()), tag.FailoverVersion(taskInfo.task.GetVersion()), tag.TaskType(taskInfo.task.GetTaskType()), tag.WorkflowDomainID(taskInfo.executionInfo.DomainID), tag.WorkflowID(taskInfo.executionInfo.WorkflowID), tag.WorkflowRunID(taskInfo.executionInfo.RunID), tag.Bool(taskInfo.potentialFalsePositive), ) v.metricsScope.IncCounter(metrics.QueueValidatorLostTaskCounter) delete(v.pendingTaskInfos, taskID) } } v.minReadTaskID = minReadTaskID } ================================================ FILE: service/history/queue/transfer_queue_validator_test.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queue import ( "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/persistence" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) type ( transferQueueValidatorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockLogger *log.MockLogger mockMetricScope *mocks.Scope processor *transferQueueProcessorBase validator *transferQueueValidator } ) const ( testValidationInterval = 100 * time.Millisecond ) func TestTransferQueueValidatorSuite(t *testing.T) { s := new(transferQueueValidatorSuite) suite.Run(t, s) } func (s *transferQueueValidatorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.mockLogger = log.NewMockLogger(gomock.NewController(s.T())) s.mockMetricScope = &mocks.Scope{} s.processor = &transferQueueProcessorBase{ processorBase: &processorBase{ shard: s.mockShard, processingQueueCollections: newProcessingQueueCollections( []ProcessingQueueState{ NewProcessingQueueState( defaultProcessingQueueLevel, newTransferTaskKey(0), maximumTransferTaskKey, NewDomainFilter(map[string]struct{}{}, true), ), }, nil, nil, ), }, } s.validator = newTransferQueueValidator( s.processor, dynamicproperties.GetDurationPropertyFn(testValidationInterval), s.mockLogger, s.mockMetricScope, ) } func (s *transferQueueValidatorSuite) TearDownTest() { s.controller.Finish() s.mockMetricScope.AssertExpectations(s.T()) } func (s *transferQueueValidatorSuite) TestAddTasks_NoTaskDropped() { executionInfo := &persistence.WorkflowExecutionInfo{} tasks := []persistence.Task{ &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 0}}, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 1}}, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 2}}, } expectedPendingTasksLen := len(tasks) s.validator.addTasks(&hcommon.NotifyTaskInfo{ExecutionInfo: executionInfo, Tasks: tasks, PersistenceError: false}) s.Len(s.validator.pendingTaskInfos, expectedPendingTasksLen) tasks = []persistence.Task{ &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 4}}, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 5}}, } expectedPendingTasksLen += len(tasks) s.validator.addTasks(&hcommon.NotifyTaskInfo{ExecutionInfo: executionInfo, Tasks: tasks, PersistenceError: false}) s.Len(s.validator.pendingTaskInfos, expectedPendingTasksLen) } func (s *transferQueueValidatorSuite) TestAddTasks_TaskDropped() { executionInfo := &persistence.WorkflowExecutionInfo{} tasks := []persistence.Task{ &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 0}}, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 1}}, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 2}}, } expectedPendingTasksLen := len(tasks) s.validator.addTasks(&hcommon.NotifyTaskInfo{ExecutionInfo: executionInfo, Tasks: tasks, PersistenceError: false}) s.Len(s.validator.pendingTaskInfos, expectedPendingTasksLen) tasks = []persistence.Task{} for i := 0; i != defaultMaxPendingTasksSize; i++ { tasks = append(tasks, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: int64(i + expectedPendingTasksLen)}}) } numDroppedTasks := expectedPendingTasksLen + len(tasks) - defaultMaxPendingTasksSize s.mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) s.mockMetricScope.On("AddCounter", metrics.QueueValidatorDropTaskCounter, int64(numDroppedTasks)).Times(1) s.validator.addTasks(&hcommon.NotifyTaskInfo{ExecutionInfo: executionInfo, Tasks: tasks, PersistenceError: false}) s.Len(s.validator.pendingTaskInfos, defaultMaxPendingTasksSize) } func (s *transferQueueValidatorSuite) TestAckTasks_NoTaskLost() { executionInfo := &persistence.WorkflowExecutionInfo{} pendingTasks := []persistence.Task{ &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 0}}, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 1}}, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 2}}, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 100}}, } s.validator.addTasks(&hcommon.NotifyTaskInfo{ExecutionInfo: executionInfo, Tasks: pendingTasks, PersistenceError: false}) loadedTasks := make(map[task.Key]task.Task, len(pendingTasks)) for _, pendingTask := range pendingTasks[:len(pendingTasks)-1] { loadedTasks[newTransferTaskKey(pendingTask.GetTaskID())] = task.NewHistoryTask( s.mockShard, &persistence.DecisionTask{ TaskData: persistence.TaskData{ TaskID: pendingTask.GetTaskID(), }, }, task.QueueTypeActiveTransfer, nil, nil, nil, nil, nil, nil, ) } time.Sleep(testValidationInterval) s.mockMetricScope.On("IncCounter", metrics.QueueValidatorValidationCounter).Times(1) readLevel := newTransferTaskKey(0) maxReadLevel := newTransferTaskKey(10) s.processor.processingQueueCollections[0].ActiveQueue().State().(*processingQueueStateImpl).readLevel = maxReadLevel s.validator.ackTasks(defaultProcessingQueueLevel, readLevel, maxReadLevel, loadedTasks) s.Len(s.validator.pendingTaskInfos, 1) } func (s *transferQueueValidatorSuite) TestAckTasks_TaskLost() { executionInfo := &persistence.WorkflowExecutionInfo{} pendingTasks := []persistence.Task{ &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 0}}, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 1}}, &persistence.DecisionTask{TaskData: persistence.TaskData{TaskID: 2}}, } s.validator.addTasks(&hcommon.NotifyTaskInfo{ExecutionInfo: executionInfo, Tasks: pendingTasks, PersistenceError: false}) loadedTasks := make(map[task.Key]task.Task, len(pendingTasks)) for _, pendingTask := range pendingTasks[1:] { loadedTasks[newTransferTaskKey(pendingTask.GetTaskID())] = task.NewHistoryTask( s.mockShard, &persistence.DecisionTask{ TaskData: persistence.TaskData{ TaskID: pendingTask.GetTaskID(), }, }, task.QueueTypeActiveTransfer, nil, nil, nil, nil, nil, nil, ) } time.Sleep(testValidationInterval) s.mockLogger.EXPECT().Error(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) s.mockMetricScope.On("IncCounter", metrics.QueueValidatorValidationCounter).Times(1) s.mockMetricScope.On("IncCounter", metrics.QueueValidatorLostTaskCounter).Times(1) readLevel := newTransferTaskKey(0) maxReadLevel := newTransferTaskKey(10) s.processor.processingQueueCollections[0].ActiveQueue().State().(*processingQueueStateImpl).readLevel = maxReadLevel s.validator.ackTasks(defaultProcessingQueueLevel, readLevel, maxReadLevel, loadedTasks) s.Empty(s.validator.pendingTaskInfos) } func (s *transferQueueValidatorSuite) TestAckTasks_LostRequestNotContinuous() { readLevel := newTransferTaskKey(0) maxReadLevel := newTransferTaskKey(10) s.validator.ackTasks(defaultProcessingQueueLevel, readLevel, maxReadLevel, nil) readLevel = newTransferTaskKey(10) maxReadLevel = newTransferTaskKey(15) s.validator.ackTasks(defaultProcessingQueueLevel, readLevel, maxReadLevel, nil) s.mockLogger.EXPECT().Error(gomock.Any(), gomock.Any()).Times(1) s.mockMetricScope.On("IncCounter", metrics.QueueValidatorInvalidLoadCounter).Times(1) readLevel = newTransferTaskKey(16) maxReadLevel = newTransferTaskKey(25) s.validator.ackTasks(defaultProcessingQueueLevel, readLevel, maxReadLevel, nil) } ================================================ FILE: service/history/queuev2/alert.go ================================================ package queuev2 type ( // Alert is created by a Monitor when some statistics of the Queue is abnormal Alert struct { AlertType AlertType AlertAttributesQueuePendingTaskCount *AlertAttributesQueuePendingTaskCount } AlertType int AlertAttributesQueuePendingTaskCount struct { CurrentPendingTaskCount int CriticalPendingTaskCount int } ) const ( AlertTypeUnspecified AlertType = iota AlertTypeQueuePendingTaskCount ) ================================================ FILE: service/history/queuev2/convert.go ================================================ package queuev2 import ( "fmt" "slices" "time" "golang.org/x/exp/maps" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func FromPersistenceQueueState(state *types.QueueState) *QueueState { virtualQueueStates := make(map[int64][]VirtualSliceState) for k, v := range state.VirtualQueueStates { virtualQueueStates[k] = FromPersistenceVirtualQueueState(v) } return &QueueState{ VirtualQueueStates: virtualQueueStates, ExclusiveMaxReadLevel: FromPersistenceTaskKey(state.ExclusiveMaxReadLevel), } } func ToPersistenceQueueState(state *QueueState) *types.QueueState { virtualQueueStates := make(map[int64]*types.VirtualQueueState) for k, v := range state.VirtualQueueStates { virtualQueueStates[k] = ToPersistenceVirtualQueueState(v) } return &types.QueueState{ VirtualQueueStates: virtualQueueStates, ExclusiveMaxReadLevel: ToPersistenceTaskKey(state.ExclusiveMaxReadLevel), } } func FromPersistenceVirtualQueueState(state *types.VirtualQueueState) []VirtualSliceState { states := make([]VirtualSliceState, 0, len(state.VirtualSliceStates)) for _, v := range state.VirtualSliceStates { states = append(states, FromPersistenceVirtualSliceState(v)) } return states } func ToPersistenceVirtualQueueState(state []VirtualSliceState) *types.VirtualQueueState { states := make([]*types.VirtualSliceState, 0, len(state)) for _, v := range state { states = append(states, ToPersistenceVirtualSliceState(v)) } return &types.VirtualQueueState{ VirtualSliceStates: states, } } func FromPersistenceVirtualSliceState(state *types.VirtualSliceState) VirtualSliceState { return VirtualSliceState{ Range: FromPersistenceTaskRange(state.TaskRange), Predicate: FromPersistencePredicate(state.Predicate), } } func ToPersistenceVirtualSliceState(state VirtualSliceState) *types.VirtualSliceState { return &types.VirtualSliceState{ TaskRange: ToPersistenceTaskRange(state.Range), Predicate: ToPersistencePredicate(state.Predicate), } } func FromPersistenceTaskRange(state *types.TaskRange) Range { return Range{ InclusiveMinTaskKey: FromPersistenceTaskKey(state.InclusiveMin), ExclusiveMaxTaskKey: FromPersistenceTaskKey(state.ExclusiveMax), } } func ToPersistenceTaskRange(r Range) *types.TaskRange { return &types.TaskRange{ InclusiveMin: ToPersistenceTaskKey(r.InclusiveMinTaskKey), ExclusiveMax: ToPersistenceTaskKey(r.ExclusiveMaxTaskKey), } } func FromPersistenceTaskKey(key *types.TaskKey) persistence.HistoryTaskKey { return persistence.NewHistoryTaskKey(time.Unix(0, key.ScheduledTimeNano).UTC(), key.TaskID) } func ToPersistenceTaskKey(key persistence.HistoryTaskKey) *types.TaskKey { return &types.TaskKey{ TaskID: key.GetTaskID(), ScheduledTimeNano: key.GetScheduledTime().UnixNano(), } } func FromPersistencePredicate(predicate *types.Predicate) Predicate { if predicate == nil { return NewUniversalPredicate() } switch predicate.PredicateType { case types.PredicateTypeUniversal: return NewUniversalPredicate() case types.PredicateTypeEmpty: return NewEmptyPredicate() case types.PredicateTypeDomainID: return NewDomainIDPredicate(predicate.GetDomainIDPredicateAttributes().DomainIDs, predicate.GetDomainIDPredicateAttributes().GetIsExclusive()) default: panic(fmt.Sprintf("unknown predicate type: %v", predicate.PredicateType)) } } func ToPersistencePredicate(predicate Predicate) *types.Predicate { switch p := predicate.(type) { case *universalPredicate: return &types.Predicate{PredicateType: types.PredicateTypeUniversal, UniversalPredicateAttributes: &types.UniversalPredicateAttributes{}} case *emptyPredicate: return &types.Predicate{PredicateType: types.PredicateTypeEmpty, EmptyPredicateAttributes: &types.EmptyPredicateAttributes{}} case *domainIDPredicate: domainIDs := maps.Keys(p.domainIDs) slices.Sort(domainIDs) return &types.Predicate{PredicateType: types.PredicateTypeDomainID, DomainIDPredicateAttributes: &types.DomainIDPredicateAttributes{DomainIDs: domainIDs, IsExclusive: &p.isExclusive}} default: panic(fmt.Sprintf("unknown predicate type: %T", p)) } } ================================================ FILE: service/history/queuev2/convert_test.go ================================================ package queuev2 import ( "math/rand" "sort" "testing" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" ) func TestConvertTaskKey(t *testing.T) { f := fuzz.New() for i := 0; i < 1000; i++ { var key types.TaskKey f.Fuzz(&key) persistenceKey := FromPersistenceTaskKey(&key) convertedKey := ToPersistenceTaskKey(persistenceKey) assert.Equal(t, key, *convertedKey) } } func TestConvertTaskRange(t *testing.T) { f := fuzz.New().NilChance(0) for i := 0; i < 1000; i++ { var r types.TaskRange f.Fuzz(&r) persistenceRange := FromPersistenceTaskRange(&r) convertedRange := ToPersistenceTaskRange(persistenceRange) assert.Equal(t, r, *convertedRange) } } func predicateFuzzGenerator(t *types.Predicate, c fuzz.Continue) { switch c.Intn(int(types.NumPredicateTypes)) { case 0: t.PredicateType = types.PredicateTypeUniversal c.Fuzz(&t.UniversalPredicateAttributes) case 1: t.PredicateType = types.PredicateTypeEmpty c.Fuzz(&t.EmptyPredicateAttributes) case 2: t.PredicateType = types.PredicateTypeDomainID c.Fuzz(&t.DomainIDPredicateAttributes) default: panic("invalid predicate type") } } func domainIDPredicateAttributesFuzzGenerator(t *types.DomainIDPredicateAttributes, c fuzz.Continue) { const maxCount = 10 // adjust as needed count := rand.Intn(maxCount) + 1 seen := make(map[string]struct{}) t.DomainIDs = make([]string, 0, count) for len(t.DomainIDs) < count { var s string c.Fuzz(&s) if s != "" && len(s) >= 3 { if _, exists := seen[s]; !exists { seen[s] = struct{}{} t.DomainIDs = append(t.DomainIDs, s) } } } sort.Strings(t.DomainIDs) var b bool c.Fuzz(&b) t.IsExclusive = &b } func TestConvertVirtualSliceState(t *testing.T) { f := fuzz.New().NilChance(0).Funcs(predicateFuzzGenerator, domainIDPredicateAttributesFuzzGenerator) for i := 0; i < 1000; i++ { var s types.VirtualSliceState f.Fuzz(&s) persistenceState := FromPersistenceVirtualSliceState(&s) convertedState := ToPersistenceVirtualSliceState(persistenceState) assert.Equal(t, s, *convertedState) } } func TestConvertVirtualQueueState(t *testing.T) { f := fuzz.New().NilChance(0).Funcs(predicateFuzzGenerator, domainIDPredicateAttributesFuzzGenerator) for i := 0; i < 1000; i++ { var s types.VirtualQueueState f.Fuzz(&s) persistenceState := FromPersistenceVirtualQueueState(&s) convertedState := ToPersistenceVirtualQueueState(persistenceState) assert.Equal(t, s, *convertedState) } } func TestConvertQueueState(t *testing.T) { f := fuzz.New().NilChance(0).Funcs(predicateFuzzGenerator, domainIDPredicateAttributesFuzzGenerator) for i := 0; i < 1000; i++ { var s types.QueueState f.Fuzz(&s) persistenceState := FromPersistenceQueueState(&s) convertedState := ToPersistenceQueueState(persistenceState) assert.Equal(t, s, *convertedState) } } ================================================ FILE: service/history/queuev2/interface.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queuev2 import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/persistence" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/queue" ) type ( Queue interface { common.Daemon Category() persistence.HistoryTaskCategory NotifyNewTask(string, *hcommon.NotifyTaskInfo) FailoverDomain(map[string]struct{}) HandleAction(context.Context, string, *queue.Action) (*queue.ActionResult, error) LockTaskProcessing() UnlockTaskProcessing() } ) ================================================ FILE: service/history/queuev2/mitigator.go ================================================ //go:generate mockgen -package $GOPACKAGE -destination mitigator_mock.go github.com/uber/cadence/service/history/queuev2 Mitigator package queuev2 import ( "maps" "slices" "time" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) const ( targetLoadFactor = 0.8 clearSliceThrottleDuration = 10 * time.Second ) type ( Mitigator interface { Mitigate(Alert) } MitigatorOptions struct { MaxVirtualQueueCount dynamicproperties.IntPropertyFn } mitigatorImpl struct { virtualQueueManager VirtualQueueManager monitor Monitor logger log.Logger metricsScope metrics.Scope options *MitigatorOptions handlers map[AlertType]func(Alert) } pendingTaskStats struct { totalPendingTaskCount int pendingTaskCountPerDomain map[string]int pendingTaskCountPerDomainPerSlice map[VirtualSlice]map[string]int slicesPerDomain map[string][]VirtualSlice } ) func NewMitigator( virtualQueueManager VirtualQueueManager, monitor Monitor, logger log.Logger, metricsScope metrics.Scope, options *MitigatorOptions, ) Mitigator { m := &mitigatorImpl{ virtualQueueManager: virtualQueueManager, monitor: monitor, logger: logger, metricsScope: metricsScope, options: options, } m.handlers = map[AlertType]func(Alert){ AlertTypeQueuePendingTaskCount: m.handleQueuePendingTaskCount, } return m } func (m *mitigatorImpl) Mitigate(alert Alert) { handler, ok := m.handlers[alert.AlertType] if !ok { m.logger.Error("unknown queue alert type", tag.AlertType(int(alert.AlertType))) return } handler(alert) m.monitor.ResolveAlert(alert.AlertType) m.logger.Info("mitigated queue alert", tag.AlertType(int(alert.AlertType))) } func (m *mitigatorImpl) handleQueuePendingTaskCount(alert Alert) { // First, try cleaning up tasks that has already been acknowledged to see if we can reduce the pending task count virtualQueues := m.virtualQueueManager.VirtualQueues() for _, virtualQueue := range virtualQueues { virtualQueue.UpdateAndGetState() } if m.monitor.GetTotalPendingTaskCount() <= alert.AlertAttributesQueuePendingTaskCount.CriticalPendingTaskCount { m.logger.Debug("mitigating queue alert, skip mitigation because the alert is no longer valid") return } // Second, getting the stats of pending tasks. We need: stats := m.collectPendingTaskStats() // Third, find virtual slices to split given the target pending task count and the stats of pending tasks targetPendingTaskCount := int(float64(alert.AlertAttributesQueuePendingTaskCount.CriticalPendingTaskCount) * targetLoadFactor) if m.logger.DebugOn() { sliceStatesPerDomain := make(map[string][]*types.VirtualSliceState) for domain, slices := range stats.slicesPerDomain { for _, s := range slices { sliceStatesPerDomain[domain] = append(sliceStatesPerDomain[domain], ToPersistenceVirtualSliceState(s.GetState())) } } for s, domainStats := range stats.pendingTaskCountPerDomainPerSlice { m.logger.Debug("mitigating queue alert, get task stats per slice", tag.Dynamic("slice", ToPersistenceVirtualSliceState(s.GetState())), tag.Dynamic("domain-stats", domainStats)) } m.logger.Debug("mitigating queue alert, get task stats", tag.AlertType(int(alert.AlertType)), tag.Dynamic("pending-task-count-per-domain", stats.pendingTaskCountPerDomain), tag.Dynamic("slices-per-domain", sliceStatesPerDomain), tag.Dynamic("pending-task-count", stats.totalPendingTaskCount), tag.Dynamic("target-task-count", targetPendingTaskCount), ) } domainsToClearPerSlice := m.findDomainsToClear(stats, targetPendingTaskCount) if m.logger.DebugOn() { for s, domains := range domainsToClearPerSlice { m.logger.Debug("mitigating queue alert, get domains to clear", tag.Dynamic("slice", ToPersistenceVirtualSliceState(s.GetState())), tag.WorkflowDomainIDs(domains)) } } // Finally, split and clear the slices m.processQueueSplitsAndClear(virtualQueues, domainsToClearPerSlice) if m.logger.DebugOn() { virtualQueues := m.virtualQueueManager.VirtualQueues() state := make(map[int64]*types.VirtualQueueState) for queueID, vq := range virtualQueues { state[queueID] = ToPersistenceVirtualQueueState(vq.GetState()) } m.logger.Debug("mitigating queue alert, get queue state after mitigation", tag.Dynamic("queue-state", state)) } } // The stats of pending tasks are used to calculate the domains to clear. We need: // 1. The total number of pending tasks per domain // 2. The number of pending tasks per domain per slice // 3. The slices that contains the tasks for each domain func (m *mitigatorImpl) collectPendingTaskStats() pendingTaskStats { stats := pendingTaskStats{ pendingTaskCountPerDomain: make(map[string]int), pendingTaskCountPerDomainPerSlice: make(map[VirtualSlice]map[string]int), slicesPerDomain: make(map[string][]VirtualSlice), } for _, virtualQueue := range m.virtualQueueManager.VirtualQueues() { virtualQueue.IterateSlices(func(slice VirtualSlice) { perDomain := slice.PendingTaskStats().PendingTaskCountPerDomain stats.pendingTaskCountPerDomainPerSlice[slice] = perDomain for domain, count := range perDomain { stats.totalPendingTaskCount += count stats.pendingTaskCountPerDomain[domain] += count stats.slicesPerDomain[domain] = append(stats.slicesPerDomain[domain], slice) } }) } for _, slicesList := range stats.slicesPerDomain { slices.SortFunc(slicesList, func(a, b VirtualSlice) int { return b.GetState().Range.InclusiveMinTaskKey.Compare(a.GetState().Range.InclusiveMinTaskKey) }) } return stats } func (m *mitigatorImpl) findDomainsToClear(stats pendingTaskStats, targetCount int) map[VirtualSlice][]string { domainsToClear := make(map[VirtualSlice][]string) pq := collection.NewPriorityQueue( func(a, b string) bool { return stats.pendingTaskCountPerDomain[a] > stats.pendingTaskCountPerDomain[b] }, slices.Collect(maps.Keys(stats.pendingTaskCountPerDomain))..., ) for stats.totalPendingTaskCount > targetCount && !pq.IsEmpty() { domain, err := pq.Remove() if err != nil { // this should never happen because we check the priority queue is not empty before calling Remove // but just want to be future proof m.logger.Error("failed to remove domain from priority queue with unexpected error", tag.Error(err)) panic(err) } if len(stats.slicesPerDomain[domain]) == 0 { continue } slice := stats.slicesPerDomain[domain][0] stats.slicesPerDomain[domain] = stats.slicesPerDomain[domain][1:] taskCount := stats.pendingTaskCountPerDomainPerSlice[slice][domain] stats.totalPendingTaskCount -= taskCount stats.pendingTaskCountPerDomain[domain] -= taskCount if stats.pendingTaskCountPerDomain[domain] > 0 { pq.Add(domain) } domainsToClear[slice] = append(domainsToClear[slice], domain) } return domainsToClear } func (m *mitigatorImpl) processQueueSplitsAndClear(virtualQueues map[int64]VirtualQueue, domainsToClear map[VirtualSlice][]string) { maxQueueID := m.options.MaxVirtualQueueCount() - 1 for queueID, vq := range virtualQueues { if queueID >= int64(maxQueueID) { // Clear slices in the last queue cleared := false vq.ClearSlices(func(slice VirtualSlice) bool { _, ok := domainsToClear[slice] cleared = cleared || ok return ok }) if cleared { vq.Pause(clearSliceThrottleDuration) } continue } var slicesToMove []VirtualSlice vq.SplitSlices(func(slice VirtualSlice) ([]VirtualSlice, bool) { domains := domainsToClear[slice] if len(domains) == 0 { return nil, false } predicate := NewDomainIDPredicate(domains, false) splitSlice, remainingSlice, ok := slice.TrySplitByPredicate(predicate) if !ok { slice.Clear() slicesToMove = append(slicesToMove, slice) return nil, true } splitSlice.Clear() slicesToMove = append(slicesToMove, splitSlice) return []VirtualSlice{remainingSlice}, true }) if len(slicesToMove) > 0 { nextQueue := m.virtualQueueManager.GetOrCreateVirtualQueue(queueID + 1) nextQueue.Pause(clearSliceThrottleDuration) nextQueue.MergeSlices(slicesToMove...) } } } ================================================ FILE: service/history/queuev2/mitigator_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/queuev2 (interfaces: Mitigator) // // Generated by this command: // // mockgen -package queuev2 -destination mitigator_mock.go github.com/uber/cadence/service/history/queuev2 Mitigator // // Package queuev2 is a generated GoMock package. package queuev2 import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockMitigator is a mock of Mitigator interface. type MockMitigator struct { ctrl *gomock.Controller recorder *MockMitigatorMockRecorder isgomock struct{} } // MockMitigatorMockRecorder is the mock recorder for MockMitigator. type MockMitigatorMockRecorder struct { mock *MockMitigator } // NewMockMitigator creates a new mock instance. func NewMockMitigator(ctrl *gomock.Controller) *MockMitigator { mock := &MockMitigator{ctrl: ctrl} mock.recorder = &MockMitigatorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMitigator) EXPECT() *MockMitigatorMockRecorder { return m.recorder } // Mitigate mocks base method. func (m *MockMitigator) Mitigate(arg0 Alert) { m.ctrl.T.Helper() m.ctrl.Call(m, "Mitigate", arg0) } // Mitigate indicates an expected call of Mitigate. func (mr *MockMitigatorMockRecorder) Mitigate(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Mitigate", reflect.TypeOf((*MockMitigator)(nil).Mitigate), arg0) } ================================================ FILE: service/history/queuev2/mitigator_test.go ================================================ package queuev2 import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) func TestNewMitigator(t *testing.T) { ctrl := gomock.NewController(t) mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockMonitor := NewMockMonitor(ctrl) logger := testlogger.New(t) metricsScope := metrics.NoopScope options := &MitigatorOptions{ MaxVirtualQueueCount: dynamicproperties.GetIntPropertyFn(10), } mitigator := NewMitigator( mockVirtualQueueManager, mockMonitor, logger, metricsScope, options, ) require.NotNil(t, mitigator) // Verify internal structure impl, ok := mitigator.(*mitigatorImpl) require.True(t, ok) assert.Equal(t, mockMonitor, impl.monitor) assert.Equal(t, logger, impl.logger) assert.Equal(t, metricsScope, impl.metricsScope) assert.Equal(t, options, impl.options) // Verify handlers are properly initialized assert.NotNil(t, impl.handlers) assert.Len(t, impl.handlers, 1) _, exists := impl.handlers[AlertTypeQueuePendingTaskCount] assert.True(t, exists) } func TestMitigator_Mitigate_KnownAlertType(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockMonitor := NewMockMonitor(ctrl) logger := testlogger.New(t) metricsScope := metrics.NoopScope options := &MitigatorOptions{ MaxVirtualQueueCount: dynamicproperties.GetIntPropertyFn(10), } mitigator := NewMitigator( mockVirtualQueueManager, mockMonitor, logger, metricsScope, options, ) impl, ok := mitigator.(*mitigatorImpl) require.True(t, ok) handlerCalled := false impl.handlers[AlertTypeQueuePendingTaskCount] = func(alert Alert) { handlerCalled = true } alert := Alert{ AlertType: AlertTypeQueuePendingTaskCount, AlertAttributesQueuePendingTaskCount: &AlertAttributesQueuePendingTaskCount{ CurrentPendingTaskCount: 150, CriticalPendingTaskCount: 100, }, } // Expect ResolveAlert to be called on the monitor mockMonitor.EXPECT().ResolveAlert(AlertTypeQueuePendingTaskCount).Times(1) mitigator.Mitigate(alert) assert.True(t, handlerCalled) } func TestMitigator_Mitigate_UnknownAlertType(t *testing.T) { ctrl := gomock.NewController(t) mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockMonitor := NewMockMonitor(ctrl) logger := testlogger.New(t) metricsScope := metrics.NoopScope options := &MitigatorOptions{ MaxVirtualQueueCount: dynamicproperties.GetIntPropertyFn(10), } mitigator := NewMitigator( mockVirtualQueueManager, mockMonitor, logger, metricsScope, options, ) // Create an alert with an unknown/unhandled alert type unknownAlertType := AlertType(999) alert := Alert{ AlertType: unknownAlertType, } mitigator.Mitigate(alert) } func TestMitigator_collectPendingTaskStats(t *testing.T) { tests := []struct { name string setupMocks func(*gomock.Controller) (*MockVirtualQueueManager, map[string][]VirtualSlice, map[VirtualSlice]map[string]int) expectedTotalPendingTaskCount int expectedPendingTaskCountPerDomain map[string]int expectedSlicesPerDomainLength map[string]int validateResults func(*testing.T, pendingTaskStats, map[string][]VirtualSlice, map[VirtualSlice]map[string]int) }{ { name: "empty virtual queues", setupMocks: func(ctrl *gomock.Controller) (*MockVirtualQueueManager, map[string][]VirtualSlice, map[VirtualSlice]map[string]int) { mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockVirtualQueueManager.EXPECT().VirtualQueues().Return(map[int64]VirtualQueue{}).Times(1) return mockVirtualQueueManager, map[string][]VirtualSlice{}, map[VirtualSlice]map[string]int{} }, expectedTotalPendingTaskCount: 0, expectedPendingTaskCountPerDomain: map[string]int{}, expectedSlicesPerDomainLength: map[string]int{}, validateResults: func(t *testing.T, stats pendingTaskStats, expectedSlicesPerDomain map[string][]VirtualSlice, expectedPendingTaskCountPerDomainPerSlice map[VirtualSlice]map[string]int) { assert.Empty(t, stats.pendingTaskCountPerDomain) assert.Empty(t, stats.pendingTaskCountPerDomainPerSlice) assert.Empty(t, stats.slicesPerDomain) assert.Empty(t, stats.pendingTaskCountPerDomainPerSlice) }, }, { name: "single queue single slice", setupMocks: func(ctrl *gomock.Controller) (*MockVirtualQueueManager, map[string][]VirtualSlice, map[VirtualSlice]map[string]int) { mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockVirtualQueue := NewMockVirtualQueue(ctrl) mockVirtualSlice := NewMockVirtualSlice(ctrl) virtualQueues := map[int64]VirtualQueue{0: mockVirtualQueue} pendingTaskStats := PendingTaskStats{ PendingTaskCountPerDomain: map[string]int{ "domain1": 10, "domain2": 5, }, } mockVirtualQueueManager.EXPECT().VirtualQueues().Return(virtualQueues).Times(1) mockVirtualQueue.EXPECT().IterateSlices(gomock.Any()).DoAndReturn(func(f func(VirtualSlice)) { f(mockVirtualSlice) }).Times(1) mockVirtualSlice.EXPECT().PendingTaskStats().Return(pendingTaskStats).Times(1) mockVirtualSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), }, Predicate: NewUniversalPredicate(), }).AnyTimes() expectedSlicesPerDomain := map[string][]VirtualSlice{ "domain1": {mockVirtualSlice}, "domain2": {mockVirtualSlice}, } expectedPendingTaskCountPerDomainPerSlice := map[VirtualSlice]map[string]int{ mockVirtualSlice: { "domain1": 10, "domain2": 5, }, } return mockVirtualQueueManager, expectedSlicesPerDomain, expectedPendingTaskCountPerDomainPerSlice }, expectedTotalPendingTaskCount: 15, expectedPendingTaskCountPerDomain: map[string]int{ "domain1": 10, "domain2": 5, }, expectedSlicesPerDomainLength: map[string]int{ "domain1": 1, "domain2": 1, }, validateResults: func(t *testing.T, stats pendingTaskStats, expectedSlicesPerDomain map[string][]VirtualSlice, expectedPendingTaskCountPerDomainPerSlice map[VirtualSlice]map[string]int) { assert.Equal(t, expectedSlicesPerDomain, stats.slicesPerDomain) assert.Equal(t, expectedPendingTaskCountPerDomainPerSlice, stats.pendingTaskCountPerDomainPerSlice) }, }, { name: "multiple queues multiple slices", setupMocks: func(ctrl *gomock.Controller) (*MockVirtualQueueManager, map[string][]VirtualSlice, map[VirtualSlice]map[string]int) { mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockVirtualQueue1 := NewMockVirtualQueue(ctrl) mockVirtualQueue2 := NewMockVirtualQueue(ctrl) mockVirtualSlice1 := NewMockVirtualSlice(ctrl) mockVirtualSlice2 := NewMockVirtualSlice(ctrl) mockVirtualSlice3 := NewMockVirtualSlice(ctrl) virtualQueues := map[int64]VirtualQueue{ 0: mockVirtualQueue1, 1: mockVirtualQueue2, } pendingTaskStats1 := PendingTaskStats{ PendingTaskCountPerDomain: map[string]int{ "domain1": 10, "domain2": 5, }, } pendingTaskStats2 := PendingTaskStats{ PendingTaskCountPerDomain: map[string]int{ "domain1": 8, "domain3": 3, }, } pendingTaskStats3 := PendingTaskStats{ PendingTaskCountPerDomain: map[string]int{ "domain2": 7, "domain3": 4, }, } mockVirtualQueueManager.EXPECT().VirtualQueues().Return(virtualQueues).Times(1) mockVirtualQueue1.EXPECT().IterateSlices(gomock.Any()).DoAndReturn(func(f func(VirtualSlice)) { f(mockVirtualSlice1) f(mockVirtualSlice2) }).Times(1) mockVirtualQueue2.EXPECT().IterateSlices(gomock.Any()).DoAndReturn(func(f func(VirtualSlice)) { f(mockVirtualSlice3) }).Times(1) mockVirtualSlice1.EXPECT().PendingTaskStats().Return(pendingTaskStats1).Times(1) mockVirtualSlice2.EXPECT().PendingTaskStats().Return(pendingTaskStats2).Times(1) mockVirtualSlice3.EXPECT().PendingTaskStats().Return(pendingTaskStats3).Times(1) mockVirtualSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(0), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mockVirtualSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(200), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(300), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mockVirtualSlice3.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(400), }, Predicate: NewUniversalPredicate(), }).AnyTimes() // Expected slices sorted by InclusiveMinTaskKey in descending order // slice1=0, slice2=200, slice3=100 -> sorted: slice2(200), slice3(100), slice1(0) expectedSlicesPerDomain := map[string][]VirtualSlice{ "domain1": {mockVirtualSlice2, mockVirtualSlice1}, // slice2[200] > slice1[0] "domain2": {mockVirtualSlice3, mockVirtualSlice1}, // slice3[100] > slice1[0] "domain3": {mockVirtualSlice2, mockVirtualSlice3}, // slice2[200] > slice3[100] } expectedPendingTaskCountPerDomainPerSlice := map[VirtualSlice]map[string]int{ mockVirtualSlice1: { "domain1": 10, "domain2": 5, }, mockVirtualSlice2: { "domain1": 8, "domain3": 3, }, mockVirtualSlice3: { "domain2": 7, "domain3": 4, }, } return mockVirtualQueueManager, expectedSlicesPerDomain, expectedPendingTaskCountPerDomainPerSlice }, expectedTotalPendingTaskCount: 37, // 10+5 + 8+3 + 7+4 = 37 expectedPendingTaskCountPerDomain: map[string]int{ "domain1": 18, // 10+8 "domain2": 12, // 5+7 "domain3": 7, // 3+4 }, expectedSlicesPerDomainLength: map[string]int{ "domain1": 2, // slice1, slice2 "domain2": 2, // slice1, slice3 "domain3": 2, // slice2, slice3 }, validateResults: func(t *testing.T, stats pendingTaskStats, expectedSlicesPerDomain map[string][]VirtualSlice, expectedPendingTaskCountPerDomainPerSlice map[VirtualSlice]map[string]int) { // Verify the exact slice order assert.Equal(t, expectedSlicesPerDomain, stats.slicesPerDomain) // Verify the pendingTaskCountPerDomainPerSlice assert.Equal(t, expectedPendingTaskCountPerDomainPerSlice, stats.pendingTaskCountPerDomainPerSlice) }, }, { name: "empty slices", setupMocks: func(ctrl *gomock.Controller) (*MockVirtualQueueManager, map[string][]VirtualSlice, map[VirtualSlice]map[string]int) { mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockVirtualQueue := NewMockVirtualQueue(ctrl) mockVirtualSlice := NewMockVirtualSlice(ctrl) virtualQueues := map[int64]VirtualQueue{0: mockVirtualQueue} emptyPendingTaskStats := PendingTaskStats{ PendingTaskCountPerDomain: map[string]int{}, } mockVirtualQueueManager.EXPECT().VirtualQueues().Return(virtualQueues).Times(1) mockVirtualQueue.EXPECT().IterateSlices(gomock.Any()).DoAndReturn(func(f func(VirtualSlice)) { f(mockVirtualSlice) }).Times(1) mockVirtualSlice.EXPECT().PendingTaskStats().Return(emptyPendingTaskStats).Times(1) mockVirtualSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(0), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, Predicate: NewUniversalPredicate(), }).AnyTimes() expectedSlicesPerDomain := map[string][]VirtualSlice{} expectedPendingTaskCountPerDomainPerSlice := map[VirtualSlice]map[string]int{ mockVirtualSlice: {}, } return mockVirtualQueueManager, expectedSlicesPerDomain, expectedPendingTaskCountPerDomainPerSlice }, expectedTotalPendingTaskCount: 0, expectedPendingTaskCountPerDomain: map[string]int{}, expectedSlicesPerDomainLength: map[string]int{}, validateResults: func(t *testing.T, stats pendingTaskStats, expectedSlicesPerDomain map[string][]VirtualSlice, expectedPendingTaskCountPerDomainPerSlice map[VirtualSlice]map[string]int) { assert.Empty(t, stats.slicesPerDomain) assert.Equal(t, expectedPendingTaskCountPerDomainPerSlice, stats.pendingTaskCountPerDomainPerSlice) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockMonitor := NewMockMonitor(ctrl) logger := testlogger.New(t) metricsScope := metrics.NoopScope options := &MitigatorOptions{ MaxVirtualQueueCount: dynamicproperties.GetIntPropertyFn(10), } mockVirtualQueueManager, expectedSlicesPerDomain, expectedPendingTaskCountPerDomainPerSlice := tt.setupMocks(ctrl) mitigator := NewMitigator( mockVirtualQueueManager, mockMonitor, logger, metricsScope, options, ) impl, ok := mitigator.(*mitigatorImpl) require.True(t, ok) impl.virtualQueueManager = mockVirtualQueueManager stats := impl.collectPendingTaskStats() // Verify basic aggregated data assert.Equal(t, tt.expectedTotalPendingTaskCount, stats.totalPendingTaskCount) assert.Equal(t, tt.expectedPendingTaskCountPerDomain, stats.pendingTaskCountPerDomain) // Verify slices per domain lengths for domain, expectedLength := range tt.expectedSlicesPerDomainLength { assert.Len(t, stats.slicesPerDomain[domain], expectedLength, "domain: %s", domain) } // Run custom validation if provided if tt.validateResults != nil { tt.validateResults(t, stats, expectedSlicesPerDomain, expectedPendingTaskCountPerDomainPerSlice) } }) } } func TestMitigator_findDomainsToClear(t *testing.T) { tests := []struct { name string setupStats func(*gomock.Controller) (pendingTaskStats, map[VirtualSlice][]string) targetCount int }{ { name: "target count zero - clear everything", setupStats: func(ctrl *gomock.Controller) (pendingTaskStats, map[VirtualSlice][]string) { mockSlice1 := NewMockVirtualSlice(ctrl) mockSlice2 := NewMockVirtualSlice(ctrl) mockSlice3 := NewMockVirtualSlice(ctrl) stats := pendingTaskStats{ totalPendingTaskCount: 142, pendingTaskCountPerDomain: map[string]int{ "domain1": 35, // higher priority "domain2": 45, "domain3": 62, }, pendingTaskCountPerDomainPerSlice: map[VirtualSlice]map[string]int{ mockSlice1: {"domain1": 21, "domain2": 34, "domain3": 55}, mockSlice2: {"domain1": 13, "domain2": 8, "domain3": 5}, mockSlice3: {"domain1": 1, "domain2": 3, "domain3": 2}, }, slicesPerDomain: map[string][]VirtualSlice{ "domain1": {mockSlice1, mockSlice2, mockSlice3}, "domain2": {mockSlice2, mockSlice3, mockSlice1}, "domain3": {mockSlice3, mockSlice1, mockSlice2}, }, } expectedResult := map[VirtualSlice][]string{ mockSlice1: {"domain3", "domain1", "domain2"}, mockSlice2: {"domain2", "domain1", "domain3"}, mockSlice3: {"domain3", "domain2", "domain1"}, } return stats, expectedResult }, targetCount: 0, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockMonitor := NewMockMonitor(ctrl) logger := testlogger.New(t) metricsScope := metrics.NoopScope options := &MitigatorOptions{ MaxVirtualQueueCount: dynamicproperties.GetIntPropertyFn(10), } mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mitigator := NewMitigator( mockVirtualQueueManager, mockMonitor, logger, metricsScope, options, ) impl, ok := mitigator.(*mitigatorImpl) require.True(t, ok) stats, expectedResult := tt.setupStats(ctrl) result := impl.findDomainsToClear(stats, tt.targetCount) // Verify the actual result content assert.Equal(t, expectedResult, result, "Result should contain the expected slice-to-domains mapping") }) } } func TestMitigator_processQueueSplitsAndClear(t *testing.T) { tests := []struct { name string setupMocks func(*gomock.Controller) (map[int64]VirtualQueue, map[VirtualSlice][]string, int) }{ { name: "no domains to clear - no operations", setupMocks: func(ctrl *gomock.Controller) (map[int64]VirtualQueue, map[VirtualSlice][]string, int) { mockVQ := NewMockVirtualQueue(ctrl) // SplitSlices is still called, but the split function should return false for all slices mockVQ.EXPECT().SplitSlices(gomock.Any()).Do(func(splitFunc func(VirtualSlice) ([]VirtualSlice, bool)) { // Since domainsToClear is empty, any slice should return split=false mockSlice := NewMockVirtualSlice(nil) remaining, split := splitFunc(mockSlice) require.False(t, split) require.Nil(t, remaining) }).Times(1) virtualQueues := map[int64]VirtualQueue{ 0: mockVQ, } domainsToClear := map[VirtualSlice][]string{} maxQueueCount := 3 return virtualQueues, domainsToClear, maxQueueCount }, }, { name: "clear slices in last queue - no splitting", setupMocks: func(ctrl *gomock.Controller) (map[int64]VirtualQueue, map[VirtualSlice][]string, int) { mockSlice1 := NewMockVirtualSlice(ctrl) mockSlice2 := NewMockVirtualSlice(ctrl) mockVQ := NewMockVirtualQueue(ctrl) // Should call ClearSlices with a predicate function mockVQ.EXPECT().ClearSlices(gomock.Any()).Do(func(predicate func(VirtualSlice) bool) { // Verify the predicate returns true for slices in domainsToClear clearedCount := 0 for _, slice := range []VirtualSlice{mockSlice1, mockSlice2} { if predicate(slice) { clearedCount++ } } // Since both slices are in domainsToClear, both should be cleared require.Equal(t, 1, clearedCount) }).Times(1) // Should pause the queue after clearing mockVQ.EXPECT().Pause(clearSliceThrottleDuration).Times(1) virtualQueues := map[int64]VirtualQueue{ 2: mockVQ, // queueID >= maxQueueCount-1 (2) } domainsToClear := map[VirtualSlice][]string{ mockSlice1: {"domain1"}, } maxQueueCount := 3 return virtualQueues, domainsToClear, maxQueueCount }, }, { name: "split and move slices - successful split", setupMocks: func(ctrl *gomock.Controller) (map[int64]VirtualQueue, map[VirtualSlice][]string, int) { mockSlice := NewMockVirtualSlice(ctrl) mockSplitSlice := NewMockVirtualSlice(ctrl) mockRemainingSlice := NewMockVirtualSlice(ctrl) mockVQ := NewMockVirtualQueue(ctrl) // Set up split behavior mockSlice.EXPECT().TrySplitByPredicate(gomock.Any()).Return(mockSplitSlice, mockRemainingSlice, true).AnyTimes() mockSplitSlice.EXPECT().Clear().AnyTimes() // Should call SplitSlices with a function mockVQ.EXPECT().SplitSlices(gomock.Any()).Do(func(splitFunc func(VirtualSlice) ([]VirtualSlice, bool)) { // Call the split function with our mock slice remaining, split := splitFunc(mockSlice) // Should return split=true and remaining slice require.True(t, split) require.Len(t, remaining, 1) }).Times(1) virtualQueues := map[int64]VirtualQueue{ 0: mockVQ, // queueID < maxQueueCount-1 } domainsToClear := map[VirtualSlice][]string{ mockSlice: {"domain1", "domain2"}, } maxQueueCount := 3 return virtualQueues, domainsToClear, maxQueueCount }, }, { name: "clear slice when split fails", setupMocks: func(ctrl *gomock.Controller) (map[int64]VirtualQueue, map[VirtualSlice][]string, int) { mockSlice := NewMockVirtualSlice(ctrl) mockVQ := NewMockVirtualQueue(ctrl) // Set up split behavior to fail mockSlice.EXPECT().TrySplitByPredicate(gomock.Any()).Return(nil, nil, false).AnyTimes() mockSlice.EXPECT().Clear().AnyTimes() mockVQ.EXPECT().SplitSlices(gomock.Any()).Do(func(splitFunc func(VirtualSlice) ([]VirtualSlice, bool)) { remaining, split := splitFunc(mockSlice) require.True(t, split) require.Nil(t, remaining) }).Times(1) virtualQueues := map[int64]VirtualQueue{ 1: mockVQ, } domainsToClear := map[VirtualSlice][]string{ mockSlice: {"domain1"}, } maxQueueCount := 4 return virtualQueues, domainsToClear, maxQueueCount }, }, { name: "multiple queues with mixed operations", setupMocks: func(ctrl *gomock.Controller) (map[int64]VirtualQueue, map[VirtualSlice][]string, int) { // Queue 0: split and move mockSlice1 := NewMockVirtualSlice(ctrl) mockSplitSlice1 := NewMockVirtualSlice(ctrl) mockRemainingSlice1 := NewMockVirtualSlice(ctrl) mockVQ0 := NewMockVirtualQueue(ctrl) // Queue 1: split and move mockSlice2 := NewMockVirtualSlice(ctrl) mockSplitSlice2 := NewMockVirtualSlice(ctrl) mockRemainingSlice2 := NewMockVirtualSlice(ctrl) mockVQ1 := NewMockVirtualQueue(ctrl) // Queue 2: clear (last queue) mockSlice3 := NewMockVirtualSlice(ctrl) mockVQ2 := NewMockVirtualQueue(ctrl) // Set up split behaviors mockSlice1.EXPECT().TrySplitByPredicate(gomock.Any()).Return(mockSplitSlice1, mockRemainingSlice1, true).AnyTimes() mockSplitSlice1.EXPECT().Clear().AnyTimes() mockSlice2.EXPECT().TrySplitByPredicate(gomock.Any()).Return(mockSplitSlice2, mockRemainingSlice2, true).AnyTimes() mockSplitSlice2.EXPECT().Clear().AnyTimes() // Queue 0: should split mockVQ0.EXPECT().SplitSlices(gomock.Any()).Times(1) // Queue 1: should split mockVQ1.EXPECT().SplitSlices(gomock.Any()).Times(1) // Queue 2: should clear (last queue) var cleared bool mockVQ2.EXPECT().ClearSlices(gomock.Any()).Do(func(predicate func(VirtualSlice) bool) { // Simulate that mockSlice3 exists in the queue and is checked if predicate(mockSlice3) { cleared = true } }).Times(1) // Pause should only be called if cleared is true mockVQ2.EXPECT().Pause(clearSliceThrottleDuration).Do(func(duration interface{}) { require.True(t, cleared, "Pause should only be called if slices were cleared") }).Times(1) virtualQueues := map[int64]VirtualQueue{ 0: mockVQ0, 1: mockVQ1, 2: mockVQ2, // Last queue (>= maxQueueCount-1) } domainsToClear := map[VirtualSlice][]string{ mockSlice1: {"domain1"}, mockSlice2: {"domain2"}, mockSlice3: {"domain3"}, } maxQueueCount := 3 return virtualQueues, domainsToClear, maxQueueCount }, }, { name: "empty domains to clear for some slices", setupMocks: func(ctrl *gomock.Controller) (map[int64]VirtualQueue, map[VirtualSlice][]string, int) { mockSlice1 := NewMockVirtualSlice(ctrl) // Has domains to clear mockSplitSlice1 := NewMockVirtualSlice(ctrl) mockRemainingSlice1 := NewMockVirtualSlice(ctrl) mockVQ := NewMockVirtualQueue(ctrl) // Set up split behavior for slice that has domains to clear mockSlice1.EXPECT().TrySplitByPredicate(gomock.Any()).Return(mockSplitSlice1, mockRemainingSlice1, true).AnyTimes() mockSplitSlice1.EXPECT().Clear().AnyTimes() mockVQ.EXPECT().SplitSlices(gomock.Any()).Do(func(splitFunc func(VirtualSlice) ([]VirtualSlice, bool)) { // Test with slice that has domains to clear remaining, split := splitFunc(mockSlice1) require.True(t, split) require.Len(t, remaining, 1) // Test with slice that has no domains to clear mockSlice2 := NewMockVirtualSlice(nil) remaining, split = splitFunc(mockSlice2) require.False(t, split) require.Nil(t, remaining) }).Times(1) virtualQueues := map[int64]VirtualQueue{ 0: mockVQ, } domainsToClear := map[VirtualSlice][]string{ mockSlice1: {"domain1"}, // mockSlice2 intentionally not in domainsToClear } maxQueueCount := 3 return virtualQueues, domainsToClear, maxQueueCount }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockMonitor := NewMockMonitor(ctrl) logger := testlogger.New(t) metricsScope := metrics.NoopScope options := &MitigatorOptions{ MaxVirtualQueueCount: dynamicproperties.GetIntPropertyFn(3), } virtualQueues, domainsToClear, maxQueueCount := tt.setupMocks(ctrl) options.MaxVirtualQueueCount = dynamicproperties.GetIntPropertyFn(maxQueueCount) // Create a mock VirtualQueueManager that returns our test queues mockVQManager := NewMockVirtualQueueManager(ctrl) // Set up expectations for GetOrCreateVirtualQueue calls if needed for queueID := range virtualQueues { if queueID < int64(maxQueueCount-1) { // Expect calls to create next queues for moving slices nextQueueID := queueID + 1 if _, exists := virtualQueues[nextQueueID]; !exists { mockNextVQ := NewMockVirtualQueue(ctrl) mockNextVQ.EXPECT().Pause(clearSliceThrottleDuration).AnyTimes() mockNextVQ.EXPECT().MergeSlices(gomock.Any()).AnyTimes() mockVQManager.EXPECT().GetOrCreateVirtualQueue(nextQueueID).Return(mockNextVQ).AnyTimes() } } } mitigator := &mitigatorImpl{ virtualQueueManager: mockVQManager, monitor: mockMonitor, logger: logger, metricsScope: metricsScope, options: options, } // Execute the method mitigator.processQueueSplitsAndClear(virtualQueues, domainsToClear) }) } } ================================================ FILE: service/history/queuev2/monitor.go ================================================ //go:generate mockgen -package $GOPACKAGE -destination monitor_mock.go github.com/uber/cadence/service/history/queuev2 Monitor package queuev2 import ( "sync" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" ) type ( Monitor interface { Subscribe(chan<- *Alert) Unsubscribe() GetTotalPendingTaskCount() int GetSlicePendingTaskCount(VirtualSlice) int SetSlicePendingTaskCount(VirtualSlice, int) RemoveSlice(VirtualSlice) ResolveAlert(AlertType) } MonitorOptions struct { EnablePendingTaskCountAlert func() bool CriticalPendingTaskCount dynamicproperties.IntPropertyFn } monitorImpl struct { sync.Mutex category persistence.HistoryTaskCategory options *MonitorOptions subscriber chan<- *Alert pendingAlerts map[AlertType]struct{} totalPendingTaskCount int slicePendingTaskCount map[VirtualSlice]int } ) func NewMonitor(category persistence.HistoryTaskCategory, options *MonitorOptions) Monitor { return &monitorImpl{ category: category, options: options, pendingAlerts: make(map[AlertType]struct{}), totalPendingTaskCount: 0, slicePendingTaskCount: make(map[VirtualSlice]int), } } func (m *monitorImpl) Subscribe(subscriber chan<- *Alert) { m.Lock() defer m.Unlock() m.subscriber = subscriber } func (m *monitorImpl) Unsubscribe() { m.Lock() defer m.Unlock() m.subscriber = nil } func (m *monitorImpl) GetTotalPendingTaskCount() int { m.Lock() defer m.Unlock() return m.totalPendingTaskCount } func (m *monitorImpl) GetSlicePendingTaskCount(slice VirtualSlice) int { m.Lock() defer m.Unlock() return m.slicePendingTaskCount[slice] } func (m *monitorImpl) SetSlicePendingTaskCount(slice VirtualSlice, count int) { m.Lock() defer m.Unlock() currentSliceCount := m.slicePendingTaskCount[slice] m.totalPendingTaskCount += count - currentSliceCount m.slicePendingTaskCount[slice] = count criticalPendingTaskCount := m.options.CriticalPendingTaskCount() if m.options.EnablePendingTaskCountAlert() && criticalPendingTaskCount > 0 && m.totalPendingTaskCount > criticalPendingTaskCount { m.sendAlertLocked(&Alert{ AlertType: AlertTypeQueuePendingTaskCount, AlertAttributesQueuePendingTaskCount: &AlertAttributesQueuePendingTaskCount{ CurrentPendingTaskCount: m.totalPendingTaskCount, CriticalPendingTaskCount: criticalPendingTaskCount, }, }) } } func (m *monitorImpl) RemoveSlice(slice VirtualSlice) { m.Lock() defer m.Unlock() if currentSliceCount, ok := m.slicePendingTaskCount[slice]; ok { m.totalPendingTaskCount -= currentSliceCount delete(m.slicePendingTaskCount, slice) } } func (m *monitorImpl) ResolveAlert(alertType AlertType) { m.Lock() defer m.Unlock() delete(m.pendingAlerts, alertType) } func (m *monitorImpl) sendAlertLocked(alert *Alert) { // deduplicate alerts if _, ok := m.pendingAlerts[alert.AlertType]; ok { return } select { case m.subscriber <- alert: m.pendingAlerts[alert.AlertType] = struct{}{} default: // do not block if subscriber is not ready } } ================================================ FILE: service/history/queuev2/monitor_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/queuev2 (interfaces: Monitor) // // Generated by this command: // // mockgen -package queuev2 -destination monitor_mock.go github.com/uber/cadence/service/history/queuev2 Monitor // // Package queuev2 is a generated GoMock package. package queuev2 import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockMonitor is a mock of Monitor interface. type MockMonitor struct { ctrl *gomock.Controller recorder *MockMonitorMockRecorder isgomock struct{} } // MockMonitorMockRecorder is the mock recorder for MockMonitor. type MockMonitorMockRecorder struct { mock *MockMonitor } // NewMockMonitor creates a new mock instance. func NewMockMonitor(ctrl *gomock.Controller) *MockMonitor { mock := &MockMonitor{ctrl: ctrl} mock.recorder = &MockMonitorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMonitor) EXPECT() *MockMonitorMockRecorder { return m.recorder } // GetSlicePendingTaskCount mocks base method. func (m *MockMonitor) GetSlicePendingTaskCount(arg0 VirtualSlice) int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSlicePendingTaskCount", arg0) ret0, _ := ret[0].(int) return ret0 } // GetSlicePendingTaskCount indicates an expected call of GetSlicePendingTaskCount. func (mr *MockMonitorMockRecorder) GetSlicePendingTaskCount(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlicePendingTaskCount", reflect.TypeOf((*MockMonitor)(nil).GetSlicePendingTaskCount), arg0) } // GetTotalPendingTaskCount mocks base method. func (m *MockMonitor) GetTotalPendingTaskCount() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTotalPendingTaskCount") ret0, _ := ret[0].(int) return ret0 } // GetTotalPendingTaskCount indicates an expected call of GetTotalPendingTaskCount. func (mr *MockMonitorMockRecorder) GetTotalPendingTaskCount() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTotalPendingTaskCount", reflect.TypeOf((*MockMonitor)(nil).GetTotalPendingTaskCount)) } // RemoveSlice mocks base method. func (m *MockMonitor) RemoveSlice(arg0 VirtualSlice) { m.ctrl.T.Helper() m.ctrl.Call(m, "RemoveSlice", arg0) } // RemoveSlice indicates an expected call of RemoveSlice. func (mr *MockMonitorMockRecorder) RemoveSlice(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveSlice", reflect.TypeOf((*MockMonitor)(nil).RemoveSlice), arg0) } // ResolveAlert mocks base method. func (m *MockMonitor) ResolveAlert(arg0 AlertType) { m.ctrl.T.Helper() m.ctrl.Call(m, "ResolveAlert", arg0) } // ResolveAlert indicates an expected call of ResolveAlert. func (mr *MockMonitorMockRecorder) ResolveAlert(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveAlert", reflect.TypeOf((*MockMonitor)(nil).ResolveAlert), arg0) } // SetSlicePendingTaskCount mocks base method. func (m *MockMonitor) SetSlicePendingTaskCount(arg0 VirtualSlice, arg1 int) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetSlicePendingTaskCount", arg0, arg1) } // SetSlicePendingTaskCount indicates an expected call of SetSlicePendingTaskCount. func (mr *MockMonitorMockRecorder) SetSlicePendingTaskCount(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSlicePendingTaskCount", reflect.TypeOf((*MockMonitor)(nil).SetSlicePendingTaskCount), arg0, arg1) } // Subscribe mocks base method. func (m *MockMonitor) Subscribe(arg0 chan<- *Alert) { m.ctrl.T.Helper() m.ctrl.Call(m, "Subscribe", arg0) } // Subscribe indicates an expected call of Subscribe. func (mr *MockMonitorMockRecorder) Subscribe(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockMonitor)(nil).Subscribe), arg0) } // Unsubscribe mocks base method. func (m *MockMonitor) Unsubscribe() { m.ctrl.T.Helper() m.ctrl.Call(m, "Unsubscribe") } // Unsubscribe indicates an expected call of Unsubscribe. func (mr *MockMonitorMockRecorder) Unsubscribe() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockMonitor)(nil).Unsubscribe)) } ================================================ FILE: service/history/queuev2/monitor_test.go ================================================ package queuev2 import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/persistence" ) func TestMonitorPendingTaskCount(t *testing.T) { monitor := NewMonitor(persistence.HistoryTaskCategoryTimer, &MonitorOptions{ CriticalPendingTaskCount: dynamicproperties.GetIntPropertyFn(100), EnablePendingTaskCountAlert: func() bool { return true }, }) assert.Equal(t, 0, monitor.GetTotalPendingTaskCount()) slice1 := &virtualSliceImpl{} slice2 := &virtualSliceImpl{} slice3 := &virtualSliceImpl{} // set pending task count for slices monitor.SetSlicePendingTaskCount(slice1, 10) assert.Equal(t, 10, monitor.GetTotalPendingTaskCount()) assert.Equal(t, 10, monitor.GetSlicePendingTaskCount(slice1)) monitor.SetSlicePendingTaskCount(slice2, 20) assert.Equal(t, 30, monitor.GetTotalPendingTaskCount()) assert.Equal(t, 20, monitor.GetSlicePendingTaskCount(slice2)) monitor.SetSlicePendingTaskCount(slice3, 30) assert.Equal(t, 60, monitor.GetTotalPendingTaskCount()) assert.Equal(t, 30, monitor.GetSlicePendingTaskCount(slice3)) // update pending task count for slices monitor.SetSlicePendingTaskCount(slice1, 15) assert.Equal(t, 65, monitor.GetTotalPendingTaskCount()) assert.Equal(t, 15, monitor.GetSlicePendingTaskCount(slice1)) monitor.SetSlicePendingTaskCount(slice2, 21) assert.Equal(t, 66, monitor.GetTotalPendingTaskCount()) assert.Equal(t, 21, monitor.GetSlicePendingTaskCount(slice2)) monitor.RemoveSlice(slice1) assert.Equal(t, 51, monitor.GetTotalPendingTaskCount()) monitor.RemoveSlice(slice2) assert.Equal(t, 30, monitor.GetTotalPendingTaskCount()) monitor.RemoveSlice(slice3) assert.Equal(t, 0, monitor.GetTotalPendingTaskCount()) alertCh := make(chan *Alert, alertChSize) monitor.Subscribe(alertCh) monitor.SetSlicePendingTaskCount(slice1, 101) assert.Equal(t, 101, monitor.GetTotalPendingTaskCount()) assert.Equal(t, 101, monitor.GetSlicePendingTaskCount(slice1)) alert := <-alertCh assert.Equal(t, AlertTypeQueuePendingTaskCount, alert.AlertType) assert.Equal(t, 101, alert.AlertAttributesQueuePendingTaskCount.CurrentPendingTaskCount) assert.Equal(t, 100, alert.AlertAttributesQueuePendingTaskCount.CriticalPendingTaskCount) _, ok := monitor.(*monitorImpl).pendingAlerts[AlertTypeQueuePendingTaskCount] assert.True(t, ok) } func TestMonitorSubscribeAndUnsubscribe(t *testing.T) { monitor := NewMonitor(persistence.HistoryTaskCategoryTimer, &MonitorOptions{}) alertCh := make(chan *Alert, alertChSize) monitor.Subscribe(alertCh) monitor.(*monitorImpl).subscriber <- &Alert{AlertType: AlertTypeQueuePendingTaskCount} alert := <-alertCh assert.Equal(t, AlertTypeQueuePendingTaskCount, alert.AlertType) monitor.Unsubscribe() assert.Nil(t, monitor.(*monitorImpl).subscriber) } func TestMonitorResolveAlert(t *testing.T) { monitor := NewMonitor(persistence.HistoryTaskCategoryTimer, &MonitorOptions{}) monitor.(*monitorImpl).pendingAlerts[AlertTypeQueuePendingTaskCount] = struct{}{} assert.Equal(t, 1, len(monitor.(*monitorImpl).pendingAlerts)) monitor.ResolveAlert(AlertTypeQueuePendingTaskCount) assert.Equal(t, 0, len(monitor.(*monitorImpl).pendingAlerts)) } ================================================ FILE: service/history/queuev2/pause_controller.go ================================================ //go:generate mockgen -package $GOPACKAGE -destination pause_controller_mock.go github.com/uber/cadence/service/history/queuev2 PauseController package queuev2 import ( "sync" "time" "github.com/uber/cadence/common/clock" ) type ( // PauseController is a controller that allows to pause and resume a background job. // For example, if you have a background job like this: // func run(ctx context.Context, wg *sync.WaitGroup, ...) { // defer wg.Done() // for { // select { // case <-ctx.Done(): // return // case <-notifyCh: // doSomething(...) // } // } // } // you can integrate the pause controller into the run function like this: // func run(ctx context.Context, wg *sync.WaitGroup, pauseController PauseController, ...) { // defer wg.Done() // pauseController.Subscribe("run", notifyCh) // for { // select { // case <-ctx.Done(): // return // case <-notifyCh: // doSomething(pauseController, ...) // } // } // } // // func doSomething(pauseController PauseController, ...) { // if someCondition { // pauseController.Pause(10 * time.Second) // } // if pauseController.IsPaused() { // return // } // // do the actual work // } PauseController interface { // Stop the pause controller but don't send notification to the subscribers. no-op if it's not paused. Stop() // Pause the job for the given duration. Zero and negative durations are ignored. // If it's already paused, the pause duration can only be updated to a longer duration. Pause(time.Duration) // Resume the job immediately. If it's not paused, this is a no-op. Resume() // Check if the job is paused. IsPaused() bool // Subscribe to the pause controller. Subscribe(string, chan<- struct{}) // Unsubscribe from the pause controller. Unsubscribe(string) } pauseControllerImpl struct { sync.Mutex subscribers map[string]chan<- struct{} timeSource clock.TimeSource pauseUntil time.Time timer clock.Timer } ) func NewPauseController(timeSource clock.TimeSource) PauseController { return &pauseControllerImpl{ timeSource: timeSource, subscribers: make(map[string]chan<- struct{}), } } func (p *pauseControllerImpl) IsPaused() bool { p.Lock() defer p.Unlock() return p.timer != nil } func (p *pauseControllerImpl) Subscribe(id string, ch chan<- struct{}) { p.Lock() defer p.Unlock() p.subscribers[id] = ch } func (p *pauseControllerImpl) Unsubscribe(id string) { p.Lock() defer p.Unlock() delete(p.subscribers, id) } func (p *pauseControllerImpl) Stop() { p.Lock() defer p.Unlock() p.stopTimerLocked() p.timer = nil p.pauseUntil = time.Time{} } func (p *pauseControllerImpl) Pause(duration time.Duration) { if duration <= 0 { return } p.Lock() defer p.Unlock() newPauseUntil := p.timeSource.Now().Add(duration) if newPauseUntil.Before(p.pauseUntil) { return } p.stopTimerLocked() p.timer = p.timeSource.AfterFunc(duration, func() { p.Lock() defer p.Unlock() p.timer = nil p.pauseUntil = time.Time{} p.notifySubscribers() }) p.pauseUntil = newPauseUntil } func (p *pauseControllerImpl) Resume() { p.Lock() defer p.Unlock() if p.timer == nil { return } p.stopTimerLocked() p.timer = nil p.pauseUntil = time.Time{} p.notifySubscribers() } func (p *pauseControllerImpl) stopTimerLocked() { if p.timer != nil { p.timer.Stop() } } func (p *pauseControllerImpl) notifySubscribers() { for _, ch := range p.subscribers { select { case ch <- struct{}{}: default: } } } ================================================ FILE: service/history/queuev2/pause_controller_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/queuev2 (interfaces: PauseController) // // Generated by this command: // // mockgen -package queuev2 -destination pause_controller_mock.go github.com/uber/cadence/service/history/queuev2 PauseController // // Package queuev2 is a generated GoMock package. package queuev2 import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" ) // MockPauseController is a mock of PauseController interface. type MockPauseController struct { ctrl *gomock.Controller recorder *MockPauseControllerMockRecorder isgomock struct{} } // MockPauseControllerMockRecorder is the mock recorder for MockPauseController. type MockPauseControllerMockRecorder struct { mock *MockPauseController } // NewMockPauseController creates a new mock instance. func NewMockPauseController(ctrl *gomock.Controller) *MockPauseController { mock := &MockPauseController{ctrl: ctrl} mock.recorder = &MockPauseControllerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPauseController) EXPECT() *MockPauseControllerMockRecorder { return m.recorder } // IsPaused mocks base method. func (m *MockPauseController) IsPaused() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsPaused") ret0, _ := ret[0].(bool) return ret0 } // IsPaused indicates an expected call of IsPaused. func (mr *MockPauseControllerMockRecorder) IsPaused() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPaused", reflect.TypeOf((*MockPauseController)(nil).IsPaused)) } // Pause mocks base method. func (m *MockPauseController) Pause(arg0 time.Duration) { m.ctrl.T.Helper() m.ctrl.Call(m, "Pause", arg0) } // Pause indicates an expected call of Pause. func (mr *MockPauseControllerMockRecorder) Pause(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pause", reflect.TypeOf((*MockPauseController)(nil).Pause), arg0) } // Resume mocks base method. func (m *MockPauseController) Resume() { m.ctrl.T.Helper() m.ctrl.Call(m, "Resume") } // Resume indicates an expected call of Resume. func (mr *MockPauseControllerMockRecorder) Resume() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resume", reflect.TypeOf((*MockPauseController)(nil).Resume)) } // Stop mocks base method. func (m *MockPauseController) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockPauseControllerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockPauseController)(nil).Stop)) } // Subscribe mocks base method. func (m *MockPauseController) Subscribe(arg0 string, arg1 chan<- struct{}) { m.ctrl.T.Helper() m.ctrl.Call(m, "Subscribe", arg0, arg1) } // Subscribe indicates an expected call of Subscribe. func (mr *MockPauseControllerMockRecorder) Subscribe(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockPauseController)(nil).Subscribe), arg0, arg1) } // Unsubscribe mocks base method. func (m *MockPauseController) Unsubscribe(arg0 string) { m.ctrl.T.Helper() m.ctrl.Call(m, "Unsubscribe", arg0) } // Unsubscribe indicates an expected call of Unsubscribe. func (mr *MockPauseControllerMockRecorder) Unsubscribe(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockPauseController)(nil).Unsubscribe), arg0) } ================================================ FILE: service/history/queuev2/pause_controller_test.go ================================================ package queuev2 import ( "sync" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "github.com/uber/cadence/common/clock" ) func TestPauseController_Basic(t *testing.T) { defer goleak.VerifyNone(t) timeSource := clock.NewMockedTimeSource() controller := NewPauseController(timeSource) ch := make(chan struct{}, 100) controller.Subscribe("test", ch) // Initially not paused assert.False(t, controller.IsPaused()) // pause for 100ms controller.Pause(100 * time.Millisecond) assert.True(t, controller.IsPaused()) // advance 50ms, should still be paused timeSource.BlockUntil(1) timeSource.Advance(50 * time.Millisecond) assert.True(t, controller.IsPaused()) // pause for 200ms and this should reset the pause duration controller.Pause(200 * time.Millisecond) assert.True(t, controller.IsPaused()) // Advance time to trigger timer timeSource.BlockUntil(1) timeSource.Advance(200 * time.Millisecond) // wait for the timer to expire <-ch assert.False(t, controller.IsPaused()) // Resume when not paused should be no-op controller.Resume() assert.False(t, controller.IsPaused()) select { case <-ch: t.Fatal("resume when not paused should be no-op") default: // Expected } // Pause then resume controller.Pause(100 * time.Millisecond) assert.True(t, controller.IsPaused()) controller.Resume() assert.False(t, controller.IsPaused()) select { case <-ch: default: t.Fatal("channel should receive notification after resume") } // Resume again should be no-op controller.Resume() assert.False(t, controller.IsPaused()) select { case <-ch: t.Fatal("resume when not paused should be no-op") default: // Expected } // Stop when not paused controller.Stop() assert.False(t, controller.IsPaused()) select { case <-ch: t.Fatal("channel should not receive notification after stop") default: // Expected } // Pause then stop controller.Pause(100 * time.Millisecond) assert.True(t, controller.IsPaused()) controller.Stop() assert.False(t, controller.IsPaused()) select { case <-ch: t.Fatal("channel should not receive notification after stop") default: // Expected } controller.Pause(100 * time.Millisecond) assert.True(t, controller.IsPaused()) // Pause duration is less than the previous pause duration, should not reset the pause duration controller.Pause(10 * time.Millisecond) timeSource.BlockUntil(1) timeSource.Advance(20 * time.Millisecond) assert.True(t, controller.IsPaused()) select { case <-ch: t.Fatal("channel should not receive notification") default: // Expected } timeSource.BlockUntil(1) timeSource.Advance(100 * time.Millisecond) // wait for the timer to expire <-ch assert.False(t, controller.IsPaused()) } func TestPauseController_Subscribe_Unsubscribe(t *testing.T) { timeSource := clock.NewMockedTimeSource() controller := NewPauseController(timeSource) // Subscribe a channel ch1 := make(chan struct{}, 1) controller.Subscribe("sub1", ch1) // Subscribe another channel ch2 := make(chan struct{}, 1) controller.Subscribe("sub2", ch2) // Unsubscribe one controller.Unsubscribe("sub1") // Pause and resume to trigger notifications controller.Pause(100 * time.Millisecond) controller.Resume() // Only ch2 should receive notification select { case <-ch1: t.Fatal("ch1 should not receive notification after unsubscribe") default: // Expected } select { case <-ch2: // Expected default: t.Fatal("ch2 should receive notification") } } func TestPauseController_NotifySubscribers(t *testing.T) { timeSource := clock.NewMockedTimeSource() controller := NewPauseController(timeSource) // Subscribe multiple channels ch1 := make(chan struct{}, 1) ch2 := make(chan struct{}, 1) ch3 := make(chan struct{}, 1) controller.Subscribe("sub1", ch1) controller.Subscribe("sub2", ch2) controller.Subscribe("sub3", ch3) // Pause and resume to trigger notifications controller.Pause(100 * time.Millisecond) controller.Resume() // All channels should receive notifications select { case <-ch1: // Expected default: t.Fatal("ch1 should receive notification") } select { case <-ch2: // Expected default: t.Fatal("ch2 should receive notification") } select { case <-ch3: // Expected default: t.Fatal("ch3 should receive notification") } } func TestPauseController_SubscriberChannelBlocking(t *testing.T) { timeSource := clock.NewMockedTimeSource() controller := NewPauseController(timeSource) // Subscribe a channel with no buffer (will block) ch := make(chan struct{}) controller.Subscribe("sub1", ch) // Pause and resume to trigger notification controller.Pause(100 * time.Millisecond) controller.Resume() // The notification should be sent non-blocking // If the channel is full or blocked, it should not cause deadlock // This test verifies that the select with default case works correctly assert.False(t, controller.IsPaused()) } func TestPauseController_ZeroDurationPause(t *testing.T) { timeSource := clock.NewMockedTimeSource() controller := NewPauseController(timeSource) // Subscribe a channel ch := make(chan struct{}, 1) controller.Subscribe("sub1", ch) // Pause with zero duration controller.Pause(0) assert.False(t, controller.IsPaused()) // Channel should not receive notification select { case <-ch: t.Fatal("channel should not receive notification for zero duration pause") default: // Expected } } func TestPauseController_NegativeDurationPause(t *testing.T) { timeSource := clock.NewMockedTimeSource() controller := NewPauseController(timeSource) // Subscribe a channel ch := make(chan struct{}, 1) controller.Subscribe("sub1", ch) // Pause with negative duration controller.Pause(-100 * time.Millisecond) assert.False(t, controller.IsPaused()) // Channel should not receive notification select { case <-ch: t.Fatal("channel should not receive notification for negative duration pause") default: // Expected } } func TestPauseController_ConcurrentAccess(t *testing.T) { defer goleak.VerifyNone(t) timeSource := clock.NewMockedTimeSource() controller := NewPauseController(timeSource) var wg sync.WaitGroup numGoroutines := 100 // Test concurrent pause/resume operations for i := 0; i < numGoroutines; i++ { wg.Add(1) go func(id int) { defer wg.Done() ch := make(chan struct{}, 1) controller.Subscribe("sub"+string(rune(id)), ch) controller.Pause(time.Duration(id+1) * time.Millisecond) controller.Resume() controller.IsPaused() controller.Unsubscribe("sub" + string(rune(id))) }(i) } wg.Wait() } ================================================ FILE: service/history/queuev2/pending_task_tracker.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination pending_task_tracker_mock.go github.com/uber/cadence/service/history/queuev2 PendingTaskTracker package queuev2 import ( "github.com/uber/cadence/common/persistence" ctask "github.com/uber/cadence/common/task" "github.com/uber/cadence/service/history/task" ) type ( // PendingTaskTracker tracks the pending tasks in a virtual slice. PendingTaskTracker interface { // AddTask adds a task to the pending task tracker. AddTask(task.Task) // PruneAckedTasks prunes the acked tasks from the pending task tracker. PruneAckedTasks() int // GetMinimumTaskKey returns the minimum task key in the pending task tracker, if there are no pending tasks, it returns MaximumHistoryTaskKey. GetMinimumTaskKey() (persistence.HistoryTaskKey, bool) // GetTasks returns all the tasks in the pending task tracker, the result should be read-only. GetTasks() map[persistence.HistoryTaskKey]task.Task // GetPendingTaskCount returns the number of pending tasks in the pending task tracker. GetPendingTaskCount() int // GetPerDomainPendingTaskCount returns the number of pending tasks per domain. GetPerDomainPendingTaskCount() map[string]int // Clear clears the pending task tracker. Clear() } pendingTaskTrackerImpl struct { taskMap map[persistence.HistoryTaskKey]task.Task taskCountPerDomain map[string]int // domainID -> task count minTaskKey persistence.HistoryTaskKey } ) func NewPendingTaskTracker() PendingTaskTracker { return &pendingTaskTrackerImpl{ taskMap: make(map[persistence.HistoryTaskKey]task.Task), taskCountPerDomain: make(map[string]int), minTaskKey: persistence.MaximumHistoryTaskKey, } } func (t *pendingTaskTrackerImpl) AddTask(task task.Task) { if len(t.taskMap) == 0 { t.minTaskKey = task.GetTaskKey() } else if t.minTaskKey.Compare(task.GetTaskKey()) > 0 { t.minTaskKey = task.GetTaskKey() } t.taskMap[task.GetTaskKey()] = task t.taskCountPerDomain[task.GetDomainID()]++ } func (t *pendingTaskTrackerImpl) GetMinimumTaskKey() (persistence.HistoryTaskKey, bool) { if len(t.taskMap) == 0 { return persistence.MaximumHistoryTaskKey, false } return t.minTaskKey, true } func (t *pendingTaskTrackerImpl) GetTasks() map[persistence.HistoryTaskKey]task.Task { return t.taskMap } func (t *pendingTaskTrackerImpl) PruneAckedTasks() int { prunedCount := 0 minTaskKey := persistence.MaximumHistoryTaskKey for key, task := range t.taskMap { if task.State() == ctask.TaskStateAcked { delete(t.taskMap, key) t.taskCountPerDomain[task.GetDomainID()]-- prunedCount++ continue } if key.Compare(minTaskKey) < 0 { minTaskKey = key } } t.minTaskKey = minTaskKey return prunedCount } func (t *pendingTaskTrackerImpl) GetPendingTaskCount() int { return len(t.taskMap) } func (t *pendingTaskTrackerImpl) GetPerDomainPendingTaskCount() map[string]int { return t.taskCountPerDomain } func (t *pendingTaskTrackerImpl) Clear() { for _, task := range t.taskMap { task.Cancel() } t.taskMap = make(map[persistence.HistoryTaskKey]task.Task) t.taskCountPerDomain = make(map[string]int) t.minTaskKey = persistence.MaximumHistoryTaskKey } ================================================ FILE: service/history/queuev2/pending_task_tracker_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/queuev2 (interfaces: PendingTaskTracker) // // Generated by this command: // // mockgen -package queuev2 -destination pending_task_tracker_mock.go github.com/uber/cadence/service/history/queuev2 PendingTaskTracker // // Package queuev2 is a generated GoMock package. package queuev2 import ( reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" task "github.com/uber/cadence/service/history/task" ) // MockPendingTaskTracker is a mock of PendingTaskTracker interface. type MockPendingTaskTracker struct { ctrl *gomock.Controller recorder *MockPendingTaskTrackerMockRecorder isgomock struct{} } // MockPendingTaskTrackerMockRecorder is the mock recorder for MockPendingTaskTracker. type MockPendingTaskTrackerMockRecorder struct { mock *MockPendingTaskTracker } // NewMockPendingTaskTracker creates a new mock instance. func NewMockPendingTaskTracker(ctrl *gomock.Controller) *MockPendingTaskTracker { mock := &MockPendingTaskTracker{ctrl: ctrl} mock.recorder = &MockPendingTaskTrackerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPendingTaskTracker) EXPECT() *MockPendingTaskTrackerMockRecorder { return m.recorder } // AddTask mocks base method. func (m *MockPendingTaskTracker) AddTask(arg0 task.Task) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddTask", arg0) } // AddTask indicates an expected call of AddTask. func (mr *MockPendingTaskTrackerMockRecorder) AddTask(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTask", reflect.TypeOf((*MockPendingTaskTracker)(nil).AddTask), arg0) } // Clear mocks base method. func (m *MockPendingTaskTracker) Clear() { m.ctrl.T.Helper() m.ctrl.Call(m, "Clear") } // Clear indicates an expected call of Clear. func (mr *MockPendingTaskTrackerMockRecorder) Clear() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockPendingTaskTracker)(nil).Clear)) } // GetMinimumTaskKey mocks base method. func (m *MockPendingTaskTracker) GetMinimumTaskKey() (persistence.HistoryTaskKey, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMinimumTaskKey") ret0, _ := ret[0].(persistence.HistoryTaskKey) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetMinimumTaskKey indicates an expected call of GetMinimumTaskKey. func (mr *MockPendingTaskTrackerMockRecorder) GetMinimumTaskKey() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMinimumTaskKey", reflect.TypeOf((*MockPendingTaskTracker)(nil).GetMinimumTaskKey)) } // GetPendingTaskCount mocks base method. func (m *MockPendingTaskTracker) GetPendingTaskCount() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPendingTaskCount") ret0, _ := ret[0].(int) return ret0 } // GetPendingTaskCount indicates an expected call of GetPendingTaskCount. func (mr *MockPendingTaskTrackerMockRecorder) GetPendingTaskCount() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingTaskCount", reflect.TypeOf((*MockPendingTaskTracker)(nil).GetPendingTaskCount)) } // GetPerDomainPendingTaskCount mocks base method. func (m *MockPendingTaskTracker) GetPerDomainPendingTaskCount() map[string]int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPerDomainPendingTaskCount") ret0, _ := ret[0].(map[string]int) return ret0 } // GetPerDomainPendingTaskCount indicates an expected call of GetPerDomainPendingTaskCount. func (mr *MockPendingTaskTrackerMockRecorder) GetPerDomainPendingTaskCount() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPerDomainPendingTaskCount", reflect.TypeOf((*MockPendingTaskTracker)(nil).GetPerDomainPendingTaskCount)) } // GetTasks mocks base method. func (m *MockPendingTaskTracker) GetTasks() map[persistence.HistoryTaskKey]task.Task { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasks") ret0, _ := ret[0].(map[persistence.HistoryTaskKey]task.Task) return ret0 } // GetTasks indicates an expected call of GetTasks. func (mr *MockPendingTaskTrackerMockRecorder) GetTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasks", reflect.TypeOf((*MockPendingTaskTracker)(nil).GetTasks)) } // PruneAckedTasks mocks base method. func (m *MockPendingTaskTracker) PruneAckedTasks() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PruneAckedTasks") ret0, _ := ret[0].(int) return ret0 } // PruneAckedTasks indicates an expected call of PruneAckedTasks. func (mr *MockPendingTaskTrackerMockRecorder) PruneAckedTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PruneAckedTasks", reflect.TypeOf((*MockPendingTaskTracker)(nil).PruneAckedTasks)) } ================================================ FILE: service/history/queuev2/pending_task_tracker_test.go ================================================ package queuev2 import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" ctask "github.com/uber/cadence/common/task" "github.com/uber/cadence/service/history/task" ) func TestPendingTaskTracker(t *testing.T) { testTime := time.Unix(0, 0) tests := []struct { name string setupTasks func(ctrl *gomock.Controller) []*task.MockTask pruneAcked bool pruneAckedCount int clear bool wantMinKey persistence.HistoryTaskKey wantHasMinKey bool wantTaskCount int wantDomainCounts map[string]int }{ { name: "empty tracker", setupTasks: func(ctrl *gomock.Controller) []*task.MockTask { return []*task.MockTask{} }, wantMinKey: persistence.MaximumHistoryTaskKey, wantHasMinKey: false, wantTaskCount: 0, wantDomainCounts: map[string]int{}, }, { name: "single task", setupTasks: func(ctrl *gomock.Controller) []*task.MockTask { mockTask := task.NewMockTask(ctrl) mockTask.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 1)).AnyTimes() mockTask.EXPECT().GetDomainID().Return("domain1").AnyTimes() mockTask.EXPECT().State().Return(ctask.TaskStatePending).AnyTimes() return []*task.MockTask{mockTask} }, wantMinKey: persistence.NewHistoryTaskKey(testTime, 1), wantHasMinKey: true, wantTaskCount: 1, wantDomainCounts: map[string]int{"domain1": 1}, }, { name: "multiple tasks", setupTasks: func(ctrl *gomock.Controller) []*task.MockTask { task1 := task.NewMockTask(ctrl) task1.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 3)).AnyTimes() task1.EXPECT().GetDomainID().Return("domain1").AnyTimes() task1.EXPECT().State().Return(ctask.TaskStatePending).AnyTimes() task2 := task.NewMockTask(ctrl) task2.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 1)).AnyTimes() task2.EXPECT().GetDomainID().Return("domain2").AnyTimes() task2.EXPECT().State().Return(ctask.TaskStatePending).AnyTimes() task3 := task.NewMockTask(ctrl) task3.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 2)).AnyTimes() task3.EXPECT().GetDomainID().Return("domain1").AnyTimes() task3.EXPECT().State().Return(ctask.TaskStatePending).AnyTimes() return []*task.MockTask{task1, task2, task3} }, wantMinKey: persistence.NewHistoryTaskKey(testTime, 1), wantHasMinKey: true, wantTaskCount: 3, wantDomainCounts: map[string]int{"domain1": 2, "domain2": 1}, }, { name: "prune acked tasks", setupTasks: func(ctrl *gomock.Controller) []*task.MockTask { task1 := task.NewMockTask(ctrl) task1.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 1)).AnyTimes() task1.EXPECT().GetDomainID().Return("domain1").AnyTimes() task1.EXPECT().State().Return(ctask.TaskStateAcked).AnyTimes() task2 := task.NewMockTask(ctrl) task2.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 2)).AnyTimes() task2.EXPECT().GetDomainID().Return("domain2").AnyTimes() task2.EXPECT().State().Return(ctask.TaskStatePending).AnyTimes() task3 := task.NewMockTask(ctrl) task3.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 3)).AnyTimes() task3.EXPECT().GetDomainID().Return("domain1").AnyTimes() task3.EXPECT().State().Return(ctask.TaskStateAcked).AnyTimes() return []*task.MockTask{task1, task2, task3} }, pruneAcked: true, pruneAckedCount: 2, wantMinKey: persistence.NewHistoryTaskKey(testTime, 2), wantHasMinKey: true, wantTaskCount: 1, wantDomainCounts: map[string]int{"domain1": 0, "domain2": 1}, }, { name: "all tasks acked", setupTasks: func(ctrl *gomock.Controller) []*task.MockTask { task1 := task.NewMockTask(ctrl) task1.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 1)).AnyTimes() task1.EXPECT().GetDomainID().Return("domain1").AnyTimes() task1.EXPECT().State().Return(ctask.TaskStateAcked).AnyTimes() task2 := task.NewMockTask(ctrl) task2.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 2)).AnyTimes() task2.EXPECT().GetDomainID().Return("domain2").AnyTimes() task2.EXPECT().State().Return(ctask.TaskStateAcked).AnyTimes() return []*task.MockTask{task1, task2} }, pruneAcked: true, pruneAckedCount: 2, wantMinKey: persistence.MaximumHistoryTaskKey, wantHasMinKey: false, wantTaskCount: 0, wantDomainCounts: map[string]int{"domain1": 0, "domain2": 0}, }, { name: "clear all tasks", setupTasks: func(ctrl *gomock.Controller) []*task.MockTask { task1 := task.NewMockTask(ctrl) task1.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 1)).AnyTimes() task1.EXPECT().GetDomainID().Return("domain1").AnyTimes() task1.EXPECT().Cancel().AnyTimes() task1.EXPECT().State().Return(ctask.TaskStateCanceled).Times(1) task2 := task.NewMockTask(ctrl) task2.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 2)).AnyTimes() task2.EXPECT().GetDomainID().Return("domain2").AnyTimes() task2.EXPECT().Cancel().AnyTimes() task2.EXPECT().State().Return(ctask.TaskStateCanceled).Times(1) task3 := task.NewMockTask(ctrl) task3.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(testTime, 3)).AnyTimes() task3.EXPECT().GetDomainID().Return("domain1").AnyTimes() task3.EXPECT().Cancel().AnyTimes() task3.EXPECT().State().Return(ctask.TaskStateCanceled).Times(1) return []*task.MockTask{task1, task2, task3} }, clear: true, wantMinKey: persistence.MaximumHistoryTaskKey, wantHasMinKey: false, wantTaskCount: 0, wantDomainCounts: map[string]int{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tracker := NewPendingTaskTracker() ctrl := gomock.NewController(t) inputTasks := tt.setupTasks(ctrl) // Setup tasks for _, task := range inputTasks { tracker.AddTask(task) } // Prune acked tasks if needed if tt.pruneAcked { prunedCount := tracker.PruneAckedTasks() assert.Equal(t, tt.pruneAckedCount, prunedCount) } // Clear all tasks if needed if tt.clear { tracker.Clear() } // Test GetMinimumTaskKey gotMinKey, gotHasMinKey := tracker.GetMinimumTaskKey() assert.Equal(t, tt.wantMinKey, gotMinKey) assert.Equal(t, tt.wantHasMinKey, gotHasMinKey) // Test GetTasks tasks := tracker.GetTasks() assert.Equal(t, tt.wantTaskCount, tracker.GetPendingTaskCount()) // Test GetPerDomainPendingTaskCount domainCounts := tracker.GetPerDomainPendingTaskCount() assert.Equal(t, tt.wantDomainCounts, domainCounts, "Per-domain task counts should match expected values") // Verify all tasks are in the map for _, task := range inputTasks { if tt.clear { // After clear, no tasks should be in the map _, exists := tasks[task.GetTaskKey()] assert.False(t, exists, "After clear, no task should be in the map") assert.Equal(t, ctask.TaskStateCanceled, task.State()) } else if tt.pruneAcked && task.State() == ctask.TaskStateAcked { _, exists := tasks[task.GetTaskKey()] assert.False(t, exists, "Acked task should not be in the map") } else { _, exists := tasks[task.GetTaskKey()] assert.True(t, exists, "Task should be in the map") } } }) } } ================================================ FILE: service/history/queuev2/predicate.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination predicate_mock.go github.com/uber/cadence/service/history/queuev2 Predicate package queuev2 import ( "maps" "github.com/uber/cadence/common/persistence" ) type ( // Predicate defines a predicate that can be used to filter tasks Predicate interface { // IsEmpty returns true if no task satisfies the predicate IsEmpty() bool // Check returns true if the task satisfies the predicate Check(task persistence.Task) bool // Equals returns true if the predicate is the same as the other predicate Equals(other Predicate) bool } domainIDPredicate struct { domainIDs map[string]struct{} isExclusive bool } universalPredicate struct{} emptyPredicate struct{} ) func NewUniversalPredicate() Predicate { return &universalPredicate{} } func (p *universalPredicate) IsEmpty() bool { return false } func (p *universalPredicate) Check(task persistence.Task) bool { return true } func (p *universalPredicate) Equals(other Predicate) bool { _, ok := other.(*universalPredicate) return ok } func NewEmptyPredicate() Predicate { return &emptyPredicate{} } func (p *emptyPredicate) IsEmpty() bool { return true } func (p *emptyPredicate) Check(task persistence.Task) bool { return false } func (p *emptyPredicate) Equals(other Predicate) bool { _, ok := other.(*emptyPredicate) return ok } func NewDomainIDPredicate(domainIDs []string, isExclusive bool) Predicate { domainIDSet := make(map[string]struct{}) for _, domainID := range domainIDs { domainIDSet[domainID] = struct{}{} } return &domainIDPredicate{ domainIDs: domainIDSet, isExclusive: isExclusive, } } func (p *domainIDPredicate) IsEmpty() bool { return len(p.domainIDs) == 0 && !p.isExclusive } func (p *domainIDPredicate) Check(task persistence.Task) bool { if _, ok := p.domainIDs[task.GetDomainID()]; ok { return !p.isExclusive } return p.isExclusive } func (p *domainIDPredicate) Equals(other Predicate) bool { o, ok := other.(*domainIDPredicate) if !ok { return false } return p.isExclusive == o.isExclusive && maps.Equal(p.domainIDs, o.domainIDs) } ================================================ FILE: service/history/queuev2/predicate_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/queuev2 (interfaces: Predicate) // // Generated by this command: // // mockgen -package queuev2 -destination predicate_mock.go github.com/uber/cadence/service/history/queuev2 Predicate // // Package queuev2 is a generated GoMock package. package queuev2 import ( reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" ) // MockPredicate is a mock of Predicate interface. type MockPredicate struct { ctrl *gomock.Controller recorder *MockPredicateMockRecorder isgomock struct{} } // MockPredicateMockRecorder is the mock recorder for MockPredicate. type MockPredicateMockRecorder struct { mock *MockPredicate } // NewMockPredicate creates a new mock instance. func NewMockPredicate(ctrl *gomock.Controller) *MockPredicate { mock := &MockPredicate{ctrl: ctrl} mock.recorder = &MockPredicateMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPredicate) EXPECT() *MockPredicateMockRecorder { return m.recorder } // Check mocks base method. func (m *MockPredicate) Check(task persistence.Task) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Check", task) ret0, _ := ret[0].(bool) return ret0 } // Check indicates an expected call of Check. func (mr *MockPredicateMockRecorder) Check(task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Check", reflect.TypeOf((*MockPredicate)(nil).Check), task) } // Equals mocks base method. func (m *MockPredicate) Equals(other Predicate) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Equals", other) ret0, _ := ret[0].(bool) return ret0 } // Equals indicates an expected call of Equals. func (mr *MockPredicateMockRecorder) Equals(other any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Equals", reflect.TypeOf((*MockPredicate)(nil).Equals), other) } // IsEmpty mocks base method. func (m *MockPredicate) IsEmpty() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsEmpty") ret0, _ := ret[0].(bool) return ret0 } // IsEmpty indicates an expected call of IsEmpty. func (mr *MockPredicateMockRecorder) IsEmpty() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsEmpty", reflect.TypeOf((*MockPredicate)(nil).IsEmpty)) } ================================================ FILE: service/history/queuev2/predicate_operation.go ================================================ package queuev2 import "fmt" func Not(predicate Predicate) Predicate { switch p := predicate.(type) { case *universalPredicate: return &emptyPredicate{} case *emptyPredicate: return &universalPredicate{} case *domainIDPredicate: isExclusive := !p.isExclusive if isExclusive && len(p.domainIDs) == 0 { return &universalPredicate{} } return &domainIDPredicate{ domainIDs: p.domainIDs, isExclusive: isExclusive, } default: panic(fmt.Sprintf("unknown predicate type: %T", p)) } } func And(p1, p2 Predicate) Predicate { switch p1 := p1.(type) { case *universalPredicate: return p2 case *emptyPredicate: return p1 case *domainIDPredicate: switch p2 := p2.(type) { case *universalPredicate: return p1 case *emptyPredicate: return p2 case *domainIDPredicate: if p1.isExclusive { if p2.isExclusive { domainIDs := unionStringSet(p1.domainIDs, p2.domainIDs) if len(domainIDs) == 0 { return &emptyPredicate{} } return &domainIDPredicate{ domainIDs: domainIDs, isExclusive: true, } } domainIDs := map[string]struct{}{} for domainID := range p2.domainIDs { if _, ok := p1.domainIDs[domainID]; !ok { domainIDs[domainID] = struct{}{} } } if len(domainIDs) == 0 { return &emptyPredicate{} } return &domainIDPredicate{ domainIDs: domainIDs, isExclusive: false, } } if p2.isExclusive { domainIDs := map[string]struct{}{} for domainID := range p1.domainIDs { if _, ok := p2.domainIDs[domainID]; !ok { domainIDs[domainID] = struct{}{} } } if len(domainIDs) == 0 { return &emptyPredicate{} } return &domainIDPredicate{ domainIDs: domainIDs, isExclusive: false, } } domainIDs := intersectStringSet(p1.domainIDs, p2.domainIDs) if len(domainIDs) == 0 { return &emptyPredicate{} } return &domainIDPredicate{ domainIDs: domainIDs, isExclusive: false, } default: panic(fmt.Sprintf("unknown predicate type: %T", p2)) } default: panic(fmt.Sprintf("unknown predicate type: %T", p1)) } } func Or(p1, p2 Predicate) Predicate { switch p1 := p1.(type) { case *universalPredicate: return p1 case *emptyPredicate: return p2 case *domainIDPredicate: switch p2 := p2.(type) { case *universalPredicate: return p2 case *emptyPredicate: return p1 case *domainIDPredicate: if p1.isExclusive { if p2.isExclusive { domainIDs := intersectStringSet(p1.domainIDs, p2.domainIDs) if len(domainIDs) == 0 { return &universalPredicate{} } return &domainIDPredicate{ domainIDs: domainIDs, isExclusive: true, } } domainIDs := diffStringSet(p1.domainIDs, p2.domainIDs) if len(domainIDs) == 0 { return &universalPredicate{} } return &domainIDPredicate{ domainIDs: domainIDs, isExclusive: true, } } if p2.isExclusive { domainIDs := diffStringSet(p2.domainIDs, p1.domainIDs) if len(domainIDs) == 0 { return &universalPredicate{} } return &domainIDPredicate{ domainIDs: domainIDs, isExclusive: true, } } domainIDs := unionStringSet(p1.domainIDs, p2.domainIDs) if len(domainIDs) == 0 { return &emptyPredicate{} } return &domainIDPredicate{ domainIDs: domainIDs, isExclusive: false, } default: panic(fmt.Sprintf("unknown predicate type: %T", p2)) } default: panic(fmt.Sprintf("unknown predicate type: %T", p1)) } } func unionStringSet(set1, set2 map[string]struct{}) map[string]struct{} { result := make(map[string]struct{}) for domainID := range set1 { result[domainID] = struct{}{} } for domainID := range set2 { result[domainID] = struct{}{} } return result } func intersectStringSet(set1, set2 map[string]struct{}) map[string]struct{} { result := make(map[string]struct{}) for domainID := range set1 { if _, ok := set2[domainID]; ok { result[domainID] = struct{}{} } } return result } func diffStringSet(set1, set2 map[string]struct{}) map[string]struct{} { result := make(map[string]struct{}) for domainID := range set1 { if _, ok := set2[domainID]; !ok { result[domainID] = struct{}{} } } return result } ================================================ FILE: service/history/queuev2/predicate_operation_test.go ================================================ package queuev2 import ( "testing" fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" ) func TestNot(t *testing.T) { tests := []struct { name string input Predicate expected Predicate }{ { name: "universalPredicate returns emptyPredicate", input: NewUniversalPredicate(), expected: NewEmptyPredicate(), }, { name: "emptyPredicate returns universalPredicate", input: NewEmptyPredicate(), expected: NewUniversalPredicate(), }, { name: "domainIDPredicate with isExclusive=true returns isExclusive=false", input: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), expected: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, { name: "domainIDPredicate with isExclusive=false returns isExclusive=true", input: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), expected: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), }, { name: "domainIDPredicate with empty domains and isExclusive=true", input: NewDomainIDPredicate([]string{}, true), expected: NewDomainIDPredicate([]string{}, false), }, { name: "domainIDPredicate with empty domains and isExclusive=false", input: NewDomainIDPredicate([]string{}, false), expected: NewUniversalPredicate(), }, { name: "domainIDPredicate with single domain", input: NewDomainIDPredicate([]string{"single-domain"}, true), expected: NewDomainIDPredicate([]string{"single-domain"}, false), }, { name: "domainIDPredicate with multiple domains", input: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3", "domain4"}, false), expected: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3", "domain4"}, true), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := Not(tt.input) assert.NotNil(t, result) // Use the Equals method to compare predicates assert.True(t, tt.expected.Equals(result), "expected %+v, got %+v", tt.expected, result) }) } } func TestAnd(t *testing.T) { tests := []struct { name string p1 Predicate p2 Predicate expected Predicate }{ // universalPredicate cases { name: "universalPredicate AND universalPredicate", p1: NewUniversalPredicate(), p2: NewUniversalPredicate(), expected: NewUniversalPredicate(), }, { name: "universalPredicate AND emptyPredicate", p1: NewUniversalPredicate(), p2: NewEmptyPredicate(), expected: NewEmptyPredicate(), }, { name: "universalPredicate AND domainIDPredicate", p1: NewUniversalPredicate(), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), expected: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, // emptyPredicate cases { name: "emptyPredicate AND universalPredicate", p1: NewEmptyPredicate(), p2: NewUniversalPredicate(), expected: NewEmptyPredicate(), }, { name: "emptyPredicate AND emptyPredicate", p1: NewEmptyPredicate(), p2: NewEmptyPredicate(), expected: NewEmptyPredicate(), }, { name: "emptyPredicate AND domainIDPredicate", p1: NewEmptyPredicate(), p2: NewDomainIDPredicate([]string{"domain1"}, true), expected: NewEmptyPredicate(), }, // domainIDPredicate AND universalPredicate { name: "domainIDPredicate AND universalPredicate", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), p2: NewUniversalPredicate(), expected: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), }, // domainIDPredicate AND emptyPredicate { name: "domainIDPredicate AND emptyPredicate", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewEmptyPredicate(), expected: NewEmptyPredicate(), }, // domainIDPredicate AND domainIDPredicate cases { name: "exclusive AND exclusive - union domains", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), p2: NewDomainIDPredicate([]string{"domain3", "domain4"}, true), expected: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3", "domain4"}, true), }, { name: "exclusive AND exclusive - overlapping domains", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), p2: NewDomainIDPredicate([]string{"domain2", "domain3"}, true), expected: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, true), }, { name: "exclusive AND inclusive - p2 domains not in p1", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), p2: NewDomainIDPredicate([]string{"domain2", "domain3", "domain4"}, false), expected: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), }, { name: "exclusive AND inclusive - all p2 domains in p1", p1: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, true), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), expected: NewEmptyPredicate(), }, { name: "inclusive AND exclusive - p1 domains not in p2", p1: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, false), p2: NewDomainIDPredicate([]string{"domain2", "domain4"}, true), expected: NewDomainIDPredicate([]string{"domain1", "domain3"}, false), }, { name: "inclusive AND exclusive - all p1 domains in p2", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, true), expected: NewEmptyPredicate(), }, { name: "inclusive AND inclusive - intersection", p1: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, false), p2: NewDomainIDPredicate([]string{"domain2", "domain3", "domain4"}, false), expected: NewDomainIDPredicate([]string{"domain2", "domain3"}, false), }, { name: "inclusive AND inclusive - no intersection", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), expected: NewEmptyPredicate(), }, { name: "inclusive AND inclusive - same domains", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), expected: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, // Edge cases with empty domain lists { name: "empty exclusive AND non-empty exclusive", p1: NewDomainIDPredicate([]string{}, true), p2: NewDomainIDPredicate([]string{"domain1"}, true), expected: NewDomainIDPredicate([]string{"domain1"}, true), }, { name: "empty inclusive AND non-empty inclusive", p1: NewDomainIDPredicate([]string{}, false), p2: NewDomainIDPredicate([]string{"domain1"}, false), expected: NewEmptyPredicate(), }, { name: "empty exclusive AND non-empty inclusive", p1: NewDomainIDPredicate([]string{}, true), p2: NewDomainIDPredicate([]string{"domain1"}, false), expected: NewDomainIDPredicate([]string{"domain1"}, false), }, { name: "non-empty inclusive AND empty exclusive", p1: NewDomainIDPredicate([]string{"domain1"}, false), p2: NewDomainIDPredicate([]string{}, true), expected: NewDomainIDPredicate([]string{"domain1"}, false), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := And(tt.p1, tt.p2) assert.NotNil(t, result) // Use the Equals method to compare predicates assert.True(t, tt.expected.Equals(result), "expected %+v, got %+v", tt.expected, result) }) } } func predicateOperationFuzzGenerator(p *Predicate, c fuzz.Continue) { switch c.Intn(3) { case 0: *p = NewUniversalPredicate() case 1: *p = NewEmptyPredicate() case 2: var domainIDPredicate domainIDPredicate c.Fuzz(&domainIDPredicate) *p = &domainIDPredicate default: panic("invalid predicate type") } } func TestAnd_Commutativity(t *testing.T) { f := fuzz.New().Funcs(predicateOperationFuzzGenerator) for i := 0; i < 1000; i++ { var p1, p2 Predicate f.Fuzz(&p1) f.Fuzz(&p2) result1 := And(p1, p2) result2 := And(p2, p1) assert.True(t, result1.Equals(result2), "And should be commutative: And(p1, p2) should equal And(p2, p1)") } } func TestAnd_LogicalCorrectness(t *testing.T) { ctrl := gomock.NewController(t) // Test data with different domain IDs testTasks := []*persistence.MockTask{ // Task with domain1 func() *persistence.MockTask { task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("domain1").AnyTimes() return task }(), // Task with domain2 func() *persistence.MockTask { task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("domain2").AnyTimes() return task }(), // Task with domain3 func() *persistence.MockTask { task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("domain3").AnyTimes() return task }(), // Task with domain4 func() *persistence.MockTask { task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("domain4").AnyTimes() return task }(), } // Test cases with different predicate combinations testCases := []struct { name string p1 Predicate p2 Predicate }{ // Universal and Empty predicates { name: "universal AND universal", p1: NewUniversalPredicate(), p2: NewUniversalPredicate(), }, { name: "universal AND empty", p1: NewUniversalPredicate(), p2: NewEmptyPredicate(), }, { name: "empty AND universal", p1: NewEmptyPredicate(), p2: NewUniversalPredicate(), }, { name: "empty AND empty", p1: NewEmptyPredicate(), p2: NewEmptyPredicate(), }, // Universal with domain predicates { name: "universal AND domain inclusive", p1: NewUniversalPredicate(), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, { name: "universal AND domain exclusive", p1: NewUniversalPredicate(), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), }, { name: "domain inclusive AND universal", p1: NewDomainIDPredicate([]string{"domain1", "domain3"}, false), p2: NewUniversalPredicate(), }, { name: "domain exclusive AND universal", p1: NewDomainIDPredicate([]string{"domain1", "domain3"}, true), p2: NewUniversalPredicate(), }, // Empty with domain predicates { name: "empty AND domain inclusive", p1: NewEmptyPredicate(), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, { name: "empty AND domain exclusive", p1: NewEmptyPredicate(), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), }, { name: "domain inclusive AND empty", p1: NewDomainIDPredicate([]string{"domain2", "domain4"}, false), p2: NewEmptyPredicate(), }, { name: "domain exclusive AND empty", p1: NewDomainIDPredicate([]string{"domain2", "domain4"}, true), p2: NewEmptyPredicate(), }, // Domain predicate combinations { name: "inclusive AND inclusive - overlapping", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain2", "domain3"}, false), }, { name: "inclusive AND inclusive - disjoint", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), }, { name: "exclusive AND exclusive - overlapping", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), p2: NewDomainIDPredicate([]string{"domain2", "domain3"}, true), }, { name: "exclusive AND exclusive - disjoint", p1: NewDomainIDPredicate([]string{"domain1"}, true), p2: NewDomainIDPredicate([]string{"domain3"}, true), }, { name: "inclusive AND exclusive - overlapping", p1: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, false), p2: NewDomainIDPredicate([]string{"domain2", "domain4"}, true), }, { name: "exclusive AND inclusive - overlapping", p1: NewDomainIDPredicate([]string{"domain1", "domain3"}, true), p2: NewDomainIDPredicate([]string{"domain2", "domain3", "domain4"}, false), }, // Edge cases with empty domain lists { name: "empty inclusive AND non-empty inclusive", p1: NewDomainIDPredicate([]string{}, false), p2: NewDomainIDPredicate([]string{"domain1"}, false), }, { name: "empty exclusive AND non-empty exclusive", p1: NewDomainIDPredicate([]string{}, true), p2: NewDomainIDPredicate([]string{"domain1"}, true), }, { name: "empty inclusive AND empty exclusive", p1: NewDomainIDPredicate([]string{}, false), p2: NewDomainIDPredicate([]string{}, true), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { andPredicate := And(tc.p1, tc.p2) // Test the logical property for each task for i, task := range testTasks { p1Result := tc.p1.Check(task) p2Result := tc.p2.Check(task) expectedResult := p1Result && p2Result actualResult := andPredicate.Check(task) assert.Equal(t, expectedResult, actualResult, "For task %d (domain=%s): And(p1, p2).Check(task) should equal p1.Check(task) && p2.Check(task). "+ "p1.Check(task)=%t, p2.Check(task)=%t, expected=%t, actual=%t", i, task.GetDomainID(), p1Result, p2Result, expectedResult, actualResult) } }) } } func TestAnd_LogicalCorrectness_FuzzTesting(t *testing.T) { ctrl := gomock.NewController(t) // Create a variety of test tasks with different domain IDs domains := []string{"domain1", "domain2", "domain3", "domain4", "domain5", ""} testTasks := make([]*persistence.MockTask, len(domains)) for i, domain := range domains { task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return(domain).AnyTimes() testTasks[i] = task } f := fuzz.New().Funcs(predicateOperationFuzzGenerator) for i := 0; i < 500; i++ { var p1, p2 Predicate f.Fuzz(&p1) f.Fuzz(&p2) andPredicate := And(p1, p2) // Test the logical property for each task for _, task := range testTasks { p1Result := p1.Check(task) p2Result := p2.Check(task) expectedResult := p1Result && p2Result actualResult := andPredicate.Check(task) assert.Equal(t, expectedResult, actualResult, "Fuzz test iteration %d: And(p1, p2).Check(task) should equal p1.Check(task) && p2.Check(task). "+ "Task domain: %s, p1.Check(task)=%t, p2.Check(task)=%t, expected=%t, actual=%t, "+ "p1=%+v, p2=%+v", i, task.GetDomainID(), p1Result, p2Result, expectedResult, actualResult, p1, p2) } } } func TestOr(t *testing.T) { tests := []struct { name string p1 Predicate p2 Predicate expected Predicate }{ // universalPredicate cases { name: "universalPredicate OR universalPredicate", p1: NewUniversalPredicate(), p2: NewUniversalPredicate(), expected: NewUniversalPredicate(), }, { name: "universalPredicate OR emptyPredicate", p1: NewUniversalPredicate(), p2: NewEmptyPredicate(), expected: NewUniversalPredicate(), }, { name: "universalPredicate OR domainIDPredicate", p1: NewUniversalPredicate(), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), expected: NewUniversalPredicate(), }, // emptyPredicate cases { name: "emptyPredicate OR universalPredicate", p1: NewEmptyPredicate(), p2: NewUniversalPredicate(), expected: NewUniversalPredicate(), }, { name: "emptyPredicate OR emptyPredicate", p1: NewEmptyPredicate(), p2: NewEmptyPredicate(), expected: NewEmptyPredicate(), }, { name: "emptyPredicate OR domainIDPredicate", p1: NewEmptyPredicate(), p2: NewDomainIDPredicate([]string{"domain1"}, true), expected: NewDomainIDPredicate([]string{"domain1"}, true), }, // domainIDPredicate OR universalPredicate { name: "domainIDPredicate OR universalPredicate", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), p2: NewUniversalPredicate(), expected: NewUniversalPredicate(), }, // domainIDPredicate OR emptyPredicate { name: "domainIDPredicate OR emptyPredicate", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewEmptyPredicate(), expected: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, // domainIDPredicate OR domainIDPredicate cases { name: "exclusive OR exclusive - intersection exists", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), p2: NewDomainIDPredicate([]string{"domain2", "domain3"}, true), expected: NewDomainIDPredicate([]string{"domain2"}, true), }, { name: "exclusive OR exclusive - no intersection", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), p2: NewDomainIDPredicate([]string{"domain3", "domain4"}, true), expected: NewUniversalPredicate(), }, { name: "exclusive OR inclusive - p1 excludes some of p2", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), p2: NewDomainIDPredicate([]string{"domain2", "domain3", "domain4"}, false), expected: NewDomainIDPredicate([]string{"domain1"}, true), }, { name: "exclusive OR inclusive - p1 excludes all of p2", p1: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, true), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), expected: NewDomainIDPredicate([]string{"domain3"}, true), }, { name: "exclusive OR inclusive - p1 excludes none of p2", p1: NewDomainIDPredicate([]string{"domain1"}, true), p2: NewDomainIDPredicate([]string{"domain2", "domain3"}, false), expected: NewDomainIDPredicate([]string{"domain1"}, true), }, { name: "inclusive OR exclusive - p2 excludes some of p1", p1: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, false), p2: NewDomainIDPredicate([]string{"domain2", "domain4"}, true), expected: NewDomainIDPredicate([]string{"domain4"}, true), }, { name: "inclusive OR exclusive - p2 excludes all of p1", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, true), expected: NewDomainIDPredicate([]string{"domain3"}, true), }, { name: "inclusive OR exclusive - p2 excludes none of p1", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain3"}, true), expected: NewDomainIDPredicate([]string{"domain3"}, true), }, { name: "inclusive OR inclusive - union", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain2", "domain3", "domain4"}, false), expected: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3", "domain4"}, false), }, { name: "inclusive OR inclusive - same domains", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), expected: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, // Edge cases with empty domain lists { name: "empty exclusive OR non-empty exclusive", p1: NewDomainIDPredicate([]string{}, true), p2: NewDomainIDPredicate([]string{"domain1"}, true), expected: NewUniversalPredicate(), }, { name: "empty inclusive OR non-empty inclusive", p1: NewDomainIDPredicate([]string{}, false), p2: NewDomainIDPredicate([]string{"domain1"}, false), expected: NewDomainIDPredicate([]string{"domain1"}, false), }, { name: "empty exclusive OR non-empty inclusive", p1: NewDomainIDPredicate([]string{}, true), p2: NewDomainIDPredicate([]string{"domain1"}, false), expected: NewUniversalPredicate(), }, { name: "non-empty inclusive OR empty exclusive", p1: NewDomainIDPredicate([]string{"domain1"}, false), p2: NewDomainIDPredicate([]string{}, true), expected: NewUniversalPredicate(), }, { name: "empty exclusive OR empty exclusive", p1: NewDomainIDPredicate([]string{}, true), p2: NewDomainIDPredicate([]string{}, true), expected: NewUniversalPredicate(), }, { name: "empty inclusive OR empty inclusive", p1: NewDomainIDPredicate([]string{}, false), p2: NewDomainIDPredicate([]string{}, false), expected: NewEmptyPredicate(), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := Or(tt.p1, tt.p2) assert.NotNil(t, result) // Use the Equals method to compare predicates assert.True(t, tt.expected.Equals(result), "expected %+v, got %+v", tt.expected, result) }) } } func TestOr_Commutativity(t *testing.T) { f := fuzz.New().Funcs(predicateOperationFuzzGenerator) for i := 0; i < 1000; i++ { var p1, p2 Predicate f.Fuzz(&p1) f.Fuzz(&p2) result1 := Or(p1, p2) result2 := Or(p2, p1) assert.True(t, result1.Equals(result2), "Or should be commutative: Or(p1, p2) should equal Or(p2, p1)") } } func TestOr_LogicalCorrectness(t *testing.T) { ctrl := gomock.NewController(t) // Test data with different domain IDs testTasks := []*persistence.MockTask{ // Task with domain1 func() *persistence.MockTask { task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("domain1").AnyTimes() return task }(), // Task with domain2 func() *persistence.MockTask { task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("domain2").AnyTimes() return task }(), // Task with domain3 func() *persistence.MockTask { task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("domain3").AnyTimes() return task }(), // Task with domain4 func() *persistence.MockTask { task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("domain4").AnyTimes() return task }(), } // Test cases with different predicate combinations testCases := []struct { name string p1 Predicate p2 Predicate }{ // Universal and Empty predicates { name: "universal OR universal", p1: NewUniversalPredicate(), p2: NewUniversalPredicate(), }, { name: "universal OR empty", p1: NewUniversalPredicate(), p2: NewEmptyPredicate(), }, { name: "empty OR universal", p1: NewEmptyPredicate(), p2: NewUniversalPredicate(), }, { name: "empty OR empty", p1: NewEmptyPredicate(), p2: NewEmptyPredicate(), }, // Universal with domain predicates { name: "universal OR domain inclusive", p1: NewUniversalPredicate(), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, { name: "universal OR domain exclusive", p1: NewUniversalPredicate(), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), }, { name: "domain inclusive OR universal", p1: NewDomainIDPredicate([]string{"domain1", "domain3"}, false), p2: NewUniversalPredicate(), }, { name: "domain exclusive OR universal", p1: NewDomainIDPredicate([]string{"domain1", "domain3"}, true), p2: NewUniversalPredicate(), }, // Empty with domain predicates { name: "empty OR domain inclusive", p1: NewEmptyPredicate(), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, { name: "empty OR domain exclusive", p1: NewEmptyPredicate(), p2: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), }, { name: "domain inclusive OR empty", p1: NewDomainIDPredicate([]string{"domain2", "domain4"}, false), p2: NewEmptyPredicate(), }, { name: "domain exclusive OR empty", p1: NewDomainIDPredicate([]string{"domain2", "domain4"}, true), p2: NewEmptyPredicate(), }, // Domain predicate combinations { name: "inclusive OR inclusive - overlapping", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain2", "domain3"}, false), }, { name: "inclusive OR inclusive - disjoint", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), p2: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), }, { name: "exclusive OR exclusive - overlapping", p1: NewDomainIDPredicate([]string{"domain1", "domain2"}, true), p2: NewDomainIDPredicate([]string{"domain2", "domain3"}, true), }, { name: "exclusive OR exclusive - disjoint", p1: NewDomainIDPredicate([]string{"domain1"}, true), p2: NewDomainIDPredicate([]string{"domain3"}, true), }, { name: "inclusive OR exclusive - overlapping", p1: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, false), p2: NewDomainIDPredicate([]string{"domain2", "domain4"}, true), }, { name: "exclusive OR inclusive - overlapping", p1: NewDomainIDPredicate([]string{"domain1", "domain3"}, true), p2: NewDomainIDPredicate([]string{"domain2", "domain3", "domain4"}, false), }, // Edge cases with empty domain lists { name: "empty inclusive OR non-empty inclusive", p1: NewDomainIDPredicate([]string{}, false), p2: NewDomainIDPredicate([]string{"domain1"}, false), }, { name: "empty exclusive OR non-empty exclusive", p1: NewDomainIDPredicate([]string{}, true), p2: NewDomainIDPredicate([]string{"domain1"}, true), }, { name: "empty inclusive OR empty exclusive", p1: NewDomainIDPredicate([]string{}, false), p2: NewDomainIDPredicate([]string{}, true), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { orPredicate := Or(tc.p1, tc.p2) // Test the logical property for each task for i, task := range testTasks { p1Result := tc.p1.Check(task) p2Result := tc.p2.Check(task) expectedResult := p1Result || p2Result actualResult := orPredicate.Check(task) assert.Equal(t, expectedResult, actualResult, "For task %d (domain=%s): Or(p1, p2).Check(task) should equal p1.Check(task) || p2.Check(task). "+ "p1.Check(task)=%t, p2.Check(task)=%t, expected=%t, actual=%t", i, task.GetDomainID(), p1Result, p2Result, expectedResult, actualResult) } }) } } func TestOr_LogicalCorrectness_FuzzTesting(t *testing.T) { ctrl := gomock.NewController(t) // Create a variety of test tasks with different domain IDs domains := []string{"domain1", "domain2", "domain3", "domain4", "domain5", ""} testTasks := make([]*persistence.MockTask, len(domains)) for i, domain := range domains { task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return(domain).AnyTimes() testTasks[i] = task } f := fuzz.New().Funcs(predicateOperationFuzzGenerator) for i := 0; i < 500; i++ { var p1, p2 Predicate f.Fuzz(&p1) f.Fuzz(&p2) orPredicate := Or(p1, p2) // Test the logical property for each task for _, task := range testTasks { p1Result := p1.Check(task) p2Result := p2.Check(task) expectedResult := p1Result || p2Result actualResult := orPredicate.Check(task) assert.Equal(t, expectedResult, actualResult, "Fuzz test iteration %d: Or(p1, p2).Check(task) should equal p1.Check(task) || p2.Check(task). "+ "Task domain: %s, p1.Check(task)=%t, p2.Check(task)=%t, expected=%t, actual=%t, "+ "p1=%+v, p2=%+v", i, task.GetDomainID(), p1Result, p2Result, expectedResult, actualResult, p1, p2) } } } ================================================ FILE: service/history/queuev2/predicate_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queuev2 import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" ) func TestNewUniversalPredicate(t *testing.T) { predicate := NewUniversalPredicate() assert.NotNil(t, predicate) assert.IsType(t, &universalPredicate{}, predicate) } func TestUniversalPredicate_IsEmpty(t *testing.T) { predicate := NewUniversalPredicate() assert.False(t, predicate.IsEmpty()) } func TestUniversalPredicate_Check(t *testing.T) { predicate := NewUniversalPredicate() // Test with nil task assert.True(t, predicate.Check(nil)) // Test with mock task ctrl := gomock.NewController(t) task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("test-domain").AnyTimes() assert.True(t, predicate.Check(task)) // Test with different domain ID task2 := persistence.NewMockTask(ctrl) task2.EXPECT().GetDomainID().Return("different-domain").AnyTimes() assert.True(t, predicate.Check(task2)) } func TestUniversalPredicate_Equals(t *testing.T) { predicate1 := NewUniversalPredicate() predicate2 := NewUniversalPredicate() emptyPredicate := NewEmptyPredicate() domainPredicate := NewDomainIDPredicate([]string{"test"}, false) // Same type should be equal assert.True(t, predicate1.Equals(predicate2)) assert.True(t, predicate2.Equals(predicate1)) // Different types should not be equal assert.False(t, predicate1.Equals(emptyPredicate)) assert.False(t, predicate1.Equals(domainPredicate)) assert.False(t, predicate1.Equals(nil)) } func TestNewEmptyPredicate(t *testing.T) { predicate := NewEmptyPredicate() assert.NotNil(t, predicate) assert.IsType(t, &emptyPredicate{}, predicate) } func TestEmptyPredicate_IsEmpty(t *testing.T) { predicate := NewEmptyPredicate() assert.True(t, predicate.IsEmpty()) } func TestEmptyPredicate_Check(t *testing.T) { predicate := NewEmptyPredicate() // Test with nil task assert.False(t, predicate.Check(nil)) // Test with mock task ctrl := gomock.NewController(t) task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("test-domain").AnyTimes() assert.False(t, predicate.Check(task)) // Test with different domain ID task2 := persistence.NewMockTask(ctrl) task2.EXPECT().GetDomainID().Return("different-domain").AnyTimes() assert.False(t, predicate.Check(task2)) } func TestEmptyPredicate_Equals(t *testing.T) { predicate1 := NewEmptyPredicate() predicate2 := NewEmptyPredicate() universalPredicate := NewUniversalPredicate() domainPredicate := NewDomainIDPredicate([]string{"test"}, false) // Same type should be equal assert.True(t, predicate1.Equals(predicate2)) assert.True(t, predicate2.Equals(predicate1)) // Different types should not be equal assert.False(t, predicate1.Equals(universalPredicate)) assert.False(t, predicate1.Equals(domainPredicate)) assert.False(t, predicate1.Equals(nil)) } func TestNewDomainIDPredicate(t *testing.T) { // Test with empty domain IDs predicate := NewDomainIDPredicate([]string{}, false) assert.NotNil(t, predicate) assert.IsType(t, &domainIDPredicate{}, predicate) // Test with single domain ID predicate = NewDomainIDPredicate([]string{"domain1"}, false) assert.NotNil(t, predicate) // Test with multiple domain IDs predicate = NewDomainIDPredicate([]string{"domain1", "domain2", "domain3"}, true) assert.NotNil(t, predicate) // Test with duplicate domain IDs (should be deduplicated) predicate = NewDomainIDPredicate([]string{"domain1", "domain1", "domain2"}, false) assert.NotNil(t, predicate) } func TestDomainIDPredicate_IsEmpty(t *testing.T) { // Empty domain IDs with inclusive mode predicate := NewDomainIDPredicate([]string{}, false) assert.True(t, predicate.IsEmpty()) // Empty domain IDs with exclusive mode predicate = NewDomainIDPredicate([]string{}, true) assert.False(t, predicate.IsEmpty()) // Non-empty domain IDs with inclusive mode predicate = NewDomainIDPredicate([]string{"domain1"}, false) assert.False(t, predicate.IsEmpty()) // Non-empty domain IDs with exclusive mode predicate = NewDomainIDPredicate([]string{"domain1"}, true) assert.False(t, predicate.IsEmpty()) } func TestDomainIDPredicate_Check_Inclusive(t *testing.T) { // Test inclusive mode (isExclusive = false) predicate := NewDomainIDPredicate([]string{"domain1", "domain2"}, false) ctrl := gomock.NewController(t) // Task with domain in the list should pass task1 := persistence.NewMockTask(ctrl) task1.EXPECT().GetDomainID().Return("domain1").AnyTimes() assert.True(t, predicate.Check(task1)) task2 := persistence.NewMockTask(ctrl) task2.EXPECT().GetDomainID().Return("domain2").AnyTimes() assert.True(t, predicate.Check(task2)) // Task with domain not in the list should fail task3 := persistence.NewMockTask(ctrl) task3.EXPECT().GetDomainID().Return("domain3").AnyTimes() assert.False(t, predicate.Check(task3)) // Test with empty domain ID task4 := persistence.NewMockTask(ctrl) task4.EXPECT().GetDomainID().Return("").AnyTimes() assert.False(t, predicate.Check(task4)) } func TestDomainIDPredicate_Check_Exclusive(t *testing.T) { // Test exclusive mode (isExclusive = true) predicate := NewDomainIDPredicate([]string{"domain1", "domain2"}, true) ctrl := gomock.NewController(t) // Task with domain in the list should fail task1 := persistence.NewMockTask(ctrl) task1.EXPECT().GetDomainID().Return("domain1").AnyTimes() assert.False(t, predicate.Check(task1)) task2 := persistence.NewMockTask(ctrl) task2.EXPECT().GetDomainID().Return("domain2").AnyTimes() assert.False(t, predicate.Check(task2)) // Task with domain not in the list should pass task3 := persistence.NewMockTask(ctrl) task3.EXPECT().GetDomainID().Return("domain3").AnyTimes() assert.True(t, predicate.Check(task3)) // Test with empty domain ID task4 := persistence.NewMockTask(ctrl) task4.EXPECT().GetDomainID().Return("").AnyTimes() assert.True(t, predicate.Check(task4)) } func TestDomainIDPredicate_Check_EmptyList(t *testing.T) { // Test with empty domain list and inclusive mode predicate := NewDomainIDPredicate([]string{}, false) ctrl := gomock.NewController(t) task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("any-domain").AnyTimes() assert.False(t, predicate.Check(task)) // No domains in list, so inclusive returns false // Test with empty domain list and exclusive mode predicate = NewDomainIDPredicate([]string{}, true) assert.True(t, predicate.Check(task)) // No domains in list, so exclusive returns true } func TestDomainIDPredicate_Equals(t *testing.T) { // Test equal predicates predicate1 := NewDomainIDPredicate([]string{"domain1", "domain2"}, false) predicate2 := NewDomainIDPredicate([]string{"domain1", "domain2"}, false) assert.True(t, predicate1.Equals(predicate2)) // Test equal predicates with different order predicate3 := NewDomainIDPredicate([]string{"domain2", "domain1"}, false) assert.True(t, predicate1.Equals(predicate3)) // Test different domain sets predicate4 := NewDomainIDPredicate([]string{"domain1", "domain3"}, false) assert.False(t, predicate1.Equals(predicate4)) // Test different domain count predicate5 := NewDomainIDPredicate([]string{"domain1"}, false) assert.False(t, predicate1.Equals(predicate5)) // Test different exclusion mode predicate6 := NewDomainIDPredicate([]string{"domain1", "domain2"}, true) assert.False(t, predicate1.Equals(predicate6)) // Test against different predicate types universalPredicate := NewUniversalPredicate() emptyPredicate := NewEmptyPredicate() assert.False(t, predicate1.Equals(universalPredicate)) assert.False(t, predicate1.Equals(emptyPredicate)) assert.False(t, predicate1.Equals(nil)) } func TestDomainIDPredicate_Equals_EmptyLists(t *testing.T) { // Test with both empty lists predicate1 := NewDomainIDPredicate([]string{}, false) predicate2 := NewDomainIDPredicate([]string{}, false) assert.True(t, predicate1.Equals(predicate2)) // Test with one empty, one non-empty predicate3 := NewDomainIDPredicate([]string{"domain1"}, false) assert.False(t, predicate1.Equals(predicate3)) // Test with both empty but different exclusion mode predicate4 := NewDomainIDPredicate([]string{}, true) assert.False(t, predicate1.Equals(predicate4)) } func TestDomainIDPredicate_DuplicateDomains(t *testing.T) { // Test that duplicate domains are handled correctly predicate := NewDomainIDPredicate([]string{"domain1", "domain1", "domain2", "domain2"}, false) ctrl := gomock.NewController(t) task1 := persistence.NewMockTask(ctrl) task1.EXPECT().GetDomainID().Return("domain1").AnyTimes() task2 := persistence.NewMockTask(ctrl) task2.EXPECT().GetDomainID().Return("domain2").AnyTimes() task3 := persistence.NewMockTask(ctrl) task3.EXPECT().GetDomainID().Return("domain3").AnyTimes() assert.True(t, predicate.Check(task1)) assert.True(t, predicate.Check(task2)) assert.False(t, predicate.Check(task3)) // Test equals with duplicate domains predicate2 := NewDomainIDPredicate([]string{"domain1", "domain2"}, false) assert.True(t, predicate.Equals(predicate2)) } func TestPredicateIntegration(t *testing.T) { // Test integration between different predicate types universal := NewUniversalPredicate() empty := NewEmptyPredicate() domainInclusive := NewDomainIDPredicate([]string{"domain1"}, false) domainExclusive := NewDomainIDPredicate([]string{"domain1"}, true) ctrl := gomock.NewController(t) task := persistence.NewMockTask(ctrl) task.EXPECT().GetDomainID().Return("domain1").AnyTimes() // Universal should always pass assert.True(t, universal.Check(task)) assert.False(t, universal.IsEmpty()) // Empty should always fail assert.False(t, empty.Check(task)) assert.True(t, empty.IsEmpty()) // Domain inclusive should pass for matching domain assert.True(t, domainInclusive.Check(task)) assert.False(t, domainInclusive.IsEmpty()) // Domain exclusive should fail for matching domain assert.False(t, domainExclusive.Check(task)) assert.False(t, domainExclusive.IsEmpty()) } ================================================ FILE: service/history/queuev2/queue_base.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queuev2 import ( "context" "time" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) const ( alertChSize = 10 // Non-default readers will use critical pending task count * this coefficient // as its max pending task count so that their loading will never trigger pending // task alert & action nonRootQueueMaxPendingTaskCoefficient = 0.8 ) type ( Options struct { PageSize dynamicproperties.IntPropertyFn DeleteBatchSize dynamicproperties.IntPropertyFn MaxPollRPS dynamicproperties.IntPropertyFn MaxPollInterval dynamicproperties.DurationPropertyFn MaxPollIntervalJitterCoefficient dynamicproperties.FloatPropertyFn UpdateAckInterval dynamicproperties.DurationPropertyFn UpdateAckIntervalJitterCoefficient dynamicproperties.FloatPropertyFn RedispatchInterval dynamicproperties.DurationPropertyFn MaxPendingTasksCount dynamicproperties.IntPropertyFn PollBackoffInterval dynamicproperties.DurationPropertyFn PollBackoffIntervalJitterCoefficient dynamicproperties.FloatPropertyFn VirtualSliceForceAppendInterval dynamicproperties.DurationPropertyFn // monitor & mitigator options CriticalPendingTaskCount dynamicproperties.IntPropertyFn EnablePendingTaskCountAlert func() bool MaxVirtualQueueCount dynamicproperties.IntPropertyFn EnableValidator dynamicproperties.BoolPropertyFn ValidationInterval dynamicproperties.DurationPropertyFn MaxStartJitterInterval dynamicproperties.DurationPropertyFn } queueBase struct { shard shard.Context taskProcessor task.Processor logger log.Logger metricsClient metrics.Client metricsScope metrics.Scope category persistence.HistoryTaskCategory options *Options timeSource clock.TimeSource taskInitializer task.Initializer rescheduler task.Rescheduler queueReader QueueReader monitor Monitor mitigator Mitigator updateQueueStateTimer clock.Timer virtualQueueManager VirtualQueueManager exclusiveAckLevel persistence.HistoryTaskKey alertCh chan *Alert newVirtualSliceState VirtualSliceState updateQueueStateFn func(ctx context.Context) } ) func newQueueBase( shard shard.Context, taskProcessor task.Processor, logger log.Logger, metricsClient metrics.Client, metricsScope metrics.Scope, category persistence.HistoryTaskCategory, taskExecutor task.Executor, options *Options, ) *queueBase { timeSource := shard.GetTimeSource() persistenceQueueState, err := shard.GetQueueState(category) if err != nil { logger.Fatal("Failed to get queue state, probably task category is not supported", tag.Error(err), tag.Dynamic("category", category)) } logger.Info("loading queue state", tag.Dynamic("queue-state", persistenceQueueState)) queueState := FromPersistenceQueueState(persistenceQueueState) exclusiveAckLevel, _ := getExclusiveAckLevelAndMaxQueueIDFromQueueState(queueState) rescheduler := task.NewRescheduler( taskProcessor, timeSource, logger, metricsScope, ) var queueType task.QueueType if category == persistence.HistoryTaskCategoryTransfer { queueType = task.QueueTypeTransfer } else if category == persistence.HistoryTaskCategoryTimer { queueType = task.QueueTypeTimer } taskInitializer := func(t persistence.Task) task.Task { return task.NewHistoryTaskV2( shard, t, queueType, task.InitializeLoggerForTask(shard.GetShardID(), t, logger), taskExecutor, taskProcessor, rescheduler, shard.GetConfig().TaskCriticalRetryCount, ) } queueReader := NewQueueReader( shard, category, ) monitor := NewMonitor( category, &MonitorOptions{ CriticalPendingTaskCount: options.CriticalPendingTaskCount, EnablePendingTaskCountAlert: options.EnablePendingTaskCountAlert, }, ) virtualQueueManager := NewVirtualQueueManager( taskProcessor, rescheduler, taskInitializer, queueReader, logger, metricsScope, timeSource, quotas.NewDynamicRateLimiter(options.MaxPollRPS.AsFloat64()), monitor, &VirtualQueueManagerOptions{ RootQueueOptions: &VirtualQueueOptions{ PageSize: options.PageSize, MaxPendingTasksCount: options.MaxPendingTasksCount, PollBackoffInterval: options.PollBackoffInterval, PollBackoffIntervalJitterCoefficient: options.PollBackoffIntervalJitterCoefficient, }, NonRootQueueOptions: &VirtualQueueOptions{ PageSize: options.PageSize, // non-root queues should not trigger task unloading // otherwise those virtual queues will keep loading, hit pending task count limit, unload, throttle, load, etc... // use a limit lower than the critical pending task count instead MaxPendingTasksCount: func(opts ...dynamicproperties.FilterOption) int { return int(float64(options.CriticalPendingTaskCount(opts...)) * nonRootQueueMaxPendingTaskCoefficient) }, PollBackoffInterval: options.PollBackoffInterval, PollBackoffIntervalJitterCoefficient: options.PollBackoffIntervalJitterCoefficient, }, VirtualSliceForceAppendInterval: options.VirtualSliceForceAppendInterval, }, queueState.VirtualQueueStates, ) mitigator := NewMitigator( virtualQueueManager, monitor, logger, metricsScope, &MitigatorOptions{ MaxVirtualQueueCount: options.MaxVirtualQueueCount, }, ) q := &queueBase{ shard: shard, taskProcessor: taskProcessor, logger: logger, metricsClient: metricsClient, metricsScope: metricsScope, category: category, options: options, timeSource: timeSource, taskInitializer: taskInitializer, rescheduler: rescheduler, queueReader: queueReader, monitor: monitor, mitigator: mitigator, exclusiveAckLevel: exclusiveAckLevel, virtualQueueManager: virtualQueueManager, alertCh: make(chan *Alert, alertChSize), newVirtualSliceState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: queueState.ExclusiveMaxReadLevel, ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, } q.updateQueueStateFn = q.updateQueueState return q } func (q *queueBase) Start() { q.rescheduler.Start() q.virtualQueueManager.Start() q.updateQueueStateTimer = q.timeSource.NewTimer(backoff.JitDuration( q.options.UpdateAckInterval(), q.options.UpdateAckIntervalJitterCoefficient(), )) q.monitor.Subscribe(q.alertCh) } func (q *queueBase) Stop() { q.monitor.Unsubscribe() q.updateQueueStateTimer.Stop() q.virtualQueueManager.Stop() q.rescheduler.Stop() } func (q *queueBase) Category() persistence.HistoryTaskCategory { return q.category } func (q *queueBase) FailoverDomain(domainIDs map[string]struct{}) { q.rescheduler.RescheduleDomains(domainIDs) } func (q *queueBase) HandleAction(ctx context.Context, clusterName string, action *queue.Action) (*queue.ActionResult, error) { return nil, nil } func (q *queueBase) LockTaskProcessing() {} func (q *queueBase) UnlockTaskProcessing() {} func (q *queueBase) processNewTasks() bool { newExclusiveMaxTaskKey := q.shard.UpdateIfNeededAndGetQueueMaxReadLevel(q.category, q.shard.GetClusterMetadata().GetCurrentClusterName()) if q.category.Type() == persistence.HistoryTaskCategoryTypeImmediate { newExclusiveMaxTaskKey = persistence.NewImmediateTaskKey(newExclusiveMaxTaskKey.GetTaskID() + 1) } newVirtualSliceState, remainingVirtualSliceState, ok := q.newVirtualSliceState.TrySplitByTaskKey(newExclusiveMaxTaskKey) if !ok { return false } q.newVirtualSliceState = remainingVirtualSliceState newVirtualSlice := NewVirtualSlice(newVirtualSliceState, q.taskInitializer, q.queueReader, NewPendingTaskTracker(), q.logger) q.logger.Debug("processing new tasks", tag.Dynamic("inclusiveMinTaskKey", newVirtualSliceState.Range.InclusiveMinTaskKey), tag.Dynamic("exclusiveMaxTaskKey", newVirtualSliceState.Range.ExclusiveMaxTaskKey)) q.virtualQueueManager.AddNewVirtualSliceToRootQueue(newVirtualSlice) return true } func (q *queueBase) updateQueueState(ctx context.Context) { q.metricsScope.IncCounter(metrics.AckLevelUpdateCounter) queueState := &QueueState{ VirtualQueueStates: q.virtualQueueManager.UpdateAndGetState(), ExclusiveMaxReadLevel: q.newVirtualSliceState.Range.InclusiveMinTaskKey, } newExclusiveAckLevel, maxQueueID := getExclusiveAckLevelAndMaxQueueIDFromQueueState(queueState) q.metricsScope.UpdateGauge(metrics.VirtualQueueCountGauge, float64(maxQueueID+1)) // for backward compatibility, we record the timer metrics in shard info scope pendingTaskCount := q.monitor.GetTotalPendingTaskCount() if q.category == persistence.HistoryTaskCategoryTransfer { q.metricsClient.RecordTimer(metrics.ShardInfoScope, metrics.ShardInfoTransferActivePendingTasksTimer, time.Duration(pendingTaskCount)) } else if q.category == persistence.HistoryTaskCategoryTimer { q.metricsClient.RecordTimer(metrics.ShardInfoScope, metrics.ShardInfoTimerActivePendingTasksTimer, time.Duration(pendingTaskCount)) } // we emit the metrics in the queue scope and experiment with gauge metrics // TODO: review the metrics and remove this comment or change the metric from gauge to histogram q.metricsScope.UpdateGauge(metrics.PendingTaskGauge, float64(pendingTaskCount)) q.logger.Debug("complete history tasks", tag.Dynamic("oldExclusiveAckLevel", q.exclusiveAckLevel), tag.Dynamic("newExclusiveAckLevel", newExclusiveAckLevel)) if newExclusiveAckLevel.Compare(q.exclusiveAckLevel) > 0 { q.metricsScope.IncCounter(metrics.TaskBatchCompleteCounter) inclusiveMinTaskKey := q.exclusiveAckLevel exclusiveMaxTaskKey := newExclusiveAckLevel if q.category.Type() == persistence.HistoryTaskCategoryTypeScheduled { inclusiveMinTaskKey = persistence.NewHistoryTaskKey(inclusiveMinTaskKey.GetScheduledTime(), 0) exclusiveMaxTaskKey = persistence.NewHistoryTaskKey(exclusiveMaxTaskKey.GetScheduledTime(), 0) } for { pageSize := q.options.DeleteBatchSize() resp, err := q.shard.GetExecutionManager().RangeCompleteHistoryTask(ctx, &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: q.category, InclusiveMinTaskKey: inclusiveMinTaskKey, ExclusiveMaxTaskKey: exclusiveMaxTaskKey, PageSize: pageSize, }) if err != nil { q.logger.Error("Failed to range complete history tasks", tag.Error(err)) q.updateQueueStateTimer.Reset(backoff.JitDuration( q.options.UpdateAckInterval(), q.options.UpdateAckIntervalJitterCoefficient(), )) return } if !persistence.HasMoreRowsToDelete(resp.TasksCompleted, pageSize) { break } } q.exclusiveAckLevel = newExclusiveAckLevel } // even though the ack level is not updated, we still need to update the queue state persistenceQueueState := ToPersistenceQueueState(queueState) q.logger.Debug("store queue state", tag.Dynamic("queue-state", persistenceQueueState), tag.PendingTaskCount(pendingTaskCount)) err := q.shard.UpdateQueueState(q.category, persistenceQueueState) if err != nil { q.logger.Error("Failed to update queue state", tag.Error(err)) q.metricsScope.IncCounter(metrics.AckLevelUpdateFailedCounter) } q.updateQueueStateTimer.Reset(backoff.JitDuration( q.options.UpdateAckInterval(), q.options.UpdateAckIntervalJitterCoefficient(), )) } func (q *queueBase) handleAlert(ctx context.Context, alert *Alert) { if alert == nil { return } q.mitigator.Mitigate(*alert) q.updateQueueStateFn(ctx) } func getExclusiveAckLevelAndMaxQueueIDFromQueueState(state *QueueState) (persistence.HistoryTaskKey, int64) { maxQueueID := int64(0) newExclusiveAckLevel := state.ExclusiveMaxReadLevel for queueID, virtualQueueState := range state.VirtualQueueStates { if len(virtualQueueState) != 0 { newExclusiveAckLevel = persistence.MinHistoryTaskKey(newExclusiveAckLevel, virtualQueueState[0].Range.InclusiveMinTaskKey) } maxQueueID = max(maxQueueID, queueID) } return newExclusiveAckLevel, maxQueueID } ================================================ FILE: service/history/queuev2/queue_base_test.go ================================================ package queuev2 import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) func TestQueueBase_ProcessNewTasks(t *testing.T) { tests := []struct { name string category persistence.HistoryTaskCategory initialVirtualSlice VirtualSliceState expectedVirtualSlices []VirtualSlice expectedNewVirtualSliceState VirtualSliceState expectError bool setupMocks func(*gomock.Controller) (*shard.MockContext, *task.MockProcessor, clock.TimeSource, *MockVirtualQueueManager) }{ { name: "Successfully process new tasks for immediate category", category: persistence.HistoryTaskCategoryTransfer, initialVirtualSlice: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, expectedNewVirtualSliceState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(201), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, expectError: false, setupMocks: func(ctrl *gomock.Controller) (*shard.MockContext, *task.MockProcessor, clock.TimeSource, *MockVirtualQueueManager) { mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockShard.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata).AnyTimes() mockShard.EXPECT().GetTimeSource().Return(mockTimeSource).AnyTimes() mockShard.EXPECT().UpdateIfNeededAndGetQueueMaxReadLevel( persistence.HistoryTaskCategoryTransfer, cluster.TestCurrentClusterName, ).Return(persistence.NewImmediateTaskKey(200)) mockVirtualQueueManager.EXPECT().AddNewVirtualSliceToRootQueue(gomock.Any()).DoAndReturn(func(s VirtualSlice) { assert.Equal(t, s.GetState().Range.InclusiveMinTaskKey, persistence.NewImmediateTaskKey(100)) assert.Equal(t, s.GetState().Range.ExclusiveMaxTaskKey, persistence.NewImmediateTaskKey(201)) }) return mockShard, mockTaskProcessor, mockTimeSource, mockVirtualQueueManager }, }, { name: "Successfully process new tasks for scheduled category", category: persistence.HistoryTaskCategoryTimer, initialVirtualSlice: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(100, 0), 100), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, expectedNewVirtualSliceState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(201, 0), 201), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, expectError: false, setupMocks: func(ctrl *gomock.Controller) (*shard.MockContext, *task.MockProcessor, clock.TimeSource, *MockVirtualQueueManager) { mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockShard.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata).AnyTimes() mockShard.EXPECT().GetTimeSource().Return(mockTimeSource).AnyTimes() mockShard.EXPECT().UpdateIfNeededAndGetQueueMaxReadLevel( persistence.HistoryTaskCategoryTimer, cluster.TestCurrentClusterName, ).Return(persistence.NewHistoryTaskKey(time.Unix(201, 0), 201)) mockVirtualQueueManager.EXPECT().AddNewVirtualSliceToRootQueue(gomock.Any()).DoAndReturn(func(s VirtualSlice) { assert.Equal(t, s.GetState().Range.InclusiveMinTaskKey, persistence.NewHistoryTaskKey(time.Unix(100, 0), 100)) assert.Equal(t, s.GetState().Range.ExclusiveMaxTaskKey, persistence.NewHistoryTaskKey(time.Unix(201, 0), 201)) }) return mockShard, mockTaskProcessor, mockTimeSource, mockVirtualQueueManager }, }, { name: "No new tasks to process", category: persistence.HistoryTaskCategoryTransfer, initialVirtualSlice: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, expectedNewVirtualSliceState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, expectError: false, setupMocks: func(ctrl *gomock.Controller) (*shard.MockContext, *task.MockProcessor, clock.TimeSource, *MockVirtualQueueManager) { mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockShard.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata).AnyTimes() mockShard.EXPECT().GetTimeSource().Return(mockTimeSource).AnyTimes() mockShard.EXPECT().UpdateIfNeededAndGetQueueMaxReadLevel( persistence.HistoryTaskCategoryTransfer, cluster.TestCurrentClusterName, ).Return(persistence.NewImmediateTaskKey(99)) return mockShard, mockTaskProcessor, mockTimeSource, mockVirtualQueueManager }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Setup ctrl := gomock.NewController(t) mockShard, mockTaskProcessor, mockTimeSource, mockVirtualQueueManager := tt.setupMocks(ctrl) queueBase := &queueBase{ shard: mockShard, taskProcessor: mockTaskProcessor, metricsClient: metrics.NoopClient, metricsScope: metrics.NoopScope, logger: testlogger.New(t), category: tt.category, timeSource: mockTimeSource, virtualQueueManager: mockVirtualQueueManager, newVirtualSliceState: tt.initialVirtualSlice, } // Execute queueBase.processNewTasks() // Verify if !tt.expectError { assert.Equal(t, queueBase.newVirtualSliceState, tt.expectedNewVirtualSliceState) } }) } } func TestQueueBase_UpdateQueueState(t *testing.T) { tests := []struct { name string category persistence.HistoryTaskCategory initialExclusiveAckLevel persistence.HistoryTaskKey initialVirtualSliceState VirtualSliceState expectedExclusiveAckLevel persistence.HistoryTaskKey expectError bool setupMocks func(*gomock.Controller) (*shard.MockContext, *task.MockProcessor, clock.TimeSource, *MockVirtualQueueManager, *MockMonitor, *MockMitigator) }{ { name: "Successfully update queue state with new ack level", category: persistence.HistoryTaskCategoryTransfer, initialExclusiveAckLevel: persistence.NewImmediateTaskKey(100), initialVirtualSliceState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1000), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, expectedExclusiveAckLevel: persistence.NewImmediateTaskKey(200), expectError: false, setupMocks: func(ctrl *gomock.Controller) (*shard.MockContext, *task.MockProcessor, clock.TimeSource, *MockVirtualQueueManager, *MockMonitor, *MockMitigator) { mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockExecutionManager := persistence.NewMockExecutionManager(ctrl) mockMonitor := NewMockMonitor(ctrl) mockMitigator := NewMockMitigator(ctrl) mockMonitor.EXPECT().GetTotalPendingTaskCount().Return(100).Times(1) mockShard.EXPECT().GetExecutionManager().Return(mockExecutionManager).AnyTimes() mockExecutionManager.EXPECT().RangeCompleteHistoryTask(gomock.Any(), &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 100, }).Return(&persistence.RangeCompleteHistoryTaskResponse{ TasksCompleted: 10, }, nil) mockShard.EXPECT().UpdateQueueState( persistence.HistoryTaskCategoryTransfer, gomock.Any(), ).Return(nil) mockVirtualQueueManager.EXPECT().UpdateAndGetState().Return(map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(200), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(300), }, Predicate: NewUniversalPredicate(), }, }, }) return mockShard, mockTaskProcessor, mockTimeSource, mockVirtualQueueManager, mockMonitor, mockMitigator }, }, { name: "Failed to range complete history tasks", category: persistence.HistoryTaskCategoryTransfer, initialExclusiveAckLevel: persistence.NewImmediateTaskKey(100), initialVirtualSliceState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1000), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, expectedExclusiveAckLevel: persistence.NewImmediateTaskKey(100), expectError: true, setupMocks: func(ctrl *gomock.Controller) (*shard.MockContext, *task.MockProcessor, clock.TimeSource, *MockVirtualQueueManager, *MockMonitor, *MockMitigator) { mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockExecutionManager := persistence.NewMockExecutionManager(ctrl) mockMonitor := NewMockMonitor(ctrl) mockMitigator := NewMockMitigator(ctrl) mockMonitor.EXPECT().GetTotalPendingTaskCount().Return(100).Times(1) mockShard.EXPECT().GetExecutionManager().Return(mockExecutionManager).AnyTimes() mockExecutionManager.EXPECT().RangeCompleteHistoryTask(gomock.Any(), &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryTransfer, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200), PageSize: 100, }).Return(nil, assert.AnError) mockVirtualQueueManager.EXPECT().UpdateAndGetState().Return(map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(200), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(300), }, Predicate: NewUniversalPredicate(), }, }, }) return mockShard, mockTaskProcessor, mockTimeSource, mockVirtualQueueManager, mockMonitor, mockMitigator }, }, { name: "Successfully update queue state with no new ack level", category: persistence.HistoryTaskCategoryTransfer, initialExclusiveAckLevel: persistence.NewImmediateTaskKey(100), initialVirtualSliceState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1000), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, expectedExclusiveAckLevel: persistence.NewImmediateTaskKey(100), expectError: false, setupMocks: func(ctrl *gomock.Controller) (*shard.MockContext, *task.MockProcessor, clock.TimeSource, *MockVirtualQueueManager, *MockMonitor, *MockMitigator) { mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockMonitor := NewMockMonitor(ctrl) mockMitigator := NewMockMitigator(ctrl) mockMonitor.EXPECT().GetTotalPendingTaskCount().Return(100).Times(1) mockShard.EXPECT().UpdateQueueState( persistence.HistoryTaskCategoryTransfer, gomock.Any(), ).Return(nil) mockVirtualQueueManager.EXPECT().UpdateAndGetState().Return(map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(300), }, Predicate: NewUniversalPredicate(), }, }, }) return mockShard, mockTaskProcessor, mockTimeSource, mockVirtualQueueManager, mockMonitor, mockMitigator }, }, { name: "Failed to update queue state", category: persistence.HistoryTaskCategoryTransfer, initialExclusiveAckLevel: persistence.NewImmediateTaskKey(300), initialVirtualSliceState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1000), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, expectedExclusiveAckLevel: persistence.NewImmediateTaskKey(200), expectError: true, setupMocks: func(ctrl *gomock.Controller) (*shard.MockContext, *task.MockProcessor, clock.TimeSource, *MockVirtualQueueManager, *MockMonitor, *MockMitigator) { mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockMonitor := NewMockMonitor(ctrl) mockMitigator := NewMockMitigator(ctrl) mockMonitor.EXPECT().GetTotalPendingTaskCount().Return(100).Times(1) mockShard.EXPECT().UpdateQueueState( persistence.HistoryTaskCategoryTransfer, gomock.Any(), ).Return(assert.AnError) mockVirtualQueueManager.EXPECT().UpdateAndGetState().Return(map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(200), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(300), }, Predicate: NewUniversalPredicate(), }, }, }) return mockShard, mockTaskProcessor, mockTimeSource, mockVirtualQueueManager, mockMonitor, mockMitigator }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Setup ctrl := gomock.NewController(t) mockShard, mockTaskProcessor, mockTimeSource, mockVirtualQueueManager, mockMonitor, mockMitigator := tt.setupMocks(ctrl) queueBase := &queueBase{ shard: mockShard, taskProcessor: mockTaskProcessor, metricsClient: metrics.NoopClient, metricsScope: metrics.NoopScope, logger: testlogger.New(t), category: tt.category, timeSource: mockTimeSource, monitor: mockMonitor, mitigator: mockMitigator, virtualQueueManager: mockVirtualQueueManager, exclusiveAckLevel: tt.initialExclusiveAckLevel, newVirtualSliceState: tt.initialVirtualSliceState, updateQueueStateTimer: mockTimeSource.NewTimer(time.Second * 10), options: &Options{ DeleteBatchSize: dynamicproperties.GetIntPropertyFn(100), UpdateAckInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), UpdateAckIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.1), }, } // Execute queueBase.updateQueueState(context.Background()) // Verify if !tt.expectError { assert.Equal(t, tt.expectedExclusiveAckLevel, queueBase.exclusiveAckLevel) } }) } } func TestQueueBase_HandleAlert(t *testing.T) { ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() mockVirtualQueueManager := NewMockVirtualQueueManager(ctrl) mockMonitor := NewMockMonitor(ctrl) mockMitigator := NewMockMitigator(ctrl) mockMitigator.EXPECT().Mitigate(Alert{AlertType: AlertTypeQueuePendingTaskCount}) updateQueueStateCalled := false queueBase := &queueBase{ shard: mockShard, taskProcessor: mockTaskProcessor, metricsClient: metrics.NoopClient, metricsScope: metrics.NoopScope, logger: testlogger.New(t), category: persistence.HistoryTaskCategoryTransfer, timeSource: mockTimeSource, monitor: mockMonitor, mitigator: mockMitigator, virtualQueueManager: mockVirtualQueueManager, exclusiveAckLevel: persistence.NewImmediateTaskKey(100), newVirtualSliceState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1000), ExclusiveMaxTaskKey: persistence.MaximumHistoryTaskKey, }, Predicate: NewUniversalPredicate(), }, updateQueueStateTimer: mockTimeSource.NewTimer(time.Second * 10), options: &Options{ DeleteBatchSize: dynamicproperties.GetIntPropertyFn(100), UpdateAckInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), UpdateAckIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.1), }, updateQueueStateFn: func(ctx context.Context) { updateQueueStateCalled = true }, } queueBase.handleAlert(context.Background(), &Alert{AlertType: AlertTypeQueuePendingTaskCount}) assert.True(t, updateQueueStateCalled) } func TestNewQueueBase(t *testing.T) { queueState := &types.QueueState{ ExclusiveMaxReadLevel: &types.TaskKey{ TaskID: 400, }, VirtualQueueStates: map[int64]*types.VirtualQueueState{ rootQueueID: { VirtualSliceStates: []*types.VirtualSliceState{ { TaskRange: &types.TaskRange{ InclusiveMin: &types.TaskKey{ TaskID: 100, }, ExclusiveMax: &types.TaskKey{ TaskID: 200, }, }, }, { TaskRange: &types.TaskRange{ InclusiveMin: &types.TaskKey{ TaskID: 200, }, ExclusiveMax: &types.TaskKey{ TaskID: 300, }, }, }, }, }, 1: { VirtualSliceStates: []*types.VirtualSliceState{ { TaskRange: &types.TaskRange{ InclusiveMin: &types.TaskKey{ TaskID: 300, }, ExclusiveMax: &types.TaskKey{ TaskID: 400, }, }, }, }, }, }, } ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() mockShard.EXPECT().GetQueueState(persistence.HistoryTaskCategoryTransfer).Return(queueState, nil) mockShard.EXPECT().GetTimeSource().Return(mockTimeSource) queueBase := newQueueBase( mockShard, mockTaskProcessor, testlogger.New(t), metrics.NoopClient, metrics.NoopScope, persistence.HistoryTaskCategoryTransfer, nil, &Options{ DeleteBatchSize: dynamicproperties.GetIntPropertyFn(100), RedispatchInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), MaxPollRPS: dynamicproperties.GetIntPropertyFn(100), }, ) assert.Equal(t, persistence.NewImmediateTaskKey(400), queueBase.newVirtualSliceState.Range.InclusiveMinTaskKey) virtualQueues := queueBase.virtualQueueManager.VirtualQueues() states := make(map[int64][]VirtualSliceState) for queueID, virtualQueue := range virtualQueues { states[queueID] = virtualQueue.GetState() } assert.Equal(t, map[int64][]VirtualSliceState{ rootQueueID: { { Range: Range{InclusiveMinTaskKey: persistence.NewImmediateTaskKey(100), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(200)}, Predicate: NewUniversalPredicate(), }, { Range: Range{InclusiveMinTaskKey: persistence.NewImmediateTaskKey(200), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(300)}, Predicate: NewUniversalPredicate(), }, }, 1: { { Range: Range{InclusiveMinTaskKey: persistence.NewImmediateTaskKey(300), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(400)}, Predicate: NewUniversalPredicate(), }, }, }, states) } ================================================ FILE: service/history/queuev2/queue_immediate.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queuev2 import ( "context" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) type ( immediateQueue struct { base *queueBase notifyCh chan struct{} status int32 shutdownWG sync.WaitGroup ctx context.Context cancel func() pollTimer clock.Timer lastPollTime time.Time } ) func NewImmediateQueue( shard shard.Context, category persistence.HistoryTaskCategory, taskProcessor task.Processor, taskExecutor task.Executor, logger log.Logger, metricsClient metrics.Client, metricsScope metrics.Scope, options *Options, ) Queue { ctx, cancel := context.WithCancel(context.Background()) return &immediateQueue{ base: newQueueBase( shard, taskProcessor, logger, metricsClient, metricsScope, category, taskExecutor, options, ), notifyCh: make(chan struct{}, 1), ctx: ctx, cancel: cancel, } } func (q *immediateQueue) Start() { if !atomic.CompareAndSwapInt32(&q.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } q.base.logger.Info("History queue state changed", tag.LifeCycleStarting) defer q.base.logger.Info("History queue state changed", tag.LifeCycleStarted) q.base.Start() q.shutdownWG.Add(1) go q.processEventLoop() q.notify() } func (q *immediateQueue) Stop() { if !atomic.CompareAndSwapInt32(&q.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } q.base.logger.Info("History queue state changed", tag.LifeCycleStopping) defer q.base.logger.Info("History queue state changed", tag.LifeCycleStopped) q.cancel() q.shutdownWG.Wait() q.base.Stop() } func (q *immediateQueue) Category() persistence.HistoryTaskCategory { return q.base.Category() } func (q *immediateQueue) FailoverDomain(domainIDs map[string]struct{}) { q.base.FailoverDomain(domainIDs) } func (q *immediateQueue) HandleAction(ctx context.Context, clusterName string, action *queue.Action) (*queue.ActionResult, error) { return q.base.HandleAction(ctx, clusterName, action) } func (q *immediateQueue) UnlockTaskProcessing() { q.base.UnlockTaskProcessing() } func (q *immediateQueue) LockTaskProcessing() { q.base.LockTaskProcessing() } func (q *immediateQueue) NotifyNewTask(clusterName string, info *hcommon.NotifyTaskInfo) { numTasks := len(info.Tasks) if numTasks == 0 { return } q.notify() q.base.metricsScope.AddCounter(metrics.NewHistoryTaskCounter, int64(numTasks)) } func (q *immediateQueue) notify() { select { case q.notifyCh <- struct{}{}: default: } } func (q *immediateQueue) processPollTimer() { // NOTE: ideally when new tasks are written to the queue, a notification should be sent to the notifyCh, // thus this periodic poll is not needed, but we keep it for now to provide a fallback mechanism in case // there is a bug in the notification mechanism if q.lastPollTime.Add(q.base.options.PollBackoffInterval()).Before(q.base.timeSource.Now()) { newTasks := q.base.processNewTasks() if newTasks { // TODO: consider changing it to warn level q.base.logger.Info("processing new tasks because poll timer fired") } q.lastPollTime = q.base.timeSource.Now() } q.pollTimer.Reset(backoff.JitDuration( q.base.options.MaxPollInterval(), q.base.options.MaxPollIntervalJitterCoefficient(), )) } func (q *immediateQueue) processEventLoop() { defer q.shutdownWG.Done() q.pollTimer = q.base.timeSource.NewTimer(backoff.JitDuration( q.base.options.MaxPollInterval(), q.base.options.MaxPollIntervalJitterCoefficient(), )) defer q.pollTimer.Stop() for { select { case <-q.notifyCh: q.base.processNewTasks() q.lastPollTime = q.base.timeSource.Now() case <-q.pollTimer.Chan(): q.processPollTimer() case <-q.base.updateQueueStateTimer.Chan(): q.base.updateQueueState(q.ctx) case alert := <-q.base.alertCh: q.base.handleAlert(q.ctx, alert) case <-q.ctx.Done(): return } } } ================================================ FILE: service/history/queuev2/queue_immediate_test.go ================================================ package queuev2 import ( "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) func TestImmediateQueue_LifeCycle(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTaskExecutor := task.NewMockExecutor(ctrl) mockLogger := testlogger.New(t) mockMetricsClient := metrics.NoopClient mockMetricsScope := metrics.NoopScope mockTimeSource := clock.NewMockedTimeSource() mockExecutionManager := persistence.NewMockExecutionManager(ctrl) // Setup mock expectations mockShard.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata).AnyTimes() mockShard.EXPECT().GetTimeSource().Return(mockTimeSource).AnyTimes() mockShard.EXPECT().GetQueueState(persistence.HistoryTaskCategoryTransfer).Return(&types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ rootQueueID: { VirtualSliceStates: []*types.VirtualSliceState{ { TaskRange: &types.TaskRange{ InclusiveMin: &types.TaskKey{ TaskID: 1, }, ExclusiveMax: &types.TaskKey{ TaskID: 10, }, }, }, }, }, }, ExclusiveMaxReadLevel: &types.TaskKey{ TaskID: 10, }, }, nil).AnyTimes() mockShard.EXPECT().GetExecutionManager().Return(mockExecutionManager).AnyTimes() mockExecutionManager.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTasksResponse{}, nil).AnyTimes() mockExecutionManager.EXPECT().RangeCompleteHistoryTask(gomock.Any(), gomock.Any()).Return(&persistence.RangeCompleteHistoryTaskResponse{}, nil).AnyTimes() mockShard.EXPECT().UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryTransfer, cluster.TestCurrentClusterName).Return(persistence.NewImmediateTaskKey(10)).AnyTimes() mockShard.EXPECT().UpdateQueueState(persistence.HistoryTaskCategoryTransfer, gomock.Any()).Return(nil).AnyTimes() options := &Options{ DeleteBatchSize: dynamicproperties.GetIntPropertyFn(100), RedispatchInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PageSize: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), MaxPollInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), MaxPollIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.1), UpdateAckInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), UpdateAckIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.1), MaxPollRPS: dynamicproperties.GetIntPropertyFn(100), MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), CriticalPendingTaskCount: dynamicproperties.GetIntPropertyFn(90), VirtualSliceForceAppendInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), EnablePendingTaskCountAlert: func() bool { return true }, MaxVirtualQueueCount: dynamicproperties.GetIntPropertyFn(2), } queue := NewImmediateQueue( mockShard, persistence.HistoryTaskCategoryTransfer, mockTaskProcessor, mockTaskExecutor, mockLogger, mockMetricsClient, mockMetricsScope, options, ).(*immediateQueue) // Test Start queue.Start() assert.Equal(t, common.DaemonStatusStarted, atomic.LoadInt32(&queue.status)) // Test NotifyNewTask queue.NotifyNewTask("test-cluster", &hcommon.NotifyTaskInfo{ Tasks: []persistence.Task{ &persistence.DecisionTask{}, }, }) // Advance time to trigger poll mockTimeSource.Advance(options.MaxPollInterval() * 2) // Advance time to trigger update ack mockTimeSource.Advance(options.UpdateAckInterval() * 2) // Test Stop queue.Stop() assert.Equal(t, common.DaemonStatusStopped, atomic.LoadInt32(&queue.status)) } ================================================ FILE: service/history/queuev2/queue_reader.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination queue_reader_mock.go github.com/uber/cadence/service/history/queuev2 QueueReader package queuev2 import ( "context" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/shard" ) type ( QueueReader interface { GetTask(context.Context, *GetTaskRequest) (*GetTaskResponse, error) } GetTaskRequest struct { Progress *GetTaskProgress Predicate Predicate PageSize int } // GetTaskProgress contains the range of the slice to read, the next page token, and the next task key GetTaskProgress struct { Range NextPageToken []byte NextTaskKey persistence.HistoryTaskKey } GetTaskResponse struct { Tasks []persistence.Task Progress *GetTaskProgress } simpleQueueReader struct { shard shard.Context category persistence.HistoryTaskCategory } ) func NewQueueReader( shard shard.Context, category persistence.HistoryTaskCategory, ) QueueReader { return &simpleQueueReader{ shard: shard, category: category, } } func (r *simpleQueueReader) GetTask(ctx context.Context, req *GetTaskRequest) (*GetTaskResponse, error) { resp, err := r.shard.GetExecutionManager().GetHistoryTasks(ctx, &persistence.GetHistoryTasksRequest{ TaskCategory: r.category, InclusiveMinTaskKey: req.Progress.InclusiveMinTaskKey, ExclusiveMaxTaskKey: req.Progress.ExclusiveMaxTaskKey, PageSize: req.PageSize, NextPageToken: req.Progress.NextPageToken, }) if err != nil { return nil, err } nextTaskKey := req.Progress.ExclusiveMaxTaskKey tasks := make([]persistence.Task, 0, len(resp.Tasks)) for _, task := range resp.Tasks { // filter out tasks that don't match the predicate if req.Predicate.Check(task) { tasks = append(tasks, task) } } // If there are more tasks to read, set the next task key to the next task key of the last task if len(resp.NextPageToken) != 0 && len(resp.Tasks) > 0 { nextTaskKey = resp.Tasks[len(resp.Tasks)-1].GetTaskKey().Next() } return &GetTaskResponse{ Tasks: tasks, Progress: &GetTaskProgress{ Range: req.Progress.Range, NextPageToken: resp.NextPageToken, NextTaskKey: nextTaskKey, }, }, nil } ================================================ FILE: service/history/queuev2/queue_reader_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/queuev2 (interfaces: QueueReader) // // Generated by this command: // // mockgen -package queuev2 -destination queue_reader_mock.go github.com/uber/cadence/service/history/queuev2 QueueReader // // Package queuev2 is a generated GoMock package. package queuev2 import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockQueueReader is a mock of QueueReader interface. type MockQueueReader struct { ctrl *gomock.Controller recorder *MockQueueReaderMockRecorder isgomock struct{} } // MockQueueReaderMockRecorder is the mock recorder for MockQueueReader. type MockQueueReaderMockRecorder struct { mock *MockQueueReader } // NewMockQueueReader creates a new mock instance. func NewMockQueueReader(ctrl *gomock.Controller) *MockQueueReader { mock := &MockQueueReader{ctrl: ctrl} mock.recorder = &MockQueueReaderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQueueReader) EXPECT() *MockQueueReaderMockRecorder { return m.recorder } // GetTask mocks base method. func (m *MockQueueReader) GetTask(arg0 context.Context, arg1 *GetTaskRequest) (*GetTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTask", arg0, arg1) ret0, _ := ret[0].(*GetTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTask indicates an expected call of GetTask. func (mr *MockQueueReaderMockRecorder) GetTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTask", reflect.TypeOf((*MockQueueReader)(nil).GetTask), arg0, arg1) } ================================================ FILE: service/history/queuev2/queue_reader_test.go ================================================ package queuev2 import ( "context" "math" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/shard" ) func TestQueueReader_GetTask(t *testing.T) { historyTasks := []persistence.Task{ &persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "test-workflow", RunID: "test-run", }, TaskData: persistence.TaskData{ Version: 1, TaskID: 1, VisibilityTimestamp: time.Now().UTC(), }, }, &persistence.ActivityTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "test-workflow", RunID: "test-run", }, TaskData: persistence.TaskData{ Version: 1, TaskID: 2, VisibilityTimestamp: time.Now().UTC(), }, }, } tests := []struct { name string request *GetTaskRequest mockSetup func(*shard.MockContext, *persistence.MockExecutionManager, *MockPredicate) expectedTasks []persistence.Task expectedToken []byte expectedNextKey persistence.HistoryTaskKey expectedError error }{ { name: "successful task retrieval", request: &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 10), }, NextPageToken: nil, NextTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), }, PageSize: 10, }, mockSetup: func(mockShard *shard.MockContext, mockExec *persistence.MockExecutionManager, mockPredicate *MockPredicate) { mockShard.EXPECT().GetExecutionManager().Return(mockExec) mockExec.EXPECT().GetHistoryTasks(gomock.Any(), &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 10), PageSize: 10, NextPageToken: nil, }).Return(&persistence.GetHistoryTasksResponse{ Tasks: historyTasks, NextPageToken: []byte("next-page"), }, nil) mockPredicate.EXPECT().Check(gomock.Any()).Return(true).AnyTimes() }, expectedTasks: historyTasks, expectedToken: []byte("next-page"), expectedNextKey: historyTasks[1].GetTaskKey().Next(), }, { name: "successful task retrieval with empty next page token", request: &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 10), }, NextPageToken: nil, NextTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), }, PageSize: 10, }, mockSetup: func(mockShard *shard.MockContext, mockExec *persistence.MockExecutionManager, mockPredicate *MockPredicate) { mockShard.EXPECT().GetExecutionManager().Return(mockExec) mockExec.EXPECT().GetHistoryTasks(gomock.Any(), &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 10), PageSize: 10, NextPageToken: nil, }).Return(&persistence.GetHistoryTasksResponse{ Tasks: historyTasks, NextPageToken: nil, }, nil) mockPredicate.EXPECT().Check(gomock.Any()).Return(true).AnyTimes() }, expectedTasks: historyTasks, expectedToken: nil, expectedNextKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 10), }, { name: "task filtering with predicate", request: &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 10), }, NextPageToken: nil, NextTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), }, PageSize: 10, }, mockSetup: func(mockShard *shard.MockContext, mockExec *persistence.MockExecutionManager, mockPredicate *MockPredicate) { mockShard.EXPECT().GetExecutionManager().Return(mockExec) mockExec.EXPECT().GetHistoryTasks(gomock.Any(), &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 10), PageSize: 10, NextPageToken: nil, }).Return(&persistence.GetHistoryTasksResponse{ Tasks: historyTasks, NextPageToken: []byte("next-page"), }, nil) mockPredicate.EXPECT().Check(historyTasks[0]).Return(true) mockPredicate.EXPECT().Check(historyTasks[1]).Return(false) }, expectedTasks: []persistence.Task{historyTasks[0]}, expectedToken: []byte("next-page"), expectedNextKey: historyTasks[1].GetTaskKey().Next(), }, { name: "error from execution manager", request: &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 10), }, NextPageToken: nil, NextTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), }, PageSize: 10, }, mockSetup: func(mockShard *shard.MockContext, mockExec *persistence.MockExecutionManager, mockPredicate *MockPredicate) { mockShard.EXPECT().GetExecutionManager().Return(mockExec) mockExec.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) }, expectedError: assert.AnError, }, { name: "empty task list", request: &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 10), }, NextPageToken: nil, NextTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), }, PageSize: 10, }, mockSetup: func(mockShard *shard.MockContext, mockExec *persistence.MockExecutionManager, mockPredicate *MockPredicate) { mockShard.EXPECT().GetExecutionManager().Return(mockExec) mockExec.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{}, NextPageToken: nil, }, nil) }, expectedTasks: []persistence.Task{}, expectedToken: nil, expectedNextKey: persistence.NewHistoryTaskKey(time.Unix(0, math.MaxInt64), 10), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { controller := gomock.NewController(t) mockShard := shard.NewMockContext(controller) mockExec := persistence.NewMockExecutionManager(controller) mockPredicate := NewMockPredicate(controller) tt.mockSetup(mockShard, mockExec, mockPredicate) reader := NewQueueReader(mockShard, persistence.HistoryTaskCategoryTimer) tt.request.Predicate = mockPredicate resp, err := reader.GetTask(context.Background(), tt.request) if tt.expectedError != nil { require.Error(t, err) assert.Equal(t, tt.expectedError, err) return } require.NoError(t, err) assert.Equal(t, tt.expectedTasks, resp.Tasks) assert.Equal(t, tt.expectedToken, resp.Progress.NextPageToken) assert.Equal(t, tt.expectedNextKey, resp.Progress.NextTaskKey) }) } } ================================================ FILE: service/history/queuev2/queue_scheduled.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queuev2 import ( "context" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) type ( scheduledQueue struct { base *queueBase timerGate clock.TimerGate newTimerCh chan struct{} nextTimeLock sync.RWMutex nextTime time.Time status int32 shutdownWG sync.WaitGroup ctx context.Context cancel func() } ) func NewScheduledQueue( shard shard.Context, category persistence.HistoryTaskCategory, taskProcessor task.Processor, taskExecutor task.Executor, logger log.Logger, metricsClient metrics.Client, metricsScope metrics.Scope, options *Options, ) Queue { ctx, cancel := context.WithCancel(context.Background()) return &scheduledQueue{ base: newQueueBase( shard, taskProcessor, logger, metricsClient, metricsScope, category, taskExecutor, options, ), timerGate: clock.NewTimerGate(shard.GetTimeSource()), newTimerCh: make(chan struct{}, 1), ctx: ctx, cancel: cancel, status: common.DaemonStatusInitialized, } } func (q *scheduledQueue) Start() { if !atomic.CompareAndSwapInt32(&q.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } q.base.logger.Info("History queue state changed", tag.LifeCycleStarting) defer q.base.logger.Info("History queue state changed", tag.LifeCycleStarted) q.base.Start() q.shutdownWG.Add(1) go q.processEventLoop() q.notify(time.Time{}) } func (q *scheduledQueue) Stop() { if !atomic.CompareAndSwapInt32(&q.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } q.base.logger.Info("History queue state changed", tag.LifeCycleStopping) defer q.base.logger.Info("History queue state changed", tag.LifeCycleStopped) q.cancel() q.timerGate.Stop() q.shutdownWG.Wait() q.base.Stop() } func (q *scheduledQueue) Category() persistence.HistoryTaskCategory { return q.base.Category() } func (q *scheduledQueue) FailoverDomain(domainIDs map[string]struct{}) { q.base.FailoverDomain(domainIDs) } func (q *scheduledQueue) HandleAction(ctx context.Context, clusterName string, action *queue.Action) (*queue.ActionResult, error) { return q.base.HandleAction(ctx, clusterName, action) } func (q *scheduledQueue) LockTaskProcessing() { q.base.LockTaskProcessing() } func (q *scheduledQueue) UnlockTaskProcessing() { q.base.UnlockTaskProcessing() } func (q *scheduledQueue) NotifyNewTask(clusterName string, info *hcommon.NotifyTaskInfo) { numTasks := len(info.Tasks) if numTasks == 0 { return } nextTime := info.Tasks[0].GetVisibilityTimestamp() for i := 1; i < numTasks; i++ { ts := info.Tasks[i].GetVisibilityTimestamp() if ts.Before(nextTime) { nextTime = ts } } q.notify(nextTime) q.base.metricsScope.AddCounter(metrics.NewHistoryTaskCounter, int64(numTasks)) } func (q *scheduledQueue) notify(t time.Time) { q.nextTimeLock.Lock() defer q.nextTimeLock.Unlock() if q.nextTime.IsZero() || t.Before(q.nextTime) { q.nextTime = t select { case q.newTimerCh <- struct{}{}: default: } } } func (q *scheduledQueue) processNextTime() { q.nextTimeLock.Lock() nextTime := q.nextTime q.nextTime = time.Time{} q.nextTimeLock.Unlock() q.timerGate.Update(nextTime) } func (q *scheduledQueue) processEventLoop() { defer q.shutdownWG.Done() for { select { case <-q.newTimerCh: q.processNextTime() case <-q.timerGate.Chan(): q.base.processNewTasks() q.lookAheadTask() case <-q.base.updateQueueStateTimer.Chan(): q.base.updateQueueState(q.ctx) case alert := <-q.base.alertCh: q.base.handleAlert(q.ctx, alert) case <-q.ctx.Done(): return } } } func (q *scheduledQueue) lookAheadTask() { lookAheadMinTime := q.base.newVirtualSliceState.Range.InclusiveMinTaskKey.GetScheduledTime() lookAheadMaxTime := lookAheadMinTime.Add(backoff.JitDuration( q.base.options.MaxPollInterval(), q.base.options.MaxPollIntervalJitterCoefficient(), )) resp, err := q.base.queueReader.GetTask(q.ctx, &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(lookAheadMinTime, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(lookAheadMaxTime, 0), }, NextTaskKey: persistence.NewHistoryTaskKey(lookAheadMaxTime, 0), }, Predicate: NewUniversalPredicate(), PageSize: 1, }) if err != nil { q.timerGate.Update(lookAheadMinTime) q.base.logger.Error("Failed to look ahead task", tag.Error(err)) return } if len(resp.Tasks) == 0 { q.timerGate.Update(lookAheadMaxTime) return } q.timerGate.Update(resp.Tasks[0].GetVisibilityTimestamp()) } ================================================ FILE: service/history/queuev2/queue_scheduled_test.go ================================================ package queuev2 import ( "errors" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" hcommon "github.com/uber/cadence/service/history/common" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" ) func TestScheduledQueue_LifeCycle(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockTaskProcessor := task.NewMockProcessor(ctrl) mockTaskExecutor := task.NewMockExecutor(ctrl) mockLogger := testlogger.New(t) mockMetricsClient := metrics.NoopClient mockMetricsScope := metrics.NoopScope mockTimeSource := clock.NewMockedTimeSource() mockExecutionManager := persistence.NewMockExecutionManager(ctrl) // Setup mock expectations mockShard.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata).AnyTimes() mockShard.EXPECT().GetTimeSource().Return(mockTimeSource).AnyTimes() mockShard.EXPECT().GetQueueState(persistence.HistoryTaskCategoryTimer).Return(&types.QueueState{ VirtualQueueStates: map[int64]*types.VirtualQueueState{ rootQueueID: { VirtualSliceStates: []*types.VirtualSliceState{ { TaskRange: &types.TaskRange{ InclusiveMin: &types.TaskKey{ ScheduledTimeNano: mockTimeSource.Now().Add(-1 * time.Hour).UnixNano(), }, ExclusiveMax: &types.TaskKey{ ScheduledTimeNano: mockTimeSource.Now().UnixNano(), }, }, }, }, }, }, ExclusiveMaxReadLevel: &types.TaskKey{ ScheduledTimeNano: mockTimeSource.Now().UnixNano(), }, }, nil).AnyTimes() mockShard.EXPECT().GetExecutionManager().Return(mockExecutionManager).AnyTimes() mockExecutionManager.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Any()).Return(&persistence.GetHistoryTasksResponse{}, nil).AnyTimes() mockExecutionManager.EXPECT().RangeCompleteHistoryTask(gomock.Any(), gomock.Any()).Return(&persistence.RangeCompleteHistoryTaskResponse{}, nil).AnyTimes() mockShard.EXPECT().UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryTimer, cluster.TestCurrentClusterName).Return(persistence.NewHistoryTaskKey(time.Now(), 10)).AnyTimes() mockShard.EXPECT().UpdateQueueState(persistence.HistoryTaskCategoryTimer, gomock.Any()).Return(nil).AnyTimes() options := &Options{ DeleteBatchSize: dynamicproperties.GetIntPropertyFn(100), RedispatchInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PageSize: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), MaxPollInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), MaxPollIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.1), UpdateAckInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), UpdateAckIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.1), MaxPollRPS: dynamicproperties.GetIntPropertyFn(100), MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), VirtualSliceForceAppendInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), CriticalPendingTaskCount: dynamicproperties.GetIntPropertyFn(90), EnablePendingTaskCountAlert: func() bool { return true }, MaxVirtualQueueCount: dynamicproperties.GetIntPropertyFn(2), } queue := NewScheduledQueue( mockShard, persistence.HistoryTaskCategoryTimer, mockTaskProcessor, mockTaskExecutor, mockLogger, mockMetricsClient, mockMetricsScope, options, ).(*scheduledQueue) // Test Start queue.Start() assert.Equal(t, common.DaemonStatusStarted, atomic.LoadInt32(&queue.status)) queue.NotifyNewTask("test-cluster", &hcommon.NotifyTaskInfo{ Tasks: []persistence.Task{ &persistence.DecisionTimeoutTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: time.Time{}, }, }, }, }) // Advance time to trigger poll mockTimeSource.Advance(options.MaxPollInterval() * 2) // Advance time to trigger update ack mockTimeSource.Advance(options.UpdateAckInterval() * 2) // Test Stop queue.Stop() assert.Equal(t, common.DaemonStatusStopped, atomic.LoadInt32(&queue.status)) } func TestScheduledQueue_LookAheadTask(t *testing.T) { defer goleak.VerifyNone(t) tests := []struct { name string setupMocks func(*gomock.Controller, *MockQueueReader, clock.TimeSource, *clock.MockTimerGate) expectedUpdate time.Time }{ { name: "successful look ahead with future task", setupMocks: func(ctrl *gomock.Controller, mockReader *MockQueueReader, mockTimeSource clock.TimeSource, mockTimerGate *clock.MockTimerGate) { now := mockTimeSource.Now() futureTime := now.Add(time.Hour) mockReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(now, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(now.Add(time.Second*10), 0), }, NextTaskKey: persistence.NewHistoryTaskKey(now.Add(time.Second*10), 0), }, Predicate: NewUniversalPredicate(), PageSize: 1, }).Return(&GetTaskResponse{ Tasks: []persistence.Task{ &persistence.DecisionTimeoutTask{ TaskData: persistence.TaskData{ VisibilityTimestamp: futureTime, }, }, }, }, nil) mockTimerGate.EXPECT().Update(futureTime) }, }, { name: "no tasks found", setupMocks: func(ctrl *gomock.Controller, mockReader *MockQueueReader, mockTimeSource clock.TimeSource, mockTimerGate *clock.MockTimerGate) { now := mockTimeSource.Now() mockReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(now, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(now.Add(time.Second*10), 0), }, NextTaskKey: persistence.NewHistoryTaskKey(now.Add(time.Second*10), 0), }, Predicate: NewUniversalPredicate(), PageSize: 1, }).Return(&GetTaskResponse{ Tasks: []persistence.Task{}, }, nil) mockTimerGate.EXPECT().Update(now.Add(time.Second * 10)) }, }, { name: "error during look ahead", setupMocks: func(ctrl *gomock.Controller, mockReader *MockQueueReader, mockTimeSource clock.TimeSource, mockTimerGate *clock.MockTimerGate) { now := mockTimeSource.Now() mockReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(now, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(now.Add(time.Second*10), 0), }, NextTaskKey: persistence.NewHistoryTaskKey(now.Add(time.Second*10), 0), }, Predicate: NewUniversalPredicate(), PageSize: 1, }).Return(nil, errors.New("test error")) mockTimerGate.EXPECT().Update(now) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockShard := shard.NewMockContext(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockTimeSource := clock.NewMockedTimeSource() mockQueueReader := NewMockQueueReader(ctrl) mockTimerGate := clock.NewMockTimerGate(ctrl) // Setup mock expectations mockShard.EXPECT().GetTimeSource().Return(mockTimeSource).AnyTimes() mockShard.EXPECT().GetClusterMetadata().Return(cluster.TestActiveClusterMetadata).AnyTimes() options := &Options{ DeleteBatchSize: dynamicproperties.GetIntPropertyFn(100), RedispatchInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PageSize: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), MaxPollInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), MaxPollIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), UpdateAckInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), UpdateAckIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.1), } now := mockTimeSource.Now() // Create scheduled queue directly queue := &scheduledQueue{ base: &queueBase{ logger: mockLogger, metricsScope: mockMetricsScope, category: persistence.HistoryTaskCategoryTimer, options: options, queueReader: mockQueueReader, newVirtualSliceState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(now, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(now.Add(time.Hour), 0), }, Predicate: NewUniversalPredicate(), }, }, timerGate: mockTimerGate, newTimerCh: make(chan struct{}, 1), } // Setup test-specific mocks and get expected update time tt.setupMocks(ctrl, mockQueueReader, mockTimeSource, mockTimerGate) // Execute lookAheadTask queue.lookAheadTask() }) } } ================================================ FILE: service/history/queuev2/queue_state.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queuev2 import "github.com/uber/cadence/common/persistence" type QueueState struct { VirtualQueueStates map[int64][]VirtualSliceState ExclusiveMaxReadLevel persistence.HistoryTaskKey } type VirtualSliceState struct { Range Range Predicate Predicate } func (s *VirtualSliceState) IsEmpty() bool { return s.Range.IsEmpty() || s.Predicate.IsEmpty() } func (s *VirtualSliceState) Contains(task persistence.Task) bool { return s.Range.Contains(task.GetTaskKey()) && s.Predicate.Check(task) } func (s *VirtualSliceState) TrySplitByTaskKey(taskKey persistence.HistoryTaskKey) (VirtualSliceState, VirtualSliceState, bool) { if !s.Range.CanSplitByTaskKey(taskKey) { return VirtualSliceState{}, VirtualSliceState{}, false } return VirtualSliceState{ Range: Range{InclusiveMinTaskKey: s.Range.InclusiveMinTaskKey, ExclusiveMaxTaskKey: taskKey}, Predicate: s.Predicate, }, VirtualSliceState{ Range: Range{InclusiveMinTaskKey: taskKey, ExclusiveMaxTaskKey: s.Range.ExclusiveMaxTaskKey}, Predicate: s.Predicate, }, true } func (s *VirtualSliceState) TrySplitByPredicate(predicate Predicate) (VirtualSliceState, VirtualSliceState, bool) { if predicate.Equals(&universalPredicate{}) || predicate.Equals(&emptyPredicate{}) || predicate.Equals(s.Predicate) { return VirtualSliceState{}, VirtualSliceState{}, false } return VirtualSliceState{ Range: s.Range, Predicate: And(s.Predicate, predicate), }, VirtualSliceState{ Range: s.Range, Predicate: And(s.Predicate, Not(predicate)), }, true } ================================================ FILE: service/history/queuev2/queue_state_test.go ================================================ package queuev2 import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" ) func TestQueueState_IsEmpty(t *testing.T) { state := &VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), }, Predicate: NewUniversalPredicate(), } assert.True(t, state.IsEmpty()) ctrl := gomock.NewController(t) mockPredicate := NewMockPredicate(ctrl) mockPredicate.EXPECT().IsEmpty().Return(true) state = &VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 100), }, Predicate: mockPredicate, } assert.True(t, state.IsEmpty()) } func TestVirtualSliceState_Contains(t *testing.T) { ctrl := gomock.NewController(t) mockPredicate := NewMockPredicate(ctrl) mockPredicate.EXPECT().Check(gomock.Any()).Return(true).AnyTimes() state := &VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 100), }, Predicate: mockPredicate, } assert.True(t, state.Contains(&persistence.DecisionTimeoutTask{ TaskData: persistence.TaskData{ TaskID: 1, VisibilityTimestamp: time.Unix(0, 1), }, })) assert.False(t, state.Contains(&persistence.DecisionTimeoutTask{ TaskData: persistence.TaskData{ TaskID: 100, VisibilityTimestamp: time.Unix(0, 10), }, })) assert.False(t, state.Contains(&persistence.DecisionTimeoutTask{ TaskData: persistence.TaskData{ TaskID: 101, VisibilityTimestamp: time.Unix(0, 10), }, })) assert.False(t, state.Contains(&persistence.DecisionTimeoutTask{ TaskData: persistence.TaskData{ TaskID: 101, VisibilityTimestamp: time.Unix(0, 0), }, })) } func TestVirtualSliceState_TrySplitByTaskKey(t *testing.T) { state := &VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 100), }, Predicate: NewUniversalPredicate(), } split1, split2, ok := state.TrySplitByTaskKey(persistence.NewHistoryTaskKey(time.Unix(0, 5), 50)) assert.True(t, ok) assert.Equal(t, Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 5), 50), }, split1.Range) assert.Equal(t, Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 5), 50), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 100), }, split2.Range) _, _, ok = state.TrySplitByTaskKey(persistence.NewHistoryTaskKey(time.Unix(0, 10), 100)) assert.False(t, ok) _, _, ok = state.TrySplitByTaskKey(persistence.NewHistoryTaskKey(time.Unix(0, 1), 0)) assert.False(t, ok) _, _, ok = state.TrySplitByTaskKey(persistence.NewHistoryTaskKey(time.Unix(0, 11), 100)) assert.False(t, ok) _, _, ok = state.TrySplitByTaskKey(persistence.NewHistoryTaskKey(time.Unix(0, 0), 101)) assert.False(t, ok) } func TestVirtualSliceState_TrySplitByPredicate(t *testing.T) { baseRange := Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 100), } basePredicate := NewDomainIDPredicate([]string{"domain1", "domain2"}, false) tests := []struct { name string state VirtualSliceState splitPredicate Predicate expectedSplit bool expectedFirst VirtualSliceState expectedSecond VirtualSliceState }{ { name: "universal predicate should not split", state: VirtualSliceState{ Range: baseRange, Predicate: basePredicate, }, splitPredicate: NewUniversalPredicate(), expectedSplit: false, expectedFirst: VirtualSliceState{}, expectedSecond: VirtualSliceState{}, }, { name: "empty predicate should not split", state: VirtualSliceState{ Range: baseRange, Predicate: basePredicate, }, splitPredicate: NewEmptyPredicate(), expectedSplit: false, expectedFirst: VirtualSliceState{}, expectedSecond: VirtualSliceState{}, }, { name: "identical predicate should not split", state: VirtualSliceState{ Range: baseRange, Predicate: basePredicate, }, splitPredicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), expectedSplit: false, expectedFirst: VirtualSliceState{}, expectedSecond: VirtualSliceState{}, }, { name: "different predicate should split successfully", state: VirtualSliceState{ Range: baseRange, Predicate: basePredicate, }, splitPredicate: NewDomainIDPredicate([]string{"domain3"}, false), expectedSplit: true, expectedFirst: VirtualSliceState{ Range: baseRange, Predicate: And(basePredicate, NewDomainIDPredicate([]string{"domain3"}, false)), }, expectedSecond: VirtualSliceState{ Range: baseRange, Predicate: And(basePredicate, Not(NewDomainIDPredicate([]string{"domain3"}, false))), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { first, second, split := tt.state.TrySplitByPredicate(tt.splitPredicate) assert.Equal(t, tt.expectedSplit, split) if tt.expectedSplit { assert.Equal(t, tt.expectedFirst.Range, first.Range) assert.Equal(t, tt.expectedSecond.Range, second.Range) // For predicates, we check if they produce the same results rather than exact equality // since the And and Not operations create new predicate instances assert.True(t, tt.expectedFirst.Predicate.Equals(first.Predicate)) assert.True(t, tt.expectedSecond.Predicate.Equals(second.Predicate)) } else { assert.Equal(t, tt.expectedFirst, first) assert.Equal(t, tt.expectedSecond, second) } }) } } ================================================ FILE: service/history/queuev2/range.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queuev2 import ( "github.com/uber/cadence/common/persistence" ) type Range struct { InclusiveMinTaskKey persistence.HistoryTaskKey ExclusiveMaxTaskKey persistence.HistoryTaskKey } func (r *Range) IsEmpty() bool { return r.InclusiveMinTaskKey.Compare(r.ExclusiveMaxTaskKey) >= 0 } func (r *Range) Contains(taskKey persistence.HistoryTaskKey) bool { return taskKey.Compare(r.InclusiveMinTaskKey) >= 0 && taskKey.Compare(r.ExclusiveMaxTaskKey) < 0 } func (r *Range) ContainsRange(other Range) bool { return r.InclusiveMinTaskKey.Compare(other.InclusiveMinTaskKey) <= 0 && r.ExclusiveMaxTaskKey.Compare(other.ExclusiveMaxTaskKey) >= 0 } func (r *Range) CanMerge(other Range) bool { return r.InclusiveMinTaskKey.Compare(other.ExclusiveMaxTaskKey) <= 0 && r.ExclusiveMaxTaskKey.Compare(other.InclusiveMinTaskKey) >= 0 } func (r *Range) CanSplitByTaskKey(taskKey persistence.HistoryTaskKey) bool { return taskKey.Compare(r.InclusiveMinTaskKey) > 0 && taskKey.Compare(r.ExclusiveMaxTaskKey) < 0 } ================================================ FILE: service/history/queuev2/range_test.go ================================================ package queuev2 import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/persistence" ) func TestRange_IsEmpty(t *testing.T) { r := Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(2), } assert.False(t, r.IsEmpty()) r = Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(1), } assert.True(t, r.IsEmpty()) r = Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), } assert.True(t, r.IsEmpty()) r = Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(1, 0), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(2, 0), 1), } assert.False(t, r.IsEmpty()) } func TestRange_Contains(t *testing.T) { r := Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(2), } assert.True(t, r.Contains(persistence.NewImmediateTaskKey(1))) assert.False(t, r.Contains(persistence.NewImmediateTaskKey(2))) assert.False(t, r.Contains(persistence.NewImmediateTaskKey(0))) assert.False(t, r.Contains(persistence.NewImmediateTaskKey(3))) r = Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 100), 0), } assert.False(t, r.Contains(persistence.NewHistoryTaskKey(time.Unix(0, 0), 0))) assert.True(t, r.Contains(persistence.NewHistoryTaskKey(time.Unix(0, 1), 0))) assert.True(t, r.Contains(persistence.NewHistoryTaskKey(time.Unix(0, 1), 1000000))) assert.True(t, r.Contains(persistence.NewHistoryTaskKey(time.Unix(0, 99), 1000000))) assert.False(t, r.Contains(persistence.NewHistoryTaskKey(time.Unix(0, 100), 0))) assert.False(t, r.Contains(persistence.NewHistoryTaskKey(time.Unix(0, 101), 0))) } func TestRange_ContainsRange(t *testing.T) { r := Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), } assert.True(t, r.ContainsRange(r)) assert.True(t, r.ContainsRange(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), })) assert.True(t, r.ContainsRange(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), })) assert.False(t, r.ContainsRange(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(15), })) assert.False(t, r.ContainsRange(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(0), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(1), })) r = Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 100), } assert.True(t, r.ContainsRange(r)) assert.True(t, r.ContainsRange(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 5), 0), })) assert.True(t, r.ContainsRange(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 5), 101), })) assert.True(t, r.ContainsRange(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 5), 1110), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 100), })) assert.False(t, r.ContainsRange(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 15), 100), })) assert.False(t, r.ContainsRange(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 100), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 101), })) } func TestRange_CanMerge(t *testing.T) { r := Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), } assert.True(t, r.CanMerge(r)) assert.True(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), })) assert.True(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(0), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), })) assert.True(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(12), })) assert.True(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(10), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), })) assert.True(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(0), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(1), })) assert.False(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(0), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(0), })) assert.False(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(15), })) r = Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 100), } assert.True(t, r.CanMerge(r)) assert.True(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 99), })) assert.True(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 100), 100), })) assert.True(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 100), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), })) assert.True(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 100), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 100), 100), })) assert.False(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 101), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 100), 100), })) assert.False(t, r.CanMerge(Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 0), 1), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), -1), })) } func TestRange_CanSplitByTaskKey(t *testing.T) { r := Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), } assert.True(t, r.CanSplitByTaskKey(persistence.NewImmediateTaskKey(5))) assert.False(t, r.CanSplitByTaskKey(persistence.NewImmediateTaskKey(1))) assert.False(t, r.CanSplitByTaskKey(persistence.NewImmediateTaskKey(10))) assert.False(t, r.CanSplitByTaskKey(persistence.NewImmediateTaskKey(0))) assert.False(t, r.CanSplitByTaskKey(persistence.NewImmediateTaskKey(11))) r = Range{ InclusiveMinTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 1), 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(time.Unix(0, 10), 100), } assert.True(t, r.CanSplitByTaskKey(persistence.NewHistoryTaskKey(time.Unix(0, 5), 100))) assert.False(t, r.CanSplitByTaskKey(persistence.NewHistoryTaskKey(time.Unix(0, 1), 0))) assert.False(t, r.CanSplitByTaskKey(persistence.NewHistoryTaskKey(time.Unix(0, 10), 100))) assert.False(t, r.CanSplitByTaskKey(persistence.NewHistoryTaskKey(time.Unix(0, 0), 0))) assert.False(t, r.CanSplitByTaskKey(persistence.NewHistoryTaskKey(time.Unix(0, 10), 101))) } ================================================ FILE: service/history/queuev2/timer_queue_factory.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queuev2 import ( "context" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" "github.com/uber/cadence/service/worker/archiver" ) type ( timerQueueFactory struct { taskProcessor task.Processor archivalClient archiver.Client } ) func NewTimerQueueFactory( taskProcessor task.Processor, archivalClient archiver.Client, ) queue.Factory { return &timerQueueFactory{ taskProcessor: taskProcessor, archivalClient: archivalClient, } } func (f *timerQueueFactory) Category() persistence.HistoryTaskCategory { return persistence.HistoryTaskCategoryTimer } func (f *timerQueueFactory) isQueueV2Enabled(shard shard.Context) bool { return shard.GetConfig().EnableTimerQueueV2(shard.GetShardID()) } func (f *timerQueueFactory) CreateQueue( shard shard.Context, executionCache execution.Cache, openExecutionCheck invariant.Invariant, ) queue.Processor { if f.isQueueV2Enabled(shard) { return f.createQueuev2(shard, executionCache, openExecutionCheck) } return f.createQueuev1(shard, executionCache, openExecutionCheck) } func (f *timerQueueFactory) createQueuev1( shard shard.Context, executionCache execution.Cache, openExecutionCheck invariant.Invariant, ) queue.Processor { return queue.NewTimerQueueProcessor( shard, f.taskProcessor, executionCache, f.archivalClient, openExecutionCheck, ) } func (f *timerQueueFactory) createQueuev2( shard shard.Context, executionCache execution.Cache, openExecutionCheck invariant.Invariant, ) queue.Processor { logger := shard.GetLogger().WithTags(tag.ComponentTimerQueueV2) activeTaskExecutor := task.NewTimerActiveTaskExecutor( shard, f.archivalClient, executionCache, logger, shard.GetMetricsClient(), shard.GetConfig(), ) historyResender := ndc.NewHistoryResender( shard.GetDomainCache(), shard.GetService().GetClientBean(), func(ctx context.Context, request *types.ReplicateEventsV2Request) error { return shard.GetEngine().ReplicateEventsV2(ctx, request) }, shard.GetConfig().StandbyTaskReReplicationContextTimeout, openExecutionCheck, logger, ) standbyTaskExecutor := task.NewTimerStandbyTaskExecutor( shard, f.archivalClient, executionCache, historyResender, logger, shard.GetMetricsClient(), shard.GetClusterMetadata().GetCurrentClusterName(), shard.GetConfig(), ) executorWrapper := task.NewExecutorWrapper( shard.GetClusterMetadata().GetCurrentClusterName(), shard.GetActiveClusterManager(), activeTaskExecutor, standbyTaskExecutor, logger, ) config := shard.GetConfig() return NewScheduledQueue( shard, persistence.HistoryTaskCategoryTimer, f.taskProcessor, executorWrapper, logger, shard.GetMetricsClient(), shard.GetMetricsClient().Scope(metrics.TimerQueueProcessorV2Scope).Tagged(metrics.ShardIDTag(shard.GetShardID())), &Options{ PageSize: config.TimerTaskBatchSize, DeleteBatchSize: config.TimerTaskDeleteBatchSize, MaxPollRPS: config.TimerProcessorMaxPollRPS, MaxPollInterval: config.TimerProcessorMaxPollInterval, MaxPollIntervalJitterCoefficient: config.TimerProcessorMaxPollIntervalJitterCoefficient, UpdateAckInterval: config.TimerProcessorUpdateAckInterval, UpdateAckIntervalJitterCoefficient: config.TimerProcessorUpdateAckIntervalJitterCoefficient, MaxPendingTasksCount: config.QueueMaxPendingTaskCount, PollBackoffInterval: config.QueueProcessorPollBackoffInterval, PollBackoffIntervalJitterCoefficient: config.QueueProcessorPollBackoffIntervalJitterCoefficient, VirtualSliceForceAppendInterval: config.VirtualSliceForceAppendInterval, MaxStartJitterInterval: dynamicproperties.GetDurationPropertyFn(0), RedispatchInterval: config.ActiveTaskRedispatchInterval, CriticalPendingTaskCount: config.QueueCriticalPendingTaskCount, EnablePendingTaskCountAlert: func() bool { return config.EnableTimerQueueV2PendingTaskCountAlert(shard.GetShardID()) }, MaxVirtualQueueCount: config.QueueMaxVirtualQueueCount, }, ) } ================================================ FILE: service/history/queuev2/timer_queue_factory_test.go ================================================ package queuev2 import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" "github.com/uber/cadence/service/worker/archiver" ) func TestTimerQueueFactory_CreateQueuev2(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShard := shard.NewTestContext( t, ctrl, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest()) // Create the factory factory := &timerQueueFactory{ taskProcessor: task.NewMockProcessor(ctrl), archivalClient: archiver.NewMockClient(ctrl), } // Test the createQueuev2 method processor := factory.createQueuev2(mockShard, execution.NewMockCache(ctrl), invariant.NewMockInvariant(ctrl)) // Verify the result assert.NotNil(t, processor) assert.Implements(t, (*queue.Processor)(nil), processor) } func TestTimerQueueFactory_Category(t *testing.T) { factory := &timerQueueFactory{} category := factory.Category() assert.Equal(t, persistence.HistoryTaskCategoryTimer, category) } func TestTimerQueueFactory_IsQueueV2Enabled(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShard := shard.NewTestContext( t, ctrl, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest()) factory := &timerQueueFactory{} // Test the isQueueV2Enabled method // by default, queue v2 is disabled enabled := factory.isQueueV2Enabled(mockShard) assert.False(t, enabled) } ================================================ FILE: service/history/queuev2/transfer_queue_factory.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package queuev2 import ( "context" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/reset" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" "github.com/uber/cadence/service/history/workflowcache" "github.com/uber/cadence/service/worker/archiver" ) type ( transferQueueFactory struct { taskProcessor task.Processor archivalClient archiver.Client wfIDCache workflowcache.WFCache } ) func NewTransferQueueFactory( taskProcessor task.Processor, archivalClient archiver.Client, wfIDCache workflowcache.WFCache, ) queue.Factory { return &transferQueueFactory{taskProcessor, archivalClient, wfIDCache} } func (f *transferQueueFactory) Category() persistence.HistoryTaskCategory { return persistence.HistoryTaskCategoryTransfer } func (f *transferQueueFactory) isQueueV2Enabled(shard shard.Context) bool { return shard.GetConfig().EnableTransferQueueV2(shard.GetShardID()) } func (f *transferQueueFactory) CreateQueue( shard shard.Context, executionCache execution.Cache, openExecutionCheck invariant.Invariant, ) queue.Processor { if f.isQueueV2Enabled(shard) { return f.createQueuev2(shard, executionCache, openExecutionCheck) } return f.createQueuev1(shard, executionCache, openExecutionCheck) } func (f *transferQueueFactory) createQueuev1( shard shard.Context, executionCache execution.Cache, openExecutionCheck invariant.Invariant, ) queue.Processor { workflowResetter := reset.NewWorkflowResetter(shard, executionCache, shard.GetLogger()) return queue.NewTransferQueueProcessor( shard, f.taskProcessor, executionCache, workflowResetter, f.archivalClient, openExecutionCheck, f.wfIDCache, ) } func (f *transferQueueFactory) createQueuev2( shard shard.Context, executionCache execution.Cache, openExecutionCheck invariant.Invariant, ) queue.Processor { logger := shard.GetLogger().WithTags(tag.ComponentTransferQueueV2) workflowResetter := reset.NewWorkflowResetter(shard, executionCache, logger) activeTaskExecutor := task.NewTransferActiveTaskExecutor( shard, f.archivalClient, executionCache, workflowResetter, logger, shard.GetConfig(), f.wfIDCache, ) historyResender := ndc.NewHistoryResender( shard.GetDomainCache(), shard.GetService().GetClientBean(), func(ctx context.Context, request *types.ReplicateEventsV2Request) error { return shard.GetEngine().ReplicateEventsV2(ctx, request) }, shard.GetConfig().StandbyTaskReReplicationContextTimeout, openExecutionCheck, logger, ) standbyTaskExecutor := task.NewTransferStandbyTaskExecutor( shard, f.archivalClient, executionCache, historyResender, logger, shard.GetClusterMetadata().GetCurrentClusterName(), shard.GetConfig(), ) executorWrapper := task.NewExecutorWrapper( shard.GetClusterMetadata().GetCurrentClusterName(), shard.GetActiveClusterManager(), activeTaskExecutor, standbyTaskExecutor, logger, ) config := shard.GetConfig() return NewImmediateQueue( shard, persistence.HistoryTaskCategoryTransfer, f.taskProcessor, executorWrapper, logger, shard.GetMetricsClient(), shard.GetMetricsClient().Scope(metrics.TransferQueueProcessorV2Scope).Tagged(metrics.ShardIDTag(shard.GetShardID())), &Options{ PageSize: config.TransferTaskBatchSize, DeleteBatchSize: config.TransferTaskDeleteBatchSize, MaxPollRPS: config.TransferProcessorMaxPollRPS, MaxPollInterval: config.TransferProcessorMaxPollInterval, MaxPollIntervalJitterCoefficient: config.TransferProcessorMaxPollIntervalJitterCoefficient, UpdateAckInterval: config.TransferProcessorUpdateAckInterval, UpdateAckIntervalJitterCoefficient: config.TransferProcessorUpdateAckIntervalJitterCoefficient, MaxPendingTasksCount: config.QueueMaxPendingTaskCount, PollBackoffInterval: config.QueueProcessorPollBackoffInterval, PollBackoffIntervalJitterCoefficient: config.QueueProcessorPollBackoffIntervalJitterCoefficient, VirtualSliceForceAppendInterval: config.VirtualSliceForceAppendInterval, EnableValidator: config.TransferProcessorEnableValidator, ValidationInterval: config.TransferProcessorValidationInterval, MaxStartJitterInterval: dynamicproperties.GetDurationPropertyFn(0), RedispatchInterval: config.ActiveTaskRedispatchInterval, CriticalPendingTaskCount: config.QueueCriticalPendingTaskCount, EnablePendingTaskCountAlert: func() bool { return config.EnableTransferQueueV2PendingTaskCountAlert(shard.GetShardID()) }, MaxVirtualQueueCount: config.QueueMaxVirtualQueueCount, }, ) } ================================================ FILE: service/history/queuev2/transfer_queue_factory_test.go ================================================ package queuev2 import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/queue" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/task" "github.com/uber/cadence/service/worker/archiver" ) func TestTransferQueueFactory_CreateQueuev2(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShard := shard.NewTestContext( t, ctrl, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest()) // Create the factory factory := &transferQueueFactory{ taskProcessor: task.NewMockProcessor(ctrl), archivalClient: archiver.NewMockClient(ctrl), } // Test the createQueuev2 method processor := factory.createQueuev2(mockShard, execution.NewMockCache(ctrl), invariant.NewMockInvariant(ctrl)) // Verify the result assert.NotNil(t, processor) assert.Implements(t, (*queue.Processor)(nil), processor) } func TestTransferQueueFactory_CreateQueuev1(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShard := shard.NewTestContext( t, ctrl, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest()) // Create the factory factory := &transferQueueFactory{ taskProcessor: task.NewMockProcessor(ctrl), archivalClient: archiver.NewMockClient(ctrl), } // Test the createQueuev1 method processor := factory.createQueuev1(mockShard, execution.NewMockCache(ctrl), invariant.NewMockInvariant(ctrl)) // Verify the result assert.NotNil(t, processor) assert.Implements(t, (*queue.Processor)(nil), processor) } func TestTransferQueueFactory_Category(t *testing.T) { factory := &transferQueueFactory{} category := factory.Category() assert.Equal(t, persistence.HistoryTaskCategoryTransfer, category) } func TestTransferQueueFactory_IsQueueV2Enabled(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShard := shard.NewTestContext( t, ctrl, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest()) factory := &transferQueueFactory{} // Test the isQueueV2Enabled method // by default, queue v2 is disabled enabled := factory.isQueueV2Enabled(mockShard) assert.False(t, enabled) } ================================================ FILE: service/history/queuev2/virtual_queue.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination virtual_queue_mock.go github.com/uber/cadence/service/history/queuev2 VirtualQueue package queuev2 import ( "container/list" "context" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/service/history/task" ) var ( taskSchedulerThrottleBackoffInterval = time.Second * 5 taskReaderErrorBackoffInterval = time.Second ) type ( VirtualQueue interface { common.Daemon // GetState return the current state of the virtual queue GetState() []VirtualSliceState // UpdateAndGetState update the state of the virtual queue and return the current state UpdateAndGetState() []VirtualSliceState // MergeSlices merge the incoming slices into the virtual queue, this is used when we want to merge slices to a non-root virtual queue MergeSlices(...VirtualSlice) // MergeWithLastSlice merge the incoming slice with the last slice in the virtual queue, this is used when we want to add a new slice to the root virtual queue to avoid nullify the effect of AppendSlices MergeWithLastSlice(VirtualSlice) // AppendSlices append the incoming slices to the virtual queue, this is used when we want to add a new slice to the root virtual queue to prevent infinite growth of the virtual slice AppendSlices(...VirtualSlice) // IterateSlices iterate over the slices in the virtual queue IterateSlices(func(VirtualSlice)) // ClearSlices calls the Clear method of the slices that satisfy the predicate function ClearSlices(func(VirtualSlice) bool) // SplitSlices applies the split function to the slices in the virtual queue and return the remaining slices that should be kept in the virtual queue and whether the split is applied SplitSlices(func(VirtualSlice) (remaining []VirtualSlice, split bool)) // Pause pauses the virtual queue for a while Pause(time.Duration) } VirtualQueueOptions struct { PageSize dynamicproperties.IntPropertyFn MaxPendingTasksCount dynamicproperties.IntPropertyFn PollBackoffInterval dynamicproperties.DurationPropertyFn PollBackoffIntervalJitterCoefficient dynamicproperties.FloatPropertyFn } virtualQueueImpl struct { queueOptions *VirtualQueueOptions processor task.Processor rescheduler task.Rescheduler logger log.Logger metricsScope metrics.Scope timeSource clock.TimeSource taskLoadRateLimiter quotas.Limiter monitor Monitor sync.RWMutex status int32 wg sync.WaitGroup ctx context.Context cancel func() notifyCh chan struct{} pauseController PauseController virtualSlices *list.List sliceToRead *list.Element } ) func NewVirtualQueue( processor task.Processor, rescheduler task.Rescheduler, logger log.Logger, metricsScope metrics.Scope, timeSource clock.TimeSource, taskLoadRateLimiter quotas.Limiter, monitor Monitor, virtualSlices []VirtualSlice, queueOptions *VirtualQueueOptions, ) VirtualQueue { ctx, cancel := context.WithCancel(context.Background()) sliceList := list.New() for _, slice := range virtualSlices { sliceList.PushBack(slice) } return &virtualQueueImpl{ queueOptions: queueOptions, processor: processor, rescheduler: rescheduler, logger: logger, metricsScope: metricsScope, timeSource: timeSource, taskLoadRateLimiter: taskLoadRateLimiter, monitor: monitor, status: common.DaemonStatusInitialized, ctx: ctx, cancel: cancel, notifyCh: make(chan struct{}, 1), pauseController: NewPauseController(timeSource), virtualSlices: sliceList, sliceToRead: sliceList.Front(), } } func (q *virtualQueueImpl) Start() { if !atomic.CompareAndSwapInt32(&q.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } q.pauseController.Subscribe("virtual-queue", q.notifyCh) q.wg.Add(1) go q.run() q.notify() q.logger.Info("Virtual queue state changed", tag.LifeCycleStarted) } func (q *virtualQueueImpl) Stop() { if !atomic.CompareAndSwapInt32(&q.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } q.pauseController.Unsubscribe("virtual-queue") q.pauseController.Stop() q.cancel() q.wg.Wait() q.RLock() defer q.RUnlock() for e := q.virtualSlices.Front(); e != nil; e = e.Next() { slice := e.Value.(VirtualSlice) slice.Clear() } q.logger.Info("Virtual queue state changed", tag.LifeCycleStopped) } func (q *virtualQueueImpl) GetState() []VirtualSliceState { q.RLock() defer q.RUnlock() states := make([]VirtualSliceState, 0, q.virtualSlices.Len()) for e := q.virtualSlices.Front(); e != nil; e = e.Next() { states = append(states, e.Value.(VirtualSlice).GetState()) } return states } func (q *virtualQueueImpl) UpdateAndGetState() []VirtualSliceState { q.Lock() defer q.Unlock() states := make([]VirtualSliceState, 0, q.virtualSlices.Len()) var next *list.Element for e := q.virtualSlices.Front(); e != nil; e = next { next = e.Next() slice := e.Value.(VirtualSlice) state := slice.UpdateAndGetState() if slice.IsEmpty() { q.virtualSlices.Remove(e) q.monitor.RemoveSlice(slice) } else { states = append(states, state) q.monitor.SetSlicePendingTaskCount(slice, slice.GetPendingTaskCount()) } } return states } func (q *virtualQueueImpl) MergeSlices(incomingSlices ...VirtualSlice) { if len(incomingSlices) == 0 { return } q.Lock() defer q.Unlock() mergedSlices := list.New() currentSliceElement := q.virtualSlices.Front() incomingSliceIdx := 0 for currentSliceElement != nil && incomingSliceIdx < len(incomingSlices) { currentSlice := currentSliceElement.Value.(VirtualSlice) incomingSlice := incomingSlices[incomingSliceIdx] if currentSlice.GetState().Range.InclusiveMinTaskKey.Compare(incomingSlice.GetState().Range.InclusiveMinTaskKey) < 0 { q.appendOrMergeSlice(mergedSlices, currentSlice) currentSliceElement = currentSliceElement.Next() } else { q.appendOrMergeSlice(mergedSlices, incomingSlice) incomingSliceIdx++ } } for ; currentSliceElement != nil; currentSliceElement = currentSliceElement.Next() { q.appendOrMergeSlice(mergedSlices, currentSliceElement.Value.(VirtualSlice)) } for _, slice := range incomingSlices[incomingSliceIdx:] { q.appendOrMergeSlice(mergedSlices, slice) } q.virtualSlices.Init() q.virtualSlices = mergedSlices q.resetNextReadSliceLocked() } func (q *virtualQueueImpl) MergeWithLastSlice(incomingSlice VirtualSlice) { q.Lock() defer q.Unlock() q.appendOrMergeSlice(q.virtualSlices, incomingSlice) q.resetNextReadSliceLocked() } func (q *virtualQueueImpl) AppendSlices(incomingSlices ...VirtualSlice) { if len(incomingSlices) == 0 { return } q.Lock() defer q.Unlock() for _, slice := range incomingSlices { q.virtualSlices.PushBack(slice) } q.resetNextReadSliceLocked() } func (q *virtualQueueImpl) IterateSlices(f func(VirtualSlice)) { q.RLock() defer q.RUnlock() for e := q.virtualSlices.Front(); e != nil; e = e.Next() { f(e.Value.(VirtualSlice)) } } func (q *virtualQueueImpl) ClearSlices(f func(VirtualSlice) bool) { q.Lock() defer q.Unlock() for e := q.virtualSlices.Front(); e != nil; e = e.Next() { slice := e.Value.(VirtualSlice) if f(slice) { slice.Clear() q.monitor.SetSlicePendingTaskCount(slice, slice.GetPendingTaskCount()) } } q.resetNextReadSliceLocked() } func (q *virtualQueueImpl) SplitSlices(f func(VirtualSlice) (remaining []VirtualSlice, split bool)) { q.Lock() defer q.Unlock() remainingSlices := list.New() for e := q.virtualSlices.Front(); e != nil; e = e.Next() { slice := e.Value.(VirtualSlice) remaining, split := f(slice) if !split { remainingSlices.PushBack(slice) continue } q.monitor.RemoveSlice(slice) for _, remainingSlice := range remaining { remainingSlices.PushBack(remainingSlice) q.monitor.SetSlicePendingTaskCount(remainingSlice, remainingSlice.GetPendingTaskCount()) } } q.virtualSlices.Init() q.virtualSlices = remainingSlices q.resetNextReadSliceLocked() } func (q *virtualQueueImpl) Pause(duration time.Duration) { q.pauseController.Pause(duration) } func (q *virtualQueueImpl) notify() { select { case q.notifyCh <- struct{}{}: default: } } func (q *virtualQueueImpl) run() { defer q.wg.Done() for { select { case <-q.ctx.Done(): return case <-q.notifyCh: q.loadAndSubmitTasks() } } } func (q *virtualQueueImpl) loadAndSubmitTasks() { if err := q.taskLoadRateLimiter.Wait(q.ctx); err != nil { if q.ctx.Err() != nil { return } // this should never happen, but we log it for debugging purposes q.logger.Error("Virtual queue failed to wait for rate limiter", tag.Error(err)) } q.Lock() defer q.Unlock() if q.sliceToRead == nil { return } pendingTaskCount := q.monitor.GetTotalPendingTaskCount() maxTaskCount := q.queueOptions.MaxPendingTasksCount() // TODO: review the metrics and remove this comment or change the metric from gauge to histogram q.metricsScope.UpdateGauge(metrics.PendingTaskGauge, float64(pendingTaskCount)) if pendingTaskCount >= maxTaskCount { q.logger.Warn("Too many pending tasks, pause loading tasks for a while", tag.PendingTaskCount(pendingTaskCount), tag.MaxTaskCount(maxTaskCount)) q.pauseController.Pause(q.queueOptions.PollBackoffInterval()) } if q.pauseController.IsPaused() { // emit a metric indicating that the virtual queue is paused q.metricsScope.UpdateGauge(metrics.VirtualQueuePausedGauge, 1.0) q.logger.Debug("virtual queue is paused", tag.PendingTaskCount(pendingTaskCount), tag.MaxTaskCount(maxTaskCount)) return } // emit a metric indicating that the virtual queue is alive q.metricsScope.UpdateGauge(metrics.VirtualQueueRunningGauge, 1.0) sliceToRead := q.sliceToRead.Value.(VirtualSlice) // This logic is to avoid the loop of loading tasks from max virtual queue -> pending task count exceeds critical task count -> unload tasks from max virtual queue // for non-root virtual queue, we know that maxTaskCount < criticalTaskCount remainingSize := maxTaskCount - pendingTaskCount if remainingSize <= 0 { remainingSize = 1 q.logger.Error("unexpected error, virtual queue is not paused when pending task count exceeds max task count limit", tag.PendingTaskCount(pendingTaskCount), tag.MaxTaskCount(maxTaskCount)) } pageSize := min(q.queueOptions.PageSize(), remainingSize) q.logger.Debug("getting tasks from virtual queue", tag.PendingTaskCount(pendingTaskCount), tag.MaxTaskCount(maxTaskCount), tag.Counter(pageSize)) tasks, err := sliceToRead.GetTasks(q.ctx, pageSize) if err != nil { q.logger.Error("Virtual queue failed to get tasks", tag.Error(err)) q.pauseController.Pause(taskReaderErrorBackoffInterval) return } q.logger.Debug("got tasks from virtual queue", tag.Counter(len(tasks))) q.monitor.SetSlicePendingTaskCount(sliceToRead, sliceToRead.GetPendingTaskCount()) now := q.timeSource.Now() for _, task := range tasks { if persistence.IsTaskCorrupted(task) { q.logger.Error("Virtual queue encountered a corrupted task", tag.Dynamic("task", task)) q.metricsScope.IncCounter(metrics.CorruptedHistoryTaskCounter) task.Ack() continue } scheduledTime := task.GetTaskKey().GetScheduledTime() // if the scheduled time is in the future, we need to reschedule the task if now.Before(scheduledTime) { q.rescheduler.RescheduleTask(task, scheduledTime) continue } // shard level metrics for the duration between a task being written to a queue and being fetched from it q.metricsScope.RecordHistogramDuration(metrics.TaskEnqueueToFetchLatency, now.Sub(task.GetVisibilityTimestamp())) task.SetInitialSubmitTime(now) submitted, err := q.processor.TrySubmit(task) if err != nil { select { case <-q.ctx.Done(): return default: q.logger.Error("Virtual queue failed to submit task", tag.Error(err)) } } if !submitted { q.metricsScope.IncCounter(metrics.ProcessingQueueThrottledCounter) q.rescheduler.RescheduleTask(task, q.timeSource.Now().Add(taskSchedulerThrottleBackoffInterval)) } } if sliceToRead.HasMoreTasks() { q.notify() return } q.sliceToRead = q.sliceToRead.Next() if q.sliceToRead != nil { q.notify() } } func (q *virtualQueueImpl) resetNextReadSliceLocked() { q.sliceToRead = nil for element := q.virtualSlices.Front(); element != nil; element = element.Next() { if element.Value.(VirtualSlice).HasMoreTasks() { q.sliceToRead = element break } } if q.sliceToRead != nil { q.notify() } } func (q *virtualQueueImpl) appendOrMergeSlice(slices *list.List, incomingSlice VirtualSlice) { if slices.Len() == 0 { slices.PushBack(incomingSlice) q.monitor.SetSlicePendingTaskCount(incomingSlice, incomingSlice.GetPendingTaskCount()) return } lastElement := slices.Back() lastSlice := lastElement.Value.(VirtualSlice) mergedSlices, merged := lastSlice.TryMergeWithVirtualSlice(incomingSlice) if !merged { slices.PushBack(incomingSlice) q.monitor.SetSlicePendingTaskCount(incomingSlice, incomingSlice.GetPendingTaskCount()) return } slices.Remove(lastElement) q.monitor.RemoveSlice(lastSlice) q.monitor.RemoveSlice(incomingSlice) // incomingSlice may already be tracked by the monitor, so we need to remove it if it's tracked for _, mergedSlice := range mergedSlices { slices.PushBack(mergedSlice) q.monitor.SetSlicePendingTaskCount(mergedSlice, mergedSlice.GetPendingTaskCount()) } } ================================================ FILE: service/history/queuev2/virtual_queue_manager.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -destination virtual_queue_manager_mock.go -package queuev2 github.com/uber/cadence/service/history/queuev2 VirtualQueueManager package queuev2 import ( "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/service/history/task" ) const ( rootQueueID = 0 ) type ( VirtualQueueManagerOptions struct { RootQueueOptions *VirtualQueueOptions NonRootQueueOptions *VirtualQueueOptions VirtualSliceForceAppendInterval dynamicproperties.DurationPropertyFn } VirtualQueueManager interface { common.Daemon VirtualQueues() map[int64]VirtualQueue GetOrCreateVirtualQueue(int64) VirtualQueue UpdateAndGetState() map[int64][]VirtualSliceState // Add a new virtual slice to the root queue. This is used when new tasks are generated and max read level is updated. // By default, all new tasks belong to the root queue, so we need to add a new virtual slice to the root queue. AddNewVirtualSliceToRootQueue(VirtualSlice) } virtualQueueManagerImpl struct { processor task.Processor taskInitializer task.Initializer rescheduler task.Rescheduler queueReader QueueReader logger log.Logger metricsScope metrics.Scope timeSource clock.TimeSource taskLoadRateLimiter quotas.Limiter monitor Monitor queueManagerOptions *VirtualQueueManagerOptions sync.RWMutex status int32 virtualQueues map[int64]VirtualQueue createVirtualQueueFn func(int64, ...VirtualSlice) VirtualQueue nextForceNewSliceTime time.Time } ) func NewVirtualQueueManager( processor task.Processor, rescheduler task.Rescheduler, taskInitializer task.Initializer, queueReader QueueReader, logger log.Logger, metricsScope metrics.Scope, timeSource clock.TimeSource, taskLoadRateLimiter quotas.Limiter, monitor Monitor, queueManagerOptions *VirtualQueueManagerOptions, virtualQueueStates map[int64][]VirtualSliceState, ) VirtualQueueManager { virtualQueues := make(map[int64]VirtualQueue) for queueID, states := range virtualQueueStates { virtualSlices := make([]VirtualSlice, len(states)) for i, state := range states { virtualSlices[i] = NewVirtualSlice(state, taskInitializer, queueReader, NewPendingTaskTracker(), logger) } var opts *VirtualQueueOptions if queueID == rootQueueID { opts = queueManagerOptions.RootQueueOptions } else { opts = queueManagerOptions.NonRootQueueOptions } virtualQueues[queueID] = NewVirtualQueue(processor, rescheduler, logger.WithTags(tag.VirtualQueueID(queueID)), metricsScope, timeSource, taskLoadRateLimiter, monitor, virtualSlices, opts) } return &virtualQueueManagerImpl{ processor: processor, taskInitializer: taskInitializer, queueReader: queueReader, rescheduler: rescheduler, logger: logger, metricsScope: metricsScope, timeSource: timeSource, taskLoadRateLimiter: taskLoadRateLimiter, monitor: monitor, queueManagerOptions: queueManagerOptions, status: common.DaemonStatusInitialized, virtualQueues: virtualQueues, createVirtualQueueFn: func(queueID int64, s ...VirtualSlice) VirtualQueue { var opts *VirtualQueueOptions if queueID == rootQueueID { opts = queueManagerOptions.RootQueueOptions } else { opts = queueManagerOptions.NonRootQueueOptions } return NewVirtualQueue(processor, rescheduler, logger.WithTags(tag.VirtualQueueID(queueID)), metricsScope, timeSource, taskLoadRateLimiter, monitor, s, opts) }, } } func (m *virtualQueueManagerImpl) Start() { if !atomic.CompareAndSwapInt32(&m.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } m.RLock() defer m.RUnlock() for _, vq := range m.virtualQueues { vq.Start() } } func (m *virtualQueueManagerImpl) Stop() { if !atomic.CompareAndSwapInt32(&m.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } m.RLock() defer m.RUnlock() for _, vq := range m.virtualQueues { vq.Stop() } } func (m *virtualQueueManagerImpl) VirtualQueues() map[int64]VirtualQueue { m.RLock() defer m.RUnlock() return m.virtualQueues } func (m *virtualQueueManagerImpl) GetOrCreateVirtualQueue(queueID int64) VirtualQueue { m.RLock() if vq, ok := m.virtualQueues[queueID]; ok { m.RUnlock() return vq } m.RUnlock() m.Lock() defer m.Unlock() if vq, ok := m.virtualQueues[queueID]; ok { return vq } m.virtualQueues[queueID] = m.createVirtualQueueFn(queueID) m.virtualQueues[queueID].Start() return m.virtualQueues[queueID] } func (m *virtualQueueManagerImpl) UpdateAndGetState() map[int64][]VirtualSliceState { m.Lock() defer m.Unlock() virtualQueueStates := make(map[int64][]VirtualSliceState) for key, vq := range m.virtualQueues { state := vq.UpdateAndGetState() if len(state) > 0 { virtualQueueStates[key] = state } else if key != rootQueueID { vq.Stop() delete(m.virtualQueues, key) } } return virtualQueueStates } func (m *virtualQueueManagerImpl) AddNewVirtualSliceToRootQueue(s VirtualSlice) { m.RLock() if vq, ok := m.virtualQueues[rootQueueID]; ok { m.RUnlock() m.appendOrMergeSlice(vq, s) return } m.RUnlock() m.Lock() defer m.Unlock() if vq, ok := m.virtualQueues[rootQueueID]; ok { m.appendOrMergeSlice(vq, s) return } m.virtualQueues[rootQueueID] = m.createVirtualQueueFn(rootQueueID, s) m.virtualQueues[rootQueueID].Start() } func (m *virtualQueueManagerImpl) appendOrMergeSlice(vq VirtualQueue, s VirtualSlice) { now := m.timeSource.Now() newVirtualSliceState := s.GetState() // TODO: we should set a limit on the number of virtual slices to prevent the size of queue state from being too large to be stored in database if now.After(m.nextForceNewSliceTime) { m.logger.Debug("append new slice to virtual queue", tag.Dynamic("currentTime", now), tag.Dynamic("nextForceNewSliceTime", m.nextForceNewSliceTime), tag.Dynamic("inclusiveMinTaskKey", newVirtualSliceState.Range.InclusiveMinTaskKey), tag.Dynamic("exclusiveMaxTaskKey", newVirtualSliceState.Range.ExclusiveMaxTaskKey)) vq.AppendSlices(s) m.nextForceNewSliceTime = now.Add(m.queueManagerOptions.VirtualSliceForceAppendInterval()) return } m.logger.Debug("merge slice to virtual queue", tag.Dynamic("currentTime", now), tag.Dynamic("nextForceNewSliceTime", m.nextForceNewSliceTime), tag.Dynamic("inclusiveMinTaskKey", newVirtualSliceState.Range.InclusiveMinTaskKey), tag.Dynamic("exclusiveMaxTaskKey", newVirtualSliceState.Range.ExclusiveMaxTaskKey)) vq.MergeWithLastSlice(s) } ================================================ FILE: service/history/queuev2/virtual_queue_manager_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/queuev2 (interfaces: VirtualQueueManager) // // Generated by this command: // // mockgen -destination virtual_queue_manager_mock.go -package queuev2 github.com/uber/cadence/service/history/queuev2 VirtualQueueManager // // Package queuev2 is a generated GoMock package. package queuev2 import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockVirtualQueueManager is a mock of VirtualQueueManager interface. type MockVirtualQueueManager struct { ctrl *gomock.Controller recorder *MockVirtualQueueManagerMockRecorder isgomock struct{} } // MockVirtualQueueManagerMockRecorder is the mock recorder for MockVirtualQueueManager. type MockVirtualQueueManagerMockRecorder struct { mock *MockVirtualQueueManager } // NewMockVirtualQueueManager creates a new mock instance. func NewMockVirtualQueueManager(ctrl *gomock.Controller) *MockVirtualQueueManager { mock := &MockVirtualQueueManager{ctrl: ctrl} mock.recorder = &MockVirtualQueueManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockVirtualQueueManager) EXPECT() *MockVirtualQueueManagerMockRecorder { return m.recorder } // AddNewVirtualSliceToRootQueue mocks base method. func (m *MockVirtualQueueManager) AddNewVirtualSliceToRootQueue(arg0 VirtualSlice) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddNewVirtualSliceToRootQueue", arg0) } // AddNewVirtualSliceToRootQueue indicates an expected call of AddNewVirtualSliceToRootQueue. func (mr *MockVirtualQueueManagerMockRecorder) AddNewVirtualSliceToRootQueue(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddNewVirtualSliceToRootQueue", reflect.TypeOf((*MockVirtualQueueManager)(nil).AddNewVirtualSliceToRootQueue), arg0) } // GetOrCreateVirtualQueue mocks base method. func (m *MockVirtualQueueManager) GetOrCreateVirtualQueue(arg0 int64) VirtualQueue { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrCreateVirtualQueue", arg0) ret0, _ := ret[0].(VirtualQueue) return ret0 } // GetOrCreateVirtualQueue indicates an expected call of GetOrCreateVirtualQueue. func (mr *MockVirtualQueueManagerMockRecorder) GetOrCreateVirtualQueue(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrCreateVirtualQueue", reflect.TypeOf((*MockVirtualQueueManager)(nil).GetOrCreateVirtualQueue), arg0) } // Start mocks base method. func (m *MockVirtualQueueManager) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockVirtualQueueManagerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockVirtualQueueManager)(nil).Start)) } // Stop mocks base method. func (m *MockVirtualQueueManager) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockVirtualQueueManagerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockVirtualQueueManager)(nil).Stop)) } // UpdateAndGetState mocks base method. func (m *MockVirtualQueueManager) UpdateAndGetState() map[int64][]VirtualSliceState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAndGetState") ret0, _ := ret[0].(map[int64][]VirtualSliceState) return ret0 } // UpdateAndGetState indicates an expected call of UpdateAndGetState. func (mr *MockVirtualQueueManagerMockRecorder) UpdateAndGetState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAndGetState", reflect.TypeOf((*MockVirtualQueueManager)(nil).UpdateAndGetState)) } // VirtualQueues mocks base method. func (m *MockVirtualQueueManager) VirtualQueues() map[int64]VirtualQueue { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "VirtualQueues") ret0, _ := ret[0].(map[int64]VirtualQueue) return ret0 } // VirtualQueues indicates an expected call of VirtualQueues. func (mr *MockVirtualQueueManagerMockRecorder) VirtualQueues() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VirtualQueues", reflect.TypeOf((*MockVirtualQueueManager)(nil).VirtualQueues)) } ================================================ FILE: service/history/queuev2/virtual_queue_manager_test.go ================================================ package queuev2 import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/task" ) func TestVirtualQueueManager_VirtualQueues(t *testing.T) { tests := []struct { name string initialStates map[int64][]VirtualSliceState expectedStates map[int64][]VirtualSliceState setupMockQueues func(map[int64]*MockVirtualQueue) }{ { name: "empty virtual queues", initialStates: map[int64][]VirtualSliceState{}, expectedStates: map[int64][]VirtualSliceState{}, setupMockQueues: func(mocks map[int64]*MockVirtualQueue) { // No mocks to set up for empty case }, }, { name: "single queue with single slice", initialStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, }, expectedStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, }, setupMockQueues: func(mocks map[int64]*MockVirtualQueue) { mocks[1].EXPECT().GetState().Return([]VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }) }, }, { name: "multiple queues with multiple slices", initialStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, 2: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, }, }, }, expectedStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, 2: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, }, }, }, setupMockQueues: func(mocks map[int64]*MockVirtualQueue) { mocks[1].EXPECT().GetState().Return([]VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }) mocks[2].EXPECT().GetState().Return([]VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, }, }) }, }, { name: "queue with empty state", initialStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, 2: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, }, }, }, expectedStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, 2: {}, }, setupMockQueues: func(mocks map[int64]*MockVirtualQueue) { mocks[1].EXPECT().GetState().Return([]VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }) mocks[2].EXPECT().GetState().Return([]VirtualSliceState{}) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) // Create mock dependencies mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockTaskInitializer := func(t persistence.Task) task.Task { mockTask := task.NewMockTask(ctrl) mockTask.EXPECT().GetTaskID().Return(t.GetTaskID()) return mockTask } mockQueueReader := NewMockQueueReader(ctrl) mockLogger := log.NewNoop() mockMetricsScope := metrics.NoopScope // Create virtual queues map with mocks virtualQueues := make(map[int64]VirtualQueue) mockQueues := make(map[int64]*MockVirtualQueue) for queueID := range tt.initialStates { mockQueue := NewMockVirtualQueue(ctrl) mockQueues[queueID] = mockQueue virtualQueues[queueID] = mockQueue } // Set up mock expectations tt.setupMockQueues(mockQueues) // Create manager instance manager := &virtualQueueManagerImpl{ processor: mockProcessor, taskInitializer: mockTaskInitializer, rescheduler: mockRescheduler, queueReader: mockQueueReader, logger: mockLogger, metricsScope: mockMetricsScope, queueManagerOptions: &VirtualQueueManagerOptions{ RootQueueOptions: &VirtualQueueOptions{}, NonRootQueueOptions: &VirtualQueueOptions{ PageSize: dynamicproperties.GetIntPropertyFn(100), }, }, status: common.DaemonStatusInitialized, virtualQueues: virtualQueues, } // Execute test vqs := manager.VirtualQueues() states := make(map[int64][]VirtualSliceState) for queueID, virtualQueue := range vqs { states[queueID] = virtualQueue.GetState() } // Verify results assert.Equal(t, tt.expectedStates, states) }) } } func TestVirtualQueueManager_UpdateAndGetState(t *testing.T) { tests := []struct { name string initialStates map[int64][]VirtualSliceState expectedStates map[int64][]VirtualSliceState setupMockQueues func(map[int64]*MockVirtualQueue) }{ { name: "empty virtual queues", initialStates: map[int64][]VirtualSliceState{}, expectedStates: map[int64][]VirtualSliceState{}, setupMockQueues: func(mocks map[int64]*MockVirtualQueue) { // No mocks to set up for empty case }, }, { name: "single queue with single slice", initialStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, }, expectedStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, }, setupMockQueues: func(mocks map[int64]*MockVirtualQueue) { mocks[1].EXPECT().UpdateAndGetState().Return([]VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }) }, }, { name: "multiple queues with multiple slices", initialStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, 2: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, }, }, }, expectedStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, 2: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, }, }, }, setupMockQueues: func(mocks map[int64]*MockVirtualQueue) { mocks[1].EXPECT().UpdateAndGetState().Return([]VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }) mocks[2].EXPECT().UpdateAndGetState().Return([]VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, }, }) }, }, { name: "queue with empty state gets removed", initialStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, 2: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, }, }, }, expectedStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, }, setupMockQueues: func(mocks map[int64]*MockVirtualQueue) { mocks[1].EXPECT().UpdateAndGetState().Return([]VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }) mocks[2].EXPECT().UpdateAndGetState().Return([]VirtualSliceState{}) mocks[2].EXPECT().Stop() }, }, { name: "all queues empty get removed", initialStates: map[int64][]VirtualSliceState{ 1: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, 2: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, }, }, }, expectedStates: map[int64][]VirtualSliceState{}, setupMockQueues: func(mocks map[int64]*MockVirtualQueue) { mocks[1].EXPECT().UpdateAndGetState().Return([]VirtualSliceState{}) mocks[1].EXPECT().Stop() mocks[2].EXPECT().UpdateAndGetState().Return([]VirtualSliceState{}) mocks[2].EXPECT().Stop() }, }, { name: "empty root queue is not removed", initialStates: map[int64][]VirtualSliceState{ 0: { { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, }, expectedStates: map[int64][]VirtualSliceState{}, setupMockQueues: func(mocks map[int64]*MockVirtualQueue) { mocks[0].EXPECT().UpdateAndGetState().Return([]VirtualSliceState{}) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) // Create mock dependencies mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockTaskInitializer := func(t persistence.Task) task.Task { mockTask := task.NewMockTask(ctrl) mockTask.EXPECT().GetTaskID().Return(t.GetTaskID()) return mockTask } mockQueueReader := NewMockQueueReader(ctrl) mockLogger := log.NewNoop() mockMetricsScope := metrics.NoopScope // Create virtual queues map with mocks virtualQueues := make(map[int64]VirtualQueue) mockQueues := make(map[int64]*MockVirtualQueue) for queueID := range tt.initialStates { mockQueue := NewMockVirtualQueue(ctrl) mockQueues[queueID] = mockQueue virtualQueues[queueID] = mockQueue } // Set up mock expectations tt.setupMockQueues(mockQueues) // Create manager instance manager := &virtualQueueManagerImpl{ processor: mockProcessor, taskInitializer: mockTaskInitializer, rescheduler: mockRescheduler, queueReader: mockQueueReader, logger: mockLogger, metricsScope: mockMetricsScope, queueManagerOptions: &VirtualQueueManagerOptions{ RootQueueOptions: &VirtualQueueOptions{}, NonRootQueueOptions: &VirtualQueueOptions{ MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), }, }, status: common.DaemonStatusInitialized, virtualQueues: virtualQueues, } // Execute test states := manager.UpdateAndGetState() // Verify results assert.Equal(t, tt.expectedStates, states) }) } } func TestVirtualQueueManager_AddNewVirtualSlice(t *testing.T) { tests := []struct { name string initialQueues map[int64]VirtualQueue newSlice VirtualSlice setupMockQueues func(map[int64]*MockVirtualQueue, *MockVirtualSlice) verifyQueues func(*testing.T, map[int64]VirtualQueue) appendSlice bool }{ { name: "merge slice to existing root queue", initialQueues: map[int64]VirtualQueue{ rootQueueID: nil, // Will be replaced with mock }, newSlice: nil, // Will be replaced with mock setupMockQueues: func(mocks map[int64]*MockVirtualQueue, slice *MockVirtualSlice) { mocks[rootQueueID].EXPECT().MergeWithLastSlice(slice) slice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }) }, verifyQueues: func(t *testing.T, queues map[int64]VirtualQueue) { assert.Contains(t, queues, int64(rootQueueID)) assert.Len(t, queues, 1) }, }, { name: "append slice to existing root queue", initialQueues: map[int64]VirtualQueue{ rootQueueID: nil, // Will be replaced with mock }, newSlice: nil, // Will be replaced with mock setupMockQueues: func(mocks map[int64]*MockVirtualQueue, slice *MockVirtualSlice) { mocks[rootQueueID].EXPECT().AppendSlices(slice) slice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }) }, verifyQueues: func(t *testing.T, queues map[int64]VirtualQueue) { assert.Contains(t, queues, int64(rootQueueID)) assert.Len(t, queues, 1) }, appendSlice: true, }, { name: "create new root queue when none exists", initialQueues: map[int64]VirtualQueue{}, newSlice: nil, // Will be replaced with mock setupMockQueues: func(mocks map[int64]*MockVirtualQueue, slice *MockVirtualSlice) { // No expectations needed as we're creating a new queue }, verifyQueues: func(t *testing.T, queues map[int64]VirtualQueue) { assert.Contains(t, queues, int64(rootQueueID)) assert.Len(t, queues, 1) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) // Create mock dependencies mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockTaskInitializer := func(t persistence.Task) task.Task { mockTask := task.NewMockTask(ctrl) mockTask.EXPECT().GetTaskID().Return(t.GetTaskID()) return mockTask } mockQueueReader := NewMockQueueReader(ctrl) mockLogger := log.NewNoop() mockMetricsScope := metrics.NoopScope mockTimeSource := clock.NewMockedTimeSource() // Create mock slice mockSlice := NewMockVirtualSlice(ctrl) // Create virtual queues map with mocks virtualQueues := make(map[int64]VirtualQueue) mockQueues := make(map[int64]*MockVirtualQueue) for queueID := range tt.initialQueues { mockQueue := NewMockVirtualQueue(ctrl) mockQueues[queueID] = mockQueue virtualQueues[queueID] = mockQueue } // Set up mock expectations tt.setupMockQueues(mockQueues, mockSlice) forceNewSliceDuration := time.Minute // Create manager instance manager := &virtualQueueManagerImpl{ processor: mockProcessor, taskInitializer: mockTaskInitializer, rescheduler: mockRescheduler, queueReader: mockQueueReader, logger: mockLogger, metricsScope: mockMetricsScope, timeSource: mockTimeSource, queueManagerOptions: &VirtualQueueManagerOptions{ RootQueueOptions: &VirtualQueueOptions{ PageSize: dynamicproperties.GetIntPropertyFn(100), }, NonRootQueueOptions: &VirtualQueueOptions{ PageSize: dynamicproperties.GetIntPropertyFn(100), }, VirtualSliceForceAppendInterval: dynamicproperties.GetDurationPropertyFn(forceNewSliceDuration), }, status: common.DaemonStatusInitialized, virtualQueues: virtualQueues, createVirtualQueueFn: func(queueID int64, s ...VirtualSlice) VirtualQueue { vq := NewMockVirtualQueue(ctrl) vq.EXPECT().Start() return vq }, nextForceNewSliceTime: mockTimeSource.Now(), } // Execute test if tt.appendSlice { mockTimeSource.Advance(forceNewSliceDuration) } manager.AddNewVirtualSliceToRootQueue(mockSlice) // Verify results tt.verifyQueues(t, manager.virtualQueues) }) } } ================================================ FILE: service/history/queuev2/virtual_queue_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/queuev2 (interfaces: VirtualQueue) // // Generated by this command: // // mockgen -package queuev2 -destination virtual_queue_mock.go github.com/uber/cadence/service/history/queuev2 VirtualQueue // // Package queuev2 is a generated GoMock package. package queuev2 import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" ) // MockVirtualQueue is a mock of VirtualQueue interface. type MockVirtualQueue struct { ctrl *gomock.Controller recorder *MockVirtualQueueMockRecorder isgomock struct{} } // MockVirtualQueueMockRecorder is the mock recorder for MockVirtualQueue. type MockVirtualQueueMockRecorder struct { mock *MockVirtualQueue } // NewMockVirtualQueue creates a new mock instance. func NewMockVirtualQueue(ctrl *gomock.Controller) *MockVirtualQueue { mock := &MockVirtualQueue{ctrl: ctrl} mock.recorder = &MockVirtualQueueMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockVirtualQueue) EXPECT() *MockVirtualQueueMockRecorder { return m.recorder } // AppendSlices mocks base method. func (m *MockVirtualQueue) AppendSlices(arg0 ...VirtualSlice) { m.ctrl.T.Helper() varargs := []any{} for _, a := range arg0 { varargs = append(varargs, a) } m.ctrl.Call(m, "AppendSlices", varargs...) } // AppendSlices indicates an expected call of AppendSlices. func (mr *MockVirtualQueueMockRecorder) AppendSlices(arg0 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendSlices", reflect.TypeOf((*MockVirtualQueue)(nil).AppendSlices), arg0...) } // ClearSlices mocks base method. func (m *MockVirtualQueue) ClearSlices(arg0 func(VirtualSlice) bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "ClearSlices", arg0) } // ClearSlices indicates an expected call of ClearSlices. func (mr *MockVirtualQueueMockRecorder) ClearSlices(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearSlices", reflect.TypeOf((*MockVirtualQueue)(nil).ClearSlices), arg0) } // GetState mocks base method. func (m *MockVirtualQueue) GetState() []VirtualSliceState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetState") ret0, _ := ret[0].([]VirtualSliceState) return ret0 } // GetState indicates an expected call of GetState. func (mr *MockVirtualQueueMockRecorder) GetState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetState", reflect.TypeOf((*MockVirtualQueue)(nil).GetState)) } // IterateSlices mocks base method. func (m *MockVirtualQueue) IterateSlices(arg0 func(VirtualSlice)) { m.ctrl.T.Helper() m.ctrl.Call(m, "IterateSlices", arg0) } // IterateSlices indicates an expected call of IterateSlices. func (mr *MockVirtualQueueMockRecorder) IterateSlices(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateSlices", reflect.TypeOf((*MockVirtualQueue)(nil).IterateSlices), arg0) } // MergeSlices mocks base method. func (m *MockVirtualQueue) MergeSlices(arg0 ...VirtualSlice) { m.ctrl.T.Helper() varargs := []any{} for _, a := range arg0 { varargs = append(varargs, a) } m.ctrl.Call(m, "MergeSlices", varargs...) } // MergeSlices indicates an expected call of MergeSlices. func (mr *MockVirtualQueueMockRecorder) MergeSlices(arg0 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MergeSlices", reflect.TypeOf((*MockVirtualQueue)(nil).MergeSlices), arg0...) } // MergeWithLastSlice mocks base method. func (m *MockVirtualQueue) MergeWithLastSlice(arg0 VirtualSlice) { m.ctrl.T.Helper() m.ctrl.Call(m, "MergeWithLastSlice", arg0) } // MergeWithLastSlice indicates an expected call of MergeWithLastSlice. func (mr *MockVirtualQueueMockRecorder) MergeWithLastSlice(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MergeWithLastSlice", reflect.TypeOf((*MockVirtualQueue)(nil).MergeWithLastSlice), arg0) } // Pause mocks base method. func (m *MockVirtualQueue) Pause(arg0 time.Duration) { m.ctrl.T.Helper() m.ctrl.Call(m, "Pause", arg0) } // Pause indicates an expected call of Pause. func (mr *MockVirtualQueueMockRecorder) Pause(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pause", reflect.TypeOf((*MockVirtualQueue)(nil).Pause), arg0) } // SplitSlices mocks base method. func (m *MockVirtualQueue) SplitSlices(arg0 func(VirtualSlice) ([]VirtualSlice, bool)) { m.ctrl.T.Helper() m.ctrl.Call(m, "SplitSlices", arg0) } // SplitSlices indicates an expected call of SplitSlices. func (mr *MockVirtualQueueMockRecorder) SplitSlices(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SplitSlices", reflect.TypeOf((*MockVirtualQueue)(nil).SplitSlices), arg0) } // Start mocks base method. func (m *MockVirtualQueue) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockVirtualQueueMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockVirtualQueue)(nil).Start)) } // Stop mocks base method. func (m *MockVirtualQueue) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockVirtualQueueMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockVirtualQueue)(nil).Stop)) } // UpdateAndGetState mocks base method. func (m *MockVirtualQueue) UpdateAndGetState() []VirtualSliceState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAndGetState") ret0, _ := ret[0].([]VirtualSliceState) return ret0 } // UpdateAndGetState indicates an expected call of UpdateAndGetState. func (mr *MockVirtualQueueMockRecorder) UpdateAndGetState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAndGetState", reflect.TypeOf((*MockVirtualQueue)(nil).UpdateAndGetState)) } ================================================ FILE: service/history/queuev2/virtual_queue_test.go ================================================ package queuev2 import ( "container/list" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/service/history/task" ) func TestVirtualQueueImpl_GetState(t *testing.T) { ctrl := gomock.NewController(t) mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockPageSize := dynamicproperties.GetIntPropertyFn(10) mockVirtualSlice1 := NewMockVirtualSlice(ctrl) mockVirtualSlice2 := NewMockVirtualSlice(ctrl) mockVirtualSlices := []VirtualSlice{ mockVirtualSlice1, mockVirtualSlice2, } mockVirtualSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }) mockVirtualSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }) mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) mockMonitor := NewMockMonitor(ctrl) queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, mockMonitor, mockVirtualSlices, &VirtualQueueOptions{ PageSize: mockPageSize, MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ) states := queue.GetState() expectedStates := []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }, } assert.Equal(t, expectedStates, states) } func TestVirtualQueueImpl_UpdateAndGetState(t *testing.T) { ctrl := gomock.NewController(t) mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockPageSize := dynamicproperties.GetIntPropertyFn(10) mockVirtualSlice1 := NewMockVirtualSlice(ctrl) mockVirtualSlice2 := NewMockVirtualSlice(ctrl) mockMonitor := NewMockMonitor(ctrl) mockVirtualSlices := []VirtualSlice{ mockVirtualSlice1, mockVirtualSlice2, } mockVirtualSlice1.EXPECT().UpdateAndGetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }) mockVirtualSlice1.EXPECT().GetPendingTaskCount().Return(1) mockVirtualSlice1.EXPECT().IsEmpty().Return(false) mockMonitor.EXPECT().SetSlicePendingTaskCount(mockVirtualSlice1, 1) mockVirtualSlice2.EXPECT().UpdateAndGetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(11), }, Predicate: NewUniversalPredicate(), }) mockVirtualSlice2.EXPECT().IsEmpty().Return(true) mockMonitor.EXPECT().RemoveSlice(mockVirtualSlice2) mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, mockMonitor, mockVirtualSlices, &VirtualQueueOptions{ PageSize: mockPageSize, MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ) states := queue.UpdateAndGetState() expectedStates := []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, } assert.Equal(t, expectedStates, states) } func TestVirtualQueue_MergeSlices(t *testing.T) { tests := []struct { name string setupMocks func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) expectedStates []VirtualSliceState }{ { name: "Merge non-overlapping slices", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) existingSlice2 := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice.EXPECT().HasMoreTasks().Return(true) existingSlice.EXPECT().TryMergeWithVirtualSlice(incomingSlice).Return([]VirtualSlice{}, false) existingSlice.EXPECT().GetPendingTaskCount().Return(1) monitor.EXPECT().SetSlicePendingTaskCount(existingSlice, 1) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).AnyTimes() incomingSlice.EXPECT().TryMergeWithVirtualSlice(existingSlice2).Return([]VirtualSlice{}, false) incomingSlice.EXPECT().GetPendingTaskCount().Return(2) monitor.EXPECT().SetSlicePendingTaskCount(incomingSlice, 2) existingSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice2.EXPECT().GetPendingTaskCount().Return(3) monitor.EXPECT().SetSlicePendingTaskCount(existingSlice2, 3) return []VirtualSlice{existingSlice, existingSlice2}, []VirtualSlice{incomingSlice}, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }, }, }, { name: "Merge overlapping slices", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) existingSlice2 := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) mergedSlice := NewMockVirtualSlice(ctrl) mergedSlice2 := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice.EXPECT().TryMergeWithVirtualSlice(gomock.Any()).Return([]VirtualSlice{mergedSlice}, true) existingSlice.EXPECT().GetPendingTaskCount().Return(1) monitor.EXPECT().SetSlicePendingTaskCount(existingSlice, 1) mergedSlice.EXPECT().GetPendingTaskCount().Return(1) monitor.EXPECT().SetSlicePendingTaskCount(mergedSlice, 1) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice.EXPECT().TryMergeWithVirtualSlice(existingSlice2).Return([]VirtualSlice{mergedSlice2}, true) mergedSlice2.EXPECT().GetPendingTaskCount().Return(2) monitor.EXPECT().SetSlicePendingTaskCount(mergedSlice2, 2) existingSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice2.EXPECT().HasMoreTasks().Return(true).AnyTimes() monitor.EXPECT().RemoveSlice(existingSlice) monitor.EXPECT().RemoveSlice(incomingSlice) monitor.EXPECT().RemoveSlice(existingSlice2) monitor.EXPECT().RemoveSlice(mergedSlice) return []VirtualSlice{existingSlice, existingSlice2}, []VirtualSlice{incomingSlice}, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, }, }, { name: "Merge empty queue with new slices", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) { incomingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() incomingSlice.EXPECT().GetPendingTaskCount().Return(1) monitor.EXPECT().SetSlicePendingTaskCount(incomingSlice, 1) incomingSlice.EXPECT().HasMoreTasks().Return(true).AnyTimes() return []VirtualSlice{}, []VirtualSlice{incomingSlice}, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) existingSlices, incomingSlices, monitor := tt.setupMocks(ctrl) queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, monitor, existingSlices, &VirtualQueueOptions{ PageSize: dynamicproperties.GetIntPropertyFn(10), MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ) queue.MergeSlices(incomingSlices...) states := queue.GetState() assert.Equal(t, tt.expectedStates, states) }) } } func TestAppendOrMergeSlice(t *testing.T) { tests := []struct { name string setupMocks func(ctrl *gomock.Controller) (VirtualSlice, VirtualSlice, Monitor) expectedStates []VirtualSliceState }{ { name: "Append when no merge possible", setupMocks: func(ctrl *gomock.Controller) (VirtualSlice, VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice.EXPECT().TryMergeWithVirtualSlice(incomingSlice).Return([]VirtualSlice{}, false) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).AnyTimes() incomingSlice.EXPECT().GetPendingTaskCount().Return(2) monitor.EXPECT().SetSlicePendingTaskCount(incomingSlice, 2) return existingSlice, incomingSlice, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, }, }, { name: "Merge when slices overlap", setupMocks: func(ctrl *gomock.Controller) (VirtualSlice, VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) mergedSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice.EXPECT().TryMergeWithVirtualSlice(incomingSlice).Return([]VirtualSlice{mergedSlice}, true) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, Predicate: NewUniversalPredicate(), }).AnyTimes() monitor.EXPECT().RemoveSlice(existingSlice) monitor.EXPECT().RemoveSlice(incomingSlice) mergedSlice.EXPECT().GetPendingTaskCount().Return(1) monitor.EXPECT().SetSlicePendingTaskCount(mergedSlice, 1) return existingSlice, incomingSlice, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, Predicate: NewUniversalPredicate(), }, }, }, { name: "Append to empty list", setupMocks: func(ctrl *gomock.Controller) (VirtualSlice, VirtualSlice, Monitor) { incomingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() incomingSlice.EXPECT().GetPendingTaskCount().Return(10) monitor.EXPECT().SetSlicePendingTaskCount(incomingSlice, 10) return nil, incomingSlice, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, }, }, { name: "Merge with multiple resulting slices", setupMocks: func(ctrl *gomock.Controller) (VirtualSlice, VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) mergedSlice1 := NewMockVirtualSlice(ctrl) mergedSlice2 := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice.EXPECT().TryMergeWithVirtualSlice(incomingSlice).Return([]VirtualSlice{mergedSlice1, mergedSlice2}, true) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, Predicate: NewUniversalPredicate(), }).AnyTimes() monitor.EXPECT().RemoveSlice(existingSlice) monitor.EXPECT().RemoveSlice(incomingSlice) mergedSlice1.EXPECT().GetPendingTaskCount().Return(1) monitor.EXPECT().SetSlicePendingTaskCount(mergedSlice1, 1) mergedSlice2.EXPECT().GetPendingTaskCount().Return(2) monitor.EXPECT().SetSlicePendingTaskCount(mergedSlice2, 2) return existingSlice, incomingSlice, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, Predicate: NewUniversalPredicate(), }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) existingSlice, incomingSlice, monitor := tt.setupMocks(ctrl) slices := list.New() if existingSlice != nil { slices.PushBack(existingSlice) } virtualQueue := &virtualQueueImpl{ virtualSlices: slices, monitor: monitor, } virtualQueue.appendOrMergeSlice(slices, incomingSlice) // Convert list to slice of states for comparison var states []VirtualSliceState for e := slices.Front(); e != nil; e = e.Next() { states = append(states, e.Value.(VirtualSlice).GetState()) } assert.Equal(t, tt.expectedStates, states) }) } } func TestVirtualQueue_LoadAndSubmitTasks(t *testing.T) { ctrl := gomock.NewController(t) mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockPageSize := dynamicproperties.GetIntPropertyFn(10) mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) mockRateLimiter.EXPECT().Wait(gomock.Any()).Return(nil).AnyTimes() mockMonitor := NewMockMonitor(ctrl) mockPauseController := NewMockPauseController(ctrl) mockVirtualSlice1 := NewMockVirtualSlice(ctrl) mockVirtualSlice2 := NewMockVirtualSlice(ctrl) mockVirtualSlices := []VirtualSlice{ mockVirtualSlice1, mockVirtualSlice2, } mockTask1 := task.NewMockTask(ctrl) mockTask1.EXPECT().GetDomainID().Return("some random domainID") mockTask1.EXPECT().GetWorkflowID().Return("some random workflowID") mockTask1.EXPECT().GetRunID().Return("some random runID") mockTask1.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(mockTimeSource.Now().Add(time.Second*-1), 1)) mockTask1.EXPECT().GetVisibilityTimestamp().Return(mockTimeSource.Now().Add(time.Second * -1)) mockTask1.EXPECT().SetInitialSubmitTime(gomock.Any()).Times(1) mockTask2 := task.NewMockTask(ctrl) mockTask2.EXPECT().GetDomainID().Return("some random domainID") mockTask2.EXPECT().GetWorkflowID().Return("some random workflowID") mockTask2.EXPECT().GetRunID().Return("some random runID") mockTask2.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(mockTimeSource.Now().Add(time.Second*1), 2)) mockTask3 := task.NewMockTask(ctrl) mockTask3.EXPECT().GetDomainID().Return("some random domainID") mockTask3.EXPECT().GetWorkflowID().Return("some random workflowID") mockTask3.EXPECT().GetRunID().Return("some random runID") mockTask3.EXPECT().GetTaskKey().Return(persistence.NewHistoryTaskKey(mockTimeSource.Now().Add(time.Second*-1), 1)) mockTask3.EXPECT().GetVisibilityTimestamp().Return(mockTimeSource.Now().Add(time.Second * -1)) mockTask3.EXPECT().SetInitialSubmitTime(gomock.Any()).Times(1) mockMonitor.EXPECT().GetTotalPendingTaskCount().Return(0) mockPauseController.EXPECT().IsPaused().Return(false) mockVirtualSlice1.EXPECT().GetTasks(gomock.Any(), 10).Return([]task.Task{mockTask1, mockTask2}, nil) mockVirtualSlice1.EXPECT().GetPendingTaskCount().Return(2) mockMonitor.EXPECT().SetSlicePendingTaskCount(mockVirtualSlice1, 2) mockVirtualSlice1.EXPECT().HasMoreTasks().Return(false) mockMonitor.EXPECT().GetTotalPendingTaskCount().Return(0) mockPauseController.EXPECT().IsPaused().Return(false) mockVirtualSlice2.EXPECT().GetTasks(gomock.Any(), 10).Return([]task.Task{mockTask3}, nil) mockVirtualSlice2.EXPECT().HasMoreTasks().Return(false) mockVirtualSlice2.EXPECT().GetPendingTaskCount().Return(1) mockMonitor.EXPECT().SetSlicePendingTaskCount(mockVirtualSlice2, 1) mockProcessor.EXPECT().TrySubmit(mockTask3).Return(false, nil) mockProcessor.EXPECT().TrySubmit(mockTask1).Return(true, nil) mockRescheduler.EXPECT().RescheduleTask(mockTask2, mockTimeSource.Now().Add(time.Second*1)) mockRescheduler.EXPECT().RescheduleTask(mockTask3, mockTimeSource.Now().Add(taskSchedulerThrottleBackoffInterval)) queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, mockMonitor, mockVirtualSlices, &VirtualQueueOptions{ PageSize: mockPageSize, MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ).(*virtualQueueImpl) queue.pauseController = mockPauseController queue.loadAndSubmitTasks() queue.loadAndSubmitTasks() assert.Nil(t, queue.sliceToRead) } func TestVirtualQueue_LifeCycle(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockPageSize := dynamicproperties.GetIntPropertyFn(10) mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) mockRateLimiter.EXPECT().Wait(gomock.Any()).Return(nil).AnyTimes() mockMonitor := NewMockMonitor(ctrl) mockPauseController := NewMockPauseController(ctrl) mockVirtualSlice1 := NewMockVirtualSlice(ctrl) mockVirtualSlices := []VirtualSlice{ mockVirtualSlice1, } mockVirtualSlice1.EXPECT().GetTasks(gomock.Any(), 10).Return([]task.Task{}, nil).MaxTimes(1) mockVirtualSlice1.EXPECT().HasMoreTasks().Return(false).MaxTimes(1) mockVirtualSlice1.EXPECT().GetPendingTaskCount().Return(0).MaxTimes(1) mockVirtualSlice1.EXPECT().Clear().Times(1) mockMonitor.EXPECT().SetSlicePendingTaskCount(mockVirtualSlice1, 0).MaxTimes(1) mockMonitor.EXPECT().GetTotalPendingTaskCount().Return(0).MaxTimes(1) mockPauseController.EXPECT().Subscribe(gomock.Any(), gomock.Any()).Times(1) mockPauseController.EXPECT().IsPaused().Return(false).AnyTimes() mockPauseController.EXPECT().Unsubscribe(gomock.Any()).Times(1) mockPauseController.EXPECT().Stop().Times(1) queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, mockMonitor, mockVirtualSlices, &VirtualQueueOptions{ PageSize: mockPageSize, MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ).(*virtualQueueImpl) queue.pauseController = mockPauseController queue.Start() queue.Stop() } func TestVirtualQueue_LifeCycle_Pause(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockPageSize := dynamicproperties.GetIntPropertyFn(10) mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) mockRateLimiter.EXPECT().Wait(gomock.Any()).Return(nil).AnyTimes() mockMonitor := NewMockMonitor(ctrl) mockVirtualSlice1 := NewMockVirtualSlice(ctrl) mockVirtualSlices := []VirtualSlice{ mockVirtualSlice1, } queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, mockMonitor, mockVirtualSlices, &VirtualQueueOptions{ PageSize: mockPageSize, MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ).(*virtualQueueImpl) gomock.InOrder( // first time we call loadAndSubmitTasks, we should pause, so set the total pending task count to be larger than MaxPendingTasksCount mockMonitor.EXPECT().GetTotalPendingTaskCount().Return(101).Times(1), // then we should resume from pause and load the tasks // to simplify the test, we just assume that there is no more tasks to load mockMonitor.EXPECT().GetTotalPendingTaskCount().Return(0).MaxTimes(1), mockVirtualSlice1.EXPECT().GetTasks(gomock.Any(), 10).Return([]task.Task{}, nil).MaxTimes(1), mockVirtualSlice1.EXPECT().GetPendingTaskCount().Return(0).MaxTimes(1), mockMonitor.EXPECT().SetSlicePendingTaskCount(mockVirtualSlice1, 0).MaxTimes(1), mockVirtualSlice1.EXPECT().HasMoreTasks().Return(false).MaxTimes(1), mockVirtualSlice1.EXPECT().Clear().Times(1), ) queue.Start() // wait for the pause controller to resume mockTimeSource.BlockUntil(1) mockTimeSource.Advance(time.Second * 10) queue.Stop() } func TestVirtualQueue_IterateSlices(t *testing.T) { ctrl := gomock.NewController(t) mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockPageSize := dynamicproperties.GetIntPropertyFn(10) mockVirtualSlice1 := NewMockVirtualSlice(ctrl) mockVirtualSlice2 := NewMockVirtualSlice(ctrl) mockVirtualSlices := []VirtualSlice{ mockVirtualSlice1, mockVirtualSlice2, } mockVirtualSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }) mockVirtualSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }) mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) mockMonitor := NewMockMonitor(ctrl) queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, mockMonitor, mockVirtualSlices, &VirtualQueueOptions{ PageSize: mockPageSize, MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ) states := []VirtualSliceState{} queue.IterateSlices(func(slice VirtualSlice) { states = append(states, slice.GetState()) }) expectedStates := []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }, } assert.Equal(t, expectedStates, states) } func TestVirtualQueue_ClearSlices(t *testing.T) { ctrl := gomock.NewController(t) mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockPageSize := dynamicproperties.GetIntPropertyFn(10) mockMonitor := NewMockMonitor(ctrl) mockVirtualSlice1 := NewMockVirtualSlice(ctrl) mockVirtualSlice2 := NewMockVirtualSlice(ctrl) mockVirtualSlices := []VirtualSlice{ mockVirtualSlice1, mockVirtualSlice2, } mockVirtualSlice1.EXPECT().Clear().Times(1) mockVirtualSlice1.EXPECT().GetPendingTaskCount().Return(0).Times(1) mockVirtualSlice1.EXPECT().HasMoreTasks().Return(false).Times(1) mockMonitor.EXPECT().SetSlicePendingTaskCount(mockVirtualSlice1, 0).Times(1) mockVirtualSlice2.EXPECT().Clear().Times(1) mockVirtualSlice2.EXPECT().GetPendingTaskCount().Return(0).Times(1) mockVirtualSlice2.EXPECT().HasMoreTasks().Return(true).Times(1) mockMonitor.EXPECT().SetSlicePendingTaskCount(mockVirtualSlice2, 0).Times(1) mockVirtualSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }) mockVirtualSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }) mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, mockMonitor, mockVirtualSlices, &VirtualQueueOptions{ PageSize: mockPageSize, MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ) queue.ClearSlices(func(slice VirtualSlice) bool { return true }) states := queue.GetState() expectedStates := []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }, } assert.Equal(t, expectedStates, states) assert.Equal(t, mockVirtualSlice2, queue.(*virtualQueueImpl).sliceToRead.Value.(VirtualSlice)) } func TestVirtualQueue_SplitSlices(t *testing.T) { ctrl := gomock.NewController(t) mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockPageSize := dynamicproperties.GetIntPropertyFn(10) mockVirtualSlice1 := NewMockVirtualSlice(ctrl) mockVirtualSlice2 := NewMockVirtualSlice(ctrl) mockVirtualSlice3 := NewMockVirtualSlice(ctrl) mockVirtualSlices := []VirtualSlice{ mockVirtualSlice1, mockVirtualSlice2, } mockMonitor := NewMockMonitor(ctrl) mockVirtualSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }) mockVirtualSlice1.EXPECT().HasMoreTasks().Return(true).Times(1) mockMonitor.EXPECT().RemoveSlice(mockVirtualSlice2).Times(1) mockVirtualSlice3.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(15), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }) mockVirtualSlice3.EXPECT().GetPendingTaskCount().Return(0).Times(1) mockMonitor.EXPECT().SetSlicePendingTaskCount(mockVirtualSlice3, 0).Times(1) mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, mockMonitor, mockVirtualSlices, &VirtualQueueOptions{ PageSize: mockPageSize, MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ) queue.SplitSlices(func(slice VirtualSlice) (remaining []VirtualSlice, split bool) { if slice == mockVirtualSlice1 { return nil, false } else if slice == mockVirtualSlice2 { return []VirtualSlice{mockVirtualSlice3}, true } return nil, false }) states := queue.GetState() expectedStates := []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(15), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }, } assert.Equal(t, expectedStates, states) assert.Equal(t, mockVirtualSlice1, queue.(*virtualQueueImpl).sliceToRead.Value.(VirtualSlice)) } func TestVirtualQueue_AppendSlices(t *testing.T) { tests := []struct { name string setupMocks func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) expectedStates []VirtualSliceState expectedSliceToReadIdx *int // nil if sliceToRead should be nil, otherwise index of expected slice }{ { name: "Append empty slice list", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).Times(1) return []VirtualSlice{existingSlice}, []VirtualSlice{}, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(0), }, { name: "Append single slice to empty queue", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) { incomingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).Times(1) incomingSlice.EXPECT().HasMoreTasks().Return(true).Times(1) return []VirtualSlice{}, []VirtualSlice{incomingSlice}, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(0), }, { name: "Append multiple slices to empty queue", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) { incomingSlice1 := NewMockVirtualSlice(ctrl) incomingSlice2 := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) incomingSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).Times(1) incomingSlice1.EXPECT().HasMoreTasks().Return(true).Times(1) incomingSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }).Times(1) return []VirtualSlice{}, []VirtualSlice{incomingSlice1, incomingSlice2}, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(0), }, { name: "Append slices to queue with existing slices", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) { existingSlice1 := NewMockVirtualSlice(ctrl) existingSlice2 := NewMockVirtualSlice(ctrl) incomingSlice1 := NewMockVirtualSlice(ctrl) incomingSlice2 := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).Times(1) existingSlice1.EXPECT().HasMoreTasks().Return(false).Times(1) existingSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }).Times(1) existingSlice2.EXPECT().HasMoreTasks().Return(true).Times(1) incomingSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(21), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(30), }, Predicate: NewUniversalPredicate(), }).Times(1) incomingSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(31), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(40), }, Predicate: NewUniversalPredicate(), }).Times(1) return []VirtualSlice{existingSlice1, existingSlice2}, []VirtualSlice{incomingSlice1, incomingSlice2}, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(21), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(30), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(31), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(40), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(1), }, { name: "Append slices when no existing slice has more tasks", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).Times(1) existingSlice.EXPECT().HasMoreTasks().Return(false).Times(1) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }).Times(1) incomingSlice.EXPECT().HasMoreTasks().Return(true).Times(1) return []VirtualSlice{existingSlice}, []VirtualSlice{incomingSlice}, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(1), }, { name: "Append slices when all slices have no more tasks", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, []VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).Times(1) existingSlice.EXPECT().HasMoreTasks().Return(false).Times(1) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }).Times(1) incomingSlice.EXPECT().HasMoreTasks().Return(false).Times(1) return []VirtualSlice{existingSlice}, []VirtualSlice{incomingSlice}, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(11), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) existingSlices, incomingSlices, monitor := tt.setupMocks(ctrl) queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, monitor, existingSlices, &VirtualQueueOptions{ PageSize: dynamicproperties.GetIntPropertyFn(10), MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ) queue.AppendSlices(incomingSlices...) states := queue.GetState() assert.Equal(t, tt.expectedStates, states) // Check sliceToRead queueImpl := queue.(*virtualQueueImpl) if tt.expectedSliceToReadIdx == nil { assert.Nil(t, queueImpl.sliceToRead, "sliceToRead should be nil") } else { assert.NotNil(t, queueImpl.sliceToRead, "sliceToRead should not be nil") if queueImpl.sliceToRead != nil { expectedSlice := append(existingSlices, incomingSlices...)[*tt.expectedSliceToReadIdx] assert.Equal(t, expectedSlice, queueImpl.sliceToRead.Value.(VirtualSlice)) } } }) } } func TestVirtualQueue_MergeWithLastSlice(t *testing.T) { tests := []struct { name string setupMocks func(ctrl *gomock.Controller) ([]VirtualSlice, VirtualSlice, Monitor) expectedStates []VirtualSliceState expectedSliceToReadIdx *int // nil if sliceToRead should be nil, otherwise index of expected slice }{ { name: "Merge with empty queue", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, VirtualSlice, Monitor) { incomingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).AnyTimes() incomingSlice.EXPECT().GetPendingTaskCount().Return(5) incomingSlice.EXPECT().HasMoreTasks().Return(true) monitor.EXPECT().SetSlicePendingTaskCount(incomingSlice, 5) return []VirtualSlice{}, incomingSlice, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(0), }, { name: "Merge with last slice - no merge possible", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, VirtualSlice, Monitor) { existingSlice1 := NewMockVirtualSlice(ctrl) existingSlice2 := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice1.EXPECT().HasMoreTasks().Return(false) existingSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice2.EXPECT().TryMergeWithVirtualSlice(incomingSlice).Return([]VirtualSlice{}, false) existingSlice2.EXPECT().HasMoreTasks().Return(true) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(15), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }).AnyTimes() incomingSlice.EXPECT().GetPendingTaskCount().Return(3) monitor.EXPECT().SetSlicePendingTaskCount(incomingSlice, 3) return []VirtualSlice{existingSlice1, existingSlice2}, incomingSlice, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(15), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(20), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(1), // existingSlice1 has more tasks }, { name: "Merge with last slice - successful merge", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, VirtualSlice, Monitor) { existingSlice1 := NewMockVirtualSlice(ctrl) existingSlice2 := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) mergedSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice1.EXPECT().HasMoreTasks().Return(true) existingSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice2.EXPECT().TryMergeWithVirtualSlice(incomingSlice).Return([]VirtualSlice{mergedSlice}, true) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(8), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(15), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(15), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice.EXPECT().GetPendingTaskCount().Return(7) monitor.EXPECT().RemoveSlice(existingSlice2) monitor.EXPECT().RemoveSlice(incomingSlice) monitor.EXPECT().SetSlicePendingTaskCount(mergedSlice, 7) return []VirtualSlice{existingSlice1, existingSlice2}, incomingSlice, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(15), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(0), // existingSlice1 has more tasks }, { name: "Merge with last slice - multiple merged slices result", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) mergedSlice1 := NewMockVirtualSlice(ctrl) mergedSlice2 := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice.EXPECT().TryMergeWithVirtualSlice(incomingSlice).Return([]VirtualSlice{mergedSlice1, mergedSlice2}, true) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(15), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice1.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice1.EXPECT().GetPendingTaskCount().Return(4) mergedSlice1.EXPECT().HasMoreTasks().Return(true) mergedSlice2.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(7), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(15), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice2.EXPECT().GetPendingTaskCount().Return(6) monitor.EXPECT().RemoveSlice(existingSlice) monitor.EXPECT().RemoveSlice(incomingSlice) monitor.EXPECT().SetSlicePendingTaskCount(mergedSlice1, 4) monitor.EXPECT().SetSlicePendingTaskCount(mergedSlice2, 6) return []VirtualSlice{existingSlice}, incomingSlice, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(7), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(15), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(0), // mergedSlice1 has more tasks }, { name: "Merge with single existing slice - no merge possible", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice.EXPECT().TryMergeWithVirtualSlice(incomingSlice).Return([]VirtualSlice{}, false) existingSlice.EXPECT().HasMoreTasks().Return(true) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(10), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(15), }, Predicate: NewUniversalPredicate(), }).AnyTimes() incomingSlice.EXPECT().GetPendingTaskCount().Return(2) monitor.EXPECT().SetSlicePendingTaskCount(incomingSlice, 2) return []VirtualSlice{existingSlice}, incomingSlice, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(10), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(15), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(0), // existingSlice has more tasks }, { name: "Merge with single existing slice - successful merge", setupMocks: func(ctrl *gomock.Controller) ([]VirtualSlice, VirtualSlice, Monitor) { existingSlice := NewMockVirtualSlice(ctrl) incomingSlice := NewMockVirtualSlice(ctrl) mergedSlice := NewMockVirtualSlice(ctrl) monitor := NewMockMonitor(ctrl) existingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }).AnyTimes() existingSlice.EXPECT().TryMergeWithVirtualSlice(incomingSlice).Return([]VirtualSlice{mergedSlice}, true) incomingSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice.EXPECT().GetState().Return(VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, Predicate: NewUniversalPredicate(), }).AnyTimes() mergedSlice.EXPECT().GetPendingTaskCount().Return(5) mergedSlice.EXPECT().HasMoreTasks().Return(true) monitor.EXPECT().RemoveSlice(existingSlice) monitor.EXPECT().RemoveSlice(incomingSlice) monitor.EXPECT().SetSlicePendingTaskCount(mergedSlice, 5) return []VirtualSlice{existingSlice}, incomingSlice, monitor }, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, Predicate: NewUniversalPredicate(), }, }, expectedSliceToReadIdx: common.Ptr(0), // mergedSlice has more tasks }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockProcessor := task.NewMockProcessor(ctrl) mockRescheduler := task.NewMockRescheduler(ctrl) mockLogger := testlogger.New(t) mockMetricsScope := metrics.NoopScope mockTimeSource := clock.NewMockedTimeSource() mockRateLimiter := quotas.NewMockLimiter(ctrl) existingSlices, incomingSlice, monitor := tt.setupMocks(ctrl) queue := NewVirtualQueue( mockProcessor, mockRescheduler, mockLogger, mockMetricsScope, mockTimeSource, mockRateLimiter, monitor, existingSlices, &VirtualQueueOptions{ PageSize: dynamicproperties.GetIntPropertyFn(10), MaxPendingTasksCount: dynamicproperties.GetIntPropertyFn(100), PollBackoffInterval: dynamicproperties.GetDurationPropertyFn(time.Second * 10), PollBackoffIntervalJitterCoefficient: dynamicproperties.GetFloatPropertyFn(0.0), }, ) queue.MergeWithLastSlice(incomingSlice) states := queue.GetState() assert.Equal(t, tt.expectedStates, states) // Check sliceToRead queueImpl := queue.(*virtualQueueImpl) if tt.expectedSliceToReadIdx == nil { assert.Nil(t, queueImpl.sliceToRead, "sliceToRead should be nil") } else { assert.NotNil(t, queueImpl.sliceToRead, "sliceToRead should not be nil") if queueImpl.sliceToRead != nil { // For MergeWithLastSlice, we need to build the final slice list differently // since the incoming slice is merged/appended to the existing slices var finalSlices []VirtualSlice for _, state := range states { // Find the slice that matches this state for e := queueImpl.virtualSlices.Front(); e != nil; e = e.Next() { slice := e.Value.(VirtualSlice) if slice.GetState() == state { finalSlices = append(finalSlices, slice) break } } } expectedSlice := finalSlices[*tt.expectedSliceToReadIdx] assert.Equal(t, expectedSlice, queueImpl.sliceToRead.Value.(VirtualSlice)) } } }) } } ================================================ FILE: service/history/queuev2/virtual_slice.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination virtual_slice_mock.go github.com/uber/cadence/service/history/queuev2 VirtualSlice package queuev2 import ( "context" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/task" ) type ( VirtualSlice interface { GetState() VirtualSliceState IsEmpty() bool GetTasks(context.Context, int) ([]task.Task, error) HasMoreTasks() bool UpdateAndGetState() VirtualSliceState GetPendingTaskCount() int Clear() PendingTaskStats() PendingTaskStats TrySplitByTaskKey(persistence.HistoryTaskKey) (VirtualSlice, VirtualSlice, bool) TrySplitByPredicate(Predicate) (VirtualSlice, VirtualSlice, bool) TryMergeWithVirtualSlice(VirtualSlice) ([]VirtualSlice, bool) } PendingTaskStats struct { PendingTaskCountPerDomain map[string]int } virtualSliceImpl struct { state VirtualSliceState taskInitializer task.Initializer queueReader QueueReader pendingTaskTracker PendingTaskTracker logger log.Logger // progress tracks the read progress of the slice, sorted by the inclusive min task key of the range, ranges are not overlapping // For a virtual slice, the progress is a task key pointing to the next task to read and the next page token // In most cases, there is only one GetTaskProgress item in the progress slice. // However, when 2 virtual slices are merged, the progress slice may contain 2 items, because even though the range of the slices have overlap, // their GetTaskProgress cannot be merged, because the next page tokens are coupled with the range of the original slices. progress []*GetTaskProgress } ) func NewVirtualSlice( state VirtualSliceState, taskInitializer task.Initializer, queueReader QueueReader, pendingTaskTracker PendingTaskTracker, logger log.Logger, ) VirtualSlice { return &virtualSliceImpl{ state: state, taskInitializer: taskInitializer, queueReader: queueReader, pendingTaskTracker: pendingTaskTracker, logger: logger, progress: []*GetTaskProgress{ { Range: state.Range, NextPageToken: nil, NextTaskKey: state.Range.InclusiveMinTaskKey, }, }, } } func (s *virtualSliceImpl) GetState() VirtualSliceState { return s.state } func (s *virtualSliceImpl) GetPendingTaskCount() int { return s.pendingTaskTracker.GetPendingTaskCount() } func (s *virtualSliceImpl) IsEmpty() bool { return s.state.IsEmpty() && !s.HasMoreTasks() } func (s *virtualSliceImpl) Clear() { s.UpdateAndGetState() s.pendingTaskTracker.Clear() s.progress = []*GetTaskProgress{ { Range: s.state.Range, NextPageToken: nil, NextTaskKey: s.state.Range.InclusiveMinTaskKey, }, } } func (s *virtualSliceImpl) GetTasks(ctx context.Context, pageSize int) ([]task.Task, error) { if len(s.progress) == 0 { return nil, nil } tasks := make([]task.Task, 0, pageSize) for len(tasks) < pageSize && len(s.progress) > 0 { resp, err := s.queueReader.GetTask(ctx, &GetTaskRequest{ Progress: s.progress[0], Predicate: s.state.Predicate, PageSize: pageSize - len(tasks), }) if err != nil { // NOTE: we must return the tasks here to let them either be submitted to scheduler or rescheduler // because they are already added to pending task tracker. Otherwise, they will become zombie tasks, // and won't be processed until shard restart. // The number of tasks returned here doesn't need to be the same as the page size even if there is still more tasks to read. // HasMoreTasks() method will still return true in this case. if len(tasks) > 0 { return tasks, nil } return nil, err } for _, t := range resp.Tasks { task := s.taskInitializer(t) tasks = append(tasks, task) s.pendingTaskTracker.AddTask(task) } // The persistence layer may return non-empty next page token even if there are no more tasks to read // We compare the next task key with the exclusive max task key to determine if there are more tasks to read instead if resp.Progress.NextTaskKey.Compare(s.progress[0].ExclusiveMaxTaskKey) < 0 { s.progress[0] = resp.Progress } else { s.progress = s.progress[1:] } } return tasks, nil } func (s *virtualSliceImpl) HasMoreTasks() bool { return len(s.progress) > 0 } func (s *virtualSliceImpl) PendingTaskStats() PendingTaskStats { return PendingTaskStats{ PendingTaskCountPerDomain: s.pendingTaskTracker.GetPerDomainPendingTaskCount(), } } func (s *virtualSliceImpl) UpdateAndGetState() VirtualSliceState { prunedCount := s.pendingTaskTracker.PruneAckedTasks() nextTaskKey := s.state.Range.ExclusiveMaxTaskKey if len(s.progress) > 0 { nextTaskKey = s.progress[0].NextTaskKey } s.logger.Debug("pruned acked tasks", tag.Counter(prunedCount), tag.Dynamic("inclusiveMinTaskKey", s.state.Range.InclusiveMinTaskKey), tag.Dynamic("exclusiveMaxTaskKey", s.state.Range.ExclusiveMaxTaskKey), tag.Dynamic("nextTaskKey", nextTaskKey)) minPendingTaskKey, ok := s.pendingTaskTracker.GetMinimumTaskKey() if !ok { if len(s.progress) > 0 { // no pending tasks, and there are more tasks to read s.state.Range.InclusiveMinTaskKey = s.progress[0].NextTaskKey } else { // no pending tasks, and no more tasks to read s.state.Range.InclusiveMinTaskKey = s.state.Range.ExclusiveMaxTaskKey } } else { if len(s.progress) > 0 { // there are pending tasks, and there are more tasks to read s.state.Range.InclusiveMinTaskKey = persistence.MinHistoryTaskKey(minPendingTaskKey, s.progress[0].NextTaskKey) } else { // there are pending tasks, and no more tasks to read s.state.Range.InclusiveMinTaskKey = minPendingTaskKey } } return s.state } func (s *virtualSliceImpl) TrySplitByTaskKey(taskKey persistence.HistoryTaskKey) (VirtualSlice, VirtualSlice, bool) { leftState, rightState, ok := s.state.TrySplitByTaskKey(taskKey) if !ok { return nil, nil, false } leftTracker := NewPendingTaskTracker() rightTracker := NewPendingTaskTracker() taskMap := s.pendingTaskTracker.GetTasks() for taskKey, task := range taskMap { if leftState.Range.Contains(taskKey) { leftTracker.AddTask(task) } else { rightTracker.AddTask(task) } } leftProgress := []*GetTaskProgress{} rightProgress := []*GetTaskProgress{} for _, progress := range s.progress { if leftState.Range.ContainsRange(progress.Range) { leftProgress = append(leftProgress, progress) continue } if rightState.Range.ContainsRange(progress.Range) { rightProgress = append(rightProgress, progress) continue } if leftState.Range.Contains(progress.NextTaskKey) { leftProgress = append(leftProgress, &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: progress.NextTaskKey, ExclusiveMaxTaskKey: leftState.Range.ExclusiveMaxTaskKey, }, NextPageToken: nil, NextTaskKey: progress.NextTaskKey, }) rightProgress = append(rightProgress, &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: rightState.Range.InclusiveMinTaskKey, ExclusiveMaxTaskKey: progress.Range.ExclusiveMaxTaskKey, }, NextPageToken: nil, NextTaskKey: rightState.Range.InclusiveMinTaskKey, }) } else { rightProgress = append(rightProgress, &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: progress.NextTaskKey, ExclusiveMaxTaskKey: progress.Range.ExclusiveMaxTaskKey, }, NextPageToken: nil, NextTaskKey: progress.NextTaskKey, }) } } leftSlice := &virtualSliceImpl{ state: leftState, taskInitializer: s.taskInitializer, queueReader: s.queueReader, pendingTaskTracker: leftTracker, progress: leftProgress, logger: s.logger, } rightSlice := &virtualSliceImpl{ state: rightState, taskInitializer: s.taskInitializer, queueReader: s.queueReader, pendingTaskTracker: rightTracker, progress: rightProgress, logger: s.logger, } return leftSlice, rightSlice, true } func (s *virtualSliceImpl) TrySplitByPredicate(predicate Predicate) (VirtualSlice, VirtualSlice, bool) { passState, failState, ok := s.state.TrySplitByPredicate(predicate) if !ok { return nil, nil, false } passTracker := NewPendingTaskTracker() failTracker := NewPendingTaskTracker() taskMap := s.pendingTaskTracker.GetTasks() for _, task := range taskMap { if passState.Predicate.Check(task) { passTracker.AddTask(task) } else { failTracker.AddTask(task) } } passProgress := make([]*GetTaskProgress, len(s.progress)) failProgress := make([]*GetTaskProgress, len(s.progress)) copy(passProgress, s.progress) copy(failProgress, s.progress) passSlice := &virtualSliceImpl{ state: passState, taskInitializer: s.taskInitializer, queueReader: s.queueReader, pendingTaskTracker: passTracker, progress: passProgress, logger: s.logger, } failSlice := &virtualSliceImpl{ state: failState, taskInitializer: s.taskInitializer, queueReader: s.queueReader, pendingTaskTracker: failTracker, progress: failProgress, logger: s.logger, } return passSlice, failSlice, true } func (s *virtualSliceImpl) TryMergeWithVirtualSlice(other VirtualSlice) ([]VirtualSlice, bool) { otherImpl, ok := other.(*virtualSliceImpl) if !ok { return nil, false } if s == other || !s.state.Range.CanMerge(other.GetState().Range) { return nil, false } if s.state.Predicate.Equals(other.GetState().Predicate) { return []VirtualSlice{mergeVirtualSlicesByRange(s, otherImpl)}, true } return mergeVirtualSlicesWithDifferentPredicate(s, otherImpl) } func mergeVirtualSlicesByRange(left, right *virtualSliceImpl) VirtualSlice { if left.state.Range.InclusiveMinTaskKey.Compare(right.state.Range.InclusiveMinTaskKey) > 0 { left, right = right, left } mergedState := VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: left.state.Range.InclusiveMinTaskKey, ExclusiveMaxTaskKey: persistence.MaxHistoryTaskKey(left.state.Range.ExclusiveMaxTaskKey, right.state.Range.ExclusiveMaxTaskKey), }, Predicate: left.state.Predicate, // left and right have the same predicate } pendingTaskTracker := left.pendingTaskTracker taskMap := right.pendingTaskTracker.GetTasks() for _, task := range taskMap { pendingTaskTracker.AddTask(task) } mergedProgress := mergeGetTaskProgressWithSamePredicate(left.progress, right.progress) return &virtualSliceImpl{ state: mergedState, taskInitializer: left.taskInitializer, queueReader: left.queueReader, pendingTaskTracker: pendingTaskTracker, progress: mergedProgress, logger: left.logger, } } func mergeGetTaskProgressWithSamePredicate(left, right []*GetTaskProgress) []*GetTaskProgress { mergedProgress := []*GetTaskProgress{} leftIndex := 0 rightIndex := 0 for leftIndex < len(left) && rightIndex < len(right) { if left[leftIndex].Range.InclusiveMinTaskKey.Compare(right[rightIndex].Range.InclusiveMinTaskKey) <= 0 { mergedProgress = appendOrMergeProgressWithSamePredicate(mergedProgress, left[leftIndex]) leftIndex++ } else { mergedProgress = appendOrMergeProgressWithSamePredicate(mergedProgress, right[rightIndex]) rightIndex++ } } for leftIndex < len(left) { mergedProgress = appendOrMergeProgressWithSamePredicate(mergedProgress, left[leftIndex]) leftIndex++ } for rightIndex < len(right) { mergedProgress = appendOrMergeProgressWithSamePredicate(mergedProgress, right[rightIndex]) rightIndex++ } return mergedProgress } func appendOrMergeProgressWithSamePredicate(mergedProgress []*GetTaskProgress, progress *GetTaskProgress) []*GetTaskProgress { if len(mergedProgress) == 0 { return append(mergedProgress, progress) } lastProgress := mergedProgress[len(mergedProgress)-1] mergedProgress = mergedProgress[:len(mergedProgress)-1] // remove the last progress return append(mergedProgress, mergeProgressWithSamePredicate(lastProgress, progress)...) } // mergeProgress merges two progress with the same predicate // Assuming the inclusive key, next key, max key of the 2 progress are [a, b, c] and [x, y, z] where a <= b <= c and x <= y <= z, // also assuming that a <= x, otherwise we can swap the left and right progress // There are 10 different cases regarding the order of a, b, c, x, y, z, and here are the cases and merged results: // [a, b, c, x, y, z] -> [b, b, c] and [y, y, z] // [a, b, x, c, y, z] -> [b, b, x] and [y, y, z] // [a, b, x, y, c, z] -> [b, b, x] and [y, y, z] // [a, b, x, y, z, c] -> [b, b, x] and [y, y, c] // [a, x, b, c, y ,z] -> [y, y, z] // [a, x, b, y, c, z] -> [y, y, z] // [a, x, b, y, z, c] -> [y, y, c] // [a, x, y, b, c, z] -> [b, b, z] // [a, x, y, b, z, c] -> [b, b, c] // [a, x, y, z, b, c] -> [b, b, c] // we only need to consider the range that hasn't been read yet, the merged result can be represented as // [b, b, min(c, x)], [max(b, y), max(b, y), max(c, z)], and if b >= x, [b, b, min(c, x)] will be an empty progress so it's omitted func mergeProgressWithSamePredicate(left, right *GetTaskProgress) []*GetTaskProgress { if left.Range.InclusiveMinTaskKey.Compare(right.Range.InclusiveMinTaskKey) > 0 { left, right = right, left } if left.NextTaskKey.Compare(right.InclusiveMinTaskKey) < 0 { return []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: left.NextTaskKey, ExclusiveMaxTaskKey: persistence.MinHistoryTaskKey(left.ExclusiveMaxTaskKey, right.InclusiveMinTaskKey), }, NextPageToken: nil, NextTaskKey: left.NextTaskKey, }, { Range: Range{ InclusiveMinTaskKey: right.NextTaskKey, ExclusiveMaxTaskKey: persistence.MaxHistoryTaskKey(left.ExclusiveMaxTaskKey, right.ExclusiveMaxTaskKey), }, NextPageToken: nil, NextTaskKey: persistence.MaxHistoryTaskKey(left.NextTaskKey, right.NextTaskKey), }, } } return []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.MaxHistoryTaskKey(left.NextTaskKey, right.NextTaskKey), ExclusiveMaxTaskKey: persistence.MaxHistoryTaskKey(left.ExclusiveMaxTaskKey, right.ExclusiveMaxTaskKey), }, NextPageToken: nil, NextTaskKey: persistence.MaxHistoryTaskKey(left.NextTaskKey, right.NextTaskKey), }, } } func mergeGetTaskProgressWithDifferentPredicate(left, right []*GetTaskProgress) []*GetTaskProgress { mergedProgress := []*GetTaskProgress{} leftIndex := 0 rightIndex := 0 for leftIndex < len(left) && rightIndex < len(right) { if left[leftIndex].NextTaskKey.Compare(right[rightIndex].NextTaskKey) <= 0 { mergedProgress = appendOrMergeProgressWithDifferentPredicate(mergedProgress, left[leftIndex]) leftIndex++ } else { mergedProgress = appendOrMergeProgressWithDifferentPredicate(mergedProgress, right[rightIndex]) rightIndex++ } } for leftIndex < len(left) { mergedProgress = appendOrMergeProgressWithDifferentPredicate(mergedProgress, left[leftIndex]) leftIndex++ } for rightIndex < len(right) { mergedProgress = appendOrMergeProgressWithDifferentPredicate(mergedProgress, right[rightIndex]) rightIndex++ } return mergedProgress } func appendOrMergeProgressWithDifferentPredicate(mergedProgress []*GetTaskProgress, progress *GetTaskProgress) []*GetTaskProgress { if len(mergedProgress) == 0 { return append(mergedProgress, progress) } lastProgress := mergedProgress[len(mergedProgress)-1] mergedProgress = mergedProgress[:len(mergedProgress)-1] // remove the last progress return append(mergedProgress, mergeProgressWithDifferentPredicate(lastProgress, progress)...) } // mergeProgress merges two progress with different predicates // Assuming the inclusive key, next key, max key of the 2 progress are [a, b, c] and [x, y, z] where a <= b <= c and x <= y <= z, // also assuming that b <= y, otherwise we can swap the left and right progress // There are 7 different cases regarding the order of a, b, c, x, y, z, and here are the cases and merged results: // [a, b, c, x, y, z] -> [b, b, c] and [y, y, z], (c < y) // [a, b, x, c, y, z] -> [b, b, c] and [y, y, z], (c < y) // [a, b, x, y, c, z] -> [b, b, z] // [a, b, x, y, z, c] -> [b, b, c] // [a, x, b, c, y ,z] -> [b, b, c] and [y, y, z], (c < y) // [a, x, b, y, c, z] -> [b, b, z] // [a, x, b, y, z, c] -> [b, b, c] // the idea is to get the union of [next task key, exclusive max task key) of the 2 progress, // and then set inclusive min task key to next task key, next page token to nil func mergeProgressWithDifferentPredicate(left, right *GetTaskProgress) []*GetTaskProgress { if left.NextTaskKey.Compare(right.NextTaskKey) > 0 { left, right = right, left } if left.Range.ExclusiveMaxTaskKey.Compare(right.NextTaskKey) < 0 { return []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: left.NextTaskKey, ExclusiveMaxTaskKey: left.ExclusiveMaxTaskKey, }, NextPageToken: nil, NextTaskKey: left.NextTaskKey, }, { Range: Range{ InclusiveMinTaskKey: right.NextTaskKey, ExclusiveMaxTaskKey: right.ExclusiveMaxTaskKey, }, NextPageToken: nil, NextTaskKey: right.NextTaskKey, }, } } return []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: left.NextTaskKey, ExclusiveMaxTaskKey: persistence.MaxHistoryTaskKey(left.ExclusiveMaxTaskKey, right.ExclusiveMaxTaskKey), }, NextPageToken: nil, NextTaskKey: left.NextTaskKey, }, } } func mergeVirtualSlicesByPredicate(this, that *virtualSliceImpl) VirtualSlice { mergedState := VirtualSliceState{ Range: this.state.Range, // this and that have the same range Predicate: Or(this.state.Predicate, that.state.Predicate), } pendingTaskTracker := this.pendingTaskTracker taskMap := that.pendingTaskTracker.GetTasks() for _, task := range taskMap { pendingTaskTracker.AddTask(task) } mergedProgress := mergeGetTaskProgressWithDifferentPredicate(this.progress, that.progress) return &virtualSliceImpl{ state: mergedState, taskInitializer: this.taskInitializer, queueReader: this.queueReader, pendingTaskTracker: pendingTaskTracker, progress: mergedProgress, logger: this.logger, } } // merge slices with different predicates, the general idea is to find the overlap range of the 2 slices, // combine the predicates of the overlap range and leave the rest as is func mergeVirtualSlicesWithDifferentPredicate(this, that *virtualSliceImpl) ([]VirtualSlice, bool) { compareInclusiveMin := this.state.Range.InclusiveMinTaskKey.Compare(that.state.Range.InclusiveMinTaskKey) if compareInclusiveMin > 0 { this, that = that, this } else if compareInclusiveMin == 0 { if this.state.Range.ExclusiveMaxTaskKey.Compare(that.state.Range.ExclusiveMaxTaskKey) > 0 { this, that = that, this } } // Use a, b to to represent the inclusive min task key and exclusive max task key of `this` // Use x, y to to represent the inclusive min task key and exclusive max task key of `that` // At this point, we know that a <= x, (in actual world, we also know that x <= b because we know that the 2 slices can be merged, but it doesn't affect the logic), so there are 5 cases to consider: // 1. {a, b, x, y} (x >= b, a < x) -> don't merge // 2. {a, x, b, y} (a < x < b < y) -> [a, x) and [x, b) and [b, y) // 3. {a, x, b, y} (a < x < b == y) -> [a, x) and [x, b) // 4. {a, x, y, b} (a < x < y < b) -> [a, x) and [x, y) and [y, b) // 5. {x, a, b, y} (x == a < b < y) -> [x, b) and [b, y) // 6. {x, a, y, b} (x == a < y == b) -> [x, b) if compareInclusiveMin == 0 { thatLeft, thatRight, ok := that.TrySplitByTaskKey(this.state.Range.ExclusiveMaxTaskKey) if !ok { // Case 6 return []VirtualSlice{mergeVirtualSlicesByPredicate(this, that)}, true } // Case 5 return []VirtualSlice{mergeVirtualSlicesByPredicate(this, thatLeft.(*virtualSliceImpl)), thatRight}, true } thisLeft, thisRight, ok := this.TrySplitByTaskKey(that.state.Range.InclusiveMinTaskKey) if !ok { // Case 1 return nil, false } mergedVirtualSlices := make([]VirtualSlice, 0, 3) mergedVirtualSlices = append(mergedVirtualSlices, thisLeft) thatLeft, thatRight, ok := that.TrySplitByTaskKey(this.state.Range.ExclusiveMaxTaskKey) if !ok { thisRightLeft, thisRightRight, ok := thisRight.TrySplitByTaskKey(that.state.Range.ExclusiveMaxTaskKey) if !ok { // Case 3: combine predicates of thisRight and that mergedVirtualSlices = append(mergedVirtualSlices, mergeVirtualSlicesByPredicate(thisRight.(*virtualSliceImpl), that)) return mergedVirtualSlices, true } // Case 4: combine predicates of thisRightLeft and that mergedVirtualSlices = append(mergedVirtualSlices, mergeVirtualSlicesByPredicate(thisRightLeft.(*virtualSliceImpl), that)) mergedVirtualSlices = append(mergedVirtualSlices, thisRightRight) return mergedVirtualSlices, true } // Case 2: combine predicates of thisRight and thatLeft mergedVirtualSlices = append(mergedVirtualSlices, mergeVirtualSlicesByPredicate(thisRight.(*virtualSliceImpl), thatLeft.(*virtualSliceImpl))) mergedVirtualSlices = append(mergedVirtualSlices, thatRight) return mergedVirtualSlices, true } ================================================ FILE: service/history/queuev2/virtual_slice_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/queuev2 (interfaces: VirtualSlice) // // Generated by this command: // // mockgen -package queuev2 -destination virtual_slice_mock.go github.com/uber/cadence/service/history/queuev2 VirtualSlice // // Package queuev2 is a generated GoMock package. package queuev2 import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" task "github.com/uber/cadence/service/history/task" ) // MockVirtualSlice is a mock of VirtualSlice interface. type MockVirtualSlice struct { ctrl *gomock.Controller recorder *MockVirtualSliceMockRecorder isgomock struct{} } // MockVirtualSliceMockRecorder is the mock recorder for MockVirtualSlice. type MockVirtualSliceMockRecorder struct { mock *MockVirtualSlice } // NewMockVirtualSlice creates a new mock instance. func NewMockVirtualSlice(ctrl *gomock.Controller) *MockVirtualSlice { mock := &MockVirtualSlice{ctrl: ctrl} mock.recorder = &MockVirtualSliceMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockVirtualSlice) EXPECT() *MockVirtualSliceMockRecorder { return m.recorder } // Clear mocks base method. func (m *MockVirtualSlice) Clear() { m.ctrl.T.Helper() m.ctrl.Call(m, "Clear") } // Clear indicates an expected call of Clear. func (mr *MockVirtualSliceMockRecorder) Clear() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockVirtualSlice)(nil).Clear)) } // GetPendingTaskCount mocks base method. func (m *MockVirtualSlice) GetPendingTaskCount() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPendingTaskCount") ret0, _ := ret[0].(int) return ret0 } // GetPendingTaskCount indicates an expected call of GetPendingTaskCount. func (mr *MockVirtualSliceMockRecorder) GetPendingTaskCount() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingTaskCount", reflect.TypeOf((*MockVirtualSlice)(nil).GetPendingTaskCount)) } // GetState mocks base method. func (m *MockVirtualSlice) GetState() VirtualSliceState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetState") ret0, _ := ret[0].(VirtualSliceState) return ret0 } // GetState indicates an expected call of GetState. func (mr *MockVirtualSliceMockRecorder) GetState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetState", reflect.TypeOf((*MockVirtualSlice)(nil).GetState)) } // GetTasks mocks base method. func (m *MockVirtualSlice) GetTasks(arg0 context.Context, arg1 int) ([]task.Task, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTasks", arg0, arg1) ret0, _ := ret[0].([]task.Task) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTasks indicates an expected call of GetTasks. func (mr *MockVirtualSliceMockRecorder) GetTasks(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTasks", reflect.TypeOf((*MockVirtualSlice)(nil).GetTasks), arg0, arg1) } // HasMoreTasks mocks base method. func (m *MockVirtualSlice) HasMoreTasks() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasMoreTasks") ret0, _ := ret[0].(bool) return ret0 } // HasMoreTasks indicates an expected call of HasMoreTasks. func (mr *MockVirtualSliceMockRecorder) HasMoreTasks() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasMoreTasks", reflect.TypeOf((*MockVirtualSlice)(nil).HasMoreTasks)) } // IsEmpty mocks base method. func (m *MockVirtualSlice) IsEmpty() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsEmpty") ret0, _ := ret[0].(bool) return ret0 } // IsEmpty indicates an expected call of IsEmpty. func (mr *MockVirtualSliceMockRecorder) IsEmpty() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsEmpty", reflect.TypeOf((*MockVirtualSlice)(nil).IsEmpty)) } // PendingTaskStats mocks base method. func (m *MockVirtualSlice) PendingTaskStats() PendingTaskStats { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PendingTaskStats") ret0, _ := ret[0].(PendingTaskStats) return ret0 } // PendingTaskStats indicates an expected call of PendingTaskStats. func (mr *MockVirtualSliceMockRecorder) PendingTaskStats() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PendingTaskStats", reflect.TypeOf((*MockVirtualSlice)(nil).PendingTaskStats)) } // TryMergeWithVirtualSlice mocks base method. func (m *MockVirtualSlice) TryMergeWithVirtualSlice(arg0 VirtualSlice) ([]VirtualSlice, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TryMergeWithVirtualSlice", arg0) ret0, _ := ret[0].([]VirtualSlice) ret1, _ := ret[1].(bool) return ret0, ret1 } // TryMergeWithVirtualSlice indicates an expected call of TryMergeWithVirtualSlice. func (mr *MockVirtualSliceMockRecorder) TryMergeWithVirtualSlice(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryMergeWithVirtualSlice", reflect.TypeOf((*MockVirtualSlice)(nil).TryMergeWithVirtualSlice), arg0) } // TrySplitByPredicate mocks base method. func (m *MockVirtualSlice) TrySplitByPredicate(arg0 Predicate) (VirtualSlice, VirtualSlice, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TrySplitByPredicate", arg0) ret0, _ := ret[0].(VirtualSlice) ret1, _ := ret[1].(VirtualSlice) ret2, _ := ret[2].(bool) return ret0, ret1, ret2 } // TrySplitByPredicate indicates an expected call of TrySplitByPredicate. func (mr *MockVirtualSliceMockRecorder) TrySplitByPredicate(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TrySplitByPredicate", reflect.TypeOf((*MockVirtualSlice)(nil).TrySplitByPredicate), arg0) } // TrySplitByTaskKey mocks base method. func (m *MockVirtualSlice) TrySplitByTaskKey(arg0 persistence.HistoryTaskKey) (VirtualSlice, VirtualSlice, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TrySplitByTaskKey", arg0) ret0, _ := ret[0].(VirtualSlice) ret1, _ := ret[1].(VirtualSlice) ret2, _ := ret[2].(bool) return ret0, ret1, ret2 } // TrySplitByTaskKey indicates an expected call of TrySplitByTaskKey. func (mr *MockVirtualSliceMockRecorder) TrySplitByTaskKey(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TrySplitByTaskKey", reflect.TypeOf((*MockVirtualSlice)(nil).TrySplitByTaskKey), arg0) } // UpdateAndGetState mocks base method. func (m *MockVirtualSlice) UpdateAndGetState() VirtualSliceState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAndGetState") ret0, _ := ret[0].(VirtualSliceState) return ret0 } // UpdateAndGetState indicates an expected call of UpdateAndGetState. func (mr *MockVirtualSliceMockRecorder) UpdateAndGetState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAndGetState", reflect.TypeOf((*MockVirtualSlice)(nil).UpdateAndGetState)) } ================================================ FILE: service/history/queuev2/virtual_slice_test.go ================================================ package queuev2 import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" gomock "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/history/task" ) func TestMergeProgressWithSamePredicate(t *testing.T) { tests := []struct { name string left *GetTaskProgress right *GetTaskProgress expected []*GetTaskProgress }{ { name: "Case 1: [a,b,c,x,y,z] - Non-overlapping ranges", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Case 2: [a,b,x,c,y,z] - Partially overlapping ranges", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Case 3: [a,b,x,y,c,z] - Overlapping ranges with interleaved keys", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, }, }, { name: "Case 4: [a,b,x,y,z,c] - Overlapping ranges with right extending beyond", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, }, }, { name: "Case 5: [a,x,b,c,y,z] - Left range contains right range", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, }, { name: "Case 6: [a,x,b,y,c,z] - Overlapping ranges with interleaved keys", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, }, }, { name: "Case 7: [a,x,b,y,z,c] - Overlapping ranges with left extending beyond", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, }, }, { name: "Case 8: [a,x,y,b,c,z] - Right range contains left range", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, }, }, { name: "Case 9: [a,x,y,b,z,c] - Overlapping ranges with right extending beyond", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(4), }, }, }, { name: "Case 10: [a,x,y,z,b,c] - Right range completely contains left range", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := mergeProgressWithSamePredicate(tt.left, tt.right) assert.Equal(t, tt.expected, result) result = mergeProgressWithSamePredicate(tt.right, tt.left) assert.Equal(t, tt.expected, result) }) } } func TestAppendOrMergeProgressWithSamePredicate(t *testing.T) { tests := []struct { name string mergedProgress []*GetTaskProgress progress *GetTaskProgress expected []*GetTaskProgress }{ { name: "Empty slice - should append", mergedProgress: []*GetTaskProgress{}, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, { name: "Non-overlapping ranges - should append", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Overlapping ranges - should merge", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Contained ranges - should merge", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, }, { name: "Multiple existing progress - should merge with last", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, NextTaskKey: persistence.NewImmediateTaskKey(6), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, NextTaskKey: persistence.NewImmediateTaskKey(6), }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := appendOrMergeProgressWithSamePredicate(tt.mergedProgress, tt.progress) assert.Equal(t, tt.expected, result) }) } } func TestMergeGetTaskProgress(t *testing.T) { tests := []struct { name string left []*GetTaskProgress right []*GetTaskProgress expected []*GetTaskProgress }{ { name: "Empty slices", left: []*GetTaskProgress{}, right: []*GetTaskProgress{}, expected: []*GetTaskProgress{}, }, { name: "Left empty, right non-empty", left: []*GetTaskProgress{}, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, { name: "Left non-empty, right empty", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, right: []*GetTaskProgress{}, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, { name: "Non-overlapping ranges", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Overlapping ranges", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Multiple progress items with overlaps", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, NextTaskKey: persistence.NewImmediateTaskKey(6), }, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(7), }, NextTaskKey: persistence.NewImmediateTaskKey(6), }, }, }, { name: "Contained ranges", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := mergeGetTaskProgressWithSamePredicate(tt.left, tt.right) assert.Equal(t, tt.expected, result) result = mergeGetTaskProgressWithSamePredicate(tt.right, tt.left) assert.Equal(t, tt.expected, result) }) } } func TestMergeVirtualSlicesByRange(t *testing.T) { tests := []struct { name string left *virtualSliceImpl right *virtualSliceImpl expected VirtualSliceState }{ { name: "Non-overlapping ranges", left: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, Predicate: NewUniversalPredicate(), }, }, right: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewUniversalPredicate(), }, }, expected: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewUniversalPredicate(), }, }, { name: "Overlapping ranges", left: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, Predicate: NewUniversalPredicate(), }, }, right: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewUniversalPredicate(), }, }, expected: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewUniversalPredicate(), }, }, { name: "Contained ranges", left: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewUniversalPredicate(), }, }, right: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, Predicate: NewUniversalPredicate(), }, }, expected: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewUniversalPredicate(), }, }, { name: "Identical ranges", left: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, Predicate: NewUniversalPredicate(), }, }, right: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, Predicate: NewUniversalPredicate(), }, }, expected: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, Predicate: NewUniversalPredicate(), }, }, { name: "Adjacent ranges", left: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, Predicate: NewUniversalPredicate(), }, }, right: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, }, expected: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPendingTaskTracker0 := NewMockPendingTaskTracker(ctrl) mockPendingTaskTracker1 := NewMockPendingTaskTracker(ctrl) mockPendingTaskTracker1.EXPECT().GetTasks().Return(map[persistence.HistoryTaskKey]task.Task{ persistence.NewImmediateTaskKey(1): task.NewMockTask(ctrl), persistence.NewImmediateTaskKey(2): task.NewMockTask(ctrl), }) mockPendingTaskTracker0.EXPECT().AddTask(gomock.Any()).Times(2) tt.left.pendingTaskTracker = mockPendingTaskTracker0 tt.right.pendingTaskTracker = mockPendingTaskTracker1 result := mergeVirtualSlicesByRange(tt.left, tt.right) assert.Equal(t, tt.expected, result.GetState()) }) } } func TestTrySplitByTaskKey(t *testing.T) { tests := []struct { name string slice *virtualSliceImpl splitKey persistence.HistoryTaskKey expectedLeft VirtualSliceState expectedRight VirtualSliceState expectedOk bool expectedLeftProgress []*GetTaskProgress expectedRightProgress []*GetTaskProgress }{ { name: "Split at middle of range", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, splitKey: persistence.NewImmediateTaskKey(3), expectedLeft: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, Predicate: NewUniversalPredicate(), }, expectedRight: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, expectedOk: true, expectedLeftProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, expectedRightProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, }, { name: "Split at middle of range with multiple progress items", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(30), }, NextTaskKey: persistence.NewImmediateTaskKey(20), NextPageToken: []byte{1, 2, 3}, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(30), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(60), }, NextTaskKey: persistence.NewImmediateTaskKey(40), NextPageToken: []byte{4, 5, 6}, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(60), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, NextTaskKey: persistence.NewImmediateTaskKey(70), NextPageToken: []byte{7, 8, 9}, }, }, }, splitKey: persistence.NewImmediateTaskKey(50), expectedLeft: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(50), }, Predicate: NewUniversalPredicate(), }, expectedRight: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(50), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, Predicate: NewUniversalPredicate(), }, expectedOk: true, expectedLeftProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(30), }, NextTaskKey: persistence.NewImmediateTaskKey(20), NextPageToken: []byte{1, 2, 3}, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(40), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(50), }, NextTaskKey: persistence.NewImmediateTaskKey(40), }, }, expectedRightProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(50), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(60), }, NextTaskKey: persistence.NewImmediateTaskKey(50), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(60), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, NextTaskKey: persistence.NewImmediateTaskKey(70), NextPageToken: []byte{7, 8, 9}, }, }, }, { name: "Split at middle of range with multiple progress items - 2", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(30), }, NextTaskKey: persistence.NewImmediateTaskKey(20), NextPageToken: []byte{1, 2, 3}, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(30), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(60), }, NextTaskKey: persistence.NewImmediateTaskKey(55), NextPageToken: []byte{4, 5, 6}, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(60), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, NextTaskKey: persistence.NewImmediateTaskKey(70), NextPageToken: []byte{7, 8, 9}, }, }, }, splitKey: persistence.NewImmediateTaskKey(50), expectedLeft: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(50), }, Predicate: NewUniversalPredicate(), }, expectedRight: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(50), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, Predicate: NewUniversalPredicate(), }, expectedOk: true, expectedLeftProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(30), }, NextTaskKey: persistence.NewImmediateTaskKey(20), NextPageToken: []byte{1, 2, 3}, }, }, expectedRightProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(55), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(60), }, NextTaskKey: persistence.NewImmediateTaskKey(55), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(60), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, NextTaskKey: persistence.NewImmediateTaskKey(70), NextPageToken: []byte{7, 8, 9}, }, }, }, { name: "Split key outside range - should fail", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, splitKey: persistence.NewImmediateTaskKey(6), expectedOk: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueueReader := NewMockQueueReader(ctrl) tt.slice.taskInitializer = func(t persistence.Task) task.Task { mockTask := task.NewMockTask(ctrl) return mockTask } tt.slice.queueReader = mockQueueReader tt.slice.pendingTaskTracker = NewPendingTaskTracker() left, right, ok := tt.slice.TrySplitByTaskKey(tt.splitKey) assert.Equal(t, tt.expectedOk, ok) if ok { assert.Equal(t, tt.expectedLeft, left.GetState()) assert.Equal(t, tt.expectedRight, right.GetState()) // Verify progress leftImpl, ok := left.(*virtualSliceImpl) assert.True(t, ok) assert.Equal(t, tt.expectedLeftProgress, leftImpl.progress) rightImpl, ok := right.(*virtualSliceImpl) assert.True(t, ok) assert.Equal(t, tt.expectedRightProgress, rightImpl.progress) } else { assert.Nil(t, left) assert.Nil(t, right) } }) } } func TestUpdateAndGetState(t *testing.T) { tests := []struct { name string slice *virtualSliceImpl expectedState VirtualSliceState setupMock func(*MockPendingTaskTracker) }{ { name: "No pending tasks, no more tasks to read", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, }, expectedState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, setupMock: func(mock *MockPendingTaskTracker) { mock.EXPECT().PruneAckedTasks() mock.EXPECT().GetMinimumTaskKey().Return(persistence.MaximumHistoryTaskKey, false) }, }, { name: "No pending tasks, has more tasks to read", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, }, expectedState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, setupMock: func(mock *MockPendingTaskTracker) { mock.EXPECT().PruneAckedTasks() mock.EXPECT().GetMinimumTaskKey().Return(persistence.MaximumHistoryTaskKey, false) }, }, { name: "Has pending tasks, no more tasks to read", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, }, expectedState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, setupMock: func(mock *MockPendingTaskTracker) { mock.EXPECT().PruneAckedTasks() mock.EXPECT().GetMinimumTaskKey().Return(persistence.NewImmediateTaskKey(2), true) }, }, { name: "Has pending tasks, has more tasks to read", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, }, }, }, expectedState: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, setupMock: func(mock *MockPendingTaskTracker) { mock.EXPECT().PruneAckedTasks() mock.EXPECT().GetMinimumTaskKey().Return(persistence.NewImmediateTaskKey(5), true) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueueReader := NewMockQueueReader(ctrl) mockPendingTaskTracker := NewMockPendingTaskTracker(ctrl) tt.slice.queueReader = mockQueueReader tt.slice.pendingTaskTracker = mockPendingTaskTracker tt.slice.logger = testlogger.New(t) // Setup mock expectations using the setupMock function tt.setupMock(mockPendingTaskTracker) result := tt.slice.UpdateAndGetState() assert.Equal(t, tt.expectedState, result) }) } } func TestGetTasks(t *testing.T) { historyTasks := []persistence.Task{ &persistence.DecisionTask{}, &persistence.ActivityTask{}, &persistence.ActivityTask{}, } tests := []struct { name string slice *virtualSliceImpl pageSize int setupMock func(*MockQueueReader, *MockPendingTaskTracker) expectedTasksCount int expectedError error expectedProgress []*GetTaskProgress }{ { name: "Empty progress - should return empty tasks", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{}, }, pageSize: 10, setupMock: func(mockQueueReader *MockQueueReader, mockPendingTaskTracker *MockPendingTaskTracker) { // No expectations needed as progress is empty }, expectedTasksCount: 0, expectedError: nil, expectedProgress: []*GetTaskProgress{}, }, { name: "Single page of tasks", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(1), }, }, }, pageSize: 2, setupMock: func(mockQueueReader *MockQueueReader, mockPendingTaskTracker *MockPendingTaskTracker) { mockQueueReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(1), }, Predicate: NewUniversalPredicate(), PageSize: 2, }).Return(&GetTaskResponse{ Tasks: []persistence.Task{historyTasks[0], historyTasks[1]}, Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token"), }, }, nil) mockPendingTaskTracker.EXPECT().AddTask(gomock.Any()).Times(2) }, expectedTasksCount: 2, expectedError: nil, expectedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token"), }, }, }, { name: "Multiple pages of tasks", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token"), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, pageSize: 3, setupMock: func(mockQueueReader *MockQueueReader, mockPendingTaskTracker *MockPendingTaskTracker) { // First page mockQueueReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token"), }, Predicate: NewUniversalPredicate(), PageSize: 3, }).Return(&GetTaskResponse{ Tasks: []persistence.Task{historyTasks[0], historyTasks[1]}, Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, nil) // Second page mockQueueReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), PageSize: 1, }).Return(&GetTaskResponse{ Tasks: []persistence.Task{historyTasks[2]}, Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(6), NextPageToken: []byte("token"), }, }, nil) mockPendingTaskTracker.EXPECT().AddTask(gomock.Any()).Times(3) }, expectedTasksCount: 3, expectedError: nil, expectedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(6), NextPageToken: []byte("token"), }, }, }, { name: "Error from queue reader", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(1), }, }, }, pageSize: 2, setupMock: func(mockQueueReader *MockQueueReader, mockPendingTaskTracker *MockPendingTaskTracker) { mockQueueReader.EXPECT().GetTask(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) }, expectedTasksCount: 0, expectedError: assert.AnError, }, { name: "Single page of tasks with error from queue reader", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(1), }, }, }, pageSize: 2, setupMock: func(mockQueueReader *MockQueueReader, mockPendingTaskTracker *MockPendingTaskTracker) { mockQueueReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(1), }, Predicate: NewUniversalPredicate(), PageSize: 2, }).Return(&GetTaskResponse{ Tasks: []persistence.Task{historyTasks[0]}, Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token"), }, }, nil) mockPendingTaskTracker.EXPECT().AddTask(gomock.Any()).Times(1) mockQueueReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token"), }, Predicate: NewUniversalPredicate(), PageSize: 1, }).Return(nil, assert.AnError) }, expectedTasksCount: 1, expectedError: nil, expectedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token"), }, }, }, { name: "Multiple pages of tasks with error from queue reader", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token"), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, pageSize: 3, setupMock: func(mockQueueReader *MockQueueReader, mockPendingTaskTracker *MockPendingTaskTracker) { // First page mockQueueReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token"), }, Predicate: NewUniversalPredicate(), PageSize: 3, }).Return(&GetTaskResponse{ Tasks: []persistence.Task{historyTasks[0], historyTasks[1]}, Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, nil) // Second page mockQueueReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), PageSize: 1, }).Return(nil, assert.AnError) mockPendingTaskTracker.EXPECT().AddTask(gomock.Any()).Times(2) }, expectedTasksCount: 2, expectedError: nil, expectedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Single page of tasks with next task key equal to exclusive max task key but non-empty next page token", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(1), }, }, }, pageSize: 2, setupMock: func(mockQueueReader *MockQueueReader, mockPendingTaskTracker *MockPendingTaskTracker) { mockQueueReader.EXPECT().GetTask(gomock.Any(), &GetTaskRequest{ Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(1), }, Predicate: NewUniversalPredicate(), PageSize: 2, }).Return(&GetTaskResponse{ Tasks: []persistence.Task{historyTasks[0]}, Progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token"), }, }, nil) mockPendingTaskTracker.EXPECT().AddTask(gomock.Any()).Times(1) }, expectedTasksCount: 1, expectedError: nil, expectedProgress: []*GetTaskProgress{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueueReader := NewMockQueueReader(ctrl) mockPendingTaskTracker := NewMockPendingTaskTracker(ctrl) tt.slice.queueReader = mockQueueReader tt.slice.pendingTaskTracker = mockPendingTaskTracker tt.slice.taskInitializer = func(t persistence.Task) task.Task { return task.NewMockTask(ctrl) } // Setup mock expectations tt.setupMock(mockQueueReader, mockPendingTaskTracker) tasks, err := tt.slice.GetTasks(context.Background(), tt.pageSize) if tt.expectedError != nil { assert.Error(t, err) assert.Equal(t, tt.expectedError, err) } else { assert.NoError(t, err) assert.Len(t, tasks, tt.expectedTasksCount) } }) } } func TestMergeProgressWithDifferentPredicate(t *testing.T) { tests := []struct { name string left *GetTaskProgress right *GetTaskProgress expected []*GetTaskProgress }{ { name: "Case 1: Non-overlapping ranges", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Case 1,2: Adjacent ranges", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Case 2: Overlapping ranges", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(2), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Case 3: Adjacent ranges", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: nil, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, { name: "Case 3: Overlapping ranges", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(4), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, { name: "Case 4: Completely overlapping ranges - left contains right", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, }, { name: "Case 3,4: Equal exclusive max task keys", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(4), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, { name: "Case 5: Overlapping ranges", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(3), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, }, { name: "Case 6: Overlapping ranges with same next task key", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, }, { name: "Case 7: Completely overlapping ranges - left contains right", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, }, { name: "Identical ranges and next task keys", left: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token1"), }, right: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextPageToken: nil, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // make sure that these test inputs are valid if tt.left.NextTaskKey.Compare(tt.left.Range.InclusiveMinTaskKey) == 0 { require.Nil(t, tt.left.NextPageToken) } else { require.NotNil(t, tt.left.NextPageToken) } if tt.right.NextTaskKey.Compare(tt.right.Range.InclusiveMinTaskKey) == 0 { require.Nil(t, tt.right.NextPageToken) } else { require.NotNil(t, tt.right.NextPageToken) } // test the merge function result := mergeProgressWithDifferentPredicate(tt.left, tt.right) assert.Equal(t, tt.expected, result) // Test commutativity - the function should handle argument order internally result2 := mergeProgressWithDifferentPredicate(tt.right, tt.left) assert.Equal(t, tt.expected, result2) }) } } func TestAppendOrMergeProgressWithDifferentPredicate(t *testing.T) { tests := []struct { name string mergedProgress []*GetTaskProgress progress *GetTaskProgress expected []*GetTaskProgress }{ { name: "Empty slice - should append", mergedProgress: []*GetTaskProgress{}, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token"), }, }, }, { name: "Non-overlapping ranges - should append both with reset tokens", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: nil, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: nil, }, }, }, { name: "Adjacent ranges - should merge into single range", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: nil, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: nil, }, }, }, { name: "Overlapping ranges - should merge with earliest next task key", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: nil, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: nil, }, }, }, { name: "Completely contained ranges - should merge to outer range", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token1"), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: nil, }, }, }, { name: "Same next task key - different ranges", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token1"), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token2"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: nil, }, }, }, { name: "Multiple existing progress - should merge with last only", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(6), NextPageToken: []byte("token3"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: nil, }, }, }, { name: "Multiple existing progress - last range non-overlapping", mergedProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, }, progress: &GetTaskProgress{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(8), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(9), NextPageToken: []byte("token3"), }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: nil, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(9), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(9), NextPageToken: nil, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := appendOrMergeProgressWithDifferentPredicate(tt.mergedProgress, tt.progress) assert.Equal(t, tt.expected, result) }) } } func TestMergeGetTaskProgressWithDifferentPredicate(t *testing.T) { tests := []struct { name string left []*GetTaskProgress right []*GetTaskProgress expected []*GetTaskProgress }{ { name: "Empty slices", left: []*GetTaskProgress{}, right: []*GetTaskProgress{}, expected: []*GetTaskProgress{}, }, { name: "Left empty, right non-empty", left: []*GetTaskProgress{}, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token"), }, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token"), }, }, }, { name: "Left non-empty, right empty", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token"), }, }, right: []*GetTaskProgress{}, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token"), }, }, }, { name: "Non-overlapping ranges - ordered by NextTaskKey", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, }, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: nil, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: nil, }, }, }, { name: "Adjacent ranges by NextTaskKey", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, }, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(4), // comes after left's range NextPageToken: []byte("token2"), }, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: nil, }, }, }, { name: "Multiple progress items - complex merge by NextTaskKey", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: []byte("token1"), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(7), NextPageToken: []byte("token2"), }, }, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(4), NextPageToken: []byte("token3"), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(8), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(9), NextPageToken: []byte("token4"), }, }, expected: []*GetTaskProgress{ // Merged based on NextTaskKey order: 2, 4, 7, 9 { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), NextPageToken: nil, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(4), NextPageToken: nil, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(7), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(7), NextPageToken: nil, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(9), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(9), NextPageToken: nil, }, }, }, { name: "Overlapping ranges with interleaved NextTaskKeys", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token1"), }, }, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(5), NextPageToken: []byte("token2"), }, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: nil, }, }, }, { name: "Same NextTaskKey - should merge ranges", left: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(4), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: []byte("token1"), }, }, right: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), // Same NextTaskKey NextPageToken: []byte("token2"), }, }, expected: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(3), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), NextPageToken: nil, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := mergeGetTaskProgressWithDifferentPredicate(tt.left, tt.right) assert.Equal(t, tt.expected, result) }) } } func TestVirtualSliceImpl_TrySplitByPredicate(t *testing.T) { tests := []struct { name string slice *virtualSliceImpl splitPredicate Predicate expectedLeft VirtualSliceState expectedRight VirtualSliceState expectedOk bool expectedLeftProgress []*GetTaskProgress expectedRightProgress []*GetTaskProgress }{ { name: "Universal predicate should not split", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, splitPredicate: NewUniversalPredicate(), expectedOk: false, }, { name: "Empty predicate should not split", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, splitPredicate: NewEmptyPredicate(), expectedOk: false, }, { name: "Identical predicate should not split", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, splitPredicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), expectedOk: false, }, { name: "Different predicate should split successfully", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, splitPredicate: NewDomainIDPredicate([]string{"domain3"}, false), expectedOk: true, expectedLeft: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: And(NewDomainIDPredicate([]string{"domain1", "domain2"}, false), NewDomainIDPredicate([]string{"domain3"}, false)), }, expectedRight: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: And(NewDomainIDPredicate([]string{"domain1", "domain2"}, false), Not(NewDomainIDPredicate([]string{"domain3"}, false))), }, expectedLeftProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, expectedRightProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, }, { name: "Split with multiple progress items", slice: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, Predicate: NewUniversalPredicate(), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(30), }, NextTaskKey: persistence.NewImmediateTaskKey(20), NextPageToken: []byte{1, 2, 3}, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(30), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(60), }, NextTaskKey: persistence.NewImmediateTaskKey(40), NextPageToken: []byte{4, 5, 6}, }, }, }, splitPredicate: NewDomainIDPredicate([]string{"domain1"}, false), expectedOk: true, expectedLeft: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, Predicate: And(NewUniversalPredicate(), NewDomainIDPredicate([]string{"domain1"}, false)), }, expectedRight: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(100), }, Predicate: And(NewUniversalPredicate(), Not(NewDomainIDPredicate([]string{"domain1"}, false))), }, expectedLeftProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(30), }, NextTaskKey: persistence.NewImmediateTaskKey(20), NextPageToken: []byte{1, 2, 3}, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(30), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(60), }, NextTaskKey: persistence.NewImmediateTaskKey(40), NextPageToken: []byte{4, 5, 6}, }, }, expectedRightProgress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(30), }, NextTaskKey: persistence.NewImmediateTaskKey(20), NextPageToken: []byte{1, 2, 3}, }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(30), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(60), }, NextTaskKey: persistence.NewImmediateTaskKey(40), NextPageToken: []byte{4, 5, 6}, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockQueueReader := NewMockQueueReader(ctrl) tt.slice.taskInitializer = func(t persistence.Task) task.Task { mockTask := task.NewMockTask(ctrl) return mockTask } tt.slice.queueReader = mockQueueReader tt.slice.pendingTaskTracker = NewPendingTaskTracker() left, right, ok := tt.slice.TrySplitByPredicate(tt.splitPredicate) assert.Equal(t, tt.expectedOk, ok) if ok { assert.Equal(t, tt.expectedLeft, left.GetState()) assert.Equal(t, tt.expectedRight, right.GetState()) // Verify progress leftImpl, ok := left.(*virtualSliceImpl) assert.True(t, ok) assert.Equal(t, tt.expectedLeftProgress, leftImpl.progress) rightImpl, ok := right.(*virtualSliceImpl) assert.True(t, ok) assert.Equal(t, tt.expectedRightProgress, rightImpl.progress) } else { assert.Nil(t, left) assert.Nil(t, right) } }) } } func TestPendingTaskStats(t *testing.T) { tests := []struct { name string mockSetup func(*MockPendingTaskTracker) expectedStats PendingTaskStats }{ { name: "Empty pending task tracker - should return empty stats", mockSetup: func(mock *MockPendingTaskTracker) { mock.EXPECT().GetPerDomainPendingTaskCount().Return(map[string]int{}) }, expectedStats: PendingTaskStats{ PendingTaskCountPerDomain: map[string]int{}, }, }, { name: "Single domain with tasks - should return correct stats", mockSetup: func(mock *MockPendingTaskTracker) { mock.EXPECT().GetPerDomainPendingTaskCount().Return(map[string]int{ "domain1": 5, }) }, expectedStats: PendingTaskStats{ PendingTaskCountPerDomain: map[string]int{ "domain1": 5, }, }, }, { name: "Multiple domains with tasks - should return correct stats", mockSetup: func(mock *MockPendingTaskTracker) { mock.EXPECT().GetPerDomainPendingTaskCount().Return(map[string]int{ "domain1": 3, "domain2": 7, "domain3": 2, }) }, expectedStats: PendingTaskStats{ PendingTaskCountPerDomain: map[string]int{ "domain1": 3, "domain2": 7, "domain3": 2, }, }, }, { name: "Domain with zero tasks - should include zero counts", mockSetup: func(mock *MockPendingTaskTracker) { mock.EXPECT().GetPerDomainPendingTaskCount().Return(map[string]int{ "domain1": 5, "domain2": 0, "domain3": 3, }) }, expectedStats: PendingTaskStats{ PendingTaskCountPerDomain: map[string]int{ "domain1": 5, "domain2": 0, "domain3": 3, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockPendingTaskTracker := NewMockPendingTaskTracker(ctrl) // Setup the virtual slice with mock dependencies slice := &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewUniversalPredicate(), }, pendingTaskTracker: mockPendingTaskTracker, } // Setup mock expectations tt.mockSetup(mockPendingTaskTracker) // Call the method under test result := slice.PendingTaskStats() // Verify the result assert.Equal(t, tt.expectedStats, result) }) } } func TestMergeVirtualSlicesWithDifferentPredicate(t *testing.T) { tests := []struct { name string this *virtualSliceImpl that *virtualSliceImpl expectedStates []VirtualSliceState expectedOk bool }{ { name: "Case 1: Non-overlapping ranges", this: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(3), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, that: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(4), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(5), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, expectedOk: false, }, { name: "Case 2: Overlapping ranges", this: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, that: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, Predicate: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, expectedOk: true, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(2), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3", "domain4"}, false), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(5), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(8), }, Predicate: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), }, }, }, { name: "Case 3: Completely overlapping ranges - left contains right and has the same exclusive max task key", this: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, that: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, expectedOk: true, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(2), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(5), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3", "domain4"}, false), }, }, }, { name: "Case 4: Completely overlapping ranges - left contains right and has different exclusive max task key", this: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, that: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(3), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, expectedOk: true, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(2), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(2), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3", "domain4"}, false), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, }, }, { name: "Case 5: Completely overlapping ranges - left contains right and has different exclusive max task key", this: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, that: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, expectedOk: true, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(6), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3", "domain4"}, false), }, { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(6), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, }, }, { name: "Case 6: Same ranges", this: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, that: &virtualSliceImpl{ state: VirtualSliceState{ Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewDomainIDPredicate([]string{"domain3", "domain4"}, false), }, progress: []*GetTaskProgress{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, NextTaskKey: persistence.NewImmediateTaskKey(2), }, }, pendingTaskTracker: NewPendingTaskTracker(), }, expectedOk: true, expectedStates: []VirtualSliceState{ { Range: Range{ InclusiveMinTaskKey: persistence.NewImmediateTaskKey(1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(10), }, Predicate: NewDomainIDPredicate([]string{"domain1", "domain2", "domain3", "domain4"}, false), }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mergedSlices, ok := mergeVirtualSlicesWithDifferentPredicate(tt.this, tt.that) assert.Equal(t, tt.expectedOk, ok) if ok { actualStates := make([]VirtualSliceState, len(mergedSlices)) for i, slice := range mergedSlices { actualStates[i] = slice.GetState() } assert.Equal(t, tt.expectedStates, actualStates) } // Test commutativity - the function should handle argument order internally mergedSlices, ok = mergeVirtualSlicesWithDifferentPredicate(tt.that, tt.this) assert.Equal(t, tt.expectedOk, ok) if ok { actualStates := make([]VirtualSliceState, len(mergedSlices)) for i, slice := range mergedSlices { actualStates[i] = slice.GetState() } assert.Equal(t, tt.expectedStates, actualStates) } }) } } ================================================ FILE: service/history/replication/dlq_handler.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package replication import ( "context" "strconv" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/shard" ) const ( defaultBeginningMessageID = -1 ) var ( errInvalidCluster = &types.BadRequestError{Message: "Invalid target cluster name."} ) type ( // DLQHandler is the interface handles replication DLQ messages DLQHandler interface { common.Daemon GetMessageCount( ctx context.Context, forceFetch bool, ) (map[string]int64, error) ReadMessages( ctx context.Context, sourceCluster string, lastMessageID int64, pageSize int, pageToken []byte, ) ([]*types.ReplicationTask, []*types.ReplicationTaskInfo, []byte, error) PurgeMessages( ctx context.Context, sourceCluster string, lastMessageID int64, ) error MergeMessages( ctx context.Context, sourceCluster string, lastMessageID int64, pageSize int, pageToken []byte, ) ([]byte, error) } dlqHandlerImpl struct { taskExecutors map[string]TaskExecutor shard shard.Context logger log.Logger metricsClient metrics.Client done chan struct{} status int32 timeSource clock.TimeSource mu sync.Mutex latestCounts map[string]int64 } ) var _ DLQHandler = (*dlqHandlerImpl)(nil) // NewDLQHandler initialize the replication message DLQ handler func NewDLQHandler( shard shard.Context, taskExecutors map[string]TaskExecutor, ) DLQHandler { if taskExecutors == nil { panic("Failed to initialize replication DLQ handler due to nil task executors") } return &dlqHandlerImpl{ shard: shard, taskExecutors: taskExecutors, logger: shard.GetLogger(), metricsClient: shard.GetMetricsClient(), done: make(chan struct{}), timeSource: clock.NewRealTimeSource(), } } // Start starts the DLQ handler func (r *dlqHandlerImpl) Start() { if !atomic.CompareAndSwapInt32(&r.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } go r.emitDLQSizeMetricsLoop() r.logger.Info("DLQ handler started.") } // Stop stops the DLQ handler func (r *dlqHandlerImpl) Stop() { if !atomic.CompareAndSwapInt32(&r.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } r.logger.Debug("DLQ handler shutting down.") close(r.done) } func (r *dlqHandlerImpl) GetMessageCount(ctx context.Context, forceFetch bool) (map[string]int64, error) { if forceFetch || r.latestCounts == nil { if err := r.fetchAndEmitMessageCount(ctx); err != nil { return nil, err } } return r.latestCounts, nil } func (r *dlqHandlerImpl) fetchAndEmitMessageCount(ctx context.Context) error { shardID := strconv.Itoa(r.shard.GetShardID()) result := map[string]int64{} for sourceCluster := range r.taskExecutors { request := persistence.GetReplicationDLQSizeRequest{SourceClusterName: sourceCluster} response, err := r.shard.GetExecutionManager().GetReplicationDLQSize(ctx, &request) if err != nil { r.logger.Error("failed to get replication DLQ size", tag.Error(err)) r.metricsClient.Scope(metrics.ReplicationDLQStatsScope).IncCounter(metrics.ReplicationDLQProbeFailed) return err } r.metricsClient.Scope( metrics.ReplicationDLQStatsScope, metrics.SourceClusterTag(sourceCluster), metrics.InstanceTag(shardID), ).UpdateGauge(metrics.ReplicationDLQSize, float64(response.Size)) if response.Size > 0 { result[sourceCluster] = response.Size } } r.mu.Lock() r.latestCounts = result r.mu.Unlock() return nil } func (r *dlqHandlerImpl) emitDLQSizeMetricsLoop() { getInterval := func() time.Duration { return backoff.JitDuration( dlqMetricsEmitTimerInterval, dlqMetricsEmitTimerCoefficient, ) } timer := r.timeSource.NewTimer(getInterval()) defer timer.Stop() for { select { case <-timer.Chan(): r.fetchAndEmitMessageCount(context.Background()) timer.Reset(getInterval()) case <-r.done: return } } } func (r *dlqHandlerImpl) ReadMessages( ctx context.Context, sourceCluster string, lastMessageID int64, pageSize int, pageToken []byte, ) ([]*types.ReplicationTask, []*types.ReplicationTaskInfo, []byte, error) { return r.readMessagesWithAckLevel( ctx, sourceCluster, lastMessageID, pageSize, pageToken, ) } func (r *dlqHandlerImpl) readMessagesWithAckLevel( ctx context.Context, sourceCluster string, lastMessageID int64, pageSize int, pageToken []byte, ) ([]*types.ReplicationTask, []*types.ReplicationTaskInfo, []byte, error) { resp, err := r.shard.GetExecutionManager().GetReplicationTasksFromDLQ( ctx, &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: sourceCluster, ReadLevel: defaultBeginningMessageID + 1, MaxReadLevel: lastMessageID + 1, BatchSize: pageSize, NextPageToken: pageToken, }, ) if err != nil { return nil, nil, nil, err } remoteAdminClient, err := r.shard.GetService().GetClientBean().GetRemoteAdminClient(sourceCluster) if err != nil { return nil, nil, nil, err } taskInfo := make([]*types.ReplicationTaskInfo, 0, len(resp.Tasks)) for _, task := range resp.Tasks { ti, err := task.ToInternalReplicationTaskInfo() if err != nil { return nil, nil, nil, err } taskInfo = append(taskInfo, ti) } response := &types.GetDLQReplicationMessagesResponse{} if len(taskInfo) > 0 { response, err = remoteAdminClient.GetDLQReplicationMessages( ctx, &types.GetDLQReplicationMessagesRequest{ TaskInfos: taskInfo, }, ) if err != nil { return nil, nil, nil, err } } return response.ReplicationTasks, taskInfo, resp.NextPageToken, nil } func (r *dlqHandlerImpl) PurgeMessages( ctx context.Context, sourceCluster string, lastMessageID int64, ) error { _, err := r.shard.GetExecutionManager().RangeDeleteReplicationTaskFromDLQ( ctx, &persistence.RangeDeleteReplicationTaskFromDLQRequest{ SourceClusterName: sourceCluster, InclusiveBeginTaskID: defaultBeginningMessageID + 1, ExclusiveEndTaskID: lastMessageID + 1, }, ) if err != nil { return err } return nil } func (r *dlqHandlerImpl) MergeMessages( ctx context.Context, sourceCluster string, lastMessageID int64, pageSize int, pageToken []byte, ) ([]byte, error) { if _, ok := r.taskExecutors[sourceCluster]; !ok { return nil, errInvalidCluster } tasks, rawTasks, token, err := r.readMessagesWithAckLevel( ctx, sourceCluster, lastMessageID, pageSize, pageToken, ) if err != nil { return nil, err } replicationTasks := map[int64]*types.ReplicationTask{} for _, task := range tasks { replicationTasks[task.SourceTaskID] = task } lastMessageID = defaultBeginningMessageID for _, raw := range rawTasks { if task, ok := replicationTasks[raw.TaskID]; ok { if _, err := r.taskExecutors[sourceCluster].execute(task, true); err != nil { return nil, err } } // If hydrated replication task does not exist in remote cluster - continue merging // Record lastMessageID with raw task id, so that they can be purged after. if lastMessageID < raw.TaskID { lastMessageID = raw.TaskID } } _, err = r.shard.GetExecutionManager().RangeDeleteReplicationTaskFromDLQ( ctx, &persistence.RangeDeleteReplicationTaskFromDLQRequest{ SourceClusterName: sourceCluster, InclusiveBeginTaskID: defaultBeginningMessageID + 1, ExclusiveEndTaskID: lastMessageID + 1, }, ) if err != nil { return nil, err } return token, nil } ================================================ FILE: service/history/replication/dlq_handler_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package replication import ( "context" "errors" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/client" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" ) type ( dlqHandlerSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext config *config.Config mockClientBean *client.MockBean adminClient *admin.MockClient executionManager *mocks.ExecutionManager shardManager *mocks.ShardManager taskExecutor *fakeTaskExecutor taskExecutors map[string]TaskExecutor sourceCluster string messageHandler *dlqHandlerImpl } ) func TestDLQMessageHandlerSuite(t *testing.T) { s := new(dlqHandlerSuite) suite.Run(t, s) } func (s *dlqHandlerSuite) SetupSuite() { } func (s *dlqHandlerSuite) TearDownSuite() { } func (s *dlqHandlerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.config = config.NewForTest() s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, ReplicationDLQAckLevel: map[string]int64{"test": -1}, }, s.config, ) s.mockClientBean = s.mockShard.Resource.ClientBean s.adminClient = s.mockShard.Resource.RemoteAdminClient s.executionManager = s.mockShard.Resource.ExecutionMgr s.shardManager = s.mockShard.Resource.ShardMgr s.taskExecutors = make(map[string]TaskExecutor) s.taskExecutor = &fakeTaskExecutor{} s.sourceCluster = "test" s.taskExecutors[s.sourceCluster] = s.taskExecutor s.messageHandler = NewDLQHandler( s.mockShard, s.taskExecutors, ).(*dlqHandlerImpl) } func (s *dlqHandlerSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *dlqHandlerSuite) TestNewDLQHandler_panic() { s.Panics(func() { NewDLQHandler(s.mockShard, nil) }, "Failed to initialize replication DLQ handler due to nil task executors") } func (s *dlqHandlerSuite) TestStartStop() { tests := []struct { name string status int32 }{ { name: "started", status: common.DaemonStatusInitialized, }, { name: "not started", status: common.DaemonStatusStopped, }, } for _, tc := range tests { s.T().Run(tc.name, func(t *testing.T) { defer goleak.VerifyNone(t) s.messageHandler.status = tc.status s.messageHandler.Start() s.messageHandler.Stop() }) } } func (s *dlqHandlerSuite) TestGetMessageCount() { size := int64(1) tests := []struct { name string latestCounts map[string]int64 forceFetch bool err error }{ { name: "success", latestCounts: map[string]int64{s.sourceCluster: size}, }, { name: "success with fetchAndEmitMessageCount call", forceFetch: true, }, { name: "error", forceFetch: true, err: errors.New("fetchAndEmitMessageCount error"), }, } for _, tc := range tests { s.T().Run(tc.name, func(t *testing.T) { s.messageHandler.latestCounts = tc.latestCounts if tc.forceFetch || tc.latestCounts == nil { s.executionManager.On("GetReplicationDLQSize", mock.Anything, mock.Anything).Return(&persistence.GetReplicationDLQSizeResponse{Size: size}, tc.err).Times(1) } counts, err := s.messageHandler.GetMessageCount(context.Background(), tc.forceFetch) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else if tc.latestCounts != nil { s.NoError(err) s.Equal(size, counts[s.sourceCluster]) } else { s.NoError(err) } }) } } func (s *dlqHandlerSuite) TestFetchAndEmitMessageCount() { tests := []struct { name string err error }{ { name: "success", err: nil, }, { name: "error", err: errors.New("error"), }, } for _, tc := range tests { s.T().Run(tc.name, func(t *testing.T) { size := int64(3) rets := &persistence.GetReplicationDLQSizeResponse{Size: size} s.messageHandler.latestCounts = make(map[string]int64) s.executionManager.On("GetReplicationDLQSize", context.Background(), mock.Anything).Return(rets, tc.err).Times(1) err := s.messageHandler.fetchAndEmitMessageCount(context.Background()) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) s.Equal(len(s.messageHandler.latestCounts), len(s.taskExecutors)) s.Equal(size, s.messageHandler.latestCounts[s.sourceCluster]) } }) } } func (s *dlqHandlerSuite) TestEmitDLQSizeMetricsLoop_FetchesAndEmitsMetricsPeriodically() { defer goleak.VerifyNone(s.T()) emissionNumber := 2 s.messageHandler.status = common.DaemonStatusStarted s.executionManager.On("GetReplicationDLQSize", mock.Anything, mock.Anything).Return(&persistence.GetReplicationDLQSizeResponse{Size: 1}, nil).Times(emissionNumber) mockTimeSource := clock.NewMockedTimeSource() s.messageHandler.timeSource = mockTimeSource go s.messageHandler.emitDLQSizeMetricsLoop() for i := 0; i < emissionNumber; i++ { mockTimeSource.BlockUntil(1) // Advance time to trigger the next emission mockTimeSource.Advance(dlqMetricsEmitTimerInterval + time.Duration(int64(float64(dlqMetricsEmitTimerInterval)*(1+dlqMetricsEmitTimerCoefficient)))) } s.messageHandler.Stop() s.Equal(common.DaemonStatusStopped, s.messageHandler.status) } func (s *dlqHandlerSuite) TestReadMessages_OK() { ctx := context.Background() lastMessageID := int64(1) pageSize := 1 var pageToken []byte resp := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), }, TaskData: persistence.TaskData{ TaskID: 1, }, }, }, } s.executionManager.On("GetReplicationTasksFromDLQ", mock.Anything, &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: s.sourceCluster, ReadLevel: 0, MaxReadLevel: lastMessageID + 1, BatchSize: pageSize, NextPageToken: pageToken, }).Return(resp, nil).Times(1) s.mockClientBean.EXPECT().GetRemoteAdminClient(s.sourceCluster).Return(s.adminClient, nil).AnyTimes() s.adminClient.EXPECT(). GetDLQReplicationMessages(ctx, gomock.Any()). Return(&types.GetDLQReplicationMessagesResponse{}, nil) tasks, info, token, err := s.messageHandler.ReadMessages(ctx, s.sourceCluster, lastMessageID, pageSize, pageToken) s.NoError(err) s.Nil(token) s.Equal(resp.Tasks[0].GetDomainID(), info[0].GetDomainID()) s.Equal(resp.Tasks[0].GetWorkflowID(), info[0].GetWorkflowID()) s.Equal(resp.Tasks[0].GetRunID(), info[0].GetRunID()) s.Nil(tasks) } func (s *dlqHandlerSuite) TestReadMessagesWithAckLevel_OK() { replicationTasksResponse := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domainID", WorkflowID: "workflowID", RunID: "runID", }, TaskData: persistence.TaskData{ TaskID: 123, Version: 1, }, FirstEventID: 1, NextEventID: 2, }, }, NextPageToken: []byte("token"), } DLQReplicationMessagesResponse := &types.GetDLQReplicationMessagesResponse{ ReplicationTasks: []*types.ReplicationTask{ { SourceTaskID: 123, }, }, } ctx := context.Background() lastMessageID := int64(123) pageSize := 12 pageToken := []byte("token") req := &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: s.sourceCluster, ReadLevel: defaultBeginningMessageID + 1, MaxReadLevel: lastMessageID + 1, BatchSize: pageSize, NextPageToken: pageToken, } s.executionManager.On("GetReplicationTasksFromDLQ", ctx, req).Return(replicationTasksResponse, nil).Times(1) s.adminClient.EXPECT(). GetDLQReplicationMessages(ctx, gomock.Any()). Return(DLQReplicationMessagesResponse, nil).Times(1) replicationTasks, taskInfo, nextPageToken, err := s.messageHandler.readMessagesWithAckLevel(ctx, s.sourceCluster, lastMessageID, pageSize, pageToken) s.NoError(err) s.Equal(replicationTasks, DLQReplicationMessagesResponse.ReplicationTasks) s.Len(taskInfo, len(replicationTasksResponse.Tasks)) // testing content of taskInfo because it's assembled in the method using tasks from replicationTasksFromDLQ for i, task := range taskInfo { t, err := replicationTasksResponse.Tasks[i].ToInternalReplicationTaskInfo() s.NoError(err) s.Equal(task, t) } s.Equal(nextPageToken, replicationTasksResponse.NextPageToken) } func (s *dlqHandlerSuite) TestReadMessagesWithAckLevel_GetReplicationTasksFromDLQFailed() { errorMessage := "GetReplicationTasksFromDLQFailed" ctx := context.Background() lastMessageID := int64(123) pageSize := 12 pageToken := []byte("token") req := &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: s.sourceCluster, ReadLevel: defaultBeginningMessageID + 1, MaxReadLevel: lastMessageID + 1, BatchSize: pageSize, NextPageToken: pageToken, } s.executionManager.On("GetReplicationTasksFromDLQ", ctx, req).Return(nil, errors.New(errorMessage)).Times(1) _, _, _, err := s.messageHandler.readMessagesWithAckLevel(ctx, s.sourceCluster, lastMessageID, pageSize, pageToken) s.Error(err) s.Equal(err, errors.New(errorMessage)) } func (s *dlqHandlerSuite) TestReadMessagesWithAckLevel_InvalidCluster() { s.executionManager.On("GetReplicationTasksFromDLQ", mock.Anything, mock.Anything).Return(nil, nil).Times(1) s.mockShard.Resource.ClientBean = client.NewMockBean(s.controller) s.mockShard.Resource.ClientBean.EXPECT().GetRemoteAdminClient("invalidCluster").Return(nil, errors.New("invalidCluster")).Times(1) _, _, _, err := s.messageHandler.readMessagesWithAckLevel(context.Background(), "invalidCluster", 123, 12, []byte("token")) s.Error(err) s.ErrorContains(err, "invalidCluster") } func (s *dlqHandlerSuite) TestReadMessagesWithAckLevel_GetDLQReplicationMessagesFailed() { errorMessage := "GetDLQReplicationMessagesFailed" ctx := context.Background() lastMessageID := int64(123) pageSize := 12 pageToken := []byte("token") req := &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: s.sourceCluster, ReadLevel: defaultBeginningMessageID + 1, MaxReadLevel: lastMessageID + 1, BatchSize: pageSize, NextPageToken: pageToken, } replicationTasksResponse := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.FailoverMarkerTask{ DomainID: "domainID", }, }, } s.executionManager.On("GetReplicationTasksFromDLQ", ctx, req).Return(replicationTasksResponse, nil).Times(1) s.adminClient.EXPECT(). GetDLQReplicationMessages(ctx, gomock.Any()). Return(nil, errors.New(errorMessage)).Times(1) _, _, _, err := s.messageHandler.readMessagesWithAckLevel(ctx, s.sourceCluster, lastMessageID, pageSize, pageToken) s.Error(err) s.Equal(err, errors.New(errorMessage)) } func (s *dlqHandlerSuite) TestPurgeMessages() { tests := []struct { name string err error }{ { name: "success", }, { name: "error", err: errors.New("error"), }, } for _, tc := range tests { s.T().Run(tc.name, func(t *testing.T) { lastMessageID := int64(1) s.executionManager.On("RangeDeleteReplicationTaskFromDLQ", mock.Anything, &persistence.RangeDeleteReplicationTaskFromDLQRequest{ SourceClusterName: s.sourceCluster, InclusiveBeginTaskID: 0, ExclusiveEndTaskID: lastMessageID + 1, }).Return(&persistence.RangeDeleteReplicationTaskFromDLQResponse{TasksCompleted: persistence.UnknownNumRowsAffected}, tc.err).Times(1) err := s.messageHandler.PurgeMessages(context.Background(), s.sourceCluster, lastMessageID) if tc.err != nil { s.Error(err) } else { s.NoError(err) } }) } } func (s *dlqHandlerSuite) TestMergeMessages_OK() { ctx := context.Background() lastMessageID := int64(2) pageSize := 1 var pageToken []byte resp := &persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), }, TaskData: persistence.TaskData{ TaskID: 1, }, }, &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), }, TaskData: persistence.TaskData{ TaskID: 2, }, }, }, } s.executionManager.On("GetReplicationTasksFromDLQ", mock.Anything, &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: s.sourceCluster, ReadLevel: 0, MaxReadLevel: lastMessageID + 1, BatchSize: pageSize, NextPageToken: pageToken, }).Return(resp, nil).Times(1) s.mockClientBean.EXPECT().GetRemoteAdminClient(s.sourceCluster).Return(s.adminClient, nil).AnyTimes() replicationTask := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistory.Ptr(), SourceTaskID: 1, } s.adminClient.EXPECT(). GetDLQReplicationMessages(ctx, gomock.Any()). Return(&types.GetDLQReplicationMessagesResponse{ ReplicationTasks: []*types.ReplicationTask{replicationTask}, }, nil) s.executionManager.On("RangeDeleteReplicationTaskFromDLQ", mock.Anything, &persistence.RangeDeleteReplicationTaskFromDLQRequest{ SourceClusterName: s.sourceCluster, InclusiveBeginTaskID: 0, ExclusiveEndTaskID: lastMessageID + 1, }).Return(&persistence.RangeDeleteReplicationTaskFromDLQResponse{TasksCompleted: persistence.UnknownNumRowsAffected}, nil).Times(1) token, err := s.messageHandler.MergeMessages(ctx, s.sourceCluster, lastMessageID, pageSize, pageToken) s.NoError(err) s.Nil(token) s.Equal(1, len(s.taskExecutor.executedTasks)) } func (s *dlqHandlerSuite) TestMergeMessages_InvalidCluster() { _, err := s.messageHandler.MergeMessages(context.Background(), "invalid", 1, 1, nil) s.Error(err) s.Equal(errInvalidCluster, err) } func (s *dlqHandlerSuite) TestMergeMessages_GetReplicationTasksFromDLQFailed() { errorMessage := "GetReplicationTasksFromDLQFailed" s.executionManager.On("GetReplicationTasksFromDLQ", mock.Anything, mock.Anything).Return(nil, errors.New(errorMessage)).Times(1) _, err := s.messageHandler.MergeMessages(context.Background(), s.sourceCluster, 1, 1, nil) s.Error(err) s.Equal(err, errors.New(errorMessage)) } func (s *dlqHandlerSuite) TestMergeMessages_RangeDeleteReplicationTaskFromDLQFailed() { errorMessage := "RangeDeleteReplicationTaskFromDLQFailed" s.executionManager.On("GetReplicationTasksFromDLQ", mock.Anything, mock.Anything).Return(&persistence.GetHistoryTasksResponse{}, nil).Times(1) s.executionManager.On("RangeDeleteReplicationTaskFromDLQ", mock.Anything, mock.Anything).Return(nil, errors.New(errorMessage)).Times(1) _, err := s.messageHandler.MergeMessages(context.Background(), s.sourceCluster, 1, 1, nil) s.Error(err) s.Equal(err, errors.New(errorMessage)) } func (s *dlqHandlerSuite) TestMergeMessages_executeFailed() { errorMessage := "executeFailed" s.taskExecutors[s.sourceCluster] = &fakeTaskExecutor{err: errors.New(errorMessage)} ctx := context.Background() lastMessageID := int64(2) pageSize := 1 var pageToken []byte s.executionManager.On("GetReplicationTasksFromDLQ", mock.Anything, &persistence.GetReplicationTasksFromDLQRequest{ SourceClusterName: s.sourceCluster, ReadLevel: 0, MaxReadLevel: lastMessageID + 1, BatchSize: pageSize, NextPageToken: pageToken, }).Return(&persistence.GetHistoryTasksResponse{Tasks: []persistence.Task{&persistence.HistoryReplicationTask{TaskData: persistence.TaskData{TaskID: 1}}}}, nil).Times(1) s.adminClient.EXPECT().GetDLQReplicationMessages(ctx, gomock.Any()). Return(&types.GetDLQReplicationMessagesResponse{ReplicationTasks: []*types.ReplicationTask{{SourceTaskID: 1}}}, nil) _, err := s.messageHandler.MergeMessages(ctx, s.sourceCluster, lastMessageID, pageSize, pageToken) s.Error(err) s.Equal(err, errors.New(errorMessage)) } type fakeTaskExecutor struct { scope metrics.ScopeIdx err error executedTasks []*types.ReplicationTask } func (e *fakeTaskExecutor) execute(replicationTask *types.ReplicationTask, _ bool) (metrics.ScopeIdx, error) { e.executedTasks = append(e.executedTasks, replicationTask) return e.scope, e.err } ================================================ FILE: service/history/replication/dynamic_task_batch_sizer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "strconv" "sync/atomic" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/rangeiter" "github.com/uber/cadence/service/history/config" ) // DynamicTaskBatchSizer is responsible for the batch size used to retrieve ReplicationTasks by TaskAckManager // It adjusts the task batch size based on the error and the getTasksResult, // and use the following rules: // // 1. If there is an error, decrease the task batch size. // In case of an increased load to the database, we should reduce the load to the database // Emitted metric with tag reason:"error" // // 2. If the task batch size is shrunk, decrease the task batch size. // The payload size of messages is too big, so we should decrease // the number of messages to be sure that future messages will not be shrunk // Emitted metric with tag reason:"shrunk" // // 3. If the read level of a passive cluster has not been changed and there are no fetched tasks, // not change the task batch size. There is no need to change because the replication is not stuck, // there just are no new tasks // Metric is not emitted // // 4. If the read level of a passive cluster has not been changed and if there are fetched tasks, // and number of previously fetched tasks is not zero, decrease the task batch size. // The replication is stuck on the passive side // Emitted metric with tag reason:"possible_stuck" // // 5. If the read level of a passive cluster has not been changed and if there are fetched tasks, // and number of previously fetched tasks is zero, not change the task batch size. // The replication is not stuck, and there are new tasks to be replicated // Metric is not emitted // // 6. If the read level of a passive cluster has been changed and if there are more tasks in db, // increase the task batch size. We should retrieve the maximum possible value at the next time, // as there are more tasks to be replicated // Emitted metric with tag reason:"more_tasks" // // 7. If the read level of a passive cluster has been changed and if there are no more tasks in db, // not change the size. The existing size is already enough, and there are no more tasks to be replicated // Metric is not emitted type DynamicTaskBatchSizer interface { analyse(err error, state *getTasksResult) value() int } // dynamicTaskBatchSizerImpl is the implementation of DynamicTaskBatchSizer type dynamicTaskBatchSizerImpl struct { // isFetchedTasks indicates that there are fetched tasks in the last GetTasks call isFetchedTasks atomic.Bool iter rangeiter.Iterator[int] logger log.Logger scope metrics.Scope } // NewDynamicTaskBatchSizer creates a new dynamicTaskBatchSizerImpl func NewDynamicTaskBatchSizer(shardID int, logger log.Logger, config *config.Config, metricsClient metrics.Client) DynamicTaskBatchSizer { logger = logger.WithTags(tag.ComponentReplicationDynamicTaskBatchSizer) return &dynamicTaskBatchSizerImpl{ logger: logger, scope: metricsClient.Scope( metrics.ReplicatorQueueProcessorScope, metrics.InstanceTag(strconv.Itoa(shardID)), ), iter: rangeiter.NewDynamicConfigLinearIterator( func() int { return config.ReplicatorProcessorMinTaskBatchSize(shardID) }, func() int { return config.ReplicatorProcessorMaxTaskBatchSize(shardID) }, func() int { return config.ReplicatorProcessorBatchSizeStepCount(shardID) }, logger, ), } } func (d *dynamicTaskBatchSizerImpl) analyse(err error, state *getTasksResult) { switch { case err != nil: d.decrease("error") case state.isShrunk: d.decrease("shrunk") case state.previousReadTaskID == state.lastReadTaskID && len(state.taskInfos) > 0 && d.isFetchedTasks.Load(): d.decrease("possible_stuck") case state.msgs.HasMore: d.increase("more_tasks") } // update isFetchedTasks if state == nil { d.isFetchedTasks.Store(false) return } d.isFetchedTasks.Store(len(state.taskInfos) != 0) } func (d *dynamicTaskBatchSizerImpl) value() int { return d.iter.Value() } func (d *dynamicTaskBatchSizerImpl) decrease(reason string) { oldVal, newVal := d.iter.Value(), d.iter.Previous() if oldVal != newVal { d.emitMetric(reason, "decrease") } d.logger.Debug("Decrease task batch size", tag.Reason(reason), tag.ReplicationTaskBatchSize(newVal)) } func (d *dynamicTaskBatchSizerImpl) increase(reason string) { oldVal, newVal := d.iter.Value(), d.iter.Next() if oldVal != newVal { d.emitMetric(reason, "increase") } d.logger.Debug("Increase task batch size", tag.Reason(reason), tag.ReplicationTaskBatchSize(newVal)) } func (d *dynamicTaskBatchSizerImpl) emitMetric(reason, decision string) { d.scope.Tagged( metrics.ReasonTag(reason), metrics.DecisionTag(decision), ).IncCounter(metrics.ReplicationDynamicTaskBatchSizerDecision) } ================================================ FILE: service/history/replication/dynamic_task_batch_sizer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "errors" "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" ) // TestDynamicTaskBatchSizer checks that batch size is changed dynamically based on the results of GetTasks func TestDynamicTaskBatchSizer(t *testing.T) { var ( // range [0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000] // start point: 500 min = 0 max = 1000 stepCount = 11 sizer = NewDynamicTaskBatchSizer(0, testlogger.New(t), &config.Config{ ReplicatorProcessorMinTaskBatchSize: func(_ int) int { return min }, ReplicatorProcessorMaxTaskBatchSize: func(_ int) int { return max }, ReplicatorProcessorBatchSizeStepCount: func(_ int) int { return stepCount }, }, metrics.NewNoopMetricsClient()).(*dynamicTaskBatchSizerImpl) ) t.Run("initial batch size", func(t *testing.T) { assert.Equal(t, 500, sizer.value()) }) t.Run("an error occurred 3 times", func(t *testing.T) { err := errors.New("error") sizer.analyse(err, nil) assert.Equal(t, 400, sizer.value()) assert.False(t, sizer.isFetchedTasks.Load()) sizer.analyse(err, nil) assert.Equal(t, 300, sizer.value()) assert.False(t, sizer.isFetchedTasks.Load()) sizer.analyse(err, nil) assert.Equal(t, 200, sizer.value()) assert.False(t, sizer.isFetchedTasks.Load()) }) t.Run("read level has changed 2 times and there are tasks in db", func(t *testing.T) { state := &getTasksResult{ previousReadTaskID: 0, lastReadTaskID: 10, taskInfos: make([]persistence.Task, 10), msgs: &types.ReplicationMessages{ HasMore: true, }, } sizer.analyse(nil, state) assert.Equal(t, 300, sizer.value()) assert.True(t, sizer.isFetchedTasks.Load()) sizer.analyse(nil, state) assert.Equal(t, 400, sizer.value()) assert.True(t, sizer.isFetchedTasks.Load()) }) t.Run("read level has changed, there are tasks in db, but shrunk", func(t *testing.T) { state := &getTasksResult{ previousReadTaskID: 0, lastReadTaskID: 10, msgs: &types.ReplicationMessages{ HasMore: true, }, taskInfos: make([]persistence.Task, 10), isShrunk: true, } sizer.analyse(nil, state) assert.Equal(t, 300, sizer.value()) assert.True(t, sizer.isFetchedTasks.Load()) }) t.Run("read level has not changed, replication tasks are returned", func(t *testing.T) { state := &getTasksResult{ previousReadTaskID: 10, lastReadTaskID: 10, taskInfos: make([]persistence.Task, 10), msgs: &types.ReplicationMessages{ HasMore: true, }, } sizer.analyse(nil, state) assert.Equal(t, 200, sizer.value()) assert.True(t, sizer.isFetchedTasks.Load()) }) t.Run("read level has not changed, replication tasks are not returned", func(t *testing.T) { state := &getTasksResult{ previousReadTaskID: 10, lastReadTaskID: 10, msgs: &types.ReplicationMessages{ HasMore: false, }, } sizer.analyse(nil, state) assert.Equal(t, 200, sizer.value()) assert.False(t, sizer.isFetchedTasks.Load()) }) t.Run("read level has changed 2 times and there are no tasks in db", func(t *testing.T) { state := &getTasksResult{ previousReadTaskID: 0, lastReadTaskID: 10, msgs: &types.ReplicationMessages{ HasMore: false, }, } sizer.analyse(nil, state) assert.Equal(t, 200, sizer.value()) assert.False(t, sizer.isFetchedTasks.Load()) sizer.analyse(nil, state) assert.Equal(t, 200, sizer.value()) assert.False(t, sizer.isFetchedTasks.Load()) }) t.Run("an error occurred 3 times", func(t *testing.T) { err := errors.New("error") sizer.analyse(err, nil) assert.Equal(t, 100, sizer.value()) assert.False(t, sizer.isFetchedTasks.Load()) sizer.analyse(err, nil) assert.Equal(t, 0, sizer.value()) assert.False(t, sizer.isFetchedTasks.Load()) sizer.analyse(err, nil) assert.Equal(t, 0, sizer.value()) assert.False(t, sizer.isFetchedTasks.Load()) }) t.Run("max has been changed to 500", func(t *testing.T) { // range [0, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500] // start point: 250 max = 500 assert.Equal(t, 250, sizer.value()) assert.False(t, sizer.isFetchedTasks.Load()) }) t.Run("shrunk 10 times", func(t *testing.T) { var want = []int{ 200, 150, 100, 50, 0, 0, 0, 0, 0, 0, } for i := 0; i < len(want); i++ { state := &getTasksResult{ taskInfos: make([]persistence.Task, 10), isShrunk: true, } sizer.analyse(nil, state) assert.Equal(t, want[i], sizer.value()) assert.True(t, sizer.isFetchedTasks.Load()) } }) t.Run("read level has changed and there are tasks in db", func(t *testing.T) { state := &getTasksResult{ previousReadTaskID: 0, lastReadTaskID: 10, taskInfos: make([]persistence.Task, 10), msgs: &types.ReplicationMessages{ HasMore: true, }, } sizer.analyse(nil, state) assert.Equal(t, 50, sizer.value()) assert.True(t, sizer.isFetchedTasks.Load()) }) t.Run("read level has changed and there are no tasks in db", func(t *testing.T) { state := &getTasksResult{ previousReadTaskID: 0, lastReadTaskID: 10, taskInfos: make([]persistence.Task, 5), msgs: &types.ReplicationMessages{ HasMore: false, }, } sizer.analyse(nil, state) assert.Equal(t, 50, sizer.value()) assert.True(t, sizer.isFetchedTasks.Load()) }) t.Run("read level has not changed, there are returned tasks, previously tasks were returned", func(t *testing.T) { state := &getTasksResult{ previousReadTaskID: 10, lastReadTaskID: 10, taskInfos: make([]persistence.Task, 5), msgs: &types.ReplicationMessages{ HasMore: false, }, } sizer.analyse(nil, state) assert.Equal(t, 0, sizer.value()) assert.True(t, sizer.isFetchedTasks.Load()) }) } ================================================ FILE: service/history/replication/metrics_emitter.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "context" "fmt" "strconv" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) const ( metricsEmissionInterval = time.Minute ) type ( // MetricsEmitterImpl is responsible for emitting source side replication metrics occasionally. MetricsEmitterImpl struct { shardID int currentCluster string remoteClusters map[string]config.ClusterInformation shardData metricsEmitterShardData reader taskReader scope metrics.Scope logger log.Logger status int32 interval time.Duration ctx context.Context cancelCtx context.CancelFunc wg sync.WaitGroup } // metricsEmitterShardData is for testing. metricsEmitterShardData interface { GetLogger() log.Logger GetClusterMetadata() cluster.Metadata GetQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey GetTimeSource() clock.TimeSource } ) // NewMetricsEmitter creates a new metrics emitter, which starts a goroutine to emit replication metrics occasionally. func NewMetricsEmitter( shardID int, shardData metricsEmitterShardData, reader taskReader, metricsClient metrics.Client, ) *MetricsEmitterImpl { currentCluster := shardData.GetClusterMetadata().GetCurrentClusterName() remoteClusters := shardData.GetClusterMetadata().GetRemoteClusterInfo() scope := metricsClient.Scope( metrics.ReplicationMetricEmitterScope, metrics.ActiveClusterTag(currentCluster), metrics.InstanceTag(strconv.Itoa(shardID)), ) logger := shardData.GetLogger().WithTags( tag.ClusterName(currentCluster), tag.ShardID(shardID)) ctx, cancel := context.WithCancel(context.Background()) return &MetricsEmitterImpl{ shardID: shardID, currentCluster: currentCluster, remoteClusters: remoteClusters, status: common.DaemonStatusInitialized, shardData: shardData, reader: reader, scope: scope, interval: metricsEmissionInterval, logger: logger, ctx: ctx, cancelCtx: cancel, } } func (m *MetricsEmitterImpl) Start() { if !atomic.CompareAndSwapInt32(&m.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } m.wg.Add(1) go m.emitMetricsLoop() m.logger.Info("ReplicationMetricsEmitter started.") } func (m *MetricsEmitterImpl) Stop() { if !atomic.CompareAndSwapInt32(&m.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } m.logger.Info("ReplicationMetricsEmitter shutting down.") m.cancelCtx() if !common.AwaitWaitGroup(&m.wg, 5*time.Second) { m.logger.Warn("ReplicationMetricsEmitter timed out on shutdown.") } } func (m *MetricsEmitterImpl) emitMetricsLoop() { defer m.wg.Done() ticker := time.NewTicker(m.interval) defer ticker.Stop() defer func() { log.CapturePanic(recover(), m.logger, nil) }() for { select { case <-m.ctx.Done(): return case <-ticker.C: m.emitMetrics() } } } func (m *MetricsEmitterImpl) emitMetrics() { for remoteClusterName := range m.remoteClusters { logger := m.logger.WithTags(tag.RemoteCluster(remoteClusterName)) scope := m.scope.Tagged(metrics.TargetClusterTag(remoteClusterName)) replicationLatency, err := m.determineReplicationLatency(remoteClusterName) if err != nil { return } scope.UpdateGauge(metrics.ReplicationLatency, float64(replicationLatency.Nanoseconds())) logger.Debug(fmt.Sprintf("ReplicationLatency metric emitted: %v", float64(replicationLatency.Nanoseconds()))) } } func (m *MetricsEmitterImpl) determineReplicationLatency(remoteClusterName string) (time.Duration, error) { logger := m.logger.WithTags(tag.RemoteCluster(remoteClusterName)) lastReadTaskID := m.shardData.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryReplication, remoteClusterName).GetTaskID() tasks, _, err := m.reader.Read(m.ctx, lastReadTaskID, lastReadTaskID+1, 1) if err != nil { logger.Error(fmt.Sprintf( "Error reading when determining replication latency, lastReadTaskID=%v", lastReadTaskID), tag.Error(err)) return 0, err } logger.Debug("Number of tasks retrieved", tag.Number(int64(len(tasks)))) var replicationLatency time.Duration if len(tasks) > 0 { creationTime := tasks[0].GetVisibilityTimestamp() replicationLatency = m.shardData.GetTimeSource().Now().Sub(creationTime) } return replicationLatency, nil } ================================================ FILE: service/history/replication/metrics_emitter_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) var ( cluster1 = "cluster1" cluster2 = "cluster2" cluster3 = "cluster3" ) func TestMetricsEmitterStartStop(t *testing.T) { goleak.VerifyNone(t) timeSource := clock.NewMockedTimeSource() metadata := newClusterMetadata(t) testShardData := newTestShardData(timeSource, metadata) metricsEmitter := NewMetricsEmitter(1, testShardData, fakeTaskReader{}, metrics.NewNoopMetricsClient()) metricsEmitter.interval = 5 * time.Millisecond metricsEmitter.Start() time.Sleep(20 * time.Millisecond) // let the metrics emitter run a few times metricsEmitter.Stop() } func TestMetricsEmitter(t *testing.T) { timeSource := clock.NewMockedTimeSource() metadata := newClusterMetadata(t) testShardData := newTestShardData(timeSource, metadata) task1 := &persistence.HistoryReplicationTask{ TaskData: persistence.TaskData{ TaskID: 1, VisibilityTimestamp: timeSource.Now().Add(-time.Hour), }, } task2 := &persistence.HistoryReplicationTask{ TaskData: persistence.TaskData{ TaskID: 2, VisibilityTimestamp: timeSource.Now().Add(-time.Minute), }, } reader := fakeTaskReader{task1, task2} metricsEmitter := NewMetricsEmitter(1, testShardData, reader, metrics.NewNoopMetricsClient()) latency, err := metricsEmitter.determineReplicationLatency(cluster2) assert.NoError(t, err) assert.Equal(t, time.Hour, latency) // Move replication level up for cluster2 and our latency shortens testShardData.clusterReplicationLevel[cluster2] = persistence.NewImmediateTaskKey(2) latency, err = metricsEmitter.determineReplicationLatency(cluster2) assert.NoError(t, err) assert.Equal(t, time.Minute, latency) // Move replication level up for cluster2 and we no longer have latency testShardData.clusterReplicationLevel[cluster2] = persistence.NewImmediateTaskKey(3) latency, err = metricsEmitter.determineReplicationLatency(cluster2) assert.NoError(t, err) assert.Equal(t, time.Duration(0), latency) // Cluster3 will still have latency latency, err = metricsEmitter.determineReplicationLatency(cluster3) assert.NoError(t, err) assert.Equal(t, time.Hour, latency) } type testShardData struct { logger log.Logger clusterReplicationLevel map[string]persistence.HistoryTaskKey timeSource clock.TimeSource metadata cluster.Metadata } func newTestShardData(timeSource clock.TimeSource, metadata cluster.Metadata) testShardData { remotes := metadata.GetRemoteClusterInfo() clusterReplicationLevels := make(map[string]persistence.HistoryTaskKey, len(remotes)) for remote := range remotes { clusterReplicationLevels[remote] = persistence.NewImmediateTaskKey(1) } return testShardData{ logger: log.NewNoop(), timeSource: timeSource, metadata: metadata, clusterReplicationLevel: clusterReplicationLevels, } } func (t testShardData) GetLogger() log.Logger { return t.logger } func (t testShardData) GetQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey { return t.clusterReplicationLevel[cluster] } func (t testShardData) GetTimeSource() clock.TimeSource { return t.timeSource } func (t testShardData) GetClusterMetadata() cluster.Metadata { return t.metadata } func newClusterMetadata(t *testing.T) cluster.Metadata { return cluster.NewMetadata( config.ClusterGroupMetadata{ PrimaryClusterName: cluster1, CurrentClusterName: cluster1, ClusterGroup: map[string]config.ClusterInformation{ cluster1: {Enabled: true}, cluster2: {Enabled: true}, cluster3: {Enabled: true}, }, }, func(d string) bool { return false }, metrics.NewNoopMetricsClient(), testlogger.New(t), ) } ================================================ FILE: service/history/replication/task_ack_manager.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2022 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "context" "errors" "fmt" "strconv" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" ) type ( // TaskAckManager is the ack manager for replication tasks TaskAckManager struct { ackLevels ackLevelStore scope metrics.Scope logger log.Logger reader taskReader store *TaskStore // replicationMessagesSizeFn is the function to calculate the size of types.ReplicationMessages replicationMessagesSizeFn types.ReplicationMessagesSizeFn // maxReplicationMessagesSize is the max size of types.ReplicationMessages // that can be sent in a single RPC call maxReplicationMessagesSize int dynamicTaskBatchSizer DynamicTaskBatchSizer timeSource clock.TimeSource } ackLevelStore interface { UpdateIfNeededAndGetQueueMaxReadLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey GetQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey UpdateQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string, ackLevel persistence.HistoryTaskKey) error } taskReader interface { Read(ctx context.Context, readLevel int64, maxReadLevel int64, batchSize int) ([]persistence.Task, bool, error) } ) // NewTaskAckManager initializes a new replication task ack manager func NewTaskAckManager( shardID int, ackLevels ackLevelStore, metricsClient metrics.Client, logger log.Logger, reader taskReader, store *TaskStore, timeSource clock.TimeSource, config *config.Config, replicationMessagesSizeFn types.ReplicationMessagesSizeFn, dynamicTaskBatchSizer DynamicTaskBatchSizer, ) TaskAckManager { return TaskAckManager{ ackLevels: ackLevels, scope: metricsClient.Scope( metrics.ReplicatorQueueProcessorScope, metrics.InstanceTag(strconv.Itoa(shardID)), ), logger: logger.WithTags(tag.ComponentReplicationAckManager), reader: reader, store: store, timeSource: timeSource, maxReplicationMessagesSize: config.MaxResponseSize, replicationMessagesSizeFn: replicationMessagesSizeFn, dynamicTaskBatchSizer: dynamicTaskBatchSizer, } } func (t *TaskAckManager) GetTasks(ctx context.Context, pollingCluster string, lastReadTaskID int64) (_ *types.ReplicationMessages, err error) { result, err := t.getTasks(ctx, pollingCluster, lastReadTaskID) t.dynamicTaskBatchSizer.analyse(err, result) if err != nil { return nil, err } return result.msgs, nil } // getTasksResult contains the result of a TaskAckManager.getTasks // It is used to adjust the task batch size by DynamicTaskBatchSizer type getTasksResult struct { previousReadTaskID int64 lastReadTaskID int64 msgs *types.ReplicationMessages taskInfos []persistence.Task isShrunk bool } func (t *TaskAckManager) getTasks(ctx context.Context, pollingCluster string, lastReadTaskID int64) (*getTasksResult, error) { var oldestUnprocessedTaskID int64 var oldestUnprocessedTaskTimestamp int64 previousReadTaskID := t.ackLevels.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryReplication, pollingCluster).GetTaskID() if lastReadTaskID == constants.EmptyMessageID { lastReadTaskID = previousReadTaskID } taskGeneratedStart := t.timeSource.Now() taskGeneratedTimer := t.scope.StartTimer(metrics.TaskLatency) defer taskGeneratedTimer.Stop() defer func() { t.scope.ExponentialHistogram(metrics.ExponentialTaskLatency, t.timeSource.Since(taskGeneratedStart)) }() batchSize := t.dynamicTaskBatchSizer.value() t.scope.UpdateGauge(metrics.ReplicationTasksBatchSize, float64(batchSize)) taskInfos, hasMore, err := t.reader.Read(ctx, lastReadTaskID, t.ackLevels.UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryReplication, pollingCluster).GetTaskID(), batchSize) if err != nil { return nil, err } tasksFetched := len(taskInfos) t.scope.RecordTimer(metrics.ReplicationTasksFetched, time.Duration(tasksFetched)) t.scope.RecordHistogramValue(metrics.ReplicationTasksFetchedHistogram, float64(tasksFetched)) // Happy path assumption - we will push all tasks to replication tasks. msgs := &types.ReplicationMessages{ ReplicationTasks: make([]*types.ReplicationTask, 0, len(taskInfos)), LastRetrievedMessageID: lastReadTaskID, HasMore: hasMore, } if len(taskInfos) > 0 { // it does not matter if we can process task or not, but we need to know what was the oldest task information we have read. // tasks must be ordered by taskID/time. oldestUnprocessedTaskID = taskInfos[0].GetTaskID() oldestUnprocessedTaskTimestamp = taskInfos[0].GetVisibilityTimestamp().UnixNano() } else { // if there are no replication tasks, use the max read level as the oldest unprocessed task // this means there are no tasks to process, so the lag is 0 oldestUnprocessedTaskID = t.ackLevels.UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryReplication, pollingCluster).GetTaskID() oldestUnprocessedTaskTimestamp = t.timeSource.Now().UnixNano() } lagRaw := int(t.ackLevels.UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryReplication, pollingCluster).GetTaskID() - oldestUnprocessedTaskID) t.scope.RecordTimer(metrics.ReplicationTasksLagRaw, time.Duration(lagRaw)) t.scope.RecordHistogramValue(metrics.ReplicationTasksLagRawHistogram, float64(lagRaw)) t.scope.RecordHistogramDuration(metrics.ReplicationTasksDelay, time.Duration(oldestUnprocessedTaskTimestamp-t.timeSource.Now().UnixNano())) // hydrate the tasks for _, info := range taskInfos { task, err := t.store.Get(ctx, pollingCluster, info) if err != nil { if errors.As(err, new(*types.BadRequestError)) || errors.As(err, new(*types.InternalDataInconsistencyError)) || errors.As(err, new(*types.EntityNotExistsError)) { t.logger.Warn("Failed to get replication task.", tag.Error(err)) } else { t.logger.Error("Failed to get replication task. Return what we have so far.", tag.Error(err)) msgs.HasMore = true break } } msgs.LastRetrievedMessageID = info.GetTaskID() if task != nil { msgs.ReplicationTasks = append(msgs.ReplicationTasks, task) } } // Sometimes the total size of replication tasks can be larger than the max response size // It caused the replication lag until history.replicatorTaskBatchSize is not adjusted to a smaller value // To prevent the lag and manual actions, we stop adding more tasks to the batch if the total size exceeds the limit isShrunk, err := t.shrinkMessagesBySize(msgs) if err != nil { return nil, err } replicationLag := int(t.ackLevels.UpdateIfNeededAndGetQueueMaxReadLevel(persistence.HistoryTaskCategoryReplication, pollingCluster).GetTaskID() - msgs.LastRetrievedMessageID) t.scope.RecordTimer(metrics.ReplicationTasksLag, time.Duration(replicationLag)) t.scope.RecordHistogramValue(metrics.ReplicationTasksLagHistogram, float64(replicationLag)) tasksReturned := len(msgs.ReplicationTasks) t.scope.RecordTimer(metrics.ReplicationTasksReturned, time.Duration(tasksReturned)) t.scope.RecordHistogramValue(metrics.ReplicationTasksReturnedHistogram, float64(tasksReturned)) tasksReturnedDiff := len(taskInfos) - len(msgs.ReplicationTasks) t.scope.RecordTimer(metrics.ReplicationTasksReturnedDiff, time.Duration(tasksReturnedDiff)) t.scope.RecordHistogramValue(metrics.ReplicationTasksReturnedDiffHistogram, float64(tasksReturnedDiff)) t.ackLevel(pollingCluster, lastReadTaskID) t.logger.Debug( "Get replication tasks", tag.SourceCluster(pollingCluster), tag.ShardReplicationAck(msgs.LastRetrievedMessageID), tag.ReadLevel(msgs.LastRetrievedMessageID), ) return &getTasksResult{ previousReadTaskID: previousReadTaskID, lastReadTaskID: lastReadTaskID, msgs: msgs, taskInfos: taskInfos, isShrunk: isShrunk, }, nil } // ackLevel updates the ack level for the given cluster func (t *TaskAckManager) ackLevel(pollingCluster string, lastReadTaskID int64) { if err := t.ackLevels.UpdateQueueClusterAckLevel(persistence.HistoryTaskCategoryReplication, pollingCluster, persistence.NewImmediateTaskKey(lastReadTaskID)); err != nil { t.logger.Error("error updating replication level for shard", tag.Error(err), tag.OperationFailed) } if err := t.store.Ack(pollingCluster, lastReadTaskID); err != nil { t.logger.Error("error updating replication level for hydrated task store", tag.Error(err), tag.OperationFailed) } } // shrinkMessagesBySize shrinks the replication messages by removing the last replication task until the total size is allowed func (t *TaskAckManager) shrinkMessagesBySize(msgs *types.ReplicationMessages) (bool, error) { // if there are no replication tasks, do nothing if len(msgs.ReplicationTasks) == 0 { return false, nil } maxSize := t.maxReplicationMessagesSize isShrunk := false for { totalSize := t.replicationMessagesSizeFn(msgs) // if the total size is allowed, return the replication messages if totalSize < maxSize { return isShrunk, nil } lastTask := msgs.ReplicationTasks[len(msgs.ReplicationTasks)-1] t.logger.Warn("Replication messages size is too large. Shrinking the messages by removing the last replication task", tag.ReplicationMessagesTotalSize(totalSize), tag.ReplicationMessagesMaxSize(maxSize), tag.ReplicationTaskID(lastTask.SourceTaskID), tag.ReplicationTaskCreationTime(lastTask.CreationTime), ) // change HasMore to true to indicate that there are more tasks to be fetched msgs.HasMore = true // remove the last replication task msgs.ReplicationTasks = msgs.ReplicationTasks[:len(msgs.ReplicationTasks)-1] // set isShrunk to true to indicate that the replication messages have been shrunk isShrunk = true // should never happen, but just in case // if there are no more replication tasks, return an error if len(msgs.ReplicationTasks) == 0 { return isShrunk, fmt.Errorf("replication messages size is too large and cannot be shrunk anymore, shard will be stuck until the message size is reduced or max size is increased") } // update the last retrieved message ID to the new last task ID msgs.LastRetrievedMessageID = msgs.ReplicationTasks[len(msgs.ReplicationTasks)-1].SourceTaskID } } ================================================ FILE: service/history/replication/task_ack_manager_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2022 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" "github.com/uber/cadence/service/history/config" ) var ( testTask11 = persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, }, TaskData: persistence.TaskData{ TaskID: 11, }, } testTask12 = persistence.SyncActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, }, TaskData: persistence.TaskData{ TaskID: 12, }, } testTask13 = persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, }, TaskData: persistence.TaskData{ TaskID: 13, }, } testTask14 = persistence.FailoverMarkerTask{ DomainID: testDomainID, TaskData: persistence.TaskData{ TaskID: 14, }, } testHydratedTask11 = types.ReplicationTask{SourceTaskID: 11, HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{DomainID: testDomainID}} testHydratedTask12 = types.ReplicationTask{SourceTaskID: 12, SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{DomainID: testDomainID}} testHydratedTask13 = types.ReplicationTask{SourceTaskID: 13, HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{DomainID: testDomainID}} testHydratedTask14 = types.ReplicationTask{SourceTaskID: 14, FailoverMarkerAttributes: &types.FailoverMarkerAttributes{DomainID: testDomainID}} testHydratedTaskErrorRecoverable = types.ReplicationTask{SourceTaskID: -100} testHydratedTaskErrorNonRecoverable = types.ReplicationTask{SourceTaskID: -200} testClusterA = "cluster-A" testClusterB = "cluster-B" testClusterC = "cluster-C" testDomainName = "test-domain-name" testDomain = cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: testDomainID, Name: testDomainName}, &persistence.DomainConfig{}, true, &persistence.DomainReplicationConfig{Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: testClusterA}, }}, 0, nil, 0, 0, 0, ) testConfig = &config.Config{ MaxResponseSize: testMaxResponseSize, } ) const ( testMaxResponseSize = 4 * 1024 * 1024 // 4MB ) func TestTaskAckManager_GetTasks(t *testing.T) { tests := []struct { name string ackLevels *fakeAckLevelStore domains domainCache reader taskReader hydrator taskHydrator pollingCluster string lastReadLevel int64 batchSize fakeDynamicTaskBatchSizer expectResult *types.ReplicationMessages expectErr string expectAckLevel int64 config *config.Config }{ { name: "main flow - no replication tasks", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(2)}, }, domains: fakeDomainCache{testDomainID: testDomain}, reader: fakeTaskReader{}, hydrator: fakeTaskHydrator{}, pollingCluster: testClusterA, lastReadLevel: 5, batchSize: 10, expectResult: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{}, LastRetrievedMessageID: 5, HasMore: false, }, expectAckLevel: 5, config: testConfig, }, { name: "main flow - continues on recoverable error", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(2)}, }, domains: fakeDomainCache{testDomainID: testDomain}, reader: fakeTaskReader{&testTask11, &testTask12, &testTask13, &testTask14}, hydrator: fakeTaskHydrator{ testTask11.TaskID: testHydratedTask11, testTask12.TaskID: testHydratedTask12, testTask13.TaskID: testHydratedTaskErrorRecoverable, // Will continue hydrating beyond this point testTask14.TaskID: testHydratedTask14, }, pollingCluster: testClusterA, lastReadLevel: 5, batchSize: 10, expectResult: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{&testHydratedTask11, &testHydratedTask12, &testHydratedTask14}, LastRetrievedMessageID: 14, HasMore: false, }, expectAckLevel: 5, config: testConfig, }, { name: "main flow - stops at non recoverable error", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(2)}, }, domains: fakeDomainCache{testDomainID: testDomain}, reader: fakeTaskReader{&testTask11, &testTask12, &testTask13, &testTask14}, hydrator: fakeTaskHydrator{ testTask11.TaskID: testHydratedTask11, testTask12.TaskID: testHydratedTask12, testTask13.TaskID: testHydratedTaskErrorNonRecoverable, // Will stop hydrating beyond this point testTask14.TaskID: testHydratedTask14, }, pollingCluster: testClusterA, lastReadLevel: 5, batchSize: 10, expectResult: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{&testHydratedTask11, &testHydratedTask12}, LastRetrievedMessageID: 12, HasMore: true, }, expectAckLevel: 5, config: testConfig, }, { name: "main flow - stops at second task, batch size is 2", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(2)}, }, domains: fakeDomainCache{testDomainID: testDomain}, reader: fakeTaskReader{&testTask11, &testTask12, &testTask13, &testTask14}, hydrator: fakeTaskHydrator{ testTask11.TaskID: testHydratedTask11, testTask12.TaskID: testHydratedTask12, // Will stop hydrating beyond this point testTask13.TaskID: testHydratedTask13, testTask14.TaskID: testHydratedTask14, }, pollingCluster: testClusterA, lastReadLevel: 5, batchSize: 2, expectResult: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{&testHydratedTask11, &testHydratedTask12}, LastRetrievedMessageID: 12, HasMore: true, }, expectAckLevel: 5, config: testConfig, }, { name: "main flow - stops at a message exceeded max response size", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(2)}, }, domains: fakeDomainCache{testDomainID: testDomain}, reader: fakeTaskReader{&testTask11, &testTask12, &testTask13, &testTask14}, hydrator: fakeTaskHydrator{ testTask11.TaskID: testHydratedTask11, testTask12.TaskID: testHydratedTask12, testTask13.TaskID: testHydratedTask13, testTask14.TaskID: testHydratedTask14, // Will stop adding tasks beyond this point }, pollingCluster: testClusterA, lastReadLevel: 5, expectResult: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{&testHydratedTask11, &testHydratedTask12, &testHydratedTask13}, LastRetrievedMessageID: 13, HasMore: true, }, expectAckLevel: 5, batchSize: 10, config: &config.Config{ MaxResponseSize: proto.FromReplicationMessages(&types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{&testHydratedTask11, &testHydratedTask12, &testHydratedTask13}, LastRetrievedMessageID: 13, HasMore: true, }).Size() + 1, }, }, { name: "main flow - fail at a message exceeded max response size", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(2)}, }, domains: fakeDomainCache{testDomainID: testDomain}, reader: fakeTaskReader{&testTask11, &testTask12, &testTask13, &testTask14}, hydrator: fakeTaskHydrator{ testTask11.TaskID: testHydratedTask11, testTask12.TaskID: testHydratedTask12, testTask13.TaskID: testHydratedTask13, testTask14.TaskID: testHydratedTask14, // Will stop adding tasks beyond this point }, pollingCluster: testClusterA, lastReadLevel: 5, batchSize: 10, expectResult: nil, expectErr: "replication messages size is too large and cannot be shrunk anymore, shard will be stuck until the message size is reduced or max size is increased", expectAckLevel: 2, config: &config.Config{ MaxResponseSize: 0, }, }, { name: "skips tasks for domains non belonging to polling cluster", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(2)}, }, domains: fakeDomainCache{testDomainID: testDomain}, reader: fakeTaskReader{&testTask11}, hydrator: fakeTaskHydrator{testTask11.TaskID: testHydratedTask11}, pollingCluster: testClusterB, lastReadLevel: 5, batchSize: 10, expectResult: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{}, LastRetrievedMessageID: 11, HasMore: false, }, expectAckLevel: 5, config: testConfig, }, { name: "uses remote ack level for first fetch (empty task ID)", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(12)}, }, domains: fakeDomainCache{testDomainID: testDomain}, reader: fakeTaskReader{&testTask11, &testTask12}, hydrator: fakeTaskHydrator{testTask12.TaskID: testHydratedTask12}, pollingCluster: testClusterA, lastReadLevel: -1, batchSize: 10, expectResult: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{&testHydratedTask12}, LastRetrievedMessageID: 12, HasMore: false, }, expectAckLevel: 12, config: testConfig, }, { name: "failed to read replication tasks - return error", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(2)}, }, reader: (fakeTaskReader)(nil), pollingCluster: testClusterA, lastReadLevel: 5, batchSize: 10, expectErr: "error reading replication tasks", config: testConfig, }, { name: "failed to get domain - stops", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(2)}, }, domains: fakeDomainCache{}, reader: fakeTaskReader{&testTask11}, hydrator: fakeTaskHydrator{}, pollingCluster: testClusterA, lastReadLevel: 5, batchSize: 10, expectResult: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{}, LastRetrievedMessageID: 5, HasMore: true, }, config: testConfig, }, { name: "failed to update ack level - no error, return response anyway", ackLevels: &fakeAckLevelStore{ readLevel: 200, remote: map[string]persistence.HistoryTaskKey{testClusterA: persistence.NewImmediateTaskKey(2)}, updateErr: errors.New("error update ack level"), }, domains: fakeDomainCache{testDomainID: testDomain}, reader: fakeTaskReader{&testTask11}, hydrator: fakeTaskHydrator{testTask11.TaskID: testHydratedTask11}, pollingCluster: testClusterA, lastReadLevel: 5, batchSize: 10, expectResult: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{&testHydratedTask11}, LastRetrievedMessageID: 11, HasMore: false, }, config: testConfig, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { taskStore := createTestTaskStore(t, tt.domains, tt.hydrator) ackManager := NewTaskAckManager( testShardID, tt.ackLevels, metrics.NewNoopMetricsClient(), log.NewNoop(), tt.reader, taskStore, clock.NewMockedTimeSource(), tt.config, proto.ReplicationMessagesSize, tt.batchSize, ) result, err := ackManager.GetTasks(context.Background(), tt.pollingCluster, tt.lastReadLevel) if tt.expectErr != "" { assert.EqualError(t, err, tt.expectErr) } else { require.NoError(t, err) assert.Equal(t, tt.expectResult, result) } if tt.expectAckLevel != 0 { assert.Equal(t, tt.expectAckLevel, tt.ackLevels.remote[tt.pollingCluster].GetTaskID()) } }) } } type fakeAckLevelStore struct { remote map[string]persistence.HistoryTaskKey readLevel int64 updateErr error } func (s *fakeAckLevelStore) UpdateIfNeededAndGetQueueMaxReadLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey { return persistence.NewImmediateTaskKey(s.readLevel) } func (s *fakeAckLevelStore) GetQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey { return s.remote[cluster] } func (s *fakeAckLevelStore) UpdateQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string, lastTaskID persistence.HistoryTaskKey) error { s.remote[cluster] = lastTaskID return s.updateErr } type fakeTaskReader []persistence.Task func (r fakeTaskReader) Read(ctx context.Context, readLevel int64, maxReadLevel int64, batchSize int) ([]persistence.Task, bool, error) { if r == nil { return nil, false, errors.New("error reading replication tasks") } hasMore := false var result []persistence.Task for _, task := range r { if task.GetTaskID() < readLevel { continue } if task.GetTaskID() >= maxReadLevel { hasMore = true break } result = append(result, task) } if len(result) > batchSize { return result[:batchSize], true, nil } return result, hasMore, nil } type fakeTaskHydrator map[int64]types.ReplicationTask func (h fakeTaskHydrator) Hydrate(ctx context.Context, task persistence.Task) (*types.ReplicationTask, error) { if hydratedTask, ok := h[task.GetTaskID()]; ok { if hydratedTask == testHydratedTaskErrorNonRecoverable { return nil, errors.New("error hydrating task") } if hydratedTask == testHydratedTaskErrorRecoverable { return nil, &types.EntityNotExistsError{} } return &hydratedTask, nil } panic("fix the test, should not reach this") } type fakeDynamicTaskBatchSizer int func (s fakeDynamicTaskBatchSizer) analyse(_ error, _ *getTasksResult) {} func (s fakeDynamicTaskBatchSizer) value() int { return int(s) } ================================================ FILE: service/history/replication/task_executor.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package replication import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/shard" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination task_executor_mock.go -self_package github.com/uber/cadence/service/history/replication type ( // TaskExecutor is the executor for replication task TaskExecutor interface { execute(replicationTask *types.ReplicationTask, forceApply bool) (metrics.ScopeIdx, error) } taskExecutorImpl struct { currentCluster string sourceCluster string shard shard.Context domainCache cache.DomainCache historyResender ndc.HistoryResender historyEngine engine.Engine metricsClient metrics.Client logger log.Logger } ) var _ TaskExecutor = (*taskExecutorImpl)(nil) // NewTaskExecutor creates an replication task executor // The executor uses by 1) DLQ replication task handler 2) history replication task processor func NewTaskExecutor( sourceCluster string, shard shard.Context, domainCache cache.DomainCache, historyResender ndc.HistoryResender, historyEngine engine.Engine, metricsClient metrics.Client, logger log.Logger, ) TaskExecutor { return &taskExecutorImpl{ currentCluster: shard.GetClusterMetadata().GetCurrentClusterName(), sourceCluster: sourceCluster, shard: shard, domainCache: domainCache, historyResender: historyResender, historyEngine: historyEngine, metricsClient: metricsClient, logger: logger, } } func (e *taskExecutorImpl) execute( replicationTask *types.ReplicationTask, forceApply bool, ) (metrics.ScopeIdx, error) { var err error var scope metrics.ScopeIdx switch replicationTask.GetTaskType() { case types.ReplicationTaskTypeSyncActivity: scope = metrics.SyncActivityTaskScope err = e.handleActivityTask(replicationTask, forceApply) case types.ReplicationTaskTypeHistoryV2: scope = metrics.HistoryReplicationV2TaskScope err = e.handleHistoryReplicationTaskV2(replicationTask, forceApply) case types.ReplicationTaskTypeFailoverMarker: scope = metrics.FailoverMarkerScope err = e.handleFailoverReplicationTask(replicationTask) default: e.logger.Error("Unknown task type.") scope = metrics.ReplicatorScope err = ErrUnknownReplicationTask } return scope, err } func (e *taskExecutorImpl) handleActivityTask( task *types.ReplicationTask, forceApply bool, ) (err error) { defer func() { if r := recover(); r != nil { attr := task.SyncActivityTaskAttributes e.logger.Error( "handleActivityTask encountered panic.", tag.WorkflowDomainID(attr.GetDomainID()), tag.WorkflowID(attr.GetWorkflowID()), tag.WorkflowRunID(attr.GetRunID()), tag.Value(r), ) panic(r) } }() attr := task.SyncActivityTaskAttributes doContinue, err := e.filterTask(attr.GetDomainID(), forceApply) if err != nil || !doContinue { return err } replicationStopWatch := e.metricsClient.StartTimer(metrics.SyncActivityTaskScope, metrics.CadenceLatency) defer replicationStopWatch.Stop() request := &types.SyncActivityRequest{ DomainID: attr.DomainID, WorkflowID: attr.WorkflowID, RunID: attr.RunID, Version: attr.Version, ScheduledID: attr.ScheduledID, ScheduledTime: attr.ScheduledTime, StartedID: attr.StartedID, StartedTime: attr.StartedTime, LastHeartbeatTime: attr.LastHeartbeatTime, Details: attr.Details, Attempt: attr.Attempt, LastFailureReason: attr.LastFailureReason, LastFailureDetails: attr.LastFailureDetails, LastWorkerIdentity: attr.LastWorkerIdentity, VersionHistory: attr.GetVersionHistory(), } ctx, cancel := context.WithTimeout(context.Background(), replicationTimeout) defer cancel() var syncActivityAction func() error // Check if the number of shards between clusters are equal. If not, redirect the request. if e.shard.GetShardID() != common.WorkflowIDToHistoryShard(attr.WorkflowID, e.shard.GetConfig().NumberOfShards) { syncActivityAction = func() error { return e.shard.GetService().GetClientBean().GetHistoryClient().SyncActivity(ctx, request) } } else { syncActivityAction = func() error { return e.historyEngine.SyncActivity(ctx, request) } } err = syncActivityAction() retryErr, ok := toRetryTaskV2Error(err) if !ok { return err } // Handle resend error e.metricsClient.IncCounter(metrics.HistoryRereplicationByActivityReplicationScope, metrics.CadenceClientRequests) stopwatch := e.metricsClient.StartTimer(metrics.HistoryRereplicationByActivityReplicationScope, metrics.CadenceClientLatency) defer stopwatch.Stop() resendErr := e.historyResender.SendSingleWorkflowHistory( e.sourceCluster, retryErr.GetDomainID(), retryErr.GetWorkflowID(), retryErr.GetRunID(), retryErr.StartEventID, retryErr.StartEventVersion, retryErr.EndEventID, retryErr.EndEventVersion, ) switch { case resendErr == nil: break case resendErr == ndc.ErrSkipTask: e.logger.Error( "skip replication sync activity task", tag.WorkflowDomainID(retryErr.GetDomainID()), tag.WorkflowID(retryErr.GetWorkflowID()), tag.WorkflowRunID(retryErr.GetRunID()), ) return nil default: e.logger.Error( "error resend history for sync activity", tag.WorkflowDomainID(retryErr.GetDomainID()), tag.WorkflowID(retryErr.GetWorkflowID()), tag.WorkflowRunID(retryErr.GetRunID()), tag.Error(resendErr), ) // should return the replication error, not the resending error return err } // should try again after back fill the history return syncActivityAction() } func (e *taskExecutorImpl) handleHistoryReplicationTaskV2( task *types.ReplicationTask, forceApply bool, ) (err error) { defer func() { if r := recover(); r != nil { attr := task.HistoryTaskV2Attributes e.logger.Error( "handleHistoryReplicationTaskV2 encountered panic.", tag.WorkflowDomainID(attr.GetDomainID()), tag.WorkflowID(attr.GetWorkflowID()), tag.WorkflowRunID(attr.GetRunID()), tag.Value(r), ) panic(r) } }() attr := task.HistoryTaskV2Attributes doContinue, err := e.filterTask(attr.GetDomainID(), forceApply) if err != nil || !doContinue { return err } replicationStopWatch := e.metricsClient.StartTimer(metrics.HistoryReplicationV2TaskScope, metrics.CadenceLatency) defer replicationStopWatch.Stop() request := &types.ReplicateEventsV2Request{ DomainUUID: attr.DomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: attr.WorkflowID, RunID: attr.RunID, }, VersionHistoryItems: attr.VersionHistoryItems, Events: attr.Events, // new run events does not need version history since there is no prior events NewRunEvents: attr.NewRunEvents, } ctx, cancel := context.WithTimeout(context.Background(), replicationTimeout) defer cancel() var historyReplicationAction func() error // Check if the number of shards between clusters are equal. If not, redirect the request. if e.shard.GetShardID() != common.WorkflowIDToHistoryShard(attr.WorkflowID, e.shard.GetConfig().NumberOfShards) { historyReplicationAction = func() error { return e.shard.GetService().GetClientBean().GetHistoryClient().ReplicateEventsV2(ctx, request) } } else { historyReplicationAction = func() error { return e.historyEngine.ReplicateEventsV2(ctx, request) } } err = historyReplicationAction() retryErr, ok := toRetryTaskV2Error(err) if !ok { return err } e.metricsClient.IncCounter(metrics.HistoryRereplicationByHistoryReplicationScope, metrics.CadenceClientRequests) resendStopWatch := e.metricsClient.StartTimer(metrics.HistoryRereplicationByHistoryReplicationScope, metrics.CadenceClientLatency) defer resendStopWatch.Stop() resendErr := e.historyResender.SendSingleWorkflowHistory( e.sourceCluster, retryErr.GetDomainID(), retryErr.GetWorkflowID(), retryErr.GetRunID(), retryErr.StartEventID, retryErr.StartEventVersion, retryErr.EndEventID, retryErr.EndEventVersion, ) switch { case resendErr == nil: break case resendErr == ndc.ErrSkipTask: e.logger.Error( "skip replication history task", tag.WorkflowDomainID(retryErr.GetDomainID()), tag.WorkflowID(retryErr.GetWorkflowID()), tag.WorkflowRunID(retryErr.GetRunID()), ) return nil default: e.logger.Error( "error resend history for history event v2", tag.WorkflowDomainID(retryErr.GetDomainID()), tag.WorkflowID(retryErr.GetWorkflowID()), tag.WorkflowRunID(retryErr.GetRunID()), tag.Error(resendErr), ) // should return the replication error, not the resending error return err } return historyReplicationAction() } func (e *taskExecutorImpl) handleFailoverReplicationTask( task *types.ReplicationTask, ) error { failoverAttributes := task.GetFailoverMarkerAttributes() failoverAttributes.CreationTime = task.CreationTime return e.shard.AddingPendingFailoverMarker(failoverAttributes) } func (e *taskExecutorImpl) filterTask( domainID string, forceApply bool, ) (bool, error) { if forceApply { return true, nil } domainEntry, err := e.domainCache.GetDomainByID(domainID) if err != nil { return false, err } shouldProcessTask := false for _, targetCluster := range domainEntry.GetReplicationConfig().Clusters { if e.currentCluster == targetCluster.ClusterName { shouldProcessTask = true break } } return shouldProcessTask, nil } func toRetryTaskV2Error(err error) (*types.RetryTaskV2Error, bool) { retError, ok := err.(*types.RetryTaskV2Error) return retError, ok } ================================================ FILE: service/history/replication/task_executor_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: task_executor.go // // Generated by this command: // // mockgen -package replication -source task_executor.go -destination task_executor_mock.go -self_package github.com/uber/cadence/service/history/replication // // Package replication is a generated GoMock package. package replication import ( reflect "reflect" gomock "go.uber.org/mock/gomock" metrics "github.com/uber/cadence/common/metrics" types "github.com/uber/cadence/common/types" ) // MockTaskExecutor is a mock of TaskExecutor interface. type MockTaskExecutor struct { ctrl *gomock.Controller recorder *MockTaskExecutorMockRecorder isgomock struct{} } // MockTaskExecutorMockRecorder is the mock recorder for MockTaskExecutor. type MockTaskExecutorMockRecorder struct { mock *MockTaskExecutor } // NewMockTaskExecutor creates a new mock instance. func NewMockTaskExecutor(ctrl *gomock.Controller) *MockTaskExecutor { mock := &MockTaskExecutor{ctrl: ctrl} mock.recorder = &MockTaskExecutorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskExecutor) EXPECT() *MockTaskExecutorMockRecorder { return m.recorder } // execute mocks base method. func (m *MockTaskExecutor) execute(replicationTask *types.ReplicationTask, forceApply bool) (metrics.ScopeIdx, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "execute", replicationTask, forceApply) ret0, _ := ret[0].(metrics.ScopeIdx) ret1, _ := ret[1].(error) return ret0, ret1 } // execute indicates an expected call of execute. func (mr *MockTaskExecutorMockRecorder) execute(replicationTask, forceApply any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "execute", reflect.TypeOf((*MockTaskExecutor)(nil).execute), replicationTask, forceApply) } ================================================ FILE: service/history/replication/task_executor_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package replication import ( "fmt" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/client" "github.com/uber/cadence/client/admin" historyClient "github.com/uber/cadence/client/history" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/shard" ) type ( taskExecutorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller sourceCluster string mockShard *shard.TestContext mockEngine *engine.MockEngine historyClient *historyClient.MockClient config *config.Config mockDomainCache *cache.MockDomainCache mockClientBean *client.MockBean adminClient *admin.MockClient executionManager *mocks.ExecutionManager nDCHistoryResender *ndc.MockHistoryResender taskHandler *taskExecutorImpl } ) func TestTaskExecutorSuite(t *testing.T) { s := new(taskExecutorSuite) suite.Run(t, s) } func (s *taskExecutorSuite) SetupSuite() { } func (s *taskExecutorSuite) TearDownSuite() { } func (s *taskExecutorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.config = config.NewForTestByShardNumber(2) s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, ReplicationAckLevel: 0, ReplicationDLQAckLevel: map[string]int64{"test": -1}, }, s.config, ) s.sourceCluster = "source-cluster" s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockClientBean = s.mockShard.Resource.ClientBean s.adminClient = s.mockShard.Resource.RemoteAdminClient s.executionManager = s.mockShard.Resource.ExecutionMgr s.nDCHistoryResender = ndc.NewMockHistoryResender(s.controller) s.mockEngine = engine.NewMockEngine(s.controller) s.historyClient = s.mockShard.Resource.HistoryClient s.taskHandler = NewTaskExecutor( s.sourceCluster, s.mockShard, s.mockDomainCache, s.nDCHistoryResender, s.mockEngine, metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), s.mockShard.GetLogger(), ).(*taskExecutorImpl) } func (s *taskExecutorSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *taskExecutorSuite) TestConvertRetryTaskV2Error_OK() { err := &types.RetryTaskV2Error{} _, ok := toRetryTaskV2Error(err) s.True(ok) } func (s *taskExecutorSuite) TestConvertRetryTaskV2Error_NotOK() { err := &types.BadRequestError{} _, ok := toRetryTaskV2Error(err) s.False(ok) } func (s *taskExecutorSuite) TestFilterTask() { domainID := uuid.New() s.mockDomainCache.EXPECT(). GetDomainByID(domainID). Return(cache.NewGlobalDomainCacheEntryForTest( nil, nil, &persistence.DomainReplicationConfig{ Clusters: []*persistence.ClusterReplicationConfig{ { ClusterName: "active", }, }}, 0, ), nil) ok, err := s.taskHandler.filterTask(domainID, false) s.NoError(err) s.True(ok) } func (s *taskExecutorSuite) TestFilterTask_Error() { domainID := uuid.New() s.mockDomainCache.EXPECT(). GetDomainByID(domainID). Return(nil, fmt.Errorf("test")) ok, err := s.taskHandler.filterTask(domainID, false) s.Error(err) s.False(ok) } func (s *taskExecutorSuite) TestFilterTask_EnforceApply() { domainID := uuid.New() ok, err := s.taskHandler.filterTask(domainID, true) s.NoError(err) s.True(ok) } func (s *taskExecutorSuite) TestProcessTask_SyncActivityReplicationTask_SameShardID() { domainID := uuid.New() workflowID := "6d89f939-e6a4-4c26-a0ed-626ce27bcc9c" // belong to shard 0 runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, } request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, } s.mockEngine.EXPECT().SyncActivity(gomock.Any(), request).Return(nil).Times(1) _, err := s.taskHandler.execute(task, true) s.NoError(err) } func (s *taskExecutorSuite) TestProcessTask_SyncActivityReplicationTask_SameShardID_RetryTaskV2Error_Success() { domainID := uuid.New() workflowID := "6d89f939-e6a4-4c26-a0ed-626ce27bcc9c" // belong to shard 0 runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, } request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, } s.mockEngine.EXPECT().SyncActivity(gomock.Any(), request).Return(&types.RetryTaskV2Error{ DomainID: "test-domain-id", WorkflowID: "test-wf-id", RunID: "test-run-id", StartEventID: common.Ptr(int64(11)), StartEventVersion: common.Ptr(int64(100)), EndEventID: common.Ptr(int64(19)), EndEventVersion: common.Ptr(int64(102)), }).Times(1) s.nDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.sourceCluster, "test-domain-id", "test-wf-id", "test-run-id", common.Ptr(int64(11)), common.Ptr(int64(100)), common.Ptr(int64(19)), common.Ptr(int64(102))). Return(nil) s.mockEngine.EXPECT().SyncActivity(gomock.Any(), request).Return(nil).Times(1) _, err := s.taskHandler.execute(task, true) s.NoError(err) } func (s *taskExecutorSuite) TestProcessTask_SyncActivityReplicationTask_SameShardID_RetryTaskV2Error_SkipTask() { domainID := uuid.New() workflowID := "6d89f939-e6a4-4c26-a0ed-626ce27bcc9c" // belong to shard 0 runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, } request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, } s.mockEngine.EXPECT().SyncActivity(gomock.Any(), request).Return(&types.RetryTaskV2Error{ DomainID: "test-domain-id", WorkflowID: "test-wf-id", RunID: "test-run-id", StartEventID: common.Ptr(int64(11)), StartEventVersion: common.Ptr(int64(100)), EndEventID: common.Ptr(int64(19)), EndEventVersion: common.Ptr(int64(102)), }).Times(1) s.nDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.sourceCluster, "test-domain-id", "test-wf-id", "test-run-id", common.Ptr(int64(11)), common.Ptr(int64(100)), common.Ptr(int64(19)), common.Ptr(int64(102))). Return(ndc.ErrSkipTask) _, err := s.taskHandler.execute(task, true) s.NoError(err) } func (s *taskExecutorSuite) TestProcessTask_SyncActivityReplicationTask_SameShardID_RetryTaskV2Error_Error() { domainID := uuid.New() workflowID := "6d89f939-e6a4-4c26-a0ed-626ce27bcc9c" // belong to shard 0 runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, } request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, } retryErr := &types.RetryTaskV2Error{ DomainID: "test-domain-id", WorkflowID: "test-wf-id", RunID: "test-run-id", StartEventID: common.Ptr(int64(11)), StartEventVersion: common.Ptr(int64(100)), EndEventID: common.Ptr(int64(19)), EndEventVersion: common.Ptr(int64(102)), } s.mockEngine.EXPECT().SyncActivity(gomock.Any(), request).Return(retryErr).Times(1) s.nDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.sourceCluster, "test-domain-id", "test-wf-id", "test-run-id", common.Ptr(int64(11)), common.Ptr(int64(100)), common.Ptr(int64(19)), common.Ptr(int64(102))). Return(fmt.Errorf("some error")) _, err := s.taskHandler.execute(task, true) s.Error(err) s.ErrorIs(err, retryErr) } func (s *taskExecutorSuite) TestProcessTask_HistoryV2ReplicationTask_SameShardID() { domainID := uuid.New() workflowID := "6d89f939-e6a4-4c26-a0ed-626ce27bcc9c" // belong to shard 0 runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, } request := &types.ReplicateEventsV2Request{ DomainUUID: domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, } s.mockEngine.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(nil).Times(1) _, err := s.taskHandler.execute(task, true) s.NoError(err) } func (s *taskExecutorSuite) TestProcessTask_HistoryV2ReplicationTask_SameShardID_RetryTaskErr_Success() { domainID := uuid.New() workflowID := "6d89f939-e6a4-4c26-a0ed-626ce27bcc9c" // belong to shard 0 runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, } request := &types.ReplicateEventsV2Request{ DomainUUID: domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, } retryErr := &types.RetryTaskV2Error{ DomainID: "test-domain-id", WorkflowID: "test-wf-id", RunID: "test-run-id", StartEventID: common.Ptr(int64(11)), StartEventVersion: common.Ptr(int64(100)), EndEventID: common.Ptr(int64(19)), EndEventVersion: common.Ptr(int64(102)), } s.mockEngine.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(retryErr).Times(1) s.nDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.sourceCluster, "test-domain-id", "test-wf-id", "test-run-id", common.Ptr(int64(11)), common.Ptr(int64(100)), common.Ptr(int64(19)), common.Ptr(int64(102))). Return(nil) s.mockEngine.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(nil).Times(1) _, err := s.taskHandler.execute(task, true) s.NoError(err) } func (s *taskExecutorSuite) TestProcessTask_HistoryV2ReplicationTask_SameShardID_RetryTaskErr_SkipTask() { domainID := uuid.New() workflowID := "6d89f939-e6a4-4c26-a0ed-626ce27bcc9c" // belong to shard 0 runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, } request := &types.ReplicateEventsV2Request{ DomainUUID: domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, } retryErr := &types.RetryTaskV2Error{ DomainID: "test-domain-id", WorkflowID: "test-wf-id", RunID: "test-run-id", StartEventID: common.Ptr(int64(11)), StartEventVersion: common.Ptr(int64(100)), EndEventID: common.Ptr(int64(19)), EndEventVersion: common.Ptr(int64(102)), } s.mockEngine.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(retryErr).Times(1) s.nDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.sourceCluster, "test-domain-id", "test-wf-id", "test-run-id", common.Ptr(int64(11)), common.Ptr(int64(100)), common.Ptr(int64(19)), common.Ptr(int64(102))). Return(ndc.ErrSkipTask) _, err := s.taskHandler.execute(task, true) s.NoError(err) } func (s *taskExecutorSuite) TestProcessTask_HistoryV2ReplicationTask_SameShardID_RetryTaskErr_Error() { domainID := uuid.New() workflowID := "6d89f939-e6a4-4c26-a0ed-626ce27bcc9c" // belong to shard 0 runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, } request := &types.ReplicateEventsV2Request{ DomainUUID: domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, } retryErr := &types.RetryTaskV2Error{ DomainID: "test-domain-id", WorkflowID: "test-wf-id", RunID: "test-run-id", StartEventID: common.Ptr(int64(11)), StartEventVersion: common.Ptr(int64(100)), EndEventID: common.Ptr(int64(19)), EndEventVersion: common.Ptr(int64(102)), } s.mockEngine.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(retryErr).Times(1) s.nDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.sourceCluster, "test-domain-id", "test-wf-id", "test-run-id", common.Ptr(int64(11)), common.Ptr(int64(100)), common.Ptr(int64(19)), common.Ptr(int64(102))). Return(fmt.Errorf("some error")) _, err := s.taskHandler.execute(task, true) s.Error(err) s.ErrorIs(err, retryErr) } func (s *taskExecutorSuite) TestProcess_HistoryV2ReplicationTask_DifferentShardID() { domainID := uuid.New() workflowID := "abc" // belong to shard 1 runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, } request := &types.ReplicateEventsV2Request{ DomainUUID: domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, } s.mockEngine.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(nil).Times(0) s.historyClient.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(nil).Times(1) _, err := s.taskHandler.execute(task, true) s.NoError(err) } func (s *taskExecutorSuite) TestProcess_SyncActivityReplicationTask_DifferentShardID() { domainID := uuid.New() workflowID := "abc" // belong to shard 1 runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, }, } request := &types.SyncActivityRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, } s.mockEngine.EXPECT().SyncActivity(gomock.Any(), request).Return(nil).Times(0) s.historyClient.EXPECT().SyncActivity(gomock.Any(), request).Return(nil).Times(1) _, err := s.taskHandler.execute(task, true) s.NoError(err) } func (s *taskExecutorSuite) TestProcess_FailoverReplicationTask() { task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeFailoverMarker.Ptr(), FailoverMarkerAttributes: &types.FailoverMarkerAttributes{ DomainID: "test-domain-id", FailoverVersion: 101, CreationTime: common.Ptr(int64(111)), }, CreationTime: common.Ptr(int64(222)), } s.mockShard.MockAddingPendingFailoverMarker = func(marker *types.FailoverMarkerAttributes) error { s.Equal(&types.FailoverMarkerAttributes{ DomainID: "test-domain-id", FailoverVersion: 101, CreationTime: common.Ptr(int64(222)), }, marker) return nil } _, err := s.taskHandler.execute(task, true) s.NoError(err) } func (s *taskExecutorSuite) TestProcess_UnknownTask() { task := &types.ReplicationTask{ TaskType: common.Ptr(types.ReplicationTaskType(-100)), } _, err := s.taskHandler.execute(task, true) s.Error(err) s.ErrorIs(err, ErrUnknownReplicationTask) } ================================================ FILE: service/history/replication/task_fetcher.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package replication import ( "context" "sync" "sync/atomic" "time" "github.com/uber/cadence/client" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination task_fetcher_mock.go -self_package github.com/uber/cadence/service/history/replication // TODO: reuse the interface and implementation defined in history/task package const ( fetchTaskRequestTimeout = 60 * time.Second requestChanBufferSize = 1000 ) type ( // TaskFetcher is responsible for fetching replication messages from remote DC. TaskFetcher interface { common.Daemon GetSourceCluster() string GetRequestChan(shardID int) chan<- *request GetRateLimiter() quotas.Limiter } // TaskFetchers is a group of fetchers, one per source DC. TaskFetchers interface { common.Daemon GetFetchers() []TaskFetcher } // taskFetcherImpl is the implementation of fetching replication messages. taskFetcherImpl struct { status int32 currentCluster string sourceCluster string config *config.Config logger log.Logger metricsScope metrics.Scope remotePeer admin.Client rateLimiter quotas.Limiter timeSource clock.TimeSource requestChan []chan *request ctx context.Context cancelCtx context.CancelFunc wg sync.WaitGroup fetchAndDistributeTasksFn func(map[int32]*request) error } // taskFetchersImpl is a group of fetchers, one per source DC. taskFetchersImpl struct { status int32 logger log.Logger fetchers []TaskFetcher } ) var _ TaskFetcher = (*taskFetcherImpl)(nil) var _ TaskFetchers = (*taskFetchersImpl)(nil) // NewTaskFetchers creates an instance of ReplicationTaskFetchers with given configs. func NewTaskFetchers( logger log.Logger, config *config.Config, clusterMetadata cluster.Metadata, clientBean client.Bean, metricsClient metrics.Client, ) (TaskFetchers, error) { currentCluster := clusterMetadata.GetCurrentClusterName() var fetchers []TaskFetcher for clusterName := range clusterMetadata.GetRemoteClusterInfo() { remoteFrontendClient, err := clientBean.GetRemoteAdminClient(clusterName) if err != nil { return nil, err } fetcher := newReplicationTaskFetcher( logger, clusterName, currentCluster, config, remoteFrontendClient, metricsClient, ) fetchers = append(fetchers, fetcher) } return &taskFetchersImpl{ fetchers: fetchers, status: common.DaemonStatusInitialized, logger: logger, }, nil } // Start starts the fetchers func (f *taskFetchersImpl) Start() { if !atomic.CompareAndSwapInt32(&f.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } for _, fetcher := range f.fetchers { fetcher.Start() } f.logger.Info("Replication task fetchers started.", tag.Counter(len(f.fetchers))) } // Stop stops the fetchers func (f *taskFetchersImpl) Stop() { if !atomic.CompareAndSwapInt32(&f.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } for _, fetcher := range f.fetchers { fetcher.Stop() } f.logger.Info("Replication task fetchers stopped.", tag.Counter(len(f.fetchers))) } // GetFetchers returns all the fetchers func (f *taskFetchersImpl) GetFetchers() []TaskFetcher { return f.fetchers } // newReplicationTaskFetcher creates a new fetcher. func newReplicationTaskFetcher( logger log.Logger, sourceCluster string, currentCluster string, config *config.Config, sourceFrontend admin.Client, metricsClient metrics.Client, ) TaskFetcher { ctx, cancel := context.WithCancel(context.Background()) requestChan := make([]chan *request, config.ReplicationTaskFetcherParallelism()) for i := 0; i < config.ReplicationTaskFetcherParallelism(); i++ { requestChan[i] = make(chan *request, requestChanBufferSize) } fetcher := &taskFetcherImpl{ status: common.DaemonStatusInitialized, config: config, logger: logger.WithTags(tag.ClusterName(sourceCluster)), metricsScope: metricsClient.Scope(metrics.ReplicationTaskFetcherScope, metrics.TargetClusterTag(sourceCluster)), remotePeer: sourceFrontend, currentCluster: currentCluster, sourceCluster: sourceCluster, rateLimiter: quotas.NewDynamicRateLimiter(config.ReplicationTaskProcessorHostQPS.AsFloat64()), timeSource: clock.NewRealTimeSource(), requestChan: requestChan, ctx: ctx, cancelCtx: cancel, } fetcher.fetchAndDistributeTasksFn = fetcher.fetchAndDistributeTasks return fetcher } // Start starts the fetcher func (f *taskFetcherImpl) Start() { if !atomic.CompareAndSwapInt32(&f.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } // NOTE: ReplicationTaskFetcherParallelism > 1 is now supported. Each fetcher goroutine handles a subset of shards // (distributed via shardID % parallelism) and runs its own fetch cycle independently. for i := 0; i < f.config.ReplicationTaskFetcherParallelism(); i++ { i := i f.wg.Add(1) go f.fetchTasks(i) } f.logger.Info("Replication task fetcher started.", tag.Counter(f.config.ReplicationTaskFetcherParallelism())) } // Stop stops the fetcher func (f *taskFetcherImpl) Stop() { if !atomic.CompareAndSwapInt32(&f.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } f.cancelCtx() if !common.AwaitWaitGroup(&f.wg, 10*time.Second) { f.logger.Warn("Replication task fetcher timed out on shutdown.") } else { f.logger.Info("Replication task fetcher graceful shutdown completed.") } } // fetchTasks collects getReplicationTasks request from shards and send out aggregated request to source frontend. func (f *taskFetcherImpl) fetchTasks(chanIdx int) { defer f.wg.Done() timer := f.timeSource.NewTimer(backoff.JitDuration( f.config.ReplicationTaskFetcherAggregationInterval(), f.config.ReplicationTaskFetcherTimerJitterCoefficient(), )) defer timer.Stop() requestByShard := make(map[int32]*request) for { select { case request := <-f.requestChan[chanIdx]: // Here we only add the request to map. We will wait until timer fires to send the request to remote. if req, ok := requestByShard[request.token.GetShardID()]; ok && req != request { // since this replication task fetcher is per host and replication task processor is per shard // during shard movement, duplicated requests can appear, if shard moved from this host to this host. f.logger.Info("Get replication task request already exist for shard.", tag.ShardID(int(request.token.GetShardID()))) close(req.respChan) } requestByShard[request.token.GetShardID()] = request case <-timer.Chan(): // When timer fires, we collect all the requests we have so far and attempt to send them to remote. err := f.fetchAndDistributeTasksFn(requestByShard) if err != nil { if _, ok := err.(*types.ServiceBusyError); ok { // slow down replication when source cluster is busy timer.Reset(f.config.ReplicationTaskFetcherServiceBusyWait()) } else { timer.Reset(backoff.JitDuration( f.config.ReplicationTaskFetcherErrorRetryWait(), f.config.ReplicationTaskFetcherTimerJitterCoefficient(), )) } } else { timer.Reset(backoff.JitDuration( f.config.ReplicationTaskFetcherAggregationInterval(), f.config.ReplicationTaskFetcherTimerJitterCoefficient(), )) } case <-f.ctx.Done(): return } } } func (f *taskFetcherImpl) fetchAndDistributeTasks(requestByShard map[int32]*request) error { startTime := f.timeSource.Now() defer func() { fetchLatency := f.timeSource.Now().Sub(startTime) f.metricsScope.ExponentialHistogram(metrics.ExponentialReplicationTaskFetchLatency, fetchLatency) }() if len(requestByShard) == 0 { // We don't receive tasks from previous fetch so processors are all sleeping. f.logger.Debug("Skip fetching as no processor is asking for tasks.") return nil } messagesByShard, err := f.getMessages(requestByShard) if err != nil { if _, ok := err.(*types.ServiceBusyError); !ok { f.logger.Error("Failed to get replication tasks", tag.Error(err)) } else { f.logger.Debug("Failed to get replication tasks because service busy") } return err } totalTasks := 0 for _, messages := range messagesByShard { totalTasks += len(messages.ReplicationTasks) } f.metricsScope.RecordHistogramValue(metrics.ReplicationTasksFetchedSize, float64(totalTasks)) f.logger.Debug("Successfully fetched replication tasks.", tag.Counter(len(messagesByShard))) for shardID, tasks := range messagesByShard { request := requestByShard[shardID] request.respChan <- tasks close(request.respChan) delete(requestByShard, shardID) } return nil } func (f *taskFetcherImpl) getMessages(requestByShard map[int32]*request) (map[int32]*types.ReplicationMessages, error) { var tokens []*types.ReplicationToken for _, request := range requestByShard { tokens = append(tokens, request.token) } ctx, cancel := context.WithTimeout(f.ctx, fetchTaskRequestTimeout) defer cancel() request := &types.GetReplicationMessagesRequest{ Tokens: tokens, ClusterName: f.currentCluster, } response, err := f.remotePeer.GetReplicationMessages(ctx, request) if err != nil { return nil, err } return response.GetMessagesByShard(), nil } // GetSourceCluster returns the source cluster for the fetcher func (f *taskFetcherImpl) GetSourceCluster() string { return f.sourceCluster } // GetRequestChan returns the request chan for the fetcher func (f *taskFetcherImpl) GetRequestChan(shardID int) chan<- *request { chanIdx := shardID % len(f.requestChan) return f.requestChan[chanIdx] } // GetRateLimiter returns the host level rate limiter for the fetcher func (f *taskFetcherImpl) GetRateLimiter() quotas.Limiter { return f.rateLimiter } ================================================ FILE: service/history/replication/task_fetcher_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: task_fetcher.go // // Generated by this command: // // mockgen -package replication -source task_fetcher.go -destination task_fetcher_mock.go -self_package github.com/uber/cadence/service/history/replication // // Package replication is a generated GoMock package. package replication import ( reflect "reflect" gomock "go.uber.org/mock/gomock" quotas "github.com/uber/cadence/common/quotas" ) // MockTaskFetcher is a mock of TaskFetcher interface. type MockTaskFetcher struct { ctrl *gomock.Controller recorder *MockTaskFetcherMockRecorder isgomock struct{} } // MockTaskFetcherMockRecorder is the mock recorder for MockTaskFetcher. type MockTaskFetcherMockRecorder struct { mock *MockTaskFetcher } // NewMockTaskFetcher creates a new mock instance. func NewMockTaskFetcher(ctrl *gomock.Controller) *MockTaskFetcher { mock := &MockTaskFetcher{ctrl: ctrl} mock.recorder = &MockTaskFetcherMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskFetcher) EXPECT() *MockTaskFetcherMockRecorder { return m.recorder } // GetRateLimiter mocks base method. func (m *MockTaskFetcher) GetRateLimiter() quotas.Limiter { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRateLimiter") ret0, _ := ret[0].(quotas.Limiter) return ret0 } // GetRateLimiter indicates an expected call of GetRateLimiter. func (mr *MockTaskFetcherMockRecorder) GetRateLimiter() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRateLimiter", reflect.TypeOf((*MockTaskFetcher)(nil).GetRateLimiter)) } // GetRequestChan mocks base method. func (m *MockTaskFetcher) GetRequestChan(shardID int) chan<- *request { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRequestChan", shardID) ret0, _ := ret[0].(chan<- *request) return ret0 } // GetRequestChan indicates an expected call of GetRequestChan. func (mr *MockTaskFetcherMockRecorder) GetRequestChan(shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRequestChan", reflect.TypeOf((*MockTaskFetcher)(nil).GetRequestChan), shardID) } // GetSourceCluster mocks base method. func (m *MockTaskFetcher) GetSourceCluster() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSourceCluster") ret0, _ := ret[0].(string) return ret0 } // GetSourceCluster indicates an expected call of GetSourceCluster. func (mr *MockTaskFetcherMockRecorder) GetSourceCluster() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSourceCluster", reflect.TypeOf((*MockTaskFetcher)(nil).GetSourceCluster)) } // Start mocks base method. func (m *MockTaskFetcher) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockTaskFetcherMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockTaskFetcher)(nil).Start)) } // Stop mocks base method. func (m *MockTaskFetcher) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockTaskFetcherMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockTaskFetcher)(nil).Stop)) } // MockTaskFetchers is a mock of TaskFetchers interface. type MockTaskFetchers struct { ctrl *gomock.Controller recorder *MockTaskFetchersMockRecorder isgomock struct{} } // MockTaskFetchersMockRecorder is the mock recorder for MockTaskFetchers. type MockTaskFetchersMockRecorder struct { mock *MockTaskFetchers } // NewMockTaskFetchers creates a new mock instance. func NewMockTaskFetchers(ctrl *gomock.Controller) *MockTaskFetchers { mock := &MockTaskFetchers{ctrl: ctrl} mock.recorder = &MockTaskFetchersMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskFetchers) EXPECT() *MockTaskFetchersMockRecorder { return m.recorder } // GetFetchers mocks base method. func (m *MockTaskFetchers) GetFetchers() []TaskFetcher { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFetchers") ret0, _ := ret[0].([]TaskFetcher) return ret0 } // GetFetchers indicates an expected call of GetFetchers. func (mr *MockTaskFetchersMockRecorder) GetFetchers() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFetchers", reflect.TypeOf((*MockTaskFetchers)(nil).GetFetchers)) } // Start mocks base method. func (m *MockTaskFetchers) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockTaskFetchersMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockTaskFetchers)(nil).Start)) } // Stop mocks base method. func (m *MockTaskFetchers) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockTaskFetchersMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockTaskFetchers)(nil).Stop)) } ================================================ FILE: service/history/replication/task_fetcher_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package replication import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/client" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" ) type ( taskFetcherSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockResource *resource.Test config *config.Config frontendClient *admin.MockClient taskFetcher *taskFetcherImpl } ) func TestReplicationTaskFetcherSuite(t *testing.T) { s := new(taskFetcherSuite) suite.Run(t, s) } func (s *taskFetcherSuite) SetupSuite() { } func (s *taskFetcherSuite) TearDownSuite() { } func (s *taskFetcherSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.History) s.frontendClient = s.mockResource.RemoteAdminClient logger := testlogger.New(s.T()) s.config = config.NewForTest() s.config.ReplicationTaskFetcherTimerJitterCoefficient = dynamicproperties.GetFloatPropertyFn(0.0) // set jitter to 0 for test s.taskFetcher = newReplicationTaskFetcher( logger, "standby", "active", s.config, s.frontendClient, metrics.NewNoopMetricsClient(), ).(*taskFetcherImpl) } func (s *taskFetcherSuite) TearDownTest() { s.controller.Finish() s.mockResource.Finish(s.T()) } func (s *taskFetcherSuite) TestGetMessages() { requestByShard := make(map[int32]*request) token := &types.ReplicationToken{ ShardID: 0, LastProcessedMessageID: 1, LastRetrievedMessageID: 2, } requestByShard[0] = &request{ token: token, } replicationMessageRequest := &types.GetReplicationMessagesRequest{ Tokens: []*types.ReplicationToken{ token, }, ClusterName: "active", } messageByShared := make(map[int32]*types.ReplicationMessages) messageByShared[0] = &types.ReplicationMessages{} expectedResponse := &types.GetReplicationMessagesResponse{ MessagesByShard: messageByShared, } s.frontendClient.EXPECT().GetReplicationMessages(gomock.Any(), replicationMessageRequest).Return(expectedResponse, nil) response, err := s.taskFetcher.getMessages(requestByShard) s.NoError(err) s.Equal(messageByShared, response) } func (s *taskFetcherSuite) TestFetchAndDistributeTasks() { requestByShard := make(map[int32]*request) token := &types.ReplicationToken{ ShardID: 0, LastProcessedMessageID: 1, LastRetrievedMessageID: 2, } respChan := make(chan *types.ReplicationMessages, 1) requestByShard[0] = &request{ token: token, respChan: respChan, } replicationMessageRequest := &types.GetReplicationMessagesRequest{ Tokens: []*types.ReplicationToken{ token, }, ClusterName: "active", } messageByShared := make(map[int32]*types.ReplicationMessages) messageByShared[0] = &types.ReplicationMessages{} expectedResponse := &types.GetReplicationMessagesResponse{ MessagesByShard: messageByShared, } s.frontendClient.EXPECT().GetReplicationMessages(gomock.Any(), replicationMessageRequest).Return(expectedResponse, nil) err := s.taskFetcher.fetchAndDistributeTasks(requestByShard) s.NoError(err) respToken := <-respChan s.Equal(messageByShared[0], respToken) } func (s *taskFetcherSuite) TestLifecycle() { defer goleak.VerifyNone(s.T()) mockTimeSource := clock.NewMockedTimeSourceAt(time.Now()) s.taskFetcher.timeSource = mockTimeSource respChan0 := make(chan *types.ReplicationMessages, 1) respChan1 := make(chan *types.ReplicationMessages, 1) respChan2 := make(chan *types.ReplicationMessages, 1) respChan3 := make(chan *types.ReplicationMessages, 1) req0 := &request{ token: &types.ReplicationToken{ ShardID: 0, LastRetrievedMessageID: 100, LastProcessedMessageID: 10, }, respChan: respChan0, } req1 := &request{ token: &types.ReplicationToken{ ShardID: 1, LastRetrievedMessageID: 10, LastProcessedMessageID: 1, }, respChan: respChan1, } req2 := &request{ token: &types.ReplicationToken{ ShardID: 0, LastRetrievedMessageID: 10, LastProcessedMessageID: 1, }, respChan: respChan2, } req3 := &request{ token: &types.ReplicationToken{ ShardID: 1, LastRetrievedMessageID: 11, LastProcessedMessageID: 2, }, respChan: respChan3, } fetchAndDistributeTasksFnCall := 0 fetchAndDistributeTasksSyncChan := []chan struct{}{make(chan struct{}), make(chan struct{}), make(chan struct{}), make(chan struct{})} s.taskFetcher.fetchAndDistributeTasksFn = func(requestByShard map[int32]*request) error { defer func() { fetchAndDistributeTasksFnCall++ close(fetchAndDistributeTasksSyncChan[fetchAndDistributeTasksFnCall-1]) }() if fetchAndDistributeTasksFnCall == 0 { s.Equal(map[int32]*request{1: req1, 0: req2}, requestByShard) return &types.ServiceBusyError{} } else if fetchAndDistributeTasksFnCall == 1 { s.Equal(map[int32]*request{1: req3, 0: req2}, requestByShard) return &types.InternalServiceError{} } else if fetchAndDistributeTasksFnCall == 2 { s.Equal(map[int32]*request{1: req3, 0: req2}, requestByShard) for shard := range requestByShard { delete(requestByShard, shard) } return nil } else if fetchAndDistributeTasksFnCall == 3 { s.Equal(map[int32]*request{}, requestByShard) return nil } return nil } s.taskFetcher.Start() defer s.taskFetcher.Stop() requestChan := s.taskFetcher.GetRequestChan(0) // send 3 replication requests to the fetcher requestChan <- req0 requestChan <- req1 requestChan <- req2 _, open := <-respChan0 // block until duplicate replication task is read from fetcher's request channel s.False(open) // process the existing replication requests and return service busy error s.Equal(0, fetchAndDistributeTasksFnCall) mockTimeSource.BlockUntil(1) mockTimeSource.Advance(s.config.ReplicationTaskFetcherAggregationInterval()) _, open = <-fetchAndDistributeTasksSyncChan[0] // block until fetchAndDistributeTasksFn is called s.False(open) s.Equal(1, fetchAndDistributeTasksFnCall) // send a new duplicate replication request to fetcher requestChan <- req3 _, open = <-respChan1 // block until duplicate replication task is read from fetcher's request channel s.False(open) // process the existing replication requests and return non-service busy error mockTimeSource.BlockUntil(1) mockTimeSource.Advance(s.config.ReplicationTaskFetcherServiceBusyWait()) _, open = <-fetchAndDistributeTasksSyncChan[1] // block until fetchAndDistributeTasksFn is called s.False(open) s.Equal(2, fetchAndDistributeTasksFnCall) // process the existing replication requests and return success mockTimeSource.BlockUntil(1) mockTimeSource.Advance(s.config.ReplicationTaskFetcherErrorRetryWait()) _, open = <-fetchAndDistributeTasksSyncChan[2] // block until fetchAndDistributeTasksFn is called s.False(open) s.Equal(3, fetchAndDistributeTasksFnCall) // process empty requests and return success mockTimeSource.BlockUntil(1) mockTimeSource.Advance(s.config.ReplicationTaskFetcherAggregationInterval()) _, open = <-fetchAndDistributeTasksSyncChan[3] // block until fetchAndDistributeTasksFn is called s.False(open) s.Equal(4, fetchAndDistributeTasksFnCall) } func TestTaskFetchers(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockBean := client.NewMockBean(ctrl) mockAdminClient := admin.NewMockClient(ctrl) logger := testlogger.New(t) cfg := config.NewForTest() mockBean.EXPECT().GetRemoteAdminClient(cluster.TestAlternativeClusterName).Return(mockAdminClient, nil) fetchers, err := NewTaskFetchers(logger, cfg, cluster.TestActiveClusterMetadata, mockBean, metrics.NewNoopMetricsClient()) assert.NoError(t, err) assert.NotNil(t, fetchers) assert.Len(t, fetchers.GetFetchers(), len(cluster.TestActiveClusterMetadata.GetRemoteClusterInfo())) fetchers.Start() fetchers.Stop() } func TestTaskFetcherParallelism(t *testing.T) { defer goleak.VerifyNone(t) logger := testlogger.New(t) cfg := config.NewForTest() parallelism := 4 cfg.ReplicationTaskFetcherParallelism = dynamicproperties.GetIntPropertyFn(parallelism) ctrl := gomock.NewController(t) mockAdminClient := admin.NewMockClient(ctrl) fetcher := newReplicationTaskFetcher( logger, "standby", "active", cfg, mockAdminClient, metrics.NewNoopMetricsClient(), ).(*taskFetcherImpl) // Test 1: Verify correct number of channels created assert.Equal(t, parallelism, len(fetcher.requestChan), "Should create 4 request channels") // Test 2: Verify shard-to-channel mapping chan0 := fetcher.GetRequestChan(0) chan1 := fetcher.GetRequestChan(1) chan4 := fetcher.GetRequestChan(4) // 4 % 4 = 0, should be same as chan0 chan5 := fetcher.GetRequestChan(5) // 5 % 4 = 1, should be same as chan1 assert.Equal(t, chan0, chan4, "Shards 0 and 4 should map to same channel (0 % 4 == 4 % 4)") assert.Equal(t, chan1, chan5, "Shards 1 and 5 should map to same channel (1 % 4 == 5 % 4)") assert.NotEqual(t, chan0, chan1, "Different channels should be different") // Test 3: Start fetcher and verify WaitGroup is properly incremented fetcher.Start() // The WaitGroup counter should now be 4 (one per goroutine) // We can verify this by calling Stop() which waits on the WaitGroup // If it hangs or times out, the goroutines weren't started correctly done := make(chan bool) go func() { fetcher.Stop() done <- true }() select { case <-done: // Success - all goroutines exited cleanly case <-time.After(11 * time.Second): t.Fatal("Stop() timed out - goroutines may not have been started correctly") } } ================================================ FILE: service/history/replication/task_hydrator.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2022 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "context" "errors" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" ) var errUnknownReplicationTask = errors.New("unknown replication task") // TaskHydrator will enrich replication task with additional information from mutable state and history events. // Mutable state and history providers can be either in-memory or persistence based implementations; // depending whether we have available data already or need to load it. type TaskHydrator struct { msProvider mutableStateProvider history historyProvider } type ( historyProvider interface { GetEventBlob(ctx context.Context, task *persistence.HistoryReplicationTask) (*types.DataBlob, error) GetNextRunEventBlob(ctx context.Context, task *persistence.HistoryReplicationTask) (*types.DataBlob, error) } mutableStateProvider interface { GetMutableState(ctx context.Context, domainID, workflowID, runID string) (mutableState, execution.ReleaseFunc, error) } mutableState interface { IsWorkflowExecutionRunning() bool GetActivityInfo(int64) (*persistence.ActivityInfo, bool) GetVersionHistories() *persistence.VersionHistories } ) // NewImmediateTaskHydrator will enrich replication tasks with additional information that is immediately available. func NewImmediateTaskHydrator(isRunning bool, vh *persistence.VersionHistories, activities map[int64]*persistence.ActivityInfo, blob, nextBlob *persistence.DataBlob) TaskHydrator { return TaskHydrator{ history: immediateHistoryProvider{blob: blob, nextBlob: nextBlob}, msProvider: immediateMutableStateProvider{immediateMutableState{isRunning, activities, vh}}, } } // NewDeferredTaskHydrator will enrich replication tasks with additional information that is not available on hand, // but is rather loaded in a deferred way later from a database and cache. func NewDeferredTaskHydrator(shardID int, historyManager persistence.HistoryManager, executionCache execution.Cache, domains domainCache) TaskHydrator { return TaskHydrator{ history: historyLoader{shardID, historyManager, domains}, msProvider: mutableStateLoader{executionCache}, } } // Hydrate will enrich replication task with additional information from mutable state and history events. func (h TaskHydrator) Hydrate(ctx context.Context, task persistence.Task) (retTask *types.ReplicationTask, retErr error) { switch t := task.(type) { case *persistence.FailoverMarkerTask: return hydrateFailoverMarkerTask(t), nil } ms, release, err := h.msProvider.GetMutableState(ctx, task.GetDomainID(), task.GetWorkflowID(), task.GetRunID()) defer func() { if release != nil { release(retErr) } }() if common.IsEntityNotExistsError(err) { return nil, nil } if err != nil { return nil, err } switch t := task.(type) { case *persistence.SyncActivityTask: return hydrateSyncActivityTask(t, ms) case *persistence.HistoryReplicationTask: versionHistories := ms.GetVersionHistories() if versionHistories != nil { // Create a copy to release workflow lock early, as hydration will make a DB call, which may take a while versionHistories = versionHistories.Duplicate() } release(nil) return hydrateHistoryReplicationTask(ctx, t, versionHistories, h.history) default: return nil, errUnknownReplicationTask } } func hydrateFailoverMarkerTask(t *persistence.FailoverMarkerTask) *types.ReplicationTask { return &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeFailoverMarker.Ptr(), SourceTaskID: t.TaskID, FailoverMarkerAttributes: &types.FailoverMarkerAttributes{ DomainID: t.DomainID, FailoverVersion: t.Version, }, CreationTime: common.Ptr(t.VisibilityTimestamp.UnixNano()), } } func hydrateSyncActivityTask(task *persistence.SyncActivityTask, ms mutableState) (*types.ReplicationTask, error) { if !ms.IsWorkflowExecutionRunning() { // workflow already finished, no need to process the replication task return nil, nil } activityInfo, ok := ms.GetActivityInfo(task.ScheduledID) if !ok { return nil, nil } var startedTime *int64 if activityInfo.StartedID != constants.EmptyEventID { startedTime = timeToUnixNano(activityInfo.StartedTime) } // Version history uses when replicate the sync activity task var versionHistory *types.VersionHistory if versionHistories := ms.GetVersionHistories(); versionHistories != nil { currentVersionHistory, err := versionHistories.GetCurrentVersionHistory() if err != nil { return nil, err } versionHistory = currentVersionHistory.ToInternalType() } return &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SourceTaskID: task.TaskID, SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: task.DomainID, WorkflowID: task.WorkflowID, RunID: task.RunID, Version: activityInfo.Version, ScheduledID: activityInfo.ScheduleID, ScheduledTime: timeToUnixNano(activityInfo.ScheduledTime), StartedID: activityInfo.StartedID, StartedTime: startedTime, LastHeartbeatTime: timeToUnixNano(activityInfo.LastHeartBeatUpdatedTime), Details: activityInfo.Details, Attempt: activityInfo.Attempt, LastFailureReason: common.Ptr(activityInfo.LastFailureReason), LastWorkerIdentity: activityInfo.LastWorkerIdentity, LastFailureDetails: activityInfo.LastFailureDetails, VersionHistory: versionHistory, }, CreationTime: common.Ptr(task.VisibilityTimestamp.UnixNano()), }, nil } func hydrateHistoryReplicationTask(ctx context.Context, task *persistence.HistoryReplicationTask, versionHistories *persistence.VersionHistories, history historyProvider) (*types.ReplicationTask, error) { if versionHistories == nil { return nil, nil } _, versionHistory, err := versionHistories.FindFirstVersionHistoryByItem(persistence.NewVersionHistoryItem(task.FirstEventID, task.Version)) if err != nil { return nil, err } // BranchToken will not set in get dlq replication message request if len(task.BranchToken) == 0 { task.BranchToken = versionHistory.GetBranchToken() } eventsBlob, err := history.GetEventBlob(ctx, task) if err != nil { return nil, err } newRunEventsBlob, err := history.GetNextRunEventBlob(ctx, task) if err != nil { return nil, err } return &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), SourceTaskID: task.TaskID, HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: task.DomainID, WorkflowID: task.WorkflowID, RunID: task.RunID, VersionHistoryItems: versionHistory.ToInternalType().Items, Events: eventsBlob, NewRunEvents: newRunEventsBlob, }, CreationTime: common.Ptr(task.VisibilityTimestamp.UnixNano()), }, nil } // historyLoader loads history event blobs on demand from a database type historyLoader struct { shardID int history persistence.HistoryManager domains domainCache } func (h historyLoader) GetEventBlob(ctx context.Context, task *persistence.HistoryReplicationTask) (*types.DataBlob, error) { return h.getEventsBlob(ctx, task.DomainID, task.BranchToken, task.FirstEventID, task.NextEventID) } func (h historyLoader) GetNextRunEventBlob(ctx context.Context, task *persistence.HistoryReplicationTask) (*types.DataBlob, error) { if len(task.NewRunBranchToken) == 0 { return nil, nil } // only get the first batch return h.getEventsBlob(ctx, task.DomainID, task.NewRunBranchToken, constants.FirstEventID, constants.FirstEventID+1) } func (h historyLoader) getEventsBlob(ctx context.Context, domainID string, branchToken []byte, minEventID, maxEventID int64) (*types.DataBlob, error) { domain, err := h.domains.GetDomainByID(domainID) if err != nil { return nil, err } resp, err := h.history.ReadRawHistoryBranch(ctx, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: minEventID, MaxEventID: maxEventID, PageSize: 2, // Load more than one to check for data inconsistency errors ShardID: &h.shardID, DomainName: domain.GetInfo().Name, }) if err != nil { return nil, err } if len(resp.HistoryEventBlobs) != 1 { return nil, &types.InternalDataInconsistencyError{Message: "replication hydrator encountered more than 1 NDC raw event batch"} } return resp.HistoryEventBlobs[0].ToInternal(), nil } // mutableStateLoader uses workflow execution cache to load mutable state type mutableStateLoader struct { cache execution.Cache } func (l mutableStateLoader) GetMutableState(ctx context.Context, domainID, workflowID, runID string) (mutableState, execution.ReleaseFunc, error) { wfContext, release, err := l.cache.GetOrCreateWorkflowExecution(ctx, domainID, types.WorkflowExecution{WorkflowID: workflowID, RunID: runID}) if err != nil { return nil, nil, err } mutableState, err := wfContext.LoadWorkflowExecution(ctx) if err != nil { release(err) return nil, nil, err } return mutableState, release, nil } func timeToUnixNano(t time.Time) *int64 { return common.Int64Ptr(t.UnixNano()) } type immediateHistoryProvider struct { blob *persistence.DataBlob nextBlob *persistence.DataBlob } func (h immediateHistoryProvider) GetEventBlob(_ context.Context, _ *persistence.HistoryReplicationTask) (*types.DataBlob, error) { if h.blob == nil { return nil, errors.New("history blob not set") } return h.blob.ToInternal(), nil } func (h immediateHistoryProvider) GetNextRunEventBlob(_ context.Context, _ *persistence.HistoryReplicationTask) (*types.DataBlob, error) { if h.nextBlob == nil { return nil, nil // Expected and common } return h.nextBlob.ToInternal(), nil } type immediateMutableStateProvider struct { ms immediateMutableState } func (r immediateMutableStateProvider) GetMutableState(_ context.Context, _, _, _ string) (mutableState, execution.ReleaseFunc, error) { return r.ms, execution.NoopReleaseFn, nil } type immediateMutableState struct { isRunning bool activities map[int64]*persistence.ActivityInfo versionHistories *persistence.VersionHistories } func (ms immediateMutableState) IsWorkflowExecutionRunning() bool { return ms.isRunning } func (ms immediateMutableState) GetActivityInfo(id int64) (*persistence.ActivityInfo, bool) { info, ok := ms.activities[id] return info, ok } func (ms immediateMutableState) GetVersionHistories() *persistence.VersionHistories { return ms.versionHistories } ================================================ FILE: service/history/replication/task_hydrator_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2022 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "bytes" "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) const ( testShardID = 0 testDomainID = "11111111-1111-1111-1111-111111111111" testWorkflowID = "workflow-id" testRunID = "22222222-2222-2222-2222-222222222222" testTaskID = 111 testCreationTime = int64(333) testFirstEventID = 6 testNextEventID = 8 testVersion = 456 testScheduleID = int64(10) testStartedID = int64(11) testLastFailureReason = "failure-reason" testWorkerIdentity = "worker-identity" testAttempt = 42 ) var ( testBranchToken = []byte{91, 92, 93} testBranchTokenNewRun = []byte{94, 95, 96} testBranchTokenVersionHistory = []byte{97, 98, 99} testDataBlob = &types.DataBlob{Data: []byte{1, 2, 3}, EncodingType: types.EncodingTypeJSON.Ptr()} testDataBlobNewRun = &types.DataBlob{Data: []byte{4, 5, 6}, EncodingType: types.EncodingTypeJSON.Ptr()} testDataBlobVersionHistory = &types.DataBlob{Data: []byte{7, 8, 9}, EncodingType: types.EncodingTypeJSON.Ptr()} testDetails = []byte{100, 101, 102} testLastFailureDetails = []byte{103, 104, 105} testScheduleTime = time.Now() testStartedTime = time.Now() testHeartbeatTime = time.Now() testWorkflowIdentifier = definition.NewWorkflowIdentifier(testDomainID, testWorkflowID, testRunID) ) func TestNewDeferredTaskHydrator(t *testing.T) { h := NewDeferredTaskHydrator(0, nil, nil, nil) require.NotNil(t, h) assert.IsType(t, historyLoader{}, h.history) assert.IsType(t, mutableStateLoader{}, h.msProvider) } func TestTaskHydrator_UnknownTask(t *testing.T) { task := &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testWorkflowIdentifier.DomainID, WorkflowID: testWorkflowIdentifier.WorkflowID, RunID: testWorkflowIdentifier.RunID, }, } th := TaskHydrator{msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{}, }, }} result, err := th.Hydrate(context.Background(), task) assert.Equal(t, errUnknownReplicationTask, err) assert.Nil(t, result) assert.True(t, th.msProvider.(*fakeMutableStateProvider).released) } func TestTaskHydrator_HydrateFailoverMarkerTask(t *testing.T) { task := &persistence.FailoverMarkerTask{ DomainID: testDomainID, TaskData: persistence.TaskData{ TaskID: testTaskID, Version: testVersion, VisibilityTimestamp: time.Unix(0, testCreationTime), }, } expected := types.ReplicationTask{ TaskType: types.ReplicationTaskTypeFailoverMarker.Ptr(), SourceTaskID: testTaskID, FailoverMarkerAttributes: &types.FailoverMarkerAttributes{ DomainID: testDomainID, FailoverVersion: testVersion, }, CreationTime: common.Int64Ptr(testCreationTime), } th := TaskHydrator{} actual, err := th.Hydrate(context.Background(), task) assert.NoError(t, err) assert.Equal(t, &expected, actual) } func TestTaskHydrator_HydrateSyncActivityTask(t *testing.T) { task := &persistence.SyncActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }, TaskData: persistence.TaskData{ TaskID: testTaskID, Version: testVersion, VisibilityTimestamp: time.Unix(0, testCreationTime), }, ScheduledID: testScheduleID, } versionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: testBranchTokenVersionHistory, Items: []*persistence.VersionHistoryItem{ {EventID: testFirstEventID, Version: testVersion}, }, }, }, } activityInfo := persistence.ActivityInfo{ Version: testVersion, ScheduleID: testScheduleID, ScheduledTime: testScheduleTime, StartedID: testStartedID, StartedTime: testStartedTime, DomainID: testDomainID, LastHeartBeatUpdatedTime: testHeartbeatTime, Details: testDetails, Attempt: testAttempt, LastFailureReason: testLastFailureReason, LastFailureDetails: testLastFailureDetails, LastWorkerIdentity: testWorkerIdentity, } tests := []struct { name string task persistence.Task msProvider mutableStateProvider expectTask *types.ReplicationTask expectErr string }{ { name: "hydrates sync activity task", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{ isWorkflowExecutionRunning: true, versionHistories: versionHistories, activityInfos: map[int64]persistence.ActivityInfo{testScheduleID: activityInfo}, }, }, }, expectTask: &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SourceTaskID: testTaskID, CreationTime: common.Int64Ptr(testCreationTime), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, Version: testVersion, ScheduledID: testScheduleID, ScheduledTime: common.Int64Ptr(testScheduleTime.UnixNano()), StartedID: testStartedID, StartedTime: common.Int64Ptr(testStartedTime.UnixNano()), LastHeartbeatTime: common.Int64Ptr(testHeartbeatTime.UnixNano()), Details: testDetails, Attempt: testAttempt, LastFailureReason: common.StringPtr(testLastFailureReason), LastWorkerIdentity: testWorkerIdentity, LastFailureDetails: testLastFailureDetails, VersionHistory: &types.VersionHistory{ Items: []*types.VersionHistoryItem{{EventID: testFirstEventID, Version: testVersion}}, BranchToken: testBranchTokenVersionHistory, }, }, }, }, { name: "workflow is not running - return nil, no error", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{ isWorkflowExecutionRunning: false, }, }, }, expectTask: nil, }, { name: "no activity info - return nil, no error", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{ isWorkflowExecutionRunning: true, activityInfos: map[int64]persistence.ActivityInfo{}, }, }, }, expectTask: nil, }, { name: "bad version histories - return error", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{ isWorkflowExecutionRunning: true, versionHistories: &persistence.VersionHistories{}, activityInfos: map[int64]persistence.ActivityInfo{testScheduleID: activityInfo}, }, }, }, expectErr: "getting branch index: 0, available branch count: 0", }, { name: "workflow does not exist - return nil, no error", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{}, }, expectTask: nil, }, { name: "error loading mutable state", task: task, msProvider: &fakeMutableStateProvider{}, expectErr: "error loading mutable state", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { th := TaskHydrator{msProvider: tt.msProvider} actualTask, err := th.Hydrate(context.Background(), tt.task) if tt.expectErr != "" { assert.EqualError(t, err, tt.expectErr) } else { require.NoError(t, err) assert.Equal(t, tt.expectTask, actualTask) } assert.True(t, th.msProvider.(*fakeMutableStateProvider).released) }) } } func TestTaskHydrator_HydrateHistoryReplicationTask(t *testing.T) { task := &persistence.HistoryReplicationTask{ TaskData: persistence.TaskData{ TaskID: testTaskID, Version: testVersion, VisibilityTimestamp: time.Unix(0, testCreationTime), }, WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }, FirstEventID: testFirstEventID, NextEventID: testNextEventID, BranchToken: testBranchToken, NewRunBranchToken: testBranchTokenNewRun, } taskWithoutBranchToken := &persistence.HistoryReplicationTask{ TaskData: persistence.TaskData{ TaskID: testTaskID, Version: testVersion, VisibilityTimestamp: time.Unix(0, testCreationTime), }, WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }, FirstEventID: testFirstEventID, NextEventID: testNextEventID, NewRunBranchToken: testBranchTokenNewRun, } versionHistories := persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: testBranchTokenVersionHistory, Items: []*persistence.VersionHistoryItem{ {EventID: testFirstEventID, Version: testVersion}, }, }, }, } tests := []struct { name string task *persistence.HistoryReplicationTask msProvider mutableStateProvider history historyProvider expectTask *types.ReplicationTask expectErr string }{ { name: "hydrates history with given branch token", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{versionHistories: &versionHistories}, }, }, history: &fakeHistoryProvider{ blobs: []historyBlob{ {branch: testBranchToken, blob: testDataBlob}, {branch: testBranchTokenNewRun, blob: testDataBlobNewRun}, }, }, expectTask: &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), SourceTaskID: testTaskID, CreationTime: common.Int64Ptr(testCreationTime), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, VersionHistoryItems: []*types.VersionHistoryItem{{EventID: testFirstEventID, Version: testVersion}}, Events: testDataBlob, NewRunEvents: testDataBlobNewRun, }, }, }, { name: "hydrates history with branch token from version histories", task: taskWithoutBranchToken, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{versionHistories: &versionHistories}, }, }, history: &fakeHistoryProvider{ blobs: []historyBlob{ {branch: testBranchTokenVersionHistory, blob: testDataBlobVersionHistory}, {branch: testBranchTokenNewRun, blob: testDataBlobNewRun}, }, }, expectTask: &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), SourceTaskID: testTaskID, CreationTime: common.Int64Ptr(testCreationTime), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, VersionHistoryItems: []*types.VersionHistoryItem{{EventID: testFirstEventID, Version: testVersion}}, Events: testDataBlobVersionHistory, NewRunEvents: testDataBlobNewRun, }, }, }, { name: "no version histories - return nil, no error", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{versionHistories: nil}, }, }, expectTask: nil, }, { name: "bad version histories - return error", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{versionHistories: &persistence.VersionHistories{}}, }, }, expectErr: "version histories does not contains given item.", }, { name: "workflow does not exist - return nil, no error", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{}, }, expectTask: nil, }, { name: "error loading mutable state", task: task, msProvider: &fakeMutableStateProvider{}, expectErr: "error loading mutable state", }, { name: "failed reading event blob - return error", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{versionHistories: &versionHistories}, }, }, history: &fakeHistoryProvider{ blobs: []historyBlob{ {branch: testBranchTokenNewRun, blob: testDataBlobNewRun}, }, }, expectErr: "failed reading history", }, { name: "failed reading event blob for new run - return error", task: task, msProvider: &fakeMutableStateProvider{ workflows: map[definition.WorkflowIdentifier]mutableState{ testWorkflowIdentifier: &fakeMutableState{versionHistories: &versionHistories}, }, }, history: &fakeHistoryProvider{ blobs: []historyBlob{ {branch: testBranchToken, blob: testDataBlob}, }, }, expectErr: "failed reading history", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { th := TaskHydrator{msProvider: tt.msProvider, history: tt.history} actualTask, err := th.Hydrate(context.Background(), tt.task) if tt.expectErr != "" { assert.EqualError(t, err, tt.expectErr) } else { require.NoError(t, err) assert.Equal(t, tt.expectTask, actualTask) } assert.True(t, th.msProvider.(*fakeMutableStateProvider).released) }) } } func TestHistoryLoader_GetEventBlob(t *testing.T) { tests := []struct { name string task *persistence.HistoryReplicationTask domains fakeDomainCache mockHistory func(hm *mocks.HistoryV2Manager) expectDataBlob *types.DataBlob expectErr string }{ { name: "loads data blob", task: &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, }, BranchToken: testBranchToken, FirstEventID: 10, NextEventID: 11, }, domains: fakeDomainCache{testDomainID: testDomain}, mockHistory: func(hm *mocks.HistoryV2Manager) { hm.On("ReadRawHistoryBranch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: testBranchToken, MinEventID: 10, MaxEventID: 11, PageSize: 2, ShardID: common.IntPtr(testShardID), DomainName: testDomainName, }).Return(&persistence.ReadRawHistoryBranchResponse{ HistoryEventBlobs: []*persistence.DataBlob{{Encoding: constants.EncodingTypeJSON, Data: testDataBlob.Data}}, }, nil) }, expectDataBlob: testDataBlob, }, { name: "failed to get domain name", task: &persistence.HistoryReplicationTask{WorkflowIdentifier: persistence.WorkflowIdentifier{DomainID: testDomainID}}, domains: fakeDomainCache{}, mockHistory: func(hm *mocks.HistoryV2Manager) {}, expectErr: "domain does not exist", }, { name: "load failure", task: &persistence.HistoryReplicationTask{WorkflowIdentifier: persistence.WorkflowIdentifier{DomainID: testDomainID}}, domains: fakeDomainCache{testDomainID: testDomain}, mockHistory: func(hm *mocks.HistoryV2Manager) { hm.On("ReadRawHistoryBranch", mock.Anything, mock.Anything).Return(nil, errors.New("load failure")) }, expectErr: "load failure", }, { name: "response must contain exactly one blob", task: &persistence.HistoryReplicationTask{WorkflowIdentifier: persistence.WorkflowIdentifier{DomainID: testDomainID}}, domains: fakeDomainCache{testDomainID: testDomain}, mockHistory: func(hm *mocks.HistoryV2Manager) { hm.On("ReadRawHistoryBranch", mock.Anything, mock.Anything).Return(&persistence.ReadRawHistoryBranchResponse{ HistoryEventBlobs: []*persistence.DataBlob{{}, {}}, // two blobs }, nil) }, expectErr: "replication hydrator encountered more than 1 NDC raw event batch", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hm := &mocks.HistoryV2Manager{} tt.mockHistory(hm) loader := historyLoader{shardID: testShardID, history: hm, domains: tt.domains} dataBlob, err := loader.GetEventBlob(context.Background(), tt.task) if tt.expectErr != "" { assert.EqualError(t, err, tt.expectErr) } else { require.NoError(t, err) assert.Equal(t, tt.expectDataBlob, dataBlob) } }) } } func TestHistoryLoader_GetNextRunEventBlob(t *testing.T) { hm := &mocks.HistoryV2Manager{} loader := historyLoader{shardID: testShardID, history: hm, domains: fakeDomainCache{testDomainID: testDomain}} dataBlob, err := loader.GetNextRunEventBlob(context.Background(), &persistence.HistoryReplicationTask{NewRunBranchToken: nil}) assert.NoError(t, err) assert.Nil(t, dataBlob) hm.On("ReadRawHistoryBranch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: testBranchTokenNewRun, MinEventID: 1, MaxEventID: 2, PageSize: 2, ShardID: common.IntPtr(testShardID), DomainName: testDomainName, }).Return(&persistence.ReadRawHistoryBranchResponse{ HistoryEventBlobs: []*persistence.DataBlob{{Encoding: constants.EncodingTypeJSON, Data: testDataBlob.Data}}, }, nil) dataBlob, err = loader.GetNextRunEventBlob(context.Background(), &persistence.HistoryReplicationTask{WorkflowIdentifier: persistence.WorkflowIdentifier{DomainID: testDomainID}, NewRunBranchToken: testBranchTokenNewRun}) assert.NoError(t, err) assert.Equal(t, testDataBlob, dataBlob) } func TestMutableStateLoader_GetMutableState(t *testing.T) { ctx := context.Background() controller := gomock.NewController(t) testShardContext := shard.NewTestContext( t, controller, &persistence.ShardInfo{ ShardID: testShardID, RangeID: 1, TransferAckLevel: 0, ClusterReplicationLevel: make(map[string]int64), }, config.NewForTest(), ) domainCache := testShardContext.Resource.DomainCache executionCache := execution.NewCache(testShardContext) expectedMS := execution.NewMockMutableState(controller) msLoader := mutableStateLoader{executionCache} domainCache.EXPECT().GetDomainName(gomock.Any()).Return(testDomainName, nil).AnyTimes() exec, release, err := executionCache.GetOrCreateWorkflowExecution(ctx, testDomainID, types.WorkflowExecution{WorkflowID: testWorkflowID, RunID: testRunID}) require.NoError(t, err) // Try getting mutable state while it is still locked, will result in an error contextWithTimeout, cancel := context.WithTimeout(ctx, time.Millisecond) defer cancel() _, _, err = msLoader.GetMutableState(contextWithTimeout, testDomainID, testWorkflowID, testRunID) assert.EqualError(t, err, "context deadline exceeded") release(nil) // Error while trying to load mutable state will be returned domainCache.EXPECT().GetDomainByID("non-existing-domain").Return(nil, errors.New("does not exist")) _, _, err = msLoader.GetMutableState(ctx, "non-existing-domain", testWorkflowID, testRunID) assert.EqualError(t, err, "does not exist") // Happy path domainCache.EXPECT().GetDomainByID(testDomainID).Return(&cache.DomainCacheEntry{}, nil) expectedMS.EXPECT().StartTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil) exec.SetWorkflowExecution(expectedMS) ms, release, err := msLoader.GetMutableState(ctx, testDomainID, testWorkflowID, testRunID) assert.NoError(t, err) assert.Equal(t, expectedMS, ms) assert.NotNil(t, release) release(nil) } func TestImmediateTaskHydrator(t *testing.T) { activityInfo := persistence.ActivityInfo{ Version: testVersion, ScheduleID: testScheduleID, ScheduledTime: testScheduleTime, StartedID: testStartedID, StartedTime: testStartedTime, DomainID: testDomainID, LastHeartBeatUpdatedTime: testHeartbeatTime, Details: testDetails, Attempt: testAttempt, LastFailureReason: testLastFailureReason, LastFailureDetails: testLastFailureDetails, LastWorkerIdentity: testWorkerIdentity, } versionHistories := &persistence.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*persistence.VersionHistory{ { BranchToken: testBranchTokenVersionHistory, Items: []*persistence.VersionHistoryItem{ {EventID: testFirstEventID, Version: testVersion}, }, }, }, } tests := []struct { name string versionHistories *persistence.VersionHistories activities map[int64]*persistence.ActivityInfo blob *persistence.DataBlob nextRunBlob *persistence.DataBlob task persistence.Task expectResult *types.ReplicationTask expectErr string }{ { name: "sync activity task - happy path", versionHistories: versionHistories, activities: map[int64]*persistence.ActivityInfo{testScheduleID: &activityInfo}, task: &persistence.SyncActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }, TaskData: persistence.TaskData{ TaskID: testTaskID, Version: testVersion, VisibilityTimestamp: time.Unix(0, testCreationTime), }, ScheduledID: testScheduleID, }, expectResult: &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SourceTaskID: testTaskID, CreationTime: common.Int64Ptr(testCreationTime), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, Version: testVersion, ScheduledID: testScheduleID, ScheduledTime: common.Int64Ptr(testScheduleTime.UnixNano()), StartedID: testStartedID, StartedTime: common.Int64Ptr(testStartedTime.UnixNano()), LastHeartbeatTime: common.Int64Ptr(testHeartbeatTime.UnixNano()), Details: testDetails, Attempt: testAttempt, LastFailureReason: common.StringPtr(testLastFailureReason), LastWorkerIdentity: testWorkerIdentity, LastFailureDetails: testLastFailureDetails, VersionHistory: &types.VersionHistory{ Items: []*types.VersionHistoryItem{{EventID: testFirstEventID, Version: testVersion}}, BranchToken: testBranchTokenVersionHistory, }, }, }, }, { name: "sync activity task - missing activity info", versionHistories: versionHistories, activities: map[int64]*persistence.ActivityInfo{}, task: &persistence.SyncActivityTask{ ScheduledID: testScheduleID, }, expectResult: nil, }, { name: "history task - happy path", versionHistories: versionHistories, blob: persistence.NewDataBlobFromInternal(testDataBlob), nextRunBlob: persistence.NewDataBlobFromInternal(testDataBlobNewRun), task: &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }, TaskData: persistence.TaskData{ TaskID: testTaskID, Version: testVersion, VisibilityTimestamp: time.Unix(0, testCreationTime), }, FirstEventID: testFirstEventID, NextEventID: testNextEventID, BranchToken: testBranchToken, NewRunBranchToken: testBranchTokenNewRun, }, expectResult: &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), SourceTaskID: testTaskID, CreationTime: common.Int64Ptr(testCreationTime), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, VersionHistoryItems: []*types.VersionHistoryItem{{EventID: testFirstEventID, Version: testVersion}}, Events: testDataBlob, NewRunEvents: testDataBlobNewRun, }, }, }, { name: "history task - no next run", versionHistories: versionHistories, blob: persistence.NewDataBlobFromInternal(testDataBlob), task: &persistence.HistoryReplicationTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }, TaskData: persistence.TaskData{ TaskID: testTaskID, Version: testVersion, VisibilityTimestamp: time.Unix(0, testCreationTime), }, FirstEventID: testFirstEventID, NextEventID: testNextEventID, BranchToken: testBranchToken, }, expectResult: &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), SourceTaskID: testTaskID, CreationTime: common.Int64Ptr(testCreationTime), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, VersionHistoryItems: []*types.VersionHistoryItem{{EventID: testFirstEventID, Version: testVersion}}, Events: testDataBlob, }, }, }, { name: "history task - missing data blob", versionHistories: versionHistories, task: &persistence.HistoryReplicationTask{ TaskData: persistence.TaskData{ Version: testVersion, }, FirstEventID: testFirstEventID, BranchToken: testBranchToken, }, expectErr: "history blob not set", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { h := NewImmediateTaskHydrator(true, tt.versionHistories, tt.activities, tt.blob, tt.nextRunBlob) result, err := h.Hydrate(context.Background(), tt.task) if tt.expectErr != "" { assert.EqualError(t, err, tt.expectErr) } else { assert.NoError(t, err) assert.Equal(t, tt.expectResult, result) } }) } } type fakeMutableStateProvider struct { workflows map[definition.WorkflowIdentifier]mutableState released bool } func (msp *fakeMutableStateProvider) GetMutableState(ctx context.Context, domainID, workflowID, runID string) (mutableState, execution.ReleaseFunc, error) { releaseFn := func(error) { msp.released = true } if msp.workflows == nil { return nil, releaseFn, errors.New("error loading mutable state") } ms, ok := msp.workflows[definition.NewWorkflowIdentifier(domainID, workflowID, runID)] if !ok { return nil, releaseFn, &types.EntityNotExistsError{} } return ms, releaseFn, nil } type fakeMutableState struct { isWorkflowExecutionRunning bool versionHistories *persistence.VersionHistories activityInfos map[int64]persistence.ActivityInfo } func (ms fakeMutableState) IsWorkflowExecutionRunning() bool { return ms.isWorkflowExecutionRunning } func (ms fakeMutableState) GetActivityInfo(scheduleID int64) (*persistence.ActivityInfo, bool) { ai, ok := ms.activityInfos[scheduleID] return &ai, ok } func (ms fakeMutableState) GetVersionHistories() *persistence.VersionHistories { return ms.versionHistories } type historyBlob struct { branch []byte blob *types.DataBlob } type fakeHistoryProvider struct { blobs []historyBlob } func (h fakeHistoryProvider) GetEventBlob(ctx context.Context, task *persistence.HistoryReplicationTask) (*types.DataBlob, error) { return h.getBlob(task.BranchToken) } func (h fakeHistoryProvider) GetNextRunEventBlob(ctx context.Context, task *persistence.HistoryReplicationTask) (*types.DataBlob, error) { return h.getBlob(task.NewRunBranchToken) } func (h fakeHistoryProvider) getBlob(branch []byte) (*types.DataBlob, error) { for _, b := range h.blobs { if bytes.Equal(b.branch, branch) { return b.blob, nil } } return nil, errors.New("failed reading history") } ================================================ FILE: service/history/replication/task_processor.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package replication import ( "context" "encoding/json" "errors" "fmt" "math" "strconv" "sync" "sync/atomic" "time" "github.com/pborman/uuid" "go.uber.org/yarpc/yarpcerrors" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/reconciliation" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) const ( dropSyncShardTaskTimeThreshold = 10 * time.Minute replicationTimeout = 30 * time.Second dlqErrorRetryWait = time.Second dlqMetricsEmitTimerInterval = 5 * time.Minute dlqMetricsEmitTimerCoefficient = 0.05 ) var ( // ErrUnknownReplicationTask is the error to indicate unknown replication task type ErrUnknownReplicationTask = &types.BadRequestError{Message: "unknown replication task"} ) type ( // TaskProcessor is responsible for processing replication tasks for a shard. TaskProcessor interface { common.Daemon } // taskProcessorImpl is responsible for processing replication tasks for a shard. taskProcessorImpl struct { currentCluster string sourceCluster string status int32 shard shard.Context historyEngine engine.Engine historySerializer persistence.PayloadSerializer config *config.Config metricsClient metrics.Client logger log.Logger taskExecutor TaskExecutor hostRateLimiter quotas.Limiter shardRateLimiter quotas.Limiter taskRetryPolicy backoff.RetryPolicy dlqRetryPolicy backoff.RetryPolicy noTaskRetrier backoff.Retrier lastProcessedMessageID int64 lastRetrievedMessageID int64 requestChan chan<- *request syncShardChan chan *types.SyncShardStatus done chan struct{} wg sync.WaitGroup } request struct { token *types.ReplicationToken respChan chan<- *types.ReplicationMessages } ) var _ TaskProcessor = (*taskProcessorImpl)(nil) // NewTaskProcessor creates a new replication task processor. func NewTaskProcessor( shard shard.Context, historyEngine engine.Engine, config *config.Config, metricsClient metrics.Client, taskFetcher TaskFetcher, taskExecutor TaskExecutor, clock clock.TimeSource, ) TaskProcessor { shardID := shard.GetShardID() sourceCluster := taskFetcher.GetSourceCluster() firstRetryPolicy := backoff.NewExponentialRetryPolicy(config.ReplicationTaskProcessorErrorRetryWait(shardID)) firstRetryPolicy.SetMaximumAttempts(config.ReplicationTaskProcessorErrorRetryMaxAttempts(shardID)) secondRetryPolicy := backoff.NewExponentialRetryPolicy(config.ReplicationTaskProcessorErrorSecondRetryWait(shardID)) secondRetryPolicy.SetMaximumInterval(config.ReplicationTaskProcessorErrorSecondRetryMaxWait(shardID)) secondRetryPolicy.SetExpirationInterval(config.ReplicationTaskProcessorErrorSecondRetryExpiration(shardID)) taskRetryPolicy := backoff.NewMultiPhasesRetryPolicy(firstRetryPolicy, secondRetryPolicy) dlqRetryPolicy := backoff.NewExponentialRetryPolicy(dlqErrorRetryWait) dlqRetryPolicy.SetExpirationInterval(backoff.NoInterval) noTaskBackoffPolicy := backoff.NewExponentialRetryPolicy(config.ReplicationTaskProcessorNoTaskRetryWait(shardID)) noTaskBackoffPolicy.SetBackoffCoefficient(1) noTaskBackoffPolicy.SetExpirationInterval(backoff.NoInterval) noTaskRetrier := backoff.NewRetrier(noTaskBackoffPolicy, clock) return &taskProcessorImpl{ currentCluster: shard.GetClusterMetadata().GetCurrentClusterName(), sourceCluster: sourceCluster, status: common.DaemonStatusInitialized, shard: shard, historyEngine: historyEngine, historySerializer: persistence.NewPayloadSerializer(), config: config, metricsClient: metricsClient, logger: shard.GetLogger().WithTags(tag.SourceCluster(sourceCluster), tag.ShardID(shardID)), taskExecutor: taskExecutor, hostRateLimiter: taskFetcher.GetRateLimiter(), shardRateLimiter: quotas.NewDynamicRateLimiter(config.ReplicationTaskProcessorShardQPS.AsFloat64()), taskRetryPolicy: taskRetryPolicy, dlqRetryPolicy: dlqRetryPolicy, noTaskRetrier: noTaskRetrier, requestChan: taskFetcher.GetRequestChan(shardID), syncShardChan: make(chan *types.SyncShardStatus, 1), done: make(chan struct{}), lastProcessedMessageID: constants.EmptyMessageID, lastRetrievedMessageID: constants.EmptyMessageID, } } // Start starts the processor func (p *taskProcessorImpl) Start() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } p.wg.Add(3) go p.processorLoop() go p.syncShardStatusLoop() go p.cleanupReplicationTaskLoop() p.logger.Info("ReplicationTaskProcessor started.") } // Stop stops the processor func (p *taskProcessorImpl) Stop() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } p.logger.Debug("ReplicationTaskProcessor shutting down.") close(p.done) if !common.AwaitWaitGroup(&p.wg, 10*time.Second) { p.logger.Warn("ReplicationTaskProcessor timed out on shutdown.") } else { p.logger.Info("ReplicationTaskProcessor shutdown completed") } } func (p *taskProcessorImpl) processorLoop() { defer func() { p.logger.Debug("Closing replication task processor.", tag.ReadLevel(p.lastRetrievedMessageID)) p.wg.Done() }() Loop: for { respChan := make(chan *types.ReplicationMessages, 1) // TODO: when we support prefetching, LastRetrievedMessageID can be different than LastProcessedMessageID if p.isShuttingDown() { return } select { case <-p.done: // shard is closing return case p.requestChan <- &request{ token: &types.ReplicationToken{ ShardID: int32(p.shard.GetShardID()), LastRetrievedMessageID: p.lastRetrievedMessageID, LastProcessedMessageID: p.lastProcessedMessageID, }, respChan: respChan, }: // signal sent, continue to process replication messages } select { case response, ok := <-respChan: if !ok { p.logger.Debug("Fetch replication messages chan closed.") continue Loop } p.logger.Debug("Got fetch replication messages response.", tag.ReadLevel(response.GetLastRetrievedMessageID()), tag.Bool(response.GetHasMore()), tag.Counter(len(response.GetReplicationTasks())), ) p.taskProcessingStartWait() p.processResponse(response) case <-p.done: return } } } func (p *taskProcessorImpl) cleanupReplicationTaskLoop() { defer p.wg.Done() shardID := p.shard.GetShardID() timer := time.NewTimer(backoff.JitDuration( p.config.ReplicationTaskProcessorCleanupInterval(shardID), p.config.ReplicationTaskProcessorCleanupJitterCoefficient(shardID), )) defer timer.Stop() for { select { case <-p.done: return case <-timer.C: if err := p.cleanupAckedReplicationTasks(); err != nil { p.logger.Error("Failed to clean up replication messages.", tag.Error(err)) p.metricsClient.Scope(metrics.ReplicationTaskCleanupScope).IncCounter(metrics.ReplicationTaskCleanupFailure) } timer.Reset(backoff.JitDuration( p.config.ReplicationTaskProcessorCleanupInterval(shardID), p.config.ReplicationTaskProcessorCleanupJitterCoefficient(shardID), )) } } } func (p *taskProcessorImpl) cleanupAckedReplicationTasks() error { minAckLevel := int64(math.MaxInt64) for clusterName := range p.shard.GetClusterMetadata().GetRemoteClusterInfo() { ackLevel := p.shard.GetQueueClusterAckLevel(persistence.HistoryTaskCategoryReplication, clusterName).GetTaskID() if ackLevel < minAckLevel { minAckLevel = ackLevel } } p.logger.Debug("Cleaning up replication task queue.", tag.ReadLevel(minAckLevel)) p.metricsClient.Scope(metrics.ReplicationTaskCleanupScope).IncCounter(metrics.ReplicationTaskCleanupCount) maxReadLevel := p.shard.UpdateIfNeededAndGetQueueMaxReadLevel( persistence.HistoryTaskCategoryReplication, p.currentCluster, ).GetTaskID() lagCount := int(maxReadLevel - minAckLevel) scope := p.metricsClient.Scope(metrics.ReplicationTaskFetcherScope, metrics.TargetClusterTag(p.currentCluster), ) scope.RecordTimer(metrics.ReplicationTasksLag, time.Duration(lagCount)) scope.RecordHistogramValue(metrics.ReplicationTasksLagHistogram, float64(lagCount)) for { pageSize := p.config.ReplicatorTaskDeleteBatchSize() resp, err := p.shard.GetExecutionManager().RangeCompleteHistoryTask( context.Background(), &persistence.RangeCompleteHistoryTaskRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(minAckLevel + 1), PageSize: pageSize, }, ) if err != nil { return err } if !persistence.HasMoreRowsToDelete(resp.TasksCompleted, pageSize) { break } } return nil } func (p *taskProcessorImpl) processResponse(response *types.ReplicationMessages) { defer func() { if r := recover(); r != nil { p.logger.Error("processResponse encountered panic.", tag.Value(r)) panic(r) } }() select { case p.syncShardChan <- response.GetSyncShardStatus(): default: } scope := p.metricsClient.Scope(metrics.ReplicationTaskFetcherScope, metrics.TargetClusterTag(p.sourceCluster)) batchRequestStartTime := time.Now() ctx := context.Background() for _, replicationTask := range response.ReplicationTasks { // TODO: move to MultiStageRateLimiter _ = p.hostRateLimiter.Wait(ctx) _ = p.shardRateLimiter.Wait(ctx) err := p.processSingleTask(replicationTask) if err != nil { // Encounter error and skip updating ack levels // TODO: Does this behavior make sense? If ack levels are not updated the whole batch will have to be re-fetched via processor loop. // Potential improvements: // 1. Update ack levels for the tasks that were processed successfully. // 2. Emit logs/metrics for these cases that we give up on updating ack levels. return } } // Note here we check replication tasks instead of hasMore. The expectation is that in a steady state // we will receive replication tasks but hasMore is false (meaning that we are always catching up). // So hasMore might not be a good indicator for additional wait. if len(response.ReplicationTasks) == 0 { backoffDuration := p.noTaskRetrier.NextBackOff() time.Sleep(backoffDuration) } else { appliedLatency := time.Since(batchRequestStartTime) scope.RecordTimer(metrics.ReplicationTasksAppliedLatency, appliedLatency) scope.ExponentialHistogram(metrics.ReplicationTasksAppliedLatencyHistogram, appliedLatency) } if p.isShuttingDown() { // avoid updating ack-levels if there's a shutdown as well remembering that GetReplication messages is a *write* api // (keeping track of consumer offsets), so we have to be careful what data it's sent return } p.lastProcessedMessageID = response.GetLastRetrievedMessageID() p.lastRetrievedMessageID = response.GetLastRetrievedMessageID() scope.UpdateGauge(metrics.LastRetrievedMessageID, float64(p.lastRetrievedMessageID)) p.noTaskRetrier.Reset() } func (p *taskProcessorImpl) syncShardStatusLoop() { defer p.wg.Done() timer := time.NewTimer(backoff.JitDuration( p.config.ShardSyncMinInterval(), p.config.ShardSyncTimerJitterCoefficient(), )) defer timer.Stop() var syncShardTask *types.SyncShardStatus for { select { case syncShardRequest := <-p.syncShardChan: syncShardTask = syncShardRequest case <-timer.C: if err := p.handleSyncShardStatus( syncShardTask, ); err != nil { p.logger.Error("failed to sync shard status", tag.Error(err)) p.metricsClient.Scope(metrics.HistorySyncShardStatusScope).IncCounter(metrics.SyncShardFromRemoteFailure) } timer.Reset(backoff.JitDuration( p.config.ShardSyncMinInterval(), p.config.ShardSyncTimerJitterCoefficient(), )) case <-p.done: return } } } func (p *taskProcessorImpl) handleSyncShardStatus(status *types.SyncShardStatus) error { if status == nil || p.shard.GetTimeSource().Now().Sub(time.Unix(0, status.GetTimestamp())) > dropSyncShardTaskTimeThreshold { return nil } p.metricsClient.Scope(metrics.HistorySyncShardStatusScope).IncCounter(metrics.SyncShardFromRemoteCounter) ctx, cancel := context.WithTimeout(context.Background(), replicationTimeout) defer cancel() return p.historyEngine.SyncShardStatus(ctx, &types.SyncShardStatusRequest{ SourceCluster: p.sourceCluster, ShardID: int64(p.shard.GetShardID()), Timestamp: status.Timestamp, }) } func (p *taskProcessorImpl) processSingleTask(replicationTask *types.ReplicationTask) error { retryTransientError := func(ctx context.Context) error { throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(p.taskRetryPolicy), backoff.WithRetryableError(isTransientRetryableError), ) return throttleRetry.Do(ctx, func(ctx context.Context) error { select { case <-p.done: // if the processor is stopping, skip the task // the ack level will not update and the new shard owner will retry the task. return nil default: return p.processTaskOnce(replicationTask) } }) } // Handle service busy error throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(common.CreateReplicationServiceBusyRetryPolicy()), backoff.WithRetryableError(common.IsServiceBusyError), ) err := throttleRetry.Do(context.Background(), retryTransientError) switch { case err == nil: return nil case common.IsServiceBusyError(err): return err case err == execution.ErrMissingVersionHistories: // skip the workflow without version histories p.logger.Warn("Encounter workflow without version histories") return nil default: // handle error } // handle error to DLQ select { case <-p.done: p.logger.Warn("Skip adding new messages to DLQ.", tag.Error(err)) return err default: request, err2 := p.generateDLQRequest(replicationTask) if err2 != nil { p.logger.Error("Failed to generate DLQ replication task.", tag.Error(err2)) // We cannot deserialize the task. Dropping it. return nil } p.logger.Error("Failed to apply replication task after retry. Putting task into DLQ.", tag.WorkflowDomainID(request.TaskInfo.GetDomainID()), tag.WorkflowID(request.TaskInfo.GetWorkflowID()), tag.WorkflowRunID(request.TaskInfo.GetRunID()), tag.TaskID(request.TaskInfo.GetTaskID()), tag.TaskType(request.TaskInfo.GetTaskType()), tag.Error(err), ) // TODO: uncomment this when the execution fixer workflow is ready // if err = p.triggerDataInconsistencyScan(replicationTask); err != nil { // p.logger.Warn("Failed to trigger data scan", tag.Error(err)) // p.metricsClient.IncCounter(metrics.ReplicationDLQStatsScope, metrics.ReplicationDLQValidationFailed) // } return p.putReplicationTaskToDLQ(request) } } func (p *taskProcessorImpl) processTaskOnce(replicationTask *types.ReplicationTask) error { ts := p.shard.GetTimeSource() startTime := ts.Now() scope, err := p.taskExecutor.execute(replicationTask, false) if err != nil { p.updateFailureMetric(scope, err, p.shard.GetShardID()) } else { now := ts.Now() mScope := p.metricsClient.Scope(scope, metrics.TargetClusterTag(p.sourceCluster)) domainID := replicationTask.HistoryTaskV2Attributes.GetDomainID() var domainName string if domainID != "" { cachedName, errorDomainName := p.shard.GetDomainCache().GetDomainName(domainID) if errorDomainName != nil { return errorDomainName } domainName = cachedName } mScope = mScope.Tagged(metrics.DomainTag(domainName)) // use consistent tags so Prometheus does not break // emit single task processing latency mScope.ExponentialHistogram(metrics.ExponentialTaskProcessingLatency, now.Sub(startTime)) // emit latency from task generated to task received mScope.ExponentialHistogram( metrics.ExponentialReplicationTaskLatency, now.Sub(time.Unix(0, replicationTask.GetCreationTime())), ) // emit single task processing latency mScope.RecordTimer(metrics.TaskProcessingLatency, now.Sub(startTime)) e2eLatency := now.Sub(time.Unix(0, replicationTask.GetCreationTime())) // emit latency from task generated to task received mScope.RecordTimer(metrics.ReplicationTaskLatency, e2eLatency) // if latency is not switched off, and exceeds threshold, emit structured log if p.config.ReplicationTaskProcessorLatencyLogThreshold() > 0 && e2eLatency >= p.config.ReplicationTaskProcessorLatencyLogThreshold() { attr := replicationTask.GetHistoryTaskV2Attributes() var workflowID, runID string if attr != nil { workflowID = attr.GetWorkflowID() runID = attr.GetRunID() } p.logger.Warn("high replication latency", tag.WorkflowDomainID(domainID), tag.WorkflowID(workflowID), tag.WorkflowRunID(runID), tag.ShardID(p.shard.GetShardID()), tag.SourceCluster(p.sourceCluster), ) } // emit the number of replication tasks mScope.IncCounter(metrics.ReplicationTasksAppliedPerDomain) shardScope := p.metricsClient.Scope(scope, metrics.TargetClusterTag(p.sourceCluster), metrics.InstanceTag(strconv.Itoa(p.shard.GetShardID()))) shardScope.IncCounter(metrics.ReplicationTasksApplied) } return err } func (p *taskProcessorImpl) putReplicationTaskToDLQ(request *persistence.PutReplicationTaskToDLQRequest) error { p.metricsClient.Scope( metrics.ReplicationDLQStatsScope, metrics.TargetClusterTag(p.sourceCluster), metrics.InstanceTag(strconv.Itoa(p.shard.GetShardID())), ).UpdateGauge( metrics.ReplicationDLQMaxLevelGauge, float64(request.TaskInfo.GetTaskID()), ) throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(p.dlqRetryPolicy), backoff.WithRetryableError(p.shouldRetryDLQ), ) // The following is guaranteed to success or retry forever until processor is shutdown. return throttleRetry.Do(context.Background(), func(ctx context.Context) error { err := p.shard.GetExecutionManager().PutReplicationTaskToDLQ(ctx, request) if err != nil { p.logger.Error("Failed to put replication task to DLQ.", tag.Error(err)) p.metricsClient.IncCounter(metrics.ReplicationTaskFetcherScope, metrics.ReplicationDLQFailed) } return err }) } func (p *taskProcessorImpl) generateDLQRequest( replicationTask *types.ReplicationTask, ) (*persistence.PutReplicationTaskToDLQRequest, error) { switch *replicationTask.TaskType { case types.ReplicationTaskTypeSyncActivity: taskAttributes := replicationTask.GetSyncActivityTaskAttributes() domainName, err := p.shard.GetDomainCache().GetDomainName(taskAttributes.GetDomainID()) if err != nil { return nil, err } return &persistence.PutReplicationTaskToDLQRequest{ SourceClusterName: p.sourceCluster, TaskInfo: &persistence.ReplicationTaskInfo{ DomainID: taskAttributes.GetDomainID(), WorkflowID: taskAttributes.GetWorkflowID(), RunID: taskAttributes.GetRunID(), TaskID: replicationTask.GetSourceTaskID(), TaskType: persistence.ReplicationTaskTypeSyncActivity, ScheduledID: taskAttributes.GetScheduledID(), }, DomainName: domainName, }, nil case types.ReplicationTaskTypeHistoryV2: taskAttributes := replicationTask.GetHistoryTaskV2Attributes() domainName, err := p.shard.GetDomainCache().GetDomainName(taskAttributes.GetDomainID()) if err != nil { return nil, err } eventsDataBlob := persistence.NewDataBlobFromInternal(taskAttributes.GetEvents()) events, err := p.historySerializer.DeserializeBatchEvents(eventsDataBlob) if err != nil { return nil, err } if len(events) == 0 { p.logger.Error("Empty events in a batch") return nil, fmt.Errorf("corrupted history event batch, empty events") } return &persistence.PutReplicationTaskToDLQRequest{ SourceClusterName: p.sourceCluster, TaskInfo: &persistence.ReplicationTaskInfo{ DomainID: taskAttributes.GetDomainID(), WorkflowID: taskAttributes.GetWorkflowID(), RunID: taskAttributes.GetRunID(), TaskID: replicationTask.GetSourceTaskID(), TaskType: persistence.ReplicationTaskTypeHistory, FirstEventID: events[0].ID, NextEventID: events[len(events)-1].ID + 1, Version: events[0].Version, }, DomainName: domainName, }, nil default: return nil, fmt.Errorf("unknown replication task type") } } func (p *taskProcessorImpl) triggerDataInconsistencyScan(replicationTask *types.ReplicationTask) error { var failoverVersion int64 var domainID string var workflowID string var runID string switch { case replicationTask.GetHistoryTaskV2Attributes() != nil: attr := replicationTask.GetHistoryTaskV2Attributes() versionHistoryItems := attr.GetVersionHistoryItems() if len(versionHistoryItems) == 0 { return errors.New("failed to trigger data scan due to invalid version history") } // version history items in same batch should be the same failoverVersion = versionHistoryItems[0].GetVersion() domainID = attr.GetDomainID() workflowID = attr.GetWorkflowID() runID = attr.GetRunID() case replicationTask.GetSyncActivityTaskAttributes() != nil: attr := replicationTask.GetSyncActivityTaskAttributes() failoverVersion = replicationTask.GetSyncActivityTaskAttributes().Version domainID = attr.GetDomainID() workflowID = attr.GetWorkflowID() runID = attr.GetRunID() default: return nil } clusterName, err := p.shard.GetClusterMetadata().ClusterNameForFailoverVersion(failoverVersion) if err != nil { return err } client, err := p.shard.GetService().GetClientBean().GetRemoteFrontendClient(clusterName) if err != nil { return err } fixExecution := entity.Execution{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, ShardID: p.shard.GetShardID(), } fixExecutionInput, err := json.Marshal(fixExecution) if err != nil { return err } // Assume the workflow is corrupted, rely on invariant to validate it _, err = client.SignalWithStartWorkflowExecution(context.Background(), &types.SignalWithStartWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, WorkflowID: reconciliation.CheckDataCorruptionWorkflowID, WorkflowType: &types.WorkflowType{Name: reconciliation.CheckDataCorruptionWorkflowType}, TaskList: &types.TaskList{Name: reconciliation.CheckDataCorruptionWorkflowTaskList}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(reconciliation.CheckDataCorruptionWorkflowTimeoutInSeconds), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(reconciliation.CheckDataCorruptionWorkflowTaskTimeoutInSeconds), Identity: "cadence-history-replication", RequestID: uuid.New(), WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), SignalName: reconciliation.CheckDataCorruptionWorkflowSignalName, SignalInput: fixExecutionInput, }) return err } func isTransientRetryableError(err error) bool { switch err.(type) { case *types.BadRequestError: return false case *types.ServiceBusyError: return false default: return true } } func (p *taskProcessorImpl) shouldRetryDLQ(err error) bool { if err == nil { return false } select { case <-p.done: p.logger.Debug("ReplicationTaskProcessor shutting down.") return false default: return true } } func (p *taskProcessorImpl) updateFailureMetric(scope metrics.ScopeIdx, err error, shardID int) { // Always update failure counter for all replicator errors shardScope := p.metricsClient.Scope(scope, metrics.InstanceTag(strconv.Itoa(shardID))) shardScope.IncCounter(metrics.ReplicatorFailures) // Also update counter to distinguish between type of failures switch err := err.(type) { case *types.ShardOwnershipLostError: shardScope.IncCounter(metrics.CadenceErrShardOwnershipLostCounter) case *types.BadRequestError: shardScope.IncCounter(metrics.CadenceErrBadRequestCounter) case *types.DomainNotActiveError: shardScope.IncCounter(metrics.CadenceErrDomainNotActiveCounter) case *types.WorkflowExecutionAlreadyStartedError: shardScope.IncCounter(metrics.CadenceErrExecutionAlreadyStartedCounter) case *types.EntityNotExistsError: shardScope.IncCounter(metrics.CadenceErrEntityNotExistsCounter) case *types.WorkflowExecutionAlreadyCompletedError: shardScope.IncCounter(metrics.CadenceErrWorkflowExecutionAlreadyCompletedCounter) case *types.LimitExceededError: shardScope.IncCounter(metrics.CadenceErrLimitExceededCounter) case *yarpcerrors.Status: if err.Code() == yarpcerrors.CodeDeadlineExceeded { shardScope.IncCounter(metrics.CadenceErrContextTimeoutCounter) } } } func (p *taskProcessorImpl) taskProcessingStartWait() { shardID := p.shard.GetShardID() time.Sleep(backoff.JitDuration( p.config.ReplicationTaskProcessorStartWait(shardID), p.config.ReplicationTaskProcessorStartWaitJitterCoefficient(shardID), )) } func (p *taskProcessorImpl) isShuttingDown() bool { select { case <-p.done: return true default: return false } } ================================================ FILE: service/history/replication/task_processor_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package replication import ( "context" "encoding/json" "errors" "fmt" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/goleak" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/client" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/reconciliation" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/shard" ) type ( taskProcessorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockEngine *engine.MockEngine config *config.Config mockDomainCache *cache.MockDomainCache mockClientBean *client.MockBean mockFrontendClient *frontend.MockClient adminClient *admin.MockClient executionManager *mocks.ExecutionManager requestChan chan *request taskFetcher *fakeTaskFetcher taskExecutor *MockTaskExecutor taskProcessor *taskProcessorImpl clock clock.MockedTimeSource } ) type fakeTaskFetcher struct { sourceCluster string requestChan chan *request rateLimiter quotas.Limiter } func (f fakeTaskFetcher) Start() {} func (f fakeTaskFetcher) Stop() {} func (f fakeTaskFetcher) GetSourceCluster() string { return f.sourceCluster } func (f fakeTaskFetcher) GetRequestChan(shardID int) chan<- *request { return f.requestChan } func (f fakeTaskFetcher) GetRateLimiter() quotas.Limiter { return f.rateLimiter } func TestTaskProcessorSuite(t *testing.T) { s := new(taskProcessorSuite) suite.Run(t, s) } func (s *taskProcessorSuite) SetupSuite() { } func (s *taskProcessorSuite) TearDownSuite() { } func (s *taskProcessorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, ClusterReplicationLevel: map[string]int64{cluster.TestAlternativeClusterName: 350}, }, s.config, ) s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockClientBean = s.mockShard.Resource.ClientBean s.mockFrontendClient = s.mockShard.Resource.RemoteFrontendClient s.adminClient = s.mockShard.Resource.RemoteAdminClient s.executionManager = s.mockShard.Resource.ExecutionMgr s.clock = clock.NewMockedTimeSource() s.mockEngine = engine.NewMockEngine(s.controller) s.config = config.NewForTest() s.config.ReplicationTaskProcessorNoTaskRetryWait = dynamicproperties.GetDurationPropertyFnFilteredByShardID(1 * time.Millisecond) metricsClient := metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) s.requestChan = make(chan *request, 10) s.taskFetcher = &fakeTaskFetcher{ sourceCluster: "standby", requestChan: s.requestChan, rateLimiter: quotas.NewDynamicRateLimiter(func() float64 { return 100 }), } s.taskExecutor = NewMockTaskExecutor(s.controller) s.taskProcessor = NewTaskProcessor( s.mockShard, s.mockEngine, s.config, metricsClient, s.taskFetcher, s.taskExecutor, s.clock, ).(*taskProcessorImpl) } func (s *taskProcessorSuite) TearDownTest() { s.mockShard.Finish(s.T()) goleak.VerifyNone(s.T()) } func (s *taskProcessorSuite) TestStartStop() { s.taskProcessor.Start() s.taskProcessor.Stop() } func (s *taskProcessorSuite) TestProcessResponse_NoTask() { response := &types.ReplicationMessages{ LastRetrievedMessageID: 100, } s.taskProcessor.processResponse(response) s.Equal(int64(100), s.taskProcessor.lastProcessedMessageID) s.Equal(int64(100), s.taskProcessor.lastRetrievedMessageID) } func (s *taskProcessorSuite) TestProcessorLoop_RequestChanPopulated() { // start the process loop so it poppulates requestChan s.taskProcessor.wg.Add(1) go s.taskProcessor.processorLoop() // wait a bit and terminate the loop time.Sleep(50 * time.Millisecond) close(s.taskProcessor.done) // check the request requestMessage := <-s.requestChan s.Equal(int32(0), requestMessage.token.GetShardID()) s.Equal(int64(-1), requestMessage.token.GetLastProcessedMessageID()) s.Equal(int64(-1), requestMessage.token.GetLastRetrievedMessageID()) s.NotNil(requestMessage.respChan) } func (s *taskProcessorSuite) TestProcessorLoop_RespChanClosed() { // start the process loop s.taskProcessor.wg.Add(1) go s.taskProcessor.processorLoop() defer close(s.taskProcessor.done) // act like taskFetcher here and populate respChan of the request requestMessage := <-s.requestChan close(requestMessage.respChan) // loop should have continued by now. validate by checking the new request select { case <-s.requestChan: // expected case <-time.After(50 * time.Millisecond): s.Fail("new request not sent to requestChan") } } func (s *taskProcessorSuite) TestProcessorLoop_TaskExecuteSuccess() { // taskExecutor will fail to execute the task // returning a non-retriable task to keep mocking simpler s.taskExecutor.EXPECT().execute(gomock.Any(), false).Return(metrics.ScopeIdx(0), nil).Times(1) // domain name will be fetched s.mockDomainCache.EXPECT().GetDomainName(testDomainID).Return(testDomainName, nil).AnyTimes() // start the process loop s.taskProcessor.wg.Add(1) go s.taskProcessor.processorLoop() // act like taskFetcher here and populate respChan of the request requestMessage := <-s.requestChan requestMessage.respChan <- &types.ReplicationMessages{ LastRetrievedMessageID: 100, ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, ScheduledID: testScheduleID, }, SourceTaskID: testTaskID, }, }, } // wait a bit and terminate the loop time.Sleep(50 * time.Millisecond) close(s.taskProcessor.done) } func (s *taskProcessorSuite) TestProcessorLoop_TaskExecuteFailed_PutDLQSuccess() { // taskExecutor will fail to execute the task // returning a non-retriable task to keep mocking simpler s.taskExecutor.EXPECT().execute(gomock.Any(), false).Return(metrics.ScopeIdx(0), &types.BadRequestError{}).Times(1) // domain name will be fetched s.mockDomainCache.EXPECT().GetDomainName(testDomainID).Return(testDomainName, nil).AnyTimes() // task will be put into dlq dlqReq := &persistence.PutReplicationTaskToDLQRequest{ SourceClusterName: "standby", // TODO move to a constant TaskInfo: &persistence.ReplicationTaskInfo{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, TaskID: testTaskID, TaskType: persistence.ReplicationTaskTypeSyncActivity, ScheduledID: testScheduleID, }, DomainName: testDomainName, } s.mockShard.Resource.ExecutionMgr.On("PutReplicationTaskToDLQ", mock.Anything, dlqReq).Return(nil).Times(1) // start the process loop s.taskProcessor.wg.Add(1) go s.taskProcessor.processorLoop() // act like taskFetcher here and populate respChan of the request requestMessage := <-s.requestChan requestMessage.respChan <- &types.ReplicationMessages{ LastRetrievedMessageID: 100, ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, ScheduledID: testScheduleID, }, SourceTaskID: testTaskID, }, }, } // wait a bit and terminate the loop time.Sleep(50 * time.Millisecond) close(s.taskProcessor.done) } func (s *taskProcessorSuite) TestProcessorLoop_TaskExecuteFailed_PutDLQFailed() { // taskExecutor will fail to execute the task // returning a non-retriable task to keep mocking simpler s.taskExecutor.EXPECT().execute(gomock.Any(), false).Return(metrics.ScopeIdx(0), &types.BadRequestError{}).Times(1) // domain name will be fetched s.mockDomainCache.EXPECT().GetDomainName(testDomainID).Return(testDomainName, nil).AnyTimes() // task will be put into dlq and will fail. It will be attempted 3 times. (first call + 2 retries based on policy overriden below) dqlRetryPolicy := backoff.NewExponentialRetryPolicy(time.Millisecond) dqlRetryPolicy.SetMaximumAttempts(2) s.taskProcessor.dlqRetryPolicy = dqlRetryPolicy dlqReq := &persistence.PutReplicationTaskToDLQRequest{ SourceClusterName: "standby", // TODO move to a constant TaskInfo: &persistence.ReplicationTaskInfo{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, TaskID: testTaskID, TaskType: persistence.ReplicationTaskTypeSyncActivity, ScheduledID: testScheduleID, }, DomainName: testDomainName, } s.mockShard.Resource.ExecutionMgr. On("PutReplicationTaskToDLQ", mock.Anything, dlqReq). Return(errors.New("failed to put to dlq")). Times(3) // start the process loop s.taskProcessor.wg.Add(1) go s.taskProcessor.processorLoop() // act like taskFetcher here and populate respChan of the request requestMessage := <-s.requestChan requestMessage.respChan <- &types.ReplicationMessages{ LastRetrievedMessageID: 100, ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, ScheduledID: testScheduleID, }, SourceTaskID: testTaskID, }, }, } // wait a bit and terminate the loop time.Sleep(50 * time.Millisecond) close(s.taskProcessor.done) } func (s *taskProcessorSuite) TestHandleSyncShardStatus() { now := time.Now() s.mockEngine.EXPECT().SyncShardStatus(gomock.Any(), &types.SyncShardStatusRequest{ SourceCluster: "standby", ShardID: 0, Timestamp: common.Int64Ptr(now.UnixNano()), }).Return(nil).Times(1) err := s.taskProcessor.handleSyncShardStatus(&types.SyncShardStatus{ Timestamp: common.Int64Ptr(now.UnixNano()), }) s.NoError(err) } func (s *taskProcessorSuite) TestPutReplicationTaskToDLQ_SyncActivityReplicationTask() { request := &persistence.PutReplicationTaskToDLQRequest{ SourceClusterName: "standby", TaskInfo: &persistence.ReplicationTaskInfo{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), TaskType: persistence.ReplicationTaskTypeSyncActivity, }, DomainName: uuid.New(), } s.executionManager.On("PutReplicationTaskToDLQ", mock.Anything, request).Return(nil) err := s.taskProcessor.putReplicationTaskToDLQ(request) s.NoError(err) } func (s *taskProcessorSuite) TestPutReplicationTaskToDLQ_HistoryV2ReplicationTask() { request := &persistence.PutReplicationTaskToDLQRequest{ SourceClusterName: "standby", TaskInfo: &persistence.ReplicationTaskInfo{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), TaskType: persistence.ReplicationTaskTypeHistory, FirstEventID: 1, NextEventID: 2, Version: 1, }, DomainName: uuid.New(), } s.executionManager.On("PutReplicationTaskToDLQ", mock.Anything, request).Return(nil) err := s.taskProcessor.putReplicationTaskToDLQ(request) s.NoError(err) } func (s *taskProcessorSuite) TestGenerateDLQRequest_ReplicationTaskTypeHistoryV2() { domainID := uuid.New() workflowID := uuid.New() runID := uuid.New() events := []*types.HistoryEvent{ { ID: 1, Version: 1, }, } serializer := s.mockShard.GetPayloadSerializer() data, err := serializer.SerializeBatchEvents(events, constants.EncodingTypeThriftRW) s.NoError(err) task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Events: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: data.Data, }, }, } s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test_domain_name", nil).AnyTimes() request, err := s.taskProcessor.generateDLQRequest(task) s.NoError(err) s.Equal("standby", request.SourceClusterName) s.Equal(int64(1), request.TaskInfo.FirstEventID) s.Equal(int64(2), request.TaskInfo.NextEventID) s.Equal(int64(1), request.TaskInfo.GetVersion()) s.Equal(domainID, request.TaskInfo.GetDomainID()) s.Equal(workflowID, request.TaskInfo.GetWorkflowID()) s.Equal(runID, request.TaskInfo.GetRunID()) s.Equal(persistence.ReplicationTaskTypeHistory, request.TaskInfo.GetTaskType()) } func (s *taskProcessorSuite) TestGenerateDLQRequest_ReplicationTaskTypeSyncActivity() { domainID := uuid.New() workflowID := uuid.New() runID := uuid.New() domainName := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, ScheduledID: 1, }, } s.mockDomainCache.EXPECT().GetDomainName(domainID).Return(domainName, nil).AnyTimes() request, err := s.taskProcessor.generateDLQRequest(task) s.NoError(err) s.Equal("standby", request.SourceClusterName) s.Equal(int64(1), request.TaskInfo.ScheduledID) s.Equal(domainID, request.TaskInfo.GetDomainID()) s.Equal(workflowID, request.TaskInfo.GetWorkflowID()) s.Equal(runID, request.TaskInfo.GetRunID()) s.Equal(persistence.ReplicationTaskTypeSyncActivity, request.TaskInfo.GetTaskType()) } func (s *taskProcessorSuite) TestGenerateDLQRequest_InvalidTaskType() { domainID := uuid.New() workflowID := uuid.New() runID := uuid.New() events := []*types.HistoryEvent{ { ID: 1, Version: 1, }, } serializer := s.mockShard.GetPayloadSerializer() data, err := serializer.SerializeBatchEvents(events, constants.EncodingTypeThriftRW) s.NoError(err) taskType := types.ReplicationTaskType(-1) task := &types.ReplicationTask{ TaskType: &taskType, HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, Events: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: data.Data, }, }, } _, err = s.taskProcessor.generateDLQRequest(task) s.ErrorContains(err, "unknown replication task type") } func (s *taskProcessorSuite) TestTriggerDataInconsistencyScan_Success() { domainID := uuid.New() workflowID := uuid.New() runID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, ScheduledID: 1, Version: 100, }, } fixExecution := entity.Execution{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, ShardID: s.mockShard.GetShardID(), } jsArray, err := json.Marshal(fixExecution) s.NoError(err) s.mockFrontendClient.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, request *types.SignalWithStartWorkflowExecutionRequest, option ...yarpc.CallOption) { s.Equal(constants.SystemLocalDomainName, request.GetDomain()) s.Equal(reconciliation.CheckDataCorruptionWorkflowID, request.GetWorkflowID()) s.Equal(reconciliation.CheckDataCorruptionWorkflowType, request.GetWorkflowType().GetName()) s.Equal(reconciliation.CheckDataCorruptionWorkflowTaskList, request.GetTaskList().GetName()) s.Equal(types.WorkflowIDReusePolicyAllowDuplicate.String(), request.GetWorkflowIDReusePolicy().String()) s.Equal(reconciliation.CheckDataCorruptionWorkflowSignalName, request.GetSignalName()) s.Equal(jsArray, request.GetSignalInput()) }).Return(&types.StartWorkflowExecutionResponse{}, nil) err = s.taskProcessor.triggerDataInconsistencyScan(task) s.NoError(err) } func (s *taskProcessorSuite) TestCleanupReplicationTaskLoop() { req := &persistence.RangeCompleteHistoryTaskRequest{ // this is min ack level of remote clusters. there's only one remote cluster in this test "standby". // its replication ack level is set to 350 in SetupTest(), and since the max key is exclusive, set the task id to 351 TaskCategory: persistence.HistoryTaskCategoryReplication, ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(351), PageSize: 50, // this comes from test config } s.executionManager.On("RangeCompleteHistoryTask", mock.Anything, req).Return(&persistence.RangeCompleteHistoryTaskResponse{ TasksCompleted: 50, // if this number equals to page size the loop continues }, nil).Times(1) s.executionManager.On("RangeCompleteHistoryTask", mock.Anything, req).Return(&persistence.RangeCompleteHistoryTaskResponse{ TasksCompleted: 15, // if this number is different than page size the loop breaks }, nil) // start the cleanup loop s.taskProcessor.wg.Add(1) go s.taskProcessor.cleanupReplicationTaskLoop() // wait a bit and terminate the loop time.Sleep(50 * time.Millisecond) close(s.taskProcessor.done) // wait until goroutine terminates s.taskProcessor.wg.Wait() } func (s *taskProcessorSuite) TestSyncShardStatusLoop_WithoutSyncShardTask() { // start the sync shard loop s.taskProcessor.wg.Add(1) go s.taskProcessor.syncShardStatusLoop() // wait a bit and terminate the loop time.Sleep(50 * time.Millisecond) close(s.taskProcessor.done) // wait until goroutine terminates s.taskProcessor.wg.Wait() } func (s *taskProcessorSuite) TestSyncShardStatusLoop_WithSyncShardTask() { now := time.Now() s.taskProcessor.syncShardChan <- &types.SyncShardStatus{ Timestamp: common.Int64Ptr(now.UnixNano()), } s.mockEngine.EXPECT().SyncShardStatus(gomock.Any(), &types.SyncShardStatusRequest{ SourceCluster: "standby", ShardID: 0, Timestamp: common.Int64Ptr(now.UnixNano()), }).DoAndReturn(func(ctx context.Context, request *types.SyncShardStatusRequest) error { close(s.taskProcessor.done) return nil }).Times(1) // start the sync shard loop s.taskProcessor.wg.Add(1) go s.taskProcessor.syncShardStatusLoop() // wait until goroutine terminates s.taskProcessor.wg.Wait() } func (s *taskProcessorSuite) TestShouldRetryDLQ() { s.False(s.taskProcessor.shouldRetryDLQ(nil)) s.True(s.taskProcessor.shouldRetryDLQ(&types.InternalServiceError{})) s.True(s.taskProcessor.shouldRetryDLQ(fmt.Errorf("error before done channel closed should be retried"))) close(s.taskProcessor.done) s.False(s.taskProcessor.shouldRetryDLQ(&types.ServiceBusyError{})) s.False(s.taskProcessor.shouldRetryDLQ(fmt.Errorf("error after done channel closed should NOT be retried"))) } func TestProcessorLoop_TaskExecuteFailed_ShardChangeErr(t *testing.T) { ctrl := gomock.NewController(t) config := config.NewForTest() mockShard := shard.NewTestContext( t, ctrl, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, ClusterReplicationLevel: map[string]int64{cluster.TestAlternativeClusterName: 350}, }, config, ) mockDomainCache := mockShard.Resource.DomainCache requestChan := make(chan *request, 10) taskFetcher := &fakeTaskFetcher{ sourceCluster: "standby", requestChan: requestChan, // ensure that the fetcher always nearly-immediately fetches rateLimiter: quotas.NewDynamicRateLimiter(func() float64 { return 100000 }), } mockEngine := engine.NewMockEngine(ctrl) metricsClient := metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) taskExecutor := NewMockTaskExecutor(ctrl) taskProcessor := NewTaskProcessor( mockShard, mockEngine, config, metricsClient, taskFetcher, taskExecutor, clock.NewMockedTimeSource(), ).(*taskProcessorImpl) // start the process loop taskProcessor.wg.Add(1) go taskProcessor.processorLoop() taskExecutor.EXPECT().execute(gomock.Any(), false). DoAndReturn(func(*types.ReplicationTask, bool) (any, any) { // take a minute like a real RPC call, enough time for it probably be doing it's thing // then to fail, return the shard error (as if the host is closing down) and then // trigger the Stop function as if the shard is properly closing. time.Sleep(time.Millisecond * 50) return 0, &persistence.ShardOwnershipLostError{Msg: "some shard err"} }).AnyTimes() // domain name will be fetched mockDomainCache.EXPECT().GetDomainName(testDomainID).Return(testDomainName, nil).AnyTimes() // act like taskFetcher here and populate respChan of the request requestMessage := <-requestChan requestMessage.respChan <- &types.ReplicationMessages{ LastRetrievedMessageID: int64(105), ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeSyncActivity.Ptr(), SyncActivityTaskAttributes: &types.SyncActivityTaskAttributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, ScheduledID: testScheduleID, }, SourceTaskID: testTaskID, }, }, } if len(taskProcessor.requestChan) != 0 { t.Error("there shoudn't have been any data sent") } time.Sleep(50 * time.Millisecond) close(taskProcessor.done) // wait a bit and terminate the loop time.Sleep(50 * time.Millisecond) taskProcessor.wg.Wait() // this is a rather complicated test, and this is the main assertion: // that *if* there's some shutdown logic thats going on, and in addition to that // there's replication tasks that can't be processed because there's shard stealing // going on, then we should expect that these in-memory offsets aren't changed and, // more importantly, aren't sent until they're successfully processed by a shard. assert.Equal(t, int64(-1), taskProcessor.lastProcessedMessageID) assert.Equal(t, int64(-1), taskProcessor.lastRetrievedMessageID) } func TestIsShuttingDown(t *testing.T) { taskProcessor := taskProcessorImpl{ done: make(chan struct{}), } assert.False(t, taskProcessor.isShuttingDown()) close(taskProcessor.done) assert.True(t, taskProcessor.isShuttingDown()) } func (s *taskProcessorSuite) TestProcessTaskOnce_OverThreshold_WithNoopLogger() { s.taskExecutor.EXPECT().execute(gomock.Any(), false).Return(metrics.ScopeIdx(0), nil).Times(1) // Domain cache is called for tagging s.mockDomainCache.EXPECT().GetDomainName(testDomainID).Return(testDomainName, nil).AnyTimes() // Use noop logger (no capturing, just to exercise the path) s.taskProcessor.logger = log.NewNoop() // Make e2e latency > threshold (e.g., 26 minutes) creation := time.Now().Add(-26 * time.Minute).UnixNano() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }, CreationTime: common.Int64Ptr(creation), } err := s.taskProcessor.processTaskOnce(task) s.NoError(err) } ================================================ FILE: service/history/replication/task_reader.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2022 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "context" "github.com/uber/cadence/common/persistence" ) // TaskReader will read replication tasks from database type TaskReader struct { shardID int executionManager persistence.ExecutionManager } // NewTaskReader creates new TaskReader func NewTaskReader(shardID int, executionManager persistence.ExecutionManager) *TaskReader { return &TaskReader{ shardID: shardID, executionManager: executionManager, } } // Read reads and returns replications tasks from readLevel to maxReadLevel func (r *TaskReader) Read(ctx context.Context, readLevel int64, maxReadLevel int64, batchSize int) ([]persistence.Task, bool, error) { // Check if it is even possible to return any results. // If not return early with empty response. Do not hit persistence. if readLevel >= maxReadLevel { return nil, false, nil } response, err := r.executionManager.GetHistoryTasks(ctx, &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(readLevel + 1), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(maxReadLevel + 1), PageSize: batchSize, }) if err != nil { return nil, false, err } hasMore := response.NextPageToken != nil return response.Tasks, hasMore, nil } ================================================ FILE: service/history/replication/task_reader_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2022 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" ) const ( testBatchSize = 50 ) var ( testTime = time.Now() testReplicationTasks = []persistence.Task{ &persistence.HistoryReplicationTask{TaskData: persistence.TaskData{TaskID: 50, VisibilityTimestamp: testTime.Add(-1 * time.Second)}}, &persistence.HistoryReplicationTask{TaskData: persistence.TaskData{TaskID: 51, VisibilityTimestamp: testTime.Add(-2 * time.Second)}}, } ) func TestTaskReader(t *testing.T) { tests := []struct { name string prepareExecutions func(m *persistence.MockExecutionManager) readLevel int64 maxReadLevel int64 expectResponse []persistence.Task expectErr string }{ { name: "read replication tasks - first read will use default batch size", readLevel: 50, maxReadLevel: 100, prepareExecutions: func(m *persistence.MockExecutionManager) { m.EXPECT().GetHistoryTasks(gomock.Any(), &persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryReplication, InclusiveMinTaskKey: persistence.NewImmediateTaskKey(51), ExclusiveMaxTaskKey: persistence.NewImmediateTaskKey(101), PageSize: testBatchSize, }).Return(&persistence.GetHistoryTasksResponse{Tasks: testReplicationTasks}, nil) }, expectResponse: testReplicationTasks, }, { name: "do not hit persistence when no task will be returned", readLevel: 50, maxReadLevel: 50, prepareExecutions: func(m *persistence.MockExecutionManager) { m.EXPECT().GetHistoryTasks(gomock.Any(), gomock.Any()).Times(0) }, expectResponse: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) em := persistence.NewMockExecutionManager(ctrl) tt.prepareExecutions(em) reader := NewTaskReader(testShardID, em) response, _, err := reader.Read(context.Background(), tt.readLevel, tt.maxReadLevel, testBatchSize) if tt.expectErr != "" { assert.EqualError(t, err, tt.expectErr) } else { require.NoError(t, err) assert.Equal(t, tt.expectResponse, response) } }) } } ================================================ FILE: service/history/replication/task_store.go ================================================ // The MIT License (MIT) // // Copyright (c) 2022 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "context" "errors" "fmt" "time" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" ) // ErrUnknownCluster is returned when given cluster is not defined in cluster metadata var ErrUnknownCluster = errors.New("unknown cluster") // TaskStore is a component that hydrates and caches replication messages so that they can be reused across several polling source clusters. // It also exposes public Put method. This allows pre-store already hydrated messages at the end of successful transaction, saving a DB call to fetch history events. // // TaskStore uses a separate cache per each source cluster allowing messages to be fetched at different rates. // Once a cache becomes full it will not accept further messages for that cluster. Later those messages be fetched from DB and hydrated again. // A cache stores only a pointer to the message. It is hydrates once and shared across caches. Cluster acknowledging the message will remove it from that corresponding cache. // Once all clusters acknowledge it, no more references will be held, and GC will eventually pick it up. type TaskStore struct { clusters map[string]cache.AckCache[*types.ReplicationTask] domains domainCache hydrator taskHydrator rateLimiter quotas.Limiter throttleRetry *backoff.ThrottleRetry timeSource clock.TimeSource scope metrics.Scope logger log.Logger lastLogTime time.Time } type ( domainCache interface { GetDomainByID(id string) (*cache.DomainCacheEntry, error) } taskHydrator interface { Hydrate(ctx context.Context, task persistence.Task) (*types.ReplicationTask, error) } ) // NewTaskStore create new instance of TaskStore func NewTaskStore( config *config.Config, clusterMetadata cluster.Metadata, domains domainCache, metricsClient metrics.Client, logger log.Logger, hydrator taskHydrator, budgetManager cache.Manager, shardID int, timeSource clock.TimeSource, ) *TaskStore { cacheName := fmt.Sprintf("replication-cache-%d", shardID) clusters := map[string]cache.AckCache[*types.ReplicationTask]{} for clusterName := range clusterMetadata.GetRemoteClusterInfo() { clusters[clusterName] = cache.NewBoundedAckCache[*types.ReplicationTask](config.ReplicatorCacheCapacity, config.ReplicatorCacheMaxSize, logger, budgetManager, cacheName) } retryPolicy := backoff.NewExponentialRetryPolicy(100 * time.Millisecond) retryPolicy.SetMaximumAttempts(config.ReplicatorReadTaskMaxRetryCount()) retryPolicy.SetBackoffCoefficient(1) return &TaskStore{ clusters: clusters, domains: domains, hydrator: hydrator, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(persistence.IsTransientError), ), scope: metricsClient.Scope(metrics.ReplicatorCacheManagerScope), logger: logger.WithTags(tag.ComponentReplicationCacheManager), rateLimiter: quotas.NewDynamicRateLimiter(config.ReplicationTaskGenerationQPS.AsFloat64()), timeSource: timeSource, } } // Get will return a hydrated replication message for a given cluster based on raw task info. // It will either return it immediately from cache or hydrate it, store in cache and then return. // // Returned task may be nil. This may be due domain not existing in a given cluster or replication message is not longer relevant. // Either case is valid and such replication message should be ignored and not returned in the response. func (m *TaskStore) Get(ctx context.Context, cluster string, info persistence.Task) (*types.ReplicationTask, error) { cacheForTargetCluster, ok := m.clusters[cluster] if !ok { return nil, ErrUnknownCluster } domain, err := m.domains.GetDomainByID(info.GetDomainID()) if err != nil { return nil, fmt.Errorf("resolving domain: %w", err) } // Domain does not exist in this cluster, do not replicate the task if !domain.HasReplicationCluster(cluster) { return nil, nil } scope := m.scope.Tagged(metrics.SourceClusterTag(cluster)) scope.IncCounter(metrics.CacheRequests) // Keep timer (backwards compatible), dual-emit exponential histogram for migration. cacheLatencyStart := m.timeSource.Now() sw := scope.StartTimer(metrics.CacheLatency) defer sw.Stop() defer func() { scope.ExponentialHistogram(metrics.ExponentialCacheLatency, m.timeSource.Since(cacheLatencyStart)) }() task := cacheForTargetCluster.Get(info.GetTaskID()) if task != nil { scope.IncCounter(metrics.CacheHitCounter) return task, nil } m.scope.IncCounter(metrics.CacheMissCounter) // Rate limit to not kill the database m.rateLimiter.Wait(ctx) op := func(ctx context.Context) error { var err error task, err = m.hydrator.Hydrate(ctx, info) return err } err = m.throttleRetry.Do(ctx, op) if err != nil { m.scope.IncCounter(metrics.CacheFailures) return nil, err } m.Put(task) return task, nil } // Put will try to store hydrated replication to all cluster caches. // Tasks may not be relevant, as domain is not enabled in some clusters. Ignore task for that cluster. // Some clusters may already have full cache. Ignore the task, it will be fetched and hydrated again later. // Some clusters may have already acknowledged such task. Ignore task, it is no longer relevant for such cluster. func (m *TaskStore) Put(task *types.ReplicationTask) { // Do not store nil tasks if task == nil { return } domain, err := m.getDomain(task) if err != nil { m.logger.Error("failed to resolve domain", tag.Error(err)) return } for targetCluster, cacheByCluster := range m.clusters { if domain != nil && !domain.HasReplicationCluster(targetCluster) { continue } scope := m.scope.Tagged(metrics.SourceClusterTag(targetCluster)) err = cacheByCluster.Put(task, task.ByteSize()) switch { case errors.Is(err, cache.ErrAckCacheFull): scope.IncCounter(metrics.CacheFullCounter) // This will help debug which shard is full. Logger already has ShardID tag attached. // Log only once a minute to not flood the logs. if m.timeSource.Since(m.lastLogTime) > time.Minute { m.logger.Warn("Replication cache is full") m.lastLogTime = m.timeSource.Now() } case errors.Is(err, cache.ErrAlreadyAcked): // No action, this is expected. // Some cluster(s) may be already past this, due to different fetch rates. } count := cacheByCluster.Count() scope.RecordTimer(metrics.CacheSize, time.Duration(count)) scope.RecordHistogramValue(metrics.CacheSizeHistogram, float64(count)) } } // Ack will acknowledge replication message for a given cluster. // This will result in all messages removed from the cache up to a given lastTaskID. func (m *TaskStore) Ack(cluster string, lastTaskID int64) error { cache, ok := m.clusters[cluster] if !ok { return ErrUnknownCluster } _, _ = cache.Ack(lastTaskID) scope := m.scope.Tagged(metrics.SourceClusterTag(cluster)) count := cache.Count() scope.RecordTimer(metrics.CacheSize, time.Duration(count)) scope.RecordHistogramValue(metrics.CacheSizeHistogram, float64(count)) return nil } func (m *TaskStore) getDomain(task *types.ReplicationTask) (*cache.DomainCacheEntry, error) { if domainID := task.GetHistoryTaskV2Attributes().GetDomainID(); domainID != "" { return m.domains.GetDomainByID(domainID) } if domainID := task.GetSyncActivityTaskAttributes().GetDomainID(); domainID != "" { return m.domains.GetDomainByID(domainID) } if domainID := task.GetFailoverMarkerAttributes().GetDomainID(); domainID != "" { return m.domains.GetDomainByID(domainID) } return nil, nil } ================================================ FILE: service/history/replication/task_store_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2022 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replication import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" hconfig "github.com/uber/cadence/service/history/config" ) func TestTaskStore(t *testing.T) { ctx := context.Background() t.Run("Get error on unknown cluster", func(t *testing.T) { ts := createTestTaskStore(t, nil, nil) _, err := ts.Get(ctx, "unknown cluster", &testTask11) assert.Equal(t, ErrUnknownCluster, err) }) t.Run("Get error resolving domain", func(t *testing.T) { ts := createTestTaskStore(t, fakeDomainCache{}, nil) _, err := ts.Get(ctx, testClusterA, &testTask11) assert.EqualError(t, err, "resolving domain: domain does not exist") }) t.Run("Get skips task for domains non belonging to polling cluster", func(t *testing.T) { ts := createTestTaskStore(t, fakeDomainCache{testDomainID: testDomain}, nil) task, err := ts.Get(ctx, testClusterB, &testTask11) assert.NoError(t, err) assert.Nil(t, task) }) t.Run("Get returns cached replication task", func(t *testing.T) { ts := createTestTaskStore(t, fakeDomainCache{testDomainID: testDomain}, nil) ts.Put(&testHydratedTask11) task, err := ts.Get(ctx, testClusterA, &testTask11) assert.NoError(t, err) assert.Equal(t, &testHydratedTask11, task) }) t.Run("Get returns non-cached replication task by hydrating it", func(t *testing.T) { ts := createTestTaskStore(t, fakeDomainCache{testDomainID: testDomain}, fakeTaskHydrator{testTask11.TaskID: testHydratedTask11}) task, err := ts.Get(ctx, testClusterA, &testTask11) assert.NoError(t, err) assert.Equal(t, &testHydratedTask11, task) }) t.Run("Get fails to hydrate replication task", func(t *testing.T) { ts := createTestTaskStore(t, fakeDomainCache{testDomainID: testDomain}, fakeTaskHydrator{testTask11.TaskID: testHydratedTaskErrorNonRecoverable}) task, err := ts.Get(ctx, testClusterA, &testTask11) assert.EqualError(t, err, "error hydrating task") assert.Nil(t, task) }) t.Run("Put does not store nil task", func(t *testing.T) { ts := createTestTaskStore(t, nil, nil) ts.Put(nil) for _, cache := range ts.clusters { assert.Zero(t, cache.Size()) } }) t.Run("Put error resolving domain - does not store task", func(t *testing.T) { ts := createTestTaskStore(t, fakeDomainCache{}, nil) ts.Put(&testHydratedTask11) ts.Put(&testHydratedTask12) ts.Put(&testHydratedTask14) for _, cache := range ts.clusters { assert.Zero(t, cache.Size()) } }) t.Run("Put hydrated task into appropriate cache", func(t *testing.T) { ts := createTestTaskStore(t, fakeDomainCache{testDomainID: testDomain}, nil) ts.Put(&testHydratedTask11) for _, cluster := range testDomain.GetReplicationConfig().Clusters { assert.Equal(t, 1, ts.clusters[cluster.ClusterName].Count()) } }) t.Run("Put hydrated task without domain info - will put it to all caches", func(t *testing.T) { ts := createTestTaskStore(t, fakeDomainCache{testDomainID: testDomain}, nil) ts.Put(&types.ReplicationTask{SourceTaskID: 123}) for _, cluster := range testDomain.GetReplicationConfig().Clusters { assert.Equal(t, 1, ts.clusters[cluster.ClusterName].Count()) } }) t.Run("Put full cache error", func(t *testing.T) { ts := createTestTaskStore(t, fakeDomainCache{testDomainID: testDomain}, nil) ts.Put(&testHydratedTask11) ts.Put(&testHydratedTask12) ts.Put(&testHydratedTask14) for _, cluster := range testDomain.GetReplicationConfig().Clusters { assert.Equal(t, 3, ts.clusters[cluster.ClusterName].Count()) } }) t.Run("Put will not store acked task", func(t *testing.T) { ts := createTestTaskStore(t, fakeDomainCache{testDomainID: testDomain}, nil) for _, cluster := range testDomain.GetReplicationConfig().Clusters { ts.Ack(cluster.ClusterName, testHydratedTask11.SourceTaskID) ts.Put(&testHydratedTask11) assert.Equal(t, 0, ts.clusters[cluster.ClusterName].Count()) } }) t.Run("Ack error on unknown cluster", func(t *testing.T) { ts := createTestTaskStore(t, nil, nil) err := ts.Ack("unknown cluster", 0) assert.Equal(t, ErrUnknownCluster, err) }) } func TestTaskStoreWithBudgetManager(t *testing.T) { ctx := context.Background() t.Run("Budget manager enforces capacity limits", func(t *testing.T) { maxCount := 5 budgetManager := cache.NewBudgetManager( "test-replication-cache", dynamicproperties.GetIntPropertyFn(1000), dynamicproperties.GetIntPropertyFn(maxCount), cache.AdmissionOptimistic, 0, metrics.NewNoopMetricsClient().Scope(metrics.ReplicatorCacheManagerScope), testlogger.New(t), dynamicproperties.GetFloatPropertyFn(1.0), ) defer budgetManager.Stop() ts := createTestTaskStoreWithBudgetManager(t, fakeDomainCache{testDomainID: testDomain}, nil, budgetManager, 1) for i := int64(1); i <= 10; i++ { task := &types.ReplicationTask{ SourceTaskID: i, HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{DomainID: testDomainID}, } ts.Put(task) } totalCached := 0 for _, cluster := range testDomain.GetReplicationConfig().Clusters { totalCached += ts.clusters[cluster.ClusterName].Count() } assert.Equal(t, int64(totalCached), budgetManager.UsedCount()) assert.Equal(t, int64(maxCount), budgetManager.UsedCount()) }) t.Run("Nil budget manager works without errors", func(t *testing.T) { ts := createTestTaskStoreWithBudgetManager(t, fakeDomainCache{testDomainID: testDomain}, nil, nil, 2) ts.Put(&testHydratedTask11) task, err := ts.Get(ctx, testClusterA, &testTask11) assert.NoError(t, err) assert.Equal(t, &testHydratedTask11, task) }) t.Run("Multiple shards get separate cache IDs", func(t *testing.T) { budgetManager := cache.NewBudgetManager( "test-replication-cache", dynamicproperties.GetIntPropertyFn(2000), dynamicproperties.GetIntPropertyFn(100), cache.AdmissionOptimistic, 0, metrics.NewNoopMetricsClient().Scope(metrics.ReplicatorCacheManagerScope), testlogger.New(t), dynamicproperties.GetFloatPropertyFn(1.0), ) defer budgetManager.Stop() ts1 := createTestTaskStoreWithBudgetManager(t, fakeDomainCache{testDomainID: testDomain}, nil, budgetManager, 1) ts2 := createTestTaskStoreWithBudgetManager(t, fakeDomainCache{testDomainID: testDomain}, nil, budgetManager, 2) ts1.Put(&testHydratedTask11) ts2.Put(&testHydratedTask12) for _, cluster := range testDomain.GetReplicationConfig().Clusters { assert.Equal(t, 1, ts1.clusters[cluster.ClusterName].Count()) assert.Equal(t, 1, ts2.clusters[cluster.ClusterName].Count()) } }) } func createTestTaskStore(t *testing.T, domains domainCache, hydrator taskHydrator) *TaskStore { return createTestTaskStoreWithBudgetManager(t, domains, hydrator, nil, 0) } func createTestTaskStoreWithBudgetManager(t *testing.T, domains domainCache, hydrator taskHydrator, budgetManager cache.Manager, shardID int) *TaskStore { cfg := hconfig.Config{ ReplicatorCacheCapacity: dynamicproperties.GetIntPropertyFn(100), ReplicationTaskGenerationQPS: dynamicproperties.GetFloatPropertyFn(0), ReplicatorReadTaskMaxRetryCount: dynamicproperties.GetIntPropertyFn(1), ReplicatorCacheMaxSize: dynamicproperties.GetIntPropertyFn(20000), } clusterMetadata := cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 0, PrimaryClusterName: testClusterC, CurrentClusterName: testClusterC, ClusterGroup: map[string]config.ClusterInformation{ testClusterA: {Enabled: true}, testClusterB: {Enabled: true}, testClusterC: {Enabled: true}, }, }, func(d string) bool { return false }, metrics.NewNoopMetricsClient(), testlogger.New(t), ) return NewTaskStore( &cfg, clusterMetadata, domains, metrics.NewNoopMetricsClient(), log.NewNoop(), hydrator, budgetManager, shardID, clock.NewRealTimeSource(), ) } type fakeDomainCache map[string]*cache.DomainCacheEntry func (cache fakeDomainCache) GetDomainByID(id string) (*cache.DomainCacheEntry, error) { if entry, ok := cache[id]; ok { return entry, nil } return nil, types.EntityNotExistsError{Message: "domain does not exist"} } ================================================ FILE: service/history/reset/resetter.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination resetter_mock.go package reset import ( "context" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" persistenceutils "github.com/uber/cadence/common/persistence/persistence-utils" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) type ( // WorkflowResetter is the new NDC compatible workflow reset component WorkflowResetter interface { ResetWorkflow( ctx context.Context, domainID string, workflowID string, baseRunID string, baseBranchToken []byte, baseRebuildLastEventID int64, baseRebuildLastEventVersion int64, baseNextEventID int64, resetRunID string, resetRequestID string, currentWorkflow execution.Workflow, resetReason string, additionalReapplyEvents []*types.HistoryEvent, skipSignalReapply bool, ) error } workflowResetterImpl struct { shard shard.Context domainCache cache.DomainCache clusterMetadata cluster.Metadata activeClusterManager activecluster.Manager historyV2Mgr persistence.HistoryManager executionCache execution.Cache newStateRebuilder nDCStateRebuilderProvider logger log.Logger } nDCStateRebuilderProvider func() execution.StateRebuilder ) var _ WorkflowResetter = (*workflowResetterImpl)(nil) // NewWorkflowResetter creates a workflow resetter func NewWorkflowResetter( shard shard.Context, executionCache execution.Cache, logger log.Logger, ) WorkflowResetter { return &workflowResetterImpl{ shard: shard, domainCache: shard.GetDomainCache(), clusterMetadata: shard.GetClusterMetadata(), activeClusterManager: shard.GetActiveClusterManager(), historyV2Mgr: shard.GetHistoryManager(), executionCache: executionCache, newStateRebuilder: func() execution.StateRebuilder { return execution.NewStateRebuilder(shard, logger) }, logger: logger, } } func (r *workflowResetterImpl) ResetWorkflow( ctx context.Context, domainID string, workflowID string, baseRunID string, baseBranchToken []byte, baseRebuildLastEventID int64, baseRebuildLastEventVersion int64, baseNextEventID int64, resetRunID string, resetRequestID string, currentWorkflow execution.Workflow, resetReason string, additionalReapplyEvents []*types.HistoryEvent, skipSignalReapply bool, ) (retError error) { activeClusterSelectionPolicy := currentWorkflow.GetMutableState().GetExecutionInfo().ActiveClusterSelectionPolicy activeClusterInfo, err := r.activeClusterManager.GetActiveClusterInfoByClusterAttribute(ctx, domainID, activeClusterSelectionPolicy.GetClusterAttribute()) if err != nil { return err } resetWorkflowVersion := activeClusterInfo.FailoverVersion currentMutableState := currentWorkflow.GetMutableState() currentWorkflowTerminated := false if currentMutableState.IsWorkflowExecutionRunning() { if err := r.terminateWorkflow( currentMutableState, resetReason, ); err != nil { return err } resetWorkflowVersion = currentMutableState.GetCurrentVersion() currentWorkflowTerminated = true } resetWorkflow, err := r.prepareResetWorkflow( ctx, domainID, workflowID, baseRunID, baseBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, baseNextEventID, resetRunID, resetRequestID, resetWorkflowVersion, resetReason, additionalReapplyEvents, skipSignalReapply, ) if err != nil { return err } defer resetWorkflow.GetReleaseFn()(retError) return r.persistToDB( ctx, currentWorkflowTerminated, currentWorkflow, resetWorkflow, ) } func (r *workflowResetterImpl) prepareResetWorkflow( ctx context.Context, domainID string, workflowID string, baseRunID string, baseBranchToken []byte, baseRebuildLastEventID int64, baseRebuildLastEventVersion int64, baseNextEventID int64, resetRunID string, resetRequestID string, resetWorkflowVersion int64, resetReason string, additionalReapplyEvents []*types.HistoryEvent, skipSignalReapply bool, ) (execution.Workflow, error) { resetWorkflow, err := r.replayResetWorkflow( ctx, domainID, workflowID, baseRunID, baseBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, resetRunID, resetRequestID, ) if err != nil { return nil, err } resetMutableState := resetWorkflow.GetMutableState() baseLastEventVersion := resetMutableState.GetCurrentVersion() if baseLastEventVersion > resetWorkflowVersion { return nil, &types.InternalServiceError{ Message: "workflowResetter encounter version mismatch.", } } if err := resetMutableState.UpdateCurrentVersion( resetWorkflowVersion, false, ); err != nil { return nil, err } resetMutableState, err = r.closePendingDecisionTask( resetMutableState, baseRunID, resetRunID, baseLastEventVersion, resetReason, resetRequestID, ) if err != nil { return nil, err } if err := r.failInflightActivity(resetMutableState.GetExecutionInfo().StartTimestamp, resetMutableState, resetReason); err != nil { return nil, err } // TODO right now only signals are eligible for reapply, so we can directly skip the whole reapply process // for the sake of performance. In the future, if there are other events that need to be reapplied, remove this check // For example, we may want to re-apply activity/timer results for https://github.com/uber/cadence/issues/2934 if !skipSignalReapply { if err := r.reapplyResetAndContinueAsNewWorkflowEvents( ctx, resetMutableState, domainID, workflowID, baseRunID, baseBranchToken, baseRebuildLastEventID+1, baseNextEventID, ); err != nil { return nil, err } } // NOTE: this is reapplying events that are passing into the API that we shouldn't skip if err := r.reapplyEvents(resetMutableState, additionalReapplyEvents); err != nil { return nil, err } if err := execution.ScheduleDecision(resetMutableState); err != nil { return nil, err } return resetWorkflow, nil } func (r *workflowResetterImpl) persistToDB( ctx context.Context, currentWorkflowTerminated bool, currentWorkflow execution.Workflow, resetWorkflow execution.Workflow, ) error { if currentWorkflowTerminated { return currentWorkflow.GetContext().UpdateWorkflowExecutionWithNewAsActive( ctx, r.shard.GetTimeSource().Now(), resetWorkflow.GetContext(), resetWorkflow.GetMutableState(), ) } currentMutableState := currentWorkflow.GetMutableState() currentRunID := currentMutableState.GetExecutionInfo().RunID currentLastWriteVersion, err := currentMutableState.GetLastWriteVersion() if err != nil { return err } now := r.shard.GetTimeSource().Now() resetWorkflowSnapshot, resetWorkflowEventsSeq, err := resetWorkflow.GetMutableState().CloseTransactionAsSnapshot( now, execution.TransactionPolicyActive, ) if err != nil { return err } if len(resetWorkflowEventsSeq) != 1 { return &types.InternalServiceError{ Message: "there should be EXACTLY one batch of events for reset", } } // reset workflow with decision task failed or timed out resetWorkflowHistory, err := resetWorkflow.GetContext().PersistNonStartWorkflowBatchEvents(ctx, resetWorkflowEventsSeq[0]) if err != nil { return err } return resetWorkflow.GetContext().CreateWorkflowExecution( ctx, resetWorkflowSnapshot, resetWorkflowHistory, persistence.CreateWorkflowModeContinueAsNew, currentRunID, currentLastWriteVersion, persistence.CreateWorkflowRequestModeNew, ) } func (r *workflowResetterImpl) replayResetWorkflow( ctx context.Context, domainID string, workflowID string, baseRunID string, baseBranchToken []byte, baseRebuildLastEventID int64, baseRebuildLastEventVersion int64, resetRunID string, resetRequestID string, ) (execution.Workflow, error) { resetContext := execution.NewContext( domainID, types.WorkflowExecution{ WorkflowID: workflowID, RunID: resetRunID, }, r.shard, r.shard.GetExecutionManager(), r.logger, ) resetBranchTokenFn := func() ([]byte, error) { resetBranchToken, err := r.forkAndGenerateBranchToken( ctx, domainID, workflowID, baseBranchToken, baseRebuildLastEventID+1, resetRunID, ) return resetBranchToken, err } resetMutableState, resetHistorySize, err := r.newStateRebuilder().Rebuild( ctx, r.shard.GetTimeSource().Now(), definition.NewWorkflowIdentifier( domainID, workflowID, baseRunID, ), baseBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, definition.NewWorkflowIdentifier( domainID, workflowID, resetRunID, ), resetBranchTokenFn, resetRequestID, ) if err != nil { return nil, err } resetContext.SetHistorySize(resetHistorySize) return execution.NewWorkflow( ctx, r.clusterMetadata, resetContext, resetMutableState, execution.NoopReleaseFn, r.logger, ), nil } func (r *workflowResetterImpl) failInflightActivity( now time.Time, mutableState execution.MutableState, terminateReason string, ) error { for _, ai := range mutableState.GetPendingActivityInfos() { switch ai.StartedID { case constants.EmptyEventID: // activity not started, noop // override the activity time now ai.ScheduledTime = now ai.TimerTaskStatus = execution.TimerTaskStatusNone if err := mutableState.UpdateActivity(ai); err != nil { return err } case constants.TransientEventID: // activity is started (with retry policy) // should not encounter this case when rebuilding mutable state return &types.InternalServiceError{ Message: "workflowResetter encounter transient activity", } default: if _, err := mutableState.AddActivityTaskFailedEvent( ai.ScheduleID, ai.StartedID, &types.RespondActivityTaskFailedRequest{ Reason: common.StringPtr(terminateReason), Details: ai.Details, Identity: ai.StartedIdentity, }, ); err != nil { return err } } } return nil } func (r *workflowResetterImpl) forkAndGenerateBranchToken( ctx context.Context, domainID string, workflowID string, forkBranchToken []byte, forkNodeID int64, resetRunID string, ) ([]byte, error) { // fork a new history branch shardID := r.shard.GetShardID() domainName, err := r.domainCache.GetDomainName(domainID) if err != nil { return nil, err } resp, err := r.historyV2Mgr.ForkHistoryBranch(ctx, &persistence.ForkHistoryBranchRequest{ ForkBranchToken: forkBranchToken, ForkNodeID: forkNodeID, Info: persistence.BuildHistoryGarbageCleanupInfo(domainID, workflowID, resetRunID), ShardID: common.IntPtr(shardID), DomainName: domainName, }) if err != nil { return nil, err } return resp.NewBranchToken, nil } func (r *workflowResetterImpl) terminateWorkflow( mutableState execution.MutableState, terminateReason string, ) error { eventBatchFirstEventID := mutableState.GetNextEventID() return execution.TerminateWorkflow( mutableState, eventBatchFirstEventID, terminateReason, nil, execution.IdentityHistoryService, ) } func (r *workflowResetterImpl) reapplyResetAndContinueAsNewWorkflowEvents( ctx context.Context, resetMutableState execution.MutableState, domainID string, workflowID string, baseRunID string, baseBranchToken []byte, baseRebuildNextEventID int64, baseNextEventID int64, ) error { // TODO change this logic to fetching all workflow [baseWorkflow, currentWorkflow] // from visibility for better coverage of events eligible for re-application. var nextRunID string var err error // first special handling the remaining events for base workflow if nextRunID, err = r.reapplyWorkflowEvents( ctx, resetMutableState, baseRebuildNextEventID, baseNextEventID, baseBranchToken, ); err != nil { return err } getNextEventIDBranchToken := func(runID string) (nextEventID int64, branchToken []byte, retError error) { context, release, err := r.executionCache.GetOrCreateWorkflowExecution( ctx, domainID, types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, ) if err != nil { return 0, nil, err } defer func() { release(retError) }() mutableState, err := context.LoadWorkflowExecution(ctx) if err != nil { // no matter what error happen, we need to retry return 0, nil, err } nextEventID = mutableState.GetNextEventID() branchToken, err = mutableState.GetCurrentBranchToken() if err != nil { return 0, nil, err } return nextEventID, branchToken, nil } // second for remaining continue as new workflow, reapply eligible events for len(nextRunID) != 0 { nextWorkflowNextEventID, nextWorkflowBranchToken, err := getNextEventIDBranchToken(nextRunID) if err != nil { return err } if nextRunID, err = r.reapplyWorkflowEvents( ctx, resetMutableState, constants.FirstEventID, nextWorkflowNextEventID, nextWorkflowBranchToken, ); err != nil { return err } } return nil } func (r *workflowResetterImpl) reapplyWorkflowEvents( ctx context.Context, mutableState execution.MutableState, firstEventID int64, nextEventID int64, branchToken []byte, ) (string, error) { // TODO change this logic to fetching all workflow [baseWorkflow, currentWorkflow] // from visibility for better coverage of events eligible for re-application. // after the above change, this API do not have to return the continue as new run ID if firstEventID == nextEventID { // This means the workflow reset to a pending decision task // and the decision task is the latest event in the workflow. return "", nil } domainID := mutableState.GetExecutionInfo().DomainID iter := collection.NewPagingIterator(r.getPaginationFn( ctx, firstEventID, nextEventID, branchToken, domainID, )) var nextRunID string var lastEvents []*types.HistoryEvent for iter.HasNext() { batch, err := iter.Next() if err != nil { return "", err } lastEvents = batch.(*types.History).Events if err := r.reapplyEvents(mutableState, lastEvents); err != nil { return "", err } } if len(lastEvents) > 0 { lastEvent := lastEvents[len(lastEvents)-1] if lastEvent.GetEventType() == types.EventTypeWorkflowExecutionContinuedAsNew { nextRunID = lastEvent.GetWorkflowExecutionContinuedAsNewEventAttributes().GetNewExecutionRunID() } } return nextRunID, nil } func (r *workflowResetterImpl) reapplyEvents( mutableState execution.MutableState, events []*types.HistoryEvent, ) error { for _, event := range events { switch event.GetEventType() { case types.EventTypeWorkflowExecutionSignaled: attr := event.GetWorkflowExecutionSignaledEventAttributes() if _, err := mutableState.AddWorkflowExecutionSignaled( attr.GetSignalName(), attr.GetInput(), attr.GetIdentity(), "", // Do not set requestID for requests reapplied, because they have already been applied previously ); err != nil { return err } default: // events other than signal will be ignored } } return nil } func (r *workflowResetterImpl) getPaginationFn( ctx context.Context, firstEventID int64, nextEventID int64, branchToken []byte, domainID string, ) collection.PaginationFn { return func(paginationToken []byte) ([]interface{}, []byte, error) { _, historyBatches, token, _, err := persistenceutils.PaginateHistory( ctx, r.historyV2Mgr, true, branchToken, firstEventID, nextEventID, paginationToken, execution.NDCDefaultPageSize, common.IntPtr(r.shard.GetShardID()), domainID, r.domainCache, ) if err != nil { return nil, nil, err } var paginateItems []interface{} for _, history := range historyBatches { paginateItems = append(paginateItems, history) } return paginateItems, token, nil } } func (r *workflowResetterImpl) closePendingDecisionTask( resetMutableState execution.MutableState, baseRunID string, resetRunID string, baseLastEventVersion int64, resetReason string, resetRequestID string, ) (execution.MutableState, error) { if len(resetMutableState.GetPendingChildExecutionInfos()) > 0 { return nil, &types.BadRequestError{ Message: "Can not reset workflow with pending child workflows", } } if decision, ok := resetMutableState.GetInFlightDecision(); ok { // reset workflow has decision task start _, err := resetMutableState.AddDecisionTaskFailedEvent( decision.ScheduleID, decision.StartedID, types.DecisionTaskFailedCauseResetWorkflow, nil, execution.IdentityHistoryService, resetReason, "", baseRunID, resetRunID, baseLastEventVersion, resetRequestID, ) if err != nil { return nil, err } } else if decision, ok := resetMutableState.GetPendingDecision(); ok { if ok { // reset workflow has decision task schedule _, err := resetMutableState.AddDecisionTaskResetTimeoutEvent( decision.ScheduleID, baseRunID, resetRunID, baseLastEventVersion, resetReason, resetRequestID, ) if err != nil { return nil, err } } } return resetMutableState, nil } ================================================ FILE: service/history/reset/resetter_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: resetter.go // // Generated by this command: // // mockgen -package reset -source resetter.go -destination resetter_mock.go // // Package reset is a generated GoMock package. package reset import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" execution "github.com/uber/cadence/service/history/execution" ) // MockWorkflowResetter is a mock of WorkflowResetter interface. type MockWorkflowResetter struct { ctrl *gomock.Controller recorder *MockWorkflowResetterMockRecorder isgomock struct{} } // MockWorkflowResetterMockRecorder is the mock recorder for MockWorkflowResetter. type MockWorkflowResetterMockRecorder struct { mock *MockWorkflowResetter } // NewMockWorkflowResetter creates a new mock instance. func NewMockWorkflowResetter(ctrl *gomock.Controller) *MockWorkflowResetter { mock := &MockWorkflowResetter{ctrl: ctrl} mock.recorder = &MockWorkflowResetterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWorkflowResetter) EXPECT() *MockWorkflowResetterMockRecorder { return m.recorder } // ResetWorkflow mocks base method. func (m *MockWorkflowResetter) ResetWorkflow(ctx context.Context, domainID, workflowID, baseRunID string, baseBranchToken []byte, baseRebuildLastEventID, baseRebuildLastEventVersion, baseNextEventID int64, resetRunID, resetRequestID string, currentWorkflow execution.Workflow, resetReason string, additionalReapplyEvents []*types.HistoryEvent, skipSignalReapply bool) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetWorkflow", ctx, domainID, workflowID, baseRunID, baseBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, baseNextEventID, resetRunID, resetRequestID, currentWorkflow, resetReason, additionalReapplyEvents, skipSignalReapply) ret0, _ := ret[0].(error) return ret0 } // ResetWorkflow indicates an expected call of ResetWorkflow. func (mr *MockWorkflowResetterMockRecorder) ResetWorkflow(ctx, domainID, workflowID, baseRunID, baseBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, baseNextEventID, resetRunID, resetRequestID, currentWorkflow, resetReason, additionalReapplyEvents, skipSignalReapply any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetWorkflow", reflect.TypeOf((*MockWorkflowResetter)(nil).ResetWorkflow), ctx, domainID, workflowID, baseRunID, baseBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, baseNextEventID, resetRunID, resetRequestID, currentWorkflow, resetReason, additionalReapplyEvents, skipSignalReapply) } ================================================ FILE: service/history/reset/resetter_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package reset import ( "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/collection" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) type ( workflowResetterSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockStateRebuilder *execution.MockStateRebuilder mockHistoryV2Mgr *mocks.HistoryV2Manager logger log.Logger domainID string workflowID string baseRunID string currentRunID string resetRunID string workflowResetter *workflowResetterImpl } ) func TestWorkflowResetterSuite(t *testing.T) { s := new(workflowResetterSuite) suite.Run(t, s) } func (s *workflowResetterSuite) SetupSuite() { } func (s *workflowResetterSuite) TearDownSuite() { } func (s *workflowResetterSuite) SetupTest() { s.Assertions = require.New(s.T()) s.logger = testlogger.New(s.Suite.T()) s.controller = gomock.NewController(s.T()) s.mockStateRebuilder = execution.NewMockStateRebuilder(s.controller) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) s.mockHistoryV2Mgr = s.mockShard.Resource.HistoryMgr s.workflowResetter = NewWorkflowResetter( s.mockShard, execution.NewCache(s.mockShard), s.logger, ).(*workflowResetterImpl) s.workflowResetter.newStateRebuilder = func() execution.StateRebuilder { return s.mockStateRebuilder } s.domainID = constants.TestDomainID s.workflowID = "some random workflow ID" s.baseRunID = uuid.New() s.currentRunID = uuid.New() s.resetRunID = uuid.New() } func (s *workflowResetterSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *workflowResetterSuite) TestPersistToDB_CurrentTerminated() { currentWorkflowTerminated := true currentWorkflow := execution.NewMockWorkflow(s.controller) currentReleaseCalled := false currentContext := execution.NewMockContext(s.controller) currentMutableState := execution.NewMockMutableState(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetContext().Return(currentContext).AnyTimes() currentWorkflow.EXPECT().GetMutableState().Return(currentMutableState).AnyTimes() currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() resetWorkflow := execution.NewMockWorkflow(s.controller) resetReleaseCalled := false resetContext := execution.NewMockContext(s.controller) resetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { resetReleaseCalled = true } resetWorkflow.EXPECT().GetContext().Return(resetContext).AnyTimes() resetWorkflow.EXPECT().GetMutableState().Return(resetMutableState).AnyTimes() resetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() currentContext.EXPECT().UpdateWorkflowExecutionWithNewAsActive( gomock.Any(), gomock.Any(), resetContext, resetMutableState, ).Return(nil).Times(1) err := s.workflowResetter.persistToDB(context.Background(), currentWorkflowTerminated, currentWorkflow, resetWorkflow) s.NoError(err) // persistToDB function is not charged of releasing locks s.False(currentReleaseCalled) s.False(resetReleaseCalled) } func (s *workflowResetterSuite) TestPersistToDB_CurrentNotTerminated() { currentWorkflowTerminated := false currentLastWriteVersion := int64(1234) currentWorkflow := execution.NewMockWorkflow(s.controller) currentReleaseCalled := false currentContext := execution.NewMockContext(s.controller) currentMutableState := execution.NewMockMutableState(s.controller) var currentReleaseFn execution.ReleaseFunc = func(error) { currentReleaseCalled = true } currentWorkflow.EXPECT().GetContext().Return(currentContext).AnyTimes() currentWorkflow.EXPECT().GetMutableState().Return(currentMutableState).AnyTimes() currentWorkflow.EXPECT().GetReleaseFn().Return(currentReleaseFn).AnyTimes() currentMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ RunID: s.currentRunID, }).AnyTimes() currentMutableState.EXPECT().GetLastWriteVersion().Return(currentLastWriteVersion, nil).AnyTimes() resetWorkflow := execution.NewMockWorkflow(s.controller) resetReleaseCalled := false resetContext := execution.NewMockContext(s.controller) resetMutableState := execution.NewMockMutableState(s.controller) var targetReleaseFn execution.ReleaseFunc = func(error) { resetReleaseCalled = true } resetWorkflow.EXPECT().GetContext().Return(resetContext).AnyTimes() resetWorkflow.EXPECT().GetMutableState().Return(resetMutableState).AnyTimes() resetWorkflow.EXPECT().GetReleaseFn().Return(targetReleaseFn).AnyTimes() resetSnapshot := &persistence.WorkflowSnapshot{} resetEventsSeq := []*persistence.WorkflowEvents{{ DomainID: s.domainID, WorkflowID: s.workflowID, RunID: s.resetRunID, BranchToken: []byte("some random reset branch token"), Events: []*types.HistoryEvent{{ ID: 123, }}, }} resetEvents := events.PersistedBlob{DataBlob: persistence.DataBlob{Data: make([]byte, 4321)}} resetMutableState.EXPECT().CloseTransactionAsSnapshot( gomock.Any(), execution.TransactionPolicyActive, ).Return(resetSnapshot, resetEventsSeq, nil).Times(1) resetContext.EXPECT().PersistNonStartWorkflowBatchEvents(gomock.Any(), resetEventsSeq[0]).Return(resetEvents, nil).Times(1) resetContext.EXPECT().CreateWorkflowExecution( gomock.Any(), resetSnapshot, resetEvents, persistence.CreateWorkflowModeContinueAsNew, s.currentRunID, currentLastWriteVersion, persistence.CreateWorkflowRequestModeNew, ).Return(nil).Times(1) err := s.workflowResetter.persistToDB(context.Background(), currentWorkflowTerminated, currentWorkflow, resetWorkflow) s.NoError(err) // persistToDB function is not charged of releasing locks s.False(currentReleaseCalled) s.False(resetReleaseCalled) } func (s *workflowResetterSuite) TestReplayResetWorkflow() { ctx := context.Background() baseBranchToken := []byte("some random base branch token") baseRebuildLastEventID := int64(1233) baseRebuildLastEventVersion := int64(12) baseNodeID := baseRebuildLastEventID + 1 resetBranchToken := []byte("some random reset branch token") resetRequestID := uuid.New() resetHistorySize := int64(4411) resetMutableState := execution.NewMockMutableState(s.controller) domainName := uuid.New() s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() s.mockHistoryV2Mgr.On("ForkHistoryBranch", mock.Anything, &persistence.ForkHistoryBranchRequest{ ForkBranchToken: baseBranchToken, ForkNodeID: baseNodeID, Info: persistence.BuildHistoryGarbageCleanupInfo(s.domainID, s.workflowID, s.resetRunID), ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }).Return(&persistence.ForkHistoryBranchResponse{NewBranchToken: resetBranchToken}, nil).Times(1) s.mockStateRebuilder.EXPECT().Rebuild( ctx, gomock.Any(), definition.NewWorkflowIdentifier( s.domainID, s.workflowID, s.baseRunID, ), baseBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, definition.NewWorkflowIdentifier( s.domainID, s.workflowID, s.resetRunID, ), gomock.Any(), resetRequestID, ).DoAndReturn(func(ctx context.Context, now time.Time, baseWorkflowIdentifier definition.WorkflowIdentifier, baseBranchToken []byte, baseRebuildLastEventID int64, baseRebuildLastEventVersion int64, targetWorkflowIdentifier definition.WorkflowIdentifier, targetBranchFn func() ([]byte, error), requestID string) (execution.MutableState, int64, error) { targetBranchToken, err := targetBranchFn() s.NoError(err) s.Equal(resetBranchToken, targetBranchToken) return resetMutableState, resetHistorySize, nil }).Times(1) resetWorkflow, err := s.workflowResetter.replayResetWorkflow( ctx, s.domainID, s.workflowID, s.baseRunID, baseBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, s.resetRunID, resetRequestID, ) s.NoError(err) s.Equal(resetHistorySize, resetWorkflow.GetContext().GetHistorySize()) s.Equal(resetMutableState, resetWorkflow.GetMutableState()) } func (s *workflowResetterSuite) TestFailInflightActivity() { now := time.Now().UTC() terminateReason := "some random termination reason" mutableState := execution.NewMockMutableState(s.controller) activity1 := &persistence.ActivityInfo{ Version: 12, ScheduleID: 123, ScheduledTime: now.Add(-10 * time.Second), StartedID: 124, Details: []byte("some random activity 1 details"), StartedIdentity: "some random activity 1 started identity", } activity2 := &persistence.ActivityInfo{ Version: 12, ScheduleID: 456, ScheduledTime: now.Add(-10 * time.Second), StartedID: commonconstants.EmptyEventID, TimerTaskStatus: execution.TimerTaskStatusCreatedScheduleToStart, } mutableState.EXPECT().GetPendingActivityInfos().Return(map[int64]*persistence.ActivityInfo{ activity1.ScheduleID: activity1, activity2.ScheduleID: activity2, }).AnyTimes() mutableState.EXPECT().AddActivityTaskFailedEvent( activity1.ScheduleID, activity1.StartedID, &types.RespondActivityTaskFailedRequest{ Reason: common.StringPtr(terminateReason), Details: activity1.Details, Identity: activity1.StartedIdentity, }, ).Return(&types.HistoryEvent{}, nil).Times(1) mutableState.EXPECT().UpdateActivity(&persistence.ActivityInfo{ Version: activity2.Version, ScheduleID: activity2.ScheduleID, ScheduledTime: now, StartedID: activity2.StartedID, TimerTaskStatus: execution.TimerTaskStatusNone, }).Return(nil).Times(1) err := s.workflowResetter.failInflightActivity(now, mutableState, terminateReason) s.NoError(err) } func (s *workflowResetterSuite) TestGenerateBranchToken() { baseBranchToken := []byte("some random base branch token") baseNodeID := int64(1234) resetBranchToken := []byte("some random reset branch token") domainName := uuid.New() s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() s.mockHistoryV2Mgr.On("ForkHistoryBranch", mock.Anything, &persistence.ForkHistoryBranchRequest{ ForkBranchToken: baseBranchToken, ForkNodeID: baseNodeID, Info: persistence.BuildHistoryGarbageCleanupInfo(s.domainID, s.workflowID, s.resetRunID), ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }).Return(&persistence.ForkHistoryBranchResponse{NewBranchToken: resetBranchToken}, nil).Times(1) newBranchToken, err := s.workflowResetter.forkAndGenerateBranchToken( context.Background(), s.domainID, s.workflowID, baseBranchToken, baseNodeID, s.resetRunID, ) s.NoError(err) s.Equal(resetBranchToken, newBranchToken) } func (s *workflowResetterSuite) TestTerminateWorkflow() { decision := &execution.DecisionInfo{ Version: 123, ScheduleID: 1234, StartedID: 5678, } nextEventID := int64(666) terminateReason := "some random terminate reason" mutableState := execution.NewMockMutableState(s.controller) mutableState.EXPECT().GetNextEventID().Return(nextEventID).AnyTimes() mutableState.EXPECT().GetInFlightDecision().Return(decision, true).Times(1) mutableState.EXPECT().AddDecisionTaskFailedEvent( decision.ScheduleID, decision.StartedID, types.DecisionTaskFailedCauseForceCloseDecision, ([]byte)(nil), execution.IdentityHistoryService, "", "", "", "", int64(0), "", ).Return(&types.HistoryEvent{}, nil).Times(1) mutableState.EXPECT().FlushBufferedEvents().Return(nil).Times(1) mutableState.EXPECT().AddWorkflowExecutionTerminatedEvent( nextEventID, terminateReason, ([]byte)(nil), execution.IdentityHistoryService, ).Return(&types.HistoryEvent{}, nil).Times(1) err := s.workflowResetter.terminateWorkflow(mutableState, terminateReason) s.NoError(err) } func (s *workflowResetterSuite) TestReapplyContinueAsNewWorkflowEvents() { ctx := context.Background() baseFirstEventID := int64(124) baseNextEventID := int64(456) baseBranchToken := []byte("some random base branch token") newRunID := uuid.New() newFirstEventID := commonconstants.FirstEventID newNextEventID := int64(6) newBranchToken := []byte("some random new branch token") domainName := "test-domain" baseEvent1 := &types.HistoryEvent{ ID: 124, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, } baseEvent2 := &types.HistoryEvent{ ID: 125, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{}, } baseEvent3 := &types.HistoryEvent{ ID: 126, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{}, } baseEvent4 := &types.HistoryEvent{ ID: 127, EventType: types.EventTypeWorkflowExecutionContinuedAsNew.Ptr(), WorkflowExecutionContinuedAsNewEventAttributes: &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: newRunID, }, } newEvent1 := &types.HistoryEvent{ ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, } newEvent2 := &types.HistoryEvent{ ID: 2, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, } newEvent3 := &types.HistoryEvent{ ID: 3, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{}, } newEvent4 := &types.HistoryEvent{ ID: 4, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{}, } newEvent5 := &types.HistoryEvent{ ID: 5, EventType: types.EventTypeWorkflowExecutionFailed.Ptr(), WorkflowExecutionFailedEventAttributes: &types.WorkflowExecutionFailedEventAttributes{}, } baseEvents := []*types.HistoryEvent{baseEvent1, baseEvent2, baseEvent3, baseEvent4} s.mockHistoryV2Mgr.On("ReadHistoryBranchByBatch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: baseBranchToken, MinEventID: baseFirstEventID, MaxEventID: baseNextEventID, PageSize: execution.NDCDefaultPageSize, NextPageToken: nil, ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }).Return(&persistence.ReadHistoryBranchByBatchResponse{ History: []*types.History{{Events: baseEvents}}, NextPageToken: nil, }, nil).Once() newEvents := []*types.HistoryEvent{newEvent1, newEvent2, newEvent3, newEvent4, newEvent5} s.mockHistoryV2Mgr.On("ReadHistoryBranchByBatch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: newBranchToken, MinEventID: newFirstEventID, MaxEventID: newNextEventID, PageSize: execution.NDCDefaultPageSize, NextPageToken: nil, ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }).Return(&persistence.ReadHistoryBranchByBatchResponse{ History: []*types.History{{Events: newEvents}}, NextPageToken: nil, }, nil).Once() resetMutableState := execution.NewMockMutableState(s.controller) s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).AnyTimes() resetContext := execution.NewMockContext(s.controller) resetContext.EXPECT().Lock(gomock.Any()).Return(nil).AnyTimes() resetContext.EXPECT().Unlock().AnyTimes() resetContext.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(resetMutableState, nil).AnyTimes() resetContext.EXPECT().ByteSize().Return(uint64(1)).AnyTimes() resetMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() resetMutableState.EXPECT().GetNextEventID().Return(newNextEventID).AnyTimes() resetMutableState.EXPECT().GetCurrentBranchToken().Return(newBranchToken, nil).AnyTimes() resetContextCacheKey := definition.NewWorkflowIdentifier(s.domainID, s.workflowID, newRunID) _, _ = s.workflowResetter.executionCache.PutIfNotExist(resetContextCacheKey, resetContext) err := s.workflowResetter.reapplyResetAndContinueAsNewWorkflowEvents( ctx, resetMutableState, s.domainID, s.workflowID, s.baseRunID, baseBranchToken, baseFirstEventID, baseNextEventID, ) s.NoError(err) } func (s *workflowResetterSuite) TestReapplyWorkflowEvents() { firstEventID := commonconstants.FirstEventID nextEventID := int64(6) branchToken := []byte("some random branch token") domainName := "test-domain" newRunID := uuid.New() event1 := &types.HistoryEvent{ ID: 1, EventType: types.EventTypeWorkflowExecutionStarted.Ptr(), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, } event2 := &types.HistoryEvent{ ID: 2, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, } event3 := &types.HistoryEvent{ ID: 3, EventType: types.EventTypeDecisionTaskStarted.Ptr(), DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{}, } event4 := &types.HistoryEvent{ ID: 4, EventType: types.EventTypeDecisionTaskCompleted.Ptr(), DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{}, } event5 := &types.HistoryEvent{ ID: 5, EventType: types.EventTypeWorkflowExecutionContinuedAsNew.Ptr(), WorkflowExecutionContinuedAsNewEventAttributes: &types.WorkflowExecutionContinuedAsNewEventAttributes{ NewExecutionRunID: newRunID, }, } events := []*types.HistoryEvent{event1, event2, event3, event4, event5} s.mockHistoryV2Mgr.On("ReadHistoryBranchByBatch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: execution.NDCDefaultPageSize, NextPageToken: nil, ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }).Return(&persistence.ReadHistoryBranchByBatchResponse{ History: []*types.History{{Events: events}}, NextPageToken: nil, }, nil).Once() s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() mutableState := execution.NewMockMutableState(s.controller) mutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{}).AnyTimes() nextRunID, err := s.workflowResetter.reapplyWorkflowEvents( context.Background(), mutableState, firstEventID, nextEventID, branchToken, ) s.NoError(err) s.Equal(newRunID, nextRunID) } func (s *workflowResetterSuite) TestClosePendingDecisionTask() { sourceMutableState := execution.NewMockMutableState(s.controller) baseRunID := uuid.New() newRunID := uuid.New() baseForkEventVerison := int64(10) reason := "test" decisionScheduleEventID := int64(2) decisionStartEventID := decisionScheduleEventID + 1 resetRequestID := "fe4a2833-f761-4cfe-91f2-6cd34c5e987a" // The workflow has decision schedule and decision start sourceMutableState.EXPECT().GetInFlightDecision().Return(&execution.DecisionInfo{ ScheduleID: decisionScheduleEventID, StartedID: decisionStartEventID, }, true).Times(1) sourceMutableState.EXPECT().GetPendingChildExecutionInfos().Return(make(map[int64]*persistence.ChildExecutionInfo)).Times(1) sourceMutableState.EXPECT().AddDecisionTaskFailedEvent( decisionScheduleEventID, decisionStartEventID, types.DecisionTaskFailedCauseResetWorkflow, nil, execution.IdentityHistoryService, reason, "", baseRunID, newRunID, baseForkEventVerison, resetRequestID, ).Return(nil, nil).Times(1) _, err := s.workflowResetter.closePendingDecisionTask( sourceMutableState, baseRunID, newRunID, baseForkEventVerison, reason, resetRequestID, ) s.NoError(err) // The workflow has only decision schedule sourceMutableState.EXPECT().GetInFlightDecision().Return(nil, false).Times(1) sourceMutableState.EXPECT().GetPendingDecision().Return(&execution.DecisionInfo{ScheduleID: decisionScheduleEventID}, true).Times(1) sourceMutableState.EXPECT().GetPendingChildExecutionInfos().Return(make(map[int64]*persistence.ChildExecutionInfo)).Times(1) sourceMutableState.EXPECT().AddDecisionTaskResetTimeoutEvent( decisionScheduleEventID, baseRunID, newRunID, baseForkEventVerison, reason, resetRequestID, ).Return(nil, nil).Times(1) _, err = s.workflowResetter.closePendingDecisionTask( sourceMutableState, baseRunID, newRunID, baseForkEventVerison, reason, resetRequestID, ) s.NoError(err) } func (s *workflowResetterSuite) TestReapplyEvents() { event1 := &types.HistoryEvent{ ID: 101, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "some random signal name", Input: []byte("some random signal input"), Identity: "some random signal identity", RequestID: "a255a38a-1e5b-47a1-a7fc-243566eed78e", }, } event2 := &types.HistoryEvent{ ID: 102, EventType: types.EventTypeDecisionTaskScheduled.Ptr(), DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, } event3 := &types.HistoryEvent{ ID: 103, EventType: types.EventTypeWorkflowExecutionSignaled.Ptr(), WorkflowExecutionSignaledEventAttributes: &types.WorkflowExecutionSignaledEventAttributes{ SignalName: "another random signal name", Input: []byte("another random signal input"), Identity: "another random signal identity", RequestID: "b4d446a7-c277-4cf7-93b4-0dc304f05346", }, } events := []*types.HistoryEvent{event1, event2, event3} mutableState := execution.NewMockMutableState(s.controller) for _, event := range events { if event.GetEventType() == types.EventTypeWorkflowExecutionSignaled { attr := event.GetWorkflowExecutionSignaledEventAttributes() mutableState.EXPECT().AddWorkflowExecutionSignaled( attr.GetSignalName(), attr.GetInput(), attr.GetIdentity(), "", ).Return(&types.HistoryEvent{}, nil).Times(1) } } err := s.workflowResetter.reapplyEvents(mutableState, events) s.NoError(err) } func (s *workflowResetterSuite) TestPagination() { firstEventID := commonconstants.FirstEventID nextEventID := int64(101) branchToken := []byte("some random branch token") domainName := "some random domain name" event1 := &types.HistoryEvent{ ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{}, } event2 := &types.HistoryEvent{ ID: 2, DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{}, } event3 := &types.HistoryEvent{ ID: 3, DecisionTaskStartedEventAttributes: &types.DecisionTaskStartedEventAttributes{}, } event4 := &types.HistoryEvent{ ID: 4, DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{}, } event5 := &types.HistoryEvent{ ID: 5, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{}, } history1 := []*types.History{{[]*types.HistoryEvent{event1, event2, event3}}} history2 := []*types.History{{[]*types.HistoryEvent{event4, event5}}} history := append(history1, history2...) pageToken := []byte("some random token") s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() s.mockHistoryV2Mgr.On("ReadHistoryBranchByBatch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: execution.NDCDefaultPageSize, NextPageToken: nil, ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }).Return(&persistence.ReadHistoryBranchByBatchResponse{ History: history1, NextPageToken: pageToken, Size: 12345, }, nil).Once() s.mockHistoryV2Mgr.On("ReadHistoryBranchByBatch", mock.Anything, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: firstEventID, MaxEventID: nextEventID, PageSize: execution.NDCDefaultPageSize, NextPageToken: pageToken, ShardID: common.IntPtr(s.mockShard.GetShardID()), DomainName: domainName, }).Return(&persistence.ReadHistoryBranchByBatchResponse{ History: history2, NextPageToken: nil, Size: 67890, }, nil).Once() paginationFn := s.workflowResetter.getPaginationFn(context.Background(), firstEventID, nextEventID, branchToken, s.domainID) iter := collection.NewPagingIterator(paginationFn) result := []*types.History{} for iter.HasNext() { item, err := iter.Next() s.NoError(err) result = append(result, item.(*types.History)) } s.Equal(history, result) } ================================================ FILE: service/history/resource/resource.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination resource_mock.go -self_package github.com/uber/cadence/service/history/resource package resource import ( "fmt" "sync/atomic" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/quotas/global/algorithm" "github.com/uber/cadence/common/quotas/permember" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/worker/archiver" ) // Resource is the interface which expose common history resources type Resource interface { resource.Resource GetEventCache() events.Cache GetRatelimiterAlgorithm() algorithm.RequestWeighted GetArchiverClient() archiver.Client } type resourceImpl struct { status int32 resource.Resource eventCache events.Cache ratelimitAlgorithm algorithm.RequestWeighted archiverClient archiver.Client } // Start starts all resources func (h *resourceImpl) Start() { if !atomic.CompareAndSwapInt32( &h.status, common.DaemonStatusInitialized, common.DaemonStatusStarted, ) { return } h.Resource.Start() h.GetLogger().Info("history resource started", tag.LifeCycleStarted) } // Stop stops all resources func (h *resourceImpl) Stop() { if !atomic.CompareAndSwapInt32( &h.status, common.DaemonStatusStarted, common.DaemonStatusStopped, ) { return } h.Resource.Stop() h.GetLogger().Info("history resource stopped", tag.LifeCycleStopped) } // GetEventCache return event cache func (h *resourceImpl) GetEventCache() events.Cache { return h.eventCache } func (h *resourceImpl) GetRatelimiterAlgorithm() algorithm.RequestWeighted { return h.ratelimitAlgorithm } func (h *resourceImpl) GetArchiverClient() archiver.Client { return h.archiverClient } // New create a new resource containing common history dependencies func New( params *resource.Params, serviceName string, config *config.Config, ) (historyResource Resource, retError error) { serviceResource, err := resource.New( params, serviceName, &service.Config{ PersistenceMaxQPS: config.PersistenceMaxQPS, PersistenceGlobalMaxQPS: config.PersistenceGlobalMaxQPS, ThrottledLoggerMaxRPS: config.ThrottledLogRPS, ReadVisibilityStoreName: nil, // history service never read, WriteVisibilityStoreName: config.WriteVisibilityStoreName, EnableLogCustomerQueryParameter: nil, // log customer parameter will be done in front-end EnableDBVisibilitySampling: config.EnableVisibilitySampling, EnableReadDBVisibilityFromClosedExecutionV2: nil, // history service never read, DBVisibilityListMaxQPS: nil, // history service never read, WriteDBVisibilityOpenMaxQPS: config.VisibilityOpenMaxQPS, WriteDBVisibilityClosedMaxQPS: config.VisibilityClosedMaxQPS, ESVisibilityListMaxQPS: nil, // history service never read, ESIndexMaxResultWindow: nil, // history service never read, ValidSearchAttributes: config.ValidSearchAttributes, // history service never read, (Pinot need this to initialize pinotQueryValidator) IsErrorRetryableFunction: common.IsServiceTransientError, }, ) if err != nil { return nil, err } eventCache := events.NewGlobalCache( config.EventsCacheGlobalInitialCount(), config.EventsCacheGlobalMaxCount(), config.EventsCacheTTL(), serviceResource.GetHistoryManager(), params.Logger, params.MetricsClient, config.EnableSizeBasedHistoryEventCache, config.EventsCacheMaxSize, serviceResource.GetDomainCache(), ) ratelimitAlgorithm, err := algorithm.New( params.MetricsClient, params.Logger, algorithm.Config{ NewDataWeight: config.GlobalRatelimiterNewDataWeight, UpdateInterval: config.GlobalRatelimiterUpdateInterval, DecayAfter: config.GlobalRatelimiterDecayAfter, GcAfter: config.GlobalRatelimiterGCAfter, }, ) if err != nil { return nil, fmt.Errorf("invalid ratelimit algorithm config: %w", err) } archivalClient := archiver.NewClient( params.MetricsClient, params.Logger, params.PublicClient, config.NumArchiveSystemWorkflows, quotas.NewDynamicRateLimiter(config.ArchiveRequestRPS.AsFloat64()), quotas.NewDynamicRateLimiter(func() float64 { return permember.PerMember( service.History, float64(config.ArchiveInlineHistoryGlobalRPS()), float64(config.ArchiveInlineHistoryRPS()), params.MembershipResolver, ) }), quotas.NewDynamicRateLimiter(func() float64 { return permember.PerMember( service.History, float64(config.ArchiveInlineVisibilityGlobalRPS()), float64(config.ArchiveInlineVisibilityRPS()), params.MembershipResolver, ) }), params.ArchiverProvider, config.AllowArchivingIncompleteHistory, ) historyResource = &resourceImpl{ Resource: serviceResource, eventCache: eventCache, ratelimitAlgorithm: ratelimitAlgorithm, archiverClient: archivalClient, } return } ================================================ FILE: service/history/resource/resource_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: resource.go // // Generated by this command: // // mockgen -package resource -source resource.go -destination resource_mock.go -self_package github.com/uber/cadence/service/history/resource // // Package resource is a generated GoMock package. package resource import ( reflect "reflect" tally "github.com/uber-go/tally" workflowserviceclient "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" client "github.com/uber/cadence/client" admin "github.com/uber/cadence/client/admin" frontend "github.com/uber/cadence/client/frontend" history "github.com/uber/cadence/client/history" matching "github.com/uber/cadence/client/matching" activecluster "github.com/uber/cadence/common/activecluster" archiver "github.com/uber/cadence/common/archiver" provider "github.com/uber/cadence/common/archiver/provider" queue "github.com/uber/cadence/common/asyncworkflow/queue" blobstore "github.com/uber/cadence/common/blobstore" cache "github.com/uber/cadence/common/cache" clock "github.com/uber/cadence/common/clock" cluster "github.com/uber/cadence/common/cluster" domain "github.com/uber/cadence/common/domain" configstore "github.com/uber/cadence/common/dynamicconfig/configstore" isolationgroup "github.com/uber/cadence/common/isolationgroup" log "github.com/uber/cadence/common/log" membership "github.com/uber/cadence/common/membership" messaging "github.com/uber/cadence/common/messaging" metrics "github.com/uber/cadence/common/metrics" persistence "github.com/uber/cadence/common/persistence" client0 "github.com/uber/cadence/common/persistence/client" algorithm "github.com/uber/cadence/common/quotas/global/algorithm" rpc "github.com/uber/cadence/common/quotas/global/rpc" events "github.com/uber/cadence/service/history/events" executorclient "github.com/uber/cadence/service/sharddistributor/client/executorclient" archiver0 "github.com/uber/cadence/service/worker/archiver" ) // MockResource is a mock of Resource interface. type MockResource struct { ctrl *gomock.Controller recorder *MockResourceMockRecorder isgomock struct{} } // MockResourceMockRecorder is the mock recorder for MockResource. type MockResourceMockRecorder struct { mock *MockResource } // NewMockResource creates a new mock instance. func NewMockResource(ctrl *gomock.Controller) *MockResource { mock := &MockResource{ctrl: ctrl} mock.recorder = &MockResourceMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockResource) EXPECT() *MockResourceMockRecorder { return m.recorder } // GetActiveClusterManager mocks base method. func (m *MockResource) GetActiveClusterManager() activecluster.Manager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveClusterManager") ret0, _ := ret[0].(activecluster.Manager) return ret0 } // GetActiveClusterManager indicates an expected call of GetActiveClusterManager. func (mr *MockResourceMockRecorder) GetActiveClusterManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterManager", reflect.TypeOf((*MockResource)(nil).GetActiveClusterManager)) } // GetArchivalMetadata mocks base method. func (m *MockResource) GetArchivalMetadata() archiver.ArchivalMetadata { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetArchivalMetadata") ret0, _ := ret[0].(archiver.ArchivalMetadata) return ret0 } // GetArchivalMetadata indicates an expected call of GetArchivalMetadata. func (mr *MockResourceMockRecorder) GetArchivalMetadata() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetArchivalMetadata", reflect.TypeOf((*MockResource)(nil).GetArchivalMetadata)) } // GetArchiverClient mocks base method. func (m *MockResource) GetArchiverClient() archiver0.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetArchiverClient") ret0, _ := ret[0].(archiver0.Client) return ret0 } // GetArchiverClient indicates an expected call of GetArchiverClient. func (mr *MockResourceMockRecorder) GetArchiverClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetArchiverClient", reflect.TypeOf((*MockResource)(nil).GetArchiverClient)) } // GetArchiverProvider mocks base method. func (m *MockResource) GetArchiverProvider() provider.ArchiverProvider { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetArchiverProvider") ret0, _ := ret[0].(provider.ArchiverProvider) return ret0 } // GetArchiverProvider indicates an expected call of GetArchiverProvider. func (mr *MockResourceMockRecorder) GetArchiverProvider() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetArchiverProvider", reflect.TypeOf((*MockResource)(nil).GetArchiverProvider)) } // GetAsyncWorkflowQueueProvider mocks base method. func (m *MockResource) GetAsyncWorkflowQueueProvider() queue.Provider { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAsyncWorkflowQueueProvider") ret0, _ := ret[0].(queue.Provider) return ret0 } // GetAsyncWorkflowQueueProvider indicates an expected call of GetAsyncWorkflowQueueProvider. func (mr *MockResourceMockRecorder) GetAsyncWorkflowQueueProvider() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAsyncWorkflowQueueProvider", reflect.TypeOf((*MockResource)(nil).GetAsyncWorkflowQueueProvider)) } // GetBlobstoreClient mocks base method. func (m *MockResource) GetBlobstoreClient() blobstore.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBlobstoreClient") ret0, _ := ret[0].(blobstore.Client) return ret0 } // GetBlobstoreClient indicates an expected call of GetBlobstoreClient. func (mr *MockResourceMockRecorder) GetBlobstoreClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlobstoreClient", reflect.TypeOf((*MockResource)(nil).GetBlobstoreClient)) } // GetClientBean mocks base method. func (m *MockResource) GetClientBean() client.Bean { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetClientBean") ret0, _ := ret[0].(client.Bean) return ret0 } // GetClientBean indicates an expected call of GetClientBean. func (mr *MockResourceMockRecorder) GetClientBean() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClientBean", reflect.TypeOf((*MockResource)(nil).GetClientBean)) } // GetClusterMetadata mocks base method. func (m *MockResource) GetClusterMetadata() cluster.Metadata { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetClusterMetadata") ret0, _ := ret[0].(cluster.Metadata) return ret0 } // GetClusterMetadata indicates an expected call of GetClusterMetadata. func (mr *MockResourceMockRecorder) GetClusterMetadata() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterMetadata", reflect.TypeOf((*MockResource)(nil).GetClusterMetadata)) } // GetDispatcher mocks base method. func (m *MockResource) GetDispatcher() *yarpc.Dispatcher { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDispatcher") ret0, _ := ret[0].(*yarpc.Dispatcher) return ret0 } // GetDispatcher indicates an expected call of GetDispatcher. func (mr *MockResourceMockRecorder) GetDispatcher() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDispatcher", reflect.TypeOf((*MockResource)(nil).GetDispatcher)) } // GetDomainAuditManager mocks base method. func (m *MockResource) GetDomainAuditManager() persistence.DomainAuditManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainAuditManager") ret0, _ := ret[0].(persistence.DomainAuditManager) return ret0 } // GetDomainAuditManager indicates an expected call of GetDomainAuditManager. func (mr *MockResourceMockRecorder) GetDomainAuditManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainAuditManager", reflect.TypeOf((*MockResource)(nil).GetDomainAuditManager)) } // GetDomainCache mocks base method. func (m *MockResource) GetDomainCache() cache.DomainCache { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainCache") ret0, _ := ret[0].(cache.DomainCache) return ret0 } // GetDomainCache indicates an expected call of GetDomainCache. func (mr *MockResourceMockRecorder) GetDomainCache() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainCache", reflect.TypeOf((*MockResource)(nil).GetDomainCache)) } // GetDomainManager mocks base method. func (m *MockResource) GetDomainManager() persistence.DomainManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainManager") ret0, _ := ret[0].(persistence.DomainManager) return ret0 } // GetDomainManager indicates an expected call of GetDomainManager. func (mr *MockResourceMockRecorder) GetDomainManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainManager", reflect.TypeOf((*MockResource)(nil).GetDomainManager)) } // GetDomainMetricsScopeCache mocks base method. func (m *MockResource) GetDomainMetricsScopeCache() cache.DomainMetricsScopeCache { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainMetricsScopeCache") ret0, _ := ret[0].(cache.DomainMetricsScopeCache) return ret0 } // GetDomainMetricsScopeCache indicates an expected call of GetDomainMetricsScopeCache. func (mr *MockResourceMockRecorder) GetDomainMetricsScopeCache() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainMetricsScopeCache", reflect.TypeOf((*MockResource)(nil).GetDomainMetricsScopeCache)) } // GetDomainReplicationQueue mocks base method. func (m *MockResource) GetDomainReplicationQueue() domain.ReplicationQueue { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainReplicationQueue") ret0, _ := ret[0].(domain.ReplicationQueue) return ret0 } // GetDomainReplicationQueue indicates an expected call of GetDomainReplicationQueue. func (mr *MockResourceMockRecorder) GetDomainReplicationQueue() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainReplicationQueue", reflect.TypeOf((*MockResource)(nil).GetDomainReplicationQueue)) } // GetEventCache mocks base method. func (m *MockResource) GetEventCache() events.Cache { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEventCache") ret0, _ := ret[0].(events.Cache) return ret0 } // GetEventCache indicates an expected call of GetEventCache. func (mr *MockResourceMockRecorder) GetEventCache() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEventCache", reflect.TypeOf((*MockResource)(nil).GetEventCache)) } // GetExecutionManager mocks base method. func (m *MockResource) GetExecutionManager(arg0 int) (persistence.ExecutionManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetExecutionManager", arg0) ret0, _ := ret[0].(persistence.ExecutionManager) ret1, _ := ret[1].(error) return ret0, ret1 } // GetExecutionManager indicates an expected call of GetExecutionManager. func (mr *MockResourceMockRecorder) GetExecutionManager(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExecutionManager", reflect.TypeOf((*MockResource)(nil).GetExecutionManager), arg0) } // GetFrontendClient mocks base method. func (m *MockResource) GetFrontendClient() frontend.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFrontendClient") ret0, _ := ret[0].(frontend.Client) return ret0 } // GetFrontendClient indicates an expected call of GetFrontendClient. func (mr *MockResourceMockRecorder) GetFrontendClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFrontendClient", reflect.TypeOf((*MockResource)(nil).GetFrontendClient)) } // GetFrontendRawClient mocks base method. func (m *MockResource) GetFrontendRawClient() frontend.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetFrontendRawClient") ret0, _ := ret[0].(frontend.Client) return ret0 } // GetFrontendRawClient indicates an expected call of GetFrontendRawClient. func (mr *MockResourceMockRecorder) GetFrontendRawClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFrontendRawClient", reflect.TypeOf((*MockResource)(nil).GetFrontendRawClient)) } // GetHistoryClient mocks base method. func (m *MockResource) GetHistoryClient() history.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryClient") ret0, _ := ret[0].(history.Client) return ret0 } // GetHistoryClient indicates an expected call of GetHistoryClient. func (mr *MockResourceMockRecorder) GetHistoryClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryClient", reflect.TypeOf((*MockResource)(nil).GetHistoryClient)) } // GetHistoryManager mocks base method. func (m *MockResource) GetHistoryManager() persistence.HistoryManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryManager") ret0, _ := ret[0].(persistence.HistoryManager) return ret0 } // GetHistoryManager indicates an expected call of GetHistoryManager. func (mr *MockResourceMockRecorder) GetHistoryManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryManager", reflect.TypeOf((*MockResource)(nil).GetHistoryManager)) } // GetHistoryRawClient mocks base method. func (m *MockResource) GetHistoryRawClient() history.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryRawClient") ret0, _ := ret[0].(history.Client) return ret0 } // GetHistoryRawClient indicates an expected call of GetHistoryRawClient. func (mr *MockResourceMockRecorder) GetHistoryRawClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryRawClient", reflect.TypeOf((*MockResource)(nil).GetHistoryRawClient)) } // GetHostInfo mocks base method. func (m *MockResource) GetHostInfo() membership.HostInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHostInfo") ret0, _ := ret[0].(membership.HostInfo) return ret0 } // GetHostInfo indicates an expected call of GetHostInfo. func (mr *MockResourceMockRecorder) GetHostInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostInfo", reflect.TypeOf((*MockResource)(nil).GetHostInfo)) } // GetHostName mocks base method. func (m *MockResource) GetHostName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHostName") ret0, _ := ret[0].(string) return ret0 } // GetHostName indicates an expected call of GetHostName. func (mr *MockResourceMockRecorder) GetHostName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostName", reflect.TypeOf((*MockResource)(nil).GetHostName)) } // GetIsolationGroupState mocks base method. func (m *MockResource) GetIsolationGroupState() isolationgroup.State { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetIsolationGroupState") ret0, _ := ret[0].(isolationgroup.State) return ret0 } // GetIsolationGroupState indicates an expected call of GetIsolationGroupState. func (mr *MockResourceMockRecorder) GetIsolationGroupState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIsolationGroupState", reflect.TypeOf((*MockResource)(nil).GetIsolationGroupState)) } // GetIsolationGroupStore mocks base method. func (m *MockResource) GetIsolationGroupStore() configstore.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetIsolationGroupStore") ret0, _ := ret[0].(configstore.Client) return ret0 } // GetIsolationGroupStore indicates an expected call of GetIsolationGroupStore. func (mr *MockResourceMockRecorder) GetIsolationGroupStore() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIsolationGroupStore", reflect.TypeOf((*MockResource)(nil).GetIsolationGroupStore)) } // GetLogger mocks base method. func (m *MockResource) GetLogger() log.Logger { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLogger") ret0, _ := ret[0].(log.Logger) return ret0 } // GetLogger indicates an expected call of GetLogger. func (mr *MockResourceMockRecorder) GetLogger() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogger", reflect.TypeOf((*MockResource)(nil).GetLogger)) } // GetMatchingClient mocks base method. func (m *MockResource) GetMatchingClient() matching.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMatchingClient") ret0, _ := ret[0].(matching.Client) return ret0 } // GetMatchingClient indicates an expected call of GetMatchingClient. func (mr *MockResourceMockRecorder) GetMatchingClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMatchingClient", reflect.TypeOf((*MockResource)(nil).GetMatchingClient)) } // GetMatchingRawClient mocks base method. func (m *MockResource) GetMatchingRawClient() matching.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMatchingRawClient") ret0, _ := ret[0].(matching.Client) return ret0 } // GetMatchingRawClient indicates an expected call of GetMatchingRawClient. func (mr *MockResourceMockRecorder) GetMatchingRawClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMatchingRawClient", reflect.TypeOf((*MockResource)(nil).GetMatchingRawClient)) } // GetMembershipResolver mocks base method. func (m *MockResource) GetMembershipResolver() membership.Resolver { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMembershipResolver") ret0, _ := ret[0].(membership.Resolver) return ret0 } // GetMembershipResolver indicates an expected call of GetMembershipResolver. func (mr *MockResourceMockRecorder) GetMembershipResolver() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMembershipResolver", reflect.TypeOf((*MockResource)(nil).GetMembershipResolver)) } // GetMessagingClient mocks base method. func (m *MockResource) GetMessagingClient() messaging.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMessagingClient") ret0, _ := ret[0].(messaging.Client) return ret0 } // GetMessagingClient indicates an expected call of GetMessagingClient. func (mr *MockResourceMockRecorder) GetMessagingClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessagingClient", reflect.TypeOf((*MockResource)(nil).GetMessagingClient)) } // GetMetricsClient mocks base method. func (m *MockResource) GetMetricsClient() metrics.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMetricsClient") ret0, _ := ret[0].(metrics.Client) return ret0 } // GetMetricsClient indicates an expected call of GetMetricsClient. func (mr *MockResourceMockRecorder) GetMetricsClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetricsClient", reflect.TypeOf((*MockResource)(nil).GetMetricsClient)) } // GetMetricsScope mocks base method. func (m *MockResource) GetMetricsScope() tally.Scope { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMetricsScope") ret0, _ := ret[0].(tally.Scope) return ret0 } // GetMetricsScope indicates an expected call of GetMetricsScope. func (mr *MockResourceMockRecorder) GetMetricsScope() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetricsScope", reflect.TypeOf((*MockResource)(nil).GetMetricsScope)) } // GetPayloadSerializer mocks base method. func (m *MockResource) GetPayloadSerializer() persistence.PayloadSerializer { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPayloadSerializer") ret0, _ := ret[0].(persistence.PayloadSerializer) return ret0 } // GetPayloadSerializer indicates an expected call of GetPayloadSerializer. func (mr *MockResourceMockRecorder) GetPayloadSerializer() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPayloadSerializer", reflect.TypeOf((*MockResource)(nil).GetPayloadSerializer)) } // GetPersistenceBean mocks base method. func (m *MockResource) GetPersistenceBean() client0.Bean { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPersistenceBean") ret0, _ := ret[0].(client0.Bean) return ret0 } // GetPersistenceBean indicates an expected call of GetPersistenceBean. func (mr *MockResourceMockRecorder) GetPersistenceBean() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPersistenceBean", reflect.TypeOf((*MockResource)(nil).GetPersistenceBean)) } // GetRatelimiterAggregatorsClient mocks base method. func (m *MockResource) GetRatelimiterAggregatorsClient() rpc.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRatelimiterAggregatorsClient") ret0, _ := ret[0].(rpc.Client) return ret0 } // GetRatelimiterAggregatorsClient indicates an expected call of GetRatelimiterAggregatorsClient. func (mr *MockResourceMockRecorder) GetRatelimiterAggregatorsClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRatelimiterAggregatorsClient", reflect.TypeOf((*MockResource)(nil).GetRatelimiterAggregatorsClient)) } // GetRatelimiterAlgorithm mocks base method. func (m *MockResource) GetRatelimiterAlgorithm() algorithm.RequestWeighted { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRatelimiterAlgorithm") ret0, _ := ret[0].(algorithm.RequestWeighted) return ret0 } // GetRatelimiterAlgorithm indicates an expected call of GetRatelimiterAlgorithm. func (mr *MockResourceMockRecorder) GetRatelimiterAlgorithm() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRatelimiterAlgorithm", reflect.TypeOf((*MockResource)(nil).GetRatelimiterAlgorithm)) } // GetRemoteAdminClient mocks base method. func (m *MockResource) GetRemoteAdminClient(cluster string) (admin.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRemoteAdminClient", cluster) ret0, _ := ret[0].(admin.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRemoteAdminClient indicates an expected call of GetRemoteAdminClient. func (mr *MockResourceMockRecorder) GetRemoteAdminClient(cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemoteAdminClient", reflect.TypeOf((*MockResource)(nil).GetRemoteAdminClient), cluster) } // GetRemoteFrontendClient mocks base method. func (m *MockResource) GetRemoteFrontendClient(cluster string) (frontend.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRemoteFrontendClient", cluster) ret0, _ := ret[0].(frontend.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRemoteFrontendClient indicates an expected call of GetRemoteFrontendClient. func (mr *MockResourceMockRecorder) GetRemoteFrontendClient(cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemoteFrontendClient", reflect.TypeOf((*MockResource)(nil).GetRemoteFrontendClient), cluster) } // GetSDKClient mocks base method. func (m *MockResource) GetSDKClient() workflowserviceclient.Interface { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSDKClient") ret0, _ := ret[0].(workflowserviceclient.Interface) return ret0 } // GetSDKClient indicates an expected call of GetSDKClient. func (mr *MockResourceMockRecorder) GetSDKClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSDKClient", reflect.TypeOf((*MockResource)(nil).GetSDKClient)) } // GetServiceName mocks base method. func (m *MockResource) GetServiceName() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetServiceName") ret0, _ := ret[0].(string) return ret0 } // GetServiceName indicates an expected call of GetServiceName. func (mr *MockResourceMockRecorder) GetServiceName() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceName", reflect.TypeOf((*MockResource)(nil).GetServiceName)) } // GetShardDistributorExecutorClient mocks base method. func (m *MockResource) GetShardDistributorExecutorClient() executorclient.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardDistributorExecutorClient") ret0, _ := ret[0].(executorclient.Client) return ret0 } // GetShardDistributorExecutorClient indicates an expected call of GetShardDistributorExecutorClient. func (mr *MockResourceMockRecorder) GetShardDistributorExecutorClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardDistributorExecutorClient", reflect.TypeOf((*MockResource)(nil).GetShardDistributorExecutorClient)) } // GetShardManager mocks base method. func (m *MockResource) GetShardManager() persistence.ShardManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardManager") ret0, _ := ret[0].(persistence.ShardManager) return ret0 } // GetShardManager indicates an expected call of GetShardManager. func (mr *MockResourceMockRecorder) GetShardManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardManager", reflect.TypeOf((*MockResource)(nil).GetShardManager)) } // GetTaskManager mocks base method. func (m *MockResource) GetTaskManager() persistence.TaskManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskManager") ret0, _ := ret[0].(persistence.TaskManager) return ret0 } // GetTaskManager indicates an expected call of GetTaskManager. func (mr *MockResourceMockRecorder) GetTaskManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskManager", reflect.TypeOf((*MockResource)(nil).GetTaskManager)) } // GetThrottledLogger mocks base method. func (m *MockResource) GetThrottledLogger() log.Logger { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetThrottledLogger") ret0, _ := ret[0].(log.Logger) return ret0 } // GetThrottledLogger indicates an expected call of GetThrottledLogger. func (mr *MockResourceMockRecorder) GetThrottledLogger() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetThrottledLogger", reflect.TypeOf((*MockResource)(nil).GetThrottledLogger)) } // GetTimeSource mocks base method. func (m *MockResource) GetTimeSource() clock.TimeSource { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTimeSource") ret0, _ := ret[0].(clock.TimeSource) return ret0 } // GetTimeSource indicates an expected call of GetTimeSource. func (mr *MockResourceMockRecorder) GetTimeSource() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeSource", reflect.TypeOf((*MockResource)(nil).GetTimeSource)) } // GetVisibilityManager mocks base method. func (m *MockResource) GetVisibilityManager() persistence.VisibilityManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVisibilityManager") ret0, _ := ret[0].(persistence.VisibilityManager) return ret0 } // GetVisibilityManager indicates an expected call of GetVisibilityManager. func (mr *MockResourceMockRecorder) GetVisibilityManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVisibilityManager", reflect.TypeOf((*MockResource)(nil).GetVisibilityManager)) } // Start mocks base method. func (m *MockResource) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockResourceMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockResource)(nil).Start)) } // Stop mocks base method. func (m *MockResource) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockResourceMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockResource)(nil).Stop)) } ================================================ FILE: service/history/resource/resource_test_utils.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package resource import ( "testing" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas/global/algorithm" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/worker/archiver" ) type ( // Test is the test implementation used for testing Test struct { *resource.Test EventCache *events.MockCache ratelimiterAlgorithm algorithm.RequestWeighted archiverClient archiver.Client } ) var _ Resource = (*Test)(nil) // NewTest returns a new test resource instance func NewTest( t *testing.T, controller *gomock.Controller, serviceMetricsIndex metrics.ServiceIdx, ) *Test { return &Test{ Test: resource.NewTest(t, controller, serviceMetricsIndex), EventCache: events.NewMockCache(controller), archiverClient: archiver.NewMockClient(controller), } } // GetEventCache for testing func (s *Test) GetEventCache() events.Cache { return s.EventCache } func (s *Test) GetRatelimiterAlgorithm() algorithm.RequestWeighted { return s.ratelimiterAlgorithm } func (s *Test) GetArchiverClient() archiver.Client { return s.archiverClient } ================================================ FILE: service/history/service.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package history import ( "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/dynamicconfig/quotas" "github.com/uber/cadence/common/log/tag" commonResource "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/handler" "github.com/uber/cadence/service/history/resource" "github.com/uber/cadence/service/history/workflowcache" "github.com/uber/cadence/service/history/wrappers/grpc" "github.com/uber/cadence/service/history/wrappers/ratelimited" "github.com/uber/cadence/service/history/wrappers/thrift" ) const ( workflowIDCacheTTL = 1 * time.Second workflowIDCacheMaxCount = 10_000 ) // Service represents the cadence-history service type Service struct { resource.Resource status int32 handler handler.Handler stopC chan struct{} params *commonResource.Params config *config.Config } // NewService builds a new cadence-history service func NewService( params *commonResource.Params, ) (resource.Resource, error) { serviceConfig := config.New( dynamicconfig.NewCollection( params.DynamicConfig, params.Logger, dynamicproperties.ClusterNameFilter(params.ClusterMetadata.GetCurrentClusterName()), ), params.PersistenceConfig.NumHistoryShards, params.RPCFactory.GetMaxMessageSize(), params.PersistenceConfig.IsAdvancedVisibilityConfigExist(), params.HostName) serviceResource, err := resource.New( params, service.History, serviceConfig, ) if err != nil { return nil, err } return &Service{ Resource: serviceResource, status: common.DaemonStatusInitialized, stopC: make(chan struct{}), params: params, config: serviceConfig, }, nil } // Start starts the service func (s *Service) Start() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } logger := s.GetLogger() logger.Info("elastic search config", tag.ESConfig(s.params.ESConfig)) logger.Info("history starting") wfIDCache := workflowcache.New(workflowcache.Params{ TTL: workflowIDCacheTTL, ExternalLimiterFactory: quotas.NewSimpleDynamicRateLimiterFactory(s.config.WorkflowIDExternalRPS), InternalLimiterFactory: quotas.NewSimpleDynamicRateLimiterFactory(s.config.WorkflowIDInternalRPS), MaxCount: workflowIDCacheMaxCount, DomainCache: s.Resource.GetDomainCache(), Logger: s.Resource.GetLogger(), MetricsClient: s.Resource.GetMetricsClient(), }) rawHandler := handler.NewHandler(s.Resource, s.config, wfIDCache) s.handler = ratelimited.NewHistoryHandler( rawHandler, wfIDCache, s.Resource.GetLogger(), ) thriftHandler := thrift.NewThriftHandler(s.handler) thriftHandler.Register(s.GetDispatcher()) grpcHandler := grpc.NewGRPCHandler(s.handler) grpcHandler.Register(s.GetDispatcher()) // must start resource first s.Resource.Start() s.handler.Start() logger.Info("history started") <-s.stopC } // Stop stops the service func (s *Service) Stop() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } // initiate graceful shutdown : // 1. remove self from the membership ring // 2. wait for other members to discover we are going down // 3. stop acquiring new shards (periodically or based on other membership changes) // 4. wait for shard ownership to transfer (and inflight requests to drain) while still accepting new requests // 5. Reject all requests arriving at rpc handler to avoid taking on more work except for RespondXXXCompleted and // RecordXXStarted APIs - for these APIs, most of the work is already one and rejecting at last stage is // probably not that desirable. If the shard is closed, these requests will fail anyways. // 6. wait for grace period // 7. force stop the whole world and return const gossipPropagationDelay = 400 * time.Millisecond const gracePeriod = 2 * time.Second remainingTime := s.config.ShutdownDrainDuration() s.GetLogger().Info("ShutdownHandler: Evicting self from membership ring") s.GetMembershipResolver().EvictSelf() s.GetLogger().Info("ShutdownHandler: Waiting for others to discover I am unhealthy") remainingTime = common.SleepWithMinDuration(gossipPropagationDelay, remainingTime) remainingTime = s.handler.PrepareToStop(remainingTime) _ = common.SleepWithMinDuration(gracePeriod, remainingTime) close(s.stopC) s.handler.Stop() s.Resource.Stop() s.GetLogger().Info("history stopped") } ================================================ FILE: service/history/shard/context.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination context_mock.go -package shard github.com/uber/cadence/history/shard/context Context package shard import ( "context" "errors" "fmt" "math" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/resource" "github.com/uber/cadence/service/history/simulation" ) type ( // Context represents a history engine shard Context interface { GetShardID() int GetService() resource.Resource GetExecutionManager() persistence.ExecutionManager GetHistoryManager() persistence.HistoryManager GetDomainCache() cache.DomainCache GetActiveClusterManager() activecluster.Manager GetClusterMetadata() cluster.Metadata GetConfig() *config.Config GetEventsCache() events.Cache GetLogger() log.Logger GetThrottledLogger() log.Logger GetMetricsClient() metrics.Client GetTimeSource() clock.TimeSource PreviousShardOwnerWasDifferent() bool GetReplicationBudgetManager() cache.Manager GetEngine() engine.Engine SetEngine(engine.Engine) GenerateTaskID() (int64, error) GenerateTaskIDs(number int) ([]int64, error) UpdateIfNeededAndGetQueueMaxReadLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey SetCurrentTime(cluster string, currentTime time.Time) GetCurrentTime(cluster string) time.Time GetLastUpdatedTime() time.Time GetQueueAckLevel(category persistence.HistoryTaskCategory) persistence.HistoryTaskKey UpdateQueueAckLevel(category persistence.HistoryTaskCategory, ackLevel persistence.HistoryTaskKey) error GetQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey UpdateQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string, ackLevel persistence.HistoryTaskKey) error GetQueueState(category persistence.HistoryTaskCategory) (*types.QueueState, error) UpdateQueueState(category persistence.HistoryTaskCategory, state *types.QueueState) error GetTransferProcessingQueueStates(cluster string) []*types.ProcessingQueueState UpdateTransferProcessingQueueStates(cluster string, states []*types.ProcessingQueueState) error GetTimerProcessingQueueStates(cluster string) []*types.ProcessingQueueState UpdateTimerProcessingQueueStates(cluster string, states []*types.ProcessingQueueState) error UpdateFailoverLevel(category persistence.HistoryTaskCategory, failoverID string, level persistence.FailoverLevel) error DeleteFailoverLevel(category persistence.HistoryTaskCategory, failoverID string) error GetAllFailoverLevels(category persistence.HistoryTaskCategory) map[string]persistence.FailoverLevel GetDomainNotificationVersion() int64 UpdateDomainNotificationVersion(domainNotificationVersion int64) error GetWorkflowExecution(ctx context.Context, request *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) CreateWorkflowExecution(ctx context.Context, request *persistence.CreateWorkflowExecutionRequest) (*persistence.CreateWorkflowExecutionResponse, error) UpdateWorkflowExecution(ctx context.Context, request *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) ConflictResolveWorkflowExecution(ctx context.Context, request *persistence.ConflictResolveWorkflowExecutionRequest) (*persistence.ConflictResolveWorkflowExecutionResponse, error) AppendHistoryV2Events(ctx context.Context, request *persistence.AppendHistoryNodesRequest, domainID string, execution types.WorkflowExecution) (*persistence.AppendHistoryNodesResponse, error) ReplicateFailoverMarkers(ctx context.Context, markers []*persistence.FailoverMarkerTask) error AddingPendingFailoverMarker(*types.FailoverMarkerAttributes) error ValidateAndUpdateFailoverMarkers() ([]*types.FailoverMarkerAttributes, error) } contextImpl struct { resource.Resource shardItem *historyShardsItem shardID int rangeID int64 executionManager persistence.ExecutionManager activeClusterManager activecluster.Manager eventsCache events.Cache closeCallback func(int, *historyShardsItem) closedAt atomic.Pointer[time.Time] config *config.Config persistenceConfig *persistence.DynamicConfiguration logger log.Logger throttledLogger log.Logger engine engine.Engine replicationBudgetManager cache.Manager sync.RWMutex lastUpdated time.Time shardInfo *persistence.ShardInfo taskSequenceNumber int64 maxTaskSequenceNumber int64 immediateTaskMaxReadLevel int64 scheduledTaskMaxReadLevelMap map[string]time.Time // cluster -> timerMaxReadLevel failoverLevels map[persistence.HistoryTaskCategory]map[string]persistence.FailoverLevel // category -> uuid -> FailoverLevel // exist only in memory remoteClusterCurrentTime map[string]time.Time // true if previous owner was different from the acquirer's identity. previousShardOwnerWasDifferent bool } ) var _ Context = (*contextImpl)(nil) type ErrShardClosed struct { Msg string ClosedAt time.Time } var _ error = (*ErrShardClosed)(nil) func (e *ErrShardClosed) Error() string { return e.Msg } const ( TimeBeforeShardClosedIsError = 10 * time.Second ) const ( // transfer/cross cluster diff/lag is in terms of taskID, which is calculated based on shard rangeID // on shard movement, taskID will increase by around 1 million logWarnTransferLevelDiff = 3000000 // 3 million logWarnCrossClusterLevelLag = 3000000 // 3 million logWarnTimerLevelDiff = time.Duration(30 * time.Minute) historySizeLogThreshold = 10 * 1024 * 1024 minContextTimeout = 1 * time.Second activeClusterLookupTimeout = 1 * time.Second ) func (s *contextImpl) GetShardID() int { return s.shardID } func (s *contextImpl) GetService() resource.Resource { return s.Resource } func (s *contextImpl) GetExecutionManager() persistence.ExecutionManager { return s.executionManager } func (s *contextImpl) GetEngine() engine.Engine { return s.engine } func (s *contextImpl) SetEngine(engine engine.Engine) { s.engine = engine } func (s *contextImpl) GetActiveClusterManager() activecluster.Manager { return s.activeClusterManager } func (s *contextImpl) GenerateTaskID() (int64, error) { s.Lock() defer s.Unlock() return s.generateTaskIDLocked() } func (s *contextImpl) GenerateTaskIDs(number int) ([]int64, error) { s.Lock() defer s.Unlock() result := []int64{} for i := 0; i < number; i++ { id, err := s.generateTaskIDLocked() if err != nil { return nil, err } result = append(result, id) } return result, nil } func (s *contextImpl) UpdateIfNeededAndGetQueueMaxReadLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey { switch category.Type() { case persistence.HistoryTaskCategoryTypeImmediate: return s.getImmediateTaskMaxReadLevel() case persistence.HistoryTaskCategoryTypeScheduled: return s.updateScheduledTaskMaxReadLevel(cluster) default: s.logger.Fatal("unknown history task category", tag.Dynamic("category-type", category.Type())) } return persistence.HistoryTaskKey{} } func (s *contextImpl) getImmediateTaskMaxReadLevel() persistence.HistoryTaskKey { s.RLock() defer s.RUnlock() return persistence.NewImmediateTaskKey(s.immediateTaskMaxReadLevel) } func (s *contextImpl) updateScheduledTaskMaxReadLevel(cluster string) persistence.HistoryTaskKey { s.Lock() defer s.Unlock() currentTime := s.GetTimeSource().Now() if cluster != "" && cluster != s.GetClusterMetadata().GetCurrentClusterName() { currentTime = s.remoteClusterCurrentTime[cluster] } newMaxReadLevel := currentTime.Add(s.config.TimerProcessorMaxTimeShift()).Truncate(persistence.DBTimestampMinPrecision) if newMaxReadLevel.After(s.scheduledTaskMaxReadLevelMap[cluster]) { s.scheduledTaskMaxReadLevelMap[cluster] = newMaxReadLevel } return persistence.NewHistoryTaskKey(s.scheduledTaskMaxReadLevelMap[cluster], 0) } func (s *contextImpl) GetQueueAckLevel(category persistence.HistoryTaskCategory) persistence.HistoryTaskKey { s.RLock() defer s.RUnlock() return s.getQueueAckLevelLocked(category) } func (s *contextImpl) getQueueAckLevelLocked(category persistence.HistoryTaskCategory) persistence.HistoryTaskKey { switch category { case persistence.HistoryTaskCategoryTransfer: return persistence.NewImmediateTaskKey(s.shardInfo.TransferAckLevel) case persistence.HistoryTaskCategoryTimer: return persistence.NewHistoryTaskKey(s.shardInfo.TimerAckLevel, 0) case persistence.HistoryTaskCategoryReplication: return persistence.NewImmediateTaskKey(s.shardInfo.ReplicationAckLevel) default: return persistence.HistoryTaskKey{} } } func (s *contextImpl) UpdateQueueAckLevel(category persistence.HistoryTaskCategory, ackLevel persistence.HistoryTaskKey) error { s.Lock() defer s.Unlock() switch category { case persistence.HistoryTaskCategoryTransfer: s.shardInfo.TransferAckLevel = ackLevel.GetTaskID() // for forward compatibility s.shardInfo.QueueStates[int32(persistence.HistoryTaskCategoryIDTransfer)] = &types.QueueState{ ExclusiveMaxReadLevel: &types.TaskKey{TaskID: ackLevel.GetTaskID() + 1}, } case persistence.HistoryTaskCategoryTimer: s.shardInfo.TimerAckLevel = ackLevel.GetScheduledTime() // for forward compatibility s.shardInfo.QueueStates[int32(persistence.HistoryTaskCategoryIDTimer)] = &types.QueueState{ ExclusiveMaxReadLevel: &types.TaskKey{ScheduledTimeNano: ackLevel.GetScheduledTime().UnixNano()}, } case persistence.HistoryTaskCategoryReplication: s.shardInfo.ReplicationAckLevel = ackLevel.GetTaskID() default: return fmt.Errorf("unknown history task category: %v", category) } s.shardInfo.StolenSinceRenew = 0 return s.updateShardInfoLocked() } func (s *contextImpl) GetQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey { s.RLock() defer s.RUnlock() switch category { case persistence.HistoryTaskCategoryTransfer: // if we can find corresponding ack level if ackLevel, ok := s.shardInfo.ClusterTransferAckLevel[cluster]; ok { return persistence.NewImmediateTaskKey(ackLevel) } // otherwise, default to existing ack level, which belongs to local cluster // this can happen if you add more cluster return persistence.NewImmediateTaskKey(s.shardInfo.TransferAckLevel) case persistence.HistoryTaskCategoryTimer: // if we can find corresponding ack level if ackLevel, ok := s.shardInfo.ClusterTimerAckLevel[cluster]; ok { return persistence.NewHistoryTaskKey(ackLevel, 0) } // otherwise, default to existing ack level, which belongs to local cluster // this can happen if you add more cluster return persistence.NewHistoryTaskKey(s.shardInfo.TimerAckLevel, 0) case persistence.HistoryTaskCategoryReplication: // if we can find corresponding replication level if replicationLevel, ok := s.shardInfo.ClusterReplicationLevel[cluster]; ok { return persistence.NewImmediateTaskKey(replicationLevel) } // New cluster always starts from -1 return persistence.NewImmediateTaskKey(-1) default: return persistence.HistoryTaskKey{} } } func (s *contextImpl) UpdateQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string, ackLevel persistence.HistoryTaskKey) error { s.Lock() defer s.Unlock() switch category { case persistence.HistoryTaskCategoryTransfer: s.shardInfo.ClusterTransferAckLevel[cluster] = ackLevel.GetTaskID() case persistence.HistoryTaskCategoryTimer: s.shardInfo.ClusterTimerAckLevel[cluster] = ackLevel.GetScheduledTime() case persistence.HistoryTaskCategoryReplication: s.shardInfo.ClusterReplicationLevel[cluster] = ackLevel.GetTaskID() default: return fmt.Errorf("unknown history task category: %v", category) } s.shardInfo.StolenSinceRenew = 0 return s.updateShardInfoLocked() } func (s *contextImpl) GetQueueState(category persistence.HistoryTaskCategory) (*types.QueueState, error) { s.RLock() defer s.RUnlock() queueState, ok := s.shardInfo.QueueStates[int32(category.ID())] if !ok { switch category { case persistence.HistoryTaskCategoryTransfer: queueState = &types.QueueState{ ExclusiveMaxReadLevel: &types.TaskKey{TaskID: s.shardInfo.TransferAckLevel + 1}, } case persistence.HistoryTaskCategoryTimer: queueState = &types.QueueState{ ExclusiveMaxReadLevel: &types.TaskKey{ScheduledTimeNano: s.shardInfo.TimerAckLevel.UnixNano()}, } default: return nil, fmt.Errorf("unknown history task category: %v", category) } } return queueState, nil } func (s *contextImpl) UpdateQueueState(category persistence.HistoryTaskCategory, state *types.QueueState) error { s.Lock() defer s.Unlock() s.shardInfo.QueueStates[int32(category.ID())] = state // for backward compatibility // we must make sure that there is no task being missed when converting the queue state // it's ok to have tasks being processed multiple times // for immediate tasks the exclusive level should be converted back to inclusive level switch category { case persistence.HistoryTaskCategoryTransfer: ackLevel := state.ExclusiveMaxReadLevel.TaskID for _, virtualQueueState := range state.VirtualQueueStates { for _, virtualSliceState := range virtualQueueState.VirtualSliceStates { ackLevel = min(ackLevel, virtualSliceState.TaskRange.InclusiveMin.TaskID) } } // exclusive to inclusive ackLevel = ackLevel - 1 s.shardInfo.TransferAckLevel = ackLevel for clusterName := range s.GetClusterMetadata().GetEnabledClusterInfo() { s.shardInfo.ClusterTransferAckLevel[clusterName] = ackLevel if s.shardInfo.TransferProcessingQueueStates.StatesByCluster == nil { s.shardInfo.TransferProcessingQueueStates.StatesByCluster = make(map[string][]*types.ProcessingQueueState) } s.shardInfo.TransferProcessingQueueStates.StatesByCluster[clusterName] = []*types.ProcessingQueueState{ { Level: common.Int32Ptr(0), AckLevel: common.Int64Ptr(ackLevel), MaxLevel: common.Int64Ptr(math.MaxInt64), DomainFilter: &types.DomainFilter{ ReverseMatch: true, }, }, } } case persistence.HistoryTaskCategoryTimer: ackLevel := state.ExclusiveMaxReadLevel.ScheduledTimeNano for _, virtualQueueState := range state.VirtualQueueStates { for _, virtualSliceState := range virtualQueueState.VirtualSliceStates { ackLevel = min(ackLevel, virtualSliceState.TaskRange.InclusiveMin.ScheduledTimeNano) } } s.shardInfo.TimerAckLevel = time.Unix(0, ackLevel) for clusterName := range s.GetClusterMetadata().GetEnabledClusterInfo() { s.shardInfo.ClusterTimerAckLevel[clusterName] = time.Unix(0, ackLevel) if s.shardInfo.TimerProcessingQueueStates.StatesByCluster == nil { s.shardInfo.TimerProcessingQueueStates.StatesByCluster = make(map[string][]*types.ProcessingQueueState) } s.shardInfo.TimerProcessingQueueStates.StatesByCluster[clusterName] = []*types.ProcessingQueueState{ { Level: common.Int32Ptr(0), AckLevel: common.Int64Ptr(ackLevel), MaxLevel: common.Int64Ptr(math.MaxInt64), DomainFilter: &types.DomainFilter{ ReverseMatch: true, }, }, } } case persistence.HistoryTaskCategoryReplication: return fmt.Errorf("replication queue state is not supported") } s.shardInfo.StolenSinceRenew = 0 return s.updateShardInfoLocked() } func (s *contextImpl) GetTransferProcessingQueueStates(cluster string) []*types.ProcessingQueueState { s.RLock() defer s.RUnlock() // if we can find corresponding processing queue states if states, ok := s.shardInfo.TransferProcessingQueueStates.StatesByCluster[cluster]; ok { return states } // check if we can find corresponding ack level var ackLevel int64 var ok bool if ackLevel, ok = s.shardInfo.ClusterTransferAckLevel[cluster]; !ok { // otherwise, default to existing ack level, which belongs to local cluster // this can happen if you add more cluster ackLevel = s.shardInfo.TransferAckLevel } // otherwise, create default queue state based on existing ack level, // which belongs to local cluster. this can happen if you add more cluster return []*types.ProcessingQueueState{ { Level: common.Int32Ptr(0), AckLevel: common.Int64Ptr(ackLevel), MaxLevel: common.Int64Ptr(math.MaxInt64), DomainFilter: &types.DomainFilter{ ReverseMatch: true, }, }, } } func (s *contextImpl) UpdateTransferProcessingQueueStates(cluster string, states []*types.ProcessingQueueState) error { s.Lock() defer s.Unlock() if len(states) == 0 { return errors.New("empty transfer processing queue states") } if s.shardInfo.TransferProcessingQueueStates.StatesByCluster == nil { s.shardInfo.TransferProcessingQueueStates.StatesByCluster = make(map[string][]*types.ProcessingQueueState) } s.shardInfo.TransferProcessingQueueStates.StatesByCluster[cluster] = states // for backward compatibility ackLevel := states[0].GetAckLevel() for _, state := range states { ackLevel = min(ackLevel, state.GetAckLevel()) } s.shardInfo.ClusterTransferAckLevel[cluster] = ackLevel s.shardInfo.StolenSinceRenew = 0 return s.updateShardInfoLocked() } func (s *contextImpl) GetTimerProcessingQueueStates(cluster string) []*types.ProcessingQueueState { s.RLock() defer s.RUnlock() // if we can find corresponding processing queue states if states, ok := s.shardInfo.TimerProcessingQueueStates.StatesByCluster[cluster]; ok { return states } // check if we can find corresponding ack level var ackLevel time.Time var ok bool if ackLevel, ok = s.shardInfo.ClusterTimerAckLevel[cluster]; !ok { // otherwise, default to existing ack level, which belongs to local cluster // this can happen if you add more cluster ackLevel = s.shardInfo.TimerAckLevel } // otherwise, create default queue state based on existing ack level, // which belongs to local cluster. this can happen if you add more cluster return []*types.ProcessingQueueState{ { Level: common.Int32Ptr(0), AckLevel: common.Int64Ptr(ackLevel.UnixNano()), MaxLevel: common.Int64Ptr(math.MaxInt64), DomainFilter: &types.DomainFilter{ ReverseMatch: true, }, }, } } func (s *contextImpl) UpdateTimerProcessingQueueStates(cluster string, states []*types.ProcessingQueueState) error { s.Lock() defer s.Unlock() if len(states) == 0 { return errors.New("empty transfer processing queue states") } if s.shardInfo.TimerProcessingQueueStates.StatesByCluster == nil { s.shardInfo.TimerProcessingQueueStates.StatesByCluster = make(map[string][]*types.ProcessingQueueState) } s.shardInfo.TimerProcessingQueueStates.StatesByCluster[cluster] = states // for backward compatibility ackLevel := states[0].GetAckLevel() for _, state := range states { ackLevel = min(ackLevel, state.GetAckLevel()) } s.shardInfo.ClusterTimerAckLevel[cluster] = time.Unix(0, ackLevel) s.shardInfo.StolenSinceRenew = 0 return s.updateShardInfoLocked() } func (s *contextImpl) UpdateFailoverLevel(category persistence.HistoryTaskCategory, failoverID string, level persistence.FailoverLevel) error { s.Lock() defer s.Unlock() if _, ok := s.failoverLevels[category]; !ok { s.failoverLevels[category] = make(map[string]persistence.FailoverLevel) } s.failoverLevels[category][failoverID] = level return nil } func (s *contextImpl) DeleteFailoverLevel(category persistence.HistoryTaskCategory, failoverID string) error { s.Lock() defer s.Unlock() if levels, ok := s.failoverLevels[category]; ok { if level, ok := levels[failoverID]; ok { delete(levels, failoverID) switch category { case persistence.HistoryTaskCategoryTransfer: s.GetMetricsClient().RecordTimer(metrics.ShardInfoScope, metrics.ShardInfoTransferFailoverLatencyTimer, time.Since(level.StartTime)) case persistence.HistoryTaskCategoryTimer: s.GetMetricsClient().RecordTimer(metrics.ShardInfoScope, metrics.ShardInfoTimerFailoverLatencyTimer, time.Since(level.StartTime)) } return nil } } return nil } func (s *contextImpl) GetAllFailoverLevels(category persistence.HistoryTaskCategory) map[string]persistence.FailoverLevel { s.RLock() defer s.RUnlock() ret := map[string]persistence.FailoverLevel{} for k, v := range s.failoverLevels[category] { ret[k] = v } return ret } func (s *contextImpl) GetDomainNotificationVersion() int64 { s.RLock() defer s.RUnlock() return s.shardInfo.DomainNotificationVersion } func (s *contextImpl) UpdateDomainNotificationVersion(domainNotificationVersion int64) error { s.Lock() defer s.Unlock() s.shardInfo.DomainNotificationVersion = domainNotificationVersion return s.updateShardInfoLocked() } func (s *contextImpl) GetWorkflowExecution( ctx context.Context, request *persistence.GetWorkflowExecutionRequest, ) (*persistence.GetWorkflowExecutionResponse, error) { request.RangeID = atomic.LoadInt64(&s.rangeID) // This is to make sure read is not blocked by write, s.rangeID is synced with s.shardInfo.RangeID if err := s.closedError(); err != nil { return nil, err } return s.executionManager.GetWorkflowExecution(ctx, request) } func (s *contextImpl) CreateWorkflowExecution( ctx context.Context, request *persistence.CreateWorkflowExecutionRequest, ) (*persistence.CreateWorkflowExecutionResponse, error) { if err := s.closedError(); err != nil { return nil, err } ctx, cancel, err := s.ensureMinContextTimeout(ctx) if err != nil { return nil, err } if cancel != nil { defer cancel() } domainID := request.NewWorkflowSnapshot.ExecutionInfo.DomainID workflowID := request.NewWorkflowSnapshot.ExecutionInfo.WorkflowID // do not try to get domain cache within shard lock domainEntry, err := s.GetDomainCache().GetDomainByID(domainID) if err != nil { return nil, err } s.Lock() defer s.Unlock() immediateTaskMaxReadLevel := int64(0) if err := s.allocateTaskIDsLocked( domainEntry, workflowID, request.NewWorkflowSnapshot.TasksByCategory, &immediateTaskMaxReadLevel, ); err != nil { return nil, err } if err := s.closedError(); err != nil { return nil, err } currentRangeID := s.getRangeID() request.RangeID = currentRangeID response, err := s.executionManager.CreateWorkflowExecution(ctx, request) switch err.(type) { case nil: // Update MaxReadLevel if write to DB succeeds s.updateMaxReadLevelLocked(immediateTaskMaxReadLevel) s.logCreateWorkflowExecutionEvents(request) return response, nil case *types.WorkflowExecutionAlreadyStartedError, *persistence.WorkflowExecutionAlreadyStartedError, *persistence.CurrentWorkflowConditionFailedError, *persistence.DuplicateRequestError, *types.ServiceBusyError: // No special handling required for these errors // We know write to DB fails if these errors are returned return nil, err case *persistence.ShardOwnershipLostError: { // Shard is stolen, trigger shutdown of history engine s.logger.Warn( "Closing shard: CreateWorkflowExecution failed due to stolen shard.", tag.Error(err), ) s.closeShard() return nil, err } default: { // We have no idea if the write failed or will eventually make it to // persistence. Increment RangeID to guarantee that subsequent reads // will either see that write, or know for certain that it failed. // This allows the callers to reliably check the outcome by performing // a read. err1 := s.renewRangeLocked(false) if err1 != nil { // At this point we have no choice but to unload the shard, so that it // gets a new RangeID when it's reloaded. s.logger.Warn( "Closing shard: CreateWorkflowExecution failed due to unknown error.", tag.Error(err), ) s.closeShard() } return nil, err } } } func (s *contextImpl) getDefaultEncoding(domainName string) constants.EncodingType { return constants.EncodingType(s.config.EventEncodingType(domainName)) } func (s *contextImpl) UpdateWorkflowExecution( ctx context.Context, request *persistence.UpdateWorkflowExecutionRequest, ) (*persistence.UpdateWorkflowExecutionResponse, error) { if err := s.closedError(); err != nil { return nil, err } ctx, cancel, err := s.ensureMinContextTimeout(ctx) if err != nil { return nil, err } if cancel != nil { defer cancel() } domainID := request.UpdateWorkflowMutation.ExecutionInfo.DomainID workflowID := request.UpdateWorkflowMutation.ExecutionInfo.WorkflowID // do not try to get domain cache within shard lock domainEntry, err := s.GetDomainCache().GetDomainByID(domainID) if err != nil { return nil, err } request.Encoding = s.getDefaultEncoding(domainEntry.GetInfo().Name) s.Lock() defer s.Unlock() immediateTaskMaxReadLevel := int64(0) if err := s.allocateTaskIDsLocked( domainEntry, workflowID, request.UpdateWorkflowMutation.TasksByCategory, &immediateTaskMaxReadLevel, ); err != nil { return nil, err } if request.NewWorkflowSnapshot != nil { if err := s.allocateTaskIDsLocked( domainEntry, workflowID, request.NewWorkflowSnapshot.TasksByCategory, &immediateTaskMaxReadLevel, ); err != nil { return nil, err } } if err := s.closedError(); err != nil { return nil, err } currentRangeID := s.getRangeID() request.RangeID = currentRangeID resp, err := s.executionManager.UpdateWorkflowExecution(ctx, request) switch err.(type) { case nil: // Update MaxReadLevel if write to DB succeeds s.updateMaxReadLevelLocked(immediateTaskMaxReadLevel) s.logUpdateWorkflowExecutionEvents(request) return resp, nil case *persistence.ConditionFailedError, *persistence.DuplicateRequestError, *types.ServiceBusyError: // No special handling required for these errors // We know write to DB fails if these errors are returned return nil, err case *persistence.ShardOwnershipLostError: { // Shard is stolen, trigger shutdown of history engine s.logger.Warn( "Closing shard: UpdateWorkflowExecution failed due to stolen shard.", tag.Error(err), ) s.closeShard() return nil, err } default: { // We have no idea if the write failed or will eventually make it to // persistence. Increment RangeID to guarantee that subsequent reads // will either see that write, or know for certain that it failed. // This allows the callers to reliably check the outcome by performing // a read. err1 := s.renewRangeLocked(false) if err1 != nil { // At this point we have no choice but to unload the shard, so that it // gets a new RangeID when it's reloaded. s.logger.Warn( "Closing shard: UpdateWorkflowExecution failed due to unknown error.", tag.Error(err), ) s.closeShard() } return nil, err } } } func (s *contextImpl) ConflictResolveWorkflowExecution( ctx context.Context, request *persistence.ConflictResolveWorkflowExecutionRequest, ) (*persistence.ConflictResolveWorkflowExecutionResponse, error) { if err := s.closedError(); err != nil { return nil, err } ctx, cancel, err := s.ensureMinContextTimeout(ctx) if err != nil { return nil, err } if cancel != nil { defer cancel() } domainID := request.ResetWorkflowSnapshot.ExecutionInfo.DomainID workflowID := request.ResetWorkflowSnapshot.ExecutionInfo.WorkflowID // do not try to get domain cache within shard lock domainEntry, err := s.GetDomainCache().GetDomainByID(domainID) if err != nil { return nil, err } request.Encoding = s.getDefaultEncoding(domainEntry.GetInfo().Name) s.Lock() defer s.Unlock() immediateTaskMaxReadLevel := int64(0) if request.CurrentWorkflowMutation != nil { if err := s.allocateTaskIDsLocked( domainEntry, workflowID, request.CurrentWorkflowMutation.TasksByCategory, &immediateTaskMaxReadLevel, ); err != nil { return nil, err } } if err := s.allocateTaskIDsLocked( domainEntry, workflowID, request.ResetWorkflowSnapshot.TasksByCategory, &immediateTaskMaxReadLevel, ); err != nil { return nil, err } if request.NewWorkflowSnapshot != nil { if err := s.allocateTaskIDsLocked( domainEntry, workflowID, request.NewWorkflowSnapshot.TasksByCategory, &immediateTaskMaxReadLevel, ); err != nil { return nil, err } } if err := s.closedError(); err != nil { return nil, err } currentRangeID := s.getRangeID() request.RangeID = currentRangeID resp, err := s.executionManager.ConflictResolveWorkflowExecution(ctx, request) switch err.(type) { case nil: // Update MaxReadLevel if write to DB succeeds s.updateMaxReadLevelLocked(immediateTaskMaxReadLevel) s.logConflictResolveWorkflowExecutionEvents(request) return resp, nil case *persistence.ConditionFailedError, *types.ServiceBusyError: // No special handling required for these errors // We know write to DB fails if these errors are returned return nil, err case *persistence.ShardOwnershipLostError: { // RangeID might have been renewed by the same host while this update was in flight // Retry the operation if we still have the shard ownership // Shard is stolen, trigger shutdown of history engine s.logger.Warn( "Closing shard: ConflictResolveWorkflowExecution failed due to stolen shard.", tag.Error(err), ) s.closeShard() return nil, err } default: { // We have no idea if the write failed or will eventually make it to // persistence. Increment RangeID to guarantee that subsequent reads // will either see that write, or know for certain that it failed. // This allows the callers to reliably check the outcome by performing // a read. err1 := s.renewRangeLocked(false) if err1 != nil { // At this point we have no choice but to unload the shard, so that it // gets a new RangeID when it's reloaded. s.logger.Warn( "Closing shard: ConflictResolveWorkflowExecution failed due to unknown error.", tag.Error(err), ) s.closeShard() } return nil, err } } } func (s *contextImpl) ensureMinContextTimeout( parent context.Context, ) (context.Context, context.CancelFunc, error) { if err := parent.Err(); err != nil { return nil, nil, err } deadline, ok := parent.Deadline() if !ok || deadline.Sub(s.GetTimeSource().Now()) >= minContextTimeout { return parent, nil, nil } childCtx, cancel := context.WithTimeout(context.Background(), minContextTimeout) return childCtx, cancel, nil } func (s *contextImpl) AppendHistoryV2Events( ctx context.Context, request *persistence.AppendHistoryNodesRequest, domainID string, execution types.WorkflowExecution, ) (*persistence.AppendHistoryNodesResponse, error) { if err := s.closedError(); err != nil { return nil, err } domainName, err := s.GetDomainCache().GetDomainName(domainID) if err != nil { return nil, err } // NOTE: do not use generateNextTransferTaskIDLocked since // generateNextTransferTaskIDLocked is not guarded by lock transactionID, err := s.GenerateTaskID() if err != nil { return nil, err } request.Encoding = s.getDefaultEncoding(domainName) request.ShardID = common.IntPtr(s.shardID) request.TransactionID = transactionID size := 0 defer func() { s.GetMetricsClient().Scope(metrics.SessionSizeStatsScope, metrics.DomainTag(domainName)). RecordTimer(metrics.HistorySize, time.Duration(size)) if size >= historySizeLogThreshold { s.throttledLogger.Warn("history size threshold breached", tag.WorkflowID(execution.GetWorkflowID()), tag.WorkflowRunID(execution.GetRunID()), tag.WorkflowDomainID(domainID), tag.WorkflowHistorySizeBytes(size)) } }() resp, err0 := s.GetHistoryManager().AppendHistoryNodes(ctx, request) if resp != nil { size = len(resp.DataBlob.Data) } return resp, err0 } func (s *contextImpl) GetConfig() *config.Config { return s.config } func (s *contextImpl) PreviousShardOwnerWasDifferent() bool { return s.previousShardOwnerWasDifferent } func (s *contextImpl) GetEventsCache() events.Cache { // the shard needs to be restarted to release the shard cache once global mode is on. if s.config.EventsCacheGlobalEnable() { return s.GetEventCache() } return s.eventsCache } func (s *contextImpl) GetLogger() log.Logger { return s.logger } func (s *contextImpl) GetThrottledLogger() log.Logger { return s.throttledLogger } func (s *contextImpl) GetReplicationBudgetManager() cache.Manager { return s.replicationBudgetManager } func (s *contextImpl) getRangeID() int64 { return s.shardInfo.RangeID } func (s *contextImpl) closedError() error { closedAt := s.closedAt.Load() if closedAt == nil { return nil } return &ErrShardClosed{ Msg: "shard closed", ClosedAt: *closedAt, } } func (s *contextImpl) closeShard() { if !s.closedAt.CompareAndSwap(nil, common.TimePtr(time.Now())) { return } s.logger.Info("Shard context closeShard called") go func() { s.closeCallback(s.shardID, s.shardItem) }() // fails any writes that may start after this point. s.shardInfo.RangeID = -1 atomic.StoreInt64(&s.rangeID, s.shardInfo.RangeID) } func (s *contextImpl) generateTaskIDLocked() (int64, error) { if err := s.updateRangeIfNeededLocked(); err != nil { return -1, err } taskID := s.taskSequenceNumber s.taskSequenceNumber++ return taskID, nil } func (s *contextImpl) updateRangeIfNeededLocked() error { if s.taskSequenceNumber < s.maxTaskSequenceNumber { return nil } return s.renewRangeLocked(false) } func (s *contextImpl) renewRangeLocked(isStealing bool) error { updatedShardInfo := s.shardInfo.ToNilSafeCopy() updatedShardInfo.RangeID++ if isStealing { updatedShardInfo.StolenSinceRenew++ } var err error if err := s.closedError(); err != nil { return err } err = s.GetShardManager().UpdateShard(context.Background(), &persistence.UpdateShardRequest{ ShardInfo: updatedShardInfo, PreviousRangeID: s.shardInfo.RangeID}) switch err.(type) { case nil: case *persistence.ShardOwnershipLostError: // Shard is stolen, trigger history engine shutdown s.logger.Warn( "Closing shard: renewRangeLocked failed due to stolen shard.", tag.Error(err), ) s.closeShard() default: s.logger.Warn("UpdateShard failed with an unknown error.", tag.Error(err), tag.ShardRangeID(updatedShardInfo.RangeID), tag.PreviousShardRangeID(s.shardInfo.RangeID)) } if err != nil { // Failure in updating shard to grab new RangeID s.logger.Error("renewRangeLocked failed.", tag.StoreOperationUpdateShard, tag.Error(err), tag.ShardRangeID(updatedShardInfo.RangeID), tag.PreviousShardRangeID(s.shardInfo.RangeID)) return err } // Range is successfully updated in cassandra now update shard context to reflect new range s.taskSequenceNumber = updatedShardInfo.RangeID << s.config.RangeSizeBits s.maxTaskSequenceNumber = (updatedShardInfo.RangeID + 1) << s.config.RangeSizeBits s.immediateTaskMaxReadLevel = s.taskSequenceNumber - 1 atomic.StoreInt64(&s.rangeID, updatedShardInfo.RangeID) s.shardInfo = updatedShardInfo s.logger.Info("Range updated for shardID", tag.ShardRangeID(s.shardInfo.RangeID), tag.Number(s.taskSequenceNumber), tag.NextNumber(s.maxTaskSequenceNumber)) return nil } func (s *contextImpl) updateMaxReadLevelLocked(rl int64) { if rl > s.immediateTaskMaxReadLevel { s.logger.Debug(fmt.Sprintf("Updating MaxReadLevel: %v", rl)) s.immediateTaskMaxReadLevel = rl } } func (s *contextImpl) updateShardInfoLocked() error { return s.persistShardInfoLocked(false) } func (s *contextImpl) forceUpdateShardInfoLocked() error { return s.persistShardInfoLocked(true) } func (s *contextImpl) persistShardInfoLocked( isForced bool, ) error { if err := s.closedError(); err != nil { return err } var err error now := clock.NewRealTimeSource().Now() if !isForced && s.lastUpdated.Add(s.config.ShardUpdateMinInterval()).After(now) { return nil } updatedShardInfo := s.shardInfo.ToNilSafeCopy() s.emitShardInfoMetricsLogsLocked() err = s.GetShardManager().UpdateShard(context.Background(), &persistence.UpdateShardRequest{ ShardInfo: updatedShardInfo, PreviousRangeID: s.shardInfo.RangeID, }) if err != nil { // Shard is stolen, trigger history engine shutdown if _, ok := err.(*persistence.ShardOwnershipLostError); ok { s.logger.Warn( "Closing shard: updateShardInfoLocked failed due to stolen shard.", tag.Error(err), ) s.closeShard() } } else { s.lastUpdated = now } return err } func (s *contextImpl) emitShardInfoMetricsLogsLocked() { currentCluster := s.GetClusterMetadata().GetCurrentClusterName() clusterInfo := s.GetClusterMetadata().GetAllClusterInfo() minTransferLevel := s.shardInfo.ClusterTransferAckLevel[currentCluster] maxTransferLevel := s.shardInfo.ClusterTransferAckLevel[currentCluster] for clusterName, v := range s.shardInfo.ClusterTransferAckLevel { if !clusterInfo[clusterName].Enabled { continue } if v < minTransferLevel { minTransferLevel = v } if v > maxTransferLevel { maxTransferLevel = v } } diffTransferLevel := maxTransferLevel - minTransferLevel minTimerLevel := s.shardInfo.ClusterTimerAckLevel[currentCluster] maxTimerLevel := s.shardInfo.ClusterTimerAckLevel[currentCluster] for clusterName, v := range s.shardInfo.ClusterTimerAckLevel { if !clusterInfo[clusterName].Enabled { continue } if v.Before(minTimerLevel) { minTimerLevel = v } if v.After(maxTimerLevel) { maxTimerLevel = v } } diffTimerLevel := maxTimerLevel.Sub(minTimerLevel) replicationLag := s.immediateTaskMaxReadLevel - s.shardInfo.ReplicationAckLevel transferLag := s.immediateTaskMaxReadLevel - s.shardInfo.TransferAckLevel timerLag := time.Since(s.shardInfo.TimerAckLevel) transferFailoverInProgress := len(s.failoverLevels[persistence.HistoryTaskCategoryTransfer]) timerFailoverInProgress := len(s.failoverLevels[persistence.HistoryTaskCategoryTimer]) if s.config.EmitShardDiffLog() && (logWarnTransferLevelDiff < diffTransferLevel || logWarnTimerLevelDiff < diffTimerLevel || logWarnTransferLevelDiff < transferLag || logWarnTimerLevelDiff < timerLag) { logger := s.logger.WithTags( tag.ShardTime(s.remoteClusterCurrentTime), tag.ShardReplicationAck(s.shardInfo.ReplicationAckLevel), tag.ShardTimerAcks(s.shardInfo.ClusterTimerAckLevel), tag.ShardTransferAcks(s.shardInfo.ClusterTransferAckLevel), ) logger.Warn("Shard ack levels diff exceeds warn threshold.") } metricsScope := s.GetMetricsClient().Scope(metrics.ShardInfoScope) metricsScope.RecordTimer(metrics.ShardInfoTransferDiffTimer, time.Duration(diffTransferLevel)) metricsScope.RecordTimer(metrics.ShardInfoTimerDiffTimer, diffTimerLevel) metricsScope.RecordTimer(metrics.ShardInfoReplicationLagTimer, time.Duration(replicationLag)) metricsScope.RecordTimer(metrics.ShardInfoTransferLagTimer, time.Duration(transferLag)) metricsScope.RecordTimer(metrics.ShardInfoTimerLagTimer, timerLag) metricsScope.RecordTimer(metrics.ShardInfoTransferFailoverInProgressTimer, time.Duration(transferFailoverInProgress)) metricsScope.RecordTimer(metrics.ShardInfoTimerFailoverInProgressTimer, time.Duration(timerFailoverInProgress)) } func (s *contextImpl) allocateTaskIDsLocked( domainEntry *cache.DomainCacheEntry, workflowID string, tasksByCategory map[persistence.HistoryTaskCategory][]persistence.Task, immediateTaskMaxReadLevel *int64, ) error { var err error var replicationTasks []persistence.Task for c, tasks := range tasksByCategory { switch c.Type() { case persistence.HistoryTaskCategoryTypeImmediate: if c.ID() == persistence.HistoryTaskCategoryIDReplication { replicationTasks = tasks continue } err = s.allocateTransferIDsLocked(tasks, immediateTaskMaxReadLevel) case persistence.HistoryTaskCategoryTypeScheduled: err = s.allocateTimerIDsLocked(domainEntry, workflowID, tasks) } if err != nil { return err } } // Ensure that task IDs for replication tasks are generated last. // This allows optimizing replication by checking whether there no potential tasks to read. return s.allocateTransferIDsLocked( replicationTasks, immediateTaskMaxReadLevel, ) } func (s *contextImpl) allocateTransferIDsLocked( tasks []persistence.Task, immediateTaskMaxReadLevel *int64, ) error { now := s.GetTimeSource().Now() for _, task := range tasks { id, err := s.generateTaskIDLocked() if err != nil { return err } s.logger.Debug(fmt.Sprintf("Assigning task ID: %v", id)) task.SetTaskID(id) // only set task visibility timestamp if it's not set if task.GetVisibilityTimestamp().IsZero() { task.SetVisibilityTimestamp(now) } *immediateTaskMaxReadLevel = id } return nil } // NOTE: allocateTimerIDsLocked should always been called after assigning taskID for transferTasks when assigning taskID together, // because Cadence Indexer assume timer taskID of deleteWorkflowExecution is larger than transfer taskID of closeWorkflowExecution // for a given workflow. func (s *contextImpl) allocateTimerIDsLocked( domainEntry *cache.DomainCacheEntry, workflowID string, timerTasks []persistence.Task, ) error { now := s.GetTimeSource().Now().Truncate(persistence.DBTimestampMinPrecision) // assign IDs for the timer tasks. They need to be assigned under shard lock. cluster := s.GetClusterMetadata().GetCurrentClusterName() for _, task := range timerTasks { ts := task.GetVisibilityTimestamp().Truncate(persistence.DBTimestampMinPrecision) // always use current cluster's max read level for queue v2, and this is safe for rollback, // because if we go back to queue v1, the standby queue and active queue will start from the same ack level to read tasks if task.GetVersion() != constants.EmptyVersion && !s.GetConfig().EnableTimerQueueV2(s.shardID) { // cannot use version to determine the corresponding cluster for timer task // this is because during failover, timer task should be created as active // or otherwise, failover + active processing logic may not pick up the task. cluster = domainEntry.GetReplicationConfig().ActiveClusterName if domainEntry.GetReplicationConfig().IsActiveActive() { // Note: This doesn't work for initial backoff timer task because the workflow's active-cluster-selection-policy row is not stored yet. // Therefore GetActiveClusterInfoByWorkflow returns current cluster (fallback logic in activecluster manager) // Queue v2 doesn't use this logic and it must be enabled to properly handle initial backoff timer task for active-active domains. // Leaving this code block instead of rejecting the whole id allocation request. // Active-active domains should not be used in Cadence clusters that don't have queue v2 enabled. ctx, cancel := context.WithTimeout(context.Background(), activeClusterLookupTimeout) lookupRes, err := s.GetActiveClusterManager().GetActiveClusterInfoByWorkflow(ctx, task.GetDomainID(), task.GetWorkflowID(), task.GetRunID()) cancel() if err != nil { return err } cluster = lookupRes.ActiveClusterName } } readCursorTS := s.scheduledTaskMaxReadLevelMap[cluster] // make sure scheduled task timestamp is higher than // 1. max read level, so that queue processor can read the task back. // 2. current time. Otherwise the task timestamp is in the past and causes aritical load latency in queue processor metrics. // Above cases can happen if shard move and new host have a time SKU, // or there is db write delay, or we are simply (re-)generating tasks for an old workflow. if ts.Before(readCursorTS) { // This can happen if shard move and new host have a time SKU, or there is db write delay. // We generate a new timer ID using timerMaxReadLevel. s.logger.Warn("New timer generated is less than read level", tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowID), tag.Timestamp(ts), tag.CursorTimestamp(readCursorTS), tag.ClusterName(cluster), tag.ValueShardAllocateTimerBeforeRead) ts = readCursorTS.Add(persistence.DBTimestampMinPrecision) } if ts.Before(now) { s.logger.Warn("New timer generated is in the past", tag.WorkflowDomainID(domainEntry.GetInfo().ID), tag.WorkflowID(workflowID), tag.Timestamp(ts), tag.ValueShardAllocateTimerBeforeRead) ts = now.Add(persistence.DBTimestampMinPrecision) } task.SetVisibilityTimestamp(ts) seqNum, err := s.generateTaskIDLocked() if err != nil { return err } task.SetTaskID(seqNum) visibilityTs := task.GetVisibilityTimestamp() s.logger.Debug(fmt.Sprintf("Assigning new timer (timestamp: %v, seq: %v)) ackLeveL: %v", visibilityTs, task.GetTaskID(), s.shardInfo.TimerAckLevel)) } return nil } func (s *contextImpl) SetCurrentTime(cluster string, currentTime time.Time) { s.Lock() defer s.Unlock() if cluster != s.GetClusterMetadata().GetCurrentClusterName() { prevTime := s.remoteClusterCurrentTime[cluster] if prevTime.Before(currentTime) { s.remoteClusterCurrentTime[cluster] = currentTime } } else { panic("Cannot set current time for current cluster") } } func (s *contextImpl) GetCurrentTime(cluster string) time.Time { s.RLock() defer s.RUnlock() if cluster != s.GetClusterMetadata().GetCurrentClusterName() { return s.remoteClusterCurrentTime[cluster] } return s.GetTimeSource().Now() } func (s *contextImpl) GetLastUpdatedTime() time.Time { s.RLock() defer s.RUnlock() return s.lastUpdated } func (s *contextImpl) ReplicateFailoverMarkers( ctx context.Context, markers []*persistence.FailoverMarkerTask, ) error { if err := s.closedError(); err != nil { return err } tasks := make([]persistence.Task, 0, len(markers)) for _, marker := range markers { tasks = append(tasks, marker) } s.Lock() defer s.Unlock() immediateTaskMaxReadLevel := int64(0) if err := s.allocateTransferIDsLocked( tasks, &immediateTaskMaxReadLevel, ); err != nil { return err } var err error if err := s.closedError(); err != nil { return err } err = s.executionManager.CreateFailoverMarkerTasks( ctx, &persistence.CreateFailoverMarkersRequest{ RangeID: s.getRangeID(), Markers: markers, }, ) switch err.(type) { case nil: // Update MaxReadLevel if write to DB succeeds s.updateMaxReadLevelLocked(immediateTaskMaxReadLevel) case *persistence.ShardOwnershipLostError: // do not retry on ShardOwnershipLostError s.logger.Warn( "Closing shard: ReplicateFailoverMarkers failed due to stolen shard.", tag.Error(err), ) s.closeShard() default: s.logger.Error( "Failed to insert the failover marker into replication queue.", tag.Error(err), ) } return err } func (s *contextImpl) AddingPendingFailoverMarker( marker *types.FailoverMarkerAttributes, ) error { domainEntry, err := s.GetDomainCache().GetDomainByID(marker.GetDomainID()) if err != nil { return err } // domain is active, the marker is expired isActive := domainEntry.IsActiveIn(s.GetClusterMetadata().GetCurrentClusterName()) if isActive || domainEntry.GetFailoverVersion() > marker.GetFailoverVersion() { s.logger.Info("Skipped out-of-date failover marker", tag.WorkflowDomainName(domainEntry.GetInfo().Name)) return nil } s.Lock() defer s.Unlock() s.shardInfo.PendingFailoverMarkers = append(s.shardInfo.PendingFailoverMarkers, marker) return s.forceUpdateShardInfoLocked() } func (s *contextImpl) ValidateAndUpdateFailoverMarkers() ([]*types.FailoverMarkerAttributes, error) { completedFailoverMarkers := make(map[*types.FailoverMarkerAttributes]struct{}) var pendingMarkers []*types.FailoverMarkerAttributes s.RLock() // Get a copy of pending markers while holding read lock pendingMarkers = make([]*types.FailoverMarkerAttributes, len(s.shardInfo.PendingFailoverMarkers)) copy(pendingMarkers, s.shardInfo.PendingFailoverMarkers) for _, marker := range s.shardInfo.PendingFailoverMarkers { domainEntry, err := s.GetDomainCache().GetDomainByID(marker.GetDomainID()) if err != nil { s.RUnlock() return nil, err } isActive := domainEntry.IsActiveIn(s.GetClusterMetadata().GetCurrentClusterName()) domainStatus := domainEntry.GetInfo().Status // Drop failover markers if domain is already active in the currentCluster // or domain have been failed over // or domain is deprecated if isActive || domainEntry.GetFailoverVersion() > marker.GetFailoverVersion() || domainStatus == persistence.DomainStatusDeprecated { completedFailoverMarkers[marker] = struct{}{} } } s.RUnlock() if len(completedFailoverMarkers) == 0 { // No markers to clean up, return the copy return pendingMarkers, nil } // clean up all pending failover tasks s.Lock() defer s.Unlock() // Re-read the current state since it might have changed currentPendingMarkers := s.shardInfo.PendingFailoverMarkers remainingMarkers := make([]*types.FailoverMarkerAttributes, 0, len(currentPendingMarkers)) for _, marker := range currentPendingMarkers { if _, ok := completedFailoverMarkers[marker]; !ok { remainingMarkers = append(remainingMarkers, marker) } } s.shardInfo.PendingFailoverMarkers = remainingMarkers if err := s.updateShardInfoLocked(); err != nil { return nil, err } return s.shardInfo.PendingFailoverMarkers, nil } func acquireShard( shardItem *historyShardsItem, closeCallback func(int, *historyShardsItem), ) (Context, error) { var shardInfo *persistence.ShardInfo retryPolicy := backoff.NewExponentialRetryPolicy(50 * time.Millisecond) retryPolicy.SetMaximumInterval(time.Second) retryPolicy.SetExpirationInterval(5 * time.Second) retryPredicate := func(err error) bool { if persistence.IsTransientError(err) { return true } _, ok := err.(*persistence.ShardAlreadyExistError) return ok } getShard := func(ctx context.Context) error { resp, err := shardItem.GetShardManager().GetShard(ctx, &persistence.GetShardRequest{ ShardID: shardItem.shardID, }) if err == nil { shardInfo = resp.ShardInfo return nil } if _, ok := err.(*types.EntityNotExistsError); !ok { return err } // EntityNotExistsError error shardInfo = &persistence.ShardInfo{ ShardID: shardItem.shardID, RangeID: 0, TransferAckLevel: 0, } return shardItem.GetShardManager().CreateShard(ctx, &persistence.CreateShardRequest{ShardInfo: shardInfo}) } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(retryPredicate), ) err := throttleRetry.Do(context.Background(), getShard) if err != nil { shardItem.logger.Error("Fail to acquire shard.", tag.Error(err)) return nil, err } updatedShardInfo := shardInfo.ToNilSafeCopy() ownershipChanged := shardInfo.Owner != shardItem.GetHostInfo().Identity() updatedShardInfo.Owner = shardItem.GetHostInfo().Identity() // initialize the cluster current time to be the same as ack level remoteClusterCurrentTime := make(map[string]time.Time) // TODO: get this information from QueueState once TimerAckLevel field is deprecated scheduledTaskMaxReadLevelMap := make(map[string]time.Time) for clusterName := range shardItem.GetClusterMetadata().GetEnabledClusterInfo() { if clusterName != shardItem.GetClusterMetadata().GetCurrentClusterName() { if currentTime, ok := shardInfo.ClusterTimerAckLevel[clusterName]; ok { remoteClusterCurrentTime[clusterName] = currentTime scheduledTaskMaxReadLevelMap[clusterName] = currentTime } else { remoteClusterCurrentTime[clusterName] = shardInfo.TimerAckLevel scheduledTaskMaxReadLevelMap[clusterName] = shardInfo.TimerAckLevel } } else { // active cluster scheduledTaskMaxReadLevelMap[clusterName] = shardInfo.TimerAckLevel } scheduledTaskMaxReadLevelMap[clusterName] = scheduledTaskMaxReadLevelMap[clusterName].Truncate(persistence.DBTimestampMinPrecision) } executionMgr, err := shardItem.GetExecutionManager(shardItem.shardID) if err != nil { return nil, err } context := &contextImpl{ Resource: shardItem.Resource, shardItem: shardItem, shardID: shardItem.shardID, executionManager: executionMgr, activeClusterManager: shardItem.GetActiveClusterManager(), shardInfo: updatedShardInfo, closeCallback: closeCallback, config: shardItem.config, remoteClusterCurrentTime: remoteClusterCurrentTime, scheduledTaskMaxReadLevelMap: scheduledTaskMaxReadLevelMap, // use ack to init read level failoverLevels: make(map[persistence.HistoryTaskCategory]map[string]persistence.FailoverLevel), logger: shardItem.logger, throttledLogger: shardItem.throttledLogger, previousShardOwnerWasDifferent: ownershipChanged, replicationBudgetManager: shardItem.replicationBudgetManager, } // TODO remove once migrated to global event cache context.eventsCache = events.NewCache( context.shardID, context.Resource.GetHistoryManager(), context.config, context.logger, context.Resource.GetMetricsClient(), shardItem.GetDomainCache(), ) context.logger.Debug(fmt.Sprintf("Global event cache mode: %v", context.config.EventsCacheGlobalEnable())) err1 := context.renewRangeLocked(true) if err1 != nil { return nil, err1 } return context, nil } func (s *contextImpl) getEventsFromWorkflowSnapshot(snapshot *persistence.WorkflowSnapshot) []simulation.E { if snapshot == nil { return nil } var events []simulation.E for category, tasks := range snapshot.TasksByCategory { for _, task := range tasks { events = append(events, simulation.E{ EventName: simulation.EventNameCreateHistoryTask, Host: s.config.HostName, ShardID: s.shardID, DomainID: task.GetDomainID(), WorkflowID: task.GetWorkflowID(), RunID: task.GetRunID(), Payload: map[string]any{ "task_category": category.Name(), "task_type": task.GetTaskType(), "task_key": task.GetTaskKey(), }, }) } } return events } func (s *contextImpl) getEventsFromWorkflowMutation(mutation *persistence.WorkflowMutation) []simulation.E { if mutation == nil { return nil } var events []simulation.E for category, tasks := range mutation.TasksByCategory { for _, task := range tasks { events = append(events, simulation.E{ EventName: simulation.EventNameCreateHistoryTask, Host: s.config.HostName, ShardID: s.shardID, DomainID: task.GetDomainID(), WorkflowID: task.GetWorkflowID(), RunID: task.GetRunID(), Payload: map[string]any{ "task_category": category.Name(), "task_type": task.GetTaskType(), "task_key": task.GetTaskKey(), }, }) } } return events } func (s *contextImpl) logCreateWorkflowExecutionEvents(request *persistence.CreateWorkflowExecutionRequest) { if !simulation.Enabled() { return } events := s.getEventsFromWorkflowSnapshot(&request.NewWorkflowSnapshot) simulation.LogEvents(events...) } func (s *contextImpl) logUpdateWorkflowExecutionEvents(request *persistence.UpdateWorkflowExecutionRequest) { if !simulation.Enabled() { return } events := s.getEventsFromWorkflowMutation(&request.UpdateWorkflowMutation) simulation.LogEvents(events...) events = s.getEventsFromWorkflowSnapshot(request.NewWorkflowSnapshot) simulation.LogEvents(events...) } func (s *contextImpl) logConflictResolveWorkflowExecutionEvents(request *persistence.ConflictResolveWorkflowExecutionRequest) { if !simulation.Enabled() { return } events := s.getEventsFromWorkflowMutation(request.CurrentWorkflowMutation) simulation.LogEvents(events...) events = s.getEventsFromWorkflowSnapshot(&request.ResetWorkflowSnapshot) simulation.LogEvents(events...) events = s.getEventsFromWorkflowSnapshot(request.NewWorkflowSnapshot) simulation.LogEvents(events...) } ================================================ FILE: service/history/shard/context_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: context.go // // Generated by this command: // // mockgen -package shard -source context.go -destination context_mock.go -package shard github.com/uber/cadence/history/shard/context Context // // Package shard is a generated GoMock package. package shard import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" activecluster "github.com/uber/cadence/common/activecluster" cache "github.com/uber/cadence/common/cache" clock "github.com/uber/cadence/common/clock" cluster "github.com/uber/cadence/common/cluster" log "github.com/uber/cadence/common/log" metrics "github.com/uber/cadence/common/metrics" persistence "github.com/uber/cadence/common/persistence" types "github.com/uber/cadence/common/types" config "github.com/uber/cadence/service/history/config" engine "github.com/uber/cadence/service/history/engine" events "github.com/uber/cadence/service/history/events" resource "github.com/uber/cadence/service/history/resource" ) // MockContext is a mock of Context interface. type MockContext struct { ctrl *gomock.Controller recorder *MockContextMockRecorder isgomock struct{} } // MockContextMockRecorder is the mock recorder for MockContext. type MockContextMockRecorder struct { mock *MockContext } // NewMockContext creates a new mock instance. func NewMockContext(ctrl *gomock.Controller) *MockContext { mock := &MockContext{ctrl: ctrl} mock.recorder = &MockContextMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockContext) EXPECT() *MockContextMockRecorder { return m.recorder } // AddingPendingFailoverMarker mocks base method. func (m *MockContext) AddingPendingFailoverMarker(arg0 *types.FailoverMarkerAttributes) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddingPendingFailoverMarker", arg0) ret0, _ := ret[0].(error) return ret0 } // AddingPendingFailoverMarker indicates an expected call of AddingPendingFailoverMarker. func (mr *MockContextMockRecorder) AddingPendingFailoverMarker(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddingPendingFailoverMarker", reflect.TypeOf((*MockContext)(nil).AddingPendingFailoverMarker), arg0) } // AppendHistoryV2Events mocks base method. func (m *MockContext) AppendHistoryV2Events(ctx context.Context, request *persistence.AppendHistoryNodesRequest, domainID string, execution types.WorkflowExecution) (*persistence.AppendHistoryNodesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AppendHistoryV2Events", ctx, request, domainID, execution) ret0, _ := ret[0].(*persistence.AppendHistoryNodesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AppendHistoryV2Events indicates an expected call of AppendHistoryV2Events. func (mr *MockContextMockRecorder) AppendHistoryV2Events(ctx, request, domainID, execution any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendHistoryV2Events", reflect.TypeOf((*MockContext)(nil).AppendHistoryV2Events), ctx, request, domainID, execution) } // ConflictResolveWorkflowExecution mocks base method. func (m *MockContext) ConflictResolveWorkflowExecution(ctx context.Context, request *persistence.ConflictResolveWorkflowExecutionRequest) (*persistence.ConflictResolveWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConflictResolveWorkflowExecution", ctx, request) ret0, _ := ret[0].(*persistence.ConflictResolveWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ConflictResolveWorkflowExecution indicates an expected call of ConflictResolveWorkflowExecution. func (mr *MockContextMockRecorder) ConflictResolveWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConflictResolveWorkflowExecution", reflect.TypeOf((*MockContext)(nil).ConflictResolveWorkflowExecution), ctx, request) } // CreateWorkflowExecution mocks base method. func (m *MockContext) CreateWorkflowExecution(ctx context.Context, request *persistence.CreateWorkflowExecutionRequest) (*persistence.CreateWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateWorkflowExecution", ctx, request) ret0, _ := ret[0].(*persistence.CreateWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateWorkflowExecution indicates an expected call of CreateWorkflowExecution. func (mr *MockContextMockRecorder) CreateWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateWorkflowExecution", reflect.TypeOf((*MockContext)(nil).CreateWorkflowExecution), ctx, request) } // DeleteFailoverLevel mocks base method. func (m *MockContext) DeleteFailoverLevel(category persistence.HistoryTaskCategory, failoverID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteFailoverLevel", category, failoverID) ret0, _ := ret[0].(error) return ret0 } // DeleteFailoverLevel indicates an expected call of DeleteFailoverLevel. func (mr *MockContextMockRecorder) DeleteFailoverLevel(category, failoverID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFailoverLevel", reflect.TypeOf((*MockContext)(nil).DeleteFailoverLevel), category, failoverID) } // GenerateTaskID mocks base method. func (m *MockContext) GenerateTaskID() (int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateTaskID") ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GenerateTaskID indicates an expected call of GenerateTaskID. func (mr *MockContextMockRecorder) GenerateTaskID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateTaskID", reflect.TypeOf((*MockContext)(nil).GenerateTaskID)) } // GenerateTaskIDs mocks base method. func (m *MockContext) GenerateTaskIDs(number int) ([]int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GenerateTaskIDs", number) ret0, _ := ret[0].([]int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GenerateTaskIDs indicates an expected call of GenerateTaskIDs. func (mr *MockContextMockRecorder) GenerateTaskIDs(number any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateTaskIDs", reflect.TypeOf((*MockContext)(nil).GenerateTaskIDs), number) } // GetActiveClusterManager mocks base method. func (m *MockContext) GetActiveClusterManager() activecluster.Manager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveClusterManager") ret0, _ := ret[0].(activecluster.Manager) return ret0 } // GetActiveClusterManager indicates an expected call of GetActiveClusterManager. func (mr *MockContextMockRecorder) GetActiveClusterManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterManager", reflect.TypeOf((*MockContext)(nil).GetActiveClusterManager)) } // GetAllFailoverLevels mocks base method. func (m *MockContext) GetAllFailoverLevels(category persistence.HistoryTaskCategory) map[string]persistence.FailoverLevel { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllFailoverLevels", category) ret0, _ := ret[0].(map[string]persistence.FailoverLevel) return ret0 } // GetAllFailoverLevels indicates an expected call of GetAllFailoverLevels. func (mr *MockContextMockRecorder) GetAllFailoverLevels(category any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllFailoverLevels", reflect.TypeOf((*MockContext)(nil).GetAllFailoverLevels), category) } // GetClusterMetadata mocks base method. func (m *MockContext) GetClusterMetadata() cluster.Metadata { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetClusterMetadata") ret0, _ := ret[0].(cluster.Metadata) return ret0 } // GetClusterMetadata indicates an expected call of GetClusterMetadata. func (mr *MockContextMockRecorder) GetClusterMetadata() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterMetadata", reflect.TypeOf((*MockContext)(nil).GetClusterMetadata)) } // GetConfig mocks base method. func (m *MockContext) GetConfig() *config.Config { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetConfig") ret0, _ := ret[0].(*config.Config) return ret0 } // GetConfig indicates an expected call of GetConfig. func (mr *MockContextMockRecorder) GetConfig() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfig", reflect.TypeOf((*MockContext)(nil).GetConfig)) } // GetCurrentTime mocks base method. func (m *MockContext) GetCurrentTime(cluster string) time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCurrentTime", cluster) ret0, _ := ret[0].(time.Time) return ret0 } // GetCurrentTime indicates an expected call of GetCurrentTime. func (mr *MockContextMockRecorder) GetCurrentTime(cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentTime", reflect.TypeOf((*MockContext)(nil).GetCurrentTime), cluster) } // GetDomainCache mocks base method. func (m *MockContext) GetDomainCache() cache.DomainCache { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainCache") ret0, _ := ret[0].(cache.DomainCache) return ret0 } // GetDomainCache indicates an expected call of GetDomainCache. func (mr *MockContextMockRecorder) GetDomainCache() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainCache", reflect.TypeOf((*MockContext)(nil).GetDomainCache)) } // GetDomainNotificationVersion mocks base method. func (m *MockContext) GetDomainNotificationVersion() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainNotificationVersion") ret0, _ := ret[0].(int64) return ret0 } // GetDomainNotificationVersion indicates an expected call of GetDomainNotificationVersion. func (mr *MockContextMockRecorder) GetDomainNotificationVersion() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainNotificationVersion", reflect.TypeOf((*MockContext)(nil).GetDomainNotificationVersion)) } // GetEngine mocks base method. func (m *MockContext) GetEngine() engine.Engine { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEngine") ret0, _ := ret[0].(engine.Engine) return ret0 } // GetEngine indicates an expected call of GetEngine. func (mr *MockContextMockRecorder) GetEngine() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEngine", reflect.TypeOf((*MockContext)(nil).GetEngine)) } // GetEventsCache mocks base method. func (m *MockContext) GetEventsCache() events.Cache { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEventsCache") ret0, _ := ret[0].(events.Cache) return ret0 } // GetEventsCache indicates an expected call of GetEventsCache. func (mr *MockContextMockRecorder) GetEventsCache() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEventsCache", reflect.TypeOf((*MockContext)(nil).GetEventsCache)) } // GetExecutionManager mocks base method. func (m *MockContext) GetExecutionManager() persistence.ExecutionManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetExecutionManager") ret0, _ := ret[0].(persistence.ExecutionManager) return ret0 } // GetExecutionManager indicates an expected call of GetExecutionManager. func (mr *MockContextMockRecorder) GetExecutionManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExecutionManager", reflect.TypeOf((*MockContext)(nil).GetExecutionManager)) } // GetHistoryManager mocks base method. func (m *MockContext) GetHistoryManager() persistence.HistoryManager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoryManager") ret0, _ := ret[0].(persistence.HistoryManager) return ret0 } // GetHistoryManager indicates an expected call of GetHistoryManager. func (mr *MockContextMockRecorder) GetHistoryManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHistoryManager", reflect.TypeOf((*MockContext)(nil).GetHistoryManager)) } // GetLastUpdatedTime mocks base method. func (m *MockContext) GetLastUpdatedTime() time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLastUpdatedTime") ret0, _ := ret[0].(time.Time) return ret0 } // GetLastUpdatedTime indicates an expected call of GetLastUpdatedTime. func (mr *MockContextMockRecorder) GetLastUpdatedTime() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastUpdatedTime", reflect.TypeOf((*MockContext)(nil).GetLastUpdatedTime)) } // GetLogger mocks base method. func (m *MockContext) GetLogger() log.Logger { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLogger") ret0, _ := ret[0].(log.Logger) return ret0 } // GetLogger indicates an expected call of GetLogger. func (mr *MockContextMockRecorder) GetLogger() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogger", reflect.TypeOf((*MockContext)(nil).GetLogger)) } // GetMetricsClient mocks base method. func (m *MockContext) GetMetricsClient() metrics.Client { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMetricsClient") ret0, _ := ret[0].(metrics.Client) return ret0 } // GetMetricsClient indicates an expected call of GetMetricsClient. func (mr *MockContextMockRecorder) GetMetricsClient() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetricsClient", reflect.TypeOf((*MockContext)(nil).GetMetricsClient)) } // GetQueueAckLevel mocks base method. func (m *MockContext) GetQueueAckLevel(category persistence.HistoryTaskCategory) persistence.HistoryTaskKey { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueAckLevel", category) ret0, _ := ret[0].(persistence.HistoryTaskKey) return ret0 } // GetQueueAckLevel indicates an expected call of GetQueueAckLevel. func (mr *MockContextMockRecorder) GetQueueAckLevel(category any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueAckLevel", reflect.TypeOf((*MockContext)(nil).GetQueueAckLevel), category) } // GetQueueClusterAckLevel mocks base method. func (m *MockContext) GetQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueClusterAckLevel", category, cluster) ret0, _ := ret[0].(persistence.HistoryTaskKey) return ret0 } // GetQueueClusterAckLevel indicates an expected call of GetQueueClusterAckLevel. func (mr *MockContextMockRecorder) GetQueueClusterAckLevel(category, cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueClusterAckLevel", reflect.TypeOf((*MockContext)(nil).GetQueueClusterAckLevel), category, cluster) } // GetQueueState mocks base method. func (m *MockContext) GetQueueState(category persistence.HistoryTaskCategory) (*types.QueueState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueState", category) ret0, _ := ret[0].(*types.QueueState) ret1, _ := ret[1].(error) return ret0, ret1 } // GetQueueState indicates an expected call of GetQueueState. func (mr *MockContextMockRecorder) GetQueueState(category any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueState", reflect.TypeOf((*MockContext)(nil).GetQueueState), category) } // GetReplicationBudgetManager mocks base method. func (m *MockContext) GetReplicationBudgetManager() cache.Manager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReplicationBudgetManager") ret0, _ := ret[0].(cache.Manager) return ret0 } // GetReplicationBudgetManager indicates an expected call of GetReplicationBudgetManager. func (mr *MockContextMockRecorder) GetReplicationBudgetManager() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationBudgetManager", reflect.TypeOf((*MockContext)(nil).GetReplicationBudgetManager)) } // GetService mocks base method. func (m *MockContext) GetService() resource.Resource { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetService") ret0, _ := ret[0].(resource.Resource) return ret0 } // GetService indicates an expected call of GetService. func (mr *MockContextMockRecorder) GetService() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetService", reflect.TypeOf((*MockContext)(nil).GetService)) } // GetShardID mocks base method. func (m *MockContext) GetShardID() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardID") ret0, _ := ret[0].(int) return ret0 } // GetShardID indicates an expected call of GetShardID. func (mr *MockContextMockRecorder) GetShardID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardID", reflect.TypeOf((*MockContext)(nil).GetShardID)) } // GetThrottledLogger mocks base method. func (m *MockContext) GetThrottledLogger() log.Logger { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetThrottledLogger") ret0, _ := ret[0].(log.Logger) return ret0 } // GetThrottledLogger indicates an expected call of GetThrottledLogger. func (mr *MockContextMockRecorder) GetThrottledLogger() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetThrottledLogger", reflect.TypeOf((*MockContext)(nil).GetThrottledLogger)) } // GetTimeSource mocks base method. func (m *MockContext) GetTimeSource() clock.TimeSource { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTimeSource") ret0, _ := ret[0].(clock.TimeSource) return ret0 } // GetTimeSource indicates an expected call of GetTimeSource. func (mr *MockContextMockRecorder) GetTimeSource() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeSource", reflect.TypeOf((*MockContext)(nil).GetTimeSource)) } // GetTimerProcessingQueueStates mocks base method. func (m *MockContext) GetTimerProcessingQueueStates(cluster string) []*types.ProcessingQueueState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTimerProcessingQueueStates", cluster) ret0, _ := ret[0].([]*types.ProcessingQueueState) return ret0 } // GetTimerProcessingQueueStates indicates an expected call of GetTimerProcessingQueueStates. func (mr *MockContextMockRecorder) GetTimerProcessingQueueStates(cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimerProcessingQueueStates", reflect.TypeOf((*MockContext)(nil).GetTimerProcessingQueueStates), cluster) } // GetTransferProcessingQueueStates mocks base method. func (m *MockContext) GetTransferProcessingQueueStates(cluster string) []*types.ProcessingQueueState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTransferProcessingQueueStates", cluster) ret0, _ := ret[0].([]*types.ProcessingQueueState) return ret0 } // GetTransferProcessingQueueStates indicates an expected call of GetTransferProcessingQueueStates. func (mr *MockContextMockRecorder) GetTransferProcessingQueueStates(cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransferProcessingQueueStates", reflect.TypeOf((*MockContext)(nil).GetTransferProcessingQueueStates), cluster) } // GetWorkflowExecution mocks base method. func (m *MockContext) GetWorkflowExecution(ctx context.Context, request *persistence.GetWorkflowExecutionRequest) (*persistence.GetWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowExecution", ctx, request) ret0, _ := ret[0].(*persistence.GetWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetWorkflowExecution indicates an expected call of GetWorkflowExecution. func (mr *MockContextMockRecorder) GetWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowExecution", reflect.TypeOf((*MockContext)(nil).GetWorkflowExecution), ctx, request) } // PreviousShardOwnerWasDifferent mocks base method. func (m *MockContext) PreviousShardOwnerWasDifferent() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PreviousShardOwnerWasDifferent") ret0, _ := ret[0].(bool) return ret0 } // PreviousShardOwnerWasDifferent indicates an expected call of PreviousShardOwnerWasDifferent. func (mr *MockContextMockRecorder) PreviousShardOwnerWasDifferent() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PreviousShardOwnerWasDifferent", reflect.TypeOf((*MockContext)(nil).PreviousShardOwnerWasDifferent)) } // ReplicateFailoverMarkers mocks base method. func (m *MockContext) ReplicateFailoverMarkers(ctx context.Context, markers []*persistence.FailoverMarkerTask) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReplicateFailoverMarkers", ctx, markers) ret0, _ := ret[0].(error) return ret0 } // ReplicateFailoverMarkers indicates an expected call of ReplicateFailoverMarkers. func (mr *MockContextMockRecorder) ReplicateFailoverMarkers(ctx, markers any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplicateFailoverMarkers", reflect.TypeOf((*MockContext)(nil).ReplicateFailoverMarkers), ctx, markers) } // SetCurrentTime mocks base method. func (m *MockContext) SetCurrentTime(cluster string, currentTime time.Time) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetCurrentTime", cluster, currentTime) } // SetCurrentTime indicates an expected call of SetCurrentTime. func (mr *MockContextMockRecorder) SetCurrentTime(cluster, currentTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCurrentTime", reflect.TypeOf((*MockContext)(nil).SetCurrentTime), cluster, currentTime) } // SetEngine mocks base method. func (m *MockContext) SetEngine(arg0 engine.Engine) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetEngine", arg0) } // SetEngine indicates an expected call of SetEngine. func (mr *MockContextMockRecorder) SetEngine(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEngine", reflect.TypeOf((*MockContext)(nil).SetEngine), arg0) } // UpdateDomainNotificationVersion mocks base method. func (m *MockContext) UpdateDomainNotificationVersion(domainNotificationVersion int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateDomainNotificationVersion", domainNotificationVersion) ret0, _ := ret[0].(error) return ret0 } // UpdateDomainNotificationVersion indicates an expected call of UpdateDomainNotificationVersion. func (mr *MockContextMockRecorder) UpdateDomainNotificationVersion(domainNotificationVersion any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDomainNotificationVersion", reflect.TypeOf((*MockContext)(nil).UpdateDomainNotificationVersion), domainNotificationVersion) } // UpdateFailoverLevel mocks base method. func (m *MockContext) UpdateFailoverLevel(category persistence.HistoryTaskCategory, failoverID string, level persistence.FailoverLevel) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateFailoverLevel", category, failoverID, level) ret0, _ := ret[0].(error) return ret0 } // UpdateFailoverLevel indicates an expected call of UpdateFailoverLevel. func (mr *MockContextMockRecorder) UpdateFailoverLevel(category, failoverID, level any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFailoverLevel", reflect.TypeOf((*MockContext)(nil).UpdateFailoverLevel), category, failoverID, level) } // UpdateIfNeededAndGetQueueMaxReadLevel mocks base method. func (m *MockContext) UpdateIfNeededAndGetQueueMaxReadLevel(category persistence.HistoryTaskCategory, cluster string) persistence.HistoryTaskKey { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateIfNeededAndGetQueueMaxReadLevel", category, cluster) ret0, _ := ret[0].(persistence.HistoryTaskKey) return ret0 } // UpdateIfNeededAndGetQueueMaxReadLevel indicates an expected call of UpdateIfNeededAndGetQueueMaxReadLevel. func (mr *MockContextMockRecorder) UpdateIfNeededAndGetQueueMaxReadLevel(category, cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIfNeededAndGetQueueMaxReadLevel", reflect.TypeOf((*MockContext)(nil).UpdateIfNeededAndGetQueueMaxReadLevel), category, cluster) } // UpdateQueueAckLevel mocks base method. func (m *MockContext) UpdateQueueAckLevel(category persistence.HistoryTaskCategory, ackLevel persistence.HistoryTaskKey) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateQueueAckLevel", category, ackLevel) ret0, _ := ret[0].(error) return ret0 } // UpdateQueueAckLevel indicates an expected call of UpdateQueueAckLevel. func (mr *MockContextMockRecorder) UpdateQueueAckLevel(category, ackLevel any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateQueueAckLevel", reflect.TypeOf((*MockContext)(nil).UpdateQueueAckLevel), category, ackLevel) } // UpdateQueueClusterAckLevel mocks base method. func (m *MockContext) UpdateQueueClusterAckLevel(category persistence.HistoryTaskCategory, cluster string, ackLevel persistence.HistoryTaskKey) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateQueueClusterAckLevel", category, cluster, ackLevel) ret0, _ := ret[0].(error) return ret0 } // UpdateQueueClusterAckLevel indicates an expected call of UpdateQueueClusterAckLevel. func (mr *MockContextMockRecorder) UpdateQueueClusterAckLevel(category, cluster, ackLevel any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateQueueClusterAckLevel", reflect.TypeOf((*MockContext)(nil).UpdateQueueClusterAckLevel), category, cluster, ackLevel) } // UpdateQueueState mocks base method. func (m *MockContext) UpdateQueueState(category persistence.HistoryTaskCategory, state *types.QueueState) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateQueueState", category, state) ret0, _ := ret[0].(error) return ret0 } // UpdateQueueState indicates an expected call of UpdateQueueState. func (mr *MockContextMockRecorder) UpdateQueueState(category, state any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateQueueState", reflect.TypeOf((*MockContext)(nil).UpdateQueueState), category, state) } // UpdateTimerProcessingQueueStates mocks base method. func (m *MockContext) UpdateTimerProcessingQueueStates(cluster string, states []*types.ProcessingQueueState) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTimerProcessingQueueStates", cluster, states) ret0, _ := ret[0].(error) return ret0 } // UpdateTimerProcessingQueueStates indicates an expected call of UpdateTimerProcessingQueueStates. func (mr *MockContextMockRecorder) UpdateTimerProcessingQueueStates(cluster, states any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTimerProcessingQueueStates", reflect.TypeOf((*MockContext)(nil).UpdateTimerProcessingQueueStates), cluster, states) } // UpdateTransferProcessingQueueStates mocks base method. func (m *MockContext) UpdateTransferProcessingQueueStates(cluster string, states []*types.ProcessingQueueState) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTransferProcessingQueueStates", cluster, states) ret0, _ := ret[0].(error) return ret0 } // UpdateTransferProcessingQueueStates indicates an expected call of UpdateTransferProcessingQueueStates. func (mr *MockContextMockRecorder) UpdateTransferProcessingQueueStates(cluster, states any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTransferProcessingQueueStates", reflect.TypeOf((*MockContext)(nil).UpdateTransferProcessingQueueStates), cluster, states) } // UpdateWorkflowExecution mocks base method. func (m *MockContext) UpdateWorkflowExecution(ctx context.Context, request *persistence.UpdateWorkflowExecutionRequest) (*persistence.UpdateWorkflowExecutionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateWorkflowExecution", ctx, request) ret0, _ := ret[0].(*persistence.UpdateWorkflowExecutionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateWorkflowExecution indicates an expected call of UpdateWorkflowExecution. func (mr *MockContextMockRecorder) UpdateWorkflowExecution(ctx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkflowExecution", reflect.TypeOf((*MockContext)(nil).UpdateWorkflowExecution), ctx, request) } // ValidateAndUpdateFailoverMarkers mocks base method. func (m *MockContext) ValidateAndUpdateFailoverMarkers() ([]*types.FailoverMarkerAttributes, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateAndUpdateFailoverMarkers") ret0, _ := ret[0].([]*types.FailoverMarkerAttributes) ret1, _ := ret[1].(error) return ret0, ret1 } // ValidateAndUpdateFailoverMarkers indicates an expected call of ValidateAndUpdateFailoverMarkers. func (mr *MockContextMockRecorder) ValidateAndUpdateFailoverMarkers() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAndUpdateFailoverMarkers", reflect.TypeOf((*MockContext)(nil).ValidateAndUpdateFailoverMarkers)) } ================================================ FILE: service/history/shard/context_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shard import ( "context" "errors" "math" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/resource" ) const ( testShardID = 123 testRangeID = 1 testTransferMaxReadLevel = 10 testMaxTransferSequenceNumber = 100 testCluster = "test-cluster" testDomain = "test-domain" testDomainID = "test-domain-id" testWorkflowID = "test-workflow-id" ) type ( contextTestSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockResource *resource.Test mockShardManager *mocks.ShardManager metricsClient metrics.Client logger log.Logger context *contextImpl } ) func TestContextSuite(t *testing.T) { s := new(contextTestSuite) suite.Run(t, s) } func (s *contextTestSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.History) s.mockShardManager = s.mockResource.ShardMgr s.metricsClient = metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) s.logger = testlogger.New(s.T()) s.context = s.newContext() } func (s *contextTestSuite) newContext() *contextImpl { eventsCache := events.NewMockCache(s.controller) config := config.NewForTest() shardInfo := &persistence.ShardInfo{ ShardID: testShardID, RangeID: testRangeID, // the following fields will be initialized // when acquiring the shard if they are nil ClusterTransferAckLevel: make(map[string]int64), ClusterTimerAckLevel: make(map[string]time.Time), ClusterReplicationLevel: make(map[string]int64), TransferProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, TimerProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, QueueStates: make(map[int32]*types.QueueState), } context := &contextImpl{ Resource: s.mockResource, shardID: shardInfo.ShardID, rangeID: shardInfo.RangeID, shardInfo: shardInfo, executionManager: s.mockResource.ExecutionMgr, activeClusterManager: s.mockResource.ActiveClusterMgr, closeCallback: func(i int, item *historyShardsItem) {}, config: config, logger: s.logger, throttledLogger: s.logger, taskSequenceNumber: 1, immediateTaskMaxReadLevel: testTransferMaxReadLevel, maxTaskSequenceNumber: testMaxTransferSequenceNumber, scheduledTaskMaxReadLevelMap: make(map[string]time.Time), remoteClusterCurrentTime: make(map[string]time.Time), failoverLevels: make(map[persistence.HistoryTaskCategory]map[string]persistence.FailoverLevel), eventsCache: eventsCache, } s.Require().True(testMaxTransferSequenceNumber < (1< 0, "Task ID should be positive") } func (s *contextTestSuite) TestAllocateTimerIDsLocked_WhenTaskHasVersionAllocatesNewTaskID() { domainCacheEntry := s.setupAllocateTimerIDsTest() // Enable timer queue v2 s.context.config.EnableTimerQueueV2 = func(int) bool { return true } task := s.createMockTimerTask(createMockTimerTaskParams{ Version: constants.EmptyVersion, Timestamp: time.Now().Add(time.Hour), DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: "test-run-id", }) originalTaskID := task.GetTaskID() err := s.context.allocateTimerIDsLocked(domainCacheEntry, testWorkflowID, []persistence.Task{task}) s.NoError(err) s.NotEqual(originalTaskID, task.GetTaskID(), "Task ID should have been updated") s.True(task.GetTaskID() > 0, "Task ID should be positive") } func (s *contextTestSuite) TestAllocateTimerIDsLocked_WhenTimerQueueV2DisabledUsesReplicationConfigClusterName() { domainInfo := &persistence.DomainInfo{ID: testDomainID} domainConfig := &persistence.DomainConfig{} replicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: "active-cluster", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: testCluster}, }, } domainCacheEntry := cache.NewDomainCacheEntryForTest( domainInfo, domainConfig, false, replicationConfig, 456, nil, 123, 0, 1, ) // Disable timer queue v2 s.context.config.EnableTimerQueueV2 = func(int) bool { return false } task := s.createMockTimerTask(createMockTimerTaskParams{ Version: constants.EmptyVersion, Timestamp: time.Now().Add(time.Hour), DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: "test-run-id", }) originalTaskID := task.GetTaskID() err := s.context.allocateTimerIDsLocked(domainCacheEntry, testWorkflowID, []persistence.Task{task}) s.NoError(err) s.NotEqual(originalTaskID, task.GetTaskID(), "Task ID should have been updated") s.True(task.GetTaskID() > 0, "Task ID should be positive") } func (s *contextTestSuite) TestAllocateTimerIDsLocked_WhenDomainIsActiveActiveUsesClusterManagerLookup() { // Create active-active domain cache entry domainInfo := &persistence.DomainInfo{ID: testDomainID} domainConfig := &persistence.DomainConfig{} replicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: "active-cluster", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: testCluster}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: "active-cluster", FailoverVersion: 456, }, }, }, }, }, } domainCacheEntry := cache.NewDomainCacheEntryForTest( domainInfo, domainConfig, true, replicationConfig, 456, nil, 123, 0, 1, ) // Disable timer queue v2 s.context.config.EnableTimerQueueV2 = func(int) bool { return false } // Setup active cluster manager mock s.mockResource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow( gomock.Any(), testDomainID, testWorkflowID, gomock.Any(), ).Return(&types.ActiveClusterInfo{ ActiveClusterName: "looked-up-cluster", }, nil).Times(1) // Create task with non-empty version to trigger the lookup logic task := s.createMockTimerTask(createMockTimerTaskParams{ Version: 123, Timestamp: time.Now().Add(time.Hour), DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: "test-run-id", }) originalTaskID := task.GetTaskID() err := s.context.allocateTimerIDsLocked(domainCacheEntry, testWorkflowID, []persistence.Task{task}) s.NoError(err) s.NotEqual(originalTaskID, task.GetTaskID(), "Task ID should have been updated") s.True(task.GetTaskID() > 0, "Task ID should be positive") } func (s *contextTestSuite) TestAllocateTimerIDsLocked_WhenTaskTimestampBeforeReadCursorAdjustsTimestamp() { s.mockResource.TimeSource = clock.NewMockedTimeSourceAt(time.Now()) testTimeNow := s.mockResource.TimeSource.Now() domainCacheEntry := s.setupAllocateTimerIDsTest() // Set up scheduled task max read level map with read cursor ahead of task timestamp // Use the actual current cluster name from cluster metadata currentCluster := s.context.GetClusterMetadata().GetCurrentClusterName() readCursor := testTimeNow.Add(time.Second) s.context.scheduledTaskMaxReadLevelMap[currentCluster] = readCursor task := s.createMockTimerTask(createMockTimerTaskParams{ Version: constants.EmptyVersion, Timestamp: readCursor.Add(-time.Second), // before read cursor DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: "test-run-id", }) err := s.context.allocateTimerIDsLocked(domainCacheEntry, testWorkflowID, []persistence.Task{task}) s.NoError(err) // Verify timestamp was adjusted to be after read cursor s.True(task.GetVisibilityTimestamp().After(readCursor), "Task timestamp should be adjusted to be after read cursor") // Verify it's the expected adjusted time (readCursor + DBTimestampMinPrecision) expectedTime := readCursor.Add(persistence.DBTimestampMinPrecision) actualTime := task.GetVisibilityTimestamp() s.Equal(expectedTime.Truncate(persistence.DBTimestampMinPrecision), actualTime.Truncate(persistence.DBTimestampMinPrecision), "Adjusted timestamp should match expected adjusted time") } func (s *contextTestSuite) TestAllocateTimerIDsLocked_WhenTaskTimestampBeforeNow() { s.mockResource.TimeSource = clock.NewMockedTimeSourceAt(time.Now()) testTimeNow := s.mockResource.TimeSource.Now() domainCacheEntry := s.setupAllocateTimerIDsTest() // Set up scheduled task max read level map with read cursor ahead of task timestamp // Use the actual current cluster name from cluster metadata currentCluster := s.context.GetClusterMetadata().GetCurrentClusterName() readCursor := testTimeNow.Add(-2 * time.Second) // read cursor is in the past s.context.scheduledTaskMaxReadLevelMap[currentCluster] = readCursor task := s.createMockTimerTask(createMockTimerTaskParams{ Version: constants.EmptyVersion, Timestamp: readCursor.Add(-time.Second), // before now but after read cursor DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: "test-run-id", }) err := s.context.allocateTimerIDsLocked(domainCacheEntry, testWorkflowID, []persistence.Task{task}) s.NoError(err) // Verify timestamp was adjusted to be after read cursor s.True(task.GetVisibilityTimestamp().After(readCursor), "Task timestamp should be adjusted to be after read cursor") // Verify it's the expected adjusted time (readCursor + DBTimestampMinPrecision) expectedTime := testTimeNow.Add(persistence.DBTimestampMinPrecision) actualTime := task.GetVisibilityTimestamp() s.Equal(expectedTime.Truncate(persistence.DBTimestampMinPrecision), actualTime.Truncate(persistence.DBTimestampMinPrecision), "Adjusted timestamp should match expected adjusted time") } func (s *contextTestSuite) TestAllocateTimerIDsLocked_WhenClusterManagerLookupFailsReturnsError() { // Create active-active domain cache entry domainInfo := &persistence.DomainInfo{ID: testDomainID} domainConfig := &persistence.DomainConfig{} replicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: "active-cluster", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: testCluster}, }, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: "active-cluster", FailoverVersion: 456, }, }, }, }, }, } domainCacheEntry := cache.NewDomainCacheEntryForTest( domainInfo, domainConfig, true, replicationConfig, 456, nil, 123, 0, 1, ) // Disable timer queue v2 s.context.config.EnableTimerQueueV2 = func(int) bool { return false } // Setup active cluster manager mock to return error s.mockResource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow( gomock.Any(), testDomainID, testWorkflowID, gomock.Any(), ).Return(nil, assert.AnError).Times(1) // Create task with non-empty version to trigger the lookup logic task := s.createMockTimerTask(createMockTimerTaskParams{ Version: 123, Timestamp: time.Now().Add(time.Hour), DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: "test-run-id", }) err := s.context.allocateTimerIDsLocked(domainCacheEntry, testWorkflowID, []persistence.Task{task}) s.Error(err) s.Equal(assert.AnError, err) } func (s *contextTestSuite) TestAllocateTimerIDsLocked_WhenTaskIDGenerationFailsReturnsError() { domainCacheEntry := s.setupAllocateTimerIDsTest() // Force task sequence number to exceed max to trigger error originalTaskSequenceNumber := s.context.taskSequenceNumber s.context.taskSequenceNumber = s.context.maxTaskSequenceNumber + 1 s.mockShardManager.On("UpdateShard", mock.Anything, mock.Anything).Return(assert.AnError) defer func() { s.context.taskSequenceNumber = originalTaskSequenceNumber }() task := s.createMockTimerTask(createMockTimerTaskParams{ Version: constants.EmptyVersion, Timestamp: time.Now().Add(time.Hour), DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: "test-run-id", }) err := s.context.allocateTimerIDsLocked(domainCacheEntry, testWorkflowID, []persistence.Task{task}) s.Error(err) s.Equal(assert.AnError, err) } func (s *contextTestSuite) TestAllocateTimerIDsLocked_WhenMultipleTasksProvidedAllocatesAllTaskIDs() { // Create domain cache entry for non-active-active domain domainInfo := &persistence.DomainInfo{ID: testDomainID} domainConfig := &persistence.DomainConfig{} replicationConfig := &persistence.DomainReplicationConfig{ ActiveClusterName: "active-cluster", Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: testCluster}, }, } domainCacheEntry := cache.NewDomainCacheEntryForTest( domainInfo, domainConfig, false, replicationConfig, 456, nil, 123, 0, 1, ) // Disable timer queue v2 s.context.config.EnableTimerQueueV2 = func(int) bool { return false } task1 := s.createMockTimerTask(createMockTimerTaskParams{ Version: constants.EmptyVersion, Timestamp: time.Now().Add(time.Hour), DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: "test-run-id-1", }) task2 := s.createMockTimerTask(createMockTimerTaskParams{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: "test-run-id-2", Version: 456, Timestamp: time.Now().Add(2 * time.Hour), }) originalTaskID1 := task1.GetTaskID() originalTaskID2 := task2.GetTaskID() err := s.context.allocateTimerIDsLocked(domainCacheEntry, testWorkflowID, []persistence.Task{task1, task2}) s.NoError(err) s.NotEqual(originalTaskID1, task1.GetTaskID(), "Task 1 ID should have been updated") s.NotEqual(originalTaskID2, task2.GetTaskID(), "Task 2 ID should have been updated") s.True(task1.GetTaskID() > 0, "Task 1 ID should be positive") s.True(task2.GetTaskID() > 0, "Task 2 ID should be positive") } ================================================ FILE: service/history/shard/context_test_utils.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package shard import ( "testing" "time" "github.com/stretchr/testify/mock" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/resource" ) // TestContext is a test implementation for shard Context interface type TestContext struct { *contextImpl Resource *resource.Test MockEventsCache *events.MockCache MockAddingPendingFailoverMarker func(*types.FailoverMarkerAttributes) error } var _ Context = (*TestContext)(nil) // NewTestContext create a new shardContext for test func NewTestContext( t *testing.T, ctrl *gomock.Controller, shardInfo *persistence.ShardInfo, config *config.Config, ) *TestContext { resource := resource.NewTest(t, ctrl, metrics.History) eventsCache := events.NewMockCache(ctrl) shardInfo = shardInfo.ToNilSafeCopy() shardInfo.ClusterTransferAckLevel = map[string]int64{resource.ClusterMetadata.GetCurrentClusterName(): 3, "standby": 2} shard := &contextImpl{ Resource: resource, shardID: shardInfo.ShardID, rangeID: shardInfo.RangeID, shardInfo: shardInfo, executionManager: resource.ExecutionMgr, activeClusterManager: resource.ActiveClusterMgr, config: config, logger: resource.GetLogger(), throttledLogger: resource.GetThrottledLogger(), taskSequenceNumber: 1, immediateTaskMaxReadLevel: 0, maxTaskSequenceNumber: 100000, scheduledTaskMaxReadLevelMap: make(map[string]time.Time), failoverLevels: make(map[persistence.HistoryTaskCategory]map[string]persistence.FailoverLevel), remoteClusterCurrentTime: make(map[string]time.Time), eventsCache: eventsCache, } return &TestContext{ contextImpl: shard, Resource: resource, MockEventsCache: eventsCache, } } // ShardInfo is a test hook for getting shard info func (s *TestContext) ShardInfo() *persistence.ShardInfo { return s.shardInfo } // SetEventsCache is a test hook for setting events cache func (s *TestContext) SetEventsCache( eventsCache events.Cache, ) { s.eventsCache = eventsCache s.MockEventsCache = nil } // Finish checks whether expectations are met func (s *TestContext) Finish( t mock.TestingT, ) { s.Resource.Finish(t) } func (s *TestContext) AddingPendingFailoverMarker(marker *types.FailoverMarkerAttributes) error { if s.MockAddingPendingFailoverMarker != nil { return s.MockAddingPendingFailoverMarker(marker) } return s.contextImpl.AddingPendingFailoverMarker(marker) } ================================================ FILE: service/history/shard/controller.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination controller_mock.go -self_package github.com/uber/cadence/service/history/shard package shard import ( "fmt" "sync" "sync/atomic" "time" workflow "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/lookup" "github.com/uber/cadence/service/history/resource" ) const ( shardControllerMembershipUpdateListenerName = "ShardController" ) var ( errShardIDOutOfBoundary = &workflow.BadRequestError{Message: "shard ID is out of boundary"} ) type ( // EngineFactory is used to create an instance of sharded history engine EngineFactory interface { CreateEngine(Context) engine.Engine } // Controller controls history service shards Controller interface { common.Daemon // PrepareToStop starts the graceful shutdown process for controller PrepareToStop() GetEngine(workflowID string) (engine.Engine, error) GetEngineForShard(shardID int) (engine.Engine, error) RemoveEngineForShard(shardID int) // Following methods describes the current status of the controller // TODO: consider converting to a unified describe method Status() int32 NumShards() int ShardIDs() []int32 } shardIDSnapshot struct { shardIDs []int32 numShards int } controller struct { resource.Resource membershipUpdateCh chan *membership.ChangedEvent engineFactory EngineFactory status int32 shuttingDown int32 shutdownWG sync.WaitGroup shutdownCh chan struct{} logger log.Logger throttledLogger log.Logger config *config.Config metricsScope metrics.Scope replicationBudgetManager cache.Manager sync.RWMutex historyShards map[int]*historyShardsItem shardIDSnapshot atomic.Pointer[shardIDSnapshot] } historyShardsItemStatus int historyShardsItem struct { resource.Resource shardID int config *config.Config logger log.Logger throttledLogger log.Logger engineFactory EngineFactory replicationBudgetManager cache.Manager sync.RWMutex status historyShardsItemStatus engine engine.Engine } ) const ( historyShardsItemStatusInitialized = iota historyShardsItemStatusStarted historyShardsItemStatusStopped ) // NewShardController creates a new shard controller func NewShardController( resource resource.Resource, factory EngineFactory, config *config.Config, replicationBudgetManager cache.Manager, ) Controller { hostAddress := resource.GetHostInfo().GetAddress() return &controller{ Resource: resource, status: common.DaemonStatusInitialized, membershipUpdateCh: make(chan *membership.ChangedEvent, 10), engineFactory: factory, historyShards: make(map[int]*historyShardsItem), shutdownCh: make(chan struct{}), logger: resource.GetLogger().WithTags(tag.ComponentShardController, tag.Address(hostAddress)), throttledLogger: resource.GetThrottledLogger().WithTags(tag.ComponentShardController, tag.Address(hostAddress)), config: config, metricsScope: resource.GetMetricsClient().Scope(metrics.HistoryShardControllerScope), replicationBudgetManager: replicationBudgetManager, } } func newHistoryShardsItem( resource resource.Resource, shardID int, factory EngineFactory, config *config.Config, replicationBudgetManager cache.Manager, ) (*historyShardsItem, error) { hostAddress := resource.GetHostInfo().GetAddress() return &historyShardsItem{ Resource: resource, shardID: shardID, status: historyShardsItemStatusInitialized, engineFactory: factory, config: config, logger: resource.GetLogger().WithTags(tag.ShardID(shardID), tag.Address(hostAddress)), throttledLogger: resource.GetThrottledLogger().WithTags(tag.ShardID(shardID), tag.Address(hostAddress)), replicationBudgetManager: replicationBudgetManager, }, nil } func (c *controller) Start() { if !atomic.CompareAndSwapInt32(&c.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } c.acquireShards() c.shutdownWG.Add(1) go c.shardManagementPump() err := c.GetMembershipResolver().Subscribe(service.History, shardControllerMembershipUpdateListenerName, c.membershipUpdateCh) if err != nil { c.logger.Error("subscribing to membership resolver", tag.Error(err)) } c.logger.Info("Shard controller state changed", tag.LifeCycleStarted) } func (c *controller) Stop() { if !atomic.CompareAndSwapInt32(&c.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } c.logger.Info("Stopping shard controller", tag.ComponentShardController) defer c.logger.Info("Stopped shard controller", tag.ComponentShardController) c.PrepareToStop() if err := c.GetMembershipResolver().Unsubscribe(service.History, shardControllerMembershipUpdateListenerName); err != nil { c.logger.Error("unsubscribing from membership resolver", tag.Error(err), tag.OperationFailed) } close(c.shutdownCh) if success := common.AwaitWaitGroup(&c.shutdownWG, time.Minute); !success { c.logger.Warn("", tag.LifeCycleStopTimedout) } c.logger.Info("Shard controller state changed", tag.LifeCycleStopped) } func (c *controller) PrepareToStop() { atomic.StoreInt32(&c.shuttingDown, 1) } func (c *controller) GetEngine(workflowID string) (engine.Engine, error) { shardID := c.config.GetShardID(workflowID) return c.GetEngineForShard(shardID) } func (c *controller) GetEngineForShard(shardID int) (engine.Engine, error) { sw := c.metricsScope.StartTimer(metrics.GetEngineForShardLatency) defer sw.Stop() item, err := c.getOrCreateHistoryShardItem(shardID) if err != nil { return nil, err } return item.getOrCreateEngine(c.shardClosedCallback) } func (c *controller) RemoveEngineForShard(shardID int) { c.removeEngineForShard(shardID, nil) } func (c *controller) Status() int32 { return atomic.LoadInt32(&c.status) } func (c *controller) NumShards() int { s := c.shardIDSnapshot.Load() if s == nil { return 0 } return s.numShards } func (c *controller) ShardIDs() []int32 { s := c.shardIDSnapshot.Load() if s == nil { return []int32{} } return s.shardIDs } func (c *controller) removeEngineForShard(shardID int, shardItem *historyShardsItem) { sw := c.metricsScope.StartTimer(metrics.RemoveEngineForShardLatency) defer sw.Stop() c.logger.Info("removeEngineForShard called", tag.ShardID(shardID)) defer c.logger.Info("removeEngineForShard completed", tag.ShardID(shardID)) currentShardItem, err := c.removeHistoryShardItem(shardID, shardItem) if err != nil { c.logger.Error("Failed to remove history shard item", tag.Error(err), tag.ShardID(shardID)) } if shardItem != nil { // if shardItem is not nil, then currentShardItem either equals to shardItem or is nil // in both cases, we need to stop the engine in shardItem shardItem.stopEngine() return } // if shardItem is nil, then stop the engine for the current shardItem, if exists if currentShardItem != nil { currentShardItem.stopEngine() } } func (c *controller) shardClosedCallback(shardID int, shardItem *historyShardsItem) { c.metricsScope.IncCounter(metrics.ShardClosedCounter) c.logger.Info("Shard controller state changed", tag.LifeCycleStopping, tag.ComponentShard, tag.ShardID(shardID), tag.Reason("shardClosedCallback")) c.removeEngineForShard(shardID, shardItem) } func (c *controller) getOrCreateHistoryShardItem(shardID int) (*historyShardsItem, error) { if shardID >= c.config.NumberOfShards || shardID < 0 { // zero based shard ID c.logger.Error(fmt.Sprintf("Received shard ID: %v is larger than supported shard number %v", shardID, c.config.NumberOfShards, ), ) return nil, errShardIDOutOfBoundary } c.RLock() if item, ok := c.historyShards[shardID]; ok { if item.isValid() { c.RUnlock() return item, nil } // if item not valid then process to create a new one } c.RUnlock() c.logger.Info("Creating new history shard item", tag.ShardID(shardID)) defer c.logger.Info("Created new history shard item", tag.ShardID(shardID)) c.Lock() defer c.Unlock() if item, ok := c.historyShards[shardID]; ok { if item.isValid() { return item, nil } // if item not valid then process to create a new one } if c.isShuttingDown() || atomic.LoadInt32(&c.status) == common.DaemonStatusStopped { return nil, fmt.Errorf("controller for host '%v' shutting down", c.GetHostInfo().Identity()) } info, err := lookup.HistoryServerByShardID(c.GetMembershipResolver(), shardID) if err != nil { return nil, err } shardBelongsToCurrentHost := info.Identity() == c.GetHostInfo().Identity() c.logger.Info("Shard belongs to current host?", tag.ShardID(shardID), tag.Value(shardBelongsToCurrentHost), tag.Dynamic("shard-owner", info.Identity()), tag.Dynamic("current-host", c.GetHostInfo().Identity()), ) if shardBelongsToCurrentHost { shardItem, err := newHistoryShardsItem( c.Resource, shardID, c.engineFactory, c.config, c.replicationBudgetManager, ) if err != nil { return nil, err } c.historyShards[shardID] = shardItem c.updateShardIDSnapshotLocked() c.metricsScope.IncCounter(metrics.ShardItemCreatedCounter) shardItem.logger.Info("Shard item state changed", tag.LifeCycleStarted, tag.ComponentShardItem) return shardItem, nil } // for backwards compatibility, always return tchannel port return nil, CreateShardOwnershipLostError(c.GetHostInfo(), info) } func (c *controller) updateShardIDSnapshotLocked() { shardIDs := make([]int32, 0, len(c.historyShards)) for shardID := range c.historyShards { shardIDs = append(shardIDs, int32(shardID)) } snapshot := &shardIDSnapshot{ shardIDs: shardIDs, numShards: len(shardIDs), } c.shardIDSnapshot.Store(snapshot) } func (c *controller) removeHistoryShardItem(shardID int, shardItem *historyShardsItem) (*historyShardsItem, error) { c.Lock() defer c.Unlock() currentShardItem, ok := c.historyShards[shardID] if !ok { return nil, fmt.Errorf("no item found to remove for shard: %v", shardID) } if shardItem != nil && currentShardItem != shardItem { // the shardItem comparison is a defensive check to make sure we are deleting // what we intend to delete. return nil, fmt.Errorf("current shardItem doesn't match the one we intend to delete for shard: %v", shardID) } delete(c.historyShards, shardID) c.updateShardIDSnapshotLocked() c.metricsScope.IncCounter(metrics.ShardItemRemovedCounter) currentShardItem.logger.Info("Shard item state changed", tag.LifeCycleStopped, tag.ComponentShardItem, tag.Number(int64(len(c.historyShards)))) return currentShardItem, nil } // shardManagementPump is the main event loop for // controller. It is responsible for acquiring / // releasing shards in response to any event that can // change the shard ownership. These events are // // a. Ring membership change // b. Periodic ticker // c. ShardOwnershipLostError and subsequent ShardClosedEvents from engine func (c *controller) shardManagementPump() { defer c.shutdownWG.Done() acquireTicker := time.NewTicker(c.config.AcquireShardInterval()) defer acquireTicker.Stop() for { select { case <-c.shutdownCh: c.doShutdown() return case <-acquireTicker.C: c.acquireShards() case changedEvent := <-c.membershipUpdateCh: c.metricsScope.IncCounter(metrics.MembershipChangedCounter) c.logger.Info("Ring membership changed", tag.ValueRingMembershipChangedEvent, tag.NumberProcessed(len(changedEvent.HostsAdded)), tag.NumberDeleted(len(changedEvent.HostsRemoved)), tag.Number(int64(len(changedEvent.HostsUpdated)))) c.acquireShards() } } } func (c *controller) acquireShards() { c.logger.Info("Acquiring shards", tag.ComponentShardController, tag.Number(int64(c.NumShards()))) defer c.logger.Info("Acquired shards", tag.ComponentShardController, tag.Number(int64(c.NumShards()))) c.metricsScope.IncCounter(metrics.AcquireShardsCounter) sw := c.metricsScope.StartTimer(metrics.AcquireShardsLatency) defer sw.Stop() numShards := c.config.NumberOfShards shardActionCh := make(chan int, numShards) // Submit all tasks to the channel. for shardID := 0; shardID < numShards; shardID++ { shardActionCh <- shardID // must be non-blocking as there is no other coordination with shutdown } close(shardActionCh) concurrency := max(c.config.AcquireShardConcurrency(), 1) var wg sync.WaitGroup wg.Add(concurrency) // Spawn workers that would do lookup and add/remove shards concurrently. for i := 0; i < concurrency; i++ { go func() { defer wg.Done() for shardID := range shardActionCh { if c.isShuttingDown() { return } info, err := lookup.HistoryServerByShardID(c.GetMembershipResolver(), shardID) if err != nil { c.logger.Error("Error looking up host for shardID", tag.Error(err), tag.OperationFailed, tag.ShardID(shardID)) } else { if info.Identity() == c.GetHostInfo().Identity() { _, err1 := c.GetEngineForShard(shardID) if err1 != nil { c.metricsScope.IncCounter(metrics.GetEngineForShardErrorCounter) c.logger.Error("Unable to create history shard engine", tag.Error(err1), tag.OperationFailed, tag.ShardID(shardID)) } } } } }() } // Wait until all shards are processed. wg.Wait() c.metricsScope.UpdateGauge(metrics.NumShardsGauge, float64(c.NumShards())) } func (c *controller) doShutdown() { c.logger.Info("Shard controller state changed", tag.LifeCycleStopping, tag.Reason("shutdown")) c.Lock() defer c.Unlock() for _, item := range c.historyShards { item.stopEngine() } c.historyShards = nil c.updateShardIDSnapshotLocked() } func (c *controller) isShuttingDown() bool { return atomic.LoadInt32(&c.shuttingDown) != 0 } func (i *historyShardsItem) getOrCreateEngine( closeCallback func(int, *historyShardsItem), ) (engine.Engine, error) { i.RLock() if i.status == historyShardsItemStatusStarted { defer i.RUnlock() return i.engine, nil } i.RUnlock() i.Lock() defer i.Unlock() switch i.status { case historyShardsItemStatusInitialized: i.logger.Info("Shard engine state changed", tag.LifeCycleStarting, tag.ComponentShardEngine) context, err := acquireShard(i, closeCallback) if err != nil { // invalidate the shardItem so that the same shardItem won't be // used to create another shardContext i.logger.Info("Shard engine state changed", tag.LifeCycleStopped, tag.ComponentShardEngine) i.status = historyShardsItemStatusStopped return nil, err } if context.PreviousShardOwnerWasDifferent() { i.GetMetricsClient().RecordTimer(metrics.ShardInfoScope, metrics.ShardItemAcquisitionLatency, context.GetCurrentTime(i.GetClusterMetadata().GetCurrentClusterName()).Sub(context.GetLastUpdatedTime())) } i.engine = i.engineFactory.CreateEngine(context) i.engine.Start() i.logger.Info("Shard engine state changed", tag.LifeCycleStarted, tag.ComponentShardEngine) i.status = historyShardsItemStatusStarted return i.engine, nil case historyShardsItemStatusStarted: return i.engine, nil case historyShardsItemStatusStopped: return nil, fmt.Errorf("shard %v for host '%v' is shut down", i.shardID, i.GetHostInfo().Identity()) default: panic(i.logInvalidStatus()) } } func (i *historyShardsItem) stopEngine() { i.Lock() defer i.Unlock() i.logger.Info("Shard item stopEngine called", tag.ComponentShardEngine, tag.Dynamic("status", i.status)) switch i.status { case historyShardsItemStatusInitialized: i.status = historyShardsItemStatusStopped case historyShardsItemStatusStarted: i.logger.Info("Shard engine state changed", tag.LifeCycleStopping, tag.ComponentShardEngine) i.engine.Stop() i.engine = nil i.logger.Info("Shard engine state changed", tag.LifeCycleStopped, tag.ComponentShardEngine) i.status = historyShardsItemStatusStopped case historyShardsItemStatusStopped: // no op default: panic(i.logInvalidStatus()) } } func (i *historyShardsItem) isValid() bool { i.RLock() defer i.RUnlock() switch i.status { case historyShardsItemStatusInitialized, historyShardsItemStatusStarted: return true case historyShardsItemStatusStopped: return false default: panic(i.logInvalidStatus()) } } func (i *historyShardsItem) logInvalidStatus() string { msg := fmt.Sprintf("Host '%v' encounter invalid status %v for shard item for shardID '%v'.", i.GetHostInfo().Identity(), i.status, i.shardID) i.logger.Error(msg) return msg } // IsShardOwnershiptLostError checks if a given error is shard ownership lost error func IsShardOwnershiptLostError(err error) bool { switch err.(type) { case *persistence.ShardOwnershipLostError: return true } return false } // CreateShardOwnershipLostError creates a new shard ownership lost error func CreateShardOwnershipLostError( currentHost membership.HostInfo, ownerHost membership.HostInfo, ) *types.ShardOwnershipLostError { address, err := ownerHost.GetNamedAddress(membership.PortTchannel) if err != nil { address = ownerHost.Identity() } return &types.ShardOwnershipLostError{ Message: fmt.Sprintf("Shard is not owned by host: %v", currentHost.Identity()), Owner: address, } } ================================================ FILE: service/history/shard/controller_benchmark_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shard import ( "sync" "testing" ) // BenchmarkController_ShardIDs-96 52588224 293.9 ns/op 66 B/op 0 allocs/op // go test -bench=. --benchtime=10s --benchmem // goos: linux // goarch: amd64 // pkg: github.com/uber/cadence/service/history/shard // cpu: AMD EPYC 7B13 // With the old approach, the benchmark result is: // BenchmarkController_ShardIDs-96 39314 324629 ns/op 272333 B/op 19 allocs/op func BenchmarkController_ShardIDs(b *testing.B) { numShards := 16384 historyShards := make(map[int]*historyShardsItem) for i := 0; i < numShards; i++ { historyShards[i] = &historyShardsItem{shardID: i} } shardController := &controller{ historyShards: historyShards, } var wg sync.WaitGroup for i := 0; i < b.N; i++ { if i%1000 == 0 { // update is much much less frequent than read wg.Add(1) go func() { defer wg.Done() shardController.Lock() shardController.updateShardIDSnapshotLocked() shardController.Unlock() }() } shardController.ShardIDs() shardController.NumShards() } wg.Wait() } ================================================ FILE: service/history/shard/controller_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: controller.go // // Generated by this command: // // mockgen -package shard -source controller.go -destination controller_mock.go -self_package github.com/uber/cadence/service/history/shard // // Package shard is a generated GoMock package. package shard import ( reflect "reflect" gomock "go.uber.org/mock/gomock" engine "github.com/uber/cadence/service/history/engine" ) // MockEngineFactory is a mock of EngineFactory interface. type MockEngineFactory struct { ctrl *gomock.Controller recorder *MockEngineFactoryMockRecorder isgomock struct{} } // MockEngineFactoryMockRecorder is the mock recorder for MockEngineFactory. type MockEngineFactoryMockRecorder struct { mock *MockEngineFactory } // NewMockEngineFactory creates a new mock instance. func NewMockEngineFactory(ctrl *gomock.Controller) *MockEngineFactory { mock := &MockEngineFactory{ctrl: ctrl} mock.recorder = &MockEngineFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockEngineFactory) EXPECT() *MockEngineFactoryMockRecorder { return m.recorder } // CreateEngine mocks base method. func (m *MockEngineFactory) CreateEngine(arg0 Context) engine.Engine { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateEngine", arg0) ret0, _ := ret[0].(engine.Engine) return ret0 } // CreateEngine indicates an expected call of CreateEngine. func (mr *MockEngineFactoryMockRecorder) CreateEngine(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateEngine", reflect.TypeOf((*MockEngineFactory)(nil).CreateEngine), arg0) } // MockController is a mock of Controller interface. type MockController struct { ctrl *gomock.Controller recorder *MockControllerMockRecorder isgomock struct{} } // MockControllerMockRecorder is the mock recorder for MockController. type MockControllerMockRecorder struct { mock *MockController } // NewMockController creates a new mock instance. func NewMockController(ctrl *gomock.Controller) *MockController { mock := &MockController{ctrl: ctrl} mock.recorder = &MockControllerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockController) EXPECT() *MockControllerMockRecorder { return m.recorder } // GetEngine mocks base method. func (m *MockController) GetEngine(workflowID string) (engine.Engine, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEngine", workflowID) ret0, _ := ret[0].(engine.Engine) ret1, _ := ret[1].(error) return ret0, ret1 } // GetEngine indicates an expected call of GetEngine. func (mr *MockControllerMockRecorder) GetEngine(workflowID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEngine", reflect.TypeOf((*MockController)(nil).GetEngine), workflowID) } // GetEngineForShard mocks base method. func (m *MockController) GetEngineForShard(shardID int) (engine.Engine, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEngineForShard", shardID) ret0, _ := ret[0].(engine.Engine) ret1, _ := ret[1].(error) return ret0, ret1 } // GetEngineForShard indicates an expected call of GetEngineForShard. func (mr *MockControllerMockRecorder) GetEngineForShard(shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEngineForShard", reflect.TypeOf((*MockController)(nil).GetEngineForShard), shardID) } // NumShards mocks base method. func (m *MockController) NumShards() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NumShards") ret0, _ := ret[0].(int) return ret0 } // NumShards indicates an expected call of NumShards. func (mr *MockControllerMockRecorder) NumShards() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NumShards", reflect.TypeOf((*MockController)(nil).NumShards)) } // PrepareToStop mocks base method. func (m *MockController) PrepareToStop() { m.ctrl.T.Helper() m.ctrl.Call(m, "PrepareToStop") } // PrepareToStop indicates an expected call of PrepareToStop. func (mr *MockControllerMockRecorder) PrepareToStop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrepareToStop", reflect.TypeOf((*MockController)(nil).PrepareToStop)) } // RemoveEngineForShard mocks base method. func (m *MockController) RemoveEngineForShard(shardID int) { m.ctrl.T.Helper() m.ctrl.Call(m, "RemoveEngineForShard", shardID) } // RemoveEngineForShard indicates an expected call of RemoveEngineForShard. func (mr *MockControllerMockRecorder) RemoveEngineForShard(shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveEngineForShard", reflect.TypeOf((*MockController)(nil).RemoveEngineForShard), shardID) } // ShardIDs mocks base method. func (m *MockController) ShardIDs() []int32 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ShardIDs") ret0, _ := ret[0].([]int32) return ret0 } // ShardIDs indicates an expected call of ShardIDs. func (mr *MockControllerMockRecorder) ShardIDs() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShardIDs", reflect.TypeOf((*MockController)(nil).ShardIDs)) } // Start mocks base method. func (m *MockController) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockControllerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockController)(nil).Start)) } // Status mocks base method. func (m *MockController) Status() int32 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Status") ret0, _ := ret[0].(int32) return ret0 } // Status indicates an expected call of Status. func (mr *MockControllerMockRecorder) Status() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockController)(nil).Status)) } // Stop mocks base method. func (m *MockController) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockControllerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockController)(nil).Stop)) } ================================================ FILE: service/history/shard/controller_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package shard import ( "errors" "fmt" "sync" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" mmocks "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/resource" ) type ( controllerSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockResource *resource.Test mockHistoryEngine *engine.MockEngine mockMembershipResolver *membership.MockResolver hostInfo membership.HostInfo mockShardManager *mmocks.ShardManager mockEngineFactory *MockEngineFactory config *config.Config logger log.Logger shardController *controller } ) func TestControllerSuite(t *testing.T) { s := new(controllerSuite) suite.Run(t, s) } func (s *controllerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.History) s.mockEngineFactory = NewMockEngineFactory(s.controller) s.mockHistoryEngine = engine.NewMockEngine(s.controller) s.mockShardManager = s.mockResource.ShardMgr s.mockMembershipResolver = s.mockResource.MembershipResolver s.hostInfo = s.mockResource.GetHostInfo() s.logger = s.mockResource.Logger s.config = config.NewForTest() s.shardController = NewShardController(s.mockResource, s.mockEngineFactory, s.config, nil).(*controller) } func (s *controllerSuite) TearDownTest() { s.controller.Finish() s.mockResource.Finish(s.T()) } func (s *controllerSuite) TestAcquireShardSuccess() { numShards := 10 s.config.NumberOfShards = numShards replicationAck := int64(201) currentClusterTransferAck := int64(210) alternativeClusterTransferAck := int64(320) currentClusterTimerAck := time.Now().Add(-100 * time.Second) alternativeClusterTimerAck := time.Now().Add(-200 * time.Second) myShards := []int{} for shardID := 0; shardID < numShards; shardID++ { hostID := shardID % 4 if hostID == 0 { myShards = append(myShards, shardID) s.mockHistoryEngine.EXPECT().Start().Return().Times(1) s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(s.hostInfo, nil).Times(2) s.mockEngineFactory.EXPECT().CreateEngine(gomock.Any()).Return(s.mockHistoryEngine).Times(1) s.mockShardManager.On("GetShard", mock.Anything, &persistence.GetShardRequest{ShardID: shardID}).Return( &persistence.GetShardResponse{ ShardInfo: &persistence.ShardInfo{ ShardID: shardID, Owner: s.hostInfo.Identity(), RangeID: 5, ReplicationAckLevel: replicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, ClusterReplicationLevel: map[string]int64{}, }, }, nil).Once() s.mockShardManager.On("UpdateShard", mock.Anything, &persistence.UpdateShardRequest{ ShardInfo: &persistence.ShardInfo{ ShardID: shardID, Owner: s.hostInfo.Identity(), RangeID: 6, StolenSinceRenew: 1, ReplicationAckLevel: replicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, TransferProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, TimerProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, ClusterReplicationLevel: map[string]int64{}, ReplicationDLQAckLevel: map[string]int64{}, QueueStates: map[int32]*types.QueueState{}, }, PreviousRangeID: 5, }).Return(nil).Once() } else { ownerHost := fmt.Sprintf("test-acquire-shard-host-%v", hostID) s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(membership.NewHostInfo(ownerHost), nil).Times(1) } } s.shardController.acquireShards() count := 0 for _, shardID := range myShards { s.NotNil(s.shardController.GetEngineForShard(shardID)) count++ } s.Equal(3, count) s.Equal(3, s.shardController.NumShards()) s.ElementsMatch([]int32{0, 4, 8}, s.shardController.ShardIDs()) } func (s *controllerSuite) TestAcquireShardsConcurrently() { numShards := 10 s.config.NumberOfShards = numShards s.config.AcquireShardConcurrency = func(opts ...dynamicproperties.FilterOption) int { return 10 } replicationAck := int64(201) currentClusterTransferAck := int64(210) alternativeClusterTransferAck := int64(320) currentClusterTimerAck := time.Now().Add(-100 * time.Second) alternativeClusterTimerAck := time.Now().Add(-200 * time.Second) var myShards []int for shardID := 0; shardID < numShards; shardID++ { hostID := shardID % 4 if hostID == 0 { myShards = append(myShards, shardID) s.mockHistoryEngine.EXPECT().Start().Return().Times(1) s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(s.hostInfo, nil).Times(2) s.mockEngineFactory.EXPECT().CreateEngine(gomock.Any()).Return(s.mockHistoryEngine).Times(1) s.mockShardManager.On("GetShard", mock.Anything, &persistence.GetShardRequest{ShardID: shardID}).Return( &persistence.GetShardResponse{ ShardInfo: &persistence.ShardInfo{ ShardID: shardID, Owner: s.hostInfo.Identity(), RangeID: 5, ReplicationAckLevel: replicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, ClusterReplicationLevel: map[string]int64{}, }, }, nil).Once() s.mockShardManager.On("UpdateShard", mock.Anything, &persistence.UpdateShardRequest{ ShardInfo: &persistence.ShardInfo{ ShardID: shardID, Owner: s.hostInfo.Identity(), RangeID: 6, StolenSinceRenew: 1, ReplicationAckLevel: replicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, TransferProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, TimerProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, ClusterReplicationLevel: map[string]int64{}, ReplicationDLQAckLevel: map[string]int64{}, QueueStates: map[int32]*types.QueueState{}, }, PreviousRangeID: 5, }).Return(nil).Once() } else { ownerHost := fmt.Sprintf("test-acquire-shard-host-%v", hostID) s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(membership.NewHostInfo(ownerHost), nil).Times(1) } } s.shardController.acquireShards() count := 0 for _, shardID := range myShards { s.NotNil(s.shardController.GetEngineForShard(shardID)) count++ } s.Equal(3, count) s.Equal(3, s.shardController.NumShards()) s.ElementsMatch([]int32{0, 4, 8}, s.shardController.ShardIDs()) } func (s *controllerSuite) TestAcquireShardLookupFailure() { numShards := 2 s.config.NumberOfShards = numShards for shardID := 0; shardID < numShards; shardID++ { s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(membership.HostInfo{}, errors.New("ring failure")).Times(1) } s.shardController.acquireShards() for shardID := 0; shardID < numShards; shardID++ { s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(membership.HostInfo{}, errors.New("ring failure")).Times(1) s.Nil(s.shardController.GetEngineForShard(shardID)) } s.Equal(0, s.shardController.NumShards()) s.Empty(s.shardController.ShardIDs()) } func (s *controllerSuite) TestAcquireShardRenewSuccess() { numShards := 2 s.config.NumberOfShards = numShards replicationAck := int64(201) currentClusterTransferAck := int64(210) alternativeClusterTransferAck := int64(320) currentClusterTimerAck := time.Now().Add(-100 * time.Second) alternativeClusterTimerAck := time.Now().Add(-200 * time.Second) for shardID := 0; shardID < numShards; shardID++ { s.mockHistoryEngine.EXPECT().Start().Return().Times(1) s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(s.hostInfo, nil).Times(2) s.mockEngineFactory.EXPECT().CreateEngine(gomock.Any()).Return(s.mockHistoryEngine).Times(1) s.mockShardManager.On("GetShard", mock.Anything, &persistence.GetShardRequest{ShardID: shardID}).Return( &persistence.GetShardResponse{ ShardInfo: &persistence.ShardInfo{ ShardID: shardID, Owner: s.hostInfo.Identity(), RangeID: 5, ReplicationAckLevel: replicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, ClusterReplicationLevel: map[string]int64{}, }, }, nil).Once() s.mockShardManager.On("UpdateShard", mock.Anything, &persistence.UpdateShardRequest{ ShardInfo: &persistence.ShardInfo{ ShardID: shardID, Owner: s.hostInfo.Identity(), RangeID: 6, StolenSinceRenew: 1, ReplicationAckLevel: replicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, TransferProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, TimerProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, ClusterReplicationLevel: map[string]int64{}, ReplicationDLQAckLevel: map[string]int64{}, QueueStates: map[int32]*types.QueueState{}, }, PreviousRangeID: 5, }).Return(nil).Once() } s.shardController.acquireShards() s.Equal(2, s.shardController.NumShards()) s.ElementsMatch([]int32{0, 1}, s.shardController.ShardIDs()) for shardID := 0; shardID < numShards; shardID++ { s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(s.hostInfo, nil).Times(1) } s.shardController.acquireShards() s.Equal(2, s.shardController.NumShards()) s.ElementsMatch([]int32{0, 1}, s.shardController.ShardIDs()) for shardID := 0; shardID < numShards; shardID++ { s.NotNil(s.shardController.GetEngineForShard(shardID)) } } func (s *controllerSuite) TestAcquireShardRenewLookupFailed() { numShards := 2 s.config.NumberOfShards = numShards replicationAck := int64(201) currentClusterTransferAck := int64(210) alternativeClusterTransferAck := int64(320) currentClusterTimerAck := time.Now().Add(-100 * time.Second) alternativeClusterTimerAck := time.Now().Add(-200 * time.Second) for shardID := 0; shardID < numShards; shardID++ { s.mockHistoryEngine.EXPECT().Start().Return().Times(1) s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(s.hostInfo, nil).Times(2) s.mockEngineFactory.EXPECT().CreateEngine(gomock.Any()).Return(s.mockHistoryEngine).Times(1) s.mockShardManager.On("GetShard", mock.Anything, &persistence.GetShardRequest{ShardID: shardID}).Return( &persistence.GetShardResponse{ ShardInfo: &persistence.ShardInfo{ ShardID: shardID, Owner: s.hostInfo.Identity(), RangeID: 5, ReplicationAckLevel: replicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, ClusterReplicationLevel: map[string]int64{}, }, }, nil).Once() s.mockShardManager.On("UpdateShard", mock.Anything, &persistence.UpdateShardRequest{ ShardInfo: &persistence.ShardInfo{ ShardID: shardID, Owner: s.hostInfo.Identity(), RangeID: 6, StolenSinceRenew: 1, ReplicationAckLevel: replicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, TransferProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, TimerProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, ClusterReplicationLevel: map[string]int64{}, ReplicationDLQAckLevel: map[string]int64{}, QueueStates: map[int32]*types.QueueState{}, }, PreviousRangeID: 5, }).Return(nil).Once() } s.shardController.acquireShards() for shardID := 0; shardID < numShards; shardID++ { s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(membership.HostInfo{}, errors.New("ring failure")).Times(1) } s.shardController.acquireShards() for shardID := 0; shardID < numShards; shardID++ { s.NotNil(s.shardController.GetEngineForShard(shardID)) } } func (s *controllerSuite) TestHistoryEngineClosed() { numShards := 4 s.config.NumberOfShards = numShards s.shardController = NewShardController(s.mockResource, s.mockEngineFactory, s.config, nil).(*controller) historyEngines := make(map[int]*engine.MockEngine) for shardID := 0; shardID < numShards; shardID++ { mockEngine := engine.NewMockEngine(s.controller) historyEngines[shardID] = mockEngine s.setupMocksForAcquireShard(shardID, mockEngine, 5, 6) } s.mockMembershipResolver.EXPECT().Subscribe(service.History, shardControllerMembershipUpdateListenerName, gomock.Any()).Return(nil).AnyTimes() s.shardController.Start() var workerWG sync.WaitGroup for w := 0; w < 10; w++ { workerWG.Add(1) go func() { for attempt := 0; attempt < 10; attempt++ { for shardID := 0; shardID < numShards; shardID++ { engine, err := s.shardController.GetEngineForShard(shardID) s.Nil(err) s.NotNil(engine) } } workerWG.Done() }() } workerWG.Wait() differentHostInfo := membership.NewHostInfo("another-host") for shardID := 0; shardID < 2; shardID++ { mockEngine := historyEngines[shardID] mockEngine.EXPECT().Stop().Return().Times(1) s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(differentHostInfo, nil).AnyTimes() s.shardController.shardClosedCallback(shardID, nil) } for w := 0; w < 10; w++ { workerWG.Add(1) go func() { for attempt := 0; attempt < 10; attempt++ { for shardID := 2; shardID < numShards; shardID++ { engine, err := s.shardController.GetEngineForShard(shardID) s.Nil(err) s.NotNil(engine) time.Sleep(20 * time.Millisecond) } } workerWG.Done() }() } for w := 0; w < 10; w++ { workerWG.Add(1) go func() { shardLost := false for attempt := 0; !shardLost && attempt < 10; attempt++ { for shardID := 0; shardID < 2; shardID++ { _, err := s.shardController.GetEngineForShard(shardID) if err != nil { s.logger.Error("ShardLost", tag.Error(err)) shardLost = true } time.Sleep(20 * time.Millisecond) } } s.True(shardLost) workerWG.Done() }() } workerWG.Wait() s.mockMembershipResolver.EXPECT().Unsubscribe(service.History, shardControllerMembershipUpdateListenerName).Return(nil).AnyTimes() for shardID := 2; shardID < numShards; shardID++ { mockEngine := historyEngines[shardID] mockEngine.EXPECT().Stop().Return().Times(1) s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(s.hostInfo, nil).AnyTimes() } s.shardController.Stop() s.Equal(0, s.shardController.NumShards()) s.Empty(s.shardController.ShardIDs()) } func (s *controllerSuite) TestShardControllerClosed() { numShards := 4 s.config.NumberOfShards = numShards s.shardController = NewShardController(s.mockResource, s.mockEngineFactory, s.config, nil).(*controller) historyEngines := make(map[int]*engine.MockEngine) for shardID := 0; shardID < numShards; shardID++ { mockEngine := engine.NewMockEngine(s.controller) historyEngines[shardID] = mockEngine s.setupMocksForAcquireShard(shardID, mockEngine, 5, 6) } s.mockMembershipResolver.EXPECT().Subscribe(service.History, shardControllerMembershipUpdateListenerName, gomock.Any()).Return(nil).AnyTimes() s.shardController.Start() var workerWG sync.WaitGroup for w := 0; w < 10; w++ { workerWG.Add(1) go func() { shardLost := false for attempt := 0; !shardLost && attempt < 10; attempt++ { for shardID := 0; shardID < numShards; shardID++ { _, err := s.shardController.GetEngineForShard(shardID) if err != nil { s.logger.Error("ShardLost", tag.Error(err)) shardLost = true } time.Sleep(20 * time.Millisecond) } } s.True(shardLost) workerWG.Done() }() } s.mockMembershipResolver.EXPECT().Unsubscribe(service.History, shardControllerMembershipUpdateListenerName).Return(nil).AnyTimes() for shardID := 0; shardID < numShards; shardID++ { mockEngine := historyEngines[shardID] mockEngine.EXPECT().Stop().Times(1) s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(s.hostInfo, nil).AnyTimes() } s.shardController.Stop() workerWG.Wait() s.Equal(0, s.shardController.NumShards()) s.Empty(s.shardController.ShardIDs()) } func (s *controllerSuite) TestGetOrCreateHistoryShardItem_InvalidShardID_Error() { s.config.NumberOfShards = 4 s.shardController = NewShardController(s.mockResource, s.mockEngineFactory, s.config, nil).(*controller) eng, err := s.shardController.GetEngineForShard(-1) s.Nil(eng) s.Error(err) eng, err = s.shardController.GetEngineForShard(s.config.NumberOfShards) s.Nil(eng) s.Error(err) } func (s *controllerSuite) setupMocksForAcquireShard(shardID int, mockEngine *engine.MockEngine, currentRangeID, newRangeID int64) { replicationAck := int64(201) currentClusterTransferAck := int64(210) alternativeClusterTransferAck := int64(320) currentClusterTimerAck := time.Now().Add(-100 * time.Second) alternativeClusterTimerAck := time.Now().Add(-200 * time.Second) // s.mockResource.ExecutionMgr.On("Close").Return() mockEngine.EXPECT().Start().Times(1) s.mockMembershipResolver.EXPECT().Lookup(service.History, string(rune(shardID))).Return(s.hostInfo, nil).Times(2) s.mockEngineFactory.EXPECT().CreateEngine(gomock.Any()).Return(mockEngine).Times(1) s.mockShardManager.On("GetShard", mock.Anything, &persistence.GetShardRequest{ShardID: shardID}).Return( &persistence.GetShardResponse{ ShardInfo: &persistence.ShardInfo{ ShardID: shardID, Owner: s.hostInfo.Identity(), RangeID: currentRangeID, ReplicationAckLevel: replicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, ClusterReplicationLevel: map[string]int64{}, }, }, nil).Once() s.mockShardManager.On("UpdateShard", mock.Anything, &persistence.UpdateShardRequest{ ShardInfo: &persistence.ShardInfo{ ShardID: shardID, Owner: s.hostInfo.Identity(), RangeID: newRangeID, StolenSinceRenew: 1, ReplicationAckLevel: replicationAck, TransferAckLevel: currentClusterTransferAck, TimerAckLevel: currentClusterTimerAck, ClusterTransferAckLevel: map[string]int64{ cluster.TestCurrentClusterName: currentClusterTransferAck, cluster.TestAlternativeClusterName: alternativeClusterTransferAck, }, ClusterTimerAckLevel: map[string]time.Time{ cluster.TestCurrentClusterName: currentClusterTimerAck, cluster.TestAlternativeClusterName: alternativeClusterTimerAck, }, TransferProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, TimerProcessingQueueStates: &types.ProcessingQueueStates{ StatesByCluster: make(map[string][]*types.ProcessingQueueState), }, ClusterReplicationLevel: map[string]int64{}, ReplicationDLQAckLevel: map[string]int64{}, QueueStates: map[int32]*types.QueueState{}, }, PreviousRangeID: currentRangeID, }).Return(nil).Once() } ================================================ FILE: service/history/simulation/event.go ================================================ package simulation import ( "encoding/json" "fmt" "os" "time" ) var enabled = false func init() { enabled = os.Getenv("HISTORY_LOG_EVENTS") == "true" } const ( EventNameCreateHistoryTask = "Create History Task" EventNameExecuteHistoryTask = "Execute History Task" ) type E struct { ShardID int DomainID string WorkflowID string RunID string EventTime time.Time // EventName describes the event. It is used to query events in simulations so don't change existing event names. EventName string Host string Payload map[string]any } func Enabled() bool { return enabled } func LogEvents(events ...E) { if !enabled { return } for _, e := range events { e.EventTime = time.Now() data, err := json.Marshal(e) if err != nil { fmt.Printf("failed to marshal event: %v", err) } fmt.Printf("History New Event: %s\n", data) } } ================================================ FILE: service/history/task/constants.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import "time" const ( loadDomainEntryForTaskRetryDelay = 100 * time.Millisecond activeTaskResubmitMaxAttempts = 10 defaultTaskEventLoggerSize = 100 stickyTaskMaxRetryCount = 100 // noPriority is the value returned if no priority is ever assigned to the task noPriority = -1 ) ================================================ FILE: service/history/task/event_logger.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "fmt" "strings" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) type ( eventLogger interface { AddEvent(eventMsg string, details ...interface{}) FlushEvents(msg string) int } event struct { timestamp time.Time message string details []interface{} } eventLoggerImpl struct { logger log.Logger timeSource clock.TimeSource events []event nextEventIdx int maxSize int } ) func newEventLogger( logger log.Logger, timeSource clock.TimeSource, maxSize int, ) eventLogger { return &eventLoggerImpl{ logger: logger, timeSource: timeSource, events: make([]event, maxSize), nextEventIdx: 0, maxSize: maxSize, } } func (e *eventLoggerImpl) AddEvent( eventMsg string, details ...interface{}, ) { e.events[e.nextEventIdx] = event{ timestamp: e.timeSource.Now(), message: eventMsg, details: details, } e.nextEventIdx = (e.nextEventIdx + 1) % e.maxSize } func (e *eventLoggerImpl) FlushEvents( msg string, ) int { var builder strings.Builder eventIdx := e.nextEventIdx eventsFlushed := 0 for i := 0; i != len(e.events); i++ { // dump all events in reverse order eventIdx-- if eventIdx < 0 { eventIdx = len(e.events) - 1 } event := e.events[eventIdx] if event.timestamp.IsZero() { break } eventsFlushed++ builder.WriteString(fmt.Sprintf("%v, %s, %v\n", event.timestamp, event.message, event.details)) } e.logger.Info(msg, tag.Key("events"), tag.Value(builder.String())) e.events = make([]event, e.maxSize) return eventsFlushed } ================================================ FILE: service/history/task/event_logger_test.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" ) type ( eventLoggerSuite struct { *require.Assertions suite.Suite mockLogger *log.MockLogger eventLogger *eventLoggerImpl } ) func TestEventLoggerSuite(t *testing.T) { s := new(eventLoggerSuite) suite.Run(t, s) } func (s *eventLoggerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.mockLogger = log.NewMockLogger(gomock.NewController(s.T())) s.eventLogger = newEventLogger( s.mockLogger, clock.NewRealTimeSource(), defaultTaskEventLoggerSize, ).(*eventLoggerImpl) } func (s *eventLoggerSuite) TestAddEvent() { for i := 0; i != defaultTaskEventLoggerSize*2; i++ { s.eventLogger.AddEvent("some random event", i) s.Equal((i+1)%defaultTaskEventLoggerSize, s.eventLogger.nextEventIdx) } for i := 0; i != defaultTaskEventLoggerSize; i++ { // check if old events got overwritten s.Equal(i+defaultTaskEventLoggerSize, s.eventLogger.events[i].details[0]) } s.Len(s.eventLogger.events, defaultTaskEventLoggerSize) } func (s *eventLoggerSuite) TestFlushEvents() { for _, numEvents := range []int{0, defaultTaskEventLoggerSize / 2, defaultTaskEventLoggerSize, defaultTaskEventLoggerSize * 2} { for i := 0; i != numEvents; i++ { s.eventLogger.AddEvent("some random event") } expectedEventsFlushed := min(numEvents, defaultTaskEventLoggerSize) s.mockLogger.EXPECT().Info(gomock.Any(), gomock.Any(), gomock.Any()).Times(1) s.Equal(expectedEventsFlushed, s.eventLogger.FlushEvents("some random message")) } } ================================================ FILE: service/history/task/executor_wrapper.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package task import ( "context" "encoding/json" "runtime/debug" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) type ( executorWrapper struct { currentClusterName string activeClusterMgr activecluster.Manager activeExecutor Executor standbyExecutor Executor logger log.Logger } ) func NewExecutorWrapper( currentClusterName string, activeClusterMgr activecluster.Manager, activeExecutor Executor, standbyExecutor Executor, logger log.Logger, ) Executor { return &executorWrapper{ currentClusterName: currentClusterName, activeClusterMgr: activeClusterMgr, activeExecutor: activeExecutor, standbyExecutor: standbyExecutor, logger: logger, } } func (e *executorWrapper) Stop() { e.activeExecutor.Stop() e.standbyExecutor.Stop() } func (e *executorWrapper) Execute(task Task) (ExecuteResponse, error) { if e.isActiveTask(task) { return e.activeExecutor.Execute(task) } return e.standbyExecutor.Execute(task) } func (e *executorWrapper) isActiveTask( task Task, ) bool { domainID := task.GetDomainID() wfID := task.GetWorkflowID() rID := task.GetRunID() activeClusterInfo, err := e.activeClusterMgr.GetActiveClusterInfoByWorkflow(context.Background(), domainID, wfID, rID) if err != nil { e.logger.Warn("Failed to get active cluster info, process task as active.", tag.WorkflowDomainID(domainID), tag.WorkflowID(wfID), tag.WorkflowRunID(rID), tag.Error(err)) return true } if activeClusterInfo.ActiveClusterName != e.currentClusterName { if e.logger.DebugOn() { taskJSON, _ := json.Marshal(task) e.logger.Debug("Process task as standby.", tag.WorkflowDomainID(domainID), tag.Dynamic("task", string(taskJSON)), tag.Dynamic("taskType", task.GetTaskType()), tag.ClusterName(activeClusterInfo.ActiveClusterName), tag.Dynamic("stack", string(debug.Stack())), ) } return false } if e.logger.DebugOn() { taskJSON, _ := json.Marshal(task) e.logger.Debug("Process task as active.", tag.WorkflowDomainID(domainID), tag.Dynamic("task", string(taskJSON)), tag.Dynamic("taskType", task.GetTaskType()), tag.ClusterName(activeClusterInfo.ActiveClusterName), tag.Dynamic("stack", string(debug.Stack())), ) } return true } ================================================ FILE: service/history/task/executor_wrapper_test.go ================================================ package task import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestExecutorWrapper_IsActiveTask(t *testing.T) { tests := []struct { name string currentCluster string domainID string workflowID string runID string domainError error isActiveActive bool activeCluster string lookupError error expectedResult bool }{ { name: "Active-Active domain - current cluster is active", currentCluster: "cluster1", domainID: "domain1", workflowID: "workflow1", runID: "run1", isActiveActive: true, activeCluster: "cluster1", expectedResult: true, }, { name: "Active-Active domain - current cluster is not active", currentCluster: "cluster1", domainID: "domain1", workflowID: "workflow1", runID: "run1", isActiveActive: true, activeCluster: "cluster2", expectedResult: false, }, { name: "Active-Active domain - lookup error", currentCluster: "cluster1", domainID: "domain1", workflowID: "workflow1", runID: "run1", isActiveActive: true, lookupError: assert.AnError, expectedResult: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) // Setup mocks mockTask := NewMockTask(ctrl) mockTask.EXPECT().GetDomainID().Return(tt.domainID).AnyTimes() mockTask.EXPECT().GetWorkflowID().Return(tt.workflowID).AnyTimes() mockTask.EXPECT().GetRunID().Return(tt.runID).AnyTimes() mockTask.EXPECT().GetInfo().Return(&persistence.DecisionTask{}).AnyTimes() mockTask.EXPECT().GetTaskType().Return(0).AnyTimes() // called by debug log mockActiveClusterMgr := activecluster.NewMockManager(ctrl) if tt.lookupError == nil { mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), tt.domainID, tt.workflowID, tt.runID). Return(&types.ActiveClusterInfo{ActiveClusterName: tt.activeCluster}, nil) } else { mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), tt.domainID, tt.workflowID, tt.runID). Return(nil, tt.lookupError) } mockLogger := testlogger.New(t) // Create executor wrapper wrapper := NewExecutorWrapper( tt.currentCluster, mockActiveClusterMgr, NewMockExecutor(ctrl), NewMockExecutor(ctrl), mockLogger, ) // Execute test result := wrapper.(*executorWrapper).isActiveTask(mockTask) // Verify result assert.Equal(t, tt.expectedResult, result) }) } } ================================================ FILE: service/history/task/interface.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go -self_package github.com/uber/cadence/service/history/task package task import ( "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/future" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/task" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/shard" ) type ( // Task is the interface for all tasks generated by history service Task interface { task.PriorityTask persistence.Task GetQueueType() QueueType GetShard() shard.Context GetAttempt() int GetInfo() persistence.Task SetInitialSubmitTime(time.Time) } // CrossClusterTask is the interface for processing cross cluster task in the source cluster CrossClusterTask interface { Task IsValid() bool IsReadyForPoll() bool GetCrossClusterRequest() (*types.CrossClusterTaskRequest, error) RecordResponse(*types.CrossClusterTaskResponse) error } // Key identifies a Task and defines a total order among tasks Key interface { Less(Key) bool } // Executor contains the execution logic for Task Executor interface { Execute(task Task) (ExecuteResponse, error) Stop() } // Filter filters Task Filter func(task persistence.Task) (bool, error) // Initializer initializes a Task based on the Info Initializer func(persistence.Task) Task // PriorityAssigner assigns priority to Tasks PriorityAssigner interface { Assign(Task) error } // Processor is the worker pool for processing Tasks Processor interface { common.Daemon Submit(Task) error TrySubmit(Task) (bool, error) } // Redispatcher buffers tasks and periodically redispatch them to Processor // redispatch can also be triggered immediately by calling the Redispatch method Redispatcher interface { common.Daemon AddTask(Task) Redispatch(targetSize int) RedispatchTask(Task, time.Time) Size() int } Rescheduler interface { common.Daemon RescheduleTask(Task, time.Time) RescheduleDomains(domainIDs map[string]struct{}) Size() int } // Fetcher is a host level component for aggregating task fetch requests // from all shards on the host and perform one fetching operation for // aggregated requests. Fetcher interface { common.Daemon GetSourceCluster() string Fetch(shardID int, fetchParams ...interface{}) future.Future } // Fetchers is a group of Fetchers, one for each source cluster Fetchers []Fetcher // QueueType is the type of task queue QueueType int ExecuteResponse struct { Scope metrics.Scope IsActiveTask bool } ) const ( // QueueTypeActiveTransfer is the queue type for active transfer queue processor (TODO: remove this when history queue v1 is deprecated) QueueTypeActiveTransfer QueueType = iota + 1 // QueueTypeStandbyTransfer is the queue type for standby transfer queue processor (TODO: remove this when history queue v1 is deprecated) QueueTypeStandbyTransfer // QueueTypeActiveTimer is the queue type for active timer queue processor (TODO: remove this when history queue v1 is deprecated) QueueTypeActiveTimer // QueueTypeStandbyTimer is the queue type for standby timer queue processor (TODO: remove this when history queue v1 is deprecated) QueueTypeStandbyTimer // QueueTypeReplication is the queue type for replication queue processor QueueTypeReplication // QueueTypeTransfer is the queue type for transfer queue processor QueueTypeTransfer // QueueTypeTimer is the queue type for timer queue processor QueueTypeTimer ) ================================================ FILE: service/history/task/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // // Generated by this command: // // mockgen -package task -source interface.go -destination interface_mock.go -self_package github.com/uber/cadence/service/history/task // // Package task is a generated GoMock package. package task import ( reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" future "github.com/uber/cadence/common/future" persistence "github.com/uber/cadence/common/persistence" task "github.com/uber/cadence/common/task" types "github.com/uber/cadence/common/types" shard "github.com/uber/cadence/service/history/shard" ) // MockTask is a mock of Task interface. type MockTask struct { ctrl *gomock.Controller recorder *MockTaskMockRecorder isgomock struct{} } // MockTaskMockRecorder is the mock recorder for MockTask. type MockTaskMockRecorder struct { mock *MockTask } // NewMockTask creates a new mock instance. func NewMockTask(ctrl *gomock.Controller) *MockTask { mock := &MockTask{ctrl: ctrl} mock.recorder = &MockTaskMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTask) EXPECT() *MockTaskMockRecorder { return m.recorder } // Ack mocks base method. func (m *MockTask) Ack() { m.ctrl.T.Helper() m.ctrl.Call(m, "Ack") } // Ack indicates an expected call of Ack. func (mr *MockTaskMockRecorder) Ack() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ack", reflect.TypeOf((*MockTask)(nil).Ack)) } // ByteSize mocks base method. func (m *MockTask) ByteSize() uint64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByteSize") ret0, _ := ret[0].(uint64) return ret0 } // ByteSize indicates an expected call of ByteSize. func (mr *MockTaskMockRecorder) ByteSize() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByteSize", reflect.TypeOf((*MockTask)(nil).ByteSize)) } // Cancel mocks base method. func (m *MockTask) Cancel() { m.ctrl.T.Helper() m.ctrl.Call(m, "Cancel") } // Cancel indicates an expected call of Cancel. func (mr *MockTaskMockRecorder) Cancel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cancel", reflect.TypeOf((*MockTask)(nil).Cancel)) } // Execute mocks base method. func (m *MockTask) Execute() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Execute") ret0, _ := ret[0].(error) return ret0 } // Execute indicates an expected call of Execute. func (mr *MockTaskMockRecorder) Execute() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockTask)(nil).Execute)) } // GetAttempt mocks base method. func (m *MockTask) GetAttempt() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAttempt") ret0, _ := ret[0].(int) return ret0 } // GetAttempt indicates an expected call of GetAttempt. func (mr *MockTaskMockRecorder) GetAttempt() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttempt", reflect.TypeOf((*MockTask)(nil).GetAttempt)) } // GetDomainID mocks base method. func (m *MockTask) GetDomainID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainID") ret0, _ := ret[0].(string) return ret0 } // GetDomainID indicates an expected call of GetDomainID. func (mr *MockTaskMockRecorder) GetDomainID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainID", reflect.TypeOf((*MockTask)(nil).GetDomainID)) } // GetInfo mocks base method. func (m *MockTask) GetInfo() persistence.Task { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetInfo") ret0, _ := ret[0].(persistence.Task) return ret0 } // GetInfo indicates an expected call of GetInfo. func (mr *MockTaskMockRecorder) GetInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInfo", reflect.TypeOf((*MockTask)(nil).GetInfo)) } // GetOriginalTaskList mocks base method. func (m *MockTask) GetOriginalTaskList() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOriginalTaskList") ret0, _ := ret[0].(string) return ret0 } // GetOriginalTaskList indicates an expected call of GetOriginalTaskList. func (mr *MockTaskMockRecorder) GetOriginalTaskList() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOriginalTaskList", reflect.TypeOf((*MockTask)(nil).GetOriginalTaskList)) } // GetOriginalTaskListKind mocks base method. func (m *MockTask) GetOriginalTaskListKind() types.TaskListKind { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOriginalTaskListKind") ret0, _ := ret[0].(types.TaskListKind) return ret0 } // GetOriginalTaskListKind indicates an expected call of GetOriginalTaskListKind. func (mr *MockTaskMockRecorder) GetOriginalTaskListKind() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOriginalTaskListKind", reflect.TypeOf((*MockTask)(nil).GetOriginalTaskListKind)) } // GetQueueType mocks base method. func (m *MockTask) GetQueueType() QueueType { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueType") ret0, _ := ret[0].(QueueType) return ret0 } // GetQueueType indicates an expected call of GetQueueType. func (mr *MockTaskMockRecorder) GetQueueType() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueType", reflect.TypeOf((*MockTask)(nil).GetQueueType)) } // GetRunID mocks base method. func (m *MockTask) GetRunID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRunID") ret0, _ := ret[0].(string) return ret0 } // GetRunID indicates an expected call of GetRunID. func (mr *MockTaskMockRecorder) GetRunID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRunID", reflect.TypeOf((*MockTask)(nil).GetRunID)) } // GetShard mocks base method. func (m *MockTask) GetShard() shard.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShard") ret0, _ := ret[0].(shard.Context) return ret0 } // GetShard indicates an expected call of GetShard. func (mr *MockTaskMockRecorder) GetShard() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShard", reflect.TypeOf((*MockTask)(nil).GetShard)) } // GetTaskCategory mocks base method. func (m *MockTask) GetTaskCategory() persistence.HistoryTaskCategory { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskCategory") ret0, _ := ret[0].(persistence.HistoryTaskCategory) return ret0 } // GetTaskCategory indicates an expected call of GetTaskCategory. func (mr *MockTaskMockRecorder) GetTaskCategory() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskCategory", reflect.TypeOf((*MockTask)(nil).GetTaskCategory)) } // GetTaskID mocks base method. func (m *MockTask) GetTaskID() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskID") ret0, _ := ret[0].(int64) return ret0 } // GetTaskID indicates an expected call of GetTaskID. func (mr *MockTaskMockRecorder) GetTaskID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskID", reflect.TypeOf((*MockTask)(nil).GetTaskID)) } // GetTaskKey mocks base method. func (m *MockTask) GetTaskKey() persistence.HistoryTaskKey { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskKey") ret0, _ := ret[0].(persistence.HistoryTaskKey) return ret0 } // GetTaskKey indicates an expected call of GetTaskKey. func (mr *MockTaskMockRecorder) GetTaskKey() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskKey", reflect.TypeOf((*MockTask)(nil).GetTaskKey)) } // GetTaskList mocks base method. func (m *MockTask) GetTaskList() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskList") ret0, _ := ret[0].(string) return ret0 } // GetTaskList indicates an expected call of GetTaskList. func (mr *MockTaskMockRecorder) GetTaskList() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskList", reflect.TypeOf((*MockTask)(nil).GetTaskList)) } // GetTaskType mocks base method. func (m *MockTask) GetTaskType() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskType") ret0, _ := ret[0].(int) return ret0 } // GetTaskType indicates an expected call of GetTaskType. func (mr *MockTaskMockRecorder) GetTaskType() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskType", reflect.TypeOf((*MockTask)(nil).GetTaskType)) } // GetVersion mocks base method. func (m *MockTask) GetVersion() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVersion") ret0, _ := ret[0].(int64) return ret0 } // GetVersion indicates an expected call of GetVersion. func (mr *MockTaskMockRecorder) GetVersion() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVersion", reflect.TypeOf((*MockTask)(nil).GetVersion)) } // GetVisibilityTimestamp mocks base method. func (m *MockTask) GetVisibilityTimestamp() time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVisibilityTimestamp") ret0, _ := ret[0].(time.Time) return ret0 } // GetVisibilityTimestamp indicates an expected call of GetVisibilityTimestamp. func (mr *MockTaskMockRecorder) GetVisibilityTimestamp() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVisibilityTimestamp", reflect.TypeOf((*MockTask)(nil).GetVisibilityTimestamp)) } // GetWorkflowID mocks base method. func (m *MockTask) GetWorkflowID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowID") ret0, _ := ret[0].(string) return ret0 } // GetWorkflowID indicates an expected call of GetWorkflowID. func (mr *MockTaskMockRecorder) GetWorkflowID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowID", reflect.TypeOf((*MockTask)(nil).GetWorkflowID)) } // HandleErr mocks base method. func (m *MockTask) HandleErr(err error) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleErr", err) ret0, _ := ret[0].(error) return ret0 } // HandleErr indicates an expected call of HandleErr. func (mr *MockTaskMockRecorder) HandleErr(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleErr", reflect.TypeOf((*MockTask)(nil).HandleErr), err) } // Nack mocks base method. func (m *MockTask) Nack(err error) { m.ctrl.T.Helper() m.ctrl.Call(m, "Nack", err) } // Nack indicates an expected call of Nack. func (mr *MockTaskMockRecorder) Nack(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Nack", reflect.TypeOf((*MockTask)(nil).Nack), err) } // Priority mocks base method. func (m *MockTask) Priority() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Priority") ret0, _ := ret[0].(int) return ret0 } // Priority indicates an expected call of Priority. func (mr *MockTaskMockRecorder) Priority() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Priority", reflect.TypeOf((*MockTask)(nil).Priority)) } // RetryErr mocks base method. func (m *MockTask) RetryErr(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RetryErr", err) ret0, _ := ret[0].(bool) return ret0 } // RetryErr indicates an expected call of RetryErr. func (mr *MockTaskMockRecorder) RetryErr(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetryErr", reflect.TypeOf((*MockTask)(nil).RetryErr), err) } // SetInitialSubmitTime mocks base method. func (m *MockTask) SetInitialSubmitTime(arg0 time.Time) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetInitialSubmitTime", arg0) } // SetInitialSubmitTime indicates an expected call of SetInitialSubmitTime. func (mr *MockTaskMockRecorder) SetInitialSubmitTime(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetInitialSubmitTime", reflect.TypeOf((*MockTask)(nil).SetInitialSubmitTime), arg0) } // SetPriority mocks base method. func (m *MockTask) SetPriority(arg0 int) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetPriority", arg0) } // SetPriority indicates an expected call of SetPriority. func (mr *MockTaskMockRecorder) SetPriority(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPriority", reflect.TypeOf((*MockTask)(nil).SetPriority), arg0) } // SetTaskID mocks base method. func (m *MockTask) SetTaskID(id int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetTaskID", id) } // SetTaskID indicates an expected call of SetTaskID. func (mr *MockTaskMockRecorder) SetTaskID(id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTaskID", reflect.TypeOf((*MockTask)(nil).SetTaskID), id) } // SetVersion mocks base method. func (m *MockTask) SetVersion(version int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetVersion", version) } // SetVersion indicates an expected call of SetVersion. func (mr *MockTaskMockRecorder) SetVersion(version any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVersion", reflect.TypeOf((*MockTask)(nil).SetVersion), version) } // SetVisibilityTimestamp mocks base method. func (m *MockTask) SetVisibilityTimestamp(timestamp time.Time) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetVisibilityTimestamp", timestamp) } // SetVisibilityTimestamp indicates an expected call of SetVisibilityTimestamp. func (mr *MockTaskMockRecorder) SetVisibilityTimestamp(timestamp any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVisibilityTimestamp", reflect.TypeOf((*MockTask)(nil).SetVisibilityTimestamp), timestamp) } // State mocks base method. func (m *MockTask) State() task.State { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "State") ret0, _ := ret[0].(task.State) return ret0 } // State indicates an expected call of State. func (mr *MockTaskMockRecorder) State() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "State", reflect.TypeOf((*MockTask)(nil).State)) } // ToInternalReplicationTaskInfo mocks base method. func (m *MockTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ToInternalReplicationTaskInfo") ret0, _ := ret[0].(*types.ReplicationTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ToInternalReplicationTaskInfo indicates an expected call of ToInternalReplicationTaskInfo. func (mr *MockTaskMockRecorder) ToInternalReplicationTaskInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToInternalReplicationTaskInfo", reflect.TypeOf((*MockTask)(nil).ToInternalReplicationTaskInfo)) } // ToTimerTaskInfo mocks base method. func (m *MockTask) ToTimerTaskInfo() (*persistence.TimerTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ToTimerTaskInfo") ret0, _ := ret[0].(*persistence.TimerTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ToTimerTaskInfo indicates an expected call of ToTimerTaskInfo. func (mr *MockTaskMockRecorder) ToTimerTaskInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToTimerTaskInfo", reflect.TypeOf((*MockTask)(nil).ToTimerTaskInfo)) } // ToTransferTaskInfo mocks base method. func (m *MockTask) ToTransferTaskInfo() (*persistence.TransferTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ToTransferTaskInfo") ret0, _ := ret[0].(*persistence.TransferTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ToTransferTaskInfo indicates an expected call of ToTransferTaskInfo. func (mr *MockTaskMockRecorder) ToTransferTaskInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToTransferTaskInfo", reflect.TypeOf((*MockTask)(nil).ToTransferTaskInfo)) } // MockCrossClusterTask is a mock of CrossClusterTask interface. type MockCrossClusterTask struct { ctrl *gomock.Controller recorder *MockCrossClusterTaskMockRecorder isgomock struct{} } // MockCrossClusterTaskMockRecorder is the mock recorder for MockCrossClusterTask. type MockCrossClusterTaskMockRecorder struct { mock *MockCrossClusterTask } // NewMockCrossClusterTask creates a new mock instance. func NewMockCrossClusterTask(ctrl *gomock.Controller) *MockCrossClusterTask { mock := &MockCrossClusterTask{ctrl: ctrl} mock.recorder = &MockCrossClusterTaskMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCrossClusterTask) EXPECT() *MockCrossClusterTaskMockRecorder { return m.recorder } // Ack mocks base method. func (m *MockCrossClusterTask) Ack() { m.ctrl.T.Helper() m.ctrl.Call(m, "Ack") } // Ack indicates an expected call of Ack. func (mr *MockCrossClusterTaskMockRecorder) Ack() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ack", reflect.TypeOf((*MockCrossClusterTask)(nil).Ack)) } // ByteSize mocks base method. func (m *MockCrossClusterTask) ByteSize() uint64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ByteSize") ret0, _ := ret[0].(uint64) return ret0 } // ByteSize indicates an expected call of ByteSize. func (mr *MockCrossClusterTaskMockRecorder) ByteSize() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByteSize", reflect.TypeOf((*MockCrossClusterTask)(nil).ByteSize)) } // Cancel mocks base method. func (m *MockCrossClusterTask) Cancel() { m.ctrl.T.Helper() m.ctrl.Call(m, "Cancel") } // Cancel indicates an expected call of Cancel. func (mr *MockCrossClusterTaskMockRecorder) Cancel() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cancel", reflect.TypeOf((*MockCrossClusterTask)(nil).Cancel)) } // Execute mocks base method. func (m *MockCrossClusterTask) Execute() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Execute") ret0, _ := ret[0].(error) return ret0 } // Execute indicates an expected call of Execute. func (mr *MockCrossClusterTaskMockRecorder) Execute() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockCrossClusterTask)(nil).Execute)) } // GetAttempt mocks base method. func (m *MockCrossClusterTask) GetAttempt() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAttempt") ret0, _ := ret[0].(int) return ret0 } // GetAttempt indicates an expected call of GetAttempt. func (mr *MockCrossClusterTaskMockRecorder) GetAttempt() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttempt", reflect.TypeOf((*MockCrossClusterTask)(nil).GetAttempt)) } // GetCrossClusterRequest mocks base method. func (m *MockCrossClusterTask) GetCrossClusterRequest() (*types.CrossClusterTaskRequest, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCrossClusterRequest") ret0, _ := ret[0].(*types.CrossClusterTaskRequest) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCrossClusterRequest indicates an expected call of GetCrossClusterRequest. func (mr *MockCrossClusterTaskMockRecorder) GetCrossClusterRequest() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCrossClusterRequest", reflect.TypeOf((*MockCrossClusterTask)(nil).GetCrossClusterRequest)) } // GetDomainID mocks base method. func (m *MockCrossClusterTask) GetDomainID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDomainID") ret0, _ := ret[0].(string) return ret0 } // GetDomainID indicates an expected call of GetDomainID. func (mr *MockCrossClusterTaskMockRecorder) GetDomainID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainID", reflect.TypeOf((*MockCrossClusterTask)(nil).GetDomainID)) } // GetInfo mocks base method. func (m *MockCrossClusterTask) GetInfo() persistence.Task { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetInfo") ret0, _ := ret[0].(persistence.Task) return ret0 } // GetInfo indicates an expected call of GetInfo. func (mr *MockCrossClusterTaskMockRecorder) GetInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInfo", reflect.TypeOf((*MockCrossClusterTask)(nil).GetInfo)) } // GetOriginalTaskList mocks base method. func (m *MockCrossClusterTask) GetOriginalTaskList() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOriginalTaskList") ret0, _ := ret[0].(string) return ret0 } // GetOriginalTaskList indicates an expected call of GetOriginalTaskList. func (mr *MockCrossClusterTaskMockRecorder) GetOriginalTaskList() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOriginalTaskList", reflect.TypeOf((*MockCrossClusterTask)(nil).GetOriginalTaskList)) } // GetOriginalTaskListKind mocks base method. func (m *MockCrossClusterTask) GetOriginalTaskListKind() types.TaskListKind { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOriginalTaskListKind") ret0, _ := ret[0].(types.TaskListKind) return ret0 } // GetOriginalTaskListKind indicates an expected call of GetOriginalTaskListKind. func (mr *MockCrossClusterTaskMockRecorder) GetOriginalTaskListKind() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOriginalTaskListKind", reflect.TypeOf((*MockCrossClusterTask)(nil).GetOriginalTaskListKind)) } // GetQueueType mocks base method. func (m *MockCrossClusterTask) GetQueueType() QueueType { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetQueueType") ret0, _ := ret[0].(QueueType) return ret0 } // GetQueueType indicates an expected call of GetQueueType. func (mr *MockCrossClusterTaskMockRecorder) GetQueueType() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueueType", reflect.TypeOf((*MockCrossClusterTask)(nil).GetQueueType)) } // GetRunID mocks base method. func (m *MockCrossClusterTask) GetRunID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRunID") ret0, _ := ret[0].(string) return ret0 } // GetRunID indicates an expected call of GetRunID. func (mr *MockCrossClusterTaskMockRecorder) GetRunID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRunID", reflect.TypeOf((*MockCrossClusterTask)(nil).GetRunID)) } // GetShard mocks base method. func (m *MockCrossClusterTask) GetShard() shard.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShard") ret0, _ := ret[0].(shard.Context) return ret0 } // GetShard indicates an expected call of GetShard. func (mr *MockCrossClusterTaskMockRecorder) GetShard() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShard", reflect.TypeOf((*MockCrossClusterTask)(nil).GetShard)) } // GetTaskCategory mocks base method. func (m *MockCrossClusterTask) GetTaskCategory() persistence.HistoryTaskCategory { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskCategory") ret0, _ := ret[0].(persistence.HistoryTaskCategory) return ret0 } // GetTaskCategory indicates an expected call of GetTaskCategory. func (mr *MockCrossClusterTaskMockRecorder) GetTaskCategory() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskCategory", reflect.TypeOf((*MockCrossClusterTask)(nil).GetTaskCategory)) } // GetTaskID mocks base method. func (m *MockCrossClusterTask) GetTaskID() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskID") ret0, _ := ret[0].(int64) return ret0 } // GetTaskID indicates an expected call of GetTaskID. func (mr *MockCrossClusterTaskMockRecorder) GetTaskID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskID", reflect.TypeOf((*MockCrossClusterTask)(nil).GetTaskID)) } // GetTaskKey mocks base method. func (m *MockCrossClusterTask) GetTaskKey() persistence.HistoryTaskKey { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskKey") ret0, _ := ret[0].(persistence.HistoryTaskKey) return ret0 } // GetTaskKey indicates an expected call of GetTaskKey. func (mr *MockCrossClusterTaskMockRecorder) GetTaskKey() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskKey", reflect.TypeOf((*MockCrossClusterTask)(nil).GetTaskKey)) } // GetTaskList mocks base method. func (m *MockCrossClusterTask) GetTaskList() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskList") ret0, _ := ret[0].(string) return ret0 } // GetTaskList indicates an expected call of GetTaskList. func (mr *MockCrossClusterTaskMockRecorder) GetTaskList() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskList", reflect.TypeOf((*MockCrossClusterTask)(nil).GetTaskList)) } // GetTaskType mocks base method. func (m *MockCrossClusterTask) GetTaskType() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskType") ret0, _ := ret[0].(int) return ret0 } // GetTaskType indicates an expected call of GetTaskType. func (mr *MockCrossClusterTaskMockRecorder) GetTaskType() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskType", reflect.TypeOf((*MockCrossClusterTask)(nil).GetTaskType)) } // GetVersion mocks base method. func (m *MockCrossClusterTask) GetVersion() int64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVersion") ret0, _ := ret[0].(int64) return ret0 } // GetVersion indicates an expected call of GetVersion. func (mr *MockCrossClusterTaskMockRecorder) GetVersion() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVersion", reflect.TypeOf((*MockCrossClusterTask)(nil).GetVersion)) } // GetVisibilityTimestamp mocks base method. func (m *MockCrossClusterTask) GetVisibilityTimestamp() time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVisibilityTimestamp") ret0, _ := ret[0].(time.Time) return ret0 } // GetVisibilityTimestamp indicates an expected call of GetVisibilityTimestamp. func (mr *MockCrossClusterTaskMockRecorder) GetVisibilityTimestamp() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVisibilityTimestamp", reflect.TypeOf((*MockCrossClusterTask)(nil).GetVisibilityTimestamp)) } // GetWorkflowID mocks base method. func (m *MockCrossClusterTask) GetWorkflowID() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkflowID") ret0, _ := ret[0].(string) return ret0 } // GetWorkflowID indicates an expected call of GetWorkflowID. func (mr *MockCrossClusterTaskMockRecorder) GetWorkflowID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkflowID", reflect.TypeOf((*MockCrossClusterTask)(nil).GetWorkflowID)) } // HandleErr mocks base method. func (m *MockCrossClusterTask) HandleErr(err error) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleErr", err) ret0, _ := ret[0].(error) return ret0 } // HandleErr indicates an expected call of HandleErr. func (mr *MockCrossClusterTaskMockRecorder) HandleErr(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleErr", reflect.TypeOf((*MockCrossClusterTask)(nil).HandleErr), err) } // IsReadyForPoll mocks base method. func (m *MockCrossClusterTask) IsReadyForPoll() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsReadyForPoll") ret0, _ := ret[0].(bool) return ret0 } // IsReadyForPoll indicates an expected call of IsReadyForPoll. func (mr *MockCrossClusterTaskMockRecorder) IsReadyForPoll() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsReadyForPoll", reflect.TypeOf((*MockCrossClusterTask)(nil).IsReadyForPoll)) } // IsValid mocks base method. func (m *MockCrossClusterTask) IsValid() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsValid") ret0, _ := ret[0].(bool) return ret0 } // IsValid indicates an expected call of IsValid. func (mr *MockCrossClusterTaskMockRecorder) IsValid() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsValid", reflect.TypeOf((*MockCrossClusterTask)(nil).IsValid)) } // Nack mocks base method. func (m *MockCrossClusterTask) Nack(err error) { m.ctrl.T.Helper() m.ctrl.Call(m, "Nack", err) } // Nack indicates an expected call of Nack. func (mr *MockCrossClusterTaskMockRecorder) Nack(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Nack", reflect.TypeOf((*MockCrossClusterTask)(nil).Nack), err) } // Priority mocks base method. func (m *MockCrossClusterTask) Priority() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Priority") ret0, _ := ret[0].(int) return ret0 } // Priority indicates an expected call of Priority. func (mr *MockCrossClusterTaskMockRecorder) Priority() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Priority", reflect.TypeOf((*MockCrossClusterTask)(nil).Priority)) } // RecordResponse mocks base method. func (m *MockCrossClusterTask) RecordResponse(arg0 *types.CrossClusterTaskResponse) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordResponse", arg0) ret0, _ := ret[0].(error) return ret0 } // RecordResponse indicates an expected call of RecordResponse. func (mr *MockCrossClusterTaskMockRecorder) RecordResponse(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordResponse", reflect.TypeOf((*MockCrossClusterTask)(nil).RecordResponse), arg0) } // RetryErr mocks base method. func (m *MockCrossClusterTask) RetryErr(err error) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RetryErr", err) ret0, _ := ret[0].(bool) return ret0 } // RetryErr indicates an expected call of RetryErr. func (mr *MockCrossClusterTaskMockRecorder) RetryErr(err any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetryErr", reflect.TypeOf((*MockCrossClusterTask)(nil).RetryErr), err) } // SetInitialSubmitTime mocks base method. func (m *MockCrossClusterTask) SetInitialSubmitTime(arg0 time.Time) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetInitialSubmitTime", arg0) } // SetInitialSubmitTime indicates an expected call of SetInitialSubmitTime. func (mr *MockCrossClusterTaskMockRecorder) SetInitialSubmitTime(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetInitialSubmitTime", reflect.TypeOf((*MockCrossClusterTask)(nil).SetInitialSubmitTime), arg0) } // SetPriority mocks base method. func (m *MockCrossClusterTask) SetPriority(arg0 int) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetPriority", arg0) } // SetPriority indicates an expected call of SetPriority. func (mr *MockCrossClusterTaskMockRecorder) SetPriority(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPriority", reflect.TypeOf((*MockCrossClusterTask)(nil).SetPriority), arg0) } // SetTaskID mocks base method. func (m *MockCrossClusterTask) SetTaskID(id int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetTaskID", id) } // SetTaskID indicates an expected call of SetTaskID. func (mr *MockCrossClusterTaskMockRecorder) SetTaskID(id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTaskID", reflect.TypeOf((*MockCrossClusterTask)(nil).SetTaskID), id) } // SetVersion mocks base method. func (m *MockCrossClusterTask) SetVersion(version int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetVersion", version) } // SetVersion indicates an expected call of SetVersion. func (mr *MockCrossClusterTaskMockRecorder) SetVersion(version any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVersion", reflect.TypeOf((*MockCrossClusterTask)(nil).SetVersion), version) } // SetVisibilityTimestamp mocks base method. func (m *MockCrossClusterTask) SetVisibilityTimestamp(timestamp time.Time) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetVisibilityTimestamp", timestamp) } // SetVisibilityTimestamp indicates an expected call of SetVisibilityTimestamp. func (mr *MockCrossClusterTaskMockRecorder) SetVisibilityTimestamp(timestamp any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVisibilityTimestamp", reflect.TypeOf((*MockCrossClusterTask)(nil).SetVisibilityTimestamp), timestamp) } // State mocks base method. func (m *MockCrossClusterTask) State() task.State { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "State") ret0, _ := ret[0].(task.State) return ret0 } // State indicates an expected call of State. func (mr *MockCrossClusterTaskMockRecorder) State() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "State", reflect.TypeOf((*MockCrossClusterTask)(nil).State)) } // ToInternalReplicationTaskInfo mocks base method. func (m *MockCrossClusterTask) ToInternalReplicationTaskInfo() (*types.ReplicationTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ToInternalReplicationTaskInfo") ret0, _ := ret[0].(*types.ReplicationTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ToInternalReplicationTaskInfo indicates an expected call of ToInternalReplicationTaskInfo. func (mr *MockCrossClusterTaskMockRecorder) ToInternalReplicationTaskInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToInternalReplicationTaskInfo", reflect.TypeOf((*MockCrossClusterTask)(nil).ToInternalReplicationTaskInfo)) } // ToTimerTaskInfo mocks base method. func (m *MockCrossClusterTask) ToTimerTaskInfo() (*persistence.TimerTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ToTimerTaskInfo") ret0, _ := ret[0].(*persistence.TimerTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ToTimerTaskInfo indicates an expected call of ToTimerTaskInfo. func (mr *MockCrossClusterTaskMockRecorder) ToTimerTaskInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToTimerTaskInfo", reflect.TypeOf((*MockCrossClusterTask)(nil).ToTimerTaskInfo)) } // ToTransferTaskInfo mocks base method. func (m *MockCrossClusterTask) ToTransferTaskInfo() (*persistence.TransferTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ToTransferTaskInfo") ret0, _ := ret[0].(*persistence.TransferTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ToTransferTaskInfo indicates an expected call of ToTransferTaskInfo. func (mr *MockCrossClusterTaskMockRecorder) ToTransferTaskInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToTransferTaskInfo", reflect.TypeOf((*MockCrossClusterTask)(nil).ToTransferTaskInfo)) } // MockKey is a mock of Key interface. type MockKey struct { ctrl *gomock.Controller recorder *MockKeyMockRecorder isgomock struct{} } // MockKeyMockRecorder is the mock recorder for MockKey. type MockKeyMockRecorder struct { mock *MockKey } // NewMockKey creates a new mock instance. func NewMockKey(ctrl *gomock.Controller) *MockKey { mock := &MockKey{ctrl: ctrl} mock.recorder = &MockKeyMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockKey) EXPECT() *MockKeyMockRecorder { return m.recorder } // Less mocks base method. func (m *MockKey) Less(arg0 Key) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Less", arg0) ret0, _ := ret[0].(bool) return ret0 } // Less indicates an expected call of Less. func (mr *MockKeyMockRecorder) Less(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Less", reflect.TypeOf((*MockKey)(nil).Less), arg0) } // MockExecutor is a mock of Executor interface. type MockExecutor struct { ctrl *gomock.Controller recorder *MockExecutorMockRecorder isgomock struct{} } // MockExecutorMockRecorder is the mock recorder for MockExecutor. type MockExecutorMockRecorder struct { mock *MockExecutor } // NewMockExecutor creates a new mock instance. func NewMockExecutor(ctrl *gomock.Controller) *MockExecutor { mock := &MockExecutor{ctrl: ctrl} mock.recorder = &MockExecutorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockExecutor) EXPECT() *MockExecutorMockRecorder { return m.recorder } // Execute mocks base method. func (m *MockExecutor) Execute(task Task) (ExecuteResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Execute", task) ret0, _ := ret[0].(ExecuteResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Execute indicates an expected call of Execute. func (mr *MockExecutorMockRecorder) Execute(task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockExecutor)(nil).Execute), task) } // Stop mocks base method. func (m *MockExecutor) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockExecutorMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockExecutor)(nil).Stop)) } // MockPriorityAssigner is a mock of PriorityAssigner interface. type MockPriorityAssigner struct { ctrl *gomock.Controller recorder *MockPriorityAssignerMockRecorder isgomock struct{} } // MockPriorityAssignerMockRecorder is the mock recorder for MockPriorityAssigner. type MockPriorityAssignerMockRecorder struct { mock *MockPriorityAssigner } // NewMockPriorityAssigner creates a new mock instance. func NewMockPriorityAssigner(ctrl *gomock.Controller) *MockPriorityAssigner { mock := &MockPriorityAssigner{ctrl: ctrl} mock.recorder = &MockPriorityAssignerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPriorityAssigner) EXPECT() *MockPriorityAssignerMockRecorder { return m.recorder } // Assign mocks base method. func (m *MockPriorityAssigner) Assign(arg0 Task) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Assign", arg0) ret0, _ := ret[0].(error) return ret0 } // Assign indicates an expected call of Assign. func (mr *MockPriorityAssignerMockRecorder) Assign(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Assign", reflect.TypeOf((*MockPriorityAssigner)(nil).Assign), arg0) } // MockProcessor is a mock of Processor interface. type MockProcessor struct { ctrl *gomock.Controller recorder *MockProcessorMockRecorder isgomock struct{} } // MockProcessorMockRecorder is the mock recorder for MockProcessor. type MockProcessorMockRecorder struct { mock *MockProcessor } // NewMockProcessor creates a new mock instance. func NewMockProcessor(ctrl *gomock.Controller) *MockProcessor { mock := &MockProcessor{ctrl: ctrl} mock.recorder = &MockProcessorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProcessor) EXPECT() *MockProcessorMockRecorder { return m.recorder } // Start mocks base method. func (m *MockProcessor) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockProcessorMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockProcessor)(nil).Start)) } // Stop mocks base method. func (m *MockProcessor) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockProcessorMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockProcessor)(nil).Stop)) } // Submit mocks base method. func (m *MockProcessor) Submit(arg0 Task) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Submit", arg0) ret0, _ := ret[0].(error) return ret0 } // Submit indicates an expected call of Submit. func (mr *MockProcessorMockRecorder) Submit(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Submit", reflect.TypeOf((*MockProcessor)(nil).Submit), arg0) } // TrySubmit mocks base method. func (m *MockProcessor) TrySubmit(arg0 Task) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TrySubmit", arg0) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // TrySubmit indicates an expected call of TrySubmit. func (mr *MockProcessorMockRecorder) TrySubmit(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TrySubmit", reflect.TypeOf((*MockProcessor)(nil).TrySubmit), arg0) } // MockRedispatcher is a mock of Redispatcher interface. type MockRedispatcher struct { ctrl *gomock.Controller recorder *MockRedispatcherMockRecorder isgomock struct{} } // MockRedispatcherMockRecorder is the mock recorder for MockRedispatcher. type MockRedispatcherMockRecorder struct { mock *MockRedispatcher } // NewMockRedispatcher creates a new mock instance. func NewMockRedispatcher(ctrl *gomock.Controller) *MockRedispatcher { mock := &MockRedispatcher{ctrl: ctrl} mock.recorder = &MockRedispatcherMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockRedispatcher) EXPECT() *MockRedispatcherMockRecorder { return m.recorder } // AddTask mocks base method. func (m *MockRedispatcher) AddTask(arg0 Task) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddTask", arg0) } // AddTask indicates an expected call of AddTask. func (mr *MockRedispatcherMockRecorder) AddTask(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTask", reflect.TypeOf((*MockRedispatcher)(nil).AddTask), arg0) } // Redispatch mocks base method. func (m *MockRedispatcher) Redispatch(targetSize int) { m.ctrl.T.Helper() m.ctrl.Call(m, "Redispatch", targetSize) } // Redispatch indicates an expected call of Redispatch. func (mr *MockRedispatcherMockRecorder) Redispatch(targetSize any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Redispatch", reflect.TypeOf((*MockRedispatcher)(nil).Redispatch), targetSize) } // RedispatchTask mocks base method. func (m *MockRedispatcher) RedispatchTask(arg0 Task, arg1 time.Time) { m.ctrl.T.Helper() m.ctrl.Call(m, "RedispatchTask", arg0, arg1) } // RedispatchTask indicates an expected call of RedispatchTask. func (mr *MockRedispatcherMockRecorder) RedispatchTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RedispatchTask", reflect.TypeOf((*MockRedispatcher)(nil).RedispatchTask), arg0, arg1) } // Size mocks base method. func (m *MockRedispatcher) Size() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Size") ret0, _ := ret[0].(int) return ret0 } // Size indicates an expected call of Size. func (mr *MockRedispatcherMockRecorder) Size() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Size", reflect.TypeOf((*MockRedispatcher)(nil).Size)) } // Start mocks base method. func (m *MockRedispatcher) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockRedispatcherMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockRedispatcher)(nil).Start)) } // Stop mocks base method. func (m *MockRedispatcher) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockRedispatcherMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockRedispatcher)(nil).Stop)) } // MockRescheduler is a mock of Rescheduler interface. type MockRescheduler struct { ctrl *gomock.Controller recorder *MockReschedulerMockRecorder isgomock struct{} } // MockReschedulerMockRecorder is the mock recorder for MockRescheduler. type MockReschedulerMockRecorder struct { mock *MockRescheduler } // NewMockRescheduler creates a new mock instance. func NewMockRescheduler(ctrl *gomock.Controller) *MockRescheduler { mock := &MockRescheduler{ctrl: ctrl} mock.recorder = &MockReschedulerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockRescheduler) EXPECT() *MockReschedulerMockRecorder { return m.recorder } // RescheduleDomains mocks base method. func (m *MockRescheduler) RescheduleDomains(domainIDs map[string]struct{}) { m.ctrl.T.Helper() m.ctrl.Call(m, "RescheduleDomains", domainIDs) } // RescheduleDomains indicates an expected call of RescheduleDomains. func (mr *MockReschedulerMockRecorder) RescheduleDomains(domainIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RescheduleDomains", reflect.TypeOf((*MockRescheduler)(nil).RescheduleDomains), domainIDs) } // RescheduleTask mocks base method. func (m *MockRescheduler) RescheduleTask(arg0 Task, arg1 time.Time) { m.ctrl.T.Helper() m.ctrl.Call(m, "RescheduleTask", arg0, arg1) } // RescheduleTask indicates an expected call of RescheduleTask. func (mr *MockReschedulerMockRecorder) RescheduleTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RescheduleTask", reflect.TypeOf((*MockRescheduler)(nil).RescheduleTask), arg0, arg1) } // Size mocks base method. func (m *MockRescheduler) Size() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Size") ret0, _ := ret[0].(int) return ret0 } // Size indicates an expected call of Size. func (mr *MockReschedulerMockRecorder) Size() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Size", reflect.TypeOf((*MockRescheduler)(nil).Size)) } // Start mocks base method. func (m *MockRescheduler) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockReschedulerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockRescheduler)(nil).Start)) } // Stop mocks base method. func (m *MockRescheduler) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockReschedulerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockRescheduler)(nil).Stop)) } // MockFetcher is a mock of Fetcher interface. type MockFetcher struct { ctrl *gomock.Controller recorder *MockFetcherMockRecorder isgomock struct{} } // MockFetcherMockRecorder is the mock recorder for MockFetcher. type MockFetcherMockRecorder struct { mock *MockFetcher } // NewMockFetcher creates a new mock instance. func NewMockFetcher(ctrl *gomock.Controller) *MockFetcher { mock := &MockFetcher{ctrl: ctrl} mock.recorder = &MockFetcherMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFetcher) EXPECT() *MockFetcherMockRecorder { return m.recorder } // Fetch mocks base method. func (m *MockFetcher) Fetch(shardID int, fetchParams ...any) future.Future { m.ctrl.T.Helper() varargs := []any{shardID} for _, a := range fetchParams { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Fetch", varargs...) ret0, _ := ret[0].(future.Future) return ret0 } // Fetch indicates an expected call of Fetch. func (mr *MockFetcherMockRecorder) Fetch(shardID any, fetchParams ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{shardID}, fetchParams...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fetch", reflect.TypeOf((*MockFetcher)(nil).Fetch), varargs...) } // GetSourceCluster mocks base method. func (m *MockFetcher) GetSourceCluster() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSourceCluster") ret0, _ := ret[0].(string) return ret0 } // GetSourceCluster indicates an expected call of GetSourceCluster. func (mr *MockFetcherMockRecorder) GetSourceCluster() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSourceCluster", reflect.TypeOf((*MockFetcher)(nil).GetSourceCluster)) } // Start mocks base method. func (m *MockFetcher) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockFetcherMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockFetcher)(nil).Start)) } // Stop mocks base method. func (m *MockFetcher) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockFetcherMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockFetcher)(nil).Stop)) } ================================================ FILE: service/history/task/priority_assigner.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "sync" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" dynamicquotas "github.com/uber/cadence/common/dynamicconfig/quotas" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" ) var ( highTaskPriority = constants.GetTaskPriority(constants.HighPriorityClass, constants.DefaultPrioritySubclass) defaultTaskPriority = constants.GetTaskPriority(constants.DefaultPriorityClass, constants.DefaultPrioritySubclass) lowTaskPriority = constants.GetTaskPriority(constants.LowPriorityClass, constants.DefaultPrioritySubclass) ) type priorityAssignerImpl struct { sync.RWMutex currentClusterName string domainCache cache.DomainCache config *config.Config logger log.Logger scope metrics.Scope rateLimiters *quotas.Collection activeClusterMgr activecluster.Manager } // NewPriorityAssigner creates a new task priority assigner func NewPriorityAssigner( currentClusterName string, domainCache cache.DomainCache, activeClusterMgr activecluster.Manager, logger log.Logger, metricClient metrics.Client, config *config.Config, ) PriorityAssigner { return &priorityAssignerImpl{ currentClusterName: currentClusterName, domainCache: domainCache, activeClusterMgr: activeClusterMgr, config: config, logger: logger, scope: metricClient.Scope(metrics.TaskPriorityAssignerScope), rateLimiters: quotas.NewCollection(dynamicquotas.NewSimpleDynamicRateLimiterFactory( config.TaskProcessRPS, )), } } func (a *priorityAssignerImpl) Assign(queueTask Task) error { if priority := queueTask.Priority(); priority != noPriority { if priority != lowTaskPriority && queueTask.GetAttempt() > a.config.TaskCriticalRetryCount() { // automatically lower the priority if task attempt exceeds certain threshold queueTask.SetPriority(lowTaskPriority) } return nil } queueType := queueTask.GetQueueType() if queueType == QueueTypeReplication { queueTask.SetPriority(lowTaskPriority) return nil } // timer, transfer or cross cluster task, first check if task is active or not and if domain is active or not isActiveTask := queueType == QueueTypeActiveTimer || queueType == QueueTypeActiveTransfer domainName, isActiveDomain, err := a.getDomainInfo(queueTask.GetDomainID(), queueTask.GetWorkflowID(), queueTask.GetRunID()) if err != nil { return err } // there are four cases here: // 1. active task for active domain // 2. active task for standby domain // 3. standby task for active domain // 4. standby task for standby domain if !isActiveTask && !isActiveDomain { // only assign low priority to tasks in the fourth case queueTask.SetPriority(lowTaskPriority) return nil } // for case 1 we should give the task a high priority // for case 2 and 3 the task will be a no-op in most cases, also give it a high priority so that // it can be quickly verified/acked and won't prevent the ack level in the processor from advancing // (especially for active processor) if !a.rateLimiters.For(domainName).Allow() { queueTask.SetPriority(defaultTaskPriority) taggedScope := a.scope.Tagged(metrics.DomainTag(domainName)) switch queueType { case QueueTypeActiveTransfer, QueueTypeStandbyTransfer: taggedScope.IncCounter(metrics.TransferTaskThrottledCounter) case QueueTypeActiveTimer, QueueTypeStandbyTimer: taggedScope.IncCounter(metrics.TimerTaskThrottledCounter) } return nil } queueTask.SetPriority(highTaskPriority) return nil } // getDomainInfo returns three pieces of information: // 1. domain name // 2. if domain is active // 3. error, if any func (a *priorityAssignerImpl) getDomainInfo(domainID, wfID, rID string) (string, bool, error) { domainEntry, err := a.domainCache.GetDomainByID(domainID) if err != nil { if _, ok := err.(*types.EntityNotExistsError); !ok { a.logger.Warn("Cannot find domain", tag.WorkflowDomainID(domainID)) return "", false, err } // it is possible that the domain is deleted // we should treat that domain as active a.logger.Warn("Cannot find domain, treat as active ", tag.WorkflowDomainID(domainID)) return "", true, nil } activeClusterInfo, err := a.activeClusterMgr.GetActiveClusterInfoByWorkflow(context.Background(), domainID, wfID, rID) if err != nil { a.logger.Warn("Failed to get active cluster info", tag.WorkflowDomainID(domainID), tag.Error(err)) return "", true, nil } if activeClusterInfo.ActiveClusterName != a.currentClusterName { return domainEntry.GetInfo().Name, false, nil } return domainEntry.GetInfo().Name, true, nil } ================================================ FILE: service/history/task/priority_assigner_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "errors" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" ) type ( taskPriorityAssignerSuite struct { *require.Assertions suite.Suite controller *gomock.Controller mockDomainCache *cache.MockDomainCache mockActiveClusterMgr *activecluster.MockManager config *config.Config priorityAssigner *priorityAssignerImpl testTaskProcessRPS int } ) func TestTaskPriorityAssignerSuite(t *testing.T) { s := new(taskPriorityAssignerSuite) suite.Run(t, s) } func (s *taskPriorityAssignerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockDomainCache = cache.NewMockDomainCache(s.controller) s.mockActiveClusterMgr = activecluster.NewMockManager(s.controller) s.testTaskProcessRPS = 10 client := dynamicconfig.NewInMemoryClient() err := client.UpdateValue(dynamicproperties.TaskProcessRPS, s.testTaskProcessRPS) s.NoError(err) dc := dynamicconfig.NewCollection(client, log.NewNoop()) s.config = config.NewForTest() s.config.TaskProcessRPS = dc.GetIntPropertyFilteredByDomain(dynamicproperties.TaskProcessRPS) s.priorityAssigner = NewPriorityAssigner( cluster.TestCurrentClusterName, s.mockDomainCache, s.mockActiveClusterMgr, log.NewNoop(), metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}), s.config, ).(*priorityAssignerImpl) } func (s *taskPriorityAssignerSuite) TearDownTest() { s.controller.Finish() } func (s *taskPriorityAssignerSuite) TestGetDomainInfo_Success_Active() { s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil) s.mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) domainName, isActive, err := s.priorityAssigner.getDomainInfo(constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID) s.NoError(err) s.Equal(constants.TestDomainName, domainName) s.True(isActive) } func (s *taskPriorityAssignerSuite) TestGetDomainInfo_Success_Passive() { s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil) s.mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestAlternativeClusterName}, nil) domainName, isActive, err := s.priorityAssigner.getDomainInfo(constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID) s.NoError(err) s.Equal(constants.TestDomainName, domainName) s.False(isActive) } func (s *taskPriorityAssignerSuite) TestGetDomainInfo_Fail_DomainNotExist() { s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return( nil, &types.EntityNotExistsError{Message: "domain not exist"}, ) domainName, isActive, err := s.priorityAssigner.getDomainInfo(constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID) s.NoError(err) s.Empty(domainName) s.True(isActive) } func (s *taskPriorityAssignerSuite) TestGetDomainInfo_Fail_UnknownError() { s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return( nil, errors.New("some random error"), ) domainName, isActive, err := s.priorityAssigner.getDomainInfo(constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID) s.Error(err) s.Empty(domainName) s.False(isActive) } func (s *taskPriorityAssignerSuite) TestAssign_ReplicationTask() { mockTask := NewMockTask(s.controller) mockTask.EXPECT().GetQueueType().Return(QueueTypeReplication).Times(1) mockTask.EXPECT().Priority().Return(noPriority).Times(1) mockTask.EXPECT().SetPriority(commonconstants.GetTaskPriority(commonconstants.LowPriorityClass, commonconstants.DefaultPrioritySubclass)).Times(1) err := s.priorityAssigner.Assign(mockTask) s.NoError(err) } func (s *taskPriorityAssignerSuite) TestAssign_StandbyTask_StandbyDomain() { s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil) s.mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestAlternativeClusterName}, nil) mockTask := NewMockTask(s.controller) mockTask.EXPECT().GetQueueType().Return(QueueTypeStandbyTransfer).AnyTimes() mockTask.EXPECT().GetDomainID().Return(constants.TestDomainID).Times(1) mockTask.EXPECT().GetWorkflowID().Return(constants.TestWorkflowID).Times(1) mockTask.EXPECT().GetRunID().Return(constants.TestRunID).Times(1) mockTask.EXPECT().Priority().Return(noPriority).Times(1) mockTask.EXPECT().SetPriority(commonconstants.GetTaskPriority(commonconstants.LowPriorityClass, commonconstants.DefaultPrioritySubclass)).Times(1) err := s.priorityAssigner.Assign(mockTask) s.NoError(err) } func (s *taskPriorityAssignerSuite) TestAssign_StandbyTask_ActiveDomain() { s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil) s.mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) mockTask := NewMockTask(s.controller) mockTask.EXPECT().GetQueueType().Return(QueueTypeStandbyTransfer).AnyTimes() mockTask.EXPECT().GetDomainID().Return(constants.TestDomainID).Times(1) mockTask.EXPECT().GetWorkflowID().Return(constants.TestWorkflowID).Times(1) mockTask.EXPECT().GetRunID().Return(constants.TestRunID).Times(1) mockTask.EXPECT().Priority().Return(noPriority).Times(1) mockTask.EXPECT().SetPriority(commonconstants.GetTaskPriority(commonconstants.HighPriorityClass, commonconstants.DefaultPrioritySubclass)).Times(1) err := s.priorityAssigner.Assign(mockTask) s.NoError(err) } func (s *taskPriorityAssignerSuite) TestAssign_ActiveTask_StandbyDomain() { s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil) s.mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestAlternativeClusterName}, nil) mockTask := NewMockTask(s.controller) mockTask.EXPECT().GetQueueType().Return(QueueTypeActiveTimer).AnyTimes() mockTask.EXPECT().GetDomainID().Return(constants.TestDomainID).Times(1) mockTask.EXPECT().GetWorkflowID().Return(constants.TestWorkflowID).Times(1) mockTask.EXPECT().GetRunID().Return(constants.TestRunID).Times(1) mockTask.EXPECT().Priority().Return(noPriority).Times(1) mockTask.EXPECT().SetPriority(commonconstants.GetTaskPriority(commonconstants.HighPriorityClass, commonconstants.DefaultPrioritySubclass)).Times(1) err := s.priorityAssigner.Assign(mockTask) s.NoError(err) } func (s *taskPriorityAssignerSuite) TestAssign_ActiveTransferTask_ActiveDomain() { s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil) s.mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) mockTask := NewMockTask(s.controller) mockTask.EXPECT().GetQueueType().Return(QueueTypeActiveTransfer).AnyTimes() mockTask.EXPECT().GetDomainID().Return(constants.TestDomainID).Times(1) mockTask.EXPECT().GetWorkflowID().Return(constants.TestWorkflowID).Times(1) mockTask.EXPECT().GetRunID().Return(constants.TestRunID).Times(1) mockTask.EXPECT().Priority().Return(noPriority).Times(1) mockTask.EXPECT().SetPriority(commonconstants.GetTaskPriority(commonconstants.HighPriorityClass, commonconstants.DefaultPrioritySubclass)).Times(1) err := s.priorityAssigner.Assign(mockTask) s.NoError(err) } func (s *taskPriorityAssignerSuite) TestAssign_ActiveTimerTask_ActiveDomain() { s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil) s.mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil) mockTask := NewMockTask(s.controller) mockTask.EXPECT().GetQueueType().Return(QueueTypeActiveTimer).AnyTimes() mockTask.EXPECT().GetDomainID().Return(constants.TestDomainID).Times(1) mockTask.EXPECT().GetWorkflowID().Return(constants.TestWorkflowID).Times(1) mockTask.EXPECT().GetRunID().Return(constants.TestRunID).Times(1) mockTask.EXPECT().Priority().Return(noPriority).Times(1) mockTask.EXPECT().SetPriority(commonconstants.GetTaskPriority(commonconstants.HighPriorityClass, commonconstants.DefaultPrioritySubclass)).Times(1) err := s.priorityAssigner.Assign(mockTask) s.NoError(err) } func (s *taskPriorityAssignerSuite) TestAssign_ThrottledTask() { s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil).AnyTimes() s.mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestCurrentClusterName}, nil).AnyTimes() for i := 0; i != s.testTaskProcessRPS*2; i++ { mockTask := NewMockTask(s.controller) mockTask.EXPECT().GetQueueType().Return(QueueTypeActiveTimer).AnyTimes() mockTask.EXPECT().GetDomainID().Return(constants.TestDomainID).Times(1) mockTask.EXPECT().GetWorkflowID().Return(constants.TestWorkflowID).Times(1) mockTask.EXPECT().GetRunID().Return(constants.TestRunID).Times(1) mockTask.EXPECT().Priority().Return(noPriority).Times(1) if i < s.testTaskProcessRPS { mockTask.EXPECT().SetPriority(commonconstants.GetTaskPriority(commonconstants.HighPriorityClass, commonconstants.DefaultPrioritySubclass)).Times(1) } else { mockTask.EXPECT().SetPriority(commonconstants.GetTaskPriority(commonconstants.DefaultPriorityClass, commonconstants.DefaultPrioritySubclass)).Times(1) } err := s.priorityAssigner.Assign(mockTask) s.NoError(err) } } func (s *taskPriorityAssignerSuite) TestAssign_AlreadyAssigned() { priority := 5 // case 1: task attempt less than critical retry count mockTask := NewMockTask(s.controller) mockTask.EXPECT().Priority().Return(priority).Times(1) mockTask.EXPECT().GetAttempt().Return(s.config.TaskCriticalRetryCount() - 1).Times(1) err := s.priorityAssigner.Assign(mockTask) s.NoError(err) // case 2: task attempt higher than critical retry count mockTask.EXPECT().Priority().Return(priority).Times(1) mockTask.EXPECT().GetAttempt().Return(s.config.TaskCriticalRetryCount() + 1).Times(1) mockTask.EXPECT().SetPriority(lowTaskPriority).Times(1) err = s.priorityAssigner.Assign(mockTask) s.NoError(err) } func (s *taskPriorityAssignerSuite) TestGetTaskPriority() { testCases := []struct { class int subClass int expectedPriority int }{ { class: commonconstants.HighPriorityClass, subClass: commonconstants.DefaultPrioritySubclass, expectedPriority: 1, }, { class: commonconstants.DefaultPriorityClass, subClass: commonconstants.LowPrioritySubclass, expectedPriority: 10, }, { class: commonconstants.LowPriorityClass, subClass: commonconstants.HighPrioritySubclass, expectedPriority: 16, }, } for _, tc := range testCases { s.Equal(tc.expectedPriority, commonconstants.GetTaskPriority(tc.class, tc.subClass)) } } ================================================ FILE: service/history/task/processor.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "errors" "sync" "sync/atomic" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/task" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" ) type DomainPriorityKey struct { DomainID string Priority int } type processorImpl struct { sync.RWMutex priorityAssigner PriorityAssigner taskProcessor task.Processor scheduler task.Scheduler[Task] status int32 logger log.Logger metricsClient metrics.Client timeSource clock.TimeSource } var ( errTaskProcessorNotRunning = errors.New("queue task processor is not running") ) const ( ephemeralTaskListGroupKey = "__ephemeral__" ) // NewProcessor creates a new task processor func NewProcessor( priorityAssigner PriorityAssigner, config *config.Config, logger log.Logger, metricsClient metrics.Client, timeSource clock.TimeSource, domainCache cache.DomainCache, ) (Processor, error) { taskProcessor := task.NewParallelTaskProcessor( logger, metricsClient, &task.ParallelTaskProcessorOptions{ QueueSize: 1, WorkerCount: config.TaskSchedulerWorkerCount, RetryPolicy: common.CreateTaskProcessingRetryPolicy(), }, ) var scheduler task.Scheduler[Task] var err error if config.EnableHierarchicalWeightedRoundRobinTaskScheduler() { taskToWeightedKeysFn := func(t Task) []task.WeightedKey[any] { var domainID, taskList string domainID = t.GetDomainID() taskList = t.GetOriginalTaskList() if t.GetOriginalTaskListKind() == types.TaskListKindEphemeral { taskList = ephemeralTaskListGroupKey } key := DomainPriorityKey{ DomainID: domainID, Priority: t.Priority(), } domainName, err := domainCache.GetDomainName(domainID) if err != nil { logger.Error("failed to get domain name from cache", tag.Error(err)) domainName = "" } if !config.EnableTaskListAwareTaskSchedulerByDomain(domainName) || t.Priority() != highTaskPriority { return []task.WeightedKey[any]{ { Key: key, Weight: getDomainPriorityWeight(logger, config, domainCache, key), }, } } return []task.WeightedKey[any]{ { Key: key, Weight: getDomainPriorityWeight(logger, config, domainCache, key), }, { Key: taskList, Weight: 1, }, } } scheduler, err = task.NewHierarchicalWeightedRoundRobinTaskScheduler( logger, metricsClient, timeSource, taskProcessor, &task.HierarchicalWeightedRoundRobinTaskPoolOptions[any, Task]{ BufferSize: config.TaskSchedulerQueueSize(), TaskToWeightedKeysFn: taskToWeightedKeysFn, }, ) if err != nil { return nil, err } } else { taskToChannelKeyFn := func(t Task) DomainPriorityKey { var domainID string domainID = t.GetDomainID() return DomainPriorityKey{ DomainID: domainID, Priority: t.Priority(), } } channelKeyToWeightFn := func(k DomainPriorityKey) int { return getDomainPriorityWeight(logger, config, domainCache, k) } scheduler, err = task.NewWeightedRoundRobinTaskScheduler( logger, metricsClient, timeSource, taskProcessor, &task.WeightedRoundRobinTaskSchedulerOptions[DomainPriorityKey, Task]{ QueueSize: config.TaskSchedulerQueueSize(), DispatcherCount: config.TaskSchedulerDispatcherCount(), TaskToChannelKeyFn: taskToChannelKeyFn, ChannelKeyToWeightFn: channelKeyToWeightFn, }, ) if err != nil { return nil, err } } return &processorImpl{ priorityAssigner: priorityAssigner, taskProcessor: taskProcessor, scheduler: scheduler, status: common.DaemonStatusInitialized, logger: logger, metricsClient: metricsClient, timeSource: timeSource, }, nil } func (p *processorImpl) Start() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } p.taskProcessor.Start() p.scheduler.Start() p.logger.Info("Queue task processor started.") } func (p *processorImpl) Stop() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } p.scheduler.Stop() p.taskProcessor.Stop() p.logger.Info("Queue task processor stopped.") } func (p *processorImpl) Submit(task Task) error { if err := p.priorityAssigner.Assign(task); err != nil { return err } return p.scheduler.Submit(task) } func (p *processorImpl) TrySubmit(task Task) (bool, error) { if err := p.priorityAssigner.Assign(task); err != nil { return false, err } return p.scheduler.TrySubmit(task) } func getDomainPriorityWeight( logger log.Logger, config *config.Config, domainCache cache.DomainCache, k DomainPriorityKey, ) int { var weights map[int]int domainName, err := domainCache.GetDomainName(k.DomainID) if err != nil { logger.Error("failed to get domain name from cache, use default round robin weights", tag.Error(err)) weights = dynamicproperties.DefaultTaskSchedulerRoundRobinWeights } else { weights, err = dynamicproperties.ConvertDynamicConfigMapPropertyToIntMap(config.TaskSchedulerDomainRoundRobinWeights(domainName)) if err != nil { logger.Error("failed to convert dynamic config map to int map, use default round robin weights", tag.Error(err)) weights = dynamicproperties.DefaultTaskSchedulerRoundRobinWeights } } weight, ok := weights[k.Priority] if !ok { logger.Error("weights not found for task priority, default to 1", tag.Dynamic("priority", k.Priority), tag.Dynamic("weights", weights)) weight = 1 } return weight } ================================================ FILE: service/history/task/processor_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "errors" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/task" "github.com/uber/cadence/service/history/config" ) type ( queueTaskProcessorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockDomainCache *cache.MockDomainCache mockPriorityAssigner *MockPriorityAssigner timeSource clock.TimeSource metricsClient metrics.Client logger log.Logger processor *processorImpl } ) func TestQueueTaskProcessorSuite(t *testing.T) { s := new(queueTaskProcessorSuite) suite.Run(t, s) } func (s *queueTaskProcessorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockDomainCache = cache.NewMockDomainCache(s.controller) s.mockPriorityAssigner = NewMockPriorityAssigner(s.controller) s.timeSource = clock.NewRealTimeSource() s.metricsClient = metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}) s.logger = testlogger.New(s.Suite.T()) s.processor = s.newTestQueueTaskProcessor() } func (s *queueTaskProcessorSuite) TearDownTest() {} func (s *queueTaskProcessorSuite) TestStartStop() { mockScheduler := task.NewMockScheduler[Task](s.controller) mockScheduler.EXPECT().Start().Times(1) mockScheduler.EXPECT().Stop().Times(1) s.processor.scheduler = mockScheduler s.processor.Start() s.processor.Stop() } func (s *queueTaskProcessorSuite) TestSubmit() { mockTask := NewMockTask(s.controller) s.mockPriorityAssigner.EXPECT().Assign(NewMockTaskMatcher(mockTask)).Return(nil).Times(1) mockScheduler := task.NewMockScheduler[Task](s.controller) mockScheduler.EXPECT().Submit(NewMockTaskMatcher(mockTask)).Return(nil).Times(1) s.processor.scheduler = mockScheduler err := s.processor.Submit(mockTask) s.NoError(err) } func (s *queueTaskProcessorSuite) TestTrySubmit_AssignPriorityFailed() { mockTask := NewMockTask(s.controller) errAssignPriority := errors.New("some randome error") s.mockPriorityAssigner.EXPECT().Assign(NewMockTaskMatcher(mockTask)).Return(errAssignPriority).Times(1) submitted, err := s.processor.TrySubmit(mockTask) s.Equal(errAssignPriority, err) s.False(submitted) } func (s *queueTaskProcessorSuite) TestTrySubmit_Fail() { mockTask := NewMockTask(s.controller) s.mockPriorityAssigner.EXPECT().Assign(NewMockTaskMatcher(mockTask)).Return(nil).Times(1) errTrySubmit := errors.New("some randome error") mockScheduler := task.NewMockScheduler[Task](s.controller) mockScheduler.EXPECT().TrySubmit(NewMockTaskMatcher(mockTask)).Return(false, errTrySubmit).Times(1) s.processor.scheduler = mockScheduler submitted, err := s.processor.TrySubmit(mockTask) s.Equal(errTrySubmit, err) s.False(submitted) } func (s *queueTaskProcessorSuite) TestNewSchedulerOptions_UnknownSchedulerType() { options, err := task.NewSchedulerOptions[int](0, 100, dynamicproperties.GetIntPropertyFn(10), 1, func(task.PriorityTask) int { return 1 }, func(int) int { return 1 }) s.Error(err) s.Nil(options) } func (s *queueTaskProcessorSuite) newTestQueueTaskProcessor() *processorImpl { config := config.NewForTest() processor, err := NewProcessor( s.mockPriorityAssigner, config, s.logger, s.metricsClient, s.timeSource, s.mockDomainCache, ) s.NoError(err) return processor.(*processorImpl) } func TestGetDomainPriorityWeight(t *testing.T) { testCases := []struct { name string mockSetup func(*cache.MockDomainCache, dynamicconfig.Client) expected int }{ { name: "success", mockSetup: func(mockDomainCache *cache.MockDomainCache, client dynamicconfig.Client) { mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("test-domain-name", nil).Times(1) client.UpdateValue(dynamicproperties.TaskSchedulerDomainRoundRobinWeights, map[string]interface{}{"1": 10}) }, expected: 10, }, { name: "domain cache error, use default", mockSetup: func(mockDomainCache *cache.MockDomainCache, client dynamicconfig.Client) { mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("", errors.New("test-error")).Times(1) client.UpdateValue(dynamicproperties.TaskSchedulerDomainRoundRobinWeights, map[string]interface{}{"1": 10}) }, expected: 500, }, { name: "invalid map value, use default", mockSetup: func(mockDomainCache *cache.MockDomainCache, client dynamicconfig.Client) { mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("test-domain-name", nil).Times(1) client.UpdateValue(dynamicproperties.TaskSchedulerDomainRoundRobinWeights, map[string]interface{}{"1": "invalid"}) }, expected: 500, }, { name: "unspecified priority", mockSetup: func(mockDomainCache *cache.MockDomainCache, client dynamicconfig.Client) { mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("test-domain-name", nil).Times(1) client.UpdateValue(dynamicproperties.TaskSchedulerDomainRoundRobinWeights, map[string]interface{}{"2": 10}) }, expected: 1, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(mockCtrl) client := dynamicconfig.NewInMemoryClient() config := config.New( dynamicconfig.NewCollection( client, testlogger.New(t), ), 1024, 1024, false, "hostname", ) tc.mockSetup(mockDomainCache, client) weight := getDomainPriorityWeight(testlogger.New(t), config, mockDomainCache, DomainPriorityKey{DomainID: "test-domain-id", Priority: 1}) require.Equal(t, tc.expected, weight) }) } } ================================================ FILE: service/history/task/rate_limited_processor.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package task import ( "context" "sync/atomic" "github.com/uber/cadence/common" ) type rateLimitedProcessor struct { baseProcessor Processor rateLimiter RateLimiter cancelCtx context.Context cancelFn context.CancelFunc status int32 } func NewRateLimitedProcessor( baseProcessor Processor, rateLimiter RateLimiter, ) Processor { ctx, cancel := context.WithCancel(context.Background()) return &rateLimitedProcessor{ baseProcessor: baseProcessor, rateLimiter: rateLimiter, cancelCtx: ctx, cancelFn: cancel, status: common.DaemonStatusInitialized, } } func (p *rateLimitedProcessor) Start() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } p.baseProcessor.Start() } func (p *rateLimitedProcessor) Stop() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } p.cancelFn() p.baseProcessor.Stop() } func (p *rateLimitedProcessor) Submit(t Task) error { if err := p.rateLimiter.Wait(p.cancelCtx, t); err != nil { return err } return p.baseProcessor.Submit(t) } func (p *rateLimitedProcessor) TrySubmit(t Task) (bool, error) { if ok := p.rateLimiter.Allow(t); !ok { return false, nil } return p.baseProcessor.TrySubmit(t) } ================================================ FILE: service/history/task/rate_limited_processor_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package task import ( "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" gomock "go.uber.org/mock/gomock" ) type rateLimitedProcessorMockDeps struct { mockProcessor *MockProcessor mockRateLimiter *MockRateLimiter } func setupMocksForRateLimitedProcessor(t *testing.T) (*rateLimitedProcessor, *rateLimitedProcessorMockDeps) { ctrl := gomock.NewController(t) deps := &rateLimitedProcessorMockDeps{ mockProcessor: NewMockProcessor(ctrl), mockRateLimiter: NewMockRateLimiter(ctrl), } processor := NewRateLimitedProcessor(deps.mockProcessor, deps.mockRateLimiter) rp, ok := processor.(*rateLimitedProcessor) require.True(t, ok) return rp, deps } func TestRateLimitedProcessorLifecycle(t *testing.T) { rp, deps := setupMocksForRateLimitedProcessor(t) deps.mockProcessor.EXPECT().Start().Times(1) rp.Start() deps.mockProcessor.EXPECT().Stop().Times(1) rp.Stop() } func TestRateLimitedProcessorSubmit(t *testing.T) { testCases := []struct { name string task Task setupMocks func(*rateLimitedProcessorMockDeps) expectError bool expectedError string }{ { name: "success", task: &noopTask{}, setupMocks: func(deps *rateLimitedProcessorMockDeps) { deps.mockRateLimiter.EXPECT().Wait(gomock.Any(), gomock.Any()).Return(nil).Times(1) deps.mockProcessor.EXPECT().Submit(gomock.Any()).Return(nil).Times(1) }, }, { name: "rate limiter error", task: &noopTask{}, setupMocks: func(deps *rateLimitedProcessorMockDeps) { deps.mockRateLimiter.EXPECT().Wait(gomock.Any(), gomock.Any()).Return(errors.New("rate limited")).Times(1) }, expectError: true, expectedError: "rate limited", }, { name: "processor error", task: &noopTask{}, setupMocks: func(deps *rateLimitedProcessorMockDeps) { deps.mockRateLimiter.EXPECT().Wait(gomock.Any(), gomock.Any()).Return(nil).Times(1) deps.mockProcessor.EXPECT().Submit(gomock.Any()).Return(errors.New("processor error")).Times(1) }, expectError: true, expectedError: "processor error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { rp, deps := setupMocksForRateLimitedProcessor(t) tc.setupMocks(deps) err := rp.Submit(tc.task) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestRateLimitedProcessorTrySubmit(t *testing.T) { testCases := []struct { name string task Task setupMocks func(*rateLimitedProcessorMockDeps) expected bool expectError bool expectedError string }{ { name: "success", task: &noopTask{}, setupMocks: func(deps *rateLimitedProcessorMockDeps) { deps.mockRateLimiter.EXPECT().Allow(gomock.Any()).Return(true) deps.mockProcessor.EXPECT().TrySubmit(gomock.Any()).Return(true, nil) }, expected: true, }, { name: "rate limited", task: &noopTask{}, setupMocks: func(deps *rateLimitedProcessorMockDeps) { deps.mockRateLimiter.EXPECT().Allow(gomock.Any()).Return(false) }, expected: false, }, { name: "error", task: &noopTask{}, setupMocks: func(deps *rateLimitedProcessorMockDeps) { deps.mockRateLimiter.EXPECT().Allow(gomock.Any()).Return(true) deps.mockProcessor.EXPECT().TrySubmit(gomock.Any()).Return(false, errors.New("submit error")) }, expected: false, expectError: true, expectedError: "submit error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { rp, deps := setupMocksForRateLimitedProcessor(t) tc.setupMocks(deps) submitted, err := rp.TrySubmit(tc.task) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, submitted) } }) } } ================================================ FILE: service/history/task/redispatcher.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ctask "github.com/uber/cadence/common/task" ) const ( defaultBufferSize = 200 ) type ( redispatchNotification struct { targetSize int doneCh chan struct{} } // RedispatcherOptions configs redispatch interval RedispatcherOptions struct { TaskRedispatchInterval dynamicproperties.DurationPropertyFn } redispatcherImpl struct { sync.Mutex taskProcessor Processor timeSource clock.TimeSource logger log.Logger metricsScope metrics.Scope status int32 shutdownCh chan struct{} shutdownWG sync.WaitGroup redispatchCh chan redispatchNotification timerGate clock.TimerGate backoffPolicy backoff.RetryPolicy pqMap map[int]collection.Queue[redispatchTask] // priority -> redispatch queue taskChFull map[int]bool // priority -> if taskCh is full } redispatchTask struct { task Task redispatchTime time.Time } ) // NewRedispatcher creates a new task Redispatcher func NewRedispatcher( taskProcessor Processor, timeSource clock.TimeSource, options *RedispatcherOptions, logger log.Logger, metricsScope metrics.Scope, ) Redispatcher { backoffPolicy := backoff.NewExponentialRetryPolicy(options.TaskRedispatchInterval()) backoffPolicy.SetBackoffCoefficient(redispatchBackoffCoefficient) backoffPolicy.SetMaximumInterval(redispatchMaxBackoffInternval) backoffPolicy.SetExpirationInterval(backoff.NoInterval) return &redispatcherImpl{ taskProcessor: taskProcessor, timeSource: timeSource, logger: logger, metricsScope: metricsScope, status: common.DaemonStatusInitialized, shutdownCh: make(chan struct{}), redispatchCh: make(chan redispatchNotification, 1), timerGate: clock.NewTimerGate(timeSource), backoffPolicy: backoffPolicy, pqMap: make(map[int]collection.Queue[redispatchTask]), taskChFull: make(map[int]bool), } } func (r *redispatcherImpl) Start() { if !atomic.CompareAndSwapInt32(&r.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } r.shutdownWG.Add(1) go r.redispatchLoop() r.logger.Info("Task redispatcher started.", tag.LifeCycleStarted) } func (r *redispatcherImpl) Stop() { if !atomic.CompareAndSwapInt32(&r.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(r.shutdownCh) r.timerGate.Stop() if success := common.AwaitWaitGroup(&r.shutdownWG, time.Minute); !success { r.logger.Warn("Task redispatcher timedout on shutdown.", tag.LifeCycleStopTimedout) } r.logger.Info("Task redispatcher stopped.", tag.LifeCycleStopped) } func (r *redispatcherImpl) AddTask(task Task) { priority := task.Priority() attempt := task.GetAttempt() r.Lock() pq := r.getOrCreatePQLocked(priority) t := r.getRedispatchTime(attempt) pq.Add(redispatchTask{ task: task, redispatchTime: t, }) r.Unlock() r.timerGate.Update(t) } func (r *redispatcherImpl) RedispatchTask(task Task, t time.Time) { priority := task.Priority() r.Lock() pq := r.getOrCreatePQLocked(priority) pq.Add(redispatchTask{ task: task, redispatchTime: t, }) r.Unlock() r.timerGate.Update(t) } // TODO: review this method, it doesn't seem to redispatch the tasks immediately func (r *redispatcherImpl) Redispatch(targetSize int) { doneCh := make(chan struct{}) ntf := redispatchNotification{ targetSize: targetSize, doneCh: doneCh, } select { case r.redispatchCh <- ntf: // block until the redispatch is done <-doneCh case <-r.shutdownCh: close(doneCh) return } } func (r *redispatcherImpl) Size() int { r.Lock() defer r.Unlock() return r.sizeLocked() } func (r *redispatcherImpl) redispatchLoop() { defer r.shutdownWG.Done() for { select { case <-r.shutdownCh: return case <-r.timerGate.Chan(): r.redispatchTasks(redispatchNotification{}) case notification := <-r.redispatchCh: r.redispatchTasks(notification) } } } func (r *redispatcherImpl) redispatchTasks(notification redispatchNotification) { r.Lock() defer r.Unlock() defer func() { if notification.doneCh != nil { close(notification.doneCh) } }() if r.isStopped() { return } queueSize := r.sizeLocked() r.metricsScope.RecordTimer(metrics.TaskRedispatchQueuePendingTasksTimer, time.Duration(queueSize)) // add some buffer here as new tasks may be added targetRedispatched := queueSize + defaultBufferSize - notification.targetSize if targetRedispatched <= 0 { // target size has already been met, no need to redispatch return } totalRedispatched := 0 now := r.timeSource.Now() for priority := range r.pqMap { r.taskChFull[priority] = false } for priority, pq := range r.pqMap { // Note the third condition regarding taskChFull is not 100% accurate // since task may get a new, lower priority upon redispatch, and // the taskCh for the new priority may not be full. // But the current estimation should be good enough as task with // lower priority should be executed after high priority ones, // so it's ok to leave them in the queue for !pq.IsEmpty() && totalRedispatched < targetRedispatched && !r.taskChFull[priority] { item, _ := pq.Peek() // error is impossible because we've checked that the queue is not empty if item.redispatchTime.After(now) { break } if item.task.State() != ctask.TaskStatePending { pq.Remove() continue } if item.task.GetAttempt() == 0 { item.task.SetInitialSubmitTime(now) } submitted, err := r.taskProcessor.TrySubmit(item.task) if err != nil { if r.isStopped() { // if error is due to shard shutdown break } // otherwise it might be error from domain cache etc, add // the task to redispatch queue so that it can be retried r.logger.Error("Failed to redispatch task", tag.Error(err)) } pq.Remove() newPriority := item.task.Priority() if err != nil || !submitted { // failed to submit, enqueue again item.redispatchTime = r.timeSource.Now().Add(redispatchFailureBackoffInterval) r.getOrCreatePQLocked(newPriority).Add(item) } if err == nil && !submitted { // task chan is full for the new priority r.taskChFull[newPriority] = true } if submitted { totalRedispatched++ } } if !pq.IsEmpty() { item, _ := pq.Peek() r.timerGate.Update(item.redispatchTime) } if r.isStopped() { return } } } func (r *redispatcherImpl) sizeLocked() int { size := 0 for _, queue := range r.pqMap { size += queue.Len() } return size } func (r *redispatcherImpl) isStopped() bool { return atomic.LoadInt32(&r.status) == common.DaemonStatusStopped } func (r *redispatcherImpl) getRedispatchTime(attempt int) time.Time { // note that elapsedTime (the first parameter) is not relevant when // the retry policy has not expiration intervaly(0, attempt))) return r.timeSource.Now().Add(r.backoffPolicy.ComputeNextDelay(0, attempt)) } func (r *redispatcherImpl) getOrCreatePQLocked(priority int) collection.Queue[redispatchTask] { if pq, ok := r.pqMap[priority]; ok { return pq } pq := collection.NewPriorityQueue(redispatchTaskCompareLess) r.pqMap[priority] = pq return pq } func redispatchTaskCompareLess( this redispatchTask, that redispatchTask, ) bool { return this.redispatchTime.Before(that.redispatchTime) } ================================================ FILE: service/history/task/redispatcher_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "errors" "math/rand" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" ctask "github.com/uber/cadence/common/task" ) type ( redispatcherSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockProcessor *MockProcessor mockTimeSource clock.MockedTimeSource options *RedispatcherOptions metricsScope metrics.Scope logger log.Logger redispatcher *redispatcherImpl } ) func TestRedispatcherSuite(t *testing.T) { s := new(redispatcherSuite) suite.Run(t, s) } func (s *redispatcherSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockProcessor = NewMockProcessor(s.controller) s.mockTimeSource = clock.NewMockedTimeSource() s.options = &RedispatcherOptions{ TaskRedispatchInterval: dynamicproperties.GetDurationPropertyFn(time.Millisecond * 50), } s.metricsScope = metrics.NewClient(tally.NoopScope, metrics.History, metrics.HistogramMigration{}).Scope(0) s.logger = testlogger.New(s.T()) s.redispatcher = NewRedispatcher( s.mockProcessor, s.mockTimeSource, s.options, s.logger, s.metricsScope, ).(*redispatcherImpl) } func (s *redispatcherSuite) TearDownTest() { s.redispatcher.Stop() } func (s *redispatcherSuite) TestRedispatch_ProcessorShutDown() { numTasks := 5 successfullyRedispatched := 3 stopDoneCh := make(chan struct{}) s.mockProcessor.EXPECT().TrySubmit(gomock.Any()).Return(true, nil).Times(successfullyRedispatched) s.mockProcessor.EXPECT().TrySubmit(gomock.Any()).DoAndReturn(func(_ interface{}) (bool, error) { go func() { s.redispatcher.Stop() close(stopDoneCh) }() <-s.redispatcher.shutdownCh return false, errors.New("processor shutdown") }).Times(1) for i := 0; i < numTasks; i++ { mockTask := NewMockTask(s.controller) mockTask.EXPECT().Priority().Return(rand.Intn(5)).AnyTimes() mockTask.EXPECT().GetAttempt().Return(0).Times(1) mockTask.EXPECT().GetAttempt().Return(0).MaxTimes(1) mockTask.EXPECT().SetInitialSubmitTime(gomock.Any()).MaxTimes(1) mockTask.EXPECT().State().Return(ctask.TaskStatePending).MaxTimes(1) s.redispatcher.AddTask(mockTask) } s.Equal(numTasks, s.redispatcher.Size()) s.mockTimeSource.Advance(2 * s.options.TaskRedispatchInterval()) s.redispatcher.Start() <-s.redispatcher.shutdownCh <-stopDoneCh s.Equal(numTasks-successfullyRedispatched, s.redispatcher.Size()) } func (s *redispatcherSuite) TestRedispatch_WithTargetSize() { numTasks := defaultBufferSize + 20 targetSize := defaultBufferSize + 10 i := 0 for ; i < numTasks+defaultBufferSize-targetSize; i++ { mockTask := NewMockTask(s.controller) mockTask.EXPECT().Priority().Return(rand.Intn(5)).AnyTimes() mockTask.EXPECT().GetAttempt().Return(0).Times(2) mockTask.EXPECT().SetInitialSubmitTime(gomock.Any()).Times(1) mockTask.EXPECT().State().Return(ctask.TaskStatePending).MaxTimes(1) s.redispatcher.AddTask(mockTask) } for ; i < numTasks; i++ { mockTask := NewMockTask(s.controller) mockTask.EXPECT().Priority().Return(rand.Intn(5)).AnyTimes() mockTask.EXPECT().GetAttempt().Return(1000).Times(1) // make sure these tasks are not dispatched mockTask.EXPECT().State().Return(ctask.TaskStatePending).MaxTimes(1) s.redispatcher.AddTask(mockTask) } s.redispatcher.Start() s.redispatcher.timerGate.Stop() s.mockProcessor.EXPECT().TrySubmit(gomock.Any()).Return(true, nil).Times(numTasks + defaultBufferSize - targetSize) s.mockTimeSource.Advance(2 * s.options.TaskRedispatchInterval()) // the time should be enough to let tasks with attempt 0 be dispatched s.redispatcher.Redispatch(targetSize) // implementation can choose to redispatch more tasks than needed sz := s.redispatcher.Size() s.Equal(targetSize-defaultBufferSize, sz) } func (s *redispatcherSuite) TestRedispatch_Backoff() { numTasks := 50 numLowAttemptTasks := 0 numHighAttemptTasks := 0 for i := 0; i < numTasks; i++ { attempt := 100 if rand.Intn(2) == 0 { numLowAttemptTasks++ attempt = 0 } else { numHighAttemptTasks++ } mockTask := NewMockTask(s.controller) mockTask.EXPECT().Priority().Return(rand.Intn(5)).AnyTimes() if attempt == 0 { mockTask.EXPECT().GetAttempt().Return(attempt).Times(2) mockTask.EXPECT().SetInitialSubmitTime(gomock.Any()).Times(1) } else { mockTask.EXPECT().GetAttempt().Return(attempt).Times(1) } mockTask.EXPECT().State().Return(ctask.TaskStatePending).MaxTimes(1) s.redispatcher.AddTask(mockTask) s.mockProcessor.EXPECT().TrySubmit(NewMockTaskMatcher(mockTask)).Return(true, nil).MaxTimes(1) } s.redispatcher.Start() s.redispatcher.timerGate.Stop() s.mockTimeSource.Advance(2 * s.options.TaskRedispatchInterval()) s.redispatcher.Redispatch(0) s.Equal(numHighAttemptTasks, s.redispatcher.Size()) } func (s *redispatcherSuite) TestRedispatch_Random() { numTasks := 100 dispatched := 0 for i := 0; i != numTasks; i++ { submitted := false attempt := 100 if rand.Intn(2) == 0 { submitted = true if rand.Intn(2) == 0 { dispatched++ attempt = 0 } } mockTask := NewMockTask(s.controller) mockTask.EXPECT().Priority().Return(rand.Intn(5)).AnyTimes() if attempt == 0 { mockTask.EXPECT().GetAttempt().Return(attempt).Times(2) mockTask.EXPECT().SetInitialSubmitTime(gomock.Any()).Times(1) } else { mockTask.EXPECT().GetAttempt().Return(attempt).Times(1) } mockTask.EXPECT().State().Return(ctask.TaskStatePending).MaxTimes(1) s.redispatcher.AddTask(mockTask) s.mockProcessor.EXPECT().TrySubmit(NewMockTaskMatcher(mockTask)).Return(submitted, nil).MaxTimes(1) } s.redispatcher.Start() s.redispatcher.timerGate.Stop() s.mockTimeSource.Advance(2 * s.options.TaskRedispatchInterval()) s.redispatcher.Redispatch(0) // implementation can choose to stop redispatch for a certain priority when previous submit has failed s.True(s.redispatcher.Size() >= numTasks-dispatched) } ================================================ FILE: service/history/task/rescheduler.go ================================================ package task import ( "context" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ctask "github.com/uber/cadence/common/task" ) const ( taskChanFullBackoff = 2 * time.Second taskChanFullBackoffJitterCoefficient = 0.5 reschedulerPQCleanupDuration = 3 * time.Minute reschedulerPQCleanupJitterCoefficient = 0.15 ) type ( rescheduledTask struct { task Task rescheduleTime time.Time } reschedulerImpl struct { scheduler Processor timeSource clock.TimeSource logger log.Logger metricsScope metrics.Scope status int32 ctx context.Context cancel context.CancelFunc shutdownWG sync.WaitGroup timerGate clock.TimerGate sync.Mutex pqMap map[DomainPriorityKey]collection.Queue[rescheduledTask] numExecutables int } ) func NewRescheduler( scheduler Processor, timeSource clock.TimeSource, logger log.Logger, metricsScope metrics.Scope, ) Rescheduler { ctx, cancel := context.WithCancel(context.Background()) return &reschedulerImpl{ scheduler: scheduler, timeSource: timeSource, logger: logger, metricsScope: metricsScope, status: common.DaemonStatusInitialized, ctx: ctx, cancel: cancel, timerGate: clock.NewTimerGate(timeSource), pqMap: make(map[DomainPriorityKey]collection.Queue[rescheduledTask]), } } func (r *reschedulerImpl) Start() { if !atomic.CompareAndSwapInt32(&r.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } r.shutdownWG.Add(1) go r.rescheduleLoop() r.logger.Info("Task rescheduler started.", tag.LifeCycleStarted) } func (r *reschedulerImpl) Stop() { if !atomic.CompareAndSwapInt32(&r.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } r.cancel() r.timerGate.Stop() if success := common.AwaitWaitGroup(&r.shutdownWG, time.Minute); !success { r.logger.Warn("Task rescheduler timedout on shutdown.", tag.LifeCycleStopTimedout) } r.logger.Info("Task rescheduler stopped.", tag.LifeCycleStopped) } func (r *reschedulerImpl) RescheduleTask( task Task, rescheduleTime time.Time, ) { r.Lock() pq := r.getOrCreatePQLocked(DomainPriorityKey{ DomainID: task.GetDomainID(), Priority: task.Priority(), }) pq.Add(rescheduledTask{ task: task, rescheduleTime: rescheduleTime, }) r.numExecutables++ r.Unlock() r.timerGate.Update(rescheduleTime) if r.isStopped() { r.drain() } } func (r *reschedulerImpl) RescheduleDomains( domainIDs map[string]struct{}, ) { r.Lock() defer r.Unlock() now := r.timeSource.Now() updatedRescheduleTime := false for key, pq := range r.pqMap { if _, ok := domainIDs[key.DomainID]; !ok { continue } updatedRescheduleTime = true // set reschedule time for all tasks in this pq to be now items := make([]rescheduledTask, 0, pq.Len()) for !pq.IsEmpty() { rescheduled, _ := pq.Remove() // scheduled queue pre-fetches tasks, // so we need to make sure the reschedule time is not before the task scheduled time scheduleTime := rescheduled.task.GetTaskKey().GetScheduledTime() if now.Before(scheduleTime) { rescheduled.rescheduleTime = scheduleTime } else { rescheduled.rescheduleTime = now } items = append(items, rescheduled) } r.pqMap[key] = r.newPriorityQueue(items) } // then update timer gate to trigger the actual reschedule if updatedRescheduleTime { r.timerGate.Update(now) } } func (r *reschedulerImpl) Size() int { r.Lock() defer r.Unlock() return r.numExecutables } func (r *reschedulerImpl) rescheduleLoop() { defer r.shutdownWG.Done() cleanupTimer := r.timeSource.NewTimer(backoff.JitDuration( reschedulerPQCleanupDuration, reschedulerPQCleanupJitterCoefficient, )) defer cleanupTimer.Stop() for { select { case <-r.ctx.Done(): r.drain() return case <-r.timerGate.Chan(): r.reschedule() case <-cleanupTimer.Chan(): r.cleanupPQ() cleanupTimer.Reset(backoff.JitDuration( reschedulerPQCleanupDuration, reschedulerPQCleanupJitterCoefficient, )) } } } func (r *reschedulerImpl) reschedule() { r.Lock() defer r.Unlock() r.metricsScope.UpdateGauge(metrics.ReschedulerTaskCountGauge, float64(r.numExecutables)) now := r.timeSource.Now() for _, pq := range r.pqMap { for !pq.IsEmpty() { rescheduled, _ := pq.Peek() if rescheduleTime := rescheduled.rescheduleTime; now.Before(rescheduleTime) { r.timerGate.Update(rescheduleTime) break } task := rescheduled.task if task.State() != ctask.TaskStatePending { pq.Remove() r.numExecutables-- continue } if task.GetAttempt() == 0 { task.SetInitialSubmitTime(now) } submitted, err := r.scheduler.TrySubmit(task) if err != nil { if r.isStopped() { // if error is due to shard shutdown break } // otherwise it might be error from domain cache etc, add // the task to reschedule queue so that it can be retried r.logger.Error("Failed to reschedule task", tag.Error(err)) } if !submitted { r.timerGate.Update(now.Add(backoff.JitDuration(taskChanFullBackoff, taskChanFullBackoffJitterCoefficient))) break } pq.Remove() r.numExecutables-- } } } func (r *reschedulerImpl) cleanupPQ() { r.Lock() defer r.Unlock() for key, pq := range r.pqMap { if pq.IsEmpty() { delete(r.pqMap, key) } } } func (r *reschedulerImpl) drain() { r.Lock() defer r.Unlock() for key, pq := range r.pqMap { for !pq.IsEmpty() { pq.Remove() } delete(r.pqMap, key) } r.numExecutables = 0 } func (r *reschedulerImpl) isStopped() bool { return atomic.LoadInt32(&r.status) == common.DaemonStatusStopped } func (r *reschedulerImpl) getOrCreatePQLocked( key DomainPriorityKey, ) collection.Queue[rescheduledTask] { if pq, ok := r.pqMap[key]; ok { return pq } pq := r.newPriorityQueue(nil) r.pqMap[key] = pq return pq } func (r *reschedulerImpl) newPriorityQueue( items []rescheduledTask, ) collection.Queue[rescheduledTask] { return collection.NewPriorityQueue(rescheduledTaskCompareLess, items...) } func rescheduledTaskCompareLess( this rescheduledTask, that rescheduledTask, ) bool { return this.rescheduleTime.Before(that.rescheduleTime) } ================================================ FILE: service/history/task/rescheduler_test.go ================================================ package task import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ctask "github.com/uber/cadence/common/task" ) func newNoopTransferTaskFromDomainID(domainID string) *noopTask { return &noopTask{ Task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: domainID, }, }, state: ctask.TaskStatePending, } } func newNoopTimerTaskFromDomainID(domainID string, scheduledTime time.Time) *noopTask { return &noopTask{ Task: &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: domainID, }, TaskData: persistence.TaskData{ VisibilityTimestamp: scheduledTime, }, }, state: ctask.TaskStatePending, } } func TestReschedulerLifeCycle(t *testing.T) { ctrl := gomock.NewController(t) mockScheduler := NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() logger := testlogger.New(t) scope := metrics.NoopScope now := mockTimeSource.Now() rescheduler := NewRescheduler(mockScheduler, mockTimeSource, logger, scope) rescheduler.Start() for i := 0; i < 10; i++ { var task Task if i%2 == 0 { task = newNoopTransferTaskFromDomainID("domainID") } else { task = newNoopTimerTaskFromDomainID("domainID", now) } rescheduler.RescheduleTask(task, now.Add(time.Second*time.Duration(i+1))) assert.Equal(t, rescheduler.Size(), i+1) } taskCh := make(chan Task, 10) mockScheduler.EXPECT().TrySubmit(gomock.Any()).DoAndReturn(func(task Task) (bool, error) { taskCh <- task return true, nil }).Times(1) mockTimeSource.Advance(time.Second) <-taskCh mockScheduler.EXPECT().TrySubmit(gomock.Any()).DoAndReturn(func(task Task) (bool, error) { taskCh <- task return true, nil }).Times(4) mockTimeSource.Advance(time.Second * 4) for i := 0; i < 4; i++ { <-taskCh } assert.Equal(t, 5, rescheduler.Size()) rescheduler.Stop() assert.Equal(t, 0, rescheduler.Size()) } func TestReschedulerRescheduleDomains(t *testing.T) { ctrl := gomock.NewController(t) mockScheduler := NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() logger := testlogger.New(t) scope := metrics.NoopScope now := mockTimeSource.Now() rescheduler := NewRescheduler(mockScheduler, mockTimeSource, logger, scope) rescheduler.Start() for i := 0; i < 10; i++ { var task Task task = newNoopTransferTaskFromDomainID("X") rescheduler.RescheduleTask(task, now.Add(time.Second*time.Duration(i+1))) assert.Equal(t, rescheduler.Size(), i+1) } for i := 0; i < 10; i++ { var task Task task = newNoopTransferTaskFromDomainID("Y") rescheduler.RescheduleTask(task, now.Add(time.Second*100)) assert.Equal(t, rescheduler.Size(), i+11) } for i := 0; i < 10; i++ { var scheduledTime time.Time if i%2 == 0 { scheduledTime = now.Add(time.Second * 1000) } else { scheduledTime = now } task := newNoopTimerTaskFromDomainID("Z", scheduledTime) rescheduler.RescheduleTask(task, now.Add(time.Second*1000)) assert.Equal(t, rescheduler.Size(), i+21) } taskCh := make(chan Task, 30) mockScheduler.EXPECT().TrySubmit(gomock.Any()).DoAndReturn(func(task Task) (bool, error) { taskCh <- task return true, nil }).Times(10) // advance time to reschedule tasks from domain X mockTimeSource.Advance(time.Second * 10) for i := 0; i < 10; i++ { task := <-taskCh assert.Equal(t, "X", task.GetDomainID()) } assert.Equal(t, 20, rescheduler.Size()) mockScheduler.EXPECT().TrySubmit(gomock.Any()).DoAndReturn(func(task Task) (bool, error) { taskCh <- task return true, nil }).Times(10) // reschedule tasks from domain Y immediately even though their reschedule time is 100s in the future rescheduler.RescheduleDomains(map[string]struct{}{"Y": {}}) for i := 0; i < 10; i++ { task := <-taskCh assert.Equal(t, "Y", task.GetDomainID()) } assert.Equal(t, 10, rescheduler.Size()) mockScheduler.EXPECT().TrySubmit(gomock.Any()).DoAndReturn(func(task Task) (bool, error) { taskCh <- task return true, nil }).Times(5) // reschedule tasks from domain Z immediately and verify that tasks with scheduled time in the future are not rescheduled rescheduler.RescheduleDomains(map[string]struct{}{"Z": {}}) for i := 0; i < 5; i++ { task := <-taskCh assert.Equal(t, "Z", task.GetDomainID()) assert.False(t, now.After(task.GetTaskKey().GetScheduledTime())) } assert.Equal(t, 5, rescheduler.Size()) rescheduler.Stop() assert.Equal(t, 0, rescheduler.Size()) } func TestReschedulerIgnoreTaskWithStateNotPending(t *testing.T) { ctrl := gomock.NewController(t) mockScheduler := NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() logger := testlogger.New(t) scope := metrics.NoopScope now := mockTimeSource.Now() rescheduler := NewRescheduler(mockScheduler, mockTimeSource, logger, scope) rescheduler.Start() for i := 0; i < 10; i++ { var task *noopTask if i%2 == 0 { task = newNoopTransferTaskFromDomainID("domainID") } else { task = newNoopTransferTaskFromDomainID("domainID") task.state = ctask.TaskStateCanceled } rescheduler.RescheduleTask(task, now.Add(time.Second)) assert.Equal(t, rescheduler.Size(), i+1) } taskCh := make(chan Task, 5) mockScheduler.EXPECT().TrySubmit(gomock.Any()).DoAndReturn(func(task Task) (bool, error) { taskCh <- task return true, nil }).Times(5) mockTimeSource.Advance(time.Second) for i := 0; i < 5; i++ { task := <-taskCh assert.Equal(t, ctask.TaskStatePending, task.State()) } assert.Equal(t, 0, rescheduler.Size()) rescheduler.Stop() } func TestReschedulerProcessorThrottled(t *testing.T) { ctrl := gomock.NewController(t) mockScheduler := NewMockProcessor(ctrl) mockTimeSource := clock.NewMockedTimeSource() logger := testlogger.New(t) scope := metrics.NoopScope now := mockTimeSource.Now() rescheduler := NewRescheduler(mockScheduler, mockTimeSource, logger, scope) rescheduler.Start() for i := 0; i < 10; i++ { task := newNoopTransferTaskFromDomainID("domainID") rescheduler.RescheduleTask(task, now.Add(time.Second)) assert.Equal(t, rescheduler.Size(), i+1) } taskCh := make(chan Task, 10) mockScheduler.EXPECT().TrySubmit(gomock.Any()).DoAndReturn(func(task Task) (bool, error) { taskCh <- task return true, nil }).Times(4) mockScheduler.EXPECT().TrySubmit(gomock.Any()).Return(false, nil).Times(1) mockTimeSource.Advance(time.Second) for i := 0; i < 4; i++ { <-taskCh } assert.Equal(t, 6, rescheduler.Size()) rescheduler.Stop() assert.Equal(t, 0, rescheduler.Size()) } ================================================ FILE: service/history/task/standby_task_util.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "errors" "fmt" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" ) type ( standbyActionFn func(context.Context, execution.Context, execution.MutableState) (interface{}, error) standbyPostActionFn func(context.Context, persistence.Task, interface{}, log.Logger) error standbyCurrentTimeFn func(persistence.Task) (time.Time, error) ) var ( errDomainBecomesActive = errors.New("domain becomes active when processing task as standby") ) func standbyTaskPostActionNoOp( ctx context.Context, taskInfo persistence.Task, postActionInfo interface{}, logger log.Logger, ) error { if postActionInfo == nil { return nil } // return error so task processing logic will retry logger.Debug("standbyTaskPostActionNoOp return redispatch error so task processing logic will retry", tag.WorkflowID(taskInfo.GetWorkflowID()), tag.WorkflowRunID(taskInfo.GetRunID()), tag.WorkflowDomainID(taskInfo.GetDomainID()), tag.TaskID(taskInfo.GetTaskID()), tag.TaskType(taskInfo.GetTaskType()), tag.FailoverVersion(taskInfo.GetVersion()), tag.Timestamp(taskInfo.GetVisibilityTimestamp())) return &redispatchError{Reason: fmt.Sprintf("post action is %T", postActionInfo)} } func standbyTaskPostActionTaskDiscarded( ctx context.Context, task persistence.Task, postActionInfo interface{}, logger log.Logger, ) error { if postActionInfo == nil { return nil } logger.Error("Discarding standby task due to task being pending for too long.", tag.WorkflowID(task.GetWorkflowID()), tag.WorkflowRunID(task.GetRunID()), tag.WorkflowDomainID(task.GetDomainID()), tag.TaskID(task.GetTaskID()), tag.TaskType(task.GetTaskType()), tag.FailoverVersion(task.GetVersion()), tag.Timestamp(task.GetVisibilityTimestamp())) return ErrTaskDiscarded } type ( historyResendInfo struct { // used by NDC lastEventID *int64 lastEventVersion *int64 } pushActivityToMatchingInfo struct { activityScheduleToStartTimeout int32 tasklist types.TaskList partitionConfig map[string]string } pushDecisionToMatchingInfo struct { decisionScheduleToStartTimeout int32 tasklist types.TaskList partitionConfig map[string]string } ) func newPushActivityToMatchingInfo( activityScheduleToStartTimeout int32, tasklist types.TaskList, partitionConfig map[string]string, ) *pushActivityToMatchingInfo { return &pushActivityToMatchingInfo{ activityScheduleToStartTimeout: activityScheduleToStartTimeout, tasklist: tasklist, partitionConfig: partitionConfig, } } func newPushDecisionToMatchingInfo( decisionScheduleToStartTimeout int32, tasklist types.TaskList, partitionConfig map[string]string, ) *pushDecisionToMatchingInfo { return &pushDecisionToMatchingInfo{ decisionScheduleToStartTimeout: decisionScheduleToStartTimeout, tasklist: tasklist, partitionConfig: partitionConfig, } } func getHistoryResendInfo( mutableState execution.MutableState, ) (*historyResendInfo, error) { versionHistories := mutableState.GetVersionHistories() if versionHistories == nil { return nil, execution.ErrMissingVersionHistories } currentBranch, err := versionHistories.GetCurrentVersionHistory() if err != nil { return nil, err } lastItem, err := currentBranch.GetLastItem() if err != nil { return nil, err } return &historyResendInfo{ lastEventID: common.Int64Ptr(lastItem.EventID), lastEventVersion: common.Int64Ptr(lastItem.Version), }, nil } func getStandbyPostActionFn( logger log.Logger, taskInfo persistence.Task, standbyNow standbyCurrentTimeFn, standbyTaskMissingEventsResendDelay time.Duration, standbyTaskMissingEventsDiscardDelay time.Duration, fetchHistoryStandbyPostActionFn standbyPostActionFn, discardTaskStandbyPostActionFn standbyPostActionFn, ) standbyPostActionFn { taskTime := taskInfo.GetVisibilityTimestamp() resendTime := taskTime.Add(standbyTaskMissingEventsResendDelay) discardTime := taskTime.Add(standbyTaskMissingEventsDiscardDelay) tags := []tag.Tag{ tag.WorkflowID(taskInfo.GetWorkflowID()), tag.WorkflowRunID(taskInfo.GetRunID()), tag.WorkflowDomainID(taskInfo.GetDomainID()), tag.TaskID(taskInfo.GetTaskID()), tag.TaskType(int(taskInfo.GetTaskType())), tag.Timestamp(taskInfo.GetVisibilityTimestamp()), } now, err := standbyNow(taskInfo) if err != nil { tags = append(tags, tag.Error(err)) logger.Error("getStandbyPostActionFn error getting current time, fallback to standbyTaskPostActionNoOp", tags...) return standbyTaskPostActionNoOp } // now < task start time + StandbyTaskMissingEventsResendDelay if now.Before(resendTime) { logger.Debug("getStandbyPostActionFn returning standbyTaskPostActionNoOp because now < task start time + StandbyTaskMissingEventsResendDelay", tags...) return standbyTaskPostActionNoOp } // task start time + StandbyTaskMissingEventsResendDelay <= now < task start time + StandbyTaskMissingEventsResendDelay if now.Before(discardTime) { logger.Debug("getStandbyPostActionFn returning fetchHistoryStandbyPostActionFn because task start time + StandbyTaskMissingEventsResendDelay <= now < task start time + StandbyTaskMissingEventsResendDelay", tags...) return fetchHistoryStandbyPostActionFn } // task start time + StandbyTaskMissingEventsResendDelay <= now logger.Debug("getStandbyPostActionFn returning discardTaskStandbyPostActionFn because task start time + StandbyTaskMissingEventsResendDelay <= now", tags...) return discardTaskStandbyPostActionFn } func getRemoteClusterName( ctx context.Context, currentCluster string, activeClusterMgr activecluster.Manager, taskInfo persistence.Task, ) (string, error) { activeClusterInfo, err := activeClusterMgr.GetActiveClusterInfoByWorkflow(ctx, taskInfo.GetDomainID(), taskInfo.GetWorkflowID(), taskInfo.GetRunID()) if err != nil { return "", err } if activeClusterInfo.ActiveClusterName == currentCluster { return "", errDomainBecomesActive } return activeClusterInfo.ActiveClusterName, nil } ================================================ FILE: service/history/task/standby_task_util_test.go ================================================ package task import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestGetRemoteClusterName(t *testing.T) { testDomainID := "test-domain-id" testWorkflowID := "test-workflow-id" testRunID := "test-run-id" currentCluster := "cluster-A" remoteCluster := "cluster-B" tests := []struct { name string setupMocks func(*gomock.Controller) activecluster.Manager taskInfo persistence.Task expectedResult string expectedError error }{ { name: "active-active domain with lookup error", setupMocks: func(ctrl *gomock.Controller) activecluster.Manager { mockActiveClusterMgr := activecluster.NewMockManager(ctrl) mockActiveClusterMgr.EXPECT(). GetActiveClusterInfoByWorkflow(gomock.Any(), testDomainID, testWorkflowID, testRunID). Return(nil, errors.New("lookup error")) return mockActiveClusterMgr }, taskInfo: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }, }, expectedResult: "", expectedError: errors.New("lookup error"), }, { name: "active-active domain becomes active", setupMocks: func(ctrl *gomock.Controller) activecluster.Manager { mockActiveClusterMgr := activecluster.NewMockManager(ctrl) mockActiveClusterMgr.EXPECT(). GetActiveClusterInfoByWorkflow(gomock.Any(), testDomainID, testWorkflowID, testRunID). Return(&types.ActiveClusterInfo{ ActiveClusterName: currentCluster, }, nil) return mockActiveClusterMgr }, taskInfo: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }, }, expectedResult: "", expectedError: errors.New("domain becomes active when processing task as standby"), }, { name: "active-active domain successful lookup", setupMocks: func(ctrl *gomock.Controller) activecluster.Manager { mockActiveClusterMgr := activecluster.NewMockManager(ctrl) mockActiveClusterMgr.EXPECT(). GetActiveClusterInfoByWorkflow(gomock.Any(), testDomainID, testWorkflowID, testRunID). Return(&types.ActiveClusterInfo{ ActiveClusterName: remoteCluster, }, nil) return mockActiveClusterMgr }, taskInfo: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: testDomainID, WorkflowID: testWorkflowID, RunID: testRunID, }, }, expectedResult: remoteCluster, expectedError: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockActiveClusterMgr := tt.setupMocks(ctrl) result, err := getRemoteClusterName( context.Background(), currentCluster, mockActiveClusterMgr, tt.taskInfo, ) if tt.expectedError != nil { assert.ErrorContains(t, err, tt.expectedError.Error()) } else { assert.NoError(t, err) } assert.Equal(t, tt.expectedResult, result) }) } } ================================================ FILE: service/history/task/task.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "errors" "fmt" "sync" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ctask "github.com/uber/cadence/common/task" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) // redispatchError is the error indicating that the timer / transfer task should be redispatched and retried. type redispatchError struct { Reason string } // Error explains why this task should be redispatched func (r *redispatchError) Error() string { return fmt.Sprintf("Redispatch reason: %q", r.Reason) } func isRedispatchErr(err error) bool { var redispatchErr *redispatchError return errors.As(err, &redispatchErr) } const ( activeTaskRedispatchInitialInterval = 5 * time.Second standbyTaskRedispatchInitialInterval = 30 * time.Second redispatchBackoffCoefficient = 1.05 redispatchMaxBackoffInternval = 2 * time.Minute redispatchFailureBackoffInterval = 2 * time.Second ) var ( // ErrTaskDiscarded is the error indicating that the timer / transfer task is pending for too long and discarded. ErrTaskDiscarded = errors.New("passive task pending for too long") // ErrTaskPendingActive is the error indicating that the task should be re-dispatched ErrTaskPendingActive = errors.New("redispatch the task while the domain is pending-active") activeTaskRedispatchPolicy = createTaskRedispatchPolicy(activeTaskRedispatchInitialInterval) standbyTaskRedispatchPolicy = createTaskRedispatchPolicy(standbyTaskRedispatchInitialInterval) ) type ( taskImpl struct { sync.Mutex persistence.Task shard shard.Context state ctask.State priority int attempt int timeSource clock.TimeSource initialSubmitTime time.Time logger log.Logger eventLogger eventLogger scope metrics.Scope // initialized when processing task to make the initialization parallel taskExecutor Executor taskProcessor Processor redispatcher Redispatcher rescheduler Rescheduler criticalRetryCount dynamicproperties.IntPropertyFn isPreviousExecutorActive bool // TODO: following three fields should be removed after new task lifecycle is implemented taskFilter Filter queueType QueueType shouldProcessTask bool } ) func NewHistoryTask( shard shard.Context, taskInfo persistence.Task, queueType QueueType, logger log.Logger, taskFilter Filter, taskExecutor Executor, taskProcessor Processor, redispatcher Redispatcher, criticalRetryCount dynamicproperties.IntPropertyFn, ) Task { timeSource := shard.GetTimeSource() var eventLogger eventLogger if shard.GetConfig().EnableDebugMode && (queueType == QueueTypeActiveTimer || queueType == QueueTypeActiveTransfer) && shard.GetConfig().EnableTaskInfoLogByDomainID(taskInfo.GetDomainID()) { eventLogger = newEventLogger(logger, timeSource, defaultTaskEventLoggerSize) eventLogger.AddEvent("Created task") } return &taskImpl{ Task: taskInfo, shard: shard, state: ctask.TaskStatePending, priority: noPriority, queueType: queueType, scope: metrics.NoopScope, logger: logger, eventLogger: eventLogger, attempt: 0, initialSubmitTime: timeSource.Now(), timeSource: timeSource, criticalRetryCount: criticalRetryCount, redispatcher: redispatcher, taskFilter: taskFilter, taskExecutor: taskExecutor, taskProcessor: taskProcessor, } } func NewHistoryTaskV2( shard shard.Context, taskInfo persistence.Task, queueType QueueType, logger log.Logger, taskExecutor Executor, taskProcessor Processor, rescheduler Rescheduler, criticalRetryCount dynamicproperties.IntPropertyFn, ) Task { timeSource := shard.GetTimeSource() var eventLogger eventLogger if shard.GetConfig().EnableDebugMode && (queueType == QueueTypeActiveTimer || queueType == QueueTypeActiveTransfer) && shard.GetConfig().EnableTaskInfoLogByDomainID(taskInfo.GetDomainID()) { eventLogger = newEventLogger(logger, timeSource, defaultTaskEventLoggerSize) eventLogger.AddEvent("Created task") } return &taskImpl{ Task: taskInfo, shard: shard, state: ctask.TaskStatePending, priority: noPriority, queueType: queueType, scope: metrics.NoopScope, logger: logger, eventLogger: eventLogger, attempt: 0, initialSubmitTime: timeSource.Now(), timeSource: timeSource, criticalRetryCount: criticalRetryCount, rescheduler: rescheduler, taskFilter: func(task persistence.Task) (bool, error) { return true, nil }, taskExecutor: taskExecutor, taskProcessor: taskProcessor, } } func (t *taskImpl) Execute() error { if t.State() != ctask.TaskStatePending { return nil } scheduleLatency := t.timeSource.Now().Sub(t.initialSubmitTime) var err error t.shouldProcessTask, err = t.taskFilter(t.Task) if err != nil { logEvent(t.eventLogger, "TaskFilter execution failed", err) time.Sleep(loadDomainEntryForTaskRetryDelay) return err } logEvent(t.eventLogger, "Executing task", t.shouldProcessTask) if !t.shouldProcessTask { return nil } executionStartTime := t.timeSource.Now() taskListTaggedScope := metrics.NoopScope defer func() { t.scope.IncCounter(metrics.TaskRequestsPerDomain) processingLatency := time.Since(executionStartTime) t.scope.RecordTimer(metrics.TaskProcessingLatencyPerDomain, processingLatency) t.scope.ExponentialHistogram(metrics.ExponentialTaskProcessingLatencyPerDomain, processingLatency) taskListTaggedScope.IncCounter(metrics.TaskRequestsPerTaskList) taskListTaggedScope.ExponentialHistogram(metrics.ExponentialTaskProcessingLatencyPerTaskList, processingLatency) }() executeResponse, err := t.taskExecutor.Execute(t) t.scope = executeResponse.Scope taskListTaggedScope = t.scope.Tagged(common.GetTaskListTag(t.GetOriginalTaskList(), t.GetOriginalTaskListKind())) if t.GetAttempt() == 0 { taskListTaggedScope.ExponentialHistogram(metrics.ExponentialTaskScheduleLatencyPerTaskList, scheduleLatency) // TODO: replace with ExponentialHistogram // domain level metrics for the duration between task being submitted to task scheduler and being executed t.scope.RecordHistogramDuration(metrics.TaskScheduleLatencyPerDomain, scheduleLatency) } if t.isPreviousExecutorActive != executeResponse.IsActiveTask { t.resetAttempt() } t.isPreviousExecutorActive = executeResponse.IsActiveTask return err } func (t *taskImpl) resetAttempt() { t.Lock() defer t.Unlock() t.attempt = 0 } func (t *taskImpl) HandleErr(err error) (retErr error) { logger := t.logger.Helper() defer func() { if retErr != nil { logEvent(t.eventLogger, "Failed to handle error", retErr) t.Lock() defer t.Unlock() t.attempt++ if t.attempt > t.criticalRetryCount() { t.scope.RecordTimer(metrics.TaskAttemptTimerPerDomain, time.Duration(t.attempt)) t.scope.IntExponentialHistogram(metrics.ExponentialTaskAttemptCountsPerDomain, t.attempt) logger.Error("Critical error processing task, retrying.", tag.Error(err), tag.OperationCritical, tag.TaskType(t.GetTaskType()), tag.AttemptCount(t.attempt), ) } } }() if err == nil { return nil } logEvent(t.eventLogger, "Handling task processing error", err) if _, ok := err.(*types.EntityNotExistsError); ok { return nil } else if _, ok := err.(*types.WorkflowExecutionAlreadyCompletedError); ok { return nil } if _, ok := t.Task.(*persistence.CloseExecutionTask); ok && err == execution.ErrMissingWorkflowStartEvent && t.shard.GetConfig().EnableDropStuckTaskByDomainID(t.GetDomainID()) { // use domainID here to avoid accessing domainCache t.scope.IncCounter(metrics.TransferTaskMissingEventCounterPerDomain) logger.Error("Drop close execution transfer task due to corrupted workflow history", tag.Error(err), tag.LifeCycleProcessingFailed) return nil } if err == errWorkflowBusy { t.scope.IncCounter(metrics.TaskWorkflowBusyPerDomain) return err } if err == errWorkflowRateLimited { // metrics are emitted within the rate limiter return err } // If the shard were recently closed we just return an error, so we retry in a bit. var errShardClosed *shard.ErrShardClosed if errors.As(err, &errShardClosed) && time.Since(errShardClosed.ClosedAt) < shard.TimeBeforeShardClosedIsError { return err } // If the task list is not owned by the host we connected to we just return an error, so we retry in a bit // with the new membership information. var taskListNotOwnedByHostError *cadence_errors.TaskListNotOwnedByHostError if errors.As(err, &taskListNotOwnedByHostError) { t.scope.IncCounter(metrics.TaskListNotOwnedByHostCounterPerDomain) return err } // this is a transient error if isRedispatchErr(err) { t.scope.IncCounter(metrics.TaskStandbyRetryCounterPerDomain) return err } // this is a transient error during graceful failover if err == ErrTaskPendingActive { t.scope.IncCounter(metrics.TaskPendingActiveCounterPerDomain) return err } if err == ErrTaskDiscarded { t.scope.IncCounter(metrics.TaskDiscardedPerDomain) err = nil } if err == execution.ErrMissingVersionHistories { t.logger.Error("Encounter 2DC workflow during task processing.") t.scope.IncCounter(metrics.TaskUnsupportedPerDomain) err = nil } // using a fairly long timeout here because domain updates is an async process // which could take a fair while to be processed by the domain queue, the DB updated // the domain cache refeshed and then updated here. var e *types.DomainNotActiveError if errors.As(err, &e) || errors.Is(err, types.DomainNotActiveError{}) { if t.timeSource.Now().Sub(t.initialSubmitTime) > 5*cache.DomainCacheRefreshInterval { t.scope.IncCounter(metrics.TaskNotActiveCounterPerDomain) // If the domain is *still* not active, drop after a while. return nil } return err } t.scope.IncCounter(metrics.TaskFailuresPerDomain) if _, ok := err.(*persistence.CurrentWorkflowConditionFailedError); ok { logger.Error("More than 2 workflow are running.", tag.Error(err), tag.LifeCycleProcessingFailed) return nil } attempt := t.GetAttempt() if attempt > stickyTaskMaxRetryCount && common.IsStickyTaskConditionError(err) { // sticky task could end up into endless loop in rare cases and // cause worker to keep getting decision timeout unless restart. // return nil here to break the endless loop return nil } logger.Error("Fail to process task", tag.Error(err), tag.LifeCycleProcessingFailed, tag.AttemptCount(attempt)) return err } func (t *taskImpl) RetryErr(err error) bool { if t.State() != ctask.TaskStatePending { return false } var errShardClosed *shard.ErrShardClosed if errors.As(err, &errShardClosed) || err == errWorkflowBusy || isRedispatchErr(err) || err == ErrTaskPendingActive || common.IsContextTimeoutError(err) { return false } return true } func (t *taskImpl) Ack() { logEvent(t.eventLogger, "Acked task") t.Lock() defer t.Unlock() if t.state != ctask.TaskStatePending { return } t.state = ctask.TaskStateAcked if t.shouldProcessTask { // Record attempt count as duration so timer mean ≈ average attempt count. t.scope.RecordTimer(metrics.TaskAttemptTimerPerDomain, time.Duration(t.attempt)) latency := time.Since(t.initialSubmitTime) queueLatency := time.Since(t.GetVisibilityTimestamp()) // Use IntExponentialHistogram with Mid1To16k buckets (1–64k) for attempt counts t.scope.IntExponentialHistogram(metrics.ExponentialTaskAttemptCountsPerDomain, t.attempt) t.scope.RecordTimer(metrics.TaskLatencyPerDomain, latency) t.scope.ExponentialHistogram(metrics.ExponentialTaskLatencyPerDomain, latency) t.scope.RecordTimer(metrics.TaskQueueLatencyPerDomain, queueLatency) t.scope.ExponentialHistogram(metrics.ExponentialTaskQueueLatencyPerDomain, queueLatency) taskListTaggedScope := t.scope.Tagged(common.GetTaskListTag(t.GetOriginalTaskList(), t.GetOriginalTaskListKind())) taskListTaggedScope.ExponentialHistogram(metrics.ExponentialTaskLatencyPerTaskList, latency) taskListTaggedScope.ExponentialHistogram(metrics.ExponentialTaskQueueLatencyPerTaskList, queueLatency) } if t.eventLogger != nil && t.shouldProcessTask && t.attempt != 0 { // only dump events when the task should be processed and has been retried t.eventLogger.FlushEvents("Task processing events") } } func (t *taskImpl) Nack(err error) { if t.State() != ctask.TaskStatePending { return } logEvent(t.eventLogger, "Nacked task") if t.shouldResubmitOnNack(err) { if submitted, _ := t.taskProcessor.TrySubmit(t); submitted { return } } if t.rescheduler != nil { t.rescheduler.RescheduleTask(t, t.timeSource.Now().Add(t.backoffDuration(t.GetAttempt()))) } else if t.redispatcher != nil { t.redispatcher.RedispatchTask(t, t.timeSource.Now().Add(t.backoffDuration(t.GetAttempt()))) } } func (t *taskImpl) Cancel() { t.Lock() defer t.Unlock() if t.state == ctask.TaskStatePending { t.state = ctask.TaskStateCanceled } } func (t *taskImpl) State() ctask.State { t.Lock() defer t.Unlock() return t.state } func (t *taskImpl) Priority() int { t.Lock() defer t.Unlock() return t.priority } func (t *taskImpl) SetPriority(priority int) { t.Lock() defer t.Unlock() t.priority = priority } func (t *taskImpl) GetShard() shard.Context { return t.shard } func (t *taskImpl) GetAttempt() int { t.Lock() defer t.Unlock() return t.attempt } func (t *taskImpl) GetInfo() persistence.Task { return t.Task } func (t *taskImpl) GetQueueType() QueueType { return t.queueType } func (t *taskImpl) SetInitialSubmitTime(submitTime time.Time) { t.Lock() defer t.Unlock() t.initialSubmitTime = submitTime } func (t *taskImpl) shouldResubmitOnNack(err error) bool { // TODO: for now only resubmit active task on Nack() // we can also consider resubmit standby tasks that fails due to certain error types // this may require change the Nack() interface to Nack(error) if errors.Is(err, errDomainBecomesActive) { return true } return t.GetAttempt() < activeTaskResubmitMaxAttempts && (t.queueType == QueueTypeActiveTransfer || t.queueType == QueueTypeActiveTimer || ((t.queueType == QueueTypeTransfer || t.queueType == QueueTypeTimer) && t.isPreviousExecutorActive)) } func (t *taskImpl) backoffDuration(attempt int) time.Duration { // TODO: we might need to consider using the same backoff policy for active and standby tasks, // but we keep them separate for now so that we can compare metrics between history queue v1 and v2 if t.isPreviousExecutorActive { return activeTaskRedispatchPolicy.ComputeNextDelay(0, attempt) } return standbyTaskRedispatchPolicy.ComputeNextDelay(0, attempt) } func logEvent( eventLogger eventLogger, msg string, detail ...interface{}, ) { if eventLogger != nil { eventLogger.AddEvent(msg, detail...) } } // getOrCreateDomainTaggedScope returns cached domain-tagged metrics scope if exists // otherwise, it creates a new domain-tagged scope, cache and return the scope func getOrCreateDomainTaggedScope( shard shard.Context, scopeIdx metrics.ScopeIdx, domainID string, logger log.Logger, ) metrics.Scope { scopeCache := shard.GetService().GetDomainMetricsScopeCache() scope, ok := scopeCache.Get(domainID, scopeIdx) if !ok { domainTag, err := getDomainTagByID(shard.GetDomainCache(), domainID) scope = shard.GetMetricsClient().Scope(scopeIdx, domainTag) if err == nil { scopeCache.Put(domainID, scopeIdx, scope) } else { logger.Error("Unable to get domainName", tag.Error(err)) } } return scope } func getDomainTagByID( domainCache cache.DomainCache, domainID string, ) (metrics.Tag, error) { domainName, err := domainCache.GetDomainName(domainID) if err != nil { return metrics.DomainUnknownTag(), err } return metrics.DomainTag(domainName), nil } func createTaskRedispatchPolicy(initialInterval time.Duration) backoff.RetryPolicy { backoffPolicy := backoff.NewExponentialRetryPolicy(initialInterval) backoffPolicy.SetBackoffCoefficient(redispatchBackoffCoefficient) backoffPolicy.SetMaximumInterval(redispatchMaxBackoffInternval) backoffPolicy.SetExpirationInterval(backoff.NoInterval) return backoffPolicy } ================================================ FILE: service/history/task/task_rate_limiter.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination task_rate_limiter_mock.go github.com/uber/cadence/service/history/task RateLimiter package task import ( "context" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" dynamicquotas "github.com/uber/cadence/common/dynamicconfig/quotas" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" ) type ( RateLimiter interface { Allow(Task) bool Wait(context.Context, Task) error } taskRateLimiterImpl struct { logger log.Logger metricsScope metrics.Scope domainCache cache.DomainCache limiters quotas.ICollection enabled dynamicproperties.BoolPropertyFn enableShadowMode dynamicproperties.BoolPropertyFnWithDomainFilter } ) func NewRateLimiter( logger log.Logger, metricsClient metrics.Client, domainCache cache.DomainCache, config *config.Config, controller shard.Controller, ) RateLimiter { rps := func(domain string) int { totalShards := float64(config.NumberOfShards) totalRPS := float64(config.TaskSchedulerGlobalDomainRPS(domain)) numShards := float64(controller.NumShards()) return int(totalRPS * numShards / totalShards) } limiterFactory := dynamicquotas.NewSimpleDynamicRateLimiterFactory(rps) return &taskRateLimiterImpl{ logger: logger, metricsScope: metricsClient.Scope(metrics.TaskSchedulerRateLimiterScope), domainCache: domainCache, enabled: config.TaskSchedulerEnableRateLimiter, enableShadowMode: config.TaskSchedulerEnableRateLimiterShadowMode, limiters: quotas.NewCollection(limiterFactory), } } func (r *taskRateLimiterImpl) Allow(t Task) bool { if !r.enabled() { return true } limiter, scope, shadow := r.getLimiter(t) allow := limiter.Allow() if allow { scope.IncCounter(metrics.TaskSchedulerAllowedCounterPerDomain) return true } scope.IncCounter(metrics.TaskSchedulerThrottledCounterPerDomain) return shadow } func (r *taskRateLimiterImpl) Wait(ctx context.Context, t Task) error { if !r.enabled() { return nil } limiter, _, _ := r.getLimiter(t) // wait has kinda complicated semantics and we haven't really used it and it's not super well understood // the current interface of rate limiter doesn't support shadow mode, so we are not supporting shadow mode for Wait method now // Besides, this code path is not hit in production right now return limiter.Wait(ctx) } func (r *taskRateLimiterImpl) getLimiter(t Task) (quotas.Limiter, metrics.Scope, bool) { domainID := t.GetDomainID() domainName, err := r.domainCache.GetDomainName(domainID) if err != nil { r.logger.Warn("failed to get domain name from domain cache", tag.WorkflowDomainID(domainID), tag.Error(err)) } return r.limiters.For(domainName), r.metricsScope.Tagged(metrics.DomainTag(domainName)), r.enableShadowMode(domainName) } ================================================ FILE: service/history/task/task_rate_limiter_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/task (interfaces: RateLimiter) // // Generated by this command: // // mockgen -package task -destination task_rate_limiter_mock.go github.com/uber/cadence/service/history/task RateLimiter // // Package task is a generated GoMock package. package task import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockRateLimiter is a mock of RateLimiter interface. type MockRateLimiter struct { ctrl *gomock.Controller recorder *MockRateLimiterMockRecorder isgomock struct{} } // MockRateLimiterMockRecorder is the mock recorder for MockRateLimiter. type MockRateLimiterMockRecorder struct { mock *MockRateLimiter } // NewMockRateLimiter creates a new mock instance. func NewMockRateLimiter(ctrl *gomock.Controller) *MockRateLimiter { mock := &MockRateLimiter{ctrl: ctrl} mock.recorder = &MockRateLimiterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockRateLimiter) EXPECT() *MockRateLimiterMockRecorder { return m.recorder } // Allow mocks base method. func (m *MockRateLimiter) Allow(arg0 Task) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Allow", arg0) ret0, _ := ret[0].(bool) return ret0 } // Allow indicates an expected call of Allow. func (mr *MockRateLimiterMockRecorder) Allow(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Allow", reflect.TypeOf((*MockRateLimiter)(nil).Allow), arg0) } // Wait mocks base method. func (m *MockRateLimiter) Wait(arg0 context.Context, arg1 Task) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Wait", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // Wait indicates an expected call of Wait. func (mr *MockRateLimiterMockRecorder) Wait(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Wait", reflect.TypeOf((*MockRateLimiter)(nil).Wait), arg0, arg1) } ================================================ FILE: service/history/task/task_rate_limiter_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package task import ( "context" "errors" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" ctask "github.com/uber/cadence/common/task" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/shard" ) type ( noopTask struct { sync.RWMutex persistence.Task queueType QueueType shard shard.Context attempt int priority int state ctask.State } ) func (s *noopTask) Execute() error { return nil } func (s *noopTask) HandleErr(err error) error { return nil } func (s *noopTask) RetryErr(err error) bool { return false } func (s *noopTask) Ack() { s.Lock() defer s.Unlock() if s.state != ctask.TaskStatePending { return } s.state = ctask.TaskStateAcked } func (s *noopTask) Nack(err error) { s.Lock() defer s.Unlock() if s.state != ctask.TaskStatePending { return } } func (s *noopTask) Cancel() { s.Lock() defer s.Unlock() if s.state == ctask.TaskStatePending { s.state = ctask.TaskStateCanceled } } func (s *noopTask) State() ctask.State { s.RLock() defer s.RUnlock() return s.state } func (s *noopTask) Priority() int { s.RLock() defer s.RUnlock() return s.priority } func (s *noopTask) SetPriority(p int) { s.priority = p } func (s *noopTask) GetShard() shard.Context { return s.shard } func (s *noopTask) GetQueueType() QueueType { return s.queueType } func (s *noopTask) GetAttempt() int { return s.attempt } func (s *noopTask) GetInfo() persistence.Task { return s.Task } func (s *noopTask) SetInitialSubmitTime(submitTime time.Time) { } type taskRateLimiterMockDeps struct { ctrl *gomock.Controller mockDomainCache *cache.MockDomainCache mockShardController *shard.MockController mockICollection *quotas.MockICollection dynamicClient dynamicconfig.Client } func setupMocksForTaskRateLimiter(t *testing.T, mockQuotasCollection bool) (*taskRateLimiterImpl, *taskRateLimiterMockDeps) { ctrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(ctrl) mockShardController := shard.NewMockController(ctrl) dynamicClient := dynamicconfig.NewInMemoryClient() deps := &taskRateLimiterMockDeps{ ctrl: ctrl, mockDomainCache: mockDomainCache, mockShardController: mockShardController, dynamicClient: dynamicClient, } config := config.New( dynamicconfig.NewCollection( dynamicClient, testlogger.New(t), ), 16, 1024, false, "hostname", ) rateLimiter := NewRateLimiter( testlogger.New(t), metrics.NewNoopMetricsClient(), deps.mockDomainCache, config, deps.mockShardController, ) r, ok := rateLimiter.(*taskRateLimiterImpl) require.True(t, ok, "rate limiter type assertion failure") if mockQuotasCollection { deps.mockICollection = quotas.NewMockICollection(ctrl) r.limiters = deps.mockICollection } return r, deps } func TestRateLimiterRPS(t *testing.T) { r, deps := setupMocksForTaskRateLimiter(t, false) deps.mockShardController.EXPECT().NumShards().Return(8).AnyTimes() require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerGlobalDomainRPS, 100)) l := r.limiters.For("test-domain").Limit() assert.Equal(t, 50, int(l)) } func TestRateLimiterAllow(t *testing.T) { testCases := []struct { name string task Task setupMocks func(*taskRateLimiterMockDeps) expected bool }{ { name: "Not rate limited", task: &noopTask{ Task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "test-domain-id", }, }, }, setupMocks: func(deps *taskRateLimiterMockDeps) { require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerGlobalDomainRPS, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiter, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiterShadowMode, false)) deps.mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("test-domain", nil) limiter := quotas.NewMockLimiter(deps.ctrl) deps.mockICollection.EXPECT().For("test-domain").Return(limiter) limiter.EXPECT().Allow().Return(true) }, expected: true, }, { name: "Not rate limited - domain cache error ignored", task: &noopTask{ Task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "test-domain-id", }, }, }, setupMocks: func(deps *taskRateLimiterMockDeps) { require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerGlobalDomainRPS, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiter, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiterShadowMode, false)) deps.mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("", errors.New("cache error")) limiter := quotas.NewMockLimiter(deps.ctrl) deps.mockICollection.EXPECT().For("").Return(limiter) limiter.EXPECT().Allow().Return(true) }, expected: true, }, { name: "Rate limited - shadow mode", task: &noopTask{ Task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "test-domain-id", }, }, }, setupMocks: func(deps *taskRateLimiterMockDeps) { require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerGlobalDomainRPS, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiter, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiterShadowMode, true)) deps.mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("test-domain", nil) limiter := quotas.NewMockLimiter(deps.ctrl) deps.mockICollection.EXPECT().For("test-domain").Return(limiter) limiter.EXPECT().Allow().Return(false) }, expected: true, }, { name: "Rate limited", task: &noopTask{ Task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "test-domain-id", }, }, }, setupMocks: func(deps *taskRateLimiterMockDeps) { require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerGlobalDomainRPS, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiter, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiterShadowMode, false)) deps.mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("test-domain", nil) limiter := quotas.NewMockLimiter(deps.ctrl) deps.mockICollection.EXPECT().For("test-domain").Return(limiter) limiter.EXPECT().Allow().Return(false) }, expected: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { r, deps := setupMocksForTaskRateLimiter(t, true) tc.setupMocks(deps) allow := r.Allow(tc.task) assert.Equal(t, tc.expected, allow) }) } } func TestRateLimiterWait(t *testing.T) { testCases := []struct { name string task Task setupMocks func(*taskRateLimiterMockDeps) expectErr bool expectedErr string }{ { name: "Not rate limited", task: &noopTask{ Task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "test-domain-id", }, }, }, setupMocks: func(deps *taskRateLimiterMockDeps) { require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerGlobalDomainRPS, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiter, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiterShadowMode, false)) deps.mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("test-domain", nil) limiter := quotas.NewMockLimiter(deps.ctrl) deps.mockICollection.EXPECT().For("test-domain").Return(limiter) limiter.EXPECT().Wait(gomock.Any()).Return(nil) }, expectErr: false, }, { name: "Not rate limited - domain cache error ignored", task: &noopTask{ Task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "test-domain-id", }, }, }, setupMocks: func(deps *taskRateLimiterMockDeps) { require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerGlobalDomainRPS, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiter, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiterShadowMode, false)) deps.mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("", errors.New("cache error")) limiter := quotas.NewMockLimiter(deps.ctrl) deps.mockICollection.EXPECT().For("").Return(limiter) limiter.EXPECT().Wait(gomock.Any()).Return(nil) }, expectErr: false, }, { name: "Rate limited - error", task: &noopTask{ Task: &persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "test-domain-id", }, }, }, setupMocks: func(deps *taskRateLimiterMockDeps) { require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerGlobalDomainRPS, 100)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiter, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.TaskSchedulerEnableRateLimiterShadowMode, false)) deps.mockDomainCache.EXPECT().GetDomainName("test-domain-id").Return("test-domain", nil) limiter := quotas.NewMockLimiter(deps.ctrl) deps.mockICollection.EXPECT().For("test-domain").Return(limiter) limiter.EXPECT().Wait(gomock.Any()).Return(errors.New("wait error")) }, expectErr: true, expectedErr: "wait error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { r, deps := setupMocksForTaskRateLimiter(t, true) tc.setupMocks(deps) err := r.Wait(context.Background(), tc.task) if tc.expectErr { assert.ErrorContains(t, err, tc.expectedErr) } else { assert.NoError(t, err) } }) } } ================================================ FILE: service/history/task/task_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ctask "github.com/uber/cadence/common/task" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/shard" ) type ( taskSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockTaskExecutor *MockExecutor mockTaskProcessor *MockProcessor mockTaskRedispatcher *MockRedispatcher mockTaskInfo *persistence.MockTask logger log.Logger maxRetryCount dynamicproperties.IntPropertyFn } ) func TestTaskSuite(t *testing.T) { s := new(taskSuite) suite.Run(t, s) } func (s *taskSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, }, config.NewForTest(), ) s.mockTaskExecutor = NewMockExecutor(s.controller) s.mockTaskProcessor = NewMockProcessor(s.controller) s.mockTaskRedispatcher = NewMockRedispatcher(s.controller) s.mockTaskInfo = persistence.NewMockTask(s.controller) s.mockTaskInfo.EXPECT().GetDomainID().Return(constants.TestDomainID).AnyTimes() s.mockTaskInfo.EXPECT().GetOriginalTaskList().Return("test-task-list").AnyTimes() s.mockTaskInfo.EXPECT().GetOriginalTaskListKind().Return(types.TaskListKindNormal).AnyTimes() s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).AnyTimes() s.logger = testlogger.New(s.Suite.T()) s.maxRetryCount = dynamicproperties.GetIntPropertyFn(10) } func (s *taskSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *taskSuite) TestExecute_TaskFilterErr() { taskFilterErr := errors.New("some random error") taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return false, taskFilterErr }) err := taskBase.Execute() s.Equal(taskFilterErr, err) } func (s *taskSuite) TestExecute_ExecutionErr() { task := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) executionErr := errors.New("some random error") s.mockTaskExecutor.EXPECT().Execute(task).Return(ExecuteResponse{ Scope: metrics.NoopScope, IsActiveTask: true, }, executionErr).Times(1) err := task.Execute() s.Equal(executionErr, err) } func (s *taskSuite) TestExecute_Success() { task := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) s.mockTaskExecutor.EXPECT().Execute(task).Return(ExecuteResponse{ Scope: metrics.NoopScope, IsActiveTask: true, }, nil).Times(1) err := task.Execute() s.NoError(err) } func (s *taskSuite) TestHandleErr_ErrEntityNotExists() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) err := &types.EntityNotExistsError{} s.NoError(taskBase.HandleErr(err)) } func (s *taskSuite) TestHandleErr_ErrTaskRetry() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) err := &redispatchError{Reason: "random-reason"} s.Equal(err, taskBase.HandleErr(err)) } func (s *taskSuite) TestHandleErr_ErrTaskDiscarded() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) err := ErrTaskDiscarded s.NoError(taskBase.HandleErr(err)) } func (s *taskSuite) TestHandleErr_ErrTargetDomainNotActive() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) err := &types.DomainNotActiveError{} // we should always return the target domain not active error // no matter that the submit time is taskBase.initialSubmitTime = time.Now().Add(-cache.DomainCacheRefreshInterval*time.Duration(5) - time.Second) s.Equal(nil, taskBase.HandleErr(err), "should drop errors after a reasonable time") taskBase.initialSubmitTime = time.Now() s.Equal(err, taskBase.HandleErr(err)) } func (s *taskSuite) TestHandleErr_ErrDomainNotActive() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) err := &types.DomainNotActiveError{} taskBase.initialSubmitTime = time.Now().Add(-cache.DomainCacheRefreshInterval*time.Duration(5) - time.Second) s.NoError(taskBase.HandleErr(err)) taskBase.initialSubmitTime = time.Now() s.Equal(err, taskBase.HandleErr(err)) } func (s *taskSuite) TestHandleErr_ErrWorkflowRateLimited() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) taskBase.initialSubmitTime = time.Now() s.Equal(errWorkflowRateLimited, taskBase.HandleErr(errWorkflowRateLimited)) } func (s *taskSuite) TestHandleErr_ErrShardRecentlyClosed() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) taskBase.initialSubmitTime = time.Now() shardClosedError := &shard.ErrShardClosed{ Msg: "shard closed", // The shard was closed within the TimeBeforeShardClosedIsError interval ClosedAt: time.Now().Add(-shard.TimeBeforeShardClosedIsError / 2), } s.Equal(shardClosedError, taskBase.HandleErr(shardClosedError)) } func (s *taskSuite) TestHandleErr_ErrTaskListNotOwnedByHost() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) taskBase.initialSubmitTime = time.Now() taskListNotOwnedByHost := &cadence_errors.TaskListNotOwnedByHostError{ OwnedByIdentity: "HostNameOwnedBy", MyIdentity: "HostNameMe", TasklistName: "TaskListName", } s.Equal(taskListNotOwnedByHost, taskBase.HandleErr(taskListNotOwnedByHost)) } func (s *taskSuite) TestHandleErr_ErrCurrentWorkflowConditionFailed() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) err := &persistence.CurrentWorkflowConditionFailedError{} s.NoError(taskBase.HandleErr(err)) } func (s *taskSuite) TestHandleErr_UnknownErr() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) // Need to mock a return value for function GetTaskType // If don't do, there'll be an error when code goes into the defer function: // Unexpected call: because: there are no expected calls of the method "GetTaskType" for that receiver // But can't put it in the setup function since it may cause other errors s.mockTaskInfo.EXPECT().GetTaskType().Return(123) // make sure it will go into the defer function. // in this case, make it 0 < attempt < stickyTaskMaxRetryCount taskBase.attempt = 10 err := errors.New("some random error") s.Equal(err, taskBase.HandleErr(err)) } func (s *taskSuite) TestTaskCancel() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) taskBase.Cancel() s.Equal(ctask.TaskStateCanceled, taskBase.State()) s.NoError(taskBase.Execute()) taskBase.Ack() s.Equal(ctask.TaskStateCanceled, taskBase.State()) taskBase.Nack(nil) s.Equal(ctask.TaskStateCanceled, taskBase.State()) s.False(taskBase.RetryErr(errors.New("some random error"))) } func (s *taskSuite) TestTaskState() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) s.Equal(ctask.TaskStatePending, taskBase.State()) taskBase.Ack() s.Equal(ctask.TaskStateAcked, taskBase.State()) taskBase.Nack(nil) s.Equal(ctask.TaskStateAcked, taskBase.State()) } func (s *taskSuite) TestTaskPriority() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) priority := 10 taskBase.SetPriority(priority) s.Equal(priority, taskBase.Priority()) } func (s *taskSuite) TestTaskNack_ResubmitSucceeded() { task := s.newTestTask( func(task persistence.Task) (bool, error) { return true, nil }, ) s.mockTaskProcessor.EXPECT().TrySubmit(task).Return(true, nil).Times(1) task.Nack(nil) s.Equal(ctask.TaskStatePending, task.State()) } func (s *taskSuite) TestTaskNack_DomainBecomesActive() { task := s.newTestTask( func(task persistence.Task) (bool, error) { return true, nil }, ) task.queueType = QueueTypeTransfer s.mockTaskProcessor.EXPECT().TrySubmit(task).Return(true, nil).Times(1) task.Nack(errDomainBecomesActive) s.Equal(ctask.TaskStatePending, task.State()) } func (s *taskSuite) TestTaskNack_ResubmitFailed() { task := s.newTestTask( func(task persistence.Task) (bool, error) { return true, nil }, ) s.mockTaskProcessor.EXPECT().TrySubmit(task).Return(false, errTaskProcessorNotRunning).Times(1) s.mockTaskRedispatcher.EXPECT().RedispatchTask(task, gomock.Any()).Times(1) task.Nack(nil) s.Equal(ctask.TaskStatePending, task.State()) } func (s *taskSuite) TestHandleErr_ErrMaxAttempts() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) taskBase.criticalRetryCount = func(i ...dynamicproperties.FilterOption) int { return 0 } s.mockTaskInfo.EXPECT().GetTaskType().Return(0) assert.NotPanics(s.T(), func() { taskBase.HandleErr(errors.New("err")) }) } func (s *taskSuite) TestRetryErr() { taskBase := s.newTestTask(func(task persistence.Task) (bool, error) { return true, nil }) s.Equal(false, taskBase.RetryErr(&shard.ErrShardClosed{})) s.Equal(false, taskBase.RetryErr(errWorkflowBusy)) s.Equal(false, taskBase.RetryErr(ErrTaskPendingActive)) s.Equal(false, taskBase.RetryErr(context.DeadlineExceeded)) s.Equal(false, taskBase.RetryErr(&redispatchError{Reason: "random-reason"})) // rate limited errors are retried s.Equal(true, taskBase.RetryErr(errWorkflowRateLimited)) } func (s *taskSuite) newTestTask( taskFilter Filter, ) *taskImpl { taskBase := NewHistoryTask( s.mockShard, s.mockTaskInfo, QueueTypeActiveTransfer, s.logger, taskFilter, s.mockTaskExecutor, s.mockTaskProcessor, s.mockTaskRedispatcher, s.maxRetryCount, ).(*taskImpl) taskBase.scope = s.mockShard.GetMetricsClient().Scope(0) return taskBase } ================================================ FILE: service/history/task/task_util.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "fmt" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) type ( mockTaskMatcher struct { task *MockTask } ) // InitializeLoggerForTask creates a new logger with additional tags for task info func InitializeLoggerForTask( shardID int, task persistence.Task, logger log.Logger, ) log.Logger { return logger.WithTags( tag.ShardID(shardID), tag.TaskID(task.GetTaskID()), tag.TaskVisibilityTimestamp(task.GetVisibilityTimestamp().UnixNano()), tag.FailoverVersion(task.GetVersion()), tag.TaskType(task.GetTaskType()), tag.WorkflowDomainID(task.GetDomainID()), tag.WorkflowID(task.GetWorkflowID()), tag.WorkflowRunID(task.GetRunID()), ) } // GetTransferTaskMetricsScope returns the metrics scope index for transfer task func GetTransferTaskMetricsScope( taskType int, isActive bool, ) metrics.ScopeIdx { switch taskType { case persistence.TransferTaskTypeActivityTask: if isActive { return metrics.TransferActiveTaskActivityScope } return metrics.TransferStandbyTaskActivityScope case persistence.TransferTaskTypeDecisionTask: if isActive { return metrics.TransferActiveTaskDecisionScope } return metrics.TransferStandbyTaskDecisionScope case persistence.TransferTaskTypeCloseExecution: if isActive { return metrics.TransferActiveTaskCloseExecutionScope } return metrics.TransferStandbyTaskCloseExecutionScope case persistence.TransferTaskTypeCancelExecution: if isActive { return metrics.TransferActiveTaskCancelExecutionScope } return metrics.TransferStandbyTaskCancelExecutionScope case persistence.TransferTaskTypeSignalExecution: if isActive { return metrics.TransferActiveTaskSignalExecutionScope } return metrics.TransferStandbyTaskSignalExecutionScope case persistence.TransferTaskTypeStartChildExecution: if isActive { return metrics.TransferActiveTaskStartChildExecutionScope } return metrics.TransferStandbyTaskStartChildExecutionScope case persistence.TransferTaskTypeRecordWorkflowStarted: if isActive { return metrics.TransferActiveTaskRecordWorkflowStartedScope } return metrics.TransferStandbyTaskRecordWorkflowStartedScope case persistence.TransferTaskTypeResetWorkflow: if isActive { return metrics.TransferActiveTaskResetWorkflowScope } return metrics.TransferStandbyTaskResetWorkflowScope case persistence.TransferTaskTypeUpsertWorkflowSearchAttributes: if isActive { return metrics.TransferActiveTaskUpsertWorkflowSearchAttributesScope } return metrics.TransferStandbyTaskUpsertWorkflowSearchAttributesScope case persistence.TransferTaskTypeRecordWorkflowClosed: if isActive { return metrics.TransferActiveTaskRecordWorkflowClosedScope } return metrics.TransferStandbyTaskRecordWorkflowClosedScope case persistence.TransferTaskTypeRecordChildExecutionCompleted: if isActive { return metrics.TransferActiveTaskRecordChildExecutionCompletedScope } return metrics.TransferStandbyTaskRecordChildExecutionCompletedScope case persistence.TransferTaskTypeApplyParentClosePolicy: if isActive { return metrics.TransferActiveTaskApplyParentClosePolicyScope } return metrics.TransferStandbyTaskApplyParentClosePolicyScope default: if isActive { return metrics.TransferActiveQueueProcessorScope } return metrics.TransferStandbyQueueProcessorScope } } // GetTimerTaskMetricScope returns the metrics scope index for timer task func GetTimerTaskMetricScope( taskType int, isActive bool, ) metrics.ScopeIdx { switch taskType { case persistence.TaskTypeDecisionTimeout: if isActive { return metrics.TimerActiveTaskDecisionTimeoutScope } return metrics.TimerStandbyTaskDecisionTimeoutScope case persistence.TaskTypeActivityTimeout: if isActive { return metrics.TimerActiveTaskActivityTimeoutScope } return metrics.TimerStandbyTaskActivityTimeoutScope case persistence.TaskTypeUserTimer: if isActive { return metrics.TimerActiveTaskUserTimerScope } return metrics.TimerStandbyTaskUserTimerScope case persistence.TaskTypeWorkflowTimeout: if isActive { return metrics.TimerActiveTaskWorkflowTimeoutScope } return metrics.TimerStandbyTaskWorkflowTimeoutScope case persistence.TaskTypeDeleteHistoryEvent: if isActive { return metrics.TimerActiveTaskDeleteHistoryEventScope } return metrics.TimerStandbyTaskDeleteHistoryEventScope case persistence.TaskTypeActivityRetryTimer: if isActive { return metrics.TimerActiveTaskActivityRetryTimerScope } return metrics.TimerStandbyTaskActivityRetryTimerScope case persistence.TaskTypeWorkflowBackoffTimer: if isActive { return metrics.TimerActiveTaskWorkflowBackoffTimerScope } return metrics.TimerStandbyTaskWorkflowBackoffTimerScope default: if isActive { return metrics.TimerActiveQueueProcessorScope } return metrics.TimerStandbyQueueProcessorScope } } // verifyTaskVersion, will return true if failover version check is successful func verifyTaskVersion( shard shard.Context, logger log.Logger, domainID string, version int64, taskVersion int64, task persistence.Task, ) (bool, error) { // the first return value is whether this task is valid for further processing domainEntry, err := shard.GetDomainCache().GetDomainByID(domainID) if err != nil { logger.Debug(fmt.Sprintf("Cannot find domainID: %v, err: %v.", domainID, err)) return false, err } if !domainEntry.IsGlobalDomain() { logger.Debug(fmt.Sprintf("DomainID: %v is not active, task: %v version check pass", domainID, task)) return true, nil } else if version != taskVersion { logger.Debug(fmt.Sprintf("DomainID: %v is active, task: %v version != target version: %v.", domainID, task, version)) return false, nil } logger.Debug(fmt.Sprintf("DomainID: %v is active, task: %v version == target version: %v.", domainID, task, version)) return true, nil } // load mutable state, if mutable state's next event ID <= task event ID, will attempt to refresh // if still mutable state's next event ID <= task event ID, will return nil, nil func loadMutableState( ctx context.Context, wfContext execution.Context, task persistence.Task, metricsScope metrics.Scope, logger log.Logger, eventID int64, ) (execution.MutableState, error) { msBuilder, err := wfContext.LoadWorkflowExecution(ctx) if err != nil { if _, ok := err.(*types.EntityNotExistsError); ok { // this could happen if this is a duplicate processing of the task, and the execution has already completed. return nil, nil } return nil, err } executionInfo := msBuilder.GetExecutionInfo() // check to see if cache needs to be refreshed as we could potentially have stale workflow execution // the exception is decision consistently fail // there will be no event generated, thus making the decision schedule ID == next event ID isDecisionRetry := (task.GetTaskType() == persistence.TaskTypeDecisionTimeout || task.GetTaskType() == persistence.TransferTaskTypeDecisionTask) && executionInfo.DecisionScheduleID == eventID && executionInfo.DecisionAttempt > 0 if eventID >= msBuilder.GetNextEventID() && !isDecisionRetry { metricsScope.IncCounter(metrics.StaleMutableStateCounter) wfContext.Clear() msBuilder, err = wfContext.LoadWorkflowExecution(ctx) if err != nil { return nil, err } // after refresh, still mutable state's next event ID <= task ID if eventID >= msBuilder.GetNextEventID() { domainName := msBuilder.GetDomainEntry().GetInfo().Name metricsScope.Tagged(metrics.DomainTag(domainName)).IncCounter(metrics.DataInconsistentCounter) logger.Error("Task Processor: task event ID >= MS NextEventID, skip.", tag.WorkflowDomainName(domainName), tag.WorkflowDomainID(task.GetDomainID()), tag.WorkflowID(task.GetWorkflowID()), tag.WorkflowRunID(task.GetRunID()), tag.TaskType(task.GetTaskType()), tag.TaskID(task.GetTaskID()), tag.WorkflowEventID(eventID), tag.WorkflowNextEventID(msBuilder.GetNextEventID()), ) return nil, nil } } return msBuilder, nil } func timeoutWorkflow( mutableState execution.MutableState, eventBatchFirstEventID int64, ) error { if decision, ok := mutableState.GetInFlightDecision(); ok { if err := execution.FailDecision( mutableState, decision, types.DecisionTaskFailedCauseForceCloseDecision, ); err != nil { return err } } _, err := mutableState.AddTimeoutWorkflowEvent( eventBatchFirstEventID, ) return err } func retryWorkflow( ctx context.Context, mutableState execution.MutableState, eventBatchFirstEventID int64, parentDomainName string, continueAsNewAttributes *types.ContinueAsNewWorkflowExecutionDecisionAttributes, ) (execution.MutableState, error) { if decision, ok := mutableState.GetInFlightDecision(); ok { if err := execution.FailDecision( mutableState, decision, types.DecisionTaskFailedCauseForceCloseDecision, ); err != nil { return nil, err } } _, newMutableState, err := mutableState.AddContinueAsNewEvent( ctx, eventBatchFirstEventID, constants.EmptyEventID, parentDomainName, continueAsNewAttributes, ) if err != nil { return nil, err } return newMutableState, nil } func getWorkflowExecution( taskInfo persistence.Task, ) types.WorkflowExecution { return types.WorkflowExecution{ WorkflowID: taskInfo.GetWorkflowID(), RunID: taskInfo.GetRunID(), } } func shouldPushToMatching( ctx context.Context, shard shard.Context, taskInfo persistence.Task, ) (bool, error) { domainEntry, err := shard.GetDomainCache().GetDomainByID(taskInfo.GetDomainID()) if err != nil { return false, err } if !domainEntry.GetReplicationConfig().IsActiveActive() { // Preserve the behavior of active-standby and local domains. Always push to matching return true, nil } // For active-active domains, only push to matching if the workflow is active in current cluster // We may revisit this logic in the future. Current idea is to not pollute tasklists with passive workflows of active-active domains // because they would cause head-of-line blocking in the tasklist. Passive task completion logic doesn't apply to active-active domains. activeClusterInfo, err := shard.GetActiveClusterManager().GetActiveClusterInfoByWorkflow(ctx, taskInfo.GetDomainID(), taskInfo.GetWorkflowID(), taskInfo.GetRunID()) if err != nil { return false, err } if activeClusterInfo.ActiveClusterName != shard.GetClusterMetadata().GetCurrentClusterName() { return false, nil } return true, nil } // NewMockTaskMatcher creates a gomock matcher for mock Task func NewMockTaskMatcher(mockTask *MockTask) gomock.Matcher { return &mockTaskMatcher{ task: mockTask, } } func (m *mockTaskMatcher) Matches(x interface{}) bool { taskPtr, ok := x.(*MockTask) if !ok { return false } return taskPtr == m.task } func (m *mockTaskMatcher) String() string { return fmt.Sprintf("is equal to %v", m.task) } ================================================ FILE: service/history/task/task_util_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "errors" "fmt" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "go.uber.org/zap" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) func TestInitializeLoggerForTask(t *testing.T) { timeSource := clock.NewMockedTimeSource() testCases := []struct { name string task persistence.Task }{ { name: "UserTimerTask", task: &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, TaskData: persistence.TaskData{ TaskID: 1, VisibilityTimestamp: timeSource.Now(), Version: 10, }, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { logger := InitializeLoggerForTask(1, tc.task, log.NewLogger(zap.NewNop())) assert.NotNil(t, logger) }) } } func TestGetTransferTaskMetricsScope(t *testing.T) { testCases := []struct { name string taskType int isActive bool expectedScope metrics.ScopeIdx }{ { name: "TransferTaskTypeActivityTask - active", taskType: persistence.TransferTaskTypeActivityTask, isActive: true, expectedScope: metrics.TransferActiveTaskActivityScope, }, { name: "TransferTaskTypeActivityTask - standby", taskType: persistence.TransferTaskTypeActivityTask, isActive: false, expectedScope: metrics.TransferStandbyTaskActivityScope, }, { name: "TransferTaskTypeDecisionTask - active", taskType: persistence.TransferTaskTypeDecisionTask, isActive: true, expectedScope: metrics.TransferActiveTaskDecisionScope, }, { name: "TransferTaskTypeDecisionTask - standby", taskType: persistence.TransferTaskTypeDecisionTask, isActive: false, expectedScope: metrics.TransferStandbyTaskDecisionScope, }, { name: "TransferTaskTypeCloseExecution - active", taskType: persistence.TransferTaskTypeCloseExecution, isActive: true, expectedScope: metrics.TransferActiveTaskCloseExecutionScope, }, { name: "TransferTaskTypeCloseExecution - standby", taskType: persistence.TransferTaskTypeCloseExecution, isActive: false, expectedScope: metrics.TransferStandbyTaskCloseExecutionScope, }, { name: "TransferTaskTypeCancelExecution - active", taskType: persistence.TransferTaskTypeCancelExecution, isActive: true, expectedScope: metrics.TransferActiveTaskCancelExecutionScope, }, { name: "TransferTaskTypeCancelExecution - standby", taskType: persistence.TransferTaskTypeCancelExecution, isActive: false, expectedScope: metrics.TransferStandbyTaskCancelExecutionScope, }, { name: "TransferTaskTypeSignalExecution - active", taskType: persistence.TransferTaskTypeSignalExecution, isActive: true, expectedScope: metrics.TransferActiveTaskSignalExecutionScope, }, { name: "TransferTaskTypeSignalExecution - standby", taskType: persistence.TransferTaskTypeSignalExecution, isActive: false, expectedScope: metrics.TransferStandbyTaskSignalExecutionScope, }, { name: "TransferTaskTypeStartChildExecution - active", taskType: persistence.TransferTaskTypeStartChildExecution, isActive: true, expectedScope: metrics.TransferActiveTaskStartChildExecutionScope, }, { name: "TransferTaskTypeStartChildExecution - standby", taskType: persistence.TransferTaskTypeStartChildExecution, isActive: false, expectedScope: metrics.TransferStandbyTaskStartChildExecutionScope, }, { name: "TransferTaskTypeRecordWorkflowStarted - active", taskType: persistence.TransferTaskTypeRecordWorkflowStarted, isActive: true, expectedScope: metrics.TransferActiveTaskRecordWorkflowStartedScope, }, { name: "TransferTaskTypeRecordWorkflowStarted - standby", taskType: persistence.TransferTaskTypeRecordWorkflowStarted, isActive: false, expectedScope: metrics.TransferStandbyTaskRecordWorkflowStartedScope, }, { name: "TransferTaskTypeResetWorkflow - active", taskType: persistence.TransferTaskTypeResetWorkflow, isActive: true, expectedScope: metrics.TransferActiveTaskResetWorkflowScope, }, { name: "TransferTaskTypeResetWorkflow - standby", taskType: persistence.TransferTaskTypeResetWorkflow, isActive: false, expectedScope: metrics.TransferStandbyTaskResetWorkflowScope, }, { name: "TransferTaskTypeUpsertWorkflowSearchAttributes - active", taskType: persistence.TransferTaskTypeUpsertWorkflowSearchAttributes, isActive: true, expectedScope: metrics.TransferActiveTaskUpsertWorkflowSearchAttributesScope, }, { name: "TransferTaskTypeUpsertWorkflowSearchAttributes - standby", taskType: persistence.TransferTaskTypeUpsertWorkflowSearchAttributes, isActive: false, expectedScope: metrics.TransferStandbyTaskUpsertWorkflowSearchAttributesScope, }, { name: "TransferTaskTypeRecordWorkflowClosed - active", taskType: persistence.TransferTaskTypeRecordWorkflowClosed, isActive: true, expectedScope: metrics.TransferActiveTaskRecordWorkflowClosedScope, }, { name: "TransferTaskTypeRecordWorkflowClosed - standby", taskType: persistence.TransferTaskTypeRecordWorkflowClosed, isActive: false, expectedScope: metrics.TransferStandbyTaskRecordWorkflowClosedScope, }, { name: "TransferTaskTypeRecordChildExecutionCompleted - active", taskType: persistence.TransferTaskTypeRecordChildExecutionCompleted, isActive: true, expectedScope: metrics.TransferActiveTaskRecordChildExecutionCompletedScope, }, { name: "TransferTaskTypeRecordChildExecutionCompleted - standby", taskType: persistence.TransferTaskTypeRecordChildExecutionCompleted, isActive: false, expectedScope: metrics.TransferStandbyTaskRecordChildExecutionCompletedScope, }, { name: "TransferTaskTypeApplyParentClosePolicy - active", taskType: persistence.TransferTaskTypeApplyParentClosePolicy, isActive: true, expectedScope: metrics.TransferActiveTaskApplyParentClosePolicyScope, }, { name: "TransferTaskTypeApplyParentClosePolicy - standby", taskType: persistence.TransferTaskTypeApplyParentClosePolicy, isActive: false, expectedScope: metrics.TransferStandbyTaskApplyParentClosePolicyScope, }, { name: "TransferTaskType not caught - active", taskType: -100, isActive: true, expectedScope: metrics.TransferActiveQueueProcessorScope, }, { name: "TransferTaskType not caught - standby", taskType: -100, isActive: false, expectedScope: metrics.TransferStandbyQueueProcessorScope, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { scope := GetTransferTaskMetricsScope(tc.taskType, tc.isActive) assert.Equal(t, tc.expectedScope, scope) }) } } func TestGetTimerTaskMetricScope(t *testing.T) { testCases := []struct { name string taskType int isActive bool expectedScope metrics.ScopeIdx }{ { name: "TimerTaskTypeDecisionTimeout - active", taskType: persistence.TaskTypeDecisionTimeout, isActive: true, expectedScope: metrics.TimerActiveTaskDecisionTimeoutScope, }, { name: "TimerTaskTypeDecisionTimeout - standby", taskType: persistence.TaskTypeDecisionTimeout, isActive: false, expectedScope: metrics.TimerStandbyTaskDecisionTimeoutScope, }, { name: "TimerTaskTypeActivityTimeout - active", taskType: persistence.TaskTypeActivityTimeout, isActive: true, expectedScope: metrics.TimerActiveTaskActivityTimeoutScope, }, { name: "TimerTaskTypeActivityTimeout - standby", taskType: persistence.TaskTypeActivityTimeout, isActive: false, expectedScope: metrics.TimerStandbyTaskActivityTimeoutScope, }, { name: "TimerTaskTypeUserTimer - active", taskType: persistence.TaskTypeUserTimer, isActive: true, expectedScope: metrics.TimerActiveTaskUserTimerScope, }, { name: "TimerTaskTypeUserTimer - standby", taskType: persistence.TaskTypeUserTimer, isActive: false, expectedScope: metrics.TimerStandbyTaskUserTimerScope, }, { name: "TimerTaskTypeWorkflowTimeout - active", taskType: persistence.TaskTypeWorkflowTimeout, isActive: true, expectedScope: metrics.TimerActiveTaskWorkflowTimeoutScope, }, { name: "TimerTaskTypeWorkflowTimeout - standby", taskType: persistence.TaskTypeWorkflowTimeout, isActive: false, expectedScope: metrics.TimerStandbyTaskWorkflowTimeoutScope, }, { name: "TimerTaskTypeActivityRetryTimer - active", taskType: persistence.TaskTypeActivityRetryTimer, isActive: true, expectedScope: metrics.TimerActiveTaskActivityRetryTimerScope, }, { name: "TimerTaskTypeActivityRetryTimer - standby", taskType: persistence.TaskTypeActivityRetryTimer, isActive: false, expectedScope: metrics.TimerStandbyTaskActivityRetryTimerScope, }, { name: "TimerTaskTypeWorkflowBackoffTimer - active", taskType: persistence.TaskTypeWorkflowBackoffTimer, isActive: true, expectedScope: metrics.TimerActiveTaskWorkflowBackoffTimerScope, }, { name: "TimerTaskTypeWorkflowBackoffTimer - standby", taskType: persistence.TaskTypeWorkflowBackoffTimer, isActive: false, expectedScope: metrics.TimerStandbyTaskWorkflowBackoffTimerScope, }, { name: "TimerTaskTypeDeleteHistoryEvent - active", taskType: persistence.TaskTypeDeleteHistoryEvent, isActive: true, expectedScope: metrics.TimerActiveTaskDeleteHistoryEventScope, }, { name: "TimerTaskTypeDeleteHistoryEvent - standby", taskType: persistence.TaskTypeDeleteHistoryEvent, isActive: false, expectedScope: metrics.TimerStandbyTaskDeleteHistoryEventScope, }, { name: "TimerTaskType not caught - active", taskType: -100, isActive: true, expectedScope: metrics.TimerActiveQueueProcessorScope, }, { name: "TimerTaskType not caught - standby", taskType: -100, isActive: false, expectedScope: metrics.TimerStandbyQueueProcessorScope, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { scope := GetTimerTaskMetricScope(tc.taskType, tc.isActive) assert.Equal(t, tc.expectedScope, scope) }) } } func Test_verifyTaskVersion(t *testing.T) { testCases := []struct { name string setupMock func(*shard.MockContext, *cache.MockDomainCache) version int64 err error response bool }{ { name: "error - could not get domain entry", setupMock: func(s *shard.MockContext, c *cache.MockDomainCache) { c.EXPECT().GetDomainByID(constants.TestDomainID).Return(nil, errors.New("test error")).Times(1) s.EXPECT().GetDomainCache().Return(c).Times(1) }, err: errors.New("test error"), response: false, }, { name: "true - domain is not global", setupMock: func(s *shard.MockContext, c *cache.MockDomainCache) { domainResponse := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName}, &persistence.DomainConfig{}, false, nil, 0, nil, 0, 0, 0) c.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainResponse, nil).Times(1) s.EXPECT().GetDomainCache().Return(c).Times(1) }, err: nil, response: true, }, { name: "false - version is different from task version", setupMock: func(s *shard.MockContext, c *cache.MockDomainCache) { domainResponse := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName}, &persistence.DomainConfig{}, true, nil, 0, nil, 0, 0, 0) c.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainResponse, nil).Times(1) s.EXPECT().GetDomainCache().Return(c).Times(1) }, version: 6, err: nil, response: false, }, { name: "true - version is same as task version", setupMock: func(s *shard.MockContext, c *cache.MockDomainCache) { domainResponse := cache.NewDomainCacheEntryForTest(&persistence.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName}, &persistence.DomainConfig{}, true, nil, 0, nil, 0, 0, 0) c.EXPECT().GetDomainByID(constants.TestDomainID).Return(domainResponse, nil).Times(1) s.EXPECT().GetDomainCache().Return(c).Times(1) }, version: 50, err: nil, response: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) s := shard.NewMockContext(ctrl) l := log.NewNoop() task := &persistence.UserTimerTask{ TaskData: persistence.TaskData{ Version: constants.TestVersion, }, } mockDomainCache := cache.NewMockDomainCache(ctrl) tc.setupMock(s, mockDomainCache) ok, err := verifyTaskVersion(s, l, constants.TestDomainID, tc.version, task.Version, task) assert.Equal(t, tc.err, err) assert.Equal(t, tc.response, ok) }) } } func Test_loadMutableState(t *testing.T) { testCases := []struct { name string setupMock func(*execution.MockContext, *execution.MockMutableState) task persistence.Task eventID int64 err error isMutableStateReturned bool }{ { name: "error - failed to load workflow execution - entity does not exist", setupMock: func(w *execution.MockContext, m *execution.MockMutableState) { w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(nil, &types.EntityNotExistsError{}).Times(1) }, err: nil, }, { name: "error - failed to load workflow execution - another error", setupMock: func(w *execution.MockContext, m *execution.MockMutableState) { w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(nil, errors.New("other-error")).Times(1) }, err: errors.New("other-error"), }, { name: "error - transfer task scheduleID > next eventID and !isDecisionRetry - error loading workflow execution", setupMock: func(w *execution.MockContext, m *execution.MockMutableState) { gomock.InOrder( w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(m, nil).Times(1), m.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{NextEventID: int64(5), DecisionAttempt: 1, DecisionScheduleID: 1}).Times(1), m.EXPECT().GetNextEventID().Return(int64(10)).Times(1), w.EXPECT().Clear().Times(1), w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(nil, errors.New("some-other-error")).Times(1), ) }, task: &persistence.DecisionTask{ ScheduleID: 11, }, eventID: 11, err: errors.New("some-other-error"), }, { name: "no mutable state returned - transfer task scheduleID > next eventID after refresh", setupMock: func(w *execution.MockContext, m *execution.MockMutableState) { gomock.InOrder( w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(m, nil).Times(1), m.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{NextEventID: int64(5), DecisionAttempt: 1, DecisionScheduleID: 1}).Times(1), m.EXPECT().GetNextEventID().Return(int64(10)).Times(1), w.EXPECT().Clear().Times(1), w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(m, nil).Times(1), m.EXPECT().GetNextEventID().Return(int64(10)).Times(1), m.EXPECT().GetDomainEntry().Return(constants.TestGlobalDomainEntry).Times(1), m.EXPECT().GetNextEventID().Return(int64(10)).Times(1), ) }, task: &persistence.DecisionTask{ ScheduleID: 11, }, eventID: 11, err: nil, }, { name: "success - transfer task scheduleID < next eventID", setupMock: func(w *execution.MockContext, m *execution.MockMutableState) { w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(m, nil).Times(1) m.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{NextEventID: int64(5), DecisionAttempt: 1, DecisionScheduleID: 1}).Times(1) m.EXPECT().GetNextEventID().Return(int64(10)).Times(1) }, task: &persistence.DecisionTask{ ScheduleID: 3, }, eventID: 3, isMutableStateReturned: true, }, { name: "error - timer task eventID > next eventID and !isDecisionRetry - error loading workflow execution", setupMock: func(w *execution.MockContext, m *execution.MockMutableState) { gomock.InOrder( w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(m, nil).Times(1), m.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{NextEventID: int64(5), DecisionAttempt: 1, DecisionScheduleID: 1}).Times(1), m.EXPECT().GetNextEventID().Return(int64(10)).Times(1), w.EXPECT().Clear().Times(1), w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(nil, errors.New("some-other-error")).Times(1), ) }, task: &persistence.DecisionTimeoutTask{}, eventID: 11, err: errors.New("some-other-error"), }, { name: "no mutable state returned - timer task eventID > next eventID after refresh", setupMock: func(w *execution.MockContext, m *execution.MockMutableState) { gomock.InOrder( w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(m, nil).Times(1), m.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{NextEventID: int64(5), DecisionAttempt: 1, DecisionScheduleID: 1}).Times(1), m.EXPECT().GetNextEventID().Return(int64(10)).Times(1), w.EXPECT().Clear().Times(1), w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(m, nil).Times(1), m.EXPECT().GetNextEventID().Return(int64(10)).Times(1), m.EXPECT().GetDomainEntry().Return(constants.TestGlobalDomainEntry).Times(1), m.EXPECT().GetNextEventID().Return(int64(10)).Times(1), ) }, task: &persistence.DecisionTimeoutTask{}, eventID: 11, err: nil, }, { name: "success - timer task eventID < next eventID", setupMock: func(w *execution.MockContext, m *execution.MockMutableState) { w.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(m, nil).Times(1) m.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{NextEventID: int64(5), DecisionAttempt: 1, DecisionScheduleID: 1}).Times(1) m.EXPECT().GetNextEventID().Return(int64(10)).Times(1) }, task: &persistence.DecisionTimeoutTask{}, eventID: 3, isMutableStateReturned: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) w := execution.NewMockContext(ctrl) m := execution.NewMockMutableState(ctrl) metricsScope := metrics.NoopScope l := log.NewNoop() tc.setupMock(w, m) ms, err := loadMutableState(context.Background(), w, tc.task, metricsScope, l, tc.eventID) assert.Equal(t, tc.err, err) if tc.err != nil { assert.Nil(t, ms) } else if tc.isMutableStateReturned { assert.Equal(t, m, ms) } else { assert.Nil(t, ms) } }) } } func Test_timeoutWorkflow(t *testing.T) { eventBatchFirstEventID := int64(2) testCases := []struct { name string setupMock func(*execution.MockMutableState) err error }{ { name: "error - execution failDecision error", setupMock: func(m *execution.MockMutableState) { decisionInfo := &execution.DecisionInfo{ ScheduleID: 1, StartedID: 2, } m.EXPECT().GetInFlightDecision().Return(decisionInfo, true).Times(1) m.EXPECT().AddDecisionTaskFailedEvent( decisionInfo.ScheduleID, decisionInfo.StartedID, types.DecisionTaskFailedCauseForceCloseDecision, nil, execution.IdentityHistoryService, "", "", "", "", int64(0), "").Return(nil, errors.New("failDecision-error")).Times(1) }, err: errors.New("failDecision-error"), }, { name: "failDecision and timeout added to mutable state", setupMock: func(m *execution.MockMutableState) { decisionInfo := &execution.DecisionInfo{ ScheduleID: 1, StartedID: 2, } m.EXPECT().GetInFlightDecision().Return(decisionInfo, true).Times(1) m.EXPECT().AddDecisionTaskFailedEvent( decisionInfo.ScheduleID, decisionInfo.StartedID, types.DecisionTaskFailedCauseForceCloseDecision, nil, execution.IdentityHistoryService, "", "", "", "", int64(0), "").Return(nil, nil).Times(1) m.EXPECT().FlushBufferedEvents().Return(nil).Times(1) m.EXPECT().AddTimeoutWorkflowEvent(eventBatchFirstEventID).Return(nil, nil).Times(1) }, err: nil, }, { name: "error - AddTimeoutWorkflowEvent error", setupMock: func(m *execution.MockMutableState) { decisionInfo := &execution.DecisionInfo{ ScheduleID: 1, StartedID: 2, } m.EXPECT().GetInFlightDecision().Return(decisionInfo, true).Times(1) m.EXPECT().AddDecisionTaskFailedEvent( decisionInfo.ScheduleID, decisionInfo.StartedID, types.DecisionTaskFailedCauseForceCloseDecision, nil, execution.IdentityHistoryService, "", "", "", "", int64(0), "").Return(nil, nil).Times(1) m.EXPECT().FlushBufferedEvents().Return(nil).Times(1) m.EXPECT().AddTimeoutWorkflowEvent(eventBatchFirstEventID).Return(nil, errors.New("timeoutWorkflow-error")).Times(1) }, err: errors.New("timeoutWorkflow-error"), }, { name: "success - timeoutWorkflow event added", setupMock: func(m *execution.MockMutableState) { decisionInfo := &execution.DecisionInfo{ ScheduleID: 1, StartedID: 2, } m.EXPECT().GetInFlightDecision().Return(decisionInfo, true).Times(1) m.EXPECT().AddDecisionTaskFailedEvent( decisionInfo.ScheduleID, decisionInfo.StartedID, types.DecisionTaskFailedCauseForceCloseDecision, nil, execution.IdentityHistoryService, "", "", "", "", int64(0), "").Return(nil, nil).Times(1) m.EXPECT().FlushBufferedEvents().Return(nil).Times(1) m.EXPECT().AddTimeoutWorkflowEvent(eventBatchFirstEventID).Return(nil, nil).Times(1) }, err: nil, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) m := execution.NewMockMutableState(ctrl) tc.setupMock(m) err := timeoutWorkflow(m, eventBatchFirstEventID) assert.Equal(t, tc.err, err) }) } } func Test_retryWorkflow(t *testing.T) { eventBatchFirstEventID := int64(2) parentDomainName := "parent-domain-name" continueAsNewAttributes := &types.ContinueAsNewWorkflowExecutionDecisionAttributes{} testCases := []struct { name string setupMock func(*execution.MockMutableState) err error }{ { name: "error - execution failDecision error", setupMock: func(m *execution.MockMutableState) { decisionInfo := &execution.DecisionInfo{ ScheduleID: 1, StartedID: 2, } m.EXPECT().GetInFlightDecision().Return(decisionInfo, true).Times(1) m.EXPECT().AddDecisionTaskFailedEvent( decisionInfo.ScheduleID, decisionInfo.StartedID, types.DecisionTaskFailedCauseForceCloseDecision, nil, execution.IdentityHistoryService, "", "", "", "", int64(0), "").Return(nil, errors.New("failDecision-error")).Times(1) }, err: errors.New("failDecision-error"), }, { name: "failDecision and continueAsNew added to mutable state", setupMock: func(m *execution.MockMutableState) { decisionInfo := &execution.DecisionInfo{ ScheduleID: 1, StartedID: 2, } m.EXPECT().GetInFlightDecision().Return(decisionInfo, true).Times(1) m.EXPECT().AddDecisionTaskFailedEvent( decisionInfo.ScheduleID, decisionInfo.StartedID, types.DecisionTaskFailedCauseForceCloseDecision, nil, execution.IdentityHistoryService, "", "", "", "", int64(0), "").Return(nil, nil).Times(1) m.EXPECT().FlushBufferedEvents().Return(nil).Times(1) m.EXPECT().AddContinueAsNewEvent( gomock.Any(), eventBatchFirstEventID, commonconstants.EmptyEventID, parentDomainName, continueAsNewAttributes, ).Return(nil, m, nil).Times(1) }, err: nil, }, { name: "error - AddContinueAsNewEvent error", setupMock: func(m *execution.MockMutableState) { decisionInfo := &execution.DecisionInfo{ ScheduleID: 1, StartedID: 2, } m.EXPECT().GetInFlightDecision().Return(decisionInfo, true).Times(1) m.EXPECT().AddDecisionTaskFailedEvent( decisionInfo.ScheduleID, decisionInfo.StartedID, types.DecisionTaskFailedCauseForceCloseDecision, nil, execution.IdentityHistoryService, "", "", "", "", int64(0), "").Return(nil, nil).Times(1) m.EXPECT().FlushBufferedEvents().Return(nil).Times(1) m.EXPECT().AddContinueAsNewEvent( gomock.Any(), eventBatchFirstEventID, commonconstants.EmptyEventID, parentDomainName, continueAsNewAttributes, ).Return(nil, nil, errors.New("continueAsNew-error")).Times(1) }, err: errors.New("continueAsNew-error"), }, { name: "success - continueAsNew event added", setupMock: func(m *execution.MockMutableState) { decisionInfo := &execution.DecisionInfo{ ScheduleID: 1, StartedID: 2, } m.EXPECT().GetInFlightDecision().Return(decisionInfo, true).Times(1) m.EXPECT().AddDecisionTaskFailedEvent( decisionInfo.ScheduleID, decisionInfo.StartedID, types.DecisionTaskFailedCauseForceCloseDecision, nil, execution.IdentityHistoryService, "", "", "", "", int64(0), "").Return(nil, nil).Times(1) m.EXPECT().FlushBufferedEvents().Return(nil).Times(1) m.EXPECT().AddContinueAsNewEvent( gomock.Any(), eventBatchFirstEventID, commonconstants.EmptyEventID, parentDomainName, continueAsNewAttributes, ).Return(nil, m, nil).Times(1) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) m := execution.NewMockMutableState(ctrl) ctx := context.Background() tc.setupMock(m) resp, err := retryWorkflow(ctx, m, eventBatchFirstEventID, "parent-domain-name", continueAsNewAttributes) assert.Equal(t, tc.err, err) if tc.err == nil { assert.Equal(t, m, resp) } }) } } func Test_mocks(t *testing.T) { ctrl := gomock.NewController(t) mockTask := NewMockTask(ctrl) resp := NewMockTaskMatcher(mockTask) assert.NotNil(t, resp) assert.Equal(t, &mockTaskMatcher{task: mockTask}, resp) matches := resp.Matches(mockTask) assert.True(t, matches) matches = resp.Matches(nil) assert.False(t, matches) assert.Equal(t, fmt.Sprintf("is equal to %v", mockTask), resp.String()) } func TestShouldPushToMatching(t *testing.T) { domainID := "domainID" wfid := "wfid" rid := "rid" currentClusterName := "currentCluster" tests := []struct { name string setupMock func(mockDomainCache *cache.MockDomainCache, mockActiveClusterMgr *activecluster.MockManager) expectedResult bool expectedError string }{ { name: "failed to get domain", setupMock: func(mockDomainCache *cache.MockDomainCache, mockActiveClusterMgr *activecluster.MockManager) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(nil, errors.New("failed to get domain")) }, expectedResult: false, expectedError: "failed to get domain", }, { name: "not an active-active domain so returns true", setupMock: func(mockDomainCache *cache.MockDomainCache, mockActiveClusterMgr *activecluster.MockManager) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(getDomainCacheEntry(true, false), nil) }, expectedResult: true, }, { name: "active-active domain and workflow is active in current cluster so returns true", setupMock: func(mockDomainCache *cache.MockDomainCache, mockActiveClusterMgr *activecluster.MockManager) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(getDomainCacheEntry(true, true), nil) mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), domainID, wfid, rid). Return(&types.ActiveClusterInfo{ ActiveClusterName: currentClusterName, }, nil) }, expectedResult: true, }, { name: "active-active domain and workflow is not active in current cluster so returns false", setupMock: func(mockDomainCache *cache.MockDomainCache, mockActiveClusterMgr *activecluster.MockManager) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(getDomainCacheEntry(true, true), nil) mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), domainID, wfid, rid). Return(&types.ActiveClusterInfo{ ActiveClusterName: "otherCluster", }, nil) }, expectedResult: false, }, { name: "active-active domain - failed to lookup workflow", setupMock: func(mockDomainCache *cache.MockDomainCache, mockActiveClusterMgr *activecluster.MockManager) { mockDomainCache.EXPECT().GetDomainByID(domainID).Return(getDomainCacheEntry(true, true), nil) mockActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), domainID, wfid, rid). Return(nil, errors.New("failed to lookup workflow")) }, expectedResult: false, expectedError: "failed to lookup workflow", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctrl := gomock.NewController(t) shard := shard.NewMockContext(ctrl) domainCache := cache.NewMockDomainCache(ctrl) shard.EXPECT().GetClusterMetadata().Return(cluster.NewMetadata( config.ClusterGroupMetadata{ CurrentClusterName: currentClusterName, }, func(d string) bool { return false }, metrics.NewNoopMetricsClient(), log.NewNoop(), )).AnyTimes() shard.EXPECT().GetDomainCache().Return(domainCache) activeClusterMgr := activecluster.NewMockManager(ctrl) shard.EXPECT().GetActiveClusterManager().Return(activeClusterMgr).AnyTimes() test.setupMock(domainCache, activeClusterMgr) mockTask := NewMockTask(ctrl) mockTask.EXPECT().GetDomainID().Return(domainID).AnyTimes() mockTask.EXPECT().GetWorkflowID().Return(wfid).AnyTimes() mockTask.EXPECT().GetRunID().Return(rid).AnyTimes() result, err := shouldPushToMatching(context.Background(), shard, mockTask) assert.Equal(t, test.expectedResult, result) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } func getDomainCacheEntry(isGlobal, isActiveActive bool) *cache.DomainCacheEntry { activeClusters := &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 1, }, }, }, }, } if !isActiveActive { activeClusters = nil } return cache.NewDomainCacheEntryForTest( nil, nil, isGlobal, &persistence.DomainReplicationConfig{ ActiveClusters: activeClusters, }, 1, nil, 1, 1, 1, ) } ================================================ FILE: service/history/task/timer_active_task_executor.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "fmt" "time" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/simulation" "github.com/uber/cadence/service/worker/archiver" ) const ( scanWorkflowTimeout = 30 * time.Second ) var ( normalDecisionTypeTag = metrics.DecisionTypeTag("normal") stickyDecisionTypeTag = metrics.DecisionTypeTag("sticky") ) type ( timerActiveTaskExecutor struct { *timerTaskExecutorBase } ) // NewTimerActiveTaskExecutor creates a new task executor for active timer task func NewTimerActiveTaskExecutor( shard shard.Context, archiverClient archiver.Client, executionCache execution.Cache, logger log.Logger, metricsClient metrics.Client, config *config.Config, ) Executor { return &timerActiveTaskExecutor{ timerTaskExecutorBase: newTimerTaskExecutorBase( shard, archiverClient, executionCache, logger, metricsClient, config, ), } } func (t *timerActiveTaskExecutor) Execute(task Task) (ExecuteResponse, error) { simulation.LogEvents(simulation.E{ EventName: simulation.EventNameExecuteHistoryTask, Host: t.shard.GetConfig().HostName, ShardID: t.shard.GetShardID(), DomainID: task.GetDomainID(), WorkflowID: task.GetWorkflowID(), RunID: task.GetRunID(), Payload: map[string]any{ "task_category": persistence.HistoryTaskCategoryTimer.Name(), "task_type": task.GetTaskType(), "task_key": task.GetTaskKey(), }, }) scope := getOrCreateDomainTaggedScope(t.shard, GetTimerTaskMetricScope(task.GetTaskType(), true), task.GetDomainID(), t.logger) executeResponse := ExecuteResponse{ Scope: scope, IsActiveTask: true, } switch timerTask := task.GetInfo().(type) { case *persistence.UserTimerTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeUserTimerTimeoutTask(ctx, timerTask) case *persistence.ActivityTimeoutTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeActivityTimeoutTask(ctx, timerTask) case *persistence.DecisionTimeoutTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeDecisionTimeoutTask(ctx, timerTask) case *persistence.WorkflowTimeoutTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeWorkflowTimeoutTask(ctx, timerTask) case *persistence.ActivityRetryTimerTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeActivityRetryTimerTask(ctx, timerTask) case *persistence.WorkflowBackoffTimerTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeWorkflowBackoffTimerTask(ctx, timerTask) case *persistence.DeleteHistoryEventTask: ctx, cancel := context.WithTimeout(t.ctx, time.Duration(t.config.DeleteHistoryEventContextTimeout())*time.Second) defer cancel() return executeResponse, t.executeDeleteHistoryEventTask(ctx, timerTask) default: return executeResponse, errUnknownTimerTask } } func (t *timerActiveTaskExecutor) executeUserTimerTimeoutTask( ctx context.Context, task *persistence.UserTimerTask, ) (retError error) { t.logger.Debug("Processing user timer", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TaskID(task.TaskID), ) wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TimerQueueProcessorScope), t.logger, task.EventID) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } timerSequence := execution.NewTimerSequence(mutableState) referenceTime := t.shard.GetTimeSource().Now() resurrectionCheckMinDelay := t.config.ResurrectionCheckMinDelay(mutableState.GetDomainEntry().GetInfo().Name) updateMutableState := false debugLog := t.logger.Debug if t.config.EnableDebugMode && t.config.EnableTimerDebugLogByDomainID(task.DomainID) { debugLog = t.logger.Info } // initialized when a timer with delay >= resurrectionCheckMinDelay // is encountered, so that we don't need to scan history multiple times // where there're multiple timers with high delay var resurrectedTimer map[string]struct{} scanWorkflowCtx, cancel := context.WithTimeout(t.ctx, scanWorkflowTimeout) defer cancel() sortedUserTimers := timerSequence.LoadAndSortUserTimers() debugLog("Sorted user timers", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.Counter(len(sortedUserTimers)), ) Loop: for _, timerSequenceID := range sortedUserTimers { timerInfo, ok := mutableState.GetUserTimerInfoByEventID(timerSequenceID.EventID) if !ok { errString := fmt.Sprintf("failed to find in user timer event ID: %v", timerSequenceID.EventID) t.logger.Error(errString) return &types.InternalServiceError{Message: errString} } delay, expired := timerSequence.IsExpired(referenceTime, timerSequenceID) debugLog("Processing user timer sequence id", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TaskID(task.TaskID), tag.WorkflowTimerID(timerInfo.TimerID), tag.WorkflowScheduleID(timerInfo.StartedID), tag.Dynamic("timer-sequence-id", timerSequenceID), tag.Dynamic("timer-info", timerInfo), tag.Dynamic("delay", delay), tag.Dynamic("expired", expired), ) if !expired { // timer sequence IDs are sorted, once there is one timer // sequence ID not expired, all after that wil not expired break Loop } if delay >= resurrectionCheckMinDelay || resurrectedTimer != nil { if resurrectedTimer == nil { // overwrite the context here as scan history may take a long time to complete // ctx will also be used by other operations like updateWorkflow ctx = scanWorkflowCtx resurrectedTimer, err = execution.GetResurrectedTimers(ctx, t.shard, mutableState) if err != nil { t.logger.Error("Timer resurrection check failed", tag.Error(err)) return err } } if _, ok := resurrectedTimer[timerInfo.TimerID]; ok { // found timer resurrection domainName := mutableState.GetDomainEntry().GetInfo().Name t.metricsClient.Scope(metrics.TimerQueueProcessorScope, metrics.DomainTag(domainName)).IncCounter(metrics.TimerResurrectionCounter) t.logger.Warn("Encounter resurrected timer, skip", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TaskID(task.TaskID), tag.WorkflowTimerID(timerInfo.TimerID), tag.WorkflowScheduleID(timerInfo.StartedID), // timerStartedEvent is basically scheduled event ) // remove resurrected timer from mutable state if err := mutableState.DeleteUserTimer(timerInfo.TimerID); err != nil { return err } updateMutableState = true continue Loop } } if _, err := mutableState.AddTimerFiredEvent(timerInfo.TimerID); err != nil { return err } updateMutableState = true debugLog("User timer fired", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TaskID(task.TaskID), tag.WorkflowTimerID(timerInfo.TimerID), tag.WorkflowScheduleID(timerInfo.StartedID), tag.WorkflowNextEventID(mutableState.GetNextEventID()), ) } if !updateMutableState { return nil } return t.updateWorkflowExecution(ctx, wfContext, mutableState, updateMutableState) } func (t *timerActiveTaskExecutor) executeActivityTimeoutTask( ctx context.Context, task *persistence.ActivityTimeoutTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() domainName, err := t.shard.GetDomainCache().GetDomainName(task.DomainID) if err != nil { return fmt.Errorf("unable to find domainID: %v, err: %v", task.DomainID, err) } mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TimerQueueProcessorScope), t.logger, task.EventID) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } wfType := mutableState.GetWorkflowType() if wfType == nil { return fmt.Errorf("unable to find workflow type, task %v", task) } timerSequence := execution.NewTimerSequence(mutableState) referenceTime := t.shard.GetTimeSource().Now() resurrectionCheckMinDelay := t.config.ResurrectionCheckMinDelay(mutableState.GetDomainEntry().GetInfo().Name) updateMutableState := false scheduleDecision := false // initialized when an activity timer with delay >= resurrectionCheckMinDelay // is encountered, so that we don't need to scan history multiple times // where there're multiple timers with high delay var resurrectedActivity map[int64]struct{} scanWorkflowCtx, cancel := context.WithTimeout(t.ctx, scanWorkflowTimeout) defer cancel() // need to clear activity heartbeat timer task mask for new activity timer task creation // NOTE: LastHeartbeatTimeoutVisibilityInSeconds is for deduping heartbeat timer creation as it's possible // one heartbeat task was persisted multiple times with different taskIDs due to the retry logic // for updating workflow execution. In that case, only one new heartbeat timeout task should be // created. isHeartBeatTask := task.TimeoutType == int(types.TimeoutTypeHeartbeat) activityInfo, ok := mutableState.GetActivityInfo(task.EventID) if isHeartBeatTask && ok && activityInfo.LastHeartbeatTimeoutVisibilityInSeconds <= task.VisibilityTimestamp.Unix() { activityInfo.TimerTaskStatus = activityInfo.TimerTaskStatus &^ execution.TimerTaskStatusCreatedHeartbeat if err := mutableState.UpdateActivity(activityInfo); err != nil { return err } updateMutableState = true } Loop: for _, timerSequenceID := range timerSequence.LoadAndSortActivityTimers() { activityInfo, ok := mutableState.GetActivityInfo(timerSequenceID.EventID) if !ok || timerSequenceID.Attempt < activityInfo.Attempt { // handle 2 cases: // 1. !ok // this case can happen since each activity can have 4 timers // and one of those 4 timers may have fired in this loop // 2. timerSequenceID.attempt < activityInfo.Attempt // retry could update activity attempt, should not timeouts new attempt // 3. it's a resurrected activity and has already been deleted in this loop continue Loop } delay, expired := timerSequence.IsExpired(referenceTime, timerSequenceID) if !expired { // timer sequence IDs are sorted, once there is one timer // sequence ID not expired, all after that wil not expired break Loop } if delay >= resurrectionCheckMinDelay || resurrectedActivity != nil { if resurrectedActivity == nil { // overwrite the context here as scan history may take a long time to complete // ctx will also be used by other operations like updateWorkflow ctx = scanWorkflowCtx resurrectedActivity, err = execution.GetResurrectedActivities(ctx, t.shard, mutableState) if err != nil { t.logger.Error("Activity resurrection check failed", tag.Error(err)) return err } } if _, ok := resurrectedActivity[activityInfo.ScheduleID]; ok { // found activity resurrection domainName := mutableState.GetDomainEntry().GetInfo().Name t.metricsClient.Scope(metrics.TimerQueueProcessorScope, metrics.DomainTag(domainName)).IncCounter(metrics.ActivityResurrectionCounter) t.logger.Warn("Encounter resurrected activity, skip", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TaskType(task.GetTaskType()), tag.TaskID(task.TaskID), tag.WorkflowActivityID(activityInfo.ActivityID), tag.WorkflowScheduleID(activityInfo.ScheduleID), ) // remove resurrected activity from mutable state if err := mutableState.DeleteActivity(activityInfo.ScheduleID); err != nil { return err } updateMutableState = true continue Loop } } // check if it's possible that the timeout is due to activity task lost if timerSequenceID.TimerType == execution.TimerTypeScheduleToStart { domainName, err := t.shard.GetDomainCache().GetDomainName(mutableState.GetExecutionInfo().DomainID) if err == nil && activityInfo.ScheduleToStartTimeout >= int32(t.config.ActivityMaxScheduleToStartTimeoutForRetry(domainName).Seconds()) { // note that we ignore the race condition for the dynamic config value change here as it's only for metric and logging purpose. // theoratically the check only applies to activities with retry policy // however for activities without retry policy, we also want to check the potential task lost and emit the metric // so reuse the same config value as a threshold so that the metric only got emitted if the activity has been started after a long time. t.metricsClient.Scope(metrics.TimerActiveTaskActivityTimeoutScope, metrics.DomainTag(domainName)).IncCounter(metrics.ActivityLostCounter) t.logger.Warn("Potentially activity task lost", tag.WorkflowDomainName(domainName), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.WorkflowScheduleID(activityInfo.ScheduleID), ) } } if ok, err := mutableState.RetryActivity( activityInfo, execution.TimerTypeToReason(timerSequenceID.TimerType), nil, ); err != nil { return err } else if ok { updateMutableState = true continue Loop } t.emitTimeoutMetricScopeWithDomainTag( mutableState.GetExecutionInfo().DomainID, metrics.TimerActiveTaskActivityTimeoutScope, timerSequenceID.TimerType, metrics.WorkflowTypeTag(wfType.GetName()), ) t.logger.Info("Activity timed out", tag.WorkflowDomainName(domainName), tag.WorkflowDomainID(task.GetDomainID()), tag.WorkflowID(task.GetWorkflowID()), tag.WorkflowRunID(task.GetRunID()), tag.ScheduleAttempt(task.Attempt), tag.FailoverVersion(task.GetVersion()), tag.ActivityTimeoutType(shared.TimeoutType(timerSequenceID.TimerType)), ) if _, err := mutableState.AddActivityTaskTimedOutEvent( activityInfo.ScheduleID, activityInfo.StartedID, execution.TimerTypeToInternal(timerSequenceID.TimerType), activityInfo.Details, ); err != nil { return err } updateMutableState = true scheduleDecision = true } if !updateMutableState { return nil } return t.updateWorkflowExecution(ctx, wfContext, mutableState, scheduleDecision) } func (t *timerActiveTaskExecutor) executeDecisionTimeoutTask( ctx context.Context, task *persistence.DecisionTimeoutTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() domainName, err := t.shard.GetDomainCache().GetDomainName(task.DomainID) if err != nil { return fmt.Errorf("unable to find domainID: %v, err: %v", task.DomainID, err) } mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TimerQueueProcessorScope), t.logger, task.EventID) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } wfType := mutableState.GetWorkflowType() if wfType == nil { return fmt.Errorf("unable to find workflow type, task %v", task) } scheduleID := task.EventID decision, ok := mutableState.GetDecisionInfo(scheduleID) if !ok { t.logger.Debug("Potentially duplicate", tag.TaskID(task.TaskID), tag.WorkflowScheduleID(scheduleID), tag.TaskType(persistence.TaskTypeDecisionTimeout)) return nil } ok, err = verifyTaskVersion(t.shard, t.logger, task.DomainID, decision.Version, task.Version, task) if err != nil || !ok { return err } if decision.Attempt != task.ScheduleAttempt { return nil } scheduleDecision := false isStickyDecision := mutableState.GetExecutionInfo().StickyTaskList != "" decisionTypeTag := normalDecisionTypeTag if isStickyDecision { decisionTypeTag = stickyDecisionTypeTag } tags := []metrics.Tag{metrics.WorkflowTypeTag(wfType.GetName()), decisionTypeTag} switch execution.TimerTypeFromInternal(types.TimeoutType(task.TimeoutType)) { case execution.TimerTypeStartToClose: t.emitTimeoutMetricScopeWithDomainTag( mutableState.GetExecutionInfo().DomainID, metrics.TimerActiveTaskDecisionTimeoutScope, execution.TimerTypeStartToClose, tags..., ) if _, err := mutableState.AddDecisionTaskTimedOutEvent( decision.ScheduleID, decision.StartedID, ); err != nil { return err } maxAttempts := t.config.DecisionRetryMaxAttempts(domainName) enforceDecisionTaskAttempts := t.config.EnforceDecisionTaskAttempts(domainName) if enforceDecisionTaskAttempts && maxAttempts > 0 && mutableState.GetExecutionInfo().DecisionAttempt > int64(maxAttempts) { message := "Decision attempt exceeds limit. Last decision attempt failed due to start-to-close timeout." executionInfo := mutableState.GetExecutionInfo() t.logger.Error(message, tag.WorkflowDomainID(executionInfo.DomainID), tag.WorkflowID(executionInfo.WorkflowID), tag.WorkflowRunID(executionInfo.RunID)) t.metricsClient.IncCounter(metrics.TimerActiveTaskDecisionTimeoutScope, metrics.DecisionRetriesExceededCounter) if _, err := mutableState.AddWorkflowExecutionTerminatedEvent( mutableState.GetNextEventID(), common.FailureReasonDecisionAttemptsExceedsLimit, []byte(message), execution.IdentityHistoryService, ); err != nil { return err } } else { scheduleDecision = true } case execution.TimerTypeScheduleToStart: if decision.StartedID != constants.EmptyEventID { // decision has already started return nil } if !isStickyDecision { t.logger.Warn("Potential lost normal decision task", tag.WorkflowDomainName(domainName), tag.WorkflowDomainID(task.GetDomainID()), tag.WorkflowID(task.GetWorkflowID()), tag.WorkflowRunID(task.GetRunID()), tag.WorkflowScheduleID(scheduleID), tag.ScheduleAttempt(task.ScheduleAttempt), tag.FailoverVersion(task.GetVersion()), ) } t.emitTimeoutMetricScopeWithDomainTag( mutableState.GetExecutionInfo().DomainID, metrics.TimerActiveTaskDecisionTimeoutScope, execution.TimerTypeScheduleToStart, tags..., ) _, err := mutableState.AddDecisionTaskScheduleToStartTimeoutEvent(scheduleID) if err != nil { return err } scheduleDecision = true } return t.updateWorkflowExecution(ctx, wfContext, mutableState, scheduleDecision) } func (t *timerActiveTaskExecutor) executeWorkflowBackoffTimerTask( ctx context.Context, task *persistence.WorkflowBackoffTimerTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TimerQueueProcessorScope), t.logger, 0) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } if task.TimeoutType == persistence.WorkflowBackoffTimeoutTypeRetry { t.metricsClient.IncCounter(metrics.TimerActiveTaskWorkflowBackoffTimerScope, metrics.WorkflowRetryBackoffTimerCount) } else { t.metricsClient.IncCounter(metrics.TimerActiveTaskWorkflowBackoffTimerScope, metrics.WorkflowCronBackoffTimerCount) } if mutableState.HasProcessedOrPendingDecision() { // already has decision task return nil } // Check if this is a cron backoff for the first decision task // When the first decision is scheduled after the backoff, we need to trigger a visibility update // to change ExecutionStatus from PENDING to STARTED executionInfo := mutableState.GetExecutionInfo() isCronBackoff := task.TimeoutType == persistence.WorkflowBackoffTimeoutTypeCron isFirstDecision := executionInfo.DecisionScheduleID == constants.EmptyEventID if isCronBackoff && isFirstDecision { // Only create upsert task if advanced visibility is enabled // For basic DB visibility, the ExecutionStatus is already set to STARTED at workflow start // to avoid showing misleading PENDING status for running workflows. // With advanced visibility, we can update PENDING -> STARTED via upsert. if common.IsAdvancedVisibilityWritingEnabled( t.config.WriteVisibilityStoreName(), t.config.IsAdvancedVisConfigExist, ) { // Add UpsertWorkflowSearchAttributes task to trigger visibility update // This will update ExecutionStatus from PENDING to STARTED in visibility // The status is calculated dynamically in the upsert handler based on decision task state mutableState.AddTransferTasks(&persistence.UpsertWorkflowSearchAttributesTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: executionInfo.DomainID, WorkflowID: executionInfo.WorkflowID, RunID: executionInfo.RunID, }, TaskData: persistence.TaskData{ Version: mutableState.GetCurrentVersion(), }, }) } } // schedule first decision task return t.updateWorkflowExecution(ctx, wfContext, mutableState, true) } func (t *timerActiveTaskExecutor) executeActivityRetryTimerTask( ctx context.Context, task *persistence.ActivityRetryTimerTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TimerQueueProcessorScope), t.logger, task.EventID) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } // generate activity task scheduledID := task.EventID activityInfo, ok := mutableState.GetActivityInfo(scheduledID) if !ok || task.Attempt < int64(activityInfo.Attempt) || activityInfo.StartedID != constants.EmptyEventID { if ok { t.logger.Info("Duplicate activity retry timer task", tag.WorkflowID(mutableState.GetExecutionInfo().WorkflowID), tag.WorkflowRunID(mutableState.GetExecutionInfo().RunID), tag.WorkflowDomainID(mutableState.GetExecutionInfo().DomainID), tag.WorkflowScheduleID(activityInfo.ScheduleID), tag.Attempt(activityInfo.Attempt), tag.FailoverVersion(activityInfo.Version), tag.TimerTaskStatus(activityInfo.TimerTaskStatus), tag.ScheduleAttempt(task.Attempt)) } return nil } ok, err = verifyTaskVersion(t.shard, t.logger, task.DomainID, activityInfo.Version, task.Version, task) if err != nil || !ok { return err } domainID := task.DomainID targetDomainID := domainID if activityInfo.DomainID != "" { targetDomainID = activityInfo.DomainID } else { // TODO remove this block after Mar, 1th, 2020 // previously, DomainID in activity info is not used, so need to get // schedule event from DB checking whether activity to be scheduled // belongs to this domain scheduledEvent, err := mutableState.GetActivityScheduledEvent(ctx, scheduledID) if err != nil { return err } if scheduledEvent.ActivityTaskScheduledEventAttributes.GetDomain() != "" { domainEntry, err := t.shard.GetDomainCache().GetDomain(scheduledEvent.ActivityTaskScheduledEventAttributes.GetDomain()) if err != nil { return &types.InternalServiceError{Message: "unable to re-schedule activity across domain."} } targetDomainID = domainEntry.GetInfo().ID } } execution := types.WorkflowExecution{ WorkflowID: task.WorkflowID, RunID: task.RunID} taskList := &types.TaskList{ Name: activityInfo.TaskList, } scheduleToStartTimeout := activityInfo.ScheduleToStartTimeout release(nil) // release earlier as we don't need the lock anymore shouldPush, err := shouldPushToMatching(ctx, t.shard, task) if err != nil { return err } if !shouldPush { return nil } _, err = t.shard.GetService().GetMatchingClient().AddActivityTask(ctx, &types.AddActivityTaskRequest{ DomainUUID: targetDomainID, SourceDomainUUID: domainID, Execution: &execution, TaskList: taskList, ScheduleID: scheduledID, ScheduleToStartTimeoutSeconds: common.Int32Ptr(scheduleToStartTimeout), PartitionConfig: mutableState.GetExecutionInfo().PartitionConfig, }) return err } func (t *timerActiveTaskExecutor) executeWorkflowTimeoutTask( ctx context.Context, task *persistence.WorkflowTimeoutTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TimerQueueProcessorScope), t.logger, 0) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } startVersion, err := mutableState.GetStartVersion() if err != nil { return err } ok, err := verifyTaskVersion(t.shard, t.logger, task.DomainID, startVersion, task.Version, task) if err != nil || !ok { return err } eventBatchFirstEventID := mutableState.GetNextEventID() timeoutReason := execution.TimerTypeToReason(execution.TimerTypeStartToClose) backoffInterval := mutableState.GetRetryBackoffDuration(timeoutReason) continueAsNewInitiator := types.ContinueAsNewInitiatorRetryPolicy if backoffInterval == backoff.NoBackoff { // check if a cron backoff is needed backoffInterval, err = mutableState.GetCronBackoffDuration(ctx) if err != nil { return err } continueAsNewInitiator = types.ContinueAsNewInitiatorCronSchedule } // ignore event id isCanceled, _ := mutableState.IsCancelRequested() if isCanceled || backoffInterval == backoff.NoBackoff { if err := timeoutWorkflow(mutableState, eventBatchFirstEventID); err != nil { return err } // We apply the update to execution using optimistic concurrency. If it fails due to a conflict than reload // the history and try the operation again. return t.updateWorkflowExecution(ctx, wfContext, mutableState, false) } // workflow timeout, but a retry or cron is needed, so we do continue as new to retry or cron startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return err } startAttributes := startEvent.WorkflowExecutionStartedEventAttributes continueAsNewAttributes := &types.ContinueAsNewWorkflowExecutionDecisionAttributes{ WorkflowType: startAttributes.WorkflowType, TaskList: startAttributes.TaskList, Input: startAttributes.Input, ExecutionStartToCloseTimeoutSeconds: startAttributes.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: startAttributes.TaskStartToCloseTimeoutSeconds, BackoffStartIntervalInSeconds: common.Int32Ptr(int32(backoffInterval.Seconds())), RetryPolicy: startAttributes.RetryPolicy, Initiator: continueAsNewInitiator.Ptr(), FailureReason: common.StringPtr(timeoutReason), CronSchedule: mutableState.GetExecutionInfo().CronSchedule, Header: startAttributes.Header, Memo: startAttributes.Memo, SearchAttributes: startAttributes.SearchAttributes, JitterStartSeconds: startAttributes.JitterStartSeconds, CronOverlapPolicy: startAttributes.CronOverlapPolicy, ActiveClusterSelectionPolicy: startAttributes.ActiveClusterSelectionPolicy, } newMutableState, err := retryWorkflow( ctx, mutableState, eventBatchFirstEventID, startAttributes.GetParentWorkflowDomain(), continueAsNewAttributes, ) if err != nil { return err } newExecutionInfo := newMutableState.GetExecutionInfo() return wfContext.UpdateWorkflowExecutionWithNewAsActive( ctx, t.shard.GetTimeSource().Now(), execution.NewContext( newExecutionInfo.DomainID, types.WorkflowExecution{ WorkflowID: newExecutionInfo.WorkflowID, RunID: newExecutionInfo.RunID, }, t.shard, t.shard.GetExecutionManager(), t.logger, ), newMutableState, ) } func (t *timerActiveTaskExecutor) updateWorkflowExecution( ctx context.Context, wfContext execution.Context, mutableState execution.MutableState, scheduleNewDecision bool, ) error { var err error if scheduleNewDecision { // Schedule a new decision. err = execution.ScheduleDecision(mutableState) if err != nil { return err } } now := t.shard.GetTimeSource().Now() t.logger.Debugf("timerActiveTaskExecutor.updateWorkflowExecution calling UpdateWorkflowExecutionAsActive for wfID %s", mutableState.GetExecutionInfo().WorkflowID, ) err = wfContext.UpdateWorkflowExecutionAsActive(ctx, now) if err != nil { // if is shard ownership error, the shard context will stop the entire history engine // we don't need to explicitly stop the queue processor here return err } return nil } func (t *timerActiveTaskExecutor) emitTimeoutMetricScopeWithDomainTag( domainID string, scope metrics.ScopeIdx, timerType execution.TimerType, tags ...metrics.Tag, ) { domainTag, err := getDomainTagByID(t.shard.GetDomainCache(), domainID) if err != nil { return } tags = append(tags, domainTag) metricsScope := t.metricsClient.Scope(scope, tags...) switch timerType { case execution.TimerTypeScheduleToStart: metricsScope.IncCounter(metrics.ScheduleToStartTimeoutCounter) case execution.TimerTypeScheduleToClose: metricsScope.IncCounter(metrics.ScheduleToCloseTimeoutCounter) case execution.TimerTypeStartToClose: metricsScope.IncCounter(metrics.StartToCloseTimeoutCounter) case execution.TimerTypeHeartbeat: metricsScope.IncCounter(metrics.HeartbeatTimeoutCounter) } } ================================================ FILE: service/history/task/timer_active_task_executor_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "bytes" "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" test "github.com/uber/cadence/service/history/testing" ) type ( timerActiveTaskExecutorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockEngine *engine.MockEngine mockDomainCache *cache.MockDomainCache mockMatchingClient *matching.MockClient mockExecutionMgr *mocks.ExecutionManager mockHistoryV2Mgr *mocks.HistoryV2Manager executionCache execution.Cache logger log.Logger domainID string domain string domainEntry *cache.DomainCacheEntry version int64 timeSource clock.MockedTimeSource timerActiveTaskExecutor *timerActiveTaskExecutor } ) func TestTimerActiveTaskExecutorSuite(t *testing.T) { s := new(timerActiveTaskExecutorSuite) suite.Run(t, s) } func (s *timerActiveTaskExecutorSuite) SetupSuite() { } func (s *timerActiveTaskExecutorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.domainID = constants.TestDomainID s.domain = constants.TestDomainName s.domainEntry = constants.TestGlobalDomainEntry s.version = s.domainEntry.GetFailoverVersion() s.timeSource = clock.NewMockedTimeSource() s.controller = gomock.NewController(s.T()) config := config.NewForTest() s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ RangeID: 1, TransferAckLevel: 0, }, config, ) s.mockShard.SetEventsCache(events.NewCache( s.mockShard.GetShardID(), s.mockShard.GetHistoryManager(), s.mockShard.GetConfig(), s.mockShard.GetLogger(), s.mockShard.GetMetricsClient(), s.mockShard.GetDomainCache(), )) s.mockShard.Resource.TimeSource = s.timeSource s.mockEngine = engine.NewMockEngine(s.controller) s.mockEngine.EXPECT().NotifyNewHistoryEvent(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewTransferTasks(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewTimerTasks(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewReplicationTasks(gomock.Any()).AnyTimes() s.mockShard.SetEngine(s.mockEngine) s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockMatchingClient = s.mockShard.Resource.MatchingClient s.mockExecutionMgr = s.mockShard.Resource.ExecutionMgr s.mockHistoryV2Mgr = s.mockShard.Resource.HistoryMgr // ack manager will use the domain information s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(constants.TestGlobalDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(constants.TestDomainName, nil).AnyTimes() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestGlobalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestGlobalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(testActiveClusterInfo, nil).AnyTimes() s.logger = s.mockShard.GetLogger() s.executionCache = execution.NewCache(s.mockShard) s.timerActiveTaskExecutor = NewTimerActiveTaskExecutor( s.mockShard, nil, s.executionCache, s.logger, s.mockShard.GetMetricsClient(), config, ).(*timerActiveTaskExecutor) } func (s *timerActiveTaskExecutorSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *timerActiveTaskExecutorSuite) TestProcessUserTimerTimeout_Fire() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerID := "timer" timerTimeout := 2 * time.Second event, _ := test.AddTimerStartedEvent(mutableState, decisionCompletionID, timerID, int64(timerTimeout.Seconds())) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextUserTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.timeSource.Advance(2 * timerTimeout) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) _, ok := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).GetUserTimerInfo(timerID) s.False(ok) } func (s *timerActiveTaskExecutorSuite) TestProcessUserTimerTimeout_Noop() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerID := "timer" timerTimeout := 2 * time.Second event, _ := test.AddTimerStartedEvent(mutableState, decisionCompletionID, timerID, int64(timerTimeout.Seconds())) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextUserTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) event = test.AddTimerFiredEvent(mutableState, timerID) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.timeSource.Advance(2 * timerTimeout) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestProcessUserTimerTimeout_Resurrected() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) // schedule two timers timerID1 := "timer1" timerTimeout1 := 2 * time.Second startEvent1, _ := test.AddTimerStartedEvent(mutableState, decisionCompletionID, timerID1, int64(timerTimeout1.Seconds())) timerID2 := "timer2" timerTimeout2 := 5 * time.Second startEvent2, _ := test.AddTimerStartedEvent(mutableState, decisionCompletionID, timerID2, int64(timerTimeout2.Seconds())) // fire timer 1 firedEvent1 := test.AddTimerFiredEvent(mutableState, timerID1) mutableState.FlushBufferedEvents() // there should be a decision scheduled event after timer1 is fired // omitted here to make the test easier to read // create timer task for timer2 timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() // remove existing timer task for timerID1 modified, err := timerSequence.CreateNextUserTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, firedEvent1.ID, firedEvent1.Version) s.NoError(err) // add resurrected timer info for timer1 persistenceMutableState.TimerInfos[timerID1] = &persistence.TimerInfo{ Version: startEvent1.Version, TimerID: timerID1, ExpiryTime: time.Unix(0, startEvent1.GetTimestamp()).Add(timerTimeout1), StartedID: startEvent1.ID, TaskStatus: execution.TimerTaskStatusNone, } s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) nextPageToken := []byte{1, 2, 3} s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.MatchedBy(func(req *persistence.ReadHistoryBranchRequest) bool { return req.MinEventID == 1 && req.NextPageToken == nil })).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{startEvent1, startEvent2}, NextPageToken: nextPageToken, }, nil).Once() s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.MatchedBy(func(req *persistence.ReadHistoryBranchRequest) bool { return req.MinEventID == 1 && bytes.Equal(req.NextPageToken, nextPageToken) })).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{firedEvent1}, NextPageToken: nil, }, nil).Once() // only timer2 should be fired s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.MatchedBy(func(req *persistence.AppendHistoryNodesRequest) bool { numTimerFiredEvents := 0 for _, event := range req.Events { if event.GetEventType() == types.EventTypeTimerFired { numTimerFiredEvents++ } } return numTimerFiredEvents == 1 })).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() // both timerInfo should be deleted s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.UpdateWorkflowExecutionRequest) bool { return len(req.UpdateWorkflowMutation.DeleteTimerInfos) == 2 })).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.timerActiveTaskExecutor.config.ResurrectionCheckMinDelay = dynamicproperties.GetDurationPropertyFnFilteredByDomain(timerTimeout2 - timerTimeout1) s.timeSource.Advance(timerTimeout2) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestProcessActivityTimeout_NoRetryPolicy_Fire() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTimeout := 2 * time.Second scheduledEvent, _ := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), ) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, scheduledEvent.ID, scheduledEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.timeSource.Advance(2 * timerTimeout) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) _, ok := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).GetActivityInfo(scheduledEvent.ID) s.False(ok) } func (s *timerActiveTaskExecutorSuite) TestProcessActivityTimeout_NoRetryPolicy_Noop() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) identity := "identity" timerTimeout := 2 * time.Second scheduledEvent, _ := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), ) startedEvent := test.AddActivityTaskStartedEvent(mutableState, scheduledEvent.ID, identity) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) completeEvent := test.AddActivityTaskCompletedEvent(mutableState, scheduledEvent.ID, startedEvent.ID, []byte(nil), identity) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, completeEvent.ID, completeEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.timeSource.Advance(2 * timerTimeout) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestProcessActivityTimeout_RetryPolicy_Retry_StartToClose() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTimeout := 2 * time.Second scheduledEvent, _ := test.AddActivityTaskScheduledEventWithRetry( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1.2, MaximumIntervalInSeconds: 5, MaximumAttempts: 5, NonRetriableErrorReasons: []string{"(╯' - ')╯ ┻━┻ "}, ExpirationIntervalInSeconds: 999, }, ) startedEvent := test.AddActivityTaskStartedEvent(mutableState, scheduledEvent.ID, "identity") s.Nil(startedEvent) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, scheduledEvent.ID, scheduledEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.timeSource.Advance(2 * timerTimeout) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) activityInfo, ok := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).GetActivityInfo(scheduledEvent.ID) s.True(ok) s.Equal(scheduledEvent.ID, activityInfo.ScheduleID) s.Equal(commonconstants.EmptyEventID, activityInfo.StartedID) // only a schedule to start timer will be created, apart from the retry timer s.Equal(int32(execution.TimerTaskStatusCreatedScheduleToStart), activityInfo.TimerTaskStatus) } func (s *timerActiveTaskExecutorSuite) TestProcessActivityTimeout_RetryPolicy_Retry_ScheduleToStart() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTimeout := 2 * time.Second scheduledEvent, _ := test.AddActivityTaskScheduledEventWithRetry( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1.2, MaximumIntervalInSeconds: 5, MaximumAttempts: 5, NonRetriableErrorReasons: []string{"(╯' - ')╯ ┻━┻ "}, ExpirationIntervalInSeconds: 999, }, ) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, scheduledEvent.ID, scheduledEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.timeSource.Advance(2 * timerTimeout) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) activityInfo, ok := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).GetActivityInfo(scheduledEvent.ID) s.True(ok) s.Equal(scheduledEvent.ID, activityInfo.ScheduleID) s.Equal(commonconstants.EmptyEventID, activityInfo.StartedID) // only a schedule to start timer will be created, apart from the retry timer s.Equal(int32(execution.TimerTaskStatusCreatedScheduleToStart), activityInfo.TimerTaskStatus) } func (s *timerActiveTaskExecutorSuite) TestProcessActivityTimeout_RetryPolicy_Noop() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) identity := "identity" timerTimeout := 2 * time.Second scheduledEvent, _ := test.AddActivityTaskScheduledEventWithRetry( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1.2, MaximumIntervalInSeconds: 5, MaximumAttempts: 5, NonRetriableErrorReasons: []string{"(╯' - ')╯ ┻━┻ "}, ExpirationIntervalInSeconds: 999, }, ) startedEvent := test.AddActivityTaskStartedEvent(mutableState, scheduledEvent.ID, identity) s.Nil(startedEvent) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) completeEvent := test.AddActivityTaskCompletedEvent(mutableState, scheduledEvent.ID, commonconstants.TransientEventID, []byte(nil), identity) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, completeEvent.ID, completeEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.timeSource.Advance(2 * timerTimeout) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestProcessActivityTimeout_Heartbeat_Noop() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) identity := "identity" timerTimeout := 2 * time.Second heartbeatTimerTimeout := time.Second scheduledEvent, _ := test.AddActivityTaskScheduledEventWithRetry( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(heartbeatTimerTimeout.Seconds()), &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1.2, MaximumIntervalInSeconds: 5, MaximumAttempts: 5, NonRetriableErrorReasons: []string{"(╯' - ')╯ ┻━┻ "}, ExpirationIntervalInSeconds: 999, }, ) startedEvent := test.AddActivityTaskStartedEvent(mutableState, scheduledEvent.ID, identity) s.Nil(startedEvent) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] s.Equal(int(execution.TimerTypeHeartbeat), task.(*persistence.ActivityTimeoutTask).TimeoutType) task.SetVisibilityTimestamp(task.GetVisibilityTimestamp().Add(-time.Second)) timerTask := s.newTimerTaskFromInfo(task) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, scheduledEvent.ID, scheduledEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestProcessActivityTimeout_Resurrected() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) identity := "identity" timerTimeout1 := 2 * time.Second timerTimeout2 := 5 * time.Second // schedule 2 activities scheduledEvent1, _ := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity1", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout1.Seconds()), int32(timerTimeout1.Seconds()), int32(timerTimeout1.Seconds()), int32(timerTimeout1.Seconds()), ) scheduledEvent2, _ := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity2", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout2.Seconds()), int32(timerTimeout2.Seconds()), int32(timerTimeout2.Seconds()), int32(timerTimeout2.Seconds()), ) startedEvent1 := test.AddActivityTaskStartedEvent(mutableState, scheduledEvent1.ID, identity) completeEvent1 := test.AddActivityTaskCompletedEvent(mutableState, scheduledEvent1.ID, startedEvent1.ID, []byte(nil), identity) mutableState.FlushBufferedEvents() // there should be a decision scheduled event after activity1 is completed // omitted here to make the test easier to read timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, completeEvent1.ID, completeEvent1.Version) s.NoError(err) // add resurrected activity info for activity1 persistenceMutableState.ActivityInfos[scheduledEvent1.ID] = &persistence.ActivityInfo{ Version: scheduledEvent1.Version, ScheduleID: scheduledEvent1.ID, ScheduledEventBatchID: scheduledEvent1.ID, ScheduledTime: time.Unix(0, scheduledEvent1.GetTimestamp()), StartedID: commonconstants.EmptyEventID, StartedTime: time.Time{}, ActivityID: "activity1", DomainID: s.domainID, ScheduleToStartTimeout: int32(timerTimeout1.Seconds()), ScheduleToCloseTimeout: int32(timerTimeout1.Seconds()), StartToCloseTimeout: int32(timerTimeout1.Seconds()), HeartbeatTimeout: int32(timerTimeout1.Seconds()), CancelRequested: false, CancelRequestID: commonconstants.EmptyEventID, LastHeartBeatUpdatedTime: time.Time{}, TimerTaskStatus: execution.TimerTaskStatusNone, TaskList: mutableState.GetExecutionInfo().TaskList, } s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) nextPageToken := []byte{1, 2, 3} s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.MatchedBy(func(req *persistence.ReadHistoryBranchRequest) bool { return req.MinEventID == 1 && req.NextPageToken == nil })).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{scheduledEvent1, scheduledEvent2, startedEvent1, completeEvent1}, NextPageToken: nextPageToken, }, nil).Once() s.mockHistoryV2Mgr.On("ReadHistoryBranch", mock.Anything, mock.MatchedBy(func(req *persistence.ReadHistoryBranchRequest) bool { return req.MinEventID == 1 && bytes.Equal(req.NextPageToken, nextPageToken) })).Return(&persistence.ReadHistoryBranchResponse{ HistoryEvents: []*types.HistoryEvent{scheduledEvent1, scheduledEvent2, startedEvent1, completeEvent1}, NextPageToken: nil, }, nil).Once() // only activity timer for activity2 should be fired s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.MatchedBy(func(req *persistence.AppendHistoryNodesRequest) bool { numActivityTimeoutEvents := 0 for _, event := range req.Events { if event.GetEventType() == types.EventTypeActivityTaskTimedOut { numActivityTimeoutEvents++ } } return numActivityTimeoutEvents == 1 })).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() // both activityInfo should be deleted s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.UpdateWorkflowExecutionRequest) bool { return len(req.UpdateWorkflowMutation.DeleteActivityInfos) == 2 })).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.timerActiveTaskExecutor.config.ResurrectionCheckMinDelay = dynamicproperties.GetDurationPropertyFnFilteredByDomain(timerTimeout2 - timerTimeout1) s.timeSource.Advance(timerTimeout2) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestDecisionScheduleToStartTimeout_NormalDecision() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeScheduleToStart), EventID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.UpdateWorkflowExecutionRequest) bool { return req.UpdateWorkflowMutation.ExecutionInfo.DecisionAttempt == 1 && req.UpdateWorkflowMutation.ExecutionInfo.DecisionScheduleID == 4 && req.UpdateWorkflowMutation.ExecutionInfo.NextEventID == 4 && // transient decision len(req.UpdateWorkflowMutation.TasksByCategory[persistence.HistoryTaskCategoryTimer]) == 1 // another schedule to start timer })).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestDecisionScheduleToStartTimeout_TransientDecision() { s.mockShard.GetConfig().NormalDecisionScheduleToStartMaxAttempts = dynamicproperties.GetIntPropertyFilteredByDomain(1) workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) decisionAttempt := int64(1) mutableState.GetExecutionInfo().DecisionAttempt = decisionAttempt // fake a transient decision di := test.AddDecisionTaskScheduledEvent(mutableState) timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeScheduleToStart), EventID: di.ScheduleID, ScheduleAttempt: decisionAttempt, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.UpdateWorkflowExecutionRequest) bool { return req.UpdateWorkflowMutation.ExecutionInfo.DecisionAttempt == 2 && req.UpdateWorkflowMutation.ExecutionInfo.DecisionScheduleID == 2 && req.UpdateWorkflowMutation.ExecutionInfo.NextEventID == 2 && // transient decision len(req.UpdateWorkflowMutation.TasksByCategory[persistence.HistoryTaskCategoryTimer]) == 0 // since the max attempt is 1 at the beginning of the test })).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestDecisionScheduleToStartTimeout_StickyDecision() { workflowExecution, mutableState, _, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) executionInfo := mutableState.GetExecutionInfo() executionInfo.StickyTaskList = "sticky-tasklist" executionInfo.StickyScheduleToStartTimeout = 1 executionInfo.LastUpdatedTimestamp = s.timeSource.Now() // schedule a second (sticky) decision task di := test.AddDecisionTaskScheduledEvent(mutableState) timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeScheduleToStart), EventID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.UpdateWorkflowExecutionRequest) bool { executionInfo := req.UpdateWorkflowMutation.ExecutionInfo return executionInfo.DecisionAttempt == 0 && // attempt is not increased when convert to normal decision executionInfo.DecisionScheduleID == 7 && executionInfo.NextEventID == 8 && // normal decision executionInfo.StickyTaskList == "" && // stickyness should be cleared executionInfo.StickyScheduleToStartTimeout == 0 && // stickyness should be cleared len(req.UpdateWorkflowMutation.TasksByCategory[persistence.HistoryTaskCategoryTimer]) == 1 // schedule to start timer })).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestDecisionStartToCloseTimeout_Fire() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) startedEvent := test.AddDecisionTaskStartedEvent(mutableState, di.ScheduleID, mutableState.GetExecutionInfo().TaskList, uuid.New()) timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeStartToClose), EventID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startedEvent.ID, startedEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) decisionInfo, ok := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).GetPendingDecision() s.True(ok) s.True(decisionInfo.ScheduleID != commonconstants.EmptyEventID) s.Equal(commonconstants.EmptyEventID, decisionInfo.StartedID) s.Equal(int64(1), decisionInfo.Attempt) } func (s *timerActiveTaskExecutorSuite) TestDecisionStartToCloseTimeout_ExceedsMaxAttempts() { s.mockShard.GetConfig().DecisionRetryMaxAttempts = dynamicproperties.GetIntPropertyFilteredByDomain(1) s.mockShard.GetConfig().EnforceDecisionTaskAttempts = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) startedEvent := test.AddDecisionTaskStartedEvent(mutableState, di.ScheduleID, mutableState.GetExecutionInfo().TaskList, uuid.New()) decisionAttempt := int64(1) mutableState.GetExecutionInfo().DecisionAttempt = decisionAttempt di.Attempt = decisionAttempt timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeStartToClose), EventID: di.ScheduleID, ScheduleAttempt: decisionAttempt, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startedEvent.ID, startedEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.UpdateWorkflowExecutionRequest) bool { return req.UpdateWorkflowMutation.ExecutionInfo.State == persistence.WorkflowStateCompleted && req.UpdateWorkflowMutation.ExecutionInfo.CloseStatus == persistence.WorkflowCloseStatusTerminated })).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) ms := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()) s.False(ms.IsWorkflowExecutionRunning()) _, closeStatus := ms.GetWorkflowStateCloseStatus() s.Equal(closeStatus, persistence.WorkflowCloseStatusTerminated) completionEvent, err := ms.GetCompletionEvent(context.Background()) s.NoError(err) s.Equal(completionEvent.WorkflowExecutionTerminatedEventAttributes.Reason, common.FailureReasonDecisionAttemptsExceedsLimit) } func (s *timerActiveTaskExecutorSuite) TestDecisionStartToCloseTimeout_BelowMaxAttempts() { s.mockShard.GetConfig().DecisionRetryMaxAttempts = dynamicproperties.GetIntPropertyFilteredByDomain(5) s.mockShard.GetConfig().EnforceDecisionTaskAttempts = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) startedEvent := test.AddDecisionTaskStartedEvent(mutableState, di.ScheduleID, mutableState.GetExecutionInfo().TaskList, uuid.New()) decisionAttempt := int64(1) mutableState.GetExecutionInfo().DecisionAttempt = decisionAttempt di.Attempt = decisionAttempt timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeStartToClose), EventID: di.ScheduleID, ScheduleAttempt: decisionAttempt, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startedEvent.ID, startedEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.UpdateWorkflowExecutionRequest) bool { return req.UpdateWorkflowMutation.ExecutionInfo.State == persistence.WorkflowStateRunning && req.UpdateWorkflowMutation.ExecutionInfo.DecisionAttempt == 2 })).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) decisionInfo, ok := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).GetPendingDecision() s.True(ok) s.Equal(int64(2), decisionInfo.Attempt) s.True(s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).IsWorkflowExecutionRunning()) } func (s *timerActiveTaskExecutorSuite) TestDecisionStartToCloseTimeout_AttemptsNotEnforced() { s.mockShard.GetConfig().DecisionRetryMaxAttempts = dynamicproperties.GetIntPropertyFilteredByDomain(1) s.mockShard.GetConfig().EnforceDecisionTaskAttempts = dynamicproperties.GetBoolPropertyFnFilteredByDomain(false) workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) startedEvent := test.AddDecisionTaskStartedEvent(mutableState, di.ScheduleID, mutableState.GetExecutionInfo().TaskList, uuid.New()) decisionAttempt := int64(1) mutableState.GetExecutionInfo().DecisionAttempt = decisionAttempt di.Attempt = decisionAttempt timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeStartToClose), EventID: di.ScheduleID, ScheduleAttempt: decisionAttempt, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startedEvent.ID, startedEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(req *persistence.UpdateWorkflowExecutionRequest) bool { return req.UpdateWorkflowMutation.ExecutionInfo.State == persistence.WorkflowStateRunning && req.UpdateWorkflowMutation.ExecutionInfo.DecisionAttempt == 2 })).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) decisionInfo, ok := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).GetPendingDecision() s.True(ok) s.Equal(int64(2), decisionInfo.Attempt) s.True(s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).IsWorkflowExecutionRunning()) } func (s *timerActiveTaskExecutorSuite) TestDecisionStartToCloseTimeout_Noop() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) startedEvent := test.AddDecisionTaskStartedEvent(mutableState, di.ScheduleID, mutableState.GetExecutionInfo().TaskList, uuid.New()) timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeStartToClose), EventID: di.ScheduleID - 1, // no corresponding decision for this scheduleID }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startedEvent.ID, startedEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestWorkflowBackoffTimer_Fire() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTask := s.newTimerTaskFromInfo(&persistence.WorkflowBackoffTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: persistence.WorkflowBackoffTimeoutTypeRetry, }) startEvent, err := mutableState.GetStartEvent(context.Background()) s.NoError(err) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startEvent.ID, startEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) decisionInfo, ok := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).GetPendingDecision() s.True(ok) s.True(decisionInfo.ScheduleID != commonconstants.EmptyEventID) s.Equal(commonconstants.EmptyEventID, decisionInfo.StartedID) s.Equal(int64(0), decisionInfo.Attempt) } func (s *timerActiveTaskExecutorSuite) TestWorkflowBackoffTimer_Noop() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTask := s.newTimerTaskFromInfo(&persistence.WorkflowBackoffTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: persistence.WorkflowBackoffTimeoutTypeRetry, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestActivityRetryTimer_Fire() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTimeout := 2 * time.Second scheduledEvent, activityInfo := test.AddActivityTaskScheduledEventWithRetry( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1.2, MaximumIntervalInSeconds: 5, MaximumAttempts: 5, NonRetriableErrorReasons: []string{"(╯' - ')╯ ┻━┻ "}, ExpirationIntervalInSeconds: 999, }, ) activityInfo.Attempt = 1 timerTask := s.newTimerTaskFromInfo(&persistence.ActivityRetryTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, EventID: activityInfo.ScheduleID, Attempt: int64(activityInfo.Attempt), }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, scheduledEvent.ID, scheduledEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockMatchingClient.EXPECT().AddActivityTask( gomock.Any(), &types.AddActivityTaskRequest{ DomainUUID: activityInfo.DomainID, SourceDomainUUID: activityInfo.DomainID, Execution: &workflowExecution, TaskList: &types.TaskList{ Name: activityInfo.TaskList, }, ScheduleID: activityInfo.ScheduleID, ScheduleToStartTimeoutSeconds: common.Int32Ptr(activityInfo.ScheduleToStartTimeout), PartitionConfig: mutableState.GetExecutionInfo().PartitionConfig, }, ).Return(&types.AddActivityTaskResponse{}, nil).Times(1) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestActivityRetryTimer_Noop() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTimeout := 2 * time.Second scheduledEvent, activityInfo := test.AddActivityTaskScheduledEventWithRetry( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), &types.RetryPolicy{ InitialIntervalInSeconds: 1, BackoffCoefficient: 1.2, MaximumIntervalInSeconds: 5, MaximumAttempts: 5, NonRetriableErrorReasons: []string{"(╯' - ')╯ ┻━┻ "}, ExpirationIntervalInSeconds: 999, }, ) startedEvent := test.AddActivityTaskStartedEvent(mutableState, scheduledEvent.ID, "identity") s.Nil(startedEvent) timerTask := s.newTimerTaskFromInfo(&persistence.ActivityRetryTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, EventID: activityInfo.ScheduleID, Attempt: int64(activityInfo.Attempt), }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, scheduledEvent.ID, scheduledEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) } func (s *timerActiveTaskExecutorSuite) TestWorkflowTimeout_Fire() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTask := s.newTimerTaskFromInfo(&persistence.WorkflowTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) running := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).IsWorkflowExecutionRunning() s.False(running) } func (s *timerActiveTaskExecutorSuite) TestWorkflowTimeout_ContinueAsNew_Retry() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) // need to override the workflow retry policy executionInfo := mutableState.GetExecutionInfo() executionInfo.HasRetryPolicy = true executionInfo.ExpirationTime = s.timeSource.Now().Add(1000 * time.Second) executionInfo.MaximumAttempts = 10 executionInfo.InitialInterval = 1 executionInfo.MaximumInterval = 1 executionInfo.BackoffCoefficient = 1 timerTask := s.newTimerTaskFromInfo(&persistence.WorkflowTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) // one for current workflow, one for new s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Times(2) s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), s.domainID, gomock.Any()).Return(&types.ActiveClusterInfo{}, nil).AnyTimes() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) state, closeStatus := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).GetWorkflowStateCloseStatus() s.Equal(persistence.WorkflowStateCompleted, state) s.Equal(persistence.WorkflowCloseStatusContinuedAsNew, closeStatus) } func (s *timerActiveTaskExecutorSuite) TestWorkflowTimeout_ContinueAsNew_Cron() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) executionInfo := mutableState.GetExecutionInfo() executionInfo.StartTimestamp = s.timeSource.Now() executionInfo.CronSchedule = "* * * * *" timerTask := s.newTimerTaskFromInfo(&persistence.WorkflowTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) // one for current workflow, one for new s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Times(2) s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByClusterAttribute(gomock.Any(), s.domainID, gomock.Any()).Return(&types.ActiveClusterInfo{}, nil).AnyTimes() _, err = s.timerActiveTaskExecutor.Execute(timerTask) s.NoError(err) state, closeStatus := s.getMutableStateFromCache(s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID()).GetWorkflowStateCloseStatus() s.Equal(persistence.WorkflowStateCompleted, state) s.Equal(persistence.WorkflowCloseStatusContinuedAsNew, closeStatus) } func (s *timerActiveTaskExecutorSuite) getMutableStateFromCache( domainID string, workflowID string, runID string, ) execution.MutableState { return s.executionCache.Get( definition.NewWorkflowIdentifier(domainID, workflowID, runID), ).(execution.Context).GetWorkflowExecution() } func (s *timerActiveTaskExecutorSuite) newTimerTaskFromInfo( task persistence.Task, ) Task { return NewHistoryTask(s.mockShard, task, QueueTypeActiveTimer, s.logger, nil, nil, nil, nil, nil) } func (s *timerActiveTaskExecutorSuite) TestActiveTaskTimeout() { deleteHistoryEventTask := s.newTimerTaskFromInfo(&persistence.DeleteHistoryEventTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: "workflowID", RunID: "runID", }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), }, }) s.timerActiveTaskExecutor.Execute(deleteHistoryEventTask) } ================================================ FILE: service/history/task/timer_standby_task_executor.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "errors" "fmt" "time" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/simulation" "github.com/uber/cadence/service/worker/archiver" ) var ( errUnexpectedTask = errors.New("unexpected task") errUnknownTimerTask = errors.New("unknown timer task") ) type ( timerStandbyTaskExecutor struct { *timerTaskExecutorBase clusterName string historyResender ndc.HistoryResender getRemoteClusterNameFn func(context.Context, persistence.Task) (string, error) } ) // NewTimerStandbyTaskExecutor creates a new task executor for standby timer task func NewTimerStandbyTaskExecutor( shard shard.Context, archiverClient archiver.Client, executionCache execution.Cache, historyResender ndc.HistoryResender, logger log.Logger, metricsClient metrics.Client, clusterName string, config *config.Config, ) Executor { return &timerStandbyTaskExecutor{ timerTaskExecutorBase: newTimerTaskExecutorBase( shard, archiverClient, executionCache, logger, metricsClient, config, ), clusterName: clusterName, historyResender: historyResender, getRemoteClusterNameFn: func(ctx context.Context, taskInfo persistence.Task) (string, error) { if shard.GetConfig().EnableTimerQueueV2(shard.GetShardID()) { return getRemoteClusterName(ctx, shard.GetClusterMetadata().GetCurrentClusterName(), shard.GetActiveClusterManager(), taskInfo) } return clusterName, nil }, } } func (t *timerStandbyTaskExecutor) Execute(task Task) (ExecuteResponse, error) { simulation.LogEvents(simulation.E{ EventName: simulation.EventNameExecuteHistoryTask, Host: t.shard.GetConfig().HostName, ShardID: t.shard.GetShardID(), DomainID: task.GetDomainID(), WorkflowID: task.GetWorkflowID(), RunID: task.GetRunID(), Payload: map[string]any{ "task_category": persistence.HistoryTaskCategoryTimer.Name(), "task_type": task.GetTaskType(), "task_key": task.GetTaskKey(), }, }) scope := getOrCreateDomainTaggedScope(t.shard, GetTimerTaskMetricScope(task.GetTaskType(), false), task.GetDomainID(), t.logger) executeResponse := ExecuteResponse{ Scope: scope, IsActiveTask: false, } switch timerTask := task.GetInfo().(type) { case *persistence.UserTimerTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeUserTimerTimeoutTask(ctx, timerTask) case *persistence.ActivityTimeoutTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeActivityTimeoutTask(ctx, timerTask) case *persistence.DecisionTimeoutTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeDecisionTimeoutTask(ctx, timerTask) case *persistence.WorkflowTimeoutTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeWorkflowTimeoutTask(ctx, timerTask) case *persistence.ActivityRetryTimerTask: // retry backoff timer should not get created on passive cluster // TODO: add error logs return executeResponse, nil case *persistence.WorkflowBackoffTimerTask: ctx, cancel := context.WithTimeout(t.ctx, taskDefaultTimeout) defer cancel() return executeResponse, t.executeWorkflowBackoffTimerTask(ctx, timerTask) case *persistence.DeleteHistoryEventTask: // special timeout for delete history event deleteHistoryEventContext, deleteHistoryEventCancel := context.WithTimeout(t.ctx, time.Duration(t.config.DeleteHistoryEventContextTimeout())*time.Second) defer deleteHistoryEventCancel() return executeResponse, t.executeDeleteHistoryEventTask(deleteHistoryEventContext, timerTask) default: return executeResponse, errUnknownTimerTask } } func (t *timerStandbyTaskExecutor) executeUserTimerTimeoutTask( ctx context.Context, timerTask *persistence.UserTimerTask, ) error { actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { timerSequence := execution.NewTimerSequence(mutableState) Loop: for _, timerSequenceID := range timerSequence.LoadAndSortUserTimers() { _, ok := mutableState.GetUserTimerInfoByEventID(timerSequenceID.EventID) if !ok { errString := fmt.Sprintf("failed to find in user timer event ID: %v", timerSequenceID.EventID) t.logger.Error(errString) return nil, &types.InternalServiceError{Message: errString} } if _, isExpired := timerSequence.IsExpired( timerTask.VisibilityTimestamp, timerSequenceID, ); isExpired { return getHistoryResendInfo(mutableState) } // since the user timer are already sorted, so if there is one timer which will not expired // all user timer after this timer will not expired break Loop //nolint:staticcheck } // if there is no user timer expired, then we are good return nil, nil } return t.processTimer( ctx, timerTask, timerTask.EventID, actionFn, getStandbyPostActionFn( t.logger, timerTask, t.getCurrentTime, t.config.StandbyTaskMissingEventsResendDelay(), t.config.StandbyTaskMissingEventsDiscardDelay(), t.fetchHistoryFromRemote, standbyTaskPostActionTaskDiscarded, ), ) } func (t *timerStandbyTaskExecutor) executeActivityTimeoutTask( ctx context.Context, timerTask *persistence.ActivityTimeoutTask, ) error { // activity heartbeat timer task is a special snowflake. // normal activity timer task on the passive side will be generated by events related to activity in history replicator, // and the standby timer processor will only need to verify whether the timer task can be safely throw away. // // activity heartbeat timer task cannot be handled in the way mentioned above. // the reason is, there is no event driving the creation of new activity heartbeat timer. // although there will be an task syncing activity from remote, the task is not an event, // and cannot attempt to recreate a new activity timer task. // // the overall solution is to attempt to generate a new activity timer task whenever the // task passed in is safe to be throw away. actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { timerSequence := execution.NewTimerSequence(mutableState) updateMutableState := false Loop: for _, timerSequenceID := range timerSequence.LoadAndSortActivityTimers() { _, ok := mutableState.GetActivityInfo(timerSequenceID.EventID) if !ok { errString := fmt.Sprintf("failed to find in memory activity timer: %v", timerSequenceID.EventID) t.logger.Error(errString) return nil, &types.InternalServiceError{Message: errString} } if _, isExpired := timerSequence.IsExpired( timerTask.VisibilityTimestamp, timerSequenceID, ); isExpired { return getHistoryResendInfo(mutableState) } // since the activity timer are already sorted, so if there is one timer which will not expired // all activity timer after this timer will not expired break Loop //nolint:staticcheck } // for reason to update mutable state & generate a new activity task, // see comments at the beginning of this function. // NOTE: this is the only place in the standby logic where mutable state can be updated // need to clear the activity heartbeat timer task marks lastWriteVersion, err := mutableState.GetLastWriteVersion() if err != nil { return nil, err } // NOTE: LastHeartbeatTimeoutVisibilityInSeconds is for deduping heartbeat timer creation as it's possible // one heartbeat task was persisted multiple times with different taskIDs due to the retry logic // for updating workflow execution. In that case, only one new heartbeat timeout task should be // created. isHeartBeatTask := timerTask.TimeoutType == int(types.TimeoutTypeHeartbeat) activityInfo, ok := mutableState.GetActivityInfo(timerTask.EventID) if isHeartBeatTask && ok && activityInfo.LastHeartbeatTimeoutVisibilityInSeconds <= timerTask.VisibilityTimestamp.Unix() { activityInfo.TimerTaskStatus = activityInfo.TimerTaskStatus &^ execution.TimerTaskStatusCreatedHeartbeat if err := mutableState.UpdateActivity(activityInfo); err != nil { return nil, err } updateMutableState = true } // passive logic need to explicitly call create timer modified, err := timerSequence.CreateNextActivityTimer() if err != nil { return nil, err } updateMutableState = updateMutableState || modified if !updateMutableState { return nil, nil } now := t.getStandbyClusterTime() // we need to handcraft some of the variables // since the job being done here is update the activity and possibly write a timer task to DB // also need to reset the current version. t.logger.Debugf("executeActivityTimeoutTask calling UpdateCurrentVersion for domain %s, wfID %v, lastWriteVersion %v", timerTask.DomainID, timerTask.WorkflowID, lastWriteVersion) if err := mutableState.UpdateCurrentVersion(lastWriteVersion, true); err != nil { return nil, err } err = wfContext.UpdateWorkflowExecutionAsPassive(ctx, now) return nil, err } return t.processTimer( ctx, timerTask, timerTask.EventID, actionFn, getStandbyPostActionFn( t.logger, timerTask, t.getCurrentTime, t.config.StandbyTaskMissingEventsResendDelay(), t.config.StandbyTaskMissingEventsDiscardDelay(), t.fetchHistoryFromRemote, standbyTaskPostActionTaskDiscarded, ), ) } func (t *timerStandbyTaskExecutor) executeDecisionTimeoutTask( ctx context.Context, timerTask *persistence.DecisionTimeoutTask, ) error { // decision schedule to start timer won't be generated for sticky decision, // since sticky is cleared when applying events on passive. // for normal decision, we don't know if a schedule to start timeout timer // is generated or not since it's based on a dynamicconfig. On passive cluster, // a timer task will be generated based on passive cluster's config, however, it // may not match the active cluster. // so we simply ignore the schedule to start timer here as the decision task will be // pushed to matching without any timeout if's not started, and the workflow // can continue execution after failover. if timerTask.TimeoutType == int(types.TimeoutTypeScheduleToStart) { return nil } actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { decision, isPending := mutableState.GetDecisionInfo(timerTask.EventID) if !isPending { return nil, nil } ok, err := verifyTaskVersion(t.shard, t.logger, timerTask.DomainID, decision.Version, timerTask.Version, timerTask) if err != nil || !ok { return nil, err } return getHistoryResendInfo(mutableState) } return t.processTimer( ctx, timerTask, timerTask.EventID, actionFn, getStandbyPostActionFn( t.logger, timerTask, t.getCurrentTime, t.config.StandbyTaskMissingEventsResendDelay(), t.config.StandbyTaskMissingEventsDiscardDelay(), t.fetchHistoryFromRemote, standbyTaskPostActionTaskDiscarded, ), ) } func (t *timerStandbyTaskExecutor) executeWorkflowBackoffTimerTask( ctx context.Context, timerTask *persistence.WorkflowBackoffTimerTask, ) error { actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { if mutableState.HasProcessedOrPendingDecision() { // if there is one decision already been processed // or has pending decision, meaning workflow has already running return nil, nil } // Note: do not need to verify task version here // logic can only go here if mutable state build's next event ID is 2 // meaning history only contains workflow started event. // we can do the checking of task version vs workflow started version // however, workflow started version is immutable // active cluster will add first decision task after backoff timeout. // standby cluster should just call ack manager to retry this task // since we are stilling waiting for the first DecisionScheduledEvent to be replicated from active side. return getHistoryResendInfo(mutableState) } return t.processTimer( ctx, timerTask, 0, actionFn, getStandbyPostActionFn( t.logger, timerTask, t.getCurrentTime, t.config.StandbyTaskMissingEventsResendDelay(), t.config.StandbyTaskMissingEventsDiscardDelay(), t.fetchHistoryFromRemote, standbyTaskPostActionTaskDiscarded, ), ) } func (t *timerStandbyTaskExecutor) executeWorkflowTimeoutTask( ctx context.Context, timerTask *persistence.WorkflowTimeoutTask, ) error { actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { // we do not need to notify new timer to base, since if there is no new event being replicated // checking again if the timer can be completed is meaningless startVersion, err := mutableState.GetStartVersion() if err != nil { return nil, err } ok, err := verifyTaskVersion(t.shard, t.logger, timerTask.DomainID, startVersion, timerTask.Version, timerTask) if err != nil || !ok { return nil, err } return getHistoryResendInfo(mutableState) } return t.processTimer( ctx, timerTask, 0, actionFn, getStandbyPostActionFn( t.logger, timerTask, t.getCurrentTime, t.config.StandbyTaskMissingEventsResendDelay(), t.config.StandbyTaskMissingEventsDiscardDelay(), t.fetchHistoryFromRemote, standbyTaskPostActionTaskDiscarded, ), ) } func (t *timerStandbyTaskExecutor) getStandbyClusterTime() time.Time { // time of remote cluster in the shard is delayed by "StandbyClusterDelay" // so to get the current accurate remote cluster time, need to add it back return t.shard.GetCurrentTime(t.clusterName).Add(t.shard.GetConfig().StandbyClusterDelay()) } func (t *timerStandbyTaskExecutor) processTimer( ctx context.Context, timerTask persistence.Task, eventID int64, actionFn standbyActionFn, postActionFn standbyPostActionFn, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( timerTask.GetDomainID(), getWorkflowExecution(timerTask), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { if isRedispatchErr(retError) { release(nil) } else { release(retError) } }() mutableState, err := loadMutableState(ctx, wfContext, timerTask, t.metricsClient.Scope(metrics.TimerQueueProcessorScope), t.logger, eventID) if err != nil { return err } if mutableState == nil { return nil } if !mutableState.IsWorkflowExecutionRunning() { // TODO: Check if workflow timeout timer comes to this point and then discarded. // workflow already finished, no need to process the timer return nil } historyResendInfo, err := actionFn(ctx, wfContext, mutableState) if err != nil { if t.logger.DebugOn() { t.logger.Debug("processTimer got error from actionFn", tag.Error(err), tag.WorkflowID(timerTask.GetWorkflowID()), tag.WorkflowRunID(timerTask.GetRunID()), tag.WorkflowDomainID(timerTask.GetDomainID()), tag.TaskID(timerTask.GetTaskID()), tag.TaskType(int(timerTask.GetTaskType())), tag.Timestamp(timerTask.GetVisibilityTimestamp()), ) } return err } if t.logger.DebugOn() { t.logger.Debug("processTimer got historyResendInfo from actionFn", tag.WorkflowID(timerTask.GetWorkflowID()), tag.WorkflowRunID(timerTask.GetRunID()), tag.WorkflowDomainID(timerTask.GetDomainID()), tag.TaskID(timerTask.GetTaskID()), tag.TaskType(int(timerTask.GetTaskType())), tag.Timestamp(timerTask.GetVisibilityTimestamp()), ) } release(nil) return postActionFn(ctx, timerTask, historyResendInfo, t.logger) } func (t *timerStandbyTaskExecutor) fetchHistoryFromRemote( ctx context.Context, taskInfo persistence.Task, postActionInfo interface{}, _ log.Logger, ) error { if postActionInfo == nil { return nil } resendInfo := postActionInfo.(*historyResendInfo) t.metricsClient.IncCounter(metrics.HistoryRereplicationByTimerTaskScope, metrics.CadenceClientRequests) stopwatch := t.metricsClient.StartTimer(metrics.HistoryRereplicationByTimerTaskScope, metrics.CadenceClientLatency) defer stopwatch.Stop() remoteClusterName, err := t.getRemoteClusterNameFn(ctx, taskInfo) if err != nil { return err } if resendInfo.lastEventID != nil && resendInfo.lastEventVersion != nil { // note history resender doesn't take in a context parameter, there's a separate dynamicconfig for // controlling the timeout for resending history. err = t.historyResender.SendSingleWorkflowHistory( remoteClusterName, taskInfo.GetDomainID(), taskInfo.GetWorkflowID(), taskInfo.GetRunID(), resendInfo.lastEventID, resendInfo.lastEventVersion, nil, nil, ) } else { err = &types.InternalServiceError{ Message: fmt.Sprintf("incomplete historyResendInfo: %v", resendInfo), } } if err != nil { t.logger.Error("Error re-replicating history from remote.", tag.ShardID(t.shard.GetShardID()), tag.WorkflowDomainID(taskInfo.GetDomainID()), tag.WorkflowID(taskInfo.GetWorkflowID()), tag.WorkflowRunID(taskInfo.GetRunID()), tag.SourceCluster(remoteClusterName), tag.Error(err), ) } else if t.logger.DebugOn() { t.logger.Debug("Successfully re-replicated history from remote.", tag.WorkflowID(taskInfo.GetWorkflowID()), tag.WorkflowRunID(taskInfo.GetRunID()), tag.WorkflowDomainID(taskInfo.GetDomainID()), tag.TaskID(taskInfo.GetTaskID()), tag.TaskType(int(taskInfo.GetTaskType())), tag.SourceCluster(remoteClusterName), ) } // return error so task processing logic will retry return &redispatchError{Reason: "fetchHistoryFromRemote"} } func (t *timerStandbyTaskExecutor) getCurrentTime(taskInfo persistence.Task) (time.Time, error) { // the schedule time of a standby task is the time from the active cluster of the task, // for history queue v2, t.clusterName is the current cluster, so we should get remote cluster name of the task // However, this only has an effect when the time skew between the current cluster and the remote cluster is large enough (probably more than 1 minute), // the impact is that the standby task may be discarded too early ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() sourceClusterName, err := t.getRemoteClusterNameFn(ctx, taskInfo) if err != nil { return time.Time{}, err } return t.shard.GetCurrentTime(sourceClusterName), nil } ================================================ FILE: service/history/task/timer_standby_task_executor_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" test "github.com/uber/cadence/service/history/testing" ) type ( timerStandbyTaskExecutorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockEngine *engine.MockEngine mockDomainCache *cache.MockDomainCache mockNDCHistoryResender *ndc.MockHistoryResender mockExecutionMgr *mocks.ExecutionManager logger log.Logger domainID string domainEntry *cache.DomainCacheEntry version int64 clusterName string timeSource clock.MockedTimeSource fetchHistoryDuration time.Duration discardDuration time.Duration timerStandbyTaskExecutor *timerStandbyTaskExecutor } ) func TestTimerStandbyTaskExecutorSuite(t *testing.T) { s := new(timerStandbyTaskExecutorSuite) suite.Run(t, s) } func (s *timerStandbyTaskExecutorSuite) SetupSuite() { } func (s *timerStandbyTaskExecutorSuite) SetupTest() { s.Assertions = require.New(s.T()) config := config.NewForTest() s.domainID = constants.TestDomainID s.domainEntry = constants.TestGlobalDomainEntry s.version = s.domainEntry.GetFailoverVersion() s.clusterName = cluster.TestAlternativeClusterName s.timeSource = clock.NewMockedTimeSource() s.fetchHistoryDuration = config.StandbyTaskMissingEventsResendDelay() + (config.StandbyTaskMissingEventsDiscardDelay()-config.StandbyTaskMissingEventsResendDelay())/2 s.discardDuration = config.StandbyTaskMissingEventsDiscardDelay() * 2 s.controller = gomock.NewController(s.T()) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ RangeID: 1, TransferAckLevel: 0, }, config, ) s.mockShard.SetEventsCache(events.NewCache( s.mockShard.GetShardID(), s.mockShard.GetHistoryManager(), s.mockShard.GetConfig(), s.mockShard.GetLogger(), s.mockShard.GetMetricsClient(), s.mockShard.GetDomainCache(), )) s.mockShard.Resource.TimeSource = s.timeSource s.mockEngine = engine.NewMockEngine(s.controller) s.mockEngine.EXPECT().NotifyNewHistoryEvent(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewTransferTasks(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewTimerTasks(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewReplicationTasks(gomock.Any()).AnyTimes() s.mockShard.SetEngine(s.mockEngine) s.mockNDCHistoryResender = ndc.NewMockHistoryResender(s.controller) // ack manager will use the domain information s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockExecutionMgr = s.mockShard.Resource.ExecutionMgr s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(constants.TestGlobalDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(constants.TestDomainName, nil).AnyTimes() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestGlobalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestGlobalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(testActiveClusterInfo, nil).AnyTimes() s.logger = s.mockShard.GetLogger() s.timerStandbyTaskExecutor = NewTimerStandbyTaskExecutor( s.mockShard, nil, execution.NewCache(s.mockShard), s.mockNDCHistoryResender, s.logger, s.mockShard.GetMetricsClient(), s.clusterName, config, ).(*timerStandbyTaskExecutor) s.timerStandbyTaskExecutor.getRemoteClusterNameFn = func(ctx context.Context, taskInfo persistence.Task) (string, error) { return s.clusterName, nil } } func (s *timerStandbyTaskExecutorSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *timerStandbyTaskExecutorSuite) TestProcessUserTimerTimeout_Pending() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerID := "timer" timerTimeout := 2 * time.Second event, _ := test.AddTimerStartedEvent(mutableState, decisionCompletionID, timerID, int64(timerTimeout.Seconds())) nextEventID := event.ID timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextUserTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now().Add(s.fetchHistoryDuration)) s.mockNDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.clusterName, timerTask.GetDomainID(), timerTask.GetWorkflowID(), timerTask.GetRunID(), common.Int64Ptr(nextEventID), common.Int64Ptr(s.version), nil, nil, ).Return(nil).Times(1) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now().Add(s.discardDuration)) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Equal(ErrTaskDiscarded, err) } func (s *timerStandbyTaskExecutorSuite) TestProcessUserTimerTimeout_Success() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerID := "timer" timerTimeout := 2 * time.Second event, _ := test.AddTimerStartedEvent(mutableState, decisionCompletionID, timerID, int64(timerTimeout.Seconds())) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextUserTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) event = test.AddTimerFiredEvent(mutableState, timerID) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Nil(err) } func (s *timerStandbyTaskExecutorSuite) TestProcessUserTimerTimeout_Multiple() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerID1 := "timer-1" timerTimeout1 := 2 * time.Second event, _ := test.AddTimerStartedEvent(mutableState, decisionCompletionID, timerID1, int64(timerTimeout1.Seconds())) timerID2 := "timer-2" timerTimeout2 := 50 * time.Second _, _ = test.AddTimerStartedEvent(mutableState, decisionCompletionID, timerID2, int64(timerTimeout2.Seconds())) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextUserTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) event = test.AddTimerFiredEvent(mutableState, timerID1) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Nil(err) } func (s *timerStandbyTaskExecutorSuite) TestProcessActivityTimeout_Pending() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTimeout := 2 * time.Second scheduledEvent, _ := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), ) nextEventID := scheduledEvent.ID timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, scheduledEvent.ID, scheduledEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now().Add(s.fetchHistoryDuration)) s.mockNDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.clusterName, timerTask.GetDomainID(), timerTask.GetWorkflowID(), timerTask.GetRunID(), common.Int64Ptr(nextEventID), common.Int64Ptr(s.version), nil, nil, ).Return(nil).Times(1) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now().Add(s.discardDuration)) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Equal(ErrTaskDiscarded, err) } func (s *timerStandbyTaskExecutorSuite) TestProcessActivityTimeout_Success() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) identity := "identity" timerTimeout := 2 * time.Second scheduledEvent, _ := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), ) startedEvent := test.AddActivityTaskStartedEvent(mutableState, scheduledEvent.ID, identity) timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] timerTask := s.newTimerTaskFromInfo(task) completeEvent := test.AddActivityTaskCompletedEvent(mutableState, scheduledEvent.ID, startedEvent.ID, []byte(nil), identity) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, completeEvent.ID, completeEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Nil(err) } func (s *timerStandbyTaskExecutorSuite) TestProcessActivityTimeout_Heartbeat_Noop() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTimeout := 2 * time.Second heartbeatTimerTimeout := time.Second scheduledEvent, _ := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity", "activity type", mutableState.GetExecutionInfo().TaskList, []byte(nil), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(timerTimeout.Seconds()), int32(heartbeatTimerTimeout.Seconds()), ) startedEvent := test.AddActivityTaskStartedEvent(mutableState, scheduledEvent.ID, "identity") mutableState.FlushBufferedEvents() timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) task := mutableState.GetTimerTasks()[0] s.Equal(int(execution.TimerTypeHeartbeat), task.(*persistence.ActivityTimeoutTask).TimeoutType) task.SetVisibilityTimestamp(task.GetVisibilityTimestamp().Add(-5 * time.Second)) timerTask := s.newTimerTaskFromInfo(task) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startedEvent.ID, startedEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Nil(err) } func (s *timerStandbyTaskExecutorSuite) TestProcessActivityTimeout_Multiple_CanUpdate() { _, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) identity := "identity" tasklist := "tasklist" activityID1 := "activity 1" activityType1 := "activity type 1" timerTimeout1 := 2 * time.Second scheduledEvent1, _ := test.AddActivityTaskScheduledEvent(mutableState, decisionCompletionID, activityID1, activityType1, tasklist, []byte(nil), int32(timerTimeout1.Seconds()), int32(timerTimeout1.Seconds()), int32(timerTimeout1.Seconds()), int32(timerTimeout1.Seconds())) startedEvent1 := test.AddActivityTaskStartedEvent(mutableState, scheduledEvent1.ID, identity) activityID2 := "activity 2" activityType2 := "activity type 2" timerTimeout2 := 20 * time.Second scheduledEvent2, _ := test.AddActivityTaskScheduledEvent(mutableState, decisionCompletionID, activityID2, activityType2, tasklist, []byte(nil), int32(timerTimeout2.Seconds()), int32(timerTimeout2.Seconds()), int32(timerTimeout2.Seconds()), int32(timerTimeout2.Seconds())) test.AddActivityTaskStartedEvent(mutableState, scheduledEvent2.ID, identity) activityInfo2 := mutableState.GetPendingActivityInfos()[scheduledEvent2.ID] activityInfo2.TimerTaskStatus |= execution.TimerTaskStatusCreatedHeartbeat activityInfo2.LastHeartBeatUpdatedTime = time.Now() timerSequence := execution.NewTimerSequence(mutableState) mutableState.DeleteTimerTasks() modified, err := timerSequence.CreateNextActivityTimer() s.NoError(err) s.True(modified) timerTask := s.newTimerTaskFromInfo(&persistence.ActivityTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: mutableState.GetExecutionInfo().WorkflowID, RunID: mutableState.GetExecutionInfo().RunID, }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: activityInfo2.LastHeartBeatUpdatedTime.Add(-5 * time.Second), }, EventID: scheduledEvent2.ID, }) completeEvent1 := test.AddActivityTaskCompletedEvent(mutableState, scheduledEvent1.ID, startedEvent1.ID, []byte(nil), identity) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, completeEvent1.ID, completeEvent1.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.MatchedBy(func(input *persistence.UpdateWorkflowExecutionRequest) bool { s.Equal(1, len(input.UpdateWorkflowMutation.TasksByCategory[persistence.HistoryTaskCategoryTimer])) s.Equal(1, len(input.UpdateWorkflowMutation.UpsertActivityInfos)) mutableState.GetExecutionInfo().LastUpdatedTimestamp = input.UpdateWorkflowMutation.ExecutionInfo.LastUpdatedTimestamp input.RangeID = 0 input.UpdateWorkflowMutation.ExecutionInfo.LastEventTaskID = 0 mutableState.GetExecutionInfo().LastEventTaskID = 0 mutableState.GetExecutionInfo().DecisionOriginalScheduledTimestamp = input.UpdateWorkflowMutation.ExecutionInfo.DecisionOriginalScheduledTimestamp s.Equal(&persistence.UpdateWorkflowExecutionRequest{ UpdateWorkflowMutation: persistence.WorkflowMutation{ ExecutionInfo: mutableState.GetExecutionInfo(), ExecutionStats: &persistence.ExecutionStats{}, TasksByCategory: input.UpdateWorkflowMutation.TasksByCategory, Condition: mutableState.GetNextEventID(), UpsertActivityInfos: input.UpdateWorkflowMutation.UpsertActivityInfos, DeleteActivityInfos: []int64{}, UpsertTimerInfos: []*persistence.TimerInfo{}, DeleteTimerInfos: []string{}, UpsertChildExecutionInfos: []*persistence.ChildExecutionInfo{}, DeleteChildExecutionInfos: []int64{}, UpsertRequestCancelInfos: []*persistence.RequestCancelInfo{}, DeleteRequestCancelInfos: []int64{}, UpsertSignalInfos: []*persistence.SignalInfo{}, DeleteSignalInfos: []int64{}, UpsertSignalRequestedIDs: []string{}, DeleteSignalRequestedIDs: []string{}, NewBufferedEvents: nil, ClearBufferedEvents: false, VersionHistories: mutableState.GetVersionHistories(), WorkflowRequests: []*persistence.WorkflowRequest{}, }, NewWorkflowSnapshot: nil, WorkflowRequestMode: persistence.CreateWorkflowRequestModeReplicated, Encoding: commonconstants.EncodingType(s.mockShard.GetConfig().EventEncodingType(s.domainID)), DomainName: constants.TestDomainName, }, input) return true })).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Nil(err) } func (s *timerStandbyTaskExecutorSuite) TestProcessDecisionTimeout_Pending() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) startedEvent := test.AddDecisionTaskStartedEvent(mutableState, di.ScheduleID, mutableState.GetExecutionInfo().TaskList, uuid.New()) nextEventID := startedEvent.ID timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeStartToClose), EventID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startedEvent.ID, startedEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now().Add(s.fetchHistoryDuration)) s.mockNDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.clusterName, timerTask.GetDomainID(), timerTask.GetWorkflowID(), timerTask.GetRunID(), common.Int64Ptr(nextEventID), common.Int64Ptr(s.version), nil, nil, ).Return(nil).Times(1) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now().Add(s.discardDuration)) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Equal(ErrTaskDiscarded, err) } func (s *timerStandbyTaskExecutorSuite) TestProcessDecisionTimeout_ScheduleToStartTimer() { execution := types.WorkflowExecution{ WorkflowID: "some random workflow ID", RunID: uuid.New(), } decisionScheduleID := int64(16384) timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: execution.GetWorkflowID(), RunID: execution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeScheduleToStart), EventID: decisionScheduleID, }) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err := s.timerStandbyTaskExecutor.Execute(timerTask) s.Equal(nil, err) } func (s *timerStandbyTaskExecutorSuite) TestProcessDecisionTimeout_Success() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTask := s.newTimerTaskFromInfo(&persistence.DecisionTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, TimeoutType: int(types.TimeoutTypeStartToClose), EventID: mutableState.GetPreviousStartedEventID() - 1, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Nil(err) } func (s *timerStandbyTaskExecutorSuite) TestProcessWorkflowBackoffTimer_Pending() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) mutableState.FlushBufferedEvents() nextEventID := mutableState.GetNextEventID() - 1 timerTask := s.newTimerTaskFromInfo(&persistence.WorkflowBackoffTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, mutableState.GetNextEventID()-1, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, time.Now().Add(s.fetchHistoryDuration)) s.mockNDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.clusterName, timerTask.GetDomainID(), timerTask.GetWorkflowID(), timerTask.GetRunID(), common.Int64Ptr(nextEventID), common.Int64Ptr(s.version), nil, nil, ).Return(nil).Times(1) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, time.Now().Add(s.discardDuration)) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Equal(ErrTaskDiscarded, err) } func (s *timerStandbyTaskExecutorSuite) TestProcessWorkflowBackoffTimer_Success() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) timerTask := s.newTimerTaskFromInfo(&persistence.WorkflowBackoffTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Nil(err) } func (s *timerStandbyTaskExecutorSuite) TestProcessWorkflowTimeout_Pending() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) mutableState.FlushBufferedEvents() nextEventID := decisionCompletionID timerTask := s.newTimerTaskFromInfo(&persistence.WorkflowTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now().Add(s.fetchHistoryDuration)) s.mockNDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.clusterName, timerTask.GetDomainID(), timerTask.GetWorkflowID(), timerTask.GetRunID(), common.Int64Ptr(nextEventID), common.Int64Ptr(s.version), nil, nil, ).Return(nil).Times(1) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now().Add(s.discardDuration)) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Equal(ErrTaskDiscarded, err) } func (s *timerStandbyTaskExecutorSuite) TestProcessWorkflowTimeout_Success() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event := test.AddCompleteWorkflowEvent(mutableState, decisionCompletionID, nil) timerTask := s.newTimerTaskFromInfo(&persistence.WorkflowTimeoutTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil).Once() s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Nil(err) } func (s *timerStandbyTaskExecutorSuite) TestProcessRetryTimeout() { workflowExecution, _, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) timerTask := s.newTimerTaskFromInfo(&persistence.ActivityRetryTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), VisibilityTimestamp: s.timeSource.Now(), }, }) s.mockShard.SetCurrentTime(s.clusterName, s.timeSource.Now()) _, err = s.timerStandbyTaskExecutor.Execute(timerTask) s.Nil(err) } func (s *timerStandbyTaskExecutorSuite) newTimerTaskFromInfo( task persistence.Task, ) Task { return NewHistoryTask(s.mockShard, task, QueueTypeStandbyTimer, s.logger, nil, nil, nil, nil, nil) } func (s *timerStandbyTaskExecutorSuite) TestTransferTaskTimeout() { deleteHistoryEventTask := s.newTimerTaskFromInfo(&persistence.DeleteHistoryEventTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: "workflowID", RunID: "runID", }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(100), }, }) s.timerStandbyTaskExecutor.Execute(deleteHistoryEventTask) } ================================================ FILE: service/history/task/timer_task_executor_base.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/worker/archiver" ) var ( taskRetryPolicy = common.CreateTaskProcessingRetryPolicy() ) type ( timerTaskExecutorBase struct { shard shard.Context archiverClient archiver.Client executionCache execution.Cache logger log.Logger metricsClient metrics.Client config *config.Config throttleRetry *backoff.ThrottleRetry ctx context.Context cancelFn context.CancelFunc } ) func newTimerTaskExecutorBase( shard shard.Context, archiverClient archiver.Client, executionCache execution.Cache, logger log.Logger, metricsClient metrics.Client, config *config.Config, ) *timerTaskExecutorBase { ctx, cancelFn := context.WithCancel(context.Background()) return &timerTaskExecutorBase{ shard: shard, archiverClient: archiverClient, executionCache: executionCache, logger: logger, metricsClient: metricsClient, config: config, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(taskRetryPolicy), backoff.WithRetryableError(persistence.IsTransientError), ), ctx: ctx, cancelFn: cancelFn, } } func (t *timerTaskExecutorBase) executeDeleteHistoryEventTask( ctx context.Context, task *persistence.DeleteHistoryEventTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TimerQueueProcessorScope), t.logger, 0) if err != nil { return err } if mutableState == nil { t.logger.Debug("could not load mutable state while attempting to clean up workflow", tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.WorkflowDomainID(task.DomainID), ) t.metricsClient.IncCounter(metrics.HistoryProcessDeleteHistoryEventScope, metrics.TimerProcessingDeletionTimerNoopDueToMutableStateNotLoading) return nil } if mutableState.IsWorkflowExecutionRunning() { t.logger.Warn("could not clean up workflow, it was running", tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.WorkflowDomainID(task.DomainID), ) t.metricsClient.IncCounter(metrics.HistoryProcessDeleteHistoryEventScope, metrics.TimerProcessingDeletionTimerNoopDueToMutableStateNotLoading) return nil } lastWriteVersion, err := mutableState.GetLastWriteVersion() if err != nil { return err } ok, err := verifyTaskVersion(t.shard, t.logger, task.DomainID, lastWriteVersion, task.Version, task) if err != nil || !ok { return err } domainCacheEntry, err := t.shard.GetDomainCache().GetDomainByID(task.DomainID) if err != nil { return err } clusterConfiguredForHistoryArchival := t.shard.GetService().GetArchivalMetadata().GetHistoryConfig().ClusterConfiguredForArchival() domainConfiguredForHistoryArchival := domainCacheEntry.GetConfig().HistoryArchivalStatus == types.ArchivalStatusEnabled archiveHistory := clusterConfiguredForHistoryArchival && domainConfiguredForHistoryArchival // TODO: @ycyang once archival backfill is in place cluster:paused && domain:enabled should be a nop rather than a delete if archiveHistory { t.metricsClient.IncCounter(metrics.HistoryProcessDeleteHistoryEventScope, metrics.WorkflowCleanupArchiveCount) return t.archiveWorkflow(ctx, task, wfContext, mutableState, domainCacheEntry) } t.metricsClient.IncCounter(metrics.HistoryProcessDeleteHistoryEventScope, metrics.WorkflowCleanupDeleteCount) return t.deleteWorkflow(ctx, task, wfContext, mutableState) } func (t *timerTaskExecutorBase) deleteWorkflow( ctx context.Context, task *persistence.DeleteHistoryEventTask, context execution.Context, msBuilder execution.MutableState, ) error { if err := t.deleteWorkflowHistory(ctx, task, msBuilder); err != nil { return err } if err := t.deleteWorkflowVisibility(ctx, task); err != nil { return err } if err := t.deleteCurrentWorkflowExecution(ctx, task); err != nil { return err } if err := t.deleteActiveClusterSelectionPolicy(ctx, task); err != nil { return err } // it must be the last one due to the nature of workflow execution deletion if err := t.deleteWorkflowExecution(ctx, task); err != nil { return err } // calling clear here to force accesses of mutable state to read database // if this is not called then callers will get mutable state even though its been removed from database context.Clear() return nil } func (t *timerTaskExecutorBase) archiveWorkflow( ctx context.Context, task *persistence.DeleteHistoryEventTask, workflowContext execution.Context, msBuilder execution.MutableState, domainCacheEntry *cache.DomainCacheEntry, ) error { branchToken, err := msBuilder.GetCurrentBranchToken() if err != nil { return err } closeFailoverVersion, err := msBuilder.GetLastWriteVersion() if err != nil { return err } req := &archiver.ClientRequest{ ArchiveRequest: &archiver.ArchiveRequest{ DomainID: task.DomainID, WorkflowID: task.WorkflowID, RunID: task.RunID, DomainName: domainCacheEntry.GetInfo().Name, ShardID: t.shard.GetShardID(), Targets: []archiver.ArchivalTarget{archiver.ArchiveTargetHistory}, URI: domainCacheEntry.GetConfig().HistoryArchivalURI, NextEventID: msBuilder.GetNextEventID(), BranchToken: branchToken, CloseFailoverVersion: closeFailoverVersion, }, CallerService: service.History, AttemptArchiveInline: false, // archive in workflow by default } executionStats, err := workflowContext.LoadExecutionStats(ctx) if err == nil && executionStats.HistorySize < int64(t.config.TimerProcessorHistoryArchivalSizeLimit()) { req.AttemptArchiveInline = true } archiveCtx, cancel := context.WithTimeout(ctx, t.config.TimerProcessorArchivalTimeLimit()) defer cancel() resp, err := t.archiverClient.Archive(archiveCtx, req) if err != nil { return err } // delete workflow history if history archival is not needed or history as been archived inline if resp.HistoryArchivedInline { t.metricsClient.IncCounter(metrics.HistoryProcessDeleteHistoryEventScope, metrics.WorkflowCleanupDeleteHistoryInlineCount) if err := t.deleteWorkflowHistory(ctx, task, msBuilder); err != nil { return err } } // delete visibility record here regardless if it's been archived inline or not // since the entire record is included as part of the archive request. if err := t.deleteWorkflowVisibility(ctx, task); err != nil { return err } if err := t.deleteActiveClusterSelectionPolicy(ctx, task); err != nil { return err } if err := t.deleteCurrentWorkflowExecution(ctx, task); err != nil { return err } if err := t.deleteWorkflowExecution(ctx, task); err != nil { return err } // calling clear here to force accesses of mutable state to read database // if this is not called then callers will get mutable state even though its been removed from database workflowContext.Clear() return nil } func (t *timerTaskExecutorBase) deleteWorkflowExecution( ctx context.Context, task *persistence.DeleteHistoryEventTask, ) error { domainName, err := t.shard.GetDomainCache().GetDomainName(task.DomainID) if err != nil { return err } op := func(ctx context.Context) error { return t.shard.GetExecutionManager().DeleteWorkflowExecution(ctx, &persistence.DeleteWorkflowExecutionRequest{ DomainID: task.DomainID, WorkflowID: task.WorkflowID, RunID: task.RunID, DomainName: domainName, }) } return t.throttleRetry.Do(ctx, op) } func (t *timerTaskExecutorBase) deleteCurrentWorkflowExecution( ctx context.Context, task *persistence.DeleteHistoryEventTask, ) error { domainName, err := t.shard.GetDomainCache().GetDomainName(task.DomainID) if err != nil { return err } op := func(ctx context.Context) error { return t.shard.GetExecutionManager().DeleteCurrentWorkflowExecution(ctx, &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: task.DomainID, WorkflowID: task.WorkflowID, RunID: task.RunID, DomainName: domainName, }) } return t.throttleRetry.Do(ctx, op) } func (t *timerTaskExecutorBase) deleteActiveClusterSelectionPolicy( ctx context.Context, task *persistence.DeleteHistoryEventTask, ) error { op := func(ctx context.Context) error { return t.shard.GetExecutionManager().DeleteActiveClusterSelectionPolicy(ctx, task.DomainID, task.WorkflowID, task.RunID) } return t.throttleRetry.Do(ctx, op) } func (t *timerTaskExecutorBase) deleteWorkflowHistory( ctx context.Context, task *persistence.DeleteHistoryEventTask, msBuilder execution.MutableState, ) error { op := func(ctx context.Context) error { branchToken, err := msBuilder.GetCurrentBranchToken() if err != nil { return err } domainName, err := t.shard.GetDomainCache().GetDomainName(task.DomainID) if err != nil { return err } return t.shard.GetHistoryManager().DeleteHistoryBranch(ctx, &persistence.DeleteHistoryBranchRequest{ BranchToken: branchToken, ShardID: common.IntPtr(t.shard.GetShardID()), DomainName: domainName, }) } return t.throttleRetry.Do(ctx, op) } func (t *timerTaskExecutorBase) deleteWorkflowVisibility( ctx context.Context, task *persistence.DeleteHistoryEventTask, ) error { domain, errorDomainName := t.shard.GetDomainCache().GetDomainName(task.DomainID) if errorDomainName != nil { return errorDomainName } op := func(ctx context.Context) error { request := &persistence.VisibilityDeleteWorkflowExecutionRequest{ DomainID: task.DomainID, Domain: domain, WorkflowID: task.WorkflowID, RunID: task.RunID, TaskID: task.TaskID, } // TODO: expose GetVisibilityManager method on shardContext interface return t.shard.GetService().GetVisibilityManager().DeleteWorkflowExecution(ctx, request) // delete from db } return t.throttleRetry.Do(ctx, op) } func (t *timerTaskExecutorBase) Stop() { t.logger.Info("Stopping timerTaskExecutorBase") t.cancelFn() } ================================================ FILE: service/history/task/timer_task_executor_base_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" executioncache "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/worker/archiver" ) type ( timerQueueTaskExecutorBaseSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockWorkflowExecutionContext *execution.MockContext mockMutableState *execution.MockMutableState mockExecutionManager *mocks.ExecutionManager mockVisibilityManager *mocks.VisibilityManager mockHistoryV2Manager *mocks.HistoryV2Manager mockArchivalClient *archiver.MockClient timerQueueTaskExecutorBase *timerTaskExecutorBase } ) func TestTimerQueueTaskExecutorBaseSuite(t *testing.T) { s := new(timerQueueTaskExecutorBaseSuite) suite.Run(t, s) } func (s *timerQueueTaskExecutorBaseSuite) SetupSuite() { } func (s *timerQueueTaskExecutorBaseSuite) TearDownSuite() { } func (s *timerQueueTaskExecutorBaseSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockWorkflowExecutionContext = execution.NewMockContext(s.controller) s.mockMutableState = execution.NewMockMutableState(s.controller) testConfig := config.NewForTest() s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, testConfig, ) s.mockExecutionManager = s.mockShard.Resource.ExecutionMgr s.mockVisibilityManager = s.mockShard.Resource.VisibilityMgr s.mockHistoryV2Manager = s.mockShard.Resource.HistoryMgr s.mockArchivalClient = archiver.NewMockClient(s.controller) logger := s.mockShard.GetLogger() s.timerQueueTaskExecutorBase = newTimerTaskExecutorBase( s.mockShard, s.mockArchivalClient, nil, logger, s.mockShard.GetMetricsClient(), testConfig, ) } func (s *timerQueueTaskExecutorBaseSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) s.timerQueueTaskExecutorBase.Stop() } func (s *timerQueueTaskExecutorBaseSuite) TestDeleteWorkflow_NoErr() { task := &persistence.DeleteHistoryEventTask{ TaskData: persistence.TaskData{ TaskID: 12345, VisibilityTimestamp: time.Now(), }, } executionInfo := types.WorkflowExecution{ WorkflowID: task.WorkflowID, RunID: task.RunID, } wfContext := execution.NewContext(task.DomainID, executionInfo, s.mockShard, s.mockExecutionManager, log.NewNoop()) s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("Sample", nil).AnyTimes() s.mockExecutionManager.On("DeleteCurrentWorkflowExecution", mock.Anything, mock.Anything).Return(nil).Once() s.mockExecutionManager.On("DeleteWorkflowExecution", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.mockExecutionManager.On("DeleteActiveClusterSelectionPolicy", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.mockHistoryV2Manager.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(nil).Once() s.mockVisibilityManager.On("DeleteWorkflowExecution", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.mockMutableState.EXPECT().GetCurrentBranchToken().Return([]byte{1, 2, 3}, nil).Times(1) s.mockMutableState.EXPECT().GetLastWriteVersion().Return(int64(1234), nil).AnyTimes() err := s.timerQueueTaskExecutorBase.deleteWorkflow(context.Background(), task, wfContext, s.mockMutableState) s.NoError(err) } func (s *timerQueueTaskExecutorBaseSuite) TestArchiveHistory_NoErr_InlineArchivalFailed() { s.mockWorkflowExecutionContext.EXPECT().LoadExecutionStats(gomock.Any()).Return(&persistence.ExecutionStats{ HistorySize: 1024, }, nil).Times(1) s.mockWorkflowExecutionContext.EXPECT().Clear().Times(1) s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("Sample", nil) s.mockMutableState.EXPECT().GetCurrentBranchToken().Return([]byte{1, 2, 3}, nil).Times(1) s.mockMutableState.EXPECT().GetLastWriteVersion().Return(int64(1234), nil).Times(1) s.mockMutableState.EXPECT().GetNextEventID().Return(int64(101)).Times(1) s.mockShard.Resource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("Sample", nil).AnyTimes() s.mockExecutionManager.On("DeleteCurrentWorkflowExecution", mock.Anything, mock.Anything).Return(nil).Once() s.mockExecutionManager.On("DeleteWorkflowExecution", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.mockExecutionManager.On("DeleteActiveClusterSelectionPolicy", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.mockVisibilityManager.On("DeleteWorkflowExecution", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.mockArchivalClient.EXPECT().Archive(gomock.Any(), gomock.Cond(func(req *archiver.ClientRequest) bool { return req.CallerService == service.History && req.AttemptArchiveInline && req.ArchiveRequest.Targets[0] == archiver.ArchiveTargetHistory })).Return(&archiver.ClientResponse{ HistoryArchivedInline: false, }, nil) domainCacheEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{}, &persistence.DomainConfig{}, false, nil, 0, nil, 0, 0, 0, ) err := s.timerQueueTaskExecutorBase.archiveWorkflow( context.Background(), &persistence.DeleteHistoryEventTask{}, s.mockWorkflowExecutionContext, s.mockMutableState, domainCacheEntry, ) s.NoError(err) } func (s *timerQueueTaskExecutorBaseSuite) TestArchiveHistory_SendSignalErr() { s.mockWorkflowExecutionContext.EXPECT().LoadExecutionStats(gomock.Any()).Return(&persistence.ExecutionStats{ HistorySize: 1024 * 1024 * 1024, }, nil).Times(1) s.mockMutableState.EXPECT().GetCurrentBranchToken().Return([]byte{1, 2, 3}, nil).Times(1) s.mockMutableState.EXPECT().GetLastWriteVersion().Return(int64(1234), nil).Times(1) s.mockMutableState.EXPECT().GetNextEventID().Return(int64(101)).Times(1) s.mockArchivalClient.EXPECT().Archive(gomock.Any(), gomock.Cond(func(req *archiver.ClientRequest) bool { return req.CallerService == service.History && !req.AttemptArchiveInline && req.ArchiveRequest.Targets[0] == archiver.ArchiveTargetHistory })).Return(nil, errors.New("failed to send signal")) domainCacheEntry := cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{}, &persistence.DomainConfig{}, false, nil, 0, nil, 0, 0, 0, ) err := s.timerQueueTaskExecutorBase.archiveWorkflow( context.Background(), &persistence.DeleteHistoryEventTask{}, s.mockWorkflowExecutionContext, s.mockMutableState, domainCacheEntry, ) s.Error(err) } func TestExecuteDeleteHistoryEventTask(t *testing.T) { tests := []struct { name string setupMocks func(*gomock.Controller) (*timerTaskExecutorBase, *persistence.DeleteHistoryEventTask) expectedError error }{ { name: "mutable was not able to be loaded", setupMocks: func(controller *gomock.Controller) (*timerTaskExecutorBase, *persistence.DeleteHistoryEventTask) { mockShard := shard.NewTestContext( t, controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) mockShard.Resource.DomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{}, &persistence.DomainConfig{}, false, nil, 0, nil, 0, 0, 0, ), nil) timerTask := &persistence.DeleteHistoryEventTask{ TaskData: persistence.TaskData{ TaskID: 123, VisibilityTimestamp: time.Now(), }, WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "test-domain", WorkflowID: "wf", RunID: "run", }, } wfContext := execution.NewContext( timerTask.DomainID, types.WorkflowExecution{ WorkflowID: timerTask.WorkflowID, RunID: timerTask.RunID, }, mockShard, mockShard.Resource.ExecutionMgr, mockShard.GetLogger(), ) executionCache := executioncache.NewMockCache(controller) executionCache.EXPECT().GetOrCreateWorkflowExecutionWithTimeout( gomock.Any(), gomock.Any(), gomock.Any(), ).Return(wfContext, func(error) {}, nil) mockExecutionMgr := mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(nil, &types.EntityNotExistsError{}).Once() return newTimerTaskExecutorBase( mockShard, &archiver.MockClient{}, executionCache, mockShard.GetLogger(), mockShard.GetMetricsClient(), mockShard.GetConfig(), ), timerTask }, expectedError: nil, }, { name: "mutable showed that the workflow was still running ", setupMocks: func(controller *gomock.Controller) (*timerTaskExecutorBase, *persistence.DeleteHistoryEventTask) { mockShard := shard.NewTestContext( t, controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, config.NewForTest(), ) mockShard.Resource.DomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{}, &persistence.DomainConfig{}, false, nil, 0, nil, 0, 0, 0, ), nil).AnyTimes() mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), "domain", "wf", "run").Return(&types.ActiveClusterInfo{}, nil).AnyTimes() timerTask := &persistence.DeleteHistoryEventTask{ TaskData: persistence.TaskData{ TaskID: 123, VisibilityTimestamp: time.Now(), }, WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "domain", WorkflowID: "wf", RunID: "run", }, } wfContext := execution.NewContext( timerTask.DomainID, types.WorkflowExecution{ WorkflowID: timerTask.WorkflowID, RunID: timerTask.RunID, }, mockShard, mockShard.Resource.ExecutionMgr, mockShard.GetLogger(), ) executionCache := executioncache.NewMockCache(controller) executionCache.EXPECT().GetOrCreateWorkflowExecutionWithTimeout( gomock.Any(), gomock.Any(), gomock.Any(), ).Return(wfContext, func(error) {}, nil) mockExecutionMgr := mockShard.Resource.ExecutionMgr mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{ State: &persistence.WorkflowMutableState{ ExecutionStats: &persistence.ExecutionStats{}, ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: "domain", WorkflowID: "wf", RunID: "run", CloseStatus: 0, State: persistence.WorkflowStateRunning, }, }, }, nil) return newTimerTaskExecutorBase( mockShard, &archiver.MockClient{}, executionCache, mockShard.GetLogger(), mockShard.GetMetricsClient(), mockShard.GetConfig(), ), timerTask }, expectedError: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() executor, timerTask := tt.setupMocks(controller) err := executor.executeDeleteHistoryEventTask(context.Background(), timerTask) if tt.expectedError != nil { require.Error(t, err) require.Equal(t, tt.expectedError, err) } else { require.NoError(t, err) } }) } } ================================================ FILE: service/history/task/transfer_active_task_executor.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2021 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "errors" "fmt" "strings" "time" "github.com/pborman/uuid" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/reset" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/simulation" "github.com/uber/cadence/service/history/workflowcache" "github.com/uber/cadence/service/worker/archiver" "github.com/uber/cadence/service/worker/parentclosepolicy" ) const ( resetWorkflowTimeout = 30 * time.Second ) var ( // ErrMissingRequestCancelInfo indicates missing request cancel info ErrMissingRequestCancelInfo = &types.InternalServiceError{Message: "unable to get request cancel info"} // ErrMissingSignalInfo indicates missing signal external ErrMissingSignalInfo = &types.InternalServiceError{Message: "unable to get signal info"} ) var ( errUnknownTransferTask = errors.New("unknown transfer task") errWorkflowBusy = errors.New("unable to get workflow execution lock within specified timeout") errWorkflowRateLimited = errors.New("workflow is being rate limited for making too many requests") ) type ( transferActiveTaskExecutor struct { *transferTaskExecutorBase historyClient history.Client parentClosePolicyClient parentclosepolicy.Client workflowResetter reset.WorkflowResetter wfIDCache workflowcache.WFCache } generatorF = func(taskGenerator execution.MutableStateTaskGenerator) error ) // NewTransferActiveTaskExecutor creates a new task executor for active transfer task func NewTransferActiveTaskExecutor( shard shard.Context, archiverClient archiver.Client, executionCache execution.Cache, workflowResetter reset.WorkflowResetter, logger log.Logger, config *config.Config, wfIDCache workflowcache.WFCache, ) Executor { return &transferActiveTaskExecutor{ transferTaskExecutorBase: newTransferTaskExecutorBase( shard, archiverClient, executionCache, logger, config, ), historyClient: shard.GetService().GetHistoryClient(), parentClosePolicyClient: parentclosepolicy.NewClient( shard.GetMetricsClient(), shard.GetLogger(), shard.GetService().GetSDKClient(), config.NumParentClosePolicySystemWorkflows(), ), workflowResetter: workflowResetter, wfIDCache: wfIDCache, } } func (t *transferActiveTaskExecutor) Execute(task Task) (ExecuteResponse, error) { simulation.LogEvents(simulation.E{ EventName: simulation.EventNameExecuteHistoryTask, Host: t.shard.GetConfig().HostName, ShardID: t.shard.GetShardID(), DomainID: task.GetDomainID(), WorkflowID: task.GetWorkflowID(), RunID: task.GetRunID(), Payload: map[string]any{ "task_category": persistence.HistoryTaskCategoryTransfer.Name(), "task_type": task.GetTaskType(), "task_key": task.GetTaskKey(), }, }) scope := getOrCreateDomainTaggedScope(t.shard, GetTransferTaskMetricsScope(task.GetTaskType(), true), task.GetDomainID(), t.logger) executeResponse := ExecuteResponse{ Scope: scope, IsActiveTask: true, } ctx, cancel := context.WithTimeout(context.Background(), taskDefaultTimeout) defer cancel() switch transferTask := task.GetInfo().(type) { case *persistence.ActivityTask: return executeResponse, t.processActivityTask(ctx, transferTask) case *persistence.DecisionTask: return executeResponse, t.processDecisionTask(ctx, transferTask) case *persistence.CloseExecutionTask: return executeResponse, t.processCloseExecution(ctx, transferTask) case *persistence.RecordWorkflowClosedTask: return executeResponse, t.processRecordWorkflowClosed(ctx, transferTask) case *persistence.RecordChildExecutionCompletedTask: return executeResponse, t.processRecordChildExecutionCompleted(ctx, transferTask) case *persistence.CancelExecutionTask: return executeResponse, t.processCancelExecution(ctx, transferTask) case *persistence.SignalExecutionTask: return executeResponse, t.processSignalExecution(ctx, transferTask) case *persistence.StartChildExecutionTask: return executeResponse, t.processStartChildExecution(ctx, transferTask) case *persistence.RecordWorkflowStartedTask: return executeResponse, t.processRecordWorkflowStarted(ctx, transferTask) case *persistence.ResetWorkflowTask: return executeResponse, t.processResetWorkflow(ctx, transferTask) case *persistence.UpsertWorkflowSearchAttributesTask: return executeResponse, t.processUpsertWorkflowSearchAttributes(ctx, transferTask) default: return executeResponse, errUnknownTransferTask } } // Empty func for now func (t *transferActiveTaskExecutor) Stop() {} func (t *transferActiveTaskExecutor) processActivityTask( ctx context.Context, task *persistence.ActivityTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TransferQueueProcessorScope), t.logger, task.ScheduleID) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } domainName := mutableState.GetDomainEntry().GetInfo().Name ai, ok := mutableState.GetActivityInfo(task.ScheduleID) if !ok { t.logger.Debug("Potentially duplicate ", tag.TaskID(task.TaskID), tag.WorkflowScheduleID(task.ScheduleID), tag.TaskType(persistence.TransferTaskTypeActivityTask)) return nil } ok, err = verifyTaskVersion(t.shard, t.logger, task.DomainID, ai.Version, task.Version, task) if err != nil || !ok { return err } timeout := min(ai.ScheduleToStartTimeout, constants.MaxTaskTimeout) taskList := types.TaskList{ Name: ai.TaskList, Kind: ai.TaskListKind.Ptr(), } if taskList.Name == "" { taskList.Name = task.TaskList } // release the context lock since we no longer need mutable state builder and // the rest of logic is making RPC call, which takes time. release(nil) // Rate limiting task processing requests if !t.allowTask(task) { return errWorkflowRateLimited } pushActivityInfo := &pushActivityToMatchingInfo{ activityScheduleToStartTimeout: timeout, tasklist: taskList, partitionConfig: mutableState.GetExecutionInfo().PartitionConfig, } err = t.pushActivity(ctx, task, pushActivityInfo) if err == nil { scope := common.NewPerTaskListScope(domainName, taskList.Name, taskList.GetKind(), t.metricsClient, metrics.TransferActiveTaskActivityScope) scope.RecordTimer(metrics.ScheduleToStartHistoryQueueLatencyPerTaskList, time.Since(task.GetVisibilityTimestamp())) } return err } func (t *transferActiveTaskExecutor) processDecisionTask( ctx context.Context, task *persistence.DecisionTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TransferQueueProcessorScope), t.logger, task.ScheduleID) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } decision, found := mutableState.GetDecisionInfo(task.ScheduleID) if !found { t.logger.Debug("Potentially duplicate ", tag.TaskID(task.TaskID), tag.WorkflowScheduleID(task.ScheduleID), tag.TaskType(persistence.TransferTaskTypeDecisionTask)) return nil } ok, err := verifyTaskVersion(t.shard, t.logger, task.DomainID, decision.Version, task.Version, task) if err != nil || !ok { return err } domainName := mutableState.GetDomainEntry().GetInfo().Name executionInfo := mutableState.GetExecutionInfo() workflowTimeout := executionInfo.WorkflowTimeout decisionTimeout := min(workflowTimeout, constants.MaxTaskTimeout) // NOTE: previously this section check whether mutable state has enabled // sticky decision, if so convert the decision to a sticky decision. // that logic has a bug which timer task for that sticky decision is not generated // the correct logic should check whether the decision task is a sticky decision // task or not. taskList := types.TaskList{ Name: task.TaskList, Kind: executionInfo.TaskListKind.Ptr(), } if mutableState.GetExecutionInfo().TaskList != task.TaskList { // this decision is an sticky decision // there shall already be an timer set taskList.Kind = types.TaskListKindSticky.Ptr() decisionTimeout = executionInfo.StickyScheduleToStartTimeout } // TODO: for normal decision, we don't know if there's a scheduleToStart // timeout timer task associated with the decision since it's determined // when creating the decision and the result is not persisted in mutable // state. // If we calculated the timeout again here, the timeout may be different, // or even lost the decision if there's originally no timeout timer task // for the decision. Using MaxTaskTimeout here for now so at least no // decision will be lost. // release the context lock since we no longer need mutable state builder and // the rest of logic is making RPC call, which takes time. release(nil) // Rate limiting task processing requests if !t.allowTask(task) { return errWorkflowRateLimited } err = t.pushDecision(ctx, task, &pushDecisionToMatchingInfo{ decisionScheduleToStartTimeout: decisionTimeout, tasklist: taskList, partitionConfig: mutableState.GetExecutionInfo().PartitionConfig, }) if _, ok := err.(*types.StickyWorkerUnavailableError); ok { // sticky worker is unavailable, switch to non-sticky task list taskList = types.TaskList{ Name: mutableState.GetExecutionInfo().TaskList, } // Continue to use sticky schedule_to_start timeout as TTL for the matching task. Because the schedule_to_start // timeout timer task is already created which will timeout this task if no worker pick it up in 5s anyway. // There is no need to reset sticky, because if this task is picked by new worker, the new worker will reset // the sticky queue to a new one. However, if worker is completely down, that schedule_to_start timeout task // will re-create a new non-sticky task and reset sticky. err = t.pushDecision(ctx, task, &pushDecisionToMatchingInfo{ decisionScheduleToStartTimeout: decisionTimeout, tasklist: taskList, partitionConfig: mutableState.GetExecutionInfo().PartitionConfig, }) } if err == nil { scope := common.NewPerTaskListScope(domainName, taskList.Name, taskList.GetKind(), t.metricsClient, metrics.TransferActiveTaskDecisionScope) scope.RecordTimer(metrics.ScheduleToStartHistoryQueueLatencyPerTaskList, time.Since(task.GetVisibilityTimestamp())) } return err } func (t *transferActiveTaskExecutor) allowTask(task persistence.Task) bool { return t.wfIDCache.AllowInternal(task.GetDomainID(), task.GetWorkflowID()) } func (t *transferActiveTaskExecutor) processCloseExecution( ctx context.Context, task *persistence.CloseExecutionTask, ) error { return t.processCloseExecutionTaskHelper(ctx, task, true, true, true) } func (t *transferActiveTaskExecutor) processRecordWorkflowClosed( ctx context.Context, task *persistence.RecordWorkflowClosedTask, ) error { return t.processCloseExecutionTaskHelper(ctx, task, true, false, false) } func (t *transferActiveTaskExecutor) processRecordChildExecutionCompleted( ctx context.Context, task *persistence.RecordChildExecutionCompletedTask, ) error { return t.processCloseExecutionTaskHelper(ctx, task, false, true, false) } // TODO: this helper function performs three operations: // 1. publish workflow closed visibility record // 2. if has parent workflow, reply to the parent workflow // 3. if has child workflow(s), apply parent close policy // ideally we should separate them into 3 functions, but it is complicated // but the fact that we want to release mutable state lock as early as possible // we should see if there's a better way to organize the code func (t *transferActiveTaskExecutor) processCloseExecutionTaskHelper( ctx context.Context, task persistence.Task, recordWorkflowClosed bool, replyToParentWorkflowIfApplicable bool, applyParentClosePolicy bool, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.GetDomainID(), getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TransferQueueProcessorScope), t.logger, 0) if err != nil { return err } if mutableState == nil || mutableState.IsWorkflowExecutionRunning() { return nil } lastWriteVersion, err := mutableState.GetLastWriteVersion() if err != nil { return err } ok, err := verifyTaskVersion(t.shard, t.logger, task.GetDomainID(), lastWriteVersion, task.GetVersion(), task) if err != nil || !ok { return err } domainEntry, err := t.shard.GetDomainCache().GetDomainByID(task.GetDomainID()) if err != nil { return err } executionInfo := mutableState.GetExecutionInfo() completionEvent, err := mutableState.GetCompletionEvent(ctx) if err != nil { return err } wfCloseTime := completionEvent.GetTimestamp() parentDomainID := executionInfo.ParentDomainID parentWorkflowID := executionInfo.ParentWorkflowID parentRunID := executionInfo.ParentRunID initiatedID := executionInfo.InitiatedID workflowTypeName := executionInfo.WorkflowTypeName workflowCloseTimestamp := wfCloseTime workflowCloseStatus := persistence.ToInternalWorkflowExecutionCloseStatus(executionInfo.CloseStatus) workflowHistoryLength := mutableState.GetNextEventID() - 1 isCron := len(executionInfo.CronSchedule) > 0 cronSchedule := executionInfo.CronSchedule numClusters := (int16)(len(domainEntry.GetReplicationConfig().Clusters)) updateTimestamp := t.shard.GetTimeSource().Now() startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return err } workflowStartTimestamp := startEvent.GetTimestamp() workflowExecutionTimestamp := getWorkflowExecutionTimestamp(mutableState, startEvent) visibilityMemo := getWorkflowMemo(executionInfo.Memo) searchAttr := executionInfo.SearchAttributes headers := getWorkflowHeaders(startEvent) domainName := mutableState.GetDomainEntry().GetInfo().Name children := mutableState.GetPendingChildExecutionInfos() executionStatus := getWorkflowExecutionStatus(mutableState, completionEvent) // Calculate ScheduledExecutionTime scheduledExecutionTimestamp := startEvent.GetTimestamp() if startEvent.WorkflowExecutionStartedEventAttributes != nil && startEvent.WorkflowExecutionStartedEventAttributes.GetFirstDecisionTaskBackoffSeconds() > 0 { scheduledExecutionTimestamp = startEvent.GetTimestamp() + int64(startEvent.WorkflowExecutionStartedEventAttributes.GetFirstDecisionTaskBackoffSeconds())*int64(time.Second) } // we've gathered all necessary information from mutable state. // release the context lock since we no longer need mutable state builder and // the rest of logic is making RPC call, which takes time. release(nil) // publish workflow closed visibility records if recordWorkflowClosed { if err := t.recordWorkflowClosed( ctx, task.GetDomainID(), task.GetWorkflowID(), task.GetRunID(), workflowTypeName, workflowStartTimestamp, workflowExecutionTimestamp.UnixNano(), workflowCloseTimestamp, *workflowCloseStatus, workflowHistoryLength, task.GetTaskID(), visibilityMemo, executionInfo.TaskList, isCron, cronSchedule, numClusters, updateTimestamp.UnixNano(), searchAttr, headers, executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute(), executionStatus, scheduledExecutionTimestamp, ); err != nil { return err } } // Communicate the result to parent execution if this is Child Workflow execution // and parent domain is in the same cluster replyToParentWorkflow := replyToParentWorkflowIfApplicable && mutableState.HasParentExecution() && executionInfo.CloseStatus != persistence.WorkflowCloseStatusContinuedAsNew if replyToParentWorkflow { recordChildCompletionCtx, cancel := context.WithTimeout(ctx, taskRPCCallTimeout) defer cancel() err := t.historyClient.RecordChildExecutionCompleted(recordChildCompletionCtx, &types.RecordChildExecutionCompletedRequest{ DomainUUID: parentDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: parentWorkflowID, RunID: parentRunID, }, InitiatedID: initiatedID, CompletedExecution: &types.WorkflowExecution{ WorkflowID: task.GetWorkflowID(), RunID: task.GetRunID(), }, CompletionEvent: completionEvent, }) // Check to see if the error is non-transient, in which case reset the error and continue with processing switch err.(type) { case *types.EntityNotExistsError, *types.WorkflowExecutionAlreadyCompletedError: err = nil } if err != nil { return err } } if applyParentClosePolicy { parentExecution := types.WorkflowExecution{ WorkflowID: task.GetWorkflowID(), RunID: task.GetRunID(), } err := t.processParentClosePolicy(ctx, task.GetDomainID(), domainName, &parentExecution, children) if err != nil { return err } } return nil } func (t *transferActiveTaskExecutor) processCancelExecution( ctx context.Context, task *persistence.CancelExecutionTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TransferQueueProcessorScope), t.logger, task.InitiatedID) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } initiatedEventID := task.InitiatedID requestCancelInfo, ok := mutableState.GetRequestCancelInfo(initiatedEventID) if !ok { return nil } ok, err = verifyTaskVersion(t.shard, t.logger, task.DomainID, requestCancelInfo.Version, task.Version, task) if err != nil || !ok { return err } targetDomainEntry, err := t.shard.GetDomainCache().GetDomainByID(task.TargetDomainID) if err != nil { // TODO: handle the case where target domain does not exist return err } targetDomainName := targetDomainEntry.GetInfo().Name // handle workflow cancel itself if task.DomainID == task.TargetDomainID && task.WorkflowID == task.TargetWorkflowID { // it does not matter if the run ID is a mismatch err = requestCancelExternalExecutionFailed( ctx, t.logger, task, wfContext, targetDomainName, task.TargetWorkflowID, task.TargetRunID, t.shard.GetTimeSource().Now(), ) return err } if err = requestCancelExternalExecutionWithRetry( ctx, t.historyClient, task, targetDomainName, requestCancelInfo.CancelRequestID, ); err != nil { t.logger.Error("Failed to cancel external workflow execution", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TargetWorkflowDomainID(task.TargetDomainID), tag.TargetWorkflowID(task.TargetWorkflowID), tag.TargetWorkflowRunID(task.TargetRunID), tag.Error(err)) // Check to see if the error is non-transient, in which case add RequestCancelFailed // event and complete transfer task by setting the err = nil if common.IsServiceTransientError(err) || common.IsContextTimeoutError(err) { // for retryable error just return return err } return requestCancelExternalExecutionFailed( ctx, t.logger, task, wfContext, targetDomainName, task.TargetWorkflowID, task.TargetRunID, t.shard.GetTimeSource().Now(), ) } t.logger.Debug("RequestCancel successfully recorded to external workflow execution", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TargetWorkflowDomainID(task.TargetDomainID), tag.TargetWorkflowID(task.TargetWorkflowID), tag.TargetWorkflowRunID(task.TargetRunID)) // Record ExternalWorkflowExecutionCancelRequested in source execution return requestCancelExternalExecutionCompleted( ctx, t.logger, task, wfContext, targetDomainName, task.TargetWorkflowID, task.TargetRunID, t.shard.GetTimeSource().Now(), ) } func (t *transferActiveTaskExecutor) processSignalExecution( ctx context.Context, task *persistence.SignalExecutionTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TransferQueueProcessorScope), t.logger, task.InitiatedID) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } initiatedEventID := task.InitiatedID signalInfo, ok := mutableState.GetSignalInfo(initiatedEventID) if !ok { // TODO: here we should also RemoveSignalMutableState from target workflow // Otherwise, target SignalRequestID still can leak if shard restart after signalExternalExecutionCompleted // To do that, probably need to add the SignalRequestID in transfer return nil } ok, err = verifyTaskVersion(t.shard, t.logger, task.DomainID, signalInfo.Version, task.Version, task) if err != nil || !ok { return err } targetDomainEntry, err := t.shard.GetDomainCache().GetDomainByID(task.TargetDomainID) if err != nil { // TODO: handle the case where target domain does not exist return err } targetDomainName := targetDomainEntry.GetInfo().Name // handle workflow signal itself if task.DomainID == task.TargetDomainID && task.WorkflowID == task.TargetWorkflowID { // it does not matter if the run ID is a mismatch return signalExternalExecutionFailed( ctx, t.logger, task, wfContext, targetDomainName, task.TargetWorkflowID, task.TargetRunID, signalInfo.Control, t.shard.GetTimeSource().Now(), ) } if err = signalExternalExecutionWithRetry( ctx, t.historyClient, task, targetDomainName, signalInfo, ); err != nil { if !common.IsExpectedError(err) { t.logger.Error("Failed to signal external workflow execution", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TargetWorkflowDomainID(task.TargetDomainID), tag.TargetWorkflowID(task.TargetWorkflowID), tag.TargetWorkflowRunID(task.TargetRunID), tag.Error(err)) } // Check to see if the error is non-transient, in which case add SignalFailed // event and complete transfer task by setting the err = nil if common.IsServiceTransientError(err) || common.IsContextTimeoutError(err) { // for retryable error just return return err } return signalExternalExecutionFailed( ctx, t.logger, task, wfContext, targetDomainName, task.TargetWorkflowID, task.TargetRunID, signalInfo.Control, t.shard.GetTimeSource().Now(), ) } t.logger.Debug("Signal successfully recorded to external workflow execution", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TargetWorkflowDomainID(task.TargetDomainID), tag.TargetWorkflowID(task.TargetWorkflowID), tag.TargetWorkflowRunID(task.TargetRunID)) err = signalExternalExecutionCompleted( ctx, t.logger, task, wfContext, targetDomainName, task.TargetWorkflowID, task.TargetRunID, signalInfo.Control, t.shard.GetTimeSource().Now(), ) if err != nil { return err } // release the context lock since we no longer need mutable state builder and // the rest of logic is making RPC call, which takes time. release(retError) // remove signalRequestedID from target workflow, after Signal detail is removed from source workflow return removeSignalMutableStateWithRetry(ctx, t.historyClient, task, signalInfo.SignalRequestID) } func (t *transferActiveTaskExecutor) processStartChildExecution( ctx context.Context, task *persistence.StartChildExecutionTask, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TransferQueueProcessorScope), t.logger, task.InitiatedID) if err != nil { return err } if mutableState == nil { return nil } initiatedEventID := task.InitiatedID childInfo, ok := mutableState.GetChildExecutionInfo(initiatedEventID) if !ok { return nil } ok, err = verifyTaskVersion(t.shard, t.logger, task.DomainID, childInfo.Version, task.Version, task) if err != nil || !ok { return err } workflowRunning := mutableState.IsWorkflowExecutionRunning() childStarted := childInfo.StartedID != constants.EmptyEventID if !workflowRunning && (!childStarted || childInfo.ParentClosePolicy != types.ParentClosePolicyAbandon) { // three cases here: // case 1: workflow not running, child started, close policy is not abandon // case 2 & 3: workflow not running, child not started, close policy is or is not abandon return nil } // Get target domain name var targetDomainName string var targetDomainEntry *cache.DomainCacheEntry if targetDomainEntry, err = t.shard.GetDomainCache().GetDomainByID(task.TargetDomainID); err != nil { if _, ok := err.(*types.EntityNotExistsError); !ok { return err } // TODO: handle the case where target domain does not exist // it is possible that the domain got deleted. Use domainID instead as this is only needed for the history event targetDomainName = task.TargetDomainID } else { targetDomainName = targetDomainEntry.GetInfo().Name } // ChildExecution already started, just create DecisionTask and complete transfer task // if parent already closed, since child workflow started event already written to history, // still schedule the decision if the parent close policy is Abandon. // If parent close policy cancel, a decision will be scheduled when processing that close policy. if childStarted { // NOTE: do not access anything related mutable state after this lock release // release the context lock since we no longer need mutable state builder and // the rest of logic is making RPC call, which takes time. release(nil) return createFirstDecisionTask( ctx, t.historyClient, task.TargetDomainID, &types.WorkflowExecution{ WorkflowID: childInfo.StartedWorkflowID, RunID: childInfo.StartedRunID, }) } // remaining 2 cases: // workflow running, child not started, close policy is or is not abandon initiatedEvent, err := mutableState.GetChildExecutionInitiatedEvent(ctx, initiatedEventID) if err != nil { return err } attributes := initiatedEvent.StartChildWorkflowExecutionInitiatedEventAttributes childRunID, err := startWorkflowWithRetry( ctx, t.historyClient, t.shard.GetTimeSource(), t.shard.GetDomainCache(), task, targetDomainName, childInfo.CreateRequestID, attributes, mutableState.GetExecutionInfo().PartitionConfig, mutableState.GetExecutionInfo().ActiveClusterSelectionPolicy, ) if err != nil { // Check to see if the error is non-transient, in which case add StartChildWorkflowExecutionFailed // event and complete transfer task by setting the err = nil switch err.(type) { // TODO: we should also handle domain not exist error here // but we probably need to introduce a new error type for DomainNotExists, // for now when getting an EntityNotExists error, we can't tell if it's domain or workflow. case *types.WorkflowExecutionAlreadyStartedError: t.logger.Info("workflow has already started", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TargetWorkflowDomainID(task.TargetDomainID), tag.TargetWorkflowID(attributes.WorkflowID), tag.WorkflowRequestID(childInfo.CreateRequestID), ) err = recordStartChildExecutionFailed(ctx, t.logger, task, wfContext, attributes, t.shard.GetTimeSource().Now()) default: t.logger.Error("Failed to start child workflow execution", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TargetWorkflowDomainID(task.TargetDomainID), tag.TargetWorkflowID(attributes.WorkflowID), tag.Error(err)) } return err } t.logger.Debug("Child Execution started successfully", tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), tag.TargetWorkflowDomainID(task.TargetDomainID), tag.TargetWorkflowID(attributes.WorkflowID), tag.TargetWorkflowRunID(childRunID)) // Child execution is successfully started, record ChildExecutionStartedEvent in parent execution err = recordChildExecutionStarted(ctx, t.logger, task, wfContext, attributes, childRunID, t.shard.GetTimeSource().Now()) if err != nil { return err } // NOTE: do not access anything related mutable state after this lock release // release the context lock since we no longer need mutable state builder and // the rest of logic is making RPC call, which takes time. release(nil) // Finally create first decision task for Child execution so it is really started // entity not exist error is checked and ignored in HandleErr() method in task.go return createFirstDecisionTask( ctx, t.historyClient, task.TargetDomainID, &types.WorkflowExecution{ WorkflowID: task.TargetWorkflowID, RunID: childRunID, }) } func (t *transferActiveTaskExecutor) processRecordWorkflowStarted( ctx context.Context, task *persistence.RecordWorkflowStartedTask, ) (retError error) { return t.processRecordWorkflowStartedOrUpsertHelper(ctx, task, true) } func (t *transferActiveTaskExecutor) processUpsertWorkflowSearchAttributes( ctx context.Context, task *persistence.UpsertWorkflowSearchAttributesTask, ) (retError error) { return t.processRecordWorkflowStartedOrUpsertHelper(ctx, task, false) } func (t *transferActiveTaskExecutor) processRecordWorkflowStartedOrUpsertHelper( ctx context.Context, task persistence.Task, recordStart bool, ) (retError error) { workflowStartedScope := getOrCreateDomainTaggedScope(t.shard, metrics.TransferActiveTaskRecordWorkflowStartedScope, task.GetDomainID(), t.logger) wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.GetDomainID(), getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { release(retError) }() mutableState, err := loadMutableState(ctx, wfContext, task, t.metricsClient.Scope(metrics.TransferQueueProcessorScope), t.logger, 0) if err != nil { return err } if mutableState == nil || !mutableState.IsWorkflowExecutionRunning() { return nil } // verify task version for RecordWorkflowStarted. // upsert doesn't require verifyTask, because it is just a sync of mutableState. if recordStart { startVersion, err := mutableState.GetStartVersion() if err != nil { return err } ok, err := verifyTaskVersion(t.shard, t.logger, task.GetDomainID(), startVersion, task.GetVersion(), task) if err != nil || !ok { return err } } domainEntry, err := t.shard.GetDomainCache().GetDomainByID(task.GetDomainID()) if err != nil { return err } executionInfo := mutableState.GetExecutionInfo() workflowTimeout := executionInfo.WorkflowTimeout wfTypeName := executionInfo.WorkflowTypeName startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return err } startTimestamp := startEvent.GetTimestamp() executionTimestamp := getWorkflowExecutionTimestamp(mutableState, startEvent) visibilityMemo := getWorkflowMemo(executionInfo.Memo) searchAttr := copySearchAttributes(executionInfo.SearchAttributes) headers := getWorkflowHeaders(startEvent) isCron := len(executionInfo.CronSchedule) > 0 cronSchedule := "" if isCron { cronSchedule = executionInfo.CronSchedule } numClusters := (int16)(len(domainEntry.GetReplicationConfig().Clusters)) updateTimestamp := t.shard.GetTimeSource().Now() isAdvancedVisibilityEnabled := common.IsAdvancedVisibilityWritingEnabled( t.config.WriteVisibilityStoreName(), t.config.IsAdvancedVisConfigExist, ) executionStatus, scheduledExecutionTimestamp := determineExecutionStatusForVisibility(startEvent, mutableState, isAdvancedVisibilityEnabled) // release the context lock since we no longer need mutable state builder and // the rest of logic is making RPC call, which takes time. release(nil) if recordStart { workflowStartedScope.IncCounter(metrics.WorkflowStartedCount) return t.recordWorkflowStarted( ctx, task.GetDomainID(), task.GetWorkflowID(), task.GetRunID(), wfTypeName, startTimestamp, executionTimestamp.UnixNano(), workflowTimeout, task.GetTaskID(), executionInfo.TaskList, isCron, numClusters, visibilityMemo, updateTimestamp.UnixNano(), searchAttr, headers, executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute(), cronSchedule, executionStatus, scheduledExecutionTimestamp, ) } return t.upsertWorkflowExecution( ctx, task.GetDomainID(), task.GetWorkflowID(), task.GetRunID(), wfTypeName, startTimestamp, executionTimestamp.UnixNano(), workflowTimeout, task.GetTaskID(), executionInfo.TaskList, visibilityMemo, isCron, numClusters, updateTimestamp.UnixNano(), searchAttr, headers, executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute(), cronSchedule, executionStatus, scheduledExecutionTimestamp, ) } func (t *transferActiveTaskExecutor) processResetWorkflow( ctx context.Context, task *persistence.ResetWorkflowTask, ) (retError error) { currentContext, currentRelease, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, getWorkflowExecution(task), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { currentRelease(retError) }() currentMutableState, err := loadMutableState(ctx, currentContext, task, t.metricsClient.Scope(metrics.TransferQueueProcessorScope), t.logger, 0) if err != nil { return err } if currentMutableState == nil { return nil } logger := t.logger.WithTags( tag.WorkflowDomainID(task.DomainID), tag.WorkflowID(task.WorkflowID), tag.WorkflowRunID(task.RunID), ) domainName, err := t.shard.GetDomainCache().GetDomainName(task.DomainID) if err != nil { return err } if !currentMutableState.IsWorkflowExecutionRunning() { // it means this this might not be current anymore, we need to check var resp *persistence.GetCurrentExecutionResponse resp, err = t.shard.GetExecutionManager().GetCurrentExecution(ctx, &persistence.GetCurrentExecutionRequest{ DomainID: task.DomainID, WorkflowID: task.WorkflowID, DomainName: domainName, }) if err != nil { return err } if resp.RunID != task.RunID { logger.Warn("Auto-Reset is skipped, because current run is stale.") return nil } } // TODO: current reset doesn't allow childWFs, in the future we will release this restriction if len(currentMutableState.GetPendingChildExecutionInfos()) > 0 { logger.Warn("Auto-Reset is skipped, because current run has pending child executions.") return nil } currentStartVersion, err := currentMutableState.GetStartVersion() if err != nil { return err } ok, err := verifyTaskVersion(t.shard, t.logger, task.DomainID, currentStartVersion, task.Version, task) if err != nil || !ok { return err } executionInfo := currentMutableState.GetExecutionInfo() domainEntry, err := t.shard.GetDomainCache().GetDomainByID(executionInfo.DomainID) if err != nil { return err } logger = logger.WithTags(tag.WorkflowDomainName(domainEntry.GetInfo().Name)) reason, resetPoint := execution.FindAutoResetPoint(t.shard.GetTimeSource(), &domainEntry.GetConfig().BadBinaries, executionInfo.AutoResetPoints) if resetPoint == nil { logger.Warn("Auto-Reset is skipped, because reset point is not found.") return nil } logger = logger.WithTags( tag.WorkflowResetBaseRunID(resetPoint.GetRunID()), tag.WorkflowBinaryChecksum(resetPoint.GetBinaryChecksum()), tag.WorkflowEventID(resetPoint.GetFirstDecisionCompletedID()), ) var baseContext execution.Context var baseMutableState execution.MutableState var baseRelease execution.ReleaseFunc if resetPoint.GetRunID() == executionInfo.RunID { baseContext = currentContext baseMutableState = currentMutableState baseRelease = currentRelease } else { baseExecution := types.WorkflowExecution{ WorkflowID: task.WorkflowID, RunID: resetPoint.GetRunID(), } baseContext, baseRelease, err = t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( task.DomainID, baseExecution, taskGetExecutionContextTimeout, ) if err != nil { return err } defer func() { baseRelease(retError) }() baseMutableState, err = loadMutableState(ctx, baseContext, task, t.metricsClient.Scope(metrics.TransferQueueProcessorScope), t.logger, 0) if err != nil { return err } if baseMutableState == nil { return nil } } // reset workflow needs to go through the history so it may take a long time. // as a result it's not subject to the taskDefaultTimeout. Otherwise the task // may got stuck if the workflow history is large. return t.resetWorkflow( task, domainEntry.GetInfo().Name, reason, resetPoint, baseContext, baseMutableState, currentContext, currentMutableState, logger, ) } func recordChildExecutionStarted( ctx context.Context, logger log.Logger, task *persistence.StartChildExecutionTask, wfContext execution.Context, initiatedAttributes *types.StartChildWorkflowExecutionInitiatedEventAttributes, runID string, now time.Time, ) error { return updateWorkflowExecution(ctx, logger, wfContext, true, func(ctx context.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return &types.EntityNotExistsError{Message: "Workflow execution already completed."} } domain := initiatedAttributes.Domain initiatedEventID := task.InitiatedID ci, ok := mutableState.GetChildExecutionInfo(initiatedEventID) if !ok || ci.StartedID != constants.EmptyEventID { return &types.EntityNotExistsError{Message: "Pending child execution not found."} } _, err := mutableState.AddChildWorkflowExecutionStartedEvent( domain, &types.WorkflowExecution{ WorkflowID: task.TargetWorkflowID, RunID: runID, }, initiatedAttributes.WorkflowType, initiatedEventID, initiatedAttributes.Header, ) return err }, now, ) } func recordStartChildExecutionFailed( ctx context.Context, logger log.Logger, task *persistence.StartChildExecutionTask, wfContext execution.Context, initiatedAttributes *types.StartChildWorkflowExecutionInitiatedEventAttributes, now time.Time, ) error { return updateWorkflowExecution(ctx, logger, wfContext, true, func(ctx context.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return &types.EntityNotExistsError{Message: "Workflow execution already completed."} } initiatedEventID := task.InitiatedID ci, ok := mutableState.GetChildExecutionInfo(initiatedEventID) if !ok || ci.StartedID != constants.EmptyEventID { return &types.EntityNotExistsError{Message: "Pending child execution not found."} } _, err := mutableState.AddStartChildWorkflowExecutionFailedEvent(initiatedEventID, types.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning, initiatedAttributes) return err }, now, ) } // createFirstDecisionTask is used by StartChildExecution transfer task to create the first decision task for // child execution. func createFirstDecisionTask( ctx context.Context, historyClient history.Client, domainID string, execution *types.WorkflowExecution, ) error { scheduleDecisionCtx, cancel := context.WithTimeout(ctx, taskRPCCallTimeout) defer cancel() err := historyClient.ScheduleDecisionTask(scheduleDecisionCtx, &types.ScheduleDecisionTaskRequest{ DomainUUID: domainID, WorkflowExecution: execution, IsFirstDecision: true, }) if err != nil { switch err.(type) { // Maybe child workflow execution already timedout or terminated // Safe to discard the error and complete this transfer task // cross cluster task need to catch entity not exist error // as the target domain may failover before first decision is scheduled. case *types.WorkflowExecutionAlreadyCompletedError: return nil } } return err } func requestCancelExternalExecutionCompleted( ctx context.Context, logger log.Logger, task *persistence.CancelExecutionTask, wfContext execution.Context, targetDomain string, targetWorkflowID string, targetRunID string, now time.Time, ) error { err := updateWorkflowExecution(ctx, logger, wfContext, true, func(ctx context.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return &types.WorkflowExecutionAlreadyCompletedError{Message: "Workflow execution already completed."} } initiatedEventID := task.InitiatedID _, ok := mutableState.GetRequestCancelInfo(initiatedEventID) if !ok { return ErrMissingRequestCancelInfo } _, err := mutableState.AddExternalWorkflowExecutionCancelRequested( initiatedEventID, targetDomain, targetWorkflowID, targetRunID, ) return err }, now, ) switch err.(type) { // this could happen if this is a duplicate processing of the task, // or the execution has already completed. case *types.EntityNotExistsError, *types.WorkflowExecutionAlreadyCompletedError: return nil } return err } func signalExternalExecutionCompleted( ctx context.Context, logger log.Logger, task *persistence.SignalExecutionTask, wfContext execution.Context, targetDomain string, targetWorkflowID string, targetRunID string, control []byte, now time.Time, ) error { err := updateWorkflowExecution(ctx, logger, wfContext, true, func(ctx context.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return &types.WorkflowExecutionAlreadyCompletedError{Message: "Workflow execution already completed."} } initiatedEventID := task.InitiatedID _, ok := mutableState.GetSignalInfo(initiatedEventID) if !ok { return ErrMissingSignalInfo } _, err := mutableState.AddExternalWorkflowExecutionSignaled( initiatedEventID, targetDomain, targetWorkflowID, targetRunID, control, ) return err }, now, ) switch err.(type) { // this could happen if this is a duplicate processing of the task, // or the execution has already completed. case *types.EntityNotExistsError, *types.WorkflowExecutionAlreadyCompletedError: return nil } return err } func requestCancelExternalExecutionFailed( ctx context.Context, logger log.Logger, task *persistence.CancelExecutionTask, wfContext execution.Context, targetDomain string, targetWorkflowID string, targetRunID string, now time.Time, ) error { err := updateWorkflowExecution(ctx, logger, wfContext, true, func(ctx context.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return &types.WorkflowExecutionAlreadyCompletedError{Message: "Workflow execution already completed."} } initiatedEventID := task.InitiatedID _, ok := mutableState.GetRequestCancelInfo(initiatedEventID) if !ok { return ErrMissingRequestCancelInfo } _, err := mutableState.AddRequestCancelExternalWorkflowExecutionFailedEvent( constants.EmptyEventID, initiatedEventID, targetDomain, targetWorkflowID, targetRunID, types.CancelExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution, ) return err }, now, ) switch err.(type) { // this could happen if this is a duplicate processing of the task, // or the execution has already completed. case *types.EntityNotExistsError, *types.WorkflowExecutionAlreadyCompletedError: return nil } return err } func signalExternalExecutionFailed( ctx context.Context, logger log.Logger, task *persistence.SignalExecutionTask, wfContext execution.Context, targetDomain string, targetWorkflowID string, targetRunID string, control []byte, now time.Time, ) error { err := updateWorkflowExecution(ctx, logger, wfContext, true, func(ctx context.Context, mutableState execution.MutableState) error { if !mutableState.IsWorkflowExecutionRunning() { return &types.WorkflowExecutionAlreadyCompletedError{Message: "Workflow execution already completed."} } initiatedEventID := task.InitiatedID _, ok := mutableState.GetSignalInfo(initiatedEventID) if !ok { return ErrMissingSignalInfo } _, err := mutableState.AddSignalExternalWorkflowExecutionFailedEvent( constants.EmptyEventID, initiatedEventID, targetDomain, targetWorkflowID, targetRunID, control, types.SignalExternalWorkflowExecutionFailedCauseUnknownExternalWorkflowExecution, ) return err }, now, ) switch err.(type) { // this could happen if this is a duplicate processing of the task, // or the execution has already completed. case *types.EntityNotExistsError, *types.WorkflowExecutionAlreadyCompletedError: return nil } return err } func updateWorkflowExecution( ctx context.Context, logger log.Logger, wfContext execution.Context, createDecisionTask bool, action func(ctx context.Context, builder execution.MutableState) error, now time.Time, ) error { mutableState, err := wfContext.LoadWorkflowExecution(ctx) if err != nil { return err } if err := action(ctx, mutableState); err != nil { return err } if createDecisionTask { // Create a transfer task to schedule a decision task err := execution.ScheduleDecision(mutableState) if err != nil { return err } } logger.Debugf("transferActiveTaskExecutor.updateWorkflowExecution calling UpdateWorkflowExecutionAsActive for wfID %s", mutableState.GetExecutionInfo().WorkflowID, ) return wfContext.UpdateWorkflowExecutionAsActive(ctx, now) } func requestCancelExternalExecutionWithRetry( ctx context.Context, historyClient history.Client, task *persistence.CancelExecutionTask, targetDomain string, cancelRequestID string, ) error { request := &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: task.TargetDomainID, CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Domain: targetDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: task.TargetWorkflowID, RunID: task.TargetRunID, }, Identity: execution.IdentityHistoryService, // Use the same request ID to dedupe RequestCancelWorkflowExecution calls RequestID: cancelRequestID, }, ExternalInitiatedEventID: common.Int64Ptr(task.InitiatedID), ExternalWorkflowExecution: &types.WorkflowExecution{ WorkflowID: task.WorkflowID, RunID: task.RunID, }, ChildWorkflowOnly: task.TargetChildWorkflowOnly, } requestCancelCtx, cancel := context.WithTimeout(ctx, taskRPCCallTimeout) defer cancel() op := func(ctx context.Context) error { return historyClient.RequestCancelWorkflowExecution(ctx, request) } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(taskRetryPolicy), backoff.WithRetryableError(common.IsServiceTransientError), ) err := throttleRetry.Do(requestCancelCtx, op) switch err.(type) { case *types.CancellationAlreadyRequestedError: // err is CancellationAlreadyRequestedError // this could happen if target workflow cancellation is already requested // mark as success err = nil } return err } func signalExternalExecutionWithRetry( ctx context.Context, historyClient history.Client, task *persistence.SignalExecutionTask, targetDomain string, signalInfo *persistence.SignalInfo, ) error { request := &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: task.TargetDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ Domain: targetDomain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: task.TargetWorkflowID, RunID: task.TargetRunID, }, Identity: execution.IdentityHistoryService, SignalName: signalInfo.SignalName, Input: signalInfo.Input, // Use same request ID to deduplicate SignalWorkflowExecution calls RequestID: signalInfo.SignalRequestID, Control: signalInfo.Control, }, ExternalWorkflowExecution: &types.WorkflowExecution{ WorkflowID: task.WorkflowID, RunID: task.RunID, }, ChildWorkflowOnly: task.TargetChildWorkflowOnly, } signalCtx, cancel := context.WithTimeout(ctx, taskRPCCallTimeout) defer cancel() op := func(ctx context.Context) error { return historyClient.SignalWorkflowExecution(ctx, request) } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(taskRetryPolicy), backoff.WithRetryableError(common.IsServiceTransientError), ) return throttleRetry.Do(signalCtx, op) } func removeSignalMutableStateWithRetry( ctx context.Context, historyClient history.Client, task *persistence.SignalExecutionTask, signalRequestID string, ) error { ctx, cancel := context.WithTimeout(ctx, taskRPCCallTimeout) defer cancel() removeSignalRequest := &types.RemoveSignalMutableStateRequest{ DomainUUID: task.TargetDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: task.TargetWorkflowID, RunID: task.TargetRunID, }, RequestID: signalRequestID, } op := func(ctx context.Context) error { return historyClient.RemoveSignalMutableState(ctx, removeSignalRequest) } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(taskRetryPolicy), backoff.WithRetryableError(common.IsServiceTransientError), ) err := throttleRetry.Do(ctx, op) switch err.(type) { case *types.EntityNotExistsError: // it's safe to discard entity not exists error here // as there's nothing to remove. return nil } return err } func startWorkflowWithRetry( ctx context.Context, historyClient history.Client, timeSource clock.TimeSource, domainCache cache.DomainCache, task *persistence.StartChildExecutionTask, targetDomain string, requestID string, attributes *types.StartChildWorkflowExecutionInitiatedEventAttributes, partitionConfig map[string]string, activeClusterSelectionPolicy *types.ActiveClusterSelectionPolicy, ) (string, error) { // Get parent domain name domainName, err := domainCache.GetDomainName(task.DomainID) if err != nil { if _, ok := err.(*types.EntityNotExistsError); !ok { return "", err } // it is possible that the domain got deleted. Use domainID instead as this is only needed for the history event domainName = task.DomainID } frontendStartReq := &types.StartWorkflowExecutionRequest{ Domain: targetDomain, WorkflowID: attributes.WorkflowID, WorkflowType: attributes.WorkflowType, TaskList: attributes.TaskList, Input: attributes.Input, Header: attributes.Header, ExecutionStartToCloseTimeoutSeconds: attributes.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: attributes.TaskStartToCloseTimeoutSeconds, // Use the same request ID to dedupe StartWorkflowExecution calls RequestID: requestID, WorkflowIDReusePolicy: attributes.WorkflowIDReusePolicy, RetryPolicy: attributes.RetryPolicy, CronSchedule: attributes.CronSchedule, CronOverlapPolicy: attributes.CronOverlapPolicy, Memo: attributes.Memo, SearchAttributes: attributes.SearchAttributes, DelayStartSeconds: attributes.DelayStartSeconds, JitterStartSeconds: attributes.JitterStartSeconds, FirstRunAtTimeStamp: attributes.FirstRunAtTimestamp, ActiveClusterSelectionPolicy: activeClusterSelectionPolicy, } historyStartReq, err := common.CreateHistoryStartWorkflowRequest(task.TargetDomainID, frontendStartReq, timeSource.Now(), partitionConfig) if err != nil { return "", err } historyStartReq.ParentExecutionInfo = &types.ParentExecutionInfo{ DomainUUID: task.DomainID, Domain: domainName, Execution: &types.WorkflowExecution{ WorkflowID: task.WorkflowID, RunID: task.RunID, }, InitiatedID: task.InitiatedID, } startWorkflowCtx, cancel := context.WithTimeout(ctx, taskRPCCallTimeout) defer cancel() var response *types.StartWorkflowExecutionResponse op := func(ctx context.Context) error { response, err = historyClient.StartWorkflowExecution(ctx, historyStartReq) return err } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(taskRetryPolicy), backoff.WithRetryableError(common.IsServiceTransientError), ) if err := throttleRetry.Do(startWorkflowCtx, op); err != nil { return "", err } return response.GetRunID(), nil } func (t *transferActiveTaskExecutor) resetWorkflow( task *persistence.ResetWorkflowTask, domain string, reason string, resetPoint *types.ResetPointInfo, baseContext execution.Context, baseMutableState execution.MutableState, currentContext execution.Context, currentMutableState execution.MutableState, logger log.Logger, ) error { var err error resetCtx, cancel := context.WithTimeout(context.Background(), resetWorkflowTimeout) defer cancel() domainID := task.DomainID WorkflowID := task.WorkflowID baseRunID := baseMutableState.GetExecutionInfo().RunID resetRunID := uuid.New() baseRebuildLastEventID := resetPoint.GetFirstDecisionCompletedID() - 1 baseVersionHistories := baseMutableState.GetVersionHistories() if baseVersionHistories == nil { return execution.ErrMissingVersionHistories } baseCurrentVersionHistory, err := baseVersionHistories.GetCurrentVersionHistory() if err != nil { return err } baseRebuildLastEventVersion, err := baseCurrentVersionHistory.GetEventVersion(baseRebuildLastEventID) if err != nil { return err } baseCurrentBranchToken := baseCurrentVersionHistory.GetBranchToken() baseNextEventID := baseMutableState.GetNextEventID() err = t.workflowResetter.ResetWorkflow( resetCtx, domainID, WorkflowID, baseRunID, baseCurrentBranchToken, baseRebuildLastEventID, baseRebuildLastEventVersion, baseNextEventID, resetRunID, uuid.New(), execution.NewWorkflow( resetCtx, t.shard.GetClusterMetadata(), currentContext, currentMutableState, execution.NoopReleaseFn, // this is fine since caller will defer on release logger, ), reason, nil, false, ) switch err.(type) { case nil: return nil case *types.BadRequestError: // This means the reset point is corrupted and not retry able. // There must be a bug in our system that we must fix.(for example, history is not the same in active/passive) t.metricsClient.IncCounter(metrics.TransferQueueProcessorScope, metrics.AutoResetPointCorruptionCounter) logger.Error("Auto-Reset workflow failed and not retryable. The reset point is corrupted.", tag.Error(err)) return nil default: // log this error and retry logger.Error("Auto-Reset workflow failed", tag.Error(err)) return err } } func (t *transferActiveTaskExecutor) processParentClosePolicy( ctx context.Context, domainID string, domainName string, parentExecution *types.WorkflowExecution, childInfos map[int64]*persistence.ChildExecutionInfo, ) error { if len(childInfos) == 0 { return nil } scope := t.metricsClient.Scope(metrics.TransferActiveTaskCloseExecutionScope) if t.shard.GetConfig().EnableParentClosePolicyWorker() && len(childInfos) >= t.shard.GetConfig().ParentClosePolicyThreshold(domainName) { batchSize := t.shard.GetConfig().ParentClosePolicyBatchSize(domainName) executions := make([]parentclosepolicy.RequestDetail, 0, min(len(childInfos), batchSize)) count := 0 for _, childInfo := range childInfos { count++ if childInfo.ParentClosePolicy == types.ParentClosePolicyAbandon { continue } executions = append(executions, parentclosepolicy.RequestDetail{ DomainID: domainID, DomainName: domainName, WorkflowID: childInfo.StartedWorkflowID, RunID: childInfo.StartedRunID, Policy: childInfo.ParentClosePolicy, }) if len(executions) == batchSize { err := t.parentClosePolicyClient.SendParentClosePolicyRequest(ctx, parentclosepolicy.Request{ DomainName: domainName, Executions: executions, }) if err != nil { return err } executions = make([]parentclosepolicy.RequestDetail, 0, min(len(childInfos)-count, batchSize)) } } if len(executions) == 0 { return nil } return t.parentClosePolicyClient.SendParentClosePolicyRequest(ctx, parentclosepolicy.Request{ DomainName: domainName, Executions: executions, }) } for _, childInfo := range childInfos { if err := t.applyParentClosePolicy( ctx, domainID, domainName, parentExecution, childInfo, ); err != nil { switch err.(type) { case *types.EntityNotExistsError, *types.WorkflowExecutionAlreadyCompletedError, *types.CancellationAlreadyRequestedError: // expected error, no-op break default: scope.IncCounter(metrics.ParentClosePolicyProcessorFailures) return err } } scope.IncCounter(metrics.ParentClosePolicyProcessorSuccess) } return nil } func (t *transferActiveTaskExecutor) applyParentClosePolicy( ctx context.Context, domainID string, domainName string, parentWorkflowExecution *types.WorkflowExecution, childInfo *persistence.ChildExecutionInfo, ) error { switch childInfo.ParentClosePolicy { case types.ParentClosePolicyAbandon: // noop return nil case types.ParentClosePolicyTerminate: return t.historyClient.TerminateWorkflowExecution(ctx, &types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: domainID, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ Domain: domainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childInfo.StartedWorkflowID, }, Reason: "by parent close policy", Identity: execution.IdentityHistoryService, // Include StartedRunID as FirstExecutionRunID on the request to allow child to be terminated across runs. // If the child does continue as new it still propagates the RunID of first execution. FirstExecutionRunID: childInfo.StartedRunID, }, ExternalWorkflowExecution: parentWorkflowExecution, ChildWorkflowOnly: true, }) case types.ParentClosePolicyRequestCancel: return t.historyClient.RequestCancelWorkflowExecution(ctx, &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: domainID, CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Domain: domainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childInfo.StartedWorkflowID, }, Identity: execution.IdentityHistoryService, // Include StartedRunID as FirstExecutionRunID on the request to allow child to be canceled across runs. // If the child does continue as new it still propagates the RunID of first execution. FirstExecutionRunID: childInfo.StartedRunID, }, ExternalWorkflowExecution: parentWorkflowExecution, ChildWorkflowOnly: true, }) default: return &types.InternalServiceError{ Message: fmt.Sprintf("unknown parent close policy: %v", childInfo.ParentClosePolicy), } } } func filterPendingChildExecutions( targetDomainIDs map[string]struct{}, children map[int64]*persistence.ChildExecutionInfo, domainCache cache.DomainCache, parentDomainEntry *cache.DomainCacheEntry, ) (map[int64]*persistence.ChildExecutionInfo, error) { if len(targetDomainIDs) == 0 { return children, nil } filteredChildren := make(map[int64]*persistence.ChildExecutionInfo, len(children)) for initiatedID, child := range children { domainID, err := execution.GetChildExecutionDomainID(child, domainCache, parentDomainEntry) if err != nil { if common.IsEntityNotExistsError(err) { // target domain deleted, ignore the child continue } return nil, err } if _, ok := targetDomainIDs[domainID]; ok { filteredChildren[initiatedID] = child } } return filteredChildren, nil } func getWorkflowExecutionStatus(mutableState execution.MutableState, closeEvent *types.HistoryEvent) types.WorkflowExecutionStatus { // Extract close status from the close event var closeStatus types.WorkflowExecutionCloseStatus switch closeEvent.GetEventType() { case types.EventTypeWorkflowExecutionCompleted: closeStatus = types.WorkflowExecutionCloseStatusCompleted case types.EventTypeWorkflowExecutionFailed: closeStatus = types.WorkflowExecutionCloseStatusFailed case types.EventTypeWorkflowExecutionCanceled: closeStatus = types.WorkflowExecutionCloseStatusCanceled case types.EventTypeWorkflowExecutionTerminated: closeStatus = types.WorkflowExecutionCloseStatusTerminated case types.EventTypeWorkflowExecutionTimedOut: closeStatus = types.WorkflowExecutionCloseStatusTimedOut case types.EventTypeWorkflowExecutionContinuedAsNew: closeStatus = types.WorkflowExecutionCloseStatusContinuedAsNew // For cron workflows that continue as new: // - If FailureReason is present and non-empty, the workflow failed // - If FailureReason is nil/empty, the workflow completed successfully if attr := closeEvent.WorkflowExecutionContinuedAsNewEventAttributes; attr != nil { failureReason := attr.GetFailureReason() if failureReason != "" { // Workflow failed - check if it's a timeout or generic failure if strings.Contains(strings.ToLower(failureReason), "timeout") || strings.Contains(strings.ToLower(failureReason), "timed out") { closeStatus = types.WorkflowExecutionCloseStatusTimedOut } else { closeStatus = types.WorkflowExecutionCloseStatusFailed } } else { // No failure reason means successful completion closeStatus = types.WorkflowExecutionCloseStatusCompleted } } default: // Unknown close event, default to completed closeStatus = types.WorkflowExecutionCloseStatusCompleted } // Map close status to execution status switch closeStatus { case types.WorkflowExecutionCloseStatusCompleted: return types.WorkflowExecutionStatusCompleted case types.WorkflowExecutionCloseStatusFailed: return types.WorkflowExecutionStatusFailed case types.WorkflowExecutionCloseStatusCanceled: return types.WorkflowExecutionStatusCanceled case types.WorkflowExecutionCloseStatusTerminated: return types.WorkflowExecutionStatusTerminated case types.WorkflowExecutionCloseStatusContinuedAsNew: return types.WorkflowExecutionStatusContinuedAsNew case types.WorkflowExecutionCloseStatusTimedOut: return types.WorkflowExecutionStatusTimedOut default: return types.WorkflowExecutionStatusCompleted } } ================================================ FILE: service/history/task/transfer_active_task_executor_test.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "encoding/json" "errors" "math/rand" "strconv" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" hclient "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/engine" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/reset" "github.com/uber/cadence/service/history/shard" test "github.com/uber/cadence/service/history/testing" "github.com/uber/cadence/service/history/workflowcache" warchiver "github.com/uber/cadence/service/worker/archiver" "github.com/uber/cadence/service/worker/parentclosepolicy" ) type ( transferActiveTaskExecutorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockEngine *engine.MockEngine mockDomainCache *cache.MockDomainCache mockWFCache *workflowcache.MockWFCache mockHistoryClient *hclient.MockClient mockMatchingClient *matching.MockClient mockVisibilityMgr *mocks.VisibilityManager mockExecutionMgr *mocks.ExecutionManager mockHistoryV2Mgr *mocks.HistoryV2Manager mockArchivalClient *warchiver.MockClient mockArchivalMetadata *archiver.MockArchivalMetadata mockArchiverProvider *provider.MockArchiverProvider mockParentClosePolicyClient *parentclosepolicy.MockClient logger log.Logger domainID string domainName string domainEntry *cache.DomainCacheEntry version int64 timeSource clock.MockedTimeSource transferActiveTaskExecutor *transferActiveTaskExecutor } ) func TestTransferActiveTaskExecutorSuite(t *testing.T) { s := new(transferActiveTaskExecutorSuite) suite.Run(t, s) } func (s *transferActiveTaskExecutorSuite) SetupSuite() { } func (s *transferActiveTaskExecutorSuite) TearDownSuite() { } func (s *transferActiveTaskExecutorSuite) SetupTest() { s.Assertions = require.New(s.T()) s.domainID = constants.TestDomainID s.domainName = constants.TestDomainName s.domainEntry = constants.TestGlobalDomainEntry s.version = s.domainEntry.GetFailoverVersion() s.timeSource = clock.NewMockedTimeSource() s.controller = gomock.NewController(s.T()) testConfig := config.NewForTest() s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ ShardID: 0, RangeID: 1, TransferAckLevel: 0, }, testConfig, ) s.mockShard.SetEventsCache(events.NewCache( s.mockShard.GetShardID(), s.mockShard.GetHistoryManager(), s.mockShard.GetConfig(), s.mockShard.GetLogger(), s.mockShard.GetMetricsClient(), s.mockShard.GetDomainCache(), )) s.mockShard.Resource.TimeSource = s.timeSource s.mockEngine = engine.NewMockEngine(s.controller) s.mockEngine.EXPECT().NotifyNewHistoryEvent(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewTransferTasks(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewTimerTasks(gomock.Any()).AnyTimes() s.mockEngine.EXPECT().NotifyNewReplicationTasks(gomock.Any()).AnyTimes() s.mockShard.SetEngine(s.mockEngine) s.mockParentClosePolicyClient = parentclosepolicy.NewMockClient(s.controller) s.mockArchivalClient = warchiver.NewMockClient(s.controller) s.mockMatchingClient = s.mockShard.Resource.MatchingClient s.mockHistoryClient = s.mockShard.Resource.HistoryClient s.mockExecutionMgr = s.mockShard.Resource.ExecutionMgr s.mockHistoryV2Mgr = s.mockShard.Resource.HistoryMgr s.mockVisibilityMgr = s.mockShard.Resource.VisibilityMgr s.mockArchivalMetadata = s.mockShard.Resource.ArchivalMetadata s.mockArchiverProvider = s.mockShard.Resource.ArchiverProvider s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockWFCache = workflowcache.NewMockWFCache(s.controller) s.mockDomainCache.EXPECT().GetDomain(constants.TestRateLimitedDomainName).Return(constants.TestRateLimitedDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(s.domainName).Return(s.domainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(constants.TestRateLimitedDomainID).Return(constants.TestRateLimitedDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(s.domainID).Return(s.domainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainID(s.domainName).Return(s.domainID, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(s.domainID).Return(s.domainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(constants.TestRateLimitedDomainID).Return(constants.TestRateLimitedDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(constants.TestRateLimitedDomainID).Return(constants.TestRateLimitedDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(constants.TestRateLimitedDomainName).Return(constants.TestRateLimitedDomainEntry, nil).AnyTimes() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestGlobalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestGlobalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(testActiveClusterInfo, nil).AnyTimes() s.logger = s.mockShard.GetLogger() s.transferActiveTaskExecutor = NewTransferActiveTaskExecutor( s.mockShard, s.mockArchivalClient, execution.NewCache(s.mockShard), nil, s.logger, testConfig, s.mockWFCache, ).(*transferActiveTaskExecutor) s.transferActiveTaskExecutor.parentClosePolicyClient = s.mockParentClosePolicyClient } func (s *transferActiveTaskExecutorSuite) TearDownTest() { s.transferActiveTaskExecutor.Stop() s.controller.Finish() s.mockShard.Finish(s.T()) } func (s *transferActiveTaskExecutorSuite) TestProcessActivityTask_Success() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event, ai := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity-1", "some random activity type", mutableState.GetExecutionInfo().TaskList, []byte{}, 1, 1, 1, 1, ) mutableState.FlushBufferedEvents() transferTask := s.newTransferTaskFromInfo(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TargetDomainID: constants.TestDomainID, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockMatchingClient.EXPECT().AddActivityTask(gomock.Any(), createAddActivityTaskRequest(transferTask, ai, mutableState.GetExecutionInfo().PartitionConfig)).Return(&types.AddActivityTaskResponse{}, nil).Times(1) s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(true).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessActivityTask_EphemeralTaskListKind() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event, ai, _, _, _, _ := mutableState.AddActivityTaskScheduledEvent(nil, decisionCompletionID, &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "activity-1", ActivityType: &types.ActivityType{Name: "some random activity type"}, TaskList: &types.TaskList{Name: mutableState.GetExecutionInfo().TaskList, Kind: types.TaskListKindEphemeral.Ptr()}, Input: []byte{}, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(1), ScheduleToStartTimeoutSeconds: common.Int32Ptr(1), StartToCloseTimeoutSeconds: common.Int32Ptr(1), HeartbeatTimeoutSeconds: common.Int32Ptr(1), }, false, ) mutableState.FlushBufferedEvents() transferTask := s.newTransferTaskFromInfo(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TargetDomainID: constants.TestDomainID, TaskList: "ignored", ScheduleID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockMatchingClient.EXPECT().AddActivityTask(gomock.Any(), createAddActivityTaskRequest(transferTask, ai, mutableState.GetExecutionInfo().PartitionConfig)).Return(&types.AddActivityTaskResponse{}, nil).Times(1) s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(true).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessActivityTask_Ratelimits() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, constants.TestDomainID) s.NoError(err) event, ai := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity-1", "some random activity type", mutableState.GetExecutionInfo().TaskList, []byte{}, 1, 1, 1, 1, ) mutableState.FlushBufferedEvents() transferTaskInRatelimitedDomain := s.newTransferTaskFromInfo(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestRateLimitedDomainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TargetDomainID: constants.TestRateLimitedDomainID, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: event.ID, }) transferTask := s.newTransferTaskFromInfo(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TargetDomainID: constants.TestDomainID, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) // expected calls to the matching service if task processing is allowed s.mockMatchingClient.EXPECT().AddActivityTask(gomock.Any(), createAddActivityTaskRequest(transferTaskInRatelimitedDomain, ai, mutableState.GetExecutionInfo().PartitionConfig)).Return(&types.AddActivityTaskResponse{}, nil).Times(1) s.mockMatchingClient.EXPECT().AddActivityTask(gomock.Any(), createAddActivityTaskRequest(transferTask, ai, mutableState.GetExecutionInfo().PartitionConfig)).Return(&types.AddActivityTaskResponse{}, nil).Times(1) // RPS still below allowed value so the task can be executed s.mockWFCache.EXPECT().AllowInternal(constants.TestRateLimitedDomainID, constants.TestWorkflowID).Return(true).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTaskInRatelimitedDomain) s.Nil(err) // RPS more than allowed limit so the task cannot be executed s.mockWFCache.EXPECT().AllowInternal(constants.TestRateLimitedDomainID, constants.TestWorkflowID).Return(false).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTaskInRatelimitedDomain) s.Error(err) s.Equal("workflow is being rate limited for making too many requests", err.Error()) // RPS still below allowed value so the task can be executed s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(true).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) // RPS more than allowed limit so the task cannot be executed s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(false).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Error(err) s.Equal("workflow is being rate limited for making too many requests", err.Error()) } func (s *transferActiveTaskExecutorSuite) TestProcessActivityTask_Duplication() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event, ai := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity-1", "some random activity type", mutableState.GetExecutionInfo().TaskList, []byte{}, 1, 1, 1, 1, ) event = test.AddActivityTaskStartedEvent(mutableState, event.ID, "") ai.StartedID = event.ID event = test.AddActivityTaskCompletedEvent(mutableState, ai.ScheduleID, ai.StartedID, nil, "") mutableState.FlushBufferedEvents() transferTask := s.newTransferTaskFromInfo(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TargetDomainID: constants.TestDomainID, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessDecisionTask_FirstDecision() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(true).Times(1) s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), createAddDecisionTaskRequest(transferTask, mutableState)).Return(&types.AddDecisionTaskResponse{}, nil).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessDecisionTask_NonFirstDecision() { workflowExecution, mutableState, _, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) // make another round of decision di := test.AddDecisionTaskScheduledEvent(mutableState) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(true).Times(1) s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), createAddDecisionTaskRequest(transferTask, mutableState)).Return(&types.AddDecisionTaskResponse{}, nil).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessDecisionTask_Ratelimits() { workflowExecution, mutableState, _, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) // make another round of decision di := test.AddDecisionTaskScheduledEvent(mutableState) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) rateLimitedTransferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestRateLimitedDomainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) // expected calls to the matching service if task processing is allowed s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), createAddDecisionTaskRequest(transferTask, mutableState)).Return(&types.AddDecisionTaskResponse{}, nil).Times(1) s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), createAddDecisionTaskRequest(rateLimitedTransferTask, mutableState)).Return(&types.AddDecisionTaskResponse{}, nil).Times(1) // RPS still below allowed value so the task can be executed s.mockWFCache.EXPECT().AllowInternal(constants.TestRateLimitedDomainID, constants.TestWorkflowID).Return(true).Times(1) _, err = s.transferActiveTaskExecutor.Execute(rateLimitedTransferTask) s.Nil(err) // RPS more than allowed limit so the task cannot be executed s.mockWFCache.EXPECT().AllowInternal(constants.TestRateLimitedDomainID, constants.TestWorkflowID).Return(false).Times(1) _, err = s.transferActiveTaskExecutor.Execute(rateLimitedTransferTask) s.Error(err) s.Equal("workflow is being rate limited for making too many requests", err.Error()) // RPS still below allowed value so the task can be executed s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(true).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) // RPS more than allowed limit so the task cannot be executed s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(false).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Error(err) s.Equal("workflow is being rate limited for making too many requests", err.Error()) } func (s *transferActiveTaskExecutorSuite) TestProcessDecisionTask_Sticky_NonFirstDecision() { workflowExecution, mutableState, _, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) // set the sticky tasklist attr executionInfo := mutableState.GetExecutionInfo() executionInfo.StickyTaskList = "some random sticky task list" executionInfo.StickyScheduleToStartTimeout = int32(233) // make another round of decision di := test.AddDecisionTaskScheduledEvent(mutableState) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TaskList: executionInfo.StickyTaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(true).Times(1) s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), createAddDecisionTaskRequest(transferTask, mutableState)).Return(&types.AddDecisionTaskResponse{}, nil).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessDecisionTask_DecisionNotSticky_MutableStateSticky() { workflowExecution, mutableState, _, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) // set the sticky tasklist attr executionInfo := mutableState.GetExecutionInfo() executionInfo.StickyTaskList = "some random sticky task list" executionInfo.StickyScheduleToStartTimeout = int32(233) // make another round of decision di := test.AddDecisionTaskScheduledEvent(mutableState) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(true).Times(1) s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), createAddDecisionTaskRequest(transferTask, mutableState)).Return(&types.AddDecisionTaskResponse{}, nil).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessDecisionTask_Duplication() { workflowExecution, mutableState, _, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(4096), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: mutableState.GetPreviousStartedEventID() - 1, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, mutableState.GetNextEventID()-1, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessDecisionTask_StickyWorkerUnavailable() { workflowExecution, mutableState, _, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) // set the sticky tasklist attr executionInfo := mutableState.GetExecutionInfo() executionInfo.StickyTaskList = "some random sticky task list" executionInfo.StickyScheduleToStartTimeout = int32(233) // make another round of decision di := test.AddDecisionTaskScheduledEvent(mutableState) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TaskList: executionInfo.StickyTaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(true).Times(1) addDecisionTaskRequest := createAddDecisionTaskRequest(transferTask, mutableState) // Create a deep copy of the expected modified request modifiedRequest := *addDecisionTaskRequest modifiedRequest.TaskList = &types.TaskList{ Name: mutableState.GetExecutionInfo().TaskList, } gomock.InOrder( s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), addDecisionTaskRequest).Return(nil, &types.StickyWorkerUnavailableError{}).Times(1), s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), gomock.Eq(&modifiedRequest)).Return(&types.AddDecisionTaskResponse{}, nil).Times(1), ) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.NoError(err) } func (s *transferActiveTaskExecutorSuite) TestProcessDecisionTask_EphemeralTaskListKind() { workflowExecution, mutableState, err := test.StartWorkflowWithTaskList(&types.TaskList{Name: "ephemmy", Kind: types.TaskListKindEphemeral.Ptr()}, s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockWFCache.EXPECT().AllowInternal(constants.TestDomainID, constants.TestWorkflowID).Return(true).Times(1) s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), createAddDecisionTaskRequest(transferTask, mutableState)).Return(&types.AddDecisionTaskResponse{}, nil).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessCloseExecution_HasParent_Success() { s.testProcessCloseExecutionWithParent( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, ) { s.mockVisibilityMgr.On("RecordWorkflowExecutionClosed", mock.Anything, mock.Anything).Return(nil).Once() s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewDisabledArchvialConfig()) }, false, ) } func (s *transferActiveTaskExecutorSuite) TestProcessCloseExecution_HasParent_Failure() { s.testProcessCloseExecutionWithParent( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, ) { s.mockVisibilityMgr.On("RecordWorkflowExecutionClosed", mock.Anything, mock.Anything).Return(nil).Once() s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewDisabledArchvialConfig()) }, true, ) } func (s *transferActiveTaskExecutorSuite) testProcessCloseExecutionWithParent( targetDomainID string, setupMockFn func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, ), failRecordChild bool, ) { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) executionInfo := mutableState.GetExecutionInfo() parentInitiatedID := int64(3222) parentExecution := types.WorkflowExecution{ WorkflowID: "some random parent workflow ID", RunID: uuid.New(), } executionInfo.ParentDomainID = targetDomainID executionInfo.ParentWorkflowID = parentExecution.WorkflowID executionInfo.ParentRunID = parentExecution.RunID executionInfo.InitiatedID = parentInitiatedID event := test.AddCompleteWorkflowEvent(mutableState, decisionCompletionID, nil) transferTask := s.newTransferTaskFromInfo(&persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) if targetDomainID == constants.TestDomainID { var recordChildErr error if failRecordChild { recordChildErr = &types.DomainNotActiveError{} } s.mockHistoryClient.EXPECT().RecordChildExecutionCompleted(gomock.Any(), &types.RecordChildExecutionCompletedRequest{ DomainUUID: targetDomainID, WorkflowExecution: &parentExecution, InitiatedID: parentInitiatedID, CompletedExecution: &workflowExecution, CompletionEvent: event, }).Return(recordChildErr).Times(1) } setupMockFn(mutableState, workflowExecution, parentExecution) _, err = s.transferActiveTaskExecutor.Execute(transferTask) if failRecordChild { s.Equal(&types.DomainNotActiveError{}, err) } else { s.NoError(err) } } func (s *transferActiveTaskExecutorSuite) TestProcessCloseExecution_NoParent() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) executionInfo := mutableState.GetExecutionInfo() executionInfo.ActiveClusterSelectionPolicy = &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, } event := test.AddCompleteWorkflowEvent(mutableState, decisionCompletionID, nil) transferTask := s.newTransferTaskFromInfo(&persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockVisibilityMgr.On("RecordWorkflowExecutionClosed", mock.Anything, mock.Anything).Return(nil).Once() s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewArchivalConfig("enabled", dynamicproperties.GetStringPropertyFn("enabled"), true, dynamicproperties.GetBoolPropertyFn(true), "disabled", "random URI")) s.mockArchivalClient.EXPECT().Archive(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) // switch on context header in viz s.mockShard.GetConfig().EnableContextHeaderInVisibility = func(domain string) bool { return true } s.mockShard.GetConfig().ValidSearchAttributes = func(opts ...dynamicproperties.FilterOption) map[string]interface{} { return map[string]interface{}{ "Header_context_key": struct{}{}, } } _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessCloseExecution_NoParent_HasFewChildren() { s.testProcessCloseExecutionNoParentHasFewChildren( map[string]string{ "child_abandon": s.domainName, "child_terminate": s.domainName, "child_cancel": s.domainName, }, func() { s.expectCancelRequest(s.domainName) s.expectTerminateRequest(s.domainName) }, ) } func (s *transferActiveTaskExecutorSuite) TestApplyParentPolicy_SameClusterChild_TargetNotActive() { s.testProcessCloseExecutionNoParentHasFewChildrenWithError( map[string]string{ "child_terminate": s.domainName, "child_cancel": s.domainName, }, func() { s.mockHistoryClient.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), gomock.Any()). Return(&types.DomainNotActiveError{}).MaxTimes(1) s.mockHistoryClient.EXPECT().TerminateWorkflowExecution(gomock.Any(), gomock.Any()). Return(&types.DomainNotActiveError{}).MaxTimes(1) }, &types.DomainNotActiveError{}, ) } func (s *transferActiveTaskExecutorSuite) expectCancelRequest(childDomainName string) { childDomainID, err := s.mockDomainCache.GetDomainID(childDomainName) s.NoError(err) s.mockHistoryClient.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), gomock.Any()).DoAndReturn( func( _ context.Context, request *types.HistoryRequestCancelWorkflowExecutionRequest, option ...yarpc.CallOption, ) error { s.Equal(childDomainID, request.DomainUUID) s.Equal(childDomainName, request.CancelRequest.Domain) s.True(request.GetChildWorkflowOnly()) errs := []error{nil, &types.CancellationAlreadyRequestedError{}, &types.EntityNotExistsError{}} return errs[rand.Intn(len(errs))] }, ).Times(1) } func (s *transferActiveTaskExecutorSuite) expectTerminateRequest(childDomainName string) { childDomainID, err := s.mockDomainCache.GetDomainID(childDomainName) s.NoError(err) s.mockHistoryClient.EXPECT().TerminateWorkflowExecution(gomock.Any(), gomock.Any()).DoAndReturn( func( _ context.Context, request *types.HistoryTerminateWorkflowExecutionRequest, option ...yarpc.CallOption, ) error { s.Equal(childDomainID, request.DomainUUID) s.Equal(childDomainName, request.TerminateRequest.Domain) errs := []error{nil, &types.EntityNotExistsError{}} return errs[rand.Intn(len(errs))] }, ).Times(1) } func (s *transferActiveTaskExecutorSuite) testProcessCloseExecutionNoParentHasFewChildren( childrenDomainNames map[string]string, setupMockFn func(), ) { s.testProcessCloseExecutionNoParentHasFewChildrenWithError(childrenDomainNames, setupMockFn, nil) } func (s *transferActiveTaskExecutorSuite) testProcessCloseExecutionNoParentHasFewChildrenWithError( childrenDomainNames map[string]string, setupMockFn func(), expectedErr error, ) { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) parentClosePolicy1 := types.ParentClosePolicyAbandon parentClosePolicy2 := types.ParentClosePolicyTerminate parentClosePolicy3 := types.ParentClosePolicyRequestCancel _, _, err = mutableState.AddStartChildWorkflowExecutionInitiatedEvent(decisionCompletionID, uuid.New(), &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: childrenDomainNames["child_abandon"], WorkflowID: "child workflow1", WorkflowType: &types.WorkflowType{ Name: "child workflow type", }, TaskList: &types.TaskList{Name: mutableState.GetExecutionInfo().TaskList}, Input: []byte("random input"), ParentClosePolicy: &parentClosePolicy1, }) s.Nil(err) _, _, err = mutableState.AddStartChildWorkflowExecutionInitiatedEvent(decisionCompletionID, uuid.New(), &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: childrenDomainNames["child_terminate"], WorkflowID: "child workflow2", WorkflowType: &types.WorkflowType{ Name: "child workflow type", }, TaskList: &types.TaskList{Name: mutableState.GetExecutionInfo().TaskList}, Input: []byte("random input"), ParentClosePolicy: &parentClosePolicy2, }) s.Nil(err) _, _, err = mutableState.AddStartChildWorkflowExecutionInitiatedEvent(decisionCompletionID, uuid.New(), &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: childrenDomainNames["child_cancel"], WorkflowID: "child workflow3", WorkflowType: &types.WorkflowType{ Name: "child workflow type", }, TaskList: &types.TaskList{Name: mutableState.GetExecutionInfo().TaskList}, Input: []byte("random input"), ParentClosePolicy: &parentClosePolicy3, }) s.Nil(err) s.NoError(mutableState.FlushBufferedEvents()) event := test.AddCompleteWorkflowEvent(mutableState, decisionCompletionID, nil) transferTask := s.newTransferTaskFromInfo(&persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockVisibilityMgr.On("RecordWorkflowExecutionClosed", mock.Anything, mock.Anything).Return(nil).Once() s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewDisabledArchvialConfig()) setupMockFn() _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Equal(expectedErr, err) } func (s *transferActiveTaskExecutorSuite) TestProcessCloseExecution_NoParent_HasManyChildren() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) numChildWorkflows := 500 for i := 0; i < numChildWorkflows; i++ { _, _, err = mutableState.AddStartChildWorkflowExecutionInitiatedEvent(decisionCompletionID, uuid.New(), &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: s.domainName, WorkflowID: "child workflow" + strconv.Itoa(i), WorkflowType: &types.WorkflowType{ Name: "child workflow type", }, TaskList: &types.TaskList{Name: mutableState.GetExecutionInfo().TaskList}, Input: []byte("random input"), ParentClosePolicy: types.ParentClosePolicyTerminate.Ptr(), }) s.Nil(err) } s.NoError(mutableState.FlushBufferedEvents()) event := test.AddCompleteWorkflowEvent(mutableState, decisionCompletionID, nil) transferTask := s.newTransferTaskFromInfo(&persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockVisibilityMgr.On("RecordWorkflowExecutionClosed", mock.Anything, mock.Anything).Return(nil).Once() s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewDisabledArchvialConfig()) s.mockParentClosePolicyClient.EXPECT().SendParentClosePolicyRequest(gomock.Any(), gomock.Cond( func(request parentclosepolicy.Request) bool { if len(request.Executions) != s.mockShard.GetConfig().ParentClosePolicyBatchSize(constants.TestDomainName) { return false } for _, executions := range request.Executions { if executions.DomainName != constants.TestDomainName { return false } } return true }, )).Return(nil).Times(numChildWorkflows / s.mockShard.GetConfig().ParentClosePolicyBatchSize(constants.TestDomainName)) s.mockParentClosePolicyClient.EXPECT().SendParentClosePolicyRequest(gomock.Any(), gomock.Cond( func(request parentclosepolicy.Request) bool { if len(request.Executions) != numChildWorkflows%s.mockShard.GetConfig().ParentClosePolicyBatchSize(constants.TestDomainName) { return false } for _, executions := range request.Executions { if executions.DomainName != constants.TestDomainName { return false } } return true }, )).Return(nil).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessCloseExecution_NoParent_HasManyAbandonedChildren() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) for i := 0; i < 10; i++ { _, _, err = mutableState.AddStartChildWorkflowExecutionInitiatedEvent(decisionCompletionID, uuid.New(), &types.StartChildWorkflowExecutionDecisionAttributes{ WorkflowID: "child workflow" + strconv.Itoa(i), WorkflowType: &types.WorkflowType{ Name: "child workflow type", }, TaskList: &types.TaskList{Name: mutableState.GetExecutionInfo().TaskList}, Input: []byte("random input"), ParentClosePolicy: types.ParentClosePolicyAbandon.Ptr(), }) s.Nil(err) } s.NoError(mutableState.FlushBufferedEvents()) event := test.AddCompleteWorkflowEvent(mutableState, decisionCompletionID, nil) transferTask := s.newTransferTaskFromInfo(&persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockVisibilityMgr.On("RecordWorkflowExecutionClosed", mock.Anything, mock.Anything).Return(nil).Once() s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewDisabledArchvialConfig()) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessCancelExecution_Success() { s.testProcessCancelExecution( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, requestCancelInfo *persistence.RequestCancelInfo, ) { persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) cancelRequest := createTestRequestCancelWorkflowExecutionRequest(constants.TestDomainName, transferTask.GetInfo().(*persistence.CancelExecutionTask), requestCancelInfo.CancelRequestID) s.mockHistoryClient.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), cancelRequest).Return(nil).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() }, ) } func (s *transferActiveTaskExecutorSuite) TestProcessCancelExecution_Failure() { s.testProcessCancelExecution( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, requestCancelInfo *persistence.RequestCancelInfo, ) { persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) cancelRequest := createTestRequestCancelWorkflowExecutionRequest(constants.TestDomainName, transferTask.GetInfo().(*persistence.CancelExecutionTask), requestCancelInfo.CancelRequestID) s.mockHistoryClient.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), cancelRequest).Return(&types.EntityNotExistsError{}).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() }, ) } func (s *transferActiveTaskExecutorSuite) TestProcessCancelExecution_Duplication() { s.testProcessCancelExecution( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, requestCancelInfo *persistence.RequestCancelInfo, ) { event = test.AddCancelRequestedEvent(mutableState, event.ID, constants.TestDomainID, targetExecution.GetWorkflowID(), targetExecution.GetRunID()) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) }, ) } func (s *transferActiveTaskExecutorSuite) testProcessCancelExecution( targetDomainID string, setupMockFn func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, cancelInitEvent *types.HistoryEvent, transferTask Task, requestCancelInfo *persistence.RequestCancelInfo, ), ) { s.testProcessCancelExecutionWithError(targetDomainID, setupMockFn, nil) } func (s *transferActiveTaskExecutorSuite) testProcessCancelExecutionWithError( targetDomainID string, setupMockFn func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, cancelInitEvent *types.HistoryEvent, transferTask Task, requestCancelInfo *persistence.RequestCancelInfo, ), expectedErr error, ) { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) targetExecution := types.WorkflowExecution{ WorkflowID: "some random target workflow ID", RunID: uuid.New(), } event, rci := test.AddRequestCancelInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, targetExecution.GetWorkflowID(), targetExecution.GetRunID(), ) transferTask := s.newTransferTaskFromInfo(&persistence.CancelExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TargetDomainID: targetDomainID, TargetWorkflowID: targetExecution.GetWorkflowID(), TargetRunID: targetExecution.GetRunID(), InitiatedID: event.ID, }) setupMockFn(mutableState, workflowExecution, targetExecution, event, transferTask, rci) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Equal(expectedErr, err) } func (s *transferActiveTaskExecutorSuite) TestProcessCancelExecution_WorkflowCancellingItself() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event, rci := test.AddRequestCancelInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID(), ) transferTask := s.newTransferTaskFromInfo(&persistence.CancelExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TargetDomainID: s.domainID, TargetWorkflowID: workflowExecution.GetWorkflowID(), TargetRunID: workflowExecution.GetRunID(), InitiatedID: event.ID, }) setupMockFn := func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, requestCancelInfo *persistence.RequestCancelInfo, ) { persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() } setupMockFn(mutableState, workflowExecution, workflowExecution, event, transferTask, rci) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.NoError(err) } func (s *transferActiveTaskExecutorSuite) TestProcessSignalExecution_Success() { s.testProcessSignalExecution( func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, signalInfo *persistence.SignalInfo, ) { persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) signalRequest := createTestSignalWorkflowExecutionRequest(constants.TestDomainName, transferTask.GetInfo().(*persistence.SignalExecutionTask), signalInfo) s.mockHistoryClient.EXPECT().SignalWorkflowExecution(gomock.Any(), signalRequest).Return(nil).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() taskInfo := transferTask.GetInfo().(*persistence.SignalExecutionTask) s.mockHistoryClient.EXPECT().RemoveSignalMutableState(gomock.Any(), &types.RemoveSignalMutableStateRequest{ DomainUUID: taskInfo.TargetDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: taskInfo.TargetWorkflowID, RunID: taskInfo.TargetRunID, }, RequestID: signalInfo.SignalRequestID, }).Return(nil).Times(1) }, ) } func (s *transferActiveTaskExecutorSuite) TestProcessSignalExecution_Failure() { for name, c := range map[string]struct { Err error ExpectedLogs []string }{ "GenericErr": { Err: assert.AnError, ExpectedLogs: []string{"Failed to signal external workflow execution"}, }, "NotExistsErr": { Err: &types.EntityNotExistsError{}, ExpectedLogs: nil, }, "WorkflowExecutionAlreadyCompleted": { Err: &types.WorkflowExecutionAlreadyCompletedError{}, ExpectedLogs: nil, }, } { s.Run(name, func() { // Need set up the suite manually, since we are in a subtest s.SetupTest() s.testProcessSignalExecutionWithErrorAndLogs( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, signalInfo *persistence.SignalInfo, ) { persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) signalRequest := createTestSignalWorkflowExecutionRequest(constants.TestDomainName, transferTask.GetInfo().(*persistence.SignalExecutionTask), signalInfo) s.mockHistoryClient.EXPECT().SignalWorkflowExecution(gomock.Any(), signalRequest).Return(c.Err).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() }, nil, c.ExpectedLogs, ) }) } } func (s *transferActiveTaskExecutorSuite) TestProcessSignalExecution_Duplication() { s.testProcessSignalExecution( func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, signalInfo *persistence.SignalInfo, ) { event = test.AddSignaledEvent(mutableState, event.ID, constants.TestDomainName, targetExecution.GetWorkflowID(), targetExecution.GetRunID(), nil) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) }, ) } func (s *transferActiveTaskExecutorSuite) TestProcessSignalExecution_WorkflowSignalingItself() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event, signalInfo := test.AddRequestSignalInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID(), "some random signal name", []byte("some random signal input"), []byte("some random signal control"), ) transferTask := s.newTransferTaskFromInfo(&persistence.SignalExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TargetDomainID: s.domainID, TargetWorkflowID: workflowExecution.GetWorkflowID(), TargetRunID: workflowExecution.GetRunID(), InitiatedID: event.ID, }) // Make sure we can observe the logs observedZapCore, _ := observer.New(zap.InfoLevel) s.transferActiveTaskExecutor.logger = log.NewLogger(zap.New(observedZapCore)) setupMockFn := func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, signalInfo *persistence.SignalInfo, ) { mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() } setupMockFn(mutableState, workflowExecution, workflowExecution, event, transferTask, signalInfo) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.NoError(err) } func (s *transferActiveTaskExecutorSuite) testProcessSignalExecution( setupMockFn func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, signalInitEvent *types.HistoryEvent, transferTask Task, signalInfo *persistence.SignalInfo, ), ) { s.testProcessSignalExecutionWithErrorAndLogs(constants.TestDomainID, setupMockFn, nil, nil) } func (s *transferActiveTaskExecutorSuite) testProcessSignalExecutionWithErrorAndLogs( targetDomainID string, setupMockFn func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, signalInitEvent *types.HistoryEvent, transferTask Task, signalInfo *persistence.SignalInfo, ), expectedErr error, expectedLogs []string, ) { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) targetExecution := types.WorkflowExecution{ WorkflowID: "some random target workflow ID", RunID: uuid.New(), } event, signalInfo := test.AddRequestSignalInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, targetExecution.GetWorkflowID(), targetExecution.GetRunID(), "some random signal name", []byte("some random signal input"), []byte("some random signal control"), ) transferTask := s.newTransferTaskFromInfo(&persistence.SignalExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TargetDomainID: targetDomainID, TargetWorkflowID: targetExecution.GetWorkflowID(), TargetRunID: targetExecution.GetRunID(), InitiatedID: event.ID, }) // Make sure we can observe the logs observedZapCore, observedLogs := observer.New(zap.InfoLevel) s.transferActiveTaskExecutor.logger = log.NewLogger(zap.New(observedZapCore)) setupMockFn(mutableState, workflowExecution, targetExecution, event, transferTask, signalInfo) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Equal(expectedErr, err) assert.Equal(s.T(), len(expectedLogs), observedLogs.Len(), "expected %v logs, but got %v logs", len(expectedLogs), observedLogs.Len()) for _, expectedLog := range expectedLogs { s.True(observedLogs.FilterMessage(expectedLog).Len() > 0, "expected log missing: %v", expectedLog) } } func (s *transferActiveTaskExecutorSuite) TestProcessStartChildExecution_Success() { s.testProcessStartChildExecution( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, childExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, childInfo *persistence.ChildExecutionInfo, ) { persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) taskInfo := transferTask.GetInfo().(*persistence.StartChildExecutionTask) event, err = mutableState.GetChildExecutionInitiatedEvent(context.Background(), taskInfo.InitiatedID) s.NoError(err) historyReq, err := createTestChildWorkflowExecutionRequest( s.domainName, constants.TestDomainName, taskInfo, event.StartChildWorkflowExecutionInitiatedEventAttributes, childInfo.CreateRequestID, s.mockShard.GetTimeSource().Now(), mutableState.GetExecutionInfo().PartitionConfig, mutableState.GetExecutionInfo().ActiveClusterSelectionPolicy, ) require.NoError(s.T(), err) s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), historyReq).Return(&types.StartWorkflowExecutionResponse{RunID: childExecution.RunID}, nil).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() s.mockHistoryClient.EXPECT().ScheduleDecisionTask(gomock.Any(), &types.ScheduleDecisionTaskRequest{ DomainUUID: constants.TestDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childExecution.WorkflowID, RunID: childExecution.RunID, }, IsFirstDecision: true, }).Return(nil).Times(1) }, ) } func (s *transferActiveTaskExecutorSuite) TestProcessStartChildExecution_Failure() { s.testProcessStartChildExecution( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, childExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, childInfo *persistence.ChildExecutionInfo, ) { persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) taskInfo := transferTask.GetInfo().(*persistence.StartChildExecutionTask) event, err = mutableState.GetChildExecutionInitiatedEvent(context.Background(), taskInfo.InitiatedID) s.NoError(err) historyReq, err := createTestChildWorkflowExecutionRequest( s.domainName, constants.TestDomainName, taskInfo, event.StartChildWorkflowExecutionInitiatedEventAttributes, childInfo.CreateRequestID, s.mockShard.GetTimeSource().Now(), mutableState.GetExecutionInfo().PartitionConfig, mutableState.GetExecutionInfo().ActiveClusterSelectionPolicy, ) require.NoError(s.T(), err) s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), historyReq).Return(nil, &types.WorkflowExecutionAlreadyStartedError{}).Times(1) s.mockHistoryV2Mgr.On("AppendHistoryNodes", mock.Anything, mock.Anything).Return(&persistence.AppendHistoryNodesResponse{}, nil).Once() s.mockExecutionMgr.On("UpdateWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.UpdateWorkflowExecutionResponse{MutableStateUpdateSessionStats: &persistence.MutableStateUpdateSessionStats{}}, nil).Once() }, ) } // This test was originally written for the Cross-cluster use-case where the target domain is not active. // However, it remains a valid test for the scenario where there's a race between parent and child in transfer // tasks. // ie: When a failover has occurred, and the parent workflow has spawned a child, this has been picked up by a // host which has not yet updated it's domain-cache to include the new information that the domain has failed over // and incorrectly thinks that the domain is not active. // In this case the correct behaviour would be to return the domainNotActive error and ensure that it's retried on the // host running the child workflow. func (s *transferActiveTaskExecutorSuite) TestProcessStartChildExecution_TargetNotActive() { s.testProcessStartChildExecutionWithError( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, childExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, childInfo *persistence.ChildExecutionInfo, ) { persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) taskInfo := transferTask.GetInfo().(*persistence.StartChildExecutionTask) _, err = mutableState.GetChildExecutionInitiatedEvent(context.Background(), taskInfo.InitiatedID) s.NoError(err) s.mockHistoryClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &types.DomainNotActiveError{ Message: "domain not active error 123", }).Times(1) }, &types.DomainNotActiveError{ Message: "domain not active error 123", }, ) } func (s *transferActiveTaskExecutorSuite) TestProcessStartChildExecution_Success_Dup() { s.testProcessStartChildExecution( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, childExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, childInfo *persistence.ChildExecutionInfo, ) { startEvent := test.AddChildWorkflowExecutionStartedEvent(mutableState, event.ID, constants.TestDomainID, childExecution.WorkflowID, childExecution.RunID, childInfo.WorkflowTypeName) childInfo.StartedID = startEvent.ID mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startEvent.ID, startEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockHistoryClient.EXPECT().ScheduleDecisionTask(gomock.Any(), &types.ScheduleDecisionTaskRequest{ DomainUUID: constants.TestDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childExecution.WorkflowID, RunID: childExecution.RunID, }, IsFirstDecision: true, }).Return(nil).Times(1) }, ) } func (s *transferActiveTaskExecutorSuite) TestProcessStartChildExecution_Dup_TargetNotActive() { s.testProcessStartChildExecutionWithError( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, childExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, childInfo *persistence.ChildExecutionInfo, ) { startEvent := test.AddChildWorkflowExecutionStartedEvent(mutableState, event.ID, constants.TestDomainID, childExecution.WorkflowID, childExecution.RunID, childInfo.WorkflowTypeName) childInfo.StartedID = startEvent.ID mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startEvent.ID, startEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockHistoryClient.EXPECT().ScheduleDecisionTask(gomock.Any(), gomock.Any()).Return(&types.DomainNotActiveError{}).Times(1) }, &types.DomainNotActiveError{}, ) } func (s *transferActiveTaskExecutorSuite) TestProcessStartChildExecution_Duplication() { s.testProcessStartChildExecution( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, childExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, childInfo *persistence.ChildExecutionInfo, ) { startEvent := test.AddChildWorkflowExecutionStartedEvent(mutableState, event.ID, constants.TestDomainID, childExecution.GetWorkflowID(), childExecution.GetRunID(), childInfo.WorkflowTypeName) childInfo.StartedID = startEvent.ID startEvent = test.AddChildWorkflowExecutionCompletedEvent(mutableState, childInfo.InitiatedID, &childExecution, &types.WorkflowExecutionCompletedEventAttributes{ Result: []byte("some random child workflow execution result"), DecisionTaskCompletedEventID: transferTask.GetInfo().(*persistence.StartChildExecutionTask).InitiatedID, }) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startEvent.ID, startEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) }, ) } func (s *transferActiveTaskExecutorSuite) TestProcessStartChildExecution_StartedAbandonChild_ParentClosed() { s.testProcessStartChildExecution( constants.TestDomainID, func( mutableState execution.MutableState, workflowExecution, childExecution types.WorkflowExecution, event *types.HistoryEvent, transferTask Task, childInfo *persistence.ChildExecutionInfo, ) { event = test.AddChildWorkflowExecutionStartedEvent(mutableState, event.ID, constants.TestDomainID, childExecution.WorkflowID, childExecution.RunID, childInfo.WorkflowTypeName) childInfo.StartedID = event.ID di := test.AddDecisionTaskScheduledEvent(mutableState) event = test.AddDecisionTaskStartedEvent(mutableState, di.ScheduleID, mutableState.GetExecutionInfo().TaskList, "some random identity") event = test.AddDecisionTaskCompletedEvent(mutableState, di.ScheduleID, event.ID, nil, "some random identity") event = test.AddCompleteWorkflowEvent(mutableState, event.ID, nil) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockHistoryClient.EXPECT().ScheduleDecisionTask(gomock.Any(), &types.ScheduleDecisionTaskRequest{ DomainUUID: constants.TestDomainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: childExecution.WorkflowID, RunID: childExecution.RunID, }, IsFirstDecision: true, }).Return(nil).Times(1) }, ) } func (s *transferActiveTaskExecutorSuite) testProcessStartChildExecution( targetDomainID string, setupMockFn func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, childInitEvent *types.HistoryEvent, transferTask Task, childInfo *persistence.ChildExecutionInfo, ), ) { s.testProcessStartChildExecutionWithError(targetDomainID, setupMockFn, nil) } func (s *transferActiveTaskExecutorSuite) testProcessStartChildExecutionWithError( targetDomainID string, setupMockFn func( mutableState execution.MutableState, workflowExecution, targetExecution types.WorkflowExecution, childInitEvent *types.HistoryEvent, transferTask Task, childInfo *persistence.ChildExecutionInfo, ), expectedErr error, ) { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) childExecution := types.WorkflowExecution{ WorkflowID: "some random child workflow ID", RunID: uuid.New(), } event, ci := test.AddStartChildWorkflowExecutionInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, childExecution.WorkflowID, "some random child workflow type", "some random child task list", nil, 1, 1, &types.RetryPolicy{ ExpirationIntervalInSeconds: 100, MaximumAttempts: 3, InitialIntervalInSeconds: 1, MaximumIntervalInSeconds: 2, BackoffCoefficient: 1, }, ) transferTask := s.newTransferTaskFromInfo(&persistence.StartChildExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, TargetDomainID: targetDomainID, TargetWorkflowID: childExecution.WorkflowID, InitiatedID: event.ID, }) setupMockFn(mutableState, workflowExecution, childExecution, event, transferTask, ci) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Equal(expectedErr, err) } func (s *transferActiveTaskExecutorSuite) TestProcessRecordWorkflowStartedTask() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) executionInfo := mutableState.GetExecutionInfo() executionInfo.CronSchedule = "@every 5s" executionInfo.ActiveClusterSelectionPolicy = &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, } startEvent, err := mutableState.GetStartEvent(context.Background()) s.NoError(err) startEvent.WorkflowExecutionStartedEventAttributes.FirstDecisionTaskBackoffSeconds = common.Int32Ptr(5) transferTask := s.newTransferTaskFromInfo(&persistence.RecordWorkflowStartedTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) if s.mockShard.GetConfig().EnableRecordWorkflowExecutionUninitialized(s.domainName) { s.mockVisibilityMgr.On( "RecordWorkflowExecutionUninitialized", mock.Anything, createRecordWorkflowExecutionUninitializedRequest(transferTask, mutableState, s.mockShard.GetTimeSource().Now(), 1234), ).Once().Return(nil) } s.mockVisibilityMgr.On( "RecordWorkflowExecutionStarted", mock.Anything, createRecordWorkflowExecutionStartedRequest( s.T(), s.domainName, startEvent, transferTask, mutableState, 2, s.mockShard.GetTimeSource().Now(), false), ).Once().Return(nil) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessRecordWorkflowStartedTaskWithContextHeader() { // switch on context header in viz s.mockShard.GetConfig().EnableContextHeaderInVisibility = func(domain string) bool { return true } s.mockShard.GetConfig().ValidSearchAttributes = func(opts ...dynamicproperties.FilterOption) map[string]interface{} { return map[string]interface{}{ "Header_context_key": struct{}{}, "123456": struct{}{}, // unsanitizable key } } workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) executionInfo := mutableState.GetExecutionInfo() executionInfo.CronSchedule = "@every 5s" startEvent, err := mutableState.GetStartEvent(context.Background()) s.NoError(err) startEvent.WorkflowExecutionStartedEventAttributes.FirstDecisionTaskBackoffSeconds = common.Int32Ptr(5) transferTask := s.newTransferTaskFromInfo(&persistence.RecordWorkflowStartedTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) if s.mockShard.GetConfig().EnableRecordWorkflowExecutionUninitialized(s.domainName) { s.mockVisibilityMgr.On( "RecordWorkflowExecutionUninitialized", mock.Anything, createRecordWorkflowExecutionUninitializedRequest(transferTask, mutableState, s.mockShard.GetTimeSource().Now(), 1234), ).Once().Return(nil) } s.mockVisibilityMgr.On( "RecordWorkflowExecutionStarted", mock.Anything, createRecordWorkflowExecutionStartedRequest( s.T(), s.domainName, startEvent, transferTask, mutableState, 2, s.mockShard.GetTimeSource().Now(), true), ).Once().Return(nil) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessUpsertWorkflowSearchAttributes() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) executionInfo := mutableState.GetExecutionInfo() executionInfo.ActiveClusterSelectionPolicy = &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, } transferTask := s.newTransferTaskFromInfo(&persistence.UpsertWorkflowSearchAttributesTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) startEvent, err := mutableState.GetStartEvent(context.Background()) s.NoError(err) s.mockVisibilityMgr.On( "UpsertWorkflowExecution", mock.Anything, createUpsertWorkflowSearchAttributesRequest( s.T(), s.domainName, startEvent, transferTask, mutableState, 2, s.mockShard.GetTimeSource().Now(), false), ).Once().Return(nil) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessUpsertWorkflowSearchAttributesWithContextHeader() { // switch on context header in viz s.mockShard.GetConfig().EnableContextHeaderInVisibility = func(domain string) bool { return true } s.mockShard.GetConfig().ValidSearchAttributes = func(opts ...dynamicproperties.FilterOption) map[string]interface{} { return map[string]interface{}{ "Header_context_key": struct{}{}, } } workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) transferTask := s.newTransferTaskFromInfo(&persistence.UpsertWorkflowSearchAttributesTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) startEvent, err := mutableState.GetStartEvent(context.Background()) s.NoError(err) s.mockVisibilityMgr.On( "UpsertWorkflowExecution", mock.Anything, createUpsertWorkflowSearchAttributesRequest( s.T(), s.domainName, startEvent, transferTask, mutableState, 2, s.mockShard.GetTimeSource().Now(), true), ).Once().Return(nil) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessResetWorkflow_ResetPointNil() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) transferTask := s.newTransferTaskFromInfo(&persistence.ResetWorkflowTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessResetWorkflow_WorkflowNotRunning() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) transferTask := s.newTransferTaskFromInfo(&persistence.ResetWorkflowTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) mutableState.GetExecutionInfo().State = persistence.WorkflowStateCompleted persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockExecutionMgr.On("GetCurrentExecution", mock.Anything, &persistence.GetCurrentExecutionRequest{DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), DomainName: s.domainName}). Return(&persistence.GetCurrentExecutionResponse{RunID: "runID"}, nil) _, err = s.transferActiveTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferActiveTaskExecutorSuite) TestProcessResetWorkflow_Success() { s.testProcessResetWorkflowWithError(true, nil, nil) } func (s *transferActiveTaskExecutorSuite) TestProcessResetWorkflow_DifferentRunID() { s.testProcessResetWorkflowWithError(false, nil, nil) } func (s *transferActiveTaskExecutorSuite) TestProcessResetWorkflow_CorruptedResetPoint() { s.testProcessResetWorkflowWithError(true, &types.BadRequestError{}, nil) } func (s *transferActiveTaskExecutorSuite) TestProcessResetWorkflow_OtherError() { s.testProcessResetWorkflowWithError(true, errors.New("some random error"), errors.New("some random error")) } func (s *transferActiveTaskExecutorSuite) testProcessResetWorkflowWithError(sameRunID bool, resetError error, returnErr error) { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) s.domainEntry.GetConfig().BadBinaries = types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ "test-binary-checksum": { Reason: "test-reason", Operator: "test-operator", CreatedTimeNano: common.Ptr(time.Now().UnixNano()), }, }, } transferTask := s.newTransferTaskFromInfo(&persistence.ResetWorkflowTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, TaskID: int64(59), }, }) firstDecisionCompletedID := int64(2) runID := workflowExecution.GetRunID() if !sameRunID { runID = uuid.New() } resetPoints := &types.ResetPoints{ Points: []*types.ResetPointInfo{ { BinaryChecksum: "test-binary-checksum", RunID: runID, FirstDecisionCompletedID: firstDecisionCompletedID, Resettable: true, }, }, } mutableState.GetExecutionInfo().AutoResetPoints = resetPoints persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) workflowResetter := reset.NewMockWorkflowResetter(s.controller) s.transferActiveTaskExecutor.workflowResetter = workflowResetter versionHistories := mutableState.GetVersionHistories() currentVersionHistory, err := versionHistories.GetCurrentVersionHistory() s.NoError(err) currentBranchToken := currentVersionHistory.GetBranchToken() rebuildLastEventVersion, err := currentVersionHistory.GetEventVersion(firstDecisionCompletedID) s.NoError(err) workflowResetter.EXPECT().ResetWorkflow( gomock.Any(), s.domainID, workflowExecution.GetWorkflowID(), workflowExecution.GetRunID(), currentBranchToken, firstDecisionCompletedID-1, rebuildLastEventVersion, mutableState.GetNextEventID(), gomock.Any(), gomock.Any(), gomock.Any(), "test-reason", nil, false).Return(resetError).Times(1) _, err = s.transferActiveTaskExecutor.Execute(transferTask) if returnErr != nil { s.Equal(returnErr, err) } else { s.Nil(err) } } func (s *transferActiveTaskExecutorSuite) TestCopySearchAttributes() { var input map[string][]byte s.Nil(copySearchAttributes(input)) key := "key" val := []byte{'1', '2', '3'} input = map[string][]byte{ key: val, } result := copySearchAttributes(input) s.Equal(input, result) result[key][0] = '0' s.Equal(byte('1'), val[0]) } func (s *transferActiveTaskExecutorSuite) newTransferTaskFromInfo( task persistence.Task, ) Task { return NewHistoryTask(s.mockShard, task, QueueTypeActiveTransfer, s.logger, nil, nil, nil, nil, nil) } func createAddActivityTaskRequest( transferTask Task, ai *persistence.ActivityInfo, partitionConfig map[string]string, ) *types.AddActivityTaskRequest { taskInfo := transferTask.GetInfo().(*persistence.ActivityTask) workflowExecution := types.WorkflowExecution{ WorkflowID: taskInfo.WorkflowID, RunID: taskInfo.RunID, } taskList := &types.TaskList{Name: ai.TaskList, Kind: ai.TaskListKind.Ptr()} return &types.AddActivityTaskRequest{ DomainUUID: taskInfo.TargetDomainID, SourceDomainUUID: taskInfo.DomainID, Execution: &workflowExecution, TaskList: taskList, ScheduleID: taskInfo.ScheduleID, ScheduleToStartTimeoutSeconds: common.Int32Ptr(ai.ScheduleToStartTimeout), PartitionConfig: partitionConfig, } } func createAddDecisionTaskRequest( transferTask Task, mutableState execution.MutableState, ) *types.AddDecisionTaskRequest { taskInfo := transferTask.GetInfo().(*persistence.DecisionTask) workflowExecution := types.WorkflowExecution{ WorkflowID: taskInfo.WorkflowID, RunID: taskInfo.RunID, } taskList := &types.TaskList{Name: taskInfo.TaskList, Kind: mutableState.GetExecutionInfo().TaskListKind.Ptr()} executionInfo := mutableState.GetExecutionInfo() timeout := executionInfo.WorkflowTimeout if mutableState.GetExecutionInfo().TaskList != taskInfo.TaskList { taskListStickyKind := types.TaskListKindSticky taskList.Kind = &taskListStickyKind timeout = executionInfo.StickyScheduleToStartTimeout } return &types.AddDecisionTaskRequest{ DomainUUID: taskInfo.DomainID, Execution: &workflowExecution, TaskList: taskList, ScheduleID: taskInfo.ScheduleID, ScheduleToStartTimeoutSeconds: common.Int32Ptr(timeout), PartitionConfig: executionInfo.PartitionConfig, } } func createRecordWorkflowExecutionStartedRequest( t *testing.T, domainName string, startEvent *types.HistoryEvent, transferTask Task, mutableState execution.MutableState, numClusters int16, updateTime time.Time, enableContextHeaderInVisibility bool, ) *persistence.RecordWorkflowExecutionStartedRequest { taskInfo := transferTask.GetInfo().(*persistence.RecordWorkflowStartedTask) workflowExecution := types.WorkflowExecution{ WorkflowID: taskInfo.WorkflowID, RunID: taskInfo.RunID, } executionInfo := mutableState.GetExecutionInfo() backoffSeconds := startEvent.WorkflowExecutionStartedEventAttributes.GetFirstDecisionTaskBackoffSeconds() executionTimestamp := int64(0) if backoffSeconds != 0 { executionTimestamp = startEvent.GetTimestamp() + int64(backoffSeconds)*int64(time.Second) } var searchAttributes map[string][]byte if enableContextHeaderInVisibility { contextValueJSONString, err := json.Marshal("contextValue") if err != nil { t.Fatal(err) } searchAttributes = map[string][]byte{ "Header_context_key": contextValueJSONString, } } executionStatus, scheduledExecutionTimestamp := determineExecutionStatusForVisibility(startEvent, mutableState, false) return &persistence.RecordWorkflowExecutionStartedRequest{ Domain: domainName, DomainUUID: taskInfo.DomainID, Execution: workflowExecution, WorkflowTypeName: executionInfo.WorkflowTypeName, StartTimestamp: startEvent.GetTimestamp(), ExecutionTimestamp: executionTimestamp, WorkflowTimeout: int64(executionInfo.WorkflowTimeout), TaskID: taskInfo.TaskID, TaskList: executionInfo.TaskList, IsCron: len(executionInfo.CronSchedule) > 0, NumClusters: numClusters, ClusterAttributeScope: executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute().GetScope(), ClusterAttributeName: executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute().GetName(), UpdateTimestamp: updateTime.UnixNano(), SearchAttributes: searchAttributes, ExecutionStatus: executionStatus, CronSchedule: executionInfo.CronSchedule, ScheduledExecutionTimestamp: scheduledExecutionTimestamp, } } func createRecordWorkflowExecutionClosedRequest( t *testing.T, domainName string, startEvent *types.HistoryEvent, transferTask Task, mutableState execution.MutableState, numClusters int16, updateTime time.Time, closeTimestamp *int64, enableContextHeaderInVisibility bool, ) *persistence.RecordWorkflowExecutionClosedRequest { taskInfo := transferTask.GetInfo().(*persistence.CloseExecutionTask) workflowExecution := types.WorkflowExecution{ WorkflowID: taskInfo.WorkflowID, RunID: taskInfo.RunID, } executionInfo := mutableState.GetExecutionInfo() backoffSeconds := startEvent.WorkflowExecutionStartedEventAttributes.GetFirstDecisionTaskBackoffSeconds() executionTimestamp := int64(0) if backoffSeconds != 0 { executionTimestamp = startEvent.GetTimestamp() + int64(backoffSeconds)*int64(time.Second) } var searchAttributes map[string][]byte if enableContextHeaderInVisibility { contextValueJSONString, err := json.Marshal("contextValue") if err != nil { t.Fatal(err) } searchAttributes = map[string][]byte{ "Header_context_key": contextValueJSONString, } } scheduledExecutionTimestamp := int64(0) if executionTimestamp != 0 { scheduledExecutionTimestamp = executionTimestamp } // Get completion event to determine execution status // For closed workflows, ExecutionStatus should reflect the close status (COMPLETED, FAILED, etc.) // not the running status (PENDING, STARTED) completionEvent, err := mutableState.GetCompletionEvent(context.Background()) if err != nil { t.Fatal(err) } executionStatus := getTestWorkflowExecutionStatus(completionEvent) return &persistence.RecordWorkflowExecutionClosedRequest{ Domain: domainName, DomainUUID: taskInfo.DomainID, Execution: workflowExecution, HistoryLength: mutableState.GetNextEventID() - 1, WorkflowTypeName: executionInfo.WorkflowTypeName, StartTimestamp: startEvent.GetTimestamp(), ExecutionTimestamp: executionTimestamp, TaskID: taskInfo.TaskID, TaskList: executionInfo.TaskList, IsCron: len(executionInfo.CronSchedule) > 0, NumClusters: numClusters, ClusterAttributeScope: executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute().GetScope(), ClusterAttributeName: executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute().GetName(), UpdateTimestamp: updateTime.UnixNano(), CloseTimestamp: *closeTimestamp, RetentionSeconds: int64(mutableState.GetDomainEntry().GetRetentionDays(taskInfo.GetWorkflowID()) * 24 * 3600), SearchAttributes: searchAttributes, ExecutionStatus: executionStatus, CronSchedule: executionInfo.CronSchedule, ScheduledExecutionTimestamp: scheduledExecutionTimestamp, } } // getTestWorkflowExecutionStatus determines execution status from close event // This mirrors the logic in getWorkflowExecutionStatus in transfer_active_task_executor.go func getTestWorkflowExecutionStatus(closeEvent *types.HistoryEvent) types.WorkflowExecutionStatus { switch closeEvent.GetEventType() { case types.EventTypeWorkflowExecutionCompleted: return types.WorkflowExecutionStatusCompleted case types.EventTypeWorkflowExecutionFailed: return types.WorkflowExecutionStatusFailed case types.EventTypeWorkflowExecutionCanceled: return types.WorkflowExecutionStatusCanceled case types.EventTypeWorkflowExecutionTerminated: return types.WorkflowExecutionStatusTerminated case types.EventTypeWorkflowExecutionTimedOut: return types.WorkflowExecutionStatusTimedOut case types.EventTypeWorkflowExecutionContinuedAsNew: // For simplicity in tests, return ContinuedAsNew // Production code has more complex logic for cron workflows return types.WorkflowExecutionStatusContinuedAsNew default: return types.WorkflowExecutionStatusCompleted } } func createTestRequestCancelWorkflowExecutionRequest( targetDomainName string, taskInfo *persistence.CancelExecutionTask, requestID string, ) *types.HistoryRequestCancelWorkflowExecutionRequest { sourceExecution := types.WorkflowExecution{ WorkflowID: taskInfo.WorkflowID, RunID: taskInfo.RunID, } targetExecution := types.WorkflowExecution{ WorkflowID: taskInfo.TargetWorkflowID, RunID: taskInfo.TargetRunID, } return &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: taskInfo.TargetDomainID, CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Domain: targetDomainName, WorkflowExecution: &targetExecution, Identity: execution.IdentityHistoryService, // Use the same request ID to dedupe RequestCancelWorkflowExecution calls RequestID: requestID, }, ExternalInitiatedEventID: common.Int64Ptr(taskInfo.InitiatedID), ExternalWorkflowExecution: &sourceExecution, ChildWorkflowOnly: taskInfo.TargetChildWorkflowOnly, } } func createTestSignalWorkflowExecutionRequest( targetDomainName string, taskInfo *persistence.SignalExecutionTask, si *persistence.SignalInfo, ) *types.HistorySignalWorkflowExecutionRequest { sourceExecution := types.WorkflowExecution{ WorkflowID: taskInfo.WorkflowID, RunID: taskInfo.RunID, } targetExecution := types.WorkflowExecution{ WorkflowID: taskInfo.TargetWorkflowID, RunID: taskInfo.TargetRunID, } return &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: taskInfo.TargetDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ Domain: targetDomainName, WorkflowExecution: &targetExecution, Identity: execution.IdentityHistoryService, SignalName: si.SignalName, Input: si.Input, RequestID: si.SignalRequestID, Control: si.Control, }, ExternalWorkflowExecution: &sourceExecution, ChildWorkflowOnly: taskInfo.TargetChildWorkflowOnly, } } func createTestChildWorkflowExecutionRequest( domainName string, childDomainName string, taskInfo *persistence.StartChildExecutionTask, attributes *types.StartChildWorkflowExecutionInitiatedEventAttributes, requestID string, now time.Time, partitionConfig map[string]string, activeClusterSelectionPolicy *types.ActiveClusterSelectionPolicy, ) (*types.HistoryStartWorkflowExecutionRequest, error) { workflowExecution := types.WorkflowExecution{ WorkflowID: taskInfo.WorkflowID, RunID: taskInfo.RunID, } frontendStartReq := &types.StartWorkflowExecutionRequest{ Domain: childDomainName, WorkflowID: attributes.WorkflowID, WorkflowType: attributes.WorkflowType, TaskList: attributes.TaskList, Input: attributes.Input, ExecutionStartToCloseTimeoutSeconds: attributes.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: attributes.TaskStartToCloseTimeoutSeconds, // Use the same request ID to dedupe StartWorkflowExecution calls RequestID: requestID, WorkflowIDReusePolicy: attributes.WorkflowIDReusePolicy, RetryPolicy: attributes.RetryPolicy, ActiveClusterSelectionPolicy: activeClusterSelectionPolicy, } parentInfo := &types.ParentExecutionInfo{ DomainUUID: taskInfo.DomainID, Domain: domainName, Execution: &workflowExecution, InitiatedID: taskInfo.InitiatedID, } historyStartReq, err := common.CreateHistoryStartWorkflowRequest( taskInfo.TargetDomainID, frontendStartReq, now, partitionConfig) if err != nil { return nil, err } historyStartReq.ParentExecutionInfo = parentInfo return historyStartReq, nil } func createUpsertWorkflowSearchAttributesRequest( t *testing.T, domainName string, startEvent *types.HistoryEvent, transferTask Task, mutableState execution.MutableState, numClusters int16, updateTime time.Time, enableContextHeaderInVisibility bool, ) *persistence.UpsertWorkflowExecutionRequest { taskInfo := transferTask.GetInfo().(*persistence.UpsertWorkflowSearchAttributesTask) workflowExecution := types.WorkflowExecution{ WorkflowID: taskInfo.WorkflowID, RunID: taskInfo.RunID, } executionInfo := mutableState.GetExecutionInfo() backoffSeconds := startEvent.WorkflowExecutionStartedEventAttributes.GetFirstDecisionTaskBackoffSeconds() executionTimestamp := int64(0) if backoffSeconds != 0 { executionTimestamp = startEvent.GetTimestamp() + int64(backoffSeconds)*int64(time.Second) } var searchAttributes map[string][]byte if enableContextHeaderInVisibility { contextValueJSONString, err := json.Marshal("contextValue") if err != nil { t.Fatal(err) } searchAttributes = map[string][]byte{ "Header_context_key": contextValueJSONString, } } executionStatus, scheduledExecutionTimestamp := determineExecutionStatus(startEvent, mutableState) return &persistence.UpsertWorkflowExecutionRequest{ Domain: domainName, DomainUUID: taskInfo.DomainID, Execution: workflowExecution, WorkflowTypeName: executionInfo.WorkflowTypeName, StartTimestamp: startEvent.GetTimestamp(), ExecutionTimestamp: executionTimestamp, WorkflowTimeout: int64(executionInfo.WorkflowTimeout), TaskID: taskInfo.TaskID, TaskList: executionInfo.TaskList, IsCron: len(executionInfo.CronSchedule) > 0, NumClusters: numClusters, ClusterAttributeScope: executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute().GetScope(), ClusterAttributeName: executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute().GetName(), UpdateTimestamp: updateTime.UnixNano(), SearchAttributes: searchAttributes, ExecutionStatus: executionStatus, CronSchedule: executionInfo.CronSchedule, ScheduledExecutionTimestamp: scheduledExecutionTimestamp, } } func createRecordWorkflowExecutionUninitializedRequest( transferTask Task, mutableState execution.MutableState, updateTime time.Time, shardID int64, ) *persistence.RecordWorkflowExecutionUninitializedRequest { workflowExecution := types.WorkflowExecution{ WorkflowID: transferTask.GetWorkflowID(), RunID: transferTask.GetRunID(), } executionInfo := mutableState.GetExecutionInfo() return &persistence.RecordWorkflowExecutionUninitializedRequest{ DomainUUID: transferTask.GetDomainID(), Execution: workflowExecution, WorkflowTypeName: executionInfo.WorkflowTypeName, UpdateTimestamp: updateTime.UnixNano(), ShardID: shardID, } } ================================================ FILE: service/history/task/transfer_standby_task_executor.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "fmt" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/history/simulation" "github.com/uber/cadence/service/worker/archiver" ) type ( transferStandbyTaskExecutor struct { *transferTaskExecutorBase clusterName string historyResender ndc.HistoryResender getRemoteClusterNameFn func(context.Context, persistence.Task) (string, error) } ) // NewTransferStandbyTaskExecutor creates a new task executor for standby transfer task func NewTransferStandbyTaskExecutor( shard shard.Context, archiverClient archiver.Client, executionCache execution.Cache, historyResender ndc.HistoryResender, logger log.Logger, clusterName string, config *config.Config, ) Executor { return &transferStandbyTaskExecutor{ transferTaskExecutorBase: newTransferTaskExecutorBase( shard, archiverClient, executionCache, logger, config, ), clusterName: clusterName, historyResender: historyResender, getRemoteClusterNameFn: func(ctx context.Context, taskInfo persistence.Task) (string, error) { if shard.GetConfig().EnableTransferQueueV2(shard.GetShardID()) { return getRemoteClusterName(ctx, shard.GetClusterMetadata().GetCurrentClusterName(), shard.GetActiveClusterManager(), taskInfo) } return clusterName, nil }, } } func (t *transferStandbyTaskExecutor) Execute(task Task) (ExecuteResponse, error) { simulation.LogEvents(simulation.E{ EventName: simulation.EventNameExecuteHistoryTask, Host: t.shard.GetConfig().HostName, ShardID: t.shard.GetShardID(), DomainID: task.GetDomainID(), WorkflowID: task.GetWorkflowID(), RunID: task.GetRunID(), Payload: map[string]any{ "task_category": persistence.HistoryTaskCategoryTransfer.Name(), "task_type": task.GetTaskType(), "task_key": task.GetTaskKey(), }, }) scope := getOrCreateDomainTaggedScope(t.shard, GetTransferTaskMetricsScope(task.GetTaskType(), false), task.GetDomainID(), t.logger) executeResponse := ExecuteResponse{ Scope: scope, IsActiveTask: false, } ctx, cancel := context.WithTimeout(context.Background(), taskDefaultTimeout) defer cancel() switch transferTask := task.GetInfo().(type) { case *persistence.ActivityTask: return executeResponse, t.processActivityTask(ctx, transferTask) case *persistence.DecisionTask: return executeResponse, t.processDecisionTask(ctx, transferTask) case *persistence.CloseExecutionTask: return executeResponse, t.processCloseExecution(ctx, transferTask) case *persistence.RecordWorkflowClosedTask: return executeResponse, t.processCloseExecution(ctx, &persistence.CloseExecutionTask{ WorkflowIdentifier: transferTask.WorkflowIdentifier, TaskData: transferTask.TaskData, }) case *persistence.RecordChildExecutionCompletedTask: // no action needed for standby // check the comment in t.processCloseExecution() return executeResponse, nil case *persistence.CancelExecutionTask: return executeResponse, t.processCancelExecution(ctx, transferTask) case *persistence.SignalExecutionTask: return executeResponse, t.processSignalExecution(ctx, transferTask) case *persistence.StartChildExecutionTask: return executeResponse, t.processStartChildExecution(ctx, transferTask) case *persistence.RecordWorkflowStartedTask: return executeResponse, t.processRecordWorkflowStarted(ctx, transferTask) case *persistence.ResetWorkflowTask: // no reset needed for standby // TODO: add error logs return executeResponse, nil case *persistence.UpsertWorkflowSearchAttributesTask: return executeResponse, t.processUpsertWorkflowSearchAttributes(ctx, transferTask) default: return executeResponse, errUnknownTransferTask } } // Empty func for now func (t *transferStandbyTaskExecutor) Stop() {} func (t *transferStandbyTaskExecutor) processActivityTask( ctx context.Context, transferTask *persistence.ActivityTask, ) error { processTaskIfClosed := false actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { activityInfo, ok := mutableState.GetActivityInfo(transferTask.ScheduleID) if !ok { return nil, nil } ok, err := verifyTaskVersion(t.shard, t.logger, transferTask.DomainID, activityInfo.Version, transferTask.Version, transferTask) if err != nil || !ok { return nil, err } taskList := types.TaskList{ Name: activityInfo.TaskList, Kind: activityInfo.TaskListKind.Ptr(), } if taskList.Name == "" { taskList.Name = transferTask.TaskList } if activityInfo.StartedID == constants.EmptyEventID { return newPushActivityToMatchingInfo( activityInfo.ScheduleToStartTimeout, taskList, mutableState.GetExecutionInfo().PartitionConfig, ), nil } return nil, nil } return t.processTransfer( ctx, processTaskIfClosed, transferTask, transferTask.ScheduleID, actionFn, getStandbyPostActionFn( t.logger, transferTask, t.getCurrentTime, t.config.StandbyTaskMissingEventsResendDelay(), t.config.StandbyTaskMissingEventsDiscardDelay(), t.pushActivity, t.pushActivity, ), ) } func (t *transferStandbyTaskExecutor) processDecisionTask( ctx context.Context, transferTask *persistence.DecisionTask, ) error { processTaskIfClosed := false actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { decisionInfo, ok := mutableState.GetDecisionInfo(transferTask.ScheduleID) if !ok { return nil, nil } executionInfo := mutableState.GetExecutionInfo() workflowTimeout := executionInfo.WorkflowTimeout decisionTimeout := min(workflowTimeout, constants.MaxTaskTimeout) if executionInfo.TaskList != transferTask.TaskList { // Experimental: try to push sticky task as regular task with sticky timeout as TTL. // workflow might be sticky before namespace become standby // there shall already be a schedule_to_start timer created decisionTimeout = executionInfo.StickyScheduleToStartTimeout } ok, err := verifyTaskVersion(t.shard, t.logger, transferTask.DomainID, decisionInfo.Version, transferTask.Version, transferTask) if err != nil || !ok { return nil, err } if decisionInfo.StartedID == constants.EmptyEventID { return newPushDecisionToMatchingInfo( decisionTimeout, types.TaskList{Name: executionInfo.TaskList, Kind: executionInfo.TaskListKind.Ptr()}, // at standby, always use non-sticky tasklist mutableState.GetExecutionInfo().PartitionConfig, ), nil } return nil, nil } return t.processTransfer( ctx, processTaskIfClosed, transferTask, transferTask.ScheduleID, actionFn, getStandbyPostActionFn( t.logger, transferTask, t.getCurrentTime, t.config.StandbyTaskMissingEventsResendDelay(), t.config.StandbyTaskMissingEventsDiscardDelay(), t.pushDecision, t.pushDecision, ), ) } func (t *transferStandbyTaskExecutor) processCloseExecution( ctx context.Context, transferTask *persistence.CloseExecutionTask, ) error { processTaskIfClosed := true actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { if mutableState.IsWorkflowExecutionRunning() { // this can happen if workflow is reset. return nil, nil } completionEvent, err := mutableState.GetCompletionEvent(ctx) if err != nil { return nil, err } wfCloseTime := completionEvent.GetTimestamp() executionInfo := mutableState.GetExecutionInfo() workflowTypeName := executionInfo.WorkflowTypeName workflowCloseTimestamp := wfCloseTime workflowCloseStatus := persistence.ToInternalWorkflowExecutionCloseStatus(executionInfo.CloseStatus) workflowHistoryLength := mutableState.GetNextEventID() - 1 startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return nil, err } workflowStartTimestamp := startEvent.GetTimestamp() workflowExecutionTimestamp := getWorkflowExecutionTimestamp(mutableState, startEvent) visibilityMemo := getWorkflowMemo(executionInfo.Memo) searchAttr := executionInfo.SearchAttributes headers := getWorkflowHeaders(startEvent) isCron := len(executionInfo.CronSchedule) > 0 cronSchedule := "" if isCron { cronSchedule = executionInfo.CronSchedule } updateTimestamp := t.shard.GetTimeSource().Now() lastWriteVersion, err := mutableState.GetLastWriteVersion() if err != nil { return nil, err } ok, err := verifyTaskVersion(t.shard, t.logger, transferTask.DomainID, lastWriteVersion, transferTask.Version, transferTask) if err != nil || !ok { return nil, err } domainEntry, err := t.shard.GetDomainCache().GetDomainByID(transferTask.DomainID) if err != nil { return nil, err } numClusters := (int16)(len(domainEntry.GetReplicationConfig().Clusters)) executionStatus := getWorkflowExecutionStatus(mutableState, completionEvent) // Calculate ScheduledExecutionTime scheduledExecutionTimestamp := startEvent.GetTimestamp() if startEvent.WorkflowExecutionStartedEventAttributes != nil && startEvent.WorkflowExecutionStartedEventAttributes.GetFirstDecisionTaskBackoffSeconds() > 0 { scheduledExecutionTimestamp = startEvent.GetTimestamp() + int64(startEvent.WorkflowExecutionStartedEventAttributes.GetFirstDecisionTaskBackoffSeconds())*int64(time.Second) } // DO NOT REPLY TO PARENT // since event replication should be done by active cluster return nil, t.recordWorkflowClosed( ctx, transferTask.DomainID, transferTask.WorkflowID, transferTask.RunID, workflowTypeName, workflowStartTimestamp, workflowExecutionTimestamp.UnixNano(), workflowCloseTimestamp, *workflowCloseStatus, workflowHistoryLength, transferTask.GetTaskID(), visibilityMemo, executionInfo.TaskList, isCron, cronSchedule, numClusters, updateTimestamp.UnixNano(), searchAttr, headers, executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute(), executionStatus, scheduledExecutionTimestamp, ) } return t.processTransfer( ctx, processTaskIfClosed, transferTask, 0, actionFn, standbyTaskPostActionNoOp, ) // no op post action, since the entire workflow is finished } func (t *transferStandbyTaskExecutor) processCancelExecution( ctx context.Context, transferTask *persistence.CancelExecutionTask, ) error { processTaskIfClosed := false actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { requestCancelInfo, ok := mutableState.GetRequestCancelInfo(transferTask.InitiatedID) if !ok { return nil, nil } ok, err := verifyTaskVersion(t.shard, t.logger, transferTask.DomainID, requestCancelInfo.Version, transferTask.Version, transferTask) if err != nil || !ok { return nil, err } return getHistoryResendInfo(mutableState) } return t.processTransfer( ctx, processTaskIfClosed, transferTask, transferTask.InitiatedID, actionFn, getStandbyPostActionFn( t.logger, transferTask, t.getCurrentTime, t.config.StandbyTaskMissingEventsResendDelay(), t.config.StandbyTaskMissingEventsDiscardDelay(), t.fetchHistoryFromRemote, standbyTaskPostActionTaskDiscarded, ), ) } func (t *transferStandbyTaskExecutor) processSignalExecution( ctx context.Context, transferTask *persistence.SignalExecutionTask, ) error { processTaskIfClosed := false actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { signalInfo, ok := mutableState.GetSignalInfo(transferTask.InitiatedID) if !ok { return nil, nil } ok, err := verifyTaskVersion(t.shard, t.logger, transferTask.DomainID, signalInfo.Version, transferTask.Version, transferTask) if err != nil || !ok { return nil, err } return getHistoryResendInfo(mutableState) } return t.processTransfer( ctx, processTaskIfClosed, transferTask, transferTask.InitiatedID, actionFn, getStandbyPostActionFn( t.logger, transferTask, t.getCurrentTime, t.config.StandbyTaskMissingEventsResendDelay(), t.config.StandbyTaskMissingEventsDiscardDelay(), t.fetchHistoryFromRemote, standbyTaskPostActionTaskDiscarded, ), ) } func (t *transferStandbyTaskExecutor) processStartChildExecution( ctx context.Context, transferTask *persistence.StartChildExecutionTask, ) error { processTaskIfClosed := false actionFn := func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { childWorkflowInfo, ok := mutableState.GetChildExecutionInfo(transferTask.InitiatedID) if !ok { return nil, nil } ok, err := verifyTaskVersion(t.shard, t.logger, transferTask.DomainID, childWorkflowInfo.Version, transferTask.Version, transferTask) if err != nil || !ok { return nil, err } if childWorkflowInfo.StartedID != constants.EmptyEventID { return nil, nil } return getHistoryResendInfo(mutableState) } return t.processTransfer( ctx, processTaskIfClosed, transferTask, transferTask.InitiatedID, actionFn, getStandbyPostActionFn( t.logger, transferTask, t.getCurrentTime, t.config.StandbyTaskMissingEventsResendDelay(), t.config.StandbyTaskMissingEventsDiscardDelay(), t.fetchHistoryFromRemote, standbyTaskPostActionTaskDiscarded, ), ) } func (t *transferStandbyTaskExecutor) processRecordWorkflowStarted( ctx context.Context, transferTask *persistence.RecordWorkflowStartedTask, ) error { processTaskIfClosed := false return t.processTransfer( ctx, processTaskIfClosed, transferTask, 0, func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { return nil, t.processRecordWorkflowStartedOrUpsertHelper(ctx, transferTask, mutableState, true) }, standbyTaskPostActionNoOp, ) } func (t *transferStandbyTaskExecutor) processUpsertWorkflowSearchAttributes( ctx context.Context, transferTask *persistence.UpsertWorkflowSearchAttributesTask, ) error { processTaskIfClosed := false return t.processTransfer( ctx, processTaskIfClosed, transferTask, 0, func(ctx context.Context, wfContext execution.Context, mutableState execution.MutableState) (interface{}, error) { return nil, t.processRecordWorkflowStartedOrUpsertHelper(ctx, transferTask, mutableState, false) }, standbyTaskPostActionNoOp, ) } func (t *transferStandbyTaskExecutor) processRecordWorkflowStartedOrUpsertHelper( ctx context.Context, transferTask persistence.Task, mutableState execution.MutableState, isRecordStart bool, ) error { workflowStartedScope := getOrCreateDomainTaggedScope(t.shard, metrics.TransferStandbyTaskRecordWorkflowStartedScope, transferTask.GetDomainID(), t.logger) // verify task version for RecordWorkflowStarted. // upsert doesn't require verifyTask, because it is just a sync of mutableState. if isRecordStart { startVersion, err := mutableState.GetStartVersion() if err != nil { return err } ok, err := verifyTaskVersion(t.shard, t.logger, transferTask.GetDomainID(), startVersion, transferTask.GetVersion(), transferTask) if err != nil || !ok { return err } } executionInfo := mutableState.GetExecutionInfo() workflowTimeout := executionInfo.WorkflowTimeout wfTypeName := executionInfo.WorkflowTypeName startEvent, err := mutableState.GetStartEvent(ctx) if err != nil { return err } startTimestamp := startEvent.GetTimestamp() executionTimestamp := getWorkflowExecutionTimestamp(mutableState, startEvent) visibilityMemo := getWorkflowMemo(executionInfo.Memo) isCron := len(executionInfo.CronSchedule) > 0 cronSchedule := "" if isCron { cronSchedule = executionInfo.CronSchedule } updateTimestamp := t.shard.GetTimeSource().Now() domainEntry, err := t.shard.GetDomainCache().GetDomainByID(transferTask.GetDomainID()) if err != nil { return err } numClusters := (int16)(len(domainEntry.GetReplicationConfig().Clusters)) searchAttr := copySearchAttributes(executionInfo.SearchAttributes) headers := getWorkflowHeaders(startEvent) isAdvancedVisibilityEnabled := common.IsAdvancedVisibilityWritingEnabled( t.config.WriteVisibilityStoreName(), t.config.IsAdvancedVisConfigExist, ) executionStatus, scheduledExecutionTimestamp := determineExecutionStatusForVisibility(startEvent, mutableState, isAdvancedVisibilityEnabled) if isRecordStart { workflowStartedScope.IncCounter(metrics.WorkflowStartedCount) return t.recordWorkflowStarted( ctx, transferTask.GetDomainID(), transferTask.GetWorkflowID(), transferTask.GetRunID(), wfTypeName, startTimestamp, executionTimestamp.UnixNano(), workflowTimeout, transferTask.GetTaskID(), executionInfo.TaskList, isCron, numClusters, visibilityMemo, updateTimestamp.UnixNano(), searchAttr, headers, executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute(), cronSchedule, executionStatus, scheduledExecutionTimestamp, ) } return t.upsertWorkflowExecution( ctx, transferTask.GetDomainID(), transferTask.GetWorkflowID(), transferTask.GetRunID(), wfTypeName, startTimestamp, executionTimestamp.UnixNano(), workflowTimeout, transferTask.GetTaskID(), executionInfo.TaskList, visibilityMemo, isCron, numClusters, updateTimestamp.UnixNano(), searchAttr, headers, executionInfo.ActiveClusterSelectionPolicy.GetClusterAttribute(), cronSchedule, executionStatus, scheduledExecutionTimestamp, ) } func (t *transferStandbyTaskExecutor) processTransfer( ctx context.Context, processTaskIfClosed bool, transferTask persistence.Task, eventID int64, actionFn standbyActionFn, postActionFn standbyPostActionFn, ) (retError error) { wfContext, release, err := t.executionCache.GetOrCreateWorkflowExecutionWithTimeout( transferTask.GetDomainID(), getWorkflowExecution(transferTask), taskGetExecutionContextTimeout, ) if err != nil { if err == context.DeadlineExceeded { return errWorkflowBusy } return err } defer func() { if isRedispatchErr(err) { release(nil) } else { release(retError) } }() mutableState, err := loadMutableState(ctx, wfContext, transferTask, t.metricsClient.Scope(metrics.TransferQueueProcessorScope), t.logger, eventID) if err != nil || mutableState == nil { return err } if !mutableState.IsWorkflowExecutionRunning() && !processTaskIfClosed { // workflow already finished, no need to process the timer return nil } historyResendInfo, err := actionFn(ctx, wfContext, mutableState) if err != nil { return err } release(nil) return postActionFn(ctx, transferTask, historyResendInfo, t.logger) } func (t *transferStandbyTaskExecutor) pushActivity( ctx context.Context, task persistence.Task, postActionInfo interface{}, logger log.Logger, ) error { if postActionInfo == nil { return nil } return t.transferTaskExecutorBase.pushActivity( ctx, task.(*persistence.ActivityTask), postActionInfo.(*pushActivityToMatchingInfo), ) } func (t *transferStandbyTaskExecutor) pushDecision( ctx context.Context, task persistence.Task, postActionInfo interface{}, logger log.Logger, ) error { if postActionInfo == nil { return nil } return t.transferTaskExecutorBase.pushDecision( ctx, task.(*persistence.DecisionTask), postActionInfo.(*pushDecisionToMatchingInfo), ) } func (t *transferStandbyTaskExecutor) fetchHistoryFromRemote( ctx context.Context, taskInfo persistence.Task, postActionInfo interface{}, _ log.Logger, ) error { if postActionInfo == nil { return nil } resendInfo := postActionInfo.(*historyResendInfo) t.metricsClient.IncCounter(metrics.HistoryRereplicationByTransferTaskScope, metrics.CadenceClientRequests) stopwatch := t.metricsClient.StartTimer(metrics.HistoryRereplicationByTransferTaskScope, metrics.CadenceClientLatency) defer stopwatch.Stop() remoteClusterName, err := t.getRemoteClusterNameFn(ctx, taskInfo) if err != nil { return err } if resendInfo.lastEventID != nil && resendInfo.lastEventVersion != nil { // note history resender doesn't take in a context parameter, there's a separate dynamicconfig for // controlling the timeout for resending history. err = t.historyResender.SendSingleWorkflowHistory( remoteClusterName, taskInfo.GetDomainID(), taskInfo.GetWorkflowID(), taskInfo.GetRunID(), resendInfo.lastEventID, resendInfo.lastEventVersion, nil, nil, ) } else { err = &types.InternalServiceError{ Message: fmt.Sprintf("incomplete historyResendInfo: %v", resendInfo), } } if err != nil { t.logger.Error("Error re-replicating history from remote.", tag.ShardID(t.shard.GetShardID()), tag.WorkflowDomainID(taskInfo.GetDomainID()), tag.WorkflowID(taskInfo.GetWorkflowID()), tag.WorkflowRunID(taskInfo.GetRunID()), tag.SourceCluster(remoteClusterName), tag.Error(err), ) } // return error so task processing logic will retry return &redispatchError{Reason: "fetchHistoryFromRemote"} } func (t *transferStandbyTaskExecutor) getCurrentTime(taskInfo persistence.Task) (time.Time, error) { // for standby task, we can always use timesource.Now, because they're supposed to be processed immediately after creation, // this is what're doing for queue v2, for queue v1, I just don't bother to change the behavior return t.shard.GetCurrentTime(t.clusterName), nil } ================================================ FILE: service/history/task/transfer_standby_task_executor_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" dc "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/ndc" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/events" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" test "github.com/uber/cadence/service/history/testing" warchiver "github.com/uber/cadence/service/worker/archiver" ) type ( transferStandbyTaskExecutorSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockShard *shard.TestContext mockDomainCache *cache.MockDomainCache mockNDCHistoryResender *ndc.MockHistoryResender mockMatchingClient *matching.MockClient mockVisibilityMgr *mocks.VisibilityManager mockExecutionMgr *mocks.ExecutionManager mockArchivalClient *warchiver.MockClient mockArchivalMetadata *archiver.MockArchivalMetadata mockArchiverProvider *provider.MockArchiverProvider logger log.Logger domainID string domainName string domainEntry *cache.DomainCacheEntry version int64 clusterName string timeSource clock.MockedTimeSource fetchHistoryDuration time.Duration discardDuration time.Duration transferStandbyTaskExecutor *transferStandbyTaskExecutor } ) func TestTransferStandbyTaskExecutorSuite(t *testing.T) { s := new(transferStandbyTaskExecutorSuite) suite.Run(t, s) } func (s *transferStandbyTaskExecutorSuite) SetupSuite() { } func (s *transferStandbyTaskExecutorSuite) SetupTest() { s.Assertions = require.New(s.T()) testConfig := config.NewForTest() s.domainID = constants.TestDomainID s.domainName = constants.TestDomainName s.domainEntry = constants.TestGlobalDomainEntry s.version = s.domainEntry.GetFailoverVersion() s.timeSource = clock.NewMockedTimeSource() s.fetchHistoryDuration = testConfig.StandbyTaskMissingEventsResendDelay() + (testConfig.StandbyTaskMissingEventsDiscardDelay()-testConfig.StandbyTaskMissingEventsResendDelay())/2 s.discardDuration = testConfig.StandbyTaskMissingEventsDiscardDelay() * 2 s.controller = gomock.NewController(s.T()) s.mockNDCHistoryResender = ndc.NewMockHistoryResender(s.controller) s.mockShard = shard.NewTestContext( s.T(), s.controller, &persistence.ShardInfo{ RangeID: 1, TransferAckLevel: 0, }, testConfig, ) s.mockShard.SetEventsCache(events.NewCache( s.mockShard.GetShardID(), s.mockShard.GetHistoryManager(), s.mockShard.GetConfig(), s.mockShard.GetLogger(), s.mockShard.GetMetricsClient(), s.mockShard.GetDomainCache(), )) s.mockShard.Resource.TimeSource = s.timeSource s.mockMatchingClient = s.mockShard.Resource.MatchingClient s.mockExecutionMgr = s.mockShard.Resource.ExecutionMgr s.mockVisibilityMgr = s.mockShard.Resource.VisibilityMgr s.mockArchivalClient = warchiver.NewMockClient(s.controller) s.mockArchivalMetadata = s.mockShard.Resource.ArchivalMetadata s.mockArchiverProvider = s.mockShard.Resource.ArchiverProvider s.mockDomainCache = s.mockShard.Resource.DomainCache s.mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(constants.TestDomainID).Return(constants.TestDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(constants.TestDomainName).Return(constants.TestGlobalDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainID(constants.TestDomainName).Return(constants.TestDomainID, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(constants.TestRateLimitedDomainID).Return(constants.TestRateLimitedDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(constants.TestRateLimitedDomainID).Return(constants.TestRateLimitedDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(constants.TestRateLimitedDomainName).Return(constants.TestRateLimitedDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainID(constants.TestRateLimitedDomainName).Return(constants.TestRateLimitedDomainID, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainByID(constants.TestActiveActiveDomainID).Return(constants.TestActiveActiveDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(constants.TestActiveActiveDomainID).Return(constants.TestActiveActiveDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(constants.TestActiveActiveDomainName).Return(constants.TestActiveActiveDomainEntry, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainID(constants.TestActiveActiveDomainName).Return(constants.TestActiveActiveDomainID, nil).AnyTimes() testActiveClusterInfo := &types.ActiveClusterInfo{ ActiveClusterName: constants.TestGlobalDomainEntry.GetReplicationConfig().ActiveClusterName, FailoverVersion: constants.TestGlobalDomainEntry.GetFailoverVersion(), } s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(testActiveClusterInfo, nil).AnyTimes() s.mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestActiveActiveDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: cluster.TestAlternativeClusterName}, nil).AnyTimes() s.logger = s.mockShard.GetLogger() s.clusterName = cluster.TestAlternativeClusterName s.transferStandbyTaskExecutor = NewTransferStandbyTaskExecutor( s.mockShard, s.mockArchivalClient, execution.NewCache(s.mockShard), s.mockNDCHistoryResender, s.logger, s.clusterName, testConfig, ).(*transferStandbyTaskExecutor) s.transferStandbyTaskExecutor.getRemoteClusterNameFn = func(ctx context.Context, taskInfo persistence.Task) (string, error) { return s.clusterName, nil } } func (s *transferStandbyTaskExecutorSuite) TearDownTest() { s.controller.Finish() s.mockShard.Finish(s.T()) s.transferStandbyTaskExecutor.Stop() } func (s *transferStandbyTaskExecutorSuite) TestProcessActivityTask_Pending() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event, _ := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity-1", "some random activity type", mutableState.GetExecutionInfo().TaskList, []byte{}, 1, 1, 1, 1, ) now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: "unused", ScheduleID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.True(isRedispatchErr(err)) } func (s *transferStandbyTaskExecutorSuite) TestProcessActivityTask_Pending_ActiveActiveDomain() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, constants.TestActiveActiveDomainID) s.NoError(err) event, _ := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity-1", "some random activity type", mutableState.GetExecutionInfo().TaskList, []byte{}, 1, 1, 1, 1, ) now := time.Now() s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.fetchHistoryDuration)) transferTask := s.newTransferTaskFromInfo(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestActiveActiveDomainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.True(isRedispatchErr(err)) } func (s *transferStandbyTaskExecutorSuite) TestProcessActivityTask_Pending_PushToMatching() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event, ai := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity-1", "some random activity type", mutableState.GetExecutionInfo().TaskList, []byte{}, 1, 1, 1, 1, ) now := time.Now() s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.fetchHistoryDuration)) transferTask := s.newTransferTaskFromInfo(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockMatchingClient.EXPECT().AddActivityTask(gomock.Any(), createAddActivityTaskRequest(transferTask, ai, mutableState.GetExecutionInfo().PartitionConfig)).Return(&types.AddActivityTaskResponse{}, nil).Times(1) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessActivityTask_Pending_TaskListKindEphemeral() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event, ai, _, _, _, _ := mutableState.AddActivityTaskScheduledEvent(nil, decisionCompletionID, &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: "activity-1", ActivityType: &types.ActivityType{Name: "some random activity type"}, TaskList: &types.TaskList{Name: mutableState.GetExecutionInfo().TaskList, Kind: types.TaskListKindEphemeral.Ptr()}, Input: []byte{}, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(1), ScheduleToStartTimeoutSeconds: common.Int32Ptr(1), StartToCloseTimeoutSeconds: common.Int32Ptr(1), HeartbeatTimeoutSeconds: common.Int32Ptr(1), }, false, ) now := time.Now() s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.fetchHistoryDuration)) transferTask := s.newTransferTaskFromInfo(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockMatchingClient.EXPECT().AddActivityTask(gomock.Any(), createAddActivityTaskRequest(transferTask, ai, mutableState.GetExecutionInfo().PartitionConfig)).Return(&types.AddActivityTaskResponse{}, nil).Times(1) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessActivityTask_Success() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event, _ := test.AddActivityTaskScheduledEvent( mutableState, decisionCompletionID, "activity-1", "some random activity type", mutableState.GetExecutionInfo().TaskList, []byte{}, 1, 1, 1, 1, ) event = test.AddActivityTaskStartedEvent(mutableState, event.ID, "") mutableState.FlushBufferedEvents() now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.ActivityTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessDecisionTask_Pending() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.True(isRedispatchErr(err)) } func (s *transferStandbyTaskExecutorSuite) TestProcessDecisionTask_Pending_ActiveActiveDomain() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, constants.TestActiveActiveDomainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) now := time.Now() s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.fetchHistoryDuration)) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: constants.TestActiveActiveDomainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.True(isRedispatchErr(err)) } func (s *transferStandbyTaskExecutorSuite) TestProcessDecisionTask_Pending_PushToMatching() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) now := time.Now() s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.fetchHistoryDuration)) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), createAddDecisionTaskRequest(transferTask, mutableState)).Return(&types.AddDecisionTaskResponse{}, nil).Times(1) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessDecisionTask_Pending_TaskListKindEphemeral() { workflowExecution, mutableState, err := test.StartWorkflowWithTaskList(&types.TaskList{Name: "ephemy", Kind: types.TaskListKindEphemeral.Ptr()}, s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) now := time.Now() s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.fetchHistoryDuration)) transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, di.ScheduleID, di.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockMatchingClient.EXPECT().AddDecisionTask(gomock.Any(), createAddDecisionTaskRequest(transferTask, mutableState)).Return(&types.AddDecisionTaskResponse{}, nil).Times(1) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessDecisionTask_Success_FirstDecision() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) event := test.AddDecisionTaskStartedEvent(mutableState, di.ScheduleID, mutableState.GetExecutionInfo().TaskList, uuid.New()) di.StartedID = event.ID persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessDecisionTask_Success_NonFirstDecision() { workflowExecution, mutableState, _, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) di := test.AddDecisionTaskScheduledEvent(mutableState) now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.DecisionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TaskList: mutableState.GetExecutionInfo().TaskList, ScheduleID: di.ScheduleID, }) event := test.AddDecisionTaskStartedEvent(mutableState, di.ScheduleID, mutableState.GetExecutionInfo().TaskList, uuid.New()) di.StartedID = event.ID persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessCloseExecution() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event := test.AddCompleteWorkflowEvent(mutableState, decisionCompletionID, nil) now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.CloseExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockVisibilityMgr.On("RecordWorkflowExecutionClosed", mock.Anything, mock.Anything).Return(nil).Once() s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewDisabledArchvialConfig()) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessRecordWorkflowClosedTask() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) event := test.AddCompleteWorkflowEvent(mutableState, decisionCompletionID, nil) now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.RecordWorkflowClosedTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockVisibilityMgr.On("RecordWorkflowExecutionClosed", mock.Anything, mock.Anything).Return(nil).Once() s.mockArchivalMetadata.On("GetVisibilityConfig").Return(archiver.NewDisabledArchvialConfig()) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessCancelExecution_Pending() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) targetExecution := types.WorkflowExecution{ WorkflowID: "some random target workflow ID", RunID: uuid.New(), } event, _ := test.AddRequestCancelInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, targetExecution.GetWorkflowID(), targetExecution.GetRunID(), ) nextEventID := event.ID now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.CancelExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TargetDomainID: constants.TestDomainID, TargetWorkflowID: targetExecution.GetWorkflowID(), TargetRunID: targetExecution.GetRunID(), InitiatedID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.fetchHistoryDuration)) s.mockNDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.clusterName, transferTask.GetDomainID(), transferTask.GetWorkflowID(), transferTask.GetRunID(), common.Int64Ptr(nextEventID), common.Int64Ptr(s.version), nil, nil, ).Return(nil).Times(1) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.discardDuration)) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Equal(ErrTaskDiscarded, err) } func (s *transferStandbyTaskExecutorSuite) TestProcessCancelExecution_Success() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) targetExecution := types.WorkflowExecution{ WorkflowID: "some random target workflow ID", RunID: uuid.New(), } event, _ := test.AddRequestCancelInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, targetExecution.GetWorkflowID(), targetExecution.GetRunID(), ) event = test.AddCancelRequestedEvent(mutableState, event.ID, constants.TestDomainID, targetExecution.GetWorkflowID(), targetExecution.GetRunID()) mutableState.FlushBufferedEvents() now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.CancelExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TargetDomainID: constants.TestDomainID, TargetWorkflowID: targetExecution.GetWorkflowID(), TargetRunID: targetExecution.GetRunID(), InitiatedID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessSignalExecution_Pending() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) targetExecution := types.WorkflowExecution{ WorkflowID: "some random target workflow ID", RunID: uuid.New(), } event, _ := test.AddRequestSignalInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, targetExecution.GetWorkflowID(), targetExecution.GetRunID(), "some random signal name", nil, nil, ) nextEventID := event.ID now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.SignalExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TargetDomainID: constants.TestDomainID, TargetWorkflowID: targetExecution.GetWorkflowID(), TargetRunID: targetExecution.GetRunID(), InitiatedID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.fetchHistoryDuration)) s.mockNDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.clusterName, transferTask.GetDomainID(), transferTask.GetWorkflowID(), transferTask.GetRunID(), common.Int64Ptr(nextEventID), common.Int64Ptr(s.version), nil, nil, ).Return(nil).Times(1) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.discardDuration)) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Equal(ErrTaskDiscarded, err) } func (s *transferStandbyTaskExecutorSuite) TestProcessSignalExecution_Success() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) targetExecution := types.WorkflowExecution{ WorkflowID: "some random target workflow ID", RunID: uuid.New(), } event, _ := test.AddRequestSignalInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, targetExecution.GetWorkflowID(), targetExecution.GetRunID(), "some random signal name", nil, nil, ) now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.SignalExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TargetDomainID: constants.TestDomainID, TargetWorkflowID: targetExecution.GetWorkflowID(), TargetRunID: targetExecution.GetRunID(), InitiatedID: event.ID, }) event = test.AddSignaledEvent(mutableState, event.ID, constants.TestDomainName, targetExecution.GetWorkflowID(), targetExecution.GetRunID(), nil) mutableState.FlushBufferedEvents() persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessStartChildExecution_Pending() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) childWorkflowID := "some random child workflow ID" event, _ := test.AddStartChildWorkflowExecutionInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, childWorkflowID, "some random child workflow type", "some random child task list", nil, 1, 1, nil, ) nextEventID := event.ID now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.StartChildExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TargetDomainID: constants.TestDomainID, TargetWorkflowID: childWorkflowID, InitiatedID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.fetchHistoryDuration)) s.mockNDCHistoryResender.EXPECT().SendSingleWorkflowHistory( s.clusterName, transferTask.GetDomainID(), transferTask.GetWorkflowID(), transferTask.GetRunID(), common.Int64Ptr(nextEventID), common.Int64Ptr(s.version), nil, nil, ).Return(nil).Times(1) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.True(isRedispatchErr(err)) s.mockShard.SetCurrentTime(s.clusterName, now.Add(s.discardDuration)) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Equal(ErrTaskDiscarded, err) } func (s *transferStandbyTaskExecutorSuite) TestProcessStartChildExecution_Success() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) childWorkflowID := "some random child workflow ID" childWorkflowType := "some random child workflow type" event, childInfo := test.AddStartChildWorkflowExecutionInitiatedEvent( mutableState, decisionCompletionID, uuid.New(), constants.TestDomainName, childWorkflowID, childWorkflowType, "some random child task list", nil, 1, 1, nil, ) event = test.AddChildWorkflowExecutionStartedEvent(mutableState, event.ID, constants.TestDomainName, childWorkflowID, uuid.New(), childWorkflowType) mutableState.FlushBufferedEvents() childInfo.StartedID = event.ID now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.StartChildExecutionTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, TargetDomainID: constants.TestDomainID, TargetWorkflowID: childWorkflowID, InitiatedID: event.ID, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, event.ID, event.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessRecordWorkflowStartedTask() { workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) executionInfo := mutableState.GetExecutionInfo() executionInfo.CronSchedule = "@every 5s" executionInfo.ActiveClusterSelectionPolicy = &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, } startEvent, err := mutableState.GetStartEvent(context.Background()) s.NoError(err) startEvent.WorkflowExecutionStartedEventAttributes.FirstDecisionTaskBackoffSeconds = common.Int32Ptr(5) now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.RecordWorkflowStartedTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startEvent.ID, startEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) if s.mockShard.GetConfig().EnableRecordWorkflowExecutionUninitialized(s.domainName) { s.mockVisibilityMgr.On( "RecordWorkflowExecutionUninitialized", mock.Anything, createRecordWorkflowExecutionUninitializedRequest(transferTask, mutableState, s.mockShard.GetTimeSource().Now(), 1234), ).Once().Return(nil) } s.mockVisibilityMgr.On( "RecordWorkflowExecutionStarted", mock.Anything, createRecordWorkflowExecutionStartedRequest( s.T(), s.domainName, startEvent, transferTask, mutableState, 2, s.mockShard.GetTimeSource().Now(), false), ).Return(nil).Once() s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessRecordWorkflowStartedTaskWithContextHeader() { // switch on context header in viz s.mockShard.GetConfig().EnableContextHeaderInVisibility = func(domain string) bool { return true } s.mockShard.GetConfig().ValidSearchAttributes = func(opts ...dc.FilterOption) map[string]interface{} { return map[string]interface{}{ "Header_context_key": struct{}{}, } } workflowExecution, mutableState, err := test.StartWorkflow(s.T(), s.mockShard, s.domainID) s.NoError(err) executionInfo := mutableState.GetExecutionInfo() executionInfo.CronSchedule = "@every 5s" startEvent, err := mutableState.GetStartEvent(context.Background()) s.NoError(err) startEvent.WorkflowExecutionStartedEventAttributes.FirstDecisionTaskBackoffSeconds = common.Int32Ptr(5) now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.RecordWorkflowStartedTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, startEvent.ID, startEvent.Version) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) if s.mockShard.GetConfig().EnableRecordWorkflowExecutionUninitialized(s.domainName) { s.mockVisibilityMgr.On( "RecordWorkflowExecutionUninitialized", mock.Anything, createRecordWorkflowExecutionUninitializedRequest(transferTask, mutableState, s.mockShard.GetTimeSource().Now(), 1234), ).Once().Return(nil) } s.mockVisibilityMgr.On( "RecordWorkflowExecutionStarted", mock.Anything, createRecordWorkflowExecutionStartedRequest( s.T(), s.domainName, startEvent, transferTask, mutableState, 2, s.mockShard.GetTimeSource().Now(), true), ).Return(nil).Once() s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessUpsertWorkflowSearchAttributesTask() { workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) executionInfo := mutableState.GetExecutionInfo() executionInfo.ActiveClusterSelectionPolicy = &types.ActiveClusterSelectionPolicy{ ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, } now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.UpsertWorkflowSearchAttributesTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) startEvent, err := mutableState.GetStartEvent(context.Background()) s.NoError(err) s.mockVisibilityMgr.On( "UpsertWorkflowExecution", mock.Anything, createUpsertWorkflowSearchAttributesRequest( s.T(), s.domainName, startEvent, transferTask, mutableState, 2, s.mockShard.GetTimeSource().Now(), false), ).Return(nil).Once() s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) TestProcessUpsertWorkflowSearchAttributesTaskWithContextHeader() { // switch on context header in viz s.mockShard.GetConfig().EnableContextHeaderInVisibility = func(domain string) bool { return true } s.mockShard.GetConfig().ValidSearchAttributes = func(opts ...dc.FilterOption) map[string]interface{} { return map[string]interface{}{ "Header_context_key": struct{}{}, } } workflowExecution, mutableState, decisionCompletionID, err := test.SetupWorkflowWithCompletedDecision(s.T(), s.mockShard, s.domainID) s.NoError(err) now := time.Now() transferTask := s.newTransferTaskFromInfo(&persistence.UpsertWorkflowSearchAttributesTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: s.domainID, WorkflowID: workflowExecution.GetWorkflowID(), RunID: workflowExecution.GetRunID(), }, TaskData: persistence.TaskData{ Version: s.version, VisibilityTimestamp: now, TaskID: int64(59), }, }) persistenceMutableState, err := test.CreatePersistenceMutableState(s.T(), mutableState, decisionCompletionID, mutableState.GetCurrentVersion()) s.NoError(err) s.mockExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&persistence.GetWorkflowExecutionResponse{State: persistenceMutableState}, nil) startEvent, err := mutableState.GetStartEvent(context.Background()) s.NoError(err) s.mockVisibilityMgr.On( "UpsertWorkflowExecution", mock.Anything, createUpsertWorkflowSearchAttributesRequest( s.T(), s.domainName, startEvent, transferTask, mutableState, 2, s.mockShard.GetTimeSource().Now(), true), ).Return(nil).Once() s.mockShard.SetCurrentTime(s.clusterName, now) _, err = s.transferStandbyTaskExecutor.Execute(transferTask) s.Nil(err) } func (s *transferStandbyTaskExecutorSuite) newTransferTaskFromInfo( task persistence.Task, ) Task { return NewHistoryTask(s.mockShard, task, QueueTypeStandbyTransfer, s.logger, nil, nil, nil, nil, nil) } ================================================ FILE: service/history/task/transfer_task_executor_base.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "context" "encoding/json" "fmt" "strings" "time" "go.uber.org/multierr" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/visibility" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" "github.com/uber/cadence/service/worker/archiver" ) const ( taskDefaultTimeout = 3 * time.Second taskGetExecutionContextTimeout = 500 * time.Millisecond taskRPCCallTimeout = 2 * time.Second secondsInDay = int32(24 * time.Hour / time.Second) defaultDomainName = "defaultDomainName" ) type ( transferTaskExecutorBase struct { shard shard.Context archiverClient archiver.Client executionCache execution.Cache logger log.Logger metricsClient metrics.Client matchingClient matching.Client visibilityMgr persistence.VisibilityManager config *config.Config throttleRetry *backoff.ThrottleRetry } ) func newTransferTaskExecutorBase( shard shard.Context, archiverClient archiver.Client, executionCache execution.Cache, logger log.Logger, config *config.Config, ) *transferTaskExecutorBase { return &transferTaskExecutorBase{ shard: shard, archiverClient: archiverClient, executionCache: executionCache, logger: logger, metricsClient: shard.GetMetricsClient(), matchingClient: shard.GetService().GetMatchingClient(), visibilityMgr: shard.GetService().GetVisibilityManager(), config: config, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(taskRetryPolicy), backoff.WithRetryableError(common.IsServiceTransientError), ), } } func (t *transferTaskExecutorBase) pushActivity( ctx context.Context, task *persistence.ActivityTask, pushActivityInfo *pushActivityToMatchingInfo, ) error { ctx, cancel := context.WithTimeout(ctx, taskRPCCallTimeout) defer cancel() if task.GetTaskType() != persistence.TransferTaskTypeActivityTask { t.logger.Fatal("Cannot process non activity task", tag.TaskType(task.GetTaskType())) } shouldPush, err := shouldPushToMatching(ctx, t.shard, task) if err != nil { return err } if !shouldPush { // The domain is an active-active domain and the task is pending // For the first few minutes, we will retry the task and then discard it if it is still pending. if t.shard.GetTimeSource().Now().Before(task.GetVisibilityTimestamp().Add(t.config.StandbyTaskMissingEventsDiscardDelay())) { err = standbyTaskPostActionNoOp(ctx, task, pushActivityInfo, t.logger) return err } return standbyTaskPostActionTaskDiscarded(ctx, task, pushActivityInfo, t.logger) } activityScheduleToStartTimeout := min(pushActivityInfo.activityScheduleToStartTimeout, constants.MaxTaskTimeout) taskList := &pushActivityInfo.tasklist partitionConfig := pushActivityInfo.partitionConfig _, err = t.matchingClient.AddActivityTask(ctx, &types.AddActivityTaskRequest{ DomainUUID: task.TargetDomainID, SourceDomainUUID: task.DomainID, Execution: &types.WorkflowExecution{ WorkflowID: task.WorkflowID, RunID: task.RunID, }, TaskList: taskList, ScheduleID: task.ScheduleID, ScheduleToStartTimeoutSeconds: common.Int32Ptr(activityScheduleToStartTimeout), PartitionConfig: partitionConfig, }) return err } func (t *transferTaskExecutorBase) pushDecision( ctx context.Context, task *persistence.DecisionTask, pushDecisionInfo *pushDecisionToMatchingInfo, ) error { ctx, cancel := context.WithTimeout(ctx, taskRPCCallTimeout) defer cancel() if task.GetTaskType() != persistence.TransferTaskTypeDecisionTask { t.logger.Fatal("Cannot process non decision task", tag.TaskType(task.GetTaskType())) } shouldPush, err := shouldPushToMatching(ctx, t.shard, task) if err != nil { return err } if !shouldPush { // The domain is an active-active domain and the task is pending // For the first few minutes, we will retry the task and then discard it if it is still pending. if t.shard.GetTimeSource().Now().Before(task.GetVisibilityTimestamp().Add(t.config.StandbyTaskMissingEventsDiscardDelay())) { return standbyTaskPostActionNoOp(ctx, task, pushDecisionInfo, t.logger) } return standbyTaskPostActionTaskDiscarded(ctx, task, pushDecisionInfo, t.logger) } decisionScheduleToStartTimeout := min(pushDecisionInfo.decisionScheduleToStartTimeout, constants.MaxTaskTimeout) tasklist := &pushDecisionInfo.tasklist partitionConfig := pushDecisionInfo.partitionConfig _, err = t.matchingClient.AddDecisionTask(ctx, &types.AddDecisionTaskRequest{ DomainUUID: task.DomainID, Execution: &types.WorkflowExecution{ WorkflowID: task.WorkflowID, RunID: task.RunID, }, TaskList: tasklist, ScheduleID: task.ScheduleID, ScheduleToStartTimeoutSeconds: common.Int32Ptr(decisionScheduleToStartTimeout), PartitionConfig: partitionConfig, }) return err } func (t *transferTaskExecutorBase) recordWorkflowStarted( ctx context.Context, domainID string, workflowID string, runID string, workflowTypeName string, startTimeUnixNano int64, executionTimeUnixNano int64, workflowTimeout int32, taskID int64, taskList string, isCron bool, numClusters int16, visibilityMemo *types.Memo, updateTimeUnixNano int64, immutableSearchAttributes map[string][]byte, headers map[string][]byte, clusterAttribute *types.ClusterAttribute, cronSchedule string, executionStatus types.WorkflowExecutionStatus, scheduledExecutionTimeUnixNano int64, ) error { domain := defaultDomainName domainEntry, err := t.shard.GetDomainCache().GetDomainByID(domainID) if err != nil { if _, ok := err.(*types.EntityNotExistsError); !ok { return err } } else { domain = domainEntry.GetInfo().Name // if sampled for longer retention is enabled, only record those sampled events if domainEntry.IsSampledForLongerRetentionEnabled(workflowID) && !domainEntry.IsSampledForLongerRetention(workflowID) { return nil } } // headers are appended into search attributes if enabled searchAttributes := copySearchAttributes(immutableSearchAttributes) if t.config.EnableContextHeaderInVisibility(domainEntry.GetInfo().Name) { // fail open, if error occurs, just log it; successfully appended headers will be stored if searchAttributes, err = appendContextHeaderToSearchAttributes(searchAttributes, headers, t.config.ValidSearchAttributes(), t.config.SearchAttributesHiddenValueKeys()); err != nil { t.logger.Error("failed to add headers to search attributes", tag.Error(err)) } } request := &persistence.RecordWorkflowExecutionStartedRequest{ DomainUUID: domainID, Domain: domain, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, WorkflowTypeName: workflowTypeName, StartTimestamp: startTimeUnixNano, ExecutionTimestamp: executionTimeUnixNano, WorkflowTimeout: int64(workflowTimeout), TaskID: taskID, Memo: visibilityMemo, TaskList: taskList, IsCron: isCron, CronSchedule: cronSchedule, NumClusters: numClusters, ClusterAttributeScope: clusterAttribute.GetScope(), ClusterAttributeName: clusterAttribute.GetName(), UpdateTimestamp: updateTimeUnixNano, SearchAttributes: searchAttributes, ShardID: int16(t.shard.GetShardID()), ExecutionStatus: executionStatus, ScheduledExecutionTimestamp: scheduledExecutionTimeUnixNano, } if t.config.EnableRecordWorkflowExecutionUninitialized(domain) { uninitializedRequest := &persistence.RecordWorkflowExecutionUninitializedRequest{ DomainUUID: domainID, Domain: domain, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, WorkflowTypeName: workflowTypeName, UpdateTimestamp: updateTimeUnixNano, ShardID: int64(t.shard.GetShardID()), } if err := t.visibilityMgr.RecordWorkflowExecutionUninitialized(ctx, uninitializedRequest); err != nil { t.logger.Error("Failed to record uninitialized workflow execution", tag.Error(err)) } } return t.visibilityMgr.RecordWorkflowExecutionStarted(ctx, request) } func (t *transferTaskExecutorBase) upsertWorkflowExecution( ctx context.Context, domainID string, workflowID string, runID string, workflowTypeName string, startTimeUnixNano int64, executionTimeUnixNano int64, workflowTimeout int32, taskID int64, taskList string, visibilityMemo *types.Memo, isCron bool, numClusters int16, updateTimeUnixNano int64, immutableSearchAttributes map[string][]byte, headers map[string][]byte, clusterAttribute *types.ClusterAttribute, cronSchedule string, executionStatus types.WorkflowExecutionStatus, scheduledExecutionTimeUnixNano int64, ) error { domain, err := t.shard.GetDomainCache().GetDomainName(domainID) if err != nil { if _, ok := err.(*types.EntityNotExistsError); !ok { return err } domain = defaultDomainName } // headers are appended into search attributes if enabled searchAttributes := copySearchAttributes(immutableSearchAttributes) if t.config.EnableContextHeaderInVisibility(domain) { // fail open, if error occurs, just log it; successfully appended headers will be stored if searchAttributes, err = appendContextHeaderToSearchAttributes(searchAttributes, headers, t.config.ValidSearchAttributes(), t.config.SearchAttributesHiddenValueKeys()); err != nil { t.logger.Error("failed to add headers to search attributes", tag.Error(err)) } } request := &persistence.UpsertWorkflowExecutionRequest{ DomainUUID: domainID, Domain: domain, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, WorkflowTypeName: workflowTypeName, StartTimestamp: startTimeUnixNano, ExecutionTimestamp: executionTimeUnixNano, WorkflowTimeout: int64(workflowTimeout), TaskID: taskID, Memo: visibilityMemo, TaskList: taskList, IsCron: isCron, NumClusters: numClusters, ClusterAttributeScope: clusterAttribute.GetScope(), ClusterAttributeName: clusterAttribute.GetName(), SearchAttributes: searchAttributes, UpdateTimestamp: updateTimeUnixNano, ShardID: int64(t.shard.GetShardID()), ExecutionStatus: executionStatus, CronSchedule: cronSchedule, ScheduledExecutionTimestamp: scheduledExecutionTimeUnixNano, } return t.visibilityMgr.UpsertWorkflowExecution(ctx, request) } func (t *transferTaskExecutorBase) recordWorkflowClosed( ctx context.Context, domainID string, workflowID string, runID string, workflowTypeName string, startTimeUnixNano int64, executionTimeUnixNano int64, endTimeUnixNano int64, closeStatus types.WorkflowExecutionCloseStatus, historyLength int64, taskID int64, visibilityMemo *types.Memo, taskList string, isCron bool, cronSchedule string, numClusters int16, updateTimeUnixNano int64, immutableSearchAttributes map[string][]byte, headers map[string][]byte, clusterAttribute *types.ClusterAttribute, executionStatus types.WorkflowExecutionStatus, scheduledExecutionTimeUnixNano int64, ) error { // Record closing in visibility store retentionSeconds := int64(0) domain := defaultDomainName recordWorkflowClose := true archiveVisibility := false domainEntry, err := t.shard.GetDomainCache().GetDomainByID(domainID) if err != nil && !isWorkflowNotExistError(err) { return err } if err == nil { // retention in domain config is in days, convert to seconds retentionSeconds = int64(domainEntry.GetRetentionDays(workflowID)) * int64(secondsInDay) domain = domainEntry.GetInfo().Name // if sampled for longer retention is enabled, only record those sampled events if domainEntry.IsSampledForLongerRetentionEnabled(workflowID) && !domainEntry.IsSampledForLongerRetention(workflowID) { recordWorkflowClose = false } clusterConfiguredForVisibilityArchival := t.shard.GetService().GetArchivalMetadata().GetVisibilityConfig().ClusterConfiguredForArchival() domainConfiguredForVisibilityArchival := domainEntry.GetConfig().VisibilityArchivalStatus == types.ArchivalStatusEnabled archiveVisibility = clusterConfiguredForVisibilityArchival && domainConfiguredForVisibilityArchival } // headers are appended into search attributes if enabled searchAttributes := copySearchAttributes(immutableSearchAttributes) if t.config.EnableContextHeaderInVisibility(domainEntry.GetInfo().Name) { // fail open, if error occurs, just log it; successfully appended headers will be stored if searchAttributes, err = appendContextHeaderToSearchAttributes(searchAttributes, headers, t.config.ValidSearchAttributes(), t.config.SearchAttributesHiddenValueKeys()); err != nil { t.logger.Error("failed to add headers to search attributes", tag.Error(err)) } } if recordWorkflowClose { if err := t.visibilityMgr.RecordWorkflowExecutionClosed(ctx, &persistence.RecordWorkflowExecutionClosedRequest{ DomainUUID: domainID, Domain: domain, Execution: types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, WorkflowTypeName: workflowTypeName, StartTimestamp: startTimeUnixNano, ExecutionTimestamp: executionTimeUnixNano, CloseTimestamp: endTimeUnixNano, Status: closeStatus, HistoryLength: historyLength, RetentionSeconds: retentionSeconds, TaskID: taskID, Memo: visibilityMemo, TaskList: taskList, SearchAttributes: searchAttributes, IsCron: isCron, CronSchedule: cronSchedule, ClusterAttributeScope: clusterAttribute.GetScope(), ClusterAttributeName: clusterAttribute.GetName(), UpdateTimestamp: updateTimeUnixNano, NumClusters: numClusters, ShardID: int16(t.shard.GetShardID()), ExecutionStatus: executionStatus, ScheduledExecutionTimestamp: scheduledExecutionTimeUnixNano, }); err != nil { return err } } if archiveVisibility { archiveCtx, cancel := context.WithTimeout(ctx, t.config.TransferProcessorVisibilityArchivalTimeLimit()) defer cancel() _, err := t.archiverClient.Archive(archiveCtx, &archiver.ClientRequest{ ArchiveRequest: &archiver.ArchiveRequest{ DomainID: domainID, DomainName: domain, WorkflowID: workflowID, RunID: runID, WorkflowTypeName: workflowTypeName, StartTimestamp: startTimeUnixNano, ExecutionTimestamp: executionTimeUnixNano, CloseTimestamp: endTimeUnixNano, CloseStatus: closeStatus, HistoryLength: historyLength, Memo: visibilityMemo, SearchAttributes: searchAttributes, VisibilityURI: domainEntry.GetConfig().VisibilityArchivalURI, URI: domainEntry.GetConfig().HistoryArchivalURI, Targets: []archiver.ArchivalTarget{archiver.ArchiveTargetVisibility}, }, CallerService: service.History, AttemptArchiveInline: true, // archive visibility inline by default }) return err } return nil } // Argument startEvent is to save additional call of msBuilder.GetStartEvent func getWorkflowExecutionTimestamp( msBuilder execution.MutableState, startEvent *types.HistoryEvent, ) time.Time { // Use value 0 to represent workflows that don't need backoff. Since ES doesn't support // comparison between two field, we need a value to differentiate them from cron workflows // or later runs of a workflow that needs retry. executionTimestamp := time.Unix(0, 0) if startEvent == nil { return executionTimestamp } if backoffSeconds := startEvent.WorkflowExecutionStartedEventAttributes.GetFirstDecisionTaskBackoffSeconds(); backoffSeconds != 0 { startTimestamp := time.Unix(0, startEvent.GetTimestamp()) executionTimestamp = startTimestamp.Add(time.Duration(backoffSeconds) * time.Second) } return executionTimestamp } func getWorkflowMemo( memo map[string][]byte, ) *types.Memo { if memo == nil { return nil } return &types.Memo{Fields: memo} } // context headers are appended to search attributes if in allow list; return errors when all context key is processed func appendContextHeaderToSearchAttributes(attr, context map[string][]byte, allowedKeys map[string]interface{}, hiddenValueKeys map[string]interface{}) (map[string][]byte, error) { // sanity check if attr == nil { attr = make(map[string][]byte) } var errGroup error for k, v := range context { sanitizedKey, err := visibility.SanitizeSearchAttributeKey(k) if err != nil { // This could never happen multierr.Append(errGroup, fmt.Errorf("fail to sanitize context key %s: %w", k, err)) continue } key := fmt.Sprintf(definition.HeaderFormat, sanitizedKey) if _, ok := attr[key]; ok { // skip if key already exists continue } if _, allowed := allowedKeys[key]; !allowed { // skip if not allowed continue } // context header are raw string bytes, need to be json encoded to be stored in search attributes // ignore error as it can't happen to err on json encoding string // context header can contain sensitive information, so we need to hide the value if it is in the hiddenValueKeys var val string if shouldRedactContextHeader(key, hiddenValueKeys) { val = "***redacted***" // Hide the actualvalue } else { val = string(v) // Convert []byte to string safely } data, _ := json.Marshal(val) attr[key] = data } return attr, errGroup } func getWorkflowHeaders(startEvent *types.HistoryEvent) map[string][]byte { attr := startEvent.GetWorkflowExecutionStartedEventAttributes() if attr == nil || attr.Header == nil { return nil } headers := make(map[string][]byte, len(attr.Header.Fields)) for k, v := range attr.Header.Fields { val := make([]byte, len(v)) copy(val, v) headers[k] = val } return headers } func copySearchAttributes( input map[string][]byte, ) map[string][]byte { if input == nil { return nil } result := make(map[string][]byte) for k, v := range input { val := make([]byte, len(v)) copy(val, v) result[k] = val } return result } func isWorkflowNotExistError(err error) bool { _, ok := err.(*types.EntityNotExistsError) return ok } func shouldRedactContextHeader(key string, hiddenValueKeys map[string]interface{}) bool { if hiddenValueKeys == nil { return false } if val, exists := hiddenValueKeys[key]; exists { switch v := val.(type) { case bool: return v case string: return strings.ToLower(v) == "true" case int: return v > 0 // 1 means true, 0 means false } } return false } // determineExecutionStatus determines whether a workflow execution is PENDING or STARTED // based on whether it has a first decision task backoff and if the decision task has been scheduled. // // A workflow is PENDING if: // - It has a firstDecisionTaskBackoffSeconds > 0, AND // - No decision task has been scheduled/processed yet // // Otherwise, it's STARTED. // // Returns: // - executionStatus: either WorkflowExecutionStatusPending or WorkflowExecutionStatusStarted // - scheduledExecutionTimestamp: startTime + firstDecisionTaskBackoffSeconds (in nanoseconds) func determineExecutionStatus( startEvent *types.HistoryEvent, mutableState execution.MutableState, ) (executionStatus types.WorkflowExecutionStatus, scheduledExecutionTimestamp int64) { executionStatus = types.WorkflowExecutionStatusStarted backoffSeconds := int32(0) if startEvent.WorkflowExecutionStartedEventAttributes != nil { backoffSeconds = startEvent.WorkflowExecutionStartedEventAttributes.GetFirstDecisionTaskBackoffSeconds() } if backoffSeconds > 0 { hasPending := mutableState.HasPendingDecision() hasInFlight := mutableState.HasInFlightDecision() hasProcessed := mutableState.HasProcessedOrPendingDecision() // Check if the first decision task has been scheduled yet // If there's no decision info, the workflow is still pending if !hasPending && !hasInFlight && !hasProcessed { executionStatus = types.WorkflowExecutionStatusPending } } // startTime + firstDecisionTaskBackoffSeconds scheduledExecutionTimestamp = startEvent.GetTimestamp() + int64(backoffSeconds)*int64(time.Second) return executionStatus, scheduledExecutionTimestamp } func determineExecutionStatusForVisibility( startEvent *types.HistoryEvent, mutableState execution.MutableState, isAdvancedVisibilityEnabled bool, ) (executionStatus types.WorkflowExecutionStatus, scheduledExecutionTimestamp int64) { executionStatus, scheduledExecutionTimestamp = determineExecutionStatus(startEvent, mutableState) // For basic DB visibility, always use STARTED status // Basic visibility cannot be updated mid-execution, so showing PENDING would be // misleading as the workflow would appear to be waiting even when it's running. // With advanced visibility, we can update PENDING -> STARTED via upsert. if !isAdvancedVisibilityEnabled && executionStatus == types.WorkflowExecutionStatusPending { executionStatus = types.WorkflowExecutionStatusStarted } return executionStatus, scheduledExecutionTimestamp } ================================================ FILE: service/history/task/transfer_task_executor_base_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package task import ( "testing" ) func TestShouldRedactContextHeader(t *testing.T) { cases := []struct { key string hiddenValueKeys map[string]interface{} expected bool }{ { key: "key1", hiddenValueKeys: map[string]interface{}{ "key1": true, }, expected: true, }, { key: "key2", hiddenValueKeys: map[string]interface{}{ "key1": true, }, expected: false, }, { key: "key3", hiddenValueKeys: map[string]interface{}{}, expected: false, }, { key: "key4", hiddenValueKeys: nil, expected: false, }, { key: "key5", hiddenValueKeys: map[string]interface{}{ "key5": "true", }, expected: true, }, } for _, c := range cases { result := shouldRedactContextHeader(c.key, c.hiddenValueKeys) if result != c.expected { t.Errorf("shouldRedactContextHeader(%s, %v) = %t; expected %t", c.key, c.hiddenValueKeys, result, c.expected) } } } ================================================ FILE: service/history/templates/ratelimited.tmpl ================================================ import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history" "github.com/uber/cadence/common/log" ) {{ $ratelimitTypeMap := dict "StartWorkflowExecution" ( dict "ratelimit" "ratelimitTypeUserPerID" "workflowID" "StartRequest.GetWorkflowID()" ) }} {{ $ratelimitTypeMap := set $ratelimitTypeMap "SignalWithStartWorkflowExecution" ( dict "ratelimit" "ratelimitTypeUserPerID" "workflowID" "SignalWithStartRequest.GetWorkflowID()" ) }} {{ $ratelimitTypeMap := set $ratelimitTypeMap "SignalWorkflowExecution" ( dict "ratelimit" "ratelimitTypeUserPerID" "workflowID" "SignalRequest.GetWorkflowExecution().GetWorkflowID()" ) }} {{ $ratelimitTypeMap := set $ratelimitTypeMap "DescribeWorkflowExecution" ( dict "ratelimit" "ratelimitTypeUserPerID" "workflowID" "Request.GetExecution().GetWorkflowID()" ) }} {{ $interfaceName := .Interface.Name }} {{ $handlerName := (index .Vars "handler") }} {{ $decorator := (printf "%s%s" (down $handlerName) $interfaceName) }} {{ $Decorator := (printf "%s%s" $handlerName $interfaceName) }} // {{$decorator}} implements {{.Interface.Type}} interface instrumented with rate limiter. type {{$decorator}} struct { wrapped {{.Interface.Type}} workflowIDCache workflowcache.WFCache logger log.Logger allowFunc func (domainID string, workflowID string) bool } // New{{$Decorator}} creates a new instance of {{$interfaceName}} with ratelimiter. func New{{$Decorator}}( wrapped {{.Interface.Type}}, workflowIDCache workflowcache.WFCache, logger log.Logger, ) {{.Interface.Type}} { wrapper := &{{$decorator}}{ wrapped: wrapped, workflowIDCache: workflowIDCache, logger: logger, } wrapper.allowFunc = wrapper.allowWfID return wrapper } {{range $method := .Interface.Methods}} func (h *{{$decorator}}) {{$method.Declaration}} { {{- if hasKey $ratelimitTypeMap $method.Name }} {{- if eq ( get ( get $ratelimitTypeMap $method.Name ) "ratelimit" ) "ratelimitTypeUserPerID" }} {{ $workflowID := ( get ( get $ratelimitTypeMap $method.Name ) "workflowID" ) }} {{ $req := (index $method.Params 1).Name }} if {{ $req }} == nil { err = validate.ErrRequestNotSet return } {{- $domainID := printf "%s.GetDomainUUID()" $req }} {{- $workflowID := printf "%s.%s" $req ( $workflowID ) }} if {{$domainID}} == "" { err = validate.ErrDomainNotSet return } if {{$workflowID}} == "" { err = validate.ErrWorkflowIDNotSet return } if !h.allowFunc({{$domainID}}, {{$workflowID}}) { err = &types.ServiceBusyError{ Message: "Too many requests for the workflow ID", Reason: constants.WorkflowIDRateLimitReason, } return } {{- end}} {{- end}} {{$method.Pass "h.wrapped."}} } {{end}} ================================================ FILE: service/history/testing/events_util.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package testing import ( "github.com/uber/cadence/common" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/execution" ) // AddWorkflowExecutionStartedEventWithParent adds WorkflowExecutionStarted event with parent workflow info func AddWorkflowExecutionStartedEventWithParent( builder execution.MutableState, workflowExecution types.WorkflowExecution, workflowType string, taskList string, input []byte, executionStartToCloseTimeout, taskStartToCloseTimeout int32, parentInfo *types.ParentExecutionInfo, identity string, activeClusterSelectionPolicy *types.ActiveClusterSelectionPolicy, ) *types.HistoryEvent { startRequest := &types.StartWorkflowExecutionRequest{ WorkflowID: workflowExecution.WorkflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: taskList}, Input: input, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(executionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(taskStartToCloseTimeout), Identity: identity, ActiveClusterSelectionPolicy: activeClusterSelectionPolicy, } event, _ := builder.AddWorkflowExecutionStartedEvent( workflowExecution, &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: constants.TestDomainID, StartRequest: startRequest, ParentExecutionInfo: parentInfo, }, ) return event } // AddWorkflowExecutionStartedEvent adds WorkflowExecutionStarted event func AddWorkflowExecutionStartedEvent( builder execution.MutableState, workflowExecution types.WorkflowExecution, workflowType string, taskList string, input []byte, executionStartToCloseTimeout int32, taskStartToCloseTimeout int32, identity string, activeClusterSelectionPolicy *types.ActiveClusterSelectionPolicy, ) *types.HistoryEvent { return AddWorkflowExecutionStartedEventWithParent(builder, workflowExecution, workflowType, taskList, input, executionStartToCloseTimeout, taskStartToCloseTimeout, nil, identity, activeClusterSelectionPolicy) } // AddDecisionTaskScheduledEvent adds DecisionTaskScheduled event func AddDecisionTaskScheduledEvent( builder execution.MutableState, ) *execution.DecisionInfo { di, _ := builder.AddDecisionTaskScheduledEvent(false) return di } // AddDecisionTaskStartedEvent adds DecisionTaskStarted event func AddDecisionTaskStartedEvent( builder execution.MutableState, scheduleID int64, taskList string, identity string, ) *types.HistoryEvent { return AddDecisionTaskStartedEventWithRequestID(builder, scheduleID, constants.TestRunID, taskList, identity) } // AddDecisionTaskStartedEventWithRequestID adds DecisionTaskStarted event with requestID func AddDecisionTaskStartedEventWithRequestID( builder execution.MutableState, scheduleID int64, requestID string, taskList string, identity string, ) *types.HistoryEvent { event, _, _ := builder.AddDecisionTaskStartedEvent(scheduleID, requestID, &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{Name: taskList}, Identity: identity, }) return event } // AddDecisionTaskCompletedEvent adds DecisionTaskCompleted event func AddDecisionTaskCompletedEvent( builder execution.MutableState, scheduleID int64, startedID int64, context []byte, identity string, ) *types.HistoryEvent { event, _ := builder.AddDecisionTaskCompletedEvent(scheduleID, startedID, &types.RespondDecisionTaskCompletedRequest{ ExecutionContext: context, Identity: identity, }, commonconstants.DefaultHistoryMaxAutoResetPoints) builder.FlushBufferedEvents() //nolint:errcheck return event } // AddActivityTaskScheduledEvent adds ActivityTaskScheduled event func AddActivityTaskScheduledEvent( builder execution.MutableState, decisionCompletedID int64, activityID string, activityType string, taskList string, input []byte, scheduleToCloseTimeout int32, scheduleToStartTimeout int32, startToCloseTimeout int32, heartbeatTimeout int32, ) (*types.HistoryEvent, *persistence.ActivityInfo) { event, ai, _, _, _, _ := builder.AddActivityTaskScheduledEvent(nil, decisionCompletedID, &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: activityID, ActivityType: &types.ActivityType{Name: activityType}, TaskList: &types.TaskList{Name: taskList, Kind: types.TaskListKindNormal.Ptr()}, Input: input, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(scheduleToCloseTimeout), ScheduleToStartTimeoutSeconds: common.Int32Ptr(scheduleToStartTimeout), StartToCloseTimeoutSeconds: common.Int32Ptr(startToCloseTimeout), HeartbeatTimeoutSeconds: common.Int32Ptr(heartbeatTimeout), }, false, ) return event, ai } // AddActivityTaskScheduledEventWithRetry adds ActivityTaskScheduled event with retry policy func AddActivityTaskScheduledEventWithRetry( builder execution.MutableState, decisionCompletedID int64, activityID string, activityType string, taskList string, input []byte, scheduleToCloseTimeout int32, scheduleToStartTimeout int32, startToCloseTimeout int32, heartbeatTimeout int32, retryPolicy *types.RetryPolicy, ) (*types.HistoryEvent, *persistence.ActivityInfo) { event, ai, _, _, _, _ := builder.AddActivityTaskScheduledEvent(nil, decisionCompletedID, &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: activityID, ActivityType: &types.ActivityType{Name: activityType}, TaskList: &types.TaskList{Name: taskList}, Input: input, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(scheduleToCloseTimeout), ScheduleToStartTimeoutSeconds: common.Int32Ptr(scheduleToStartTimeout), StartToCloseTimeoutSeconds: common.Int32Ptr(startToCloseTimeout), HeartbeatTimeoutSeconds: common.Int32Ptr(heartbeatTimeout), RetryPolicy: retryPolicy, }, false, ) return event, ai } // AddActivityTaskStartedEvent adds ActivityTaskStarted event func AddActivityTaskStartedEvent( builder execution.MutableState, scheduleID int64, identity string, ) *types.HistoryEvent { ai, _ := builder.GetActivityInfo(scheduleID) event, _ := builder.AddActivityTaskStartedEvent(ai, scheduleID, constants.TestRunID, identity) return event } // AddActivityTaskCompletedEvent adds ActivityTaskCompleted event func AddActivityTaskCompletedEvent( builder execution.MutableState, scheduleID int64, startedID int64, result []byte, identity string, ) *types.HistoryEvent { event, _ := builder.AddActivityTaskCompletedEvent(scheduleID, startedID, &types.RespondActivityTaskCompletedRequest{ Result: result, Identity: identity, }) return event } // AddActivityTaskFailedEvent adds ActivityTaskFailed event func AddActivityTaskFailedEvent( builder execution.MutableState, scheduleID int64, startedID int64, reason string, details []byte, identity string, ) *types.HistoryEvent { event, _ := builder.AddActivityTaskFailedEvent(scheduleID, startedID, &types.RespondActivityTaskFailedRequest{ Reason: common.StringPtr(reason), Details: details, Identity: identity, }) return event } // AddTimerStartedEvent adds TimerStarted event func AddTimerStartedEvent( builder execution.MutableState, decisionCompletedEventID int64, timerID string, timeOut int64, ) (*types.HistoryEvent, *persistence.TimerInfo) { event, ti, _ := builder.AddTimerStartedEvent(decisionCompletedEventID, &types.StartTimerDecisionAttributes{ TimerID: timerID, StartToFireTimeoutSeconds: common.Int64Ptr(timeOut), }) return event, ti } // AddTimerFiredEvent adds TimerFired event func AddTimerFiredEvent( mutableState execution.MutableState, timerID string, ) *types.HistoryEvent { event, _ := mutableState.AddTimerFiredEvent(timerID) return event } // AddRequestCancelInitiatedEvent adds RequestCancelExternalWorkflowExecutionInitiated event func AddRequestCancelInitiatedEvent( builder execution.MutableState, decisionCompletedEventID int64, cancelRequestID string, domain string, workflowID string, runID string, ) (*types.HistoryEvent, *persistence.RequestCancelInfo) { event, rci, _ := builder.AddRequestCancelExternalWorkflowExecutionInitiatedEvent(decisionCompletedEventID, cancelRequestID, &types.RequestCancelExternalWorkflowExecutionDecisionAttributes{ Domain: domain, WorkflowID: workflowID, RunID: runID, }) return event, rci } // AddCancelRequestedEvent adds ExternalWorkflowExecutionCancelRequested event func AddCancelRequestedEvent( builder execution.MutableState, initiatedID int64, domain string, workflowID string, runID string, ) *types.HistoryEvent { event, _ := builder.AddExternalWorkflowExecutionCancelRequested(initiatedID, domain, workflowID, runID) return event } // AddRequestSignalInitiatedEvent adds SignalExternalWorkflowExecutionInitiated event func AddRequestSignalInitiatedEvent( builder execution.MutableState, decisionCompletedEventID int64, signalRequestID string, domain string, workflowID string, runID string, signalName string, input []byte, control []byte, ) (*types.HistoryEvent, *persistence.SignalInfo) { event, si, _ := builder.AddSignalExternalWorkflowExecutionInitiatedEvent(decisionCompletedEventID, signalRequestID, &types.SignalExternalWorkflowExecutionDecisionAttributes{ Domain: domain, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, SignalName: signalName, Input: input, Control: control, }) return event, si } // AddSignaledEvent adds ExternalWorkflowExecutionSignaled event func AddSignaledEvent( builder execution.MutableState, initiatedID int64, domain string, workflowID string, runID string, control []byte, ) *types.HistoryEvent { event, _ := builder.AddExternalWorkflowExecutionSignaled(initiatedID, domain, workflowID, runID, control) return event } // AddStartChildWorkflowExecutionInitiatedEvent adds ChildWorkflowExecutionInitiated event func AddStartChildWorkflowExecutionInitiatedEvent( builder execution.MutableState, decisionCompletedID int64, createRequestID string, domain string, workflowID string, workflowType string, tasklist string, input []byte, executionStartToCloseTimeout int32, taskStartToCloseTimeout int32, retryPolicy *types.RetryPolicy, ) (*types.HistoryEvent, *persistence.ChildExecutionInfo) { event, cei, _ := builder.AddStartChildWorkflowExecutionInitiatedEvent(decisionCompletedID, createRequestID, &types.StartChildWorkflowExecutionDecisionAttributes{ Domain: domain, WorkflowID: workflowID, WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: &types.TaskList{Name: tasklist}, Input: input, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(executionStartToCloseTimeout), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(taskStartToCloseTimeout), Control: nil, RetryPolicy: retryPolicy, }) return event, cei } // AddChildWorkflowExecutionStartedEvent adds ChildWorkflowExecutionStarted event func AddChildWorkflowExecutionStartedEvent( builder execution.MutableState, initiatedID int64, domain string, workflowID string, runID string, workflowType string, ) *types.HistoryEvent { event, _ := builder.AddChildWorkflowExecutionStartedEvent( domain, &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, &types.WorkflowType{Name: workflowType}, initiatedID, &types.Header{}, ) return event } // AddChildWorkflowExecutionCompletedEvent adds ChildWorkflowExecutionCompleted event func AddChildWorkflowExecutionCompletedEvent( builder execution.MutableState, initiatedID int64, childExecution *types.WorkflowExecution, attributes *types.WorkflowExecutionCompletedEventAttributes, ) *types.HistoryEvent { event, _ := builder.AddChildWorkflowExecutionCompletedEvent(initiatedID, childExecution, attributes) return event } // AddCompleteWorkflowEvent adds WorkflowExecutionCompleted event func AddCompleteWorkflowEvent( builder execution.MutableState, decisionCompletedEventID int64, result []byte, ) *types.HistoryEvent { event, _ := builder.AddCompletedWorkflowEvent(decisionCompletedEventID, &types.CompleteWorkflowExecutionDecisionAttributes{ Result: result, }) return event } // AddFailWorkflowEvent adds WorkflowExecutionFailed event func AddFailWorkflowEvent( builder execution.MutableState, decisionCompletedEventID int64, reason string, details []byte, ) *types.HistoryEvent { event, _ := builder.AddFailWorkflowEvent(decisionCompletedEventID, &types.FailWorkflowExecutionDecisionAttributes{ Reason: &reason, Details: details, }) return event } ================================================ FILE: service/history/testing/workflow_util.go ================================================ // Copyright (c) 2021 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testing import ( "testing" "github.com/pborman/uuid" "github.com/uber/cadence/common" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) // StartWorkflow setup a workflow for testing purpose func StartWorkflow( t *testing.T, mockShard *shard.TestContext, sourceDomainID string, ) (types.WorkflowExecution, execution.MutableState, error) { return StartWorkflowWithTaskList(&types.TaskList{ Name: "some random task list", }, mockShard, sourceDomainID) } func StartWorkflowWithTaskList( tl *types.TaskList, mockShard *shard.TestContext, sourceDomainID string, ) (types.WorkflowExecution, execution.MutableState, error) { workflowExecution := types.WorkflowExecution{ WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, } workflowType := "some random workflow type" entry, err := mockShard.GetDomainCache().GetDomainByID(sourceDomainID) if err != nil { return types.WorkflowExecution{}, nil, err } version := entry.GetFailoverVersion() mutableState := execution.NewMutableStateBuilderWithVersionHistoriesWithEventV2( mockShard, mockShard.GetLogger(), version, workflowExecution.GetRunID(), entry, ) _, err = mutableState.AddWorkflowExecutionStartedEvent( workflowExecution, &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: sourceDomainID, StartRequest: &types.StartWorkflowExecutionRequest{ WorkflowType: &types.WorkflowType{Name: workflowType}, TaskList: tl, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(2), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(1), Header: &types.Header{Fields: map[string][]byte{ "context-key": []byte("contextValue"), "123456": []byte("123456"), // unsanitizable key "invalid-context-key": []byte("invalidContextValue"), }}, }, PartitionConfig: map[string]string{"userid": uuid.New()}, }, ) if err != nil { return types.WorkflowExecution{}, nil, err } return workflowExecution, mutableState, nil } // SetupWorkflowWithCompletedDecision setup a workflow with a completed decision task for testing purpose func SetupWorkflowWithCompletedDecision( t *testing.T, mockShard *shard.TestContext, sourceDomainID string, ) (types.WorkflowExecution, execution.MutableState, int64, error) { workflowExecution, mutableState, err := StartWorkflow(t, mockShard, sourceDomainID) if err != nil { return types.WorkflowExecution{}, nil, 0, err } di := AddDecisionTaskScheduledEvent(mutableState) event := AddDecisionTaskStartedEvent(mutableState, di.ScheduleID, mutableState.GetExecutionInfo().TaskList, uuid.New()) di.StartedID = event.ID event = AddDecisionTaskCompletedEvent(mutableState, di.ScheduleID, di.StartedID, nil, "some random identity") return workflowExecution, mutableState, event.ID, nil } // CreatePersistenceMutableState generated a persistence representation of the mutable state // a based on the in memory version func CreatePersistenceMutableState( t *testing.T, ms execution.MutableState, lastEventID int64, lastEventVersion int64, ) (*persistence.WorkflowMutableState, error) { if ms.GetVersionHistories() != nil { currentVersionHistory, err := ms.GetVersionHistories().GetCurrentVersionHistory() if err != nil { return nil, err } err = currentVersionHistory.AddOrUpdateItem(persistence.NewVersionHistoryItem( lastEventID, lastEventVersion, )) if err != nil { return nil, err } } return execution.CreatePersistenceMutableState(t, ms), nil } ================================================ FILE: service/history/workflow/context.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package workflow import ( "context" "github.com/uber/cadence/service/history/execution" ) type ( // Context is an helper interface on top of execution.Context Context interface { GetContext() execution.Context GetMutableState() execution.MutableState ReloadMutableState(ctx context.Context) (execution.MutableState, error) GetReleaseFn() execution.ReleaseFunc GetWorkflowID() string GetRunID() string } contextImpl struct { context execution.Context mutableState execution.MutableState releaseFn execution.ReleaseFunc } ) // NewContext creates a new helper instance on top of execution.Context func NewContext( context execution.Context, releaseFn execution.ReleaseFunc, mutableState execution.MutableState, ) Context { return &contextImpl{ context: context, releaseFn: releaseFn, mutableState: mutableState, } } func (w *contextImpl) GetContext() execution.Context { return w.context } func (w *contextImpl) GetMutableState() execution.MutableState { return w.mutableState } func (w *contextImpl) ReloadMutableState(ctx context.Context) (execution.MutableState, error) { mutableState, err := w.GetContext().LoadWorkflowExecution(ctx) if err != nil { return nil, err } w.mutableState = mutableState return mutableState, nil } func (w *contextImpl) GetReleaseFn() execution.ReleaseFunc { return w.releaseFn } func (w *contextImpl) GetWorkflowID() string { return w.GetContext().GetExecution().GetWorkflowID() } func (w *contextImpl) GetRunID() string { return w.GetContext().GetExecution().GetRunID() } ================================================ FILE: service/history/workflow/errors.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package workflow import ( "errors" "github.com/uber/cadence/common/types" ) var ( // ErrStaleState is the error returned during state update indicating that cached mutable state could be stale ErrStaleState = errors.New("cache mutable state could potentially be stale") // ErrMaxAttemptsExceeded is exported temporarily for integration test ErrMaxAttemptsExceeded = errors.New("maximum attempts exceeded to update history") // ErrActivityTaskNotFound is the error to indicate activity task could be duplicate and activity already completed ErrActivityTaskNotFound = &types.EntityNotExistsError{Message: "activity task not found"} // ErrNotExists is the error to indicate workflow doesn't exist ErrNotExists = &types.EntityNotExistsError{Message: "workflow execution already completed"} // ErrAlreadyCompleted is the error to indicate workflow execution already completed ErrAlreadyCompleted = &types.WorkflowExecutionAlreadyCompletedError{Message: "workflow execution already completed"} // ErrParentMismatch is the error to parent execution is given and mismatch ErrParentMismatch = &types.EntityNotExistsError{Message: "workflow parent does not match"} // ErrDeserializingToken is the error to indicate task token is invalid ErrDeserializingToken = &types.BadRequestError{Message: "error deserializing task token"} // ErrSerializingToken is the error to indicate task token can not be serialized ErrSerializingToken = &types.BadRequestError{Message: "error serializing task token"} // ErrSignalOverSize is the error to indicate signal input size is > 256K ErrSignalOverSize = &types.BadRequestError{Message: "signal input size is over 256K"} // ErrCancellationAlreadyRequested is the error indicating cancellation for target workflow is already requested ErrCancellationAlreadyRequested = &types.CancellationAlreadyRequestedError{Message: "cancellation already requested for this workflow execution"} // ErrSignalsLimitExceeded is the error indicating limit reached for maximum number of signal events ErrSignalsLimitExceeded = &types.LimitExceededError{Message: "exceeded workflow execution limit for signal events"} // ErrQueryEnteredInvalidState is error indicating query entered invalid state ErrQueryEnteredInvalidState = &types.BadRequestError{Message: "query entered invalid state, this should be impossible"} // ErrQueryWorkflowBeforeFirstDecision is error indicating that query was attempted before first decision task completed ErrQueryWorkflowBeforeFirstDecision = &types.QueryFailedError{Message: "workflow must handle at least one decision task before it can be queried"} // ErrConsistentQueryNotEnabled is error indicating that consistent query was requested but either cluster or domain does not enable consistent query ErrConsistentQueryNotEnabled = &types.BadRequestError{Message: "cluster or domain does not enable strongly consistent query but strongly consistent query was requested"} // ErrConsistentQueryBufferExceeded is error indicating that too many consistent queries have been buffered and until buffered queries are finished new consistent queries cannot be buffered ErrConsistentQueryBufferExceeded = &types.InternalServiceError{Message: "consistent query buffer is full, cannot accept new consistent queries"} // ErrConcurrentStartRequest is error indicating there is an outstanding start workflow request. The incoming request fails to acquires the lock before the outstanding request finishes. ErrConcurrentStartRequest = &types.ServiceBusyError{Message: "an outstanding start workflow request is in-progress. Failed to acquire the resource."} ) ================================================ FILE: service/history/workflow/util.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package workflow import ( "context" "time" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/execution" ) var ConditionalRetryCount = 5 type ( UpdateAction struct { Noop bool CreateDecision bool } UpdateActionFunc func(execution.Context, execution.MutableState) (*UpdateAction, error) ) var ( UpdateWithNewDecision = &UpdateAction{ CreateDecision: true, } UpdateWithoutDecision = &UpdateAction{ CreateDecision: false, } ) func LoadOnce( ctx context.Context, cache execution.Cache, domainID string, workflowID string, runID string, ) (Context, error) { wfContext, release, err := cache.GetOrCreateWorkflowExecution( ctx, domainID, types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, ) if err != nil { return nil, err } mutableState, err := wfContext.LoadWorkflowExecution(ctx) if err != nil { release(err) return nil, err } return NewContext(wfContext, release, mutableState), nil } func Load( ctx context.Context, cache execution.Cache, executionManager persistence.ExecutionManager, domainID string, domainName string, workflowID string, runID string, ) (Context, error) { if runID != "" { return LoadOnce(ctx, cache, domainID, workflowID, runID) } for attempt := 0; attempt < ConditionalRetryCount; attempt++ { workflowContext, err := LoadOnce(ctx, cache, domainID, workflowID, "") if err != nil { return nil, err } if workflowContext.GetMutableState().IsWorkflowExecutionRunning() { return workflowContext, nil } // workflow not running, need to check current record resp, err := executionManager.GetCurrentExecution( ctx, &persistence.GetCurrentExecutionRequest{ DomainID: domainID, WorkflowID: workflowID, DomainName: domainName, }, ) if err != nil { workflowContext.GetReleaseFn()(err) return nil, err } if resp.RunID == workflowContext.GetRunID() { return workflowContext, nil } workflowContext.GetReleaseFn()(nil) } return nil, &types.InternalServiceError{Message: "unable to locate current workflow execution"} } // ///////////////// Util function for updating workflows /////////////////// // UpdateWithActionFunc updates the given workflow execution. // If runID is empty, it only tries to load the current workflow once. // If the update should always be applied to the current run, use UpdateCurrentWithActionFunc instead. func UpdateWithActionFunc( ctx context.Context, logger log.Logger, cache execution.Cache, domainID string, execution types.WorkflowExecution, now time.Time, action UpdateActionFunc, ) (retError error) { workflowContext, err := LoadOnce(ctx, cache, domainID, execution.GetWorkflowID(), execution.GetRunID()) if err != nil { return err } defer func() { workflowContext.GetReleaseFn()(retError) }() return updateHelper(ctx, logger, workflowContext, now, action) } // UpdateCurrentWithActionFunc updates the given workflow execution or current execution if runID is empty. // It's the same as UpdateWithActionFunc if runID is not empty. // This function is suitable for the case when the change should always be applied to the current execution. func UpdateCurrentWithActionFunc( ctx context.Context, logger log.Logger, cache execution.Cache, executionManager persistence.ExecutionManager, domainID string, domainCache cache.DomainCache, execution types.WorkflowExecution, now time.Time, action UpdateActionFunc, ) (retError error) { domainName, err := domainCache.GetDomainName(domainID) if err != nil { return nil } workflowContext, err := Load(ctx, cache, executionManager, domainID, domainName, execution.GetWorkflowID(), execution.GetRunID()) if err != nil { return err } defer func() { workflowContext.GetReleaseFn()(retError) }() return updateHelper(ctx, logger, workflowContext, now, action) } // TODO: deprecate and use UpdateWithActionFunc func UpdateWithAction( ctx context.Context, logger log.Logger, cache execution.Cache, domainID string, execution types.WorkflowExecution, createDecisionTask bool, now time.Time, action func(wfContext execution.Context, mutableState execution.MutableState) error, ) error { return UpdateWithActionFunc( ctx, logger, cache, domainID, execution, now, getUpdateActionFunc(createDecisionTask, action), ) } func getUpdateActionFunc( createDecisionTask bool, action func(wfContext execution.Context, mutableState execution.MutableState) error, ) UpdateActionFunc { return func(wfContext execution.Context, mutableState execution.MutableState) (*UpdateAction, error) { err := action(wfContext, mutableState) if err != nil { return nil, err } postActions := &UpdateAction{ CreateDecision: createDecisionTask, } return postActions, nil } } func updateHelper( ctx context.Context, logger log.Logger, workflowContext Context, now time.Time, action UpdateActionFunc, ) (retError error) { UpdateHistoryLoop: for attempt := 0; attempt < ConditionalRetryCount; attempt++ { wfContext := workflowContext.GetContext() mutableState := workflowContext.GetMutableState() // conduct caller action postActions, err := action(wfContext, mutableState) if err != nil { if err == ErrStaleState { // Handler detected that cached workflow mutable could potentially be stale // Reload workflow execution history workflowContext.GetContext().Clear() if attempt != ConditionalRetryCount-1 { _, err = workflowContext.ReloadMutableState(ctx) if err != nil { return err } } continue UpdateHistoryLoop } // Returned error back to the caller return err } if postActions.Noop { return nil } if postActions.CreateDecision { // Create a transfer task to schedule a decision task if !mutableState.HasPendingDecision() { _, err := mutableState.AddDecisionTaskScheduledEvent(false) if err != nil { return &types.InternalServiceError{Message: "Failed to add decision scheduled event."} } } } logger.Debug("updateHelper calling UpdateWorkflowExecutionAsActive", tag.WorkflowID(mutableState.GetExecutionInfo().WorkflowID)) err = workflowContext.GetContext().UpdateWorkflowExecutionAsActive(ctx, now) if _, ok := err.(*persistence.DuplicateRequestError); ok { return nil } if execution.IsConflictError(err) { if attempt != ConditionalRetryCount-1 { _, err = workflowContext.ReloadMutableState(ctx) if err != nil { return err } } continue UpdateHistoryLoop } return err } return ErrMaxAttemptsExceeded } ================================================ FILE: service/history/workflow/util_test.go ================================================ // Copyright (c) 2017-2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package workflow import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" commonconstants "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/config" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/history/execution" "github.com/uber/cadence/service/history/shard" ) func TestUpdateHelper(t *testing.T) { testCases := []struct { msg string mockSetupFn func(*execution.MockContext, *execution.MockMutableState) actionFn UpdateActionFunc }{ { msg: "stale mutable state", mockSetupFn: func(mockContext *execution.MockContext, mockMutableState *execution.MockMutableState) { mockContext.EXPECT().Clear().Times(1) mockMutableState.EXPECT().GetNextEventID().Return(commonconstants.FirstEventID).Times(1) mockMutableState.EXPECT().GetNextEventID().Return(commonconstants.FirstEventID + 1).Times(1) }, actionFn: func(context execution.Context, mutableState execution.MutableState) (*UpdateAction, error) { if mutableState.GetNextEventID() == commonconstants.FirstEventID { return nil, ErrStaleState } return &UpdateAction{Noop: true}, nil }, }, { msg: "schedule new decision", mockSetupFn: func(mockContext *execution.MockContext, mockMutableState *execution.MockMutableState) { mockMutableState.EXPECT().HasPendingDecision().Return(false).Times(1) mockMutableState.EXPECT().AddDecisionTaskScheduledEvent(gomock.Any()).Return(&execution.DecisionInfo{}, nil).Times(1) mockContext.EXPECT().UpdateWorkflowExecutionAsActive(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, actionFn: func(context execution.Context, mutableState execution.MutableState) (*UpdateAction, error) { return UpdateWithNewDecision, nil }, }, { msg: "update workflow conflict", mockSetupFn: func(mockContext *execution.MockContext, mockMutableState *execution.MockMutableState) { mockContext.EXPECT().UpdateWorkflowExecutionAsActive(gomock.Any(), gomock.Any()).Return(execution.NewConflictError(t, assert.AnError)).Times(ConditionalRetryCount - 1) mockContext.EXPECT().UpdateWorkflowExecutionAsActive(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, actionFn: func(context execution.Context, mutableState execution.MutableState) (*UpdateAction, error) { return UpdateWithoutDecision, nil }, }, { msg: "duplicate request", mockSetupFn: func(mockContext *execution.MockContext, mockMutableState *execution.MockMutableState) { mockContext.EXPECT().UpdateWorkflowExecutionAsActive(gomock.Any(), gomock.Any()).Return(&persistence.DuplicateRequestError{}) }, actionFn: func(context execution.Context, mutableState execution.MutableState) (*UpdateAction, error) { return UpdateWithoutDecision, nil }, }, } for _, tc := range testCases { t.Run(tc.msg, func(t *testing.T) { controller := gomock.NewController(t) mockMutableState := execution.NewMockMutableState(controller) mockMutableState.EXPECT().GetExecutionInfo().Return(&persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, State: persistence.WorkflowStateRunning, }).AnyTimes() mockContext := execution.NewMockContext(controller) mockContext.EXPECT().LoadWorkflowExecution(gomock.Any()).Return(mockMutableState, nil).AnyTimes() workflowContext := NewContext(mockContext, nil, mockMutableState) tc.mockSetupFn(mockContext, mockMutableState) err := updateHelper(context.Background(), testlogger.New(t), workflowContext, time.Now(), tc.actionFn) require.NoError(t, err) }) } } func TestWorkflowLoad(t *testing.T) { persistenceMS := &persistence.WorkflowMutableState{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, State: persistence.WorkflowStateRunning, }, ExecutionStats: &persistence.ExecutionStats{}, } testCases := []struct { msg string runID string mockSetupFn func(*shard.TestContext) }{ { msg: "runID not empty", runID: constants.TestRunID, mockSetupFn: func(mockShard *shard.TestContext) { mockShard.Resource.ExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return( &persistence.GetWorkflowExecutionResponse{ State: persistenceMS, }, nil, ).Times(1) mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil).AnyTimes() }, }, { msg: "current run closed", runID: "", mockSetupFn: func(mockShard *shard.TestContext) { persistenceMS.ExecutionInfo.State = persistence.WorkflowStateCompleted mockShard.Resource.ExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return( &persistence.GetWorkflowExecutionResponse{ State: persistenceMS, }, nil, ).Times(1) mockShard.Resource.ActiveClusterMgr.EXPECT().GetActiveClusterInfoByWorkflow(gomock.Any(), constants.TestDomainID, constants.TestWorkflowID, constants.TestRunID).Return(&types.ActiveClusterInfo{ActiveClusterName: "test-active-cluster"}, nil).AnyTimes() mockShard.Resource.ExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return( &persistence.GetCurrentExecutionResponse{ RunID: constants.TestRunID, }, nil, ).Times(2) }, }, } for _, tc := range testCases { t.Run(tc.msg, func(t *testing.T) { controller := gomock.NewController(t) mockShard := shard.NewTestContext( t, controller, &persistence.ShardInfo{ ShardID: 10, RangeID: 1, }, config.NewForTest(), ) mockDomainCache := mockShard.Resource.DomainCache mockDomainCache.EXPECT().GetDomainByID(constants.TestLocalDomainEntry.GetInfo().ID).Return(constants.TestLocalDomainEntry, nil) mockDomainCache.EXPECT().GetDomainName(constants.TestLocalDomainEntry.GetInfo().ID).Return(constants.TestLocalDomainEntry.GetInfo().Name, nil).AnyTimes() tc.mockSetupFn(mockShard) workflowContext, err := Load( context.Background(), execution.NewCache(mockShard), mockShard.Resource.ExecutionMgr, constants.TestDomainID, constants.TestDomainName, constants.TestWorkflowID, tc.runID, ) require.NoError(t, err) require.Equal(t, constants.TestWorkflowID, workflowContext.GetWorkflowID()) require.Equal(t, constants.TestRunID, workflowContext.GetRunID()) workflowContext.GetReleaseFn()(nil) }) } } ================================================ FILE: service/history/workflowcache/cache.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package=$GOPACKAGE -destination=cache_mock.go github.com/uber/cadence/service/history/workflowcache WFCache package workflowcache import ( "errors" "time" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" ) var ( errDomainName = errors.New("failed to get domain name from domainID") ) // WFCache is a per workflow cache used for workflow specific in memory data type WFCache interface { AllowExternal(domainID string, workflowID string) bool AllowInternal(domainID string, workflowID string) bool } type wfCache struct { lru cache.Cache externalLimiterFactory quotas.LimiterFactory internalLimiterFactory quotas.LimiterFactory domainCache cache.DomainCache metricsClient metrics.Client logger log.Logger timeSource clock.TimeSource // we use functions to get cache items, and the current time, so we can mock it in unit tests getCacheItemFn func(domainName string, workflowID string) (*cacheValue, error) } type cacheKey struct { domainName string workflowID string } type cacheValue struct { externalRateLimiter quotas.Limiter internalRateLimiter quotas.Limiter externalCountMetric workflowIDCountMetric internalCountMetric workflowIDCountMetric } // Params is the parameters for a new WFCache type Params struct { TTL time.Duration MaxCount int ExternalLimiterFactory quotas.LimiterFactory InternalLimiterFactory quotas.LimiterFactory DomainCache cache.DomainCache MetricsClient metrics.Client Logger log.Logger } // New creates a new WFCache func New(params Params) WFCache { cache := &wfCache{ lru: cache.New(&cache.Options{ TTL: params.TTL, Pin: false, MaxCount: params.MaxCount, ActivelyEvict: true, MetricsScope: params.MetricsClient.Scope(metrics.HistoryWorkflowCacheScope), Logger: params.Logger, }), externalLimiterFactory: params.ExternalLimiterFactory, internalLimiterFactory: params.InternalLimiterFactory, domainCache: params.DomainCache, metricsClient: params.MetricsClient, timeSource: clock.NewRealTimeSource(), logger: params.Logger, } // We set getCacheItemFn to cache.getCacheItem so that we can mock it in unit tests cache.getCacheItemFn = cache.getCacheItem return cache } type rateLimitType int const ( external rateLimitType = iota internal ) func (c *wfCache) allow(domainID string, workflowID string, rateLimitType rateLimitType) bool { domainName, err := c.domainCache.GetDomainName(domainID) if err != nil { c.logError(domainID, workflowID, errDomainName) // The cache is not enabled if the domain does not exist or there is an error getting it (fail open) return true } c.metricsClient. Scope(metrics.HistoryClientWfIDCacheScope). UpdateGauge(metrics.WorkflowIDCacheSizeGauge, float64(c.lru.Size())) // Locking is not needed because both getCacheItem and the rate limiter are thread safe value, err := c.getCacheItemFn(domainName, workflowID) if err != nil { c.logError(domainID, workflowID, err) // If we can't get the cache item, we should allow the request through return true } switch rateLimitType { case external: value.externalCountMetric.updatePerDomainMaxWFRequestCount(domainName, c.timeSource, c.metricsClient, metrics.WorkflowIDCacheRequestsExternalMaxRequestsPerSecondsTimer) if !value.externalRateLimiter.Allow() { c.emitRateLimitMetrics( domainID, workflowID, domainName, "external", metrics.WorkflowIDCacheRequestsExternalRatelimitedCounter, ) return false } return true case internal: value.internalCountMetric.updatePerDomainMaxWFRequestCount(domainName, c.timeSource, c.metricsClient, metrics.WorkflowIDCacheRequestsInternalMaxRequestsPerSecondsTimer) if !value.internalRateLimiter.Allow() { c.emitRateLimitMetrics( domainID, workflowID, domainName, "internal", metrics.WorkflowIDCacheRequestsInternalRatelimitedCounter, ) return false } return true default: // This should never happen, and we fail open c.logError(domainID, workflowID, errors.New("unknown rate limit type")) return true } } func (c *wfCache) emitRateLimitMetrics( domainID string, workflowID string, domainName string, callType string, metric metrics.MetricIdx, ) { c.metricsClient.Scope( metrics.HistoryClientWfIDCacheScope, metrics.DomainTag(domainName), ).IncCounter(metric) c.logger.Info( "Rate limiting workflowID", tag.RequestType(callType), tag.WorkflowDomainID(domainID), tag.WorkflowDomainName(domainName), tag.WorkflowID(workflowID), ) } // AllowExternal returns true if the rate limiter for this domain/workflow allows an external request func (c *wfCache) AllowExternal(domainID string, workflowID string) bool { return c.allow(domainID, workflowID, external) } // AllowInternal returns true if the rate limiter for this domain/workflow allows an internal request func (c *wfCache) AllowInternal(domainID string, workflowID string) bool { return c.allow(domainID, workflowID, internal) } func (c *wfCache) getCacheItem(domainName string, workflowID string) (*cacheValue, error) { // The underlying lru cache is thread safe, so there is no need to lock key := cacheKey{ domainName: domainName, workflowID: workflowID, } value, ok := c.lru.Get(key).(*cacheValue) if ok { return value, nil } value = &cacheValue{ externalRateLimiter: c.externalLimiterFactory.GetLimiter(domainName), internalRateLimiter: c.internalLimiterFactory.GetLimiter(domainName), } // PutIfNotExist is thread safe, and will either return the value that was already in the cache or the value we just created // another thread might have inserted a value between the Get and PutIfNotExist, but that is ok // it should never return an error as we do not use Pin valueInterface, err := c.lru.PutIfNotExist(key, value) if err != nil { return nil, err } value, ok = valueInterface.(*cacheValue) // This should never happen, either the value was already in the cache or we just inserted it if !ok { return nil, errors.New("Failed to insert new value into cache") } return value, err } func (c *wfCache) logError(domainID string, workflowID string, err error) { c.logger.Error("Unexpected error from workflow cache", tag.Error(err), tag.WorkflowDomainID(domainID), tag.WorkflowID(workflowID), tag.WorkflowIDCacheSize(c.lru.Size()), ) } ================================================ FILE: service/history/workflowcache/cache_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/history/workflowcache (interfaces: WFCache) // // Generated by this command: // // mockgen -package=workflowcache -destination=cache_mock.go github.com/uber/cadence/service/history/workflowcache WFCache // // Package workflowcache is a generated GoMock package. package workflowcache import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockWFCache is a mock of WFCache interface. type MockWFCache struct { ctrl *gomock.Controller recorder *MockWFCacheMockRecorder isgomock struct{} } // MockWFCacheMockRecorder is the mock recorder for MockWFCache. type MockWFCacheMockRecorder struct { mock *MockWFCache } // NewMockWFCache creates a new mock instance. func NewMockWFCache(ctrl *gomock.Controller) *MockWFCache { mock := &MockWFCache{ctrl: ctrl} mock.recorder = &MockWFCacheMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWFCache) EXPECT() *MockWFCacheMockRecorder { return m.recorder } // AllowExternal mocks base method. func (m *MockWFCache) AllowExternal(domainID, workflowID string) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AllowExternal", domainID, workflowID) ret0, _ := ret[0].(bool) return ret0 } // AllowExternal indicates an expected call of AllowExternal. func (mr *MockWFCacheMockRecorder) AllowExternal(domainID, workflowID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowExternal", reflect.TypeOf((*MockWFCache)(nil).AllowExternal), domainID, workflowID) } // AllowInternal mocks base method. func (m *MockWFCache) AllowInternal(domainID, workflowID string) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AllowInternal", domainID, workflowID) ret0, _ := ret[0].(bool) return ret0 } // AllowInternal indicates an expected call of AllowInternal. func (mr *MockWFCacheMockRecorder) AllowInternal(domainID, workflowID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowInternal", reflect.TypeOf((*MockWFCache)(nil).AllowInternal), domainID, workflowID) } ================================================ FILE: service/history/workflowcache/cache_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package workflowcache import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" ) const ( testDomainID = "B59344B2-4166-462D-9CBD-22B25D2A7B1B" testDomainName = "testDomainName" testWorkflowID = "8ED9219B-36A2-4FD0-B9EA-6298A0F2ED1A" testWorkflowID2 = "F6E31C3D-3E54-4530-BDBE-68AEBA475473" ) // TestWfCache_AllowSingleWorkflow tests that the cache will use the correct rate limiter for internal and external requests. func TestWfCache_AllowSingleWorkflow(t *testing.T) { ctrl := gomock.NewController(t) domainCache := cache.NewMockDomainCache(ctrl) domainCache.EXPECT().GetDomainName(testDomainID).Return(testDomainName, nil).Times(4) // The external rate limiter will allow the first request, but not the second. externalLimiter := quotas.NewMockLimiter(ctrl) externalLimiter.EXPECT().Allow().Return(true).Times(1) externalLimiter.EXPECT().Allow().Return(false).Times(1) externalLimiterFactory := quotas.NewMockLimiterFactory(ctrl) externalLimiterFactory.EXPECT().GetLimiter(testDomainName).Return(externalLimiter).Times(1) // The internal rate limiter will allow the second request, but not the first. internalLimiter := quotas.NewMockLimiter(ctrl) internalLimiter.EXPECT().Allow().Return(false).Times(1) internalLimiter.EXPECT().Allow().Return(true).Times(1) internalLimiterFactory := quotas.NewMockLimiterFactory(ctrl) internalLimiterFactory.EXPECT().GetLimiter(testDomainName).Return(internalLimiter).Times(1) wfCache := New(Params{ // The cache TTL is set to 1 minute, so all requests will hit the cache TTL: time.Minute, MaxCount: 1_000, ExternalLimiterFactory: externalLimiterFactory, InternalLimiterFactory: internalLimiterFactory, Logger: log.NewNoop(), DomainCache: domainCache, MetricsClient: metrics.NewNoopMetricsClient(), }) assert.True(t, wfCache.AllowExternal(testDomainID, testWorkflowID)) assert.False(t, wfCache.AllowExternal(testDomainID, testWorkflowID)) assert.False(t, wfCache.AllowInternal(testDomainID, testWorkflowID)) assert.True(t, wfCache.AllowInternal(testDomainID, testWorkflowID)) } // TestWfCache_AllowMultipleWorkflow tests that the cache will use the correct rate limiter for different workflows. func TestWfCache_AllowMultipleWorkflow(t *testing.T) { ctrl := gomock.NewController(t) domainCache := cache.NewMockDomainCache(ctrl) domainCache.EXPECT().GetDomainName(testDomainID).Return(testDomainName, nil).Times(4) // The external rate limiter for wf1 will allow the first request, but not the second. externalLimiterWf1 := quotas.NewMockLimiter(ctrl) externalLimiterWf1.EXPECT().Allow().Return(true).Times(1) externalLimiterWf1.EXPECT().Allow().Return(false).Times(1) // The external rate limiter for wf2 will allow the second request, but not the first. externalLimiterWf2 := quotas.NewMockLimiter(ctrl) externalLimiterWf2.EXPECT().Allow().Return(false).Times(1) externalLimiterWf2.EXPECT().Allow().Return(true).Times(1) externalLimiterFactory := quotas.NewMockLimiterFactory(ctrl) externalLimiterFactory.EXPECT().GetLimiter(testDomainName).Return(externalLimiterWf1).Times(1) externalLimiterFactory.EXPECT().GetLimiter(testDomainName).Return(externalLimiterWf2).Times(1) // We do not expect calls to the internal rate limiters, but they will still be created. internalLimiterWf1 := quotas.NewMockLimiter(ctrl) internalLimiterWf2 := quotas.NewMockLimiter(ctrl) internalLimiterFactory := quotas.NewMockLimiterFactory(ctrl) internalLimiterFactory.EXPECT().GetLimiter(testDomainName).Return(internalLimiterWf1).Times(1) internalLimiterFactory.EXPECT().GetLimiter(testDomainName).Return(internalLimiterWf2).Times(1) wfCache := New(Params{ TTL: time.Minute, MaxCount: 1_000, ExternalLimiterFactory: externalLimiterFactory, InternalLimiterFactory: internalLimiterFactory, Logger: log.NewNoop(), DomainCache: domainCache, MetricsClient: metrics.NewNoopMetricsClient(), }) assert.True(t, wfCache.AllowExternal(testDomainID, testWorkflowID)) assert.False(t, wfCache.AllowExternal(testDomainID, testWorkflowID2)) assert.False(t, wfCache.AllowExternal(testDomainID, testWorkflowID)) assert.True(t, wfCache.AllowExternal(testDomainID, testWorkflowID2)) } // TestWfCache_AllowInternalError tests that the cache will allow internal requests through if there is an error getting the rate limiter. func TestWfCache_AllowError(t *testing.T) { ctrl := gomock.NewController(t) domainCache := cache.NewMockDomainCache(ctrl) domainCache.EXPECT().GetDomainName(testDomainID).Return(testDomainName, nil).Times(2) // Setup the mock logger logger := log.NewMockLogger(ctrl) logger.EXPECT().Error( "Unexpected error from workflow cache", []tag.Tag{ tag.Error(assert.AnError), tag.WorkflowDomainID(testDomainID), tag.WorkflowID(testWorkflowID), tag.WorkflowIDCacheSize(0), }, ).Times(2) logger.EXPECT().Info( "LRU cache initialized", []tag.Tag{ tag.Value(map[string]interface{}{ "isSizeBased": false, "maxCount": 1000, "maxSize": 1073741824, }), }).Times(1) // Setup the cache, we do not need the factories, as we will mock the getCacheItemFn wfCache := New(Params{ TTL: time.Minute, MaxCount: 1_000, ExternalLimiterFactory: nil, InternalLimiterFactory: nil, Logger: logger, DomainCache: domainCache, MetricsClient: metrics.NewNoopMetricsClient(), }).(*wfCache) // We set getCacheItemFn to a function that will return an error so that we can test the error logic wfCache.getCacheItemFn = func(domainName string, workflowID string) (*cacheValue, error) { return nil, assert.AnError } // We fail open assert.True(t, wfCache.AllowExternal(testDomainID, testWorkflowID)) assert.True(t, wfCache.AllowInternal(testDomainID, testWorkflowID)) } // TestWfCache_AllowDomainCacheError tests that the cache will allow requests through if there is an error getting the domain name. func TestWfCache_AllowDomainCacheError(t *testing.T) { ctrl := gomock.NewController(t) domainCache := cache.NewMockDomainCache(ctrl) domainCache.EXPECT().GetDomainName(testDomainID).Return("", assert.AnError).Times(2) // Setup the mock logger logger := log.NewMockLogger(ctrl) logger.EXPECT().Error( "Unexpected error from workflow cache", []tag.Tag{ tag.Error(errDomainName), tag.WorkflowDomainID(testDomainID), tag.WorkflowID(testWorkflowID), tag.WorkflowIDCacheSize(0), }, ).Times(2) logger.EXPECT().Info( "LRU cache initialized", []tag.Tag{ tag.Value(map[string]interface{}{ "isSizeBased": false, "maxCount": 1000, "maxSize": 1073741824, }), }).Times(1) // Setup the cache, we do not need the factories, as we will mock the getCacheItemFn wfCache := New(Params{ TTL: time.Minute, MaxCount: 1_000, ExternalLimiterFactory: nil, InternalLimiterFactory: nil, Logger: logger, DomainCache: domainCache, MetricsClient: metrics.NewNoopMetricsClient(), }) // We fail open assert.True(t, wfCache.AllowExternal(testDomainID, testWorkflowID)) assert.True(t, wfCache.AllowInternal(testDomainID, testWorkflowID)) } func TestWfCache_RejectLog(t *testing.T) { ctrl := gomock.NewController(t) domainCache := cache.NewMockDomainCache(ctrl) domainCache.EXPECT().GetDomainName(testDomainID).Return(testDomainName, nil).Times(2) // The external rate limiter will reject externalLimiter := quotas.NewMockLimiter(ctrl) externalLimiter.EXPECT().Allow().Return(false).Times(1) externalLimiterFactory := quotas.NewMockLimiterFactory(ctrl) externalLimiterFactory.EXPECT().GetLimiter(testDomainName).Return(externalLimiter).Times(1) // The internal rate limiter will reject internalLimiter := quotas.NewMockLimiter(ctrl) internalLimiter.EXPECT().Allow().Return(false).Times(1) internalLimiterFactory := quotas.NewMockLimiterFactory(ctrl) internalLimiterFactory.EXPECT().GetLimiter(testDomainName).Return(internalLimiter).Times(1) // Setup the mock logger logger := log.NewMockLogger(ctrl) logger.EXPECT().Info( "LRU cache initialized", []tag.Tag{ tag.Value(map[string]interface{}{ "isSizeBased": false, "maxCount": 1000, "maxSize": 1073741824, }), }).Times(1) logger.EXPECT().Warn("Cache is strictly count-based because value *workflowcache.cacheValue does not implement sizable").Times(1) expectRatelimitLog(logger, "external") expectRatelimitLog(logger, "internal") wfCache := New(Params{ TTL: time.Minute, MaxCount: 1_000, ExternalLimiterFactory: externalLimiterFactory, InternalLimiterFactory: internalLimiterFactory, Logger: logger, DomainCache: domainCache, MetricsClient: metrics.NewNoopMetricsClient(), }) assert.False(t, wfCache.AllowExternal(testDomainID, testWorkflowID)) assert.False(t, wfCache.AllowInternal(testDomainID, testWorkflowID)) } func expectRatelimitLog(logger *log.MockLogger, requestType string) { logger.EXPECT().Info( "Rate limiting workflowID", []tag.Tag{ tag.RequestType(requestType), tag.WorkflowDomainID(testDomainID), tag.WorkflowDomainName(testDomainName), tag.WorkflowID(testWorkflowID), }, ).Times(1) } ================================================ FILE: service/history/workflowcache/metrics.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package workflowcache import ( "sync" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/metrics" ) // workflowIDCountMetric holds the count of requests for a given second, for a domain/workflowID pair // This is used to emit the max count of requests for a given domain // Ideally we would just emit the count of requests for a given domain/workflowID pair, but this is not // possible, due to the high cardinality of workflowIDs type workflowIDCountMetric struct { sync.Mutex startingSecond time.Time count int } func (cm *workflowIDCountMetric) reset(now time.Time) { cm.startingSecond = now cm.count = 0 } func (cm *workflowIDCountMetric) updatePerDomainMaxWFRequestCount( domainName string, timeSource clock.TimeSource, metricsClient metrics.Client, metric metrics.MetricIdx, ) { cm.Lock() defer cm.Unlock() if timeSource.Since(cm.startingSecond) > time.Second { cm.reset(timeSource.Now().UTC()) } cm.count++ // We can just use the upper of the metric, so it is not an issue to emit all the counts metricsClient.Scope(metrics.HistoryClientWfIDCacheScope, metrics.DomainTag(domainName)). RecordTimer(metric, time.Duration(cm.count)) } ================================================ FILE: service/history/workflowcache/metrics_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package workflowcache import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/metrics" ) func TestUpdatePerDomainMaxWFRequestCount(t *testing.T) { domainName := "some domain name" metric := metrics.WorkflowIDCacheRequestsInternalMaxRequestsPerSecondsTimer cases := []struct { name string updatePerDomainMaxWFRequestCount func(metricsClient metrics.Client, source clock.MockedTimeSource) expecetMetrics []time.Duration }{ { name: "Single workflowID", updatePerDomainMaxWFRequestCount: func(metricsClient metrics.Client, timeSource clock.MockedTimeSource) { workflowID1 := &workflowIDCountMetric{} workflowID1.updatePerDomainMaxWFRequestCount(domainName, timeSource, metricsClient, metric) // Emits 1 workflowID1.updatePerDomainMaxWFRequestCount(domainName, timeSource, metricsClient, metric) // Emits 2 }, expecetMetrics: []time.Duration{1, 2}, }, { name: "Separate workflowIDs", updatePerDomainMaxWFRequestCount: func(metricsClient metrics.Client, timeSource clock.MockedTimeSource) { workflowID1 := &workflowIDCountMetric{} workflowID1.updatePerDomainMaxWFRequestCount(domainName, timeSource, metricsClient, metric) // Emits 1 workflowID2 := &workflowIDCountMetric{} workflowID2.updatePerDomainMaxWFRequestCount(domainName, timeSource, metricsClient, metric) // Emits 1 workflowID2.updatePerDomainMaxWFRequestCount(domainName, timeSource, metricsClient, metric) // Emits 2 workflowID2.updatePerDomainMaxWFRequestCount(domainName, timeSource, metricsClient, metric) // Emits 3 workflowID1.updatePerDomainMaxWFRequestCount(domainName, timeSource, metricsClient, metric) // Emits 2 }, expecetMetrics: []time.Duration{1, 1, 2, 3, 2}, }, { name: "Reset", updatePerDomainMaxWFRequestCount: func(metricsClient metrics.Client, timeSource clock.MockedTimeSource) { workflowID1 := &workflowIDCountMetric{} workflowID1.updatePerDomainMaxWFRequestCount(domainName, timeSource, metricsClient, metric) // Emits 1 workflowID1.updatePerDomainMaxWFRequestCount(domainName, timeSource, metricsClient, metric) // Emits 2 timeSource.Advance(1100 * time.Millisecond) workflowID1.updatePerDomainMaxWFRequestCount(domainName, timeSource, metricsClient, metric) // Emits 1 }, expecetMetrics: []time.Duration{1, 2, 1}, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { testScope := tally.NewTestScope("", make(map[string]string)) timeSource := clock.NewMockedTimeSourceAt(time.Unix(123, 456)) metricsClient := metrics.NewClient(testScope, metrics.History, metrics.HistogramMigration{}) tc.updatePerDomainMaxWFRequestCount(metricsClient, timeSource) // We expect the domain tag to be set to "all" and "some domain name", we don't know the order, so use a set expectedDomainTags := map[string]struct{}{"all": {}, "some domain name": {}} actualDomainTags := map[string]struct{}{} timers := testScope.Snapshot().Timers() assert.Equal(t, 2, len(timers)) for _, v := range timers { actualDomainTags[v.Tags()["domain"]] = struct{}{} assert.Equal(t, tc.expecetMetrics, v.Values()) } assert.Equal(t, expectedDomainTags, actualDomainTags) }) } } ================================================ FILE: service/history/wrappers/grpc/grpc_handler.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package grpc import ( "context" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc" historyv1 "github.com/uber/cadence/.gen/proto/history/v1" "github.com/uber/cadence/common/types/mapper/proto" ) func (g GRPCHandler) Register(dispatcher *yarpc.Dispatcher) { dispatcher.Register(historyv1.BuildHistoryAPIYARPCProcedures(g)) dispatcher.Register(apiv1.BuildMetaAPIYARPCProcedures(g)) } func (g GRPCHandler) Health(ctx context.Context, _ *apiv1.HealthRequest) (*apiv1.HealthResponse, error) { response, err := g.h.Health(ctx) return proto.FromHealthResponse(response), proto.FromError(err) } ================================================ FILE: service/history/wrappers/grpc/grpc_handler_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" historyv1 "github.com/uber/cadence/.gen/proto/history/v1" "github.com/uber/cadence/common/types/mapper/proto" "github.com/uber/cadence/service/history/handler" ) type GRPCHandler struct { h handler.Handler } func NewGRPCHandler(h handler.Handler) GRPCHandler { return GRPCHandler{h} } func (g GRPCHandler) CloseShard(ctx context.Context, request *historyv1.CloseShardRequest) (*historyv1.CloseShardResponse, error) { err := g.h.CloseShard(ctx, proto.ToHistoryCloseShardRequest(request)) return &historyv1.CloseShardResponse{}, proto.FromError(err) } func (g GRPCHandler) CountDLQMessages(ctx context.Context, request *historyv1.CountDLQMessagesRequest) (*historyv1.CountDLQMessagesResponse, error) { response, err := g.h.CountDLQMessages(ctx, proto.ToHistoryCountDLQMessagesRequest(request)) return proto.FromHistoryCountDLQMessagesResponse(response), proto.FromError(err) } func (g GRPCHandler) DescribeHistoryHost(ctx context.Context, request *historyv1.DescribeHistoryHostRequest) (*historyv1.DescribeHistoryHostResponse, error) { response, err := g.h.DescribeHistoryHost(ctx, proto.ToHistoryDescribeHistoryHostRequest(request)) return proto.FromHistoryDescribeHistoryHostResponse(response), proto.FromError(err) } func (g GRPCHandler) DescribeMutableState(ctx context.Context, request *historyv1.DescribeMutableStateRequest) (*historyv1.DescribeMutableStateResponse, error) { response, err := g.h.DescribeMutableState(ctx, proto.ToHistoryDescribeMutableStateRequest(request)) return proto.FromHistoryDescribeMutableStateResponse(response), proto.FromError(err) } func (g GRPCHandler) DescribeQueue(ctx context.Context, request *historyv1.DescribeQueueRequest) (*historyv1.DescribeQueueResponse, error) { response, err := g.h.DescribeQueue(ctx, proto.ToHistoryDescribeQueueRequest(request)) return proto.FromHistoryDescribeQueueResponse(response), proto.FromError(err) } func (g GRPCHandler) DescribeWorkflowExecution(ctx context.Context, request *historyv1.DescribeWorkflowExecutionRequest) (*historyv1.DescribeWorkflowExecutionResponse, error) { response, err := g.h.DescribeWorkflowExecution(ctx, proto.ToHistoryDescribeWorkflowExecutionRequest(request)) return proto.FromHistoryDescribeWorkflowExecutionResponse(response), proto.FromError(err) } func (g GRPCHandler) GetCrossClusterTasks(ctx context.Context, request *historyv1.GetCrossClusterTasksRequest) (*historyv1.GetCrossClusterTasksResponse, error) { response, err := g.h.GetCrossClusterTasks(ctx, proto.ToHistoryGetCrossClusterTasksRequest(request)) return proto.FromHistoryGetCrossClusterTasksResponse(response), proto.FromError(err) } func (g GRPCHandler) GetDLQReplicationMessages(ctx context.Context, request *historyv1.GetDLQReplicationMessagesRequest) (*historyv1.GetDLQReplicationMessagesResponse, error) { response, err := g.h.GetDLQReplicationMessages(ctx, proto.ToHistoryGetDLQReplicationMessagesRequest(request)) return proto.FromHistoryGetDLQReplicationMessagesResponse(response), proto.FromError(err) } func (g GRPCHandler) GetFailoverInfo(ctx context.Context, request *historyv1.GetFailoverInfoRequest) (*historyv1.GetFailoverInfoResponse, error) { response, err := g.h.GetFailoverInfo(ctx, proto.ToHistoryGetFailoverInfoRequest(request)) return proto.FromHistoryGetFailoverInfoResponse(response), proto.FromError(err) } func (g GRPCHandler) GetMutableState(ctx context.Context, request *historyv1.GetMutableStateRequest) (*historyv1.GetMutableStateResponse, error) { response, err := g.h.GetMutableState(ctx, proto.ToHistoryGetMutableStateRequest(request)) return proto.FromHistoryGetMutableStateResponse(response), proto.FromError(err) } func (g GRPCHandler) GetReplicationMessages(ctx context.Context, request *historyv1.GetReplicationMessagesRequest) (*historyv1.GetReplicationMessagesResponse, error) { response, err := g.h.GetReplicationMessages(ctx, proto.ToHistoryGetReplicationMessagesRequest(request)) return proto.FromHistoryGetReplicationMessagesResponse(response), proto.FromError(err) } func (g GRPCHandler) MergeDLQMessages(ctx context.Context, request *historyv1.MergeDLQMessagesRequest) (*historyv1.MergeDLQMessagesResponse, error) { response, err := g.h.MergeDLQMessages(ctx, proto.ToHistoryMergeDLQMessagesRequest(request)) return proto.FromHistoryMergeDLQMessagesResponse(response), proto.FromError(err) } func (g GRPCHandler) NotifyFailoverMarkers(ctx context.Context, request *historyv1.NotifyFailoverMarkersRequest) (*historyv1.NotifyFailoverMarkersResponse, error) { err := g.h.NotifyFailoverMarkers(ctx, proto.ToHistoryNotifyFailoverMarkersRequest(request)) return &historyv1.NotifyFailoverMarkersResponse{}, proto.FromError(err) } func (g GRPCHandler) PollMutableState(ctx context.Context, request *historyv1.PollMutableStateRequest) (*historyv1.PollMutableStateResponse, error) { response, err := g.h.PollMutableState(ctx, proto.ToHistoryPollMutableStateRequest(request)) return proto.FromHistoryPollMutableStateResponse(response), proto.FromError(err) } func (g GRPCHandler) PurgeDLQMessages(ctx context.Context, request *historyv1.PurgeDLQMessagesRequest) (*historyv1.PurgeDLQMessagesResponse, error) { err := g.h.PurgeDLQMessages(ctx, proto.ToHistoryPurgeDLQMessagesRequest(request)) return &historyv1.PurgeDLQMessagesResponse{}, proto.FromError(err) } func (g GRPCHandler) QueryWorkflow(ctx context.Context, request *historyv1.QueryWorkflowRequest) (*historyv1.QueryWorkflowResponse, error) { response, err := g.h.QueryWorkflow(ctx, proto.ToHistoryQueryWorkflowRequest(request)) return proto.FromHistoryQueryWorkflowResponse(response), proto.FromError(err) } func (g GRPCHandler) RatelimitUpdate(ctx context.Context, request *historyv1.RatelimitUpdateRequest) (*historyv1.RatelimitUpdateResponse, error) { response, err := g.h.RatelimitUpdate(ctx, proto.ToHistoryRatelimitUpdateRequest(request)) return proto.FromHistoryRatelimitUpdateResponse(response), proto.FromError(err) } func (g GRPCHandler) ReadDLQMessages(ctx context.Context, request *historyv1.ReadDLQMessagesRequest) (*historyv1.ReadDLQMessagesResponse, error) { response, err := g.h.ReadDLQMessages(ctx, proto.ToHistoryReadDLQMessagesRequest(request)) return proto.FromHistoryReadDLQMessagesResponse(response), proto.FromError(err) } func (g GRPCHandler) ReapplyEvents(ctx context.Context, request *historyv1.ReapplyEventsRequest) (*historyv1.ReapplyEventsResponse, error) { err := g.h.ReapplyEvents(ctx, proto.ToHistoryReapplyEventsRequest(request)) return &historyv1.ReapplyEventsResponse{}, proto.FromError(err) } func (g GRPCHandler) RecordActivityTaskHeartbeat(ctx context.Context, request *historyv1.RecordActivityTaskHeartbeatRequest) (*historyv1.RecordActivityTaskHeartbeatResponse, error) { response, err := g.h.RecordActivityTaskHeartbeat(ctx, proto.ToHistoryRecordActivityTaskHeartbeatRequest(request)) return proto.FromHistoryRecordActivityTaskHeartbeatResponse(response), proto.FromError(err) } func (g GRPCHandler) RecordActivityTaskStarted(ctx context.Context, request *historyv1.RecordActivityTaskStartedRequest) (*historyv1.RecordActivityTaskStartedResponse, error) { response, err := g.h.RecordActivityTaskStarted(ctx, proto.ToHistoryRecordActivityTaskStartedRequest(request)) return proto.FromHistoryRecordActivityTaskStartedResponse(response), proto.FromError(err) } func (g GRPCHandler) RecordChildExecutionCompleted(ctx context.Context, request *historyv1.RecordChildExecutionCompletedRequest) (*historyv1.RecordChildExecutionCompletedResponse, error) { err := g.h.RecordChildExecutionCompleted(ctx, proto.ToHistoryRecordChildExecutionCompletedRequest(request)) return &historyv1.RecordChildExecutionCompletedResponse{}, proto.FromError(err) } func (g GRPCHandler) RecordDecisionTaskStarted(ctx context.Context, request *historyv1.RecordDecisionTaskStartedRequest) (*historyv1.RecordDecisionTaskStartedResponse, error) { response, err := g.h.RecordDecisionTaskStarted(ctx, proto.ToHistoryRecordDecisionTaskStartedRequest(request)) return proto.FromHistoryRecordDecisionTaskStartedResponse(response), proto.FromError(err) } func (g GRPCHandler) RefreshWorkflowTasks(ctx context.Context, request *historyv1.RefreshWorkflowTasksRequest) (*historyv1.RefreshWorkflowTasksResponse, error) { err := g.h.RefreshWorkflowTasks(ctx, proto.ToHistoryRefreshWorkflowTasksRequest(request)) return &historyv1.RefreshWorkflowTasksResponse{}, proto.FromError(err) } func (g GRPCHandler) RemoveSignalMutableState(ctx context.Context, request *historyv1.RemoveSignalMutableStateRequest) (*historyv1.RemoveSignalMutableStateResponse, error) { err := g.h.RemoveSignalMutableState(ctx, proto.ToHistoryRemoveSignalMutableStateRequest(request)) return &historyv1.RemoveSignalMutableStateResponse{}, proto.FromError(err) } func (g GRPCHandler) RemoveTask(ctx context.Context, request *historyv1.RemoveTaskRequest) (*historyv1.RemoveTaskResponse, error) { err := g.h.RemoveTask(ctx, proto.ToHistoryRemoveTaskRequest(request)) return &historyv1.RemoveTaskResponse{}, proto.FromError(err) } func (g GRPCHandler) ReplicateEventsV2(ctx context.Context, request *historyv1.ReplicateEventsV2Request) (*historyv1.ReplicateEventsV2Response, error) { err := g.h.ReplicateEventsV2(ctx, proto.ToHistoryReplicateEventsV2Request(request)) return &historyv1.ReplicateEventsV2Response{}, proto.FromError(err) } func (g GRPCHandler) RequestCancelWorkflowExecution(ctx context.Context, request *historyv1.RequestCancelWorkflowExecutionRequest) (*historyv1.RequestCancelWorkflowExecutionResponse, error) { err := g.h.RequestCancelWorkflowExecution(ctx, proto.ToHistoryRequestCancelWorkflowExecutionRequest(request)) return &historyv1.RequestCancelWorkflowExecutionResponse{}, proto.FromError(err) } func (g GRPCHandler) ResetQueue(ctx context.Context, request *historyv1.ResetQueueRequest) (*historyv1.ResetQueueResponse, error) { err := g.h.ResetQueue(ctx, proto.ToHistoryResetQueueRequest(request)) return &historyv1.ResetQueueResponse{}, proto.FromError(err) } func (g GRPCHandler) ResetStickyTaskList(ctx context.Context, request *historyv1.ResetStickyTaskListRequest) (*historyv1.ResetStickyTaskListResponse, error) { response, err := g.h.ResetStickyTaskList(ctx, proto.ToHistoryResetStickyTaskListRequest(request)) return proto.FromHistoryResetStickyTaskListResponse(response), proto.FromError(err) } func (g GRPCHandler) ResetWorkflowExecution(ctx context.Context, request *historyv1.ResetWorkflowExecutionRequest) (*historyv1.ResetWorkflowExecutionResponse, error) { response, err := g.h.ResetWorkflowExecution(ctx, proto.ToHistoryResetWorkflowExecutionRequest(request)) return proto.FromHistoryResetWorkflowExecutionResponse(response), proto.FromError(err) } func (g GRPCHandler) RespondActivityTaskCanceled(ctx context.Context, request *historyv1.RespondActivityTaskCanceledRequest) (*historyv1.RespondActivityTaskCanceledResponse, error) { err := g.h.RespondActivityTaskCanceled(ctx, proto.ToHistoryRespondActivityTaskCanceledRequest(request)) return &historyv1.RespondActivityTaskCanceledResponse{}, proto.FromError(err) } func (g GRPCHandler) RespondActivityTaskCompleted(ctx context.Context, request *historyv1.RespondActivityTaskCompletedRequest) (*historyv1.RespondActivityTaskCompletedResponse, error) { err := g.h.RespondActivityTaskCompleted(ctx, proto.ToHistoryRespondActivityTaskCompletedRequest(request)) return &historyv1.RespondActivityTaskCompletedResponse{}, proto.FromError(err) } func (g GRPCHandler) RespondActivityTaskFailed(ctx context.Context, request *historyv1.RespondActivityTaskFailedRequest) (*historyv1.RespondActivityTaskFailedResponse, error) { err := g.h.RespondActivityTaskFailed(ctx, proto.ToHistoryRespondActivityTaskFailedRequest(request)) return &historyv1.RespondActivityTaskFailedResponse{}, proto.FromError(err) } func (g GRPCHandler) RespondCrossClusterTasksCompleted(ctx context.Context, request *historyv1.RespondCrossClusterTasksCompletedRequest) (*historyv1.RespondCrossClusterTasksCompletedResponse, error) { response, err := g.h.RespondCrossClusterTasksCompleted(ctx, proto.ToHistoryRespondCrossClusterTasksCompletedRequest(request)) return proto.FromHistoryRespondCrossClusterTasksCompletedResponse(response), proto.FromError(err) } func (g GRPCHandler) RespondDecisionTaskCompleted(ctx context.Context, request *historyv1.RespondDecisionTaskCompletedRequest) (*historyv1.RespondDecisionTaskCompletedResponse, error) { response, err := g.h.RespondDecisionTaskCompleted(ctx, proto.ToHistoryRespondDecisionTaskCompletedRequest(request)) return proto.FromHistoryRespondDecisionTaskCompletedResponse(response), proto.FromError(err) } func (g GRPCHandler) RespondDecisionTaskFailed(ctx context.Context, request *historyv1.RespondDecisionTaskFailedRequest) (*historyv1.RespondDecisionTaskFailedResponse, error) { err := g.h.RespondDecisionTaskFailed(ctx, proto.ToHistoryRespondDecisionTaskFailedRequest(request)) return &historyv1.RespondDecisionTaskFailedResponse{}, proto.FromError(err) } func (g GRPCHandler) ScheduleDecisionTask(ctx context.Context, request *historyv1.ScheduleDecisionTaskRequest) (*historyv1.ScheduleDecisionTaskResponse, error) { err := g.h.ScheduleDecisionTask(ctx, proto.ToHistoryScheduleDecisionTaskRequest(request)) return &historyv1.ScheduleDecisionTaskResponse{}, proto.FromError(err) } func (g GRPCHandler) SignalWithStartWorkflowExecution(ctx context.Context, request *historyv1.SignalWithStartWorkflowExecutionRequest) (*historyv1.SignalWithStartWorkflowExecutionResponse, error) { response, err := g.h.SignalWithStartWorkflowExecution(ctx, proto.ToHistorySignalWithStartWorkflowExecutionRequest(request)) return proto.FromHistorySignalWithStartWorkflowExecutionResponse(response), proto.FromError(err) } func (g GRPCHandler) SignalWorkflowExecution(ctx context.Context, request *historyv1.SignalWorkflowExecutionRequest) (*historyv1.SignalWorkflowExecutionResponse, error) { err := g.h.SignalWorkflowExecution(ctx, proto.ToHistorySignalWorkflowExecutionRequest(request)) return &historyv1.SignalWorkflowExecutionResponse{}, proto.FromError(err) } func (g GRPCHandler) StartWorkflowExecution(ctx context.Context, request *historyv1.StartWorkflowExecutionRequest) (*historyv1.StartWorkflowExecutionResponse, error) { response, err := g.h.StartWorkflowExecution(ctx, proto.ToHistoryStartWorkflowExecutionRequest(request)) return proto.FromHistoryStartWorkflowExecutionResponse(response), proto.FromError(err) } func (g GRPCHandler) SyncActivity(ctx context.Context, request *historyv1.SyncActivityRequest) (*historyv1.SyncActivityResponse, error) { err := g.h.SyncActivity(ctx, proto.ToHistorySyncActivityRequest(request)) return &historyv1.SyncActivityResponse{}, proto.FromError(err) } func (g GRPCHandler) SyncShardStatus(ctx context.Context, request *historyv1.SyncShardStatusRequest) (*historyv1.SyncShardStatusResponse, error) { err := g.h.SyncShardStatus(ctx, proto.ToHistorySyncShardStatusRequest(request)) return &historyv1.SyncShardStatusResponse{}, proto.FromError(err) } func (g GRPCHandler) TerminateWorkflowExecution(ctx context.Context, request *historyv1.TerminateWorkflowExecutionRequest) (*historyv1.TerminateWorkflowExecutionResponse, error) { err := g.h.TerminateWorkflowExecution(ctx, proto.ToHistoryTerminateWorkflowExecutionRequest(request)) return &historyv1.TerminateWorkflowExecutionResponse{}, proto.FromError(err) } ================================================ FILE: service/history/wrappers/ratelimited/handler_generated.go ================================================ package ratelimited // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/ratelimited.tmpl // gowrap: http://github.com/hexdigest/gowrap //go:generate gowrap gen -p github.com/uber/cadence/service/history/handler -i Handler -t ../../templates/ratelimited.tmpl -o handler_generated.go -v handler=History -l "" import ( "context" "time" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/frontend/validate" "github.com/uber/cadence/service/history/handler" "github.com/uber/cadence/service/history/workflowcache" ) // historyHandler implements handler.Handler interface instrumented with rate limiter. type historyHandler struct { wrapped handler.Handler workflowIDCache workflowcache.WFCache logger log.Logger allowFunc func(domainID string, workflowID string) bool } // NewHistoryHandler creates a new instance of Handler with ratelimiter. func NewHistoryHandler( wrapped handler.Handler, workflowIDCache workflowcache.WFCache, logger log.Logger, ) handler.Handler { wrapper := &historyHandler{ wrapped: wrapped, workflowIDCache: workflowIDCache, logger: logger, } wrapper.allowFunc = wrapper.allowWfID return wrapper } func (h *historyHandler) CloseShard(ctx context.Context, cp1 *types.CloseShardRequest) (err error) { return h.wrapped.CloseShard(ctx, cp1) } func (h *historyHandler) CountDLQMessages(ctx context.Context, cp1 *types.CountDLQMessagesRequest) (hp1 *types.HistoryCountDLQMessagesResponse, err error) { return h.wrapped.CountDLQMessages(ctx, cp1) } func (h *historyHandler) DescribeHistoryHost(ctx context.Context, dp1 *types.DescribeHistoryHostRequest) (dp2 *types.DescribeHistoryHostResponse, err error) { return h.wrapped.DescribeHistoryHost(ctx, dp1) } func (h *historyHandler) DescribeMutableState(ctx context.Context, dp1 *types.DescribeMutableStateRequest) (dp2 *types.DescribeMutableStateResponse, err error) { return h.wrapped.DescribeMutableState(ctx, dp1) } func (h *historyHandler) DescribeQueue(ctx context.Context, dp1 *types.DescribeQueueRequest) (dp2 *types.DescribeQueueResponse, err error) { return h.wrapped.DescribeQueue(ctx, dp1) } func (h *historyHandler) DescribeWorkflowExecution(ctx context.Context, hp1 *types.HistoryDescribeWorkflowExecutionRequest) (dp1 *types.DescribeWorkflowExecutionResponse, err error) { if hp1 == nil { err = validate.ErrRequestNotSet return } if hp1.GetDomainUUID() == "" { err = validate.ErrDomainNotSet return } if hp1.Request.GetExecution().GetWorkflowID() == "" { err = validate.ErrWorkflowIDNotSet return } if !h.allowFunc(hp1.GetDomainUUID(), hp1.Request.GetExecution().GetWorkflowID()) { err = &types.ServiceBusyError{ Message: "Too many requests for the workflow ID", Reason: constants.WorkflowIDRateLimitReason, } return } return h.wrapped.DescribeWorkflowExecution(ctx, hp1) } func (h *historyHandler) GetCrossClusterTasks(ctx context.Context, gp1 *types.GetCrossClusterTasksRequest) (gp2 *types.GetCrossClusterTasksResponse, err error) { return h.wrapped.GetCrossClusterTasks(ctx, gp1) } func (h *historyHandler) GetDLQReplicationMessages(ctx context.Context, gp1 *types.GetDLQReplicationMessagesRequest) (gp2 *types.GetDLQReplicationMessagesResponse, err error) { return h.wrapped.GetDLQReplicationMessages(ctx, gp1) } func (h *historyHandler) GetFailoverInfo(ctx context.Context, gp1 *types.GetFailoverInfoRequest) (gp2 *types.GetFailoverInfoResponse, err error) { return h.wrapped.GetFailoverInfo(ctx, gp1) } func (h *historyHandler) GetMutableState(ctx context.Context, gp1 *types.GetMutableStateRequest) (gp2 *types.GetMutableStateResponse, err error) { return h.wrapped.GetMutableState(ctx, gp1) } func (h *historyHandler) GetReplicationMessages(ctx context.Context, gp1 *types.GetReplicationMessagesRequest) (gp2 *types.GetReplicationMessagesResponse, err error) { return h.wrapped.GetReplicationMessages(ctx, gp1) } func (h *historyHandler) Health(ctx context.Context) (hp1 *types.HealthStatus, err error) { return h.wrapped.Health(ctx) } func (h *historyHandler) MergeDLQMessages(ctx context.Context, mp1 *types.MergeDLQMessagesRequest) (mp2 *types.MergeDLQMessagesResponse, err error) { return h.wrapped.MergeDLQMessages(ctx, mp1) } func (h *historyHandler) NotifyFailoverMarkers(ctx context.Context, np1 *types.NotifyFailoverMarkersRequest) (err error) { return h.wrapped.NotifyFailoverMarkers(ctx, np1) } func (h *historyHandler) PollMutableState(ctx context.Context, pp1 *types.PollMutableStateRequest) (pp2 *types.PollMutableStateResponse, err error) { return h.wrapped.PollMutableState(ctx, pp1) } func (h *historyHandler) PrepareToStop(d1 time.Duration) (d2 time.Duration) { return h.wrapped.PrepareToStop(d1) } func (h *historyHandler) PurgeDLQMessages(ctx context.Context, pp1 *types.PurgeDLQMessagesRequest) (err error) { return h.wrapped.PurgeDLQMessages(ctx, pp1) } func (h *historyHandler) QueryWorkflow(ctx context.Context, hp1 *types.HistoryQueryWorkflowRequest) (hp2 *types.HistoryQueryWorkflowResponse, err error) { return h.wrapped.QueryWorkflow(ctx, hp1) } func (h *historyHandler) RatelimitUpdate(ctx context.Context, rp1 *types.RatelimitUpdateRequest) (rp2 *types.RatelimitUpdateResponse, err error) { return h.wrapped.RatelimitUpdate(ctx, rp1) } func (h *historyHandler) ReadDLQMessages(ctx context.Context, rp1 *types.ReadDLQMessagesRequest) (rp2 *types.ReadDLQMessagesResponse, err error) { return h.wrapped.ReadDLQMessages(ctx, rp1) } func (h *historyHandler) ReapplyEvents(ctx context.Context, hp1 *types.HistoryReapplyEventsRequest) (err error) { return h.wrapped.ReapplyEvents(ctx, hp1) } func (h *historyHandler) RecordActivityTaskHeartbeat(ctx context.Context, hp1 *types.HistoryRecordActivityTaskHeartbeatRequest) (rp1 *types.RecordActivityTaskHeartbeatResponse, err error) { return h.wrapped.RecordActivityTaskHeartbeat(ctx, hp1) } func (h *historyHandler) RecordActivityTaskStarted(ctx context.Context, rp1 *types.RecordActivityTaskStartedRequest) (rp2 *types.RecordActivityTaskStartedResponse, err error) { return h.wrapped.RecordActivityTaskStarted(ctx, rp1) } func (h *historyHandler) RecordChildExecutionCompleted(ctx context.Context, rp1 *types.RecordChildExecutionCompletedRequest) (err error) { return h.wrapped.RecordChildExecutionCompleted(ctx, rp1) } func (h *historyHandler) RecordDecisionTaskStarted(ctx context.Context, rp1 *types.RecordDecisionTaskStartedRequest) (rp2 *types.RecordDecisionTaskStartedResponse, err error) { return h.wrapped.RecordDecisionTaskStarted(ctx, rp1) } func (h *historyHandler) RefreshWorkflowTasks(ctx context.Context, hp1 *types.HistoryRefreshWorkflowTasksRequest) (err error) { return h.wrapped.RefreshWorkflowTasks(ctx, hp1) } func (h *historyHandler) RemoveSignalMutableState(ctx context.Context, rp1 *types.RemoveSignalMutableStateRequest) (err error) { return h.wrapped.RemoveSignalMutableState(ctx, rp1) } func (h *historyHandler) RemoveTask(ctx context.Context, rp1 *types.RemoveTaskRequest) (err error) { return h.wrapped.RemoveTask(ctx, rp1) } func (h *historyHandler) ReplicateEventsV2(ctx context.Context, rp1 *types.ReplicateEventsV2Request) (err error) { return h.wrapped.ReplicateEventsV2(ctx, rp1) } func (h *historyHandler) RequestCancelWorkflowExecution(ctx context.Context, hp1 *types.HistoryRequestCancelWorkflowExecutionRequest) (err error) { return h.wrapped.RequestCancelWorkflowExecution(ctx, hp1) } func (h *historyHandler) ResetQueue(ctx context.Context, rp1 *types.ResetQueueRequest) (err error) { return h.wrapped.ResetQueue(ctx, rp1) } func (h *historyHandler) ResetStickyTaskList(ctx context.Context, hp1 *types.HistoryResetStickyTaskListRequest) (hp2 *types.HistoryResetStickyTaskListResponse, err error) { return h.wrapped.ResetStickyTaskList(ctx, hp1) } func (h *historyHandler) ResetWorkflowExecution(ctx context.Context, hp1 *types.HistoryResetWorkflowExecutionRequest) (rp1 *types.ResetWorkflowExecutionResponse, err error) { return h.wrapped.ResetWorkflowExecution(ctx, hp1) } func (h *historyHandler) RespondActivityTaskCanceled(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCanceledRequest) (err error) { return h.wrapped.RespondActivityTaskCanceled(ctx, hp1) } func (h *historyHandler) RespondActivityTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondActivityTaskCompletedRequest) (err error) { return h.wrapped.RespondActivityTaskCompleted(ctx, hp1) } func (h *historyHandler) RespondActivityTaskFailed(ctx context.Context, hp1 *types.HistoryRespondActivityTaskFailedRequest) (err error) { return h.wrapped.RespondActivityTaskFailed(ctx, hp1) } func (h *historyHandler) RespondCrossClusterTasksCompleted(ctx context.Context, rp1 *types.RespondCrossClusterTasksCompletedRequest) (rp2 *types.RespondCrossClusterTasksCompletedResponse, err error) { return h.wrapped.RespondCrossClusterTasksCompleted(ctx, rp1) } func (h *historyHandler) RespondDecisionTaskCompleted(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskCompletedRequest) (hp2 *types.HistoryRespondDecisionTaskCompletedResponse, err error) { return h.wrapped.RespondDecisionTaskCompleted(ctx, hp1) } func (h *historyHandler) RespondDecisionTaskFailed(ctx context.Context, hp1 *types.HistoryRespondDecisionTaskFailedRequest) (err error) { return h.wrapped.RespondDecisionTaskFailed(ctx, hp1) } func (h *historyHandler) ScheduleDecisionTask(ctx context.Context, sp1 *types.ScheduleDecisionTaskRequest) (err error) { return h.wrapped.ScheduleDecisionTask(ctx, sp1) } func (h *historyHandler) SignalWithStartWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWithStartWorkflowExecutionRequest) (sp1 *types.StartWorkflowExecutionResponse, err error) { if hp1 == nil { err = validate.ErrRequestNotSet return } if hp1.GetDomainUUID() == "" { err = validate.ErrDomainNotSet return } if hp1.SignalWithStartRequest.GetWorkflowID() == "" { err = validate.ErrWorkflowIDNotSet return } if !h.allowFunc(hp1.GetDomainUUID(), hp1.SignalWithStartRequest.GetWorkflowID()) { err = &types.ServiceBusyError{ Message: "Too many requests for the workflow ID", Reason: constants.WorkflowIDRateLimitReason, } return } return h.wrapped.SignalWithStartWorkflowExecution(ctx, hp1) } func (h *historyHandler) SignalWorkflowExecution(ctx context.Context, hp1 *types.HistorySignalWorkflowExecutionRequest) (err error) { if hp1 == nil { err = validate.ErrRequestNotSet return } if hp1.GetDomainUUID() == "" { err = validate.ErrDomainNotSet return } if hp1.SignalRequest.GetWorkflowExecution().GetWorkflowID() == "" { err = validate.ErrWorkflowIDNotSet return } if !h.allowFunc(hp1.GetDomainUUID(), hp1.SignalRequest.GetWorkflowExecution().GetWorkflowID()) { err = &types.ServiceBusyError{ Message: "Too many requests for the workflow ID", Reason: constants.WorkflowIDRateLimitReason, } return } return h.wrapped.SignalWorkflowExecution(ctx, hp1) } func (h *historyHandler) Start() { h.wrapped.Start() return } func (h *historyHandler) StartWorkflowExecution(ctx context.Context, hp1 *types.HistoryStartWorkflowExecutionRequest) (sp1 *types.StartWorkflowExecutionResponse, err error) { if hp1 == nil { err = validate.ErrRequestNotSet return } if hp1.GetDomainUUID() == "" { err = validate.ErrDomainNotSet return } if hp1.StartRequest.GetWorkflowID() == "" { err = validate.ErrWorkflowIDNotSet return } if !h.allowFunc(hp1.GetDomainUUID(), hp1.StartRequest.GetWorkflowID()) { err = &types.ServiceBusyError{ Message: "Too many requests for the workflow ID", Reason: constants.WorkflowIDRateLimitReason, } return } return h.wrapped.StartWorkflowExecution(ctx, hp1) } func (h *historyHandler) Stop() { h.wrapped.Stop() return } func (h *historyHandler) SyncActivity(ctx context.Context, sp1 *types.SyncActivityRequest) (err error) { return h.wrapped.SyncActivity(ctx, sp1) } func (h *historyHandler) SyncShardStatus(ctx context.Context, sp1 *types.SyncShardStatusRequest) (err error) { return h.wrapped.SyncShardStatus(ctx, sp1) } func (h *historyHandler) TerminateWorkflowExecution(ctx context.Context, hp1 *types.HistoryTerminateWorkflowExecutionRequest) (err error) { return h.wrapped.TerminateWorkflowExecution(ctx, hp1) } ================================================ FILE: service/history/wrappers/ratelimited/handler_generated_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ratelimited import ( "context" "fmt" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/handler" ) const ( testDomainID = "test-domain-id" testWorkflowID = "test-workflow-id" testDomainName = "test-domain-name" ) func TestRatelimitedEndpoints_Table(t *testing.T) { controller := gomock.NewController(t) handlerMock := handler.NewMockHandler(controller) wrapper := NewHistoryHandler(handlerMock, nil, log.NewNoop()) // We define the calls that should be ratelimited limitedCalls := []struct { name string // Defines how to call the wrapper function (correct request type, and call) callWrapper func() (interface{}, error) // Defines the expected call to the wrapped handler (what to call if the call is not ratelimited) expectCallToEndpoint func() }{ { name: "StartWorkflowExecution", callWrapper: func() (interface{}, error) { startRequest := &types.HistoryStartWorkflowExecutionRequest{ DomainUUID: testDomainID, StartRequest: &types.StartWorkflowExecutionRequest{WorkflowID: testWorkflowID}, } return wrapper.StartWorkflowExecution(context.Background(), startRequest) }, expectCallToEndpoint: func() { handlerMock.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, }, { name: "SignalWithStartWorkflowExecution", callWrapper: func() (interface{}, error) { signalWithStartRequest := &types.HistorySignalWithStartWorkflowExecutionRequest{ DomainUUID: testDomainID, SignalWithStartRequest: &types.SignalWithStartWorkflowExecutionRequest{WorkflowID: testWorkflowID}, } return wrapper.SignalWithStartWorkflowExecution(context.Background(), signalWithStartRequest) }, expectCallToEndpoint: func() { handlerMock.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, }, { name: "SignalWorkflowExecution", callWrapper: func() (interface{}, error) { signalRequest := &types.HistorySignalWorkflowExecutionRequest{ DomainUUID: testDomainID, SignalRequest: &types.SignalWorkflowExecutionRequest{ WorkflowExecution: &types.WorkflowExecution{WorkflowID: testWorkflowID}, }, } return nil, wrapper.SignalWorkflowExecution(context.Background(), signalRequest) }, expectCallToEndpoint: func() { handlerMock.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, }, { name: "DescribeWorkflowExecution", callWrapper: func() (interface{}, error) { describeRequest := &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: testDomainID, Request: &types.DescribeWorkflowExecutionRequest{ Execution: &types.WorkflowExecution{WorkflowID: testWorkflowID}, }, } return wrapper.DescribeWorkflowExecution(context.Background(), describeRequest) }, expectCallToEndpoint: func() { handlerMock.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) }, }, } for _, endpoint := range limitedCalls { t.Run(fmt.Sprintf("%s, %s", endpoint.name, "not limited"), func(t *testing.T) { wrapper.(*historyHandler).allowFunc = func(string, string) bool { return true } endpoint.expectCallToEndpoint() _, err := endpoint.callWrapper() assert.NoError(t, err) }) t.Run(fmt.Sprintf("%s, %s", endpoint.name, "limited"), func(t *testing.T) { wrapper.(*historyHandler).allowFunc = func(string, string) bool { return false } _, err := endpoint.callWrapper() var sbErr *types.ServiceBusyError assert.ErrorAs(t, err, &sbErr) assert.ErrorContains(t, err, "Too many requests for the workflow ID") assert.Equal(t, constants.WorkflowIDRateLimitReason, sbErr.Reason) }) } } ================================================ FILE: service/history/wrappers/ratelimited/ratelimit.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ratelimited func (h *historyHandler) allowWfID(domainUUID, workflowID string) bool { return h.workflowIDCache.AllowExternal(domainUUID, workflowID) } ================================================ FILE: service/history/wrappers/ratelimited/ratelimit_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package ratelimited import ( "fmt" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/service/history/workflowcache" ) func TestAllowWfID(t *testing.T) { tests := []struct { workflowIDCacheAllow bool expected bool }{ { workflowIDCacheAllow: true, expected: true, }, { workflowIDCacheAllow: false, expected: false, }, } for _, tt := range tests { t.Run(fmt.Sprintf("workflowIDCacheAllow: %t", tt.workflowIDCacheAllow), func(t *testing.T) { ctrl := gomock.NewController(t) workflowIDCacheMock := workflowcache.NewMockWFCache(ctrl) workflowIDCacheMock.EXPECT().AllowExternal(testDomainID, testWorkflowID).Return(tt.workflowIDCacheAllow).Times(1) h := &historyHandler{ workflowIDCache: workflowIDCacheMock, logger: log.NewNoop(), } got := h.allowWfID(testDomainID, testWorkflowID) assert.Equal(t, tt.expected, got) }) } } ================================================ FILE: service/history/wrappers/thrift/thrift_handler.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/.gen/go/health" "github.com/uber/cadence/.gen/go/health/metaserver" "github.com/uber/cadence/.gen/go/history/historyserviceserver" "github.com/uber/cadence/common/types/mapper/thrift" "github.com/uber/cadence/service/history/handler" ) type ThriftHandler struct { h handler.Handler } func NewThriftHandler(h handler.Handler) ThriftHandler { return ThriftHandler{h} } func (t ThriftHandler) Register(dispatcher *yarpc.Dispatcher) { dispatcher.Register(historyserviceserver.New(&t)) dispatcher.Register(metaserver.New(&t)) } func (t ThriftHandler) Health(ctx context.Context) (*health.HealthStatus, error) { response, err := t.h.Health(ctx) return thrift.FromHealthStatus(response), thrift.FromError(err) } ================================================ FILE: service/history/wrappers/thrift/thrift_handler_generated.go ================================================ package thrift // Code generated by gowrap. DO NOT EDIT. // template: ../../../templates/thrift.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/.gen/go/history" "github.com/uber/cadence/.gen/go/replicator" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/types/mapper/thrift" ) func (g ThriftHandler) CloseShard(ctx context.Context, Request *shared.CloseShardRequest) (err error) { err = g.h.CloseShard(ctx, thrift.ToHistoryCloseShardRequest(Request)) return thrift.FromError(err) } func (g ThriftHandler) DescribeHistoryHost(ctx context.Context, Request *shared.DescribeHistoryHostRequest) (dp1 *shared.DescribeHistoryHostResponse, err error) { response, err := g.h.DescribeHistoryHost(ctx, thrift.ToHistoryDescribeHistoryHostRequest(Request)) return thrift.FromHistoryDescribeHistoryHostResponse(response), thrift.FromError(err) } func (g ThriftHandler) DescribeMutableState(ctx context.Context, Request *history.DescribeMutableStateRequest) (dp1 *history.DescribeMutableStateResponse, err error) { response, err := g.h.DescribeMutableState(ctx, thrift.ToHistoryDescribeMutableStateRequest(Request)) return thrift.FromHistoryDescribeMutableStateResponse(response), thrift.FromError(err) } func (g ThriftHandler) DescribeQueue(ctx context.Context, Request *shared.DescribeQueueRequest) (dp1 *shared.DescribeQueueResponse, err error) { response, err := g.h.DescribeQueue(ctx, thrift.ToHistoryDescribeQueueRequest(Request)) return thrift.FromHistoryDescribeQueueResponse(response), thrift.FromError(err) } func (g ThriftHandler) DescribeWorkflowExecution(ctx context.Context, DescribeRequest *history.DescribeWorkflowExecutionRequest) (dp1 *shared.DescribeWorkflowExecutionResponse, err error) { response, err := g.h.DescribeWorkflowExecution(ctx, thrift.ToHistoryDescribeWorkflowExecutionRequest(DescribeRequest)) return thrift.FromHistoryDescribeWorkflowExecutionResponse(response), thrift.FromError(err) } func (g ThriftHandler) GetCrossClusterTasks(ctx context.Context, Request *shared.GetCrossClusterTasksRequest) (gp1 *shared.GetCrossClusterTasksResponse, err error) { response, err := g.h.GetCrossClusterTasks(ctx, thrift.ToHistoryGetCrossClusterTasksRequest(Request)) return thrift.FromHistoryGetCrossClusterTasksResponse(response), thrift.FromError(err) } func (g ThriftHandler) GetDLQReplicationMessages(ctx context.Context, Request *replicator.GetDLQReplicationMessagesRequest) (gp1 *replicator.GetDLQReplicationMessagesResponse, err error) { response, err := g.h.GetDLQReplicationMessages(ctx, thrift.ToHistoryGetDLQReplicationMessagesRequest(Request)) return thrift.FromHistoryGetDLQReplicationMessagesResponse(response), thrift.FromError(err) } func (g ThriftHandler) GetFailoverInfo(ctx context.Context, Request *history.GetFailoverInfoRequest) (gp1 *history.GetFailoverInfoResponse, err error) { response, err := g.h.GetFailoverInfo(ctx, thrift.ToHistoryGetFailoverInfoRequest(Request)) return thrift.FromHistoryGetFailoverInfoResponse(response), thrift.FromError(err) } func (g ThriftHandler) GetMutableState(ctx context.Context, GetRequest *history.GetMutableStateRequest) (gp1 *history.GetMutableStateResponse, err error) { response, err := g.h.GetMutableState(ctx, thrift.ToHistoryGetMutableStateRequest(GetRequest)) return thrift.FromHistoryGetMutableStateResponse(response), thrift.FromError(err) } func (g ThriftHandler) GetReplicationMessages(ctx context.Context, Request *replicator.GetReplicationMessagesRequest) (gp1 *replicator.GetReplicationMessagesResponse, err error) { response, err := g.h.GetReplicationMessages(ctx, thrift.ToHistoryGetReplicationMessagesRequest(Request)) return thrift.FromHistoryGetReplicationMessagesResponse(response), thrift.FromError(err) } func (g ThriftHandler) MergeDLQMessages(ctx context.Context, Request *replicator.MergeDLQMessagesRequest) (mp1 *replicator.MergeDLQMessagesResponse, err error) { response, err := g.h.MergeDLQMessages(ctx, thrift.ToHistoryMergeDLQMessagesRequest(Request)) return thrift.FromHistoryMergeDLQMessagesResponse(response), thrift.FromError(err) } func (g ThriftHandler) NotifyFailoverMarkers(ctx context.Context, Request *history.NotifyFailoverMarkersRequest) (err error) { err = g.h.NotifyFailoverMarkers(ctx, thrift.ToHistoryNotifyFailoverMarkersRequest(Request)) return thrift.FromError(err) } func (g ThriftHandler) PollMutableState(ctx context.Context, PollRequest *history.PollMutableStateRequest) (pp1 *history.PollMutableStateResponse, err error) { response, err := g.h.PollMutableState(ctx, thrift.ToHistoryPollMutableStateRequest(PollRequest)) return thrift.FromHistoryPollMutableStateResponse(response), thrift.FromError(err) } func (g ThriftHandler) PurgeDLQMessages(ctx context.Context, Request *replicator.PurgeDLQMessagesRequest) (err error) { err = g.h.PurgeDLQMessages(ctx, thrift.ToHistoryPurgeDLQMessagesRequest(Request)) return thrift.FromError(err) } func (g ThriftHandler) QueryWorkflow(ctx context.Context, QueryRequest *history.QueryWorkflowRequest) (qp1 *history.QueryWorkflowResponse, err error) { response, err := g.h.QueryWorkflow(ctx, thrift.ToHistoryQueryWorkflowRequest(QueryRequest)) return thrift.FromHistoryQueryWorkflowResponse(response), thrift.FromError(err) } func (g ThriftHandler) RatelimitUpdate(ctx context.Context, Request *history.RatelimitUpdateRequest) (rp1 *history.RatelimitUpdateResponse, err error) { response, err := g.h.RatelimitUpdate(ctx, thrift.ToHistoryRatelimitUpdateRequest(Request)) return thrift.FromHistoryRatelimitUpdateResponse(response), thrift.FromError(err) } func (g ThriftHandler) ReadDLQMessages(ctx context.Context, Request *replicator.ReadDLQMessagesRequest) (rp1 *replicator.ReadDLQMessagesResponse, err error) { response, err := g.h.ReadDLQMessages(ctx, thrift.ToHistoryReadDLQMessagesRequest(Request)) return thrift.FromHistoryReadDLQMessagesResponse(response), thrift.FromError(err) } func (g ThriftHandler) ReapplyEvents(ctx context.Context, ReapplyEventsRequest *history.ReapplyEventsRequest) (err error) { err = g.h.ReapplyEvents(ctx, thrift.ToHistoryReapplyEventsRequest(ReapplyEventsRequest)) return thrift.FromError(err) } func (g ThriftHandler) RecordActivityTaskHeartbeat(ctx context.Context, HeartbeatRequest *history.RecordActivityTaskHeartbeatRequest) (rp1 *shared.RecordActivityTaskHeartbeatResponse, err error) { response, err := g.h.RecordActivityTaskHeartbeat(ctx, thrift.ToHistoryRecordActivityTaskHeartbeatRequest(HeartbeatRequest)) return thrift.FromHistoryRecordActivityTaskHeartbeatResponse(response), thrift.FromError(err) } func (g ThriftHandler) RecordActivityTaskStarted(ctx context.Context, AddRequest *history.RecordActivityTaskStartedRequest) (rp1 *history.RecordActivityTaskStartedResponse, err error) { response, err := g.h.RecordActivityTaskStarted(ctx, thrift.ToHistoryRecordActivityTaskStartedRequest(AddRequest)) return thrift.FromHistoryRecordActivityTaskStartedResponse(response), thrift.FromError(err) } func (g ThriftHandler) RecordChildExecutionCompleted(ctx context.Context, CompletionRequest *history.RecordChildExecutionCompletedRequest) (err error) { err = g.h.RecordChildExecutionCompleted(ctx, thrift.ToHistoryRecordChildExecutionCompletedRequest(CompletionRequest)) return thrift.FromError(err) } func (g ThriftHandler) RecordDecisionTaskStarted(ctx context.Context, AddRequest *history.RecordDecisionTaskStartedRequest) (rp1 *history.RecordDecisionTaskStartedResponse, err error) { response, err := g.h.RecordDecisionTaskStarted(ctx, thrift.ToHistoryRecordDecisionTaskStartedRequest(AddRequest)) return thrift.FromHistoryRecordDecisionTaskStartedResponse(response), thrift.FromError(err) } func (g ThriftHandler) RefreshWorkflowTasks(ctx context.Context, Request *history.RefreshWorkflowTasksRequest) (err error) { err = g.h.RefreshWorkflowTasks(ctx, thrift.ToHistoryRefreshWorkflowTasksRequest(Request)) return thrift.FromError(err) } func (g ThriftHandler) RemoveSignalMutableState(ctx context.Context, RemoveRequest *history.RemoveSignalMutableStateRequest) (err error) { err = g.h.RemoveSignalMutableState(ctx, thrift.ToHistoryRemoveSignalMutableStateRequest(RemoveRequest)) return thrift.FromError(err) } func (g ThriftHandler) RemoveTask(ctx context.Context, Request *shared.RemoveTaskRequest) (err error) { err = g.h.RemoveTask(ctx, thrift.ToHistoryRemoveTaskRequest(Request)) return thrift.FromError(err) } func (g ThriftHandler) ReplicateEventsV2(ctx context.Context, ReplicateV2Request *history.ReplicateEventsV2Request) (err error) { err = g.h.ReplicateEventsV2(ctx, thrift.ToHistoryReplicateEventsV2Request(ReplicateV2Request)) return thrift.FromError(err) } func (g ThriftHandler) RequestCancelWorkflowExecution(ctx context.Context, CancelRequest *history.RequestCancelWorkflowExecutionRequest) (err error) { err = g.h.RequestCancelWorkflowExecution(ctx, thrift.ToHistoryRequestCancelWorkflowExecutionRequest(CancelRequest)) return thrift.FromError(err) } func (g ThriftHandler) ResetQueue(ctx context.Context, Request *shared.ResetQueueRequest) (err error) { err = g.h.ResetQueue(ctx, thrift.ToHistoryResetQueueRequest(Request)) return thrift.FromError(err) } func (g ThriftHandler) ResetStickyTaskList(ctx context.Context, ResetRequest *history.ResetStickyTaskListRequest) (rp1 *history.ResetStickyTaskListResponse, err error) { response, err := g.h.ResetStickyTaskList(ctx, thrift.ToHistoryResetStickyTaskListRequest(ResetRequest)) return thrift.FromHistoryResetStickyTaskListResponse(response), thrift.FromError(err) } func (g ThriftHandler) ResetWorkflowExecution(ctx context.Context, ResetRequest *history.ResetWorkflowExecutionRequest) (rp1 *shared.ResetWorkflowExecutionResponse, err error) { response, err := g.h.ResetWorkflowExecution(ctx, thrift.ToHistoryResetWorkflowExecutionRequest(ResetRequest)) return thrift.FromHistoryResetWorkflowExecutionResponse(response), thrift.FromError(err) } func (g ThriftHandler) RespondActivityTaskCanceled(ctx context.Context, CanceledRequest *history.RespondActivityTaskCanceledRequest) (err error) { err = g.h.RespondActivityTaskCanceled(ctx, thrift.ToHistoryRespondActivityTaskCanceledRequest(CanceledRequest)) return thrift.FromError(err) } func (g ThriftHandler) RespondActivityTaskCompleted(ctx context.Context, CompleteRequest *history.RespondActivityTaskCompletedRequest) (err error) { err = g.h.RespondActivityTaskCompleted(ctx, thrift.ToHistoryRespondActivityTaskCompletedRequest(CompleteRequest)) return thrift.FromError(err) } func (g ThriftHandler) RespondActivityTaskFailed(ctx context.Context, FailRequest *history.RespondActivityTaskFailedRequest) (err error) { err = g.h.RespondActivityTaskFailed(ctx, thrift.ToHistoryRespondActivityTaskFailedRequest(FailRequest)) return thrift.FromError(err) } func (g ThriftHandler) RespondCrossClusterTasksCompleted(ctx context.Context, Request *shared.RespondCrossClusterTasksCompletedRequest) (rp1 *shared.RespondCrossClusterTasksCompletedResponse, err error) { response, err := g.h.RespondCrossClusterTasksCompleted(ctx, thrift.ToHistoryRespondCrossClusterTasksCompletedRequest(Request)) return thrift.FromHistoryRespondCrossClusterTasksCompletedResponse(response), thrift.FromError(err) } func (g ThriftHandler) RespondDecisionTaskCompleted(ctx context.Context, CompleteRequest *history.RespondDecisionTaskCompletedRequest) (rp1 *history.RespondDecisionTaskCompletedResponse, err error) { response, err := g.h.RespondDecisionTaskCompleted(ctx, thrift.ToHistoryRespondDecisionTaskCompletedRequest(CompleteRequest)) return thrift.FromHistoryRespondDecisionTaskCompletedResponse(response), thrift.FromError(err) } func (g ThriftHandler) RespondDecisionTaskFailed(ctx context.Context, FailedRequest *history.RespondDecisionTaskFailedRequest) (err error) { err = g.h.RespondDecisionTaskFailed(ctx, thrift.ToHistoryRespondDecisionTaskFailedRequest(FailedRequest)) return thrift.FromError(err) } func (g ThriftHandler) ScheduleDecisionTask(ctx context.Context, ScheduleRequest *history.ScheduleDecisionTaskRequest) (err error) { err = g.h.ScheduleDecisionTask(ctx, thrift.ToHistoryScheduleDecisionTaskRequest(ScheduleRequest)) return thrift.FromError(err) } func (g ThriftHandler) SignalWithStartWorkflowExecution(ctx context.Context, SignalWithStartRequest *history.SignalWithStartWorkflowExecutionRequest) (sp1 *shared.StartWorkflowExecutionResponse, err error) { response, err := g.h.SignalWithStartWorkflowExecution(ctx, thrift.ToHistorySignalWithStartWorkflowExecutionRequest(SignalWithStartRequest)) return thrift.FromHistorySignalWithStartWorkflowExecutionResponse(response), thrift.FromError(err) } func (g ThriftHandler) SignalWorkflowExecution(ctx context.Context, SignalRequest *history.SignalWorkflowExecutionRequest) (err error) { err = g.h.SignalWorkflowExecution(ctx, thrift.ToHistorySignalWorkflowExecutionRequest(SignalRequest)) return thrift.FromError(err) } func (g ThriftHandler) StartWorkflowExecution(ctx context.Context, StartRequest *history.StartWorkflowExecutionRequest) (sp1 *shared.StartWorkflowExecutionResponse, err error) { response, err := g.h.StartWorkflowExecution(ctx, thrift.ToHistoryStartWorkflowExecutionRequest(StartRequest)) return thrift.FromHistoryStartWorkflowExecutionResponse(response), thrift.FromError(err) } func (g ThriftHandler) SyncActivity(ctx context.Context, SyncActivityRequest *history.SyncActivityRequest) (err error) { err = g.h.SyncActivity(ctx, thrift.ToHistorySyncActivityRequest(SyncActivityRequest)) return thrift.FromError(err) } func (g ThriftHandler) SyncShardStatus(ctx context.Context, SyncShardStatusRequest *history.SyncShardStatusRequest) (err error) { err = g.h.SyncShardStatus(ctx, thrift.ToHistorySyncShardStatusRequest(SyncShardStatusRequest)) return thrift.FromError(err) } func (g ThriftHandler) TerminateWorkflowExecution(ctx context.Context, TerminateRequest *history.TerminateWorkflowExecutionRequest) (err error) { err = g.h.TerminateWorkflowExecution(ctx, thrift.ToHistoryTerminateWorkflowExecutionRequest(TerminateRequest)) return thrift.FromError(err) } ================================================ FILE: service/history/wrappers/thrift/thrift_handler_test.go ================================================ // Copyright (c) 2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package thrift import ( "context" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/.gen/go/health" hist "github.com/uber/cadence/.gen/go/history" "github.com/uber/cadence/.gen/go/replicator" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/handler" ) func TestThriftHandler(t *testing.T) { ctrl := gomock.NewController(t) h := handler.NewMockHandler(ctrl) th := NewThriftHandler(h) ctx := context.Background() internalErr := &types.InternalServiceError{Message: "test"} expectedErr := &shared.InternalServiceError{Message: "test"} t.Run("Health", func(t *testing.T) { h.EXPECT().Health(ctx).Return(&types.HealthStatus{}, internalErr).Times(1) resp, err := th.Health(ctx) assert.Equal(t, health.HealthStatus{Msg: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("CloseShard", func(t *testing.T) { h.EXPECT().CloseShard(ctx, &types.CloseShardRequest{}).Return(internalErr).Times(1) err := th.CloseShard(ctx, &shared.CloseShardRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("DescribeHistoryHost", func(t *testing.T) { h.EXPECT().DescribeHistoryHost(ctx, &types.DescribeHistoryHostRequest{}).Return(&types.DescribeHistoryHostResponse{}, internalErr).Times(1) resp, err := th.DescribeHistoryHost(ctx, &shared.DescribeHistoryHostRequest{}) assert.Equal(t, shared.DescribeHistoryHostResponse{NumberOfShards: common.Int32Ptr(0), ShardControllerStatus: common.StringPtr(""), Address: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("DescribeMutableState", func(t *testing.T) { h.EXPECT().DescribeMutableState(ctx, &types.DescribeMutableStateRequest{}).Return(&types.DescribeMutableStateResponse{}, internalErr).Times(1) resp, err := th.DescribeMutableState(ctx, &hist.DescribeMutableStateRequest{}) assert.Equal(t, hist.DescribeMutableStateResponse{MutableStateInCache: common.StringPtr(""), MutableStateInDatabase: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("DescribeQueue", func(t *testing.T) { h.EXPECT().DescribeQueue(ctx, &types.DescribeQueueRequest{}).Return(&types.DescribeQueueResponse{}, internalErr).Times(1) resp, err := th.DescribeQueue(ctx, &shared.DescribeQueueRequest{}) assert.Equal(t, shared.DescribeQueueResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("DescribeWorkflowExecution", func(t *testing.T) { h.EXPECT().DescribeWorkflowExecution(ctx, &types.HistoryDescribeWorkflowExecutionRequest{}).Return(&types.DescribeWorkflowExecutionResponse{}, internalErr).Times(1) resp, err := th.DescribeWorkflowExecution(ctx, &hist.DescribeWorkflowExecutionRequest{}) assert.Equal(t, shared.DescribeWorkflowExecutionResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("GetCrossClusterTasks", func(t *testing.T) { h.EXPECT().GetCrossClusterTasks(ctx, &types.GetCrossClusterTasksRequest{}).Return(&types.GetCrossClusterTasksResponse{}, internalErr).Times(1) resp, err := th.GetCrossClusterTasks(ctx, &shared.GetCrossClusterTasksRequest{}) assert.Equal(t, shared.GetCrossClusterTasksResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("GetDLQReplicationMessages", func(t *testing.T) { h.EXPECT().GetDLQReplicationMessages(ctx, &types.GetDLQReplicationMessagesRequest{}).Return(&types.GetDLQReplicationMessagesResponse{}, internalErr).Times(1) resp, err := th.GetDLQReplicationMessages(ctx, &replicator.GetDLQReplicationMessagesRequest{}) assert.Equal(t, replicator.GetDLQReplicationMessagesResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("GetMutableState", func(t *testing.T) { h.EXPECT().GetMutableState(ctx, &types.GetMutableStateRequest{}).Return(&types.GetMutableStateResponse{}, internalErr).Times(1) resp, err := th.GetMutableState(ctx, &hist.GetMutableStateRequest{}) assert.Equal(t, hist.GetMutableStateResponse{ IsWorkflowRunning: common.BoolPtr(false), NextEventId: common.Int64Ptr(0), LastFirstEventId: common.Int64Ptr(0), ClientLibraryVersion: common.StringPtr(""), ClientFeatureVersion: common.StringPtr(""), ClientImpl: common.StringPtr(""), EventStoreVersion: common.Int32Ptr(0), IsStickyTaskListEnabled: common.BoolPtr(false), HistorySize: common.Int64Ptr(0), }, *resp) assert.Equal(t, expectedErr, err) }) t.Run("GetReplicationMessages", func(t *testing.T) { h.EXPECT().GetReplicationMessages(ctx, &types.GetReplicationMessagesRequest{}).Return(&types.GetReplicationMessagesResponse{}, internalErr).Times(1) resp, err := th.GetReplicationMessages(ctx, &replicator.GetReplicationMessagesRequest{}) assert.Equal(t, replicator.GetReplicationMessagesResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("MergeDLQMessages", func(t *testing.T) { h.EXPECT().MergeDLQMessages(ctx, &types.MergeDLQMessagesRequest{}).Return(&types.MergeDLQMessagesResponse{}, internalErr).Times(1) resp, err := th.MergeDLQMessages(ctx, &replicator.MergeDLQMessagesRequest{}) assert.Equal(t, replicator.MergeDLQMessagesResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("NotifyFailoverMarkers", func(t *testing.T) { h.EXPECT().NotifyFailoverMarkers(ctx, &types.NotifyFailoverMarkersRequest{}).Return(internalErr).Times(1) err := th.NotifyFailoverMarkers(ctx, &hist.NotifyFailoverMarkersRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("PollMutableState", func(t *testing.T) { h.EXPECT().PollMutableState(ctx, &types.PollMutableStateRequest{}).Return(&types.PollMutableStateResponse{}, internalErr).Times(1) resp, err := th.PollMutableState(ctx, &hist.PollMutableStateRequest{}) assert.Equal(t, hist.PollMutableStateResponse{ NextEventId: common.Int64Ptr(0), LastFirstEventId: common.Int64Ptr(0), ClientLibraryVersion: common.StringPtr(""), ClientFeatureVersion: common.StringPtr(""), ClientImpl: common.StringPtr(""), }, *resp) assert.Equal(t, expectedErr, err) }) t.Run("PurgeDLQMessages", func(t *testing.T) { h.EXPECT().PurgeDLQMessages(ctx, &types.PurgeDLQMessagesRequest{}).Return(internalErr).Times(1) err := th.PurgeDLQMessages(ctx, &replicator.PurgeDLQMessagesRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("QueryWorkflow", func(t *testing.T) { h.EXPECT().QueryWorkflow(ctx, &types.HistoryQueryWorkflowRequest{}).Return(&types.HistoryQueryWorkflowResponse{}, internalErr).Times(1) resp, err := th.QueryWorkflow(ctx, &hist.QueryWorkflowRequest{}) assert.Equal(t, hist.QueryWorkflowResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ReadDLQMessages", func(t *testing.T) { h.EXPECT().ReadDLQMessages(ctx, &types.ReadDLQMessagesRequest{}).Return(&types.ReadDLQMessagesResponse{}, internalErr).Times(1) resp, err := th.ReadDLQMessages(ctx, &replicator.ReadDLQMessagesRequest{}) assert.Equal(t, replicator.ReadDLQMessagesResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ReapplyEvents", func(t *testing.T) { h.EXPECT().ReapplyEvents(ctx, &types.HistoryReapplyEventsRequest{}).Return(internalErr).Times(1) err := th.ReapplyEvents(ctx, &hist.ReapplyEventsRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RecordActivityTaskHeartbeat", func(t *testing.T) { h.EXPECT().RecordActivityTaskHeartbeat(ctx, &types.HistoryRecordActivityTaskHeartbeatRequest{}).Return(&types.RecordActivityTaskHeartbeatResponse{}, internalErr).Times(1) resp, err := th.RecordActivityTaskHeartbeat(ctx, &hist.RecordActivityTaskHeartbeatRequest{}) assert.Equal(t, shared.RecordActivityTaskHeartbeatResponse{CancelRequested: common.BoolPtr(false)}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RecordActivityTaskStarted", func(t *testing.T) { h.EXPECT().RecordActivityTaskStarted(ctx, &types.RecordActivityTaskStartedRequest{}).Return(&types.RecordActivityTaskStartedResponse{}, internalErr).Times(1) resp, err := th.RecordActivityTaskStarted(ctx, &hist.RecordActivityTaskStartedRequest{}) assert.Equal(t, hist.RecordActivityTaskStartedResponse{WorkflowDomain: common.StringPtr(""), Attempt: common.Int64Ptr(0)}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RecordChildExecutionCompleted", func(t *testing.T) { h.EXPECT().RecordChildExecutionCompleted(ctx, &types.RecordChildExecutionCompletedRequest{}).Return(internalErr).Times(1) err := th.RecordChildExecutionCompleted(ctx, &hist.RecordChildExecutionCompletedRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RecordDecisionTaskStarted", func(t *testing.T) { h.EXPECT().RecordDecisionTaskStarted(ctx, &types.RecordDecisionTaskStartedRequest{}).Return(&types.RecordDecisionTaskStartedResponse{}, internalErr).Times(1) resp, err := th.RecordDecisionTaskStarted(ctx, &hist.RecordDecisionTaskStartedRequest{}) assert.Equal(t, hist.RecordDecisionTaskStartedResponse{ ScheduledEventId: common.Int64Ptr(0), StartedEventId: common.Int64Ptr(0), NextEventId: common.Int64Ptr(0), Attempt: common.Int64Ptr(0), StickyExecutionEnabled: common.BoolPtr(false), EventStoreVersion: common.Int32Ptr(0), HistorySize: common.Int64Ptr(0), }, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RefreshWorkflowTasks", func(t *testing.T) { h.EXPECT().RefreshWorkflowTasks(ctx, &types.HistoryRefreshWorkflowTasksRequest{}).Return(internalErr).Times(1) err := th.RefreshWorkflowTasks(ctx, &hist.RefreshWorkflowTasksRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RemoveSignalMutableState", func(t *testing.T) { h.EXPECT().RemoveSignalMutableState(ctx, &types.RemoveSignalMutableStateRequest{}).Return(internalErr).Times(1) err := th.RemoveSignalMutableState(ctx, &hist.RemoveSignalMutableStateRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RemoveTask", func(t *testing.T) { h.EXPECT().RemoveTask(ctx, &types.RemoveTaskRequest{}).Return(internalErr).Times(1) err := th.RemoveTask(ctx, &shared.RemoveTaskRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("ReplicateEventsV2", func(t *testing.T) { h.EXPECT().ReplicateEventsV2(ctx, &types.ReplicateEventsV2Request{}).Return(internalErr).Times(1) err := th.ReplicateEventsV2(ctx, &hist.ReplicateEventsV2Request{}) assert.Equal(t, expectedErr, err) }) t.Run("RequestCancelWorkflowExecution", func(t *testing.T) { h.EXPECT().RequestCancelWorkflowExecution(ctx, &types.HistoryRequestCancelWorkflowExecutionRequest{}).Return(internalErr).Times(1) err := th.RequestCancelWorkflowExecution(ctx, &hist.RequestCancelWorkflowExecutionRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("ResetQueue", func(t *testing.T) { h.EXPECT().ResetQueue(ctx, &types.ResetQueueRequest{}).Return(internalErr).Times(1) err := th.ResetQueue(ctx, &shared.ResetQueueRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("ResetStickyTaskList", func(t *testing.T) { h.EXPECT().ResetStickyTaskList(ctx, &types.HistoryResetStickyTaskListRequest{}).Return(&types.HistoryResetStickyTaskListResponse{}, internalErr).Times(1) resp, err := th.ResetStickyTaskList(ctx, &hist.ResetStickyTaskListRequest{}) assert.Equal(t, hist.ResetStickyTaskListResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ResetWorkflowExecution", func(t *testing.T) { h.EXPECT().ResetWorkflowExecution(ctx, &types.HistoryResetWorkflowExecutionRequest{}).Return(&types.ResetWorkflowExecutionResponse{}, internalErr).Times(1) resp, err := th.ResetWorkflowExecution(ctx, &hist.ResetWorkflowExecutionRequest{}) assert.Equal(t, shared.ResetWorkflowExecutionResponse{RunId: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RespondActivityTaskCanceled", func(t *testing.T) { h.EXPECT().RespondActivityTaskCanceled(ctx, &types.HistoryRespondActivityTaskCanceledRequest{}).Return(internalErr).Times(1) err := th.RespondActivityTaskCanceled(ctx, &hist.RespondActivityTaskCanceledRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RespondActivityTaskCompleted", func(t *testing.T) { h.EXPECT().RespondActivityTaskCompleted(ctx, &types.HistoryRespondActivityTaskCompletedRequest{}).Return(internalErr).Times(1) err := th.RespondActivityTaskCompleted(ctx, &hist.RespondActivityTaskCompletedRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RespondActivityTaskFailed", func(t *testing.T) { h.EXPECT().RespondActivityTaskFailed(ctx, &types.HistoryRespondActivityTaskFailedRequest{}).Return(internalErr).Times(1) err := th.RespondActivityTaskFailed(ctx, &hist.RespondActivityTaskFailedRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("RespondCrossClusterTasksCompleted", func(t *testing.T) { h.EXPECT().RespondCrossClusterTasksCompleted(ctx, &types.RespondCrossClusterTasksCompletedRequest{}).Return(&types.RespondCrossClusterTasksCompletedResponse{}, internalErr).Times(1) resp, err := th.RespondCrossClusterTasksCompleted(ctx, &shared.RespondCrossClusterTasksCompletedRequest{}) assert.Equal(t, shared.RespondCrossClusterTasksCompletedResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RespondDecisionTaskCompleted", func(t *testing.T) { h.EXPECT().RespondDecisionTaskCompleted(ctx, &types.HistoryRespondDecisionTaskCompletedRequest{}).Return(&types.HistoryRespondDecisionTaskCompletedResponse{}, internalErr).Times(1) resp, err := th.RespondDecisionTaskCompleted(ctx, &hist.RespondDecisionTaskCompletedRequest{}) assert.Equal(t, hist.RespondDecisionTaskCompletedResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RespondDecisionTaskFailed", func(t *testing.T) { h.EXPECT().RespondDecisionTaskFailed(ctx, &types.HistoryRespondDecisionTaskFailedRequest{}).Return(internalErr).Times(1) err := th.RespondDecisionTaskFailed(ctx, &hist.RespondDecisionTaskFailedRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("ScheduleDecisionTask", func(t *testing.T) { h.EXPECT().ScheduleDecisionTask(ctx, &types.ScheduleDecisionTaskRequest{}).Return(internalErr).Times(1) err := th.ScheduleDecisionTask(ctx, &hist.ScheduleDecisionTaskRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("SignalWithStartWorkflowExecution", func(t *testing.T) { h.EXPECT().SignalWithStartWorkflowExecution(ctx, &types.HistorySignalWithStartWorkflowExecutionRequest{}).Return(&types.StartWorkflowExecutionResponse{}, internalErr).Times(1) resp, err := th.SignalWithStartWorkflowExecution(ctx, &hist.SignalWithStartWorkflowExecutionRequest{}) assert.Equal(t, shared.StartWorkflowExecutionResponse{RunId: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("SignalWorkflowExecution", func(t *testing.T) { h.EXPECT().SignalWorkflowExecution(ctx, &types.HistorySignalWorkflowExecutionRequest{}).Return(internalErr).Times(1) err := th.SignalWorkflowExecution(ctx, &hist.SignalWorkflowExecutionRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("StartWorkflowExecution", func(t *testing.T) { h.EXPECT().StartWorkflowExecution(ctx, &types.HistoryStartWorkflowExecutionRequest{}).Return(&types.StartWorkflowExecutionResponse{}, internalErr).Times(1) resp, err := th.StartWorkflowExecution(ctx, &hist.StartWorkflowExecutionRequest{}) assert.Equal(t, shared.StartWorkflowExecutionResponse{RunId: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("SyncActivity", func(t *testing.T) { h.EXPECT().SyncActivity(ctx, &types.SyncActivityRequest{}).Return(internalErr).Times(1) err := th.SyncActivity(ctx, &hist.SyncActivityRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("SyncShardStatus", func(t *testing.T) { h.EXPECT().SyncShardStatus(ctx, &types.SyncShardStatusRequest{}).Return(internalErr).Times(1) err := th.SyncShardStatus(ctx, &hist.SyncShardStatusRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("TerminateWorkflowExecution", func(t *testing.T) { h.EXPECT().TerminateWorkflowExecution(ctx, &types.HistoryTerminateWorkflowExecutionRequest{}).Return(internalErr).Times(1) err := th.TerminateWorkflowExecution(ctx, &hist.TerminateWorkflowExecutionRequest{}) assert.Equal(t, expectedErr, err) }) } ================================================ FILE: service/matching/config/config.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package config import ( "time" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" ) type ( // Config represents configuration for cadence-matching service Config struct { PersistenceMaxQPS dynamicproperties.IntPropertyFn PersistenceGlobalMaxQPS dynamicproperties.IntPropertyFn EnableSyncMatch dynamicproperties.BoolPropertyFnWithTaskListInfoFilters UserRPS dynamicproperties.IntPropertyFn WorkerRPS dynamicproperties.IntPropertyFn DomainUserRPS dynamicproperties.IntPropertyFnWithDomainFilter DomainWorkerRPS dynamicproperties.IntPropertyFnWithDomainFilter ShutdownDrainDuration dynamicproperties.DurationPropertyFn // taskListManager configuration RangeSize int64 ReadRangeSize dynamicproperties.IntPropertyFn EnableReturnAllTaskListKinds dynamicproperties.BoolPropertyFn GetTasksBatchSize dynamicproperties.IntPropertyFnWithTaskListInfoFilters UpdateAckInterval dynamicproperties.DurationPropertyFnWithTaskListInfoFilters IdleTasklistCheckInterval dynamicproperties.DurationPropertyFnWithTaskListInfoFilters MaxTasklistIdleTime dynamicproperties.DurationPropertyFnWithTaskListInfoFilters NumTasklistWritePartitions dynamicproperties.IntPropertyFnWithTaskListInfoFilters NumTasklistReadPartitions dynamicproperties.IntPropertyFnWithTaskListInfoFilters ForwarderMaxOutstandingPolls dynamicproperties.IntPropertyFnWithTaskListInfoFilters ForwarderMaxOutstandingTasks dynamicproperties.IntPropertyFnWithTaskListInfoFilters ForwarderMaxRatePerSecond dynamicproperties.IntPropertyFnWithTaskListInfoFilters ForwarderMaxChildrenPerNode dynamicproperties.IntPropertyFnWithTaskListInfoFilters AppendTaskTimeout dynamicproperties.DurationPropertyFnWithTaskListInfoFilters AsyncTaskDispatchTimeout dynamicproperties.DurationPropertyFnWithTaskListInfoFilters LocalPollWaitTime dynamicproperties.DurationPropertyFnWithTaskListInfoFilters LocalTaskWaitTime dynamicproperties.DurationPropertyFnWithTaskListInfoFilters TaskIsolationDuration dynamicproperties.DurationPropertyFnWithTaskListInfoFilters TaskIsolationPollerWindow dynamicproperties.DurationPropertyFnWithTaskListInfoFilters EnableGetNumberOfPartitionsFromCache dynamicproperties.BoolPropertyFnWithTaskListInfoFilters PartitionUpscaleRPS dynamicproperties.IntPropertyFnWithTaskListInfoFilters PartitionDownscaleFactor dynamicproperties.FloatPropertyFnWithTaskListInfoFilters PartitionUpscaleSustainedDuration dynamicproperties.DurationPropertyFnWithTaskListInfoFilters PartitionDownscaleSustainedDuration dynamicproperties.DurationPropertyFnWithTaskListInfoFilters AdaptiveScalerUpdateInterval dynamicproperties.DurationPropertyFnWithTaskListInfoFilters EnableAdaptiveScaler dynamicproperties.BoolPropertyFnWithTaskListInfoFilters EnablePartitionEmptyCheck dynamicproperties.BoolPropertyFnWithTaskListInfoFilters EnableStandbyTaskCompletion dynamicproperties.BoolPropertyFnWithTaskListInfoFilters EnableClientAutoConfig dynamicproperties.BoolPropertyFnWithTaskListInfoFilters QPSTrackerInterval dynamicproperties.DurationPropertyFnWithTaskListInfoFilters OverrideTaskListRPS dynamicproperties.FloatPropertyFnWithTaskListInfoFilters EnablePartitionIsolationGroupAssignment dynamicproperties.BoolPropertyFnWithTaskListInfoFilters IsolationGroupUpscaleSustainedDuration dynamicproperties.DurationPropertyFnWithTaskListInfoFilters IsolationGroupDownscaleSustainedDuration dynamicproperties.DurationPropertyFnWithTaskListInfoFilters IsolationGroupHasPollersSustainedDuration dynamicproperties.DurationPropertyFnWithTaskListInfoFilters IsolationGroupNoPollersSustainedDuration dynamicproperties.DurationPropertyFnWithTaskListInfoFilters IsolationGroupsPerPartition dynamicproperties.IntPropertyFnWithTaskListInfoFilters // Time to hold a poll request before returning an empty response if there are no tasks LongPollExpirationInterval dynamicproperties.DurationPropertyFnWithTaskListInfoFilters MinTaskThrottlingBurstSize dynamicproperties.IntPropertyFnWithTaskListInfoFilters MaxTaskDeleteBatchSize dynamicproperties.IntPropertyFnWithTaskListInfoFilters // taskWriter configuration OutstandingTaskAppendsThreshold dynamicproperties.IntPropertyFnWithTaskListInfoFilters MaxTaskBatchSize dynamicproperties.IntPropertyFnWithTaskListInfoFilters ThrottledLogRPS dynamicproperties.IntPropertyFn // debugging configuration EnableDebugMode bool // note that this value is initialized once on service start EnableTaskInfoLogByDomainID dynamicproperties.BoolPropertyFnWithDomainIDFilter ActivityTaskSyncMatchWaitTime dynamicproperties.DurationPropertyFnWithDomainFilter // isolation configuration EnableTasklistIsolation dynamicproperties.BoolPropertyFnWithDomainFilter AllIsolationGroups func() []string // hostname info HostName string // RPCConfig contains RPC configuration including ports and bindOnLocalHost RPCConfig config.RPC // rate limiter configuration TaskDispatchRPS float64 TaskDispatchRPSTTL time.Duration // task gc configuration MaxTimeBetweenTaskDeletes time.Duration EnableTasklistOwnershipGuard dynamicproperties.BoolPropertyFn ExcludeShortLivedTaskListsFromShardManager dynamicproperties.BoolPropertyFn PercentageOnboardedToShardManager dynamicproperties.IntPropertyFn } ForwarderConfig struct { ForwarderMaxOutstandingPolls func() int ForwarderMaxOutstandingTasks func() int ForwarderMaxRatePerSecond func() int ForwarderMaxChildrenPerNode func() int } TaskListConfig struct { ForwarderConfig EnableSyncMatch func() bool // Time to hold a poll request before returning an empty response if there are no tasks LongPollExpirationInterval func() time.Duration RangeSize int64 ReadRangeSize dynamicproperties.IntPropertyFn ActivityTaskSyncMatchWaitTime dynamicproperties.DurationPropertyFnWithDomainFilter GetTasksBatchSize func() int UpdateAckInterval func() time.Duration IdleTasklistCheckInterval func() time.Duration MaxTasklistIdleTime func() time.Duration MinTaskThrottlingBurstSize func() int MaxTaskDeleteBatchSize func() int AppendTaskTimeout func() time.Duration AsyncTaskDispatchTimeout func() time.Duration LocalPollWaitTime func() time.Duration LocalTaskWaitTime func() time.Duration PartitionUpscaleRPS func() int PartitionDownscaleFactor func() float64 PartitionUpscaleSustainedDuration func() time.Duration PartitionDownscaleSustainedDuration func() time.Duration AdaptiveScalerUpdateInterval func() time.Duration QPSTrackerInterval func() time.Duration OverrideTaskListRPS func() float64 EnablePartitionIsolationGroupAssignment func() bool IsolationGroupUpscaleSustainedDuration func() time.Duration IsolationGroupDownscaleSustainedDuration func() time.Duration IsolationGroupHasPollersSustainedDuration func() time.Duration IsolationGroupNoPollersSustainedDuration func() time.Duration IsolationGroupsPerPartition func() int // taskWriter configuration OutstandingTaskAppendsThreshold func() int MaxTaskBatchSize func() int NumWritePartitions func() int NumReadPartitions func() int EnableGetNumberOfPartitionsFromCache func() bool EnableAdaptiveScaler func() bool EnablePartitionEmptyCheck func() bool // isolation configuration EnableTasklistIsolation func() bool // A function which returns all the isolation groups AllIsolationGroups func() []string TaskIsolationDuration func() time.Duration TaskIsolationPollerWindow func() time.Duration // hostname HostName string // rate limiter configuration TaskDispatchRPS float64 TaskDispatchRPSTTL time.Duration // task gc configuration MaxTimeBetweenTaskDeletes time.Duration // standby task completion configuration EnableStandbyTaskCompletion func() bool EnableClientAutoConfig func() bool } ) // NewConfig returns new service config with default values func NewConfig(dc *dynamicconfig.Collection, hostName string, rpcConfig config.RPC, getIsolationGroups func() []string) *Config { return &Config{ PersistenceMaxQPS: dc.GetIntProperty(dynamicproperties.MatchingPersistenceMaxQPS), PersistenceGlobalMaxQPS: dc.GetIntProperty(dynamicproperties.MatchingPersistenceGlobalMaxQPS), EnableSyncMatch: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicproperties.MatchingEnableSyncMatch), UserRPS: dc.GetIntProperty(dynamicproperties.MatchingUserRPS), WorkerRPS: dc.GetIntProperty(dynamicproperties.MatchingWorkerRPS), DomainUserRPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.MatchingDomainUserRPS), DomainWorkerRPS: dc.GetIntPropertyFilteredByDomain(dynamicproperties.MatchingDomainWorkerRPS), RangeSize: 100000, ReadRangeSize: dc.GetIntProperty(dynamicproperties.MatchingReadRangeSize), GetTasksBatchSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingGetTasksBatchSize), UpdateAckInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingUpdateAckInterval), IdleTasklistCheckInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingIdleTasklistCheckInterval), MaxTasklistIdleTime: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MaxTasklistIdleTime), LongPollExpirationInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingLongPollExpirationInterval), MinTaskThrottlingBurstSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingMinTaskThrottlingBurstSize), MaxTaskDeleteBatchSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingMaxTaskDeleteBatchSize), OutstandingTaskAppendsThreshold: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingOutstandingTaskAppendsThreshold), MaxTaskBatchSize: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingMaxTaskBatchSize), ThrottledLogRPS: dc.GetIntProperty(dynamicproperties.MatchingThrottledLogRPS), NumTasklistWritePartitions: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingNumTasklistWritePartitions), NumTasklistReadPartitions: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingNumTasklistReadPartitions), ForwarderMaxOutstandingPolls: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingForwarderMaxOutstandingPolls), ForwarderMaxOutstandingTasks: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingForwarderMaxOutstandingTasks), ForwarderMaxRatePerSecond: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingForwarderMaxRatePerSecond), ForwarderMaxChildrenPerNode: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingForwarderMaxChildrenPerNode), EnableGetNumberOfPartitionsFromCache: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicproperties.MatchingEnableGetNumberOfPartitionsFromCache), ShutdownDrainDuration: dc.GetDurationProperty(dynamicproperties.MatchingShutdownDrainDuration), EnableDebugMode: dc.GetBoolProperty(dynamicproperties.EnableDebugMode)(), EnableTaskInfoLogByDomainID: dc.GetBoolPropertyFilteredByDomainID(dynamicproperties.MatchingEnableTaskInfoLogByDomainID), ActivityTaskSyncMatchWaitTime: dc.GetDurationPropertyFilteredByDomain(dynamicproperties.MatchingActivityTaskSyncMatchWaitTime), EnableTasklistIsolation: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.EnableTasklistIsolation), AppendTaskTimeout: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.AppendTaskTimeout), AsyncTaskDispatchTimeout: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.AsyncTaskDispatchTimeout), EnableTasklistOwnershipGuard: dc.GetBoolProperty(dynamicproperties.MatchingEnableTasklistGuardAgainstOwnershipShardLoss), LocalPollWaitTime: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.LocalPollWaitTime), LocalTaskWaitTime: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.LocalTaskWaitTime), PartitionUpscaleRPS: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingPartitionUpscaleRPS), PartitionDownscaleFactor: dc.GetFloat64PropertyFilteredByTaskListInfo(dynamicproperties.MatchingPartitionDownscaleFactor), PartitionUpscaleSustainedDuration: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingPartitionUpscaleSustainedDuration), PartitionDownscaleSustainedDuration: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingPartitionDownscaleSustainedDuration), AdaptiveScalerUpdateInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingAdaptiveScalerUpdateInterval), EnableAdaptiveScaler: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicproperties.MatchingEnableAdaptiveScaler), EnablePartitionEmptyCheck: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicproperties.MatchingEnablePartitionEmptyCheck), QPSTrackerInterval: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingQPSTrackerInterval), OverrideTaskListRPS: dc.GetFloat64PropertyFilteredByTaskListInfo(dynamicproperties.MatchingOverrideTaskListRPS), EnablePartitionIsolationGroupAssignment: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicproperties.EnablePartitionIsolationGroupAssignment), IsolationGroupUpscaleSustainedDuration: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingIsolationGroupUpscaleSustainedDuration), IsolationGroupDownscaleSustainedDuration: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingIsolationGroupDownscaleSustainedDuration), IsolationGroupHasPollersSustainedDuration: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingIsolationGroupHasPollersSustainedDuration), IsolationGroupNoPollersSustainedDuration: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.MatchingIsolationGroupNoPollersSustainedDuration), IsolationGroupsPerPartition: dc.GetIntPropertyFilteredByTaskListInfo(dynamicproperties.MatchingIsolationGroupsPerPartition), TaskIsolationDuration: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.TaskIsolationDuration), TaskIsolationPollerWindow: dc.GetDurationPropertyFilteredByTaskListInfo(dynamicproperties.TaskIsolationPollerWindow), HostName: hostName, RPCConfig: rpcConfig, TaskDispatchRPS: 100000.0, TaskDispatchRPSTTL: time.Minute, MaxTimeBetweenTaskDeletes: time.Second, AllIsolationGroups: getIsolationGroups, EnableStandbyTaskCompletion: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicproperties.MatchingEnableStandbyTaskCompletion), EnableClientAutoConfig: dc.GetBoolPropertyFilteredByTaskListInfo(dynamicproperties.MatchingEnableClientAutoConfig), EnableReturnAllTaskListKinds: dc.GetBoolProperty(dynamicproperties.MatchingEnableReturnAllTaskListKinds), ExcludeShortLivedTaskListsFromShardManager: dc.GetBoolProperty(dynamicproperties.MatchingExcludeShortLivedTaskListsFromShardManager), PercentageOnboardedToShardManager: dc.GetIntProperty(dynamicproperties.MatchingPercentageOnboardedToShardManager), } } ================================================ FILE: service/matching/config/config_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package config import ( "reflect" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) type configTestCase struct { key dynamicproperties.Key value interface{} } func TestNewConfig(t *testing.T) { hostname := "hostname" fields := map[string]configTestCase{ "PersistenceMaxQPS": {dynamicproperties.MatchingPersistenceMaxQPS, 1}, "PersistenceGlobalMaxQPS": {dynamicproperties.MatchingPersistenceGlobalMaxQPS, 2}, "EnableSyncMatch": {dynamicproperties.MatchingEnableSyncMatch, true}, "UserRPS": {dynamicproperties.MatchingUserRPS, 3}, "WorkerRPS": {dynamicproperties.MatchingWorkerRPS, 4}, "DomainUserRPS": {dynamicproperties.MatchingDomainUserRPS, 5}, "DomainWorkerRPS": {dynamicproperties.MatchingDomainWorkerRPS, 6}, "RangeSize": {nil, int64(100000)}, "ReadRangeSize": {dynamicproperties.MatchingReadRangeSize, 50000}, "GetTasksBatchSize": {dynamicproperties.MatchingGetTasksBatchSize, 7}, "UpdateAckInterval": {dynamicproperties.MatchingUpdateAckInterval, time.Duration(8)}, "IdleTasklistCheckInterval": {dynamicproperties.MatchingIdleTasklistCheckInterval, time.Duration(9)}, "MaxTasklistIdleTime": {dynamicproperties.MaxTasklistIdleTime, time.Duration(10)}, "LongPollExpirationInterval": {dynamicproperties.MatchingLongPollExpirationInterval, time.Duration(11)}, "MinTaskThrottlingBurstSize": {dynamicproperties.MatchingMinTaskThrottlingBurstSize, 12}, "MaxTaskDeleteBatchSize": {dynamicproperties.MatchingMaxTaskDeleteBatchSize, 13}, "OutstandingTaskAppendsThreshold": {dynamicproperties.MatchingOutstandingTaskAppendsThreshold, 14}, "MaxTaskBatchSize": {dynamicproperties.MatchingMaxTaskBatchSize, 15}, "ThrottledLogRPS": {dynamicproperties.MatchingThrottledLogRPS, 16}, "NumTasklistWritePartitions": {dynamicproperties.MatchingNumTasklistWritePartitions, 17}, "NumTasklistReadPartitions": {dynamicproperties.MatchingNumTasklistReadPartitions, 18}, "ForwarderMaxOutstandingPolls": {dynamicproperties.MatchingForwarderMaxOutstandingPolls, 19}, "ForwarderMaxOutstandingTasks": {dynamicproperties.MatchingForwarderMaxOutstandingTasks, 20}, "ForwarderMaxRatePerSecond": {dynamicproperties.MatchingForwarderMaxRatePerSecond, 21}, "ForwarderMaxChildrenPerNode": {dynamicproperties.MatchingForwarderMaxChildrenPerNode, 22}, "ShutdownDrainDuration": {dynamicproperties.MatchingShutdownDrainDuration, time.Duration(23)}, "EnableDebugMode": {dynamicproperties.EnableDebugMode, false}, "EnableTaskInfoLogByDomainID": {dynamicproperties.MatchingEnableTaskInfoLogByDomainID, true}, "ActivityTaskSyncMatchWaitTime": {dynamicproperties.MatchingActivityTaskSyncMatchWaitTime, time.Duration(24)}, "EnableTasklistIsolation": {dynamicproperties.EnableTasklistIsolation, false}, "AsyncTaskDispatchTimeout": {dynamicproperties.AsyncTaskDispatchTimeout, time.Duration(25)}, "LocalPollWaitTime": {dynamicproperties.LocalPollWaitTime, time.Duration(10)}, "LocalTaskWaitTime": {dynamicproperties.LocalTaskWaitTime, time.Duration(10)}, "HostName": {nil, hostname}, "RPCConfig": {nil, config.RPC{}}, "TaskDispatchRPS": {nil, 100000.0}, "TaskDispatchRPSTTL": {nil, time.Minute}, "MaxTimeBetweenTaskDeletes": {nil, time.Second}, "AllIsolationGroups": {nil, []string{"zone-1", "zone-2"}}, "EnableTasklistOwnershipGuard": {dynamicproperties.MatchingEnableTasklistGuardAgainstOwnershipShardLoss, false}, "EnableGetNumberOfPartitionsFromCache": {dynamicproperties.MatchingEnableGetNumberOfPartitionsFromCache, false}, "EnablePartitionEmptyCheck": {dynamicproperties.MatchingEnablePartitionEmptyCheck, true}, "PartitionUpscaleRPS": {dynamicproperties.MatchingPartitionUpscaleRPS, 30}, "PartitionDownscaleFactor": {dynamicproperties.MatchingPartitionDownscaleFactor, 31.0}, "PartitionUpscaleSustainedDuration": {dynamicproperties.MatchingPartitionUpscaleSustainedDuration, time.Duration(32)}, "PartitionDownscaleSustainedDuration": {dynamicproperties.MatchingPartitionDownscaleSustainedDuration, time.Duration(33)}, "AdaptiveScalerUpdateInterval": {dynamicproperties.MatchingAdaptiveScalerUpdateInterval, time.Duration(34)}, "EnableAdaptiveScaler": {dynamicproperties.MatchingEnableAdaptiveScaler, true}, "QPSTrackerInterval": {dynamicproperties.MatchingQPSTrackerInterval, 5 * time.Second}, "OverrideTaskListRPS": {dynamicproperties.MatchingOverrideTaskListRPS, 1500.0}, "EnableStandbyTaskCompletion": {dynamicproperties.MatchingEnableStandbyTaskCompletion, false}, "EnableClientAutoConfig": {dynamicproperties.MatchingEnableClientAutoConfig, false}, "TaskIsolationDuration": {dynamicproperties.TaskIsolationDuration, time.Duration(35)}, "TaskIsolationPollerWindow": {dynamicproperties.TaskIsolationPollerWindow, time.Duration(36)}, "EnablePartitionIsolationGroupAssignment": {dynamicproperties.EnablePartitionIsolationGroupAssignment, true}, "IsolationGroupUpscaleSustainedDuration": {dynamicproperties.MatchingIsolationGroupUpscaleSustainedDuration, time.Duration(37)}, "IsolationGroupDownscaleSustainedDuration": {dynamicproperties.MatchingIsolationGroupDownscaleSustainedDuration, time.Duration(38)}, "IsolationGroupHasPollersSustainedDuration": {dynamicproperties.MatchingIsolationGroupHasPollersSustainedDuration, time.Duration(39)}, "IsolationGroupNoPollersSustainedDuration": {dynamicproperties.MatchingIsolationGroupNoPollersSustainedDuration, time.Duration(40)}, "IsolationGroupsPerPartition": {dynamicproperties.MatchingIsolationGroupsPerPartition, 41}, "EnableReturnAllTaskListKinds": {dynamicproperties.MatchingEnableReturnAllTaskListKinds, true}, "AppendTaskTimeout": {dynamicproperties.AppendTaskTimeout, time.Duration(42)}, "ExcludeShortLivedTaskListsFromShardManager": {dynamicproperties.MatchingExcludeShortLivedTaskListsFromShardManager, false}, "PercentageOnboardedToShardManager": {dynamicproperties.MatchingPercentageOnboardedToShardManager, 0}, } client := dynamicconfig.NewInMemoryClient() for fieldName, expected := range fields { if expected.key != nil { err := client.UpdateValue(expected.key, expected.value) if err != nil { t.Errorf("Failed to update config for %s: %s", fieldName, err) } } } dc := dynamicconfig.NewCollection(client, testlogger.New(t)) config := NewConfig(dc, hostname, config.RPC{}, isolationGroupsHelper) assertFieldsMatch(t, *config, fields) } func assertFieldsMatch(t *testing.T, config interface{}, fields map[string]configTestCase) { configType := reflect.ValueOf(config) for i := 0; i < configType.NumField(); i++ { f := configType.Field(i) fieldName := configType.Type().Field(i).Name if expected, ok := fields[fieldName]; ok { actual := getValue(&f) if f.Kind() == reflect.Slice { assert.ElementsMatch(t, expected.value, actual, "Incorrect value for field: %s", fieldName) } else { assert.Equal(t, expected.value, actual, "Incorrect value for field: %s", fieldName) } } else { t.Errorf("Unknown property on Config: %s", fieldName) } } } func getValue(f *reflect.Value) interface{} { switch f.Kind() { case reflect.Func: switch fn := f.Interface().(type) { case dynamicproperties.IntPropertyFn: return fn() case dynamicproperties.IntPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.IntPropertyFnWithTaskListInfoFilters: return fn("domain", "tasklist", int(types.TaskListTypeDecision)) case dynamicproperties.BoolPropertyFn: return fn() case dynamicproperties.BoolPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.BoolPropertyFnWithDomainIDFilter: return fn("domain") case dynamicproperties.BoolPropertyFnWithTaskListInfoFilters: return fn("domain", "tasklist", int(types.TaskListTypeDecision)) case dynamicproperties.DurationPropertyFn: return fn() case dynamicproperties.DurationPropertyFnWithDomainFilter: return fn("domain") case dynamicproperties.DurationPropertyFnWithTaskListInfoFilters: return fn("domain", "tasklist", int(types.TaskListTypeDecision)) case dynamicproperties.FloatPropertyFn: return fn() case dynamicproperties.MapPropertyFn: return fn() case dynamicproperties.StringPropertyFn: return fn() case dynamicproperties.FloatPropertyFnWithTaskListInfoFilters: return fn("domain", "tasklist", int(types.TaskListTypeDecision)) case func() []string: return fn() default: panic("Unable to handle type: " + f.Type().Name()) } default: return f.Interface() } } func isolationGroupsHelper() []string { return []string{"zone-1", "zone-2"} } func TestGetTasksBatchSizeValidation(t *testing.T) { tests := []struct { name string configValue int expected int }{ { name: "valid positive batch size", configValue: 1000, expected: 1000, }, { name: "valid small batch size", configValue: 1, expected: 1, }, { name: "zero batch size returns zero (validation happens at usage site)", configValue: 0, expected: 0, }, { name: "negative batch size returns negative (validation happens at usage site)", configValue: -5, expected: -5, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create a mock dynamic config client client := dynamicconfig.NewInMemoryClient() err := client.UpdateValue(dynamicproperties.MatchingGetTasksBatchSize, tt.configValue) assert.NoError(t, err) dc := dynamicconfig.NewCollection(client, testlogger.New(t)) config := NewConfig(dc, "test-host", config.RPC{}, isolationGroupsHelper) // Test that GetTasksBatchSize returns the raw value (validation happens at usage site) result := config.GetTasksBatchSize("test-domain", "test-tasklist", int(types.TaskListTypeDecision)) assert.Equal(t, tt.expected, result, "GetTasksBatchSize should return raw config value") // Test with different task list types result = config.GetTasksBatchSize("test-domain", "test-tasklist", int(types.TaskListTypeActivity)) assert.Equal(t, tt.expected, result, "GetTasksBatchSize should return raw config value for activity task list") }) } } ================================================ FILE: service/matching/event/logger.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package event import ( "encoding/json" "fmt" "os" "time" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) var enabled = false func init() { enabled = os.Getenv("MATCHING_LOG_EVENTS") == "true" } type E struct { persistence.TaskInfo TaskListName string TaskListKind *types.TaskListKind TaskListType int // persistence.TaskListTypeDecision or persistence.TaskListTypeActivity EventTime time.Time // EventName describes the event. It is used to query events in simulations so don't change existing event names. EventName string Host string Payload map[string]any } func Log(events ...E) { if !enabled { return } for _, e := range events { e.EventTime = time.Now() data, err := json.Marshal(e) if err != nil { fmt.Printf("failed to marshal event: %v", err) } fmt.Printf("Matching New Event: %s\n", data) } } ================================================ FILE: service/matching/handler/context.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package handler import ( "context" "errors" "sync" "github.com/uber/cadence/common" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type handlerContext struct { context.Context scope metrics.Scope logger log.Logger } func newHandlerContext( ctx context.Context, domainName string, taskList *types.TaskList, metricsClient metrics.Client, metricsScope metrics.ScopeIdx, logger log.Logger, ) *handlerContext { return &handlerContext{ Context: ctx, scope: common.NewPerTaskListScope(domainName, taskList.GetName(), taskList.GetKind(), metricsClient, metricsScope).Tagged(metrics.GetContextTags(ctx)...), logger: logger.WithTags(tag.WorkflowDomainName(domainName), tag.WorkflowTaskListName(taskList.GetName())), } } // startProfiling initiates recording of request metrics func (reqCtx *handlerContext) startProfiling(wg *sync.WaitGroup) metrics.Stopwatch { wg.Wait() sw := reqCtx.scope.StartTimer(metrics.CadenceLatencyPerTaskList) reqCtx.scope.IncCounter(metrics.CadenceRequestsPerTaskList) return sw } func (reqCtx *handlerContext) handleErr(err error) error { if err == nil { return nil } logger := reqCtx.logger.Helper() switch { case errors.As(err, new(*types.InternalServiceError)): reqCtx.scope.IncCounter(metrics.CadenceFailuresPerTaskList) logger.Error("Internal service error", tag.Error(err)) return err case errors.As(err, new(*types.BadRequestError)): reqCtx.scope.IncCounter(metrics.CadenceErrBadRequestPerTaskListCounter) return err case errors.As(err, new(*types.EntityNotExistsError)): reqCtx.scope.IncCounter(metrics.CadenceErrEntityNotExistsPerTaskListCounter) return err case errors.As(err, new(*types.WorkflowExecutionAlreadyStartedError)): reqCtx.scope.IncCounter(metrics.CadenceErrExecutionAlreadyStartedPerTaskListCounter) return err case errors.As(err, new(*types.DomainAlreadyExistsError)): reqCtx.scope.IncCounter(metrics.CadenceErrDomainAlreadyExistsPerTaskListCounter) return err case errors.As(err, new(*types.QueryFailedError)): reqCtx.scope.IncCounter(metrics.CadenceErrQueryFailedPerTaskListCounter) return err case errors.As(err, new(*types.LimitExceededError)): reqCtx.scope.IncCounter(metrics.CadenceErrLimitExceededPerTaskListCounter) return err case errors.As(err, new(*types.ServiceBusyError)): reqCtx.scope.IncCounter(metrics.CadenceErrServiceBusyPerTaskListCounter) return err case errors.As(err, new(*types.DomainNotActiveError)): reqCtx.scope.IncCounter(metrics.CadenceErrDomainNotActivePerTaskListCounter) return err case errors.As(err, new(*types.RemoteSyncMatchedError)): reqCtx.scope.IncCounter(metrics.CadenceErrRemoteSyncMatchFailedPerTaskListCounter) return err case errors.As(err, new(*types.StickyWorkerUnavailableError)): reqCtx.scope.IncCounter(metrics.CadenceErrStickyWorkerUnavailablePerTaskListCounter) return err case errors.As(err, new(*types.ReadOnlyPartitionError)): reqCtx.scope.IncCounter(metrics.CadenceErrReadOnlyPartitionPerTaskListCounter) return err case errors.As(err, new(*cadence_errors.TaskListNotOwnedByHostError)): reqCtx.scope.IncCounter(metrics.CadenceErrTaskListNotOwnedByHostPerTaskListCounter) return err default: reqCtx.scope.IncCounter(metrics.CadenceFailuresPerTaskList) logger.Error("Uncategorized error", tag.Error(err)) return &types.InternalServiceError{Message: err.Error()} } } ================================================ FILE: service/matching/handler/context_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) func TestHandleErrNil(t *testing.T) { reqCtx := &handlerContext{ scope: metrics.NoopScope, logger: log.NewNoop(), } err := reqCtx.handleErr(nil) assert.NoError(t, err) } func TestHandleErrKnowErrors(t *testing.T) { testCases := []struct { name string err error wantErr error }{ { name: "InternalServiceError", err: &types.InternalServiceError{}, }, { name: "BadRequestError", err: &types.BadRequestError{}, }, { name: "EntityNotExistsError", err: &types.EntityNotExistsError{}, }, { name: "WorkflowExecutionAlreadyStartedError", err: &types.WorkflowExecutionAlreadyStartedError{}, }, { name: "DomainAlreadyExistsError", err: &types.DomainAlreadyExistsError{}, }, { name: "QueryFailedError", err: &types.QueryFailedError{}, }, { name: "LimitExceededError", err: &types.LimitExceededError{}, }, { name: "ServiceBusyError", err: &types.ServiceBusyError{}, }, { name: "DomainNotActiveError", err: &types.DomainNotActiveError{}, }, { name: "RemoteSyncMatchedError", err: &types.RemoteSyncMatchedError{}, }, { name: "StickyWorkerUnavailableError", err: &types.StickyWorkerUnavailableError{}, }, { name: "ReadOnlyPartitionError", err: &types.ReadOnlyPartitionError{}, }, { name: "TaskListNotOwnedByHostError", err: &cadence_errors.TaskListNotOwnedByHostError{}, }, } for _, tc := range testCases { reqCtx := &handlerContext{ scope: metrics.NoopScope, logger: log.NewNoop(), } t.Run(tc.name, func(t *testing.T) { handledErr := reqCtx.handleErr(tc.err) require.Equal(t, tc.err, handledErr) wrappedErr := fmt.Errorf("wrapped: %w", tc.err) wrappedHandledErr := reqCtx.handleErr(wrappedErr) require.Equal(t, wrappedErr, wrappedHandledErr) }) } } func TestHandleErrUncategorizedError(t *testing.T) { reqCtx := &handlerContext{ scope: metrics.NoopScope, logger: log.NewNoop(), } err := reqCtx.handleErr(assert.AnError) assert.ErrorAs(t, err, new(*types.InternalServiceError)) } ================================================ FILE: service/matching/handler/engine.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "bytes" "context" "errors" "fmt" "sync" "time" "github.com/pborman/uuid" "github.com/uber-go/tally" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/event" "github.com/uber/cadence/service/matching/tasklist" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) const ( // If sticky poller is not seem in last 10s, we treat it as sticky worker unavailable // This seems aggressive, but the default sticky schedule_to_start timeout is 5s, so 10s seems reasonable. _stickyPollerUnavailableWindow = 10 * time.Second // _defaultSDReportTTL is the default TTL for shard status reports from matching executor to shard distributor. // This controls how frequently the executor reports its shard load/status to the distributor. _defaultSDReportTTL = 1 * time.Minute // _recordTaskStartedTimeout is the maximum time allowed for RecordDecisionTaskStarted or RecordActivityTaskStarted // Any time we spend attempting to start an individual task is blocking that poller from starting a different task. // If a task is taking too long we'd rather try other tasks to maintain higher throughput. _recordTaskStartedTimeout = time.Second ) // Implements matching.Engine // TODO: Switch implementation from lock/channel based to a partitioned agent // to simplify code and reduce possibility of synchronization errors. type ( queryResult struct { workerResponse *types.MatchingRespondQueryTaskCompletedRequest internalError error } // lockableQueryTaskMap maps query TaskID (which is a UUID generated in QueryWorkflow() call) to a channel // that QueryWorkflow() will block on. The channel is unblocked either by worker sending response through // RespondQueryTaskCompleted() or through an internal service error causing cadence to be unable to dispatch // query task to workflow worker. lockableQueryTaskMap struct { sync.RWMutex queryTaskMap map[string]chan *queryResult } matchingEngineImpl struct { taskListCreationLock sync.Mutex taskListRegistry tasklist.TaskListRegistry shutdownCompletion *sync.WaitGroup shutdown chan struct{} taskManager persistence.TaskManager clusterMetadata cluster.Metadata historyService history.Client matchingClient matching.Client tokenSerializer common.TaskTokenSerializer logger log.Logger metricsClient metrics.Client metricsScope tally.Scope executor executorclient.Executor[tasklist.ShardProcessor] taskListsFactory *tasklist.ShardProcessorFactory config *config.Config lockableQueryTaskMap lockableQueryTaskMap domainCache cache.DomainCache versionChecker client.VersionChecker membershipResolver membership.Resolver isolationState isolationgroup.State timeSource clock.TimeSource failoverNotificationVersion int64 ShardDistributorMatchingConfig clientcommon.Config drainObserver clientcommon.DrainSignalObserver } ) var ( recordTaskStartedRetryPolicy = common.CreateRecordTaskStartedRetryPolicy() errPumpClosed = errors.New("task list pump closed its channel") _stickyPollerUnavailableError = &types.StickyWorkerUnavailableError{Message: "sticky worker is unavailable, please use non-sticky task list."} ) var _ Engine = (*matchingEngineImpl)(nil) // Asserts that interface is indeed implemented // NewEngine creates an instance of matching engine func NewEngine( taskManager persistence.TaskManager, clusterMetadata cluster.Metadata, historyService history.Client, matchingClient matching.Client, config *config.Config, logger log.Logger, metricsClient metrics.Client, metricsScope tally.Scope, domainCache cache.DomainCache, resolver membership.Resolver, isolationState isolationgroup.State, timeSource clock.TimeSource, shardDistributorClient executorclient.Client, ShardDistributorMatchingConfig clientcommon.Config, drainObserver clientcommon.DrainSignalObserver, ) Engine { e := &matchingEngineImpl{ taskListRegistry: tasklist.NewTaskListRegistry(metricsClient), shutdown: make(chan struct{}), shutdownCompletion: &sync.WaitGroup{}, taskManager: taskManager, clusterMetadata: clusterMetadata, historyService: historyService, tokenSerializer: common.NewJSONTaskTokenSerializer(), logger: logger.WithTags(tag.ComponentMatchingEngine), metricsClient: metricsClient, metricsScope: metricsScope, matchingClient: matchingClient, config: config, lockableQueryTaskMap: lockableQueryTaskMap{queryTaskMap: make(map[string]chan *queryResult)}, domainCache: domainCache, versionChecker: client.NewVersionChecker(), membershipResolver: resolver, isolationState: isolationState, timeSource: timeSource, ShardDistributorMatchingConfig: ShardDistributorMatchingConfig, drainObserver: drainObserver, } e.setupExecutor(shardDistributorClient) e.shutdownCompletion.Add(1) go e.runMembershipChangeLoop() return e } func (e *matchingEngineImpl) Start() { e.executor.Start(context.Background()) e.registerDomainFailoverCallback() } func (e *matchingEngineImpl) Stop() { close(e.shutdown) e.executor.Stop() // Executes Stop() on each task list outside of lock for _, l := range e.taskListRegistry.AllManagers() { l.Stop() } e.unregisterDomainFailoverCallback() e.shutdownCompletion.Wait() } func (e *matchingEngineImpl) setupExecutor(shardDistributorExecutorClient executorclient.Client) { // If no shard-distributor namespaces are configured, use a no-op executor so that // the matching service falls back to local hash-ring assignment entirely. if len(e.ShardDistributorMatchingConfig.Namespaces) == 0 { e.logger.Info("No shard-distributor-matching namespaces configured, using no-op executor") e.executor = executorclient.NewNoopExecutor[tasklist.ShardProcessor]() taskListFactory := &tasklist.ShardProcessorFactory{ TaskListsRegistry: e.taskListRegistry, ReportTTL: _defaultSDReportTTL, TimeSource: e.timeSource, } e.taskListsFactory = taskListFactory return } cfg, reportTTL := e.getValidatedShardDistributorConfig() taskListFactory := &tasklist.ShardProcessorFactory{ TaskListsRegistry: e.taskListRegistry, ReportTTL: reportTTL, TimeSource: e.timeSource, } e.taskListsFactory = taskListFactory // Get the IP address to advertise to external services // This respects bindOnLocalHost config (127.0.0.1 for local dev, external IP for production) hostIP, err := rpc.GetListenIP(e.config.RPCConfig) if err != nil { e.logger.Fatal("Failed to get listen IP", tag.Error(err)) } params := executorclient.Params[tasklist.ShardProcessor]{ ExecutorClient: shardDistributorExecutorClient, MetricsScope: e.metricsScope, Logger: e.logger, ShardProcessorFactory: taskListFactory, Config: cfg, TimeSource: e.timeSource, Metadata: map[string]string{ "tchannel": fmt.Sprintf("%d", e.config.RPCConfig.Port), "grpc": fmt.Sprintf("%d", e.config.RPCConfig.GRPCPort), "hostIP": hostIP.String(), }, DrainObserver: e.drainObserver, } executor, err := executorclient.NewExecutor[tasklist.ShardProcessor](params) if err != nil { e.logger.Fatal("Failed to create new executor", tag.Error(err)) } e.executor = executor } func (e *matchingEngineImpl) getValidatedShardDistributorConfig() (clientcommon.Config, time.Duration) { cfg := e.ShardDistributorMatchingConfig if len(cfg.Namespaces) > 1 { e.logger.Fatal("matching service does not support multiple namespaces", tag.Value(cfg.Namespaces)) } // Get TTLReport from config, default if not configured reportTTL := _defaultSDReportTTL if len(cfg.Namespaces) == 1 && cfg.Namespaces[0].TTLReport != 0 { reportTTL = cfg.Namespaces[0].TTLReport } return cfg, reportTTL } func (e *matchingEngineImpl) String() string { // Executes taskList.String() on each task list outside of lock buf := new(bytes.Buffer) for i, tl := range e.taskListRegistry.AllManagers() { if i >= 1000 { break } fmt.Fprintf(buf, "\n%s", tl.String()) } return buf.String() } // Returns taskListManager for a task list. If not already cached gets new range from DB and // if successful creates one. func (e *matchingEngineImpl) getOrCreateTaskListManager(ctx context.Context, taskList *tasklist.Identifier, taskListKind types.TaskListKind) (tasklist.Manager, error) { // The first check is an optimization so almost all requests will have a task list manager // and return avoiding the write lock result, ok := e.taskListRegistry.ManagerByTaskListIdentifier(*taskList) excludedFromShardDistributor := e.isExcludedFromShardDistributor(taskList.GetName()) // Task lists excluded from the ShardDistributor (short-lived task lists with UUIDs) bypass // the executor/shard-processor entirely and always use local hash-ring assignment. if excludedFromShardDistributor && ok { return result, nil } if !excludedFromShardDistributor { sp, _ := e.executor.GetShardProcess(ctx, taskList.GetName()) if sp != nil && ok { return result, nil } } err := e.errIfShardOwnershipLost(ctx, taskList) if err != nil { return nil, err } // If it gets here, write lock and check again in case a task list is created between the two locks e.taskListCreationLock.Lock() if result, ok := e.taskListRegistry.ManagerByTaskListIdentifier(*taskList); ok { e.taskListCreationLock.Unlock() return result, nil } // common tagged logger logger := e.logger.WithTags( tag.WorkflowTaskListName(taskList.GetName()), tag.WorkflowTaskListType(taskList.GetType()), tag.WorkflowDomainID(taskList.GetDomainID()), ) logger.Info("Task list manager state changed", tag.LifeCycleStarting) params := tasklist.ManagerParams{ DomainCache: e.domainCache, Logger: e.logger, MetricsClient: e.metricsClient, TaskManager: e.taskManager, ClusterMetadata: e.clusterMetadata, IsolationState: e.isolationState, MatchingClient: e.matchingClient, Registry: e.taskListRegistry, TaskList: taskList, TaskListKind: taskListKind, Cfg: e.config, TimeSource: e.timeSource, CreateTime: e.timeSource.Now(), HistoryService: e.historyService, } mgr, err := tasklist.NewManager(params) if err != nil { e.taskListCreationLock.Unlock() logger.Info("Task list manager state changed", tag.LifeCycleStartFailed, tag.Error(err)) return nil, err } e.taskListRegistry.Register(*taskList, mgr) e.taskListCreationLock.Unlock() err = mgr.Start(context.Background()) if err != nil { logger.Info("Task list manager state changed", tag.LifeCycleStartFailed, tag.Error(err)) return nil, err } logger.Info("Task list manager state changed", tag.LifeCycleStarted) event.Log(event.E{ TaskListName: taskList.GetName(), TaskListKind: &taskListKind, TaskListType: taskList.GetType(), TaskInfo: persistence.TaskInfo{ DomainID: taskList.GetDomainID(), }, EventName: "TaskListManager Started", Host: e.config.HostName, }) return mgr, nil } // AddDecisionTask either delivers task directly to waiting poller or save it into task list persistence. func (e *matchingEngineImpl) AddDecisionTask( hCtx *handlerContext, request *types.AddDecisionTaskRequest, ) (*types.AddDecisionTaskResponse, error) { startT := time.Now() domainID := request.GetDomainUUID() taskListName := request.GetTaskList().GetName() taskListKind := request.GetTaskList().GetKind() taskListType := persistence.TaskListTypeDecision event.Log(event.E{ TaskListName: taskListName, TaskListKind: &taskListKind, TaskListType: taskListType, TaskInfo: persistence.TaskInfo{ DomainID: domainID, ScheduleID: request.GetScheduleID(), PartitionConfig: request.GetPartitionConfig(), }, EventName: "Received AddDecisionTask", Host: e.config.HostName, Payload: map[string]any{ "RequestForwardedFrom": request.GetForwardedFrom(), }, }) e.emitInfoOrDebugLog( domainID, "Received AddDecisionTask", tag.WorkflowTaskListName(taskListName), tag.WorkflowID(request.Execution.GetWorkflowID()), tag.WorkflowRunID(request.Execution.GetRunID()), tag.WorkflowDomainID(domainID), tag.WorkflowTaskListType(taskListType), tag.WorkflowScheduleID(request.GetScheduleID()), tag.WorkflowTaskListKind(int32(request.GetTaskList().GetKind())), ) taskListID, err := tasklist.NewIdentifier(domainID, taskListName, taskListType) if err != nil { return nil, err } // get the domainName domainName, err := e.domainCache.GetDomainName(domainID) if err != nil { return nil, err } // Only emit traffic metrics if the tasklist is not sticky and is not forwarded if int32(request.GetTaskList().GetKind()) == 0 && request.ForwardedFrom == "" { e.metricsClient.Scope(metrics.MatchingAddTaskScope).Tagged(metrics.DomainTag(domainName), metrics.TaskListTag(taskListName), metrics.TaskListTypeTag("decision_task"), metrics.MatchingHostTag(e.config.HostName)).IncCounter(metrics.CadenceTasklistRequests) e.emitInfoOrDebugLog(domainID, "Emitting tasklist counter on decision task", tag.WorkflowTaskListName(taskListName), tag.Dynamic("taskListBaseName", taskListID.GetRoot())) } tlMgr, err := e.getOrCreateTaskListManager(hCtx.Context, taskListID, taskListKind) if err != nil { return nil, err } if taskListKind == types.TaskListKindSticky { // check if the sticky worker is still available, if not, fail this request early if !tlMgr.HasPollerAfter(e.timeSource.Now().Add(-_stickyPollerUnavailableWindow)) { return nil, _stickyPollerUnavailableError } } taskInfo := &persistence.TaskInfo{ DomainID: domainID, RunID: request.Execution.GetRunID(), WorkflowID: request.Execution.GetWorkflowID(), ScheduleID: request.GetScheduleID(), ScheduleToStartTimeoutSeconds: request.GetScheduleToStartTimeoutSeconds(), CreatedTime: e.timeSource.Now(), PartitionConfig: request.GetPartitionConfig(), } syncMatched, err := tlMgr.AddTask(hCtx.Context, tasklist.AddTaskParams{ TaskInfo: taskInfo, Source: request.GetSource(), ForwardedFrom: request.GetForwardedFrom(), }) if err != nil { return nil, err } if syncMatched { hCtx.scope.RecordTimer(metrics.SyncMatchLatencyPerTaskList, time.Since(startT)) } return &types.AddDecisionTaskResponse{ PartitionConfig: tlMgr.TaskListPartitionConfig(), }, nil } // AddActivityTask either delivers task directly to waiting poller or save it into task list persistence. func (e *matchingEngineImpl) AddActivityTask( hCtx *handlerContext, request *types.AddActivityTaskRequest, ) (*types.AddActivityTaskResponse, error) { startT := time.Now() domainID := request.GetDomainUUID() taskListName := request.GetTaskList().GetName() taskListKind := request.GetTaskList().GetKind() taskListType := persistence.TaskListTypeActivity e.emitInfoOrDebugLog( domainID, "Received AddActivityTask", tag.WorkflowTaskListName(taskListName), tag.WorkflowID(request.Execution.GetWorkflowID()), tag.WorkflowRunID(request.Execution.GetRunID()), tag.WorkflowDomainID(domainID), tag.WorkflowTaskListType(taskListType), tag.WorkflowScheduleID(request.GetScheduleID()), tag.WorkflowTaskListKind(int32(request.GetTaskList().GetKind())), ) taskListID, err := tasklist.NewIdentifier(domainID, taskListName, taskListType) if err != nil { return nil, err } // get the domainName domainName, err := e.domainCache.GetDomainName(domainID) if err != nil { return nil, err } // Only emit traffic metrics if the tasklist is not sticky and is not forwarded if request.GetTaskList().GetKind() == types.TaskListKindNormal && request.ForwardedFrom == "" { e.metricsClient.Scope(metrics.MatchingAddTaskScope).Tagged(metrics.DomainTag(domainName), metrics.TaskListTag(taskListName), metrics.TaskListTypeTag("activity_task"), metrics.MatchingHostTag(e.config.HostName)).IncCounter(metrics.CadenceTasklistRequests) e.emitInfoOrDebugLog(domainID, "Emitting tasklist counter on activity task", tag.WorkflowTaskListName(taskListName), tag.Dynamic("taskListBaseName", taskListID.GetRoot())) } tlMgr, err := e.getOrCreateTaskListManager(hCtx.Context, taskListID, taskListKind) if err != nil { return nil, err } taskInfo := &persistence.TaskInfo{ DomainID: request.GetSourceDomainUUID(), RunID: request.Execution.GetRunID(), WorkflowID: request.Execution.GetWorkflowID(), ScheduleID: request.GetScheduleID(), ScheduleToStartTimeoutSeconds: request.GetScheduleToStartTimeoutSeconds(), CreatedTime: e.timeSource.Now(), PartitionConfig: request.GetPartitionConfig(), } syncMatched, err := tlMgr.AddTask(hCtx.Context, tasklist.AddTaskParams{ TaskInfo: taskInfo, Source: request.GetSource(), ForwardedFrom: request.GetForwardedFrom(), ActivityTaskDispatchInfo: request.ActivityTaskDispatchInfo, }) if err != nil { return nil, err } if syncMatched { hCtx.scope.RecordTimer(metrics.SyncMatchLatencyPerTaskList, time.Since(startT)) } return &types.AddActivityTaskResponse{ PartitionConfig: tlMgr.TaskListPartitionConfig(), }, nil } // PollForDecisionTask tries to get the decision task using exponential backoff. func (e *matchingEngineImpl) PollForDecisionTask( hCtx *handlerContext, req *types.MatchingPollForDecisionTaskRequest, ) (*types.MatchingPollForDecisionTaskResponse, error) { domainID := req.GetDomainUUID() pollerID := req.GetPollerID() request := req.PollRequest taskListName := request.GetTaskList().GetName() taskListKind := request.GetTaskList().GetKind() e.logger.Debug("Received PollForDecisionTask for taskList", tag.WorkflowTaskListName(taskListName), tag.WorkflowDomainID(domainID), ) event.Log(event.E{ TaskListName: taskListName, TaskListKind: &taskListKind, TaskListType: persistence.TaskListTypeDecision, TaskInfo: persistence.TaskInfo{ DomainID: domainID, }, EventName: "Received PollForDecisionTask", Host: e.config.HostName, Payload: map[string]any{ "RequestForwardedFrom": req.GetForwardedFrom(), "IsolationGroup": req.GetIsolationGroup(), }, }) pollLoop: for { if err := common.IsValidContext(hCtx.Context); err != nil { return nil, err } taskListID, err := tasklist.NewIdentifier(domainID, taskListName, persistence.TaskListTypeDecision) if err != nil { return nil, fmt.Errorf("couldn't create new decision tasklist %w", err) } // Add frontend generated pollerID to context so tasklistMgr can support cancellation of // long-poll when frontend calls CancelOutstandingPoll API pollerCtx := tasklist.ContextWithPollerID(hCtx.Context, pollerID) pollerCtx = tasklist.ContextWithIdentity(pollerCtx, request.GetIdentity()) pollerCtx = tasklist.ContextWithIsolationGroup(pollerCtx, req.GetIsolationGroup()) tlMgr, err := e.getOrCreateTaskListManager(hCtx.Context, taskListID, taskListKind) if err != nil { return nil, fmt.Errorf("couldn't load tasklist manager: %w", err) } startT := time.Now() // Record the start time task, err := tlMgr.GetTask(pollerCtx, nil) if err != nil { // TODO: Is empty poll the best reply for errPumpClosed? if errors.Is(err, tasklist.ErrNoTasks) || errors.Is(err, errPumpClosed) { e.logger.Debug("no decision tasks", tag.WorkflowTaskListName(taskListName), tag.WorkflowDomainID(domainID), tag.Error(err), ) event.Log(event.E{ TaskListName: taskListName, TaskListKind: &taskListKind, TaskListType: persistence.TaskListTypeDecision, TaskInfo: persistence.TaskInfo{ DomainID: domainID, }, EventName: "PollForDecisionTask returned no tasks", Host: e.config.HostName, Payload: map[string]any{ "RequestForwardedFrom": req.GetForwardedFrom(), }, }) domainName, _ := e.domainCache.GetDomainName(domainID) return &types.MatchingPollForDecisionTaskResponse{ PartitionConfig: tlMgr.TaskListPartitionConfig(), LoadBalancerHints: tlMgr.LoadBalancerHints(), AutoConfigHint: &types.AutoConfigHint{ EnableAutoConfig: e.config.EnableClientAutoConfig(domainName, taskListName, persistence.TaskListTypeDecision), PollerWaitTimeInMs: time.Since(startT).Milliseconds(), }, }, nil } return nil, fmt.Errorf("couldn't get task: %w", err) } if task.IsStarted() { event.Log(event.E{ TaskListName: taskListName, TaskListKind: &taskListKind, TaskListType: persistence.TaskListTypeDecision, TaskInfo: task.Info(), EventName: "PollForDecisionTask returning already started task", Host: e.config.HostName, }) resp := task.PollForDecisionResponse() resp.PartitionConfig = tlMgr.TaskListPartitionConfig() resp.LoadBalancerHints = tlMgr.LoadBalancerHints() resp.AutoConfigHint = task.AutoConfigHint return resp, nil // TODO: Maybe add history expose here? } e.emitForwardedFromStats(hCtx.scope, task.IsForwarded(), req.GetForwardedFrom()) if task.IsQuery() { task.Finish(nil) // this only means query task sync match succeed. // for query task, we don't need to update history to record decision task started. but we need to know // the NextEventID so front end knows what are the history events to load for this decision task. mutableStateResp, err := e.historyService.GetMutableState(hCtx.Context, &types.GetMutableStateRequest{ DomainUUID: req.DomainUUID, Execution: task.WorkflowExecution(), }) if err != nil { // will notify query client that the query task failed e.deliverQueryResult(task.Query.TaskID, &queryResult{internalError: err}) //nolint:errcheck return &types.MatchingPollForDecisionTaskResponse{ PartitionConfig: tlMgr.TaskListPartitionConfig(), LoadBalancerHints: tlMgr.LoadBalancerHints(), }, nil } isStickyEnabled := false supportsSticky := e.versionChecker.SupportsStickyQuery(mutableStateResp.GetClientImpl(), mutableStateResp.GetClientFeatureVersion()) == nil if len(mutableStateResp.StickyTaskList.GetName()) != 0 && supportsSticky { isStickyEnabled = true } resp := &types.RecordDecisionTaskStartedResponse{ PreviousStartedEventID: mutableStateResp.PreviousStartedEventID, NextEventID: mutableStateResp.NextEventID, WorkflowType: mutableStateResp.WorkflowType, StickyExecutionEnabled: isStickyEnabled, WorkflowExecutionTaskList: mutableStateResp.TaskList, BranchToken: mutableStateResp.CurrentBranchToken, HistorySize: mutableStateResp.HistorySize, } return e.createPollForDecisionTaskResponse(task, resp, hCtx.scope, tlMgr.TaskListPartitionConfig(), tlMgr.LoadBalancerHints()), nil } e.emitTaskIsolationMetrics(hCtx.scope, task.Event.PartitionConfig, req.GetIsolationGroup()) resp, err := e.recordDecisionTaskStarted(hCtx.Context, request, task) if err != nil { switch err.(type) { case *types.DomainNotActiveError: e.emitInfoOrDebugLog( task.Event.DomainID, "Decision task dropped because domain is not active", tag.WorkflowDomainID(domainID), tag.WorkflowID(task.Event.WorkflowID), tag.WorkflowRunID(task.Event.RunID), tag.WorkflowTaskListName(taskListName), tag.WorkflowScheduleID(task.Event.ScheduleID), tag.TaskID(task.Event.TaskID), ) // NOTE: There is a risk that if the domain is failed over back immediately. To prevent the decision task from being stuck, we must let // history service to regenerate the decision transfer task before dropping the task in matching if err := e.refreshWorkflowTasks(hCtx.Context, task.Event.DomainID, task.WorkflowExecution()); err != nil { task.Finish(err) } else { task.Finish(nil) } case *types.EntityNotExistsError, *types.WorkflowExecutionAlreadyCompletedError, *types.EventAlreadyStartedError: domainName, _ := e.domainCache.GetDomainName(domainID) hCtx.scope. Tagged(metrics.DomainTag(domainName)). Tagged(metrics.TaskListTag(taskListName)). IncCounter(metrics.PollDecisionTaskAlreadyStartedCounterPerTaskList) e.emitInfoOrDebugLog( task.Event.DomainID, "Duplicated decision task", tag.WorkflowDomainID(domainID), tag.WorkflowID(task.Event.WorkflowID), tag.WorkflowRunID(task.Event.RunID), tag.WorkflowTaskListName(taskListName), tag.WorkflowScheduleID(task.Event.ScheduleID), tag.TaskID(task.Event.TaskID), tag.Error(err), ) task.Finish(nil) default: e.logger.Error("unknown error recording task started", tag.WorkflowDomainID(domainID), tag.WorkflowID(task.Event.WorkflowID), tag.WorkflowRunID(task.Event.RunID), tag.WorkflowTaskListName(taskListName), tag.WorkflowScheduleID(task.Event.ScheduleID), tag.TaskID(task.Event.TaskID), tag.Error(err), ) task.Finish(err) } continue pollLoop } task.Finish(nil) event.Log(event.E{ TaskListName: taskListName, TaskListKind: &taskListKind, TaskListType: persistence.TaskListTypeDecision, TaskInfo: task.Info(), EventName: "PollForDecisionTask returning task", Host: e.config.HostName, Payload: map[string]any{ "TaskIsForwarded": task.IsForwarded(), "RequestForwardedFrom": req.GetForwardedFrom(), "Latency": time.Since(task.Info().CreatedTime).Milliseconds(), "IsolationGroup": req.GetIsolationGroup(), }, }) return e.createPollForDecisionTaskResponse(task, resp, hCtx.scope, tlMgr.TaskListPartitionConfig(), tlMgr.LoadBalancerHints()), nil } } // pollForActivityTaskOperation takes one task from the task manager, update workflow execution history, mark task as // completed and return it to user. If a task from task manager is already started, return an empty response, without // error. Timeouts handled by the timer queue. func (e *matchingEngineImpl) PollForActivityTask( hCtx *handlerContext, req *types.MatchingPollForActivityTaskRequest, ) (*types.MatchingPollForActivityTaskResponse, error) { domainID := req.GetDomainUUID() pollerID := req.GetPollerID() request := req.PollRequest taskListName := request.GetTaskList().GetName() e.logger.Debug("Received PollForActivityTask", tag.WorkflowTaskListName(taskListName), tag.WorkflowDomainID(domainID), ) pollLoop: for { err := common.IsValidContext(hCtx.Context) if err != nil { return nil, err } taskListID, err := tasklist.NewIdentifier(domainID, taskListName, persistence.TaskListTypeActivity) if err != nil { return nil, err } var maxDispatch *float64 if request.TaskListMetadata != nil { maxDispatch = request.TaskListMetadata.MaxTasksPerSecond } // Add frontend generated pollerID to context so tasklistMgr can support cancellation of // long-poll when frontend calls CancelOutstandingPoll API pollerCtx := tasklist.ContextWithPollerID(hCtx.Context, pollerID) pollerCtx = tasklist.ContextWithIdentity(pollerCtx, request.GetIdentity()) pollerCtx = tasklist.ContextWithIsolationGroup(pollerCtx, req.GetIsolationGroup()) taskListKind := request.TaskList.GetKind() tlMgr, err := e.getOrCreateTaskListManager(hCtx.Context, taskListID, taskListKind) if err != nil { return nil, fmt.Errorf("couldn't load tasklist manager: %w", err) } startT := time.Now() // Record the start time task, err := tlMgr.GetTask(pollerCtx, maxDispatch) if err != nil { // TODO: Is empty poll the best reply for errPumpClosed? if errors.Is(err, tasklist.ErrNoTasks) || errors.Is(err, errPumpClosed) { domainName, _ := e.domainCache.GetDomainName(domainID) return &types.MatchingPollForActivityTaskResponse{ PartitionConfig: tlMgr.TaskListPartitionConfig(), LoadBalancerHints: tlMgr.LoadBalancerHints(), AutoConfigHint: &types.AutoConfigHint{ EnableAutoConfig: e.config.EnableClientAutoConfig(domainName, taskListName, persistence.TaskListTypeDecision), PollerWaitTimeInMs: time.Since(startT).Milliseconds(), }, }, nil } e.logger.Error("Received unexpected err while getting task", tag.WorkflowTaskListName(taskListName), tag.WorkflowDomainID(domainID), tag.Error(err), ) return nil, err } if task.IsStarted() { // task received from remote is already started. So, simply forward the response resp := task.PollForActivityResponse() resp.PartitionConfig = tlMgr.TaskListPartitionConfig() resp.LoadBalancerHints = tlMgr.LoadBalancerHints() resp.AutoConfigHint = task.AutoConfigHint return resp, nil } e.emitForwardedFromStats(hCtx.scope, task.IsForwarded(), req.GetForwardedFrom()) e.emitTaskIsolationMetrics(hCtx.scope, task.Event.PartitionConfig, req.GetIsolationGroup()) if task.ActivityTaskDispatchInfo != nil { task.Finish(nil) return e.createSyncMatchPollForActivityTaskResponse(task, task.ActivityTaskDispatchInfo, tlMgr.TaskListPartitionConfig(), tlMgr.LoadBalancerHints()), nil } resp, err := e.recordActivityTaskStarted(hCtx.Context, request, task) if err != nil { switch err.(type) { case *types.DomainNotActiveError: e.emitInfoOrDebugLog( task.Event.DomainID, "Decision task dropped because domain is not active", tag.WorkflowDomainID(domainID), tag.WorkflowID(task.Event.WorkflowID), tag.WorkflowRunID(task.Event.RunID), tag.WorkflowTaskListName(taskListName), tag.WorkflowScheduleID(task.Event.ScheduleID), tag.TaskID(task.Event.TaskID), ) // NOTE: There is a risk that if the domain is failed over back immediately. To prevent the decision task from being stuck, we must let // history service to regenerate the decision transfer task before dropping the task in matching if err := e.refreshWorkflowTasks(hCtx.Context, task.Event.DomainID, task.WorkflowExecution()); err != nil { task.Finish(err) } else { task.Finish(nil) } case *types.EntityNotExistsError, *types.WorkflowExecutionAlreadyCompletedError, *types.EventAlreadyStartedError: domainName, _ := e.domainCache.GetDomainName(domainID) hCtx.scope. Tagged(metrics.DomainTag(domainName)). Tagged(metrics.TaskListTag(taskListName)). IncCounter(metrics.PollActivityTaskAlreadyStartedCounterPerTaskList) e.emitInfoOrDebugLog( task.Event.DomainID, "Duplicated activity task", tag.WorkflowDomainID(domainID), tag.WorkflowID(task.Event.WorkflowID), tag.WorkflowRunID(task.Event.RunID), tag.WorkflowTaskListName(taskListName), tag.WorkflowScheduleID(task.Event.ScheduleID), tag.TaskID(task.Event.TaskID), ) task.Finish(nil) default: task.Finish(err) } continue pollLoop } task.Finish(nil) return e.createPollForActivityTaskResponse(task, resp, hCtx.scope, tlMgr.TaskListPartitionConfig(), tlMgr.LoadBalancerHints()), nil } } func (e *matchingEngineImpl) createSyncMatchPollForActivityTaskResponse( task *tasklist.InternalTask, activityTaskDispatchInfo *types.ActivityTaskDispatchInfo, partitionConfig *types.TaskListPartitionConfig, loadBalancerHints *types.LoadBalancerHints, ) *types.MatchingPollForActivityTaskResponse { scheduledEvent := activityTaskDispatchInfo.ScheduledEvent attributes := scheduledEvent.ActivityTaskScheduledEventAttributes response := &types.MatchingPollForActivityTaskResponse{} response.ActivityID = attributes.ActivityID response.ActivityType = attributes.ActivityType response.Header = attributes.Header response.Input = attributes.Input response.WorkflowExecution = task.WorkflowExecution() response.ScheduledTimestampOfThisAttempt = activityTaskDispatchInfo.ScheduledTimestampOfThisAttempt response.ScheduledTimestamp = scheduledEvent.Timestamp response.ScheduleToCloseTimeoutSeconds = attributes.ScheduleToCloseTimeoutSeconds response.StartedTimestamp = activityTaskDispatchInfo.StartedTimestamp response.StartToCloseTimeoutSeconds = attributes.StartToCloseTimeoutSeconds response.HeartbeatTimeoutSeconds = attributes.HeartbeatTimeoutSeconds token := &common.TaskToken{ DomainID: task.Event.DomainID, WorkflowID: task.Event.WorkflowID, WorkflowType: activityTaskDispatchInfo.WorkflowType.GetName(), RunID: task.Event.RunID, ScheduleID: task.Event.ScheduleID, ScheduleAttempt: common.Int64Default(activityTaskDispatchInfo.Attempt), ActivityID: attributes.GetActivityID(), ActivityType: attributes.GetActivityType().GetName(), } response.TaskToken, _ = e.tokenSerializer.Serialize(token) response.Attempt = int32(token.ScheduleAttempt) response.HeartbeatDetails = activityTaskDispatchInfo.HeartbeatDetails response.WorkflowType = activityTaskDispatchInfo.WorkflowType response.WorkflowDomain = activityTaskDispatchInfo.WorkflowDomain response.PartitionConfig = partitionConfig response.LoadBalancerHints = loadBalancerHints response.AutoConfigHint = task.AutoConfigHint return response } // QueryWorkflow creates a DecisionTask with query data, send it through sync match channel, wait for that DecisionTask // to be processed by worker, and then return the query result. func (e *matchingEngineImpl) QueryWorkflow( hCtx *handlerContext, queryRequest *types.MatchingQueryWorkflowRequest, ) (*types.MatchingQueryWorkflowResponse, error) { domainID := queryRequest.GetDomainUUID() taskListName := queryRequest.GetTaskList().GetName() taskListKind := queryRequest.GetTaskList().GetKind() taskListID, err := tasklist.NewIdentifier(domainID, taskListName, persistence.TaskListTypeDecision) if err != nil { return nil, err } tlMgr, err := e.getOrCreateTaskListManager(hCtx.Context, taskListID, taskListKind) if err != nil { return nil, err } if taskListKind == types.TaskListKindSticky { // check if the sticky worker is still available, if not, fail this request early if !tlMgr.HasPollerAfter(e.timeSource.Now().Add(-_stickyPollerUnavailableWindow)) { return nil, _stickyPollerUnavailableError } } taskID := uuid.New() queryResultCh := make(chan *queryResult, 1) e.lockableQueryTaskMap.put(taskID, queryResultCh) defer e.lockableQueryTaskMap.delete(taskID) resp, err := tlMgr.DispatchQueryTask(hCtx.Context, taskID, queryRequest) // if get response or error it means that query task was handled by forwarding to another matching host // this remote host's result can be returned directly if err != nil { return nil, err } if resp != nil { resp.PartitionConfig = tlMgr.TaskListPartitionConfig() return resp, nil } // if get here it means that dispatch of query task has occurred locally // must wait on result channel to get query result resp, err = e.waitForQueryResult(hCtx, queryRequest.GetQueryRequest().GetQueryConsistencyLevel() == types.QueryConsistencyLevelStrong, queryResultCh) if err != nil { return nil, err } resp.PartitionConfig = tlMgr.TaskListPartitionConfig() return resp, nil } func (e *matchingEngineImpl) waitForQueryResult(hCtx *handlerContext, isStrongConsistencyQuery bool, queryResultCh <-chan *queryResult) (*types.MatchingQueryWorkflowResponse, error) { select { case result := <-queryResultCh: if result.internalError != nil { return nil, result.internalError } workerResponse := result.workerResponse // if query was intended as consistent query check to see if worker supports consistent query if isStrongConsistencyQuery { if err := e.versionChecker.SupportsConsistentQuery( workerResponse.GetCompletedRequest().GetWorkerVersionInfo().GetImpl(), workerResponse.GetCompletedRequest().GetWorkerVersionInfo().GetFeatureVersion()); err != nil { return nil, err } } switch workerResponse.GetCompletedRequest().GetCompletedType() { case types.QueryTaskCompletedTypeCompleted: return &types.MatchingQueryWorkflowResponse{QueryResult: workerResponse.GetCompletedRequest().GetQueryResult()}, nil case types.QueryTaskCompletedTypeFailed: return nil, &types.QueryFailedError{Message: workerResponse.GetCompletedRequest().GetErrorMessage()} default: return nil, &types.InternalServiceError{Message: "unknown query completed type"} } case <-hCtx.Done(): return nil, hCtx.Err() } } func (e *matchingEngineImpl) RespondQueryTaskCompleted(hCtx *handlerContext, request *types.MatchingRespondQueryTaskCompletedRequest) error { if err := e.deliverQueryResult(request.GetTaskID(), &queryResult{workerResponse: request}); err != nil { hCtx.scope.IncCounter(metrics.RespondQueryTaskFailedPerTaskListCounter) return err } return nil } func (e *matchingEngineImpl) deliverQueryResult(taskID string, queryResult *queryResult) error { queryResultCh, ok := e.lockableQueryTaskMap.get(taskID) if !ok { return &types.InternalServiceError{Message: "query task not found, or already expired"} } queryResultCh <- queryResult return nil } func (e *matchingEngineImpl) CancelOutstandingPoll( hCtx *handlerContext, request *types.CancelOutstandingPollRequest, ) error { domainID := request.GetDomainUUID() taskListType := int(request.GetTaskListType()) taskListName := request.GetTaskList().GetName() taskListKind := request.GetTaskList().GetKind() pollerID := request.GetPollerID() taskListID, err := tasklist.NewIdentifier(domainID, taskListName, taskListType) if err != nil { return err } tlMgr, err := e.getOrCreateTaskListManager(hCtx.Context, taskListID, taskListKind) if err != nil { return err } tlMgr.CancelPoller(pollerID) return nil } func (e *matchingEngineImpl) DescribeTaskList( hCtx *handlerContext, request *types.MatchingDescribeTaskListRequest, ) (*types.DescribeTaskListResponse, error) { domainID := request.GetDomainUUID() taskListType := persistence.TaskListTypeDecision if request.DescRequest.GetTaskListType() == types.TaskListTypeActivity { taskListType = persistence.TaskListTypeActivity } taskListName := request.GetDescRequest().GetTaskList().GetName() taskListKind := request.GetDescRequest().GetTaskList().GetKind() taskListID, err := tasklist.NewIdentifier(domainID, taskListName, taskListType) if err != nil { return nil, err } tlMgr, err := e.getOrCreateTaskListManager(hCtx.Context, taskListID, taskListKind) if err != nil { return nil, err } return tlMgr.DescribeTaskList(request.DescRequest.GetIncludeTaskListStatus()), nil } func (e *matchingEngineImpl) ListTaskListPartitions( hCtx *handlerContext, request *types.MatchingListTaskListPartitionsRequest, ) (*types.ListTaskListPartitionsResponse, error) { activityTaskListInfo, err := e.listTaskListPartitions(request, persistence.TaskListTypeActivity) if err != nil { return nil, err } decisionTaskListInfo, err := e.listTaskListPartitions(request, persistence.TaskListTypeDecision) if err != nil { return nil, err } resp := &types.ListTaskListPartitionsResponse{ ActivityTaskListPartitions: activityTaskListInfo, DecisionTaskListPartitions: decisionTaskListInfo, } return resp, nil } func (e *matchingEngineImpl) listTaskListPartitions( request *types.MatchingListTaskListPartitionsRequest, taskListType int, ) ([]*types.TaskListPartitionMetadata, error) { partitions, err := e.getAllPartitions( request, taskListType, ) if err != nil { return nil, err } var partitionHostInfo []*types.TaskListPartitionMetadata for _, partition := range partitions { host, _ := e.getHostInfo(partition) partitionHostInfo = append(partitionHostInfo, &types.TaskListPartitionMetadata{ Key: partition, OwnerHostName: host, }) } return partitionHostInfo, nil } func (e *matchingEngineImpl) getTaskListsByDomainAndKind(domainID string, taskListKind *types.TaskListKind) *types.GetTaskListsByDomainResponse { decisionTaskListMap := make(map[string]*types.DescribeTaskListResponse) activityTaskListMap := make(map[string]*types.DescribeTaskListResponse) for _, tlm := range e.taskListRegistry.ManagersByDomainID(domainID) { if taskListKind == nil || tlm.GetTaskListKind() == *taskListKind { tl := tlm.TaskListID() if types.TaskListType(tl.GetType()) == types.TaskListTypeDecision { decisionTaskListMap[tl.GetRoot()] = tlm.DescribeTaskList(false) } else { activityTaskListMap[tl.GetRoot()] = tlm.DescribeTaskList(false) } } } return &types.GetTaskListsByDomainResponse{ DecisionTaskListMap: decisionTaskListMap, ActivityTaskListMap: activityTaskListMap, } } func (e *matchingEngineImpl) GetTaskListsByDomain( hCtx *handlerContext, request *types.GetTaskListsByDomainRequest, ) (*types.GetTaskListsByDomainResponse, error) { domainID, err := e.domainCache.GetDomainID(request.GetDomain()) if err != nil { return nil, err } tlKind := types.TaskListKindNormal.Ptr() if e.config.EnableReturnAllTaskListKinds() { tlKind = nil } return e.getTaskListsByDomainAndKind(domainID, tlKind), nil } func (e *matchingEngineImpl) UpdateTaskListPartitionConfig( hCtx *handlerContext, request *types.MatchingUpdateTaskListPartitionConfigRequest, ) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) { domainID := request.DomainUUID taskListName := request.TaskList.GetName() taskListKind := request.TaskList.GetKind() taskListType := persistence.TaskListTypeDecision if request.GetTaskListType() == types.TaskListTypeActivity { taskListType = persistence.TaskListTypeActivity } domainName, err := e.domainCache.GetDomainName(domainID) if err != nil { return nil, err } if e.config.EnableAdaptiveScaler(domainName, taskListName, taskListType) { return nil, &types.BadRequestError{Message: "Manual update is not allowed because adaptive scaler is enabled."} } if taskListKind != types.TaskListKindNormal { return nil, &types.BadRequestError{Message: "Only normal tasklist's partition config can be updated."} } if request.PartitionConfig == nil { return nil, &types.BadRequestError{Message: "Task list partition config is not set in the request."} } taskListID, err := tasklist.NewIdentifier(domainID, taskListName, taskListType) if err != nil { return nil, err } if !taskListID.IsRoot() { return nil, &types.BadRequestError{Message: "Only root partition's partition config can be updated."} } tlMgr, err := e.getOrCreateTaskListManager(hCtx.Context, taskListID, taskListKind) if err != nil { return nil, err } err = tlMgr.UpdateTaskListPartitionConfig(hCtx.Context, request.PartitionConfig) if err != nil { return nil, err } return &types.MatchingUpdateTaskListPartitionConfigResponse{}, nil } func (e *matchingEngineImpl) RefreshTaskListPartitionConfig( hCtx *handlerContext, request *types.MatchingRefreshTaskListPartitionConfigRequest, ) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) { domainID := request.DomainUUID taskListName := request.TaskList.GetName() taskListKind := request.TaskList.GetKind() taskListType := persistence.TaskListTypeDecision if request.GetTaskListType() == types.TaskListTypeActivity { taskListType = persistence.TaskListTypeActivity } if taskListKind != types.TaskListKindNormal { return nil, &types.BadRequestError{Message: "Only normal tasklist's partition config can be updated."} } taskListID, err := tasklist.NewIdentifier(domainID, taskListName, taskListType) if err != nil { return nil, err } if taskListID.IsRoot() && request.PartitionConfig != nil { return nil, &types.BadRequestError{Message: "PartitionConfig must be nil for root partition."} } tlMgr, err := e.getOrCreateTaskListManager(hCtx.Context, taskListID, taskListKind) if err != nil { return nil, err } err = tlMgr.RefreshTaskListPartitionConfig(hCtx.Context, request.PartitionConfig) if err != nil { return nil, err } return &types.MatchingRefreshTaskListPartitionConfigResponse{}, nil } func (e *matchingEngineImpl) getHostInfo(partitionKey string) (string, error) { host, err := e.membershipResolver.Lookup(service.Matching, partitionKey) if err != nil { return "", err } return host.GetAddress(), nil } func (e *matchingEngineImpl) getAllPartitions( request *types.MatchingListTaskListPartitionsRequest, taskListType int, ) ([]string, error) { domainID, err := e.domainCache.GetDomainID(request.GetDomain()) if err != nil { return nil, err } taskList := request.GetTaskList() taskListID, err := tasklist.NewIdentifier(domainID, taskList.GetName(), taskListType) if err != nil { return nil, err } rootPartition := taskListID.GetRoot() partitionKeys := []string{rootPartition} n := e.config.NumTasklistWritePartitions(request.GetDomain(), rootPartition, taskListType) for i := 1; i < n; i++ { partitionKeys = append(partitionKeys, fmt.Sprintf("%v%v/%v", constants.ReservedTaskListPrefix, rootPartition, i)) } return partitionKeys, nil } func (e *matchingEngineImpl) unloadTaskList(tlMgr tasklist.Manager) { unregistered := e.taskListRegistry.Unregister(tlMgr) if unregistered { tlMgr.Stop() } } // Populate the decision task response based on context and scheduled/started events. func (e *matchingEngineImpl) createPollForDecisionTaskResponse( task *tasklist.InternalTask, historyResponse *types.RecordDecisionTaskStartedResponse, scope metrics.Scope, partitionConfig *types.TaskListPartitionConfig, loadBalancerHints *types.LoadBalancerHints, ) *types.MatchingPollForDecisionTaskResponse { var token []byte if task.IsQuery() { // for a query task queryRequest := task.Query.Request execution := task.WorkflowExecution() taskToken := &common.QueryTaskToken{ DomainID: queryRequest.DomainUUID, WorkflowID: execution.WorkflowID, RunID: execution.RunID, TaskList: queryRequest.TaskList.Name, TaskID: task.Query.TaskID, } token, _ = e.tokenSerializer.SerializeQueryTaskToken(taskToken) } else { taskToken := &common.TaskToken{ DomainID: task.Event.DomainID, WorkflowID: task.Event.WorkflowID, RunID: task.Event.RunID, ScheduleID: historyResponse.GetScheduledEventID(), ScheduleAttempt: historyResponse.GetAttempt(), } token, _ = e.tokenSerializer.Serialize(taskToken) if task.ResponseC == nil { scope.RecordTimer(metrics.AsyncMatchLatencyPerTaskList, time.Since(task.Event.CreatedTime)) } } response := common.CreateMatchingPollForDecisionTaskResponse(historyResponse, task.WorkflowExecution(), token) if task.Query != nil { response.Query = task.Query.Request.QueryRequest.Query } response.BacklogCountHint = task.BacklogCountHint response.PartitionConfig = partitionConfig response.LoadBalancerHints = loadBalancerHints response.AutoConfigHint = task.AutoConfigHint return response } // Populate the activity task response based on context and scheduled/started events. func (e *matchingEngineImpl) createPollForActivityTaskResponse( task *tasklist.InternalTask, historyResponse *types.RecordActivityTaskStartedResponse, scope metrics.Scope, partitionConfig *types.TaskListPartitionConfig, loadBalancerHints *types.LoadBalancerHints, ) *types.MatchingPollForActivityTaskResponse { scheduledEvent := historyResponse.ScheduledEvent if scheduledEvent.ActivityTaskScheduledEventAttributes == nil { panic("GetActivityTaskScheduledEventAttributes is not set") } attributes := scheduledEvent.ActivityTaskScheduledEventAttributes if attributes.ActivityID == "" { panic("ActivityTaskScheduledEventAttributes.ActivityID is not set") } if task.ResponseC == nil { scope.RecordTimer(metrics.AsyncMatchLatencyPerTaskList, time.Since(task.Event.CreatedTime)) } response := &types.MatchingPollForActivityTaskResponse{} response.ActivityID = attributes.ActivityID response.ActivityType = attributes.ActivityType response.Header = attributes.Header response.Input = attributes.Input response.WorkflowExecution = task.WorkflowExecution() response.ScheduledTimestampOfThisAttempt = historyResponse.ScheduledTimestampOfThisAttempt response.ScheduledTimestamp = scheduledEvent.Timestamp response.ScheduleToCloseTimeoutSeconds = attributes.ScheduleToCloseTimeoutSeconds response.StartedTimestamp = historyResponse.StartedTimestamp response.StartToCloseTimeoutSeconds = attributes.StartToCloseTimeoutSeconds response.HeartbeatTimeoutSeconds = attributes.HeartbeatTimeoutSeconds token := &common.TaskToken{ DomainID: task.Event.DomainID, WorkflowID: task.Event.WorkflowID, WorkflowType: historyResponse.WorkflowType.GetName(), RunID: task.Event.RunID, ScheduleID: task.Event.ScheduleID, ScheduleAttempt: historyResponse.GetAttempt(), ActivityID: attributes.GetActivityID(), ActivityType: attributes.GetActivityType().GetName(), } response.TaskToken, _ = e.tokenSerializer.Serialize(token) response.Attempt = int32(token.ScheduleAttempt) response.HeartbeatDetails = historyResponse.HeartbeatDetails response.WorkflowType = historyResponse.WorkflowType response.WorkflowDomain = historyResponse.WorkflowDomain response.PartitionConfig = partitionConfig response.LoadBalancerHints = loadBalancerHints response.AutoConfigHint = task.AutoConfigHint return response } func (e *matchingEngineImpl) recordDecisionTaskStarted( ctx context.Context, pollReq *types.PollForDecisionTaskRequest, task *tasklist.InternalTask, ) (*types.RecordDecisionTaskStartedResponse, error) { request := &types.RecordDecisionTaskStartedRequest{ DomainUUID: task.Event.DomainID, WorkflowExecution: task.WorkflowExecution(), ScheduleID: task.Event.ScheduleID, TaskID: task.Event.TaskID, RequestID: uuid.New(), PollRequest: pollReq, } var resp *types.RecordDecisionTaskStartedResponse op := func(ctx context.Context) error { var err error resp, err = e.historyService.RecordDecisionTaskStarted(ctx, request) return err } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(recordTaskStartedRetryPolicy), backoff.WithRetryableError(isMatchingRetryableError), backoff.WithOperationTimeout(_recordTaskStartedTimeout), backoff.WithContextExpiration(), ) err := throttleRetry.Do(ctx, op) return resp, err } func (e *matchingEngineImpl) recordActivityTaskStarted( ctx context.Context, pollReq *types.PollForActivityTaskRequest, task *tasklist.InternalTask, ) (*types.RecordActivityTaskStartedResponse, error) { request := &types.RecordActivityTaskStartedRequest{ DomainUUID: task.Event.DomainID, WorkflowExecution: task.WorkflowExecution(), ScheduleID: task.Event.ScheduleID, TaskID: task.Event.TaskID, RequestID: uuid.New(), PollRequest: pollReq, } var resp *types.RecordActivityTaskStartedResponse op := func(ctx context.Context) error { var err error resp, err = e.historyService.RecordActivityTaskStarted(ctx, request) return err } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(recordTaskStartedRetryPolicy), backoff.WithRetryableError(isMatchingRetryableError), backoff.WithOperationTimeout(_recordTaskStartedTimeout), backoff.WithContextExpiration(), ) err := throttleRetry.Do(ctx, op) return resp, err } func (e *matchingEngineImpl) refreshWorkflowTasks( ctx context.Context, domainID string, workflowExecution *types.WorkflowExecution, ) error { request := &types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: domainID, Request: &types.RefreshWorkflowTasksRequest{ Execution: workflowExecution, }, } err := e.historyService.RefreshWorkflowTasks(ctx, request) if err != nil { var e *types.EntityNotExistsError if errors.As(err, &e) { return nil } return err } return nil } func (e *matchingEngineImpl) emitForwardedFromStats( scope metrics.Scope, isTaskForwarded bool, pollForwardedFrom string, ) { isPollForwarded := len(pollForwardedFrom) > 0 switch { case isTaskForwarded && isPollForwarded: scope.IncCounter(metrics.RemoteToRemoteMatchPerTaskListCounter) case isTaskForwarded: scope.IncCounter(metrics.RemoteToLocalMatchPerTaskListCounter) case isPollForwarded: scope.IncCounter(metrics.LocalToRemoteMatchPerTaskListCounter) default: scope.IncCounter(metrics.LocalToLocalMatchPerTaskListCounter) } } func (e *matchingEngineImpl) emitTaskIsolationMetrics( scope metrics.Scope, partitionConfig map[string]string, pollerIsolationGroup string, ) { if currentGroup, ok := partitionConfig[isolationgroup.GroupKey]; ok { originalGroup, ok := partitionConfig[isolationgroup.OriginalGroupKey] if !ok { originalGroup = currentGroup } scope.Tagged(metrics.IsolationGroupTag(originalGroup), metrics.PollerIsolationGroupTag(pollerIsolationGroup)).IncCounter(metrics.IsolationTaskMatchPerTaskListCounter) if originalGroup == pollerIsolationGroup { scope.Tagged(metrics.IsolationGroupTag(originalGroup)).IncCounter(metrics.IsolationSuccessPerTaskListCounter) } } } func (e *matchingEngineImpl) emitInfoOrDebugLog( domainID string, msg string, tags ...tag.Tag, ) { if e.config.EnableDebugMode && e.config.EnableTaskInfoLogByDomainID(domainID) { e.logger.Info(msg, tags...) } else { e.logger.Debug(msg, tags...) } } func (e *matchingEngineImpl) errIfShardOwnershipLost(ctx context.Context, taskList *tasklist.Identifier) error { if !e.config.EnableTasklistOwnershipGuard() { return nil } self, err := e.membershipResolver.WhoAmI() if err != nil { return fmt.Errorf("failed to lookup self im membership: %w", err) } newNotOwnedByHostError := func(newOwner string) error { return cadence_errors.NewTaskListNotOwnedByHostError( newOwner, self.Identity(), taskList.GetName(), ) } if e.isShuttingDown() { e.logger.Warn("request to get tasklist is being rejected because engine is shutting down", tag.WorkflowDomainID(taskList.GetDomainID()), tag.WorkflowTaskListType(taskList.GetType()), tag.WorkflowTaskListName(taskList.GetName()), ) return newNotOwnedByHostError("not known") } // Task lists excluded from the ShardDistributor bypass the executor entirely and rely on // the local hash-ring for ownership, so skip the SD-based ownership check for them. if !e.isExcludedFromShardDistributor(taskList.GetName()) { // We have a shard-processor shared by all the task lists with the same name. // For now there is no 1:1 mapping between shards and tasklists. (#tasklists >= #shards) sp, err := e.executor.GetShardProcess(ctx, taskList.GetName()) if err != nil { if errors.Is(err, executorclient.ErrShardProcessNotFound) { // The shard is not assigned to this host – treat it as an ownership loss, // not an internal error. return newNotOwnedByHostError("not known") } return fmt.Errorf("failed to lookup ownership in SD: %w", err) } if sp == nil { return newNotOwnedByHostError("not known") } return nil } // Defensive check to make sure we actually own the task list // If we try to create a task list manager for a task list that is not owned by us, return an error // The new task list manager will steal the task list from the current owner, which should only happen if // the task list is owned by the current host. taskListOwner, err := e.membershipResolver.Lookup(service.Matching, taskList.GetName()) if err != nil { return fmt.Errorf("failed to lookup task list owner: %w", err) } if taskListOwner.Identity() != self.Identity() { e.logger.Warn("Request to get tasklist is being rejected because engine does not own this shard", tag.WorkflowDomainID(taskList.GetDomainID()), tag.WorkflowTaskListType(taskList.GetType()), tag.WorkflowTaskListName(taskList.GetName()), ) return newNotOwnedByHostError(taskListOwner.Identity()) } return nil } func (e *matchingEngineImpl) isShuttingDown() bool { select { case <-e.shutdown: return true default: return false } } // isExcludedFromShardDistributor returns true if the task list should bypass the // ShardDistributor and executor, and instead rely on local hash-ring assignment. // This applies to short-lived task lists (e.g. sticky or bits task lists whose names // contain a UUID) when the corresponding feature flag is enabled. func (e *matchingEngineImpl) isExcludedFromShardDistributor(taskListName string) bool { excludeTaskList := membership.TaskListExcludedFromShardDistributor(taskListName, uint64(e.config.PercentageOnboardedToShardManager()), e.config.ExcludeShortLivedTaskListsFromShardManager()) return excludeTaskList } func (e *matchingEngineImpl) domainChangeCallback(nextDomains []*cache.DomainCacheEntry) { newFailoverNotificationVersion := e.failoverNotificationVersion for _, domain := range nextDomains { if domain.GetFailoverNotificationVersion() > newFailoverNotificationVersion { newFailoverNotificationVersion = domain.GetFailoverNotificationVersion() } if !isDomainEligibleToDisconnectPollers(domain, e.failoverNotificationVersion) { continue } taskListNormal := types.TaskListKindNormal resp := e.getTaskListsByDomainAndKind(domain.GetInfo().ID, &taskListNormal) for taskListName := range resp.DecisionTaskListMap { e.disconnectTaskListPollersAfterDomainFailover(taskListName, domain, persistence.TaskListTypeDecision, taskListNormal) } for taskListName := range resp.ActivityTaskListMap { e.disconnectTaskListPollersAfterDomainFailover(taskListName, domain, persistence.TaskListTypeActivity, taskListNormal) } taskListSticky := types.TaskListKindSticky resp = e.getTaskListsByDomainAndKind(domain.GetInfo().ID, &taskListSticky) for taskListName := range resp.DecisionTaskListMap { e.disconnectTaskListPollersAfterDomainFailover(taskListName, domain, persistence.TaskListTypeDecision, taskListSticky) } } e.failoverNotificationVersion = newFailoverNotificationVersion } func (e *matchingEngineImpl) registerDomainFailoverCallback() { catchUpFn := func(domainCache cache.DomainCache, _ cache.PrepareCallbackFn, _ cache.CallbackFn) { for _, domain := range domainCache.GetAllDomain() { if domain.GetFailoverNotificationVersion() > e.failoverNotificationVersion { e.failoverNotificationVersion = domain.GetFailoverNotificationVersion() } } } e.domainCache.RegisterDomainChangeCallback( service.Matching, catchUpFn, func() {}, e.domainChangeCallback) } func (e *matchingEngineImpl) unregisterDomainFailoverCallback() { e.domainCache.UnregisterDomainChangeCallback(service.Matching) } func (e *matchingEngineImpl) disconnectTaskListPollersAfterDomainFailover(taskListName string, domain *cache.DomainCacheEntry, taskType int, taskListKind types.TaskListKind) { taskList, err := tasklist.NewIdentifier(domain.GetInfo().ID, taskListName, taskType) if err != nil { return } tlMgr, err := e.getOrCreateTaskListManager(context.Background(), taskList, taskListKind) if err != nil { e.logger.Error("Couldn't load tasklist manager", tag.Error(err)) return } err = tlMgr.ReleaseBlockedPollers() if err != nil { e.logger.Error("Couldn't disconnect tasklist pollers after domain failover", tag.Error(err), tag.WorkflowDomainID(domain.GetInfo().ID), tag.WorkflowDomainName(domain.GetInfo().Name), tag.WorkflowTaskListName(taskListName), tag.WorkflowTaskListType(taskType), ) return } } func (m *lockableQueryTaskMap) put(key string, value chan *queryResult) { m.Lock() defer m.Unlock() m.queryTaskMap[key] = value } func (m *lockableQueryTaskMap) get(key string) (chan *queryResult, bool) { m.RLock() defer m.RUnlock() result, ok := m.queryTaskMap[key] return result, ok } func (m *lockableQueryTaskMap) delete(key string) { m.Lock() defer m.Unlock() delete(m.queryTaskMap, key) } func isMatchingRetryableError(err error) bool { switch err.(type) { case *types.EntityNotExistsError, *types.WorkflowExecutionAlreadyCompletedError, *types.EventAlreadyStartedError, *types.DomainNotActiveError: return false } return true } func isDomainEligibleToDisconnectPollers(domain *cache.DomainCacheEntry, currentVersion int64) bool { return domain.IsGlobalDomain() && domain.GetReplicationConfig() != nil && !domain.GetReplicationConfig().IsActiveActive() && domain.GetFailoverNotificationVersion() > currentVersion } ================================================ FILE: service/matching/handler/engine_integration_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "context" "errors" "fmt" "net/http" "strings" "sync" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/goleak" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/activecluster" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" commonConfig "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/isolationgroup/defaultisolationgroupstate" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/tasklist" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" "github.com/uber/cadence/service/sharddistributor/client/executorclient" sdconfig "github.com/uber/cadence/service/sharddistributor/config" ) type ( matchingEngineSuite struct { suite.Suite controller *gomock.Controller mockHistoryClient *history.MockClient mockMatchingClient *matching.MockClient mockDomainCache *cache.MockDomainCache mockMembershipResolver *membership.MockResolver mockIsolationStore *dynamicconfig.MockClient mockShardExecutorClient *executorclient.MockClient mockExecutor *executorclient.MockExecutor[tasklist.ShardProcessor] ShardDistributorMatchingConfig clientcommon.Config matchingEngine *matchingEngineImpl taskManager *tasklist.TestTaskManager isolationState isolationgroup.State mockExecutionManager *mocks.ExecutionManager mockTimeSource clock.MockedTimeSource logger log.Logger handlerContext *handlerContext mockActiveClusterMgr activecluster.MockManager sync.Mutex } ) const ( _minBurst = 10000 matchingTestDomainName = "matching-test" matchingTestTaskList = "matching-test-tasklist" returnEmptyTaskTimeBudget = time.Second _defaultTaskDispatchRPS = 100000.0 defaultTaskBufferIsolationGroup = "" ) var errRemoteSyncMatchFailed = &types.RemoteSyncMatchedError{Message: "remote sync match failed"} func TestMatchingEngineSuite(t *testing.T) { s := new(matchingEngineSuite) suite.Run(t, s) } func (s *matchingEngineSuite) SetupSuite() { // http.Handle("/test/tasks", http.HandlerFunc(s.TasksHandler)) } // Renders content of taskManager and matchingEngine when called at http://localhost:6060/test/tasks // Uncomment HTTP server initialization in SetupSuite method to enable. func (s *matchingEngineSuite) TasksHandler(w http.ResponseWriter, r *http.Request) { s.Lock() defer s.Unlock() w.Header().Set("Content-Type", "text/plain; charset=utf-8") fmt.Fprintf(w, "%v\n", s.taskManager) fmt.Fprintf(w, "%v\n", s.matchingEngine) } func (s *matchingEngineSuite) TearDownSuite() { } func (s *matchingEngineSuite) SetupTest() { s.Lock() defer s.Unlock() s.logger = testlogger.New(s.Suite.T()).WithTags(tag.Dynamic("test-name", s.T().Name())) tlKindNormal := types.TaskListKindNormal s.mockExecutionManager = &mocks.ExecutionManager{} s.controller = gomock.NewController(s.T()) s.mockHistoryClient = history.NewMockClient(s.controller) s.mockMatchingClient = matching.NewMockClient(s.controller) s.mockTimeSource = clock.NewMockedTimeSourceAt(time.Now()) s.taskManager = tasklist.NewTestTaskManager(s.T(), s.logger, s.mockTimeSource) s.mockDomainCache = cache.NewMockDomainCache(s.controller) s.mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.CreateDomainCacheEntry(matchingTestDomainName), nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.CreateDomainCacheEntry(matchingTestDomainName), nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(matchingTestDomainName, nil).AnyTimes() s.mockDomainCache.EXPECT().RegisterDomainChangeCallback(service.Matching, gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() s.mockDomainCache.EXPECT().UnregisterDomainChangeCallback(service.Matching).AnyTimes() s.mockMembershipResolver = membership.NewMockResolver(s.controller) s.mockMembershipResolver.EXPECT().Lookup(gomock.Any(), gomock.Any()).Return(membership.HostInfo{}, nil).AnyTimes() s.mockMembershipResolver.EXPECT().WhoAmI().Return(membership.HostInfo{}, nil).AnyTimes() s.mockMembershipResolver.EXPECT().Subscribe(service.Matching, "matching-engine", gomock.Any()).AnyTimes() s.mockIsolationStore = dynamicconfig.NewMockClient(s.controller) dcClient := dynamicconfig.NewInMemoryClient() s.NoError(dcClient.UpdateValue(dynamicproperties.EnableTasklistIsolation, true)) dc := dynamicconfig.NewCollection(dcClient, s.logger) s.isolationState, _ = defaultisolationgroupstate.NewDefaultIsolationGroupStateWatcherWithConfigStoreClient(s.logger, dc, s.mockDomainCache, s.mockIsolationStore, metrics.NewNoopMetricsClient(), getIsolationGroupsHelper) s.handlerContext = newHandlerContext( context.Background(), matchingTestDomainName, &types.TaskList{Name: matchingTestTaskList, Kind: &tlKindNormal}, metrics.NewClient(tally.NoopScope, metrics.Matching, metrics.HistogramMigration{}), metrics.MatchingTaskListMgrScope, testlogger.New(s.Suite.T()), ) s.mockActiveClusterMgr = *activecluster.NewMockManager(s.controller) s.matchingEngine = s.newMatchingEngine(defaultTestConfig(), s.taskManager) s.mockExecutor = s.matchingEngine.executor.(*executorclient.MockExecutor[tasklist.ShardProcessor]) s.matchingEngine.Start() } func (s *matchingEngineSuite) TearDownTest() { s.mockExecutionManager.AssertExpectations(s.T()) s.matchingEngine.Stop() s.controller.Finish() } func (s *matchingEngineSuite) newMatchingEngine( config *config.Config, taskMgr persistence.TaskManager, ) *matchingEngineImpl { e := NewEngine( taskMgr, cluster.GetTestClusterMetadata(true), s.mockHistoryClient, s.mockMatchingClient, config, s.logger, metrics.NewClient(tally.NoopScope, metrics.Matching, metrics.HistogramMigration{}), tally.NoopScope, s.mockDomainCache, s.mockMembershipResolver, s.isolationState, s.mockTimeSource, s.mockShardExecutorClient, defaultSDExecutorConfig(), nil, ).(*matchingEngineImpl) // Replace the real executor with a mock that behaves as a fully onboarded SD executor. mockExec := executorclient.NewMockExecutor[tasklist.ShardProcessor](s.controller) mockExec.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(&tasklist.MockShardProcessor{}, nil).AnyTimes() mockExec.EXPECT().Start(gomock.Any()).AnyTimes() mockExec.EXPECT().Stop().AnyTimes() e.executor = mockExec return e } func (s *matchingEngineSuite) TestPollForActivityTasksEmptyResult() { s.PollForTasksEmptyResultTest(context.Background(), persistence.TaskListTypeActivity) } func (s *matchingEngineSuite) TestPollForDecisionTasksEmptyResult() { s.PollForTasksEmptyResultTest(context.Background(), persistence.TaskListTypeDecision) } func (s *matchingEngineSuite) TestPollForActivityTasksEmptyResultWithShortContext() { shortContextTimeout := returnEmptyTaskTimeBudget + 10*time.Millisecond callContext, cancel := context.WithTimeout(context.Background(), shortContextTimeout) defer cancel() s.PollForTasksEmptyResultTest(callContext, persistence.TaskListTypeActivity) } func (s *matchingEngineSuite) TestPollForDecisionTasksEmptyResultWithShortContext() { shortContextTimeout := returnEmptyTaskTimeBudget + 10*time.Millisecond callContext, cancel := context.WithTimeout(context.Background(), shortContextTimeout) defer cancel() s.PollForTasksEmptyResultTest(callContext, persistence.TaskListTypeDecision) } func (s *matchingEngineSuite) TestOnlyUnloadMatchingInstance() { taskListID := tasklist.NewTestTaskListID( s.T(), uuid.New(), "makeToast", persistence.TaskListTypeActivity) tlKind := types.TaskListKindNormal tlm, err := s.matchingEngine.getOrCreateTaskListManager(context.Background(), taskListID, tlKind) s.Require().NoError(err) params := tasklist.ManagerParams{ DomainCache: s.matchingEngine.domainCache, Logger: s.matchingEngine.logger, MetricsClient: s.matchingEngine.metricsClient, TaskManager: s.matchingEngine.taskManager, ClusterMetadata: s.matchingEngine.clusterMetadata, IsolationState: s.matchingEngine.isolationState, MatchingClient: s.matchingEngine.matchingClient, Registry: s.matchingEngine.taskListRegistry, TaskList: taskListID, // same taskListID as above TaskListKind: tlKind, Cfg: s.matchingEngine.config, TimeSource: s.matchingEngine.timeSource, CreateTime: s.matchingEngine.timeSource.Now(), HistoryService: s.matchingEngine.historyService, } tlm2, err := tasklist.NewManager(params) s.Require().NoError(err) // try to unload a different tlm instance with the same taskListID s.matchingEngine.unloadTaskList(tlm2) got, err := s.matchingEngine.getOrCreateTaskListManager(context.Background(), taskListID, tlKind) s.Require().NoError(err) s.Require().Same(tlm, got, "Unload call with non-matching taskListManager should not cause unload") // this time unload the right tlm s.matchingEngine.unloadTaskList(tlm) got, err = s.matchingEngine.getOrCreateTaskListManager(context.Background(), taskListID, tlKind) s.Require().NoError(err) s.Require().NotSame(tlm, got, "Unload call with matching incarnation should have caused unload") } func (s *matchingEngineSuite) TestPollForDecisionTasks() { s.PollForDecisionTasksResultTest() } func (s *matchingEngineSuite) PollForDecisionTasksResultTest() { taskType := persistence.TaskListTypeDecision domainID := "domainId" tl := "makeToast" tlKind := types.TaskListKindNormal stickyTl := "makeStickyToast" stickyTlKind := types.TaskListKindSticky identity := "selfDrivingToaster" stickyTaskList := &types.TaskList{} stickyTaskList.Name = stickyTl stickyTaskList.Kind = &stickyTlKind s.matchingEngine.config.RangeSize = 2 // to test that range is not updated without tasks s.matchingEngine.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(1) s.matchingEngine.config.LongPollExpirationInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(10 * time.Millisecond) runID := "run1" workflowID := "workflow1" workflowType := types.WorkflowType{ Name: "workflow", } execution := types.WorkflowExecution{RunID: runID, WorkflowID: workflowID} scheduleID := int64(0) // History service is using mock s.mockHistoryClient.EXPECT().RecordDecisionTaskStarted(gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, taskRequest *types.RecordDecisionTaskStartedRequest, option ...yarpc.CallOption) (*types.RecordDecisionTaskStartedResponse, error) { s.logger.Debug("Mock Received RecordDecisionTaskStartedRequest") taskListKindNormal := types.TaskListKindNormal response := &types.RecordDecisionTaskStartedResponse{} response.WorkflowType = &workflowType response.PreviousStartedEventID = common.Int64Ptr(scheduleID) response.ScheduledEventID = scheduleID + 1 response.Attempt = 0 response.StickyExecutionEnabled = true response.WorkflowExecutionTaskList = &types.TaskList{ Name: tl, Kind: &taskListKindNormal, } return response, nil }).AnyTimes() addRequest := &addTaskRequest{ TaskType: taskType, DomainUUID: domainID, Execution: &execution, TaskList: stickyTaskList, ScheduleToStartTimeoutSeconds: 1, } _, err := addTask(s.matchingEngine, s.handlerContext, addRequest) s.Error(err) s.Contains(err.Error(), "sticky worker is unavailable") // poll the sticky tasklist, should get no result pollReq := &pollTaskRequest{ TaskType: taskType, DomainUUID: domainID, TaskList: stickyTaskList, Identity: identity, } resp, err := pollTask(s.matchingEngine, s.handlerContext, pollReq) s.NoError(err) s.NotNil(resp.AutoConfigHint) resp.AutoConfigHint = nil s.Equal(&pollTaskResponse{}, resp) // add task to sticky tasklist again, this time it should pass _, err = addTask(s.matchingEngine, s.handlerContext, addRequest) s.NoError(err) resp, err = pollTask(s.matchingEngine, s.handlerContext, pollReq) expectedResp := &pollTaskResponse{ TaskToken: resp.TaskToken, WorkflowExecution: &execution, WorkflowType: &workflowType, PreviousStartedEventID: common.Int64Ptr(scheduleID), Attempt: 0, BacklogCountHint: 1, StickyExecutionEnabled: true, WorkflowExecutionTaskList: &types.TaskList{ Name: tl, Kind: &tlKind, }, AutoConfigHint: &types.AutoConfigHint{ EnableAutoConfig: false, PollerWaitTimeInMs: 0, }, } s.Nil(err) s.Equal(expectedResp, resp) } func (s *matchingEngineSuite) PollForTasksEmptyResultTest(callContext context.Context, taskType int) { s.matchingEngine.config.RangeSize = 2 // to test that range is not updated without tasks s.matchingEngine.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(1) if _, ok := callContext.Deadline(); !ok { s.matchingEngine.config.LongPollExpirationInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(10 * time.Millisecond) } domainID := "domainId" tl := "makeToast" identity := "selfDrivingToaster" taskList := &types.TaskList{Name: tl} var taskListType types.TaskListType tlID := tasklist.NewTestTaskListID(s.T(), domainID, tl, taskType) s.handlerContext.Context = callContext const pollCount = 10 for i := 0; i < pollCount; i++ { pollReq := &pollTaskRequest{ TaskType: taskType, DomainUUID: domainID, TaskList: taskList, Identity: identity, } pollResp, err := pollTask(s.matchingEngine, s.handlerContext, pollReq) s.NotNil(pollResp.AutoConfigHint) pollResp.AutoConfigHint = nil // poller wait time is not a fixed value, exclude it from comparison s.NoError(err) s.Equal(&pollTaskResponse{}, pollResp) if taskType == persistence.TaskListTypeActivity { taskListType = types.TaskListTypeActivity } else { taskListType = types.TaskListTypeDecision } select { case <-callContext.Done(): s.FailNow("Call context has expired.") default: } // check the poller information s.handlerContext.Context = context.Background() descResp, err := s.matchingEngine.DescribeTaskList(s.handlerContext, &types.MatchingDescribeTaskListRequest{ DomainUUID: domainID, DescRequest: &types.DescribeTaskListRequest{ TaskList: taskList, TaskListType: &taskListType, IncludeTaskListStatus: false, }, }) s.NoError(err) s.Equal(1, len(descResp.Pollers)) s.Equal(identity, descResp.Pollers[0].GetIdentity()) s.NotEmpty(descResp.Pollers[0].GetLastAccessTime()) s.Nil(descResp.GetTaskListStatus()) } s.EqualValues(1, s.taskManager.GetRangeID(tlID)) } func (s *matchingEngineSuite) TestQueryWorkflow() { domainID := "domainId" tl := "makeToast" tlKind := types.TaskListKindNormal stickyTl := "makeStickyToast" stickyTlKind := types.TaskListKindSticky identity := "selfDrivingToaster" taskList := &types.TaskList{ Name: tl, Kind: &tlKind, } stickyTaskList := &types.TaskList{} stickyTaskList.Name = stickyTl stickyTaskList.Kind = &stickyTlKind s.matchingEngine.config.RangeSize = 2 // to test that range is not updated without tasks s.matchingEngine.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(1) runID := "run1" workflowID := "workflow1" workflowType := types.WorkflowType{ Name: "workflow", } execution := types.WorkflowExecution{RunID: runID, WorkflowID: workflowID} // History service is using mock s.mockHistoryClient.EXPECT().GetMutableState(gomock.Any(), &types.GetMutableStateRequest{ DomainUUID: domainID, Execution: &execution, }).Return(&types.GetMutableStateResponse{ PreviousStartedEventID: common.Int64Ptr(123), NextEventID: 345, WorkflowType: &workflowType, TaskList: taskList, CurrentBranchToken: []byte(`branch token`), HistorySize: 999, ClientImpl: "uber-go", ClientFeatureVersion: "1.0.0", StickyTaskList: stickyTaskList, }, nil) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() resp, err := s.matchingEngine.PollForDecisionTask(s.handlerContext, &types.MatchingPollForDecisionTaskRequest{ DomainUUID: domainID, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: taskList, Identity: identity, }, }) s.NoError(err) s.NotNil(resp.TaskToken) token, err := s.matchingEngine.tokenSerializer.DeserializeQueryTaskToken(resp.TaskToken) s.NoError(err) s.Equal(domainID, token.DomainID) s.Equal(workflowID, token.WorkflowID) s.Equal(runID, token.RunID) s.Equal(tl, token.TaskList) s.True(resp.StickyExecutionEnabled) err = s.matchingEngine.RespondQueryTaskCompleted(s.handlerContext, &types.MatchingRespondQueryTaskCompletedRequest{ DomainUUID: domainID, TaskList: taskList, TaskID: token.TaskID, CompletedRequest: &types.RespondQueryTaskCompletedRequest{ TaskToken: []byte(``), CompletedType: types.QueryTaskCompletedTypeCompleted.Ptr(), QueryResult: []byte(`result`), WorkerVersionInfo: &types.WorkerVersionInfo{ Impl: "uber-go", FeatureVersion: "1.5.0", }, }, }) s.NoError(err) }() time.Sleep(10 * time.Millisecond) // wait for poller to start resp, err := s.matchingEngine.QueryWorkflow(s.handlerContext, &types.MatchingQueryWorkflowRequest{ DomainUUID: domainID, TaskList: taskList, QueryRequest: &types.QueryWorkflowRequest{ Domain: "domain", Execution: &execution, QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }, }) wg.Wait() s.NoError(err) s.Equal(&types.MatchingQueryWorkflowResponse{ QueryResult: []byte(`result`), }, resp) } func (s *matchingEngineSuite) TestAddActivityTasks() { s.AddTasksTest(persistence.TaskListTypeActivity, false) } func (s *matchingEngineSuite) TestAddDecisionTasks() { s.AddTasksTest(persistence.TaskListTypeDecision, false) } func (s *matchingEngineSuite) TestAddActivityTasksForwarded() { s.AddTasksTest(persistence.TaskListTypeActivity, true) } func (s *matchingEngineSuite) TestAddDecisionTasksForwarded() { s.AddTasksTest(persistence.TaskListTypeDecision, true) } func (s *matchingEngineSuite) AddTasksTest(taskType int, isForwarded bool) { s.matchingEngine.config.RangeSize = 300 // override to low number for the test s.matchingEngine.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(150) domainID := "domainId" tl := "makeToast" forwardedFrom := "/__cadence_sys/makeToast/1" taskList := &types.TaskList{Name: tl} const taskCount = 111 runID := "run1" workflowID := "workflow1" execution := types.WorkflowExecution{RunID: runID, WorkflowID: workflowID} for i := int64(0); i < taskCount; i++ { scheduleID := i * 3 addRequest := &addTaskRequest{ TaskType: taskType, DomainUUID: domainID, Execution: &execution, ScheduleID: scheduleID, TaskList: taskList, ScheduleToStartTimeoutSeconds: 1, } if isForwarded { addRequest.ForwardedFrom = forwardedFrom } _, err := addTask(s.matchingEngine, s.handlerContext, addRequest) switch isForwarded { case false: s.NoError(err) case true: s.Equal(errRemoteSyncMatchFailed, err) } } switch isForwarded { case false: s.EqualValues(taskCount, s.taskManager.GetTaskCount(tasklist.NewTestTaskListID(s.T(), domainID, tl, taskType))) case true: s.EqualValues(0, s.taskManager.GetTaskCount(tasklist.NewTestTaskListID(s.T(), domainID, tl, taskType))) } } func (s *matchingEngineSuite) TestAddAndPollDecisionTasks() { s.AddAndPollTasks(persistence.TaskListTypeDecision, false) } func (s *matchingEngineSuite) TestAddAndPollActivityTasks() { s.AddAndPollTasks(persistence.TaskListTypeDecision, false) } func (s *matchingEngineSuite) TestAddAndPollDecisionTasksIsolation() { s.AddAndPollTasks(persistence.TaskListTypeDecision, true) } func (s *matchingEngineSuite) TestAddAndPollActivityTasksIsolation() { s.AddAndPollTasks(persistence.TaskListTypeDecision, true) } func (s *matchingEngineSuite) AddAndPollTasks(taskType int, enableIsolation bool) { s.matchingEngine.config.LongPollExpirationInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(10 * time.Millisecond) s.matchingEngine.config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomainID(enableIsolation) isolationGroups := s.matchingEngine.config.AllIsolationGroups() const taskCount = 6 const initialRangeID = 102 // TODO: Understand why publish is low when rangeSize is 3 const rangeSize = 30 testParam := newTestParam(s.T(), taskType) s.taskManager.SetRangeID(testParam.TaskListID, initialRangeID) s.matchingEngine.config.RangeSize = rangeSize // override to low number for the test s.matchingEngine.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) s.setupGetDrainStatus() for i := int64(0); i < taskCount; i++ { scheduleID := i * 3 addRequest := &addTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, Execution: testParam.WorkflowExecution, ScheduleID: scheduleID, TaskList: testParam.TaskList, ScheduleToStartTimeoutSeconds: 1, PartitionConfig: map[string]string{isolationgroup.GroupKey: isolationGroups[int(i)%len(isolationGroups)]}, } _, err := addTask(s.matchingEngine, s.handlerContext, addRequest) s.NoError(err) } s.EqualValues(taskCount, s.taskManager.GetTaskCount(testParam.TaskListID)) s.setupRecordTaskStartedMock(taskType, testParam, false) for i := int64(0); i < taskCount; { scheduleID := i * 3 pollReq := &pollTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, TaskList: testParam.TaskList, Identity: testParam.Identity, IsolationGroup: isolationGroups[int(i)%len(isolationGroups)], } result, err := pollTask(s.matchingEngine, s.handlerContext, pollReq) s.NoError(err) s.NotNil(result) if isEmptyToken(result.TaskToken) { s.logger.Debug("empty poll returned") continue } s.assertPollTaskResponse(taskType, testParam, scheduleID, result) i++ } s.EqualValues(0, s.taskManager.GetTaskCount(testParam.TaskListID)) expectedRange := getExpectedRange(initialRangeID, taskCount, rangeSize) // Due to conflicts some ids are skipped and more real ranges are used. s.True(expectedRange <= s.taskManager.GetRangeID(testParam.TaskListID)) } func (s *matchingEngineSuite) TestSyncMatchActivityTasks() { s.SyncMatchTasks(persistence.TaskListTypeActivity, false) } func (s *matchingEngineSuite) TestSyncMatchDecisionTasks() { s.SyncMatchTasks(persistence.TaskListTypeDecision, false) } func (s *matchingEngineSuite) TestSyncMatchActivityTasksIsolation() { s.SyncMatchTasks(persistence.TaskListTypeActivity, true) } func (s *matchingEngineSuite) TestSyncMatchDecisionTasksIsolation() { s.SyncMatchTasks(persistence.TaskListTypeDecision, true) } func (s *matchingEngineSuite) SyncMatchTasks(taskType int, enableIsolation bool) { s.matchingEngine.config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomainID(enableIsolation) const taskCount = 10 const initialRangeID = 102 // TODO: Understand why publish is low when rangeSize is 3 const rangeSize = 30 var throttledTaskCount int64 if taskType == persistence.TaskListTypeActivity { throttledTaskCount = 3 } isolationGroups := s.matchingEngine.config.AllIsolationGroups // Set a short long poll expiration so we don't have to wait too long for 0 throttling cases s.matchingEngine.config.LongPollExpirationInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(200 * time.Millisecond) s.matchingEngine.config.RangeSize = rangeSize // override to low number for the test s.matchingEngine.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) s.matchingEngine.config.TaskDispatchRPSTTL = time.Nanosecond s.matchingEngine.config.MinTaskThrottlingBurstSize = dynamicproperties.GetIntPropertyFilteredByTaskListInfo(_minBurst) // So we can get snapshots scope := tally.NewTestScope("test", nil) s.matchingEngine.metricsClient = metrics.NewClient(scope, metrics.Matching, metrics.HistogramMigration{}) testParam := newTestParam(s.T(), taskType) s.taskManager.SetRangeID(testParam.TaskListID, initialRangeID) s.setupGetDrainStatus() s.setupRecordTaskStartedMock(taskType, testParam, false) pollFunc := func(maxDispatch float64, isolationGroup string) (*pollTaskResponse, error) { pollReq := &pollTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, TaskList: testParam.TaskList, Identity: testParam.Identity, TaskListMetadata: &types.TaskListMetadata{MaxTasksPerSecond: &maxDispatch}, IsolationGroup: isolationGroup, } return pollTask(s.matchingEngine, s.handlerContext, pollReq) } for i := int64(0); i < taskCount; i++ { scheduleID := i * 3 group := isolationGroups()[int(i)%len(isolationGroups())] var wg sync.WaitGroup var result *pollTaskResponse var pollErr error wg.Add(1) go func() { defer wg.Done() result, pollErr = pollFunc(_defaultTaskDispatchRPS, group) }() time.Sleep(20 * time.Millisecond) // Wait for a short period of time to let the poller start so that sync match will happen addRequest := &addTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, Execution: testParam.WorkflowExecution, ScheduleID: scheduleID, TaskList: testParam.TaskList, ScheduleToStartTimeoutSeconds: 1, PartitionConfig: map[string]string{isolationgroup.GroupKey: group}, } _, err := addTask(s.matchingEngine, s.handlerContext, addRequest) wg.Wait() s.NoError(err) s.NoError(pollErr) s.NotNil(result) s.assertPollTaskResponse(taskType, testParam, scheduleID, result) } // expect more than half of the tasks get sync matches s.True(s.taskManager.GetCreateTaskCount(testParam.TaskListID) < taskCount/2) // Set the dispatch RPS to 0, to verify that poller will not get any task and task will be persisted into database // Revert the dispatch RPS and verify that poller will get the task for i := int64(0); i < throttledTaskCount; i++ { scheduleID := i * 3 group := isolationGroups()[int(i)%len(isolationGroups())] var pollerDone sync.WaitGroup var result *pollTaskResponse var pollErr error pollerDone.Add(1) go func() { defer pollerDone.Done() result, pollErr = pollFunc(0.0, group) }() time.Sleep(20 * time.Millisecond) // Wait for a short period of time to let the poller start so that sync match will happen addRequest := &addTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, Execution: testParam.WorkflowExecution, ScheduleID: scheduleID, TaskList: testParam.TaskList, ScheduleToStartTimeoutSeconds: 1, PartitionConfig: map[string]string{isolationgroup.GroupKey: group}, } _, err := addTask(s.matchingEngine, s.handlerContext, addRequest) pollerDone.Wait() s.NoError(err) s.NoError(pollErr) s.NotNil(result) // when ratelimit is set to zero, poller is expected to return empty result // reset ratelimit, poll again and make sure task is returned this time s.True(isEmptyToken(result.TaskToken)) // If we don't increment the mockTime then the RateLimiter will never accept a higher RPS s.mockTimeSource.Advance(time.Nanosecond) result, pollErr = pollFunc(_defaultTaskDispatchRPS, group) s.NoError(err) s.NoError(pollErr) s.NotNil(result) s.False(isEmptyToken(result.TaskToken)) s.assertPollTaskResponse(taskType, testParam, scheduleID, result) } s.True(int(throttledTaskCount) <= s.taskManager.GetCreateTaskCount(testParam.TaskListID)) s.EqualValues(0, s.taskManager.GetTaskCount(testParam.TaskListID)) expectedRange := getExpectedRange(initialRangeID, int(taskCount+throttledTaskCount), rangeSize) // Due to conflicts some ids are skipped and more real ranges are used. s.True(expectedRange <= s.taskManager.GetRangeID(testParam.TaskListID)) if throttledTaskCount > 0 { syncCtr := scope.Snapshot().Counters()["test.sync_throttle_count_per_tl+domain="+matchingTestDomainName+",operation=TaskListMgr,tasklist="+testParam.TaskList.Name+",tasklistType=activity"] s.EqualValues(throttledTaskCount, int(syncCtr.Value())) } // check the poller information descResp, err := s.matchingEngine.DescribeTaskList(s.handlerContext, &types.MatchingDescribeTaskListRequest{ DomainUUID: testParam.DomainID, DescRequest: &types.DescribeTaskListRequest{ TaskList: testParam.TaskList, TaskListType: testParam.TaskListType, IncludeTaskListStatus: true, }, }) s.NoError(err) s.Equal(1, len(descResp.Pollers)) s.Equal(testParam.Identity, descResp.Pollers[0].GetIdentity()) s.NotEmpty(descResp.Pollers[0].GetLastAccessTime()) s.Equal(_defaultTaskDispatchRPS, descResp.Pollers[0].GetRatePerSecond()) s.NotNil(descResp.GetTaskListStatus()) s.True(descResp.GetTaskListStatus().GetRatePerSecond() >= (_defaultTaskDispatchRPS - 1)) } func (s *matchingEngineSuite) TestConcurrentAddAndPollActivities() { s.ConcurrentAddAndPollTasks(persistence.TaskListTypeActivity, 20, 100, false, false) } func (s *matchingEngineSuite) TestConcurrentAddAndPollActivitiesWithZeroDispatch() { s.ConcurrentAddAndPollTasks(persistence.TaskListTypeActivity, 20, 100, true, false) } func (s *matchingEngineSuite) TestConcurrentAddAndPollDecisions() { s.ConcurrentAddAndPollTasks(persistence.TaskListTypeDecision, 20, 100, false, false) } func (s *matchingEngineSuite) TestConcurrentAddAndPollActivitiesIsolation() { s.ConcurrentAddAndPollTasks(persistence.TaskListTypeActivity, 20, 100, false, true) } func (s *matchingEngineSuite) TestConcurrentAddAndPollActivitiesWithZeroDispatchIsolation() { s.ConcurrentAddAndPollTasks(persistence.TaskListTypeActivity, 20, 100, true, true) } func (s *matchingEngineSuite) TestConcurrentAddAndPollDecisionsIsolation() { s.ConcurrentAddAndPollTasks(persistence.TaskListTypeDecision, 20, 100, false, true) } func (s *matchingEngineSuite) ConcurrentAddAndPollTasks(taskType int, workerCount int, taskCount int64, throttled, enableIsolation bool) { s.matchingEngine.config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomainID(enableIsolation) isolationGroups := s.matchingEngine.config.AllIsolationGroups dispatchLimitFn := func(wc int, attempt int) float64 { return _defaultTaskDispatchRPS } if throttled { dispatchLimitFn = func(wc int, attempt int) float64 { if attempt%50 == 0 && wc%5 == 0 { // Gets triggered atleast 20 times return 0 } return _defaultTaskDispatchRPS } } scope := tally.NewTestScope("test", nil) s.matchingEngine.metricsClient = metrics.NewClient(scope, metrics.Matching, metrics.HistogramMigration{}) const initialRangeID = 0 const rangeSize = 3 var scheduleID int64 = 123 testParam := newTestParam(s.T(), taskType) tlKind := types.TaskListKindNormal s.matchingEngine.config.RangeSize = rangeSize // override to low number for the test s.matchingEngine.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) // The TaskListLimiter uses the MockTimeSource, which will be incremented by 1ns after each poll s.matchingEngine.config.TaskDispatchRPSTTL = 50 * time.Nanosecond s.matchingEngine.config.MinTaskThrottlingBurstSize = dynamicproperties.GetIntPropertyFilteredByTaskListInfo(_minBurst) // Make the pollers time out relatively quickly if there are no tasks s.matchingEngine.config.LongPollExpirationInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(10 * time.Millisecond) s.taskManager.SetRangeID(testParam.TaskListID, initialRangeID) s.setupGetDrainStatus() var wg sync.WaitGroup wg.Add(2 * workerCount) for p := 0; p < workerCount; p++ { go func() { defer wg.Done() for i := int64(0); i < taskCount; i++ { group := isolationGroups()[int(i)%len(isolationGroups())] // let each worker to generate tasks for all isolation groups addRequest := &addTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, Execution: testParam.WorkflowExecution, ScheduleID: scheduleID, TaskList: testParam.TaskList, ScheduleToStartTimeoutSeconds: 1, PartitionConfig: map[string]string{isolationgroup.GroupKey: group}, } _, err := addTask(s.matchingEngine, s.handlerContext, addRequest) if err != nil { s.logger.Info("Failure in AddActivityTask", tag.Error(err)) i-- } } }() } s.setupRecordTaskStartedMock(taskType, testParam, false) for p := 0; p < workerCount; p++ { go func(wNum int) { defer wg.Done() attempt := 0 for i := int64(0); i < taskCount; { attempt++ maxDispatch := dispatchLimitFn(wNum, attempt) group := isolationGroups()[int(wNum)%len(isolationGroups())] // let each worker only polls from one isolation group pollReq := &pollTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, TaskList: testParam.TaskList, Identity: testParam.Identity, TaskListMetadata: &types.TaskListMetadata{MaxTasksPerSecond: &maxDispatch}, IsolationGroup: group, } result, err := pollTask(s.matchingEngine, s.handlerContext, pollReq) s.mockTimeSource.Advance(time.Nanosecond) s.NoError(err) s.NotNil(result) if isEmptyToken(result.TaskToken) { s.logger.Debug("empty poll returned") continue } s.assertPollTaskResponse(taskType, testParam, scheduleID, result) i++ } }(p) } wg.Wait() totalTasks := int(taskCount) * workerCount persisted := s.taskManager.GetCreateTaskCount(testParam.TaskListID) s.True(persisted < totalTasks) expectedRange := getExpectedRange(initialRangeID, persisted, rangeSize) // Due to conflicts some ids are skipped and more real ranges are used. s.True(expectedRange <= s.taskManager.GetRangeID(testParam.TaskListID)) mgr, err := s.matchingEngine.getOrCreateTaskListManager(context.Background(), testParam.TaskListID, tlKind) s.NoError(err) // stop the tasklist manager to force the acked tasks to be deleted mgr.Stop() s.EqualValues(0, s.taskManager.GetTaskCount(testParam.TaskListID)) syncCtr := scope.Snapshot().Counters()["test.sync_throttle_count_per_tl+domain="+matchingTestDomainName+",operation=TaskListMgr,tasklist="+testParam.TaskList.Name+",tasklistType=activity"] bufCtr := scope.Snapshot().Counters()["test.buffer_throttle_count_per_tl+domain="+matchingTestDomainName+",operation=TaskListMgr,tasklist="+testParam.TaskList.Name+",tasklistType=activity"] total := int64(0) if syncCtr != nil { total += syncCtr.Value() } if bufCtr != nil { total += bufCtr.Value() } if throttled { // atleast once from 0 dispatch poll, and until TTL is hit at which time throttle limit is reset // hard to predict exactly how many times, since the atomic.Value load might not have updated. s.True(total >= 1) } else { s.EqualValues(0, total) } } func (s *matchingEngineSuite) TestPollActivityWithExpiredContext() { s.PollWithExpiredContext(persistence.TaskListTypeActivity) } func (s *matchingEngineSuite) TestPollDecisionWithExpiredContext() { s.PollWithExpiredContext(persistence.TaskListTypeDecision) } func (s *matchingEngineSuite) PollWithExpiredContext(taskType int) { identity := "nobody" domainID := "domainId" tl := "makeToast" taskList := &types.TaskList{Name: tl} // Try with cancelled context ctx, cancel := context.WithTimeout(context.Background(), time.Second) cancel() s.handlerContext.Context = ctx pollReq := &pollTaskRequest{ TaskType: taskType, DomainUUID: domainID, TaskList: taskList, Identity: identity, } _, err := pollTask(s.matchingEngine, s.handlerContext, pollReq) s.Equal(ctx.Err(), err) // Try with expired context ctx, cancel = context.WithTimeout(context.Background(), time.Second) defer cancel() s.handlerContext.Context = ctx resp, err := pollTask(s.matchingEngine, s.handlerContext, pollReq) s.Nil(err) s.NotNil(resp.AutoConfigHint) resp.AutoConfigHint = nil s.Equal(&pollTaskResponse{}, resp) } func (s *matchingEngineSuite) TestMultipleEnginesActivitiesRangeStealing() { s.MultipleEnginesTasksRangeStealing(persistence.TaskListTypeActivity) } func (s *matchingEngineSuite) TestMultipleEnginesDecisionsRangeStealing() { s.MultipleEnginesTasksRangeStealing(persistence.TaskListTypeDecision) } func (s *matchingEngineSuite) MultipleEnginesTasksRangeStealing(taskType int) { s.T().Cleanup(func() { goleak.VerifyNone(s.T()) }) // Add N tasks to engine1 and then N tasks to engine2. Then poll all tasks from engine2. Engine1 should be closed const N = 10 const initialRangeID = 0 const rangeSize = 5 var scheduleID int64 = 123 testParam := newTestParam(s.T(), taskType) s.taskManager.SetRangeID(testParam.TaskListID, initialRangeID) s.matchingEngine.config.RangeSize = rangeSize // override to low number for the test s.matchingEngine.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) engine1 := s.newMatchingEngine(defaultTestConfig(), s.taskManager) engine1.config.RangeSize = rangeSize engine1.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) engine1.Start() defer engine1.Stop() createAddTaskReq := func() *addTaskRequest { return &addTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, Execution: testParam.WorkflowExecution, ScheduleID: scheduleID, TaskList: testParam.TaskList, ScheduleToStartTimeoutSeconds: 600, } } // First add tasks to engine1 for i := 0; i < N; i++ { _, err := addTask(engine1, s.handlerContext, createAddTaskReq()) s.Require().NoError(err) } engine2 := s.newMatchingEngine(defaultTestConfig(), s.taskManager) engine2.config.RangeSize = rangeSize engine2.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) engine2.Start() defer engine2.Stop() // Then add tasks to engine2. It should be able to steal lease and process tasks for i := 0; i < N; i++ { _, err := addTask(engine2, s.handlerContext, createAddTaskReq()) s.Require().NoError(err) } _, err := addTask(engine1, s.handlerContext, createAddTaskReq()) // Adding another task to engine1 should fail because it will not have the lease s.Require().ErrorContains(err, "task list shutting down") s.EqualValues(2*N, s.taskManager.GetCreateTaskCount(testParam.TaskListID)) s.setupRecordTaskStartedMock(taskType, testParam, true) // Poll all tasks from engine2. for i := 0; i < 2*N; i++ { pollReq := &pollTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, TaskList: testParam.TaskList, Identity: testParam.Identity, } result, err := pollTask(engine2, s.handlerContext, pollReq) s.Require().NoError(err) s.NotNil(result) if isEmptyToken(result.TaskToken) { s.Fail("empty poll returned") } s.assertPollTaskResponse(taskType, testParam, scheduleID, result) } s.EqualValues(0, s.taskManager.GetTaskCount(testParam.TaskListID)) totalTasks := 2 * N persisted := s.taskManager.GetCreateTaskCount(testParam.TaskListID) // No sync matching as all messages are added first s.EqualValues(totalTasks, persisted) expectedRange := getExpectedRange(initialRangeID, persisted, rangeSize) // Due to conflicts some ids are skipped and more real ranges are used. s.True(expectedRange <= s.taskManager.GetRangeID(testParam.TaskListID)) } func (s *matchingEngineSuite) TestAddTaskAfterStartFailure() { taskType := persistence.TaskListTypeActivity runID := "run1" workflowID := "workflow1" workflowExecution := types.WorkflowExecution{RunID: runID, WorkflowID: workflowID} domainID := "domainId" tl := "makeToast" tlID := tasklist.NewTestTaskListID(s.T(), domainID, tl, taskType) tlKind := types.TaskListKindNormal taskList := &types.TaskList{Name: tl} scheduleID := int64(0) addRequest := &addTaskRequest{ TaskType: taskType, DomainUUID: domainID, Execution: &workflowExecution, ScheduleID: scheduleID, TaskList: taskList, ScheduleToStartTimeoutSeconds: 1, } _, err := addTask(s.matchingEngine, s.handlerContext, addRequest) s.NoError(err) s.EqualValues(1, s.taskManager.GetTaskCount(tlID)) tlMgr, err := s.matchingEngine.getOrCreateTaskListManager(context.Background(), tlID, tlKind) s.NoError(err) ctx, err := tlMgr.GetTask(context.Background(), nil) s.NoError(err) ctx.Finish(errors.New("test error")) s.EqualValues(1, s.taskManager.GetTaskCount(tlID)) ctx2, err := tlMgr.GetTask(context.Background(), nil) s.NoError(err) s.NotEqual(ctx.Event.TaskID, ctx2.Event.TaskID) s.Equal(ctx.Event.WorkflowID, ctx2.Event.WorkflowID) s.Equal(ctx.Event.RunID, ctx2.Event.RunID) s.Equal(ctx.Event.ScheduleID, ctx2.Event.ScheduleID) ctx2.Finish(nil) s.EqualValues(0, s.taskManager.GetTaskCount(tlID)) } func (s *matchingEngineSuite) TestUnloadActivityTasklistOnIsolationConfigChange() { s.UnloadTasklistOnIsolationConfigChange(persistence.TaskListTypeActivity) } func (s *matchingEngineSuite) TestUnloadDecisionTasklistOnIsolationConfigChange() { s.UnloadTasklistOnIsolationConfigChange(persistence.TaskListTypeDecision) } func (s *matchingEngineSuite) UnloadTasklistOnIsolationConfigChange(taskType int) { s.matchingEngine.config.LongPollExpirationInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(50 * time.Millisecond) s.matchingEngine.config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomainID(false) const taskCount = 1000 const initialRangeID = 102 // TODO: Understand why publish is low when rangeSize is 3 const rangeSize = 30 testParam := newTestParam(s.T(), taskType) s.taskManager.SetRangeID(testParam.TaskListID, initialRangeID) s.matchingEngine.config.RangeSize = rangeSize // override to low number for the test s.matchingEngine.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) addRequest := &addTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, Execution: testParam.WorkflowExecution, ScheduleID: 333, TaskList: testParam.TaskList, ScheduleToStartTimeoutSeconds: 1, } _, err := addTask(s.matchingEngine, s.handlerContext, addRequest) s.NoError(err) // enable isolation and verify that poller should not get any task s.matchingEngine.config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomainID(true) s.setupGetDrainStatus() s.setupRecordTaskStartedMock(taskType, testParam, false) pollReq := &pollTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, TaskList: testParam.TaskList, Identity: testParam.Identity, } result, err := pollTask(s.matchingEngine, s.handlerContext, pollReq) s.NoError(err) s.NotNil(result.AutoConfigHint) result.AutoConfigHint = nil s.Equal(&pollTaskResponse{}, result) result, err = pollTask(s.matchingEngine, s.handlerContext, pollReq) s.NoError(err) s.NotNil(result) s.assertPollTaskResponse(taskType, testParam, 333, result) // disable isolation again and verify add tasklist should fail s.matchingEngine.config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomainID(false) _, err = addTask(s.matchingEngine, s.handlerContext, addRequest) s.Error(err) s.Contains(err.Error(), "task list shutting down") } func (s *matchingEngineSuite) TestDrainActivityBacklogNoPollersIsolationGroup() { s.DrainBacklogNoPollersIsolationGroup(persistence.TaskListTypeActivity) } func (s *matchingEngineSuite) TestDrainDecisionBacklogNoPollersIsolationGroup() { s.DrainBacklogNoPollersIsolationGroup(persistence.TaskListTypeDecision) } func (s *matchingEngineSuite) DrainBacklogNoPollersIsolationGroup(taskType int) { s.matchingEngine.config.LongPollExpirationInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(10 * time.Millisecond) s.matchingEngine.config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomainID(true) s.matchingEngine.config.AsyncTaskDispatchTimeout = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(10 * time.Millisecond) isolationGroups := s.matchingEngine.config.AllIsolationGroups const taskCount = 1000 const initialRangeID = 102 // TODO: Understand why publish is low when rangeSize is 3 const rangeSize = 30 // use a const scheduleID because we don't know the order of task polled const scheduleID = 11111 testParam := newTestParam(s.T(), taskType) s.taskManager.SetRangeID(testParam.TaskListID, initialRangeID) s.matchingEngine.config.RangeSize = rangeSize // override to low number for the test s.matchingEngine.config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) _, err := s.matchingEngine.getOrCreateTaskListManager(context.Background(), testParam.TaskListID, testParam.TaskList.GetKind()) s.NoError(err) // advance the time a bit more than warmup time of new tasklist after the creation of tasklist manager, which is 1 minute s.mockTimeSource.Advance(time.Minute) s.mockTimeSource.Advance(time.Second) s.setupGetDrainStatus() for i := int64(0); i < taskCount; i++ { addRequest := &addTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, Execution: testParam.WorkflowExecution, ScheduleID: scheduleID, TaskList: testParam.TaskList, ScheduleToStartTimeoutSeconds: 1, PartitionConfig: map[string]string{isolationgroup.GroupKey: isolationGroups()[int(i)%len(isolationGroups())]}, } _, err := addTask(s.matchingEngine, s.handlerContext, addRequest) s.NoError(err) } s.EqualValues(taskCount, s.taskManager.GetTaskCount(testParam.TaskListID)) s.setupRecordTaskStartedMock(taskType, testParam, false) for i := int64(0); i < taskCount; { pollReq := &pollTaskRequest{ TaskType: taskType, DomainUUID: testParam.DomainID, TaskList: testParam.TaskList, Identity: testParam.Identity, IsolationGroup: isolationGroups()[0], } result, err := pollTask(s.matchingEngine, s.handlerContext, pollReq) s.NoError(err) s.NotNil(result) if isEmptyToken(result.TaskToken) { s.logger.Debug("empty poll returned") continue } s.assertPollTaskResponse(taskType, testParam, scheduleID, result) i++ } s.EqualValues(0, s.taskManager.GetTaskCount(testParam.TaskListID)) expectedRange := getExpectedRange(initialRangeID, taskCount, rangeSize) // Due to conflicts some ids are skipped and more real ranges are used. s.True(expectedRange <= s.taskManager.GetRangeID(testParam.TaskListID)) } func (s *matchingEngineSuite) setupRecordTaskStartedMock(taskType int, param *testParam, checkDuplicate bool) { startedTasks := make(map[int64]bool) if taskType == persistence.TaskListTypeActivity { s.mockHistoryClient.EXPECT().RecordActivityTaskStarted(gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, taskRequest *types.RecordActivityTaskStartedRequest, option ...yarpc.CallOption) (*types.RecordActivityTaskStartedResponse, error) { s.logger.Debug(fmt.Sprintf("Mock Received RecordActivityTaskStartedRequest, taskID: %v", taskRequest.TaskID)) if checkDuplicate { if _, ok := startedTasks[taskRequest.TaskID]; ok { return nil, &types.EventAlreadyStartedError{Message: "already started"} } startedTasks[taskRequest.TaskID] = true } return &types.RecordActivityTaskStartedResponse{ ScheduledEvent: newActivityTaskScheduledEvent(taskRequest.ScheduleID, 0, &types.ScheduleActivityTaskDecisionAttributes{ ActivityID: param.ActivityID, TaskList: param.TaskList, ActivityType: param.ActivityType, Input: param.ActivityInput, Header: param.ActivityHeader, ScheduleToCloseTimeoutSeconds: common.Int32Ptr(100), ScheduleToStartTimeoutSeconds: common.Int32Ptr(50), StartToCloseTimeoutSeconds: common.Int32Ptr(50), HeartbeatTimeoutSeconds: common.Int32Ptr(10), }), }, nil }).AnyTimes() } else { s.mockHistoryClient.EXPECT().RecordDecisionTaskStarted(gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, taskRequest *types.RecordDecisionTaskStartedRequest, option ...yarpc.CallOption) (*types.RecordDecisionTaskStartedResponse, error) { s.logger.Debug(fmt.Sprintf("Mock Received RecordDecisionTaskStartedRequest, taskID: %v", taskRequest.TaskID)) if checkDuplicate { if _, ok := startedTasks[taskRequest.TaskID]; ok { return nil, &types.EventAlreadyStartedError{Message: "already started"} } startedTasks[taskRequest.TaskID] = true } return &types.RecordDecisionTaskStartedResponse{ PreviousStartedEventID: ¶m.StartedEventID, StartedEventID: param.StartedEventID, ScheduledEventID: taskRequest.ScheduleID, WorkflowType: param.WorkflowType, }, nil }).AnyTimes() } } func (s *matchingEngineSuite) setupGetDrainStatus() { s.mockIsolationStore.EXPECT().GetListValue(dynamicproperties.DefaultIsolationGroupConfigStoreManagerGlobalMapping, nil).Return(nil, nil).AnyTimes() } func (s *matchingEngineSuite) awaitCondition(cond func() bool, timeout time.Duration) bool { expiry := time.Now().Add(timeout) for !cond() { time.Sleep(time.Millisecond * 5) if time.Now().After(expiry) { return false } } return true } func (s *matchingEngineSuite) assertPollTaskResponse(taskType int, param *testParam, scheduleID int64, actual *pollTaskResponse) { if taskType == persistence.TaskListTypeActivity { token := &common.TaskToken{ DomainID: param.DomainID, WorkflowID: param.WorkflowExecution.WorkflowID, RunID: param.WorkflowExecution.RunID, ScheduleID: scheduleID, ActivityID: param.ActivityID, ActivityType: param.ActivityType.Name, } s.EqualValues(token, actual.TaskToken) s.EqualValues(param.ActivityID, actual.ActivityID) s.EqualValues(param.ActivityType, actual.ActivityType) s.EqualValues(param.ActivityInput, actual.Input) s.EqualValues(param.ActivityHeader, actual.Header) s.EqualValues(param.WorkflowExecution, actual.WorkflowExecution) } else { token := &common.TaskToken{ DomainID: param.DomainID, WorkflowID: param.WorkflowExecution.WorkflowID, RunID: param.WorkflowExecution.RunID, ScheduleID: scheduleID, } s.EqualValues(token, actual.TaskToken) s.EqualValues(param.WorkflowExecution, actual.WorkflowExecution) s.EqualValues(param.WorkflowType, actual.WorkflowType) s.EqualValues(param.StartedEventID, actual.StartedEventID) } } func (s *matchingEngineSuite) TestConfigDefaultHostName() { configEmpty := config.Config{} s.NotEqualValues(s.matchingEngine.config.HostName, configEmpty.HostName) s.EqualValues(configEmpty.HostName, "") } func newActivityTaskScheduledEvent(eventID int64, decisionTaskCompletedEventID int64, scheduleAttributes *types.ScheduleActivityTaskDecisionAttributes) *types.HistoryEvent { historyEvent := newHistoryEvent(eventID, types.EventTypeActivityTaskScheduled) attributes := &types.ActivityTaskScheduledEventAttributes{} attributes.ActivityID = scheduleAttributes.ActivityID attributes.ActivityType = scheduleAttributes.ActivityType attributes.TaskList = scheduleAttributes.TaskList attributes.Input = scheduleAttributes.Input attributes.Header = scheduleAttributes.Header attributes.ScheduleToCloseTimeoutSeconds = scheduleAttributes.ScheduleToCloseTimeoutSeconds attributes.ScheduleToStartTimeoutSeconds = scheduleAttributes.ScheduleToStartTimeoutSeconds attributes.StartToCloseTimeoutSeconds = scheduleAttributes.StartToCloseTimeoutSeconds attributes.HeartbeatTimeoutSeconds = scheduleAttributes.HeartbeatTimeoutSeconds attributes.DecisionTaskCompletedEventID = decisionTaskCompletedEventID historyEvent.ActivityTaskScheduledEventAttributes = attributes return historyEvent } func newHistoryEvent(eventID int64, eventType types.EventType) *types.HistoryEvent { ts := common.Int64Ptr(time.Now().UnixNano()) historyEvent := &types.HistoryEvent{} historyEvent.ID = eventID historyEvent.Timestamp = ts historyEvent.EventType = &eventType return historyEvent } func validateTimeRange(t time.Time, expectedDuration time.Duration) bool { currentTime := time.Now() diff := time.Duration(currentTime.UnixNano() - t.UnixNano()) if diff > expectedDuration { fmt.Printf("Current time: %v, Application time: %v, Difference: %v \n", currentTime, t, diff) return false } return true } func defaultTestConfig() *config.Config { config := config.NewConfig(dynamicconfig.NewNopCollection(), "some random hostname", commonConfig.RPC{}, getIsolationGroupsHelper) config.LongPollExpirationInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(100 * time.Millisecond) config.MaxTaskDeleteBatchSize = dynamicproperties.GetIntPropertyFilteredByTaskListInfo(1) config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(50000) config.GetTasksBatchSize = dynamicproperties.GetIntPropertyFilteredByTaskListInfo(10) config.AsyncTaskDispatchTimeout = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(10 * time.Millisecond) config.MaxTimeBetweenTaskDeletes = time.Duration(0) config.EnableTasklistOwnershipGuard = func(opts ...dynamicproperties.FilterOption) bool { return true } return config } func defaultSDExecutorConfig() clientcommon.Config { return clientcommon.Config{ Namespaces: []clientcommon.NamespaceConfig{{ Namespace: "cadence-matching", HeartBeatInterval: 1 * time.Second, MigrationMode: sdconfig.MigrationModeONBOARDED, TTLShard: 5 * time.Minute, TTLReport: 1 * time.Minute, }}, } } func getExpectedRange(initialRangeID, taskCount, rangeSize int) int64 { expectedRange := int64(initialRangeID + taskCount/rangeSize) if taskCount%rangeSize > 0 { expectedRange++ } return expectedRange } type testParam struct { DomainID string WorkflowExecution *types.WorkflowExecution TaskList *types.TaskList TaskListID *tasklist.Identifier TaskListType *types.TaskListType Identity string ActivityID string ActivityType *types.ActivityType ActivityInput []byte ActivityHeader *types.Header WorkflowType *types.WorkflowType StartedEventID int64 ScheduledEventID int64 } func newTestParam(t *testing.T, taskType int) *testParam { domainID := uuid.New() taskList := &types.TaskList{ Name: strings.ReplaceAll(uuid.New(), "-", ""), // metric tags are sanitized } tlID := tasklist.NewTestTaskListID(t, domainID, taskList.Name, taskType) taskListType := types.TaskListType(taskType) return &testParam{ DomainID: domainID, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: uuid.New(), RunID: uuid.New(), }, TaskList: taskList, TaskListID: tlID, TaskListType: &taskListType, Identity: uuid.New(), ActivityID: uuid.New(), ActivityType: &types.ActivityType{ Name: uuid.New(), }, ActivityInput: []byte(uuid.New()), ActivityHeader: &types.Header{Fields: map[string][]byte{"tracing": []byte("tracing data")}}, WorkflowType: &types.WorkflowType{Name: uuid.New()}, ScheduledEventID: 1412, } } type addTaskRequest struct { TaskType int DomainUUID string Execution *types.WorkflowExecution TaskList *types.TaskList ScheduleID int64 ScheduleToStartTimeoutSeconds int32 Source *types.TaskSource ForwardedFrom string PartitionConfig map[string]string } type addTaskResponse struct { PartitionConfig *types.TaskListPartitionConfig } func addTask(engine *matchingEngineImpl, hCtx *handlerContext, request *addTaskRequest) (*addTaskResponse, error) { if request.TaskType == persistence.TaskListTypeActivity { resp, err := engine.AddActivityTask(hCtx, &types.AddActivityTaskRequest{ SourceDomainUUID: request.DomainUUID, DomainUUID: request.DomainUUID, Execution: request.Execution, TaskList: request.TaskList, ScheduleID: request.ScheduleID, ScheduleToStartTimeoutSeconds: &request.ScheduleToStartTimeoutSeconds, Source: request.Source, ForwardedFrom: request.ForwardedFrom, PartitionConfig: request.PartitionConfig, }) if err != nil { return nil, err } return &addTaskResponse{ PartitionConfig: resp.PartitionConfig, }, nil } resp, err := engine.AddDecisionTask(hCtx, &types.AddDecisionTaskRequest{ DomainUUID: request.DomainUUID, Execution: request.Execution, TaskList: request.TaskList, ScheduleID: request.ScheduleID, ScheduleToStartTimeoutSeconds: &request.ScheduleToStartTimeoutSeconds, Source: request.Source, ForwardedFrom: request.ForwardedFrom, PartitionConfig: request.PartitionConfig, }) if err != nil { return nil, err } return &addTaskResponse{ PartitionConfig: resp.PartitionConfig, }, nil } type pollTaskRequest struct { TaskType int DomainUUID string PollerID string TaskList *types.TaskList Identity string ForwardedFrom string IsolationGroup string TaskListMetadata *types.TaskListMetadata BinaryChecksum string } type pollTaskResponse struct { TaskToken *common.TaskToken WorkflowExecution *types.WorkflowExecution ActivityID string ActivityType *types.ActivityType Input []byte ScheduledTimestamp *int64 ScheduleToCloseTimeoutSeconds *int32 StartedTimestamp *int64 StartToCloseTimeoutSeconds *int32 HeartbeatTimeoutSeconds *int32 ScheduledTimestampOfThisAttempt *int64 HeartbeatDetails []byte WorkflowType *types.WorkflowType WorkflowDomain string Header *types.Header PreviousStartedEventID *int64 StartedEventID int64 Attempt int64 NextEventID int64 BacklogCountHint int64 StickyExecutionEnabled bool Query *types.WorkflowQuery DecisionInfo *types.TransientDecisionInfo WorkflowExecutionTaskList *types.TaskList EventStoreVersion int32 BranchToken []byte Queries map[string]*types.WorkflowQuery AutoConfigHint *types.AutoConfigHint } func pollTask(engine *matchingEngineImpl, hCtx *handlerContext, request *pollTaskRequest) (*pollTaskResponse, error) { if request.TaskType == persistence.TaskListTypeActivity { resp, err := engine.PollForActivityTask(hCtx, &types.MatchingPollForActivityTaskRequest{ DomainUUID: request.DomainUUID, PollerID: request.PollerID, PollRequest: &types.PollForActivityTaskRequest{ TaskList: request.TaskList, Identity: request.Identity, TaskListMetadata: request.TaskListMetadata, }, IsolationGroup: request.IsolationGroup, ForwardedFrom: request.ForwardedFrom, }) if err != nil { return nil, err } var token *common.TaskToken if len(resp.TaskToken) > 0 { token, err = engine.tokenSerializer.Deserialize(resp.TaskToken) if err != nil { return nil, err } } return &pollTaskResponse{ TaskToken: token, WorkflowExecution: resp.WorkflowExecution, ActivityID: resp.ActivityID, ActivityType: resp.ActivityType, Input: resp.Input, ScheduledTimestamp: resp.ScheduledTimestamp, ScheduleToCloseTimeoutSeconds: resp.ScheduleToCloseTimeoutSeconds, StartedTimestamp: resp.StartedTimestamp, StartToCloseTimeoutSeconds: resp.StartToCloseTimeoutSeconds, HeartbeatTimeoutSeconds: resp.HeartbeatTimeoutSeconds, Attempt: int64(resp.Attempt), ScheduledTimestampOfThisAttempt: resp.ScheduledTimestampOfThisAttempt, HeartbeatDetails: resp.HeartbeatDetails, WorkflowType: resp.WorkflowType, WorkflowDomain: resp.WorkflowDomain, Header: resp.Header, AutoConfigHint: resp.AutoConfigHint, }, nil } resp, err := engine.PollForDecisionTask(hCtx, &types.MatchingPollForDecisionTaskRequest{ DomainUUID: request.DomainUUID, PollerID: request.PollerID, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: request.TaskList, Identity: request.Identity, BinaryChecksum: request.BinaryChecksum, }, IsolationGroup: request.IsolationGroup, ForwardedFrom: request.ForwardedFrom, }) if err != nil { return nil, err } var token *common.TaskToken if len(resp.TaskToken) > 0 { token, err = engine.tokenSerializer.Deserialize(resp.TaskToken) if err != nil { return nil, err } } return &pollTaskResponse{ TaskToken: token, WorkflowExecution: resp.WorkflowExecution, WorkflowType: resp.WorkflowType, PreviousStartedEventID: resp.PreviousStartedEventID, StartedEventID: resp.StartedEventID, Attempt: resp.Attempt, NextEventID: resp.NextEventID, BacklogCountHint: resp.BacklogCountHint, StickyExecutionEnabled: resp.StickyExecutionEnabled, Query: resp.Query, DecisionInfo: resp.DecisionInfo, WorkflowExecutionTaskList: resp.WorkflowExecutionTaskList, EventStoreVersion: resp.EventStoreVersion, BranchToken: resp.BranchToken, ScheduledTimestamp: resp.ScheduledTimestamp, StartedTimestamp: resp.StartedTimestamp, Queries: resp.Queries, AutoConfigHint: resp.AutoConfigHint, }, nil } func isEmptyToken(token *common.TaskToken) bool { return token == nil || *token == common.TaskToken{} } func getIsolationGroupsHelper() []string { return []string{"zone-a", "zone-b"} } ================================================ FILE: service/matching/handler/engine_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "context" "errors" "fmt" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" commonerrors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/tasklist" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) func mustNewIdentifier(t *testing.T, domainID, taskListName string, taskListType int) *tasklist.Identifier { t.Helper() id, err := tasklist.NewIdentifier(domainID, taskListName, taskListType) require.NoError(t, err) return id } // newMockManagerWithTaskListID returns a MockManager with TaskListID() stubbed to return id (AnyTimes). func newMockManagerWithTaskListID(ctrl *gomock.Controller, id *tasklist.Identifier) *tasklist.MockManager { mgr := tasklist.NewMockManager(ctrl) mgr.EXPECT().TaskListID().Return(id).AnyTimes() return mgr } func TestGetTaskListsByDomain(t *testing.T) { testCases := []struct { name string mockSetup func(*cache.MockDomainCache, map[tasklist.Identifier]*tasklist.MockManager, map[tasklist.Identifier]*tasklist.MockManager) returnAllKinds bool wantErr bool want *types.GetTaskListsByDomainResponse }{ { name: "domain cache error", mockSetup: func(mockDomainCache *cache.MockDomainCache, mockTaskListManagers map[tasklist.Identifier]*tasklist.MockManager, mockStickyManagers map[tasklist.Identifier]*tasklist.MockManager) { mockDomainCache.EXPECT().GetDomainID("test-domain").Return("", errors.New("cache failure")) }, wantErr: true, }, { name: "success", mockSetup: func(mockDomainCache *cache.MockDomainCache, mockTaskListManagers map[tasklist.Identifier]*tasklist.MockManager, mockStickyManagers map[tasklist.Identifier]*tasklist.MockManager) { mockDomainCache.EXPECT().GetDomainID("test-domain").Return("test-domain-id", nil) for id, mockManager := range mockTaskListManagers { if id.GetDomainID() == "test-domain-id" { mockManager.EXPECT().GetTaskListKind().Return(types.TaskListKindNormal) mockManager.EXPECT().DescribeTaskList(false).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: fmt.Sprintf("test-poller-%s", id.GetRoot()), }, }, }) } } for id, mockManager := range mockStickyManagers { if id.GetDomainID() == "test-domain-id" { mockManager.EXPECT().GetTaskListKind().Return(types.TaskListKindSticky) } } }, wantErr: false, want: &types.GetTaskListsByDomainResponse{ DecisionTaskListMap: map[string]*types.DescribeTaskListResponse{ "decision0": { Pollers: []*types.PollerInfo{ { Identity: "test-poller-decision0", }, }, }, }, ActivityTaskListMap: map[string]*types.DescribeTaskListResponse{ "activity0": { Pollers: []*types.PollerInfo{ { Identity: "test-poller-activity0", }, }, }, }, }, }, { name: "success - all kinds", returnAllKinds: true, mockSetup: func(mockDomainCache *cache.MockDomainCache, mockTaskListManagers map[tasklist.Identifier]*tasklist.MockManager, mockStickyManagers map[tasklist.Identifier]*tasklist.MockManager) { mockDomainCache.EXPECT().GetDomainID("test-domain").Return("test-domain-id", nil) for id, mockManager := range mockTaskListManagers { if id.GetDomainID() == "test-domain-id" { mockManager.EXPECT().DescribeTaskList(false).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: fmt.Sprintf("test-poller-%s", id.GetRoot()), }, }, }) } } for id, mockManager := range mockStickyManagers { if id.GetDomainID() == "test-domain-id" { mockManager.EXPECT().DescribeTaskList(false).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: fmt.Sprintf("sticky-poller-%s", id.GetRoot()), }, }, }) } } }, wantErr: false, want: &types.GetTaskListsByDomainResponse{ DecisionTaskListMap: map[string]*types.DescribeTaskListResponse{ "decision0": { Pollers: []*types.PollerInfo{ { Identity: "test-poller-decision0", }, }, }, "sticky0": { Pollers: []*types.PollerInfo{ { Identity: "sticky-poller-sticky0", }, }, }, }, ActivityTaskListMap: map[string]*types.DescribeTaskListResponse{ "activity0": { Pollers: []*types.PollerInfo{ { Identity: "test-poller-activity0", }, }, }, }, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(mockCtrl) decisionTasklistID := mustNewIdentifier(t, "test-domain-id", "decision0", 0) activityTasklistID := mustNewIdentifier(t, "test-domain-id", "activity0", 1) otherDomainTasklistID := mustNewIdentifier(t, "other-domain-id", "other0", 0) stickyTasklistID := mustNewIdentifier(t, "test-domain-id", "sticky0", 0) mockDecisionTaskListManager := newMockManagerWithTaskListID(mockCtrl, decisionTasklistID) mockActivityTaskListManager := newMockManagerWithTaskListID(mockCtrl, activityTasklistID) mockOtherDomainTaskListManager := newMockManagerWithTaskListID(mockCtrl, otherDomainTasklistID) mockStickyManager := newMockManagerWithTaskListID(mockCtrl, stickyTasklistID) mockTaskListManagers := map[tasklist.Identifier]*tasklist.MockManager{ *decisionTasklistID: mockDecisionTaskListManager, *activityTasklistID: mockActivityTaskListManager, *otherDomainTasklistID: mockOtherDomainTaskListManager, } mockStickyManagers := map[tasklist.Identifier]*tasklist.MockManager{ *stickyTasklistID: mockStickyManager, } tc.mockSetup(mockDomainCache, mockTaskListManagers, mockStickyManagers) taskListRegistry := tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()) engine := &matchingEngineImpl{ domainCache: mockDomainCache, taskListRegistry: taskListRegistry, config: &config.Config{ EnableReturnAllTaskListKinds: func(opts ...dynamicproperties.FilterOption) bool { return tc.returnAllKinds }, }, } taskListRegistry.Register(*decisionTasklistID, mockDecisionTaskListManager) taskListRegistry.Register(*activityTasklistID, mockActivityTaskListManager) taskListRegistry.Register(*otherDomainTasklistID, mockOtherDomainTaskListManager) taskListRegistry.Register(*stickyTasklistID, mockStickyManager) resp, err := engine.GetTaskListsByDomain(nil, &types.GetTaskListsByDomainRequest{Domain: "test-domain"}) if tc.wantErr { require.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tc.want, resp) } }) } } func TestListTaskListPartitions(t *testing.T) { testCases := []struct { name string req *types.MatchingListTaskListPartitionsRequest mockSetup func(*cache.MockDomainCache, *membership.MockResolver) wantErr bool want *types.ListTaskListPartitionsResponse }{ { name: "domain cache error", req: &types.MatchingListTaskListPartitionsRequest{ Domain: "test-domain", TaskList: &types.TaskList{ Name: "test-tasklist", }, }, mockSetup: func(mockDomainCache *cache.MockDomainCache, mockResolver *membership.MockResolver) { mockDomainCache.EXPECT().GetDomainID("test-domain").Return("", errors.New("cache failure")) }, wantErr: true, }, { name: "invalid tasklist name", req: &types.MatchingListTaskListPartitionsRequest{ Domain: "test-domain", TaskList: &types.TaskList{ Name: "/__cadence_sys/invalid-tasklist-name", }, }, mockSetup: func(mockDomainCache *cache.MockDomainCache, mockResolver *membership.MockResolver) { mockDomainCache.EXPECT().GetDomainID("test-domain").Return("test-domain-id", nil) }, wantErr: true, }, { name: "success", req: &types.MatchingListTaskListPartitionsRequest{ Domain: "test-domain", TaskList: &types.TaskList{ Name: "test-tasklist", }, }, mockSetup: func(mockDomainCache *cache.MockDomainCache, mockResolver *membership.MockResolver) { // activity tasklist mockDomainCache.EXPECT().GetDomainID("test-domain").Return("test-domain-id", nil) mockResolver.EXPECT().Lookup(gomock.Any(), "test-tasklist").Return(membership.NewHostInfo("addr2"), nil) mockResolver.EXPECT().Lookup(gomock.Any(), "/__cadence_sys/test-tasklist/1").Return(membership.HostInfo{}, errors.New("some error")) mockResolver.EXPECT().Lookup(gomock.Any(), "/__cadence_sys/test-tasklist/2").Return(membership.NewHostInfo("addr3"), nil) // decision tasklist mockDomainCache.EXPECT().GetDomainID("test-domain").Return("test-domain-id", nil) mockResolver.EXPECT().Lookup(gomock.Any(), "test-tasklist").Return(membership.NewHostInfo("addr0"), nil) mockResolver.EXPECT().Lookup(gomock.Any(), "/__cadence_sys/test-tasklist/1").Return(membership.HostInfo{}, errors.New("some error")) mockResolver.EXPECT().Lookup(gomock.Any(), "/__cadence_sys/test-tasklist/2").Return(membership.NewHostInfo("addr1"), nil) }, wantErr: false, want: &types.ListTaskListPartitionsResponse{ DecisionTaskListPartitions: []*types.TaskListPartitionMetadata{ { Key: "test-tasklist", OwnerHostName: "addr0", }, { Key: "/__cadence_sys/test-tasklist/1", OwnerHostName: "", }, { Key: "/__cadence_sys/test-tasklist/2", OwnerHostName: "addr1", }, }, ActivityTaskListPartitions: []*types.TaskListPartitionMetadata{ { Key: "test-tasklist", OwnerHostName: "addr2", }, { Key: "/__cadence_sys/test-tasklist/1", OwnerHostName: "", }, { Key: "/__cadence_sys/test-tasklist/2", OwnerHostName: "addr3", }, }, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockResolver := membership.NewMockResolver(mockCtrl) tc.mockSetup(mockDomainCache, mockResolver) engine := &matchingEngineImpl{ domainCache: mockDomainCache, membershipResolver: mockResolver, config: &config.Config{ NumTasklistWritePartitions: dynamicproperties.GetIntPropertyFilteredByTaskListInfo(3), }, } resp, err := engine.ListTaskListPartitions(nil, tc.req) if tc.wantErr { require.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tc.want, resp) } }) } } func TestCancelOutstandingPoll(t *testing.T) { testCases := []struct { name string req *types.CancelOutstandingPollRequest mockSetup func(mockCtrl *gomock.Controller, processor *tasklist.MockManager, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) wantErr bool }{ { name: "invalid tasklist name", req: &types.CancelOutstandingPollRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "/__cadence_sys/invalid-tasklist-name", }, PollerID: "test-poller-id", }, mockSetup: func(mockCtrl *gomock.Controller, mockManager *tasklist.MockManager, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { }, wantErr: true, }, { name: "success", req: &types.CancelOutstandingPollRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", }, PollerID: "test-poller-id", }, mockSetup: func(mockCtrl *gomock.Controller, mockManager *tasklist.MockManager, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(tasklist.NewMockShardProcessor(mockCtrl), nil) mockManager.EXPECT().CancelPoller("test-poller-id") }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) tasklistID := mustNewIdentifier(t, "test-domain-id", "test-tasklist", 0) mockManager := newMockManagerWithTaskListID(mockCtrl, tasklistID) executor := executorclient.NewMockExecutor[tasklist.ShardProcessor](mockCtrl) tc.mockSetup(mockCtrl, mockManager, executor) taskListRegistry := tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()) engine := &matchingEngineImpl{ taskListRegistry: taskListRegistry, executor: executor, config: &config.Config{ ExcludeShortLivedTaskListsFromShardManager: func(opts ...dynamicproperties.FilterOption) bool { return false }, PercentageOnboardedToShardManager: func(opts ...dynamicproperties.FilterOption) int { return 100 }, }, } taskListRegistry.Register(*tasklistID, mockManager) hCtx := &handlerContext{Context: context.Background()} err := engine.CancelOutstandingPoll(hCtx, tc.req) if tc.wantErr { require.Error(t, err) } else { require.NoError(t, err) } }) } } func TestErrIfShardOwnershipLost(t *testing.T) { taskListID := mustNewIdentifier(t, "test-domain-id", "test-tasklist", 0) newEngine := func(t *testing.T) (*matchingEngineImpl, *executorclient.MockExecutor[tasklist.ShardProcessor], *membership.MockResolver) { t.Helper() ctrl := gomock.NewController(t) executor := executorclient.NewMockExecutor[tasklist.ShardProcessor](ctrl) resolver := membership.NewMockResolver(ctrl) resolver.EXPECT().WhoAmI().Return(membership.NewDetailedHostInfo("self", "self", nil), nil).AnyTimes() engine := &matchingEngineImpl{ executor: executor, membershipResolver: resolver, config: &config.Config{ EnableTasklistOwnershipGuard: func(opts ...dynamicproperties.FilterOption) bool { return true }, ExcludeShortLivedTaskListsFromShardManager: func(opts ...dynamicproperties.FilterOption) bool { return false }, PercentageOnboardedToShardManager: func(opts ...dynamicproperties.FilterOption) int { return 100 }, }, shutdown: make(chan struct{}), logger: log.NewNoop(), } return engine, executor, resolver } assertTypedOwnershipErr := func(t *testing.T, err error, ownedBy, me string) { t.Helper() require.Error(t, err) var ownershipErr *commonerrors.TaskListNotOwnedByHostError require.ErrorAs(t, err, &ownershipErr) assert.Equal(t, ownedBy, ownershipErr.OwnedByIdentity) assert.Equal(t, me, ownershipErr.MyIdentity) } t.Run("ownership guard disabled", func(t *testing.T) { engine, _, _ := newEngine(t) engine.config.EnableTasklistOwnershipGuard = func(opts ...dynamicproperties.FilterOption) bool { return false } err := engine.errIfShardOwnershipLost(context.Background(), taskListID) require.NoError(t, err) }) t.Run("not excluded from sd with shard process error", func(t *testing.T) { engine, executor, _ := newEngine(t) executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(nil, errors.New("sd lookup failed")) err := engine.errIfShardOwnershipLost(context.Background(), taskListID) require.Error(t, err) assert.ErrorContains(t, err, "failed to lookup ownership in SD") }) t.Run("not excluded from sd with shard process not found returns ownership error", func(t *testing.T) { engine, executor, _ := newEngine(t) executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(nil, executorclient.ErrShardProcessNotFound) err := engine.errIfShardOwnershipLost(context.Background(), taskListID) assertTypedOwnershipErr(t, err, "not known", "self") }) t.Run("not excluded from sd and shard no longer owned", func(t *testing.T) { engine, executor, _ := newEngine(t) executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(nil, nil) err := engine.errIfShardOwnershipLost(context.Background(), taskListID) assertTypedOwnershipErr(t, err, "not known", "self") }) t.Run("not excluded from sd and shard is still owned by this host", func(t *testing.T) { engine, executor, _ := newEngine(t) executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()). Return(&tasklist.MockShardProcessor{}, nil). // not nil being returned because the shard is still owned by this host AnyTimes() err := engine.errIfShardOwnershipLost(context.Background(), taskListID) require.NoError(t, err) }) t.Run("matching engine is shutting down", func(t *testing.T) { engine, _, _ := newEngine(t) close(engine.shutdown) err := engine.errIfShardOwnershipLost(context.Background(), taskListID) assertTypedOwnershipErr(t, err, "not known", "self") }) t.Run("excluded from sd, ringpop and owner has changed", func(t *testing.T) { engine, _, resolver := newEngine(t) engine.config.ExcludeShortLivedTaskListsFromShardManager = func(opts ...dynamicproperties.FilterOption) bool { return true } excludedID := mustNewIdentifier(t, "test-domain-id", "tasklist-550e8400-e29b-41d4-a716-446655440000", 0) resolver.EXPECT().Lookup(service.Matching, excludedID.GetName()).Return(membership.NewDetailedHostInfo("owner", "owner", nil), nil) err := engine.errIfShardOwnershipLost(context.Background(), excludedID) assertTypedOwnershipErr(t, err, "owner", "self") }) t.Run("excluded from sd, ringpop and owner is the same", func(t *testing.T) { engine, _, resolver := newEngine(t) engine.config.ExcludeShortLivedTaskListsFromShardManager = func(opts ...dynamicproperties.FilterOption) bool { return true } excludedID := mustNewIdentifier(t, "test-domain-id", "tasklist-550e8400-e29b-41d4-a716-446655440000", 0) resolver.EXPECT().Lookup(service.Matching, excludedID.GetName()).Return(membership.NewDetailedHostInfo("self", "self", nil), nil) err := engine.errIfShardOwnershipLost(context.Background(), excludedID) require.NoError(t, err) }) t.Run("non-excluded tasklist with uuid-like name and flag disabled still uses executor", func(t *testing.T) { engine, executor, _ := newEngine(t) engine.config.ExcludeShortLivedTaskListsFromShardManager = func(opts ...dynamicproperties.FilterOption) bool { return false } uuidID := mustNewIdentifier(t, "test-domain-id", "tasklist-550e8400-e29b-41d4-a716-446655440000", 0) executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(&tasklist.MockShardProcessor{}, nil) err := engine.errIfShardOwnershipLost(context.Background(), uuidID) require.NoError(t, err) }) } func TestIsExcludedFromShardDistributor(t *testing.T) { tests := []struct { name string taskListName string flagEnabled bool want bool }{ { name: "flag disabled, uuid name", taskListName: "tasklist-550e8400-e29b-41d4-a716-446655440000", flagEnabled: false, want: false, }, { name: "flag enabled, no uuid in name", taskListName: "my-regular-tasklist", flagEnabled: true, want: false, }, { name: "flag enabled, uuid in name", taskListName: "tasklist-550e8400-e29b-41d4-a716-446655440000", flagEnabled: true, want: true, }, { name: "flag enabled, uuid-only name", taskListName: "550e8400-e29b-41d4-a716-446655440000", flagEnabled: true, want: true, }, { name: "flag disabled, no uuid in name", taskListName: "my-regular-tasklist", flagEnabled: false, want: false, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { engine := &matchingEngineImpl{ config: &config.Config{ ExcludeShortLivedTaskListsFromShardManager: func(opts ...dynamicproperties.FilterOption) bool { return tc.flagEnabled }, PercentageOnboardedToShardManager: func(opts ...dynamicproperties.FilterOption) int { return 100 }, }, } got := engine.isExcludedFromShardDistributor(tc.taskListName) assert.Equal(t, tc.want, got) }) } } func TestRespondQueryTaskCompleted(t *testing.T) { testCases := []struct { name string req *types.MatchingRespondQueryTaskCompletedRequest queryTaskMap map[string]chan *queryResult wantErr bool }{ { name: "success", req: &types.MatchingRespondQueryTaskCompletedRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", }, TaskID: "id-0", }, queryTaskMap: map[string]chan *queryResult{ "id-0": make(chan *queryResult, 1), }, wantErr: false, }, { name: "query task not found", req: &types.MatchingRespondQueryTaskCompletedRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", }, TaskID: "id-0", }, queryTaskMap: map[string]chan *queryResult{}, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { engine := &matchingEngineImpl{ lockableQueryTaskMap: lockableQueryTaskMap{ queryTaskMap: tc.queryTaskMap, }, } err := engine.RespondQueryTaskCompleted(&handlerContext{scope: metrics.NewNoopMetricsClient().Scope(0)}, tc.req) if tc.wantErr { require.Error(t, err) } else { require.NoError(t, err) } }) } } func TestQueryWorkflow(t *testing.T) { testCases := []struct { name string req *types.MatchingQueryWorkflowRequest hCtx *handlerContext mockSetup func(*tasklist.MockManager, *lockableQueryTaskMap, *gomock.Controller, *executorclient.MockExecutor[tasklist.ShardProcessor]) wantErr bool want *types.MatchingQueryWorkflowResponse }{ { name: "invalid tasklist name", req: &types.MatchingQueryWorkflowRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "/__cadence_sys/invalid-tasklist-name", }, }, mockSetup: func(mockManager *tasklist.MockManager, queryResultMap *lockableQueryTaskMap, mockCtrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { }, wantErr: true, }, { name: "sticky worker unavailable", req: &types.MatchingQueryWorkflowRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", Kind: types.TaskListKindSticky.Ptr(), }, }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, queryResultMap *lockableQueryTaskMap, mockCtrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(tasklist.NewMockShardProcessor(mockCtrl), nil) mockManager.EXPECT().HasPollerAfter(gomock.Any()).Return(false) }, wantErr: true, }, { name: "failed to dispatch query task", req: &types.MatchingQueryWorkflowRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", }, }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, queryResultMap *lockableQueryTaskMap, mockCtrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(tasklist.NewMockShardProcessor(mockCtrl), nil) mockManager.EXPECT().DispatchQueryTask(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("some error")) }, wantErr: true, }, { name: "success", req: &types.MatchingQueryWorkflowRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", }, }, hCtx: &handlerContext{ Context: func() context.Context { return context.Background() }(), }, mockSetup: func(mockManager *tasklist.MockManager, queryResultMap *lockableQueryTaskMap, mockCtrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(tasklist.NewMockShardProcessor(mockCtrl), nil) mockManager.EXPECT().DispatchQueryTask(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, taskID string, request *types.MatchingQueryWorkflowRequest) (*types.MatchingQueryWorkflowResponse, error) { queryResChan, ok := queryResultMap.get(taskID) if !ok { return nil, errors.New("cannot find query result channel by taskID") } queryResChan <- &queryResult{workerResponse: &types.MatchingRespondQueryTaskCompletedRequest{ TaskID: taskID, CompletedRequest: &types.RespondQueryTaskCompletedRequest{ QueryResult: []byte("some result"), }, }} return nil, nil }) mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }) }, wantErr: false, want: &types.MatchingQueryWorkflowResponse{ QueryResult: []byte("some result"), PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) tasklistID := mustNewIdentifier(t, "test-domain-id", "test-tasklist", 0) mockManager := newMockManagerWithTaskListID(mockCtrl, tasklistID) executor := executorclient.NewMockExecutor[tasklist.ShardProcessor](mockCtrl) taskListRegistry := tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()) engine := &matchingEngineImpl{ taskListRegistry: taskListRegistry, timeSource: clock.NewRealTimeSource(), lockableQueryTaskMap: lockableQueryTaskMap{queryTaskMap: make(map[string]chan *queryResult)}, executor: executor, config: &config.Config{ ExcludeShortLivedTaskListsFromShardManager: func(opts ...dynamicproperties.FilterOption) bool { return false }, PercentageOnboardedToShardManager: func(opts ...dynamicproperties.FilterOption) int { return 100 }, }, } taskListRegistry.Register(*tasklistID, mockManager) tc.mockSetup(mockManager, &engine.lockableQueryTaskMap, mockCtrl, executor) resp, err := engine.QueryWorkflow(tc.hCtx, tc.req) if tc.wantErr { require.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tc.want, resp) } }) } } func TestWaitForQueryResult(t *testing.T) { testCases := []struct { name string result *queryResult mockSetup func(*client.MockVersionChecker) wantErr bool assertErr func(*testing.T, error) want *types.MatchingQueryWorkflowResponse }{ { name: "internal error", result: &queryResult{ internalError: errors.New("some error"), }, mockSetup: func(mockVersionChecker *client.MockVersionChecker) {}, assertErr: func(t *testing.T, err error) { assert.Equal(t, "some error", err.Error()) }, wantErr: true, }, { name: "strong consistency query not supported", result: &queryResult{ workerResponse: &types.MatchingRespondQueryTaskCompletedRequest{ CompletedRequest: &types.RespondQueryTaskCompletedRequest{ WorkerVersionInfo: &types.WorkerVersionInfo{ Impl: "uber-go", FeatureVersion: "1.0.0", }, }, }, }, mockSetup: func(mockVersionChecker *client.MockVersionChecker) { mockVersionChecker.EXPECT().SupportsConsistentQuery("uber-go", "1.0.0").Return(errors.New("version error")) }, assertErr: func(t *testing.T, err error) { assert.Equal(t, "version error", err.Error()) }, wantErr: true, }, { name: "success - query task completed", result: &queryResult{ workerResponse: &types.MatchingRespondQueryTaskCompletedRequest{ CompletedRequest: &types.RespondQueryTaskCompletedRequest{ WorkerVersionInfo: &types.WorkerVersionInfo{ Impl: "uber-go", FeatureVersion: "1.0.0", }, CompletedType: types.QueryTaskCompletedTypeCompleted.Ptr(), QueryResult: []byte("some result"), }, }, }, mockSetup: func(mockVersionChecker *client.MockVersionChecker) { mockVersionChecker.EXPECT().SupportsConsistentQuery("uber-go", "1.0.0").Return(nil) }, wantErr: false, want: &types.MatchingQueryWorkflowResponse{ QueryResult: []byte("some result"), }, }, { name: "query task failed", result: &queryResult{ workerResponse: &types.MatchingRespondQueryTaskCompletedRequest{ CompletedRequest: &types.RespondQueryTaskCompletedRequest{ WorkerVersionInfo: &types.WorkerVersionInfo{ Impl: "uber-go", FeatureVersion: "1.0.0", }, CompletedType: types.QueryTaskCompletedTypeFailed.Ptr(), ErrorMessage: "query failed", }, }, }, mockSetup: func(mockVersionChecker *client.MockVersionChecker) { mockVersionChecker.EXPECT().SupportsConsistentQuery("uber-go", "1.0.0").Return(nil) }, assertErr: func(t *testing.T, err error) { var e *types.QueryFailedError assert.ErrorAs(t, err, &e) assert.Equal(t, "query failed", e.Message) }, wantErr: true, }, { name: "unknown query result", result: &queryResult{ workerResponse: &types.MatchingRespondQueryTaskCompletedRequest{ CompletedRequest: &types.RespondQueryTaskCompletedRequest{ WorkerVersionInfo: &types.WorkerVersionInfo{ Impl: "uber-go", FeatureVersion: "1.0.0", }, CompletedType: types.QueryTaskCompletedType(100).Ptr(), }, }, }, mockSetup: func(mockVersionChecker *client.MockVersionChecker) { mockVersionChecker.EXPECT().SupportsConsistentQuery("uber-go", "1.0.0").Return(nil) }, assertErr: func(t *testing.T, err error) { var e *types.InternalServiceError assert.ErrorAs(t, err, &e) assert.Equal(t, "unknown query completed type", e.Message) }, wantErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockVersionChecker := client.NewMockVersionChecker(mockCtrl) tc.mockSetup(mockVersionChecker) engine := &matchingEngineImpl{ versionChecker: mockVersionChecker, } hCtx := &handlerContext{ Context: context.Background(), } ch := make(chan *queryResult, 1) ch <- tc.result resp, err := engine.waitForQueryResult(hCtx, true, ch) if tc.wantErr { require.Error(t, err) if tc.assertErr != nil { tc.assertErr(t, err) } } else { require.NoError(t, err) assert.Equal(t, tc.want, resp) } }) } } func TestIsShuttingDown(t *testing.T) { wg := sync.WaitGroup{} wg.Add(0) mockCtrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(gomock.NewController(t)) mockDomainCache.EXPECT().RegisterDomainChangeCallback(service.Matching, gomock.Any(), gomock.Any(), gomock.Any()).Times(1) mockDomainCache.EXPECT().UnregisterDomainChangeCallback(service.Matching).Times(1) mockExecutor := executorclient.NewMockExecutor[tasklist.ShardProcessor](mockCtrl) mockExecutor.EXPECT().Start(gomock.Any()) mockExecutor.EXPECT().Stop() e := matchingEngineImpl{ domainCache: mockDomainCache, shutdownCompletion: &wg, shutdown: make(chan struct{}), executor: mockExecutor, taskListRegistry: tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()), } e.Start() assert.False(t, e.isShuttingDown()) e.Stop() assert.True(t, e.isShuttingDown()) } func TestGetTasklistsNotOwned(t *testing.T) { ctrl := gomock.NewController(t) resolver := membership.NewMockResolver(ctrl) resolver.EXPECT().WhoAmI().Return(membership.NewDetailedHostInfo("self", "host123", nil), nil) tl1 := mustNewIdentifier(t, "", "tl1", 0) tl2 := mustNewIdentifier(t, "", "tl2", 0) tl3 := mustNewIdentifier(t, "", "tl3", 0) tl1m := newMockManagerWithTaskListID(ctrl, tl1) tl2m := newMockManagerWithTaskListID(ctrl, tl2) tl3m := newMockManagerWithTaskListID(ctrl, tl3) resolver.EXPECT().Lookup(service.Matching, tl1.GetName()).Return(membership.NewDetailedHostInfo("", "host123", nil), nil) resolver.EXPECT().Lookup(service.Matching, tl2.GetName()).Return(membership.NewDetailedHostInfo("", "host456", nil), nil) resolver.EXPECT().Lookup(service.Matching, tl3.GetName()).Return(membership.NewDetailedHostInfo("", "host123", nil), nil) e := matchingEngineImpl{ shutdown: make(chan struct{}), membershipResolver: resolver, taskListRegistry: tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()), config: &config.Config{ EnableTasklistOwnershipGuard: func(opts ...dynamicproperties.FilterOption) bool { return true }, }, logger: log.NewNoop(), } e.taskListRegistry.Register(*tl1, tl1m) e.taskListRegistry.Register(*tl2, tl2m) e.taskListRegistry.Register(*tl3, tl3m) tls, err := e.getNonOwnedTasklistsLocked() assert.NoError(t, err) assert.Equal(t, []tasklist.Manager{tl2m}, tls) } func TestShutDownTasklistsNotOwned(t *testing.T) { ctrl := gomock.NewController(t) resolver := membership.NewMockResolver(ctrl) resolver.EXPECT().WhoAmI().Return(membership.NewDetailedHostInfo("self", "host123", nil), nil) tl1 := mustNewIdentifier(t, "", "tl1", 0) tl2 := mustNewIdentifier(t, "", "tl2", 0) tl3 := mustNewIdentifier(t, "", "tl3", 0) tl1m := newMockManagerWithTaskListID(ctrl, tl1) tl2m := newMockManagerWithTaskListID(ctrl, tl2) tl3m := newMockManagerWithTaskListID(ctrl, tl3) resolver.EXPECT().Lookup(service.Matching, tl1.GetName()).Return(membership.NewDetailedHostInfo("", "host123", nil), nil) resolver.EXPECT().Lookup(service.Matching, tl2.GetName()).Return(membership.NewDetailedHostInfo("", "host456", nil), nil) resolver.EXPECT().Lookup(service.Matching, tl3.GetName()).Return(membership.NewDetailedHostInfo("", "host123", nil), nil) e := matchingEngineImpl{ shutdown: make(chan struct{}), membershipResolver: resolver, taskListRegistry: tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()), config: &config.Config{ EnableTasklistOwnershipGuard: func(opts ...dynamicproperties.FilterOption) bool { return true }, }, metricsClient: metrics.NewNoopMetricsClient(), logger: log.NewNoop(), } e.taskListRegistry.Register(*tl1, tl1m) e.taskListRegistry.Register(*tl2, tl2m) e.taskListRegistry.Register(*tl3, tl3m) wg := sync.WaitGroup{} wg.Add(1) tl2m.EXPECT().String().AnyTimes() tl2m.EXPECT().Stop().Do(func() { wg.Done() }) err := e.shutDownNonOwnedTasklists() wg.Wait() assert.NoError(t, err) } func TestUpdateTaskListPartitionConfig(t *testing.T) { testCases := []struct { name string req *types.MatchingUpdateTaskListPartitionConfigRequest enableAdaptiveScaler bool hCtx *handlerContext mockSetup func(*tasklist.MockManager, *gomock.Controller, *executorclient.MockExecutor[tasklist.ShardProcessor]) expectError bool expectedError string }{ { name: "success", req: &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", }, TaskListType: types.TaskListTypeActivity.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, ctrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(tasklist.NewMockShardProcessor(ctrl), nil) mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }).Return(nil) }, expectError: false, }, { name: "tasklist manager error", req: &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", }, TaskListType: types.TaskListTypeActivity.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, ctrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { executor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(tasklist.NewMockShardProcessor(ctrl), nil) mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }).Return(errors.New("tasklist manager error")) }, expectError: true, expectedError: "tasklist manager error", }, { name: "non root partition error", req: &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "/__cadence_sys/test-tasklist/1", }, TaskListType: types.TaskListTypeActivity.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, ctrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { }, expectError: true, expectedError: "Only root partition's partition config can be updated.", }, { name: "invalid tasklist name", req: &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "/__cadence_sys/test-tasklist", }, TaskListType: types.TaskListTypeActivity.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, ctrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { }, expectError: true, expectedError: "invalid partitioned task list name /__cadence_sys/test-tasklist", }, { name: "nil partition config", req: &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", }, TaskListType: types.TaskListTypeActivity.Ptr(), }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, ctrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { }, expectError: true, expectedError: "Task list partition config is not set in the request.", }, { name: "invalid tasklist kind", req: &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", Kind: types.TaskListKindSticky.Ptr(), }, TaskListType: types.TaskListTypeActivity.Ptr(), }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, ctrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { }, expectError: true, expectedError: "Only normal tasklist's partition config can be updated.", }, { name: "manual update not allowed", req: &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", Kind: types.TaskListKindSticky.Ptr(), }, TaskListType: types.TaskListTypeActivity.Ptr(), }, enableAdaptiveScaler: true, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, ctrl *gomock.Controller, executor *executorclient.MockExecutor[tasklist.ShardProcessor]) { }, expectError: true, expectedError: "Manual update is not allowed because adaptive scaler is enabled.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(mockCtrl) mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil) tasklistID := mustNewIdentifier(t, "test-domain-id", "test-tasklist", 1) mockManager := newMockManagerWithTaskListID(mockCtrl, tasklistID) mockExecutor := executorclient.NewMockExecutor[tasklist.ShardProcessor](mockCtrl) tc.mockSetup(mockManager, mockCtrl, mockExecutor) taskListRegistry := tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()) engine := &matchingEngineImpl{ taskListRegistry: taskListRegistry, timeSource: clock.NewRealTimeSource(), domainCache: mockDomainCache, config: &config.Config{ EnableAdaptiveScaler: dynamicproperties.GetBoolPropertyFilteredByTaskListInfo(tc.enableAdaptiveScaler), ExcludeShortLivedTaskListsFromShardManager: func(opts ...dynamicproperties.FilterOption) bool { return false }, PercentageOnboardedToShardManager: func(opts ...dynamicproperties.FilterOption) int { return 100 }, }, executor: mockExecutor, } taskListRegistry.Register(*tasklistID, mockManager) _, err := engine.UpdateTaskListPartitionConfig(tc.hCtx, tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestRefreshTaskListPartitionConfig(t *testing.T) { testCases := []struct { name string req *types.MatchingRefreshTaskListPartitionConfigRequest hCtx *handlerContext mockSetup func(*tasklist.MockManager, *gomock.Controller, *executorclient.MockExecutor[tasklist.ShardProcessor]) expectError bool expectedError string }{ { name: "success", req: &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "/__cadence_sys/test-tasklist/1", }, TaskListType: types.TaskListTypeActivity.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, mockCtrl *gomock.Controller, mockExecutor *executorclient.MockExecutor[tasklist.ShardProcessor]) { mockExecutor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(tasklist.NewMockShardProcessor(mockCtrl), nil) mockManager.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }).Return(nil) }, expectError: false, }, { name: "tasklist manager error", req: &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "/__cadence_sys/test-tasklist/1", }, TaskListType: types.TaskListTypeActivity.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, mockCtrl *gomock.Controller, mockExecutor *executorclient.MockExecutor[tasklist.ShardProcessor]) { mockExecutor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(tasklist.NewMockShardProcessor(mockCtrl), nil) mockManager.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }).Return(errors.New("tasklist manager error")) }, expectError: true, expectedError: "tasklist manager error", }, { name: "invalid tasklist name", req: &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "/__cadence_sys/test-tasklist", }, TaskListType: types.TaskListTypeActivity.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, ctrl *gomock.Controller, mockExecutor *executorclient.MockExecutor[tasklist.ShardProcessor]) { }, expectError: true, expectedError: "invalid partitioned task list name /__cadence_sys/test-tasklist", }, { name: "invalid tasklist kind", req: &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", Kind: types.TaskListKindSticky.Ptr(), }, TaskListType: types.TaskListTypeActivity.Ptr(), }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, mockCtrl *gomock.Controller, mockExecutor *executorclient.MockExecutor[tasklist.ShardProcessor]) { }, expectError: true, expectedError: "Only normal tasklist's partition config can be updated.", }, { name: "invalid request for root partition", req: &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{ Name: "test-tasklist", }, TaskListType: types.TaskListTypeActivity.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }, }, hCtx: &handlerContext{ Context: context.Background(), }, mockSetup: func(mockManager *tasklist.MockManager, ctrl *gomock.Controller, mockExecutor *executorclient.MockExecutor[tasklist.ShardProcessor]) { }, expectError: true, expectedError: "PartitionConfig must be nil for root partition.", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) tasklistID := mustNewIdentifier(t, "test-domain-id", "test-tasklist", 1) tasklistID2 := mustNewIdentifier(t, "test-domain-id", "/__cadence_sys/test-tasklist/1", 1) mockManager := newMockManagerWithTaskListID(mockCtrl, tasklistID) mockExecutor := executorclient.NewMockExecutor[tasklist.ShardProcessor](mockCtrl) tc.mockSetup(mockManager, mockCtrl, mockExecutor) taskListRegistry := tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()) engine := &matchingEngineImpl{ taskListRegistry: taskListRegistry, timeSource: clock.NewRealTimeSource(), executor: mockExecutor, config: &config.Config{ ExcludeShortLivedTaskListsFromShardManager: func(opts ...dynamicproperties.FilterOption) bool { return false }, PercentageOnboardedToShardManager: func(opts ...dynamicproperties.FilterOption) int { return 100 }, }, } taskListRegistry.Register(*tasklistID, mockManager) taskListRegistry.Register(*tasklistID2, mockManager) _, err := engine.RefreshTaskListPartitionConfig(tc.hCtx, tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } }) } } func Test_domainChangeCallback(t *testing.T) { mockCtrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(mockCtrl) clusters := []string{"cluster0", "cluster1"} tlGlobalDecision1 := mustNewIdentifier(t, "global-domain-1-id", "global-domain-1", persistence.TaskListTypeDecision) tlGlobalActivity1 := mustNewIdentifier(t, "global-domain-1-id", "global-domain-1", persistence.TaskListTypeActivity) tlGlobalDecision2 := mustNewIdentifier(t, "global-domain-2-id", "global-domain-2", persistence.TaskListTypeDecision) tlGlobalActivity2 := mustNewIdentifier(t, "global-domain-2-id", "global-domain-2", persistence.TaskListTypeActivity) tlGlobalSticky2 := mustNewIdentifier(t, "global-domain-2-id", "sticky-global-domain-2", persistence.TaskListTypeDecision) tlGlobalActivity3 := mustNewIdentifier(t, "global-domain-3-id", "global-domain-3", persistence.TaskListTypeActivity) tlGlobalDecision3 := mustNewIdentifier(t, "global-domain-3-id", "global-domain-3", persistence.TaskListTypeDecision) tlGlobalSticky3 := mustNewIdentifier(t, "global-domain-3-id", "sticky-global-domain-3", persistence.TaskListTypeDecision) tlLocalDecision1 := mustNewIdentifier(t, "local-domain-1-id", "local-domain-1", persistence.TaskListTypeDecision) tlLocalActivity1 := mustNewIdentifier(t, "local-domain-1-id", "local-domain-1", persistence.TaskListTypeActivity) tlActiveActiveDecision1 := mustNewIdentifier(t, "active-active-domain-1-id", "active-active-domain-1", persistence.TaskListTypeDecision) tlActiveActiveActivity1 := mustNewIdentifier(t, "active-active-domain-1-id", "active-active-domain-1", persistence.TaskListTypeActivity) newNormalManager := func(id *tasklist.Identifier) *tasklist.MockManager { mgr := newMockManagerWithTaskListID(mockCtrl, id) mgr.EXPECT().GetTaskListKind().Return(types.TaskListKindNormal).AnyTimes() mgr.EXPECT().DescribeTaskList(gomock.Any()).Return(&types.DescribeTaskListResponse{}).AnyTimes() return mgr } newStickyManager := func(id *tasklist.Identifier) *tasklist.MockManager { mgr := newMockManagerWithTaskListID(mockCtrl, id) mgr.EXPECT().GetTaskListKind().Return(types.TaskListKindSticky).AnyTimes() mgr.EXPECT().DescribeTaskList(gomock.Any()).Return(&types.DescribeTaskListResponse{}).AnyTimes() return mgr } mockGlobalDecision1 := newNormalManager(tlGlobalDecision1) mockGlobalActivity1 := newNormalManager(tlGlobalActivity1) mockGlobalDecision2 := newNormalManager(tlGlobalDecision2) mockGlobalActivity2 := newNormalManager(tlGlobalActivity2) mockGlobalSticky2 := newStickyManager(tlGlobalSticky2) mockGlobalDecision3 := newNormalManager(tlGlobalDecision3) mockGlobalActivity3 := newNormalManager(tlGlobalActivity3) mockGlobalSticky3 := newStickyManager(tlGlobalSticky3) mockLocalDecision1 := newNormalManager(tlLocalDecision1) mockLocalActivity1 := newNormalManager(tlLocalActivity1) mockActiveActiveDecision1 := newNormalManager(tlActiveActiveDecision1) mockActiveActiveActivity1 := newNormalManager(tlActiveActiveActivity1) mockExecutor := executorclient.NewMockExecutor[tasklist.ShardProcessor](mockCtrl) mockExecutor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(tasklist.NewMockShardProcessor(mockCtrl), nil).AnyTimes() engine := &matchingEngineImpl{ domainCache: mockDomainCache, failoverNotificationVersion: 1, config: defaultTestConfig(), logger: log.NewNoop(), taskListRegistry: tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()), executor: mockExecutor, } engine.taskListRegistry.Register(*tlGlobalDecision1, mockGlobalDecision1) engine.taskListRegistry.Register(*tlGlobalActivity1, mockGlobalActivity1) engine.taskListRegistry.Register(*tlGlobalDecision2, mockGlobalDecision2) engine.taskListRegistry.Register(*tlGlobalActivity2, mockGlobalActivity2) engine.taskListRegistry.Register(*tlGlobalSticky2, mockGlobalSticky2) engine.taskListRegistry.Register(*tlGlobalDecision3, mockGlobalDecision3) engine.taskListRegistry.Register(*tlGlobalActivity3, mockGlobalActivity3) engine.taskListRegistry.Register(*tlGlobalSticky3, mockGlobalSticky3) engine.taskListRegistry.Register(*tlLocalDecision1, mockLocalDecision1) engine.taskListRegistry.Register(*tlLocalActivity1, mockLocalActivity1) engine.taskListRegistry.Register(*tlActiveActiveDecision1, mockActiveActiveDecision1) engine.taskListRegistry.Register(*tlActiveActiveActivity1, mockActiveActiveActivity1) // Eligible for failover handling is defined by isDomainEligibleToDisconnectPollers. mockGlobalDecision1.EXPECT().ReleaseBlockedPollers().Times(0) // global-domain-1 has failover version 0 (<= current 1), so not eligible. mockGlobalActivity1.EXPECT().ReleaseBlockedPollers().Times(0) // global-domain-1 has failover version 0 (<= current 1), so not eligible. mockGlobalDecision2.EXPECT().ReleaseBlockedPollers().Times(1) // global-domain-2 is global, non-active-active, and version 4 > 1. mockGlobalActivity2.EXPECT().ReleaseBlockedPollers().Times(1) // global-domain-2 is global, non-active-active, and version 4 > 1. mockGlobalSticky2.EXPECT().ReleaseBlockedPollers().Times(1) // sticky task list under eligible global-domain-2. mockGlobalDecision3.EXPECT().ReleaseBlockedPollers().Times(1) // global-domain-3 is eligible. mockGlobalActivity3.EXPECT().ReleaseBlockedPollers().Times(1) // global-domain-3 is eligible. mockGlobalSticky3.EXPECT().ReleaseBlockedPollers().Times(1) // sticky task list under eligible global-domain-3. mockLocalDecision1.EXPECT().ReleaseBlockedPollers().Times(0) // local domains are not eligible. mockLocalActivity1.EXPECT().ReleaseBlockedPollers().Times(0) // local domains are not eligible. mockActiveActiveDecision1.EXPECT().ReleaseBlockedPollers().Times(0) // active-active domains are not eligible. mockActiveActiveActivity1.EXPECT().ReleaseBlockedPollers().Times(0) // active-active domains are not eligible. domains := []*cache.DomainCacheEntry{ cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "global-domain-1", ID: "global-domain-1-id"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: clusters[0], Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "cluster0"}, {ClusterName: "cluster1"}}}, 0, nil, 0, 0, 6, ), cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "global-domain-2", ID: "global-domain-2-id"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: clusters[1], Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "cluster0"}, {ClusterName: "cluster1"}}}, 0, nil, 4, 0, 4, ), cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "global-domain-3", ID: "global-domain-3-id"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: clusters[1], Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: "cluster0"}, {ClusterName: "cluster1"}}}, 0, nil, 5, 0, 5, ), cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "local-domain-1", ID: "local-domain-1-id"}, nil, false, nil, 0, nil, 0, 0, 3, ), cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "active-active-domain-1", ID: "active-active-domain-1-id"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "us-west": { ActiveClusterName: "cluster0", FailoverVersion: 1, }, }, }, }, }}, 0, nil, 0, 0, 2, ), } engine.domainChangeCallback(domains) assert.Equal(t, int64(5), engine.failoverNotificationVersion, "5 is the highest failover notification version in the fixtures (global-domain-3)") } func Test_registerDomainFailoverCallback(t *testing.T) { ctrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(ctrl) // Capture the registered catchUpFn var registeredCatchUpFn func(cache.DomainCache, cache.PrepareCallbackFn, cache.CallbackFn) mockDomainCache.EXPECT().RegisterDomainChangeCallback( service.Matching, // id of the callback gomock.Any(), // catchUpFn gomock.Any(), // lockTaskProcessingForDomainUpdate gomock.Any(), // domainChangeCB ).Do(func(_ string, catchUpFn, _, _ interface{}) { if fn, ok := catchUpFn.(cache.CatchUpFn); ok { registeredCatchUpFn = fn } else { t.Fatalf("Failed to convert catchUpFn to cache.CatchUpFn: got type %T", catchUpFn) } }).Times(1) engine := &matchingEngineImpl{ domainCache: mockDomainCache, failoverNotificationVersion: 0, config: defaultTestConfig(), logger: log.NewNoop(), taskListRegistry: tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()), } engine.registerDomainFailoverCallback() t.Run("catchUpFn - No failoverNotificationVersion updates", func(t *testing.T) { mockDomainCache.EXPECT().GetAllDomain().Return(map[string]*cache.DomainCacheEntry{ "uuid-domain1": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "uuid-domain1", Name: "domain1"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "A"}, 0, nil, 0, 0, 1, ), "uuid-domain2": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "uuid-domain2", Name: "domain2"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "A"}, 0, nil, 0, 0, 4, ), }) prepareCalled := false callbackCalled := false prepare := func() { prepareCalled = true } callback := func([]*cache.DomainCacheEntry) { callbackCalled = true } if registeredCatchUpFn != nil { registeredCatchUpFn(mockDomainCache, prepare, callback) assert.False(t, prepareCalled, "prepareCallback should not be called") assert.False(t, callbackCalled, "callback should not be called") } else { assert.Fail(t, "catchUpFn was not registered") } assert.Equal(t, int64(0), engine.failoverNotificationVersion) }) t.Run("catchUpFn - No failoverNotificationVersion updates", func(t *testing.T) { mockDomainCache.EXPECT().GetAllDomain().Return(map[string]*cache.DomainCacheEntry{ "uuid-domain1": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "uuid-domain1", Name: "domain1"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "A"}, 0, nil, 3, 0, 3, ), "uuid-domain2": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "uuid-domain2", Name: "domain2"}, nil, true, &persistence.DomainReplicationConfig{ActiveClusterName: "A"}, 0, nil, 2, 0, 4, ), }) prepareCalled := false callbackCalled := false prepare := func() { prepareCalled = true } callback := func([]*cache.DomainCacheEntry) { callbackCalled = true } if registeredCatchUpFn != nil { registeredCatchUpFn(mockDomainCache, prepare, callback) assert.False(t, prepareCalled, "prepareCallback should not be called") assert.False(t, callbackCalled, "callback should not be called") } else { assert.Fail(t, "catchUpFn was not registered") } assert.Equal(t, int64(3), engine.failoverNotificationVersion) }) } func TestRefreshWorkflowTasks(t *testing.T) { testCases := []struct { name string ctx context.Context domainID string workflowExecution *types.WorkflowExecution mockSetup func(*history.MockClient) wantErr bool assertErr func(*testing.T, error) }{ { name: "success", ctx: context.Background(), domainID: "test-domain-id", workflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, mockSetup: func(mockHistoryService *history.MockClient) { mockHistoryService.EXPECT().RefreshWorkflowTasks( gomock.Any(), &types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: "test-domain-id", Request: &types.RefreshWorkflowTasksRequest{ Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, }, ).Return(nil) }, wantErr: false, }, { name: "entity not exists error - returns nil", ctx: context.Background(), domainID: "test-domain-id", workflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, mockSetup: func(mockHistoryService *history.MockClient) { mockHistoryService.EXPECT().RefreshWorkflowTasks( gomock.Any(), gomock.Any(), ).Return(&types.EntityNotExistsError{Message: "workflow not found"}) }, wantErr: false, }, { name: "internal service error", ctx: context.Background(), domainID: "test-domain-id", workflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, mockSetup: func(mockHistoryService *history.MockClient) { mockHistoryService.EXPECT().RefreshWorkflowTasks( gomock.Any(), gomock.Any(), ).Return(&types.InternalServiceError{Message: "internal error"}) }, wantErr: true, assertErr: func(t *testing.T, err error) { var serviceErr *types.InternalServiceError assert.ErrorAs(t, err, &serviceErr) assert.Equal(t, "internal error", serviceErr.Message) }, }, { name: "generic error propagated", ctx: context.Background(), domainID: "test-domain-id", workflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, mockSetup: func(mockHistoryService *history.MockClient) { mockHistoryService.EXPECT().RefreshWorkflowTasks( gomock.Any(), gomock.Any(), ).Return(errors.New("some error")) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, "some error", err.Error()) }, }, { name: "workflow execution already completed error", ctx: context.Background(), domainID: "test-domain-id", workflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, mockSetup: func(mockHistoryService *history.MockClient) { mockHistoryService.EXPECT().RefreshWorkflowTasks( gomock.Any(), gomock.Any(), ).Return(&types.WorkflowExecutionAlreadyCompletedError{Message: "workflow already completed"}) }, wantErr: true, assertErr: func(t *testing.T, err error) { var completedErr *types.WorkflowExecutionAlreadyCompletedError assert.ErrorAs(t, err, &completedErr) assert.Equal(t, "workflow already completed", completedErr.Message) }, }, { name: "context canceled", ctx: func() context.Context { ctx, cancel := context.WithCancel(context.Background()); cancel(); return ctx }(), domainID: "test-domain-id", workflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, mockSetup: func(mockHistoryService *history.MockClient) { mockHistoryService.EXPECT().RefreshWorkflowTasks( gomock.Any(), gomock.Any(), ).Return(context.Canceled) }, wantErr: true, assertErr: func(t *testing.T, err error) { assert.Equal(t, context.Canceled, err) }, }, { name: "empty domain id", ctx: context.Background(), domainID: "", workflowExecution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, mockSetup: func(mockHistoryService *history.MockClient) { mockHistoryService.EXPECT().RefreshWorkflowTasks( gomock.Any(), &types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: "", Request: &types.RefreshWorkflowTasksRequest{ Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: "test-run-id", }, }, }, ).Return(nil) }, wantErr: false, }, { name: "nil workflow execution", ctx: context.Background(), domainID: "test-domain-id", workflowExecution: nil, mockSetup: func(mockHistoryService *history.MockClient) { mockHistoryService.EXPECT().RefreshWorkflowTasks( gomock.Any(), &types.HistoryRefreshWorkflowTasksRequest{ DomainUIID: "test-domain-id", Request: &types.RefreshWorkflowTasksRequest{ Execution: nil, }, }, ).Return(nil) }, wantErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) mockHistoryService := history.NewMockClient(mockCtrl) tc.mockSetup(mockHistoryService) engine := &matchingEngineImpl{ historyService: mockHistoryService, } err := engine.refreshWorkflowTasks(tc.ctx, tc.domainID, tc.workflowExecution) if tc.wantErr { require.Error(t, err) if tc.assertErr != nil { tc.assertErr(t, err) } } else { require.NoError(t, err) } }) } } func TestSetupExecutorWithEmptyConfig(t *testing.T) { // When no shard-distributor namespaces are configured, setupExecutor must // succeed and install a no-op executor so the matching service falls back // to local hash-ring assignment. registry := tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()) engine := &matchingEngineImpl{ taskListRegistry: registry, logger: log.NewNoop(), timeSource: clock.NewRealTimeSource(), ShardDistributorMatchingConfig: clientcommon.Config{}, // empty – no namespaces config: &config.Config{}, } // Must not panic or fatal; executor must be set afterward. engine.setupExecutor(nil) require.NotNil(t, engine.executor) require.NotNil(t, engine.taskListsFactory) // The no-op executor reports itself as not onboarded to SD. assert.False(t, engine.executor.IsOnboardedToSD()) } ================================================ FILE: service/matching/handler/handler.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package handler import ( "context" "sync" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" dynamicquotas "github.com/uber/cadence/common/dynamicconfig/quotas" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" ) type ( // handlerImpl is an implementation for matching service independent of wire protocol handlerImpl struct { engine Engine metricsClient metrics.Client startWG sync.WaitGroup userRateLimiter quotas.Policy workerRateLimiter quotas.Policy logger log.Logger throttledLogger log.Logger domainCache cache.DomainCache } ) var ( errMatchingHostThrottle = &types.ServiceBusyError{Message: "Matching host rps exceeded"} ) // NewHandler creates a thrift handler for the matching service func NewHandler( engine Engine, config *config.Config, domainCache cache.DomainCache, metricsClient metrics.Client, logger log.Logger, throttledLogger log.Logger, ) Handler { handler := &handlerImpl{ metricsClient: metricsClient, userRateLimiter: quotas.NewMultiStageRateLimiter( quotas.NewDynamicRateLimiter(config.UserRPS.AsFloat64()), quotas.NewCollection(dynamicquotas.NewFallbackDynamicRateLimiterFactory( config.DomainUserRPS, config.UserRPS, )), ), workerRateLimiter: quotas.NewMultiStageRateLimiter( quotas.NewDynamicRateLimiter(config.WorkerRPS.AsFloat64()), quotas.NewCollection(dynamicquotas.NewFallbackDynamicRateLimiterFactory( config.DomainWorkerRPS, config.WorkerRPS, )), ), engine: engine, logger: logger, throttledLogger: throttledLogger, domainCache: domainCache, } // prevent us from trying to serve requests before matching engine is started and ready handler.startWG.Add(1) return handler } // Start starts the handler func (h *handlerImpl) Start() { h.engine.Start() h.startWG.Done() } // Stop stops the handler func (h *handlerImpl) Stop() { h.engine.Stop() } // Health is for health check func (h *handlerImpl) Health(ctx context.Context) (*types.HealthStatus, error) { h.startWG.Wait() h.logger.Debug("Matching service health check endpoint reached.") hs := &types.HealthStatus{Ok: true, Msg: "matching good"} return hs, nil } func (h *handlerImpl) newHandlerContext( ctx context.Context, domainName string, taskList *types.TaskList, scope metrics.ScopeIdx, ) *handlerContext { return newHandlerContext( ctx, domainName, taskList, h.metricsClient, scope, h.logger, ) } // AddActivityTask - adds an activity task. func (h *handlerImpl) AddActivityTask( ctx context.Context, request *types.AddActivityTaskRequest, ) (resp *types.AddActivityTaskResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() domainName := h.domainName(request.GetDomainUUID()) hCtx := h.newHandlerContext( ctx, domainName, request.GetTaskList(), metrics.MatchingAddActivityTaskScope, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() if request.GetForwardedFrom() != "" { hCtx.scope.IncCounter(metrics.ForwardedPerTaskListCounter) } if ok := h.workerRateLimiter.Allow(quotas.Info{Domain: domainName}); !ok { return nil, hCtx.handleErr(errMatchingHostThrottle) } resp, err := h.engine.AddActivityTask(hCtx, request) return resp, hCtx.handleErr(err) } // AddDecisionTask - adds a decision task. func (h *handlerImpl) AddDecisionTask( ctx context.Context, request *types.AddDecisionTaskRequest, ) (resp *types.AddDecisionTaskResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() domainName := h.domainName(request.GetDomainUUID()) hCtx := h.newHandlerContext( ctx, domainName, request.GetTaskList(), metrics.MatchingAddDecisionTaskScope, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() if request.GetForwardedFrom() != "" { hCtx.scope.IncCounter(metrics.ForwardedPerTaskListCounter) } if ok := h.workerRateLimiter.Allow(quotas.Info{Domain: domainName}); !ok { return nil, hCtx.handleErr(errMatchingHostThrottle) } resp, err := h.engine.AddDecisionTask(hCtx, request) return resp, hCtx.handleErr(err) } // PollForActivityTask - long poll for an activity task. func (h *handlerImpl) PollForActivityTask( ctx context.Context, request *types.MatchingPollForActivityTaskRequest, ) (resp *types.MatchingPollForActivityTaskResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() domainName := h.domainName(request.GetDomainUUID()) hCtx := h.newHandlerContext( ctx, domainName, request.GetPollRequest().GetTaskList(), metrics.MatchingPollForActivityTaskScope, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() if request.GetForwardedFrom() != "" { hCtx.scope.IncCounter(metrics.ForwardedPerTaskListCounter) } if ok := h.workerRateLimiter.Allow(quotas.Info{Domain: domainName}); !ok { return nil, hCtx.handleErr(errMatchingHostThrottle) } if _, err := common.ValidateLongPollContextTimeoutIsSet(ctx, "PollForActivityTask", h.throttledLogger, ); err != nil { return nil, hCtx.handleErr(err) } response, err := h.engine.PollForActivityTask(hCtx, request) return response, hCtx.handleErr(err) } // PollForDecisionTask - long poll for a decision task. func (h *handlerImpl) PollForDecisionTask( ctx context.Context, request *types.MatchingPollForDecisionTaskRequest, ) (resp *types.MatchingPollForDecisionTaskResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() domainName := h.domainName(request.GetDomainUUID()) hCtx := h.newHandlerContext( ctx, domainName, request.GetPollRequest().GetTaskList(), metrics.MatchingPollForDecisionTaskScope, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() if request.GetForwardedFrom() != "" { hCtx.scope.IncCounter(metrics.ForwardedPerTaskListCounter) } if ok := h.workerRateLimiter.Allow(quotas.Info{Domain: domainName}); !ok { return nil, hCtx.handleErr(errMatchingHostThrottle) } if _, err := common.ValidateLongPollContextTimeoutIsSet( ctx, "PollForDecisionTask", h.throttledLogger, ); err != nil { return nil, hCtx.handleErr(err) } response, err := h.engine.PollForDecisionTask(hCtx, request) return response, hCtx.handleErr(err) } // QueryWorkflow queries a given workflow synchronously and return the query result. func (h *handlerImpl) QueryWorkflow( ctx context.Context, request *types.MatchingQueryWorkflowRequest, ) (resp *types.MatchingQueryWorkflowResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() domainName := h.domainName(request.GetDomainUUID()) hCtx := h.newHandlerContext( ctx, domainName, request.GetTaskList(), metrics.MatchingQueryWorkflowScope, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() if request.GetForwardedFrom() != "" { hCtx.scope.IncCounter(metrics.ForwardedPerTaskListCounter) } if ok := h.userRateLimiter.Allow(quotas.Info{Domain: domainName}); !ok { return nil, hCtx.handleErr(errMatchingHostThrottle) } response, err := h.engine.QueryWorkflow(hCtx, request) return response, hCtx.handleErr(err) } // RespondQueryTaskCompleted responds a query task completed func (h *handlerImpl) RespondQueryTaskCompleted( ctx context.Context, request *types.MatchingRespondQueryTaskCompletedRequest, ) (retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() domainName := h.domainName(request.GetDomainUUID()) hCtx := h.newHandlerContext( ctx, domainName, request.GetTaskList(), metrics.MatchingRespondQueryTaskCompletedScope, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() // Count the request in the RPS, but we still accept it even if RPS is exceeded h.workerRateLimiter.Allow(quotas.Info{Domain: domainName}) err := h.engine.RespondQueryTaskCompleted(hCtx, request) return hCtx.handleErr(err) } // CancelOutstandingPoll is used to cancel outstanding pollers func (h *handlerImpl) CancelOutstandingPoll(ctx context.Context, request *types.CancelOutstandingPollRequest) (retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() domainName := h.domainName(request.GetDomainUUID()) hCtx := h.newHandlerContext( ctx, domainName, request.GetTaskList(), metrics.MatchingCancelOutstandingPollScope, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() // Count the request in the RPS, but we still accept it even if RPS is exceeded h.workerRateLimiter.Allow(quotas.Info{Domain: domainName}) err := h.engine.CancelOutstandingPoll(hCtx, request) return hCtx.handleErr(err) } // DescribeTaskList returns information about the target tasklist, right now this API returns the // pollers which polled this tasklist in last few minutes. If includeTaskListStatus field is true, // it will also return status of tasklist's ackManager (readLevel, ackLevel, backlogCountHint and taskIDBlock). func (h *handlerImpl) DescribeTaskList( ctx context.Context, request *types.MatchingDescribeTaskListRequest, ) (resp *types.DescribeTaskListResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() domainName := h.domainName(request.GetDomainUUID()) hCtx := h.newHandlerContext( ctx, domainName, request.GetDescRequest().GetTaskList(), metrics.MatchingDescribeTaskListScope, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() if ok := h.userRateLimiter.Allow(quotas.Info{Domain: domainName}); !ok { return nil, hCtx.handleErr(errMatchingHostThrottle) } response, err := h.engine.DescribeTaskList(hCtx, request) return response, hCtx.handleErr(err) } // ListTaskListPartitions returns information about partitions for a taskList func (h *handlerImpl) ListTaskListPartitions( ctx context.Context, request *types.MatchingListTaskListPartitionsRequest, ) (resp *types.ListTaskListPartitionsResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() hCtx := newHandlerContext( ctx, request.GetDomain(), request.GetTaskList(), h.metricsClient, metrics.MatchingListTaskListPartitionsScope, h.logger, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() if ok := h.userRateLimiter.Allow(quotas.Info{Domain: request.GetDomain()}); !ok { return nil, hCtx.handleErr(errMatchingHostThrottle) } response, err := h.engine.ListTaskListPartitions(hCtx, request) return response, hCtx.handleErr(err) } // GetTaskListsByDomain returns information about partitions for a taskList func (h *handlerImpl) GetTaskListsByDomain( ctx context.Context, request *types.GetTaskListsByDomainRequest, ) (resp *types.GetTaskListsByDomainResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() hCtx := newHandlerContext( ctx, request.GetDomain(), nil, h.metricsClient, metrics.MatchingGetTaskListsByDomainScope, h.logger, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() if ok := h.userRateLimiter.Allow(quotas.Info{Domain: request.GetDomain()}); !ok { return nil, hCtx.handleErr(errMatchingHostThrottle) } response, err := h.engine.GetTaskListsByDomain(hCtx, request) return response, hCtx.handleErr(err) } func (h *handlerImpl) UpdateTaskListPartitionConfig( ctx context.Context, request *types.MatchingUpdateTaskListPartitionConfigRequest, ) (resp *types.MatchingUpdateTaskListPartitionConfigResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() domainName := h.domainName(request.DomainUUID) hCtx := h.newHandlerContext( ctx, domainName, request.TaskList, metrics.MatchingUpdateTaskListPartitionConfigScope, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() if ok := h.userRateLimiter.Allow(quotas.Info{Domain: domainName}); !ok { return nil, hCtx.handleErr(errMatchingHostThrottle) } response, err := h.engine.UpdateTaskListPartitionConfig(hCtx, request) return response, hCtx.handleErr(err) } func (h *handlerImpl) RefreshTaskListPartitionConfig( ctx context.Context, request *types.MatchingRefreshTaskListPartitionConfigRequest, ) (resp *types.MatchingRefreshTaskListPartitionConfigResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() domainName := h.domainName(request.DomainUUID) hCtx := h.newHandlerContext( ctx, domainName, request.TaskList, metrics.MatchingRefreshTaskListPartitionConfigScope, ) sw := hCtx.startProfiling(&h.startWG) defer sw.Stop() // Count the request in the RPS, but we still accept it even if RPS is exceeded h.userRateLimiter.Allow(quotas.Info{Domain: domainName}) response, err := h.engine.RefreshTaskListPartitionConfig(hCtx, request) return response, hCtx.handleErr(err) } func (h *handlerImpl) domainName(id string) string { domainName, err := h.domainCache.GetDomainName(id) if err != nil { return "" } return domainName } ================================================ FILE: service/matching/handler/handler_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package handler import ( "context" "errors" "sync" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" commonConfig "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" dynamicquotas "github.com/uber/cadence/common/dynamicconfig/quotas" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" ) const ( testDomain = "test-domain" ) type ( handlerSuite struct { suite.Suite *require.Assertions controller *gomock.Controller mockResource *resource.Test mockEngine *MockEngine mockDomainCache *cache.MockDomainCache mockLimiter *quotas.MockLimiter handler *handlerImpl testDomain string } ) func TestHandlerSuite(t *testing.T) { s := new(handlerSuite) suite.Run(t, s) } func (s *handlerSuite) SetupSuite() {} func (s *handlerSuite) TearDownSuite() {} func (s *handlerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.Matching) s.mockEngine = NewMockEngine(s.controller) s.mockDomainCache = cache.NewMockDomainCache(s.controller) s.mockLimiter = quotas.NewMockLimiter(s.controller) // Create a handler with a mock limiter s.handler = &handlerImpl{ engine: s.mockEngine, metricsClient: s.mockResource.MetricsClient, startWG: sync.WaitGroup{}, userRateLimiter: quotas.NewMultiStageRateLimiter( s.mockLimiter, quotas.NewCollection(dynamicquotas.NewSimpleDynamicRateLimiterFactory(func(domain string) int { return 10 })), ), workerRateLimiter: quotas.NewMultiStageRateLimiter( s.mockLimiter, quotas.NewCollection(dynamicquotas.NewSimpleDynamicRateLimiterFactory(func(domain string) int { return 10 })), ), logger: s.mockResource.GetLogger(), throttledLogger: s.mockResource.GetThrottledLogger(), domainCache: s.mockDomainCache, } s.testDomain = testDomain } func (s *handlerSuite) TearDownTest() { s.controller.Finish() s.mockResource.Finish(s.T()) } func (s *handlerSuite) getHandler(config *config.Config) Handler { return NewHandler(s.mockEngine, config, s.mockDomainCache, s.mockResource.MetricsClient, s.mockResource.GetLogger(), s.mockResource.GetThrottledLogger()) } func (s *handlerSuite) TestNewHandler() { cfg := config.NewConfig(dynamicconfig.NewCollection(dynamicconfig.NewInMemoryClient(), s.mockResource.Logger), "matching-test", commonConfig.RPC{}, getIsolationGroupsHelper) handler := s.getHandler(cfg) s.NotNil(handler) } func (s *handlerSuite) TestStart() { defer goleak.VerifyNone(s.T()) cfg := config.NewConfig(dynamicconfig.NewCollection(dynamicconfig.NewInMemoryClient(), s.mockResource.Logger), "matching-test", commonConfig.RPC{}, getIsolationGroupsHelper) handler := s.getHandler(cfg) s.mockEngine.EXPECT().Start().Times(1) handler.Start() } func (s *handlerSuite) TestStop() { defer goleak.VerifyNone(s.T()) cfg := config.NewConfig(dynamicconfig.NewCollection(dynamicconfig.NewInMemoryClient(), s.mockResource.Logger), "matching-test", commonConfig.RPC{}, getIsolationGroupsHelper) handler := s.getHandler(cfg) s.mockEngine.EXPECT().Start().Times(1) s.mockEngine.EXPECT().Stop().Times(1) handler.Start() handler.Stop() } func (s *handlerSuite) TestHealth() { resp, err := s.handler.Health(context.Background()) s.NoError(err) s.Equal(&types.HealthStatus{Ok: true, Msg: "matching good"}, resp) } func (s *handlerSuite) TestNewHandlerContext() { handlerCtx := s.handler.newHandlerContext(context.Background(), testDomain, &types.TaskList{Name: "test-task-list"}, 0) s.NotNil(handlerCtx) s.IsType(&handlerContext{}, handlerCtx) } func (s *handlerSuite) TestAddActivityTask() { request := types.AddActivityTaskRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{Name: "test-task-list"}, ForwardedFrom: "forwarded-from", } testCases := []struct { name string setupMocks func() want *types.AddActivityTaskResponse err error }{ { name: "Success case", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().AddActivityTask(gomock.Any(), &request).Return(&types.AddActivityTaskResponse{ PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(2), WritePartitions: partitions(2), }, }, nil).Times(1) }, want: &types.AddActivityTaskResponse{ PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(2), WritePartitions: partitions(2), }, }, }, { name: "Error case - rate limiter not allowed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(false).Times(1) }, err: &types.ServiceBusyError{Message: "Matching host rps exceeded"}, }, { name: "Error case - AddActivityTask failed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) // Ensure Allow() returns true s.mockEngine.EXPECT().AddActivityTask(gomock.Any(), &request).Return(nil, errors.New("add-activity-error")).Times(1) }, err: &types.InternalServiceError{Message: "add-activity-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) resp, err := s.handler.AddActivityTask(context.Background(), &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.Equal(tc.want, resp) s.NoError(err) } }) } } func (s *handlerSuite) TestAddDecisionTask() { request := types.AddDecisionTaskRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{Name: "test-task-list"}, ForwardedFrom: "forwarded-from", } testCases := []struct { name string setupMocks func() want *types.AddDecisionTaskResponse err error }{ { name: "Success case", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().AddDecisionTask(gomock.Any(), &request).Return(&types.AddDecisionTaskResponse{ PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(2), WritePartitions: partitions(2), }, }, nil).Times(1) }, want: &types.AddDecisionTaskResponse{ PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(2), WritePartitions: partitions(2), }, }, }, { name: "Error case - rate limiter not allowed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(false).Times(1) }, err: &types.ServiceBusyError{Message: "Matching host rps exceeded"}, }, { name: "Error case - AddDecisionTask failed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) // Ensure Allow() returns true s.mockEngine.EXPECT().AddDecisionTask(gomock.Any(), &request).Return(nil, errors.New("add-decision-error")).Times(1) }, err: &types.InternalServiceError{Message: "add-decision-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) resp, err := s.handler.AddDecisionTask(context.Background(), &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.Equal(tc.want, resp) s.NoError(err) } }) } } func (s *handlerSuite) TestPollForActivityTask() { request := types.MatchingPollForActivityTaskRequest{ DomainUUID: "test-domain-id", ForwardedFrom: "forwarded-from", } testCases := []struct { name string setupMocks func() getCtx func() (context.Context, context.CancelFunc) err error }{ { name: "Success case", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().PollForActivityTask(gomock.Any(), &request). Return(&types.MatchingPollForActivityTaskResponse{TaskToken: []byte("task-token")}, nil).Times(1) }, getCtx: func() (context.Context, context.CancelFunc) { ctx, cancel := context.WithDeadline(context.Background(), time.Now()) return ctx, cancel }, }, { name: "Error case - rate limiter not allowed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(false).Times(1) }, getCtx: func() (context.Context, context.CancelFunc) { return context.Background(), nil }, err: &types.ServiceBusyError{Message: "Matching host rps exceeded"}, }, { name: "Error case - LongPollContextTimeout not set", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) }, getCtx: func() (context.Context, context.CancelFunc) { return context.Background(), nil }, err: common.ErrContextTimeoutNotSet, }, { name: "Error case - PollForActivityTask failed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().PollForActivityTask(gomock.Any(), &request).Return(nil, errors.New("poll-activity-error")).Times(1) }, getCtx: func() (context.Context, context.CancelFunc) { ctx, cancel := context.WithDeadline(context.Background(), time.Now()) return ctx, cancel }, err: &types.InternalServiceError{Message: "poll-activity-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) ctx, cancel := tc.getCtx() if cancel != nil { defer cancel() } resp, err := s.handler.PollForActivityTask(ctx, &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) s.Equal(&types.MatchingPollForActivityTaskResponse{TaskToken: []byte("task-token")}, resp) } }) } } func (s *handlerSuite) TestPollForDecisionTask() { request := types.MatchingPollForDecisionTaskRequest{ DomainUUID: "test-domain-id", ForwardedFrom: "forwarded-from", } testCases := []struct { name string setupMocks func() getCtx func() (context.Context, context.CancelFunc) err error }{ { name: "Success case", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().PollForDecisionTask(gomock.Any(), &request). Return(&types.MatchingPollForDecisionTaskResponse{TaskToken: []byte("task-token")}, nil).Times(1) }, getCtx: func() (context.Context, context.CancelFunc) { ctx, cancel := context.WithDeadline(context.Background(), time.Now()) return ctx, cancel }, }, { name: "Error case - rate limiter not allowed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(false).Times(1) }, getCtx: func() (context.Context, context.CancelFunc) { return context.Background(), nil }, err: &types.ServiceBusyError{Message: "Matching host rps exceeded"}, }, { name: "Error case - LongPollContextTimeout not set", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) }, getCtx: func() (context.Context, context.CancelFunc) { return context.Background(), nil }, err: common.ErrContextTimeoutNotSet, }, { name: "Error case - PollForDecisionTask failed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().PollForDecisionTask(gomock.Any(), &request).Return(nil, errors.New("poll-decision-error")).Times(1) }, getCtx: func() (context.Context, context.CancelFunc) { ctx, cancel := context.WithDeadline(context.Background(), time.Now()) return ctx, cancel }, err: &types.InternalServiceError{Message: "poll-decision-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) ctx, cancel := tc.getCtx() if cancel != nil { defer cancel() } resp, err := s.handler.PollForDecisionTask(ctx, &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) s.Equal(&types.MatchingPollForDecisionTaskResponse{TaskToken: []byte("task-token")}, resp) } }) } } func (s *handlerSuite) TestQueryWorkflow() { request := types.MatchingQueryWorkflowRequest{ DomainUUID: "test-domain-id", ForwardedFrom: "forwarded-from", } testCases := []struct { name string setupMocks func() err error }{ { name: "Success case", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().QueryWorkflow(gomock.Any(), &request). Return(&types.MatchingQueryWorkflowResponse{QueryResult: []byte("query-result")}, nil).Times(1) }, }, { name: "Error case - rate limiter not allowed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(false).Times(1) }, err: &types.ServiceBusyError{Message: "Matching host rps exceeded"}, }, { name: "Error case - QueryWorkflow failed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().QueryWorkflow(gomock.Any(), &request).Return(nil, errors.New("query-error")).Times(1) }, err: &types.InternalServiceError{Message: "query-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) resp, err := s.handler.QueryWorkflow(context.Background(), &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) s.Equal(&types.MatchingQueryWorkflowResponse{QueryResult: []byte("query-result")}, resp) } }) } } func (s *handlerSuite) TestRespondQueryTaskCompleted() { request := types.MatchingRespondQueryTaskCompletedRequest{ DomainUUID: "test-domain-id", } testCases := []struct { name string setupMocks func() err error }{ { name: "Success case", setupMocks: func() { s.mockEngine.EXPECT().RespondQueryTaskCompleted(gomock.Any(), &request).Return(nil).Times(1) }, }, { name: "Error case - RespondQueryTaskCompleted failed", setupMocks: func() { s.mockEngine.EXPECT().RespondQueryTaskCompleted(gomock.Any(), &request).Return(errors.New("respond-query-error")).Times(1) }, err: &types.InternalServiceError{Message: "respond-query-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) s.mockLimiter.EXPECT().Allow().Return(true).Times(1) err := s.handler.RespondQueryTaskCompleted(context.Background(), &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestCancelOutstandingPoll() { request := types.CancelOutstandingPollRequest{ DomainUUID: "test-domain-id", } testCases := []struct { name string setupMocks func() err error }{ { name: "Success case", setupMocks: func() { s.mockEngine.EXPECT().CancelOutstandingPoll(gomock.Any(), &request).Return(nil).Times(1) }, }, { name: "Error case - CancelOutstandingPoll failed", setupMocks: func() { s.mockEngine.EXPECT().CancelOutstandingPoll(gomock.Any(), &request).Return(errors.New("cancel-poll-error")).Times(1) }, err: &types.InternalServiceError{Message: "cancel-poll-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) s.mockLimiter.EXPECT().Allow().Return(true).Times(1) err := s.handler.CancelOutstandingPoll(context.Background(), &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *handlerSuite) TestDescribeTaskList() { request := types.MatchingDescribeTaskListRequest{ DomainUUID: "test-domain-id", DescRequest: &types.DescribeTaskListRequest{ TaskList: &types.TaskList{Name: "test-task-list"}, }, } testCases := []struct { name string setupMocks func() err error }{ { name: "Success case", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().DescribeTaskList(gomock.Any(), &request). Return(&types.DescribeTaskListResponse{Pollers: []*types.PollerInfo{{}}}, nil).Times(1) }, }, { name: "Error case - rate limiter not allowed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(false).Times(1) }, err: &types.ServiceBusyError{Message: "Matching host rps exceeded"}, }, { name: "Error case - DescribeTaskList failed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().DescribeTaskList(gomock.Any(), &request). Return(nil, errors.New("describe-tasklist-error")).Times(1) }, err: &types.InternalServiceError{Message: "describe-tasklist-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) resp, err := s.handler.DescribeTaskList(context.Background(), &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) s.Equal(&types.DescribeTaskListResponse{Pollers: []*types.PollerInfo{{}}}, resp) } }) } } func (s *handlerSuite) TestListTaskListPartitions() { request := types.MatchingListTaskListPartitionsRequest{ TaskList: &types.TaskList{Name: "test-task-list"}, Domain: s.testDomain, } testCases := []struct { name string setupMocks func() err error }{ { name: "Success case", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().ListTaskListPartitions(gomock.Any(), &request). Return(&types.ListTaskListPartitionsResponse{ActivityTaskListPartitions: []*types.TaskListPartitionMetadata{ {Key: "test-key", OwnerHostName: "test-host"}}}, nil).Times(1) }, }, { name: "Error case - rate limiter not allowed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(false).Times(1) }, err: &types.ServiceBusyError{Message: "Matching host rps exceeded"}, }, { name: "Error case - ListTaskListPartitions failed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().ListTaskListPartitions(gomock.Any(), &request). Return(nil, errors.New("list-tasklist-partitions-error")).Times(1) }, err: &types.InternalServiceError{Message: "list-tasklist-partitions-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() resp, err := s.handler.ListTaskListPartitions(context.Background(), &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) s.Equal(&types.ListTaskListPartitionsResponse{ActivityTaskListPartitions: []*types.TaskListPartitionMetadata{ {Key: "test-key", OwnerHostName: "test-host"}}}, resp) } }) } } func (s *handlerSuite) TestGetTaskListsByDomain() { request := types.GetTaskListsByDomainRequest{ Domain: s.testDomain, } testCases := []struct { name string setupMocks func() err error }{ { name: "Success case", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().GetTaskListsByDomain(gomock.Any(), &request). Return(&types.GetTaskListsByDomainResponse{ DecisionTaskListMap: map[string]*types.DescribeTaskListResponse{"test-decision-task-list": {}}, ActivityTaskListMap: map[string]*types.DescribeTaskListResponse{"test-activity-task-list": {}}, }, nil).Times(1) }, }, { name: "Error case - rate limiter not allowed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(false).Times(1) }, err: &types.ServiceBusyError{Message: "Matching host rps exceeded"}, }, { name: "Error case - GetTaskListsByDomain failed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().GetTaskListsByDomain(gomock.Any(), &request). Return(nil, errors.New("get-tasklists-error")).Times(1) }, err: &types.InternalServiceError{Message: "get-tasklists-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() resp, err := s.handler.GetTaskListsByDomain(context.Background(), &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) s.Equal(&types.GetTaskListsByDomainResponse{ DecisionTaskListMap: map[string]*types.DescribeTaskListResponse{"test-decision-task-list": {}}, ActivityTaskListMap: map[string]*types.DescribeTaskListResponse{"test-activity-task-list": {}}, }, resp) } }) } } func (s *handlerSuite) TestDomainName() { testCases := []struct { name string setupMocks func() resp string }{ { name: "Success case", setupMocks: func() { s.mockDomainCache.EXPECT().GetDomainName(s.testDomain).Return(s.testDomain, nil).Times(1) }, resp: s.testDomain, }, { name: "Error case - GetDomainName failed", setupMocks: func() { s.mockDomainCache.EXPECT().GetDomainName(s.testDomain).Return("", errors.New("get-domain-error")).Times(1) }, resp: "", }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() resp := s.handler.domainName(s.testDomain) s.Equal(tc.resp, resp) }) } } func (s *handlerSuite) TestRefreshTaskListPartitionConfig() { request := types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{Name: "test-task-list"}, } testCases := []struct { name string setupMocks func() want *types.MatchingRefreshTaskListPartitionConfigResponse err error }{ { name: "Success case", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &request). Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil).Times(1) }, want: &types.MatchingRefreshTaskListPartitionConfigResponse{}, }, { name: "Error case - RefreshTaskListPartitionConfig failed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &request). Return(nil, errors.New("refresh-tasklist-error")).Times(1) }, err: &types.InternalServiceError{Message: "refresh-tasklist-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) resp, err := s.handler.RefreshTaskListPartitionConfig(context.Background(), &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) s.Equal(tc.want, resp) } }) } } func (s *handlerSuite) TestUpdateTaskListPartitionConfig() { request := types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: "test-domain-id", TaskList: &types.TaskList{Name: "test-task-list"}, } testCases := []struct { name string setupMocks func() want *types.MatchingUpdateTaskListPartitionConfigResponse err error }{ { name: "Success case", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &request). Return(&types.MatchingUpdateTaskListPartitionConfigResponse{}, nil).Times(1) }, want: &types.MatchingUpdateTaskListPartitionConfigResponse{}, }, { name: "Error case - rate limiter not allowed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(false).Times(1) }, err: &types.ServiceBusyError{Message: "Matching host rps exceeded"}, }, { name: "Error case - UpdateTaskListPartitionConfig failed", setupMocks: func() { s.mockLimiter.EXPECT().Allow().Return(true).Times(1) s.mockEngine.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &request). Return(nil, errors.New("update-tasklist-error")).Times(1) }, err: &types.InternalServiceError{Message: "update-tasklist-error"}, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks() s.mockDomainCache.EXPECT().GetDomainName(request.DomainUUID).Return(s.testDomain, nil).Times(1) resp, err := s.handler.UpdateTaskListPartitionConfig(context.Background(), &request) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) s.Equal(tc.want, resp) } }) } } func partitions(num int) map[int]*types.TaskListPartition { result := make(map[int]*types.TaskListPartition, num) for i := 0; i < num; i++ { result[i] = &types.TaskListPartition{} } return result } ================================================ FILE: service/matching/handler/interfaces.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interfaces_mock.go -package handler github.com/uber/cadence/service/matching/handler Handler //go:generate gowrap gen -g -p . -i Handler -t ../../templates/grpc.tmpl -o ../wrappers/grpc/grpc_handler_generated.go -v handler=GRPC -v package=matchingv1 -v path=github.com/uber/cadence/.gen/proto/matching/v1 -v prefix=Matching //go:generate gowrap gen -g -p ../../../.gen/go/matching/matchingserviceserver -i Interface -t ../../templates/thrift.tmpl -o ../wrappers/thrift/thrift_handler_generated.go -v handler=Thrift -v prefix=Matching package handler import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) type ( // Engine exposes interfaces for clients to poll for activity and decision tasks. Engine interface { common.Daemon AddDecisionTask(hCtx *handlerContext, request *types.AddDecisionTaskRequest) (*types.AddDecisionTaskResponse, error) AddActivityTask(hCtx *handlerContext, request *types.AddActivityTaskRequest) (*types.AddActivityTaskResponse, error) PollForDecisionTask(hCtx *handlerContext, request *types.MatchingPollForDecisionTaskRequest) (*types.MatchingPollForDecisionTaskResponse, error) PollForActivityTask(hCtx *handlerContext, request *types.MatchingPollForActivityTaskRequest) (*types.MatchingPollForActivityTaskResponse, error) QueryWorkflow(hCtx *handlerContext, request *types.MatchingQueryWorkflowRequest) (*types.MatchingQueryWorkflowResponse, error) RespondQueryTaskCompleted(hCtx *handlerContext, request *types.MatchingRespondQueryTaskCompletedRequest) error CancelOutstandingPoll(hCtx *handlerContext, request *types.CancelOutstandingPollRequest) error DescribeTaskList(hCtx *handlerContext, request *types.MatchingDescribeTaskListRequest) (*types.DescribeTaskListResponse, error) ListTaskListPartitions(hCtx *handlerContext, request *types.MatchingListTaskListPartitionsRequest) (*types.ListTaskListPartitionsResponse, error) GetTaskListsByDomain(hCtx *handlerContext, request *types.GetTaskListsByDomainRequest) (*types.GetTaskListsByDomainResponse, error) UpdateTaskListPartitionConfig(hCtx *handlerContext, request *types.MatchingUpdateTaskListPartitionConfigRequest) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) RefreshTaskListPartitionConfig(hCtx *handlerContext, request *types.MatchingRefreshTaskListPartitionConfigRequest) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) } // Handler interface for matching service Handler interface { common.Daemon Health(context.Context) (*types.HealthStatus, error) AddActivityTask(context.Context, *types.AddActivityTaskRequest) (*types.AddActivityTaskResponse, error) AddDecisionTask(context.Context, *types.AddDecisionTaskRequest) (*types.AddDecisionTaskResponse, error) CancelOutstandingPoll(context.Context, *types.CancelOutstandingPollRequest) error DescribeTaskList(context.Context, *types.MatchingDescribeTaskListRequest) (*types.DescribeTaskListResponse, error) ListTaskListPartitions(context.Context, *types.MatchingListTaskListPartitionsRequest) (*types.ListTaskListPartitionsResponse, error) GetTaskListsByDomain(context.Context, *types.GetTaskListsByDomainRequest) (*types.GetTaskListsByDomainResponse, error) PollForActivityTask(context.Context, *types.MatchingPollForActivityTaskRequest) (*types.MatchingPollForActivityTaskResponse, error) PollForDecisionTask(context.Context, *types.MatchingPollForDecisionTaskRequest) (*types.MatchingPollForDecisionTaskResponse, error) QueryWorkflow(context.Context, *types.MatchingQueryWorkflowRequest) (*types.MatchingQueryWorkflowResponse, error) RespondQueryTaskCompleted(context.Context, *types.MatchingRespondQueryTaskCompletedRequest) error UpdateTaskListPartitionConfig(context.Context, *types.MatchingUpdateTaskListPartitionConfigRequest) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) RefreshTaskListPartitionConfig(context.Context, *types.MatchingRefreshTaskListPartitionConfigRequest) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) } ) ================================================ FILE: service/matching/handler/interfaces_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go // // Generated by this command: // // mockgen -package handler -source interfaces.go -destination interfaces_mock.go -package handler github.com/uber/cadence/service/matching/handler Handler // // Package handler is a generated GoMock package. package handler import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockEngine is a mock of Engine interface. type MockEngine struct { ctrl *gomock.Controller recorder *MockEngineMockRecorder isgomock struct{} } // MockEngineMockRecorder is the mock recorder for MockEngine. type MockEngineMockRecorder struct { mock *MockEngine } // NewMockEngine creates a new mock instance. func NewMockEngine(ctrl *gomock.Controller) *MockEngine { mock := &MockEngine{ctrl: ctrl} mock.recorder = &MockEngineMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockEngine) EXPECT() *MockEngineMockRecorder { return m.recorder } // AddActivityTask mocks base method. func (m *MockEngine) AddActivityTask(hCtx *handlerContext, request *types.AddActivityTaskRequest) (*types.AddActivityTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddActivityTask", hCtx, request) ret0, _ := ret[0].(*types.AddActivityTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AddActivityTask indicates an expected call of AddActivityTask. func (mr *MockEngineMockRecorder) AddActivityTask(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActivityTask", reflect.TypeOf((*MockEngine)(nil).AddActivityTask), hCtx, request) } // AddDecisionTask mocks base method. func (m *MockEngine) AddDecisionTask(hCtx *handlerContext, request *types.AddDecisionTaskRequest) (*types.AddDecisionTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTask", hCtx, request) ret0, _ := ret[0].(*types.AddDecisionTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTask indicates an expected call of AddDecisionTask. func (mr *MockEngineMockRecorder) AddDecisionTask(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTask", reflect.TypeOf((*MockEngine)(nil).AddDecisionTask), hCtx, request) } // CancelOutstandingPoll mocks base method. func (m *MockEngine) CancelOutstandingPoll(hCtx *handlerContext, request *types.CancelOutstandingPollRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CancelOutstandingPoll", hCtx, request) ret0, _ := ret[0].(error) return ret0 } // CancelOutstandingPoll indicates an expected call of CancelOutstandingPoll. func (mr *MockEngineMockRecorder) CancelOutstandingPoll(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelOutstandingPoll", reflect.TypeOf((*MockEngine)(nil).CancelOutstandingPoll), hCtx, request) } // DescribeTaskList mocks base method. func (m *MockEngine) DescribeTaskList(hCtx *handlerContext, request *types.MatchingDescribeTaskListRequest) (*types.DescribeTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeTaskList", hCtx, request) ret0, _ := ret[0].(*types.DescribeTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeTaskList indicates an expected call of DescribeTaskList. func (mr *MockEngineMockRecorder) DescribeTaskList(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTaskList", reflect.TypeOf((*MockEngine)(nil).DescribeTaskList), hCtx, request) } // GetTaskListsByDomain mocks base method. func (m *MockEngine) GetTaskListsByDomain(hCtx *handlerContext, request *types.GetTaskListsByDomainRequest) (*types.GetTaskListsByDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskListsByDomain", hCtx, request) ret0, _ := ret[0].(*types.GetTaskListsByDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTaskListsByDomain indicates an expected call of GetTaskListsByDomain. func (mr *MockEngineMockRecorder) GetTaskListsByDomain(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskListsByDomain", reflect.TypeOf((*MockEngine)(nil).GetTaskListsByDomain), hCtx, request) } // ListTaskListPartitions mocks base method. func (m *MockEngine) ListTaskListPartitions(hCtx *handlerContext, request *types.MatchingListTaskListPartitionsRequest) (*types.ListTaskListPartitionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTaskListPartitions", hCtx, request) ret0, _ := ret[0].(*types.ListTaskListPartitionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTaskListPartitions indicates an expected call of ListTaskListPartitions. func (mr *MockEngineMockRecorder) ListTaskListPartitions(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTaskListPartitions", reflect.TypeOf((*MockEngine)(nil).ListTaskListPartitions), hCtx, request) } // PollForActivityTask mocks base method. func (m *MockEngine) PollForActivityTask(hCtx *handlerContext, request *types.MatchingPollForActivityTaskRequest) (*types.MatchingPollForActivityTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PollForActivityTask", hCtx, request) ret0, _ := ret[0].(*types.MatchingPollForActivityTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForActivityTask indicates an expected call of PollForActivityTask. func (mr *MockEngineMockRecorder) PollForActivityTask(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForActivityTask", reflect.TypeOf((*MockEngine)(nil).PollForActivityTask), hCtx, request) } // PollForDecisionTask mocks base method. func (m *MockEngine) PollForDecisionTask(hCtx *handlerContext, request *types.MatchingPollForDecisionTaskRequest) (*types.MatchingPollForDecisionTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PollForDecisionTask", hCtx, request) ret0, _ := ret[0].(*types.MatchingPollForDecisionTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForDecisionTask indicates an expected call of PollForDecisionTask. func (mr *MockEngineMockRecorder) PollForDecisionTask(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForDecisionTask", reflect.TypeOf((*MockEngine)(nil).PollForDecisionTask), hCtx, request) } // QueryWorkflow mocks base method. func (m *MockEngine) QueryWorkflow(hCtx *handlerContext, request *types.MatchingQueryWorkflowRequest) (*types.MatchingQueryWorkflowResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryWorkflow", hCtx, request) ret0, _ := ret[0].(*types.MatchingQueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // QueryWorkflow indicates an expected call of QueryWorkflow. func (mr *MockEngineMockRecorder) QueryWorkflow(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockEngine)(nil).QueryWorkflow), hCtx, request) } // RefreshTaskListPartitionConfig mocks base method. func (m *MockEngine) RefreshTaskListPartitionConfig(hCtx *handlerContext, request *types.MatchingRefreshTaskListPartitionConfigRequest) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RefreshTaskListPartitionConfig", hCtx, request) ret0, _ := ret[0].(*types.MatchingRefreshTaskListPartitionConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RefreshTaskListPartitionConfig indicates an expected call of RefreshTaskListPartitionConfig. func (mr *MockEngineMockRecorder) RefreshTaskListPartitionConfig(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTaskListPartitionConfig", reflect.TypeOf((*MockEngine)(nil).RefreshTaskListPartitionConfig), hCtx, request) } // RespondQueryTaskCompleted mocks base method. func (m *MockEngine) RespondQueryTaskCompleted(hCtx *handlerContext, request *types.MatchingRespondQueryTaskCompletedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondQueryTaskCompleted", hCtx, request) ret0, _ := ret[0].(error) return ret0 } // RespondQueryTaskCompleted indicates an expected call of RespondQueryTaskCompleted. func (mr *MockEngineMockRecorder) RespondQueryTaskCompleted(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondQueryTaskCompleted", reflect.TypeOf((*MockEngine)(nil).RespondQueryTaskCompleted), hCtx, request) } // Start mocks base method. func (m *MockEngine) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockEngineMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockEngine)(nil).Start)) } // Stop mocks base method. func (m *MockEngine) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockEngineMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockEngine)(nil).Stop)) } // UpdateTaskListPartitionConfig mocks base method. func (m *MockEngine) UpdateTaskListPartitionConfig(hCtx *handlerContext, request *types.MatchingUpdateTaskListPartitionConfigRequest) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskListPartitionConfig", hCtx, request) ret0, _ := ret[0].(*types.MatchingUpdateTaskListPartitionConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskListPartitionConfig indicates an expected call of UpdateTaskListPartitionConfig. func (mr *MockEngineMockRecorder) UpdateTaskListPartitionConfig(hCtx, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListPartitionConfig", reflect.TypeOf((*MockEngine)(nil).UpdateTaskListPartitionConfig), hCtx, request) } // MockHandler is a mock of Handler interface. type MockHandler struct { ctrl *gomock.Controller recorder *MockHandlerMockRecorder isgomock struct{} } // MockHandlerMockRecorder is the mock recorder for MockHandler. type MockHandlerMockRecorder struct { mock *MockHandler } // NewMockHandler creates a new mock instance. func NewMockHandler(ctrl *gomock.Controller) *MockHandler { mock := &MockHandler{ctrl: ctrl} mock.recorder = &MockHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHandler) EXPECT() *MockHandlerMockRecorder { return m.recorder } // AddActivityTask mocks base method. func (m *MockHandler) AddActivityTask(arg0 context.Context, arg1 *types.AddActivityTaskRequest) (*types.AddActivityTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddActivityTask", arg0, arg1) ret0, _ := ret[0].(*types.AddActivityTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AddActivityTask indicates an expected call of AddActivityTask. func (mr *MockHandlerMockRecorder) AddActivityTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActivityTask", reflect.TypeOf((*MockHandler)(nil).AddActivityTask), arg0, arg1) } // AddDecisionTask mocks base method. func (m *MockHandler) AddDecisionTask(arg0 context.Context, arg1 *types.AddDecisionTaskRequest) (*types.AddDecisionTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddDecisionTask", arg0, arg1) ret0, _ := ret[0].(*types.AddDecisionTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AddDecisionTask indicates an expected call of AddDecisionTask. func (mr *MockHandlerMockRecorder) AddDecisionTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDecisionTask", reflect.TypeOf((*MockHandler)(nil).AddDecisionTask), arg0, arg1) } // CancelOutstandingPoll mocks base method. func (m *MockHandler) CancelOutstandingPoll(arg0 context.Context, arg1 *types.CancelOutstandingPollRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CancelOutstandingPoll", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // CancelOutstandingPoll indicates an expected call of CancelOutstandingPoll. func (mr *MockHandlerMockRecorder) CancelOutstandingPoll(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelOutstandingPoll", reflect.TypeOf((*MockHandler)(nil).CancelOutstandingPoll), arg0, arg1) } // DescribeTaskList mocks base method. func (m *MockHandler) DescribeTaskList(arg0 context.Context, arg1 *types.MatchingDescribeTaskListRequest) (*types.DescribeTaskListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeTaskList", arg0, arg1) ret0, _ := ret[0].(*types.DescribeTaskListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeTaskList indicates an expected call of DescribeTaskList. func (mr *MockHandlerMockRecorder) DescribeTaskList(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTaskList", reflect.TypeOf((*MockHandler)(nil).DescribeTaskList), arg0, arg1) } // GetTaskListsByDomain mocks base method. func (m *MockHandler) GetTaskListsByDomain(arg0 context.Context, arg1 *types.GetTaskListsByDomainRequest) (*types.GetTaskListsByDomainResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskListsByDomain", arg0, arg1) ret0, _ := ret[0].(*types.GetTaskListsByDomainResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTaskListsByDomain indicates an expected call of GetTaskListsByDomain. func (mr *MockHandlerMockRecorder) GetTaskListsByDomain(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskListsByDomain", reflect.TypeOf((*MockHandler)(nil).GetTaskListsByDomain), arg0, arg1) } // Health mocks base method. func (m *MockHandler) Health(arg0 context.Context) (*types.HealthStatus, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Health", arg0) ret0, _ := ret[0].(*types.HealthStatus) ret1, _ := ret[1].(error) return ret0, ret1 } // Health indicates an expected call of Health. func (mr *MockHandlerMockRecorder) Health(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockHandler)(nil).Health), arg0) } // ListTaskListPartitions mocks base method. func (m *MockHandler) ListTaskListPartitions(arg0 context.Context, arg1 *types.MatchingListTaskListPartitionsRequest) (*types.ListTaskListPartitionsResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTaskListPartitions", arg0, arg1) ret0, _ := ret[0].(*types.ListTaskListPartitionsResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTaskListPartitions indicates an expected call of ListTaskListPartitions. func (mr *MockHandlerMockRecorder) ListTaskListPartitions(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTaskListPartitions", reflect.TypeOf((*MockHandler)(nil).ListTaskListPartitions), arg0, arg1) } // PollForActivityTask mocks base method. func (m *MockHandler) PollForActivityTask(arg0 context.Context, arg1 *types.MatchingPollForActivityTaskRequest) (*types.MatchingPollForActivityTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PollForActivityTask", arg0, arg1) ret0, _ := ret[0].(*types.MatchingPollForActivityTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForActivityTask indicates an expected call of PollForActivityTask. func (mr *MockHandlerMockRecorder) PollForActivityTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForActivityTask", reflect.TypeOf((*MockHandler)(nil).PollForActivityTask), arg0, arg1) } // PollForDecisionTask mocks base method. func (m *MockHandler) PollForDecisionTask(arg0 context.Context, arg1 *types.MatchingPollForDecisionTaskRequest) (*types.MatchingPollForDecisionTaskResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PollForDecisionTask", arg0, arg1) ret0, _ := ret[0].(*types.MatchingPollForDecisionTaskResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForDecisionTask indicates an expected call of PollForDecisionTask. func (mr *MockHandlerMockRecorder) PollForDecisionTask(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForDecisionTask", reflect.TypeOf((*MockHandler)(nil).PollForDecisionTask), arg0, arg1) } // QueryWorkflow mocks base method. func (m *MockHandler) QueryWorkflow(arg0 context.Context, arg1 *types.MatchingQueryWorkflowRequest) (*types.MatchingQueryWorkflowResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryWorkflow", arg0, arg1) ret0, _ := ret[0].(*types.MatchingQueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // QueryWorkflow indicates an expected call of QueryWorkflow. func (mr *MockHandlerMockRecorder) QueryWorkflow(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWorkflow", reflect.TypeOf((*MockHandler)(nil).QueryWorkflow), arg0, arg1) } // RefreshTaskListPartitionConfig mocks base method. func (m *MockHandler) RefreshTaskListPartitionConfig(arg0 context.Context, arg1 *types.MatchingRefreshTaskListPartitionConfigRequest) (*types.MatchingRefreshTaskListPartitionConfigResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RefreshTaskListPartitionConfig", arg0, arg1) ret0, _ := ret[0].(*types.MatchingRefreshTaskListPartitionConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RefreshTaskListPartitionConfig indicates an expected call of RefreshTaskListPartitionConfig. func (mr *MockHandlerMockRecorder) RefreshTaskListPartitionConfig(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTaskListPartitionConfig", reflect.TypeOf((*MockHandler)(nil).RefreshTaskListPartitionConfig), arg0, arg1) } // RespondQueryTaskCompleted mocks base method. func (m *MockHandler) RespondQueryTaskCompleted(arg0 context.Context, arg1 *types.MatchingRespondQueryTaskCompletedRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RespondQueryTaskCompleted", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RespondQueryTaskCompleted indicates an expected call of RespondQueryTaskCompleted. func (mr *MockHandlerMockRecorder) RespondQueryTaskCompleted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RespondQueryTaskCompleted", reflect.TypeOf((*MockHandler)(nil).RespondQueryTaskCompleted), arg0, arg1) } // Start mocks base method. func (m *MockHandler) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockHandlerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockHandler)(nil).Start)) } // Stop mocks base method. func (m *MockHandler) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockHandlerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockHandler)(nil).Stop)) } // UpdateTaskListPartitionConfig mocks base method. func (m *MockHandler) UpdateTaskListPartitionConfig(arg0 context.Context, arg1 *types.MatchingUpdateTaskListPartitionConfigRequest) (*types.MatchingUpdateTaskListPartitionConfigResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskListPartitionConfig", arg0, arg1) ret0, _ := ret[0].(*types.MatchingUpdateTaskListPartitionConfigResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTaskListPartitionConfig indicates an expected call of UpdateTaskListPartitionConfig. func (mr *MockHandlerMockRecorder) UpdateTaskListPartitionConfig(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListPartitionConfig", reflect.TypeOf((*MockHandler)(nil).UpdateTaskListPartitionConfig), arg0, arg1) } ================================================ FILE: service/matching/handler/membership.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "fmt" "sync" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/matching/tasklist" ) const subscriptionBufferSize = 1000 // Because there's a bunch of conditions under which matching may be holding a tasklist // reader daemon and other live procesess but when it doesn't (according to the rest of the hashring) // own the tasklist anymore, this listener watches for membership changes and purges anything disused // in the hashring on membership changes. // // Combined with the guard on tasklist instantiation, it should prevent incorrect or poorly timed // creating of tasklist ownership and database shard thrashing between hosts while they figure out // which host is the real owner of the tasklist. // // This is not the main shutdown process, its just an optimization. func (e *matchingEngineImpl) runMembershipChangeLoop() { defer func() { if r := recover(); r != nil { e.logger.Error("matching membership watcher changes caused a panic, recovering", tag.Dynamic("recovered-panic", r)) } }() defer e.shutdownCompletion.Done() if !e.config.EnableTasklistOwnershipGuard() { return } listener := make(chan *membership.ChangedEvent, subscriptionBufferSize) if err := e.membershipResolver.Subscribe(service.Matching, "matching-engine", listener); err != nil { e.logger.Error("Failed to subscribe to membership updates") return } for { select { case event := <-listener: err := e.shutDownNonOwnedTasklists() if err != nil { e.logger.Error("Error while trying to determine if tasklists have been shutdown", tag.Error(err), tag.MembershipChangeEvent(event), ) } case <-e.shutdown: return } } } func (e *matchingEngineImpl) shutDownNonOwnedTasklists() error { if !e.config.EnableTasklistOwnershipGuard() { return nil } noLongerOwned, err := e.getNonOwnedTasklistsLocked() if err != nil { return err } tasklistsShutdownWG := sync.WaitGroup{} for _, tl := range noLongerOwned { // for each of the tasklists that are no longer owned, kick off the // process of stopping them. The stopping process is IO heavy and // can take a while, so do them in parallel to efficiently unload tasklists not owned tasklistsShutdownWG.Add(1) go func(tl tasklist.Manager) { defer func() { if r := recover(); r != nil { e.logger.Error("panic occurred while trying to shut down tasklist", tag.Dynamic("recovered-panic", r)) } }() defer tasklistsShutdownWG.Done() e.logger.Info("shutting down tasklist preemptively because they are no longer owned by this host", tag.WorkflowTaskListType(tl.TaskListID().GetType()), tag.WorkflowTaskListName(tl.TaskListID().GetName()), tag.WorkflowDomainID(tl.TaskListID().GetDomainID()), tag.Dynamic("tasklist-debug-info", tl.String()), ) e.unloadTaskList(tl) }(tl) } tasklistsShutdownWG.Wait() return nil } func (e *matchingEngineImpl) getNonOwnedTasklistsLocked() ([]tasklist.Manager, error) { if !e.config.EnableTasklistOwnershipGuard() { return nil, nil } var toShutDown []tasklist.Manager taskLists := e.taskListRegistry.AllManagers() self, err := e.membershipResolver.WhoAmI() if err != nil { return nil, fmt.Errorf("failed to lookup self im membership: %w", err) } for _, tl := range taskLists { taskListOwner, err := e.membershipResolver.Lookup(service.Matching, tl.TaskListID().GetName()) if err != nil { return nil, fmt.Errorf("failed to lookup task list owner: %w", err) } if taskListOwner.Identity() != self.Identity() { toShutDown = append(toShutDown, tl) } } e.logger.Info("Got list of non-owned-tasklists", tag.Dynamic("tasklist-debug-info", toShutDown), ) return toShutDown, nil } ================================================ FILE: service/matching/handler/membership_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "context" "errors" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" cadence_errors "github.com/uber/cadence/common/errors" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/tasklist" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) func TestGetTaskListManager_OwnerShip(t *testing.T) { testCases := []struct { name string lookUpResult string lookUpErr error whoAmIResult string whoAmIErr error tasklistGuardEnabled bool expectedError error }{ { name: "Not owned by current host", lookUpResult: "A", whoAmIResult: "B", tasklistGuardEnabled: true, expectedError: new(cadence_errors.TaskListNotOwnedByHostError), }, { name: "LookupError", lookUpErr: assert.AnError, tasklistGuardEnabled: true, expectedError: assert.AnError, }, { name: "WhoAmIError", whoAmIErr: assert.AnError, tasklistGuardEnabled: true, expectedError: assert.AnError, }, { name: "when feature is not enabled, expect previous behaviour to continue", lookUpResult: "A", whoAmIResult: "B", tasklistGuardEnabled: false, expectedError: nil, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) logger := log.NewNoop() mockTimeSource := clock.NewMockedTimeSourceAt(time.Now()) taskManager := tasklist.NewTestTaskManager(t, logger, mockTimeSource) mockHistoryClient := history.NewMockClient(ctrl) mockMatchingClient := matching.NewMockClient(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) resolverMock := membership.NewMockResolver(ctrl) resolverMock.EXPECT().Subscribe(service.Matching, "matching-engine", gomock.Any()).AnyTimes() mockShardDistributorExecutorClient := executorclient.NewMockClient(ctrl) // this is only if the call goes through mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.CreateDomainCacheEntry(matchingTestDomainName), nil).AnyTimes() mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.CreateDomainCacheEntry(matchingTestDomainName), nil).AnyTimes() mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return(matchingTestDomainName, nil).AnyTimes() config := defaultTestConfig() taskListEnabled := tc.tasklistGuardEnabled config.EnableTasklistOwnershipGuard = func(opts ...dynamicproperties.FilterOption) bool { return taskListEnabled } // Exclude all task lists from the ShardDistributor so that errIfShardOwnershipLost // exercises the ringpop (hash-ring) ownership path that these test cases are actually // testing. With PercentageOnboardedToShardManager=0, no task list name is below the // percentage threshold, so every name is considered excluded regardless of whether it // contains a UUID. config.ExcludeShortLivedTaskListsFromShardManager = func(opts ...dynamicproperties.FilterOption) bool { return true } config.PercentageOnboardedToShardManager = func(opts ...dynamicproperties.FilterOption) int { return 0 } matchingEngine := NewEngine( taskManager, cluster.GetTestClusterMetadata(true), mockHistoryClient, mockMatchingClient, config, logger, metrics.NewClient(tally.NoopScope, metrics.Matching, metrics.HistogramMigration{}), tally.NoopScope, mockDomainCache, resolverMock, isolationgroup.NewMockState(ctrl), mockTimeSource, mockShardDistributorExecutorClient, defaultSDExecutorConfig(), nil, ).(*matchingEngineImpl) // All task lists are excluded from the ShardDistributor, so GetShardProcess is // never called. Only Start/Stop are needed for lifecycle management. mockExec := executorclient.NewMockExecutor[tasklist.ShardProcessor](ctrl) mockExec.EXPECT().Start(gomock.Any()).AnyTimes() mockExec.EXPECT().Stop().AnyTimes() matchingEngine.executor = mockExec resolverMock.EXPECT().Lookup(gomock.Any(), gomock.Any()).Return( membership.NewDetailedHostInfo("", tc.lookUpResult, make(membership.PortMap)), tc.lookUpErr, ).AnyTimes() resolverMock.EXPECT().WhoAmI().Return( membership.NewDetailedHostInfo("", tc.whoAmIResult, make(membership.PortMap)), tc.whoAmIErr, ).AnyTimes() _, err := matchingEngine.getOrCreateTaskListManager(context.Background(), tasklist.NewTestTaskListID(t, "domain", "tasklist", persistence.TaskListTypeActivity), types.TaskListKindNormal, ) if tc.expectedError != nil { assert.ErrorAs(t, err, &tc.expectedError) } else { assert.NoError(t, err) } }) } } func TestMembershipSubscriptionRecoversAfterPanic(t *testing.T) { assert.NotPanics(t, func() { ctrl := gomock.NewController(t) r := resource.NewTest(t, ctrl, 0) r.MembershipResolver.EXPECT().Subscribe(service.Matching, "matching-engine", gomock.Any()).DoAndReturn(func(_, _, _ any) { panic("a panic has occurred") }) engine := matchingEngineImpl{ membershipResolver: r.MembershipResolver, config: &config.Config{ EnableTasklistOwnershipGuard: func(opts ...dynamicproperties.FilterOption) bool { return true }, }, logger: log.NewNoop(), shutdown: make(chan struct{}), } engine.runMembershipChangeLoop() }) } func TestSubscriptionAndShutdown(t *testing.T) { ctrl := gomock.NewController(t) mockResolver := membership.NewMockResolver(ctrl) mockExecutor := executorclient.NewMockExecutor[tasklist.ShardProcessor](ctrl) mockExecutor.EXPECT().Stop() shutdownWG := sync.WaitGroup{} shutdownWG.Add(1) mockDomainCache := cache.NewMockDomainCache(ctrl) engine := matchingEngineImpl{ shutdownCompletion: &shutdownWG, membershipResolver: mockResolver, taskListRegistry: tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()), config: &config.Config{EnableTasklistOwnershipGuard: func(opts ...dynamicproperties.FilterOption) bool { return true }}, shutdown: make(chan struct{}), logger: log.NewNoop(), domainCache: mockDomainCache, executor: mockExecutor, } mockResolver.EXPECT().WhoAmI().Return(membership.NewDetailedHostInfo("host2", "host2", nil), nil).AnyTimes() mockResolver.EXPECT().Subscribe(service.Matching, "matching-engine", gomock.Any()) mockDomainCache.EXPECT().UnregisterDomainChangeCallback(service.Matching).Times(1) go engine.runMembershipChangeLoop() engine.Stop() assert.True(t, common.AwaitWaitGroup(&shutdownWG, 10*time.Second), "runMembershipChangeLoop has to be shut down") } func TestSubscriptionAndErrorReturned(t *testing.T) { ctrl := gomock.NewController(t) mockResolver := membership.NewMockResolver(ctrl) mockExecutor := executorclient.NewMockExecutor[tasklist.ShardProcessor](ctrl) mockExecutor.EXPECT().Stop() mockDomainCache := cache.NewMockDomainCache(ctrl) shutdownWG := sync.WaitGroup{} shutdownWG.Add(1) membershipChangeHandledWG := sync.WaitGroup{} membershipChangeHandledWG.Add(1) engine := matchingEngineImpl{ shutdownCompletion: &shutdownWG, membershipResolver: mockResolver, taskListRegistry: tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()), config: &config.Config{EnableTasklistOwnershipGuard: func(opts ...dynamicproperties.FilterOption) bool { return true }}, shutdown: make(chan struct{}), logger: log.NewNoop(), domainCache: mockDomainCache, executor: mockExecutor, } // this should trigger the error case on a membership event // unfortunately, this is purely for code-coverage, no checks are involved mockResolver.EXPECT().WhoAmI().DoAndReturn(func() (membership.HostInfo, error) { membershipChangeHandledWG.Done() return membership.HostInfo{}, errors.New("failure") }).MinTimes(1) mockResolver.EXPECT().Subscribe(service.Matching, "matching-engine", gomock.Any()).Do( func(service string, name string, inc chan<- *membership.ChangedEvent) { m := membership.ChangedEvent{ HostsAdded: nil, HostsUpdated: nil, HostsRemoved: []string{"host123"}, } inc <- &m }) mockDomainCache.EXPECT().UnregisterDomainChangeCallback(service.Matching).Times(1) go engine.runMembershipChangeLoop() assert.True(t, common.AwaitWaitGroup(&membershipChangeHandledWG, 10*time.Second), "membership event is not handled", ) engine.Stop() assert.True(t, common.AwaitWaitGroup(&shutdownWG, 10*time.Second), "runMembershipChangeLoop has to be shut down", ) } func TestSubscribeToMembershipChangesQuitsIfSubscribeFails(t *testing.T) { ctrl := gomock.NewController(t) mockResolver := membership.NewMockResolver(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) logger, logs := testlogger.NewObserved(t) shutdownWG := sync.WaitGroup{} shutdownWG.Add(1) engine := matchingEngineImpl{ shutdownCompletion: &shutdownWG, membershipResolver: mockResolver, taskListRegistry: tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()), config: &config.Config{EnableTasklistOwnershipGuard: func(opts ...dynamicproperties.FilterOption) bool { return true }}, shutdown: make(chan struct{}), logger: logger, domainCache: mockDomainCache, } mockResolver.EXPECT().Subscribe(service.Matching, "matching-engine", gomock.Any()). Return(errors.New("failed to subscribe")) mockDomainCache.EXPECT().UnregisterDomainChangeCallback(service.Matching).AnyTimes() go engine.runMembershipChangeLoop() // we do not stop `engine` here - it has to shut down after failing to Subscribe assert.True( t, common.AwaitWaitGroup(&shutdownWG, 10*time.Second), "runMembershipChangeLoop should immediately shut down because of critical error", ) // check we emitted error-message filteredLogs := logs.FilterMessage("Failed to subscribe to membership updates") assert.Equal(t, 1, filteredLogs.Len(), "error-message should be produced") } func TestGetTasklistManagerShutdownScenario(t *testing.T) { ctrl := gomock.NewController(t) mockResolver := membership.NewMockResolver(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) self := membership.NewDetailedHostInfo("self", "self", nil) mockResolver.EXPECT().WhoAmI().Return(self, nil).AnyTimes() mockDomainCache.EXPECT().UnregisterDomainChangeCallback(service.Matching).Times(1) mockExecutor := executorclient.NewMockExecutor[tasklist.ShardProcessor](ctrl) mockExecutor.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() mockExecutor.EXPECT().Stop() shutdownWG := sync.WaitGroup{} shutdownWG.Add(0) engine := matchingEngineImpl{ shutdownCompletion: &shutdownWG, membershipResolver: mockResolver, taskListRegistry: tasklist.NewTaskListRegistry(metrics.NewNoopMetricsClient()), config: &config.Config{ EnableTasklistOwnershipGuard: func(opts ...dynamicproperties.FilterOption) bool { return true }, ExcludeShortLivedTaskListsFromShardManager: func(opts ...dynamicproperties.FilterOption) bool { return true }, PercentageOnboardedToShardManager: func(opts ...dynamicproperties.FilterOption) int { return 100 }, }, shutdown: make(chan struct{}), logger: log.NewNoop(), domainCache: mockDomainCache, executor: mockExecutor, } // set this engine to be shutting down to trigger the tasklistGetTasklistByID guard engine.Stop() tl, _ := tasklist.NewIdentifier("domainid", "tl", 0) res, err := engine.getOrCreateTaskListManager(context.Background(), tl, types.TaskListKindNormal) assertErr := &cadence_errors.TaskListNotOwnedByHostError{} assert.ErrorAs(t, err, &assertErr) assert.Nil(t, res) } ================================================ FILE: service/matching/liveness/liveness.go ================================================ // Modifications Copyright (c) 2020 Uber Technologies Inc. // Copyright (c) 2020 Temporal Technologies, Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package liveness import ( "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" ) type ( Liveness struct { status int32 timeSource clock.TimeSource ttl time.Duration // stopCh is used to signal the liveness to stop stopCh chan struct{} // wg is used to wait for the liveness to stop wg sync.WaitGroup // broadcast shutdown functions broadcastShutdownFn func() lastEventTimeNano int64 } ) var _ common.Daemon = (*Liveness)(nil) // NewLiveness creates a Liveness daemon that calls the broadcastShutdownFn if it does not receive MarkAlive() within ttl // NOTE: livesness needs to be stopped explicitly to avoid go routine leak func NewLiveness(timeSource clock.TimeSource, ttl time.Duration, broadcastShutdownFn func()) *Liveness { return &Liveness{ status: common.DaemonStatusInitialized, timeSource: timeSource, ttl: ttl, stopCh: make(chan struct{}), broadcastShutdownFn: broadcastShutdownFn, lastEventTimeNano: timeSource.Now().UnixNano(), } } func (l *Liveness) Start() { if !atomic.CompareAndSwapInt32(&l.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } l.wg.Add(1) checkTimer := l.timeSource.NewTicker(l.ttl / 2) go l.eventLoop(checkTimer) } // Stop ONLY shuts down liveness does not block on broadcastShutdownFn func (l *Liveness) Stop() { if !atomic.CompareAndSwapInt32(&l.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(l.stopCh) l.wg.Wait() } func (l *Liveness) eventLoop(ticker clock.Ticker) { defer l.wg.Done() defer ticker.Stop() for { select { case <-ticker.Chan(): if !l.IsAlive() { go l.broadcastShutdownFn() // do not block shutdown return } case <-l.stopCh: return } } } func (l *Liveness) IsAlive() bool { now := l.timeSource.Now().UnixNano() lastUpdate := atomic.LoadInt64(&l.lastEventTimeNano) return now-lastUpdate < l.ttl.Nanoseconds() } func (l *Liveness) MarkAlive() { now := l.timeSource.Now().UnixNano() atomic.StoreInt64(&l.lastEventTimeNano, now) } ================================================ FILE: service/matching/liveness/liveness_test.go ================================================ // Modifications Copyright (c) 2020 Uber Technologies Inc. // Copyright (c) 2020 Temporal Technologies, Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package liveness import ( "sync/atomic" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/clock" ) type ( livenessSuite struct { suite.Suite *require.Assertions timeSource clock.MockedTimeSource ttl time.Duration shutdownFlag int32 liveness *Liveness } ) func TestLivenessSuite(t *testing.T) { s := new(livenessSuite) suite.Run(t, s) } func (s *livenessSuite) SetupTest() { s.Assertions = require.New(s.T()) s.ttl = 500 * time.Millisecond s.timeSource = clock.NewMockedTimeSource() s.shutdownFlag = 0 s.liveness = NewLiveness(s.timeSource, s.ttl, func() { atomic.CompareAndSwapInt32(&s.shutdownFlag, 0, 1) s.liveness.Stop() }) } func (s *livenessSuite) TestIsAlive_No() { s.timeSource.Advance(s.ttl) alive := s.liveness.IsAlive() s.False(alive) } func (s *livenessSuite) TestIsAlive_Yes() { s.timeSource.Advance(s.ttl / 2) alive := s.liveness.IsAlive() s.True(alive) } func (s *livenessSuite) TestMarkAlive_Noop() { lastEventTime := s.liveness.lastEventTimeNano // not advanding time so markAlive will be a noop s.liveness.MarkAlive() s.Equal(lastEventTime, s.liveness.lastEventTimeNano) } func (s *livenessSuite) TestMarkAlive_Updated() { s.timeSource.Advance(time.Duration(1)) s.liveness.MarkAlive() s.Equal(s.timeSource.Now().UnixNano(), s.liveness.lastEventTimeNano) } func (s *livenessSuite) TestEventLoop_Noop() { s.liveness.Start() defer s.liveness.Stop() // advance time ttl/2 and mark alive s.timeSource.Advance(s.ttl / 2) s.liveness.MarkAlive() s.True(s.liveness.IsAlive()) // advance time ttl/2 more and validate still alive s.timeSource.Advance(s.ttl / 2) time.Sleep(100 * time.Millisecond) // give event loop time to run s.True(s.liveness.IsAlive()) s.Equal(int32(0), atomic.LoadInt32(&s.shutdownFlag)) } func (s *livenessSuite) TestEventLoop_Shutdown() { s.liveness.Start() defer s.liveness.Stop() s.timeSource.Advance(s.ttl) <-s.liveness.stopCh s.Equal(int32(1), atomic.LoadInt32(&s.shutdownFlag)) } ================================================ FILE: service/matching/poller/manager.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package poller import ( "context" "sync" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/types" ) const ( pollerHistoryInitSize = 0 pollerHistoryInitMaxSize = 5000 pollerHistoryTTL = 5 * time.Minute ) type ( Info struct { Identity string RatePerSecond float64 IsolationGroup string } Manager interface { StartPoll(pollerID string, cancelFunc context.CancelFunc, info *Info) EndPoll(pollerID string) CancelPoll(pollerID string) bool HasPollerFromIsolationGroupAfter(isolationGroup string, after time.Time) bool HasPollerAfter(after time.Time) bool GetCount() int GetCountByIsolationGroup(after time.Time) map[string]int ListInfo() []*types.PollerInfo } historicalPoller struct { info *Info outstanding bool } outstandingPoller struct { info *Info cancel context.CancelFunc } manager struct { // identity -> historicalPoller historyCache cache.Cache timeSource clock.TimeSource lock sync.RWMutex mostRecentPollEnd time.Time mostRecentPollEndByGroup map[string]time.Time outstandingCountByGroup map[string]int // pollerID -> outstandingPoller outstanding map[string]outstandingPoller // OnHistoryUpdatedFunc is a function called when the historyCache was updated onHistoryUpdatedFunc HistoryUpdatedFunc } // HistoryUpdatedFunc is a type for notifying applications when the poller historyCache was updated HistoryUpdatedFunc func() ) func NewPollerManager(historyUpdatedFunc HistoryUpdatedFunc, timeSource clock.TimeSource) Manager { opts := &cache.Options{ InitialCapacity: pollerHistoryInitSize, TTL: pollerHistoryTTL, Pin: false, MaxCount: pollerHistoryInitMaxSize, TimeSource: timeSource, } return &manager{ historyCache: cache.New(opts), timeSource: timeSource, onHistoryUpdatedFunc: historyUpdatedFunc, mostRecentPollEndByGroup: make(map[string]time.Time), outstandingCountByGroup: make(map[string]int), outstanding: make(map[string]outstandingPoller), } } func (m *manager) StartPoll(pollerID string, cancelFunc context.CancelFunc, info *Info) { if info.Identity != "" { m.historyCache.Put(info.Identity, &historicalPoller{ info: info, // If there's no PollerID then we'll never have a subsequent EndPoll. Treat it like it isn't outstanding // so that we don't keep returning it forever // It doesn't seem like there's a possible code path where this happens outstanding: pollerID != "", }) if m.onHistoryUpdatedFunc != nil { m.onHistoryUpdatedFunc() } } if pollerID != "" { m.lock.Lock() defer m.lock.Unlock() m.outstanding[pollerID] = outstandingPoller{ info: info, cancel: cancelFunc, } if info.IsolationGroup != "" { m.outstandingCountByGroup[info.IsolationGroup]++ } } } func (m *manager) EndPoll(pollerID string) { poller, ok := m.tryRemovePoller(pollerID) if ok && poller.info.Identity != "" { // Refresh the cache to update the timestamp and clear outstanding value m.historyCache.Put(poller.info.Identity, &historicalPoller{ info: poller.info, outstanding: false, }) if m.onHistoryUpdatedFunc != nil { m.onHistoryUpdatedFunc() } } } func (m *manager) tryRemovePoller(pollerID string) (outstandingPoller, bool) { m.lock.Lock() defer m.lock.Unlock() now := m.timeSource.Now() poller, ok := m.outstanding[pollerID] if ok { delete(m.outstanding, pollerID) if poller.info.IsolationGroup != "" { m.mostRecentPollEndByGroup[poller.info.IsolationGroup] = now m.outstandingCountByGroup[poller.info.IsolationGroup]-- } } // reset the mostRecentPollEnd even if we didn't find the poller. They might not have specified a PollerID. // It doesn't seem possible outside of tests, but there's no harm in being safe m.mostRecentPollEnd = now return poller, ok } func (m *manager) CancelPoll(pollerID string) bool { poller, ok := m.tryGetPoller(pollerID) if ok { poller.cancel() } return ok } func (m *manager) tryGetPoller(pollerID string) (outstandingPoller, bool) { m.lock.RLock() defer m.lock.RUnlock() poller, ok := m.outstanding[pollerID] return poller, ok } func (m *manager) HasPollerFromIsolationGroupAfter(isolationGroup string, earliest time.Time) bool { m.lock.RLock() defer m.lock.RUnlock() return m.outstandingCountByGroup[isolationGroup] > 0 || m.mostRecentPollEndByGroup[isolationGroup].After(earliest) } func (m *manager) HasPollerAfter(earliestAccessTime time.Time) bool { m.lock.RLock() defer m.lock.RUnlock() return len(m.outstanding) > 0 || m.mostRecentPollEnd.After(earliestAccessTime) } func (m *manager) GetCount() int { return m.historyCache.Size() } func (m *manager) GetCountByIsolationGroup(after time.Time) map[string]int { groupSet := make(map[string]int) m.forEachPoller(after, func(identity string, info *Info, lastAccessTime time.Time) { if info.IsolationGroup != "" { groupSet[info.IsolationGroup]++ } }) return groupSet } func (m *manager) ListInfo() []*types.PollerInfo { var result []*types.PollerInfo // optimistic size get, it can change before Iterator call. size := m.historyCache.Size() result = make([]*types.PollerInfo, 0, size) m.forEachPoller(time.Time{}, func(identity string, info *Info, lastAccessTime time.Time) { result = append(result, &types.PollerInfo{ Identity: identity, LastAccessTime: common.Int64Ptr(lastAccessTime.UnixNano()), RatePerSecond: info.RatePerSecond, }) }) return result } func (m *manager) forEachPoller(after time.Time, callback func(string, *Info, time.Time)) { ite := m.historyCache.Iterator() defer ite.Close() for ite.HasNext() { entry := ite.Next() key := entry.Key().(string) value := entry.Value().(*historicalPoller) lastAccessTime := entry.CreateTime() if after.IsZero() || value.outstanding || after.Before(lastAccessTime) { callback(key, value.info, lastAccessTime) } } } ================================================ FILE: service/matching/poller/manager_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package poller import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/types" ) var NoopFunc = func() {} func TestManager_HistoryCallback(t *testing.T) { mockTime := clock.NewMockedTimeSource() counter := 0 m := NewPollerManager(func() { counter++ }, mockTime) m.StartPoll("a", NoopFunc, &Info{Identity: "a"}) assert.Equal(t, 1, counter) m.EndPoll("a") assert.Equal(t, 2, counter) // Require identity counter = 0 m.StartPoll("b", NoopFunc, &Info{}) assert.Equal(t, 0, counter) } func TestManager_CancelPoll(t *testing.T) { t.Run("happy path", func(t *testing.T) { mockTime := clock.NewMockedTimeSource() m := NewPollerManager(NoopFunc, mockTime) counter := 0 m.StartPoll("a", func() { counter++ }, &Info{}) res := m.CancelPoll("a") assert.Equal(t, true, res) assert.Equal(t, 1, counter) }) t.Run("repeated", func(t *testing.T) { mockTime := clock.NewMockedTimeSource() m := NewPollerManager(NoopFunc, mockTime) counter := 0 m.StartPoll("a", func() { counter++ }, &Info{}) // Cancel doesn't remove it from the outstanding res := m.CancelPoll("a") res2 := m.CancelPoll("a") assert.Equal(t, true, res) assert.Equal(t, true, res2) assert.Equal(t, 2, counter) }) t.Run("unknown", func(t *testing.T) { mockTime := clock.NewMockedTimeSource() m := NewPollerManager(NoopFunc, mockTime) counter := 0 m.StartPoll("b", func() { counter++ }, &Info{}) res := m.CancelPoll("a") assert.Equal(t, false, res) assert.Equal(t, 0, counter) }) } func TestManager_HasPollerFromIsolationGroupAfter(t *testing.T) { startTime := time.Date(2024, time.October, 28, 0, 0, 0, 0, time.UTC) group := "the group" cases := []struct { name string fn func(mockTime clock.MockedTimeSource, m Manager) after time.Time result bool }{ { name: "outstanding poller", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{IsolationGroup: group}) }, after: startTime, result: true, }, { name: "recent poller", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{IsolationGroup: group}) mockTime.Advance(time.Second) m.EndPoll("a") }, after: startTime, result: true, }, { name: "boundary condition poller", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{IsolationGroup: group}) mockTime.Advance(time.Nanosecond) m.EndPoll("a") }, // Needs to after this time, not at this time after: startTime.Add(time.Nanosecond), result: false, }, { name: "expired poller", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{IsolationGroup: group}) m.EndPoll("a") }, after: startTime.Add(time.Minute), result: false, }, { name: "never polled", fn: func(mockTime clock.MockedTimeSource, m Manager) {}, after: startTime, result: false, }, { name: "unrelated poller", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{IsolationGroup: "other"}) mockTime.Advance(time.Second) m.EndPoll("a") }, after: startTime, result: false, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { mockTime := clock.NewMockedTimeSourceAt(startTime) m := NewPollerManager(NoopFunc, mockTime) tc.fn(mockTime, m) assert.Equal(t, tc.result, m.HasPollerFromIsolationGroupAfter(group, tc.after)) }) } } func TestManager_HasPollerAfter(t *testing.T) { startTime := time.Date(2024, time.October, 28, 0, 0, 0, 0, time.UTC) cases := []struct { name string fn func(mockTime clock.MockedTimeSource, m Manager) after time.Time result bool }{ { name: "outstanding poller", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{}) }, after: startTime, result: true, }, { name: "recent poller", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{}) mockTime.Advance(time.Second) m.EndPoll("a") }, after: startTime, result: true, }, { name: "boundary condition poller", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{}) mockTime.Advance(time.Nanosecond) m.EndPoll("a") }, // Needs to after this time, not at this time after: startTime.Add(time.Nanosecond), result: false, }, { name: "expired poller", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{}) m.EndPoll("a") }, after: startTime.Add(time.Minute), result: false, }, { name: "never polled", fn: func(mockTime clock.MockedTimeSource, m Manager) {}, after: startTime, result: false, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { mockTime := clock.NewMockedTimeSourceAt(startTime) m := NewPollerManager(NoopFunc, mockTime) tc.fn(mockTime, m) assert.Equal(t, tc.result, m.HasPollerAfter(tc.after)) }) } } func TestManager_GetCount(t *testing.T) { mockTime := clock.NewMockedTimeSource() m := NewPollerManager(NoopFunc, mockTime) m.StartPoll("a", NoopFunc, &Info{Identity: "aIdent"}) m.EndPoll("a") m.StartPoll("b", NoopFunc, &Info{Identity: "bIdent"}) mockTime.Advance(4 * time.Minute) // t = 4m m.EndPoll("b") mockTime.Advance(2 * time.Minute) // t = 6m m.StartPoll("c", NoopFunc, &Info{Identity: "cIdent"}) m.EndPoll("c") m.StartPoll("d", NoopFunc, &Info{Identity: "dIdent"}) // Since the cache doesn't actively evict, we still see a in the results assert.Equal(t, 4, m.GetCount()) } func TestManager_ListInfo(t *testing.T) { startTime := time.Date(2024, time.October, 28, 0, 0, 0, 0, time.UTC) cases := []struct { name string fn func(mockTime clock.MockedTimeSource, m Manager) result []*types.PollerInfo }{ { name: "happy path", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{Identity: "aIdent"}) m.EndPoll("a") m.StartPoll("b", NoopFunc, &Info{Identity: "bIdent"}) mockTime.Advance(time.Minute) // t = 1m m.EndPoll("b") mockTime.Advance(time.Minute) // t = 2m m.StartPoll("c", NoopFunc, &Info{Identity: "cIdent", RatePerSecond: 1.0}) m.EndPoll("c") m.StartPoll("d", NoopFunc, &Info{Identity: "dIdent"}) }, result: []*types.PollerInfo{ { LastAccessTime: common.Int64Ptr(startTime.Add(2 * time.Minute).UnixNano()), Identity: "dIdent", RatePerSecond: 0, }, { LastAccessTime: common.Int64Ptr(startTime.Add(2 * time.Minute).UnixNano()), Identity: "cIdent", RatePerSecond: 1.0, }, { LastAccessTime: common.Int64Ptr(startTime.Add(time.Minute).UnixNano()), Identity: "bIdent", RatePerSecond: 0, }, { LastAccessTime: common.Int64Ptr(startTime.UnixNano()), Identity: "aIdent", RatePerSecond: 0, }, }, }, { name: "exclude pollers with no identity", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{Identity: ""}) m.EndPoll("a") }, result: []*types.PollerInfo{}, }, { name: "include pollers with no pollerID", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("", NoopFunc, &Info{Identity: "aIdent"}) }, result: []*types.PollerInfo{ { LastAccessTime: common.Int64Ptr(startTime.UnixNano()), Identity: "aIdent", RatePerSecond: 0, }, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { mockTime := clock.NewMockedTimeSourceAt(startTime) m := NewPollerManager(NoopFunc, mockTime) tc.fn(mockTime, m) assert.Equal(t, tc.result, m.ListInfo()) }) } } func TestManager_GetCountByIsolationGroup(t *testing.T) { startTime := time.Date(2024, time.October, 28, 0, 0, 0, 0, time.UTC) cases := []struct { name string fn func(mockTime clock.MockedTimeSource, m Manager) after time.Time result map[string]int }{ { name: "happy path", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{Identity: "aIdent", IsolationGroup: "groupA"}) m.EndPoll("a") m.StartPoll("b", NoopFunc, &Info{Identity: "bIdent", IsolationGroup: "groupA"}) mockTime.Advance(time.Minute) // t = 1m m.EndPoll("b") mockTime.Advance(time.Minute) // t = 2m m.StartPoll("c", NoopFunc, &Info{Identity: "cIdent", RatePerSecond: 1.0, IsolationGroup: "groupB"}) m.EndPoll("c") m.StartPoll("d", NoopFunc, &Info{Identity: "dIdent"}) }, result: map[string]int{ "groupA": 2, "groupB": 1, }, }, { name: "some expired", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{Identity: "aIdent", IsolationGroup: "groupA"}) m.EndPoll("a") m.StartPoll("b", NoopFunc, &Info{Identity: "bIdent", IsolationGroup: "groupA"}) mockTime.Advance(time.Minute) // t = 1m m.EndPoll("b") mockTime.Advance(time.Minute) // t = 2m m.StartPoll("c", NoopFunc, &Info{Identity: "cIdent", RatePerSecond: 1.0, IsolationGroup: "groupB"}) m.EndPoll("c") m.StartPoll("d", NoopFunc, &Info{Identity: "dIdent"}) }, after: startTime.Add(time.Minute - time.Nanosecond), result: map[string]int{ "groupA": 1, "groupB": 1, }, }, { name: "exclude pollers with no identity", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("a", NoopFunc, &Info{Identity: ""}) m.EndPoll("a") }, after: startTime.Add(-time.Nanosecond), result: map[string]int{}, }, { name: "include pollers with no pollerID", fn: func(mockTime clock.MockedTimeSource, m Manager) { m.StartPoll("", NoopFunc, &Info{Identity: "aIdent", IsolationGroup: "groupA"}) }, after: startTime.Add(-time.Nanosecond), result: map[string]int{ "groupA": 1, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { mockTime := clock.NewMockedTimeSourceAt(startTime) m := NewPollerManager(NoopFunc, mockTime) tc.fn(mockTime, m) assert.Equal(t, tc.result, m.GetCountByIsolationGroup(tc.after)) }) } } ================================================ FILE: service/matching/service.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package matching import ( "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/handler" "github.com/uber/cadence/service/matching/wrappers/grpc" "github.com/uber/cadence/service/matching/wrappers/thrift" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" ) // Service represents the cadence-matching service type Service struct { resource.Resource status int32 handler handler.Handler stopC chan struct{} config *config.Config ShardDistributorMatchingConfig clientcommon.Config drainObserver clientcommon.DrainSignalObserver } // NewService builds a new cadence-matching service func NewService( params *resource.Params, ) (resource.Resource, error) { serviceConfig := config.NewConfig( dynamicconfig.NewCollection( params.DynamicConfig, params.Logger, dynamicproperties.ClusterNameFilter(params.ClusterMetadata.GetCurrentClusterName()), ), params.HostName, params.RPCConfig, params.GetIsolationGroups, ) serviceResource, err := resource.New( params, service.Matching, &service.Config{ PersistenceMaxQPS: serviceConfig.PersistenceMaxQPS, PersistenceGlobalMaxQPS: serviceConfig.PersistenceGlobalMaxQPS, ThrottledLoggerMaxRPS: serviceConfig.ThrottledLogRPS, IsErrorRetryableFunction: common.IsServiceTransientError, // matching doesn't need visibility config as it never read or write visibility }, ) if err != nil { return nil, err } return &Service{ Resource: serviceResource, status: common.DaemonStatusInitialized, config: serviceConfig, stopC: make(chan struct{}), ShardDistributorMatchingConfig: params.ShardDistributorMatchingConfig, drainObserver: params.DrainObserver, }, nil } // Start starts the service func (s *Service) Start() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } logger := s.GetLogger() logger.Info("matching starting") engine := handler.NewEngine( s.GetTaskManager(), s.GetClusterMetadata(), s.GetHistoryClient(), s.GetMatchingRawClient(), // Use non retry client inside matching s.config, s.GetLogger(), s.GetMetricsClient(), s.GetMetricsScope(), s.GetDomainCache(), s.GetMembershipResolver(), s.GetIsolationGroupState(), s.GetTimeSource(), s.GetShardDistributorExecutorClient(), s.ShardDistributorMatchingConfig, s.drainObserver, ) s.handler = handler.NewHandler(engine, s.config, s.GetDomainCache(), s.GetMetricsClient(), s.GetLogger(), s.GetThrottledLogger()) thriftHandler := thrift.NewThriftHandler(s.handler) thriftHandler.Register(s.GetDispatcher()) grpcHandler := grpc.NewGRPCHandler(s.handler) grpcHandler.Register(s.GetDispatcher()) // must start base service first s.Resource.Start() s.handler.Start() logger.Info("matching started") <-s.stopC } // Stop stops the service func (s *Service) Stop() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } // remove self from membership ring and wait for traffic to drain s.GetLogger().Info("ShutdownHandler: Evicting self from membership ring") s.GetMembershipResolver().EvictSelf() s.GetLogger().Info("ShutdownHandler: Waiting for others to discover I am unhealthy") time.Sleep(s.config.ShutdownDrainDuration()) close(s.stopC) s.handler.Stop() s.Resource.Stop() s.GetLogger().Info("matching stopped") } ================================================ FILE: service/matching/tasklist/adaptive_scaler.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "context" "math" "sync" "sync/atomic" "golang.org/x/sync/errgroup" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/event" ) type ( AdaptiveScaler interface { common.Daemon } adaptiveScalerImpl struct { taskListID *Identifier tlMgr Manager config *config.TaskListConfig timeSource clock.TimeSource logger log.Logger scope metrics.Scope matchingClient matching.Client taskListType *types.TaskListType status int32 wg sync.WaitGroup ctx context.Context cancel func() overLoad clock.Sustain underLoad clock.Sustain isolation *isolationBalancer baseEvent event.E } aggregatePartitionMetrics struct { totalQPS float64 qpsByIsolationGroup map[string]float64 hasPollersByIsolationGroup map[string]bool byPartition map[int]*partitionMetrics isIsolationEnabled bool } partitionMetrics struct { qps float64 readOnly bool empty bool } ) func NewAdaptiveScaler( taskListID *Identifier, tlMgr Manager, config *config.TaskListConfig, timeSource clock.TimeSource, logger log.Logger, scope metrics.Scope, matchingClient matching.Client, baseEvent event.E, ) AdaptiveScaler { ctx, cancel := context.WithCancel(context.Background()) return &adaptiveScalerImpl{ taskListID: taskListID, tlMgr: tlMgr, config: config, timeSource: timeSource, logger: logger.WithTags(tag.ComponentTaskListAdaptiveScaler), scope: scope, matchingClient: matchingClient, taskListType: getTaskListType(taskListID.GetType()), ctx: ctx, cancel: cancel, isolation: newIsolationBalancer(timeSource, scope, config), overLoad: clock.NewSustain(timeSource, config.PartitionUpscaleSustainedDuration), underLoad: clock.NewSustain(timeSource, config.PartitionDownscaleSustainedDuration), baseEvent: baseEvent, } } func (a *adaptiveScalerImpl) Start() { if !atomic.CompareAndSwapInt32(&a.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } a.logger.Info("adaptive task list scaler state changed", tag.LifeCycleStarted) a.wg.Add(1) go a.runPeriodicLoop() } func (a *adaptiveScalerImpl) Stop() { if !atomic.CompareAndSwapInt32(&a.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } a.cancel() a.wg.Wait() a.logger.Info("adaptive task list scaler state changed", tag.LifeCycleStopped) } func (a *adaptiveScalerImpl) runPeriodicLoop() { defer a.wg.Done() timer := a.timeSource.NewTimer(a.config.AdaptiveScalerUpdateInterval()) defer timer.Stop() for { select { case <-a.ctx.Done(): return case <-timer.Chan(): a.run() timer.Reset(a.config.AdaptiveScalerUpdateInterval()) } } } func (a *adaptiveScalerImpl) run() { if !a.config.EnableAdaptiveScaler() || !a.config.EnableGetNumberOfPartitionsFromCache() { return } partitionConfig := a.getPartitionConfig() m, err := a.collectPartitionMetrics(partitionConfig) // TODO: Handle this better. Maybe we should allow scaling up but not down if our data is incomplete? if err != nil { a.underLoad.Reset() a.overLoad.Reset() a.isolation.reset() a.logger.Error("Failed to collect partition metrics", tag.Error(err)) return } // adjust the number of write partitions based on qps numWritePartitions := a.calculateWritePartitionCount(m.totalQPS, len(partitionConfig.WritePartitions)) writePartitions, writeChanged := a.adjustWritePartitions(partitionConfig.WritePartitions, numWritePartitions) isolationChanged := false if m.isIsolationEnabled { writePartitions, isolationChanged = a.isolation.adjustWritePartitions(m, writePartitions) if isolationChanged { a.scope.IncCounter(metrics.IsolationRebalance) } } else { a.isolation.reset() } // adjustReadPartitions will copy over any changes to the writePartitions, so it needs to happen after we // potentially change the isolation group assignments readPartitions, readChanged := a.adjustReadPartitions(m, partitionConfig.ReadPartitions, writePartitions) e := a.baseEvent e.EventName = "AdaptiveScalerCalculationResult" e.Payload = map[string]any{ "NumReadPartitions": len(readPartitions), "NumWritePartitions": len(writePartitions), "ReadChanged": readChanged, "WriteChanged": writeChanged, "IsolationChanged": isolationChanged, "QPS": m.totalQPS, } event.Log(e) if !writeChanged && !readChanged && !isolationChanged { return } newConfig := &types.TaskListPartitionConfig{ ReadPartitions: readPartitions, WritePartitions: writePartitions, } a.logger.Info("adaptive scaler is updating number of partitions", tag.CurrentQPS(m.totalQPS), tag.NumReadPartitions(len(readPartitions)), tag.NumWritePartitions(len(writePartitions)), tag.ReadChanged(readChanged), tag.WriteChanged(writeChanged), tag.IsolationChanged(isolationChanged), tag.Dynamic("old-partition-config", partitionConfig), tag.Dynamic("new-partition-config", newConfig), ) a.scope.IncCounter(metrics.CadenceRequests) err = a.tlMgr.UpdateTaskListPartitionConfig(a.ctx, newConfig) if err != nil { a.logger.Error("failed to update task list partition config", tag.Error(err)) a.scope.IncCounter(metrics.CadenceFailures) } } func (a *adaptiveScalerImpl) getPartitionConfig() *types.TaskListPartitionConfig { partitionConfig := a.tlMgr.TaskListPartitionConfig() if partitionConfig == nil { partitionConfig = &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, } } return partitionConfig } func (a *adaptiveScalerImpl) calculateWritePartitionCount(qps float64, numWritePartitions int) int { upscaleRps := float64(a.config.PartitionUpscaleRPS()) partitions := float64(numWritePartitions) downscaleFactor := a.config.PartitionDownscaleFactor() upscaleThreshold := partitions * upscaleRps downscaleThreshold := (partitions - 1) * upscaleRps * downscaleFactor a.scope.UpdateGauge(metrics.EstimatedAddTaskQPSGauge, qps) a.scope.UpdateGauge(metrics.TaskListPartitionUpscaleThresholdGauge, upscaleThreshold) a.scope.UpdateGauge(metrics.TaskListPartitionDownscaleThresholdGauge, downscaleThreshold) result := numWritePartitions if a.overLoad.CheckAndReset(qps > upscaleThreshold) { result = getNumberOfPartitions(qps, upscaleRps) a.scope.IncCounter(metrics.PartitionUpscale) a.logger.Info("adjust write partitions", tag.CurrentQPS(qps), tag.PartitionUpscaleThreshold(upscaleThreshold), tag.PartitionDownscaleThreshold(downscaleThreshold), tag.PartitionDownscaleFactor(downscaleFactor), tag.CurrentNumWritePartitions(numWritePartitions), tag.NumWritePartitions(result)) } if a.underLoad.CheckAndReset(qps < downscaleThreshold) { result = getNumberOfPartitions(qps, upscaleRps) a.scope.IncCounter(metrics.PartitionDownscale) a.logger.Info("adjust write partitions", tag.CurrentQPS(qps), tag.PartitionUpscaleThreshold(upscaleThreshold), tag.PartitionDownscaleThreshold(downscaleThreshold), tag.PartitionDownscaleFactor(downscaleFactor), tag.CurrentNumWritePartitions(numWritePartitions), tag.NumWritePartitions(result)) } return result } func (a *adaptiveScalerImpl) adjustWritePartitions(writePartitions map[int]*types.TaskListPartition, targetWritePartitions int) (map[int]*types.TaskListPartition, bool) { if len(writePartitions) == targetWritePartitions { return writePartitions, false } result := make(map[int]*types.TaskListPartition, targetWritePartitions) for i := 0; i < targetWritePartitions; i++ { if p, ok := writePartitions[i]; ok { result[i] = p } else { result[i] = &types.TaskListPartition{} } } return result, true } func (a *adaptiveScalerImpl) adjustReadPartitions(m *aggregatePartitionMetrics, oldReadPartitions map[int]*types.TaskListPartition, newWritePartitions map[int]*types.TaskListPartition) (map[int]*types.TaskListPartition, bool) { result := make(map[int]*types.TaskListPartition, len(newWritePartitions)) changed := false for id, p := range oldReadPartitions { result[id] = p } for id, p := range newWritePartitions { if _, ok := result[id]; !ok { changed = true } result[id] = p } for i := len(result) - 1; i >= len(newWritePartitions); i-- { p, ok := m.byPartition[i] if !ok { resp, err := a.describePartition(i) if err != nil { a.logger.Warn("failed to get partition metrics", tag.WorkflowTaskListName(a.taskListID.GetPartition(i)), tag.Error(err)) break } p = a.toPartitionMetrics(i, resp) } if p.readOnly && p.empty { changed = true delete(result, i) a.scope.IncCounter(metrics.PartitionDrained) } else { break } } if changed { a.logger.Info("adjust read partitions", tag.NumReadPartitions(len(result)), tag.NumWritePartitions(len(newWritePartitions))) } return result, changed } func (a *adaptiveScalerImpl) collectPartitionMetrics(config *types.TaskListPartitionConfig) (*aggregatePartitionMetrics, error) { if a.config.EnablePartitionIsolationGroupAssignment() { return a.fetchMetricsFromPartitions(config) } return a.assumeEvenQPS(config) } func (a *adaptiveScalerImpl) assumeEvenQPS(config *types.TaskListPartitionConfig) (*aggregatePartitionMetrics, error) { resp := a.tlMgr.DescribeTaskList(true) totalQPS := resp.TaskListStatus.NewTasksPerSecond * float64(len(config.WritePartitions)) return &aggregatePartitionMetrics{ totalQPS: totalQPS, isIsolationEnabled: false, }, nil } func (a *adaptiveScalerImpl) fetchMetricsFromPartitions(config *types.TaskListPartitionConfig) (*aggregatePartitionMetrics, error) { var mutex sync.Mutex results := make(map[int]*types.DescribeTaskListResponse, len(config.ReadPartitions)) g := &errgroup.Group{} for p := range config.ReadPartitions { partitionID := p g.Go(func() (e error) { defer func() { log.CapturePanic(recover(), a.logger, &e) }() result, e := a.describePartition(partitionID) if e != nil { a.logger.Warn("failed to get partition metrics", tag.WorkflowTaskListName(a.taskListID.GetPartition(partitionID)), tag.Error(e)) } if result != nil { mutex.Lock() defer mutex.Unlock() results[partitionID] = result } return e }) } err := g.Wait() if err != nil { return nil, err } return a.toAggregateMetrics(results), err } func (a *adaptiveScalerImpl) describePartition(partitionID int) (*types.DescribeTaskListResponse, error) { if partitionID == 0 { return a.tlMgr.DescribeTaskList(true), nil } return a.matchingClient.DescribeTaskList(a.ctx, &types.MatchingDescribeTaskListRequest{ DomainUUID: a.taskListID.GetDomainID(), DescRequest: &types.DescribeTaskListRequest{ TaskListType: a.taskListType, TaskList: &types.TaskList{ Name: a.taskListID.GetPartition(partitionID), Kind: types.TaskListKindNormal.Ptr(), }, IncludeTaskListStatus: true, }, }) } func (a *adaptiveScalerImpl) toAggregateMetrics(partitions map[int]*types.DescribeTaskListResponse) *aggregatePartitionMetrics { total := 0.0 byIsolationGroup := make(map[string]float64) hasPollersByIsolationGroup := make(map[string]bool) byPartition := make(map[int]*partitionMetrics, len(partitions)) for id, p := range partitions { for ig, groupMetrics := range p.TaskListStatus.IsolationGroupMetrics { byIsolationGroup[ig] += groupMetrics.NewTasksPerSecond hasPollersByIsolationGroup[ig] = hasPollersByIsolationGroup[ig] || groupMetrics.PollerCount > 0 } total += p.TaskListStatus.NewTasksPerSecond byPartition[id] = a.toPartitionMetrics(id, p) } return &aggregatePartitionMetrics{ totalQPS: total, qpsByIsolationGroup: byIsolationGroup, hasPollersByIsolationGroup: hasPollersByIsolationGroup, byPartition: byPartition, isIsolationEnabled: true, } } func (a *adaptiveScalerImpl) toPartitionMetrics(id int, p *types.DescribeTaskListResponse) *partitionMetrics { hasWritePartition := true if p.PartitionConfig != nil { _, hasWritePartition = p.PartitionConfig.WritePartitions[id] } var empty bool if a.config.EnablePartitionEmptyCheck() { empty = p.TaskListStatus.Empty } else { empty = p.TaskListStatus.BacklogCountHint == 0 } return &partitionMetrics{ qps: p.TaskListStatus.NewTasksPerSecond, empty: empty, readOnly: !hasWritePartition, } } func getTaskListType(taskListType int) *types.TaskListType { if taskListType == persistence.TaskListTypeDecision { return types.TaskListTypeDecision.Ptr() } else if taskListType == persistence.TaskListTypeActivity { return types.TaskListTypeActivity.Ptr() } return nil } func getNumberOfPartitions(qps float64, upscaleQPS float64) int { p := int(math.Ceil(qps / upscaleQPS)) if p <= 0 { p = 1 } return p } ================================================ FILE: service/matching/tasklist/adaptive_scaler_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common/clock" commonConfig "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/stats" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/event" ) type mockAdaptiveScalerDeps struct { id *Identifier mockManager *MockManager mockQPSTracker *stats.MockQPSTracker mockTimeSource clock.MockedTimeSource mockMatchingClient *matching.MockClient dynamicClient dynamicconfig.Client config *config.TaskListConfig logger log.Logger scope metrics.Scope } func setupMocksForAdaptiveScaler(t *testing.T, taskListID *Identifier) (*adaptiveScalerImpl, *mockAdaptiveScalerDeps) { ctrl := gomock.NewController(t) logger := testlogger.New(t) scope := metrics.NoopScope mockManager := NewMockManager(ctrl) mockQPSTracker := stats.NewMockQPSTracker(ctrl) mockTimeSource := clock.NewMockedTimeSourceAt(time.Now()) mockMatchingClient := matching.NewMockClient(ctrl) dynamicClient := dynamicconfig.NewInMemoryClient() cfg := newTaskListConfig(taskListID, config.NewConfig(dynamicconfig.NewCollection(dynamicClient, logger), "test-host", commonConfig.RPC{}, func() []string { return nil }), "test-domain") deps := &mockAdaptiveScalerDeps{ id: taskListID, mockManager: mockManager, mockQPSTracker: mockQPSTracker, mockTimeSource: mockTimeSource, mockMatchingClient: mockMatchingClient, dynamicClient: dynamicClient, config: cfg, } scaler := NewAdaptiveScaler(taskListID, mockManager, cfg, mockTimeSource, logger, scope, mockMatchingClient, event.E{}).(*adaptiveScalerImpl) return scaler, deps } func TestAdaptiveScalerLifecycle(t *testing.T) { defer goleak.VerifyNone(t) taskListID, err := NewIdentifier("test-domain-id", "test-task-list", 0) require.NoError(t, err) scaler, _ := setupMocksForAdaptiveScaler(t, taskListID) // test idempotency assert.NotPanics(t, scaler.Start) assert.NotPanics(t, scaler.Start) assert.NotPanics(t, scaler.Stop) assert.NotPanics(t, scaler.Stop) } func TestAdaptiveScalerRun(t *testing.T) { testCases := []struct { name string mockSetup func(*mockAdaptiveScalerDeps) cycles int }{ { name: "no op", mockSetup: func(deps *mockAdaptiveScalerDeps) { mockDescribeTaskList(deps, 0, withPartitionsAndQPS(1, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(nil) }, cycles: 1, }, { name: "overload start", mockSetup: func(deps *mockAdaptiveScalerDeps) { mockDescribeTaskList(deps, 0, withPartitionsAndQPS(1, 300)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(nil) }, cycles: 1, }, { name: "overload sustained", mockSetup: func(deps *mockAdaptiveScalerDeps) { // overload start mockDescribeTaskList(deps, 0, withPartitionsAndQPS(1, 300)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(nil) // overload passing sustained period mockDescribeTaskList(deps, 0, withPartitionsAndQPS(1, 300)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(nil) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ ReadPartitions: partitions(2), WritePartitions: partitions(2), }).Return(nil) }, cycles: 2, }, { name: "overload fluctuate", mockSetup: func(deps *mockAdaptiveScalerDeps) { // overload start mockDescribeTaskList(deps, 0, withPartitionsAndQPS(1, 300)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(nil) // load back to normal mockDescribeTaskList(deps, 0, withPartitionsAndQPS(1, 100)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(nil) // overload start mockDescribeTaskList(deps, 0, withPartitionsAndQPS(1, 300)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(nil) // load back to normal mockDescribeTaskList(deps, 0, withPartitionsAndQPS(1, 100)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(nil) }, cycles: 4, }, { name: "underload start", mockSetup: func(deps *mockAdaptiveScalerDeps) { mockDescribeTaskList(deps, 0, withPartitionsAndQPS(10, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(10), ReadPartitions: partitions(10), }) }, cycles: 1, }, { name: "underload sustained", mockSetup: func(deps *mockAdaptiveScalerDeps) { mockDescribeTaskList(deps, 0, withPartitionsAndQPS(10, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(10), ReadPartitions: partitions(10), }) mockDescribeTaskList(deps, 0, withPartitionsAndQPS(10, 0)) // Partition 9 will be checked if it is drained, but it won't have received the update yet mockDescribeTaskList(deps, 9, withPartitionsAndQPS(10, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(10), ReadPartitions: partitions(10), }) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ WritePartitions: partitions(1), ReadPartitions: partitions(10), }).Return(nil) }, cycles: 2, }, { name: "underload sustained then drain - require empty", mockSetup: func(deps *mockAdaptiveScalerDeps) { deps.config.EnablePartitionEmptyCheck = func() bool { return true } // Start of Cycle 1 mockDescribeTaskList(deps, 0, withPartitionsAndQPS(3, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(3), ReadPartitions: partitions(3), }) // Start of Cycle 2 mockDescribeTaskList(deps, 0, withPartitionsAndQPS(3, 0)) // Partition 2 will be checked but won't be drained because it hasn't received the update yet mockDescribeTaskList(deps, 2, withPartitionsAndQPS(3, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(3), ReadPartitions: partitions(3), }) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ WritePartitions: partitions(1), ReadPartitions: partitions(3), }).Return(nil) // Start of cycle 3 mockDescribeTaskList(deps, 0, withPartitionsAndBacklog(3, 1, 0)) // 2 will be checked and drained because Empty == true, BacklogCountHint is ignored mockDescribeTaskList(deps, 2, &types.DescribeTaskListResponse{ Pollers: nil, TaskListStatus: &types.TaskListStatus{NewTasksPerSecond: 0, BacklogCountHint: 1000, Empty: true}, PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: partitions(3), WritePartitions: partitions(1), }, }) // 1 will be checked and won't be drained because Empty == false, even though BacklogCountHint == 0 mockDescribeTaskList(deps, 1, &types.DescribeTaskListResponse{ Pollers: nil, TaskListStatus: &types.TaskListStatus{NewTasksPerSecond: 0, BacklogCountHint: 0, Empty: false}, PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: partitions(3), WritePartitions: partitions(1), }, }) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(1), ReadPartitions: partitions(3), }) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ WritePartitions: partitions(1), ReadPartitions: partitions(2), }).Return(nil) }, cycles: 3, }, { name: "underload sustained then drain", mockSetup: func(deps *mockAdaptiveScalerDeps) { mockDescribeTaskList(deps, 0, withPartitionsAndQPS(10, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(10), ReadPartitions: partitions(10), }) mockDescribeTaskList(deps, 0, withPartitionsAndQPS(10, 0)) // Partition 9 will be checked if it is drained, but it won't have received the update yet mockDescribeTaskList(deps, 9, withPartitionsAndQPS(10, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(10), ReadPartitions: partitions(10), }) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ WritePartitions: partitions(1), ReadPartitions: partitions(10), }).Return(nil) mockDescribeTaskList(deps, 0, withPartitionsAndBacklog(10, 1, 0)) mockDescribeTaskList(deps, 9, withPartitionsAndBacklog(10, 1, 0)) mockDescribeTaskList(deps, 8, withPartitionsAndBacklog(10, 1, 0)) mockDescribeTaskList(deps, 7, withPartitionsAndBacklog(10, 1, 0)) mockDescribeTaskList(deps, 6, withPartitionsAndBacklog(10, 1, 0)) mockDescribeTaskList(deps, 5, withPartitionsAndBacklog(10, 1, 0)) mockDescribeTaskList(deps, 4, withPartitionsAndBacklog(10, 1, 1)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(1), ReadPartitions: partitions(10), }) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ WritePartitions: partitions(1), ReadPartitions: partitions(5), }).Return(nil) }, cycles: 3, }, { name: "overload but no fluctuation", mockSetup: func(deps *mockAdaptiveScalerDeps) { // overload start mockDescribeTaskList(deps, 0, withPartitionsAndQPS(1, 210)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(nil) // overload passing sustained period mockDescribeTaskList(deps, 0, withPartitionsAndQPS(1, 210)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(nil) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ ReadPartitions: partitions(2), WritePartitions: partitions(2), }).Return(nil) // not overload with 1 partition, but avoid fluctuation, so don't scale down mockDescribeTaskList(deps, 0, withPartitionsAndQPS(2, 190)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ ReadPartitions: partitions(2), WritePartitions: partitions(2), }) mockDescribeTaskList(deps, 0, withPartitionsAndQPS(2, 190)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ ReadPartitions: partitions(2), WritePartitions: partitions(2), }) }, cycles: 4, }, { name: "isolation - aggregate metrics to scale up", mockSetup: func(deps *mockAdaptiveScalerDeps) { deps.config.EnablePartitionIsolationGroupAssignment = func() bool { return true } // overload start mockDescribeTaskList(deps, 0, withPartitionsAndQPS(2, 1)) mockDescribeTaskList(deps, 1, withPartitionsAndQPS(2, 400)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(2), ReadPartitions: partitions(2), }) // overload passing sustained period mockDescribeTaskList(deps, 0, withPartitionsAndQPS(2, 1)) mockDescribeTaskList(deps, 1, withPartitionsAndQPS(2, 400)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(2), ReadPartitions: partitions(2), }) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ ReadPartitions: partitions(3), WritePartitions: partitions(3), }).Return(nil) }, cycles: 2, }, { name: "isolation - aggregate metrics to scale down", mockSetup: func(deps *mockAdaptiveScalerDeps) { deps.config.EnablePartitionIsolationGroupAssignment = func() bool { return true } // underload start mockDescribeTaskList(deps, 0, withPartitionsAndQPS(3, 200)) mockDescribeTaskList(deps, 1, withPartitionsAndQPS(3, 49)) mockDescribeTaskList(deps, 2, withPartitionsAndQPS(3, 50)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(3), ReadPartitions: partitions(3), }) // underload passing sustained period mockDescribeTaskList(deps, 0, withPartitionsAndQPS(3, 200)) mockDescribeTaskList(deps, 1, withPartitionsAndQPS(3, 49)) mockDescribeTaskList(deps, 2, withPartitionsAndQPS(3, 50)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(3), ReadPartitions: partitions(3), }) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ ReadPartitions: partitions(3), WritePartitions: partitions(2), }).Return(nil) mockDescribeTaskList(deps, 0, withPartitionsAndQPS(2, 200)) mockDescribeTaskList(deps, 1, withPartitionsAndQPS(2, 99)) mockDescribeTaskList(deps, 2, withPartitionsAndBacklog(3, 2, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(2), ReadPartitions: partitions(3), }) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ ReadPartitions: partitions(2), WritePartitions: partitions(2), }).Return(nil) }, cycles: 3, }, { name: "isolation - scale group up", mockSetup: func(deps *mockAdaptiveScalerDeps) { deps.config.EnablePartitionIsolationGroupAssignment = func() bool { return true } partitionConfig := &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"c", "d"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"c", "d"}}, }, } partition0Resp := withConfigAndQPS(partitionConfig, map[string]float64{ "a": 101, "b": 20, }) partition1Resp := withConfigAndQPS(partitionConfig, map[string]float64{ "c": 20, "d": 20, }) // overload start for a mockDescribeTaskList(deps, 0, partition0Resp) mockDescribeTaskList(deps, 1, partition1Resp) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(partitionConfig) // overload sustained mockDescribeTaskList(deps, 0, partition0Resp) mockDescribeTaskList(deps, 1, partition1Resp) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(partitionConfig) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "c", "d"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "c", "d"}}, }, }).Return(nil) }, cycles: 2, }, { name: "isolation - scale group down", mockSetup: func(deps *mockAdaptiveScalerDeps) { deps.config.EnablePartitionIsolationGroupAssignment = func() bool { return true } partitionConfig := &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "c", "d"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "c", "d"}}, }, } partition0Resp := withConfigAndQPS(partitionConfig, map[string]float64{ "a": 74, "b": 50, }) partition1Resp := withConfigAndQPS(partitionConfig, map[string]float64{ "c": 50, "d": 50, }) // overload start for a mockDescribeTaskList(deps, 0, partition0Resp) mockDescribeTaskList(deps, 1, partition1Resp) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(partitionConfig) // overload sustained mockDescribeTaskList(deps, 0, partition0Resp) mockDescribeTaskList(deps, 1, partition1Resp) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(partitionConfig) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"c", "d"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"c", "d"}}, }, }).Return(nil) }, cycles: 2, }, { name: "isolation - scale partitions down", mockSetup: func(deps *mockAdaptiveScalerDeps) { deps.config.EnablePartitionIsolationGroupAssignment = func() bool { return true } partitionConfig := &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "c", "d"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "c", "d"}}, }, } partition0Resp := withConfigAndQPS(partitionConfig, map[string]float64{ "a": 74, "b": 20, }) partition1Resp := withConfigAndQPS(partitionConfig, map[string]float64{ "c": 20, "d": 20, }) // overload start for a mockDescribeTaskList(deps, 0, partition0Resp) mockDescribeTaskList(deps, 1, partition1Resp) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(partitionConfig) // overload sustained mockDescribeTaskList(deps, 0, partition0Resp) mockDescribeTaskList(deps, 1, partition1Resp) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(partitionConfig) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {IsolationGroups: []string{"a", "c", "d"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }).Return(nil) drainConfig := &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, 1: {IsolationGroups: []string{"c", "d"}}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, } // Drain partition 1 mockDescribeTaskList(deps, 0, partition0Resp) mockDescribeTaskList(deps, 1, withConfigAndQPS(drainConfig, nil)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(drainConfig) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: {}, }, }).Return(nil) }, cycles: 3, }, { name: "isolation - error calling DescribeTaskList results in no-op", mockSetup: func(deps *mockAdaptiveScalerDeps) { deps.config.EnablePartitionIsolationGroupAssignment = func() bool { return true } mockDescribeTaskList(deps, 0, withPartitionsAndQPS(3, 0)) mockDescribeTaskList(deps, 1, withPartitionsAndQPS(3, 0)) mockDescribeTaskListWithErr(deps, 2, context.DeadlineExceeded) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(3), ReadPartitions: partitions(3), }) // underload would normally pass sustain period, but the error resets it mockDescribeTaskList(deps, 0, withPartitionsAndQPS(3, 0)) mockDescribeTaskList(deps, 1, withPartitionsAndQPS(3, 0)) mockDescribeTaskList(deps, 2, withPartitionsAndQPS(3, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(3), ReadPartitions: partitions(3), }) // Now we can scale down mockDescribeTaskList(deps, 0, withPartitionsAndQPS(3, 0)) mockDescribeTaskList(deps, 1, withPartitionsAndQPS(3, 0)) mockDescribeTaskList(deps, 2, withPartitionsAndQPS(3, 0)) deps.mockManager.EXPECT().TaskListPartitionConfig().Return(&types.TaskListPartitionConfig{ WritePartitions: partitions(3), ReadPartitions: partitions(3), }) deps.mockManager.EXPECT().UpdateTaskListPartitionConfig(gomock.Any(), &types.TaskListPartitionConfig{ ReadPartitions: partitions(3), WritePartitions: partitions(1), }).Return(nil) }, cycles: 3, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { taskListID, err := NewIdentifier("test-domain-id", "test-task-list", 0) require.NoError(t, err) scaler, deps := setupMocksForAdaptiveScaler(t, taskListID) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingEnableAdaptiveScaler, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingEnableGetNumberOfPartitionsFromCache, true)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingPartitionUpscaleRPS, 200)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingPartitionDownscaleFactor, 0.75)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingPartitionUpscaleSustainedDuration, time.Second)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingPartitionDownscaleSustainedDuration, time.Second)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupsPerPartition, 2)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupUpscaleSustainedDuration, time.Second)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupDownscaleSustainedDuration, time.Second)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupHasPollersSustainedDuration, time.Second)) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupNoPollersSustainedDuration, time.Second)) tc.mockSetup(deps) for i := 0; i < tc.cycles; i++ { scaler.run() deps.mockTimeSource.Advance(time.Second) } }) } } func withConfigAndQPS(config *types.TaskListPartitionConfig, qpsByGroup map[string]float64) *types.DescribeTaskListResponse { isolationMetrics := make(map[string]*types.IsolationGroupMetrics) total := float64(0) for group, qps := range qpsByGroup { isolationMetrics[group] = &types.IsolationGroupMetrics{ NewTasksPerSecond: qps, PollerCount: 1, } total += qps } return &types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{}, TaskListStatus: &types.TaskListStatus{ BacklogCountHint: 0, ReadLevel: 0, AckLevel: 0, RatePerSecond: 0, TaskIDBlock: nil, IsolationGroupMetrics: isolationMetrics, NewTasksPerSecond: total, }, PartitionConfig: config, } } func withPartitionsAndQPS(numPartitions int, qps float64) *types.DescribeTaskListResponse { return &types.DescribeTaskListResponse{ Pollers: nil, TaskListStatus: &types.TaskListStatus{NewTasksPerSecond: qps}, PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: partitions(numPartitions), WritePartitions: partitions(numPartitions), }, } } func withPartitionsAndBacklog(numRead, numWrite int, backlog int64) *types.DescribeTaskListResponse { return &types.DescribeTaskListResponse{ Pollers: nil, TaskListStatus: &types.TaskListStatus{NewTasksPerSecond: 0, BacklogCountHint: backlog}, PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: partitions(numRead), WritePartitions: partitions(numWrite), }, } } func mockDescribeTaskList(mocks *mockAdaptiveScalerDeps, partitionID int, resp *types.DescribeTaskListResponse) { if partitionID == 0 { mocks.mockManager.EXPECT().DescribeTaskList(true).Return(resp) } else { mocks.mockMatchingClient.EXPECT().DescribeTaskList(gomock.Any(), &types.MatchingDescribeTaskListRequest{ DomainUUID: mocks.id.domainID, DescRequest: &types.DescribeTaskListRequest{ TaskList: &types.TaskList{ Name: mocks.id.GetPartition(partitionID), Kind: types.TaskListKindNormal.Ptr(), }, TaskListType: types.TaskListTypeDecision.Ptr(), IncludeTaskListStatus: true, }, }).Return(resp, nil) } } func mockDescribeTaskListWithErr(mocks *mockAdaptiveScalerDeps, partitionID int, err error) { mocks.mockMatchingClient.EXPECT().DescribeTaskList(gomock.Any(), &types.MatchingDescribeTaskListRequest{ DomainUUID: mocks.id.domainID, DescRequest: &types.DescribeTaskListRequest{ TaskList: &types.TaskList{ Name: mocks.id.GetPartition(partitionID), Kind: types.TaskListKindNormal.Ptr(), }, TaskListType: types.TaskListTypeDecision.Ptr(), IncludeTaskListStatus: true, }, }).Return(nil, err) } ================================================ FILE: service/matching/tasklist/db.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "context" "sync" "sync/atomic" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" ) type ( taskListDB struct { sync.RWMutex domainID string domainName string taskListName string taskListKind int taskType int rangeID int64 backlogCount int64 ackLevel int64 partitionConfig *persistence.TaskListPartitionConfig store persistence.TaskManager logger log.Logger } taskListState struct { rangeID int64 ackLevel int64 } ) // newTaskListDB returns an instance of an object that represents // persistence view of a taskList. All mutations / reads to taskLists // wrt persistence go through this object. // // This class will serialize writes to persistence that do condition updates. There are // two reasons for doing this: // - To work around known Cassandra issue where concurrent LWT to the same partition cause timeout errors // - To provide the guarantee that there is only writer who updates taskList in persistence at any given point in time // This guarantee makes some of the other code simpler and there is no impact to perf because updates to tasklist are // spread out and happen in background routines func newTaskListDB(store persistence.TaskManager, domainID string, domainName string, name string, taskType int, kind int, logger log.Logger) *taskListDB { return &taskListDB{ domainID: domainID, domainName: domainName, taskListName: name, taskListKind: kind, taskType: taskType, store: store, logger: logger, } } // RangeID returns the current persistence view of rangeID func (db *taskListDB) RangeID() int64 { db.RLock() defer db.RUnlock() return db.rangeID } // BacklogCount returns the current backlog size func (db *taskListDB) BacklogCount() int64 { return atomic.LoadInt64(&db.backlogCount) } func (db *taskListDB) PartitionConfig() *persistence.TaskListPartitionConfig { db.RLock() defer db.RUnlock() return db.partitionConfig } // RenewLease renews the lease on a tasklist. If there is no previous lease, // this method will attempt to steal tasklist from current owner func (db *taskListDB) RenewLease() (taskListState, error) { db.Lock() defer db.Unlock() resp, err := db.store.LeaseTaskList(context.Background(), &persistence.LeaseTaskListRequest{ DomainID: db.domainID, TaskList: db.taskListName, TaskType: db.taskType, TaskListKind: db.taskListKind, RangeID: atomic.LoadInt64(&db.rangeID), DomainName: db.domainName, }) if err != nil { return taskListState{}, err } db.rangeID = resp.TaskListInfo.RangeID db.ackLevel = resp.TaskListInfo.AckLevel db.partitionConfig = resp.TaskListInfo.AdaptivePartitionConfig return taskListState{rangeID: db.rangeID, ackLevel: resp.TaskListInfo.AckLevel}, nil } // UpdateState updates the taskList state with the given value func (db *taskListDB) UpdateState(ackLevel int64) error { db.Lock() defer db.Unlock() _, err := db.store.UpdateTaskList(context.Background(), &persistence.UpdateTaskListRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: db.domainID, Name: db.taskListName, TaskType: db.taskType, AckLevel: ackLevel, RangeID: db.rangeID, Kind: db.taskListKind, AdaptivePartitionConfig: db.partitionConfig, }, DomainName: db.domainName, }) if err != nil { return err } db.ackLevel = ackLevel return nil } func (db *taskListDB) UpdateTaskListPartitionConfig(partitionConfig *persistence.TaskListPartitionConfig) error { db.Lock() defer db.Unlock() _, err := db.store.UpdateTaskList(context.Background(), &persistence.UpdateTaskListRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: db.domainID, Name: db.taskListName, TaskType: db.taskType, AckLevel: db.ackLevel, RangeID: db.rangeID, Kind: db.taskListKind, AdaptivePartitionConfig: partitionConfig, }, DomainName: db.domainName, }) if err != nil { return err } db.partitionConfig = partitionConfig return nil } // CreateTasks creates a batch of given tasks for this task list func (db *taskListDB) CreateTasks(tasks []*persistence.CreateTaskInfo) (*persistence.CreateTasksResponse, error) { db.Lock() defer db.Unlock() return db.store.CreateTasks(context.Background(), &persistence.CreateTasksRequest{ TaskListInfo: &persistence.TaskListInfo{ DomainID: db.domainID, Name: db.taskListName, TaskType: db.taskType, RangeID: db.rangeID, }, Tasks: tasks, DomainName: db.domainName, }) } // GetTasks returns a batch of tasks between the given range func (db *taskListDB) GetTasks(minTaskID int64, maxTaskID int64, batchSize int) (*persistence.GetTasksResponse, error) { return db.store.GetTasks(context.Background(), &persistence.GetTasksRequest{ DomainID: db.domainID, TaskList: db.taskListName, TaskType: db.taskType, BatchSize: batchSize, ReadLevel: minTaskID, // exclusive MaxReadLevel: &maxTaskID, // inclusive DomainName: db.domainName, }) } // CompleteTasksLessThan deletes of tasks less than the given taskID. Limit is // the upper bound of number of tasks that can be deleted by this method. It may // or may not be honored func (db *taskListDB) CompleteTasksLessThan(taskID int64, limit int) (int, error) { resp, err := db.store.CompleteTasksLessThan(context.Background(), &persistence.CompleteTasksLessThanRequest{ DomainID: db.domainID, TaskListName: db.taskListName, TaskType: db.taskType, TaskID: taskID, Limit: limit, DomainName: db.domainName, }) if err != nil { db.logger.Error("Persistent store operation failure", tag.StoreOperationCompleteTasksLessThan, tag.Error(err), tag.TaskID(taskID), tag.TaskType(db.taskType), tag.WorkflowTaskListName(db.taskListName)) return 0, err } return resp.TasksCompleted, nil } // GetTaskListSize gets the backlog size of a tasklist func (db *taskListDB) GetTaskListSize(ackLevel int64) (int64, error) { resp, err := db.store.GetTaskListSize(context.Background(), &persistence.GetTaskListSizeRequest{ DomainID: db.domainID, DomainName: db.domainName, TaskListName: db.taskListName, TaskListType: db.taskType, AckLevel: ackLevel, }) if err != nil { return 0, err } atomic.StoreInt64(&db.backlogCount, resp.Size) return resp.Size, nil } func (db *taskListDB) GetTaskListInfo(taskListName string) (*persistence.TaskListInfo, error) { resp, err := db.store.GetTaskList(context.Background(), &persistence.GetTaskListRequest{ DomainID: db.domainID, DomainName: db.domainName, TaskList: taskListName, TaskType: db.taskType, }) if err != nil { return nil, err } return resp.TaskListInfo, nil } ================================================ FILE: service/matching/tasklist/forwarder.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "context" "errors" "sync/atomic" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" ) type ( // forwarderImpl is the type that contains state pertaining to // the api call forwarder component forwarderImpl struct { scope metrics.Scope cfg *config.ForwarderConfig taskListID *Identifier taskListKind types.TaskListKind client matching.Client // token channels that vend tokens necessary to make // API calls exposed by forwarder. Tokens are used // to enforce maxOutstanding forwarded calls from this // instance. And channels are used so that the caller // can use them in a select{} block along with other // conditions addReqToken atomic.Value pollReqToken atomic.Value // cached values of maxOutstanding dynamic config values. // these are used to detect changes outstandingTasksLimit int32 outstandingPollsLimit int32 // todo: implement a rate limiter that automatically // adjusts rate based on ServiceBusy errors from API calls limiter quotas.Limiter } // ForwarderReqToken is the token that must be acquired before // making forwarder API calls. This type contains the state // for the token itself ForwarderReqToken struct { ch chan *ForwarderReqToken } ) var ( ErrNoParent = errors.New("cannot find parent task list for forwarding") ErrTaskListKind = errors.New("forwarding is not supported on sticky task list") ErrInvalidTaskListType = errors.New("unrecognized task list type") ErrForwarderSlowDown = errors.New("tasklist forwarding throttle limit exceeded") ) // noopForwarderTokenC refers to a token channel that blocks forever var noopForwarderTokenC <-chan *ForwarderReqToken = make(chan *ForwarderReqToken) // newForwarder returns an instance of Forwarder object which // can be used to forward api request calls from a task list // child partition to a task list parent partition. The returned // forwarder is tied to a single task list. All of the exposed // methods can return the following errors: // Returns following errors: // - errNoParent: If this task list doesn't have a parent to forward to // - errTaskListKind: If the task list is a sticky task list. Sticky task lists are never partitioned // - errForwarderSlowDown: When the rate limit is exceeded // - errInvalidTaskType: If the task list type is invalid func newForwarder( cfg *config.ForwarderConfig, taskListID *Identifier, kind types.TaskListKind, client matching.Client, scope metrics.Scope, ) Forwarder { rpsFunc := func() float64 { return float64(cfg.ForwarderMaxRatePerSecond()) } fwdr := &forwarderImpl{ cfg: cfg, client: client, taskListID: taskListID, taskListKind: kind, outstandingTasksLimit: int32(cfg.ForwarderMaxOutstandingTasks()), outstandingPollsLimit: int32(cfg.ForwarderMaxOutstandingPolls()), limiter: quotas.NewDynamicRateLimiter(rpsFunc), scope: scope, } fwdr.addReqToken.Store(newForwarderReqToken(int(fwdr.outstandingTasksLimit))) fwdr.pollReqToken.Store(newForwarderReqToken(int(fwdr.outstandingPollsLimit))) return fwdr } // ForwardTask forwards an activity or decision task to the parent task list partition if it exist func (fwdr *forwarderImpl) ForwardTask(ctx context.Context, task *InternalTask) error { if fwdr.taskListKind == types.TaskListKindSticky { return ErrTaskListKind } name := fwdr.taskListID.Parent(fwdr.cfg.ForwarderMaxChildrenPerNode()) if name == "" { return ErrNoParent } if !fwdr.limiter.Allow() { return ErrForwarderSlowDown } var err error sw := fwdr.scope.StartTimer(metrics.ForwardTaskLatencyPerTaskList) defer sw.Stop() switch fwdr.taskListID.GetType() { case persistence.TaskListTypeDecision: _, err = fwdr.client.AddDecisionTask(ctx, &types.AddDecisionTaskRequest{ DomainUUID: task.Event.DomainID, Execution: task.WorkflowExecution(), TaskList: &types.TaskList{ Name: name, Kind: &fwdr.taskListKind, }, ScheduleID: task.Event.ScheduleID, ScheduleToStartTimeoutSeconds: &task.Event.ScheduleToStartTimeoutSeconds, Source: &task.source, ForwardedFrom: fwdr.taskListID.GetName(), PartitionConfig: task.Event.PartitionConfig, }) case persistence.TaskListTypeActivity: _, err = fwdr.client.AddActivityTask(ctx, &types.AddActivityTaskRequest{ DomainUUID: fwdr.taskListID.GetDomainID(), SourceDomainUUID: task.Event.DomainID, Execution: task.WorkflowExecution(), TaskList: &types.TaskList{ Name: name, Kind: &fwdr.taskListKind, }, ScheduleID: task.Event.ScheduleID, ScheduleToStartTimeoutSeconds: &task.Event.ScheduleToStartTimeoutSeconds, Source: &task.source, ForwardedFrom: fwdr.taskListID.GetName(), PartitionConfig: task.Event.PartitionConfig, }) default: return ErrInvalidTaskListType } return fwdr.handleErr(err) } // ForwardQueryTask forwards a query task to parent task list partition, if it exist func (fwdr *forwarderImpl) ForwardQueryTask( ctx context.Context, task *InternalTask, ) (*types.MatchingQueryWorkflowResponse, error) { if fwdr.taskListKind == types.TaskListKindSticky { return nil, ErrTaskListKind } name := fwdr.taskListID.Parent(fwdr.cfg.ForwarderMaxChildrenPerNode()) if name == "" { return nil, ErrNoParent } sw := fwdr.scope.StartTimer(metrics.ForwardQueryLatencyPerTaskList) defer sw.Stop() resp, err := fwdr.client.QueryWorkflow(ctx, &types.MatchingQueryWorkflowRequest{ DomainUUID: task.Query.Request.DomainUUID, TaskList: &types.TaskList{ Name: name, Kind: &fwdr.taskListKind, }, QueryRequest: task.Query.Request.QueryRequest, ForwardedFrom: fwdr.taskListID.GetName(), }) return resp, fwdr.handleErr(err) } // ForwardPoll forwards a poll request to parent task list partition if it exist func (fwdr *forwarderImpl) ForwardPoll(ctx context.Context) (*InternalTask, error) { if fwdr.taskListKind == types.TaskListKindSticky { return nil, ErrTaskListKind } name := fwdr.taskListID.Parent(fwdr.cfg.ForwarderMaxChildrenPerNode()) if name == "" { return nil, ErrNoParent } sw := fwdr.scope.StartTimer(metrics.ForwardPollLatencyPerTaskList) defer sw.Stop() pollerID := PollerIDFromContext(ctx) identity := IdentityFromContext(ctx) isolationGroup := IsolationGroupFromContext(ctx) switch fwdr.taskListID.GetType() { case persistence.TaskListTypeDecision: resp, err := fwdr.client.PollForDecisionTask(ctx, &types.MatchingPollForDecisionTaskRequest{ DomainUUID: fwdr.taskListID.GetDomainID(), PollerID: pollerID, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: name, Kind: &fwdr.taskListKind, }, Identity: identity, }, ForwardedFrom: fwdr.taskListID.GetName(), IsolationGroup: isolationGroup, }) if err != nil { return nil, fwdr.handleErr(err) } return newInternalStartedTask(&startedTaskInfo{decisionTaskInfo: resp}), nil case persistence.TaskListTypeActivity: resp, err := fwdr.client.PollForActivityTask(ctx, &types.MatchingPollForActivityTaskRequest{ DomainUUID: fwdr.taskListID.GetDomainID(), PollerID: pollerID, PollRequest: &types.PollForActivityTaskRequest{ TaskList: &types.TaskList{ Name: name, Kind: &fwdr.taskListKind, }, Identity: identity, }, ForwardedFrom: fwdr.taskListID.GetName(), IsolationGroup: isolationGroup, }) if err != nil { return nil, fwdr.handleErr(err) } return newInternalStartedTask(&startedTaskInfo{activityTaskInfo: resp}), nil } return nil, ErrInvalidTaskListType } // AddReqTokenC returns a channel that can be used to wait for a token // that's necessary before making a ForwardTask or ForwardQueryTask API call. // After the API call is invoked, token.release() must be invoked func (fwdr *forwarderImpl) AddReqTokenC() <-chan *ForwarderReqToken { fwdr.refreshTokenC(&fwdr.addReqToken, &fwdr.outstandingTasksLimit, int32(fwdr.cfg.ForwarderMaxOutstandingTasks())) return fwdr.addReqToken.Load().(*ForwarderReqToken).ch } // PollReqTokenC returns a channel that can be used to wait for a token // that's necessary before making a ForwardPoll API call. After the API // call is invoked, token.release() must be invoked func (fwdr *forwarderImpl) PollReqTokenC() <-chan *ForwarderReqToken { fwdr.refreshTokenC(&fwdr.pollReqToken, &fwdr.outstandingPollsLimit, int32(fwdr.cfg.ForwarderMaxOutstandingPolls())) return fwdr.pollReqToken.Load().(*ForwarderReqToken).ch } func (fwdr *forwarderImpl) refreshTokenC(value *atomic.Value, curr *int32, maxLimit int32) { currLimit := atomic.LoadInt32(curr) if currLimit != maxLimit { if atomic.CompareAndSwapInt32(curr, currLimit, maxLimit) { value.Store(newForwarderReqToken(int(maxLimit))) } } } func (fwdr *forwarderImpl) handleErr(err error) error { if _, ok := err.(*types.ServiceBusyError); ok { return ErrForwarderSlowDown } return err } func newForwarderReqToken(maxOutstanding int) *ForwarderReqToken { reqToken := &ForwarderReqToken{ch: make(chan *ForwarderReqToken, maxOutstanding)} for i := 0; i < maxOutstanding; i++ { reqToken.ch <- reqToken } return reqToken } func (token *ForwarderReqToken) release() { token.ch <- token } ================================================ FILE: service/matching/tasklist/forwarder_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "context" "math/rand" "sync" "sync/atomic" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" ) type ForwarderTestSuite struct { suite.Suite controller *gomock.Controller client *matching.MockClient fwdr *forwarderImpl cfg *config.ForwarderConfig taskList *Identifier } func TestForwarderSuite(t *testing.T) { suite.Run(t, new(ForwarderTestSuite)) } func (t *ForwarderTestSuite) SetupTest() { t.controller = gomock.NewController(t.T()) t.client = matching.NewMockClient(t.controller) t.cfg = &config.ForwarderConfig{ ForwarderMaxOutstandingPolls: func() int { return 1 }, ForwarderMaxRatePerSecond: func() int { return 2 }, ForwarderMaxChildrenPerNode: func() int { return 20 }, ForwarderMaxOutstandingTasks: func() int { return 1 }, } id, err := NewIdentifier("fwdr", "tl0", persistence.TaskListTypeDecision) t.NoError(err) t.taskList = id t.fwdr = newForwarder(t.cfg, t.taskList, types.TaskListKindNormal, t.client, metrics.NoopScope).(*forwarderImpl) } func (t *ForwarderTestSuite) TearDownTest() { t.controller.Finish() } func (t *ForwarderTestSuite) TestForwardTaskError() { task := newInternalTask(&persistence.TaskInfo{}, nil, types.TaskSourceHistory, "", false, nil, "") t.Equal(ErrNoParent, t.fwdr.ForwardTask(context.Background(), task)) t.usingTasklistPartition(persistence.TaskListTypeActivity) t.fwdr.taskListKind = types.TaskListKindSticky t.Equal(ErrTaskListKind, t.fwdr.ForwardTask(context.Background(), task)) } func (t *ForwarderTestSuite) TestForwardDecisionTask() { t.usingTasklistPartition(persistence.TaskListTypeDecision) var request *types.AddDecisionTaskRequest t.client.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.AddDecisionTaskRequest, option ...yarpc.CallOption) { request = arg1 }, ).Return(&types.AddDecisionTaskResponse{}, nil).Times(1) taskInfo := t.newTaskInfo() task := newInternalTask(taskInfo, nil, types.TaskSourceHistory, "", false, nil, "") t.NoError(t.fwdr.ForwardTask(context.Background(), task)) t.NotNil(request) t.Equal(t.taskList.Parent(20), request.TaskList.GetName()) t.Equal(t.fwdr.taskListKind, request.TaskList.GetKind()) t.Equal(taskInfo.DomainID, request.GetDomainUUID()) t.Equal(taskInfo.WorkflowID, request.GetExecution().GetWorkflowID()) t.Equal(taskInfo.RunID, request.GetExecution().GetRunID()) t.Equal(taskInfo.ScheduleID, request.GetScheduleID()) t.Equal(taskInfo.ScheduleToStartTimeoutSeconds, request.GetScheduleToStartTimeoutSeconds()) t.Equal(t.taskList.name, request.GetForwardedFrom()) } func (t *ForwarderTestSuite) TestForwardActivityTask() { t.usingTasklistPartition(persistence.TaskListTypeActivity) var request *types.AddActivityTaskRequest t.client.EXPECT().AddActivityTask(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.AddActivityTaskRequest, option ...yarpc.CallOption) { request = arg1 }, ).Return(&types.AddActivityTaskResponse{}, nil).Times(1) taskInfo := t.newTaskInfo() task := newInternalTask(taskInfo, nil, types.TaskSourceHistory, "", false, nil, "") t.NoError(t.fwdr.ForwardTask(context.Background(), task)) t.NotNil(request) t.Equal(t.taskList.Parent(20), request.TaskList.GetName()) t.Equal(t.fwdr.taskListKind, request.TaskList.GetKind()) t.Equal(t.taskList.domainID, request.GetDomainUUID()) t.Equal(taskInfo.DomainID, request.GetSourceDomainUUID()) t.Equal(taskInfo.WorkflowID, request.GetExecution().GetWorkflowID()) t.Equal(taskInfo.RunID, request.GetExecution().GetRunID()) t.Equal(taskInfo.ScheduleID, request.GetScheduleID()) t.Equal(taskInfo.ScheduleToStartTimeoutSeconds, request.GetScheduleToStartTimeoutSeconds()) t.Equal(t.taskList.name, request.GetForwardedFrom()) } func (t *ForwarderTestSuite) TestForwardTaskRateExceeded() { t.usingTasklistPartition(persistence.TaskListTypeActivity) rps := 2 t.client.EXPECT().AddActivityTask(gomock.Any(), gomock.Any()).Return(&types.AddActivityTaskResponse{}, nil).Times(rps) taskInfo := t.newTaskInfo() task := newInternalTask(taskInfo, nil, types.TaskSourceHistory, "", false, nil, "") for i := 0; i < rps; i++ { t.NoError(t.fwdr.ForwardTask(context.Background(), task)) } t.Equal(ErrForwarderSlowDown, t.fwdr.ForwardTask(context.Background(), task)) } func (t *ForwarderTestSuite) TestForwardQueryTaskError() { task := newInternalQueryTask("id1", &types.MatchingQueryWorkflowRequest{}) _, err := t.fwdr.ForwardQueryTask(context.Background(), task) t.Equal(ErrNoParent, err) t.usingTasklistPartition(persistence.TaskListTypeDecision) t.fwdr.taskListKind = types.TaskListKindSticky _, err = t.fwdr.ForwardQueryTask(context.Background(), task) t.Equal(ErrTaskListKind, err) } func (t *ForwarderTestSuite) TestForwardQueryTask() { t.usingTasklistPartition(persistence.TaskListTypeDecision) task := newInternalQueryTask("id1", &types.MatchingQueryWorkflowRequest{}) resp := &types.MatchingQueryWorkflowResponse{} var request *types.MatchingQueryWorkflowRequest t.client.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.MatchingQueryWorkflowRequest, option ...yarpc.CallOption) { request = arg1 }, ).Return(resp, nil).Times(1) gotResp, err := t.fwdr.ForwardQueryTask(context.Background(), task) t.NoError(err) t.Equal(t.taskList.Parent(20), request.TaskList.GetName()) t.Equal(t.fwdr.taskListKind, request.TaskList.GetKind()) t.True(task.Query.Request.QueryRequest == request.QueryRequest) t.Equal(resp, gotResp) } func (t *ForwarderTestSuite) TestForwardQueryTaskRateNotEnforced() { t.usingTasklistPartition(persistence.TaskListTypeActivity) task := newInternalQueryTask("id1", &types.MatchingQueryWorkflowRequest{}) resp := &types.MatchingQueryWorkflowResponse{} rps := 2 t.client.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()).Return(resp, nil).Times(rps + 1) for i := 0; i < rps; i++ { _, err := t.fwdr.ForwardQueryTask(context.Background(), task) t.NoError(err) } _, err := t.fwdr.ForwardQueryTask(context.Background(), task) t.NoError(err) // no rateliming should be enforced for query task } func (t *ForwarderTestSuite) TestForwardPollError() { _, err := t.fwdr.ForwardPoll(context.Background()) t.Equal(ErrNoParent, err) t.usingTasklistPartition(persistence.TaskListTypeActivity) t.fwdr.taskListKind = types.TaskListKindSticky _, err = t.fwdr.ForwardPoll(context.Background()) t.Equal(ErrTaskListKind, err) } func (t *ForwarderTestSuite) TestForwardPollForDecision() { t.usingTasklistPartition(persistence.TaskListTypeDecision) pollerID := uuid.New() ctx := ContextWithPollerID(context.Background(), pollerID) ctx = ContextWithIdentity(ctx, "id1") resp := &types.MatchingPollForDecisionTaskResponse{} var request *types.MatchingPollForDecisionTaskRequest t.client.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.MatchingPollForDecisionTaskRequest, option ...yarpc.CallOption) { request = arg1 }, ).Return(resp, nil).Times(1) task, err := t.fwdr.ForwardPoll(ctx) t.NoError(err) t.NotNil(task) t.NotNil(request) t.Equal(pollerID, request.GetPollerID()) t.Equal(t.taskList.domainID, request.GetDomainUUID()) t.Equal("id1", request.GetPollRequest().GetIdentity()) t.Equal(t.taskList.Parent(20), request.GetPollRequest().GetTaskList().GetName()) t.Equal(t.fwdr.taskListKind, request.GetPollRequest().GetTaskList().GetKind()) t.Equal(resp, task.PollForDecisionResponse()) t.Nil(task.PollForActivityResponse()) } func (t *ForwarderTestSuite) TestForwardPollForActivity() { t.usingTasklistPartition(persistence.TaskListTypeActivity) pollerID := uuid.New() ctx := ContextWithPollerID(context.Background(), pollerID) ctx = ContextWithIdentity(ctx, "id1") resp := &types.MatchingPollForActivityTaskResponse{} var request *types.MatchingPollForActivityTaskRequest t.client.EXPECT().PollForActivityTask(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.MatchingPollForActivityTaskRequest, option ...yarpc.CallOption) { request = arg1 }, ).Return(resp, nil).Times(1) task, err := t.fwdr.ForwardPoll(ctx) t.NoError(err) t.NotNil(task) t.NotNil(request) t.Equal(pollerID, request.GetPollerID()) t.Equal(t.taskList.domainID, request.GetDomainUUID()) t.Equal("id1", request.GetPollRequest().GetIdentity()) t.Equal(t.taskList.Parent(20), request.GetPollRequest().GetTaskList().GetName()) t.Equal(t.fwdr.taskListKind, request.GetPollRequest().GetTaskList().GetKind()) t.Equal(resp, task.PollForActivityResponse()) t.Nil(task.PollForDecisionResponse()) } func (t *ForwarderTestSuite) TestMaxOutstandingConcurrency() { concurrency := 50 testCases := []struct { name string mustLeakToken bool output int32 }{ {"contention", false, int32(concurrency)}, {"token_leak", true, 1}, } var adds int32 var polls int32 var wg sync.WaitGroup for _, tc := range testCases { adds = 0 polls = 0 t.Run(tc.name, func() { for i := 0; i < concurrency; i++ { wg.Add(1) go func() { select { case token := <-t.fwdr.AddReqTokenC(): if !tc.mustLeakToken { token.release() } atomic.AddInt32(&adds, 1) case <-time.After(time.Millisecond * 100): break } select { case token := <-t.fwdr.PollReqTokenC(): if !tc.mustLeakToken { token.release() } atomic.AddInt32(&polls, 1) case <-time.After(time.Millisecond * 100): break } wg.Done() }() } t.True(common.AwaitWaitGroup(&wg, time.Second)) t.Equal(tc.output, adds) t.Equal(tc.output, polls) }) } } func (t *ForwarderTestSuite) TestMaxOutstandingConfigUpdate() { maxOutstandingTasks := int32(1) maxOutstandingPolls := int32(1) t.fwdr.cfg.ForwarderMaxOutstandingTasks = func() int { return int(atomic.LoadInt32(&maxOutstandingTasks)) } t.fwdr.cfg.ForwarderMaxOutstandingPolls = func() int { return int(atomic.LoadInt32(&maxOutstandingPolls)) } startC := make(chan struct{}) doneWG := sync.WaitGroup{} for i := 0; i < 10; i++ { doneWG.Add(1) go func() { <-startC token1 := <-t.fwdr.AddReqTokenC() token1.release() token2 := <-t.fwdr.PollReqTokenC() token2.release() doneWG.Done() }() } maxOutstandingTasks = 10 maxOutstandingPolls = 10 close(startC) t.True(common.AwaitWaitGroup(&doneWG, time.Second)) t.Equal(10, cap(t.fwdr.addReqToken.Load().(*ForwarderReqToken).ch)) t.Equal(10, cap(t.fwdr.pollReqToken.Load().(*ForwarderReqToken).ch)) } func (t *ForwarderTestSuite) usingTasklistPartition(taskType int) { t.taskList = NewTestTaskListID(t.T(), "fwdr", constants.ReservedTaskListPrefix+"tl0/1", taskType) t.fwdr.taskListID = t.taskList } func (t *ForwarderTestSuite) newTaskInfo() *persistence.TaskInfo { return &persistence.TaskInfo{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), TaskID: rand.Int63(), ScheduleID: rand.Int63(), ScheduleToStartTimeoutSeconds: rand.Int31(), } } ================================================ FILE: service/matching/tasklist/identifier.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "bytes" "fmt" "strconv" "strings" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" ) type ( // Identifier is the key that uniquely identifies a task list Identifier struct { qualifiedTaskListName domainID string taskType int } // qualifiedTaskListName refers to the fully qualified task list name qualifiedTaskListName struct { name string // internal name of the tasks list baseName string // original name of the task list as specified by user partition int // partitionID of task list } ) // newTaskListName returns a fully qualified task list name. // Fully qualified names contain additional metadata about task list // derived from their given name. The additional metadata only makes sense // when a task list has more than one partition. When there is more than // one partition for a user specified task list, each of the // individual partitions have an internal name of the form // // /__cadence_sys/[original-name]/[partitionID] // // The name of the root partition is always the same as the user specified name. Rest of // the partitions follow the naming convention above. In addition, the task lists partitions // logically form a N-ary tree where N is configurable dynamically. The tree formation is an // optimization to allow for partitioned task lists to dispatch tasks with low latency when // throughput is low - See https://github.com/uber/cadence/issues/2098 // // Returns error if the given name is non-compliant with the required format // for task list names func newTaskListName(name string) (qualifiedTaskListName, error) { tn := qualifiedTaskListName{ name: name, baseName: name, } if err := tn.init(); err != nil { return qualifiedTaskListName{}, err } return tn, nil } // IsRoot returns true if this task list is a root partition func (tn *qualifiedTaskListName) IsRoot() bool { return tn.partition == 0 } func (tn *qualifiedTaskListName) Partition() int { return tn.partition } // GetRoot returns the root name for a task list func (tn *qualifiedTaskListName) GetRoot() string { return tn.baseName } // GetName returns the name of the task list func (tn *qualifiedTaskListName) GetName() string { return tn.name } // Parent returns the name of the parent task list // input: // // degree: Number of children at each level of the tree // // Returns empty string if this task list is the root func (tn *qualifiedTaskListName) Parent(degree int) string { if tn.IsRoot() || degree == 0 { return "" } pid := (tn.partition+degree-1)/degree - 1 return tn.GetPartition(pid) } func (tn *qualifiedTaskListName) GetPartition(partition int) string { if partition == 0 { return tn.baseName } return fmt.Sprintf("%v%v/%v", constants.ReservedTaskListPrefix, tn.baseName, partition) } func (tn *qualifiedTaskListName) init() error { if !strings.HasPrefix(tn.name, constants.ReservedTaskListPrefix) { return nil } suffixOff := strings.LastIndex(tn.name, "/") if suffixOff <= len(constants.ReservedTaskListPrefix) { return fmt.Errorf("invalid partitioned task list name %v", tn.name) } p, err := strconv.Atoi(tn.name[suffixOff+1:]) if err != nil || p <= 0 { return fmt.Errorf("invalid partitioned task list name %v", tn.name) } tn.partition = p tn.baseName = tn.name[len(constants.ReservedTaskListPrefix):suffixOff] return nil } // NewIdentifier returns identifier which uniquely identifies as task list func NewIdentifier( domainID string, taskListName string, taskType int, ) (*Identifier, error) { name, err := newTaskListName(taskListName) if err != nil { return nil, err } return &Identifier{ qualifiedTaskListName: name, domainID: domainID, taskType: taskType, }, nil } // GetDomainID returns the domain ID of the task list func (tid *Identifier) GetDomainID() string { return tid.domainID } // GetType returns the task type of the task list func (tid *Identifier) GetType() int { return tid.taskType } func (tid *Identifier) String() string { var b bytes.Buffer b.WriteString("[") b.WriteString("name=") b.WriteString(tid.name) b.WriteString("type=") if tid.taskType == persistence.TaskListTypeActivity { b.WriteString("activity") } else { b.WriteString("decision") } b.WriteString("]") return b.String() } ================================================ FILE: service/matching/tasklist/identifier_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "fmt" "strconv" "testing" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/persistence" ) func TestValidTaskListNames(t *testing.T) { testCases := []struct { input string baseName string partition int }{ {"0", "0", 0}, {"list0", "list0", 0}, {"/list0", "/list0", 0}, {"/list0/", "/list0/", 0}, {"__cadence_sys/list0", "__cadence_sys/list0", 0}, {"__cadence_sys/list0/", "__cadence_sys/list0/", 0}, {"/__cadence_sys_list0", "/__cadence_sys_list0", 0}, {"/__cadence_sys/list0/1", "list0", 1}, {"/__cadence_sys//list0//41", "/list0/", 41}, {"/__cadence_sys//__cadence_sys/sys/0/41", "/__cadence_sys/sys/0", 41}, } for _, tc := range testCases { t.Run(tc.input, func(t *testing.T) { tn, err := newTaskListName(tc.input) require.NoError(t, err) require.Equal(t, tc.partition, tn.partition) require.Equal(t, tc.partition == 0, tn.IsRoot()) require.Equal(t, tc.baseName, tn.baseName) require.Equal(t, tc.baseName, tn.GetRoot()) require.Equal(t, tc.input, tn.name) require.Equal(t, tc.input, tn.GetName()) // NewIdentifier should validate taskListName as well id, err := NewIdentifier("domain-name", tc.input, persistence.TaskListTypeActivity) require.NoError(t, err) require.Equal(t, "domain-name", id.GetDomainID()) require.Equal(t, persistence.TaskListTypeActivity, id.GetType()) }) } } func TestTaskListParentName(t *testing.T) { testCases := []struct { name string degree int output string }{ /* unexpected input */ {"list0", 0, ""}, /* 1-ary tree */ {"list0", 1, ""}, {"/__cadence_sys/list0/1", 1, "list0"}, {"/__cadence_sys/list0/2", 1, "/__cadence_sys/list0/1"}, /* 2-ary tree */ {"list0", 2, ""}, {"/__cadence_sys/list0/1", 2, "list0"}, {"/__cadence_sys/list0/2", 2, "list0"}, {"/__cadence_sys/list0/3", 2, "/__cadence_sys/list0/1"}, {"/__cadence_sys/list0/4", 2, "/__cadence_sys/list0/1"}, {"/__cadence_sys/list0/5", 2, "/__cadence_sys/list0/2"}, {"/__cadence_sys/list0/6", 2, "/__cadence_sys/list0/2"}, /* 3-ary tree */ {"/__cadence_sys/list0/1", 3, "list0"}, {"/__cadence_sys/list0/2", 3, "list0"}, {"/__cadence_sys/list0/3", 3, "list0"}, {"/__cadence_sys/list0/4", 3, "/__cadence_sys/list0/1"}, {"/__cadence_sys/list0/5", 3, "/__cadence_sys/list0/1"}, {"/__cadence_sys/list0/6", 3, "/__cadence_sys/list0/1"}, {"/__cadence_sys/list0/7", 3, "/__cadence_sys/list0/2"}, {"/__cadence_sys/list0/10", 3, "/__cadence_sys/list0/3"}, } for _, tc := range testCases { t.Run(tc.name+"#"+strconv.Itoa(tc.degree), func(t *testing.T) { tn, err := newTaskListName(tc.name) require.NoError(t, err) require.Equal(t, tc.output, tn.Parent(tc.degree)) }) } } func TestInvalidTasklistNames(t *testing.T) { inputs := []string{ "/__cadence_sys/", "/__cadence_sys/0", "/__cadence_sys//1", "/__cadence_sys//0", "/__cadence_sys/list0", "/__cadence_sys/list0/0", "/__cadence_sys/list0/-1", } for _, name := range inputs { t.Run(name, func(t *testing.T) { _, err := newTaskListName(name) require.Error(t, err) // NewIdentifier should validate taskListName as well _, err = NewIdentifier("domain-name", name, persistence.TaskListTypeActivity) require.Error(t, err) }) } } func TestTaskListIDToString(t *testing.T) { id, err := NewIdentifier("test-domain", "/tasklist/", persistence.TaskListTypeActivity) require.NoError(t, err) require.Equal(t, "[name=/tasklist/type=activity]", fmt.Sprint(id)) id, err = NewIdentifier("test-domain", "/tasklist/", persistence.TaskListTypeDecision) require.NoError(t, err) require.Equal(t, "[name=/tasklist/type=decision]", fmt.Sprint(id)) } ================================================ FILE: service/matching/tasklist/interfaces.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interfaces_mock.go github.com/uber/cadence/service/matching/tasklist Manager //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interfaces_mock.go github.com/uber/cadence/service/matching/tasklist TaskListRegistry //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interfaces_mock.go github.com/uber/cadence/service/matching/tasklist TaskMatcher //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interfaces_mock.go github.com/uber/cadence/service/matching/tasklist Forwarder //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interfaces_mock.go github.com/uber/cadence/service/matching/tasklist TaskCompleter //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interfaces_mock.go github.com/uber/cadence/service/matching/tasklist ShardProcessor package tasklist import ( "context" "time" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) type ( // TaskListRegistry is a registry of task list managers // it tracks all task list managers and provides a way to get them by identifier, domain ID, or task list name TaskListRegistry interface { // Register registers a manager for a given identifier. // we can override the manager for the same identifier if it is already registered // this case should be handled by the caller Register(id Identifier, mgr Manager) // Unregister unregisters a manager for a given identifier. // it returns true if the manager was unregistered, false if it was not found Unregister(mgr Manager) bool // AllManagers returns a list of all managers. AllManagers() []Manager ManagersByDomainID(domainID string) []Manager ManagersByTaskListName(name string) []Manager // ManagerByTaskListIdentifier returns a manager for a given identifier. // it returns the manager and true if it was found, false if it was not found ManagerByTaskListIdentifier(id Identifier) (Manager, bool) } Manager interface { Start(ctx context.Context) error Stop() // AddTask adds a task to the task list. This method will first attempt a synchronous // match with a poller. When that fails, task will be written to database and later // asynchronously matched with a poller AddTask(ctx context.Context, params AddTaskParams) (syncMatch bool, err error) // GetTask blocks waiting for a task Returns error when context deadline is exceeded // maxDispatchPerSecond is the max rate at which tasks are allowed to be dispatched // from this task list to pollers GetTask(ctx context.Context, maxDispatchPerSecond *float64) (*InternalTask, error) // DispatchTask dispatches a task to a poller. When there are no pollers to pick // up the task, this method will return error. Task will not be persisted to db DispatchTask(ctx context.Context, task *InternalTask) error // DispatchQueryTask will dispatch query to local or remote poller. If forwarded then result or error is returned, // if dispatched to local poller then nil and nil is returned. DispatchQueryTask(ctx context.Context, taskID string, request *types.MatchingQueryWorkflowRequest) (*types.MatchingQueryWorkflowResponse, error) CancelPoller(pollerID string) GetAllPollerInfo() []*types.PollerInfo HasPollerAfter(accessTime time.Time) bool // DescribeTaskList returns information about the target tasklist DescribeTaskList(includeTaskListStatus bool) *types.DescribeTaskListResponse String() string GetTaskListKind() types.TaskListKind TaskListID() *Identifier TaskListPartitionConfig() *types.TaskListPartitionConfig UpdateTaskListPartitionConfig(context.Context, *types.TaskListPartitionConfig) error RefreshTaskListPartitionConfig(context.Context, *types.TaskListPartitionConfig) error LoadBalancerHints() *types.LoadBalancerHints QueriesPerSecond() float64 ReleaseBlockedPollers() error } TaskMatcher interface { DisconnectBlockedPollers() Offer(ctx context.Context, task *InternalTask) (bool, error) OfferOrTimeout(ctx context.Context, startT time.Time, task *InternalTask) (bool, error) OfferQuery(ctx context.Context, task *InternalTask) (*types.MatchingQueryWorkflowResponse, error) MustOffer(ctx context.Context, task *InternalTask) error Poll(ctx context.Context, isolationGroup string) (*InternalTask, error) PollForQuery(ctx context.Context) (*InternalTask, error) RefreshCancelContext() } Forwarder interface { ForwardTask(ctx context.Context, task *InternalTask) error ForwardQueryTask(ctx context.Context, task *InternalTask) (*types.MatchingQueryWorkflowResponse, error) ForwardPoll(ctx context.Context) (*InternalTask, error) AddReqTokenC() <-chan *ForwarderReqToken PollReqTokenC() <-chan *ForwarderReqToken } TaskCompleter interface { CompleteTaskIfStarted(ctx context.Context, task *InternalTask) error } ShardProcessor interface { Start(ctx context.Context) error Stop() GetShardReport() executorclient.ShardReport SetShardStatus(types.ShardStatus) } ) ================================================ FILE: service/matching/tasklist/interfaces_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go // // Generated by this command: // // mockgen -package tasklist -source interfaces.go -destination interfaces_mock.go github.com/uber/cadence/service/matching/tasklist ShardProcessor // // Package tasklist is a generated GoMock package. package tasklist import ( context "context" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" executorclient "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) // MockTaskListRegistry is a mock of TaskListRegistry interface. type MockTaskListRegistry struct { ctrl *gomock.Controller recorder *MockTaskListRegistryMockRecorder isgomock struct{} } // MockTaskListRegistryMockRecorder is the mock recorder for MockTaskListRegistry. type MockTaskListRegistryMockRecorder struct { mock *MockTaskListRegistry } // NewMockTaskListRegistry creates a new mock instance. func NewMockTaskListRegistry(ctrl *gomock.Controller) *MockTaskListRegistry { mock := &MockTaskListRegistry{ctrl: ctrl} mock.recorder = &MockTaskListRegistryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskListRegistry) EXPECT() *MockTaskListRegistryMockRecorder { return m.recorder } // AllManagers mocks base method. func (m *MockTaskListRegistry) AllManagers() []Manager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AllManagers") ret0, _ := ret[0].([]Manager) return ret0 } // AllManagers indicates an expected call of AllManagers. func (mr *MockTaskListRegistryMockRecorder) AllManagers() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllManagers", reflect.TypeOf((*MockTaskListRegistry)(nil).AllManagers)) } // ManagerByTaskListIdentifier mocks base method. func (m *MockTaskListRegistry) ManagerByTaskListIdentifier(id Identifier) (Manager, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ManagerByTaskListIdentifier", id) ret0, _ := ret[0].(Manager) ret1, _ := ret[1].(bool) return ret0, ret1 } // ManagerByTaskListIdentifier indicates an expected call of ManagerByTaskListIdentifier. func (mr *MockTaskListRegistryMockRecorder) ManagerByTaskListIdentifier(id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ManagerByTaskListIdentifier", reflect.TypeOf((*MockTaskListRegistry)(nil).ManagerByTaskListIdentifier), id) } // ManagersByDomainID mocks base method. func (m *MockTaskListRegistry) ManagersByDomainID(domainID string) []Manager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ManagersByDomainID", domainID) ret0, _ := ret[0].([]Manager) return ret0 } // ManagersByDomainID indicates an expected call of ManagersByDomainID. func (mr *MockTaskListRegistryMockRecorder) ManagersByDomainID(domainID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ManagersByDomainID", reflect.TypeOf((*MockTaskListRegistry)(nil).ManagersByDomainID), domainID) } // ManagersByTaskListName mocks base method. func (m *MockTaskListRegistry) ManagersByTaskListName(name string) []Manager { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ManagersByTaskListName", name) ret0, _ := ret[0].([]Manager) return ret0 } // ManagersByTaskListName indicates an expected call of ManagersByTaskListName. func (mr *MockTaskListRegistryMockRecorder) ManagersByTaskListName(name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ManagersByTaskListName", reflect.TypeOf((*MockTaskListRegistry)(nil).ManagersByTaskListName), name) } // Register mocks base method. func (m *MockTaskListRegistry) Register(id Identifier, mgr Manager) { m.ctrl.T.Helper() m.ctrl.Call(m, "Register", id, mgr) } // Register indicates an expected call of Register. func (mr *MockTaskListRegistryMockRecorder) Register(id, mgr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockTaskListRegistry)(nil).Register), id, mgr) } // Unregister mocks base method. func (m *MockTaskListRegistry) Unregister(mgr Manager) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Unregister", mgr) ret0, _ := ret[0].(bool) return ret0 } // Unregister indicates an expected call of Unregister. func (mr *MockTaskListRegistryMockRecorder) Unregister(mgr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unregister", reflect.TypeOf((*MockTaskListRegistry)(nil).Unregister), mgr) } // MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller recorder *MockManagerMockRecorder isgomock struct{} } // MockManagerMockRecorder is the mock recorder for MockManager. type MockManagerMockRecorder struct { mock *MockManager } // NewMockManager creates a new mock instance. func NewMockManager(ctrl *gomock.Controller) *MockManager { mock := &MockManager{ctrl: ctrl} mock.recorder = &MockManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockManager) EXPECT() *MockManagerMockRecorder { return m.recorder } // AddTask mocks base method. func (m *MockManager) AddTask(ctx context.Context, params AddTaskParams) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddTask", ctx, params) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // AddTask indicates an expected call of AddTask. func (mr *MockManagerMockRecorder) AddTask(ctx, params any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTask", reflect.TypeOf((*MockManager)(nil).AddTask), ctx, params) } // CancelPoller mocks base method. func (m *MockManager) CancelPoller(pollerID string) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelPoller", pollerID) } // CancelPoller indicates an expected call of CancelPoller. func (mr *MockManagerMockRecorder) CancelPoller(pollerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelPoller", reflect.TypeOf((*MockManager)(nil).CancelPoller), pollerID) } // DescribeTaskList mocks base method. func (m *MockManager) DescribeTaskList(includeTaskListStatus bool) *types.DescribeTaskListResponse { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DescribeTaskList", includeTaskListStatus) ret0, _ := ret[0].(*types.DescribeTaskListResponse) return ret0 } // DescribeTaskList indicates an expected call of DescribeTaskList. func (mr *MockManagerMockRecorder) DescribeTaskList(includeTaskListStatus any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTaskList", reflect.TypeOf((*MockManager)(nil).DescribeTaskList), includeTaskListStatus) } // DispatchQueryTask mocks base method. func (m *MockManager) DispatchQueryTask(ctx context.Context, taskID string, request *types.MatchingQueryWorkflowRequest) (*types.MatchingQueryWorkflowResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DispatchQueryTask", ctx, taskID, request) ret0, _ := ret[0].(*types.MatchingQueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // DispatchQueryTask indicates an expected call of DispatchQueryTask. func (mr *MockManagerMockRecorder) DispatchQueryTask(ctx, taskID, request any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DispatchQueryTask", reflect.TypeOf((*MockManager)(nil).DispatchQueryTask), ctx, taskID, request) } // DispatchTask mocks base method. func (m *MockManager) DispatchTask(ctx context.Context, task *InternalTask) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DispatchTask", ctx, task) ret0, _ := ret[0].(error) return ret0 } // DispatchTask indicates an expected call of DispatchTask. func (mr *MockManagerMockRecorder) DispatchTask(ctx, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DispatchTask", reflect.TypeOf((*MockManager)(nil).DispatchTask), ctx, task) } // GetAllPollerInfo mocks base method. func (m *MockManager) GetAllPollerInfo() []*types.PollerInfo { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllPollerInfo") ret0, _ := ret[0].([]*types.PollerInfo) return ret0 } // GetAllPollerInfo indicates an expected call of GetAllPollerInfo. func (mr *MockManagerMockRecorder) GetAllPollerInfo() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllPollerInfo", reflect.TypeOf((*MockManager)(nil).GetAllPollerInfo)) } // GetTask mocks base method. func (m *MockManager) GetTask(ctx context.Context, maxDispatchPerSecond *float64) (*InternalTask, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTask", ctx, maxDispatchPerSecond) ret0, _ := ret[0].(*InternalTask) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTask indicates an expected call of GetTask. func (mr *MockManagerMockRecorder) GetTask(ctx, maxDispatchPerSecond any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTask", reflect.TypeOf((*MockManager)(nil).GetTask), ctx, maxDispatchPerSecond) } // GetTaskListKind mocks base method. func (m *MockManager) GetTaskListKind() types.TaskListKind { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTaskListKind") ret0, _ := ret[0].(types.TaskListKind) return ret0 } // GetTaskListKind indicates an expected call of GetTaskListKind. func (mr *MockManagerMockRecorder) GetTaskListKind() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTaskListKind", reflect.TypeOf((*MockManager)(nil).GetTaskListKind)) } // HasPollerAfter mocks base method. func (m *MockManager) HasPollerAfter(accessTime time.Time) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasPollerAfter", accessTime) ret0, _ := ret[0].(bool) return ret0 } // HasPollerAfter indicates an expected call of HasPollerAfter. func (mr *MockManagerMockRecorder) HasPollerAfter(accessTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasPollerAfter", reflect.TypeOf((*MockManager)(nil).HasPollerAfter), accessTime) } // LoadBalancerHints mocks base method. func (m *MockManager) LoadBalancerHints() *types.LoadBalancerHints { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LoadBalancerHints") ret0, _ := ret[0].(*types.LoadBalancerHints) return ret0 } // LoadBalancerHints indicates an expected call of LoadBalancerHints. func (mr *MockManagerMockRecorder) LoadBalancerHints() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadBalancerHints", reflect.TypeOf((*MockManager)(nil).LoadBalancerHints)) } // QueriesPerSecond mocks base method. func (m *MockManager) QueriesPerSecond() float64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueriesPerSecond") ret0, _ := ret[0].(float64) return ret0 } // QueriesPerSecond indicates an expected call of QueriesPerSecond. func (mr *MockManagerMockRecorder) QueriesPerSecond() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueriesPerSecond", reflect.TypeOf((*MockManager)(nil).QueriesPerSecond)) } // RefreshTaskListPartitionConfig mocks base method. func (m *MockManager) RefreshTaskListPartitionConfig(arg0 context.Context, arg1 *types.TaskListPartitionConfig) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RefreshTaskListPartitionConfig", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RefreshTaskListPartitionConfig indicates an expected call of RefreshTaskListPartitionConfig. func (mr *MockManagerMockRecorder) RefreshTaskListPartitionConfig(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTaskListPartitionConfig", reflect.TypeOf((*MockManager)(nil).RefreshTaskListPartitionConfig), arg0, arg1) } // ReleaseBlockedPollers mocks base method. func (m *MockManager) ReleaseBlockedPollers() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReleaseBlockedPollers") ret0, _ := ret[0].(error) return ret0 } // ReleaseBlockedPollers indicates an expected call of ReleaseBlockedPollers. func (mr *MockManagerMockRecorder) ReleaseBlockedPollers() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReleaseBlockedPollers", reflect.TypeOf((*MockManager)(nil).ReleaseBlockedPollers)) } // Start mocks base method. func (m *MockManager) Start(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start", ctx) ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockManagerMockRecorder) Start(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockManager)(nil).Start), ctx) } // Stop mocks base method. func (m *MockManager) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockManagerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockManager)(nil).Stop)) } // String mocks base method. func (m *MockManager) String() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "String") ret0, _ := ret[0].(string) return ret0 } // String indicates an expected call of String. func (mr *MockManagerMockRecorder) String() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockManager)(nil).String)) } // TaskListID mocks base method. func (m *MockManager) TaskListID() *Identifier { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TaskListID") ret0, _ := ret[0].(*Identifier) return ret0 } // TaskListID indicates an expected call of TaskListID. func (mr *MockManagerMockRecorder) TaskListID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskListID", reflect.TypeOf((*MockManager)(nil).TaskListID)) } // TaskListPartitionConfig mocks base method. func (m *MockManager) TaskListPartitionConfig() *types.TaskListPartitionConfig { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TaskListPartitionConfig") ret0, _ := ret[0].(*types.TaskListPartitionConfig) return ret0 } // TaskListPartitionConfig indicates an expected call of TaskListPartitionConfig. func (mr *MockManagerMockRecorder) TaskListPartitionConfig() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskListPartitionConfig", reflect.TypeOf((*MockManager)(nil).TaskListPartitionConfig)) } // UpdateTaskListPartitionConfig mocks base method. func (m *MockManager) UpdateTaskListPartitionConfig(arg0 context.Context, arg1 *types.TaskListPartitionConfig) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTaskListPartitionConfig", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // UpdateTaskListPartitionConfig indicates an expected call of UpdateTaskListPartitionConfig. func (mr *MockManagerMockRecorder) UpdateTaskListPartitionConfig(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTaskListPartitionConfig", reflect.TypeOf((*MockManager)(nil).UpdateTaskListPartitionConfig), arg0, arg1) } // MockTaskMatcher is a mock of TaskMatcher interface. type MockTaskMatcher struct { ctrl *gomock.Controller recorder *MockTaskMatcherMockRecorder isgomock struct{} } // MockTaskMatcherMockRecorder is the mock recorder for MockTaskMatcher. type MockTaskMatcherMockRecorder struct { mock *MockTaskMatcher } // NewMockTaskMatcher creates a new mock instance. func NewMockTaskMatcher(ctrl *gomock.Controller) *MockTaskMatcher { mock := &MockTaskMatcher{ctrl: ctrl} mock.recorder = &MockTaskMatcherMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskMatcher) EXPECT() *MockTaskMatcherMockRecorder { return m.recorder } // DisconnectBlockedPollers mocks base method. func (m *MockTaskMatcher) DisconnectBlockedPollers() { m.ctrl.T.Helper() m.ctrl.Call(m, "DisconnectBlockedPollers") } // DisconnectBlockedPollers indicates an expected call of DisconnectBlockedPollers. func (mr *MockTaskMatcherMockRecorder) DisconnectBlockedPollers() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisconnectBlockedPollers", reflect.TypeOf((*MockTaskMatcher)(nil).DisconnectBlockedPollers)) } // MustOffer mocks base method. func (m *MockTaskMatcher) MustOffer(ctx context.Context, task *InternalTask) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MustOffer", ctx, task) ret0, _ := ret[0].(error) return ret0 } // MustOffer indicates an expected call of MustOffer. func (mr *MockTaskMatcherMockRecorder) MustOffer(ctx, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MustOffer", reflect.TypeOf((*MockTaskMatcher)(nil).MustOffer), ctx, task) } // Offer mocks base method. func (m *MockTaskMatcher) Offer(ctx context.Context, task *InternalTask) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Offer", ctx, task) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // Offer indicates an expected call of Offer. func (mr *MockTaskMatcherMockRecorder) Offer(ctx, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Offer", reflect.TypeOf((*MockTaskMatcher)(nil).Offer), ctx, task) } // OfferOrTimeout mocks base method. func (m *MockTaskMatcher) OfferOrTimeout(ctx context.Context, startT time.Time, task *InternalTask) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OfferOrTimeout", ctx, startT, task) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // OfferOrTimeout indicates an expected call of OfferOrTimeout. func (mr *MockTaskMatcherMockRecorder) OfferOrTimeout(ctx, startT, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OfferOrTimeout", reflect.TypeOf((*MockTaskMatcher)(nil).OfferOrTimeout), ctx, startT, task) } // OfferQuery mocks base method. func (m *MockTaskMatcher) OfferQuery(ctx context.Context, task *InternalTask) (*types.MatchingQueryWorkflowResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OfferQuery", ctx, task) ret0, _ := ret[0].(*types.MatchingQueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // OfferQuery indicates an expected call of OfferQuery. func (mr *MockTaskMatcherMockRecorder) OfferQuery(ctx, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OfferQuery", reflect.TypeOf((*MockTaskMatcher)(nil).OfferQuery), ctx, task) } // Poll mocks base method. func (m *MockTaskMatcher) Poll(ctx context.Context, isolationGroup string) (*InternalTask, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Poll", ctx, isolationGroup) ret0, _ := ret[0].(*InternalTask) ret1, _ := ret[1].(error) return ret0, ret1 } // Poll indicates an expected call of Poll. func (mr *MockTaskMatcherMockRecorder) Poll(ctx, isolationGroup any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Poll", reflect.TypeOf((*MockTaskMatcher)(nil).Poll), ctx, isolationGroup) } // PollForQuery mocks base method. func (m *MockTaskMatcher) PollForQuery(ctx context.Context) (*InternalTask, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PollForQuery", ctx) ret0, _ := ret[0].(*InternalTask) ret1, _ := ret[1].(error) return ret0, ret1 } // PollForQuery indicates an expected call of PollForQuery. func (mr *MockTaskMatcherMockRecorder) PollForQuery(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollForQuery", reflect.TypeOf((*MockTaskMatcher)(nil).PollForQuery), ctx) } // RefreshCancelContext mocks base method. func (m *MockTaskMatcher) RefreshCancelContext() { m.ctrl.T.Helper() m.ctrl.Call(m, "RefreshCancelContext") } // RefreshCancelContext indicates an expected call of RefreshCancelContext. func (mr *MockTaskMatcherMockRecorder) RefreshCancelContext() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshCancelContext", reflect.TypeOf((*MockTaskMatcher)(nil).RefreshCancelContext)) } // MockForwarder is a mock of Forwarder interface. type MockForwarder struct { ctrl *gomock.Controller recorder *MockForwarderMockRecorder isgomock struct{} } // MockForwarderMockRecorder is the mock recorder for MockForwarder. type MockForwarderMockRecorder struct { mock *MockForwarder } // NewMockForwarder creates a new mock instance. func NewMockForwarder(ctrl *gomock.Controller) *MockForwarder { mock := &MockForwarder{ctrl: ctrl} mock.recorder = &MockForwarderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockForwarder) EXPECT() *MockForwarderMockRecorder { return m.recorder } // AddReqTokenC mocks base method. func (m *MockForwarder) AddReqTokenC() <-chan *ForwarderReqToken { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddReqTokenC") ret0, _ := ret[0].(<-chan *ForwarderReqToken) return ret0 } // AddReqTokenC indicates an expected call of AddReqTokenC. func (mr *MockForwarderMockRecorder) AddReqTokenC() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddReqTokenC", reflect.TypeOf((*MockForwarder)(nil).AddReqTokenC)) } // ForwardPoll mocks base method. func (m *MockForwarder) ForwardPoll(ctx context.Context) (*InternalTask, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ForwardPoll", ctx) ret0, _ := ret[0].(*InternalTask) ret1, _ := ret[1].(error) return ret0, ret1 } // ForwardPoll indicates an expected call of ForwardPoll. func (mr *MockForwarderMockRecorder) ForwardPoll(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForwardPoll", reflect.TypeOf((*MockForwarder)(nil).ForwardPoll), ctx) } // ForwardQueryTask mocks base method. func (m *MockForwarder) ForwardQueryTask(ctx context.Context, task *InternalTask) (*types.MatchingQueryWorkflowResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ForwardQueryTask", ctx, task) ret0, _ := ret[0].(*types.MatchingQueryWorkflowResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ForwardQueryTask indicates an expected call of ForwardQueryTask. func (mr *MockForwarderMockRecorder) ForwardQueryTask(ctx, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForwardQueryTask", reflect.TypeOf((*MockForwarder)(nil).ForwardQueryTask), ctx, task) } // ForwardTask mocks base method. func (m *MockForwarder) ForwardTask(ctx context.Context, task *InternalTask) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ForwardTask", ctx, task) ret0, _ := ret[0].(error) return ret0 } // ForwardTask indicates an expected call of ForwardTask. func (mr *MockForwarderMockRecorder) ForwardTask(ctx, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForwardTask", reflect.TypeOf((*MockForwarder)(nil).ForwardTask), ctx, task) } // PollReqTokenC mocks base method. func (m *MockForwarder) PollReqTokenC() <-chan *ForwarderReqToken { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PollReqTokenC") ret0, _ := ret[0].(<-chan *ForwarderReqToken) return ret0 } // PollReqTokenC indicates an expected call of PollReqTokenC. func (mr *MockForwarderMockRecorder) PollReqTokenC() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PollReqTokenC", reflect.TypeOf((*MockForwarder)(nil).PollReqTokenC)) } // MockTaskCompleter is a mock of TaskCompleter interface. type MockTaskCompleter struct { ctrl *gomock.Controller recorder *MockTaskCompleterMockRecorder isgomock struct{} } // MockTaskCompleterMockRecorder is the mock recorder for MockTaskCompleter. type MockTaskCompleterMockRecorder struct { mock *MockTaskCompleter } // NewMockTaskCompleter creates a new mock instance. func NewMockTaskCompleter(ctrl *gomock.Controller) *MockTaskCompleter { mock := &MockTaskCompleter{ctrl: ctrl} mock.recorder = &MockTaskCompleterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTaskCompleter) EXPECT() *MockTaskCompleterMockRecorder { return m.recorder } // CompleteTaskIfStarted mocks base method. func (m *MockTaskCompleter) CompleteTaskIfStarted(ctx context.Context, task *InternalTask) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CompleteTaskIfStarted", ctx, task) ret0, _ := ret[0].(error) return ret0 } // CompleteTaskIfStarted indicates an expected call of CompleteTaskIfStarted. func (mr *MockTaskCompleterMockRecorder) CompleteTaskIfStarted(ctx, task any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompleteTaskIfStarted", reflect.TypeOf((*MockTaskCompleter)(nil).CompleteTaskIfStarted), ctx, task) } // MockShardProcessor is a mock of ShardProcessor interface. type MockShardProcessor struct { ctrl *gomock.Controller recorder *MockShardProcessorMockRecorder isgomock struct{} } // MockShardProcessorMockRecorder is the mock recorder for MockShardProcessor. type MockShardProcessorMockRecorder struct { mock *MockShardProcessor } // NewMockShardProcessor creates a new mock instance. func NewMockShardProcessor(ctrl *gomock.Controller) *MockShardProcessor { mock := &MockShardProcessor{ctrl: ctrl} mock.recorder = &MockShardProcessorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShardProcessor) EXPECT() *MockShardProcessorMockRecorder { return m.recorder } // GetShardReport mocks base method. func (m *MockShardProcessor) GetShardReport() executorclient.ShardReport { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardReport") ret0, _ := ret[0].(executorclient.ShardReport) return ret0 } // GetShardReport indicates an expected call of GetShardReport. func (mr *MockShardProcessorMockRecorder) GetShardReport() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardReport", reflect.TypeOf((*MockShardProcessor)(nil).GetShardReport)) } // SetShardStatus mocks base method. func (m *MockShardProcessor) SetShardStatus(arg0 types.ShardStatus) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetShardStatus", arg0) } // SetShardStatus indicates an expected call of SetShardStatus. func (mr *MockShardProcessorMockRecorder) SetShardStatus(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetShardStatus", reflect.TypeOf((*MockShardProcessor)(nil).SetShardStatus), arg0) } // Start mocks base method. func (m *MockShardProcessor) Start(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start", ctx) ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockShardProcessorMockRecorder) Start(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockShardProcessor)(nil).Start), ctx) } // Stop mocks base method. func (m *MockShardProcessor) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockShardProcessorMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockShardProcessor)(nil).Stop)) } ================================================ FILE: service/matching/tasklist/isolation_balancer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "math" "slices" "golang.org/x/exp/maps" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" ) type ( isolationBalancer struct { timeSource clock.TimeSource scope metrics.Scope config *config.TaskListConfig groupState map[string]*isolationGroupState } isolationGroupState struct { underLoad clock.Sustain overLoad clock.Sustain noPollers clock.Sustain hasPollers clock.Sustain canAssignToPartitions bool } ) func newIsolationBalancer(timeSource clock.TimeSource, scope metrics.Scope, config *config.TaskListConfig) *isolationBalancer { return &isolationBalancer{ timeSource: timeSource, scope: scope, config: config, groupState: make(map[string]*isolationGroupState), } } func (i *isolationBalancer) adjustWritePartitions(metrics *aggregatePartitionMetrics, writePartitions map[int]*types.TaskListPartition) (map[int]*types.TaskListPartition, bool) { i.refreshGroups(metrics, writePartitions) partitionCount := i.getPartitionsPerGroup(writePartitions) partitionGoal := i.calculatePartitionGoal(metrics, partitionCount, len(writePartitions)) // If there's a single partition we can't actually change anything, but we still want to run the above calculations // so that we record the correct sustain values. That way when a new partition is added we can immediately respond // with the correct number of pollers if len(writePartitions) == 1 { // If we resized to 1 partition, remove all assignments from that partition and clear all group status if len(writePartitions[0].IsolationGroups) != 0 { i.reset() return map[int]*types.TaskListPartition{ 0: {}, }, true } return writePartitions, false } return i.applyGroupChanges(metrics, partitionCount, partitionGoal, writePartitions) } func (i *isolationBalancer) reset() { maps.Clear(i.groupState) } func (i *isolationBalancer) refreshGroups(metrics *aggregatePartitionMetrics, writePartitions map[int]*types.TaskListPartition) { polledOrAssigned := make(map[string]bool) // Track it for resizing/removal for _, p := range writePartitions { for _, group := range p.IsolationGroups { polledOrAssigned[group] = true if _, ok := i.groupState[group]; !ok { i.groupState[group] = i.createIsolationGroupState(true) } } } // Track it as we might assign it if they maintain pollers for group := range metrics.hasPollersByIsolationGroup { polledOrAssigned[group] = true if _, ok := i.groupState[group]; !ok { i.groupState[group] = i.createIsolationGroupState(false) } } for group := range i.groupState { // No pollers and no write partitions means we don't need to track it if !polledOrAssigned[group] { delete(i.groupState, group) continue } } } func (i *isolationBalancer) createIsolationGroupState(assignToPartitions bool) *isolationGroupState { return &isolationGroupState{ underLoad: clock.NewSustain(i.timeSource, i.config.IsolationGroupDownscaleSustainedDuration), overLoad: clock.NewSustain(i.timeSource, i.config.IsolationGroupUpscaleSustainedDuration), noPollers: clock.NewSustain(i.timeSource, i.config.IsolationGroupNoPollersSustainedDuration), hasPollers: clock.NewSustain(i.timeSource, i.config.IsolationGroupHasPollersSustainedDuration), canAssignToPartitions: assignToPartitions, } } func (i *isolationBalancer) getPartitionsPerGroup(writePartitions map[int]*types.TaskListPartition) map[string]int { partitionCount := make(map[string]int) for _, p := range writePartitions { for _, group := range p.IsolationGroups { partitionCount[group]++ } } return partitionCount } func (i *isolationBalancer) calculatePartitionGoal(aggregateMetrics *aggregatePartitionMetrics, partitionCountByGroup map[string]int, writePartitionCount int) map[string]int { partitionGoal := make(map[string]int) assignableGroups := 0 // Identify groups that have switched to assignable and count them. This impacts the minimum partitions per group for group, state := range i.groupState { groupHasPollers := aggregateMetrics.hasPollersByIsolationGroup[group] sustainedPollers := state.hasPollers.CheckAndReset(groupHasPollers) sustainedNoPollers := state.noPollers.CheckAndReset(!groupHasPollers) if state.canAssignToPartitions { if sustainedNoPollers { i.scope.Tagged(metrics.IsolationGroupTag(group)).IncCounter(metrics.IsolationGroupStoppedPolling) state.canAssignToPartitions = false } else { assignableGroups++ } } else if sustainedPollers { i.scope.Tagged(metrics.IsolationGroupTag(group)).IncCounter(metrics.IsolationGroupStartedPolling) state.canAssignToPartitions = true state.overLoad.Reset() state.underLoad.Reset() assignableGroups++ } } minPartitions := i.getMinPartitionsPerGroup(writePartitionCount, assignableGroups) for group, state := range i.groupState { // Remove all assignments from inactive groups if !state.canAssignToPartitions { partitionGoal[group] = 0 i.scope.Tagged(metrics.IsolationGroupTag(group)).UpdateGauge(metrics.IsolationGroupPartitionsGauge, 0) continue } currentPartitions := partitionCountByGroup[group] groupQPS := aggregateMetrics.qpsByIsolationGroup[group] upscaleRps := float64(i.config.PartitionUpscaleRPS()) / float64(i.config.IsolationGroupsPerPartition()) downscaleFactor := i.config.PartitionDownscaleFactor() upscaleThreshold := float64(max(currentPartitions, 1)) * upscaleRps downscaleThreshold := float64(max(currentPartitions-1, 0)) * upscaleRps * downscaleFactor sustainedOverLoad := state.overLoad.Check(groupQPS > upscaleThreshold) sustainedUnderLoad := state.underLoad.Check(groupQPS < downscaleThreshold) idealPartitions := getNumberOfPartitions(groupQPS, upscaleRps) // partitions must be >= minPartitions and <= writePartitionCount targetPartitions := min(max(minPartitions, idealPartitions), writePartitionCount) // We may have a sustained overload but already be at the max number of partitions. Only reset the sustain // if we can actually make changes. This allows for instantly scaling up if the number of write partitions // changes // If currentPartitions == 0 then we just scaled up from not having pollers. Allow for more than the minimum if (sustainedOverLoad || sustainedUnderLoad || currentPartitions == 0) && targetPartitions != currentPartitions { if sustainedOverLoad { i.scope.Tagged(metrics.IsolationGroupTag(group)).IncCounter(metrics.IsolationGroupUpscale) } else if sustainedUnderLoad { i.scope.Tagged(metrics.IsolationGroupTag(group)).IncCounter(metrics.IsolationGroupDownscale) } state.overLoad.Reset() state.underLoad.Reset() partitionGoal[group] = targetPartitions } else { partitionGoal[group] = max(currentPartitions, minPartitions) } i.scope.Tagged(metrics.IsolationGroupTag(group)).UpdateGauge(metrics.IsolationGroupPartitionsGauge, float64(partitionGoal[group])) } return partitionGoal } func (i *isolationBalancer) applyGroupChanges(m *aggregatePartitionMetrics, partitionCount, partitionGoal map[string]int, partitions map[int]*types.TaskListPartition) (map[int]*types.TaskListPartition, bool) { groupSizePerPartition := make(map[string]float64) var toRemove []string var toAdd []string assignableGroups := 0 for group, state := range i.groupState { if state.canAssignToPartitions { assignableGroups++ } change := partitionGoal[group] - partitionCount[group] goalPartitions := partitionGoal[group] if goalPartitions > 0 { groupSizePerPartition[group] = m.qpsByIsolationGroup[group] / float64(goalPartitions) } for j := 0; j < change; j++ { toAdd = append(toAdd, group) } for j := change; j < 0; j++ { toRemove = append(toRemove, group) } } // partition id to set of groups partitionGroups := make(map[int]map[string]any) for id, partition := range partitions { set := make(map[string]any) for _, group := range partition.IsolationGroups { set[group] = true } partitionGroups[id] = set } removePartitions(toRemove, groupSizePerPartition, partitionGroups) addPartitions(toAdd, groupSizePerPartition, partitionGroups) minimumGroups := i.getMinGroupsPerPartition(assignableGroups) movedGroupBetweenPartitions := ensureMinimumGroupsPerPartition(minimumGroups, groupSizePerPartition, partitionGroups) result := make(map[int]*types.TaskListPartition) for id, groups := range partitionGroups { if len(groups) == 0 { result[id] = &types.TaskListPartition{} } else { asSlice := maps.Keys(groups) // Sort for the sake of stability slices.Sort(asSlice) result[id] = &types.TaskListPartition{IsolationGroups: asSlice} } } return result, movedGroupBetweenPartitions || len(toRemove) > 0 || len(toAdd) > 0 } func (i *isolationBalancer) getMinPartitionsPerGroup(writePartitionCount, assignableGroups int) int { if assignableGroups == 0 { return 0 } return int(math.Ceil(float64(writePartitionCount*i.getMinGroupsPerPartition(assignableGroups)) / float64(assignableGroups))) } func (i *isolationBalancer) getMinGroupsPerPartition(assignableGroups int) int { return min(i.config.IsolationGroupsPerPartition(), assignableGroups) } func removePartitions(toRemove []string, groupSizePerPartition map[string]float64, groupsByPartitionID map[int]map[string]any) { // Use a greedy approach to remove the specified groups (which may include duplicates) from the largest partition // Similarly to adding partitions we sort the input by size first slices.SortFunc(toRemove, byValueDescending(groupSizePerPartition)) for _, group := range toRemove { partitionID, ok := findMaxWithGroup(group, groupSizePerPartition, groupsByPartitionID) if ok { delete(groupsByPartitionID[partitionID], group) } } } func addPartitions(toAdd []string, groupSizePerPartition map[string]float64, groupsByPartitionID map[int]map[string]any) { // Use greedy number partitioning to add the group to the smallest partition where it isn't already present. This // is an approximate solution. Sorting the input by size in a descending manner should result in a more even // distribution. slices.SortFunc(toAdd, byValueDescending(groupSizePerPartition)) for _, group := range toAdd { partitionID, ok := findMinWithoutGroup(group, groupSizePerPartition, groupsByPartitionID) if ok { groupsByPartitionID[partitionID][group] = true } } } func ensureMinimumGroupsPerPartition(minGroups int, groupSizePerPartition map[string]float64, groupsByPartitionID map[int]map[string]any) bool { // Every partition needs to be assigned groups that have pollers, otherwise we're unable to process tasks on those // partitions. // // Sort the partitions by the number of isolation groups assigned to them, and then use a two-pointers approach // to iteratively take groups from partitions that have the most and move them to a partition that has the least. // calculatePartitionGoal enforces that there are at least "getMinPartitionsPerGroup" instances of each group, // which in turn ensures that every partition can be assigned "getMinGroupsPerPartition" by only moving groups // around. // // The specific groups that we move between partitions isn't particularly significant, since the size of each group // within a partition has an inherent upper-bound before we scale up. Ideally we keep partitions relatively close // in size, so we use a simple heuristic of minimizing the difference in total size between the two partitions that // we're considering. changed := false partitionIDsByGroupCount := maps.Keys(groupsByPartitionID) slices.SortFunc(partitionIDsByGroupCount, func(a, b int) int { aLen := len(groupsByPartitionID[a]) bLen := len(groupsByPartitionID[b]) if aLen < bLen { return -1 } else if aLen == bLen { // Fall back to size ascending. Larger is later to prioritize moving groups from those partitions aSize := getPartitionSize(groupSizePerPartition, groupsByPartitionID[a]) bSize := getPartitionSize(groupSizePerPartition, groupsByPartitionID[b]) if aSize < bSize { return -1 } else if aSize == bSize { return 0 } else { return 1 } } else { return 1 } }) lo, hi := 0, len(partitionIDsByGroupCount)-1 for lo < hi { loPartition := groupsByPartitionID[partitionIDsByGroupCount[lo]] hiPartition := groupsByPartitionID[partitionIDsByGroupCount[hi]] if len(loPartition) >= minGroups { lo++ continue } if len(hiPartition) <= minGroups { hi-- continue } changed = true needed := minGroups - len(loPartition) available := len(hiPartition) - minGroups movePartitions(min(needed, available), hiPartition, loPartition, groupSizePerPartition) } return changed } func movePartitions(amount int, from, to map[string]any, groupSizePerPartition map[string]float64) { fromSize := getPartitionSize(groupSizePerPartition, from) toSize := getPartitionSize(groupSizePerPartition, to) // This is non-optimal when amount > 1 but it's good enough // Solving it ideally is number partitioning problem, exactly what we're trying to solve across all partitions for range amount { best := math.MaxFloat64 bestGroup := "" for group := range from { if _, ok := to[group]; !ok { groupSize := groupSizePerPartition[group] diff := math.Abs(fromSize - toSize - (2 * groupSize)) if diff < best { best = diff bestGroup = group } } } delete(from, bestGroup) fromSize -= groupSizePerPartition[bestGroup] toSize += groupSizePerPartition[bestGroup] to[bestGroup] = true } } func findMaxWithGroup(group string, groupSizePerPartition map[string]float64, groupsByPartitionID map[int]map[string]any) (int, bool) { maxSize := -math.MaxFloat64 maxPartition := -1 // iterate in order of partitionID to make decisions deterministic for partitionID := 0; partitionID < len(groupsByPartitionID); partitionID++ { set := groupsByPartitionID[partitionID] if _, ok := set[group]; ok { partitionSize := getPartitionSize(groupSizePerPartition, set) if maxSize < partitionSize { maxSize = partitionSize maxPartition = partitionID } } } return maxPartition, maxPartition != -1 } func findMinWithoutGroup(group string, groupSizePerPartition map[string]float64, groupsByPartitionID map[int]map[string]any) (int, bool) { minSize := math.MaxFloat64 minPartition := -1 // iterate in order of partitionID to make decisions deterministic for partitionID := 0; partitionID < len(groupsByPartitionID); partitionID++ { set := groupsByPartitionID[partitionID] if _, ok := set[group]; !ok { partitionSize := getPartitionSize(groupSizePerPartition, set) if partitionSize < minSize { minSize = partitionSize minPartition = partitionID } } } return minPartition, minPartition != -1 } func getPartitionSize(groupSizePerPartition map[string]float64, groupSet map[string]any) float64 { total := float64(0) for group := range groupSet { total += groupSizePerPartition[group] } return total } func byValueDescending[T comparable](m map[T]float64) func(a, b T) int { return func(a, b T) int { aVal := m[a] bVal := m[b] if aVal == bVal { return 0 } else if aVal > bVal { return -1 } else { return 1 } } } ================================================ FILE: service/matching/tasklist/isolation_balancer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/clock" commonConfig "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" ) const cycleInterval = time.Second * 15 type testCycle struct { // If not specified, defaults to the previous cycle's value metrics *aggregatePartitionMetrics // If not specified, defaults to the previous cycle's value partitions map[int]*types.TaskListPartition // If not specified then there should be no change this cycle. The returned value should be equal to partitions // and changed should be false expected map[int]*types.TaskListPartition callback func(dynamicClient dynamicconfig.Client) } func TestAdjustWritePartitions(t *testing.T) { cases := []struct { name string groupsPerPartition int cycles []*testCycle }{ { name: "initial balance", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, }, }, { name: "initial balance - from one partition", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {}, }, }, {}, {}, {}, { partitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, }, }, { name: "initial balance - from one partition skewed", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 300, "b": 75, "c": 2, "d": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {}, }, }, {}, {}, {}, { partitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "c", "d"}}, }, }, }, }, { name: "one partition minimum", groupsPerPartition: 1, cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a"}}, 1: {IsolationGroups: []string{"b"}}, }, }, }, }, { name: "require pollers", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a"}}, 1: {IsolationGroups: []string{"a"}}, }, }, }, }, { name: "fewer groups than partitions", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, "c": 98, "d": 97, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, 2: {}, 3: {}, 4: {}, 5: {}, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "d"}}, 1: {IsolationGroups: []string{"a", "d"}}, 2: {IsolationGroups: []string{"a", "d"}}, 3: {IsolationGroups: []string{"b", "c"}}, 4: {IsolationGroups: []string{"b", "c"}}, 5: {IsolationGroups: []string{"b", "c"}}, }, }, }, }, { name: "fewer partitions than groups", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, "c": 98, "d": 97, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "d"}}, 1: {IsolationGroups: []string{"b", "c"}}, }, }, }, }, { name: "fewer active groups than IsolationGroupsPerPartition", groupsPerPartition: 4, cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, }, }, { name: "single partition", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {}, }, }, {}, {}, {}, {}, }, }, { name: "single group", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {}, 1: {}, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a"}}, 1: {IsolationGroups: []string{"a"}}, }, }, }, }, { name: "pollers added", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, }, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "c"}}, 1: {IsolationGroups: []string{"a", "b", "c"}}, }, }, }, }, { name: "pollers removed", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "c"}}, 1: {IsolationGroups: []string{"a", "b", "c"}}, }, }, { metrics: &aggregatePartitionMetrics{ totalQPS: 1000, qpsByIsolationGroup: map[string]float64{ "a": 101, "b": 100, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, }, }, { name: "scale up - single partition", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, "c": 98, "d": 97, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "d"}}, 1: {IsolationGroups: []string{"b", "c"}}, }, }, { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 101, "b": 99, "c": 98, "d": 97, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "d"}}, 1: {IsolationGroups: []string{"a", "b", "c"}}, }, }, }, }, { name: "scale up - multiple partition", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 201, "b": 99, "c": 98, "d": 97, "e": 15, "f": 10, "g": 5, "h": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "h": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "h"}}, // 202 1: {IsolationGroups: []string{"b", "g"}}, // 104 2: {IsolationGroups: []string{"c", "f"}}, // 108 3: {IsolationGroups: []string{"d", "e"}}, // 112 }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "h"}}, // 68 1: {IsolationGroups: []string{"a", "b", "g"}}, // 171 2: {IsolationGroups: []string{"a", "c", "f"}}, // 175 3: {IsolationGroups: []string{"d", "e"}}, // 112 }, }, }, }, { name: "scale up - multiple groups", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 102, "b": 101, "c": 98, "d": 97, "e": 15, "f": 10, "g": 5, "h": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "h": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "h"}}, // 103 1: {IsolationGroups: []string{"b", "g"}}, // 106 2: {IsolationGroups: []string{"c", "f"}}, // 108 3: {IsolationGroups: []string{"d", "e"}}, // 112 }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ // Kind of a weird case as 0 and 1 (which have the groups that need to be split) // also happen to be the smallest groups. 0: {IsolationGroups: []string{"a", "b", "h"}}, // 103 1: {IsolationGroups: []string{"a", "b", "g"}}, // 106 2: {IsolationGroups: []string{"c", "f"}}, // 108 3: {IsolationGroups: []string{"d", "e"}}, // 112 }, }, }, }, { name: "immediate scale up on new partition", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 303, "b": 99, "c": 98, "d": 97, "e": 15, "f": 10, "g": 5, "h": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "h": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "g", "h"}}, // 206 1: {IsolationGroups: []string{"a", "c", "f"}}, // 209 2: {IsolationGroups: []string{"a", "d", "e"}}, // 213 }, }, {}, {}, {}, {}, // If we had the space "a" would have scaled up on this cycle { partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "g", "h"}}, 1: {IsolationGroups: []string{"a", "c", "f"}}, 2: {IsolationGroups: []string{"a", "d", "e"}}, // adaptive scaler gave us another partition 3: {}, }, expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "g", "h"}}, // 81.75 1: {IsolationGroups: []string{"a", "c", "f"}}, // 183.75 2: {IsolationGroups: []string{"a", "d", "e"}}, // 187.75 // a is scaled up and b is taken from group 0 because it has the most isolation groups // and minimizes the difference in size between the two groups (93 for b vs 96 for g vs 103 for h) 3: {IsolationGroups: []string{"a", "b"}}, // 174.75 }, }, }, }, { name: "immediate scale up on new partition - multiple", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 303, "b": 303, "c": 98, "d": 97, "e": 15, "f": 10, "g": 5, "h": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "h": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "c"}}, // 300 1: {IsolationGroups: []string{"a", "b", "d"}}, // 299 2: {IsolationGroups: []string{"a", "b", "e", "f", "g", "h"}}, // 233 }, }, {}, {}, {}, {}, // If we had the space "a" and "b" would have scaled up on this cycle { partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "c"}}, 1: {IsolationGroups: []string{"a", "b", "d"}}, 2: {IsolationGroups: []string{"a", "b", "e", "f", "g", "h"}}, // adaptive scaler gave us another partition 3: {}, }, expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "c"}}, 1: {IsolationGroups: []string{"a", "b", "d"}}, 2: {IsolationGroups: []string{"a", "b", "e", "f", "g", "h"}}, 3: {IsolationGroups: []string{"a", "b"}}, }, }, }, }, { name: "scale down - single partition", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 75, "b": 99, "c": 98, "d": 97, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "c"}}, 1: {IsolationGroups: []string{"a", "b", "d"}}, }, }, { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 74, "b": 99, "c": 98, "d": 97, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "c"}}, 1: {IsolationGroups: []string{"b", "d"}}, }, }, }, }, { name: "scale down - multiple partition", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 74, "b": 99, "c": 98, "d": 97, "e": 15, "f": 10, "g": 5, "h": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "h": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "h"}}, // 25 1: {IsolationGroups: []string{"a", "b", "g"}}, // 129 2: {IsolationGroups: []string{"a", "c", "f"}}, // 132 3: {IsolationGroups: []string{"d", "e"}}, // 112 }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "h"}}, // 25 1: {IsolationGroups: []string{"b", "g"}}, // 105 2: {IsolationGroups: []string{"c", "f"}}, // 108 3: {IsolationGroups: []string{"d", "e"}}, // 112 }, }, }, }, { name: "scale down - multiple groups", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 74, "b": 73, "c": 98, "d": 97, "e": 15, "f": 10, "g": 5, "h": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "h": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "h"}}, // ~49 1: {IsolationGroups: []string{"a", "b", "g"}}, // ~54 2: {IsolationGroups: []string{"c", "f"}}, // 108 3: {IsolationGroups: []string{"d", "e"}}, // 112 }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "h"}}, 1: {IsolationGroups: []string{"b", "g"}}, 2: {IsolationGroups: []string{"c", "f"}}, 3: {IsolationGroups: []string{"d", "e"}}, }, }, }, }, { name: "scale down - single partition left", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 74, "b": 73, "c": 98, "d": 97, "e": 15, "f": 10, "g": 5, "h": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, "e": true, "f": true, "g": true, "h": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "h"}}, }, expected: map[int]*types.TaskListPartition{ 0: {}, }, }, }, }, { name: "scale up then down", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, "c": 98, "d": 97, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "d"}}, 1: {IsolationGroups: []string{"b", "c"}}, }, }, { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 101, "b": 99, "c": 98, "d": 97, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "d"}}, 1: {IsolationGroups: []string{"a", "b", "c"}}, }, }, { partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "d"}}, 1: {IsolationGroups: []string{"a", "b", "c"}}, }, metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 199, "b": 99, "c": 98, "d": 97, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, }, { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 74, "b": 99, "c": 98, "d": 97, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, "c": true, "d": true, }, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "d"}}, 1: {IsolationGroups: []string{"b", "c"}}, }, }, }, }, { name: "poller added then removed", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a"}}, 1: {IsolationGroups: []string{"a"}}, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, { partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 100, "b": 99, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, }, }, }, {}, {}, {}, { expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a"}}, 1: {IsolationGroups: []string{"a"}}, }, }, { partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a"}}, 1: {IsolationGroups: []string{"a"}}, }, }, }, }, { name: "ensure minimum partitions", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 2, "b": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a"}}, 1: {IsolationGroups: []string{"b"}}, }, expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, }, }, { name: "increase minimum partitions", groupsPerPartition: 1, cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 2, "b": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a"}}, 1: {IsolationGroups: []string{"b"}}, }, }, { callback: func(dynamicClient dynamicconfig.Client) { require.NoError(t, dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupsPerPartition, 2)) }, expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, }, }, { name: "decrease minimum partitions", groupsPerPartition: 2, cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 2, "b": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a"}}, 1: {IsolationGroups: []string{"b"}}, }, expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, { callback: func(dynamicClient dynamicconfig.Client) { require.NoError(t, dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupsPerPartition, 1)) }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, {}, {}, {}, { // Has to go through a normal scale down operation expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"b"}}, 1: {IsolationGroups: []string{"a"}}, }, }, }, }, { name: "new partition - increase partitionsPerGroup", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 2, "b": 1, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, }, }, { partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, 2: {}, }, // adaptive scaler gave us a new partition and even though we don't think these groups need more // partitions, we need to ensure every partition is assigned groups expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, 1: {IsolationGroups: []string{"a", "b"}}, 2: {IsolationGroups: []string{"a", "b"}}, }, }, }, }, { name: "new partition - move groups", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 99, "b": 1, "c": 2, "d": 3, "e": 4, "f": 5, "g": 6, "h": 7, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, // Even though the others aren't polled, they're still active because they're assigned // to a partition and we haven't been missing pollers long enough to remove them }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, // 100 1: {IsolationGroups: []string{"c", "d", "e", "f", "g", "h"}}, // 27 }, }, { partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, // 100 1: {IsolationGroups: []string{"c", "d", "e", "f", "g", "h"}}, // 27 2: {}, }, expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b"}}, // 100 1: {IsolationGroups: []string{"c", "d", "e", "f"}}, // 13 2: {IsolationGroups: []string{"g", "h"}}, // 14 }, }, }, }, { name: "new partition - move groups from multiple partitions", cycles: []*testCycle{ { metrics: &aggregatePartitionMetrics{ qpsByIsolationGroup: map[string]float64{ "a": 99, "b": 1, "c": 2, "d": 3, "e": 4, "f": 5, }, hasPollersByIsolationGroup: map[string]bool{ "a": true, "b": true, }, }, partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "c"}}, // 103 1: {IsolationGroups: []string{"d", "e", "f"}}, // 12 }, }, { partitions: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"a", "b", "c"}}, // 103 1: {IsolationGroups: []string{"d", "e", "f"}}, // 12 2: {}, }, expected: map[int]*types.TaskListPartition{ 0: {IsolationGroups: []string{"b", "c"}}, // 3 1: {IsolationGroups: []string{"e", "f"}}, // 9 2: {IsolationGroups: []string{"a", "d"}}, // 102 }, }, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { cycles := tc.cycles dynamicClient := dynamicconfig.NewInMemoryClient() tcConfig := isolationConfig(t, dynamicClient, tc.groupsPerPartition) mockTime := clock.NewMockedTimeSource() balancer := newIsolationBalancer(mockTime, metrics.NoopScope, tcConfig) var prevMetrics *aggregatePartitionMetrics var prevPartitions map[int]*types.TaskListPartition for i, cycle := range cycles { mockTime.Advance(cycleInterval) cycleMetrics := cycle.metrics if cycleMetrics == nil { cycleMetrics = prevMetrics } cyclePartitions := cycle.partitions if cyclePartitions == nil { cyclePartitions = prevPartitions } if len(cyclePartitions) == 0 || cycleMetrics == nil { assert.Fail(t, "invalid cycle configuration at cycle %d", i) return } if cycle.callback != nil { cycle.callback(dynamicClient) } actual, actualChanged := balancer.adjustWritePartitions(cycleMetrics, cyclePartitions) if cycle.expected != nil { assert.True(t, actualChanged, "cycle %d - expected change", i) assert.Equal(t, cycle.expected, actual, "cycle %d - did not match expected", i) } else { assert.False(t, actualChanged, "cycle %d - expected no change", i) assert.Equal(t, cyclePartitions, actual, "cycle %d - was unexpectedly changed", i) } prevMetrics = cycleMetrics prevPartitions = cyclePartitions } }) } } func isolationConfig(t *testing.T, dynamicClient dynamicconfig.Client, groupsPerPartition int) *config.TaskListConfig { if groupsPerPartition == 0 { groupsPerPartition = 2 } taskListID, err := NewIdentifier("test-domain-id", "test-task-list", 0) require.NoError(t, err) logger := testlogger.New(t) cfg := newTaskListConfig(taskListID, config.NewConfig(dynamicconfig.NewCollection(dynamicClient, logger), "test-host", commonConfig.RPC{}, func() []string { return nil }), "test-domain") require.NoError(t, dynamicClient.UpdateValue(dynamicproperties.MatchingPartitionUpscaleRPS, 200)) require.NoError(t, dynamicClient.UpdateValue(dynamicproperties.MatchingPartitionDownscaleFactor, 0.75)) require.NoError(t, dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupsPerPartition, groupsPerPartition)) require.NoError(t, dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupUpscaleSustainedDuration, cycleInterval*4)) require.NoError(t, dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupDownscaleSustainedDuration, cycleInterval*4)) require.NoError(t, dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupHasPollersSustainedDuration, cycleInterval*4)) require.NoError(t, dynamicClient.UpdateValue(dynamicproperties.MatchingIsolationGroupNoPollersSustainedDuration, cycleInterval*4)) return cfg } ================================================ FILE: service/matching/tasklist/matcher.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "context" "errors" "fmt" "sync" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/ctxutils" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/event" ) // taskMatcherImpl matches a task producer with a task consumer // Producers are usually rpc calls from history or taskReader // that drains backlog from db. Consumers are the task list pollers type taskMatcherImpl struct { log log.Logger // synchronous task channel to match producer/consumer for any isolation group // tasks having no isolation requirement are added to this channel // and pollers from all isolation groups read from this channel taskC chan *InternalTask // synchronos task channels to match producer/consumer for a certain isolation group // the key is the name of the isolation group isolatedTaskC map[string]chan *InternalTask // synchronous task channel to match query task - the reason to have // separate channel for this is because there are cases when consumers // are interested in queryTasks but not others. Example is when domain is // not active in a cluster queryTaskC chan *InternalTask // ratelimiter that limits the rate at which tasks can be dispatched to consumers limiter quotas.Limiter fwdr Forwarder scope metrics.Scope // domain metric scope config *config.TaskListConfig cancelCtx context.Context // used to cancel long polling cancelFunc context.CancelFunc cancelLock sync.Mutex tasklist *Identifier tasklistKind types.TaskListKind numReadPartitionsFn func(*config.TaskListConfig) int } // ErrTasklistThrottled implies a tasklist was throttled var ErrTasklistThrottled = errors.New("tasklist limit exceeded") // newTaskMatcher returns a task matcher instance. The returned instance can be // used by task producers and consumers to find a match. Both sync matches and non-sync // matches should use this implementation func newTaskMatcher( config *config.TaskListConfig, fwdr Forwarder, scope metrics.Scope, isolationGroups []string, log log.Logger, tasklist *Identifier, tasklistKind types.TaskListKind, limiter quotas.Limiter, ) TaskMatcher { isolatedTaskC := make(map[string]chan *InternalTask) for _, g := range isolationGroups { isolatedTaskC[g] = make(chan *InternalTask) } cancelCtx, cancelFunc := context.WithCancel(context.Background()) matcher := &taskMatcherImpl{ log: log, scope: scope, fwdr: fwdr, taskC: make(chan *InternalTask), isolatedTaskC: isolatedTaskC, queryTaskC: make(chan *InternalTask), config: config, tasklist: tasklist, tasklistKind: tasklistKind, limiter: limiter, cancelCtx: cancelCtx, cancelFunc: cancelFunc, } return matcher } // DisconnectBlockedPollers gradually disconnects pollers which are blocked on long polling func (tm *taskMatcherImpl) DisconnectBlockedPollers() { tm.cancelFunc() } // Offer offers a task to a potential consumer (poller) // If the task is successfully matched with a consumer, this // method will return true and no error. If the task is matched // but consumer returned error, then this method will return // true and error message. This method should not be used for query // task. This method should ONLY be used for sync match. // // When a local poller is not available and forwarding to a parent // task list partition is possible, this method will attempt forwarding // to the parent partition. // // Cases when this method will block: // // Ratelimit: // When a ratelimit token is not available, this method might block // waiting for a token until the provided context timeout. Rate limits are // not enforced for forwarded tasks from child partition. // // Forwarded tasks that originated from db backlog: // When this method is called with a task that is forwarded from a // remote partition and if (1) this task list is root (2) task // was from db backlog - this method will block until context timeout // trying to match with a poller. The caller is expected to set the // correct context timeout. // // returns error when: // - ratelimit is exceeded (does not apply to query task) // - context deadline is exceeded // - task is matched and consumer returns error in response channel func (tm *taskMatcherImpl) Offer(ctx context.Context, task *InternalTask) (bool, error) { startT := time.Now() if !task.IsForwarded() { err := tm.ratelimit(ctx) if err != nil { tm.scope.IncCounter(metrics.SyncThrottlePerTaskListCounter) return false, err } } e := event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), } localWaitTime := tm.config.LocalTaskWaitTime() if localWaitTime > 0 { childCtx, cancel := context.WithTimeout(ctx, localWaitTime) select { case tm.getTaskC(task) <- task: // poller picked up the task cancel() if task.ResponseC != nil { // if there is a response channel, block until resp is received // and return error if the response contains error err := <-task.ResponseC tm.scope.RecordTimer(metrics.SyncMatchLocalPollLatencyPerTaskList, time.Since(startT)) if err == nil { e.EventName = "Offer task due to local wait" e.Payload = map[string]any{ "TaskIsForwarded": task.IsForwarded(), } event.Log(e) } return true, err } return false, nil case <-childCtx.Done(): cancel() } } select { case tm.getTaskC(task) <- task: // poller picked up the task if task.ResponseC != nil { // if there is a response channel, block until resp is received // and return error if the response contains error err := <-task.ResponseC tm.scope.RecordTimer(metrics.SyncMatchLocalPollLatencyPerTaskList, time.Since(startT)) return true, err } return false, nil default: // no poller waiting for tasks, try forwarding this task to the // root partition if possible select { case token := <-tm.fwdrAddReqTokenC(): e.EventName = "Attempting to Forward Task" event.Log(e) err := tm.fwdr.ForwardTask(ctx, task) token.release() if err == nil { // task was remotely sync matched on the parent partition tm.scope.RecordTimer(metrics.SyncMatchForwardPollLatencyPerTaskList, time.Since(startT)) return true, nil } if errors.Is(err, ErrForwarderSlowDown) { tm.scope.IncCounter(metrics.SyncMatchForwardTaskThrottleErrorPerTasklist) } default: if !tm.isForwardingAllowed() && // we are the root partition and forwarding is not possible task.source == types.TaskSourceDbBacklog && // task was from backlog (stored in db) task.IsForwarded() { // task came from a child partition // a forwarded backlog task from a child partition, block trying // to match with a poller until ctx timeout return tm.OfferOrTimeout(ctx, startT, task) } } return false, nil } } // OfferOrTimeout offers a task to a poller and blocks until a poller picks up the task or context timeouts func (tm *taskMatcherImpl) OfferOrTimeout(ctx context.Context, startT time.Time, task *InternalTask) (bool, error) { if !task.IsForwarded() { err := tm.ratelimit(ctx) if err != nil { // If context was canceled/timed out, return without error (consistent with original behavior) if err == ctx.Err() { return false, nil } tm.scope.IncCounter(metrics.SyncThrottlePerTaskListCounter) return false, err } } select { case tm.getTaskC(task) <- task: // poller picked up the task if task.ResponseC != nil { select { case err := <-task.ResponseC: tm.scope.RecordTimer(metrics.SyncMatchLocalPollLatencyPerTaskList, time.Since(startT)) return true, err case <-ctx.Done(): return false, nil } } return task.ActivityTaskDispatchInfo != nil, nil case <-ctx.Done(): return false, nil } } // OfferQuery will either match task to local poller or will forward query task. // Local match is always attempted before forwarding is attempted. If local match occurs // response and error are both nil, if forwarding occurs then response or error is returned. func (tm *taskMatcherImpl) OfferQuery(ctx context.Context, task *InternalTask) (*types.MatchingQueryWorkflowResponse, error) { select { case tm.queryTaskC <- task: <-task.ResponseC return nil, nil default: } fwdrTokenC := tm.fwdrAddReqTokenC() for { select { case tm.queryTaskC <- task: <-task.ResponseC return nil, nil case token := <-fwdrTokenC: resp, err := tm.fwdr.ForwardQueryTask(ctx, task) token.release() if err == nil { return resp, nil } if err == ErrForwarderSlowDown { // if we are rate limited, try only local match for the // remainder of the context timeout left fwdrTokenC = noopForwarderTokenC continue } return nil, err case <-ctx.Done(): return nil, ctx.Err() } } } // MustOffer blocks until a consumer is found to handle this task // Returns error only when context is canceled, expired or the ratelimit is set to zero (allow nothing) func (tm *taskMatcherImpl) MustOffer(ctx context.Context, task *InternalTask) error { e := event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), TaskInfo: task.Info(), } if err := tm.ratelimit(ctx); err != nil { e.EventName = "Throttled While Dispatching" event.Log(e) return fmt.Errorf("rate limit error dispatching: %w", err) } startT := time.Now() // attempt a match with local poller first. When that // doesn't succeed, try both local match and remote match taskC := tm.getTaskC(task) localWaitTime := tm.config.LocalTaskWaitTime() childCtx, cancel := context.WithTimeout(ctx, localWaitTime) select { case taskC <- task: // poller picked up the task cancel() tm.scope.IncCounter(metrics.AsyncMatchLocalPollCounterPerTaskList) tm.scope.RecordTimer(metrics.AsyncMatchLocalPollLatencyPerTaskList, time.Since(startT)) e.EventName = "Dispatched to Local Poller" event.Log(e) return nil case <-ctx.Done(): cancel() e.EventName = "Context Done While Dispatching to Local Poller" event.Log(e) return fmt.Errorf("context done when trying to forward local task: %w", ctx.Err()) case <-childCtx.Done(): cancel() } attempt := 0 forLoop: for { if err := ctx.Err(); err != nil { e.EventName = "Context Done While Dispatching to Local or Forwarding" event.Log(e) return fmt.Errorf("failed to offer task: %w", ctx.Err()) } select { case taskC <- task: // poller picked up the task e.EventName = "Dispatched to Local Poller" event.Log(e) tm.scope.IncCounter(metrics.AsyncMatchLocalPollCounterPerTaskList) tm.scope.RecordTimer(metrics.AsyncMatchLocalPollAttemptPerTaskList, time.Duration(attempt)) tm.scope.RecordTimer(metrics.AsyncMatchLocalPollLatencyPerTaskList, time.Since(startT)) return nil case token := <-tm.fwdrAddReqTokenC(): e.EventName = "Attempting to Forward Task" event.Log(e) childCtx, cancel := context.WithTimeout(ctx, time.Second*2) err := tm.fwdr.ForwardTask(childCtx, task) token.release() if err != nil { if errors.Is(err, ErrForwarderSlowDown) { tm.scope.IncCounter(metrics.AsyncMatchForwardTaskThrottleErrorPerTasklist) } e.EventName = "Task Forwarding Failed" e.Payload = map[string]any{"error": err.Error()} event.Log(e) e.Payload = nil tm.log.Debug("failed to forward task", tag.Error(err), tag.TaskID(task.Event.TaskID), ) // forwarder returns error only when the call is rate limited. To // avoid a busy loop on such rate limiting events, we only attempt to make // the next forwarded call after this childCtx expires. Till then, we block // hoping for a local poller match select { case taskC <- task: // poller picked up the task e.EventName = "Dispatched to Local Poller (after failed forward)" event.Log(e) cancel() tm.scope.IncCounter(metrics.AsyncMatchLocalPollAfterForwardFailedCounterPerTaskList) tm.scope.RecordTimer(metrics.AsyncMatchLocalPollAfterForwardFailedAttemptPerTaskList, time.Duration(attempt)) tm.scope.RecordTimer(metrics.AsyncMatchLocalPollAfterForwardFailedLatencyPerTaskList, time.Since(startT)) return nil case <-childCtx.Done(): attempt++ cancel() continue forLoop } } cancel() e.EventName = "Task Forwarded" event.Log(e) tm.scope.IncCounter(metrics.AsyncMatchForwardPollCounterPerTaskList) tm.scope.RecordTimer(metrics.AsyncMatchForwardPollAttemptPerTaskList, time.Duration(attempt)) tm.scope.RecordTimer(metrics.AsyncMatchForwardPollLatencyPerTaskList, time.Since(startT)) // at this point, we forwarded the task to a parent partition which // in turn dispatched the task to a poller. Make sure we delete the // task from the database task.Finish(nil) return nil case <-ctx.Done(): e.EventName = "Context Done While Dispatching to Local or Forwarding" event.Log(e) return fmt.Errorf("failed to offer task: %w", ctx.Err()) } } } // Poll blocks until a task is found or context deadline is exceeded // On success, the returned task could be a query task or a regular task // Returns ErrNoTasks when context deadline is exceeded // Returns ErrMatcherClosed when matching is closed func (tm *taskMatcherImpl) Poll(ctx context.Context, isolationGroup string) (*InternalTask, error) { startT := time.Now() isolatedTaskC, ok := tm.isolatedTaskC[isolationGroup] if !ok && isolationGroup != "" { // fallback to default isolation group instead of making poller crash if the isolation group is invalid isolatedTaskC = tm.taskC tm.scope.IncCounter(metrics.PollerInvalidIsolationGroupCounter) } // we want cancellation of taskMatcher to be treated as cancellation of client context // original context (ctx) won't be affected ctxWithCancelPropagation, stopFn := ctxutils.WithPropagatedContextCancel(ctx, tm.cancelCtx) defer stopFn() var task *InternalTask var err error defer func() { if task != nil { task.AutoConfigHint = &types.AutoConfigHint{ EnableAutoConfig: tm.config.EnableClientAutoConfig(), PollerWaitTimeInMs: time.Since(startT).Milliseconds(), } } }() // try local match first without blocking until context timeout if task, err = tm.pollNonBlocking(ctxWithCancelPropagation, isolatedTaskC, tm.taskC, tm.queryTaskC); err == nil { tm.scope.RecordTimer(metrics.PollLocalMatchLatencyPerTaskList, time.Since(startT)) return task, nil } // there is no local poller available to pickup this task. Now block waiting // either for a local poller or a forwarding token to be available. When a // forwarding token becomes available, send this poll to a parent partition tm.log.Debug("falling back to non-local polling", tag.IsolationGroup(isolationGroup), tag.Dynamic("isolated channel", len(isolatedTaskC)), tag.Dynamic("fallback channel", len(tm.taskC)), ) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), EventName: "Matcher Falling Back to Non-Local Polling", }) task, err = tm.pollOrForward(ctxWithCancelPropagation, startT, isolationGroup, isolatedTaskC, tm.taskC, tm.queryTaskC) return task, err } // PollForQuery blocks until a *query* task is found or context deadline is exceeded // Returns ErrNoTasks when context deadline is exceeded // Returns ErrMatcherClosed when matching is closed func (tm *taskMatcherImpl) PollForQuery(ctx context.Context) (*InternalTask, error) { startT := time.Now() // try local match first without blocking until context timeout if task, err := tm.pollNonBlocking(ctx, nil, nil, tm.queryTaskC); err == nil { tm.scope.RecordTimer(metrics.PollLocalMatchLatencyPerTaskList, time.Since(startT)) return task, nil } ctxWithCancelPropagation, stopFn := ctxutils.WithPropagatedContextCancel(ctx, tm.cancelCtx) defer stopFn() // there is no local poller available to pickup this task. Now block waiting // either for a local poller or a forwarding token to be available. When a // forwarding token becomes available, send this poll to a parent partition return tm.pollOrForward(ctxWithCancelPropagation, startT, "", nil, nil, tm.queryTaskC) } func (tm *taskMatcherImpl) RefreshCancelContext() { tm.cancelLock.Lock() defer tm.cancelLock.Unlock() tm.cancelCtx, tm.cancelFunc = context.WithCancel(context.Background()) } func (tm *taskMatcherImpl) pollOrForward( ctx context.Context, startT time.Time, isolationGroup string, isolatedTaskC <-chan *InternalTask, taskC <-chan *InternalTask, queryTaskC <-chan *InternalTask, ) (*InternalTask, error) { select { case task := <-isolatedTaskC: if task.ResponseC != nil { tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) } tm.scope.RecordTimer(metrics.PollLocalMatchLatencyPerTaskList, time.Since(startT)) tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), TaskInfo: task.Info(), EventName: "Matched Task (pollOrForward)", Payload: map[string]any{ "TaskIsForwarded": task.IsForwarded(), "SyncMatched": task.ResponseC != nil, "FromIsolatedTaskC": true, "IsolationGroup": task.isolationGroup, }, }) return task, nil case task := <-taskC: if task.ResponseC != nil { tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) } tm.scope.RecordTimer(metrics.PollLocalMatchLatencyPerTaskList, time.Since(startT)) tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), TaskInfo: task.Info(), EventName: "Matched Task (pollOrForward)", Payload: map[string]any{ "TaskIsForwarded": task.IsForwarded(), "SyncMatched": task.ResponseC != nil, "FromIsolatedTaskC": false, "IsolationGroup": task.isolationGroup, }, }) return task, nil case task := <-queryTaskC: tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) return task, nil case <-ctx.Done(): tm.scope.IncCounter(metrics.PollTimeoutPerTaskListCounter) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), EventName: "Poll Timeout", }) return nil, ErrNoTasks case token := <-tm.fwdrPollReqTokenC(): event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), EventName: "Attempting to Forward Poll", Payload: map[string]any{ "IsolationGroup": isolationGroup, }, }) if task, err := tm.fwdr.ForwardPoll(ctx); err == nil { token.release() tm.scope.RecordTimer(metrics.PollForwardMatchLatencyPerTaskList, time.Since(startT)) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), EventName: "Forwarded Poll returned task", }) return task, nil } token.release() return tm.poll(ctx, startT, isolatedTaskC, taskC, queryTaskC) } } func (tm *taskMatcherImpl) poll( ctx context.Context, startT time.Time, isolatedTaskC <-chan *InternalTask, taskC <-chan *InternalTask, queryTaskC <-chan *InternalTask, ) (*InternalTask, error) { select { case task := <-isolatedTaskC: if task.ResponseC != nil { tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) } tm.scope.RecordTimer(metrics.PollLocalMatchAfterForwardFailedLatencyPerTaskList, time.Since(startT)) tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), TaskInfo: task.Info(), EventName: "Matched Task (poll)", Payload: map[string]any{ "TaskIsForwarded": task.IsForwarded(), "SyncMatched": task.ResponseC != nil, "FromIsolatedTaskC": true, "IsolationGroup": task.isolationGroup, }, }) return task, nil case task := <-taskC: if task.ResponseC != nil { tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) } tm.scope.RecordTimer(metrics.PollLocalMatchAfterForwardFailedLatencyPerTaskList, time.Since(startT)) tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), TaskInfo: task.Info(), EventName: "Matched Task (poll)", Payload: map[string]any{ "TaskIsForwarded": task.IsForwarded(), "SyncMatched": task.ResponseC != nil, "FromIsolatedTaskC": false, "IsolationGroup": task.isolationGroup, }, }) return task, nil case task := <-queryTaskC: tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) return task, nil case <-ctx.Done(): tm.scope.IncCounter(metrics.PollTimeoutPerTaskListCounter) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), EventName: "Poll Timeout", }) return nil, ErrNoTasks } } func (tm *taskMatcherImpl) pollLocalWait( ctx context.Context, isolatedTaskC <-chan *InternalTask, taskC <-chan *InternalTask, queryTaskC <-chan *InternalTask, ) (*InternalTask, error) { select { case task := <-isolatedTaskC: if task.ResponseC != nil { tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) } tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), TaskInfo: task.Info(), EventName: "Matched Task Nonblocking", Payload: map[string]any{ "TaskIsForwarded": task.IsForwarded(), "SyncMatched": task.ResponseC != nil, "FromIsolatedTaskC": true, "IsolationGroup": task.isolationGroup, }, }) return task, nil case task := <-taskC: if task.ResponseC != nil { tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) } tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), TaskInfo: task.Info(), EventName: "Matched Task Nonblocking", Payload: map[string]any{ "TaskIsForwarded": task.IsForwarded(), "SyncMatched": task.ResponseC != nil, "FromIsolatedTaskC": false, "IsolationGroup": task.isolationGroup, }, }) return task, nil case task := <-queryTaskC: tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) return task, nil case <-ctx.Done(): event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), EventName: "Matcher Found No Tasks Nonblocking", }) return nil, ErrNoTasks } } func (tm *taskMatcherImpl) pollNonBlocking( ctx context.Context, isolatedTaskC <-chan *InternalTask, taskC <-chan *InternalTask, queryTaskC <-chan *InternalTask, ) (*InternalTask, error) { waitTime := tm.config.LocalPollWaitTime() if waitTime > 0 { childCtx, cancel := context.WithTimeout(ctx, waitTime) defer cancel() return tm.pollLocalWait(childCtx, isolatedTaskC, taskC, queryTaskC) } select { case task := <-isolatedTaskC: if task.ResponseC != nil { tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) } tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), TaskInfo: task.Info(), EventName: "Matched Task Nonblocking", Payload: map[string]any{ "TaskIsForwarded": task.IsForwarded(), "SyncMatched": task.ResponseC != nil, "FromIsolatedTaskC": true, "IsolationGroup": task.isolationGroup, }, }) return task, nil case task := <-taskC: if task.ResponseC != nil { tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) } tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), TaskInfo: task.Info(), EventName: "Matched Task Nonblocking", Payload: map[string]any{ "TaskIsForwarded": task.IsForwarded(), "SyncMatched": task.ResponseC != nil, "FromIsolatedTaskC": false, "IsolationGroup": task.isolationGroup, }, }) return task, nil case task := <-queryTaskC: tm.scope.IncCounter(metrics.PollSuccessWithSyncPerTaskListCounter) tm.scope.IncCounter(metrics.PollSuccessPerTaskListCounter) return task, nil default: event.Log(event.E{ TaskListName: tm.tasklist.GetName(), TaskListType: tm.tasklist.GetType(), TaskListKind: tm.tasklistKind.Ptr(), EventName: "Matcher Found No Tasks Nonblocking", }) return nil, ErrNoTasks } } func (tm *taskMatcherImpl) fwdrPollReqTokenC() <-chan *ForwarderReqToken { if tm.fwdr == nil { return noopForwarderTokenC } return tm.fwdr.PollReqTokenC() } func (tm *taskMatcherImpl) fwdrAddReqTokenC() <-chan *ForwarderReqToken { if tm.fwdr == nil { return noopForwarderTokenC } return tm.fwdr.AddReqTokenC() } func (tm *taskMatcherImpl) ratelimit(ctx context.Context) error { err := tm.limiter.Wait(ctx) if errors.Is(err, clock.ErrCannotWait) { // "err != ctx.Err()" may also be correct, as that would mean "gave up due to context". // // in this branch: either the request would wait longer than ctx's timeout, // or the limiter's config does not allow any operations at all (burst 0). // in either case, this is returned immediately. return ErrTasklistThrottled } return err // nil if success, non-nil if canceled } func (tm *taskMatcherImpl) isForwardingAllowed() bool { return tm.fwdr != nil } func (tm *taskMatcherImpl) getTaskC(task *InternalTask) chan<- *InternalTask { taskC := tm.taskC if isolatedTaskC, ok := tm.isolatedTaskC[task.isolationGroup]; ok && task.isolationGroup != "" { taskC = isolatedTaskC } return taskC } ================================================ FILE: service/matching/tasklist/matcher_test.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "context" "math" "math/rand" "sync" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/goleak" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "golang.org/x/time/rate" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" commonConfig "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" ) const testDomainName = "test-domain" type MatcherTestSuite struct { suite.Suite controller *gomock.Controller client *matching.MockClient fwdr Forwarder cfg *config.TaskListConfig taskList *Identifier matcher *taskMatcherImpl // matcher for child partition rootMatcher TaskMatcher // matcher for parent partition isolationGroups []string } func TestMatcherSuite(t *testing.T) { suite.Run(t, new(MatcherTestSuite)) } func (t *MatcherTestSuite) SetupTest() { t.controller = gomock.NewController(t.T()) t.client = matching.NewMockClient(t.controller) cfg := config.NewConfig(dynamicconfig.NewNopCollection(), "some random hostname", commonConfig.RPC{}, func() []string { return nil }) cfg.TaskDispatchRPSTTL = 0 t.taskList = NewTestTaskListID(t.T(), uuid.New(), constants.ReservedTaskListPrefix+"tl0/1", persistence.TaskListTypeDecision) tlCfg := newTaskListConfig(t.taskList, cfg, testDomainName) tlCfg.ForwarderConfig = config.ForwarderConfig{ ForwarderMaxOutstandingPolls: func() int { return 1 }, ForwarderMaxOutstandingTasks: func() int { return 1 }, ForwarderMaxRatePerSecond: func() int { return 2 }, ForwarderMaxChildrenPerNode: func() int { return 20 }, } t.cfg = tlCfg t.isolationGroups = []string{"dca1", "dca2"} t.fwdr = newForwarder(&t.cfg.ForwarderConfig, t.taskList, types.TaskListKindNormal, t.client, metrics.NoopScope) t.matcher = newTaskMatcher(tlCfg, t.fwdr, metrics.NoopScope, []string{"dca1", "dca2"}, log.NewNoop(), t.taskList, types.TaskListKindNormal, clock.NewRatelimiter(rate.Limit(100), 100)).(*taskMatcherImpl) rootTaskList := NewTestTaskListID(t.T(), t.taskList.GetDomainID(), t.taskList.Parent(20), persistence.TaskListTypeDecision) rootTasklistCfg := newTaskListConfig(rootTaskList, cfg, testDomainName) t.rootMatcher = newTaskMatcher(rootTasklistCfg, nil, metrics.NoopScope, []string{"dca1", "dca2"}, log.NewNoop(), t.taskList, types.TaskListKindNormal, clock.NewRatelimiter(rate.Limit(100), 100)).(*taskMatcherImpl) } func (t *MatcherTestSuite) TearDownTest() { t.controller.Finish() } func (t *MatcherTestSuite) TestLocalSyncMatch() { t.disableRemoteForwarding() wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, "") if err == nil { task.Finish(nil) } }) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) syncMatch, err := t.matcher.Offer(ctx, task) cancel() wait() t.NoError(err) t.True(syncMatch) } func (t *MatcherTestSuite) TestIsolationLocalSyncMatch() { const isolationGroup = "dca1" t.disableRemoteForwarding() wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, isolationGroup) if err == nil { task.Finish(nil) } }) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "dca1") ctx, cancel := context.WithTimeout(context.Background(), time.Second) syncMatch, err := t.matcher.Offer(ctx, task) cancel() wait() t.NoError(err) t.True(syncMatch) } func (t *MatcherTestSuite) TestRemoteSyncMatch() { t.testRemoteSyncMatch(types.TaskSourceHistory, "") } func (t *MatcherTestSuite) TestIsolationRemoteSyncMatch() { t.testRemoteSyncMatch(types.TaskSourceHistory, "dca1") } func (t *MatcherTestSuite) TestRemoteSyncMatchBlocking() { t.testRemoteSyncMatch(types.TaskSourceDbBacklog, "") } func (t *MatcherTestSuite) TestIsolationRemoteSyncMatchBlocking() { t.testRemoteSyncMatch(types.TaskSourceDbBacklog, "dca1") } func (t *MatcherTestSuite) testRemoteSyncMatch(taskSource types.TaskSource, isolationGroup string) { pollSigC := make(chan struct{}) bgctx, bgcancel := context.WithTimeout(context.Background(), time.Second) go func() { <-pollSigC if taskSource == types.TaskSourceDbBacklog { // when task is from dbBacklog, sync match SHOULD block // so lets delay polling by a bit to verify that time.Sleep(time.Millisecond * 10) } task, err := t.matcher.Poll(bgctx, isolationGroup) bgcancel() if err == nil && !task.IsStarted() { task.Finish(nil) } }() t.client.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any()).DoAndReturn( func(arg0 context.Context, arg1 *types.MatchingPollForDecisionTaskRequest, option ...yarpc.CallOption) (*types.MatchingPollForDecisionTaskResponse, error) { task, err := t.rootMatcher.Poll(arg0, isolationGroup) if err != nil { return nil, err } task.Finish(nil) return &types.MatchingPollForDecisionTaskResponse{ WorkflowExecution: task.WorkflowExecution(), }, nil }, ).AnyTimes() task := newInternalTask(t.newTaskInfo(), nil, taskSource, "", true, nil, isolationGroup) ctx, cancel := context.WithTimeout(context.Background(), time.Second) var err error var remoteSyncMatch bool var req *types.AddDecisionTaskRequest t.client.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.AddDecisionTaskRequest, option ...yarpc.CallOption) { req = arg1 task.forwardedFrom = req.GetForwardedFrom() close(pollSigC) if taskSource != types.TaskSourceDbBacklog { // when task is not from backlog, wait a bit for poller to arrive first // when task is from backlog, offer blocks, so we don't need to do this time.Sleep(time.Millisecond) // Sleep for local poll wait time so that poller will be forwarded to root partition time.Sleep(t.matcher.config.LocalPollWaitTime()) } remoteSyncMatch, err = t.rootMatcher.Offer(ctx, task) }, ).Return(&types.AddDecisionTaskResponse{}, nil) _, err0 := t.matcher.Offer(ctx, task) t.NoError(err0) cancel() <-bgctx.Done() // wait for async work to finish t.NotNil(req) t.NoError(err) t.True(remoteSyncMatch) t.Equal(t.taskList.name, req.GetForwardedFrom()) t.Equal(t.taskList.Parent(20), req.GetTaskList().GetName()) } func (t *MatcherTestSuite) TestSyncMatchFailure() { task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) var req *types.AddDecisionTaskRequest t.client.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.AddDecisionTaskRequest, option ...yarpc.CallOption) { req = arg1 }, ).Return(nil, &types.ServiceBusyError{}) syncMatch, err := t.matcher.Offer(ctx, task) cancel() t.NotNil(req) t.NoError(err) t.False(syncMatch) } func (t *MatcherTestSuite) TestRateLimitHandling() { scope := mocks.Scope{} scope.On("IncCounter", metrics.SyncMatchForwardTaskThrottleErrorPerTasklist) scope.On("RecordTimer", mock.Anything, mock.Anything) t.matcher.scope = &scope for i := 0; i < 5; i++ { t.client.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any()).Return(&types.AddDecisionTaskResponse{}, nil).AnyTimes() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) _, err := t.matcher.Offer(ctx, task) cancel() assert.NoError(t.T(), err) } } func (t *MatcherTestSuite) TestIsolationSyncMatchFailure() { const isolationGroup = "dca2" t.disableRemoteForwarding() wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, isolationGroup) if err == nil { task.Finish(nil) } }) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "dca1") ctx, cancel := context.WithTimeout(context.Background(), time.Second) syncMatch, err := t.matcher.Offer(ctx, task) cancel() wait() t.NoError(err) t.False(syncMatch) } func (t *MatcherTestSuite) TestQueryLocalSyncMatch() { t.disableRemoteForwarding() wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.PollForQuery(ctx) if err == nil && task.IsQuery() { task.Finish(nil) } }) task := newInternalQueryTask(uuid.New(), &types.MatchingQueryWorkflowRequest{}) ctx, cancel := context.WithTimeout(context.Background(), time.Second) resp, err := t.matcher.OfferQuery(ctx, task) cancel() wait() t.NoError(err) t.Nil(resp) } func (t *MatcherTestSuite) TestQueryRemoteSyncMatch() { ready, wait := ensureAsyncAfterReady(time.Second, func(ctx context.Context) { task, err := t.matcher.PollForQuery(ctx) if err == nil && task.IsQuery() { task.Finish(nil) } }) var remotePollResp *types.MatchingPollForDecisionTaskResponse t.client.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any()).DoAndReturn( func(arg0 context.Context, arg1 *types.MatchingPollForDecisionTaskRequest, option ...yarpc.CallOption) (*types.MatchingPollForDecisionTaskResponse, error) { task, err := t.rootMatcher.PollForQuery(arg0) if err != nil { return nil, err } else if task.IsQuery() { task.Finish(nil) res := &types.MatchingPollForDecisionTaskResponse{ Query: &types.WorkflowQuery{}, } remotePollResp = res return res, nil } return nil, nil }, ).AnyTimes() task := newInternalQueryTask(uuid.New(), &types.MatchingQueryWorkflowRequest{}) ctx, cancel := context.WithTimeout(context.Background(), time.Second) var req *types.MatchingQueryWorkflowRequest t.client.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.MatchingQueryWorkflowRequest, option ...yarpc.CallOption) { req = arg1 task.forwardedFrom = req.GetForwardedFrom() ready() t.rootMatcher.OfferQuery(ctx, task) }, ).Return(&types.MatchingQueryWorkflowResponse{QueryResult: []byte("answer")}, nil) result, err := t.matcher.OfferQuery(ctx, task) cancel() wait() t.NotNil(req) t.NoError(err) t.NotNil(result) t.NotNil(remotePollResp.Query) t.Equal("answer", string(result.QueryResult)) t.Equal(t.taskList.name, req.GetForwardedFrom()) t.Equal(t.taskList.Parent(20), req.GetTaskList().GetName()) } func (t *MatcherTestSuite) TestQueryRemoteSyncMatchError() { <-t.fwdr.PollReqTokenC() matched := false ready, wait := ensureAsyncAfterReady(time.Second, func(ctx context.Context) { task, err := t.matcher.PollForQuery(ctx) if err == nil && task.IsQuery() { matched = true task.Finish(nil) } }) task := newInternalQueryTask(uuid.New(), &types.MatchingQueryWorkflowRequest{}) ctx, cancel := context.WithTimeout(context.Background(), time.Second) var req *types.MatchingQueryWorkflowRequest t.client.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.MatchingQueryWorkflowRequest, option ...yarpc.CallOption) { req = arg1 ready() }, ).Return(nil, &types.ServiceBusyError{}) result, err := t.matcher.OfferQuery(ctx, task) cancel() wait() t.NotNil(req) t.NoError(err) t.Nil(result) t.True(matched) } func (t *MatcherTestSuite) TestMustOfferLocalMatch() { t.disableRemoteForwarding() wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, "") if err == nil { task.Finish(nil) } }) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) err := t.matcher.MustOffer(ctx, task) cancel() wait() t.NoError(err) } func (t *MatcherTestSuite) TestIsolationMustOfferLocalMatch() { // force disable remote forwarding t.disableRemoteForwarding() wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, "dca1") if err == nil { task.Finish(nil) } }) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "dca1") ctx, cancel := context.WithTimeout(context.Background(), time.Second) err := t.matcher.MustOffer(ctx, task) cancel() wait() t.NoError(err) } func (t *MatcherTestSuite) TestMustOfferRemoteMatch() { pollSigC := make(chan struct{}) forwardPollSigC := make(chan struct{}) t.client.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any()).DoAndReturn( func(arg0 context.Context, arg1 *types.MatchingPollForDecisionTaskRequest, option ...yarpc.CallOption) (*types.MatchingPollForDecisionTaskResponse, error) { close(forwardPollSigC) <-pollSigC time.Sleep(time.Millisecond * 500) // delay poll to verify that offer blocks on parent task, err := t.rootMatcher.Poll(arg0, "") if err != nil { return nil, err } task.Finish(nil) return &types.MatchingPollForDecisionTaskResponse{ WorkflowExecution: task.WorkflowExecution(), }, nil }, ).AnyTimes() taskCompleted := false completionFunc := func(*persistence.TaskInfo, error) { taskCompleted = true } task := newInternalTask(t.newTaskInfo(), completionFunc, types.TaskSourceDbBacklog, "", false, nil, "") ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) var err error var remoteSyncMatch bool var req *types.AddDecisionTaskRequest t.client.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any()).Return(nil, &types.ServiceBusyError{}).Times(1) t.client.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.AddDecisionTaskRequest, option ...yarpc.CallOption) { req = arg1 task := newInternalTask(task.Event.TaskInfo, nil, types.TaskSourceDbBacklog, req.GetForwardedFrom(), true, nil, "") close(pollSigC) remoteSyncMatch, err = t.rootMatcher.Offer(ctx, task) }, ).Return(&types.AddDecisionTaskResponse{}, nil) // Poll needs to happen before MustOffer, or else it goes into the non-blocking path. var pollResultMu sync.Mutex var polledTask *InternalTask var pollErr error wait := ensureAsyncReady(time.Second*3, func(ctx context.Context) { pollResultMu.Lock() defer pollResultMu.Unlock() polledTask, pollErr = t.matcher.Poll(ctx, "") }) <-forwardPollSigC t.NoError(t.matcher.MustOffer(ctx, task)) cancel() wait() pollResultMu.Lock() defer pollResultMu.Unlock() t.NoError(pollErr) t.NotNil(polledTask) t.NotNil(req) t.NoError(err) t.True(remoteSyncMatch) t.True(taskCompleted) t.Equal(t.taskList.name, req.GetForwardedFrom()) t.Equal(t.taskList.Parent(20), req.GetTaskList().GetName()) } func (t *MatcherTestSuite) TestMustOfferRemoteRateLimit() { scope := mocks.Scope{} scope.On("IncCounter", metrics.AsyncMatchForwardTaskThrottleErrorPerTasklist) scope.On("RecordTimer", mock.Anything, mock.Anything) t.matcher.scope = &scope completionFunc := func(*persistence.TaskInfo, error) {} for i := 0; i < 5; i++ { scope.On("IncCounter", metrics.AsyncMatchForwardPollCounterPerTaskList) t.client.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any()).Return(&types.AddDecisionTaskResponse{}, nil) task := newInternalTask(t.newTaskInfo(), completionFunc, types.TaskSourceDbBacklog, "", false, nil, "") ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) t.NoError(t.matcher.MustOffer(ctx, task)) cancel() } } func (t *MatcherTestSuite) TestIsolationMustOfferRemoteMatch() { pollSigC := make(chan struct{}) forwardPollSigC := make(chan struct{}) t.client.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any()).DoAndReturn( func(arg0 context.Context, arg1 *types.MatchingPollForDecisionTaskRequest, option ...yarpc.CallOption) (*types.MatchingPollForDecisionTaskResponse, error) { close(forwardPollSigC) <-pollSigC time.Sleep(time.Millisecond * 500) // delay poll to verify that offer blocks on parent task, err := t.rootMatcher.Poll(arg0, "dca1") if err != nil { return nil, err } task.Finish(nil) return &types.MatchingPollForDecisionTaskResponse{ WorkflowExecution: task.WorkflowExecution(), }, nil }, ).AnyTimes() taskCompleted := false completionFunc := func(*persistence.TaskInfo, error) { taskCompleted = true } task := newInternalTask(t.newTaskInfo(), completionFunc, types.TaskSourceDbBacklog, "", false, nil, "dca1") ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) var err error var remoteSyncMatch bool var req *types.AddDecisionTaskRequest t.client.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any()).Return(nil, &types.ServiceBusyError{}).Times(1) t.client.EXPECT().AddDecisionTask(gomock.Any(), gomock.Any()).Do( func(arg0 context.Context, arg1 *types.AddDecisionTaskRequest, option ...yarpc.CallOption) { req = arg1 task := newInternalTask(task.Event.TaskInfo, nil, types.TaskSourceDbBacklog, req.GetForwardedFrom(), true, nil, "dca1") close(pollSigC) remoteSyncMatch, err = t.rootMatcher.Offer(arg0, task) }, ).Return(&types.AddDecisionTaskResponse{}, nil) // Poll needs to happen before MustOffer, or else it goes into the non-blocking path. var pollResultMu sync.Mutex var polledTask *InternalTask var pollErr error wait := ensureAsyncReady(time.Second*3, func(ctx context.Context) { pollResultMu.Lock() defer pollResultMu.Unlock() polledTask, pollErr = t.matcher.Poll(ctx, "dca1") }) <-forwardPollSigC t.NoError(t.matcher.MustOffer(ctx, task)) cancel() wait() pollResultMu.Lock() defer pollResultMu.Unlock() t.NoError(pollErr) t.NotNil(polledTask) t.NotNil(req) t.NoError(err) t.True(remoteSyncMatch) t.True(taskCompleted) t.Equal(t.taskList.name, req.GetForwardedFrom()) t.Equal(t.taskList.Parent(20), req.GetTaskList().GetName()) } func (t *MatcherTestSuite) TestPollersDisconnectedAfterDisconnectBlockedPollers() { defer goleak.VerifyNone(t.T()) t.disableRemoteForwarding() t.matcher.DisconnectBlockedPollers() longPollingCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() task, err := t.matcher.Poll(longPollingCtx, "") t.ErrorIs(err, ErrNoTasks, "closed matcher should result in no tasks") t.Nil(task) t.NoError(longPollingCtx.Err(), "the parent context was not cancelled, the child context was cancelled") } func (t *MatcherTestSuite) TestPollersConnectedAfterDisconnectBlockedPollersSetCancelContext() { defer goleak.VerifyNone(t.T()) t.disableRemoteForwarding() t.matcher.DisconnectBlockedPollers() t.matcher.RefreshCancelContext() longPollingCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() task, err := t.matcher.Poll(longPollingCtx, "") t.ErrorIs(err, ErrNoTasks, "no tasks to be matched to poller") t.Nil(task) t.ErrorIs(longPollingCtx.Err(), context.DeadlineExceeded, "the child context wasn't cancelled, the parent context timed out") } func (t *MatcherTestSuite) TestRemotePoll() { pollToken := <-t.fwdr.PollReqTokenC() var req *types.MatchingPollForDecisionTaskRequest t.client.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any()).DoAndReturn( func(arg0 context.Context, arg1 *types.MatchingPollForDecisionTaskRequest, option ...yarpc.CallOption) (*types.MatchingPollForDecisionTaskResponse, error) { req = arg1 return nil, nil }, ) ready, wait := ensureAsyncAfterReady(0, func(_ context.Context) { pollToken.release() }) ctx, cancel := context.WithTimeout(context.Background(), time.Second) ready() task, err := t.matcher.Poll(ctx, "") cancel() wait() t.NoError(err) t.NotNil(req) t.NotNil(task) t.True(task.IsStarted()) } func (t *MatcherTestSuite) TestRemotePollForQuery() { pollToken := <-t.fwdr.PollReqTokenC() var req *types.MatchingPollForDecisionTaskRequest t.client.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any()).DoAndReturn( func(arg0 context.Context, arg1 *types.MatchingPollForDecisionTaskRequest, option ...yarpc.CallOption) (*types.MatchingPollForDecisionTaskResponse, error) { req = arg1 return nil, nil }, ) ready, wait := ensureAsyncAfterReady(0, func(_ context.Context) { pollToken.release() }) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() ready() task, err := t.matcher.PollForQuery(ctx) wait() t.NoError(err) t.NotNil(req) t.NotNil(task) t.True(task.IsStarted()) } func (t *MatcherTestSuite) TestIsolationPollFailure() { t.disableRemoteForwarding() ctx, cancel := context.WithTimeout(context.Background(), time.Second) task, err := t.matcher.Poll(ctx, "invalid-group") cancel() t.Error(err) t.Nil(task) } func (t *MatcherTestSuite) TestOffer_RateLimited() { t.matcher.limiter = clock.NewRatelimiter(0, 0) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "") ctx := context.Background() matched, err := t.matcher.Offer(ctx, task) t.ErrorIs(err, ErrTasklistThrottled) t.False(matched) } func (t *MatcherTestSuite) TestOfferOrTimeout_RateLimited() { t.matcher.limiter = clock.NewRatelimiter(0, 0) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, &types.ActivityTaskDispatchInfo{}, "") ctx := context.Background() matched, err := t.matcher.OfferOrTimeout(ctx, time.Now(), task) t.ErrorIs(err, ErrTasklistThrottled) t.False(matched) } func (t *MatcherTestSuite) TestOfferOrTimeout_ForwardedNotRateLimited() { // Forwarded tasks should not be rate limited t.matcher.limiter = clock.NewRatelimiter(0, 0) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "forwarded-from", false, &types.ActivityTaskDispatchInfo{}, "") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) defer cancel() // Should timeout instead of being rate limited since forwarded tasks bypass rate limiting matched, err := t.matcher.OfferOrTimeout(ctx, time.Now(), task) t.NoError(err) t.False(matched) } func (t *MatcherTestSuite) TestOffer_NoTimeoutSyncMatchedNoError() { defer goleak.VerifyNone(t.T()) t.matcher.config.LocalTaskWaitTime = func() time.Duration { return 0 } wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, "") if err == nil { task.Finish(nil) } }) t.disableRemoteForwarding() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) syncMatched, err := t.matcher.Offer(ctx, task) cancel() wait() t.NoError(err) t.True(syncMatched) } func (t *MatcherTestSuite) TestOffer_NoTimeoutSyncMatchedError() { defer goleak.VerifyNone(t.T()) t.matcher.config.LocalTaskWaitTime = func() time.Duration { return 0 } wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, "") if err == nil { task.Finish(nil) } }) t.disableRemoteForwarding() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") task.ResponseC <- errShutdown ctx, cancel := context.WithTimeout(context.Background(), time.Second) syncMatched, err := t.matcher.Offer(ctx, task) cancel() wait() t.Error(err) t.True(syncMatched) } func (t *MatcherTestSuite) TestOffer_NoTimeoutAsyncMatchedNoError() { defer goleak.VerifyNone(t.T()) t.matcher.config.LocalTaskWaitTime = func() time.Duration { return 0 } wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, "") if err == nil { task.Finish(nil) } }) t.disableRemoteForwarding() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) syncMatched, err := t.matcher.Offer(ctx, task) cancel() wait() t.NoError(err) t.False(syncMatched) } func (t *MatcherTestSuite) TestOffer_AsyncMatchedNoError() { defer goleak.VerifyNone(t.T()) wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, "") if err == nil { task.Finish(nil) } }) t.disableRemoteForwarding() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) syncMatch, err := t.matcher.Offer(ctx, task) cancel() wait() t.NoError(err) t.False(syncMatch) } func (t *MatcherTestSuite) TestOfferOrTimeout_SyncMatchTimedOut() { defer goleak.VerifyNone(t.T()) t.disableRemoteForwarding() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") wait := ensureAsyncReady(time.Second, func(ctx context.Context) { time.Sleep(time.Millisecond * 100) task, err := t.matcher.Poll(ctx, "") if err == nil { task.Finish(nil) } }) ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) defer cancel() matched, err := t.matcher.OfferOrTimeout(ctx, time.Now(), task) wait() t.NoError(err) t.False(matched) } func (t *MatcherTestSuite) TestOfferOrTimeout_AsyncMatchNotMatched() { defer goleak.VerifyNone(t.T()) t.disableRemoteForwarding() wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, "") if err == nil { task.Finish(nil) } }) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) matched, err := t.matcher.OfferOrTimeout(ctx, time.Now(), task) cancel() wait() t.NoError(err) t.False(matched) } func (t *MatcherTestSuite) TestOfferOrTimeout_AsyncMatchMatched() { defer goleak.VerifyNone(t.T()) t.disableRemoteForwarding() wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, "") if err == nil { task.Finish(nil) } }) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, &types.ActivityTaskDispatchInfo{}, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) matched, err := t.matcher.OfferOrTimeout(ctx, time.Now(), task) cancel() wait() t.NoError(err) t.True(matched) } func (t *MatcherTestSuite) TestOfferOrTimeout_TimedOut() { t.disableRemoteForwarding() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, &types.ActivityTaskDispatchInfo{}, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) cancel() matched, err := t.matcher.OfferOrTimeout(ctx, time.Now(), task) t.NoError(err) t.False(matched) } func (t *MatcherTestSuite) TestOfferQuery_ForwardError() { ctx := context.Background() task := newInternalQueryTask(uuid.New(), &types.MatchingQueryWorkflowRequest{}) mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder fn := func() <-chan *ForwarderReqToken { c := make(chan *ForwarderReqToken, 1) c <- &ForwarderReqToken{ ch: make(chan *ForwarderReqToken, 1), } return c } mockForwarder.EXPECT().AddReqTokenC().Return(fn()).Times(1) mockForwarder.EXPECT().ForwardQueryTask(ctx, task).Return(nil, ErrNoParent).Times(1) retTask, err := t.matcher.OfferQuery(ctx, task) t.ErrorIs(err, ErrNoParent) t.Nil(retTask) } func (t *MatcherTestSuite) TestOfferQuery_ContextExpired() { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) cancel() mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder task := newInternalQueryTask(uuid.New(), &types.MatchingQueryWorkflowRequest{}) mockForwarder.EXPECT().AddReqTokenC().Times(1) retTask, err := t.matcher.OfferQuery(ctx, task) t.ErrorIs(err, context.Canceled) t.Nil(retTask) } func (t *MatcherTestSuite) TestMustOffer_ContextExpiredFirstAttempt() { ctx, cancel := context.WithTimeout(context.Background(), t.matcher.config.LocalTaskWaitTime()) defer cancel() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "") err := t.matcher.MustOffer(ctx, task) t.ErrorIs(err, context.DeadlineExceeded) } func (t *MatcherTestSuite) TestMustOffer_ContextExpiredAfterFirstAttempt() { ctx, cancel := context.WithTimeout(context.Background(), 2*t.matcher.config.LocalTaskWaitTime()) defer cancel() mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder mockForwarder.EXPECT().AddReqTokenC().Times(1) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "") err := t.matcher.MustOffer(ctx, task) t.ErrorIs(err, context.DeadlineExceeded) } func (t *MatcherTestSuite) TestMustOffer_LocalMatchAfterChildCtxExpired() { ctx := context.Background() mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder t.disableRemoteForwarding() mockForwarder.EXPECT().AddReqTokenC().AnyTimes() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "") go func() { // Waits for the child context to expire; by default, it is 10 ms. // Forces no forwarding and the context background will never expire, therefore guaranteeing that the poller // will pick up the task after the first attempt. time.Sleep(200 * time.Millisecond) retTask, err := t.matcher.Poll(ctx, "") if err == nil { retTask.Finish(nil) } }() err := t.matcher.MustOffer(ctx, task) t.NoError(err) } func (t *MatcherTestSuite) TestMustOffer_LocalMatchAfterForwardError() { ctx := context.Background() mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder forwardToken := &ForwarderReqToken{ ch: make(chan *ForwarderReqToken, 1), } fn := func() <-chan *ForwarderReqToken { c := make(chan *ForwarderReqToken, 1) c <- forwardToken return c } task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "") mockForwarder.EXPECT().AddReqTokenC().Return(fn()).Times(1) mockForwarder.EXPECT().ForwardTask(gomock.Any(), task).Return(ErrNoParent).Times(1) go func() { <-forwardToken.ch retTask, err := t.matcher.Poll(ctx, "") if err == nil { retTask.Finish(nil) } }() err := t.matcher.MustOffer(ctx, task) t.NoError(err) } func (t *MatcherTestSuite) TestMustOffer_ContextExpiredAfterForwardError() { ctx, cancel := context.WithCancel(context.Background()) mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder forwardToken := &ForwarderReqToken{ ch: make(chan *ForwarderReqToken, 1), } // Have 1 token ready and waiting c := make(chan *ForwarderReqToken, 1) c <- forwardToken task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", false, nil, "") mockForwarder.EXPECT().AddReqTokenC().Return(c).Times(1) mockForwarder.EXPECT().ForwardTask(gomock.Any(), task).Return(ErrNoParent).Times(1) go func() { // Wait for the token to be returned <-forwardToken.ch // using cancel here to simulate the context being expired cancel() }() err := t.matcher.MustOffer(ctx, task) t.ErrorIs(err, context.Canceled) t.ErrorContains(err, "failed to offer task:") } func (t *MatcherTestSuite) Test_pollOrForward_PollIsolatedTask() { ctx := context.Background() startT := time.Now() isolationGroup := "dca1" isolatedTaskC := make(chan *InternalTask, 1) t.disableRemoteForwarding() mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder // Mock the fwdrPollReqTokenC method to return a controlled channel mockTokenC := make(chan *ForwarderReqToken) mockForwarder.EXPECT().PollReqTokenC().Return(mockTokenC).AnyTimes() // Test pollOrForward for isolated task - poll isolatedTask := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, isolationGroup) isolatedTaskC <- isolatedTask retTask, err := t.matcher.pollOrForward(ctx, startT, isolationGroup, isolatedTaskC, nil, nil) t.NoError(err) t.NotNil(retTask) t.Equal(isolatedTask, retTask) } func (t *MatcherTestSuite) Test_pollOrForward_PollTask() { ctx := context.Background() startT := time.Now() taskC := make(chan *InternalTask, 1) t.disableRemoteForwarding() mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder // Mock the fwdrPollReqTokenC method to return a controlled channel mockTokenC := make(chan *ForwarderReqToken) mockForwarder.EXPECT().PollReqTokenC().Return(mockTokenC).AnyTimes() // Test pollOrForward for regular task - poll task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") taskC <- task retTask, err := t.matcher.pollOrForward(ctx, startT, "", nil, taskC, nil) t.NoError(err) t.NotNil(retTask) t.Equal(task, retTask) } func (t *MatcherTestSuite) Test_pollOrForward_PollQueryTask() { ctx := context.Background() startT := time.Now() queryTaskC := make(chan *InternalTask, 1) mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder // Mock the fwdrPollReqTokenC method to return a controlled channel mockTokenC := make(chan *ForwarderReqToken) mockForwarder.EXPECT().PollReqTokenC().Return(mockTokenC).AnyTimes() // Test pollOrForward for query task - poll queryTask := newInternalQueryTask(uuid.New(), &types.MatchingQueryWorkflowRequest{}) queryTaskC <- queryTask retTask, err := t.matcher.pollOrForward(ctx, startT, "", nil, nil, queryTaskC) t.NoError(err) t.NotNil(retTask) t.Equal(queryTask, retTask) } func (t *MatcherTestSuite) Test_pollOrForward_ForwardTask() { ctx := context.Background() startT := time.Now() isolationGroup := "dca1" mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder // Mock the fwdrPollReqTokenC method to return a controlled channel mockTokenC := make(chan *ForwarderReqToken, 1) forwardToken := &ForwarderReqToken{ ch: make(chan *ForwarderReqToken, 1), } mockTokenC <- forwardToken mockForwarder.EXPECT().PollReqTokenC().Return(mockTokenC).AnyTimes() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") mockForwarder.EXPECT().ForwardPoll(ctx).Return(task, nil).Times(1) retTask, err := t.matcher.pollOrForward(ctx, startT, isolationGroup, nil, nil, nil) t.NoError(err) t.NotNil(retTask) t.Equal(task, retTask) } func (t *MatcherTestSuite) Test_pollOrForward_ForwardTaskThenPoll() { ctx := context.Background() startT := time.Now() isolationGroup := "dca1" taskC := make(chan *InternalTask, 1) mockForwarder := NewMockForwarder(t.controller) t.matcher.fwdr = mockForwarder // Mock the fwdrPollReqTokenC method to return a controlled channel mockTokenC := make(chan *ForwarderReqToken, 1) forwardToken := &ForwarderReqToken{ ch: make(chan *ForwarderReqToken, 1), } mockTokenC <- forwardToken mockForwarder.EXPECT().PollReqTokenC().Return(mockTokenC).AnyTimes() task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") mockForwarder.EXPECT().ForwardPoll(ctx).Return(nil, ErrNoParent).Times(1) // Add the task after the forwarderReqToken is released // It's not a race condition because the PollReqTokenC is mocked and does not use the isolatedCh go func() { select { case <-forwardToken.ch: taskC <- task } }() retTask, err := t.matcher.pollOrForward(ctx, startT, isolationGroup, nil, taskC, nil) t.NoError(err) t.NotNil(retTask) t.Equal(task, retTask) } func (t *MatcherTestSuite) Test_poll_IsolatedTask() { ctx := context.Background() startT := time.Now() isolationGroup := "dca1" isolatedTaskC := make(chan *InternalTask, 1) isolatedTask := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, isolationGroup) isolatedTaskC <- isolatedTask retTask, err := t.matcher.poll(ctx, startT, isolatedTaskC, nil, nil) t.NoError(err) t.NotNil(retTask) t.Equal(isolatedTask, retTask) } func (t *MatcherTestSuite) Test_poll_Task() { ctx := context.Background() startT := time.Now() taskC := make(chan *InternalTask, 1) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") taskC <- task retTask, err := t.matcher.poll(ctx, startT, nil, taskC, nil) t.NoError(err) t.NotNil(retTask) t.Equal(task, retTask) } func (t *MatcherTestSuite) Test_poll_QueryTask() { ctx := context.Background() startT := time.Now() queryTaskC := make(chan *InternalTask, 1) queryTask := newInternalQueryTask(uuid.New(), &types.MatchingQueryWorkflowRequest{}) queryTaskC <- queryTask retTask, err := t.matcher.poll(ctx, startT, nil, nil, queryTaskC) t.NoError(err) t.NotNil(retTask) t.Equal(queryTask, retTask) } func (t *MatcherTestSuite) Test_poll_ContextExpired() { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) cancel() startT := time.Now() retTask, err := t.matcher.poll(ctx, startT, nil, nil, nil) t.ErrorIs(err, ErrNoTasks) t.Nil(retTask) } func (t *MatcherTestSuite) Test_pollNonBlocking_IsolatedTask() { t.matcher.config.LocalPollWaitTime = func() time.Duration { return 0 } ctx := context.Background() isolationGroup := "dca1" isolatedTaskC := make(chan *InternalTask, 1) isolatedTask := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, isolationGroup) isolatedTaskC <- isolatedTask retTask, err := t.matcher.pollNonBlocking(ctx, isolatedTaskC, nil, nil) t.NoError(err) t.NotNil(retTask) t.Equal(isolatedTask, retTask) } func (t *MatcherTestSuite) Test_pollNonBlocking_Task() { t.matcher.config.LocalPollWaitTime = func() time.Duration { return 0 } ctx := context.Background() taskC := make(chan *InternalTask, 1) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") taskC <- task retTask, err := t.matcher.pollNonBlocking(ctx, nil, taskC, nil) t.NoError(err) t.NotNil(retTask) t.Equal(task, retTask) } func (t *MatcherTestSuite) Test_pollNonBlocking_QueryTask() { t.matcher.config.LocalPollWaitTime = func() time.Duration { return 0 } ctx := context.Background() queryTaskC := make(chan *InternalTask, 1) queryTask := newInternalQueryTask(uuid.New(), &types.MatchingQueryWorkflowRequest{}) queryTaskC <- queryTask retTask, err := t.matcher.pollNonBlocking(ctx, nil, nil, queryTaskC) t.NoError(err) t.NotNil(retTask) t.Equal(queryTask, retTask) } func (t *MatcherTestSuite) Test_pollNonBlocking_NoTasks() { t.matcher.config.LocalPollWaitTime = func() time.Duration { return 0 } ctx := context.Background() retTask, err := t.matcher.pollNonBlocking(ctx, nil, nil, nil) t.ErrorIs(err, ErrNoTasks) t.Nil(retTask) } func (t *MatcherTestSuite) Test_fwdrPollReqTokenC() { t.matcher.fwdr = nil t.Equal(noopForwarderTokenC, t.matcher.fwdrPollReqTokenC()) } func (t *MatcherTestSuite) disableRemoteForwarding() { // force disable remote forwarding <-t.fwdr.AddReqTokenC() <-t.fwdr.PollReqTokenC() } func (t *MatcherTestSuite) newDomainCache() cache.DomainCache { domainName := testDomainName dc := cache.NewMockDomainCache(t.controller) dc.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() return dc } func (t *MatcherTestSuite) newTaskInfo() *persistence.TaskInfo { return &persistence.TaskInfo{ DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), TaskID: rand.Int63(), ScheduleID: rand.Int63(), ScheduleToStartTimeoutSeconds: rand.Int31(), } } func TestRatelimitBehavior(t *testing.T) { // NOT t.Parallel() to avoid noise from cpu-heavy tests const ( granularity = 100 * time.Millisecond precision = float64(granularity / 10) // for elapsed-time delta checks ) // code prior to ~june 2024, kept around until new behavior is thoroughly validated, // largely to make regression tests easier to build if we encounter problems orig := func(ctx context.Context, limiter *rate.Limiter) (*rate.Reservation, error) { select { case <-ctx.Done(): return nil, ctx.Err() default: } deadline, ok := ctx.Deadline() if !ok { if err := limiter.Wait(ctx); err != nil { return nil, err } return nil, nil } rsv := limiter.Reserve() // If we have to wait too long for reservation, give up and return if !rsv.OK() || rsv.Delay() > time.Until(deadline) { if rsv.OK() { // if we were indeed given a reservation, return it before we bail out rsv.Cancel() } return nil, ErrTasklistThrottled } time.Sleep(rsv.Delay()) return rsv, nil } tests := map[string]struct { run func(allowable func(ctx context.Context) (*rate.Reservation, error)) error err error duration time.Duration beginTokens int endTokens int }{ "no deadline returns immediately if available": { run: func(allowable func(ctx context.Context) (*rate.Reservation, error)) error { _, err := allowable(context.Background()) return err }, duration: 0, beginTokens: 1, }, "no deadline waits until available": { run: func(allowable func(ctx context.Context) (*rate.Reservation, error)) error { _, err := allowable(context.Background()) return err }, duration: granularity, }, "canceling no deadline while waiting returns ctx err": { run: func(allowable func(ctx context.Context) (*rate.Reservation, error)) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func() { time.Sleep(granularity / 2) cancel() }() _, err := allowable(ctx) return err }, err: context.Canceled, duration: granularity / 2, }, "returns immediately if canceled": { run: func(allowable func(ctx context.Context) (*rate.Reservation, error)) error { ctx, cancel := context.WithCancel(context.Background()) cancel() _, err := allowable(ctx) return err }, err: context.Canceled, duration: 0, beginTokens: 1, endTokens: 1, }, "sufficient deadline returns immediately if available": { run: func(allowable func(ctx context.Context) (*rate.Reservation, error)) error { ctx, cancel := context.WithTimeout(context.Background(), granularity*2) defer cancel() _, err := allowable(ctx) return err }, duration: 0, beginTokens: 1, }, "sufficient deadline waits": { run: func(allowable func(ctx context.Context) (*rate.Reservation, error)) error { ctx, cancel := context.WithTimeout(context.Background(), granularity*2) defer cancel() _, err := allowable(ctx) return err }, duration: granularity, }, "insufficient deadline stops immediately": { run: func(allowable func(ctx context.Context) (*rate.Reservation, error)) error { ctx, cancel := context.WithTimeout(context.Background(), granularity/2) defer cancel() _, err := allowable(ctx) return err }, err: ErrTasklistThrottled, duration: 0, beginTokens: 0, }, } for name, test := range tests { test := test // closure copy t.Run(name, func(t *testing.T) { t.Parallel() check := func(t *testing.T, allow func() bool, allowable func(ctx context.Context) (*rate.Reservation, error)) { for allow() { // drain tokens to start } if test.beginTokens > 0 { time.Sleep(time.Duration(test.beginTokens) * granularity) } start := time.Now() err := test.run(allowable) dur := time.Since(start).Round(time.Millisecond) if test.err != nil { assert.ErrorIs(t, err, test.err, "wrong err returned, got %v", err) } else { assert.NoError(t, err) } assert.InDeltaf(t, test.duration, dur, precision, "duration should be within %v of %v, was %v", time.Duration(precision), test.duration, dur) availableTokens := 0 for allow() { availableTokens++ } assert.Equal(t, test.endTokens, availableTokens, "should have %v tokens available after the test runs", test.endTokens) } t.Run("orig", func(t *testing.T) { t.Parallel() perSecond := time.Second / granularity limiter := rate.NewLimiter(rate.Limit(perSecond), int(perSecond)) check(t, limiter.Allow, func(ctx context.Context) (*rate.Reservation, error) { return orig(ctx, limiter) }) }) t.Run("current", func(t *testing.T) { t.Parallel() perSecond := float64(time.Second / granularity) limiter := clock.NewRatelimiter(rate.Limit(perSecond), int(perSecond)) check(t, limiter.Allow, func(ctx context.Context) (*rate.Reservation, error) { return nil, (&taskMatcherImpl{limiter: limiter}).ratelimit(ctx) }) }) }) } t.Run("known misleading behavior", func(t *testing.T) { t.Parallel() run := func(t *testing.T, limit *rate.Limiter) time.Duration { // more than enough time to wait ctx, cancel := context.WithTimeout(context.Background(), granularity*2) defer cancel() start := time.Now() t.Logf("tokens before wait: %0.5f", limit.Tokens()) res, err := orig(ctx, limit) t.Logf("tokens after wait: %0.5f", limit.Tokens()) elapsed := time.Since(start).Round(time.Millisecond) require.NoError(t, err, "should have succeeded") tokensBefore := limit.Tokens() t.Logf("tokens before cancel: %0.5f", tokensBefore) res.Cancel() tokensAfter := limit.Tokens() t.Logf("tokens after cancel: %0.5f", tokensAfter) assert.Lessf(t, math.Abs(tokensBefore-tokensAfter), 0.1, "canceling a delay-passed reservation does not return any tokens. had %0.5f, cancel(), now have %0.5f", tokensBefore, tokensAfter) assert.Lessf(t, tokensAfter, 0.1, "near zero tokens should remain") return elapsed // varies per test } t.Run("orig returned tokens are un-cancel-able if available immediately", func(t *testing.T) { t.Parallel() perSecond := time.Second / granularity limit := rate.NewLimiter(rate.Limit(perSecond), int(perSecond)) for limit.Allow() { // drain tokens to start } time.Sleep(granularity) // restore one full token elapsed := run(t, limit) require.Lessf(t, elapsed, granularity/10, "should have returned basically immediately") }) t.Run("orig returned tokens are un-cancel-able if it had to wait", func(t *testing.T) { t.Parallel() perSecond := time.Second / granularity limit := rate.NewLimiter(rate.Limit(perSecond), int(perSecond)) for limit.Allow() { // drain tokens to start } time.Sleep(granularity / 2) // restore only half of one token so it waits elapsed := run(t, limit) require.InDeltaf(t, elapsed, granularity/2, precision, "should have waited %v for the remaining half token", granularity/2) }) }) t.Run("known different behavior", func(t *testing.T) { t.Parallel() t.Run("canceling while waiting with a deadline", func(t *testing.T) { t.Parallel() cancelWhileWaiting := func(cb func(context.Context) error) (time.Duration, error) { // sufficient deadline, but canceled after half a token recovers ctx, cancel := context.WithTimeout(context.Background(), granularity*2) defer cancel() go func() { time.Sleep(granularity / 2) cancel() }() start := time.Now() err := cb(ctx) elapsed := time.Since(start) return elapsed, err } t.Run("orig does not stop", func(t *testing.T) { t.Parallel() perSecond := time.Second / granularity limit := rate.NewLimiter(rate.Limit(perSecond), int(perSecond)) for limit.Allow() { // drain tokens to start } elapsed, err := cancelWhileWaiting(func(ctx context.Context) error { _, err := orig(ctx, limit) return err }) assert.NoError(t, err, "continues waiting and returns successfully despite being canceled") assert.InDeltaf(t, granularity, elapsed, precision, "waits until a full token would recover") assert.False(t, limit.Allow(), "consumes the token that was recovered") time.Sleep(granularity / 2) assert.False(t, limit.Allow(), "did not have 0.5 tokens available after waiting") // should be 0.5 now though }) t.Run("new code stops quickly and returns unused tokens", func(t *testing.T) { t.Parallel() perSecond := float64(time.Second / granularity) limit := clock.NewRatelimiter(rate.Limit(perSecond), int(perSecond)) for limit.Allow() { // drain tokens to start } elapsed, err := cancelWhileWaiting(func(ctx context.Context) error { return (&taskMatcherImpl{limiter: limit}).ratelimit(ctx) }) assert.ErrorIs(t, err, context.Canceled, "gives up and returns context error") assert.InDeltaf(t, granularity/2, elapsed, precision, "waits only until canceled") assert.False(t, limit.Allow(), "does not yet have a full token, only 0.5") time.Sleep(granularity / 2) // note this is false in the original code assert.True(t, limit.Allow(), "0.5 -> 1 tokens recovered shows 0.5 were available after waiting") }) }) }) } // Try to ensure a blocking callback in a goroutine is not running until the thing immediately // after `ready()` has blocked, so tests can ensure that the callback contents happen last. // // Try to delay calling `ready()` until *immediately* before the blocking call for best results. // // This is a best-effort technique, as there is no way to reliably synchronize this kind of thing // without exposing internal latches or having a more sophisticated locking library than Go offers. // In case of flakiness, increase the time.Sleep and hope for the best. // // Note that adding fmt.Println() calls touches synchronization code (for I/O), so it may change behavior. func ensureAsyncAfterReady(ctxTimeout time.Duration, cb func(ctx context.Context)) (ready func(), wait func()) { ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout) ready = func() { go func() { defer cancel() // since `go func()` is non-blocking, the ready()-ing goroutine should generally continue, // and read whatever blocking point is relevant before this goroutine runs. // in many cases this sleep is unnecessary (especially with -cpu=1), but it does help. time.Sleep(1 * time.Millisecond) cb(ctx) }() } wait = func() { <-ctx.Done() } return ready, wait } // Try to ensure a blocking callback is actively blocked in a goroutine before returning, so tests can // ensure that the callback contents happen first. // Do NOT access shared variables in the callback without mutex, as it may cause data races. // // This is a best-effort technique, as there is no way to reliably synchronize this kind of thing // without exposing internal latches or having a more sophisticated locking library than Go offers. // In case of flakiness, increase the time.Sleep and hope for the best. // // Note that adding fmt.Println() calls touches synchronization code (for I/O), so it may change behavior. func ensureAsyncReady(ctxTimeout time.Duration, cb func(ctx context.Context)) (wait func()) { running := make(chan struct{}) closed := make(chan struct{}) ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout) go func() { // Defers are stacked. The last one added is the first one to run. // We want to cancel the context which will make the callback return because it's a Poll, // and then close the closed channel. // This way the returned wait function will block until the callback has returned. defer close(closed) defer cancel() close(running) cb(ctx) }() <-running // ensure the goroutine is alive // `close(running)` is non-blocking, so it should generally begin polling before yielding control to other goroutines, // but there is still a race to reach whatever blocking sync point exists between the code being tested. // In many cases this sleep is completely unnecessary (especially with -cpu=1), but it does help. time.Sleep(1 * time.Millisecond) return func() { <-closed } } func (t *MatcherTestSuite) Test_LocalSyncMatch_AutoConfigHint() { t.disableRemoteForwarding() wait := ensureAsyncReady(time.Second, func(ctx context.Context) { task, err := t.matcher.Poll(ctx, "") t.NotNil(task.AutoConfigHint) t.Equal(false, task.AutoConfigHint.EnableAutoConfig) // disabled by default if err == nil { task.Finish(nil) } }) task := newInternalTask(t.newTaskInfo(), nil, types.TaskSourceHistory, "", true, nil, "") ctx, cancel := context.WithTimeout(context.Background(), time.Second) _, err := t.matcher.Offer(ctx, task) cancel() wait() t.NoError(err) } ================================================ FILE: service/matching/tasklist/shard_processor.go ================================================ package tasklist import ( "context" "errors" "sync" "sync/atomic" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) type ShardProcessorParams struct { ShardID string TaskListsRegistry TaskListRegistry ReportTTL time.Duration TimeSource clock.TimeSource } type shardProcessorImpl struct { shardID string taskListsRegistry TaskListRegistry Status atomic.Int32 reportLock sync.RWMutex shardReport executorclient.ShardReport reportTime time.Time reportTTL time.Duration timeSource clock.TimeSource } func NewShardProcessor(params ShardProcessorParams) (ShardProcessor, error) { err := validateSPParams(params) if err != nil { return nil, err } shardprocessor := &shardProcessorImpl{ shardID: params.ShardID, taskListsRegistry: params.TaskListsRegistry, shardReport: executorclient.ShardReport{}, reportTime: params.TimeSource.Now(), reportTTL: params.ReportTTL, timeSource: params.TimeSource, } shardprocessor.SetShardStatus(types.ShardStatusREADY) shardprocessor.shardReport = executorclient.ShardReport{ ShardLoad: 0, Status: types.ShardStatusREADY, } return shardprocessor, nil } // Start is now not doing anything since the shard lifecycle management is still handled by the logic in matching. // In the future the executor client will start the shard processor when a shard is assigned. // Ideally we want to have a tasklist mapping to a shard process, but in order to do that we need a major refactoring // of tasklists partitions and reloading processes func (sp *shardProcessorImpl) Start(ctx context.Context) error { return nil } // Stop is stopping the tasklist when a shard is not assigned to this executor anymore. func (sp *shardProcessorImpl) Stop() { toShutDown := sp.taskListsRegistry.ManagersByTaskListName(sp.shardID) for _, tlMgr := range toShutDown { tlMgr.Stop() } } func (sp *shardProcessorImpl) GetShardReport() executorclient.ShardReport { sp.reportLock.Lock() defer sp.reportLock.Unlock() load := sp.shardReport.ShardLoad if sp.reportTime.Add(sp.reportTTL).Before(sp.timeSource.Now()) { load = sp.getShardLoad() } sp.shardReport = executorclient.ShardReport{ ShardLoad: load, Status: types.ShardStatus(sp.Status.Load()), } return sp.shardReport } func (sp *shardProcessorImpl) SetShardStatus(status types.ShardStatus) { sp.Status.Store(int32(status)) } func (sp *shardProcessorImpl) getShardLoad() float64 { var load float64 // We assign a shard only based on the task list name // so task lists of different task type (decisions/activities), of different kind (normal, sticky, ephemeral) or partitions // will be assigned all to the same matching instance (executor) // we need to sum the rps for each of the tasklist to calculate the load. for _, tlMgr := range sp.taskListsRegistry.ManagersByTaskListName(sp.shardID) { qps := tlMgr.QueriesPerSecond() load = load + qps } return load } func validateSPParams(params ShardProcessorParams) error { if params.ShardID == "" { return errors.New("ShardID must be specified") } if params.TaskListsRegistry == nil { return errors.New("TaskListsRegistry must be specified") } if params.TimeSource == nil { return errors.New("TimeSource must be specified") } return nil } ================================================ FILE: service/matching/tasklist/shard_processor_factory.go ================================================ package tasklist import ( "time" "github.com/uber/cadence/common/clock" ) // ShardProcessorFactory is a generic factory for creating ShardProcessor instances. type ShardProcessorFactory struct { TaskListsRegistry TaskListRegistry ReportTTL time.Duration TimeSource clock.TimeSource } func (spf ShardProcessorFactory) NewShardProcessor(shardID string) (ShardProcessor, error) { params := ShardProcessorParams{ ShardID: shardID, TaskListsRegistry: spf.TaskListsRegistry, ReportTTL: spf.ReportTTL, TimeSource: spf.TimeSource, } return NewShardProcessor(params) } ================================================ FILE: service/matching/tasklist/shard_processor_test.go ================================================ package tasklist import ( "testing" "time" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func mustNewIdentifier(domainID, name string, taskType int) *Identifier { id, err := NewIdentifier(domainID, name, taskType) if err != nil { panic(err) } return id } var testIdentifier = mustNewIdentifier("domain-id", "tl", persistence.TaskListTypeDecision) type shardProcessorTestData struct { mockRegistry *MockTaskListRegistry shardProcessor ShardProcessor } func newShardProcessorTestData(t *testing.T, taskListID *Identifier) shardProcessorTestData { ctrl := gomock.NewController(t) mockRegistry := NewMockTaskListRegistry(ctrl) mockRegistry.EXPECT().ManagersByTaskListName(taskListID.GetName()).Return([]Manager{}).AnyTimes() params := ShardProcessorParams{ ShardID: taskListID.GetName(), TaskListsRegistry: mockRegistry, ReportTTL: 1 * time.Millisecond, TimeSource: clock.NewRealTimeSource(), } shardProcessor, err := NewShardProcessor(params) require.NoError(t, err) return shardProcessorTestData{ mockRegistry: mockRegistry, shardProcessor: shardProcessor, } } func TestNewShardProcessorFailsWithEmptyParams(t *testing.T) { params := ShardProcessorParams{} sp, err := NewShardProcessor(params) require.Nil(t, sp) require.Error(t, err) } func TestGetShardReport(t *testing.T) { td := newShardProcessorTestData(t, testIdentifier) shardReport := td.shardProcessor.GetShardReport() require.NotNil(t, shardReport) require.Equal(t, float64(0), shardReport.ShardLoad) require.Equal(t, types.ShardStatusREADY, shardReport.Status) } func TestSetShardStatus(t *testing.T) { defer goleak.VerifyNone(t) td := newShardProcessorTestData(t, testIdentifier) td.shardProcessor.SetShardStatus(types.ShardStatusREADY) shardReport := td.shardProcessor.GetShardReport() require.NotNil(t, shardReport) require.Equal(t, float64(0), shardReport.ShardLoad) require.Equal(t, types.ShardStatusREADY, shardReport.Status) } ================================================ FILE: service/matching/tasklist/task.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) // TODO: review the usage of InternalTask and provide a better abstraction type ( // genericTaskInfo contains the info for an activity or decision task genericTaskInfo struct { *persistence.TaskInfo completionFunc func(*persistence.TaskInfo, error) } // queryTaskInfo contains the info for a query task queryTaskInfo struct { TaskID string Request *types.MatchingQueryWorkflowRequest } // startedTaskInfo contains info for any task received from // another matching host. This type of task is already marked as started startedTaskInfo struct { decisionTaskInfo *types.MatchingPollForDecisionTaskResponse activityTaskInfo *types.MatchingPollForActivityTaskResponse } // InternalTask represents an activity, decision, query or started (received from another host). // this struct is more like a union and only one of [ query, event, forwarded ] is // non-nil for any given task InternalTask struct { Event *genericTaskInfo // non-nil for activity or decision task that's locally generated Query *queryTaskInfo // non-nil for a query task that's locally sync matched started *startedTaskInfo // non-nil for a task received from a parent partition which is already started domainName string source types.TaskSource forwardedFrom string // name of the child partition this task is forwarded from (empty if not forwarded) isolationGroup string // isolation group of this task (empty if it can be polled by workers from any isolation group) ResponseC chan error // non-nil only where there is a caller waiting for response (sync-match) BacklogCountHint int64 ActivityTaskDispatchInfo *types.ActivityTaskDispatchInfo AutoConfigHint *types.AutoConfigHint // worker auto-scaler hint, which includes enable auto config flag and poller wait time on the matching engine } ) func newInternalTask( info *persistence.TaskInfo, completionFunc func(*persistence.TaskInfo, error), source types.TaskSource, forwardedFrom string, forSyncMatch bool, activityTaskDispatchInfo *types.ActivityTaskDispatchInfo, isolationGroup string, ) *InternalTask { task := &InternalTask{ Event: &genericTaskInfo{ TaskInfo: info, completionFunc: completionFunc, }, source: source, forwardedFrom: forwardedFrom, isolationGroup: isolationGroup, ActivityTaskDispatchInfo: activityTaskDispatchInfo, } if forSyncMatch { task.ResponseC = make(chan error, 1) } // Rewrite the partitionConfig to match how we're dispatching it // OriginalIsolationGroup is populated here and isn't written to the DB. If it's already // present then it's a forwarded task and we should respect it. if configIsolationGroup, ok := task.Event.PartitionConfig[isolationgroup.GroupKey]; ok { partitionConfig := make(map[string]string, 3) if originalIsolationGroup, ok := task.Event.PartitionConfig[isolationgroup.OriginalGroupKey]; ok { partitionConfig[isolationgroup.OriginalGroupKey] = originalIsolationGroup } else { partitionConfig[isolationgroup.OriginalGroupKey] = configIsolationGroup } partitionConfig[isolationgroup.GroupKey] = isolationGroup partitionConfig[isolationgroup.WorkflowIDKey] = task.Event.PartitionConfig[isolationgroup.WorkflowIDKey] task.Event.PartitionConfig = partitionConfig } return task } func newInternalQueryTask( taskID string, request *types.MatchingQueryWorkflowRequest, ) *InternalTask { return &InternalTask{ Query: &queryTaskInfo{ TaskID: taskID, Request: request, }, forwardedFrom: request.GetForwardedFrom(), ResponseC: make(chan error, 1), } } func newInternalStartedTask(info *startedTaskInfo) *InternalTask { return &InternalTask{started: info} } // isQuery returns true if the underlying task is a query task func (task *InternalTask) IsQuery() bool { return task.Query != nil } // isStarted is true when this task is already marked as started func (task *InternalTask) IsStarted() bool { return task.started != nil } // isForwarded returns true if the underlying task is forwarded by a remote matching host // forwarded tasks are already marked as started in history func (task *InternalTask) IsForwarded() bool { return task.forwardedFrom != "" } func (task *InternalTask) IsSyncMatch() bool { return task.ResponseC != nil } func (task *InternalTask) Info() persistence.TaskInfo { if task == nil || task.Event == nil || task.Event.TaskInfo == nil { return persistence.TaskInfo{} } return *task.Event.TaskInfo } func (task *InternalTask) WorkflowExecution() *types.WorkflowExecution { switch { case task.Event != nil: return &types.WorkflowExecution{WorkflowID: task.Event.WorkflowID, RunID: task.Event.RunID} case task.Query != nil: return task.Query.Request.GetQueryRequest().GetExecution() case task.started != nil && task.started.decisionTaskInfo != nil: return task.started.decisionTaskInfo.WorkflowExecution case task.started != nil && task.started.activityTaskInfo != nil: return task.started.activityTaskInfo.WorkflowExecution } return &types.WorkflowExecution{} } // pollForDecisionResponse returns the poll response for a decision task that is // already marked as started. This method should only be called when isStarted() is true func (task *InternalTask) PollForDecisionResponse() *types.MatchingPollForDecisionTaskResponse { if task.IsStarted() { return task.started.decisionTaskInfo } return nil } // pollForActivityResponse returns the poll response for an activity task that is // already marked as started. This method should only be called when isStarted() is true func (task *InternalTask) PollForActivityResponse() *types.MatchingPollForActivityTaskResponse { if task.IsStarted() { return task.started.activityTaskInfo } return nil } // finish marks a task as finished. Should be called after a poller picks up a task // and marks it as started. If the task is unable to marked as started, then this // method should be called with a non-nil error argument. func (task *InternalTask) Finish(err error) { switch { case task.ResponseC != nil: task.ResponseC <- err case task.Event.completionFunc != nil: task.Event.completionFunc(task.Event.TaskInfo, err) } } ================================================ FILE: service/matching/tasklist/task_completer.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "context" "errors" "fmt" "time" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( taskCompleterImpl struct { domainCache cache.DomainCache taskListID *Identifier clusterMetadata cluster.Metadata historyService history.Client scope metrics.Scope logger log.Logger throttleRetry *backoff.ThrottleRetry timeSource clock.TimeSource } ) // DomainIsActiveInThisClusterError type type DomainIsActiveInThisClusterError struct { Message string } // Implement the Error method for DomainIsActiveInThisClusterError func (e *DomainIsActiveInThisClusterError) Error() string { return e.Message } var ( errWorkflowExecutionInfoIsNil = errors.New("workflow execution info is nil") errTaskTypeNotSupported = errors.New("task type not supported") errTaskNotStarted = errors.New("task not started") errWaitTimeNotReachedForEntityNotExists = errors.New("wait time not reached for workflow EntityNotExistsError") errDomainIsActive = &DomainIsActiveInThisClusterError{Message: "domain is active"} historyServiceOperationRetryPolicy = common.CreateTaskCompleterRetryPolicy() ) func newTaskCompleter(tlMgr *taskListManagerImpl, retryPolicy backoff.RetryPolicy) TaskCompleter { return &taskCompleterImpl{ domainCache: tlMgr.domainCache, historyService: tlMgr.historyService, taskListID: tlMgr.taskListID, clusterMetadata: tlMgr.clusterMetadata, scope: tlMgr.scope, logger: tlMgr.logger, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(isRetryableError), ), timeSource: tlMgr.timeSource, } } func (tc *taskCompleterImpl) CompleteTaskIfStarted(ctx context.Context, task *InternalTask) error { op := func(ctx context.Context) (err error) { domainEntry, err := tc.domainCache.GetDomainByID(task.Event.TaskInfo.DomainID) if err != nil { return fmt.Errorf("unable to fetch domain from cache: %w", err) } if domainEntry.IsActiveIn(tc.clusterMetadata.GetCurrentClusterName()) { tc.logger.Debug("Domain is active in the current cluster, completing task", tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.WorkflowID(task.Event.WorkflowID), tag.WorkflowRunID(task.Event.RunID), ) return errDomainIsActive } req := &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: task.Event.TaskInfo.DomainID, Request: &types.DescribeWorkflowExecutionRequest{ Domain: task.domainName, Execution: &types.WorkflowExecution{ WorkflowID: task.Event.WorkflowID, RunID: task.Event.RunID, }, }, } workflowExecutionResponse, err := tc.historyService.DescribeWorkflowExecution(ctx, req) if errors.As(err, new(*types.EntityNotExistsError)) { tc.logger.Warn("Workflow execution not found while attempting to complete task on standby cluster", tag.WorkflowID(task.Event.WorkflowID), tag.WorkflowRunID(task.Event.RunID)) // this is a guard to prevent some race condition or quorum issue where the workflow is not found in the database if tc.timeSource.Since(task.Event.CreatedTime) > 24*time.Hour { task.Finish(nil) tc.scope.IncCounter(metrics.StandbyClusterTasksCompletedCounterPerTaskList) return nil } return errWaitTimeNotReachedForEntityNotExists } else if err != nil { return fmt.Errorf("unable to fetch workflow execution from the history service: %w", err) } if workflowExecutionResponse.WorkflowExecutionInfo == nil { return errWorkflowExecutionInfoIsNil } isTaskStarted, err := tc.isTaskStarted(task, workflowExecutionResponse) if err != nil { return fmt.Errorf("unable to determine if task has started: %w", err) } if isTaskStarted { task.Finish(nil) tc.scope.IncCounter(metrics.StandbyClusterTasksCompletedCounterPerTaskList) return nil } tc.scope.IncCounter(metrics.StandbyClusterTasksNotStartedCounterPerTaskList) return errTaskNotStarted } err := tc.throttleRetry.Do(ctx, op) if err != nil && !errors.Is(err, errDomainIsActive) && !errors.Is(err, errTaskNotStarted) { tc.scope.IncCounter(metrics.StandbyClusterTasksCompletionFailurePerTaskList) tc.logger.Error("Error completing task on domain's standby cluster", tag.Error(err), tag.WorkflowID(task.Event.WorkflowID), tag.WorkflowRunID(task.Event.RunID), tag.MatchingTaskID(task.Event.TaskID)) } return err } func (tc *taskCompleterImpl) isTaskStarted(task *InternalTask, workflowExecutionResponse *types.DescribeWorkflowExecutionResponse) (bool, error) { if workflowExecutionResponse.WorkflowExecutionInfo.CloseStatus != nil { return true, nil } // taskType can only be Activity or Decision, leaving the default case for future proofing switch tc.taskListID.taskType { case persistence.TaskListTypeActivity: return tc.isActivityTaskStarted(task, workflowExecutionResponse), nil case persistence.TaskListTypeDecision: return tc.isDecisionTaskStarted(task, workflowExecutionResponse), nil default: return false, errTaskTypeNotSupported } } func (tc *taskCompleterImpl) isDecisionTaskStarted(task *InternalTask, workflowExecutionResponse *types.DescribeWorkflowExecutionResponse) bool { // if there is no pending decision task, that means that this task has been already started if workflowExecutionResponse.PendingDecision == nil { return true } // if the scheduleID is different from the pending decision scheduleID or the state is started, then the decision task with the task's scheduleID has already been started if task.Event.ScheduleID < workflowExecutionResponse.PendingDecision.ScheduleID || (task.Event.ScheduleID == workflowExecutionResponse.PendingDecision.ScheduleID && *workflowExecutionResponse.PendingDecision.State == types.PendingDecisionStateStarted) { return true } tc.logger.Debug("Decision task not started.", tag.WorkflowID(task.Event.WorkflowID), tag.WorkflowRunID(task.Event.RunID), tag.MatchingTaskID(task.Event.TaskID), tag.MatchingTaskScheduleID(task.Event.ScheduleID), tag.WorkflowScheduleID(workflowExecutionResponse.PendingDecision.ScheduleID), tag.DecisionTaskState(int32(*workflowExecutionResponse.PendingDecision.State)), ) return false } func (tc *taskCompleterImpl) isActivityTaskStarted(task *InternalTask, workflowExecutionResponse *types.DescribeWorkflowExecutionResponse) bool { // if the scheduleID is different from all pending tasks' scheduleID or the pending activity has PendingActivityStateStarted, then the activity task with the task's scheduleID has already been started for _, pendingActivity := range workflowExecutionResponse.PendingActivities { if task.Event.ScheduleID == pendingActivity.ScheduleID { if *pendingActivity.State == types.PendingActivityStateStarted { return true } tc.logger.Debug("Activity task not started.", tag.WorkflowID(task.Event.WorkflowID), tag.WorkflowRunID(task.Event.RunID), tag.MatchingTaskID(task.Event.TaskID), tag.MatchingTaskScheduleID(task.Event.ScheduleID), tag.WorkflowScheduleID(pendingActivity.ScheduleID), tag.ActivityTaskState(int32(*pendingActivity.State)), ) return false } } return true } func isRetryableError(err error) bool { // entityNotExistsError is returned when the workflow is not found in the database var domainIsActiveInThisClusterError *DomainIsActiveInThisClusterError switch { case errors.As(err, &domainIsActiveInThisClusterError): return false } return true } ================================================ FILE: service/matching/tasklist/task_completer_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" ) var retryPolicyMaxAttempts = 3 func createTestTaskCompleter(controller *gomock.Controller, taskType int) *taskCompleterImpl { mockDomainCache := cache.NewMockDomainCache(controller) mockHistoryService := history.NewMockClient(controller) retryPolicy := backoff.NewExponentialRetryPolicy(100 * time.Millisecond) retryPolicy.SetMaximumAttempts(retryPolicyMaxAttempts) tlMgr := &taskListManagerImpl{ domainCache: mockDomainCache, historyService: mockHistoryService, taskListID: &Identifier{domainID: constants.TestDomainID, taskType: taskType}, clusterMetadata: cluster.GetTestClusterMetadata(true), scope: metrics.NoopScope, logger: log.NewNoop(), } tc := newTaskCompleter(tlMgr, retryPolicy) return tc.(*taskCompleterImpl) } func TestCompleteTaskIfStarted(t *testing.T) { ctx := context.Background() createdAt := time.Date(2020, 8, 1, 0, 0, 0, 0, time.UTC) testCases := []struct { name string setupMock func(*types.HistoryDescribeWorkflowExecutionRequest, *cache.MockDomainCache, *history.MockClient, clock.MockedTimeSource) task func(chan bool) *InternalTask taskType int isTaskComplete bool err error }{ { name: "error - could not get domain by ID from cache", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(nil, errors.New("error-getting-domain-by-id")).Times(retryPolicyMaxAttempts + 1) }, isTaskComplete: false, err: errors.New("error-getting-domain-by-id"), }, { name: "error - domain is active", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalDomainEntry, nil).Times(1) }, isTaskComplete: false, err: errDomainIsActive, }, { name: "error - could not fetch workflow execution from history service", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(retryPolicyMaxAttempts + 1) mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(nil, errors.New("error-describing-workflow-execution")).Times(retryPolicyMaxAttempts + 1) }, isTaskComplete: false, err: errors.New("error-describing-workflow-execution"), }, { name: "error - no WorkflowExecutionInfo in workflow execution response", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(retryPolicyMaxAttempts + 1) resp := &types.DescribeWorkflowExecutionResponse{} mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(retryPolicyMaxAttempts + 1) }, isTaskComplete: false, err: errWorkflowExecutionInfoIsNil, }, { name: "error - task type not supported", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(retryPolicyMaxAttempts + 1) resp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, } mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(retryPolicyMaxAttempts + 1) }, taskType: 999, isTaskComplete: false, err: errTaskTypeNotSupported, }, { name: "error - decision task not started - scheduleID greater than PendingDecision scheduleID", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 3, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(retryPolicyMaxAttempts + 1) resp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, PendingDecision: &types.PendingDecisionInfo{ ScheduleID: 2, State: types.PendingDecisionStateScheduled.Ptr(), }, } mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(retryPolicyMaxAttempts + 1) }, taskType: persistence.TaskListTypeDecision, isTaskComplete: false, err: errTaskNotStarted, }, { name: "error - decision task not started - scheduleID equal to PendingDecision scheduleID but PendingDecision state is not PendingDecisionStateStarted", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 3, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(retryPolicyMaxAttempts + 1) resp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, PendingDecision: &types.PendingDecisionInfo{ ScheduleID: 3, State: types.PendingDecisionStateScheduled.Ptr(), }, } mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(retryPolicyMaxAttempts + 1) }, taskType: persistence.TaskListTypeDecision, isTaskComplete: false, err: errTaskNotStarted, }, { name: "error - activity task not started - activity matching scheduleID is in PendingActivities but its state is not PendingActivityStateStarted", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 3, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(retryPolicyMaxAttempts + 1) resp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, PendingActivities: []*types.PendingActivityInfo{ { ScheduleID: 3, State: types.PendingActivityStateScheduled.Ptr(), }, }, } mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(retryPolicyMaxAttempts + 1) }, taskType: persistence.TaskListTypeActivity, isTaskComplete: false, err: errTaskNotStarted, }, { name: "error - workflow not found and task created less than 24 hours before completion attempt", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(retryPolicyMaxAttempts + 1) mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(nil, &types.EntityNotExistsError{}).Times(retryPolicyMaxAttempts + 1) }, isTaskComplete: false, err: errWaitTimeNotReachedForEntityNotExists, }, { name: "complete task - workflow not found and task created more than 24 hours before completion attempt", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(1) mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(nil, &types.EntityNotExistsError{}).Times(1) timeSource.Advance(time.Hour*24 + time.Second) }, isTaskComplete: true, err: nil, }, { name: "complete task - workflow closed", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 3, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(1) resp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{ CloseStatus: types.WorkflowExecutionCloseStatusCompleted.Ptr(), }, } mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(1) }, taskType: persistence.TaskListTypeDecision, isTaskComplete: true, err: nil, }, { name: "complete decision task - no pending decision", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 3, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(1) resp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, } mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(1) }, taskType: persistence.TaskListTypeDecision, isTaskComplete: true, err: nil, }, { name: "complete decision task - scheduleID is less than PendingDecision scheduleID", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 2, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(1) resp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, PendingDecision: &types.PendingDecisionInfo{ ScheduleID: 3, }, } mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(1) }, taskType: persistence.TaskListTypeDecision, isTaskComplete: true, err: nil, }, { name: "complete decision task - scheduleID is equal to PendingDecision scheduleID and PendingDecision state is PendingDecisionStateStarted", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 3, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(1) resp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, PendingDecision: &types.PendingDecisionInfo{ ScheduleID: 3, State: types.PendingDecisionStateStarted.Ptr(), }, } mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(1) }, taskType: persistence.TaskListTypeDecision, isTaskComplete: true, err: nil, }, { name: "complete activity task - no activity matching scheduleID in PendingActivities", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 3, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(1) resp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, PendingActivities: []*types.PendingActivityInfo{ { ScheduleID: 2, State: types.PendingActivityStateScheduled.Ptr(), }, { ScheduleID: 4, State: types.PendingActivityStateScheduled.Ptr(), }, }, } mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(1) }, taskType: persistence.TaskListTypeActivity, isTaskComplete: true, err: nil, }, { name: "complete activity task - activity matching scheduleID is in PendingActivities and its state is PendingActivityStateStarted", task: func(isComplete chan bool) *InternalTask { return &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, ScheduleID: 3, CreatedTime: createdAt, }, completionFunc: func(_ *persistence.TaskInfo, _ error) { isComplete <- true }, }, } }, setupMock: func(req *types.HistoryDescribeWorkflowExecutionRequest, mockDomainCache *cache.MockDomainCache, mockHistoryService *history.MockClient, timeSource clock.MockedTimeSource) { mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(constants.TestGlobalStandbyDomainEntry, nil).Times(1) resp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, PendingActivities: []*types.PendingActivityInfo{ { ScheduleID: 3, State: types.PendingActivityStateStarted.Ptr(), }, }, } mockHistoryService.EXPECT().DescribeWorkflowExecution(gomock.Any(), req).Return(resp, nil).Times(1) }, taskType: persistence.TaskListTypeActivity, isTaskComplete: true, err: nil, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) tCmp := createTestTaskCompleter(ctrl, tc.taskType) defer ctrl.Finish() isTaskComplete := make(chan bool, 1) task := tc.task(isTaskComplete) req := &types.HistoryDescribeWorkflowExecutionRequest{ DomainUUID: task.Event.TaskInfo.DomainID, Request: &types.DescribeWorkflowExecutionRequest{ Domain: task.domainName, Execution: &types.WorkflowExecution{ WorkflowID: task.Event.WorkflowID, RunID: task.Event.RunID, }, }, } mockedTimeSource := clock.NewMockedTimeSourceAt(createdAt) tCmp.timeSource = mockedTimeSource tc.setupMock(req, tCmp.domainCache.(*cache.MockDomainCache), tCmp.historyService.(*history.MockClient), mockedTimeSource) err := tCmp.CompleteTaskIfStarted(ctx, task) if tc.err != nil { assert.Error(t, err) if errors.Unwrap(err) != nil { assert.ErrorContains(t, errors.Unwrap(err), tc.err.Error()) } else { assert.ErrorContains(t, err, tc.err.Error()) } } else { assert.NoError(t, err) } var val bool select { case val = <-isTaskComplete: default: } assert.Equal(t, tc.isTaskComplete, val) }) } } ================================================ FILE: service/matching/tasklist/task_gc.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "sync/atomic" "time" "github.com/uber/cadence/service/matching/config" ) type taskGC struct { lock int64 db *taskListDB ackLevel int64 lastDeleteTime time.Time config *config.TaskListConfig } // newTaskGC returns an instance of a task garbage collector object // taskGC internally maintains a delete cursor and attempts to delete // a batch of tasks everytime Run() method is called. // // In order for the taskGC to actually delete tasks when Run() is called, one of // two conditions must be met // - Size Threshold: More than MaxDeleteBatchSize tasks are waiting to be deleted (rough estimation) // - Time Threshold: Time since previous delete was attempted exceeds maxTimeBetweenTaskDeletes // // Finally, the Run() method is safe to be called from multiple threads. The underlying // implementation will make sure only one caller executes Run() and others simply bail out func newTaskGC(db *taskListDB, config *config.TaskListConfig) *taskGC { return &taskGC{db: db, config: config} } // Run deletes a batch of completed tasks, if its possible to do so // Only attempts deletion if size or time thresholds are met func (tgc *taskGC) Run(ackLevel int64) { tgc.tryDeleteNextBatch(ackLevel, false) } // RunNow deletes a batch of completed tasks if its possible to do so // This method attempts deletions without waiting for size/time threshold to be met func (tgc *taskGC) RunNow(ackLevel int64) { tgc.tryDeleteNextBatch(ackLevel, true) } func (tgc *taskGC) tryDeleteNextBatch(ackLevel int64, ignoreTimeCond bool) { if !tgc.tryLock() { return } defer tgc.unlock() batchSize := tgc.config.MaxTaskDeleteBatchSize() if !tgc.checkPrecond(ackLevel, batchSize, ignoreTimeCond) { return } tgc.lastDeleteTime = time.Now() for { n, err := tgc.db.CompleteTasksLessThan(ackLevel, batchSize) if err != nil { break } if n < batchSize { tgc.ackLevel = ackLevel break } } } func (tgc *taskGC) checkPrecond(ackLevel int64, batchSize int, ignoreTimeCond bool) bool { backlog := ackLevel - tgc.ackLevel if backlog >= int64(batchSize) { return true } return backlog > 0 && (ignoreTimeCond || time.Since(tgc.lastDeleteTime) > tgc.config.MaxTimeBetweenTaskDeletes) } func (tgc *taskGC) tryLock() bool { return atomic.CompareAndSwapInt64(&tgc.lock, 0, 1) } func (tgc *taskGC) unlock() { atomic.StoreInt64(&tgc.lock, 0) } ================================================ FILE: service/matching/tasklist/task_list_limiter.go ================================================ package tasklist import ( "context" "math" "sync" "time" "go.uber.org/atomic" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/matching/config" ) const precision = 0.001 type taskListLimiter struct { backing clock.Ratelimiter timeSource clock.TimeSource scope metrics.Scope ttl time.Duration minBurst int lock sync.Mutex value atomic.Float64 lastReceivedValue atomic.Time lastUpdate atomic.Time countPartitions func() int partitions int } func newTaskListLimiter(timeSource clock.TimeSource, scope metrics.Scope, config *config.TaskListConfig, numPartitions func() int) *taskListLimiter { l := &taskListLimiter{ timeSource: timeSource, scope: scope, ttl: config.TaskDispatchRPSTTL, countPartitions: numPartitions, minBurst: config.MinTaskThrottlingBurstSize(), } l.value.Store(config.TaskDispatchRPS) l.partitions = numPartitions() now := timeSource.Now() l.lastUpdate.Store(now) l.lastReceivedValue.Store(now) l.backing = clock.NewRatelimiter(l.getLimitAndBurst()) return l } func (l *taskListLimiter) Allow() bool { return l.backing.Allow() } func (l *taskListLimiter) Wait(ctx context.Context) error { return l.backing.Wait(ctx) } func (l *taskListLimiter) Reserve() clock.Reservation { return l.backing.Reserve() } func (l *taskListLimiter) Limit() rate.Limit { return l.backing.Limit() } func (l *taskListLimiter) ReportLimit(rps float64) { now := l.timeSource.Now() // Optimistically reject it without locking if it's >= current and within the TTL if now.Sub(l.lastUpdate.Load()) < l.ttl { current := l.value.Load() if rps > current { return } // If it's roughly equal to the current value, track the timestamp // We'll maintain this value in the next update unless something lower // comes along if math.Abs(current-rps) < precision { l.lastReceivedValue.Store(now) return } // else if rps < current, we try to update } l.tryUpdate(rps) } func (l *taskListLimiter) tryUpdate(rps float64) { l.lock.Lock() defer l.lock.Unlock() now := l.timeSource.Now() current := l.value.Load() lastUpdated := l.lastUpdate.Load() ttlElapsed := now.Sub(lastUpdated) >= l.ttl changed := false // Take the lower value, or if the ttl expired and haven't received the current low value within the TTL, take the // new value if rps < current || (ttlElapsed && !l.lastReceivedValue.Load().After(now.Add(-l.ttl))) { l.lastUpdate.Store(now) l.value.Store(rps) l.lastReceivedValue.Store(now) l.partitions = l.countPartitions() changed = true l.scope.UpdateGauge(metrics.RateLimitPerTaskListGauge, rps) } else if ttlElapsed { // If the TTL elapsed, recalculate the partition count in case it changed l.lastUpdate.Store(now) newPartitions := l.countPartitions() if newPartitions != l.partitions { l.partitions = newPartitions changed = true } l.scope.UpdateGauge(metrics.RateLimitPerTaskListGauge, current) } if changed { l.backing.SetLimitAndBurst(l.getLimitAndBurst()) } } func (l *taskListLimiter) getLimitAndBurst() (rate.Limit, int) { rps := l.value.Load() rps = rps / float64(l.partitions) burst := max(int(math.Ceil(rps)), l.minBurst) if rps == 0 { burst = 0 } return rate.Limit(rps), burst } ================================================ FILE: service/matching/tasklist/task_list_limiter_test.go ================================================ package tasklist import ( "testing" "time" "github.com/stretchr/testify/assert" "golang.org/x/time/rate" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/matching/config" ) func TestTaskListLimiter(t *testing.T) { const ttl = time.Second const defaultRps = 100 cases := []struct { name string fn func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) expected rate.Limit }{ { name: "no update", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) {}, expected: rate.Limit(defaultRps), }, { name: "take lower", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { limiter.ReportLimit(1) }, expected: rate.Limit(1), }, { name: "take lowest", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { limiter.ReportLimit(50) limiter.ReportLimit(10) }, expected: rate.Limit(10), }, { name: "ignore higher", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { limiter.ReportLimit(defaultRps + 1) }, expected: rate.Limit(defaultRps), }, { name: "take higher after ttl", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { mockClock.Advance(ttl) limiter.ReportLimit(101) }, expected: rate.Limit(101), }, { name: "keep lower if reported within ttl", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { mockClock.Advance(time.Millisecond) limiter.ReportLimit(defaultRps) mockClock.Advance(ttl - time.Nanosecond) limiter.ReportLimit(defaultRps + 1) }, expected: rate.Limit(defaultRps), }, { name: "taker higher if lower not recently reported", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { mockClock.Advance(time.Millisecond) limiter.ReportLimit(defaultRps) mockClock.Advance(ttl + time.Nanosecond) limiter.ReportLimit(defaultRps + 1) }, expected: rate.Limit(defaultRps + 1), }, { name: "recalculate partitions on ttl expiration - lower limit", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { mockClock.Advance(ttl) *numPartitions = 2 limiter.ReportLimit(50) }, expected: rate.Limit(25), }, { name: "recalculate partitions on ttl expiration - equal limit", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { mockClock.Advance(ttl) *numPartitions = 2 limiter.ReportLimit(defaultRps) }, expected: rate.Limit(50), }, { name: "recalculate partitions on ttl expiration - higher limit", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { mockClock.Advance(ttl) *numPartitions = 2 limiter.ReportLimit(defaultRps + 2) }, expected: rate.Limit(51), }, { name: "recalculate partitions on early updates", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { *numPartitions = 2 limiter.ReportLimit(50) }, expected: rate.Limit(25), }, { name: "recalculate partitions even when keeping lower limit", fn: func(mockClock clock.MockedTimeSource, limiter *taskListLimiter, numPartitions *int) { // lastUpdate time limiter.ReportLimit(50) mockClock.Advance(time.Nanosecond) limiter.ReportLimit(50) mockClock.Advance(ttl - time.Nanosecond) *numPartitions = 2 // 100 won't be applied because we received 50 within the TTL, but // it's been TTL since we last did an update so recalculate the partitions limiter.ReportLimit(100) }, expected: rate.Limit(25), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { mockClock := clock.NewMockedTimeSource() scope := metrics.NoopScope numPartitions := 1 tlConfig := &config.TaskListConfig{ TaskDispatchRPSTTL: ttl, TaskDispatchRPS: defaultRps, MinTaskThrottlingBurstSize: func() int { return 1 }, OverrideTaskListRPS: func() float64 { return 0 }, } limiter := newTaskListLimiter(mockClock, scope, tlConfig, func() int { return numPartitions }) tc.fn(mockClock, limiter, &numPartitions) assert.Equal(t, tc.expected, limiter.Limit()) }) } } ================================================ FILE: service/matching/tasklist/task_list_manager.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "bytes" "context" "errors" "fmt" "reflect" "slices" "sync" "sync/atomic" "time" "golang.org/x/sync/errgroup" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/stats" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/event" "github.com/uber/cadence/service/matching/liveness" "github.com/uber/cadence/service/matching/poller" ) const ( // Time budget for empty task to propagate through the function stack and be returned to // pollForActivityTask or pollForDecisionTask handler. returnEmptyTaskTimeBudget = time.Second noIsolationTimeout = time.Duration(0) minimumIsolationDuration = time.Millisecond * 50 notifyPartitionConfigTimeout = 5 * time.Second ) var ( // ErrNoTasks is exported temporarily for integration test ErrNoTasks = errors.New("no tasks") persistenceOperationRetryPolicy = common.CreatePersistenceRetryPolicy() taskListActivityTypeTag = metrics.TaskListTypeTag("activity") taskListDecisionTypeTag = metrics.TaskListTypeTag("decision") IsolationLeakCauseError = metrics.IsolationLeakCause("error") IsolationLeakCauseGroupDrained = metrics.IsolationLeakCause("group_drained") IsolationLeakCauseGroupUnknown = metrics.IsolationLeakCause("group_unknown") IsolationLeakCauseNoRecentPollers = metrics.IsolationLeakCause("no_recent_pollers") IsolationLeakCausePartitionChange = metrics.IsolationLeakCause("partition_change") IsolationLeakCauseExpired = metrics.IsolationLeakCause("expired") defaultPartition = &types.TaskListPartition{} ) type ( pollerIDCtxKey struct{} identityCtxKey struct{} isolationGroupCtxKey struct{} ManagerParams struct { DomainCache cache.DomainCache Logger log.Logger MetricsClient metrics.Client TaskManager persistence.TaskManager ClusterMetadata cluster.Metadata IsolationState isolationgroup.State MatchingClient matching.Client Registry TaskListRegistry TaskList *Identifier TaskListKind types.TaskListKind Cfg *config.Config TimeSource clock.TimeSource CreateTime time.Time HistoryService history.Client } AddTaskParams struct { TaskInfo *persistence.TaskInfo Source types.TaskSource ForwardedFrom string ActivityTaskDispatchInfo *types.ActivityTaskDispatchInfo } // Single task list in memory state taskListManagerImpl struct { createTime time.Time enableIsolation bool taskListID *Identifier taskListKind types.TaskListKind // sticky taskList has different process in persistence config *config.TaskListConfig db *taskListDB taskWriter *taskWriter taskReader *taskReader // reads tasks from db and async matches it with poller liveness *liveness.Liveness taskGC *taskGC taskAckManager messaging.AckManager // tracks ackLevel for delivered messages matcher TaskMatcher // for matching a task producer with a poller limiter *taskListLimiter clusterMetadata cluster.Metadata domainCache cache.DomainCache isolationState isolationgroup.State isolationGroups []string logger log.Logger scope metrics.Scope timeSource clock.TimeSource matchingClient matching.Client domainName string // pollers stores poller which poll from this tasklist in last few minutes pollers poller.Manager startWG sync.WaitGroup // ensures that background processes do not start until setup is ready stopWG sync.WaitGroup stopped int32 stoppedLock sync.RWMutex registry TaskListRegistry throttleRetry *backoff.ThrottleRetry qpsTracker stats.QPSTrackerGroup adaptiveScaler AdaptiveScaler partitionConfigLock sync.RWMutex partitionConfig *types.TaskListPartitionConfig historyService history.Client taskCompleter TaskCompleter } ) const ( // maxSyncMatchWaitTime is the max amount of time that we are willing to wait for a sync match to happen maxSyncMatchWaitTime = 200 * time.Millisecond ) var errRemoteSyncMatchFailed = &types.RemoteSyncMatchedError{Message: "remote sync match failed"} func NewManager(p ManagerParams) (Manager, error) { err := validateParams(p) if err != nil { return nil, err } domainName, err := p.DomainCache.GetDomainName(p.TaskList.GetDomainID()) if err != nil { return nil, err } taskListConfig := newTaskListConfig(p.TaskList, p.Cfg, domainName) scope := common.NewPerTaskListScope(domainName, p.TaskList.GetName(), p.TaskListKind, p.MetricsClient, metrics.MatchingTaskListMgrScope). Tagged(getTaskListTypeTag(p.TaskList.GetType())) db := newTaskListDB(p.TaskManager, p.TaskList.GetDomainID(), domainName, p.TaskList.GetName(), p.TaskList.GetType(), int(p.TaskListKind), p.Logger) var isolationGroups []string if p.TaskListKind != types.TaskListKindSticky && taskListConfig.EnableTasklistIsolation() { isolationGroups = slices.Clone(p.Cfg.AllIsolationGroups()) slices.Sort(isolationGroups) } tlMgr := &taskListManagerImpl{ createTime: p.CreateTime, enableIsolation: taskListConfig.EnableTasklistIsolation(), domainCache: p.DomainCache, clusterMetadata: p.ClusterMetadata, isolationState: p.IsolationState, isolationGroups: isolationGroups, taskListID: p.TaskList, taskListKind: p.TaskListKind, logger: p.Logger.WithTags(tag.WorkflowDomainName(domainName), tag.WorkflowTaskListName(p.TaskList.GetName()), tag.WorkflowTaskListType(p.TaskList.GetType())), db: db, taskAckManager: messaging.NewAckManager(p.Logger), taskGC: newTaskGC(db, taskListConfig), config: taskListConfig, matchingClient: p.MatchingClient, domainName: domainName, scope: scope, timeSource: p.TimeSource, registry: p.Registry, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(persistenceOperationRetryPolicy), backoff.WithRetryableError(persistence.IsTransientError), ), historyService: p.HistoryService, } tlMgr.pollers = poller.NewPollerManager(func() { scope.UpdateGauge(metrics.PollerPerTaskListCounter, float64(tlMgr.pollers.GetCount())) }, p.TimeSource) livenessInterval := taskListConfig.IdleTasklistCheckInterval() tlMgr.liveness = liveness.NewLiveness(p.TimeSource, livenessInterval, func() { tlMgr.logger.Info("Task list manager stopping because no recent events", tag.Dynamic("interval", livenessInterval)) tlMgr.Stop() }) baseEvent := event.E{ TaskListName: p.TaskList.GetName(), TaskListKind: &p.TaskListKind, TaskListType: p.TaskList.GetType(), } tlMgr.qpsTracker = stats.NewEmaFixedWindowQPSTracker(p.TimeSource, 0.5, taskListConfig.QPSTrackerInterval(), baseEvent) if p.TaskList.IsRoot() && p.TaskListKind == types.TaskListKindNormal { adaptiveScalerScope := common.NewPerTaskListScope(domainName, p.TaskList.GetName(), p.TaskListKind, p.MetricsClient, metrics.MatchingAdaptiveScalerScope). Tagged(getTaskListTypeTag(p.TaskList.GetType())) tlMgr.adaptiveScaler = NewAdaptiveScaler(p.TaskList, tlMgr, taskListConfig, p.TimeSource, tlMgr.logger, adaptiveScalerScope, p.MatchingClient, baseEvent) } var fwdr Forwarder if tlMgr.isFowardingAllowed(p.TaskList, p.TaskListKind) { fwdr = newForwarder(&taskListConfig.ForwarderConfig, p.TaskList, p.TaskListKind, p.MatchingClient, scope) } numReadPartitionsFn := func() int { if taskListConfig.EnableGetNumberOfPartitionsFromCache() { partitionConfig := tlMgr.TaskListPartitionConfig() r := 1 if partitionConfig != nil { r = len(partitionConfig.ReadPartitions) } return r } return taskListConfig.NumReadPartitions() } tlMgr.limiter = newTaskListLimiter(p.TimeSource, tlMgr.scope, taskListConfig, numReadPartitionsFn) tlMgr.matcher = newTaskMatcher(taskListConfig, fwdr, tlMgr.scope, isolationGroups, tlMgr.logger, p.TaskList, p.TaskListKind, tlMgr.limiter).(*taskMatcherImpl) tlMgr.taskWriter = newTaskWriter(tlMgr) tlMgr.taskReader = newTaskReader(tlMgr, isolationGroups) tlMgr.taskCompleter = newTaskCompleter(tlMgr, historyServiceOperationRetryPolicy) tlMgr.startWG.Add(1) return tlMgr, nil } // Starts reading pump for the given task list. // The pump fills up taskBuffer from persistence. func (c *taskListManagerImpl) Start(ctx context.Context) error { defer c.startWG.Done() if !c.taskListID.IsRoot() && c.taskListKind == types.TaskListKindNormal { var info *persistence.TaskListInfo err := c.throttleRetry.Do(context.Background(), func(ctx context.Context) error { var err error info, err = c.db.GetTaskListInfo(c.taskListID.GetRoot()) return err }) if err != nil { // This is an edge case, and only occur before we fully migrate partition config to database for a tasklist. // Currently, if a task list is configured with multiple partitions in dynamicconfig before the creation of the task list, // a non-root partition can receive a request before the root partition and when it task list manager tries to read partition config from the root partition it will get this error. // For example, if in the dynamicconfig we set all task list to have 2 partitions by default, all the non-root partitions of newly created task lists will get this error. // This will not happen once we fully migrate partition config to database. Because in that case, root partition will always be created before non-root partitions. var e *types.EntityNotExistsError if !errors.As(err, &e) { c.Stop() return err } } else { c.partitionConfig = info.AdaptivePartitionConfig.ToInternalType() } } if err := c.taskWriter.Start(); err != nil { c.Stop() return err } if c.taskListID.IsRoot() && c.taskListKind == types.TaskListKindNormal { c.partitionConfig = c.db.PartitionConfig().ToInternalType() c.logger.Info("get task list partition config from db", tag.Dynamic("root-partition", c.taskListID.GetRoot()), tag.Dynamic("task-list-partition-config", c.partitionConfig)) if c.partitionConfig != nil { startConfig := c.partitionConfig // push update notification to all non-root partitions on start c.stopWG.Add(1) go func() { defer c.stopWG.Done() ctx, cancel := context.WithTimeout(context.Background(), notifyPartitionConfigTimeout) defer cancel() c.notifyPartitionConfig(ctx, nil, startConfig) }() } } c.liveness.Start() c.taskReader.Start() c.qpsTracker.Start() if c.adaptiveScaler != nil { c.adaptiveScaler.Start() } return nil } // Stop stops task list manager and calls Stop on all background child objects func (c *taskListManagerImpl) Stop() { c.stoppedLock.Lock() defer c.stoppedLock.Unlock() if !atomic.CompareAndSwapInt32(&c.stopped, 0, 1) { return } // Notify parent registry to unregister this manager c.registry.Unregister(c) if c.adaptiveScaler != nil { c.adaptiveScaler.Stop() } c.qpsTracker.Stop() c.liveness.Stop() c.taskWriter.Stop() c.taskReader.Stop() c.matcher.DisconnectBlockedPollers() c.stopWG.Wait() c.logger.Info("Task list manager state changed", tag.LifeCycleStopped) } func (c *taskListManagerImpl) handleErr(err error) error { var e *persistence.ConditionFailedError if errors.As(err, &e) { // This indicates the task list may have moved to another host. c.scope.IncCounter(metrics.ConditionFailedErrorPerTaskListCounter) c.logger.Info("Stopping task list due to persistence condition failure.", tag.Error(err)) c.Stop() if c.taskListKind == types.TaskListKindSticky { // TODO: we don't see this error in our logs, we might be able to remove this error err = &types.InternalServiceError{Message: constants.StickyTaskConditionFailedErrorMsg} } } return err } func (c *taskListManagerImpl) TaskListPartitionConfig() *types.TaskListPartitionConfig { c.partitionConfigLock.RLock() defer c.partitionConfigLock.RUnlock() scope := c.scope.Tagged(metrics.TaskListRootPartitionTag(c.taskListID.GetRoot())) if c.partitionConfig == nil { // if partition config is nil, read/write partition count is considered 1. Emit those metrics for continuity scope.UpdateGauge(metrics.TaskListPartitionConfigNumReadGauge, 1) scope.UpdateGauge(metrics.TaskListPartitionConfigNumWriteGauge, 1) return nil } config := *c.partitionConfig c.logger.Debug("current partition config", tag.Dynamic("root-partition", c.taskListID.GetRoot()), tag.Dynamic("config", config)) scope.UpdateGauge(metrics.TaskListPartitionConfigNumReadGauge, float64(len(config.ReadPartitions))) scope.UpdateGauge(metrics.TaskListPartitionConfigNumWriteGauge, float64(len(config.WritePartitions))) scope.UpdateGauge(metrics.TaskListPartitionConfigVersionGauge, float64(config.Version)) return &config } func (c *taskListManagerImpl) PartitionReadConfig() (*types.TaskListPartition, bool) { tlConfig := c.TaskListPartitionConfig() // no config indicates 1 partition if tlConfig == nil { return defaultPartition, true } partition, ok := tlConfig.ReadPartitions[c.taskListID.Partition()] return partition, ok } func (c *taskListManagerImpl) PartitionWriteConfig() (*types.TaskListPartition, bool) { tlConfig := c.TaskListPartitionConfig() // no config indicates 1 partition if tlConfig == nil { return defaultPartition, true } partition, ok := tlConfig.WritePartitions[c.taskListID.Partition()] return partition, ok } func (c *taskListManagerImpl) LoadBalancerHints() *types.LoadBalancerHints { c.startWG.Wait() if c.timeSource.Now().Sub(c.createTime) < time.Second*10 { return nil } return &types.LoadBalancerHints{ BacklogCount: c.taskAckManager.GetBacklogCount(), RatePerSecond: c.qpsTracker.QPS(), } } func (c *taskListManagerImpl) QueriesPerSecond() float64 { return c.qpsTracker.QPS() } func isTaskListPartitionConfigEqual(a types.TaskListPartitionConfig, b types.TaskListPartitionConfig) bool { a.Version = b.Version return reflect.DeepEqual(a, b) } func (c *taskListManagerImpl) RefreshTaskListPartitionConfig(ctx context.Context, config *types.TaskListPartitionConfig) error { c.startWG.Wait() if config == nil { // if config is nil, we'll reload it from database var info *persistence.TaskListInfo err := c.throttleRetry.Do(ctx, func(ctx context.Context) error { var err error info, err = c.db.GetTaskListInfo(c.taskListID.GetRoot()) return err }) if err != nil { return err } config = info.AdaptivePartitionConfig.ToInternalType() c.partitionConfigLock.Lock() c.partitionConfig = config c.partitionConfigLock.Unlock() return nil } c.partitionConfigLock.Lock() defer c.partitionConfigLock.Unlock() if c.partitionConfig == nil || c.partitionConfig.Version < config.Version { c.partitionConfig = config } return nil } // UpdateTaskListPartitionConfig updates the task list partition config. It is called by adaptive scaler component on the root partition. // Root tasklist manager will update the partition config in the database and notify all non-root partitions. func (c *taskListManagerImpl) UpdateTaskListPartitionConfig(ctx context.Context, config *types.TaskListPartitionConfig) error { c.startWG.Wait() oldConfig, newConfig, err := c.updatePartitionConfig(ctx, config) if err != nil { return err } if newConfig != nil { // push update notification to all non-root partitions c.notifyPartitionConfig(ctx, oldConfig, newConfig) } return nil } func (c *taskListManagerImpl) updatePartitionConfig(ctx context.Context, newConfig *types.TaskListPartitionConfig) (*types.TaskListPartitionConfig, *types.TaskListPartitionConfig, error) { err := validatePartitionConfig(newConfig) if err != nil { return nil, nil, err } var version int64 c.partitionConfigLock.Lock() defer c.partitionConfigLock.Unlock() oldConfig := c.partitionConfig if oldConfig != nil { if isTaskListPartitionConfigEqual(*oldConfig, *newConfig) { return nil, nil, nil } version = oldConfig.Version } else { if len(newConfig.ReadPartitions) == 1 && len(newConfig.WritePartitions) == 1 { return nil, nil, nil } } err = c.throttleRetry.Do(ctx, func(ctx context.Context) error { return c.db.UpdateTaskListPartitionConfig(toPersistenceConfig(version+1, newConfig)) }) if err != nil { // We're not sure whether the update was persisted or not, // Stop the tasklist manager and let it be reloaded c.scope.IncCounter(metrics.TaskListPartitionUpdateFailedCounter) c.Stop() return nil, nil, err } c.partitionConfig = c.db.PartitionConfig().ToInternalType() return oldConfig, c.partitionConfig, nil } func (c *taskListManagerImpl) notifyPartitionConfig(ctx context.Context, oldConfig, newConfig *types.TaskListPartitionConfig) { taskListType := types.TaskListTypeDecision.Ptr() if c.taskListID.GetType() == persistence.TaskListTypeActivity { taskListType = types.TaskListTypeActivity.Ptr() } toNotify := make(map[int]any) if oldConfig != nil { for id := range oldConfig.ReadPartitions { if id != 0 { toNotify[id] = true } } } for id := range newConfig.ReadPartitions { if id != 0 { toNotify[id] = true } } g := &errgroup.Group{} for p := range toNotify { taskListName := c.taskListID.GetPartition(p) g.Go(func() (e error) { defer func() { log.CapturePanic(recover(), c.logger, &e) }() _, e = c.matchingClient.RefreshTaskListPartitionConfig(ctx, &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: c.taskListID.GetDomainID(), TaskList: &types.TaskList{Name: taskListName, Kind: &c.taskListKind}, TaskListType: taskListType, PartitionConfig: newConfig, }) if e != nil { c.logger.Error("failed to notify partition", tag.Error(e), tag.Dynamic("task-list-partition-name", taskListName)) } return e }) } err := g.Wait() if err != nil { c.logger.Error("failed to notify all partitions", tag.Error(err)) } } // AddTask adds a task to the task list. This method will first attempt a synchronous // match with a poller. When there are no pollers or if rate limit is exceeded, task will // be written to database and later asynchronously matched with a poller. // It returns whether the sync match succeeded, along with any error that occurred. func (c *taskListManagerImpl) AddTask(ctx context.Context, params AddTaskParams) (bool, error) { c.startWG.Wait() if c.shouldReload() { c.Stop() return false, errShutdown } if c.config.EnableGetNumberOfPartitionsFromCache() { _, ok := c.PartitionWriteConfig() if !ok { return false, &types.ReadOnlyPartitionError{Message: "Current partition is drained."} } } if params.ForwardedFrom == "" { // request sent by history service c.liveness.MarkAlive() if isolationGroup, ok := params.TaskInfo.PartitionConfig[isolationgroup.GroupKey]; ok { c.qpsTracker.ReportGroup(isolationGroup, 1) } else { c.qpsTracker.ReportCounter(1) } c.scope.UpdateGauge(metrics.EstimatedAddTaskQPSGauge, c.qpsTracker.QPS()) } // Sync match flow var syncMatch bool e := event.E{ TaskListName: c.taskListID.GetName(), TaskListKind: &c.taskListKind, TaskListType: c.taskListID.GetType(), TaskInfo: *params.TaskInfo, } domainEntry, err := c.domainCache.GetDomainByID(params.TaskInfo.DomainID) if err != nil { // If we cannot fetch the domain entry from the cache, we cannot proceed. Sync match fails. return false, err } // Check if the task was forwarded from another partition isForwarded := params.ForwardedFrom != "" if !domainEntry.IsActiveIn(c.clusterMetadata.GetCurrentClusterName()) { // standby task, only persist when task is not forwarded from child partition syncMatch = false if isForwarded { return syncMatch, errRemoteSyncMatchFailed } // Persist the standby task, but the sync match still fails. // Return the false syncMatch flag along with any error _, err = c.taskWriter.appendTask(ctx, params.TaskInfo) if err == nil { // Signal the task reader only if appendTask succeeded c.taskReader.Signal() } return syncMatch, err } isolationGroup, _ := c.getIsolationGroupForTask(ctx, params.TaskInfo) // active task, try sync match first syncMatch, err = c.trySyncMatch(ctx, params, isolationGroup) if syncMatch { e.EventName = "SyncMatched so not persisted" event.Log(e) return syncMatch, err } if params.ActivityTaskDispatchInfo != nil { return syncMatch, errRemoteSyncMatchFailed } if isForwarded { // forwarded from child partition - only do sync match // child partition will persist the task when sync match fails e.EventName = "Could not SyncMatched Forwarded Task so not persisted" event.Log(e) return syncMatch, errRemoteSyncMatchFailed } e.EventName = "Task Sent to Writer" event.Log(e) if _, err := c.taskWriter.appendTask(ctx, params.TaskInfo); err != nil { return syncMatch, err } c.taskReader.Signal() return syncMatch, nil } // DispatchTask dispatches a task to a poller on the active side. When there are no pollers to pick // up the task or if the rate limit is exceeded, this method will return error. Task // *will not* be persisted to db. On the passive side, dispatches the task to the taskCompleter; it will attempt // to complete the task if it has already been started. func (c *taskListManagerImpl) DispatchTask(ctx context.Context, task *InternalTask) error { // check if this is the active cluster for the domain domainEntry, err := c.domainCache.GetDomainByID(task.Event.TaskInfo.DomainID) if err != nil { return fmt.Errorf("unable to fetch domain from cache: %w", err) } if domainEntry.IsActiveIn(c.clusterMetadata.GetCurrentClusterName()) { c.logger.Debug("Domain is active in the current cluster, dispatching task", tag.WorkflowDomainID(task.Event.TaskInfo.DomainID), tag.WorkflowDomainName(domainEntry.GetInfo().Name), tag.WorkflowID(task.Event.TaskInfo.WorkflowID), tag.WorkflowRunID(task.Event.TaskInfo.RunID), ) return c.matcher.MustOffer(ctx, task) } // optional configuration to enable cleanup of tasks, in the standby cluster, that have already been started if c.config.EnableStandbyTaskCompletion() && !domainEntry.GetReplicationConfig().IsActiveActive() { if err := c.taskCompleter.CompleteTaskIfStarted(ctx, task); err != nil { if errors.Is(err, errDomainIsActive) { return c.matcher.MustOffer(ctx, task) } return err } return nil } return c.matcher.MustOffer(ctx, task) } // DispatchQueryTask will dispatch query to local or remote poller. If forwarded then result or error is returned, // if dispatched to local poller then nil and nil is returned. func (c *taskListManagerImpl) DispatchQueryTask( ctx context.Context, taskID string, request *types.MatchingQueryWorkflowRequest, ) (*types.MatchingQueryWorkflowResponse, error) { c.startWG.Wait() task := newInternalQueryTask(taskID, request) return c.matcher.OfferQuery(ctx, task) } // GetTask blocks waiting for a task. // Returns error when context deadline is exceeded // maxDispatchPerSecond is the max rate at which tasks are allowed // to be dispatched from this task list to pollers func (c *taskListManagerImpl) GetTask( ctx context.Context, maxDispatchPerSecond *float64, ) (*InternalTask, error) { if c.shouldReload() { c.Stop() return nil, ErrNoTasks } c.liveness.MarkAlive() // TODO: consider return early if QPS and backlog count are both 0, // since there is no task to be returned task, err := c.getTask(ctx, maxDispatchPerSecond) if err != nil { return nil, fmt.Errorf("couldn't get task: %w", err) } task.domainName = c.domainName task.BacklogCountHint = c.taskAckManager.GetBacklogCount() return task, nil } func (c *taskListManagerImpl) getTask(ctx context.Context, maxDispatchPerSecond *float64) (*InternalTask, error) { c.emitMisconfiguredPartitionMetrics() // We need to set a shorter timeout than the original ctx; otherwise, by the time ctx deadline is // reached, instead of emptyTask, context timeout error is returned to the frontend by the rpc stack, // which counts against our SLO. By shortening the timeout by a very small amount, the emptyTask can be // returned to the handler before a context timeout error is generated. childCtx, cancel := c.newChildContext(ctx, c.config.LongPollExpirationInterval(), returnEmptyTaskTimeBudget) defer cancel() isolationGroup := IsolationGroupFromContext(ctx) pollerID := PollerIDFromContext(ctx) identity := IdentityFromContext(ctx) rps := -1.0 rpsOverride := c.config.OverrideTaskListRPS() if rpsOverride > 0 { rps = rpsOverride } else if maxDispatchPerSecond != nil { rps = *maxDispatchPerSecond } if rps >= 0 { c.limiter.ReportLimit(rps) } else { rps = c.config.TaskDispatchRPS } c.pollers.StartPoll(pollerID, cancel, &poller.Info{ Identity: identity, IsolationGroup: isolationGroup, RatePerSecond: rps, }) defer c.pollers.EndPoll(pollerID) domainEntry, err := c.domainCache.GetDomainByID(c.taskListID.GetDomainID()) if err != nil { return nil, fmt.Errorf("unable to fetch domain from cache: %w", err) } if !domainEntry.IsActiveIn(c.clusterMetadata.GetCurrentClusterName()) { return c.matcher.PollForQuery(childCtx) } if c.isIsolationMatcherEnabled() { return c.matcher.Poll(childCtx, isolationGroup) } return c.matcher.Poll(childCtx, "") } // GetAllPollerInfo returns all pollers that polled from this tasklist in last few minutes func (c *taskListManagerImpl) GetAllPollerInfo() []*types.PollerInfo { return c.pollers.ListInfo() } // HasPollerAfter checks if there is any poller after a timestamp func (c *taskListManagerImpl) HasPollerAfter(accessTime time.Time) bool { return c.pollers.HasPollerAfter(accessTime) } func (c *taskListManagerImpl) CancelPoller(pollerID string) { if c.pollers.CancelPoll(pollerID) { c.logger.Info("canceled outstanding poller", tag.WorkflowDomainName(c.domainName)) } } // DescribeTaskList returns information about the target tasklist, right now this API returns the // pollers which polled this tasklist in last few minutes and status of tasklist's ackManager // (readLevel, ackLevel, backlogCountHint and taskIDBlock). func (c *taskListManagerImpl) DescribeTaskList(includeTaskListStatus bool) *types.DescribeTaskListResponse { response := &types.DescribeTaskListResponse{ Pollers: c.GetAllPollerInfo(), TaskList: &types.TaskList{ Name: c.taskListID.GetName(), Kind: &c.taskListKind, }, } response.PartitionConfig = c.TaskListPartitionConfig() if !includeTaskListStatus { return response } idBlock := rangeIDToTaskIDBlock(c.db.RangeID(), c.config.RangeSize) isolationGroups := c.config.AllIsolationGroups() pollerCounts := c.pollers.GetCountByIsolationGroup(c.timeSource.Now().Add(-1 * c.config.TaskIsolationPollerWindow())) isolationGroupMetrics := make(map[string]*types.IsolationGroupMetrics, len(isolationGroups)) for _, group := range isolationGroups { isolationGroupMetrics[group] = &types.IsolationGroupMetrics{ NewTasksPerSecond: c.qpsTracker.GroupQPS(group), PollerCount: int64(pollerCounts[group]), } } response.TaskListStatus = &types.TaskListStatus{ ReadLevel: c.taskAckManager.GetReadLevel(), AckLevel: c.taskAckManager.GetAckLevel(), BacklogCountHint: c.taskAckManager.GetBacklogCount(), RatePerSecond: float64(c.limiter.Limit()), TaskIDBlock: &types.TaskIDBlock{ StartID: idBlock.start, EndID: idBlock.end, }, IsolationGroupMetrics: isolationGroupMetrics, NewTasksPerSecond: c.qpsTracker.QPS(), Empty: c.taskAckManager.GetAckLevel() == c.taskWriter.GetMaxReadLevel(), } return response } func (c *taskListManagerImpl) ReleaseBlockedPollers() error { c.stoppedLock.RLock() defer c.stoppedLock.RUnlock() if atomic.LoadInt32(&c.stopped) == 1 { c.logger.Info("Task list manager is already stopped") return errShutdown } c.matcher.DisconnectBlockedPollers() c.matcher.RefreshCancelContext() return nil } func (c *taskListManagerImpl) String() string { buf := new(bytes.Buffer) if c.taskListID.GetType() == persistence.TaskListTypeActivity { buf.WriteString("Activity") } else { buf.WriteString("Decision") } rangeID := c.db.RangeID() fmt.Fprintf(buf, " task list %v\n", c.taskListID.GetName()) fmt.Fprintf(buf, "RangeID=%v\n", rangeID) fmt.Fprintf(buf, "TaskIDBlock=%+v\n", rangeIDToTaskIDBlock(rangeID, c.config.RangeSize)) fmt.Fprintf(buf, "AckLevel=%v\n", c.taskAckManager.GetAckLevel()) fmt.Fprintf(buf, "MaxReadLevel=%v\n", c.taskAckManager.GetReadLevel()) return buf.String() } func (c *taskListManagerImpl) GetTaskListKind() types.TaskListKind { return c.taskListKind } func (c *taskListManagerImpl) TaskListID() *Identifier { return c.taskListID } func (c *taskListManagerImpl) trySyncMatch(ctx context.Context, params AddTaskParams, isolationGroup string) (bool, error) { task := newInternalTask(params.TaskInfo, nil, params.Source, params.ForwardedFrom, true, params.ActivityTaskDispatchInfo, isolationGroup) childCtx := ctx cancel := func() {} waitTime := maxSyncMatchWaitTime if params.ActivityTaskDispatchInfo != nil { waitTime = c.config.ActivityTaskSyncMatchWaitTime(params.ActivityTaskDispatchInfo.WorkflowDomain) } if !task.IsForwarded() { // when task is forwarded from another matching host, we trust the context as is // otherwise, we override to limit the amount of time we can block on sync match childCtx, cancel = c.newChildContext(ctx, waitTime, time.Second) } var matched bool var err error if params.ActivityTaskDispatchInfo != nil { matched, err = c.matcher.OfferOrTimeout(childCtx, c.timeSource.Now(), task) } else { matched, err = c.matcher.Offer(childCtx, task) } cancel() return matched, err } // newChildContext creates a child context with desired timeout. // if tailroom is non-zero, then child context timeout will be // the minOf(parentCtx.Deadline()-tailroom, timeout). Use this // method to create child context when childContext cannot use // all of parent's deadline but instead there is a need to leave // some time for parent to do some post-work func (c *taskListManagerImpl) newChildContext( parent context.Context, timeout time.Duration, tailroom time.Duration, ) (context.Context, context.CancelFunc) { select { case <-parent.Done(): return parent, func() {} default: } deadline, ok := parent.Deadline() if !ok { return context.WithTimeout(parent, timeout) } remaining := time.Until(deadline) - tailroom if remaining < timeout { timeout = time.Duration(max(0, int64(remaining))) } return context.WithTimeout(parent, timeout) } func (c *taskListManagerImpl) isFowardingAllowed(taskList *Identifier, kind types.TaskListKind) bool { return !taskList.IsRoot() && kind != types.TaskListKindSticky } func (c *taskListManagerImpl) isIsolationMatcherEnabled() bool { return c.taskListKind != types.TaskListKindSticky && c.enableIsolation } func (c *taskListManagerImpl) shouldReload() bool { return c.config.EnableTasklistIsolation() != c.enableIsolation } func (c *taskListManagerImpl) getIsolationGroupForTask(ctx context.Context, taskInfo *persistence.TaskInfo) (string, time.Duration) { if !c.enableIsolation || len(taskInfo.PartitionConfig) == 0 || c.taskListKind == types.TaskListKindSticky { return defaultTaskBufferIsolationGroup, noIsolationTimeout } group := taskInfo.PartitionConfig[isolationgroup.GroupKey] if group == defaultTaskBufferIsolationGroup { return defaultTaskBufferIsolationGroup, noIsolationTimeout } isDrained, err := c.isolationState.IsDrained(ctx, c.domainName, group) if err != nil { // if we're unable to get the isolation group, log the error and fallback to no isolation c.logger.Error("Failed to determine whether isolation group is drained", tag.IsolationGroup(group), tag.WorkflowID(taskInfo.WorkflowID), tag.WorkflowRunID(taskInfo.RunID), tag.TaskID(taskInfo.TaskID), tag.Error(err)) c.scope.Tagged(metrics.IsolationGroupTag(group), IsolationLeakCauseError).IncCounter(metrics.TaskIsolationLeakPerTaskList) return defaultTaskBufferIsolationGroup, noIsolationTimeout } if isDrained { c.scope.Tagged(metrics.IsolationGroupTag(group), IsolationLeakCauseGroupDrained).IncCounter(metrics.TaskIsolationLeakPerTaskList) return defaultTaskBufferIsolationGroup, noIsolationTimeout } if _, ok := slices.BinarySearch(c.isolationGroups, group); !ok { c.scope.Tagged(metrics.IsolationGroupTag(group), IsolationLeakCauseGroupUnknown).IncCounter(metrics.TaskIsolationLeakPerTaskList) return defaultTaskBufferIsolationGroup, noIsolationTimeout } if !c.pollers.HasPollerFromIsolationGroupAfter(group, c.timeSource.Now().Add(-1*c.config.TaskIsolationPollerWindow())) { c.scope.Tagged(metrics.IsolationGroupTag(group), IsolationLeakCauseNoRecentPollers).IncCounter(metrics.TaskIsolationLeakPerTaskList) return defaultTaskBufferIsolationGroup, noIsolationTimeout } partition, ok := c.PartitionReadConfig() if !ok || (len(partition.IsolationGroups) != 0 && !slices.Contains(partition.IsolationGroups, group)) { c.scope.Tagged(metrics.IsolationGroupTag(group), IsolationLeakCausePartitionChange).IncCounter(metrics.TaskIsolationLeakPerTaskList) return defaultTaskBufferIsolationGroup, noIsolationTimeout } totalTaskIsolationDuration := c.config.TaskIsolationDuration() taskIsolationDuration := noIsolationTimeout if totalTaskIsolationDuration != noIsolationTimeout { taskLatency := c.timeSource.Now().Sub(taskInfo.CreatedTime) if taskLatency < (totalTaskIsolationDuration - minimumIsolationDuration) { taskIsolationDuration = totalTaskIsolationDuration - taskLatency } else { c.logger.Debug("Leaking task due to taskIsolationDuration expired", tag.IsolationGroup(group), tag.IsolationDuration(taskIsolationDuration), tag.TaskLatency(taskLatency)) c.scope.Tagged(metrics.IsolationGroupTag(group), IsolationLeakCauseExpired).IncCounter(metrics.TaskIsolationLeakPerTaskList) return defaultTaskBufferIsolationGroup, noIsolationTimeout } } return group, taskIsolationDuration } func (c *taskListManagerImpl) emitMisconfiguredPartitionMetrics() { if !c.taskListID.IsRoot() || c.taskListKind == types.TaskListKindSticky { // only emit the metric in root partition of non-sticky tasklist return } if c.config.NumReadPartitions() != c.config.NumWritePartitions() { c.scope.UpdateGauge(metrics.TaskListReadWritePartitionMismatchGauge, 1) } pollerCount := c.pollers.GetCount() if pollerCount < c.config.NumReadPartitions() || pollerCount < c.config.NumWritePartitions() { c.scope.Tagged(metrics.IsolationEnabledTag(c.enableIsolation)).UpdateGauge(metrics.TaskListPollerPartitionMismatchGauge, 1) } } func getTaskListTypeTag(taskListType int) metrics.Tag { switch taskListType { case persistence.TaskListTypeActivity: return taskListActivityTypeTag case persistence.TaskListTypeDecision: return taskListDecisionTypeTag default: return metrics.TaskListTypeTag("") } } func createServiceBusyError(msg string) *types.ServiceBusyError { return &types.ServiceBusyError{Message: msg} } func rangeIDToTaskIDBlock(rangeID, rangeSize int64) taskIDBlock { return taskIDBlock{ start: (rangeID-1)*rangeSize + 1, end: rangeID * rangeSize, } } func toPersistenceConfig(version int64, config *types.TaskListPartitionConfig) *persistence.TaskListPartitionConfig { read := make(map[int]*persistence.TaskListPartition, len(config.ReadPartitions)) write := make(map[int]*persistence.TaskListPartition, len(config.WritePartitions)) for id, p := range config.ReadPartitions { read[id] = &persistence.TaskListPartition{IsolationGroups: p.IsolationGroups} } for id, p := range config.WritePartitions { write[id] = &persistence.TaskListPartition{IsolationGroups: p.IsolationGroups} } return &persistence.TaskListPartitionConfig{ Version: version, ReadPartitions: read, WritePartitions: write, } } func validatePartitionConfig(config *types.TaskListPartitionConfig) error { if len(config.ReadPartitions) < 1 { return &types.BadRequestError{Message: "read partitions < 1"} } if len(config.WritePartitions) < 1 { return &types.BadRequestError{Message: "write partitions < 1"} } if len(config.ReadPartitions) < len(config.WritePartitions) { return &types.BadRequestError{Message: fmt.Sprintf("read partitions (%d) < write partitions (%d)", len(config.ReadPartitions), len(config.WritePartitions))} } if _, ok := config.ReadPartitions[0]; !ok { return &types.BadRequestError{Message: "the root partition must always be in read partitions"} } if _, ok := config.WritePartitions[0]; !ok { return &types.BadRequestError{Message: "the root partition must always be in write partitions"} } for id := range config.WritePartitions { if _, ok := config.ReadPartitions[id]; !ok { return &types.BadRequestError{Message: fmt.Sprintf("partition %d included in write but not read", id)} } } return nil } func newTaskListConfig(id *Identifier, cfg *config.Config, domainName string) *config.TaskListConfig { taskListName := id.GetName() taskType := id.GetType() return &config.TaskListConfig{ RangeSize: cfg.RangeSize, ReadRangeSize: cfg.ReadRangeSize, AllIsolationGroups: cfg.AllIsolationGroups, EnableTasklistIsolation: func() bool { return cfg.EnableTasklistIsolation(domainName) }, ActivityTaskSyncMatchWaitTime: cfg.ActivityTaskSyncMatchWaitTime, GetTasksBatchSize: func() int { return cfg.GetTasksBatchSize(domainName, taskListName, taskType) }, UpdateAckInterval: func() time.Duration { return cfg.UpdateAckInterval(domainName, taskListName, taskType) }, IdleTasklistCheckInterval: func() time.Duration { return cfg.IdleTasklistCheckInterval(domainName, taskListName, taskType) }, MaxTasklistIdleTime: func() time.Duration { return cfg.MaxTasklistIdleTime(domainName, taskListName, taskType) }, MinTaskThrottlingBurstSize: func() int { return cfg.MinTaskThrottlingBurstSize(domainName, taskListName, taskType) }, EnableSyncMatch: func() bool { return cfg.EnableSyncMatch(domainName, taskListName, taskType) }, LongPollExpirationInterval: func() time.Duration { return cfg.LongPollExpirationInterval(domainName, taskListName, taskType) }, MaxTaskDeleteBatchSize: func() int { return cfg.MaxTaskDeleteBatchSize(domainName, taskListName, taskType) }, OutstandingTaskAppendsThreshold: func() int { return cfg.OutstandingTaskAppendsThreshold(domainName, taskListName, taskType) }, MaxTaskBatchSize: func() int { return cfg.MaxTaskBatchSize(domainName, taskListName, taskType) }, NumWritePartitions: func() int { return max(1, cfg.NumTasklistWritePartitions(domainName, taskListName, taskType)) }, NumReadPartitions: func() int { return max(1, cfg.NumTasklistReadPartitions(domainName, taskListName, taskType)) }, EnableGetNumberOfPartitionsFromCache: func() bool { return cfg.EnableGetNumberOfPartitionsFromCache(domainName, id.GetRoot(), taskType) }, AppendTaskTimeout: func() time.Duration { return cfg.AppendTaskTimeout(domainName, taskListName, taskType) }, AsyncTaskDispatchTimeout: func() time.Duration { return cfg.AsyncTaskDispatchTimeout(domainName, taskListName, taskType) }, LocalPollWaitTime: func() time.Duration { return cfg.LocalPollWaitTime(domainName, taskListName, taskType) }, LocalTaskWaitTime: func() time.Duration { return cfg.LocalTaskWaitTime(domainName, taskListName, taskType) }, PartitionUpscaleRPS: func() int { return cfg.PartitionUpscaleRPS(domainName, taskListName, taskType) }, PartitionDownscaleFactor: func() float64 { return cfg.PartitionDownscaleFactor(domainName, taskListName, taskType) }, PartitionUpscaleSustainedDuration: func() time.Duration { return cfg.PartitionUpscaleSustainedDuration(domainName, taskListName, taskType) }, PartitionDownscaleSustainedDuration: func() time.Duration { return cfg.PartitionDownscaleSustainedDuration(domainName, taskListName, taskType) }, AdaptiveScalerUpdateInterval: func() time.Duration { return cfg.AdaptiveScalerUpdateInterval(domainName, taskListName, taskType) }, EnablePartitionIsolationGroupAssignment: func() bool { return cfg.EnablePartitionIsolationGroupAssignment(domainName, taskListName, taskType) }, IsolationGroupUpscaleSustainedDuration: func() time.Duration { return cfg.IsolationGroupUpscaleSustainedDuration(domainName, taskListName, taskType) }, IsolationGroupDownscaleSustainedDuration: func() time.Duration { return cfg.IsolationGroupDownscaleSustainedDuration(domainName, taskListName, taskType) }, IsolationGroupHasPollersSustainedDuration: func() time.Duration { return cfg.IsolationGroupHasPollersSustainedDuration(domainName, taskListName, taskType) }, IsolationGroupNoPollersSustainedDuration: func() time.Duration { return cfg.IsolationGroupNoPollersSustainedDuration(domainName, taskListName, taskType) }, IsolationGroupsPerPartition: func() int { return cfg.IsolationGroupsPerPartition(domainName, taskListName, taskType) }, QPSTrackerInterval: func() time.Duration { return cfg.QPSTrackerInterval(domainName, taskListName, taskType) }, OverrideTaskListRPS: func() float64 { return cfg.OverrideTaskListRPS(domainName, taskListName, taskType) }, EnableAdaptiveScaler: func() bool { return cfg.EnableAdaptiveScaler(domainName, taskListName, taskType) }, EnablePartitionEmptyCheck: func() bool { return cfg.EnablePartitionEmptyCheck(domainName, taskListName, taskType) }, TaskIsolationDuration: func() time.Duration { return cfg.TaskIsolationDuration(domainName, taskListName, taskType) }, TaskIsolationPollerWindow: func() time.Duration { return cfg.TaskIsolationPollerWindow(domainName, taskListName, taskType) }, ForwarderConfig: config.ForwarderConfig{ ForwarderMaxOutstandingPolls: func() int { return cfg.ForwarderMaxOutstandingPolls(domainName, taskListName, taskType) }, ForwarderMaxOutstandingTasks: func() int { return cfg.ForwarderMaxOutstandingTasks(domainName, taskListName, taskType) }, ForwarderMaxRatePerSecond: func() int { return cfg.ForwarderMaxRatePerSecond(domainName, taskListName, taskType) }, ForwarderMaxChildrenPerNode: func() int { return max(1, cfg.ForwarderMaxChildrenPerNode(domainName, taskListName, taskType)) }, }, HostName: cfg.HostName, TaskDispatchRPS: cfg.TaskDispatchRPS, TaskDispatchRPSTTL: cfg.TaskDispatchRPSTTL, MaxTimeBetweenTaskDeletes: cfg.MaxTimeBetweenTaskDeletes, EnableStandbyTaskCompletion: func() bool { return cfg.EnableStandbyTaskCompletion(domainName, taskListName, taskType) }, EnableClientAutoConfig: func() bool { return cfg.EnableClientAutoConfig(domainName, taskListName, taskType) }, } } func IdentityFromContext(ctx context.Context) string { val, ok := ctx.Value(identityCtxKey{}).(string) if !ok { return "" } return val } func ContextWithIdentity(ctx context.Context, identity string) context.Context { return context.WithValue(ctx, identityCtxKey{}, identity) } func PollerIDFromContext(ctx context.Context) string { val, ok := ctx.Value(pollerIDCtxKey{}).(string) if !ok { return "" } return val } func ContextWithPollerID(ctx context.Context, pollerID string) context.Context { return context.WithValue(ctx, pollerIDCtxKey{}, pollerID) } func IsolationGroupFromContext(ctx context.Context) string { val, ok := ctx.Value(isolationGroupCtxKey{}).(string) if !ok { return "" } return val } func ContextWithIsolationGroup(ctx context.Context, isolationGroup string) context.Context { return context.WithValue(ctx, isolationGroupCtxKey{}, isolationGroup) } func validateParams(p ManagerParams) (err error) { if p.DomainCache == nil { return errors.New("ManagerParams.DomainCache is required") } if p.Logger == nil { return errors.New("ManagerParams.Logger is required") } if p.MetricsClient == nil { return errors.New("ManagerParams.MetricsClient is required") } if p.TaskManager == nil { return errors.New("ManagerParams.TaskManager is required") } if p.IsolationState == nil { return errors.New("ManagerParams.IsolationState is required") } if p.MatchingClient == nil { return errors.New("ManagerParams.MatchingClient is required") } if p.Registry == nil { return errors.New("ManagerParams.Registry is required") } if p.TaskList == nil { return errors.New("ManagerParams.TaskList is required") } if p.Cfg == nil { return errors.New("ManagerParams.Cfg is required") } if p.TimeSource == nil { return errors.New("ManagerParams.TimeSource is required") } if p.HistoryService == nil { return errors.New("ManagerParams.HistoryService is required") } return nil } ================================================ FILE: service/matching/tasklist/task_list_manager_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "context" "errors" "strconv" "sync" "sync/atomic" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/goleak" "go.uber.org/mock/gomock" "golang.org/x/sync/errgroup" "golang.org/x/time/rate" "github.com/uber/cadence/client/history" "github.com/uber/cadence/client/matching" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" commonConfig "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/stats" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/poller" ) type mockDeps struct { mockDomainCache *cache.MockDomainCache mockTaskManager *persistence.MockTaskManager mockIsolationState *isolationgroup.MockState mockMatchingClient *matching.MockClient mockTimeSource clock.MockedTimeSource dynamicClient dynamicconfig.Client } var ( testIsolationGroups = []string{"datacenterA", "datacenterB"} ) func setupMocksForTaskListManager(t *testing.T, taskListID *Identifier, taskListKind types.TaskListKind) (*taskListManagerImpl, *mockDeps) { ctrl := gomock.NewController(t) dynamicClient := dynamicconfig.NewInMemoryClient() logger := testlogger.New(t) metricsClient := metrics.NewNoopMetricsClient() clusterMetadata := cluster.GetTestClusterMetadata(true) deps := &mockDeps{ mockDomainCache: cache.NewMockDomainCache(ctrl), mockTaskManager: persistence.NewMockTaskManager(ctrl), mockIsolationState: isolationgroup.NewMockState(ctrl), mockMatchingClient: matching.NewMockClient(ctrl), mockTimeSource: clock.NewMockedTimeSource(), dynamicClient: dynamicClient, } deps.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("domainName", nil).Times(1) config := config.NewConfig(dynamicconfig.NewCollection(dynamicClient, logger), "hostname", commonConfig.RPC{}, getIsolationgroupsHelper) mockHistoryService := history.NewMockClient(ctrl) mockRegistry := NewMockTaskListRegistry(ctrl) mockRegistry.EXPECT().Unregister(gomock.Any()).AnyTimes() params := ManagerParams{ DomainCache: deps.mockDomainCache, Logger: logger, MetricsClient: metricsClient, TaskManager: deps.mockTaskManager, ClusterMetadata: clusterMetadata, IsolationState: deps.mockIsolationState, MatchingClient: deps.mockMatchingClient, Registry: mockRegistry, TaskList: taskListID, TaskListKind: taskListKind, Cfg: config, TimeSource: deps.mockTimeSource, CreateTime: deps.mockTimeSource.Now(), HistoryService: mockHistoryService, } tlm, err := NewManager(params) require.NoError(t, err) return tlm.(*taskListManagerImpl), deps } func defaultTestConfig() *config.Config { config := config.NewConfig(dynamicconfig.NewNopCollection(), "some random hostname", commonConfig.RPC{}, getIsolationgroupsHelper) config.LongPollExpirationInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(100 * time.Millisecond) config.MaxTaskDeleteBatchSize = dynamicproperties.GetIntPropertyFilteredByTaskListInfo(1) config.AllIsolationGroups = getIsolationgroupsHelper config.GetTasksBatchSize = dynamicproperties.GetIntPropertyFilteredByTaskListInfo(10) config.AsyncTaskDispatchTimeout = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(10 * time.Millisecond) config.LocalTaskWaitTime = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(time.Millisecond) return config } func TestDeliverBufferTasks(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) tests := []func(tlm *taskListManagerImpl){ func(tlm *taskListManagerImpl) { tlm.taskReader.cancelFunc() }, func(tlm *taskListManagerImpl) { tlm.limiter.ReportLimit(0.1) tlm.taskReader.taskBuffers[defaultTaskBufferIsolationGroup] <- &persistence.TaskInfo{} err := tlm.matcher.(*taskMatcherImpl).ratelimit(context.Background()) // consume the token assert.NoError(t, err) tlm.taskReader.cancelFunc() }, } for _, test := range tests { tlm := createTestTaskListManager(t, logger, controller) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() tlm.taskReader.dispatchBufferedTasks(defaultTaskBufferIsolationGroup) }() test(tlm) // dispatchBufferedTasks should stop after invocation of the test function wg.Wait() } } func TestTaskListString(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) tlm := createTestTaskListManager(t, logger, controller) got := tlm.String() want := "Activity task list tl\nRangeID=0\nTaskIDBlock={start:-99999 end:0}\nAckLevel=-1\nMaxReadLevel=-1\n" assert.Equal(t, want, got) } func TestDeliverBufferTasks_NoPollers(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) tlm := createTestTaskListManager(t, logger, controller) tlm.taskReader.taskBuffers[defaultTaskBufferIsolationGroup] <- &persistence.TaskInfo{} var wg sync.WaitGroup wg.Add(1) go func() { tlm.taskReader.dispatchBufferedTasks("") wg.Done() }() time.Sleep(100 * time.Millisecond) // let go routine run first and block on tasksForPoll tlm.taskReader.cancelFunc() wg.Wait() } func TestReadLevelForAllExpiredTasksInBatch(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) tlm := createTestTaskListManager(t, logger, controller) tlm.db.rangeID = int64(1) tlm.taskAckManager.SetAckLevel(0) tlm.taskAckManager.SetReadLevel(0) require.Equal(t, int64(0), tlm.taskAckManager.GetAckLevel()) require.Equal(t, int64(0), tlm.taskAckManager.GetReadLevel()) // Add all expired tasks tasks := []*persistence.TaskInfo{ { TaskID: 11, Expiry: time.Now().Add(-time.Minute), CreatedTime: time.Now().Add(-time.Hour), }, { TaskID: 12, Expiry: time.Now().Add(-time.Minute), CreatedTime: time.Now().Add(-time.Hour), }, } require.True(t, tlm.taskReader.addTasksToBuffer(tasks)) require.Equal(t, int64(0), tlm.taskAckManager.GetAckLevel()) require.Equal(t, int64(12), tlm.taskAckManager.GetReadLevel()) // Now add a mix of valid and expired tasks require.True(t, tlm.taskReader.addTasksToBuffer([]*persistence.TaskInfo{ { TaskID: 13, Expiry: time.Now().Add(-time.Minute), CreatedTime: time.Now().Add(-time.Hour), }, { TaskID: 14, Expiry: time.Now().Add(time.Hour), CreatedTime: time.Now().Add(time.Minute), }, })) require.Equal(t, int64(0), tlm.taskAckManager.GetAckLevel()) require.Equal(t, int64(14), tlm.taskAckManager.GetReadLevel()) } func createTestTaskListManager(t *testing.T, logger log.Logger, controller *gomock.Controller) *taskListManagerImpl { return createTestTaskListManagerWithConfig(t, logger, controller, defaultTestConfig(), clock.NewMockedTimeSource()) } func createTestTaskListManagerWithConfig(t *testing.T, logger log.Logger, controller *gomock.Controller, cfg *config.Config, timeSource clock.TimeSource) *taskListManagerImpl { tm := NewTestTaskManager(t, logger, timeSource) mockIsolationState := isolationgroup.NewMockState(controller) mockIsolationState.EXPECT().IsDrained(gomock.Any(), "domainName", gomock.Any()).Return(false, nil).AnyTimes() mockDomainCache := cache.NewMockDomainCache(controller) mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.CreateDomainCacheEntry("domainName"), nil).AnyTimes() mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("domainName", nil).AnyTimes() mockMatchingClient := matching.NewMockClient(controller) mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() mockHistoryService := history.NewMockClient(controller) mockRegistry := NewMockTaskListRegistry(controller) mockRegistry.EXPECT().Unregister(gomock.Any()).AnyTimes() tl := "tl" dID := "domain" tlID, err := NewIdentifier(dID, tl, persistence.TaskListTypeActivity) if err != nil { panic(err) } params := ManagerParams{ DomainCache: mockDomainCache, Logger: logger, MetricsClient: metrics.NewClient(tally.NoopScope, metrics.Matching, metrics.HistogramMigration{}), TaskManager: tm, ClusterMetadata: cluster.GetTestClusterMetadata(true), IsolationState: mockIsolationState, MatchingClient: mockMatchingClient, Registry: mockRegistry, TaskList: tlID, TaskListKind: types.TaskListKindNormal, Cfg: cfg, TimeSource: timeSource, CreateTime: timeSource.Now(), HistoryService: mockHistoryService, } tlMgr, err := NewManager(params) if err != nil { logger.Fatal("error when createTestTaskListManager", tag.Error(err)) } return tlMgr.(*taskListManagerImpl) } func TestTaskListManagerRegistryNotification(t *testing.T) { ctrl := gomock.NewController(t) mockRegistry := NewMockTaskListRegistry(ctrl) // Create a minimal task list manager for testing taskListID := NewTestTaskListID(t, uuid.New(), "test-tasklist", 0) tlm, deps := setupMocksForTaskListManager(t, taskListID, types.TaskListKindNormal) // Set up expectations for Start/Stop deps.mockTaskManager.EXPECT().LeaseTaskList(gomock.Any(), gomock.Any()).Return(&persistence.LeaseTaskListResponse{TaskListInfo: &persistence.TaskListInfo{}}, nil).AnyTimes() deps.mockTaskManager.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any()).Return(&persistence.UpdateTaskListResponse{}, nil).AnyTimes() // Replace the registry with our mock tlm.registry = mockRegistry // Expect Unregister to be called exactly once with the manager instance mockRegistry.EXPECT().Unregister(tlm).Times(1) // Start the manager err := tlm.Start(context.Background()) require.NoError(t, err) // Stop should call Unregister tlm.Stop() // Verify the manager stopped require.Equal(t, int32(1), tlm.stopped) // Second call should be a no-op tlm.Stop() require.Equal(t, int32(1), tlm.stopped) } func TestDescribeTaskList(t *testing.T) { // Magic values hardcoded in matching/config.go. Not much of a config :( defaultRps := 100000.0 defaultRangeSize := 100000 startedID := int64(1) firstIDBlock := &types.TaskIDBlock{ StartID: startedID, EndID: int64(defaultRangeSize), } cases := []struct { name string includeStatus bool pollers map[string]poller.Info allowance func(ctrl *gomock.Controller, impl *taskListManagerImpl) expectedStatus *types.TaskListStatus expectedConfig *types.TaskListPartitionConfig }{ { name: "no status, pollers, or config", }, { name: "no status, with config", allowance: func(_ *gomock.Controller, impl *taskListManagerImpl) { err := impl.RefreshTaskListPartitionConfig(context.Background(), &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(3), WritePartitions: partitions(2), }) require.NoError(t, err) }, expectedConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(3), WritePartitions: partitions(2), }, }, { name: "no status, with pollers", pollers: map[string]poller.Info{ "pollerID": { Identity: "pollerIdentity", RatePerSecond: 1.0, IsolationGroup: "a", }, }, }, { name: "with status", includeStatus: true, expectedStatus: &types.TaskListStatus{ RatePerSecond: defaultRps, TaskIDBlock: firstIDBlock, IsolationGroupMetrics: map[string]*types.IsolationGroupMetrics{ "datacenterA": {}, "datacenterB": {}, }, Empty: true, }, }, { name: "with status, tasks completed", includeStatus: true, allowance: func(_ *gomock.Controller, tlm *taskListManagerImpl) { for i := startedID; i < 11; i++ { err := tlm.taskAckManager.ReadItem(i) require.NoError(t, err) } for i := startedID; i < 5; i++ { tlm.taskAckManager.AckItem(i) } }, expectedStatus: &types.TaskListStatus{ BacklogCountHint: 6, ReadLevel: 10, AckLevel: 4, RatePerSecond: defaultRps, TaskIDBlock: firstIDBlock, IsolationGroupMetrics: map[string]*types.IsolationGroupMetrics{ "datacenterA": {}, "datacenterB": {}, }, Empty: false, }, }, { name: "with status, pollers and metrics", includeStatus: true, pollers: map[string]poller.Info{ "a-1": { Identity: "a1Identity", RatePerSecond: 1.0, IsolationGroup: "datacenterA", }, }, allowance: func(ctrl *gomock.Controller, impl *taskListManagerImpl) { mockQPS := stats.NewMockQPSTrackerGroup(ctrl) mockQPS.EXPECT().GroupQPS("datacenterA").Return(float64(75.0)) mockQPS.EXPECT().GroupQPS("datacenterB").Return(float64(25.0)) mockQPS.EXPECT().QPS().Return(float64(100.0)) impl.qpsTracker = mockQPS impl.limiter.ReportLimit(1.0) }, expectedStatus: &types.TaskListStatus{ RatePerSecond: 1.0, // From poller TaskIDBlock: firstIDBlock, NewTasksPerSecond: 100, IsolationGroupMetrics: map[string]*types.IsolationGroupMetrics{ "datacenterA": { PollerCount: 1, NewTasksPerSecond: 75.0, }, "datacenterB": { PollerCount: 0, NewTasksPerSecond: 25.0, }, }, Empty: true, }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { expectedTl := &types.TaskList{Name: "tl", Kind: types.TaskListKindNormal.Ptr()} controller := gomock.NewController(t) logger := testlogger.New(t) tlm := createTestTaskListManager(t, logger, controller) tlm.db.rangeID = int64(1) tlm.taskAckManager.SetAckLevel(0) tlm.startWG.Done() expectedPollers := make([]*types.PollerInfo, 0, len(tc.pollers)) for id, info := range tc.pollers { tlm.pollers.StartPoll(id, func() {}, &info) expectedPollers = append(expectedPollers, &types.PollerInfo{ LastAccessTime: common.Int64Ptr(tlm.timeSource.Now().UnixNano()), Identity: info.Identity, RatePerSecond: info.RatePerSecond, }) } if tc.allowance != nil { tc.allowance(controller, tlm) } result := tlm.DescribeTaskList(tc.includeStatus) assert.Equal(t, expectedTl, result.TaskList) assert.Equal(t, tc.expectedStatus, result.TaskListStatus) assert.Equal(t, tc.expectedConfig, result.PartitionConfig) assert.ElementsMatch(t, expectedPollers, result.Pollers) }) } } func TestQueriesPerSecond(t *testing.T) { testCases := []struct { name string mockSetup func(ctrl *gomock.Controller, tlm *taskListManagerImpl) expectedQPS float64 }{ { name: "returns QPS from tracker", mockSetup: func(ctrl *gomock.Controller, tlm *taskListManagerImpl) { mockQPS := stats.NewMockQPSTrackerGroup(ctrl) mockQPS.EXPECT().QPS().Return(float64(42.5)) tlm.qpsTracker = mockQPS }, expectedQPS: 42.5, }, { name: "returns zero QPS", mockSetup: func(ctrl *gomock.Controller, tlm *taskListManagerImpl) { mockQPS := stats.NewMockQPSTrackerGroup(ctrl) mockQPS.EXPECT().QPS().Return(float64(0)) tlm.qpsTracker = mockQPS }, expectedQPS: 0, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) logger := testlogger.New(t) tlm := createTestTaskListManager(t, logger, ctrl) tc.mockSetup(ctrl, tlm) actualQPS := tlm.QueriesPerSecond() assert.Equal(t, tc.expectedQPS, actualQPS) }) } } func TestCheckIdleTaskList(t *testing.T) { defer goleak.VerifyNone(t) cfg := config.NewConfig(dynamicconfig.NewNopCollection(), "some random hostname", commonConfig.RPC{}, getIsolationgroupsHelper) cfg.IdleTasklistCheckInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(10 * time.Millisecond) t.Run("Idle task-list", func(t *testing.T) { ctrl := gomock.NewController(t) tlm := createTestTaskListManagerWithConfig(t, testlogger.New(t), ctrl, cfg, clock.NewRealTimeSource()) require.NoError(t, tlm.Start(context.Background())) require.EqualValues(t, 0, atomic.LoadInt32(&tlm.stopped), "idle check interval had not passed yet") time.Sleep(20 * time.Millisecond) require.EqualValues(t, 1, atomic.LoadInt32(&tlm.stopped), "idle check interval should have pass") }) t.Run("Active poll-er", func(t *testing.T) { ctrl := gomock.NewController(t) tlm := createTestTaskListManagerWithConfig(t, testlogger.New(t), ctrl, cfg, clock.NewRealTimeSource()) require.NoError(t, tlm.Start(context.Background())) ctx, cancel := context.WithTimeout(context.Background(), time.Second) _, _ = tlm.GetTask(ctx, nil) cancel() // task list manager should have been stopped, // but GetTask extends auto-stop until the next check-idle-task-list-interval time.Sleep(6 * time.Millisecond) require.EqualValues(t, 0, atomic.LoadInt32(&tlm.stopped)) time.Sleep(20 * time.Millisecond) require.EqualValues(t, 1, atomic.LoadInt32(&tlm.stopped), "idle check interval should have pass") }) t.Run("Active adding task", func(t *testing.T) { domainID := uuid.New() workflowID := uuid.New() runID := uuid.New() addTaskParam := AddTaskParams{ TaskInfo: &persistence.TaskInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, ScheduleID: 2, ScheduleToStartTimeoutSeconds: 5, CreatedTime: time.Now(), }, } ctrl := gomock.NewController(t) tlm := createTestTaskListManagerWithConfig(t, testlogger.New(t), ctrl, cfg, clock.NewRealTimeSource()) require.NoError(t, tlm.Start(context.Background())) time.Sleep(8 * time.Millisecond) ctx, cancel := context.WithTimeout(context.Background(), time.Second) _, err := tlm.AddTask(ctx, addTaskParam) require.NoError(t, err) cancel() // task list manager should have been stopped, // but AddTask extends auto-stop until the next check-idle-task-list-interval time.Sleep(6 * time.Millisecond) require.EqualValues(t, 0, atomic.LoadInt32(&tlm.stopped)) time.Sleep(20 * time.Millisecond) require.EqualValues(t, 1, atomic.LoadInt32(&tlm.stopped), "idle check interval should have pass") }) } func TestAddTaskStandby(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) cfg := config.NewConfig(dynamicconfig.NewNopCollection(), "some random hostname", commonConfig.RPC{}, getIsolationgroupsHelper) cfg.IdleTasklistCheckInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(10 * time.Millisecond) tlm := createTestTaskListManagerWithConfig(t, logger, controller, cfg, clock.NewMockedTimeSource()) require.NoError(t, tlm.Start(context.Background())) // stop taskWriter so that we can check if there's any call to it // otherwise the task persist process is async and hard to test tlm.taskWriter.Stop() domainID := uuid.New() workflowID := "some random workflowID" runID := "some random runID" addTaskParam := AddTaskParams{ TaskInfo: &persistence.TaskInfo{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, ScheduleID: 2, ScheduleToStartTimeoutSeconds: 5, CreatedTime: time.Now(), }, } testStandbyDomainEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: domainID, Name: "some random domain name"}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestAlternativeClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, ) mockDomainCache := tlm.domainCache.(*cache.MockDomainCache) mockDomainCache.EXPECT().GetDomainByID(domainID).Return(testStandbyDomainEntry, nil).AnyTimes() syncMatch, err := tlm.AddTask(context.Background(), addTaskParam) require.Equal(t, errShutdown, err) // task writer was stopped above require.False(t, syncMatch) addTaskParam.ForwardedFrom = "from child partition" syncMatch, err = tlm.AddTask(context.Background(), addTaskParam) require.Error(t, err) // should not persist the task require.False(t, syncMatch) } // return a client side tasklist throttle error from the rate limiter. // The expected behaviour is to retry func TestRateLimitErrorsFromTasklistDispatch(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) config := defaultTestConfig() config.EnableTasklistIsolation = func(domain string) bool { return true } tlm := createTestTaskListManagerWithConfig(t, logger, controller, config, clock.NewMockedTimeSource()) tlm.taskReader.dispatchTask = func(ctx context.Context, task *InternalTask) error { return ErrTasklistThrottled } tlm.taskReader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return "datacenterA", -1 } breakDispatcher, breakRetryLoop := tlm.taskReader.dispatchSingleTaskFromBuffer(&persistence.TaskInfo{}) assert.False(t, breakDispatcher) assert.False(t, breakRetryLoop) } // This is a bit of a strange unit-test as it's // ensuring that invalid behaviour is handled defensively. // It *should never be the case* that the isolation group tries to // dispatch to a buffer that isn't there, however, if it does, we want to just // log this, emit a metric and fallback to the default isolation group. func TestMisconfiguredZoneDoesNotBlock(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) config := defaultTestConfig() config.EnableTasklistIsolation = func(domain string) bool { return true } tlm := createTestTaskListManagerWithConfig(t, logger, controller, config, clock.NewMockedTimeSource()) invalidIsolationGroup := "invalid" dispatched := make(map[string]int) // record dispatched isolation group tlm.taskReader.dispatchTask = func(ctx context.Context, task *InternalTask) error { dispatched[task.isolationGroup] = dispatched[task.isolationGroup] + 1 return nil } tlm.taskReader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return invalidIsolationGroup, -1 } maxBufferSize := config.GetTasksBatchSize("", "", 0) - 1 for i := 0; i < maxBufferSize; i++ { breakDispatcher, breakRetryLoop := tlm.taskReader.dispatchSingleTaskFromBuffer(&persistence.TaskInfo{}) assert.False(t, breakDispatcher, "dispatch isn't shutting down") assert.True(t, breakRetryLoop, "should be able to successfully dispatch all these tasks to the default isolation group") } // We should see them all being redirected assert.Equal(t, dispatched[""], maxBufferSize) // we should see none in the returned isolation group assert.Equal(t, dispatched[invalidIsolationGroup], 0) // ok, and here we try and ensure that this *does not block // and instead complains and live-retries breakDispatcher, breakRetryLoop := tlm.taskReader.dispatchSingleTaskFromBuffer(&persistence.TaskInfo{}) assert.False(t, breakDispatcher, "dispatch isn't shutting down") assert.True(t, breakRetryLoop, "task should be dispatched to default channel") } func TestGetIsolationGroupForTask(t *testing.T) { defaultAvailableIsolationGroups := []string{ "a", "b", "c", } taskIsolationPollerWindow := time.Second * 10 testCases := []struct { name string taskIsolationGroup string taskIsolationDuration time.Duration taskLatency time.Duration availableIsolationGroups []string recentPollers []string expectedGroup string expectedDuration time.Duration drainedGroups map[string]bool isolationStateErr error disableTaskIsolation bool partitionConfig *types.TaskListPartition }{ { name: "success - recent poller allows group", taskIsolationGroup: "a", availableIsolationGroups: defaultAvailableIsolationGroups, expectedGroup: "a", expectedDuration: 0, recentPollers: []string{"a"}, }, { name: "success - with isolation duration", taskIsolationGroup: "b", taskIsolationDuration: time.Second, availableIsolationGroups: defaultAvailableIsolationGroups, expectedGroup: "b", expectedDuration: time.Second, recentPollers: []string{"b"}, }, { name: "success - low task latency", taskIsolationGroup: "a", taskIsolationDuration: time.Second, taskLatency: time.Millisecond * 300, availableIsolationGroups: defaultAvailableIsolationGroups, expectedGroup: "a", expectedDuration: time.Second - (time.Millisecond * 300), recentPollers: []string{"a"}, }, { name: "success - other group drained", taskIsolationGroup: "a", availableIsolationGroups: defaultAvailableIsolationGroups, expectedGroup: "a", expectedDuration: 0, recentPollers: []string{"a"}, drainedGroups: map[string]bool{ "b": true, }, }, { name: "success - right partition", taskIsolationGroup: "a", availableIsolationGroups: defaultAvailableIsolationGroups, expectedGroup: "a", expectedDuration: 0, recentPollers: []string{"a"}, partitionConfig: &types.TaskListPartition{IsolationGroups: []string{"a", "b", "c", "d"}}, }, { name: "leak - no recent pollers", taskIsolationGroup: "a", availableIsolationGroups: defaultAvailableIsolationGroups, expectedGroup: "", expectedDuration: 0, recentPollers: nil, }, { name: "leak - no matching recent poller", taskIsolationGroup: "a", taskIsolationDuration: time.Second, availableIsolationGroups: defaultAvailableIsolationGroups, expectedGroup: "", expectedDuration: 0, recentPollers: []string{"b"}, }, { name: "leak - unknown group", taskIsolationGroup: "a", taskIsolationDuration: time.Second, availableIsolationGroups: []string{"b"}, expectedGroup: "", expectedDuration: 0, }, { name: "leak - unknown group even with recent poller", taskIsolationGroup: "a", taskIsolationDuration: time.Second, availableIsolationGroups: []string{"b"}, expectedGroup: "", expectedDuration: 0, recentPollers: []string{"a"}, }, { name: "leak - task latency", taskIsolationGroup: "a", taskIsolationDuration: time.Second, taskLatency: time.Second, availableIsolationGroups: defaultAvailableIsolationGroups, recentPollers: []string{"a"}, expectedGroup: "", expectedDuration: 0, }, { name: "leak - task latency close to taskIsolationDuration", taskIsolationGroup: "a", taskIsolationDuration: time.Second, taskLatency: time.Second - minimumIsolationDuration, availableIsolationGroups: defaultAvailableIsolationGroups, recentPollers: []string{"a"}, expectedGroup: "", expectedDuration: 0, }, { name: "leak - group drained", taskIsolationGroup: "a", taskIsolationDuration: time.Second, availableIsolationGroups: defaultAvailableIsolationGroups, recentPollers: []string{"a"}, expectedGroup: "", expectedDuration: 0, drainedGroups: map[string]bool{ "a": true, }, }, { name: "leak - state error", taskIsolationGroup: "a", taskIsolationDuration: time.Second, availableIsolationGroups: defaultAvailableIsolationGroups, recentPollers: []string{"a"}, isolationStateErr: errors.New(">:("), expectedGroup: "", expectedDuration: 0, }, { name: "leak - task isolation disabled", taskIsolationGroup: "a", taskIsolationDuration: time.Second, availableIsolationGroups: defaultAvailableIsolationGroups, recentPollers: []string{"a"}, expectedGroup: "", expectedDuration: 0, disableTaskIsolation: true, }, { name: "leak - wrong partition", taskIsolationGroup: "a", availableIsolationGroups: defaultAvailableIsolationGroups, expectedGroup: "", expectedDuration: 0, recentPollers: []string{"a"}, partitionConfig: &types.TaskListPartition{IsolationGroups: []string{"b", "c", "d"}}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) config := defaultTestConfig() config.EnableTasklistIsolation = func(domain string) bool { return !tc.disableTaskIsolation } config.TaskIsolationDuration = func(domain string, taskList string, taskType int) time.Duration { return tc.taskIsolationDuration } config.TaskIsolationPollerWindow = func(domain string, taskList string, taskType int) time.Duration { return taskIsolationPollerWindow } config.AllIsolationGroups = func() []string { return tc.availableIsolationGroups } mockClock := clock.NewMockedTimeSource() tlm := createTestTaskListManagerWithConfig(t, logger, controller, config, mockClock) if tc.partitionConfig != nil { tlm.startWG.Done() // Add a partition and update the root to the specified config. // If we're not adding a new partition it is treated as a no-op since the default is 1 partition err := tlm.UpdateTaskListPartitionConfig(context.Background(), &types.TaskListPartitionConfig{ ReadPartitions: map[int]*types.TaskListPartition{ 0: tc.partitionConfig, 1: {}, }, WritePartitions: map[int]*types.TaskListPartition{ 0: tc.partitionConfig, 1: {}, }, }) require.NoError(t, err) } mockIsolationGroupState := isolationgroup.NewMockState(controller) if tc.isolationStateErr != nil { mockIsolationGroupState.EXPECT().IsDrained(gomock.Any(), "domainName", gomock.Any()).Return(false, tc.isolationStateErr).AnyTimes() } else { mockIsolationGroupState.EXPECT().IsDrained(gomock.Any(), "domainName", gomock.Any()).DoAndReturn(func(ctx context.Context, domainName, group string) (bool, error) { return tc.drainedGroups[group], nil }).AnyTimes() } tlm.isolationState = mockIsolationGroupState for i, pollerGroup := range tc.recentPollers { tlm.pollers.StartPoll(strconv.Itoa(i), func() {}, &poller.Info{Identity: pollerGroup, IsolationGroup: pollerGroup}) } taskInfo := &persistence.TaskInfo{ DomainID: "domainId", RunID: "run1", WorkflowID: "workflow1", ScheduleID: 5, ScheduleToStartTimeoutSeconds: 1, PartitionConfig: map[string]string{ isolationgroup.GroupKey: tc.taskIsolationGroup, isolationgroup.WorkflowIDKey: "workflow1", }, CreatedTime: mockClock.Now().Add(-1 * tc.taskLatency), } actual, actualDuration := tlm.getIsolationGroupForTask(context.Background(), taskInfo) assert.Equal(t, tc.expectedGroup, actual) assert.Equal(t, tc.expectedDuration, actualDuration) }) } } func TestTaskWriterShutdown(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) tlm := createTestTaskListManager(t, logger, controller) err := tlm.Start(context.Background()) assert.NoError(t, err) // stop the task writer explicitly tlm.taskWriter.Stop() // now attempt to add a task addParams := AddTaskParams{ TaskInfo: &persistence.TaskInfo{ DomainID: "domainId", RunID: "run1", WorkflowID: "workflow1", ScheduleID: 5, ScheduleToStartTimeoutSeconds: 1, }, } _, err = tlm.AddTask(context.Background(), addParams) assert.Error(t, err) // test race tlm.taskWriter.stopped = 0 _, err = tlm.AddTask(context.Background(), addParams) assert.Error(t, err) tlm.taskWriter.stopped = 1 // reset it back to old value tlm.Stop() } func TestTaskListManagerGetTaskBatch(t *testing.T) { const taskCount = 1200 const rangeSize = 10 controller := gomock.NewController(t) mockIsolationState := isolationgroup.NewMockState(controller) mockIsolationState.EXPECT().IsDrained(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() mockDomainCache := cache.NewMockDomainCache(controller) mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.CreateDomainCacheEntry("domainName"), nil).AnyTimes() mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("domainName", nil).AnyTimes() mockHistoryService := history.NewMockClient(controller) logger := testlogger.New(t) timeSource := clock.NewRealTimeSource() tm := NewTestTaskManager(t, logger, timeSource) taskListID := NewTestTaskListID(t, "domainId", "tl", 0) cfg := defaultTestConfig() cfg.RangeSize = rangeSize cfg.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) mockRegistry := NewMockTaskListRegistry(controller) mockRegistry.EXPECT().Unregister(gomock.Any()).AnyTimes() params := ManagerParams{ DomainCache: mockDomainCache, Logger: logger, MetricsClient: metrics.NewClient(tally.NoopScope, metrics.Matching, metrics.HistogramMigration{}), TaskManager: tm, ClusterMetadata: cluster.GetTestClusterMetadata(true), IsolationState: mockIsolationState, MatchingClient: matching.NewMockClient(controller), Registry: mockRegistry, TaskList: taskListID, TaskListKind: types.TaskListKindNormal, Cfg: cfg, TimeSource: timeSource, CreateTime: timeSource.Now(), HistoryService: mockHistoryService, } tlMgr, err := NewManager(params) assert.NoError(t, err) tlm := tlMgr.(*taskListManagerImpl) err = tlm.Start(context.Background()) assert.NoError(t, err) // add taskCount tasks for i := int64(0); i < taskCount; i++ { scheduleID := i * 3 addParams := AddTaskParams{ TaskInfo: &persistence.TaskInfo{ DomainID: "domainId", RunID: "run1", WorkflowID: "workflow1", ScheduleID: scheduleID, ScheduleToStartTimeoutSeconds: 100, }, } _, err = tlm.AddTask(context.Background(), addParams) assert.NoError(t, err) } assert.Equal(t, taskCount, tm.GetTaskCount(taskListID)) // wait until all tasks are read by the task pump and enqeued into the in-memory buffer // at the end of this step, ackManager readLevel will also be equal to the buffer size expectedBufSize := min(cap(tlm.taskReader.taskBuffers[defaultTaskBufferIsolationGroup]), taskCount) assert.True(t, awaitCondition(func() bool { return len(tlm.taskReader.taskBuffers[defaultTaskBufferIsolationGroup]) == expectedBufSize }, 10*time.Second)) // stop all goroutines that read / write tasks in the background // remainder of this test works with the in-memory buffer tlm.Stop() // SetReadLevel should NEVER be called without updating ackManager.outstandingTasks // This is only for unit test purpose tlm.taskAckManager.SetReadLevel(tlm.taskWriter.GetMaxReadLevel()) tasks, readLevel, isReadBatchDone, err := tlm.taskReader.getTaskBatch(tlm.taskAckManager.GetReadLevel(), tlm.taskWriter.GetMaxReadLevel()) assert.NoError(t, err) assert.Equal(t, 0, len(tasks)) assert.Equal(t, readLevel, tlm.taskWriter.GetMaxReadLevel()) assert.True(t, isReadBatchDone) tlm.taskAckManager.SetReadLevel(0) tasks, readLevel, isReadBatchDone, err = tlm.taskReader.getTaskBatch(tlm.taskAckManager.GetReadLevel(), tlm.taskWriter.GetMaxReadLevel()) assert.NoError(t, err) assert.Equal(t, rangeSize/2, len(tasks)) assert.Equal(t, rangeSize/2, int(readLevel)) assert.True(t, isReadBatchDone) // reset the ackManager readLevel to the buffer size and consume // the in-memory tasks by calling Poll API - assert ackMgr state // at the end tlm.taskAckManager.SetReadLevel(int64(expectedBufSize)) // complete rangeSize events newParams := ManagerParams{ DomainCache: mockDomainCache, Logger: logger, MetricsClient: metrics.NewClient(tally.NoopScope, metrics.Matching, metrics.HistogramMigration{}), TaskManager: tm, ClusterMetadata: cluster.GetTestClusterMetadata(true), IsolationState: mockIsolationState, MatchingClient: matching.NewMockClient(controller), Registry: mockRegistry, TaskList: taskListID, TaskListKind: types.TaskListKindNormal, Cfg: cfg, TimeSource: timeSource, CreateTime: timeSource.Now(), HistoryService: mockHistoryService, } tlMgr, err = NewManager(newParams) assert.NoError(t, err) tlm = tlMgr.(*taskListManagerImpl) err = tlm.Start(context.Background()) assert.NoError(t, err) for i := int64(0); i < rangeSize; i++ { task, err := tlm.GetTask(context.Background(), nil) if errors.Is(err, ErrNoTasks) { continue } assert.NotNil(t, task.AutoConfigHint) assert.NoError(t, err) assert.NotNil(t, task) task.Finish(nil) } assert.Equal(t, taskCount-rangeSize, tm.GetTaskCount(taskListID)) tasks, _, isReadBatchDone, err = tlm.taskReader.getTaskBatch(tlm.taskAckManager.GetReadLevel(), tlm.taskWriter.GetMaxReadLevel()) assert.NoError(t, err) assert.True(t, 0 < len(tasks) && len(tasks) <= rangeSize) assert.True(t, isReadBatchDone) tlm.Stop() } func TestTaskListReaderPumpAdvancesAckLevelAfterEmptyReads(t *testing.T) { const taskCount = 5 const rangeSize = 10 const nLeaseRenewals = 15 controller := gomock.NewController(t) mockIsolationState := isolationgroup.NewMockState(controller) mockIsolationState.EXPECT().IsDrained(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() mockDomainCache := cache.NewMockDomainCache(controller) mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.CreateDomainCacheEntry("domainName"), nil).AnyTimes() mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("domainName", nil).AnyTimes() mockHistoryService := history.NewMockClient(controller) logger := testlogger.New(t) timeSource := clock.NewRealTimeSource() tm := NewTestTaskManager(t, logger, timeSource) taskListID := NewTestTaskListID(t, "domainId", "tl", 0) cfg := defaultTestConfig() cfg.RangeSize = rangeSize cfg.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) mockRegistry := NewMockTaskListRegistry(controller) mockRegistry.EXPECT().Unregister(gomock.Any()).AnyTimes() params := ManagerParams{ DomainCache: mockDomainCache, Logger: logger, MetricsClient: metrics.NewClient(tally.NoopScope, metrics.Matching, metrics.HistogramMigration{}), TaskManager: tm, ClusterMetadata: cluster.GetTestClusterMetadata(true), IsolationState: mockIsolationState, MatchingClient: matching.NewMockClient(controller), Registry: mockRegistry, TaskList: taskListID, TaskListKind: types.TaskListKindNormal, Cfg: cfg, TimeSource: timeSource, CreateTime: timeSource.Now(), HistoryService: mockHistoryService, } tlMgr, err := NewManager(params) require.NoError(t, err) tlm := tlMgr.(*taskListManagerImpl) // simulate lease renewal multiple times without writing any tasks for i := 0; i < nLeaseRenewals; i++ { tlm.taskWriter.renewLeaseWithRetry() } err = tlm.Start(context.Background()) // this call will also renew lease require.NoError(t, err) defer tlm.Stop() // we expect AckLevel to advance and skip all the previously leased ranges expectedAckLevel := int64(rangeSize) * nLeaseRenewals // wait until task pump will read batches of empty ranges assert.True(t, awaitCondition(func() bool { return tlm.taskAckManager.GetAckLevel() == expectedAckLevel }, 10*time.Second)) assert.Equal( t, expectedAckLevel, tlm.taskAckManager.GetAckLevel(), "we should ack ranges of all the previously acquired leases", ) assert.Equal( t, tlm.taskWriter.GetMaxReadLevel(), tlm.taskAckManager.GetAckLevel(), "we should have been acked everything possible", ) maxReadLevelBeforeAddingTasks := tlm.taskWriter.GetMaxReadLevel() // verify new task writes go beyond the MaxReadLevel/AckLevel for i := int64(0); i < taskCount; i++ { addParams := AddTaskParams{ TaskInfo: &persistence.TaskInfo{ DomainID: "domainId", RunID: "run1", WorkflowID: "workflow1", ScheduleID: i, }, } _, err = tlm.AddTask(context.Background(), addParams) require.NoError(t, err) assert.Equal(t, maxReadLevelBeforeAddingTasks+i+1, tlm.taskWriter.GetMaxReadLevel()) } } func TestTaskListManagerGetTaskBatch_ReadBatchDone(t *testing.T) { const rangeSize = 10 const maxReadLevel = int64(120) config := defaultTestConfig() config.RangeSize = rangeSize config.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) controller := gomock.NewController(t) logger := testlogger.New(t) tlm := createTestTaskListManagerWithConfig(t, logger, controller, config, clock.NewMockedTimeSource()) tlm.taskAckManager.SetReadLevel(0) atomic.StoreInt64(&tlm.taskWriter.maxReadLevel, maxReadLevel) tasks, readLevel, isReadBatchDone, err := tlm.taskReader.getTaskBatch(tlm.taskAckManager.GetReadLevel(), tlm.taskWriter.GetMaxReadLevel()) assert.Empty(t, tasks) assert.Equal(t, int64(rangeSize/2*10), readLevel) assert.False(t, isReadBatchDone) assert.NoError(t, err) tlm.taskAckManager.SetReadLevel(readLevel) atomic.StoreInt64(&tlm.taskWriter.maxReadLevel, maxReadLevel) tasks, readLevel, isReadBatchDone, err = tlm.taskReader.getTaskBatch(tlm.taskAckManager.GetReadLevel(), tlm.taskWriter.GetMaxReadLevel()) assert.Empty(t, tasks) assert.Equal(t, 2*int64(rangeSize/2*10), readLevel) assert.False(t, isReadBatchDone) assert.NoError(t, err) tlm.taskAckManager.SetReadLevel(readLevel) tasks, readLevel, isReadBatchDone, err = tlm.taskReader.getTaskBatch(tlm.taskAckManager.GetReadLevel(), tlm.taskWriter.GetMaxReadLevel()) assert.Empty(t, tasks) assert.Equal(t, maxReadLevel, readLevel) assert.True(t, isReadBatchDone) assert.NoError(t, err) } func awaitCondition(cond func() bool, timeout time.Duration) bool { expiry := time.Now().Add(timeout) for !cond() { time.Sleep(time.Millisecond * 5) if time.Now().After(expiry) { return false } } return true } func TestTaskExpiryAndCompletion(t *testing.T) { const taskCount = 20 const rangeSize = 10 testCases := []struct { name string batchSize int maxTimeBtwnDeletes time.Duration }{ {"test taskGC deleting due to size threshold", 2, time.Minute}, {"test taskGC deleting due to time condition", 100, time.Nanosecond}, } for _, tc := range testCases { t.Run("", func(t *testing.T) { controller := gomock.NewController(t) mockIsolationState := isolationgroup.NewMockState(controller) mockIsolationState.EXPECT().IsDrained(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() mockDomainCache := cache.NewMockDomainCache(controller) mockDomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(cache.CreateDomainCacheEntry("domainName"), nil).AnyTimes() mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("domainName", nil).AnyTimes() mockHistoryService := history.NewMockClient(controller) logger := testlogger.New(t) timeSource := clock.NewRealTimeSource() tm := NewTestTaskManager(t, logger, timeSource) taskListID := NewTestTaskListID(t, "domainId", "tl", 0) cfg := defaultTestConfig() cfg.RangeSize = rangeSize cfg.ReadRangeSize = dynamicproperties.GetIntPropertyFn(rangeSize / 2) cfg.MaxTaskDeleteBatchSize = dynamicproperties.GetIntPropertyFilteredByTaskListInfo(tc.batchSize) cfg.MaxTimeBetweenTaskDeletes = tc.maxTimeBtwnDeletes // set idle timer check to a really small value to assert that we don't accidentally drop tasks while blocking // on enqueuing a task to task buffer cfg.IdleTasklistCheckInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(20 * time.Millisecond) mockRegistry := NewMockTaskListRegistry(controller) mockRegistry.EXPECT().Unregister(gomock.Any()).AnyTimes() params := ManagerParams{ DomainCache: mockDomainCache, Logger: logger, MetricsClient: metrics.NewClient(tally.NoopScope, metrics.Matching, metrics.HistogramMigration{}), TaskManager: tm, ClusterMetadata: cluster.GetTestClusterMetadata(true), IsolationState: mockIsolationState, MatchingClient: matching.NewMockClient(controller), Registry: mockRegistry, TaskList: taskListID, TaskListKind: types.TaskListKindNormal, Cfg: cfg, TimeSource: timeSource, CreateTime: timeSource.Now(), HistoryService: mockHistoryService, } tlMgr, err := NewManager(params) assert.NoError(t, err) tlm := tlMgr.(*taskListManagerImpl) err = tlm.Start(context.Background()) assert.NoError(t, err) for i := int64(0); i < taskCount; i++ { scheduleID := i * 3 addParams := AddTaskParams{ TaskInfo: &persistence.TaskInfo{ DomainID: "domainId", RunID: "run1", WorkflowID: "workflow1", ScheduleID: scheduleID, ScheduleToStartTimeoutSeconds: 100, }, } if i%2 == 0 { // simulates creating a task whose scheduledToStartTimeout is already expired addParams.TaskInfo.ScheduleToStartTimeoutSeconds = -5 } _, err = tlm.AddTask(context.Background(), addParams) assert.NoError(t, err) } assert.Equal(t, taskCount, tm.GetTaskCount(taskListID)) // wait until all tasks are loaded by into in-memory buffers by task list manager // the buffer size should be one less than expected because dispatcher will dequeue the head assert.True(t, awaitCondition(func() bool { return len(tlm.taskReader.taskBuffers[defaultTaskBufferIsolationGroup]) >= (taskCount/2 - 1) }, time.Second)) remaining := taskCount for i := 0; i < 2; i++ { // verify that (1) expired tasks are not returned in poll result (2) taskCleaner deletes tasks correctly for i := int64(0); i < taskCount/4; i++ { task, err := tlm.GetTask(context.Background(), nil) assert.NoError(t, err) assert.NotNil(t, task) task.Finish(nil) } remaining -= taskCount / 2 // since every other task is expired, we expect half the tasks to be deleted // after poll consumed 1/4th of what is available assert.Equal(t, remaining, tm.GetTaskCount(taskListID)) } tlm.Stop() }) } } func TestTaskListManagerImpl_HasPollerAfter(t *testing.T) { for name, tc := range map[string]struct { outstandingPollers []string prepareManager func(*taskListManagerImpl) }{ "has_outstanding_pollers": { prepareManager: func(tlm *taskListManagerImpl) { tlm.pollers.StartPoll("poller1", func() {}, &poller.Info{Identity: "foo"}) }, }, "no_outstanding_pollers": { prepareManager: func(tlm *taskListManagerImpl) { tlm.pollers.StartPoll("poller1", func() {}, &poller.Info{Identity: "foo"}) tlm.pollers.EndPoll("poller1") }, }, } { t.Run(name, func(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) tlm := createTestTaskListManager(t, logger, controller) err := tlm.Start(context.Background()) assert.NoError(t, err) if tc.prepareManager != nil { tc.prepareManager(tlm) } assert.True(t, tlm.HasPollerAfter(time.Time{})) }) } } func getIsolationgroupsHelper() []string { return testIsolationGroups } func TestRefreshTaskListPartitionConfig(t *testing.T) { testCases := []struct { name string req *types.TaskListPartitionConfig originalConfig *types.TaskListPartitionConfig setupMocks func(*mockDeps) expectedConfig *types.TaskListPartitionConfig expectError bool expectedError string }{ { name: "success - refresh from request", req: &types.TaskListPartitionConfig{ Version: 2, ReadPartitions: partitions(3), WritePartitions: partitions(3), }, setupMocks: func(m *mockDeps) {}, expectedConfig: &types.TaskListPartitionConfig{ Version: 2, ReadPartitions: partitions(3), WritePartitions: partitions(3), }, }, { name: "success - ignore older version", req: &types.TaskListPartitionConfig{ Version: 2, ReadPartitions: partitions(3), WritePartitions: partitions(3), }, originalConfig: &types.TaskListPartitionConfig{ Version: 3, ReadPartitions: partitions(2), WritePartitions: partitions(2), }, setupMocks: func(m *mockDeps) {}, expectedConfig: &types.TaskListPartitionConfig{ Version: 3, ReadPartitions: partitions(2), WritePartitions: partitions(2), }, }, { name: "success - refresh from database", originalConfig: &types.TaskListPartitionConfig{ Version: 3, ReadPartitions: partitions(2), WritePartitions: partitions(2), }, setupMocks: func(deps *mockDeps) { deps.mockTaskManager.EXPECT().GetTaskList(gomock.Any(), &persistence.GetTaskListRequest{ DomainID: "domain-id", DomainName: "domainName", TaskList: "tl", TaskType: persistence.TaskListTypeDecision, }).Return(&persistence.GetTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 4, ReadPartitions: persistencePartitions(10), WritePartitions: persistencePartitions(10), }, }, }, nil) }, expectedConfig: &types.TaskListPartitionConfig{ Version: 4, ReadPartitions: partitions(10), WritePartitions: partitions(10), }, }, { name: "failed to refresh from database", originalConfig: &types.TaskListPartitionConfig{ Version: 3, ReadPartitions: partitions(2), WritePartitions: partitions(2), }, setupMocks: func(deps *mockDeps) { deps.mockTaskManager.EXPECT().GetTaskList(gomock.Any(), &persistence.GetTaskListRequest{ DomainID: "domain-id", DomainName: "domainName", TaskList: "tl", TaskType: persistence.TaskListTypeDecision, }).Return(nil, errors.New("some error")) }, expectedConfig: &types.TaskListPartitionConfig{ Version: 3, ReadPartitions: partitions(2), WritePartitions: partitions(2), }, expectError: true, expectedError: "some error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tlID, err := NewIdentifier("domain-id", "tl", persistence.TaskListTypeDecision) require.NoError(t, err) tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) tc.setupMocks(deps) tlm.partitionConfig = tc.originalConfig tlm.startWG.Done() err = tlm.RefreshTaskListPartitionConfig(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) assert.Equal(t, tc.expectedConfig, tlm.TaskListPartitionConfig()) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedConfig, tlm.TaskListPartitionConfig()) } }) } } func TestUpdateTaskListPartitionConfig(t *testing.T) { testCases := []struct { name string req *types.TaskListPartitionConfig originalConfig *types.TaskListPartitionConfig setupMocks func(*mockDeps) expectedConfig *types.TaskListPartitionConfig expectError bool expectedError string }{ { name: "success - no op", req: &types.TaskListPartitionConfig{ ReadPartitions: partitions(3), WritePartitions: partitions(3), }, originalConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(3), WritePartitions: partitions(3), }, setupMocks: func(m *mockDeps) {}, expectedConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(3), WritePartitions: partitions(3), }, }, { name: "success - no op, nil pointer", req: &types.TaskListPartitionConfig{ ReadPartitions: partitions(1), WritePartitions: partitions(1), }, originalConfig: nil, setupMocks: func(m *mockDeps) {}, expectedConfig: nil, }, { name: "success - update", req: &types.TaskListPartitionConfig{ ReadPartitions: partitions(3), WritePartitions: partitions(1), }, originalConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(3), WritePartitions: partitions(3), }, setupMocks: func(deps *mockDeps) { deps.mockTaskManager.EXPECT().UpdateTaskList(gomock.Any(), &persistence.UpdateTaskListRequest{ DomainName: "domainName", TaskListInfo: &persistence.TaskListInfo{ DomainID: "domain-id", Name: "tl", AckLevel: 0, RangeID: 0, Kind: persistence.TaskListKindNormal, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 2, ReadPartitions: persistencePartitions(3), WritePartitions: persistencePartitions(1), }, }, }).Return(&persistence.UpdateTaskListResponse{}, nil) deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "domain-id", TaskList: &types.TaskList{Name: "/__cadence_sys/tl/1", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 2, ReadPartitions: partitions(3), WritePartitions: partitions(1), }, }).Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil) deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "domain-id", TaskList: &types.TaskList{Name: "/__cadence_sys/tl/2", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 2, ReadPartitions: partitions(3), WritePartitions: partitions(1), }, }).Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil) }, expectedConfig: &types.TaskListPartitionConfig{ Version: 2, ReadPartitions: partitions(3), WritePartitions: partitions(1), }, }, { name: "success - push failures are ignored", req: &types.TaskListPartitionConfig{ ReadPartitions: partitions(3), WritePartitions: partitions(1), }, originalConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(3), WritePartitions: partitions(3), }, setupMocks: func(deps *mockDeps) { deps.mockTaskManager.EXPECT().UpdateTaskList(gomock.Any(), &persistence.UpdateTaskListRequest{ DomainName: "domainName", TaskListInfo: &persistence.TaskListInfo{ DomainID: "domain-id", Name: "tl", AckLevel: 0, RangeID: 0, Kind: persistence.TaskListKindNormal, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 2, ReadPartitions: persistencePartitions(3), WritePartitions: persistencePartitions(1), }, }, }).Return(&persistence.UpdateTaskListResponse{}, nil) deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "domain-id", TaskList: &types.TaskList{Name: "/__cadence_sys/tl/1", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 2, ReadPartitions: partitions(3), WritePartitions: partitions(1), }, }).Return(nil, errors.New("matching client error")) deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "domain-id", TaskList: &types.TaskList{Name: "/__cadence_sys/tl/2", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 2, ReadPartitions: partitions(3), WritePartitions: partitions(1), }, }).Return(nil, errors.New("matching client error")) }, expectedConfig: &types.TaskListPartitionConfig{ Version: 2, ReadPartitions: partitions(3), WritePartitions: partitions(1), }, }, { name: "failed to update", req: &types.TaskListPartitionConfig{ ReadPartitions: partitions(3), WritePartitions: partitions(1), }, originalConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(3), WritePartitions: partitions(3), }, setupMocks: func(deps *mockDeps) { deps.mockTaskManager.EXPECT().UpdateTaskList(gomock.Any(), &persistence.UpdateTaskListRequest{ DomainName: "domainName", TaskListInfo: &persistence.TaskListInfo{ DomainID: "domain-id", Name: "tl", AckLevel: 0, RangeID: 0, Kind: persistence.TaskListKindNormal, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 2, ReadPartitions: persistencePartitions(3), WritePartitions: persistencePartitions(1), }, }, }).Return(nil, errors.New("some error")) }, expectedConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(3), WritePartitions: partitions(3), }, expectError: true, expectedError: "some error", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tlID, err := NewIdentifier("domain-id", "tl", persistence.TaskListTypeDecision) require.NoError(t, err) tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) tc.setupMocks(deps) tlm.partitionConfig = tc.originalConfig tlm.startWG.Done() err = tlm.UpdateTaskListPartitionConfig(context.Background(), tc.req) if tc.expectError { assert.ErrorContains(t, err, tc.expectedError) assert.Equal(t, int32(1), tlm.stopped) } else { assert.NoError(t, err) } assert.Equal(t, tc.expectedConfig, tlm.TaskListPartitionConfig()) }) } } func TestRefreshTaskListPartitionConfigConcurrency(t *testing.T) { tlID, err := NewIdentifier("domain-id", "/__cadence_sys/tl/1", persistence.TaskListTypeDecision) require.NoError(t, err) tlm, _ := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) tlm.startWG.Done() var g errgroup.Group for i := 0; i < 100; i++ { v := i g.Go(func() error { return tlm.RefreshTaskListPartitionConfig(context.Background(), &types.TaskListPartitionConfig{Version: int64(v), ReadPartitions: partitions(v), WritePartitions: partitions(v)}) }) } require.NoError(t, g.Wait()) assert.Equal(t, int64(99), tlm.TaskListPartitionConfig().Version) } func TestUpdateTaskListPartitionConfigConcurrency(t *testing.T) { tlID, err := NewIdentifier("domain-id", "/__cadence_sys/tl/1", persistence.TaskListTypeDecision) require.NoError(t, err) tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) deps.mockTaskManager.EXPECT().UpdateTaskList(gomock.Any(), gomock.Any()).Return(&persistence.UpdateTaskListResponse{}, nil).AnyTimes() deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), gomock.Any()).Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil).AnyTimes() tlm.startWG.Done() var g errgroup.Group for i := 2; i < 102; i++ { v := i g.Go(func() error { return tlm.UpdateTaskListPartitionConfig(context.Background(), &types.TaskListPartitionConfig{ReadPartitions: partitions(v), WritePartitions: partitions(v)}) }) } require.NoError(t, g.Wait()) assert.Equal(t, int64(100), tlm.TaskListPartitionConfig().Version) } func TestManagerStart_RootPartition(t *testing.T) { tlID, err := NewIdentifier("domain-id", "tl", persistence.TaskListTypeDecision) require.NoError(t, err) tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) deps.mockTaskManager.EXPECT().LeaseTaskList(gomock.Any(), &persistence.LeaseTaskListRequest{ DomainID: "domain-id", DomainName: "domainName", TaskList: "tl", TaskType: persistence.TaskListTypeDecision, }).Return(&persistence.LeaseTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "domain-id", Name: "tl", Kind: persistence.TaskListKindNormal, AckLevel: 0, RangeID: 0, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 1, ReadPartitions: persistencePartitions(2), WritePartitions: persistencePartitions(2), }, }, }, nil) deps.mockMatchingClient.EXPECT().RefreshTaskListPartitionConfig(gomock.Any(), &types.MatchingRefreshTaskListPartitionConfigRequest{ DomainUUID: "domain-id", TaskList: &types.TaskList{Name: "/__cadence_sys/tl/1", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(2), WritePartitions: partitions(2), }, }).Return(&types.MatchingRefreshTaskListPartitionConfigResponse{}, nil) assert.NoError(t, tlm.Start(context.Background())) assert.Equal(t, &types.TaskListPartitionConfig{Version: 1, ReadPartitions: partitions(2), WritePartitions: partitions(2)}, tlm.TaskListPartitionConfig()) tlm.stopWG.Wait() } func TestManagerStart_NonRootPartition(t *testing.T) { tlID, err := NewIdentifier("domain-id", "/__cadence_sys/tl/1", persistence.TaskListTypeDecision) require.NoError(t, err) tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) deps.mockTaskManager.EXPECT().GetTaskList(gomock.Any(), &persistence.GetTaskListRequest{ DomainID: "domain-id", DomainName: "domainName", TaskList: "tl", TaskType: persistence.TaskListTypeDecision, }).Return(&persistence.GetTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "domain-id", Name: "tl", Kind: persistence.TaskListKindNormal, AckLevel: 0, RangeID: 0, AdaptivePartitionConfig: &persistence.TaskListPartitionConfig{ Version: 1, ReadPartitions: persistencePartitions(3), WritePartitions: persistencePartitions(3), }, }, }, nil) deps.mockTaskManager.EXPECT().LeaseTaskList(gomock.Any(), &persistence.LeaseTaskListRequest{ DomainID: "domain-id", DomainName: "domainName", TaskList: "/__cadence_sys/tl/1", TaskType: persistence.TaskListTypeDecision, }).Return(&persistence.LeaseTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ DomainID: "domain-id", Name: "/__cadence_sys/tl/1", Kind: persistence.TaskListKindNormal, AckLevel: 0, RangeID: 0, }, }, nil) assert.NoError(t, tlm.Start(context.Background())) assert.Equal(t, &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: partitions(3), WritePartitions: partitions(3), }, tlm.TaskListPartitionConfig()) } func TestDispatchTask(t *testing.T) { testCases := []struct { name string mockSetup func(matcher *MockTaskMatcher, taskCompleter *MockTaskCompleter, ctx context.Context, task *InternalTask) enableStandByTaskCompletion bool activeClusterName string err error }{ { name: "active cluster - disabled StandByTaskCompletion - task sent to MustOffer and no error returned", mockSetup: func(matcher *MockTaskMatcher, taskCompleter *MockTaskCompleter, ctx context.Context, task *InternalTask) { matcher.EXPECT().MustOffer(ctx, task).Return(nil).Times(1) }, activeClusterName: cluster.TestCurrentClusterName, err: nil, }, { name: "active cluster - disabled StandByTaskCompletion - task sent to MustOffer and error returned", mockSetup: func(matcher *MockTaskMatcher, taskCompleter *MockTaskCompleter, ctx context.Context, task *InternalTask) { matcher.EXPECT().MustOffer(ctx, task).Return(errors.New("no-task-completion-must-offer-error")).Times(1) }, activeClusterName: cluster.TestCurrentClusterName, err: errors.New("no-task-completion-must-offer-error"), }, { name: "active cluster - enabled StandByTaskCompletion - task sent to MustOffer and no error returned", mockSetup: func(matcher *MockTaskMatcher, taskCompleter *MockTaskCompleter, ctx context.Context, task *InternalTask) { matcher.EXPECT().MustOffer(ctx, task).Return(nil).Times(1) }, enableStandByTaskCompletion: true, activeClusterName: cluster.TestCurrentClusterName, err: nil, }, { name: "active cluster - enabled StandByTaskCompletion - task sent to MustOffer and error returned", mockSetup: func(matcher *MockTaskMatcher, taskCompleter *MockTaskCompleter, ctx context.Context, task *InternalTask) { matcher.EXPECT().MustOffer(ctx, task).Return(errors.New("task-completion-must-offer-error")).Times(1) }, enableStandByTaskCompletion: true, activeClusterName: cluster.TestCurrentClusterName, err: errors.New("task-completion-must-offer-error"), }, { name: "standby cluster - disabled StandByTaskCompletion - task sent to MustOffer and no error returned", mockSetup: func(matcher *MockTaskMatcher, taskCompleter *MockTaskCompleter, ctx context.Context, task *InternalTask) { matcher.EXPECT().MustOffer(ctx, task).Return(nil).Times(1) }, activeClusterName: cluster.TestAlternativeClusterName, err: nil, }, { name: "standby cluster - disabled StandByTaskCompletion - task sent to MustOffer and error returned", mockSetup: func(matcher *MockTaskMatcher, taskCompleter *MockTaskCompleter, ctx context.Context, task *InternalTask) { matcher.EXPECT().MustOffer(ctx, task).Return(errors.New("no-task-completion-must-offer-error")).Times(1) }, activeClusterName: cluster.TestAlternativeClusterName, err: errors.New("no-task-completion-must-offer-error"), }, { name: "standby cluster - enabled StandByTaskCompletion - task completed", mockSetup: func(matcher *MockTaskMatcher, taskCompleter *MockTaskCompleter, ctx context.Context, task *InternalTask) { taskCompleter.EXPECT().CompleteTaskIfStarted(ctx, task).Return(nil).Times(1) }, enableStandByTaskCompletion: true, activeClusterName: cluster.TestAlternativeClusterName, err: nil, }, { name: "standby cluster - enabled StandByTaskCompletion - task completion error", mockSetup: func(matcher *MockTaskMatcher, taskCompleter *MockTaskCompleter, ctx context.Context, task *InternalTask) { taskCompleter.EXPECT().CompleteTaskIfStarted(ctx, task).Return(errTaskNotStarted).Times(1) }, enableStandByTaskCompletion: true, activeClusterName: cluster.TestAlternativeClusterName, err: errTaskNotStarted, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) tlm := createTestTaskListManager(t, logger, controller) task := &InternalTask{ Event: &genericTaskInfo{ TaskInfo: &persistence.TaskInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, }, } taskMatcher := NewMockTaskMatcher(controller) taskCompleter := NewMockTaskCompleter(controller) tlm.matcher = taskMatcher tlm.taskCompleter = taskCompleter tlm.config.EnableStandbyTaskCompletion = func() bool { return tc.enableStandByTaskCompletion } mockDomainCache := cache.NewMockDomainCache(controller) cacheEntry := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: constants.TestDomainID, Name: constants.TestDomainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: tc.activeClusterName, Clusters: []*persistence.ClusterReplicationConfig{{ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}}, }, 1, ) mockDomainCache.EXPECT().GetDomainByID(constants.TestDomainID).Return(cacheEntry, nil).AnyTimes() tlm.domainCache = mockDomainCache ctx := context.Background() tc.mockSetup(taskMatcher, taskCompleter, ctx, task) err := tlm.DispatchTask(ctx, task) if tc.err != nil { assert.Error(t, err) assert.Equal(t, tc.err, err) } else { assert.NoError(t, err) } }) } } func TestGetNumPartitions(t *testing.T) { tlID, err := NewIdentifier("domain-id", "tl", persistence.TaskListTypeDecision) require.NoError(t, err) tlm, deps := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) require.NoError(t, deps.dynamicClient.UpdateValue(dynamicproperties.MatchingEnableGetNumberOfPartitionsFromCache, true)) assert.NotPanics(t, func() { tlm.limiter.ReportLimit(float64(100)) }) } func TestDisconnectBlockedPollers(t *testing.T) { tests := []struct { name string newActiveClusterName *string mockSetup func(mockMatcher *MockTaskMatcher) stopped int32 expectedErr error }{ { name: "disconnect blocked pollers and refresh cancel context", newActiveClusterName: common.StringPtr("new-active-cluster"), mockSetup: func(mockMatcher *MockTaskMatcher) { mockMatcher.EXPECT().DisconnectBlockedPollers().Times(1) mockMatcher.EXPECT().RefreshCancelContext().Times(1) }, expectedErr: nil, }, { name: "tasklist manager is shutting down, noop", newActiveClusterName: common.StringPtr("new-active-cluster"), mockSetup: func(mockMatcher *MockTaskMatcher) {}, stopped: int32(1), expectedErr: errShutdown, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { tlID, err := NewIdentifier("domain-id", "tl", persistence.TaskListTypeDecision) require.NoError(t, err) tlm, _ := setupMocksForTaskListManager(t, tlID, types.TaskListKindNormal) mockMatcher := NewMockTaskMatcher(gomock.NewController(t)) tlm.matcher = mockMatcher tc.mockSetup(mockMatcher) tlm.stopped = tc.stopped err = tlm.ReleaseBlockedPollers() assert.Equal(t, tc.expectedErr, err) }) } } func partitions(num int) map[int]*types.TaskListPartition { result := make(map[int]*types.TaskListPartition, num) for i := 0; i < num; i++ { result[i] = &types.TaskListPartition{} } return result } func persistencePartitions(num int) map[int]*persistence.TaskListPartition { result := make(map[int]*persistence.TaskListPartition, num) for i := 0; i < num; i++ { result[i] = &persistence.TaskListPartition{} } return result } func TestTaskListUsesOverrideRPS(t *testing.T) { testCases := []struct { name string overrideRPS float64 maxDispatchPerSecond *float64 expectedRPS float64 }{ { name: "Uses maxDispatchPerSecond when overrideRPS is zero", overrideRPS: 0.0, maxDispatchPerSecond: common.Float64Ptr(75.0), expectedRPS: 75.0, }, { name: "Uses maxDispatchPerSecond when overrideRPS is negative", overrideRPS: -1.0, maxDispatchPerSecond: common.Float64Ptr(60.0), expectedRPS: 60.0, }, { name: "OverrideRPS takes precedence when higher than maxDispatchPerSecond", overrideRPS: 200.0, maxDispatchPerSecond: common.Float64Ptr(50.0), expectedRPS: 200.0, }, { name: "OverrideRPS takes precedence when lower than maxDispatchPerSecond", overrideRPS: 25.0, maxDispatchPerSecond: common.Float64Ptr(100.0), expectedRPS: 25.0, }, { name: "No maxDispatchPerSecond and no overrideRPS", overrideRPS: 0.0, maxDispatchPerSecond: nil, expectedRPS: 100000.0, }, { name: "OverrideRPS only, with no maxDispatchPerSecond", overrideRPS: 150.0, maxDispatchPerSecond: nil, expectedRPS: 150.0, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) logger := testlogger.New(t) cfg := defaultTestConfig() cfg.OverrideTaskListRPS = func(domain, taskList string, taskType int) float64 { return tc.overrideRPS } tlm := createTestTaskListManagerWithConfig(t, logger, controller, cfg, clock.NewMockedTimeSource()) _, _ = tlm.GetTask(context.Background(), tc.maxDispatchPerSecond) assert.Equal(t, rate.Limit(tc.expectedRPS), tlm.limiter.Limit()) }) } } ================================================ FILE: service/matching/tasklist/task_list_registry.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "sync" "github.com/uber/cadence/common/metrics" ) type taskListRegistryImpl struct { sync.RWMutex taskLists map[Identifier]Manager // task lists indexed by domain ID and task list name // to quickly get all task lists for a domain or a task list name // they are always in sync with the taskLists map taskListsByDomainID map[string]map[Identifier]Manager taskListsByTaskListName map[string]map[Identifier]Manager metricsClient metrics.Client } func NewTaskListRegistry(metricsClient metrics.Client) TaskListRegistry { return &taskListRegistryImpl{ taskLists: make(map[Identifier]Manager), taskListsByDomainID: make(map[string]map[Identifier]Manager), taskListsByTaskListName: make(map[string]map[Identifier]Manager), metricsClient: metricsClient, } } func (r *taskListRegistryImpl) Register(id Identifier, mgr Manager) { r.Lock() defer r.Unlock() // we can override the manager for the same identifier if it is already registered // this case should be handled by the caller r.taskLists[id] = mgr if _, ok := r.taskListsByDomainID[id.GetDomainID()]; !ok { r.taskListsByDomainID[id.GetDomainID()] = make(map[Identifier]Manager) } if _, ok := r.taskListsByTaskListName[id.GetName()]; !ok { r.taskListsByTaskListName[id.GetName()] = make(map[Identifier]Manager) } r.taskListsByDomainID[id.GetDomainID()][id] = mgr r.taskListsByTaskListName[id.GetName()][id] = mgr r.updateMetricsLocked() } func (r *taskListRegistryImpl) Unregister(mgr Manager) bool { id := mgr.TaskListID() r.Lock() defer r.Unlock() // we need to make sure we still hold the given `mgr` or we already replaced with a new one. currentTlMgr, ok := r.taskLists[*id] if ok && currentTlMgr == mgr { delete(r.taskLists, *id) delete(r.taskListsByDomainID[id.GetDomainID()], *id) if len(r.taskListsByDomainID[id.GetDomainID()]) == 0 { delete(r.taskListsByDomainID, id.GetDomainID()) } delete(r.taskListsByTaskListName[id.GetName()], *id) if len(r.taskListsByTaskListName[id.GetName()]) == 0 { delete(r.taskListsByTaskListName, id.GetName()) } r.updateMetricsLocked() return true } return false } func (r *taskListRegistryImpl) ManagerByTaskListIdentifier(id Identifier) (Manager, bool) { r.RLock() defer r.RUnlock() tlMgr, ok := r.taskLists[id] return tlMgr, ok } func (r *taskListRegistryImpl) AllManagers() []Manager { r.RLock() defer r.RUnlock() res := make([]Manager, 0, len(r.taskLists)) for _, tlMgr := range r.taskLists { res = append(res, tlMgr) } return res } func (r *taskListRegistryImpl) ManagersByDomainID(domainID string) []Manager { r.RLock() defer r.RUnlock() var res []Manager for _, tlm := range r.taskListsByDomainID[domainID] { res = append(res, tlm) } return res } func (r *taskListRegistryImpl) ManagersByTaskListName(name string) []Manager { r.RLock() defer r.RUnlock() var res []Manager for _, tlm := range r.taskListsByTaskListName[name] { res = append(res, tlm) } return res } func (r *taskListRegistryImpl) updateMetricsLocked() { r.metricsClient.Scope(metrics.MatchingTaskListMgrScope).UpdateGauge( metrics.TaskListManagersGauge, float64(len(r.taskLists)), ) } ================================================ FILE: service/matching/tasklist/task_list_registry_test.go ================================================ package tasklist import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/metrics" metricsmocks "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/persistence" ) func mustNewIdentifierForTest(t *testing.T, domainID, taskListName string) *Identifier { t.Helper() id, err := NewIdentifier(domainID, taskListName, persistence.TaskListTypeDecision) require.NoError(t, err) return id } func newMockManagerWithID(t *testing.T, ctrl *gomock.Controller, id *Identifier) *MockManager { t.Helper() mgr := NewMockManager(ctrl) mgr.EXPECT().TaskListID().Return(id).AnyTimes() return mgr } func TestTaskListRegistry_RegisterLookupAndUnregister(t *testing.T) { ctrl := gomock.NewController(t) metricsClient := metricsmocks.Client{} metricsScope := metricsmocks.Scope{} metricsClient.On("Scope", metrics.MatchingTaskListMgrScope).Return(&metricsScope) registry := NewTaskListRegistry(&metricsClient) id := mustNewIdentifierForTest(t, "domain-a", "task-list-a") initialMgr := newMockManagerWithID(t, ctrl, id) updatedMgr := newMockManagerWithID(t, ctrl, id) metricsScope.On("UpdateGauge", metrics.TaskListManagersGauge, float64(1)).Once() registry.Register(*id, initialMgr) got, ok := registry.ManagerByTaskListIdentifier(*id) require.True(t, ok) assert.Equal(t, initialMgr, got) // Re-register with the same identifier should replace the manager. metricsScope.On("UpdateGauge", metrics.TaskListManagersGauge, float64(1)).Once() registry.Register(*id, updatedMgr) got, ok = registry.ManagerByTaskListIdentifier(*id) require.True(t, ok) assert.Equal(t, updatedMgr, got) // Unregister should not remove a replaced/stale manager. assert.False(t, registry.Unregister(initialMgr)) got, ok = registry.ManagerByTaskListIdentifier(*id) require.True(t, ok) assert.Equal(t, updatedMgr, got) // Unregistering the current manager should remove the entry. metricsScope.On("UpdateGauge", metrics.TaskListManagersGauge, float64(0)).Once() assert.True(t, registry.Unregister(updatedMgr)) _, ok = registry.ManagerByTaskListIdentifier(*id) assert.False(t, ok) impl := registry.(*taskListRegistryImpl) assert.Empty(t, impl.taskListsByDomainID, "should be empty because there are no other task lists for this domain") assert.Empty(t, impl.taskListsByTaskListName, "should be empty because there are no other task lists for this task list name") metricsClient.AssertExpectations(t) metricsScope.AssertExpectations(t) } func TestTaskListRegistry_Filters(t *testing.T) { ctrl := gomock.NewController(t) registry := NewTaskListRegistry(metrics.NewNoopMetricsClient()) domainA1 := mustNewIdentifierForTest(t, "domain-a", "shared-name") domainA2 := mustNewIdentifierForTest(t, "domain-a", "other-name") domainB1 := mustNewIdentifierForTest(t, "domain-b", "shared-name") mgrA1 := newMockManagerWithID(t, ctrl, domainA1) mgrA2 := newMockManagerWithID(t, ctrl, domainA2) mgrB1 := newMockManagerWithID(t, ctrl, domainB1) registry.Register(*domainA1, mgrA1) registry.Register(*domainA2, mgrA2) registry.Register(*domainB1, mgrB1) t.Run("all managers", func(t *testing.T) { assert.ElementsMatch(t, []Manager{mgrA1, mgrA2, mgrB1}, registry.AllManagers()) }) t.Run("managers by domain", func(t *testing.T) { assert.ElementsMatch(t, []Manager{mgrA1, mgrA2}, registry.ManagersByDomainID("domain-a")) assert.ElementsMatch(t, []Manager{mgrB1}, registry.ManagersByDomainID("domain-b")) assert.Empty(t, registry.ManagersByDomainID("missing-domain")) }) t.Run("managers by task list name", func(t *testing.T) { assert.ElementsMatch( t, []Manager{mgrA1, mgrB1}, registry.ManagersByTaskListName("shared-name"), ) assert.ElementsMatch(t, []Manager{mgrA2}, registry.ManagersByTaskListName("other-name")) assert.Empty(t, registry.ManagersByTaskListName("missing-name")) }) } ================================================ FILE: service/matching/tasklist/task_reader.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "context" "errors" "runtime" "sync" "sync/atomic" "time" "golang.org/x/time/rate" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/event" ) var epochStartTime = time.Unix(0, 0) const ( defaultTaskBufferIsolationGroup = "" // a task buffer which is not using an isolation group ) type ( taskReader struct { // taskBuffers: This is the in-memory queue of tasks for dispatch // that are enqueued for pollers to pickup. It's written to by // - getTasksPump - the primary means of loading async matching tasks // - task dispatch redirection - when a task is redirected from another isolation group taskBuffers map[string]chan *persistence.TaskInfo notifyC chan struct{} // Used as signal to notify pump of new tasks tlMgr *taskListManagerImpl taskListID *Identifier config *config.TaskListConfig db *taskListDB taskWriter *taskWriter taskGC *taskGC taskAckManager messaging.AckManager domainCache cache.DomainCache clusterMetadata cluster.Metadata timeSource clock.TimeSource // The cancel objects are to cancel the ratelimiter Wait in dispatchBufferedTasks. The ideal // approach is to use request-scoped contexts and use a unique one for each call to Wait. However // in order to cancel it on shutdown, we need a new goroutine for each call that would wait on // the shutdown channel. To optimize on efficiency, we instead create one and tag it on the struct // so the cancel can be called directly on shutdown. cancelCtx context.Context cancelFunc context.CancelFunc stopped int64 // set to 1 if the reader is stopped or is shutting down logger log.Logger scope metrics.Scope throttleRetry *backoff.ThrottleRetry handleErr func(error) error onFatalErr func() dispatchTask func(context.Context, *InternalTask) error getIsolationGroupForTask func(context.Context, *persistence.TaskInfo) (string, time.Duration) rateLimit func() rate.Limit // stopWg is used to wait for all dispatchers to stop. stopWg sync.WaitGroup } ) func newTaskReader(tlMgr *taskListManagerImpl, isolationGroups []string) *taskReader { ctx, cancel := context.WithCancel(context.Background()) taskBuffers := make(map[string]chan *persistence.TaskInfo) // Validate batch size to prevent system failures batchSize := tlMgr.config.GetTasksBatchSize() if batchSize <= 0 { fallback := dynamicproperties.IntKeys[dynamicproperties.MatchingGetTasksBatchSize].DefaultValue tlMgr.logger.Warn("matching.getTasksBatchSize is set to invalid value, using default value", tag.Dynamic("invalidBatchSize", batchSize), tag.Dynamic("correctedBatchSize", fallback)) batchSize = fallback } taskBuffers[defaultTaskBufferIsolationGroup] = make(chan *persistence.TaskInfo, batchSize-1) for _, g := range isolationGroups { taskBuffers[g] = make(chan *persistence.TaskInfo, batchSize-1) } return &taskReader{ tlMgr: tlMgr, taskListID: tlMgr.taskListID, config: tlMgr.config, db: tlMgr.db, taskWriter: tlMgr.taskWriter, taskGC: tlMgr.taskGC, taskAckManager: tlMgr.taskAckManager, cancelCtx: ctx, cancelFunc: cancel, notifyC: make(chan struct{}, 1), // we always dequeue the head of the buffer and try to dispatch it to a poller // so allocate one less than desired target buffer size taskBuffers: taskBuffers, domainCache: tlMgr.domainCache, clusterMetadata: tlMgr.clusterMetadata, timeSource: tlMgr.timeSource, logger: tlMgr.logger, scope: tlMgr.scope, handleErr: tlMgr.handleErr, onFatalErr: tlMgr.Stop, dispatchTask: tlMgr.DispatchTask, getIsolationGroupForTask: tlMgr.getIsolationGroupForTask, rateLimit: tlMgr.limiter.Limit, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(persistenceOperationRetryPolicy), backoff.WithRetryableError(persistence.IsTransientError), ), } } func (tr *taskReader) Start() { tr.Signal() for g := range tr.taskBuffers { g := g tr.stopWg.Add(1) go func() { defer tr.stopWg.Done() tr.dispatchBufferedTasks(g) }() } tr.stopWg.Add(1) go func() { defer tr.stopWg.Done() tr.getTasksPump() }() } func (tr *taskReader) Stop() { if atomic.CompareAndSwapInt64(&tr.stopped, 0, 1) { tr.cancelFunc() if err := tr.persistAckLevel(); err != nil { tr.logger.Error("Persistent store operation failure", tag.StoreOperationUpdateTaskList, tag.Error(err)) } tr.taskGC.RunNow(tr.taskAckManager.GetAckLevel()) tr.stopWg.Wait() } } func (tr *taskReader) Signal() { var event struct{} select { case tr.notifyC <- event: default: // channel already has an event, don't block } } func (tr *taskReader) dispatchBufferedTasks(isolationGroup string) { dispatchLoop: for { select { case taskInfo, ok := <-tr.taskBuffers[isolationGroup]: if !ok { // Task list getTasks pump is shutdown break dispatchLoop } event.Log(event.E{ TaskListName: tr.taskListID.GetName(), TaskListType: tr.taskListID.GetType(), TaskListKind: &tr.tlMgr.taskListKind, TaskInfo: *taskInfo, EventName: "Attempting to Dispatch Buffered Task", }) breakDispatchLoop := tr.dispatchSingleTaskFromBufferWithRetries(taskInfo) if breakDispatchLoop { // shutting down break dispatchLoop } case <-tr.cancelCtx.Done(): break dispatchLoop } } } func (tr *taskReader) getTasksPump() { updateAckTimer := tr.timeSource.NewTimer(tr.config.UpdateAckInterval()) defer updateAckTimer.Stop() getTasksPumpLoop: for { tr.scope.UpdateGauge(metrics.TaskBacklogPerTaskListGauge, float64(tr.taskAckManager.GetBacklogCount())) select { case <-tr.cancelCtx.Done(): break getTasksPumpLoop case <-tr.notifyC: { initialReadLevel := tr.taskAckManager.GetReadLevel() maxReadLevel := tr.taskWriter.GetMaxReadLevel() tasks, readLevel, isReadBatchDone, err := tr.getTaskBatch(initialReadLevel, maxReadLevel) if err != nil { tr.Signal() // re-enqueue the event // TODO: Should we ever stop retrying on db errors? continue getTasksPumpLoop } if len(tasks) == 0 { tr.taskAckManager.SetReadLevel(readLevel) if tr.taskAckManager.GetAckLevel() == initialReadLevel { // Even though we didn't handle any tasks, we want to advance the ack-level // in order to avoid needless querying database the next time. // This is safe since we started reading exactly from the current AckLevel and read no tasks tr.taskAckManager.SetAckLevel(readLevel) } if !isReadBatchDone { tr.Signal() } continue getTasksPumpLoop } if !tr.addTasksToBuffer(tasks) { break getTasksPumpLoop } // There maybe more tasks. We yield now, but signal pump to check again later. tr.Signal() } case <-updateAckTimer.Chan(): { ackLevel := tr.taskAckManager.GetAckLevel() if size, err := tr.db.GetTaskListSize(ackLevel); err == nil { tr.scope.UpdateGauge(metrics.TaskCountPerTaskListGauge, float64(size)) } if err := tr.handleErr(tr.persistAckLevel()); err != nil { tr.logger.Error("Persistent store operation failure", tag.StoreOperationUpdateTaskList, tag.Error(err)) // keep going as saving ack is not critical } tr.Signal() // periodically signal pump to check persistence for tasks updateAckTimer.Reset(tr.config.UpdateAckInterval()) } } } } func (tr *taskReader) getTaskBatchWithRange(readLevel int64, maxReadLevel int64) ([]*persistence.TaskInfo, error) { var response *persistence.GetTasksResponse // Validate batch size to prevent requesting 0 tasks batchSize := tr.config.GetTasksBatchSize() if batchSize <= 0 { fallback := dynamicproperties.IntKeys[dynamicproperties.MatchingGetTasksBatchSize].DefaultValue tr.logger.Warn("matching.getTasksBatchSize is set to invalid value, using default value", tag.Dynamic("invalidBatchSize", batchSize), tag.Dynamic("correctedBatchSize", fallback)) batchSize = fallback } op := func(ctx context.Context) (err error) { response, err = tr.db.GetTasks(readLevel, maxReadLevel, batchSize) return } err := tr.throttleRetry.Do(context.Background(), op) if err != nil { tr.logger.Error("Persistent store operation failure", tag.StoreOperationGetTasks, tag.Error(err), tag.WorkflowTaskListName(tr.taskListID.GetName()), tag.WorkflowTaskListType(tr.taskListID.GetType())) return nil, err } return response.Tasks, nil } // Returns a batch of tasks from persistence starting form current read level. // Also return a number that can be used to update readLevel // Also return a bool to indicate whether read is finished func (tr *taskReader) getTaskBatch(readLevel, maxReadLevel int64) ([]*persistence.TaskInfo, int64, bool, error) { var tasks []*persistence.TaskInfo // counter i is used to break and let caller check whether tasklist is still alive and need resume read. for i := 0; i < 10 && readLevel < maxReadLevel; i++ { upper := readLevel + int64(tr.config.ReadRangeSize()) if upper > maxReadLevel { upper = maxReadLevel } tasks, err := tr.getTaskBatchWithRange(readLevel, upper) if err != nil { return nil, readLevel, true, err } // return as long as it grabs any tasks if len(tasks) > 0 { return tasks, upper, true, nil } readLevel = upper } return tasks, readLevel, readLevel == maxReadLevel, nil // caller will update readLevel when no task grabbed } func (tr *taskReader) isTaskExpired(t *persistence.TaskInfo) bool { return !t.Expiry.IsZero() && t.Expiry.After(epochStartTime) && tr.timeSource.Now().After(t.Expiry) } func (tr *taskReader) addTasksToBuffer(tasks []*persistence.TaskInfo) bool { for _, t := range tasks { if !tr.addSingleTaskToBuffer(t) { return false // we are shutting down the task list } } return true } func (tr *taskReader) addSingleTaskToBuffer(task *persistence.TaskInfo) bool { if tr.isTaskExpired(task) { tr.scope.IncCounter(metrics.ExpiredTasksPerTaskListCounter) // Also increment readLevel for expired tasks otherwise it could result in // looping over the same tasks if all tasks read in the batch are expired tr.taskAckManager.SetReadLevel(task.TaskID) return true } err := tr.taskAckManager.ReadItem(task.TaskID) if err != nil { tr.logger.Fatal("critical bug when adding item to ackManager", tag.Error(err)) } // Ignore the isolation duration as we're just putting it into a buffer to be dispatched later. isolationGroup, _ := tr.getIsolationGroupForTask(tr.cancelCtx, task) buffer, ok := tr.taskBuffers[isolationGroup] if !ok { buffer = tr.taskBuffers[defaultTaskBufferIsolationGroup] } select { case buffer <- task: return true case <-tr.cancelCtx.Done(): return false } } func (tr *taskReader) persistAckLevel() error { ackLevel := tr.taskAckManager.GetAckLevel() if ackLevel >= 0 { maxReadLevel := tr.taskWriter.GetMaxReadLevel() // note: this metrics is only an estimation for the lag. taskID in DB may not be continuous, // especially when task list ownership changes. tr.scope.UpdateGauge(metrics.TaskLagPerTaskListGauge, float64(maxReadLevel-ackLevel)) return tr.db.UpdateState(ackLevel) } return nil } // completeTask marks a task as processed. Only tasks created by taskReader (i.e. backlog from db) reach // here. As part of completion: // - task is deleted from the database when err is nil // - new task is created and current task is deleted when err is not nil func (tr *taskReader) completeTask(task *persistence.TaskInfo, err error) { if err != nil { // failed to start the task. // We cannot just remove it from persistence because then it will be lost. // We handle this by writing the task back to persistence with a higher taskID. // This will allow subsequent tasks to make progress, and hopefully by the time this task is picked-up // again the underlying reason for failing to start will be resolved. // Note that RecordTaskStarted only fails after retrying for a long time, so a single task will not be // re-written to persistence frequently. op := func(ctx context.Context) error { _, err := tr.taskWriter.appendTask(ctx, task) return err } err = tr.throttleRetry.Do(context.Background(), op) if err != nil { // OK, we also failed to write to persistence. // This should only happen in very extreme cases where persistence is completely down. // We still can't lose the old task so we just unload the entire task list tr.logger.Error("Failed to complete task", tag.Error(err)) tr.onFatalErr() return } tr.Signal() } ackLevel := tr.taskAckManager.AckItem(task.TaskID) tr.taskGC.Run(ackLevel) } func (tr *taskReader) newDispatchContext(isolationGroup string, isolationDuration time.Duration) (context.Context, context.CancelFunc) { rps := float64(tr.rateLimit()) if isolationGroup != "" || rps > 1e-7 { // 1e-7 is a random number chosen to avoid overflow, normally user don't set such a low rps timeout := tr.getDispatchTimeout(rps, isolationDuration) domainEntry, err := tr.domainCache.GetDomainByID(tr.taskListID.GetDomainID()) if err != nil { // we don't know if the domain is active in the current cluster, assume it is active and set the timeout return context.WithTimeout(tr.cancelCtx, timeout) } if domainEntry.IsActiveIn(tr.clusterMetadata.GetCurrentClusterName()) { tr.logger.Debug("domain is active in the current cluster, setting timeout", tag.WorkflowDomainName(domainEntry.GetInfo().Name), ) // if the domain is active in the current cluster, set the timeout return context.WithTimeout(tr.cancelCtx, timeout) } } return tr.cancelCtx, func() {} } func (tr *taskReader) getDispatchTimeout(rps float64, isolationDuration time.Duration) time.Duration { // this is the minimum timeout required to dispatch a task, if the timeout value is smaller than this // async task dispatch can be completely throttled, which could happen when ratePerSecond is pretty low minTimeout := time.Duration(float64(len(tr.taskBuffers))/rps) * time.Second // timeout = max (min(asyncDispatchTimeout, isolationDuration), minTimeout) timeout := tr.config.AsyncTaskDispatchTimeout() if timeout > isolationDuration && isolationDuration != noIsolationTimeout { timeout = isolationDuration } if timeout < minTimeout { timeout = minTimeout } return timeout } func (tr *taskReader) dispatchSingleTaskFromBufferWithRetries(taskInfo *persistence.TaskInfo) (breakDispatchLoop bool) { // retry loop for dispatching a single task for { breakDispatchLoop, breakRetryLoop := tr.dispatchSingleTaskFromBuffer(taskInfo) if breakRetryLoop { return breakDispatchLoop } } } func (tr *taskReader) dispatchSingleTaskFromBuffer(taskInfo *persistence.TaskInfo) (breakDispatchLoop bool, breakRetries bool) { e := event.E{ TaskListName: tr.taskListID.GetName(), TaskListType: tr.taskListID.GetType(), TaskListKind: &tr.tlMgr.taskListKind, TaskInfo: *taskInfo, } if tr.isTaskExpired(taskInfo) { e.EventName = "Task Expired" event.Log(e) tr.scope.IncCounter(metrics.ExpiredTasksPerTaskListCounter) tr.taskAckManager.AckItem(taskInfo.TaskID) return false, true } isolationGroup, isolationDuration := tr.getIsolationGroupForTask(tr.cancelCtx, taskInfo) _, isolationGroupIsKnown := tr.taskBuffers[isolationGroup] if !isolationGroupIsKnown { isolationGroup = defaultTaskBufferIsolationGroup isolationDuration = noIsolationTimeout } task := newInternalTask(taskInfo, tr.completeTask, types.TaskSourceDbBacklog, "", false, nil, isolationGroup) dispatchCtx, cancel := tr.newDispatchContext(isolationGroup, isolationDuration) timerScope := tr.scope.StartTimer(metrics.AsyncMatchLatencyPerTaskList) err := tr.dispatchTask(dispatchCtx, task) timerScope.Stop() cancel() if err == nil { e.EventName = "Dispatched Buffered Task" event.Log(e) return false, true } if errors.Is(err, context.Canceled) { e.EventName = "Dispatch Failed because Context Cancelled" event.Log(e) tr.logger.Info("Tasklist manager context is cancelled, shutting down") return true, true } if errors.Is(err, context.DeadlineExceeded) { // it only happens when isolation is enabled and there is no pollers from the given isolation group // if this happens, we don't want to block the task dispatching, because there might be pollers from // other isolation groups, we just simply continue and dispatch the task to a new isolation group which // has pollers tr.logger.Warn("Async task dispatch timed out", tag.IsolationGroup(isolationGroup), tag.WorkflowRunID(taskInfo.RunID), tag.WorkflowID(taskInfo.WorkflowID), tag.TaskID(taskInfo.TaskID), tag.Error(err), tag.WorkflowDomainID(taskInfo.DomainID), ) e.EventName = "Dispatch Timed Out" event.Log(e) tr.scope.IncCounter(metrics.AsyncMatchDispatchTimeoutCounterPerTaskList) return false, false } if errors.Is(err, ErrTasklistThrottled) { e.EventName = "Dispatch failed because throttled. Will retry dispatch" event.Log(e) tr.scope.IncCounter(metrics.BufferThrottlePerTaskListCounter) runtime.Gosched() return false, false } if errors.Is(err, errTaskNotStarted) { e.EventName = "Dispatch failed on completing task on the passive side because task not started." event.Log(e) return false, false } if errors.Is(err, errWaitTimeNotReachedForEntityNotExists) { e.EventName = "Dispatch failed on completing task on the passive side because workflow could not be found and not enough time has passed to complete the task. Will retry dispatch" event.Log(e) return false, false } e.EventName = "Dispatch failed because of unknown error. Will retry dispatch" e.Payload = map[string]any{ "error": err, } event.Log(e) e.Payload = nil tr.scope.IncCounter(metrics.BufferUnknownTaskDispatchError) tr.logger.Error("unknown error while dispatching task", tag.Error(err), tag.IsolationGroup(isolationGroup), tag.WorkflowRunID(taskInfo.RunID), tag.WorkflowID(taskInfo.WorkflowID), tag.TaskID(taskInfo.TaskID), tag.WorkflowDomainID(taskInfo.DomainID), ) return false, false } ================================================ FILE: service/matching/tasklist/task_reader_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "context" "errors" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" commonConfig "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/matching/config" ) const defaultIsolationGroup = "a" const defaultAsyncDispatchTimeout = 3 * time.Second var defaultIsolationGroups = []string{ "a", "b", "c", "d", } func TestDispatchSingleTaskFromBuffer(t *testing.T) { testCases := []struct { name string allowances func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) ttl int breakDispatch bool breakRetries bool }{ { name: "success - no isolation", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return "", -1 } markCalled := requireCallbackInvocation(t, "expected task to be dispatched") reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { assert.Equal(t, "", task.isolationGroup) markCalled() return nil } }, breakDispatch: false, breakRetries: true, }, { name: "success - isolation", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return defaultIsolationGroup, -1 } markCalled := requireCallbackInvocation(t, "expected task to be dispatched") reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { assert.Equal(t, defaultIsolationGroup, task.isolationGroup) markCalled() return nil } }, breakDispatch: false, breakRetries: true, }, { name: "success - unknown isolation group", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return "mystery group", -1 } markCalled := requireCallbackInvocation(t, "expected task to be dispatched") reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { assert.Equal(t, "", task.isolationGroup) markCalled() return nil } }, breakDispatch: false, breakRetries: true, }, { name: "success - skip expired tasks", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return defaultIsolationGroup, -1 } reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { t.Fatal("task must not be dispatched") return nil } }, ttl: -2, breakDispatch: false, breakRetries: true, }, { name: "Error - context cancelled, should stop", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return defaultIsolationGroup, -1 } reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { return context.Canceled } }, breakDispatch: true, breakRetries: true, }, { name: "Error - Deadline Exceeded, should retry", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return defaultIsolationGroup, -1 } reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { return context.DeadlineExceeded } }, breakDispatch: false, breakRetries: false, }, { name: "Error - throttled, should retry", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return defaultIsolationGroup, -1 } reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { return ErrTasklistThrottled } }, breakDispatch: false, breakRetries: false, }, { name: "Error - unknown, should retry", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return defaultIsolationGroup, -1 } reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { return errors.New("wow") } }, breakDispatch: false, breakRetries: false, }, { name: "Error - task not started and not expired, should retry", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return defaultIsolationGroup, -1 } reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { return errTaskNotStarted } }, ttl: 100, breakDispatch: false, breakRetries: false, }, { name: "Error - task not started and expired, should retry", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return defaultIsolationGroup, -1 } reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { mockTime.Advance(time.Hour) return errTaskNotStarted } }, ttl: 1, breakDispatch: false, breakRetries: false, }, { name: "Error - time not reached to complete task without workflow execution, should retry", allowances: func(t *testing.T, reader *taskReader, mockTime clock.MockedTimeSource) { reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return defaultIsolationGroup, -1 } reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { return errWaitTimeNotReachedForEntityNotExists } }, breakDispatch: false, breakRetries: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) timeSource := clock.NewMockedTimeSource() c := defaultConfig() tlm := createTestTaskListManagerWithConfig(t, testlogger.New(t), controller, c, timeSource) reader := tlm.taskReader tc.allowances(t, reader, timeSource) taskInfo := newTask(timeSource) taskInfo.Expiry = timeSource.Now().Add(time.Duration(tc.ttl) * time.Second) breakDispatch, breakRetries := reader.dispatchSingleTaskFromBuffer(taskInfo) assert.Equal(t, tc.breakDispatch, breakDispatch) assert.Equal(t, tc.breakRetries, breakRetries) }) } } func TestGetDispatchTimeout(t *testing.T) { testCases := []struct { name string isolationDuration time.Duration dispatchRps float64 expected time.Duration }{ { name: "default - async dispatch timeout", isolationDuration: noIsolationTimeout, dispatchRps: 1000, expected: defaultAsyncDispatchTimeout, }, { name: "isolation duration below async dispatch timeout", isolationDuration: 1 * time.Second, dispatchRps: 1000, expected: 1 * time.Second, }, { name: "isolation duration above async dispatch timeout", isolationDuration: 5 * time.Second, dispatchRps: 1000, expected: defaultAsyncDispatchTimeout, }, { name: "no isolation - low dispatch rps extends timeout", isolationDuration: noIsolationTimeout, dispatchRps: 0.1, // rate is divided by 4 isolation groups (plus default buffer) and only one task gets dispatched per 10 seconds expected: 50 * time.Second, }, { name: "with isolation - low dispatch rps extends timeout", isolationDuration: time.Second, dispatchRps: 0.1, // rate is divided by 4 isolation groups (plus default buffer) and only one task gets dispatched per 10 seconds // This means taskIsolationDuration is extended, and we don't leak tasks as quickly if the // task list has a very low RPS expected: 50 * time.Second, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) timeSource := clock.NewMockedTimeSource() c := defaultConfig() tlm := createTestTaskListManagerWithConfig(t, testlogger.New(t), controller, c, timeSource) reader := tlm.taskReader actual := reader.getDispatchTimeout(tc.dispatchRps, tc.isolationDuration) assert.Equal(t, tc.expected, actual) }) } } func TestTaskPump(t *testing.T) { cases := []struct { name string tasks []*persistence.TaskInfo assertions func(t *testing.T, tasks []*InternalTask) }{ { name: "single task", tasks: []*persistence.TaskInfo{ taskWithIsolationGroup("a"), }, assertions: func(t *testing.T, tasks []*InternalTask) { assert.Equal(t, 1, len(tasks)) assert.Equal(t, "a", tasks[0].isolationGroup) }, }, { name: "unknown isolation group", tasks: []*persistence.TaskInfo{ taskWithIsolationGroup("unknown"), }, assertions: func(t *testing.T, tasks []*InternalTask) { assert.Equal(t, 1, len(tasks)) assert.Equal(t, "", tasks[0].isolationGroup) }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) timeSource := clock.NewMockedTimeSource() c := defaultConfig() tlm := createTestTaskListManagerWithConfig(t, testlogger.New(t), controller, c, timeSource) reader := tlm.taskReader var lock sync.Mutex received := make([]*InternalTask, 0) done := make(chan struct{}) reader.dispatchTask = func(ctx context.Context, task *InternalTask) error { lock.Lock() defer lock.Unlock() received = append(received, task) if len(received) == len(tc.tasks) { close(done) } return nil } reader.getIsolationGroupForTask = func(ctx context.Context, info *persistence.TaskInfo) (string, time.Duration) { return info.PartitionConfig["isolation-group"], -1 } for i, taskInfo := range tc.tasks { taskInfo.CreatedTime = timeSource.Now() taskInfo.Expiry = timeSource.Now().Add(10 * time.Second) _, err := tlm.db.CreateTasks([]*persistence.CreateTaskInfo{ { Data: taskInfo, TaskID: int64(1 + i), }, }) require.NoError(t, err) } // This is how the reader knows the range that's valid to read tlm.taskWriter.maxReadLevel = int64(len(tc.tasks)) ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() reader.Start() select { case <-ctx.Done(): t.Fatalf("timed out waiting for tasks to be dispatched") case <-done: } reader.Stop() tc.assertions(t, received) }) } } func defaultConfig() *config.Config { config := config.NewConfig(dynamicconfig.NewNopCollection(), "some random hostname", commonConfig.RPC{}, func() []string { return defaultIsolationGroups }) config.EnableTasklistIsolation = dynamicproperties.GetBoolPropertyFnFilteredByDomain(true) config.LongPollExpirationInterval = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(100 * time.Millisecond) config.MaxTaskDeleteBatchSize = dynamicproperties.GetIntPropertyFilteredByTaskListInfo(1) config.GetTasksBatchSize = dynamicproperties.GetIntPropertyFilteredByTaskListInfo(10) config.AsyncTaskDispatchTimeout = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(defaultAsyncDispatchTimeout) config.LocalTaskWaitTime = dynamicproperties.GetDurationPropertyFnFilteredByTaskListInfo(time.Millisecond) return config } func taskWithIsolationGroup(ig string) *persistence.TaskInfo { return &persistence.TaskInfo{ DomainID: "domain-id", WorkflowID: "workflow-id", RunID: "run-id", TaskID: 1, ScheduleID: 2, ScheduleToStartTimeoutSeconds: 10, PartitionConfig: map[string]string{ "isolation-group": ig, }, } } func newTask(timeSource clock.TimeSource) *persistence.TaskInfo { return &persistence.TaskInfo{ DomainID: "domain-id", WorkflowID: "workflow-id", RunID: "run-id", TaskID: 1, ScheduleID: 2, ScheduleToStartTimeoutSeconds: 10, Expiry: timeSource.Now().Add(10 * time.Second), CreatedTime: timeSource.Now(), PartitionConfig: map[string]string{ "isolation-group": defaultIsolationGroup, }, } } func requireCallbackInvocation(t *testing.T, msg string) func() { called := false t.Cleanup(func() { if !called { t.Fatal(msg) } }) return func() { called = true } } func TestTaskReaderBatchSizeValidation(t *testing.T) { tests := []struct { name string configValue int expectedBuffer int }{ { name: "valid positive batch size", configValue: 1000, expectedBuffer: 999, // buffer size is batchSize - 1 }, { name: "valid small batch size", configValue: 1, expectedBuffer: 0, // buffer size is batchSize - 1 }, { name: "zero batch size should be corrected to default (1000)", configValue: 0, expectedBuffer: 999, // corrected to 1000, so buffer is 999 }, { name: "negative batch size should be corrected to default (1000)", configValue: -5, expectedBuffer: 999, // corrected to 1000, so buffer is 999 }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { batchSize := tt.configValue if batchSize <= 0 { // This is the validation logic from newTaskReader - use default value (1000) batchSize = 1000 } // Test buffer creation with validated batch size expectedCapacity := batchSize - 1 if expectedCapacity < 0 { expectedCapacity = 0 } // Simulate the buffer creation logic buffer := make(chan *persistence.TaskInfo, expectedCapacity) actualCapacity := cap(buffer) assert.Equal(t, tt.expectedBuffer, actualCapacity, "Task buffer should have correct capacity after validation") }) } } ================================================ FILE: service/matching/tasklist/task_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) func TestNewInternalTask(t *testing.T) { cases := []struct { name string partitionConfig map[string]string source types.TaskSource forwardedFrom string forSyncMatch bool isolationGroup string expectedPartitionConfig map[string]string additionalAssertions func(t *testing.T, task *InternalTask) }{ { name: "sync match", source: types.TaskSourceHistory, forSyncMatch: true, additionalAssertions: func(t *testing.T, task *InternalTask) { // Only initialized for sync match assert.NotNil(t, task.ResponseC) assert.True(t, task.IsSyncMatch()) }, }, { name: "async match", source: types.TaskSourceDbBacklog, forSyncMatch: false, additionalAssertions: func(t *testing.T, task *InternalTask) { // Only initialized for sync match assert.Nil(t, task.ResponseC) assert.False(t, task.IsSyncMatch()) }, }, { name: "forwarded from history", source: types.TaskSourceDbBacklog, forSyncMatch: true, forwardedFrom: "elsewhere", additionalAssertions: func(t *testing.T, task *InternalTask) { assert.True(t, task.IsForwarded()) assert.True(t, task.IsSyncMatch()) }, }, { name: "forwarded from backlog", source: types.TaskSourceDbBacklog, forSyncMatch: true, forwardedFrom: "elsewhere", additionalAssertions: func(t *testing.T, task *InternalTask) { assert.True(t, task.IsForwarded()) // Still technically sync match, just on a different host assert.True(t, task.IsSyncMatch()) }, }, { name: "tasklist isolation", source: types.TaskSourceDbBacklog, isolationGroup: "a", partitionConfig: map[string]string{ isolationgroup.GroupKey: "a", isolationgroup.WorkflowIDKey: "workflowID", }, expectedPartitionConfig: map[string]string{ isolationgroup.OriginalGroupKey: "a", isolationgroup.GroupKey: "a", isolationgroup.WorkflowIDKey: "workflowID", }, }, { name: "tasklist isolation - leaked", source: types.TaskSourceDbBacklog, isolationGroup: "", partitionConfig: map[string]string{ isolationgroup.GroupKey: "a", isolationgroup.WorkflowIDKey: "workflowID", }, expectedPartitionConfig: map[string]string{ isolationgroup.OriginalGroupKey: "a", isolationgroup.GroupKey: "", isolationgroup.WorkflowIDKey: "workflowID", }, }, { name: "tasklist isolation - forwarded", source: types.TaskSourceDbBacklog, isolationGroup: "", partitionConfig: map[string]string{ isolationgroup.OriginalGroupKey: "a", isolationgroup.GroupKey: "", isolationgroup.WorkflowIDKey: "workflowID", }, expectedPartitionConfig: map[string]string{ isolationgroup.OriginalGroupKey: "a", isolationgroup.GroupKey: "", isolationgroup.WorkflowIDKey: "workflowID", }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { completionFunc := func(_ *persistence.TaskInfo, _ error) {} taskInfo := defaultTaskInfo(tc.partitionConfig) activityDispatchInfo := &types.ActivityTaskDispatchInfo{WorkflowDomain: "domain"} task := newInternalTask(taskInfo, completionFunc, tc.source, tc.forwardedFrom, tc.forSyncMatch, activityDispatchInfo, tc.isolationGroup) assert.Equal(t, defaultTaskInfo(tc.expectedPartitionConfig), task.Event.TaskInfo) assert.NotNil(t, task.Event.completionFunc) assert.Equal(t, tc.source, task.source) assert.Equal(t, tc.forwardedFrom, task.forwardedFrom) assert.Equal(t, tc.isolationGroup, task.isolationGroup) assert.Equal(t, activityDispatchInfo, task.ActivityTaskDispatchInfo) if tc.additionalAssertions != nil { tc.additionalAssertions(t, task) } }) } } func defaultTaskInfo(partitionConfig map[string]string) *persistence.TaskInfo { return &persistence.TaskInfo{ DomainID: "DomainID", WorkflowID: "WorkflowID", RunID: "RunID", TaskID: 1, ScheduleID: 2, ScheduleToStartTimeoutSeconds: 3, Expiry: time.UnixMicro(4), CreatedTime: time.UnixMicro(5), PartitionConfig: partitionConfig, } } ================================================ FILE: service/matching/tasklist/task_writer.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "context" "errors" "fmt" "sync/atomic" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/config" "github.com/uber/cadence/service/matching/event" ) type ( writeTaskResponse struct { err error persistenceResponse *persistence.CreateTasksResponse } writeTaskRequest struct { taskInfo *persistence.TaskInfo responseCh chan<- *writeTaskResponse } taskIDBlock struct { start int64 end int64 } // taskWriter writes tasks sequentially to persistence taskWriter struct { db *taskListDB config *config.TaskListConfig taskListID *Identifier taskAckManager messaging.AckManager appendCh chan *writeTaskRequest taskIDBlock taskIDBlock maxReadLevel int64 stopped int64 // set to 1 if the writer is stopped or is shutting down logger log.Logger scope metrics.Scope stopCh chan struct{} // shutdown signal for all routines in this class throttleRetry *backoff.ThrottleRetry handleErr func(error) error onFatalErr func() } ) // errShutdown indicates that the task list is shutting down var errShutdown = errors.New("task list shutting down") func newTaskWriter(tlMgr *taskListManagerImpl) *taskWriter { return &taskWriter{ db: tlMgr.db, config: tlMgr.config, taskListID: tlMgr.taskListID, taskAckManager: tlMgr.taskAckManager, stopCh: make(chan struct{}), appendCh: make(chan *writeTaskRequest, tlMgr.config.OutstandingTaskAppendsThreshold()), logger: tlMgr.logger, scope: tlMgr.scope, handleErr: tlMgr.handleErr, onFatalErr: tlMgr.Stop, throttleRetry: backoff.NewThrottleRetry( backoff.WithRetryPolicy(persistenceOperationRetryPolicy), backoff.WithRetryableError(persistence.IsTransientError), ), } } func (w *taskWriter) Start() error { // Make sure to grab the range first before starting task writer, as it needs the range to initialize maxReadLevel state, err := w.renewLeaseWithRetry() if err != nil { return err } w.taskAckManager.SetAckLevel(state.ackLevel) block := rangeIDToTaskIDBlock(state.rangeID, w.config.RangeSize) w.taskIDBlock = block w.maxReadLevel = block.start - 1 go w.taskWriterLoop() return nil } // Stop stops the taskWriter func (w *taskWriter) Stop() { if atomic.CompareAndSwapInt64(&w.stopped, 0, 1) { close(w.stopCh) } } func (w *taskWriter) isStopped() bool { return atomic.LoadInt64(&w.stopped) == 1 } func (w *taskWriter) appendTask(ctx context.Context, taskInfo *persistence.TaskInfo) (*persistence.CreateTasksResponse, error) { // Make context to respect the append task timeout. tCtx, cancel := context.WithTimeout(ctx, w.config.AppendTaskTimeout()) defer cancel() // If the writer has already stopped, it returns an error if w.isStopped() { return nil, errShutdown } // Create a result channel for this request. // Use buffer size 1 so the writer can send one response even if this call is already timed out. // Without a buffer, the writer can block forever when no receiver is waiting. ch := make(chan *writeTaskResponse, 1) req := &writeTaskRequest{ taskInfo: taskInfo, responseCh: ch, } select { case w.appendCh <- req: // If enqueue succeed select { // Wait for the task result after the enqueue case r := <-ch: // If the writer sends the task result after the DB I/O task return r.persistenceResponse, r.err case <-tCtx.Done(): // If the writer didn't send the task result within a timeout threshold return nil, fmt.Errorf("failed to receive a success response from the channel within a timeout threshold: %w", tCtx.Err()) case <-w.stopCh: // if we are shutting down, this request will never make // it to cassandra, just bail out and fail this request return nil, errShutdown } case <-tCtx.Done(): // If a channel is full and timed out waiting to enqueue return nil, createServiceBusyError("task queue is full and timed out waiting to enqueue") } } func (w *taskWriter) GetMaxReadLevel() int64 { return atomic.LoadInt64(&w.maxReadLevel) } func (w *taskWriter) allocTaskIDs(count int) ([]int64, error) { result := make([]int64, count) for i := 0; i < count; i++ { if w.taskIDBlock.start > w.taskIDBlock.end { // we ran out of current allocation block newBlock, err := w.allocTaskIDBlock(w.taskIDBlock.end) if err != nil { return nil, err } w.taskIDBlock = newBlock } result[i] = w.taskIDBlock.start w.taskIDBlock.start++ } return result, nil } func (w *taskWriter) allocTaskIDBlock(prevBlockEnd int64) (taskIDBlock, error) { currBlock := rangeIDToTaskIDBlock(w.db.RangeID(), w.config.RangeSize) if currBlock.end != prevBlockEnd { return taskIDBlock{}, fmt.Errorf("allocTaskIDBlock: invalid state: prevBlockEnd:%v != currTaskIDBlock:%+v", prevBlockEnd, currBlock) } state, err := w.renewLeaseWithRetry() if err != nil { return taskIDBlock{}, err } return rangeIDToTaskIDBlock(state.rangeID, w.config.RangeSize), nil } func (w *taskWriter) renewLeaseWithRetry() (taskListState, error) { var newState taskListState op := func(ctx context.Context) (err error) { newState, err = w.db.RenewLease() return } w.scope.IncCounter(metrics.LeaseRequestPerTaskListCounter) err := w.throttleRetry.Do(context.Background(), op) if err != nil { w.scope.IncCounter(metrics.LeaseFailurePerTaskListCounter) w.onFatalErr() return newState, err } return newState, nil } func (w *taskWriter) taskWriterLoop() { writerLoop: for { select { case request := <-w.appendCh: { if w.isStopped() { break writerLoop } // read a batch of requests from the channel reqs := []*writeTaskRequest{request} reqs = w.getWriteBatch(reqs) batchSize := len(reqs) maxReadLevel := int64(0) taskIDs, err := w.allocTaskIDs(batchSize) if err != nil { w.logger.Error("error allocating task ids", tag.Error(err), ) w.sendWriteResponse(reqs, err, nil) continue writerLoop } tasks := []*persistence.CreateTaskInfo{} events := []event.E{} for i, req := range reqs { tasks = append(tasks, &persistence.CreateTaskInfo{ TaskID: taskIDs[i], Data: req.taskInfo, }) kind := types.TaskListKind(w.db.taskListKind) events = append(events, event.E{ TaskListName: w.db.taskListName, TaskListKind: &kind, TaskListType: w.db.taskType, TaskInfo: *req.taskInfo, EventName: "Task Written to DB", }) maxReadLevel = taskIDs[i] } resp, err := w.db.CreateTasks(tasks) err = w.handleErr(err) if err != nil { w.logger.Error("Persistent store operation failure", tag.StoreOperationCreateTasks, tag.Error(err), tag.Number(taskIDs[0]), tag.NextNumber(taskIDs[batchSize-1]), ) } else { event.Log(events...) } // Update the maxReadLevel after the writes are completed. if maxReadLevel > 0 { atomic.StoreInt64(&w.maxReadLevel, maxReadLevel) } w.sendWriteResponse(reqs, err, resp) } case <-w.stopCh: // we don't close the appendCh here // because that can cause "send on closed channel" panic on the appendTask() break writerLoop } } } func (w *taskWriter) getWriteBatch(reqs []*writeTaskRequest) []*writeTaskRequest { readLoop: for i := 0; i < w.config.MaxTaskBatchSize(); i++ { select { case req := <-w.appendCh: reqs = append(reqs, req) default: // channel is empty, don't block break readLoop } } return reqs } func (w *taskWriter) sendWriteResponse(reqs []*writeTaskRequest, err error, persistenceResponse *persistence.CreateTasksResponse) { for _, req := range reqs { resp := &writeTaskResponse{ err: err, persistenceResponse: persistenceResponse, } // appendTask() listens stopCh and terminates early without reading from responseCh. // Therefore we need to listen stopCh here to avoid getting stuck while pushing response to responseCh. select { case <-w.stopCh: case req.responseCh <- resp: } } } ================================================ FILE: service/matching/tasklist/testing.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package tasklist import ( "context" "fmt" "sync" "testing" "time" "github.com/davecgh/go-spew/spew" "github.com/emirpasic/gods/maps/treemap" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" ) var _ persistence.TaskManager = (*TestTaskManager)(nil) // Asserts that interface is indeed implemented type ( TestTaskManager struct { sync.Mutex t *testing.T taskLists map[Identifier]*testTaskListManager logger log.Logger timeSource clock.TimeSource } testTaskListManager struct { sync.RWMutex rangeID int64 ackLevel int64 kind int createTaskCount int tasks *treemap.Map adaptivePartitionConfig *persistence.TaskListPartitionConfig } ) func newTestTaskListManager() *testTaskListManager { return &testTaskListManager{tasks: treemap.NewWith(int64Comparator)} } func NewTestTaskManager(t *testing.T, logger log.Logger, timeSource clock.TimeSource) *TestTaskManager { return &TestTaskManager{ t: t, taskLists: make(map[Identifier]*testTaskListManager), logger: logger, timeSource: timeSource, } } func (m *TestTaskManager) GetName() string { return "test" } func (m *TestTaskManager) Close() { } // LeaseTaskList provides a mock function with given fields: ctx, request func (m *TestTaskManager) LeaseTaskList( _ context.Context, request *persistence.LeaseTaskListRequest, ) (*persistence.LeaseTaskListResponse, error) { tlm := m.getTaskListManager(NewTestTaskListID(m.t, request.DomainID, request.TaskList, request.TaskType)) tlm.Lock() defer tlm.Unlock() if request.RangeID > 0 && request.RangeID != tlm.rangeID { return nil, &persistence.ConditionFailedError{ Msg: fmt.Sprintf("leaseTaskList:renew failed: taskList:%v, taskListType:%v, haveRangeID:%v, gotRangeID:%v", request.TaskList, request.TaskType, request.RangeID, tlm.rangeID), } } tlm.rangeID++ tlm.kind = request.TaskListKind m.logger.Debug(fmt.Sprintf("testTaskManager.LeaseTaskList rangeID=%v", tlm.rangeID)) return &persistence.LeaseTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ AckLevel: tlm.ackLevel, DomainID: request.DomainID, Name: request.TaskList, TaskType: request.TaskType, RangeID: tlm.rangeID, Kind: tlm.kind, AdaptivePartitionConfig: tlm.adaptivePartitionConfig, }, }, nil } func (m *TestTaskManager) GetTaskList( _ context.Context, request *persistence.GetTaskListRequest, ) (*persistence.GetTaskListResponse, error) { tlm := m.getTaskListManager(NewTestTaskListID(m.t, request.DomainID, request.TaskList, request.TaskType)) tlm.RLock() defer tlm.RUnlock() return &persistence.GetTaskListResponse{ TaskListInfo: &persistence.TaskListInfo{ AckLevel: tlm.ackLevel, DomainID: request.DomainID, Name: request.TaskList, TaskType: request.TaskType, RangeID: tlm.rangeID, Kind: tlm.kind, AdaptivePartitionConfig: tlm.adaptivePartitionConfig, }, }, nil } // UpdateTaskList provides a mock function with given fields: ctx, request func (m *TestTaskManager) UpdateTaskList( _ context.Context, request *persistence.UpdateTaskListRequest, ) (*persistence.UpdateTaskListResponse, error) { m.logger.Debug(fmt.Sprintf("testTaskManager.UpdateTaskList taskListInfo=%v, ackLevel=%v", request.TaskListInfo, request.TaskListInfo.AckLevel)) tli := request.TaskListInfo tlm := m.getTaskListManager(NewTestTaskListID(m.t, tli.DomainID, tli.Name, tli.TaskType)) tlm.Lock() defer tlm.Unlock() if tlm.rangeID != tli.RangeID { return nil, &persistence.ConditionFailedError{ Msg: fmt.Sprintf("Failed to update task list: name=%v, type=%v, expected rangeID=%v, input rangeID=%v", tli.Name, tli.TaskType, tlm.rangeID, tli.RangeID), } } tlm.ackLevel = tli.AckLevel return &persistence.UpdateTaskListResponse{}, nil } // CompleteTask provides a mock function with given fields: ctx, request func (m *TestTaskManager) CompleteTask( _ context.Context, request *persistence.CompleteTaskRequest, ) error { m.logger.Debug(fmt.Sprintf("testTaskManager.CompleteTask taskID=%v, ackLevel=%v", request.TaskID, request.TaskList.AckLevel)) if request.TaskID <= 0 { panic(fmt.Errorf("Invalid taskID=%v", request.TaskID)) } tli := request.TaskList tlm := m.getTaskListManager(NewTestTaskListID(m.t, tli.DomainID, tli.Name, tli.TaskType)) tlm.Lock() defer tlm.Unlock() tlm.tasks.Remove(request.TaskID) return nil } // CompleteTasksLessThan provides a mock function with given fields: ctx, request func (m *TestTaskManager) CompleteTasksLessThan( _ context.Context, request *persistence.CompleteTasksLessThanRequest, ) (*persistence.CompleteTasksLessThanResponse, error) { m.logger.Debug(fmt.Sprintf("testTaskManager.CompleteTasksLessThan taskID=%v", request.TaskID)) tlm := m.getTaskListManager(NewTestTaskListID(m.t, request.DomainID, request.TaskListName, request.TaskType)) tlm.Lock() defer tlm.Unlock() rowsDeleted := 0 keys := tlm.tasks.Keys() for _, key := range keys { id := key.(int64) if id <= request.TaskID { tlm.tasks.Remove(id) rowsDeleted++ } } return &persistence.CompleteTasksLessThanResponse{TasksCompleted: rowsDeleted}, nil } // ListTaskList provides a mock function with given fields: ctx, request func (m *TestTaskManager) ListTaskList( _ context.Context, request *persistence.ListTaskListRequest, ) (*persistence.ListTaskListResponse, error) { return nil, fmt.Errorf("unsupported operation") } // DeleteTaskList provides a mock function with given fields: ctx, request func (m *TestTaskManager) DeleteTaskList( _ context.Context, request *persistence.DeleteTaskListRequest, ) error { m.Lock() defer m.Unlock() key := NewTestTaskListID(m.t, request.DomainID, request.TaskListName, request.TaskListType) delete(m.taskLists, *key) return nil } // CreateTask provides a mock function with given fields: ctx, request func (m *TestTaskManager) CreateTasks( _ context.Context, request *persistence.CreateTasksRequest, ) (*persistence.CreateTasksResponse, error) { domainID := request.TaskListInfo.DomainID taskList := request.TaskListInfo.Name taskType := request.TaskListInfo.TaskType rangeID := request.TaskListInfo.RangeID tlm := m.getTaskListManager(NewTestTaskListID(m.t, domainID, taskList, taskType)) tlm.Lock() defer tlm.Unlock() // First validate the entire batch for _, task := range request.Tasks { m.logger.Debug(fmt.Sprintf("testTaskManager.CreateTask taskID=%v, rangeID=%v", task.TaskID, rangeID)) if task.TaskID <= 0 { panic(fmt.Errorf("Invalid taskID=%v", task.TaskID)) } if tlm.rangeID != rangeID { m.logger.Debug(fmt.Sprintf("testTaskManager.CreateTask ConditionFailedError taskID=%v, rangeID: %v, db rangeID: %v", task.TaskID, rangeID, tlm.rangeID)) return nil, &persistence.ConditionFailedError{ Msg: fmt.Sprintf("testTaskManager.CreateTask failed. TaskList: %v, taskType: %v, rangeID: %v, db rangeID: %v", taskList, taskType, rangeID, tlm.rangeID), } } _, ok := tlm.tasks.Get(task.TaskID) if ok { panic(fmt.Sprintf("Duplicated TaskID %v", task.TaskID)) } } // Then insert all tasks if no errors for _, task := range request.Tasks { scheduleID := task.Data.ScheduleID info := &persistence.TaskInfo{ DomainID: domainID, WorkflowID: task.Data.WorkflowID, RunID: task.Data.RunID, ScheduleID: scheduleID, TaskID: task.TaskID, PartitionConfig: task.Data.PartitionConfig, } if task.Data.ScheduleToStartTimeoutSeconds != 0 { info.Expiry = m.timeSource.Now().Add(time.Duration(task.Data.ScheduleToStartTimeoutSeconds) * time.Second) } tlm.tasks.Put(task.TaskID, info) tlm.createTaskCount++ } return &persistence.CreateTasksResponse{}, nil } // GetTasks provides a mock function with given fields: ctx, request func (m *TestTaskManager) GetTasks( _ context.Context, request *persistence.GetTasksRequest, ) (*persistence.GetTasksResponse, error) { m.logger.Debug(fmt.Sprintf("testTaskManager.GetTasks readLevel=%v, maxReadLevel=%v", request.ReadLevel, *request.MaxReadLevel)) tlm := m.getTaskListManager(NewTestTaskListID(m.t, request.DomainID, request.TaskList, request.TaskType)) tlm.Lock() defer tlm.Unlock() var tasks []*persistence.TaskInfo it := tlm.tasks.Iterator() for it.Next() { taskID := it.Key().(int64) if taskID <= request.ReadLevel { continue } if taskID > *request.MaxReadLevel { break } tasks = append(tasks, it.Value().(*persistence.TaskInfo)) } return &persistence.GetTasksResponse{ Tasks: tasks, }, nil } func (m *TestTaskManager) GetTaskListSize(_ context.Context, request *persistence.GetTaskListSizeRequest) (*persistence.GetTaskListSizeResponse, error) { tlm := m.getTaskListManager(NewTestTaskListID(m.t, request.DomainID, request.TaskListName, request.TaskListType)) tlm.Lock() defer tlm.Unlock() count := int64(0) it := tlm.tasks.Iterator() for it.Next() { taskID := it.Key().(int64) if taskID <= request.AckLevel { continue } count++ } return &persistence.GetTaskListSizeResponse{Size: count}, nil } func (m *TestTaskManager) GetOrphanTasks(_ context.Context, request *persistence.GetOrphanTasksRequest) (*persistence.GetOrphanTasksResponse, error) { return &persistence.GetOrphanTasksResponse{}, nil } // getTaskCount returns number of tasks in a task list func (m *TestTaskManager) GetTaskCount(taskList *Identifier) int { tlm := m.getTaskListManager(taskList) tlm.Lock() defer tlm.Unlock() return tlm.tasks.Size() } // getCreateTaskCount returns how many times CreateTask was called func (m *TestTaskManager) GetCreateTaskCount(taskList *Identifier) int { tlm := m.getTaskListManager(taskList) tlm.Lock() defer tlm.Unlock() return tlm.createTaskCount } func (m *TestTaskManager) SetRangeID(taskList *Identifier, rangeID int64) { tlm := m.getTaskListManager(taskList) tlm.Lock() defer tlm.Unlock() tlm.rangeID = rangeID } func (m *TestTaskManager) GetRangeID(taskList *Identifier) int64 { tlm := m.getTaskListManager(taskList) tlm.Lock() defer tlm.Unlock() return tlm.rangeID } func (m *TestTaskManager) getTaskListManager(id *Identifier) *testTaskListManager { m.Lock() defer m.Unlock() result, ok := m.taskLists[*id] if ok { return result } result = newTestTaskListManager() m.taskLists[*id] = result return result } func (m *TestTaskManager) String() string { m.Lock() defer m.Unlock() var result string for id, tl := range m.taskLists { tl.Lock() if id.taskType == persistence.TaskListTypeActivity { result += "Activity" } else { result += "Decision" } result += " task list " + id.name result += "\n" result += fmt.Sprintf("AckLevel=%v\n", tl.ackLevel) result += fmt.Sprintf("CreateTaskCount=%v\n", tl.createTaskCount) result += fmt.Sprintf("RangeID=%v\n", tl.rangeID) result += "Tasks=\n" for _, t := range tl.tasks.Values() { result += spew.Sdump(t) result += "\n" } tl.Unlock() } return result } func NewTestTaskListID(t *testing.T, domainID string, taskListName string, taskType int) *Identifier { id, err := NewIdentifier(domainID, taskListName, taskType) if err != nil { t.Fatal("failed to create task list identifier for test", err) } return id } func int64Comparator(a, b interface{}) int { aAsserted := a.(int64) bAsserted := b.(int64) switch { case aAsserted > bAsserted: return 1 case aAsserted < bAsserted: return -1 default: return 0 } } ================================================ FILE: service/matching/wrappers/grpc/grpc_handler.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package grpc import ( "context" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc" matchingv1 "github.com/uber/cadence/.gen/proto/matching/v1" "github.com/uber/cadence/common/types/mapper/proto" ) func (g GRPCHandler) Register(dispatcher *yarpc.Dispatcher) { dispatcher.Register(matchingv1.BuildMatchingAPIYARPCProcedures(g)) dispatcher.Register(apiv1.BuildMetaAPIYARPCProcedures(g)) } func (g GRPCHandler) Health(ctx context.Context, _ *apiv1.HealthRequest) (*apiv1.HealthResponse, error) { response, err := g.h.Health(ctx) return proto.FromHealthResponse(response), proto.FromError(err) } ================================================ FILE: service/matching/wrappers/grpc/grpc_handler_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" matchingv1 "github.com/uber/cadence/.gen/proto/matching/v1" "github.com/uber/cadence/common/types/mapper/proto" "github.com/uber/cadence/service/matching/handler" ) type GRPCHandler struct { h handler.Handler } func NewGRPCHandler(h handler.Handler) GRPCHandler { return GRPCHandler{h} } func (g GRPCHandler) AddActivityTask(ctx context.Context, request *matchingv1.AddActivityTaskRequest) (*matchingv1.AddActivityTaskResponse, error) { response, err := g.h.AddActivityTask(ctx, proto.ToMatchingAddActivityTaskRequest(request)) return proto.FromMatchingAddActivityTaskResponse(response), proto.FromError(err) } func (g GRPCHandler) AddDecisionTask(ctx context.Context, request *matchingv1.AddDecisionTaskRequest) (*matchingv1.AddDecisionTaskResponse, error) { response, err := g.h.AddDecisionTask(ctx, proto.ToMatchingAddDecisionTaskRequest(request)) return proto.FromMatchingAddDecisionTaskResponse(response), proto.FromError(err) } func (g GRPCHandler) CancelOutstandingPoll(ctx context.Context, request *matchingv1.CancelOutstandingPollRequest) (*matchingv1.CancelOutstandingPollResponse, error) { err := g.h.CancelOutstandingPoll(ctx, proto.ToMatchingCancelOutstandingPollRequest(request)) return &matchingv1.CancelOutstandingPollResponse{}, proto.FromError(err) } func (g GRPCHandler) DescribeTaskList(ctx context.Context, request *matchingv1.DescribeTaskListRequest) (*matchingv1.DescribeTaskListResponse, error) { response, err := g.h.DescribeTaskList(ctx, proto.ToMatchingDescribeTaskListRequest(request)) return proto.FromMatchingDescribeTaskListResponse(response), proto.FromError(err) } func (g GRPCHandler) GetTaskListsByDomain(ctx context.Context, request *matchingv1.GetTaskListsByDomainRequest) (*matchingv1.GetTaskListsByDomainResponse, error) { response, err := g.h.GetTaskListsByDomain(ctx, proto.ToMatchingGetTaskListsByDomainRequest(request)) return proto.FromMatchingGetTaskListsByDomainResponse(response), proto.FromError(err) } func (g GRPCHandler) ListTaskListPartitions(ctx context.Context, request *matchingv1.ListTaskListPartitionsRequest) (*matchingv1.ListTaskListPartitionsResponse, error) { response, err := g.h.ListTaskListPartitions(ctx, proto.ToMatchingListTaskListPartitionsRequest(request)) return proto.FromMatchingListTaskListPartitionsResponse(response), proto.FromError(err) } func (g GRPCHandler) PollForActivityTask(ctx context.Context, request *matchingv1.PollForActivityTaskRequest) (*matchingv1.PollForActivityTaskResponse, error) { response, err := g.h.PollForActivityTask(ctx, proto.ToMatchingPollForActivityTaskRequest(request)) return proto.FromMatchingPollForActivityTaskResponse(response), proto.FromError(err) } func (g GRPCHandler) PollForDecisionTask(ctx context.Context, request *matchingv1.PollForDecisionTaskRequest) (*matchingv1.PollForDecisionTaskResponse, error) { response, err := g.h.PollForDecisionTask(ctx, proto.ToMatchingPollForDecisionTaskRequest(request)) return proto.FromMatchingPollForDecisionTaskResponse(response), proto.FromError(err) } func (g GRPCHandler) QueryWorkflow(ctx context.Context, request *matchingv1.QueryWorkflowRequest) (*matchingv1.QueryWorkflowResponse, error) { response, err := g.h.QueryWorkflow(ctx, proto.ToMatchingQueryWorkflowRequest(request)) return proto.FromMatchingQueryWorkflowResponse(response), proto.FromError(err) } func (g GRPCHandler) RefreshTaskListPartitionConfig(ctx context.Context, request *matchingv1.RefreshTaskListPartitionConfigRequest) (*matchingv1.RefreshTaskListPartitionConfigResponse, error) { response, err := g.h.RefreshTaskListPartitionConfig(ctx, proto.ToMatchingRefreshTaskListPartitionConfigRequest(request)) return proto.FromMatchingRefreshTaskListPartitionConfigResponse(response), proto.FromError(err) } func (g GRPCHandler) RespondQueryTaskCompleted(ctx context.Context, request *matchingv1.RespondQueryTaskCompletedRequest) (*matchingv1.RespondQueryTaskCompletedResponse, error) { err := g.h.RespondQueryTaskCompleted(ctx, proto.ToMatchingRespondQueryTaskCompletedRequest(request)) return &matchingv1.RespondQueryTaskCompletedResponse{}, proto.FromError(err) } func (g GRPCHandler) UpdateTaskListPartitionConfig(ctx context.Context, request *matchingv1.UpdateTaskListPartitionConfigRequest) (*matchingv1.UpdateTaskListPartitionConfigResponse, error) { response, err := g.h.UpdateTaskListPartitionConfig(ctx, proto.ToMatchingUpdateTaskListPartitionConfigRequest(request)) return proto.FromMatchingUpdateTaskListPartitionConfigResponse(response), proto.FromError(err) } ================================================ FILE: service/matching/wrappers/thrift/thrift_handler.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package thrift import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/.gen/go/health" "github.com/uber/cadence/.gen/go/health/metaserver" "github.com/uber/cadence/.gen/go/matching/matchingserviceserver" "github.com/uber/cadence/common/types/mapper/thrift" "github.com/uber/cadence/service/matching/handler" ) type ThriftHandler struct { h handler.Handler } func NewThriftHandler(h handler.Handler) ThriftHandler { return ThriftHandler{h} } func (t ThriftHandler) Register(dispatcher *yarpc.Dispatcher) { dispatcher.Register(matchingserviceserver.New(&t)) dispatcher.Register(metaserver.New(&t)) } func (t ThriftHandler) Health(ctx context.Context) (*health.HealthStatus, error) { response, err := t.h.Health(ctx) return thrift.FromHealthStatus(response), thrift.FromError(err) } ================================================ FILE: service/matching/wrappers/thrift/thrift_handler_generated.go ================================================ package thrift // Code generated by gowrap. DO NOT EDIT. // template: ../../../templates/thrift.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/.gen/go/matching" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/types/mapper/thrift" ) func (g ThriftHandler) AddActivityTask(ctx context.Context, AddRequest *matching.AddActivityTaskRequest) (err error) { _, err = g.h.AddActivityTask(ctx, thrift.ToMatchingAddActivityTaskRequest(AddRequest)) return thrift.FromError(err) } func (g ThriftHandler) AddDecisionTask(ctx context.Context, AddRequest *matching.AddDecisionTaskRequest) (err error) { _, err = g.h.AddDecisionTask(ctx, thrift.ToMatchingAddDecisionTaskRequest(AddRequest)) return thrift.FromError(err) } func (g ThriftHandler) CancelOutstandingPoll(ctx context.Context, Request *matching.CancelOutstandingPollRequest) (err error) { err = g.h.CancelOutstandingPoll(ctx, thrift.ToMatchingCancelOutstandingPollRequest(Request)) return thrift.FromError(err) } func (g ThriftHandler) DescribeTaskList(ctx context.Context, Request *matching.DescribeTaskListRequest) (dp1 *shared.DescribeTaskListResponse, err error) { response, err := g.h.DescribeTaskList(ctx, thrift.ToMatchingDescribeTaskListRequest(Request)) return thrift.FromMatchingDescribeTaskListResponse(response), thrift.FromError(err) } func (g ThriftHandler) GetTaskListsByDomain(ctx context.Context, Request *shared.GetTaskListsByDomainRequest) (gp1 *shared.GetTaskListsByDomainResponse, err error) { response, err := g.h.GetTaskListsByDomain(ctx, thrift.ToMatchingGetTaskListsByDomainRequest(Request)) return thrift.FromMatchingGetTaskListsByDomainResponse(response), thrift.FromError(err) } func (g ThriftHandler) ListTaskListPartitions(ctx context.Context, Request *matching.ListTaskListPartitionsRequest) (lp1 *shared.ListTaskListPartitionsResponse, err error) { response, err := g.h.ListTaskListPartitions(ctx, thrift.ToMatchingListTaskListPartitionsRequest(Request)) return thrift.FromMatchingListTaskListPartitionsResponse(response), thrift.FromError(err) } func (g ThriftHandler) PollForActivityTask(ctx context.Context, PollRequest *matching.PollForActivityTaskRequest) (pp1 *shared.PollForActivityTaskResponse, err error) { response, err := g.h.PollForActivityTask(ctx, thrift.ToMatchingPollForActivityTaskRequest(PollRequest)) return thrift.FromMatchingPollForActivityTaskResponse(response), thrift.FromError(err) } func (g ThriftHandler) PollForDecisionTask(ctx context.Context, PollRequest *matching.PollForDecisionTaskRequest) (pp1 *matching.PollForDecisionTaskResponse, err error) { response, err := g.h.PollForDecisionTask(ctx, thrift.ToMatchingPollForDecisionTaskRequest(PollRequest)) return thrift.FromMatchingPollForDecisionTaskResponse(response), thrift.FromError(err) } func (g ThriftHandler) QueryWorkflow(ctx context.Context, QueryRequest *matching.QueryWorkflowRequest) (qp1 *shared.QueryWorkflowResponse, err error) { response, err := g.h.QueryWorkflow(ctx, thrift.ToMatchingQueryWorkflowRequest(QueryRequest)) return thrift.FromMatchingQueryWorkflowResponse(response), thrift.FromError(err) } func (g ThriftHandler) RespondQueryTaskCompleted(ctx context.Context, Request *matching.RespondQueryTaskCompletedRequest) (err error) { err = g.h.RespondQueryTaskCompleted(ctx, thrift.ToMatchingRespondQueryTaskCompletedRequest(Request)) return thrift.FromError(err) } ================================================ FILE: service/matching/wrappers/thrift/thrift_handler_test.go ================================================ // Copyright (c) 2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package thrift import ( "context" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/.gen/go/health" m "github.com/uber/cadence/.gen/go/matching" s "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/matching/handler" ) func TestThriftHandler(t *testing.T) { ctrl := gomock.NewController(t) h := handler.NewMockHandler(ctrl) th := NewThriftHandler(h) ctx := context.Background() internalErr := &types.InternalServiceError{Message: "test"} expectedErr := &s.InternalServiceError{Message: "test"} t.Run("Health", func(t *testing.T) { h.EXPECT().Health(ctx).Return(&types.HealthStatus{}, internalErr).Times(1) resp, err := th.Health(ctx) assert.Equal(t, health.HealthStatus{Msg: common.StringPtr("")}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("AddActivityTask", func(t *testing.T) { h.EXPECT().AddActivityTask(ctx, &types.AddActivityTaskRequest{}).Return(nil, internalErr).Times(1) err := th.AddActivityTask(ctx, &m.AddActivityTaskRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("AddDecisionTask", func(t *testing.T) { h.EXPECT().AddDecisionTask(ctx, &types.AddDecisionTaskRequest{}).Return(nil, internalErr).Times(1) err := th.AddDecisionTask(ctx, &m.AddDecisionTaskRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("CancelOutstandingPoll", func(t *testing.T) { h.EXPECT().CancelOutstandingPoll(ctx, &types.CancelOutstandingPollRequest{}).Return(internalErr).Times(1) err := th.CancelOutstandingPoll(ctx, &m.CancelOutstandingPollRequest{}) assert.Equal(t, expectedErr, err) }) t.Run("DescribeTaskList", func(t *testing.T) { h.EXPECT().DescribeTaskList(ctx, &types.MatchingDescribeTaskListRequest{}).Return(&types.DescribeTaskListResponse{}, internalErr).Times(1) resp, err := th.DescribeTaskList(ctx, &m.DescribeTaskListRequest{}) assert.Equal(t, s.DescribeTaskListResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("ListTaskListPartitions", func(t *testing.T) { h.EXPECT().ListTaskListPartitions(ctx, &types.MatchingListTaskListPartitionsRequest{}).Return(&types.ListTaskListPartitionsResponse{}, internalErr).Times(1) resp, err := th.ListTaskListPartitions(ctx, &m.ListTaskListPartitionsRequest{}) assert.Equal(t, s.ListTaskListPartitionsResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("PollForActivityTask", func(t *testing.T) { h.EXPECT().PollForActivityTask(ctx, &types.MatchingPollForActivityTaskRequest{}).Return(&types.MatchingPollForActivityTaskResponse{}, internalErr).Times(1) resp, err := th.PollForActivityTask(ctx, &m.PollForActivityTaskRequest{}) assert.Equal(t, s.PollForActivityTaskResponse{WorkflowDomain: common.StringPtr(""), ActivityId: common.StringPtr(""), Attempt: common.Int32Ptr(0)}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("PollForDecisionTask", func(t *testing.T) { h.EXPECT().PollForDecisionTask(ctx, &types.MatchingPollForDecisionTaskRequest{}).Return(&types.MatchingPollForDecisionTaskResponse{}, internalErr).Times(1) resp, err := th.PollForDecisionTask(ctx, &m.PollForDecisionTaskRequest{}) assert.Equal(t, m.PollForDecisionTaskResponse{ StartedEventId: common.Int64Ptr(0), NextEventId: common.Int64Ptr(0), Attempt: common.Int64Ptr(0), StickyExecutionEnabled: common.BoolPtr(false), BacklogCountHint: common.Int64Ptr(0), EventStoreVersion: common.Int32Ptr(0), TotalHistoryBytes: common.Int64Ptr(0), }, *resp) assert.Equal(t, expectedErr, err) }) t.Run("QueryWorkflow", func(t *testing.T) { h.EXPECT().QueryWorkflow(ctx, &types.MatchingQueryWorkflowRequest{}).Return(&types.MatchingQueryWorkflowResponse{}, internalErr).Times(1) resp, err := th.QueryWorkflow(ctx, &m.QueryWorkflowRequest{}) assert.Equal(t, s.QueryWorkflowResponse{}, *resp) assert.Equal(t, expectedErr, err) }) t.Run("RespondQueryTaskCompleted", func(t *testing.T) { h.EXPECT().RespondQueryTaskCompleted(ctx, &types.MatchingRespondQueryTaskCompletedRequest{}).Return(internalErr).Times(1) err := th.RespondQueryTaskCompleted(ctx, &m.RespondQueryTaskCompletedRequest{}) assert.Equal(t, expectedErr, err) }) } ================================================ FILE: service/sharddistributor/canary/config/config.go ================================================ package config // Config is the configuration for the shard distributor canary type Config struct { Canary CanaryConfig `yaml:"canary"` } type CanaryConfig struct { // NumFixedExecutors is the number of executors of fixed namespace // Values more than 1 will create multiple executors processing the same fixed namespace // Default: 1 NumFixedExecutors int `yaml:"numFixedExecutors"` // NumEphemeralExecutors is the number of executors of ephemeral namespace // Values more than 1 will create multiple executors processing the same ephemeral namespace // Default: 1 NumEphemeralExecutors int `yaml:"numEphemeralExecutors"` } ================================================ FILE: service/sharddistributor/canary/executors/executors.go ================================================ package executors import ( "go.uber.org/fx" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/service/sharddistributor/canary/config" "github.com/uber/cadence/service/sharddistributor/canary/externalshardassignment" "github.com/uber/cadence/service/sharddistributor/canary/processor" "github.com/uber/cadence/service/sharddistributor/canary/processorephemeral" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) const ( // LocalPassthroughNamespace namespace used to test the local passthrough LocalPassthroughNamespace = "test-local-passthrough" // LocalPassthroughShadowNamespace namespace used to test the local passthrough shadow LocalPassthroughShadowNamespace = "test-local-passthrough-shadow" // DistributedPassthroughNamespace namespace used to test the distributed passthrough DistributedPassthroughNamespace = "test-distributed-passthrough" // ExternalAssignmentNamespace namespace used to test the external assignments ExternalAssignmentNamespace = "test-external-assignment" ) type ExecutorResult struct { fx.Out Executor executorclient.Executor[*processor.ShardProcessor] `group:"executor-fixed-proc"` } type ExecutorEphemeralResult struct { fx.Out Executor executorclient.Executor[*processorephemeral.ShardProcessor] `group:"executor-ephemeral-proc"` } type ExecutorsResult struct { fx.Out Executors []executorclient.Executor[*processor.ShardProcessor] `group:"executor-fixed-proc,flatten"` } type ExecutorsEphemeralResult struct { fx.Out Executors []executorclient.Executor[*processorephemeral.ShardProcessor] `group:"executor-ephemeral-proc,flatten"` } func NewExecutorsWithFixedNamespace(params executorclient.Params[*processor.ShardProcessor], namespace string, numExecutors int) (ExecutorsResult, error) { var result ExecutorsResult if numExecutors <= 0 { numExecutors = 1 } for i := 0; i < numExecutors; i++ { executor, err := executorclient.NewExecutorWithNamespace(params, namespace) if err != nil { return ExecutorsResult{}, err } result.Executors = append(result.Executors, executor) } return result, nil } func NewExecutorWithFixedNamespace(params executorclient.Params[*processor.ShardProcessor], namespace string) (ExecutorResult, error) { executor, err := executorclient.NewExecutorWithNamespace(params, namespace) return ExecutorResult{Executor: executor}, err } func NewExecutorsWithEphemeralNamespace(params executorclient.Params[*processorephemeral.ShardProcessor], namespace string, numExecutors int) (ExecutorsEphemeralResult, error) { var result ExecutorsEphemeralResult if numExecutors <= 0 { numExecutors = 1 } for i := 0; i < numExecutors; i++ { executor, err := executorclient.NewExecutorWithNamespace(params, namespace) if err != nil { return ExecutorsEphemeralResult{}, err } result.Executors = append(result.Executors, executor) } return result, nil } func NewExecutorWithEphemeralNamespace(params executorclient.Params[*processorephemeral.ShardProcessor], namespace string) (ExecutorEphemeralResult, error) { executor, err := executorclient.NewExecutorWithNamespace(params, namespace) return ExecutorEphemeralResult{Executor: executor}, err } func NewExecutorLocalPassthroughNamespace(params executorclient.Params[*processor.ShardProcessor]) (ExecutorResult, error) { executor, err := executorclient.NewExecutorWithNamespace(params, LocalPassthroughNamespace) return ExecutorResult{Executor: executor}, err } func NewExecutorLocalPassthroughShadowNamespace(params executorclient.Params[*processorephemeral.ShardProcessor]) (ExecutorEphemeralResult, error) { executor, err := executorclient.NewExecutorWithNamespace(params, LocalPassthroughShadowNamespace) return ExecutorEphemeralResult{Executor: executor}, err } func NewExecutorDistributedPassthroughNamespace(params executorclient.Params[*processor.ShardProcessor]) (ExecutorResult, error) { executor, err := executorclient.NewExecutorWithNamespace(params, DistributedPassthroughNamespace) return ExecutorResult{Executor: executor}, err } func NewExecutorExternalAssignmentNamespace(params executorclient.Params[*processorephemeral.ShardProcessor], shardDistributorClient sharddistributor.Client, namespace string) (ExecutorEphemeralResult, *externalshardassignment.ShardAssigner, error) { executor, err := executorclient.NewExecutorWithNamespace(params, namespace) assigner := externalshardassignment.NewShardAssigner(externalshardassignment.ShardAssignerParams{ Logger: params.Logger, TimeSource: params.TimeSource, ShardDistributor: shardDistributorClient, Executorclient: executor, }, namespace) return ExecutorEphemeralResult{Executor: executor}, assigner, err } type ExecutorsParams struct { fx.In Lc fx.Lifecycle ExecutorsFixed []executorclient.Executor[*processor.ShardProcessor] `group:"executor-fixed-proc"` Executorsephemeral []executorclient.Executor[*processorephemeral.ShardProcessor] `group:"executor-ephemeral-proc"` } func NewExecutorsModule(params ExecutorsParams) { for _, e := range params.ExecutorsFixed { params.Lc.Append(fx.StartStopHook(e.Start, e.Stop)) } for _, e := range params.Executorsephemeral { params.Lc.Append(fx.StartStopHook(e.Start, e.Stop)) } } func Module(fixedNamespace, ephemeralNamespace, externalAssignmentNamespace string) fx.Option { return fx.Module("Executors", // Executor that is used for testing a namespace with fixed shards fx.Provide(func(cfg config.Config, params executorclient.Params[*processor.ShardProcessor]) (ExecutorsResult, error) { return NewExecutorsWithFixedNamespace(params, fixedNamespace, cfg.Canary.NumFixedExecutors) }), // Executor that is used for testing a namespaces with ephemeral shards fx.Provide(func(cfg config.Config, params executorclient.Params[*processorephemeral.ShardProcessor]) (ExecutorsEphemeralResult, error) { return NewExecutorsWithEphemeralNamespace(params, ephemeralNamespace, cfg.Canary.NumEphemeralExecutors) }), // Executor used for testing a namespace where the shards are assigned externally and reflected in the state of the SD // this is reproducing the behaviour that matching service is going to have during the DistributedPassthrough mode fx.Module("Executor-with-external-assignment", fx.Provide(func(params executorclient.Params[*processorephemeral.ShardProcessor], shardDistributorClient sharddistributor.Client) (ExecutorEphemeralResult, *externalshardassignment.ShardAssigner, error) { return NewExecutorExternalAssignmentNamespace(params, shardDistributorClient, externalAssignmentNamespace) }), fx.Invoke(func(lifecycle fx.Lifecycle, shardAssigner *externalshardassignment.ShardAssigner) { lifecycle.Append(fx.StartStopHook(shardAssigner.Start, shardAssigner.Stop)) }), ), fx.Invoke(NewExecutorsModule), ) } ================================================ FILE: service/sharddistributor/canary/executors/executors_test.go ================================================ package executors import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/fx" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/service/sharddistributor/canary/processor" "github.com/uber/cadence/service/sharddistributor/canary/processorephemeral" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) // mockLifecycle is a simple mock implementation of fx.Lifecycle for testing type mockLifecycle struct { hookCount int } func (m *mockLifecycle) Append(hook fx.Hook) { m.hookCount++ } func TestNewExecutorsFixedNamespace(t *testing.T) { ctrl := gomock.NewController(t) tests := []struct { name string params executorclient.Params[*processor.ShardProcessor] newExecutor func(params executorclient.Params[*processor.ShardProcessor]) (ExecutorResult, error) }{ { name: "TestNewExecutorWithFixedNamespace", params: createMockParams[*processor.ShardProcessor](ctrl, "shard-distributor-canary"), newExecutor: func(params executorclient.Params[*processor.ShardProcessor]) (ExecutorResult, error) { return NewExecutorWithFixedNamespace(params, "shard-distributor-canary") }}, { name: "TestNewExecutorLocalPassthroughNamespace", params: createMockParams[*processor.ShardProcessor](ctrl, LocalPassthroughNamespace), newExecutor: NewExecutorLocalPassthroughNamespace, }, { name: "TestNewExecutorDistributedPassthroughNamespace", params: createMockParams[*processor.ShardProcessor](ctrl, DistributedPassthroughNamespace), newExecutor: NewExecutorDistributedPassthroughNamespace, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := tt.newExecutor(tt.params) require.NoError(t, err) require.NotNil(t, result.Executor) }) } } func TestNewExecutorsEphemeralNamespace(t *testing.T) { ctrl := gomock.NewController(t) tests := []struct { name string params executorclient.Params[*processorephemeral.ShardProcessor] newExecutor func(params executorclient.Params[*processorephemeral.ShardProcessor]) (ExecutorEphemeralResult, error) }{ { name: "TestNewExecutorWithEphemeralNamespace", params: createMockParams[*processorephemeral.ShardProcessor](ctrl, "shard-distributor-canary-ephemeral"), newExecutor: func(params executorclient.Params[*processorephemeral.ShardProcessor]) (ExecutorEphemeralResult, error) { return NewExecutorWithEphemeralNamespace(params, "shard-distributor-canary-ephemeral") }}, { name: "TestNewExecutorLocalPassthroughShadowNamespace", params: createMockParams[*processorephemeral.ShardProcessor](ctrl, LocalPassthroughShadowNamespace), newExecutor: NewExecutorLocalPassthroughShadowNamespace, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := tt.newExecutor(tt.params) require.NoError(t, err) require.NotNil(t, result.Executor) }) } } func TestNewExecutorExternalAssignmentNamespace(t *testing.T) { ctrl := gomock.NewController(t) mockShardDistributorClient := sharddistributor.NewMockClient(ctrl) params := createMockParams[*processorephemeral.ShardProcessor](ctrl, "test-external-assignment") result, assigner, err := NewExecutorExternalAssignmentNamespace(params, mockShardDistributorClient, "test-external-assignment") require.NoError(t, err) require.NotNil(t, result.Executor) require.NotNil(t, assigner) // Verify that the assigner is properly configured and can start/stop assigner.Start() defer assigner.Stop() } func TestNewExecutor_InvalidConfig(t *testing.T) { ctrl := gomock.NewController(t) mockShardProcessorFactory := executorclient.NewMockShardProcessorFactory[*processor.ShardProcessor](ctrl) tests := []struct { name string params executorclient.Params[*processor.ShardProcessor] newExecutor func(params executorclient.Params[*processor.ShardProcessor]) (ExecutorResult, error) errorString string }{ { name: "No namespaces configured", params: executorclient.Params[*processor.ShardProcessor]{ MetricsScope: tally.NoopScope, Logger: log.NewNoop(), ShardProcessorFactory: mockShardProcessorFactory, Config: clientcommon.Config{ Namespaces: []clientcommon.NamespaceConfig{}, }, TimeSource: clock.NewMockedTimeSource(), }, errorString: "at least one namespace must be configured", }, { name: "No valid namespace", params: executorclient.Params[*processor.ShardProcessor]{ MetricsScope: tally.NoopScope, Logger: log.NewNoop(), ShardProcessorFactory: mockShardProcessorFactory, Config: clientcommon.Config{ Namespaces: []clientcommon.NamespaceConfig{ { Namespace: "wrong-namespace", HeartBeatInterval: 5 * time.Second, MigrationMode: "onboarded", }, }, }, TimeSource: clock.NewMockedTimeSource(), }, errorString: "namespace shard-distributor-canary not found in config", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := NewExecutorWithFixedNamespace(tt.params, "shard-distributor-canary") require.Error(t, err) assert.Contains(t, err.Error(), tt.errorString) }) } } func TestNewExecutorsModule(t *testing.T) { ctrl := gomock.NewController(t) // Create a mock lifecycle tests := []struct { name string params ExecutorsParams expectedInvocation int }{ { name: "multiple executors", params: ExecutorsParams{ ExecutorsFixed: []executorclient.Executor[*processor.ShardProcessor]{ executorclient.NewMockExecutor[*processor.ShardProcessor](ctrl), executorclient.NewMockExecutor[*processor.ShardProcessor](ctrl), }, Executorsephemeral: []executorclient.Executor[*processorephemeral.ShardProcessor]{ executorclient.NewMockExecutor[*processorephemeral.ShardProcessor](ctrl), }, }, expectedInvocation: 3, }, { name: "no executors", params: ExecutorsParams{ ExecutorsFixed: []executorclient.Executor[*processor.ShardProcessor]{}, Executorsephemeral: []executorclient.Executor[*processorephemeral.ShardProcessor]{}, }, expectedInvocation: 0, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockLifecycle := &mockLifecycle{} tt.params.Lc = mockLifecycle // Call NewExecutorsModule - it should not panic or error // The function doesn't return anything, so we just verify it executes successfully require.NotPanics(t, func() { NewExecutorsModule(tt.params) }) // Verify that lifecycle hooks were registered for all executors assert.Equal(t, tt.expectedInvocation, mockLifecycle.hookCount) }) } } // Helper functions to create mock parameters func createMockParams[SP executorclient.ShardProcessor]( ctrl *gomock.Controller, namespace string, ) executorclient.Params[SP] { mockShardProcessorFactory := executorclient.NewMockShardProcessorFactory[SP](ctrl) return executorclient.Params[SP]{ MetricsScope: tally.NoopScope, Logger: log.NewNoop(), ShardProcessorFactory: mockShardProcessorFactory, Config: clientcommon.Config{ Namespaces: []clientcommon.NamespaceConfig{ { Namespace: namespace, HeartBeatInterval: 5 * time.Second, MigrationMode: "onboarded", }, }, }, TimeSource: clock.NewMockedTimeSource(), } } func TestNewExecutorsWithFixedNamespace(t *testing.T) { ctrl := gomock.NewController(t) tests := []struct { name string namespace string numExecutors int expected int }{ { name: "zero executors defaults to one", namespace: "test-namespace", numExecutors: 0, expected: 1, }, { name: "negative executors defaults to one", namespace: "test-namespace", numExecutors: -1, expected: 1, }, { name: "one executor", namespace: "test-namespace", numExecutors: 1, expected: 1, }, { name: "two executors", namespace: "test-namespace", numExecutors: 2, expected: 2, }, { name: "five executors", namespace: "test-namespace", numExecutors: 5, expected: 5, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { params := createMockParams[*processor.ShardProcessor](ctrl, tt.namespace) result, err := NewExecutorsWithFixedNamespace(params, tt.namespace, tt.numExecutors) require.NoError(t, err) assert.Len(t, result.Executors, tt.expected) for _, executor := range result.Executors { assert.NotNil(t, executor) } }) } } func TestNewExecutorsWithEphemeralNamespace(t *testing.T) { ctrl := gomock.NewController(t) tests := []struct { name string namespace string numExecutors int expected int }{ { name: "zero executors defaults to one", namespace: "test-namespace", numExecutors: 0, expected: 1, }, { name: "negative executors defaults to one", namespace: "test-namespace", numExecutors: -1, expected: 1, }, { name: "one executor", namespace: "test-namespace", numExecutors: 1, expected: 1, }, { name: "two executors", namespace: "test-namespace", numExecutors: 2, expected: 2, }, { name: "five executors", namespace: "test-namespace", numExecutors: 5, expected: 5, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { params := createMockParams[*processorephemeral.ShardProcessor](ctrl, tt.namespace) result, err := NewExecutorsWithEphemeralNamespace(params, tt.namespace, tt.numExecutors) require.NoError(t, err) assert.Len(t, result.Executors, tt.expected) for _, executor := range result.Executors { assert.NotNil(t, executor) } }) } } ================================================ FILE: service/sharddistributor/canary/externalshardassignment/shardassigner.go ================================================ package externalshardassignment import ( "context" "math/rand" "strconv" "sync" "time" "github.com/google/uuid" "go.uber.org/fx" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/canary/processorephemeral" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) const ( shardAssignmentInterval = 4 * time.Second minimumAssignedShards = 3 ) // ShardAssigneer assigns shards to the executor for canary testing type ShardAssigner struct { logger log.Logger timeSource clock.TimeSource shardProcessors map[string]*processorephemeral.ShardProcessor executorclient executorclient.Executor[*processorephemeral.ShardProcessor] stopChan chan struct{} goRoutineWg sync.WaitGroup namespace string } // ShardAssignerParams contains the dependencies needed to create a ShardParam type ShardAssignerParams struct { Logger log.Logger TimeSource clock.TimeSource ShardDistributor sharddistributor.Client Executorclient executorclient.Executor[*processorephemeral.ShardProcessor] } // NewShardCreator creates a new ShardCreator instance with the given parameters and namespace func NewShardAssigner(params ShardAssignerParams, namespace string) *ShardAssigner { sp := make(map[string]*processorephemeral.ShardProcessor) return &ShardAssigner{ logger: params.Logger, timeSource: params.TimeSource, shardProcessors: sp, executorclient: params.Executorclient, stopChan: make(chan struct{}), goRoutineWg: sync.WaitGroup{}, namespace: namespace, } } // Start begins the shard creation process in a background goroutine func (s *ShardAssigner) Start() { s.goRoutineWg.Add(1) go s.process(context.Background()) s.logger.Info("Shard assigner started") } // Stop stops the shard creation process and waits for the goroutine to finish func (s *ShardAssigner) Stop() { close(s.stopChan) s.goRoutineWg.Wait() } // ShardCreatorModule creates an fx module for the shard creator with the given namespace func ShardAssignerModule(namespace string) fx.Option { return fx.Module("shard-assigner", fx.Provide(func(params ShardAssignerParams) *ShardAssigner { return NewShardAssigner(params, namespace) }), fx.Invoke(func(lifecycle fx.Lifecycle, shardAssigner *ShardAssigner) { lifecycle.Append(fx.StartStopHook(shardAssigner.Start, shardAssigner.Stop)) }), ) } func (s *ShardAssigner) process(ctx context.Context) { defer s.goRoutineWg.Done() ticker := s.timeSource.NewTicker(shardAssignmentInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-s.stopChan: return case <-ticker.Chan(): if len(s.shardProcessors) > minimumAssignedShards { var shardToRemove string shardToRemoveIndex := rand.Intn(len(s.shardProcessors)) for shardID := range s.shardProcessors { if shardToRemoveIndex == 0 { shardToRemove = shardID break } shardToRemoveIndex-- } err := s.executorclient.RemoveShardsFromLocalLogic([]string{shardToRemove}) if err != nil { s.logger.Error("Failed to remove shards", tag.Error(err)) continue } delete(s.shardProcessors, shardToRemove) s.logger.Info("Removed a shard from external source", tag.ShardKey(shardToRemove)) } // Simulate the assignment of new shards newAssignedShard := uuid.New().String() s.logger.Info("Assign a new shard from external source", tag.ShardKey(newAssignedShard)) shardAssignment := map[string]*types.ShardAssignment{ newAssignedShard: { Status: types.AssignmentStatusREADY, }, } err := s.executorclient.AssignShardsFromLocalLogic(context.Background(), shardAssignment) if err != nil { s.logger.Error("Failed to assign shard from external source", tag.Error(err)) continue } sp, err := s.executorclient.GetShardProcess(ctx, newAssignedShard) if err != nil { s.logger.Error("failed to get shard assigned", tag.ShardKey(newAssignedShard), tag.Error(err)) } else { s.logger.Info("shard assigned", tag.ShardStatus(string(sp.GetShardReport().Status)), tag.ShardLoad(strconv.FormatFloat(sp.GetShardReport().ShardLoad, 'f', -1, 64))) } s.shardProcessors[newAssignedShard] = sp } } } ================================================ FILE: service/sharddistributor/canary/externalshardassignment/shardassigner_test.go ================================================ package externalshardassignment import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/service/sharddistributor/canary/processorephemeral" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) func TestSShardAssigner_Lifecycle(t *testing.T) { goleak.VerifyNone(t) logger := log.NewNoop() timeSource := clock.NewMockedTimeSource() ctrl := gomock.NewController(t) executorclientmock := executorclient.NewMockExecutor[*processorephemeral.ShardProcessor](ctrl) namespace := "test-namespace" executorclientmock.EXPECT().AssignShardsFromLocalLogic(gomock.Any(), gomock.Any()) executorclientmock.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) executorclientmock.EXPECT().AssignShardsFromLocalLogic(gomock.Any(), gomock.Any()) executorclientmock.EXPECT().GetShardProcess(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) params := ShardAssignerParams{ Logger: logger, TimeSource: timeSource, Executorclient: executorclientmock, } assigner := NewShardAssigner(params, namespace) assigner.Start() // Wait for the goroutine to start timeSource.BlockUntil(1) // Trigger shard creation that will fail timeSource.Advance(shardAssignmentInterval + 100*time.Millisecond) time.Sleep(10 * time.Millisecond) // Allow processing // Trigger another shard creation to ensure processing continues after error timeSource.Advance(shardAssignmentInterval + 100*time.Millisecond) time.Sleep(10 * time.Millisecond) // Allow processing assigner.Stop() } ================================================ FILE: service/sharddistributor/canary/factory/factory.go ================================================ package factory import ( "go.uber.org/fx" "go.uber.org/zap" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) // ShardProcessorFactory is a generic factory for creating ShardProcessor instances. type ShardProcessorFactory[T executorclient.ShardProcessor] struct { logger *zap.Logger timeSource clock.TimeSource constructor func(shardID string, timeSource clock.TimeSource, logger *zap.Logger) T } // NewShardProcessor creates a new ShardProcessor. func (s *ShardProcessorFactory[T]) NewShardProcessor(shardID string) (T, error) { return s.constructor(shardID, s.timeSource, s.logger), nil } // Params are the parameters for creating a ShardProcessorFactory. type Params struct { fx.In Logger *zap.Logger TimeSource clock.TimeSource } // NewShardProcessorFactory creates a new ShardProcessorFactory with a constructor function. func NewShardProcessorFactory[T executorclient.ShardProcessor]( params Params, constructor func(shardID string, timeSource clock.TimeSource, logger *zap.Logger) T, ) executorclient.ShardProcessorFactory[T] { return &ShardProcessorFactory[T]{ logger: params.Logger, timeSource: params.TimeSource, constructor: constructor, } } ================================================ FILE: service/sharddistributor/canary/factory/factory_test.go ================================================ package factory import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/service/sharddistributor/canary/processor" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) func TestNewShardProcessorFactory(t *testing.T) { logger := zaptest.NewLogger(t) timeSource := clock.NewRealTimeSource() params := Params{ Logger: logger, TimeSource: timeSource, } factory := NewShardProcessorFactory(params, processor.NewShardProcessor) // Test that the factory implements the correct interface var _ executorclient.ShardProcessorFactory[*processor.ShardProcessor] = factory // Test creating a processor processor, err := factory.NewShardProcessor("test-shard-1") require.NoError(t, err) assert.NotNil(t, processor) } func TestShardProcessorFactory_NewShardProcessor(t *testing.T) { logger := zaptest.NewLogger(t) timeSource := clock.NewRealTimeSource() factory := &ShardProcessorFactory[*processor.ShardProcessor]{ logger: logger, timeSource: timeSource, constructor: processor.NewShardProcessor, } // Test creating multiple processors processor1, err := factory.NewShardProcessor("shard-1") require.NoError(t, err) processor2, err := factory.NewShardProcessor("shard-2") require.NoError(t, err) // Ensure they are different instances assert.NotEqual(t, processor1, processor2) } ================================================ FILE: service/sharddistributor/canary/handler/ping_handler.go ================================================ package handler import ( "context" "go.uber.org/fx" "go.uber.org/zap" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/service/sharddistributor/canary/processor" "github.com/uber/cadence/service/sharddistributor/canary/processorephemeral" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) // PingHandler handles ping requests to verify executor ownership of shards type PingHandler struct { logger *zap.Logger executorsFixed map[string]executorclient.Executor[*processor.ShardProcessor] // namespace -> executor executorsEphemeral map[string]executorclient.Executor[*processorephemeral.ShardProcessor] // namespace -> executor } // Params are the parameters for creating a PingHandler type Params struct { fx.In Logger *zap.Logger ExecutorsFixed []executorclient.Executor[*processor.ShardProcessor] `group:"executor-fixed-proc"` ExecutorsEphemeral []executorclient.Executor[*processorephemeral.ShardProcessor] `group:"executor-ephemeral-proc"` } // NewPingHandler creates a new PingHandler func NewPingHandler(params Params) *PingHandler { // Create maps of executors for quick lookup executorsFixed := make(map[string]executorclient.Executor[*processor.ShardProcessor]) for _, executor := range params.ExecutorsFixed { executorsFixed[executor.GetNamespace()] = executor } executorsEphemeral := make(map[string]executorclient.Executor[*processorephemeral.ShardProcessor]) for _, executor := range params.ExecutorsEphemeral { executorsEphemeral[executor.GetNamespace()] = executor } // Return the handler return &PingHandler{ logger: params.Logger, executorsFixed: executorsFixed, executorsEphemeral: executorsEphemeral, } } // Ping handles ping requests to check shard ownership func (h *PingHandler) Ping(ctx context.Context, request *sharddistributorv1.PingRequest) (*sharddistributorv1.PingResponse, error) { h.logger.Info("Received ping request", zap.String("shard_key", request.GetShardKey()), zap.String("namespace", request.GetNamespace())) namespace := request.GetNamespace() // Check fixed executors if executor, found := h.executorsFixed[namespace]; found { return checkOwnerShipAndLog(ctx, executor, request, h), nil } // Check ephemeral executors if executor, found := h.executorsEphemeral[namespace]; found { return checkOwnerShipAndLog(ctx, executor, request, h), nil } // Namespace not found h.logger.Warn("Namespace executor not found", zap.String("namespace", namespace)) return &sharddistributorv1.PingResponse{ ExecutorId: "", OwnsShard: false, ShardKey: request.GetShardKey(), }, nil } func checkOwnerShipAndLog[T executorclient.ShardProcessor](ctx context.Context, executor executorclient.Executor[T], request *sharddistributorv1.PingRequest, h *PingHandler) *sharddistributorv1.PingResponse { // We just check that we have a processor for the shard _, err := executor.GetShardProcess(ctx, request.GetShardKey()) ownshard := err == nil metadata := executor.GetMetadata() executorID := getExecutorID(metadata) response := &sharddistributorv1.PingResponse{ ExecutorId: executorID, OwnsShard: ownshard, ShardKey: request.GetShardKey(), } h.logger.Info("Responding to ping", zap.String("shard_key", request.GetShardKey()), zap.Bool("owns_shard", ownshard), zap.String("executor_id", executorID)) return response } func getExecutorID(metadata map[string]string) string { if addr, ok := metadata["grpc_address"]; ok && addr != "" { return addr } return "" } ================================================ FILE: service/sharddistributor/canary/handler/ping_handler_test.go ================================================ package handler import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "go.uber.org/zap" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/service/sharddistributor/canary/processor" "github.com/uber/cadence/service/sharddistributor/canary/processorephemeral" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) func TestPingHandler_Ping(t *testing.T) { tests := []struct { name string namespace string shardKey string setup func(*gomock.Controller) ([]executorclient.Executor[*processor.ShardProcessor], []executorclient.Executor[*processorephemeral.ShardProcessor]) wantID string wantOwns bool }{ { name: "fixed executor owns shard", namespace: "ns1", shardKey: "shard-1", setup: func(ctrl *gomock.Controller) ([]executorclient.Executor[*processor.ShardProcessor], []executorclient.Executor[*processorephemeral.ShardProcessor]) { exec := executorclient.NewMockExecutor[*processor.ShardProcessor](ctrl) exec.EXPECT().GetNamespace().Return("ns1").AnyTimes() exec.EXPECT().GetShardProcess(gomock.Any(), "shard-1").Return(&processor.ShardProcessor{}, nil) exec.EXPECT().GetMetadata().Return(map[string]string{"grpc_address": "127.0.0.1:7953"}) return []executorclient.Executor[*processor.ShardProcessor]{exec}, nil }, wantID: "127.0.0.1:7953", wantOwns: true, }, { name: "fixed executor does not own shard", namespace: "ns1", shardKey: "shard-2", setup: func(ctrl *gomock.Controller) ([]executorclient.Executor[*processor.ShardProcessor], []executorclient.Executor[*processorephemeral.ShardProcessor]) { exec := executorclient.NewMockExecutor[*processor.ShardProcessor](ctrl) exec.EXPECT().GetNamespace().Return("ns1").AnyTimes() exec.EXPECT().GetShardProcess(gomock.Any(), "shard-2").Return(nil, errors.New("not found")) exec.EXPECT().GetMetadata().Return(map[string]string{"grpc_address": "127.0.0.1:7954"}) return []executorclient.Executor[*processor.ShardProcessor]{exec}, nil }, wantID: "127.0.0.1:7954", wantOwns: false, }, { name: "ephemeral executor owns shard", namespace: "ns2", shardKey: "shard-3", setup: func(ctrl *gomock.Controller) ([]executorclient.Executor[*processor.ShardProcessor], []executorclient.Executor[*processorephemeral.ShardProcessor]) { exec := executorclient.NewMockExecutor[*processorephemeral.ShardProcessor](ctrl) exec.EXPECT().GetNamespace().Return("ns2").AnyTimes() exec.EXPECT().GetShardProcess(gomock.Any(), "shard-3").Return(&processorephemeral.ShardProcessor{}, nil) exec.EXPECT().GetMetadata().Return(map[string]string{"grpc_address": "127.0.0.1:7955"}) return nil, []executorclient.Executor[*processorephemeral.ShardProcessor]{exec} }, wantID: "127.0.0.1:7955", wantOwns: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() fixed, ephemeral := tt.setup(ctrl) handler := NewPingHandler(Params{ Logger: zap.NewNop(), ExecutorsFixed: fixed, ExecutorsEphemeral: ephemeral, }) resp, err := handler.Ping(context.Background(), &sharddistributorv1.PingRequest{ Namespace: tt.namespace, ShardKey: tt.shardKey, }) require.NoError(t, err) assert.Equal(t, tt.wantID, resp.ExecutorId) assert.Equal(t, tt.wantOwns, resp.OwnsShard) assert.Equal(t, tt.shardKey, resp.ShardKey) }) } } ================================================ FILE: service/sharddistributor/canary/module.go ================================================ package canary import ( "go.uber.org/fx" "go.uber.org/yarpc" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/service/sharddistributor/canary/config" "github.com/uber/cadence/service/sharddistributor/canary/executors" "github.com/uber/cadence/service/sharddistributor/canary/factory" "github.com/uber/cadence/service/sharddistributor/canary/handler" "github.com/uber/cadence/service/sharddistributor/canary/pinger" "github.com/uber/cadence/service/sharddistributor/canary/processor" "github.com/uber/cadence/service/sharddistributor/canary/processorephemeral" "github.com/uber/cadence/service/sharddistributor/canary/sharddistributorclient" "github.com/uber/cadence/service/sharddistributor/canary/sharddistributorexecutorclient" "github.com/uber/cadence/service/sharddistributor/client/executorclient" "github.com/uber/cadence/service/sharddistributor/client/spectatorclient" ) type NamespacesNames struct { fx.In FixedNamespace string EphemeralNamespace string ExternalAssignmentNamespace string SharddistributorServiceName string Config config.Config } func Module(namespacesNames NamespacesNames) fx.Option { return fx.Module("shard-distributor-canary", opts(namespacesNames)) } func opts(names NamespacesNames) fx.Option { return fx.Options( fx.Supply(names.Config), fx.Provide(sharddistributorv1.NewFxShardDistributorExecutorAPIYARPCClient(names.SharddistributorServiceName)), fx.Provide(sharddistributorv1.NewFxShardDistributorAPIYARPCClient(names.SharddistributorServiceName)), fx.Provide(sharddistributorclient.NewShardDistributorClient), fx.Provide(sharddistributorexecutorclient.NewShardDistributorExecutorClient), // Modules for the shard distributor canary fx.Provide( func(params factory.Params) executorclient.ShardProcessorFactory[*processor.ShardProcessor] { return factory.NewShardProcessorFactory(params, processor.NewShardProcessor) }, func(params factory.Params) executorclient.ShardProcessorFactory[*processorephemeral.ShardProcessor] { return factory.NewShardProcessorFactory(params, processorephemeral.NewShardProcessor) }, ), // Simple way to instantiate executor if only one namespace is used // executorclient.ModuleWithNamespace[*processor.ShardProcessor](names.FixedNamespace), // executorclient.ModuleWithNamespace[*processorephemeral.ShardProcessor](names.EphemeralNamespace), // Instantiate executors for multiple namespaces executors.Module(names.FixedNamespace, names.EphemeralNamespace, names.ExternalAssignmentNamespace), processorephemeral.ShardCreatorModule([]string{names.EphemeralNamespace}), spectatorclient.Module(), fx.Provide(spectatorclient.NewSpectatorPeerChooser), fx.Invoke(func(chooser spectatorclient.SpectatorPeerChooserInterface, lc fx.Lifecycle) { lc.Append(fx.StartStopHook(chooser.Start, chooser.Stop)) }), // Create canary client using the dispatcher's client config fx.Provide(func(dispatcher *yarpc.Dispatcher) sharddistributorv1.ShardDistributorExecutorCanaryAPIYARPCClient { config := dispatcher.ClientConfig("shard-distributor-canary") return sharddistributorv1.NewShardDistributorExecutorCanaryAPIYARPCClient(config) }), fx.Provide(func(params pinger.Params) *pinger.Pinger { return pinger.NewPinger(params, names.FixedNamespace, 32) }), fx.Invoke(func(p *pinger.Pinger, lc fx.Lifecycle) { lc.Append(fx.StartStopHook(p.Start, p.Stop)) }), // Register canary ping handler to receive ping requests from other executors fx.Provide(handler.NewPingHandler), fx.Provide(fx.Annotate( func(h *handler.PingHandler) sharddistributorv1.ShardDistributorExecutorCanaryAPIYARPCServer { return h }, )), fx.Provide(sharddistributorv1.NewFxShardDistributorExecutorCanaryAPIYARPCProcedures()), // There is a circular dependency between the spectator client and the peer chooser, since // the yarpc dispatcher needs the peer chooser and the peer chooser needs the spectators, which needs the yarpc dispatcher. // To break the circular dependency, we set the spectators on the peer chooser here. fx.Invoke(func(chooser spectatorclient.SpectatorPeerChooserInterface, spectators *spectatorclient.Spectators) { chooser.SetSpectators(spectators) }), ) } ================================================ FILE: service/sharddistributor/canary/module_test.go ================================================ package canary import ( "testing" "time" "github.com/golang/mock/gomock" "github.com/uber-go/tally" "go.uber.org/fx" "go.uber.org/fx/fxtest" ubergomock "go.uber.org/mock/gomock" "go.uber.org/yarpc" "go.uber.org/yarpc/api/peer" "go.uber.org/yarpc/api/transport/transporttest" "go.uber.org/yarpc/transport/grpc" "go.uber.org/yarpc/yarpctest" "go.uber.org/zap/zaptest" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) func TestModule(t *testing.T) { // Create mocks ctrl := gomock.NewController(t) uberCtrl := ubergomock.NewController(t) mockLogger := log.NewNoop() mockClientConfig := transporttest.NewMockClientConfig(ctrl) transport := grpc.NewTransport() outbound := transport.NewOutbound(yarpctest.NewFakePeerList()) mockClientConfig.EXPECT().Caller().Return("test-executor").AnyTimes() mockClientConfig.EXPECT().Service().Return("shard-distributor").AnyTimes() mockClientConfig.EXPECT().GetUnaryOutbound().Return(outbound).AnyTimes() mockClientConfigProvider := transporttest.NewMockClientConfigProvider(ctrl) mockClientConfigProvider.EXPECT().ClientConfig("cadence-shard-distributor").Return(mockClientConfig).AnyTimes() // Create executor yarpc client mock mockYARPCClient := executorclient.NewMockShardDistributorExecutorAPIYARPCClient(uberCtrl) mockYARPCClient.EXPECT(). Heartbeat(ubergomock.Any(), ubergomock.Any(), ubergomock.Any()). Return(&sharddistributorv1.HeartbeatResponse{}, nil). AnyTimes() config := clientcommon.Config{ Namespaces: []clientcommon.NamespaceConfig{ {Namespace: "shard-distributor-canary", HeartBeatInterval: 5 * time.Second, MigrationMode: "onboarded"}, {Namespace: "shard-distributor-canary-ephemeral", HeartBeatInterval: 5 * time.Second, MigrationMode: "onboarded"}, {Namespace: "test-local-passthrough", HeartBeatInterval: 1 * time.Second, MigrationMode: "local_pass"}, {Namespace: "test-local-passthrough-shadow", HeartBeatInterval: 1 * time.Second, MigrationMode: "local_pass_shadow"}, {Namespace: "test-distributed-passthrough", HeartBeatInterval: 1 * time.Second, MigrationMode: "distributed_pass"}, {Namespace: "test-external-assignment", HeartBeatInterval: 1 * time.Second, MigrationMode: "distributed_pass"}, }, } // Create a mock dispatcher with the required outbound dispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: "test-canary", Outbounds: yarpc.Outbounds{ "shard-distributor-canary": { Unary: outbound, }, }, }) // Create a test app with the library, check that it starts and stops fxtest.New(t, fx.Supply( fx.Annotate(tally.NoopScope, fx.As(new(tally.Scope))), fx.Annotate(clock.NewMockedTimeSource(), fx.As(new(clock.TimeSource))), fx.Annotate(mockLogger, fx.As(new(log.Logger))), fx.Annotate(mockClientConfigProvider, fx.As(new(yarpc.ClientConfig))), fx.Annotate(transport, fx.As(new(peer.Transport))), zaptest.NewLogger(t), config, dispatcher, ), // Replacing the real YARPC client with mock to handle the draining heartbeat fx.Decorate(func() sharddistributorv1.ShardDistributorExecutorAPIYARPCClient { return mockYARPCClient }), Module(NamespacesNames{ FixedNamespace: "shard-distributor-canary", EphemeralNamespace: "shard-distributor-canary-ephemeral", ExternalAssignmentNamespace: "test-external-assignment", SharddistributorServiceName: "cadence-shard-distributor", }), ).RequireStart().RequireStop() } ================================================ FILE: service/sharddistributor/canary/pinger/canary_client_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/.gen/proto/sharddistributor/v1 (interfaces: ShardDistributorExecutorCanaryAPIYARPCClient) // // Generated by this command: // // mockgen -package pinger -destination canary_client_mock.go github.com/uber/cadence/.gen/proto/sharddistributor/v1 ShardDistributorExecutorCanaryAPIYARPCClient // // Package pinger is a generated GoMock package. package pinger import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" ) // MockShardDistributorExecutorCanaryAPIYARPCClient is a mock of ShardDistributorExecutorCanaryAPIYARPCClient interface. type MockShardDistributorExecutorCanaryAPIYARPCClient struct { ctrl *gomock.Controller recorder *MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder isgomock struct{} } // MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder is the mock recorder for MockShardDistributorExecutorCanaryAPIYARPCClient. type MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder struct { mock *MockShardDistributorExecutorCanaryAPIYARPCClient } // NewMockShardDistributorExecutorCanaryAPIYARPCClient creates a new mock instance. func NewMockShardDistributorExecutorCanaryAPIYARPCClient(ctrl *gomock.Controller) *MockShardDistributorExecutorCanaryAPIYARPCClient { mock := &MockShardDistributorExecutorCanaryAPIYARPCClient{ctrl: ctrl} mock.recorder = &MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShardDistributorExecutorCanaryAPIYARPCClient) EXPECT() *MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder { return m.recorder } // Ping mocks base method. func (m *MockShardDistributorExecutorCanaryAPIYARPCClient) Ping(arg0 context.Context, arg1 *sharddistributorv1.PingRequest, arg2 ...yarpc.CallOption) (*sharddistributorv1.PingResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Ping", varargs...) ret0, _ := ret[0].(*sharddistributorv1.PingResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Ping indicates an expected call of Ping. func (mr *MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder) Ping(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockShardDistributorExecutorCanaryAPIYARPCClient)(nil).Ping), varargs...) } ================================================ FILE: service/sharddistributor/canary/pinger/pingAndLog.go ================================================ package pinger import ( "context" "time" "go.uber.org/yarpc" "go.uber.org/zap" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/service/sharddistributor/client/spectatorclient" ) const ( pingTimeout = 5 * time.Second ) func PingShard(ctx context.Context, canaryClient sharddistributorv1.ShardDistributorExecutorCanaryAPIYARPCClient, logger *zap.Logger, namespace, shardKey string) { request := &sharddistributorv1.PingRequest{ ShardKey: shardKey, Namespace: namespace, } ctx, cancel := context.WithTimeout(ctx, pingTimeout) defer cancel() response, err := canaryClient.Ping(ctx, request, yarpc.WithShardKey(shardKey), yarpc.WithHeader(spectatorclient.NamespaceHeader, namespace)) if err != nil { logger.Error("Failed to ping shard", zap.String("namespace", namespace), zap.String("shard_key", shardKey), zap.Error(err)) return } // Verify response if !response.GetOwnsShard() { logger.Warn("Executor does not own shard", zap.String("namespace", namespace), zap.String("shard_key", shardKey), zap.String("executor_id", response.GetExecutorId())) return } logger.Info("Successfully pinged shard owner", zap.String("namespace", namespace), zap.String("shard_key", shardKey), zap.String("executor_id", response.GetExecutorId())) } ================================================ FILE: service/sharddistributor/canary/pinger/pinger.go ================================================ package pinger import ( "context" "fmt" "math/rand" "sync" "time" "go.uber.org/fx" "go.uber.org/zap" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" ) //go:generate mockgen -package $GOPACKAGE -destination canary_client_mock.go github.com/uber/cadence/.gen/proto/sharddistributor/v1 ShardDistributorExecutorCanaryAPIYARPCClient const ( pingInterval = 1 * time.Second pingJitterCoeff = 0.1 // 10% jitter ) // Pinger periodically pings shard owners in the fixed namespace type Pinger struct { logger *zap.Logger timeSource clock.TimeSource canaryClient sharddistributorv1.ShardDistributorExecutorCanaryAPIYARPCClient namespace string numShards int ctx context.Context cancel context.CancelFunc wg sync.WaitGroup } // Params are the parameters for creating a Pinger type Params struct { fx.In Logger *zap.Logger TimeSource clock.TimeSource CanaryClient sharddistributorv1.ShardDistributorExecutorCanaryAPIYARPCClient } // NewPinger creates a new Pinger for the fixed namespace func NewPinger(params Params, namespace string, numShards int) *Pinger { return &Pinger{ logger: params.Logger, timeSource: params.TimeSource, canaryClient: params.CanaryClient, namespace: namespace, numShards: numShards, } } // Start begins the periodic ping loop func (p *Pinger) Start(ctx context.Context) { p.logger.Info("Starting canary pinger", zap.String("namespace", p.namespace), zap.Int("num_shards", p.numShards)) p.ctx, p.cancel = context.WithCancel(context.WithoutCancel(ctx)) p.wg.Add(1) go p.pingLoop() } // Stop stops the ping loop func (p *Pinger) Stop() { if p.cancel != nil { p.cancel() } p.wg.Wait() } func (p *Pinger) pingLoop() { defer p.wg.Done() ticker := p.timeSource.NewTicker(backoff.JitDuration(pingInterval, pingJitterCoeff)) defer ticker.Stop() for { select { case <-p.ctx.Done(): p.logger.Info("Pinger context done, stopping") return case <-ticker.Chan(): p.pingRandomShard() ticker.Reset(backoff.JitDuration(pingInterval, pingJitterCoeff)) } } } // Pings a random shard in the namespace and logs the results func (p *Pinger) pingRandomShard() { shardNum := rand.Intn(p.numShards) shardKey := fmt.Sprintf("%d", shardNum) PingShard(p.ctx, p.canaryClient, p.logger, p.namespace, shardKey) } ================================================ FILE: service/sharddistributor/canary/pinger/pinger_test.go ================================================ package pinger import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/mock/gomock" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/common/clock" ) func TestPingerStartStop(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockClient := NewMockShardDistributorExecutorCanaryAPIYARPCClient(ctrl) pinger := NewPinger(Params{ Logger: zap.NewNop(), TimeSource: clock.NewRealTimeSource(), CanaryClient: mockClient, }, "test-ns", 10) pinger.Start(context.Background()) pinger.Stop() } func TestPingerPingRandomShard(t *testing.T) { defer goleak.VerifyNone(t) cases := []struct { name string setupClientMock func(*MockShardDistributorExecutorCanaryAPIYARPCClient) expectedLog string }{ { name: "owns shard", setupClientMock: func(mockClient *MockShardDistributorExecutorCanaryAPIYARPCClient) { mockClient.EXPECT().Ping(gomock.Any(), gomock.Any(), gomock.Any()). Return(&sharddistributorv1.PingResponse{ OwnsShard: true, ExecutorId: "127.0.0.1:7953", }, nil) }, expectedLog: "Successfully pinged shard owner", }, { name: "does not own shard", setupClientMock: func(mockClient *MockShardDistributorExecutorCanaryAPIYARPCClient) { mockClient.EXPECT(). Ping(gomock.Any(), gomock.Any(), gomock.Any()). Return(&sharddistributorv1.PingResponse{ OwnsShard: false, ExecutorId: "127.0.0.1:7953", }, nil) }, expectedLog: "Executor does not own shard", }, { name: "RPC error", setupClientMock: func(mockClient *MockShardDistributorExecutorCanaryAPIYARPCClient) { mockClient.EXPECT(). Ping(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil, errors.New("network error")) }, expectedLog: "Failed to ping shard", }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockClient := NewMockShardDistributorExecutorCanaryAPIYARPCClient(ctrl) zapCore, logs := observer.New(zap.InfoLevel) logger := zap.New(zapCore) pinger := NewPinger(Params{ Logger: logger, TimeSource: clock.NewRealTimeSource(), CanaryClient: mockClient, }, "test-ns", 10) pinger.ctx = context.Background() tt.setupClientMock(mockClient) pinger.pingRandomShard() assert.Equal(t, 1, logs.FilterMessage(tt.expectedLog).Len()) }) } } ================================================ FILE: service/sharddistributor/canary/processor/shardprocessor.go ================================================ package processor import ( "context" "strconv" "sync" "sync/atomic" "time" "go.uber.org/zap" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) // This is a small shard processor, the only thing it currently does it // count the number of steps it has processed and log that information. const ( processInterval = 10 * time.Second ) // NewShardProcessor creates a new ShardProcessor. func NewShardProcessor(shardID string, timeSource clock.TimeSource, logger *zap.Logger) *ShardProcessor { p := &ShardProcessor{ shardID: shardID, shardLoad: shardLoadFromID(shardID), timeSource: timeSource, logger: logger, stopChan: make(chan struct{}), } p.status.Store(int32(types.ShardStatusREADY)) return p } // ShardProcessor is a processor for a shard. type ShardProcessor struct { shardID string shardLoad float64 timeSource clock.TimeSource logger *zap.Logger stopChan chan struct{} goRoutineWg sync.WaitGroup processSteps int status atomic.Int32 } var _ executorclient.ShardProcessor = (*ShardProcessor)(nil) // GetShardReport implements executorclient.ShardProcessor. func (p *ShardProcessor) GetShardReport() executorclient.ShardReport { return executorclient.ShardReport{ ShardLoad: p.shardLoad, // We return a load from shardID Status: types.ShardStatus(p.status.Load()), // Report the shard as ready since it's actively processing } } // Start implements executorclient.ShardProcessor. func (p *ShardProcessor) Start(_ context.Context) error { p.logger.Info("Starting shard processor", zap.String("shardID", p.shardID)) p.goRoutineWg.Add(1) go p.process() return nil } // Stop implements executorclient.ShardProcessor. func (p *ShardProcessor) Stop() { p.logger.Info("Stopping shard processor", zap.String("shardID", p.shardID)) close(p.stopChan) p.goRoutineWg.Wait() } func (p *ShardProcessor) SetShardStatus(status types.ShardStatus) { p.status.Store(int32(status)) } func (p *ShardProcessor) process() { defer p.goRoutineWg.Done() ticker := p.timeSource.NewTicker(processInterval) defer ticker.Stop() for { select { case <-p.stopChan: p.logger.Info("Stopping shard processor", zap.String("shardID", p.shardID), zap.Int("steps", p.processSteps)) return case <-ticker.Chan(): p.processSteps++ p.logger.Info("Processing shard", zap.String("shardID", p.shardID), zap.Int("steps", p.processSteps)) } } } // shardLoadFromID returns a shard load based on the shard ID. // If the shard ID is not a valid integer, it returns 1.0. func shardLoadFromID(shardID string) float64 { if parsed, err := strconv.Atoi(shardID); err == nil && parsed > 0 { return float64(parsed) } return 1.0 } ================================================ FILE: service/sharddistributor/canary/processor/shardprocessor_test.go ================================================ package processor import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/zap/zaptest" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/types" ) func TestNewShardProcessor(t *testing.T) { shardID := "test-shard-123" timeSource := clock.NewRealTimeSource() logger := zaptest.NewLogger(t) processor := NewShardProcessor(shardID, timeSource, logger) require.NotNil(t, processor) assert.Equal(t, shardID, processor.shardID) assert.Equal(t, timeSource, processor.timeSource) assert.Equal(t, logger, processor.logger) assert.NotNil(t, processor.stopChan) } func TestShardProcessor_GetShardReport(t *testing.T) { processor := NewShardProcessor("test-shard", clock.NewRealTimeSource(), zaptest.NewLogger(t)) report := processor.GetShardReport() // the simple implementation just returns 1.0 for load and READY status assert.Equal(t, 1.0, report.ShardLoad) assert.Equal(t, types.ShardStatusREADY, report.Status) } func TestShardProcessor_Start_Process_Stop(t *testing.T) { // Verify that after stopping the processor, there are no goroutines left goleak.VerifyNone(t) logger := zaptest.NewLogger(t) clock := clock.NewMockedTimeSource() processor := NewShardProcessor("test-shard", clock, logger) ctx := context.Background() processor.Start(ctx) // The processor will block on time clock.BlockUntil(1) // Let time pass until the ticker is triggered clock.Advance(processInterval + 1*time.Second) // Sleep for a bit to schedule the go-routine time.Sleep(10 * time.Millisecond) // Stop the processor processor.Stop() // Assert that the processor has processed at least once assert.Greater(t, processor.processSteps, 0) } func Test_shardLoadFromID(t *testing.T) { tests := []struct { shardID string want float64 }{ {"0", 1.0}, {"shard-1", 1.0}, {"42", 42.0}, {"999", 999.0}, {"", 1.0}, } for _, tt := range tests { t.Run(tt.shardID, func(t *testing.T) { got := shardLoadFromID(tt.shardID) assert.Equal(t, tt.want, got) }) } } ================================================ FILE: service/sharddistributor/canary/processorephemeral/canary_client_mock_test.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/.gen/proto/sharddistributor/v1 (interfaces: ShardDistributorExecutorCanaryAPIYARPCClient) // // Generated by this command: // // mockgen -package processorephemeral -destination canary_client_mock_test.go github.com/uber/cadence/.gen/proto/sharddistributor/v1 ShardDistributorExecutorCanaryAPIYARPCClient // // Package processorephemeral is a generated GoMock package. package processorephemeral import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" ) // MockShardDistributorExecutorCanaryAPIYARPCClient is a mock of ShardDistributorExecutorCanaryAPIYARPCClient interface. type MockShardDistributorExecutorCanaryAPIYARPCClient struct { ctrl *gomock.Controller recorder *MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder isgomock struct{} } // MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder is the mock recorder for MockShardDistributorExecutorCanaryAPIYARPCClient. type MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder struct { mock *MockShardDistributorExecutorCanaryAPIYARPCClient } // NewMockShardDistributorExecutorCanaryAPIYARPCClient creates a new mock instance. func NewMockShardDistributorExecutorCanaryAPIYARPCClient(ctrl *gomock.Controller) *MockShardDistributorExecutorCanaryAPIYARPCClient { mock := &MockShardDistributorExecutorCanaryAPIYARPCClient{ctrl: ctrl} mock.recorder = &MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShardDistributorExecutorCanaryAPIYARPCClient) EXPECT() *MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder { return m.recorder } // Ping mocks base method. func (m *MockShardDistributorExecutorCanaryAPIYARPCClient) Ping(arg0 context.Context, arg1 *sharddistributorv1.PingRequest, arg2 ...yarpc.CallOption) (*sharddistributorv1.PingResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Ping", varargs...) ret0, _ := ret[0].(*sharddistributorv1.PingResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Ping indicates an expected call of Ping. func (mr *MockShardDistributorExecutorCanaryAPIYARPCClientMockRecorder) Ping(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockShardDistributorExecutorCanaryAPIYARPCClient)(nil).Ping), varargs...) } ================================================ FILE: service/sharddistributor/canary/processorephemeral/shardcreator.go ================================================ package processorephemeral import ( "context" "sync" "time" "github.com/google/uuid" "go.uber.org/fx" "go.uber.org/zap" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/service/sharddistributor/canary/pinger" ) //go:generate mockgen -package $GOPACKAGE -destination canary_client_mock_test.go github.com/uber/cadence/.gen/proto/sharddistributor/v1 ShardDistributorExecutorCanaryAPIYARPCClient const ( shardCreationInterval = 1 * time.Second ) // ShardCreator creates shards at regular intervals for ephemeral canary testing type ShardCreator struct { logger *zap.Logger timeSource clock.TimeSource canaryClient sharddistributorv1.ShardDistributorExecutorCanaryAPIYARPCClient namespaces []string stopChan chan struct{} goRoutineWg sync.WaitGroup } // ShardCreatorParams contains the dependencies needed to create a ShardCreator type ShardCreatorParams struct { fx.In Logger *zap.Logger TimeSource clock.TimeSource CanaryClient sharddistributorv1.ShardDistributorExecutorCanaryAPIYARPCClient } // NewShardCreator creates a new ShardCreator instance with the given parameters and namespace func NewShardCreator(params ShardCreatorParams, namespaces []string) *ShardCreator { return &ShardCreator{ logger: params.Logger, timeSource: params.TimeSource, canaryClient: params.CanaryClient, stopChan: make(chan struct{}), goRoutineWg: sync.WaitGroup{}, namespaces: namespaces, } } // Start begins the shard creation process in a background goroutine func (s *ShardCreator) Start() { s.goRoutineWg.Add(1) go s.process(context.Background()) s.logger.Info("Shard creator started") } // Stop stops the shard creation process and waits for the goroutine to finish func (s *ShardCreator) Stop() { close(s.stopChan) s.goRoutineWg.Wait() s.logger.Info("Shard creator stopped") } // ShardCreatorModule creates an fx module for the shard creator with the given namespace func ShardCreatorModule(namespace []string) fx.Option { return fx.Module("shard-creator", fx.Provide(func(params ShardCreatorParams) *ShardCreator { return NewShardCreator(params, namespace) }), fx.Invoke(func(lifecycle fx.Lifecycle, shardCreator *ShardCreator) { lifecycle.Append(fx.StartStopHook(shardCreator.Start, shardCreator.Stop)) }), ) } func (s *ShardCreator) process(ctx context.Context) { defer s.goRoutineWg.Done() ticker := s.timeSource.NewTicker(shardCreationInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-s.stopChan: return case <-ticker.Chan(): for _, namespace := range s.namespaces { shardKey := uuid.New().String() s.logger.Info("Creating shard", zap.String("shardKey", shardKey), zap.String("namespace", namespace)) pinger.PingShard(ctx, s.canaryClient, s.logger, namespace, shardKey) } } } } ================================================ FILE: service/sharddistributor/canary/processorephemeral/shardcreator_test.go ================================================ package processorephemeral import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/goleak" "go.uber.org/mock/gomock" "go.uber.org/zap/zaptest" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/common/clock" ) func TestShardCreator_PingsShards(t *testing.T) { goleak.VerifyNone(t) logger := zaptest.NewLogger(t) timeSource := clock.NewMockedTimeSource() ctrl := gomock.NewController(t) namespace := "test-namespace" mockCanaryClient := NewMockShardDistributorExecutorCanaryAPIYARPCClient(ctrl) // Ping happens after successful GetShardOwner mockCanaryClient.EXPECT(). Ping(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn(func(ctx interface{}, req *sharddistributorv1.PingRequest, opts ...interface{}) (*sharddistributorv1.PingResponse, error) { assert.NotEmpty(t, req.ShardKey) assert.Equal(t, namespace, req.Namespace) return &sharddistributorv1.PingResponse{ OwnsShard: true, ExecutorId: "executor-1", }, nil }) params := ShardCreatorParams{ Logger: logger, TimeSource: timeSource, CanaryClient: mockCanaryClient, } creator := NewShardCreator(params, []string{namespace}) creator.Start() // Wait for the goroutine to start and do it's ping timeSource.BlockUntil(1) timeSource.Advance(shardCreationInterval + 100*time.Millisecond) time.Sleep(10 * time.Millisecond) creator.Stop() } ================================================ FILE: service/sharddistributor/canary/processorephemeral/shardprocessor.go ================================================ package processorephemeral import ( "context" "math/rand" "sync" "sync/atomic" "time" "go.uber.org/zap" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) // This is a small shard processor, the only thing it currently does it // count the number of steps it has processed and log that information. const ( processInterval = 10 * time.Second // We create a new shard every second. For each of them we have a chance of them to be done of 1/60 every second. // This means the average time to complete a shard is 60 seconds. // It also means we in average have 60 shards per instance running at any given time. stopInterval = 1 * time.Second shardProcessorDoneChance = 60 ) // NewShardProcessor creates a new ShardProcessor. func NewShardProcessor(shardID string, timeSource clock.TimeSource, logger *zap.Logger) *ShardProcessor { p := &ShardProcessor{ shardID: shardID, timeSource: timeSource, logger: logger, stopChan: make(chan struct{}), } p.SetShardStatus(types.ShardStatusREADY) return p } // ShardProcessor is a processor for a shard. type ShardProcessor struct { shardID string timeSource clock.TimeSource logger *zap.Logger stopChan chan struct{} goRoutineWg sync.WaitGroup processSteps int status atomic.Int32 } var _ executorclient.ShardProcessor = (*ShardProcessor)(nil) // GetShardReport implements executorclient.ShardProcessor. func (p *ShardProcessor) GetShardReport() executorclient.ShardReport { return executorclient.ShardReport{ ShardLoad: 1.0, // We return 1.0 for all shards for now. Status: types.ShardStatus(p.status.Load()), // Report the status of the shard } } // Start implements executorclient.ShardProcessor. func (p *ShardProcessor) Start(_ context.Context) error { p.logger.Info("Starting shard processor", zap.String("shardID", p.shardID)) p.goRoutineWg.Add(1) go p.process() return nil } // Stop implements executorclient.ShardProcessor. func (p *ShardProcessor) Stop() { close(p.stopChan) p.goRoutineWg.Wait() } func (p *ShardProcessor) SetShardStatus(status types.ShardStatus) { p.status.Store(int32(status)) } func (p *ShardProcessor) process() { defer p.goRoutineWg.Done() ticker := p.timeSource.NewTicker(processInterval) defer ticker.Stop() stopTicker := p.timeSource.NewTicker(stopInterval) defer stopTicker.Stop() for { select { case <-p.stopChan: p.logger.Info("Stopping shard processor", zap.String("shardID", p.shardID), zap.Int("steps", p.processSteps), zap.String("status", types.ShardStatus(p.status.Load()).String())) return case <-ticker.Chan(): p.logger.Info("Processing shard", zap.String("shardID", p.shardID), zap.Int("steps", p.processSteps), zap.String("status", types.ShardStatus(p.status.Load()).String())) case <-stopTicker.Chan(): p.processSteps++ if rand.Intn(shardProcessorDoneChance) == 0 { p.logger.Info("Setting shard processor to done", zap.String("shardID", p.shardID), zap.Int("steps", p.processSteps), zap.String("status", types.ShardStatus(p.status.Load()).String())) p.SetShardStatus(types.ShardStatusDONE) } } } } ================================================ FILE: service/sharddistributor/canary/processorephemeral/shardprocessor_test.go ================================================ package processorephemeral import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/zap/zaptest" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/types" ) func TestNewShardProcessor(t *testing.T) { shardID := "test-shard-123" timeSource := clock.NewRealTimeSource() logger := zaptest.NewLogger(t) processor := NewShardProcessor(shardID, timeSource, logger) require.NotNil(t, processor) assert.Equal(t, shardID, processor.shardID) assert.Equal(t, timeSource, processor.timeSource) assert.Equal(t, logger, processor.logger) assert.NotNil(t, processor.stopChan) } func TestShardProcessor_GetShardReport(t *testing.T) { processor := NewShardProcessor("test-shard", clock.NewRealTimeSource(), zaptest.NewLogger(t)) report := processor.GetShardReport() // the simple implementation just returns 1.0 for load and READY status assert.Equal(t, 1.0, report.ShardLoad) assert.Equal(t, types.ShardStatusREADY, report.Status) } func TestShardProcessor_Start_Process_Stop(t *testing.T) { // Verify that after stopping the processor, there are no goroutines left goleak.VerifyNone(t) logger := zaptest.NewLogger(t) clock := clock.NewMockedTimeSource() processor := NewShardProcessor("test-shard", clock, logger) ctx := context.Background() processor.Start(ctx) // The processor will block on time clock.BlockUntil(1) // Let time pass until the ticker is triggered clock.Advance(processInterval + 1*time.Second) // Sleep for a bit to schedule the go-routine time.Sleep(10 * time.Millisecond) // Stop the processor processor.Stop() // Assert that the processor has processed at least once assert.Greater(t, processor.processSteps, 0) } ================================================ FILE: service/sharddistributor/canary/sharddistributorclient/shardDistributorClient.go ================================================ package sharddistributorclient import ( "go.uber.org/fx" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/client/wrappers/grpc" timeoutwrapper "github.com/uber/cadence/client/wrappers/timeout" ) // Params contains the dependencies needed to create a shard distributor client type Params struct { fx.In YarpcClient sharddistributorv1.ShardDistributorAPIYARPCClient } // NewShardDistributorClient creates a new shard distributor client with GRPC and timeout wrappers func NewShardDistributorClient(p Params) (sharddistributor.Client, error) { shardDistributorExecutorClient := grpc.NewShardDistributorClient(p.YarpcClient) shardDistributorExecutorClient = timeoutwrapper.NewShardDistributorClient(shardDistributorExecutorClient, timeoutwrapper.ShardDistributorExecutorDefaultTimeout) return shardDistributorExecutorClient, nil } ================================================ FILE: service/sharddistributor/canary/sharddistributorexecutorclient/shardDistributorExecutorClient.go ================================================ package sharddistributorexecutorclient import ( "go.uber.org/fx" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/client/wrappers/grpc" timeoutwrapper "github.com/uber/cadence/client/wrappers/timeout" "github.com/uber/cadence/service/sharddistributor/client/executorclient" ) // Params contains the dependencies needed to create a shard distributor client type Params struct { fx.In YarpcClient sharddistributorv1.ShardDistributorExecutorAPIYARPCClient } // NewShardDistributorExecutorClient creates a new shard distributor executor client with GRPC and timeout wrappers func NewShardDistributorExecutorClient(p Params) (executorclient.Client, error) { shardDistributorExecutorClient := grpc.NewShardDistributorExecutorClient(p.YarpcClient) shardDistributorExecutorClient = timeoutwrapper.NewShardDistributorExecutorClient(shardDistributorExecutorClient, timeoutwrapper.ShardDistributorExecutorDefaultTimeout) return shardDistributorExecutorClient, nil } ================================================ FILE: service/sharddistributor/client/clientcommon/config.go ================================================ package clientcommon import ( "fmt" "strings" "time" "github.com/uber/cadence/common/types" sdconfig "github.com/uber/cadence/service/sharddistributor/config" ) const defaultPeerTTL = 2 * time.Minute // NamespaceConfig represents configuration for a single namespace type NamespaceConfig struct { Namespace string `yaml:"namespace"` HeartBeatInterval time.Duration `yaml:"heartbeat_interval"` MigrationMode string `yaml:"migration_mode"` TTLShard time.Duration `yaml:"ttl_shard"` // time after which shards are stopped if they are not used TTLReport time.Duration `yaml:"ttl_report"` // time after which the shard report status (including load) needs to be updated } // GetMigrationMode converts the string migration mode to types.MigrationMode using the shared configMode map func (nc *NamespaceConfig) GetMigrationMode() types.MigrationMode { mode := strings.ToLower(strings.TrimSpace(nc.MigrationMode)) if migrationMode, ok := sdconfig.MigrationMode[mode]; ok { return migrationMode } // Default to INVALID if not specified or unrecognized return types.MigrationModeINVALID } // Config represents configuration for multiple namespaces type Config struct { Namespaces []NamespaceConfig `yaml:"namespaces"` PeerTTL time.Duration `yaml:"peer_ttl"` // TTL for idle peers; default 2m if zero } // GetConfigForNamespace returns the config for a specific namespace func (c *Config) GetConfigForNamespace(namespace string) (*NamespaceConfig, error) { for _, ns := range c.Namespaces { if ns.Namespace == namespace { return &ns, nil } } return nil, fmt.Errorf("namespace %s not found in config", namespace) } // GetSingleConfig returns the config if there's exactly one namespace, otherwise returns an error func (c *Config) GetSingleConfig() (*NamespaceConfig, error) { if len(c.Namespaces) == 0 { return nil, fmt.Errorf("no namespaces configured") } if len(c.Namespaces) > 1 { return nil, fmt.Errorf("multiple namespaces configured (%d), must specify which namespace to use", len(c.Namespaces)) } return &c.Namespaces[0], nil } // GetPeerTTL returns the configured peer TTL, or the default 2m if not set. func (c *Config) GetPeerTTL() time.Duration { if c.PeerTTL <= 0 { return defaultPeerTTL } return c.PeerTTL } // Validate validates the configuration func (c *Config) Validate() error { if len(c.Namespaces) == 0 { return fmt.Errorf("at least one namespace must be configured") } seenNamespaces := make(map[string]bool) for i, ns := range c.Namespaces { if ns.Namespace == "" { return fmt.Errorf("namespace %d: namespace name cannot be empty", i) } if ns.HeartBeatInterval <= 0 { return fmt.Errorf("namespace %d (%s): heartbeat_interval must be greater than 0", i, ns.Namespace) } if seenNamespaces[ns.Namespace] { return fmt.Errorf("duplicate namespace: %s", ns.Namespace) } seenNamespaces[ns.Namespace] = true } return nil } ================================================ FILE: service/sharddistributor/client/clientcommon/config_test.go ================================================ package clientcommon import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" ) func TestNamespaceConfig_GetMigrationMode(t *testing.T) { tests := []struct { name string migrationMode string expected types.MigrationMode }{ { name: "local_passthrough", migrationMode: "local_pass", expected: types.MigrationModeLOCALPASSTHROUGH, }, { name: "local_passthrough_shadow", migrationMode: "local_pass_shadow", expected: types.MigrationModeLOCALPASSTHROUGHSHADOW, }, { name: "distributed_passthrough", migrationMode: "distributed_pass", expected: types.MigrationModeDISTRIBUTEDPASSTHROUGH, }, { name: "onboarded", migrationMode: "onboarded", expected: types.MigrationModeONBOARDED, }, { name: "invalid", migrationMode: "invalid", expected: types.MigrationModeINVALID, }, { name: "empty string", migrationMode: "", expected: types.MigrationModeINVALID, }, { name: "unknown mode", migrationMode: "unknown_mode", expected: types.MigrationModeINVALID, }, { name: "case insensitive - uppercase", migrationMode: "ONBOARDED", expected: types.MigrationModeONBOARDED, }, { name: "case insensitive - mixed case", migrationMode: "Local_Pass", expected: types.MigrationModeLOCALPASSTHROUGH, }, { name: "whitespace trimming", migrationMode: " onboarded ", expected: types.MigrationModeONBOARDED, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { config := &NamespaceConfig{ MigrationMode: tt.migrationMode, } assert.Equal(t, tt.expected, config.GetMigrationMode()) }) } } func TestConfig_GetPeerTTL(t *testing.T) { tests := []struct { name string peerTTL time.Duration expected time.Duration }{ { name: "zero uses default", peerTTL: 0, expected: 2 * time.Minute, }, { name: "non-zero returned as-is", peerTTL: 5 * time.Minute, expected: 5 * time.Minute, }, { name: "negative uses default", peerTTL: -1 * time.Second, expected: 2 * time.Minute, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { cfg := Config{PeerTTL: tc.peerTTL} assert.Equal(t, tc.expected, cfg.GetPeerTTL()) }) } } ================================================ FILE: service/sharddistributor/client/clientcommon/constants.go ================================================ package clientcommon const ( GrpcAddressMetadataKey = "grpc_address" ) ================================================ FILE: service/sharddistributor/client/clientcommon/drain_observer.go ================================================ package clientcommon //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination drain_observer_mock.go . DrainSignalObserver // DrainSignalObserver observes infrastructure drain signals. // Drain is reversible: if the instance reappears in discovery, // Undrain() fires, allowing the consumer to resume operations. // // Implementations use close-to-broadcast semantics: the returned channel is // closed when the event occurs, so all goroutines selecting on it wake up. // After each close, a fresh channel is created for the next cycle. type DrainSignalObserver interface { // Drain returns a channel closed when the instance is // removed from service discovery. Drain() <-chan struct{} // Undrain returns a channel closed when the instance is // added back to service discovery after a drain. Undrain() <-chan struct{} } ================================================ FILE: service/sharddistributor/client/clientcommon/drain_observer_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: drain_observer.go // // Generated by this command: // // mockgen -package clientcommon -source drain_observer.go -destination drain_observer_mock.go . DrainSignalObserver // // Package clientcommon is a generated GoMock package. package clientcommon import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockDrainSignalObserver is a mock of DrainSignalObserver interface. type MockDrainSignalObserver struct { ctrl *gomock.Controller recorder *MockDrainSignalObserverMockRecorder isgomock struct{} } // MockDrainSignalObserverMockRecorder is the mock recorder for MockDrainSignalObserver. type MockDrainSignalObserverMockRecorder struct { mock *MockDrainSignalObserver } // NewMockDrainSignalObserver creates a new mock instance. func NewMockDrainSignalObserver(ctrl *gomock.Controller) *MockDrainSignalObserver { mock := &MockDrainSignalObserver{ctrl: ctrl} mock.recorder = &MockDrainSignalObserverMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDrainSignalObserver) EXPECT() *MockDrainSignalObserverMockRecorder { return m.recorder } // Drain mocks base method. func (m *MockDrainSignalObserver) Drain() <-chan struct{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Drain") ret0, _ := ret[0].(<-chan struct{}) return ret0 } // Drain indicates an expected call of Drain. func (mr *MockDrainSignalObserverMockRecorder) Drain() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Drain", reflect.TypeOf((*MockDrainSignalObserver)(nil).Drain)) } // Undrain mocks base method. func (m *MockDrainSignalObserver) Undrain() <-chan struct{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Undrain") ret0, _ := ret[0].(<-chan struct{}) return ret0 } // Undrain indicates an expected call of Undrain. func (mr *MockDrainSignalObserverMockRecorder) Undrain() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Undrain", reflect.TypeOf((*MockDrainSignalObserver)(nil).Undrain)) } ================================================ FILE: service/sharddistributor/client/executorclient/client.go ================================================ package executorclient import ( "context" "errors" "fmt" "os" "github.com/google/uuid" "github.com/uber-go/tally" "go.uber.org/fx" "go.uber.org/yarpc" timeoutwrapper "github.com/uber/cadence/client/wrappers/timeout" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" "github.com/uber/cadence/service/sharddistributor/client/executorclient/metricsconstants" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go . ShardProcessorFactory,ShardProcessor,Executor,Client // ErrShardProcessNotFound is returned by GetShardProcess when this host is not // assigned the requested shard. Callers that interpret shard ownership should // treat this as an ownership-loss signal rather than an internal error. var ErrShardProcessNotFound = errors.New("shard process not found") type Client interface { Heartbeat(context.Context, *types.ExecutorHeartbeatRequest, ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) } type ExecutorMetadata map[string]string type ShardReport struct { ShardLoad float64 Status types.ShardStatus } type ShardProcessor interface { Start(ctx context.Context) error Stop() GetShardReport() ShardReport SetShardStatus(types.ShardStatus) } type ShardProcessorFactory[SP ShardProcessor] interface { NewShardProcessor(shardID string) (SP, error) } type Executor[SP ShardProcessor] interface { Start(ctx context.Context) Stop() GetShardProcess(ctx context.Context, shardID string) (SP, error) // Get the namespace this executor is responsible for GetNamespace() string // Set metadata for the executor SetMetadata(metadata map[string]string) // Get the current metadata of the executor GetMetadata() map[string]string // AssignShardsFromLocalLogic is used for the migration during local-passthrough, local-passthrough-shadow, distributed-passthrough AssignShardsFromLocalLogic(ctx context.Context, shardAssignment map[string]*types.ShardAssignment) error // RemoveShardsFromLocalLogic is used for the migration during local-passthrough, local-passthrough-shadow, distributed-passthrough RemoveShardsFromLocalLogic(shardIDs []string) error // IsOnboardedToSD is returning true if the executor relies on SD for distribution IsOnboardedToSD() bool } type Params[SP ShardProcessor] struct { fx.In ExecutorClient Client MetricsScope tally.Scope Logger log.Logger ShardProcessorFactory ShardProcessorFactory[SP] Config clientcommon.Config TimeSource clock.TimeSource Metadata ExecutorMetadata `optional:"true"` DrainObserver clientcommon.DrainSignalObserver `optional:"true"` } // NewExecutorWithNamespace creates an executor for a specific namespace func NewExecutorWithNamespace[SP ShardProcessor](params Params[SP], namespace string) (Executor[SP], error) { // Validate the config first if err := params.Config.Validate(); err != nil { return nil, fmt.Errorf("invalid config: %w", err) } // Get config for the specified namespace namespaceConfig, err := params.Config.GetConfigForNamespace(namespace) if err != nil { return nil, fmt.Errorf("get config for namespace %s: %w", namespace, err) } return newExecutorWithConfig(params, namespaceConfig) } // NewExecutor creates an executor using auto-selection (single namespace only) func NewExecutor[SP ShardProcessor](params Params[SP]) (Executor[SP], error) { // Validate the config first if err := params.Config.Validate(); err != nil { return nil, fmt.Errorf("invalid config: %w", err) } // Auto-select if there's only one namespace namespaceConfig, err := params.Config.GetSingleConfig() if err != nil { return nil, fmt.Errorf("auto-select namespace: %w", err) } return newExecutorWithConfig(params, namespaceConfig) } func newExecutorWithConfig[SP ShardProcessor](params Params[SP], namespaceConfig *clientcommon.NamespaceConfig) (Executor[SP], error) { shardDistributorClient, err := createShardDistributorExecutorClient(params.ExecutorClient, params.MetricsScope, params.Logger) if err != nil { return nil, fmt.Errorf("create shard distributor executor client: %w", err) } // TODO: get executor ID from environment executorID := uuid.New().String() hostname, err := os.Hostname() if err != nil { return nil, fmt.Errorf("get hostname: %w", err) } metricsScope := params.MetricsScope.Tagged(map[string]string{ metrics.OperationTagName: metricsconstants.ShardDistributorExecutorOperationTagName, "namespace": namespaceConfig.Namespace, }) hostMetricsScope := metricsScope.Tagged(map[string]string{ "host": hostname, }) executor := &executorImpl[SP]{ logger: params.Logger, shardDistributorClient: shardDistributorClient, shardProcessorFactory: params.ShardProcessorFactory, heartBeatInterval: namespaceConfig.HeartBeatInterval, ttlShard: namespaceConfig.TTLShard, namespace: namespaceConfig.Namespace, executorID: executorID, timeSource: params.TimeSource, stopC: make(chan struct{}), metrics: metricsScope, hostMetrics: hostMetricsScope, metadata: syncExecutorMetadata{ data: params.Metadata, }, drainObserver: params.DrainObserver, } executor.setMigrationMode(namespaceConfig.GetMigrationMode()) return executor, nil } func createShardDistributorExecutorClient(client Client, metricsScope tally.Scope, logger log.Logger) (Client, error) { shardDistributorExecutorClient := timeoutwrapper.NewShardDistributorExecutorClient(client, timeoutwrapper.ShardDistributorExecutorDefaultTimeout) if metricsScope != nil { shardDistributorExecutorClient = NewMeteredShardDistributorExecutorClient(shardDistributorExecutorClient, metricsScope) } return shardDistributorExecutorClient, nil } func Module[SP ShardProcessor]() fx.Option { return fx.Module("shard-distributor-executor-client", fx.Provide(NewExecutor[SP]), fx.Invoke(func(executor Executor[SP], lc fx.Lifecycle) { lc.Append(fx.StartStopHook(executor.Start, executor.Stop)) }), ) } // ModuleWithNamespace creates an executor module for a specific namespace func ModuleWithNamespace[SP ShardProcessor](namespace string) fx.Option { return fx.Module(fmt.Sprintf("shard-distributor-executor-client-%s", namespace), fx.Provide(func(params Params[SP]) (Executor[SP], error) { return NewExecutorWithNamespace(params, namespace) }), fx.Invoke(func(executor Executor[SP], lc fx.Lifecycle) { lc.Append(fx.StartStopHook(executor.Start, executor.Stop)) }), ) } ================================================ FILE: service/sharddistributor/client/executorclient/client_test.go ================================================ package executorclient import ( "testing" "time" "github.com/uber-go/tally" "go.uber.org/fx" "go.uber.org/fx/fxtest" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" ) func TestModule(t *testing.T) { ctrl := gomock.NewController(t) mockLogger := log.NewNoop() mockShardProcessorFactory := NewMockShardProcessorFactory[*MockShardProcessor](ctrl) shardDistributorExecutorClient := NewMockClient(ctrl) shardDistributorExecutorClient.EXPECT(). Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ExecutorHeartbeatResponse{}, nil). AnyTimes() // Example config config := clientcommon.Config{ Namespaces: []clientcommon.NamespaceConfig{ { Namespace: "test-namespace", HeartBeatInterval: 5 * time.Second, }, }, } // Create a test app with the library, check that it starts and stops fxtest.New(t, fx.Provide(func() Client { return shardDistributorExecutorClient }), fx.Supply( fx.Annotate(tally.NoopScope, fx.As(new(tally.Scope))), fx.Annotate(mockLogger, fx.As(new(log.Logger))), fx.Annotate(mockShardProcessorFactory, fx.As(new(ShardProcessorFactory[*MockShardProcessor]))), fx.Annotate(clock.NewMockedTimeSource(), fx.As(new(clock.TimeSource))), config, ), Module[*MockShardProcessor](), ).RequireStart().RequireStop() } // Create distinct mock processor types for testing multiple namespaces type MockShardProcessor1 struct { *MockShardProcessor } type MockShardProcessor2 struct { *MockShardProcessor } func TestModuleWithNamespace(t *testing.T) { ctrl := gomock.NewController(t) mockLogger := log.NewNoop() shardDistributorExecutorClient := NewMockClient(ctrl) shardDistributorExecutorClient.EXPECT(). Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ExecutorHeartbeatResponse{}, nil). AnyTimes() mockFactory1 := NewMockShardProcessorFactory[*MockShardProcessor1](ctrl) mockFactory2 := NewMockShardProcessorFactory[*MockShardProcessor2](ctrl) // Multi-namespace config config := clientcommon.Config{ Namespaces: []clientcommon.NamespaceConfig{ { Namespace: "namespace1", HeartBeatInterval: 5 * time.Second, }, { Namespace: "namespace2", HeartBeatInterval: 10 * time.Second, }, }, } // Create a test app with two namespace-specific modules using different processor types fxtest.New(t, fx.Provide(func() Client { return shardDistributorExecutorClient }), fx.Supply( fx.Annotate(tally.NoopScope, fx.As(new(tally.Scope))), fx.Annotate(mockLogger, fx.As(new(log.Logger))), fx.Annotate(clock.NewMockedTimeSource(), fx.As(new(clock.TimeSource))), fx.Annotate(mockFactory1, fx.As(new(ShardProcessorFactory[*MockShardProcessor1]))), fx.Annotate(mockFactory2, fx.As(new(ShardProcessorFactory[*MockShardProcessor2]))), config, ), // Two namespace-specific modules with different processor types ModuleWithNamespace[*MockShardProcessor1]("namespace1"), ModuleWithNamespace[*MockShardProcessor2]("namespace2"), ).RequireStart().RequireStop() } ================================================ FILE: service/sharddistributor/client/executorclient/clientimpl.go ================================================ package executorclient import ( "context" "errors" "fmt" "sync" "sync/atomic" "time" "github.com/uber-go/tally" "github.com/uber/cadence/client/sharddistributorexecutor" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" "github.com/uber/cadence/service/sharddistributor/client/executorclient/metricsconstants" "github.com/uber/cadence/service/sharddistributor/client/executorclient/syncgeneric" ) var ( // ErrLocalPassthroughMode indicates that the heartbeat loop should stop due to local passthrough mode ErrLocalPassthroughMode = errors.New("local passthrough mode: stopping heartbeat loop") // ErrAssignmentDivergenceLocalShard indicates that the local shard is not reported back from the heartbeat ErrAssignmentDivergenceLocalShard = errors.New("assignment divergence: local shard not in heartbeat or not ready") // ErrAssignmentDivergenceHeartbeatShard indicates that the shard in the heartbeat is not present in the local assignment ErrAssignmentDivergenceHeartbeatShard = errors.New("assignment divergence: heartbeat shard not in local") ) type processorState int32 const ( processorStateStarting processorState = iota processorStateStarted processorStateStopping ) const ( heartbeatJitterCoeff = 0.1 // 10% jitter drainingHeartbeatTimeout = 5 * time.Second ) type managedProcessor[SP ShardProcessor] struct { processor SP state atomic.Int32 } type syncExecutorMetadata struct { sync.RWMutex data map[string]string } func (m *syncExecutorMetadata) Set(metadata map[string]string) { m.Lock() defer m.Unlock() m.data = metadata } func (m *syncExecutorMetadata) Get() map[string]string { m.RLock() defer m.RUnlock() // Copy the map result := make(map[string]string, len(m.data)) for k, v := range m.data { result[k] = v } return result } func (mp *managedProcessor[SP]) setState(state processorState) { mp.state.Store(int32(state)) } func (mp *managedProcessor[SP]) getState() processorState { return processorState(mp.state.Load()) } func newManagedProcessor[SP ShardProcessor](processor SP, state processorState) *managedProcessor[SP] { managed := &managedProcessor[SP]{ processor: processor, state: atomic.Int32{}, } managed.setState(state) return managed } type executorImpl[SP ShardProcessor] struct { logger log.Logger shardDistributorClient sharddistributorexecutor.Client shardProcessorFactory ShardProcessorFactory[SP] namespace string stopC chan struct{} heartBeatInterval time.Duration ttlShard time.Duration managedProcessors syncgeneric.Map[string, *managedProcessor[SP]] processorsToLastUse syncgeneric.Map[string, time.Time] executorID string timeSource clock.TimeSource processLoopWG sync.WaitGroup assignmentMutex sync.Mutex metrics tally.Scope hostMetrics tally.Scope migrationMode atomic.Int32 metadata syncExecutorMetadata drainObserver clientcommon.DrainSignalObserver } func (e *executorImpl[SP]) setMigrationMode(mode types.MigrationMode) { e.migrationMode.Store(int32(mode)) } func (e *executorImpl[SP]) getMigrationMode() types.MigrationMode { return types.MigrationMode(e.migrationMode.Load()) } func (e *executorImpl[SP]) Start(ctx context.Context) { e.logger.Info("starting shard distributor executor", tag.ShardNamespace(e.namespace)) e.processLoopWG.Add(2) go func() { defer e.processLoopWG.Done() e.heartbeatloop(context.WithoutCancel(ctx)) }() go func() { defer e.processLoopWG.Done() e.shardCleanUpLoop(context.WithoutCancel(ctx)) }() } func (e *executorImpl[SP]) Stop() { e.logger.Info("stopping shard distributor executor", tag.ShardNamespace(e.namespace)) close(e.stopC) e.processLoopWG.Wait() } func (e *executorImpl[SP]) GetShardProcess(ctx context.Context, shardID string) (SP, error) { e.processorsToLastUse.Store(shardID, e.timeSource.Now()) shardProcess, ok := e.managedProcessors.Load(shardID) if !ok { if e.getMigrationMode() == types.MigrationModeLOCALPASSTHROUGH { // Fail immediately if we are in LOCAL_PASSTHROUGH mode var zero SP return zero, fmt.Errorf("%w for shard ID: %s", ErrShardProcessNotFound, shardID) } // Do a heartbeat and check again err := e.heartbeatAndUpdateAssignment(ctx) if err != nil { var zero SP return zero, fmt.Errorf("heartbeat and assign shards: %w", err) } // Check again if the shard process is found shardProcess, ok = e.managedProcessors.Load(shardID) if !ok { var zero SP return zero, fmt.Errorf("%w for shard ID: %s", ErrShardProcessNotFound, shardID) } } return shardProcess.processor, nil } func (e *executorImpl[SP]) IsOnboardedToSD() bool { return e.getMigrationMode() == types.MigrationModeONBOARDED } func (e *executorImpl[SP]) AssignShardsFromLocalLogic(ctx context.Context, shardAssignment map[string]*types.ShardAssignment) error { e.assignmentMutex.Lock() defer e.assignmentMutex.Unlock() if e.getMigrationMode() == types.MigrationModeONBOARDED { return fmt.Errorf("migration mode is onborded, no local assignemnt allowed") } e.logger.Info("Executing external shard assignment") e.addNewShards(ctx, shardAssignment) return nil } func (e *executorImpl[SP]) RemoveShardsFromLocalLogic(shardIDs []string) error { if e.getMigrationMode() == types.MigrationModeONBOARDED { return fmt.Errorf("migration mode is onborded, no local assignemnt allowed") } return e.removeShards(shardIDs) } func (e *executorImpl[SP]) removeShards(shardIDs []string) error { e.assignmentMutex.Lock() defer e.assignmentMutex.Unlock() e.logger.Info("Executing external shard deletion assignment") e.deleteShards(shardIDs) return nil } // drainChannel returns the drain signal channel, or nil if no observer is configured. func (e *executorImpl[SP]) drainChannel() <-chan struct{} { if e.drainObserver != nil { return e.drainObserver.Drain() } return nil } func (e *executorImpl[SP]) heartbeatloop(ctx context.Context) { // Check if initial migration mode is LOCAL_PASSTHROUGH - if so, skip heartbeating entirely if e.getMigrationMode() == types.MigrationModeLOCALPASSTHROUGH { e.logger.Info("initial migration mode is local passthrough, skipping heartbeat loop") return } heartBeatTimer := e.timeSource.NewTimer(backoff.JitDuration(e.heartBeatInterval, heartbeatJitterCoeff)) defer heartBeatTimer.Stop() drainCh := e.drainChannel() for { select { case <-ctx.Done(): e.logger.Info("shard distributor executor context done, stopping") e.stopShardProcessors() e.sendDrainingHeartbeat() return case <-e.stopC: e.logger.Info("shard distributor executor stopped") e.stopShardProcessors() e.sendDrainingHeartbeat() return case <-drainCh: e.logger.Info("drain signal received, stopping shard processors") e.stopShardProcessors() e.sendDrainingHeartbeat() if !e.waitForUndrain(ctx) { return } e.logger.Info("undrain signal received, resuming heartbeat") drainCh = e.drainObserver.Drain() heartBeatTimer.Reset(backoff.JitDuration(e.heartBeatInterval, heartbeatJitterCoeff)) case <-heartBeatTimer.Chan(): heartBeatTimer.Reset(backoff.JitDuration(e.heartBeatInterval, heartbeatJitterCoeff)) err := e.heartbeatAndUpdateAssignment(ctx) if errors.Is(err, ErrLocalPassthroughMode) { e.logger.Info("local passthrough mode: stopping heartbeat loop") return } if err != nil { e.logger.Error("failed to heartbeat and assign shards", tag.Error(err)) continue } } } } // waitForUndrain blocks until the undrain signal fires or the executor is stopped. // Returns true if undrained (caller should resume), false if stopped. func (e *executorImpl[SP]) waitForUndrain(ctx context.Context) bool { if e.drainObserver == nil { return false } undrainCh := e.drainObserver.Undrain() select { case <-ctx.Done(): return false case <-e.stopC: return false case <-undrainCh: return true } } func (e *executorImpl[SP]) heartbeatAndUpdateAssignment(ctx context.Context) error { if !e.assignmentMutex.TryLock() { e.logger.Error("still doing assignment, skipping heartbeat") e.metrics.Counter(metricsconstants.ShardDistributorExecutorHeartbeatSkipped).Inc(1) return nil } defer e.assignmentMutex.Unlock() shardAssignment, err := e.heartbeatAndHandleMigrationMode(ctx) if err != nil { return err } if shardAssignment != nil { e.updateShardAssignmentMetered(ctx, shardAssignment) } return nil } func (e *executorImpl[SP]) heartbeatAndHandleMigrationMode(ctx context.Context) (shardAssignment map[string]*types.ShardAssignment, err error) { shardAssignment, migrationMode, err := e.heartbeat(ctx) if err != nil { // TODO: should we stop the executor, and drop all the shards? return nil, fmt.Errorf("failed to heartbeat: %w", err) } // Handle migration mode logic switch migrationMode { case types.MigrationModeLOCALPASSTHROUGH: // LOCAL_PASSTHROUGH: statically assigned, stop heartbeating return nil, ErrLocalPassthroughMode case types.MigrationModeLOCALPASSTHROUGHSHADOW: // LOCAL_PASSTHROUGH_SHADOW: check response but don't apply it err = e.compareAssignments(shardAssignment) return nil, err case types.MigrationModeDISTRIBUTEDPASSTHROUGH: // DISTRIBUTED_PASSTHROUGH: validate then apply the assignment err = e.compareAssignments(shardAssignment) if err != nil { return nil, err } return shardAssignment, nil // Continue with applying the assignment from heartbeat case types.MigrationModeONBOARDED: // ONBOARDED: normal flow, apply the assignment from heartbeat return shardAssignment, nil // Continue with normal assignment logic below default: e.logger.Warn("unknown migration mode, skipping assignment", tag.ShardNamespace(e.namespace), tag.Dynamic("migration-mode", migrationMode)) return nil, nil } } func (e *executorImpl[SP]) updateShardAssignmentMetered(ctx context.Context, shardAssignment map[string]*types.ShardAssignment) { startTime := e.timeSource.Now() defer e.metrics. Histogram(metricsconstants.ShardDistributorExecutorAssignLoopLatency, metricsconstants.ShardDistributorExecutorAssignLoopLatencyBuckets). RecordDuration(e.timeSource.Since(startTime)) e.updateShardAssignment(ctx, shardAssignment) } func (e *executorImpl[SP]) heartbeat(ctx context.Context) (shardAssignments map[string]*types.ShardAssignment, migrationMode types.MigrationMode, err error) { return e.sendHeartbeat(ctx, types.ExecutorStatusACTIVE) } func (e *executorImpl[SP]) sendHeartbeat(ctx context.Context, status types.ExecutorStatus) (map[string]*types.ShardAssignment, types.MigrationMode, error) { // Fill in the shard status reports shardStatusReports := make(map[string]*types.ShardStatusReport) e.managedProcessors.Range(func(shardID string, managedProcessor *managedProcessor[SP]) bool { if managedProcessor.getState() == processorStateStarted { shardStatus := managedProcessor.processor.GetShardReport() shardStatusReports[shardID] = &types.ShardStatusReport{ ShardLoad: shardStatus.ShardLoad, Status: shardStatus.Status, } } return true }) e.hostMetrics.Gauge(metricsconstants.ShardDistributorExecutorOwnedShards).Update(float64(len(shardStatusReports))) // Create the request request := &types.ExecutorHeartbeatRequest{ Namespace: e.namespace, ExecutorID: e.executorID, Status: status, ShardStatusReports: shardStatusReports, Metadata: e.metadata.Get(), } // Send the request response, err := e.shardDistributorClient.Heartbeat(ctx, request) if err != nil { return nil, types.MigrationModeINVALID, fmt.Errorf("send heartbeat: %w", err) } previousMode := e.getMigrationMode() currentMode := response.MigrationMode if previousMode != currentMode { e.logger.Info("migration mode transition", tag.Dynamic("previous", previousMode), tag.Dynamic("current", currentMode), tag.ShardNamespace(e.namespace), tag.ShardExecutor(e.executorID)) e.setMigrationMode(currentMode) } return response.ShardAssignments, response.MigrationMode, nil } func (e *executorImpl[SP]) sendDrainingHeartbeat() { ctx, cancel := context.WithTimeout(context.Background(), drainingHeartbeatTimeout) defer cancel() _, _, err := e.sendHeartbeat(ctx, types.ExecutorStatusDRAINING) if err != nil { e.logger.Error("failed to send draining heartbeat", tag.Error(err)) } } func (e *executorImpl[SP]) updateShardAssignment(ctx context.Context, shardAssignments map[string]*types.ShardAssignment) { wg := sync.WaitGroup{} // Stop shard processing for shards not assigned to this executor e.managedProcessors.Range(func(shardID string, managedProcessor *managedProcessor[SP]) bool { if assignment, ok := shardAssignments[shardID]; !ok || assignment.Status != types.AssignmentStatusREADY { wg.Add(1) go func(shardID string) { defer wg.Done() e.stopManagerProcessor(shardID) }(shardID) } return true }) // Start shard processing for shards assigned to this executor for shardID, assignment := range shardAssignments { if assignment.Status == types.AssignmentStatusREADY { wg.Add(1) go func(shardID string) { defer wg.Done() e.addManagerProcessor(ctx, shardID) }(shardID) } } wg.Wait() } func (e *executorImpl[SP]) addNewShards(ctx context.Context, shardAssignments map[string]*types.ShardAssignment) { wg := sync.WaitGroup{} for shardID, assignment := range shardAssignments { if assignment.Status == types.AssignmentStatusREADY { wg.Add(1) go func(shardID string) { defer wg.Done() e.addManagerProcessor(ctx, shardID) }(shardID) } } wg.Wait() } func (e *executorImpl[SP]) deleteShards(shardIDs []string) { wg := sync.WaitGroup{} for _, shardID := range shardIDs { wg.Add(1) go func(shardID string) { defer wg.Done() e.stopManagerProcessor(shardID) }(shardID) } wg.Wait() } func (e *executorImpl[SP]) stopShardProcessors() { wg := sync.WaitGroup{} e.managedProcessors.Range(func(shardID string, managedProcessor *managedProcessor[SP]) bool { wg.Add(1) go func(shardID string) { defer wg.Done() e.stopManagerProcessor(shardID) }(shardID) return true }) wg.Wait() } func (e *executorImpl[SP]) addManagerProcessor(ctx context.Context, shardID string) { if _, ok := e.managedProcessors.Load(shardID); !ok { e.metrics.Counter(metricsconstants.ShardDistributorExecutorShardsStarted).Inc(1) processor, err := e.shardProcessorFactory.NewShardProcessor(shardID) if err != nil { e.logger.Error("failed to create shard processor", tag.Error(err)) e.metrics.Counter(metricsconstants.ShardDistributorExecutorProcessorCreationFailures).Inc(1) return } managedProcessor := newManagedProcessor(processor, processorStateStarting) e.managedProcessors.Store(shardID, managedProcessor) processor.Start(ctx) managedProcessor.setState(processorStateStarted) } } func (e *executorImpl[SP]) stopManagerProcessor(shardID string) { managedProcessor, ok := e.managedProcessors.Load(shardID) // If the processor do not exist for the shard, or it is already stopping, skip it if !ok || managedProcessor.getState() == processorStateStopping { return } e.metrics.Counter(metricsconstants.ShardDistributorExecutorShardsStopped).Inc(1) managedProcessor.setState(processorStateStopping) managedProcessor.processor.Stop() e.managedProcessors.Delete(shardID) } func (e *executorImpl[SP]) shardCleanUpLoop(ctx context.Context) { // We don't run the loop for invalid durations if e.ttlShard <= 0 { return } shardCleanUpTimer := e.timeSource.NewTimer(backoff.JitDuration(e.ttlShard, heartbeatJitterCoeff)) defer shardCleanUpTimer.Stop() for { select { case <-ctx.Done(): return case <-e.stopC: return case <-shardCleanUpTimer.Chan(): e.processorsToLastUse.Range(func(shardID string, time time.Time) bool { if time.Add(e.ttlShard).Before(e.timeSource.Now()) { if e.getMigrationMode() == types.MigrationModeONBOARDED { mp, ok := e.managedProcessors.Load(shardID) if ok { mp.processor.SetShardStatus(types.ShardStatusDONE) } } else { e.deleteShards([]string{shardID}) } e.processorsToLastUse.Delete(shardID) } return true }) } } } // compareAssignments compares the local assignments with the heartbeat response assignments // return error if the assignment are not the same and emits convergence or divergence metrics func (e *executorImpl[SP]) compareAssignments(heartbeatAssignments map[string]*types.ShardAssignment) error { // Get current local assignments localAssignments := make(map[string]bool) e.managedProcessors.Range(func(shardID string, managedProcessor *managedProcessor[SP]) bool { if managedProcessor.getState() == processorStateStarted { localAssignments[shardID] = true } return true }) // Check if all local assignments are in heartbeat assignments with READY status for shardID := range localAssignments { assignment, exists := heartbeatAssignments[shardID] if !exists || assignment.Status != types.AssignmentStatusREADY { e.logger.Warn("assignment divergence: local shard not in heartbeat or not ready", tag.Dynamic("shard-id", shardID)) e.emitMetricsConvergence(false) return ErrAssignmentDivergenceLocalShard } } // Check if all heartbeat READY assignments are in local assignments for shardID, assignment := range heartbeatAssignments { if assignment.Status == types.AssignmentStatusREADY { if !localAssignments[shardID] { e.logger.Warn("assignment divergence: heartbeat shard not in local", tag.Dynamic("shard-id", shardID)) e.emitMetricsConvergence(false) return ErrAssignmentDivergenceHeartbeatShard } } } e.emitMetricsConvergence(true) return nil } func (e *executorImpl[SP]) emitMetricsConvergence(converged bool) { if converged { e.metrics.Counter(metricsconstants.ShardDistributorExecutorAssignmentConvergence).Inc(1) } else { e.metrics.Counter(metricsconstants.ShardDistributorExecutorAssignmentDivergence).Inc(1) } } func (e *executorImpl[SP]) GetNamespace() string { return e.namespace } func (e *executorImpl[SP]) SetMetadata(metadata map[string]string) { e.metadata.Set(metadata) } func (e *executorImpl[SP]) GetMetadata() map[string]string { return e.metadata.Get() } ================================================ FILE: service/sharddistributor/client/executorclient/clientimpl_test.go ================================================ package executorclient import ( "context" "fmt" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "go.uber.org/goleak" "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributorexecutor" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/executorclient/syncgeneric" ) // closeDrainObserver is a test helper that implements DrainSignalObserver // with close-to-broadcast semantics, matching the real implementation. type closeDrainObserver struct { mu sync.Mutex drainCh chan struct{} undrainCh chan struct{} } func newCloseDrainObserver() *closeDrainObserver { return &closeDrainObserver{ drainCh: make(chan struct{}), undrainCh: make(chan struct{}), } } func (o *closeDrainObserver) Drain() <-chan struct{} { o.mu.Lock() defer o.mu.Unlock() return o.drainCh } func (o *closeDrainObserver) Undrain() <-chan struct{} { o.mu.Lock() defer o.mu.Unlock() return o.undrainCh } func (o *closeDrainObserver) SignalDrain() { o.mu.Lock() defer o.mu.Unlock() close(o.drainCh) o.undrainCh = make(chan struct{}) } func (o *closeDrainObserver) SignalUndrain() { o.mu.Lock() defer o.mu.Unlock() close(o.undrainCh) o.drainCh = make(chan struct{}) } func expectDrainingHeartbeat(t *testing.T, mockClient *sharddistributorexecutor.MockClient) { mockClient.EXPECT(). Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *types.ExecutorHeartbeatRequest, _ ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) { assert.Equal(t, types.ExecutorStatusDRAINING, req.Status) return &types.ExecutorHeartbeatResponse{}, nil }) } func newTestExecutor( client sharddistributorexecutor.Client, factory ShardProcessorFactory[*MockShardProcessor], timeSource clock.TimeSource, ) *executorImpl[*MockShardProcessor] { if timeSource == nil { timeSource = clock.NewMockedTimeSource() } return &executorImpl[*MockShardProcessor]{ logger: log.NewNoop(), metrics: tally.NoopScope, hostMetrics: tally.NoopScope, shardDistributorClient: client, shardProcessorFactory: factory, namespace: "test-namespace", stopC: make(chan struct{}), heartBeatInterval: 10 * time.Second, managedProcessors: syncgeneric.Map[string, *managedProcessor[*MockShardProcessor]]{}, executorID: "test-executor-id", timeSource: timeSource, } } func TestHeartBeatLoop(t *testing.T) { // Ensure that there are no goroutines leaked defer goleak.VerifyNone(t) // Create mocks ctrl := gomock.NewController(t) mockShardDistributorClient := sharddistributorexecutor.NewMockClient(ctrl) // We expect nothing is assigned to the executor, and we assign two shards to it mockShardDistributorClient.EXPECT().Heartbeat(gomock.Any(), &types.ExecutorHeartbeatRequest{ Namespace: "test-namespace", ExecutorID: "test-executor-id", Status: types.ExecutorStatusACTIVE, ShardStatusReports: make(map[string]*types.ShardStatusReport), Metadata: make(map[string]string), }, gomock.Any()). Return(&types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{ "test-shard-id1": {Status: types.AssignmentStatusREADY}, "test-shard-id2": {Status: types.AssignmentStatusREADY}, }, MigrationMode: types.MigrationModeONBOARDED, }, nil) expectDrainingHeartbeat(t, mockShardDistributorClient) // The two shards are assigned to the executor, so we expect them to be created, started and stopped mockShardProcessor1 := NewMockShardProcessor(ctrl) mockShardProcessor1.EXPECT().Start(gomock.Any()) mockShardProcessor1.EXPECT().Stop() mockShardProcessor2 := NewMockShardProcessor(ctrl) mockShardProcessor2.EXPECT().Start(gomock.Any()) mockShardProcessor2.EXPECT().Stop() mockShardProcessorFactory := NewMockShardProcessorFactory[*MockShardProcessor](ctrl) mockShardProcessorFactory.EXPECT().NewShardProcessor(gomock.Any()).Return(mockShardProcessor1, nil) mockShardProcessorFactory.EXPECT().NewShardProcessor(gomock.Any()).Return(mockShardProcessor2, nil) // We use a mock time source to control the heartbeat loop mockTimeSource := clock.NewMockedTimeSource() // Create the executor executor := newTestExecutor(mockShardDistributorClient, mockShardProcessorFactory, mockTimeSource) // Start the executor, and defer stopping it executor.Start(context.Background()) defer executor.Stop() // Make sure the heartbeat loop has done an iteration and assigned the shards to the executor mockTimeSource.BlockUntil(1) mockTimeSource.Advance(15 * time.Second) time.Sleep(10 * time.Millisecond) // Force the heartbeatloop goroutine to run mockTimeSource.BlockUntil(1) // Assert that the two shards are assigned to the executor processor1, err := executor.GetShardProcess(context.Background(), "test-shard-id1") assert.NoError(t, err) assert.Equal(t, mockShardProcessor1, processor1) processor2, err := executor.GetShardProcess(context.Background(), "test-shard-id2") assert.NoError(t, err) assert.Equal(t, mockShardProcessor2, processor2) // Check that non-owned-shard-id is not in the local cache, we check directly since we don't want to trigger a heartbeat _, ok := executor.managedProcessors.Load("non-owned-shard-id") assert.False(t, ok) } func TestHeartbeat(t *testing.T) { // Setup mocks ctrl := gomock.NewController(t) // We have two shards assigned to the executor, and we expect a third shard to be assigned to it shardDistributorClient := sharddistributorexecutor.NewMockClient(ctrl) shardDistributorClient.EXPECT().Heartbeat(gomock.Any(), &types.ExecutorHeartbeatRequest{ Namespace: "test-namespace", ExecutorID: "test-executor-id", Status: types.ExecutorStatusACTIVE, ShardStatusReports: map[string]*types.ShardStatusReport{ "test-shard-id1": {Status: types.ShardStatusREADY, ShardLoad: 0.123}, "test-shard-id2": {Status: types.ShardStatusREADY, ShardLoad: 0.456}, }, Metadata: make(map[string]string), }, gomock.Any()).Return(&types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{ "test-shard-id1": {Status: types.AssignmentStatusREADY}, "test-shard-id2": {Status: types.AssignmentStatusREADY}, "test-shard-id3": {Status: types.AssignmentStatusREADY}, }, MigrationMode: types.MigrationModeONBOARDED, }, nil) shardProcessorMock1 := NewMockShardProcessor(ctrl) shardProcessorMock1.EXPECT().GetShardReport().Return(ShardReport{ShardLoad: 0.123, Status: types.ShardStatusREADY}) shardProcessorMock2 := NewMockShardProcessor(ctrl) shardProcessorMock2.EXPECT().GetShardReport().Return(ShardReport{ShardLoad: 0.456, Status: types.ShardStatusREADY}) // Create the executor executor := newTestExecutor(shardDistributorClient, nil, nil) executor.managedProcessors.Store("test-shard-id1", newManagedProcessor(shardProcessorMock1, processorStateStarted)) executor.managedProcessors.Store("test-shard-id2", newManagedProcessor(shardProcessorMock2, processorStateStarted)) // Do the call to heartbeat shardAssignments, _, err := executor.heartbeat(context.Background()) // Assert that we now have 3 shards in the assignment assert.NoError(t, err) assert.Equal(t, 3, len(shardAssignments)) assert.Equal(t, types.AssignmentStatusREADY, shardAssignments["test-shard-id1"].Status) assert.Equal(t, types.AssignmentStatusREADY, shardAssignments["test-shard-id2"].Status) assert.Equal(t, types.AssignmentStatusREADY, shardAssignments["test-shard-id3"].Status) } func TestHeartBeatLoop_ShardAssignmentChange(t *testing.T) { ctrl := gomock.NewController(t) // Setup mocks shardProcessorMock1 := NewMockShardProcessor(ctrl) shardProcessorMock2 := NewMockShardProcessor(ctrl) shardProcessorMock3 := NewMockShardProcessor(ctrl) shardProcessorFactory := NewMockShardProcessorFactory[*MockShardProcessor](ctrl) shardProcessorFactory.EXPECT().NewShardProcessor(gomock.Any()).Return(shardProcessorMock3, nil) // Create the executor currently has shards 1 and 2 assigned to it executor := newTestExecutor(nil, shardProcessorFactory, nil) executor.managedProcessors.Store("test-shard-id1", newManagedProcessor(shardProcessorMock1, processorStateStarted)) executor.managedProcessors.Store("test-shard-id2", newManagedProcessor(shardProcessorMock2, processorStateStarted)) // We expect to get a new assignment with shards 2 and 3 assigned to it newAssignment := map[string]*types.ShardAssignment{ "test-shard-id2": {Status: types.AssignmentStatusREADY}, "test-shard-id3": {Status: types.AssignmentStatusREADY}, } // With the new assignment, shardProcessorMock1 should be stopped and shardProcessorMock3 should be started shardProcessorMock1.EXPECT().Stop() shardProcessorMock3.EXPECT().Start(gomock.Any()) // Update the shard assignment executor.updateShardAssignment(context.Background(), newAssignment) time.Sleep(10 * time.Millisecond) // Force the updateShardAssignment goroutines to run // Assert that we now have the 2 shards in the assignment processor2, err := executor.GetShardProcess(context.Background(), "test-shard-id2") assert.NoError(t, err) assert.Equal(t, shardProcessorMock2, processor2) processor3, err := executor.GetShardProcess(context.Background(), "test-shard-id3") assert.NoError(t, err) assert.Equal(t, shardProcessorMock3, processor3) // Check that we do not have shard "test-shard-id1" in the local cache // we lookup directly since we don't want to trigger a heartbeat _, ok := executor.managedProcessors.Load("test-shard-id1") assert.False(t, ok) } func TestAssignShardsFromLocalLogic(t *testing.T) { ctrl := gomock.NewController(t) tests := []struct { name string params map[string]*types.ShardAssignment setup func() *executorImpl[*MockShardProcessor] assert func(err error, executor *executorImpl[*MockShardProcessor]) }{ { name: "AssignShardsFromLocalLogic fails if the namespace is onboarded", params: map[string]*types.ShardAssignment{}, setup: func() *executorImpl[*MockShardProcessor] { executor := newTestExecutor(nil, nil, nil) executor.setMigrationMode(types.MigrationModeONBOARDED) return executor }, assert: func(err error, executor *executorImpl[*MockShardProcessor]) {}, }, { name: "AssignShardsFromLocalLogic succeed with only logs if it is not possible to create a new shard processor", params: map[string]*types.ShardAssignment{ "test-shard-id2": {Status: types.AssignmentStatusREADY}, "test-shard-id3": {Status: types.AssignmentStatusREADY}}, setup: func() *executorImpl[*MockShardProcessor] { shardProcessorMock1 := NewMockShardProcessor(ctrl) shardProcessorMock2 := NewMockShardProcessor(ctrl) shardProcessorFactory := NewMockShardProcessorFactory[*MockShardProcessor](ctrl) // Setup mocks shardProcessorFactory.EXPECT().NewShardProcessor(gomock.Any()).Return(nil, assert.AnError) executor := newTestExecutor(nil, shardProcessorFactory, nil) executor.managedProcessors.Store("test-shard-id1", newManagedProcessor(shardProcessorMock1, processorStateStarted)) executor.managedProcessors.Store("test-shard-id2", newManagedProcessor(shardProcessorMock2, processorStateStarted)) return executor }, assert: func(err error, executor *executorImpl[*MockShardProcessor]) { assert.NoError(t, err) }, }, { name: "AssignShardsFromLocalLogic succeed ", params: map[string]*types.ShardAssignment{ "test-shard-id2": {Status: types.AssignmentStatusREADY}, "test-shard-id3": {Status: types.AssignmentStatusREADY}}, setup: func() *executorImpl[*MockShardProcessor] { shardProcessorMock1 := NewMockShardProcessor(ctrl) shardProcessorMock2 := NewMockShardProcessor(ctrl) shardProcessorMock3 := NewMockShardProcessor(ctrl) shardProcessorFactory := NewMockShardProcessorFactory[*MockShardProcessor](ctrl) shardProcessorFactory.EXPECT().NewShardProcessor(gomock.Any()).Return(shardProcessorMock3, nil) executor := newTestExecutor(nil, shardProcessorFactory, nil) executor.managedProcessors.Store("test-shard-id1", newManagedProcessor(shardProcessorMock1, processorStateStarted)) executor.managedProcessors.Store("test-shard-id2", newManagedProcessor(shardProcessorMock2, processorStateStarted)) // With the new assignment, shardProcessorMock3 should be started shardProcessorMock3.EXPECT().Start(gomock.Any()) shardProcessorMock1.EXPECT().GetShardReport().Return(ShardReport{Status: types.ShardStatusREADY}) shardProcessorMock2.EXPECT().GetShardReport().Return(ShardReport{Status: types.ShardStatusREADY}) shardProcessorMock3.EXPECT().GetShardReport().Return(ShardReport{Status: types.ShardStatusREADY}) return executor }, assert: func(_ error, executor *executorImpl[*MockShardProcessor]) { // Assert that we now have the 3 shards in the assignment processor1, err := executor.GetShardProcess(context.Background(), "test-shard-id1") assert.NoError(t, err) assert.Equal(t, types.ShardStatusREADY, processor1.GetShardReport().Status) processor2, err := executor.GetShardProcess(context.Background(), "test-shard-id2") assert.NoError(t, err) assert.Equal(t, types.ShardStatusREADY, processor2.GetShardReport().Status) processor3, err := executor.GetShardProcess(context.Background(), "test-shard-id3") assert.NoError(t, err) assert.Equal(t, types.ShardStatusREADY, processor3.GetShardReport().Status) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { executor := test.setup() err := executor.AssignShardsFromLocalLogic(context.Background(), test.params) time.Sleep(10 * time.Millisecond) // Force the updateShardAssignment goroutines to run test.assert(err, executor) }) } } func TestRemoveShardsFromLocalLogic(t *testing.T) { ctrl := gomock.NewController(t) tests := []struct { name string params []string setup func() *executorImpl[*MockShardProcessor] assert func(err error, executor *executorImpl[*MockShardProcessor]) }{ { name: "RemoveShardsFromLocalLogic fails if the namespace is onboarded", params: []string{}, setup: func() *executorImpl[*MockShardProcessor] { executor := newTestExecutor(nil, nil, nil) executor.setMigrationMode(types.MigrationModeONBOARDED) return executor }, assert: func(err error, executor *executorImpl[*MockShardProcessor]) {}, }, { name: "RemoveShardsFromLocalLogic succeed ", params: []string{ "test-shard-id2", "test-shard-id3"}, setup: func() *executorImpl[*MockShardProcessor] { shardProcessorMock1 := NewMockShardProcessor(ctrl) shardProcessorMock2 := NewMockShardProcessor(ctrl) executor := newTestExecutor(nil, nil, nil) executor.managedProcessors.Store("test-shard-id1", newManagedProcessor(shardProcessorMock1, processorStateStarted)) executor.managedProcessors.Store("test-shard-id2", newManagedProcessor(shardProcessorMock2, processorStateStarted)) // With the new assignment, shardProcessorMock2 should be stopped shardProcessorMock2.EXPECT().Stop() shardProcessorMock1.EXPECT().GetShardReport().Return(ShardReport{Status: types.ShardStatusREADY}) return executor }, assert: func(_ error, executor *executorImpl[*MockShardProcessor]) { // Assert that we now have the 1 shard in the assignment processor1, err := executor.GetShardProcess(context.Background(), "test-shard-id1") assert.NoError(t, err) assert.Equal(t, types.ShardStatusREADY, processor1.GetShardReport().Status) // Check that we do not have shard "test-shard-id2" in the local cache // we lookup directly since we don't want to trigger a heartbeat _, ok := executor.managedProcessors.Load("test-shard-id2") assert.False(t, ok) }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { executor := test.setup() err := executor.RemoveShardsFromLocalLogic(test.params) time.Sleep(10 * time.Millisecond) // Force the updateShardAssignment goroutines to run test.assert(err, executor) }) } } func TestHeartbeat_WithMigrationMode(t *testing.T) { ctrl := gomock.NewController(t) // Test that heartbeat returns migration mode correctly shardDistributorClient := sharddistributorexecutor.NewMockClient(ctrl) shardDistributorClient.EXPECT().Heartbeat(gomock.Any(), &types.ExecutorHeartbeatRequest{ Namespace: "test-namespace", ExecutorID: "test-executor-id", Status: types.ExecutorStatusACTIVE, ShardStatusReports: map[string]*types.ShardStatusReport{}, Metadata: make(map[string]string), }, gomock.Any()).Return(&types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{ "test-shard-id1": {Status: types.AssignmentStatusREADY}, }, MigrationMode: types.MigrationModeDISTRIBUTEDPASSTHROUGH, }, nil) executor := newTestExecutor(shardDistributorClient, nil, nil) executor.setMigrationMode(types.MigrationModeINVALID) shardAssignments, migrationMode, err := executor.heartbeat(context.Background()) assert.NoError(t, err) assert.Equal(t, 1, len(shardAssignments)) assert.Equal(t, types.MigrationModeDISTRIBUTEDPASSTHROUGH, migrationMode) assert.Equal(t, types.MigrationModeDISTRIBUTEDPASSTHROUGH, executor.getMigrationMode()) } func TestHeartbeat_MigrationModeTransition(t *testing.T) { ctrl := gomock.NewController(t) shardDistributorClient := sharddistributorexecutor.NewMockClient(ctrl) shardDistributorClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{}, MigrationMode: types.MigrationModeONBOARDED, }, nil) executor := newTestExecutor(shardDistributorClient, nil, nil) executor.setMigrationMode(types.MigrationModeDISTRIBUTEDPASSTHROUGH) _, migrationMode, err := executor.heartbeat(context.Background()) assert.NoError(t, err) assert.Equal(t, types.MigrationModeONBOARDED, migrationMode) assert.Equal(t, types.MigrationModeONBOARDED, executor.getMigrationMode()) } func TestHeartbeatLoop_LocalPassthrough_SkipsHeartbeat(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShardDistributorClient := sharddistributorexecutor.NewMockClient(ctrl) // No heartbeat should be called for LOCAL_PASSTHROUGH mode mockShardDistributorClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()).Times(0) mockTimeSource := clock.NewMockedTimeSource() executor := newTestExecutor(mockShardDistributorClient, nil, mockTimeSource) executor.setMigrationMode(types.MigrationModeLOCALPASSTHROUGH) executor.Start(context.Background()) defer executor.Stop() // Give some time for the heartbeat loop to potentially run (it shouldn't) time.Sleep(10 * time.Millisecond) } func TestHeartbeatLoop_LocalPassthroughShadow_SkipsAssignment(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShardDistributorClient := sharddistributorexecutor.NewMockClient(ctrl) // Heartbeat should be called but assignment should not be applied mockShardDistributorClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{ "test-shard-id1": {Status: types.AssignmentStatusREADY}, }, MigrationMode: types.MigrationModeLOCALPASSTHROUGHSHADOW, }, nil) expectDrainingHeartbeat(t, mockShardDistributorClient) mockShardProcessorFactory := NewMockShardProcessorFactory[*MockShardProcessor](ctrl) // No shard processor should be created mockShardProcessorFactory.EXPECT().NewShardProcessor(gomock.Any()).Times(0) mockTimeSource := clock.NewMockedTimeSource() executor := newTestExecutor(mockShardDistributorClient, mockShardProcessorFactory, mockTimeSource) executor.setMigrationMode(types.MigrationModeONBOARDED) executor.Start(context.Background()) defer executor.Stop() mockTimeSource.BlockUntil(1) mockTimeSource.Advance(15 * time.Second) time.Sleep(10 * time.Millisecond) mockTimeSource.BlockUntil(1) // Assert that no shards were assigned, we check directly since we don't want to trigger a heartbeat _, ok := executor.managedProcessors.Load("test-shard-id1") assert.False(t, ok) } func TestHeartbeatLoop_DistributedPassthrough_AppliesAssignment(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShardDistributorClient := sharddistributorexecutor.NewMockClient(ctrl) mockShardProcessor := NewMockShardProcessor(ctrl) mockShardProcessor.EXPECT().GetShardReport().Return(ShardReport{Status: types.ShardStatusREADY}).AnyTimes() mockShardProcessor.EXPECT().Stop() mockShardDistributorClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{ "test-shard-id1": {Status: types.AssignmentStatusREADY}, }, MigrationMode: types.MigrationModeDISTRIBUTEDPASSTHROUGH, }, nil) expectDrainingHeartbeat(t, mockShardDistributorClient) mockShardProcessorFactory := NewMockShardProcessorFactory[*MockShardProcessor](ctrl) mockTimeSource := clock.NewMockedTimeSource() executor := newTestExecutor(mockShardDistributorClient, mockShardProcessorFactory, mockTimeSource) executor.setMigrationMode(types.MigrationModeONBOARDED) // Pre-populate the executor with the shard to ensure convergence executor.managedProcessors.Store("test-shard-id1", newManagedProcessor(mockShardProcessor, processorStateStarted)) executor.Start(context.Background()) defer executor.Stop() mockTimeSource.BlockUntil(1) mockTimeSource.Advance(15 * time.Second) time.Sleep(10 * time.Millisecond) mockTimeSource.BlockUntil(1) // Assert that the shard was assigned processor, err := executor.GetShardProcess(context.Background(), "test-shard-id1") assert.NoError(t, err) assert.Equal(t, mockShardProcessor, processor) } func TestHeartbeatLoop_StopSignalSendsDrainingHeartbeat(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShardDistributorClient := sharddistributorexecutor.NewMockClient(ctrl) expectDrainingHeartbeat(t, mockShardDistributorClient) mockTimeSource := clock.NewMockedTimeSource() executor := newTestExecutor(mockShardDistributorClient, nil, mockTimeSource) ctx, cancel := context.WithCancel(context.Background()) defer cancel() done := make(chan struct{}) go func() { executor.heartbeatloop(ctx) close(done) }() close(executor.stopC) <-done } func TestHeartbeatLoop_ContextCancelSendsDrainingHeartbeat(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockShardDistributorClient := sharddistributorexecutor.NewMockClient(ctrl) expectDrainingHeartbeat(t, mockShardDistributorClient) mockTimeSource := clock.NewMockedTimeSource() executor := newTestExecutor(mockShardDistributorClient, nil, mockTimeSource) ctx, cancel := context.WithCancel(context.Background()) cancel() // cancelling the context executor.heartbeatloop(ctx) } func TestCompareAssignments_Converged(t *testing.T) { ctrl := gomock.NewController(t) shardProcessorMock1 := NewMockShardProcessor(ctrl) shardProcessorMock2 := NewMockShardProcessor(ctrl) testScope := tally.NewTestScope("test", nil) executor := newTestExecutor(nil, nil, nil) executor.metrics = testScope executor.managedProcessors.Store("test-shard-id1", newManagedProcessor(shardProcessorMock1, processorStateStarted)) executor.managedProcessors.Store("test-shard-id2", newManagedProcessor(shardProcessorMock2, processorStateStarted)) heartbeatAssignments := map[string]*types.ShardAssignment{ "test-shard-id1": {Status: types.AssignmentStatusREADY}, "test-shard-id2": {Status: types.AssignmentStatusREADY}, } err := executor.compareAssignments(heartbeatAssignments) // Verify no error is returned when assignments converge assert.NoError(t, err) // Verify convergence metric was emitted snapshot := testScope.Snapshot() assert.Equal(t, int64(1), snapshot.Counters()["test.shard_distributor_executor_assignment_convergence+"].Value()) } func TestCompareAssignments_Diverged_MissingShard(t *testing.T) { ctrl := gomock.NewController(t) shardProcessorMock1 := NewMockShardProcessor(ctrl) shardProcessorMock2 := NewMockShardProcessor(ctrl) testScope := tally.NewTestScope("test", nil) executor := newTestExecutor(nil, nil, nil) executor.metrics = testScope executor.managedProcessors.Store("test-shard-id1", newManagedProcessor(shardProcessorMock1, processorStateStarted)) executor.managedProcessors.Store("test-shard-id2", newManagedProcessor(shardProcessorMock2, processorStateStarted)) // Heartbeat only has shard 1, local has both 1 and 2 heartbeatAssignments := map[string]*types.ShardAssignment{ "test-shard-id1": {Status: types.AssignmentStatusREADY}, } err := executor.compareAssignments(heartbeatAssignments) // Verify error is returned when local shard is missing from heartbeat assert.ErrorIs(t, err, ErrAssignmentDivergenceLocalShard) // Verify divergence metric was emitted snapshot := testScope.Snapshot() assert.Equal(t, int64(1), snapshot.Counters()["test.shard_distributor_executor_assignment_divergence+"].Value()) } func TestCompareAssignments_Diverged_ExtraShard(t *testing.T) { ctrl := gomock.NewController(t) shardProcessorMock1 := NewMockShardProcessor(ctrl) testScope := tally.NewTestScope("test", nil) executor := newTestExecutor(nil, nil, nil) executor.metrics = testScope executor.managedProcessors.Store("test-shard-id1", newManagedProcessor(shardProcessorMock1, processorStateStarted)) // Heartbeat has shards 1 and 2, local only has 1 heartbeatAssignments := map[string]*types.ShardAssignment{ "test-shard-id1": {Status: types.AssignmentStatusREADY}, "test-shard-id2": {Status: types.AssignmentStatusREADY}, } err := executor.compareAssignments(heartbeatAssignments) // Verify error is returned when heartbeat has extra shard not in local assert.ErrorIs(t, err, ErrAssignmentDivergenceHeartbeatShard) // Verify divergence metric was emitted snapshot := testScope.Snapshot() assert.Equal(t, int64(1), snapshot.Counters()["test.shard_distributor_executor_assignment_divergence+"].Value()) } func TestCompareAssignments_Diverged_WrongStatus(t *testing.T) { ctrl := gomock.NewController(t) shardProcessorMock1 := NewMockShardProcessor(ctrl) testScope := tally.NewTestScope("test", nil) executor := newTestExecutor(nil, nil, nil) executor.metrics = testScope executor.managedProcessors.Store("test-shard-id1", newManagedProcessor(shardProcessorMock1, processorStateStarted)) // Heartbeat has shard 1 but with INVALID status heartbeatAssignments := map[string]*types.ShardAssignment{ "test-shard-id1": {Status: types.AssignmentStatusINVALID}, } err := executor.compareAssignments(heartbeatAssignments) // Verify error is returned when local shard has wrong status in heartbeat assert.ErrorIs(t, err, ErrAssignmentDivergenceLocalShard) // Verify divergence metric was emitted snapshot := testScope.Snapshot() assert.Equal(t, int64(1), snapshot.Counters()["test.shard_distributor_executor_assignment_divergence+"].Value()) } func TestGetShardProcess_NonOwnedShard_Fails(t *testing.T) { cases := map[string]struct { migrationMode types.MigrationMode expectedError error shardsInCache []string shardsReturnedOnHeartbeat map[string]*types.ShardAssignment heartbeatCallsExpected int heartBeatError error setupMocks func(shardProcessorFactory *MockShardProcessorFactory[*MockShardProcessor], shardProcessor *MockShardProcessor) }{ "empty cache local passthrough": { migrationMode: types.MigrationModeLOCALPASSTHROUGH, expectedError: ErrShardProcessNotFound, shardsInCache: []string{}, heartbeatCallsExpected: 0, }, "shard found": { migrationMode: types.MigrationModeONBOARDED, shardsInCache: []string{"test-shard-id1"}, heartbeatCallsExpected: 0, }, "shard found on heartbeat": { migrationMode: types.MigrationModeONBOARDED, shardsInCache: []string{}, shardsReturnedOnHeartbeat: map[string]*types.ShardAssignment{ "test-shard-id1": {Status: types.AssignmentStatusREADY}, }, heartbeatCallsExpected: 1, setupMocks: func(processorFactory *MockShardProcessorFactory[*MockShardProcessor], processor *MockShardProcessor) { processorFactory.EXPECT().NewShardProcessor(gomock.Any()).Return(processor, nil) processor.EXPECT().Start(gomock.Any()) }, }, "shard not found on heartbeat": { migrationMode: types.MigrationModeONBOARDED, shardsInCache: []string{}, shardsReturnedOnHeartbeat: map[string]*types.ShardAssignment{}, heartbeatCallsExpected: 1, expectedError: ErrShardProcessNotFound, }, "heartbeat error": { migrationMode: types.MigrationModeONBOARDED, shardsInCache: []string{}, heartbeatCallsExpected: 1, expectedError: fmt.Errorf("heartbeat and assign shards"), heartBeatError: assert.AnError, }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { ctrl := gomock.NewController(t) shardDistributorClient := sharddistributorexecutor.NewMockClient(ctrl) shardDistributorClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ExecutorHeartbeatResponse{ ShardAssignments: tc.shardsReturnedOnHeartbeat, MigrationMode: tc.migrationMode, }, tc.heartBeatError).Times(tc.heartbeatCallsExpected) shardProcessorFactory := NewMockShardProcessorFactory[*MockShardProcessor](ctrl) if tc.setupMocks != nil { tc.setupMocks(shardProcessorFactory, NewMockShardProcessor(ctrl)) } executor := newTestExecutor(shardDistributorClient, shardProcessorFactory, clock.NewMockedTimeSource()) executor.setMigrationMode(tc.migrationMode) for _, shardID := range tc.shardsInCache { executor.managedProcessors.Store(shardID, newManagedProcessor(NewMockShardProcessor(ctrl), processorStateStarted)) } _, err := executor.GetShardProcess(context.Background(), "test-shard-id1") if tc.expectedError != nil { assert.ErrorContains(t, err, tc.expectedError.Error()) } else { assert.NoError(t, err) } }) } } func TestExecutorMetadata_SetAndGet(t *testing.T) { metadata := &syncExecutorMetadata{ data: make(map[string]string), } // Set some metadata testData := map[string]string{ "key1": "value1", "key2": "value2", } metadata.Set(testData) // Get the metadata result := metadata.Get() assert.Equal(t, testData, result) } func TestExecutorMetadata_DefensiveCopy(t *testing.T) { metadata := &syncExecutorMetadata{ data: map[string]string{ "key1": "value1", }, } // Get the metadata result := metadata.Get() // Modify the returned map result["key1"] = "modified" result["key2"] = "new" // Original metadata should be unchanged original := metadata.Get() assert.Equal(t, "value1", original["key1"]) assert.NotContains(t, original, "key2") } func TestExecutorMetadata_ConcurrentAccess(t *testing.T) { metadata := &syncExecutorMetadata{ data: make(map[string]string), } const numGoroutines = 100 const numOperations = 100 var wg sync.WaitGroup wg.Add(numGoroutines * 2) // Concurrent writers for i := 0; i < numGoroutines; i++ { go func(id int) { defer wg.Done() for j := 0; j < numOperations; j++ { metadata.Set(map[string]string{ fmt.Sprintf("key-%d", id): fmt.Sprintf("value-%d-%d", id, j), }) } }(i) } // Concurrent readers for i := 0; i < numGoroutines; i++ { go func() { defer wg.Done() for j := 0; j < numOperations; j++ { _ = metadata.Get() } }() } wg.Wait() // Should not panic and should have some data result := metadata.Get() assert.NotNil(t, result) } func TestExecutorMetadata_NilHandling(t *testing.T) { metadata := &syncExecutorMetadata{ data: nil, } // Get should return empty map, not nil result := metadata.Get() assert.NotNil(t, result) assert.Empty(t, result) // Set with nil should work metadata.Set(nil) result = metadata.Get() assert.NotNil(t, result) assert.Empty(t, result) } func TestExecutorMetadata_EmptyMap(t *testing.T) { metadata := &syncExecutorMetadata{ data: make(map[string]string), } result := metadata.Get() assert.NotNil(t, result) assert.Empty(t, result) // Set empty map metadata.Set(map[string]string{}) result = metadata.Get() assert.NotNil(t, result) assert.Empty(t, result) } func TestShardCleanupLoop(t *testing.T) { tests := []struct { name string ttlShard time.Duration migrationMode types.MigrationMode setupProcessors func(executor *executorImpl[*MockShardProcessor], ctrl *gomock.Controller) []*MockShardProcessor advanceTime time.Duration expectedShardsDeleted []string expectedShardsKept []string }{ { name: "cleanup loop does not run when ttlShard is zero", ttlShard: 0, setupProcessors: func(executor *executorImpl[*MockShardProcessor], ctrl *gomock.Controller) []*MockShardProcessor { processor := NewMockShardProcessor(ctrl) executor.managedProcessors.Store("shard-1", newManagedProcessor(processor, processorStateStarted)) executor.processorsToLastUse.Store("shard-1", executor.timeSource.Now()) return []*MockShardProcessor{processor} }, advanceTime: 10 * time.Second, expectedShardsDeleted: []string{}, expectedShardsKept: []string{"shard-1"}, }, { name: "cleanup loop does not run when ttlShard is negative", ttlShard: -1 * time.Second, setupProcessors: func(executor *executorImpl[*MockShardProcessor], ctrl *gomock.Controller) []*MockShardProcessor { processor := NewMockShardProcessor(ctrl) executor.managedProcessors.Store("shard-1", newManagedProcessor(processor, processorStateStarted)) executor.processorsToLastUse.Store("shard-1", executor.timeSource.Now()) return []*MockShardProcessor{processor} }, advanceTime: 10 * time.Second, expectedShardsDeleted: []string{}, expectedShardsKept: []string{"shard-1"}, }, { name: "cleanup removes expired shard in non-onboarded mode", ttlShard: 5 * time.Second, migrationMode: types.MigrationModeLOCALPASSTHROUGH, setupProcessors: func(executor *executorImpl[*MockShardProcessor], ctrl *gomock.Controller) []*MockShardProcessor { processor := NewMockShardProcessor(ctrl) processor.EXPECT().Stop() executor.managedProcessors.Store("shard-1", newManagedProcessor(processor, processorStateStarted)) executor.processorsToLastUse.Store("shard-1", executor.timeSource.Now()) return []*MockShardProcessor{processor} }, advanceTime: 6 * time.Second, expectedShardsDeleted: []string{"shard-1"}, expectedShardsKept: []string{}, }, { name: "cleanup does not remove non-expired shard", ttlShard: 10 * time.Second, migrationMode: types.MigrationModeLOCALPASSTHROUGH, setupProcessors: func(executor *executorImpl[*MockShardProcessor], ctrl *gomock.Controller) []*MockShardProcessor { processor := NewMockShardProcessor(ctrl) executor.managedProcessors.Store("shard-1", newManagedProcessor(processor, processorStateStarted)) executor.processorsToLastUse.Store("shard-1", executor.timeSource.Now()) return []*MockShardProcessor{processor} }, advanceTime: 5 * time.Second, expectedShardsDeleted: []string{}, expectedShardsKept: []string{"shard-1"}, }, { name: "cleanup sets shard status to DONE and removes from processorsToLastUse in onboarded mode", ttlShard: 5 * time.Second, migrationMode: types.MigrationModeONBOARDED, setupProcessors: func(executor *executorImpl[*MockShardProcessor], ctrl *gomock.Controller) []*MockShardProcessor { processor := NewMockShardProcessor(ctrl) processor.EXPECT().SetShardStatus(types.ShardStatusDONE) executor.managedProcessors.Store("shard-1", newManagedProcessor(processor, processorStateStarted)) executor.processorsToLastUse.Store("shard-1", executor.timeSource.Now()) return []*MockShardProcessor{processor} }, advanceTime: 6 * time.Second, expectedShardsDeleted: []string{}, // Shard is removed from processorsToLastUse but kept in managedProcessors expectedShardsKept: []string{"shard-1"}, }, { name: "cleanup removes multiple expired shards in non-onboarded mode", ttlShard: 5 * time.Second, migrationMode: types.MigrationModeLOCALPASSTHROUGH, setupProcessors: func(executor *executorImpl[*MockShardProcessor], ctrl *gomock.Controller) []*MockShardProcessor { processor1 := NewMockShardProcessor(ctrl) processor1.EXPECT().Stop() processor2 := NewMockShardProcessor(ctrl) processor2.EXPECT().Stop() processor3 := NewMockShardProcessor(ctrl) executor.managedProcessors.Store("shard-1", newManagedProcessor(processor1, processorStateStarted)) executor.processorsToLastUse.Store("shard-1", executor.timeSource.Now()) executor.managedProcessors.Store("shard-2", newManagedProcessor(processor2, processorStateStarted)) executor.processorsToLastUse.Store("shard-2", executor.timeSource.Now()) executor.managedProcessors.Store("shard-3", newManagedProcessor(processor3, processorStateStarted)) executor.processorsToLastUse.Store("shard-3", executor.timeSource.Now().Add(3*time.Second)) return []*MockShardProcessor{processor1, processor2, processor3} }, advanceTime: 6 * time.Second, expectedShardsDeleted: []string{"shard-1", "shard-2"}, expectedShardsKept: []string{"shard-3"}, }, { name: "cleanup in distributed passthrough mode removes expired shards", ttlShard: 5 * time.Second, migrationMode: types.MigrationModeDISTRIBUTEDPASSTHROUGH, setupProcessors: func(executor *executorImpl[*MockShardProcessor], ctrl *gomock.Controller) []*MockShardProcessor { processor := NewMockShardProcessor(ctrl) processor.EXPECT().Stop() executor.managedProcessors.Store("shard-1", newManagedProcessor(processor, processorStateStarted)) executor.processorsToLastUse.Store("shard-1", executor.timeSource.Now()) return []*MockShardProcessor{processor} }, advanceTime: 6 * time.Second, expectedShardsDeleted: []string{"shard-1"}, expectedShardsKept: []string{}, }, { name: "cleanup removes shard from processorsToLastUse map even if not in managedProcessors", ttlShard: 5 * time.Second, migrationMode: types.MigrationModeLOCALPASSTHROUGH, setupProcessors: func(executor *executorImpl[*MockShardProcessor], ctrl *gomock.Controller) []*MockShardProcessor { // Add to processorsToLastUse but not to managedProcessors executor.processorsToLastUse.Store("shard-orphan", executor.timeSource.Now()) return []*MockShardProcessor{} }, advanceTime: 6 * time.Second, expectedShardsDeleted: []string{}, expectedShardsKept: []string{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockTimeSource := clock.NewMockedTimeSource() executor := newTestExecutor(nil, nil, mockTimeSource) executor.ttlShard = tt.ttlShard if tt.migrationMode != types.MigrationModeINVALID { executor.setMigrationMode(tt.migrationMode) } // Setup processors tt.setupProcessors(executor, ctrl) // Start the executor ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() executor.shardCleanUpLoop(ctx) }() // For invalid ttlShard, the loop returns immediately if tt.ttlShard <= 0 { cancel() wg.Wait() } else { // Wait for timer to be set mockTimeSource.BlockUntil(1) // Advance time to trigger cleanup mockTimeSource.Advance(tt.advanceTime) // Stop the loop before waiting for next timer cancel() wg.Wait() } // Verify expected shards are deleted for _, shardID := range tt.expectedShardsDeleted { _, ok := executor.managedProcessors.Load(shardID) assert.False(t, ok, "shard %s should be deleted", shardID) _, ok = executor.processorsToLastUse.Load(shardID) assert.False(t, ok, "shard %s should be removed from processorsToLastUse", shardID) } // Verify expected shards are kept for _, shardID := range tt.expectedShardsKept { _, ok := executor.managedProcessors.Load(shardID) assert.True(t, ok, "shard %s should be kept", shardID) } }) } } func TestShardCleanupLoop_StopsOnContextCancel(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockTimeSource := clock.NewMockedTimeSource() executor := newTestExecutor(nil, nil, mockTimeSource) executor.ttlShard = 10 * time.Second processor := NewMockShardProcessor(ctrl) executor.managedProcessors.Store("shard-1", newManagedProcessor(processor, processorStateStarted)) executor.processorsToLastUse.Store("shard-1", executor.timeSource.Now()) ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) go func() { executor.shardCleanUpLoop(ctx) close(done) }() // Wait for timer mockTimeSource.BlockUntil(1) // Cancel context cancel() // Wait for loop to exit <-done } func TestShardCleanupLoop_StopsOnStopChannel(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockTimeSource := clock.NewMockedTimeSource() executor := newTestExecutor(nil, nil, mockTimeSource) executor.ttlShard = 10 * time.Second processor := NewMockShardProcessor(ctrl) executor.managedProcessors.Store("shard-1", newManagedProcessor(processor, processorStateStarted)) executor.processorsToLastUse.Store("shard-1", executor.timeSource.Now()) ctx := context.Background() done := make(chan struct{}) go func() { executor.shardCleanUpLoop(ctx) close(done) }() // Wait for timer mockTimeSource.BlockUntil(1) // Close stop channel close(executor.stopC) // Wait for loop to exit <-done } func TestHeartbeatLoop_DrainSignal(t *testing.T) { tests := []struct { name string setup func( t *testing.T, ctrl *gomock.Controller, executor *executorImpl[*MockShardProcessor], observer *closeDrainObserver, mockTimeSource clock.MockedTimeSource, mockClient *sharddistributorexecutor.MockClient, ) }{ { name: "drain stops processors and sends draining heartbeat", setup: func( t *testing.T, ctrl *gomock.Controller, executor *executorImpl[*MockShardProcessor], observer *closeDrainObserver, mockTimeSource clock.MockedTimeSource, mockClient *sharddistributorexecutor.MockClient, ) { processor := NewMockShardProcessor(ctrl) processor.EXPECT().GetShardReport().Return(ShardReport{Status: types.ShardStatusREADY}).AnyTimes() processor.EXPECT().Stop() executor.managedProcessors.Store("shard-1", newManagedProcessor(processor, processorStateStarted)) mockClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{ "shard-1": {Status: types.AssignmentStatusREADY}, }, MigrationMode: types.MigrationModeONBOARDED, }, nil) expectDrainingHeartbeat(t, mockClient) ctx, cancel := context.WithCancel(context.Background()) defer cancel() done := make(chan struct{}) go func() { executor.heartbeatloop(ctx) close(done) }() // Trigger a heartbeat mockTimeSource.BlockUntil(1) mockTimeSource.Advance(15 * time.Second) time.Sleep(10 * time.Millisecond) // Signal drain observer.SignalDrain() time.Sleep(50 * time.Millisecond) _, ok := executor.managedProcessors.Load("shard-1") assert.False(t, ok, "shard processor should be stopped after drain") cancel() <-done }, }, { name: "drain then undrain resumes heartbeating", setup: func( t *testing.T, ctrl *gomock.Controller, executor *executorImpl[*MockShardProcessor], observer *closeDrainObserver, mockTimeSource clock.MockedTimeSource, mockClient *sharddistributorexecutor.MockClient, ) { processor := NewMockShardProcessor(ctrl) processor.EXPECT().GetShardReport().Return(ShardReport{Status: types.ShardStatusREADY}).AnyTimes() processor.EXPECT().Stop() executor.managedProcessors.Store("shard-1", newManagedProcessor(processor, processorStateStarted)) // Phase 1: active heartbeat, then draining heartbeat on drain // Phase 2: active heartbeat after undrain, then draining heartbeat on stop activeHB := mockClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{ "shard-1": {Status: types.AssignmentStatusREADY}, }, MigrationMode: types.MigrationModeONBOARDED, }, nil) drainingHB := mockClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *types.ExecutorHeartbeatRequest, _ ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) { assert.Equal(t, types.ExecutorStatusDRAINING, req.Status) return &types.ExecutorHeartbeatResponse{}, nil }).After(activeHB) resumedHB := mockClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *types.ExecutorHeartbeatRequest, _ ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) { assert.Equal(t, types.ExecutorStatusACTIVE, req.Status) return &types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{}, MigrationMode: types.MigrationModeONBOARDED, }, nil }).After(drainingHB) mockClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *types.ExecutorHeartbeatRequest, _ ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) { assert.Equal(t, types.ExecutorStatusDRAINING, req.Status) return &types.ExecutorHeartbeatResponse{}, nil }).After(resumedHB) ctx, cancel := context.WithCancel(context.Background()) defer cancel() done := make(chan struct{}) go func() { executor.heartbeatloop(ctx) close(done) }() // Phase 1: heartbeat, then drain mockTimeSource.BlockUntil(1) mockTimeSource.Advance(15 * time.Second) time.Sleep(10 * time.Millisecond) observer.SignalDrain() time.Sleep(50 * time.Millisecond) // Phase 2: undrain resumes, heartbeat again observer.SignalUndrain() time.Sleep(50 * time.Millisecond) mockTimeSource.BlockUntil(1) mockTimeSource.Advance(15 * time.Second) time.Sleep(10 * time.Millisecond) cancel() <-done }, }, { name: "drain -> undrain -> drain -> context cancelled", setup: func( t *testing.T, ctrl *gomock.Controller, executor *executorImpl[*MockShardProcessor], observer *closeDrainObserver, mockTimeSource clock.MockedTimeSource, mockClient *sharddistributorexecutor.MockClient, ) { // Heartbeat sequence: // 1. ACTIVE (initial heartbeat) // 2. DRAINING (drain #1) // 3. ACTIVE (after undrain #1) // 4. DRAINING (drain #2) // 5. DRAINING (context cancelled while waiting for undrain #2) activeHB1 := mockClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). Return(&types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{}, MigrationMode: types.MigrationModeONBOARDED, }, nil) drainingHB1 := mockClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *types.ExecutorHeartbeatRequest, _ ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) { assert.Equal(t, types.ExecutorStatusDRAINING, req.Status) return &types.ExecutorHeartbeatResponse{}, nil }).After(activeHB1) activeHB2 := mockClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *types.ExecutorHeartbeatRequest, _ ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) { assert.Equal(t, types.ExecutorStatusACTIVE, req.Status) return &types.ExecutorHeartbeatResponse{ ShardAssignments: map[string]*types.ShardAssignment{}, MigrationMode: types.MigrationModeONBOARDED, }, nil }).After(drainingHB1) mockClient.EXPECT().Heartbeat(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *types.ExecutorHeartbeatRequest, _ ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) { assert.Equal(t, types.ExecutorStatusDRAINING, req.Status) return &types.ExecutorHeartbeatResponse{}, nil }).After(activeHB2) ctx, cancel := context.WithCancel(context.Background()) defer cancel() done := make(chan struct{}) go func() { executor.heartbeatloop(ctx) close(done) }() // Initial heartbeat mockTimeSource.BlockUntil(1) mockTimeSource.Advance(15 * time.Second) time.Sleep(10 * time.Millisecond) // Drain #1 observer.SignalDrain() time.Sleep(50 * time.Millisecond) // Undrain #1 - resumes heartbeating observer.SignalUndrain() time.Sleep(50 * time.Millisecond) mockTimeSource.BlockUntil(1) mockTimeSource.Advance(15 * time.Second) time.Sleep(10 * time.Millisecond) // Drain #2 observer.SignalDrain() time.Sleep(50 * time.Millisecond) // Cancel while waiting for undrain #2 cancel() <-done }, }, { name: "drain then executor stops", setup: func( t *testing.T, ctrl *gomock.Controller, executor *executorImpl[*MockShardProcessor], observer *closeDrainObserver, mockTimeSource clock.MockedTimeSource, mockClient *sharddistributorexecutor.MockClient, ) { expectDrainingHeartbeat(t, mockClient) done := make(chan struct{}) go func() { executor.heartbeatloop(context.Background()) close(done) }() mockTimeSource.BlockUntil(1) observer.SignalDrain() time.Sleep(50 * time.Millisecond) close(executor.stopC) <-done }, }, { name: "drain then context cancelled", setup: func( t *testing.T, ctrl *gomock.Controller, executor *executorImpl[*MockShardProcessor], observer *closeDrainObserver, mockTimeSource clock.MockedTimeSource, mockClient *sharddistributorexecutor.MockClient, ) { expectDrainingHeartbeat(t, mockClient) ctx, cancel := context.WithCancel(context.Background()) defer cancel() done := make(chan struct{}) go func() { executor.heartbeatloop(ctx) close(done) }() mockTimeSource.BlockUntil(1) observer.SignalDrain() time.Sleep(50 * time.Millisecond) cancel() <-done }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockClient := sharddistributorexecutor.NewMockClient(ctrl) mockTimeSource := clock.NewMockedTimeSource() observer := newCloseDrainObserver() executor := newTestExecutor(mockClient, nil, mockTimeSource) executor.drainObserver = observer tt.setup(t, ctrl, executor, observer, mockTimeSource, mockClient) }) } } ================================================ FILE: service/sharddistributor/client/executorclient/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: client.go // // Generated by this command: // // mockgen -package executorclient -source client.go -destination interface_mock.go . ShardProcessorFactory,ShardProcessor,Executor,Client // // Package executorclient is a generated GoMock package. package executorclient import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" types "github.com/uber/cadence/common/types" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // Heartbeat mocks base method. func (m *MockClient) Heartbeat(arg0 context.Context, arg1 *types.ExecutorHeartbeatRequest, arg2 ...yarpc.CallOption) (*types.ExecutorHeartbeatResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Heartbeat", varargs...) ret0, _ := ret[0].(*types.ExecutorHeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Heartbeat indicates an expected call of Heartbeat. func (mr *MockClientMockRecorder) Heartbeat(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Heartbeat", reflect.TypeOf((*MockClient)(nil).Heartbeat), varargs...) } // MockShardProcessor is a mock of ShardProcessor interface. type MockShardProcessor struct { ctrl *gomock.Controller recorder *MockShardProcessorMockRecorder isgomock struct{} } // MockShardProcessorMockRecorder is the mock recorder for MockShardProcessor. type MockShardProcessorMockRecorder struct { mock *MockShardProcessor } // NewMockShardProcessor creates a new mock instance. func NewMockShardProcessor(ctrl *gomock.Controller) *MockShardProcessor { mock := &MockShardProcessor{ctrl: ctrl} mock.recorder = &MockShardProcessorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShardProcessor) EXPECT() *MockShardProcessorMockRecorder { return m.recorder } // GetShardReport mocks base method. func (m *MockShardProcessor) GetShardReport() ShardReport { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardReport") ret0, _ := ret[0].(ShardReport) return ret0 } // GetShardReport indicates an expected call of GetShardReport. func (mr *MockShardProcessorMockRecorder) GetShardReport() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardReport", reflect.TypeOf((*MockShardProcessor)(nil).GetShardReport)) } // SetShardStatus mocks base method. func (m *MockShardProcessor) SetShardStatus(arg0 types.ShardStatus) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetShardStatus", arg0) } // SetShardStatus indicates an expected call of SetShardStatus. func (mr *MockShardProcessorMockRecorder) SetShardStatus(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetShardStatus", reflect.TypeOf((*MockShardProcessor)(nil).SetShardStatus), arg0) } // Start mocks base method. func (m *MockShardProcessor) Start(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start", ctx) ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockShardProcessorMockRecorder) Start(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockShardProcessor)(nil).Start), ctx) } // Stop mocks base method. func (m *MockShardProcessor) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockShardProcessorMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockShardProcessor)(nil).Stop)) } // MockShardProcessorFactory is a mock of ShardProcessorFactory interface. type MockShardProcessorFactory[SP ShardProcessor] struct { ctrl *gomock.Controller recorder *MockShardProcessorFactoryMockRecorder[SP] isgomock struct{} } // MockShardProcessorFactoryMockRecorder is the mock recorder for MockShardProcessorFactory. type MockShardProcessorFactoryMockRecorder[SP ShardProcessor] struct { mock *MockShardProcessorFactory[SP] } // NewMockShardProcessorFactory creates a new mock instance. func NewMockShardProcessorFactory[SP ShardProcessor](ctrl *gomock.Controller) *MockShardProcessorFactory[SP] { mock := &MockShardProcessorFactory[SP]{ctrl: ctrl} mock.recorder = &MockShardProcessorFactoryMockRecorder[SP]{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShardProcessorFactory[SP]) EXPECT() *MockShardProcessorFactoryMockRecorder[SP] { return m.recorder } // NewShardProcessor mocks base method. func (m *MockShardProcessorFactory[SP]) NewShardProcessor(shardID string) (SP, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NewShardProcessor", shardID) ret0, _ := ret[0].(SP) ret1, _ := ret[1].(error) return ret0, ret1 } // NewShardProcessor indicates an expected call of NewShardProcessor. func (mr *MockShardProcessorFactoryMockRecorder[SP]) NewShardProcessor(shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewShardProcessor", reflect.TypeOf((*MockShardProcessorFactory[SP])(nil).NewShardProcessor), shardID) } // MockExecutor is a mock of Executor interface. type MockExecutor[SP ShardProcessor] struct { ctrl *gomock.Controller recorder *MockExecutorMockRecorder[SP] isgomock struct{} } // MockExecutorMockRecorder is the mock recorder for MockExecutor. type MockExecutorMockRecorder[SP ShardProcessor] struct { mock *MockExecutor[SP] } // NewMockExecutor creates a new mock instance. func NewMockExecutor[SP ShardProcessor](ctrl *gomock.Controller) *MockExecutor[SP] { mock := &MockExecutor[SP]{ctrl: ctrl} mock.recorder = &MockExecutorMockRecorder[SP]{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockExecutor[SP]) EXPECT() *MockExecutorMockRecorder[SP] { return m.recorder } // AssignShardsFromLocalLogic mocks base method. func (m *MockExecutor[SP]) AssignShardsFromLocalLogic(ctx context.Context, shardAssignment map[string]*types.ShardAssignment) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AssignShardsFromLocalLogic", ctx, shardAssignment) ret0, _ := ret[0].(error) return ret0 } // AssignShardsFromLocalLogic indicates an expected call of AssignShardsFromLocalLogic. func (mr *MockExecutorMockRecorder[SP]) AssignShardsFromLocalLogic(ctx, shardAssignment any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssignShardsFromLocalLogic", reflect.TypeOf((*MockExecutor[SP])(nil).AssignShardsFromLocalLogic), ctx, shardAssignment) } // GetMetadata mocks base method. func (m *MockExecutor[SP]) GetMetadata() map[string]string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMetadata") ret0, _ := ret[0].(map[string]string) return ret0 } // GetMetadata indicates an expected call of GetMetadata. func (mr *MockExecutorMockRecorder[SP]) GetMetadata() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetadata", reflect.TypeOf((*MockExecutor[SP])(nil).GetMetadata)) } // GetNamespace mocks base method. func (m *MockExecutor[SP]) GetNamespace() string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNamespace") ret0, _ := ret[0].(string) return ret0 } // GetNamespace indicates an expected call of GetNamespace. func (mr *MockExecutorMockRecorder[SP]) GetNamespace() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNamespace", reflect.TypeOf((*MockExecutor[SP])(nil).GetNamespace)) } // GetShardProcess mocks base method. func (m *MockExecutor[SP]) GetShardProcess(ctx context.Context, shardID string) (SP, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardProcess", ctx, shardID) ret0, _ := ret[0].(SP) ret1, _ := ret[1].(error) return ret0, ret1 } // GetShardProcess indicates an expected call of GetShardProcess. func (mr *MockExecutorMockRecorder[SP]) GetShardProcess(ctx, shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardProcess", reflect.TypeOf((*MockExecutor[SP])(nil).GetShardProcess), ctx, shardID) } // IsOnboardedToSD mocks base method. func (m *MockExecutor[SP]) IsOnboardedToSD() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsOnboardedToSD") ret0, _ := ret[0].(bool) return ret0 } // IsOnboardedToSD indicates an expected call of IsOnboardedToSD. func (mr *MockExecutorMockRecorder[SP]) IsOnboardedToSD() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOnboardedToSD", reflect.TypeOf((*MockExecutor[SP])(nil).IsOnboardedToSD)) } // RemoveShardsFromLocalLogic mocks base method. func (m *MockExecutor[SP]) RemoveShardsFromLocalLogic(shardIDs []string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoveShardsFromLocalLogic", shardIDs) ret0, _ := ret[0].(error) return ret0 } // RemoveShardsFromLocalLogic indicates an expected call of RemoveShardsFromLocalLogic. func (mr *MockExecutorMockRecorder[SP]) RemoveShardsFromLocalLogic(shardIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveShardsFromLocalLogic", reflect.TypeOf((*MockExecutor[SP])(nil).RemoveShardsFromLocalLogic), shardIDs) } // SetMetadata mocks base method. func (m *MockExecutor[SP]) SetMetadata(metadata map[string]string) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetMetadata", metadata) } // SetMetadata indicates an expected call of SetMetadata. func (mr *MockExecutorMockRecorder[SP]) SetMetadata(metadata any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMetadata", reflect.TypeOf((*MockExecutor[SP])(nil).SetMetadata), metadata) } // Start mocks base method. func (m *MockExecutor[SP]) Start(ctx context.Context) { m.ctrl.T.Helper() m.ctrl.Call(m, "Start", ctx) } // Start indicates an expected call of Start. func (mr *MockExecutorMockRecorder[SP]) Start(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockExecutor[SP])(nil).Start), ctx) } // Stop mocks base method. func (m *MockExecutor[SP]) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockExecutorMockRecorder[SP]) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockExecutor[SP])(nil).Stop)) } ================================================ FILE: service/sharddistributor/client/executorclient/meteredClientDecorater.go ================================================ package executorclient import ( "context" "github.com/uber-go/tally" "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributorexecutor" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/executorclient/metricsconstants" ) // TODO: consider using gowrap to generate this code type meteredShardDistributorExecutorClient struct { client sharddistributorexecutor.Client metricsScope tally.Scope } // NewShardDistributorExecutorClient creates a new instance of sharddistributorexecutorClient with retry policy func NewMeteredShardDistributorExecutorClient(client sharddistributorexecutor.Client, metricsScope tally.Scope) sharddistributorexecutor.Client { return &meteredShardDistributorExecutorClient{ client: client, metricsScope: metricsScope, } } func (c *meteredShardDistributorExecutorClient) Heartbeat(ctx context.Context, ep1 *types.ExecutorHeartbeatRequest, p1 ...yarpc.CallOption) (ep2 *types.ExecutorHeartbeatResponse, err error) { var scope tally.Scope scope = c.metricsScope.Tagged(map[string]string{ metrics.OperationTagName: metricsconstants.ShardDistributorExecutorHeartbeatOperationTagName, }) scope.Counter(metricsconstants.ShardDistributorExecutorClientRequests).Inc(1) sw := scope.Timer(metricsconstants.ShardDistributorExecutorClientLatency).Start() ep2, err = c.client.Heartbeat(ctx, ep1, p1...) sw.Stop() if err != nil { scope.Counter(metricsconstants.ShardDistributorExecutorClientFailures).Inc(1) } return ep2, err } ================================================ FILE: service/sharddistributor/client/executorclient/metricsconstants/metrics.go ================================================ package metricsconstants import ( "time" "github.com/uber-go/tally" ) const ( // Operation tag names for ShardDistributorExecutor metrics ShardDistributorExecutorOperationTagName = "ShardDistributorExecutor" ShardDistributorExecutorHeartbeatOperationTagName = "ShardDistributorExecutorHeartbeat" // Counter metrics ShardDistributorExecutorHeartbeatSkipped = "shard_distributor_executor_heartbeat_skipped" ShardDistributorExecutorShardsStarted = "shard_distributor_executor_shards_started" ShardDistributorExecutorShardsStopped = "shard_distributor_executor_shards_stopped" ShardDistributorExecutorProcessorCreationFailures = "shard_distributor_executor_processor_creation_failures" ShardDistributorExecutorClientRequests = "shard_distributor_executor_client_requests" ShardDistributorExecutorClientFailures = "shard_distributor_executor_client_failures" ShardDistributorExecutorAssignmentDivergence = "shard_distributor_executor_assignment_divergence" ShardDistributorExecutorAssignmentConvergence = "shard_distributor_executor_assignment_convergence" // Gauge metrics ShardDistributorExecutorOwnedShards = "shard_distributor_executor_owned_shards" // Histogram/Timer metrics ShardDistributorExecutorAssignLoopLatency = "shard_distributor_executor_assign_loop_latency" ShardDistributorExecutorClientLatency = "shard_distributor_executor_client_latency" ) var ( // Histogram buckets for ShardDistributorExecutor metrics ShardDistributorExecutorAssignLoopLatencyBuckets = tally.DurationBuckets([]time.Duration{ 0, 1 * time.Millisecond, 5 * time.Millisecond, 10 * time.Millisecond, 25 * time.Millisecond, 50 * time.Millisecond, 100 * time.Millisecond, 200 * time.Millisecond, 500 * time.Millisecond, 1 * time.Second, 2 * time.Second, 5 * time.Second, 10 * time.Second, 20 * time.Second, 60 * time.Second, }) ) ================================================ FILE: service/sharddistributor/client/executorclient/noop.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package executorclient import ( "context" "github.com/uber/cadence/common/types" ) // noopExecutor is an Executor implementation used when no shard-distributor // config is provided. It acts as if all task lists are locally assigned // (local-passthrough behaviour): it never contacts the shard distributor, // never manages shard processors, and reports itself as not onboarded to SD. type noopExecutor[SP ShardProcessor] struct{} // NewNoopExecutor returns an Executor that is a no-op. Use this when the // shard-distributor config is absent so that the rest of the matching engine // can operate using the local hash-ring exclusively. func NewNoopExecutor[SP ShardProcessor]() Executor[SP] { return &noopExecutor[SP]{} } func (e *noopExecutor[SP]) Start(_ context.Context) {} func (e *noopExecutor[SP]) Stop() {} func (e *noopExecutor[SP]) GetNamespace() string { return "" } func (e *noopExecutor[SP]) IsOnboardedToSD() bool { return false } func (e *noopExecutor[SP]) SetMetadata(_ map[string]string) {} func (e *noopExecutor[SP]) GetMetadata() map[string]string { return nil } // GetShardProcess always returns ErrShardProcessNotFound so that callers // fall back to local hash-ring ownership checks. func (e *noopExecutor[SP]) GetShardProcess(_ context.Context, _ string) (SP, error) { var zero SP return zero, ErrShardProcessNotFound } // AssignShardsFromLocalLogic is a no-op: without SD there is no shard // assignment to manage. func (e *noopExecutor[SP]) AssignShardsFromLocalLogic(_ context.Context, _ map[string]*types.ShardAssignment) error { return nil } // RemoveShardsFromLocalLogic is a no-op: without SD there is no shard // assignment to manage. func (e *noopExecutor[SP]) RemoveShardsFromLocalLogic(_ []string) error { return nil } ================================================ FILE: service/sharddistributor/client/executorclient/noop_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package executorclient import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" ) func TestNoopExecutor(t *testing.T) { exec := NewNoopExecutor[*MockShardProcessor]() t.Run("Start and Stop are no-ops", func(t *testing.T) { // should not panic exec.Start(context.Background()) exec.Stop() }) t.Run("GetNamespace returns empty string", func(t *testing.T) { assert.Equal(t, "", exec.GetNamespace()) }) t.Run("IsOnboardedToSD returns false", func(t *testing.T) { assert.False(t, exec.IsOnboardedToSD()) }) t.Run("SetMetadata and GetMetadata are no-ops", func(t *testing.T) { exec.SetMetadata(map[string]string{"key": "value"}) assert.Nil(t, exec.GetMetadata()) }) t.Run("GetShardProcess returns ErrShardProcessNotFound", func(t *testing.T) { sp, err := exec.GetShardProcess(context.Background(), "any-shard") assert.Nil(t, sp) assert.ErrorIs(t, err, ErrShardProcessNotFound) }) t.Run("AssignShardsFromLocalLogic is a no-op", func(t *testing.T) { err := exec.AssignShardsFromLocalLogic(context.Background(), map[string]*types.ShardAssignment{ "shard-1": {}, }) assert.NoError(t, err) }) t.Run("RemoveShardsFromLocalLogic is a no-op", func(t *testing.T) { err := exec.RemoveShardsFromLocalLogic([]string{"shard-1"}) assert.NoError(t, err) }) } ================================================ FILE: service/sharddistributor/client/executorclient/syncgeneric/map.go ================================================ package syncgeneric import "sync" // Map is a wrapper around sync.Map that provides a more convenient, generic, // and type-safe API. type Map[K comparable, V any] struct { contents sync.Map } // Load returns the value stored in the map for a key, or nil if no // value is present. // The ok result indicates whether value was found in the map. func (m *Map[K, V]) Load(key K) (V, bool) { val, ok := m.contents.Load(key) if !ok { var zero V return zero, false } return val.(V), true } // Store sets the value for a key. func (m *Map[K, V]) Store(key K, value V) { m.contents.Store(key, value) } // Delete deletes the value for a key. func (m *Map[K, V]) Delete(key K) { m.contents.Delete(key) } // Range calls f sequentially for each key and value present in the map. // If f returns false, range stops the iteration. // // Range does not necessarily correspond to any consistent snapshot of the Map's // contents: no key will be visited more than once, but if the value for any key // is stored or deleted concurrently (including by f), Range may reflect any // mapping for that key from any point during the Range call. Range does not // block other methods on the receiver; even f itself may call any method on m. // // Range may be O(N) with the number of elements in the map even if f returns // false after a constant number of calls. func (m *Map[K, V]) Range(f func(key K, value V) bool) { m.contents.Range(func(key, value interface{}) bool { return f(key.(K), value.(V)) }) } ================================================ FILE: service/sharddistributor/client/executorclient/syncgeneric/map_test.go ================================================ package syncgeneric import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) type testKey struct { str string i int } type testValue struct { m map[string]string } var ( testKey1 = &testKey{str: "key1", i: 1} testKey2 = &testKey{str: "key2", i: 2} testValue1 = &testValue{m: map[string]string{"key1": "value1"}} testValue2 = &testValue{m: map[string]string{"key2": "value2"}} ) func testMap(t *testing.T) *Map[*testKey, *testValue] { // Create the type-safe map m := &Map[*testKey, *testValue]{} // Insert some data into the underlying sync.Map m.contents.Store(testKey1, testValue1) m.contents.Store(testKey2, testValue2) loaded, ok := m.contents.Load(testKey1) require.True(t, ok) require.Equal(t, testValue1, loaded) loaded, ok = m.contents.Load(testKey2) require.True(t, ok) require.Equal(t, testValue2, loaded) return m } func TestStoreAndLoad(t *testing.T) { cases := map[string]struct { key *testKey value *testValue }{ "new key": {key: &testKey{str: "key3", i: 1}, value: &testValue{m: map[string]string{"key3": "value3"}}}, "existing key": {key: testKey1, value: testValue1}, "nil key": {key: nil, value: &testValue{m: map[string]string{"key1": "value1-updated"}}}, "nil value": {key: &testKey{str: "key1", i: 1}, value: nil}, } for name, c := range cases { t.Run(name, func(t *testing.T) { m := testMap(t) m.Store(c.key, c.value) loaded, ok := m.Load(c.key) assert.True(t, ok) assert.Equal(t, c.value, loaded) }) } } func TestLoad(t *testing.T) { t.Run("load existing key", func(t *testing.T) { m := testMap(t) loaded, ok := m.Load(testKey1) assert.True(t, ok) assert.Equal(t, testValue1, loaded) }) t.Run("load non-existing key", func(t *testing.T) { m := testMap(t) loaded, ok := m.Load(&testKey{str: "key3", i: 1}) assert.False(t, ok) assert.Nil(t, loaded) }) t.Run("load non-existing key with zero value", func(t *testing.T) { m := Map[int, string]{} loaded, ok := m.Load(0) assert.False(t, ok) // Should return the zero value of the type assert.Equal(t, "", loaded) }) t.Run("load non-existing key with other zero value", func(t *testing.T) { type s struct { _ string _ int _ *int } m := Map[int, s]{} loaded, ok := m.Load(0) assert.False(t, ok) assert.Equal(t, s{}, loaded) }) } func TestDelete(t *testing.T) { m := testMap(t) // testKey1 is in the map loaded, ok := m.Load(testKey1) assert.True(t, ok) assert.Equal(t, testValue1, loaded) // delete testKey1 m.Delete(testKey1) // testKey1 is no longer in the map loaded, ok = m.Load(testKey1) assert.False(t, ok) assert.Nil(t, loaded) } func TestRange(t *testing.T) { m := testMap(t) // We have 2 keys in the map count := 0 m.Range(func(key *testKey, value *testValue) bool { count++ return true }) assert.Equal(t, 2, count) } ================================================ FILE: service/sharddistributor/client/executorclient/yarpc_client_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/.gen/proto/sharddistributor/v1 (interfaces: ShardDistributorExecutorAPIYARPCClient) // // Generated by this command: // // mockgen -package executorclient -destination yarpc_client_mock.go github.com/uber/cadence/.gen/proto/sharddistributor/v1 ShardDistributorExecutorAPIYARPCClient // // Package executorclient is a generated GoMock package. package executorclient import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" yarpc "go.uber.org/yarpc" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" ) // MockShardDistributorExecutorAPIYARPCClient is a mock of ShardDistributorExecutorAPIYARPCClient interface. type MockShardDistributorExecutorAPIYARPCClient struct { ctrl *gomock.Controller recorder *MockShardDistributorExecutorAPIYARPCClientMockRecorder isgomock struct{} } // MockShardDistributorExecutorAPIYARPCClientMockRecorder is the mock recorder for MockShardDistributorExecutorAPIYARPCClient. type MockShardDistributorExecutorAPIYARPCClientMockRecorder struct { mock *MockShardDistributorExecutorAPIYARPCClient } // NewMockShardDistributorExecutorAPIYARPCClient creates a new mock instance. func NewMockShardDistributorExecutorAPIYARPCClient(ctrl *gomock.Controller) *MockShardDistributorExecutorAPIYARPCClient { mock := &MockShardDistributorExecutorAPIYARPCClient{ctrl: ctrl} mock.recorder = &MockShardDistributorExecutorAPIYARPCClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShardDistributorExecutorAPIYARPCClient) EXPECT() *MockShardDistributorExecutorAPIYARPCClientMockRecorder { return m.recorder } // Heartbeat mocks base method. func (m *MockShardDistributorExecutorAPIYARPCClient) Heartbeat(arg0 context.Context, arg1 *sharddistributorv1.HeartbeatRequest, arg2 ...yarpc.CallOption) (*sharddistributorv1.HeartbeatResponse, error) { m.ctrl.T.Helper() varargs := []any{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Heartbeat", varargs...) ret0, _ := ret[0].(*sharddistributorv1.HeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Heartbeat indicates an expected call of Heartbeat. func (mr *MockShardDistributorExecutorAPIYARPCClientMockRecorder) Heartbeat(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Heartbeat", reflect.TypeOf((*MockShardDistributorExecutorAPIYARPCClient)(nil).Heartbeat), varargs...) } ================================================ FILE: service/sharddistributor/client/spectatorclient/client.go ================================================ package spectatorclient import ( "context" "fmt" "github.com/uber-go/tally" "go.uber.org/fx" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" csync "github.com/uber/cadence/service/sharddistributor/client/spectatorclient/sync" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interface_mock.go . Spectator // EnabledFunc is a function that returns true if the spectator is enabled // This is used to disable the spectator in the case of a migration type EnabledFunc func() bool type Spectators struct { spectators map[string]Spectator } func (s *Spectators) ForNamespace(namespace string) (Spectator, error) { spectator, ok := s.spectators[namespace] if !ok { return nil, fmt.Errorf("spectator not found for namespace %s", namespace) } return spectator, nil } func (s *Spectators) Start(ctx context.Context) error { for namespace, spectator := range s.spectators { if err := spectator.Start(ctx); err != nil { return fmt.Errorf("start spectator for namespace %s: %w", namespace, err) } } return nil } func (s *Spectators) Stop() { for _, spectator := range s.spectators { spectator.Stop() } } func NewSpectators(params Params) (*Spectators, error) { spectators := make(map[string]Spectator) for _, namespace := range params.Config.Namespaces { spectator, err := NewSpectatorWithNamespace(params, namespace.Namespace) if err != nil { return nil, fmt.Errorf("create spectator for namespace %s: %w", namespace.Namespace, err) } spectators[namespace.Namespace] = spectator } return &Spectators{spectators: spectators}, nil } type Spectator interface { Start(ctx context.Context) error Stop() // GetShardOwner returns the owner of a shard GetShardOwner(ctx context.Context, shardKey string) (*ShardOwner, error) } type Params struct { fx.In Client sharddistributor.Client MetricsScope tally.Scope Logger log.Logger Config clientcommon.Config TimeSource clock.TimeSource Enabled EnabledFunc `optional:"true"` } // NewSpectatorWithNamespace creates a spectator for a specific namespace func NewSpectatorWithNamespace(params Params, namespace string) (Spectator, error) { return newSpectatorImpl(params, namespace) } // NewSpectator creates a spectator for the single namespace in config func NewSpectator(params Params) (Spectator, error) { cfg, err := params.Config.GetSingleConfig() if err != nil { return nil, err } return newSpectatorImpl(params, cfg.Namespace) } func newSpectatorImpl(params Params, namespace string) (Spectator, error) { // Get config for the specified namespace namespaceConfig, err := params.Config.GetConfigForNamespace(namespace) if err != nil { return nil, fmt.Errorf("get config for namespace %s: %w", namespace, err) } return newSpectatorWithConfig(params, namespaceConfig) } func newSpectatorWithConfig(params Params, namespaceConfig *clientcommon.NamespaceConfig) (Spectator, error) { enabled := params.Enabled if enabled == nil { enabled = func() bool { return true } } impl := &spectatorImpl{ namespace: namespaceConfig.Namespace, config: *namespaceConfig, client: params.Client, logger: params.Logger, scope: params.MetricsScope, timeSource: params.TimeSource, firstStateSignal: csync.NewResettableSignal(), enabled: enabled, } return impl, nil } // Module creates a spectator module using auto-selection (single namespace only) func Module() fx.Option { return fx.Module("shard-distributor-spectator-client", fx.Provide(NewSpectators), fx.Invoke(func(spectators *Spectators, lc fx.Lifecycle) { lc.Append(fx.StartStopHook(spectators.Start, spectators.Stop)) }), ) } ================================================ FILE: service/sharddistributor/client/spectatorclient/clientimpl.go ================================================ package spectatorclient import ( "context" "fmt" "sync" "time" "github.com/uber-go/tally" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" csync "github.com/uber/cadence/service/sharddistributor/client/spectatorclient/sync" ) // stateFn represents a state in the election state machine. // Each state is a function that blocks until a transition occurs // and returns the next state function, or nil to stop. // Note this is a recursive type definition. type stateFn func(ctx context.Context) stateFn const ( streamRetryInterval = 1 * time.Second streamRetryJitterCoeff = 0.1 // 10% jitter (900ms - 1100ms) ) // ShardOwner contains information about the executor that owns a shard type ShardOwner struct { ExecutorID string Metadata map[string]string } type spectatorImpl struct { namespace string enabled EnabledFunc config clientcommon.NamespaceConfig client sharddistributor.Client scope tally.Scope logger log.Logger timeSource clock.TimeSource stream sharddistributor.WatchNamespaceStateClient cancel context.CancelFunc stopWG sync.WaitGroup // State storage with lock for thread-safe access // Map from shard ID to shard owner (executor ID + metadata) stateMu sync.RWMutex shardToOwner map[string]*ShardOwner // Signal to notify when first state is received firstStateSignal *csync.ResettableSignal } func (s *spectatorImpl) Start(ctx context.Context) error { // Create a cancellable context for the lifetime of the spectator // Use context.WithoutCancel to inherit values but not cancellation from fx lifecycle ctx ctx, s.cancel = context.WithCancel(context.WithoutCancel(ctx)) s.stopWG.Add(1) go func() { defer s.stopWG.Done() s.watchLoop(ctx) }() return nil } func (s *spectatorImpl) Stop() { if s.cancel != nil { s.cancel() } // Close the firstStateSignal to unblock any goroutines waiting for first state s.firstStateSignal.Done() s.stopWG.Wait() } func (s *spectatorImpl) watchLoop(ctx context.Context) { defer s.logger.Info("Shutting down, stopping watch loop", tag.ShardNamespace(s.namespace)) s.logger.Info("Starting watch loop for namespace", tag.ShardNamespace(s.namespace)) var state stateFn if s.enabled() { state = s.connectState } else { state = s.disabledState } for state != nil { state = state(ctx) } } func (s *spectatorImpl) connectState(ctx context.Context) stateFn { defer s.logger.Info("Exiting connect state", tag.ShardNamespace(s.namespace)) s.logger.Info("Starting connect state for namespace", tag.ShardNamespace(s.namespace)) if !s.enabled() { return s.disabledState } stream, err := s.client.WatchNamespaceState(ctx, &types.WatchNamespaceStateRequest{ Namespace: s.namespace, }) if err != nil { if ctx.Err() != nil { return nil } s.logger.Error("Failed to create stream, retrying", tag.Error(err), tag.ShardNamespace(s.namespace)) if err := s.timeSource.SleepWithContext(ctx, backoff.JitDuration(streamRetryInterval, streamRetryJitterCoeff)); err != nil { return nil } return s.connectState } s.stream = stream return s.enabledState } func (s *spectatorImpl) enabledState(ctx context.Context) stateFn { defer s.logger.Info("Exiting enabled state", tag.ShardNamespace(s.namespace)) defer func() { if err := s.stream.CloseSend(); err != nil { s.logger.Warn("Failed to close stream", tag.Error(err), tag.ShardNamespace(s.namespace)) } }() s.logger.Info("Starting enabled state for namespace", tag.ShardNamespace(s.namespace)) for { if !s.enabled() { return s.disabledState } response, err := s.stream.Recv() if err != nil { if ctx.Err() != nil { s.logger.Info("Recv interrupted by client shutdown", tag.ShardNamespace(s.namespace)) return nil } s.logger.Warn("Stream error (server issue), will reconnect", tag.Error(err), tag.ShardNamespace(s.namespace)) if err := s.timeSource.SleepWithContext(ctx, backoff.JitDuration(streamRetryInterval, streamRetryJitterCoeff)); err != nil { return nil } return s.connectState } // Process the response s.handleResponse(response) } } func (s *spectatorImpl) disabledState(ctx context.Context) stateFn { defer s.logger.Info("Exiting disabled state", tag.ShardNamespace(s.namespace)) s.logger.Info("Starting disabled state for namespace", tag.ShardNamespace(s.namespace)) // We reset the first state signal to ensure we wait for the first state to be received when we re-enable. s.firstStateSignal.Reset() for { // Sleep for a short period of time before checking again. // If the context is cancelled, we return nil to exit the loop. if err := s.timeSource.SleepWithContext(ctx, backoff.JitDuration(streamRetryInterval, streamRetryJitterCoeff)); err != nil { return nil } if s.enabled() { return s.connectState } } } func (s *spectatorImpl) handleResponse(response *types.WatchNamespaceStateResponse) { // Build inverted map: shard ID -> shard owner (executor ID + metadata) shardToOwner := make(map[string]*ShardOwner) for _, executor := range response.Executors { owner := &ShardOwner{ ExecutorID: executor.ExecutorID, Metadata: executor.Metadata, } for _, shard := range executor.AssignedShards { shardToOwner[shard.ShardKey] = owner } } s.stateMu.Lock() s.shardToOwner = shardToOwner s.stateMu.Unlock() // Signal that first state has been received - this function is free to call // multiple times. s.firstStateSignal.Done() s.logger.Debug("Received namespace state update", tag.ShardNamespace(s.namespace), tag.Counter(len(response.Executors))) } // GetShardOwner returns the full owner information including metadata for a given shard. // It first waits for the initial state to be received, then checks the cache. // If not found in cache, it falls back to querying the shard distributor directly. func (s *spectatorImpl) GetShardOwner(ctx context.Context, shardKey string) (*ShardOwner, error) { // Wait for first state to be received to avoid flooding shard distributor on startup if err := s.firstStateSignal.Wait(ctx); err != nil { return nil, fmt.Errorf("wait for first state: %w", err) } // Check cache first s.stateMu.RLock() owner := s.shardToOwner[shardKey] s.stateMu.RUnlock() if owner != nil { return owner, nil } // Cache miss - fall back to RPC call s.logger.Debug("Shard not found in cache, querying shard distributor", tag.ShardKey(shardKey), tag.ShardNamespace(s.namespace)) response, err := s.client.GetShardOwner(ctx, &types.GetShardOwnerRequest{ Namespace: s.namespace, ShardKey: shardKey, }) if err != nil { return nil, fmt.Errorf("get shard owner from shard distributor: %w", err) } return &ShardOwner{ ExecutorID: response.Owner, Metadata: response.Metadata, }, nil } ================================================ FILE: service/sharddistributor/client/spectatorclient/clientimpl_test.go ================================================ package spectatorclient import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" csync "github.com/uber/cadence/service/sharddistributor/client/spectatorclient/sync" ) func TestWatchLoopBasicFlow(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockClient := sharddistributor.NewMockClient(ctrl) mockStream := sharddistributor.NewMockWatchNamespaceStateClient(ctrl) // Create a context to control when the mock stream should unblock streamCtx, cancelStream := context.WithCancel(context.Background()) spectator := &spectatorImpl{ namespace: "test-ns", client: mockClient, logger: log.NewNoop(), scope: tally.NoopScope, timeSource: clock.NewRealTimeSource(), firstStateSignal: csync.NewResettableSignal(), enabled: func() bool { return true }, } // Expect stream creation mockClient.EXPECT(). WatchNamespaceState(gomock.Any(), &types.WatchNamespaceStateRequest{Namespace: "test-ns"}). Return(mockStream, nil) // First Recv returns state mockStream.EXPECT().Recv().Return(&types.WatchNamespaceStateResponse{ Executors: []*types.ExecutorShardAssignment{ { ExecutorID: "executor-1", Metadata: map[string]string{ "grpc_address": "127.0.0.1:7953", }, AssignedShards: []*types.Shard{ {ShardKey: "shard-1"}, {ShardKey: "shard-2"}, }, }, }, }, nil) // Second Recv blocks until shutdown mockStream.EXPECT().Recv().DoAndReturn(func(...interface{}) (*types.WatchNamespaceStateResponse, error) { // Wait for context to be done <-streamCtx.Done() return nil, streamCtx.Err() }) mockStream.EXPECT().CloseSend().Return(nil) ctx := context.Background() err := spectator.Start(ctx) require.NoError(t, err) defer func() { cancelStream() spectator.Stop() }() // Wait for first state require.NoError(t, spectator.firstStateSignal.Wait(context.Background())) // Query shard owner owner, err := spectator.GetShardOwner(context.Background(), "shard-1") assert.NoError(t, err) assert.Equal(t, "executor-1", owner.ExecutorID) assert.Equal(t, "127.0.0.1:7953", owner.Metadata["grpc_address"]) owner, err = spectator.GetShardOwner(context.Background(), "shard-2") assert.NoError(t, err) assert.Equal(t, "executor-1", owner.ExecutorID) } func TestGetShardOwner_CacheMiss_FallbackToRPC(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockClient := sharddistributor.NewMockClient(ctrl) mockStream := sharddistributor.NewMockWatchNamespaceStateClient(ctrl) // Create a context to control when the mock stream should unblock streamCtx, cancelStream := context.WithCancel(context.Background()) spectator := &spectatorImpl{ namespace: "test-ns", client: mockClient, logger: log.NewNoop(), scope: tally.NoopScope, timeSource: clock.NewRealTimeSource(), firstStateSignal: csync.NewResettableSignal(), enabled: func() bool { return true }, } // Setup stream mockClient.EXPECT(). WatchNamespaceState(gomock.Any(), gomock.Any()). Return(mockStream, nil) // First Recv returns state mockStream.EXPECT().Recv().Return(&types.WatchNamespaceStateResponse{ Executors: []*types.ExecutorShardAssignment{ { ExecutorID: "executor-1", Metadata: map[string]string{ "grpc_address": "127.0.0.1:7953", }, AssignedShards: []*types.Shard{{ShardKey: "shard-1"}}, }, }, }, nil) // Second Recv blocks until shutdown mockStream.EXPECT().Recv().AnyTimes().DoAndReturn(func(...interface{}) (*types.WatchNamespaceStateResponse, error) { // Wait for context to be done <-streamCtx.Done() return nil, streamCtx.Err() }) mockStream.EXPECT().CloseSend().Return(nil) // Expect RPC fallback for unknown shard mockClient.EXPECT(). GetShardOwner(gomock.Any(), &types.GetShardOwnerRequest{ Namespace: "test-ns", ShardKey: "unknown-shard", }). Return(&types.GetShardOwnerResponse{ Owner: "executor-2", Metadata: map[string]string{ "grpc_address": "127.0.0.1:7954", }, }, nil) spectator.Start(context.Background()) defer func() { cancelStream() spectator.Stop() }() require.NoError(t, spectator.firstStateSignal.Wait(context.Background())) // Cache hit owner, err := spectator.GetShardOwner(context.Background(), "shard-1") assert.NoError(t, err) assert.Equal(t, "executor-1", owner.ExecutorID) // Cache miss - should trigger RPC owner, err = spectator.GetShardOwner(context.Background(), "unknown-shard") assert.NoError(t, err) assert.Equal(t, "executor-2", owner.ExecutorID) assert.Equal(t, "127.0.0.1:7954", owner.Metadata["grpc_address"]) } func TestStreamReconnection(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockClient := sharddistributor.NewMockClient(ctrl) mockStream1 := sharddistributor.NewMockWatchNamespaceStateClient(ctrl) mockStream2 := sharddistributor.NewMockWatchNamespaceStateClient(ctrl) mockTimeSource := clock.NewMockedTimeSource() // Create a context to control when the mock stream should unblock streamCtx, cancelStream := context.WithCancel(context.Background()) spectator := &spectatorImpl{ namespace: "test-ns", client: mockClient, logger: log.NewNoop(), scope: tally.NoopScope, timeSource: mockTimeSource, firstStateSignal: csync.NewResettableSignal(), enabled: func() bool { return true }, } // First stream fails immediately mockClient.EXPECT(). WatchNamespaceState(gomock.Any(), gomock.Any()). Return(mockStream1, nil) mockStream1.EXPECT().Recv().Return(nil, errors.New("network error")) mockStream1.EXPECT().CloseSend().Return(nil) // Second stream succeeds mockClient.EXPECT(). WatchNamespaceState(gomock.Any(), gomock.Any()). Return(mockStream2, nil) // First Recv returns state mockStream2.EXPECT().Recv().Return(&types.WatchNamespaceStateResponse{ Executors: []*types.ExecutorShardAssignment{{ExecutorID: "executor-1"}}, }, nil) // Second Recv blocks until shutdown mockStream2.EXPECT().Recv().AnyTimes().DoAndReturn(func(...interface{}) (*types.WatchNamespaceStateResponse, error) { // Wait for context to be done <-streamCtx.Done() return nil, errors.New("shutdown") }) mockStream2.EXPECT().CloseSend().Return(nil) spectator.Start(context.Background()) defer func() { cancelStream() spectator.Stop() }() // Wait for the goroutine to be blocked in Sleep, then advance time mockTimeSource.BlockUntil(1) // Wait for 1 goroutine to be blocked in Sleep mockTimeSource.Advance(2 * time.Second) require.NoError(t, spectator.firstStateSignal.Wait(context.Background())) } func TestGetShardOwner_TimeoutBeforeFirstState(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) mockClient := sharddistributor.NewMockClient(ctrl) spectator := &spectatorImpl{ namespace: "test-ns", client: mockClient, logger: log.NewNoop(), scope: tally.NoopScope, timeSource: clock.NewRealTimeSource(), firstStateSignal: csync.NewResettableSignal(), enabled: func() bool { return true }, } // Create a context with a short timeout ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond) defer cancel() // Try to get shard owner before first state is received // Should timeout and return an error _, err := spectator.GetShardOwner(ctx, "shard-1") assert.Error(t, err) assert.Contains(t, err.Error(), "wait for first state") } func TestWatchLoopDisabled(t *testing.T) { defer goleak.VerifyNone(t) stateSignal := csync.NewResettableSignal() timeSource := clock.NewMockedTimeSource() spectator := &spectatorImpl{ firstStateSignal: stateSignal, timeSource: timeSource, logger: log.NewNoop(), enabled: func() bool { return false }, } err := spectator.Start(context.Background()) assert.NoError(t, err) // Disabled state enters a sleep loop, verify it sleeps periodically timeSource.BlockUntil(1) timeSource.Advance(1200 * time.Millisecond) timeSource.BlockUntil(1) timeSource.Advance(1200 * time.Millisecond) // Stop exits cleanly and calls Done() on the signal spectator.Stop() // After Stop(), Done() has been called so Wait returns nil err = stateSignal.Wait(context.Background()) assert.NoError(t, err) } ================================================ FILE: service/sharddistributor/client/spectatorclient/interface_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: client.go // // Generated by this command: // // mockgen -package spectatorclient -source client.go -destination interface_mock.go . Spectator // // Package spectatorclient is a generated GoMock package. package spectatorclient import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockSpectator is a mock of Spectator interface. type MockSpectator struct { ctrl *gomock.Controller recorder *MockSpectatorMockRecorder isgomock struct{} } // MockSpectatorMockRecorder is the mock recorder for MockSpectator. type MockSpectatorMockRecorder struct { mock *MockSpectator } // NewMockSpectator creates a new mock instance. func NewMockSpectator(ctrl *gomock.Controller) *MockSpectator { mock := &MockSpectator{ctrl: ctrl} mock.recorder = &MockSpectatorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSpectator) EXPECT() *MockSpectatorMockRecorder { return m.recorder } // GetShardOwner mocks base method. func (m *MockSpectator) GetShardOwner(ctx context.Context, shardKey string) (*ShardOwner, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardOwner", ctx, shardKey) ret0, _ := ret[0].(*ShardOwner) ret1, _ := ret[1].(error) return ret0, ret1 } // GetShardOwner indicates an expected call of GetShardOwner. func (mr *MockSpectatorMockRecorder) GetShardOwner(ctx, shardKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardOwner", reflect.TypeOf((*MockSpectator)(nil).GetShardOwner), ctx, shardKey) } // Start mocks base method. func (m *MockSpectator) Start(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start", ctx) ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockSpectatorMockRecorder) Start(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockSpectator)(nil).Start), ctx) } // Stop mocks base method. func (m *MockSpectator) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockSpectatorMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockSpectator)(nil).Stop)) } ================================================ FILE: service/sharddistributor/client/spectatorclient/metricsconstants/metrics.go ================================================ package metricsconstants const ( // Operation tag names for ShardDistributorSpectator metrics ShardDistributorSpectatorOperationTagName = "ShardDistributorSpectator" ShardDistributorSpectatorGetShardOwnerOperationTagName = "ShardDistributorSpectatorGetShardOwner" ShardDistributorSpectatorWatchNamespaceStateOperationTagName = "ShardDistributorSpectatorWatchNamespaceState" // Counter metrics ShardDistributorSpectatorClientRequests = "shard_distributor_spectator_client_requests" ShardDistributorSpectatorClientFailures = "shard_distributor_spectator_client_failures" // Timer metrics ShardDistributorSpectatorClientLatency = "shard_distributor_spectator_client_latency" ) ================================================ FILE: service/sharddistributor/client/spectatorclient/peer_chooser.go ================================================ package spectatorclient import ( "context" "fmt" "sync" "time" "go.uber.org/fx" "go.uber.org/yarpc/api/peer" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/peer/hostport" "go.uber.org/yarpc/yarpcerrors" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" ) const ( NamespaceHeader = "x-shard-distributor-namespace" ) type trackedPeer struct { peer peer.Peer lastUsed time.Time } // SpectatorPeerChooserInterface extends peer.Chooser with SetSpectators method type SpectatorPeerChooserInterface interface { peer.Chooser SetSpectators(spectators *Spectators) } // SpectatorPeerChooser is a peer.Chooser that uses the Spectator to route requests // to the correct executor based on shard ownership. // This is the shard distributor equivalent of Cadence's RingpopPeerChooser. // // Flow: // 1. Client calls RPC with yarpc.WithShardKey("shard-key") // 2. Choose() is called with req.ShardKey = "shard-key" // 3. Query Spectator for shard owner // 4. Extract grpc_address from owner metadata // 5. Create/reuse peer for that address // 6. Return peer to YARPC for connection type SpectatorPeerChooser struct { spectators *Spectators transport peer.Transport logger log.Logger namespace string peersMutex sync.RWMutex peers map[string]*trackedPeer // grpc_address -> peer timeSource clock.TimeSource peerTTL time.Duration stopCh chan struct{} stopWG sync.WaitGroup } type SpectatorPeerChooserParams struct { fx.In Transport peer.Transport Logger log.Logger Config clientcommon.Config TimeSource clock.TimeSource } // NewSpectatorPeerChooser creates a new peer chooser that routes based on shard distributor ownership func NewSpectatorPeerChooser( params SpectatorPeerChooserParams, ) SpectatorPeerChooserInterface { return &SpectatorPeerChooser{ transport: params.Transport, logger: params.Logger, peers: make(map[string]*trackedPeer), peerTTL: params.Config.GetPeerTTL(), timeSource: params.TimeSource, } } // Start satisfies the peer.Chooser interface func (c *SpectatorPeerChooser) Start() error { c.logger.Info("Starting shard distributor peer chooser", tag.ShardNamespace(c.namespace)) c.startEvictionLoop() return nil } // Stop satisfies the peer.Chooser interface func (c *SpectatorPeerChooser) Stop() error { c.logger.Info("Stopping shard distributor peer chooser", tag.ShardNamespace(c.namespace)) if c.stopCh != nil { close(c.stopCh) c.stopWG.Wait() c.stopCh = nil } c.peersMutex.Lock() defer c.peersMutex.Unlock() for addr, tp := range c.peers { if err := c.transport.ReleasePeer(tp.peer, &noOpSubscriber{}); err != nil { c.logger.Error("Failed to release peer", tag.Error(err), tag.Address(addr)) } } c.peers = make(map[string]*trackedPeer) return nil } // IsRunning satisfies the peer.Chooser interface func (c *SpectatorPeerChooser) IsRunning() bool { return true } // Choose returns a peer for the given shard key by: // 0. Looking up the spectator for the namespace using the x-shard-distributor-namespace header // 1. Looking up the shard owner via the Spectator // 2. Extracting the grpc_address from the owner's metadata // 3. Creating/reusing a peer for that address // // The ShardKey in the request is the shard key (e.g., shard ID) // The function returns // peer: the peer to use for the request // onFinish: a function to call when the request is finished (currently no-op) // err: the error if the request failed func (c *SpectatorPeerChooser) Choose(ctx context.Context, req *transport.Request) (peer peer.Peer, onFinish func(error), err error) { if req.ShardKey == "" { return nil, nil, yarpcerrors.InvalidArgumentErrorf("chooser requires ShardKey to be non-empty") } // Get the spectator for the namespace namespace, ok := req.Headers.Get(NamespaceHeader) if !ok || namespace == "" { return nil, nil, yarpcerrors.InvalidArgumentErrorf("chooser requires x-shard-distributor-namespace header to be non-empty") } spectator, err := c.spectators.ForNamespace(namespace) if err != nil { return nil, nil, yarpcerrors.InvalidArgumentErrorf("get spectator for namespace %s: %w", namespace, err) } // Query spectator for shard owner owner, err := spectator.GetShardOwner(ctx, req.ShardKey) if err != nil { return nil, nil, yarpcerrors.UnavailableErrorf("get shard owner for key %s: %v", req.ShardKey, err) } // Extract GRPC address from owner metadata grpcAddress, ok := owner.Metadata[clientcommon.GrpcAddressMetadataKey] if !ok || grpcAddress == "" { return nil, nil, yarpcerrors.InternalErrorf("no grpc_address in metadata for executor %s owning shard %s", owner.ExecutorID, req.ShardKey) } // Get peer for this address p, err := c.getOrCreatePeer(grpcAddress) if err != nil { return nil, nil, yarpcerrors.InternalErrorf("get or create peer for address %s: %v", grpcAddress, err) } return p, func(error) {}, nil } func (c *SpectatorPeerChooser) SetSpectators(spectators *Spectators) { c.spectators = spectators } func (c *SpectatorPeerChooser) getOrCreatePeer(grpcAddress string) (peer.Peer, error) { c.peersMutex.Lock() defer c.peersMutex.Unlock() if tp, ok := c.peers[grpcAddress]; ok { tp.lastUsed = c.timeSource.Now() return tp.peer, nil } p, err := c.transport.RetainPeer(hostport.Identify(grpcAddress), &noOpSubscriber{}) if err != nil { return nil, fmt.Errorf("retain peer: %w", err) } tp := &trackedPeer{peer: p, lastUsed: c.timeSource.Now()} c.peers[grpcAddress] = tp return p, nil } func (c *SpectatorPeerChooser) evictStalePeers() { now := c.timeSource.Now() c.peersMutex.Lock() defer c.peersMutex.Unlock() for addr, tp := range c.peers { if now.Sub(tp.lastUsed) > c.peerTTL { if err := c.transport.ReleasePeer(tp.peer, &noOpSubscriber{}); err != nil { c.logger.Error("Failed to release stale peer", tag.Error(err), tag.Address(addr)) } delete(c.peers, addr) } } } func (c *SpectatorPeerChooser) startEvictionLoop() { if c.stopCh != nil { return // already started } c.stopCh = make(chan struct{}) c.stopWG.Add(1) go func() { defer c.stopWG.Done() // Tick at half the TTL so a peer is evicted within at most 1.5x the TTL // after its last use (worst case: eviction check just missed, then TTL passes, // then next tick fires at TTL/2 later). ticker := c.timeSource.NewTicker(c.peerTTL / 2) defer ticker.Stop() for { select { case <-ticker.Chan(): c.evictStalePeers() case <-c.stopCh: return } } }() } // noOpSubscriber is a no-op implementation of peer.Subscriber type noOpSubscriber struct{} func (*noOpSubscriber) NotifyStatusChanged(peer.Identifier) {} ================================================ FILE: service/sharddistributor/client/spectatorclient/peer_chooser_test.go ================================================ package spectatorclient import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/peer/hostport" "go.uber.org/yarpc/transport/grpc" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" ) func TestSpectatorPeerChooser_Choose_MissingShardKey(t *testing.T) { chooser := &SpectatorPeerChooser{ logger: testlogger.New(t), peers: make(map[string]*trackedPeer), timeSource: clock.NewRealTimeSource(), } req := &transport.Request{ ShardKey: "", Headers: transport.NewHeaders(), } p, onFinish, err := chooser.Choose(context.Background(), req) assert.Error(t, err) assert.Nil(t, p) assert.Nil(t, onFinish) assert.Contains(t, err.Error(), "ShardKey") } func TestSpectatorPeerChooser_Choose_MissingNamespaceHeader(t *testing.T) { chooser := &SpectatorPeerChooser{ logger: testlogger.New(t), peers: make(map[string]*trackedPeer), timeSource: clock.NewRealTimeSource(), } req := &transport.Request{ ShardKey: "shard-1", Headers: transport.NewHeaders(), } p, onFinish, err := chooser.Choose(context.Background(), req) assert.Error(t, err) assert.Nil(t, p) assert.Nil(t, onFinish) assert.Contains(t, err.Error(), "x-shard-distributor-namespace") } func TestSpectatorPeerChooser_Choose_SpectatorNotFound(t *testing.T) { chooser := &SpectatorPeerChooser{ logger: testlogger.New(t), peers: make(map[string]*trackedPeer), timeSource: clock.NewRealTimeSource(), spectators: &Spectators{spectators: make(map[string]Spectator)}, } req := &transport.Request{ ShardKey: "shard-1", Headers: transport.NewHeaders().With(NamespaceHeader, "unknown-namespace"), } p, onFinish, err := chooser.Choose(context.Background(), req) assert.Error(t, err) assert.Nil(t, p) assert.Nil(t, onFinish) assert.Contains(t, err.Error(), "spectator not found") } func TestSpectatorPeerChooser_StartStop(t *testing.T) { chooser := &SpectatorPeerChooser{ logger: testlogger.New(t), peers: make(map[string]*trackedPeer), timeSource: clock.NewRealTimeSource(), peerTTL: time.Minute, } err := chooser.Start() require.NoError(t, err) assert.True(t, chooser.IsRunning()) err = chooser.Stop() assert.NoError(t, err) } func TestSpectatorPeerChooser_SetSpectators(t *testing.T) { chooser := &SpectatorPeerChooser{ logger: testlogger.New(t), } spectators := &Spectators{spectators: make(map[string]Spectator)} chooser.SetSpectators(spectators) assert.Equal(t, spectators, chooser.spectators) } func TestSpectatorPeerChooser_Choose_Success(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockSpectator := NewMockSpectator(ctrl) peerTransport := grpc.NewTransport() require.NoError(t, peerTransport.Start()) defer peerTransport.Stop() chooser := &SpectatorPeerChooser{ transport: peerTransport, logger: testlogger.New(t), peers: make(map[string]*trackedPeer), timeSource: clock.NewRealTimeSource(), spectators: &Spectators{ spectators: map[string]Spectator{ "test-namespace": mockSpectator, }, }, } ctx := context.Background() req := &transport.Request{ ShardKey: "shard-1", Headers: transport.NewHeaders().With(NamespaceHeader, "test-namespace"), } // Mock spectator to return shard owner with grpc_address mockSpectator.EXPECT(). GetShardOwner(ctx, "shard-1"). Return(&ShardOwner{ ExecutorID: "executor-1", Metadata: map[string]string{ clientcommon.GrpcAddressMetadataKey: "127.0.0.1:7953", }, }, nil) // Execute p, onFinish, err := chooser.Choose(ctx, req) // Assert assert.NoError(t, err) assert.NotNil(t, p) assert.NotNil(t, onFinish) assert.Equal(t, "127.0.0.1:7953", p.Identifier()) assert.Len(t, chooser.peers, 1) assert.Equal(t, "127.0.0.1:7953", chooser.peers["127.0.0.1:7953"].peer.Identifier()) } func TestSpectatorPeerChooser_Choose_ReusesPeer(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockSpectator := NewMockSpectator(ctrl) peerTransport := grpc.NewTransport() require.NoError(t, peerTransport.Start()) defer peerTransport.Stop() chooser := &SpectatorPeerChooser{ transport: peerTransport, logger: testlogger.New(t), peers: make(map[string]*trackedPeer), timeSource: clock.NewRealTimeSource(), spectators: &Spectators{ spectators: map[string]Spectator{ "test-namespace": mockSpectator, }, }, } req := &transport.Request{ ShardKey: "shard-1", Headers: transport.NewHeaders().With(NamespaceHeader, "test-namespace"), } // First call creates the peer mockSpectator.EXPECT(). GetShardOwner(gomock.Any(), "shard-1"). Return(&ShardOwner{ ExecutorID: "executor-1", Metadata: map[string]string{ clientcommon.GrpcAddressMetadataKey: "127.0.0.1:7953", }, }, nil).Times(2) firstPeer, _, err := chooser.Choose(context.Background(), req) require.NoError(t, err) // Second call should reuse the same peer secondPeer, _, err := chooser.Choose(context.Background(), req) // Assert - should reuse existing peer assert.NoError(t, err) assert.Equal(t, firstPeer, secondPeer) assert.Len(t, chooser.peers, 1) } func TestSpectatorPeerChooser_Choose_TracksLastUsed(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockSpectator := NewMockSpectator(ctrl) peerTransport := grpc.NewTransport() require.NoError(t, peerTransport.Start()) defer peerTransport.Stop() mockClock := clock.NewMockedTimeSource() chooser := &SpectatorPeerChooser{ transport: peerTransport, logger: testlogger.New(t), peers: make(map[string]*trackedPeer), timeSource: mockClock, spectators: &Spectators{ spectators: map[string]Spectator{"ns": mockSpectator}, }, } req := &transport.Request{ ShardKey: "shard-1", Headers: transport.NewHeaders().With(NamespaceHeader, "ns"), } // First call — creates peer, sets lastUsed to t0 mockSpectator.EXPECT(). GetShardOwner(gomock.Any(), "shard-1"). Return(&ShardOwner{ ExecutorID: "exec-1", Metadata: map[string]string{clientcommon.GrpcAddressMetadataKey: "127.0.0.1:7953"}, }, nil).Times(2) _, _, err := chooser.Choose(context.Background(), req) require.NoError(t, err) firstLastUsed := chooser.peers["127.0.0.1:7953"].lastUsed // Advance the clock mockClock.Advance(30 * time.Second) // Second call — reuses peer, should update lastUsed to t0+30s _, _, err = chooser.Choose(context.Background(), req) require.NoError(t, err) secondLastUsed := chooser.peers["127.0.0.1:7953"].lastUsed // lastUsed should have advanced assert.True(t, secondLastUsed.After(firstLastUsed), "lastUsed should be updated on reuse") } func TestSpectatorPeerChooser_StartStop_WithTTL(t *testing.T) { peerTransport := grpc.NewTransport() require.NoError(t, peerTransport.Start()) defer peerTransport.Stop() chooser := &SpectatorPeerChooser{ transport: peerTransport, logger: testlogger.New(t), peers: make(map[string]*trackedPeer), timeSource: clock.NewRealTimeSource(), peerTTL: 100 * time.Millisecond, } require.NoError(t, chooser.Start()) assert.NotNil(t, chooser.stopCh, "eviction loop should be started") require.NoError(t, chooser.Stop()) // After Stop, the goroutine must have exited (stopWG.Wait() returned) // Verify idempotency: a second Stop should not panic require.NoError(t, chooser.Stop()) } func TestSpectatorPeerChooser_EvictStalePeers(t *testing.T) { tests := []struct { name string peerTTL time.Duration advanceBy time.Duration expectEvicted bool }{ { name: "peer within TTL is kept", peerTTL: 2 * time.Minute, advanceBy: 1 * time.Minute, expectEvicted: false, }, { name: "peer exactly at TTL boundary is kept", peerTTL: 2 * time.Minute, advanceBy: 2 * time.Minute, expectEvicted: false, }, { name: "peer past TTL is evicted", peerTTL: 2 * time.Minute, advanceBy: 2*time.Minute + time.Millisecond, expectEvicted: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { peerTransport := grpc.NewTransport() require.NoError(t, peerTransport.Start()) defer peerTransport.Stop() mockClock := clock.NewMockedTimeSource() chooser := &SpectatorPeerChooser{ transport: peerTransport, logger: testlogger.New(t), peers: make(map[string]*trackedPeer), timeSource: mockClock, peerTTL: tc.peerTTL, } // Manually insert a tracked peer with lastUsed = now p, err := peerTransport.RetainPeer(hostport.Identify("127.0.0.1:7953"), &noOpSubscriber{}) require.NoError(t, err) chooser.peers["127.0.0.1:7953"] = &trackedPeer{ peer: p, lastUsed: mockClock.Now(), } // Advance clock mockClock.Advance(tc.advanceBy) // Run eviction directly chooser.evictStalePeers() if tc.expectEvicted { assert.Empty(t, chooser.peers) } else { assert.Len(t, chooser.peers, 1) } }) } } ================================================ FILE: service/sharddistributor/client/spectatorclient/sync/resettable_signal.go ================================================ package sync import ( "context" "errors" "sync" ) // ErrReset is returned by Wait() when Reset() is called while goroutines are waiting var ErrReset = errors.New("signal was reset") // resettableSignal is a synchronization primitive that allows waiting for a one-time // signal with context support, and can be reset to wait for a new signal. // Similar to sync.WaitGroup but for single completion events with reset capability. type ResettableSignal struct { mu sync.Mutex doneCh chan struct{} resetCh chan struct{} done bool } // NewResettableSignal creates a new resettable signal in waiting state func NewResettableSignal() *ResettableSignal { return &ResettableSignal{ doneCh: make(chan struct{}), resetCh: make(chan struct{}), } } // Done signals that the event has completed. Safe to call multiple times (idempotent). func (s *ResettableSignal) Done() { s.mu.Lock() defer s.mu.Unlock() if !s.done { s.done = true close(s.doneCh) } } // Wait blocks until either Done() is called, the context is cancelled, or Reset() is called. // Returns: // - nil if Done() was called // - ctx.Err() if context was cancelled // - ErrReset if Reset() was called while waiting func (s *ResettableSignal) Wait(ctx context.Context) error { s.mu.Lock() doneCh := s.doneCh resetCh := s.resetCh done := s.done s.mu.Unlock() // Fast path: already done if done { return nil } select { case <-doneCh: return nil case <-resetCh: return ErrReset case <-ctx.Done(): return ctx.Err() } } // Reset resets the signal to waiting state. Any goroutines currently blocked in Wait() // will immediately be unblocked with ErrReset. func (s *ResettableSignal) Reset() { s.mu.Lock() defer s.mu.Unlock() // Close reset channel to unblock any waiters (they'll get ErrReset) // Only close if not already done (to avoid closing a closed channel) if !s.done { close(s.resetCh) } // Create new channels and reset done flag s.doneCh = make(chan struct{}) s.resetCh = make(chan struct{}) s.done = false } ================================================ FILE: service/sharddistributor/client/spectatorclient/sync/resettable_signal_test.go ================================================ package sync import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" ) func TestResettableSignal_DoneWait(t *testing.T) { signal := NewResettableSignal() signal.Done() ctx := context.Background() err := signal.Wait(ctx) assert.NoError(t, err) } func TestResettableSignal_WaitDone(t *testing.T) { signal := NewResettableSignal() done := make(chan error) go func() { done <- signal.Wait(context.Background()) }() // Give goroutine time to start waiting time.Sleep(10 * time.Millisecond) signal.Done() select { case err := <-done: assert.NoError(t, err) case <-time.After(1 * time.Second): t.Fatal("Wait did not complete after Done") } } func TestResettableSignal_ContextCancellation(t *testing.T) { signal := NewResettableSignal() ctx, cancel := context.WithCancel(context.Background()) done := make(chan error) go func() { done <- signal.Wait(ctx) }() time.Sleep(10 * time.Millisecond) cancel() select { case err := <-done: assert.Error(t, err) assert.Equal(t, context.Canceled, err) case <-time.After(1 * time.Second): t.Fatal("Wait did not complete after context cancellation") } } func TestResettableSignal_ResetWhileWaiting(t *testing.T) { signal := NewResettableSignal() done := make(chan error) go func() { done <- signal.Wait(context.Background()) }() time.Sleep(10 * time.Millisecond) signal.Reset() select { case err := <-done: assert.Error(t, err) assert.True(t, errors.Is(err, ErrReset), "expected ErrReset, got %v", err) case <-time.After(1 * time.Second): t.Fatal("Wait did not complete after Reset") } } func TestResettableSignal_MultipleWaitersReset(t *testing.T) { signal := NewResettableSignal() const numWaiters = 5 results := make([]chan error, numWaiters) for i := 0; i < numWaiters; i++ { i := i // capture the loop variable, the linter insists results[i] = make(chan error) go func() { results[i] <- signal.Wait(context.Background()) }() } // Give goroutines time to start waiting time.Sleep(10 * time.Millisecond) signal.Reset() for i, ch := range results { select { case err := <-ch: assert.Error(t, err, "waiter %d should get error", i) assert.True(t, errors.Is(err, ErrReset), "waiter %d: expected ErrReset, got %v", i, err) case <-time.After(1 * time.Second): t.Fatalf("Waiter %d did not complete after Reset", i) } } } func TestResettableSignal_ResetAfterDone(t *testing.T) { signal := NewResettableSignal() // Terminate and reset the signal signal.Done() signal.Reset() // Now signal should be waiting again ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) defer cancel() err := signal.Wait(ctx) assert.Error(t, err) assert.ErrorIs(t, err, context.DeadlineExceeded) } func TestResettableSignal_ResetThenDoneThenWait(t *testing.T) { signal := NewResettableSignal() // Do a full cycle signal.Done() signal.Reset() signal.Done() err := signal.Wait(context.Background()) assert.NoError(t, err) } func TestResettableSignal_IdempotentDone(t *testing.T) { signal := NewResettableSignal() // Call Done multiple times signal.Done() signal.Done() signal.Done() err := signal.Wait(context.Background()) assert.NoError(t, err) } ================================================ FILE: service/sharddistributor/config/config.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package config import ( "time" "gopkg.in/yaml.v2" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) type ( // Config represents configuration for shard manager service Config struct { LoadBalancingMode dynamicproperties.StringPropertyFnWithNamespaceFilters MigrationMode dynamicproperties.StringPropertyFnWithNamespaceFilters MaxEtcdTxnOps dynamicproperties.IntPropertyFn LoadBalancingNaive LoadBalancingNaiveConfig } LoadBalancingNaiveConfig struct { MaxDeviation dynamicproperties.Float64PropertyFnWithNamespaceFilters } StaticConfig struct { // ShardDistribution is the configuration for leader election mechanism that is used by Shard distributor to handle shard distribution per namespace. ShardDistribution ShardDistribution `yaml:"shardDistribution"` } // ShardDistribution is a configuration for leader election running. ShardDistribution struct { LeaderStore Store `yaml:"leaderStore"` Election Election `yaml:"election"` Namespaces []Namespace `yaml:"namespaces"` Process LeaderProcess `yaml:"process"` Store Store `yaml:"store"` } // Store is a generic container for any storage configuration that should be parsed by the implementation. Store struct { StorageParams *YamlNode `yaml:"storageParams"` } Namespace struct { Name string `yaml:"name"` Type string `yaml:"type"` // The field is a string since it is shared between global config Supported values: fixed|ephemeral. Mode string `yaml:"mode"` // TODO: this should be an ENUM with possible modes: enabled, read_only, proxy, disabled // ShardNum is defined for fixed namespace. ShardNum int64 `yaml:"shardNum"` } Election struct { LeaderPeriod time.Duration `yaml:"leaderPeriod"` // Time to hold leadership before resigning MaxRandomDelay time.Duration `yaml:"maxRandomDelay"` // Maximum random delay before campaigning FailedElectionCooldown time.Duration `yaml:"failedElectionCooldown"` // wait between election attempts with unhandled errors } LeaderProcess struct { // Period is the maximum duration between shard rebalance operations // Default: 1 second Period time.Duration `yaml:"period"` // RebalanceCooldown is the minimum duration between shard rebalance operations // Default: 250ms RebalanceCooldown time.Duration `yaml:"rebalanceCooldown"` // Timeout is the maximum duration of a single shard rebalance operation // Default: 1 second Timeout time.Duration `yaml:"timeout"` // HeartbeatTTL is the duration after which, if no heartbeat is received from an executor, // the executor is considered stale and its shards are eligible for redistribution. // Default: 10 seconds HeartbeatTTL time.Duration `yaml:"heartbeatTTL"` } // YamlNode is a lazy-unmarshaler, because *yaml.Node only exists in gopkg.in/yaml.v3, not v2, // and go.uber.org/config currently uses only v2. YamlNode struct { unmarshal func(out any) error } ) const ( NamespaceTypeFixed = "fixed" NamespaceTypeEphemeral = "ephemeral" ) const ( MigrationModeINVALID = "invalid" MigrationModeLOCALPASSTHROUGH = "local_pass" MigrationModeLOCALPASSTHROUGHSHADOW = "local_pass_shadow" MigrationModeDISTRIBUTEDPASSTHROUGH = "distributed_pass" MigrationModeONBOARDED = "onboarded" ) // MigrationMode maps string migration mode values to types.MigrationMode var MigrationMode = map[string]types.MigrationMode{ MigrationModeINVALID: types.MigrationModeINVALID, MigrationModeLOCALPASSTHROUGH: types.MigrationModeLOCALPASSTHROUGH, MigrationModeLOCALPASSTHROUGHSHADOW: types.MigrationModeLOCALPASSTHROUGHSHADOW, MigrationModeDISTRIBUTEDPASSTHROUGH: types.MigrationModeDISTRIBUTEDPASSTHROUGH, MigrationModeONBOARDED: types.MigrationModeONBOARDED, } // NewConfig returns a new instance of Config func NewConfig(dc *dynamicconfig.Collection) *Config { return &Config{ LoadBalancingMode: dc.GetStringPropertyFilteredByNamespace(dynamicproperties.ShardDistributorLoadBalancingMode), MigrationMode: dc.GetStringPropertyFilteredByNamespace(dynamicproperties.ShardDistributorMigrationMode), MaxEtcdTxnOps: dc.GetIntProperty(dynamicproperties.ShardDistributorMaxEtcdTxnOps), LoadBalancingNaive: LoadBalancingNaiveConfig{ MaxDeviation: dc.GetFloat64PropertyFilteredByNamespace(dynamicproperties.ShardDistributorLoadBalancingNaiveMaxDeviation), }, } } // GetMigrationMode gets the migration mode for a given namespace // If the mode is not set, it defaults to MigrationModeINVALID func (c *Config) GetMigrationMode(namespace string) types.MigrationMode { mode, ok := MigrationMode[c.MigrationMode(namespace)] if !ok { return MigrationMode[MigrationModeINVALID] } return mode } const ( LoadBalancingModeINVALID = "invalid" LoadBalancingModeNAIVE = "naive" LoadBalancingModeGREEDY = "greedy" ) // LoadBalancingMode maps string migration mode values to types.LoadBalancingMode var LoadBalancingMode = map[string]types.LoadBalancingMode{ LoadBalancingModeINVALID: types.LoadBalancingModeINVALID, LoadBalancingModeNAIVE: types.LoadBalancingModeNAIVE, LoadBalancingModeGREEDY: types.LoadBalancingModeGREEDY, } // GetLoadBalancingMode gets the load balancing mode for a given namespace // If the mode is invalid, it returns types.LoadBalancingModeINVALID func (c *Config) GetLoadBalancingMode(namespace string) types.LoadBalancingMode { mode, ok := LoadBalancingMode[c.LoadBalancingMode(namespace)] if !ok { return types.LoadBalancingModeINVALID } return mode } func (s *ShardDistribution) GetMigrationMode(namespace string) types.MigrationMode { for _, ns := range s.Namespaces { if ns.Name == namespace { return MigrationMode[ns.Mode] } } // TODO in the dynamic configuration I will setup a default value return MigrationMode[MigrationModeONBOARDED] } var _ yaml.Unmarshaler = (*YamlNode)(nil) func (y *YamlNode) UnmarshalYAML(unmarshal func(interface{}) error) error { y.unmarshal = unmarshal return nil } func (y *YamlNode) Decode(out any) error { if y == nil { return nil } return y.unmarshal(out) } ================================================ FILE: service/sharddistributor/config/config_test.go ================================================ package config import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) func TestNewDynamicConfigCreatesInstanceWithProperties(t *testing.T) { dc := dynamicconfig.NewNopCollection() config := NewConfig(dc) assert.NotNil(t, config) assert.NotNil(t, config.LoadBalancingMode) assert.NotNil(t, config.MigrationMode) } func TestGetMigrationMode(t *testing.T) { tests := []struct { name string configValue string expectedMode types.MigrationMode }{ { name: "LocalPassthrough", configValue: MigrationModeLOCALPASSTHROUGH, expectedMode: types.MigrationModeLOCALPASSTHROUGH, }, { name: "LocalPassthroughShadow", configValue: MigrationModeLOCALPASSTHROUGHSHADOW, expectedMode: types.MigrationModeLOCALPASSTHROUGHSHADOW, }, { name: "DistributedPassthrough", configValue: MigrationModeDISTRIBUTEDPASSTHROUGH, expectedMode: types.MigrationModeDISTRIBUTEDPASSTHROUGH, }, { name: "Onboarded", configValue: MigrationModeONBOARDED, expectedMode: types.MigrationModeONBOARDED, }, { name: "Empty", configValue: "", expectedMode: types.MigrationModeINVALID, }, { name: "Invalid", configValue: MigrationModeINVALID, expectedMode: types.MigrationModeINVALID, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { client := dynamicconfig.NewInMemoryClient() err := client.UpdateValue(dynamicproperties.ShardDistributorMigrationMode, tt.configValue) require.NoError(t, err) dc := dynamicconfig.NewCollection(client, testlogger.New(t)) config := NewConfig(dc) mode := config.GetMigrationMode("test-namespace") assert.Equal(t, tt.expectedMode, mode) }) } } func TestGetLoadBalancingMode(t *testing.T) { tests := []struct { name string configValue string expectedMode types.LoadBalancingMode }{ { name: "Naive", configValue: "naive", expectedMode: types.LoadBalancingModeNAIVE, }, { name: "Greedy", configValue: "greedy", expectedMode: types.LoadBalancingModeGREEDY, }, { name: "Invalid", configValue: "invalid", expectedMode: types.LoadBalancingModeINVALID, }, { name: "Empty", configValue: "", expectedMode: types.LoadBalancingModeINVALID, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { client := dynamicconfig.NewInMemoryClient() err := client.UpdateValue(dynamicproperties.ShardDistributorLoadBalancingMode, tt.configValue) require.NoError(t, err) dc := dynamicconfig.NewCollection(client, testlogger.New(t)) config := NewConfig(dc) mode := config.GetLoadBalancingMode("test-namespace") assert.Equal(t, tt.expectedMode, mode) }) } } ================================================ FILE: service/sharddistributor/config/configtest/config.go ================================================ package configtest import ( "testing" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/service/sharddistributor/config" ) type ConfigEntry struct { Key dynamicproperties.Key Value interface{} } func NewTestMigrationConfig(t *testing.T, configEntries ...ConfigEntry) *config.Config { client := dynamicconfig.NewInMemoryClient() for _, entry := range configEntries { err := client.UpdateValue(entry.Key, entry.Value) if err != nil { t.Errorf("Failed to update config ") } } dc := dynamicconfig.NewCollection(client, testlogger.New(t)) migrationConfig := config.NewConfig(dc) return migrationConfig } ================================================ FILE: service/sharddistributor/handler/batcher.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "context" "sync" "time" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/types" ) const ( _requestsChannelLimit = 1024 _intervalJitterCoeff = 0.1 // 10% jitter ) // ephemeralAssignmentBatchFn is a function that assigns a batch of shards within a namespace // and returns a map of shardKey -> GetShardOwnerResponse for each successfully assigned shard. // Keys absent from the result map indicate an error for that specific shard. type ephemeralAssignmentBatchFn func(ctx context.Context, namespace string, shardKeys []string) (map[string]*types.GetShardOwnerResponse, error) // batchRequest is a single caller's request submitted to the shardBatcher. type batchRequest struct { namespace string shardKey string // respChan is a buffered channel (cap 1) so the batcher goroutine never blocks // when writing the result back. respChan chan batchResponse } type batchResponse struct { resp *types.GetShardOwnerResponse err error } // shardBatcher collects GetShardOwner calls for ephemeral namespaces over a // configurable time window and processes them in a single batch. // // Usage: // // b := newShardBatcher(100*time.Millisecond, processFn) // b.Start() // defer b.Stop() // resp, err := b.Submit(ctx, &types.GetShardOwnerRequest{Namespace: namespace, ShardKey: shardKey}) type shardBatcher struct { timeSource clock.TimeSource interval time.Duration processBatch ephemeralAssignmentBatchFn // requestChan is shared across all goroutines; requests are keyed by namespace // inside the loop so a single goroutine can handle multiple namespaces. requestChan chan *batchRequest ctx context.Context cancel context.CancelFunc wg sync.WaitGroup } func newShardBatcher(timeSource clock.TimeSource, interval time.Duration, processBatch ephemeralAssignmentBatchFn) *shardBatcher { ctx, cancel := context.WithCancel(context.Background()) return &shardBatcher{ timeSource: timeSource, interval: interval, processBatch: processBatch, requestChan: make(chan *batchRequest, _requestsChannelLimit), ctx: ctx, cancel: cancel, } } // Start launches the background batching loop. func (b *shardBatcher) Start() { b.wg.Add(1) go b.loop() } // Stop signals the batching loop to shut down and waits for it to finish. // Any requests that have already been submitted but not yet flushed will be // drained and processed before the loop exits. func (b *shardBatcher) Stop() { b.cancel() b.wg.Wait() } // Submit enqueues a single GetShardOwner request and blocks until the batcher // has processed the batch that contains it, or until ctx is cancelled. func (b *shardBatcher) Submit(ctx context.Context, request *types.GetShardOwnerRequest) (*types.GetShardOwnerResponse, error) { req := &batchRequest{ namespace: request.Namespace, shardKey: request.ShardKey, respChan: make(chan batchResponse, 1), } select { case b.requestChan <- req: case <-ctx.Done(): return nil, ctx.Err() case <-b.ctx.Done(): return nil, b.ctx.Err() } select { case res := <-req.respChan: return res.resp, res.err case <-ctx.Done(): return nil, ctx.Err() case <-b.ctx.Done(): return nil, b.ctx.Err() } } func (b *shardBatcher) loop() { defer b.wg.Done() ticker := b.timeSource.NewTicker(backoff.JitDuration(b.interval, _intervalJitterCoeff)) defer ticker.Stop() // pending maps namespace -> list of batchRequests accumulated since last flush. pending := make(map[string][]*batchRequest) for { select { case req := <-b.requestChan: pending[req.namespace] = append(pending[req.namespace], req) case <-ticker.Chan(): b.flush(pending) pending = make(map[string][]*batchRequest) case <-b.ctx.Done(): // drainAndCancel sends a cancellation response to all requests that arrived // after the last tick but before the context was cancelled. b.drainAndCancel(pending) return } } } // flush processes all accumulated requests namespace by namespace. func (b *shardBatcher) flush(pending map[string][]*batchRequest) { for namespace, reqs := range pending { if len(reqs) == 0 { continue } shardKeys := make([]string, len(reqs)) for i, r := range reqs { shardKeys[i] = r.shardKey } // Use a background context so individual caller cancellations do not // abort the whole batch; callers will surface their own context error // via the select in Submit. ctx, cancel := context.WithTimeout(context.Background(), 5*b.interval) results, err := b.processBatch(ctx, namespace, shardKeys) cancel() for _, req := range reqs { var res batchResponse if err != nil { res = batchResponse{err: err} } else { res = batchResponse{resp: results[req.shardKey]} if res.resp == nil { // processBatch is expected to always include an entry for // every key it was given; a missing entry is an internal error. res = batchResponse{err: &types.InternalServiceError{ Message: "batch processor returned no result for shard key: " + req.shardKey, }} } } // Non-blocking write: respChan has capacity 1 and each req has // exactly one writer (this loop) and one reader (Submit). req.respChan <- res } } } func (b *shardBatcher) drainAndCancel(pending map[string][]*batchRequest) { // First flush whatever was already accumulated. b.flush(pending) // Then drain the channel itself. for { select { case req := <-b.requestChan: req.respChan <- batchResponse{err: b.ctx.Err()} default: return } } } ================================================ FILE: service/sharddistributor/handler/batcher_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "context" "errors" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/types" ) // processFnFromMap returns an ephemeralAssignmentBatchFn that resolves shard keys from a // fixed map, returning an empty map (and no error) for any key not present. func processFnFromMap(results map[string]*types.GetShardOwnerResponse) ephemeralAssignmentBatchFn { return func(_ context.Context, _ string, shardKeys []string) (map[string]*types.GetShardOwnerResponse, error) { out := make(map[string]*types.GetShardOwnerResponse, len(shardKeys)) for _, k := range shardKeys { if v, ok := results[k]; ok { out[k] = v } } return out, nil } } // advanceUntilDone repeatedly advances the mocked clock by step until done is closed. // Each advance fires one ticker cycle, draining whatever requests have been enqueued // since the previous tick. This avoids races between goroutine scheduling and a // single Advance call. func advanceUntilDone(ts clock.MockedTimeSource, done <-chan struct{}, step time.Duration) { for { select { case <-done: return default: ts.Advance(step) } } } func TestShardBatcher_Submit(t *testing.T) { defer goleak.VerifyNone(t) tests := []struct { name string batchFn ephemeralAssignmentBatchFn namespace string shardKey string // ctxFn builds the context used for the Submit call; defaults to context.Background(). ctxFn func() context.Context wantErr bool wantErrIs error wantErrMsg string wantOwner string }{ { name: "single request resolved from map", batchFn: processFnFromMap(map[string]*types.GetShardOwnerResponse{ "shard-1": {Owner: "exec-1", Namespace: "ns"}, }), namespace: "ns", shardKey: "shard-1", wantOwner: "exec-1", }, { name: "batch function returns error - propagated to caller", batchFn: func(_ context.Context, _ string, _ []string) (map[string]*types.GetShardOwnerResponse, error) { return nil, errors.New("storage unavailable") }, namespace: "ns", shardKey: "shard-1", wantErr: true, wantErrMsg: "storage unavailable", }, { name: "key absent from batch result returns internal error", batchFn: func(_ context.Context, _ string, _ []string) (map[string]*types.GetShardOwnerResponse, error) { return map[string]*types.GetShardOwnerResponse{}, nil }, namespace: "ns", shardKey: "shard-missing", wantErr: true, wantErrMsg: "shard-missing", }, { name: "context cancelled before submit", batchFn: processFnFromMap(nil), namespace: "ns", shardKey: "shard-1", ctxFn: func() context.Context { ctx, cancel := context.WithCancel(context.Background()) cancel() return ctx }, wantErr: true, wantErrIs: context.Canceled, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ts := clock.NewMockedTimeSource() b := newShardBatcher(ts, 10*time.Millisecond, tc.batchFn) b.Start() defer b.Stop() ctx := context.Background() if tc.ctxFn != nil { ctx = tc.ctxFn() } type result struct { resp *types.GetShardOwnerResponse err error } done := make(chan struct{}) ch := make(chan result, 1) go func() { resp, err := b.Submit(ctx, &types.GetShardOwnerRequest{Namespace: tc.namespace, ShardKey: tc.shardKey}) ch <- result{resp, err} close(done) }() // Keep ticking until the goroutine finishes. Advancing by 2x the interval // per step accounts for jitter and ensures the request is flushed even if // it races with the first tick. ts.BlockUntil(1) advanceUntilDone(ts, done, 20*time.Millisecond) got := <-ch if tc.wantErr { require.Error(t, got.err) if tc.wantErrIs != nil { assert.ErrorIs(t, got.err, tc.wantErrIs) } if tc.wantErrMsg != "" { assert.ErrorContains(t, got.err, tc.wantErrMsg) } return } require.NoError(t, got.err) if tc.wantOwner != "" { assert.Equal(t, tc.wantOwner, got.resp.Owner) } }) } } func TestShardBatcher_MultipleNamespacesIsolated(t *testing.T) { defer goleak.VerifyNone(t) tests := []struct { name string results map[string]*types.GetShardOwnerResponse requests []struct { namespace string shardKey string wantOwner string } }{ { name: "two namespaces resolved independently", results: map[string]*types.GetShardOwnerResponse{ "shard-a": {Owner: "exec-ns1", Namespace: "ns1"}, "shard-b": {Owner: "exec-ns2", Namespace: "ns2"}, }, requests: []struct { namespace string shardKey string wantOwner string }{ {namespace: "ns1", shardKey: "shard-a", wantOwner: "exec-ns1"}, {namespace: "ns2", shardKey: "shard-b", wantOwner: "exec-ns2"}, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ts := clock.NewMockedTimeSource() b := newShardBatcher(ts, 10*time.Millisecond, processFnFromMap(tc.results)) b.Start() defer b.Stop() type result struct { owner string err error } got := make([]result, len(tc.requests)) var wg sync.WaitGroup for i, req := range tc.requests { wg.Add(1) go func(i int, namespace, shardKey string) { defer wg.Done() resp, err := b.Submit(context.Background(), &types.GetShardOwnerRequest{Namespace: namespace, ShardKey: shardKey}) if err == nil { got[i] = result{owner: resp.Owner} } else { got[i] = result{err: err} } }(i, req.namespace, req.shardKey) } done := make(chan struct{}) go func() { wg.Wait(); close(done) }() ts.BlockUntil(1) advanceUntilDone(ts, done, 20*time.Millisecond) for i, req := range tc.requests { require.NoError(t, got[i].err) assert.Equal(t, req.wantOwner, got[i].owner) } }) } } func TestShardBatcher_ErrorPropagatedToAllCallers(t *testing.T) { defer goleak.VerifyNone(t) tests := []struct { name string batchErr error numCallers int }{ { name: "error propagated to all concurrent callers", batchErr: errors.New("storage unavailable"), numCallers: 5, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { batchFn := func(_ context.Context, _ string, _ []string) (map[string]*types.GetShardOwnerResponse, error) { return nil, tc.batchErr } ts := clock.NewMockedTimeSource() b := newShardBatcher(ts, 10*time.Millisecond, batchFn) b.Start() defer b.Stop() errs := make([]error, tc.numCallers) var wg sync.WaitGroup for i := range tc.numCallers { wg.Add(1) go func(i int) { defer wg.Done() _, errs[i] = b.Submit(context.Background(), &types.GetShardOwnerRequest{Namespace: "ns", ShardKey: "shard"}) }(i) } done := make(chan struct{}) go func() { wg.Wait(); close(done) }() ts.BlockUntil(1) advanceUntilDone(ts, done, 20*time.Millisecond) for i, err := range errs { assert.ErrorContains(t, err, tc.batchErr.Error(), "caller %d should receive the batch error", i) } }) } } func TestShardBatcher_ConcurrentRequestsBatchedTogether(t *testing.T) { defer goleak.VerifyNone(t) tests := []struct { name string numShards int interval time.Duration }{ { name: "20 concurrent shards collapsed into fewer batch calls", numShards: 20, interval: 50 * time.Millisecond, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { results := make(map[string]*types.GetShardOwnerResponse, tc.numShards) for i := range tc.numShards { key := "shard-" + string(rune('A'+i)) results[key] = &types.GetShardOwnerResponse{Owner: "exec-1", Namespace: "ns"} } // maxBatchSize tracks the largest single batch seen. Batching is confirmed // when at least one flush call receives more than one shard key. var mu sync.Mutex maxBatchSize := 0 batchFn := func(_ context.Context, _ string, shardKeys []string) (map[string]*types.GetShardOwnerResponse, error) { mu.Lock() if len(shardKeys) > maxBatchSize { maxBatchSize = len(shardKeys) } mu.Unlock() out := make(map[string]*types.GetShardOwnerResponse, len(shardKeys)) for _, k := range shardKeys { if v, ok := results[k]; ok { out[k] = v } } return out, nil } ts := clock.NewMockedTimeSource() b := newShardBatcher(ts, tc.interval, batchFn) b.Start() defer b.Stop() var wg sync.WaitGroup for i := range tc.numShards { wg.Add(1) go func(i int) { defer wg.Done() key := "shard-" + string(rune('A'+i)) resp, err := b.Submit(context.Background(), &types.GetShardOwnerRequest{Namespace: "ns", ShardKey: key}) require.NoError(t, err) assert.Equal(t, "exec-1", resp.Owner) }(i) } done := make(chan struct{}) go func() { wg.Wait(); close(done) }() // Keep ticking until all callers finish. Each advance drains whatever // has been enqueued since the previous tick. ts.BlockUntil(1) advanceUntilDone(ts, done, 2*tc.interval) mu.Lock() defer mu.Unlock() assert.Greater(t, maxBatchSize, 1, "expected at least one batch call to receive more than one shard") }) } } func TestShardBatcher_StopDrainsAndCancelsRemainingRequests(t *testing.T) { defer goleak.VerifyNone(t) tests := []struct { name string stopTimeout time.Duration }{ { name: "in-flight request resolves after Stop", stopTimeout: 500 * time.Millisecond, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { block := make(chan struct{}) batchFn := func(_ context.Context, _ string, _ []string) (map[string]*types.GetShardOwnerResponse, error) { <-block return nil, nil } ts := clock.NewMockedTimeSource() b := newShardBatcher(ts, 5*time.Millisecond, batchFn) b.Start() errCh := make(chan error, 1) done := make(chan struct{}) go func() { _, err := b.Submit(context.Background(), &types.GetShardOwnerRequest{Namespace: "ns", ShardKey: "shard-1"}) errCh <- err close(done) }() // Keep ticking until the flush is triggered and the goroutine unblocks. ts.BlockUntil(1) go advanceUntilDone(ts, done, 10*time.Millisecond) // Unblock the batchFn and stop the batcher. close(block) b.Stop() select { case <-errCh: // ok — caller received either a result or a cancellation error case <-time.After(tc.stopTimeout): t.Fatal("Submit did not return after Stop") } }) } } ================================================ FILE: service/sharddistributor/handler/ephemeral_assigner.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "context" "errors" "fmt" "math" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/store" ) // assignEphemeralBatch is the ephemeralAssignmentBatchFn wired into the shardBatcher. // It processes a whole batch of unassigned shard keys for a single ephemeral // namespace using two storage operations: // 1. GetState — read current namespace state once for the whole batch. // 2. AssignShards — write all new assignments atomically in one operation. // // After the write, GetExecutor is called once per unique chosen executor (not // per shard) to fetch metadata for the response, since metadata is stored // separately in the shard cache and is not returned by GetState. // // Within the batch the least-loaded ACTIVE executor is chosen per shard, with // the in-batch running count updated after each pick so load is spread evenly. func (h *handlerImpl) assignEphemeralBatch(ctx context.Context, namespace string, shardKeys []string) (map[string]*types.GetShardOwnerResponse, error) { state, err := h.storage.GetState(ctx, namespace) if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("get namespace state: %v", err)} } assignedCounts, err := buildAssignedCounts(state) if err != nil { return nil, err } chosenExecutors, err := pickExecutors(namespace, shardKeys, assignedCounts) if err != nil { return nil, err } mergeAssignments(state, chosenExecutors) if err := h.storage.AssignShards(ctx, namespace, store.AssignShardsRequest{NewState: state}, store.NopGuard()); err != nil { if errors.Is(err, store.ErrVersionConflict) { // Return the version-conflict sentinel unwrapped so callers can // detect it with errors.Is and decide whether to retry. return nil, fmt.Errorf("assign ephemeral shards: %w", err) } return nil, &types.InternalServiceError{Message: fmt.Sprintf("assign ephemeral shards: %v", err)} } executorOwners, err := h.fetchExecutorMetadata(ctx, namespace, chosenExecutors) if err != nil { return nil, err } return buildResults(namespace, shardKeys, chosenExecutors, executorOwners), nil } // buildAssignedCounts returns a map of executorID -> current shard count for // all ACTIVE executors in the given namespace state. func buildAssignedCounts(state *store.NamespaceState) (map[string]int, error) { counts := make(map[string]int, len(state.ShardAssignments)) for executorID, assignment := range state.ShardAssignments { executorState, ok := state.Executors[executorID] if !ok || executorState.Status != types.ExecutorStatusACTIVE { continue } counts[executorID] = len(assignment.AssignedShards) } return counts, nil } // pickExecutors assigns each shard key to the least-loaded active executor, // updating the in-batch running count after every pick to spread load evenly. // It returns a map of shardKey -> chosen executorID. func pickExecutors(namespace string, shardKeys []string, assignedCounts map[string]int) (map[string]string, error) { chosenExecutors := make(map[string]string, len(shardKeys)) for _, shardKey := range shardKeys { chosenExecutor := "" minCount := math.MaxInt for executorID, count := range assignedCounts { if count < minCount { minCount = count chosenExecutor = executorID } } if chosenExecutor == "" { return nil, &types.InternalServiceError{Message: "no active executors available for namespace: " + namespace} } chosenExecutors[shardKey] = chosenExecutor assignedCounts[chosenExecutor]++ } return chosenExecutors, nil } // mergeAssignments folds the chosen shard→executor assignments back into state. // The AssignedShards maps are copied to avoid mutating the object returned by // GetState. func mergeAssignments(state *store.NamespaceState, chosenExecutors map[string]string) { for executorID, shardsForExecutor := range invertMap(chosenExecutors) { existing := state.ShardAssignments[executorID] newShards := make(map[string]*types.ShardAssignment, len(existing.AssignedShards)+len(shardsForExecutor)) for k, v := range existing.AssignedShards { newShards[k] = v } for _, shardKey := range shardsForExecutor { newShards[shardKey] = &types.ShardAssignment{Status: types.AssignmentStatusREADY} } existing.AssignedShards = newShards state.ShardAssignments[executorID] = existing } } // fetchExecutorMetadata calls GetExecutor once per unique chosen executor to // retrieve metadata. Metadata is stored separately from HeartbeatState and is // not returned by GetState. func (h *handlerImpl) fetchExecutorMetadata(ctx context.Context, namespace string, chosenExecutors map[string]string) (map[string]*store.ShardOwner, error) { executorOwners := make(map[string]*store.ShardOwner, len(chosenExecutors)) for _, executorID := range chosenExecutors { if _, already := executorOwners[executorID]; already { continue } owner, err := h.storage.GetExecutor(ctx, namespace, executorID) if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("get executor %q: %v", executorID, err)} } executorOwners[executorID] = owner } return executorOwners, nil } // buildResults constructs the shardKey -> GetShardOwnerResponse map from the // chosen executors and their fetched metadata. func buildResults(namespace string, shardKeys []string, chosenExecutors map[string]string, executorOwners map[string]*store.ShardOwner) map[string]*types.GetShardOwnerResponse { results := make(map[string]*types.GetShardOwnerResponse, len(shardKeys)) for _, shardKey := range shardKeys { executorID := chosenExecutors[shardKey] owner := executorOwners[executorID] results[shardKey] = &types.GetShardOwnerResponse{ Owner: owner.ExecutorID, Namespace: namespace, Metadata: owner.Metadata, } } return results } // invertMap turns map[shardKey]executorID into map[executorID][]shardKey. func invertMap(m map[string]string) map[string][]string { out := make(map[string][]string) for shardKey, executorID := range m { out[executorID] = append(out[executorID], shardKey) } return out } ================================================ FILE: service/sharddistributor/handler/ephemeral_assigner_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "context" "errors" "testing" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/store" ) func TestAssignEphemeralBatch(t *testing.T) { tests := []struct { name string namespace string shardKeys []string setupMocks func(mockStore *store.MockStore) expectedOwners map[string]string // shardKey -> expected owner expectedError bool expectedErrMsg string }{ { name: "AssignsToLeastLoadedExecutor", namespace: _testNamespaceEphemeral, shardKeys: []string{"NON-EXISTING-SHARD"}, setupMocks: func(mockStore *store.MockStore) { mockStore.EXPECT().GetState(gomock.Any(), _testNamespaceEphemeral).Return(&store.NamespaceState{ Executors: map[string]store.HeartbeatState{ "owner1": {Status: types.ExecutorStatusACTIVE}, "owner2": {Status: types.ExecutorStatusACTIVE}, }, ShardAssignments: map[string]store.AssignedState{ "owner1": { AssignedShards: map[string]*types.ShardAssignment{ "shard1": {Status: types.AssignmentStatusREADY}, "shard2": {Status: types.AssignmentStatusREADY}, "shard3": {Status: types.AssignmentStatusREADY}, }, }, "owner2": { AssignedShards: map[string]*types.ShardAssignment{ "shard4": {Status: types.AssignmentStatusREADY}, }, }, }, }, nil) // owner2 has the fewest shards; expect a single batch AssignShards call. mockStore.EXPECT().AssignShards(gomock.Any(), _testNamespaceEphemeral, gomock.Any(), gomock.Any()).Return(nil) mockStore.EXPECT().GetExecutor(gomock.Any(), _testNamespaceEphemeral, "owner2").Return(&store.ShardOwner{ ExecutorID: "owner2", Metadata: map[string]string{"ip": "127.0.0.1", "port": "1234"}, }, nil) }, expectedOwners: map[string]string{"NON-EXISTING-SHARD": "owner2"}, }, { name: "SkipDrainingExecutor", namespace: _testNamespaceEphemeral, shardKeys: []string{"NON-EXISTING-SHARD"}, setupMocks: func(mockStore *store.MockStore) { mockStore.EXPECT().GetState(gomock.Any(), _testNamespaceEphemeral).Return(&store.NamespaceState{ Executors: map[string]store.HeartbeatState{ "owner1": {Status: types.ExecutorStatusACTIVE}, // owner2 is DRAINING, should be skipped even though it has fewer shards "owner2": {Status: types.ExecutorStatusDRAINING}, }, ShardAssignments: map[string]store.AssignedState{ "owner1": { AssignedShards: map[string]*types.ShardAssignment{ "shard1": {Status: types.AssignmentStatusREADY}, "shard2": {Status: types.AssignmentStatusREADY}, "shard3": {Status: types.AssignmentStatusREADY}, }, }, "owner2": { // owner2 has fewer shards but is DRAINING, so should be skipped AssignedShards: map[string]*types.ShardAssignment{ "shard4": {Status: types.AssignmentStatusREADY}, }, }, }, }, nil) // owner1 should be selected; single batch write mockStore.EXPECT().AssignShards(gomock.Any(), _testNamespaceEphemeral, gomock.Any(), gomock.Any()).Return(nil) mockStore.EXPECT().GetExecutor(gomock.Any(), _testNamespaceEphemeral, "owner1").Return(&store.ShardOwner{ ExecutorID: "owner1", Metadata: map[string]string{"ip": "127.0.0.1", "port": "1234"}, }, nil) }, expectedOwners: map[string]string{"NON-EXISTING-SHARD": "owner1"}, }, { name: "GetStateFailure", namespace: _testNamespaceEphemeral, shardKeys: []string{"NON-EXISTING-SHARD"}, setupMocks: func(mockStore *store.MockStore) { mockStore.EXPECT().GetState(gomock.Any(), _testNamespaceEphemeral).Return(nil, errors.New("get state failure")) }, expectedError: true, expectedErrMsg: "get state failure", }, { // When two batches race and the first wins, the second gets // ErrVersionConflict from AssignShards. This is surfaced as an // internal error; the caller is expected to retry GetShardOwner, // which will then find the shard already assigned in the cache. name: "VersionConflict", namespace: _testNamespaceEphemeral, shardKeys: []string{"CONCURRENT-SHARD"}, setupMocks: func(mockStore *store.MockStore) { mockStore.EXPECT().GetState(gomock.Any(), _testNamespaceEphemeral).Return(&store.NamespaceState{ Executors: map[string]store.HeartbeatState{"owner1": {Status: types.ExecutorStatusACTIVE}}, ShardAssignments: map[string]store.AssignedState{"owner1": {AssignedShards: map[string]*types.ShardAssignment{}}}}, nil) mockStore.EXPECT().AssignShards(gomock.Any(), _testNamespaceEphemeral, gomock.Any(), gomock.Any()).Return(store.ErrVersionConflict) }, expectedError: true, expectedErrMsg: "version conflict", }, { name: "NoActiveExecutors", namespace: _testNamespaceEphemeral, shardKeys: []string{"NON-EXISTING-SHARD"}, setupMocks: func(mockStore *store.MockStore) { // All executors are DRAINING — none are eligible for assignment. mockStore.EXPECT().GetState(gomock.Any(), _testNamespaceEphemeral).Return(&store.NamespaceState{ Executors: map[string]store.HeartbeatState{ "owner1": {Status: types.ExecutorStatusDRAINING}, "owner2": {Status: types.ExecutorStatusDRAINING}, }, ShardAssignments: map[string]store.AssignedState{ "owner1": {AssignedShards: map[string]*types.ShardAssignment{}}, "owner2": {AssignedShards: map[string]*types.ShardAssignment{}}, }, }, nil) }, expectedError: true, expectedErrMsg: "no active executors available for namespace", }, { name: "AssignShardsFailure", namespace: _testNamespaceEphemeral, shardKeys: []string{"NON-EXISTING-SHARD"}, setupMocks: func(mockStore *store.MockStore) { mockStore.EXPECT().GetState(gomock.Any(), _testNamespaceEphemeral).Return(&store.NamespaceState{ Executors: map[string]store.HeartbeatState{"owner1": {Status: types.ExecutorStatusACTIVE}}, ShardAssignments: map[string]store.AssignedState{"owner1": {AssignedShards: map[string]*types.ShardAssignment{}}}}, nil) mockStore.EXPECT().AssignShards(gomock.Any(), _testNamespaceEphemeral, gomock.Any(), gomock.Any()).Return(errors.New("assign shards failure")) }, expectedError: true, expectedErrMsg: "assign shards failure", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockStorage := store.NewMockStore(ctrl) h := &handlerImpl{ logger: testlogger.New(t), storage: mockStorage, } if tt.setupMocks != nil { tt.setupMocks(mockStorage) } results, err := h.assignEphemeralBatch(context.Background(), tt.namespace, tt.shardKeys) if tt.expectedError { require.Error(t, err) require.Contains(t, err.Error(), tt.expectedErrMsg) require.Nil(t, results) } else { require.NoError(t, err) require.Len(t, results, len(tt.expectedOwners)) for shardKey, expectedOwner := range tt.expectedOwners { require.Equal(t, expectedOwner, results[shardKey].Owner) require.Equal(t, tt.namespace, results[shardKey].Namespace) require.Equal(t, map[string]string{"ip": "127.0.0.1", "port": "1234"}, results[shardKey].Metadata) } } }) } } ================================================ FILE: service/sharddistributor/handler/executor.go ================================================ package handler import ( "context" "errors" "fmt" "time" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store" ) const ( _maxMetadataKeys = 32 _maxMetadataKeyLength = 128 _maxMetadataValueSize = 512 * 1024 // 512KB ) type executor struct { logger log.Logger timeSource clock.TimeSource storage store.Store shardDistributionCfg config.ShardDistribution cfg *config.Config metricsClient metrics.Client } func NewExecutorHandler( logger log.Logger, storage store.Store, timeSource clock.TimeSource, shardDistributionCfg config.ShardDistribution, cfg *config.Config, metricsClient metrics.Client, ) Executor { return &executor{ logger: logger, timeSource: timeSource, storage: storage, shardDistributionCfg: shardDistributionCfg, cfg: cfg, metricsClient: metricsClient, } } func (h *executor) Heartbeat(ctx context.Context, request *types.ExecutorHeartbeatRequest) (*types.ExecutorHeartbeatResponse, error) { previousHeartbeat, assignedShards, err := h.storage.GetHeartbeat(ctx, request.Namespace, request.ExecutorID) // We ignore Executor not found errors, since it just means that this executor heartbeat the first time. if err != nil && !errors.Is(err, store.ErrExecutorNotFound) { return nil, &types.InternalServiceError{Message: fmt.Sprintf("failed to get heartbeat: %v", err)} } heartbeatTime := h.timeSource.Now().UTC() mode := h.cfg.GetMigrationMode(request.Namespace) shardAssignedInBackground := true switch mode { case types.MigrationModeINVALID: return nil, &types.InternalServiceError{Message: fmt.Sprintf("namespace's migration mode is invalid: %v", err)} case types.MigrationModeLOCALPASSTHROUGH: h.logger.Info("Migration mode is local passthrough, no calls to heartbeat should be allowed", tag.ShardNamespace(request.Namespace), tag.ShardExecutor(request.ExecutorID)) return _convertResponse(nil, mode), nil // From SD perspective the behaviour is the same case types.MigrationModeLOCALPASSTHROUGHSHADOW, types.MigrationModeDISTRIBUTEDPASSTHROUGH: assignedShards, err = h.assignShardsInCurrentHeartbeat(ctx, request, assignedShards) if err != nil { return nil, err } shardAssignedInBackground = false } newHeartbeat := store.HeartbeatState{ LastHeartbeat: heartbeatTime, Status: request.Status, ReportedShards: request.ShardStatusReports, Metadata: request.GetMetadata(), } if err := validateMetadata(newHeartbeat.Metadata); err != nil { return nil, types.BadRequestError{Message: fmt.Sprintf("invalid metadata: %s", err)} } err = h.storage.RecordHeartbeat(ctx, request.Namespace, request.ExecutorID, newHeartbeat) if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("failed to record heartbeat: %v", err)} } // emit shard assignment metrics only if shards are assigned in the background // shard assignment in heartbeat doesn't involve any assignment changes happening in the background // thus there was no shard handover and no assignment distribution latency // to measure, so don't need to emit metrics in that case if shardAssignedInBackground { h.emitShardAssignmentMetrics(request.Namespace, heartbeatTime, previousHeartbeat, assignedShards) } return _convertResponse(assignedShards, mode), nil } // emitShardAssignmentMetrics emits the following metrics for newly assigned shards: // - ShardAssignmentDistributionLatency: time taken since the shard was assigned to heartbeat time // - ShardHandoverLatency: time taken since the previous executor's last heartbeat to heartbeat time func (h *executor) emitShardAssignmentMetrics(namespace string, heartbeatTime time.Time, previousHeartbeat *store.HeartbeatState, assignedState *store.AssignedState) { // find newly assigned shards, if there are none, no handovers happened newAssignedShardIDs := filterNewlyAssignedShardIDs(previousHeartbeat, assignedState) if len(newAssignedShardIDs) == 0 { // no need to emit ShardDistributorShardAssignmentDistributionLatency due to no handovers return } metricsScope := h.metricsClient.Scope(metrics.ShardDistributorHeartbeatScope). Tagged(metrics.NamespaceTag(namespace)) distributionLatency := heartbeatTime.Sub(assignedState.LastUpdated) metricsScope.RecordHistogramDuration(metrics.ShardDistributorShardAssignmentDistributionLatency, distributionLatency) for _, shardID := range newAssignedShardIDs { handoverStats, ok := assignedState.ShardHandoverStats[shardID] if !ok { // no handover stats for this shard, means it was never handed over before // so no handover latency metric to emit continue } handoverLatency := heartbeatTime.Sub(handoverStats.PreviousExecutorLastHeartbeatTime) metricsScope.Tagged(metrics.HandoverTypeTag(handoverStats.HandoverType.String())). RecordHistogramDuration(metrics.ShardDistributorShardHandoverLatency, handoverLatency) } } // assignShardsInCurrentHeartbeat is used during the migration phase to assign the shards to the executors according to what is reported during the heartbeat func (h *executor) assignShardsInCurrentHeartbeat(ctx context.Context, request *types.ExecutorHeartbeatRequest, assignedShards *store.AssignedState) (*store.AssignedState, error) { modRevision := int64(0) if assignedShards != nil { modRevision = assignedShards.ModRevision } newState := store.AssignedState{ AssignedShards: make(map[string]*types.ShardAssignment), LastUpdated: h.timeSource.Now().UTC(), ModRevision: modRevision, } for shard := range request.GetShardStatusReports() { newState.AssignedShards[shard] = &types.ShardAssignment{ Status: types.AssignmentStatusREADY, } } assignShardsRequest := store.AssignShardsRequest{ NewState: &store.NamespaceState{ ShardAssignments: map[string]store.AssignedState{ request.GetExecutorID(): newState, }, }, } err := h.storage.AssignShards(ctx, request.GetNamespace(), assignShardsRequest, store.NopGuard()) if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("failed to assign shards in the current heartbeat: %v", err)} } return &newState, nil } func _convertResponse(shards *store.AssignedState, mode types.MigrationMode) *types.ExecutorHeartbeatResponse { res := &types.ExecutorHeartbeatResponse{} res.MigrationMode = mode if shards == nil { return res } res.ShardAssignments = shards.AssignedShards return res } func validateMetadata(metadata map[string]string) error { if len(metadata) > _maxMetadataKeys { return fmt.Errorf("metadata has %d keys, which exceeds the maximum of %d", len(metadata), _maxMetadataKeys) } for key, value := range metadata { if len(key) > _maxMetadataKeyLength { return fmt.Errorf("metadata key %q has length %d, which exceeds the maximum of %d", key, len(key), _maxMetadataKeyLength) } if len(value) > _maxMetadataValueSize { return fmt.Errorf("metadata value for key %q has size %d bytes, which exceeds the maximum of %d bytes", key, len(value), _maxMetadataValueSize) } } return nil } func filterNewlyAssignedShardIDs(previousHeartbeat *store.HeartbeatState, assignedState *store.AssignedState) []string { // if assignedState is nil, no shards are assigned if assignedState == nil { return nil } var newAssignedShardIDs []string for assignedShardID := range assignedState.AssignedShards { if previousHeartbeat == nil || !shardInReportedShards(previousHeartbeat.ReportedShards, assignedShardID) { newAssignedShardIDs = append(newAssignedShardIDs, assignedShardID) } } return newAssignedShardIDs } func shardInReportedShards(reportedShards map[string]*types.ShardStatusReport, shardID string) bool { _, ok := reportedShards[shardID] return ok } ================================================ FILE: service/sharddistributor/handler/executor_test.go ================================================ package handler import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" metricmocks "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store" ) func TestHeartbeat(t *testing.T) { ctx := context.Background() namespace := "test-namespace" executorID := "test-executor" now := time.Now().UTC() // Test Case 1: First Heartbeat t.Run("FirstHeartbeat", func(t *testing.T) { ctrl := gomock.NewController(t) mockStore := store.NewMockStore(ctrl) mockTimeSource := clock.NewMockedTimeSourceAt(now) shardDistributionCfg := config.ShardDistribution{} cfg := newConfig(t, []configEntry{}) handler := NewExecutorHandler(testlogger.New(t), mockStore, mockTimeSource, shardDistributionCfg, cfg, metrics.NoopClient) req := &types.ExecutorHeartbeatRequest{ Namespace: namespace, ExecutorID: executorID, Status: types.ExecutorStatusACTIVE, } mockStore.EXPECT().GetHeartbeat(gomock.Any(), namespace, executorID).Return(nil, nil, store.ErrExecutorNotFound) mockStore.EXPECT().RecordHeartbeat(gomock.Any(), namespace, executorID, store.HeartbeatState{ LastHeartbeat: now, Status: types.ExecutorStatusACTIVE, }) _, err := handler.Heartbeat(ctx, req) require.NoError(t, err) }) // Test Case 2: Subsequent heartbeat records a new heartbeat t.Run("SubsequentHeartbeatRecords", func(t *testing.T) { ctrl := gomock.NewController(t) mockStore := store.NewMockStore(ctrl) mockTimeSource := clock.NewMockedTimeSourceAt(now) shardDistributionCfg := config.ShardDistribution{} cfg := newConfig(t, []configEntry{}) handler := NewExecutorHandler(testlogger.New(t), mockStore, mockTimeSource, shardDistributionCfg, cfg, metrics.NoopClient) req := &types.ExecutorHeartbeatRequest{ Namespace: namespace, ExecutorID: executorID, Status: types.ExecutorStatusACTIVE, } previousHeartbeat := store.HeartbeatState{ LastHeartbeat: now, Status: types.ExecutorStatusACTIVE, } mockStore.EXPECT().GetHeartbeat(gomock.Any(), namespace, executorID).Return(&previousHeartbeat, nil, nil) mockStore.EXPECT().RecordHeartbeat(gomock.Any(), namespace, executorID, store.HeartbeatState{ LastHeartbeat: now, Status: types.ExecutorStatusACTIVE, }) _, err := handler.Heartbeat(ctx, req) require.NoError(t, err) }) // Test Case 3: Status Change (with update) t.Run("StatusChange", func(t *testing.T) { ctrl := gomock.NewController(t) mockStore := store.NewMockStore(ctrl) mockTimeSource := clock.NewMockedTimeSourceAt(now) shardDistributionCfg := config.ShardDistribution{} cfg := newConfig(t, []configEntry{}) handler := NewExecutorHandler(testlogger.New(t), mockStore, mockTimeSource, shardDistributionCfg, cfg, metrics.NoopClient) req := &types.ExecutorHeartbeatRequest{ Namespace: namespace, ExecutorID: executorID, Status: types.ExecutorStatusDRAINING, // Status changed } previousHeartbeat := store.HeartbeatState{ LastHeartbeat: now, Status: types.ExecutorStatusACTIVE, } mockStore.EXPECT().GetHeartbeat(gomock.Any(), namespace, executorID).Return(&previousHeartbeat, nil, nil) mockStore.EXPECT().RecordHeartbeat(gomock.Any(), namespace, executorID, store.HeartbeatState{ LastHeartbeat: now, Status: types.ExecutorStatusDRAINING, }) _, err := handler.Heartbeat(ctx, req) require.NoError(t, err) }) // Test Case 4: Storage Error t.Run("StorageError", func(t *testing.T) { ctrl := gomock.NewController(t) mockStore := store.NewMockStore(ctrl) mockTimeSource := clock.NewMockedTimeSource() shardDistributionCfg := config.ShardDistribution{} cfg := newConfig(t, []configEntry{}) handler := NewExecutorHandler(testlogger.New(t), mockStore, mockTimeSource, shardDistributionCfg, cfg, metrics.NoopClient) req := &types.ExecutorHeartbeatRequest{ Namespace: namespace, ExecutorID: executorID, Status: types.ExecutorStatusACTIVE, } expectedErr := errors.New("storage is down") mockStore.EXPECT().GetHeartbeat(gomock.Any(), namespace, executorID).Return(nil, nil, expectedErr) _, err := handler.Heartbeat(ctx, req) require.Error(t, err) require.Contains(t, err.Error(), expectedErr.Error()) }) // Test Case 5: Heartbeat with executor associated invalid migration mode t.Run("MigrationModeInvald", func(t *testing.T) { ctrl := gomock.NewController(t) mockStore := store.NewMockStore(ctrl) mockTimeSource := clock.NewMockedTimeSource() shardDistributionCfg := config.ShardDistribution{ Namespaces: []config.Namespace{{Name: namespace, Mode: config.MigrationModeINVALID}}, } cfg := newConfig(t, []configEntry{{dynamicproperties.ShardDistributorMigrationMode, config.MigrationModeINVALID}}) handler := NewExecutorHandler(testlogger.New(t), mockStore, mockTimeSource, shardDistributionCfg, cfg, metrics.NoopClient) req := &types.ExecutorHeartbeatRequest{ Namespace: namespace, ExecutorID: executorID, Status: types.ExecutorStatusACTIVE, } previousHeartbeat := store.HeartbeatState{ LastHeartbeat: now, Status: types.ExecutorStatusACTIVE, } expectedErr := errors.New("migration mode is invalid") mockStore.EXPECT().GetHeartbeat(gomock.Any(), namespace, executorID).Return(&previousHeartbeat, nil, nil) _, err := handler.Heartbeat(ctx, req) require.Error(t, err) require.Contains(t, err.Error(), expectedErr.Error()) }) // Test Case 6: Heartbeat with executor associated with local passthrough mode t.Run("MigrationModeLocalPassthrough", func(t *testing.T) { ctrl := gomock.NewController(t) mockStore := store.NewMockStore(ctrl) mockTimeSource := clock.NewMockedTimeSource() shardDistributionCfg := config.ShardDistribution{ Namespaces: []config.Namespace{{Name: namespace, Mode: config.MigrationModeLOCALPASSTHROUGH}}, } cfg := newConfig(t, []configEntry{{dynamicproperties.ShardDistributorMigrationMode, config.MigrationModeLOCALPASSTHROUGH}}) handler := NewExecutorHandler(testlogger.New(t), mockStore, mockTimeSource, shardDistributionCfg, cfg, metrics.NoopClient) req := &types.ExecutorHeartbeatRequest{ Namespace: namespace, ExecutorID: executorID, Status: types.ExecutorStatusACTIVE, } previousHeartbeat := store.HeartbeatState{ LastHeartbeat: now, Status: types.ExecutorStatusACTIVE, } mockStore.EXPECT().GetHeartbeat(gomock.Any(), namespace, executorID).Return(&previousHeartbeat, nil, nil) resp, err := handler.Heartbeat(ctx, req) require.NoError(t, err) require.Equal(t, types.MigrationModeLOCALPASSTHROUGH, resp.MigrationMode) }) // Test Case 7: Heartbeat with executor associated with local passthrough shadow t.Run("MigrationModeLocalPassthroughWithAssignmentChanges", func(t *testing.T) { ctrl := gomock.NewController(t) mockStore := store.NewMockStore(ctrl) mockTimeSource := clock.NewMockedTimeSource() shardDistributionCfg := config.ShardDistribution{ Namespaces: []config.Namespace{{Name: namespace, Mode: config.MigrationModeLOCALPASSTHROUGHSHADOW}}, } cfg := newConfig(t, []configEntry{{dynamicproperties.ShardDistributorMigrationMode, config.MigrationModeLOCALPASSTHROUGHSHADOW}}) handler := NewExecutorHandler(testlogger.New(t), mockStore, mockTimeSource, shardDistributionCfg, cfg, metrics.NoopClient) req := &types.ExecutorHeartbeatRequest{ Namespace: namespace, ExecutorID: executorID, Status: types.ExecutorStatusACTIVE, ShardStatusReports: map[string]*types.ShardStatusReport{ "shard0": {Status: types.ShardStatusREADY, ShardLoad: 1.0}, }, } previousHeartbeat := store.HeartbeatState{ LastHeartbeat: now, Status: types.ExecutorStatusACTIVE, ReportedShards: map[string]*types.ShardStatusReport{ "shard1": {Status: types.ShardStatusREADY, ShardLoad: 1.0}, }, } assignedState := store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "shard1": {Status: types.AssignmentStatusREADY}, }, } mockStore.EXPECT().GetHeartbeat(gomock.Any(), namespace, executorID).Return(&previousHeartbeat, &assignedState, nil) mockStore.EXPECT().AssignShards(gomock.Any(), namespace, gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, namespace string, request store.AssignShardsRequest, guard store.GuardFunc) error { // Expect to Assign the shard in the request expectedRequest := store.AssignShardsRequest{ NewState: &store.NamespaceState{ ShardAssignments: map[string]store.AssignedState{ executorID: {AssignedShards: map[string]*types.ShardAssignment{"shard0": {Status: types.AssignmentStatusREADY}}}, }, }, } require.Equal(t, expectedRequest.NewState.ShardAssignments[executorID].AssignedShards, request.NewState.ShardAssignments[executorID].AssignedShards) return nil }, ) mockStore.EXPECT().RecordHeartbeat(gomock.Any(), namespace, executorID, gomock.AssignableToTypeOf(store.HeartbeatState{})).DoAndReturn( func(_ context.Context, _ string, _ string, hb store.HeartbeatState) error { // Validate status and reported shards, ignore exact timestamp require.Equal(t, types.ExecutorStatusACTIVE, hb.Status) require.Contains(t, hb.ReportedShards, "shard0") return nil }, ) _, err := handler.Heartbeat(ctx, req) require.NoError(t, err) }, ) // Test Case 8: Heartbeat with executor associated with distributed passthrough t.Run("MigrationModeDISTRIBUTEDPASSTHROUGHAssignmentFailure", func(t *testing.T) { ctrl := gomock.NewController(t) mockStore := store.NewMockStore(ctrl) mockTimeSource := clock.NewMockedTimeSource() shardDistributionCfg := config.ShardDistribution{ Namespaces: []config.Namespace{{Name: namespace, Mode: config.MigrationModeLOCALPASSTHROUGHSHADOW}}, } cfg := newConfig(t, []configEntry{{dynamicproperties.ShardDistributorMigrationMode, config.MigrationModeLOCALPASSTHROUGHSHADOW}}) handler := NewExecutorHandler(testlogger.New(t), mockStore, mockTimeSource, shardDistributionCfg, cfg, metrics.NoopClient) req := &types.ExecutorHeartbeatRequest{ Namespace: namespace, ExecutorID: executorID, Status: types.ExecutorStatusACTIVE, ShardStatusReports: map[string]*types.ShardStatusReport{ "shard0": {Status: types.ShardStatusREADY, ShardLoad: 1.0}, }, } previousHeartbeat := store.HeartbeatState{ LastHeartbeat: now, Status: types.ExecutorStatusACTIVE, ReportedShards: map[string]*types.ShardStatusReport{ "shard1": {Status: types.ShardStatusREADY, ShardLoad: 1.0}, }, } assignedState := store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "shard1": {Status: types.AssignmentStatusREADY}, }, } mockStore.EXPECT().GetHeartbeat(gomock.Any(), namespace, executorID).Return(&previousHeartbeat, &assignedState, nil) expectedErr := errors.New("assignemnt failed") mockStore.EXPECT().AssignShards(gomock.Any(), namespace, gomock.Any(), gomock.Any()).Return(expectedErr) _, err := handler.Heartbeat(ctx, req) require.Error(t, err) require.Contains(t, err.Error(), expectedErr.Error()) }) // Test Case 9: Heartbeat with metadata validation failure - too many keys t.Run("MetadataValidationTooManyKeys", func(t *testing.T) { ctrl := gomock.NewController(t) mockStore := store.NewMockStore(ctrl) mockTimeSource := clock.NewMockedTimeSourceAt(now) shardDistributionCfg := config.ShardDistribution{} cfg := newConfig(t, []configEntry{}) handler := NewExecutorHandler(testlogger.New(t), mockStore, mockTimeSource, shardDistributionCfg, cfg, metrics.NoopClient) // Create metadata with more than max allowed keys metadata := make(map[string]string) for i := 0; i < _maxMetadataKeys+1; i++ { metadata[string(rune('a'+i))] = "value" } req := &types.ExecutorHeartbeatRequest{ Namespace: namespace, ExecutorID: executorID, Status: types.ExecutorStatusACTIVE, Metadata: metadata, } mockStore.EXPECT().GetHeartbeat(gomock.Any(), namespace, executorID).Return(nil, nil, store.ErrExecutorNotFound) _, err := handler.Heartbeat(ctx, req) require.Error(t, err) require.Contains(t, err.Error(), "invalid metadata: metadata has 33 keys, which exceeds the maximum of 32") }) } func TestValidateMetadata(t *testing.T) { // Helper function to generate metadata with N keys makeMetadataWithKeys := func(n int) map[string]string { metadata := make(map[string]string) for i := 0; i < n; i++ { metadata[string(rune('a'+i))] = "value" } return metadata } testCases := []struct { name string metadata map[string]string expectError bool errorSubstring string }{ { name: "ValidMetadata", metadata: map[string]string{ "key1": "value1", "key2": "value2", }, expectError: false, }, { name: "EmptyMetadata", metadata: map[string]string{}, expectError: false, }, { name: "NilMetadata", metadata: nil, expectError: false, }, { name: "TooManyKeys", metadata: makeMetadataWithKeys(_maxMetadataKeys + 1), expectError: true, errorSubstring: "exceeds the maximum of 32", }, { name: "ExactlyMaxKeys", metadata: makeMetadataWithKeys(_maxMetadataKeys), expectError: false, }, { name: "KeyTooLong", metadata: map[string]string{ string(make([]byte, _maxMetadataKeyLength+1)): "value", }, expectError: true, errorSubstring: "exceeds the maximum of 128", }, { name: "KeyExactlyMaxLength", metadata: map[string]string{ string(make([]byte, _maxMetadataKeyLength)): "value", }, expectError: false, }, { name: "ValueTooLarge", metadata: map[string]string{ "key": string(make([]byte, _maxMetadataValueSize+1)), }, expectError: true, errorSubstring: "exceeds the maximum of 524288 bytes", }, { name: "ValueExactlyMaxSize", metadata: map[string]string{ "key": string(make([]byte, _maxMetadataValueSize)), }, expectError: false, }, { name: "MultipleValidationErrors", metadata: func() map[string]string { metadata := makeMetadataWithKeys(_maxMetadataKeys + 1) longKey := string(make([]byte, _maxMetadataKeyLength+1)) metadata[longKey] = "value" return metadata }(), expectError: true, errorSubstring: "exceeds the maximum of 32", // First validation error }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := validateMetadata(tc.metadata) if tc.expectError { require.Error(t, err) require.Contains(t, err.Error(), tc.errorSubstring) } else { require.NoError(t, err) } }) } } func TestConvertResponse(t *testing.T) { testCases := []struct { name string input *store.AssignedState expectedResp *types.ExecutorHeartbeatResponse }{ { name: "Nil input", input: nil, expectedResp: &types.ExecutorHeartbeatResponse{ ShardAssignments: make(map[string]*types.ShardAssignment), MigrationMode: types.MigrationModeONBOARDED, }, }, { name: "Empty input", input: &store.AssignedState{ AssignedShards: make(map[string]*types.ShardAssignment), }, expectedResp: &types.ExecutorHeartbeatResponse{ ShardAssignments: make(map[string]*types.ShardAssignment), MigrationMode: types.MigrationModeONBOARDED, }, }, { name: "Populated input", input: &store.AssignedState{ AssignedShards: makeReadyAssignedShards("shard-1", "shard-2"), }, expectedResp: &types.ExecutorHeartbeatResponse{ ShardAssignments: makeReadyAssignedShards("shard-1", "shard-2"), MigrationMode: types.MigrationModeONBOARDED, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // In Go, you can't initialize a map in a struct to nil directly, // so we handle the nil case for ShardAssignments separately for comparison. if tc.expectedResp.ShardAssignments == nil { tc.expectedResp.ShardAssignments = make(map[string]*types.ShardAssignment) } res := _convertResponse(tc.input, types.MigrationModeONBOARDED) // Ensure ShardAssignments is not nil for comparison purposes if res.ShardAssignments == nil { res.ShardAssignments = make(map[string]*types.ShardAssignment) } require.Equal(t, tc.expectedResp, res) }) } } type configEntry struct { key dynamicproperties.Key value interface{} } func newConfig(t *testing.T, configEntries []configEntry) *config.Config { client := dynamicconfig.NewInMemoryClient() for _, entry := range configEntries { err := client.UpdateValue(entry.key, entry.value) if err != nil { t.Errorf("Failed to update config ") } } dc := dynamicconfig.NewCollection(client, testlogger.New(t)) return config.NewConfig(dc) } func TestFilterNewlyAssignedShardIDs(t *testing.T) { type testCase struct { name string previous *store.HeartbeatState assigned *store.AssignedState expected []string } tests := []testCase{ { name: "nil previousHeartbeat returns all assigned", previous: nil, assigned: &store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "shard1": {}, "shard2": {}, }, }, expected: []string{"shard1", "shard2"}, }, { name: "no new assigned shards", previous: &store.HeartbeatState{ ReportedShards: map[string]*types.ShardStatusReport{ "shard1": {}, "shard2": {}, }, }, assigned: &store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "shard1": {}, "shard2": {}, }, }, expected: []string{}, }, { name: "some new assigned shards", previous: &store.HeartbeatState{ ReportedShards: map[string]*types.ShardStatusReport{ "shard1": {}, }, }, assigned: &store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "shard1": {}, "shard2": {}, "shard3": {}, }, }, expected: []string{"shard2", "shard3"}, }, { name: "empty assigned returns empty", previous: &store.HeartbeatState{ ReportedShards: map[string]*types.ShardStatusReport{ "shard1": {}, }, }, assigned: &store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{}, }, expected: []string{}, }, { name: "nil assignedState returns nil", previous: &store.HeartbeatState{ ReportedShards: map[string]*types.ShardStatusReport{ "shard1": {}, }, }, assigned: nil, expected: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := filterNewlyAssignedShardIDs(tt.previous, tt.assigned) require.ElementsMatch(t, tt.expected, result) }) } } func TestEmitShardAssignmentMetrics(t *testing.T) { heartbeatTime := time.Now().UTC() namespace := "test-namespace" shardID := "shard-1" emptyHeartbeatState := &store.HeartbeatState{ReportedShards: map[string]*types.ShardStatusReport{}} type expectHandoverMetric struct { Latency time.Duration HandoverType types.HandoverType } type testCase struct { name string previousHeartbeat *store.HeartbeatState assignedState *store.AssignedState expectedDistributionLatency *time.Duration expectedHandoverLatencies []*expectHandoverMetric } testCases := []testCase{ { name: "no new assigned shards", previousHeartbeat: &store.HeartbeatState{ReportedShards: map[string]*types.ShardStatusReport{shardID: {}}}, assignedState: &store.AssignedState{AssignedShards: makeReadyAssignedShards(shardID)}, expectedDistributionLatency: nil, expectedHandoverLatencies: nil, }, { name: "newly assigned shard with handover stats", previousHeartbeat: emptyHeartbeatState, assignedState: &store.AssignedState{ AssignedShards: makeReadyAssignedShards(shardID), LastUpdated: heartbeatTime.Add(-10 * time.Second), ShardHandoverStats: map[string]store.ShardHandoverStats{ shardID: { PreviousExecutorLastHeartbeatTime: heartbeatTime.Add(-20 * time.Second), HandoverType: types.HandoverTypeGRACEFUL, }, }, }, expectedDistributionLatency: common.Ptr(10 * time.Second), expectedHandoverLatencies: []*expectHandoverMetric{{Latency: 20 * time.Second, HandoverType: types.HandoverTypeGRACEFUL}}, }, { name: "one assigned shard with no handover stats", previousHeartbeat: emptyHeartbeatState, assignedState: &store.AssignedState{ AssignedShards: makeReadyAssignedShards(shardID), LastUpdated: heartbeatTime.Add(-5 * time.Second), ShardHandoverStats: map[string]store.ShardHandoverStats{}, }, expectedDistributionLatency: common.Ptr(5 * time.Second), expectedHandoverLatencies: nil, }, { name: "one assigned shard with nil handover stats", previousHeartbeat: emptyHeartbeatState, assignedState: &store.AssignedState{ AssignedShards: makeReadyAssignedShards(shardID), LastUpdated: heartbeatTime.Add(-5 * time.Second), ShardHandoverStats: nil, }, expectedDistributionLatency: common.Ptr(5 * time.Second), expectedHandoverLatencies: nil, }, { name: "multiple newly assigned shards with handover stats", previousHeartbeat: emptyHeartbeatState, assignedState: &store.AssignedState{ AssignedShards: makeReadyAssignedShards("shard-1", "shard-2"), LastUpdated: heartbeatTime.Add(-15 * time.Second), ShardHandoverStats: map[string]store.ShardHandoverStats{ "shard-1": { PreviousExecutorLastHeartbeatTime: heartbeatTime.Add(-25 * time.Second), HandoverType: types.HandoverTypeGRACEFUL, }, "shard-2": { PreviousExecutorLastHeartbeatTime: heartbeatTime.Add(-30 * time.Second), HandoverType: types.HandoverTypeEMERGENCY, }, }, }, expectedDistributionLatency: common.Ptr(15 * time.Second), expectedHandoverLatencies: []*expectHandoverMetric{ {Latency: 30 * time.Second, HandoverType: types.HandoverTypeGRACEFUL}, {Latency: 25 * time.Second, HandoverType: types.HandoverTypeEMERGENCY}, }, }, { name: "multiple newly assigned shards with some handover stats", previousHeartbeat: emptyHeartbeatState, assignedState: &store.AssignedState{ AssignedShards: makeReadyAssignedShards("shard-1", "shard-2"), LastUpdated: heartbeatTime.Add(-15 * time.Second), ShardHandoverStats: map[string]store.ShardHandoverStats{ "shard-1": { PreviousExecutorLastHeartbeatTime: heartbeatTime.Add(-25 * time.Second), HandoverType: types.HandoverTypeGRACEFUL, }, }, }, expectedDistributionLatency: common.Ptr(15 * time.Second), expectedHandoverLatencies: []*expectHandoverMetric{{Latency: 25 * time.Second, HandoverType: types.HandoverTypeGRACEFUL}}, }, { name: "multiple newly assigned shards without handover stats", previousHeartbeat: emptyHeartbeatState, assignedState: &store.AssignedState{ AssignedShards: makeReadyAssignedShards("shard-1", "shard-2"), LastUpdated: heartbeatTime.Add(-15 * time.Second), }, expectedDistributionLatency: common.Ptr(15 * time.Second), expectedHandoverLatencies: nil, }, { name: "nil handover stats with new assigned shard", previousHeartbeat: emptyHeartbeatState, assignedState: &store.AssignedState{ AssignedShards: makeReadyAssignedShards(shardID), LastUpdated: heartbeatTime.Add(-5 * time.Second), ShardHandoverStats: nil, }, expectedDistributionLatency: common.Ptr(5 * time.Second), expectedHandoverLatencies: nil, }, { name: "newly assigned shard with previous heartbeat containing reported shards", previousHeartbeat: &store.HeartbeatState{ ReportedShards: map[string]*types.ShardStatusReport{ "shard-2": {}, }, }, assignedState: &store.AssignedState{ AssignedShards: makeReadyAssignedShards("shard-1", "shard-2"), LastUpdated: heartbeatTime.Add(-8 * time.Second), ShardHandoverStats: map[string]store.ShardHandoverStats{ "shard-1": { PreviousExecutorLastHeartbeatTime: heartbeatTime.Add(-18 * time.Second), HandoverType: types.HandoverTypeGRACEFUL, }, }, }, expectedDistributionLatency: common.Ptr(8 * time.Second), expectedHandoverLatencies: []*expectHandoverMetric{{Latency: 18 * time.Second, HandoverType: types.HandoverTypeGRACEFUL}}, }, { name: "multiple new assigned shards, previous heartbeat contains some", previousHeartbeat: &store.HeartbeatState{ ReportedShards: map[string]*types.ShardStatusReport{ "shard-2": {}, }, }, assignedState: &store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "shard-1": {Status: types.AssignmentStatusREADY}, "shard-2": {Status: types.AssignmentStatusREADY}, "shard-3": {Status: types.AssignmentStatusREADY}, }, LastUpdated: heartbeatTime.Add(-12 * time.Second), ShardHandoverStats: map[string]store.ShardHandoverStats{ "shard-1": { PreviousExecutorLastHeartbeatTime: heartbeatTime.Add(-22 * time.Second), HandoverType: types.HandoverTypeGRACEFUL, }, "shard-3": { PreviousExecutorLastHeartbeatTime: heartbeatTime.Add(-30 * time.Second), HandoverType: types.HandoverTypeEMERGENCY, }, }, }, expectedDistributionLatency: common.Ptr(12 * time.Second), expectedHandoverLatencies: []*expectHandoverMetric{ {Latency: 22 * time.Second, HandoverType: types.HandoverTypeGRACEFUL}, {Latency: 30 * time.Second, HandoverType: types.HandoverTypeEMERGENCY}, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() metricsClient := &metricmocks.Client{} metricsScope := &metricmocks.Scope{} if tc.expectedDistributionLatency != nil { metricsClient.On("Scope", metrics.ShardDistributorHeartbeatScope).Return(metricsScope).Once() metricsScope.On("Tagged", metrics.NamespaceTag(namespace)).Return(metricsScope).Once() metricsScope.On("RecordHistogramDuration", metrics.ShardDistributorShardAssignmentDistributionLatency, *tc.expectedDistributionLatency).Once() } if tc.expectedHandoverLatencies != nil { for _, expected := range tc.expectedHandoverLatencies { metricsScope.On("Tagged", metrics.HandoverTypeTag(expected.HandoverType.String())).Return(metricsScope) metricsScope.On("RecordHistogramDuration", metrics.ShardDistributorShardHandoverLatency, expected.Latency).Once() } } exec := &executor{metricsClient: metricsClient, logger: testlogger.New(t)} exec.emitShardAssignmentMetrics(namespace, heartbeatTime, tc.previousHeartbeat, tc.assignedState) metricsClient.AssertExpectations(t) metricsScope.AssertExpectations(t) }) } } // makeReadyAssignedShards is a helper function to create a map of shard assignments with READY status. func makeReadyAssignedShards(shardIDs ...string) map[string]*types.ShardAssignment { return makeAssignedShards(types.AssignmentStatusREADY, shardIDs...) } // makeAssignedShards is a helper function to create a map of shard assignments with the given status. func makeAssignedShards(status types.AssignmentStatus, shardIDs ...string) map[string]*types.ShardAssignment { assignedShards := make(map[string]*types.ShardAssignment) for _, shardID := range shardIDs { assignedShards[shardID] = &types.ShardAssignment{Status: status} } return assignedShards } ================================================ FILE: service/sharddistributor/handler/handler.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "context" "errors" "fmt" "slices" "sync" "time" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store" ) const ( // ephemeralBatchInterval is the time window over which GetShardOwner calls for // ephemeral namespaces are collected before being processed as a single batch. ephemeralBatchInterval = 100 * time.Millisecond // versionConflictRetryInitialInterval is the starting backoff for retries // triggered when a concurrent shard assignment causes a version conflict. versionConflictRetryInitialInterval = 50 * time.Millisecond // versionConflictRetryMaxInterval caps the per-attempt sleep. versionConflictRetryMaxInterval = 1 * time.Second // versionConflictRetryMaxAttempts is the maximum number of retry attempts // before the error is surfaced to the caller. versionConflictRetryMaxAttempts = 3 ) func NewHandler( logger log.Logger, timeSource clock.TimeSource, shardDistributionCfg config.ShardDistribution, storage store.Store, ) Handler { handler := &handlerImpl{ logger: logger, shardDistributionCfg: shardDistributionCfg, storage: storage, } handler.batcher = newShardBatcher(timeSource, ephemeralBatchInterval, handler.assignEphemeralBatch) // prevent us from trying to serve requests before shard distributor is started and ready handler.startWG.Add(1) return handler } type handlerImpl struct { logger log.Logger startWG sync.WaitGroup storage store.Store shardDistributionCfg config.ShardDistribution batcher *shardBatcher } func (h *handlerImpl) Start() { h.batcher.Start() h.startWG.Done() } func (h *handlerImpl) Stop() { h.batcher.Stop() } func (h *handlerImpl) Health(ctx context.Context) (*types.HealthStatus, error) { h.startWG.Wait() h.logger.Debug("Shard Distributor service health check endpoint reached.") hs := &types.HealthStatus{Ok: true, Msg: "shard distributor good"} return hs, nil } func (h *handlerImpl) GetShardOwner(ctx context.Context, request *types.GetShardOwnerRequest) (resp *types.GetShardOwnerResponse, retError error) { defer func() { log.CapturePanic(recover(), h.logger, &retError) }() namespaceIdx := slices.IndexFunc(h.shardDistributionCfg.Namespaces, func(namespace config.Namespace) bool { return namespace.Name == request.Namespace }) if namespaceIdx == -1 { return nil, &types.NamespaceNotFoundError{ Namespace: request.Namespace, } } shardOwner, err := h.storage.GetShardOwner(ctx, request.Namespace, request.ShardKey) if errors.Is(err, store.ErrShardNotFound) { if h.shardDistributionCfg.Namespaces[namespaceIdx].Type == config.NamespaceTypeEphemeral { return h.getOrAssignEphemeralShard(ctx, request) } return nil, &types.ShardNotFoundError{ Namespace: request.Namespace, ShardKey: request.ShardKey, } } if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("failed to get shard owner: %v", err)} } return &types.GetShardOwnerResponse{ Owner: shardOwner.ExecutorID, Metadata: shardOwner.Metadata, Namespace: request.Namespace, }, nil } // getOrAssignEphemeralShard assigns an ephemeral shard that does not yet exist // in storage. It submits the request to the batcher and, on a version conflict // (concurrent assignment by another goroutine), retries with exponential backoff. // Each retry re-reads storage first: if the concurrent writer already committed // the assignment we return it immediately without re-submitting to the batcher. func (h *handlerImpl) getOrAssignEphemeralShard(ctx context.Context, request *types.GetShardOwnerRequest) (*types.GetShardOwnerResponse, error) { retryPolicy := backoff.NewExponentialRetryPolicy(versionConflictRetryInitialInterval) retryPolicy.SetMaximumInterval(versionConflictRetryMaxInterval) retryPolicy.SetMaximumAttempts(versionConflictRetryMaxAttempts) throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(func(err error) bool { return errors.Is(err, store.ErrVersionConflict) }), ) var resp *types.GetShardOwnerResponse isRetry := false err := throttleRetry.Do(ctx, func(ctx context.Context) error { if isRetry { // A concurrent batch won the race. Re-read storage first: if the // winner already committed our shard's assignment we can return // immediately without re-submitting to the batcher. owner, err := h.storage.GetShardOwner(ctx, request.Namespace, request.ShardKey) if err != nil && !errors.Is(err, store.ErrShardNotFound) { return &types.InternalServiceError{Message: fmt.Sprintf("failed to get shard owner: %v", err)} } if err == nil { resp = &types.GetShardOwnerResponse{ Owner: owner.ExecutorID, Metadata: owner.Metadata, Namespace: request.Namespace, } return nil } } isRetry = true // Submit to the batcher to assign the shard. var err error resp, err = h.batcher.Submit(ctx, request) return err }) if err != nil { return nil, &types.InternalServiceError{Message: fmt.Sprintf("failed to assign ephemeral shard: %v", err)} } return resp, nil } func (h *handlerImpl) WatchNamespaceState(request *types.WatchNamespaceStateRequest, server WatchNamespaceStateServer) error { h.startWG.Wait() // Subscribe to state changes from storage assignmentChangesChan, unSubscribe, err := h.storage.SubscribeToAssignmentChanges(server.Context(), request.Namespace) defer unSubscribe() if err != nil { return &types.InternalServiceError{Message: fmt.Sprintf("failed to subscribe to namespace state: %v", err)} } // Stream subsequent updates for { select { case <-server.Context().Done(): return server.Context().Err() case assignmentChanges, ok := <-assignmentChangesChan: if !ok { return fmt.Errorf("unexpected close of updates channel") } response := &types.WatchNamespaceStateResponse{ Executors: make([]*types.ExecutorShardAssignment, 0, len(assignmentChanges)), } for executor, shardIDs := range assignmentChanges { response.Executors = append(response.Executors, &types.ExecutorShardAssignment{ ExecutorID: executor.ExecutorID, AssignedShards: WrapShards(shardIDs), Metadata: executor.Metadata, }) } err = server.Send(response) if err != nil { return fmt.Errorf("send response: %w", err) } } } } func WrapShards(shardIDs []string) []*types.Shard { shards := make([]*types.Shard, 0, len(shardIDs)) for _, shardID := range shardIDs { shards = append(shards, &types.Shard{ShardKey: shardID}) } return shards } ================================================ FILE: service/sharddistributor/handler/handler_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "context" "errors" "fmt" "sync" "testing" "time" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store" ) const ( _testNamespaceFixed = "test-fixed" _testNamespaceEphemeral = "test-ephemeral" ) // newTestHandler creates a handlerImpl wired with a real shardBatcher backed by // the provided store mock. The batcher is started and the handler is returned // ready to use; callers should call Stop() when done. func newTestHandler(t *testing.T, cfg config.ShardDistribution, mockStore *store.MockStore) *handlerImpl { t.Helper() handler := &handlerImpl{ logger: testlogger.New(t), shardDistributionCfg: cfg, storage: mockStore, } handler.batcher = newShardBatcher(clock.NewRealTimeSource(), 10*time.Millisecond, handler.assignEphemeralBatch) handler.batcher.Start() t.Cleanup(handler.batcher.Stop) return handler } func TestGetShardOwner(t *testing.T) { cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ { Name: _testNamespaceFixed, Type: config.NamespaceTypeFixed, ShardNum: 32, }, { Name: _testNamespaceEphemeral, Type: config.NamespaceTypeEphemeral, }, }, } tests := []struct { name string request *types.GetShardOwnerRequest setupMocks func(mockStore *store.MockStore) expectedOwner string expectedError bool expectedErrMsg string }{ { name: "InvalidNamespace", request: &types.GetShardOwnerRequest{ Namespace: "namespace not found invalidNamespace", ShardKey: "1", }, expectedError: true, expectedErrMsg: "namespace not found", }, { name: "LookupError", request: &types.GetShardOwnerRequest{ Namespace: _testNamespaceFixed, ShardKey: "1", }, setupMocks: func(mockStore *store.MockStore) { mockStore.EXPECT().GetShardOwner(gomock.Any(), _testNamespaceFixed, "1").Return(nil, errors.New("lookup error")) }, expectedError: true, expectedErrMsg: "lookup error", }, { name: "Existing_Success_Fixed", request: &types.GetShardOwnerRequest{ Namespace: _testNamespaceFixed, ShardKey: "123", }, setupMocks: func(mockStore *store.MockStore) { mockStore.EXPECT().GetShardOwner(gomock.Any(), _testNamespaceFixed, "123").Return(&store.ShardOwner{ ExecutorID: "owner1", Metadata: map[string]string{"ip": "127.0.0.1", "port": "1234"}, }, nil) }, expectedOwner: "owner1", expectedError: false, }, { name: "ShardNotFound_Fixed", request: &types.GetShardOwnerRequest{ Namespace: _testNamespaceFixed, ShardKey: "NON-EXISTING-SHARD", }, setupMocks: func(mockStore *store.MockStore) { mockStore.EXPECT().GetShardOwner(gomock.Any(), _testNamespaceFixed, "NON-EXISTING-SHARD").Return(nil, store.ErrShardNotFound) }, expectedError: true, expectedErrMsg: "shard not found", }, { name: "Existing_Success_Ephemeral", request: &types.GetShardOwnerRequest{ Namespace: _testNamespaceEphemeral, ShardKey: "123", }, setupMocks: func(mockStore *store.MockStore) { mockStore.EXPECT().GetShardOwner(gomock.Any(), _testNamespaceEphemeral, "123").Return(&store.ShardOwner{ ExecutorID: "owner1", Metadata: map[string]string{"ip": "127.0.0.1", "port": "1234"}, }, nil) }, expectedOwner: "owner1", expectedError: false, }, { // ShardNotFound for an ephemeral namespace routes to the batcher, which // calls assignEphemeralBatch. This case validates the routing only; // detailed assignment behaviour is covered in TestAssignEphemeralBatch. name: "ShardNotFound_Ephemeral_RoutesToBatcher", request: &types.GetShardOwnerRequest{ Namespace: _testNamespaceEphemeral, ShardKey: "NON-EXISTING-SHARD", }, setupMocks: func(mockStore *store.MockStore) { mockStore.EXPECT().GetShardOwner(gomock.Any(), _testNamespaceEphemeral, "NON-EXISTING-SHARD").Return(nil, store.ErrShardNotFound) mockStore.EXPECT().GetState(gomock.Any(), _testNamespaceEphemeral).Return(&store.NamespaceState{ Executors: map[string]store.HeartbeatState{"owner1": {Status: types.ExecutorStatusACTIVE}}, ShardAssignments: map[string]store.AssignedState{"owner1": {AssignedShards: map[string]*types.ShardAssignment{}}}, }, nil) mockStore.EXPECT().AssignShards(gomock.Any(), _testNamespaceEphemeral, gomock.Any(), gomock.Any()).Return(nil) mockStore.EXPECT().GetExecutor(gomock.Any(), _testNamespaceEphemeral, "owner1").Return(&store.ShardOwner{ ExecutorID: "owner1", Metadata: map[string]string{"ip": "127.0.0.1", "port": "1234"}, }, nil) }, expectedOwner: "owner1", expectedError: false, }, { // A version conflict from AssignShards causes the batcher to return // ErrVersionConflict. getOrAssignEphemeralShard re-reads storage on // retry; here the concurrent winner has already written the assignment // so the second GetShardOwner call succeeds and no second batcher // submission is required. name: "Ephemeral_VersionConflict_ResolvedByStorageRead", request: &types.GetShardOwnerRequest{ Namespace: _testNamespaceEphemeral, ShardKey: "NON-EXISTING-SHARD", }, setupMocks: func(mockStore *store.MockStore) { // Initial lookup — shard absent. mockStore.EXPECT().GetShardOwner(gomock.Any(), _testNamespaceEphemeral, "NON-EXISTING-SHARD"). Return(nil, store.ErrShardNotFound) // Batcher fires: GetState + AssignShards returns a version conflict. mockStore.EXPECT().GetState(gomock.Any(), _testNamespaceEphemeral).Return(&store.NamespaceState{ Executors: map[string]store.HeartbeatState{"owner1": {Status: types.ExecutorStatusACTIVE}}, ShardAssignments: map[string]store.AssignedState{"owner1": {AssignedShards: map[string]*types.ShardAssignment{}}}, }, nil) mockStore.EXPECT().AssignShards(gomock.Any(), _testNamespaceEphemeral, gomock.Any(), gomock.Any()). Return(store.ErrVersionConflict) // Retry: re-read finds the shard already assigned by the concurrent winner. mockStore.EXPECT().GetShardOwner(gomock.Any(), _testNamespaceEphemeral, "NON-EXISTING-SHARD"). Return(&store.ShardOwner{ ExecutorID: "owner1", Metadata: map[string]string{"ip": "127.0.0.1", "port": "1234"}, }, nil) }, expectedOwner: "owner1", expectedError: false, }, { // A version conflict from AssignShards is retried; on the retry // GetShardOwner still returns ErrShardNotFound, so the batcher is // re-submitted and this time AssignShards succeeds. name: "Ephemeral_VersionConflict_RetriedAndSucceeds", request: &types.GetShardOwnerRequest{ Namespace: _testNamespaceEphemeral, ShardKey: "NON-EXISTING-SHARD", }, setupMocks: func(mockStore *store.MockStore) { // Initial lookup — shard absent. mockStore.EXPECT().GetShardOwner(gomock.Any(), _testNamespaceEphemeral, "NON-EXISTING-SHARD"). Return(nil, store.ErrShardNotFound) // First batcher attempt: version conflict. mockStore.EXPECT().GetState(gomock.Any(), _testNamespaceEphemeral).Return(&store.NamespaceState{ Executors: map[string]store.HeartbeatState{"owner1": {Status: types.ExecutorStatusACTIVE}}, ShardAssignments: map[string]store.AssignedState{"owner1": {AssignedShards: map[string]*types.ShardAssignment{}}}, }, nil) mockStore.EXPECT().AssignShards(gomock.Any(), _testNamespaceEphemeral, gomock.Any(), gomock.Any()). Return(fmt.Errorf("assign ephemeral shards: %w", store.ErrVersionConflict)) // Retry re-read — shard still absent, so batcher is submitted again. mockStore.EXPECT().GetShardOwner(gomock.Any(), _testNamespaceEphemeral, "NON-EXISTING-SHARD"). Return(nil, store.ErrShardNotFound) // Second batcher attempt: succeeds. mockStore.EXPECT().GetState(gomock.Any(), _testNamespaceEphemeral).Return(&store.NamespaceState{ Executors: map[string]store.HeartbeatState{"owner1": {Status: types.ExecutorStatusACTIVE}}, ShardAssignments: map[string]store.AssignedState{"owner1": {AssignedShards: map[string]*types.ShardAssignment{}}}, }, nil) mockStore.EXPECT().AssignShards(gomock.Any(), _testNamespaceEphemeral, gomock.Any(), gomock.Any()).Return(nil) mockStore.EXPECT().GetExecutor(gomock.Any(), _testNamespaceEphemeral, "owner1").Return(&store.ShardOwner{ ExecutorID: "owner1", Metadata: map[string]string{"ip": "127.0.0.1", "port": "1234"}, }, nil) }, expectedOwner: "owner1", expectedError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockStorage := store.NewMockStore(ctrl) handler := newTestHandler(t, cfg, mockStorage) if tt.setupMocks != nil { tt.setupMocks(mockStorage) } resp, err := handler.GetShardOwner(context.Background(), tt.request) if tt.expectedError { require.Error(t, err) require.Contains(t, err.Error(), tt.expectedErrMsg) require.Nil(t, resp) } else { require.NoError(t, err) require.Equal(t, tt.expectedOwner, resp.Owner) require.Equal(t, tt.request.Namespace, resp.Namespace) expectedMetadata := map[string]string{"ip": "127.0.0.1", "port": "1234"} require.Equal(t, expectedMetadata, resp.Metadata) } }) } } func TestWatchNamespaceState(t *testing.T) { ctrl := gomock.NewController(t) logger := testlogger.New(t) mockStorage := store.NewMockStore(ctrl) mockServer := NewMockWatchNamespaceStateServer(ctrl) cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ {Name: "test-ns", Type: config.NamespaceTypeFixed, ShardNum: 2}, }, } handler := &handlerImpl{ logger: logger, shardDistributionCfg: cfg, storage: mockStorage, startWG: sync.WaitGroup{}, } ctx, cancel := context.WithCancel(context.Background()) updatesChan := make(chan map[*store.ShardOwner][]string, 1) unsubscribe := func() { close(updatesChan) } mockServer.EXPECT().Context().Return(ctx).AnyTimes() mockStorage.EXPECT().SubscribeToAssignmentChanges(gomock.Any(), "test-ns").Return(updatesChan, unsubscribe, nil) // Expect update send mockServer.EXPECT().Send(gomock.Any()).DoAndReturn(func(resp *types.WatchNamespaceStateResponse) error { require.Len(t, resp.Executors, 1) require.Equal(t, "executor-1", resp.Executors[0].ExecutorID) return nil }) // Send update, then cancel go func() { time.Sleep(10 * time.Millisecond) updatesChan <- map[*store.ShardOwner][]string{ {ExecutorID: "executor-1", Metadata: map[string]string{}}: {"shard-1"}, } cancel() }() err := handler.WatchNamespaceState(&types.WatchNamespaceStateRequest{Namespace: "test-ns"}, mockServer) require.Error(t, err) require.ErrorIs(t, err, context.Canceled) } ================================================ FILE: service/sharddistributor/handler/interfaces.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package handler import ( "context" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination interfaces_mock.go //go:generate gowrap gen -g -p . -i Handler -t ../../templates/grpc.tmpl -o ../wrappers/grpc/grpc_handler_generated.go -v handler=GRPC -v package=sharddistributorv1 -v path=github.com/uber/cadence/.gen/proto/sharddistributor/v1 -v prefix=ShardDistributor //go:generate gowrap gen -g -p . -i Executor -t ../../templates/grpc.tmpl -o ../wrappers/grpc/grpc_executor_generated.go -v handler=ExecutorGRPC -v package=sharddistributorv1 -v path=github.com/uber/cadence/.gen/proto/sharddistributor/v1 -v prefix=ShardDistributorExecutor //go:generate gowrap gen -g -p . -i Handler -t ../templates/metered.tmpl -o ../wrappers/metered/api_generated.go -v handler=Metrics //go:generate gowrap gen -g -p . -i Executor -t ../templates/metered.tmpl -o ../wrappers/metered/executor_generated.go -v handler=ExecutorMetrics // Handler is the interface for shard distributor handler type Handler interface { common.Daemon Health(context.Context) (*types.HealthStatus, error) GetShardOwner(context.Context, *types.GetShardOwnerRequest) (*types.GetShardOwnerResponse, error) WatchNamespaceState(*types.WatchNamespaceStateRequest, WatchNamespaceStateServer) error } type Executor interface { Heartbeat(context.Context, *types.ExecutorHeartbeatRequest) (*types.ExecutorHeartbeatResponse, error) } type WatchNamespaceStateServer interface { Context() context.Context Send(*types.WatchNamespaceStateResponse) error } ================================================ FILE: service/sharddistributor/handler/interfaces_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go // // Generated by this command: // // mockgen -package handler -source interfaces.go -destination interfaces_mock.go // // Package handler is a generated GoMock package. package handler import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" types "github.com/uber/cadence/common/types" ) // MockHandler is a mock of Handler interface. type MockHandler struct { ctrl *gomock.Controller recorder *MockHandlerMockRecorder isgomock struct{} } // MockHandlerMockRecorder is the mock recorder for MockHandler. type MockHandlerMockRecorder struct { mock *MockHandler } // NewMockHandler creates a new mock instance. func NewMockHandler(ctrl *gomock.Controller) *MockHandler { mock := &MockHandler{ctrl: ctrl} mock.recorder = &MockHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHandler) EXPECT() *MockHandlerMockRecorder { return m.recorder } // GetShardOwner mocks base method. func (m *MockHandler) GetShardOwner(arg0 context.Context, arg1 *types.GetShardOwnerRequest) (*types.GetShardOwnerResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardOwner", arg0, arg1) ret0, _ := ret[0].(*types.GetShardOwnerResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetShardOwner indicates an expected call of GetShardOwner. func (mr *MockHandlerMockRecorder) GetShardOwner(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardOwner", reflect.TypeOf((*MockHandler)(nil).GetShardOwner), arg0, arg1) } // Health mocks base method. func (m *MockHandler) Health(arg0 context.Context) (*types.HealthStatus, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Health", arg0) ret0, _ := ret[0].(*types.HealthStatus) ret1, _ := ret[1].(error) return ret0, ret1 } // Health indicates an expected call of Health. func (mr *MockHandlerMockRecorder) Health(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockHandler)(nil).Health), arg0) } // Start mocks base method. func (m *MockHandler) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockHandlerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockHandler)(nil).Start)) } // Stop mocks base method. func (m *MockHandler) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockHandlerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockHandler)(nil).Stop)) } // WatchNamespaceState mocks base method. func (m *MockHandler) WatchNamespaceState(arg0 *types.WatchNamespaceStateRequest, arg1 WatchNamespaceStateServer) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WatchNamespaceState", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // WatchNamespaceState indicates an expected call of WatchNamespaceState. func (mr *MockHandlerMockRecorder) WatchNamespaceState(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WatchNamespaceState", reflect.TypeOf((*MockHandler)(nil).WatchNamespaceState), arg0, arg1) } // MockExecutor is a mock of Executor interface. type MockExecutor struct { ctrl *gomock.Controller recorder *MockExecutorMockRecorder isgomock struct{} } // MockExecutorMockRecorder is the mock recorder for MockExecutor. type MockExecutorMockRecorder struct { mock *MockExecutor } // NewMockExecutor creates a new mock instance. func NewMockExecutor(ctrl *gomock.Controller) *MockExecutor { mock := &MockExecutor{ctrl: ctrl} mock.recorder = &MockExecutorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockExecutor) EXPECT() *MockExecutorMockRecorder { return m.recorder } // Heartbeat mocks base method. func (m *MockExecutor) Heartbeat(arg0 context.Context, arg1 *types.ExecutorHeartbeatRequest) (*types.ExecutorHeartbeatResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Heartbeat", arg0, arg1) ret0, _ := ret[0].(*types.ExecutorHeartbeatResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Heartbeat indicates an expected call of Heartbeat. func (mr *MockExecutorMockRecorder) Heartbeat(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Heartbeat", reflect.TypeOf((*MockExecutor)(nil).Heartbeat), arg0, arg1) } // MockWatchNamespaceStateServer is a mock of WatchNamespaceStateServer interface. type MockWatchNamespaceStateServer struct { ctrl *gomock.Controller recorder *MockWatchNamespaceStateServerMockRecorder isgomock struct{} } // MockWatchNamespaceStateServerMockRecorder is the mock recorder for MockWatchNamespaceStateServer. type MockWatchNamespaceStateServerMockRecorder struct { mock *MockWatchNamespaceStateServer } // NewMockWatchNamespaceStateServer creates a new mock instance. func NewMockWatchNamespaceStateServer(ctrl *gomock.Controller) *MockWatchNamespaceStateServer { mock := &MockWatchNamespaceStateServer{ctrl: ctrl} mock.recorder = &MockWatchNamespaceStateServerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWatchNamespaceStateServer) EXPECT() *MockWatchNamespaceStateServerMockRecorder { return m.recorder } // Context mocks base method. func (m *MockWatchNamespaceStateServer) Context() context.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Context") ret0, _ := ret[0].(context.Context) return ret0 } // Context indicates an expected call of Context. func (mr *MockWatchNamespaceStateServerMockRecorder) Context() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockWatchNamespaceStateServer)(nil).Context)) } // Send mocks base method. func (m *MockWatchNamespaceStateServer) Send(arg0 *types.WatchNamespaceStateResponse) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Send", arg0) ret0, _ := ret[0].(error) return ret0 } // Send indicates an expected call of Send. func (mr *MockWatchNamespaceStateServerMockRecorder) Send(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockWatchNamespaceStateServer)(nil).Send), arg0) } ================================================ FILE: service/sharddistributor/leader/election/election.go ================================================ package election import ( "context" "errors" "fmt" "math/rand" "time" "go.uber.org/fx" "go.uber.org/multierr" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/leader/process" "github.com/uber/cadence/service/sharddistributor/store" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination=election_mock.go Factory,Elector var errSelfResign = fmt.Errorf("self-resigned") // Module provides election factory for fx app. var Module = fx.Module( "leader-election", fx.Provide(NewElectionFactory), ) type ProcessFunc func(ctx context.Context) error // Elector handles leader election for a specific namespace type Elector interface { Run(ctx context.Context) <-chan bool } // Factory creates elector instances type Factory interface { CreateElector(ctx context.Context, namespaceCfg config.Namespace) (Elector, error) } type electionFactory struct { hostname string cfg config.Election leaderStore store.Elector store store.Store logger log.Logger serviceID string clock clock.TimeSource processFactory process.Factory } type elector struct { hostname string namespace config.Namespace leaderStore store.Elector store store.Store logger log.Logger cfg config.Election leaderStarted time.Time clock clock.TimeSource processFactory process.Factory } type FactoryParams struct { fx.In HostName string `name:"hostname"` Cfg config.ShardDistribution LeaderStore store.Elector Logger log.Logger Clock clock.TimeSource ProcessFactory process.Factory Store store.Store } // NewElectionFactory creates a new election factory func NewElectionFactory(p FactoryParams) Factory { return &electionFactory{ cfg: p.Cfg.Election, leaderStore: p.LeaderStore, store: p.Store, logger: p.Logger, clock: p.Clock, hostname: p.HostName, processFactory: p.ProcessFactory, } } // CreateElector creates a new elector for the given namespace func (f *electionFactory) CreateElector(ctx context.Context, namespaceCfg config.Namespace) (Elector, error) { return &elector{ namespace: namespaceCfg, leaderStore: f.leaderStore, store: f.store, logger: f.logger.WithTags(tag.ComponentLeaderElection, tag.ShardNamespace(namespaceCfg.Name)), cfg: f.cfg, clock: f.clock, hostname: f.hostname, processFactory: f.processFactory, }, nil } // Run starts the leader election process it returns a channel that will return the value if the current instance becomes the leader or resigns from leadership. func (e *elector) Run(ctx context.Context) <-chan bool { leaderCh := make(chan bool, 1) // Create a child context that we can explicitly cancel when errors occur runCtx, cancelRun := context.WithCancel(ctx) go func() { defer close(leaderCh) defer cancelRun() // Ensure child context is canceled on exit defer func() { e.logger.Info("Leader election process exiting") if r := recover(); r != nil { e.logger.Error("Panic in election process", tag.Value(r)) } }() for { if err := e.runElection(runCtx, leaderCh); err != nil { // Check if parent context is already canceled if runCtx.Err() != nil { e.logger.Info("Context canceled, stopping election loop", tag.Error(runCtx.Err())) return } // Self resign, immediately retry, otherwise, wait if !errors.Is(err, errSelfResign) { e.logger.Error("Error in election, retrying", tag.Error(err)) select { case <-runCtx.Done(): e.logger.Info("Context canceled, stopping election loop, exit immediately", tag.Error(runCtx.Err())) return // Context was canceled, exit immediately case <-e.clock.After(e.cfg.FailedElectionCooldown): e.logger.Info("Cooldown period ended, retrying election") // Continue after cooldown } } } if runCtx.Err() != nil { e.logger.Info("Context canceled, stopping election loop", tag.Error(runCtx.Err())) break } } }() return leaderCh } // runElection runs a single election attempt func (e *elector) runElection(ctx context.Context, leaderCh chan<- bool) (err error) { e.logger.Info("Run election") // Add random delay before campaigning to spread load across instances delay := time.Duration(rand.Intn(int(e.cfg.MaxRandomDelay))) e.logger.Debug("Adding random delay before campaigning", tag.ElectionDelay(delay)) select { case <-e.clock.After(delay): e.logger.Debug("Random delay before campaigning completed") // Continue after delay case <-ctx.Done(): return fmt.Errorf("context cancelled during pre-campaign delay: %w", ctx.Err()) } e.logger.Info("Creating election") election, err := e.leaderStore.CreateElection(ctx, e.namespace.Name) if err != nil { return fmt.Errorf("create session: %w", err) } var leaderProcess process.Processor defer func() { resignErr := e.resign(election, leaderProcess) if resignErr != nil { if err == nil { err = resignErr } else { // Something already went wrong, so the process is most likely not started or stopped. // It should be safe to report leadership lost. e.logger.Error("Error resigning leader", tag.Error(resignErr)) } } // We are no longer a leader and OnResign is called - notify the manager that leader elected process leaderCh <- false }() e.logger.Info("Starting campaign for leader") // Campaign to become leader if err := election.Campaign(ctx, e.hostname); err != nil { return fmt.Errorf("failed to campaign: %w", err) } leaderProcess = e.processFactory.CreateProcessor(e.namespace, e.store, election) e.logger.Debug("Run leader process") err = leaderProcess.Run(ctx) if err != nil { return fmt.Errorf("onLeader: %w", err) } // Successfully became leader e.leaderStarted = e.clock.Now() leaderCh <- true e.logger.Info("Became leader") // Start a timer to voluntarily resign after the leadership period leaderTimer := e.clock.NewTimer(e.cfg.LeaderPeriod) defer leaderTimer.Stop() // Watch for session expiration, context cancellation, or timer expiration select { case <-ctx.Done(): e.logger.Info("Context cancelled while leader") return nil case <-election.Done(): e.logger.Info("Session expired while leader") return fmt.Errorf("session expired") case <-leaderTimer.Chan(): e.logger.Info("Leadership period ended, voluntarily resigning") return errSelfResign } } func (e *elector) resign(election store.Election, processor process.Processor) error { ctx, cancel := e.clock.ContextWithTimeout(context.Background(), 3*time.Second) defer cancel() var resignErr error if processor != nil { // First try to terminate the processor to stop leader processing if err := processor.Terminate(ctx); err != nil { resignErr = fmt.Errorf("terminate processor: %w", err) } } // Then try to resign leadership if err := election.Resign(ctx); err != nil { resignErr = multierr.Append(resignErr, fmt.Errorf("resign election: %w", err)) } // Finally, try to clean up the election // to be sure all resources are released if err := election.Cleanup(ctx); err != nil { resignErr = multierr.Append(resignErr, fmt.Errorf("cleanup election: %w", err)) } return resignErr } ================================================ FILE: service/sharddistributor/leader/election/election_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: election.go // // Generated by this command: // // mockgen -package election -source election.go -destination=election_mock.go Factory,Elector // // Package election is a generated GoMock package. package election import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" config "github.com/uber/cadence/service/sharddistributor/config" ) // MockElector is a mock of Elector interface. type MockElector struct { ctrl *gomock.Controller recorder *MockElectorMockRecorder isgomock struct{} } // MockElectorMockRecorder is the mock recorder for MockElector. type MockElectorMockRecorder struct { mock *MockElector } // NewMockElector creates a new mock instance. func NewMockElector(ctrl *gomock.Controller) *MockElector { mock := &MockElector{ctrl: ctrl} mock.recorder = &MockElectorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockElector) EXPECT() *MockElectorMockRecorder { return m.recorder } // Run mocks base method. func (m *MockElector) Run(ctx context.Context) <-chan bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Run", ctx) ret0, _ := ret[0].(<-chan bool) return ret0 } // Run indicates an expected call of Run. func (mr *MockElectorMockRecorder) Run(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockElector)(nil).Run), ctx) } // MockFactory is a mock of Factory interface. type MockFactory struct { ctrl *gomock.Controller recorder *MockFactoryMockRecorder isgomock struct{} } // MockFactoryMockRecorder is the mock recorder for MockFactory. type MockFactoryMockRecorder struct { mock *MockFactory } // NewMockFactory creates a new mock instance. func NewMockFactory(ctrl *gomock.Controller) *MockFactory { mock := &MockFactory{ctrl: ctrl} mock.recorder = &MockFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFactory) EXPECT() *MockFactoryMockRecorder { return m.recorder } // CreateElector mocks base method. func (m *MockFactory) CreateElector(ctx context.Context, namespaceCfg config.Namespace) (Elector, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateElector", ctx, namespaceCfg) ret0, _ := ret[0].(Elector) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateElector indicates an expected call of CreateElector. func (mr *MockFactoryMockRecorder) CreateElector(ctx, namespaceCfg any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateElector", reflect.TypeOf((*MockFactory)(nil).CreateElector), ctx, namespaceCfg) } ================================================ FILE: service/sharddistributor/leader/election/election_test.go ================================================ package election import ( "context" "errors" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/leader/process" "github.com/uber/cadence/service/sharddistributor/store" ) const ( _testHost = "localhost" ) var ( _testNamespace = config.Namespace{Name: "test-namespace"} ) var ( _testLeaderPeriod = time.Minute _testMaxRandomDelay = time.Second _testFailedElectionCooldown = 10 * time.Second ) func TestElector_Run(t *testing.T) { goleak.VerifyNone(t) ctrl := gomock.NewController(t) logger := testlogger.New(t) timeSource := clock.NewMockedTimeSource() election := store.NewMockElection(ctrl) election.EXPECT().Campaign(gomock.Any(), _testHost).Return(nil) election.EXPECT().Done().Return(make(chan struct{})) finished := make(chan struct{}) // once test is done cleanup will be called election.EXPECT().Resign(gomock.Any()).DoAndReturn(func(_ context.Context) error { close(finished) return nil }) election.EXPECT().Cleanup(gomock.Any()).Return(nil) leaderStore := store.NewMockElector(ctrl) leaderStore.EXPECT().CreateElection(gomock.Any(), _testNamespace.Name).Return(election, nil) shardStore := store.NewMockStore(ctrl) processFactory := process.NewMockFactory(ctrl) processRunner := process.NewMockProcessor(ctrl) processFactory.EXPECT().CreateProcessor(_testNamespace, shardStore, election).Return(processRunner) factory := NewElectionFactory(FactoryParams{ HostName: _testHost, Cfg: config.ShardDistribution{ Election: config.Election{ LeaderPeriod: _testLeaderPeriod, MaxRandomDelay: _testMaxRandomDelay, FailedElectionCooldown: _testFailedElectionCooldown, }, }, LeaderStore: leaderStore, Store: shardStore, Logger: logger, Clock: timeSource, ProcessFactory: processFactory, }) el, err := factory.CreateElector(context.Background(), _testNamespace) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Track callback executions onLeaderCalled := false onResignCalled := false processRunner.EXPECT().Run(gomock.Any()).DoAndReturn(func(_ context.Context) error { onLeaderCalled = true return nil }) processRunner.EXPECT().Terminate(gomock.Any()).DoAndReturn(func(ctx context.Context) error { onResignCalled = true return nil }) go func() { // Wait until run will stop on timer timeSource.BlockUntil(1) // Advance the time to kick in the election. timeSource.Advance(_testMaxRandomDelay) }() leaderChan := el.Run(ctx) assert.True(t, <-leaderChan) assert.True(t, onLeaderCalled, "OnLeader callback should have been called") assert.False(t, onResignCalled, "OnResign callback should not have been called") cancel() <-finished } func TestElector_Run_Resign(t *testing.T) { goleak.VerifyNone(t) t.Run("context_canceled", func(t *testing.T) { leaderChan, p := prepareRun(t, nil, nil) p.election.EXPECT().Resign(gomock.Any()).Return(nil) p.cancel() assert.False(t, <-leaderChan) // Wait for the goroutine to exit for range leaderChan { } }) t.Run("session_expired", func(t *testing.T) { leaderChan, p := prepareRun(t, nil, nil) p.election.EXPECT().Resign(gomock.Any()).Return(nil) close(p.electionCh) assert.False(t, <-leaderChan) p.cancel() // Wait for the goroutine to exit for range leaderChan { } }) t.Run("leader_resign", func(t *testing.T) { // Verify onResign is called before resignation onResignCalled := false leaderChan, p := prepareRun(t, nil, func(ctx context.Context) error { onResignCalled = true return nil }) // We should be blocked on the timer. p.timeSource.BlockUntil(1) p.election.EXPECT().Resign(gomock.Any()).DoAndReturn(func(ctx context.Context) error { assert.True(t, onResignCalled, "OnResign callback should be called before Resign") return nil }) p.timeSource.Advance(_testLeaderPeriod + 1) p.timeSource.BlockUntil(1) assert.False(t, <-leaderChan) p.cancel() // Wait for the goroutine to exit for range leaderChan { } }) t.Run("onResign_error", func(t *testing.T) { // Set onResign to return an error onResignCalled := false resignErr := errors.New("resign error") onResign := func(ctx context.Context) error { onResignCalled = true return resignErr } leaderChan, p := prepareRun(t, nil, onResign) p.election.EXPECT().Resign(gomock.Any()).Return(nil) // We should be blocked on the timer. p.timeSource.BlockUntil(1) // The resign function on election should not be called if onResign returns an error p.timeSource.Advance(_testLeaderPeriod + 1) p.timeSource.BlockUntil(1) assert.False(t, <-leaderChan) assert.True(t, onResignCalled, "OnResign callback should have been called") p.cancel() // Wait for the goroutine to exit for range leaderChan { } }) t.Run("OnResign_and_resign_error", func(t *testing.T) { // Set onResign to return an error onResignCalled := false resignErr := errors.New("resign error") onResign := func(ctx context.Context) error { onResignCalled = true return resignErr } leaderChan, p := prepareRun(t, nil, onResign) p.election.EXPECT().Resign(gomock.Any()).Return(fmt.Errorf("failed to resign")) // We should be blocked on the timer. p.timeSource.BlockUntil(1) // The resign function on election should not be called if onResign returns an error p.timeSource.Advance(_testLeaderPeriod + 1) p.timeSource.BlockUntil(1) assert.False(t, <-leaderChan) assert.True(t, onResignCalled, "OnResign callback should have been called") p.cancel() // Wait for the goroutine to exit for range leaderChan { } }) } type runParams struct { ctx context.Context cancel context.CancelFunc timeSource clock.MockedTimeSource electionCh chan struct{} election *store.MockElection onLeader ProcessFunc onResign ProcessFunc } func prepareRun(t *testing.T, onLeader, onResign ProcessFunc) (<-chan bool, runParams) { ctrl := gomock.NewController(t) logger := testlogger.New(t) timeSource := clock.NewMockedTimeSource() electionCh := make(chan struct{}) election := store.NewMockElection(ctrl) election.EXPECT().Campaign(gomock.Any(), _testHost).Return(nil) election.EXPECT().Cleanup(gomock.Any()).Return(nil) election.EXPECT().Done().Return(electionCh) leaderStore := store.NewMockElector(ctrl) leaderStore.EXPECT().CreateElection(gomock.Any(), _testNamespace.Name).Return(election, nil) shardStore := store.NewMockStore(ctrl) processFactory := process.NewMockFactory(ctrl) processRunner := process.NewMockProcessor(ctrl) processFactory.EXPECT().CreateProcessor(_testNamespace, shardStore, election).Return(processRunner) factory := NewElectionFactory(FactoryParams{ HostName: _testHost, Cfg: config.ShardDistribution{ Election: config.Election{ LeaderPeriod: _testLeaderPeriod, MaxRandomDelay: _testMaxRandomDelay, FailedElectionCooldown: _testFailedElectionCooldown, }, }, LeaderStore: leaderStore, Store: shardStore, Logger: logger, Clock: timeSource, ProcessFactory: processFactory, }) elector, err := factory.CreateElector(context.Background(), _testNamespace) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) // Default callbacks if onLeader == nil { onLeader = func(ctx context.Context) error { return nil } } if onResign == nil { onResign = func(ctx context.Context) error { return nil } } processRunner.EXPECT().Run(gomock.Any()).DoAndReturn(onLeader) processRunner.EXPECT().Terminate(gomock.Any()).DoAndReturn(onResign) go func() { // Wait until run will stop on timer timeSource.BlockUntil(1) // Advance the time to kick in the election. timeSource.Advance(_testMaxRandomDelay) }() leaderChan := elector.Run(ctx) assert.True(t, <-leaderChan) return leaderChan, runParams{ ctx: ctx, cancel: cancel, timeSource: timeSource, electionCh: electionCh, election: election, onLeader: onLeader, onResign: onResign, } } func TestOnLeader_Error(t *testing.T) { goleak.VerifyNone(t) ctrl := gomock.NewController(t) logger := testlogger.New(t) timeSource := clock.NewMockedTimeSource() election := store.NewMockElection(ctrl) election.EXPECT().Campaign(gomock.Any(), _testHost).Return(nil) // Expect resignation after onLeader failure election.EXPECT().Resign(gomock.Any()).Return(nil) election.EXPECT().Cleanup(gomock.Any()).Return(nil) leaderStore := store.NewMockElector(ctrl) leaderStore.EXPECT().CreateElection(gomock.Any(), _testNamespace.Name).Return(election, nil) shardStore := store.NewMockStore(ctrl) processFactory := process.NewMockFactory(ctrl) processRunner := process.NewMockProcessor(ctrl) processFactory.EXPECT().CreateProcessor(_testNamespace, shardStore, election).Return(processRunner) // Create elector directly for test control el := &elector{ namespace: _testNamespace, leaderStore: leaderStore, store: shardStore, logger: logger, cfg: config.Election{ LeaderPeriod: _testLeaderPeriod, MaxRandomDelay: _testMaxRandomDelay, FailedElectionCooldown: _testFailedElectionCooldown, }, clock: timeSource, hostname: _testHost, processFactory: processFactory, } // Create a cancelable context for the test ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() // Ensure context is canceled at the end of the test // Make onLeader return an error onLeaderErr := errors.New("leader error") processRunner.EXPECT().Run(gomock.Any()).Return(onLeaderErr) processRunner.EXPECT().Terminate(gomock.Any()).Return(nil) go func() { // Wait until run will stop on timer timeSource.BlockUntil(1) // Advance the time to kick in the election. timeSource.Advance(_testMaxRandomDelay) }() // Run the test leaderCh := make(chan bool, 1) err := el.runElection(ctx, leaderCh) // Error should contain our onLeader error require.Error(t, err) assert.Contains(t, err.Error(), "onLeader") assert.Contains(t, err.Error(), "leader error") } ================================================ FILE: service/sharddistributor/leader/namespace/manager.go ================================================ package namespace import ( "context" "fmt" "sync" "go.uber.org/fx" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/service/sharddistributor/client/clientcommon" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/leader/election" ) // Module provides namespace manager component for an fx app. var Module = fx.Module( "namespace-manager", fx.Invoke(NewManager), ) // stateFn is a recursive function type representing a state in the election // state machine. // Each state function blocks until a transition occurs and returns the next state function, // or nil to stop the machine. type stateFn func(ctx context.Context) stateFn type Manager struct { cfg config.ShardDistribution logger log.Logger electionFactory election.Factory drainObserver clientcommon.DrainSignalObserver namespaces map[string]*namespaceHandler ctx context.Context cancel context.CancelFunc } type namespaceHandler struct { logger log.Logger electionFactory election.Factory namespaceCfg config.Namespace drainObserver clientcommon.DrainSignalObserver cleanupWg sync.WaitGroup } type ManagerParams struct { fx.In Cfg config.ShardDistribution Logger log.Logger ElectionFactory election.Factory Lifecycle fx.Lifecycle DrainObserver clientcommon.DrainSignalObserver `optional:"true"` } // NewManager creates a new namespace manager func NewManager(p ManagerParams) *Manager { manager := &Manager{ cfg: p.Cfg, logger: p.Logger.WithTags(tag.ComponentNamespaceManager), electionFactory: p.ElectionFactory, drainObserver: p.DrainObserver, namespaces: make(map[string]*namespaceHandler), } p.Lifecycle.Append(fx.StartStopHook(manager.Start, manager.Stop)) return manager } // Start initializes the namespace manager and starts handling all namespaces func (m *Manager) Start(ctx context.Context) error { m.ctx, m.cancel = context.WithCancel(context.Background()) for _, ns := range m.cfg.Namespaces { m.logger.Info("Starting namespace handler", tag.ShardNamespace(ns.Name)) if err := m.handleNamespace(ns); err != nil { return err } } return nil } // Stop gracefully stops all namespace handlers. // Cancels the manager context which cascades to all handler contexts, // then waits for all election goroutines to finish. func (m *Manager) Stop(ctx context.Context) error { if m.cancel == nil { return fmt.Errorf("manager was not running") } m.cancel() for ns, handler := range m.namespaces { m.logger.Info("Waiting for namespace handler to stop", tag.ShardNamespace(ns)) handler.cleanupWg.Wait() } return nil } // handleNamespace sets up a namespace handler and starts its election goroutine. func (m *Manager) handleNamespace(namespaceCfg config.Namespace) error { if _, exists := m.namespaces[namespaceCfg.Name]; exists { return fmt.Errorf("namespace %s already running", namespaceCfg.Name) } handler := &namespaceHandler{ logger: m.logger.WithTags(tag.ShardNamespace(namespaceCfg.Name)), electionFactory: m.electionFactory, namespaceCfg: namespaceCfg, drainObserver: m.drainObserver, } m.namespaces[namespaceCfg.Name] = handler handler.cleanupWg.Add(1) go handler.runElection(m.ctx) return nil } // runElection drives the election state machine for a namespace. // It starts in the campaigning state and follows state transitions // until a state returns nil (stop). func (h *namespaceHandler) runElection(ctx context.Context) { defer h.cleanupWg.Done() for state := h.campaigning; state != nil; { state = state(ctx) } } func (h *namespaceHandler) drainChannel() <-chan struct{} { if h.drainObserver != nil { return h.drainObserver.Drain() } return nil } func (h *namespaceHandler) startElection(ctx context.Context) (<-chan bool, context.CancelFunc, error) { electorCtx, cancel := context.WithCancel(ctx) elector, err := h.electionFactory.CreateElector(electorCtx, h.namespaceCfg) if err != nil { cancel() return nil, nil, err } return elector.Run(electorCtx), cancel, nil } // campaigning creates an elector and participates in leader election. // Transitions: h.idle on drain, h.campaigning on recoverable error, nil on stop. func (h *namespaceHandler) campaigning(ctx context.Context) stateFn { h.logger.Info("Entering campaigning state") drainCh := h.drainChannel() select { case <-drainCh: h.logger.Info("Drain signal detected before election start") return h.idle default: } leaderCh, cancel, err := h.startElection(ctx) if err != nil { h.logger.Error("Failed to create elector", tag.Error(err)) return nil } defer cancel() for { select { case <-ctx.Done(): return nil case <-drainCh: h.logger.Info("Drain signal received, resigning from election") return h.idle case isLeader, ok := <-leaderCh: if !ok { h.logger.Error("Election channel closed unexpectedly") return h.campaigning } if isLeader { h.logger.Info("Became leader for namespace") } else { h.logger.Info("Lost leadership for namespace") } } } } // idle waits for an undrain signal to resume campaigning. // Transitions: h.campaigning on undrain, nil on stop. func (h *namespaceHandler) idle(ctx context.Context) stateFn { h.logger.Info("Entering idle state (drained)") var undrainCh <-chan struct{} if h.drainObserver != nil { undrainCh = h.drainObserver.Undrain() } select { case <-ctx.Done(): return nil case <-undrainCh: h.logger.Info("Undrain signal received, resuming election") return h.campaigning } } ================================================ FILE: service/sharddistributor/leader/namespace/manager_test.go ================================================ package namespace import ( "context" "errors" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/fx/fxtest" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/leader/election" ) // mockElectorRun returns a DoAndReturn function that simulates an elector: // it returns a leaderCh and closes it when the context is cancelled. func mockElectorRun(leaderCh chan bool) func(ctx context.Context) <-chan bool { return func(ctx context.Context) <-chan bool { go func() { <-ctx.Done() close(leaderCh) }() return (<-chan bool)(leaderCh) } } // closeDrainObserver is a test helper that simulates the close-and-recreate // semantics of the real DrainSignalObserver. It wraps a mock and manages // the channel lifecycle. type closeDrainObserver struct { mu sync.Mutex drainCh chan struct{} undrainCh chan struct{} } func newCloseDrainObserver() *closeDrainObserver { return &closeDrainObserver{ drainCh: make(chan struct{}), undrainCh: make(chan struct{}), } } func (o *closeDrainObserver) Drain() <-chan struct{} { o.mu.Lock() defer o.mu.Unlock() return o.drainCh } func (o *closeDrainObserver) Undrain() <-chan struct{} { o.mu.Lock() defer o.mu.Unlock() return o.undrainCh } func (o *closeDrainObserver) SignalDrain() { o.mu.Lock() defer o.mu.Unlock() close(o.drainCh) o.undrainCh = make(chan struct{}) } func (o *closeDrainObserver) SignalUndrain() { o.mu.Lock() defer o.mu.Unlock() close(o.undrainCh) o.drainCh = make(chan struct{}) } func TestNewManager(t *testing.T) { logger := testlogger.New(t) ctrl := gomock.NewController(t) electionFactory := election.NewMockFactory(ctrl) cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ {Name: "test-namespace"}, }, } manager := NewManager(ManagerParams{ Cfg: cfg, Logger: logger, ElectionFactory: electionFactory, Lifecycle: fxtest.NewLifecycle(t), }) assert.NotNil(t, manager) assert.Equal(t, cfg, manager.cfg) assert.Equal(t, 0, len(manager.namespaces)) } func TestStartManager(t *testing.T) { logger := testlogger.New(t) ctrl := gomock.NewController(t) electionFactory := election.NewMockFactory(ctrl) elector := election.NewMockElector(ctrl) leaderCh := make(chan bool) electionFactory.EXPECT().CreateElector(gomock.Any(), gomock.Any()).Return(elector, nil) elector.EXPECT().Run(gomock.Any()).DoAndReturn(mockElectorRun(leaderCh)) cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ {Name: "test-namespace"}, }, } manager := &Manager{ cfg: cfg, logger: logger, electionFactory: electionFactory, namespaces: make(map[string]*namespaceHandler), } err := manager.Start(context.Background()) time.Sleep(10 * time.Millisecond) assert.NoError(t, err) assert.NotNil(t, manager.ctx) assert.NotNil(t, manager.cancel) assert.Equal(t, 1, len(manager.namespaces)) assert.Contains(t, manager.namespaces, "test-namespace") // Cleanup manager.cancel() manager.namespaces["test-namespace"].cleanupWg.Wait() } func TestStartManagerWithElectorError(t *testing.T) { logger := testlogger.New(t) ctrl := gomock.NewController(t) electionFactory := election.NewMockFactory(ctrl) cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ {Name: "test-namespace"}, }, } expectedErr := errors.New("elector creation failed") electionFactory.EXPECT().CreateElector(gomock.Any(), config.Namespace{Name: "test-namespace"}).Return(nil, expectedErr) manager := &Manager{ cfg: cfg, logger: logger, electionFactory: electionFactory, namespaces: make(map[string]*namespaceHandler), } err := manager.Start(context.Background()) assert.NoError(t, err) // The goroutine exits on elector creation error handler := manager.namespaces["test-namespace"] handler.cleanupWg.Wait() // Cleanup manager.cancel() } func TestStopManager(t *testing.T) { logger := testlogger.New(t) ctrl := gomock.NewController(t) electionFactory := election.NewMockFactory(ctrl) elector := election.NewMockElector(ctrl) leaderCh := make(chan bool) electionFactory.EXPECT().CreateElector(gomock.Any(), gomock.Any()).Return(elector, nil) elector.EXPECT().Run(gomock.Any()).DoAndReturn(mockElectorRun(leaderCh)) cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ {Name: "test-namespace"}, }, } manager := &Manager{ cfg: cfg, logger: logger, electionFactory: electionFactory, namespaces: make(map[string]*namespaceHandler), } _ = manager.Start(context.Background()) time.Sleep(10 * time.Millisecond) err := manager.Stop(context.Background()) assert.NoError(t, err) } func TestHandleNamespaceAlreadyExists(t *testing.T) { logger := testlogger.New(t) ctrl := gomock.NewController(t) electionFactory := election.NewMockFactory(ctrl) manager := &Manager{ cfg: config.ShardDistribution{}, logger: logger, electionFactory: electionFactory, namespaces: make(map[string]*namespaceHandler), } manager.ctx, manager.cancel = context.WithCancel(context.Background()) defer manager.cancel() manager.namespaces["test-namespace"] = &namespaceHandler{} err := manager.handleNamespace(config.Namespace{Name: "test-namespace"}) assert.ErrorContains(t, err, "namespace test-namespace already running") } func TestRunElection_LeadershipEvents(t *testing.T) { logger := testlogger.New(t) ctrl := gomock.NewController(t) electionFactory := election.NewMockFactory(ctrl) elector := election.NewMockElector(ctrl) leaderCh := make(chan bool) electionFactory.EXPECT().CreateElector(gomock.Any(), gomock.Any()).Return(elector, nil) elector.EXPECT().Run(gomock.Any()).DoAndReturn(mockElectorRun(leaderCh)) cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ {Name: "test-namespace"}, }, } manager := &Manager{ cfg: cfg, logger: logger, electionFactory: electionFactory, namespaces: make(map[string]*namespaceHandler), } err := manager.Start(context.Background()) require.NoError(t, err) leaderCh <- true time.Sleep(10 * time.Millisecond) leaderCh <- false time.Sleep(10 * time.Millisecond) err = manager.Stop(context.Background()) assert.NoError(t, err) } func TestDrainSignal_TriggersResign(t *testing.T) { logger := testlogger.New(t) ctrl := gomock.NewController(t) electionFactory := election.NewMockFactory(ctrl) elector := election.NewMockElector(ctrl) leaderCh := make(chan bool) electionFactory.EXPECT().CreateElector(gomock.Any(), gomock.Any()).Return(elector, nil) elector.EXPECT().Run(gomock.Any()).DoAndReturn(mockElectorRun(leaderCh)) observer := newCloseDrainObserver() cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ {Name: "test-namespace"}, }, } manager := &Manager{ cfg: cfg, logger: logger, electionFactory: electionFactory, drainObserver: observer, namespaces: make(map[string]*namespaceHandler), } err := manager.Start(context.Background()) require.NoError(t, err) // Wait for the elector to be running leaderCh <- true time.Sleep(10 * time.Millisecond) // Close drain channel — all handlers see it observer.SignalDrain() time.Sleep(50 * time.Millisecond) // Handler should be in an idle state err = manager.Stop(context.Background()) assert.NoError(t, err) } func TestDrainSignal_NilDrainObserver(t *testing.T) { logger := testlogger.New(t) ctrl := gomock.NewController(t) electionFactory := election.NewMockFactory(ctrl) elector := election.NewMockElector(ctrl) leaderCh := make(chan bool) electionFactory.EXPECT().CreateElector(gomock.Any(), gomock.Any()).Return(elector, nil) elector.EXPECT().Run(gomock.Any()).DoAndReturn(mockElectorRun(leaderCh)) cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ {Name: "test-namespace"}, }, } manager := &Manager{ cfg: cfg, logger: logger, electionFactory: electionFactory, namespaces: make(map[string]*namespaceHandler), } err := manager.Start(context.Background()) require.NoError(t, err) assert.Nil(t, manager.drainObserver) err = manager.Stop(context.Background()) assert.NoError(t, err) } func TestDrainSignal_ManagerStopsBeforeDrain(t *testing.T) { logger := testlogger.New(t) ctrl := gomock.NewController(t) electionFactory := election.NewMockFactory(ctrl) elector := election.NewMockElector(ctrl) leaderCh := make(chan bool) electionFactory.EXPECT().CreateElector(gomock.Any(), gomock.Any()).Return(elector, nil) elector.EXPECT().Run(gomock.Any()).DoAndReturn(mockElectorRun(leaderCh)) observer := newCloseDrainObserver() cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ {Name: "test-namespace"}, }, } manager := &Manager{ cfg: cfg, logger: logger, electionFactory: electionFactory, drainObserver: observer, namespaces: make(map[string]*namespaceHandler), } err := manager.Start(context.Background()) require.NoError(t, err) // Stop before drain fires err = manager.Stop(context.Background()) assert.NoError(t, err) } func TestDrainThenUndrain_ResumesElection(t *testing.T) { logger, logs := testlogger.NewObserved(t) ctrl := gomock.NewController(t) electionFactory := election.NewMockFactory(ctrl) elector1 := election.NewMockElector(ctrl) leaderCh1 := make(chan bool) elector2 := election.NewMockElector(ctrl) leaderCh2 := make(chan bool) gomock.InOrder( electionFactory.EXPECT().CreateElector(gomock.Any(), gomock.Any()).Return(elector1, nil), electionFactory.EXPECT().CreateElector(gomock.Any(), gomock.Any()).Return(elector2, nil), ) elector1.EXPECT().Run(gomock.Any()).DoAndReturn(mockElectorRun(leaderCh1)) elector2.EXPECT().Run(gomock.Any()).DoAndReturn(mockElectorRun(leaderCh2)) observer := newCloseDrainObserver() cfg := config.ShardDistribution{ Namespaces: []config.Namespace{ {Name: "test-namespace"}, }, } manager := &Manager{ cfg: cfg, logger: logger, electionFactory: electionFactory, drainObserver: observer, namespaces: make(map[string]*namespaceHandler), } err := manager.Start(context.Background()) require.NoError(t, err) // Phase 1: elector1 running, verify it becomes leader leaderCh1 <- true time.Sleep(10 * time.Millisecond) assert.Equal(t, 1, logs.FilterMessage("Became leader for namespace").Len(), "expected leader elected in phase 1") // Drain - elector1 resigns observer.SignalDrain() time.Sleep(50 * time.Millisecond) assert.Equal(t, 1, logs.FilterMessage("Drain signal received, resigning from election").Len()) // Undrain - elector2 created, campaign again observer.SignalUndrain() time.Sleep(50 * time.Millisecond) assert.Equal(t, 1, logs.FilterMessage("Undrain signal received, resuming election").Len()) // Phase 2: verify elector2 is running and becomes leader after undrain leaderCh2 <- true time.Sleep(10 * time.Millisecond) assert.Equal(t, 2, logs.FilterMessage("Became leader for namespace").Len(), "expected leader elected in both phases") err = manager.Stop(context.Background()) assert.NoError(t, err) } ================================================ FILE: service/sharddistributor/leader/process/process_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: processor.go // // Generated by this command: // // mockgen -package process -source processor.go -destination=process_mock.go Factory,Processor // // Package process is a generated GoMock package. package process import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" config "github.com/uber/cadence/service/sharddistributor/config" store "github.com/uber/cadence/service/sharddistributor/store" ) // MockProcessor is a mock of Processor interface. type MockProcessor struct { ctrl *gomock.Controller recorder *MockProcessorMockRecorder isgomock struct{} } // MockProcessorMockRecorder is the mock recorder for MockProcessor. type MockProcessorMockRecorder struct { mock *MockProcessor } // NewMockProcessor creates a new mock instance. func NewMockProcessor(ctrl *gomock.Controller) *MockProcessor { mock := &MockProcessor{ctrl: ctrl} mock.recorder = &MockProcessorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockProcessor) EXPECT() *MockProcessorMockRecorder { return m.recorder } // Run mocks base method. func (m *MockProcessor) Run(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Run", ctx) ret0, _ := ret[0].(error) return ret0 } // Run indicates an expected call of Run. func (mr *MockProcessorMockRecorder) Run(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockProcessor)(nil).Run), ctx) } // Terminate mocks base method. func (m *MockProcessor) Terminate(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Terminate", ctx) ret0, _ := ret[0].(error) return ret0 } // Terminate indicates an expected call of Terminate. func (mr *MockProcessorMockRecorder) Terminate(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Terminate", reflect.TypeOf((*MockProcessor)(nil).Terminate), ctx) } // MockFactory is a mock of Factory interface. type MockFactory struct { ctrl *gomock.Controller recorder *MockFactoryMockRecorder isgomock struct{} } // MockFactoryMockRecorder is the mock recorder for MockFactory. type MockFactoryMockRecorder struct { mock *MockFactory } // NewMockFactory creates a new mock instance. func NewMockFactory(ctrl *gomock.Controller) *MockFactory { mock := &MockFactory{ctrl: ctrl} mock.recorder = &MockFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFactory) EXPECT() *MockFactoryMockRecorder { return m.recorder } // CreateProcessor mocks base method. func (m *MockFactory) CreateProcessor(cfg config.Namespace, storage store.Store, election store.Election) Processor { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateProcessor", cfg, storage, election) ret0, _ := ret[0].(Processor) return ret0 } // CreateProcessor indicates an expected call of CreateProcessor. func (mr *MockFactoryMockRecorder) CreateProcessor(cfg, storage, election any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateProcessor", reflect.TypeOf((*MockFactory)(nil).CreateProcessor), cfg, storage, election) } ================================================ FILE: service/sharddistributor/leader/process/processor.go ================================================ package process import ( "context" "errors" "fmt" "maps" "math" "math/rand" "slices" "sort" "strconv" "sync" "time" "go.uber.org/fx" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination=process_mock.go Factory,Processor // Module provides processor factory for fx app. var Module = fx.Module( "leader-process", fx.Provide(NewProcessorFactory), ) // Processor represents a process that runs when the instance is the leader type Processor interface { Run(ctx context.Context) error Terminate(ctx context.Context) error } // Factory creates processor instances type Factory interface { // CreateProcessor creates a new processor, it takes the generic store // and the election object which provides the transactional guard. CreateProcessor(cfg config.Namespace, storage store.Store, election store.Election) Processor } const ( _defaultPeriod = time.Second _defaultHeartbeatTTL = 10 * time.Second _defaultTimeout = 1 * time.Second _defaultCooldown = 250 * time.Millisecond ) type processorFactory struct { logger log.Logger timeSource clock.TimeSource cfg config.LeaderProcess metricsClient metrics.Client sdConfig *config.Config } type namespaceProcessor struct { namespaceCfg config.Namespace logger log.Logger metricsClient metrics.Client timeSource clock.TimeSource running bool cancel context.CancelFunc sdConfig *config.Config cfg config.LeaderProcess wg sync.WaitGroup shardStore store.Store election store.Election } // NewProcessorFactory creates a new processor factory func NewProcessorFactory( logger log.Logger, metricsClient metrics.Client, timeSource clock.TimeSource, cfg config.ShardDistribution, sdConfig *config.Config, ) Factory { if cfg.Process.Period == 0 { cfg.Process.Period = _defaultPeriod } if cfg.Process.HeartbeatTTL == 0 { cfg.Process.HeartbeatTTL = _defaultHeartbeatTTL } if cfg.Process.Timeout == 0 { cfg.Process.Timeout = _defaultTimeout } if cfg.Process.RebalanceCooldown == 0 { cfg.Process.RebalanceCooldown = _defaultCooldown } return &processorFactory{ logger: logger, timeSource: timeSource, cfg: cfg.Process, metricsClient: metricsClient, sdConfig: sdConfig, } } // CreateProcessor creates a new processor for the given namespace func (f *processorFactory) CreateProcessor(cfg config.Namespace, shardStore store.Store, election store.Election) Processor { return &namespaceProcessor{ namespaceCfg: cfg, logger: f.logger.WithTags(tag.ComponentLeaderProcessor, tag.ShardNamespace(cfg.Name)), timeSource: f.timeSource, cfg: f.cfg, shardStore: shardStore, election: election, // Store the election object metricsClient: f.metricsClient, sdConfig: f.sdConfig, } } // Run begins processing for this namespace func (p *namespaceProcessor) Run(ctx context.Context) error { if p.running { return fmt.Errorf("processor is already running") } pCtx, cancel := context.WithCancel(ctx) p.cancel = cancel p.running = true p.logger.Info("Starting") p.wg.Add(1) // Start the process in a goroutine go p.runProcess(pCtx) return nil } // Terminate halts processing for this namespace func (p *namespaceProcessor) Terminate(ctx context.Context) error { if !p.running { return fmt.Errorf("processor has not been started") } p.logger.Info("Stopping") if p.cancel != nil { p.cancel() p.cancel = nil } p.running = false // Ensure that the process has stopped. p.wg.Wait() return nil } // runProcess launches and manages the processing loops. func (p *namespaceProcessor) runProcess(ctx context.Context) { defer p.wg.Done() var loopWg sync.WaitGroup loopWg.Add(2) // We have two loops to manage. // Launch the assignment and executor cleanup process in its own goroutine. go func() { defer loopWg.Done() p.runRebalancingLoop(ctx) }() // Launch the shard stats cleanup process in its own goroutine. go func() { defer loopWg.Done() p.runShardStatsCleanupLoop(ctx) }() // Wait for both loops to exit. loopWg.Wait() } // runRebalancingLoop handles shard assignment and redistribution. func (p *namespaceProcessor) runRebalancingLoop(ctx context.Context) { // Buffered channel to allow one pending rebalance trigger. triggerChan := make(chan string, 1) // Perform an initial rebalance on startup. err := p.rebalanceShards(ctx) if err != nil { p.logger.Error("initial rebalance failed", tag.Error(err)) } if err := p.runRebalanceTriggeringLoop(ctx, triggerChan); err != nil { p.logger.Error("failed to start rebalance triggering loop", tag.Error(err)) return } nextRebalanceAllowedAt := p.timeSource.Now() for { select { case <-ctx.Done(): p.logger.Info("Rebalancing loop cancelled") return case triggerReason := <-triggerChan: // If an update comes in before the cooldown has expired, // we wait until the cooldown has passed since the last rebalance before processing it. // This ensures that we don't rebalance too frequently in response to a flurry of updates p.timeSource.Sleep(nextRebalanceAllowedAt.Sub(p.timeSource.Now())) nextRebalanceAllowedAt = p.timeSource.Now().Add(p.cfg.RebalanceCooldown) p.logger.Info("Rebalancing triggered", tag.Dynamic("triggerReason", triggerReason)) if err := p.rebalanceShards(ctx); err != nil { p.logger.Error("rebalance failed", tag.Error(err)) // If rebalance fails, we want to trigger another rebalance ASAP, // but with a cooldown to avoid rebalance storms if the underlying issue is persistent. select { case triggerChan <- "Previous rebalance failed": default: // If the channel is full, we skip sending the update to avoid blocking the loop. } } } } } // runRebalanceTriggeringLoop monitors for state changes and periodic triggers to initiate rebalancing. // it doesn't block Subscribe calls to avoid a growing backlog of updates. func (p *namespaceProcessor) runRebalanceTriggeringLoop(ctx context.Context, triggerChan chan<- string) error { updateChan, err := p.shardStore.SubscribeToExecutorStatusChanges(ctx, p.namespaceCfg.Name) if err != nil { p.logger.Error("Failed to subscribe to state changes, stopping rebalancing loop.", tag.Error(err)) return err } go p.rebalanceTriggeringLoop(ctx, updateChan, triggerChan) return nil } func (p *namespaceProcessor) rebalanceTriggeringLoop(ctx context.Context, updateChan <-chan int64, triggerChan chan<- string) { ticker := p.timeSource.NewTicker(p.cfg.Period) defer ticker.Stop() tryTriggerRebalancing := func(reason string) { select { case triggerChan <- reason: default: p.logger.Info("Rebalance already pending, skipping trigger attempt", tag.Dynamic("reason", reason)) } } for { select { case <-ctx.Done(): p.logger.Info("Rebalance triggering loop cancelled") return case <-ticker.Chan(): tryTriggerRebalancing("Periodic reconciliation triggered") case _, ok := <-updateChan: if !ok { p.logger.Info("Update channel closed, stopping rebalance triggering loop") return } tryTriggerRebalancing("State change detected") } } } // runShardStatsCleanupLoop periodically removes stale shard statistics. func (p *namespaceProcessor) runShardStatsCleanupLoop(ctx context.Context) { ticker := p.timeSource.NewTicker(p.cfg.HeartbeatTTL) defer ticker.Stop() for { select { case <-ctx.Done(): p.logger.Info("Shard stats cleanup loop cancelled.") return case <-ticker.Chan(): // Only perform shard stats cleanup in GREEDY load balancing mode // TODO: refactor this to not have this loop for non-GREEDY modes if p.sdConfig.GetLoadBalancingMode(p.namespaceCfg.Name) != types.LoadBalancingModeGREEDY { p.logger.Debug("Load balancing mode is not GREEDY, skipping shard stats cleanup.", tag.ShardNamespace(p.namespaceCfg.Name)) continue } p.logger.Info("Periodic shard stats cleanup triggered.") namespaceState, err := p.shardStore.GetState(ctx, p.namespaceCfg.Name) if err != nil { p.logger.Error("Failed to get state for shard stats cleanup", tag.Error(err)) continue } staleShardStats := p.identifyStaleShardStats(namespaceState) if len(staleShardStats) == 0 { // No stale shard stats to delete continue } if err := p.shardStore.DeleteShardStats(ctx, p.namespaceCfg.Name, staleShardStats, p.election.Guard()); err != nil { p.logger.Error("Failed to delete stale shard stats", tag.Error(err)) } } } } // identifyStaleExecutors returns a list of executors who have not reported a heartbeat recently. func (p *namespaceProcessor) identifyStaleExecutors(namespaceState *store.NamespaceState) map[string]int64 { expiredExecutors := make(map[string]int64) now := p.timeSource.Now().UTC() for executorID, state := range namespaceState.Executors { if now.Sub(state.LastHeartbeat) > p.cfg.HeartbeatTTL { p.logger.Info("Executor has not reported a heartbeat recently", tag.ShardExecutor(executorID), tag.ShardNamespace(p.namespaceCfg.Name), tag.Value(state.LastHeartbeat)) expiredExecutors[executorID] = namespaceState.ShardAssignments[executorID].ModRevision } } return expiredExecutors } // identifyStaleShardStats returns a list of shard statistics that are no longer relevant. func (p *namespaceProcessor) identifyStaleShardStats(namespaceState *store.NamespaceState) []string { activeShards := make(map[string]struct{}) now := p.timeSource.Now().UTC() // 1. build set of active executors // add all assigned shards from executors that are ACTIVE and not stale for executorID, assignedState := range namespaceState.ShardAssignments { executor, exists := namespaceState.Executors[executorID] if !exists { continue } isActive := executor.Status == types.ExecutorStatusACTIVE isNotStale := now.Sub(executor.LastHeartbeat) <= p.cfg.HeartbeatTTL if isActive && isNotStale { for shardID := range assignedState.AssignedShards { activeShards[shardID] = struct{}{} } } } // add all shards in ReportedShards where the status is not DONE for _, heartbeatState := range namespaceState.Executors { for shardID, shardStatusReport := range heartbeatState.ReportedShards { if shardStatusReport.Status != types.ShardStatusDONE { activeShards[shardID] = struct{}{} } } } // 2. build set of stale shard stats // append all shard stats that are not in the active shards set var staleShardStats []string for shardID, stats := range namespaceState.ShardStats { if _, ok := activeShards[shardID]; ok { continue } recentUpdate := !stats.LastUpdateTime.IsZero() && now.Sub(stats.LastUpdateTime) <= p.cfg.HeartbeatTTL recentMove := !stats.LastMoveTime.IsZero() && now.Sub(stats.LastMoveTime) <= p.cfg.HeartbeatTTL if recentUpdate || recentMove { // Preserve stats that have been updated recently to allow cooldown/load history to // survive executor churn. These shards are likely awaiting reassignment, // so we don't want to delete them. continue } staleShardStats = append(staleShardStats, shardID) } return staleShardStats } // rebalanceShards is the core logic for distributing shards among active executors. func (p *namespaceProcessor) rebalanceShards(ctx context.Context) (err error) { metricsLoopScope := p.metricsClient.Scope( metrics.ShardDistributorAssignLoopScope, metrics.NamespaceTag(p.namespaceCfg.Name), metrics.NamespaceTypeTag(p.namespaceCfg.Type), ) metricsLoopScope.AddCounter(metrics.ShardDistributorAssignLoopAttempts, 1) defer func() { if err != nil { metricsLoopScope.AddCounter(metrics.ShardDistributorAssignLoopFail, 1) } else { metricsLoopScope.AddCounter(metrics.ShardDistributorAssignLoopSuccess, 1) } }() start := p.timeSource.Now() defer func() { metricsLoopScope.RecordHistogramDuration(metrics.ShardDistributorAssignLoopShardRebalanceLatency, p.timeSource.Now().Sub(start)) }() ctx, cancel := context.WithTimeout(ctx, p.cfg.Timeout) defer cancel() return p.rebalanceShardsImpl(ctx, metricsLoopScope) } func (p *namespaceProcessor) rebalanceShardsImpl(ctx context.Context, metricsLoopScope metrics.Scope) (err error) { namespaceState, err := p.shardStore.GetState(ctx, p.namespaceCfg.Name) if err != nil { return fmt.Errorf("get state: %w", err) } // Identify stale executors that need to be removed staleExecutors := p.identifyStaleExecutors(namespaceState) if len(staleExecutors) > 0 { p.logger.Info("Identified stale executors for removal", tag.ShardExecutors(slices.Collect(maps.Keys(staleExecutors)))) } activeExecutors := p.getActiveExecutors(namespaceState, staleExecutors) if len(activeExecutors) == 0 { p.logger.Error("No active executors found. Cannot assign shards.") // Cleanup stale executors even if no active executors remain if len(staleExecutors) > 0 { p.logger.Info("Cleaning up stale executors (no active executors)", tag.ShardExecutors(slices.Collect(maps.Keys(staleExecutors)))) if err := p.shardStore.DeleteExecutors(ctx, p.namespaceCfg.Name, slices.Collect(maps.Keys(staleExecutors)), p.election.Guard()); err != nil { p.logger.Error("Failed to delete stale executors", tag.Error(err)) } } return nil } p.logger.Info("Active executors", tag.ShardExecutors(activeExecutors)) deletedShards := p.findDeletedShards(namespaceState) shardsToReassign, currentAssignments := p.findShardsToReassign(activeExecutors, namespaceState, deletedShards, staleExecutors) metricsLoopScope.UpdateGauge(metrics.ShardDistributorAssignLoopNumRebalancedShards, float64(len(shardsToReassign))) // If there are deleted shards or stale executors, the distribution has changed. assignedToEmptyExecutors := assignShardsToEmptyExecutors(currentAssignments) updatedAssignments := p.updateAssignments(shardsToReassign, activeExecutors, currentAssignments) isRebalancedByShardLoad := p.rebalanceByShardLoad(calcShardLoad(namespaceState), currentAssignments) distributionChanged := len(deletedShards) > 0 || len(staleExecutors) > 0 || assignedToEmptyExecutors || updatedAssignments || isRebalancedByShardLoad if !distributionChanged { p.logger.Info("No changes to distribution detected. Skipping rebalance.") return nil } newState := p.getNewAssignmentsState(namespaceState, currentAssignments) p.emitExecutorMetric(namespaceState, metricsLoopScope) p.emitOldestExecutorHeartbeatLag(namespaceState, metricsLoopScope) if p.sdConfig.GetMigrationMode(p.namespaceCfg.Name) != types.MigrationModeONBOARDED { p.logger.Info("Running rebalancing in shadow mode", tag.Dynamic("old_assignments", namespaceState.ShardAssignments), tag.Dynamic("new_assignments", newState)) p.emitActiveShardMetric(namespaceState.ShardAssignments, metricsLoopScope) if len(staleExecutors) > 0 { p.logger.Info("Cleaning up stale executors in shadow mode", tag.ShardExecutors(slices.Collect(maps.Keys(staleExecutors)))) if err := p.shardStore.DeleteExecutors(ctx, p.namespaceCfg.Name, slices.Collect(maps.Keys(staleExecutors)), p.election.Guard()); err != nil { p.logger.Error("Failed to delete stale executors in shadow mode", tag.Error(err)) // Non-blocking: stale executors in shadow mode will be cleaned up the next cycle } } return nil } namespaceState.ShardAssignments = newState p.logger.Info("Applying new shard distribution.") // Use the leader guard for the assign and delete operation. err = p.shardStore.AssignShards(ctx, p.namespaceCfg.Name, store.AssignShardsRequest{ NewState: namespaceState, ExecutorsToDelete: staleExecutors, }, p.election.Guard()) if err != nil { return fmt.Errorf("assign shards: %w", err) } p.emitActiveShardMetric(namespaceState.ShardAssignments, metricsLoopScope) return nil } func (p *namespaceProcessor) emitActiveShardMetric(shardAssignments map[string]store.AssignedState, metricsLoopScope metrics.Scope) { totalActiveShards := 0 for _, assignedState := range shardAssignments { totalActiveShards += len(assignedState.AssignedShards) } metricsLoopScope.UpdateGauge(metrics.ShardDistributorActiveShards, float64(totalActiveShards)) } func (p *namespaceProcessor) emitExecutorMetric(namespaceState *store.NamespaceState, metricsLoopScope metrics.Scope) { for status, count := range namespaceState.CountExecutorsByStatus() { metricsLoopScope.Tagged(metrics.ExecutorStatusTag(status.String())).UpdateGauge(metrics.ShardDistributorTotalExecutors, float64(count)) } } func (p *namespaceProcessor) emitOldestExecutorHeartbeatLag(namespaceState *store.NamespaceState, metricsLoopScope metrics.Scope) { if len(namespaceState.Executors) == 0 { return } var oldestHeartbeat time.Time for _, executor := range namespaceState.Executors { if oldestHeartbeat.IsZero() || executor.LastHeartbeat.Before(oldestHeartbeat) { oldestHeartbeat = executor.LastHeartbeat } } lag := p.timeSource.Now().Sub(oldestHeartbeat) metricsLoopScope.UpdateGauge(metrics.ShardDistributorOldestExecutorHeartbeatLag, float64(lag.Milliseconds())) } func (p *namespaceProcessor) findDeletedShards(namespaceState *store.NamespaceState) map[string]store.ShardState { deletedShards := make(map[string]store.ShardState) for executorID, executor := range namespaceState.Executors { for shardID, shardState := range executor.ReportedShards { if shardState.Status == types.ShardStatusDONE { deletedShards[shardID] = store.ShardState{ ExecutorID: executorID, } } } } return deletedShards } func (p *namespaceProcessor) findShardsToReassign( activeExecutors []string, namespaceState *store.NamespaceState, deletedShards map[string]store.ShardState, staleExecutors map[string]int64, ) ([]string, map[string][]string) { allShards := make(map[string]struct{}) for _, shardID := range getShards(p.namespaceCfg, namespaceState, deletedShards) { allShards[shardID] = struct{}{} } shardsToReassign := make([]string, 0) currentAssignments := make(map[string][]string) for _, executorID := range activeExecutors { currentAssignments[executorID] = []string{} } for executorID, state := range namespaceState.ShardAssignments { isActive := namespaceState.Executors[executorID].Status == types.ExecutorStatusACTIVE _, isStale := staleExecutors[executorID] for shardID := range state.AssignedShards { if _, ok := allShards[shardID]; ok { delete(allShards, shardID) // If executor is active AND not stale, keep the assignment if isActive && !isStale { currentAssignments[executorID] = append(currentAssignments[executorID], shardID) } else { // Otherwise, reassign the shard (executor is either inactive or stale) shardsToReassign = append(shardsToReassign, shardID) } } } } for shardID := range allShards { shardsToReassign = append(shardsToReassign, shardID) } return shardsToReassign, currentAssignments } func (*namespaceProcessor) updateAssignments(shardsToReassign []string, activeExecutors []string, currentAssignments map[string][]string) (distributionChanged bool) { if len(shardsToReassign) == 0 { return false } i := rand.Intn(len(activeExecutors)) for _, shardID := range shardsToReassign { executorID := activeExecutors[i%len(activeExecutors)] currentAssignments[executorID] = append(currentAssignments[executorID], shardID) i++ } return true } // calcShardLoad returns a map of shardID to its load based on the latest reported shard loads from executors func calcShardLoad(namespaceState *store.NamespaceState) map[string]float64 { shardLoad := make(map[string]float64) for _, state := range namespaceState.Executors { for shardID, report := range state.ReportedShards { shardLoad[shardID] = report.ShardLoad } } return shardLoad } // rebalanceByShardLoad does a rebalance if a difference between hottest and coldest executors' loads is more than maxDeviation // in this case the hottest shard will be moved to the coldest executor func (p *namespaceProcessor) rebalanceByShardLoad(shardLoad map[string]float64, currentAssignments map[string][]string) (distributedChanged bool) { // no rebalance if there are no more than 1 executor if len(currentAssignments) < 2 { return false } var ( hottestExecutorLoad = float64(0) hottestExecutorID = "" hottestShardID = "" hottestShardLoad = float64(0) coldestExecutorLoad = math.MaxFloat64 coldestExecutorID = "" ) // finding loads of hottest, coldest executors and hottest shard executorLoad := make(map[string]float64) for executorID, shardIDs := range currentAssignments { for _, shardID := range shardIDs { executorLoad[executorID] += shardLoad[shardID] } if executorLoad[executorID] <= coldestExecutorLoad { coldestExecutorLoad = executorLoad[executorID] coldestExecutorID = executorID } if executorLoad[executorID] >= hottestExecutorLoad { hottestExecutorLoad = executorLoad[executorID] hottestExecutorID = executorID var maxShardLoad = float64(0) for _, shardID := range shardIDs { if shardLoad[shardID] >= maxShardLoad { hottestShardID = shardID maxShardLoad = shardLoad[shardID] } } hottestShardLoad = maxShardLoad } } // no rebalance if a deviation between coldest and hottest executors less than maxDeviation if hottestExecutorLoad/coldestExecutorLoad < p.sdConfig.LoadBalancingNaive.MaxDeviation(p.namespaceCfg.Name) { return false } // no rebalance if coldest executor becomes a hottest if coldestExecutorLoad+hottestShardLoad >= hottestExecutorLoad { return false } // remove the hottest Shard from the hottest executor // put it to the coldest executor for i, shardID := range currentAssignments[hottestExecutorID] { if shardID == hottestShardID { currentAssignments[hottestExecutorID] = append(currentAssignments[hottestExecutorID][:i], currentAssignments[hottestExecutorID][i+1:]...) } } currentAssignments[coldestExecutorID] = append(currentAssignments[coldestExecutorID], hottestShardID) return true } func (p *namespaceProcessor) getNewAssignmentsState(namespaceState *store.NamespaceState, currentAssignments map[string][]string) map[string]store.AssignedState { newState := make(map[string]store.AssignedState, len(currentAssignments)) for executorID, shards := range currentAssignments { assignedShardsMap := make(map[string]*types.ShardAssignment) for _, shardID := range shards { assignedShardsMap[shardID] = &types.ShardAssignment{Status: types.AssignmentStatusREADY} } modRevision := int64(0) // Should be 0 if we have not seen it yet if namespaceAssignments, ok := namespaceState.ShardAssignments[executorID]; ok { modRevision = namespaceAssignments.ModRevision } newState[executorID] = store.AssignedState{ AssignedShards: assignedShardsMap, LastUpdated: p.timeSource.Now().UTC(), ModRevision: modRevision, ShardHandoverStats: p.addHandoverStatsToExecutorAssignedState(namespaceState, executorID, shards), } } return newState } func (p *namespaceProcessor) addHandoverStatsToExecutorAssignedState( namespaceState *store.NamespaceState, executorID string, shardIDs []string, ) map[string]store.ShardHandoverStats { var newStats = make(map[string]store.ShardHandoverStats) // Prepare handover stats for each shard for _, shardID := range shardIDs { handoverStats := p.newHandoverStats(namespaceState, shardID, executorID) // If there is no handover (first assignment), we skip adding handover stats if handoverStats != nil { newStats[shardID] = *handoverStats } } return newStats } // newHandoverStats creates shard handover statistics if a handover occurred. func (p *namespaceProcessor) newHandoverStats( namespaceState *store.NamespaceState, shardID string, newExecutorID string, ) *store.ShardHandoverStats { logger := p.logger.WithTags( tag.ShardNamespace(p.namespaceCfg.Name), tag.ShardKey(shardID), tag.ShardExecutor(newExecutorID), ) // Fetch previous shard owners from cache prevExecutor, err := p.shardStore.GetShardOwner(context.Background(), p.namespaceCfg.Name, shardID) if err != nil && !errors.Is(err, store.ErrShardNotFound) { logger.Warn("failed to get shard owner for shard statistic", tag.Error(err)) return nil } // previous executor is not found in cache // meaning this is the first assignment of the shard // so we skip updating handover stats if prevExecutor == nil { return nil } // No change in assignment // meaning no handover occurred // skip updating handover stats if prevExecutor.ExecutorID == newExecutorID { return nil } // previous executor heartbeat is not found in namespace state // meaning the executor has already been cleaned up // skip updating handover stats prevExecutorHeartbeat, ok := namespaceState.Executors[prevExecutor.ExecutorID] if !ok { logger.Info("previous executor heartbeat not found, skipping handover stats") return nil } handoverType := types.HandoverTypeEMERGENCY // Consider it a graceful handover if the previous executor was in DRAINING or DRAINED status // otherwise, it's an emergency handover if prevExecutorHeartbeat.Status == types.ExecutorStatusDRAINING || prevExecutorHeartbeat.Status == types.ExecutorStatusDRAINED { handoverType = types.HandoverTypeGRACEFUL } return &store.ShardHandoverStats{ HandoverType: handoverType, PreviousExecutorLastHeartbeatTime: prevExecutorHeartbeat.LastHeartbeat, } } func (*namespaceProcessor) getActiveExecutors(namespaceState *store.NamespaceState, staleExecutors map[string]int64) []string { var activeExecutors []string for id, state := range namespaceState.Executors { // Executor must be ACTIVE and not stale if state.Status == types.ExecutorStatusACTIVE { if _, ok := staleExecutors[id]; !ok { activeExecutors = append(activeExecutors, id) } } } sort.Strings(activeExecutors) return activeExecutors } func assignShardsToEmptyExecutors(currentAssignments map[string][]string) bool { emptyExecutors := make([]string, 0) executorsWithShards := make([]string, 0) minShardsCurrentlyAssigned := 0 // Ensure the iteration is deterministic. executors := make([]string, 0, len(currentAssignments)) for executorID := range currentAssignments { executors = append(executors, executorID) } slices.Sort(executors) for _, executorID := range executors { if len(currentAssignments[executorID]) == 0 { emptyExecutors = append(emptyExecutors, executorID) } else { executorsWithShards = append(executorsWithShards, executorID) if minShardsCurrentlyAssigned == 0 || len(currentAssignments[executorID]) < minShardsCurrentlyAssigned { minShardsCurrentlyAssigned = len(currentAssignments[executorID]) } } } // If there are no empty executors or no executors with shards, we don't need to do anything. if len(emptyExecutors) == 0 || len(executorsWithShards) == 0 { return false } // We calculate the number of shards to assign each of the empty executors. The idea is to assume all current executors have // the same number of shards `minShardsCurrentlyAssigned`. We use the minimum so when steeling we don't have to worry about // steeling more shards that the executors have. // We then calculate the total number of assumed shards `minShardsCurrentlyAssigned * len(executorsWithShards)` and divide it by the // number of current executors. This gives us the number of shards per executor, thus the number of shards to assign to each of the // empty executors. numShardsToAssignEmptyExecutors := minShardsCurrentlyAssigned * len(executorsWithShards) / len(currentAssignments) stealRound := 0 for i := 0; i < numShardsToAssignEmptyExecutors; i++ { for _, emptyExecutor := range emptyExecutors { executorToSteelFrom := executorsWithShards[stealRound%len(executorsWithShards)] stealRound++ stolenShard := currentAssignments[executorToSteelFrom][0] currentAssignments[executorToSteelFrom] = currentAssignments[executorToSteelFrom][1:] currentAssignments[emptyExecutor] = append(currentAssignments[emptyExecutor], stolenShard) } } return true } func getShards(cfg config.Namespace, namespaceState *store.NamespaceState, deletedShards map[string]store.ShardState) []string { if cfg.Type == config.NamespaceTypeFixed { return makeShards(cfg.ShardNum) } else if cfg.Type == config.NamespaceTypeEphemeral { shards := make([]string, 0) for _, state := range namespaceState.ShardAssignments { for shardID := range state.AssignedShards { if _, ok := deletedShards[shardID]; !ok { shards = append(shards, shardID) } } } return shards } return nil } func makeShards(num int64) []string { shards := make([]string, num) for i := range num { shards[i] = strconv.FormatInt(i, 10) } return shards } ================================================ FILE: service/sharddistributor/leader/process/processor_test.go ================================================ package process import ( "context" "errors" "slices" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" metricmocks "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/config/configtest" "github.com/uber/cadence/service/sharddistributor/store" ) type testDependencies struct { ctrl *gomock.Controller store *store.MockStore election *store.MockElection timeSource clock.MockedTimeSource factory Factory cfg config.Namespace sdConfig *config.Config } func setupProcessorTest(t *testing.T, namespaceType string) *testDependencies { migrationConfig := configtest.NewTestMigrationConfig(t, configtest.ConfigEntry{ Key: dynamicproperties.ShardDistributorMigrationMode, Value: config.MigrationModeONBOARDED}) return setupProcessorTestWithMigrationConfig(t, namespaceType, migrationConfig) } func setupProcessorTestWithMigrationConfig(t *testing.T, namespaceType string, migrationConfig *config.Config) *testDependencies { ctrl := gomock.NewController(t) mockedClock := clock.NewMockedTimeSource() deps := &testDependencies{ ctrl: ctrl, store: store.NewMockStore(ctrl), election: store.NewMockElection(ctrl), timeSource: mockedClock, cfg: config.Namespace{Name: "test-ns", ShardNum: 2, Type: namespaceType, Mode: config.MigrationModeONBOARDED}, } deps.sdConfig = &config.Config{ LoadBalancingMode: func(namespace string) string { return config.LoadBalancingModeNAIVE }, LoadBalancingNaive: config.LoadBalancingNaiveConfig{ MaxDeviation: func(namespace string) float64 { return 2.0 }, }, MigrationMode: migrationConfig.MigrationMode, } deps.factory = NewProcessorFactory( testlogger.New(t), metrics.NewNoopMetricsClient(), mockedClock, config.ShardDistribution{ Process: config.LeaderProcess{ Period: time.Second, HeartbeatTTL: time.Second, }, }, deps.sdConfig, ) return deps } func TestRunAndTerminate(t *testing.T) { defer goleak.VerifyNone(t) mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election) ctx, cancel := context.WithCancel(context.Background()) mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{}, nil).AnyTimes() mocks.store.EXPECT().SubscribeToExecutorStatusChanges(gomock.Any(), mocks.cfg.Name).Return(make(chan int64), nil).AnyTimes() err := processor.Run(ctx) require.NoError(t, err) err = processor.Run(ctx) require.Error(t, err) assert.Contains(t, err.Error(), "processor is already running") err = processor.Terminate(context.Background()) require.NoError(t, err) err = processor.Terminate(context.Background()) require.Error(t, err) assert.Contains(t, err.Error(), "processor has not been started") cancel() } func TestRebalanceShards_InitialDistribution(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() state := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}, "exec-2": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}, } mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{Executors: state}, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "0").Return(nil, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "1").Return(nil, nil) mocks.election.EXPECT().Guard().Return(store.NopGuard()) mocks.store.EXPECT().AssignShards(gomock.Any(), mocks.cfg.Name, gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, _ string, request store.AssignShardsRequest, _ store.GuardFunc) error { assert.Len(t, request.NewState.ShardAssignments, 2) assert.Len(t, request.NewState.ShardAssignments["exec-1"].AssignedShards, 1) assert.Len(t, request.NewState.ShardAssignments["exec-2"].AssignedShards, 1) assert.Lenf(t, request.NewState.ShardAssignments["exec-1"].ShardHandoverStats, 0, "no handover stats should be present on initial assignment") assert.Lenf(t, request.NewState.ShardAssignments["exec-2"].ShardHandoverStats, 0, "no handover stats should be present on initial assignment") return nil }, ) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) } func TestRebalanceShards_ExecutorRemoved(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() heartbeats := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}, "exec-2": {Status: types.ExecutorStatusDRAINING, LastHeartbeat: now}, } assignments := map[string]store.AssignedState{ "exec-2": { AssignedShards: map[string]*types.ShardAssignment{ "0": {Status: types.AssignmentStatusREADY}, "1": {Status: types.AssignmentStatusREADY}, }, }, } mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: heartbeats, ShardAssignments: assignments, }, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "0").Return(&store.ShardOwner{ExecutorID: "exec-2"}, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "1").Return(&store.ShardOwner{ExecutorID: "exec-1"}, nil) mocks.election.EXPECT().Guard().Return(store.NopGuard()) mocks.store.EXPECT().AssignShards(gomock.Any(), mocks.cfg.Name, gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, _ string, request store.AssignShardsRequest, _ store.GuardFunc) error { assert.Len(t, request.NewState.ShardAssignments["exec-1"].AssignedShards, 2) assert.Len(t, request.NewState.ShardAssignments["exec-2"].AssignedShards, 0) assert.Lenf(t, request.NewState.ShardAssignments["exec-1"].ShardHandoverStats, 1, "only shard 0 should have handover stats") assert.Lenf(t, request.NewState.ShardAssignments["exec-2"].ShardHandoverStats, 0, "no handover stats should be present for drained executor") return nil }, ) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) } func TestRebalanceShards_ExecutorStale(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() heartbeats := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}, "exec-2": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-2 * time.Second)}, } assignments := map[string]store.AssignedState{ "exec-1": { AssignedShards: map[string]*types.ShardAssignment{ "0": {Status: types.AssignmentStatusREADY}, }, ModRevision: 1, }, "exec-2": { AssignedShards: map[string]*types.ShardAssignment{ "1": {Status: types.AssignmentStatusREADY}, }, ModRevision: 1, }, } mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: heartbeats, ShardAssignments: assignments, }, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "0").Return(&store.ShardOwner{ExecutorID: "exec-1"}, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "1").Return(&store.ShardOwner{ExecutorID: "exec-2"}, nil) mocks.election.EXPECT().Guard().Return(store.NopGuard()) mocks.store.EXPECT().AssignShards(gomock.Any(), mocks.cfg.Name, gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, _ string, request store.AssignShardsRequest, _ store.GuardFunc) error { assert.Len(t, request.NewState.ShardAssignments, 1) assert.Len(t, request.NewState.ShardAssignments["exec-1"].AssignedShards, 2) assert.Len(t, request.NewState.ShardAssignments["exec-1"].ShardHandoverStats, 1, "only shard 1 should have handover stats") assert.Equal(t, request.ExecutorsToDelete, map[string]int64{"exec-2": 1}) return nil }, ) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) } func TestRebalanceShards_NoActiveExecutors(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() state := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusDRAINING, LastHeartbeat: now}, } mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{Executors: state}, nil) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) } func TestRebalanceShards_NoActiveExecutors_WithStaleExecutors(t *testing.T) { t.Run("one stale executor", func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() executorStates := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusDRAINING, LastHeartbeat: now}, "exec-2": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-10 * time.Minute)}, } expectedStaleExecutorIDs := []string{"exec-2"} mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: executorStates, }, nil) mocks.election.EXPECT().Guard().Return(store.NopGuard()) mocks.store.EXPECT().DeleteExecutors(gomock.Any(), mocks.cfg.Name, gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, namespace string, executorIDs []string, _ store.GuardFunc) error { assert.ElementsMatch(t, expectedStaleExecutorIDs, executorIDs) assert.Equal(t, mocks.cfg.Name, namespace) return nil }) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) }) t.Run("all stale executor", func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() executorStates := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-10 * time.Minute)}, "exec-2": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-10 * time.Minute)}, } expectedStaleExecutorIDs := []string{"exec-1", "exec-2"} mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: executorStates, }, nil) mocks.election.EXPECT().Guard().Return(store.NopGuard()) mocks.store.EXPECT().DeleteExecutors(gomock.Any(), mocks.cfg.Name, gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, namespace string, executorIDs []string, _ store.GuardFunc) error { assert.ElementsMatch(t, expectedStaleExecutorIDs, executorIDs) assert.Equal(t, mocks.cfg.Name, namespace) return nil }) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) }) } func TestCleanupStaleExecutors(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() heartbeats := map[string]store.HeartbeatState{ "exec-active": {LastHeartbeat: now}, "exec-stale": {LastHeartbeat: now.Add(-2 * time.Second)}, } namespaceState := &store.NamespaceState{Executors: heartbeats} staleExecutors := processor.identifyStaleExecutors(namespaceState) assert.Equal(t, map[string]int64{"exec-stale": 0}, staleExecutors) } func TestCleanupStaleShardStats(t *testing.T) { t.Run("stale shard stats are deleted", func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now().UTC() heartbeats := map[string]store.HeartbeatState{ "exec-active": {LastHeartbeat: now, Status: types.ExecutorStatusACTIVE}, "exec-stale": {LastHeartbeat: now.Add(-2 * time.Second)}, } assignments := map[string]store.AssignedState{ "exec-active": { AssignedShards: map[string]*types.ShardAssignment{ "shard-1": {Status: types.AssignmentStatusREADY}, "shard-2": {Status: types.AssignmentStatusREADY}, }, }, "exec-stale": { AssignedShards: map[string]*types.ShardAssignment{ "shard-3": {Status: types.AssignmentStatusREADY}, }, }, } shardStats := map[string]store.ShardStatistics{ "shard-1": {SmoothedLoad: 1.0, LastUpdateTime: now, LastMoveTime: now}, "shard-2": {SmoothedLoad: 2.0, LastUpdateTime: now, LastMoveTime: now}, "shard-3": {SmoothedLoad: 3.0, LastUpdateTime: now.Add(-2 * time.Second), LastMoveTime: now.Add(-2 * time.Second)}, } namespaceState := &store.NamespaceState{ Executors: heartbeats, ShardAssignments: assignments, ShardStats: shardStats, } staleShardStats := processor.identifyStaleShardStats(namespaceState) assert.Equal(t, []string{"shard-3"}, staleShardStats) }) t.Run("recent shard stats are preserved", func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() expiredExecutor := now.Add(-2 * time.Second) namespaceState := &store.NamespaceState{ Executors: map[string]store.HeartbeatState{ "exec-stale": {LastHeartbeat: expiredExecutor}, }, ShardAssignments: map[string]store.AssignedState{}, ShardStats: map[string]store.ShardStatistics{ "shard-1": {SmoothedLoad: 5.0, LastUpdateTime: now, LastMoveTime: now}, }, } staleShardStats := processor.identifyStaleShardStats(namespaceState) assert.Empty(t, staleShardStats) }) } func TestRebalance_StoreErrors(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) expectedErr := errors.New("store is down") mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(nil, expectedErr) err := processor.rebalanceShards(context.Background()) require.Error(t, err) assert.Contains(t, err.Error(), expectedErr.Error()) now := mocks.timeSource.Now() mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: map[string]store.HeartbeatState{"e": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}}, }, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "0").Return(nil, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "1").Return(nil, nil) mocks.election.EXPECT().Guard().Return(store.NopGuard()) mocks.store.EXPECT().AssignShards(gomock.Any(), mocks.cfg.Name, gomock.Any(), gomock.Any()).Return(expectedErr) err = processor.rebalanceShards(context.Background()) require.Error(t, err) assert.Contains(t, err.Error(), expectedErr.Error()) } func TestRunLoop_SubscriptionError(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) expectedErr := errors.New("subscription failed") mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{}, nil) mocks.store.EXPECT().SubscribeToExecutorStatusChanges(gomock.Any(), mocks.cfg.Name).Return(nil, expectedErr) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() processor.runRebalancingLoop(context.Background()) }() wg.Wait() } func TestRunLoop_ContextCancellation(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) ctx, cancel := context.WithCancel(context.Background()) // Setup for the initial call to rebalanceShards and the subscription mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{}, nil) mocks.store.EXPECT().SubscribeToExecutorStatusChanges(gomock.Any(), mocks.cfg.Name).Return(make(chan int64), nil) processor.wg.Add(1) // Run the process in a separate goroutine to avoid blocking the test go processor.runProcess(ctx) // Wait for the two loops (rebalance and cleanup) to create their tickers mocks.timeSource.BlockUntil(2) // Now, cancel the context to signal the loops to stop cancel() // Wait for the main process loop to exit gracefully processor.wg.Wait() } func TestRebalanceShards_WithUnassignedShardsButMigrationModeNotOnboarded(t *testing.T) { migrationConfig := configtest.NewTestMigrationConfig(t, configtest.ConfigEntry{ Key: dynamicproperties.ShardDistributorMigrationMode, Value: config.MigrationModeDISTRIBUTEDPASSTHROUGH}) mocks := setupProcessorTestWithMigrationConfig(t, config.NamespaceTypeFixed, migrationConfig) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() heartbeats := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}, } // Note: shard "1" is missing from assignments assignments := map[string]store.AssignedState{ "exec-1": { AssignedShards: map[string]*types.ShardAssignment{ "0": {Status: types.AssignmentStatusREADY}, }, }, } mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "0").Return(nil, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "1").Return(nil, nil) mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: heartbeats, ShardAssignments: assignments, }, nil) // These are the expected calls in case of onboarding, with the assignment of new shards mocks.election.EXPECT().Guard().Return(store.NopGuard()).Times(0) mocks.store.EXPECT().AssignShards(gomock.Any(), mocks.cfg.Name, gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, _ string, request store.AssignShardsRequest, _ store.GuardFunc) error { assert.Len(t, request.NewState.ShardAssignments["exec-1"].AssignedShards, 2, "Both shards should now be assigned to exec-1") return nil }, ).Times(0) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) } func TestRebalanceShards_ShadowModeWithStaleExecutors(t *testing.T) { t.Run("stale executors are deleted in shadow mode", func(t *testing.T) { migrationConfig := configtest.NewTestMigrationConfig(t, configtest.ConfigEntry{ Key: dynamicproperties.ShardDistributorMigrationMode, Value: config.MigrationModeDISTRIBUTEDPASSTHROUGH}) mocks := setupProcessorTestWithMigrationConfig(t, config.NamespaceTypeFixed, migrationConfig) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() heartbeats := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}, "exec-2": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-10 * time.Second)}, } assignments := map[string]store.AssignedState{ "exec-1": { AssignedShards: map[string]*types.ShardAssignment{ "0": {Status: types.AssignmentStatusREADY}, }, ModRevision: 1, }, "exec-2": { AssignedShards: map[string]*types.ShardAssignment{ "1": {Status: types.AssignmentStatusREADY}, }, ModRevision: 1, }, } mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: heartbeats, ShardAssignments: assignments, }, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "0").Return(&store.ShardOwner{ExecutorID: "exec-1"}, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "1").Return(&store.ShardOwner{ExecutorID: "exec-2"}, nil) mocks.election.EXPECT().Guard().Return(store.NopGuard()) mocks.store.EXPECT().DeleteExecutors(gomock.Any(), mocks.cfg.Name, []string{"exec-2"}, gomock.Any()).Return(nil) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) }) t.Run("delete executors error is non-blocking in shadow mode", func(t *testing.T) { migrationConfig := configtest.NewTestMigrationConfig(t, configtest.ConfigEntry{ Key: dynamicproperties.ShardDistributorMigrationMode, Value: config.MigrationModeDISTRIBUTEDPASSTHROUGH}) mocks := setupProcessorTestWithMigrationConfig(t, config.NamespaceTypeFixed, migrationConfig) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() heartbeats := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}, "exec-2": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-10 * time.Second)}, } assignments := map[string]store.AssignedState{ "exec-1": { AssignedShards: map[string]*types.ShardAssignment{ "0": {Status: types.AssignmentStatusREADY}, }, ModRevision: 1, }, "exec-2": { AssignedShards: map[string]*types.ShardAssignment{ "1": {Status: types.AssignmentStatusREADY}, }, ModRevision: 1, }, } mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: heartbeats, ShardAssignments: assignments, }, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "0").Return(&store.ShardOwner{ExecutorID: "exec-1"}, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "1").Return(&store.ShardOwner{ExecutorID: "exec-2"}, nil) mocks.election.EXPECT().Guard().Return(store.NopGuard()) mocks.store.EXPECT().DeleteExecutors(gomock.Any(), mocks.cfg.Name, []string{"exec-2"}, gomock.Any()).Return(errors.New("transaction failed")) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) }) t.Run("no stale executors - delete not called in shadow mode", func(t *testing.T) { migrationConfig := configtest.NewTestMigrationConfig(t, configtest.ConfigEntry{ Key: dynamicproperties.ShardDistributorMigrationMode, Value: config.MigrationModeDISTRIBUTEDPASSTHROUGH}) mocks := setupProcessorTestWithMigrationConfig(t, config.NamespaceTypeFixed, migrationConfig) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() heartbeats := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}, } assignments := map[string]store.AssignedState{ "exec-1": { AssignedShards: map[string]*types.ShardAssignment{ "0": {Status: types.AssignmentStatusREADY}, }, }, } mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: heartbeats, ShardAssignments: assignments, }, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "0").Return(nil, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "1").Return(nil, nil) // DeleteExecutors should not be called when there are no stale executors, thus Times(0) mocks.store.EXPECT().DeleteExecutors(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) }) } func TestRebalanceShards_NoShardsToReassign(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() heartbeats := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}, } assignments := map[string]store.AssignedState{ "exec-1": { AssignedShards: map[string]*types.ShardAssignment{ "0": {Status: types.AssignmentStatusREADY}, "1": {Status: types.AssignmentStatusREADY}, }, }, } mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: heartbeats, ShardAssignments: assignments, }, nil) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) } func TestRebalanceShards_WithUnassignedShards(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() heartbeats := map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE, LastHeartbeat: now}, } // Note: shard "1" is missing from assignments assignments := map[string]store.AssignedState{ "exec-1": { AssignedShards: map[string]*types.ShardAssignment{ "0": {Status: types.AssignmentStatusREADY}, }, }, } mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "0").Return(nil, nil) mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, "1").Return(nil, nil) mocks.store.EXPECT().GetState(gomock.Any(), mocks.cfg.Name).Return(&store.NamespaceState{ Executors: heartbeats, ShardAssignments: assignments, }, nil) mocks.election.EXPECT().Guard().Return(store.NopGuard()) mocks.store.EXPECT().AssignShards(gomock.Any(), mocks.cfg.Name, gomock.Any(), gomock.Any()).DoAndReturn( func(_ context.Context, _ string, request store.AssignShardsRequest, _ store.GuardFunc) error { assert.Len(t, request.NewState.ShardAssignments["exec-1"].AssignedShards, 2, "Both shards should now be assigned to exec-1") return nil }, ) err := processor.rebalanceShards(context.Background()) require.NoError(t, err) } func TestGetShards_Utility(t *testing.T) { t.Run("Fixed type", func(t *testing.T) { cfg := config.Namespace{Type: config.NamespaceTypeFixed, ShardNum: 5} shards := getShards(cfg, nil, nil) assert.Equal(t, []string{"0", "1", "2", "3", "4"}, shards) }) t.Run("Ephemeral type", func(t *testing.T) { cfg := config.Namespace{Type: config.NamespaceTypeEphemeral} nsState := &store.NamespaceState{ ShardAssignments: map[string]store.AssignedState{ "executor1": { AssignedShards: map[string]*types.ShardAssignment{ "s0": {Status: types.AssignmentStatusREADY}, "s1": {Status: types.AssignmentStatusREADY}, "s2": {Status: types.AssignmentStatusREADY}, }, }, "executor2": { AssignedShards: map[string]*types.ShardAssignment{ "s3": {Status: types.AssignmentStatusREADY}, "s4": {Status: types.AssignmentStatusREADY}, }, }, }, } shards := getShards(cfg, nsState, nil) slices.Sort(shards) assert.Equal(t, []string{"s0", "s1", "s2", "s3", "s4"}, shards) }) t.Run("Ephemeral type with deleted shards", func(t *testing.T) { cfg := config.Namespace{Type: config.NamespaceTypeEphemeral} nsState := &store.NamespaceState{ ShardAssignments: map[string]store.AssignedState{ "executor1": { AssignedShards: map[string]*types.ShardAssignment{ "s0": {Status: types.AssignmentStatusREADY}, "s1": {Status: types.AssignmentStatusREADY}, "s2": {Status: types.AssignmentStatusREADY}, }, }, "executor2": { AssignedShards: map[string]*types.ShardAssignment{ "s3": {Status: types.AssignmentStatusREADY}, "s4": {Status: types.AssignmentStatusREADY}, }, }, }, } deletedShards := map[string]store.ShardState{ "s0": {}, "s1": {}, } shards := getShards(cfg, nsState, deletedShards) slices.Sort(shards) assert.Equal(t, []string{"s2", "s3", "s4"}, shards) }) // Unknown type t.Run("Other type", func(t *testing.T) { cfg := config.Namespace{Type: "other"} shards := getShards(cfg, nil, nil) assert.Nil(t, shards) }) } func TestAssignShardsToEmptyExecutors(t *testing.T) { cases := []struct { name string inputAssignments map[string][]string expectedAssignments map[string][]string expectedDistributonChanged bool }{ { name: "no executors", inputAssignments: map[string][]string{}, expectedAssignments: map[string][]string{}, expectedDistributonChanged: false, }, { name: "no empty executors", inputAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2", "shard-3", "shard-4", "shard-5", "shard-6"}, "exec-2": {"shard-7", "shard-8"}, }, expectedAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2", "shard-3", "shard-4", "shard-5", "shard-6"}, "exec-2": {"shard-7", "shard-8"}, }, expectedDistributonChanged: false, }, { name: "empty executor", inputAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2", "shard-3", "shard-4", "shard-5", "shard-6"}, "exec-2": {"shard-7", "shard-8", "shard-9", "shard-10"}, "exec-3": {}, }, expectedAssignments: map[string][]string{ "exec-1": {"shard-2", "shard-3", "shard-4", "shard-5", "shard-6"}, "exec-2": {"shard-8", "shard-9", "shard-10"}, "exec-3": {"shard-1", "shard-7"}, }, expectedDistributonChanged: true, }, { name: "all empty executors", inputAssignments: map[string][]string{"exec-1": {}, "exec-2": {}, "exec-3": {}}, expectedAssignments: map[string][]string{"exec-1": {}, "exec-2": {}, "exec-3": {}}, expectedDistributonChanged: false, }, { name: "multiple empty executors", inputAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2", "shard-3", "shard-4", "shard-5", "shard-6", "shard-7", "shard-8", "shard-9", "shard-10"}, "exec-2": {"shard-11", "shard-12", "shard-13", "shard-14", "shard-15", "shard-16", "shard-17"}, "exec-3": {"shard-18", "shard-19", "shard-20", "shard-21", "shard-22", "shard-23", "shard-24"}, "exec-4": {}, "exec-5": {}, }, expectedAssignments: map[string][]string{ "exec-1": {"shard-4", "shard-5", "shard-6", "shard-7", "shard-8", "shard-9", "shard-10"}, "exec-2": {"shard-14", "shard-15", "shard-16", "shard-17"}, "exec-3": {"shard-20", "shard-21", "shard-22", "shard-23", "shard-24"}, "exec-4": {"shard-1", "shard-18", "shard-12", "shard-3"}, "exec-5": {"shard-11", "shard-2", "shard-19", "shard-13"}, }, expectedDistributonChanged: true, }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { actualDistributionChanged := assignShardsToEmptyExecutors(c.inputAssignments) assert.Equal(t, c.expectedAssignments, c.inputAssignments) assert.Equal(t, c.expectedDistributonChanged, actualDistributionChanged) }) } } func TestNewHandoverStats(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := time.Now().UTC() shardID := "shard-1" newExecutorID := "exec-new" type testCase struct { name string getOwner *store.ShardOwner getOwnerErr error executors map[string]store.HeartbeatState expectShardStats *store.ShardHandoverStats // nil means expect nil result } testCases := []testCase{ { name: "error other than shard not found -> stat without handover", getOwner: nil, getOwnerErr: errors.New("random error"), executors: map[string]store.HeartbeatState{}, expectShardStats: nil, }, { name: "ErrShardNotFound -> stat without handover", getOwner: nil, getOwnerErr: store.ErrShardNotFound, executors: map[string]store.HeartbeatState{}, expectShardStats: nil, }, { name: "same executor as previous -> nil", getOwner: &store.ShardOwner{ExecutorID: newExecutorID}, getOwnerErr: nil, executors: map[string]store.HeartbeatState{ newExecutorID: { Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-10 * time.Second)}, }, expectShardStats: nil, }, { name: "prev executor different but heartbeat missing -> no handover", getOwner: &store.ShardOwner{ExecutorID: "old-exec"}, getOwnerErr: nil, executors: map[string]store.HeartbeatState{}, expectShardStats: nil, }, { name: "prev executor ACTIVE -> emergency handover", getOwner: &store.ShardOwner{ExecutorID: "old-active"}, getOwnerErr: nil, executors: map[string]store.HeartbeatState{ "old-active": { Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-10 * time.Second), }, }, expectShardStats: &store.ShardHandoverStats{ HandoverType: types.HandoverTypeEMERGENCY, PreviousExecutorLastHeartbeatTime: now.Add(-10 * time.Second), }, }, { name: "prev executor DRAINING -> graceful handover", getOwner: &store.ShardOwner{ExecutorID: "old-draining"}, getOwnerErr: nil, executors: map[string]store.HeartbeatState{ "old-draining": { Status: types.ExecutorStatusDRAINING, LastHeartbeat: now.Add(-10 * time.Second), }, }, expectShardStats: &store.ShardHandoverStats{ HandoverType: types.HandoverTypeGRACEFUL, PreviousExecutorLastHeartbeatTime: now.Add(-10 * time.Second), }, }, { name: "prev executor DRAINED -> graceful handover", getOwner: &store.ShardOwner{ExecutorID: "old-drained"}, getOwnerErr: nil, executors: map[string]store.HeartbeatState{ "old-drained": { Status: types.ExecutorStatusDRAINING, LastHeartbeat: now.Add(-10 * time.Second), }, }, expectShardStats: &store.ShardHandoverStats{ HandoverType: types.HandoverTypeGRACEFUL, PreviousExecutorLastHeartbeatTime: now.Add(-10 * time.Second), }, }, } for _, tc := range testCases { mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, shardID).Return(tc.getOwner, tc.getOwnerErr) t.Run(tc.name, func(t *testing.T) { stat := processor.newHandoverStats(&store.NamespaceState{Executors: tc.executors}, shardID, newExecutorID) if tc.expectShardStats == nil { require.Nil(t, stat) return } require.NotNil(t, stat) require.Equal(t, tc.expectShardStats, stat) }) } } func TestAddHandoverStatsToExecutorAssignedState(t *testing.T) { now := time.Now().UTC() executorID := "exec-1" shardIDs := []string{"shard-1", "shard-2"} for name, tc := range map[string]struct { name string executors map[string]store.HeartbeatState getOwners map[string]*store.ShardOwner getOwnerErrs map[string]error expected map[string]store.ShardHandoverStats }{ "no previous owner for both shards": { getOwners: map[string]*store.ShardOwner{"shard-1": nil, "shard-2": nil}, getOwnerErrs: map[string]error{"shard-1": store.ErrShardNotFound, "shard-2": store.ErrShardNotFound}, executors: map[string]store.HeartbeatState{}, expected: map[string]store.ShardHandoverStats{}, }, "emergency handover for shard-1, no handover for shard-2": { getOwners: map[string]*store.ShardOwner{ "shard-1": {ExecutorID: "old-active"}, "shard-2": nil, }, getOwnerErrs: map[string]error{ "shard-1": nil, "shard-2": store.ErrShardNotFound, }, executors: map[string]store.HeartbeatState{ "old-active": { Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-10 * time.Second), }, }, expected: map[string]store.ShardHandoverStats{ "shard-1": { HandoverType: types.HandoverTypeEMERGENCY, PreviousExecutorLastHeartbeatTime: now.Add(-10 * time.Second), }, }, }, "graceful handover for shard-1": { getOwners: map[string]*store.ShardOwner{ "shard-1": {ExecutorID: "old-draining"}, "shard-2": nil, }, getOwnerErrs: map[string]error{ "shard-1": nil, }, executors: map[string]store.HeartbeatState{ "old-draining": { Status: types.ExecutorStatusDRAINING, LastHeartbeat: now.Add(-20 * time.Second), }, }, expected: map[string]store.ShardHandoverStats{ "shard-1": { HandoverType: types.HandoverTypeGRACEFUL, PreviousExecutorLastHeartbeatTime: now.Add(-20 * time.Second), }, }, }, "same executor as previous, no handover": { getOwners: map[string]*store.ShardOwner{ "shard-1": {ExecutorID: executorID}, "shard-2": nil, }, getOwnerErrs: map[string]error{ "shard-1": nil, }, executors: map[string]store.HeartbeatState{ executorID: { Status: types.ExecutorStatusACTIVE, LastHeartbeat: now, }, }, expected: map[string]store.ShardHandoverStats{}, }, } { t.Run(name, func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) for _, shardID := range shardIDs { mocks.store.EXPECT().GetShardOwner(gomock.Any(), mocks.cfg.Name, shardID).Return(tc.getOwners[shardID], tc.getOwnerErrs[shardID]).AnyTimes() } namespaceState := &store.NamespaceState{ Executors: tc.executors, } stats := processor.addHandoverStatsToExecutorAssignedState(namespaceState, executorID, shardIDs) assert.Equal(t, tc.expected, stats) }) } } func TestRebalanceByShardLoad(t *testing.T) { cases := []struct { name string shardLoad map[string]float64 currentAssignments map[string][]string maxDeviation float64 expectedDistributionChange bool expectedAssignments map[string][]string }{ { name: "single executor - no rebalance", shardLoad: map[string]float64{"shard-1": 10.0}, currentAssignments: map[string][]string{"exec-1": {"shard-1"}}, maxDeviation: 2.0, expectedDistributionChange: false, expectedAssignments: map[string][]string{"exec-1": {"shard-1"}}, }, { name: "balanced load - no rebalance needed", shardLoad: map[string]float64{ "shard-1": 10.0, "shard-2": 10.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1"}, // 10.0 "exec-2": {"shard-2"}, // 10.0 }, maxDeviation: 2.0, expectedDistributionChange: false, expectedAssignments: map[string][]string{ "exec-1": {"shard-1"}, // 10.0 "exec-2": {"shard-2"}, // 10.0 }, }, { name: "deviation below threshold - no rebalance", shardLoad: map[string]float64{ "shard-1": 10.0, "shard-2": 15.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1"}, // 10.0 "exec-2": {"shard-2"}, // 15.0 }, maxDeviation: 2.0, expectedDistributionChange: false, expectedAssignments: map[string][]string{ "exec-1": {"shard-1"}, // 10.0 "exec-2": {"shard-2"}, // 15.0 }, }, { name: "multiple shards - hottest moved", shardLoad: map[string]float64{ "shard-1": 5.0, "shard-2": 30.0, "shard-3": 20.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1"}, // 5.0 "exec-2": {"shard-2", "shard-3"}, // 50.0 }, maxDeviation: 2.0, expectedDistributionChange: true, expectedAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2"}, // 35.0 "exec-2": {"shard-3"}, // 20.0 }, }, { name: "coldest would become hottest - no rebalance", shardLoad: map[string]float64{ "shard-1": 10.0, "shard-2": 100.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1"}, // 10.0 "exec-2": {"shard-2"}, // 100.0 }, maxDeviation: 2.0, expectedDistributionChange: false, expectedAssignments: map[string][]string{ "exec-1": {"shard-1"}, "exec-2": {"shard-2"}, }, }, { name: "multiple shards per executor", shardLoad: map[string]float64{ "shard-1": 5.0, "shard-2": 5.0, "shard-3": 40.0, "shard-4": 30.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2"}, // 10.0 "exec-2": {"shard-3", "shard-4"}, // 70.0 }, maxDeviation: 2.0, expectedDistributionChange: true, expectedAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2", "shard-3"}, // 50.0 "exec-2": {"shard-4"}, // 30.0 }, }, { name: "zero load shards - no rebalance", shardLoad: map[string]float64{ "shard-1": 0.0, "shard-2": 50.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1"}, // 0.0 "exec-2": {"shard-2"}, // 50.0 }, maxDeviation: 2.0, expectedDistributionChange: false, expectedAssignments: map[string][]string{ "exec-1": {"shard-1"}, "exec-2": {"shard-2"}, }, }, { name: "new shard load - equal shards - no rebalance", shardLoad: map[string]float64{ "shard-2": 0.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1"}, // 0.0 "exec-2": {"shard-2"}, // 50.0 }, maxDeviation: 2.0, expectedDistributionChange: false, expectedAssignments: map[string][]string{ "exec-1": {"shard-1"}, "exec-2": {"shard-2"}, }, }, { name: "four executors - balanced load", shardLoad: map[string]float64{ "shard-1": 10.0, "shard-2": 10.0, "shard-3": 10.0, "shard-4": 10.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1"}, "exec-2": {"shard-2"}, "exec-3": {"shard-3"}, "exec-4": {"shard-4"}, }, maxDeviation: 2.0, expectedDistributionChange: false, expectedAssignments: map[string][]string{ "exec-1": {"shard-1"}, "exec-2": {"shard-2"}, "exec-3": {"shard-3"}, "exec-4": {"shard-4"}, }, }, { name: "four executors - one overloaded - no rebalance", shardLoad: map[string]float64{ "shard-1": 10.0, "shard-2": 10.0, "shard-3": 10.0, "shard-4": 50.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1"}, "exec-2": {"shard-2"}, "exec-3": {"shard-3"}, "exec-4": {"shard-4"}, }, maxDeviation: 2.0, expectedDistributionChange: false, expectedAssignments: map[string][]string{ "exec-1": {"shard-1"}, "exec-2": {"shard-2"}, "exec-3": {"shard-3"}, "exec-4": {"shard-4"}, }, }, { name: "four executors - uneven distribution - stale executor", shardLoad: map[string]float64{ "shard-1": 15.0, "shard-2": 15.0, "shard-3": 15.0, "shard-4": 15.0, "shard-5": 40.0, "shard-6": 40.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2", "shard-5"}, // 70.0 "exec-2": {"shard-3", "shard-4"}, // 30.0 "exec-3": {}, // 0.0 "exec-4": {"shard-6"}, // 40.0 }, maxDeviation: 2.0, expectedDistributionChange: true, expectedAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2"}, // 30.0 "exec-2": {"shard-3", "shard-4"}, // 30.0 "exec-3": {"shard-5"}, // 40.0 "exec-4": {"shard-6"}, // 40.0 }, }, { name: "four executors - mixed load with multiple shards", shardLoad: map[string]float64{ "shard-1": 5.0, "shard-2": 5.0, "shard-3": 5.0, "shard-4": 2.0, "shard-5": 25.0, "shard-6": 25.0, "shard-7": 15.0, "shard-8": 15.0, }, currentAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2"}, // 10.0 "exec-2": {"shard-3", "shard-4"}, // 7.0 "exec-3": {"shard-5", "shard-6"}, // 50.0 "exec-4": {"shard-7", "shard-8"}, // 30.0 }, maxDeviation: 2.0, expectedDistributionChange: true, expectedAssignments: map[string][]string{ "exec-1": {"shard-1", "shard-2"}, // 10.0 "exec-2": {"shard-3", "shard-4", "shard-6"}, // 32.0 "exec-3": {"shard-5"}, // 25.0 "exec-4": {"shard-7", "shard-8"}, // 30.0 }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) mocks.cfg.Name = tc.name mocks.sdConfig.LoadBalancingNaive.MaxDeviation = func(namespace string) float64 { return tc.maxDeviation } defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) distributionChanged := processor.rebalanceByShardLoad(tc.shardLoad, tc.currentAssignments) assert.Equal(t, tc.expectedDistributionChange, distributionChanged, "distribution change mismatch") assert.Equal(t, tc.expectedAssignments, tc.currentAssignments, "final assignments mismatch") }) } } func TestEmitExecutorMetric(t *testing.T) { tests := []struct { name string executors map[string]store.HeartbeatState expectedCounts map[types.ExecutorStatus]int }{ { name: "empty executors", executors: map[string]store.HeartbeatState{}, expectedCounts: map[types.ExecutorStatus]int{}, }, { name: "single active executor", executors: map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE}, }, expectedCounts: map[types.ExecutorStatus]int{ types.ExecutorStatusACTIVE: 1, }, }, { name: "multiple executors", executors: map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE}, "exec-2": {Status: types.ExecutorStatusACTIVE}, "exec-3": {Status: types.ExecutorStatusDRAINING}, "exec-4": {Status: types.ExecutorStatusDRAINED}, }, expectedCounts: map[types.ExecutorStatus]int{ types.ExecutorStatusACTIVE: 2, types.ExecutorStatusDRAINING: 1, types.ExecutorStatusDRAINED: 1, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) namespaceState := &store.NamespaceState{ Executors: tt.executors, } metricsScope := &metricmocks.Scope{} for status, count := range tt.expectedCounts { taggedScope := &metricmocks.Scope{} metricsScope.On("Tagged", metrics.ExecutorStatusTag(status.String())).Return(taggedScope).Once() taggedScope.On("UpdateGauge", metrics.ShardDistributorTotalExecutors, float64(count)).Once() } processor.emitExecutorMetric(namespaceState, metricsScope) metricsScope.AssertExpectations(t) }) } } func TestEmitOldestExecutorHeartbeatLag(t *testing.T) { tests := []struct { name string executors map[string]store.HeartbeatState expectedLag *float64 }{ { name: "empty executors", executors: map[string]store.HeartbeatState{}, expectedLag: nil, }, { name: "single executor", executors: map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE}, }, expectedLag: common.Float64Ptr(5000), }, { name: "multiple executors", executors: map[string]store.HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE}, // 5 seconds "exec-2": {Status: types.ExecutorStatusACTIVE}, // 10 seconds (oldest) "exec-3": {Status: types.ExecutorStatusDRAINING}, // 3 seconds }, expectedLag: common.Float64Ptr(10000), // 10 seconds }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) now := mocks.timeSource.Now() if tt.name == "single executor" { tt.executors["exec-1"] = store.HeartbeatState{ Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-5 * time.Second), } } else if tt.name == "multiple executors" { tt.executors["exec-1"] = store.HeartbeatState{ Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-5 * time.Second), } tt.executors["exec-2"] = store.HeartbeatState{ Status: types.ExecutorStatusACTIVE, LastHeartbeat: now.Add(-10 * time.Second), // oldest } tt.executors["exec-3"] = store.HeartbeatState{ Status: types.ExecutorStatusDRAINING, LastHeartbeat: now.Add(-3 * time.Second), } } namespaceState := &store.NamespaceState{ Executors: tt.executors, } metricsScope := &metricmocks.Scope{} if tt.expectedLag != nil { metricsScope.On("UpdateGauge", metrics.ShardDistributorOldestExecutorHeartbeatLag, *tt.expectedLag).Once() } processor.emitOldestExecutorHeartbeatLag(namespaceState, metricsScope) metricsScope.AssertExpectations(t) }) } } func TestRunRebalanceTriggeringLoop(t *testing.T) { t.Run("no events from subscribe, trigger from ticker", func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) ctx, cancel := context.WithCancel(context.Background()) defer cancel() updateChan := make(chan int64) triggerChan := make(chan string, 1) go processor.rebalanceTriggeringLoop(ctx, updateChan, triggerChan) // Wait for ticker to be created mocks.timeSource.BlockUntil(1) // Advance time to trigger the ticker mocks.timeSource.Advance(processor.cfg.Period) // Expect trigger from periodic reconciliation select { case reason := <-triggerChan: assert.Equal(t, "Periodic reconciliation triggered", reason) case <-time.After(time.Second): t.Fatal("expected trigger from ticker, but timed out") } cancel() }) t.Run("events from subscribe before period, trigger from state change", func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) ctx, cancel := context.WithCancel(context.Background()) defer cancel() updateChan := make(chan int64, 1) triggerChan := make(chan string, 1) go processor.rebalanceTriggeringLoop(ctx, updateChan, triggerChan) // Wait for ticker to be created mocks.timeSource.BlockUntil(1) // Send a state change event before the ticker fires updateChan <- 1 // Expect trigger from state change select { case reason := <-triggerChan: assert.Equal(t, "State change detected", reason) case <-time.After(time.Second): t.Fatal("expected trigger from state change, but timed out") } cancel() }) t.Run("triggerChan full, multiple subscribe events, loop not stuck", func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Use unbuffered channel for updates to ensure they are processed one at a time updateChan := make(chan int64) triggerChan := make(chan string, 1) go processor.rebalanceTriggeringLoop(ctx, updateChan, triggerChan) // Wait for ticker to be created mocks.timeSource.BlockUntil(1) // Don't read from triggerChan yet to keep it full // Send multiple state change events for i := int64(0); i <= 10; i++ { select { case updateChan <- i: case <-time.After(time.Second): // Expect that the loop is not stuck t.Fatalf("failed to send update %d, channel blocked", i) } } // Expect trigger from state change select { case reason := <-triggerChan: assert.Equal(t, "State change detected", reason) case <-time.After(time.Second): t.Fatal("expected trigger from state change, but timed out") } cancel() }) t.Run("update channel closed stops loop", func(t *testing.T) { mocks := setupProcessorTest(t, config.NamespaceTypeFixed) defer mocks.ctrl.Finish() processor := mocks.factory.CreateProcessor(mocks.cfg, mocks.store, mocks.election).(*namespaceProcessor) ctx := context.Background() updateChan := make(chan int64) triggerChan := make(chan string, 1) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() processor.rebalanceTriggeringLoop(ctx, updateChan, triggerChan) }() // Wait for ticker to be created mocks.timeSource.BlockUntil(1) // Close update channel close(updateChan) // Wait for loop to exit done := make(chan struct{}) go func() { wg.Wait() close(done) }() select { case <-done: // Loop exited as expected case <-time.After(time.Second): t.Fatal("loop did not exit after updateChan closed") } }) } ================================================ FILE: service/sharddistributor/sharddistributorfx/fx_test.go ================================================ package sharddistributorfx import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/fx" "go.uber.org/fx/fxtest" "go.uber.org/goleak" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store" ) func TestFxServiceStartStop(t *testing.T) { defer goleak.VerifyNone(t) testDispatcher := yarpc.NewDispatcher(yarpc.Config{Name: "test"}) ctrl := gomock.NewController(t) app := fxtest.New(t, testlogger.Module(t), fx.Provide( func() metrics.Client { return metrics.NewNoopMetricsClient() }, func() rpc.Factory { factory := rpc.NewMockFactory(ctrl) factory.EXPECT().GetDispatcher().Return(testDispatcher) return factory }, func() *dynamicconfig.Collection { return dynamicconfig.NewNopCollection() }, fx.Annotated{Target: func() string { return "testHost" }, Name: "hostname"}, func() store.Elector { return store.NewMockElector(ctrl) }, func() store.Store { return store.NewMockStore(ctrl) }, func() config.ShardDistribution { return config.ShardDistribution{} }, func() clock.TimeSource { return clock.NewMockedTimeSource() }, ), Module) app.RequireStart().RequireStop() // API should be registered inside dispatcher. assert.True(t, len(testDispatcher.Introspect().Procedures) > 3) } ================================================ FILE: service/sharddistributor/sharddistributorfx/sharddistributorfx.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package sharddistributorfx import ( "go.uber.org/fx" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/rpc" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/handler" "github.com/uber/cadence/service/sharddistributor/leader/election" "github.com/uber/cadence/service/sharddistributor/leader/namespace" "github.com/uber/cadence/service/sharddistributor/leader/process" "github.com/uber/cadence/service/sharddistributor/store" meteredStore "github.com/uber/cadence/service/sharddistributor/store/wrappers/metered" "github.com/uber/cadence/service/sharddistributor/wrappers/grpc" "github.com/uber/cadence/service/sharddistributor/wrappers/metered" ) var Module = fx.Module("sharddistributor", namespace.Module, election.Module, process.Module, fx.Provide(config.NewConfig), fx.Decorate(func(s store.Store, metricsClient metrics.Client, logger log.Logger, timeSource clock.TimeSource) store.Store { return meteredStore.NewStore(s, metricsClient, logger, timeSource) }), fx.Invoke(registerHandlers)) type registerHandlersParams struct { fx.In ShardDistributionCfg config.ShardDistribution Logger log.Logger MetricsClient metrics.Client RPCFactory rpc.Factory Config *config.Config TimeSource clock.TimeSource Store store.Store Lifecycle fx.Lifecycle } func registerHandlers(params registerHandlersParams) error { dispatcher := params.RPCFactory.GetDispatcher() rawHandler := handler.NewHandler(params.Logger, params.TimeSource, params.ShardDistributionCfg, params.Store) wrappedHandler := metered.NewMetricsHandler(rawHandler, params.Logger, params.MetricsClient) executorHandler := handler.NewExecutorHandler(params.Logger, params.Store, params.TimeSource, params.ShardDistributionCfg, params.Config, params.MetricsClient) wrappedExecutor := metered.NewExecutorMetricsExecutor(executorHandler, params.Logger, params.MetricsClient) grpcHandler := grpc.NewGRPCHandler(wrappedHandler) grpcHandler.Register(dispatcher) executorGRPCHander := grpc.NewExecutorGRPCExecutor(wrappedExecutor) executorGRPCHander.Register(dispatcher) params.Lifecycle.Append(fx.StartStopHook(rawHandler.Start, rawHandler.Stop)) return nil } ================================================ FILE: service/sharddistributor/sharddistributorspectatorclient/factory/meteredClientDecorater.go ================================================ package factory import ( "context" "github.com/uber-go/tally" "go.uber.org/yarpc" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/client/spectatorclient/metricsconstants" ) // TODO: consider using gowrap to generate this code type meteredShardDistributorClient struct { client sharddistributor.Client metricsScope tally.Scope } // NewMeteredShardDistributorClient creates a new instance of metered shard distributor client func NewMeteredShardDistributorClient(client sharddistributor.Client, metricsScope tally.Scope) sharddistributor.Client { return &meteredShardDistributorClient{ client: client, metricsScope: metricsScope, } } func (c *meteredShardDistributorClient) GetShardOwner(ctx context.Context, request *types.GetShardOwnerRequest, opts ...yarpc.CallOption) (*types.GetShardOwnerResponse, error) { scope := c.metricsScope.Tagged(map[string]string{ metrics.OperationTagName: metricsconstants.ShardDistributorSpectatorGetShardOwnerOperationTagName, }) scope.Counter(metricsconstants.ShardDistributorSpectatorClientRequests).Inc(1) sw := scope.Timer(metricsconstants.ShardDistributorSpectatorClientLatency).Start() response, err := c.client.GetShardOwner(ctx, request, opts...) sw.Stop() if err != nil { scope.Counter(metricsconstants.ShardDistributorSpectatorClientFailures).Inc(1) } return response, err } func (c *meteredShardDistributorClient) WatchNamespaceState(ctx context.Context, request *types.WatchNamespaceStateRequest, opts ...yarpc.CallOption) (sharddistributor.WatchNamespaceStateClient, error) { scope := c.metricsScope.Tagged(map[string]string{ metrics.OperationTagName: metricsconstants.ShardDistributorSpectatorWatchNamespaceStateOperationTagName, }) scope.Counter(metricsconstants.ShardDistributorSpectatorClientRequests).Inc(1) sw := scope.Timer(metricsconstants.ShardDistributorSpectatorClientLatency).Start() stream, err := c.client.WatchNamespaceState(ctx, request, opts...) sw.Stop() if err != nil { scope.Counter(metricsconstants.ShardDistributorSpectatorClientFailures).Inc(1) } return stream, err } ================================================ FILE: service/sharddistributor/sharddistributorspectatorclient/factory/sharddistributorspectatorclient.go ================================================ package factory import ( "github.com/uber-go/tally" "go.uber.org/fx" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/client/sharddistributor" "github.com/uber/cadence/client/wrappers/grpc" "github.com/uber/cadence/client/wrappers/retryable" "github.com/uber/cadence/client/wrappers/timeout" "github.com/uber/cadence/common" ) type Params struct { fx.In YarpcClient sharddistributorv1.ShardDistributorAPIYARPCClient MetricsScope tally.Scope } func NewShardDistributorSpectatorClient(params Params) (sharddistributor.Client, error) { // Wrap the YARPC client with GRPC wrapper client := grpc.NewShardDistributorClient(params.YarpcClient) // Add timeout wrapper client = timeout.NewShardDistributorClient(client, timeout.ShardDistributorDefaultTimeout) // Add metered wrapper if params.MetricsScope != nil { client = NewMeteredShardDistributorClient(client, params.MetricsScope) } // Add retry wrapper client = retryable.NewShardDistributorClient( client, common.CreateShardDistributorServiceRetryPolicy(), common.IsServiceTransientError, ) return client, nil } ================================================ FILE: service/sharddistributor/store/etcd/etcdclient/client.go ================================================ package etcdclient import clientv3 "go.etcd.io/etcd/client/v3" //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination=client_mock.go Client // Client is an interface that groups the etcd clientv3 interfaces type Client interface { clientv3.Cluster clientv3.KV clientv3.Lease clientv3.Watcher clientv3.Auth clientv3.Maintenance } ================================================ FILE: service/sharddistributor/store/etcd/etcdclient/client_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: client.go // // Generated by this command: // // mockgen -package etcdclient -source client.go -destination=client_mock.go Client // // Package etcdclient is a generated GoMock package. package etcdclient import ( context "context" io "io" reflect "reflect" clientv3 "go.etcd.io/etcd/client/v3" gomock "go.uber.org/mock/gomock" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // AlarmDisarm mocks base method. func (m_2 *MockClient) AlarmDisarm(ctx context.Context, m *clientv3.AlarmMember) (*clientv3.AlarmResponse, error) { m_2.ctrl.T.Helper() ret := m_2.ctrl.Call(m_2, "AlarmDisarm", ctx, m) ret0, _ := ret[0].(*clientv3.AlarmResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AlarmDisarm indicates an expected call of AlarmDisarm. func (mr *MockClientMockRecorder) AlarmDisarm(ctx, m any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AlarmDisarm", reflect.TypeOf((*MockClient)(nil).AlarmDisarm), ctx, m) } // AlarmList mocks base method. func (m *MockClient) AlarmList(ctx context.Context) (*clientv3.AlarmResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AlarmList", ctx) ret0, _ := ret[0].(*clientv3.AlarmResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AlarmList indicates an expected call of AlarmList. func (mr *MockClientMockRecorder) AlarmList(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AlarmList", reflect.TypeOf((*MockClient)(nil).AlarmList), ctx) } // AuthDisable mocks base method. func (m *MockClient) AuthDisable(ctx context.Context) (*clientv3.AuthDisableResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AuthDisable", ctx) ret0, _ := ret[0].(*clientv3.AuthDisableResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AuthDisable indicates an expected call of AuthDisable. func (mr *MockClientMockRecorder) AuthDisable(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthDisable", reflect.TypeOf((*MockClient)(nil).AuthDisable), ctx) } // AuthEnable mocks base method. func (m *MockClient) AuthEnable(ctx context.Context) (*clientv3.AuthEnableResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AuthEnable", ctx) ret0, _ := ret[0].(*clientv3.AuthEnableResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AuthEnable indicates an expected call of AuthEnable. func (mr *MockClientMockRecorder) AuthEnable(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthEnable", reflect.TypeOf((*MockClient)(nil).AuthEnable), ctx) } // AuthStatus mocks base method. func (m *MockClient) AuthStatus(ctx context.Context) (*clientv3.AuthStatusResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AuthStatus", ctx) ret0, _ := ret[0].(*clientv3.AuthStatusResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AuthStatus indicates an expected call of AuthStatus. func (mr *MockClientMockRecorder) AuthStatus(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthStatus", reflect.TypeOf((*MockClient)(nil).AuthStatus), ctx) } // Authenticate mocks base method. func (m *MockClient) Authenticate(ctx context.Context, name, password string) (*clientv3.AuthenticateResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Authenticate", ctx, name, password) ret0, _ := ret[0].(*clientv3.AuthenticateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Authenticate indicates an expected call of Authenticate. func (mr *MockClientMockRecorder) Authenticate(ctx, name, password any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authenticate", reflect.TypeOf((*MockClient)(nil).Authenticate), ctx, name, password) } // Close mocks base method. func (m *MockClient) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockClientMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockClient)(nil).Close)) } // Compact mocks base method. func (m *MockClient) Compact(ctx context.Context, rev int64, opts ...clientv3.CompactOption) (*clientv3.CompactResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, rev} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Compact", varargs...) ret0, _ := ret[0].(*clientv3.CompactResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Compact indicates an expected call of Compact. func (mr *MockClientMockRecorder) Compact(ctx, rev any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, rev}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Compact", reflect.TypeOf((*MockClient)(nil).Compact), varargs...) } // Defragment mocks base method. func (m *MockClient) Defragment(ctx context.Context, endpoint string) (*clientv3.DefragmentResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Defragment", ctx, endpoint) ret0, _ := ret[0].(*clientv3.DefragmentResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Defragment indicates an expected call of Defragment. func (mr *MockClientMockRecorder) Defragment(ctx, endpoint any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Defragment", reflect.TypeOf((*MockClient)(nil).Defragment), ctx, endpoint) } // Delete mocks base method. func (m *MockClient) Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, key} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Delete", varargs...) ret0, _ := ret[0].(*clientv3.DeleteResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Delete indicates an expected call of Delete. func (mr *MockClientMockRecorder) Delete(ctx, key any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, key}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), varargs...) } // Do mocks base method. func (m *MockClient) Do(ctx context.Context, op clientv3.Op) (clientv3.OpResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Do", ctx, op) ret0, _ := ret[0].(clientv3.OpResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Do indicates an expected call of Do. func (mr *MockClientMockRecorder) Do(ctx, op any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Do", reflect.TypeOf((*MockClient)(nil).Do), ctx, op) } // Get mocks base method. func (m *MockClient) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, key} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Get", varargs...) ret0, _ := ret[0].(*clientv3.GetResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Get indicates an expected call of Get. func (mr *MockClientMockRecorder) Get(ctx, key any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, key}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), varargs...) } // Grant mocks base method. func (m *MockClient) Grant(ctx context.Context, ttl int64) (*clientv3.LeaseGrantResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Grant", ctx, ttl) ret0, _ := ret[0].(*clientv3.LeaseGrantResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Grant indicates an expected call of Grant. func (mr *MockClientMockRecorder) Grant(ctx, ttl any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Grant", reflect.TypeOf((*MockClient)(nil).Grant), ctx, ttl) } // HashKV mocks base method. func (m *MockClient) HashKV(ctx context.Context, endpoint string, rev int64) (*clientv3.HashKVResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HashKV", ctx, endpoint, rev) ret0, _ := ret[0].(*clientv3.HashKVResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // HashKV indicates an expected call of HashKV. func (mr *MockClientMockRecorder) HashKV(ctx, endpoint, rev any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HashKV", reflect.TypeOf((*MockClient)(nil).HashKV), ctx, endpoint, rev) } // KeepAlive mocks base method. func (m *MockClient) KeepAlive(ctx context.Context, id clientv3.LeaseID) (<-chan *clientv3.LeaseKeepAliveResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "KeepAlive", ctx, id) ret0, _ := ret[0].(<-chan *clientv3.LeaseKeepAliveResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // KeepAlive indicates an expected call of KeepAlive. func (mr *MockClientMockRecorder) KeepAlive(ctx, id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeepAlive", reflect.TypeOf((*MockClient)(nil).KeepAlive), ctx, id) } // KeepAliveOnce mocks base method. func (m *MockClient) KeepAliveOnce(ctx context.Context, id clientv3.LeaseID) (*clientv3.LeaseKeepAliveResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "KeepAliveOnce", ctx, id) ret0, _ := ret[0].(*clientv3.LeaseKeepAliveResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // KeepAliveOnce indicates an expected call of KeepAliveOnce. func (mr *MockClientMockRecorder) KeepAliveOnce(ctx, id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeepAliveOnce", reflect.TypeOf((*MockClient)(nil).KeepAliveOnce), ctx, id) } // Leases mocks base method. func (m *MockClient) Leases(ctx context.Context) (*clientv3.LeaseLeasesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Leases", ctx) ret0, _ := ret[0].(*clientv3.LeaseLeasesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Leases indicates an expected call of Leases. func (mr *MockClientMockRecorder) Leases(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Leases", reflect.TypeOf((*MockClient)(nil).Leases), ctx) } // MemberAdd mocks base method. func (m *MockClient) MemberAdd(ctx context.Context, peerAddrs []string) (*clientv3.MemberAddResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MemberAdd", ctx, peerAddrs) ret0, _ := ret[0].(*clientv3.MemberAddResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MemberAdd indicates an expected call of MemberAdd. func (mr *MockClientMockRecorder) MemberAdd(ctx, peerAddrs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MemberAdd", reflect.TypeOf((*MockClient)(nil).MemberAdd), ctx, peerAddrs) } // MemberAddAsLearner mocks base method. func (m *MockClient) MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*clientv3.MemberAddResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MemberAddAsLearner", ctx, peerAddrs) ret0, _ := ret[0].(*clientv3.MemberAddResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MemberAddAsLearner indicates an expected call of MemberAddAsLearner. func (mr *MockClientMockRecorder) MemberAddAsLearner(ctx, peerAddrs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MemberAddAsLearner", reflect.TypeOf((*MockClient)(nil).MemberAddAsLearner), ctx, peerAddrs) } // MemberList mocks base method. func (m *MockClient) MemberList(ctx context.Context) (*clientv3.MemberListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MemberList", ctx) ret0, _ := ret[0].(*clientv3.MemberListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MemberList indicates an expected call of MemberList. func (mr *MockClientMockRecorder) MemberList(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MemberList", reflect.TypeOf((*MockClient)(nil).MemberList), ctx) } // MemberPromote mocks base method. func (m *MockClient) MemberPromote(ctx context.Context, id uint64) (*clientv3.MemberPromoteResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MemberPromote", ctx, id) ret0, _ := ret[0].(*clientv3.MemberPromoteResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MemberPromote indicates an expected call of MemberPromote. func (mr *MockClientMockRecorder) MemberPromote(ctx, id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MemberPromote", reflect.TypeOf((*MockClient)(nil).MemberPromote), ctx, id) } // MemberRemove mocks base method. func (m *MockClient) MemberRemove(ctx context.Context, id uint64) (*clientv3.MemberRemoveResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MemberRemove", ctx, id) ret0, _ := ret[0].(*clientv3.MemberRemoveResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MemberRemove indicates an expected call of MemberRemove. func (mr *MockClientMockRecorder) MemberRemove(ctx, id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MemberRemove", reflect.TypeOf((*MockClient)(nil).MemberRemove), ctx, id) } // MemberUpdate mocks base method. func (m *MockClient) MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*clientv3.MemberUpdateResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MemberUpdate", ctx, id, peerAddrs) ret0, _ := ret[0].(*clientv3.MemberUpdateResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MemberUpdate indicates an expected call of MemberUpdate. func (mr *MockClientMockRecorder) MemberUpdate(ctx, id, peerAddrs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MemberUpdate", reflect.TypeOf((*MockClient)(nil).MemberUpdate), ctx, id, peerAddrs) } // MoveLeader mocks base method. func (m *MockClient) MoveLeader(ctx context.Context, transfereeID uint64) (*clientv3.MoveLeaderResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MoveLeader", ctx, transfereeID) ret0, _ := ret[0].(*clientv3.MoveLeaderResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // MoveLeader indicates an expected call of MoveLeader. func (mr *MockClientMockRecorder) MoveLeader(ctx, transfereeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MoveLeader", reflect.TypeOf((*MockClient)(nil).MoveLeader), ctx, transfereeID) } // Put mocks base method. func (m *MockClient) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, key, val} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Put", varargs...) ret0, _ := ret[0].(*clientv3.PutResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Put indicates an expected call of Put. func (mr *MockClientMockRecorder) Put(ctx, key, val any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, key, val}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockClient)(nil).Put), varargs...) } // RequestProgress mocks base method. func (m *MockClient) RequestProgress(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RequestProgress", ctx) ret0, _ := ret[0].(error) return ret0 } // RequestProgress indicates an expected call of RequestProgress. func (mr *MockClientMockRecorder) RequestProgress(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestProgress", reflect.TypeOf((*MockClient)(nil).RequestProgress), ctx) } // Revoke mocks base method. func (m *MockClient) Revoke(ctx context.Context, id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Revoke", ctx, id) ret0, _ := ret[0].(*clientv3.LeaseRevokeResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Revoke indicates an expected call of Revoke. func (mr *MockClientMockRecorder) Revoke(ctx, id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Revoke", reflect.TypeOf((*MockClient)(nil).Revoke), ctx, id) } // RoleAdd mocks base method. func (m *MockClient) RoleAdd(ctx context.Context, name string) (*clientv3.AuthRoleAddResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RoleAdd", ctx, name) ret0, _ := ret[0].(*clientv3.AuthRoleAddResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RoleAdd indicates an expected call of RoleAdd. func (mr *MockClientMockRecorder) RoleAdd(ctx, name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RoleAdd", reflect.TypeOf((*MockClient)(nil).RoleAdd), ctx, name) } // RoleDelete mocks base method. func (m *MockClient) RoleDelete(ctx context.Context, role string) (*clientv3.AuthRoleDeleteResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RoleDelete", ctx, role) ret0, _ := ret[0].(*clientv3.AuthRoleDeleteResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RoleDelete indicates an expected call of RoleDelete. func (mr *MockClientMockRecorder) RoleDelete(ctx, role any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RoleDelete", reflect.TypeOf((*MockClient)(nil).RoleDelete), ctx, role) } // RoleGet mocks base method. func (m *MockClient) RoleGet(ctx context.Context, role string) (*clientv3.AuthRoleGetResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RoleGet", ctx, role) ret0, _ := ret[0].(*clientv3.AuthRoleGetResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RoleGet indicates an expected call of RoleGet. func (mr *MockClientMockRecorder) RoleGet(ctx, role any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RoleGet", reflect.TypeOf((*MockClient)(nil).RoleGet), ctx, role) } // RoleGrantPermission mocks base method. func (m *MockClient) RoleGrantPermission(ctx context.Context, name, key, rangeEnd string, permType clientv3.PermissionType) (*clientv3.AuthRoleGrantPermissionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RoleGrantPermission", ctx, name, key, rangeEnd, permType) ret0, _ := ret[0].(*clientv3.AuthRoleGrantPermissionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RoleGrantPermission indicates an expected call of RoleGrantPermission. func (mr *MockClientMockRecorder) RoleGrantPermission(ctx, name, key, rangeEnd, permType any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RoleGrantPermission", reflect.TypeOf((*MockClient)(nil).RoleGrantPermission), ctx, name, key, rangeEnd, permType) } // RoleList mocks base method. func (m *MockClient) RoleList(ctx context.Context) (*clientv3.AuthRoleListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RoleList", ctx) ret0, _ := ret[0].(*clientv3.AuthRoleListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RoleList indicates an expected call of RoleList. func (mr *MockClientMockRecorder) RoleList(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RoleList", reflect.TypeOf((*MockClient)(nil).RoleList), ctx) } // RoleRevokePermission mocks base method. func (m *MockClient) RoleRevokePermission(ctx context.Context, role, key, rangeEnd string) (*clientv3.AuthRoleRevokePermissionResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RoleRevokePermission", ctx, role, key, rangeEnd) ret0, _ := ret[0].(*clientv3.AuthRoleRevokePermissionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // RoleRevokePermission indicates an expected call of RoleRevokePermission. func (mr *MockClientMockRecorder) RoleRevokePermission(ctx, role, key, rangeEnd any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RoleRevokePermission", reflect.TypeOf((*MockClient)(nil).RoleRevokePermission), ctx, role, key, rangeEnd) } // Snapshot mocks base method. func (m *MockClient) Snapshot(ctx context.Context) (io.ReadCloser, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Snapshot", ctx) ret0, _ := ret[0].(io.ReadCloser) ret1, _ := ret[1].(error) return ret0, ret1 } // Snapshot indicates an expected call of Snapshot. func (mr *MockClientMockRecorder) Snapshot(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Snapshot", reflect.TypeOf((*MockClient)(nil).Snapshot), ctx) } // Status mocks base method. func (m *MockClient) Status(ctx context.Context, endpoint string) (*clientv3.StatusResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Status", ctx, endpoint) ret0, _ := ret[0].(*clientv3.StatusResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Status indicates an expected call of Status. func (mr *MockClientMockRecorder) Status(ctx, endpoint any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockClient)(nil).Status), ctx, endpoint) } // TimeToLive mocks base method. func (m *MockClient) TimeToLive(ctx context.Context, id clientv3.LeaseID, opts ...clientv3.LeaseOption) (*clientv3.LeaseTimeToLiveResponse, error) { m.ctrl.T.Helper() varargs := []any{ctx, id} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "TimeToLive", varargs...) ret0, _ := ret[0].(*clientv3.LeaseTimeToLiveResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // TimeToLive indicates an expected call of TimeToLive. func (mr *MockClientMockRecorder) TimeToLive(ctx, id any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, id}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimeToLive", reflect.TypeOf((*MockClient)(nil).TimeToLive), varargs...) } // Txn mocks base method. func (m *MockClient) Txn(ctx context.Context) clientv3.Txn { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Txn", ctx) ret0, _ := ret[0].(clientv3.Txn) return ret0 } // Txn indicates an expected call of Txn. func (mr *MockClientMockRecorder) Txn(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Txn", reflect.TypeOf((*MockClient)(nil).Txn), ctx) } // UserAdd mocks base method. func (m *MockClient) UserAdd(ctx context.Context, name, password string) (*clientv3.AuthUserAddResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UserAdd", ctx, name, password) ret0, _ := ret[0].(*clientv3.AuthUserAddResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UserAdd indicates an expected call of UserAdd. func (mr *MockClientMockRecorder) UserAdd(ctx, name, password any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserAdd", reflect.TypeOf((*MockClient)(nil).UserAdd), ctx, name, password) } // UserAddWithOptions mocks base method. func (m *MockClient) UserAddWithOptions(ctx context.Context, name, password string, opt *clientv3.UserAddOptions) (*clientv3.AuthUserAddResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UserAddWithOptions", ctx, name, password, opt) ret0, _ := ret[0].(*clientv3.AuthUserAddResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UserAddWithOptions indicates an expected call of UserAddWithOptions. func (mr *MockClientMockRecorder) UserAddWithOptions(ctx, name, password, opt any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserAddWithOptions", reflect.TypeOf((*MockClient)(nil).UserAddWithOptions), ctx, name, password, opt) } // UserChangePassword mocks base method. func (m *MockClient) UserChangePassword(ctx context.Context, name, password string) (*clientv3.AuthUserChangePasswordResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UserChangePassword", ctx, name, password) ret0, _ := ret[0].(*clientv3.AuthUserChangePasswordResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UserChangePassword indicates an expected call of UserChangePassword. func (mr *MockClientMockRecorder) UserChangePassword(ctx, name, password any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserChangePassword", reflect.TypeOf((*MockClient)(nil).UserChangePassword), ctx, name, password) } // UserDelete mocks base method. func (m *MockClient) UserDelete(ctx context.Context, name string) (*clientv3.AuthUserDeleteResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UserDelete", ctx, name) ret0, _ := ret[0].(*clientv3.AuthUserDeleteResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UserDelete indicates an expected call of UserDelete. func (mr *MockClientMockRecorder) UserDelete(ctx, name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserDelete", reflect.TypeOf((*MockClient)(nil).UserDelete), ctx, name) } // UserGet mocks base method. func (m *MockClient) UserGet(ctx context.Context, name string) (*clientv3.AuthUserGetResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UserGet", ctx, name) ret0, _ := ret[0].(*clientv3.AuthUserGetResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UserGet indicates an expected call of UserGet. func (mr *MockClientMockRecorder) UserGet(ctx, name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserGet", reflect.TypeOf((*MockClient)(nil).UserGet), ctx, name) } // UserGrantRole mocks base method. func (m *MockClient) UserGrantRole(ctx context.Context, user, role string) (*clientv3.AuthUserGrantRoleResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UserGrantRole", ctx, user, role) ret0, _ := ret[0].(*clientv3.AuthUserGrantRoleResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UserGrantRole indicates an expected call of UserGrantRole. func (mr *MockClientMockRecorder) UserGrantRole(ctx, user, role any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserGrantRole", reflect.TypeOf((*MockClient)(nil).UserGrantRole), ctx, user, role) } // UserList mocks base method. func (m *MockClient) UserList(ctx context.Context) (*clientv3.AuthUserListResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UserList", ctx) ret0, _ := ret[0].(*clientv3.AuthUserListResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UserList indicates an expected call of UserList. func (mr *MockClientMockRecorder) UserList(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserList", reflect.TypeOf((*MockClient)(nil).UserList), ctx) } // UserRevokeRole mocks base method. func (m *MockClient) UserRevokeRole(ctx context.Context, name, role string) (*clientv3.AuthUserRevokeRoleResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UserRevokeRole", ctx, name, role) ret0, _ := ret[0].(*clientv3.AuthUserRevokeRoleResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // UserRevokeRole indicates an expected call of UserRevokeRole. func (mr *MockClientMockRecorder) UserRevokeRole(ctx, name, role any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserRevokeRole", reflect.TypeOf((*MockClient)(nil).UserRevokeRole), ctx, name, role) } // Watch mocks base method. func (m *MockClient) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan { m.ctrl.T.Helper() varargs := []any{ctx, key} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Watch", varargs...) ret0, _ := ret[0].(clientv3.WatchChan) return ret0 } // Watch indicates an expected call of Watch. func (mr *MockClientMockRecorder) Watch(ctx, key any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, key}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockClient)(nil).Watch), varargs...) } ================================================ FILE: service/sharddistributor/store/etcd/etcdkeys/etcdkeys.go ================================================ package etcdkeys import ( "fmt" "strings" ) // BuildNamespacePrefix constructs the etcd key prefix for a given namespace. // result: / func BuildNamespacePrefix(prefix, namespace string) string { return fmt.Sprintf("%s/%s", prefix, namespace) } // BuildExecutorsPrefix constructs the etcd key prefix for executors within a given namespace. // result: //executors/ func BuildExecutorsPrefix(prefix, namespace string) string { return fmt.Sprintf("%s/executors/", BuildNamespacePrefix(prefix, namespace)) } // BuildExecutorIDPrefix constructs the etcd key prefix for a specific executor within a namespace. // result: //executors// func BuildExecutorIDPrefix(prefix, namespace, executorID string) string { return fmt.Sprintf("%s%s/", BuildExecutorsPrefix(prefix, namespace), executorID) } // ExecutorKeyType represents the allowed executor-level key types in etcd. // Use BuildExecutorKey to construct keys of these types. type ExecutorKeyType string const ( ExecutorHeartbeatKey ExecutorKeyType = "heartbeat" ExecutorStatusKey ExecutorKeyType = "status" ExecutorReportedShardsKey ExecutorKeyType = "reported_shards" ExecutorAssignedStateKey ExecutorKeyType = "assigned_state" ExecutorMetadataKey ExecutorKeyType = "metadata" ExecutorShardStatisticsKey ExecutorKeyType = "statistics" ) // validExecutorKeyTypes defines the set of valid executor key types. var validExecutorKeyTypes = map[ExecutorKeyType]struct{}{ ExecutorHeartbeatKey: {}, ExecutorStatusKey: {}, ExecutorReportedShardsKey: {}, ExecutorAssignedStateKey: {}, ExecutorMetadataKey: {}, ExecutorShardStatisticsKey: {}, } // IsValidExecutorKeyType checks if the provided key type is valid. func IsValidExecutorKeyType(keyType ExecutorKeyType) bool { _, exist := validExecutorKeyTypes[keyType] return exist } // BuildExecutorKey constructs the etcd key for a specific executor and key type. // result: //executors// func BuildExecutorKey(prefix, namespace, executorID string, keyType ExecutorKeyType) string { return fmt.Sprintf("%s%s", BuildExecutorIDPrefix(prefix, namespace, executorID), keyType) } // ParseExecutorKey parses an etcd key and extracts the executor ID and key type. // It returns an error if the key does not conform to the expected format. // Expected format of key: //executors// func ParseExecutorKey(prefix, namespace, key string) (executorID string, keyType ExecutorKeyType, err error) { prefix = BuildExecutorsPrefix(prefix, namespace) if !strings.HasPrefix(key, prefix) { return "", "", fmt.Errorf("key '%s' does not have expected prefix '%s'", key, prefix) } remainder := strings.TrimPrefix(key, prefix) parts := strings.Split(remainder, "/") if len(parts) < 2 { return "", "", fmt.Errorf("unexpected key format: %s", key) } // For metadata keys, the format is: executorID/metadata/metadataKey // For other keys, the format is: executorID/keyType // We return executorID and the first keyType (e.g., "metadata") if len(parts) > 2 && ExecutorKeyType(parts[1]) == ExecutorMetadataKey { // This is a metadata key, return "metadata" as the keyType return parts[0], ExecutorMetadataKey, nil } if len(parts) != 2 { return "", "", fmt.Errorf("unexpected key format: %s", key) } if !IsValidExecutorKeyType(ExecutorKeyType(parts[1])) { return "", "", fmt.Errorf("invalid executor key type: %s", parts[1]) } return parts[0], ExecutorKeyType(parts[1]), nil } // BuildMetadataKey constructs the etcd key for a specific metadata entry of an executor. // result: //executors//metadata/ func BuildMetadataKey(prefix string, namespace, executorID, metadataKey string) string { return fmt.Sprintf("%s/%s", BuildExecutorKey(prefix, namespace, executorID, ExecutorMetadataKey), metadataKey) } ================================================ FILE: service/sharddistributor/store/etcd/etcdkeys/etcdkeys_test.go ================================================ package etcdkeys import ( "testing" "github.com/stretchr/testify/assert" ) func TestBuildNamespacePrefix(t *testing.T) { got := BuildNamespacePrefix("/cadence", "test-ns") assert.Equal(t, "/cadence/test-ns", got) } func TestBuildExecutorsPrefix(t *testing.T) { got := BuildExecutorsPrefix("/cadence", "test-ns") assert.Equal(t, "/cadence/test-ns/executors/", got) } func TestBuildExecutorKey(t *testing.T) { got := BuildExecutorKey("/cadence", "test-ns", "exec-1", "heartbeat") assert.Equal(t, "/cadence/test-ns/executors/exec-1/heartbeat", got) } func TestParseExecutorKey(t *testing.T) { // Valid key executorID, keyType, err := ParseExecutorKey("/cadence", "test-ns", "/cadence/test-ns/executors/exec-1/heartbeat") assert.NoError(t, err) assert.Equal(t, "exec-1", executorID) assert.Equal(t, ExecutorHeartbeatKey, keyType) // Prefix missing _, _, err = ParseExecutorKey("/cadence", "test-ns", "/wrong/prefix") assert.ErrorContains(t, err, "key '/wrong/prefix' does not have expected prefix '/cadence/test-ns/executors/'") // Unexpected key format _, _, err = ParseExecutorKey("/cadence", "test-ns", "/cadence/test-ns/executors/exec-1/heartbeat/extra") assert.ErrorContains(t, err, "unexpected key format: /cadence/test-ns/executors/exec-1/heartbeat/extra") } func TestBuildMetadataKey(t *testing.T) { got := BuildMetadataKey("/cadence", "test-ns", "exec-1", "my-metadata-key") assert.Equal(t, "/cadence/test-ns/executors/exec-1/metadata/my-metadata-key", got) } func TestParseExecutorKey_MetadataKey(t *testing.T) { // Test that ParseExecutorKey correctly identifies metadata keys // and that we can extract the metadata key name from the full key metadataKey := BuildMetadataKey("/cadence", "test-ns", "exec-1", "hostname") executorID, keyType, err := ParseExecutorKey("/cadence", "test-ns", metadataKey) assert.NoError(t, err) assert.Equal(t, "exec-1", executorID) assert.Equal(t, ExecutorMetadataKey, keyType) } func TestParseExecutorKey_InvalidKeyType(t *testing.T) { key := BuildExecutorIDPrefix("/cadence", "test-ns", "exec-1") + "invalid_type" _, _, err := ParseExecutorKey("/cadence", "test-ns", key) assert.ErrorContains(t, err, "invalid executor key type: invalid_type") } ================================================ FILE: service/sharddistributor/store/etcd/etcdtypes/state.go ================================================ package etcdtypes import ( "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/store" ) type AssignedState struct { AssignedShards map[string]*types.ShardAssignment `json:"assigned_shards"` ShardHandoverStats map[string]ShardHandoverStats `json:"shard_handover_stats,omitempty"` LastUpdated Time `json:"last_updated"` // ModRevision is the etcd mod revision for this record. It is not serialized. ModRevision int64 `json:"-"` } // ToAssignedState converts the current AssignedState to store.AssignedState. func (s *AssignedState) ToAssignedState() *store.AssignedState { if s == nil { return nil } return &store.AssignedState{ AssignedShards: s.AssignedShards, ShardHandoverStats: convertMap(s.ShardHandoverStats, ToShardHandoverStats), LastUpdated: s.LastUpdated.ToTime(), ModRevision: s.ModRevision, } } // FromAssignedState creates an AssignedState from a store.AssignedState. func FromAssignedState(src *store.AssignedState) *AssignedState { if src == nil { return nil } return &AssignedState{ AssignedShards: src.AssignedShards, LastUpdated: Time(src.LastUpdated), ShardHandoverStats: convertMap(src.ShardHandoverStats, FromShardHandoverStats), ModRevision: src.ModRevision, } } type ShardHandoverStats struct { PreviousExecutorLastHeartbeatTime Time `json:"previous_executor_last_heartbeat_time"` HandoverType types.HandoverType `json:"handover_type"` } // FromShardHandoverStats creates an ShardHandoverStats from a store.ShardHandoverStats. func FromShardHandoverStats(src *store.ShardHandoverStats) *ShardHandoverStats { if src == nil { return nil } return &ShardHandoverStats{ PreviousExecutorLastHeartbeatTime: Time(src.PreviousExecutorLastHeartbeatTime), HandoverType: src.HandoverType, } } // ToShardHandoverStats converts the current ShardHandoverStats to store.ShardHandoverStats. func ToShardHandoverStats(src *ShardHandoverStats) *store.ShardHandoverStats { if src == nil { return nil } return &store.ShardHandoverStats{ PreviousExecutorLastHeartbeatTime: src.PreviousExecutorLastHeartbeatTime.ToTime(), HandoverType: src.HandoverType, } } type ShardStatistics struct { SmoothedLoad float64 `json:"smoothed_load"` LastUpdateTime Time `json:"last_update_time"` LastMoveTime Time `json:"last_move_time"` } // ToShardStatistics converts the current ShardStatistics to store.ShardStatistics. func (s *ShardStatistics) ToShardStatistics() *store.ShardStatistics { if s == nil { return nil } return &store.ShardStatistics{ SmoothedLoad: s.SmoothedLoad, LastUpdateTime: s.LastUpdateTime.ToTime(), LastMoveTime: s.LastMoveTime.ToTime(), } } // FromShardStatistics creates a ShardStatistics from a store.ShardStatistics. func FromShardStatistics(src *store.ShardStatistics) *ShardStatistics { if src == nil { return nil } return &ShardStatistics{ SmoothedLoad: src.SmoothedLoad, LastUpdateTime: Time(src.LastUpdateTime), LastMoveTime: Time(src.LastMoveTime), } } // ConvertMap converts a map[K]SrcType to map[K]DstType using a provided converter function. func convertMap[K comparable, SrcType any, DstType any](src map[K]SrcType, converter func(*SrcType) *DstType) map[K]DstType { if src == nil { return nil } dst := make(map[K]DstType, len(src)) for k, v := range src { dst[k] = *converter(&v) } return dst } ================================================ FILE: service/sharddistributor/store/etcd/etcdtypes/state_test.go ================================================ package etcdtypes import ( "encoding/json" "reflect" "testing" "time" "github.com/stretchr/testify/require" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/store" ) func TestAssignedState_FieldNumberMatched(t *testing.T) { require.Equal(t, reflect.TypeOf(AssignedState{}).NumField(), reflect.TypeOf(store.AssignedState{}).NumField(), "AssignedState field count mismatch with store.AssignedState; ensure conversion is updated", ) } func TestAssignedState_ToAssignedState(t *testing.T) { tests := map[string]struct { input *AssignedState expect *store.AssignedState }{ "nil": { input: nil, expect: nil, }, "success": { input: &AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "1": {Status: types.AssignmentStatusREADY}, }, LastUpdated: Time(time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC)), ModRevision: 42, }, expect: &store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "1": {Status: types.AssignmentStatusREADY}, }, LastUpdated: time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC), ModRevision: 42, }, }, "success with map": { input: &AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "1": {Status: types.AssignmentStatusREADY}, }, ShardHandoverStats: map[string]ShardHandoverStats{ "1": { PreviousExecutorLastHeartbeatTime: Time(time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC)), HandoverType: types.HandoverTypeGRACEFUL, }, }, LastUpdated: Time(time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC)), ModRevision: 42, }, expect: &store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "1": {Status: types.AssignmentStatusREADY}, }, ShardHandoverStats: map[string]store.ShardHandoverStats{ "1": { PreviousExecutorLastHeartbeatTime: time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC), HandoverType: types.HandoverTypeGRACEFUL, }, }, LastUpdated: time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC), ModRevision: 42, }, }, } for name, c := range tests { t.Run(name, func(t *testing.T) { got := c.input.ToAssignedState() if c.expect == nil { require.Nil(t, got) return } require.NotNil(t, got) require.Equal(t, len(c.input.AssignedShards), len(got.AssignedShards)) for k := range c.input.AssignedShards { require.Equal(t, c.input.AssignedShards[k].Status, got.AssignedShards[k].Status) } require.Equal(t, time.Time(c.input.LastUpdated).UnixNano(), got.LastUpdated.UnixNano()) require.Equal(t, c.input.ModRevision, got.ModRevision) }) } } func TestAssignedState_FromAssignedState(t *testing.T) { tests := map[string]struct { input *store.AssignedState expect *AssignedState }{ "nil": { input: nil, expect: nil, }, "success": { input: &store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "9": {Status: types.AssignmentStatusREADY}, }, LastUpdated: time.Date(2025, 11, 18, 13, 0, 0, 987654321, time.UTC), ModRevision: 77, }, expect: &AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "9": {Status: types.AssignmentStatusREADY}, }, LastUpdated: Time(time.Date(2025, 11, 18, 13, 0, 0, 987654321, time.UTC)), ModRevision: 77, }, }, "with map": { input: &store.AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "9": {Status: types.AssignmentStatusREADY}, }, ShardHandoverStats: map[string]store.ShardHandoverStats{ "9": { PreviousExecutorLastHeartbeatTime: time.Date(2025, 11, 18, 13, 0, 0, 987654321, time.UTC), HandoverType: types.HandoverTypeGRACEFUL, }, }, LastUpdated: time.Date(2025, 11, 18, 13, 0, 0, 987654321, time.UTC), ModRevision: 77, }, expect: &AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "9": {Status: types.AssignmentStatusREADY}, }, ShardHandoverStats: map[string]ShardHandoverStats{ "9": { PreviousExecutorLastHeartbeatTime: Time(time.Date(2025, 11, 18, 13, 0, 0, 987654321, time.UTC)), HandoverType: types.HandoverTypeGRACEFUL, }, }, LastUpdated: Time(time.Date(2025, 11, 18, 13, 0, 0, 987654321, time.UTC)), ModRevision: 77, }, }, } for name, c := range tests { t.Run(name, func(t *testing.T) { got := FromAssignedState(c.input) if c.expect == nil { require.Nil(t, got) return } require.NotNil(t, got) require.Equal(t, len(c.input.AssignedShards), len(got.AssignedShards)) for k := range c.input.AssignedShards { require.Equal(t, c.input.AssignedShards[k].Status, got.AssignedShards[k].Status) } require.Equal(t, c.input.LastUpdated.UnixNano(), time.Time(got.LastUpdated).UnixNano()) require.Equal(t, c.input.ModRevision, got.ModRevision) }) } } func TestAssignedState_JSONMarshalling(t *testing.T) { tests := map[string]struct { input *AssignedState jsonStr string }{ "simple": { input: &AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "1": {Status: types.AssignmentStatusREADY}, }, LastUpdated: Time(time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC)), ModRevision: 42, }, jsonStr: `{"assigned_shards":{"1":{"status":"AssignmentStatusREADY"}},"last_updated":"2025-11-18T12:00:00.123456789Z"}`, }, "with map": { input: &AssignedState{ AssignedShards: map[string]*types.ShardAssignment{ "1": {Status: types.AssignmentStatusREADY}, }, ShardHandoverStats: map[string]ShardHandoverStats{ "1": { PreviousExecutorLastHeartbeatTime: Time(time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC)), HandoverType: types.HandoverTypeGRACEFUL, }, }, LastUpdated: Time(time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC)), ModRevision: 42, }, jsonStr: `{"assigned_shards":{"1":{"status":"AssignmentStatusREADY"}},"shard_handover_stats":{"1":{"previous_executor_last_heartbeat_time":"2025-11-18T12:00:00.123456789Z","handover_type":"HandoverTypeGRACEFUL"}},"last_updated":"2025-11-18T12:00:00.123456789Z"}`, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { // Marshal to JSON b, err := json.Marshal(tc.input) require.NoError(t, err) require.JSONEq(t, tc.jsonStr, string(b)) // Unmarshal from JSON var unmarshalled AssignedState err = json.Unmarshal([]byte(tc.jsonStr), &unmarshalled) require.NoError(t, err) require.Equal(t, tc.input.AssignedShards["1"].Status, unmarshalled.AssignedShards["1"].Status) require.Equal(t, time.Time(tc.input.LastUpdated).UnixNano(), time.Time(unmarshalled.LastUpdated).UnixNano()) if tc.input.ShardHandoverStats != nil { require.NotNil(t, unmarshalled.ShardHandoverStats) require.Equal(t, tc.input.ShardHandoverStats["1"].HandoverType, unmarshalled.ShardHandoverStats["1"].HandoverType) require.Equal(t, time.Time(tc.input.ShardHandoverStats["1"].PreviousExecutorLastHeartbeatTime).UnixNano(), time.Time(unmarshalled.ShardHandoverStats["1"].PreviousExecutorLastHeartbeatTime).UnixNano()) } }) } } func TestShardStatistics_FieldNumberMatched(t *testing.T) { require.Equal(t, reflect.TypeOf(ShardStatistics{}).NumField(), reflect.TypeOf(store.ShardStatistics{}).NumField(), "ShardStatistics field count mismatch with store.ShardStatistics; ensure conversion is updated", ) } func TestShardStatistics_ToShardStatistics(t *testing.T) { tests := map[string]struct { input *ShardStatistics expect *store.ShardStatistics }{ "nil": { input: nil, expect: nil, }, "success": { input: &ShardStatistics{ SmoothedLoad: 12.34, LastUpdateTime: Time(time.Date(2025, 11, 18, 14, 0, 0, 111111111, time.UTC)), LastMoveTime: Time(time.Date(2025, 11, 18, 15, 0, 0, 222222222, time.UTC)), }, expect: &store.ShardStatistics{ SmoothedLoad: 12.34, LastUpdateTime: time.Date(2025, 11, 18, 14, 0, 0, 111111111, time.UTC), LastMoveTime: time.Date(2025, 11, 18, 15, 0, 0, 222222222, time.UTC), }, }, } for name, c := range tests { t.Run(name, func(t *testing.T) { got := c.input.ToShardStatistics() if c.expect == nil { require.Nil(t, got) return } require.NotNil(t, got) require.Equal(t, c.input.SmoothedLoad, got.SmoothedLoad) require.Equal(t, time.Time(c.input.LastUpdateTime).UnixNano(), got.LastUpdateTime.UnixNano()) require.Equal(t, time.Time(c.input.LastMoveTime).UnixNano(), got.LastMoveTime.UnixNano()) }) } } func TestShardStatistics_FromShardStatistics(t *testing.T) { tests := map[string]struct { input *store.ShardStatistics expect *ShardStatistics }{ "nil": { input: nil, expect: nil, }, "success": { input: &store.ShardStatistics{ SmoothedLoad: 99.01, LastUpdateTime: time.Date(2025, 11, 18, 16, 0, 0, 333333333, time.UTC), LastMoveTime: time.Date(2025, 11, 18, 17, 0, 0, 444444444, time.UTC), }, expect: &ShardStatistics{ SmoothedLoad: 99.01, LastUpdateTime: Time(time.Date(2025, 11, 18, 16, 0, 0, 333333333, time.UTC)), LastMoveTime: Time(time.Date(2025, 11, 18, 17, 0, 0, 444444444, time.UTC)), }, }, } for name, c := range tests { t.Run(name, func(t *testing.T) { got := FromShardStatistics(c.input) if c.expect == nil { require.Nil(t, got) return } require.NotNil(t, got) require.InDelta(t, c.input.SmoothedLoad, got.SmoothedLoad, 0.0000001) require.Equal(t, c.input.LastUpdateTime.UnixNano(), time.Time(got.LastUpdateTime).UnixNano()) require.Equal(t, c.input.LastMoveTime.UnixNano(), time.Time(got.LastMoveTime).UnixNano()) }) } } func TestShardStatistics_JSONMarshalling(t *testing.T) { const jsonStr = `{"smoothed_load":12.34,"last_update_time":"2025-11-18T14:00:00.111111111Z","last_move_time":"2025-11-18T15:00:00.222222222Z"}` state := &ShardStatistics{ SmoothedLoad: 12.34, LastUpdateTime: Time(time.Date(2025, 11, 18, 14, 0, 0, 111111111, time.UTC)), LastMoveTime: Time(time.Date(2025, 11, 18, 15, 0, 0, 222222222, time.UTC)), } // Marshal to JSON b, err := json.Marshal(state) require.NoError(t, err) require.JSONEq(t, jsonStr, string(b)) // Unmarshal from JSON var unmarshalled ShardStatistics err = json.Unmarshal([]byte(jsonStr), &unmarshalled) require.NoError(t, err) require.InDelta(t, state.SmoothedLoad, unmarshalled.SmoothedLoad, 0.0000001) require.Equal(t, time.Time(state.LastUpdateTime).UnixNano(), time.Time(unmarshalled.LastUpdateTime).UnixNano()) require.Equal(t, time.Time(state.LastMoveTime).UnixNano(), time.Time(unmarshalled.LastMoveTime).UnixNano()) } func TestShardHandoverStats_FieldNumberMatched(t *testing.T) { require.Equal(t, reflect.TypeOf(ShardHandoverStats{}).NumField(), reflect.TypeOf(store.ShardHandoverStats{}).NumField(), "ShardHandoverStats field count mismatch with store.ShardHandoverStats; ensure conversion is updated", ) } func TestShardHandoverStats_ToShardHandoverStats(t *testing.T) { tests := map[string]struct { input *ShardHandoverStats expect *store.ShardHandoverStats }{ "nil": { input: nil, expect: nil, }, "success": { input: &ShardHandoverStats{ PreviousExecutorLastHeartbeatTime: Time(time.Date(2025, 11, 18, 18, 0, 0, 555555555, time.UTC)), HandoverType: types.HandoverTypeGRACEFUL, }, expect: &store.ShardHandoverStats{ PreviousExecutorLastHeartbeatTime: time.Date(2025, 11, 18, 18, 0, 0, 555555555, time.UTC), HandoverType: types.HandoverTypeGRACEFUL, }, }, } for name, c := range tests { t.Run(name, func(t *testing.T) { got := ToShardHandoverStats(c.input) if c.expect == nil { require.Nil(t, got) return } require.NotNil(t, got) require.Equal(t, c.input.HandoverType, got.HandoverType) require.Equal(t, time.Time(c.input.PreviousExecutorLastHeartbeatTime).UnixNano(), got.PreviousExecutorLastHeartbeatTime.UnixNano()) }) } } func TestShardHandoverStats_FromShardHandoverStats(t *testing.T) { tests := map[string]struct { input *store.ShardHandoverStats expect *ShardHandoverStats }{ "nil": { input: nil, expect: nil, }, "success": { input: &store.ShardHandoverStats{ PreviousExecutorLastHeartbeatTime: time.Date(2025, 11, 18, 19, 0, 0, 666666666, time.UTC), HandoverType: types.HandoverTypeGRACEFUL, }, expect: &ShardHandoverStats{ PreviousExecutorLastHeartbeatTime: Time(time.Date(2025, 11, 18, 19, 0, 0, 666666666, time.UTC)), HandoverType: types.HandoverTypeGRACEFUL, }, }, } for name, c := range tests { t.Run(name, func(t *testing.T) { got := FromShardHandoverStats(c.input) if c.expect == nil { require.Nil(t, got) return } require.NotNil(t, got) require.Equal(t, c.input.HandoverType, got.HandoverType) require.Equal(t, c.input.PreviousExecutorLastHeartbeatTime.UnixNano(), time.Time(got.PreviousExecutorLastHeartbeatTime).UnixNano()) }) } } func TestShardHandoverStats_JSONMarshalling(t *testing.T) { const jsonStr = `{"previous_executor_last_heartbeat_time":"2025-11-18T20:00:00.777777777Z","handover_type":"HandoverTypeGRACEFUL"}` stats := &ShardHandoverStats{ PreviousExecutorLastHeartbeatTime: Time(time.Date(2025, 11, 18, 20, 0, 0, 777777777, time.UTC)), HandoverType: types.HandoverTypeGRACEFUL, } // Marshal to JSON b, err := json.Marshal(stats) require.NoError(t, err) require.JSONEq(t, jsonStr, string(b)) // Unmarshal from JSON var unmarshalled ShardHandoverStats err = json.Unmarshal([]byte(jsonStr), &unmarshalled) require.NoError(t, err) require.Equal(t, stats.HandoverType, unmarshalled.HandoverType) require.Equal(t, time.Time(stats.PreviousExecutorLastHeartbeatTime).UnixNano(), time.Time(unmarshalled.PreviousExecutorLastHeartbeatTime).UnixNano()) } ================================================ FILE: service/sharddistributor/store/etcd/etcdtypes/time.go ================================================ package etcdtypes import "time" // Time is a wrapper around time that implements JSON marshalling/unmarshalling // in time.RFC3339Nano format to keep precision when storing in etcd. // Convert to UTC before storing/parsing to ensure consistency. type Time time.Time // ToTimePtr converts *time.Time to *Time. func ToTimePtr(t *time.Time) *Time { if t == nil { return nil } tt := Time(*t) return &tt } // ToTime converts Time back to time.Time. func (t Time) ToTime() time.Time { return time.Time(t) } // ToTimePtr converts Time back to *time.Time. func (t *Time) ToTimePtr() *time.Time { if t == nil { return nil } tt := time.Time(*t) return &tt } // MarshalJSON implements the json.Marshaler interface. // It encodes the time in time.RFC3339Nano format. func (t Time) MarshalJSON() ([]byte, error) { s := FormatTime(time.Time(t)) return []byte(`"` + s + `"`), nil } // UnmarshalJSON implements the json.Unmarshaler interface. // It decodes the time from time.RFC3339Nano format. func (t *Time) UnmarshalJSON(data []byte) error { str := string(data) if len(str) >= 2 && str[0] == '"' && str[len(str)-1] == '"' { str = str[1 : len(str)-1] } parsed, err := ParseTime(str) if err != nil { return err } *t = Time(parsed) return nil } // ParseTime parses a string in time.RFC3339Nano format and returns a time.Time in UTC. func ParseTime(s string) (time.Time, error) { parsed, err := time.ParseInLocation(time.RFC3339Nano, s, time.UTC) if err != nil { return time.Time{}, err } return parsed.UTC(), nil } // FormatTime converts time.Time to UTC and // formats time.Time to a string in time.RFC3339Nano format. func FormatTime(t time.Time) string { return t.UTC().Format(time.RFC3339Nano) } ================================================ FILE: service/sharddistributor/store/etcd/etcdtypes/time_test.go ================================================ package etcdtypes import ( "encoding/json" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestTimeToTime verifies ToTime returns the original time.Time value. func TestTimeToTime(t *testing.T) { now := time.Now() require.Equal(t, now, Time(now).ToTime()) } func TestTimeToTimePtr_NilInput_ReturnsNil(t *testing.T) { var input *time.Time result := ToTimePtr(input) require.Nil(t, result) } func TestTimeToTimePtr(t *testing.T) { now := time.Now() result := ToTimePtr(&now) require.NotNil(t, result) require.Equal(t, Time(now), *result) } func TestTimeToTimePtr_Nil(t *testing.T) { result := ToTimePtr(nil) require.Nil(t, result) } func TestTimeMarshalJSON(t *testing.T) { for name, c := range map[string]struct { input Time expectOutput string expectErr string }{ "zero time": { input: Time(time.Time{}), expectOutput: `"0001-01-01T00:00:00Z"`, expectErr: "", }, "utc time with nanos": { input: Time(time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC)), expectOutput: `"2025-11-18T12:00:00.123456789Z"`, expectErr: "", }, "non-UTC time": { input: Time(time.Date(2025, 11, 18, 1, 2, 3, 456789012, time.FixedZone("X", -2*3600))), expectOutput: `"2025-11-18T03:02:03.456789012Z"`, expectErr: "", }, } { t.Run(name, func(t *testing.T) { output, err := c.input.MarshalJSON() require.Equal(t, c.expectOutput, string(output)) if c.expectErr != "" { require.Error(t, err) assert.Contains(t, err.Error(), c.expectErr) } else { require.NoError(t, err) } }) } } func TestTimeUnmarshalJSON(t *testing.T) { for name, c := range map[string]struct { input []byte expectTime Time expectErr string }{ "zero time": { input: []byte(`"0001-01-01T00:00:00Z"`), expectTime: Time(time.Time{}), expectErr: "", }, "valid RFC3339Nano": { input: []byte(`"2025-11-18T12:00:00.123456789Z"`), expectTime: Time(time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC)), expectErr: "", }, "non-UTC time": { input: []byte(`"2025-11-17T23:02:03.456789012Z"`), expectTime: Time(time.Date(2025, 11, 17, 23, 2, 3, 456789012, time.UTC)), expectErr: "", }, "invalid format": { input: []byte(`"not-a-time"`), expectTime: Time{}, expectErr: "cannot parse", }, "empty string": { input: []byte(`""`), expectTime: Time{}, expectErr: "cannot parse", }, } { t.Run(name, func(t *testing.T) { var tim Time err := json.Unmarshal(c.input, &tim) require.Equal(t, c.expectTime, tim) if c.expectErr != "" { require.Error(t, err) assert.Contains(t, err.Error(), c.expectErr) } else { require.NoError(t, err) } }) } } // TestParseTimeSuccess checks valid parsing scenarios. func TestParseTimeSuccess(t *testing.T) { for name, c := range map[string]struct { input string expected time.Time }{ "with nanos": { input: "2025-11-18T12:00:00.123456789Z", expected: time.Date(2025, 11, 18, 12, 0, 0, 123456789, time.UTC), }, "without nanos": { input: "2000-01-01T00:00:00Z", expected: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), }, "non-UTC time": { input: "2025-11-18T01:02:03.456789012-02:00", expected: time.Date(2025, 11, 18, 3, 2, 3, 456789012, time.UTC), }, } { t.Run(name, func(t *testing.T) { parsed, err := ParseTime(c.input) require.NoError(t, err, "unexpected error for input %q", c.input) assert.Equal(t, c.expected, parsed) assert.Equal(t, c.expected.UnixNano(), parsed.UnixNano(), "ParseTime mismatch got %v want %v", parsed, c.expected) assert.Equal(t, time.UTC, parsed.Location(), "ParseTime location mismatch got %v want %v", parsed.Location(), time.UTC) }) } } func TestParseTime_Failures(t *testing.T) { for name, c := range map[string]struct { input string }{ "not-a-time": {input: "not-a-time"}, "empty string": {input: ""}, "invalid month": {input: "2025-13-01T00:00:00Z"}, } { t.Run(name, func(t *testing.T) { _, err := ParseTime(c.input) assert.Error(t, err, "[%s] expected error for input %q", name, c.input) }) } } func TestFormatTime(t *testing.T) { for name, c := range map[string]struct { input time.Time expected string }{ "non-UTC with nanos": { input: time.Date(2025, 11, 17, 21, 2, 3, 456789012, time.FixedZone("X", -2*3600)), expected: "2025-11-17T23:02:03.456789012Z", }, "non-UTC without nanos": { input: time.Date(2000, 1, 1, 0, 0, 0, 0, time.FixedZone("Y", +5*3600)), expected: "1999-12-31T19:00:00Z", }, "zero time": { input: time.Time{}, expected: "0001-01-01T00:00:00Z", }, } { t.Run(name, func(t *testing.T) { str := FormatTime(c.input) assert.Equal(t, c.expected, str) }) } } // TestTime_JSONMarshalling ensures Marshal and Unmarshal retains nanosecond precision. func TestTime_JSONMarshalling(t *testing.T) { for name, c := range map[string]struct { input time.Time }{ "zero time": { input: time.Time{}, }, "start of 2025": { input: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC), }, "now with nanos": { input: time.Now().UTC(), }, "max nanos": { input: time.Date(2025, 6, 30, 23, 59, 59, 999999999, time.UTC), }, } { t.Run(name, func(t *testing.T) { wrapped := Time(c.input) b, err := json.Marshal(wrapped) require.NoError(t, err, "marshal error") var back Time require.NoError(t, json.Unmarshal(b, &back), "unmarshal error") assert.Equal(t, c.input.UTC().UnixNano(), time.Time(back).UnixNano(), "round-trip lost precision: got %v want %v", time.Time(back), c.input.UTC()) }) } } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/client.go ================================================ package executorstore import ( "fmt" "time" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/fx" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdclient" ) type ClientParams struct { fx.In // If provided, this client will be used instead of creating a new one Client *clientv3.Client `optional:"true"` Cfg ETCDConfig Lifecycle fx.Lifecycle } type ClientOutput struct { fx.Out Client etcdclient.Client `name:"executorstore"` } // NewClient creates a new etcd client or uses the provided one func NewClient(p ClientParams) (ClientOutput, error) { if p.Client != nil { return ClientOutput{Client: p.Client}, nil } etcdClient, err := clientv3.New(clientv3.Config{ Endpoints: p.Cfg.Endpoints, DialTimeout: p.Cfg.DialTimeout, }) if err != nil { return ClientOutput{}, err } p.Lifecycle.Append(fx.StopHook(etcdClient.Close)) return ClientOutput{Client: etcdClient}, nil } type ETCDConfig struct { Endpoints []string `yaml:"endpoints"` DialTimeout time.Duration `yaml:"dialTimeout"` Prefix string `yaml:"prefix"` Compression string `yaml:"compression"` } // NewETCDConfig parses ETCDConfig from ShardDistribution config func NewETCDConfig(cfg config.ShardDistribution) (ETCDConfig, error) { var etcdCfg ETCDConfig if err := cfg.Store.StorageParams.Decode(&etcdCfg); err != nil { return etcdCfg, fmt.Errorf("bad config for etcd store: %w", err) } return etcdCfg, nil } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/client_test.go ================================================ package executorstore import ( "testing" "time" "github.com/stretchr/testify/require" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/fx/fxtest" "gopkg.in/yaml.v2" "github.com/uber/cadence/service/sharddistributor/config" ) func TestNewClient_WithProvidedClient(t *testing.T) { mockClient := &clientv3.Client{} params := ClientParams{ Client: mockClient, } output, err := NewClient(params) require.NoError(t, err) require.Equal(t, mockClient, output.Client) } func TestNewClient_WithInvalidConfig(t *testing.T) { params := ClientParams{ Cfg: ETCDConfig{ Endpoints: []string{}, DialTimeout: 5 * time.Second, }, Lifecycle: fxtest.NewLifecycle(t), } output, err := NewClient(params) require.Error(t, err) require.Nil(t, output.Client) } func TestNewETCDConfig_WithValidConfig(t *testing.T) { etcdCfg := ETCDConfig{ Endpoints: []string{"127.0.0.1:2379"}, DialTimeout: 5 * time.Second, Prefix: "/prefix", Compression: "none", } encoded, err := yaml.Marshal(etcdCfg) require.NoError(t, err) decoded := &config.YamlNode{} err = yaml.Unmarshal(encoded, decoded) require.NoError(t, err) sdConfig := config.ShardDistribution{ Store: config.Store{ StorageParams: decoded, }, } resultCfg, err := NewETCDConfig(sdConfig) require.NoError(t, err) require.Equal(t, etcdCfg, resultCfg) } func TestNewETCDConfig_WithInvalidConfig(t *testing.T) { encoded, err := yaml.Marshal("") require.NoError(t, err) decoded := &config.YamlNode{} err = yaml.Unmarshal(encoded, decoded) require.NoError(t, err) sdConfig := config.ShardDistribution{ Store: config.Store{ StorageParams: decoded, }, } _, err = NewETCDConfig(sdConfig) require.Error(t, err) } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/common/compression.go ================================================ package common import ( "bytes" "encoding/json" "fmt" "io" "strings" "github.com/golang/snappy" ) var ( // _snappyHeader is a magic prefix prepended to compressed data to distinguish it from uncompressed data _snappyHeader = []byte{0xff, 0x06, 0x00, 0x00, 's', 'N', 'a', 'P', 'p', 'Y'} ) const ( // CompressionSnappy indicates snappy compression should be applied CompressionSnappy = "snappy" ) type compressionMode int const ( compressionNone compressionMode = iota compressionSnappy ) // RecordWriter handles serialization of data for etcd, applying compression when configured. type RecordWriter struct { mode compressionMode } // NewRecordWriter constructs a RecordWriter based on the configured compression type. func NewRecordWriter(compressionType string) (*RecordWriter, error) { switch strings.ToLower(compressionType) { case "", "none": return &RecordWriter{mode: compressionNone}, nil case CompressionSnappy: return &RecordWriter{mode: compressionSnappy}, nil default: return nil, fmt.Errorf("unsupported compression type: %s", compressionType) } } // Write serializes data using the configured compression mode. func (w *RecordWriter) Write(data []byte) ([]byte, error) { if w.mode == compressionNone { return data, nil } var buf bytes.Buffer writer := snappy.NewBufferedWriter(&buf) if _, err := writer.Write(data); err != nil { return nil, err } if err := writer.Close(); err != nil { return nil, err } return buf.Bytes(), nil } // Decompress decodes snappy-compressed data // If the snappy header is present, it will successfully decompress it or return an error // If the snappy header is absent, it treats data as uncompressed and returns it as-is func Decompress(data []byte) ([]byte, error) { if len(data) == 0 { return data, nil } if !bytes.HasPrefix(data, _snappyHeader) { return data, nil } r := snappy.NewReader(bytes.NewReader(data)) decompressed, err := io.ReadAll(r) if err != nil { return nil, err } return decompressed, nil } // DecompressAndUnmarshal decompresses data and unmarshals it into the target func DecompressAndUnmarshal(data []byte, target interface{}) error { decompressed, err := Decompress(data) if err != nil { return fmt.Errorf("decompress: %w", err) } if err := json.Unmarshal(decompressed, target); err != nil { return fmt.Errorf("unmarshal: %w", err) } return nil } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/common/compression_test.go ================================================ package common import ( "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestRecordWriter(t *testing.T) { original := []byte("test-data") t.Run("no compression when empty", func(t *testing.T) { writer, err := NewRecordWriter("") require.NoError(t, err) out, err := writer.Write(original) require.NoError(t, err) assert.Equal(t, original, out) }) t.Run("no compression when none", func(t *testing.T) { writer, err := NewRecordWriter("none") require.NoError(t, err) out, err := writer.Write(original) require.NoError(t, err) assert.Equal(t, original, out) }) t.Run("snappy compression", func(t *testing.T) { writer, err := NewRecordWriter(CompressionSnappy) require.NoError(t, err) out, err := writer.Write(original) require.NoError(t, err) require.NotNil(t, out) assert.NotEqual(t, original, out) decompressed, err := Decompress(out) require.NoError(t, err) assert.Equal(t, original, decompressed) }) t.Run("unsupported compression", func(t *testing.T) { writer, err := NewRecordWriter("unsupported") require.Error(t, err) assert.Nil(t, writer) }) } func TestDecompress(t *testing.T) { t.Run("Empty data", func(t *testing.T) { decompressed, err := Decompress([]byte{}) require.NoError(t, err) assert.Empty(t, decompressed) }) t.Run("Nil data", func(t *testing.T) { decompressed, err := Decompress(nil) require.NoError(t, err) assert.Nil(t, decompressed) }) t.Run("Uncompressed data", func(t *testing.T) { uncompressed := []byte(`{"status":"ACTIVE"}`) result, err := Decompress(uncompressed) require.NoError(t, err) assert.Equal(t, uncompressed, result, "Uncompressed data is returned as-is") var status map[string]string err = json.Unmarshal(result, &status) require.NoError(t, err) assert.Equal(t, "ACTIVE", status["status"]) }) t.Run("Compressed data", func(t *testing.T) { original := []byte(`{"status":"DRAINING"}`) writer, err := NewRecordWriter(CompressionSnappy) require.NoError(t, err) compressed, err := writer.Write(original) require.NoError(t, err) result, err := Decompress(compressed) require.NoError(t, err) assert.Equal(t, original, result) var status map[string]string err = json.Unmarshal(result, &status) require.NoError(t, err) assert.Equal(t, "DRAINING", status["status"]) }) } func TestDecompressAndUnmarshal(t *testing.T) { type testData struct { Status string `json:"status"` Shards []string `json:"shards"` } t.Run("Uncompressed data", func(t *testing.T) { data := []byte(`{"status":"ACTIVE","shards":["shard1","shard2"]}`) var result testData err := DecompressAndUnmarshal(data, &result) require.NoError(t, err) assert.Equal(t, "ACTIVE", result.Status) assert.Equal(t, []string{"shard1", "shard2"}, result.Shards) }) t.Run("Compressed data", func(t *testing.T) { original := testData{ Status: "DRAINING", Shards: []string{"shard3", "shard4"}, } originalJSON, _ := json.Marshal(original) writer, err := NewRecordWriter(CompressionSnappy) require.NoError(t, err) compressed, err := writer.Write(originalJSON) require.NoError(t, err) var result testData err = DecompressAndUnmarshal(compressed, &result) require.NoError(t, err) assert.Equal(t, original.Status, result.Status) assert.Equal(t, original.Shards, result.Shards) }) t.Run("Invalid JSON in uncompressed data", func(t *testing.T) { invalidJSON := []byte(`{invalid json}`) var result testData err := DecompressAndUnmarshal(invalidJSON, &result) require.Error(t, err) assert.Contains(t, err.Error(), "unmarshal") }) } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/etcdstore.go ================================================ package executorstore //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination=executorstore_mock.go ExecutorStore import ( "context" "encoding/json" "errors" "fmt" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/fx" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdclient" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdkeys" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdtypes" "github.com/uber/cadence/service/sharddistributor/store/etcd/executorstore/common" "github.com/uber/cadence/service/sharddistributor/store/etcd/executorstore/shardcache" ) const ( // guardOpOverhead is the number of transaction slots consumed by the leadership guard's If condition. guardOpOverhead = 1 ) var ( _executorStatusRunningJSON = fmt.Sprintf(`"%s"`, types.ExecutorStatusACTIVE) ) type executorStoreImpl struct { client etcdclient.Client prefix string logger log.Logger shardCache *shardcache.ShardToExecutorCache timeSource clock.TimeSource recordWriter *common.RecordWriter cfg *config.Config metricsClient metrics.Client } // shardStatisticsUpdate holds the staged statistics for a shard so we can write them // to etcd after the main AssignShards transaction commits. type shardStatisticsUpdate struct { executorID string stats map[string]etcdtypes.ShardStatistics } // ExecutorStoreParams defines the dependencies for the etcd store, for use with fx. type ExecutorStoreParams struct { fx.In Client etcdclient.Client `name:"executorstore"` ETCDConfig ETCDConfig Lifecycle fx.Lifecycle Logger log.Logger TimeSource clock.TimeSource Config *config.Config MetricsClient metrics.Client } // NewStore creates a new etcd-backed store and provides it to the fx application. func NewStore(p ExecutorStoreParams) (store.Store, error) { shardCache := shardcache.NewShardToExecutorCache(p.ETCDConfig.Prefix, p.Client, p.Logger, p.TimeSource, p.MetricsClient) timeSource := p.TimeSource if timeSource == nil { timeSource = clock.NewRealTimeSource() } recordWriter, err := common.NewRecordWriter(p.ETCDConfig.Compression) if err != nil { return nil, fmt.Errorf("create record writer: %w", err) } store := &executorStoreImpl{ client: p.Client, prefix: p.ETCDConfig.Prefix, logger: p.Logger, shardCache: shardCache, timeSource: timeSource, recordWriter: recordWriter, cfg: p.Config, metricsClient: p.MetricsClient, } p.Lifecycle.Append(fx.StartStopHook(store.Start, store.Stop)) return store, nil } func (s *executorStoreImpl) Start() { s.shardCache.Start() } func (s *executorStoreImpl) Stop() { s.shardCache.Stop() } // --- HeartbeatStore Implementation --- func (s *executorStoreImpl) RecordHeartbeat(ctx context.Context, namespace, executorID string, request store.HeartbeatState) error { heartbeatKey := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorHeartbeatKey) stateKey := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorStatusKey) reportedShardsKey := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorReportedShardsKey) reportedShardsData, err := json.Marshal(request.ReportedShards) if err != nil { return fmt.Errorf("marshal reported shards: %w", err) } jsonState, err := json.Marshal(request.Status) if err != nil { return fmt.Errorf("marshal assinged state: %w", err) } // Compress data before writing to etcd compressedReportedShards, err := s.recordWriter.Write(reportedShardsData) if err != nil { return fmt.Errorf("compress reported shards: %w", err) } compressedState, err := s.recordWriter.Write(jsonState) if err != nil { return fmt.Errorf("compress assigned state: %w", err) } // Build all operations including metadata ops := []clientv3.Op{ clientv3.OpPut(heartbeatKey, etcdtypes.FormatTime(request.LastHeartbeat)), clientv3.OpPut(stateKey, string(compressedState)), clientv3.OpPut(reportedShardsKey, string(compressedReportedShards)), } for key, value := range request.Metadata { metadataKey := etcdkeys.BuildMetadataKey(s.prefix, namespace, executorID, key) ops = append(ops, clientv3.OpPut(metadataKey, value)) } // Atomically update both the timestamp and the state. _, err = s.client.Txn(ctx).Then(ops...).Commit() if err != nil { return fmt.Errorf("record heartbeat: %w", err) } return nil } // GetHeartbeat retrieves the last known heartbeat state for a single executor. func (s *executorStoreImpl) GetHeartbeat(ctx context.Context, namespace string, executorID string) (*store.HeartbeatState, *store.AssignedState, error) { // The prefix for all keys related to a single executor. executorIDPrefix := etcdkeys.BuildExecutorIDPrefix(s.prefix, namespace, executorID) resp, err := s.client.Get(ctx, executorIDPrefix, clientv3.WithPrefix()) if err != nil { return nil, nil, fmt.Errorf("etcd get failed for executor %s: %w", executorID, err) } if resp.Count == 0 { return nil, nil, store.ErrExecutorNotFound } heartbeatState := &store.HeartbeatState{} assignedState := &etcdtypes.AssignedState{} found := false for _, kv := range resp.Kvs { key := string(kv.Key) value := string(kv.Value) _, keyType, keyErr := etcdkeys.ParseExecutorKey(s.prefix, namespace, key) if keyErr != nil { continue // Ignore unexpected keys } found = true // We found at least one valid key part for the executor. switch keyType { case etcdkeys.ExecutorHeartbeatKey: heartbeatState.LastHeartbeat, err = etcdtypes.ParseTime(value) if err != nil { return nil, nil, fmt.Errorf("parse heartbeat timestamp: %w", err) } case etcdkeys.ExecutorStatusKey: if err := common.DecompressAndUnmarshal(kv.Value, &heartbeatState.Status); err != nil { return nil, nil, fmt.Errorf("parse executor status: %w", err) } case etcdkeys.ExecutorReportedShardsKey: if err := common.DecompressAndUnmarshal(kv.Value, &heartbeatState.ReportedShards); err != nil { return nil, nil, fmt.Errorf("parse reported shards: %w", err) } case etcdkeys.ExecutorAssignedStateKey: assignedState.ModRevision = kv.ModRevision if err := common.DecompressAndUnmarshal(kv.Value, &assignedState); err != nil { return nil, nil, fmt.Errorf("parse assigned shards: %w", err) } } } if !found { // This case is unlikely if resp.Count > 0, but is a good safeguard. return nil, nil, store.ErrExecutorNotFound } return heartbeatState, assignedState.ToAssignedState(), nil } // --- ShardStore Implementation --- func (s *executorStoreImpl) GetState(ctx context.Context, namespace string) (*store.NamespaceState, error) { heartbeatStates := make(map[string]store.HeartbeatState) assignedStates := make(map[string]store.AssignedState) shardStats := make(map[string]store.ShardStatistics) executorPrefix := etcdkeys.BuildExecutorsPrefix(s.prefix, namespace) resp, err := s.client.Get(ctx, executorPrefix, clientv3.WithPrefix()) if err != nil { return nil, fmt.Errorf("get executor data: %w", err) } for _, kv := range resp.Kvs { key := string(kv.Key) value := string(kv.Value) executorID, keyType, keyErr := etcdkeys.ParseExecutorKey(s.prefix, namespace, key) if keyErr != nil { continue } heartbeat := heartbeatStates[executorID] assigned := assignedStates[executorID] switch keyType { case etcdkeys.ExecutorHeartbeatKey: heartbeat.LastHeartbeat, err = etcdtypes.ParseTime(value) if err != nil { return nil, fmt.Errorf("parse heartbeat timestamp: %w", err) } case etcdkeys.ExecutorStatusKey: if err := common.DecompressAndUnmarshal(kv.Value, &heartbeat.Status); err != nil { return nil, fmt.Errorf("parse executor status: %w", err) } case etcdkeys.ExecutorReportedShardsKey: if err := common.DecompressAndUnmarshal(kv.Value, &heartbeat.ReportedShards); err != nil { return nil, fmt.Errorf("parse reported shards: %w", err) } case etcdkeys.ExecutorAssignedStateKey: var assignedRaw etcdtypes.AssignedState if err := common.DecompressAndUnmarshal(kv.Value, &assignedRaw); err != nil { return nil, fmt.Errorf("parse assigned shards: %w, %s", err, value) } assignedRaw.ModRevision = kv.ModRevision assigned = *assignedRaw.ToAssignedState() case etcdkeys.ExecutorShardStatisticsKey: // Only load shard statistics if the load balancing mode requires it // TODO: refactor this code to not have a dependency on dynamic config in the store layer if s.cfg.GetLoadBalancingMode(namespace) == types.LoadBalancingModeGREEDY { executorShardStats := make(map[string]etcdtypes.ShardStatistics) if err := common.DecompressAndUnmarshal(kv.Value, &executorShardStats); err != nil { return nil, fmt.Errorf("parse executor shard statistics: %w, %s", err, value) } for shardID, stat := range executorShardStats { shardStats[shardID] = *stat.ToShardStatistics() } } } heartbeatStates[executorID] = heartbeat assignedStates[executorID] = assigned } return &store.NamespaceState{ Executors: heartbeatStates, ShardStats: shardStats, ShardAssignments: assignedStates, }, nil } func (s *executorStoreImpl) SubscribeToAssignmentChanges(ctx context.Context, namespace string) (<-chan map[*store.ShardOwner][]string, func(), error) { return s.shardCache.Subscribe(ctx, namespace) } func (s *executorStoreImpl) SubscribeToExecutorStatusChanges(ctx context.Context, namespace string) (<-chan int64, error) { revisionChan := make(chan int64, 1) go func() { defer close(revisionChan) scope := s.metricsClient.Scope(metrics.ShardDistributorWatchScope). Tagged(metrics.NamespaceTag(namespace)). Tagged(metrics.ShardDistributorWatchTypeTag("rebalance")) watchChan := s.client.Watch(ctx, etcdkeys.BuildExecutorsPrefix(s.prefix, namespace), clientv3.WithPrefix(), clientv3.WithPrevKV(), ) for watchResp := range watchChan { if err := watchResp.Err(); err != nil { return } // Track watch metrics sw := scope.StartTimer(metrics.ShardDistributorWatchProcessingLatency) scope.AddCounter(metrics.ShardDistributorWatchEventsReceived, int64(len(watchResp.Events))) if !s.hasExecutorStatusChanged(watchResp, namespace) { sw.Stop() continue } // If the channel is full, it means the previous revision hasn't been processed yet. // Pop the old revision to make room for the new one, ensuring we always have the latest revision. select { case <-revisionChan: default: } revisionChan <- watchResp.Header.Revision sw.Stop() } }() return revisionChan, nil } // hasExecutorStatusChanged checks if any of the events in the watch response correspond to changes in executor status. func (s *executorStoreImpl) hasExecutorStatusChanged(watchResp clientv3.WatchResponse, namespace string) bool { for _, event := range watchResp.Events { _, keyType, err := etcdkeys.ParseExecutorKey(s.prefix, namespace, string(event.Kv.Key)) if err != nil { s.logger.Warn("Received watch event with unrecognized key format", tag.Key(string(event.Kv.Key))) continue } // Only consider changes to the ExecutorStatusKey as significant for triggering a revision update. if keyType != etcdkeys.ExecutorStatusKey { continue } // If the previous value is the same as the new value, it means the status didn't actually change if event.PrevKv != nil && string(event.PrevKv.Value) == string(event.Kv.Value) { continue } return true } return false } func (s *executorStoreImpl) AssignShards(ctx context.Context, namespace string, request store.AssignShardsRequest, guard store.GuardFunc) (err error) { var ops []clientv3.Op var opsElse []clientv3.Op var comparisons []clientv3.Cmp comparisonMaps := make(map[string]int64) // TODO: Should be extracted to a higher level so that statistics updates are prepared if s.cfg.GetLoadBalancingMode(namespace) == types.LoadBalancingModeGREEDY { statsUpdates, errUpdate := s.prepareShardStatisticsUpdates(ctx, namespace, request.NewState.ShardAssignments) if errUpdate != nil { return fmt.Errorf("prepare shard statistics: %w", err) } defer func() { // Apply the shard statistics updates after the main transaction commits. // Only apply if there was no error in the main transaction. if err != nil { return } s.applyShardStatisticsUpdates(ctx, namespace, statsUpdates) }() } // 1. Prepare operations to delete stale executors and add comparisons to ensure they haven't been modified for executorID, expectedModRevision := range request.ExecutorsToDelete { // Build the assigned state key to check for concurrent modifications executorStateKey := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorAssignedStateKey) // Add a comparison to ensure the executor's assigned state hasn't changed // This prevents deleting an executor that just received a shard assignment comparisons = append(comparisons, clientv3.Compare(clientv3.ModRevision(executorStateKey), "=", expectedModRevision)) comparisonMaps[executorStateKey] = expectedModRevision // Delete all keys for this executor executorPrefix := etcdkeys.BuildExecutorIDPrefix(s.prefix, namespace, executorID) ops = append(ops, clientv3.OpDelete(executorPrefix, clientv3.WithPrefix())) opsElse = append(opsElse, clientv3.OpGet(executorStateKey)) } // 2. Prepare operations to update executor states and shard ownership, // and comparisons to check for concurrent modifications. for executorID, state := range request.NewState.ShardAssignments { // Update the executor's assigned_state key. executorStateKey := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorAssignedStateKey) value, err := json.Marshal(etcdtypes.FromAssignedState(&state)) if err != nil { return fmt.Errorf("marshal assigned shards for executor %s: %w", executorID, err) } compressedValue, err := s.recordWriter.Write(value) if err != nil { return fmt.Errorf("compress assigned shards for executor %s: %w", executorID, err) } ops = append(ops, clientv3.OpPut(executorStateKey, string(compressedValue))) comparisons = append(comparisons, clientv3.Compare(clientv3.ModRevision(executorStateKey), "=", state.ModRevision)) comparisonMaps[executorStateKey] = state.ModRevision opsElse = append(opsElse, clientv3.OpGet(executorStateKey)) } if len(ops) == 0 { return nil } // 3. Apply the guard function to get the base transaction, which may already have an 'If' condition for leadership. nativeTxn := s.client.Txn(ctx) guardedTxn, err := guard(nativeTxn) if err != nil { return fmt.Errorf("apply transaction guard: %w", err) } etcdGuardedTxn, ok := guardedTxn.(clientv3.Txn) if !ok { return fmt.Errorf("guard function returned invalid transaction type") } // 4. Create a nested transaction operation. This allows us to add our own 'If' (comparisons) // and 'Then' (ops) logic that will only execute if the outer guard's 'If' condition passes. // we catch what is the state in the else operations so we can identify which part of the condition failed nestedTxnOp := clientv3.OpTxn( comparisons, // Our IF conditions ops, // Our THEN operations opsElse, // Our ELSE operations ) // 5. Add the nested transaction to the guarded transaction's THEN clause and commit. etcdGuardedTxn = etcdGuardedTxn.Then(nestedTxnOp) txnResp, err := etcdGuardedTxn.Commit() if err != nil { return fmt.Errorf("commit shard assignments transaction: %w", err) } // 6. Check the results of both the outer and nested transactions. if !txnResp.Succeeded { // This means the guard's condition (e.g., leadership) failed. return fmt.Errorf("%w: transaction failed, leadership may have changed", store.ErrVersionConflict) } // The guard's condition passed. Now check if our nested transaction succeeded. // Since we only have one Op in our 'Then', we check the first response. if len(txnResp.Responses) == 0 { return fmt.Errorf("unexpected empty response from transaction") } nestedResp := txnResp.Responses[0].GetResponseTxn() if !nestedResp.Succeeded { // This means our revision checks failed. failingRevisionString := "" for _, keyValue := range nestedResp.Responses[0].GetResponseRange().Kvs { expectedValue, ok := comparisonMaps[string(keyValue.Key)] if !ok || expectedValue != keyValue.ModRevision { failingRevisionString = failingRevisionString + fmt.Sprintf("{ key: %s, expected:%v, actual: %v }", string(keyValue.Key), expectedValue, keyValue.ModRevision) } } return fmt.Errorf("%w: transaction failed, a shard may have been concurrently assigned, %v", store.ErrVersionConflict, failingRevisionString) } return nil } func (s *executorStoreImpl) AssignShard(ctx context.Context, namespace, shardID, executorID string) error { assignedState := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorAssignedStateKey) statusKey := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorStatusKey) // Use a read-modify-write loop to handle concurrent updates safely. for { var comparisons []clientv3.Cmp var ops []clientv3.Op // 1. Get the current assigned state of the executor resp, err := s.client.Get(ctx, assignedState) if err != nil { return fmt.Errorf("get executor assigned state: %w", err) } var state etcdtypes.AssignedState modRevision := int64(0) // A revision of 0 means the key doesn't exist yet. if len(resp.Kvs) > 0 { // If the executor already has shards, load its state. kv := resp.Kvs[0] modRevision = kv.ModRevision if err := common.DecompressAndUnmarshal(kv.Value, &state); err != nil { return fmt.Errorf("parse assigned state: %w", err) } } else { // If this is the first shard, initialize the state map. state.AssignedShards = make(map[string]*types.ShardAssignment) } // 2. Get the executor state. statusResp, err := s.client.Get(ctx, statusKey) if err != nil { return fmt.Errorf("get executor status: %w", err) } if len(statusResp.Kvs) == 0 { return store.ErrExecutorNotFound } statusValue := string(statusResp.Kvs[0].Value) decompressedStatusValue, err := common.Decompress(statusResp.Kvs[0].Value) if err != nil { return fmt.Errorf("decompress executor status: %w", err) } if string(decompressedStatusValue) != _executorStatusRunningJSON { return fmt.Errorf("%w: executor status is %s", store.ErrVersionConflict, statusValue) } statusModRev := statusResp.Kvs[0].ModRevision // 3. Modify the state in memory, adding the new shard if it's not already there. if _, alreadyAssigned := state.AssignedShards[shardID]; !alreadyAssigned { state.AssignedShards[shardID] = &types.ShardAssignment{Status: types.AssignmentStatusREADY} } // Update the last updated timestamp. now := s.timeSource.Now().UTC() state.LastUpdated = etcdtypes.Time(now) // Compress new state value newStateValue, err := json.Marshal(state) if err != nil { return fmt.Errorf("marshal new assigned state: %w", err) } compressedStateValue, err := s.recordWriter.Write(newStateValue) if err != nil { return fmt.Errorf("compress new assigned state: %w", err) } ops = append(ops, clientv3.OpPut(assignedState, string(compressedStateValue))) // 4. Prepare and commit the transaction with four atomic checks. // a) Check that the executor's status ACTIVE has not been changed. comparisons = append(comparisons, clientv3.Compare(clientv3.ModRevision(statusKey), "=", statusModRev)) // b) Check that the assigned_state has not been modified concurrently. comparisons = append(comparisons, clientv3.Compare(clientv3.ModRevision(assignedState), "=", modRevision)) // c) Check that the cache is up to date. cmp, err := s.shardCache.GetExecutorModRevisionCmp(namespace) if err != nil { return fmt.Errorf("get executor mod revision cmp: %w", err) } comparisons = append(comparisons, cmp...) // We check the shard cache to see if the shard is already assigned to an executor. shardOwner, err := s.shardCache.GetShardOwner(ctx, namespace, shardID) if err != nil && !errors.Is(err, store.ErrShardNotFound) { return fmt.Errorf("checking shard owner: %w", err) } if err == nil { return &store.ErrShardAlreadyAssigned{ShardID: shardID, AssignedTo: shardOwner.ExecutorID, Metadata: shardOwner.Metadata} } // TODO: Extract to higher level so that statistics updates are prepared if s.cfg.GetLoadBalancingMode(namespace) == types.LoadBalancingModeGREEDY { executorStatsKey := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorShardStatisticsKey) statsResp, err := s.client.Get(ctx, executorStatsKey) if err != nil { return fmt.Errorf("get shard statistics: %w", err) } executorShardStats := make(map[string]etcdtypes.ShardStatistics) if len(statsResp.Kvs) > 0 { if err := common.DecompressAndUnmarshal(statsResp.Kvs[0].Value, &executorShardStats); err != nil { return fmt.Errorf("parse shard statistics: %w", err) } } shardStats, ok := executorShardStats[shardID] if !ok { shardStats.SmoothedLoad = 0 shardStats.LastUpdateTime = etcdtypes.Time(now) } shardStats.LastMoveTime = etcdtypes.Time(now) executorShardStats[shardID] = shardStats newStatsValue, err := json.Marshal(executorShardStats) if err != nil { return fmt.Errorf("marshal new shard statistics: %w", err) } compressedStatsValue, err := s.recordWriter.Write(newStatsValue) if err != nil { return fmt.Errorf("compress new shard statistics: %w", err) } ops = append(ops, clientv3.OpPut(executorStatsKey, string(compressedStatsValue))) } txnResp, err := s.client.Txn(ctx). If(comparisons...). Then(ops...). Commit() if err != nil { return fmt.Errorf("assign shard transaction: %w", err) } if txnResp.Succeeded { return nil } // If the transaction failed, another process interfered. // Provide a specific error if the status check failed. currentStatusResp, err := s.client.Get(ctx, statusKey) if err != nil || len(currentStatusResp.Kvs) == 0 { return store.ErrExecutorNotFound } decompressedStatus, err := common.Decompress(currentStatusResp.Kvs[0].Value) if err != nil { return fmt.Errorf("decompress executor status %w", err) } if string(decompressedStatus) != _executorStatusRunningJSON { return fmt.Errorf(`%w: executor status is %s"`, store.ErrVersionConflict, currentStatusResp.Kvs[0].Value) } s.logger.Info("Assign shard transaction failed due to a conflict. Retrying...", tag.ShardNamespace(namespace), tag.ShardKey(shardID), tag.ShardExecutor(executorID)) // Otherwise, it was a revision mismatch. Loop to retry the operation. } } // commitGuardedOps commits the given operations in batches to stay within etcd's per-transaction operation limit. // Each batch creates a new guarded transaction. If any batch fails, the function returns immediately // with the error func (s *executorStoreImpl) commitGuardedOps(ctx context.Context, ops []clientv3.Op, guard store.GuardFunc) error { if len(ops) == 0 { return nil } maxOpsPerTxn := s.cfg.MaxEtcdTxnOps() - guardOpOverhead if maxOpsPerTxn < 1 { maxOpsPerTxn = 1 } numBatches := (len(ops) + maxOpsPerTxn - 1) / maxOpsPerTxn batchSize := (len(ops) + numBatches - 1) / numBatches for i := 0; i < len(ops); i += batchSize { end := i + batchSize if end > len(ops) { end = len(ops) } nativeTxn := s.client.Txn(ctx) guardedTxn, err := guard(nativeTxn) if err != nil { return fmt.Errorf("apply transaction guard: %w", err) } etcdGuardedTxn, ok := guardedTxn.(clientv3.Txn) if !ok { return fmt.Errorf("guard function returned invalid transaction type") } etcdGuardedTxn = etcdGuardedTxn.Then(ops[i:end]...) resp, err := etcdGuardedTxn.Commit() if err != nil { return fmt.Errorf("commit batch: %w", err) } if !resp.Succeeded { return fmt.Errorf("transaction failed, leadership may have changed") } } return nil } // DeleteExecutors deletes the given executors from the store. It does not delete the shards owned by the executors, this // should be handled by the namespace processor loop as we want to reassign, not delete the shards. func (s *executorStoreImpl) DeleteExecutors(ctx context.Context, namespace string, executorIDs []string, guard store.GuardFunc) error { if len(executorIDs) == 0 { return nil } ops := make([]clientv3.Op, 0, len(executorIDs)) for _, executorID := range executorIDs { executorIDPrefix := etcdkeys.BuildExecutorIDPrefix(s.prefix, namespace, executorID) ops = append(ops, clientv3.OpDelete(executorIDPrefix, clientv3.WithPrefix())) } if err := s.commitGuardedOps(ctx, ops, guard); err != nil { return fmt.Errorf("delete executors: %w", err) } return nil } func (s *executorStoreImpl) DeleteAssignedStates(ctx context.Context, namespace string, executorIDs []string, guard store.GuardFunc) error { if len(executorIDs) == 0 { return nil } ops := make([]clientv3.Op, 0, len(executorIDs)) for _, executorID := range executorIDs { executorIDPrefix := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorAssignedStateKey) ops = append(ops, clientv3.OpDelete(executorIDPrefix, clientv3.WithPrefix())) } if err := s.commitGuardedOps(ctx, ops, guard); err != nil { return fmt.Errorf("delete assigned states: %w", err) } return nil } // DeleteShardStats deletes shard statistics for the given shard IDs. // If the operation fails (e.g. due to leadership loss), it returns immediately. // Partial deletions are acceptable as the periodic cleanup loop will retry remaining keys. func (s *executorStoreImpl) DeleteShardStats(ctx context.Context, namespace string, shardIDs []string, guard store.GuardFunc) error { if len(shardIDs) == 0 { return nil } // Build a lookup for shard IDs to delete. toDelete := make(map[string]struct{}, len(shardIDs)) for _, shardID := range shardIDs { toDelete[shardID] = struct{}{} } // Fetch all statistics at the executor level for this namespace. executorPrefix := etcdkeys.BuildExecutorsPrefix(s.prefix, namespace) resp, err := s.client.Get(ctx, executorPrefix, clientv3.WithPrefix()) if err != nil { return fmt.Errorf("get executor data for shard stats deletion: %w", err) } ops := make([]clientv3.Op, 0) for _, kv := range resp.Kvs { key := string(kv.Key) executorID, keyType, keyErr := etcdkeys.ParseExecutorKey(s.prefix, namespace, key) if keyErr != nil || keyType != etcdkeys.ExecutorShardStatisticsKey { continue } executorStats := make(map[string]etcdtypes.ShardStatistics) if err := common.DecompressAndUnmarshal(kv.Value, &executorStats); err != nil { s.logger.Warn( "failed to parse executor shard statistics during cleanup", tag.ShardNamespace(namespace), tag.ShardExecutor(executorID), tag.Error(err), ) continue } changed := false for shardID := range executorStats { if _, ok := toDelete[shardID]; ok { delete(executorStats, shardID) changed = true } } if !changed { continue } statsKey := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorShardStatisticsKey) if len(executorStats) == 0 { ops = append(ops, clientv3.OpDelete(statsKey)) continue } payload, err := json.Marshal(executorStats) if err != nil { s.logger.Warn( "failed to marshal executor shard statistics during cleanup", tag.ShardNamespace(namespace), tag.ShardExecutor(executorID), tag.Error(err), ) continue } compressedPayload, err := s.recordWriter.Write(payload) if err != nil { s.logger.Warn( "failed to compress executor shard statistics during cleanup", tag.ShardNamespace(namespace), tag.ShardExecutor(executorID), tag.Error(err), ) continue } ops = append(ops, clientv3.OpPut(statsKey, string(compressedPayload))) } if err := s.commitGuardedOps(ctx, ops, guard); err != nil { return fmt.Errorf("delete shard stats: %w", err) } return nil } func (s *executorStoreImpl) GetShardOwner(ctx context.Context, namespace, shardID string) (*store.ShardOwner, error) { return s.shardCache.GetShardOwner(ctx, namespace, shardID) } func (s *executorStoreImpl) GetExecutor(ctx context.Context, namespace string, executorID string) (*store.ShardOwner, error) { return s.shardCache.GetExecutor(ctx, namespace, executorID) } func (s *executorStoreImpl) prepareShardStatisticsUpdates(ctx context.Context, namespace string, newAssignments map[string]store.AssignedState) ([]shardStatisticsUpdate, error) { executorStatsCache := make(map[string]map[string]etcdtypes.ShardStatistics) changedExecutors := make(map[string]struct{}) for executorID, state := range newAssignments { for shardID := range state.AssignedShards { now := s.timeSource.Now().UTC() oldOwner, err := s.shardCache.GetShardOwner(ctx, namespace, shardID) if err != nil && !errors.Is(err, store.ErrShardNotFound) { return nil, fmt.Errorf("lookup cached shard owner: %w", err) } if err == nil && oldOwner.ExecutorID == executorID { continue } var stats etcdtypes.ShardStatistics if err == nil { oldStats, err := s.getOrLoadExecutorShardStatistics(ctx, namespace, oldOwner.ExecutorID, executorStatsCache) if err != nil { return nil, err } if existing, ok := oldStats[shardID]; ok { stats = existing } delete(oldStats, shardID) changedExecutors[oldOwner.ExecutorID] = struct{}{} } else { stats.SmoothedLoad = 0 stats.LastUpdateTime = etcdtypes.Time(now) } stats.LastMoveTime = etcdtypes.Time(now) newStats, err := s.getOrLoadExecutorShardStatistics(ctx, namespace, executorID, executorStatsCache) if err != nil { return nil, err } newStats[shardID] = stats changedExecutors[executorID] = struct{}{} } } updates := make([]shardStatisticsUpdate, 0, len(changedExecutors)) for executorID := range changedExecutors { stats := executorStatsCache[executorID] updates = append(updates, shardStatisticsUpdate{ executorID: executorID, stats: stats, }) } return updates, nil } // applyShardStatisticsUpdates updates shard statistics. // Is intentionally made tolerant of failures since the data is telemetry only. func (s *executorStoreImpl) applyShardStatisticsUpdates(ctx context.Context, namespace string, updates []shardStatisticsUpdate) { for _, update := range updates { statsKey := etcdkeys.BuildExecutorKey(s.prefix, namespace, update.executorID, etcdkeys.ExecutorShardStatisticsKey) if len(update.stats) == 0 { if _, err := s.client.Delete(ctx, statsKey); err != nil { s.logger.Warn( "failed to delete executor shard statistics", tag.ShardNamespace(namespace), tag.ShardExecutor(update.executorID), tag.Error(err), ) } continue } payload, err := json.Marshal(update.stats) if err != nil { s.logger.Warn( "failed to marshal shard statistics after assignment", tag.ShardNamespace(namespace), tag.ShardExecutor(update.executorID), tag.Error(err), ) continue } compressedPayload, err := s.recordWriter.Write(payload) if err != nil { s.logger.Warn( "failed to compress shard statistics after assignment", tag.ShardNamespace(namespace), tag.ShardExecutor(update.executorID), tag.Error(err), ) continue } if _, err := s.client.Put(ctx, statsKey, string(compressedPayload)); err != nil { s.logger.Warn( "failed to update shard statistics", tag.ShardNamespace(namespace), tag.ShardExecutor(update.executorID), tag.Error(err), ) } } } // getExecutorShardStatistics returns the shard statistics for the given executor from etcd. func (s *executorStoreImpl) getExecutorShardStatistics(ctx context.Context, namespace, executorID string) (map[string]etcdtypes.ShardStatistics, error) { statsKey := etcdkeys.BuildExecutorKey(s.prefix, namespace, executorID, etcdkeys.ExecutorShardStatisticsKey) resp, err := s.client.Get(ctx, statsKey) if err != nil { return nil, fmt.Errorf("get executor shard statistics: %w", err) } stats := make(map[string]etcdtypes.ShardStatistics) if len(resp.Kvs) == 0 { return stats, nil } if err := common.DecompressAndUnmarshal(resp.Kvs[0].Value, &stats); err != nil { return nil, fmt.Errorf("parse executor shard statistics: %w", err) } return stats, nil } // getOrLoadExecutorShardStatistics returns the shard statistics for the given executor. // If the statistics are not cached, it will fetch them from etcd. func (s *executorStoreImpl) getOrLoadExecutorShardStatistics( ctx context.Context, namespace, executorID string, cache map[string]map[string]etcdtypes.ShardStatistics, ) (map[string]etcdtypes.ShardStatistics, error) { // Load from cache if available. if stats, ok := cache[executorID]; ok { return stats, nil } // Otherwise, load from etcd. stats, err := s.getExecutorShardStatistics(ctx, namespace, executorID) if err != nil { return nil, err } cache[executorID] = stats return stats, nil } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/etcdstore_test.go ================================================ package executorstore import ( "context" "encoding/json" "fmt" "strconv" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/fx/fxtest" "go.uber.org/mock/gomock" "gopkg.in/yaml.v2" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdclient" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdkeys" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdtypes" "github.com/uber/cadence/service/sharddistributor/store/etcd/executorstore/common" "github.com/uber/cadence/service/sharddistributor/store/etcd/leaderstore" "github.com/uber/cadence/service/sharddistributor/store/etcd/testhelper" ) // TestRecordHeartbeat verifies that an executor's heartbeat is correctly stored. func TestRecordHeartbeat(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() now := time.Now().UTC() executorID := "executor-TestRecordHeartbeat" req := store.HeartbeatState{ LastHeartbeat: now, Status: types.ExecutorStatusACTIVE, ReportedShards: map[string]*types.ShardStatusReport{ "shard-TestRecordHeartbeat": {Status: types.ShardStatusREADY}, }, Metadata: map[string]string{ "key-1": "value-1", "key-2": "value-2", }, } err := executorStore.RecordHeartbeat(ctx, tc.Namespace, executorID, req) require.NoError(t, err) // Verify directly in etcd heartbeatKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, etcdkeys.ExecutorHeartbeatKey) stateKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, etcdkeys.ExecutorStatusKey) reportedShardsKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, etcdkeys.ExecutorReportedShardsKey) metadataKey1 := etcdkeys.BuildMetadataKey(tc.EtcdPrefix, tc.Namespace, executorID, "key-1") metadataKey2 := etcdkeys.BuildMetadataKey(tc.EtcdPrefix, tc.Namespace, executorID, "key-2") resp, err := tc.Client.Get(ctx, heartbeatKey) require.NoError(t, err) assert.Equal(t, int64(1), resp.Count, "Heartbeat key should exist") assert.Equal(t, etcdtypes.FormatTime(now), string(resp.Kvs[0].Value)) resp, err = tc.Client.Get(ctx, stateKey) require.NoError(t, err) require.Equal(t, int64(1), resp.Count, "State key should exist") decompressedState, err := common.Decompress(resp.Kvs[0].Value) require.NoError(t, err) assert.Equal(t, stringStatus(types.ExecutorStatusACTIVE), string(decompressedState)) resp, err = tc.Client.Get(ctx, reportedShardsKey) require.NoError(t, err) require.Equal(t, int64(1), resp.Count, "Reported shards key should exist") decompressedReportedShards, err := common.Decompress(resp.Kvs[0].Value) require.NoError(t, err) var reportedShards map[string]*types.ShardStatusReport err = json.Unmarshal(decompressedReportedShards, &reportedShards) require.NoError(t, err) require.Len(t, reportedShards, 1) assert.Equal(t, types.ShardStatusREADY, reportedShards["shard-TestRecordHeartbeat"].Status) resp, err = tc.Client.Get(ctx, metadataKey1) require.NoError(t, err) require.Equal(t, int64(1), resp.Count, "Metadata key 1 should exist") assert.Equal(t, "value-1", string(resp.Kvs[0].Value)) resp, err = tc.Client.Get(ctx, metadataKey2) require.NoError(t, err) require.Equal(t, int64(1), resp.Count, "Metadata key 2 should exist") assert.Equal(t, "value-2", string(resp.Kvs[0].Value)) } func TestRecordHeartbeat_NoCompression(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) var etcdCfg struct { Endpoints []string `yaml:"endpoints"` DialTimeout time.Duration `yaml:"dialTimeout"` Prefix string `yaml:"prefix"` Compression string `yaml:"compression"` } require.NoError(t, tc.LeaderCfg.Store.StorageParams.Decode(&etcdCfg)) etcdCfg.Compression = "none" encodedCfg, err := yaml.Marshal(etcdCfg) require.NoError(t, err) var yamlNode *config.YamlNode require.NoError(t, yaml.Unmarshal(encodedCfg, &yamlNode)) tc.LeaderCfg.Store.StorageParams = yamlNode tc.LeaderCfg.LeaderStore.StorageParams = yamlNode tc.Compression = "none" executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() executorID := "executor-no-compression" req := store.HeartbeatState{ LastHeartbeat: time.Now().UTC(), Status: types.ExecutorStatusACTIVE, ReportedShards: map[string]*types.ShardStatusReport{ "shard-no-compression": {Status: types.ShardStatusREADY}, }, } require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, executorID, req)) stateKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, etcdkeys.ExecutorStatusKey) require.NoError(t, err) reportedShardsKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, etcdkeys.ExecutorReportedShardsKey) require.NoError(t, err) stateResp, err := tc.Client.Get(ctx, stateKey) require.NoError(t, err) require.Equal(t, int64(1), stateResp.Count) statusJSON, err := json.Marshal(req.Status) require.NoError(t, err) assert.Equal(t, string(statusJSON), string(stateResp.Kvs[0].Value)) reportedResp, err := tc.Client.Get(ctx, reportedShardsKey) require.NoError(t, err) require.Equal(t, int64(1), reportedResp.Count) reportedJSON, err := json.Marshal(req.ReportedShards) require.NoError(t, err) assert.Equal(t, string(reportedJSON), string(reportedResp.Kvs[0].Value)) } func TestGetHeartbeat(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() now := time.Now().UTC() executorID := "executor-get" req := store.HeartbeatState{ Status: types.ExecutorStatusDRAINING, LastHeartbeat: now, } // 1. Record a heartbeat err := executorStore.RecordHeartbeat(ctx, tc.Namespace, executorID, req) require.NoError(t, err) // Assign shards to one executor assignState := map[string]store.AssignedState{ executorID: { AssignedShards: map[string]*types.ShardAssignment{ "shard-1": {Status: types.AssignmentStatusREADY}, }, }, } require.NoError(t, executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{ NewState: &store.NamespaceState{ ShardAssignments: assignState, }, }, store.NopGuard())) // 2. Get the heartbeat back hb, assignedFromDB, err := executorStore.GetHeartbeat(ctx, tc.Namespace, executorID) require.NoError(t, err) require.NotNil(t, hb) // 3. Verify the state assert.Equal(t, types.ExecutorStatusDRAINING, hb.Status) assert.Equal(t, now, hb.LastHeartbeat) require.NotNil(t, assignedFromDB.AssignedShards) assert.Equal(t, assignState[executorID].AssignedShards, assignedFromDB.AssignedShards) // 4. Test getting a non-existent executor _, _, err = executorStore.GetHeartbeat(ctx, tc.Namespace, "executor-non-existent") require.Error(t, err) assert.ErrorIs(t, err, store.ErrExecutorNotFound) } // TestGetState verifies that the store can accurately retrieve the state of all executors. func TestGetState(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() executorID1 := "exec-TestGetState-1" executorID2 := "exec-TestGetState-2" shardID1 := "shard-1" shardID2 := "shard-2" // Setup: Record heartbeats and assign shards. require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, executorID1, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, executorID2, store.HeartbeatState{Status: types.ExecutorStatusDRAINING})) require.NoError(t, executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{ NewState: &store.NamespaceState{ ShardAssignments: map[string]store.AssignedState{ executorID1: {AssignedShards: map[string]*types.ShardAssignment{shardID1: {}}}, executorID2: {AssignedShards: map[string]*types.ShardAssignment{shardID2: {}}}, }, }, }, store.NopGuard())) // Action: Get the state. namespaceState, err := executorStore.GetState(ctx, tc.Namespace) require.NoError(t, err) // Verification: // Check Executors require.Len(t, namespaceState.Executors, 2, "Should retrieve two heartbeat states") assert.Equal(t, types.ExecutorStatusACTIVE, namespaceState.Executors[executorID1].Status) assert.Equal(t, types.ExecutorStatusDRAINING, namespaceState.Executors[executorID2].Status) // Check ShardAssignments (from executor records) require.Len(t, namespaceState.ShardAssignments, 2, "Should retrieve two assignment states") assert.Contains(t, namespaceState.ShardAssignments[executorID1].AssignedShards, shardID1) assert.Contains(t, namespaceState.ShardAssignments[executorID2].AssignedShards, shardID2) } // TestAssignShards_WithRevisions tests the optimistic locking logic of AssignShards. func TestAssignShards_WithRevisions(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() executorID1 := "exec-rev-1" executorID2 := "exec-rev-2" t.Run("Success", func(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) recordHeartbeats(ctx, t, executorStore, tc.Namespace, executorID1, executorID2) // Define a new state: assign shard1 to exec1 newState := &store.NamespaceState{ ShardAssignments: map[string]store.AssignedState{ executorID1: {AssignedShards: map[string]*types.ShardAssignment{"shard-1": {}}}, }, } // Assign - should succeed err := executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{NewState: newState}, store.NopGuard()) require.NoError(t, err) // Verify the assignment state, err := executorStore.GetState(ctx, tc.Namespace) require.NoError(t, err) assert.Contains(t, state.ShardAssignments[executorID1].AssignedShards, "shard-1") }) t.Run("ConflictOnNewShard", func(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) recordHeartbeats(ctx, t, executorStore, tc.Namespace, executorID1, executorID2) // Process A defines its desired state: assign shard-new to exec1 processAState := &store.NamespaceState{ ShardAssignments: map[string]store.AssignedState{ executorID1: {AssignedShards: map[string]*types.ShardAssignment{"shard-new": {}}}, executorID2: {}, }, } // Process B defines its desired state: assign shard-new to exec2 processBState := &store.NamespaceState{ ShardAssignments: map[string]store.AssignedState{ executorID1: {}, executorID2: {AssignedShards: map[string]*types.ShardAssignment{"shard-new": {}}}, }, } // Process A succeeds err := executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{NewState: processAState}, store.NopGuard()) require.NoError(t, err) // Process B tries to commit, but its revision check for shard-new (rev=0) will fail. err = executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{NewState: processBState}, store.NopGuard()) require.Error(t, err) assert.ErrorIs(t, err, store.ErrVersionConflict) }) t.Run("ConflictOnExistingShard", func(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) recordHeartbeats(ctx, t, executorStore, tc.Namespace, executorID1, executorID2) shardID := "shard-to-move" // 1. Setup: Assign the shard to executor1 setupState, err := executorStore.GetState(ctx, tc.Namespace) require.NoError(t, err) setupState.ShardAssignments = map[string]store.AssignedState{ executorID1: {AssignedShards: map[string]*types.ShardAssignment{shardID: {}}}, } require.NoError(t, executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{NewState: setupState}, store.NopGuard())) // 2. Process A reads the state, intending to move the shard to executor2 stateForProcA, err := executorStore.GetState(ctx, tc.Namespace) require.NoError(t, err) stateForProcA.ShardAssignments = map[string]store.AssignedState{ executorID1: {ModRevision: stateForProcA.ShardAssignments[executorID1].ModRevision}, executorID2: {AssignedShards: map[string]*types.ShardAssignment{shardID: {}}, ModRevision: 0}, } // 3. In the meantime, another process makes a different change (e.g., re-assigns to same executor, which changes revision) intermediateState, err := executorStore.GetState(ctx, tc.Namespace) require.NoError(t, err) intermediateState.ShardAssignments = map[string]store.AssignedState{ executorID1: { AssignedShards: map[string]*types.ShardAssignment{shardID: {}}, ModRevision: intermediateState.ShardAssignments[executorID1].ModRevision, }, } require.NoError(t, executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{NewState: intermediateState}, store.NopGuard())) // 4. Process A tries to commit its change. It will fail because its stored revision for the shard is now stale. err = executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{NewState: stateForProcA}, store.NopGuard()) require.Error(t, err) assert.ErrorIs(t, err, store.ErrVersionConflict) }) t.Run("NoChanges", func(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) recordHeartbeats(ctx, t, executorStore, tc.Namespace, executorID1, executorID2) // Get the current state state, err := executorStore.GetState(ctx, tc.Namespace) require.NoError(t, err) // Call AssignShards with the same assignments err = executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{NewState: state}, store.NopGuard()) require.NoError(t, err, "Assigning with no changes should succeed") }) } // TestGuardedOperations verifies that AssignShards and DeleteExecutors respect the leader guard. func TestGuardedOperations(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() namespace := "test-guarded-ns" executorID := "exec-to-delete" // 1. Create two potential leaders // FIX: Use the correct constructor for the leader elector. elector, err := leaderstore.NewLeaderStore(leaderstore.StoreParams{Client: tc.Client, Cfg: tc.LeaderCfg, Lifecycle: fxtest.NewLifecycle(t)}) require.NoError(t, err) election1, err := elector.CreateElection(ctx, namespace) defer election1.Cleanup(ctx) defer func() { _ = election1.Cleanup(ctx) }() election2, err := elector.CreateElection(ctx, namespace) defer election2.Cleanup(ctx) defer func() { _ = election2.Cleanup(ctx) }() // 2. First node becomes leader require.NoError(t, election1.Campaign(ctx, "host-1")) validGuard := election1.Guard() // 3. Use the valid guard to assign shards - should succeed assignState := map[string]store.AssignedState{"exec-1": {}} err = executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{NewState: &store.NamespaceState{ShardAssignments: assignState}}, validGuard) require.NoError(t, err, "Assigning shards with a valid leader guard should succeed") // 4. First node resigns, second node becomes leader require.NoError(t, election1.Resign(ctx)) require.NoError(t, election2.Campaign(ctx, "host-2")) // 5. Use the now-invalid guard from the first leader - should fail err = executorStore.AssignShards(ctx, tc.Namespace, store.AssignShardsRequest{NewState: &store.NamespaceState{ShardAssignments: assignState}}, validGuard) require.Error(t, err, "Assigning shards with a stale leader guard should fail") // 6. Use the NopGuard to delete an executor - should succeed require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, executorID, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) err = executorStore.DeleteExecutors(ctx, tc.Namespace, []string{executorID}, store.NopGuard()) require.NoError(t, err, "Deleting an executor without a guard should succeed") // Verify deletion newState, err := executorStore.GetState(ctx, namespace) require.NoError(t, err) _, ok := newState.ShardAssignments[executorID] require.False(t, ok, "Executor should have been deleted") } // TestSubscribe verifies that the subscription channel receives notifications for significant changes. func TestSubscribeToExecutorStatusChanges(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() executorID := "exec-sub" // Start subscription sub, err := executorStore.SubscribeToExecutorStatusChanges(ctx, tc.Namespace) require.NoError(t, err) // Test case #1: Update heartbeat without changing status or reported shards - should NOT trigger notification { // Manually put a heartbeat update, which is an insignificant change heartbeatKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, "heartbeat") _, err = tc.Client.Put(ctx, heartbeatKey, "timestamp") require.NoError(t, err) select { case <-sub: t.Fatal("Should not receive notification for a heartbeat-only update") case <-time.After(100 * time.Millisecond): // Expected behavior } } // Test case #2: Update reported shards without changing status - should NOT trigger notification { // Manually put a reported shards update, which is an insignificant change reportedShardsKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, "reported_shards") writer, err := common.NewRecordWriter(tc.Compression) require.NoError(t, err) compressedShards, err := writer.Write([]byte(`{"shard-1":{"status":"running"}}`)) require.NoError(t, err) _, err = tc.Client.Put(ctx, reportedShardsKey, string(compressedShards)) require.NoError(t, err) select { case <-sub: t.Fatal("Should not receive notification for a reported-shards-only update") case <-time.After(100 * time.Millisecond): // Expected behavior } } // Test case #3: Update status without prevKV - should trigger notification { statusKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, "status") _, err = tc.Client.Put(ctx, statusKey, stringStatus(types.ExecutorStatusDRAINING)) require.NoError(t, err) select { case rev, ok := <-sub: require.True(t, ok, "Channel should be open") assert.Greater(t, rev, int64(0), "Should receive a valid revision for status change") case <-time.After(1 * time.Second): t.Fatal("Should have received a notification for a status change") } } // Test case #4: Update status with prevKV but the same value - should NOT trigger notification { statusKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, "status") _, err = tc.Client.Put(ctx, statusKey, stringStatus(types.ExecutorStatusDRAINING)) require.NoError(t, err) select { case <-sub: t.Fatal("Should not receive notification") case <-time.After(100 * time.Millisecond): // Expected behavior } } // Test case #5: Update status with prevKV - should trigger notification { statusKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, "status") _, err = tc.Client.Put(ctx, statusKey, stringStatus(types.ExecutorStatusACTIVE)) require.NoError(t, err) select { case rev, ok := <-sub: require.True(t, ok, "Channel should be open") assert.Greater(t, rev, int64(0), "Should receive a valid revision for status change") case <-time.After(1 * time.Second): t.Fatal("Should have received a notification for a status change") } } } func TestDeleteExecutors_Empty(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() err := executorStore.DeleteExecutors(ctx, tc.Namespace, []string{}, store.NopGuard()) require.NoError(t, err) } // TestDeleteExecutors covers various scenarios for the DeleteExecutors method. func TestDeleteExecutors(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // Setup: Create two active executors for the tests. executorID1 := "executor-to-delete-1" executorID2 := "executor-to-delete-2" survivingExecutorID := "executor-survivor" require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, executorID1, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, executorID2, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, survivingExecutorID, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) t.Run("SucceedsForNonExistentExecutor", func(t *testing.T) { // Action: Delete a non-existent executor. err := executorStore.DeleteExecutors(ctx, tc.Namespace, []string{"non-existent-executor"}, store.NopGuard()) // Verification: Should not return an error. require.NoError(t, err) }) t.Run("DeletesMultipleExecutors", func(t *testing.T) { // Setup: Create and assign shards to multiple executors. execToDelete1 := "multi-delete-1" execToDelete2 := "multi-delete-2" execToKeep := "multi-keep-1" shardOfDeletedExecutor1 := "multi-shard-1" shardOfDeletedExecutor2 := "multi-shard-2" shardOfSurvivingExecutor := "multi-shard-keep" require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, execToDelete1, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, execToDelete2, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, execToKeep, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) require.NoError(t, executorStore.AssignShard(ctx, tc.Namespace, shardOfDeletedExecutor1, execToDelete1)) require.NoError(t, executorStore.AssignShard(ctx, tc.Namespace, shardOfDeletedExecutor2, execToDelete2)) require.NoError(t, executorStore.AssignShard(ctx, tc.Namespace, shardOfSurvivingExecutor, execToKeep)) // Action: Delete two of the three executors in one call. err := executorStore.DeleteExecutors(ctx, tc.Namespace, []string{execToDelete1, execToDelete2}, store.NopGuard()) require.NoError(t, err) // Verification: // 1. Check deleted executors are gone. _, _, err = executorStore.GetHeartbeat(ctx, tc.Namespace, execToDelete1) assert.ErrorIs(t, err, store.ErrExecutorNotFound, "Executor 1 should be gone") _, _, err = executorStore.GetHeartbeat(ctx, tc.Namespace, execToDelete2) assert.ErrorIs(t, err, store.ErrExecutorNotFound, "Executor 2 should be gone") // 2. Check that the surviving executor remain. _, _, err = executorStore.GetHeartbeat(ctx, tc.Namespace, execToKeep) assert.NoError(t, err, "Surviving executor should still exist") }) } func TestParseExecutorKey_Errors(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) _, _, err := etcdkeys.ParseExecutorKey(tc.EtcdPrefix, tc.Namespace, "/wrong/prefix/exec/heartbeat") require.Error(t, err) assert.Contains(t, err.Error(), "does not have expected prefix") key := etcdkeys.BuildExecutorsPrefix(tc.EtcdPrefix, tc.Namespace) + "too/many/parts" _, _, err = etcdkeys.ParseExecutorKey(tc.EtcdPrefix, tc.Namespace, key) require.Error(t, err) assert.Contains(t, err.Error(), "unexpected key format") } // TestAssignAndGetShardOwnerRoundtrip verifies the successful assignment and retrieval of a shard owner. func TestAssignAndGetShardOwnerRoundtrip(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc).(*executorStoreImpl) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() now := executorStore.timeSource.Now().UTC() executorID := "executor-roundtrip" shardID := "shard-roundtrip" // Setup: Create an active executor. err := executorStore.RecordHeartbeat(ctx, tc.Namespace, executorID, store.HeartbeatState{Status: types.ExecutorStatusACTIVE}) require.NoError(t, err) // 1. Assign a shard to the active executor. err = executorStore.AssignShard(ctx, tc.Namespace, shardID, executorID) require.NoError(t, err, "Should successfully assign shard to an active executor") // 2. Get the owner and verify it's the correct executor. state, err := executorStore.GetState(ctx, tc.Namespace) require.NoError(t, err) assert.Contains(t, state.ShardAssignments[executorID].AssignedShards, shardID) assert.Equal(t, now, state.ShardAssignments[executorID].LastUpdated) } // TestAssignShardErrors tests the various error conditions when assigning a shard. func TestAssignShardErrors(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() activeExecutorID := "executor-active-errors" drainingExecutorID := "executor-draining-errors" shardID1 := "shard-err-1" shardID2 := "shard-err-2" // Setup: Create an active and a draining executor, and assign one shard. err := executorStore.RecordHeartbeat(ctx, tc.Namespace, activeExecutorID, store.HeartbeatState{Status: types.ExecutorStatusACTIVE}) require.NoError(t, err) err = executorStore.RecordHeartbeat(ctx, tc.Namespace, drainingExecutorID, store.HeartbeatState{Status: types.ExecutorStatusDRAINING}) require.NoError(t, err) err = executorStore.AssignShard(ctx, tc.Namespace, shardID1, activeExecutorID) require.NoError(t, err) // Case 1: Assigning an already-assigned shard. err = executorStore.AssignShard(ctx, tc.Namespace, shardID1, activeExecutorID) require.Error(t, err, "Should fail to assign an already-assigned shard") var alreadyAssigned *store.ErrShardAlreadyAssigned require.ErrorAs(t, err, &alreadyAssigned) assert.Equal(t, shardID1, alreadyAssigned.ShardID) assert.Equal(t, activeExecutorID, alreadyAssigned.AssignedTo) assert.NotNil(t, alreadyAssigned.Metadata) // Case 2: Assigning to a non-existent executor. err = executorStore.AssignShard(ctx, tc.Namespace, shardID2, "non-existent-executor") require.Error(t, err, "Should fail to assign to a non-existent executor") assert.ErrorIs(t, err, store.ErrExecutorNotFound, "Error should be ErrExecutorNotFound") // Case 3: Assigning to a non-active (draining) executor. err = executorStore.AssignShard(ctx, tc.Namespace, shardID2, drainingExecutorID) require.Error(t, err, "Should fail to assign to a draining executor") assert.ErrorIs(t, err, store.ErrVersionConflict, "Error should be ErrVersionConflict for non-active executor") } // TestShardStatisticsPersistence verifies that shard statistics are preserved on assignment // when they already exist, and that GetState exposes them. func TestShardStatisticsPersistence(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) executorStore.(*executorStoreImpl).cfg = &config.Config{ LoadBalancingMode: func(namespace string) string { return config.LoadBalancingModeGREEDY }, MaxEtcdTxnOps: dynamicproperties.GetIntPropertyFn(128), } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() executorID := "exec-stats" shardID := "shard-stats" // 1. Setup: ensure executor is ACTIVE require.NoError(t, executorStore.RecordHeartbeat(ctx, tc.Namespace, executorID, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) // 2. Pre-create shard statistics as if coming from prior history stats := store.ShardStatistics{SmoothedLoad: 12.5, LastUpdateTime: time.Unix(1234, 0).UTC(), LastMoveTime: time.Unix(5678, 0).UTC()} // The stats the executor should have after assignment executorStats := map[string]etcdtypes.ShardStatistics{ shardID: *etcdtypes.FromShardStatistics(&stats), } payload, err := json.Marshal(executorStats) require.NoError(t, err) statsKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, etcdkeys.ExecutorShardStatisticsKey) // Write those stats to etcd under the executor (exec-stats) stats key _, err = tc.Client.Put(ctx, statsKey, string(payload)) require.NoError(t, err) // 3. Assign the shard via AssignShard (should not clobber existing metrics) require.NoError(t, executorStore.AssignShard(ctx, tc.Namespace, shardID, executorID)) // 4. Verify via GetState that metrics are preserved and exposed nsState, err := executorStore.GetState(ctx, tc.Namespace) require.NoError(t, err) require.Contains(t, nsState.ShardStats, shardID) updatedStats := nsState.ShardStats[shardID] assert.Equal(t, stats.SmoothedLoad, updatedStats.SmoothedLoad) assert.Equal(t, stats.LastUpdateTime, updatedStats.LastUpdateTime) // This should be greater than the last move time assert.Greater(t, updatedStats.LastMoveTime, stats.LastMoveTime) // 5. Also ensure assignment recorded correctly require.Contains(t, nsState.ShardAssignments[executorID].AssignedShards, shardID) } // TestGetShardStatisticsForMissingShard verifies GetState does not report statistics for unknown shards. func TestGetShardStatisticsForMissingShard(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // No metrics are written; GetState should not contain unknown shard st, err := executorStore.GetState(ctx, tc.Namespace) require.NoError(t, err) assert.NotContains(t, st.ShardStats, "unknown") } // TestDeleteShardStatsDeletesAllStats verifies that shard statistics are correctly deleted. func TestDeleteShardStatsDeletesAllStats(t *testing.T) { tc := testhelper.SetupStoreTestCluster(t) executorStore := createStore(t, tc) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() totalShardStats := 135 // number of stats to add and make stale shardIDs := make([]string, 0, totalShardStats) executorID := "exec-delete-stats" // ensure executor exists ctxHeartbeat, cancelHb := context.WithTimeout(context.Background(), 10*time.Second) defer cancelHb() require.NoError(t, executorStore.RecordHeartbeat(ctxHeartbeat, tc.Namespace, executorID, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) // Create stale stats executorStats := make(map[string]etcdtypes.ShardStatistics) for i := 0; i < totalShardStats; i++ { shardID := "stale-stats-" + strconv.Itoa(i) shardIDs = append(shardIDs, shardID) stats := store.ShardStatistics{ SmoothedLoad: float64(i), LastUpdateTime: time.Unix(int64(i), 0).UTC(), LastMoveTime: time.Unix(int64(i), 0).UTC(), } executorStats[shardID] = *etcdtypes.FromShardStatistics(&stats) } statsKey := etcdkeys.BuildExecutorKey(tc.EtcdPrefix, tc.Namespace, executorID, etcdkeys.ExecutorShardStatisticsKey) payload, err := json.Marshal(executorStats) require.NoError(t, err) _, err = tc.Client.Put(ctx, statsKey, string(payload)) require.NoError(t, err) require.NoError(t, executorStore.DeleteShardStats(ctx, tc.Namespace, shardIDs, store.NopGuard())) nsState, err := executorStore.GetState(ctx, tc.Namespace) require.NoError(t, err) // All stats should be deleted assert.Empty(t, nsState.ShardStats) } // --- Test Setup --- func stringStatus(s types.ExecutorStatus) string { res, err := json.Marshal(s) if err != nil { panic(err) } return string(res) } func recordHeartbeats(ctx context.Context, t *testing.T, executorStore store.Store, namespace string, executorIDs ...string) { t.Helper() for _, executorID := range executorIDs { require.NoError(t, executorStore.RecordHeartbeat(ctx, namespace, executorID, store.HeartbeatState{Status: types.ExecutorStatusACTIVE})) } } // trackingTxn implements clientv3.Txn to record operations per batch for testing. type trackingTxn struct { opsCount int commitFn func(numOps int) (*clientv3.TxnResponse, error) } func (t *trackingTxn) If(_ ...clientv3.Cmp) clientv3.Txn { return t } func (t *trackingTxn) Else(_ ...clientv3.Op) clientv3.Txn { return t } func (t *trackingTxn) Then(ops ...clientv3.Op) clientv3.Txn { t.opsCount += len(ops); return t } func (t *trackingTxn) Commit() (*clientv3.TxnResponse, error) { return t.commitFn(t.opsCount) } func TestCommitGuardedOps_Batching(t *testing.T) { const testMaxTxnOps = 128 const testMaxOpsPerBatch = testMaxTxnOps - guardOpOverhead tests := []struct { name string numOps int expectedBatches int }{ { name: "ZeroOps", numOps: 0, expectedBatches: 0, }, { name: "SingleOp", numOps: 1, expectedBatches: 1, }, { name: "BelowLimit", numOps: 50, expectedBatches: 1, }, { name: "ExactlyAtLimit", numOps: testMaxOpsPerBatch, expectedBatches: 1, }, { name: "OneOverLimit", numOps: testMaxOpsPerBatch + 1, expectedBatches: 2, }, { name: "ExactlyTwoBatches", numOps: testMaxOpsPerBatch * 2, expectedBatches: 2, }, { name: "MultipleBatches", numOps: testMaxOpsPerBatch*3 + 10, expectedBatches: 4, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockClient := etcdclient.NewMockClient(ctrl) var batchSizes []int ops := make([]clientv3.Op, tt.numOps) for i := range ops { ops[i] = clientv3.OpDelete(fmt.Sprintf("/test/key/%d", i)) } for range tt.expectedBatches { txn := &trackingTxn{ commitFn: func(numOps int) (*clientv3.TxnResponse, error) { batchSizes = append(batchSizes, numOps) return &clientv3.TxnResponse{Succeeded: true}, nil }, } mockClient.EXPECT().Txn(gomock.Any()).Return(txn) } s := &executorStoreImpl{ client: mockClient, cfg: &config.Config{MaxEtcdTxnOps: dynamicproperties.GetIntPropertyFn(testMaxTxnOps)}, } err := s.commitGuardedOps(context.Background(), ops, store.NopGuard()) require.NoError(t, err) require.Len(t, batchSizes, tt.expectedBatches) totalOps := 0 for _, size := range batchSizes { assert.LessOrEqual(t, size, testMaxOpsPerBatch) totalOps += size } assert.Equal(t, tt.numOps, totalOps) }) } } func TestCommitGuardedOps_CommitError_StopsEarly(t *testing.T) { ctrl := gomock.NewController(t) mockClient := etcdclient.NewMockClient(ctrl) commitCount := 0 makeTxn := func(err error) *trackingTxn { return &trackingTxn{ commitFn: func(numOps int) (*clientv3.TxnResponse, error) { commitCount++ if err != nil { return nil, err } return &clientv3.TxnResponse{Succeeded: true}, nil }, } } // Use a small limit so we get multiple batches with fewer ops const testMaxTxnOps = 10 ops := make([]clientv3.Op, testMaxTxnOps+5) for i := range ops { ops[i] = clientv3.OpDelete(fmt.Sprintf("/test/key/%d", i)) } mockClient.EXPECT().Txn(gomock.Any()).Return(makeTxn(fmt.Errorf("etcd unavailable"))) s := &executorStoreImpl{ client: mockClient, cfg: &config.Config{MaxEtcdTxnOps: dynamicproperties.GetIntPropertyFn(testMaxTxnOps)}, } err := s.commitGuardedOps(context.Background(), ops, store.NopGuard()) require.Error(t, err) assert.Contains(t, err.Error(), "commit batch") assert.Equal(t, 1, commitCount, "should stop after first failing batch") } func TestCommitGuardedOps_LeadershipLost_StopsEarly(t *testing.T) { ctrl := gomock.NewController(t) mockClient := etcdclient.NewMockClient(ctrl) commitCount := 0 makeTxn := func(succeeded bool) *trackingTxn { return &trackingTxn{ commitFn: func(numOps int) (*clientv3.TxnResponse, error) { commitCount++ return &clientv3.TxnResponse{Succeeded: succeeded}, nil }, } } const testMaxTxnOps = 10 ops := make([]clientv3.Op, testMaxTxnOps+5) for i := range ops { ops[i] = clientv3.OpDelete(fmt.Sprintf("/test/key/%d", i)) } mockClient.EXPECT().Txn(gomock.Any()).Return(makeTxn(false)) s := &executorStoreImpl{ client: mockClient, cfg: &config.Config{MaxEtcdTxnOps: dynamicproperties.GetIntPropertyFn(testMaxTxnOps)}, } err := s.commitGuardedOps(context.Background(), ops, store.NopGuard()) require.Error(t, err) assert.Contains(t, err.Error(), "leadership may have changed") assert.Equal(t, 1, commitCount, "should stop after first leadership failure") } func TestCommitGuardedOps_GuardError(t *testing.T) { ctrl := gomock.NewController(t) mockClient := etcdclient.NewMockClient(ctrl) txn := &trackingTxn{ commitFn: func(numOps int) (*clientv3.TxnResponse, error) { t.Fatal("should not reach commit") return nil, nil }, } mockClient.EXPECT().Txn(gomock.Any()).Return(txn) failingGuard := func(txn store.Txn) (store.Txn, error) { return nil, fmt.Errorf("guard failure") } ops := []clientv3.Op{clientv3.OpDelete("/test/key")} s := &executorStoreImpl{ client: mockClient, cfg: &config.Config{MaxEtcdTxnOps: dynamicproperties.GetIntPropertyFn(128)}, } err := s.commitGuardedOps(context.Background(), ops, failingGuard) require.Error(t, err) assert.Contains(t, err.Error(), "apply transaction guard") } func createStore(t *testing.T, tc *testhelper.StoreTestCluster) store.Store { t.Helper() etcdConfig, err := NewETCDConfig(tc.LeaderCfg) require.NoError(t, err) store, err := NewStore(ExecutorStoreParams{ Client: tc.Client, ETCDConfig: etcdConfig, Lifecycle: fxtest.NewLifecycle(t), Logger: testlogger.New(t), TimeSource: clock.NewMockedTimeSourceAt(time.Now()), MetricsClient: metrics.NewNoopMetricsClient(), Config: &config.Config{ LoadBalancingMode: func(namespace string) string { return config.LoadBalancingModeNAIVE }, MaxEtcdTxnOps: dynamicproperties.GetIntPropertyFn(128), }, }) require.NoError(t, err) return store } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/executorstore_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: etcdstore.go // // Generated by this command: // // mockgen -package executorstore -source etcdstore.go -destination=executorstore_mock.go ExecutorStore // // Package executorstore is a generated GoMock package. package executorstore ================================================ FILE: service/sharddistributor/store/etcd/executorstore/module.go ================================================ package executorstore import "go.uber.org/fx" var Module = fx.Module("executorstore", fx.Provide(NewStore), fx.Provide(NewClient), fx.Provide(NewETCDConfig), ) ================================================ FILE: service/sharddistributor/store/etcd/executorstore/shardcache/namespaceshardcache.go ================================================ package shardcache import ( "context" "fmt" "strings" "sync" "time" clientv3 "go.etcd.io/etcd/client/v3" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/sharddistributor/store" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdclient" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdkeys" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdtypes" "github.com/uber/cadence/service/sharddistributor/store/etcd/executorstore/common" ) const ( // RetryInterval for watch failures is between 50ms to 150ms namespaceRefreshLoopWatchJitterCoeff = 0.5 namespaceRefreshLoopWatchRetryInterval = 100 * time.Millisecond ) type namespaceShardToExecutor struct { sync.RWMutex shardToExecutor map[string]*store.ShardOwner // shardID -> shardOwner shardOwners map[string]*store.ShardOwner // executorID -> shardOwner executorState map[*store.ShardOwner][]string // executor -> shardIDs executorRevision map[string]int64 namespace string etcdPrefix string stopCh chan struct{} logger log.Logger client etcdclient.Client timeSource clock.TimeSource pubSub *executorStatePubSub metricsClient metrics.Client } func newNamespaceShardToExecutor(etcdPrefix, namespace string, client etcdclient.Client, stopCh chan struct{}, logger log.Logger, timeSource clock.TimeSource, metricsClient metrics.Client) (*namespaceShardToExecutor, error) { return &namespaceShardToExecutor{ shardToExecutor: make(map[string]*store.ShardOwner), executorState: make(map[*store.ShardOwner][]string), executorRevision: make(map[string]int64), shardOwners: make(map[string]*store.ShardOwner), namespace: namespace, etcdPrefix: etcdPrefix, stopCh: stopCh, logger: logger.WithTags(tag.ShardNamespace(namespace)), client: client, timeSource: timeSource, pubSub: newExecutorStatePubSub(logger, namespace), metricsClient: metricsClient, }, nil } func (n *namespaceShardToExecutor) Start(wg *sync.WaitGroup) { wg.Add(1) go func() { defer wg.Done() n.namespaceRefreshLoop() }() } func (n *namespaceShardToExecutor) GetShardOwner(ctx context.Context, shardID string) (*store.ShardOwner, error) { shardOwner, err := n.getShardOwnerInMap(ctx, &n.shardToExecutor, shardID) if err != nil { return nil, fmt.Errorf("get shard owner in map: %w", err) } if shardOwner != nil { return shardOwner, nil } return nil, store.ErrShardNotFound } func (n *namespaceShardToExecutor) GetExecutor(ctx context.Context, executorID string) (*store.ShardOwner, error) { shardOwner, err := n.getShardOwnerInMap(ctx, &n.shardOwners, executorID) if err != nil { return nil, fmt.Errorf("get shard owner in map: %w", err) } if shardOwner != nil { return shardOwner, nil } return nil, store.ErrExecutorNotFound } func (n *namespaceShardToExecutor) GetExecutorModRevisionCmp() ([]clientv3.Cmp, error) { n.RLock() defer n.RUnlock() comparisons := []clientv3.Cmp{} for executor, revision := range n.executorRevision { executorAssignedStateKey := etcdkeys.BuildExecutorKey(n.etcdPrefix, n.namespace, executor, etcdkeys.ExecutorAssignedStateKey) comparisons = append(comparisons, clientv3.Compare(clientv3.ModRevision(executorAssignedStateKey), "=", revision)) } return comparisons, nil } func (n *namespaceShardToExecutor) Subscribe(ctx context.Context) (<-chan map[*store.ShardOwner][]string, func()) { subCh, unSub := n.pubSub.subscribe(ctx) // The go routine sends the initial state to the subscriber. go func() { initialState := n.getExecutorState() select { case <-ctx.Done(): n.logger.Warn("context finished before initial state was sent") case subCh <- initialState: n.logger.Info("initial state sent to subscriber", tag.Value(initialState)) } }() return subCh, unSub } func (n *namespaceShardToExecutor) namespaceRefreshLoop() { triggerCh := n.runWatchLoop() for { select { case <-n.stopCh: n.logger.Info("stop channel closed, exiting namespaceRefreshLoop") return case _, ok := <-triggerCh: if !ok { n.logger.Info("trigger channel closed, exiting namespaceRefreshLoop") return } if err := n.refresh(context.Background()); err != nil { n.logger.Error("failed to refresh namespace shard to executor", tag.Error(err)) } } } } func (n *namespaceShardToExecutor) runWatchLoop() <-chan struct{} { triggerCh := make(chan struct{}, 1) go func() { defer close(triggerCh) for { if err := n.watch(triggerCh); err != nil { n.logger.Error("error watching in namespaceRefreshLoop, retrying...", tag.Error(err)) n.timeSource.Sleep(backoff.JitDuration( namespaceRefreshLoopWatchRetryInterval, namespaceRefreshLoopWatchJitterCoeff, )) continue } n.logger.Info("namespaceRefreshLoop is exiting") return } }() return triggerCh } func (n *namespaceShardToExecutor) watch(triggerCh chan<- struct{}) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() scope := n.metricsClient.Scope(metrics.ShardDistributorWatchScope). Tagged(metrics.NamespaceTag(n.namespace)). Tagged(metrics.ShardDistributorWatchTypeTag("cache_refresh")) watchChan := n.client.Watch( // WithRequireLeader ensures that the etcd cluster has a leader clientv3.WithRequireLeader(ctx), etcdkeys.BuildExecutorsPrefix(n.etcdPrefix, n.namespace), clientv3.WithPrefix(), clientv3.WithPrevKV(), ) for { select { case <-n.stopCh: n.logger.Info("stop channel closed, exiting watch loop") return nil case watchResp, ok := <-watchChan: if err := watchResp.Err(); err != nil { return fmt.Errorf("watch response: %w", err) } if !ok { return fmt.Errorf("watch channel closed") } // Track watch metrics sw := scope.StartTimer(metrics.ShardDistributorWatchProcessingLatency) scope.AddCounter(metrics.ShardDistributorWatchEventsReceived, int64(len(watchResp.Events))) // Only trigger refresh if the change is related to executor assigned state or metadata if !n.hasExecutorStateChanged(watchResp) { sw.Stop() continue } select { case triggerCh <- struct{}{}: default: n.logger.Info("Cache is being refreshed, skipping trigger") } sw.Stop() } } } // hasExecutorStateChanged checks if any of the events in the watch response indicate a change to executor assigned state or metadata, // and if the value actually changed (not just same value written again) func (n *namespaceShardToExecutor) hasExecutorStateChanged(watchResp clientv3.WatchResponse) bool { for _, event := range watchResp.Events { _, keyType, keyErr := etcdkeys.ParseExecutorKey(n.etcdPrefix, n.namespace, string(event.Kv.Key)) if keyErr != nil { n.logger.Warn("Received watch event with unrecognized key format", tag.Value(keyErr)) continue } // Only refresh on changes to assigned state or metadata, ignore other changes under the executor prefix such as executor heartbeat keys if keyType != etcdkeys.ExecutorAssignedStateKey && keyType != etcdkeys.ExecutorMetadataKey { continue } // Check if value actually changed (skip if same value written again) if event.PrevKv != nil && string(event.Kv.Value) == string(event.PrevKv.Value) { continue } return true } return false } func (n *namespaceShardToExecutor) refresh(ctx context.Context) error { err := n.refreshExecutorState(ctx) if err != nil { return fmt.Errorf("refresh executor state: %w", err) } n.pubSub.publish(n.getExecutorState()) return nil } func (n *namespaceShardToExecutor) getExecutorState() map[*store.ShardOwner][]string { n.RLock() defer n.RUnlock() executorState := make(map[*store.ShardOwner][]string) for executor, shardIDs := range n.executorState { executorState[executor] = make([]string, len(shardIDs)) copy(executorState[executor], shardIDs) } return executorState } func (n *namespaceShardToExecutor) refreshExecutorState(ctx context.Context) error { executorPrefix := etcdkeys.BuildExecutorsPrefix(n.etcdPrefix, n.namespace) resp, err := n.client.Get(ctx, executorPrefix, clientv3.WithPrefix()) if err != nil { return fmt.Errorf("get executor prefix for namespace %s: %w", n.namespace, err) } n.Lock() defer n.Unlock() // Clear the cache, so we don't have any stale data n.shardToExecutor = make(map[string]*store.ShardOwner) n.executorState = make(map[*store.ShardOwner][]string) n.executorRevision = make(map[string]int64) n.shardOwners = make(map[string]*store.ShardOwner) for _, kv := range resp.Kvs { executorID, keyType, keyErr := etcdkeys.ParseExecutorKey(n.etcdPrefix, n.namespace, string(kv.Key)) if keyErr != nil { continue } switch keyType { case etcdkeys.ExecutorAssignedStateKey: shardOwner := getOrCreateShardOwner(n.shardOwners, executorID) var assignedState etcdtypes.AssignedState err = common.DecompressAndUnmarshal(kv.Value, &assignedState) if err != nil { return fmt.Errorf("parse assigned state: %w", err) } // Build both shard->executor and executor->shards mappings shardIDs := make([]string, 0, len(assignedState.AssignedShards)) for shardID := range assignedState.AssignedShards { n.shardToExecutor[shardID] = shardOwner shardIDs = append(shardIDs, shardID) n.executorRevision[executorID] = kv.ModRevision } n.executorState[shardOwner] = shardIDs case etcdkeys.ExecutorMetadataKey: shardOwner := getOrCreateShardOwner(n.shardOwners, executorID) metadataKey := strings.TrimPrefix(string(kv.Key), etcdkeys.BuildMetadataKey(n.etcdPrefix, n.namespace, executorID, "")) shardOwner.Metadata[metadataKey] = string(kv.Value) default: continue } } return nil } // getOrCreateShardOwner retrieves an existing ShardOwner from the map or creates a new one if it doesn't exist func getOrCreateShardOwner(shardOwners map[string]*store.ShardOwner, executorID string) *store.ShardOwner { shardOwner, ok := shardOwners[executorID] if !ok { shardOwner = &store.ShardOwner{ ExecutorID: executorID, Metadata: make(map[string]string), } shardOwners[executorID] = shardOwner } return shardOwner } // getShardOwnerInMap retrieves a shard owner from the map if it exists, otherwise it refreshes the cache and tries again // it takes a pointer to the map. When the cache is refreshed, the map is updated, so we need to pass a pointer to the map func (n *namespaceShardToExecutor) getShardOwnerInMap(ctx context.Context, m *map[string]*store.ShardOwner, key string) (*store.ShardOwner, error) { n.RLock() shardOwner, ok := (*m)[key] n.RUnlock() if ok { return shardOwner, nil } // Force refresh the cache err := n.refresh(ctx) if err != nil { return nil, fmt.Errorf("refresh for namespace %s: %w", n.namespace, err) } // Check the cache again after refresh n.RLock() shardOwner, ok = (*m)[key] n.RUnlock() if ok { return shardOwner, nil } return nil, nil } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/shardcache/namespaceshardcache_test.go ================================================ package shardcache import ( "context" "encoding/json" "sync" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.etcd.io/etcd/api/v3/mvccpb" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/store" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdclient" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdkeys" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdtypes" "github.com/uber/cadence/service/sharddistributor/store/etcd/testhelper" ) func TestNamespaceShardToExecutor_Lifecycle(t *testing.T) { testCluster := testhelper.SetupStoreTestCluster(t) logger := testlogger.New(t) stopCh := make(chan struct{}) defer close(stopCh) // Setup: Create executor-1 with shard-1 setupExecutorWithShards(t, testCluster, "executor-1", []string{"shard-1"}, map[string]string{ "hostname": "executor-1-host", "version": "v1.0.0", }) // Start the cache namespaceShardToExecutor, err := newNamespaceShardToExecutor(testCluster.EtcdPrefix, testCluster.Namespace, testCluster.Client, stopCh, logger, clock.NewRealTimeSource(), metrics.NewNoopMetricsClient()) assert.NoError(t, err) namespaceShardToExecutor.Start(&sync.WaitGroup{}) time.Sleep(50 * time.Millisecond) // Verify executor-1 owns shard-1 with correct metadata verifyShardOwner(t, namespaceShardToExecutor, "shard-1", "executor-1", map[string]string{ "hostname": "executor-1-host", "version": "v1.0.0", }) // Check the cache is populated namespaceShardToExecutor.RLock() _, ok := namespaceShardToExecutor.executorRevision["executor-1"] assert.True(t, ok) assert.Equal(t, "executor-1", namespaceShardToExecutor.shardToExecutor["shard-1"].ExecutorID) namespaceShardToExecutor.RUnlock() // Add executor-2 with shard-2 to trigger watch update setupExecutorWithShards(t, testCluster, "executor-2", []string{"shard-2"}, map[string]string{ "hostname": "executor-2-host", "region": "us-west", }) time.Sleep(100 * time.Millisecond) // Check that executor-2 and shard-2 is in the cache namespaceShardToExecutor.RLock() _, ok = namespaceShardToExecutor.executorRevision["executor-2"] assert.True(t, ok) assert.Equal(t, "executor-2", namespaceShardToExecutor.shardToExecutor["shard-2"].ExecutorID) namespaceShardToExecutor.RUnlock() // Verify executor-2 owns shard-2 with correct metadata verifyShardOwner(t, namespaceShardToExecutor, "shard-2", "executor-2", map[string]string{ "hostname": "executor-2-host", "region": "us-west", }) } func TestNamespaceShardToExecutor_Subscribe(t *testing.T) { testCluster := testhelper.SetupStoreTestCluster(t) logger := testlogger.New(t) stopCh := make(chan struct{}) defer close(stopCh) // Setup: Create executor-1 with shard-1 setupExecutorWithShards(t, testCluster, "executor-1", []string{"shard-1"}, map[string]string{ "hostname": "executor-1-host", "version": "v1.0.0", }) // Start the cache namespaceShardToExecutor, err := newNamespaceShardToExecutor(testCluster.EtcdPrefix, testCluster.Namespace, testCluster.Client, stopCh, logger, clock.NewRealTimeSource(), metrics.NewNoopMetricsClient()) assert.NoError(t, err) namespaceShardToExecutor.Start(&sync.WaitGroup{}) // Refresh the cache to get the initial state err = namespaceShardToExecutor.refresh(context.Background()) require.NoError(t, err) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() subCh, unSub := namespaceShardToExecutor.Subscribe(ctx) defer unSub() var wg sync.WaitGroup wg.Add(1) // start listener go func() { defer wg.Done() // Check that we get the initial state state := <-subCh assert.Len(t, state, 1) verifyExecutorInState(t, state, "executor-1", []string{"shard-1"}, map[string]string{ "hostname": "executor-1-host", "version": "v1.0.0", }) // Check that we get the updated state state = <-subCh assert.Len(t, state, 2) verifyExecutorInState(t, state, "executor-1", []string{"shard-1"}, map[string]string{ "hostname": "executor-1-host", "version": "v1.0.0", }) verifyExecutorInState(t, state, "executor-2", []string{"shard-2"}, map[string]string{ "hostname": "executor-2-host", "region": "us-west", }) }() time.Sleep(10 * time.Millisecond) // Add executor-2 with shard-2 to trigger new subscription update setupExecutorWithShards(t, testCluster, "executor-2", []string{"shard-2"}, map[string]string{ "hostname": "executor-2-host", "region": "us-west", }) wg.Wait() } func TestNamespaceShardToExecutor_watch_watchChanErrors(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() logger := testlogger.New(t) mockClient := etcdclient.NewMockClient(ctrl) stopCh := make(chan struct{}) testPrefix := "/test-prefix" testNamespace := "test-namespace" // Mock the Watch call to return our watch channel watchChan := make(chan clientv3.WatchResponse) mockClient.EXPECT(). Watch(gomock.Any(), gomock.Any(), gomock.Any()). Return(watchChan). AnyTimes() e, err := newNamespaceShardToExecutor(testPrefix, testNamespace, mockClient, stopCh, logger, clock.NewRealTimeSource(), metrics.NewNoopMetricsClient()) require.NoError(t, err) triggerChan := make(chan struct{}, 1) // Test Case #1 // Test received compact revision error from watch channel { go func() { watchChan <- clientv3.WatchResponse{ CompactRevision: 100, } }() err = e.watch(triggerChan) require.Error(t, err) assert.ErrorContains(t, err, "etcdserver: mvcc: required revision has been compacted") } // Test Case #2 // Test closed watch channel { close(watchChan) err = e.watch(triggerChan) require.Error(t, err) assert.ErrorContains(t, err, "watch channel closed") } } func TestNamespaceShardToExecutor_watch_triggerChBlocking(t *testing.T) { tc := setupNamespaceShardToExecutorTestCase(t) defer tc.ctrl.Finish() defer goleak.VerifyNone(t) // Create a triggerCh with buffer size 1, but never read from it triggerChan := make(chan struct{}, 1) executorKey := etcdkeys.BuildExecutorKey(tc.prefix, tc.namespace, tc.executorID, etcdkeys.ExecutorAssignedStateKey) // Start watch in a goroutine watchDone := make(chan error, 1) go func() { watchDone <- tc.e.watch(triggerChan) }() // Send many events - the loop should not block even though triggerCh is full for i := 0; i < 100; i++ { select { case tc.watchChan <- clientv3.WatchResponse{ Events: []*clientv3.Event{ { Type: clientv3.EventTypePut, Kv: &mvccpb.KeyValue{ Key: []byte(executorKey), }, }, }, }: case <-time.After(100 * time.Millisecond): t.Fatal("watch loop is stuck - could not send event to watchChan") } } // Close stopCh to exit the watch loop close(tc.stopCh) select { case err := <-watchDone: assert.NoError(t, err) case <-time.After(1 * time.Second): t.Fatal("watch loop did not exit after stopCh was closed") } } func TestNamespaceShardToExecutor_namespaceRefreshLoop_notTriggersRefresh_reportedShards(t *testing.T) { tc := setupNamespaceShardToExecutorTestCase(t) defer tc.ctrl.Finish() defer goleak.VerifyNone(t) wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() tc.e.namespaceRefreshLoop() }() key := etcdkeys.BuildExecutorKey( tc.prefix, tc.namespace, tc.executorID, etcdkeys.ExecutorReportedShardsKey, ) tc.watchChan <- clientv3.WatchResponse{ Events: []*clientv3.Event{ { Type: clientv3.EventTypePut, Kv: &mvccpb.KeyValue{ Key: []byte(key), }, }, }, } // mock for refresh should not be called, so no need to set expectation on etcdClient.EXPECT().Get() // use Never with condition that checks shardOwners is still empty to verify that refresh is not triggered require.Neverf(t, func() bool { tc.e.RLock() defer tc.e.RUnlock() return len(tc.e.shardOwners) > 0 }, 100*time.Millisecond, 1*time.Millisecond, "expected no refresh to be triggered for reported shards change") // Close stopCh to exit the loop close(tc.stopCh) wg.Wait() } func TestNamespaceShardToExecutor_namespaceRefreshLoop_notTriggersRefresh_noUpdates(t *testing.T) { tc := setupNamespaceShardToExecutorTestCase(t) defer tc.ctrl.Finish() defer goleak.VerifyNone(t) wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() tc.e.namespaceRefreshLoop() }() metadataValue := "metadata-value" metadataKey := "metadata-key" key := etcdkeys.BuildMetadataKey( tc.prefix, tc.namespace, tc.executorID, metadataKey, ) tc.watchChan <- clientv3.WatchResponse{ Events: []*clientv3.Event{ { Type: clientv3.EventTypePut, Kv: &mvccpb.KeyValue{ Key: []byte(key), Value: []byte(metadataValue), }, PrevKv: &mvccpb.KeyValue{ Key: []byte(key), Value: []byte(metadataValue), }, }, }, } // mock for refresh should not be called, so no need to set expectation on etcdClient.EXPECT().Get() // use Never with condition that checks shardOwners is still empty to verify that refresh is not triggered require.Neverf(t, func() bool { tc.e.RLock() defer tc.e.RUnlock() return len(tc.e.shardOwners) > 0 }, 100*time.Millisecond, 1*time.Millisecond, "expected no refresh to be triggered for the same metadata value") // Close stopCh to exit the loop close(tc.stopCh) wg.Wait() } func TestNamespaceShardToExecutor_namespaceRefreshLoop_triggersRefresh(t *testing.T) { tc := setupNamespaceShardToExecutorTestCase(t) defer tc.ctrl.Finish() defer goleak.VerifyNone(t) wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() tc.e.namespaceRefreshLoop() }() metadataValue := "metadata-value" metadataKey := "metadata-key" key := etcdkeys.BuildMetadataKey( tc.prefix, tc.namespace, tc.executorID, metadataKey, ) // Mock Get call for refresh tc.etcdClient.EXPECT(). Get(gomock.Any(), tc.executorPrefix, gomock.Any()). Return( &clientv3.GetResponse{Kvs: []*mvccpb.KeyValue{ { Key: []byte(key), Value: []byte(metadataValue), }, }}, nil, ) // Send a watch event for metadata change which should trigger the refresh tc.watchChan <- clientv3.WatchResponse{ Events: []*clientv3.Event{ { Type: clientv3.EventTypePut, Kv: &mvccpb.KeyValue{ Key: []byte(key), Value: []byte(metadataValue), }, PrevKv: &mvccpb.KeyValue{ Key: []byte(key), Value: []byte("previous value"), }, }, }, } // Wait for the refresh to be triggered and the shard owner to be updated with the new metadata value require.Eventually(t, func() bool { tc.e.RLock() defer tc.e.RUnlock() shardOwner, ok := tc.e.shardOwners[tc.executorID] if !ok { return false } return shardOwner.Metadata[metadataKey] == metadataValue }, time.Second, 1*time.Millisecond, "expected metadata value to be updated in shard owner after refresh") // Close stopCh to exit the loop close(tc.stopCh) wg.Wait() } func TestNamespaceShardToExecutor_namespaceRefreshLoop_watchError(t *testing.T) { defer goleak.VerifyNone(t) ctrl := gomock.NewController(t) defer ctrl.Finish() logger := testlogger.New(t) mockClient := etcdclient.NewMockClient(ctrl) timeSource := clock.NewMockedTimeSource() stopCh := make(chan struct{}) testPrefix := "/test-prefix" testNamespace := "test-namespace" // mock for first watch call that receives error watchChanRcvErr := make(chan clientv3.WatchResponse) mockClient.EXPECT(). Watch(gomock.Any(), gomock.Any(), gomock.Any()). Return(watchChanRcvErr) // mock for second watch call that receives closed channel watchChanClosed := make(chan clientv3.WatchResponse) mockClient.EXPECT(). Watch(gomock.Any(), gomock.Any(), gomock.Any()). Return(watchChanClosed) // mock for third watch call that will be used when stopCh is closed // maybe called or not if stopCh is closed before retry interval mockClient.EXPECT(). Watch(gomock.Any(), gomock.Any(), gomock.Any()). Return(make(chan clientv3.WatchResponse)). MinTimes(0). MaxTimes(1) e, err := newNamespaceShardToExecutor(testPrefix, testNamespace, mockClient, stopCh, logger, timeSource, metrics.NewNoopMetricsClient()) require.NoError(t, err) wg := sync.WaitGroup{} wg.Add(1) finished := atomic.Bool{} go func() { defer wg.Done() e.namespaceRefreshLoop() finished.Store(true) }() // Test Case #1: watchChan receives error { // Sends a response containing compact revision to simulate error watchChanRcvErr <- clientv3.WatchResponse{ CompactRevision: 100, } timeSource.BlockUntil(1) require.False(t, finished.Load(), "namespaceRefreshLoop should not exit on watch error") } // Test Case #2: watchChan is closed { timeSource.Advance(2 * namespaceRefreshLoopWatchRetryInterval) // Sends a response containing compact revision to simulate error close(watchChanClosed) timeSource.BlockUntil(1) require.False(t, finished.Load(), "namespaceRefreshLoop should not exit on watch error") } // Test Case #3: stopCh is closed { timeSource.Advance(2 * namespaceRefreshLoopWatchRetryInterval) close(stopCh) wg.Wait() require.True(t, finished.Load(), "namespaceRefreshLoop should exit on watch error") } } // setupExecutorWithShards creates an executor in etcd with assigned shards and metadata func setupExecutorWithShards(t *testing.T, testCluster *testhelper.StoreTestCluster, executorID string, shards []string, metadata map[string]string) { // Create assigned state assignedState := &etcdtypes.AssignedState{ AssignedShards: make(map[string]*types.ShardAssignment), } for _, shardID := range shards { assignedState.AssignedShards[shardID] = &types.ShardAssignment{Status: types.AssignmentStatusREADY} } assignedStateJSON, err := json.Marshal(assignedState) require.NoError(t, err) var operations []clientv3.Op executorAssignedStateKey := etcdkeys.BuildExecutorKey(testCluster.EtcdPrefix, testCluster.Namespace, executorID, etcdkeys.ExecutorAssignedStateKey) operations = append(operations, clientv3.OpPut(executorAssignedStateKey, string(assignedStateJSON))) // Add metadata for key, value := range metadata { metadataKey := etcdkeys.BuildMetadataKey(testCluster.EtcdPrefix, testCluster.Namespace, executorID, key) operations = append(operations, clientv3.OpPut(metadataKey, value)) } txnResp, err := testCluster.Client.Txn(context.Background()).Then(operations...).Commit() require.NoError(t, err) require.True(t, txnResp.Succeeded) } func verifyExecutorInState(t *testing.T, state map[*store.ShardOwner][]string, executorID string, shards []string, metadata map[string]string) { executorInState := false for executor, executorShards := range state { if executor.ExecutorID == executorID { assert.Equal(t, shards, executorShards) assert.Equal(t, metadata, executor.Metadata) executorInState = true break } } assert.True(t, executorInState) } // verifyShardOwner checks that a shard has the expected owner and metadata func verifyShardOwner(t *testing.T, cache *namespaceShardToExecutor, shardID, expectedExecutorID string, expectedMetadata map[string]string) { owner, err := cache.GetShardOwner(context.Background(), shardID) require.NoError(t, err) require.NotNil(t, owner) assert.Equal(t, expectedExecutorID, owner.ExecutorID) for key, expectedValue := range expectedMetadata { assert.Equal(t, expectedValue, owner.Metadata[key]) } executor, err := cache.GetExecutor(context.Background(), expectedExecutorID) require.NoError(t, err) require.NotNil(t, executor) assert.Equal(t, expectedExecutorID, executor.ExecutorID) for key, expectedValue := range expectedMetadata { assert.Equal(t, expectedValue, executor.Metadata[key]) } } type namespaceShardToExecutorTestCase struct { ctrl *gomock.Controller e *namespaceShardToExecutor etcdClient *etcdclient.MockClient timeSource clock.TimeSource watchChan chan clientv3.WatchResponse stopCh chan struct{} executorID string prefix string namespace string executorPrefix string } func setupNamespaceShardToExecutorTestCase(t *testing.T) *namespaceShardToExecutorTestCase { var tc = new(namespaceShardToExecutorTestCase) tc.ctrl = gomock.NewController(t) logger := testlogger.New(t) tc.etcdClient = etcdclient.NewMockClient(tc.ctrl) tc.stopCh = make(chan struct{}) tc.prefix = "/test-prefix" tc.namespace = "test-namespace" tc.executorID = "executor-1" tc.executorPrefix = etcdkeys.BuildExecutorsPrefix(tc.prefix, tc.namespace) // Mock the Watch call to return our watch channel tc.watchChan = make(chan clientv3.WatchResponse) tc.etcdClient.EXPECT(). Watch(gomock.Any(), gomock.Any(), gomock.Any()). Return(tc.watchChan). AnyTimes() e, err := newNamespaceShardToExecutor(tc.prefix, tc.namespace, tc.etcdClient, tc.stopCh, logger, clock.NewRealTimeSource(), metrics.NewNoopMetricsClient()) require.NoError(t, err) tc.e = e return tc } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/shardcache/pubsub.go ================================================ package shardcache import ( "context" "sync" "github.com/google/uuid" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/service/sharddistributor/store" ) // executorStatePubSub manages subscriptions to executor state changes type executorStatePubSub struct { mu sync.RWMutex subscribers map[string]chan<- map[*store.ShardOwner][]string logger log.Logger namespace string } func newExecutorStatePubSub(logger log.Logger, namespace string) *executorStatePubSub { return &executorStatePubSub{ subscribers: make(map[string]chan<- map[*store.ShardOwner][]string), logger: logger, namespace: namespace, } } // Subscribe returns a channel that receives executor state updates. func (p *executorStatePubSub) subscribe(ctx context.Context) (chan map[*store.ShardOwner][]string, func()) { ch := make(chan map[*store.ShardOwner][]string) uniqueID := uuid.New().String() p.mu.Lock() defer p.mu.Unlock() p.subscribers[uniqueID] = ch unSub := func() { p.unSubscribe(uniqueID) } return ch, unSub } func (p *executorStatePubSub) unSubscribe(uniqueID string) { p.mu.Lock() defer p.mu.Unlock() delete(p.subscribers, uniqueID) } // Publish sends the state to all subscribers (non-blocking) func (p *executorStatePubSub) publish(state map[*store.ShardOwner][]string) { p.mu.RLock() defer p.mu.RUnlock() for _, sub := range p.subscribers { select { case sub <- state: default: // Subscriber is not reading fast enough, skip this update p.logger.Warn("Subscriber not keeping up with state updates, dropping update", tag.ShardNamespace(p.namespace)) } } } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/shardcache/pubsub_test.go ================================================ package shardcache import ( "context" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/service/sharddistributor/store" ) func TestExecutorStatePubSub_SubscribeUnsubscribe(t *testing.T) { defer goleak.VerifyNone(t) pubsub := newExecutorStatePubSub(testlogger.New(t), "test-ns") ch, unsub := pubsub.subscribe(context.Background()) assert.NotNil(t, ch) assert.Len(t, pubsub.subscribers, 1) unsub() assert.Len(t, pubsub.subscribers, 0) // Unsubscribe is idempotent unsub() assert.Len(t, pubsub.subscribers, 0) } func TestExecutorStatePubSub_Publish(t *testing.T) { defer goleak.VerifyNone(t) t.Run("no subscribers doesn't panic", func(t *testing.T) { pubsub := newExecutorStatePubSub(testlogger.New(t), "test-ns") require.NotPanics(t, func() { pubsub.publish(map[*store.ShardOwner][]string{}) }) }) t.Run("multiple subscribers receive updates", func(t *testing.T) { pubsub := newExecutorStatePubSub(testlogger.New(t), "test-ns") ch1, unsub1 := pubsub.subscribe(context.Background()) ch2, unsub2 := pubsub.subscribe(context.Background()) defer unsub1() defer unsub2() testState := map[*store.ShardOwner][]string{ {ExecutorID: "exec-1", Metadata: map[string]string{}}: {"shard-1"}, } var wg sync.WaitGroup wg.Add(2) go func() { state := <-ch1 assert.Equal(t, testState, state) wg.Done() }() go func() { state := <-ch2 assert.Equal(t, testState, state) wg.Done() }() time.Sleep(10 * time.Millisecond) pubsub.publish(testState) wg.Wait() }) t.Run("non-blocking publish to slow consumer", func(t *testing.T) { pubsub := newExecutorStatePubSub(testlogger.New(t), "test-ns") // We create a subscriber that doesn't read from the channel, this should still not block _, slowUnsub := pubsub.subscribe(context.Background()) defer slowUnsub() testState := map[*store.ShardOwner][]string{ {ExecutorID: "exec-1", Metadata: map[string]string{}}: {"shard-1"}, } // We do not read from the slow channel, this should still not block for range 10 { pubsub.publish(testState) } }) } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/shardcache/shardcache.go ================================================ package shardcache import ( "context" "fmt" "sync" clientv3 "go.etcd.io/etcd/client/v3" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/sharddistributor/store" "github.com/uber/cadence/service/sharddistributor/store/etcd/etcdclient" ) type NamespaceToShards map[string]*namespaceShardToExecutor type ShardToExecutorCache struct { sync.RWMutex namespaceToShards NamespaceToShards timeSource clock.TimeSource client etcdclient.Client stopC chan struct{} logger log.Logger prefix string wg sync.WaitGroup metricsClient metrics.Client } func NewShardToExecutorCache( prefix string, client etcdclient.Client, logger log.Logger, timeSource clock.TimeSource, metricsClient metrics.Client, ) *ShardToExecutorCache { shardCache := &ShardToExecutorCache{ namespaceToShards: make(NamespaceToShards), timeSource: timeSource, stopC: make(chan struct{}), logger: logger, prefix: prefix, client: client, wg: sync.WaitGroup{}, metricsClient: metricsClient, } return shardCache } func (s *ShardToExecutorCache) Start() {} func (s *ShardToExecutorCache) Stop() { close(s.stopC) s.wg.Wait() } func (s *ShardToExecutorCache) GetShardOwner(ctx context.Context, namespace, shardID string) (*store.ShardOwner, error) { namespaceShardToExecutor, err := s.getNamespaceShardToExecutor(namespace) if err != nil { return nil, fmt.Errorf("get namespace shard to executor: %w", err) } return namespaceShardToExecutor.GetShardOwner(ctx, shardID) } func (s *ShardToExecutorCache) GetExecutor(ctx context.Context, namespace, executorID string) (*store.ShardOwner, error) { namespaceShardToExecutor, err := s.getNamespaceShardToExecutor(namespace) if err != nil { return nil, fmt.Errorf("get namespace shard to executor: %w", err) } return namespaceShardToExecutor.GetExecutor(ctx, executorID) } func (s *ShardToExecutorCache) GetExecutorModRevisionCmp(namespace string) ([]clientv3.Cmp, error) { namespaceShardToExecutor, err := s.getNamespaceShardToExecutor(namespace) if err != nil { return nil, fmt.Errorf("get namespace shard to executor: %w", err) } return namespaceShardToExecutor.GetExecutorModRevisionCmp() } func (s *ShardToExecutorCache) Subscribe(ctx context.Context, namespace string) (<-chan map[*store.ShardOwner][]string, func(), error) { namespaceShardToExecutor, err := s.getNamespaceShardToExecutor(namespace) if err != nil { return nil, nil, fmt.Errorf("get namespace shard to executor: %w", err) } ch, unSub := namespaceShardToExecutor.Subscribe(ctx) return ch, unSub, nil } func (s *ShardToExecutorCache) getNamespaceShardToExecutor(namespace string) (*namespaceShardToExecutor, error) { s.RLock() namespaceShardToExecutor, ok := s.namespaceToShards[namespace] s.RUnlock() if ok { return namespaceShardToExecutor, nil } s.Lock() defer s.Unlock() namespaceShardToExecutor, err := newNamespaceShardToExecutor(s.prefix, namespace, s.client, s.stopC, s.logger, s.timeSource, s.metricsClient) if err != nil { return nil, fmt.Errorf("new namespace shard to executor: %w", err) } namespaceShardToExecutor.Start(&s.wg) s.namespaceToShards[namespace] = namespaceShardToExecutor return namespaceShardToExecutor, nil } ================================================ FILE: service/sharddistributor/store/etcd/executorstore/shardcache/shardcache_test.go ================================================ package shardcache import ( "context" "testing" "github.com/stretchr/testify/assert" clientv3 "go.etcd.io/etcd/client/v3" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/sharddistributor/store/etcd/testhelper" ) func TestNewShardToExecutorCache(t *testing.T) { logger := testlogger.New(t) client := &clientv3.Client{} cache := NewShardToExecutorCache("some-prefix", client, logger, clock.NewRealTimeSource(), metrics.NewNoopMetricsClient()) assert.NotNil(t, cache) assert.NotNil(t, cache.namespaceToShards) assert.NotNil(t, cache.stopC) assert.Equal(t, logger, cache.logger) assert.Equal(t, "some-prefix", cache.prefix) assert.Equal(t, client, cache.client) } func TestShardExecutorCacheForwarding(t *testing.T) { testCluster := testhelper.SetupStoreTestCluster(t) logger := testlogger.New(t) // Setup: Create executor-1 with shard-1 and metadata setupExecutorWithShards(t, testCluster, "executor-1", []string{"shard-1"}, map[string]string{ "datacenter": "dc1", "rack": "rack-42", }) cache := NewShardToExecutorCache(testCluster.EtcdPrefix, testCluster.Client, logger, clock.NewRealTimeSource(), metrics.NewNoopMetricsClient()) cache.Start() defer cache.Stop() // This will read the namespace from the store as the cache is empty owner, err := cache.GetShardOwner(context.Background(), testCluster.Namespace, "shard-1") assert.NoError(t, err) assert.Equal(t, "executor-1", owner.ExecutorID) assert.Equal(t, "dc1", owner.Metadata["datacenter"]) assert.Equal(t, "rack-42", owner.Metadata["rack"]) // Check the cache is populated assert.Greater(t, cache.namespaceToShards[testCluster.Namespace].executorRevision["executor-1"], int64(0)) assert.Equal(t, "executor-1", cache.namespaceToShards[testCluster.Namespace].shardToExecutor["shard-1"].ExecutorID) // Check the executor is also cached executor, err := cache.GetExecutor(context.Background(), testCluster.Namespace, "executor-1") assert.NoError(t, err) assert.Equal(t, "executor-1", executor.ExecutorID) assert.Equal(t, "dc1", executor.Metadata["datacenter"]) assert.Equal(t, "rack-42", executor.Metadata["rack"]) } ================================================ FILE: service/sharddistributor/store/etcd/leaderstore/etcdleaderstore.go ================================================ package leaderstore import ( "context" "fmt" "time" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/concurrency" "go.uber.org/fx" "github.com/uber/cadence/common/log" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store" ) const ( // defaultElectionTTL is the default time-to-live for an election session. // If the leader does not renew its lease within this time, it will lose leadership. defaultElectionTTL = 10 * time.Second ) type LeaderStore struct { client *clientv3.Client electionConfig etcdCfg } type etcdCfg struct { Endpoints []string `yaml:"endpoints"` DialTimeout time.Duration `yaml:"dialTimeout"` Prefix string `yaml:"prefix"` ElectionTTL time.Duration `yaml:"electionTTL"` } // StoreParams defines the dependencies for the etcd store, for use with fx. type StoreParams struct { fx.In Client *clientv3.Client `optional:"true"` Cfg config.ShardDistribution Lifecycle fx.Lifecycle Logger log.Logger } // NewLeaderStore creates a new leaderstore backed by ETCD. func NewLeaderStore(p StoreParams) (store.Elector, error) { var err error var out etcdCfg if err := p.Cfg.LeaderStore.StorageParams.Decode(&out); err != nil { return nil, fmt.Errorf("bad config: %w", err) } etcdClient := p.Client if etcdClient == nil { etcdClient, err = clientv3.New(clientv3.Config{ Endpoints: out.Endpoints, DialTimeout: out.DialTimeout, }) if err != nil { return nil, err } } if out.ElectionTTL == 0 { out.ElectionTTL = defaultElectionTTL } p.Lifecycle.Append(fx.StopHook(etcdClient.Close)) return &LeaderStore{ client: etcdClient, electionConfig: out, }, nil } func (ls *LeaderStore) CreateElection(ctx context.Context, namespace string) (el store.Election, err error) { // Create a new session for election session, err := concurrency.NewSession(ls.client, concurrency.WithTTL(int(ls.electionConfig.ElectionTTL.Seconds())), concurrency.WithContext(ctx)) if err != nil { return nil, fmt.Errorf("failed to create session: %w", err) } namespacePrefix := fmt.Sprintf("%s/%s", ls.electionConfig.Prefix, namespace) electionKey := fmt.Sprintf("%s/leader", namespacePrefix) etcdElection := concurrency.NewElection(session, electionKey) return &election{election: etcdElection, session: session, prefix: namespacePrefix}, nil } // election is a wrapper around etcd.concurrency.Election to abstract implementation from etcd types. type election struct { session *concurrency.Session election *concurrency.Election prefix string } func (e *election) Resign(ctx context.Context) error { return e.election.Resign(ctx) } func (e *election) Cleanup(ctx context.Context) error { err := e.session.Close() if err != nil { return fmt.Errorf("close session: %w", err) } return nil } func (e *election) Campaign(ctx context.Context, host string) error { return e.election.Campaign(ctx, host) } func (e *election) Done() <-chan struct{} { return e.session.Done() } func (e *election) Guard() store.GuardFunc { return func(txn store.Txn) (store.Txn, error) { // The guard receives the generic Txn and asserts it to the concrete type it expects. etcdTxn, ok := txn.(clientv3.Txn) if !ok { return nil, fmt.Errorf("invalid transaction type for etcd guard: expected clientv3.Txn, got %T", txn) } // It applies the etcd-specific condition and returns the modified generic Txn. return etcdTxn.If(clientv3.Compare(clientv3.ModRevision(e.election.Key()), "=", e.election.Rev())), nil } } ================================================ FILE: service/sharddistributor/store/etcd/leaderstore/etcdleaderstore_test.go ================================================ package leaderstore import ( "context" "flag" "fmt" "os" "strings" "testing" "time" "github.com/stretchr/testify/require" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/fx/fxtest" "gopkg.in/yaml.v2" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/service/sharddistributor/store" "github.com/uber/cadence/testflags" ) // TestCreateElection tests that an election can be created successfully func TestCreateElection(t *testing.T) { tc := setupETCDCluster(t) timeout := 5 * time.Second ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() namespace := "test-namespace" elect, err := tc.store.CreateElection(ctx, namespace) require.NoError(t, err) require.NotNil(t, elect) // Clean up err = elect.Cleanup(ctx) require.NoError(t, err) } // TestCampaign tests that a node can campaign for leadership func TestCampaign(t *testing.T) { tc := setupETCDCluster(t) // Use a separate context for test operations ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() namespace := "test-namespace-campaign" election, err := tc.store.CreateElection(ctx, namespace) require.NoError(t, err) defer election.Cleanup(ctx) // Start campaigning for leadership host := "test-host-1" err = election.Campaign(ctx, host) require.NoError(t, err) // Verify leadership was obtained by checking the key in etcd // First create a client to connect to etcd client, err := clientv3.New(clientv3.Config{ Endpoints: tc.endpoints, DialTimeout: 5 * time.Second, }) require.NoError(t, err) defer client.Close() // Get the key and verify it exists key := fmt.Sprintf("%s/%s/leader", tc.storeConfig.Prefix, namespace) resp, err := client.Get(ctx, key) require.NoError(t, err) require.NotEqual(t, 0, resp.Count, "Leader key should exist") } // TestResign tests resigning leadership func TestResign(t *testing.T) { tc := setupETCDCluster(t) // Use a separate context for test operations ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() namespace := "test-namespace-resign" election, err := tc.store.CreateElection(ctx, namespace) require.NoError(t, err) defer election.Cleanup(ctx) // Start campaigning for leadership host := "test-host-1" err = election.Campaign(ctx, host) require.NoError(t, err) // Resign the leadership err = election.Resign(ctx) require.NoError(t, err) // Verify leadership was resigned by checking that someone else can become leader election2, err := tc.store.CreateElection(ctx, namespace) require.NoError(t, err) defer election2.Cleanup(ctx) err = election2.Campaign(ctx, "host-2") require.NoError(t, err, "Second host should be able to become leader after first resigned") } // TestMultipleNodes tests multiple nodes competing for leadership func TestMultipleNodes(t *testing.T) { tc := setupETCDCluster(t) // Use a separate context for test operations ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() namespace := "test-namespace-multiple" // Create first election election1, err := tc.store.CreateElection(ctx, namespace) require.NoError(t, err) defer election1.Cleanup(ctx) // Create second election election2, err := tc.store.CreateElection(ctx, namespace) require.NoError(t, err) defer election2.Cleanup(ctx) // First node campaigns err = election1.Campaign(ctx, "host1") require.NoError(t, err) // Second node campaigns - this should block as first node already has leadership // We'll use a channel to track when it's done and a shorter context to timeout campaignDone := make(chan struct{}) campaignErr := make(chan error, 1) ctxTimeout, cancelTimeout := context.WithTimeout(ctx, 1*time.Second) defer cancelTimeout() go func() { err := election2.Campaign(ctxTimeout, "host2") if err != nil { campaignErr <- err } close(campaignDone) }() // Verify second node is blocked (should timeout) select { case err := <-campaignErr: // Expected to get a timeout error require.Error(t, err, "Expected a timeout error for the second campaign") require.Contains(t, err.Error(), "context deadline exceeded", "Expected a context deadline error") case <-campaignDone: t.Error("Second node should not have been able to become leader while first node holds leadership") case <-time.After(2 * time.Second): t.Error("Expected the second campaign to timeout quickly") } // First node resigns err = election1.Resign(ctx) require.NoError(t, err) // Now the second node should be able to become leader // Create a new election for the second host election3, err := tc.store.CreateElection(ctx, namespace) require.NoError(t, err) defer election3.Cleanup(ctx) err = election3.Campaign(ctx, "host3") require.NoError(t, err, "Third host should be able to become leader after first host resigned") } // TestSessionDone tests the Done channel behavior func TestSessionDone(t *testing.T) { tc := setupETCDCluster(t) // Use a separate context for test operations ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() namespace := "test-namespace-done" election, err := tc.store.CreateElection(ctx, namespace) require.NoError(t, err) doneCh := election.Done() require.NotNil(t, doneCh) // Clean up should close the session, which should close the Done channel err = election.Cleanup(ctx) require.NoError(t, err) // Verify the Done channel is closed select { case <-doneCh: // Expected - channel should be closed case <-time.After(2 * time.Second): t.Error("Done channel should be closed after Cleanup") } } // testCluster represents a test etcd cluster with its resources type testCluster struct { store store.Elector storeConfig etcdCfg endpoints []string } // setupETCDCluster initializes an etcd cluster for testing func setupETCDCluster(t *testing.T) *testCluster { t.Helper() flag.Parse() testflags.RequireEtcd(t) endpoints := strings.Split(os.Getenv("ETCD_ENDPOINTS"), ",") if endpoints == nil || len(endpoints) == 0 || endpoints[0] == "" { // default etcd port endpoints = []string{"localhost:2379"} } t.Logf("ETCD endpoints: %v", endpoints) testConfig := etcdCfg{ Endpoints: endpoints, DialTimeout: 5 * time.Second, Prefix: fmt.Sprintf("/election/%s", t.Name()), ElectionTTL: 5 * time.Second, } // Create store storeParams := StoreParams{ Cfg: config.ShardDistribution{LeaderStore: config.Store{StorageParams: createConfig(t, testConfig)}}, Lifecycle: fxtest.NewLifecycle(t), } store, err := NewLeaderStore(storeParams) require.NoError(t, err) return &testCluster{ store: store, storeConfig: testConfig, endpoints: endpoints, } } func createConfig(t *testing.T, cfg etcdCfg) *config.YamlNode { t.Helper() yamlCfg, err := yaml.Marshal(cfg) require.NoError(t, err) var res *config.YamlNode err = yaml.Unmarshal(yamlCfg, &res) require.NoError(t, err) return res } ================================================ FILE: service/sharddistributor/store/etcd/module.go ================================================ package etcd import ( "go.uber.org/fx" "github.com/uber/cadence/service/sharddistributor/store/etcd/executorstore" "github.com/uber/cadence/service/sharddistributor/store/etcd/leaderstore" ) var Module = fx.Module("etcd", executorstore.Module, fx.Provide(leaderstore.NewLeaderStore), ) ================================================ FILE: service/sharddistributor/store/etcd/testhelper/testhelper.go ================================================ package testhelper import ( "context" "flag" "fmt" "os" "strings" "testing" "time" "github.com/stretchr/testify/require" clientv3 "go.etcd.io/etcd/client/v3" "gopkg.in/yaml.v2" "github.com/uber/cadence/service/sharddistributor/config" "github.com/uber/cadence/testflags" ) type StoreTestCluster struct { EtcdPrefix string Namespace string LeaderCfg config.ShardDistribution Client *clientv3.Client Compression string } func SetupStoreTestCluster(t *testing.T) *StoreTestCluster { t.Helper() flag.Parse() testflags.RequireEtcd(t) namespace := fmt.Sprintf("ns-%s", strings.ToLower(t.Name())) endpoints := strings.Split(os.Getenv("ETCD_ENDPOINTS"), ",") if len(endpoints) == 0 || endpoints[0] == "" { endpoints = []string{"localhost:2379"} } t.Logf("ETCD endpoints: %v", endpoints) etcdPrefix := fmt.Sprintf("/test-shard-store/%s", t.Name()) etcdConfigRaw := map[string]interface{}{ "endpoints": endpoints, "dialTimeout": "5s", "prefix": etcdPrefix, "electionTTL": "5s", // Needed for leader config part "compression": "snappy", } yamlCfg, err := yaml.Marshal(etcdConfigRaw) require.NoError(t, err) var yamlNode *config.YamlNode err = yaml.Unmarshal(yamlCfg, &yamlNode) require.NoError(t, err) leaderCfg := config.ShardDistribution{ Store: config.Store{StorageParams: yamlNode}, LeaderStore: config.Store{StorageParams: yamlNode}, } client, err := clientv3.New(clientv3.Config{Endpoints: endpoints, DialTimeout: 5 * time.Second}) require.NoError(t, err) t.Cleanup(func() { client.Close() }) _, err = client.Delete(context.Background(), etcdPrefix, clientv3.WithPrefix()) require.NoError(t, err) return &StoreTestCluster{ Namespace: namespace, EtcdPrefix: etcdPrefix, LeaderCfg: leaderCfg, Client: client, } } ================================================ FILE: service/sharddistributor/store/leaderstore.go ================================================ package store import ( "context" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination=leaderstore_mock.go Elector,Election // Elector is an interface that provides a way to establish a session for election. // It establishes connection and a session and provides Election to run for leader. type Elector interface { CreateElection(ctx context.Context, namespace string) (Election, error) } // Election is an interface that establishes leader campaign. type Election interface { // Campaign is a blocking call that will block until either leadership is acquired and return nil or block. Campaign(ctx context.Context, hostname string) error // Resign resigns from leadership. Resign(ctx context.Context) error // Done returns a channel that notifies that the election session closed. Done() <-chan struct{} // Cleanup stops internal processes and releases keys. Cleanup(ctx context.Context) error // Guard returns a transaction guard representing the current leadership term. // This guard can be passed to the generic store to perform leader-protected writes. Guard() GuardFunc } ================================================ FILE: service/sharddistributor/store/leaderstore_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: leaderstore.go // // Generated by this command: // // mockgen -package store -source leaderstore.go -destination=leaderstore_mock.go Elector,Election // // Package store is a generated GoMock package. package store import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockElector is a mock of Elector interface. type MockElector struct { ctrl *gomock.Controller recorder *MockElectorMockRecorder isgomock struct{} } // MockElectorMockRecorder is the mock recorder for MockElector. type MockElectorMockRecorder struct { mock *MockElector } // NewMockElector creates a new mock instance. func NewMockElector(ctrl *gomock.Controller) *MockElector { mock := &MockElector{ctrl: ctrl} mock.recorder = &MockElectorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockElector) EXPECT() *MockElectorMockRecorder { return m.recorder } // CreateElection mocks base method. func (m *MockElector) CreateElection(ctx context.Context, namespace string) (Election, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateElection", ctx, namespace) ret0, _ := ret[0].(Election) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateElection indicates an expected call of CreateElection. func (mr *MockElectorMockRecorder) CreateElection(ctx, namespace any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateElection", reflect.TypeOf((*MockElector)(nil).CreateElection), ctx, namespace) } // MockElection is a mock of Election interface. type MockElection struct { ctrl *gomock.Controller recorder *MockElectionMockRecorder isgomock struct{} } // MockElectionMockRecorder is the mock recorder for MockElection. type MockElectionMockRecorder struct { mock *MockElection } // NewMockElection creates a new mock instance. func NewMockElection(ctrl *gomock.Controller) *MockElection { mock := &MockElection{ctrl: ctrl} mock.recorder = &MockElectionMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockElection) EXPECT() *MockElectionMockRecorder { return m.recorder } // Campaign mocks base method. func (m *MockElection) Campaign(ctx context.Context, hostname string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Campaign", ctx, hostname) ret0, _ := ret[0].(error) return ret0 } // Campaign indicates an expected call of Campaign. func (mr *MockElectionMockRecorder) Campaign(ctx, hostname any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Campaign", reflect.TypeOf((*MockElection)(nil).Campaign), ctx, hostname) } // Cleanup mocks base method. func (m *MockElection) Cleanup(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Cleanup", ctx) ret0, _ := ret[0].(error) return ret0 } // Cleanup indicates an expected call of Cleanup. func (mr *MockElectionMockRecorder) Cleanup(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cleanup", reflect.TypeOf((*MockElection)(nil).Cleanup), ctx) } // Done mocks base method. func (m *MockElection) Done() <-chan struct{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Done") ret0, _ := ret[0].(<-chan struct{}) return ret0 } // Done indicates an expected call of Done. func (mr *MockElectionMockRecorder) Done() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Done", reflect.TypeOf((*MockElection)(nil).Done)) } // Guard mocks base method. func (m *MockElection) Guard() GuardFunc { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Guard") ret0, _ := ret[0].(GuardFunc) return ret0 } // Guard indicates an expected call of Guard. func (mr *MockElectionMockRecorder) Guard() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Guard", reflect.TypeOf((*MockElection)(nil).Guard)) } // Resign mocks base method. func (m *MockElection) Resign(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Resign", ctx) ret0, _ := ret[0].(error) return ret0 } // Resign indicates an expected call of Resign. func (mr *MockElectionMockRecorder) Resign(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resign", reflect.TypeOf((*MockElection)(nil).Resign), ctx) } ================================================ FILE: service/sharddistributor/store/state.go ================================================ package store import ( "time" "github.com/uber/cadence/common/types" ) type HeartbeatState struct { // LastHeartbeat is the time of the last heartbeat received from the executor LastHeartbeat time.Time Status types.ExecutorStatus ReportedShards map[string]*types.ShardStatusReport Metadata map[string]string } type AssignedState struct { // AssignedShards holds the current assignment of shards to this executor // Key: ShardID AssignedShards map[string]*types.ShardAssignment // ShardHandoverStats holds handover statistics of all shards experienced handovers to this executor // Mostly all shards in AssignedShards will have corresponding entries here // But if a shard was assigned but never had a handover (e.g., first assignment), it does not have an entry here // Key: ShardID ShardHandoverStats map[string]ShardHandoverStats // LastUpdated is the time when this assignment state was last updated // Used to calculate assignment distribution latency for newly assigned shards LastUpdated time.Time ModRevision int64 } // ShardHandoverStats holds statistics related to the latest handover of a shard type ShardHandoverStats struct { // PreviousExecutorLastHeartbeatTime is the last heartbeat time received // from the previous executor before the shard was reassigned. PreviousExecutorLastHeartbeatTime time.Time // HandoverType indicates the type of handover that occurred during the last shard reassignment. HandoverType types.HandoverType } type NamespaceState struct { // Executors holds the heartbeat states of all executors in the namespace. // Key: ExecutorID Executors map[string]HeartbeatState // ShardStats holds the statistics of all shards in the namespace. // Only loaded for namespace which types.LoadBalancingMode is types.LoadBalancingModeGREEDY // Key: ShardID ShardStats map[string]ShardStatistics // ShardAssignments holds the assignment states of all shards in the namespace. // Key: ExecutorID ShardAssignments map[string]AssignedState } type ShardState struct { ExecutorID string } type ShardStatistics struct { // EWMA of shard load that persists across executor changes SmoothedLoad float64 // LastUpdateTime is the heartbeat timestamp that last updated the EWMA LastUpdateTime time.Time // LastMoveTime is the timestamp when this shard was last reassigned LastMoveTime time.Time } type ShardOwner struct { ExecutorID string Metadata map[string]string } // CountExecutorsByStatus returns a map of executor status to the count of executors with that status func (ns *NamespaceState) CountExecutorsByStatus() map[types.ExecutorStatus]int { counts := make(map[types.ExecutorStatus]int) for _, executor := range ns.Executors { counts[executor.Status]++ } return counts } ================================================ FILE: service/sharddistributor/store/state_test.go ================================================ package store import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/cadence/common/types" ) func TestNamespaceState_CountExecutorsByStatus(t *testing.T) { tests := []struct { name string executors map[string]HeartbeatState expected map[types.ExecutorStatus]int }{ { name: "empty executors", executors: map[string]HeartbeatState{}, expected: map[types.ExecutorStatus]int{}, }, { name: "single active executor", executors: map[string]HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE}, }, expected: map[types.ExecutorStatus]int{ types.ExecutorStatusACTIVE: 1, }, }, { name: "multiple executors same status", executors: map[string]HeartbeatState{ "exec-1": {Status: types.ExecutorStatusACTIVE}, "exec-2": {Status: types.ExecutorStatusACTIVE}, "exec-3": {Status: types.ExecutorStatusACTIVE}, }, expected: map[types.ExecutorStatus]int{ types.ExecutorStatusACTIVE: 3, }, }, { name: "all statuses", executors: map[string]HeartbeatState{ "exec-1": {Status: types.ExecutorStatusINVALID}, "exec-2": {Status: types.ExecutorStatusACTIVE}, "exec-3": {Status: types.ExecutorStatusDRAINING}, "exec-4": {Status: types.ExecutorStatusDRAINED}, }, expected: map[types.ExecutorStatus]int{ types.ExecutorStatusINVALID: 1, types.ExecutorStatusACTIVE: 1, types.ExecutorStatusDRAINING: 1, types.ExecutorStatusDRAINED: 1, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ns := &NamespaceState{ Executors: tt.executors, } result := ns.CountExecutorsByStatus() assert.Equal(t, tt.expected, result) }) } } ================================================ FILE: service/sharddistributor/store/store.go ================================================ package store import ( "context" "fmt" ) //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination=store_mock.go Store //go:generate gowrap gen -g -p . -i Store -t ./wrappers/templates/metered.tmpl -o ./wrappers/metered/store_generated.go -v handler=Wrapped var ( // ErrExecutorNotFound is an error that is returned when queries executor is not registered in the storage. ErrExecutorNotFound = fmt.Errorf("executor not found") // ErrShardNotFound is an error that is returned when a shard does not exist. ErrShardNotFound = fmt.Errorf("shard not found") // ErrVersionConflict is an error that is returned if during operations some precondition failed. ErrVersionConflict = fmt.Errorf("version conflict") // ErrExecutorNotRunning is an error that is returned when shard is attempted to be assigned to a not running executor. ErrExecutorNotRunning = fmt.Errorf("executor not running") ) type ErrShardAlreadyAssigned struct { ShardID string AssignedTo string Metadata map[string]string } func (e *ErrShardAlreadyAssigned) Error() string { return fmt.Sprintf("shard %s is already assigned to %s", e.ShardID, e.AssignedTo) } // Txn represents a generic, backend-agnostic transaction. // It is used as a vehicle for the GuardFunc to operate on. type Txn interface{} // GuardFunc is a function that applies a transactional precondition. // It takes a generic transaction, applies a backend-specific guard, // and returns the modified transaction. type GuardFunc func(Txn) (Txn, error) // NopGuard is a no-op guard that can be used when no transactional // check is required. It simply returns the transaction as-is. func NopGuard() GuardFunc { return func(txn Txn) (Txn, error) { return txn, nil } } // AssignShardsRequest is a request to assign shards to executors, and remove unused shards. type AssignShardsRequest struct { // NewState is the new state of the namespace, containing the new assignments of shards to executors. NewState *NamespaceState // ExecutorsToDelete maps executor IDs to their expected ModRevision for deletion. // The ModRevision is used to ensure the executor's assigned state hasn't changed since we decided to delete it. ExecutorsToDelete map[string]int64 } type Store interface { // GetState retrieves the current state of a namespace, including executors, // shard statistics, and shard assignments. GetState(ctx context.Context, namespace string) (*NamespaceState, error) // AssignShards assigns multiple shards to executors within a namespace. // It also updates shard statistics and deletes specified executors // The operation is atomic and guarded by the provided GuardFunc. AssignShards(ctx context.Context, namespace string, request AssignShardsRequest, guard GuardFunc) error // AssignShard assigns a single shard to an executor within a namespace. AssignShard(ctx context.Context, namespace string, shardID string, executorID string) error // SubscribeToExecutorStatusChanges subscribes to changes of executors' status key within a namespace. SubscribeToExecutorStatusChanges(ctx context.Context, namespace string) (<-chan int64, error) DeleteExecutors(ctx context.Context, namespace string, executorIDs []string, guard GuardFunc) error // DeleteAssignedStates deletes the assigned states of multiple executors within a namespace. DeleteAssignedStates(ctx context.Context, namespace string, executorIDs []string, guard GuardFunc) error // GetShardOwner retrieves the owner of a specific shard within a namespace. // It returns ErrShardNotFound if the shard does not exist. GetShardOwner(ctx context.Context, namespace, shardID string) (*ShardOwner, error) SubscribeToAssignmentChanges(ctx context.Context, namespace string) (<-chan map[*ShardOwner][]string, func(), error) // GetExecutor retrieves an executor within a namespace. GetExecutor(ctx context.Context, namespace string, executorID string) (*ShardOwner, error) GetHeartbeat(ctx context.Context, namespace string, executorID string) (*HeartbeatState, *AssignedState, error) RecordHeartbeat(ctx context.Context, namespace, executorID string, state HeartbeatState) error DeleteShardStats(ctx context.Context, namespace string, shardIDs []string, guard GuardFunc) error } ================================================ FILE: service/sharddistributor/store/store_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: store.go // // Generated by this command: // // mockgen -package store -source store.go -destination=store_mock.go Store // // Package store is a generated GoMock package. package store import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockTxn is a mock of Txn interface. type MockTxn struct { ctrl *gomock.Controller recorder *MockTxnMockRecorder isgomock struct{} } // MockTxnMockRecorder is the mock recorder for MockTxn. type MockTxnMockRecorder struct { mock *MockTxn } // NewMockTxn creates a new mock instance. func NewMockTxn(ctrl *gomock.Controller) *MockTxn { mock := &MockTxn{ctrl: ctrl} mock.recorder = &MockTxnMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTxn) EXPECT() *MockTxnMockRecorder { return m.recorder } // MockStore is a mock of Store interface. type MockStore struct { ctrl *gomock.Controller recorder *MockStoreMockRecorder isgomock struct{} } // MockStoreMockRecorder is the mock recorder for MockStore. type MockStoreMockRecorder struct { mock *MockStore } // NewMockStore creates a new mock instance. func NewMockStore(ctrl *gomock.Controller) *MockStore { mock := &MockStore{ctrl: ctrl} mock.recorder = &MockStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStore) EXPECT() *MockStoreMockRecorder { return m.recorder } // AssignShard mocks base method. func (m *MockStore) AssignShard(ctx context.Context, namespace, shardID, executorID string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AssignShard", ctx, namespace, shardID, executorID) ret0, _ := ret[0].(error) return ret0 } // AssignShard indicates an expected call of AssignShard. func (mr *MockStoreMockRecorder) AssignShard(ctx, namespace, shardID, executorID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssignShard", reflect.TypeOf((*MockStore)(nil).AssignShard), ctx, namespace, shardID, executorID) } // AssignShards mocks base method. func (m *MockStore) AssignShards(ctx context.Context, namespace string, request AssignShardsRequest, guard GuardFunc) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AssignShards", ctx, namespace, request, guard) ret0, _ := ret[0].(error) return ret0 } // AssignShards indicates an expected call of AssignShards. func (mr *MockStoreMockRecorder) AssignShards(ctx, namespace, request, guard any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssignShards", reflect.TypeOf((*MockStore)(nil).AssignShards), ctx, namespace, request, guard) } // DeleteAssignedStates mocks base method. func (m *MockStore) DeleteAssignedStates(ctx context.Context, namespace string, executorIDs []string, guard GuardFunc) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteAssignedStates", ctx, namespace, executorIDs, guard) ret0, _ := ret[0].(error) return ret0 } // DeleteAssignedStates indicates an expected call of DeleteAssignedStates. func (mr *MockStoreMockRecorder) DeleteAssignedStates(ctx, namespace, executorIDs, guard any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAssignedStates", reflect.TypeOf((*MockStore)(nil).DeleteAssignedStates), ctx, namespace, executorIDs, guard) } // DeleteExecutors mocks base method. func (m *MockStore) DeleteExecutors(ctx context.Context, namespace string, executorIDs []string, guard GuardFunc) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteExecutors", ctx, namespace, executorIDs, guard) ret0, _ := ret[0].(error) return ret0 } // DeleteExecutors indicates an expected call of DeleteExecutors. func (mr *MockStoreMockRecorder) DeleteExecutors(ctx, namespace, executorIDs, guard any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteExecutors", reflect.TypeOf((*MockStore)(nil).DeleteExecutors), ctx, namespace, executorIDs, guard) } // DeleteShardStats mocks base method. func (m *MockStore) DeleteShardStats(ctx context.Context, namespace string, shardIDs []string, guard GuardFunc) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteShardStats", ctx, namespace, shardIDs, guard) ret0, _ := ret[0].(error) return ret0 } // DeleteShardStats indicates an expected call of DeleteShardStats. func (mr *MockStoreMockRecorder) DeleteShardStats(ctx, namespace, shardIDs, guard any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteShardStats", reflect.TypeOf((*MockStore)(nil).DeleteShardStats), ctx, namespace, shardIDs, guard) } // GetExecutor mocks base method. func (m *MockStore) GetExecutor(ctx context.Context, namespace, executorID string) (*ShardOwner, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetExecutor", ctx, namespace, executorID) ret0, _ := ret[0].(*ShardOwner) ret1, _ := ret[1].(error) return ret0, ret1 } // GetExecutor indicates an expected call of GetExecutor. func (mr *MockStoreMockRecorder) GetExecutor(ctx, namespace, executorID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExecutor", reflect.TypeOf((*MockStore)(nil).GetExecutor), ctx, namespace, executorID) } // GetHeartbeat mocks base method. func (m *MockStore) GetHeartbeat(ctx context.Context, namespace, executorID string) (*HeartbeatState, *AssignedState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHeartbeat", ctx, namespace, executorID) ret0, _ := ret[0].(*HeartbeatState) ret1, _ := ret[1].(*AssignedState) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // GetHeartbeat indicates an expected call of GetHeartbeat. func (mr *MockStoreMockRecorder) GetHeartbeat(ctx, namespace, executorID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHeartbeat", reflect.TypeOf((*MockStore)(nil).GetHeartbeat), ctx, namespace, executorID) } // GetShardOwner mocks base method. func (m *MockStore) GetShardOwner(ctx context.Context, namespace, shardID string) (*ShardOwner, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetShardOwner", ctx, namespace, shardID) ret0, _ := ret[0].(*ShardOwner) ret1, _ := ret[1].(error) return ret0, ret1 } // GetShardOwner indicates an expected call of GetShardOwner. func (mr *MockStoreMockRecorder) GetShardOwner(ctx, namespace, shardID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardOwner", reflect.TypeOf((*MockStore)(nil).GetShardOwner), ctx, namespace, shardID) } // GetState mocks base method. func (m *MockStore) GetState(ctx context.Context, namespace string) (*NamespaceState, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetState", ctx, namespace) ret0, _ := ret[0].(*NamespaceState) ret1, _ := ret[1].(error) return ret0, ret1 } // GetState indicates an expected call of GetState. func (mr *MockStoreMockRecorder) GetState(ctx, namespace any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetState", reflect.TypeOf((*MockStore)(nil).GetState), ctx, namespace) } // RecordHeartbeat mocks base method. func (m *MockStore) RecordHeartbeat(ctx context.Context, namespace, executorID string, state HeartbeatState) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RecordHeartbeat", ctx, namespace, executorID, state) ret0, _ := ret[0].(error) return ret0 } // RecordHeartbeat indicates an expected call of RecordHeartbeat. func (mr *MockStoreMockRecorder) RecordHeartbeat(ctx, namespace, executorID, state any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordHeartbeat", reflect.TypeOf((*MockStore)(nil).RecordHeartbeat), ctx, namespace, executorID, state) } // SubscribeToAssignmentChanges mocks base method. func (m *MockStore) SubscribeToAssignmentChanges(ctx context.Context, namespace string) (<-chan map[*ShardOwner][]string, func(), error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SubscribeToAssignmentChanges", ctx, namespace) ret0, _ := ret[0].(<-chan map[*ShardOwner][]string) ret1, _ := ret[1].(func()) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // SubscribeToAssignmentChanges indicates an expected call of SubscribeToAssignmentChanges. func (mr *MockStoreMockRecorder) SubscribeToAssignmentChanges(ctx, namespace any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeToAssignmentChanges", reflect.TypeOf((*MockStore)(nil).SubscribeToAssignmentChanges), ctx, namespace) } // SubscribeToExecutorStatusChanges mocks base method. func (m *MockStore) SubscribeToExecutorStatusChanges(ctx context.Context, namespace string) (<-chan int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SubscribeToExecutorStatusChanges", ctx, namespace) ret0, _ := ret[0].(<-chan int64) ret1, _ := ret[1].(error) return ret0, ret1 } // SubscribeToExecutorStatusChanges indicates an expected call of SubscribeToExecutorStatusChanges. func (mr *MockStoreMockRecorder) SubscribeToExecutorStatusChanges(ctx, namespace any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeToExecutorStatusChanges", reflect.TypeOf((*MockStore)(nil).SubscribeToExecutorStatusChanges), ctx, namespace) } ================================================ FILE: service/sharddistributor/store/wrappers/metered/base.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metered import ( "errors" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/sharddistributor/store" ) type base struct { metricClient metrics.Client logger log.Logger timeSource clock.TimeSource } func (p *base) updateErrorMetricPerNamespace(err error, scopeWithNamespaceTags metrics.Scope) { if errors.Is(err, store.ErrExecutorNotFound) { scopeWithNamespaceTags.IncCounter(metrics.ShardDistributorStoreExecutorNotFound) } scopeWithNamespaceTags.IncCounter(metrics.ShardDistributorStoreFailuresPerNamespace) } func (p *base) call(scope metrics.ScopeIdx, op func() error, tags ...metrics.Tag) error { metricsScope := p.metricClient.Scope(scope, tags...) metricsScope.IncCounter(metrics.ShardDistributorStoreRequestsPerNamespace) before := p.timeSource.Now() err := op() duration := p.timeSource.Since(before) metricsScope.RecordHistogramDuration(metrics.ShardDistributorStoreLatencyHistogramPerNamespace, duration) if err != nil { p.updateErrorMetricPerNamespace(err, metricsScope) } return err } ================================================ FILE: service/sharddistributor/store/wrappers/metered/metered_test.go ================================================ package metered import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/store" ) const ( _testNamespace = "test_namespace" _testExecutorID = "test_executorID" ) func TestMeteredStore_GetHeartbeat(t *testing.T) { heartbeatRes := &store.HeartbeatState{ LastHeartbeat: time.Now().UTC(), } assignedState := &store.AssignedState{ LastUpdated: time.Now().UTC(), } tests := []struct { name string error error }{ { name: "Success", error: nil, }, { name: "NotFound", error: store.ErrExecutorNotFound, }, { name: "Failure", error: &types.InternalServiceError{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.ShardDistributor, metrics.HistogramMigration{}) timeSource := clock.NewMockedTimeSource() mockHandler := store.NewMockStore(ctrl) mockHandler.EXPECT().GetHeartbeat(gomock.Any(), _testNamespace, _testExecutorID).Do(func(ctx context.Context, namespace string, executorID string) { timeSource.Advance(time.Second) }).Return(heartbeatRes, assignedState, tt.error) mockLogger := log.NewMockLogger(ctrl) mockLogger.EXPECT().Helper().Return(mockLogger).AnyTimes() wrapped := NewStore(mockHandler, metricsClient, mockLogger, timeSource).(*meteredStore) gotHeartbeat, gotAssignedState, err := wrapped.GetHeartbeat(context.Background(), _testNamespace, _testExecutorID) assert.Equal(t, heartbeatRes, gotHeartbeat) assert.Equal(t, assignedState, gotAssignedState) assert.Equal(t, tt.error, err) // check that the metrics were emitted for this method requestCounterName := "test.shard_distributor_store_requests_per_namespace+namespace=test_namespace,operation=StoreGetHeartbeat" assert.Contains(t, testScope.Snapshot().Counters(), requestCounterName) requestCounter := testScope.Snapshot().Counters()[requestCounterName] assert.Equal(t, int64(1), requestCounter.Value()) latencyHistogramName := "test.shard_distributor_store_latency_histogram_per_namespace+namespace=test_namespace,operation=StoreGetHeartbeat" allHistograms := testScope.Snapshot().Histograms() assert.Contains(t, allHistograms, latencyHistogramName) }) } } ================================================ FILE: service/sharddistributor/store/wrappers/metered/store_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/sharddistributor/store" ) // meteredStore implements store.Store interface instrumented with metrics. type meteredStore struct { base wrapped store.Store } // NewStore creates a new instance of Store with metrics. func NewStore( wrapped store.Store, metricClient metrics.Client, logger log.Logger, timeSource clock.TimeSource, ) store.Store { if wrapped == nil { return nil } return &meteredStore{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger, timeSource: timeSource, }, } } func (c *meteredStore) AssignShard(ctx context.Context, namespace string, shardID string, executorID string) (err error) { op := func() error { err = c.wrapped.AssignShard(ctx, namespace, shardID, executorID) return err } err = c.call(metrics.ShardDistributorStoreAssignShardScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) AssignShards(ctx context.Context, namespace string, request store.AssignShardsRequest, guard store.GuardFunc) (err error) { op := func() error { err = c.wrapped.AssignShards(ctx, namespace, request, guard) return err } err = c.call(metrics.ShardDistributorStoreAssignShardsScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) DeleteAssignedStates(ctx context.Context, namespace string, executorIDs []string, guard store.GuardFunc) (err error) { op := func() error { err = c.wrapped.DeleteAssignedStates(ctx, namespace, executorIDs, guard) return err } err = c.call(metrics.ShardDistributorStoreDeleteAssignedStatesScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) DeleteExecutors(ctx context.Context, namespace string, executorIDs []string, guard store.GuardFunc) (err error) { op := func() error { err = c.wrapped.DeleteExecutors(ctx, namespace, executorIDs, guard) return err } err = c.call(metrics.ShardDistributorStoreDeleteExecutorsScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) DeleteShardStats(ctx context.Context, namespace string, shardIDs []string, guard store.GuardFunc) (err error) { op := func() error { err = c.wrapped.DeleteShardStats(ctx, namespace, shardIDs, guard) return err } err = c.call(metrics.ShardDistributorStoreDeleteShardStatsScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) GetExecutor(ctx context.Context, namespace string, executorID string) (sp1 *store.ShardOwner, err error) { op := func() error { sp1, err = c.wrapped.GetExecutor(ctx, namespace, executorID) return err } err = c.call(metrics.ShardDistributorStoreGetExecutorScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) GetHeartbeat(ctx context.Context, namespace string, executorID string) (hp1 *store.HeartbeatState, ap1 *store.AssignedState, err error) { op := func() error { hp1, ap1, err = c.wrapped.GetHeartbeat(ctx, namespace, executorID) return err } err = c.call(metrics.ShardDistributorStoreGetHeartbeatScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) GetShardOwner(ctx context.Context, namespace string, shardID string) (sp1 *store.ShardOwner, err error) { op := func() error { sp1, err = c.wrapped.GetShardOwner(ctx, namespace, shardID) return err } err = c.call(metrics.ShardDistributorStoreGetShardOwnerScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) GetState(ctx context.Context, namespace string) (np1 *store.NamespaceState, err error) { op := func() error { np1, err = c.wrapped.GetState(ctx, namespace) return err } err = c.call(metrics.ShardDistributorStoreGetStateScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) RecordHeartbeat(ctx context.Context, namespace string, executorID string, state store.HeartbeatState) (err error) { op := func() error { err = c.wrapped.RecordHeartbeat(ctx, namespace, executorID, state) return err } err = c.call(metrics.ShardDistributorStoreRecordHeartbeatScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) SubscribeToAssignmentChanges(ctx context.Context, namespace string) (ch1 <-chan map[*store.ShardOwner][]string, f1 func(), err error) { op := func() error { ch1, f1, err = c.wrapped.SubscribeToAssignmentChanges(ctx, namespace) return err } err = c.call(metrics.ShardDistributorStoreSubscribeToAssignmentChangesScope, op, metrics.NamespaceTag(namespace)) return } func (c *meteredStore) SubscribeToExecutorStatusChanges(ctx context.Context, namespace string) (ch1 <-chan int64, err error) { op := func() error { ch1, err = c.wrapped.SubscribeToExecutorStatusChanges(ctx, namespace) return err } err = c.call(metrics.ShardDistributorStoreSubscribeToExecutorStatusChangesScope, op, metrics.NamespaceTag(namespace)) return } ================================================ FILE: service/sharddistributor/store/wrappers/templates/metered.tmpl ================================================ import ( "context" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/sharddistributor/store" ) {{ $decorator := (printf "metered%s" .Interface.Name) }} {{ $interfaceName := .Interface.Name }} // {{$decorator}} implements {{.Interface.Type}} interface instrumented with metrics. type {{$decorator}} struct { base wrapped {{.Interface.Type}} } // New{{.Interface.Name}} creates a new instance of {{.Interface.Name}} with metrics. func New{{.Interface.Name}}( wrapped store.{{.Interface.Name}}, metricClient metrics.Client, logger log.Logger, timeSource clock.TimeSource, ) store.{{.Interface.Name}} { if wrapped == nil { return nil } return &{{$decorator}}{ wrapped: wrapped, base: base{ metricClient: metricClient, logger: logger, timeSource: timeSource, }, } } {{range $methodName, $method := .Interface.Methods}} {{- if (and $method.AcceptsContext $method.ReturnsError)}} func (c *{{$decorator}}) {{$method.Declaration}} { op := func() error { {{$method.ResultsNames}} = c.wrapped.{{$method.Call}} return err } {{$scopeName := printf "metrics.ShardDistributorStore%sScope" $methodName}} {{ $namespaceParam := (index $method.Params 1).Name }} {{$extraTags := printf ", metrics.NamespaceTag(%s)" $namespaceParam}} err = c.call({{$scopeName}}, op{{$extraTags}}) return } {{else}} func (c *{{$decorator}}) {{$method.Declaration}} { {{ $method.Pass "c.wrapped." }} } {{end}} {{end}} ================================================ FILE: service/sharddistributor/templates/metered.tmpl ================================================ import ( "context" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/handler" ) {{- $interfaceName := .Interface.Name}} {{- $interfaceType := .Interface.Type}} {{- $handlerName := (index .Vars "handler")}} {{- $decorator := (printf "%s%s" (down $handlerName) $interfaceName) }} {{- $Decorator := (printf "%s%s" $handlerName $interfaceName) }} type {{$decorator}} struct { handler {{.Interface.Type}} logger log.Logger metricsClient metrics.Client } func New{{$Decorator}}(handler {{$.Interface.Type}}, logger log.Logger, metricsClient metrics.Client) {{.Interface.Type}} { return &{{$decorator}}{ handler: handler, logger: logger, metricsClient: metricsClient, } } {{range $method := .Interface.Methods}} func (h *{{$decorator}}) {{$method.Declaration}} { {{- if has $method.Name (list "Health" "Start" "Stop")}} {{ $method.Pass "h.handler." }} {{- else}} defer func() { log.CapturePanic(recover(), h.logger, &err) }() {{- $namespace := "" }} {{- range $method.Params }} {{- if contains "Request" .Type }} {{- $namespace = printf "%s.GetNamespace()" .Name }} {{- end }} {{- end }} scope := h.metricsClient.Scope({{ printf "metrics.ShardDistributor%sScope" $method.Name }}) scope = scope.Tagged(metrics.NamespaceTag({{ $namespace }})) scope.IncCounter(metrics.ShardDistributorRequests) sw := scope.StartTimer(metrics.ShardDistributorLatency) defer sw.Stop() logger := h.logger.WithTags(tag.ShardNamespace({{ $namespace }})) {{$method.ResultsNames}} = h.handler.{{ $method.Call }} if err != nil { handleErr(err, scope, logger) } return {{ $method.ResultsNames }} {{- end}} } {{end}} ================================================ FILE: service/sharddistributor/wrappers/grpc/grpc_executor_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/common/types/mapper/proto" "github.com/uber/cadence/service/sharddistributor/handler" ) type ExecutorGRPCExecutor struct { h handler.Executor } func NewExecutorGRPCExecutor(h handler.Executor) ExecutorGRPCExecutor { return ExecutorGRPCExecutor{h} } func (g ExecutorGRPCExecutor) Heartbeat(ctx context.Context, request *sharddistributorv1.HeartbeatRequest) (*sharddistributorv1.HeartbeatResponse, error) { response, err := g.h.Heartbeat(ctx, proto.ToShardDistributorExecutorHeartbeatRequest(request)) return proto.FromShardDistributorExecutorHeartbeatResponse(response), proto.FromError(err) } ================================================ FILE: service/sharddistributor/wrappers/grpc/grpc_handler.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package grpc import ( "context" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/common/types/mapper/proto" ) func (g GRPCHandler) Register(dispatcher *yarpc.Dispatcher) { dispatcher.Register(sharddistributorv1.BuildShardDistributorAPIYARPCProcedures(g)) } func (g GRPCHandler) Health(ctx context.Context, _ *apiv1.HealthRequest) (*apiv1.HealthResponse, error) { response, err := g.h.Health(ctx) if err != nil { return nil, err } return proto.FromHealthResponse(response), proto.FromError(err) } func (g ExecutorGRPCExecutor) Register(dispatcher *yarpc.Dispatcher) { dispatcher.Register(sharddistributorv1.BuildShardDistributorExecutorAPIYARPCProcedures(g)) } ================================================ FILE: service/sharddistributor/wrappers/grpc/grpc_handler_generated.go ================================================ package grpc // Code generated by gowrap. DO NOT EDIT. // template: ../../../templates/grpc.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" sharddistributorv1 "github.com/uber/cadence/.gen/proto/sharddistributor/v1" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" "github.com/uber/cadence/service/sharddistributor/handler" ) type GRPCHandler struct { h handler.Handler } func NewGRPCHandler(h handler.Handler) GRPCHandler { return GRPCHandler{h} } func (g GRPCHandler) GetShardOwner(ctx context.Context, request *sharddistributorv1.GetShardOwnerRequest) (*sharddistributorv1.GetShardOwnerResponse, error) { response, err := g.h.GetShardOwner(ctx, proto.ToShardDistributorGetShardOwnerRequest(request)) return proto.FromShardDistributorGetShardOwnerResponse(response), proto.FromError(err) } func (g GRPCHandler) WatchNamespaceState(request *sharddistributorv1.WatchNamespaceStateRequest, server sharddistributorv1.ShardDistributorAPIServiceWatchNamespaceStateYARPCServer) error { err := g.h.WatchNamespaceState(proto.ToShardDistributorWatchNamespaceStateRequest(request), &watchnamespacestateServer{server: server}) return err } type watchnamespacestateServer struct { server sharddistributorv1.ShardDistributorAPIServiceWatchNamespaceStateYARPCServer } func (s *watchnamespacestateServer) Context() context.Context { return s.server.Context() } func (s *watchnamespacestateServer) Send(response *types.WatchNamespaceStateResponse) error { return s.server.Send(proto.FromShardDistributorWatchNamespaceStateResponse(response)) } ================================================ FILE: service/sharddistributor/wrappers/metered/api_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/handler" ) type metricsHandler struct { handler handler.Handler logger log.Logger metricsClient metrics.Client } func NewMetricsHandler(handler handler.Handler, logger log.Logger, metricsClient metrics.Client) handler.Handler { return &metricsHandler{ handler: handler, logger: logger, metricsClient: metricsClient, } } func (h *metricsHandler) GetShardOwner(ctx context.Context, gp1 *types.GetShardOwnerRequest) (gp2 *types.GetShardOwnerResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() scope := h.metricsClient.Scope(metrics.ShardDistributorGetShardOwnerScope) scope = scope.Tagged(metrics.NamespaceTag(gp1.GetNamespace())) scope.IncCounter(metrics.ShardDistributorRequests) sw := scope.StartTimer(metrics.ShardDistributorLatency) defer sw.Stop() logger := h.logger.WithTags(tag.ShardNamespace(gp1.GetNamespace())) gp2, err = h.handler.GetShardOwner(ctx, gp1) if err != nil { handleErr(err, scope, logger) } return gp2, err } func (h *metricsHandler) Health(ctx context.Context) (hp1 *types.HealthStatus, err error) { return h.handler.Health(ctx) } func (h *metricsHandler) Start() { h.handler.Start() return } func (h *metricsHandler) Stop() { h.handler.Stop() return } func (h *metricsHandler) WatchNamespaceState(wp1 *types.WatchNamespaceStateRequest, w1 handler.WatchNamespaceStateServer) (err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() scope := h.metricsClient.Scope(metrics.ShardDistributorWatchNamespaceStateScope) scope = scope.Tagged(metrics.NamespaceTag(wp1.GetNamespace())) scope.IncCounter(metrics.ShardDistributorRequests) sw := scope.StartTimer(metrics.ShardDistributorLatency) defer sw.Stop() logger := h.logger.WithTags(tag.ShardNamespace(wp1.GetNamespace())) err = h.handler.WatchNamespaceState(wp1, w1) if err != nil { handleErr(err, scope, logger) } return err } ================================================ FILE: service/sharddistributor/wrappers/metered/executor_generated.go ================================================ package metered // Code generated by gowrap. DO NOT EDIT. // template: ../../templates/metered.tmpl // gowrap: http://github.com/hexdigest/gowrap import ( "context" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/handler" ) type executormetricsExecutor struct { handler handler.Executor logger log.Logger metricsClient metrics.Client } func NewExecutorMetricsExecutor(handler handler.Executor, logger log.Logger, metricsClient metrics.Client) handler.Executor { return &executormetricsExecutor{ handler: handler, logger: logger, metricsClient: metricsClient, } } func (h *executormetricsExecutor) Heartbeat(ctx context.Context, ep1 *types.ExecutorHeartbeatRequest) (ep2 *types.ExecutorHeartbeatResponse, err error) { defer func() { log.CapturePanic(recover(), h.logger, &err) }() scope := h.metricsClient.Scope(metrics.ShardDistributorHeartbeatScope) scope = scope.Tagged(metrics.NamespaceTag(ep1.GetNamespace())) scope.IncCounter(metrics.ShardDistributorRequests) sw := scope.StartTimer(metrics.ShardDistributorLatency) defer sw.Stop() logger := h.logger.WithTags(tag.ShardNamespace(ep1.GetNamespace())) ep2, err = h.handler.Heartbeat(ctx, ep1) if err != nil { handleErr(err, scope, logger) } return ep2, err } ================================================ FILE: service/sharddistributor/wrappers/metered/metered.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metered import ( "context" "errors" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) func handleErr(err error, scope metrics.Scope, logger log.Logger) error { logger = logger.Helper() switch { case errors.As(err, new(*types.InternalServiceError)): scope.IncCounter(metrics.ShardDistributorFailures) logger.Error("Internal service error", tag.Error(err)) return err case errors.As(err, new(*types.NamespaceNotFoundError)): scope.IncCounter(metrics.ShardDistributorErrNamespaceNotFound) return err case errors.As(err, new(*types.ShardNotFoundError)): scope.IncCounter(metrics.ShardDistributorErrShardNotFound) return err } if errors.Is(err, context.DeadlineExceeded) { logger.Error("request timeout", tag.Error(err)) scope.IncCounter(metrics.ShardDistributorErrContextTimeoutCounter) return err } logger.Error("internal uncategorized error", tag.Error(err)) scope.IncCounter(metrics.ShardDistributorFailures) return err } ================================================ FILE: service/sharddistributor/wrappers/metered/metered_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package metered import ( "context" "errors" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/sharddistributor/handler" ) func TestMetricsHandler_GetShardOwner(t *testing.T) { request := &types.GetShardOwnerRequest{ ShardKey: "test-shard-key", Namespace: "test-namespace", } response := &types.GetShardOwnerResponse{ Owner: "test-owner", Namespace: "test-namespace", } tests := []struct { name string setupMocks func(logger *log.MockLogger) error error }{ { name: "Success", setupMocks: func(logger *log.MockLogger) {}, error: nil, }, { name: "Failure", setupMocks: func(logger *log.MockLogger) { logger.EXPECT().Helper().Return(logger) logger.EXPECT().Error( "Internal service error", []tag.Tag{tag.Error(&types.InternalServiceError{})}, ).Times(1) }, error: &types.InternalServiceError{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.ShardDistributor, metrics.HistogramMigration{}) mockHandler := handler.NewMockHandler(ctrl) mockHandler.EXPECT().GetShardOwner(gomock.Any(), request).Return(response, tt.error) mockLogger := log.NewMockLogger(ctrl) mockLogger.EXPECT().WithTags([]tag.Tag{tag.ShardNamespace("test-namespace")}).Return(mockLogger) mockMetricHandler := NewMetricsHandler(mockHandler, mockLogger, metricsClient).(*metricsHandler) tt.setupMocks(mockLogger) gotResponse, err := mockMetricHandler.GetShardOwner(context.Background(), request) assert.Equal(t, response, gotResponse) assert.Equal(t, tt.error, err) // check that the metrics were emitted for this method requestCounterName := "test.shard_distributor_requests+namespace=test-namespace,operation=GetShardOwner" requestCounter := testScope.Snapshot().Counters()[requestCounterName] assert.NotNil(t, requestCounter) assert.Equal(t, int64(1), requestCounter.Value()) latencyTimerName := "test.shard_distributor_latency+namespace=test-namespace,operation=GetShardOwner" latencyTimer := testScope.Snapshot().Timers()[latencyTimerName] assert.NotNil(t, latencyTimer) // check that the latency timer was run, and is not 0 assert.Greater(t, latencyTimer.Values()[0], int64(0)) }) } } // For these methods we expect no metrics nor logs to be emitted func TestPassThroughMethods(t *testing.T) { tests := []struct { name string call func(*testing.T, *metricsHandler, *handler.MockHandler) }{ { name: "Health", call: func(t *testing.T, h *metricsHandler, handlerMock *handler.MockHandler) { ctx := context.Background() healthStatus := &types.HealthStatus{ Ok: true, Msg: "test", } handlerMock.EXPECT().Health(ctx).Return(healthStatus, nil) healthStatusRet, err := h.Health(ctx) assert.NoError(t, err) assert.Equal(t, healthStatus, healthStatusRet) }, }, { name: "Start", call: func(t *testing.T, h *metricsHandler, handlerMock *handler.MockHandler) { handlerMock.EXPECT().Start() h.Start() }, }, { name: "Stop", call: func(t *testing.T, h *metricsHandler, handlerMock *handler.MockHandler) { handlerMock.EXPECT().Stop() h.Stop() }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.ShardDistributor, metrics.HistogramMigration{}) mockHandler := handler.NewMockHandler(ctrl) // We expect _no_ log calls mockLogger := log.NewMockLogger(ctrl) testMetricsHandler := NewMetricsHandler(mockHandler, mockLogger, metricsClient).(*metricsHandler) tt.call(t, testMetricsHandler, mockHandler) // check that no metrics were emitted for this method assert.Empty(t, testScope.Snapshot().Counters()) assert.Empty(t, testScope.Snapshot().Timers()) assert.Empty(t, testScope.Snapshot().Gauges()) assert.Empty(t, testScope.Snapshot().Histograms()) }) } } func TestHandleErr(t *testing.T) { tests := []struct { name string err error expectedError error setupMocks func(mockLogger *log.MockLogger) metricName string }{ { name: "InternalServiceError", err: &types.InternalServiceError{}, expectedError: &types.InternalServiceError{}, setupMocks: func(mockLogger *log.MockLogger) { mockLogger.EXPECT().Error( "Internal service error", []tag.Tag{tag.Error(&types.InternalServiceError{})}, ).Times(1) }, metricName: "shard_distributor_failures", }, { name: "NamespaceNotFoundError", err: &types.NamespaceNotFoundError{}, expectedError: &types.NamespaceNotFoundError{}, setupMocks: func(mockLogger *log.MockLogger) {}, metricName: "shard_distributor_err_namespace_not_found", }, { name: "ShardNotFoundError", err: &types.ShardNotFoundError{}, expectedError: &types.ShardNotFoundError{}, setupMocks: func(mockLogger *log.MockLogger) {}, metricName: "shard_distributor_err_shard_not_found", }, { name: "ContextDeadlineExceeded", err: context.DeadlineExceeded, expectedError: context.DeadlineExceeded, setupMocks: func(mockLogger *log.MockLogger) { mockLogger.EXPECT().Error( "request timeout", []tag.Tag{tag.Error(context.DeadlineExceeded)}, ).Times(1) }, metricName: "shard_distributor_err_context_timeout", }, { name: "UncategorizedError", err: errors.New("uncategorized error"), expectedError: errors.New("uncategorized error"), setupMocks: func(mockLogger *log.MockLogger) { mockLogger.EXPECT().Error( "internal uncategorized error", []tag.Tag{tag.Error(errors.New("uncategorized error"))}, ).Times(1) }, metricName: "shard_distributor_failures", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { testScope := tally.NewTestScope("test", nil) metricsClient := metrics.NewClient(testScope, metrics.ShardDistributor, metrics.HistogramMigration{}) mockLogger := log.NewMockLogger(gomock.NewController(t)) mockLogger.EXPECT().Helper().Return(mockLogger) tt.setupMocks(mockLogger) err := handleErr(tt.err, metricsClient.Scope(metrics.ShardDistributorGetShardOwnerScope), mockLogger) require.Equal(t, tt.expectedError, err) // check metrics fullName := fmt.Sprintf("test.%s+operation=GetShardOwner", tt.metricName) counter := testScope.Snapshot().Counters()[fullName] assert.NotNil(t, counter) assert.Equal(t, int64(1), counter.Value()) }) } } ================================================ FILE: service/templates/grpc.tmpl ================================================ {{$packagePath := (index .Vars "path")}} {{$package := (index .Vars "package")}} {{$prefix := (index .Vars "prefix")}} import ( "context" "go.uber.org/yarpc" {{$package}} "{{$packagePath}}" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/proto" ) {{$interfaceName := .Interface.Name}} {{$handlerName := (index .Vars "handler")}} {{ $Decorator := (printf "%s%s" $handlerName $interfaceName) }} {{$denylist := list "Start" "Stop" "PrepareToStop" "Health"}} type {{$Decorator}} struct { h {{.Interface.Type}} } func New{{$Decorator}}(h {{.Interface.Type}}) {{$Decorator}} { return {{$Decorator}}{h} } {{range $method := .Interface.Methods}} {{if not (has $method.Name $denylist)}} {{$Request := printf "%sRequest" $method.Name}} {{$Response := printf "%sResponse" $method.Name}} {{- $isStreaming := false}} {{- range $method.Params}} {{- if contains "Server" .Type}} {{- $isStreaming = true}} {{- end}} {{- end}} {{- if $isStreaming}} func (g {{$Decorator}}) {{$method.Name}}(request *{{$package}}.{{$Request}}, server {{$package}}.{{$prefix}}APIService{{$method.Name}}YARPCServer) error { err := g.h.{{$method.Name}}(proto.To{{$prefix}}{{$Request}}(request), &{{lower $method.Name}}Server{server: server}) return err } type {{lower $method.Name}}Server struct { server {{$package}}.{{$prefix}}APIService{{$method.Name}}YARPCServer } func (s *{{lower $method.Name}}Server) Context() context.Context { return s.server.Context() } func (s *{{lower $method.Name}}Server) Send(response *types.{{$Response}}) error { return s.server.Send(proto.From{{$prefix}}{{$Response}}(response)) } {{- else}} func (g {{$Decorator}}) {{$method.Name}}(ctx context.Context, request *{{$package}}.{{$Request}}) (*{{$package}}.{{$Response}}, error) { {{- if eq (len $method.Params) 1}} {{- if eq (len $method.Results) 1}} err := g.h.{{$method.Call}} {{- else}} response, err := g.h.{{$method.Name}}(ctx) {{- end}} {{- else}} {{- if eq (len $method.Results) 1}} err := g.h.{{$method.Name}}(ctx, proto.To{{$prefix}}{{$Request}}(request)) {{- else}} response, err := g.h.{{$method.Name}}(ctx, proto.To{{$prefix}}{{$Request}}(request)) {{- end}} {{- end}} {{- if eq (len $method.Results) 1}} return &{{$package}}.{{$Response}}{}, proto.FromError(err) {{- else}} return proto.From{{$prefix}}{{$Response}}(response), proto.FromError(err) {{- end}} } {{- end}} {{end}} {{end}} ================================================ FILE: service/templates/thrift.tmpl ================================================ import ( "context" "go.uber.org/yarpc" "github.com/uber/cadence/.gen/go/admin" "github.com/uber/cadence/.gen/go/history" "github.com/uber/cadence/.gen/go/matching" "github.com/uber/cadence/.gen/go/replicator" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common/types/mapper/thrift" ) {{$prefix := (index .Vars "prefix")}} {{$handlerName := (index .Vars "handler")}} {{ $Decorator := (printf "%sHandler" $handlerName) }} {{range $method := .Interface.Methods}} {{$Request := printf "%sRequest" $method.Name}} {{$Response := printf "%sResponse" $method.Name}} func (g {{$Decorator}}) {{$method.Declaration}} { {{- if eq (len $method.Params) 1}} {{- if eq (len $method.Results) 1}} {{(index $method.Results 0).Name}} = g.h.{{$method.Call}} {{- else}} response, {{(index $method.Results 1).Name}} := g.h.{{$method.Call}} {{- end}} {{- else}} {{- if eq (len $method.Results) 1}} {{- if or (eq $method.Name "AddDecisionTask") (eq $method.Name "AddActivityTask")}} _, {{(index $method.Results 0).Name}} = g.h.{{$method.Name}}({{(index $method.Params 0).Name}}, thrift.To{{$prefix}}{{$Request}}({{(index $method.Params 1).Name}})) {{- else}} {{(index $method.Results 0).Name}} = g.h.{{$method.Name}}({{(index $method.Params 0).Name}}, thrift.To{{$prefix}}{{$Request}}({{(index $method.Params 1).Name}})) {{- end}} {{- else}} response, {{(index $method.Results 1).Name}} := g.h.{{$method.Name}}({{(index $method.Params 0).Name}}, thrift.To{{$prefix}}{{$Request}}({{(index $method.Params 1).Name}})) {{- end}} {{- end}} {{- if eq (len $method.Results) 1}} return thrift.FromError({{(index $method.Results 0).Name}}) {{- else}} return thrift.From{{$prefix}}{{$Response}}(response), thrift.FromError({{(index $method.Results 1).Name}}) {{- end}} } {{end}} ================================================ FILE: service/worker/README.md ================================================ Cadence Worker ============== Cadence Worker is a new role for Cadence service used for hosting any components responsible for performing background processing on the Cadence cluster. Replicator ---------- Replicator is a background worker responsible for consuming replication tasks generated by remote Cadence clusters and pass it down to processor so they can be applied to local Cadence cluster. Quickstart for local development with multiple Cadence clusters and replication ==================================== 1. Start dependency using docker if you don't have one running: ``` docker compose -f docker/dev/cassandra.yml up ``` Then install the schemas: ``` make install-schema-xdc ``` 2. Start Cadence development server for cluster0, cluster1 and cluster2: ``` ./cadence-server --zone xdc_cluster0 start ./cadence-server --zone xdc_cluster1 start ./cadence-server --zone xdc_cluster2 start ``` 3. Create a global Cadence domain that replicates data across clusters ``` cadence --do samples-domain domain register --ac cluster0 --cl cluster0,cluster1,cluster2 ``` Then run a helloworld from [Go Client Sample](https://github.com/cadence-workflow/cadence-samples/) or [Java Client Sample](https://github.com/cadence-workflow/cadence-java-samples) 4. Failover a domain between clusters: Failover to cluster1: ``` cadence --do samples-domain domain update --ac cluster1 ``` or failover to cluster2: ``` cadence --do samples-domain domain update --ac cluster2 ``` Failback to cluster0: ``` cadence --do samples-domain domain update --ac cluster0 ``` ## Multiple region setup In a multiple region setup, use another set of config instead. ``` ./cadence-server --zone cross_region_cluster0 start ./cadence-server --zone cross_region_cluster1 start ./cadence-server --zone cross_region_cluster2 start ``` Right now the only difference is at clusterGroupMetadata.clusterRedirectionPolicy. In multiple region setup, network communication overhead between clusters is high so should use "selected-apis-forwarding". workflow/activity workers need to be connected to each cluster to keep high availability. Archiver -------- Archiver is used to handle archival of workflow execution histories. It does this by hosting a cadence client worker and running an archival system workflow. The archival client gets used to initiate archival through signal sending. The archiver shards work across several workflows. ================================================ FILE: service/worker/archiver/activities.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "context" "errors" "go.uber.org/cadence" "go.uber.org/cadence/activity" "github.com/uber/cadence/common" carchiver "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" ) const ( uploadHistoryActivityFnName = "uploadHistoryActivity" deleteHistoryActivityFnName = "deleteHistoryActivity" archiveVisibilityActivityFnName = "archiveVisibilityActivity" ) var ( errUploadNonRetriable = errors.New("upload non-retriable error") errDeleteNonRetriable = errors.New("delete non-retriable error") errArchiveVisibilityNonRetriable = errors.New("archive visibility non-retriable error") uploadHistoryActivityNonRetryableErrors = []string{"cadenceInternal:Panic", errUploadNonRetriable.Error()} deleteHistoryActivityNonRetryableErrors = []string{"cadenceInternal:Panic", errDeleteNonRetriable.Error()} ) func uploadHistoryActivity(ctx context.Context, request ArchiveRequest) (err error) { container := ctx.Value(bootstrapContainerKey).(*BootstrapContainer) scope := container.MetricsClient.Scope(metrics.ArchiverUploadHistoryActivityScope, metrics.DomainTag(request.DomainName)) sw := scope.StartTimer(metrics.CadenceLatency) defer func() { sw.Stop() if err != nil { if err.Error() == errUploadNonRetriable.Error() { scope.IncCounter(metrics.ArchiverNonRetryableErrorCount) } err = cadence.NewCustomError(err.Error()) } }() logger := tagLoggerWithHistoryRequest(tagLoggerWithActivityInfo(container.Logger, activity.GetInfo(ctx)), &request) URI, err := carchiver.NewURI(request.URI) if err != nil { logger.Error(carchiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason("failed to get history archival uri"), tag.ArchivalURI(request.URI), tag.Error(err)) return errUploadNonRetriable } historyArchiver, err := container.ArchiverProvider.GetHistoryArchiver(URI.Scheme(), service.Worker) if err != nil { logger.Error(carchiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason("failed to get history archiver"), tag.Error(err)) return errUploadNonRetriable } allowArchivingIncompleteHistoryOpt := carchiver.GetArchivingIncompleteHistoryOption(container.Config.AllowArchivingIncompleteHistory) err = historyArchiver.Archive(ctx, URI, &carchiver.ArchiveHistoryRequest{ ShardID: request.ShardID, DomainID: request.DomainID, DomainName: request.DomainName, WorkflowID: request.WorkflowID, RunID: request.RunID, BranchToken: request.BranchToken, NextEventID: request.NextEventID, CloseFailoverVersion: request.CloseFailoverVersion, }, carchiver.GetHeartbeatArchiveOption(), carchiver.GetNonRetriableErrorOption(errUploadNonRetriable), allowArchivingIncompleteHistoryOpt) if err == nil { return nil } if errors.Is(err, errUploadNonRetriable) { logger.Error(carchiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason("got non-retryable error from history archiver"), tag.Error(err), ) return errUploadNonRetriable } logger.Error(carchiver.ArchiveTransientErrorMsg, tag.ArchivalArchiveFailReason("got retryable error from history archiver"), tag.Error(err), ) return err } func deleteHistoryActivity(ctx context.Context, request ArchiveRequest) (err error) { container := ctx.Value(bootstrapContainerKey).(*BootstrapContainer) scope := container.MetricsClient.Scope(metrics.ArchiverDeleteHistoryActivityScope, metrics.DomainTag(request.DomainName)) sw := scope.StartTimer(metrics.CadenceLatency) defer func() { sw.Stop() if err != nil { if err.Error() == errDeleteNonRetriable.Error() { scope.IncCounter(metrics.ArchiverNonRetryableErrorCount) } err = cadence.NewCustomError(err.Error()) } }() err = container.HistoryV2Manager.DeleteHistoryBranch(ctx, &persistence.DeleteHistoryBranchRequest{ BranchToken: request.BranchToken, ShardID: common.IntPtr(request.ShardID), DomainName: request.DomainName, }) if err == nil { return nil } logger := tagLoggerWithHistoryRequest(tagLoggerWithActivityInfo(container.Logger, activity.GetInfo(ctx)), &request) logger.Error("failed to delete history events", tag.Error(err)) if !persistence.IsTransientError(err) { return errDeleteNonRetriable } return err } func archiveVisibilityActivity(ctx context.Context, request ArchiveRequest) (err error) { container := ctx.Value(bootstrapContainerKey).(*BootstrapContainer) scope := container.MetricsClient.Scope(metrics.ArchiverArchiveVisibilityActivityScope, metrics.DomainTag(request.DomainName)) sw := scope.StartTimer(metrics.CadenceLatency) defer func() { sw.Stop() if err != nil { if err.Error() == errArchiveVisibilityNonRetriable.Error() { scope.IncCounter(metrics.ArchiverNonRetryableErrorCount) } err = cadence.NewCustomError(err.Error()) } }() logger := tagLoggerWithVisibilityRequest(tagLoggerWithActivityInfo(container.Logger, activity.GetInfo(ctx)), &request) URI, err := carchiver.NewURI(request.VisibilityURI) if err != nil { logger.Error(carchiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason("failed to get visibility archival uri"), tag.ArchivalURI(request.VisibilityURI), tag.Error(err)) return errArchiveVisibilityNonRetriable } visibilityArchiver, err := container.ArchiverProvider.GetVisibilityArchiver(URI.Scheme(), service.Worker) if err != nil { logger.Error(carchiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason("failed to get visibility archiver"), tag.Error(err)) return errArchiveVisibilityNonRetriable } err = visibilityArchiver.Archive(ctx, URI, &carchiver.ArchiveVisibilityRequest{ DomainID: request.DomainID, DomainName: request.DomainName, WorkflowID: request.WorkflowID, RunID: request.RunID, WorkflowTypeName: request.WorkflowTypeName, StartTimestamp: request.StartTimestamp, ExecutionTimestamp: request.ExecutionTimestamp, CloseTimestamp: request.CloseTimestamp, CloseStatus: request.CloseStatus, HistoryLength: request.HistoryLength, Memo: request.Memo, SearchAttributes: convertSearchAttributesToString(request.SearchAttributes), HistoryArchivalURI: request.URI, }, carchiver.GetNonRetriableErrorOption(errArchiveVisibilityNonRetriable)) if err == nil { return nil } if err.Error() == errArchiveVisibilityNonRetriable.Error() { logger.Error(carchiver.ArchiveNonRetriableErrorMsg, tag.ArchivalArchiveFailReason("got non-retryable error from visibility archiver")) return errArchiveVisibilityNonRetriable } logger.Error(carchiver.ArchiveTransientErrorMsg, tag.ArchivalArchiveFailReason("got retryable error from visibility archiver"), tag.Error(err)) return err } ================================================ FILE: service/worker/archiver/activities_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "context" "errors" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "go.uber.org/mock/gomock" carchiver "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" mmocks "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/service" ) const ( testDomainID = "test-domain-id" testDomainName = "test-domain-name" testWorkflowID = "test-workflow-id" testRunID = "test-run-id" testNextEventID = 1800 testCloseFailoverVersion = 100 testScheme = "testScheme" testArchivalURI = testScheme + "://history/archival" ) var ( testBranchToken = []byte{1, 2, 3} errPersistenceNonRetryable = errors.New("persistence non-retryable error") ) type activitiesSuite struct { suite.Suite testsuite.WorkflowTestSuite controller *gomock.Controller logger log.Logger metricsClient *mmocks.Client metricsScope *mmocks.Scope archiverProvider *provider.MockArchiverProvider historyArchiver *carchiver.HistoryArchiverMock visibilityArchiver *carchiver.VisibilityArchiverMock } func TestActivitiesSuite(t *testing.T) { suite.Run(t, new(activitiesSuite)) } func (s *activitiesSuite) SetupTest() { s.controller = gomock.NewController(s.T()) s.logger = testlogger.New(s.T()) s.metricsClient = &mmocks.Client{} s.metricsScope = &mmocks.Scope{} s.archiverProvider = provider.NewMockArchiverProvider(s.controller) s.historyArchiver = &carchiver.HistoryArchiverMock{} s.visibilityArchiver = &carchiver.VisibilityArchiverMock{} s.metricsScope.On("StartTimer", metrics.CadenceLatency).Return(metrics.NewTestStopwatch()).Maybe() s.metricsScope.On("RecordTimer", mock.Anything, mock.Anything).Maybe() } func (s *activitiesSuite) TearDownTest() { s.metricsClient.AssertExpectations(s.T()) s.metricsScope.AssertExpectations(s.T()) s.historyArchiver.AssertExpectations(s.T()) s.visibilityArchiver.AssertExpectations(s.T()) } func (s *activitiesSuite) TestUploadHistory_Fail_InvalidURI() { s.metricsClient.On("Scope", metrics.ArchiverUploadHistoryActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() s.metricsScope.On("IncCounter", metrics.ArchiverNonRetryableErrorCount).Once() container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, URI: "some invalid URI without scheme", } _, err := env.ExecuteActivity(uploadHistoryActivity, request) s.Equal(errUploadNonRetriable.Error(), err.Error()) } func (s *activitiesSuite) TestUploadHistory_Fail_GetArchiverError() { s.metricsClient.On("Scope", metrics.ArchiverUploadHistoryActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() s.metricsScope.On("IncCounter", metrics.ArchiverNonRetryableErrorCount).Once() s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), service.Worker).Return(nil, errors.New("failed to get archiver")) container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, ArchiverProvider: s.archiverProvider, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, URI: testArchivalURI, } _, err := env.ExecuteActivity(uploadHistoryActivity, request) s.Equal(errUploadNonRetriable.Error(), err.Error()) } func (s *activitiesSuite) TestUploadHistory_Fail_ArchiveNonRetriableError() { s.metricsClient.On("Scope", metrics.ArchiverUploadHistoryActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() s.metricsScope.On("IncCounter", metrics.ArchiverNonRetryableErrorCount).Once() s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errUploadNonRetriable) s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), service.Worker).Return(s.historyArchiver, nil) container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, ArchiverProvider: s.archiverProvider, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, URI: testArchivalURI, } _, err := env.ExecuteActivity(uploadHistoryActivity, request) s.Equal(errUploadNonRetriable.Error(), err.Error()) } func (s *activitiesSuite) TestUploadHistory_Fail_ArchiveRetriableError() { s.metricsClient.On("Scope", metrics.ArchiverUploadHistoryActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() testArchiveErr := errors.New("some transient error") s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testArchiveErr) s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), service.Worker).Return(s.historyArchiver, nil) container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, ArchiverProvider: s.archiverProvider, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, URI: testArchivalURI, } _, err := env.ExecuteActivity(uploadHistoryActivity, request) s.Equal(testArchiveErr.Error(), err.Error()) } func (s *activitiesSuite) TestUploadHistory_Success() { s.metricsClient.On("Scope", metrics.ArchiverUploadHistoryActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), service.Worker).Return(s.historyArchiver, nil) container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, ArchiverProvider: s.archiverProvider, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, URI: testArchivalURI, } _, err := env.ExecuteActivity(uploadHistoryActivity, request) s.NoError(err) } func (s *activitiesSuite) TestDeleteHistoryActivity_Fail_DeleteFromV2NonRetryableError() { s.metricsClient.On("Scope", metrics.ArchiverDeleteHistoryActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() s.metricsScope.On("IncCounter", metrics.ArchiverNonRetryableErrorCount).Once() mockHistoryV2Manager := &mocks.HistoryV2Manager{} mockHistoryV2Manager.On("DeleteHistoryBranch", mock.Anything, mock.Anything).Return(errPersistenceNonRetryable) container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, HistoryV2Manager: mockHistoryV2Manager, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, BranchToken: testBranchToken, NextEventID: testNextEventID, CloseFailoverVersion: testCloseFailoverVersion, URI: testArchivalURI, } _, err := env.ExecuteActivity(deleteHistoryActivity, request) s.Equal(errDeleteNonRetriable.Error(), err.Error()) } func (s *activitiesSuite) TestArchiveVisibilityActivity_Fail_InvalidURI() { s.metricsClient.On("Scope", metrics.ArchiverArchiveVisibilityActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() s.metricsScope.On("IncCounter", metrics.ArchiverNonRetryableErrorCount).Once() container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, VisibilityURI: "some invalid URI without scheme", } _, err := env.ExecuteActivity(archiveVisibilityActivity, request) s.Equal(errArchiveVisibilityNonRetriable.Error(), err.Error()) } func (s *activitiesSuite) TestArchiveVisibilityActivity_Fail_GetArchiverError() { s.metricsClient.On("Scope", metrics.ArchiverArchiveVisibilityActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() s.metricsScope.On("IncCounter", metrics.ArchiverNonRetryableErrorCount).Once() s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), service.Worker).Return(nil, errors.New("failed to get archiver")) container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, ArchiverProvider: s.archiverProvider, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, VisibilityURI: testArchivalURI, } _, err := env.ExecuteActivity(archiveVisibilityActivity, request) s.Equal(errArchiveVisibilityNonRetriable.Error(), err.Error()) } func (s *activitiesSuite) TestArchiveVisibilityActivity_Fail_ArchiveNonRetriableError() { s.metricsClient.On("Scope", metrics.ArchiverArchiveVisibilityActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() s.metricsScope.On("IncCounter", metrics.ArchiverNonRetryableErrorCount).Once() s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errArchiveVisibilityNonRetriable) s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), service.Worker).Return(s.visibilityArchiver, nil) container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, ArchiverProvider: s.archiverProvider, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, VisibilityURI: testArchivalURI, } _, err := env.ExecuteActivity(archiveVisibilityActivity, request) s.Equal(errArchiveVisibilityNonRetriable.Error(), err.Error()) } func (s *activitiesSuite) TestArchiveVisibilityActivity_Fail_ArchiveRetriableError() { s.metricsClient.On("Scope", metrics.ArchiverArchiveVisibilityActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() testArchiveErr := errors.New("some transient error") s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testArchiveErr) s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), service.Worker).Return(s.visibilityArchiver, nil) container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, ArchiverProvider: s.archiverProvider, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, VisibilityURI: testArchivalURI, } _, err := env.ExecuteActivity(archiveVisibilityActivity, request) s.Equal(testArchiveErr.Error(), err.Error()) } func (s *activitiesSuite) TestArchiveVisibilityActivity_Success() { s.metricsClient.On("Scope", metrics.ArchiverArchiveVisibilityActivityScope, metrics.DomainTag(testDomainName)).Return(s.metricsScope).Once() s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), service.Worker).Return(s.visibilityArchiver, nil) container := &BootstrapContainer{ Logger: s.logger, MetricsClient: s.metricsClient, ArchiverProvider: s.archiverProvider, Config: &Config{ AllowArchivingIncompleteHistory: dynamicproperties.GetBoolPropertyFn(false), }, } env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), bootstrapContainerKey, container), }) request := ArchiveRequest{ DomainID: testDomainID, DomainName: testDomainName, WorkflowID: testWorkflowID, RunID: testRunID, VisibilityURI: testArchivalURI, } _, err := env.ExecuteActivity(archiveVisibilityActivity, request) s.NoError(err) } ================================================ FILE: service/worker/archiver/client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "context" "errors" "fmt" "math/rand" "time" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" cclient "go.uber.org/cadence/client" carchiver "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/quotas" "github.com/uber/cadence/common/types" ) //go:generate mockgen -package=$GOPACKAGE -destination=client_mock.go -self_package=github.com/uber/cadence/service/worker/archiver github.com/uber/cadence/service/worker/archiver Client type ( // ClientRequest is the archive request sent to the archiver client ClientRequest struct { ArchiveRequest *ArchiveRequest CallerService string AttemptArchiveInline bool } // ClientResponse is the archive response returned from the archiver client ClientResponse struct { HistoryArchivedInline bool } // ArchiveRequest is the request signal sent to the archival workflow ArchiveRequest struct { DomainID string DomainName string WorkflowID string RunID string // history archival ShardID int BranchToken []byte NextEventID int64 CloseFailoverVersion int64 URI string // should be historyURI, but keep the existing name for backward compatibility // visibility archival WorkflowTypeName string StartTimestamp int64 ExecutionTimestamp int64 CloseTimestamp int64 CloseStatus types.WorkflowExecutionCloseStatus HistoryLength int64 Memo *types.Memo SearchAttributes map[string][]byte VisibilityURI string // archival targets: history and/or visibility Targets []ArchivalTarget } // Client is used to archive workflow histories Client interface { Archive(context.Context, *ClientRequest) (*ClientResponse, error) } client struct { metricsScope metrics.Scope logger log.Logger cadenceClient cclient.Client numWorkflows dynamicproperties.IntPropertyFn rateLimiter quotas.Limiter inlineHistoryRateLimiter quotas.Limiter inlineVisibilityRateLimiter quotas.Limiter archiverProvider provider.ArchiverProvider archivingIncompleteHistory dynamicproperties.BoolPropertyFn } // ArchivalTarget is either history or visibility ArchivalTarget int ) const ( signalTimeout = 300 * time.Millisecond tooManyRequestsErrMsg = "too many requests to archival workflow" ) const ( // ArchiveTargetHistory is the archive target for workflow history ArchiveTargetHistory ArchivalTarget = iota // ArchiveTargetVisibility is the archive target for workflow visibility record ArchiveTargetVisibility ) var ( errInlineArchivalThrottled = errors.New("inline archival throttled") ) // NewClient creates a new Client func NewClient( metricsClient metrics.Client, logger log.Logger, publicClient workflowserviceclient.Interface, numWorkflows dynamicproperties.IntPropertyFn, requestRateLimiter quotas.Limiter, inlineHistoryRateLimiter quotas.Limiter, inlineVisibilityRateLimiter quotas.Limiter, archiverProvider provider.ArchiverProvider, archivingIncompleteHistory dynamicproperties.BoolPropertyFn, ) Client { return &client{ metricsScope: metricsClient.Scope(metrics.ArchiverClientScope), logger: logger, cadenceClient: cclient.NewClient(publicClient, constants.SystemLocalDomainName, &cclient.Options{}), numWorkflows: numWorkflows, rateLimiter: requestRateLimiter, inlineHistoryRateLimiter: inlineHistoryRateLimiter, inlineVisibilityRateLimiter: inlineVisibilityRateLimiter, archiverProvider: archiverProvider, archivingIncompleteHistory: archivingIncompleteHistory, } } // Archive starts an archival task func (c *client) Archive(ctx context.Context, request *ClientRequest) (*ClientResponse, error) { scopeWithDomainTag := c.metricsScope.Tagged(metrics.DomainTag(request.ArchiveRequest.DomainName)) for _, target := range request.ArchiveRequest.Targets { switch target { case ArchiveTargetHistory: scopeWithDomainTag.IncCounter(metrics.ArchiverClientHistoryRequestCountPerDomain) case ArchiveTargetVisibility: scopeWithDomainTag.IncCounter(metrics.ArchiverClientVisibilityRequestCountPerDomain) } } logger := c.logger.WithTags( tag.ArchivalCallerServiceName(request.CallerService), tag.ArchivalArchiveAttemptedInline(request.AttemptArchiveInline), ) resp := &ClientResponse{ HistoryArchivedInline: false, } if request.AttemptArchiveInline { results := []chan error{} for _, target := range request.ArchiveRequest.Targets { ch := make(chan error) results = append(results, ch) switch target { case ArchiveTargetHistory: go c.archiveHistoryInline(ctx, request, logger, ch) case ArchiveTargetVisibility: go c.archiveVisibilityInline(ctx, request, logger, ch) default: close(ch) } } targets := []ArchivalTarget{} for i, target := range request.ArchiveRequest.Targets { if <-results[i] != nil { targets = append(targets, target) } else if target == ArchiveTargetHistory { resp.HistoryArchivedInline = true } } request.ArchiveRequest.Targets = targets } if len(request.ArchiveRequest.Targets) != 0 { if err := c.sendArchiveSignal(ctx, request.ArchiveRequest, logger); err != nil { return nil, err } } return resp, nil } func (c *client) archiveHistoryInline(ctx context.Context, request *ClientRequest, logger log.Logger, errCh chan error) { logger = tagLoggerWithHistoryRequest(logger, request.ArchiveRequest) scopeWithDomainTag := c.metricsScope.Tagged(metrics.DomainTag(request.ArchiveRequest.DomainName)) if !c.inlineHistoryRateLimiter.Allow() { scopeWithDomainTag.IncCounter(metrics.ArchiverClientHistoryInlineArchiveThrottledCountPerDomain) logger.Debug("inline history archival throttled") errCh <- errInlineArchivalThrottled return } var err error defer func() { if err != nil { scopeWithDomainTag.IncCounter(metrics.ArchiverClientHistoryInlineArchiveFailureCountPerDomain) logger.Info("failed to perform workflow history archival inline", tag.Error(err)) } errCh <- err }() scopeWithDomainTag.IncCounter(metrics.ArchiverClientHistoryInlineArchiveAttemptCountPerDomain) URI, err := carchiver.NewURI(request.ArchiveRequest.URI) if err != nil { return } historyArchiver, err := c.archiverProvider.GetHistoryArchiver(URI.Scheme(), request.CallerService) if err != nil { return } allowArchivingIncompleteHistoryOpt := carchiver.GetArchivingIncompleteHistoryOption(c.archivingIncompleteHistory) err = historyArchiver.Archive(ctx, URI, &carchiver.ArchiveHistoryRequest{ ShardID: request.ArchiveRequest.ShardID, DomainID: request.ArchiveRequest.DomainID, DomainName: request.ArchiveRequest.DomainName, WorkflowID: request.ArchiveRequest.WorkflowID, RunID: request.ArchiveRequest.RunID, BranchToken: request.ArchiveRequest.BranchToken, NextEventID: request.ArchiveRequest.NextEventID, CloseFailoverVersion: request.ArchiveRequest.CloseFailoverVersion, }, allowArchivingIncompleteHistoryOpt) } func (c *client) archiveVisibilityInline(ctx context.Context, request *ClientRequest, logger log.Logger, errCh chan error) { logger = tagLoggerWithVisibilityRequest(logger, request.ArchiveRequest) scopeWithDomainTag := c.metricsScope.Tagged(metrics.DomainTag(request.ArchiveRequest.DomainName)) if !c.inlineVisibilityRateLimiter.Allow() { scopeWithDomainTag.IncCounter(metrics.ArchiverClientVisibilityInlineArchiveThrottledCountPerDomain) logger.Debug("inline visibility archival throttled") errCh <- errInlineArchivalThrottled return } var err error defer func() { if err != nil { scopeWithDomainTag.IncCounter(metrics.ArchiverClientVisibilityInlineArchiveFailureCountPerDomain) logger.Info("failed to perform visibility archival inline", tag.Error(err)) } errCh <- err }() scopeWithDomainTag.IncCounter(metrics.ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain) URI, err := carchiver.NewURI(request.ArchiveRequest.VisibilityURI) if err != nil { return } visibilityArchiver, err := c.archiverProvider.GetVisibilityArchiver(URI.Scheme(), request.CallerService) if err != nil { return } err = visibilityArchiver.Archive(ctx, URI, &carchiver.ArchiveVisibilityRequest{ DomainID: request.ArchiveRequest.DomainID, DomainName: request.ArchiveRequest.DomainName, WorkflowID: request.ArchiveRequest.WorkflowID, RunID: request.ArchiveRequest.RunID, WorkflowTypeName: request.ArchiveRequest.WorkflowTypeName, StartTimestamp: request.ArchiveRequest.StartTimestamp, ExecutionTimestamp: request.ArchiveRequest.ExecutionTimestamp, CloseTimestamp: request.ArchiveRequest.CloseTimestamp, CloseStatus: request.ArchiveRequest.CloseStatus, HistoryLength: request.ArchiveRequest.HistoryLength, Memo: request.ArchiveRequest.Memo, SearchAttributes: convertSearchAttributesToString(request.ArchiveRequest.SearchAttributes), HistoryArchivalURI: request.ArchiveRequest.URI, }) } func (c *client) sendArchiveSignal(ctx context.Context, request *ArchiveRequest, taggedLogger log.Logger) error { scopeWithDomainTag := c.metricsScope.Tagged(metrics.DomainTag(request.DomainName)) scopeWithDomainTag.IncCounter(metrics.ArchiverClientSendSignalCountPerDomain) if ok := c.rateLimiter.Allow(); !ok { c.logger.Error(tooManyRequestsErrMsg) scopeWithDomainTag.IncCounter(metrics.CadenceErrServiceBusyCounter) return errors.New(tooManyRequestsErrMsg) } workflowID := fmt.Sprintf("%v-%v", workflowIDPrefix, rand.Intn(c.numWorkflows())) workflowOptions := cclient.StartWorkflowOptions{ ID: workflowID, TaskList: decisionTaskList, ExecutionStartToCloseTimeout: workflowStartToCloseTimeout, DecisionTaskStartToCloseTimeout: workflowTaskStartToCloseTimeout, WorkflowIDReusePolicy: cclient.WorkflowIDReusePolicyAllowDuplicate, } signalCtx, cancel := context.WithTimeout(context.Background(), signalTimeout) defer cancel() _, err := c.cadenceClient.SignalWithStartWorkflow(signalCtx, workflowID, signalName, *request, workflowOptions, archivalWorkflowFnName, nil) if err != nil { taggedLogger = taggedLogger.WithTags( tag.ArchivalRequestDomainID(request.DomainID), tag.ArchivalRequestDomainName(request.DomainName), tag.ArchivalRequestWorkflowID(request.WorkflowID), tag.ArchivalRequestRunID(request.RunID), tag.WorkflowID(workflowID), tag.Error(err), ) taggedLogger.Error("failed to send signal to archival system workflow") scopeWithDomainTag.IncCounter(metrics.ArchiverClientSendSignalFailureCountPerDomain) return err } return nil } ================================================ FILE: service/worker/archiver/client_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/worker/archiver (interfaces: Client) // // Generated by this command: // // mockgen -package=archiver -destination=client_mock.go -self_package=github.com/uber/cadence/service/worker/archiver github.com/uber/cadence/service/worker/archiver Client // // Package archiver is a generated GoMock package. package archiver import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // Archive mocks base method. func (m *MockClient) Archive(arg0 context.Context, arg1 *ClientRequest) (*ClientResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Archive", arg0, arg1) ret0, _ := ret[0].(*ClientResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // Archive indicates an expected call of Archive. func (mr *MockClientMockRecorder) Archive(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Archive", reflect.TypeOf((*MockClient)(nil).Archive), arg0, arg1) } ================================================ FILE: service/worker/archiver/client_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "context" "errors" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/cadence/mocks" "go.uber.org/mock/gomock" "golang.org/x/time/rate" carchiver "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" mmocks "github.com/uber/cadence/common/metrics/mocks" ) type clientSuite struct { *require.Assertions suite.Suite controller *gomock.Controller archiverProvider *provider.MockArchiverProvider historyArchiver *carchiver.HistoryArchiverMock visibilityArchiver *carchiver.VisibilityArchiverMock metricsClient *mmocks.Client metricsScope *mmocks.Scope cadenceClient *mocks.Client client *client } func TestClientSuite(t *testing.T) { suite.Run(t, new(clientSuite)) } func (s *clientSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.archiverProvider = provider.NewMockArchiverProvider(s.controller) s.historyArchiver = &carchiver.HistoryArchiverMock{} s.visibilityArchiver = &carchiver.VisibilityArchiverMock{} s.metricsClient = &mmocks.Client{} s.metricsScope = &mmocks.Scope{} s.cadenceClient = &mocks.Client{} s.metricsClient.On("Scope", metrics.ArchiverClientScope, mock.Anything).Return(s.metricsScope).Once() s.client = NewClient( s.metricsClient, log.NewNoop(), nil, dynamicproperties.GetIntPropertyFn(1000), clock.NewRatelimiter(rate.Limit(1000), 1), clock.NewRatelimiter(rate.Limit(1), 1), clock.NewRatelimiter(rate.Limit(1), 1), s.archiverProvider, dynamicproperties.GetBoolPropertyFn(false), ).(*client) s.client.cadenceClient = s.cadenceClient } func (s *clientSuite) TearDownTest() { s.historyArchiver.AssertExpectations(s.T()) s.visibilityArchiver.AssertExpectations(s.T()) s.metricsClient.AssertExpectations(s.T()) s.metricsScope.AssertExpectations(s.T()) } func (s *clientSuite) TestArchiveVisibilityInlineSuccess() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.visibilityArchiver, nil).Times(1) s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain).Once() resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ VisibilityURI: "test:///visibility/archival", Targets: []ArchivalTarget{ArchiveTargetVisibility}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) s.False(resp.HistoryArchivedInline) } func (s *clientSuite) TestArchiveVisibilityInlineThrottled() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.visibilityArchiver, nil).Times(1) s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityRequestCountPerDomain).Times(2) scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveThrottledCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalCountPerDomain).Once() s.cadenceClient.On("SignalWithStartWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(v ArchiveRequest) bool { return len(v.Targets) == 1 && v.Targets[0] == ArchiveTargetVisibility }), mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) for i := 0; i < 2; i++ { resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ VisibilityURI: "test:///visibility/archival", Targets: []ArchivalTarget{ArchiveTargetVisibility}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) } } func (s *clientSuite) TestArchiveVisibilityInlineFail_SendSignalSuccess() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.visibilityArchiver, nil).Times(1) s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("some random error")).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveFailureCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalCountPerDomain).Once() s.cadenceClient.On("SignalWithStartWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(v ArchiveRequest) bool { return len(v.Targets) == 1 && v.Targets[0] == ArchiveTargetVisibility }), mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ VisibilityURI: "test:///visibility/archival", Targets: []ArchivalTarget{ArchiveTargetVisibility}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) s.False(resp.HistoryArchivedInline) } func (s *clientSuite) TestArchiveVisibilityInlineFail_SendSignalFail() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.visibilityArchiver, nil).Times(1) s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("some random error")).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveFailureCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalFailureCountPerDomain).Once() s.cadenceClient.On("SignalWithStartWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(v ArchiveRequest) bool { return len(v.Targets) == 1 && v.Targets[0] == ArchiveTargetVisibility }), mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("some random error")) resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ VisibilityURI: "test:///visibility/archival", Targets: []ArchivalTarget{ArchiveTargetVisibility}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.Error(err) s.Nil(resp) } func (s *clientSuite) TestArchiveHistoryInlineSuccess() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.historyArchiver, nil).Times(1) s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveAttemptCountPerDomain).Once() resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ URI: "test:///history/archival", Targets: []ArchivalTarget{ArchiveTargetHistory}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) s.True(resp.HistoryArchivedInline) } func (s *clientSuite) TestArchiveHistoryInlineThrottled() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.historyArchiver, nil).Times(1) s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryRequestCountPerDomain).Times(2) scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveThrottledCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalCountPerDomain).Once() s.cadenceClient.On("SignalWithStartWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(v ArchiveRequest) bool { return len(v.Targets) == 1 && v.Targets[0] == ArchiveTargetHistory }), mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) for i := 0; i < 2; i++ { resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ URI: "test:///history/archival", Targets: []ArchivalTarget{ArchiveTargetHistory}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) } } func (s *clientSuite) TestArchiveHistoryInlineFail_SendSignalSuccess() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.historyArchiver, nil).Times(1) s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("some random error")).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveFailureCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalCountPerDomain).Once() s.cadenceClient.On("SignalWithStartWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(v ArchiveRequest) bool { return len(v.Targets) == 1 && v.Targets[0] == ArchiveTargetHistory }), mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ URI: "test:///history/archival", Targets: []ArchivalTarget{ArchiveTargetHistory}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) s.False(resp.HistoryArchivedInline) } func (s *clientSuite) TestArchiveHistoryInlineFail_SendSignalFail() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.historyArchiver, nil).Times(1) s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("some random error")).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveFailureCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalFailureCountPerDomain).Once() s.cadenceClient.On("SignalWithStartWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(v ArchiveRequest) bool { return len(v.Targets) == 1 && v.Targets[0] == ArchiveTargetHistory }), mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("some random error")) resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ URI: "test:///history/archival", Targets: []ArchivalTarget{ArchiveTargetHistory}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.Error(err) s.Nil(resp) } func (s *clientSuite) TestArchiveInline_HistoryFail_VisibilitySuccess() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.historyArchiver, nil).Times(1) s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.visibilityArchiver, nil).Times(1) s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("some random error")).Once() s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveFailureCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalCountPerDomain).Once() s.cadenceClient.On("SignalWithStartWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(v ArchiveRequest) bool { return len(v.Targets) == 1 && v.Targets[0] == ArchiveTargetHistory }), mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ URI: "test:///history/archival", VisibilityURI: "test:///visibility/archival", Targets: []ArchivalTarget{ArchiveTargetHistory, ArchiveTargetVisibility}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) s.False(resp.HistoryArchivedInline) } func (s *clientSuite) TestArchiveInline_VisibilityFail_HistorySuccess() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.historyArchiver, nil).Times(1) s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.visibilityArchiver, nil).Times(1) s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("some random error")).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveFailureCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalCountPerDomain).Once() s.cadenceClient.On("SignalWithStartWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(v ArchiveRequest) bool { return len(v.Targets) == 1 && v.Targets[0] == ArchiveTargetVisibility }), mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ URI: "test:///history/archival", VisibilityURI: "test:///visibility/archival", Targets: []ArchivalTarget{ArchiveTargetHistory, ArchiveTargetVisibility}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) s.True(resp.HistoryArchivedInline) } func (s *clientSuite) TestArchiveInline_VisibilityFail_HistoryFail() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.historyArchiver, nil).Times(1) s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.visibilityArchiver, nil).Times(1) s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("some random error")).Once() s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("some random error")).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveFailureCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveFailureCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalCountPerDomain).Once() s.cadenceClient.On("SignalWithStartWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(v ArchiveRequest) bool { return len(v.Targets) == 2 }), mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ URI: "test:///history/archival", VisibilityURI: "test:///visibility/archival", Targets: []ArchivalTarget{ArchiveTargetHistory, ArchiveTargetVisibility}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) s.False(resp.HistoryArchivedInline) } func (s *clientSuite) TestArchiveInline_VisibilitySuccess_HistorySuccess() { scopeDomain := &mmocks.Scope{} s.archiverProvider.EXPECT().GetHistoryArchiver(gomock.Any(), gomock.Any()).Return(s.historyArchiver, nil).Times(1) s.archiverProvider.EXPECT().GetVisibilityArchiver(gomock.Any(), gomock.Any()).Return(s.visibilityArchiver, nil).Times(1) s.historyArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.visibilityArchiver.On("Archive", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryInlineArchiveAttemptCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityInlineArchiveAttemptCountPerDomain).Once() resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ URI: "test:///history/archival", VisibilityURI: "test:///visibility/archival", Targets: []ArchivalTarget{ArchiveTargetHistory, ArchiveTargetVisibility}, DomainName: "test_domain_name", }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) s.True(resp.HistoryArchivedInline) } func (s *clientSuite) TestArchiveSendSignal_Success() { scopeDomain := &mmocks.Scope{} s.cadenceClient.On("SignalWithStartWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.MatchedBy(func(v ArchiveRequest) bool { return len(v.Targets) == 2 }), mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) s.metricsScope.On("Tagged", mock.Anything).Return(scopeDomain) scopeDomain.On("IncCounter", metrics.ArchiverClientHistoryRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientVisibilityRequestCountPerDomain).Once() scopeDomain.On("IncCounter", metrics.ArchiverClientSendSignalCountPerDomain).Once() resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ URI: "test:///history/archival", VisibilityURI: "test:///visibility/archival", Targets: []ArchivalTarget{ArchiveTargetHistory, ArchiveTargetVisibility}, DomainName: "test_domain_name", }, AttemptArchiveInline: false, }) s.NoError(err) s.NotNil(resp) s.False(resp.HistoryArchivedInline) } func (s *clientSuite) TestArchiveUnknownTarget() { s.metricsScope.On("Tagged", mock.Anything).Return(&mmocks.Scope{}) resp, err := s.client.Archive(context.Background(), &ClientRequest{ ArchiveRequest: &ArchiveRequest{ Targets: []ArchivalTarget{3}, }, AttemptArchiveInline: true, }) s.NoError(err) s.NotNil(resp) s.False(resp.HistoryArchivedInline) } ================================================ FILE: service/worker/archiver/client_worker.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "context" "time" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/activity" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" ) type ( // ClientWorker is a cadence client worker ClientWorker interface { Start() error Stop() } clientWorker struct { worker worker.Worker domainCache cache.DomainCache } // BootstrapContainer contains everything need for bootstrapping BootstrapContainer struct { PublicClient workflowserviceclient.Interface MetricsClient metrics.Client Logger log.Logger HistoryV2Manager persistence.HistoryManager DomainCache cache.DomainCache Config *Config ArchiverProvider provider.ArchiverProvider } // Config for ClientWorker Config struct { ArchiverConcurrency dynamicproperties.IntPropertyFn ArchivalsPerIteration dynamicproperties.IntPropertyFn TimeLimitPerArchivalIteration dynamicproperties.DurationPropertyFn AllowArchivingIncompleteHistory dynamicproperties.BoolPropertyFn } contextKey int ) const ( workflowIDPrefix = "cadence-archival" decisionTaskList = "cadence-archival-tl" signalName = "cadence-archival-signal" archivalWorkflowFnName = "archivalWorkflow" workflowStartToCloseTimeout = time.Hour * 24 * 30 workflowTaskStartToCloseTimeout = time.Minute bootstrapContainerKey contextKey = iota ) // these globals exist as a work around because no primitive exists to pass such objects to workflow code var ( globalLogger log.Logger globalMetricsClient metrics.Client globalConfig *Config ) func init() { workflow.RegisterWithOptions(archivalWorkflow, workflow.RegisterOptions{Name: archivalWorkflowFnName}) activity.RegisterWithOptions(uploadHistoryActivity, activity.RegisterOptions{Name: uploadHistoryActivityFnName}) activity.RegisterWithOptions(deleteHistoryActivity, activity.RegisterOptions{Name: deleteHistoryActivityFnName}) activity.RegisterWithOptions(archiveVisibilityActivity, activity.RegisterOptions{Name: archiveVisibilityActivityFnName}) } // NewClientWorker returns a new ClientWorker func NewClientWorker(container *BootstrapContainer) ClientWorker { globalLogger = container.Logger.WithTags(tag.ComponentArchiver, tag.WorkflowDomainName(constants.SystemLocalDomainName)) globalMetricsClient = container.MetricsClient globalConfig = container.Config actCtx := context.WithValue(context.Background(), bootstrapContainerKey, container) wo := worker.Options{ BackgroundActivityContext: actCtx, } return &clientWorker{ worker: worker.New(container.PublicClient, constants.SystemLocalDomainName, decisionTaskList, wo), domainCache: container.DomainCache, } } // Start the ClientWorker func (w *clientWorker) Start() error { if err := w.worker.Start(); err != nil { w.worker.Stop() return err } return nil } // Stop the ClientWorker func (w *clientWorker) Stop() { w.worker.Stop() w.domainCache.Stop() } ================================================ FILE: service/worker/archiver/handler.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "time" "go.uber.org/cadence" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) //go:generate mockgen -package=$GOPACKAGE -destination=handler_mock.go -self_package=github.com/uber/cadence/service/worker/archiver github.com/uber/cadence/service/worker/archiver Handler type ( // Handler is used to process archival requests Handler interface { Start() Finished() []uint64 } handler struct { ctx workflow.Context logger log.Logger metricsClient metrics.Client concurrency int requestCh workflow.Channel resultCh workflow.Channel } ) // NewHandler returns a new Handler func NewHandler( ctx workflow.Context, logger log.Logger, metricsClient metrics.Client, concurrency int, requestCh workflow.Channel, ) Handler { return &handler{ ctx: ctx, logger: logger, metricsClient: metricsClient, concurrency: concurrency, requestCh: requestCh, resultCh: workflow.NewChannel(ctx), } } // Start spawns concurrency count of coroutine to handle archivals (does not block). func (h *handler) Start() { h.metricsClient.IncCounter(metrics.ArchiverScope, metrics.ArchiverStartedCount) for i := 0; i < h.concurrency; i++ { workflow.Go(h.ctx, func(ctx workflow.Context) { h.metricsClient.IncCounter(metrics.ArchiverScope, metrics.ArchiverCoroutineStartedCount) var handledHashes []uint64 for { var request ArchiveRequest if more := h.requestCh.Receive(ctx, &request); !more { break } h.handleRequest(ctx, &request) handledHashes = append(handledHashes, hash(request)) } h.resultCh.Send(ctx, handledHashes) h.metricsClient.IncCounter(metrics.ArchiverScope, metrics.ArchiverCoroutineStoppedCount) }) } } // Finished will block until all work has been finished. // Returns hashes of requests handled. func (h *handler) Finished() []uint64 { var handledHashes []uint64 for i := 0; i < h.concurrency; i++ { var subResult []uint64 h.resultCh.Receive(h.ctx, &subResult) handledHashes = append(handledHashes, subResult...) } h.metricsClient.IncCounter(metrics.ArchiverScope, metrics.ArchiverStoppedCount) return handledHashes } func (h *handler) handleRequest(ctx workflow.Context, request *ArchiveRequest) { // For backward compatibility targets := request.Targets if len(targets) == 0 { targets = append(targets, ArchiveTargetHistory) } pendingRequests := []workflow.Channel{} for _, target := range targets { doneCh := workflow.NewChannel(ctx) pendingRequests = append(pendingRequests, doneCh) switch target { case ArchiveTargetHistory: workflow.Go(ctx, func(ctx workflow.Context) { h.handleHistoryRequest(ctx, request) doneCh.Close() }) case ArchiveTargetVisibility: workflow.Go(ctx, func(ctx workflow.Context) { h.handleVisibilityRequest(ctx, request) doneCh.Close() }) default: doneCh.Close() } } for _, doneCh := range pendingRequests { doneCh.Receive(ctx, nil) } } func (h *handler) handleHistoryRequest(ctx workflow.Context, request *ArchiveRequest) { sw := h.metricsClient.StartTimer(metrics.ArchiverScope, metrics.ArchiverHandleHistoryRequestLatency) logger := tagLoggerWithHistoryRequest(h.logger, request) ao := workflow.ActivityOptions{ ScheduleToStartTimeout: 1 * time.Minute, StartToCloseTimeout: 1 * time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2.0, ExpirationInterval: 5 * time.Minute, NonRetriableErrorReasons: uploadHistoryActivityNonRetryableErrors, }, } actCtx := workflow.WithActivityOptions(ctx, ao) uploadSW := h.metricsClient.StartTimer(metrics.ArchiverScope, metrics.ArchiverUploadWithRetriesLatency) err := workflow.ExecuteActivity(actCtx, uploadHistoryActivityFnName, *request).Get(actCtx, nil) if err != nil { logger.Error("failed to archive history, will move on to deleting history without archiving", tag.Error(err)) h.metricsClient.IncCounter(metrics.ArchiverScope, metrics.ArchiverUploadFailedAllRetriesCount) } else { h.metricsClient.IncCounter(metrics.ArchiverScope, metrics.ArchiverUploadSuccessCount) } uploadSW.Stop() lao := workflow.LocalActivityOptions{ ScheduleToCloseTimeout: 1 * time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2.0, ExpirationInterval: 5 * time.Minute, NonRetriableErrorReasons: deleteHistoryActivityNonRetryableErrors, }, } deleteSW := h.metricsClient.StartTimer(metrics.ArchiverScope, metrics.ArchiverDeleteWithRetriesLatency) localActCtx := workflow.WithLocalActivityOptions(ctx, lao) err = workflow.ExecuteLocalActivity(localActCtx, deleteHistoryActivity, *request).Get(localActCtx, nil) if err != nil { logger.Error("deleting history failed, this means zombie histories are left", tag.Error(err)) h.metricsClient.IncCounter(metrics.ArchiverScope, metrics.ArchiverDeleteFailedAllRetriesCount) } else { h.metricsClient.IncCounter(metrics.ArchiverScope, metrics.ArchiverDeleteSuccessCount) } deleteSW.Stop() sw.Stop() } func (h *handler) handleVisibilityRequest(ctx workflow.Context, request *ArchiveRequest) { sw := h.metricsClient.StartTimer(metrics.ArchiverScope, metrics.ArchiverHandleVisibilityRequestLatency) logger := tagLoggerWithVisibilityRequest(h.logger, request) ao := workflow.ActivityOptions{ ScheduleToStartTimeout: 1 * time.Minute, StartToCloseTimeout: 1 * time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2.0, ExpirationInterval: 5 * time.Minute, NonRetriableErrorReasons: uploadHistoryActivityNonRetryableErrors, }, } actCtx := workflow.WithActivityOptions(ctx, ao) err := workflow.ExecuteActivity(actCtx, archiveVisibilityActivityFnName, *request).Get(actCtx, nil) if err != nil { logger.Error("failed to archive workflow visibility record", tag.Error(err)) h.metricsClient.IncCounter(metrics.ArchiverScope, metrics.ArchiverHandleVisibilityFailedAllRetiresCount) } else { h.metricsClient.IncCounter(metrics.ArchiverScope, metrics.ArchiverHandleVisibilitySuccessCount) } sw.Stop() } ================================================ FILE: service/worker/archiver/handler_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/worker/archiver (interfaces: Handler) // // Generated by this command: // // mockgen -package=archiver -destination=handler_mock.go -self_package=github.com/uber/cadence/service/worker/archiver github.com/uber/cadence/service/worker/archiver Handler // // Package archiver is a generated GoMock package. package archiver import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockHandler is a mock of Handler interface. type MockHandler struct { ctrl *gomock.Controller recorder *MockHandlerMockRecorder isgomock struct{} } // MockHandlerMockRecorder is the mock recorder for MockHandler. type MockHandlerMockRecorder struct { mock *MockHandler } // NewMockHandler creates a new mock instance. func NewMockHandler(ctrl *gomock.Controller) *MockHandler { mock := &MockHandler{ctrl: ctrl} mock.recorder = &MockHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHandler) EXPECT() *MockHandlerMockRecorder { return m.recorder } // Finished mocks base method. func (m *MockHandler) Finished() []uint64 { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Finished") ret0, _ := ret[0].([]uint64) return ret0 } // Finished indicates an expected call of Finished. func (mr *MockHandlerMockRecorder) Finished() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Finished", reflect.TypeOf((*MockHandler)(nil).Finished)) } // Start mocks base method. func (m *MockHandler) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockHandlerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockHandler)(nil).Start)) } ================================================ FILE: service/worker/archiver/handler_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "context" "errors" "fmt" "math/rand" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" mmocks "github.com/uber/cadence/common/metrics/mocks" ) var ( handlerTestMetrics *mmocks.Client handlerTestLogger *log.MockLogger ) type handlerSuite struct { suite.Suite testsuite.WorkflowTestSuite } func TestHandlerSuite(t *testing.T) { suite.Run(t, new(handlerSuite)) } func (s *handlerSuite) SetupSuite() { workflow.Register(handleHistoryRequestWorkflow) workflow.Register(handleVisibilityRequestWorkflow) workflow.Register(startAndFinishArchiverWorkflow) } func (s *handlerSuite) SetupTest() { handlerTestMetrics = &mmocks.Client{} handlerTestMetrics.On("StartTimer", mock.Anything, mock.Anything).Return(metrics.NopStopwatch()) handlerTestLogger = log.NewMockLogger(gomock.NewController(s.T())) handlerTestLogger.EXPECT().WithTags(gomock.Any()).Return(handlerTestLogger).AnyTimes() } func (s *handlerSuite) TearDownTest() { handlerTestMetrics.AssertExpectations(s.T()) } func (s *handlerSuite) TestHandleHistoryRequest_UploadFails_NonRetryableError() { handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverUploadFailedAllRetriesCount).Once() handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverDeleteSuccessCount).Once() handlerTestLogger.EXPECT().Error(gomock.Any(), gomock.Any()).Times(1) env := s.NewTestWorkflowEnvironment() env.OnActivity(uploadHistoryActivityFnName, mock.Anything, mock.Anything).Return(errors.New("some random error")) env.OnActivity(deleteHistoryActivityFnName, mock.Anything, mock.Anything).Return(nil) env.ExecuteWorkflow(handleHistoryRequestWorkflow, ArchiveRequest{}) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *handlerSuite) TestHandleHistoryRequest_UploadFails_ExpireRetryTimeout() { handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverUploadFailedAllRetriesCount).Once() handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverDeleteSuccessCount).Once() handlerTestLogger.EXPECT().Error(gomock.Any(), gomock.Any()).Times(1) timeoutErr := workflow.NewTimeoutError(shared.TimeoutTypeStartToClose) env := s.NewTestWorkflowEnvironment() env.OnActivity(uploadHistoryActivityFnName, mock.Anything, mock.Anything).Return(timeoutErr) env.OnActivity(deleteHistoryActivityFnName, mock.Anything, mock.Anything).Return(nil) env.ExecuteWorkflow(handleHistoryRequestWorkflow, ArchiveRequest{}) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *handlerSuite) TestHandleHistoryRequest_UploadSuccess() { handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverUploadSuccessCount).Once() handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverDeleteSuccessCount).Once() env := s.NewTestWorkflowEnvironment() env.OnActivity(uploadHistoryActivityFnName, mock.Anything, mock.Anything).Return(nil) env.OnActivity(deleteHistoryActivityFnName, mock.Anything, mock.Anything).Return(nil) env.ExecuteWorkflow(handleHistoryRequestWorkflow, ArchiveRequest{}) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *handlerSuite) TestHandleHistoryRequest_DeleteFails_NonRetryableError() { handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverUploadSuccessCount).Once() handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverDeleteFailedAllRetriesCount).Once() handlerTestLogger.EXPECT().Error(gomock.Any(), gomock.Any()).Times(1) env := s.NewTestWorkflowEnvironment() env.OnActivity(uploadHistoryActivityFnName, mock.Anything, mock.Anything).Return(nil) env.OnActivity(deleteHistoryActivityFnName, mock.Anything, mock.Anything).Return(func(context.Context, ArchiveRequest) error { return cadence.NewCustomError(errDeleteNonRetriable.Error()) }) env.ExecuteWorkflow(handleHistoryRequestWorkflow, ArchiveRequest{}) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *handlerSuite) TestHandleHistoryRequest_DeleteFailsThenSucceeds() { handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverUploadSuccessCount).Once() handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverDeleteSuccessCount).Once() env := s.NewTestWorkflowEnvironment() env.OnActivity(uploadHistoryActivityFnName, mock.Anything, mock.Anything).Return(nil) firstRun := true env.OnActivity(deleteHistoryActivityFnName, mock.Anything, mock.Anything).Return(func(context.Context, ArchiveRequest) error { if firstRun { firstRun = false return errors.New("some retryable error") } return nil }) env.ExecuteWorkflow(handleHistoryRequestWorkflow, ArchiveRequest{}) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *handlerSuite) TestHandleVisibilityRequest_Fail() { handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverHandleVisibilityFailedAllRetiresCount).Once() handlerTestLogger.EXPECT().Error(gomock.Any(), gomock.Any()).Times(1) env := s.NewTestWorkflowEnvironment() env.OnActivity(archiveVisibilityActivityFnName, mock.Anything, mock.Anything).Return(errors.New("some random error")) env.ExecuteWorkflow(handleVisibilityRequestWorkflow, ArchiveRequest{}) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *handlerSuite) TestHandleVisibilityRequest_Success() { handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverHandleVisibilitySuccessCount).Once() env := s.NewTestWorkflowEnvironment() env.OnActivity(archiveVisibilityActivityFnName, mock.Anything, mock.Anything).Return(nil) env.ExecuteWorkflow(handleVisibilityRequestWorkflow, ArchiveRequest{}) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *handlerSuite) TestRunArchiver() { numRequests := 1000 concurrency := 10 handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverUploadSuccessCount).Times(numRequests) handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverDeleteSuccessCount).Times(numRequests) handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverHandleVisibilitySuccessCount).Times(numRequests) handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverStartedCount).Once() handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverCoroutineStartedCount).Times(concurrency) handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverCoroutineStoppedCount).Times(concurrency) handlerTestMetrics.On("IncCounter", metrics.ArchiverScope, metrics.ArchiverStoppedCount).Once() env := s.NewTestWorkflowEnvironment() env.OnActivity(uploadHistoryActivityFnName, mock.Anything, mock.Anything).Return(nil) env.OnActivity(deleteHistoryActivityFnName, mock.Anything, mock.Anything).Return(nil) env.OnActivity(archiveVisibilityActivityFnName, mock.Anything, mock.Anything).Return(nil) env.ExecuteWorkflow(startAndFinishArchiverWorkflow, concurrency, numRequests) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func handleHistoryRequestWorkflow(ctx workflow.Context, request ArchiveRequest) error { handler := NewHandler(ctx, handlerTestLogger, handlerTestMetrics, 0, nil).(*handler) handler.handleHistoryRequest(ctx, &request) return nil } func handleVisibilityRequestWorkflow(ctx workflow.Context, request ArchiveRequest) error { handler := NewHandler(ctx, handlerTestLogger, handlerTestMetrics, 0, nil).(*handler) handler.handleVisibilityRequest(ctx, &request) return nil } func startAndFinishArchiverWorkflow(ctx workflow.Context, concurrency int, numRequests int) error { requestCh := workflow.NewBufferedChannel(ctx, numRequests) handler := NewHandler(ctx, handlerTestLogger, handlerTestMetrics, concurrency, requestCh) handler.Start() sentHashes := make([]uint64, numRequests) workflow.Go(ctx, func(ctx workflow.Context) { for i := 0; i < numRequests; i++ { ar, hash := randomArchiveRequest() requestCh.Send(ctx, ar) sentHashes[i] = hash } requestCh.Close() }) handledHashes := handler.Finished() if !hashesEqual(handledHashes, sentHashes) { return errors.New("handled hashes does not equal sent hashes") } return nil } func randomArchiveRequest() (ArchiveRequest, uint64) { ar := ArchiveRequest{ DomainID: fmt.Sprintf("%v", rand.Intn(1000)), WorkflowID: fmt.Sprintf("%v", rand.Intn(1000)), RunID: fmt.Sprintf("%v", rand.Intn(1000)), Targets: []ArchivalTarget{ArchiveTargetHistory, ArchiveTargetVisibility}, } return ar, hash(ar) } ================================================ FILE: service/worker/archiver/pump.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "time" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" ) //go:generate mockgen -package=$GOPACKAGE -destination=pump_mock.go -self_package=github.com/uber/cadence/service/worker/archiver github.com/uber/cadence/service/worker/archiver Pump type ( // Pump pumps archival requests into request channel Pump interface { Run() PumpResult } // PumpResult is the result of pumping requests into request channel PumpResult struct { PumpedHashes []uint64 UnhandledCarryover []ArchiveRequest TimeoutWithoutSignals bool } pump struct { ctx workflow.Context logger log.Logger metricsClient metrics.Client carryover []ArchiveRequest timeout time.Duration requestLimit int requestCh workflow.Channel signalCh workflow.Channel } ) // NewPump returns a new Pump func NewPump( ctx workflow.Context, logger log.Logger, metricsClient metrics.Client, carryover []ArchiveRequest, timeout time.Duration, requestLimit int, requestCh workflow.Channel, signalCh workflow.Channel, ) Pump { return &pump{ ctx: ctx, logger: logger, metricsClient: metricsClient, carryover: carryover, timeout: timeout, requestLimit: requestLimit, requestCh: requestCh, signalCh: signalCh, } } // Run pumps requests into request channel. // Blocks until either timout occurs or request limit is satisfied. // Returns a PumpResult which contains a summary of what was pumped. // Upon returning request channel is closed. func (p *pump) Run() PumpResult { sw := p.metricsClient.StartTimer(metrics.ArchiverPumpScope, metrics.CadenceLatency) carryoverBoundIndex := len(p.carryover) if carryoverBoundIndex > p.requestLimit { carryoverBoundIndex = p.requestLimit } var unhandledCarryover []ArchiveRequest for i := carryoverBoundIndex; i < len(p.carryover); i++ { unhandledCarryover = append(unhandledCarryover, p.carryover[i]) } p.metricsClient.UpdateGauge(metrics.ArchiverPumpScope, metrics.ArchiverBacklogSizeGauge, float64(len(unhandledCarryover))) pumpResult := PumpResult{ UnhandledCarryover: unhandledCarryover, } for i := 0; i < carryoverBoundIndex; i++ { request := p.carryover[i] p.requestCh.Send(p.ctx, request) pumpResult.PumpedHashes = append(pumpResult.PumpedHashes, hash(request)) } if len(pumpResult.PumpedHashes) == p.requestLimit { sw.Stop() p.requestCh.Close() return pumpResult } selector := workflow.NewSelector(p.ctx) finished := false selector.AddFuture(workflow.NewTimer(p.ctx, p.timeout), func(_ workflow.Future) { p.metricsClient.IncCounter(metrics.ArchiverPumpScope, metrics.ArchiverPumpTimeoutCount) if len(p.carryover) == len(pumpResult.PumpedHashes) { p.metricsClient.IncCounter(metrics.ArchiverPumpScope, metrics.ArchiverPumpTimeoutWithoutSignalsCount) pumpResult.TimeoutWithoutSignals = true } finished = true }) selector.AddReceive(p.signalCh, func(ch workflow.Channel, more bool) { if !more { p.logger.Error("signal channel channel closed unexpectedly") p.metricsClient.IncCounter(metrics.ArchiverPumpScope, metrics.ArchiverPumpSignalChannelClosedCount) finished = true return } var request ArchiveRequest ch.Receive(p.ctx, &request) p.requestCh.Send(p.ctx, request) pumpResult.PumpedHashes = append(pumpResult.PumpedHashes, hash(request)) finished = len(pumpResult.PumpedHashes) == p.requestLimit if finished { p.metricsClient.IncCounter(metrics.ArchiverPumpScope, metrics.ArchiverPumpSignalThresholdCount) } }) for !finished { selector.Select(p.ctx) } sw.Stop() p.requestCh.Close() return pumpResult } ================================================ FILE: service/worker/archiver/pump_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/worker/archiver (interfaces: Pump) // // Generated by this command: // // mockgen -package=archiver -destination=pump_mock.go -self_package=github.com/uber/cadence/service/worker/archiver github.com/uber/cadence/service/worker/archiver Pump // // Package archiver is a generated GoMock package. package archiver import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockPump is a mock of Pump interface. type MockPump struct { ctrl *gomock.Controller recorder *MockPumpMockRecorder isgomock struct{} } // MockPumpMockRecorder is the mock recorder for MockPump. type MockPumpMockRecorder struct { mock *MockPump } // NewMockPump creates a new mock instance. func NewMockPump(ctrl *gomock.Controller) *MockPump { mock := &MockPump{ctrl: ctrl} mock.recorder = &MockPumpMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPump) EXPECT() *MockPumpMockRecorder { return m.recorder } // Run mocks base method. func (m *MockPump) Run() PumpResult { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Run") ret0, _ := ret[0].(PumpResult) return ret0 } // Run indicates an expected call of Run. func (mr *MockPumpMockRecorder) Run() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockPump)(nil).Run)) } ================================================ FILE: service/worker/archiver/pump_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "errors" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" mmocks "github.com/uber/cadence/common/metrics/mocks" ) var ( pumpTestMetrics *mmocks.Client pumpTestLogger *log.MockLogger ) type pumpSuite struct { suite.Suite testsuite.WorkflowTestSuite } func TestPumpSuite(t *testing.T) { suite.Run(t, new(pumpSuite)) } func (s *pumpSuite) SetupSuite() { workflow.Register(carryoverSatisfiesLimitWorkflow) workflow.Register(pumpWorkflow) workflow.Register(signalChClosePumpWorkflow) workflow.Register(signalAndCarryoverPumpWorkflow) } func (s *pumpSuite) SetupTest() { pumpTestMetrics = &mmocks.Client{} pumpTestMetrics.On("StartTimer", mock.Anything, mock.Anything).Return(metrics.NopStopwatch()).Once() pumpTestLogger = log.NewMockLogger(gomock.NewController(s.T())) } func (s *pumpSuite) TearDownTest() { pumpTestMetrics.AssertExpectations(s.T()) } func (s *pumpSuite) TestPumpRun_CarryoverLargerThanLimit() { pumpTestMetrics.On("UpdateGauge", metrics.ArchiverPumpScope, metrics.ArchiverBacklogSizeGauge, float64(1)).Once() env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(carryoverSatisfiesLimitWorkflow, 10, 11) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *pumpSuite) TestPumpRun_CarryoverExactlyMatchesLimit() { pumpTestMetrics.On("UpdateGauge", metrics.ArchiverPumpScope, metrics.ArchiverBacklogSizeGauge, float64(0)).Once() env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(carryoverSatisfiesLimitWorkflow, 10, 10) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *pumpSuite) TestPumpRun_TimeoutWithoutSignals() { pumpTestMetrics.On("UpdateGauge", metrics.ArchiverPumpScope, metrics.ArchiverBacklogSizeGauge, float64(0)).Once() pumpTestMetrics.On("IncCounter", metrics.ArchiverPumpScope, metrics.ArchiverPumpTimeoutCount).Once() pumpTestMetrics.On("IncCounter", metrics.ArchiverPumpScope, metrics.ArchiverPumpTimeoutWithoutSignalsCount).Once() env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(pumpWorkflow, 10, 0) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *pumpSuite) TestPumpRun_TimeoutWithSignals() { pumpTestMetrics.On("UpdateGauge", metrics.ArchiverPumpScope, metrics.ArchiverBacklogSizeGauge, float64(0)).Once() pumpTestMetrics.On("IncCounter", metrics.ArchiverPumpScope, metrics.ArchiverPumpTimeoutCount).Once() env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(pumpWorkflow, 10, 5) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *pumpSuite) TestPumpRun_SignalsGottenSatisfyLimit() { pumpTestMetrics.On("UpdateGauge", metrics.ArchiverPumpScope, metrics.ArchiverBacklogSizeGauge, float64(0)).Once() pumpTestMetrics.On("IncCounter", metrics.ArchiverPumpScope, metrics.ArchiverPumpSignalThresholdCount).Once() env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(pumpWorkflow, 10, 10) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *pumpSuite) TestPumpRun_SignalsAndCarryover() { pumpTestMetrics.On("UpdateGauge", metrics.ArchiverPumpScope, metrics.ArchiverBacklogSizeGauge, float64(0)).Once() pumpTestMetrics.On("IncCounter", metrics.ArchiverPumpScope, metrics.ArchiverPumpSignalThresholdCount).Once() env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(signalAndCarryoverPumpWorkflow, 10, 5, 5) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func (s *pumpSuite) TestPumpRun_SignalChannelClosedUnexpectedly() { pumpTestMetrics.On("UpdateGauge", metrics.ArchiverPumpScope, metrics.ArchiverBacklogSizeGauge, float64(0)).Once() pumpTestMetrics.On("IncCounter", metrics.ArchiverPumpScope, metrics.ArchiverPumpSignalChannelClosedCount).Once() pumpTestLogger.EXPECT().Error(gomock.Any(), gomock.Any()).Times(1) env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(signalChClosePumpWorkflow, 10, 5) env.AssertExpectations(s.T()) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func carryoverSatisfiesLimitWorkflow(ctx workflow.Context, requestLimit int, carryoverSize int) error { unhandledCarryoverSize := carryoverSize - requestLimit carryover, carryoverHashes := randomCarryover(carryoverSize) requestCh := workflow.NewBufferedChannel(ctx, requestLimit) pump := NewPump(ctx, pumpTestLogger, pumpTestMetrics, carryover, time.Nanosecond, requestLimit, requestCh, nil) actual := pump.Run() expected := PumpResult{ PumpedHashes: carryoverHashes[:len(carryoverHashes)-unhandledCarryoverSize], UnhandledCarryover: carryover[len(carryover)-unhandledCarryoverSize:], TimeoutWithoutSignals: false, } if !pumpResultsEqual(expected, actual) { return errors.New("did not get expected pump result") } if !channelContainsExpected(ctx, requestCh, carryover[:len(carryover)-unhandledCarryoverSize]) { return errors.New("request channel was not populated with expected values") } return nil } func runPumpWorkflow(ctx workflow.Context, requestLimit int, numRequests int, signalCh workflow.Channel, signalHashes []uint64, signalsSent []ArchiveRequest) error { requestCh := workflow.NewBufferedChannel(ctx, requestLimit) pump := NewPump(ctx, pumpTestLogger, pumpTestMetrics, nil, time.Nanosecond, requestLimit, requestCh, signalCh) actual := pump.Run() expected := PumpResult{ PumpedHashes: signalHashes, UnhandledCarryover: nil, TimeoutWithoutSignals: numRequests == 0, } if !pumpResultsEqual(expected, actual) { return errors.New("did not get expected pump result") } if !channelContainsExpected(ctx, requestCh, signalsSent) { return errors.New("request channel was not populated with expected values") } return nil } func pumpWorkflow(ctx workflow.Context, requestLimit int, numRequests int) error { signalCh := workflow.NewBufferedChannel(ctx, requestLimit) signalsSent, signalHashes := sendRequestsToChannel(ctx, signalCh, numRequests) return runPumpWorkflow(ctx, requestLimit, numRequests, signalCh, signalHashes, signalsSent) } func signalChClosePumpWorkflow(ctx workflow.Context, requestLimit int, numRequests int) error { signalCh := workflow.NewBufferedChannel(ctx, requestLimit) signalsSent, signalHashes := sendRequestsToChannelBlocking(ctx, signalCh, numRequests) signalCh.Close() return runPumpWorkflow(ctx, requestLimit, numRequests, signalCh, signalHashes, signalsSent) } func signalAndCarryoverPumpWorkflow(ctx workflow.Context, requestLimit int, carryoverSize, numSignals int) error { signalCh := workflow.NewBufferedChannel(ctx, requestLimit) signalsSent, signalHashes := sendRequestsToChannel(ctx, signalCh, numSignals) carryover, carryoverHashes := randomCarryover(carryoverSize) requestCh := workflow.NewBufferedChannel(ctx, requestLimit) pump := NewPump(ctx, pumpTestLogger, pumpTestMetrics, carryover, time.Nanosecond, requestLimit, requestCh, signalCh) actual := pump.Run() expected := PumpResult{ PumpedHashes: append(carryoverHashes, signalHashes...), UnhandledCarryover: nil, TimeoutWithoutSignals: false, } if !pumpResultsEqual(expected, actual) { return errors.New("did not get expected pump result") } if !channelContainsExpected(ctx, requestCh, append(carryover, signalsSent...)) { return errors.New("request channel was not populated with expected values") } return nil } func sendRequestsToChannel(ctx workflow.Context, ch workflow.Channel, numRequests int) ([]ArchiveRequest, []uint64) { requests := make([]ArchiveRequest, numRequests) hashes := make([]uint64, numRequests) workflow.Go(ctx, func(ctx workflow.Context) { for i := 0; i < numRequests; i++ { requests[i], hashes[i] = randomArchiveRequest() ch.Send(ctx, requests[i]) } }) return requests, hashes } func sendRequestsToChannelBlocking(ctx workflow.Context, ch workflow.Channel, numRequests int) ([]ArchiveRequest, []uint64) { requests := make([]ArchiveRequest, numRequests) hashes := make([]uint64, numRequests) for i := 0; i < numRequests; i++ { requests[i], hashes[i] = randomArchiveRequest() ch.Send(ctx, requests[i]) } return requests, hashes } func channelContainsExpected(ctx workflow.Context, ch workflow.Channel, expected []ArchiveRequest) bool { for i := 0; i < len(expected); i++ { var actual ArchiveRequest if !ch.Receive(ctx, &actual) { return false } if hash(expected[i]) != hash(actual) { return false } } return !ch.Receive(ctx, nil) } func randomCarryover(count int) ([]ArchiveRequest, []uint64) { carryover := make([]ArchiveRequest, count) hashes := make([]uint64, count) for i := 0; i < count; i++ { carryover[i], hashes[i] = randomArchiveRequest() } return carryover, hashes } func pumpResultsEqual(expected PumpResult, actual PumpResult) bool { return expected.TimeoutWithoutSignals == actual.TimeoutWithoutSignals && requestsEqual(expected.UnhandledCarryover, actual.UnhandledCarryover) && hashesEqual(expected.PumpedHashes, actual.PumpedHashes) } func requestsEqual(expected []ArchiveRequest, actual []ArchiveRequest) bool { if len(expected) != len(actual) { return false } for i := 0; i < len(expected); i++ { if hash(expected[i]) != hash(actual[i]) { return false } } return true } ================================================ FILE: service/worker/archiver/replay_metrics_client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "time" "github.com/uber-go/tally" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/metrics" ) type ( replayMetricsClient struct { client metrics.Client ctx workflow.Context } replayMetricsScope struct { scope metrics.Scope ctx workflow.Context } ) // NewReplayMetricsClient creates a metrics client which is aware of cadence's replay mode func NewReplayMetricsClient(client metrics.Client, ctx workflow.Context) metrics.Client { return &replayMetricsClient{ client: client, ctx: ctx, } } // IncCounter increments a counter metric func (r *replayMetricsClient) IncCounter(scope metrics.ScopeIdx, counter metrics.MetricIdx) { if workflow.IsReplaying(r.ctx) { return } r.client.IncCounter(scope, counter) } // AddCounter adds delta to the counter metric func (r *replayMetricsClient) AddCounter(scope metrics.ScopeIdx, counter metrics.MetricIdx, delta int64) { if workflow.IsReplaying(r.ctx) { return } r.client.AddCounter(scope, counter, delta) } // StartTimer starts a timer for the given metric name. Time will be recorded when stopwatch is stopped. func (r *replayMetricsClient) StartTimer(scope metrics.ScopeIdx, timer metrics.MetricIdx) tally.Stopwatch { if workflow.IsReplaying(r.ctx) { return metrics.NopStopwatch() } return r.client.StartTimer(scope, timer) } // RecordTimer starts a timer for the given metric name func (r *replayMetricsClient) RecordTimer(scope metrics.ScopeIdx, timer metrics.MetricIdx, d time.Duration) { if workflow.IsReplaying(r.ctx) { return } r.client.RecordTimer(scope, timer, d) } // RecordHistogramDuration record and emit a duration func (r *replayMetricsClient) RecordHistogramDuration(scope metrics.ScopeIdx, timer metrics.MetricIdx, d time.Duration) { if workflow.IsReplaying(r.ctx) { return } r.client.RecordHistogramDuration(scope, timer, d) } // UpdateGauge reports Gauge type absolute value metric func (r *replayMetricsClient) UpdateGauge(scope metrics.ScopeIdx, gauge metrics.MetricIdx, value float64) { if workflow.IsReplaying(r.ctx) { return } r.client.UpdateGauge(scope, gauge, value) } // Scope returns a client that adds the given tags to all metrics func (r *replayMetricsClient) Scope(scope metrics.ScopeIdx, tags ...metrics.Tag) metrics.Scope { return NewReplayMetricsScope(r.client.Scope(scope, tags...), r.ctx) } // NewReplayMetricsScope creates a metrics scope which is aware of cadence's replay mode func NewReplayMetricsScope(scope metrics.Scope, ctx workflow.Context) metrics.Scope { return &replayMetricsScope{ scope: scope, ctx: ctx, } } // IncCounter increments a counter metric func (r *replayMetricsScope) IncCounter(counter metrics.MetricIdx) { if workflow.IsReplaying(r.ctx) { return } r.scope.IncCounter(counter) } // AddCounter adds delta to the counter metric func (r *replayMetricsScope) AddCounter(counter metrics.MetricIdx, delta int64) { if workflow.IsReplaying(r.ctx) { return } r.scope.AddCounter(counter, delta) } // StartTimer starts a timer for the given metric name. Time will be recorded when stopwatch is stopped. func (r *replayMetricsScope) StartTimer(timer metrics.MetricIdx) metrics.Stopwatch { if workflow.IsReplaying(r.ctx) { return metrics.NewTestStopwatch() } return r.scope.StartTimer(timer) } // RecordTimer starts a timer for the given metric name func (r *replayMetricsScope) RecordTimer(timer metrics.MetricIdx, d time.Duration) { if workflow.IsReplaying(r.ctx) { return } r.scope.RecordTimer(timer, d) } // RecordHistogramDuration records a duration value in a histogram func (r *replayMetricsScope) RecordHistogramDuration(timer metrics.MetricIdx, d time.Duration) { if workflow.IsReplaying(r.ctx) { return } r.scope.RecordHistogramDuration(timer, d) } // RecordHistogramValue records a value in a histogram func (r *replayMetricsScope) RecordHistogramValue(timer metrics.MetricIdx, value float64) { if workflow.IsReplaying(r.ctx) { return } r.scope.RecordHistogramValue(timer, value) } func (r *replayMetricsScope) ExponentialHistogram(hist metrics.MetricIdx, d time.Duration) { if workflow.IsReplaying(r.ctx) { return } r.scope.ExponentialHistogram(hist, d) } func (r *replayMetricsScope) IntExponentialHistogram(hist metrics.MetricIdx, value int) { if workflow.IsReplaying(r.ctx) { return } r.scope.IntExponentialHistogram(hist, value) } // UpdateGauge reports Gauge type absolute value metric func (r *replayMetricsScope) UpdateGauge(gauge metrics.MetricIdx, value float64) { if workflow.IsReplaying(r.ctx) { return } r.scope.UpdateGauge(gauge, value) } // Tagged return an internal replay aware scope that can be used to add additional information to metrics func (r *replayMetricsScope) Tagged(tags ...metrics.Tag) metrics.Scope { return NewReplayMetricsScope(r.scope.Tagged(tags...), r.ctx) } ================================================ FILE: service/worker/archiver/testdata/archival_workflow_history_v1.json ================================================ [{"eventId":1,"timestamp":1569606123623079000,"eventType":"WorkflowExecutionStarted","version":-24,"taskId":6291586,"workflowExecutionStartedEventAttributes":{"workflowType":{"name":"archivalWorkflow"},"taskList":{"name":"cadence-archival-tl"},"input":"bnVsbAo=","executionStartToCloseTimeoutSeconds":2592000,"taskStartToCloseTimeoutSeconds":60,"originalExecutionRunId":"2dbedb91-4d8f-4c74-a979-4b7b9c204077","identity":"59844@ycyang-C02XW41WJGH6@","firstExecutionRunId":"2dbedb91-4d8f-4c74-a979-4b7b9c204077","attempt":0,"cronSchedule":"","firstDecisionTaskBackoffSeconds":0,"header":{}}},{"eventId":2,"timestamp":1569606123623099000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291587,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjoyLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzQwZjhlZTY3LTAyMmYtNDA4ZC04M2U1LTFkYjU3ZTJjNWJmZiIsIlJ1bklEIjoiZjJiMzYwYTAtZDkwYS00YWZhLWFkODgtYmEwNDFmYWQ2YTQyIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1JtTW1Jek5qQmhNQzFrT1RCaExUUmhabUV0WVdRNE9DMWlZVEEwTVdaaFpEWmhORElMQUJRQUFBQWtPRFF3TXpBM1lqa3RPVEEzTmkwMFpXVXlMVGd5WVRBdE5EVm1NakZrTmpGa056RTVEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":3,"timestamp":1569606123623106000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291590,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"cadence-archival-tl"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":4,"timestamp":1569606123627344000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291595,"decisionTaskStartedEventAttributes":{"scheduledEventId":3,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"de9215e7-2bcc-4294-9fac-1dc75ed159fc"}},{"eventId":5,"timestamp":1569606123634126000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291598,"decisionTaskCompletedEventAttributes":{"scheduledEventId":3,"startedEventId":4,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":6,"timestamp":1569606123634168000,"eventType":"MarkerRecorded","version":-24,"taskId":6291599,"markerRecordedEventAttributes":{"markerName":"SideEffect","details":"MAoiZXlKQmNtTm9hWFpsY2tOdmJtTjFjbkpsYm1ONUlqbzFNQ3dpUVhKamFHbDJZV3h6VUdWeVNYUmxjbUYwYVc5dUlqb3hNREF3TENKVWFXMWxiR2x0YVhSUVpYSkpkR1Z5WVhScGIyNGlPakV5T1RZd01EQXdNREF3TURBd01EQjlDZz09Igo=","decisionTaskCompletedEventId":5}},{"eventId":7,"timestamp":1569606123634180000,"eventType":"TimerStarted","version":-24,"taskId":6291600,"timerStartedEventAttributes":{"timerId":"1","startToFireTimeoutSeconds":1296000,"decisionTaskCompletedEventId":5}},{"eventId":8,"timestamp":1569606123634200000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291601,"activityTaskScheduledEventAttributes":{"activityId":"2","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjoyLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzQwZjhlZTY3LTAyMmYtNDA4ZC04M2U1LTFkYjU3ZTJjNWJmZiIsIlJ1bklEIjoiZjJiMzYwYTAtZDkwYS00YWZhLWFkODgtYmEwNDFmYWQ2YTQyIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1JtTW1Jek5qQmhNQzFrT1RCaExUUmhabUV0WVdRNE9DMWlZVEEwTVdaaFpEWmhORElMQUJRQUFBQWtPRFF3TXpBM1lqa3RPVEEzTmkwMFpXVXlMVGd5WVRBdE5EVm1NakZrTmpGa056RTVEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":5,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":9,"timestamp":1569606123638659000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291607,"activityTaskStartedEventAttributes":{"scheduledEventId":8,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"7dccfd5f-fb84-450f-b452-3a5dd5f382e9","attempt":0}},{"eventId":10,"timestamp":1569606123643937000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291608,"activityTaskCompletedEventAttributes":{"scheduledEventId":8,"startedEventId":9,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":11,"timestamp":1569606123643948000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291611,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":12,"timestamp":1569606123647878000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291615,"decisionTaskStartedEventAttributes":{"scheduledEventId":11,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-tl-scanner-tasklist-0","requestId":"774fa75d-7cef-47c8-96be-4a6db8a4cb99"}},{"eventId":13,"timestamp":1569606123654654000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291618,"decisionTaskCompletedEventAttributes":{"scheduledEventId":11,"startedEventId":12,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":14,"timestamp":1569606123654683000,"eventType":"MarkerRecorded","version":-24,"taskId":6291619,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiMyIsImFjdGl2aXR5VHlwZSI6ImRlbGV0ZUhpc3RvcnlBY3Rpdml0eSIsInJlcGxheVRpbWUiOiIyMDE5LTA5LTI3VDEwOjQyOjAzLjY0OTczMTYxMy0wNzowMCJ9Cg==","decisionTaskCompletedEventId":13}},{"eventId":15,"timestamp":1569606123918543000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291621,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzBjMGQxNDkxLWI1YzAtNGIxZC04NjliLTc0OWEwNWUwNDMyNiIsIlJ1bklEIjoiNGIwYjFiMjYtZjllMi00OGEwLWJjZGQtMTdlMmNlMTQwNzVkIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1EwWWpCaU1XSXlOaTFtT1dVeUxUUTRZVEF0WW1Oa1pDMHhOMlV5WTJVeE5EQTNOV1FMQUJRQUFBQWtZekptWVdKak4yWXROelJpT1MwMFlqYzFMVGhsT0RrdE1EbGlaakJsTlRJeU1qWmhEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":16,"timestamp":1569606123918557000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291623,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":17,"timestamp":1569606123922726000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291627,"decisionTaskStartedEventAttributes":{"scheduledEventId":16,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"081890a8-8942-4434-ac52-9e668a406ee2"}},{"eventId":18,"timestamp":1569606123928159000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291630,"decisionTaskCompletedEventAttributes":{"scheduledEventId":16,"startedEventId":17,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":19,"timestamp":1569606123928190000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291631,"activityTaskScheduledEventAttributes":{"activityId":"4","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzBjMGQxNDkxLWI1YzAtNGIxZC04NjliLTc0OWEwNWUwNDMyNiIsIlJ1bklEIjoiNGIwYjFiMjYtZjllMi00OGEwLWJjZGQtMTdlMmNlMTQwNzVkIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1EwWWpCaU1XSXlOaTFtT1dVeUxUUTRZVEF0WW1Oa1pDMHhOMlV5WTJVeE5EQTNOV1FMQUJRQUFBQWtZekptWVdKak4yWXROelJpT1MwMFlqYzFMVGhsT0RrdE1EbGlaakJsTlRJeU1qWmhEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":18,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":20,"timestamp":1569606123932704000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291636,"activityTaskStartedEventAttributes":{"scheduledEventId":19,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"d8b8a27d-e66d-4ae5-89ac-058cd336b660","attempt":0}},{"eventId":21,"timestamp":1569606123938439000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291637,"activityTaskCompletedEventAttributes":{"scheduledEventId":19,"startedEventId":20,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":22,"timestamp":1569606123938450000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291640,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":23,"timestamp":1569606123942512000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291644,"decisionTaskStartedEventAttributes":{"scheduledEventId":22,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-processor-parent-close-policy","requestId":"b66d91d8-f367-4ddd-aad3-1071f937bc48"}},{"eventId":24,"timestamp":1569606123950344000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291647,"decisionTaskCompletedEventAttributes":{"scheduledEventId":22,"startedEventId":23,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":25,"timestamp":1569606123950371000,"eventType":"MarkerRecorded","version":-24,"taskId":6291648,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiNSIsImFjdGl2aXR5VHlwZSI6ImRlbGV0ZUhpc3RvcnlBY3Rpdml0eSIsInJlcGxheVRpbWUiOiIyMDE5LTA5LTI3VDEwOjQyOjAzLjk0NDIyNDE3NS0wNzowMCJ9Cg==","decisionTaskCompletedEventId":24}},{"eventId":26,"timestamp":1569606124919680000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291650,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzAzYzI4MDRkLWNjZGMtNDI0MS05MmM2LWMzNzM5MGJhM2NkZSIsIlJ1bklEIjoiNDk4MWQzMTUtYjEyZi00YWE4LWJhOWQtNzYwNzczZmMzYTllIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1EwT1RneFpETXhOUzFpTVRKbUxUUmhZVGd0WW1FNVpDMDNOakEzTnpObVl6TmhPV1VMQUJRQUFBQWtOakZrTnpBelltSXRaR0V5T1MwME56VmhMV0ZtWlRNdE9EVTJaRGN5TXpkaU1ETTJEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":27,"timestamp":1569606124919689000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291652,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":28,"timestamp":1569606124922147000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291656,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzI0ODFiMWMzLTkyZDMtNGEyZi04NmZiLWUyM2MyNTI2ZDhmMCIsIlJ1bklEIjoiODM1YTFhZGItMTkxNy00ZWU2LTgzMDAtZGE0YmQyMTUyYzgxIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1E0TXpWaE1XRmtZaTB4T1RFM0xUUmxaVFl0T0RNd01DMWtZVFJpWkRJeE5USmpPREVMQUJRQUFBQWtOMlJoWXpSak5qTXRZelk1WmkwME56QTJMVGd6TlRndFlXTTFNbU00TkRSbFpESTREd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":29,"timestamp":1569606124924481000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291658,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkX2RmYzZiODBlLTA0NmEtNGU0Yy1iZTQyLWVlZmY0YmFlMjMxNyIsIlJ1bklEIjoiZjlmMzQwY2ItNGRkNS00YmU2LThmYTEtNDI4NWUwYTUyOTFkIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1JtT1dZek5EQmpZaTAwWkdRMUxUUmlaVFl0T0daaE1TMDBNamcxWlRCaE5USTVNV1FMQUJRQUFBQWtaRE13WmpFMVpUQXRPVFEzTkMwME9HVTVMVGcyWVdVdE56RTVOR1ExWVRreE1tUmpEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":30,"timestamp":1569606124927549000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291660,"decisionTaskStartedEventAttributes":{"scheduledEventId":27,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-tl-scanner-tasklist-0","requestId":"a4207044-03be-4913-857c-1caadff199f6"}},{"eventId":31,"timestamp":1569606124934407000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291663,"decisionTaskCompletedEventAttributes":{"scheduledEventId":27,"startedEventId":30,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":32,"timestamp":1569606124934430000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291664,"activityTaskScheduledEventAttributes":{"activityId":"6","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzAzYzI4MDRkLWNjZGMtNDI0MS05MmM2LWMzNzM5MGJhM2NkZSIsIlJ1bklEIjoiNDk4MWQzMTUtYjEyZi00YWE4LWJhOWQtNzYwNzczZmMzYTllIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1EwT1RneFpETXhOUzFpTVRKbUxUUmhZVGd0WW1FNVpDMDNOakEzTnpObVl6TmhPV1VMQUJRQUFBQWtOakZrTnpBelltSXRaR0V5T1MwME56VmhMV0ZtWlRNdE9EVTJaRGN5TXpkaU1ETTJEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":31,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":33,"timestamp":1569606124934440000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291665,"activityTaskScheduledEventAttributes":{"activityId":"7","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzI0ODFiMWMzLTkyZDMtNGEyZi04NmZiLWUyM2MyNTI2ZDhmMCIsIlJ1bklEIjoiODM1YTFhZGItMTkxNy00ZWU2LTgzMDAtZGE0YmQyMTUyYzgxIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1E0TXpWaE1XRmtZaTB4T1RFM0xUUmxaVFl0T0RNd01DMWtZVFJpWkRJeE5USmpPREVMQUJRQUFBQWtOMlJoWXpSak5qTXRZelk1WmkwME56QTJMVGd6TlRndFlXTTFNbU00TkRSbFpESTREd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":31,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":34,"timestamp":1569606124934446000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291666,"activityTaskScheduledEventAttributes":{"activityId":"8","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkX2RmYzZiODBlLTA0NmEtNGU0Yy1iZTQyLWVlZmY0YmFlMjMxNyIsIlJ1bklEIjoiZjlmMzQwY2ItNGRkNS00YmU2LThmYTEtNDI4NWUwYTUyOTFkIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1JtT1dZek5EQmpZaTAwWkdRMUxUUmlaVFl0T0daaE1TMDBNamcxWlRCaE5USTVNV1FMQUJRQUFBQWtaRE13WmpFMVpUQXRPVFEzTkMwME9HVTVMVGcyWVdVdE56RTVOR1ExWVRreE1tUmpEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":31,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":35,"timestamp":1569606124938736000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291679,"activityTaskStartedEventAttributes":{"scheduledEventId":32,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"57e8b0cb-14b3-46c5-9549-5910a4c17cc4","attempt":0}},{"eventId":36,"timestamp":1569606124945961000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291680,"activityTaskCompletedEventAttributes":{"scheduledEventId":32,"startedEventId":35,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":37,"timestamp":1569606124945974000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291683,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":38,"timestamp":1569606124940672000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291690,"activityTaskStartedEventAttributes":{"scheduledEventId":33,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"de068cc4-efd0-4ddf-afd8-c4e2272c134c","attempt":0}},{"eventId":39,"timestamp":1569606124949415000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291691,"activityTaskCompletedEventAttributes":{"scheduledEventId":33,"startedEventId":38,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":40,"timestamp":1569606124942620000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291698,"activityTaskStartedEventAttributes":{"scheduledEventId":34,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"6bfef50a-28cb-4f05-b8d5-915d58347891","attempt":0}},{"eventId":41,"timestamp":1569606124953872000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291699,"activityTaskCompletedEventAttributes":{"scheduledEventId":34,"startedEventId":40,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":42,"timestamp":1569606124957961000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291703,"decisionTaskStartedEventAttributes":{"scheduledEventId":37,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"9008323e-b89d-4c43-967f-e23846522466"}},{"eventId":43,"timestamp":1569606124964261000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291713,"decisionTaskCompletedEventAttributes":{"scheduledEventId":37,"startedEventId":42,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":44,"timestamp":1569606124964293000,"eventType":"MarkerRecorded","version":-24,"taskId":6291714,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiMTEiLCJhY3Rpdml0eVR5cGUiOiJkZWxldGVIaXN0b3J5QWN0aXZpdHkiLCJyZXBsYXlUaW1lIjoiMjAxOS0wOS0yN1QxMDo0MjowNC45NTkwODgxNDgtMDc6MDAifQo=","decisionTaskCompletedEventId":43}},{"eventId":45,"timestamp":1569606124964297000,"eventType":"MarkerRecorded","version":-24,"taskId":6291715,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiOSIsImFjdGl2aXR5VHlwZSI6ImRlbGV0ZUhpc3RvcnlBY3Rpdml0eSIsInJlcGxheVRpbWUiOiIyMDE5LTA5LTI3VDEwOjQyOjA0Ljk1OTQ2MzQyNi0wNzowMCJ9Cg==","decisionTaskCompletedEventId":43}},{"eventId":46,"timestamp":1569606124964301000,"eventType":"MarkerRecorded","version":-24,"taskId":6291716,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiMTAiLCJhY3Rpdml0eVR5cGUiOiJkZWxldGVIaXN0b3J5QWN0aXZpdHkiLCJyZXBsYXlUaW1lIjoiMjAxOS0wOS0yN1QxMDo0MjowNC45NTk3MTM3MzYtMDc6MDAifQo=","decisionTaskCompletedEventId":43}},{"eventId":47,"timestamp":1569606124979355000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291728,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjozLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzc0NmFkOGI5LTAyYTEtNGNlNy1iNzkzLTI3ZDgwNjllZGU3MyIsIlJ1bklEIjoiNTY0YTY2OWYtYmM4OS00M2I5LWE2ZjEtN2IwYzc4NGEyNmViIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1ExTmpSaE5qWTVaaTFpWXpnNUxUUXpZamt0WVRabU1TMDNZakJqTnpnMFlUSTJaV0lMQUJRQUFBQWtORGt6TVRKbFpERXRNR1kyTkMwME0yVmpMVGcwWXpRdFpXVXlaREZrWVRaak5tUmtEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":48,"timestamp":1569606124979363000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291730,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":49,"timestamp":1569606124983474000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291734,"decisionTaskStartedEventAttributes":{"scheduledEventId":48,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-processor-parent-close-policy","requestId":"82a5d9c2-1deb-494c-98ae-bb0c3681abd3"}},{"eventId":50,"timestamp":1569606124988413000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291737,"decisionTaskCompletedEventAttributes":{"scheduledEventId":48,"startedEventId":49,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":51,"timestamp":1569606124988446000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291738,"activityTaskScheduledEventAttributes":{"activityId":"12","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjozLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzc0NmFkOGI5LTAyYTEtNGNlNy1iNzkzLTI3ZDgwNjllZGU3MyIsIlJ1bklEIjoiNTY0YTY2OWYtYmM4OS00M2I5LWE2ZjEtN2IwYzc4NGEyNmViIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1ExTmpSaE5qWTVaaTFpWXpnNUxUUXpZamt0WVRabU1TMDNZakJqTnpnMFlUSTJaV0lMQUJRQUFBQWtORGt6TVRKbFpERXRNR1kyTkMwME0yVmpMVGcwWXpRdFpXVXlaREZrWVRaak5tUmtEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":50,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":52,"timestamp":1569606124992421000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291743,"activityTaskStartedEventAttributes":{"scheduledEventId":51,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"640f192d-c9b3-4ff7-8e71-3234d7bb09c5","attempt":0}},{"eventId":53,"timestamp":1569606124997683000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291744,"activityTaskCompletedEventAttributes":{"scheduledEventId":51,"startedEventId":52,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":54,"timestamp":1569606124997696000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291747,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":55,"timestamp":1569606125001619000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291751,"decisionTaskStartedEventAttributes":{"scheduledEventId":54,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-tl-scanner-tasklist-0","requestId":"0f0ce318-bc46-4439-81c9-c6288e063df5"}},{"eventId":56,"timestamp":1569606125007998000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291754,"decisionTaskCompletedEventAttributes":{"scheduledEventId":54,"startedEventId":55,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":57,"timestamp":1569606125008025000,"eventType":"MarkerRecorded","version":-24,"taskId":6291755,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiMTMiLCJhY3Rpdml0eVR5cGUiOiJkZWxldGVIaXN0b3J5QWN0aXZpdHkiLCJyZXBsYXlUaW1lIjoiMjAxOS0wOS0yN1QxMDo0MjowNS4wMDI4NTU2NjItMDc6MDAifQo=","decisionTaskCompletedEventId":56}},{"eventId":58,"timestamp":1569606125218433000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291757,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjoyLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkX2NmMzNkNzJhLWZhYzktNDA4Zi1hZDJmLThjMTFlZjFkNTQ3NSIsIlJ1bklEIjoiMDJmMjE3M2QtY2EzZC00MDMyLWI5NjEtZDNjNTYyOGRiYjIwIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1F3TW1ZeU1UY3paQzFqWVROa0xUUXdNekl0WWprMk1TMWtNMk0xTmpJNFpHSmlNakFMQUJRQUFBQWtZV1kzTlRCa01UQXRaVFl4TkMwME56QmtMVGhtT1RVdE1UaGtOekU0WWpjeVlqbGhEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":59,"timestamp":1569606125218443000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291759,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":60,"timestamp":1569606125221695000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291763,"decisionTaskStartedEventAttributes":{"scheduledEventId":59,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"8ee8cc2f-88ce-476d-b40f-e82544a9a3f9"}},{"eventId":61,"timestamp":1569606125226189000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291766,"decisionTaskCompletedEventAttributes":{"scheduledEventId":59,"startedEventId":60,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":62,"timestamp":1569606125226225000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291767,"activityTaskScheduledEventAttributes":{"activityId":"14","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjoyLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkX2NmMzNkNzJhLWZhYzktNDA4Zi1hZDJmLThjMTFlZjFkNTQ3NSIsIlJ1bklEIjoiMDJmMjE3M2QtY2EzZC00MDMyLWI5NjEtZDNjNTYyOGRiYjIwIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1F3TW1ZeU1UY3paQzFqWVROa0xUUXdNekl0WWprMk1TMWtNMk0xTmpJNFpHSmlNakFMQUJRQUFBQWtZV1kzTlRCa01UQXRaVFl4TkMwME56QmtMVGhtT1RVdE1UaGtOekU0WWpjeVlqbGhEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":61,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":63,"timestamp":1569606125229902000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291772,"activityTaskStartedEventAttributes":{"scheduledEventId":62,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"870d2b5f-3286-4992-9b83-911d9cb10842","attempt":0}},{"eventId":64,"timestamp":1569606125234358000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291773,"activityTaskCompletedEventAttributes":{"scheduledEventId":62,"startedEventId":63,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":65,"timestamp":1569606125234368000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291776,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":66,"timestamp":1569606125238253000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291780,"decisionTaskStartedEventAttributes":{"scheduledEventId":65,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-processor-parent-close-policy","requestId":"44177a34-efe8-4efc-a406-4018f9a46a87"}},{"eventId":67,"timestamp":1569606125244383000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291783,"decisionTaskCompletedEventAttributes":{"scheduledEventId":65,"startedEventId":66,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":68,"timestamp":1569606125244421000,"eventType":"MarkerRecorded","version":-24,"taskId":6291784,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiMTUiLCJhY3Rpdml0eVR5cGUiOiJkZWxldGVIaXN0b3J5QWN0aXZpdHkiLCJyZXBsYXlUaW1lIjoiMjAxOS0wOS0yN1QxMDo0MjowNS4yMzk2NTkyOTUtMDc6MDAifQo=","decisionTaskCompletedEventId":67}},{"eventId":69,"timestamp":1569606125920400000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291786,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzZhN2MxYjI5LTEzMjktNDgyYi04NTM3LTk4MmE5M2JjNjM3ZiIsIlJ1bklEIjoiNWQzMzQ4MDQtNzQ3ZC00ODAyLTkwYjctZmNiNjI2OWFiMDVkIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1ExWkRNek5EZ3dOQzAzTkRka0xUUTRNREl0T1RCaU55MW1ZMkkyTWpZNVlXSXdOV1FMQUJRQUFBQWtPREF6T0dKaU16Y3RPRE14TUMwME9UVm1MVGxqTlRNdE9HVmpPVEl5WVdVeE1tRTFEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":70,"timestamp":1569606125920408000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291788,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":71,"timestamp":1569606125923245000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291792,"decisionTaskStartedEventAttributes":{"scheduledEventId":70,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-tl-scanner-tasklist-0","requestId":"c7ee25d5-3d85-464d-a3ad-197f30ad4c13"}},{"eventId":72,"timestamp":1569606125926950000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291795,"decisionTaskCompletedEventAttributes":{"scheduledEventId":70,"startedEventId":71,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":73,"timestamp":1569606125926971000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291796,"activityTaskScheduledEventAttributes":{"activityId":"16","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzZhN2MxYjI5LTEzMjktNDgyYi04NTM3LTk4MmE5M2JjNjM3ZiIsIlJ1bklEIjoiNWQzMzQ4MDQtNzQ3ZC00ODAyLTkwYjctZmNiNjI2OWFiMDVkIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1ExWkRNek5EZ3dOQzAzTkRka0xUUTRNREl0T1RCaU55MW1ZMkkyTWpZNVlXSXdOV1FMQUJRQUFBQWtPREF6T0dKaU16Y3RPRE14TUMwME9UVm1MVGxqTlRNdE9HVmpPVEl5WVdVeE1tRTFEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":72,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":74,"timestamp":1569606125930046000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291801,"activityTaskStartedEventAttributes":{"scheduledEventId":73,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"3a35e9d7-790f-41df-a518-0f4d6a5bc0f2","attempt":0}},{"eventId":75,"timestamp":1569606125933614000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291802,"activityTaskCompletedEventAttributes":{"scheduledEventId":73,"startedEventId":74,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":76,"timestamp":1569606125933623000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291805,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":77,"timestamp":1569606125936580000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291809,"decisionTaskStartedEventAttributes":{"scheduledEventId":76,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"3bd96a83-c2fa-4979-8abb-c3ae80cc6ebb"}},{"eventId":78,"timestamp":1569606125941102000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291812,"decisionTaskCompletedEventAttributes":{"scheduledEventId":76,"startedEventId":77,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":79,"timestamp":1569606125941122000,"eventType":"MarkerRecorded","version":-24,"taskId":6291813,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiMTciLCJhY3Rpdml0eVR5cGUiOiJkZWxldGVIaXN0b3J5QWN0aXZpdHkiLCJyZXBsYXlUaW1lIjoiMjAxOS0wOS0yN1QxMDo0MjowNS45Mzc0NDI0NTEtMDc6MDAifQo=","decisionTaskCompletedEventId":78}},{"eventId":80,"timestamp":1569606126048936000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291846,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjozLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkX2I1YWJjYmNmLTZiZjAtNGE2My05YTM4LTU0NzhhM2MzOWM5MCIsIlJ1bklEIjoiY2VkYWRkZGEtZTM1My00MTQwLWI5MzktZjQ5OWFjMThlOGQ0IiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1JqWldSaFpHUmtZUzFsTXpVekxUUXhOREF0WWprek9TMW1ORGs1WVdNeE9HVTRaRFFMQUJRQUFBQWtaVGM0WXpBMFpURXRaRFJpTmkwME4yWTBMVGt5Wm1RdE1tTTRZell3WTJNeVkyUTJEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":81,"timestamp":1569606126048949000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291848,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":82,"timestamp":1569606126052440000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291852,"decisionTaskStartedEventAttributes":{"scheduledEventId":81,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-processor-parent-close-policy","requestId":"304f5dac-44a4-4af2-a759-c206b5a3ec46"}},{"eventId":83,"timestamp":1569606126056194000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291855,"decisionTaskCompletedEventAttributes":{"scheduledEventId":81,"startedEventId":82,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":84,"timestamp":1569606126056216000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291856,"activityTaskScheduledEventAttributes":{"activityId":"18","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjozLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkX2I1YWJjYmNmLTZiZjAtNGE2My05YTM4LTU0NzhhM2MzOWM5MCIsIlJ1bklEIjoiY2VkYWRkZGEtZTM1My00MTQwLWI5MzktZjQ5OWFjMThlOGQ0IiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1JqWldSaFpHUmtZUzFsTXpVekxUUXhOREF0WWprek9TMW1ORGs1WVdNeE9HVTRaRFFMQUJRQUFBQWtaVGM0WXpBMFpURXRaRFJpTmkwME4yWTBMVGt5Wm1RdE1tTTRZell3WTJNeVkyUTJEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":83,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":85,"timestamp":1569606126058989000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291861,"activityTaskStartedEventAttributes":{"scheduledEventId":84,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"b247c133-bc34-472b-9bbd-a4e5e1f942d3","attempt":0}},{"eventId":86,"timestamp":1569606126062977000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291862,"activityTaskCompletedEventAttributes":{"scheduledEventId":84,"startedEventId":85,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":87,"timestamp":1569606126062987000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291865,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":88,"timestamp":1569606126066232000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291869,"decisionTaskStartedEventAttributes":{"scheduledEventId":87,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-tl-scanner-tasklist-0","requestId":"26054030-971f-486e-99d8-4151f7f3535b"}},{"eventId":89,"timestamp":1569606126071558000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291872,"decisionTaskCompletedEventAttributes":{"scheduledEventId":87,"startedEventId":88,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":90,"timestamp":1569606126071585000,"eventType":"MarkerRecorded","version":-24,"taskId":6291873,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiMTkiLCJhY3Rpdml0eVR5cGUiOiJkZWxldGVIaXN0b3J5QWN0aXZpdHkiLCJyZXBsYXlUaW1lIjoiMjAxOS0wOS0yN1QxMDo0MjowNi4wNjcyNDQ4OTEtMDc6MDAifQo=","decisionTaskCompletedEventId":89}},{"eventId":91,"timestamp":1569606126219398000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291875,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjoyLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkX2U5YTE2MDRiLWY1YjEtNGY3Yi1hNjY0LTQ1Yjg4ZjU5YjY2OSIsIlJ1bklEIjoiYzNkMTVhZWQtMWViNS00NGU2LWJmYTMtMWNlNmY2OTIzZDEzIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1JqTTJReE5XRmxaQzB4WldJMUxUUTBaVFl0WW1aaE15MHhZMlUyWmpZNU1qTmtNVE1MQUJRQUFBQWtPRGMyTjJFNVpqVXROMlJsWlMwME5HWTBMV0U0Wm1RdFkyWmpZVEEyTUdVeFpXWTJEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":92,"timestamp":1569606126219405000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291877,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":93,"timestamp":1569606126221920000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291881,"decisionTaskStartedEventAttributes":{"scheduledEventId":92,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"d0a8f7d9-ed21-4c69-8656-348d497ae0ac"}},{"eventId":94,"timestamp":1569606126225163000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291884,"decisionTaskCompletedEventAttributes":{"scheduledEventId":92,"startedEventId":93,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":95,"timestamp":1569606126225187000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291885,"activityTaskScheduledEventAttributes":{"activityId":"20","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjoyLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkX2U5YTE2MDRiLWY1YjEtNGY3Yi1hNjY0LTQ1Yjg4ZjU5YjY2OSIsIlJ1bklEIjoiYzNkMTVhZWQtMWViNS00NGU2LWJmYTMtMWNlNmY2OTIzZDEzIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1JqTTJReE5XRmxaQzB4WldJMUxUUTBaVFl0WW1aaE15MHhZMlUyWmpZNU1qTmtNVE1MQUJRQUFBQWtPRGMyTjJFNVpqVXROMlJsWlMwME5HWTBMV0U0Wm1RdFkyWmpZVEEyTUdVeFpXWTJEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":94,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":96,"timestamp":1569606126228158000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291890,"activityTaskStartedEventAttributes":{"scheduledEventId":95,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"8b9abe6e-e31f-4fd0-ba44-761d7ec7f4b7","attempt":0}},{"eventId":97,"timestamp":1569606126231778000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291891,"activityTaskCompletedEventAttributes":{"scheduledEventId":95,"startedEventId":96,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":98,"timestamp":1569606126231788000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291894,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":99,"timestamp":1569606126234682000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291898,"decisionTaskStartedEventAttributes":{"scheduledEventId":98,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-processor-parent-close-policy","requestId":"0cd5dc7b-2a41-49d7-9db8-e952f8ce5cc2"}},{"eventId":100,"timestamp":1569606126239125000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291901,"decisionTaskCompletedEventAttributes":{"scheduledEventId":98,"startedEventId":99,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":101,"timestamp":1569606126239145000,"eventType":"MarkerRecorded","version":-24,"taskId":6291902,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiMjEiLCJhY3Rpdml0eVR5cGUiOiJkZWxldGVIaXN0b3J5QWN0aXZpdHkiLCJyZXBsYXlUaW1lIjoiMjAxOS0wOS0yN1QxMDo0MjowNi4yMzU0Njg2OTMtMDc6MDAifQo=","decisionTaskCompletedEventId":100}},{"eventId":102,"timestamp":1569606126923765000,"eventType":"WorkflowExecutionSignaled","version":-24,"taskId":6291904,"workflowExecutionSignaledEventAttributes":{"signalName":"cadence-archival-signal","input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzlmY2U4ZGEzLTMwNzMtNGY1Yi1iMjIxLWU3ZTFlZTM1ZWE0MCIsIlJ1bklEIjoiZGI3OTlhNWMtMmJkYS00OTZjLWI3MDMtNWY1ODNjN2IzZTkzIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1JrWWpjNU9XRTFZeTB5WW1SaExUUTVObU10WWpjd015MDFaalU0TTJNM1lqTmxPVE1MQUJRQUFBQWtNMkkwTW1SbU0yVXRNelJpWkMwME5USTNMV0k0T1RRdE1qQXlPRGhpWVdOaU1UZGpEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","identity":"59844@ycyang-C02XW41WJGH6@"}},{"eventId":103,"timestamp":1569606126923772000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291906,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":104,"timestamp":1569606126927973000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291910,"decisionTaskStartedEventAttributes":{"scheduledEventId":103,"identity":"59844@ycyang-C02XW41WJGH6@cadence-sys-tl-scanner-tasklist-0","requestId":"9ed3f383-bfe8-4f05-b7af-3ebe0763f3ec"}},{"eventId":105,"timestamp":1569606126931546000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291913,"decisionTaskCompletedEventAttributes":{"scheduledEventId":103,"startedEventId":104,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":106,"timestamp":1569606126931568000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":6291914,"activityTaskScheduledEventAttributes":{"activityId":"22","activityType":{"name":"uploadHistoryActivity"},"taskList":{"name":"cadence-archival-tl"},"input":"eyJTaGFyZElEIjoxLCJEb21haW5JRCI6IjhlNjI3NmQ4LWZhOTQtNDZjMS1hYjY5LWFkOGRhOTEyZWE4MCIsIkRvbWFpbk5hbWUiOiJzYW1wbGVzLWRvbWFpbiIsIldvcmtmbG93SUQiOiJoZWxsb3dvcmxkXzlmY2U4ZGEzLTMwNzMtNGY1Yi1iMjIxLWU3ZTFlZTM1ZWE0MCIsIlJ1bklEIjoiZGI3OTlhNWMtMmJkYS00OTZjLWI3MDMtNWY1ODNjN2IzZTkzIiwiRXZlbnRTdG9yZVZlcnNpb24iOjIsIkJyYW5jaFRva2VuIjoiV1FzQUNnQUFBQ1JrWWpjNU9XRTFZeTB5WW1SaExUUTVObU10WWpjd015MDFaalU0TTJNM1lqTmxPVE1MQUJRQUFBQWtNMkkwTW1SbU0yVXRNelJpWkMwME5USTNMV0k0T1RRdE1qQXlPRGhpWVdOaU1UZGpEd0FlREFBQUFBQUEiLCJOZXh0RXZlbnRJRCI6MTIsIkNsb3NlRmFpbG92ZXJWZXJzaW9uIjotMjQsIlVSSSI6ImZpbGU6Ly8vdG1wL2NhZGVuY2VfYXJjaGl2YWwvZGV2ZWxvcG1lbnQifQo=","scheduleToCloseTimeoutSeconds":300,"scheduleToStartTimeoutSeconds":300,"startToCloseTimeoutSeconds":60,"heartbeatTimeoutSeconds":0,"decisionTaskCompletedEventId":105,"retryPolicy":{"initialIntervalInSeconds":1,"backoffCoefficient":2,"maximumIntervalInSeconds":100,"maximumAttempts":0,"nonRetriableErrorReasons":["cadenceInternal:Panic","upload non-retriable error"],"expirationIntervalInSeconds":300},"header":{}}},{"eventId":107,"timestamp":1569606126934664000,"eventType":"ActivityTaskStarted","version":-24,"taskId":6291919,"activityTaskStartedEventAttributes":{"scheduledEventId":106,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"0e6ba111-0876-423c-9962-6f20948f48fa","attempt":0}},{"eventId":108,"timestamp":1569606126938683000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":6291920,"activityTaskCompletedEventAttributes":{"scheduledEventId":106,"startedEventId":107,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl"}},{"eventId":109,"timestamp":1569606126938692000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":6291923,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"ycyang-C02XW41WJGH6:7ce34128-dace-4a8a-a3ca-6eaa9c97dd13"},"startToCloseTimeoutSeconds":60,"attempt":0}},{"eventId":110,"timestamp":1569606126941424000,"eventType":"DecisionTaskStarted","version":-24,"taskId":6291927,"decisionTaskStartedEventAttributes":{"scheduledEventId":109,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","requestId":"c0bbf240-9a0e-4323-a182-a06decb5b2f0"}},{"eventId":111,"timestamp":1569606126945774000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":6291930,"decisionTaskCompletedEventAttributes":{"scheduledEventId":109,"startedEventId":110,"identity":"59844@ycyang-C02XW41WJGH6@cadence-archival-tl","binaryChecksum":"a3f549be2faf4819fc3dcb4732046568"}},{"eventId":112,"timestamp":1569606126945794000,"eventType":"MarkerRecorded","version":-24,"taskId":6291931,"markerRecordedEventAttributes":{"markerName":"LocalActivity","details":"eyJhY3Rpdml0eUlkIjoiMjMiLCJhY3Rpdml0eVR5cGUiOiJkZWxldGVIaXN0b3J5QWN0aXZpdHkiLCJyZXBsYXlUaW1lIjoiMjAxOS0wOS0yN1QxMDo0MjowNi45NDIzMTU2MjMtMDc6MDAifQo=","decisionTaskCompletedEventId":111}}] ================================================ FILE: service/worker/archiver/util.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "bytes" "encoding/json" "time" "github.com/dgryski/go-farm" "go.uber.org/cadence/activity" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" ) // MaxArchivalIterationTimeout returns the max allowed timeout for a single iteration of archival workflow func MaxArchivalIterationTimeout() time.Duration { return workflowStartToCloseTimeout / 2 } func hash(i interface{}) uint64 { var b bytes.Buffer // please make sure encoder is deterministic (especially when encoding map objects) // use json not gob here as json will sort map keys, while gob is non-deterministic json.NewEncoder(&b).Encode(i) //nolint:errcheck return farm.Fingerprint64(b.Bytes()) } func hashesEqual(a []uint64, b []uint64) bool { if len(a) != len(b) { return false } aMap := make(map[uint64]int) for _, elem := range a { aMap[elem] = aMap[elem] + 1 } for _, elem := range b { count := aMap[elem] if count == 0 { return false } aMap[elem] = aMap[elem] - 1 } return true } func tagLoggerWithHistoryRequest(logger log.Logger, request *ArchiveRequest) log.Logger { return logger.WithTags( tag.ShardID(request.ShardID), tag.ArchivalRequestDomainID(request.DomainID), tag.ArchivalRequestDomainName(request.DomainName), tag.ArchivalRequestWorkflowID(request.WorkflowID), tag.ArchivalRequestRunID(request.RunID), tag.ArchivalRequestBranchToken(request.BranchToken), tag.ArchivalRequestNextEventID(request.NextEventID), tag.ArchivalRequestCloseFailoverVersion(request.CloseFailoverVersion), tag.ArchivalURI(request.URI), ) } func tagLoggerWithVisibilityRequest(logger log.Logger, request *ArchiveRequest) log.Logger { return logger.WithTags( tag.ArchivalRequestDomainID(request.DomainID), tag.ArchivalRequestDomainName(request.DomainName), tag.ArchivalRequestWorkflowID(request.WorkflowID), tag.ArchivalRequestRunID(request.RunID), tag.ArchivalURI(request.URI), ) } func tagLoggerWithActivityInfo(logger log.Logger, activityInfo activity.Info) log.Logger { return logger.WithTags( tag.WorkflowID(activityInfo.WorkflowExecution.ID), tag.WorkflowRunID(activityInfo.WorkflowExecution.RunID), tag.Attempt(activityInfo.Attempt)) } func convertSearchAttributesToString(searchAttr map[string][]byte) map[string]string { searchAttrStr := make(map[string]string) for k, v := range searchAttr { searchAttrStr[k] = string(v) } return searchAttrStr } ================================================ FILE: service/worker/archiver/util_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common/types" ) type UtilSuite struct { *require.Assertions suite.Suite } func TestUtilSuite(t *testing.T) { suite.Run(t, new(UtilSuite)) } func (s *UtilSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *UtilSuite) TestHashDeterminism() { testCases := []struct { instance interface{} }{ { instance: "some random string", }, { instance: map[string]string{ "key1": "value1", "key2": "value2", "key3": "value3", }, }, { instance: []string{"value1", "value2", "value3"}, }, { instance: ArchiveRequest{ DomainID: "some random domainID", ShardID: 0, BranchToken: []byte{1, 2, 3}, NextEventID: int64(123), CloseStatus: types.WorkflowExecutionCloseStatusContinuedAsNew, Memo: &types.Memo{ Fields: map[string][]byte{ "memoKey1": []byte{1, 2, 3}, "memoKey2": []byte{4, 5, 6}, }, }, SearchAttributes: map[string][]byte{ "customKey1": []byte{1, 2, 3}, "customKey2": []byte{4, 5, 6}, }, Targets: []ArchivalTarget{ArchiveTargetHistory, ArchiveTargetVisibility}, }, }, } for _, tc := range testCases { expectedHash := hash(tc.instance) for i := 0; i != 100; i++ { s.Equal(expectedHash, hash(tc.instance)) } } } func (s *UtilSuite) TestHashesEqual() { testCases := []struct { a []uint64 b []uint64 equal bool }{ { a: nil, b: nil, equal: true, }, { a: []uint64{1, 2, 3}, b: []uint64{1, 2, 3}, equal: true, }, { a: []uint64{1, 2}, b: []uint64{1, 2, 3}, equal: false, }, { a: []uint64{1, 2, 3}, b: []uint64{1, 2}, equal: false, }, { a: []uint64{1, 2, 5, 5, 5}, b: []uint64{1, 2, 5, 5, 5}, equal: true, }, { a: []uint64{1, 2, 5, 5}, b: []uint64{1, 2, 5, 5, 5}, equal: false, }, { a: []uint64{1, 2, 5, 5, 5, 5}, b: []uint64{1, 2, 5, 5, 5}, equal: false, }, } for _, tc := range testCases { s.Equal(tc.equal, hashesEqual(tc.a, tc.b)) } } ================================================ FILE: service/worker/archiver/workflow.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "time" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type dynamicConfigResult struct { ArchiverConcurrency int ArchivalsPerIteration int TimelimitPerIteration time.Duration } func archivalWorkflow(ctx workflow.Context, carryover []ArchiveRequest) error { return archivalWorkflowHelper(ctx, globalLogger, globalMetricsClient, globalConfig, nil, nil, carryover) } func archivalWorkflowHelper( ctx workflow.Context, logger log.Logger, metricsClient metrics.Client, config *Config, handler Handler, // enables tests to inject mocks pump Pump, // enables tests to inject mocks carryover []ArchiveRequest, ) error { metricsClient = NewReplayMetricsClient(metricsClient, ctx) metricsClient.IncCounter(metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverWorkflowStartedCount) sw := metricsClient.StartTimer(metrics.ArchiverArchivalWorkflowScope, metrics.CadenceLatency) workflowInfo := workflow.GetInfo(ctx) logger = logger.WithTags( tag.WorkflowID(workflowInfo.WorkflowExecution.ID), tag.WorkflowRunID(workflowInfo.WorkflowExecution.RunID), tag.WorkflowTaskListName(workflowInfo.TaskListName), tag.WorkflowType(workflowInfo.WorkflowType.Name)) logger = log.NewReplayLogger(logger, ctx, false) logger.Info("archival system workflow started") var dcResult dynamicConfigResult _ = workflow.SideEffect( ctx, func(ctx workflow.Context) interface{} { timeLimit := config.TimeLimitPerArchivalIteration() maxTimeLimit := MaxArchivalIterationTimeout() if timeLimit > maxTimeLimit { timeLimit = maxTimeLimit } return dynamicConfigResult{ ArchiverConcurrency: config.ArchiverConcurrency(), ArchivalsPerIteration: config.ArchivalsPerIteration(), TimelimitPerIteration: timeLimit, } }).Get(&dcResult) requestCh := workflow.NewBufferedChannel(ctx, dcResult.ArchivalsPerIteration) if handler == nil { handler = NewHandler(ctx, logger, metricsClient, dcResult.ArchiverConcurrency, requestCh) } handlerSW := metricsClient.StartTimer(metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverHandleAllRequestsLatency) handler.Start() signalCh := workflow.GetSignalChannel(ctx, signalName) if pump == nil { pump = NewPump(ctx, logger, metricsClient, carryover, dcResult.TimelimitPerIteration, dcResult.ArchivalsPerIteration, requestCh, signalCh) } pumpResult := pump.Run() metricsClient.AddCounter(metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverNumPumpedRequestsCount, int64(len(pumpResult.PumpedHashes))) handledHashes := handler.Finished() handlerSW.Stop() metricsClient.AddCounter(metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverNumHandledRequestsCount, int64(len(handledHashes))) if !hashesEqual(pumpResult.PumpedHashes, handledHashes) { logger.Error("handled archival requests do not match pumped archival requests") metricsClient.IncCounter(metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverPumpedNotEqualHandledCount) } if pumpResult.TimeoutWithoutSignals { logger.Info("workflow stopping because pump did not get any signals within timeout threshold") metricsClient.IncCounter(metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverWorkflowStoppingCount) sw.Stop() return nil } for { var request ArchiveRequest if ok := signalCh.ReceiveAsync(&request); !ok { break } pumpResult.UnhandledCarryover = append(pumpResult.UnhandledCarryover, request) } logger.Info("archival system workflow continue as new") ctx = workflow.WithExecutionStartToCloseTimeout(ctx, workflowStartToCloseTimeout) ctx = workflow.WithWorkflowTaskStartToCloseTimeout(ctx, workflowTaskStartToCloseTimeout) sw.Stop() return workflow.NewContinueAsNewError(ctx, archivalWorkflowFnName, pumpResult.UnhandledCarryover) } ================================================ FILE: service/worker/archiver/workflow_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package archiver import ( "errors" "testing" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" mmocks "github.com/uber/cadence/common/metrics/mocks" ) var ( workflowTestMetrics *mmocks.Client workflowTestLogger log.Logger workflowTestHandler *MockHandler workflowTestPump *MockPump workflowTestConfig *Config ) type workflowSuite struct { suite.Suite testsuite.WorkflowTestSuite } func (s *workflowSuite) SetupSuite() { workflow.Register(archivalWorkflowTest) } func TestWorkflowSuite(t *testing.T) { suite.Run(t, new(workflowSuite)) } func (s *workflowSuite) SetupTest() { ctrl := gomock.NewController(s.T()) workflowTestMetrics = &mmocks.Client{} workflowTestLogger = testlogger.New(s.T()) workflowTestHandler = NewMockHandler(ctrl) workflowTestPump = NewMockPump(ctrl) workflowTestConfig = &Config{ ArchiverConcurrency: dynamicproperties.GetIntPropertyFn(0), ArchivalsPerIteration: dynamicproperties.GetIntPropertyFn(0), TimeLimitPerArchivalIteration: dynamicproperties.GetDurationPropertyFn(MaxArchivalIterationTimeout()), } } func (s *workflowSuite) TestArchivalWorkflow_Fail_HashesDoNotEqual() { workflowTestMetrics.On("IncCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverWorkflowStartedCount).Once() workflowTestMetrics.On("StartTimer", metrics.ArchiverArchivalWorkflowScope, metrics.CadenceLatency).Return(metrics.NopStopwatch()).Once() workflowTestMetrics.On("StartTimer", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverHandleAllRequestsLatency).Return(metrics.NopStopwatch()).Once() workflowTestMetrics.On("AddCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverNumPumpedRequestsCount, int64(3)).Once() workflowTestMetrics.On("AddCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverNumHandledRequestsCount, int64(3)).Once() workflowTestMetrics.On("IncCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverPumpedNotEqualHandledCount).Once() workflowTestHandler.EXPECT().Start().Times(1) workflowTestHandler.EXPECT().Finished().Return([]uint64{9, 7, 0}).Times(1) workflowTestPump.EXPECT().Run().Return(PumpResult{ PumpedHashes: []uint64{8, 7, 0}, }).Times(1) env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(archivalWorkflowTest) s.True(env.IsWorkflowCompleted()) var continueAsNewError *workflow.ContinueAsNewError ok := errors.As(env.GetWorkflowError(), &continueAsNewError) s.True(ok, "Called ContinueAsNew") env.AssertExpectations(s.T()) } func (s *workflowSuite) TestArchivalWorkflow_Exit_TimeoutWithoutSignals() { workflowTestMetrics.On("IncCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverWorkflowStartedCount).Once() workflowTestMetrics.On("StartTimer", metrics.ArchiverArchivalWorkflowScope, metrics.CadenceLatency).Return(metrics.NopStopwatch()).Once() workflowTestMetrics.On("StartTimer", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverHandleAllRequestsLatency).Return(metrics.NopStopwatch()).Once() workflowTestMetrics.On("AddCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverNumPumpedRequestsCount, int64(0)).Once() workflowTestMetrics.On("AddCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverNumHandledRequestsCount, int64(0)).Once() workflowTestMetrics.On("IncCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverWorkflowStoppingCount).Once() workflowTestHandler.EXPECT().Start().Times(1) workflowTestHandler.EXPECT().Finished().Return([]uint64{}).Times(1) workflowTestPump.EXPECT().Run().Return(PumpResult{ PumpedHashes: []uint64{}, TimeoutWithoutSignals: true, }).Times(1) env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(archivalWorkflowTest) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) env.AssertExpectations(s.T()) } func (s *workflowSuite) TestArchivalWorkflow_Success() { workflowTestMetrics.On("IncCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverWorkflowStartedCount).Once() workflowTestMetrics.On("StartTimer", metrics.ArchiverArchivalWorkflowScope, metrics.CadenceLatency).Return(metrics.NopStopwatch()).Once() workflowTestMetrics.On("StartTimer", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverHandleAllRequestsLatency).Return(metrics.NopStopwatch()).Once() workflowTestMetrics.On("AddCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverNumPumpedRequestsCount, int64(5)).Once() workflowTestMetrics.On("AddCounter", metrics.ArchiverArchivalWorkflowScope, metrics.ArchiverNumHandledRequestsCount, int64(5)).Once() workflowTestHandler.EXPECT().Start().Times(1) workflowTestHandler.EXPECT().Finished().Return([]uint64{1, 2, 3, 4, 5}).Times(1) workflowTestPump.EXPECT().Run().Return(PumpResult{ PumpedHashes: []uint64{1, 2, 3, 4, 5}, }).Times(1) env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(archivalWorkflowTest) s.True(env.IsWorkflowCompleted()) var continueAsNewError *workflow.ContinueAsNewError ok := errors.As(env.GetWorkflowError(), &continueAsNewError) s.True(ok, "Called ContinueAsNew") env.AssertExpectations(s.T()) } func (s *workflowSuite) TestReplayArchiveHistoryWorkflow() { logger := testlogger.NewZap(s.T()) globalLogger = workflowTestLogger globalMetricsClient = metrics.NewClient(tally.NewTestScope("replay", nil), metrics.Worker, metrics.HistogramMigration{}) globalConfig = &Config{ ArchiverConcurrency: dynamicproperties.GetIntPropertyFn(50), ArchivalsPerIteration: dynamicproperties.GetIntPropertyFn(1000), TimeLimitPerArchivalIteration: dynamicproperties.GetDurationPropertyFn(MaxArchivalIterationTimeout()), } err := worker.ReplayWorkflowHistoryFromJSONFile(logger, "testdata/archival_workflow_history_v1.json") s.NoError(err) } func archivalWorkflowTest(ctx workflow.Context) error { return archivalWorkflowHelper(ctx, workflowTestLogger, workflowTestMetrics, workflowTestConfig, workflowTestHandler, workflowTestPump, nil) } ================================================ FILE: service/worker/asyncworkflow/async_workflow_consumer_manager.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package asyncworkflow import ( "context" "sync" "time" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/asyncworkflow/queue/provider" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) const ( defaultRefreshInterval = 1 * time.Minute defaultShutdownTimeout = 5 * time.Second ) type ConsumerManagerOptions func(*ConsumerManager) func WithTimeSource(timeSrc clock.TimeSource) ConsumerManagerOptions { return func(c *ConsumerManager) { c.timeSrc = timeSrc } } func WithRefreshInterval(interval time.Duration) ConsumerManagerOptions { return func(c *ConsumerManager) { c.refreshInterval = interval } } func WithEnabledPropertyFn(enabledFn dynamicproperties.BoolPropertyFn) ConsumerManagerOptions { return func(c *ConsumerManager) { c.enabledFn = enabledFn } } func WithEmitConsumerCountMetrifFn(fn func(int)) ConsumerManagerOptions { return func(c *ConsumerManager) { c.emitConsumerCountMetricFn = fn } } func NewConsumerManager( logger log.Logger, metricsClient metrics.Client, domainCache cache.DomainCache, queueProvider queue.Provider, frontendClient frontend.Client, options ...ConsumerManagerOptions, ) *ConsumerManager { ctx, cancel := context.WithCancel(context.Background()) cm := &ConsumerManager{ enabledFn: dynamicproperties.GetBoolPropertyFn(true), logger: logger.WithTags(tag.ComponentAsyncWFConsumptionManager), metricsClient: metricsClient, domainCache: domainCache, queueProvider: queueProvider, frontendClient: frontendClient, refreshInterval: defaultRefreshInterval, shutdownTimeout: defaultShutdownTimeout, ctx: ctx, cancelFn: cancel, activeConsumers: make(map[string]provider.Consumer), timeSrc: clock.NewRealTimeSource(), } cm.emitConsumerCountMetricFn = cm.emitConsumerCountMetric for _, opt := range options { opt(cm) } return cm } type ConsumerManager struct { // all member variables are accessed without any mutex with the assumption that they are only accessed by the background loop enabledFn dynamicproperties.BoolPropertyFn logger log.Logger metricsClient metrics.Client timeSrc clock.TimeSource domainCache cache.DomainCache queueProvider queue.Provider frontendClient frontend.Client refreshInterval time.Duration shutdownTimeout time.Duration ctx context.Context cancelFn context.CancelFunc wg sync.WaitGroup activeConsumers map[string]provider.Consumer emitConsumerCountMetricFn func(int) } func (c *ConsumerManager) Start() { c.logger.Info("Starting ConsumerManager") c.wg.Add(1) go c.run() } func (c *ConsumerManager) Stop() { c.logger.Info("Stopping ConsumerManager") c.cancelFn() c.wg.Wait() if !common.AwaitWaitGroup(&c.wg, c.shutdownTimeout) { c.logger.Warn("ConsumerManager timed out on shutdown", tag.Dynamic("timeout", c.shutdownTimeout)) return } c.stopConsumers() c.logger.Info("Stopped ConsumerManager") } func (c *ConsumerManager) run() { defer c.wg.Done() ticker := c.timeSrc.NewTicker(c.refreshInterval) defer ticker.Stop() c.logger.Info("ConsumerManager background loop started", tag.Dynamic("refresh-interval", c.refreshInterval)) enabled := c.enabledFn() if enabled { c.refreshConsumers() } else { c.logger.Info("ConsumerManager is disabled at the moment so skipping initial refresh") } for { select { case <-ticker.Chan(): previouslyEnabled := enabled enabled = c.enabledFn() if enabled != previouslyEnabled { c.logger.Info("ConsumerManager enabled state changed", tag.Dynamic("enabled", enabled)) } if enabled { // refresh consumers every round when consumer is enabled c.refreshConsumers() } else { // stop consumers when consumer is disabled c.stopConsumers() } case <-c.ctx.Done(): c.logger.Info("ConsumerManager background loop stopped because context is done") return } } } func (c *ConsumerManager) refreshConsumers() { domains := c.domainCache.GetAllDomain() c.logger.Info("Refreshing consumers", tag.Dynamic("domain-count", len(domains)), tag.Dynamic("consumer-count", len(c.activeConsumers))) refCounts := make(map[string]int, len(c.activeConsumers)) for _, domain := range domains { select { default: case <-c.ctx.Done(): c.logger.Info("refreshConsumers is terminating because context is done") return } c.logger.Debug("Refreshing consumers for domain", tag.WorkflowDomainName(domain.GetInfo().Name), tag.Dynamic("domain-config", domain.GetConfig())) // domain config is not set or async workflow config is not set if domain.GetConfig() == nil || domain.GetConfig().AsyncWorkflowConfig == (types.AsyncWorkflowConfiguration{}) { continue } cfg := domain.GetConfig().AsyncWorkflowConfig queue, err := c.getQueue(cfg) if err != nil { c.logger.Error("Failed to get queue", tag.Error(err), tag.WorkflowDomainName(domain.GetInfo().Name)) continue } if !cfg.Enabled { // Already running active consumers for such queues will be stopped in the next loop continue } // async workflow config is enabled. check if consumer is already running if c.activeConsumers[queue.ID()] != nil { c.logger.Debug("Consumer already running", tag.WorkflowDomainName(domain.GetInfo().Name), tag.AsyncWFQueueID(queue.ID())) refCounts[queue.ID()]++ continue } c.logger.Info("Starting consumer", tag.WorkflowDomainName(domain.GetInfo().Name), tag.AsyncWFQueueID(queue.ID())) consumer, err := queue.CreateConsumer(&provider.Params{ Logger: c.logger, MetricsClient: c.metricsClient, FrontendClient: c.frontendClient, }) if err != nil { c.logger.Error("Failed to create consumer", tag.Error(err), tag.WorkflowDomainName(domain.GetInfo().Name), tag.AsyncWFQueueID(queue.ID())) continue } if err := consumer.Start(); err != nil { c.logger.Error("Failed to start consumer", tag.Error(err), tag.WorkflowDomainName(domain.GetInfo().Name), tag.AsyncWFQueueID(queue.ID())) continue } c.activeConsumers[queue.ID()] = consumer refCounts[queue.ID()]++ c.logger.Info("Created and started consumer", tag.WorkflowDomainName(domain.GetInfo().Name), tag.AsyncWFQueueID(queue.ID())) } // stop consumers that are not needed for qID, consumer := range c.activeConsumers { if refCounts[qID] > 0 { continue } c.logger.Info("Stopping consumer because it's not needed", tag.AsyncWFQueueID(qID)) consumer.Stop() delete(c.activeConsumers, qID) c.logger.Info("Stopped consumer", tag.AsyncWFQueueID(qID)) } c.logger.Info("Refreshed consumers", tag.Dynamic("consumer-count", len(c.activeConsumers))) c.emitConsumerCountMetricFn(len(c.activeConsumers)) } func (c *ConsumerManager) emitConsumerCountMetric(count int) { c.metricsClient.Scope(metrics.AsyncWorkflowConsumerScope).UpdateGauge(metrics.AsyncWorkflowConsumerCount, float64(count)) } func (c *ConsumerManager) stopConsumers() { if len(c.activeConsumers) == 0 { return } c.logger.Info("Stopping all active consumers", tag.Dynamic("consumer-count", len(c.activeConsumers))) for qID, consumer := range c.activeConsumers { consumer.Stop() c.logger.Info("Stopped consumer", tag.AsyncWFQueueID(qID)) delete(c.activeConsumers, qID) } c.emitConsumerCountMetricFn(len(c.activeConsumers)) c.logger.Info("Stopped all active consumers", tag.Dynamic("consumer-count", len(c.activeConsumers))) } func (c *ConsumerManager) getQueue(cfg types.AsyncWorkflowConfiguration) (provider.Queue, error) { if cfg.PredefinedQueueName != "" { return c.queueProvider.GetPredefinedQueue(cfg.PredefinedQueueName) } return c.queueProvider.GetQueue(cfg.QueueType, cfg.QueueConfig) } ================================================ FILE: service/worker/asyncworkflow/async_workflow_consumer_manager_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package asyncworkflow import ( "errors" "fmt" "sort" "sync/atomic" "testing" "time" "github.com/google/go-cmp/cmp" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/asyncworkflow/queue" "github.com/uber/cadence/common/asyncworkflow/queue/provider" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type domainWithConfig struct { name string asyncWFCfg types.AsyncWorkflowConfiguration failQueueCreation bool failConsumerCreation bool failConsumerStart bool } func TestConsumerManager(t *testing.T) { tests := []struct { name string // firstRoundDomains will be returned by the domain cache when the consumer manager starts firstRoundDomains []domainWithConfig // wantFirstRoundConsumers is the list of consumers that should be created after the first round wantFirstRoundConsumers []string // secondRoundDomains will be returned by the domain cache after the first refresh interval secondRoundDomains []domainWithConfig // wantSecondRoundConsumers is the list of consumers that should be created after the second round wantSecondRoundConsumers []string }{ { name: "no domains", }, { name: "empty queue config", firstRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{}, }, }, wantFirstRoundConsumers: []string{}, }, { name: "queue creation fails", firstRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "queue1", }, failQueueCreation: true, }, }, wantFirstRoundConsumers: []string{}, }, { name: "consumer creation fails", firstRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "queue1", }, failConsumerCreation: true, }, }, wantFirstRoundConsumers: []string{}, }, { name: "consumer start fails", firstRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "queue1", }, failConsumerStart: true, }, }, wantFirstRoundConsumers: []string{}, }, { name: "queue disabled", firstRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: false, PredefinedQueueName: "queue1", }, }, }, wantFirstRoundConsumers: []string{}, }, { name: "queue disabled in second round", firstRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "queue1", }, }, }, wantFirstRoundConsumers: []string{"queue1"}, secondRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: false, PredefinedQueueName: "queue1", }, }, }, wantSecondRoundConsumers: []string{}, }, { name: "same consumers for both rounds", firstRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "queue1", }, }, }, wantFirstRoundConsumers: []string{"queue1"}, secondRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "queue1", }, }, }, wantSecondRoundConsumers: []string{"queue1"}, }, { name: "shared queue by multiple domains and different enable-disable states", firstRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "shared_queue", }, }, { name: "domain2", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: false, PredefinedQueueName: "shared_queue", }, }, { name: "domain3", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"brokers":["localhost:9092"],"topics":["test-topic"]}`), }, }, }, }, wantFirstRoundConsumers: []string{ "shared_queue", `queuetype:kafka,queueconfig:{"brokers":["localhost:9092"],"topics":["test-topic"]}`, }, secondRoundDomains: []domainWithConfig{ { name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "shared_queue", }, }, { name: "domain2", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, PredefinedQueueName: "shared_queue", }, }, }, wantSecondRoundConsumers: []string{"shared_queue"}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockTimeSrc := clock.NewMockedTimeSource() mockDomainCache := cache.NewMockDomainCache(ctrl) // setup mocks for 2 rounds of domain cache refresh mockQueueProvider := queue.NewMockProvider(ctrl) for _, domainsForRound := range [][]domainWithConfig{tc.firstRoundDomains, tc.secondRoundDomains} { mockDomainCache.EXPECT(). GetAllDomain(). Return(toDomainCacheEntries(domainsForRound)). Times(1) for _, dwc := range domainsForRound { if dwc.asyncWFCfg == (types.AsyncWorkflowConfiguration{}) { continue } if dwc.failQueueCreation { mockQueueProvider.EXPECT(). GetPredefinedQueue(dwc.asyncWFCfg.PredefinedQueueName). Return(nil, errors.New("queue creation failed")) } else { queueMock := provider.NewMockQueue(ctrl) queueMock.EXPECT().ID().Return(queueID(dwc.asyncWFCfg)).AnyTimes() if dwc.asyncWFCfg.PredefinedQueueName != "" { mockQueueProvider.EXPECT(). GetPredefinedQueue(dwc.asyncWFCfg.PredefinedQueueName). Return(queueMock, nil).AnyTimes() } else { mockQueueProvider.EXPECT(). GetQueue(gomock.Any(), gomock.Any()). Return(queueMock, nil).AnyTimes() } if !dwc.asyncWFCfg.Enabled { continue } if dwc.failConsumerCreation { queueMock.EXPECT().CreateConsumer(gomock.Any()). Return(nil, errors.New("consumer creation failed")).AnyTimes() } else { mockConsumer := provider.NewMockConsumer(ctrl) var startErr error if dwc.failConsumerStart { startErr = errors.New("consumer start failed") } mockConsumer.EXPECT().Start().Return(startErr).AnyTimes() mockConsumer.EXPECT().Stop().AnyTimes() queueMock.EXPECT().CreateConsumer(gomock.Any()).Return(mockConsumer, nil).AnyTimes() } } } } // create consumer manager cm := NewConsumerManager( testlogger.New(t), metrics.NewNoopMetricsClient(), mockDomainCache, mockQueueProvider, nil, WithTimeSource(mockTimeSrc), ) cm.Start() defer cm.Stop() // wait for the first round of consumers to be created time.Sleep(50 * time.Millisecond) // verify consumers t.Log("first round comparison") if diff := cmpQueueIDs(cm.activeConsumers, tc.wantFirstRoundConsumers); diff != "" { t.Fatalf("Consumer mismatch after first round (-want +got):\n%s", diff) } // wait for the second round of consumers to be created mockTimeSrc.Advance(defaultRefreshInterval) time.Sleep(50 * time.Millisecond) // verify consumers t.Log("second round comparison") if diff := cmpQueueIDs(cm.activeConsumers, tc.wantSecondRoundConsumers); diff != "" { t.Fatalf("Consumer mismatch after second round (-want +got):\n%s", diff) } }) } } func TestConsumerManagerEnabledDisabled(t *testing.T) { ctrl := gomock.NewController(t) mockTimeSrc := clock.NewMockedTimeSource() mockDomainCache := cache.NewMockDomainCache(ctrl) mockQueueProvider := queue.NewMockProvider(ctrl) dwc := domainWithConfig{ name: "domain1", asyncWFCfg: types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "kafka", QueueConfig: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`{"brokers":["localhost:9092"],"topics":["test-topic"]}`), }, }, } mockDomainCache.EXPECT().GetAllDomain().Return(toDomainCacheEntries([]domainWithConfig{dwc})).AnyTimes() queueMock := provider.NewMockQueue(ctrl) queueMock.EXPECT().ID().Return(queueID(dwc.asyncWFCfg)).AnyTimes() mockQueueProvider.EXPECT().GetQueue(gomock.Any(), gomock.Any()).Return(queueMock, nil).AnyTimes() mockConsumer := provider.NewMockConsumer(ctrl) mockConsumer.EXPECT().Start().Return(nil).AnyTimes() mockConsumer.EXPECT().Stop().AnyTimes() queueMock.EXPECT().CreateConsumer(gomock.Any()).Return(mockConsumer, nil).AnyTimes() var consumerMgrEnabled, consumerCount int32 // create consumer manager cm := NewConsumerManager( testlogger.New(t), metrics.NewNoopMetricsClient(), mockDomainCache, mockQueueProvider, nil, WithTimeSource(mockTimeSrc), WithEnabledPropertyFn(func(opts ...dynamicproperties.FilterOption) bool { return atomic.LoadInt32(&consumerMgrEnabled) == 1 }), WithEmitConsumerCountMetrifFn(func(count int) { atomic.StoreInt32(&consumerCount, int32(count)) }), ) cm.Start() defer cm.Stop() // wait for the first round of consumers to be created and verify consumer count atomic.StoreInt32(&consumerMgrEnabled, 1) time.Sleep(50 * time.Millisecond) t.Log("first round comparison") got := atomic.LoadInt32(&consumerCount) want := 1 // consumer manager is enabled if got != int32(want) { t.Fatalf("Consumer count mismatch after first round, want: %v, got: %v", want, got) } // disable consumer manager and wait for the second round of refresh atomic.StoreInt32(&consumerMgrEnabled, 0) mockTimeSrc.Advance(defaultRefreshInterval) time.Sleep(50 * time.Millisecond) got = atomic.LoadInt32(&consumerCount) want = 0 // all consumers should be stopped when consumer manager is disabled if got != int32(want) { t.Fatalf("Consumer count mismatch after second round, want: %v, got: %v", want, got) } // enable consumer manager and wait for the third round of refresh atomic.StoreInt32(&consumerMgrEnabled, 1) mockTimeSrc.Advance(defaultRefreshInterval) time.Sleep(50 * time.Millisecond) got = atomic.LoadInt32(&consumerCount) want = 1 // consumer manager is enabled if got != int32(want) { t.Fatalf("Consumer count mismatch after third round, want: %v, got: %v", want, got) } } func toDomainCacheEntries(domains []domainWithConfig) map[string]*cache.DomainCacheEntry { result := make(map[string]*cache.DomainCacheEntry, len(domains)) for _, d := range domains { result[d.name] = cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ Name: d.name, }, &persistence.DomainConfig{ AsyncWorkflowConfig: d.asyncWFCfg, }, nil, 0, ) } return result } func queueID(asyncWFCfg types.AsyncWorkflowConfiguration) string { if asyncWFCfg.PredefinedQueueName != "" { return asyncWFCfg.PredefinedQueueName } if asyncWFCfg.QueueConfig == nil { return "" } return fmt.Sprintf("queuetype:%s,queueconfig:%s", asyncWFCfg.QueueType, string(asyncWFCfg.QueueConfig.Data)) } func cmpQueueIDs(activeConsumers map[string]provider.Consumer, want []string) string { got := make([]string, 0, len(activeConsumers)) for qID := range activeConsumers { got = append(got, qID) } if want == nil { want = []string{} } sort.Strings(got) sort.Strings(want) return cmp.Diff(want, got) } ================================================ FILE: service/worker/batcher/batcher.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package batcher import ( "context" "github.com/opentracing/opentracing-go" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/worker" "github.com/uber/cadence/client" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type ( // Config defines the configuration for batcher Config struct { AdminOperationToken dynamicproperties.StringPropertyFn // ClusterMetadata contains the metadata for this cluster ClusterMetadata cluster.Metadata } // BootstrapParams contains the set of params needed to bootstrap // the batcher sub-system BootstrapParams struct { // Config contains the configuration for scanner Config Config // ServiceClient is an instance of cadence service client ServiceClient workflowserviceclient.Interface // MetricsClient is an instance of metrics object for emitting stats MetricsClient metrics.Client Logger log.Logger // TallyScope is an instance of tally metrics scope TallyScope tally.Scope // ClientBean is an instance of client.Bean for a collection of clients ClientBean client.Bean } // Batcher is the background sub-system that execute workflow for batch operations // It is also the context object that get's passed around within the scanner workflows / activities Batcher struct { cfg Config svcClient workflowserviceclient.Interface clientBean client.Bean metricsClient metrics.Client tallyScope tally.Scope logger log.Logger } ) // New returns a new instance of batcher daemon Batcher func New(params *BootstrapParams) *Batcher { cfg := params.Config return &Batcher{ cfg: cfg, svcClient: params.ServiceClient, metricsClient: params.MetricsClient, tallyScope: params.TallyScope, logger: params.Logger.WithTags(tag.ComponentBatcher), clientBean: params.ClientBean, } } // Start starts the scanner func (s *Batcher) Start() error { // start worker for batch operation workflows ctx := context.WithValue(context.Background(), BatcherContextKey, s) workerOpts := worker.Options{ MetricsScope: s.tallyScope, BackgroundActivityContext: ctx, Tracer: opentracing.GlobalTracer(), } batchWorker := worker.New(s.svcClient, constants.BatcherLocalDomainName, BatcherTaskListName, workerOpts) return batchWorker.Start() } ================================================ FILE: service/worker/batcher/batcher_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package batcher import ( "testing" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/mock/gomock" "github.com/uber/cadence/client" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" ) func Test__Start(t *testing.T) { batcher, mockResource := setuptest(t) err := batcher.Start() require.NoError(t, err) mockResource.Finish(t) } func setuptest(t *testing.T) (*Batcher, *resource.Test) { ctrl := gomock.NewController(t) mockResource := resource.NewTest(t, ctrl, metrics.Worker) mockClientBean := client.NewMockBean(ctrl) mockResource.SDKClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&shared.DescribeDomainResponse{}, nil).AnyTimes() mockResource.SDKClient.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&shared.PollForDecisionTaskResponse{}, nil).AnyTimes() mockResource.SDKClient.EXPECT().PollForActivityTask(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&shared.PollForActivityTaskResponse{}, nil).AnyTimes() sdkClient := mockResource.GetSDKClient() mockClientBean.EXPECT().GetFrontendClient().Return(mockResource.FrontendClient).AnyTimes() mockClientBean.EXPECT().GetRemoteAdminClient(gomock.Any()).Return(mockResource.RemoteAdminClient, nil).AnyTimes() return New(&BootstrapParams{ Logger: testlogger.New(t), ServiceClient: sdkClient, ClientBean: mockClientBean, TallyScope: tally.TestScope(nil), Config: Config{ ClusterMetadata: cluster.NewMetadata( config.ClusterGroupMetadata{ FailoverVersionIncrement: 12, PrimaryClusterName: "test-primary-cluster", CurrentClusterName: "test-primary-cluster", ClusterGroup: map[string]config.ClusterInformation{ "test-primary-cluster": {}, "test-secondary-cluster": {}, }, }, nil, metrics.NewClient(tally.NoopScope, metrics.Worker, metrics.HistogramMigration{}), testlogger.New(t), ), }, }), mockResource } ================================================ FILE: service/worker/batcher/entities.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package batcher import ( "time" "github.com/uber/cadence/common/types" ) // TerminateParams is the parameters for terminating workflow type TerminateParams struct { // this indicates whether to terminate children workflow. Default to true. // TODO https://github.com/uber/cadence/issues/2159 // Ideally default should be childPolicy of the workflow. But it's currently totally broken. TerminateChildren *bool } // CancelParams is the parameters for canceling workflow type CancelParams struct { // this indicates whether to cancel children workflow. Default to true. // TODO https://github.com/uber/cadence/issues/2159 // Ideally default should be childPolicy of the workflow. But it's currently totally broken. CancelChildren *bool } // SignalParams is the parameters for signaling workflow type SignalParams struct { SignalName string Input string } // ReplicateParams is the parameters for replicating workflow type ReplicateParams struct { SourceCluster string TargetCluster string } // BatchParams is the parameters for batch operation workflow type BatchParams struct { // Target domain to execute batch operation DomainName string // To get the target workflows for processing Query string // Reason for the operation Reason string // Supporting: reset,terminate BatchType string // Below are all optional // TerminateParams is params only for BatchTypeTerminate TerminateParams TerminateParams // CancelParams is params only for BatchTypeCancel CancelParams CancelParams // SignalParams is params only for BatchTypeSignal SignalParams SignalParams // ReplicateParams is params only for BatchTypeReplicate ReplicateParams ReplicateParams // RPS of processing. Default to DefaultRPS // TODO we will implement smarter way than this static rate limiter: https://github.com/uber/cadence/issues/2138 RPS int // Number of goroutines running in parallel to process Concurrency int // Number of workflows processed in a batch PageSize int // Number of attempts for each workflow to process in case of retryable error before giving up AttemptsOnRetryableError int // timeout for activity heartbeat ActivityHeartBeatTimeout time.Duration // Max number of attempts for the activity to retry. Default to 0 (unlimited) MaxActivityRetries int // errors that will not retry which consumes AttemptsOnRetryableError. Default to empty NonRetryableErrors []string // internal conversion for NonRetryableErrors _nonRetryableErrors map[string]struct{} } // HeartBeatDetails is the struct for heartbeat details type HeartBeatDetails struct { PageToken []byte CurrentPage int // This is just an estimation for visibility TotalEstimate int64 // Number of workflows processed successfully SuccessCount int // Number of workflows that give up due to errors. ErrorCount int } type taskDetail struct { execution types.WorkflowExecution attempts int // passing along the current heartbeat details to make heartbeat within a task so that it won't timeout hbd HeartBeatDetails } ================================================ FILE: service/worker/batcher/workflow.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package batcher import ( "context" "fmt" "time" "github.com/google/uuid" "go.uber.org/cadence" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "golang.org/x/time/rate" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type ( contextKey string ) const ( BatcherContextKey contextKey = "batcherContext" // BatcherTaskListName is the tasklist name BatcherTaskListName = "cadence-sys-batcher-tasklist" // BatchWFTypeName is the workflow type BatchWFTypeName = "cadence-sys-batch-workflow" batchActivityName = "cadence-sys-batch-activity" // InfiniteDuration is a long duration(20 yrs) we used for infinite workflow running InfiniteDuration = 20 * 365 * 24 * time.Hour _nonRetriableReason = "non-retriable-error" // DefaultRPS is the default RPS DefaultRPS = 50 // DefaultConcurrency is the default concurrency DefaultConcurrency = 5 // DefaultPageSize is the default page size DefaultPageSize = 1000 // DefaultAttemptsOnRetryableError is the default value for AttemptsOnRetryableError DefaultAttemptsOnRetryableError = 50 // DefaultActivityHeartBeatTimeout is the default value for ActivityHeartBeatTimeout DefaultActivityHeartBeatTimeout = time.Second * 10 // DefaultMaxActivityRetries is the default value for MaxActivityRetries DefaultMaxActivityRetries = 4 ) const ( // BatchTypeTerminate is batch type for terminating workflows BatchTypeTerminate = "terminate" // BatchTypeCancel is the batch type for canceling workflows BatchTypeCancel = "cancel" // BatchTypeSignal is batch type for signaling workflows BatchTypeSignal = "signal" // BatchTypeReplicate is batch type for replicating workflows BatchTypeReplicate = "replicate" ) // AllBatchTypes is the batch types we supported var AllBatchTypes = []string{BatchTypeTerminate, BatchTypeCancel, BatchTypeSignal, BatchTypeReplicate} var ( BatchActivityRetryPolicy = cadence.RetryPolicy{ InitialInterval: 10 * time.Second, BackoffCoefficient: 1.7, MaximumInterval: 5 * time.Minute, ExpirationInterval: InfiniteDuration, NonRetriableErrorReasons: []string{_nonRetriableReason}, } batchActivityOptions = workflow.ActivityOptions{ ScheduleToStartTimeout: 5 * time.Minute, StartToCloseTimeout: InfiniteDuration, RetryPolicy: &BatchActivityRetryPolicy, } ) func init() { workflow.RegisterWithOptions(BatchWorkflow, workflow.RegisterOptions{Name: BatchWFTypeName}) activity.RegisterWithOptions(BatchActivity, activity.RegisterOptions{Name: batchActivityName}) } // BatchWorkflow is the workflow that runs a batch job of resetting workflows func BatchWorkflow(ctx workflow.Context, batchParams BatchParams) (HeartBeatDetails, error) { batchParams = setDefaultParams(batchParams) err := validateParams(batchParams) if err != nil { return HeartBeatDetails{}, err } batchActivityOptions.HeartbeatTimeout = batchParams.ActivityHeartBeatTimeout batchActivityOptions.RetryPolicy.MaximumAttempts = int32(batchParams.MaxActivityRetries) opt := workflow.WithActivityOptions(ctx, batchActivityOptions) var result HeartBeatDetails err = workflow.ExecuteActivity(opt, batchActivityName, batchParams).Get(ctx, &result) return result, err } func validateParams(params BatchParams) error { if params.BatchType == "" || params.Reason == "" || params.DomainName == "" || params.Query == "" { return fmt.Errorf("must provide required parameters: BatchType/Reason/DomainName/Query") } switch params.BatchType { case BatchTypeSignal: if params.SignalParams.SignalName == "" { return fmt.Errorf("must provide signal name") } return nil case BatchTypeReplicate: if params.ReplicateParams.SourceCluster == "" { return fmt.Errorf("must provide source cluster") } if params.ReplicateParams.TargetCluster == "" { return fmt.Errorf("must provide target cluster") } return nil case BatchTypeCancel: fallthrough case BatchTypeTerminate: return nil default: return fmt.Errorf("not supported batch type: %v", params.BatchType) } } func setDefaultParams(params BatchParams) BatchParams { if params.RPS <= 0 { params.RPS = DefaultRPS } if params.Concurrency <= 0 { params.Concurrency = DefaultConcurrency } if params.PageSize <= 0 { params.PageSize = DefaultPageSize } if params.AttemptsOnRetryableError <= 0 { params.AttemptsOnRetryableError = DefaultAttemptsOnRetryableError } if params.ActivityHeartBeatTimeout <= 0 { params.ActivityHeartBeatTimeout = DefaultActivityHeartBeatTimeout } if len(params.NonRetryableErrors) > 0 { params._nonRetryableErrors = make(map[string]struct{}, len(params.NonRetryableErrors)) for _, estr := range params.NonRetryableErrors { params._nonRetryableErrors[estr] = struct{}{} } } if params.TerminateParams.TerminateChildren == nil { params.TerminateParams.TerminateChildren = common.BoolPtr(true) } if params.MaxActivityRetries < 0 { params.MaxActivityRetries = DefaultMaxActivityRetries } return params } // BatchActivity is activity for processing batch operation func BatchActivity(ctx context.Context, batchParams BatchParams) (HeartBeatDetails, error) { batcher := ctx.Value(BatcherContextKey).(*Batcher) client := batcher.clientBean.GetFrontendClient() var adminClient admin.Client if batchParams.BatchType == BatchTypeReplicate { currentCluster := batcher.cfg.ClusterMetadata.GetCurrentClusterName() if currentCluster != batchParams.ReplicateParams.SourceCluster { return HeartBeatDetails{}, cadence.NewCustomError(_nonRetriableReason, fmt.Sprintf("the activity must run in the source cluster, current cluster is %s", currentCluster)) } var err error adminClient, err = batcher.clientBean.GetRemoteAdminClient(batchParams.ReplicateParams.TargetCluster) if err != nil { return HeartBeatDetails{}, cadence.NewCustomError(_nonRetriableReason, err.Error()) } } domainResp, err := client.DescribeDomain(ctx, &types.DescribeDomainRequest{ Name: &batchParams.DomainName, }) if err != nil { return HeartBeatDetails{}, err } domainID := domainResp.GetDomainInfo().GetUUID() hbd, ok := getHeartBeatDetails(ctx) if !ok { resp, err := client.CountWorkflowExecutions(ctx, &types.CountWorkflowExecutionsRequest{ Domain: batchParams.DomainName, Query: batchParams.Query, }) if err != nil { return HeartBeatDetails{}, err } hbd.TotalEstimate = resp.GetCount() } rateLimiter := rate.NewLimiter(rate.Limit(batchParams.RPS), batchParams.RPS) taskCh := make(chan taskDetail, batchParams.PageSize) respCh := make(chan error, batchParams.PageSize) for i := 0; i < batchParams.Concurrency; i++ { go startTaskProcessor(ctx, batchParams, domainID, taskCh, respCh, rateLimiter, client, adminClient) } for { // TODO https://github.com/uber/cadence/issues/2154 // Need to improve scan concurrency because it will hold an ES resource until the workflow finishes. // And we can't use list API because terminate / reset will mutate the result. resp, err := client.ScanWorkflowExecutions(ctx, &types.ListWorkflowExecutionsRequest{ Domain: batchParams.DomainName, PageSize: int32(batchParams.PageSize), NextPageToken: hbd.PageToken, Query: batchParams.Query, }) if err != nil { return HeartBeatDetails{}, err } batchCount := len(resp.Executions) if batchCount <= 0 { break } // send all tasks for _, wf := range resp.Executions { taskCh <- taskDetail{ execution: *wf.Execution, attempts: 0, hbd: hbd, } } succCount := 0 errCount := 0 // wait for counters indicate this batch is done Loop: for { select { case err := <-respCh: if err == nil { succCount++ } else { errCount++ } if succCount+errCount == batchCount { break Loop } case <-ctx.Done(): return HeartBeatDetails{}, ctx.Err() } } hbd.CurrentPage++ hbd.PageToken = resp.NextPageToken hbd.SuccessCount += succCount hbd.ErrorCount += errCount activity.RecordHeartbeat(ctx, hbd) if len(hbd.PageToken) == 0 { break } } return hbd, nil } func getHeartBeatDetails(ctx context.Context) (hbd HeartBeatDetails, ok bool) { if activity.HasHeartbeatDetails(ctx) { if err := activity.GetHeartbeatDetails(ctx, &hbd); err != nil { batcher := ctx.Value(BatcherContextKey).(*Batcher) batcher.metricsClient.IncCounter(metrics.BatcherScope, metrics.BatcherProcessorFailures) getActivityLogger(ctx).Error("Failed to recover from last heartbeat, start over from beginning", tag.Error(err)) return HeartBeatDetails{}, false } return hbd, true } return hbd, false } func startTaskProcessor( ctx context.Context, batchParams BatchParams, domainID string, taskCh chan taskDetail, respCh chan error, limiter *rate.Limiter, client frontend.Client, adminClient admin.Client, ) { batcher := ctx.Value(BatcherContextKey).(*Batcher) for { select { case <-ctx.Done(): return case task := <-taskCh: if isDone(ctx) { return } var err error requestID := uuid.New().String() switch batchParams.BatchType { case BatchTypeTerminate: err = processTask(ctx, limiter, task, batchParams, client, batchParams.TerminateParams.TerminateChildren, func(workflowID, runID string) error { return client.TerminateWorkflowExecution(ctx, &types.TerminateWorkflowExecutionRequest{ Domain: batchParams.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Reason: batchParams.Reason, Identity: BatchWFTypeName, }) }) case BatchTypeCancel: err = processTask(ctx, limiter, task, batchParams, client, batchParams.CancelParams.CancelChildren, func(workflowID, runID string) error { return client.RequestCancelWorkflowExecution(ctx, &types.RequestCancelWorkflowExecutionRequest{ Domain: batchParams.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Identity: BatchWFTypeName, RequestID: requestID, }) }) case BatchTypeSignal: err = processTask(ctx, limiter, task, batchParams, client, common.BoolPtr(false), func(workflowID, runID string) error { return client.SignalWorkflowExecution(ctx, &types.SignalWorkflowExecutionRequest{ Domain: batchParams.DomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Identity: BatchWFTypeName, RequestID: requestID, SignalName: batchParams.SignalParams.SignalName, Input: []byte(batchParams.SignalParams.Input), }) }) case BatchTypeReplicate: err = processTask(ctx, limiter, task, batchParams, client, common.BoolPtr(false), func(workflowID, runID string) error { return adminClient.ResendReplicationTasks(ctx, &types.ResendReplicationTasksRequest{ DomainID: domainID, WorkflowID: workflowID, RunID: runID, RemoteCluster: batchParams.ReplicateParams.SourceCluster, }) }) } if err != nil { batcher.metricsClient.IncCounter(metrics.BatcherScope, metrics.BatcherProcessorFailures) getActivityLogger(ctx).Error("Failed to process batch operation task", tag.Error(err)) _, ok := batchParams._nonRetryableErrors[err.Error()] if ok || task.attempts >= batchParams.AttemptsOnRetryableError { respCh <- err } else { // put back to the channel if less than attemptsOnError task.attempts++ taskCh <- task } } else { batcher.metricsClient.IncCounter(metrics.BatcherScope, metrics.BatcherProcessorSuccess) respCh <- nil } } } } func processTask( ctx context.Context, limiter *rate.Limiter, task taskDetail, batchParams BatchParams, client frontend.Client, applyOnChild *bool, procFn func(string, string) error, ) error { wfs := []types.WorkflowExecution{task.execution} for len(wfs) > 0 { wf := wfs[0] wfs = wfs[1:] err := limiter.Wait(ctx) if err != nil { return err } activity.RecordHeartbeat(ctx, task.hbd) err = procFn(wf.GetWorkflowID(), wf.GetRunID()) if err != nil { // EntityNotExistsError means wf is not running or deleted if _, ok := err.(*types.EntityNotExistsError); ok { continue } return err } resp, err := client.DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: batchParams.DomainName, Execution: &types.WorkflowExecution{ WorkflowID: wf.WorkflowID, RunID: wf.RunID, }, }) if err != nil { // EntityNotExistsError means wf is deleted if _, ok := err.(*types.EntityNotExistsError); ok { continue } return err } // TODO https://github.com/uber/cadence/issues/2159 // By default should use ChildPolicy, but it is totally broken in Cadence, we need to fix it before using if applyOnChild != nil && *applyOnChild && len(resp.PendingChildren) > 0 { getActivityLogger(ctx).Info("Found more child workflows to process", tag.Number(int64(len(resp.PendingChildren)))) for _, ch := range resp.PendingChildren { wfs = append(wfs, types.WorkflowExecution{ WorkflowID: ch.WorkflowID, RunID: ch.RunID, }) } } } return nil } func isDone(ctx context.Context) bool { select { case <-ctx.Done(): return true default: return false } } func getActivityLogger(ctx context.Context) log.Logger { batcher := ctx.Value(BatcherContextKey).(*Batcher) wfInfo := activity.GetInfo(ctx) return batcher.logger.WithTags( tag.WorkflowID(wfInfo.WorkflowExecution.ID), tag.WorkflowRunID(wfInfo.WorkflowExecution.RunID), tag.WorkflowDomainName(wfInfo.WorkflowDomain), ) } ================================================ FILE: service/worker/batcher/workflow_retry_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package batcher import ( "context" "testing" "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/metrics" mmocks "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" ) type workflowRetrySuite struct { suite.Suite testsuite.WorkflowTestSuite workflowEnv *testsuite.TestWorkflowEnvironment activityEnv *testsuite.TestActivityEnvironment mockResource *resource.Test metricsMock *mmocks.Client } func TestWorkflowRetrySuite(t *testing.T) { suite.Run(t, new(workflowRetrySuite)) } func (s *workflowRetrySuite) SetupTest() { s.workflowEnv = s.NewTestWorkflowEnvironment() s.workflowEnv.RegisterWorkflow(BatchWorkflow) s.activityEnv = s.NewTestActivityEnvironment() s.activityEnv.RegisterActivity(BatchActivity) batcher, mockResource := setuptest(s.T()) s.mockResource = mockResource s.metricsMock = &mmocks.Client{} batcher.metricsClient = s.metricsMock mockResource.FrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(&types.DescribeDomainResponse{}, nil).AnyTimes() mockResource.FrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.CountWorkflowExecutionsResponse{Count: 1}, nil).AnyTimes() mockResource.FrontendClient.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockResource.FrontendClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(&types.DescribeWorkflowExecutionResponse{}, nil).AnyTimes() ctx := context.WithValue(context.Background(), BatcherContextKey, batcher) workerOpts := worker.Options{ MetricsScope: tally.TestScope(nil), BackgroundActivityContext: ctx, Tracer: opentracing.GlobalTracer(), } s.workflowEnv.SetWorkerOptions(workerOpts) s.activityEnv.SetWorkerOptions(workerOpts) } func (s *workflowRetrySuite) TestActivityRetries() { params := createParams(BatchTypeCancel) params.MaxActivityRetries = 4 s.metricsMock.On("IncCounter", metrics.BatcherScope, metrics.BatcherProcessorSuccess).Times(2) s.metricsMock.On("IncCounter", metrics.BatcherScope, metrics.BatcherProcessorFailures).Times(5) // First call to scan workflow executions succeeds, but returns a bad page token s.mockResource.FrontendClient.EXPECT().ScanWorkflowExecutions(gomock.Any(), &types.ListWorkflowExecutionsRequest{ Domain: params.DomainName, PageSize: int32(params.PageSize), NextPageToken: nil, Query: params.Query, }). Return(&types.ListWorkflowExecutionsResponse{ Executions: []*types.WorkflowExecutionInfo{{Execution: &types.WorkflowExecution{WorkflowID: "wid", RunID: "rid"}}}, NextPageToken: []byte("bad-page-token"), }, nil).Times(1) // Max attempt is set to 4, so we try 5 times, 0,1,2,3,4 s.mockResource.FrontendClient.EXPECT().ScanWorkflowExecutions(gomock.Any(), &types.ListWorkflowExecutionsRequest{ Domain: params.DomainName, PageSize: int32(params.PageSize), NextPageToken: []byte("bad-page-token"), Query: params.Query, }). Return(&types.ListWorkflowExecutionsResponse{ Executions: []*types.WorkflowExecutionInfo{{Execution: &types.WorkflowExecution{WorkflowID: "wid", RunID: "rid"}}}, NextPageToken: []byte("bad-page-token"), }, assert.AnError).Times(5) s.workflowEnv.ExecuteWorkflow(BatchWorkflow, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.ErrorContains(s.workflowEnv.GetWorkflowError(), assert.AnError.Error()) } func (s *workflowRetrySuite) TearDownTest() { s.workflowEnv.AssertExpectations(s.T()) } ================================================ FILE: service/worker/batcher/workflow_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package batcher import ( "context" "testing" "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/metrics" mmocks "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/types" ) type workflowSuite struct { suite.Suite testsuite.WorkflowTestSuite workflowEnv *testsuite.TestWorkflowEnvironment activityEnv *testsuite.TestActivityEnvironment } func TestWorkflowSuite(t *testing.T) { suite.Run(t, new(workflowSuite)) } func (s *workflowSuite) SetupTest() { s.workflowEnv = s.NewTestWorkflowEnvironment() s.workflowEnv.RegisterWorkflow(BatchWorkflow) s.activityEnv = s.NewTestActivityEnvironment() s.activityEnv.RegisterActivity(BatchActivity) batcher, mockResource := setuptest(s.T()) metricsMock := &mmocks.Client{} metricsMock.On("IncCounter", metrics.BatcherScope, metrics.BatcherProcessorSuccess).Once() batcher.metricsClient = metricsMock mockResource.FrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(&types.DescribeDomainResponse{}, nil).AnyTimes() mockResource.FrontendClient.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.ListWorkflowExecutionsResponse{ Executions: []*types.WorkflowExecutionInfo{{Execution: &types.WorkflowExecution{WorkflowID: "wid", RunID: "rid"}}}, NextPageToken: nil, }, nil).AnyTimes() mockResource.FrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.CountWorkflowExecutionsResponse{Count: 1}, nil).AnyTimes() mockResource.FrontendClient.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockResource.FrontendClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(&types.DescribeWorkflowExecutionResponse{}, nil).AnyTimes() mockResource.FrontendClient.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockResource.FrontendClient.EXPECT().TerminateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockResource.RemoteAdminClient.EXPECT().ResendReplicationTasks(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() ctx := context.WithValue(context.Background(), BatcherContextKey, batcher) workerOpts := worker.Options{ MetricsScope: tally.TestScope(nil), BackgroundActivityContext: ctx, Tracer: opentracing.GlobalTracer(), } s.activityEnv.SetWorkerOptions(workerOpts) } func (s *workflowSuite) TestWorkflow() { params := createParams(BatchTypeCancel) activityHeartBeatDeatils := HeartBeatDetails{} s.workflowEnv.OnActivity(batchActivityName, mock.Anything, mock.Anything).Return(activityHeartBeatDeatils, nil) s.workflowEnv.ExecuteWorkflow(BatchWorkflow, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.NoError(s.workflowEnv.GetWorkflowError()) } func (s *workflowSuite) TestActivity_BatchCancel() { params := createParams(BatchTypeCancel) _, err := s.activityEnv.ExecuteActivity(BatchActivity, params) s.NoError(err) } func (s *workflowSuite) TestActivity_BatchTerminate() { params := createParams(BatchTypeTerminate) _, err := s.activityEnv.ExecuteActivity(BatchActivity, params) s.NoError(err) } func (s *workflowSuite) TestActivity_BatchSignal() { params := createParams(BatchTypeSignal) _, err := s.activityEnv.ExecuteActivity(BatchActivity, params) s.NoError(err) } func (s *workflowSuite) TestActivity_BatchReplicate() { params := createParams(BatchTypeReplicate) _, err := s.activityEnv.ExecuteActivity(BatchActivity, params) s.NoError(err) } func (s *workflowSuite) TestWorkflow_BatchTypeCancelValidationError() { params := createParams(BatchTypeCancel) params.Query = "" s.workflowEnv.ExecuteWorkflow(BatchWorkflow, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.ErrorContains(s.workflowEnv.GetWorkflowError(), "must provide required parameters: BatchType/Reason/DomainName/Query") } func (s *workflowSuite) TestWorkflow_UnsupportedBatchType() { params := createParams("invalid-batch-type") s.workflowEnv.ExecuteWorkflow(BatchWorkflow, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.ErrorContains(s.workflowEnv.GetWorkflowError(), "not supported batch type") } func (s *workflowSuite) TestWorkflow_BatchTypeSignalValidation() { params := createParams(BatchTypeSignal) params.SignalParams.SignalName = "" s.workflowEnv.ExecuteWorkflow(BatchWorkflow, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.ErrorContains(s.workflowEnv.GetWorkflowError(), "must provide signal name") } func (s *workflowSuite) TestWorkflow_BatchTypeReplicateSourceCLusterValidation() { params := createParams(BatchTypeReplicate) params.ReplicateParams.SourceCluster = "" s.workflowEnv.ExecuteWorkflow(BatchWorkflow, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.ErrorContains(s.workflowEnv.GetWorkflowError(), "must provide source cluster") } func (s *workflowSuite) TestWorkflow_BatchTypeReplicateTargetCLusterValidation() { params := createParams(BatchTypeReplicate) params.ReplicateParams.TargetCluster = "" s.workflowEnv.ExecuteWorkflow(BatchWorkflow, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.ErrorContains(s.workflowEnv.GetWorkflowError(), "must provide target cluster") } func (s *workflowSuite) TearDownTest() { s.workflowEnv.AssertExpectations(s.T()) } func createParams(batchType string) BatchParams { return BatchParams{ DomainName: "test-domain", Query: "Closetime=missing", Reason: "unit-test", BatchType: batchType, TerminateParams: TerminateParams{ TerminateChildren: common.BoolPtr(true), }, CancelParams: CancelParams{ CancelChildren: common.BoolPtr(true), }, SignalParams: SignalParams{ SignalName: "test-signal-name", }, ReplicateParams: ReplicateParams{ SourceCluster: "test-primary-cluster", TargetCluster: "test-secondary-cluster", }, RPS: 5, Concurrency: 5, PageSize: 10, AttemptsOnRetryableError: 0, ActivityHeartBeatTimeout: 0, MaxActivityRetries: 0, NonRetryableErrors: []string{"HeartbeatTimeoutError"}, _nonRetryableErrors: nil, } } ================================================ FILE: service/worker/diagnostics/activities.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package diagnostics import ( "context" "fmt" "time" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/diagnostics/analytics" "github.com/uber/cadence/service/worker/diagnostics/invariant" ) const ( linkToTimeoutsRunbook = "https://cadenceworkflow.io/docs/workflow-troubleshooting/timeouts/" linkToFailuresRunbook = "https://cadenceworkflow.io/docs/workflow-troubleshooting/activity-failures/" linkToRetriesRunbook = "https://cadenceworkflow.io/docs/workflow-troubleshooting/retries" WfDiagnosticsAppName = "workflow-diagnostics" _maxPageSize = 1000 // current maximum page size for fetching workflow history _contextTimeout = 1 * time.Minute // timeout to fetch the whole execution history _maxIssuesPerInvariant = 10 // maximum number of issues to return per invariant check ) type identifyIssuesParams struct { Execution *types.WorkflowExecution Domain string } func (w *dw) identifyIssues(ctx context.Context, info identifyIssuesParams) ([]invariant.InvariantCheckResult, error) { result := make([]invariant.InvariantCheckResult, 0) history, err := w.getWorkflowExecutionHistory(ctx, info.Execution, info.Domain) if err != nil { return nil, err } for _, inv := range w.invariants { issues, err := inv.Check(ctx, invariant.InvariantCheckInput{ WorkflowExecutionHistory: history, Domain: info.Domain, }) if err != nil { return nil, err } result = append(result, capNumberOfIssues(issues)...) } return result, nil } func (w *dw) getWorkflowExecutionHistory(ctx context.Context, execution *types.WorkflowExecution, domain string) (*types.GetWorkflowExecutionHistoryResponse, error) { frontendClient := w.clientBean.GetFrontendClient() var nextPageToken []byte var history []*types.HistoryEvent ctx, cancel := context.WithTimeout(ctx, _contextTimeout) defer cancel() for { response, err := frontendClient.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: domain, Execution: execution, MaximumPageSize: _maxPageSize, NextPageToken: nextPageToken, WaitForNewEvent: false, HistoryEventFilterType: types.HistoryEventFilterTypeAllEvent.Ptr(), SkipArchival: true, }) if err != nil { return nil, fmt.Errorf("failed to get history: %w", err) } for _, event := range response.GetHistory().GetEvents() { if event != nil { history = append(history, event) } } if response.NextPageToken == nil { return &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: history, }}, nil } nextPageToken = response.NextPageToken } } type rootCauseIssuesParams struct { Domain string Issues []invariant.InvariantCheckResult } func (w *dw) rootCauseIssues(ctx context.Context, info rootCauseIssuesParams) ([]invariant.InvariantRootCauseResult, error) { result := make([]invariant.InvariantRootCauseResult, 0) for _, inv := range w.invariants { rootCause, err := inv.RootCause(ctx, invariant.InvariantRootCauseInput{ Domain: info.Domain, Issues: info.Issues, }) if err != nil { return nil, err } result = append(result, rootCause...) } return result, nil } func (w *dw) emitUsageLogs(ctx context.Context, info analytics.WfDiagnosticsUsageData) error { if w.messagingClient == nil { // skip emitting logs if messaging client is not provided since it is optional w.logger.Error("messaging client is not provided, skipping emitting wf-diagnostics usage logs", tag.WorkflowDomainName(info.Domain)) return nil } return w.emit(ctx, info, w.messagingClient) } func (w *dw) emit(ctx context.Context, info analytics.WfDiagnosticsUsageData, client messaging.Client) error { producer, err := client.NewProducer(WfDiagnosticsAppName) if err != nil { // skip emitting logs if producer cannot be created since it is optional w.logger.Error("producer creation failed, skipping emitting wf-diagnostics usage logs", tag.WorkflowDomainName(info.Domain)) return nil } emitter := analytics.NewEmitter(analytics.EmitterParams{ Producer: producer, }) return emitter.EmitUsageData(ctx, info) } // capNumberOfIssues limits the number of issues to avoid overwhelming the result with too many issues. func capNumberOfIssues(issues []invariant.InvariantCheckResult) []invariant.InvariantCheckResult { if len(issues) > _maxIssuesPerInvariant { return issues[:_maxIssuesPerInvariant] } return issues } ================================================ FILE: service/worker/diagnostics/activities_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package diagnostics import ( "context" "encoding/json" "testing" "time" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/client" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/diagnostics/analytics" "github.com/uber/cadence/service/worker/diagnostics/invariant" "github.com/uber/cadence/service/worker/diagnostics/invariant/failure" "github.com/uber/cadence/service/worker/diagnostics/invariant/retry" ) const ( workflowTimeoutSecond = int32(110) testTimeStamp = int64(2547596872371000000) timeUnit = time.Second ) func Test__identifyIssues(t *testing.T) { dwtest := testDiagnosticWorkflow(t, testWorkflowExecutionHistoryResponseWithMultipleIssues()) actMetadata := failure.FailureIssuesMetadata{ Identity: "localhost", ActivityType: "test-activity", ActivityScheduledID: 2, ActivityStartedID: 3, } actMetadataInBytes, err := json.Marshal(actMetadata) require.NoError(t, err) expectedResult := []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: failure.ActivityFailed.String(), Reason: failure.GenericError.String(), Metadata: actMetadataInBytes, }, } for i := 0; i < _maxIssuesPerInvariant; i++ { retryMetadata := retry.RetryMetadata{ EventID: int64(i), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 1, }, } retryMetadataInBytes, err := json.Marshal(retryMetadata) require.NoError(t, err) expectedResult = append(expectedResult, invariant.InvariantCheckResult{ IssueID: i, InvariantType: retry.ActivityRetryIssue.String(), Reason: retry.RetryPolicyValidationMaxAttempts.String(), Metadata: retryMetadataInBytes, }) } result, err := dwtest.identifyIssues(context.Background(), identifyIssuesParams{Execution: &types.WorkflowExecution{ WorkflowID: "123", RunID: "abc", }}) require.NoError(t, err) require.Equal(t, _maxIssuesPerInvariant+1, len(result)) // retry invariant returns 10 issues (capped) , failure invariant returns 1 issue require.Equal(t, expectedResult, result) } func Test__rootCauseIssues(t *testing.T) { dwtest := testDiagnosticWorkflow(t, testWorkflowExecutionHistoryResponse()) actMetadata := failure.FailureIssuesMetadata{ Identity: "localhost", ActivityScheduledID: 1, ActivityStartedID: 2, } actMetadataInBytes, err := json.Marshal(actMetadata) require.NoError(t, err) issues := []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: failure.ActivityFailed.String(), Reason: failure.CustomError.String(), Metadata: actMetadataInBytes, }, } expectedRootCause := []invariant.InvariantRootCauseResult{ { IssueID: 0, RootCause: invariant.RootCauseTypeServiceSideCustomError, Metadata: actMetadataInBytes, }, } result, err := dwtest.rootCauseIssues(context.Background(), rootCauseIssuesParams{Domain: "test-domain", Issues: issues}) require.NoError(t, err) require.Equal(t, expectedRootCause, result) } func Test__emit(t *testing.T) { ctrl := gomock.NewController(t) dwtest := testDiagnosticWorkflow(t, testWorkflowExecutionHistoryResponse()) mockClient := messaging.NewMockClient(ctrl) mockProducer := messaging.NewMockProducer(ctrl) mockProducer.EXPECT().Publish(gomock.Any(), gomock.Any()).Return(nil) mockClient.EXPECT().NewProducer(WfDiagnosticsAppName).Return(mockProducer, nil) err := dwtest.emit(context.Background(), analytics.WfDiagnosticsUsageData{}, mockClient) require.NoError(t, err) } func testDiagnosticWorkflow(t *testing.T, history *types.GetWorkflowExecutionHistoryResponse) *dw { ctrl := gomock.NewController(t) mockClientBean := client.NewMockBean(ctrl) mockFrontendClient := frontend.NewMockClient(ctrl) mockClientBean.EXPECT().GetFrontendClient().Return(mockFrontendClient).AnyTimes() mockFrontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any()).Return(history, nil).AnyTimes() return &dw{ clientBean: mockClientBean, invariants: []invariant.Invariant{failure.NewInvariant(), retry.NewInvariant()}, } } func testWorkflowExecutionHistoryResponse() *types.GetWorkflowExecutionHistoryResponse { return &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { ID: 1, Timestamp: common.Int64Ptr(testTimeStamp), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(workflowTimeoutSecond), }, }, { ID: 2, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityID: "101", ActivityType: &types.ActivityType{Name: "test-activity"}, StartToCloseTimeoutSeconds: common.Int32Ptr(int32(10)), HeartbeatTimeoutSeconds: common.Int32Ptr(int32(5)), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 1, }, }, }, { ID: 3, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ Identity: "localhost", Attempt: 0, }, }, { ID: 4, Timestamp: common.Int64Ptr(testTimeStamp), ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ Reason: common.StringPtr("cadenceInternal:Generic"), Details: []byte("test-activity-failure"), Identity: "localhost", ScheduledEventID: 2, StartedEventID: 3, }, }, { ID: 5, Timestamp: common.Int64Ptr(testTimeStamp + int64(workflowTimeoutSecond)*timeUnit.Nanoseconds()), WorkflowExecutionTimedOutEventAttributes: &types.WorkflowExecutionTimedOutEventAttributes{TimeoutType: types.TimeoutTypeStartToClose.Ptr()}, }, }, }, } } func testWorkflowExecutionHistoryResponseWithMultipleIssues() *types.GetWorkflowExecutionHistoryResponse { testResponse := &types.GetWorkflowExecutionHistoryResponse{History: &types.History{ Events: []*types.HistoryEvent{ { ID: 1, Timestamp: common.Int64Ptr(testTimeStamp), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(workflowTimeoutSecond), }, }, }, }} for i := 0; i <= 20; i++ { testResponse.History.Events = append(testResponse.History.Events, &types.HistoryEvent{ ID: int64(i), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityID: string(rune(i)), ActivityType: &types.ActivityType{Name: "test-activity"}, StartToCloseTimeoutSeconds: common.Int32Ptr(int32(10)), HeartbeatTimeoutSeconds: common.Int32Ptr(int32(5)), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 1, }, }, }) } testResponse.History.Events = append(testResponse.History.Events, &types.HistoryEvent{ ID: 4, Timestamp: common.Int64Ptr(testTimeStamp), ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ Reason: common.StringPtr("cadenceInternal:Generic"), Details: []byte("test-activity-failure"), Identity: "localhost", ScheduledEventID: 2, StartedEventID: 3, }, }) return testResponse } func Test__identifyIssuesWithPaginatedHistory(t *testing.T) { ctrl := gomock.NewController(t) mockClientBean := client.NewMockBean(ctrl) mockFrontendClient := frontend.NewMockClient(ctrl) token := []byte("next-page-token") testExecution := &types.WorkflowExecution{ WorkflowID: "123", RunID: "abc", } partialWFHistoryResponse := &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 1, }, Attempt: 0, }, }, }, }, NextPageToken: token, } remainingWFHistoryResponse := &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { WorkflowExecutionFailedEventAttributes: &types.WorkflowExecutionFailedEventAttributes{ Reason: common.StringPtr("cadenceInternal:Timeout START_TO_CLOSE"), DecisionTaskCompletedEventID: 10, }, }, }, }, } mockClientBean.EXPECT().GetFrontendClient().Return(mockFrontendClient).AnyTimes() firstCall := mockFrontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), &types.GetWorkflowExecutionHistoryRequest{ Execution: testExecution, MaximumPageSize: 1000, NextPageToken: nil, WaitForNewEvent: false, HistoryEventFilterType: types.HistoryEventFilterTypeAllEvent.Ptr(), SkipArchival: true, }).Return(partialWFHistoryResponse, nil) mockFrontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), &types.GetWorkflowExecutionHistoryRequest{ Execution: testExecution, MaximumPageSize: 1000, NextPageToken: token, WaitForNewEvent: false, HistoryEventFilterType: types.HistoryEventFilterTypeAllEvent.Ptr(), SkipArchival: true, }).Return(remainingWFHistoryResponse, nil).After(firstCall) retryMetadata := retry.RetryMetadata{ EventID: 1, RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 1, }, } retryMetadataInBytes, err := json.Marshal(retryMetadata) require.NoError(t, err) failureMetadataInBytes, err := json.Marshal(failure.FailureIssuesMetadata{}) require.NoError(t, err) expectedResult := []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: retry.WorkflowRetryIssue.String(), Reason: "MaximumAttempts set to 1 will not retry since maximum attempts includes the first attempt.", Metadata: retryMetadataInBytes, }, { IssueID: 0, InvariantType: failure.WorkflowFailed.String(), Reason: "The failure is caused by a timeout during the execution", Metadata: failureMetadataInBytes, }, } dwtest := &dw{ clientBean: mockClientBean, invariants: []invariant.Invariant{retry.NewInvariant(), failure.NewInvariant()}, } result, err := dwtest.identifyIssues(context.Background(), identifyIssuesParams{Execution: testExecution}) require.NoError(t, err) require.Equal(t, expectedResult, result) } ================================================ FILE: service/worker/diagnostics/analytics/emitter.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package analytics import ( "context" "encoding/json" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common" "github.com/uber/cadence/common/messaging" ) type Emitter DataEmitter type emitter struct { producer messaging.Producer } type EmitterParams struct { Producer messaging.Producer } func NewEmitter(p EmitterParams) DataEmitter { return &emitter{ producer: p.Producer, } } func (et *emitter) EmitUsageData(ctx context.Context, data WfDiagnosticsUsageData) error { msg := make(map[string]interface{}) msg[Domain] = data.Domain msg[WorkflowID] = data.WorkflowID msg[RunID] = data.RunID msg[Identity] = data.Identity msg[SatisfactionFeedback] = data.SatisfactionFeedback msg[IssueType] = data.IssueType msg[DiagnosticsWfID] = data.DiagnosticsWorkflowID msg[DiagnosticsWfRunID] = data.DiagnosticsRunID msg[Environment] = data.Environment msg[DiagnosticsStartTime] = data.DiagnosticsStartTime.UTC().UnixMilli() msg[DiagnosticsEndTime] = data.DiagnosticsEndTime.UTC().UnixMilli() serializedMsg, err := json.Marshal(msg) if err != nil { return err } pinotMsg := &indexer.PinotMessage{ WorkflowID: common.StringPtr(data.DiagnosticsWorkflowID), Payload: serializedMsg, } return et.producer.Publish(ctx, pinotMsg) } ================================================ FILE: service/worker/diagnostics/analytics/emitter_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package analytics import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common/mocks" ) func Test__EmitUsageData(t *testing.T) { testdata := WfDiagnosticsUsageData{ Domain: "test-domain", WorkflowID: "wid", RunID: "rid", Identity: "test@uber.com", IssueType: "timeout", DiagnosticsWorkflowID: "diagnostics-wid", DiagnosticsRunID: "diagnostics-rid", Environment: "test-env", DiagnosticsStartTime: time.Now(), DiagnosticsEndTime: time.Now().Add(1 * time.Minute), SatisfactionFeedback: false, } mockErr := errors.New("mockErr") tests := map[string]struct { data WfDiagnosticsUsageData producerMockAffordance func(mockProducer *mocks.KafkaProducer) expectedError error }{ "Case1: normal case": { data: testdata, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { require.Equal(t, testdata.DiagnosticsWorkflowID, input.GetWorkflowID()) return true })).Return(nil).Once() }, expectedError: nil, }, "Case1: error case": { data: testdata, producerMockAffordance: func(mockProducer *mocks.KafkaProducer) { mockProducer.On("Publish", mock.Anything, mock.MatchedBy(func(input *indexer.PinotMessage) bool { require.Equal(t, testdata.DiagnosticsWorkflowID, input.GetWorkflowID()) return true })).Return(mockErr).Once() }, expectedError: mockErr, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { mockProducer := &mocks.KafkaProducer{} emitter := NewEmitter(EmitterParams{ Producer: mockProducer, }) test.producerMockAffordance(mockProducer) err := emitter.EmitUsageData(context.Background(), test.data) if test.expectedError != nil { require.Error(t, err) } else { require.NoError(t, err) } }) } } ================================================ FILE: service/worker/diagnostics/analytics/interface.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package analytics import ( "context" "time" ) type WfDiagnosticsUsageData struct { Domain string WorkflowID string RunID string Identity string IssueType string DiagnosticsWorkflowID string DiagnosticsRunID string Environment string DiagnosticsStartTime time.Time DiagnosticsEndTime time.Time SatisfactionFeedback bool } // DataEmitter is the interface to emit workflow diagnostics data type DataEmitter interface { EmitUsageData(context.Context, WfDiagnosticsUsageData) error } ================================================ FILE: service/worker/diagnostics/analytics/types.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package analytics const ( Domain = "domain" WorkflowID = "workflowID" RunID = "runID" Identity = "identity" IssueType = "issue_type" DiagnosticsWfID = "diagnostics_workflowID" DiagnosticsWfRunID = "diagnostics_workflow_runID" SatisfactionFeedback = "satisfaction_feedback" Environment = "environment" DiagnosticsStartTime = "diagnostics_start_time" DiagnosticsEndTime = "diagnostics_end_time" ) ================================================ FILE: service/worker/diagnostics/invariant/failure/failure.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package failure import ( "context" "strings" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/diagnostics/invariant" ) // Failure is an invariant that will be used to identify the different failures in the workflow execution history type Failure invariant.Invariant type failure struct{} func NewInvariant() Failure { return &failure{} } func (f *failure) Check(ctx context.Context, params invariant.InvariantCheckInput) ([]invariant.InvariantCheckResult, error) { result := make([]invariant.InvariantCheckResult, 0) events := params.WorkflowExecutionHistory.GetHistory().GetEvents() issueID := 0 for _, event := range events { if event.GetWorkflowExecutionFailedEventAttributes() != nil && event.WorkflowExecutionFailedEventAttributes.Reason != nil { attr := event.WorkflowExecutionFailedEventAttributes reason := attr.Reason identity := fetchIdentity(attr, events) if *reason == common.FailureReasonDecisionBlobSizeExceedsLimit { result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: DecisionCausedFailure.String(), Reason: DecisionBlobSizeLimit.String(), Metadata: invariant.MarshalData(FailureIssuesMetadata{Identity: identity}), }) issueID++ } else { result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: WorkflowFailed.String(), Reason: ErrorTypeFromReason(*reason).String(), Metadata: invariant.MarshalData(FailureIssuesMetadata{Identity: identity}), }) issueID++ } } if event.GetActivityTaskFailedEventAttributes() != nil && event.ActivityTaskFailedEventAttributes.Reason != nil { attr := event.ActivityTaskFailedEventAttributes reason := attr.Reason scheduled := fetchScheduledEvent(attr, events) if *reason == common.FailureReasonHeartbeatExceedsLimit { result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: ActivityFailed.String(), Reason: HeartBeatBlobSizeLimit.String(), Metadata: invariant.MarshalData(FailureIssuesMetadata{ Identity: attr.Identity, ActivityType: scheduled.ActivityType.GetName(), ActivityScheduledID: attr.ScheduledEventID, ActivityStartedID: attr.StartedEventID, }), }) issueID++ } else if *reason == common.FailureReasonCompleteResultExceedsLimit { result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: ActivityFailed.String(), Reason: ActivityOutputBlobSizeLimit.String(), Metadata: invariant.MarshalData(FailureIssuesMetadata{ Identity: attr.Identity, ActivityType: scheduled.ActivityType.GetName(), ActivityScheduledID: attr.ScheduledEventID, ActivityStartedID: attr.StartedEventID, }), }) issueID++ } else { result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: ActivityFailed.String(), Reason: ErrorTypeFromReason(*reason).String(), Metadata: invariant.MarshalData(FailureIssuesMetadata{ Identity: attr.Identity, ActivityType: scheduled.ActivityType.GetName(), ActivityScheduledID: attr.ScheduledEventID, ActivityStartedID: attr.StartedEventID, }), }) issueID++ } } } return result, nil } func ErrorTypeFromReason(reason string) ErrorType { if strings.Contains(reason, "Generic") { return GenericError } if strings.Contains(reason, "Panic") { return PanicError } if strings.Contains(reason, "Timeout") { return TimeoutError } return CustomError } func fetchScheduledEvent(attr *types.ActivityTaskFailedEventAttributes, events []*types.HistoryEvent) *types.ActivityTaskScheduledEventAttributes { for _, event := range events { if event.ID == attr.GetScheduledEventID() { return event.GetActivityTaskScheduledEventAttributes() } } return nil } func fetchIdentity(attr *types.WorkflowExecutionFailedEventAttributes, events []*types.HistoryEvent) string { for _, event := range events { if event.ID == attr.DecisionTaskCompletedEventID { return event.GetDecisionTaskCompletedEventAttributes().Identity } } return "" } func (f *failure) RootCause(ctx context.Context, params invariant.InvariantRootCauseInput) ([]invariant.InvariantRootCauseResult, error) { result := make([]invariant.InvariantRootCauseResult, 0) for _, issue := range params.Issues { switch issue.Reason { case GenericError.String(): result = append(result, invariant.InvariantRootCauseResult{ IssueID: issue.IssueID, RootCause: invariant.RootCauseTypeServiceSideIssue, Metadata: issue.Metadata, }) case PanicError.String(): result = append(result, invariant.InvariantRootCauseResult{ IssueID: issue.IssueID, RootCause: invariant.RootCauseTypeServiceSidePanic, Metadata: issue.Metadata, }) case CustomError.String(): result = append(result, invariant.InvariantRootCauseResult{ IssueID: issue.IssueID, RootCause: invariant.RootCauseTypeServiceSideCustomError, Metadata: issue.Metadata, }) } } return result, nil } ================================================ FILE: service/worker/diagnostics/invariant/failure/failure_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package failure import ( "context" "encoding/json" "testing" "github.com/stretchr/testify/require" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/diagnostics/invariant" ) const ( testDomain = "test-domain" ) func Test__Check(t *testing.T) { metadata := FailureIssuesMetadata{ Identity: "localhost", } metadataInBytes, err := json.Marshal(metadata) require.NoError(t, err) actMetadata := FailureIssuesMetadata{ Identity: "localhost", ActivityType: "test-activity", ActivityScheduledID: 1, ActivityStartedID: 2, } actMetadataInBytes, err := json.Marshal(actMetadata) require.NoError(t, err) testCases := []struct { name string testData *types.GetWorkflowExecutionHistoryResponse expectedResult []invariant.InvariantCheckResult err error }{ { name: "workflow execution timeout", testData: failedWfHistory(), expectedResult: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: ActivityFailed.String(), Reason: GenericError.String(), Metadata: actMetadataInBytes, }, { IssueID: 1, InvariantType: ActivityFailed.String(), Reason: PanicError.String(), Metadata: actMetadataInBytes, }, { IssueID: 2, InvariantType: ActivityFailed.String(), Reason: CustomError.String(), Metadata: actMetadataInBytes, }, { IssueID: 3, InvariantType: WorkflowFailed.String(), Reason: TimeoutError.String(), Metadata: metadataInBytes, }, }, err: nil, }, { name: "blob size limit exceeded", testData: blobSizeLimitExceededHistory(), expectedResult: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: ActivityFailed.String(), Reason: ActivityOutputBlobSizeLimit.String(), Metadata: actMetadataInBytes, }, { IssueID: 1, InvariantType: DecisionCausedFailure.String(), Reason: DecisionBlobSizeLimit.String(), Metadata: metadataInBytes, }, }, err: nil, }, } for _, tc := range testCases { inv := NewInvariant() result, err := inv.Check(context.Background(), invariant.InvariantCheckInput{ WorkflowExecutionHistory: tc.testData, Domain: testDomain, }) require.Equal(t, tc.err, err) require.Equal(t, len(tc.expectedResult), len(result)) require.ElementsMatch(t, tc.expectedResult, result) } } func failedWfHistory() *types.GetWorkflowExecutionHistoryResponse { return &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { ID: 1, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityID: "101", ActivityType: &types.ActivityType{Name: "test-activity"}, }, }, { ID: 2, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ Identity: "localhost", Attempt: 0, }, }, { ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ Reason: common.StringPtr("cadenceInternal:Generic"), Details: []byte("test-activity-failure"), Identity: "localhost", ScheduledEventID: 1, StartedEventID: 2, }, }, { ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ Reason: common.StringPtr("cadenceInternal:Panic"), Details: []byte("test-activity-failure"), Identity: "localhost", ScheduledEventID: 1, StartedEventID: 2, }, }, { ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ Reason: common.StringPtr("custom error"), Details: []byte("test-activity-failure"), Identity: "localhost", ScheduledEventID: 1, StartedEventID: 2, }, }, { ID: 10, DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ Identity: "localhost", }, }, { WorkflowExecutionFailedEventAttributes: &types.WorkflowExecutionFailedEventAttributes{ Reason: common.StringPtr("cadenceInternal:Timeout START_TO_CLOSE"), Details: []byte("test-activity-failure"), DecisionTaskCompletedEventID: 10, }, }, }, }, } } func blobSizeLimitExceededHistory() *types.GetWorkflowExecutionHistoryResponse { return &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { ID: 1, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ActivityID: "101", ActivityType: &types.ActivityType{Name: "test-activity"}, }, }, { ID: 2, ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ Identity: "localhost", Attempt: 0, }, }, { ActivityTaskFailedEventAttributes: &types.ActivityTaskFailedEventAttributes{ Reason: common.StringPtr("COMPLETE_RESULT_EXCEEDS_LIMIT"), Details: []byte("test-activity-failure"), Identity: "localhost", ScheduledEventID: 1, StartedEventID: 2, }, }, { ID: 10, DecisionTaskCompletedEventAttributes: &types.DecisionTaskCompletedEventAttributes{ Identity: "localhost", }, }, { WorkflowExecutionFailedEventAttributes: &types.WorkflowExecutionFailedEventAttributes{ Reason: common.StringPtr("DECISION_BLOB_SIZE_EXCEEDS_LIMIT"), Details: []byte("test-wf-failure"), DecisionTaskCompletedEventID: 10, }, }, }, }, } } func Test__RootCause(t *testing.T) { metadata := FailureIssuesMetadata{ Identity: "localhost", } metadataInBytes, err := json.Marshal(metadata) require.NoError(t, err) testCases := []struct { name string input []invariant.InvariantCheckResult expectedResult []invariant.InvariantRootCauseResult err error }{ { name: "customer side known failure", input: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: ActivityFailed.String(), Reason: CustomError.String(), Metadata: metadataInBytes, }}, expectedResult: []invariant.InvariantRootCauseResult{{ IssueID: 0, RootCause: invariant.RootCauseTypeServiceSideCustomError, Metadata: metadataInBytes, }}, err: nil, }, { name: "customer side error", input: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: ActivityFailed.String(), Reason: GenericError.String(), Metadata: metadataInBytes, }}, expectedResult: []invariant.InvariantRootCauseResult{{ IssueID: 0, RootCause: invariant.RootCauseTypeServiceSideIssue, Metadata: metadataInBytes, }}, err: nil, }, { name: "customer side panic", input: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: ActivityFailed.String(), Reason: PanicError.String(), Metadata: metadataInBytes, }}, expectedResult: []invariant.InvariantRootCauseResult{{ IssueID: 0, RootCause: invariant.RootCauseTypeServiceSidePanic, Metadata: metadataInBytes, }}, err: nil, }, } inv := NewInvariant() for _, tc := range testCases { result, err := inv.RootCause(context.Background(), invariant.InvariantRootCauseInput{ Issues: tc.input, }) require.Equal(t, tc.err, err) require.Equal(t, len(tc.expectedResult), len(result)) require.ElementsMatch(t, tc.expectedResult, result) } } ================================================ FILE: service/worker/diagnostics/invariant/failure/types.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package failure type ErrorType string const ( CustomError ErrorType = "The failure is caused by a specific custom error returned from the service code" GenericError ErrorType = "The failure is because of an error returned from the service code" PanicError ErrorType = "The failure is caused by a panic in the service code" TimeoutError ErrorType = "The failure is caused by a timeout during the execution" HeartBeatBlobSizeLimit ErrorType = "Heartbeat details has exceeded the blob size limit" ActivityOutputBlobSizeLimit ErrorType = "Activity output has exceeded the blob size limit" DecisionBlobSizeLimit ErrorType = "Decision result caused to exceed blob size limit" ) func (e ErrorType) String() string { return string(e) } type FailureType string const ( ActivityFailed FailureType = "Activity Failed" WorkflowFailed FailureType = "Workflow Failed" DecisionCausedFailure FailureType = "Decision caused failure" ) func (f FailureType) String() string { return string(f) } type FailureIssuesMetadata struct { Identity string ActivityType string ActivityScheduledID int64 ActivityStartedID int64 } // BlobSizeMetadata includes the details of blob size limits type BlobSizeMetadata struct { BlobSizeWarnLimit int32 BlobSizeErrorLimit int32 } type FailureRootcauseMetadata struct { BlobSizeMetadata *BlobSizeMetadata } ================================================ FILE: service/worker/diagnostics/invariant/interface.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package invariant import ( "context" "encoding/json" "github.com/uber/cadence/common/types" ) // InvariantCheckResult is the result from the invariant check type InvariantCheckResult struct { IssueID int InvariantType string Reason string Metadata []byte } // InvariantRootCauseResult is the root cause for the issues identified in the invariant check type InvariantRootCauseResult struct { IssueID int RootCause RootCause Metadata []byte } type InvariantCheckInput struct { WorkflowExecutionHistory *types.GetWorkflowExecutionHistoryResponse Domain string } type InvariantRootCauseInput struct { Domain string Issues []InvariantCheckResult } type RootCause string const ( RootCauseTypeMissingPollers RootCause = "There are no pollers for the tasklist" RootCauseTypePollersStatus RootCause = "There are pollers for the tasklist. Check backlog status" RootCauseTypeNoHeartBeatTimeoutNoRetryPolicy RootCause = "Heartbeat timeout and retry policy are not configured" RootCauseTypeHeartBeatingNotEnabledWithRetryPolicy RootCause = "Heartbeat timeout not enabled for activity but there is a retry policy configured" RootCauseTypeHeartBeatingEnabledWithoutRetryPolicy RootCause = "Heartbeat timeout enabled for activity but there is no retry policy configured" RootCauseTypeHeartBeatingEnabledMissingHeartbeat RootCause = "Heartbeat timeout enabled for activity but timed out due to missing heartbeat" RootCauseTypeServiceSideIssue RootCause = "There is an issue in the worker service that is causing a failure. Check identity for service logs" RootCauseTypeServiceSidePanic RootCause = "There is a panic in the activity/workflow that is causing a failure" RootCauseTypeServiceSideCustomError RootCause = "Customised error returned by the activity/workflow" RootCauseTypeBlobSizeLimit RootCause = "Workflow has exceeded the blob size limits configured for the domain" ) func (r RootCause) String() string { return string(r) } // Invariant represents a condition of a workflow execution. type Invariant interface { Check(context.Context, InvariantCheckInput) ([]InvariantCheckResult, error) RootCause(context.Context, InvariantRootCauseInput) ([]InvariantRootCauseResult, error) } func MarshalData(rc any) []byte { data, _ := json.Marshal(rc) return data } ================================================ FILE: service/worker/diagnostics/invariant/retry/retry.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package retry import ( "context" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/diagnostics/invariant" ) // Retry is an invariant that will be used to identify the issues regarding retries in the workflow execution history type Retry invariant.Invariant type retry struct { } func NewInvariant() Retry { return &retry{} } func (r *retry) Check(ctx context.Context, params invariant.InvariantCheckInput) ([]invariant.InvariantCheckResult, error) { result := make([]invariant.InvariantCheckResult, 0) events := params.WorkflowExecutionHistory.GetHistory().GetEvents() issueID := 0 startedEvent := fetchWfStartedEvent(events) if issue := checkRetryPolicy(startedEvent.RetryPolicy); issue != "" { result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: WorkflowRetryIssue.String(), Reason: issue.String(), Metadata: invariant.MarshalData(RetryMetadata{ EventID: 1, RetryPolicy: startedEvent.RetryPolicy, }), }) issueID++ } for _, event := range events { if event.GetActivityTaskScheduledEventAttributes() != nil { attr := event.GetActivityTaskScheduledEventAttributes() if issue := checkRetryPolicy(attr.RetryPolicy); issue != "" { result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: ActivityRetryIssue.String(), Reason: issue.String(), Metadata: invariant.MarshalData(RetryMetadata{ EventID: event.ID, RetryPolicy: attr.RetryPolicy, }), }) issueID++ } if attr.GetStartToCloseTimeoutSeconds() <= attr.GetHeartbeatTimeoutSeconds() { result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: ActivityHeartbeatIssue.String(), Reason: HeartBeatTimeoutEqualToStartToCloseTimeout.String(), Metadata: invariant.MarshalData(RetryMetadata{ EventID: event.ID, }), }) issueID++ } } } return result, nil } func fetchWfStartedEvent(events []*types.HistoryEvent) *types.WorkflowExecutionStartedEventAttributes { for _, event := range events { if event.GetWorkflowExecutionStartedEventAttributes() != nil { return event.GetWorkflowExecutionStartedEventAttributes() } } return nil } func checkRetryPolicy(policy *types.RetryPolicy) IssueType { if policy == nil { return "" } if policy.GetExpirationIntervalInSeconds() == 0 && policy.GetMaximumAttempts() == 1 { return RetryPolicyValidationMaxAttempts } if policy.GetMaximumAttempts() == 0 && policy.GetExpirationIntervalInSeconds() < policy.GetInitialIntervalInSeconds() { return RetryPolicyValidationExpInterval } return "" } func (r *retry) RootCause(ctx context.Context, params invariant.InvariantRootCauseInput) ([]invariant.InvariantRootCauseResult, error) { // Not implemented since this invariant does not have any root cause. // Issue identified in Check() are the root cause. result := make([]invariant.InvariantRootCauseResult, 0) return result, nil } ================================================ FILE: service/worker/diagnostics/invariant/retry/retry_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package retry import ( "context" "encoding/json" "testing" "github.com/stretchr/testify/require" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/diagnostics/invariant" ) func Test__Check(t *testing.T) { invalidAttemptsMetadata := RetryMetadata{ EventID: 5, RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 1, }, } invalidAttemptsMetadataInBytes, err := json.Marshal(invalidAttemptsMetadata) require.NoError(t, err) invalidExpIntervalMetadata := RetryMetadata{ EventID: 1, RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 10, ExpirationIntervalInSeconds: 5, }, } invalidExpIntervalMetadataInBytes, err := json.Marshal(invalidExpIntervalMetadata) require.NoError(t, err) invalidHBMetadata := RetryMetadata{ EventID: 6, } invalidHBMetadataInBytes, err := json.Marshal(invalidHBMetadata) require.NoError(t, err) testCases := []struct { name string testData *types.GetWorkflowExecutionHistoryResponse expectedResult []invariant.InvariantCheckResult err error }{ { name: "invalid retry policy", testData: invalidRetryPolicyWfHistory(), expectedResult: []invariant.InvariantCheckResult{ { IssueID: 1, InvariantType: ActivityRetryIssue.String(), Reason: RetryPolicyValidationMaxAttempts.String(), Metadata: invalidAttemptsMetadataInBytes, }, { IssueID: 0, InvariantType: WorkflowRetryIssue.String(), Reason: RetryPolicyValidationExpInterval.String(), Metadata: invalidExpIntervalMetadataInBytes, }, { IssueID: 2, InvariantType: ActivityHeartbeatIssue.String(), Reason: HeartBeatTimeoutEqualToStartToCloseTimeout.String(), Metadata: invalidHBMetadataInBytes, }, }, err: nil, }, } for _, tc := range testCases { inv := NewInvariant() result, err := inv.Check(context.Background(), invariant.InvariantCheckInput{ WorkflowExecutionHistory: tc.testData, }) require.Equal(t, tc.err, err) require.Equal(t, len(tc.expectedResult), len(result)) require.ElementsMatch(t, tc.expectedResult, result) } } func retriedWfHistory() *types.GetWorkflowExecutionHistoryResponse { return &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 2, }, Attempt: 0, }, }, { WorkflowExecutionContinuedAsNewEventAttributes: &types.WorkflowExecutionContinuedAsNewEventAttributes{ FailureReason: common.StringPtr("cadenceInternal:Timeout START_TO_CLOSE"), DecisionTaskCompletedEventID: 10, }, }, }, }, } } func invalidRetryPolicyWfHistory() *types.GetWorkflowExecutionHistoryResponse { timeout := int32(5) return &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { ID: 1, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 10, ExpirationIntervalInSeconds: 5, }, }, }, { ID: 5, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ HeartbeatTimeoutSeconds: common.Int32Ptr(timeout), StartToCloseTimeoutSeconds: common.Int32Ptr(timeout * 2), RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 1, }, }, }, { ID: 6, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ HeartbeatTimeoutSeconds: common.Int32Ptr(timeout), StartToCloseTimeoutSeconds: common.Int32Ptr(timeout), RetryPolicy: nil, }, }, }, }, } } ================================================ FILE: service/worker/diagnostics/invariant/retry/types.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package retry import "github.com/uber/cadence/common/types" type RetryType string const ( WorkflowRetryIssue RetryType = "Workflow Retry configured but invalid" ActivityRetryIssue RetryType = "Activity Retry configured but invalid" ActivityHeartbeatIssue RetryType = "Activity Heartbeat configured but invalid" ) func (r RetryType) String() string { return string(r) } type IssueType string const ( RetryPolicyValidationMaxAttempts IssueType = "MaximumAttempts set to 1 will not retry since maximum attempts includes the first attempt." RetryPolicyValidationExpInterval IssueType = "ExpirationIntervalInSeconds less than InitialIntervalInSeconds will not retry." HeartBeatTimeoutEqualToStartToCloseTimeout IssueType = "Heartbeat timeout being equal or higher than StartToClose timeout will not provide any benefit." ) func (i IssueType) String() string { return string(i) } type RetryMetadata struct { EventID int64 RetryPolicy *types.RetryPolicy } ================================================ FILE: service/worker/diagnostics/invariant/timeout/timeout.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package timeout import ( "context" "encoding/json" "fmt" "time" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/.gen/go/shared" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/diagnostics/invariant" ) type Timeout invariant.Invariant type timeout struct { client workflowserviceclient.Interface } type Params struct { Client workflowserviceclient.Interface } func NewInvariant(p Params) invariant.Invariant { return &timeout{ client: p.Client, } } func (t *timeout) Check(ctx context.Context, params invariant.InvariantCheckInput) ([]invariant.InvariantCheckResult, error) { result := make([]invariant.InvariantCheckResult, 0) events := params.WorkflowExecutionHistory.GetHistory().GetEvents() issueID := 0 for _, event := range events { if event.WorkflowExecutionTimedOutEventAttributes != nil { timeoutLimit := getWorkflowExecutionConfiguredTimeout(events) data := ExecutionTimeoutMetadata{ ExecutionTime: getExecutionTime(1, event.ID, events), ConfiguredTimeout: time.Duration(timeoutLimit) * time.Second, LastOngoingEvent: events[len(events)-2], Tasklist: getWorkflowExecutionTasklist(events), } result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: TimeoutTypeExecution.String(), Reason: event.GetWorkflowExecutionTimedOutEventAttributes().GetTimeoutType().String(), Metadata: invariant.MarshalData(data), }) issueID++ } if event.ActivityTaskTimedOutEventAttributes != nil { metadata, err := getActivityTaskMetadata(event, events) if err != nil { return nil, err } result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: TimeoutTypeActivity.String(), Reason: event.GetActivityTaskTimedOutEventAttributes().GetTimeoutType().String(), Metadata: invariant.MarshalData(metadata), }) issueID++ } if event.DecisionTaskTimedOutEventAttributes != nil { reason, metadata := reasonForDecisionTaskTimeouts(event, events) result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: TimeoutTypeDecision.String(), Reason: reason, Metadata: invariant.MarshalData(metadata), }) issueID++ } if event.ChildWorkflowExecutionTimedOutEventAttributes != nil { timeoutLimit := getChildWorkflowExecutionConfiguredTimeout(event, events) data := ChildWfTimeoutMetadata{ ExecutionTime: getExecutionTime(event.GetChildWorkflowExecutionTimedOutEventAttributes().StartedEventID, event.ID, events), ConfiguredTimeout: time.Duration(timeoutLimit) * time.Second, Execution: event.GetChildWorkflowExecutionTimedOutEventAttributes().WorkflowExecution, } result = append(result, invariant.InvariantCheckResult{ IssueID: issueID, InvariantType: TimeoutTypeChildWorkflow.String(), Reason: event.GetChildWorkflowExecutionTimedOutEventAttributes().TimeoutType.String(), Metadata: invariant.MarshalData(data), }) issueID++ } } return result, nil } func (t *timeout) RootCause(ctx context.Context, params invariant.InvariantRootCauseInput) ([]invariant.InvariantRootCauseResult, error) { result := make([]invariant.InvariantRootCauseResult, 0) for _, issue := range params.Issues { if issue.InvariantType == TimeoutTypeActivity.String() || issue.InvariantType == TimeoutTypeExecution.String() { pollerStatus, err := t.checkTasklist(ctx, issue, params.Domain) if err != nil { return nil, err } result = append(result, pollerStatus) } if issue.InvariantType == TimeoutTypeActivity.String() { heartbeatStatus, err := checkHeartbeatStatus(issue) if err != nil { return nil, err } result = append(result, heartbeatStatus...) } } return result, nil } func (t *timeout) checkTasklist(ctx context.Context, issue invariant.InvariantCheckResult, domain string) (invariant.InvariantRootCauseResult, error) { var taskList *types.TaskList var tasklistType *shared.TaskListType switch issue.InvariantType { case TimeoutTypeExecution.String(): var metadata ExecutionTimeoutMetadata err := json.Unmarshal(issue.Metadata, &metadata) if err != nil { return invariant.InvariantRootCauseResult{}, err } taskList = metadata.Tasklist tasklistType = shared.TaskListTypeDecision.Ptr() case TimeoutTypeActivity.String(): var metadata ActivityTimeoutMetadata err := json.Unmarshal(issue.Metadata, &metadata) if err != nil { return invariant.InvariantRootCauseResult{}, err } taskList = metadata.Tasklist tasklistType = shared.TaskListTypeActivity.Ptr() } if taskList == nil { return invariant.InvariantRootCauseResult{}, fmt.Errorf("tasklist not set") } resp, err := t.client.DescribeTaskList(ctx, &shared.DescribeTaskListRequest{ Domain: &domain, TaskList: &shared.TaskList{ Name: &taskList.Name, Kind: taskListKind(taskList.GetKind()), }, TaskListType: tasklistType, }) if err != nil { return invariant.InvariantRootCauseResult{}, err } tasklistBacklog := resp.GetTaskListStatus().GetBacklogCountHint() polllersMetadataInBytes := invariant.MarshalData(PollersMetadata{ TaskListName: taskList.Name, TaskListBacklog: tasklistBacklog, }) if len(resp.GetPollers()) == 0 { return invariant.InvariantRootCauseResult{ IssueID: issue.IssueID, RootCause: invariant.RootCauseTypeMissingPollers, Metadata: polllersMetadataInBytes, }, nil } return invariant.InvariantRootCauseResult{ IssueID: issue.IssueID, RootCause: invariant.RootCauseTypePollersStatus, Metadata: polllersMetadataInBytes, }, nil } func taskListKind(kind types.TaskListKind) *shared.TaskListKind { if kind.String() == shared.TaskListKindNormal.String() { return shared.TaskListKindNormal.Ptr() } return shared.TaskListKindSticky.Ptr() } func checkHeartbeatStatus(issue invariant.InvariantCheckResult) ([]invariant.InvariantRootCauseResult, error) { var metadata ActivityTimeoutMetadata err := json.Unmarshal(issue.Metadata, &metadata) if err != nil { return nil, err } heartbeatingMetadataInBytes := invariant.MarshalData(HeartbeatingMetadata{TimeElapsed: metadata.TimeElapsed, RetryPolicy: metadata.RetryPolicy}) if metadata.HeartBeatTimeout == 0 && activityStarted(metadata) { if metadata.RetryPolicy != nil { return []invariant.InvariantRootCauseResult{ { IssueID: issue.IssueID, RootCause: invariant.RootCauseTypeHeartBeatingNotEnabledWithRetryPolicy, Metadata: heartbeatingMetadataInBytes, }, }, nil } return []invariant.InvariantRootCauseResult{ { IssueID: issue.IssueID, RootCause: invariant.RootCauseTypeNoHeartBeatTimeoutNoRetryPolicy, Metadata: heartbeatingMetadataInBytes, }, }, nil } if metadata.HeartBeatTimeout > 0 && metadata.TimeoutType.String() == types.TimeoutTypeHeartbeat.String() { if metadata.RetryPolicy == nil { return []invariant.InvariantRootCauseResult{ { IssueID: issue.IssueID, RootCause: invariant.RootCauseTypeHeartBeatingEnabledWithoutRetryPolicy, Metadata: heartbeatingMetadataInBytes, }, }, nil } return []invariant.InvariantRootCauseResult{ { IssueID: issue.IssueID, RootCause: invariant.RootCauseTypeHeartBeatingEnabledMissingHeartbeat, Metadata: heartbeatingMetadataInBytes, }, }, nil } return nil, nil } func activityStarted(metadata ActivityTimeoutMetadata) bool { return metadata.TimeoutType.String() != types.TimeoutTypeScheduleToStart.String() } ================================================ FILE: service/worker/diagnostics/invariant/timeout/timeout_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package timeout import ( "context" "encoding/json" "testing" "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" publicservicetest "go.uber.org/cadence/.gen/go/cadence/workflowservicetest" "go.uber.org/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/diagnostics/invariant" ) const ( workflowTimeoutSecond = int32(110) taskTimeoutSecond = int32(50) testTimeStamp = int64(2547596872371000000) timeUnit = time.Second testTasklist = "test-tasklist" testDomain = "test-domain" testTaskListBacklog = int64(10) ) func Test__Check(t *testing.T) { decisionTimeoutMetadata := DecisionTimeoutMetadata{ConfiguredTimeout: 50 * time.Second} decisionTimeoutMetadataInBytes, err := json.Marshal(decisionTimeoutMetadata) require.NoError(t, err) testCases := []struct { name string testData *types.GetWorkflowExecutionHistoryResponse expectedResult []invariant.InvariantCheckResult err error }{ { name: "workflow execution timeout", testData: wfTimeoutHistory(), expectedResult: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: TimeoutTypeExecution.String(), Reason: "START_TO_CLOSE", Metadata: wfTimeoutDataInBytes(t), }, }, err: nil, }, { name: "child workflow execution timeout", testData: childWfTimeoutHistory(), expectedResult: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: TimeoutTypeChildWorkflow.String(), Reason: "START_TO_CLOSE", Metadata: childWfTimeoutDataInBytes(t), }, }, err: nil, }, { name: "activity timeout", testData: activityTimeoutHistory(), expectedResult: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: TimeoutTypeActivity.String(), Reason: "SCHEDULE_TO_START", Metadata: activityScheduleToStartTimeoutDataInBytes(t), }, { IssueID: 1, InvariantType: TimeoutTypeActivity.String(), Reason: "HEARTBEAT", Metadata: activityHeartBeatTimeoutDataInBytes(t), }, }, err: nil, }, { name: "decision timeout", testData: decisionTimeoutHistory(), expectedResult: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: TimeoutTypeDecision.String(), Reason: "START_TO_CLOSE", Metadata: decisionTimeoutMetadataInBytes, }, { IssueID: 1, InvariantType: TimeoutTypeDecision.String(), Reason: "workflow reset - New run ID: new run ID", Metadata: decisionTimeoutMetadataInBytes, }, }, err: nil, }, } ctrl := gomock.NewController(t) mockClient := publicservicetest.NewMockClient(ctrl) for _, tc := range testCases { inv := NewInvariant(Params{ Client: mockClient, }) result, err := inv.Check(context.Background(), invariant.InvariantCheckInput{ WorkflowExecutionHistory: tc.testData, Domain: testDomain, }) require.Equal(t, tc.err, err) require.Equal(t, len(tc.expectedResult), len(result)) for i := range result { require.Equal(t, tc.expectedResult[i], result[i]) } } } func wfTimeoutHistory() *types.GetWorkflowExecutionHistoryResponse { return &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { ID: 1, Timestamp: common.Int64Ptr(testTimeStamp), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(workflowTimeoutSecond), TaskList: &types.TaskList{ Name: testTasklist, Kind: nil, }, }, }, { ID: 2, Timestamp: common.Int64Ptr(testTimeStamp + int64(workflowTimeoutSecond)*timeUnit.Nanoseconds()), WorkflowExecutionTimedOutEventAttributes: &types.WorkflowExecutionTimedOutEventAttributes{TimeoutType: types.TimeoutTypeStartToClose.Ptr()}, }, }, }, } } func childWfTimeoutHistory() *types.GetWorkflowExecutionHistoryResponse { return &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { ID: 1, StartChildWorkflowExecutionInitiatedEventAttributes: &types.StartChildWorkflowExecutionInitiatedEventAttributes{ ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(workflowTimeoutSecond), }, }, { ID: 2, Timestamp: common.Int64Ptr(testTimeStamp), ChildWorkflowExecutionStartedEventAttributes: &types.ChildWorkflowExecutionStartedEventAttributes{ InitiatedEventID: 1, }, }, { ID: 3, Timestamp: common.Int64Ptr(testTimeStamp + int64(workflowTimeoutSecond)*timeUnit.Nanoseconds()), ChildWorkflowExecutionTimedOutEventAttributes: &types.ChildWorkflowExecutionTimedOutEventAttributes{ InitiatedEventID: 1, StartedEventID: 2, TimeoutType: types.TimeoutTypeStartToClose.Ptr(), WorkflowExecution: &types.WorkflowExecution{ WorkflowID: "123", RunID: "abc", }, }, }, }, }, } } func activityTimeoutHistory() *types.GetWorkflowExecutionHistoryResponse { return &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { ID: 1, Timestamp: common.Int64Ptr(testTimeStamp), ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ ScheduleToStartTimeoutSeconds: common.Int32Ptr(taskTimeoutSecond), TaskList: &types.TaskList{ Name: testTasklist, Kind: nil, }, }, }, { ID: 2, Timestamp: common.Int64Ptr(testTimeStamp + int64(taskTimeoutSecond)*timeUnit.Nanoseconds()), ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 1, TimeoutType: types.TimeoutTypeScheduleToStart.Ptr(), }, }, { ID: 3, ActivityTaskScheduledEventAttributes: &types.ActivityTaskScheduledEventAttributes{ HeartbeatTimeoutSeconds: common.Int32Ptr(taskTimeoutSecond), TaskList: &types.TaskList{ Name: testTasklist, Kind: nil, }, }, }, { ID: 4, Timestamp: common.Int64Ptr(testTimeStamp), ActivityTaskStartedEventAttributes: &types.ActivityTaskStartedEventAttributes{ ScheduledEventID: 21, }, }, { ID: 5, Timestamp: common.Int64Ptr(testTimeStamp + int64(taskTimeoutSecond)*timeUnit.Nanoseconds()), ActivityTaskTimedOutEventAttributes: &types.ActivityTaskTimedOutEventAttributes{ ScheduledEventID: 3, StartedEventID: 4, TimeoutType: types.TimeoutTypeHeartbeat.Ptr(), }, }, }, }, } } func decisionTimeoutHistory() *types.GetWorkflowExecutionHistoryResponse { return &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { ID: 13, DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ StartToCloseTimeoutSeconds: common.Int32Ptr(taskTimeoutSecond), }, }, { DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: 13, StartedEventID: 14, Cause: types.DecisionTaskTimedOutCauseTimeout.Ptr(), TimeoutType: types.TimeoutTypeStartToClose.Ptr(), }, }, { ID: 23, DecisionTaskScheduledEventAttributes: &types.DecisionTaskScheduledEventAttributes{ StartToCloseTimeoutSeconds: common.Int32Ptr(taskTimeoutSecond), }, }, { DecisionTaskTimedOutEventAttributes: &types.DecisionTaskTimedOutEventAttributes{ ScheduledEventID: 23, Cause: types.DecisionTaskTimedOutCauseReset.Ptr(), Reason: "workflow reset", NewRunID: "new run ID", }, }, }, }, } } func wfTimeoutDataInBytes(t *testing.T) []byte { data := ExecutionTimeoutMetadata{ ExecutionTime: 110 * time.Second, ConfiguredTimeout: 110 * time.Second, LastOngoingEvent: &types.HistoryEvent{ ID: 1, Timestamp: common.Int64Ptr(testTimeStamp), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(workflowTimeoutSecond), TaskList: &types.TaskList{ Name: testTasklist, Kind: nil, }, }, }, Tasklist: &types.TaskList{ Name: testTasklist, Kind: nil, }, } dataInBytes, err := json.Marshal(data) require.NoError(t, err) return dataInBytes } func activityScheduleToStartTimeoutData() ActivityTimeoutMetadata { return ActivityTimeoutMetadata{ TimeoutType: types.TimeoutTypeScheduleToStart.Ptr(), ConfiguredTimeout: 50 * time.Second, TimeElapsed: 50 * time.Second, RetryPolicy: nil, HeartBeatTimeout: 0, Tasklist: &types.TaskList{ Name: testTasklist, Kind: nil, }, } } func activityStartToCloseTimeoutData() ActivityTimeoutMetadata { return ActivityTimeoutMetadata{ TimeoutType: types.TimeoutTypeStartToClose.Ptr(), ConfiguredTimeout: 50 * time.Second, TimeElapsed: 50 * time.Second, RetryPolicy: nil, HeartBeatTimeout: 0, Tasklist: &types.TaskList{ Name: testTasklist, Kind: nil, }, } } func activityScheduleToStartTimeoutDataInBytes(t *testing.T) []byte { data := activityScheduleToStartTimeoutData() dataInBytes, err := json.Marshal(data) require.NoError(t, err) return dataInBytes } func activityStartToCloseTimeoutDataInBytes(t *testing.T) []byte { data := activityStartToCloseTimeoutData() dataInBytes, err := json.Marshal(data) require.NoError(t, err) return dataInBytes } func activityHeartBeatTimeoutDataInBytes(t *testing.T) []byte { actTimeoutData := activityStartToCloseTimeoutData() actTimeoutData.TimeoutType = types.TimeoutTypeHeartbeat.Ptr() actTimeoutData.HeartBeatTimeout = 50 * time.Second actHeartBeatTimeoutDataInBytes, err := json.Marshal(actTimeoutData) require.NoError(t, err) return actHeartBeatTimeoutDataInBytes } func activityHeartBeatTimeoutDataWithRetryPolicyInBytes(t *testing.T) []byte { actTimeoutData := activityStartToCloseTimeoutData() actTimeoutData.TimeoutType = types.TimeoutTypeHeartbeat.Ptr() actTimeoutData.HeartBeatTimeout = 50 * time.Second actTimeoutData.RetryPolicy = &types.RetryPolicy{ MaximumAttempts: 3, } actHeartBeatTimeoutDataInBytes, err := json.Marshal(actTimeoutData) require.NoError(t, err) return actHeartBeatTimeoutDataInBytes } func childWfTimeoutDataInBytes(t *testing.T) []byte { data := ChildWfTimeoutMetadata{ ExecutionTime: 110 * time.Second, ConfiguredTimeout: 110 * time.Second, Execution: &types.WorkflowExecution{ WorkflowID: "123", RunID: "abc", }, } dataInBytes, err := json.Marshal(data) require.NoError(t, err) return dataInBytes } func Test__RootCause(t *testing.T) { actStartToCloseTimeoutData := activityStartToCloseTimeoutData() pollersMetadataInBytes, err := json.Marshal(PollersMetadata{TaskListName: testTasklist, TaskListBacklog: testTaskListBacklog}) require.NoError(t, err) heartBeatingMetadataInBytes, err := json.Marshal(HeartbeatingMetadata{TimeElapsed: actStartToCloseTimeoutData.TimeElapsed}) require.NoError(t, err) heartBeatingMetadataWithRetryPolicyInBytes, err := json.Marshal(HeartbeatingMetadata{TimeElapsed: actStartToCloseTimeoutData.TimeElapsed, RetryPolicy: &types.RetryPolicy{MaximumAttempts: 3}}) require.NoError(t, err) testCases := []struct { name string input []invariant.InvariantCheckResult clientExpects func(client *publicservicetest.MockClient) expectedResult []invariant.InvariantRootCauseResult err error }{ { name: "workflow execution timeout without pollers", input: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: TimeoutTypeExecution.String(), Reason: "START_TO_CLOSE", Metadata: wfTimeoutDataInBytes(t), }, }, clientExpects: func(client *publicservicetest.MockClient) { client.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any()).Return(&shared.DescribeTaskListResponse{ Pollers: nil, TaskListStatus: &shared.TaskListStatus{ BacklogCountHint: common.Int64Ptr(testTaskListBacklog), }, }, nil) }, expectedResult: []invariant.InvariantRootCauseResult{ { IssueID: 0, RootCause: invariant.RootCauseTypeMissingPollers, Metadata: pollersMetadataInBytes, }, }, err: nil, }, { name: "workflow execution timeout with pollers", input: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: TimeoutTypeExecution.String(), Reason: "START_TO_CLOSE", Metadata: wfTimeoutDataInBytes(t), }, }, clientExpects: func(client *publicservicetest.MockClient) { client.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any()).Return(&shared.DescribeTaskListResponse{ Pollers: []*shared.PollerInfo{ { Identity: common.StringPtr("dca24-xy"), }, }, TaskListStatus: &shared.TaskListStatus{ BacklogCountHint: common.Int64Ptr(testTaskListBacklog), }, }, nil) }, expectedResult: []invariant.InvariantRootCauseResult{ { IssueID: 0, RootCause: invariant.RootCauseTypePollersStatus, Metadata: pollersMetadataInBytes, }, }, err: nil, }, { name: "activity timeout and heart beating not enabled", input: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: TimeoutTypeActivity.String(), Reason: "START_TO_CLOSE", Metadata: activityStartToCloseTimeoutDataInBytes(t), }, }, clientExpects: func(client *publicservicetest.MockClient) { client.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any()).Return(&shared.DescribeTaskListResponse{ Pollers: []*shared.PollerInfo{ { Identity: common.StringPtr("dca24-xy"), }, }, TaskListStatus: &shared.TaskListStatus{ BacklogCountHint: common.Int64Ptr(testTaskListBacklog), }, }, nil) }, expectedResult: []invariant.InvariantRootCauseResult{ { IssueID: 0, RootCause: invariant.RootCauseTypePollersStatus, Metadata: pollersMetadataInBytes, }, { IssueID: 0, RootCause: invariant.RootCauseTypeNoHeartBeatTimeoutNoRetryPolicy, Metadata: heartBeatingMetadataInBytes, }, }, err: nil, }, { name: "activity schedule to start timeout", input: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: TimeoutTypeActivity.String(), Reason: "SCHEDULE_TO_START", Metadata: activityScheduleToStartTimeoutDataInBytes(t), }, }, clientExpects: func(client *publicservicetest.MockClient) { client.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any()).Return(&shared.DescribeTaskListResponse{ Pollers: []*shared.PollerInfo{ { Identity: common.StringPtr("dca24-xy"), }, }, TaskListStatus: &shared.TaskListStatus{ BacklogCountHint: common.Int64Ptr(testTaskListBacklog), }, }, nil) }, expectedResult: []invariant.InvariantRootCauseResult{ { IssueID: 0, RootCause: invariant.RootCauseTypePollersStatus, Metadata: pollersMetadataInBytes, }, }, err: nil, }, { name: "activity timeout and heart beating enabled with retry policy", input: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: TimeoutTypeActivity.String(), Reason: "START_TO_CLOSE", Metadata: activityHeartBeatTimeoutDataWithRetryPolicyInBytes(t), }, }, clientExpects: func(client *publicservicetest.MockClient) { client.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any()).Return(&shared.DescribeTaskListResponse{ Pollers: []*shared.PollerInfo{ { Identity: common.StringPtr("dca24-xy"), }, }, TaskListStatus: &shared.TaskListStatus{ BacklogCountHint: common.Int64Ptr(testTaskListBacklog), }, }, nil) }, expectedResult: []invariant.InvariantRootCauseResult{ { IssueID: 0, RootCause: invariant.RootCauseTypePollersStatus, Metadata: pollersMetadataInBytes, }, { IssueID: 0, RootCause: invariant.RootCauseTypeHeartBeatingEnabledMissingHeartbeat, Metadata: heartBeatingMetadataWithRetryPolicyInBytes, }, }, err: nil, }, { name: "activity timeout and heart beating enabled without retry policy", input: []invariant.InvariantCheckResult{ { IssueID: 0, InvariantType: TimeoutTypeActivity.String(), Reason: "START_TO_CLOSE", Metadata: activityHeartBeatTimeoutDataInBytes(t), }, }, clientExpects: func(client *publicservicetest.MockClient) { client.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any()).Return(&shared.DescribeTaskListResponse{ Pollers: []*shared.PollerInfo{ { Identity: common.StringPtr("dca24-xy"), }, }, TaskListStatus: &shared.TaskListStatus{ BacklogCountHint: common.Int64Ptr(testTaskListBacklog), }, }, nil) }, expectedResult: []invariant.InvariantRootCauseResult{ { IssueID: 0, RootCause: invariant.RootCauseTypePollersStatus, Metadata: pollersMetadataInBytes, }, { IssueID: 0, RootCause: invariant.RootCauseTypeHeartBeatingEnabledWithoutRetryPolicy, Metadata: heartBeatingMetadataInBytes, }, }, err: nil, }, } ctrl := gomock.NewController(t) mockClient := publicservicetest.NewMockClient(ctrl) inv := NewInvariant(Params{ Client: mockClient, }) for _, tc := range testCases { tc.clientExpects(mockClient) result, err := inv.RootCause(context.Background(), invariant.InvariantRootCauseInput{ Domain: testDomain, Issues: tc.input, }) require.Equal(t, tc.err, err) require.Equal(t, len(tc.expectedResult), len(result)) for i := range result { require.Equal(t, tc.expectedResult[i], result[i]) } } } ================================================ FILE: service/worker/diagnostics/invariant/timeout/timeout_utils.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package timeout import ( "fmt" "sort" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) func reasonForDecisionTaskTimeouts(event *types.HistoryEvent, allEvents []*types.HistoryEvent) (string, DecisionTimeoutMetadata) { eventScheduledID := event.GetDecisionTaskTimedOutEventAttributes().GetScheduledEventID() attr := event.GetDecisionTaskTimedOutEventAttributes() cause := attr.GetCause() var reason string switch cause { case types.DecisionTaskTimedOutCauseTimeout: reason = attr.TimeoutType.String() case types.DecisionTaskTimedOutCauseReset: newRunID := attr.GetNewRunID() reason = fmt.Sprintf("%s - New run ID: %s", attr.Reason, newRunID) } return reason, DecisionTimeoutMetadata{ ConfiguredTimeout: time.Duration(getDecisionTaskConfiguredTimeout(eventScheduledID, allEvents)) * time.Second, } } func getWorkflowExecutionConfiguredTimeout(events []*types.HistoryEvent) int32 { for _, event := range events { if event.ID == 1 { // event 1 is workflow execution started event return event.GetWorkflowExecutionStartedEventAttributes().GetExecutionStartToCloseTimeoutSeconds() } } return 0 } func getWorkflowExecutionTasklist(events []*types.HistoryEvent) *types.TaskList { for _, event := range events { if event.ID == 1 { // event 1 is workflow execution started event return event.GetWorkflowExecutionStartedEventAttributes().TaskList } } return nil } func getActivityTaskMetadata(e *types.HistoryEvent, events []*types.HistoryEvent) (ActivityTimeoutMetadata, error) { eventScheduledID := e.GetActivityTaskTimedOutEventAttributes().GetScheduledEventID() eventStartedID := e.GetActivityTaskTimedOutEventAttributes().StartedEventID timeoutType := e.GetActivityTaskTimedOutEventAttributes().GetTimeoutType() var configuredTimeout int32 var timeElapsed time.Duration for _, event := range events { if event.ID == eventScheduledID { attr := event.GetActivityTaskScheduledEventAttributes() switch timeoutType { case types.TimeoutTypeHeartbeat: configuredTimeout = attr.GetHeartbeatTimeoutSeconds() timeElapsed = getExecutionTime(eventStartedID, e.ID, events) case types.TimeoutTypeScheduleToClose: configuredTimeout = attr.GetScheduleToCloseTimeoutSeconds() timeElapsed = getExecutionTime(eventScheduledID, e.ID, events) case types.TimeoutTypeScheduleToStart: configuredTimeout = attr.GetScheduleToStartTimeoutSeconds() timeElapsed = getExecutionTime(eventScheduledID, e.ID, events) case types.TimeoutTypeStartToClose: configuredTimeout = attr.GetStartToCloseTimeoutSeconds() timeElapsed = getExecutionTime(eventStartedID, e.ID, events) default: return ActivityTimeoutMetadata{}, fmt.Errorf("unknown timeout type") } return ActivityTimeoutMetadata{ TimeoutType: timeoutType.Ptr(), ConfiguredTimeout: time.Duration(configuredTimeout) * time.Second, TimeElapsed: timeElapsed, RetryPolicy: attr.RetryPolicy, HeartBeatTimeout: time.Duration(attr.GetHeartbeatTimeoutSeconds()) * time.Second, Tasklist: attr.TaskList, }, nil } } return ActivityTimeoutMetadata{}, fmt.Errorf("activity scheduled event not found") } func getDecisionTaskConfiguredTimeout(eventScheduledID int64, events []*types.HistoryEvent) int32 { for _, event := range events { if event.ID == eventScheduledID { return event.GetDecisionTaskScheduledEventAttributes().GetStartToCloseTimeoutSeconds() } } return 0 } func getChildWorkflowExecutionConfiguredTimeout(e *types.HistoryEvent, events []*types.HistoryEvent) int32 { wfInitiatedID := e.GetChildWorkflowExecutionTimedOutEventAttributes().GetInitiatedEventID() for _, event := range events { if event.ID == wfInitiatedID { return event.GetStartChildWorkflowExecutionInitiatedEventAttributes().GetExecutionStartToCloseTimeoutSeconds() } } return 0 } func getExecutionTime(startID, timeoutID int64, events []*types.HistoryEvent) time.Duration { sort.SliceStable(events, func(i, j int) bool { return events[i].ID < events[j].ID }) firstEvent := events[startID-1] lastEvent := events[timeoutID-1] return time.Unix(0, common.Int64Default(lastEvent.Timestamp)).Sub(time.Unix(0, common.Int64Default(firstEvent.Timestamp))) } ================================================ FILE: service/worker/diagnostics/invariant/timeout/types.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package timeout import ( "time" "github.com/uber/cadence/common/types" ) type TimeoutType string const ( TimeoutTypeExecution TimeoutType = "The Workflow Execution has timed out" TimeoutTypeActivity TimeoutType = "Activity task has timed out" TimeoutTypeDecision TimeoutType = "Decision task has timed out" TimeoutTypeChildWorkflow TimeoutType = "Child Workflow Execution has timed out" ) func (tt TimeoutType) String() string { return string(tt) } type ExecutionTimeoutMetadata struct { ExecutionTime time.Duration ConfiguredTimeout time.Duration Tasklist *types.TaskList LastOngoingEvent *types.HistoryEvent } type ChildWfTimeoutMetadata struct { ExecutionTime time.Duration ConfiguredTimeout time.Duration Execution *types.WorkflowExecution } type ActivityTimeoutMetadata struct { TimeoutType *types.TimeoutType ConfiguredTimeout time.Duration TimeElapsed time.Duration RetryPolicy *types.RetryPolicy HeartBeatTimeout time.Duration Tasklist *types.TaskList } type DecisionTimeoutMetadata struct { ConfiguredTimeout time.Duration } type PollersMetadata struct { TaskListName string TaskListBacklog int64 } type HeartbeatingMetadata struct { TimeElapsed time.Duration RetryPolicy *types.RetryPolicy } type TimeoutIssuesMetadata struct { ExecutionTimeout *ExecutionTimeoutMetadata ActivityTimeout *ActivityTimeoutMetadata ChildWfTimeout *ChildWfTimeoutMetadata DecisionTimeout *DecisionTimeoutMetadata } type TimeoutRootcauseMetadata struct { PollersMetadata *PollersMetadata HeartBeatingMetadata *HeartbeatingMetadata } ================================================ FILE: service/worker/diagnostics/module.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package diagnostics import ( "context" "github.com/opentracing/opentracing-go" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/activity" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "github.com/uber/cadence/client" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/worker/diagnostics/invariant" ) type DiagnosticsWorkflow interface { Start() error Stop() } type dw struct { svcClient workflowserviceclient.Interface clientBean client.Bean metricsClient metrics.Client messagingClient messaging.Client logger log.Logger tallyScope tally.Scope worker worker.Worker invariants []invariant.Invariant clusterMetadata cluster.Metadata } type Params struct { ServiceClient workflowserviceclient.Interface ClientBean client.Bean MetricsClient metrics.Client MessagingClient messaging.Client Logger log.Logger TallyScope tally.Scope Invariants []invariant.Invariant ClusterMetadata cluster.Metadata } // New creates a new diagnostics workflow. func New(params Params) DiagnosticsWorkflow { return &dw{ svcClient: params.ServiceClient, metricsClient: params.MetricsClient, messagingClient: params.MessagingClient, tallyScope: params.TallyScope, clientBean: params.ClientBean, logger: params.Logger, invariants: params.Invariants, clusterMetadata: params.ClusterMetadata, } } // Start starts the worker func (w *dw) Start() error { workerOpts := worker.Options{ MetricsScope: w.tallyScope, BackgroundActivityContext: context.Background(), Tracer: opentracing.GlobalTracer(), MaxConcurrentActivityTaskPollers: 10, MaxConcurrentDecisionTaskPollers: 10, } newWorker := worker.New(w.svcClient, constants.SystemLocalDomainName, tasklist, workerOpts) newWorker.RegisterWorkflowWithOptions(w.DiagnosticsWorkflow, workflow.RegisterOptions{Name: diagnosticsWorkflow}) newWorker.RegisterWorkflowWithOptions(w.DiagnosticsStarterWorkflow, workflow.RegisterOptions{Name: diagnosticsStarterWorkflow}) newWorker.RegisterActivityWithOptions(w.identifyIssues, activity.RegisterOptions{Name: identifyIssuesActivity}) newWorker.RegisterActivityWithOptions(w.rootCauseIssues, activity.RegisterOptions{Name: rootCauseIssuesActivity}) newWorker.RegisterActivityWithOptions(w.emitUsageLogs, activity.RegisterOptions{Name: emitUsageLogsActivity}) w.worker = newWorker return newWorker.Start() } func (w *dw) Stop() { w.worker.Stop() } ================================================ FILE: service/worker/diagnostics/module_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package diagnostics import ( "testing" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/mock/gomock" "github.com/uber/cadence/client" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/service/worker/diagnostics/invariant" "github.com/uber/cadence/service/worker/diagnostics/invariant/failure" ) func Test__Start(t *testing.T) { dwTest, mockResource := setuptest(t) err := dwTest.Start() require.NoError(t, err) dwTest.Stop() mockResource.Finish(t) } func setuptest(t *testing.T) (DiagnosticsWorkflow, *resource.Test) { ctrl := gomock.NewController(t) mockClientBean := client.NewMockBean(ctrl) mockResource := resource.NewTest(t, ctrl, metrics.Worker) sdkClient := mockResource.GetSDKClient() mockResource.SDKClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&shared.DescribeDomainResponse{}, nil).AnyTimes() mockResource.SDKClient.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&shared.PollForDecisionTaskResponse{}, nil).AnyTimes() mockResource.SDKClient.EXPECT().PollForActivityTask(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&shared.PollForActivityTaskResponse{}, nil).AnyTimes() return New(Params{ ServiceClient: sdkClient, ClientBean: mockClientBean, MetricsClient: nil, TallyScope: tally.TestScope(nil), Invariants: []invariant.Invariant{failure.NewInvariant()}, }), mockResource } ================================================ FILE: service/worker/diagnostics/parent_workflow.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package diagnostics import ( "fmt" "time" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/service/worker/diagnostics/analytics" ) const ( diagnosticsStarterWorkflow = "diagnostics-starter-workflow" emitUsageLogsActivity = "emitUsageLogs" queryDiagnosticsReport = "query-diagnostics-report" issueTypeTimeouts = "Timeout" issueTypeFailures = "Failure" issueTypeRetry = "Retry" ) type DiagnosticsStarterWorkflowInput struct { Domain string Identity string WorkflowID string RunID string } type DiagnosticsStarterWorkflowResult struct { DiagnosticsResult *DiagnosticsWorkflowResult DiagnosticsCompleted bool } func (w *dw) DiagnosticsStarterWorkflow(ctx workflow.Context, params DiagnosticsStarterWorkflowInput) (*DiagnosticsStarterWorkflowResult, error) { var diagWfResult DiagnosticsWorkflowResult workflowResult := DiagnosticsStarterWorkflowResult{ DiagnosticsResult: &diagWfResult, } err := workflow.SetQueryHandler(ctx, queryDiagnosticsReport, func() (DiagnosticsStarterWorkflowResult, error) { return workflowResult, nil }) if err != nil { return nil, err } future := workflow.ExecuteChildWorkflow(ctx, w.DiagnosticsWorkflow, DiagnosticsWorkflowInput{ Domain: params.Domain, WorkflowID: params.WorkflowID, RunID: params.RunID, }) var childWfExec workflow.Execution var childWfStart, childWfEnd time.Time if err = future.GetChildWorkflowExecution().Get(ctx, &childWfExec); err != nil { return nil, fmt.Errorf("Workflow Diagnostics start failed: %w", err) } childWfStart = workflow.Now(ctx) err = future.Get(ctx, &diagWfResult) if err != nil { return nil, fmt.Errorf("Workflow Diagnostics failed: %w", err) } workflowResult.DiagnosticsCompleted = true childWfEnd = workflow.Now(ctx) info := workflow.GetInfo(ctx) activityOptions := workflow.ActivityOptions{ ScheduleToCloseTimeout: time.Second * 10, ScheduleToStartTimeout: time.Second * 5, StartToCloseTimeout: time.Second * 5, } activityCtx := workflow.WithActivityOptions(ctx, activityOptions) err = workflow.ExecuteActivity(activityCtx, emitUsageLogsActivity, analytics.WfDiagnosticsUsageData{ Domain: params.Domain, WorkflowID: params.WorkflowID, RunID: params.RunID, Identity: params.Identity, IssueType: getIssueType(diagWfResult), Environment: w.clusterMetadata.GetCurrentClusterName(), DiagnosticsWorkflowID: childWfExec.ID, DiagnosticsRunID: childWfExec.RunID, DiagnosticsStartTime: childWfStart, DiagnosticsEndTime: childWfEnd, }).Get(ctx, nil) if err != nil { w.logger.Error("wf-diagnostics usage logs emission failed", tag.Error(err), tag.WorkflowID(info.WorkflowExecution.ID), tag.WorkflowRunID(info.WorkflowExecution.RunID)) } return &workflowResult, nil } func getIssueType(result DiagnosticsWorkflowResult) string { var issueType string if result.Timeouts != nil { issueType = fmt.Sprintf("%s-%s", issueType, issueTypeTimeouts) } if result.Failures != nil { issueType = fmt.Sprintf("%s-%s", issueType, issueTypeFailures) } if result.Retries != nil { issueType = fmt.Sprintf("%s-%s", issueType, issueTypeRetry) } return issueType } ================================================ FILE: service/worker/diagnostics/workflow.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package diagnostics import ( "encoding/json" "fmt" "time" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/diagnostics/invariant" "github.com/uber/cadence/service/worker/diagnostics/invariant/failure" "github.com/uber/cadence/service/worker/diagnostics/invariant/retry" "github.com/uber/cadence/service/worker/diagnostics/invariant/timeout" ) const ( diagnosticsWorkflow = "diagnostics-workflow" tasklist = "diagnostics-wf-tasklist" identifyIssuesActivity = "identifyIssues" rootCauseIssuesActivity = "rootCauseIssues" ) type DiagnosticsWorkflowInput struct { Domain string WorkflowID string RunID string } type DiagnosticsWorkflowResult struct { Timeouts *timeoutDiagnostics Failures *failureDiagnostics Retries *retryDiagnostics } type timeoutDiagnostics struct { Issues []*timeoutIssuesResult RootCause []*timeoutRootCauseResult Runbook string } type timeoutIssuesResult struct { IssueID int InvariantType string Reason string Metadata *timeout.TimeoutIssuesMetadata } type timeoutRootCauseResult struct { IssueID int RootCauseType string Metadata *timeout.TimeoutRootcauseMetadata } type failureDiagnostics struct { Issues []*failureIssuesResult RootCause []*failureRootCauseResult Runbook string } type failureIssuesResult struct { IssueID int InvariantType string Reason string Metadata *failure.FailureIssuesMetadata } type failureRootCauseResult struct { IssueID int RootCauseType string Metadata *failure.FailureRootcauseMetadata } type retryDiagnostics struct { Issues []*retryIssuesResult Runbook string } type retryIssuesResult struct { IssueID int InvariantType string Reason string Metadata retry.RetryMetadata } func (w *dw) DiagnosticsWorkflow(ctx workflow.Context, params DiagnosticsWorkflowInput) (*DiagnosticsWorkflowResult, error) { scope := w.metricsClient.Scope(metrics.DiagnosticsWorkflowScope, metrics.DomainTag(params.Domain)) scope.IncCounter(metrics.DiagnosticsWorkflowStartedCount) sw := scope.StartTimer(metrics.DiagnosticsWorkflowExecutionLatency) defer sw.Stop() var timeoutsResult *timeoutDiagnostics var failureResult *failureDiagnostics var retryResult *retryDiagnostics var checkResult []invariant.InvariantCheckResult var rootCauseResult []invariant.InvariantRootCauseResult activityOptions := workflow.ActivityOptions{ ScheduleToCloseTimeout: time.Second * 10, ScheduleToStartTimeout: time.Second * 5, StartToCloseTimeout: time.Second * 5, } activityCtx := workflow.WithActivityOptions(ctx, activityOptions) err := workflow.ExecuteActivity(activityCtx, identifyIssuesActivity, identifyIssuesParams{ Execution: &types.WorkflowExecution{ WorkflowID: params.WorkflowID, RunID: params.RunID, }, Domain: params.Domain, }).Get(ctx, &checkResult) if err != nil { return nil, fmt.Errorf("IdentifyIssues: %w", err) } err = workflow.ExecuteActivity(activityCtx, rootCauseIssuesActivity, rootCauseIssuesParams{ Domain: params.Domain, Issues: checkResult, }).Get(ctx, &rootCauseResult) if err != nil { return nil, fmt.Errorf("RootCauseIssues: %w", err) } timeoutIssues, err := retrieveTimeoutIssues(checkResult) if err != nil { return nil, fmt.Errorf("RetrieveTimeoutIssues: %w", err) } if len(timeoutIssues) > 0 { timeoutRootCause, err := retrieveTimeoutRootCause(rootCauseResult) if err != nil { return nil, fmt.Errorf("RetrieveTimeoutRootCause: %w", err) } timeoutsResult = &timeoutDiagnostics{ Issues: timeoutIssues, RootCause: timeoutRootCause, Runbook: linkToTimeoutsRunbook, } } failureIssues, err := retrieveFailureIssues(checkResult) if err != nil { return nil, fmt.Errorf("RetrieveFailureIssues: %w", err) } if len(failureIssues) > 0 { failureRootCause, err := retrieveFailureRootCause(rootCauseResult) if err != nil { return nil, fmt.Errorf("RetrieveFailureRootCause: %w", err) } failureResult = &failureDiagnostics{ Issues: failureIssues, RootCause: failureRootCause, Runbook: linkToFailuresRunbook, } } retryIssues, err := retrieveRetryIssues(checkResult) if err != nil { return nil, fmt.Errorf("RetrieveRetryIssues: %w", err) } if len(retryIssues) > 0 { retryResult = &retryDiagnostics{ Issues: retryIssues, Runbook: linkToRetriesRunbook, } } scope.IncCounter(metrics.DiagnosticsWorkflowSuccess) return &DiagnosticsWorkflowResult{ Timeouts: timeoutsResult, Failures: failureResult, Retries: retryResult, }, nil } func retrieveTimeoutIssues(issues []invariant.InvariantCheckResult) ([]*timeoutIssuesResult, error) { result := make([]*timeoutIssuesResult, 0) for _, issue := range issues { switch issue.InvariantType { case timeout.TimeoutTypeExecution.String(): var metadata timeout.ExecutionTimeoutMetadata err := json.Unmarshal(issue.Metadata, &metadata) if err != nil { return nil, err } result = append(result, &timeoutIssuesResult{ IssueID: issue.IssueID, InvariantType: issue.InvariantType, Reason: issue.Reason, Metadata: &timeout.TimeoutIssuesMetadata{ ExecutionTimeout: &metadata, }, }) case timeout.TimeoutTypeActivity.String(): var metadata timeout.ActivityTimeoutMetadata err := json.Unmarshal(issue.Metadata, &metadata) if err != nil { return nil, err } result = append(result, &timeoutIssuesResult{ IssueID: issue.IssueID, InvariantType: issue.InvariantType, Reason: issue.Reason, Metadata: &timeout.TimeoutIssuesMetadata{ ActivityTimeout: &metadata, }, }) case timeout.TimeoutTypeChildWorkflow.String(): var metadata timeout.ChildWfTimeoutMetadata err := json.Unmarshal(issue.Metadata, &metadata) if err != nil { return nil, err } result = append(result, &timeoutIssuesResult{ IssueID: issue.IssueID, InvariantType: issue.InvariantType, Reason: issue.Reason, Metadata: &timeout.TimeoutIssuesMetadata{ ChildWfTimeout: &metadata, }, }) case timeout.TimeoutTypeDecision.String(): var metadata timeout.DecisionTimeoutMetadata err := json.Unmarshal(issue.Metadata, &metadata) if err != nil { return nil, err } result = append(result, &timeoutIssuesResult{ IssueID: issue.IssueID, InvariantType: issue.InvariantType, Reason: issue.Reason, Metadata: &timeout.TimeoutIssuesMetadata{ DecisionTimeout: &metadata, }, }) } } return result, nil } func retrieveTimeoutRootCause(rootCause []invariant.InvariantRootCauseResult) ([]*timeoutRootCauseResult, error) { result := make([]*timeoutRootCauseResult, 0) for _, rc := range rootCause { if rootCausePollersRelated(rc.RootCause) { var metadata timeout.PollersMetadata err := json.Unmarshal(rc.Metadata, &metadata) if err != nil { return nil, err } result = append(result, &timeoutRootCauseResult{ IssueID: rc.IssueID, RootCauseType: rc.RootCause.String(), Metadata: &timeout.TimeoutRootcauseMetadata{ PollersMetadata: &metadata, }, }) } else if rootCauseHeartBeatRelated(rc.RootCause) { var metadata timeout.HeartbeatingMetadata err := json.Unmarshal(rc.Metadata, &metadata) if err != nil { return nil, err } result = append(result, &timeoutRootCauseResult{ IssueID: rc.IssueID, RootCauseType: rc.RootCause.String(), Metadata: &timeout.TimeoutRootcauseMetadata{ HeartBeatingMetadata: &metadata, }, }) } } return result, nil } func retrieveFailureIssues(issues []invariant.InvariantCheckResult) ([]*failureIssuesResult, error) { result := make([]*failureIssuesResult, 0) for _, issue := range issues { if issue.InvariantType == failure.ActivityFailed.String() || issue.InvariantType == failure.WorkflowFailed.String() || issue.InvariantType == failure.DecisionCausedFailure.String() { var data failure.FailureIssuesMetadata err := json.Unmarshal(issue.Metadata, &data) if err != nil { return nil, err } result = append(result, &failureIssuesResult{ IssueID: issue.IssueID, InvariantType: issue.InvariantType, Reason: issue.Reason, Metadata: &data, }) } } return result, nil } func retrieveFailureRootCause(rootCause []invariant.InvariantRootCauseResult) ([]*failureRootCauseResult, error) { result := make([]*failureRootCauseResult, 0) for _, rc := range rootCause { if rc.RootCause == invariant.RootCauseTypeServiceSideIssue || rc.RootCause == invariant.RootCauseTypeServiceSidePanic || rc.RootCause == invariant.RootCauseTypeServiceSideCustomError { result = append(result, &failureRootCauseResult{ IssueID: rc.IssueID, RootCauseType: rc.RootCause.String(), }) } if rc.RootCause == invariant.RootCauseTypeBlobSizeLimit { var metadata failure.FailureRootcauseMetadata err := json.Unmarshal(rc.Metadata, &metadata) if err != nil { return nil, err } result = append(result, &failureRootCauseResult{ IssueID: rc.IssueID, RootCauseType: rc.RootCause.String(), Metadata: &failure.FailureRootcauseMetadata{ BlobSizeMetadata: metadata.BlobSizeMetadata, }, }) } } return result, nil } func retrieveRetryIssues(issues []invariant.InvariantCheckResult) ([]*retryIssuesResult, error) { result := make([]*retryIssuesResult, 0) for _, issue := range issues { if issueRetryRelated(issue) { var data retry.RetryMetadata err := json.Unmarshal(issue.Metadata, &data) if err != nil { return nil, err } result = append(result, &retryIssuesResult{ IssueID: issue.IssueID, InvariantType: issue.InvariantType, Reason: issue.Reason, Metadata: data, }) } } return result, nil } func rootCauseHeartBeatRelated(rootCause invariant.RootCause) bool { for _, rc := range []invariant.RootCause{invariant.RootCauseTypeNoHeartBeatTimeoutNoRetryPolicy, invariant.RootCauseTypeHeartBeatingNotEnabledWithRetryPolicy, invariant.RootCauseTypeHeartBeatingEnabledWithoutRetryPolicy, invariant.RootCauseTypeHeartBeatingEnabledMissingHeartbeat} { if rc == rootCause { return true } } return false } func rootCausePollersRelated(rootCause invariant.RootCause) bool { for _, rc := range []invariant.RootCause{invariant.RootCauseTypePollersStatus, invariant.RootCauseTypeMissingPollers} { if rc == rootCause { return true } } return false } func issueRetryRelated(issue invariant.InvariantCheckResult) bool { for _, i := range []string{retry.WorkflowRetryIssue.String(), retry.ActivityRetryIssue.String(), retry.ActivityHeartbeatIssue.String()} { if issue.InvariantType == i { return true } } return false } ================================================ FILE: service/worker/diagnostics/workflow_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package diagnostics import ( "encoding/json" "errors" "fmt" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/activity" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/diagnostics/invariant" "github.com/uber/cadence/service/worker/diagnostics/invariant/failure" "github.com/uber/cadence/service/worker/diagnostics/invariant/retry" "github.com/uber/cadence/service/worker/diagnostics/invariant/timeout" ) type diagnosticsWorkflowTestSuite struct { suite.Suite testsuite.WorkflowTestSuite workflowEnv *testsuite.TestWorkflowEnvironment dw *dw } func TestDiagnosticsWorkflowTestSuite(t *testing.T) { suite.Run(t, new(diagnosticsWorkflowTestSuite)) } func (s *diagnosticsWorkflowTestSuite) SetupTest() { s.workflowEnv = s.NewTestWorkflowEnvironment() controller := gomock.NewController(s.T()) mockResource := resource.NewTest(s.T(), controller, metrics.Worker) publicClient := mockResource.GetSDKClient() s.dw = &dw{ logger: mockResource.GetLogger(), svcClient: publicClient, clientBean: mockResource.ClientBean, metricsClient: mockResource.GetMetricsClient(), invariants: []invariant.Invariant{timeout.NewInvariant(timeout.Params{Client: publicClient}), failure.NewInvariant(), retry.NewInvariant()}, } s.T().Cleanup(func() { mockResource.Finish(s.T()) }) s.workflowEnv.RegisterWorkflowWithOptions(s.dw.DiagnosticsStarterWorkflow, workflow.RegisterOptions{Name: diagnosticsStarterWorkflow}) s.workflowEnv.RegisterWorkflowWithOptions(s.dw.DiagnosticsWorkflow, workflow.RegisterOptions{Name: diagnosticsWorkflow}) s.workflowEnv.RegisterActivityWithOptions(s.dw.identifyIssues, activity.RegisterOptions{Name: identifyIssuesActivity}) s.workflowEnv.RegisterActivityWithOptions(s.dw.rootCauseIssues, activity.RegisterOptions{Name: rootCauseIssuesActivity}) s.workflowEnv.RegisterActivityWithOptions(s.dw.emitUsageLogs, activity.RegisterOptions{Name: emitUsageLogsActivity}) } func (s *diagnosticsWorkflowTestSuite) TearDownTest() { s.workflowEnv.AssertExpectations(s.T()) } func (s *diagnosticsWorkflowTestSuite) TestWorkflow() { params := &DiagnosticsStarterWorkflowInput{ Domain: "test", WorkflowID: "123", RunID: "abc", } workflowTimeoutData := timeout.ExecutionTimeoutMetadata{ ExecutionTime: 110 * time.Second, ConfiguredTimeout: 110 * time.Second, LastOngoingEvent: &types.HistoryEvent{ ID: 1, Timestamp: common.Int64Ptr(testTimeStamp), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(workflowTimeoutSecond), }, }, } workflowTimeoutDataInBytes, err := json.Marshal(workflowTimeoutData) s.NoError(err) actMetadata := failure.FailureIssuesMetadata{ Identity: "localhost", ActivityScheduledID: 1, ActivityStartedID: 2, } actMetadataInBytes, err := json.Marshal(actMetadata) s.NoError(err) issues := []invariant.InvariantCheckResult{ { IssueID: 1, InvariantType: timeout.TimeoutTypeExecution.String(), Reason: "START_TO_CLOSE", Metadata: workflowTimeoutDataInBytes, }, { IssueID: 1, InvariantType: failure.ActivityFailed.String(), Reason: failure.ActivityOutputBlobSizeLimit.String(), Metadata: actMetadataInBytes, }, } timeoutIssues := []*timeoutIssuesResult{ { IssueID: 1, InvariantType: timeout.TimeoutTypeExecution.String(), Reason: "START_TO_CLOSE", Metadata: &timeout.TimeoutIssuesMetadata{ ExecutionTimeout: &workflowTimeoutData, }, }, } taskListBacklog := int64(10) pollersMetadataInBytes, err := json.Marshal(timeout.PollersMetadata{TaskListName: "test", TaskListBacklog: taskListBacklog}) s.NoError(err) blobSizeMetadataInBytes, err := json.Marshal(failure.FailureRootcauseMetadata{ BlobSizeMetadata: &failure.BlobSizeMetadata{ BlobSizeWarnLimit: 5, BlobSizeErrorLimit: 10, }, }) s.NoError(err) rootCause := []invariant.InvariantRootCauseResult{ { IssueID: 1, RootCause: invariant.RootCauseTypePollersStatus, Metadata: pollersMetadataInBytes, }, { IssueID: 2, RootCause: invariant.RootCauseTypeBlobSizeLimit, Metadata: blobSizeMetadataInBytes, }, } failureRootCause := []*failureRootCauseResult{ { IssueID: 2, RootCauseType: invariant.RootCauseTypeBlobSizeLimit.String(), Metadata: &failure.FailureRootcauseMetadata{ BlobSizeMetadata: &failure.BlobSizeMetadata{ BlobSizeWarnLimit: 5, BlobSizeErrorLimit: 10, }, }, }, } timeoutRootCause := []*timeoutRootCauseResult{ { IssueID: 1, RootCauseType: invariant.RootCauseTypePollersStatus.String(), Metadata: &timeout.TimeoutRootcauseMetadata{ PollersMetadata: &timeout.PollersMetadata{TaskListName: "test", TaskListBacklog: taskListBacklog}, }, }} s.workflowEnv.OnActivity(identifyIssuesActivity, mock.Anything, mock.Anything).Return(issues, nil) s.workflowEnv.OnActivity(rootCauseIssuesActivity, mock.Anything, mock.Anything).Return(rootCause, nil) s.workflowEnv.OnActivity(emitUsageLogsActivity, mock.Anything, mock.Anything).Return(nil) s.workflowEnv.ExecuteWorkflow(diagnosticsStarterWorkflow, params) s.True(s.workflowEnv.IsWorkflowCompleted()) var result DiagnosticsStarterWorkflowResult s.NoError(s.workflowEnv.GetWorkflowResult(&result)) s.ElementsMatch(timeoutIssues, result.DiagnosticsResult.Timeouts.Issues) s.ElementsMatch(timeoutRootCause, result.DiagnosticsResult.Timeouts.RootCause) s.ElementsMatch(failureRootCause, result.DiagnosticsResult.Failures.RootCause) s.True(result.DiagnosticsCompleted) queriedResult := s.queryDiagnostics() s.ElementsMatch(queriedResult.DiagnosticsResult.Timeouts.Issues, result.DiagnosticsResult.Timeouts.Issues) s.ElementsMatch(queriedResult.DiagnosticsResult.Timeouts.RootCause, result.DiagnosticsResult.Timeouts.RootCause) s.True(queriedResult.DiagnosticsCompleted) } func (s *diagnosticsWorkflowTestSuite) TestWorkflow_Error() { params := &DiagnosticsWorkflowInput{ Domain: "test", WorkflowID: "123", RunID: "abc", } mockErr := errors.New("mockErr") errExpected := fmt.Errorf("IdentifyIssues: %w", mockErr) s.workflowEnv.OnActivity(identifyIssuesActivity, mock.Anything, mock.Anything).Return(nil, mockErr) s.workflowEnv.ExecuteWorkflow(diagnosticsWorkflow, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.Error(s.workflowEnv.GetWorkflowError()) s.EqualError(s.workflowEnv.GetWorkflowError(), errExpected.Error()) } func (s *diagnosticsWorkflowTestSuite) TestWorkflow_NoErrorIfEmitLogsActivityFails() { params := &DiagnosticsWorkflowInput{ Domain: "test", WorkflowID: "123", RunID: "abc", } mockErr := errors.New("mockErr") s.workflowEnv.OnActivity(identifyIssuesActivity, mock.Anything, mock.Anything).Return(nil, nil) s.workflowEnv.OnActivity(rootCauseIssuesActivity, mock.Anything, mock.Anything).Return(nil, nil) s.workflowEnv.OnActivity(emitUsageLogsActivity, mock.Anything, mock.Anything).Return(mockErr) s.workflowEnv.ExecuteWorkflow(diagnosticsStarterWorkflow, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.NoError(s.workflowEnv.GetWorkflowError()) } func (s *diagnosticsWorkflowTestSuite) queryDiagnostics() DiagnosticsStarterWorkflowResult { queryFuture, err := s.workflowEnv.QueryWorkflow(queryDiagnosticsReport) s.NoError(err) var result DiagnosticsStarterWorkflowResult err = queryFuture.Get(&result) s.NoError(err) return result } func (s *diagnosticsWorkflowTestSuite) Test__retrieveTimeoutIssues() { workflowTimeoutData := timeout.ExecutionTimeoutMetadata{ ExecutionTime: 110 * time.Second, ConfiguredTimeout: 110 * time.Second, LastOngoingEvent: &types.HistoryEvent{ ID: 1, Timestamp: common.Int64Ptr(testTimeStamp), WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(workflowTimeoutSecond), }, }, } workflowTimeoutDataInBytes, err := json.Marshal(workflowTimeoutData) s.NoError(err) childWorkflowTimeoutData := timeout.ChildWfTimeoutMetadata{ ExecutionTime: 110 * time.Second, ConfiguredTimeout: 110 * time.Second, } childWorkflowTimeoutDataInBytes, err := json.Marshal(childWorkflowTimeoutData) s.NoError(err) activityTimeoutData := timeout.ActivityTimeoutMetadata{ TimeoutType: types.TimeoutTypeStartToClose.Ptr(), ConfiguredTimeout: 5 * time.Second, TimeElapsed: 5 * time.Second, HeartBeatTimeout: 0, } activityTimeoutDataInBytes, err := json.Marshal(activityTimeoutData) s.NoError(err) descTimeoutData := timeout.DecisionTimeoutMetadata{ ConfiguredTimeout: 5 * time.Second, } descTimeoutDataInBytes, err := json.Marshal(activityTimeoutData) s.NoError(err) issues := []invariant.InvariantCheckResult{ { IssueID: 1, InvariantType: timeout.TimeoutTypeExecution.String(), Reason: "START_TO_CLOSE", Metadata: workflowTimeoutDataInBytes, }, { IssueID: 2, InvariantType: timeout.TimeoutTypeActivity.String(), Reason: "START_TO_CLOSE", Metadata: activityTimeoutDataInBytes, }, { IssueID: 3, InvariantType: timeout.TimeoutTypeDecision.String(), Reason: "START_TO_CLOSE", Metadata: descTimeoutDataInBytes, }, { IssueID: 4, InvariantType: timeout.TimeoutTypeChildWorkflow.String(), Reason: "START_TO_CLOSE", Metadata: childWorkflowTimeoutDataInBytes, }, } timeoutIssues := []*timeoutIssuesResult{ { IssueID: 1, InvariantType: timeout.TimeoutTypeExecution.String(), Reason: "START_TO_CLOSE", Metadata: &timeout.TimeoutIssuesMetadata{ ExecutionTimeout: &workflowTimeoutData, }, }, { IssueID: 2, InvariantType: timeout.TimeoutTypeActivity.String(), Reason: "START_TO_CLOSE", Metadata: &timeout.TimeoutIssuesMetadata{ ActivityTimeout: &activityTimeoutData, }, }, { IssueID: 3, InvariantType: timeout.TimeoutTypeDecision.String(), Reason: "START_TO_CLOSE", Metadata: &timeout.TimeoutIssuesMetadata{ DecisionTimeout: &descTimeoutData, }, }, { IssueID: 4, InvariantType: timeout.TimeoutTypeChildWorkflow.String(), Reason: "START_TO_CLOSE", Metadata: &timeout.TimeoutIssuesMetadata{ ChildWfTimeout: &childWorkflowTimeoutData, }, }, } result, err := retrieveTimeoutIssues(issues) s.NoError(err) s.ElementsMatch(timeoutIssues, result) } func (s *diagnosticsWorkflowTestSuite) Test__retrieveTimeoutRootCause() { taskListBacklog := int64(10) pollersMetadataInBytes, err := json.Marshal(timeout.PollersMetadata{TaskListBacklog: taskListBacklog}) s.NoError(err) heartBeatingMetadataInBytes, err := json.Marshal(timeout.HeartbeatingMetadata{TimeElapsed: 5 * time.Second}) s.NoError(err) rootCause := []invariant.InvariantRootCauseResult{ { IssueID: 1, RootCause: invariant.RootCauseTypePollersStatus, Metadata: pollersMetadataInBytes, }, { IssueID: 2, RootCause: invariant.RootCauseTypeNoHeartBeatTimeoutNoRetryPolicy, Metadata: heartBeatingMetadataInBytes, }, } timeoutRootCause := []*timeoutRootCauseResult{ { IssueID: 1, RootCauseType: invariant.RootCauseTypePollersStatus.String(), Metadata: &timeout.TimeoutRootcauseMetadata{ PollersMetadata: &timeout.PollersMetadata{TaskListBacklog: taskListBacklog}, }, }, { IssueID: 2, RootCauseType: invariant.RootCauseTypeNoHeartBeatTimeoutNoRetryPolicy.String(), Metadata: &timeout.TimeoutRootcauseMetadata{ HeartBeatingMetadata: &timeout.HeartbeatingMetadata{TimeElapsed: 5 * time.Second}, }, }, } result, err := retrieveTimeoutRootCause(rootCause) s.NoError(err) s.ElementsMatch(timeoutRootCause, result) } func (s *diagnosticsWorkflowTestSuite) Test__retrieveFailureIssues() { actMetadata := failure.FailureIssuesMetadata{ Identity: "localhost", ActivityScheduledID: 1, ActivityStartedID: 2, } actMetadataInBytes, err := json.Marshal(actMetadata) s.NoError(err) issues := []invariant.InvariantCheckResult{ { IssueID: 1, InvariantType: failure.ActivityFailed.String(), Reason: failure.CustomError.String(), Metadata: actMetadataInBytes, }, } failureIssues := []*failureIssuesResult{ { IssueID: 1, InvariantType: failure.ActivityFailed.String(), Reason: failure.CustomError.String(), Metadata: &actMetadata, }, } result, err := retrieveFailureIssues(issues) s.NoError(err) s.ElementsMatch(failureIssues, result) } func (s *diagnosticsWorkflowTestSuite) Test__retrieveFailureRootCause() { blobSizeMetadataInBytes, err := json.Marshal(failure.FailureRootcauseMetadata{ BlobSizeMetadata: &failure.BlobSizeMetadata{ BlobSizeWarnLimit: 5, BlobSizeErrorLimit: 10, }, }) s.NoError(err) rootCause := []invariant.InvariantRootCauseResult{ { IssueID: 1, RootCause: invariant.RootCauseTypeServiceSideIssue, }, { IssueID: 2, RootCause: invariant.RootCauseTypeBlobSizeLimit, Metadata: blobSizeMetadataInBytes, }, } failureRootCause := []*failureRootCauseResult{ { IssueID: 1, RootCauseType: invariant.RootCauseTypeServiceSideIssue.String(), }, { IssueID: 2, RootCauseType: invariant.RootCauseTypeBlobSizeLimit.String(), Metadata: &failure.FailureRootcauseMetadata{ BlobSizeMetadata: &failure.BlobSizeMetadata{ BlobSizeWarnLimit: 5, BlobSizeErrorLimit: 10, }, }, }, } result, err := retrieveFailureRootCause(rootCause) s.NoError(err) s.ElementsMatch(failureRootCause, result) } func (s *diagnosticsWorkflowTestSuite) Test__retrieveRetryIssues() { retryMetadata := retry.RetryMetadata{ RetryPolicy: &types.RetryPolicy{ InitialIntervalInSeconds: 1, MaximumAttempts: 1, }, } retryMetadataInBytes, err := json.Marshal(retryMetadata) s.NoError(err) issues := []invariant.InvariantCheckResult{ { IssueID: 1, InvariantType: retry.ActivityRetryIssue.String(), Reason: retry.RetryPolicyValidationMaxAttempts.String(), Metadata: retryMetadataInBytes, }, { IssueID: 2, InvariantType: retry.WorkflowRetryIssue.String(), Reason: retry.RetryPolicyValidationMaxAttempts.String(), Metadata: retryMetadataInBytes, }, } retryIssues := []*retryIssuesResult{ { IssueID: 1, InvariantType: retry.ActivityRetryIssue.String(), Reason: retry.RetryPolicyValidationMaxAttempts.String(), Metadata: retryMetadata, }, { IssueID: 2, InvariantType: retry.WorkflowRetryIssue.String(), Reason: retry.RetryPolicyValidationMaxAttempts.String(), Metadata: retryMetadata, }, } result, err := retrieveRetryIssues(issues) s.NoError(err) s.ElementsMatch(retryIssues, result) } ================================================ FILE: service/worker/domaindeprecation/activities.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package domaindeprecation import ( "context" "errors" "fmt" "go.uber.org/cadence" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/types" ) // DisableArchivalActivity disables archival for the domain func (w *domainDeprecator) DisableArchivalActivity(ctx context.Context, params DomainDeprecationParams) error { client := w.clientBean.GetFrontendClient() disabled := types.ArchivalStatusDisabled describeRequest := &types.DescribeDomainRequest{ Name: ¶ms.DomainName, } domainResp, err := client.DescribeDomain(ctx, describeRequest) if err != nil { var entityNotExistsError *types.EntityNotExistsError if errors.As(err, &entityNotExistsError) { return cadence.NewCustomError(ErrDomainDoesNotExistNonRetryable) } return fmt.Errorf("failed to describe domain: %v", err) } // Check if archival is already disabled if *domainResp.Configuration.VisibilityArchivalStatus == disabled && *domainResp.Configuration.HistoryArchivalStatus == disabled { w.logger.Info("Archival is already disabled for domain", tag.WorkflowDomainName(params.DomainName)) return nil } updateRequest := &types.UpdateDomainRequest{ Name: params.DomainName, HistoryArchivalStatus: &disabled, VisibilityArchivalStatus: &disabled, SecurityToken: params.SecurityToken, } updateResp, err := client.UpdateDomain(ctx, updateRequest) if err != nil { return fmt.Errorf("failed to update domain: %v", err) } if *updateResp.Configuration.VisibilityArchivalStatus != disabled || *updateResp.Configuration.HistoryArchivalStatus != disabled { return fmt.Errorf("failed to disable archival for domain %s", params.DomainName) } w.logger.Info("Disabled archival for domain", tag.WorkflowDomainName(params.DomainName)) return nil } // DeprecateDomainActivity deprecates the domain func (w *domainDeprecator) DeprecateDomainActivity(ctx context.Context, params DomainDeprecationParams) error { client := w.clientBean.GetFrontendClient() err := client.DeprecateDomain(ctx, &types.DeprecateDomainRequest{ Name: params.DomainName, SecurityToken: params.SecurityToken, }) if err != nil { return fmt.Errorf("failed to deprecate domain: %v", err) } return nil } // CheckOpenWorkflowsActivity checks if there are any open workflows in the domain func (w *domainDeprecator) CheckOpenWorkflowsActivity(ctx context.Context, params DomainDeprecationParams) (bool, error) { client := w.clientBean.GetFrontendClient() countRequest := &types.CountWorkflowExecutionsRequest{ Domain: params.DomainName, Query: "CloseTime = missing", } countResp, err := client.CountWorkflowExecutions(ctx, countRequest) if err != nil { return false, fmt.Errorf("failed to count open workflows: %v", err) } hasOpenWorkflows := countResp.Count > 0 if hasOpenWorkflows { w.logger.Info("Found open workflows in domain", tag.WorkflowDomainName(params.DomainName), tag.Number(countResp.Count)) } else { w.logger.Info("No open workflows found in domain", tag.WorkflowDomainName(params.DomainName)) } return hasOpenWorkflows, nil } ================================================ FILE: service/worker/domaindeprecation/activities_test.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package domaindeprecation import ( "context" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/client" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/types" ) func TestDisableArchivalActivity(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockClient := frontend.NewMockClient(ctrl) mockClientBean := client.NewMockBean(ctrl) mockClientBean.EXPECT().GetFrontendClient().Return(mockClient).AnyTimes() deprecator := &domainDeprecator{ cfg: Config{ AdminOperationToken: dynamicproperties.GetStringPropertyFn(""), }, clientBean: mockClientBean, logger: testlogger.New(t), } testDomain := "test-domain" securityToken := "token" disabled := types.ArchivalStatusDisabled enabled := types.ArchivalStatusEnabled tests := []struct { name string setupMocks func() expectedError error }{ { name: "Success - Disable archival", setupMocks: func() { mockClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return( &types.DescribeDomainResponse{ Configuration: &types.DomainConfiguration{ VisibilityArchivalStatus: &enabled, HistoryArchivalStatus: &enabled, }, }, nil) mockClient.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return( &types.UpdateDomainResponse{ Configuration: &types.DomainConfiguration{ VisibilityArchivalStatus: &disabled, HistoryArchivalStatus: &disabled, }, }, nil) }, expectedError: nil, }, { name: "Success - Archival already disabled", setupMocks: func() { mockClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return( &types.DescribeDomainResponse{ Configuration: &types.DomainConfiguration{ VisibilityArchivalStatus: &disabled, HistoryArchivalStatus: &disabled, }, }, nil) }, expectedError: nil, }, { name: "Error - Describe domain fails", setupMocks: func() { mockClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return( nil, assert.AnError) }, expectedError: assert.AnError, }, { name: "Error - Update domain fails", setupMocks: func() { mockClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return( &types.DescribeDomainResponse{ Configuration: &types.DomainConfiguration{ VisibilityArchivalStatus: &enabled, HistoryArchivalStatus: &enabled, }, }, nil) mockClient.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return( nil, assert.AnError) }, expectedError: assert.AnError, }, { name: "Error - Domain does not exist", setupMocks: func() { mockClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return( nil, types.EntityNotExistsError{}, ) }, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.setupMocks() err := deprecator.DisableArchivalActivity(context.Background(), DomainDeprecationParams{ DomainName: testDomain, SecurityToken: securityToken, }) if tt.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestDeprecateDomainActivity(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockClient := frontend.NewMockClient(ctrl) mockClientBean := client.NewMockBean(ctrl) mockClientBean.EXPECT().GetFrontendClient().Return(mockClient).AnyTimes() deprecator := &domainDeprecator{ cfg: Config{ AdminOperationToken: dynamicproperties.GetStringPropertyFn(""), }, clientBean: mockClientBean, logger: testlogger.New(t), } testDomain := "test-domain" securityToken := "token" tests := []struct { name string setupMocks func() expectedError error }{ { name: "Success", setupMocks: func() { mockClient.EXPECT().DeprecateDomain(gomock.Any(), gomock.Any()).Return(nil) }, expectedError: nil, }, { name: "Error", setupMocks: func() { mockClient.EXPECT().DeprecateDomain(gomock.Any(), gomock.Any()).Return(assert.AnError) }, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.setupMocks() err := deprecator.DeprecateDomainActivity(context.Background(), DomainDeprecationParams{ DomainName: testDomain, SecurityToken: securityToken, }) if tt.expectedError != nil { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestCheckOpenWorkflowsActivity(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockClient := frontend.NewMockClient(ctrl) mockClientBean := client.NewMockBean(ctrl) mockClientBean.EXPECT().GetFrontendClient().Return(mockClient).AnyTimes() deprecator := &domainDeprecator{ cfg: Config{ AdminOperationToken: dynamicproperties.GetStringPropertyFn(""), }, clientBean: mockClientBean, logger: testlogger.New(t), } testDomain := "test-domain" tests := []struct { name string setupMocks func() expectedResult bool expectedError error }{ { name: "Success - no open workflows", setupMocks: func() { mockClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.CountWorkflowExecutionsResponse{Count: 0}, nil) }, expectedResult: false, expectedError: nil, }, { name: "Success - has open workflows", setupMocks: func() { mockClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.CountWorkflowExecutionsResponse{Count: 100}, nil) }, expectedResult: true, expectedError: nil, }, { name: "Error", setupMocks: func() { mockClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.CountWorkflowExecutionsResponse{Count: 0}, assert.AnError) }, expectedResult: false, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.setupMocks() hasOpenWorkflows, err := deprecator.CheckOpenWorkflowsActivity(context.Background(), DomainDeprecationParams{ DomainName: testDomain, }) if tt.expectedError != nil { assert.Error(t, err) } else { assert.Equal(t, tt.expectedResult, hasOpenWorkflows) assert.NoError(t, err) } }) } } ================================================ FILE: service/worker/domaindeprecation/entities.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package domaindeprecation // DomainDeprecationParams contains the parameters required for domain deprecation workflow. type DomainDeprecationParams struct { DomainName string `json:"domain_name"` SecurityToken string `json:"security_token"` } ================================================ FILE: service/worker/domaindeprecation/helpers.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package domaindeprecation import "time" const ( // ErrDomainDoesNotExistNonRetryable is error reason used for Cadence non-retryable errors ErrDomainDoesNotExistNonRetryable = "domain does not exist" // ErrAccessDeniedNonRetryable is permission error that require manual intervention ErrAccessDeniedNonRetryable = "AccessDeniedError" // ErrWorkflowAlreadyCompletedNonRetryable is error that indicates workflow execution already completed ErrWorkflowAlreadyCompletedNonRetryable = "WorkflowExecutionAlreadyCompletedError" // DefaultRPS is the default RPS DefaultRPS = 50 // DefaultConcurrency is the default concurrency DefaultConcurrency = 5 // DefaultPageSize is the default page size DefaultPageSize = 1000 // DefaultAttemptsOnRetryableError is the default value for AttemptsOnRetryableError DefaultAttemptsOnRetryableError = 50 // DefaultActivityHeartBeatTimeout is the default value for ActivityHeartBeatTimeout DefaultActivityHeartBeatTimeout = time.Second * 10 // DefaultMaxActivityRetries is the default value for MaxActivityRetries DefaultMaxActivityRetries = 4 ) ================================================ FILE: service/worker/domaindeprecation/module.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package domaindeprecation import ( "context" "github.com/opentracing/opentracing-go" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/activity" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "github.com/uber/cadence/client" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/service/worker/batcher" ) type ( DomainDeprecationWorker interface { Start() error Stop() } // Config defines the configuration for domain deprecator Config struct { // AdminOperationToken is a dynamic config that provides the security token for admin operations AdminOperationToken dynamicproperties.StringPropertyFn } domainDeprecator struct { cfg Config svcClient workflowserviceclient.Interface clientBean client.Bean metricsClient metrics.Client worker worker.Worker tally tally.Scope logger log.Logger } Params struct { Config Config ServiceClient workflowserviceclient.Interface ClientBean client.Bean MetricsClient metrics.Client Tally tally.Scope Logger log.Logger } ) // New creates a new domain deprecation workflow. func New(params Params) DomainDeprecationWorker { return &domainDeprecator{ cfg: params.Config, svcClient: params.ServiceClient, clientBean: params.ClientBean, metricsClient: params.MetricsClient, tally: params.Tally, logger: params.Logger, } } // Start starts the worker func (w *domainDeprecator) Start() error { batcherParams := &batcher.BootstrapParams{ Config: batcher.Config{ AdminOperationToken: w.cfg.AdminOperationToken, }, ServiceClient: w.svcClient, ClientBean: w.clientBean, MetricsClient: w.metricsClient, Logger: w.logger, TallyScope: w.tally, } batcherInstance := batcher.New(batcherParams) ctx := context.WithValue(context.Background(), batcher.BatcherContextKey, batcherInstance) workerOpts := worker.Options{ MetricsScope: w.tally, BackgroundActivityContext: ctx, Tracer: opentracing.GlobalTracer(), MaxConcurrentActivityTaskPollers: 10, MaxConcurrentDecisionTaskPollers: 10, } newWorker := worker.New(w.svcClient, constants.SystemLocalDomainName, DomainDeprecationTaskListName, workerOpts) newWorker.RegisterWorkflowWithOptions(w.DomainDeprecationWorkflow, workflow.RegisterOptions{Name: DomainDeprecationWorkflowTypeName}) newWorker.RegisterActivityWithOptions(w.DisableArchivalActivity, activity.RegisterOptions{Name: disableArchivalActivity, EnableAutoHeartbeat: true}) newWorker.RegisterActivityWithOptions(w.CheckOpenWorkflowsActivity, activity.RegisterOptions{Name: checkOpenWorkflowsActivity, EnableAutoHeartbeat: true}) newWorker.RegisterActivityWithOptions(w.DeprecateDomainActivity, activity.RegisterOptions{Name: deprecateDomainActivity, EnableAutoHeartbeat: true}) w.worker = newWorker return newWorker.Start() } func (w *domainDeprecator) Stop() { w.worker.Stop() } ================================================ FILE: service/worker/domaindeprecation/module_test.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package domaindeprecation import ( "testing" "github.com/stretchr/testify/require" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/mock/gomock" "github.com/uber/cadence/client" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" ) func Test__Start(t *testing.T) { domainDeprecationWorkerTest, mockResource := setupTest(t) err := domainDeprecationWorkerTest.Start() require.NoError(t, err) domainDeprecationWorkerTest.Stop() mockResource.Finish(t) } func setupTest(t *testing.T) (DomainDeprecationWorker, *resource.Test) { ctrl := gomock.NewController(t) mockResource := resource.NewTest(t, ctrl, metrics.Worker) mockResource.SDKClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&shared.DescribeDomainResponse{}, nil).AnyTimes() mockResource.SDKClient.EXPECT().PollForDecisionTask(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&shared.PollForDecisionTaskResponse{}, nil).AnyTimes() mockResource.SDKClient.EXPECT().PollForActivityTask(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&shared.PollForActivityTaskResponse{}, nil).AnyTimes() mockClientBean := client.NewMockBean(ctrl) mockSvcClient := mockResource.GetSDKClient() return New(Params{ Config: Config{ AdminOperationToken: dynamicproperties.GetStringPropertyFn(""), }, ServiceClient: mockSvcClient, ClientBean: mockClientBean, Tally: tally.TestScope(nil), Logger: mockResource.GetLogger(), }), mockResource } ================================================ FILE: service/worker/domaindeprecation/workflow.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package domaindeprecation import ( "fmt" "time" "github.com/pborman/uuid" "go.uber.org/cadence" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/common" "github.com/uber/cadence/service/worker/batcher" ) const ( DomainDeprecationWorkflowTypeName = "domain-deprecation-workflow" DomainDeprecationTaskListName = "domain-deprecation-tasklist" domainDeprecationBatchPrefix = "domain-deprecation-batch" disableArchivalActivity = "disableArchival" deprecateDomainActivity = "deprecateDomain" checkOpenWorkflowsActivity = "checkOpenWorkflows" workflowStartToCloseTimeout = time.Hour * 24 * 30 workflowTaskStartToCloseTimeout = 5 * time.Minute // MaxBatchWorkflowAttempts is the maximum number of attempts to terminate open workflows MaxBatchWorkflowAttempts = 3 // VisibilityRefreshWaitTime is the time to wait for visibility storage to refresh after workflow termination VisibilityRefreshWaitTime = 3 * time.Minute ) var ( retryPolicy = cadence.RetryPolicy{ InitialInterval: 10 * time.Second, BackoffCoefficient: 1.7, MaximumInterval: 5 * time.Minute, ExpirationInterval: 24 * time.Hour, NonRetriableErrorReasons: []string{ ErrDomainDoesNotExistNonRetryable, ErrAccessDeniedNonRetryable, }, } activityOptions = workflow.ActivityOptions{ ScheduleToStartTimeout: 5 * time.Minute, StartToCloseTimeout: 5 * time.Minute, HeartbeatTimeout: 5 * time.Minute, RetryPolicy: &retryPolicy, } ) // DomainDeprecationWorkflow is the workflow that handles domain deprecation process func (w *domainDeprecator) DomainDeprecationWorkflow(ctx workflow.Context, params DomainDeprecationParams) error { logger := workflow.GetLogger(ctx) logger.Info("Starting domain deprecation workflow", zap.String("domain", params.DomainName)) // Step 1: Activity disables archival err := workflow.ExecuteActivity( workflow.WithActivityOptions(ctx, activityOptions), w.DisableArchivalActivity, params, ).Get(ctx, nil) if err != nil { return err } // Step 2: Deprecate a domain err = workflow.ExecuteActivity( workflow.WithActivityOptions(ctx, activityOptions), w.DeprecateDomainActivity, params, ).Get(ctx, nil) if err != nil { return err } // Step 3: Start child batch workflow to terminate open workflows of a domain batchParams := batcher.BatchParams{ DomainName: params.DomainName, Query: "CloseTime = missing", Reason: "domain is deprecated", BatchType: batcher.BatchTypeTerminate, TerminateParams: batcher.TerminateParams{ TerminateChildren: common.BoolPtr(true), }, RPS: DefaultRPS, Concurrency: DefaultConcurrency, PageSize: DefaultPageSize, AttemptsOnRetryableError: DefaultAttemptsOnRetryableError, ActivityHeartBeatTimeout: DefaultActivityHeartBeatTimeout, MaxActivityRetries: DefaultMaxActivityRetries, NonRetryableErrors: []string{ ErrAccessDeniedNonRetryable, ErrWorkflowAlreadyCompletedNonRetryable, }, } for attempt := 1; attempt <= MaxBatchWorkflowAttempts; attempt++ { logger.Info("Starting batch workflow", zap.String("domain", params.DomainName), zap.Int("attempt", attempt), zap.Int("max_attempts", MaxBatchWorkflowAttempts)) workflowID := uuid.New() childWorkflowOptions := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{ WorkflowID: fmt.Sprintf("%s-%s-%s-%d", domainDeprecationBatchPrefix, params.DomainName, workflowID, attempt), ExecutionStartToCloseTimeout: workflowStartToCloseTimeout, TaskStartToCloseTimeout: workflowTaskStartToCloseTimeout, }) var result batcher.HeartBeatDetails err = workflow.ExecuteChildWorkflow(childWorkflowOptions, batcher.BatchWorkflow, batchParams).Get(ctx, &result) if err != nil { return fmt.Errorf("batch workflow failed on attempt %d: %v", attempt, err) } // Wait for visibility storage to refresh err = workflow.Sleep(ctx, VisibilityRefreshWaitTime) if err != nil { return fmt.Errorf("workflow sleep failed on attempt %d: %v", attempt, err) } // Check if there are still open workflows var hasOpenWorkflows bool err = workflow.ExecuteActivity( workflow.WithActivityOptions(ctx, activityOptions), w.CheckOpenWorkflowsActivity, params, ).Get(ctx, &hasOpenWorkflows) if err != nil { return fmt.Errorf("failed to check open workflows on attempt %d: %v", attempt, err) } if !hasOpenWorkflows { logger.Info("No open workflows found after batch workflow", zap.String("domain", params.DomainName), zap.Int("attempt", attempt)) break } if attempt == MaxBatchWorkflowAttempts { return fmt.Errorf("failed to terminate all workflows after %d attempts", MaxBatchWorkflowAttempts) } logger.Info("Found open workflows after batch workflow, will retry", zap.String("domain", params.DomainName), zap.Int("attempt", attempt)) } logger.Info("Domain deprecation workflow completed successfully", zap.String("domain", params.DomainName)) return nil } ================================================ FILE: service/worker/domaindeprecation/workflow_test.go ================================================ // Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package domaindeprecation import ( "errors" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/cadence/activity" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/service/worker/batcher" ) var ( testDomain = "test-domain" defaultParams = DomainDeprecationParams{ DomainName: testDomain, SecurityToken: "token", } ) type domainDeprecationWorkflowTestSuite struct { suite.Suite testsuite.WorkflowTestSuite workflowEnv *testsuite.TestWorkflowEnvironment deprecator *domainDeprecator } func TestDomainDeprecationWorkflowTestSuite(t *testing.T) { suite.Run(t, new(domainDeprecationWorkflowTestSuite)) } func (s *domainDeprecationWorkflowTestSuite) SetupTest() { s.workflowEnv = s.NewTestWorkflowEnvironment() controller := gomock.NewController(s.T()) mockResource := resource.NewTest(s.T(), controller, metrics.Worker) publicClient := mockResource.GetSDKClient() s.deprecator = &domainDeprecator{ cfg: Config{ AdminOperationToken: dynamicproperties.GetStringPropertyFn(""), }, svcClient: publicClient, clientBean: mockResource.ClientBean, metricsClient: metrics.NewNoopMetricsClient(), tally: tally.NoopScope, logger: mockResource.GetLogger(), } s.T().Cleanup(func() { mockResource.Finish(s.T()) }) s.workflowEnv.RegisterWorkflowWithOptions(s.deprecator.DomainDeprecationWorkflow, workflow.RegisterOptions{Name: DomainDeprecationWorkflowTypeName}) s.workflowEnv.RegisterActivityWithOptions(s.deprecator.DisableArchivalActivity, activity.RegisterOptions{Name: disableArchivalActivity}) s.workflowEnv.RegisterActivityWithOptions(s.deprecator.DeprecateDomainActivity, activity.RegisterOptions{Name: deprecateDomainActivity}) s.workflowEnv.RegisterActivityWithOptions(s.deprecator.CheckOpenWorkflowsActivity, activity.RegisterOptions{Name: checkOpenWorkflowsActivity}) } func (s *domainDeprecationWorkflowTestSuite) TearDownTest() { s.workflowEnv.AssertExpectations(s.T()) } func (s *domainDeprecationWorkflowTestSuite) TestWorkflow_Success() { s.workflowEnv.OnActivity(disableArchivalActivity, mock.Anything, defaultParams).Return(nil) s.workflowEnv.OnActivity(deprecateDomainActivity, mock.Anything, defaultParams).Return(nil) s.workflowEnv.OnWorkflow(batcher.BatchWorkflow, mock.Anything, mock.Anything).Return( batcher.HeartBeatDetails{ SuccessCount: 10, ErrorCount: 0, }, nil).Once() s.workflowEnv.OnActivity(checkOpenWorkflowsActivity, mock.Anything, defaultParams).Return(false, nil) s.workflowEnv.ExecuteWorkflow(DomainDeprecationWorkflowTypeName, defaultParams) s.True(s.workflowEnv.IsWorkflowCompleted()) s.NoError(s.workflowEnv.GetWorkflowError()) } func (s *domainDeprecationWorkflowTestSuite) TestWorkflow_Disable_Archival_Error() { mockErr := errors.New("error") s.workflowEnv.OnActivity(disableArchivalActivity, mock.Anything, defaultParams).Return(mockErr) s.workflowEnv.ExecuteWorkflow(DomainDeprecationWorkflowTypeName, defaultParams) s.True(s.workflowEnv.IsWorkflowCompleted()) s.Error(s.workflowEnv.GetWorkflowError()) s.ErrorContains(s.workflowEnv.GetWorkflowError(), mockErr.Error()) } func (s *domainDeprecationWorkflowTestSuite) TestWorkflow_Deprecate_Domain_Error() { mockErr := errors.New("error") s.workflowEnv.OnActivity(disableArchivalActivity, mock.Anything, defaultParams).Return(nil) s.workflowEnv.OnActivity(deprecateDomainActivity, mock.Anything, defaultParams).Return(mockErr) s.workflowEnv.ExecuteWorkflow(DomainDeprecationWorkflowTypeName, defaultParams) s.True(s.workflowEnv.IsWorkflowCompleted()) s.Error(s.workflowEnv.GetWorkflowError()) s.ErrorContains(s.workflowEnv.GetWorkflowError(), mockErr.Error()) } func (s *domainDeprecationWorkflowTestSuite) TestWorkflow_BatchTerminate_ChildWorkflowError() { mockErr := errors.New("batch workflow failed") s.workflowEnv.OnActivity(disableArchivalActivity, mock.Anything, defaultParams).Return(nil) s.workflowEnv.OnActivity(deprecateDomainActivity, mock.Anything, defaultParams).Return(nil) s.workflowEnv.OnWorkflow(batcher.BatchWorkflow, mock.Anything, mock.Anything).Return( batcher.HeartBeatDetails{}, mockErr) s.workflowEnv.ExecuteWorkflow(DomainDeprecationWorkflowTypeName, defaultParams) s.True(s.workflowEnv.IsWorkflowCompleted()) s.Error(s.workflowEnv.GetWorkflowError()) s.ErrorContains(s.workflowEnv.GetWorkflowError(), mockErr.Error()) } func (s *domainDeprecationWorkflowTestSuite) TestWorkflow_CheckOpenWorkflows_Error() { mockErr := errors.New("error") s.workflowEnv.OnActivity(disableArchivalActivity, mock.Anything, defaultParams).Return(nil) s.workflowEnv.OnActivity(deprecateDomainActivity, mock.Anything, defaultParams).Return(nil) s.workflowEnv.OnWorkflow(batcher.BatchWorkflow, mock.Anything, mock.Anything).Return( batcher.HeartBeatDetails{ SuccessCount: 10, ErrorCount: 0, }, nil).Once() s.workflowEnv.OnActivity(checkOpenWorkflowsActivity, mock.Anything, defaultParams).Return(true, mockErr) s.workflowEnv.ExecuteWorkflow(DomainDeprecationWorkflowTypeName, defaultParams) s.True(s.workflowEnv.IsWorkflowCompleted()) s.Error(s.workflowEnv.GetWorkflowError()) s.ErrorContains(s.workflowEnv.GetWorkflowError(), mockErr.Error()) } ================================================ FILE: service/worker/esanalyzer/analyzer.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esanalyzer import ( "context" "time" "github.com/opentracing/opentracing-go" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/.gen/go/shared" cclient "go.uber.org/cadence/client" "go.uber.org/cadence/worker" "github.com/uber/cadence/client" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" es "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/service/worker/workercommon" ) type readMode string const ( Pinot readMode = "pinot" ES readMode = "es" ) type ( // Analyzer is the background sub-system to query ElasticSearch and execute mitigations Analyzer struct { svcClient workflowserviceclient.Interface frontendClient frontend.Client clientBean client.Bean esClient es.GenericClient pinotClient pinot.GenericClient readMode readMode logger log.Logger tallyScope tally.Scope visibilityIndexName string pinotTableName string resource resource.Resource domainCache cache.DomainCache config *Config } // Config contains all configs for ElasticSearch Analyzer Config struct { ESAnalyzerPause dynamicproperties.BoolPropertyFn ESAnalyzerTimeWindow dynamicproperties.DurationPropertyFn ESAnalyzerMaxNumDomains dynamicproperties.IntPropertyFn ESAnalyzerMaxNumWorkflowTypes dynamicproperties.IntPropertyFn ESAnalyzerLimitToTypes dynamicproperties.StringPropertyFn ESAnalyzerEnableAvgDurationBasedChecks dynamicproperties.BoolPropertyFn ESAnalyzerLimitToDomains dynamicproperties.StringPropertyFn ESAnalyzerNumWorkflowsToRefresh dynamicproperties.IntPropertyFnWithWorkflowTypeFilter ESAnalyzerBufferWaitTime dynamicproperties.DurationPropertyFnWithWorkflowTypeFilter ESAnalyzerMinNumWorkflowsForAvg dynamicproperties.IntPropertyFnWithWorkflowTypeFilter ESAnalyzerWorkflowDurationWarnThresholds dynamicproperties.StringPropertyFn ESAnalyzerWorkflowVersionDomains dynamicproperties.StringPropertyFn ESAnalyzerWorkflowTypeDomains dynamicproperties.StringPropertyFn } Workflow struct { analyzer *Analyzer } EsAggregateCount struct { AggregateKey string `json:"key"` AggregateCount int64 `json:"doc_count"` } ) const ( taskListName = "cadence-sys-es-analyzer" startUpDelay = time.Second * 10 ) // New returns a new instance as daemon func New( svcClient workflowserviceclient.Interface, frontendClient frontend.Client, clientBean client.Bean, esClient es.GenericClient, pinotClient pinot.GenericClient, esConfig *config.ElasticSearchConfig, pinotConfig *config.PinotVisibilityConfig, logger log.Logger, tallyScope tally.Scope, resource resource.Resource, domainCache cache.DomainCache, config *Config, ) *Analyzer { var mode readMode var indexName string var pinotTableName string if esClient != nil { mode = ES indexName = esConfig.Indices[constants.VisibilityAppName] pinotTableName = "" } else if pinotClient != nil { mode = Pinot indexName = "" pinotTableName = pinotConfig.Table } return &Analyzer{ svcClient: svcClient, frontendClient: frontendClient, clientBean: clientBean, esClient: esClient, pinotClient: pinotClient, readMode: mode, logger: logger, tallyScope: tallyScope, visibilityIndexName: indexName, pinotTableName: pinotTableName, resource: resource, domainCache: domainCache, config: config, } } // Start starts the scanner func (a *Analyzer) Start() error { ctx := context.Background() a.StartWorkflow(ctx) ctx = context.Background() a.StartDomainWFTypeCountWorkflow(ctx) workerOpts := worker.Options{ MetricsScope: a.tallyScope, BackgroundActivityContext: ctx, Tracer: opentracing.GlobalTracer(), } esWorker := worker.New(a.svcClient, constants.SystemLocalDomainName, taskListName, workerOpts) err := esWorker.Start() return err } func (a *Analyzer) StartWorkflow(ctx context.Context) { initWorkflow(a) go workercommon.StartWorkflowWithRetry(esanalyzerWFTypeName, startUpDelay, a.resource, func(client cclient.Client) error { _, err := client.StartWorkflow(ctx, wfOptions, esanalyzerWFTypeName) switch err.(type) { case *shared.WorkflowExecutionAlreadyStartedError: return nil default: a.logger.Error("Failed to start ElasticSearch Analyzer", tag.Error(err)) return err } }) } func (a *Analyzer) StartDomainWFTypeCountWorkflow(ctx context.Context) { initDomainWorkflowTypeCountWorkflow(a) go workercommon.StartWorkflowWithRetry(domainWFTypeCountWorkflowTypeName, startUpDelay, a.resource, func(client cclient.Client) error { _, err := client.StartWorkflow(ctx, domainWfTypeCountStartOptions, domainWFTypeCountWorkflowTypeName) switch err.(type) { case *shared.WorkflowExecutionAlreadyStartedError: return nil default: a.logger.Error("Failed to start domain wf type count workflow", tag.Error(err)) return err } }) } ================================================ FILE: service/worker/esanalyzer/analyzer_test.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABxILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esanalyzer import ( "context" "encoding/json" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/cadence/activity" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "go.uber.org/zap" "github.com/uber/cadence/client" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/elasticsearch" esMocks "github.com/uber/cadence/common/elasticsearch/mocks" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/metrics/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/pinot" "github.com/uber/cadence/service/history/resource" ) type esanalyzerWorkflowTestSuite struct { suite.Suite testsuite.WorkflowTestSuite activityEnv *testsuite.TestActivityEnvironment workflowEnv *testsuite.TestWorkflowEnvironment controller *gomock.Controller resource *resource.Test mockAdminClient *admin.MockClient mockDomainCache *cache.MockDomainCache clientBean *client.MockBean logger *log.MockLogger mockMetricClient *mocks.Client scopedMetricClient *mocks.Scope mockESClient *esMocks.GenericClient tallyScope tally.Scope analyzer *Analyzer workflow *Workflow config Config DomainID string DomainName string WorkflowType string WorkflowID string RunID string WorkflowVersion string } func TestESAnalyzerWorkflowTestSuite(t *testing.T) { suite.Run(t, new(esanalyzerWorkflowTestSuite)) } func (s *esanalyzerWorkflowTestSuite) SetupTest() { s.DomainID = "deadbeef-0123-4567-890a-bcdef0123460" s.DomainName = "test-domain" s.WorkflowType = "test-workflow-type" s.WorkflowID = "test-workflow_id" s.RunID = "test-run_id" s.WorkflowVersion = "test-workflow-version" s.tallyScope = tally.NoopScope activeDomainCache := cache.NewGlobalDomainCacheEntryForTest( &persistence.DomainInfo{ID: s.DomainID, Name: s.DomainName}, &persistence.DomainConfig{Retention: 1}, &persistence.DomainReplicationConfig{ ActiveClusterName: cluster.TestCurrentClusterName, Clusters: []*persistence.ClusterReplicationConfig{ {ClusterName: cluster.TestCurrentClusterName}, {ClusterName: cluster.TestAlternativeClusterName}, }, }, 1234, ) s.config = Config{ ESAnalyzerPause: dynamicproperties.GetBoolPropertyFn(false), ESAnalyzerTimeWindow: dynamicproperties.GetDurationPropertyFn(time.Hour * 24 * 30), ESAnalyzerMaxNumDomains: dynamicproperties.GetIntPropertyFn(500), ESAnalyzerMaxNumWorkflowTypes: dynamicproperties.GetIntPropertyFn(100), ESAnalyzerLimitToTypes: dynamicproperties.GetStringPropertyFn(""), ESAnalyzerLimitToDomains: dynamicproperties.GetStringPropertyFn(""), ESAnalyzerNumWorkflowsToRefresh: dynamicproperties.GetIntPropertyFilteredByWorkflowType(2), ESAnalyzerBufferWaitTime: dynamicproperties.GetDurationPropertyFilteredByWorkflowType(time.Minute * 30), ESAnalyzerMinNumWorkflowsForAvg: dynamicproperties.GetIntPropertyFilteredByWorkflowType(100), ESAnalyzerWorkflowDurationWarnThresholds: dynamicproperties.GetStringPropertyFn(""), } s.activityEnv = s.NewTestActivityEnvironment() s.workflowEnv = s.NewTestWorkflowEnvironment() s.controller = gomock.NewController(s.T()) s.mockDomainCache = cache.NewMockDomainCache(s.controller) s.resource = resource.NewTest(s.T(), s.controller, metrics.Worker) s.mockAdminClient = admin.NewMockClient(s.controller) s.clientBean = client.NewMockBean(s.controller) s.logger = log.NewMockLogger(s.controller) s.mockMetricClient = &mocks.Client{} s.scopedMetricClient = &mocks.Scope{} s.mockESClient = &esMocks.GenericClient{} // // s.mockDomainCache.EXPECT().GetDomainByID(s.DomainID).Return(activeDomainCache, nil).AnyTimes() s.mockDomainCache.EXPECT().GetDomain(s.DomainName).Return(activeDomainCache, nil).AnyTimes() // SET UP ANALYZER s.analyzer = &Analyzer{ svcClient: s.resource.GetSDKClient(), clientBean: s.clientBean, domainCache: s.mockDomainCache, logger: s.logger, esClient: s.mockESClient, config: &s.config, tallyScope: s.tallyScope, } s.activityEnv.SetTestTimeout(time.Second * 5) s.activityEnv.SetWorkerOptions(worker.Options{BackgroundActivityContext: context.Background()}) // REGISTER WORKFLOWS AND ACTIVITIES s.workflow = &Workflow{analyzer: s.analyzer} s.workflowEnv.RegisterWorkflowWithOptions( s.workflow.workflowFunc, workflow.RegisterOptions{Name: esanalyzerWFTypeName}) s.workflowEnv.RegisterActivityWithOptions( s.workflow.emitWorkflowVersionMetrics, activity.RegisterOptions{Name: emitWorkflowVersionMetricsActivity}, ) s.activityEnv.RegisterActivityWithOptions( s.workflow.emitWorkflowVersionMetrics, activity.RegisterOptions{Name: emitWorkflowVersionMetricsActivity}, ) s.workflowEnv.RegisterWorkflowWithOptions( s.workflow.emitWorkflowTypeCount, workflow.RegisterOptions{Name: domainWFTypeCountWorkflowTypeName}) s.activityEnv.RegisterActivityWithOptions( s.workflow.emitWorkflowTypeCountMetrics, activity.RegisterOptions{Name: emitDomainWorkflowTypeCountMetricsActivity}, ) } func (s *esanalyzerWorkflowTestSuite) TearDownTest() { defer s.controller.Finish() defer s.resource.Finish(s.T()) s.workflowEnv.AssertExpectations(s.T()) } func (s *esanalyzerWorkflowTestSuite) TestExecuteWorkflow() { s.workflowEnv.OnActivity(emitWorkflowVersionMetricsActivity, mock.Anything).Return(nil).Times(1) s.workflowEnv.ExecuteWorkflow(esanalyzerWFTypeName, mock.Anything) err := s.workflowEnv.GetWorkflowResult(nil) s.NoError(err) } func (s *esanalyzerWorkflowTestSuite) TestEmitWorkflowVersionMetricsActivity() { s.config.ESAnalyzerWorkflowVersionDomains = dynamicproperties.GetStringPropertyFn( fmt.Sprintf(`["%s"]`, s.DomainName), ) esRaw := ` { "took": 642, "timed_out": false, "_shards": { "total": 20, "successful": 20, "skipped": 0, "failed": 0 }, "hits": { "total": 4588, "max_score": 0, "hits": [ ] }, "aggregations": { "wftypes": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "CourierUpdateWorkflow", "doc_count": 4539, "versions": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ ] } }, { "key": "UpdateTipWorkflow", "doc_count": 33, "versions": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "update tip efp flow-1", "doc_count": 3 } ] } }, { "key": "RoboCourierWorkflow", "doc_count": 12, "versions": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ ] } }, { "key": "ImproveDropoffPinWorkflow", "doc_count": 2, "versions": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ ] } }, { "key": "OrderStateUpdatedWorkflow", "doc_count": 2, "versions": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ ] } } ] } } } ` var rawEs elasticsearch.RawResponse err := json.Unmarshal([]byte(esRaw), &rawEs) s.NoError(err) s.mockESClient.On("SearchRaw", mock.Anything, mock.Anything, mock.Anything).Return( &rawEs, nil).Times(1) _, err = s.activityEnv.ExecuteActivity(s.workflow.emitWorkflowVersionMetrics) s.NoError(err) } func (s *esanalyzerWorkflowTestSuite) TestEmitWorkflowTypeCountMetricsActivity() { s.config.ESAnalyzerWorkflowTypeDomains = dynamicproperties.GetStringPropertyFn( fmt.Sprintf(`["%s"]`, s.DomainName), ) esRaw := ` { "took": 642, "timed_out": false, "_shards": { "total": 20, "successful": 20, "skipped": 0, "failed": 0 }, "hits": { "total": 4588, "max_score": 0, "hits": [ ] }, "aggregations": { "wftypes": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "CourierUpdateWorkflow", "doc_count": 4539 }, { "key": "UpdateTipWorkflow", "doc_count": 33 }, { "key": "RoboCourierWorkflow", "doc_count": 12 }, { "key": "ImproveDropoffPinWorkflow", "doc_count": 2 }, { "key": "OrderStateUpdatedWorkflow", "doc_count": 2 } ] } } } ` var rawEs elasticsearch.RawResponse err := json.Unmarshal([]byte(esRaw), &rawEs) s.NoError(err) s.mockESClient.On("SearchRaw", mock.Anything, mock.Anything, mock.Anything).Return( &rawEs, nil).Times(1) _, err = s.activityEnv.ExecuteActivity(s.workflow.emitWorkflowTypeCountMetrics) s.NoError(err) } func TestNewAnalyzer(t *testing.T) { mockESConfig := &config.ElasticSearchConfig{ Indices: map[string]string{ constants.VisibilityAppName: "test", }, } mockPinotConfig := &config.PinotVisibilityConfig{ Table: "test", } mockESClient := &esMocks.GenericClient{} testAnalyzer1 := New(nil, nil, nil, mockESClient, nil, mockESConfig, mockPinotConfig, nil, nil, nil, nil, nil) mockPinotClient := &pinot.MockGenericClient{} testAnalyzer2 := New(nil, nil, nil, nil, mockPinotClient, mockESConfig, mockPinotConfig, nil, nil, nil, nil, nil) assert.Equal(t, testAnalyzer1.readMode, ES) assert.Equal(t, testAnalyzer2.readMode, Pinot) } func TestEmitWorkflowTypeCountMetricsESErrorCases(t *testing.T) { mockESConfig := &config.ElasticSearchConfig{ Indices: map[string]string{ constants.VisibilityAppName: "test", }, } mockPinotConfig := &config.PinotVisibilityConfig{ Table: "test", } ctrl := gomock.NewController(t) mockESClient := &esMocks.GenericClient{} mockDomainCache := cache.NewMockDomainCache(ctrl) testAnalyzer := New(nil, nil, nil, mockESClient, nil, mockESConfig, mockPinotConfig, log.NewNoop(), tally.NoopScope, nil, mockDomainCache, nil) testWorkflow := &Workflow{analyzer: testAnalyzer} tests := map[string]struct { domainCacheAffordance func(mockDomainCache *cache.MockDomainCache) ESClientAffordance func(mockESClient *esMocks.GenericClient) expectedErr error }{ "Case1: error getting domain": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, ESClientAffordance: func(mockESClient *esMocks.GenericClient) {}, expectedErr: fmt.Errorf("error"), }, "Case2: error ES searchRaw": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, ESClientAffordance: func(mockESClient *esMocks.GenericClient) { mockESClient.On("SearchRaw", mock.Anything, mock.Anything, mock.Anything).Return( nil, fmt.Errorf("error")).Times(1) }, expectedErr: fmt.Errorf("error"), }, "Case3: foundAggregation is false": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, ESClientAffordance: func(mockESClient *esMocks.GenericClient) { mockESClient.On("SearchRaw", mock.Anything, mock.Anything, mock.Anything).Return( &elasticsearch.RawResponse{ Aggregations: map[string]json.RawMessage{}, }, nil).Times(1) }, expectedErr: fmt.Errorf("aggregation failed for domain in ES: test-domain"), }, "Case4: error unmarshalling aggregation": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, ESClientAffordance: func(mockESClient *esMocks.GenericClient) { mockESClient.On("SearchRaw", mock.Anything, mock.Anything, mock.Anything).Return( &elasticsearch.RawResponse{ Aggregations: map[string]json.RawMessage{ "wftypes": []byte("invalid"), }, }, nil).Times(1) }, expectedErr: fmt.Errorf("invalid character 'i' looking for beginning of value"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // Set up mocks test.domainCacheAffordance(mockDomainCache) test.ESClientAffordance(mockESClient) err := testWorkflow.emitWorkflowTypeCountMetricsES(context.Background(), "test-domain", zap.NewNop()) if err == nil { assert.Equal(t, test.expectedErr, err) } else { assert.Equal(t, test.expectedErr.Error(), err.Error()) } }) } } func TestEmitWorkflowVersionMetricsESErrorCases(t *testing.T) { mockESConfig := &config.ElasticSearchConfig{ Indices: map[string]string{ constants.VisibilityAppName: "test", }, } mockPinotConfig := &config.PinotVisibilityConfig{ Table: "test", } ctrl := gomock.NewController(t) mockESClient := &esMocks.GenericClient{} mockDomainCache := cache.NewMockDomainCache(ctrl) testAnalyzer := New(nil, nil, nil, mockESClient, nil, mockESConfig, mockPinotConfig, log.NewNoop(), tally.NoopScope, nil, mockDomainCache, nil) testWorkflow := &Workflow{analyzer: testAnalyzer} tests := map[string]struct { domainCacheAffordance func(mockDomainCache *cache.MockDomainCache) ESClientAffordance func(mockESClient *esMocks.GenericClient) expectedErr error }{ "Case1: error getting domain": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(nil, fmt.Errorf("error")).Times(1) }, ESClientAffordance: func(mockESClient *esMocks.GenericClient) {}, expectedErr: fmt.Errorf("error"), }, "Case2: error ES searchRaw": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, ESClientAffordance: func(mockESClient *esMocks.GenericClient) { mockESClient.On("SearchRaw", mock.Anything, mock.Anything, mock.Anything).Return( nil, fmt.Errorf("error")).Times(1) }, expectedErr: fmt.Errorf("error"), }, "Case3: foundAggregation is false": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, ESClientAffordance: func(mockESClient *esMocks.GenericClient) { mockESClient.On("SearchRaw", mock.Anything, mock.Anything, mock.Anything).Return( &elasticsearch.RawResponse{ Aggregations: map[string]json.RawMessage{}, }, nil).Times(1) }, expectedErr: fmt.Errorf("aggregation failed for domain in ES: test-domain"), }, "Case4: error unmarshalling aggregation": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, ESClientAffordance: func(mockESClient *esMocks.GenericClient) { mockESClient.On("SearchRaw", mock.Anything, mock.Anything, mock.Anything).Return( &elasticsearch.RawResponse{ Aggregations: map[string]json.RawMessage{ "wftypes": []byte("invalid"), }, }, nil).Times(1) }, expectedErr: fmt.Errorf("invalid character 'i' looking for beginning of value"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // Set up mocks test.domainCacheAffordance(mockDomainCache) test.ESClientAffordance(mockESClient) err := testWorkflow.emitWorkflowVersionMetricsES(context.Background(), "test-domain", zap.NewNop()) if err == nil { assert.Equal(t, test.expectedErr, err) } else { assert.Equal(t, test.expectedErr.Error(), err.Error()) } }) } } func TestEmitWorkflowTypeCountMetricsPinot(t *testing.T) { mockPinotConfig := &config.PinotVisibilityConfig{ Table: "test", } mockESConfig := &config.ElasticSearchConfig{ Indices: map[string]string{ constants.VisibilityAppName: "test", }, } ctrl := gomock.NewController(t) mockPinotClient := pinot.NewMockGenericClient(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) testAnalyzer := New(nil, nil, nil, nil, mockPinotClient, mockESConfig, mockPinotConfig, log.NewNoop(), tally.NoopScope, nil, mockDomainCache, nil) testWorkflow := &Workflow{analyzer: testAnalyzer} tests := map[string]struct { domainCacheAffordance func(mockDomainCache *cache.MockDomainCache) PinotClientAffordance func(mockPinotClient *pinot.MockGenericClient) expectedErr error }{ "Case0: success": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{ {"test0", float64(1)}, {"test1", float64(2)}, }, nil).Times(1) }, expectedErr: nil, }, "Case1: error getting domain": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(nil, fmt.Errorf("domain error")).Times(1) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) {}, expectedErr: fmt.Errorf("domain error"), }, "Case2: error Pinot query": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return(nil, fmt.Errorf("pinot error")).Times(1) }, expectedErr: fmt.Errorf("pinot error"), }, "Case3: Aggregation is empty": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{}, nil).Times(1) }, expectedErr: fmt.Errorf("aggregation failed for domain in Pinot: test-domain"), }, "Case4: error parsing workflow count": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{ {"test", "invalid"}, }, nil).Times(1) }, expectedErr: fmt.Errorf("error parsing workflow count for workflow type test"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // Set up mocks test.domainCacheAffordance(mockDomainCache) test.PinotClientAffordance(mockPinotClient) err := testWorkflow.emitWorkflowTypeCountMetricsPinot("test-domain", zap.NewNop()) if err == nil { assert.Equal(t, test.expectedErr, err) } else { assert.Equal(t, test.expectedErr.Error(), err.Error()) } }) } } func TestEmitWorkflowVersionMetricsPinot(t *testing.T) { mockPinotConfig := &config.PinotVisibilityConfig{ Table: "test", } mockESConfig := &config.ElasticSearchConfig{ Indices: map[string]string{ constants.VisibilityAppName: "test", }, } ctrl := gomock.NewController(t) mockPinotClient := pinot.NewMockGenericClient(ctrl) mockDomainCache := cache.NewMockDomainCache(ctrl) testAnalyzer := New(nil, nil, nil, nil, mockPinotClient, mockESConfig, mockPinotConfig, log.NewNoop(), tally.NoopScope, nil, mockDomainCache, nil) testWorkflow := &Workflow{analyzer: testAnalyzer} tests := map[string]struct { domainCacheAffordance func(mockDomainCache *cache.MockDomainCache) PinotClientAffordance func(mockPinotClient *pinot.MockGenericClient) expectedErr error }{ "Case0: success": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil).Times(3) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{ {"test-wf-type0", float64(100)}, {"test-wf-type1", float64(200)}, }, nil).Times(1) mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{ {"test-wf-version0", float64(10)}, {"test-wf-version1", float64(20)}, }, nil).Times(1) mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{ {"test-wf-version3", float64(10)}, {"test-wf-version4", float64(2)}, }, nil).Times(1) }, expectedErr: nil, }, "Case1: error getting domain": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(nil, fmt.Errorf("domain error")).Times(1) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) {}, expectedErr: fmt.Errorf("domain error"), }, "Case2: error Pinot query": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return(nil, fmt.Errorf("pinot error")).Times(1) }, expectedErr: fmt.Errorf("failed to query Pinot to find workflow type count Info: test-domain, error: pinot error"), }, "Case3: Aggregation is empty": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{}, nil).Times(1) }, expectedErr: fmt.Errorf("aggregation failed for domain in Pinot: test-domain"), }, "Case4: error parsing workflow count": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{ {"test", "invalid"}, }, nil).Times(1) }, expectedErr: fmt.Errorf("error parsing workflow count for workflow type test"), }, "Case5-1: failure case in queryWorkflowVersionsWithType: getWorkflowVersionPinotQuery error": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil).Times(1) mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(nil, fmt.Errorf("domain error")).Times(1) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{ {"test-wf-type0", float64(100)}, {"test-wf-type1", float64(200)}, }, nil).Times(1) }, expectedErr: fmt.Errorf("error querying workflow versions for workflow type: test-wf-type0: error: domain error"), }, "Case5-2: failure case in queryWorkflowVersionsWithType: SearchAggr error": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil).Times(2) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{ {"test-wf-type0", float64(100)}, {"test-wf-type1", float64(200)}, }, nil).Times(1) mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return(nil, fmt.Errorf("pinot error")).Times(1) }, expectedErr: fmt.Errorf("error querying workflow versions for workflow type: test-wf-type0: error: pinot error"), }, "Case5-3: failure case in queryWorkflowVersionsWithType: error parsing workflow count": { domainCacheAffordance: func(mockDomainCache *cache.MockDomainCache) { mockDomainCache.EXPECT().GetDomain(gomock.Any()).Return(cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{ID: "test-id"}, nil, false, nil, 0, nil, 0, 0, 0), nil).Times(2) }, PinotClientAffordance: func(mockPinotClient *pinot.MockGenericClient) { mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{ {"test-wf-type0", float64(100)}, {"test-wf-type1", float64(200)}, }, nil).Times(1) mockPinotClient.EXPECT().SearchAggr(gomock.Any()).Return([][]interface{}{ {"test-wf-version0", float64(3.14)}, {"test-wf-version1", 20}, }, nil).Times(1) }, expectedErr: fmt.Errorf("error querying workflow versions for workflow type: " + "test-wf-type0: error: error parsing workflow count for cadence version test-wf-version1"), }, } for name, test := range tests { t.Run(name, func(t *testing.T) { // Set up mocks test.domainCacheAffordance(mockDomainCache) test.PinotClientAffordance(mockPinotClient) err := testWorkflow.emitWorkflowVersionMetricsPinot("test-domain", zap.NewNop()) if err == nil { assert.Equal(t, test.expectedErr, err) } else { assert.Equal(t, test.expectedErr.Error(), err.Error()) } }) } } ================================================ FILE: service/worker/esanalyzer/domainWorkflowTypeCountWorkflow.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esanalyzer import ( "context" "encoding/json" "fmt" "time" "go.uber.org/cadence" "go.uber.org/cadence/activity" cclient "go.uber.org/cadence/client" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/common/pinot" ) const ( workflowTypeCountMetrics = "workflow_type_count" // workflow constants domainWFTypeCountWorkflowID = "cadence-sys-tl-esanalyzer-domain-wf-type-count" domainWFTypeCountWorkflowTypeName = "cadence-sys-es-analyzer-domain-wf-type-count-workflow" emitDomainWorkflowTypeCountMetricsActivity = "cadence-sys-es-analyzer-emit-domain-workflow-type-count-metrics" ) type ( DomainWorkflowTypeCount struct { WorkflowTypes []EsAggregateCount `json:"buckets"` } ) var ( workflowActivityOptions = workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: 5 * time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: 10 * time.Second, BackoffCoefficient: 1.7, MaximumInterval: 5 * time.Minute, ExpirationInterval: 10 * time.Minute, }, } domainWfTypeCountStartOptions = cclient.StartWorkflowOptions{ ID: domainWFTypeCountWorkflowID, TaskList: taskListName, ExecutionStartToCloseTimeout: 5 * time.Minute, CronSchedule: "*/5 * * * *", } ) func initDomainWorkflowTypeCountWorkflow(a *Analyzer) { w := Workflow{analyzer: a} workflow.RegisterWithOptions(w.emitWorkflowTypeCount, workflow.RegisterOptions{Name: domainWFTypeCountWorkflowTypeName}) activity.RegisterWithOptions( w.emitWorkflowTypeCountMetrics, activity.RegisterOptions{Name: emitDomainWorkflowTypeCountMetricsActivity}, ) } // emitWorkflowTypeCount queries ElasticSearch for workflow count per type and emit metrics func (w *Workflow) emitWorkflowTypeCount(ctx workflow.Context) error { if w.analyzer.config.ESAnalyzerPause() { logger := workflow.GetLogger(ctx) logger.Info("Skipping ESAnalyzer execution cycle since it was paused") return nil } var err error err = workflow.ExecuteActivity( workflow.WithActivityOptions(ctx, workflowActivityOptions), emitDomainWorkflowTypeCountMetricsActivity, ).Get(ctx, &err) if err != nil { return err } return nil } // get open workflows count per workflow type for specified domain func (w *Workflow) getDomainWorkflowTypeCountQuery(domainName string) (string, error) { domain, err := w.analyzer.domainCache.GetDomain(domainName) if err != nil { return "", err } // exclude uninitialized workflow executions by checking whether record has start time field return fmt.Sprintf(` { "aggs" : { "wftypes" : { "terms" : { "field" : "WorkflowType"} } }, "query": { "bool": { "must_not": { "exists": { "field": "CloseTime" } }, "must": [ { "match" : { "DomainID" : "%s" } }, { "exists": { "field": "StartTime" } } ] } }, "size": 0 } `, domain.GetInfo().ID), nil } // emitWorkflowTypeCountMetrics is an activity that emits the running workflow type counts of a domain // it will switch between ES and Pinot based on the readMode func (w *Workflow) emitWorkflowTypeCountMetrics(ctx context.Context) error { logger := activity.GetLogger(ctx) var workflowMetricDomainNames []string workflowMetricDomains := w.analyzer.config.ESAnalyzerWorkflowTypeDomains() if len(workflowMetricDomains) > 0 { err := json.Unmarshal([]byte(workflowMetricDomains), &workflowMetricDomainNames) if err != nil { return err } var failedDomains []string for _, domainName := range workflowMetricDomainNames { switch w.analyzer.readMode { case ES: err = w.emitWorkflowTypeCountMetricsES(ctx, domainName, logger) case Pinot: err = w.emitWorkflowTypeCountMetricsPinot(domainName, logger) default: err = w.emitWorkflowTypeCountMetricsES(ctx, domainName, logger) } if err != nil { logger.Error(fmt.Sprintf("failed to emit workflow type metrics for domain %s", domainName), zap.Error(err)) failedDomains = append(failedDomains, domainName) } } if len(failedDomains) == len(workflowMetricDomainNames) { return fmt.Errorf("failed to emit workflow type metrics for all domains") } } return nil } func (w *Workflow) getDomainWorkflowTypeCountPinotQuery(domainName string) (string, error) { domain, err := w.analyzer.domainCache.GetDomain(domainName) if err != nil { return "", err } // exclude uninitialized workflow executions by checking whether record has start time field // there's a "LIMIT 10" because in ES, Aggr clause by default returns the top 10 results return fmt.Sprintf(` SELECT WorkflowType, COUNT(*) AS count FROM %s WHERE DomainID = '%s' AND CloseStatus = -1 AND StartTime > 0 GROUP BY WorkflowType ORDER BY count LIMIT 10 `, w.analyzer.pinotTableName, domain.GetInfo().ID), nil } func (w *Workflow) emitWorkflowTypeCountMetricsPinot(domainName string, logger *zap.Logger) error { wfTypeCountPinotQuery, err := w.getDomainWorkflowTypeCountPinotQuery(domainName) if err != nil { logger.Error("Failed to get Pinot query to find domain workflow type Info", zap.Error(err), zap.String("DomainName", domainName), ) return err } response, err := w.analyzer.pinotClient.SearchAggr(&pinot.SearchRequest{Query: wfTypeCountPinotQuery}) if err != nil { logger.Error("Failed to query Pinot to find workflow type count Info", zap.Error(err), zap.String("VisibilityQuery", wfTypeCountPinotQuery), zap.String("DomainName", domainName), ) return err } foundAggregation := len(response) > 0 if !foundAggregation { logger.Error("Pinot error: aggregation failed.", zap.Error(err), zap.String("Aggregation", fmt.Sprintf("%v", response)), zap.String("DomainName", domainName), zap.String("VisibilityQuery", wfTypeCountPinotQuery), ) return fmt.Errorf("aggregation failed for domain in Pinot: %s", domainName) } var domainWorkflowTypeCount DomainWorkflowTypeCount for _, row := range response { workflowType := row[0].(string) // even though the count is a int, it is returned as a float64, need to pay attention to this workflowCount, ok := row[1].(float64) if !ok { logger.Error("Error parsing workflow count", zap.Error(err), zap.String("WorkflowType", workflowType), zap.String("DomainName", domainName), zap.Float64("WorkflowCount", workflowCount), zap.String("WorkflowCountType", fmt.Sprintf("%T", row[1])), zap.String("raw data", fmt.Sprintf("%#v", response)), ) return fmt.Errorf("error parsing workflow count for workflow type %s", workflowType) } domainWorkflowTypeCount.WorkflowTypes = append(domainWorkflowTypeCount.WorkflowTypes, EsAggregateCount{ AggregateKey: workflowType, AggregateCount: int64(workflowCount), }) } for _, workflowType := range domainWorkflowTypeCount.WorkflowTypes { w.analyzer.tallyScope.Tagged( map[string]string{domainTag: domainName, workflowTypeTag: workflowType.AggregateKey}, ).Gauge(workflowTypeCountMetrics).Update(float64(workflowType.AggregateCount)) } return nil } func (w *Workflow) emitWorkflowTypeCountMetricsES(ctx context.Context, domainName string, logger *zap.Logger) error { wfTypeCountEsQuery, err := w.getDomainWorkflowTypeCountQuery(domainName) if err != nil { logger.Error("Failed to get ElasticSearch query to find domain workflow type Info", zap.Error(err), zap.String("DomainName", domainName), ) return err } response, err := w.analyzer.esClient.SearchRaw(ctx, w.analyzer.visibilityIndexName, wfTypeCountEsQuery) if err != nil { logger.Error("Failed to query ElasticSearch to find workflow type count Info", zap.Error(err), zap.String("VisibilityQuery", wfTypeCountEsQuery), zap.String("DomainName", domainName), ) return err } agg, foundAggregation := response.Aggregations[workflowTypesAggKey] if !foundAggregation { logger.Error("ElasticSearch error: aggregation failed.", zap.Error(err), zap.String("Aggregation", string(agg)), zap.String("DomainName", domainName), zap.String("VisibilityQuery", wfTypeCountEsQuery), ) return fmt.Errorf("aggregation failed for domain in ES: %s", domainName) } var domainWorkflowTypeCount DomainWorkflowTypeCount err = json.Unmarshal(agg, &domainWorkflowTypeCount) if err != nil { logger.Error("ElasticSearch error parsing aggregation.", zap.Error(err), zap.String("Aggregation", string(agg)), zap.String("DomainName", domainName), zap.String("VisibilityQuery", wfTypeCountEsQuery), ) return err } for _, workflowType := range domainWorkflowTypeCount.WorkflowTypes { w.analyzer.tallyScope.Tagged( map[string]string{domainTag: domainName, workflowTypeTag: workflowType.AggregateKey}, ).Gauge(workflowTypeCountMetrics).Update(float64(workflowType.AggregateCount)) } return nil } ================================================ FILE: service/worker/esanalyzer/workflow.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package esanalyzer import ( "context" "encoding/json" "fmt" "time" "go.uber.org/cadence" "go.uber.org/cadence/activity" cclient "go.uber.org/cadence/client" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/common/pinot" ) const ( workflowTypesAggKey = "wftypes" domainTag = "domain" workflowVersionTag = "workflowVersion" workflowVersionCountMetrics = "workflow_version_count" workflowTypeTag = "workflowType" // workflow constants esAnalyzerWFID = "cadence-sys-tl-esanalyzer" esanalyzerWFTypeName = "cadence-sys-es-analyzer-workflow" emitWorkflowVersionMetricsActivity = "cadence-sys-es-analyzer-emit-workflow-version-metrics" ) type ( DomainWorkflowVersionCount struct { WorkflowTypes []WorkflowTypeCount `json:"buckets"` } WorkflowTypeCount struct { EsAggregateCount WorkflowVersions WorkflowVersionCount `json:"versions"` } WorkflowVersionCount struct { WorkflowVersions []EsAggregateCount `json:"buckets"` } ) var ( retryPolicy = cadence.RetryPolicy{ InitialInterval: 10 * time.Second, BackoffCoefficient: 1.7, MaximumInterval: 5 * time.Minute, ExpirationInterval: 10 * time.Minute, } getWorkflowMetricsOptions = workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: 5 * time.Minute, RetryPolicy: &retryPolicy, } wfOptions = cclient.StartWorkflowOptions{ ID: esAnalyzerWFID, TaskList: taskListName, ExecutionStartToCloseTimeout: 10 * time.Minute, CronSchedule: "*/10 * * * *", } ) func initWorkflow(a *Analyzer) { w := Workflow{analyzer: a} workflow.RegisterWithOptions(w.workflowFunc, workflow.RegisterOptions{Name: esanalyzerWFTypeName}) activity.RegisterWithOptions( w.emitWorkflowVersionMetrics, activity.RegisterOptions{Name: emitWorkflowVersionMetricsActivity}, ) } // workflowFunc queries ElasticSearch for information and do something with it func (w *Workflow) workflowFunc(ctx workflow.Context) error { if w.analyzer.config.ESAnalyzerPause() { logger := workflow.GetLogger(ctx) logger.Info("Skipping ESAnalyzer execution cycle since it was paused") return nil } var err error err = workflow.ExecuteActivity( workflow.WithActivityOptions(ctx, getWorkflowMetricsOptions), emitWorkflowVersionMetricsActivity, ).Get(ctx, &err) if err != nil { return err } return nil } func (w *Workflow) getWorkflowVersionQuery(domainName string) (string, error) { domain, err := w.analyzer.domainCache.GetDomain(domainName) if err != nil { return "", err } return fmt.Sprintf(` { "aggs" : { "wftypes" : { "terms" : { "field" : "WorkflowType"}, "aggs": { "versions": { "terms" : { "field" : "Attr.CadenceChangeVersion"} } } } }, "query": { "bool": { "must_not": { "exists": { "field": "CloseTime" } }, "must": [ { "match" : { "DomainID" : "%s" } } ] } }, "size": 0 } `, domain.GetInfo().ID), nil } func (w *Workflow) getWorkflowTypePinotQuery(domainName string) (string, error) { domain, err := w.analyzer.domainCache.GetDomain(domainName) if err != nil { return "", err } // exclude uninitialized workflow executions by checking whether record has start time field // there's a "LIMIT 10" because in ES, Aggr clause by default returns the top 10 results return fmt.Sprintf(` SELECT WorkflowType, COUNT(*) AS count FROM %s WHERE DomainID = '%s' AND CloseStatus = -1 AND StartTime > 0 GROUP BY WorkflowType ORDER BY count DESC LIMIT 10 `, w.analyzer.pinotTableName, domain.GetInfo().ID), nil } func (w *Workflow) getWorkflowVersionPinotQuery(domainName string, wfType string) (string, error) { domain, err := w.analyzer.domainCache.GetDomain(domainName) if err != nil { return "", err } // exclude uninitialized workflow executions by checking whether record has start time field // there's a "LIMIT 10" because in ES, Aggr clause by default returns the top 10 results return fmt.Sprintf(` SELECT JSON_EXTRACT_SCALAR(Attr, '$.CadenceChangeVersion', 'STRING_ARRAY') AS CadenceChangeVersion, COUNT(*) AS count FROM %s WHERE DomainID = '%s' AND CloseStatus = -1 AND StartTime > 0 AND WorkflowType = '%s' GROUP BY JSON_EXTRACT_SCALAR(Attr, '$.CadenceChangeVersion', 'STRING_ARRAY') ORDER BY count DESC LIMIT 10 `, w.analyzer.pinotTableName, domain.GetInfo().ID, wfType), nil } // emitWorkflowVersionMetrics is an activity that emits the running WF versions of a domain func (w *Workflow) emitWorkflowVersionMetrics(ctx context.Context) error { logger := activity.GetLogger(ctx) var workflowMetricDomainNames []string workflowMetricDomains := w.analyzer.config.ESAnalyzerWorkflowVersionDomains() if len(workflowMetricDomains) > 0 { err := json.Unmarshal([]byte(workflowMetricDomains), &workflowMetricDomainNames) if err != nil { return err } var failedDomains []string for _, domainName := range workflowMetricDomainNames { switch w.analyzer.readMode { case ES: err = w.emitWorkflowVersionMetricsES(ctx, domainName, logger) case Pinot: err = w.emitWorkflowVersionMetricsPinot(domainName, logger) default: err = w.emitWorkflowVersionMetricsES(ctx, domainName, logger) } if err != nil { logger.Error(fmt.Sprintf("failed to emit workflow version metrics for domain %s", domainName), zap.Error(err)) failedDomains = append(failedDomains, domainName) } } if len(failedDomains) == len(workflowMetricDomainNames) { return fmt.Errorf("failed to emit workflow version metrics for all domains") } } return nil } func (w *Workflow) emitWorkflowVersionMetricsPinot(domainName string, logger *zap.Logger) error { wfVersionPinotQuery, err := w.getWorkflowTypePinotQuery(domainName) if err != nil { logger.Error("Failed to get Pinot query to find workflow type Info", zap.Error(err), zap.String("DomainName", domainName), ) return err } response, err := w.analyzer.pinotClient.SearchAggr(&pinot.SearchRequest{Query: wfVersionPinotQuery}) if err != nil { logger.Error("Failed to query Pinot to find workflow type count Info", zap.Error(err), zap.String("VisibilityQuery", wfVersionPinotQuery), zap.String("DomainName", domainName), ) return fmt.Errorf("failed to query Pinot to find workflow type count Info: %s, error: %s", domainName, err.Error()) } foundAggregation := len(response) > 0 if !foundAggregation { logger.Error("Pinot error: aggregation failed.", zap.Error(err), zap.String("Aggregation", fmt.Sprintf("%v", response)), zap.String("DomainName", domainName), zap.String("VisibilityQuery", wfVersionPinotQuery), ) return fmt.Errorf("aggregation failed for domain in Pinot: %s", domainName) } var domainWorkflowVersionCount DomainWorkflowVersionCount for _, row := range response { workflowType := row[0].(string) workflowCount, ok := row[1].(float64) if !ok { logger.Error("error parsing workflow count for cadence version", zap.Error(err), zap.String("WorkflowType", workflowType), zap.String("DomainName", domainName), zap.Float64("WorkflowCount", workflowCount), zap.String("WorkflowCountType", fmt.Sprintf("%T", row[1])), zap.String("raw data", fmt.Sprintf("%#v", response)), ) return fmt.Errorf("error parsing workflow count for workflow type %s", workflowType) } workflowVersions, err := w.queryWorkflowVersionsWithType(domainName, workflowType, logger) if err != nil { logger.Error("Error querying workflow versions", zap.Error(err), zap.String("WorkflowType", workflowType), zap.String("DomainName", domainName), ) return fmt.Errorf("error querying workflow versions for workflow type: %s: error: %s", workflowType, err.Error()) } domainWorkflowVersionCount.WorkflowTypes = append(domainWorkflowVersionCount.WorkflowTypes, WorkflowTypeCount{ EsAggregateCount: EsAggregateCount{ AggregateKey: workflowType, AggregateCount: int64(workflowCount), }, WorkflowVersions: workflowVersions, }) } for _, workflowType := range domainWorkflowVersionCount.WorkflowTypes { for _, workflowVersion := range workflowType.WorkflowVersions.WorkflowVersions { w.analyzer.tallyScope.Tagged( map[string]string{domainTag: domainName, workflowVersionTag: workflowVersion.AggregateKey, workflowTypeTag: workflowType.AggregateKey}, ).Gauge(workflowVersionCountMetrics).Update(float64(workflowVersion.AggregateCount)) } } return nil } func (w *Workflow) queryWorkflowVersionsWithType(domainName string, wfType string, logger *zap.Logger) (WorkflowVersionCount, error) { wfVersionPinotQuery, err := w.getWorkflowVersionPinotQuery(domainName, wfType) if err != nil { logger.Error("Failed to get Pinot query to find workflow version Info", zap.Error(err), zap.String("DomainName", domainName), ) return WorkflowVersionCount{}, err } response, err := w.analyzer.pinotClient.SearchAggr(&pinot.SearchRequest{Query: wfVersionPinotQuery}) if err != nil { logger.Error("Failed to query Pinot to find workflow type count Info", zap.Error(err), zap.String("VisibilityQuery", wfVersionPinotQuery), zap.String("DomainName", domainName), ) return WorkflowVersionCount{}, err } foundAggregation := len(response) > 0 // if no CadenceChangeVersion is found, return an empty WorkflowVersionCount, no errors if !foundAggregation { return WorkflowVersionCount{}, nil } var workflowVersions WorkflowVersionCount for _, row := range response { workflowVersion := row[0].(string) workflowCount, ok := row[1].(float64) if !ok { logger.Error("error parsing workflow count for cadence version", zap.Error(err), zap.String("WorkflowVersion", workflowVersion), zap.String("DomainName", domainName), zap.Float64("WorkflowCount", workflowCount), zap.String("WorkflowCountType", fmt.Sprintf("%T", row[1])), zap.String("raw data", fmt.Sprintf("%#v", response)), ) return WorkflowVersionCount{}, fmt.Errorf("error parsing workflow count for cadence version %s", workflowVersion) } workflowVersions.WorkflowVersions = append(workflowVersions.WorkflowVersions, EsAggregateCount{ AggregateKey: workflowVersion, AggregateCount: int64(workflowCount), }) } return workflowVersions, nil } func (w *Workflow) emitWorkflowVersionMetricsES(ctx context.Context, domainName string, logger *zap.Logger) error { wfVersionEsQuery, err := w.getWorkflowVersionQuery(domainName) if err != nil { logger.Error("Failed to get ElasticSearch query to find workflow version Info", zap.Error(err), zap.String("DomainName", domainName), ) return err } response, err := w.analyzer.esClient.SearchRaw(ctx, w.analyzer.visibilityIndexName, wfVersionEsQuery) if err != nil { logger.Error("Failed to query ElasticSearch to find workflow version Info", zap.Error(err), zap.String("VisibilityQuery", wfVersionEsQuery), zap.String("DomainName", domainName), ) return err } agg, foundAggregation := response.Aggregations[workflowTypesAggKey] if !foundAggregation { logger.Error("ElasticSearch error: aggregation failed.", zap.Error(err), zap.String("Aggregation", string(agg)), zap.String("DomainName", domainName), zap.String("VisibilityQuery", wfVersionEsQuery), ) return fmt.Errorf("aggregation failed for domain in ES: %s", domainName) } var domainWorkflowVersionCount DomainWorkflowVersionCount err = json.Unmarshal(agg, &domainWorkflowVersionCount) if err != nil { logger.Error("ElasticSearch error parsing aggregation.", zap.Error(err), zap.String("Aggregation", string(agg)), zap.String("DomainName", domainName), zap.String("VisibilityQuery", wfVersionEsQuery), ) return err } for _, workflowType := range domainWorkflowVersionCount.WorkflowTypes { for _, workflowVersion := range workflowType.WorkflowVersions.WorkflowVersions { w.analyzer.tallyScope.Tagged( map[string]string{domainTag: domainName, workflowVersionTag: workflowVersion.AggregateKey, workflowTypeTag: workflowType.AggregateKey}, ).Gauge(workflowVersionCountMetrics).Update(float64(workflowVersion.AggregateCount)) } } return nil } ================================================ FILE: service/worker/failovermanager/rebalance_workflow.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package failovermanager import ( "context" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) type ( // RebalanceParams contains parameters for rebalance workflow RebalanceParams struct { // BatchFailoverSize is number of domains to failover in one batch BatchFailoverSize int // BatchFailoverWaitTimeInSeconds is the waiting time between batch failover BatchFailoverWaitTimeInSeconds int } // RebalanceResult contains the result from the rebalance workflow RebalanceResult struct { SuccessDomains []string FailedDomains []string } // DomainRebalanceData contains the result from getRebalanceDomains activity DomainRebalanceData struct { DomainName string PreferredCluster string } ) // RebalanceWorkflow is to rebalance domains across clusters based on rebalance policy. func RebalanceWorkflow(ctx workflow.Context, params *RebalanceParams) (*RebalanceResult, error) { // get rebalance domains ao := workflow.WithActivityOptions(ctx, getGetDomainsActivityOptions()) var domainData []*DomainRebalanceData err := workflow.ExecuteActivity(ao, getRebalanceDomainsActivityName).Get(ctx, &domainData) if err != nil { return nil, err } domainPerCluster := getDomainsByCluster(domainData) result := &RebalanceResult{ SuccessDomains: []string{}, FailedDomains: []string{}, } // failover domains for rebalance for cluster, domains := range domainPerCluster { failoverParams := &FailoverParams{ TargetCluster: cluster, BatchFailoverSize: params.BatchFailoverSize, BatchFailoverWaitTimeInSeconds: params.BatchFailoverWaitTimeInSeconds, } successDomains, failedDomains := failoverDomainsByBatch( ctx, domains, failoverParams, func() {}, false, ) result.SuccessDomains = append(result.SuccessDomains, successDomains...) result.FailedDomains = append(result.FailedDomains, failedDomains...) } return result, nil } // GetDomainsForRebalanceActivity activity fetch domains for rebalance func GetDomainsForRebalanceActivity(ctx context.Context) ([]*DomainRebalanceData, error) { domains, err := getAllDomains(ctx, nil) if err != nil { return nil, err } var res []*DomainRebalanceData for _, domain := range domains { if shouldAllowRebalance(domain) { domainName := domain.GetDomainInfo().GetName() res = append(res, &DomainRebalanceData{ DomainName: domainName, PreferredCluster: getPreferredClusterName(domain), }) } } return res, nil } func getPreferredClusterName(domain *types.DescribeDomainResponse) string { return domain.GetDomainInfo().GetData()[constants.DomainDataKeyForPreferredCluster] } func shouldAllowRebalance(domain *types.DescribeDomainResponse) bool { preferredCluster := getPreferredClusterName(domain) return isDomainFailoverManagedByCadence(domain) && domain.IsGlobalDomain && domain.GetDomainInfo().GetStatus() == types.DomainStatusRegistered && len(preferredCluster) != 0 && preferredCluster != domain.ReplicationConfiguration.GetActiveClusterName() && isPreferredClusterInClusterListForDomain(preferredCluster, domain) } func getDomainsByCluster(domainData []*DomainRebalanceData) map[string][]string { domainPerCluster := make(map[string][]string) for _, domain := range domainData { domainPerCluster[domain.PreferredCluster] = append(domainPerCluster[domain.PreferredCluster], domain.DomainName) } return domainPerCluster } func isPreferredClusterInClusterListForDomain(preferredCluster string, domain *types.DescribeDomainResponse) bool { for _, cluster := range domain.ReplicationConfiguration.Clusters { if cluster.ClusterName == preferredCluster { return true } } return false } ================================================ FILE: service/worker/failovermanager/rebalance_workflow_test.go ================================================ // Copyright (c) 2017-2021 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package failovermanager import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/activity" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" ) type rebalanceWorkflowTestSuite struct { suite.Suite testsuite.WorkflowTestSuite activityEnv *testsuite.TestActivityEnvironment workflowEnv *testsuite.TestWorkflowEnvironment } func TestRebalanceWorkflowTestSuite(t *testing.T) { suite.Run(t, new(rebalanceWorkflowTestSuite)) } func (s *rebalanceWorkflowTestSuite) SetupTest() { s.activityEnv = s.NewTestActivityEnvironment() s.workflowEnv = s.NewTestWorkflowEnvironment() s.workflowEnv.RegisterWorkflowWithOptions(RebalanceWorkflow, workflow.RegisterOptions{Name: RebalanceWorkflowTypeName}) s.workflowEnv.RegisterActivityWithOptions(FailoverActivity, activity.RegisterOptions{Name: failoverActivityName}) s.workflowEnv.RegisterActivityWithOptions(GetDomainsForRebalanceActivity, activity.RegisterOptions{Name: getRebalanceDomainsActivityName}) s.activityEnv.RegisterActivityWithOptions(FailoverActivity, activity.RegisterOptions{Name: failoverActivityName}) s.activityEnv.RegisterActivityWithOptions(GetDomainsForRebalanceActivity, activity.RegisterOptions{Name: getRebalanceDomainsActivityName}) } func (s *rebalanceWorkflowTestSuite) TearDownTest() { s.workflowEnv.AssertExpectations(s.T()) } func (s *rebalanceWorkflowTestSuite) TestGetDomainsForRebalanceActivity_ReturnOne() { actEnv, mockResource := s.prepareTestActivityEnv() domains := &types.ListDomainsResponse{ Domains: []*types.DescribeDomainResponse{ { DomainInfo: &types.DomainInfo{ Name: "d1", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", constants.DomainDataKeyForPreferredCluster: "c2", }, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, { DomainInfo: &types.DomainInfo{ Name: "d2", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "false", constants.DomainDataKeyForPreferredCluster: "c2", }, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, { DomainInfo: &types.DomainInfo{ Name: "d3", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", constants.DomainDataKeyForPreferredCluster: "c1", }, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, { DomainInfo: &types.DomainInfo{ Name: "d4", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "false", }, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, { DomainInfo: &types.DomainInfo{ Name: "d5", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", }, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, }, } mockResource.FrontendClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(domains, nil) actFuture, err := actEnv.ExecuteActivity(GetDomainsForRebalanceActivity) s.NoError(err) var domainData []*DomainRebalanceData err = actFuture.Get(&domainData) s.NoError(err) s.Equal(1, len(domainData)) } func (s *rebalanceWorkflowTestSuite) TestGetDomainsForRebalanceActivity_Error() { actEnv, mockResource := s.prepareTestActivityEnv() mockResource.FrontendClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()). Return(nil, fmt.Errorf("test")) _, err := actEnv.ExecuteActivity(GetDomainsForRebalanceActivity) s.Error(err) } func (s *rebalanceWorkflowTestSuite) TestWorkflow_Success() { params := &RebalanceParams{ BatchFailoverWaitTimeInSeconds: 10, BatchFailoverSize: 10, } domainData := []*DomainRebalanceData{ { DomainName: "d1", PreferredCluster: "c1", }, { DomainName: "d2", PreferredCluster: "c1", }, { DomainName: "d3", PreferredCluster: "c2", }, } s.workflowEnv.OnActivity(getRebalanceDomainsActivityName, mock.Anything).Return(domainData, nil) failoverActivityParams1 := &FailoverActivityParams{ Domains: []string{"d1", "d2"}, TargetCluster: "c1", } failoverActivityResult1 := &FailoverActivityResult{ SuccessDomains: []string{"d1", "d2"}, } failoverActivityParams2 := &FailoverActivityParams{ Domains: []string{"d3"}, TargetCluster: "c2", } failoverActivityResult2 := &FailoverActivityResult{ SuccessDomains: []string{"d3"}, } s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, failoverActivityParams1).Return(failoverActivityResult1, nil).Times(1) s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, failoverActivityParams2).Return(failoverActivityResult2, nil).Times(1) s.workflowEnv.ExecuteWorkflow(RebalanceWorkflowTypeName, params) var result RebalanceResult err := s.workflowEnv.GetWorkflowResult(&result) s.NoError(err) s.Equal(3, len(result.SuccessDomains)) s.Equal(0, len(result.FailedDomains)) } func (s *rebalanceWorkflowTestSuite) TestWorkflow_HalfFailoverActivityError_NoWorkflowError() { params := &RebalanceParams{ BatchFailoverWaitTimeInSeconds: 10, BatchFailoverSize: 10, } domainData := []*DomainRebalanceData{ { DomainName: "d1", PreferredCluster: "c1", }, { DomainName: "d2", PreferredCluster: "c1", }, { DomainName: "d3", PreferredCluster: "c2", }, } s.workflowEnv.OnActivity(getRebalanceDomainsActivityName, mock.Anything).Return(domainData, nil) failoverActivityParams1 := &FailoverActivityParams{ Domains: []string{"d1", "d2"}, TargetCluster: "c1", } failoverActivityResult1 := &FailoverActivityResult{ SuccessDomains: []string{"d1", "d2"}, } failoverActivityParams2 := &FailoverActivityParams{ Domains: []string{"d3"}, TargetCluster: "c2", } s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, failoverActivityParams1).Return(failoverActivityResult1, nil).Times(1) s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, failoverActivityParams2). Return(nil, fmt.Errorf("test")).Times(1) s.workflowEnv.ExecuteWorkflow(RebalanceWorkflowTypeName, params) var result RebalanceResult err := s.workflowEnv.GetWorkflowResult(&result) s.NoError(err) s.Equal(2, len(result.SuccessDomains)) s.Equal(1, len(result.FailedDomains)) } func (s *rebalanceWorkflowTestSuite) TestWorkflow_FailoverActivityError_NoWorkflowError() { params := &RebalanceParams{ BatchFailoverWaitTimeInSeconds: 10, BatchFailoverSize: 10, } domainData := []*DomainRebalanceData{ { DomainName: "d1", PreferredCluster: "c1", }, { DomainName: "d2", PreferredCluster: "c1", }, { DomainName: "d3", PreferredCluster: "c2", }, } s.workflowEnv.OnActivity(getRebalanceDomainsActivityName, mock.Anything).Return(domainData, nil) s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("test")) s.workflowEnv.ExecuteWorkflow(RebalanceWorkflowTypeName, params) var result RebalanceResult err := s.workflowEnv.GetWorkflowResult(&result) s.NoError(err) s.Equal(0, len(result.SuccessDomains)) s.Equal(3, len(result.FailedDomains)) } func (s *rebalanceWorkflowTestSuite) TestWorkflow_GetRebalanceDomainsActivityError_WorkflowError() { params := &RebalanceParams{ BatchFailoverWaitTimeInSeconds: 10, BatchFailoverSize: 10, } s.workflowEnv.OnActivity(getRebalanceDomainsActivityName, mock.Anything).Return(nil, fmt.Errorf("test")) s.workflowEnv.ExecuteWorkflow(RebalanceWorkflowTypeName, params) var result RebalanceResult err := s.workflowEnv.GetWorkflowResult(&result) s.Error(err) } func (s *rebalanceWorkflowTestSuite) TestShouldAllowRebalance() { testCases := []struct { name string domain *types.DescribeDomainResponse expect bool }{ { name: "allow rebalance", domain: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "d1", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", constants.DomainDataKeyForPreferredCluster: "c2", }, Status: types.DomainStatusRegistered.Ptr(), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, expect: true, }, { name: "not allow rebalance because domain failover is not managed by cadence", domain: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "d1", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "false", constants.DomainDataKeyForPreferredCluster: "c2", }, Status: types.DomainStatusRegistered.Ptr(), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, expect: false, }, { name: "not allow rebalance because domain is not global domain", domain: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "d1", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", constants.DomainDataKeyForPreferredCluster: "c2", }, Status: types.DomainStatusRegistered.Ptr(), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: false, }, expect: false, }, { name: "not allow rebalance because domain status is not registered", domain: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "d1", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", constants.DomainDataKeyForPreferredCluster: "c2", }, Status: types.DomainStatusDeprecated.Ptr(), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, }, { name: "not allow rebalance because domain has no preferred cluster", domain: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "d1", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", }, Status: types.DomainStatusRegistered.Ptr(), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, }, { name: "not allow rebalance because preferred cluster is the same as active cluster", domain: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "d1", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", constants.DomainDataKeyForPreferredCluster: "c1", }, Status: types.DomainStatusRegistered.Ptr(), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, }, { name: "not allow rebalance because preferred cluster is not in cluster list", domain: &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "d1", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", constants.DomainDataKeyForPreferredCluster: "c3", }, Status: types.DomainStatusRegistered.Ptr(), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { s.Equal(tc.expect, shouldAllowRebalance(tc.domain)) }) } } func (s *rebalanceWorkflowTestSuite) prepareTestActivityEnv() (*testsuite.TestActivityEnvironment, *resource.Test) { controller := gomock.NewController(s.T()) mockResource := resource.NewTest(s.T(), controller, metrics.Worker) ctx := &FailoverManager{ svcClient: mockResource.GetSDKClient(), clientBean: mockResource.ClientBean, } s.activityEnv.SetTestTimeout(time.Second * 5) s.activityEnv.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), failoverManagerContextKey, ctx), }) s.T().Cleanup(func() { mockResource.Finish(s.T()) }) return s.activityEnv, mockResource } ================================================ FILE: service/worker/failovermanager/starter.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package failovermanager import ( "context" "github.com/opentracing/opentracing-go" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/activity" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "github.com/uber/cadence/client" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type ( // Config defines the configuration for failover Config struct { AdminOperationToken dynamicproperties.StringPropertyFn // ClusterMetadata contains the metadata for this cluster ClusterMetadata cluster.Metadata } // BootstrapParams contains the set of params needed to bootstrap // failover manager BootstrapParams struct { // Config contains the configuration for scanner Config Config // ServiceClient is an instance of cadence service client ServiceClient workflowserviceclient.Interface // MetricsClient is an instance of metrics object for emitting stats MetricsClient metrics.Client Logger log.Logger // TallyScope is an instance of tally metrics scope TallyScope tally.Scope // ClientBean is an instance of client.Bean for a collection of clients ClientBean client.Bean } // FailoverManager of cadence worker service FailoverManager struct { cfg Config svcClient workflowserviceclient.Interface clientBean client.Bean metricsClient metrics.Client tallyScope tally.Scope logger log.Logger worker worker.Worker } ) // New returns a new instance of FailoverManager func New(params *BootstrapParams) *FailoverManager { return &FailoverManager{ cfg: params.Config, svcClient: params.ServiceClient, metricsClient: params.MetricsClient, tallyScope: params.TallyScope, logger: params.Logger.WithTags(tag.ComponentBatcher), clientBean: params.ClientBean, } } // Start starts the worker func (s *FailoverManager) Start() error { ctx := context.WithValue(context.Background(), failoverManagerContextKey, s) workerOpts := worker.Options{ MetricsScope: s.tallyScope, BackgroundActivityContext: ctx, Tracer: opentracing.GlobalTracer(), } failoverWorker := worker.New(s.svcClient, constants.SystemLocalDomainName, TaskListName, workerOpts) failoverWorker.RegisterWorkflowWithOptions(FailoverWorkflow, workflow.RegisterOptions{Name: FailoverWorkflowTypeName}) failoverWorker.RegisterWorkflowWithOptions(RebalanceWorkflow, workflow.RegisterOptions{Name: RebalanceWorkflowTypeName}) failoverWorker.RegisterActivityWithOptions(FailoverActivity, activity.RegisterOptions{Name: failoverActivityName}) failoverWorker.RegisterActivityWithOptions(GetDomainsActivity, activity.RegisterOptions{Name: getDomainsActivityName}) failoverWorker.RegisterActivityWithOptions(GetDomainsForRebalanceActivity, activity.RegisterOptions{Name: getRebalanceDomainsActivityName}) s.worker = failoverWorker return failoverWorker.Start() } // Stop stops the worker func (s *FailoverManager) Stop() { s.worker.Stop() } ================================================ FILE: service/worker/failovermanager/workflow.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package failovermanager import ( "context" "encoding/json" "errors" "fmt" "strings" "time" "go.uber.org/cadence" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" ) type ( contextKey string ) const ( failoverManagerContextKey contextKey = "failoverManagerContext" // TaskListName tasklist TaskListName = "cadence-sys-failoverManager-tasklist" // FailoverWorkflowTypeName workflow type name FailoverWorkflowTypeName = "cadence-sys-failoverManager-workflow" // RebalanceWorkflowTypeName is rebalance workflow type name RebalanceWorkflowTypeName = "cadence-sys-rebalance-workflow" // WorkflowID will be reused to ensure only one workflow running FailoverWorkflowID = "cadence-failover-manager" RebalanceWorkflowID = "cadence-rebalance-workflow" DrillWorkflowID = FailoverWorkflowID + "-drill" failoverActivityName = "cadence-sys-failover-activity" getDomainsActivityName = "cadence-sys-getDomains-activity" getRebalanceDomainsActivityName = "cadence-sys-getRebalanceDomains-activity" defaultBatchFailoverSize = 20 defaultBatchFailoverWaitTimeInSeconds = 30 errMsgParamsIsNil = "params is nil" errMsgTargetClusterIsEmpty = "targetCluster is empty" errMsgSourceClusterIsEmpty = "sourceCluster is empty" errMsgTargetClusterIsSameAsSource = "targetCluster is same as sourceCluster" // QueryType for failover workflow QueryType = "state" // PauseSignal signal name for pause PauseSignal = "pause" // ResumeSignal signal name for resume ResumeSignal = "resume" // workflow states for query // WorkflowInitialized state WorkflowInitialized = "initialized" // WorkflowRunning state WorkflowRunning = "running" // WorkflowPaused state WorkflowPaused = "paused" // WorkflowCompleted state WorkflowCompleted = "complete" // WorkflowAborted state WorkflowAborted = "aborted" unknownOperator = "unknown" ) type ( // FailoverParams is the arg for failoverWorkflow FailoverParams struct { // TargetCluster is the destination of failover TargetCluster string // SourceCluster is from which cluster the domains are active before failover SourceCluster string // BatchFailoverSize is number of domains to failover in one batch BatchFailoverSize int // BatchFailoverWaitTimeInSeconds is the waiting time between batch failover BatchFailoverWaitTimeInSeconds int // Domains candidates to be failover Domains []string // DrillWaitTime defines the wait time of a failover drill DrillWaitTime time.Duration // GracefulFailoverTimeoutInSeconds GracefulFailoverTimeoutInSeconds *int32 } // FailoverResult is workflow result FailoverResult struct { SuccessDomains []string FailedDomains []string SuccessResetDomains []string FailedResetDomains []string } // GetDomainsActivityParams params for activity GetDomainsActivityParams struct { TargetCluster string SourceCluster string Domains []string } // FailoverActivityParams params for activity FailoverActivityParams struct { Domains []string TargetCluster string GracefulFailoverTimeoutInSeconds *int32 } // FailoverActivityResult result for failover activity FailoverActivityResult struct { SuccessDomains []string FailedDomains []string } // QueryResult for failover progress QueryResult struct { TotalDomains int Success int Failed int State string TargetCluster string SourceCluster string SuccessDomains []string // SuccessDomains are guaranteed succeed processed FailedDomains []string // FailedDomains contains false positive SuccessResetDomains []string // SuccessResetDomains are domains successfully reset in drill mode FailedResetDomains []string // FailedResetDomains contains false positive in drill mode Operator string } ) // FailoverWorkflow is the workflow that managed failover all domains with IsManagedByCadence=true func FailoverWorkflow(ctx workflow.Context, params *FailoverParams) (*FailoverResult, error) { err := validateParams(params) if err != nil { return nil, err } // define query properties var failedDomains []string var successDomains []string var successResetDomains []string var failedResetDomains []string var totalNumOfDomains int wfState := WorkflowInitialized operator := getOperator(ctx) err = workflow.SetQueryHandler(ctx, QueryType, func(input []byte) (*QueryResult, error) { return &QueryResult{ TotalDomains: totalNumOfDomains, Success: len(successDomains), Failed: len(failedDomains), State: wfState, TargetCluster: params.TargetCluster, SourceCluster: params.SourceCluster, SuccessDomains: successDomains, FailedDomains: failedDomains, SuccessResetDomains: successResetDomains, FailedResetDomains: failedResetDomains, Operator: operator, }, nil }) if err != nil { return nil, err } // get target domains ao := workflow.WithActivityOptions(ctx, getGetDomainsActivityOptions()) getDomainsParams := &GetDomainsActivityParams{ TargetCluster: params.TargetCluster, SourceCluster: params.SourceCluster, Domains: params.Domains, } var domains []string err = workflow.ExecuteActivity(ao, GetDomainsActivity, getDomainsParams).Get(ctx, &domains) if err != nil { return nil, err } totalNumOfDomains = len(domains) pauseCh := workflow.GetSignalChannel(ctx, PauseSignal) resumeCh := workflow.GetSignalChannel(ctx, ResumeSignal) var shouldPause bool checkPauseSignal := func() { shouldPause = pauseCh.ReceiveAsync(nil) if shouldPause { wfState = WorkflowPaused resumeCh.Receive(ctx, nil) // clean up all pending pause signal cleanupChannel(pauseCh) } wfState = WorkflowRunning } // failover in batch successDomains, failedDomains = failoverDomainsByBatch(ctx, domains, params, checkPauseSignal, false) if params.DrillWaitTime == 0 { // This is a normal failover wfState = WorkflowCompleted return &FailoverResult{ SuccessDomains: successDomains, FailedDomains: failedDomains, }, nil } workflow.Sleep(ctx, params.DrillWaitTime) // Reset domains to original cluster successResetDomains, failedResetDomains = failoverDomainsByBatch(ctx, domains, params, checkPauseSignal, true) wfState = WorkflowCompleted return &FailoverResult{ SuccessDomains: successDomains, FailedDomains: failedDomains, SuccessResetDomains: successResetDomains, FailedResetDomains: failedResetDomains, }, nil } func failoverDomainsByBatch( ctx workflow.Context, domains []string, params *FailoverParams, pauseSignalHandler func(), reverseFailover bool, ) (successDomains []string, failedDomains []string) { totalNumOfDomains := len(domains) batchSize := params.BatchFailoverSize times := totalNumOfDomains/batchSize + 1 ao := workflow.WithActivityOptions(ctx, getFailoverActivityOptions()) targetCluster := params.TargetCluster if reverseFailover { targetCluster = params.SourceCluster } for i := 0; i < times; i++ { pauseSignalHandler() failoverActivityParams := &FailoverActivityParams{ Domains: domains[i*batchSize : min((i+1)*batchSize, totalNumOfDomains)], TargetCluster: targetCluster, GracefulFailoverTimeoutInSeconds: params.GracefulFailoverTimeoutInSeconds, } var actResult FailoverActivityResult err := workflow.ExecuteActivity(ao, FailoverActivity, failoverActivityParams).Get(ctx, &actResult) if err != nil { // Domains in failed activity can be either failovered or not, but we treated them as failed. // This makes the query result for FailedDomains contains false positive results. failedDomains = append(failedDomains, failoverActivityParams.Domains...) } else { successDomains = append(successDomains, actResult.SuccessDomains...) failedDomains = append(failedDomains, actResult.FailedDomains...) } if i != times-1 { workflow.Sleep(ctx, time.Duration(params.BatchFailoverWaitTimeInSeconds)*time.Second) } } return } func getOperator(ctx workflow.Context) string { memo := workflow.GetInfo(ctx).Memo if memo == nil || len(memo.Fields) == 0 { return unknownOperator } opBytes, ok := memo.Fields[constants.MemoKeyForOperator] if !ok { return unknownOperator } var operator string err := json.Unmarshal(opBytes, &operator) if err != nil { return unknownOperator } return operator } func getGetDomainsActivityOptions() workflow.ActivityOptions { return workflow.ActivityOptions{ ScheduleToStartTimeout: 10 * time.Second, StartToCloseTimeout: 20 * time.Second, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: 2 * time.Second, BackoffCoefficient: 2, MaximumInterval: 1 * time.Minute, ExpirationInterval: 10 * time.Minute, NonRetriableErrorReasons: []string{ errMsgParamsIsNil, errMsgTargetClusterIsEmpty, errMsgSourceClusterIsEmpty, errMsgTargetClusterIsSameAsSource}, }, } } func getFailoverActivityOptions() workflow.ActivityOptions { return workflow.ActivityOptions{ ScheduleToStartTimeout: 10 * time.Second, StartToCloseTimeout: 10 * time.Second, } } func validateParams(params *FailoverParams) error { if params == nil { return errors.New(errMsgParamsIsNil) } if params.BatchFailoverSize <= 0 { params.BatchFailoverSize = defaultBatchFailoverSize } if params.BatchFailoverWaitTimeInSeconds <= 0 { params.BatchFailoverWaitTimeInSeconds = defaultBatchFailoverWaitTimeInSeconds } return validateTargetAndSourceCluster(params.TargetCluster, params.SourceCluster) } // GetDomainsActivity activity def func GetDomainsActivity(ctx context.Context, params *GetDomainsActivityParams) ([]string, error) { err := validateGetDomainsActivityParams(params) if err != nil { return nil, err } domains, err := getAllDomains(ctx, params.Domains) if err != nil { return nil, err } var res []string for _, domain := range domains { if shouldFailover(domain, params.SourceCluster) { domainName := domain.GetDomainInfo().GetName() res = append(res, domainName) } } return res, nil } func validateGetDomainsActivityParams(params *GetDomainsActivityParams) error { if params == nil { return errors.New(errMsgParamsIsNil) } return validateTargetAndSourceCluster(params.TargetCluster, params.SourceCluster) } func validateTargetAndSourceCluster(targetCluster, sourceCluster string) error { if len(targetCluster) == 0 { return errors.New(errMsgTargetClusterIsEmpty) } if len(sourceCluster) == 0 { return errors.New(errMsgSourceClusterIsEmpty) } if sourceCluster == targetCluster { return errors.New(errMsgTargetClusterIsSameAsSource) } return nil } func shouldFailover(domain *types.DescribeDomainResponse, sourceCluster string) bool { if !domain.GetIsGlobalDomain() { return false } // Skip deprecated and deleted domains if domain.DomainInfo != nil && domain.DomainInfo.Status != nil { status := *domain.DomainInfo.Status if status == types.DomainStatusDeprecated || status == types.DomainStatusDeleted { return false } } // TODO(active-active): Remove this check once failover drills are supported for // active-active workflows if domain.ReplicationConfiguration.ActiveClusters != nil && len(domain.ReplicationConfiguration.ActiveClusters.AttributeScopes) > 0 { return false } currentActiveCluster := domain.ReplicationConfiguration.GetActiveClusterName() isDomainTarget := currentActiveCluster == sourceCluster return isDomainTarget && isDomainFailoverManagedByCadence(domain) } func isDomainFailoverManagedByCadence(domain *types.DescribeDomainResponse) bool { domainData := domain.DomainInfo.GetData() return strings.ToLower(strings.TrimSpace(domainData[constants.DomainDataKeyForManagedFailover])) == "true" } func getClient(ctx context.Context) frontend.Client { manager := ctx.Value(failoverManagerContextKey).(*FailoverManager) feClient := manager.clientBean.GetFrontendClient() return feClient } func getRemoteClient(ctx context.Context, clusterName string) (frontend.Client, error) { manager := ctx.Value(failoverManagerContextKey).(*FailoverManager) return manager.clientBean.GetRemoteFrontendClient(clusterName) } func getAllDomains(ctx context.Context, targetDomains []string) ([]*types.DescribeDomainResponse, error) { feClient := getClient(ctx) var res []*types.DescribeDomainResponse isTargetDomainsProvided := len(targetDomains) > 0 targetDomainsSet := make(map[string]struct{}) if isTargetDomainsProvided { for _, domain := range targetDomains { targetDomainsSet[domain] = struct{}{} } } pagesize := int32(200) var token []byte for more := true; more; more = len(token) > 0 { listRequest := &types.ListDomainsRequest{ PageSize: pagesize, NextPageToken: token, } listResp, err := feClient.ListDomains(ctx, listRequest) if err != nil { return nil, err } token = listResp.GetNextPageToken() if isTargetDomainsProvided { for _, domain := range listResp.GetDomains() { if _, ok := targetDomainsSet[domain.DomainInfo.GetName()]; ok { res = append(res, domain) } } } else { res = append(res, listResp.GetDomains()...) } activity.RecordHeartbeat(ctx, len(res)) } return res, nil } // FailoverActivity activity def func FailoverActivity(ctx context.Context, params *FailoverActivityParams) (*FailoverActivityResult, error) { logger := activity.GetLogger(ctx) frontendClient := getClient(ctx) domains := params.Domains var successDomains []string var failedDomains []string for _, domain := range domains { // Check if poller exist if err := validateTaskListPollerInfo(ctx, params.TargetCluster, domain); err != nil { logger.Error("Failed to validate task list poller info", zap.Error(err)) failedDomains = append(failedDomains, domain) continue } updateRequest := &types.UpdateDomainRequest{ Name: domain, ActiveClusterName: common.StringPtr(params.TargetCluster), } if params.GracefulFailoverTimeoutInSeconds != nil { updateRequest.FailoverTimeoutInSeconds = params.GracefulFailoverTimeoutInSeconds } _, err := frontendClient.UpdateDomain(ctx, updateRequest) if err != nil { failedDomains = append(failedDomains, domain) } else { successDomains = append(successDomains, domain) } } return &FailoverActivityResult{ SuccessDomains: successDomains, FailedDomains: failedDomains, }, nil } func cleanupChannel(channel workflow.Channel) { for { if hasValue := channel.ReceiveAsync(nil); !hasValue { return } } } func validateTaskListPollerInfo(ctx context.Context, targetCluster string, domain string) error { remoteFrontendClient, err := getRemoteClient(ctx, targetCluster) if err != nil { return err } frontendClient := getClient(ctx) localTaskListResponse, err := frontendClient.GetTaskListsByDomain(ctx, &types.GetTaskListsByDomainRequest{Domain: domain}) if err != nil { return fmt.Errorf("failed to get task list for domain %s", domain) } remoteTaskListRepsonse, err := remoteFrontendClient.GetTaskListsByDomain(ctx, &types.GetTaskListsByDomainRequest{Domain: domain}) if err != nil { return fmt.Errorf("failed to get task list for domain %s", domain) } for name, tl := range localTaskListResponse.GetDecisionTaskListMap() { if len(tl.GetPollers()) != 0 { remoteTaskList, ok := remoteTaskListRepsonse.GetDecisionTaskListMap()[name] if !ok || len(remoteTaskList.GetPollers()) == 0 { return fmt.Errorf("received zero poller in decision task list %s with domain %s", name, domain) } } } for name, tl := range localTaskListResponse.GetActivityTaskListMap() { if len(tl.GetPollers()) != 0 { remoteTaskList, ok := remoteTaskListRepsonse.GetActivityTaskListMap()[name] if !ok || len(remoteTaskList.GetPollers()) == 0 { return fmt.Errorf("received zero poller in decision task list %s with domain %s", name, domain) } } } return nil } ================================================ FILE: service/worker/failovermanager/workflow_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package failovermanager import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/activity" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/types" ) var clusters = []*types.ClusterReplicationConfiguration{ { ClusterName: "c1", }, { ClusterName: "c2", }, } type failoverWorkflowTestSuite struct { suite.Suite testsuite.WorkflowTestSuite activityEnv *testsuite.TestActivityEnvironment workflowEnv *testsuite.TestWorkflowEnvironment } func TestFailoverWorkflowTestSuite(t *testing.T) { suite.Run(t, new(failoverWorkflowTestSuite)) } func (s *failoverWorkflowTestSuite) SetupTest() { s.activityEnv = s.NewTestActivityEnvironment() s.workflowEnv = s.NewTestWorkflowEnvironment() s.workflowEnv.RegisterWorkflowWithOptions(FailoverWorkflow, workflow.RegisterOptions{Name: FailoverWorkflowTypeName}) s.workflowEnv.RegisterActivityWithOptions(FailoverActivity, activity.RegisterOptions{Name: failoverActivityName}) s.workflowEnv.RegisterActivityWithOptions(GetDomainsActivity, activity.RegisterOptions{Name: getDomainsActivityName}) s.activityEnv.RegisterActivityWithOptions(FailoverActivity, activity.RegisterOptions{Name: failoverActivityName}) s.activityEnv.RegisterActivityWithOptions(GetDomainsActivity, activity.RegisterOptions{Name: getDomainsActivityName}) } func (s *failoverWorkflowTestSuite) TearDownTest() { s.workflowEnv.AssertExpectations(s.T()) } func (s *failoverWorkflowTestSuite) TestValidateParams() { s.Error(validateParams(nil)) params := &FailoverParams{} s.Error(validateParams(params)) params.TargetCluster = "t" s.Error(validateParams(params)) params.SourceCluster = "t" s.Error(validateParams(params)) params.SourceCluster = "s" s.NoError(validateParams(params)) } func (s *failoverWorkflowTestSuite) TestWorkflow_InvalidParams() { params := &FailoverParams{} s.workflowEnv.ExecuteWorkflow(FailoverWorkflowTypeName, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.Error(s.workflowEnv.GetWorkflowError()) } func (s *failoverWorkflowTestSuite) TestWorkflow_GetDomainActivityError() { err := errors.New("mockErr") s.workflowEnv.OnActivity(getDomainsActivityName, mock.Anything, mock.Anything).Return(nil, err) params := &FailoverParams{ TargetCluster: "t", SourceCluster: "s", } s.workflowEnv.ExecuteWorkflow(FailoverWorkflowTypeName, params) s.True(s.workflowEnv.IsWorkflowCompleted()) s.Equal("mockErr", s.workflowEnv.GetWorkflowError().Error()) } func (s *failoverWorkflowTestSuite) TestWorkflow_FailoverActivityError() { domains := []string{"d1"} err := errors.New("mockErr") s.workflowEnv.OnActivity(getDomainsActivityName, mock.Anything, mock.Anything).Return(domains, nil) s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, mock.Anything).Return(nil, err) params := &FailoverParams{ TargetCluster: "t", SourceCluster: "s", } s.workflowEnv.ExecuteWorkflow(FailoverWorkflowTypeName, params) var result FailoverResult s.NoError(s.workflowEnv.GetWorkflowResult(&result)) s.Equal(0, len(result.SuccessDomains)) s.Equal(domains, result.FailedDomains) } func (s *failoverWorkflowTestSuite) TestWorkflow_Success() { domains := []string{"d1"} mockFailoverActivityResult := &FailoverActivityResult{ SuccessDomains: []string{"d1"}, } s.workflowEnv.OnActivity(getDomainsActivityName, mock.Anything, mock.Anything).Return(domains, nil) s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, mock.Anything).Return(mockFailoverActivityResult, nil) params := &FailoverParams{ TargetCluster: "t", SourceCluster: "s", } s.workflowEnv.ExecuteWorkflow(FailoverWorkflowTypeName, params) var result FailoverResult s.NoError(s.workflowEnv.GetWorkflowResult(&result)) s.Equal(mockFailoverActivityResult.SuccessDomains, result.SuccessDomains) s.Equal(mockFailoverActivityResult.FailedDomains, result.FailedDomains) queryResult, err := s.workflowEnv.QueryWorkflow(QueryType) s.NoError(err) var res QueryResult s.NoError(queryResult.Get(&res)) s.Equal(len(domains), res.TotalDomains) s.Equal(len(domains), res.Success) s.Equal(0, res.Failed) s.Equal(WorkflowCompleted, res.State) s.Equal("t", res.TargetCluster) s.Equal("s", res.SourceCluster) s.Equal(domains, res.SuccessDomains) s.Equal(0, len(res.FailedDomains)) s.Equal(unknownOperator, res.Operator) } func (s *failoverWorkflowTestSuite) TestWorkflow_Success_Batches() { domains := []string{"d1", "d2", "d3"} expectFailoverActivityParams1 := &FailoverActivityParams{ Domains: []string{"d1", "d2"}, TargetCluster: "t", } mockFailoverActivityResult1 := &FailoverActivityResult{ SuccessDomains: []string{"d1", "d2"}, } expectFailoverActivityParams2 := &FailoverActivityParams{ Domains: []string{"d3"}, TargetCluster: "t", } mockFailoverActivityResult2 := &FailoverActivityResult{ FailedDomains: []string{"d3"}, } s.workflowEnv.OnActivity(getDomainsActivityName, mock.Anything, mock.Anything).Return(domains, nil) s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, expectFailoverActivityParams1).Return(mockFailoverActivityResult1, nil).Once() s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, expectFailoverActivityParams2).Return(mockFailoverActivityResult2, nil).Once() params := &FailoverParams{ TargetCluster: "t", SourceCluster: "s", BatchFailoverSize: 2, Domains: domains, } s.workflowEnv.ExecuteWorkflow(FailoverWorkflowTypeName, params) var result FailoverResult s.NoError(s.workflowEnv.GetWorkflowResult(&result)) s.Equal(mockFailoverActivityResult1.SuccessDomains, result.SuccessDomains) s.Equal(mockFailoverActivityResult2.FailedDomains, result.FailedDomains) } func (s *failoverWorkflowTestSuite) TestWorkflow_Pause() { domains := []string{"d1"} mockFailoverActivityResult := &FailoverActivityResult{ SuccessDomains: []string{"d1"}, } s.workflowEnv.OnActivity(getDomainsActivityName, mock.Anything, mock.Anything).Return(domains, nil) s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, mock.Anything).Return(mockFailoverActivityResult, nil).Once() s.workflowEnv.RegisterDelayedCallback(func() { s.workflowEnv.SignalWorkflow(PauseSignal, nil) }, time.Millisecond*0) s.workflowEnv.RegisterDelayedCallback(func() { s.assertQueryState(s.workflowEnv, WorkflowPaused) }, time.Millisecond*100) s.workflowEnv.RegisterDelayedCallback(func() { s.workflowEnv.SignalWorkflow(ResumeSignal, nil) }, time.Millisecond*200) s.workflowEnv.RegisterDelayedCallback(func() { s.assertQueryState(s.workflowEnv, WorkflowRunning) }, time.Millisecond*300) params := &FailoverParams{ TargetCluster: "t", SourceCluster: "s", BatchFailoverSize: 2, Domains: domains, } s.workflowEnv.ExecuteWorkflow(FailoverWorkflowTypeName, params) var result FailoverResult s.NoError(s.workflowEnv.GetWorkflowResult(&result)) s.Equal(mockFailoverActivityResult.SuccessDomains, result.SuccessDomains) } func (s *failoverWorkflowTestSuite) TestWorkflow_WithDrillWaitTime_Success() { domains := []string{"d1"} mockFailoverActivityResult := &FailoverActivityResult{ SuccessDomains: []string{"d1"}, } s.workflowEnv.OnActivity(getDomainsActivityName, mock.Anything, mock.Anything).Return(domains, nil) s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, mock.Anything).Return(mockFailoverActivityResult, nil) params := &FailoverParams{ TargetCluster: "t", SourceCluster: "s", DrillWaitTime: 1 * time.Second, } var timerCount int s.workflowEnv.SetOnTimerScheduledListener(func(timerID string, duration time.Duration) { timerCount++ if duration != time.Second && duration != 30*time.Second { s.Fail("Receive unknown timer.") } }) s.workflowEnv.SetOnTimerFiredListener(func(timerID string) { timerCount-- }) s.workflowEnv.ExecuteWorkflow(FailoverWorkflowTypeName, params) var result FailoverResult s.NoError(s.workflowEnv.GetWorkflowResult(&result)) s.Equal(mockFailoverActivityResult.SuccessDomains, result.SuccessDomains) s.Equal(mockFailoverActivityResult.FailedDomains, result.FailedDomains) queryResult, err := s.workflowEnv.QueryWorkflow(QueryType) s.NoError(err) s.Equal(0, timerCount) var res QueryResult s.NoError(queryResult.Get(&res)) s.Equal(len(domains), res.TotalDomains) s.Equal(len(domains), res.Success) s.Equal(0, res.Failed) s.Equal(WorkflowCompleted, res.State) s.Equal("t", res.TargetCluster) s.Equal("s", res.SourceCluster) s.Equal(domains, res.SuccessDomains) s.Equal(0, len(res.FailedDomains)) s.Equal(unknownOperator, res.Operator) s.Equal(domains, res.SuccessResetDomains) s.Equal(0, len(res.FailedResetDomains)) } func (s *failoverWorkflowTestSuite) TestShouldFailover() { tests := []struct { domain *types.DescribeDomainResponse sourceCluster string expected bool }{ { domain: &types.DescribeDomainResponse{ IsGlobalDomain: false, }, sourceCluster: "c1", expected: false, }, { domain: &types.DescribeDomainResponse{ IsGlobalDomain: true, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, }, sourceCluster: "c2", expected: false, }, { domain: &types.DescribeDomainResponse{ IsGlobalDomain: true, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c2", Clusters: clusters, }, }, sourceCluster: "c2", expected: false, }, { domain: &types.DescribeDomainResponse{ IsGlobalDomain: true, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c2", Clusters: clusters, }, DomainInfo: &types.DomainInfo{ Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", }, }, }, sourceCluster: "c2", expected: true, }, { domain: &types.DescribeDomainResponse{ IsGlobalDomain: true, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c2", Clusters: clusters, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "c1": { ActiveClusterName: "c1", }, }, }, }, }, }, DomainInfo: &types.DomainInfo{ Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", }, }, }, sourceCluster: "c2", expected: false, }, { domain: &types.DescribeDomainResponse{ IsGlobalDomain: true, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c2", Clusters: clusters, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, }, }, }, DomainInfo: &types.DomainInfo{ Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", }, }, }, sourceCluster: "c2", expected: false, }, } for _, t := range tests { s.Equal(t.expected, shouldFailover(t.domain, t.sourceCluster)) } } func (s *failoverWorkflowTestSuite) TestGetDomainsActivity() { env, mockResource := s.prepareTestActivityEnv() domains := &types.ListDomainsResponse{ Domains: []*types.DescribeDomainResponse{ { DomainInfo: &types.DomainInfo{ Name: "d1", Data: map[string]string{constants.DomainDataKeyForManagedFailover: "true"}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, }, } mockResource.FrontendClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(domains, nil) params := &GetDomainsActivityParams{ TargetCluster: "c2", SourceCluster: "c1", } actResult, err := env.ExecuteActivity(getDomainsActivityName, params) s.NoError(err) var result []string s.NoError(actResult.Get(&result)) s.Equal([]string{"d1"}, result) } func (s *failoverWorkflowTestSuite) TestGetDomainsActivity_WithTargetDomains() { env, mockResource := s.prepareTestActivityEnv() domains := &types.ListDomainsResponse{ Domains: []*types.DescribeDomainResponse{ { DomainInfo: &types.DomainInfo{ Name: "d1", Data: map[string]string{constants.DomainDataKeyForManagedFailover: "true"}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, { DomainInfo: &types.DomainInfo{ Name: "d2", Data: map[string]string{constants.DomainDataKeyForManagedFailover: "true"}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, { DomainInfo: &types.DomainInfo{ Name: "d3", }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, }, } mockResource.FrontendClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(domains, nil) params := &GetDomainsActivityParams{ TargetCluster: "c2", SourceCluster: "c1", Domains: []string{"d1", "d3"}, // only target d1 and d3 } actResult, err := env.ExecuteActivity(getDomainsActivityName, params) s.NoError(err) var result []string s.NoError(actResult.Get(&result)) s.Equal([]string{"d1"}, result) // d3 filtered out because not managed } func (s *failoverWorkflowTestSuite) TestGetDomainsActivity_FiltersActiveActiveDomains() { env, mockResource := s.prepareTestActivityEnv() domains := &types.ListDomainsResponse{ Domains: []*types.DescribeDomainResponse{ { DomainInfo: &types.DomainInfo{ Name: "regular-domain", Data: map[string]string{constants.DomainDataKeyForManagedFailover: "true"}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, { DomainInfo: &types.DomainInfo{ Name: "active-active-domain", Data: map[string]string{constants.DomainDataKeyForManagedFailover: "true"}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: "c1", }, }, }, }, }, }, IsGlobalDomain: true, }, { DomainInfo: &types.DomainInfo{ Name: "empty-active-clusters-domain", Data: map[string]string{constants.DomainDataKeyForManagedFailover: "true"}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{}, }, }, }, }, IsGlobalDomain: true, }, }, } mockResource.FrontendClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(domains, nil) params := &GetDomainsActivityParams{ TargetCluster: "c2", SourceCluster: "c1", } actResult, err := env.ExecuteActivity(getDomainsActivityName, params) s.NoError(err) var result []string s.NoError(actResult.Get(&result)) s.Equal([]string{"regular-domain"}, result) } func (s *failoverWorkflowTestSuite) TestGetDomainsActivity_WithActiveActiveTargetDomains() { env, mockResource := s.prepareTestActivityEnv() domains := &types.ListDomainsResponse{ Domains: []*types.DescribeDomainResponse{ { DomainInfo: &types.DomainInfo{ Name: "regular-domain", Data: map[string]string{constants.DomainDataKeyForManagedFailover: "true"}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, }, IsGlobalDomain: true, }, { DomainInfo: &types.DomainInfo{ Name: "active-active-domain", Data: map[string]string{constants.DomainDataKeyForManagedFailover: "true"}, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: clusters, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": { ActiveClusterName: "c1", }, "region2": { ActiveClusterName: "c2", }, }, }, }, }, }, IsGlobalDomain: true, }, }, } mockResource.FrontendClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(domains, nil) params := &GetDomainsActivityParams{ TargetCluster: "c2", SourceCluster: "c1", Domains: []string{"regular-domain", "active-active-domain"}, } actResult, err := env.ExecuteActivity(getDomainsActivityName, params) s.NoError(err) var result []string s.NoError(actResult.Get(&result)) s.Equal([]string{"regular-domain"}, result) } func (s *failoverWorkflowTestSuite) TestFailoverActivity_ForceFailover_Success() { env, mockResource := s.prepareTestActivityEnv() domains := []string{"d1", "d2"} describeTaskListResp := &types.DescribeTaskListResponse{Pollers: []*types.PollerInfo{ { Identity: "test", }, }} taskListMap := map[string]*types.DescribeTaskListResponse{ "tl": describeTaskListResp, } mockResource.FrontendClient.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(nil, nil).Times(len(domains)) mockResource.FrontendClient.EXPECT().GetTaskListsByDomain(gomock.Any(), gomock.Any()).Return(&types.GetTaskListsByDomainResponse{ DecisionTaskListMap: taskListMap, ActivityTaskListMap: taskListMap, }, nil).Times(len(domains)) mockResource.RemoteFrontendClient.EXPECT().GetTaskListsByDomain(gomock.Any(), gomock.Any()).Return(&types.GetTaskListsByDomainResponse{ DecisionTaskListMap: taskListMap, ActivityTaskListMap: taskListMap, }, nil).Times(len(domains)) params := &FailoverActivityParams{ Domains: domains, TargetCluster: "c2", } actResult, err := env.ExecuteActivity(failoverActivityName, params) s.NoError(err) var result FailoverActivityResult s.NoError(actResult.Get(&result)) s.Equal(domains, result.SuccessDomains) } func (s *failoverWorkflowTestSuite) TestFailoverActivity_GracefulFailover_Success() { env, mockResource := s.prepareTestActivityEnv() domains := []string{"d1", "d2"} describeTaskListResp := &types.DescribeTaskListResponse{Pollers: []*types.PollerInfo{ { Identity: "test", }, }} taskListMap := map[string]*types.DescribeTaskListResponse{ "tl": describeTaskListResp, } params := &FailoverActivityParams{ Domains: domains, TargetCluster: "c2", GracefulFailoverTimeoutInSeconds: common.Int32Ptr(int32(10)), } updateRequest1 := &types.UpdateDomainRequest{ Name: "d1", ActiveClusterName: common.StringPtr("c2"), FailoverTimeoutInSeconds: params.GracefulFailoverTimeoutInSeconds, } updateRequest2 := &types.UpdateDomainRequest{ Name: "d2", ActiveClusterName: common.StringPtr("c2"), FailoverTimeoutInSeconds: params.GracefulFailoverTimeoutInSeconds, } mockResource.FrontendClient.EXPECT().UpdateDomain(gomock.Any(), updateRequest1).Return(nil, nil).Times(1) mockResource.FrontendClient.EXPECT().UpdateDomain(gomock.Any(), updateRequest2).Return(nil, nil).Times(1) mockResource.FrontendClient.EXPECT().GetTaskListsByDomain(gomock.Any(), gomock.Any()).Return(&types.GetTaskListsByDomainResponse{ DecisionTaskListMap: taskListMap, ActivityTaskListMap: taskListMap, }, nil).Times(len(domains)) mockResource.RemoteFrontendClient.EXPECT().GetTaskListsByDomain(gomock.Any(), gomock.Any()).Return(&types.GetTaskListsByDomainResponse{ DecisionTaskListMap: taskListMap, ActivityTaskListMap: taskListMap, }, nil).Times(len(domains)) actResult, err := env.ExecuteActivity(failoverActivityName, params) s.NoError(err) var result FailoverActivityResult s.NoError(actResult.Get(&result)) s.Equal(domains, result.SuccessDomains) } func (s *failoverWorkflowTestSuite) TestFailoverActivity_Error() { env, mockResource := s.prepareTestActivityEnv() domains := []string{"d1", "d2"} targetCluster := "c2" updateRequest1 := &types.UpdateDomainRequest{ Name: "d1", ActiveClusterName: common.StringPtr(targetCluster), } updateRequest2 := &types.UpdateDomainRequest{ Name: "d2", ActiveClusterName: common.StringPtr(targetCluster), } describeTaskListResp := &types.DescribeTaskListResponse{Pollers: []*types.PollerInfo{ { Identity: "test", }, }} taskListMap := map[string]*types.DescribeTaskListResponse{ "tl": describeTaskListResp, } mockResource.FrontendClient.EXPECT().UpdateDomain(gomock.Any(), updateRequest1).Return(nil, nil) mockResource.FrontendClient.EXPECT().UpdateDomain(gomock.Any(), updateRequest2).Return(nil, errors.New("mockErr")) mockResource.FrontendClient.EXPECT().GetTaskListsByDomain(gomock.Any(), gomock.Any()).Return(&types.GetTaskListsByDomainResponse{ DecisionTaskListMap: taskListMap, ActivityTaskListMap: taskListMap, }, nil).Times(len(domains)) mockResource.RemoteFrontendClient.EXPECT().GetTaskListsByDomain(gomock.Any(), gomock.Any()).Return(&types.GetTaskListsByDomainResponse{ DecisionTaskListMap: taskListMap, ActivityTaskListMap: taskListMap, }, nil).Times(len(domains)) params := &FailoverActivityParams{ Domains: domains, TargetCluster: targetCluster, } actResult, err := env.ExecuteActivity(failoverActivityName, params) s.NoError(err) var result FailoverActivityResult s.NoError(actResult.Get(&result)) s.Equal([]string{"d1"}, result.SuccessDomains) s.Equal([]string{"d2"}, result.FailedDomains) } func (s *failoverWorkflowTestSuite) TestFailoverActivity_NoPoller_Error() { env, mockResource := s.prepareTestActivityEnv() domains := []string{"d1", "d2"} targetCluster := "c2" describeTaskListResp1 := &types.DescribeTaskListResponse{Pollers: []*types.PollerInfo{ { Identity: "test", }, }} taskListMap1 := map[string]*types.DescribeTaskListResponse{ "tl": describeTaskListResp1, } describeTaskListResp2 := &types.DescribeTaskListResponse{Pollers: []*types.PollerInfo{}} taskListMap2 := map[string]*types.DescribeTaskListResponse{ "tl": describeTaskListResp2, } mockResource.FrontendClient.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Times(0) mockResource.FrontendClient.EXPECT().GetTaskListsByDomain(gomock.Any(), gomock.Any()).Return(&types.GetTaskListsByDomainResponse{ DecisionTaskListMap: taskListMap1, ActivityTaskListMap: taskListMap1, }, nil).Times(len(domains)) mockResource.RemoteFrontendClient.EXPECT().GetTaskListsByDomain(gomock.Any(), gomock.Any()).Return(&types.GetTaskListsByDomainResponse{ DecisionTaskListMap: taskListMap2, ActivityTaskListMap: taskListMap2, }, nil).Times(len(domains)) params := &FailoverActivityParams{ Domains: domains, TargetCluster: targetCluster, } actResult, err := env.ExecuteActivity(failoverActivityName, params) s.NoError(err) var result FailoverActivityResult s.NoError(actResult.Get(&result)) s.Equal(0, len(result.SuccessDomains)) s.Equal([]string{"d1", "d2"}, result.FailedDomains) } func (s *failoverWorkflowTestSuite) TestGetOperator() { operator := "testOperator" s.workflowEnv.SetMemoOnStart(map[string]interface{}{ constants.MemoKeyForOperator: operator, }) s.workflowEnv.OnActivity(getDomainsActivityName, mock.Anything, mock.Anything).Return(nil, nil) s.workflowEnv.OnActivity(failoverActivityName, mock.Anything, mock.Anything).Return(nil, nil) params := &FailoverParams{ TargetCluster: "t", SourceCluster: "s", } s.workflowEnv.ExecuteWorkflow(FailoverWorkflowTypeName, params) var result FailoverResult s.NoError(s.workflowEnv.GetWorkflowResult(&result)) queryResult, err := s.workflowEnv.QueryWorkflow(QueryType) s.NoError(err) var res QueryResult s.NoError(queryResult.Get(&res)) s.Equal(operator, res.Operator) } func (s *failoverWorkflowTestSuite) TestShouldFailover_DeprecatedDomain() { deprecatedDomain := &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "test-deprecated-domain", Status: types.DomainStatusDeprecated.Ptr(), Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", }, }, IsGlobalDomain: true, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "cluster1", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "cluster1"}, {ClusterName: "cluster2"}, }, }, } result := shouldFailover(deprecatedDomain, "cluster1") s.False(result, "Deprecated domains should not be included in failover") } func (s *failoverWorkflowTestSuite) TestShouldFailover_DeletedDomain() { deletedDomain := &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "test-deleted-domain", Status: types.DomainStatusDeleted.Ptr(), Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", }, }, IsGlobalDomain: true, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "cluster1", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "cluster1"}, {ClusterName: "cluster2"}, }, }, } result := shouldFailover(deletedDomain, "cluster1") s.False(result, "Deleted domains should not be included in failover") } func (s *failoverWorkflowTestSuite) assertQueryState(env *testsuite.TestWorkflowEnvironment, expectedState string) { queryResult, err := env.QueryWorkflow(QueryType) s.NoError(err) var res QueryResult s.NoError(queryResult.Get(&res)) s.Equal(expectedState, res.State) } func (s *failoverWorkflowTestSuite) prepareTestActivityEnv() (*testsuite.TestActivityEnvironment, *resource.Test) { controller := gomock.NewController(s.T()) mockResource := resource.NewTest(s.T(), controller, metrics.Worker) ctx := &FailoverManager{ svcClient: mockResource.GetSDKClient(), clientBean: mockResource.ClientBean, } s.activityEnv.SetTestTimeout(time.Second * 5) s.activityEnv.SetWorkerOptions(worker.Options{ BackgroundActivityContext: context.WithValue(context.Background(), failoverManagerContextKey, ctx), }) s.T().Cleanup(func() { mockResource.Finish(s.T()) }) return s.activityEnv, mockResource } ================================================ FILE: service/worker/indexer/esProcessor.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package indexer import ( "context" "encoding/json" "fmt" "math/rand" "time" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/definition" es "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/elasticsearch/bulk" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" ) const ( // retry configs for es bulk bulkProcessor esProcessorInitialRetryInterval = 200 * time.Millisecond esProcessorMaxRetryInterval = 20 * time.Second ) type ( // ESProcessorImpl implements ESProcessor, it's an agent of GenericBulkProcessor ESProcessorImpl struct { bulkProcessor bulk.GenericBulkProcessor mapToKafkaMsg collection.ConcurrentTxMap // used to map ES request to kafka message config *Config logger log.Logger scope metrics.Scope msgEncoder codec.BinaryEncoder } kafkaMessageWithMetrics struct { // value of ESProcessorImpl.mapToKafkaMsg message messaging.Message swFromAddToAck *metrics.Stopwatch // metric from message add to process, to message ack/nack } ) // newESProcessor creates new ESProcessor func newESProcessor( name string, config *Config, client es.GenericClient, logger log.Logger, metricsClient metrics.Client, ) (*ESProcessorImpl, error) { p := &ESProcessorImpl{ config: config, logger: logger.WithTags(tag.ComponentIndexerESProcessor), scope: metricsClient.Scope(metrics.ESProcessorScope), msgEncoder: defaultEncoder, } params := &bulk.BulkProcessorParameters{ Name: name, NumOfWorkers: config.ESProcessorNumOfWorkers(), BulkActions: config.ESProcessorBulkActions(), BulkSize: config.ESProcessorBulkSize(), FlushInterval: config.ESProcessorFlushInterval(), Backoff: bulk.NewExponentialBackoff(esProcessorInitialRetryInterval, esProcessorMaxRetryInterval), BeforeFunc: p.bulkBeforeAction, AfterFunc: p.bulkAfterAction, } processor, err := client.RunBulkProcessor(context.Background(), params) if err != nil { return nil, err } p.bulkProcessor = processor p.mapToKafkaMsg = collection.NewShardedConcurrentTxMap(1024, p.hashFn) return p, nil } func (p *ESProcessorImpl) Start() { // current implementation (v6 and v7) allows to invoke Start() multiple times p.bulkProcessor.Start(context.Background()) } func (p *ESProcessorImpl) Stop() { p.bulkProcessor.Stop() //nolint:errcheck p.mapToKafkaMsg = nil } // Add an ES request, and an map item for kafka message func (p *ESProcessorImpl) Add(request *bulk.GenericBulkableAddRequest, key string, kafkaMsg messaging.Message) { actionWhenFoundDuplicates := func(key interface{}, value interface{}) error { return kafkaMsg.Ack() } sw := p.scope.StartTimer(metrics.ESProcessorProcessMsgLatency) mapVal := newKafkaMessageWithMetrics(kafkaMsg, &sw) _, isDup, _ := p.mapToKafkaMsg.PutOrDo(key, mapVal, actionWhenFoundDuplicates) if isDup { return } p.bulkProcessor.Add(request) } // bulkBeforeAction is triggered before bulk bulkProcessor commit func (p *ESProcessorImpl) bulkBeforeAction(executionID int64, requests []bulk.GenericBulkableRequest) { p.scope.AddCounter(metrics.ESProcessorRequests, int64(len(requests))) } // bulkAfterAction is triggered after bulk bulkProcessor commit func (p *ESProcessorImpl) bulkAfterAction(id int64, requests []bulk.GenericBulkableRequest, response *bulk.GenericBulkResponse, err *bulk.GenericError) { if err != nil { isRetryable := isResponseRetriable(err.Status) for _, request := range requests { if isRetryable { // This happens after configured retry, which means something bad happens on cluster or index // When cluster back to live, bulkProcessor will re-commit those failure requests // retryable errors will be retried by the bulk processor p.logger.Error("Error commit bulk request. ES request failed and is retryable", tag.Error(err.Details), tag.ESRequest(request.String())) } else { key := p.retrieveKafkaKey(request) if key == "" { continue } wid, rid, domainID := p.getMsgWithInfo(key) // check if it is a delete request and status code // 404 means the document does not exist // 409 means means the document's version does not match (or if the document has been updated or deleted by another process) // this can happen during the data migration, the doc was deleted in the old index but not exists in the new index if err.Status == 409 { // Sample version conflict logs to reduce spam (log every 100th conflict) if rand.Intn(100) == 0 { p.logger.Info("Request encountered a version conflict. Acknowledging to prevent retry.", tag.ESResponseStatus(err.Status), tag.ESRequest(request.String()), tag.WorkflowID(wid), tag.WorkflowRunID(rid), tag.WorkflowDomainID(domainID)) } p.ackKafkaMsg(key) continue } else if err.Status == 404 { req, err := request.Source() if err == nil && p.isDeleteRequest(req) { p.logger.Info("Document has been deleted. Acknowledging to prevent retry.", tag.ESResponseStatus(404), tag.ESRequest(request.String()), tag.WorkflowID(wid), tag.WorkflowRunID(rid), tag.WorkflowDomainID(domainID)) p.ackKafkaMsg(key) continue } else { p.logger.Error("Get request source err.", tag.Error(err), tag.ESRequest(request.String())) p.scope.IncCounter(metrics.ESProcessorCorruptedData) } } p.logger.Error("Error commit bulk request. ES request failed and is not retryable", tag.ESResponseStatus(err.Status), tag.ESRequest(request.String()), tag.WorkflowID(wid), tag.WorkflowRunID(rid), tag.WorkflowDomainID(domainID)) p.nackKafkaMsg(key) } p.scope.IncCounter(metrics.ESProcessorFailures) } return } responseItems := response.Items for i := 0; i < len(requests); i++ { key := p.retrieveKafkaKey(requests[i]) if key == "" { continue } responseItem := responseItems[i] for _, resp := range responseItem { switch { case isResponseSuccess(resp.Status): p.ackKafkaMsg(key) case !isResponseRetriable(resp.Status): wid, rid, domainID := p.getMsgWithInfo(key) p.logger.Error("ES request failed.", tag.ESResponseStatus(resp.Status), tag.ESResponseError(getErrorMsgFromESResp(resp)), tag.WorkflowID(wid), tag.WorkflowRunID(rid), tag.WorkflowDomainID(domainID)) p.nackKafkaMsg(key) default: // bulk bulkProcessor will retry p.logger.Info("ES request retried.", tag.ESResponseStatus(resp.Status)) p.scope.IncCounter(metrics.ESProcessorRetries) } } } } func (p *ESProcessorImpl) ackKafkaMsg(key string) { p.ackKafkaMsgHelper(key, false) } func (p *ESProcessorImpl) nackKafkaMsg(key string) { p.ackKafkaMsgHelper(key, true) } func (p *ESProcessorImpl) ackKafkaMsgHelper(key string, nack bool) { kafkaMsg, ok := p.getKafkaMsg(key) if !ok { return } if nack { kafkaMsg.Nack() } else { kafkaMsg.Ack() } p.mapToKafkaMsg.Remove(key) } func (p *ESProcessorImpl) getKafkaMsg(key string) (kafkaMsg *kafkaMessageWithMetrics, ok bool) { msg, ok := p.mapToKafkaMsg.Get(key) if !ok { return // duplicate kafka message } kafkaMsg, ok = msg.(*kafkaMessageWithMetrics) if !ok { // must be bug in code and bad deployment p.logger.Fatal("Message is not kafka message.", tag.ESKey(key)) } return kafkaMsg, ok } func (p *ESProcessorImpl) retrieveKafkaKey(request bulk.GenericBulkableRequest) string { req, err := request.Source() if err != nil { p.logger.Error("Get request source err.", tag.Error(err), tag.ESRequest(request.String())) p.scope.IncCounter(metrics.ESProcessorCorruptedData) return "" } var key string if !p.isDeleteRequest(req) { // index or update requests var body map[string]interface{} if err := json.Unmarshal([]byte(req[1]), &body); err != nil { p.logger.Error("Unmarshal index request body err.", tag.Error(err)) p.scope.IncCounter(metrics.ESProcessorCorruptedData) return "" } k, ok := body[definition.KafkaKey] if !ok { // must be bug in code and bad deployment, check processor that add es requests panic(definition.KafkaKey + " not found") } key, ok = k.(string) if !ok { // must be bug in code and bad deployment, check processor that add es requests panic("kafkaKey is not string") } } else { // delete requests var body map[string]map[string]interface{} if err := json.Unmarshal([]byte(req[0]), &body); err != nil { p.logger.Error("Unmarshal delete request body err.", tag.Error(err)) p.scope.IncCounter(metrics.ESProcessorCorruptedData) return "" } opMap, ok := body["delete"] if !ok { // must be bug, check if dependency changed panic("delete key not found in request") } k, ok := opMap["_id"] if !ok { // must be bug in code and bad deployment, check processor that add es requests panic("_id not found in request opMap") } key, _ = k.(string) } return key } func (p *ESProcessorImpl) getMsgWithInfo(key string) (wid string, rid string, domainID string) { kafkaMsg, ok := p.getKafkaMsg(key) if !ok { return } var msg indexer.Message if err := p.msgEncoder.Decode(kafkaMsg.message.Value(), &msg); err != nil { p.logger.Error("failed to deserialize kafka message.", tag.Error(err)) return } return msg.GetWorkflowID(), msg.GetRunID(), msg.GetDomainID() } func (p *ESProcessorImpl) hashFn(key interface{}) uint32 { id, ok := key.(string) if !ok { return 0 } numOfShards := p.config.IndexerConcurrency() return uint32(common.WorkflowIDToHistoryShard(id, numOfShards)) } func (p *ESProcessorImpl) isDeleteRequest(request []string) bool { // The Source() method typically returns a slice of strings, where each string represents a part of the bulk request in JSON format. // For delete operations, the Source() method typically returns only one part // The metadata that specifies the delete action, including _index and _id. // "{\"delete\":{\"_index\":\"my-index\",\"_id\":\"1\"}}" // For index/update operations, the Source() method typically returns two parts // reference: https://www.elastic.co/guide/en/elasticsearch/reference/6.8/docs-bulk.html return len(request) == 1 } // 409 - Version Conflict // 404 - Not Found func isResponseSuccess(status int) bool { if status >= 200 && status < 300 || status == 409 || status == 404 { return true } return false } // isResponseRetriable is complaint with GenericBulkProcessorService.RetryItemStatusCodes // responses with these status will be kept in queue and retried until success // 408 - Request Timeout // 429 - Too Many Requests // 500 - Node not connected // 503 - Service Unavailable // 507 - Insufficient Storage var retryableStatusCode = map[int]struct{}{408: {}, 429: {}, 500: {}, 503: {}, 507: {}} func isResponseRetriable(status int) bool { _, ok := retryableStatusCode[status] return ok } func getErrorMsgFromESResp(resp *bulk.GenericBulkResponseItem) string { var errMsg string if resp.Error != nil { errMsg = fmt.Sprintf("%v", resp.Error) } return errMsg } func newKafkaMessageWithMetrics(kafkaMsg messaging.Message, stopwatch *metrics.Stopwatch) *kafkaMessageWithMetrics { return &kafkaMessageWithMetrics{ message: kafkaMsg, swFromAddToAck: stopwatch, } } func (km *kafkaMessageWithMetrics) Ack() { km.message.Ack() // nolint:errcheck if km.swFromAddToAck != nil { km.swFromAddToAck.Stop() } } func (km *kafkaMessageWithMetrics) Nack() { km.message.Nack() //nolint:errcheck if km.swFromAddToAck != nil { km.swFromAddToAck.Stop() } } ================================================ FILE: service/worker/indexer/esProcessor_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package indexer import ( "fmt" "sync" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/elasticsearch/bulk" mocks2 "github.com/uber/cadence/common/elasticsearch/bulk/mocks" esMocks "github.com/uber/cadence/common/elasticsearch/mocks" "github.com/uber/cadence/common/log/testlogger" msgMocks "github.com/uber/cadence/common/messaging/mocks" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/metrics/mocks" ) type esProcessorSuite struct { suite.Suite esProcessor *ESProcessorImpl mockBulkProcessor *mocks2.GenericBulkProcessor mockESClient *esMocks.GenericClient mockScope *mocks.Scope } var ( testIndex = "test-index" testType = elasticsearch.GetESDocType() testID = "test-doc-id" testStopWatch = metrics.NoopScope.StartTimer(metrics.ESProcessorProcessMsgLatency) testScope = metrics.ESProcessorScope testMetric = metrics.ESProcessorProcessMsgLatency ) func TestESProcessorSuite(t *testing.T) { s := new(esProcessorSuite) suite.Run(t, s) } func (s *esProcessorSuite) SetupSuite() { } func (s *esProcessorSuite) SetupTest() { config := &Config{ IndexerConcurrency: dynamicproperties.GetIntPropertyFn(32), ESProcessorNumOfWorkers: dynamicproperties.GetIntPropertyFn(1), ESProcessorBulkActions: dynamicproperties.GetIntPropertyFn(10), ESProcessorBulkSize: dynamicproperties.GetIntPropertyFn(2 << 20), ESProcessorFlushInterval: dynamicproperties.GetDurationPropertyFn(1 * time.Minute), } s.mockBulkProcessor = &mocks2.GenericBulkProcessor{} s.mockScope = &mocks.Scope{} p := &ESProcessorImpl{ config: config, logger: testlogger.New(s.T()), scope: s.mockScope, msgEncoder: defaultEncoder, } p.mapToKafkaMsg = collection.NewShardedConcurrentTxMap(1024, p.hashFn) p.bulkProcessor = s.mockBulkProcessor s.esProcessor = p s.mockESClient = &esMocks.GenericClient{} } func (s *esProcessorSuite) TearDownTest() { s.mockBulkProcessor.AssertExpectations(s.T()) s.mockESClient.AssertExpectations(s.T()) } func (s *esProcessorSuite) TestNewESProcessorAndStart() { config := &Config{ ESProcessorNumOfWorkers: dynamicproperties.GetIntPropertyFn(1), ESProcessorBulkActions: dynamicproperties.GetIntPropertyFn(10), ESProcessorBulkSize: dynamicproperties.GetIntPropertyFn(2 << 20), ESProcessorFlushInterval: dynamicproperties.GetDurationPropertyFn(1 * time.Minute), } processorName := "test-bulkProcessor" s.mockESClient.On("RunBulkProcessor", mock.Anything, mock.MatchedBy(func(input *bulk.BulkProcessorParameters) bool { s.Equal(processorName, input.Name) s.Equal(config.ESProcessorNumOfWorkers(), input.NumOfWorkers) s.Equal(config.ESProcessorBulkActions(), input.BulkActions) s.Equal(config.ESProcessorBulkSize(), input.BulkSize) s.Equal(config.ESProcessorFlushInterval(), input.FlushInterval) s.NotNil(input.Backoff) s.NotNil(input.AfterFunc) return true })).Return(&mocks2.GenericBulkProcessor{}, nil).Once() processor, err := newESProcessor(processorName, config, s.mockESClient, s.esProcessor.logger, metrics.NewNoopMetricsClient()) s.NoError(err) s.NotNil(processor.mapToKafkaMsg) } func (s *esProcessorSuite) TestStop() { s.mockBulkProcessor.On("Stop").Return(nil).Once() s.esProcessor.Stop() s.Nil(s.esProcessor.mapToKafkaMsg) } func (s *esProcessorSuite) TestAdd() { request := &bulk.GenericBulkableAddRequest{RequestType: bulk.BulkableIndexRequest} mockKafkaMsg := &msgMocks.Message{} key := "test-key" s.Equal(0, s.esProcessor.mapToKafkaMsg.Len()) s.mockBulkProcessor.On("Add", request).Return().Once() s.mockScope.On("StartTimer", testMetric).Return(testStopWatch).Once() s.esProcessor.Add(request, key, mockKafkaMsg) s.Equal(1, s.esProcessor.mapToKafkaMsg.Len()) mockKafkaMsg.AssertExpectations(s.T()) // handle duplicate mockKafkaMsg.On("Ack").Return(nil).Once() s.mockScope.On("StartTimer", testMetric).Return(testStopWatch).Once() s.esProcessor.Add(request, key, mockKafkaMsg) s.Equal(1, s.esProcessor.mapToKafkaMsg.Len()) mockKafkaMsg.AssertExpectations(s.T()) } func (s *esProcessorSuite) TestAdd_ConcurrentAdd() { request := &bulk.GenericBulkableAddRequest{RequestType: bulk.BulkableIndexRequest} mockKafkaMsg := &msgMocks.Message{} key := "test-key" addFunc := func(wg *sync.WaitGroup) { s.mockScope.On("StartTimer", testMetric).Return(testStopWatch).Once() s.esProcessor.Add(request, key, mockKafkaMsg) wg.Done() } duplicates := 5 wg := &sync.WaitGroup{} wg.Add(duplicates) s.mockBulkProcessor.On("Add", request).Return().Once() mockKafkaMsg.On("Ack").Return(nil).Times(duplicates - 1) for i := 0; i < duplicates; i++ { addFunc(wg) } wg.Wait() mockKafkaMsg.AssertExpectations(s.T()) } func (s *esProcessorSuite) TestBulkAfterActionX() { version := int64(3) testKey := "testKey" request := &mocks2.GenericBulkableRequest{} request.On("String").Return("") request.On("Source").Return([]string{string(`{"delete":{"_id":"testKey"}}`)}, nil) requests := []bulk.GenericBulkableRequest{request} mSuccess := map[string]*bulk.GenericBulkResponseItem{ "index": { Index: testIndex, Type: testType, ID: testID, Version: version, Status: 200, }, } response := &bulk.GenericBulkResponse{ Took: 3, Errors: false, Items: []map[string]*bulk.GenericBulkResponseItem{mSuccess}, } mockKafkaMsg := &msgMocks.Message{} mapVal := newKafkaMessageWithMetrics(mockKafkaMsg, &testStopWatch) s.esProcessor.mapToKafkaMsg.Put(testKey, mapVal) mockKafkaMsg.On("Ack").Return(nil).Once() s.esProcessor.bulkAfterAction(0, requests, response, nil) mockKafkaMsg.AssertExpectations(s.T()) } func (s *esProcessorSuite) TestBulkAfterAction_Nack() { version := int64(3) testKey := "testKey" request := &mocks2.GenericBulkableRequest{} request.On("String").Return("") request.On("Source").Return([]string{string(`{"delete":{"_id":"testKey"}}`)}, nil) requests := []bulk.GenericBulkableRequest{request} mFailed := map[string]*bulk.GenericBulkResponseItem{ "index": { Index: testIndex, Type: testType, ID: testID, Version: version, Status: 400, }, } response := &bulk.GenericBulkResponse{ Took: 3, Errors: false, Items: []map[string]*bulk.GenericBulkResponseItem{mFailed}, } wid := "test-workflowID" rid := "test-runID" domainID := "test-domainID" payload := s.getEncodedMsg(wid, rid, domainID) mockKafkaMsg := &msgMocks.Message{} mapVal := newKafkaMessageWithMetrics(mockKafkaMsg, &testStopWatch) s.esProcessor.mapToKafkaMsg.Put(testKey, mapVal) mockKafkaMsg.On("Nack").Return(nil).Once() mockKafkaMsg.On("Value").Return(payload).Once() // s.mockBulkProcessor.On("RetrieveKafkaKey", request, mock.Anything, mock.Anything).Return(testKey) s.esProcessor.bulkAfterAction(0, requests, response, nil) mockKafkaMsg.AssertExpectations(s.T()) } func (s *esProcessorSuite) TestBulkAfterAction_Error() { version := int64(3) testKey := "testKey" request := &mocks2.GenericBulkableRequest{} request.On("String").Return("") request.On("Source").Return([]string{string(`{"delete":{"_id":"testKey"}}`)}, nil) requests := []bulk.GenericBulkableRequest{request} mFailed := map[string]*bulk.GenericBulkResponseItem{ "index": { Index: testIndex, Type: testType, ID: testID, Version: version, Status: 400, }, } response := &bulk.GenericBulkResponse{ Took: 3, Errors: true, Items: []map[string]*bulk.GenericBulkResponseItem{mFailed}, } wid := "test-workflowID" rid := "test-runID" domainID := "test-domainID" payload := s.getEncodedMsg(wid, rid, domainID) mockKafkaMsg := &msgMocks.Message{} mapVal := newKafkaMessageWithMetrics(mockKafkaMsg, &testStopWatch) s.esProcessor.mapToKafkaMsg.Put(testKey, mapVal) mockKafkaMsg.On("Nack").Return(nil).Once() mockKafkaMsg.On("Value").Return(payload).Once() s.mockScope.On("IncCounter", metrics.ESProcessorFailures).Once() s.esProcessor.bulkAfterAction(0, requests, response, &bulk.GenericError{Details: fmt.Errorf("some error")}) } func (s *esProcessorSuite) TestBulkAfterAction_Error_Nack() { version := int64(3) testKey := "testKey" request := &mocks2.GenericBulkableRequest{} request.On("String").Return("") request.On("Source").Return([]string{`{"delete":{"_index":"test-index","_id":"testKey"}}`}, nil) requests := []bulk.GenericBulkableRequest{request} mFailed := map[string]*bulk.GenericBulkResponseItem{ "delete": { Index: testIndex, Type: testType, ID: testID, Version: version, Status: 409, }, } response := &bulk.GenericBulkResponse{ Took: 3, Errors: true, Items: []map[string]*bulk.GenericBulkResponseItem{mFailed}, } wid := "test-workflowID" rid := "test-runID" domainID := "test-domainID" payload := s.getEncodedMsg(wid, rid, domainID) mockKafkaMsg := &msgMocks.Message{} mapVal := newKafkaMessageWithMetrics(mockKafkaMsg, &testStopWatch) s.esProcessor.mapToKafkaMsg.Put(testKey, mapVal) mockKafkaMsg.On("Nack").Return(nil).Once() mockKafkaMsg.On("Ack").Return(nil).Once() // Expect Ack to be called mockKafkaMsg.On("Value").Return(payload).Once() s.mockScope.On("IncCounter", metrics.ESProcessorFailures).Once() s.esProcessor.bulkAfterAction(0, requests, response, &bulk.GenericError{Status: 404, Details: fmt.Errorf("some error")}) } func (s *esProcessorSuite) TestAckKafkaMsg() { key := "test-key" // no msg in map, nothing called s.esProcessor.ackKafkaMsg(key) request := &bulk.GenericBulkableAddRequest{} mockKafkaMsg := &msgMocks.Message{} s.mockScope.On("StartTimer", testMetric).Return(testStopWatch).Once() s.mockBulkProcessor.On("Add", request).Return().Once() s.esProcessor.Add(request, key, mockKafkaMsg) s.Equal(1, s.esProcessor.mapToKafkaMsg.Len()) mockKafkaMsg.On("Ack").Return(nil).Once() s.esProcessor.ackKafkaMsg(key) mockKafkaMsg.AssertExpectations(s.T()) s.Equal(0, s.esProcessor.mapToKafkaMsg.Len()) } func (s *esProcessorSuite) TestNackKafkaMsg() { key := "test-key-nack" // no msg in map, nothing called s.esProcessor.nackKafkaMsg(key) request := &bulk.GenericBulkableAddRequest{} mockKafkaMsg := &msgMocks.Message{} s.mockBulkProcessor.On("Add", request).Return().Once() s.mockScope.On("StartTimer", testMetric).Return(testStopWatch).Once() s.esProcessor.Add(request, key, mockKafkaMsg) s.Equal(1, s.esProcessor.mapToKafkaMsg.Len()) mockKafkaMsg.On("Nack").Return(nil).Once() s.esProcessor.nackKafkaMsg(key) mockKafkaMsg.AssertExpectations(s.T()) s.Equal(0, s.esProcessor.mapToKafkaMsg.Len()) } func (s *esProcessorSuite) TestHashFn() { s.Equal(uint32(0), s.esProcessor.hashFn(0)) s.NotEqual(uint32(0), s.esProcessor.hashFn("test")) } func (s *esProcessorSuite) getEncodedMsg(wid string, rid string, domainID string) []byte { indexMsg := &indexer.Message{ DomainID: common.StringPtr(domainID), WorkflowID: common.StringPtr(wid), RunID: common.StringPtr(rid), } payload, err := s.esProcessor.msgEncoder.Encode(indexMsg) s.NoError(err) return payload } func (s *esProcessorSuite) TestGetMsgWithInfo() { testKey := "test-key" testWid := "test-workflowID" testRid := "test-runID" testDomainid := "test-domainID" payload := s.getEncodedMsg(testWid, testRid, testDomainid) mockKafkaMsg := &msgMocks.Message{} mockKafkaMsg.On("Value").Return(payload).Once() mapVal := newKafkaMessageWithMetrics(mockKafkaMsg, &testStopWatch) s.esProcessor.mapToKafkaMsg.Put(testKey, mapVal) wid, rid, domainID := s.esProcessor.getMsgWithInfo(testKey) s.Equal(testWid, wid) s.Equal(testRid, rid) s.Equal(testDomainid, domainID) } func (s *esProcessorSuite) TestGetMsgInfo_Error() { testKey := "test-key" mockKafkaMsg := &msgMocks.Message{} mockKafkaMsg.On("Value").Return([]byte{}).Once() mapVal := newKafkaMessageWithMetrics(mockKafkaMsg, &testStopWatch) s.esProcessor.mapToKafkaMsg.Put(testKey, mapVal) wid, rid, domainID := s.esProcessor.getMsgWithInfo(testKey) s.Equal("", wid) s.Equal("", rid) s.Equal("", domainID) } func (s *esProcessorSuite) TestIsResponseSuccess() { for i := 200; i < 300; i++ { s.True(isResponseSuccess(i)) } status := []int{409, 404} for _, code := range status { s.True(isResponseSuccess(code)) } status = []int{100, 199, 300, 400, 500, 408, 429, 503, 507} for _, code := range status { s.False(isResponseSuccess(code)) } } func (s *esProcessorSuite) TestIsResponseRetriable() { status := []int{408, 429, 500, 503, 507} for _, code := range status { s.True(isResponseRetriable(code)) } } func (s *esProcessorSuite) TestIsErrorRetriable() { tests := []struct { input *bulk.GenericError expected bool }{ { input: &bulk.GenericError{Status: 400}, expected: false, }, { input: &bulk.GenericError{Status: 408}, expected: true, }, { input: &bulk.GenericError{}, expected: false, }, } for _, test := range tests { s.Equal(test.expected, isResponseRetriable(test.input.Status)) } } func (s *esProcessorSuite) TestIsDeleteRequest() { tests := []struct { request bulk.GenericBulkableRequest bIsDelete bool }{ { request: bulk.NewBulkIndexRequest(). ID("request.ID"). Index("request.Index"). Version(int64(0)). VersionType("request.VersionType").Doc("request.Doc"), bIsDelete: false, }, { request: bulk.NewBulkDeleteRequest(). ID("request.ID"). Index("request.Index"), bIsDelete: true, }, } for _, test := range tests { req, _ := test.request.Source() s.Equal(test.bIsDelete, s.esProcessor.isDeleteRequest(req)) } } func (s *esProcessorSuite) TestIsDeleteRequest_Error() { request := &MockBulkableRequest{} s.mockScope.On("IncCounter", mock.AnythingOfType("int")).Return() req, err := request.Source() s.False(s.esProcessor.isDeleteRequest(req)) s.Error(err) } // MockBulkableRequest is a mock implementation of the GenericBulkableRequest interface type MockBulkableRequest struct{} // String returns a mock string func (m *MockBulkableRequest) String() string { return "mock request" } // Source returns an error to simulate a failure func (m *MockBulkableRequest) Source() ([]string, error) { return nil, fmt.Errorf("simulated source error") } func (s *esProcessorSuite) TestBulkAfterAction_Nack_Shadow_WithError() { version := int64(3) testKey := "testKey" request := &mocks2.GenericBulkableRequest{} request.On("String").Return("") request.On("Source").Return([]string{string(`{"delete":{"_id":"testKey"}}`)}, nil) requests := []bulk.GenericBulkableRequest{request} mFailed := map[string]*bulk.GenericBulkResponseItem{ "index": { Index: testIndex, Type: testType, ID: testID, Version: version, Status: 400, }, } response := &bulk.GenericBulkResponse{ Took: 3, Errors: false, Items: []map[string]*bulk.GenericBulkResponseItem{mFailed}, } // Mock error to be passed to the after action functions mockErr := &bulk.GenericError{ Status: 500, Details: fmt.Errorf("Test error occurred"), } wid := "test-workflowID" rid := "test-runID" domainID := "test-domainID" payload := s.getEncodedMsg(wid, rid, domainID) mockKafkaMsg := &msgMocks.Message{} mapVal := newKafkaMessageWithMetrics(mockKafkaMsg, &testStopWatch) s.esProcessor.mapToKafkaMsg.Put(testKey, mapVal) // Mock Kafka message Nack and Value mockKafkaMsg.On("Nack").Return(nil).Once() mockKafkaMsg.On("Value").Return(payload).Once() s.mockScope.On("IncCounter", mock.AnythingOfType("metrics.MetricIdx")).Return() // Execute bulkAfterAction for primary processor with error s.esProcessor.bulkAfterAction(0, requests, response, mockErr) } func (s *esProcessorSuite) TestBulkAfterAction_Shadow_Fail_WithoutError() { version := int64(3) testKey := "testKey" request := &mocks2.GenericBulkableRequest{} request.On("String").Return("") request.On("Source").Return([]string{string(`{"delete":{"_id":"testKey"}}`)}, nil) requests := []bulk.GenericBulkableRequest{request} mFailed := map[string]*bulk.GenericBulkResponseItem{ "index": { Index: testIndex, Type: testType, ID: testID, Version: version, Status: 400, }, } response := &bulk.GenericBulkResponse{ Took: 3, Errors: false, Items: []map[string]*bulk.GenericBulkResponseItem{mFailed}, } wid := "test-workflowID" rid := "test-runID" domainID := "test-domainID" payload := s.getEncodedMsg(wid, rid, domainID) mockKafkaMsg := &msgMocks.Message{} mapVal := newKafkaMessageWithMetrics(mockKafkaMsg, &testStopWatch) s.esProcessor.mapToKafkaMsg.Put(testKey, mapVal) // Mock Kafka message Nack and Value mockKafkaMsg.On("Nack").Return(nil).Once() mockKafkaMsg.On("Value").Return(payload).Once() s.mockScope.On("IncCounter", mock.AnythingOfType("int")).Return() // Execute bulkAfterAction for primary processor with error s.esProcessor.bulkAfterAction(0, requests, response, nil) } ================================================ FILE: service/worker/indexer/indexer.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination indexer_mock.go github.com/uber/cadence/service/worker/indexer ESProcessor package indexer import ( "encoding/json" "fmt" "sync" "sync/atomic" "time" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" es "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/elasticsearch/bulk" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) const ( versionTypeExternal = "external" processorName = "visibility-processor" migrationProcessorName = "migration-visibility-processor" ) var ( errUnknownMessageType = &types.BadRequestError{Message: "unknown message type"} defaultEncoder = codec.NewThriftRWEncoder() ) type ( ESProcessor interface { common.Daemon Add(request *bulk.GenericBulkableAddRequest, key string, kafkaMsg messaging.Message) } // Indexer used to consumer data from kafka then send to ElasticSearch Indexer struct { esIndexName string consumer messaging.Consumer visibilityProcessor ESProcessor config *Config logger log.Logger scope metrics.Scope msgEncoder codec.BinaryEncoder isStarted int32 isStopped int32 shutdownWG sync.WaitGroup shutdownCh chan struct{} } DualIndexer struct { SourceIndexer *Indexer DestIndexer *Indexer } // Config contains all configs for indexer Config struct { IndexerConcurrency dynamicproperties.IntPropertyFn ESProcessorNumOfWorkers dynamicproperties.IntPropertyFn ESProcessorBulkActions dynamicproperties.IntPropertyFn // max number of requests in bulk ESProcessorBulkSize dynamicproperties.IntPropertyFn // max total size of bytes in bulk ESProcessorFlushInterval dynamicproperties.DurationPropertyFn ValidSearchAttributes dynamicproperties.MapPropertyFn EnableQueryAttributeValidation dynamicproperties.BoolPropertyFn } ) // NewIndexer create a new Indexer func NewIndexer( config *Config, client messaging.Client, visibilityClient es.GenericClient, visibilityName string, consumerName string, logger log.Logger, metricsClient metrics.Client, ) *Indexer { logger = logger.WithTags(tag.ComponentIndexer) visibilityProcessor, err := newESProcessor(processorName, config, visibilityClient, logger, metricsClient) if err != nil { logger.Fatal("Index ES processor state changed", tag.LifeCycleStartFailed, tag.Error(err)) } if consumerName == "" { consumerName = getConsumerName(visibilityName) } consumer, err := client.NewConsumer(constants.VisibilityAppName, consumerName) if err != nil { logger.Fatal("Index consumer state changed", tag.LifeCycleStartFailed, tag.Error(err)) } return &Indexer{ config: config, esIndexName: visibilityName, consumer: consumer, logger: logger.WithTags(tag.ComponentIndexerProcessor), scope: metricsClient.Scope(metrics.IndexProcessorScope), shutdownCh: make(chan struct{}), visibilityProcessor: visibilityProcessor, msgEncoder: defaultEncoder, } } func getConsumerName(topic string) string { return fmt.Sprintf("%s-consumer", topic) } func (i *Indexer) Start() error { if !atomic.CompareAndSwapInt32(&i.isStarted, 0, 1) { return nil } if err := i.consumer.Start(); err != nil { i.logger.Info("Index consumer state changed", tag.LifeCycleStartFailed, tag.Error(err)) return err } i.visibilityProcessor.Start() i.shutdownWG.Add(1) go i.processorPump() i.logger.Info("Index bulkProcessor state changed", tag.LifeCycleStarted) return nil } func (i *Indexer) Stop() { if !atomic.CompareAndSwapInt32(&i.isStopped, 0, 1) { return } i.logger.Info("Index bulkProcessor state changed", tag.LifeCycleStopping) defer i.logger.Info("Index bulkProcessor state changed", tag.LifeCycleStopped) if atomic.LoadInt32(&i.isStarted) == 1 { close(i.shutdownCh) } if success := common.AwaitWaitGroup(&i.shutdownWG, time.Minute); !success { i.logger.Info("Index bulkProcessor state changed", tag.LifeCycleStopTimedout) } } func (i *Indexer) processorPump() { defer i.shutdownWG.Done() var workerWG sync.WaitGroup for workerID := 0; workerID < i.config.IndexerConcurrency(); workerID++ { workerWG.Add(1) go i.messageProcessLoop(&workerWG) } <-i.shutdownCh // Processor is shutting down, close the underlying consumer and esProcessor i.consumer.Stop() i.visibilityProcessor.Stop() i.logger.Info("Index bulkProcessor pump shutting down.") if success := common.AwaitWaitGroup(&workerWG, 10*time.Second); !success { i.logger.Warn("Index bulkProcessor timed out on worker shutdown.") } } func (i *Indexer) messageProcessLoop(workerWG *sync.WaitGroup) { defer workerWG.Done() for msg := range i.consumer.Messages() { sw := i.scope.StartTimer(metrics.IndexProcessorProcessMsgLatency) err := i.process(msg) sw.Stop() if err != nil { msg.Nack() //nolint:errcheck } } } func (i *Indexer) process(kafkaMsg messaging.Message) error { logger := i.logger.WithTags(tag.KafkaPartition(kafkaMsg.Partition()), tag.KafkaOffset(kafkaMsg.Offset()), tag.AttemptStart(time.Now())) indexMsg, err := i.deserialize(kafkaMsg.Value()) if err != nil { logger.Error("Failed to deserialize index messages.", tag.Error(err)) i.scope.IncCounter(metrics.IndexProcessorCorruptedData) return err } return i.addMessageToES(indexMsg, kafkaMsg, logger) } func (i *Indexer) deserialize(payload []byte) (*indexer.Message, error) { var msg indexer.Message if err := i.msgEncoder.Decode(payload, &msg); err != nil { return nil, err } return &msg, nil } func (i *Indexer) addMessageToES(indexMsg *indexer.Message, kafkaMsg messaging.Message, logger log.Logger) error { docID := es.GenerateDocID(indexMsg.GetWorkflowID(), indexMsg.GetRunID()) // check and skip invalid docID if len(docID) >= es.GetESDocIDSizeLimit() { logger.Error("Index message is too long", tag.WorkflowDomainID(indexMsg.GetDomainID()), tag.WorkflowID(indexMsg.GetWorkflowID()), tag.WorkflowRunID(indexMsg.GetRunID())) kafkaMsg.Nack() return nil } var keyToKafkaMsg string req := &bulk.GenericBulkableAddRequest{ Index: i.esIndexName, Type: es.GetESDocType(), ID: docID, VersionType: versionTypeExternal, Version: indexMsg.GetVersion(), } switch indexMsg.GetMessageType() { case indexer.MessageTypeIndex: keyToKafkaMsg = fmt.Sprintf("%v-%v", kafkaMsg.Partition(), kafkaMsg.Offset()) req.Doc = i.generateESDoc(indexMsg, keyToKafkaMsg) req.RequestType = bulk.BulkableIndexRequest case indexer.MessageTypeDelete: keyToKafkaMsg = docID req.RequestType = bulk.BulkableDeleteRequest case indexer.MessageTypeCreate: keyToKafkaMsg = fmt.Sprintf("%v-%v", kafkaMsg.Partition(), kafkaMsg.Offset()) req.Doc = i.generateESDoc(indexMsg, keyToKafkaMsg) req.RequestType = bulk.BulkableCreateRequest default: logger.Error("Unknown message type") i.scope.IncCounter(metrics.IndexProcessorCorruptedData) return errUnknownMessageType } i.visibilityProcessor.Add(req, keyToKafkaMsg, kafkaMsg) return nil } func (i *Indexer) generateESDoc(msg *indexer.Message, keyToKafkaMsg string) map[string]interface{} { doc := i.dumpFieldsToMap(msg.Fields, msg.GetDomainID()) fulfillDoc(doc, msg, keyToKafkaMsg) return doc } func (i *Indexer) decodeSearchAttrBinary(bytes []byte, key string) interface{} { var val interface{} err := json.Unmarshal(bytes, &val) if err != nil { i.logger.Error("Error when decode search attributes values.", tag.Error(err), tag.ESField(key)) i.scope.IncCounter(metrics.IndexProcessorCorruptedData) } return val } func (i *Indexer) dumpFieldsToMap(fields map[string]*indexer.Field, domainID string) map[string]interface{} { doc := make(map[string]interface{}) attr := make(map[string]interface{}) for k, v := range fields { if !i.isValidFieldToES(k) { i.logger.Error("Unregistered field.", tag.ESField(k), tag.WorkflowDomainID(domainID)) i.scope.IncCounter(metrics.IndexProcessorCorruptedData) continue } // skip VisibilityOperation since it’s not being used for advanced visibility if k == es.VisibilityOperation { continue } switch v.GetType() { case indexer.FieldTypeString: doc[k] = v.GetStringData() case indexer.FieldTypeInt: doc[k] = v.GetIntData() case indexer.FieldTypeBool: doc[k] = v.GetBoolData() case indexer.FieldTypeBinary: if k == definition.Memo { doc[k] = v.GetBinaryData() } else { // custom search attributes attr[k] = i.decodeSearchAttrBinary(v.GetBinaryData(), k) } default: // there must be bug in code and bad deployment, check data sent from producer i.logger.Fatal("Unknown field type") } } doc[definition.Attr] = attr return doc } func (i *Indexer) isValidFieldToES(field string) bool { if !i.config.EnableQueryAttributeValidation() { return true } if _, ok := i.config.ValidSearchAttributes()[field]; ok { return true } if field == definition.Memo || field == definition.KafkaKey || field == definition.Encoding || field == es.VisibilityOperation { return true } return false } func fulfillDoc(doc map[string]interface{}, msg *indexer.Message, keyToKafkaMsg string) { doc[definition.DomainID] = msg.GetDomainID() doc[definition.WorkflowID] = msg.GetWorkflowID() doc[definition.RunID] = msg.GetRunID() doc[definition.KafkaKey] = keyToKafkaMsg } ================================================ FILE: service/worker/indexer/indexer_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: indexer.go // // Generated by this command: // // mockgen -package indexer -source indexer.go -destination indexer_mock.go github.com/uber/cadence/service/worker/indexer ESProcessor // // Package indexer is a generated GoMock package. package indexer import ( reflect "reflect" gomock "go.uber.org/mock/gomock" bulk "github.com/uber/cadence/common/elasticsearch/bulk" messaging "github.com/uber/cadence/common/messaging" ) // MockESProcessor is a mock of ESProcessor interface. type MockESProcessor struct { ctrl *gomock.Controller recorder *MockESProcessorMockRecorder isgomock struct{} } // MockESProcessorMockRecorder is the mock recorder for MockESProcessor. type MockESProcessorMockRecorder struct { mock *MockESProcessor } // NewMockESProcessor creates a new mock instance. func NewMockESProcessor(ctrl *gomock.Controller) *MockESProcessor { mock := &MockESProcessor{ctrl: ctrl} mock.recorder = &MockESProcessorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockESProcessor) EXPECT() *MockESProcessorMockRecorder { return m.recorder } // Add mocks base method. func (m *MockESProcessor) Add(request *bulk.GenericBulkableAddRequest, key string, kafkaMsg messaging.Message) { m.ctrl.T.Helper() m.ctrl.Call(m, "Add", request, key, kafkaMsg) } // Add indicates an expected call of Add. func (mr *MockESProcessorMockRecorder) Add(request, key, kafkaMsg any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockESProcessor)(nil).Add), request, key, kafkaMsg) } // Start mocks base method. func (m *MockESProcessor) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockESProcessorMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockESProcessor)(nil).Start)) } // Stop mocks base method. func (m *MockESProcessor) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockESProcessorMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockESProcessor)(nil).Stop)) } ================================================ FILE: service/worker/indexer/indexer_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package indexer import ( "fmt" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common" "github.com/uber/cadence/common/definition" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/elasticsearch/bulk" mocks2 "github.com/uber/cadence/common/elasticsearch/bulk/mocks" esMocks "github.com/uber/cadence/common/elasticsearch/mocks" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" ) func TestNewDualIndexer(t *testing.T) { ctrl := gomock.NewController(t) config := &Config{ ESProcessorNumOfWorkers: dynamicproperties.GetIntPropertyFn(1), ESProcessorBulkActions: dynamicproperties.GetIntPropertyFn(10), ESProcessorBulkSize: dynamicproperties.GetIntPropertyFn(2 << 20), ESProcessorFlushInterval: dynamicproperties.GetDurationPropertyFn(1 * time.Minute), } processorName := "test-bulkProcessor" consumerName := "test-bulkProcessor-os-consumer" mockESClient := &esMocks.GenericClient{} mockESClient.On("RunBulkProcessor", mock.Anything, mock.MatchedBy(func(input *bulk.BulkProcessorParameters) bool { return true })).Return(&mocks2.GenericBulkProcessor{}, nil).Times(2) mockMessagingClient := messaging.NewMockClient(ctrl) mockMessagingClient.EXPECT().NewConsumer("visibility", "test-bulkProcessor-consumer").Return(nil, nil).Times(1) mockMessagingClient.EXPECT().NewConsumer("visibility", "test-bulkProcessor-os-consumer").Return(nil, nil).Times(1) indexer := NewMigrationDualIndexer(config, mockMessagingClient, mockESClient, mockESClient, processorName, processorName, "", consumerName, testlogger.New(t), metrics.NewNoopMetricsClient()) assert.NotNil(t, indexer) } func TestNewIndexer(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() config := &Config{ ESProcessorNumOfWorkers: dynamicproperties.GetIntPropertyFn(1), ESProcessorBulkActions: dynamicproperties.GetIntPropertyFn(10), ESProcessorBulkSize: dynamicproperties.GetIntPropertyFn(2 << 20), ESProcessorFlushInterval: dynamicproperties.GetDurationPropertyFn(1 * time.Minute), } processorName := "test-bulkProcessor" mockESClient := &esMocks.GenericClient{} mockESClient.On("RunBulkProcessor", mock.Anything, mock.MatchedBy(func(input *bulk.BulkProcessorParameters) bool { return true })).Return(&mocks2.GenericBulkProcessor{}, nil).Times(2) mockMessagingClient := messaging.NewMockClient(ctrl) mockMessagingClient.EXPECT().NewConsumer("visibility", "test-bulkProcessor-consumer").Return(nil, nil).Times(1) indexer := NewIndexer(config, mockMessagingClient, mockESClient, processorName, "", testlogger.New(t), metrics.NewNoopMetricsClient()) assert.NotNil(t, indexer) } // TestIndexerStart tests the Start method of Indexer func TestIndexerStart(t *testing.T) { ctrl := gomock.NewController(t) config := &Config{ ESProcessorNumOfWorkers: dynamicproperties.GetIntPropertyFn(1), ESProcessorBulkActions: dynamicproperties.GetIntPropertyFn(10), ESProcessorBulkSize: dynamicproperties.GetIntPropertyFn(2 << 20), ESProcessorFlushInterval: dynamicproperties.GetDurationPropertyFn(1 * time.Minute), IndexerConcurrency: dynamicproperties.GetIntPropertyFn(1), } mockConsumer := messaging.NewMockConsumer(ctrl) mockConsumer.EXPECT().Start().Return(nil).Times(1) messageChan := make(chan messaging.Message) mockConsumer.EXPECT().Messages().Return((<-chan messaging.Message)(messageChan)).Times(1) mockConsumer.EXPECT().Stop().Return().Times(1) mockvisibiltyProcessor := NewMockESProcessor(ctrl) mockvisibiltyProcessor.EXPECT().Start().Return().Times(1) mockvisibiltyProcessor.EXPECT().Stop().Return().Times(1) indexer := &Indexer{ config: config, esIndexName: "test-index", consumer: mockConsumer, logger: log.NewNoop(), scope: metrics.NoopScope, shutdownCh: make(chan struct{}), visibilityProcessor: mockvisibiltyProcessor, msgEncoder: defaultEncoder, } err := indexer.Start() assert.NoError(t, err) close(messageChan) indexer.Stop() defer goleak.VerifyNone(t) } // TestIndexerStart_ConsumerError tests the Start method when consumer.Start returns an error func TestIndexerStart_ConsumerError(t *testing.T) { ctrl := gomock.NewController(t) config := &Config{ ESProcessorNumOfWorkers: dynamicproperties.GetIntPropertyFn(1), ESProcessorBulkActions: dynamicproperties.GetIntPropertyFn(10), ESProcessorBulkSize: dynamicproperties.GetIntPropertyFn(2 << 20), ESProcessorFlushInterval: dynamicproperties.GetDurationPropertyFn(1 * time.Minute), IndexerConcurrency: dynamicproperties.GetIntPropertyFn(1), } mockConsumer := messaging.NewMockConsumer(ctrl) mockConsumer.EXPECT().Start().Return(fmt.Errorf("some error")).Times(1) mockvisibiltyProcessor := NewMockESProcessor(ctrl) indexer := &Indexer{ config: config, esIndexName: "test-index", consumer: mockConsumer, logger: log.NewNoop(), scope: metrics.NoopScope, shutdownCh: make(chan struct{}), visibilityProcessor: mockvisibiltyProcessor, msgEncoder: defaultEncoder, } err := indexer.Start() assert.ErrorContains(t, err, "some error") } func TestIndexerStop(t *testing.T) { ctrl := gomock.NewController(t) config := &Config{ ESProcessorNumOfWorkers: dynamicproperties.GetIntPropertyFn(1), ESProcessorBulkActions: dynamicproperties.GetIntPropertyFn(10), ESProcessorBulkSize: dynamicproperties.GetIntPropertyFn(2 << 20), ESProcessorFlushInterval: dynamicproperties.GetDurationPropertyFn(1 * time.Minute), IndexerConcurrency: dynamicproperties.GetIntPropertyFn(1), } // Mock the messaging consumer mockConsumer := messaging.NewMockConsumer(ctrl) messageChan := make(chan messaging.Message) mockConsumer.EXPECT().Messages().Return((<-chan messaging.Message)(messageChan)).AnyTimes() // No specific expectations for Start or Stop since they're not called in Stop() // Mock the visibility processor mockVisibilityProcessor := NewMockESProcessor(ctrl) // Create the Indexer instance with mocks indexer := &Indexer{ config: config, esIndexName: "test-index", consumer: mockConsumer, logger: log.NewNoop(), scope: metrics.NoopScope, shutdownCh: make(chan struct{}), visibilityProcessor: mockVisibilityProcessor, msgEncoder: defaultEncoder, } // Simulate that the indexer was started atomic.StoreInt32(&indexer.isStarted, 1) // Simulate the processorPump goroutine that waits on shutdownCh indexer.shutdownWG.Add(1) go func() { defer indexer.shutdownWG.Done() <-indexer.shutdownCh }() // Call Stop and verify behavior indexer.Stop() // Verify that shutdownCh is closed select { case <-indexer.shutdownCh: // Expected: shutdownCh should be closed default: t.Error("shutdownCh is not closed") } // Verify that the WaitGroup has completed success := common.AwaitWaitGroup(&indexer.shutdownWG, time.Second) assert.True(t, success) // Verify that isStopped flag is set assert.Equal(t, int32(1), atomic.LoadInt32(&indexer.isStopped)) // Call Stop again to ensure idempotency indexer.Stop() defer goleak.VerifyNone(t) } func TestIsValidFieldToES(t *testing.T) { tests := map[string]struct { testIndexer *Indexer field string expectedRes bool }{ "not EnableQueryAttributeValidation": { testIndexer: &Indexer{ config: &Config{ EnableQueryAttributeValidation: dynamicproperties.GetBoolPropertyFn(false), }, }, field: "someField", expectedRes: true, }, "field is valid": { testIndexer: &Indexer{ config: &Config{ EnableQueryAttributeValidation: dynamicproperties.GetBoolPropertyFn(true), ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(map[string]interface{}{"someField": "ok"}), }, }, field: "someField", expectedRes: true, }, "field is not valid, but meet definition": { testIndexer: &Indexer{ config: &Config{ EnableQueryAttributeValidation: dynamicproperties.GetBoolPropertyFn(true), ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, }, field: definition.Memo, expectedRes: true, }, "false": { testIndexer: &Indexer{ config: &Config{ EnableQueryAttributeValidation: dynamicproperties.GetBoolPropertyFn(true), ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, }, field: "stuff", expectedRes: false, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { res := tc.testIndexer.isValidFieldToES(tc.field) assert.Equal(t, tc.expectedRes, res) }) } } func TestFulfillDoc_AllFieldsPresent(t *testing.T) { domainID := "domain1" workflowID := "workflow1" runID := "run1" keyToKafkaMsg := "kafka-key-1" doc := map[string]interface{}{} msg := &indexer.Message{ DomainID: &domainID, WorkflowID: &workflowID, RunID: &runID, } expectedDoc := map[string]interface{}{ definition.DomainID: domainID, definition.WorkflowID: workflowID, definition.RunID: runID, definition.KafkaKey: keyToKafkaMsg, } fulfillDoc(doc, msg, keyToKafkaMsg) assert.Equal(t, expectedDoc, doc, "fulfillDoc() result mismatch") } func TestDumpFieldsToMap(t *testing.T) { testIndexer := &Indexer{ config: &Config{ EnableQueryAttributeValidation: dynamicproperties.GetBoolPropertyFn(true), ValidSearchAttributes: dynamicproperties.GetMapPropertyFn(map[string]interface{}{}), }, logger: log.NewNoop(), scope: metrics.NoopScope, } stringPtr := "string" tests := map[string]struct { fields map[string]*indexer.Field expected map[string]interface{} }{ "empty fields": { fields: map[string]*indexer.Field{}, expected: map[string]interface{}{ "Attr": map[string]interface{}{}, }, }, "different fields": { fields: map[string]*indexer.Field{ "invalid": { StringData: &stringPtr, }, }, expected: map[string]interface{}{ "Attr": map[string]interface{}{}, }, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { res := testIndexer.dumpFieldsToMap(tc.fields, "id") assert.Equal(t, tc.expected, res) }) } } ================================================ FILE: service/worker/indexer/migration_indexer.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package indexer import ( "github.com/uber/cadence/common/constants" es "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/messaging" "github.com/uber/cadence/common/metrics" ) // NewMigrationDualIndexer create a new Indexer that will be used during visibility migration // When migrate from ES to OS, we will have this indexer to index to both ES and OS func NewMigrationDualIndexer(config *Config, client messaging.Client, primaryClient es.GenericClient, secondaryClient es.GenericClient, primaryVisibilityName string, secondaryVisibilityName string, primaryConsumerName string, secondaryConsumerName string, logger log.Logger, metricsClient metrics.Client) *DualIndexer { logger = logger.WithTags(tag.ComponentIndexer) visibilityProcessor, err := newESProcessor(processorName, config, primaryClient, logger, metricsClient) if err != nil { logger.Fatal("Index ES processor state changed", tag.LifeCycleStartFailed, tag.Error(err)) } if primaryConsumerName == "" { primaryConsumerName = getConsumerName(primaryVisibilityName) } consumer, err := client.NewConsumer(constants.VisibilityAppName, primaryConsumerName) if err != nil { logger.Fatal("Index consumer state changed", tag.LifeCycleStartFailed, tag.Error(err)) } sourceIndexer := &Indexer{ config: config, esIndexName: primaryVisibilityName, consumer: consumer, logger: logger.WithTags(tag.ComponentIndexerProcessor), scope: metricsClient.Scope(metrics.IndexProcessorScope), shutdownCh: make(chan struct{}), visibilityProcessor: visibilityProcessor, msgEncoder: defaultEncoder, } secondaryVisibilityProcessor, err := newESProcessor(migrationProcessorName, config, secondaryClient, logger, metricsClient) if err != nil { logger.Fatal("Migration Index ES processor state changed", tag.LifeCycleStartFailed, tag.Error(err)) } if secondaryConsumerName == "" { secondaryConsumerName = getConsumerName(primaryVisibilityName) } secondaryConsumer, err := client.NewConsumer(constants.VisibilityAppName, secondaryConsumerName) if err != nil { logger.Fatal("Migration Index consumer state changed", tag.LifeCycleStartFailed, tag.Error(err)) } destIndexer := &Indexer{ config: config, esIndexName: secondaryVisibilityName, consumer: secondaryConsumer, logger: logger.WithTags(tag.ComponentIndexerProcessor), scope: metricsClient.Scope(metrics.IndexProcessorScope), shutdownCh: make(chan struct{}), visibilityProcessor: secondaryVisibilityProcessor, msgEncoder: defaultEncoder, } return &DualIndexer{ SourceIndexer: sourceIndexer, DestIndexer: destIndexer, } } func (i *DualIndexer) Start() error { if err := i.SourceIndexer.Start(); err != nil { i.SourceIndexer.Stop() return err } if err := i.DestIndexer.Start(); err != nil { i.DestIndexer.Stop() return err } return nil } func (i *DualIndexer) Stop() { i.SourceIndexer.Stop() i.DestIndexer.Stop() } ================================================ FILE: service/worker/parentclosepolicy/client.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package parentclosepolicy import ( "context" "fmt" "math/rand" "time" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" cclient "go.uber.org/cadence/client" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" ) //go:generate mockgen -package=$GOPACKAGE -destination=client_mock.go -self_package=github.com/uber/cadence/service/worker/parentclosepolicy github.com/uber/cadence/service/worker/parentclosepolicy Client type ( // Client is used to send request to processor workflow Client interface { SendParentClosePolicyRequest(context.Context, Request) error } clientImpl struct { metricsClient metrics.Client logger log.Logger cadenceClient cclient.Client numWorkflows int } ) var _ Client = (*clientImpl)(nil) const ( signalTimeout = 400 * time.Millisecond workflowIDPrefix = "parent-close-policy-workflow" ) // NewClient creates a new Client func NewClient( metricsClient metrics.Client, logger log.Logger, publicClient workflowserviceclient.Interface, numWorkflows int, ) Client { return &clientImpl{ metricsClient: metricsClient, logger: logger, cadenceClient: cclient.NewClient(publicClient, constants.SystemLocalDomainName, &cclient.Options{}), numWorkflows: numWorkflows, } } func (c *clientImpl) SendParentClosePolicyRequest( ctx context.Context, request Request, ) error { randomID := rand.Intn(c.numWorkflows) workflowID := fmt.Sprintf("%v-%v", workflowIDPrefix, randomID) workflowOptions := cclient.StartWorkflowOptions{ ID: workflowID, TaskList: processorTaskListName, ExecutionStartToCloseTimeout: infiniteDuration, DecisionTaskStartToCloseTimeout: time.Minute, WorkflowIDReusePolicy: cclient.WorkflowIDReusePolicyAllowDuplicate, } signalCtx, cancel := context.WithTimeout(ctx, signalTimeout) defer cancel() _, err := c.cadenceClient.SignalWithStartWorkflow(signalCtx, workflowID, processorChannelName, request, workflowOptions, processorWFTypeName, nil) return err } ================================================ FILE: service/worker/parentclosepolicy/client_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/service/worker/parentclosepolicy (interfaces: Client) // // Generated by this command: // // mockgen -package=parentclosepolicy -destination=client_mock.go -self_package=github.com/uber/cadence/service/worker/parentclosepolicy github.com/uber/cadence/service/worker/parentclosepolicy Client // // Package parentclosepolicy is a generated GoMock package. package parentclosepolicy import ( context "context" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockClient is a mock of Client interface. type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. type MockClientMockRecorder struct { mock *MockClient } // NewMockClient creates a new mock instance. func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } // SendParentClosePolicyRequest mocks base method. func (m *MockClient) SendParentClosePolicyRequest(arg0 context.Context, arg1 Request) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendParentClosePolicyRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // SendParentClosePolicyRequest indicates an expected call of SendParentClosePolicyRequest. func (mr *MockClientMockRecorder) SendParentClosePolicyRequest(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendParentClosePolicyRequest", reflect.TypeOf((*MockClient)(nil).SendParentClosePolicyRequest), arg0, arg1) } ================================================ FILE: service/worker/parentclosepolicy/processor.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package parentclosepolicy import ( "context" "github.com/opentracing/opentracing-go" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/worker" "github.com/uber/cadence/client" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" ) type ( // BootstrapParams contains the set of params needed to bootstrap // the sub-system BootstrapParams struct { // Config contains the configuration for scanner // ServiceClient is an instance of cadence service client ServiceClient workflowserviceclient.Interface // MetricsClient is an instance of metrics object for emitting stats MetricsClient metrics.Client Logger log.Logger // TallyScope is an instance of tally metrics scope TallyScope tally.Scope // ClientBean is the collection of clients ClientBean client.Bean // DomainCache is the cache for domain information and configuration DomainCache cache.DomainCache // NumWorkflows is the total number of workflows for processing parent close policy NumWorkflows int } // Processor is the background sub-system that execute workflow for ParentClosePolicy Processor struct { svcClient workflowserviceclient.Interface clientBean client.Bean domainCache cache.DomainCache numWorkflows int metricsClient metrics.Client tallyScope tally.Scope logger log.Logger } ) // New returns a new instance as daemon func New(params *BootstrapParams) *Processor { return &Processor{ svcClient: params.ServiceClient, clientBean: params.ClientBean, domainCache: params.DomainCache, numWorkflows: params.NumWorkflows, metricsClient: params.MetricsClient, tallyScope: params.TallyScope, logger: params.Logger.WithTags(tag.ComponentBatcher), } } // Start starts the scanner func (s *Processor) Start() error { ctx := context.WithValue(context.Background(), processorContextKey, s) workerOpts := worker.Options{ MetricsScope: s.tallyScope, BackgroundActivityContext: ctx, Tracer: opentracing.GlobalTracer(), } processorWorker := worker.New(s.svcClient, constants.SystemLocalDomainName, processorTaskListName, workerOpts) return processorWorker.Start() } ================================================ FILE: service/worker/parentclosepolicy/workflow.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package parentclosepolicy import ( "context" "fmt" "math/rand" "time" "github.com/pborman/uuid" "go.uber.org/cadence" "go.uber.org/cadence/activity" "go.uber.org/cadence/encoded" "go.uber.org/cadence/workflow" "github.com/uber/cadence/client" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/types" ) type ( contextKey string ) const ( processorContextKey contextKey = "processorContext" // processorTaskListName is the tasklist name processorTaskListName = "cadence-sys-processor-parent-close-policy" // processorWFTypeName is the workflow type processorWFTypeName = "cadence-sys-parent-close-policy-workflow" processorActivityName = "cadence-sys-parent-close-policy-activity" infiniteDuration = 20 * 365 * 24 * time.Hour processorChannelName = "ParentClosePolicyProcessorChannelName" ) type ( // RequestDetail defines detail of each workflow to process RequestDetail struct { DomainID string DomainName string WorkflowID string RunID string Policy types.ParentClosePolicy } // Request defines the request for parent close policy Request struct { ParentExecution types.WorkflowExecution Executions []RequestDetail // DEPRECATED: the following field is deprecated since childworkflow // might in a different domain, use the DomainName field in RequestDetail DomainName string } ) var ( retryPolicy = cadence.RetryPolicy{ InitialInterval: 10 * time.Second, BackoffCoefficient: 1.7, MaximumInterval: 5 * time.Minute, ExpirationInterval: infiniteDuration, } activityOptions = workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: 5 * time.Minute, RetryPolicy: &retryPolicy, } ) func init() { workflow.RegisterWithOptions(ProcessorWorkflow, workflow.RegisterOptions{Name: processorWFTypeName}) activity.RegisterWithOptions(ProcessorActivity, activity.RegisterOptions{Name: processorActivityName}) } // ProcessorWorkflow is the workflow that performs actions for ParentClosePolicy func ProcessorWorkflow(ctx workflow.Context) error { requestCh := workflow.GetSignalChannel(ctx, processorChannelName) for { var request Request if !requestCh.ReceiveAsync(&request) { // no more request break } opt := workflow.WithActivityOptions(ctx, activityOptions) _ = workflow.ExecuteActivity(opt, processorActivityName, request).Get(ctx, nil) } return nil } // ProcessorActivity is activity for processing batch operation func ProcessorActivity(ctx context.Context, request Request) error { processor := ctx.Value(processorContextKey).(*Processor) domainCache := processor.domainCache historyClient := processor.clientBean.GetHistoryClient() logger := getActivityLogger(ctx) scope := processor.metricsClient.Scope(metrics.ParentClosePolicyProcessorScope) childWorkflowOnly := false if request.ParentExecution.WorkflowID != "" && request.ParentExecution.RunID != "" { // this is for backward compatibility // ideally we should always set childWorkflowOnly = true // however if ParentExecution is not specified, setting it to true // will cause terminate or cancel request to return mismatch error childWorkflowOnly = true } remoteExecutions := make(map[string][]RequestDetail) for _, execution := range request.Executions { domainName := execution.DomainName if domainName == "" { // for backward compatibility domainName = request.DomainName } var err error domainID := execution.DomainID if domainID == "" { // for backward compatibility domainID, err = domainCache.GetDomainID(domainName) if err != nil { if common.IsEntityNotExistsError(err) { scope.IncCounter(metrics.ParentClosePolicyProcessorSuccess) continue } scope.IncCounter(metrics.ParentClosePolicyProcessorFailures) logger.Error("Failed to process parent close policy", tag.Error(err)) return err } } switch execution.Policy { case types.ParentClosePolicyAbandon: // no-op continue case types.ParentClosePolicyTerminate: terminateReq := &types.HistoryTerminateWorkflowExecutionRequest{ DomainUUID: domainID, TerminateRequest: &types.TerminateWorkflowExecutionRequest{ Domain: domainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: execution.WorkflowID, }, Reason: "by parent close policy", Identity: processorWFTypeName, FirstExecutionRunID: execution.RunID, }, } if childWorkflowOnly { terminateReq.ChildWorkflowOnly = true terminateReq.ExternalWorkflowExecution = &request.ParentExecution } err = historyClient.TerminateWorkflowExecution(ctx, terminateReq) case types.ParentClosePolicyRequestCancel: cancelReq := &types.HistoryRequestCancelWorkflowExecutionRequest{ DomainUUID: domainID, CancelRequest: &types.RequestCancelWorkflowExecutionRequest{ Domain: domainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: execution.WorkflowID, }, Identity: processorWFTypeName, FirstExecutionRunID: execution.RunID, }, } if childWorkflowOnly { cancelReq.ChildWorkflowOnly = true cancelReq.ExternalWorkflowExecution = &request.ParentExecution } err = historyClient.RequestCancelWorkflowExecution(ctx, cancelReq) default: err = fmt.Errorf("unknown parent close policy: %v", execution.Policy) } if err != nil { switch err.(type) { case *types.EntityNotExistsError, *types.WorkflowExecutionAlreadyCompletedError, *types.CancellationAlreadyRequestedError: err = nil case *types.DomainNotActiveError: var domainEntry *cache.DomainCacheEntry if domainEntry, err = domainCache.GetDomainByID(domainID); err == nil { cluster := domainEntry.GetReplicationConfig().ActiveClusterName remoteExecutions[cluster] = append(remoteExecutions[cluster], execution) } } } if err != nil { scope.IncCounter(metrics.ParentClosePolicyProcessorFailures) logger.Error("Failed to process parent close policy", tag.Error(err)) return err } scope.IncCounter(metrics.ParentClosePolicyProcessorSuccess) } if err := signalRemoteCluster( ctx, processor.clientBean, request.ParentExecution, remoteExecutions, processor.numWorkflows, ); err != nil { scope.IncCounter(metrics.ParentClosePolicyProcessorFailures) logger.Error("Failed to signal remote parent close policy workflow", tag.Error(err)) return err } return nil } func signalRemoteCluster( ctx context.Context, clientBean client.Bean, parentExecution types.WorkflowExecution, remoteExecutions map[string][]RequestDetail, numWorkflows int, ) error { for cluster, executions := range remoteExecutions { remoteClient, err := clientBean.GetRemoteFrontendClient(cluster) if err != nil { return err } signalCtx, cancel := context.WithTimeout(ctx, signalTimeout) signalValue := Request{ ParentExecution: parentExecution, Executions: executions, } dc := encoded.GetDefaultDataConverter() signalInput, err := dc.ToData(signalValue) if err != nil { cancel() return err } _, err = remoteClient.SignalWithStartWorkflowExecution(signalCtx, &types.SignalWithStartWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, RequestID: uuid.New(), WorkflowID: fmt.Sprintf("%v-%v", workflowIDPrefix, rand.Intn(numWorkflows)), WorkflowType: &types.WorkflowType{Name: processorWFTypeName}, TaskList: &types.TaskList{Name: processorTaskListName}, Input: nil, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(int32(infiniteDuration.Seconds())), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(60), Identity: "cadence-worker", WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), SignalName: processorChannelName, SignalInput: signalInput, }) cancel() if err != nil { return err } } return nil } func getActivityLogger(ctx context.Context) log.Logger { processor := ctx.Value(processorContextKey).(*Processor) wfInfo := activity.GetInfo(ctx) return processor.logger.WithTags( tag.WorkflowID(wfInfo.WorkflowExecution.ID), tag.WorkflowRunID(wfInfo.WorkflowExecution.RunID), tag.WorkflowDomainName(wfInfo.WorkflowDomain), ) } ================================================ FILE: service/worker/replicator/domain_replication_processor.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replicator import ( "context" "fmt" "sync" "sync/atomic" "time" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" ) const ( fetchTaskRequestTimeout = 10 * time.Second pollTimerJitterCoefficient = 0.2 pollIntervalSecs = 1 taskProcessorErrorRetryWait = time.Second taskProcessorErrorRetryBackoffCoefficient = 1 ) type ( domainReplicationProcessor struct { hostInfo membership.HostInfo membershipResolver membership.Resolver status int32 sourceCluster string currentCluster string logger log.Logger remotePeer admin.Client taskExecutor domain.ReplicationTaskExecutor metricsClient metrics.Client throttleRetry *backoff.ThrottleRetry lastProcessedMessageID int64 lastRetrievedMessageID int64 ctx context.Context cancelFn context.CancelFunc wg sync.WaitGroup timeSource clock.TimeSource domainReplicationQueue domain.ReplicationQueue } ) func newDomainReplicationProcessor( sourceCluster string, currentCluster string, logger log.Logger, remotePeer admin.Client, metricsClient metrics.Client, taskExecutor domain.ReplicationTaskExecutor, hostInfo membership.HostInfo, resolver membership.Resolver, domainReplicationQueue domain.ReplicationQueue, replicationMaxRetry time.Duration, timeSource clock.TimeSource, ) *domainReplicationProcessor { retryPolicy := backoff.NewExponentialRetryPolicy(taskProcessorErrorRetryWait) retryPolicy.SetBackoffCoefficient(taskProcessorErrorRetryBackoffCoefficient) retryPolicy.SetExpirationInterval(replicationMaxRetry) throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(isTransientRetryableError), ) ctx, cancel := context.WithCancel(context.Background()) return &domainReplicationProcessor{ hostInfo: hostInfo, membershipResolver: resolver, status: common.DaemonStatusInitialized, sourceCluster: sourceCluster, currentCluster: currentCluster, logger: logger, remotePeer: remotePeer, taskExecutor: taskExecutor, metricsClient: metricsClient, throttleRetry: throttleRetry, lastProcessedMessageID: -1, lastRetrievedMessageID: -1, ctx: ctx, cancelFn: cancel, timeSource: timeSource, domainReplicationQueue: domainReplicationQueue, } } func (p *domainReplicationProcessor) Start() { if !atomic.CompareAndSwapInt32(&p.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } p.wg.Add(1) go p.processorLoop() p.logger.Info("Domain replication processor started.") } func (p *domainReplicationProcessor) processorLoop() { defer p.wg.Done() dur := getWaitDuration() timer := p.timeSource.NewTimer(dur) defer timer.Stop() for { select { case <-timer.Chan(): p.fetchDomainReplicationTasks() timer.Reset(getWaitDuration()) case <-p.ctx.Done(): return } } } func (p *domainReplicationProcessor) fetchDomainReplicationTasks() { // The following is a best effort to make sure only one worker is processing tasks for a // particular source cluster. When the ring is under reconfiguration, it is possible that // for a small period of time two or more workers think they are the owner and try to execute // the processing logic. This will not result in correctness issue as domain replication task // processing will be protected by version check. info, err := p.membershipResolver.Lookup(service.Worker, p.sourceCluster) if err != nil { p.logger.Info("Failed to lookup host info. Skip current run.") return } if info.Identity() != p.hostInfo.Identity() { p.logger.Debug(fmt.Sprintf("Worker not responsible for source cluster %v.", p.sourceCluster)) return } ctx, cancel := context.WithTimeout(p.ctx, fetchTaskRequestTimeout) request := &types.GetDomainReplicationMessagesRequest{ LastRetrievedMessageID: common.Int64Ptr(p.lastRetrievedMessageID), LastProcessedMessageID: common.Int64Ptr(p.lastProcessedMessageID), ClusterName: p.currentCluster, } response, err := p.remotePeer.GetDomainReplicationMessages(ctx, request) defer cancel() if err != nil { p.logger.Error("Failed to get replication tasks", tag.Error(err)) return } p.logger.Debug("Successfully fetched domain replication tasks.", tag.Counter(len(response.Messages.ReplicationTasks))) for taskIndex := range response.Messages.ReplicationTasks { task := response.Messages.ReplicationTasks[taskIndex] err := p.throttleRetry.Do(p.ctx, func(ctx context.Context) error { return p.handleDomainReplicationTask(task) }) if err != nil { p.logger.Error("Failed to apply domain replication tasks", tag.Error(err)) dlqErr := p.throttleRetry.Do(context.Background(), func(ctx context.Context) error { return p.putDomainReplicationTaskToDLQ(task) }) if dlqErr != nil { p.logger.Error("Failed to put replication tasks to DLQ", tag.Error(dlqErr)) p.metricsClient.IncCounter(metrics.DomainReplicationTaskScope, metrics.ReplicatorDLQFailures) return } } } p.lastProcessedMessageID = response.Messages.GetLastRetrievedMessageID() p.lastRetrievedMessageID = response.Messages.GetLastRetrievedMessageID() } func (p *domainReplicationProcessor) putDomainReplicationTaskToDLQ(task *types.ReplicationTask) error { domainAttribute := task.GetDomainTaskAttributes() if domainAttribute == nil { return &types.InternalServiceError{ Message: "Domain replication task does not set domain task attribute", } } p.metricsClient.Scope( metrics.DomainReplicationTaskScope, metrics.DomainTag(domainAttribute.GetInfo().GetName()), ).IncCounter(metrics.DomainReplicationEnqueueDLQCount) return p.domainReplicationQueue.PublishToDLQ(context.Background(), task) } func (p *domainReplicationProcessor) handleDomainReplicationTask( task *types.ReplicationTask, ) error { p.metricsClient.IncCounter(metrics.DomainReplicationTaskScope, metrics.ReplicatorMessages) sw := p.metricsClient.StartTimer(metrics.DomainReplicationTaskScope, metrics.ReplicatorLatency) defer sw.Stop() err := p.taskExecutor.Execute(task.DomainTaskAttributes) if err != nil { p.metricsClient.IncCounter(metrics.DomainReplicationTaskScope, metrics.ReplicatorFailures) } return err } func (p *domainReplicationProcessor) Stop() { p.logger.Info("Domain replication processor stopping.") p.cancelFn() p.wg.Wait() p.logger.Info("Domain replication processor stopped.") } func getWaitDuration() time.Duration { return backoff.JitDuration(time.Duration(pollIntervalSecs)*time.Second, pollTimerJitterCoefficient) } func isTransientRetryableError(err error) bool { switch err.(type) { case *types.BadRequestError: return false default: return true } } ================================================ FILE: service/worker/replicator/domain_replication_processor_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replicator import ( "errors" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" ) type domainReplicationSuite struct { suite.Suite *require.Assertions controller *gomock.Controller sourceCluster string currentCluster string taskExecutor *domain.MockReplicationTaskExecutor remoteClient *admin.MockClient domainReplicationQueue *domain.MockReplicationQueue replicationProcessor *domainReplicationProcessor timeSource clock.MockedTimeSource } func TestDomainReplicationSuite(t *testing.T) { s := new(domainReplicationSuite) suite.Run(t, s) } func (s *domainReplicationSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) resource := resource.NewTest(s.T(), s.controller, metrics.Worker) s.sourceCluster = "active" s.currentCluster = "standby" s.taskExecutor = domain.NewMockReplicationTaskExecutor(s.controller) s.domainReplicationQueue = domain.NewMockReplicationQueue(s.controller) s.remoteClient = resource.RemoteAdminClient serviceResolver := resource.MembershipResolver serviceResolver.EXPECT().Lookup(service.Worker, s.sourceCluster).Return(resource.GetHostInfo(), nil).AnyTimes() s.timeSource = clock.NewMockedTimeSource() s.replicationProcessor = newDomainReplicationProcessor( s.sourceCluster, s.currentCluster, resource.GetLogger(), s.remoteClient, resource.GetMetricsClient(), s.taskExecutor, resource.GetHostInfo(), serviceResolver, s.domainReplicationQueue, time.Millisecond, s.timeSource, ) retryPolicy := backoff.NewExponentialRetryPolicy(time.Nanosecond) retryPolicy.SetMaximumAttempts(1) s.replicationProcessor.throttleRetry = backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryPolicy), backoff.WithRetryableError(isTransientRetryableError), ) } func (s *domainReplicationSuite) TearDownTest() { } func (s *domainReplicationSuite) TestStartStop() { s.replicationProcessor.Start() // second call should be no-op s.replicationProcessor.Start() // yield execution so background goroutine starts time.Sleep(50 * time.Millisecond) s.remoteClient.EXPECT(). GetDomainReplicationMessages(gomock.Any(), gomock.Any()). Return(&types.GetDomainReplicationMessagesResponse{ Messages: &types.ReplicationMessages{}, }, nil) // advance time to let it run one iteration s.timeSource.Advance(pollIntervalSecs * 5 * time.Second) // yield execution time.Sleep(50 * time.Millisecond) // stop processor s.replicationProcessor.Stop() // validate no goroutines left goleak.VerifyNone(s.T()) } func (s *domainReplicationSuite) TestHandleDomainReplicationTask() { domainID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeDomain.Ptr(), DomainTaskAttributes: &types.DomainTaskAttributes{ ID: domainID, }, } s.taskExecutor.EXPECT().Execute(task.DomainTaskAttributes).Return(nil).Times(1) err := s.replicationProcessor.handleDomainReplicationTask(task) s.NoError(err) s.taskExecutor.EXPECT().Execute(task.DomainTaskAttributes).Return(errors.New("test")).Times(1) err = s.replicationProcessor.handleDomainReplicationTask(task) s.Error(err) } func (s *domainReplicationSuite) TestPutDomainReplicationTaskToDLQ() { domainID := uuid.New() task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeDomain.Ptr(), } err := s.replicationProcessor.putDomainReplicationTaskToDLQ(task) s.Error(err) task.DomainTaskAttributes = &types.DomainTaskAttributes{ ID: domainID, } s.domainReplicationQueue.EXPECT().PublishToDLQ(gomock.Any(), task).Return(nil).Times(1) err = s.replicationProcessor.putDomainReplicationTaskToDLQ(task) s.NoError(err) s.domainReplicationQueue.EXPECT().PublishToDLQ(gomock.Any(), task).Return(errors.New("test")).Times(1) err = s.replicationProcessor.putDomainReplicationTaskToDLQ(task) s.Error(err) } func (s *domainReplicationSuite) TestFetchDomainReplicationTasks() { domainID1 := uuid.New() domainID2 := uuid.New() lastMessageID := int64(1000) resp := &types.GetDomainReplicationMessagesResponse{ Messages: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), DomainTaskAttributes: &types.DomainTaskAttributes{ ID: domainID1, }, }, { TaskType: types.ReplicationTaskTypeDomain.Ptr(), DomainTaskAttributes: &types.DomainTaskAttributes{ ID: domainID2, }, }, }, LastRetrievedMessageID: lastMessageID, }, } s.remoteClient.EXPECT().GetDomainReplicationMessages(gomock.Any(), gomock.Any()).Return(resp, nil) s.taskExecutor.EXPECT().Execute(resp.Messages.ReplicationTasks[0].DomainTaskAttributes).Return(nil).Times(1) s.taskExecutor.EXPECT().Execute(resp.Messages.ReplicationTasks[1].DomainTaskAttributes).Return(nil).Times(1) s.replicationProcessor.fetchDomainReplicationTasks() s.Equal(lastMessageID, s.replicationProcessor.lastProcessedMessageID) s.Equal(lastMessageID, s.replicationProcessor.lastRetrievedMessageID) } func (s *domainReplicationSuite) TestFetchDomainReplicationTasks_Failed() { lastMessageID := int64(1000) s.remoteClient.EXPECT().GetDomainReplicationMessages(gomock.Any(), gomock.Any()).Return(nil, errors.New("test")) s.replicationProcessor.fetchDomainReplicationTasks() s.NotEqual(lastMessageID, s.replicationProcessor.lastProcessedMessageID) s.NotEqual(lastMessageID, s.replicationProcessor.lastRetrievedMessageID) } func (s *domainReplicationSuite) TestFetchDomainReplicationTasks_FailedOnExecution() { domainID1 := uuid.New() domainID2 := uuid.New() lastMessageID := int64(1000) resp := &types.GetDomainReplicationMessagesResponse{ Messages: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), DomainTaskAttributes: &types.DomainTaskAttributes{ ID: domainID1, }, }, { TaskType: types.ReplicationTaskTypeDomain.Ptr(), DomainTaskAttributes: &types.DomainTaskAttributes{ ID: domainID2, }, }, }, LastRetrievedMessageID: lastMessageID, }, } s.remoteClient.EXPECT().GetDomainReplicationMessages(gomock.Any(), gomock.Any()).Return(resp, nil) s.taskExecutor.EXPECT().Execute(gomock.Any()).Return(errors.New("test")).AnyTimes() s.domainReplicationQueue.EXPECT().PublishToDLQ(gomock.Any(), gomock.Any()).Return(nil).Times(2) s.replicationProcessor.fetchDomainReplicationTasks() s.Equal(lastMessageID, s.replicationProcessor.lastProcessedMessageID) s.Equal(lastMessageID, s.replicationProcessor.lastRetrievedMessageID) } func (s *domainReplicationSuite) TestFetchDomainReplicationTasks_FailedOnDLQ() { domainID1 := uuid.New() domainID2 := uuid.New() lastMessageID := int64(1001) resp := &types.GetDomainReplicationMessagesResponse{ Messages: &types.ReplicationMessages{ ReplicationTasks: []*types.ReplicationTask{ { TaskType: types.ReplicationTaskTypeDomain.Ptr(), DomainTaskAttributes: &types.DomainTaskAttributes{ ID: domainID1, }, }, { TaskType: types.ReplicationTaskTypeDomain.Ptr(), DomainTaskAttributes: &types.DomainTaskAttributes{ ID: domainID2, }, }, }, LastRetrievedMessageID: lastMessageID, }, } s.remoteClient.EXPECT().GetDomainReplicationMessages(gomock.Any(), gomock.Any()).Return(resp, nil) s.taskExecutor.EXPECT().Execute(gomock.Any()).Return(nil).AnyTimes() s.domainReplicationQueue.EXPECT().PublishToDLQ(gomock.Any(), gomock.Any()).Return(errors.New("test")).Times(0) s.replicationProcessor.fetchDomainReplicationTasks() s.Equal(lastMessageID, s.replicationProcessor.lastProcessedMessageID) s.Equal(lastMessageID, s.replicationProcessor.lastRetrievedMessageID) } ================================================ FILE: service/worker/replicator/replicator.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package replicator import ( "time" "github.com/uber/cadence/client" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/metrics" ) type ( // Replicator is the processor for replication tasks Replicator struct { clusterMetadata cluster.Metadata domainReplicationTaskExecutor domain.ReplicationTaskExecutor clientBean client.Bean domainProcessors []*domainReplicationProcessor logger log.Logger metricsClient metrics.Client hostInfo membership.HostInfo membershipResolver membership.Resolver domainReplicationQueue domain.ReplicationQueue replicationMaxRetry time.Duration } ) // NewReplicator creates a new replicator for processing replication tasks func NewReplicator( clusterMetadata cluster.Metadata, clientBean client.Bean, logger log.Logger, metricsClient metrics.Client, hostInfo membership.HostInfo, membership membership.Resolver, domainReplicationQueue domain.ReplicationQueue, domainReplicationTaskExecutor domain.ReplicationTaskExecutor, replicationMaxRetry time.Duration, ) *Replicator { logger = logger.WithTags(tag.ComponentReplicator) return &Replicator{ hostInfo: hostInfo, membershipResolver: membership, clusterMetadata: clusterMetadata, domainReplicationTaskExecutor: domainReplicationTaskExecutor, clientBean: clientBean, logger: logger, metricsClient: metricsClient, domainReplicationQueue: domainReplicationQueue, replicationMaxRetry: replicationMaxRetry, } } // Start is called to start replicator func (r *Replicator) Start() error { currentClusterName := r.clusterMetadata.GetCurrentClusterName() for clusterName := range r.clusterMetadata.GetRemoteClusterInfo() { adminClient, err := r.clientBean.GetRemoteAdminClient(clusterName) if err != nil { return err } processor := newDomainReplicationProcessor( clusterName, currentClusterName, r.logger.WithTags(tag.ComponentReplicationTaskProcessor, tag.SourceCluster(clusterName)), adminClient, r.metricsClient, r.domainReplicationTaskExecutor, r.hostInfo, r.membershipResolver, r.domainReplicationQueue, r.replicationMaxRetry, clock.NewRealTimeSource(), ) r.domainProcessors = append(r.domainProcessors, processor) } for _, domainProcessor := range r.domainProcessors { domainProcessor.Start() } return nil } // Stop is called to stop replicator func (r *Replicator) Stop() { for _, domainProcessor := range r.domainProcessors { domainProcessor.Stop() } } ================================================ FILE: service/worker/scanner/README.md ================================================ # A brief* overview of Scanner and Fixer and how to control them First and foremost: most code in this folder is disabled by default dynamic config, and it should be considered beta-like quality in general. Understand what you are enabling before enabling it, and run it at your own risk. It is shared primarily so others can learn from and leverage what we have already encountered, if they end up needing it or something similar. **Any _actually recommended_ fixing processes will be explicitly called out in release notes or similar.** This document is **not** recommending any, merely describing. There are a variety of problems with the Scanner and Fixer related code, so please do not take this document as a sign that it is a structure we want to _keep_. It has just been confusing enough that it took substantial time to understand, and now some of that effort is written down to save people the full effort in the future. ## What is this folder for This folder as a whole contains a variety of data-cleanup workflows. Stuff like: - Find old, unnecessary data and delete it. - Find data caused by old bugs and fix it. - Clean up and remove abandoned tasklists so they do not continue taking up space. As a general rule, these all scan the full database (for one kind of data), check some data, and clean it up if necessary. *How their code does that* varies quite a bit, however. E.g. the "history scavenger" finds old branches of history that lost their CAS race to update the workflow's official history. It is not possible to guarantee that these are cleaned up while a workflow runs, because any cleanup could have failed. Instead, the history scavenger periodically walks through the whole database and looks for these orphaned history branches, and deletes them. The most complex of these processes are based around `Scanner` and `Fixer`, and so **this readme is almost exclusively written for them**. For others, just read their code, it's probably faster than reading any doc about their code. ## Basic structure of `Scanner` and `Fixer` workflows - "Invariants" define `Check` and `Fix` methods that make sure our invariants hold, and fixes them if they did not for some reason. - These are in the `common/reconciliation/invariant` folder, e.g. [concreteExecutionExists.go](../../../common/reconciliation/invariant/concreteExecutionExists.go) checks (`Check`) that a current execution record points to a concrete record that exists. If it does not, it deletes the current record (the `Fix`). - Some of these have a paired "invariant collection" which is currently a 1:1 relationship, and is used elsewhere to refer to the invariants by a name where it isn't unique per data-type (e.g. timer only has one). - These are often bundled together in an `InvariantManager`, which runs multiple and aggregates the results. - Invariants are _almost_ exclusively used by Scanner and Fixer. One is also used as part of replication processing, in [ndc/history_resender.go](../../../common/ndc/history_resender.go). That use is essentially completely unrelated to Scanner or Fixer, and is ignored for this doc. - [Scanner](shardscanner/scanner.go) runs only the `Check`, and pushes all failing checks to the blobstore. - It gets its core data through an Iterator, whose implementation depends on your datastore. - On each Iterator item, it runs through its list of Invariants (via an `InvariantManager`). - Per item, the aggregated result from the InvariantManager is collected, and pushed to a blob-store. - [Fixer](shardscanner/fixer.go) runs only `Fix`, on the downloaded results from the most-recent Scanner. - Structurally it's extremely similar to Scanner, but it calls `Fix` instead, and it gets its data from a different Iterator (this one iterates over the scanner results in your blobstore). - _All_ configured invariants run, not just the ones that failed `Check` previously. - Because only `Fix` is called, invariants should likely `Check` first internally. - ^ The workflows that run Scanner and Fixer are started at service startup _if enabled_, and are run as continuous every-minute crons that essentially never expire. - Each scanner / fixer type uses its own tasklist, and these workers are only started if enabled. - This effectively means that enabling one works immediately after a service start, but disabling _only pauses_, which may be undesirable if they are resumed after a lengthy delay. - Because these are crons, only the original start arguments are retained, not new ones if changes are made. - **Manually cancel or terminate the cron workflows if you change relevant fields, and (re)start a worker to start the new versions.** - Each workflow that runs processes *only one data-source*, primarily via its Iterator. - This means all invariants within a scanner/fixer run handle the same type of data. - The bulk of all ^ this is plugged together by `*shardscanner.ScannerConfig` instances, which contain everything that customizes each particular _kind_ of scanner/fixer, which is stored in / retrieved from the context by the workflow's type as a key. - E.g. see the [concrete_execution.go config](executions/concrete_execution.go). - The workflow type (registered function name) is in there, as are the start args, some high level dynamic config to control the scanner/fixer workflows (enabled, concurrency, etc), and "hooks" for both scanner and fixer. - The workflows largely do not care about this config, they just run the same activities each time and let the activities figure out what to do. - Activities get their config and other dependencies from [Get{Scanner,Fixer}Context](shardscanner/types.go)), which contain this config. - Hooks (fields in the `ScannerConfig`) are where much of the non-workflow behavior comes from. - E.g. the concrete scanner hooks bundle up a "manager" (InvariantManager), "iterator" (walks the datasource and yields individual items to check), and "custom config" (dynamic config to control which invariants are enabled) as part of the `create*Hooks` funcs in [concrete_exeuction.go](executions/concrete_execution.go). - And so, ultimately the workflows essentially take this config and create a Scanner or Fixer out of them (in activities), as you can see in e.g. [the scanner workflow](shardscanner/scanner_workflow.go). - It reads some config through an activity (which gets its per-workflow-type context). - This is used to decide parallelism / pass additional args to the scan activities. - The `scanShardActivity` (in [shardscanner/activities.go](shardscanner/activities.go) essentially iterates over shards, and runs `scanShard` on each one, which creates a `NewScanner` that gets its config and behavior from args / environment / hooks. - Fixer is **very** similar, except it also queries the previous Scanner run to get the list of blobstore files that it needs to process. Last but not least: there are other workflows in this folder which _do not_ follow these patterns! - E.g. the [tasklist scanner and history scavenger](workflow.go), and [CheckDataCorruptionWorkflow](data_corruption_workflow.go). - These are MUCH more localized in behavior and simpler, so they are not covered in this document. Just read the code :) - Their workflows are still started in the main entry-point, [scanner.go](scanner.go). See the `Start` function, most are pretty easily found in there. Hopefully that made sense. ## Config Scanners and fixers are generally disabled by default, as they can consume substantial resources and may delete or modify data to correct issues. Because of this, you generally need to modify your dynamic config to run them. At the time of writing, you can enable these with config like the following. Enable scanner workflows (these are per data source / per record type, like "concrete executions" and "timers"): ```yaml worker.executionsScannerEnabled: - value: true # default false worker.currentExecutionsScannerEnabled: - value: true # default false worker.timersScannerEnabled: - value: true # default false worker.historyScannerEnabled: - value: true # default false worker.taskListScannerEnabled: - value: true # default true, only used on sql stores ``` Enable scanner invariants (currently each one only supports one data source / record type, but there may be multiple invariants for the data source): ```yaml # concretes worker.executionsScannerInvariantCollectionStale: - value: true # default false worker.executionsScannerInvariantCollectionMutableState: - value: true # default true worker.executionsScannerInvariantCollectionHistory: - value: true # default true # timer invariant is implied as there is only one. # to enable it, enable the workflow. # currents, NONE OF THESE WORK because of type mismatch worker.currentExecutionsScannerInvariantCollectionHistory: - value: true # default true worker.currentExecutionsInvariantCollectionMutableState: - value: true # default true ``` Enable fixer workflows (also one per type): ```yaml worker.concreteExecutionFixerEnabled: - value: true # default false worker.currentExecutionFixerEnabled: - value: true # default false worker.timersFixerEnabled: - value: true # default false ``` Enable fixer to run on a domain (required to do anything to a domain's data, which also means nothing will be fixed without this): ```yaml worker.currentExecutionFixerDomainAllow: - value: true # default false constraints: {domainName: "your-domain"} # for example, or have no constraints to enable for all domains worker.concreteExecutionFixerDomainAllow: - value: true # default false worker.timersFixerDomainAllow: - value: true # default false ``` Enable fixer invariants: ```yaml # concretes worker.executionsFixerInvariantCollectionStale: - value: true # default false worker.executionsFixerInvariantCollectionMutableState: - value: true # default true worker.executionsFixerInvariantCollectionHistory: - value: true # default true # timer invariant is enabled if timer-fixer is enabled, as there is only one # current execution fixer has never worked and does not currently support dynamic config ``` ## Verifying locally There are a few ways to run local clusters and make changes and test things out, but this is the way I did things when reading and changing this code: 1. Run the default docker-compose cluster ([docker-compose.yml](../../../docker/docker-compose.yml)) 2. Make your config/code/etc changes locally for scanner / fixer. 3. `make cadence-server` to ensure it builds 4. `./cadence-server start --services worker` to start up a worker that will connect to your docker compose cluster. 5. Browse history via the web UI, usually http://localhost:8088/domains/cadence-system/workflows The default docker compose setup starts a worker instance, but due to the default dynamic config setup where all but `worker.taskListScannerEnabled` are disabled, the in-docker worker will not run (most) scanner/fixer tasklists and will not steal any tasks from a local worker. So you can often simply run it without any changes, start up your local (customized) worker service outside of docker, and everything will Just Work™. This way you can leverage the normal docker compose yaml files with minimal effort, use the web UI to observe the results, and rapidly change/rebuild/rerun/debug/etc without needing to deal with docker. If you **do** need to debug the tasklist scanner, I would recommend making a custom build, and modifying the docker compose file to use your build. Details on that are below, but they **are not necessary** for other scanners/fixers. ### Docker compose changes (tasklist scanner only) There are a few ways to achieve this, but I like modifying the `docker-compose*.yaml` file to use a custom local build, and just changing its dynamic config to disable the tasklist scanner. To do that, see the [docker/README.md file](../../../docker/README.md) for instructions. Personally I prefer making a unique auto-setup tag so it does not replace any non-customized docker compose runs in the future. E.g.: ```yaml services: # ... cadence: image: ubercadence/server:my-auto-setup # use your new tag # ... environment: # note this env var, it is the file you need to change - "DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml" ``` And just make a build after changing the dynamic config file. This will copy the file into the docker image, and any local changes won't affect the running container. ```yaml worker.taskListScannerEnabled: - value: false # default true, only used on sql stores ``` Just set ^ this, and make sure the others are not explicitly enabled as they are disabled by default, and you're likely done. ### Other things to change outside docker - Config, as running the server locally will generally use the `config/dynamicconfig/development.yaml` file, so you likely want to change that one to enable your code. - Data, to give your invariant / scanner something to notice - Running some workflows and then deleting / modifying the data by hand in the database is fairly simple. - Your invariants, to prevent prematurely purging any manual data changes you made. - I'm fond of this trick: ```go func (i *invariant) Check(...) { x := true // go vet doesn't currently complain if x { // about dead code with this. handy! return CheckResult{Failed} // fake failure, so all records go to fixer } // ... the rest of your normal code, unchanged } func (i *invariant) Fix(...) { // print the fix rather than doing it, so the next runs try too. // or do the `if x {` trick here too } ``` - Your IDE, to launch with only the worker, as other services are not necessary and it will just slow down startup/shutdown. - Just make sure you have `start --services worker` in its start-args. ### Running it all and checking the results Add some breakpoints or print statements, run it and see what happens :) If Scanner found anything interesting, you should now have a `/tmp/blobstore` folder with files like `{uuid()}_0.corrupted`. These uuids are _random_, and the `_0.corrupted` suffix marks them as page 0 (out of N), and that they refer to corrupted entries. You'll have one uuid per Scanner shard (configurable, for concurrency) that found issues, and multiple pages per shard if the results exceeded the paging size limit. If Fixer found anything, `/tmp/blobstore` should now have `{uuid()}_0.skipped` and/or `{uuid()}_0.fixed` files. These uuids are _also random_, and do not refer to the Scanner file that their data came from, and the uuid and paging patterns are the same as Scanner. You may also have `*.failed` files, following the same pattern. Similarly, these are cases where an invariant returned a Failure result (they can come from scanner _or_ fixer). Only `*.corrupted` files from scanner will be processed by fixer, however. Note that `*.failed` files can contain invariant results of _all_ statuses, as the status of a record _trends towards_ "failed", and only the final status is recorded. For details, see the behavior in the [InvariantManger](../../../common/reconciliation/invariant/invariantManager.go). You can also see the results in the scanner and fixer workflows in the UI. In particular: - Scanner: - Each scanner type has a unique ID, like `concreteExecutionsScannerWFID = "cadence-sys-executions-scanner"`. - Check the activities to see how many corruptions were found per shard - Query its `aggregate_report` (works in UI, others require arguments and you currently need to use the CLI) to get overall counts. - The activities return results with UUIDs and page numbers. These are the UUIDs and page ranges for files in `/tmp/blobstore`, so you can look up specific detailed results. - Otherwise tbh I grab a known workflow ID and grep the most-recent batch of files for it. - Fixer: - Check the recent fixer workflows for fix results. - If it has completed already, it is likely _not_ the most-recent or the currently running one. Check older ones until you find some with more than ~ a dozen events, as those are no-ops. - The activities accept UUIDs and page ranges from scanner (these match the return values in scanner, and refer to the scan-result files in `/tmp/blobstore`), and return the same kind of structure (new random UUIDs, new page ranges, referring to new files in `/tmp/blobstore`). - Again, I would also recommend just grepping for known IDs that should have been processed. If you are not printing or debugging whatever info you are looking for, check the contents of these files to verify they're doing what you expect! #### An example working scanner/fixer setup, visible in /tmp/blobstore files This is the new "stale" invariant working in its concrete execution scanner -> the concrete execution fixer also working, with faked results to simplify testing. I also ran all the other concrete invariants because I was curious. I made a change like this: ```go func (c *staleWorkflowCheck) Check( ctx context.Context, execution interface{}, ) CheckResult { x := true if x { return CheckResult{ CheckResultType: CheckResultTypeCorrupted, InvariantName: c.Name(), Info: "fake corrupt", } } _, result := c.check(ctx, execution) return result } ``` so the `Check` call would always fail, and the `Fix` call would run the real check. And added dynamic config like this: ```yaml # enable these invariants worker.executionsScannerInvariantCollectionStale: - value: true # default false worker.executionsScannerInvariantCollectionMutableState: - value: true # default true worker.executionsScannerInvariantCollectionHistory: - value: true # default true worker.executionsFixerInvariantCollectionStale: - value: true # default false worker.executionsFixerInvariantCollectionMutableState: - value: true # default true worker.executionsFixerInvariantCollectionHistory: - value: true # default true # these invariants are all run by the concrete execution scanner/fixer worker.executionsScannerEnabled: # note slightly different name - value: true # default false worker.concreteExecutionFixerEnabled: - value: true # default false worker.concreteExecutionFixerDomainAllow: - value: true # default false ``` After a scanner and fixer run, `/tmp/blobstore` contained `*.corrupted` and `*.skipped` files. The `*.corrupted` files contained data like this: ```json { "Input": { "Execution": { ... }, "Result": { "CheckResultType": "corrupted", "DeterminingInvariantType": "stale_workflow", "CheckResults": [ { "CheckResultType": "healthy", "InvariantName": "history_exists", "Info": "", "InfoDetails": "" }, { "CheckResultType": "healthy", "InvariantName": "open_current_execution", "Info": "", "InfoDetails": "" }, { "CheckResultType": "corrupted", "InvariantName": "stale_workflow", "Info": "fake corrupt", "InfoDetails": "" } ] } }, "Result": { "FixResultType": "skipped", "DeterminingInvariantName": null, "FixResults": null } } ``` You can see the two healthy invariants, and the one I faked. When fixer then ran I got this in a `*.skipped` file: ```json { "Execution": { ... }, "Input": { "Execution": { ... }, "Result": { "CheckResultType": "corrupted", "DeterminingInvariantType": "stale_workflow", "CheckResults": [ { "CheckResultType": "healthy", "InvariantName": "history_exists", "Info": "", "InfoDetails": "" }, { "CheckResultType": "healthy", "InvariantName": "open_current_execution", "Info": "", "InfoDetails": "" }, { "CheckResultType": "corrupted", "InvariantName": "stale_workflow", "Info": "fake corrupt", "InfoDetails": "" } ] } }, "Result": { "FixResultType": "skipped", "DeterminingInvariantName": null, "FixResults": [ { "FixResultType": "skipped", "InvariantName": "history_exists", "CheckResult": { "CheckResultType": "healthy", "InvariantName": "history_exists", "Info": "", "InfoDetails": "" }, "Info": "skipped fix because execution was healthy", "InfoDetails": "" }, { "FixResultType": "skipped", "InvariantName": "open_current_execution", "CheckResult": { "CheckResultType": "healthy", "InvariantName": "open_current_execution", "Info": "", "InfoDetails": "" }, "Info": "skipped fix because execution was healthy", "InfoDetails": "" }, { "FixResultType": "skipped", "InvariantName": "stale_workflow", "CheckResult": { "CheckResultType": "", "InvariantName": "", "Info": "", "InfoDetails": "" }, "Info": "no need to fix: completed workflow still within retention + 10-day buffer", "InfoDetails": "completed workflow still within retention + 10-day buffer, closed 2023-09-20 20:26:04.924876012 -0500 CDT and allowed to exist until 2023-10-07" } ] } } ``` Notice that _all three_ invariants ran in the fixer (all three were enabled), and all three fixes were skipped because they did not find any problems. If I had also faked the stale workflow invariant `Fix`, you would see a `FixResultType` of "fixed" on that invariant in fixer, and a file named `*.fixed` rather than `*.skipped`. #### An example bad scanner/fixer setup This is the current-execution scanner working -> the current-execution fixer misbehaving and using the wrong type, and creating `*.failed` files, as of commit `eb55629d`. I faked the current execution invariant to always say "corrupt" in `Check`, and panic in `Fix`, and enabled the current execution scanner and fixer in dynamic config, and ran the worker. First, the scanner run creates `*.corrupted` files with entries like this: ```json { "Input": { "Execution": { ... }, "Result": { "CheckResultType": "corrupted", "DeterminingInvariantType": "concrete_execution_exists", "CheckResults": [ { "CheckResultType": "corrupted", "InvariantName": "concrete_execution_exists", "Info": "execution is open without having concrete execution", "InfoDetails": "concrete execution not found. WorkflowId: e905c98f-108a-4191-9ef2-ca07a1361f9c, RunId: 6bc5386b-c043-4eb1-a332-c3bb7b5188f0" } ] } }, "Result": { "FixResultType": "skipped", "DeterminingInvariantName": null, "FixResults": null } } ``` This shows that it identified my "always corrupt" results correctly, in the [concrete_execution_exists invariant](../../../common/reconciliation/invariant/concreteExecutionExists.go). This is later consumed by the current execution fixer, which produces `*.failed` files with contents like this: ```json { "Execution": { ... }, "Input": { "Execution": { ... }, "Result": { "CheckResultType": "corrupted", "DeterminingInvariantType": "concrete_execution_exists", "CheckResults": [ { "CheckResultType": "corrupted", "InvariantName": "concrete_execution_exists", "Info": "execution is open without having concrete execution", "InfoDetails": "concrete execution not found. WorkflowId: e905c98f-108a-4191-9ef2-ca07a1361f9c, RunId: 6bc5386b-c043-4eb1-a332-c3bb7b5188f0" } ] } }, "Result": { "FixResultType": "failed", "DeterminingInvariantName": "history_exists", "FixResults": [ { "FixResultType": "failed", "InvariantName": "history_exists", "CheckResult": { "CheckResultType": "failed", "InvariantName": "history_exists", "Info": "failed to check: expected concrete execution", "InfoDetails": "" }, "Info": "failed fix because check failed", "InfoDetails": "" }, { "FixResultType": "failed", "InvariantName": "open_current_execution", "CheckResult": { "CheckResultType": "failed", "InvariantName": "open_current_execution", "Info": "failed to check: expected concrete execution", "InfoDetails": "" }, "Info": "failed fix because check failed", "InfoDetails": "" } ] } } ``` You can see scanner's data as the input-result, and _completely different invariants_ running and failing as the fixer result. ## Thoughts and prayers Most notably: - The current implementation is extremely difficult to extend externally, as the code depends heavily on constants that cannot be added to or changed. - Changing this likely requires rewriting a significant amount of the code, but does seem worth doing. - Future versions really should try to fix this. Custom database plugins may have unique problems and need unique scan/fix tools, and those are unlikely to be open-source friendly and useful to run on _every_ database. - The current-execution _fixer_ has never been run successfully anywhere. Beware drawing any conclusions from its code or config. - The scanner appears to work, and the invariant seems correct, but the fixer was written to use the _concrete_ execution invariants, and that has caused it to always fail when run. - This might be fixed and (optionally) enabled or deleted in the future. - Current, concrete, and timer scan/fix code is _very similar_ but not identical. - Maybe this is good, maybe not, but it can be confusing. Read carefully. - Config keys, configurability, all things config-like vary widely between scan/fix implementations. - Copy/paste, don't guess. And verify it before calling it "done". - The current "scan everything, then fix everything" structure is problematically slow on large clusters, and quite resource-wasteful due to doing everything at least 2x. - Generally speaking it's probably not a bad thing to slow it down and re-check, but large clusters can take _weeks_ before fixer starts. That's horrible for verifying fixes, or addressing any issues quickly. - Queries feel like an odd way to pass data from scanner to fixer. - I _suspect_ they were done to avoid returning too much data, violating a blob-size limit constraint. This... might be valid? The data it actually uses is saved by fixer's querying activity though, so it is still bound by that limit. - Due to results being spread between many activities and many queries, overviews are hard to get. We could instead push it all to a new blobstore file, for human use. - There's a lot of indirection in general for unknown reasons, and that makes control-flow extremely hard to figure out. - Possibly it was intended for more flexibility than it has seen, possibly it would simply benefit from some modernizing (instance fields rather than background activity context), possibly it's a functional-like lament about the lack of generics in Go when it was written, I'm really not sure. Overall it seems like it probably deserves a rewrite, though some of the parts are pretty clearly reusable (invariants, iterators, etc). --- [*]: may not actually be brief ================================================ FILE: service/worker/scanner/data_corruption_workflow.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scanner import ( "context" "fmt" "time" "go.uber.org/cadence/activity" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/zap" c "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/fetcher" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/resource" ) func init() { workflow.RegisterWithOptions( CheckDataCorruptionWorkflow, workflow.RegisterOptions{Name: reconciliation.CheckDataCorruptionWorkflowType}, ) activity.Register(ExecutionFixerActivity) activity.Register(EmitResultMetricsActivity) } const ( workflowTimer = 5 * time.Minute maxSignalNumber = 1000 fixerActivityTimeout = time.Minute ) // CheckDataCorruptionWorkflow is invoked by remote cluster via signals func CheckDataCorruptionWorkflow(ctx workflow.Context, fixList []entity.Execution) error { logger := workflow.GetLogger(ctx) signalCh := workflow.GetSignalChannel(ctx, reconciliation.CheckDataCorruptionWorkflowSignalName) signalCount := 0 maxReceivedSignalNumber := workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} { return maxSignalNumber }) for { selector := workflow.NewSelector(ctx) // timer timerCtx, timerCancel := workflow.WithCancel(ctx) waitTimer := workflow.NewTimer(timerCtx, workflowTimer) selector.AddFuture(waitTimer, func(f workflow.Future) { // do nothing. Unblock the selector }) // signal selector.AddReceive(signalCh, func(c workflow.Channel, more bool) { var fixExecution entity.Execution for c.ReceiveAsync(&fixExecution) { signalCount++ fixList = append(fixList, fixExecution) } }) selector.Select(ctx) if len(fixList) == 0 { return nil } timerCancel() timeout := fixerActivityTimeout * time.Duration(len(fixList)) activityOptions = workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: timeout, HeartbeatTimeout: fixerActivityTimeout, RetryPolicy: &activityRetryPolicy, } activityCtx := workflow.WithActivityOptions(ctx, activityOptions) var fixResults []invariant.FixResult err := workflow.ExecuteActivity(activityCtx, ExecutionFixerActivity, fixList).Get(activityCtx, &fixResults) if err != nil { logger.Error("failed to run execution fixer activity", zap.Error(err)) return err } activityOptions = workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: time.Minute, } activityCtx = workflow.WithActivityOptions(ctx, activityOptions) err = workflow.ExecuteActivity(activityCtx, EmitResultMetricsActivity, fixResults).Get(activityCtx, nil) if err != nil { logger.Error("failed to run execution fixer activity", zap.Error(err)) return err } fixList = []entity.Execution{} workflow.GetMetricsScope(ctx) var maxSignalCount int if err := maxReceivedSignalNumber.Get(&maxSignalCount); err != nil { logger.Error("failed to get max supported signal number") return err } if signalCount > maxSignalCount { var fixExecution entity.Execution for signalCh.ReceiveAsync(&fixExecution) { fixList = append(fixList, fixExecution) } return workflow.NewContinueAsNewError(ctx, reconciliation.CheckDataCorruptionWorkflowType, fixList) } } } func ExecutionFixerActivity(ctx context.Context, fixList []entity.Execution) ([]invariant.FixResult, error) { var result []invariant.FixResult index := 0 if activity.HasHeartbeatDetails(ctx) { activity.GetHeartbeatDetails(ctx, &index) } for index < len(fixList) { execution := fixList[index] pr, cache, err := getDefaultDAO(ctx, execution.ShardID) if err != nil { return nil, err } request := fetcher.ExecutionRequest{ DomainID: execution.DomainID, WorkflowID: execution.WorkflowID, RunID: execution.RunID, } concreteExecution, err := fetcher.ConcreteExecution(ctx, pr, request) if err != nil { return nil, err } currentExecutionInvariant := invariant.NewOpenCurrentExecution(pr, cache) fixResult := currentExecutionInvariant.Fix(ctx, concreteExecution) result = append(result, fixResult) historyInvariant := invariant.NewHistoryExists(pr, cache) fixResult = historyInvariant.Fix(ctx, concreteExecution) result = append(result, fixResult) activity.RecordHeartbeat(ctx, index) index++ } return result, nil } func getDefaultDAO( ctx context.Context, shardID int, ) (persistence.Retryer, cache.DomainCache, error) { sc, err := getScannerContext(ctx) if err != nil { return nil, nil, fmt.Errorf("cannot find key %v in context", reconciliation.CheckDataCorruptionWorkflowType) } res := sc.resource cache := res.GetDomainCache() execManager, err := res.GetExecutionManager(shardID) if err != nil { return nil, nil, err } pr := persistence.NewPersistenceRetryer(execManager, res.GetHistoryManager(), c.CreatePersistenceRetryPolicy()) return pr, cache, nil } func EmitResultMetricsActivity(ctx context.Context, fixResults []invariant.FixResult) error { sc, err := getScannerContext(ctx) if err != nil { return fmt.Errorf("cannot find key %v in context", reconciliation.CheckDataCorruptionWorkflowType) } res := sc.resource for _, result := range fixResults { scope := res.GetMetricsClient().Scope( metrics.CheckDataCorruptionWorkflowScope, metrics.InvariantTypeTag(string(result.InvariantName))) scope.IncCounter(metrics.DataCorruptionWorkflowCount) switch result.FixResultType { case invariant.FixResultTypeFailed: scope.IncCounter(metrics.DataCorruptionWorkflowFailure) case invariant.FixResultTypeFixed: scope.IncCounter(metrics.DataCorruptionWorkflowSuccessCount) case invariant.FixResultTypeSkipped: scope.IncCounter(metrics.DataCorruptionWorkflowSkipCount) } } return nil } func NewDataCorruptionWorkflowWorker( resource resource.Resource, params *BootstrapParams, ) *Scanner { zapLogger, err := zap.NewProduction() if err != nil { resource.GetLogger().Fatal("failed to initialize zap logger", tag.Error(err)) } return &Scanner{ context: scannerContext{ resource: resource, }, tallyScope: params.TallyScope, zapLogger: zapLogger.Named("data-corruption-workflow"), } } func (s *Scanner) StartDataCorruptionWorkflowWorker() error { ctx := context.WithValue(context.Background(), contextKey(reconciliation.CheckDataCorruptionWorkflowType), s.context) workerOpts := worker.Options{ Logger: s.zapLogger, MetricsScope: s.tallyScope, MaxConcurrentActivityExecutionSize: maxConcurrentActivityExecutionSize, MaxConcurrentDecisionTaskExecutionSize: maxConcurrentDecisionTaskExecutionSize, BackgroundActivityContext: ctx, } err := worker.New( s.context.resource.GetSDKClient(), constants.SystemLocalDomainName, reconciliation.CheckDataCorruptionWorkflowTaskList, workerOpts, ).Start() return err } ================================================ FILE: service/worker/scanner/data_corruption_workflow_test.go ================================================ // Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scanner import ( "context" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/metrics" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/service/history/constants" ) const testWorkflowName = "default-test-workflow-type-name" var validBranchToken = []byte{89, 11, 0, 10, 0, 0, 0, 12, 116, 101, 115, 116, 45, 116, 114, 101, 101, 45, 105, 100, 11, 0, 20, 0, 0, 0, 14, 116, 101, 115, 116, 45, 98, 114, 97, 110, 99, 104, 45, 105, 100, 0} type dataCorruptionWorkflowTestSuite struct { suite.Suite testsuite.WorkflowTestSuite } func TestDataCorruptionWorkflowTestSuite(t *testing.T) { suite.Run(t, new(dataCorruptionWorkflowTestSuite)) } func (s *dataCorruptionWorkflowTestSuite) TestWorkflow_SignalWorkflow_Success() { signalOnce := false env := s.NewTestWorkflowEnvironment() env.OnActivity(ExecutionFixerActivity, mock.Anything, mock.Anything).Return([]invariant.FixResult{}, nil).Times(1) env.OnActivity(EmitResultMetricsActivity, mock.Anything, mock.Anything).Return(nil).Times(1) env.SetOnTimerScheduledListener(func(timerID string, duration time.Duration) { if !signalOnce { env.SignalWorkflow(reconciliation.CheckDataCorruptionWorkflowSignalName, entity.Execution{ ShardID: 0, DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), }) signalOnce = true } }) env.ExecuteWorkflow(reconciliation.CheckDataCorruptionWorkflowType, nil) s.True(env.IsWorkflowCompleted()) env.AssertExpectations(s.T()) } func (s *dataCorruptionWorkflowTestSuite) TestWorkflow_TimerFire_Success() { isTimerFire := false env := s.NewTestWorkflowEnvironment() env.SetOnTimerFiredListener(func(timerID string) { isTimerFire = true }) env.ExecuteWorkflow(reconciliation.CheckDataCorruptionWorkflowType, nil) s.True(env.IsWorkflowCompleted()) s.True(isTimerFire) env.AssertExpectations(s.T()) env.AssertNotCalled(s.T(), "ExecutionFixerActivity", mock.Anything, mock.Anything) env.AssertNotCalled(s.T(), "EmitResultMetricsActivity", mock.Anything, mock.Anything) } func (s *dataCorruptionWorkflowTestSuite) TestExecutionFixerActivity_Success() { env := s.NewTestActivityEnvironment() controller := gomock.NewController(s.T()) mockResource := resource.NewTest(s.T(), controller, metrics.Worker) defer mockResource.Finish(s.T()) fixList := []entity.Execution{ { ShardID: 0, DomainID: uuid.New(), WorkflowID: uuid.New(), RunID: uuid.New(), }, } mockResource.ExecutionMgr.On("GetShardID").Return(0) mockResource.ExecutionMgr.On("GetCurrentExecution", mock.Anything, mock.Anything).Return(&p.GetCurrentExecutionResponse{ RunID: fixList[0].RunID, State: 2, CloseStatus: 1, LastWriteVersion: 0, }, nil) mockResource.ExecutionMgr.On("GetWorkflowExecution", mock.Anything, mock.Anything).Return(&p.GetWorkflowExecutionResponse{ State: &p.WorkflowMutableState{ ExecutionInfo: &p.WorkflowExecutionInfo{ BranchToken: validBranchToken, }, VersionHistories: &p.VersionHistories{ CurrentVersionHistoryIndex: 0, Histories: []*p.VersionHistory{ { BranchToken: validBranchToken, }, }, }, }, }, nil) mockResource.ExecutionMgr.On("DeleteWorkflowExecution", mock.Anything, mock.Anything).Return(nil) mockResource.ExecutionMgr.On("DeleteCurrentWorkflowExecution", mock.Anything, mock.Anything).Return(nil) mockResource.HistoryMgr.On("ReadHistoryBranch", mock.Anything, mock.Anything).Return(&p.ReadHistoryBranchResponse{}, nil) ctx := context.WithValue(context.Background(), contextKey(testWorkflowName), scannerContext{resource: mockResource}) env.SetTestTimeout(time.Second * 5) env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: ctx, }) tlScavengerHBInterval = time.Millisecond * 10 mockResource.DomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain-name", nil).AnyTimes() mockResource.DomainCache.EXPECT().GetDomainByID(gomock.Any()).Return(constants.TestGlobalDomainEntry, nil).AnyTimes() _, err := env.ExecuteActivity(ExecutionFixerActivity, fixList) s.NoError(err) } ================================================ FILE: service/worker/scanner/executions/concrete_execution.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package executions import ( "context" "fmt" "strconv" "time" cclient "go.uber.org/cadence/client" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/service/worker/scanner/shardscanner" ) const ( // ConcreteExecutionsScannerWFTypeName defines workflow type name for concrete executions scanner ConcreteExecutionsScannerWFTypeName = "cadence-sys-executions-scanner-workflow" concreteExecutionsScannerWFID = "cadence-sys-executions-scanner" concreteExecutionsScannerTaskListName = "cadence-sys-executions-scanner-tasklist-0" // ConcreteExecutionsFixerWFTypeName defines workflow type name for concrete executions fixer ConcreteExecutionsFixerWFTypeName = "cadence-sys-executions-fixer-workflow" concreteExecutionsFixerWFID = "cadence-sys-executions-fixer" concreteExecutionsFixerTaskListName = "cadence-sys-executions-fixer-tasklist-0" ) // ConcreteScannerWorkflow starts concrete executions scanner. func ConcreteScannerWorkflow(ctx workflow.Context, params shardscanner.ScannerWorkflowParams) error { logger := workflow.GetLogger(ctx) logger.Info("Starting ConcreteExecutionsScannerWorkflow", zap.Any("Params", params)) wf, err := shardscanner.NewScannerWorkflow(ctx, ConcreteExecutionsScannerWFTypeName, params) if err != nil { logger.Error("Failed to create new scanner workflow", zap.Error(err)) return err } err = wf.Start(ctx) if err != nil { logger.Error("Failed to start scanner workflow", zap.Error(err)) } return err } // ConcreteFixerWorkflow starts concrete executions fixer. func ConcreteFixerWorkflow(ctx workflow.Context, params shardscanner.FixerWorkflowParams) error { logger := workflow.GetLogger(ctx) logger.Info("Starting ConcreteExecutionsFixerWorkflow", zap.Any("Params", params)) wf, err := shardscanner.NewFixerWorkflow(ctx, ConcreteExecutionsFixerWFTypeName, params) if err != nil { logger.Error("Failed to create new fixer workflow", zap.Error(err)) return err } err = wf.Start(ctx) if err != nil { logger.Error("Failed to start fixer workflow", zap.Error(err)) } return err } // concreteExecutionScannerHooks provides hooks for concrete executions scanner func concreteExecutionScannerHooks() *shardscanner.ScannerHooks { h, err := shardscanner.NewScannerHooks(concreteExecutionScannerManager, concreteExecutionScannerIterator, concreteExecutionCustomScannerConfig) if err != nil { return nil } return h } // concreteExecutionFixerHooks provides hooks needed for concrete executions fixer. func concreteExecutionFixerHooks() *shardscanner.FixerHooks { h, err := shardscanner.NewFixerHooks(concreteExecutionFixerManager, concreteExecutionFixerIterator, concreteExecutionCustomFixerConfig) if err != nil { return nil } return h } // concreteExecutionScannerManager provides invariant manager for concrete execution scanner func concreteExecutionScannerManager( ctx context.Context, pr persistence.Retryer, params shardscanner.ScanShardActivityParams, domainCache cache.DomainCache, ) invariant.Manager { collections := ParseCollections(params.ScannerConfig) var ivs []invariant.Invariant for _, fn := range ConcreteExecutionType.ToInvariants(collections, zap.NewNop()) { ivs = append(ivs, fn(pr, domainCache)) } return invariant.NewInvariantManager(ivs) } // concreteExecutionScannerIterator provides iterator for concrete execution scanner. func concreteExecutionScannerIterator( ctx context.Context, pr persistence.Retryer, params shardscanner.ScanShardActivityParams, ) pagination.Iterator { it := ConcreteExecutionType.ToIterator() return it(ctx, pr, params.PageSize) } // concreteExecutionFixerIterator provides iterator for concrete execution fixer. func concreteExecutionFixerIterator(ctx context.Context, client blobstore.Client, keys store.Keys, _ shardscanner.FixShardActivityParams) store.ScanOutputIterator { return store.NewBlobstoreIterator(ctx, client, keys, ConcreteExecutionType.ToBlobstoreEntity()) } // concreteExecutionFixerManager provides invariant manager for concrete execution fixer. func concreteExecutionFixerManager(_ context.Context, pr persistence.Retryer, params shardscanner.FixShardActivityParams, domainCache cache.DomainCache) invariant.Manager { // convert to invariants. // this may produce an empty list if it all fixers are intentionally disabled, // or if the list came from a previous version of the server which lacked this config. var collections []invariant.Collection for k, v := range params.EnabledInvariants { if v == strconv.FormatBool(true) { ivc, err := invariant.CollectionString(k) if err != nil { panic(fmt.Sprintf("invalid collection name: %v", err)) // error includes the name } collections = append(collections, ivc) } } var ivs []invariant.Invariant for _, fn := range ConcreteExecutionType.ToInvariants(collections, zap.NewNop()) { ivs = append(ivs, fn(pr, domainCache)) } return invariant.NewInvariantManager(ivs) } // concreteExecutionCustomScannerConfig resolves dynamic config for concrete executions scanner. func concreteExecutionCustomScannerConfig(ctx shardscanner.ScannerContext) shardscanner.CustomScannerConfig { res := shardscanner.CustomScannerConfig{} if ctx.Config.DynamicCollection.GetBoolProperty(dynamicproperties.ConcreteExecutionsScannerInvariantCollectionHistory)() { res[invariant.CollectionHistory.String()] = strconv.FormatBool(true) } if ctx.Config.DynamicCollection.GetBoolProperty(dynamicproperties.ConcreteExecutionsScannerInvariantCollectionMutableState)() { res[invariant.CollectionMutableState.String()] = strconv.FormatBool(true) } if ctx.Config.DynamicCollection.GetBoolProperty(dynamicproperties.ConcreteExecutionsScannerInvariantCollectionStale)() { res[invariant.CollectionStale.String()] = strconv.FormatBool(true) } return res } // concreteExecutionCustomFixerConfig resolves dynamic config for concrete executions scanner. func concreteExecutionCustomFixerConfig(ctx shardscanner.FixerContext) shardscanner.CustomScannerConfig { res := shardscanner.CustomScannerConfig{} // unlike scanner, fixer expects keys to exist when both true and false, to differentiate from pre-config behavior res[invariant.CollectionHistory.String()] = strconv.FormatBool( ctx.Config.DynamicCollection.GetBoolProperty(dynamicproperties.ConcreteExecutionsFixerInvariantCollectionHistory)(), ) res[invariant.CollectionMutableState.String()] = strconv.FormatBool( ctx.Config.DynamicCollection.GetBoolProperty(dynamicproperties.ConcreteExecutionsFixerInvariantCollectionMutableState)(), ) res[invariant.CollectionStale.String()] = strconv.FormatBool( ctx.Config.DynamicCollection.GetBoolProperty(dynamicproperties.ConcreteExecutionsFixerInvariantCollectionStale)(), ) return res } // ConcreteExecutionConfig configures concrete execution scanner func ConcreteExecutionConfig(dc *dynamicconfig.Collection) *shardscanner.ScannerConfig { return &shardscanner.ScannerConfig{ ScannerWFTypeName: ConcreteExecutionsScannerWFTypeName, FixerWFTypeName: ConcreteExecutionsFixerWFTypeName, DynamicParams: shardscanner.DynamicParams{ ScannerEnabled: dc.GetBoolProperty(dynamicproperties.ConcreteExecutionsScannerEnabled), FixerEnabled: dc.GetBoolProperty(dynamicproperties.ConcreteExecutionFixerEnabled), Concurrency: dc.GetIntProperty(dynamicproperties.ConcreteExecutionsScannerConcurrency), PageSize: dc.GetIntProperty(dynamicproperties.ConcreteExecutionsScannerPersistencePageSize), BlobstoreFlushThreshold: dc.GetIntProperty(dynamicproperties.ConcreteExecutionsScannerBlobstoreFlushThreshold), ActivityBatchSize: dc.GetIntProperty(dynamicproperties.ConcreteExecutionsScannerActivityBatchSize), AllowDomain: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.ConcreteExecutionFixerDomainAllow), }, DynamicCollection: dc, ScannerHooks: concreteExecutionScannerHooks, FixerHooks: concreteExecutionFixerHooks, StartWorkflowOptions: cclient.StartWorkflowOptions{ ID: concreteExecutionsScannerWFID, TaskList: concreteExecutionsScannerTaskListName, ExecutionStartToCloseTimeout: 20 * 365 * 24 * time.Hour, WorkflowIDReusePolicy: cclient.WorkflowIDReusePolicyAllowDuplicate, CronSchedule: "*/5 * * * *", }, StartFixerOptions: cclient.StartWorkflowOptions{ ID: concreteExecutionsFixerWFID, TaskList: concreteExecutionsFixerTaskListName, ExecutionStartToCloseTimeout: 20 * 365 * 24 * time.Hour, WorkflowIDReusePolicy: cclient.WorkflowIDReusePolicyAllowDuplicate, CronSchedule: "*/5 * * * *", }, } } ================================================ FILE: service/worker/scanner/executions/concrete_execution_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package executions import ( "context" "strconv" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/common/types/testdata" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/worker/scanner/shardscanner" ) type concreteExectionsWorkflowsSuite struct { suite.Suite testsuite.WorkflowTestSuite } func TestConcreteScannerWorkflowSuite(t *testing.T) { suite.Run(t, new(concreteExectionsWorkflowsSuite)) } func (s *concreteExectionsWorkflowsSuite) SetupSuite() { workflow.Register(ConcreteScannerWorkflow) } func (s *concreteExectionsWorkflowsSuite) TestScannerWorkflow_Success() { env := s.NewTestWorkflowEnvironment() env.OnActivity(shardscanner.ActivityScannerConfig, mock.Anything, mock.Anything).Return(shardscanner.ResolvedScannerWorkflowConfig{ GenericScannerConfig: shardscanner.GenericScannerConfig{ Enabled: true, Concurrency: 3, ActivityBatchSize: 5, }, }, nil) env.OnActivity(shardscanner.ActivityScannerEmitMetrics, mock.Anything, mock.Anything).Return(nil) shards := shardscanner.Shards{ Range: &shardscanner.ShardRange{ Min: 0, Max: 30, }, } batches := [][]int{ {0, 3, 6, 9, 12}, {15, 18, 21, 24, 27}, {1, 4, 7, 10, 13}, {16, 19, 22, 25, 28}, {2, 5, 8, 11, 14}, {17, 20, 23, 26, 29}, } for _, batch := range batches { var reports []shardscanner.ScanReport for i := range batch { if i == 0 { reports = append(reports, shardscanner.ScanReport{ ShardID: batch[i], Stats: shardscanner.ScanStats{ EntitiesCount: 10, }, Result: shardscanner.ScanResult{ ControlFlowFailure: &shardscanner.ControlFlowFailure{ Info: "got control flow failure", }, }, }) } else { reports = append(reports, shardscanner.ScanReport{ ShardID: batch[i], Stats: shardscanner.ScanStats{ EntitiesCount: 10, CorruptedCount: 2, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 1, }, }, Result: shardscanner.ScanResult{ ShardScanKeys: &shardscanner.ScanKeys{ Corrupt: &store.Keys{ UUID: "test_uuid", MinPage: 0, MaxPage: 10, }, }, }, }) } } var customc shardscanner.CustomScannerConfig env.OnActivity(shardscanner.ActivityScanShard, mock.Anything, shardscanner.ScanShardActivityParams{ Shards: batch, ScannerConfig: customc, }).Return(reports, nil) } env.ExecuteWorkflow(ConcreteScannerWorkflow, shardscanner.ScannerWorkflowParams{ Shards: shards, }) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) aggValue, err := env.QueryWorkflow(shardscanner.AggregateReportQuery) s.NoError(err) var agg shardscanner.AggregateScanReportResult s.NoError(aggValue.Get(&agg)) s.Equal(shardscanner.AggregateScanReportResult{ EntitiesCount: 240, CorruptedCount: 48, CheckFailedCount: 24, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 24, }, }, agg) for i := 0; i < 30; i++ { shardReportValue, err := env.QueryWorkflow(shardscanner.ShardReportQuery, i) s.NoError(err) var shardReport *shardscanner.ScanReport s.NoError(shardReportValue.Get(&shardReport)) if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { s.Equal(&shardscanner.ScanReport{ ShardID: i, Stats: shardscanner.ScanStats{ EntitiesCount: 10, }, Result: shardscanner.ScanResult{ ControlFlowFailure: &shardscanner.ControlFlowFailure{ Info: "got control flow failure", }, }, }, shardReport) } else { s.Equal(&shardscanner.ScanReport{ ShardID: i, Stats: shardscanner.ScanStats{ EntitiesCount: 10, CorruptedCount: 2, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 1, }, }, Result: shardscanner.ScanResult{ ShardScanKeys: &shardscanner.ScanKeys{ Corrupt: &store.Keys{ UUID: "test_uuid", MinPage: 0, MaxPage: 10, }, }, }, }, shardReport) } } statusValue, err := env.QueryWorkflow(shardscanner.ShardStatusQuery, shardscanner.PaginatedShardQueryRequest{}) s.NoError(err) var status *shardscanner.ShardStatusQueryResult s.NoError(statusValue.Get(&status)) expected := make(map[int]shardscanner.ShardStatus) for i := 0; i < 30; i++ { if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { expected[i] = shardscanner.ShardStatusControlFlowFailure } else { expected[i] = shardscanner.ShardStatusSuccess } } s.Equal(shardscanner.ShardStatusResult(expected), status.Result) s.True(status.ShardQueryPaginationToken.IsDone) s.Nil(status.ShardQueryPaginationToken.NextShardID) // check for paginated query result statusValue, err = env.QueryWorkflow(shardscanner.ShardStatusQuery, shardscanner.PaginatedShardQueryRequest{ StartingShardID: common.IntPtr(5), LimitShards: common.IntPtr(10), }) s.NoError(err) status = &shardscanner.ShardStatusQueryResult{} s.NoError(statusValue.Get(&status)) expected = make(map[int]shardscanner.ShardStatus) for i := 5; i < 15; i++ { if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { expected[i] = shardscanner.ShardStatusControlFlowFailure } else { expected[i] = shardscanner.ShardStatusSuccess } } s.Equal(shardscanner.ShardStatusResult(expected), status.Result) s.False(status.ShardQueryPaginationToken.IsDone) s.Equal(15, *status.ShardQueryPaginationToken.NextShardID) corruptionKeysValue, err := env.QueryWorkflow(shardscanner.ShardCorruptKeysQuery, shardscanner.PaginatedShardQueryRequest{}) s.NoError(err) var shardCorruptKeysResult *shardscanner.ShardCorruptKeysQueryResult s.NoError(corruptionKeysValue.Get(&shardCorruptKeysResult)) expectedCorrupted := make(map[int]store.Keys) for i := 0; i < 30; i++ { if i != 0 && i != 1 && i != 2 && i != 15 && i != 16 && i != 17 { expectedCorrupted[i] = store.Keys{ UUID: "test_uuid", MinPage: 0, MaxPage: 10, } } } s.Equal(shardscanner.ShardCorruptKeysResult(expectedCorrupted), shardCorruptKeysResult.Result) } func (s *concreteExectionsWorkflowsSuite) TestConcreteScannerWorkflow_NewScannerWorkflow_Error() { env := s.NewTestWorkflowEnvironment() params := shardscanner.ScannerWorkflowParams{ Shards: shardscanner.Shards{ Range: &shardscanner.ShardRange{ Min: 0, }, }, } env.ExecuteWorkflow(ConcreteScannerWorkflow, params) s.True(env.IsWorkflowCompleted()) s.ErrorContains(env.GetWorkflowError(), "empty Range provided") } func (s *concreteExectionsWorkflowsSuite) TestConcreteScannerWorkflow_Start_Error() { env := s.NewTestWorkflowEnvironment() params := shardscanner.ScannerWorkflowParams{ Shards: shardscanner.Shards{ Range: &shardscanner.ShardRange{ Min: 0, Max: 30, }, }, ScannerWorkflowConfigOverwrites: shardscanner.ScannerWorkflowConfigOverwrites{}, } cfg := shardscanner.ScannerConfigActivityParams{ Overwrites: params.ScannerWorkflowConfigOverwrites, } env.OnActivity(shardscanner.ActivityScannerConfig, mock.Anything, cfg).Return(shardscanner.ResolvedScannerWorkflowConfig{}, assert.AnError) env.ExecuteWorkflow(ConcreteScannerWorkflow, params) s.True(env.IsWorkflowCompleted()) s.ErrorContains(env.GetWorkflowError(), assert.AnError.Error()) } func (s *concreteExectionsWorkflowsSuite) TestConcreteFixerWorkflow_NewScannerWorkflow_Error() { env := s.NewTestWorkflowEnvironment() params := shardscanner.FixerWorkflowParams{ ScannerWorkflowWorkflowID: constants.TestWorkflowID, ScannerWorkflowRunID: constants.TestRunID, } fixerCorruptedKeysActivityParams := shardscanner.FixerCorruptedKeysActivityParams{ ScannerWorkflowWorkflowID: params.ScannerWorkflowWorkflowID, ScannerWorkflowRunID: params.ScannerWorkflowRunID, StartingShardID: nil, } env.OnActivity(shardscanner.ActivityFixerCorruptedKeys, mock.Anything, fixerCorruptedKeysActivityParams).Return(&shardscanner.FixerCorruptedKeysActivityResult{}, assert.AnError) env.ExecuteWorkflow(ConcreteFixerWorkflow, params) s.True(env.IsWorkflowCompleted()) s.ErrorContains(env.GetWorkflowError(), assert.AnError.Error()) } func (s *concreteExectionsWorkflowsSuite) TestConcreteFixerWorkflow_Start_Error() { env := s.NewTestWorkflowEnvironment() params := shardscanner.FixerWorkflowParams{ ScannerWorkflowWorkflowID: constants.TestWorkflowID, ScannerWorkflowRunID: constants.TestRunID, } fixerCorruptedKeysActivityParams := shardscanner.FixerCorruptedKeysActivityParams{ ScannerWorkflowWorkflowID: params.ScannerWorkflowWorkflowID, ScannerWorkflowRunID: params.ScannerWorkflowRunID, StartingShardID: nil, } env.OnActivity(shardscanner.ActivityFixerCorruptedKeys, mock.Anything, fixerCorruptedKeysActivityParams).Return(&shardscanner.FixerCorruptedKeysActivityResult{ CorruptedKeys: []shardscanner.CorruptedKeysEntry{ { ShardID: 2, CorruptedKeys: store.Keys{ UUID: "test_uuid", MinPage: 0, MaxPage: 10, }, }, }, MinShard: common.IntPtr(0), MaxShard: common.IntPtr(30), }, nil) env.OnActivity(shardscanner.ActivityFixerConfig, mock.Anything, shardscanner.FixShardConfigParams{}).Return(&shardscanner.FixShardConfigResults{}, assert.AnError) env.ExecuteWorkflow(ConcreteFixerWorkflow, params) s.True(env.IsWorkflowCompleted()) s.ErrorContains(env.GetWorkflowError(), assert.AnError.Error()) } func Test_concreteExecutionScannerHooks(t *testing.T) { h := concreteExecutionScannerHooks() assert.NotNil(t, h) } func Test_concreteExecutionFixerHooks(t *testing.T) { h := concreteExecutionFixerHooks() assert.NotNil(t, h) } func Test_concreteExecutionScannerManager(t *testing.T) { params := shardscanner.ScanShardActivityParams{ Shards: []int{1, 2, 3}, ScannerConfig: shardscanner.CustomScannerConfig{ "CollectionHistory": strconv.FormatBool(true), }, } m := concreteExecutionScannerManager(context.Background(), nil, params, nil) assert.NotNil(t, m) } func Test_concreteExecutionScannerIterator(t *testing.T) { params := shardscanner.ScanShardActivityParams{ Shards: []int{1, 2, 3}, ScannerConfig: shardscanner.CustomScannerConfig{ "CollectionHistory": strconv.FormatBool(true), }, PageSize: 1, } ctrl := gomock.NewController(t) mockRetryer := persistence.NewMockRetryer(ctrl) mockRetryer.EXPECT().ListConcreteExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListConcreteExecutionsResponse{ Executions: []*persistence.ListConcreteExecutionsEntity{ { ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, BranchToken: testdata.BranchToken, }, }, }, }, nil).Times(1) i := concreteExecutionScannerIterator(context.Background(), mockRetryer, params) assert.NotNil(t, i) } func Test_concreteExecutionFixerIterator(t *testing.T) { ctx := context.Background() mockClient := blobstore.NewMockClient(gomock.NewController(t)) req := &blobstore.GetRequest{ Key: concreteExecutionsFixerTaskListName + "_0.", } mockClient.EXPECT().Get(ctx, req).Return(&blobstore.GetResponse{}, nil).Times(1) it := concreteExecutionFixerIterator( ctx, mockClient, store.Keys{UUID: concreteExecutionsFixerTaskListName}, shardscanner.FixShardActivityParams{}) assert.NotNil(t, it) } func Test_concreteExecutionFixerManager(t *testing.T) { mockRetryer := persistence.NewMockRetryer(gomock.NewController(t)) params := shardscanner.FixShardActivityParams{ EnabledInvariants: shardscanner.CustomScannerConfig{ "CollectionHistory": strconv.FormatBool(true), }, } m := concreteExecutionFixerManager(context.Background(), mockRetryer, params, nil) assert.NotNil(t, m) } func Test_concreteExecutionFixerManager_Panic(t *testing.T) { mockRetryer := persistence.NewMockRetryer(gomock.NewController(t)) params := shardscanner.FixShardActivityParams{ EnabledInvariants: shardscanner.CustomScannerConfig{ "NotAnInvariantKey": strconv.FormatBool(true), }, } assert.Panics(t, func() { concreteExecutionFixerManager(context.Background(), mockRetryer, params, nil) }) } func Test_concreteExecutionCustomScannerConfig(t *testing.T) { mockClient := dynamicconfig.NewMockClient(gomock.NewController(t)) collection := dynamicconfig.NewCollection(mockClient, log.NewNoop()) mockClient.EXPECT().GetBoolValue(gomock.Any(), gomock.Any()).Return(true, nil).Times(3) ctx := shardscanner.ScannerContext{ Config: &shardscanner.ScannerConfig{ DynamicCollection: collection, }, } cfg := concreteExecutionCustomScannerConfig(ctx) assert.NotNil(t, cfg) assert.Len(t, cfg, 3) assert.Equal(t, "true", cfg[invariant.CollectionHistory.String()]) assert.Equal(t, "true", cfg[invariant.CollectionMutableState.String()]) assert.Equal(t, "true", cfg[invariant.CollectionStale.String()]) } func Test_concreteExecutionCustomFixerConfig(t *testing.T) { mockClient := dynamicconfig.NewMockClient(gomock.NewController(t)) collection := dynamicconfig.NewCollection(mockClient, log.NewNoop()) mockClient.EXPECT().GetBoolValue(gomock.Any(), gomock.Any()).Return(true, nil).Times(3) ctx := shardscanner.FixerContext{ Config: &shardscanner.ScannerConfig{ DynamicCollection: collection, }, } cfg := concreteExecutionCustomFixerConfig(ctx) assert.NotNil(t, cfg) assert.Len(t, cfg, 3) assert.Equal(t, "true", cfg[invariant.CollectionHistory.String()]) assert.Equal(t, "true", cfg[invariant.CollectionMutableState.String()]) assert.Equal(t, "true", cfg[invariant.CollectionStale.String()]) } func TestConcreteExecutionConfig(t *testing.T) { mockClient := dynamicconfig.NewMockClient(gomock.NewController(t)) collection := dynamicconfig.NewCollection(mockClient, log.NewNoop()) cfg := ConcreteExecutionConfig(collection) assert.NotNil(t, cfg) } ================================================ FILE: service/worker/scanner/executions/current_execution.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package executions import ( "context" "strconv" "time" cclient "go.uber.org/cadence/client" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/service/worker/scanner/shardscanner" ) const ( // currentExecutionsScannerWFID is the current execution scanner workflow ID currentExecutionsScannerWFID = "cadence-sys-current-executions-scanner" // CurrentExecutionsScannerWFTypeName is the current execution scanner workflow type CurrentExecutionsScannerWFTypeName = "cadence-sys-current-executions-scanner-workflow" // CurrentExecutionsScannerTaskListName is the current execution scanner workflow tasklist CurrentExecutionsScannerTaskListName = "cadence-sys-current-executions-scanner-tasklist-0" // CurrentExecutionsFixerWFTypeName is the current execution fixer workflow ID CurrentExecutionsFixerWFTypeName = "cadence-sys-current-executions-fixer-workflow" currentExecutionsFixerWFID = "cadence-sys-current-executions-fixer" // CurrentExecutionsFixerTaskListName is the current execution fixer workflow tasklist CurrentExecutionsFixerTaskListName = "cadence-sys-current-executions-fixer-tasklist-0" ) /* !!!!!!!!!!!!!! NOTE: Current execution fixers have never been run. Beware drawing any conclusions from current-execution scanner/fixer code. !!!!!!!!!!!!!! While this code appears structurally complete, the wrong fixer manager is being used, and we have apparently never fully enabled it in our production clusters. It likely needs further checks and possibly a rewrite before attempting to use. */ // CurrentScannerWorkflow is the workflow that scans over all current executions func CurrentScannerWorkflow( ctx workflow.Context, params shardscanner.ScannerWorkflowParams, ) error { logger := workflow.GetLogger(ctx) logger.Info("Starting CurrentScannerWorkflow", zap.Any("Params", params)) wf, err := shardscanner.NewScannerWorkflow(ctx, CurrentExecutionsScannerWFTypeName, params) if err != nil { logger.Error("Failed to start new scanner workflow", zap.Error(err)) return err } err = wf.Start(ctx) if err != nil { logger.Error("Failed to execute scanner workflow", zap.Error(err)) } return err } // currentExecutionScannerHooks provides hooks for current executions scanner. func currentExecutionScannerHooks() *shardscanner.ScannerHooks { wf, err := shardscanner.NewScannerHooks(currentExecutionScannerManager, currentExecutionScannerIterator, currentExecutionCustomScannerConfig) if err != nil { return nil } return wf } // currentExecutionScannerManager is the current execution scanner manager func currentExecutionScannerManager( ctx context.Context, pr persistence.Retryer, params shardscanner.ScanShardActivityParams, domainCache cache.DomainCache, ) invariant.Manager { logger := zap.L() logger.Info("Creating invariant manager for current execution scanner", zap.Any("Params", params)) var ivs []invariant.Invariant collections := ParseCollections(params.ScannerConfig) for _, fn := range CurrentExecutionType.ToInvariants(collections, zap.NewNop()) { ivs = append(ivs, fn(pr, domainCache)) } return invariant.NewInvariantManager(ivs) } // CurrentFixerWorkflow starts current executions fixer. func CurrentFixerWorkflow( ctx workflow.Context, params shardscanner.FixerWorkflowParams, ) error { wf, err := shardscanner.NewFixerWorkflow(ctx, CurrentExecutionsFixerWFTypeName, params) if err != nil { return err } return wf.Start(ctx) } // currentExecutionCustomScannerConfig resolves dynamic config for current executions scanner. func currentExecutionCustomScannerConfig(ctx shardscanner.ScannerContext) shardscanner.CustomScannerConfig { res := shardscanner.CustomScannerConfig{} if ctx.Config.DynamicCollection.GetBoolProperty(dynamicproperties.CurrentExecutionsScannerInvariantCollectionHistory)() { res[invariant.CollectionHistory.String()] = strconv.FormatBool(true) } if ctx.Config.DynamicCollection.GetBoolProperty(dynamicproperties.CurrentExecutionsScannerInvariantCollectionMutableState)() { res[invariant.CollectionMutableState.String()] = strconv.FormatBool(true) } return res } // currentExecutionFixerHooks provides hooks for current executions fixer. func currentExecutionFixerHooks() *shardscanner.FixerHooks { noCustomConfig := func(fixer shardscanner.FixerContext) shardscanner.CustomScannerConfig { return nil } // TODO: yes, this DOES incorrectly use the concrete execution fixer manager, which does not work. // It is retained for now to avoid making a lot of mostly-unrelated changes / fixes / cleanup. h, err := shardscanner.NewFixerHooks(concreteExecutionFixerManager, currentExecutionFixerIterator, noCustomConfig) if err != nil { return nil } return h } // CurrentExecutionConfig configures current execution scanner func CurrentExecutionConfig(dc *dynamicconfig.Collection) *shardscanner.ScannerConfig { return &shardscanner.ScannerConfig{ ScannerWFTypeName: CurrentExecutionsScannerWFTypeName, FixerWFTypeName: CurrentExecutionsFixerWFTypeName, DynamicCollection: dc, DynamicParams: shardscanner.DynamicParams{ ScannerEnabled: dc.GetBoolProperty(dynamicproperties.CurrentExecutionsScannerEnabled), FixerEnabled: dc.GetBoolProperty(dynamicproperties.CurrentExecutionFixerEnabled), Concurrency: dc.GetIntProperty(dynamicproperties.CurrentExecutionsScannerConcurrency), PageSize: dc.GetIntProperty(dynamicproperties.CurrentExecutionsScannerPersistencePageSize), BlobstoreFlushThreshold: dc.GetIntProperty(dynamicproperties.CurrentExecutionsScannerBlobstoreFlushThreshold), ActivityBatchSize: dc.GetIntProperty(dynamicproperties.CurrentExecutionsScannerActivityBatchSize), AllowDomain: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.CurrentExecutionFixerDomainAllow), }, ScannerHooks: currentExecutionScannerHooks, FixerHooks: currentExecutionFixerHooks, StartWorkflowOptions: cclient.StartWorkflowOptions{ ID: currentExecutionsScannerWFID, TaskList: CurrentExecutionsScannerTaskListName, ExecutionStartToCloseTimeout: 20 * 365 * 24 * time.Hour, WorkflowIDReusePolicy: cclient.WorkflowIDReusePolicyAllowDuplicate, CronSchedule: "*/5 * * * *", }, StartFixerOptions: cclient.StartWorkflowOptions{ ID: currentExecutionsFixerWFID, TaskList: CurrentExecutionsFixerTaskListName, ExecutionStartToCloseTimeout: 20 * 365 * 24 * time.Hour, WorkflowIDReusePolicy: cclient.WorkflowIDReusePolicyAllowDuplicate, CronSchedule: "*/5 * * * *", }, } } // currentExecutionScannerIterator is the iterator of current executions func currentExecutionScannerIterator( ctx context.Context, pr persistence.Retryer, params shardscanner.ScanShardActivityParams, ) pagination.Iterator { return CurrentExecutionType.ToIterator()(ctx, pr, params.PageSize) } // currentExecutionFixerIterator is the iterator of fixer execution func currentExecutionFixerIterator( ctx context.Context, client blobstore.Client, keys store.Keys, _ shardscanner.FixShardActivityParams, ) store.ScanOutputIterator { return store.NewBlobstoreIterator(ctx, client, keys, CurrentExecutionType.ToBlobstoreEntity()) } ================================================ FILE: service/worker/scanner/executions/current_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package executions import ( "context" "strconv" "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/service/history/constants" "github.com/uber/cadence/service/worker/scanner/shardscanner" ) type currentExecutionsWorkflowsSuite struct { suite.Suite testsuite.WorkflowTestSuite } func TestCurrentScannerWorkflowSuite(t *testing.T) { suite.Run(t, new(currentExecutionsWorkflowsSuite)) } func (s *currentExecutionsWorkflowsSuite) SetupSuite() { workflow.Register(CurrentScannerWorkflow) } func (s *currentExecutionsWorkflowsSuite) TestScannerWorkflow_Success() { env := s.NewTestWorkflowEnvironment() env.OnActivity(shardscanner.ActivityScannerConfig, mock.Anything, mock.Anything).Return(shardscanner.ResolvedScannerWorkflowConfig{ GenericScannerConfig: shardscanner.GenericScannerConfig{ Enabled: true, Concurrency: 3, ActivityBatchSize: 5, }, }, nil) env.OnActivity(shardscanner.ActivityScannerEmitMetrics, mock.Anything, mock.Anything).Return(nil) shards := shardscanner.Shards{ Range: &shardscanner.ShardRange{ Min: 0, Max: 30, }, } batches := [][]int{ {0, 3, 6, 9, 12}, {15, 18, 21, 24, 27}, {1, 4, 7, 10, 13}, {16, 19, 22, 25, 28}, {2, 5, 8, 11, 14}, {17, 20, 23, 26, 29}, } for _, batch := range batches { var reports []shardscanner.ScanReport for i := range batch { if i == 0 { reports = append(reports, shardscanner.ScanReport{ ShardID: batch[i], Stats: shardscanner.ScanStats{ EntitiesCount: 10, }, Result: shardscanner.ScanResult{ ControlFlowFailure: &shardscanner.ControlFlowFailure{ Info: "got control flow failure", }, }, }) } else { reports = append(reports, shardscanner.ScanReport{ ShardID: batch[i], Stats: shardscanner.ScanStats{ EntitiesCount: 10, CorruptedCount: 2, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 1, }, }, Result: shardscanner.ScanResult{ ShardScanKeys: &shardscanner.ScanKeys{ Corrupt: &store.Keys{ UUID: "test_uuid", MinPage: 0, MaxPage: 10, }, }, }, }) } } var customScannerConfig shardscanner.CustomScannerConfig env.OnActivity(shardscanner.ActivityScanShard, mock.Anything, shardscanner.ScanShardActivityParams{ Shards: batch, ScannerConfig: customScannerConfig, }).Return(reports, nil) } env.ExecuteWorkflow(CurrentScannerWorkflow, shardscanner.ScannerWorkflowParams{ Shards: shards, }) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) aggValue, err := env.QueryWorkflow(shardscanner.AggregateReportQuery) s.NoError(err) var agg shardscanner.AggregateScanReportResult s.NoError(aggValue.Get(&agg)) s.Equal(shardscanner.AggregateScanReportResult{ EntitiesCount: 240, CorruptedCount: 48, CheckFailedCount: 24, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 24, }, }, agg) for i := 0; i < 30; i++ { shardReportValue, err := env.QueryWorkflow(shardscanner.ShardReportQuery, i) s.NoError(err) var shardReport *shardscanner.ScanReport s.NoError(shardReportValue.Get(&shardReport)) if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { s.Equal(&shardscanner.ScanReport{ ShardID: i, Stats: shardscanner.ScanStats{ EntitiesCount: 10, }, Result: shardscanner.ScanResult{ ControlFlowFailure: &shardscanner.ControlFlowFailure{ Info: "got control flow failure", }, }, }, shardReport) } else { s.Equal(&shardscanner.ScanReport{ ShardID: i, Stats: shardscanner.ScanStats{ EntitiesCount: 10, CorruptedCount: 2, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 1, }, }, Result: shardscanner.ScanResult{ ShardScanKeys: &shardscanner.ScanKeys{ Corrupt: &store.Keys{ UUID: "test_uuid", MinPage: 0, MaxPage: 10, }, }, }, }, shardReport) } } statusValue, err := env.QueryWorkflow(shardscanner.ShardStatusQuery, shardscanner.PaginatedShardQueryRequest{}) s.NoError(err) var status *shardscanner.ShardStatusQueryResult s.NoError(statusValue.Get(&status)) expected := make(map[int]shardscanner.ShardStatus) for i := 0; i < 30; i++ { if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { expected[i] = shardscanner.ShardStatusControlFlowFailure } else { expected[i] = shardscanner.ShardStatusSuccess } } s.Equal(shardscanner.ShardStatusResult(expected), status.Result) s.True(status.ShardQueryPaginationToken.IsDone) s.Nil(status.ShardQueryPaginationToken.NextShardID) // check for paginated query result statusValue, err = env.QueryWorkflow(shardscanner.ShardStatusQuery, shardscanner.PaginatedShardQueryRequest{ StartingShardID: common.IntPtr(5), LimitShards: common.IntPtr(10), }) s.NoError(err) status = &shardscanner.ShardStatusQueryResult{} s.NoError(statusValue.Get(&status)) expected = make(map[int]shardscanner.ShardStatus) for i := 5; i < 15; i++ { if i == 0 || i == 1 || i == 2 { expected[i] = shardscanner.ShardStatusControlFlowFailure } else { expected[i] = shardscanner.ShardStatusSuccess } } s.Equal(shardscanner.ShardStatusResult(expected), status.Result) s.False(status.ShardQueryPaginationToken.IsDone) s.Equal(15, *status.ShardQueryPaginationToken.NextShardID) corruptionKeysValue, err := env.QueryWorkflow(shardscanner.ShardCorruptKeysQuery, shardscanner.PaginatedShardQueryRequest{}) s.NoError(err) var shardCorruptKeysResult *shardscanner.ShardCorruptKeysQueryResult s.NoError(corruptionKeysValue.Get(&shardCorruptKeysResult)) expectedCorrupted := make(map[int]store.Keys) for i := 0; i < 30; i++ { if i != 0 && i != 1 && i != 2 && i != 15 && i != 16 && i != 17 { expectedCorrupted[i] = store.Keys{ UUID: "test_uuid", MinPage: 0, MaxPage: 10, } } } s.Equal(shardscanner.ShardCorruptKeysResult(expectedCorrupted), shardCorruptKeysResult.Result) } func (s *currentExecutionsWorkflowsSuite) TestScannerWorkflow_NewScannerWorkflow_Error() { env := s.NewTestWorkflowEnvironment() env.ExecuteWorkflow(CurrentScannerWorkflow, shardscanner.ScannerWorkflowParams{}) s.True(env.IsWorkflowCompleted()) s.ErrorContains(env.GetWorkflowError(), "must provide either List or Range") } func (s *currentExecutionsWorkflowsSuite) TestScannerWorkflow_Start_Error() { env := s.NewTestWorkflowEnvironment() env.OnActivity(shardscanner.ActivityScannerConfig, mock.Anything, mock.Anything).Return(shardscanner.ResolvedScannerWorkflowConfig{}, assert.AnError) env.ExecuteWorkflow(CurrentScannerWorkflow, shardscanner.ScannerWorkflowParams{ Shards: shardscanner.Shards{ List: []int{1}, }, }) s.True(env.IsWorkflowCompleted()) s.ErrorContains(env.GetWorkflowError(), assert.AnError.Error()) } func Test_currentExecutionScannerHooks(t *testing.T) { hooks := currentExecutionScannerHooks() assert.NotNil(t, hooks) } func Test_currentExecutionScannerManager(t *testing.T) { mockRetryer := persistence.NewMockRetryer(gomock.NewController(t)) params := shardscanner.ScanShardActivityParams{ ScannerConfig: shardscanner.CustomScannerConfig{ "CollectionMutableState": "true", }, } manager := currentExecutionScannerManager(context.Background(), mockRetryer, params, nil) assert.NotNil(t, manager) } func (s *currentExecutionsWorkflowsSuite) TestCurrentFixerWorkflow_NewFixerWorkflow_Error() { env := s.NewTestWorkflowEnvironment() env.OnActivity(shardscanner.ActivityFixerCorruptedKeys, mock.Anything, mock.Anything).Return(&shardscanner.FixerCorruptedKeysActivityResult{}, assert.AnError) env.ExecuteWorkflow(CurrentFixerWorkflow, shardscanner.FixerWorkflowParams{}) s.True(env.IsWorkflowCompleted()) s.ErrorContains(env.GetWorkflowError(), assert.AnError.Error()) } func (s *currentExecutionsWorkflowsSuite) TestCurrentFixerWorkflow_Start_Error() { env := s.NewTestWorkflowEnvironment() env.OnActivity(shardscanner.ActivityFixerCorruptedKeys, mock.Anything, mock.Anything). Return(&shardscanner.FixerCorruptedKeysActivityResult{ CorruptedKeys: []shardscanner.CorruptedKeysEntry{{ShardID: 1, CorruptedKeys: store.Keys{UUID: uuid.New()}}}, MinShard: common.IntPtr(1), MaxShard: common.IntPtr(3), }, nil) env.OnActivity(shardscanner.ActivityFixerConfig, mock.Anything, mock.Anything).Return(&shardscanner.FixShardConfigResults{}, assert.AnError) env.ExecuteWorkflow(CurrentFixerWorkflow, shardscanner.FixerWorkflowParams{}) s.True(env.IsWorkflowCompleted()) s.ErrorContains(env.GetWorkflowError(), assert.AnError.Error()) } func (s *currentExecutionsWorkflowsSuite) TestCurrentFixerWorkflow_Success() { env := s.NewTestWorkflowEnvironment() env.OnActivity(shardscanner.ActivityFixerCorruptedKeys, mock.Anything, mock.Anything). Return(&shardscanner.FixerCorruptedKeysActivityResult{ CorruptedKeys: []shardscanner.CorruptedKeysEntry{{ShardID: 1, CorruptedKeys: store.Keys{UUID: uuid.New()}}}, MinShard: common.IntPtr(1), MaxShard: common.IntPtr(3), }, nil) env.OnActivity(shardscanner.ActivityFixerConfig, mock.Anything, mock.Anything).Return(&shardscanner.FixShardConfigResults{}, nil) env.OnActivity(shardscanner.ActivityFixShard, mock.Anything, mock.Anything).Return([]shardscanner.FixReport{{ShardID: 1}}, nil) env.ExecuteWorkflow(CurrentFixerWorkflow, shardscanner.FixerWorkflowParams{}) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) } func Test_currentExecutionCustomScannerConfig(t *testing.T) { mockClient := dynamicconfig.NewMockClient(gomock.NewController(t)) collection := dynamicconfig.NewCollection(mockClient, log.NewNoop()) mockClient.EXPECT().GetBoolValue(gomock.Any(), gomock.Any()).Return(true, nil).Times(2) ctx := shardscanner.ScannerContext{ Config: &shardscanner.ScannerConfig{ DynamicCollection: collection, }, } cfg := currentExecutionCustomScannerConfig(ctx) assert.NotNil(t, cfg) assert.Len(t, cfg, 2) assert.Equal(t, "true", cfg[invariant.CollectionHistory.String()]) assert.Equal(t, "true", cfg[invariant.CollectionMutableState.String()]) } func Test_currentExecutionFixerHooks(t *testing.T) { h := currentExecutionFixerHooks() assert.NotNil(t, h) } func TestCurrentExecutionConfig(t *testing.T) { mockClient := dynamicconfig.NewMockClient(gomock.NewController(t)) collection := dynamicconfig.NewCollection(mockClient, log.NewNoop()) cfg := CurrentExecutionConfig(collection) assert.NotNil(t, cfg) } func Test_currentExecutionScannerIterator(t *testing.T) { params := shardscanner.ScanShardActivityParams{ Shards: []int{1, 2, 3}, ScannerConfig: shardscanner.CustomScannerConfig{ "CollectionHistory": strconv.FormatBool(true), }, PageSize: 1, } ctrl := gomock.NewController(t) mockRetryer := persistence.NewMockRetryer(ctrl) mockRetryer.EXPECT().ListCurrentExecutions(gomock.Any(), gomock.Any()).Return(&persistence.ListCurrentExecutionsResponse{ Executions: []*persistence.CurrentWorkflowExecution{ { DomainID: constants.TestDomainID, WorkflowID: constants.TestWorkflowID, RunID: constants.TestRunID, }, }, }, nil).Times(1) mockRetryer.EXPECT().GetShardID().Return(2) i := currentExecutionScannerIterator(context.Background(), mockRetryer, params) assert.NotNil(t, i) } func Test_currentExecutionFixerIterator(t *testing.T) { ctx := context.Background() mockClient := blobstore.NewMockClient(gomock.NewController(t)) req := &blobstore.GetRequest{ Key: CurrentExecutionsFixerTaskListName + "_0.", } mockClient.EXPECT().Get(ctx, req).Return(&blobstore.GetResponse{}, nil).Times(1) it := currentExecutionFixerIterator( ctx, mockClient, store.Keys{UUID: CurrentExecutionsFixerTaskListName}, shardscanner.FixShardActivityParams{}) assert.NotNil(t, it) } ================================================ FILE: service/worker/scanner/executions/scantype_enumer_generated.go ================================================ // Code generated by "enumer -type=ScanType -output scantype_enumer_generated.go"; DO NOT EDIT. package executions import ( "fmt" "strings" ) const _ScanTypeName = "ConcreteExecutionTypeCurrentExecutionType" var _ScanTypeIndex = [...]uint8{0, 21, 41} const _ScanTypeLowerName = "concreteexecutiontypecurrentexecutiontype" func (i ScanType) String() string { if i < 0 || i >= ScanType(len(_ScanTypeIndex)-1) { return fmt.Sprintf("ScanType(%d)", i) } return _ScanTypeName[_ScanTypeIndex[i]:_ScanTypeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ScanTypeNoOp() { var x [1]struct{} _ = x[ConcreteExecutionType-(0)] _ = x[CurrentExecutionType-(1)] } var _ScanTypeValues = []ScanType{ConcreteExecutionType, CurrentExecutionType} var _ScanTypeNameToValueMap = map[string]ScanType{ _ScanTypeName[0:21]: ConcreteExecutionType, _ScanTypeLowerName[0:21]: ConcreteExecutionType, _ScanTypeName[21:41]: CurrentExecutionType, _ScanTypeLowerName[21:41]: CurrentExecutionType, } var _ScanTypeNames = []string{ _ScanTypeName[0:21], _ScanTypeName[21:41], } // ScanTypeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ScanTypeString(s string) (ScanType, error) { if val, ok := _ScanTypeNameToValueMap[s]; ok { return val, nil } if val, ok := _ScanTypeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ScanType values", s) } // ScanTypeValues returns all values of the enum func ScanTypeValues() []ScanType { return _ScanTypeValues } // ScanTypeStrings returns a slice of all String values of the enum func ScanTypeStrings() []string { strs := make([]string, len(_ScanTypeNames)) copy(strs, _ScanTypeNames) return strs } // IsAScanType returns "true" if the value is listed in the enum definition. "false" otherwise func (i ScanType) IsAScanType() bool { for _, v := range _ScanTypeValues { if i == v { return true } } return false } ================================================ FILE: service/worker/scanner/executions/types.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package executions //go:generate enumer -type=ScanType -output scantype_enumer_generated.go import ( "context" "strconv" "go.uber.org/zap" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/fetcher" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/service/worker/scanner/shardscanner" ) const ( // ConcreteExecutionType concrete execution entity ConcreteExecutionType ScanType = iota // CurrentExecutionType current execution entity CurrentExecutionType ) // ScanType is the enum for representing different entity types to scan type ScanType int type ( // InvariantFactory represents a function which returns Invariant InvariantFactory func(retryer persistence.Retryer, domainCache cache.DomainCache) invariant.Invariant // ExecutionFetcher represents a function which returns specific execution entity ExecutionFetcher func(ctx context.Context, retryer persistence.Retryer, request fetcher.ExecutionRequest) (entity.Entity, error) ) // ToBlobstoreEntity picks struct depending on scanner type. func (st ScanType) ToBlobstoreEntity() entity.Entity { switch st { case ConcreteExecutionType: return &entity.ConcreteExecution{} case CurrentExecutionType: return &entity.CurrentExecution{} } panic("unknown scan type") } // ToIterator selects appropriate iterator. It will panic if scan type is unknown. func (st ScanType) ToIterator() func(ctx context.Context, retryer persistence.Retryer, pageSize int) pagination.Iterator { switch st { case ConcreteExecutionType: return fetcher.ConcreteExecutionIterator case CurrentExecutionType: return fetcher.CurrentExecutionIterator default: panic("unknown scan type") } } // ToExecutionFetcher selects appropriate execution fetcher. Fetcher returns single execution entity. It will panic if scan type is unknown. func (st ScanType) ToExecutionFetcher() ExecutionFetcher { switch st { case ConcreteExecutionType: return fetcher.ConcreteExecution case CurrentExecutionType: return fetcher.CurrentExecution default: panic("unknown scan type") } } // ToInvariants returns list of invariants to be checked depending on scan type. func (st ScanType) ToInvariants(collections []invariant.Collection, logger *zap.Logger) []InvariantFactory { var fns []InvariantFactory switch st { case ConcreteExecutionType: for _, collection := range collections { switch collection { case invariant.CollectionDomain: fns = append(fns, invariant.NewInactiveDomainExists) case invariant.CollectionHistory: fns = append(fns, invariant.NewHistoryExists) case invariant.CollectionStale: fns = append(fns, func(pr persistence.Retryer, dc cache.DomainCache) invariant.Invariant { return invariant.NewStaleWorkflow(pr, dc, logger.Named(string(invariant.StaleWorkflow))) }) case invariant.CollectionMutableState: fns = append(fns, invariant.NewOpenCurrentExecution) } } return fns case CurrentExecutionType: for _, collection := range collections { switch collection { case invariant.CollectionMutableState: fns = append(fns, invariant.NewConcreteExecutionExists) } } return fns default: panic("unknown scan type") } } // ParseCollections converts string based map to list of collections func ParseCollections(params shardscanner.CustomScannerConfig) []invariant.Collection { var collections []invariant.Collection for k, v := range params { c, e := invariant.CollectionString(k) if e != nil { continue } enabled, err := strconv.ParseBool(v) if enabled && err == nil { collections = append(collections, c) } } return collections } ================================================ FILE: service/worker/scanner/executions/workflows_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package executions ================================================ FILE: service/worker/scanner/executor/executor.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package executor import ( "sync" "sync/atomic" "github.com/uber/cadence/common" "github.com/uber/cadence/common/metrics" ) type ( // Task defines the interface for a runnable task Task interface { // Run should execute the task and return well known status codes Run() TaskStatus } // Executor defines the interface for any executor which can // accept tasks and execute them based on some policy Executor interface { // Start starts the executor Start() // Stop stops the executor Stop() // Submit is a blocking call that accepts a task to execute Submit(task Task) bool // TaskCount returns the number of outstanding tasks in the executor TaskCount() int64 } // fixedPoolExecutor is an implementation of an executor that uses fixed size // goroutine pool. This executor also supports deferred execution of tasks // for fairness fixedPoolExecutor struct { size int maxDeferred int runQ *runQueue outstanding int64 status int32 metrics metrics.Client metricScope metrics.ScopeIdx stopC chan struct{} stopWG sync.WaitGroup } // TaskStatus is the return code from a Task TaskStatus int ) const ( // TaskStatusDone indicates task is finished successfully TaskStatusDone TaskStatus = iota // TaskStatusDefer indicates task should be scheduled again for execution at later time TaskStatusDefer // TaskStatusErr indicates task is finished with errors TaskStatusErr ) // NewFixedSizePoolExecutor returns an implementation of task executor that maintains // a fixed size pool of goroutines.The returned executor also allows task processing to // to be deferred for fairness. To defer processing of a task, simply return TaskStatsDefer // from your task.Run method. When a task is deferred, it will be added to the tail of a // deferredTaskQ which in turn will be processed after the current runQ is drained func NewFixedSizePoolExecutor(size int, maxDeferred int, metrics metrics.Client, scope metrics.ScopeIdx) Executor { stopC := make(chan struct{}) return &fixedPoolExecutor{ size: size, maxDeferred: maxDeferred, runQ: newRunQueue(size, stopC), metrics: metrics, metricScope: scope, stopC: stopC, } } // Start starts the executor func (e *fixedPoolExecutor) Start() { if !atomic.CompareAndSwapInt32(&e.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } for i := 0; i < e.size; i++ { e.stopWG.Add(1) go e.worker() } } // Stop stops the executor func (e *fixedPoolExecutor) Stop() { if !atomic.CompareAndSwapInt32(&e.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } close(e.stopC) e.stopWG.Wait() } // Submit is a blocking call that accepts a task for execution func (e *fixedPoolExecutor) Submit(task Task) bool { if !e.alive() { return false } added := e.runQ.add(task) if added { atomic.AddInt64(&e.outstanding, 1) } return added } // TaskCount returns the total of number of tasks currently outstanding func (e *fixedPoolExecutor) TaskCount() int64 { return atomic.LoadInt64(&e.outstanding) } func (e *fixedPoolExecutor) worker() { defer e.stopWG.Done() for e.alive() { task, ok := e.runQ.remove() if !ok { return } status := task.Run() if status == TaskStatusDefer { if e.runQ.deferredCount() < e.maxDeferred { e.runQ.addAndDefer(task) e.metrics.IncCounter(e.metricScope, metrics.ExecutorTasksDeferredCount) continue } e.metrics.IncCounter(e.metricScope, metrics.ExecutorTasksDroppedCount) } atomic.AddInt64(&e.outstanding, -1) } } func (e *fixedPoolExecutor) alive() bool { return atomic.LoadInt32(&e.status) == common.DaemonStatusStarted } ================================================ FILE: service/worker/scanner/executor/executor_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package executor import ( "sync" "sync/atomic" "testing" "time" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "github.com/uber/cadence/common" "github.com/uber/cadence/common/metrics" ) type ( ExecutorTestSuite struct { suite.Suite } testTask struct { next TaskStatus counter *int64 } ) func TestExecutionTestSuite(t *testing.T) { suite.Run(t, new(ExecutorTestSuite)) } func (s *ExecutorTestSuite) TestStartStop() { e := NewFixedSizePoolExecutor( 4, 4, metrics.NewClient(tally.NoopScope, metrics.Worker, metrics.HistogramMigration{}), metrics.TaskListScavengerScope) e.Start() e.Stop() } func (s *ExecutorTestSuite) TestTaskExecution() { e := NewFixedSizePoolExecutor( 32, 100, metrics.NewClient(tally.NoopScope, metrics.Worker, metrics.HistogramMigration{}), metrics.TaskListScavengerScope) e.Start() var runCounter int64 var startWG sync.WaitGroup for i := 0; i < 5; i++ { startWG.Add(1) go func() { defer startWG.Done() for i := 0; i < 20; i++ { if i%2 == 0 { e.Submit(&testTask{TaskStatusDefer, &runCounter}) continue } e.Submit(&testTask{TaskStatusDone, &runCounter}) } }() } s.True(common.AwaitWaitGroup(&startWG, time.Second*10)) s.True(s.awaitCompletion(e)) s.Equal(int64(150), runCounter) e.Stop() } func (s *ExecutorTestSuite) awaitCompletion(e Executor) bool { expiry := time.Now().Add(time.Second * 10) for time.Now().Before(expiry) { if e.TaskCount() == 0 { return true } time.Sleep(time.Millisecond * 50) } return false } func (tt *testTask) Run() TaskStatus { atomic.AddInt64(tt.counter, 1) status := tt.next if status == TaskStatusDefer { tt.next = TaskStatusDone } return status } ================================================ FILE: service/worker/scanner/executor/runq.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package executor import ( "container/list" "sync" ) type ( runQueue struct { tasks chan Task deferredTasks *threadSafeList stopC chan struct{} } // threadSafeList is a wrapper around list.List which // guarantees thread safety threadSafeList struct { sync.Mutex list *list.List } ) // newRunQueue returns an instance of task run queue func newRunQueue(size int, stopC chan struct{}) *runQueue { return &runQueue{ tasks: make(chan Task, size), deferredTasks: newThreadSafeList(), stopC: stopC, } } func (rq *runQueue) add(task Task) bool { select { case rq.tasks <- task: return true case <-rq.stopC: return false } } func (rq *runQueue) addAndDefer(task Task) { rq.deferredTasks.add(task) } // remove returns the next task from run queue // if there are no active tasks in runq, a task // from deferredQ is returned func (rq *runQueue) remove() (Task, bool) { // Give 1st priority to taskQ -if there are no more tasks, handout tasks from deferredQ select { case job, ok := <-rq.tasks: if !ok { return nil, false } return job, true default: if task := rq.deferredTasks.remove(); task != nil { return task.Value.(Task), true } } // at this point, there are no tasks either in taskQ or deferredQ // simply block on taskQ until we have tasks select { case job, ok := <-rq.tasks: if !ok { return nil, false } return job, true case <-rq.stopC: return nil, false } } func (rq *runQueue) deferredCount() int { return rq.deferredTasks.len() } // newThreadSafeList returns a new thread safe linked list func newThreadSafeList() *threadSafeList { return &threadSafeList{ list: list.New(), } } func (tl *threadSafeList) add(elem interface{}) { tl.Lock() defer tl.Unlock() tl.list.PushBack(elem) } func (tl *threadSafeList) len() int { tl.Lock() defer tl.Unlock() return tl.list.Len() } func (tl *threadSafeList) remove() *list.Element { tl.Lock() defer tl.Unlock() e := tl.list.Front() if e != nil { tl.list.Remove(e) } return e } ================================================ FILE: service/worker/scanner/history/scavenger.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package history import ( "context" "time" "go.uber.org/cadence/activity" "golang.org/x/time/rate" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( // ScavengerHeartbeatDetails is the heartbeat detail for HistoryScavengerActivity ScavengerHeartbeatDetails struct { NextPageToken []byte CurrentPage int SkipCount int ErrorCount int SuccCount int } // Scavenger is the type that holds the state for history scavenger daemon Scavenger struct { db p.HistoryManager client history.Client hbd ScavengerHeartbeatDetails rps int limiter *rate.Limiter maxWorkflowRetentionInDays dynamicproperties.IntPropertyFn metrics metrics.Client logger log.Logger isInTest bool domainCache cache.DomainCache } taskDetail struct { domainID string workflowID string runID string treeID string branchID string // passing along the current heartbeat details to make heartbeat within a task so that it won't timeout hbd ScavengerHeartbeatDetails } ) const ( // used this to decide how many goroutines to process rpsPerConcurrency = 50 pageSize = 1000 ) // only clean up history branches that older than this threshold // we double the MaxWorkflowRetentionPeriodInDays to avoid racing condition with history archival. // Our history archiver delete mutable state, and then upload history to blob store and then delete history. // This scanner will face racing condition with archiver because it relys on describe mutable state returning entityNotExist error. // That's why we need to keep MaxWorkflowRetentionPeriodInDays stable and not decreasing all the time. func getHistoryCleanupThreshold(maxWorkflowRetentionInDays int) time.Duration { return time.Hour * 24 * time.Duration(maxWorkflowRetentionInDays) * 2 } // NewScavenger returns an instance of history scavenger daemon // The Scavenger can be started by calling the Run() method on the // returned object. Calling the Run() method will result in one // complete iteration over all of the history branches in the system. For // each branch, the scavenger will attempt // - describe the corresponding workflow execution // - deletion of history itself, if there are no workflow execution func NewScavenger( db p.HistoryManager, rps int, client history.Client, hbd ScavengerHeartbeatDetails, metricsClient metrics.Client, logger log.Logger, maxWorkflowRetentionInDays dynamicproperties.IntPropertyFn, domainCache cache.DomainCache, ) *Scavenger { rateLimiter := rate.NewLimiter(rate.Limit(rps), rps) return &Scavenger{ db: db, client: client, hbd: hbd, rps: rps, limiter: rateLimiter, maxWorkflowRetentionInDays: maxWorkflowRetentionInDays, metrics: metricsClient, logger: logger, domainCache: domainCache, } } // Run runs the scavenger func (s *Scavenger) Run(ctx context.Context) (ScavengerHeartbeatDetails, error) { taskCh := make(chan taskDetail, pageSize) respCh := make(chan error, pageSize) concurrency := s.rps/rpsPerConcurrency + 1 for i := 0; i < concurrency; i++ { go s.startTaskProcessor(ctx, taskCh, respCh) } for { resp, err := s.db.GetAllHistoryTreeBranches(ctx, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, NextPageToken: s.hbd.NextPageToken, }) if err != nil { return s.hbd, err } batchCount := len(resp.Branches) skips := 0 errorsOnSplitting := 0 // send all tasks for _, br := range resp.Branches { if time.Now().Add(-1 * getHistoryCleanupThreshold(s.maxWorkflowRetentionInDays())).Before(br.ForkTime) { batchCount-- skips++ s.metrics.IncCounter(metrics.HistoryScavengerScope, metrics.HistoryScavengerSkipCount) continue } domainID, wid, rid, err := p.SplitHistoryGarbageCleanupInfo(br.Info) if err != nil { batchCount-- errorsOnSplitting++ s.logger.Error("scavenger: unable to parse the history cleanup info", tag.WorkflowTreeID(br.TreeID), tag.WorkflowBranchID(br.BranchID), tag.DetailInfo(br.Info)) s.metrics.IncCounter(metrics.HistoryScavengerScope, metrics.HistoryScavengerErrorCount) continue } taskCh <- taskDetail{ domainID: domainID, workflowID: wid, runID: rid, treeID: br.TreeID, branchID: br.BranchID, hbd: s.hbd, } } succCount := 0 errCount := 0 if batchCount > 0 { // wait for counters indicate this batch is done Loop: for { select { case err := <-respCh: if err == nil { s.metrics.IncCounter(metrics.HistoryScavengerScope, metrics.HistoryScavengerSuccessCount) succCount++ } else { s.metrics.IncCounter(metrics.HistoryScavengerScope, metrics.HistoryScavengerErrorCount) errCount++ } if succCount+errCount == batchCount { break Loop } case <-ctx.Done(): return s.hbd, ctx.Err() } } } s.hbd.CurrentPage++ s.hbd.NextPageToken = resp.NextPageToken s.hbd.SuccCount += succCount s.hbd.ErrorCount += errCount + errorsOnSplitting s.hbd.SkipCount += skips if !s.isInTest { activity.RecordHeartbeat(ctx, s.hbd) } if len(s.hbd.NextPageToken) == 0 { break } } return s.hbd, nil } func (s *Scavenger) startTaskProcessor( ctx context.Context, taskCh chan taskDetail, respCh chan error, ) { for { select { case <-ctx.Done(): return case task := <-taskCh: if isDone(ctx) { return } if !s.isInTest { activity.RecordHeartbeat(ctx, s.hbd) } err := s.limiter.Wait(ctx) if err != nil { respCh <- err s.logger.Error("encounter error when wait for rate limiter", getTaskLoggingTags(err, task)...) continue } // this checks if the mutableState still exists // if not then the history branch is garbage, we need to delete the history branch _, err = s.client.DescribeMutableState(ctx, &types.DescribeMutableStateRequest{ DomainUUID: task.domainID, Execution: &types.WorkflowExecution{ WorkflowID: task.workflowID, RunID: task.runID, }, }) if err != nil { if _, ok := err.(*types.EntityNotExistsError); ok { // deleting history branch var branchToken []byte branchToken, err = p.NewHistoryBranchTokenByBranchID(task.treeID, task.branchID) if err != nil { respCh <- err s.logger.Error("encounter error when creating branch token", getTaskLoggingTags(err, task)...) continue } domainName, err := s.domainCache.GetDomainName(task.domainID) if err != nil { respCh <- err s.logger.Error("Unexpected: Encountered error while fetching domain name", getTaskLoggingTags(err, task)...) continue } err = s.db.DeleteHistoryBranch(ctx, &p.DeleteHistoryBranchRequest{ BranchToken: branchToken, // This is a required argument but it is not needed for Cassandra. // Since this scanner is only for Cassandra, // we can fill any number here to let to code go through ShardID: common.IntPtr(1), DomainName: domainName, }) if err != nil { respCh <- err s.logger.Error("encounter error when deleting garbage history branch", getTaskLoggingTags(err, task)...) } else { // deleted garbage s.logger.Info("deleted history garbage", getTaskLoggingTags(nil, task)...) respCh <- nil } } else { s.logger.Error("encounter error when describing the mutable state", getTaskLoggingTags(err, task)...) respCh <- err } } else { // no garbage respCh <- nil } } } } func getTaskLoggingTags(err error, task taskDetail) []tag.Tag { if err != nil { return []tag.Tag{ tag.Error(err), tag.WorkflowDomainID(task.domainID), tag.WorkflowID(task.workflowID), tag.WorkflowRunID(task.runID), tag.WorkflowTreeID(task.treeID), tag.WorkflowBranchID(task.branchID), } } return []tag.Tag{ tag.WorkflowDomainID(task.domainID), tag.WorkflowID(task.workflowID), tag.WorkflowRunID(task.runID), tag.WorkflowTreeID(task.treeID), tag.WorkflowBranchID(task.branchID), } } func isDone(ctx context.Context) bool { select { case <-ctx.Done(): return true default: return false } } ================================================ FILE: service/worker/scanner/history/scavenger_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package history import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) type ( ScavengerTestSuite struct { suite.Suite logger log.Logger metric metrics.Client mockCache *cache.MockDomainCache } ) func TestScavengerTestSuite(t *testing.T) { suite.Run(t, new(ScavengerTestSuite)) } func (s *ScavengerTestSuite) SetupTest() { s.logger = testlogger.New(s.T()) s.metric = metrics.NewClient(tally.NoopScope, metrics.Worker, metrics.HistogramMigration{}) controller := gomock.NewController(s.T()) s.mockCache = cache.NewMockDomainCache(controller) } func (s *ScavengerTestSuite) createTestScavenger(rps int) (*mocks.HistoryV2Manager, *history.MockClient, *Scavenger) { db := &mocks.HistoryV2Manager{} controller := gomock.NewController(s.T()) workflowClient := history.NewMockClient(controller) maxWorkflowRetentionInDays := dynamicproperties.GetIntPropertyFn(dynamicproperties.MaxRetentionDays.DefaultInt()) scvgr := NewScavenger(db, rps, workflowClient, ScavengerHeartbeatDetails{}, s.metric, s.logger, maxWorkflowRetentionInDays, s.mockCache) scvgr.isInTest = true return db, workflowClient, scvgr } func (s *ScavengerTestSuite) TestAllSkipTasksTwoPages() { db, _, scvgr := s.createTestScavenger(100) db.On("GetAllHistoryTreeBranches", mock.Anything, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, }).Return(&p.GetAllHistoryTreeBranchesResponse{ NextPageToken: []byte("page1"), Branches: []p.HistoryBranchDetail{ { TreeID: "treeID1", BranchID: "branchID1", ForkTime: time.Now(), Info: p.BuildHistoryGarbageCleanupInfo("domainID1", "workflowID1", "runID1"), }, { TreeID: "treeID2", BranchID: "branchID2", ForkTime: time.Now(), Info: p.BuildHistoryGarbageCleanupInfo("domainID2", "workflowID2", "runID2"), }, }, }, nil).Once() db.On("GetAllHistoryTreeBranches", mock.Anything, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, NextPageToken: []byte("page1"), }).Return(&p.GetAllHistoryTreeBranchesResponse{ Branches: []p.HistoryBranchDetail{ { TreeID: "treeID3", BranchID: "branchID3", ForkTime: time.Now(), Info: p.BuildHistoryGarbageCleanupInfo("domainID3", "workflowID3", "runID3"), }, { TreeID: "treeID4", BranchID: "branchID4", ForkTime: time.Now(), Info: p.BuildHistoryGarbageCleanupInfo("domainID4", "workflowID4", "runID4"), }, }, }, nil).Once() ctx, cancel := context.WithCancel(context.Background()) defer cancel() hbd, err := scvgr.Run(ctx) s.Nil(err) s.Equal(4, hbd.SkipCount) s.Equal(0, hbd.SuccCount) s.Equal(0, hbd.ErrorCount) s.Equal(2, hbd.CurrentPage) s.Equal(0, len(hbd.NextPageToken)) } func (s *ScavengerTestSuite) TestAllErrorSplittingTasksTwoPages() { db, _, scvgr := s.createTestScavenger(100) db.On("GetAllHistoryTreeBranches", mock.Anything, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, }).Return(&p.GetAllHistoryTreeBranchesResponse{ NextPageToken: []byte("page1"), Branches: []p.HistoryBranchDetail{ { TreeID: "treeID1", BranchID: "branchID1", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: "error-info", }, { TreeID: "treeID2", BranchID: "branchID2", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: "error-info", }, }, }, nil).Once() db.On("GetAllHistoryTreeBranches", mock.Anything, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, NextPageToken: []byte("page1"), }).Return(&p.GetAllHistoryTreeBranchesResponse{ Branches: []p.HistoryBranchDetail{ { TreeID: "treeID3", BranchID: "branchID3", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: "error-info", }, { TreeID: "treeID4", BranchID: "branchID4", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: "error-info", }, }, }, nil).Once() ctx, cancel := context.WithCancel(context.Background()) defer cancel() hbd, err := scvgr.Run(ctx) s.Nil(err) s.Equal(0, hbd.SkipCount) s.Equal(0, hbd.SuccCount) s.Equal(4, hbd.ErrorCount) s.Equal(2, hbd.CurrentPage) s.Equal(0, len(hbd.NextPageToken)) } func (s *ScavengerTestSuite) TestNoGarbageTwoPages() { db, client, scvgr := s.createTestScavenger(100) db.On("GetAllHistoryTreeBranches", mock.Anything, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, }).Return(&p.GetAllHistoryTreeBranchesResponse{ NextPageToken: []byte("page1"), Branches: []p.HistoryBranchDetail{ { TreeID: "treeID1", BranchID: "branchID1", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID1", "workflowID1", "runID1"), }, { TreeID: "treeID2", BranchID: "branchID2", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID2", "workflowID2", "runID2"), }, }, }, nil).Once() db.On("GetAllHistoryTreeBranches", mock.Anything, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, NextPageToken: []byte("page1"), }).Return(&p.GetAllHistoryTreeBranchesResponse{ Branches: []p.HistoryBranchDetail{ { TreeID: "treeID3", BranchID: "branchID3", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID3", "workflowID3", "runID3"), }, { TreeID: "treeID4", BranchID: "branchID4", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID4", "workflowID4", "runID4"), }, }, }, nil).Once() client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID1", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID1", RunID: "runID1", }, }).Return(nil, nil) client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID2", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID2", RunID: "runID2", }, }).Return(nil, nil) client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID3", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID3", RunID: "runID3", }, }).Return(nil, nil) client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID4", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID4", RunID: "runID4", }, }).Return(nil, nil) ctx, cancel := context.WithCancel(context.Background()) defer cancel() hbd, err := scvgr.Run(ctx) s.Nil(err) s.Equal(0, hbd.SkipCount) s.Equal(4, hbd.SuccCount) s.Equal(0, hbd.ErrorCount) s.Equal(2, hbd.CurrentPage) s.Equal(0, len(hbd.NextPageToken)) } func (s *ScavengerTestSuite) TestDeletingBranchesTwoPages() { db, client, scvgr := s.createTestScavenger(100) db.On("GetAllHistoryTreeBranches", mock.Anything, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, }).Return(&p.GetAllHistoryTreeBranchesResponse{ NextPageToken: []byte("page1"), Branches: []p.HistoryBranchDetail{ { TreeID: "treeID1", BranchID: "branchID1", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID1", "workflowID1", "runID1"), }, { TreeID: "treeID2", BranchID: "branchID2", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID2", "workflowID2", "runID2"), }, }, }, nil).Once() db.On("GetAllHistoryTreeBranches", mock.Anything, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, NextPageToken: []byte("page1"), }).Return(&p.GetAllHistoryTreeBranchesResponse{ Branches: []p.HistoryBranchDetail{ { TreeID: "treeID3", BranchID: "branchID3", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID3", "workflowID3", "runID3"), }, { TreeID: "treeID4", BranchID: "branchID4", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID4", "workflowID4", "runID4"), }, }, }, nil).Once() client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID1", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID1", RunID: "runID1", }, }).Return(nil, &types.EntityNotExistsError{}) client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID2", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID2", RunID: "runID2", }, }).Return(nil, &types.EntityNotExistsError{}) client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID3", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID3", RunID: "runID3", }, }).Return(nil, &types.EntityNotExistsError{}) client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID4", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID4", RunID: "runID4", }, }).Return(nil, &types.EntityNotExistsError{}) domainName := "test-domainName" s.mockCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() branchToken1, err := p.NewHistoryBranchTokenByBranchID("treeID1", "branchID1") s.Nil(err) db.On("DeleteHistoryBranch", mock.Anything, &p.DeleteHistoryBranchRequest{ BranchToken: branchToken1, ShardID: common.IntPtr(1), DomainName: domainName, }).Return(nil).Once() branchToken2, err := p.NewHistoryBranchTokenByBranchID("treeID2", "branchID2") s.Nil(err) db.On("DeleteHistoryBranch", mock.Anything, &p.DeleteHistoryBranchRequest{ BranchToken: branchToken2, ShardID: common.IntPtr(1), DomainName: domainName, }).Return(nil).Once() branchToken3, err := p.NewHistoryBranchTokenByBranchID("treeID3", "branchID3") s.Nil(err) db.On("DeleteHistoryBranch", mock.Anything, &p.DeleteHistoryBranchRequest{ BranchToken: branchToken3, ShardID: common.IntPtr(1), DomainName: domainName, }).Return(nil).Once() branchToken4, err := p.NewHistoryBranchTokenByBranchID("treeID4", "branchID4") s.Nil(err) db.On("DeleteHistoryBranch", mock.Anything, &p.DeleteHistoryBranchRequest{ BranchToken: branchToken4, ShardID: common.IntPtr(1), DomainName: domainName, }).Return(nil).Once() ctx, cancel := context.WithCancel(context.Background()) defer cancel() hbd, err := scvgr.Run(ctx) s.Nil(err) s.Equal(0, hbd.SkipCount) s.Equal(4, hbd.SuccCount) s.Equal(0, hbd.ErrorCount) s.Equal(2, hbd.CurrentPage) s.Equal(0, len(hbd.NextPageToken)) } func (s *ScavengerTestSuite) TestMixesTwoPages() { db, client, scvgr := s.createTestScavenger(100) db.On("GetAllHistoryTreeBranches", mock.Anything, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, }).Return(&p.GetAllHistoryTreeBranchesResponse{ NextPageToken: []byte("page1"), Branches: []p.HistoryBranchDetail{ { // skip TreeID: "treeID1", BranchID: "branchID1", ForkTime: time.Now(), Info: p.BuildHistoryGarbageCleanupInfo("domainID1", "workflowID1", "runID1"), }, { // split error TreeID: "treeID2", BranchID: "branchID2", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: "error-info", }, }, }, nil).Once() db.On("GetAllHistoryTreeBranches", mock.Anything, &p.GetAllHistoryTreeBranchesRequest{ PageSize: pageSize, NextPageToken: []byte("page1"), }).Return(&p.GetAllHistoryTreeBranchesResponse{ Branches: []p.HistoryBranchDetail{ { // delete succ TreeID: "treeID3", BranchID: "branchID3", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID3", "workflowID3", "runID3"), }, { // delete fail TreeID: "treeID4", BranchID: "branchID4", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID4", "workflowID4", "runID4"), }, { // not delete TreeID: "treeID5", BranchID: "branchID5", ForkTime: time.Now().Add(-getHistoryCleanupThreshold(dynamicproperties.MaxRetentionDays.DefaultInt()) * 2), Info: p.BuildHistoryGarbageCleanupInfo("domainID5", "workflowID5", "runID5"), }, }, }, nil).Once() client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID3", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID3", RunID: "runID3", }, }).Return(nil, &types.EntityNotExistsError{}) client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID4", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID4", RunID: "runID4", }, }).Return(nil, &types.EntityNotExistsError{}) client.EXPECT().DescribeMutableState(gomock.Any(), &types.DescribeMutableStateRequest{ DomainUUID: "domainID5", Execution: &types.WorkflowExecution{ WorkflowID: "workflowID5", RunID: "runID5", }, }).Return(nil, nil) domainName := "test-domainName" s.mockCache.EXPECT().GetDomainName(gomock.Any()).Return(domainName, nil).AnyTimes() branchToken3, err := p.NewHistoryBranchTokenByBranchID("treeID3", "branchID3") s.Nil(err) db.On("DeleteHistoryBranch", mock.Anything, &p.DeleteHistoryBranchRequest{ BranchToken: branchToken3, ShardID: common.IntPtr(1), DomainName: domainName, }).Return(nil).Once() branchToken4, err := p.NewHistoryBranchTokenByBranchID("treeID4", "branchID4") s.Nil(err) db.On("DeleteHistoryBranch", mock.Anything, &p.DeleteHistoryBranchRequest{ BranchToken: branchToken4, ShardID: common.IntPtr(1), DomainName: domainName, }).Return(fmt.Errorf("failed to delete history")).Once() ctx, cancel := context.WithCancel(context.Background()) defer cancel() hbd, err := scvgr.Run(ctx) s.Nil(err) s.Equal(1, hbd.SkipCount) s.Equal(2, hbd.SuccCount) s.Equal(2, hbd.ErrorCount) s.Equal(2, hbd.CurrentPage) s.Equal(0, len(hbd.NextPageToken)) } ================================================ FILE: service/worker/scanner/scanner.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scanner import ( "context" "fmt" "time" "github.com/uber-go/tally" "go.uber.org/cadence" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/activity" "go.uber.org/cadence/client" "go.uber.org/cadence/worker" "go.uber.org/zap" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/service/worker/scanner/shardscanner" "github.com/uber/cadence/service/worker/scanner/tasklist" "github.com/uber/cadence/service/worker/workercommon" ) const ( // scannerStartUpDelay is to let services warm up scannerStartUpDelay = time.Second * 4 ) type ( contextKey string // Config defines the configuration for scanner Config struct { // ScannerPersistenceMaxQPS the max rate of calls to persistence // Right now is being used by historyScanner to determine the rate of persistence API calls ScannerPersistenceMaxQPS dynamicproperties.IntPropertyFn // TaskListScannerEnabled indicates if taskList scanner should be started as part of scanner TaskListScannerEnabled dynamicproperties.BoolPropertyFn // TaskListScannerOptions contains options for TaskListScanner TaskListScannerOptions tasklist.Options // Persistence contains the persistence configuration Persistence *config.Persistence // ClusterMetadata contains the metadata for this cluster ClusterMetadata cluster.Metadata // HistoryScannerEnabled indicates if history scanner should be started as part of scanner HistoryScannerEnabled dynamicproperties.BoolPropertyFn // ShardScanners is a list of shard scanner configs ShardScanners []*shardscanner.ScannerConfig MaxWorkflowRetentionInDays dynamicproperties.IntPropertyFn } // BootstrapParams contains the set of params needed to bootstrap // the scanner sub-system BootstrapParams struct { // Config contains the configuration for scanner Config Config // TallyScope is an instance of tally metrics scope TallyScope tally.Scope } // scannerContext is the context object that get's // passed around within the scanner workflows / activities scannerContext struct { resource resource.Resource cfg Config } // Scanner is the background sub-system that does full scans // of database tables to cleanup resources, monitor anomalies // and emit stats for analytics Scanner struct { context scannerContext tallyScope tally.Scope zapLogger *zap.Logger startWorkflowWithRetryFn func(string, time.Duration, resource.Resource, func(client client.Client) error) error newWorkerFn func(workflowserviceclient.Interface, string, string, worker.Options) worker.Worker } ) // New returns a new instance of scanner daemon // Scanner is the background sub-system that does full // scans of database tables in an attempt to cleanup // resources, monitor system anamolies and emit stats // for analysis and alerting func New( resource resource.Resource, params *BootstrapParams, ) *Scanner { zapLogger, err := zap.NewProduction() if err != nil { resource.GetLogger().Fatal("failed to initialize zap logger", tag.Error(err)) } zapLogger.Info("Initializing new scanner") return &Scanner{ context: scannerContext{ resource: resource, cfg: params.Config, }, tallyScope: params.TallyScope, zapLogger: zapLogger.Named("scanner"), startWorkflowWithRetryFn: workercommon.StartWorkflowWithRetry, newWorkerFn: worker.New, } } // Start starts the scanner func (s *Scanner) Start() error { ctx := context.Background() var workerTaskListNames []string var wtl []string for _, sc := range s.context.cfg.ShardScanners { ctx, wtl = s.startShardScanner(ctx, sc) workerTaskListNames = append(workerTaskListNames, wtl...) } if s.context.cfg.Persistence.DefaultStoreType() == config.StoreTypeSQL { if s.context.cfg.TaskListScannerEnabled() { ctx = s.startScanner( ctx, tlScannerWFStartOptions, tlScannerWFTypeName) workerTaskListNames = append(workerTaskListNames, tlScannerTaskListName) } } if s.context.cfg.HistoryScannerEnabled() { ctx = s.startScanner( ctx, historyScannerWFStartOptions, historyScannerWFTypeName) workerTaskListNames = append(workerTaskListNames, historyScannerTaskListName) } workerOpts := worker.Options{ Logger: s.zapLogger, MetricsScope: s.tallyScope, MaxConcurrentActivityExecutionSize: maxConcurrentActivityExecutionSize, MaxConcurrentDecisionTaskExecutionSize: maxConcurrentDecisionTaskExecutionSize, BackgroundActivityContext: ctx, } for _, tl := range workerTaskListNames { s.zapLogger.Info("Starting worker for task list", zap.String("TaskList", tl)) if err := s.newWorkerFn(s.context.resource.GetSDKClient(), constants.SystemLocalDomainName, tl, workerOpts).Start(); err != nil { s.zapLogger.Error("Failed to start worker", zap.String("TaskList", tl), zap.Error(err)) return err } } s.zapLogger.Info("Scanner started successfully", zap.Strings("workerTaskListNames", workerTaskListNames)) return nil } func (s *Scanner) startScanner(ctx context.Context, options client.StartWorkflowOptions, workflowName string) context.Context { go s.startWorkflowWithRetryFn(workflowName, scannerStartUpDelay, s.context.resource, func(client client.Client) error { return s.startWorkflow(client, options, workflowName, nil) }) return NewScannerContext(ctx, workflowName, s.context) } func (s *Scanner) startShardScanner( ctx context.Context, config *shardscanner.ScannerConfig, ) (context.Context, []string) { var workerTaskListNames []string if config.DynamicParams.ScannerEnabled() { ctx = shardscanner.NewScannerContext( ctx, config.ScannerWFTypeName, shardscanner.NewShardScannerContext(s.context.resource, config), ) go s.startWorkflowWithRetryFn( config.ScannerWFTypeName, scannerStartUpDelay, s.context.resource, func(client client.Client) error { return s.startWorkflow(client, config.StartWorkflowOptions, config.ScannerWFTypeName, shardscanner.ScannerWorkflowParams{ Shards: shardscanner.Shards{ Range: &shardscanner.ShardRange{ Min: 0, Max: s.context.cfg.Persistence.NumHistoryShards, }, }, }) }) workerTaskListNames = append(workerTaskListNames, config.StartWorkflowOptions.TaskList) } if config.DynamicParams.FixerEnabled() { ctx = shardscanner.NewFixerContext( ctx, config.FixerWFTypeName, shardscanner.NewShardFixerContext(s.context.resource, config), ) go s.startWorkflowWithRetryFn( config.FixerWFTypeName, scannerStartUpDelay, s.context.resource, func(client client.Client) error { return s.startWorkflow(client, config.StartFixerOptions, config.FixerWFTypeName, shardscanner.FixerWorkflowParams{ ScannerWorkflowWorkflowID: config.StartWorkflowOptions.ID, }) }) workerTaskListNames = append(workerTaskListNames, config.StartFixerOptions.TaskList) } return ctx, workerTaskListNames } func (s *Scanner) startWorkflow( client client.Client, options client.StartWorkflowOptions, workflowType string, workflowArg interface{}, ) error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) var err error if workflowArg != nil { _, err = client.StartWorkflow(ctx, options, workflowType, workflowArg) } else { _, err = client.StartWorkflow(ctx, options, workflowType) } cancel() if cadence.IsWorkflowExecutionAlreadyStartedError(err) { s.zapLogger.Error("Workflow had already started", zap.String("workflowType", workflowType), zap.Error(err)) return nil } if err != nil { s.zapLogger.Error("Failed to start workflow", zap.String("workflowType", workflowType), zap.Error(err)) } else { s.zapLogger.Info("Workflow started", zap.String("workflowType", workflowType)) } return err } // NewScannerContext provides context to be used as background activity context // it uses typed, private key to reduce access scope func NewScannerContext(ctx context.Context, workflowName string, scannerContext scannerContext) context.Context { return context.WithValue(ctx, contextKey(workflowName), scannerContext) } // getScannerContext extracts scanner context from activity context // it uses typed, private key to reduce access scope func getScannerContext(ctx context.Context) (scannerContext, error) { info := activity.GetInfo(ctx) if info.WorkflowType == nil { return scannerContext{}, fmt.Errorf("workflowType is nil") } val, ok := ctx.Value(contextKey(info.WorkflowType.Name)).(scannerContext) if !ok { return scannerContext{}, fmt.Errorf("context type is not %T for a key %q", val, info.WorkflowType.Name) } return val, nil } ================================================ FILE: service/worker/scanner/scanner_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package scanner import ( "errors" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/client" "go.uber.org/cadence/mocks" "go.uber.org/cadence/worker" "go.uber.org/goleak" "go.uber.org/mock/gomock" "go.uber.org/zap" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" commonResource "github.com/uber/cadence/common/resource" "github.com/uber/cadence/service/history/resource" "github.com/uber/cadence/service/worker/scanner/shardscanner" worker2 "github.com/uber/cadence/service/worker/worker" ) type scannerTestSuite struct { suite.Suite mockCtrl *gomock.Controller mockResource *resource.Test mockScope tally.Scope scanner *Scanner mockWorker *worker2.MockWorker mockClient *mocks.Client } func TestScannerSuite(t *testing.T) { suite.Run(t, new(scannerTestSuite)) } func (s *scannerTestSuite) SetupTest() { s.mockCtrl = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.mockCtrl, metrics.Worker) s.mockScope = tally.NoopScope s.mockWorker = worker2.NewMockWorker(s.mockCtrl) s.mockClient = new(mocks.Client) s.scanner = &Scanner{ context: scannerContext{ resource: s.mockResource, cfg: Config{}, }, tallyScope: s.mockScope, zapLogger: zap.NewNop(), startWorkflowWithRetryFn: func(_ string, _ time.Duration, _ commonResource.Resource, _ func(client client.Client) error) error { return nil }, newWorkerFn: func(_ workflowserviceclient.Interface, _ string, _ string, _ worker.Options) worker.Worker { return s.mockWorker }, } } func (s *scannerTestSuite) TearDownTest() { s.mockCtrl.Finish() } func (s *scannerTestSuite) TestNew() { params := &BootstrapParams{ Config: Config{}, TallyScope: s.mockScope, } s.NotNil(New(s.mockResource, params)) } func (s *scannerTestSuite) TestStart() { testCases := []struct { name string cfg Config setupMocks func() err error }{ { name: "with shard scanners", cfg: Config{ ShardScanners: []*shardscanner.ScannerConfig{ { ScannerWFTypeName: "testScannerWF1", DynamicParams: shardscanner.DynamicParams{ ScannerEnabled: func(opts ...dynamicproperties.FilterOption) bool { return true }, FixerEnabled: func(opts ...dynamicproperties.FilterOption) bool { return true }, }, StartWorkflowOptions: client.StartWorkflowOptions{ TaskList: "shard-scanner-task-list-1", }, ScannerHooks: func() *shardscanner.ScannerHooks { return &shardscanner.ScannerHooks{ GetScannerConfig: func(scanner shardscanner.ScannerContext) shardscanner.CustomScannerConfig { return map[string]string{} }, } }, FixerHooks: func() *shardscanner.FixerHooks { return &shardscanner.FixerHooks{ GetFixerConfig: func(fixer shardscanner.FixerContext) shardscanner.CustomScannerConfig { return map[string]string{} }, } }, }, }, Persistence: &config.Persistence{ DefaultStore: "nosql", DataStores: map[string]config.DataStore{ "nosql": { NoSQL: &config.NoSQL{}, }, }, }, HistoryScannerEnabled: func(opts ...dynamicproperties.FilterOption) bool { return false }, }, setupMocks: func() { // this is mocking the worker being instantiated and started // in here the mockWorker is being started twice, but in reality // each worker gets started only once s.mockWorker.EXPECT().Start().Return(nil).Times(1) s.mockWorker.EXPECT().Start().Return(nil).Times(1) }, }, { name: "with default store type SQL with TaskListScanner enabled", cfg: Config{ Persistence: &config.Persistence{ DefaultStore: "sql", DataStores: map[string]config.DataStore{ "sql": { SQL: &config.SQL{}, }, }, }, TaskListScannerEnabled: func(opts ...dynamicproperties.FilterOption) bool { return true }, HistoryScannerEnabled: func(opts ...dynamicproperties.FilterOption) bool { return false }, }, setupMocks: func() { s.mockWorker.EXPECT().Start().Return(nil).Times(1) }, }, { name: "with HistoryScanner enabled", cfg: Config{ Persistence: &config.Persistence{ DefaultStore: "nosql", DataStores: map[string]config.DataStore{ "nosql": { NoSQL: &config.NoSQL{}, }, }, }, TaskListScannerEnabled: func(opts ...dynamicproperties.FilterOption) bool { return false }, HistoryScannerEnabled: func(opts ...dynamicproperties.FilterOption) bool { return true }, }, setupMocks: func() { s.mockWorker.EXPECT().Start().Return(nil).Times(1) }, }, { name: "failed to start worker", cfg: Config{ Persistence: &config.Persistence{ DefaultStore: "nosql", DataStores: map[string]config.DataStore{ "nosql": { NoSQL: &config.NoSQL{}, }, }, }, TaskListScannerEnabled: func(opts ...dynamicproperties.FilterOption) bool { return false }, HistoryScannerEnabled: func(opts ...dynamicproperties.FilterOption) bool { return true }, }, setupMocks: func() { s.mockWorker.EXPECT().Start().Return(errors.New("some new error")).Times(1) }, err: errors.New("some new error"), }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { defer goleak.VerifyNone(s.T()) s.scanner.context.cfg = tc.cfg tc.setupMocks() err := s.scanner.Start() if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } func (s *scannerTestSuite) Test__startWorkflow() { testCases := []struct { name string options client.StartWorkflowOptions workflowType string args interface{} setupMocks func(client.StartWorkflowOptions, string, interface{}) err error }{ { name: "success with args", options: client.StartWorkflowOptions{ ID: "testIDWithArgs", }, workflowType: "workflowTypeWithArgs", args: "testArgs", setupMocks: func(options client.StartWorkflowOptions, workflowType string, args interface{}) { s.mockClient.On("StartWorkflow", mock.Anything, options, workflowType, args).Return(nil, nil) }, }, { name: "success with no args", options: client.StartWorkflowOptions{ ID: "testIDWithNoArgs", }, workflowType: "workflowTypeWithArgs", setupMocks: func(options client.StartWorkflowOptions, workflowType string, args interface{}) { s.mockClient.On("StartWorkflow", mock.Anything, options, workflowType).Return(nil, nil) }, }, { name: "WorkflowExecutionAlreadyStarted error", options: client.StartWorkflowOptions{ ID: "testIDWorkflowExecutionAlreadyStarted", }, workflowType: "workflowTypeWorkflowExecutionAlreadyStarted", setupMocks: func(options client.StartWorkflowOptions, workflowType string, args interface{}) { s.mockClient.On("StartWorkflow", mock.Anything, options, workflowType).Return(nil, &shared.WorkflowExecutionAlreadyStartedError{}) }, err: nil, }, { name: "another error", options: client.StartWorkflowOptions{ ID: "testIDAnotherError", }, workflowType: "workflowTypeAnotherError", setupMocks: func(options client.StartWorkflowOptions, workflowType string, args interface{}) { s.mockClient.On("StartWorkflow", mock.Anything, options, workflowType).Return(nil, errors.New("some random error")) }, err: errors.New("some random error"), }, } for _, tc := range testCases { s.T().Run(tc.name, func(t *testing.T) { tc.setupMocks(tc.options, tc.workflowType, tc.args) err := s.scanner.startWorkflow(s.mockClient, tc.options, tc.workflowType, tc.args) if tc.err != nil { s.Error(err) s.Equal(tc.err, err) } else { s.NoError(err) } }) } } ================================================ FILE: service/worker/scanner/shardscanner/activities.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "encoding/json" "errors" "fmt" "time" "go.uber.org/cadence" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" c "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/store" ) const ( // ActivityScannerEmitMetrics is the activity name for scannerEmitMetricsActivity ActivityScannerEmitMetrics = "cadence-sys-shardscanner-emit-metrics-activity" // ActivityScannerConfig is the activity name scannerConfigActivity ActivityScannerConfig = "cadence-sys-shardscanner-config-activity" // ActivityFixerConfig is the activity name fixerConfigActivity ActivityFixerConfig = "cadence-sys-shardscanner-fixer-config-activity" // ActivityScanShard is the activity name for scanShardActivity ActivityScanShard = "cadence-sys-shardscanner-scanshard-activity" // ActivityFixerCorruptedKeys is the activity name for fixerCorruptedKeysActivity ActivityFixerCorruptedKeys = "cadence-sys-shardscanner-corruptedkeys-activity" // ActivityFixShard is the activity name for fixShardActivity ActivityFixShard = "cadence-sys-shardscanner-fixshard-activity" // ShardCorruptKeysQuery is the query name for the query used to get all completed shards with at least one corruption ShardCorruptKeysQuery = "shard_corrupt_keys" ) // scannerConfigActivity will read dynamic config, apply overwrites and return a resolved config. func scannerConfigActivity( activityCtx context.Context, params ScannerConfigActivityParams, ) (ResolvedScannerWorkflowConfig, error) { ctx, err := GetScannerContext(activityCtx) if err != nil { return ResolvedScannerWorkflowConfig{}, err } dc := ctx.Config.DynamicParams result := ResolvedScannerWorkflowConfig{ GenericScannerConfig: GenericScannerConfig{ Enabled: dc.ScannerEnabled(), Concurrency: dc.Concurrency(), PageSize: dc.PageSize(), BlobstoreFlushThreshold: dc.BlobstoreFlushThreshold(), ActivityBatchSize: dc.ActivityBatchSize(), }, } if ctx.Hooks != nil && ctx.Hooks.GetScannerConfig != nil { result.CustomScannerConfig = ctx.Hooks.GetScannerConfig(ctx) } overwrites := params.Overwrites.GenericScannerConfig if overwrites.Enabled != nil { result.GenericScannerConfig.Enabled = *overwrites.Enabled } if overwrites.Concurrency != nil { result.GenericScannerConfig.Concurrency = *overwrites.Concurrency } if overwrites.PageSize != nil { result.GenericScannerConfig.PageSize = *overwrites.PageSize } if overwrites.BlobstoreFlushThreshold != nil { result.GenericScannerConfig.BlobstoreFlushThreshold = *overwrites.BlobstoreFlushThreshold } if overwrites.ActivityBatchSize != nil { result.GenericScannerConfig.ActivityBatchSize = *overwrites.ActivityBatchSize } if params.Overwrites.CustomScannerConfig != nil { result.CustomScannerConfig = *params.Overwrites.CustomScannerConfig } return result, nil } // scanShardActivity will scan a collection of shards for invariant violations. func scanShardActivity( activityCtx context.Context, params ScanShardActivityParams, ) ([]ScanReport, error) { heartbeatDetails := ScanShardHeartbeatDetails{ LastShardIndexHandled: -1, Reports: nil, } ctx, err := GetScannerContext(activityCtx) if err != nil { return nil, err } if activity.HasHeartbeatDetails(activityCtx) { if err := activity.GetHeartbeatDetails(activityCtx, &heartbeatDetails); err != nil { ctx.Logger.Error("getting heartbeat details", tag.Error(err)) return nil, err } } for i := heartbeatDetails.LastShardIndexHandled + 1; i < len(params.Shards); i++ { currentShardID := params.Shards[i] shardReport, err := scanShard(activityCtx, params, currentShardID, heartbeatDetails) if err != nil { ctx.Logger.Error("scanning shard", tag.Error(err)) return nil, err } heartbeatDetails = ScanShardHeartbeatDetails{ LastShardIndexHandled: i, Reports: append(heartbeatDetails.Reports, *shardReport), } } return heartbeatDetails.Reports, nil } func scanShard( activityCtx context.Context, params ScanShardActivityParams, shardID int, heartbeatDetails ScanShardHeartbeatDetails, ) (*ScanReport, error) { ctx, err := GetScannerContext(activityCtx) if err != nil { return nil, err } info := activity.GetInfo(activityCtx) scope := ctx.Scope.Tagged( metrics.ActivityTypeTag(ActivityScanShard), metrics.WorkflowTypeTag(info.WorkflowType.Name), metrics.DomainTag(constants.SystemLocalDomainName), ) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() if ctx.Hooks == nil { return nil, cadence.NewCustomError(ErrMissingHooks) } resources := ctx.Resource execManager, err := resources.GetExecutionManager(shardID) if err != nil { scope.IncCounter(metrics.CadenceFailures) return nil, err } pr := persistence.NewPersistenceRetryer(execManager, resources.GetHistoryManager(), c.CreatePersistenceRetryPolicy()) scanner := NewScanner( shardID, ctx.Hooks.Iterator(activityCtx, pr, params), resources.GetBlobstoreClient(), params.BlobstoreFlushThreshold, ctx.Hooks.Manager(activityCtx, pr, params, resources.GetDomainCache()), func() { activity.RecordHeartbeat(activityCtx, heartbeatDetails) }, scope, resources.GetDomainCache(), ) report := scanner.Scan(activityCtx) if report.Result.ControlFlowFailure != nil { scope.IncCounter(metrics.CadenceFailures) } return &report, nil } // fixerCorruptedKeysActivity will fetch the keys of blobs from shards with corruptions from a completed scan workflow. // If scan workflow is not closed or if query fails activity will return an error. // Accepts as input the shard to start query at and returns a next page token, therefore this activity can // be used to do pagination. func fixerCorruptedKeysActivity( activityCtx context.Context, params FixerCorruptedKeysActivityParams, ) (*FixerCorruptedKeysActivityResult, error) { ctx, err := GetFixerContext(activityCtx) if err != nil { return nil, err } client := ctx.Resource.GetSDKClient() if params.ScannerWorkflowRunID == "" { listResp, err := client.ListClosedWorkflowExecutions(activityCtx, &shared.ListClosedWorkflowExecutionsRequest{ Domain: c.StringPtr(constants.SystemLocalDomainName), MaximumPageSize: c.Int32Ptr(10), NextPageToken: nil, StartTimeFilter: &shared.StartTimeFilter{ EarliestTime: c.Int64Ptr(0), LatestTime: c.Int64Ptr(time.Now().UnixNano()), }, ExecutionFilter: &shared.WorkflowExecutionFilter{ WorkflowId: c.StringPtr(params.ScannerWorkflowWorkflowID), }, }) if err != nil { return nil, err } if len(listResp.Executions) > 10 { return nil, errors.New("got unexpected number of executions back from list") } // ListClosedWorkflowExecutions API doesn't support querying by workflow ID and status filter at the same time, // and we want to avoid using a scan result with Terminated state. for _, executionInfo := range listResp.Executions { if *executionInfo.CloseStatus == shared.WorkflowExecutionCloseStatusContinuedAsNew { params.ScannerWorkflowRunID = *executionInfo.Execution.RunId break } } if len(params.ScannerWorkflowRunID) == 0 { return nil, errors.New("failed to find a recent scanner workflow execution with ContinuedAsNew status") } } descResp, err := client.DescribeWorkflowExecution(activityCtx, &shared.DescribeWorkflowExecutionRequest{ Domain: c.StringPtr(constants.SystemLocalDomainName), Execution: &shared.WorkflowExecution{ WorkflowId: c.StringPtr(params.ScannerWorkflowWorkflowID), RunId: c.StringPtr(params.ScannerWorkflowRunID), }, }) if err != nil { return nil, err } if descResp.WorkflowExecutionInfo.CloseStatus == nil { return nil, cadence.NewCustomError(ErrScanWorkflowNotClosed) } queryArgs := PaginatedShardQueryRequest{ StartingShardID: params.StartingShardID, } queryArgsBytes, err := json.Marshal(queryArgs) if err != nil { return nil, cadence.NewCustomError(ErrSerialization) } queryResp, err := client.QueryWorkflow(activityCtx, &shared.QueryWorkflowRequest{ Domain: c.StringPtr(constants.SystemLocalDomainName), Execution: &shared.WorkflowExecution{ WorkflowId: c.StringPtr(params.ScannerWorkflowWorkflowID), RunId: c.StringPtr(params.ScannerWorkflowRunID), }, Query: &shared.WorkflowQuery{ QueryType: c.StringPtr(ShardCorruptKeysQuery), QueryArgs: queryArgsBytes, }, }) if err != nil { return nil, err } queryResult := &ShardCorruptKeysQueryResult{} if err := json.Unmarshal(queryResp.QueryResult, &queryResult); err != nil { return nil, cadence.NewCustomError(ErrSerialization) } var corrupted []CorruptedKeysEntry var minShardID *int var maxShardID *int for sid, keys := range queryResult.Result { if minShardID == nil || *minShardID > sid { minShardID = c.IntPtr(sid) } if maxShardID == nil || *maxShardID < sid { maxShardID = c.IntPtr(sid) } corrupted = append(corrupted, CorruptedKeysEntry{ ShardID: sid, CorruptedKeys: keys, }) } return &FixerCorruptedKeysActivityResult{ CorruptedKeys: corrupted, MinShard: minShardID, MaxShard: maxShardID, ShardQueryPaginationToken: queryResult.ShardQueryPaginationToken, }, nil } type ( FixShardConfigParams struct { // intentionally empty, no args needed currently. just reserving arg space for future needs. } FixShardConfigResults struct { EnabledInvariants CustomScannerConfig } ) // fixerConfigActivity returns a list of all enabled invariants for this fixer. // The type of the workflow determines the type of the fixer (concrete, current, etc). // // It essentially mirrors scannerConfigActivity, but does not try to merge into a common structure. func fixerConfigActivity(activityCtx context.Context, params FixShardConfigParams) (*FixShardConfigResults, error) { ctx, err := GetFixerContext(activityCtx) if err != nil { return nil, err } cfg := ctx.Hooks.GetFixerConfig(ctx) if len(cfg) == 0 { // sanity check for new code. historically this field did not exist, now it is required to be populated. return nil, fmt.Errorf(`invalid empty fixer config, you must explicitly specify "true" or "false" for all relevant invariants`) } return &FixShardConfigResults{ EnabledInvariants: cfg, }, nil } // fixShardActivity will fix a collection of shards. func fixShardActivity( activityCtx context.Context, params FixShardActivityParams, ) ([]FixReport, error) { ctx, err := GetFixerContext(activityCtx) if err != nil { return nil, err } heartbeatDetails := FixShardHeartbeatDetails{ LastShardIndexHandled: -1, Reports: nil, } if activity.HasHeartbeatDetails(activityCtx) { if err := activity.GetHeartbeatDetails(activityCtx, &heartbeatDetails); err != nil { ctx.Logger.Error("getting heartbeat details", tag.Error(err)) return nil, err } } for i := heartbeatDetails.LastShardIndexHandled + 1; i < len(params.CorruptedKeysEntries); i++ { currentShardID := params.CorruptedKeysEntries[i].ShardID currentKeys := params.CorruptedKeysEntries[i].CorruptedKeys shardReport, err := fixShard(activityCtx, params, currentShardID, currentKeys, heartbeatDetails) if err != nil { ctx.Logger.Error("fixing shard", tag.Error(err)) return nil, err } heartbeatDetails = FixShardHeartbeatDetails{ LastShardIndexHandled: i, Reports: append(heartbeatDetails.Reports, *shardReport), } } return heartbeatDetails.Reports, nil } func fixShard( activityCtx context.Context, params FixShardActivityParams, shardID int, corruptedKeys store.Keys, heartbeatDetails FixShardHeartbeatDetails, ) (*FixReport, error) { ctx, err := GetFixerContext(activityCtx) if err != nil { return nil, err } resource := ctx.Resource info := activity.GetInfo(activityCtx) scope := ctx.Scope.Tagged( metrics.ActivityTypeTag(ActivityFixShard), metrics.WorkflowTypeTag(info.WorkflowType.Name), metrics.DomainTag(constants.SystemLocalDomainName), ) sw := scope.StartTimer(metrics.CadenceLatency) defer sw.Stop() if ctx.Hooks == nil { return nil, cadence.NewCustomError(ErrMissingHooks) } execManager, err := resource.GetExecutionManager(shardID) if err != nil { scope.IncCounter(metrics.CadenceFailures) return nil, err } pr := persistence.NewPersistenceRetryer(execManager, resource.GetHistoryManager(), c.CreatePersistenceRetryPolicy()) fixer := NewFixer( activityCtx, shardID, ctx.Hooks.InvariantManager(activityCtx, pr, params, resource.GetDomainCache()), ctx.Hooks.Iterator(activityCtx, resource.GetBlobstoreClient(), corruptedKeys, params), resource.GetBlobstoreClient(), params.ResolvedFixerWorkflowConfig.BlobstoreFlushThreshold, func() { activity.RecordHeartbeat(activityCtx, heartbeatDetails) }, resource.GetDomainCache(), ctx.Config.DynamicParams.AllowDomain, scope, ) report := fixer.Fix() if report.Result.ControlFlowFailure != nil { scope.IncCounter(metrics.CadenceFailures) } return &report, nil } // scannerEmitMetricsActivity will emit metrics for a complete run of ShardScanner func scannerEmitMetricsActivity( activityCtx context.Context, params ScannerEmitMetricsActivityParams, ) error { ctx, err := GetScannerContext(activityCtx) if err != nil { return err } info := activity.GetInfo(activityCtx) scope := ctx.Scope.Tagged( metrics.ActivityTypeTag(ActivityScannerEmitMetrics), metrics.WorkflowTypeTag(info.WorkflowType.Name), metrics.DomainTag(constants.SystemLocalDomainName), ) scope.UpdateGauge(metrics.CadenceShardSuccessGauge, float64(params.ShardSuccessCount)) scope.UpdateGauge(metrics.CadenceShardFailureGauge, float64(params.ShardControlFlowFailureCount)) agg := params.AggregateReportResult scope.UpdateGauge(metrics.ScannerExecutionsGauge, float64(agg.EntitiesCount)) scope.UpdateGauge(metrics.ScannerCorruptedGauge, float64(agg.CorruptedCount)) scope.UpdateGauge(metrics.ScannerCheckFailedGauge, float64(agg.CheckFailedCount)) for k, v := range agg.CorruptionByType { scope.Tagged(metrics.InvariantTypeTag(string(k))).UpdateGauge(metrics.ScannerCorruptionByTypeGauge, float64(v)) } shardStats := params.ShardDistributionStats scope.UpdateGauge(metrics.ScannerShardSizeMaxGauge, float64(shardStats.Max)) scope.UpdateGauge(metrics.ScannerShardSizeMedianGauge, float64(shardStats.Median)) scope.UpdateGauge(metrics.ScannerShardSizeMinGauge, float64(shardStats.Min)) scope.UpdateGauge(metrics.ScannerShardSizeNinetyGauge, float64(shardStats.P90)) scope.UpdateGauge(metrics.ScannerShardSizeSeventyFiveGauge, float64(shardStats.P75)) scope.UpdateGauge(metrics.ScannerShardSizeTwentyFiveGauge, float64(shardStats.P25)) scope.UpdateGauge(metrics.ScannerShardSizeTenGauge, float64(shardStats.P10)) return nil } ================================================ FILE: service/worker/scanner/shardscanner/activities_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "encoding/json" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/suite" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/common/resource" ) const testWorkflowName = "default-test-workflow-type-name" type activitiesSuite struct { suite.Suite testsuite.WorkflowTestSuite controller *gomock.Controller mockResource *resource.Test } func TestActivitiesSuite(t *testing.T) { suite.Run(t, new(activitiesSuite)) } func (s *activitiesSuite) SetupSuite() { activity.Register(scannerConfigActivity) activity.Register(fixerCorruptedKeysActivity) activity.Register(scanShardActivity) activity.Register(fixShardActivity) } func (s *activitiesSuite) SetupTest() { s.controller = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.Worker) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).AnyTimes() s.mockResource.DomainCache = domainCache } func (s *activitiesSuite) TearDownTest() { s.controller.Finish() } func (s *activitiesSuite) TestScanShardActivity() { testCases := []struct { params ScanShardActivityParams wantErr bool managerHook func(ctx context.Context, pr persistence.Retryer, params ScanShardActivityParams, cache cache.DomainCache) invariant.Manager itHook func(ctx context.Context, pr persistence.Retryer, params ScanShardActivityParams) pagination.Iterator workflowName string }{ { params: ScanShardActivityParams{ Shards: []int{0}, }, managerHook: func(ctx context.Context, pr persistence.Retryer, params ScanShardActivityParams, cache cache.DomainCache) invariant.Manager { manager := invariant.NewMockManager(s.controller) manager.EXPECT().RunChecks(gomock.Any(), gomock.Any()). AnyTimes(). Return( invariant.ManagerCheckResult{CheckResultType: invariant.CheckResultTypeHealthy}, ) return manager }, itHook: func(ctx context.Context, pr persistence.Retryer, params ScanShardActivityParams) pagination.Iterator { it := pagination.NewMockIterator(s.controller) calls := 0 it.EXPECT().HasNext().DoAndReturn( func() bool { if calls > 1 { return false } calls++ return true }, ).AnyTimes() it.EXPECT().Next().Return(&entity.ConcreteExecution{}, nil).Times(2) return it }, workflowName: testWorkflowName, wantErr: false, }, { params: ScanShardActivityParams{ Shards: []int{0}, }, workflowName: "wrong-test-name", wantErr: true, }, } for _, tc := range testCases { env := s.NewTestActivityEnvironment() hooks, _ := NewScannerHooks(tc.managerHook, tc.itHook, func(scanner ScannerContext) CustomScannerConfig { return nil // no config overrides }) sc := NewShardScannerContext(s.mockResource, &ScannerConfig{ ScannerHooks: func() *ScannerHooks { return hooks }, }) ctx := NewScannerContext(context.Background(), tc.workflowName, sc) env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: ctx, }) report, err := env.ExecuteActivity(scanShardActivity, tc.params) if tc.wantErr { s.Error(err) break } else { s.NoError(err) } var reports []ScanReport s.NoError(report.Get(&reports)) for _, v := range s.mockResource.MetricsScope.Snapshot().Timers() { tags := v.Tags() s.Equal("cadence-sys-shardscanner-scanshard-activity", tags["activityType"]) s.Equal(tc.workflowName, tags["workflowType"]) } } } func (s *activitiesSuite) TestFixShardActivity() { testCases := []struct { name string params FixShardActivityParams wantErr bool managerHook FixerManagerCB itHook FixerIteratorCB }{ { name: "run fixer", wantErr: false, params: FixShardActivityParams{ CorruptedKeysEntries: []CorruptedKeysEntry{ {ShardID: 1, CorruptedKeys: struct { UUID string MinPage int MaxPage int Extension store.Extension }{UUID: "test-uuid", MinPage: 0, MaxPage: 1, Extension: "test-ext"}}, }, ResolvedFixerWorkflowConfig: ResolvedFixerWorkflowConfig{}, }, managerHook: func(ctx context.Context, pr persistence.Retryer, p FixShardActivityParams, cache cache.DomainCache) invariant.Manager { manager := invariant.NewMockManager(s.controller) manager.EXPECT().RunFixes(gomock.Any(), gomock.Any()). AnyTimes(). Return( invariant.ManagerFixResult{FixResultType: invariant.FixResultTypeFixed}, ) return manager }, itHook: func(ctx context.Context, client blobstore.Client, k store.Keys, params FixShardActivityParams) store.ScanOutputIterator { it := store.NewMockScanOutputIterator(s.controller) calls := 0 it.EXPECT().HasNext().DoAndReturn( func() bool { if calls > 1 { return false } calls++ return true }, ).AnyTimes() it.EXPECT().Next().Return(&store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "test_domain", }, }, }, nil).AnyTimes() return it }, }, } for _, tc := range testCases { s.Run(tc.name, func() { s.mockResource.BlobstoreClient. EXPECT(). Put(gomock.Any(), gomock.Any()). Return(&blobstore.PutResponse{}, nil). Times(2) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).AnyTimes() s.mockResource.DomainCache = domainCache cfg := &ScannerConfig{ DynamicParams: DynamicParams{ AllowDomain: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), }, } if tc.itHook != nil && tc.managerHook != nil { cfg.FixerHooks = func() *FixerHooks { return &FixerHooks{ InvariantManager: tc.managerHook, Iterator: tc.itHook, } } } fc := NewShardFixerContext(s.mockResource, cfg) env := s.NewTestActivityEnvironment() env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: NewFixerContext(context.Background(), testWorkflowName, fc), }) report, execErr := env.ExecuteActivity(fixShardActivity, tc.params) if tc.wantErr { s.Error(execErr) } else { var reports []FixReport s.NoError(report.Get(&reports)) } }) } } func (s *activitiesSuite) TestScannerConfigActivity() { testCases := []struct { dynamicParams *DynamicParams params ScannerConfigActivityParams resolved ResolvedScannerWorkflowConfig addHook bool }{ { dynamicParams: &DynamicParams{ ScannerEnabled: dynamicproperties.GetBoolPropertyFn(true), Concurrency: dynamicproperties.GetIntPropertyFn(10), PageSize: dynamicproperties.GetIntPropertyFn(100), ActivityBatchSize: dynamicproperties.GetIntPropertyFn(10), BlobstoreFlushThreshold: dynamicproperties.GetIntPropertyFn(1000), }, params: ScannerConfigActivityParams{ Overwrites: ScannerWorkflowConfigOverwrites{}, }, addHook: true, resolved: ResolvedScannerWorkflowConfig{ GenericScannerConfig: GenericScannerConfig{ Enabled: true, Concurrency: 10, ActivityBatchSize: 10, PageSize: 100, BlobstoreFlushThreshold: 1000, }, CustomScannerConfig: CustomScannerConfig{ "test-key": "test-value", }, }, }, { dynamicParams: &DynamicParams{ ScannerEnabled: dynamicproperties.GetBoolPropertyFn(true), Concurrency: dynamicproperties.GetIntPropertyFn(10), PageSize: dynamicproperties.GetIntPropertyFn(100), ActivityBatchSize: dynamicproperties.GetIntPropertyFn(10), BlobstoreFlushThreshold: dynamicproperties.GetIntPropertyFn(1000), }, params: ScannerConfigActivityParams{ Overwrites: ScannerWorkflowConfigOverwrites{}, }, resolved: ResolvedScannerWorkflowConfig{ GenericScannerConfig: GenericScannerConfig{ Enabled: true, Concurrency: 10, ActivityBatchSize: 10, PageSize: 100, BlobstoreFlushThreshold: 1000, }, }, }, { dynamicParams: &DynamicParams{ ScannerEnabled: dynamicproperties.GetBoolPropertyFn(true), Concurrency: dynamicproperties.GetIntPropertyFn(10), ActivityBatchSize: dynamicproperties.GetIntPropertyFn(100), PageSize: dynamicproperties.GetIntPropertyFn(100), BlobstoreFlushThreshold: dynamicproperties.GetIntPropertyFn(1000), }, params: ScannerConfigActivityParams{ Overwrites: ScannerWorkflowConfigOverwrites{ GenericScannerConfig: GenericScannerConfigOverwrites{ Enabled: common.BoolPtr(false), ActivityBatchSize: common.IntPtr(1), BlobstoreFlushThreshold: common.IntPtr(100), }, CustomScannerConfig: &CustomScannerConfig{ "test": "test", }, }, }, resolved: ResolvedScannerWorkflowConfig{ GenericScannerConfig: GenericScannerConfig{ Enabled: false, Concurrency: 10, ActivityBatchSize: 1, PageSize: 100, BlobstoreFlushThreshold: 100, }, CustomScannerConfig: CustomScannerConfig{ "test": "test", }, }, }, } for _, tc := range testCases { env := s.NewTestActivityEnvironment() cfg := &ScannerConfig{ DynamicParams: *tc.dynamicParams, ScannerHooks: func() *ScannerHooks { return &ScannerHooks{} }, } if tc.addHook { cfg.ScannerHooks = func() *ScannerHooks { return &ScannerHooks{ GetScannerConfig: func(scanner ScannerContext) CustomScannerConfig { return map[string]string{"test-key": "test-value"} }, } } } env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: NewScannerContext( context.Background(), testWorkflowName, NewShardScannerContext(s.mockResource, cfg), ), }, ) resolvedValue, err := env.ExecuteActivity(scannerConfigActivity, tc.params) s.NoError(err) var resolved ResolvedScannerWorkflowConfig s.NoError(resolvedValue.Get(&resolved)) s.Equal(tc.resolved, resolved) } } func (s *activitiesSuite) TestFixerCorruptedKeysActivity() { s.mockResource.SDKClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(&shared.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &shared.WorkflowExecutionInfo{ CloseStatus: shared.WorkflowExecutionCloseStatusCompleted.Ptr(), }, }, nil) queryResult := &ShardCorruptKeysQueryResult{ Result: map[int]store.Keys{ 1: {UUID: "first"}, 2: {UUID: "second"}, 3: {UUID: "third"}, }, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: common.IntPtr(4), IsDone: false, }, } queryResultData, err := json.Marshal(queryResult) response := &shared.ListClosedWorkflowExecutionsResponse{ Executions: []*shared.WorkflowExecutionInfo{ { CloseStatus: shared.WorkflowExecutionCloseStatusCompleted.Ptr(), }, { Execution: &shared.WorkflowExecution{ WorkflowId: common.StringPtr("test-list-workflow-id"), RunId: common.StringPtr(uuid.New()), }, Type: &shared.WorkflowType{ Name: common.StringPtr("test-list-workflow-type"), }, StartTime: common.Int64Ptr(time.Now().UnixNano()), CloseTime: common.Int64Ptr(time.Now().Add(time.Hour).UnixNano()), CloseStatus: shared.WorkflowExecutionCloseStatusContinuedAsNew.Ptr(), HistoryLength: common.Int64Ptr(12), }, }, } s.NoError(err) s.mockResource.SDKClient.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(response, nil) s.mockResource.SDKClient.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()).Return(&shared.QueryWorkflowResponse{ QueryResult: queryResultData, }, nil) env := s.getFixerActivityEnvironment() fixerResultValue, err := env.ExecuteActivity(fixerCorruptedKeysActivity, FixerCorruptedKeysActivityParams{}) s.NoError(err) fixerResult := &FixerCorruptedKeysActivityResult{} s.NoError(fixerResultValue.Get(&fixerResult)) s.Equal(1, *fixerResult.MinShard) s.Equal(3, *fixerResult.MaxShard) s.Equal(ShardQueryPaginationToken{ NextShardID: common.IntPtr(4), IsDone: false, }, fixerResult.ShardQueryPaginationToken) s.Contains(fixerResult.CorruptedKeys, CorruptedKeysEntry{ ShardID: 1, CorruptedKeys: store.Keys{ UUID: "first", }, }) s.Contains(fixerResult.CorruptedKeys, CorruptedKeysEntry{ ShardID: 2, CorruptedKeys: store.Keys{ UUID: "second", }, }) s.Contains(fixerResult.CorruptedKeys, CorruptedKeysEntry{ ShardID: 3, CorruptedKeys: store.Keys{ UUID: "third", }, }) } func (s *activitiesSuite) TestFixerCorruptedKeysActivity_Fails_WhenNoSuitableExecutionsAreFound() { response := &shared.ListClosedWorkflowExecutionsResponse{ Executions: []*shared.WorkflowExecutionInfo{ { CloseStatus: shared.WorkflowExecutionCloseStatusCompleted.Ptr(), }, { CloseStatus: shared.WorkflowExecutionCloseStatusCanceled.Ptr(), }, { CloseStatus: shared.WorkflowExecutionCloseStatusTimedOut.Ptr(), }, { CloseStatus: shared.WorkflowExecutionCloseStatusTerminated.Ptr(), }, { CloseStatus: shared.WorkflowExecutionCloseStatusFailed.Ptr(), }, }, } s.mockResource.SDKClient.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(response, nil) env := s.getFixerActivityEnvironment() fixerResultValue, err := env.ExecuteActivity(fixerCorruptedKeysActivity, FixerCorruptedKeysActivityParams{}) s.Nil(fixerResultValue) s.EqualError(err, "failed to find a recent scanner workflow execution with ContinuedAsNew status") } func (s *activitiesSuite) getFixerActivityEnvironment() *testsuite.TestActivityEnvironment { env := s.NewTestActivityEnvironment() cfg := &ScannerConfig{ DynamicParams: DynamicParams{ AllowDomain: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), }, FixerHooks: func() *FixerHooks { return &FixerHooks{} }, } fc := NewShardFixerContext(s.mockResource, cfg) env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: NewFixerContext(context.Background(), testWorkflowName, fc), }) return env } ================================================ FILE: service/worker/scanner/shardscanner/aggregators.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "fmt" "sort" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" ) const ( // ShardStatusSuccess indicates the scan on the shard ran successfully ShardStatusSuccess ShardStatus = "success" // ShardStatusControlFlowFailure indicates the scan on the shard failed ShardStatusControlFlowFailure ShardStatus = "control_flow_failure" // ShardStatusRunning indicates the shard has not completed yet ShardStatusRunning ShardStatus = "running" maxShardQueryResult = 1000 ) type ( // ShardStatus is the type which indicates the status of a shard scan. ShardStatus string // ShardStatusResult indicates the status for all shards ShardStatusResult map[int]ShardStatus // ShardStatusSummaryResult indicates the counts of shards in each status ShardStatusSummaryResult map[ShardStatus]int // AggregateScanReportResult indicates the result of summing together all // shard reports which have finished scan. AggregateScanReportResult ScanStats // AggregateFixReportResult indicates the result of summing together all // shard reports that have finished for fix. AggregateFixReportResult FixStats // ShardCorruptKeysResult is a map of all shards which have finished scan successfully and have at least one corruption ShardCorruptKeysResult map[int]store.Keys // ScanReportError is a type that is used to send either error or report on a channel. // Exactly one of Report and ErrorStr should be non-nil. ScanReportError struct { Reports []ScanReport ErrorStr *string } // FixerCorruptedKeysActivityResult is the result of fixerCorruptedKeysActivity FixerCorruptedKeysActivityResult struct { CorruptedKeys []CorruptedKeysEntry MinShard *int MaxShard *int ShardQueryPaginationToken ShardQueryPaginationToken } // CorruptedKeysEntry is a pair of shardID and corrupted keys CorruptedKeysEntry struct { ShardID int CorruptedKeys store.Keys } // ScanShardHeartbeatDetails is the heartbeat details for scan shard ScanShardHeartbeatDetails struct { LastShardIndexHandled int Reports []ScanReport } // FixShardHeartbeatDetails is the heartbeat details for the fix shard FixShardHeartbeatDetails struct { LastShardIndexHandled int Reports []FixReport } // FixReportError is a type that is used to send either error or report on a channel. // Exactly one of Report and ErrorStr should be non-nil. FixReportError struct { Reports []FixReport ErrorStr *string } // ShardSizeQueryRequest is the request used for ShardSizeQuery. // The following must be true: 0 <= StartIndex < EndIndex <= len(shards successfully finished) // The following must be true: EndIndex - StartIndex <= maxShardQueryResult. // StartIndex is inclusive, EndIndex is exclusive. ShardSizeQueryRequest struct { StartIndex int EndIndex int } // PaginatedShardQueryRequest is the request used for queries which return results over all shards PaginatedShardQueryRequest struct { // StartingShardID is the first shard to start iteration from. // Setting to nil will start iteration from the beginning of the shards. StartingShardID *int // LimitShards indicates the maximum number of results that can be returned. // If nil or larger than allowed maximum, will default to maximum allowed. LimitShards *int } // ShardQueryPaginationToken is used to return information used to make the next query ShardQueryPaginationToken struct { // NextShardID is one greater than the highest shard returned in the current query. // NextShardID is nil if IsDone is true. // It is possible to get NextShardID != nil and on the next call to get an empty result with IsDone = true. NextShardID *int IsDone bool } // ShardStatusQueryResult is the query result for ShardStatusQuery ShardStatusQueryResult struct { Result ShardStatusResult ShardQueryPaginationToken ShardQueryPaginationToken } // ShardCorruptKeysQueryResult is the query result for ShardCorruptKeysQuery ShardCorruptKeysQueryResult struct { Result ShardCorruptKeysResult ShardQueryPaginationToken ShardQueryPaginationToken } // ShardSizeQueryResult is the result from ShardSizeQuery. // Contains sorted list of shards, sorted by the number of executions per shard. ShardSizeQueryResult []ShardSizeTuple // ShardSizeTuple indicates the size and sorted index of a single shard ShardSizeTuple struct { ShardID int EntitiesCount int64 } // ShardDistributionStats contains stats on the distribution of executions in shards. // It is used by the ScannerEmitMetricsActivityParams. ShardDistributionStats struct { Max int64 Median int64 Min int64 P90 int64 P75 int64 P25 int64 P10 int64 } // ShardFixResultAggregator is used to keep aggregated fix metrics ShardFixResultAggregator struct { minShard int maxShard int reports map[int]FixReport domainStats map[string]*FixStats status ShardStatusResult statusSummary ShardStatusSummaryResult aggregation AggregateFixReportResult } // ShardScanResultAggregator is used to keep aggregated scan metrics ShardScanResultAggregator struct { minShard int maxShard int reports map[int]ScanReport domainStats map[string]*ScanStats status ShardStatusResult statusSummary ShardStatusSummaryResult aggregation AggregateScanReportResult shardSizes ShardSizeQueryResult corruptionKeys map[int]store.Keys } // DomainReportQueryRequest is the request used for queries which return stats broken by domains DomainReportQueryRequest struct { // DomainID specifies a single domain for which the stats are requested // Setting to nil indicates stats for all domains are requested DomainID *string } // DomainReportQueryResult is the query result for DomainReportQuery in the scanner workflow DomainScanReportQueryResult struct { Reports []DomainScanStats } // DomainFixReportQueryResult is the query result for DomainReportQuery in the fixer workflow DomainFixReportQueryResult struct { Reports []DomainFixStats } ) // NewShardFixResultAggregator returns an instance of ShardFixResultAggregator func NewShardFixResultAggregator( corruptKeys []CorruptedKeysEntry, minShard int, maxShard int, ) *ShardFixResultAggregator { status := make(map[int]ShardStatus, len(corruptKeys)) for _, s := range corruptKeys { status[s.ShardID] = ShardStatusRunning } statusSummary := map[ShardStatus]int{ ShardStatusRunning: len(corruptKeys), ShardStatusSuccess: 0, ShardStatusControlFlowFailure: 0, } return &ShardFixResultAggregator{ minShard: minShard, maxShard: maxShard, reports: make(map[int]FixReport), domainStats: make(map[string]*FixStats), status: status, statusSummary: statusSummary, aggregation: AggregateFixReportResult{}, } } // GetStatusSummary returns scan status summary. func (a *ShardScanResultAggregator) GetStatusSummary() ShardStatusSummaryResult { return a.statusSummary } // GetStatusSummary returns fix status summary. func (a *ShardFixResultAggregator) GetStatusSummary() ShardStatusSummaryResult { return a.statusSummary } // GetAggregation returns scan aggregation. func (a *ShardFixResultAggregator) GetAggregation() AggregateFixReportResult { return a.aggregation } // GetStatusResult returns paginated results for a range of shards func (a *ShardFixResultAggregator) GetStatusResult(req PaginatedShardQueryRequest) (*ShardStatusQueryResult, error) { return getStatusResult(a.minShard, a.maxShard, req, a.status) } // GetStatusResult returns stats broken by domain IDs func (a *ShardFixResultAggregator) GetDomainStatus(req DomainReportQueryRequest) (*DomainFixReportQueryResult, error) { responseStats := []DomainFixStats{} if req.DomainID == nil { domainIDs := make([]string, 0) for domainID := range a.domainStats { domainIDs = append(domainIDs, domainID) } sort.Strings(domainIDs) for _, domainID := range domainIDs { responseStats = append(responseStats, DomainFixStats{ DomainID: domainID, Stats: *a.domainStats[domainID], }) } } else if domainStats, ok := a.domainStats[*req.DomainID]; ok { responseStats = append(responseStats, DomainFixStats{ DomainID: *req.DomainID, Stats: *domainStats, }) } else { return nil, fmt.Errorf("domain %v does not exist or relevant stats have not been reported yet", req.DomainID) } result := &DomainFixReportQueryResult{ Reports: responseStats, } return result, nil } // AddReport adds fix report for a shard. func (a *ShardFixResultAggregator) AddReport(report FixReport) { a.reports[report.ShardID] = report a.statusSummary[ShardStatusRunning]-- if report.Result.ControlFlowFailure != nil { a.status[report.ShardID] = ShardStatusControlFlowFailure a.statusSummary[ShardStatusControlFlowFailure]++ } else { a.status[report.ShardID] = ShardStatusSuccess a.statusSummary[ShardStatusSuccess]++ } if report.Result.ShardFixKeys != nil { a.adjustAggregation(report.Stats, func(a, b int64) int64 { return a + b }) } if report.DomainStats != nil { a.updateDomainStats(report) } } func (a *ShardFixResultAggregator) updateDomainStats(report FixReport) { for domainID, domainStats := range report.DomainStats { if _, ok := a.domainStats[domainID]; !ok { a.domainStats[domainID] = &FixStats{} } aggregateStats := a.domainStats[domainID] aggregateStats.EntitiesCount += domainStats.EntitiesCount aggregateStats.FixedCount += domainStats.FixedCount aggregateStats.SkippedCount += domainStats.SkippedCount aggregateStats.FailedCount += domainStats.FailedCount } } // GetReport returns fix report for a shard. func (a *ShardFixResultAggregator) GetReport(shardID int) (*FixReport, error) { if _, ok := a.status[shardID]; !ok { return nil, fmt.Errorf("shard %v is not included in shards which will be processed", shardID) } if report, ok := a.reports[shardID]; ok { return &report, nil } return nil, fmt.Errorf("shard %v has not finished yet, check back later for report", shardID) } func (a *ShardFixResultAggregator) GetAllFixResults() (map[int]FixResult, error) { result := make(map[int]FixResult, len(a.reports)) for k, v := range a.reports { if v.Result.Empty() { continue } result[k] = v.Result } return result, nil } func (a *ShardFixResultAggregator) adjustAggregation(stats FixStats, fn func(a, b int64) int64) { a.aggregation.EntitiesCount = fn(a.aggregation.EntitiesCount, stats.EntitiesCount) a.aggregation.SkippedCount = fn(a.aggregation.SkippedCount, stats.SkippedCount) a.aggregation.FailedCount = fn(a.aggregation.FailedCount, stats.FailedCount) a.aggregation.FixedCount = fn(a.aggregation.FixedCount, stats.FixedCount) } // NewShardScanResultAggregator returns aggregator for a scan result. func NewShardScanResultAggregator( shards []int, minShard int, maxShard int, ) *ShardScanResultAggregator { status := make(map[int]ShardStatus) for _, s := range shards { status[s] = ShardStatusRunning } statusSummary := map[ShardStatus]int{ ShardStatusSuccess: 0, ShardStatusControlFlowFailure: 0, ShardStatusRunning: len(shards), } return &ShardScanResultAggregator{ minShard: minShard, maxShard: maxShard, reports: make(map[int]ScanReport), domainStats: map[string]*ScanStats{}, status: status, statusSummary: statusSummary, shardSizes: nil, aggregation: AggregateScanReportResult{ CorruptionByType: make(map[invariant.Name]int64), }, corruptionKeys: make(map[int]store.Keys), } } // GetAggregateReport returns aggregated scan report. func (a *ShardScanResultAggregator) GetAggregateReport() AggregateScanReportResult { return a.aggregation } // GetShardSizeQueryResult returns shard size statistics. func (a *ShardScanResultAggregator) GetShardSizeQueryResult(req ShardSizeQueryRequest) (ShardSizeQueryResult, error) { if req.StartIndex < 0 || req.StartIndex >= req.EndIndex || req.EndIndex > len(a.shardSizes) { return nil, fmt.Errorf("index out of bounds exception (required startIndex >= 0 && startIndex < endIndex && endIndex <= %v)", len(a.shardSizes)) } if req.EndIndex-req.StartIndex > maxShardQueryResult { return nil, fmt.Errorf("too many shards requested, the limit is %v", maxShardQueryResult) } return a.shardSizes[req.StartIndex:req.EndIndex], nil } // GetCorruptionKeys returns a list of corrupt keys func (a *ShardScanResultAggregator) GetCorruptionKeys(req PaginatedShardQueryRequest) (*ShardCorruptKeysQueryResult, error) { startingShardID := a.minShard if req.StartingShardID != nil { startingShardID = *req.StartingShardID } if err := shardInBounds(a.minShard, a.maxShard, startingShardID); err != nil { return nil, err } limit := maxShardQueryResult if req.LimitShards != nil && *req.LimitShards > 0 && *req.LimitShards < maxShardQueryResult { limit = *req.LimitShards } result := make(map[int]store.Keys) currentShardID := startingShardID for len(result) < limit && currentShardID <= a.maxShard { keys, ok := a.corruptionKeys[currentShardID] if !ok { currentShardID++ continue } result[currentShardID] = keys currentShardID++ } if currentShardID > a.maxShard { return &ShardCorruptKeysQueryResult{ Result: result, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: nil, IsDone: true, }, }, nil } return &ShardCorruptKeysQueryResult{ Result: result, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: ¤tShardID, IsDone: false, }, }, nil } // GetStatusResult returns scan status for a range of shards. func (a *ShardScanResultAggregator) GetStatusResult(req PaginatedShardQueryRequest) (*ShardStatusQueryResult, error) { return getStatusResult(a.minShard, a.maxShard, req, a.status) } // AddReport adds scan report for a shard. func (a *ShardScanResultAggregator) AddReport(report ScanReport) { if report.Result.ShardScanKeys != nil { a.insertReportIntoSizes(report) } a.reports[report.ShardID] = report a.statusSummary[ShardStatusRunning]-- if report.Result.ControlFlowFailure != nil { a.status[report.ShardID] = ShardStatusControlFlowFailure a.statusSummary[ShardStatusControlFlowFailure]++ } else { a.status[report.ShardID] = ShardStatusSuccess a.statusSummary[ShardStatusSuccess]++ } if report.Result.ShardScanKeys != nil { a.adjustAggregation(report.Stats, func(a, b int64) int64 { return a + b }) if report.Result.ShardScanKeys.Corrupt != nil { a.corruptionKeys[report.ShardID] = *report.Result.ShardScanKeys.Corrupt } } if report.DomainStats != nil { a.updateDomainStats(report) } } func (a *ShardScanResultAggregator) updateDomainStats(report ScanReport) { for domainID, domainStats := range report.DomainStats { if _, ok := a.domainStats[domainID]; !ok { a.domainStats[domainID] = &ScanStats{ CorruptionByType: make(map[invariant.Name]int64), } } aggregateStats := a.domainStats[domainID] aggregateStats.CorruptedCount += domainStats.CorruptedCount aggregateStats.CheckFailedCount += domainStats.CheckFailedCount aggregateStats.EntitiesCount += domainStats.EntitiesCount for corruptionType, count := range domainStats.CorruptionByType { aggregateStats.CorruptionByType[corruptionType] += count } } } func (a *ShardScanResultAggregator) insertReportIntoSizes(report ScanReport) { tuple := ShardSizeTuple{ ShardID: report.ShardID, EntitiesCount: report.Stats.EntitiesCount, } insertIndex := 0 for insertIndex < len(a.shardSizes) { if a.shardSizes[insertIndex].EntitiesCount < tuple.EntitiesCount { break } insertIndex++ } a.shardSizes = append(a.shardSizes, ShardSizeTuple{}) copy(a.shardSizes[insertIndex+1:], a.shardSizes[insertIndex:]) a.shardSizes[insertIndex] = tuple } // GetShardDistributionStats returns aggregated size statistics func (a *ShardScanResultAggregator) GetShardDistributionStats() ShardDistributionStats { return ShardDistributionStats{ Max: a.shardSizes[0].EntitiesCount, Median: a.shardSizes[int(float64(len(a.shardSizes))*.5)].EntitiesCount, Min: a.shardSizes[len(a.shardSizes)-1].EntitiesCount, P90: a.shardSizes[int(float64(len(a.shardSizes))*.1)].EntitiesCount, P75: a.shardSizes[int(float64(len(a.shardSizes))*.25)].EntitiesCount, P25: a.shardSizes[int(float64(len(a.shardSizes))*.75)].EntitiesCount, P10: a.shardSizes[int(float64(len(a.shardSizes))*.9)].EntitiesCount, } } // GetReport returns a report for a single shard. func (a *ShardScanResultAggregator) GetReport(shardID int) (*ScanReport, error) { if _, ok := a.status[shardID]; !ok { return nil, fmt.Errorf("shard %v is not included in shards which will be processed", shardID) } if report, ok := a.reports[shardID]; ok { return &report, nil } return nil, fmt.Errorf("shard %v has not finished yet, check back later for report", shardID) } // GetDomainStatus returns stats broken by domain IDs func (a *ShardScanResultAggregator) GetDomainStatus(req DomainReportQueryRequest) (*DomainScanReportQueryResult, error) { responseStats := []DomainScanStats{} if req.DomainID == nil { domainIDs := make([]string, 0) for domainID := range a.domainStats { domainIDs = append(domainIDs, domainID) } sort.Strings(domainIDs) for _, domainID := range domainIDs { responseStats = append(responseStats, DomainScanStats{ DomainID: domainID, Stats: *a.domainStats[domainID], }) } } else if domainStats, ok := a.domainStats[*req.DomainID]; ok { responseStats = append(responseStats, DomainScanStats{ DomainID: *req.DomainID, Stats: *domainStats, }) } else { return nil, fmt.Errorf("domain %v does not exist or relevant stats have not been reported yet", req.DomainID) } result := &DomainScanReportQueryResult{ Reports: responseStats, } return result, nil } func (a *ShardScanResultAggregator) adjustAggregation(stats ScanStats, fn func(a, b int64) int64) { a.aggregation.EntitiesCount = fn(a.aggregation.EntitiesCount, stats.EntitiesCount) a.aggregation.CorruptedCount = fn(a.aggregation.CorruptedCount, stats.CorruptedCount) a.aggregation.CheckFailedCount = fn(a.aggregation.CheckFailedCount, stats.CheckFailedCount) for k, v := range stats.CorruptionByType { a.aggregation.CorruptionByType[k] = fn(a.aggregation.CorruptionByType[k], v) } } func (a *ShardScanResultAggregator) GetAllScanResults() (map[int]ScanResult, error) { result := make(map[int]ScanResult, len(a.reports)) for k, v := range a.reports { if v.Result.Empty() { continue } result[k] = v.Result } return result, nil } func getStatusResult( minShardID int, maxShardID int, req PaginatedShardQueryRequest, status ShardStatusResult, ) (*ShardStatusQueryResult, error) { startingShardID := minShardID if req.StartingShardID != nil { startingShardID = *req.StartingShardID } if err := shardInBounds(minShardID, maxShardID, startingShardID); err != nil { return nil, err } limit := maxShardQueryResult if req.LimitShards != nil && *req.LimitShards > 0 && *req.LimitShards < maxShardQueryResult { limit = *req.LimitShards } result := make(map[int]ShardStatus) currentShardID := startingShardID for len(result) < limit && currentShardID <= maxShardID { status, ok := status[currentShardID] if !ok { currentShardID++ continue } result[currentShardID] = status currentShardID++ } if currentShardID > maxShardID { return &ShardStatusQueryResult{ Result: result, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: nil, IsDone: true, }, }, nil } return &ShardStatusQueryResult{ Result: result, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: ¤tShardID, IsDone: false, }, }, nil } func shardInBounds(minShardID, maxShardID, shardID int) error { if shardID > maxShardID || shardID < minShardID { return fmt.Errorf("requested shard %v is outside of bounds (min: %v and max: %v)", shardID, minShardID, maxShardID) } return nil } ================================================ FILE: service/worker/scanner/shardscanner/aggregators_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "math/rand" "testing" "github.com/stretchr/testify/suite" c "github.com/uber/cadence/common" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" ) type aggregatorsSuite struct { suite.Suite } func TestAggregatorSuite(t *testing.T) { suite.Run(t, new(aggregatorsSuite)) } func (s *aggregatorsSuite) TestShardScanResultAggregator() { agg := NewShardScanResultAggregator([]int{1, 2, 3}, 1, 3) expected := &ShardScanResultAggregator{ minShard: 1, maxShard: 3, reports: map[int]ScanReport{}, domainStats: map[string]*ScanStats{}, status: map[int]ShardStatus{ 1: ShardStatusRunning, 2: ShardStatusRunning, 3: ShardStatusRunning, }, aggregation: AggregateScanReportResult{ CorruptionByType: make(map[invariant.Name]int64), }, corruptionKeys: make(map[int]store.Keys), statusSummary: map[ShardStatus]int{ ShardStatusRunning: 3, ShardStatusControlFlowFailure: 0, ShardStatusSuccess: 0, }, shardSizes: nil, } s.Equal(expected, agg) report, err := agg.GetReport(1) s.Nil(report) s.Equal("shard 1 has not finished yet, check back later for report", err.Error()) report, err = agg.GetReport(5) s.Nil(report) s.Equal("shard 5 is not included in shards which will be processed", err.Error()) firstReport := ScanReport{ ShardID: 1, Stats: ScanStats{ EntitiesCount: 10, CorruptedCount: 3, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 2, invariant.OpenCurrentExecution: 1, }, }, Result: ScanResult{ ShardScanKeys: &ScanKeys{ Corrupt: &store.Keys{ UUID: "test_uuid", }, }, }, DomainStats: map[string]*ScanStats{ "ABC": &ScanStats{ EntitiesCount: 5, CorruptedCount: 2, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 1, invariant.OpenCurrentExecution: 1, }, }, "DEF": &ScanStats{ EntitiesCount: 5, CorruptedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 1, }, }, }, } agg.AddReport(firstReport) expected.status[1] = ShardStatusSuccess expected.statusSummary[ShardStatusRunning] = 2 expected.statusSummary[ShardStatusSuccess] = 1 expected.reports[1] = firstReport expected.shardSizes = []ShardSizeTuple{ { ShardID: 1, EntitiesCount: 10, }, } expected.aggregation.EntitiesCount = 10 expected.aggregation.CorruptedCount = 3 expected.aggregation.CheckFailedCount = 1 expected.aggregation.CorruptionByType = map[invariant.Name]int64{ invariant.HistoryExists: 2, invariant.OpenCurrentExecution: 1, } expected.domainStats["ABC"] = &ScanStats{ EntitiesCount: 5, CorruptedCount: 2, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 1, invariant.OpenCurrentExecution: 1, }, } expected.domainStats["DEF"] = &ScanStats{ EntitiesCount: 5, CorruptedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 1, }, } expected.corruptionKeys = map[int]store.Keys{ 1: { UUID: "test_uuid", }, } s.Equal(expected, agg) report, err = agg.GetReport(1) s.NoError(err) s.Equal(firstReport, *report) secondReport := ScanReport{ ShardID: 2, Stats: ScanStats{ EntitiesCount: 10, CorruptedCount: 3, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 2, invariant.OpenCurrentExecution: 1, }, }, Result: ScanResult{ ControlFlowFailure: &ControlFlowFailure{}, }, DomainStats: map[string]*ScanStats{ "ABC": &ScanStats{ EntitiesCount: 5, CorruptedCount: 2, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 1, invariant.OpenCurrentExecution: 1, }, }, "DEF": &ScanStats{ EntitiesCount: 5, CorruptedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 1, }, }, }, } agg.AddReport(secondReport) expected.status[2] = ShardStatusControlFlowFailure expected.statusSummary[ShardStatusRunning] = 1 expected.statusSummary[ShardStatusControlFlowFailure] = 1 expected.reports[2] = secondReport expected.shardSizes = []ShardSizeTuple{ { ShardID: 1, EntitiesCount: 10, }, } expected.domainStats["ABC"].EntitiesCount *= 2 expected.domainStats["ABC"].CorruptedCount *= 2 expected.domainStats["ABC"].CheckFailedCount *= 2 expected.domainStats["ABC"].CorruptionByType[invariant.HistoryExists] *= 2 expected.domainStats["ABC"].CorruptionByType[invariant.OpenCurrentExecution] *= 2 expected.domainStats["DEF"].EntitiesCount *= 2 expected.domainStats["DEF"].CorruptedCount *= 2 expected.domainStats["DEF"].CorruptionByType[invariant.HistoryExists] *= 2 s.Equal(expected, agg) shardStatus, err := agg.GetStatusResult(PaginatedShardQueryRequest{ StartingShardID: c.IntPtr(1), LimitShards: c.IntPtr(2), }) s.NoError(err) s.Equal(&ShardStatusQueryResult{ Result: map[int]ShardStatus{ 1: ShardStatusSuccess, 2: ShardStatusControlFlowFailure, }, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: c.IntPtr(3), IsDone: false, }, }, shardStatus) corruptedKeys, err := agg.GetCorruptionKeys(PaginatedShardQueryRequest{ StartingShardID: c.IntPtr(1), LimitShards: c.IntPtr(3), }) s.NoError(err) s.Equal(&ShardCorruptKeysQueryResult{ Result: map[int]store.Keys{ 1: { UUID: "test_uuid", }, }, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: nil, IsDone: true, }, }, corruptedKeys) domainReport, err := agg.GetDomainStatus(DomainReportQueryRequest{ DomainID: c.StringPtr("ABC"), }) s.NoError(err) s.Equal(&DomainScanReportQueryResult{ Reports: []DomainScanStats{ { DomainID: "ABC", Stats: ScanStats{ EntitiesCount: 10, CorruptedCount: 4, CheckFailedCount: 2, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 2, invariant.OpenCurrentExecution: 2, }, }, }, }, }, domainReport) allDomainsReport, err := agg.GetDomainStatus(DomainReportQueryRequest{}) s.NoError(err) s.Equal(&DomainScanReportQueryResult{ Reports: []DomainScanStats{ { DomainID: "ABC", Stats: ScanStats{ EntitiesCount: 10, CorruptedCount: 4, CheckFailedCount: 2, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 2, invariant.OpenCurrentExecution: 2, }, }, }, { DomainID: "DEF", Stats: ScanStats{ EntitiesCount: 10, CorruptedCount: 2, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 2, }, }, }, }, }, allDomainsReport) } func (s *aggregatorsSuite) TestShardFixResultAggregator() { agg := NewShardFixResultAggregator([]CorruptedKeysEntry{{ShardID: 1}, {ShardID: 2}, {ShardID: 3}}, 1, 3) expected := &ShardFixResultAggregator{ minShard: 1, maxShard: 3, reports: map[int]FixReport{}, domainStats: map[string]*FixStats{}, status: map[int]ShardStatus{ 1: ShardStatusRunning, 2: ShardStatusRunning, 3: ShardStatusRunning, }, statusSummary: map[ShardStatus]int{ ShardStatusRunning: 3, ShardStatusControlFlowFailure: 0, ShardStatusSuccess: 0, }, aggregation: AggregateFixReportResult{}, } s.Equal(expected, agg) report, err := agg.GetReport(1) s.Nil(report) s.Equal("shard 1 has not finished yet, check back later for report", err.Error()) report, err = agg.GetReport(5) s.Nil(report) s.Equal("shard 5 is not included in shards which will be processed", err.Error()) firstReport := FixReport{ ShardID: 1, Stats: FixStats{ EntitiesCount: 10, FixedCount: 3, FailedCount: 1, }, Result: FixResult{ ShardFixKeys: &FixKeys{ Fixed: &store.Keys{ UUID: "test_uuid", }, }, }, } agg.AddReport(firstReport) expected.status[1] = ShardStatusSuccess expected.statusSummary[ShardStatusSuccess] = 1 expected.statusSummary[ShardStatusRunning] = 2 expected.reports[1] = firstReport expected.aggregation.EntitiesCount = 10 expected.aggregation.FixedCount = 3 expected.aggregation.FailedCount = 1 s.Equal(expected, agg) report, err = agg.GetReport(1) s.NoError(err) s.Equal(firstReport, *report) secondReport := FixReport{ ShardID: 2, Stats: FixStats{ EntitiesCount: 10, FixedCount: 3, FailedCount: 1, }, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{}, }, } agg.AddReport(secondReport) expected.status[2] = ShardStatusControlFlowFailure expected.statusSummary[ShardStatusControlFlowFailure] = 1 expected.statusSummary[ShardStatusRunning] = 1 expected.reports[2] = secondReport s.Equal(expected, agg) shardStatus, err := agg.GetStatusResult(PaginatedShardQueryRequest{ StartingShardID: c.IntPtr(1), LimitShards: c.IntPtr(2), }) s.NoError(err) s.Equal(&ShardStatusQueryResult{ Result: map[int]ShardStatus{ 1: ShardStatusSuccess, 2: ShardStatusControlFlowFailure, }, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: c.IntPtr(3), IsDone: false, }, }, shardStatus) } func (s *aggregatorsSuite) TestGetStatusResult() { testCases := []struct { minShardID int maxShardID int req PaginatedShardQueryRequest status ShardStatusResult expectedResult *ShardStatusQueryResult expectedError bool }{ { minShardID: 0, maxShardID: 5, req: PaginatedShardQueryRequest{ StartingShardID: c.IntPtr(6), }, expectedResult: nil, expectedError: true, }, { minShardID: 0, maxShardID: 5, req: PaginatedShardQueryRequest{ StartingShardID: c.IntPtr(0), LimitShards: c.IntPtr(10), }, status: map[int]ShardStatus{ 1: ShardStatusRunning, 2: ShardStatusRunning, 3: ShardStatusSuccess, 4: ShardStatusSuccess, 5: ShardStatusControlFlowFailure, }, expectedResult: &ShardStatusQueryResult{ Result: map[int]ShardStatus{ 1: ShardStatusRunning, 2: ShardStatusRunning, 3: ShardStatusSuccess, 4: ShardStatusSuccess, 5: ShardStatusControlFlowFailure, }, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: nil, IsDone: true, }, }, expectedError: false, }, { minShardID: 0, maxShardID: 5, req: PaginatedShardQueryRequest{ StartingShardID: c.IntPtr(0), LimitShards: c.IntPtr(2), }, status: map[int]ShardStatus{ 1: ShardStatusRunning, 2: ShardStatusRunning, 3: ShardStatusSuccess, 4: ShardStatusSuccess, 5: ShardStatusControlFlowFailure, }, expectedResult: &ShardStatusQueryResult{ Result: map[int]ShardStatus{ 1: ShardStatusRunning, 2: ShardStatusRunning, }, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: c.IntPtr(3), IsDone: false, }, }, expectedError: false, }, { minShardID: 0, maxShardID: 5, req: PaginatedShardQueryRequest{ StartingShardID: c.IntPtr(0), LimitShards: c.IntPtr(3), }, status: map[int]ShardStatus{ 1: ShardStatusRunning, 2: ShardStatusRunning, 4: ShardStatusSuccess, 5: ShardStatusControlFlowFailure, }, expectedResult: &ShardStatusQueryResult{ Result: map[int]ShardStatus{ 1: ShardStatusRunning, 2: ShardStatusRunning, 4: ShardStatusSuccess, }, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: c.IntPtr(5), IsDone: false, }, }, expectedError: false, }, { minShardID: 0, maxShardID: 5, req: PaginatedShardQueryRequest{ StartingShardID: c.IntPtr(2), LimitShards: c.IntPtr(3), }, status: map[int]ShardStatus{ 1: ShardStatusRunning, 2: ShardStatusRunning, 4: ShardStatusSuccess, 5: ShardStatusControlFlowFailure, }, expectedResult: &ShardStatusQueryResult{ Result: map[int]ShardStatus{ 2: ShardStatusRunning, 4: ShardStatusSuccess, 5: ShardStatusControlFlowFailure, }, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: nil, IsDone: true, }, }, expectedError: false, }, } for _, tc := range testCases { result, err := getStatusResult(tc.minShardID, tc.maxShardID, tc.req, tc.status) s.Equal(tc.expectedResult, result) if tc.expectedError { s.Error(err) } else { s.NoError(err) } } } func (s *aggregatorsSuite) TestGetShardSizeQueryResult() { testCases := []struct { shardSizes []ShardSizeTuple req ShardSizeQueryRequest expectedErrorStr *string expectedResult ShardSizeQueryResult }{ { shardSizes: nil, req: ShardSizeQueryRequest{ StartIndex: -1, }, expectedErrorStr: c.StringPtr("index out of bounds exception (required startIndex >= 0 && startIndex < endIndex && endIndex <= 0)"), expectedResult: nil, }, { shardSizes: nil, req: ShardSizeQueryRequest{ StartIndex: 1, EndIndex: 1, }, expectedErrorStr: c.StringPtr("index out of bounds exception (required startIndex >= 0 && startIndex < endIndex && endIndex <= 0)"), expectedResult: nil, }, { shardSizes: nil, req: ShardSizeQueryRequest{ StartIndex: 0, EndIndex: 1, }, expectedErrorStr: c.StringPtr("index out of bounds exception (required startIndex >= 0 && startIndex < endIndex && endIndex <= 0)"), expectedResult: nil, }, { shardSizes: make([]ShardSizeTuple, 10), req: ShardSizeQueryRequest{ StartIndex: 0, EndIndex: 11, }, expectedErrorStr: c.StringPtr("index out of bounds exception (required startIndex >= 0 && startIndex < endIndex && endIndex <= 10)"), expectedResult: nil, }, { shardSizes: make([]ShardSizeTuple, 10000), req: ShardSizeQueryRequest{ StartIndex: 0, EndIndex: maxShardQueryResult + 1, }, expectedErrorStr: c.StringPtr("too many shards requested, the limit is 1000"), expectedResult: nil, }, { shardSizes: []ShardSizeTuple{ { ShardID: 1, }, { ShardID: 2, }, { ShardID: 3, }, { ShardID: 4, }, { ShardID: 5, }, }, req: ShardSizeQueryRequest{ StartIndex: 0, EndIndex: 1, }, expectedErrorStr: nil, expectedResult: []ShardSizeTuple{ { ShardID: 1, }, }, }, { shardSizes: []ShardSizeTuple{ { ShardID: 1, }, { ShardID: 2, }, { ShardID: 3, }, { ShardID: 4, }, { ShardID: 5, }, }, req: ShardSizeQueryRequest{ StartIndex: 0, EndIndex: 5, }, expectedErrorStr: nil, expectedResult: []ShardSizeTuple{ { ShardID: 1, }, { ShardID: 2, }, { ShardID: 3, }, { ShardID: 4, }, { ShardID: 5, }, }, }, } for _, tc := range testCases { agg := &ShardScanResultAggregator{ shardSizes: tc.shardSizes, } result, err := agg.GetShardSizeQueryResult(tc.req) if tc.expectedErrorStr != nil { s.Equal(*tc.expectedErrorStr, err.Error()) } else { s.Equal(tc.expectedResult, result) } } } func (s *aggregatorsSuite) TestInsertReportIntoSizes() { randomReport := func() ScanReport { return ScanReport{ ShardID: 0, Stats: ScanStats{ EntitiesCount: int64(rand.Intn(10)), }, } } agg := &ShardScanResultAggregator{} for i := 0; i < 1000; i++ { agg.insertReportIntoSizes(randomReport()) } for i := 0; i < 999; i++ { s.GreaterOrEqual(agg.shardSizes[i].EntitiesCount, agg.shardSizes[i+1].EntitiesCount) } } ================================================ FILE: service/worker/scanner/shardscanner/fixer.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "fmt" "github.com/pborman/uuid" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" ) // Fixer is used to fix entities in a shard. It is responsible for three things: // 1. Confirming that each entity it scans is corrupted. // 2. Attempting to fix any confirmed corrupted executions. // 3. Recording skipped entities, failed to fix entities and successfully fix entities to durable store. // 4. Producing a FixReport type Fixer interface { Fix() FixReport } type ( // ShardFixer is a generic fixer which iterates over entities provided by iterator // implementations of this fixer have to provided invariant manager and iterator. ShardFixer struct { ctx context.Context shardID int itr store.ScanOutputIterator skippedWriter store.ExecutionWriter failedWriter store.ExecutionWriter fixedWriter store.ExecutionWriter invariantManager invariant.Manager progressReportFn func() domainCache cache.DomainCache allowDomain dynamicproperties.BoolPropertyFnWithDomainFilter scope metrics.Scope } ) // NewFixer constructs a new shard fixer. func NewFixer( ctx context.Context, shardID int, manager invariant.Manager, iterator store.ScanOutputIterator, blobstoreClient blobstore.Client, blobstoreFlushThreshold int, progressReportFn func(), domainCache cache.DomainCache, allowDomain dynamicproperties.BoolPropertyFnWithDomainFilter, scope metrics.Scope, ) *ShardFixer { id := uuid.New() return &ShardFixer{ ctx: ctx, shardID: shardID, itr: iterator, skippedWriter: store.NewBlobstoreWriter(id, store.SkippedExtension, blobstoreClient, blobstoreFlushThreshold), failedWriter: store.NewBlobstoreWriter(id, store.FailedExtension, blobstoreClient, blobstoreFlushThreshold), fixedWriter: store.NewBlobstoreWriter(id, store.FixedExtension, blobstoreClient, blobstoreFlushThreshold), invariantManager: manager, progressReportFn: progressReportFn, domainCache: domainCache, allowDomain: allowDomain, scope: scope, } } // Fix scans over all executions in shard and runs invariant fixes per execution. func (f *ShardFixer) Fix() FixReport { result := FixReport{ ShardID: f.shardID, DomainStats: map[string]*FixStats{}, } for f.itr.HasNext() { f.progressReportFn() soe, err := f.itr.Next() if err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "blobstore iterator returned error", InfoDetails: err.Error(), } return result } domainID := soe.Execution.(entity.Entity).GetDomainID() domainName, err := f.domainCache.GetDomainName(domainID) if err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "failed to get domain name", InfoDetails: err.Error(), } return result } if _, ok := result.DomainStats[domainID]; !ok { result.DomainStats[domainID] = &FixStats{} } var fixResult invariant.ManagerFixResult if f.allowDomain(domainName) { fixResult = f.invariantManager.RunFixes(f.ctx, soe.Execution) } else { fixResult = invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeSkipped, } } result.Stats.EntitiesCount++ result.DomainStats[domainID].EntitiesCount++ foe := store.FixOutputEntity{ Execution: soe.Execution, Input: *soe, Result: fixResult, } invariantName := "" if fixResult.DeterminingInvariantName != nil { invariantName = string(*fixResult.DeterminingInvariantName) } f.scope.Tagged( metrics.DomainTag(domainName), metrics.InvariantTypeTag(invariantName), metrics.ShardScannerFixResult(string(fixResult.FixResultType)), ).IncCounter(metrics.ShardScannerFix) switch fixResult.FixResultType { case invariant.FixResultTypeFixed: if err := f.fixedWriter.Add(foe); err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "blobstore add failed for fixed execution fix", InfoDetails: err.Error(), } return result } result.Stats.FixedCount++ result.DomainStats[domainID].FixedCount++ case invariant.FixResultTypeSkipped: if err := f.skippedWriter.Add(foe); err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "blobstore add failed for skipped execution fix", InfoDetails: err.Error(), } return result } result.Stats.SkippedCount++ result.DomainStats[domainID].SkippedCount++ case invariant.FixResultTypeFailed: if err := f.failedWriter.Add(foe); err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "blobstore add failed for failed execution fix", InfoDetails: err.Error(), } return result } result.Stats.FailedCount++ result.DomainStats[domainID].FailedCount++ default: panic(fmt.Sprintf("unknown FixResultType: %v", fixResult.FixResultType)) } } if err := f.fixedWriter.Flush(); err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "failed to flush for fixed execution fixes", InfoDetails: err.Error(), } return result } if err := f.skippedWriter.Flush(); err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "failed to flush for skipped execution fixes", InfoDetails: err.Error(), } return result } if err := f.failedWriter.Flush(); err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "failed to flush for failed execution fixes", InfoDetails: err.Error(), } return result } result.Result.ShardFixKeys = &FixKeys{ Fixed: f.fixedWriter.FlushedKeys(), Failed: f.failedWriter.FlushedKeys(), Skipped: f.skippedWriter.FlushedKeys(), } return result } ================================================ FILE: service/worker/scanner/shardscanner/fixer_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "errors" "fmt" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/common/resource" ) type FixerSuite struct { *require.Assertions suite.Suite mockResource *resource.Test controller *gomock.Controller } func TestFixerSuite(t *testing.T) { suite.Run(t, new(FixerSuite)) } func (s *FixerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) s.mockResource = resource.NewTest(s.T(), s.controller, metrics.Worker) } func (s *FixerSuite) TearDownTest() { s.controller.Finish() } func (s *FixerSuite) TestFix_Failure_FirstIteratorError() { mockItr := store.NewMockScanOutputIterator(s.controller) mockItr.EXPECT().HasNext().Return(true).Times(1) mockItr.EXPECT().Next().Return(nil, errors.New("iterator error")).Times(1) fixer := &ShardFixer{ shardID: 0, itr: mockItr, progressReportFn: func() {}, } result := fixer.Fix() s.Equal(FixReport{ ShardID: 0, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "blobstore iterator returned error", InfoDetails: "iterator error", }, }, DomainStats: map[string]*FixStats{}, }, result) } func (s *FixerSuite) TestFix_Failure_NonFirstError() { mockItr := store.NewMockScanOutputIterator(s.controller) iteratorCallNumber := 0 mockItr.EXPECT().HasNext().DoAndReturn(func() bool { return iteratorCallNumber < 5 }).Times(5) mockItr.EXPECT().Next().DoAndReturn(func() (*store.ScanOutputEntity, error) { defer func() { iteratorCallNumber++ }() if iteratorCallNumber < 4 { return &store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "test_domain", }, }, }, nil } return nil, fmt.Errorf("iterator got error on: %v", iteratorCallNumber) }).Times(5) mockInvariantManager := invariant.NewMockManager(s.controller) mockInvariantManager.EXPECT().RunFixes(gomock.Any(), gomock.Any()).Return(invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFixed, }).Times(4) fixedWriter := store.NewMockExecutionWriter(s.controller) fixedWriter.EXPECT().Add(gomock.Any()).Return(nil).Times(4) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).Times(4) fixer := &ShardFixer{ shardID: 0, itr: mockItr, invariantManager: mockInvariantManager, fixedWriter: fixedWriter, progressReportFn: func() {}, domainCache: domainCache, allowDomain: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), scope: metrics.NoopScope, } result := fixer.Fix() s.Equal(FixReport{ ShardID: 0, Stats: FixStats{ EntitiesCount: 4, FixedCount: 4, }, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "blobstore iterator returned error", InfoDetails: "iterator got error on: 4", }, }, DomainStats: map[string]*FixStats{ "test_domain": { EntitiesCount: 4, FixedCount: 4, SkippedCount: 0, FailedCount: 0, }, }, }, result) } func (s *FixerSuite) TestFix_Failure_SkippedWriterError() { mockItr := store.NewMockScanOutputIterator(s.controller) mockItr.EXPECT().HasNext().Return(true).Times(1) mockItr.EXPECT().Next().Return(&store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "test_domain", }, }, }, nil).Times(1) mockInvariantManager := invariant.NewMockManager(s.controller) mockInvariantManager.EXPECT().RunFixes(gomock.Any(), gomock.Any()).Return(invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeSkipped, }).Times(1) skippedWriter := store.NewMockExecutionWriter(s.controller) skippedWriter.EXPECT().Add(gomock.Any()).Return(errors.New("skipped writer error")).Times(1) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).Times(1) fixer := &ShardFixer{ shardID: 0, itr: mockItr, skippedWriter: skippedWriter, invariantManager: mockInvariantManager, progressReportFn: func() {}, domainCache: domainCache, allowDomain: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), scope: metrics.NoopScope, } result := fixer.Fix() s.Equal(FixReport{ ShardID: 0, Stats: FixStats{ EntitiesCount: 1, }, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "blobstore add failed for skipped execution fix", InfoDetails: "skipped writer error", }, }, DomainStats: map[string]*FixStats{ "test_domain": { EntitiesCount: 1, FixedCount: 0, SkippedCount: 0, FailedCount: 0, }, }, }, result) } func (s *FixerSuite) TestFix_Failure_FailedWriterError() { mockItr := store.NewMockScanOutputIterator(s.controller) mockItr.EXPECT().HasNext().Return(true).Times(1) mockItr.EXPECT().Next().Return(&store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "test_domain", }, }, }, nil).Times(1) mockInvariantManager := invariant.NewMockManager(s.controller) mockInvariantManager.EXPECT().RunFixes(gomock.Any(), gomock.Any()).Return(invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFailed, }).Times(1) failedWriter := store.NewMockExecutionWriter(s.controller) failedWriter.EXPECT().Add(gomock.Any()).Return(errors.New("failed writer error")).Times(1) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).Times(1) fixer := &ShardFixer{ shardID: 0, itr: mockItr, failedWriter: failedWriter, invariantManager: mockInvariantManager, progressReportFn: func() {}, domainCache: domainCache, allowDomain: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), scope: metrics.NoopScope, } result := fixer.Fix() s.Equal(FixReport{ ShardID: 0, Stats: FixStats{ EntitiesCount: 1, }, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "blobstore add failed for failed execution fix", InfoDetails: "failed writer error", }, }, DomainStats: map[string]*FixStats{ "test_domain": { EntitiesCount: 1, FixedCount: 0, SkippedCount: 0, FailedCount: 0, }, }, }, result) } func (s *FixerSuite) TestFix_Failure_FixedWriterError() { mockItr := store.NewMockScanOutputIterator(s.controller) mockItr.EXPECT().HasNext().Return(true).Times(1) mockItr.EXPECT().Next().Return(&store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "test_domain", }, }, }, nil).Times(1) mockInvariantManager := invariant.NewMockManager(s.controller) mockInvariantManager.EXPECT().RunFixes(gomock.Any(), gomock.Any()).Return(invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFixed, }).Times(1) fixedWriter := store.NewMockExecutionWriter(s.controller) fixedWriter.EXPECT().Add(gomock.Any()).Return(errors.New("fixed writer error")).Times(1) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).Times(1) fixer := &ShardFixer{ shardID: 0, itr: mockItr, fixedWriter: fixedWriter, invariantManager: mockInvariantManager, progressReportFn: func() {}, domainCache: domainCache, allowDomain: dynamicproperties.GetBoolPropertyFnFilteredByDomain(true), scope: metrics.NoopScope, } result := fixer.Fix() s.Equal(FixReport{ ShardID: 0, Stats: FixStats{ EntitiesCount: 1, }, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "blobstore add failed for fixed execution fix", InfoDetails: "fixed writer error", }, }, DomainStats: map[string]*FixStats{ "test_domain": { EntitiesCount: 1, FixedCount: 0, SkippedCount: 0, FailedCount: 0, }, }, }, result) } func (s *FixerSuite) TestFix_Failure_FixedWriterFlushError() { mockItr := store.NewMockScanOutputIterator(s.controller) mockItr.EXPECT().HasNext().Return(false).Times(1) fixedWriter := store.NewMockExecutionWriter(s.controller) fixedWriter.EXPECT().Flush().Return(errors.New("fix writer flush failed")).Times(1) fixer := &ShardFixer{ shardID: 0, itr: mockItr, fixedWriter: fixedWriter, progressReportFn: func() {}, } result := fixer.Fix() s.Equal(FixReport{ ShardID: 0, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "failed to flush for fixed execution fixes", InfoDetails: "fix writer flush failed", }, }, DomainStats: map[string]*FixStats{}, }, result) } func (s *FixerSuite) TestFix_Failure_SkippedWriterFlushError() { mockItr := store.NewMockScanOutputIterator(s.controller) mockItr.EXPECT().HasNext().Return(false).Times(1) fixedWriter := store.NewMockExecutionWriter(s.controller) fixedWriter.EXPECT().Flush().Return(nil) skippedWriter := store.NewMockExecutionWriter(s.controller) skippedWriter.EXPECT().Flush().Return(errors.New("skip writer flush failed")).Times(1) fixer := &ShardFixer{ shardID: 0, itr: mockItr, fixedWriter: fixedWriter, skippedWriter: skippedWriter, progressReportFn: func() {}, } result := fixer.Fix() s.Equal(FixReport{ ShardID: 0, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "failed to flush for skipped execution fixes", InfoDetails: "skip writer flush failed", }, }, DomainStats: map[string]*FixStats{}, }, result) } func (s *FixerSuite) TestFix_Failure_FailedWriterFlushError() { mockItr := store.NewMockScanOutputIterator(s.controller) mockItr.EXPECT().HasNext().Return(false).Times(1) fixedWriter := store.NewMockExecutionWriter(s.controller) fixedWriter.EXPECT().Flush().Return(nil) skippedWriter := store.NewMockExecutionWriter(s.controller) skippedWriter.EXPECT().Flush().Return(nil).Times(1) failedWriter := store.NewMockExecutionWriter(s.controller) failedWriter.EXPECT().Flush().Return(errors.New("fail writer flush failed")).Times(1) fixer := &ShardFixer{ shardID: 0, itr: mockItr, fixedWriter: fixedWriter, skippedWriter: skippedWriter, failedWriter: failedWriter, progressReportFn: func() {}, } result := fixer.Fix() s.Equal(FixReport{ ShardID: 0, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "failed to flush for failed execution fixes", InfoDetails: "fail writer flush failed", }, }, DomainStats: map[string]*FixStats{}, }, result) } func (s *FixerSuite) TestFix_Success() { mockItr := store.NewMockScanOutputIterator(s.controller) iteratorCallNumber := 0 mockItr.EXPECT().HasNext().DoAndReturn(func() bool { return iteratorCallNumber < 12 }).Times(13) mockItr.EXPECT().Next().DoAndReturn(func() (*store.ScanOutputEntity, error) { defer func() { iteratorCallNumber++ }() switch iteratorCallNumber { case 0, 1, 2, 3: return &store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "skipped", }, }, }, nil case 4, 5: return &store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "history_missing", }, }, }, nil case 6: return &store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "first_history_event", }, }, }, nil case 7: return &store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "orphan_execution", }, }, }, nil case 8, 9: return &store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "failed", }, }, }, nil case 10, 11: return &store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "disallow_domain", }, }, }, nil default: panic("should not get here") } }).Times(12) mockInvariantManager := invariant.NewMockManager(s.controller) mockInvariantManager.EXPECT().RunFixes(gomock.Any(), &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "skipped", }, }).Return(invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeSkipped, FixResults: []invariant.FixResult{ { FixResultType: invariant.FixResultTypeSkipped, InvariantName: invariant.HistoryExists, }, { FixResultType: invariant.FixResultTypeSkipped, }, { FixResultType: invariant.FixResultTypeSkipped, InvariantName: invariant.OpenCurrentExecution, }, }, }).Times(4) mockInvariantManager.EXPECT().RunFixes(gomock.Any(), &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "history_missing", }, }).Return(invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFixed, FixResults: []invariant.FixResult{ { FixResultType: invariant.FixResultTypeFixed, InvariantName: invariant.HistoryExists, Info: "history did not exist", }, }, }).Times(2) mockInvariantManager.EXPECT().RunFixes(gomock.Any(), &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "first_history_event", }, }).Return(invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFixed, FixResults: []invariant.FixResult{ { FixResultType: invariant.FixResultTypeSkipped, InvariantName: invariant.HistoryExists, }, { FixResultType: invariant.FixResultTypeFixed, Info: "first event is not valid", }, }, }).Times(1) mockInvariantManager.EXPECT().RunFixes(gomock.Any(), &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "orphan_execution", State: persistence.WorkflowStateCreated, }, }).Return(invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFixed, FixResults: []invariant.FixResult{ { FixResultType: invariant.FixResultTypeSkipped, InvariantName: invariant.HistoryExists, }, { FixResultType: invariant.FixResultTypeSkipped, }, { FixResultType: invariant.FixResultTypeFixed, InvariantName: invariant.OpenCurrentExecution, Info: "execution was orphan", }, }, }).Times(1) mockInvariantManager.EXPECT().RunFixes(gomock.Any(), &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "failed", }, }).Return(invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFailed, FixResults: []invariant.FixResult{ { FixResultType: invariant.FixResultTypeFailed, InvariantName: invariant.HistoryExists, Info: "failed to check if history exists", }, }, }).Times(2) mockFixedWriter := store.NewMockExecutionWriter(s.controller) mockFixedWriter.EXPECT().Add(store.FixOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "history_missing", }, }, Input: store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "history_missing", }, }, }, Result: invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFixed, FixResults: []invariant.FixResult{ { FixResultType: invariant.FixResultTypeFixed, InvariantName: invariant.HistoryExists, Info: "history did not exist", }, }, }, }).Times(2) mockFixedWriter.EXPECT().Add(store.FixOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "first_history_event", }, }, Input: store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "first_history_event", }, }, }, Result: invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFixed, FixResults: []invariant.FixResult{ { FixResultType: invariant.FixResultTypeSkipped, InvariantName: invariant.HistoryExists, }, { FixResultType: invariant.FixResultTypeFixed, Info: "first event is not valid", }, }, }, }).Times(1) mockFixedWriter.EXPECT().Add(store.FixOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "orphan_execution", }, }, Input: store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "orphan_execution", }, }, }, Result: invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFixed, FixResults: []invariant.FixResult{ { FixResultType: invariant.FixResultTypeSkipped, InvariantName: invariant.HistoryExists, }, { FixResultType: invariant.FixResultTypeSkipped, }, { FixResultType: invariant.FixResultTypeFixed, InvariantName: invariant.OpenCurrentExecution, Info: "execution was orphan", }, }, }, }).Times(1) mockFailedWriter := store.NewMockExecutionWriter(s.controller) mockFailedWriter.EXPECT().Add(store.FixOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "failed", }, }, Input: store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "failed", }, }, }, Result: invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeFailed, FixResults: []invariant.FixResult{ { FixResultType: invariant.FixResultTypeFailed, InvariantName: invariant.HistoryExists, Info: "failed to check if history exists", }, }, }, }).Times(2) mockSkippedWriter := store.NewMockExecutionWriter(s.controller) mockSkippedWriter.EXPECT().Add(store.FixOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "skipped", }, }, Input: store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "skipped", }, }, }, Result: invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeSkipped, FixResults: []invariant.FixResult{ { FixResultType: invariant.FixResultTypeSkipped, InvariantName: invariant.HistoryExists, }, { FixResultType: invariant.FixResultTypeSkipped, }, { FixResultType: invariant.FixResultTypeSkipped, InvariantName: invariant.OpenCurrentExecution, }, }, }, }).Times(4) mockSkippedWriter.EXPECT().Add(store.FixOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "disallow_domain", }, }, Input: store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "disallow_domain", }, }, }, Result: invariant.ManagerFixResult{ FixResultType: invariant.FixResultTypeSkipped, }, }).Times(2) mockSkippedWriter.EXPECT().Flush().Return(nil) mockFailedWriter.EXPECT().Flush().Return(nil) mockFixedWriter.EXPECT().Flush().Return(nil) mockSkippedWriter.EXPECT().FlushedKeys().Return(&store.Keys{UUID: "skipped_keys_uuid"}) mockFailedWriter.EXPECT().FlushedKeys().Return(&store.Keys{UUID: "failed_keys_uuid"}) mockFixedWriter.EXPECT().FlushedKeys().Return(&store.Keys{UUID: "fixed_keys_uuid"}) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName("skipped").Return("skipped", nil).Times(4) domainCache.EXPECT().GetDomainName("history_missing").Return("history_missing", nil).Times(2) domainCache.EXPECT().GetDomainName("first_history_event").Return("first_history_event", nil).Times(1) domainCache.EXPECT().GetDomainName("orphan_execution").Return("orphan_execution", nil).Times(1) domainCache.EXPECT().GetDomainName("failed").Return("failed", nil).Times(2) domainCache.EXPECT().GetDomainName("disallow_domain").Return("disallow_domain", nil).Times(2) allowDomain := func(domain string) bool { return domain != "disallow_domain" } fixer := &ShardFixer{ shardID: 0, invariantManager: mockInvariantManager, skippedWriter: mockSkippedWriter, failedWriter: mockFailedWriter, fixedWriter: mockFixedWriter, itr: mockItr, progressReportFn: func() {}, domainCache: domainCache, allowDomain: allowDomain, scope: metrics.NoopScope, } result := fixer.Fix() s.Equal(FixReport{ ShardID: 0, Stats: FixStats{ EntitiesCount: 12, FixedCount: 4, SkippedCount: 6, FailedCount: 2, }, Result: FixResult{ ShardFixKeys: &FixKeys{ Fixed: &store.Keys{UUID: "fixed_keys_uuid"}, Failed: &store.Keys{UUID: "failed_keys_uuid"}, Skipped: &store.Keys{UUID: "skipped_keys_uuid"}, }, }, DomainStats: map[string]*FixStats{ "disallow_domain": { EntitiesCount: 2, FixedCount: 0, SkippedCount: 2, FailedCount: 0, }, "failed": { EntitiesCount: 2, FixedCount: 0, SkippedCount: 0, FailedCount: 2, }, "first_history_event": { EntitiesCount: 1, FixedCount: 1, SkippedCount: 0, FailedCount: 0, }, "history_missing": { EntitiesCount: 2, FixedCount: 2, SkippedCount: 0, FailedCount: 0, }, "orphan_execution": { EntitiesCount: 1, FixedCount: 1, SkippedCount: 0, FailedCount: 0, }, "skipped": { EntitiesCount: 4, FixedCount: 0, SkippedCount: 4, FailedCount: 0, }, }, }, result) } ================================================ FILE: service/worker/scanner/shardscanner/fixer_workflow.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "errors" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" ) const ( fixShardReportChan = "fixShardReportChan" ) var ( errQueryNotReady = errors.New("query is not yet ready to be handled, please try again shortly") ) type ( // FixerManagerCB is a function which returns invariant manager for fixer. FixerManagerCB func( context.Context, persistence.Retryer, FixShardActivityParams, cache.DomainCache, ) invariant.Manager // FixerIteratorCB is a function which returns ScanOutputIterator for fixer. FixerIteratorCB func( context.Context, blobstore.Client, store.Keys, FixShardActivityParams, ) store.ScanOutputIterator // FixerHooks holds callback functions for shard scanner workflow implementation. FixerHooks struct { InvariantManager FixerManagerCB Iterator FixerIteratorCB GetFixerConfig func(fixer FixerContext) CustomScannerConfig } // FixerWorkflow is the workflow that fixes all entities from a scan output. FixerWorkflow struct { Aggregator *ShardFixResultAggregator Params FixerWorkflowParams Keys *FixerCorruptedKeysActivityResult } ) // NewFixerHooks returns initialized callbacks for shard scanner workflow implementation. func NewFixerHooks( manager FixerManagerCB, iterator FixerIteratorCB, config func(fixer FixerContext) CustomScannerConfig, ) (*FixerHooks, error) { if manager == nil || iterator == nil || config == nil { return nil, errors.New("all fixer hooks args are required") } return &FixerHooks{ InvariantManager: manager, Iterator: iterator, GetFixerConfig: config, }, nil } // NewFixerWorkflow returns a new instance of fixer workflow func NewFixerWorkflow( ctx workflow.Context, name string, params FixerWorkflowParams, ) (*FixerWorkflow, error) { if len(name) < 1 { return nil, errors.New("workflow name is not provided") } wf := FixerWorkflow{ Params: params, } corruptKeys, err := GetCorruptedKeys(ctx, wf.Params) if err != nil { return nil, err } if corruptKeys.CorruptedKeys == nil { return nil, errors.New("corrupted keys not found") } wf.Keys = corruptKeys wf.Aggregator = NewShardFixResultAggregator(corruptKeys.CorruptedKeys, *corruptKeys.MinShard, *corruptKeys.MaxShard) for name, fn := range setHandlers(wf.Aggregator) { if err := workflow.SetQueryHandler(ctx, name, fn); err != nil { return nil, err } } return &wf, nil } func supportsFixerConfig(ctx workflow.Context) bool { // this can probably be removed after a version or three, it just prevents a one-time // non-determinism failure when resuming a previous version's fixer run. return workflow.GetVersion(ctx, "dynamic fixer config", workflow.DefaultVersion, 1) == 1 } // Start starts a shard fixer workflow. func (fx *FixerWorkflow) Start(ctx workflow.Context) error { resolvedConfig := resolveFixerConfig(fx.Params.FixerWorkflowConfigOverwrites) var enabled CustomScannerConfig if supportsFixerConfig(ctx) { activityCtx := getShortActivityContext(ctx) var out FixShardConfigResults if err := workflow.ExecuteActivity(activityCtx, ActivityFixerConfig, FixShardConfigParams{}).Get(activityCtx, &out); err != nil { return err } enabled = out.EnabledInvariants } shardReportChan := workflow.GetSignalChannel(ctx, fixShardReportChan) for i := 0; i < resolvedConfig.Concurrency; i++ { idx := i workflow.Go(ctx, func(ctx workflow.Context) { batches := getCorruptedKeysBatches(resolvedConfig.ActivityBatchSize, resolvedConfig.Concurrency, fx.Keys.CorruptedKeys, idx) for _, batch := range batches { activityCtx := getLongActivityContext(ctx) var reports []FixReport if err := workflow.ExecuteActivity(activityCtx, ActivityFixShard, FixShardActivityParams{ CorruptedKeysEntries: batch, ResolvedFixerWorkflowConfig: resolvedConfig, EnabledInvariants: enabled, }).Get(ctx, &reports); err != nil { errStr := err.Error() shardReportChan.Send(ctx, FixReportError{ Reports: nil, ErrorStr: &errStr, }) return } shardReportChan.Send(ctx, FixReportError{ Reports: reports, ErrorStr: nil, }) } }) } for i := 0; i < len(fx.Keys.CorruptedKeys); { var reportErr FixReportError shardReportChan.Receive(ctx, &reportErr) if reportErr.ErrorStr != nil { return errors.New(*reportErr.ErrorStr) } for _, report := range reportErr.Reports { fx.Aggregator.AddReport(report) i++ } } return nil } func resolveFixerConfig(overwrites FixerWorkflowConfigOverwrites) ResolvedFixerWorkflowConfig { resolvedConfig := ResolvedFixerWorkflowConfig{ Concurrency: 25, BlobstoreFlushThreshold: 1000, ActivityBatchSize: 200, } if overwrites.Concurrency != nil { resolvedConfig.Concurrency = *overwrites.Concurrency } if overwrites.BlobstoreFlushThreshold != nil { resolvedConfig.BlobstoreFlushThreshold = *overwrites.BlobstoreFlushThreshold } if overwrites.ActivityBatchSize != nil { resolvedConfig.ActivityBatchSize = *overwrites.ActivityBatchSize } return resolvedConfig } func setHandlers(aggregator *ShardFixResultAggregator) map[string]interface{} { return map[string]interface{}{ ShardReportQuery: func(shardID int) (*FixReport, error) { if aggregator == nil { return nil, errQueryNotReady } return aggregator.GetReport(shardID) }, ShardStatusQuery: func(req PaginatedShardQueryRequest) (*ShardStatusQueryResult, error) { if aggregator == nil { return nil, errQueryNotReady } return aggregator.GetStatusResult(req) }, ShardStatusSummaryQuery: func() (ShardStatusSummaryResult, error) { if aggregator == nil { return nil, errQueryNotReady } return aggregator.GetStatusSummary(), nil }, AggregateReportQuery: func() (AggregateFixReportResult, error) { if aggregator == nil { return AggregateFixReportResult{}, errQueryNotReady } return aggregator.GetAggregation(), nil }, DomainReportQuery: func(req DomainReportQueryRequest) (*DomainFixReportQueryResult, error) { if aggregator == nil { return nil, errQueryNotReady } return aggregator.GetDomainStatus(req) }, AllResultsQuery: func() (map[int]FixResult, error) { if aggregator == nil { return nil, errQueryNotReady } return aggregator.GetAllFixResults() }, } } func getCorruptedKeysBatches( batchSize int, concurrency int, corruptedKeys []CorruptedKeysEntry, workerIdx int, ) [][]CorruptedKeysEntry { batchIndices := getBatchIndices(batchSize, concurrency, len(corruptedKeys), workerIdx) var result [][]CorruptedKeysEntry for _, batch := range batchIndices { var curr []CorruptedKeysEntry for _, i := range batch { curr = append(curr, corruptedKeys[i]) } result = append(result, curr) } return result } ================================================ FILE: service/worker/scanner/shardscanner/fixer_workflow_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "testing" "github.com/stretchr/testify/suite" "github.com/uber/cadence/common" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" ) type fixerWorkflowSuite struct { suite.Suite } func TestFixerWorkflowSuite(t *testing.T) { suite.Run(t, new(fixerWorkflowSuite)) } func (s *fixerWorkflowSuite) TestResolveFixerConfig() { result := resolveFixerConfig(FixerWorkflowConfigOverwrites{ Concurrency: common.IntPtr(1000), }) s.Equal(ResolvedFixerWorkflowConfig{ Concurrency: 1000, BlobstoreFlushThreshold: 1000, ActivityBatchSize: 200, }, result) } func (s *fixerWorkflowSuite) TestGetCorruptedKeysBatches() { var keys []CorruptedKeysEntry for i := 5; i < 50; i += 2 { keys = append(keys, CorruptedKeysEntry{ ShardID: i, }) } batches := getCorruptedKeysBatches(5, 3, keys, 1) s.Equal([][]CorruptedKeysEntry{ { {ShardID: 7}, {ShardID: 13}, {ShardID: 19}, {ShardID: 25}, {ShardID: 31}, }, { {ShardID: 37}, {ShardID: 43}, {ShardID: 49}, }, }, batches) } func (s *fixerWorkflowSuite) TestNewFixerHooks() { testCases := []struct { name string manager FixerManagerCB iterator FixerIteratorCB wantErr bool }{ { name: "both arguments are nil", manager: nil, iterator: nil, wantErr: true, }, { name: "invariant is nil", manager: func( ctx context.Context, retryer persistence.Retryer, params FixShardActivityParams, cache cache.DomainCache, ) invariant.Manager { return nil }, iterator: nil, wantErr: true, }, { name: "both provided", manager: func( ctx context.Context, retryer persistence.Retryer, params FixShardActivityParams, cache cache.DomainCache, ) invariant.Manager { return nil }, iterator: func( ctx context.Context, client blobstore.Client, keys store.Keys, params FixShardActivityParams, ) store.ScanOutputIterator { return nil }, wantErr: false, }, } for _, tc := range testCases { s.Run(tc.name, func() { _, err := NewFixerHooks(tc.manager, tc.iterator, func(fixer FixerContext) CustomScannerConfig { return nil // no config overrides }) if tc.wantErr { s.Error(err) } else { s.NoError(err) } }) } } ================================================ FILE: service/worker/scanner/shardscanner/scanner.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "fmt" "github.com/pborman/uuid" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" ) // Scanner is used to scan over given iterator. It is responsible for three things: // 1. Checking invariants for each entity. // 2. Recording corruption and failures to durable store. // 3. Producing a ScanReport type Scanner interface { Scan() ScanReport } type ( // ShardScanner is a generic scanner which iterates over entities provided by iterator // implementations of this scanner have to provided invariant manager and iterator ShardScanner struct { shardID int itr pagination.Iterator failedWriter store.ExecutionWriter corruptedWriter store.ExecutionWriter invariantManager invariant.Manager progressReportFn func() scope metrics.Scope domainCache cache.DomainCache } ) // NewScanner constructs a new ShardScanner func NewScanner( shardID int, iterator pagination.Iterator, blobstoreClient blobstore.Client, blobstoreFlushThreshold int, manager invariant.Manager, progressReportFn func(), scope metrics.Scope, domainCache cache.DomainCache, ) *ShardScanner { id := uuid.New() return &ShardScanner{ shardID: shardID, itr: iterator, failedWriter: store.NewBlobstoreWriter(id, store.FailedExtension, blobstoreClient, blobstoreFlushThreshold), corruptedWriter: store.NewBlobstoreWriter(id, store.CorruptedExtension, blobstoreClient, blobstoreFlushThreshold), invariantManager: manager, progressReportFn: progressReportFn, scope: scope, domainCache: domainCache, } } // Scan scans over all executions in shard and runs invariant checks per execution. func (s *ShardScanner) Scan(ctx context.Context) ScanReport { result := ScanReport{ ShardID: s.shardID, Stats: ScanStats{ CorruptionByType: make(map[invariant.Name]int64), }, DomainStats: map[string]*ScanStats{}, } for s.itr.HasNext() { s.progressReportFn() execution, err := s.itr.Next() if err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "persistence iterator returned error", InfoDetails: err.Error(), } return result } checkResult := s.invariantManager.RunChecks(ctx, execution) domainID, err := s.getDomainIDFromEntity(execution) if err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "failed to get domainID from entity", InfoDetails: err.Error(), } return result } domainName, err := s.domainCache.GetDomainName(*domainID) if err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "failed to get domain name from cache", InfoDetails: err.Error(), } return result } if _, ok := result.DomainStats[*domainID]; !ok { result.DomainStats[*domainID] = &ScanStats{ CorruptionByType: make(map[invariant.Name]int64), } } result.DomainStats[*domainID].EntitiesCount++ result.Stats.EntitiesCount++ invariantName := "" if checkResult.DeterminingInvariantType != nil { invariantName = string(*checkResult.DeterminingInvariantType) } s.scope.Tagged( metrics.DomainTag(domainName), metrics.InvariantTypeTag(invariantName), metrics.ShardScannerScanResult(string(checkResult.CheckResultType)), ).IncCounter(metrics.ShardScannerScan) switch checkResult.CheckResultType { case invariant.CheckResultTypeHealthy: // do nothing if execution is healthy case invariant.CheckResultTypeCorrupted: if err := s.corruptedWriter.Add(store.ScanOutputEntity{ Execution: execution, Result: checkResult, }); err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "blobstore add failed for corrupted execution check", InfoDetails: err.Error(), } return result } result.Stats.CorruptedCount++ result.Stats.CorruptionByType[*checkResult.DeterminingInvariantType]++ result.DomainStats[*domainID].CorruptedCount++ result.DomainStats[*domainID].CorruptionByType[*checkResult.DeterminingInvariantType]++ case invariant.CheckResultTypeFailed: if err := s.failedWriter.Add(store.ScanOutputEntity{ Execution: execution, Result: checkResult, }); err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "blobstore add failed for failed execution check", InfoDetails: err.Error(), } return result } result.Stats.CheckFailedCount++ result.DomainStats[*domainID].CheckFailedCount++ default: panic(fmt.Sprintf("unknown CheckResultType: %v", checkResult.CheckResultType)) } } if err := s.failedWriter.Flush(); err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "failed to flush for failed execution checks", InfoDetails: err.Error(), } return result } if err := s.corruptedWriter.Flush(); err != nil { result.Result.ControlFlowFailure = &ControlFlowFailure{ Info: "failed to flush for corrupted execution checks", InfoDetails: err.Error(), } return result } result.Result.ShardScanKeys = &ScanKeys{ Corrupt: s.corruptedWriter.FlushedKeys(), Failed: s.failedWriter.FlushedKeys(), } return result } func (s *ShardScanner) getDomainIDFromEntity(e interface{}) (*string, error) { concreteExecution, ok := e.(*entity.ConcreteExecution) if ok { return &concreteExecution.DomainID, nil } currentExecution, ok := e.(*entity.CurrentExecution) if ok { return ¤tExecution.DomainID, nil } timer, ok := e.(*entity.Timer) if ok { return &timer.DomainID, nil } return nil, fmt.Errorf("unknown entity type in scanner: %T", e) } ================================================ FILE: service/worker/scanner/shardscanner/scanner_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "errors" "fmt" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" ) type ScannerSuite struct { *require.Assertions suite.Suite controller *gomock.Controller } func TestScannerSuite(t *testing.T) { suite.Run(t, new(ScannerSuite)) } func (s *ScannerSuite) SetupTest() { s.Assertions = require.New(s.T()) s.controller = gomock.NewController(s.T()) } func (s *ScannerSuite) TearDownTest() { s.controller.Finish() } func (s *ScannerSuite) TestScan_Failure_FirstIteratorError() { mockItr := pagination.NewMockIterator(s.controller) mockItr.EXPECT().HasNext().Return(true).Times(1) mockItr.EXPECT().Next().Return(nil, errors.New("iterator error")).Times(1) scanner := &ShardScanner{ shardID: 0, itr: mockItr, progressReportFn: func() {}, } result := scanner.Scan(context.Background()) s.Equal(ScanReport{ ShardID: 0, Stats: ScanStats{ CorruptionByType: make(map[invariant.Name]int64), }, Result: ScanResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "persistence iterator returned error", InfoDetails: "iterator error", }, }, DomainStats: make(map[string]*ScanStats), }, result) } func (s *ScannerSuite) TestScan_Failure_NonFirstError() { mockItr := pagination.NewMockIterator(s.controller) iteratorCallNumber := 0 mockItr.EXPECT().HasNext().DoAndReturn(func() bool { return iteratorCallNumber < 5 }).Times(5) mockItr.EXPECT().Next().DoAndReturn(func() (*entity.ConcreteExecution, error) { defer func() { iteratorCallNumber++ }() if iteratorCallNumber < 4 { return &entity.ConcreteExecution{Execution: entity.Execution{DomainID: "ABC"}}, nil } return nil, fmt.Errorf("iterator got error on: %v", iteratorCallNumber) }).Times(5) mockInvariantManager := invariant.NewMockManager(s.controller) mockInvariantManager.EXPECT().RunChecks(gomock.Any(), gomock.Any()).Return(invariant.ManagerCheckResult{ CheckResultType: invariant.CheckResultTypeHealthy, }).Times(4) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).Times(4) scanner := &ShardScanner{ shardID: 0, itr: mockItr, invariantManager: mockInvariantManager, progressReportFn: func() {}, domainCache: domainCache, scope: metrics.NoopScope, } result := scanner.Scan(context.Background()) s.Equal(ScanReport{ ShardID: 0, Stats: ScanStats{ EntitiesCount: 4, CorruptionByType: make(map[invariant.Name]int64), }, Result: ScanResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "persistence iterator returned error", InfoDetails: "iterator got error on: 4", }, }, DomainStats: map[string]*ScanStats{ "ABC": &ScanStats{ EntitiesCount: 4, CorruptionByType: make(map[invariant.Name]int64), }, }, }, result) } func (s *ScannerSuite) TestScan_Failure_CorruptedWriterError() { mockItr := pagination.NewMockIterator(s.controller) mockItr.EXPECT().HasNext().Return(true).Times(1) mockItr.EXPECT().Next().Return(&entity.ConcreteExecution{Execution: entity.Execution{DomainID: "ABC"}}, nil).Times(1) mockInvariantManager := invariant.NewMockManager(s.controller) mockInvariantManager.EXPECT().RunChecks(gomock.Any(), gomock.Any()).Return(invariant.ManagerCheckResult{ CheckResultType: invariant.CheckResultTypeCorrupted, }).Times(1) corruptedWriter := store.NewMockExecutionWriter(s.controller) corruptedWriter.EXPECT().Add(gomock.Any()).Return(errors.New("corrupted writer add failed")).Times(1) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).AnyTimes() scanner := &ShardScanner{ shardID: 0, itr: mockItr, invariantManager: mockInvariantManager, corruptedWriter: corruptedWriter, progressReportFn: func() {}, domainCache: domainCache, scope: metrics.NoopScope, } result := scanner.Scan(context.Background()) s.Equal(ScanReport{ ShardID: 0, Stats: ScanStats{ EntitiesCount: 1, CorruptionByType: make(map[invariant.Name]int64), }, Result: ScanResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "blobstore add failed for corrupted execution check", InfoDetails: "corrupted writer add failed", }, }, DomainStats: map[string]*ScanStats{ "ABC": &ScanStats{ EntitiesCount: 1, CorruptionByType: make(map[invariant.Name]int64), }, }, }, result) } func (s *ScannerSuite) TestScan_Failure_FailedWriterError() { mockItr := pagination.NewMockIterator(s.controller) mockItr.EXPECT().HasNext().Return(true).Times(1) mockItr.EXPECT().Next().Return(&entity.ConcreteExecution{Execution: entity.Execution{DomainID: "ABC"}}, nil).Times(1) mockInvariantManager := invariant.NewMockManager(s.controller) mockInvariantManager.EXPECT().RunChecks(gomock.Any(), gomock.Any()).Return(invariant.ManagerCheckResult{ CheckResultType: invariant.CheckResultTypeFailed, }).Times(1) failedWriter := store.NewMockExecutionWriter(s.controller) failedWriter.EXPECT().Add(gomock.Any()).Return(errors.New("failed writer add failed")).Times(1) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).AnyTimes() scanner := &ShardScanner{ shardID: 0, itr: mockItr, invariantManager: mockInvariantManager, failedWriter: failedWriter, progressReportFn: func() {}, domainCache: domainCache, scope: metrics.NoopScope, } result := scanner.Scan(context.Background()) s.Equal(ScanReport{ ShardID: 0, Stats: ScanStats{ EntitiesCount: 1, CorruptionByType: make(map[invariant.Name]int64), }, Result: ScanResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "blobstore add failed for failed execution check", InfoDetails: "failed writer add failed", }, }, DomainStats: map[string]*ScanStats{ "ABC": &ScanStats{ EntitiesCount: 1, CorruptionByType: make(map[invariant.Name]int64), }, }, }, result) } func (s *ScannerSuite) TestScan_Failure_FailedWriterFlushError() { mockItr := pagination.NewMockIterator(s.controller) mockItr.EXPECT().HasNext().Return(false).Times(1) failedWriter := store.NewMockExecutionWriter(s.controller) failedWriter.EXPECT().Flush().Return(errors.New("failed writer flush failed")).Times(1) scanner := &ShardScanner{ shardID: 0, itr: mockItr, failedWriter: failedWriter, progressReportFn: func() {}, } result := scanner.Scan(context.Background()) s.Equal(ScanReport{ ShardID: 0, Stats: ScanStats{ EntitiesCount: 0, CorruptionByType: make(map[invariant.Name]int64), }, Result: ScanResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "failed to flush for failed execution checks", InfoDetails: "failed writer flush failed", }, }, DomainStats: make(map[string]*ScanStats), }, result) } func (s *ScannerSuite) TestScan_Failure_CorruptedWriterFlushError() { mockItr := pagination.NewMockIterator(s.controller) mockItr.EXPECT().HasNext().Return(false).Times(1) corruptedWriter := store.NewMockExecutionWriter(s.controller) corruptedWriter.EXPECT().Flush().Return(errors.New("corrupted writer flush failed")).Times(1) failedWriter := store.NewMockExecutionWriter(s.controller) failedWriter.EXPECT().Flush().Return(nil).Times(1) scanner := &ShardScanner{ shardID: 0, itr: mockItr, corruptedWriter: corruptedWriter, failedWriter: failedWriter, progressReportFn: func() {}, } result := scanner.Scan(context.Background()) s.Equal(ScanReport{ ShardID: 0, Stats: ScanStats{ EntitiesCount: 0, CorruptionByType: make(map[invariant.Name]int64), }, Result: ScanResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "failed to flush for corrupted execution checks", InfoDetails: "corrupted writer flush failed", }, }, DomainStats: make(map[string]*ScanStats), }, result) } func (s *ScannerSuite) TestScan_Success() { mockItr := pagination.NewMockIterator(s.controller) iteratorCallNumber := 0 mockItr.EXPECT().HasNext().DoAndReturn(func() bool { return iteratorCallNumber < 10 }).Times(11) mockItr.EXPECT().Next().DoAndReturn(func() (*entity.ConcreteExecution, error) { defer func() { iteratorCallNumber++ }() switch iteratorCallNumber { case 0, 1, 2, 3: return &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "healthy", }, }, nil case 4, 5, 6: return &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "history_missing", State: persistence.WorkflowStateCompleted, }, }, nil case 7: return &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "orphan_execution", State: persistence.WorkflowStateCreated, }, }, nil case 8, 9: return &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "failed", }, }, nil default: panic("should not get here") } }).Times(10) mockInvariantManager := invariant.NewMockManager(s.controller) mockInvariantManager.EXPECT().RunChecks(context.Background(), &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "healthy", }, }).Return(invariant.ManagerCheckResult{ CheckResultType: invariant.CheckResultTypeHealthy, }).Times(4) mockInvariantManager.EXPECT().RunChecks(context.Background(), &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "history_missing", State: persistence.WorkflowStateCompleted, }, }).Return(invariant.ManagerCheckResult{ CheckResultType: invariant.CheckResultTypeCorrupted, DeterminingInvariantType: invariant.NamePtr(invariant.HistoryExists), CheckResults: []invariant.CheckResult{ { CheckResultType: invariant.CheckResultTypeCorrupted, InvariantName: invariant.HistoryExists, Info: "history did not exist", }, }, }).Times(3) mockInvariantManager.EXPECT().RunChecks(context.Background(), &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "orphan_execution", State: persistence.WorkflowStateCreated, }}).Return(invariant.ManagerCheckResult{ CheckResultType: invariant.CheckResultTypeCorrupted, DeterminingInvariantType: invariant.NamePtr(invariant.OpenCurrentExecution), CheckResults: []invariant.CheckResult{ { CheckResultType: invariant.CheckResultTypeHealthy, InvariantName: invariant.HistoryExists, }, { CheckResultType: invariant.CheckResultTypeCorrupted, InvariantName: invariant.OpenCurrentExecution, Info: "execution was orphan", }, }, }).Times(1) mockInvariantManager.EXPECT().RunChecks(context.Background(), &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "failed", }}).Return(invariant.ManagerCheckResult{ CheckResultType: invariant.CheckResultTypeFailed, DeterminingInvariantType: invariant.NamePtr(invariant.HistoryExists), CheckResults: []invariant.CheckResult{ { CheckResultType: invariant.CheckResultTypeFailed, InvariantName: invariant.HistoryExists, Info: "failed to check if history exists", }, }, }).Times(2) mockCorruptedWriter := store.NewMockExecutionWriter(s.controller) mockCorruptedWriter.EXPECT().Add(store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "history_missing", State: persistence.WorkflowStateCompleted, }}, Result: invariant.ManagerCheckResult{ CheckResultType: invariant.CheckResultTypeCorrupted, DeterminingInvariantType: invariant.NamePtr(invariant.HistoryExists), CheckResults: []invariant.CheckResult{ { CheckResultType: invariant.CheckResultTypeCorrupted, InvariantName: invariant.HistoryExists, Info: "history did not exist", }, }, }, }).Times(3) mockCorruptedWriter.EXPECT().Add(store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "orphan_execution", State: persistence.WorkflowStateCreated, }}, Result: invariant.ManagerCheckResult{ CheckResultType: invariant.CheckResultTypeCorrupted, DeterminingInvariantType: invariant.NamePtr(invariant.OpenCurrentExecution), CheckResults: []invariant.CheckResult{ { CheckResultType: invariant.CheckResultTypeHealthy, InvariantName: invariant.HistoryExists, }, { CheckResultType: invariant.CheckResultTypeCorrupted, InvariantName: invariant.OpenCurrentExecution, Info: "execution was orphan", }, }, }, }).Times(1) mockFailedWriter := store.NewMockExecutionWriter(s.controller) mockFailedWriter.EXPECT().Add(store.ScanOutputEntity{ Execution: &entity.ConcreteExecution{ Execution: entity.Execution{ DomainID: "failed", }}, Result: invariant.ManagerCheckResult{ CheckResultType: invariant.CheckResultTypeFailed, DeterminingInvariantType: invariant.NamePtr(invariant.HistoryExists), CheckResults: []invariant.CheckResult{ { CheckResultType: invariant.CheckResultTypeFailed, InvariantName: invariant.HistoryExists, Info: "failed to check if history exists", }, }, }, }).Times(2) mockCorruptedWriter.EXPECT().Flush().Return(nil) mockFailedWriter.EXPECT().Flush().Return(nil) mockCorruptedWriter.EXPECT().FlushedKeys().Return(&store.Keys{UUID: "corrupt_keys_uuid"}) mockFailedWriter.EXPECT().FlushedKeys().Return(&store.Keys{UUID: "failed_keys_uuid"}) domainCache := cache.NewMockDomainCache(s.controller) domainCache.EXPECT().GetDomainName(gomock.Any()).Return("test-domain", nil).AnyTimes() scanner := &ShardScanner{ shardID: 0, invariantManager: mockInvariantManager, corruptedWriter: mockCorruptedWriter, failedWriter: mockFailedWriter, itr: mockItr, progressReportFn: func() {}, domainCache: domainCache, scope: metrics.NoopScope, } result := scanner.Scan(context.Background()) s.Equal(ScanReport{ ShardID: 0, Stats: ScanStats{ EntitiesCount: 10, CorruptedCount: 4, CheckFailedCount: 2, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 3, invariant.OpenCurrentExecution: 1, }, }, Result: ScanResult{ ShardScanKeys: &ScanKeys{ Corrupt: &store.Keys{UUID: "corrupt_keys_uuid"}, Failed: &store.Keys{UUID: "failed_keys_uuid"}, }, }, DomainStats: map[string]*ScanStats{ "failed": &ScanStats{ EntitiesCount: 2, CheckFailedCount: 2, CorruptionByType: make(map[invariant.Name]int64), }, "healthy": &ScanStats{ EntitiesCount: 4, CorruptionByType: make(map[invariant.Name]int64), }, "history_missing": &ScanStats{ EntitiesCount: 3, CorruptedCount: 3, CorruptionByType: map[invariant.Name]int64{ invariant.HistoryExists: 3, }, }, "orphan_execution": &ScanStats{ EntitiesCount: 1, CorruptedCount: 1, CorruptionByType: map[invariant.Name]int64{ invariant.OpenCurrentExecution: 1, }, }, }, }, result) } func (s *ScannerSuite) TestGetDomainIDFromEntity() { scanner := &ShardScanner{} concreteExecution := entity.ConcreteExecution{Execution: entity.Execution{DomainID: "A"}} concreteExecutionDomain, err := scanner.getDomainIDFromEntity(&concreteExecution) s.NoError(err) s.Equal("A", *concreteExecutionDomain) currentExecution := entity.CurrentExecution{Execution: entity.Execution{DomainID: "B"}} currentExecutionDomain, err := scanner.getDomainIDFromEntity(¤tExecution) s.NoError(err) s.Equal("B", *currentExecutionDomain) timer := entity.CurrentExecution{Execution: entity.Execution{DomainID: "C"}} timerDomain, err := scanner.getDomainIDFromEntity(&timer) s.NoError(err) s.Equal("C", *timerDomain) baseExecution := entity.Execution{DomainID: "D"} result, err := scanner.getDomainIDFromEntity(&baseExecution) s.Nil(result) s.Error(err) } ================================================ FILE: service/worker/scanner/shardscanner/scanner_workflow.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "errors" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" ) const ( // ShardReportQuery is the query name for the query used to get a single shard's report ShardReportQuery = "shard_report" // ShardStatusQuery is the query name for the query used to get the status of all shards ShardStatusQuery = "shard_status" // ShardStatusSummaryQuery is the query name for the query used to get the shard status -> counts map ShardStatusSummaryQuery = "shard_status_summary" // AggregateReportQuery is the query name for the query used to get the aggregate result of all finished shards AggregateReportQuery = "aggregate_report" // ShardSizeQuery is the query name for the query used to get the number of executions per shard in sorted order ShardSizeQuery = "shard_size" // DomainReportQuery is the query name for the query used to get the reports per domains for all finished shards DomainReportQuery = "domain_report" // AllResultsQuery returns filenames / paginating data for corruptions and failures in this workflow, // for shards which have finished processing. This works for both scanner and fixer, and the return structures // are very similar. // // This data is also available for a single shard under ShardReportQuery, but using that requires // re-querying repeatedly if more than that single shard's data is desired, e.g. for manual // troubleshooting purposes. AllResultsQuery = "all_results" scanShardReportChan = "scanShardReportChan" ) // ManagerCB is a function which returns invariant manager for scanner. type ManagerCB func( context.Context, persistence.Retryer, ScanShardActivityParams, cache.DomainCache, ) invariant.Manager // IteratorCB is a function which returns iterator for scanner. type IteratorCB func( context.Context, persistence.Retryer, ScanShardActivityParams, ) pagination.Iterator // ScannerWorkflow is a workflow which scans and checks entities in a shard. type ScannerWorkflow struct { Aggregator *ShardScanResultAggregator Params ScannerWorkflowParams Name string Shards []int } // ScannerHooks allows provide manager and iterator for different types of scanners. type ScannerHooks struct { Manager ManagerCB Iterator IteratorCB GetScannerConfig func(scanner ScannerContext) CustomScannerConfig } // NewScannerWorkflow creates instance of shard scanner func NewScannerWorkflow( ctx workflow.Context, name string, params ScannerWorkflowParams, ) (*ScannerWorkflow, error) { if name == "" { return nil, errors.New("workflow name is not provided") } if err := params.Shards.Validate(); err != nil { return nil, err } shards, minShard, maxShard := params.Shards.Flatten() wf := ScannerWorkflow{ Name: name, Params: params, Aggregator: NewShardScanResultAggregator(shards, minShard, maxShard), Shards: shards, } for name, fn := range getScanHandlers(wf.Aggregator) { if err := workflow.SetQueryHandler(ctx, name, fn); err != nil { return nil, err } } return &wf, nil } // Start starts a shard scanner workflow. func (wf *ScannerWorkflow) Start(ctx workflow.Context) error { activityCtx := getShortActivityContext(ctx) var resolvedConfig ResolvedScannerWorkflowConfig if err := workflow.ExecuteActivity(activityCtx, ActivityScannerConfig, ScannerConfigActivityParams{ Overwrites: wf.Params.ScannerWorkflowConfigOverwrites, }).Get(ctx, &resolvedConfig); err != nil { return err } if !resolvedConfig.GenericScannerConfig.Enabled { return nil } shardReportChan := workflow.GetSignalChannel(ctx, scanShardReportChan) for i := 0; i < resolvedConfig.GenericScannerConfig.Concurrency; i++ { idx := i workflow.Go(ctx, func(ctx workflow.Context) { batches := getShardBatches(resolvedConfig.GenericScannerConfig.ActivityBatchSize, resolvedConfig.GenericScannerConfig.Concurrency, wf.Shards, idx) for _, batch := range batches { activityCtx = getLongActivityContext(ctx) var reports []ScanReport if err := workflow.ExecuteActivity(activityCtx, ActivityScanShard, ScanShardActivityParams{ Shards: batch, PageSize: resolvedConfig.GenericScannerConfig.PageSize, BlobstoreFlushThreshold: resolvedConfig.GenericScannerConfig.BlobstoreFlushThreshold, ScannerConfig: resolvedConfig.CustomScannerConfig, }).Get(ctx, &reports); err != nil { errStr := err.Error() shardReportChan.Send(ctx, ScanReportError{ Reports: nil, ErrorStr: &errStr, }) return } shardReportChan.Send(ctx, ScanReportError{ Reports: reports, ErrorStr: nil, }) } }) } for i := 0; i < len(wf.Shards); { var reportErr ScanReportError shardReportChan.Receive(ctx, &reportErr) if reportErr.ErrorStr != nil { return errors.New(*reportErr.ErrorStr) } for _, report := range reportErr.Reports { wf.Aggregator.AddReport(report) i++ } } activityCtx = getShortActivityContext(ctx) summary := wf.Aggregator.GetStatusSummary() return workflow.ExecuteActivity(activityCtx, ActivityScannerEmitMetrics, ScannerEmitMetricsActivityParams{ ShardSuccessCount: summary[ShardStatusSuccess], ShardControlFlowFailureCount: summary[ShardStatusControlFlowFailure], AggregateReportResult: wf.Aggregator.GetAggregateReport(), ShardDistributionStats: wf.Aggregator.GetShardDistributionStats(), }).Get(ctx, nil) } func getScanHandlers(aggregator *ShardScanResultAggregator) map[string]interface{} { return map[string]interface{}{ ShardReportQuery: func(shardID int) (*ScanReport, error) { return aggregator.GetReport(shardID) }, ShardStatusQuery: func(req PaginatedShardQueryRequest) (*ShardStatusQueryResult, error) { return aggregator.GetStatusResult(req) }, ShardStatusSummaryQuery: func() (ShardStatusSummaryResult, error) { return aggregator.GetStatusSummary(), nil }, AggregateReportQuery: func() (AggregateScanReportResult, error) { return aggregator.GetAggregateReport(), nil }, ShardCorruptKeysQuery: func(req PaginatedShardQueryRequest) (*ShardCorruptKeysQueryResult, error) { return aggregator.GetCorruptionKeys(req) }, ShardSizeQuery: func(req ShardSizeQueryRequest) (ShardSizeQueryResult, error) { return aggregator.GetShardSizeQueryResult(req) }, DomainReportQuery: func(req DomainReportQueryRequest) (*DomainScanReportQueryResult, error) { return aggregator.GetDomainStatus(req) }, AllResultsQuery: func() (map[int]ScanResult, error) { return aggregator.GetAllScanResults() }, } } func getShardBatches( batchSize int, concurrency int, shards []int, workerIdx int, ) [][]int { batchIndices := getBatchIndices(batchSize, concurrency, len(shards), workerIdx) var result [][]int for _, batch := range batchIndices { var curr []int for _, i := range batch { curr = append(curr, shards[i]) } result = append(result, curr) } return result } // NewScannerHooks is used to have per scanner iterator and invariant manager func NewScannerHooks(manager ManagerCB, iterator IteratorCB, config func(scanner ScannerContext) CustomScannerConfig) (*ScannerHooks, error) { if manager == nil || iterator == nil || config == nil { return nil, errors.New("all scanner hooks args are required") } return &ScannerHooks{ Manager: manager, Iterator: iterator, GetScannerConfig: config, }, nil } ================================================ FILE: service/worker/scanner/shardscanner/scanner_workflow_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "testing" "github.com/stretchr/testify/suite" "go.uber.org/cadence/testsuite" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" ) type scannerWorkflowsSuite struct { suite.Suite testsuite.WorkflowTestSuite } func TestScannerWorkflowSuite(t *testing.T) { suite.Run(t, new(scannerWorkflowsSuite)) } func (s *scannerWorkflowsSuite) TestGetBatchIndices() { testCases := []struct { batchSize int concurrency int sliceLength int workerIdx int batches [][]int }{ { batchSize: 1, concurrency: 1, sliceLength: 20, workerIdx: 0, batches: [][]int{{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}}, }, { batchSize: 2, concurrency: 1, sliceLength: 20, workerIdx: 0, batches: [][]int{{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}}, }, { batchSize: 7, concurrency: 1, sliceLength: 20, workerIdx: 0, batches: [][]int{{0, 1, 2, 3, 4, 5, 6}, {7, 8, 9, 10, 11, 12, 13}, {14, 15, 16, 17, 18, 19}}, }, { batchSize: 5, concurrency: 3, sliceLength: 20, workerIdx: 0, batches: [][]int{{0, 3, 6, 9, 12}, {15, 18}}, }, { batchSize: 5, concurrency: 3, sliceLength: 20, workerIdx: 1, batches: [][]int{{1, 4, 7, 10, 13}, {16, 19}}, }, { batchSize: 5, concurrency: 3, sliceLength: 20, workerIdx: 2, batches: [][]int{{2, 5, 8, 11, 14}, {17}}, }, } for _, tc := range testCases { s.Equal(tc.batches, getBatchIndices(tc.batchSize, tc.concurrency, tc.sliceLength, tc.workerIdx)) } } func (s *scannerWorkflowsSuite) TestGetShardBatches() { var shards []int for i := 5; i < 50; i += 2 { shards = append(shards, i) } batches := getShardBatches(5, 3, shards, 1) s.Equal([][]int{ {7, 13, 19, 25, 31}, {37, 43, 49}, }, batches) } func (s *scannerWorkflowsSuite) TestFlattenShards() { testCases := []struct { input Shards expectedList []int expectedMin int expectedMax int msg string }{ { input: Shards{ List: []int{1, 2, 3}, }, expectedList: []int{1, 2, 3}, expectedMin: 1, expectedMax: 3, msg: "Shard list provided", }, { input: Shards{ Range: &ShardRange{ Min: 5, Max: 10, }, }, expectedList: []int{5, 6, 7, 8, 9}, expectedMin: 5, expectedMax: 9, msg: "Shard range provided", }, { input: Shards{ List: []int{1, 90, 2, 3}, }, expectedList: []int{1, 90, 2, 3}, expectedMin: 1, expectedMax: 90, msg: "Unordered shard list provided", }, } for _, tc := range testCases { shardList, min, max := tc.input.Flatten() s.Equal(tc.expectedList, shardList) s.Equal(tc.expectedMin, min) s.Equal(tc.expectedMax, max) } } func (s *scannerWorkflowsSuite) TestValidateShards() { testCases := []struct { shards Shards expectErr bool }{ { shards: Shards{}, expectErr: true, }, { shards: Shards{ List: []int{}, Range: &ShardRange{}, }, expectErr: true, }, { shards: Shards{ List: []int{}, }, expectErr: true, }, { shards: Shards{ Range: &ShardRange{ Min: 0, Max: 0, }, }, expectErr: true, }, { shards: Shards{ Range: &ShardRange{ Min: 0, Max: 1, }, }, expectErr: false, }, } for _, tc := range testCases { err := tc.shards.Validate() if tc.expectErr { s.Error(err) } else { s.NoError(err) } } } func (s *fixerWorkflowSuite) TestNewScannerHooks() { testCases := []struct { name string manager ManagerCB iterator IteratorCB wantErr bool }{ { name: "both arguments are nil", manager: nil, iterator: nil, wantErr: true, }, { name: "manager is not provided", manager: nil, iterator: func( ctx context.Context, retryer persistence.Retryer, params ScanShardActivityParams, ) pagination.Iterator { return nil }, wantErr: true, }, { name: "iterator is not provided", manager: func( ctx context.Context, retryer persistence.Retryer, params ScanShardActivityParams, cache cache.DomainCache, ) invariant.Manager { return nil }, iterator: nil, wantErr: true, }, { name: "both provided", manager: func( ctx context.Context, retryer persistence.Retryer, params ScanShardActivityParams, cache cache.DomainCache, ) invariant.Manager { return nil }, iterator: func( ctx context.Context, retryer persistence.Retryer, params ScanShardActivityParams, ) pagination.Iterator { return nil }, wantErr: false, }, } for _, tc := range testCases { s.Run(tc.name, func() { _, err := NewScannerHooks(tc.manager, tc.iterator, func(scanner ScannerContext) CustomScannerConfig { return nil // no config overrides }) if tc.wantErr { s.Error(err) } else { s.NoError(err) } }) } } ================================================ FILE: service/worker/scanner/shardscanner/types.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "errors" "fmt" "time" "go.uber.org/cadence" "go.uber.org/cadence/activity" "go.uber.org/cadence/client" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/common/resource" ) const ( // ErrScanWorkflowNotClosed indicates fix was attempted on scan workflow which was not finished ErrScanWorkflowNotClosed = "scan workflow is not closed, only can run fix on output of finished scan workflow" // ErrSerialization indicates a serialization or deserialization error occurred ErrSerialization = "encountered serialization error" // ErrMissingHooks indicates scanner is not providing hooks to Invariant manager or Iterator ErrMissingHooks = "hooks are not provided for this scanner" ) type ( contextKey string // ScannerContext is the resource that is available in activities under ShardScanner context key ScannerContext struct { Resource resource.Resource Hooks *ScannerHooks Scope metrics.Scope Config *ScannerConfig Logger log.Logger } // FixerContext is the resource that is available to activities under ShardFixer key FixerContext struct { Resource resource.Resource Hooks *FixerHooks Scope metrics.Scope Config *ScannerConfig Logger log.Logger } // ScannerEmitMetricsActivityParams is the parameter for scannerEmitMetricsActivity ScannerEmitMetricsActivityParams struct { ShardSuccessCount int ShardControlFlowFailureCount int AggregateReportResult AggregateScanReportResult ShardDistributionStats ShardDistributionStats } // ShardRange identifies a set of shards based on min (inclusive) and max (exclusive) ShardRange struct { Min int Max int } // Shards identify the shards that should be scanned. // Exactly one of List or Range should be non-nil. Shards struct { List []int Range *ShardRange } // ScannerWorkflowParams are the parameters to the scan workflow ScannerWorkflowParams struct { Shards Shards ScannerWorkflowConfigOverwrites ScannerWorkflowConfigOverwrites } // ScannerConfigActivityParams is the parameter for scannerConfigActivity ScannerConfigActivityParams struct { Overwrites ScannerWorkflowConfigOverwrites } // ScanShardActivityParams is the parameter for scanShardActivity ScanShardActivityParams struct { Shards []int PageSize int BlobstoreFlushThreshold int ScannerConfig CustomScannerConfig } // FixerWorkflowParams are the parameters to the fix workflow FixerWorkflowParams struct { ScannerWorkflowWorkflowID string ScannerWorkflowRunID string FixerWorkflowConfigOverwrites FixerWorkflowConfigOverwrites } // ScanReport is the report of running Scan on a single shard. ScanReport struct { ShardID int Stats ScanStats Result ScanResult DomainStats map[string]*ScanStats } // DomainStats is the report of stats for one domain DomainScanStats struct { DomainID string Stats ScanStats } // DomainStats is the report of stats for one domain DomainFixStats struct { DomainID string Stats FixStats } // ScanStats indicates the stats of entities which were handled by shard Scan. ScanStats struct { EntitiesCount int64 CorruptedCount int64 CheckFailedCount int64 CorruptionByType map[invariant.Name]int64 } // ScanResult indicates the result of running scan on a shard. // Exactly one of ControlFlowFailure or ScanKeys will be non-nil ScanResult struct { ShardScanKeys *ScanKeys ControlFlowFailure *ControlFlowFailure } // ScanKeys are the keys to the blobs that were uploaded during scan. // Keys can be nil if there were no uploads. ScanKeys struct { Corrupt *store.Keys Failed *store.Keys } // FixReport is the report of running Fix on a single shard FixReport struct { ShardID int Stats FixStats Result FixResult DomainStats map[string]*FixStats } // FixStats indicates the stats of executions that were handled by shard Fix. FixStats struct { EntitiesCount int64 FixedCount int64 SkippedCount int64 FailedCount int64 } // FixResult indicates the result of running fix on a shard. // Exactly one of ControlFlowFailure or FixKeys will be non-nil. FixResult struct { ShardFixKeys *FixKeys ControlFlowFailure *ControlFlowFailure } // FixKeys are the keys to the blobs that were uploaded during fix. // Keys can be nil if there were no uploads. FixKeys struct { Skipped *store.Keys Failed *store.Keys Fixed *store.Keys } // ControlFlowFailure indicates an error occurred which makes it impossible to // even attempt to check or fix one or more execution(s). Note that it is not a ControlFlowFailure // if a check or fix fails, it is only a ControlFlowFailure if // an error is encountered which makes even attempting to check or fix impossible. ControlFlowFailure struct { Info string InfoDetails string } // FixerCorruptedKeysActivityParams is the parameter for fixerCorruptedKeysActivity FixerCorruptedKeysActivityParams struct { ScannerWorkflowWorkflowID string ScannerWorkflowRunID string StartingShardID *int } // FixShardActivityParams is the parameter for fixShardActivity FixShardActivityParams struct { CorruptedKeysEntries []CorruptedKeysEntry ResolvedFixerWorkflowConfig ResolvedFixerWorkflowConfig // EnabledInvariants contains all known invariants for fixer, with "true" or "false" values. // In current code it should never be empty. // // If empty, EnabledInvariants came from old serialized data prior to this field existing, // and the historical list of invariants should be used. This should be a one-time event // after upgrading. EnabledInvariants CustomScannerConfig } // CustomScannerConfig is used to pass key/value parameters between shardscanner activity and scanner/fixer implementations. // this is used to have activities with better consistency, as the workflow records one config and uses it for all shards, // even after config / code changes. // // It is currently only used to pass invariant names to control whether they are enabled or not. // Please do not use this for other purposes. CustomScannerConfig map[string]string // GenericScannerConfig is a generic params for all shard scanners GenericScannerConfig struct { Enabled bool Concurrency int PageSize int BlobstoreFlushThreshold int ActivityBatchSize int } // GenericScannerConfigOverwrites allows to override generic params GenericScannerConfigOverwrites struct { Enabled *bool Concurrency *int PageSize *int BlobstoreFlushThreshold *int ActivityBatchSize *int } // ResolvedScannerWorkflowConfig is the resolved config after reading dynamic config // and applying overwrites. ResolvedScannerWorkflowConfig struct { GenericScannerConfig GenericScannerConfig CustomScannerConfig CustomScannerConfig } // ScannerWorkflowConfigOverwrites enables overwriting the values in dynamic config. // If provided workflow will favor overwrites over dynamic config. // Any overwrites that are left as nil will fall back to using dynamic config. ScannerWorkflowConfigOverwrites struct { GenericScannerConfig GenericScannerConfigOverwrites CustomScannerConfig *CustomScannerConfig } // DynamicParams is the dynamic config for scanner workflow. DynamicParams struct { ScannerEnabled dynamicproperties.BoolPropertyFn FixerEnabled dynamicproperties.BoolPropertyFn Concurrency dynamicproperties.IntPropertyFn PageSize dynamicproperties.IntPropertyFn BlobstoreFlushThreshold dynamicproperties.IntPropertyFn ActivityBatchSize dynamicproperties.IntPropertyFn AllowDomain dynamicproperties.BoolPropertyFnWithDomainFilter } // ScannerConfig is the config for ShardScanner workflow ScannerConfig struct { ScannerWFTypeName string FixerWFTypeName string ScannerHooks func() *ScannerHooks FixerHooks func() *FixerHooks DynamicParams DynamicParams DynamicCollection *dynamicconfig.Collection StartWorkflowOptions client.StartWorkflowOptions StartFixerOptions client.StartWorkflowOptions } // FixerWorkflowConfigOverwrites enables overwriting the default values. // If provided workflow will favor overwrites over defaults. // Any overwrites that are left as nil will fall back to defaults. FixerWorkflowConfigOverwrites struct { Concurrency *int BlobstoreFlushThreshold *int ActivityBatchSize *int } // ResolvedFixerWorkflowConfig is the resolved config after reading defaults and applying overwrites. ResolvedFixerWorkflowConfig struct { Concurrency int BlobstoreFlushThreshold int ActivityBatchSize int } ) func getShortActivityContext(ctx workflow.Context) workflow.Context { activityOptions := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: 5 * time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 1.7, ExpirationInterval: 10 * time.Minute, NonRetriableErrorReasons: []string{ErrScanWorkflowNotClosed, ErrSerialization, ErrMissingHooks}, }, } return workflow.WithActivityOptions(ctx, activityOptions) } func getLongActivityContext(ctx workflow.Context) workflow.Context { activityOptions := workflow.ActivityOptions{ ScheduleToStartTimeout: time.Minute, StartToCloseTimeout: 48 * time.Hour, HeartbeatTimeout: time.Minute, RetryPolicy: &cadence.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 1.7, ExpirationInterval: 48 * time.Hour, NonRetriableErrorReasons: []string{ErrScanWorkflowNotClosed, ErrSerialization, ErrMissingHooks}, }, } return workflow.WithActivityOptions(ctx, activityOptions) } // GetCorruptedKeys is a workflow which is used to retrieve keys for fixer workflow. func GetCorruptedKeys( ctx workflow.Context, params FixerWorkflowParams, ) (*FixerCorruptedKeysActivityResult, error) { fixerCorruptedKeysActivityParams := FixerCorruptedKeysActivityParams{ ScannerWorkflowWorkflowID: params.ScannerWorkflowWorkflowID, ScannerWorkflowRunID: params.ScannerWorkflowRunID, StartingShardID: nil, } var minShardID *int var maxShardID *int var corruptKeys []CorruptedKeysEntry isFirst := true for isFirst || fixerCorruptedKeysActivityParams.StartingShardID != nil { isFirst = false corruptedKeysResult := &FixerCorruptedKeysActivityResult{} activityCtx := getShortActivityContext(ctx) if err := workflow.ExecuteActivity( activityCtx, ActivityFixerCorruptedKeys, fixerCorruptedKeysActivityParams).Get(ctx, &corruptedKeysResult); err != nil { return nil, err } fixerCorruptedKeysActivityParams.StartingShardID = corruptedKeysResult.ShardQueryPaginationToken.NextShardID if len(corruptedKeysResult.CorruptedKeys) == 0 { continue } corruptKeys = append(corruptKeys, corruptedKeysResult.CorruptedKeys...) if corruptedKeysResult.MinShard != nil && (minShardID == nil || *minShardID > *corruptedKeysResult.MinShard) { minShardID = corruptedKeysResult.MinShard } if corruptedKeysResult.MaxShard != nil && (maxShardID == nil || *maxShardID < *corruptedKeysResult.MaxShard) { maxShardID = corruptedKeysResult.MaxShard } } return &FixerCorruptedKeysActivityResult{ CorruptedKeys: corruptKeys, MinShard: minShardID, MaxShard: maxShardID, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: nil, IsDone: true, }, }, nil } func getBatchIndices( batchSize int, concurrency int, sliceLength int, workerIdx int, ) [][]int { var batches [][]int var currBatch []int for i := 0; i < sliceLength; i++ { if i%concurrency == workerIdx { currBatch = append(currBatch, i) if len(currBatch) == batchSize { batches = append(batches, currBatch) currBatch = nil } } } if len(currBatch) > 0 { batches = append(batches, currBatch) } return batches } // Validate validates shard list or range func (s Shards) Validate() error { if s.List == nil && s.Range == nil { return errors.New("must provide either List or Range") } if s.List != nil && s.Range != nil { return errors.New("only one of List or Range can be provided") } if s.List != nil && len(s.List) == 0 { return errors.New("empty List provided") } if s.Range != nil && s.Range.Max <= s.Range.Min { return errors.New("empty Range provided") } return nil } // Flatten flattens Shards to a list of shard IDs and finds the min/max shardID func (s Shards) Flatten() ([]int, int, int) { shardList := s.List if len(shardList) == 0 { shardList = []int{} for i := s.Range.Min; i < s.Range.Max; i++ { shardList = append(shardList, i) } } min := shardList[0] max := shardList[0] for i := 1; i < len(shardList); i++ { if shardList[i] < min { min = shardList[i] } if shardList[i] > max { max = shardList[i] } } return shardList, min, max } // NewShardScannerContext sets scanner context up func NewShardScannerContext( res resource.Resource, config *ScannerConfig, ) ScannerContext { return ScannerContext{ Resource: res, Scope: res.GetMetricsClient().Scope(metrics.ExecutionsScannerScope), Config: config, Logger: res.GetLogger().WithTags(tag.ComponentShardScanner), Hooks: config.ScannerHooks(), } } // NewShardFixerContext sets fixer context up func NewShardFixerContext( res resource.Resource, config *ScannerConfig, ) FixerContext { return FixerContext{ Resource: res, Scope: res.GetMetricsClient().Scope(metrics.ExecutionsFixerScope), Config: config, Hooks: config.FixerHooks(), Logger: res.GetLogger().WithTags(tag.ComponentShardFixer), } } // NewContext provides context to be used as background activity context func NewFixerContext( ctx context.Context, workflowName string, fixerContext FixerContext, ) context.Context { return context.WithValue(ctx, contextKey(workflowName), fixerContext) } // NewContext provides context to be used as background activity context func NewScannerContext( ctx context.Context, workflowName string, scannerContext ScannerContext, ) context.Context { return context.WithValue(ctx, contextKey(workflowName), scannerContext) } // GetScannerContext extracts scanner context from activity context func GetScannerContext( ctx context.Context, ) (ScannerContext, error) { info := activity.GetInfo(ctx) if info.WorkflowType == nil { return ScannerContext{}, fmt.Errorf("workflowType is nil") } val, ok := ctx.Value(contextKey(info.WorkflowType.Name)).(ScannerContext) if !ok { return ScannerContext{}, fmt.Errorf("context type is not %T for a key %q", val, info.WorkflowType.Name) } return val, nil } // GetFixerContext extracts fixer context from activity context // it uses typed, private key to reduce access scope func GetFixerContext( ctx context.Context, ) (FixerContext, error) { info := activity.GetInfo(ctx) if info.WorkflowType == nil { return FixerContext{}, fmt.Errorf("workflowType is nil") } val, ok := ctx.Value(contextKey(info.WorkflowType.Name)).(FixerContext) if !ok { return FixerContext{}, fmt.Errorf("context type is not %T for a key %q", val, info.WorkflowType.Name) } return val, nil } // Empty returns true if this ScanResult has no "real" data, e.g. only nils or empty values. func (s *ScanResult) Empty() bool { if s == nil { return true } if s.ControlFlowFailure != nil && (*s.ControlFlowFailure != ControlFlowFailure{}) { return false // at least control flow failure has data } if s.ShardScanKeys != nil { if s.ShardScanKeys.Corrupt != nil && (*s.ShardScanKeys.Corrupt != store.Keys{}) { return false // corrupt data exists } if s.ShardScanKeys.Failed != nil && (*s.ShardScanKeys.Failed != store.Keys{}) { return false // failed data exists } } return true // both empty } // Empty returns true if this FixResult has no "real" data, e.g. only nils or empty values. func (f *FixResult) Empty() bool { if f == nil { return true } if f.ControlFlowFailure != nil && (*f.ControlFlowFailure != ControlFlowFailure{}) { return false // at least control flow failure has data } if f.ShardFixKeys != nil { if f.ShardFixKeys.Fixed != nil && (*f.ShardFixKeys.Fixed != store.Keys{}) { return false // fixed data exists } if f.ShardFixKeys.Failed != nil && (*f.ShardFixKeys.Failed != store.Keys{}) { return false // failed data exists } if f.ShardFixKeys.Skipped != nil && (*f.ShardFixKeys.Skipped != store.Keys{}) { return false // skipped data exists } } return true // both empty } ================================================ FILE: service/worker/scanner/shardscanner/types_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "context" "testing" ) func TestGetFixerContextPanicsWhenNonActivityContextProvided(t *testing.T) { defer func() { recover() }() GetFixerContext(context.Background()) t.Errorf("should have panicked") } func TestGetScannerContextPanicsWhenNonActivityContextProvided(t *testing.T) { defer func() { recover() }() GetScannerContext(context.Background()) t.Errorf("should have panicked") } ================================================ FILE: service/worker/scanner/shardscanner/workflows.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import "go.uber.org/cadence/activity" func init() { // shardscanner activities activity.RegisterWithOptions(scannerEmitMetricsActivity, activity.RegisterOptions{Name: ActivityScannerEmitMetrics}) activity.RegisterWithOptions(scanShardActivity, activity.RegisterOptions{Name: ActivityScanShard}) activity.RegisterWithOptions(scannerConfigActivity, activity.RegisterOptions{Name: ActivityScannerConfig}) activity.RegisterWithOptions(fixerConfigActivity, activity.RegisterOptions{Name: ActivityFixerConfig}) activity.RegisterWithOptions(fixerCorruptedKeysActivity, activity.RegisterOptions{Name: ActivityFixerCorruptedKeys}) activity.RegisterWithOptions(fixShardActivity, activity.RegisterOptions{Name: ActivityFixShard}) } ================================================ FILE: service/worker/scanner/shardscanner/workflows_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package shardscanner import ( "errors" "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" ) type workflowsSuite struct { suite.Suite testsuite.WorkflowTestSuite env *testsuite.TestWorkflowEnvironment } func TestWorkflowsSuite(t *testing.T) { suite.Run(t, new(workflowsSuite)) } func (s *workflowsSuite) SetupTest() { s.env = s.WorkflowTestSuite.NewTestWorkflowEnvironment() s.env.RegisterWorkflow(NewTestWorkflow) s.env.RegisterWorkflow(NewTestFixerWorkflow) s.env.RegisterWorkflow(GetCorruptedKeys) } func (s *workflowsSuite) TestScannerWorkflow_Failure_ScanShard() { s.env.OnActivity(ActivityScannerConfig, mock.Anything, mock.Anything).Return(ResolvedScannerWorkflowConfig{ GenericScannerConfig: GenericScannerConfig{ Enabled: true, Concurrency: 3, ActivityBatchSize: 5, }, }, nil) shards := Shards{ Range: &ShardRange{ Min: 0, Max: 30, }, } batches := [][]int{ {0, 3, 6, 9, 12}, {15, 18, 21, 24, 27}, {1, 4, 7, 10, 13}, {16, 19, 22, 25, 28}, {2, 5, 8, 11, 14}, {17, 20, 23, 26, 29}, } for i, batch := range batches { var reports []ScanReport var err error if i == len(batches)-1 { reports = nil err = errors.New("scan shard activity got error") } else { err = nil for _, s := range batch { reports = append(reports, ScanReport{ ShardID: s, Stats: ScanStats{ EntitiesCount: 10, }, Result: ScanResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "got control flow failure", }, }, }) } } s.env.OnActivity(ActivityScanShard, mock.Anything, ScanShardActivityParams{ Shards: batch, }).Return(reports, err) } s.env.ExecuteWorkflow(NewTestWorkflow, "test-workflow", ScannerWorkflowParams{ Shards: shards, }) s.True(s.env.IsWorkflowCompleted()) s.Equal("scan shard activity got error", s.env.GetWorkflowError().Error()) } func (s *workflowsSuite) TestScannerWorkflow_Failure_ScannerConfigActivity() { s.env.OnActivity(ActivityScannerConfig, mock.Anything, mock.Anything).Return(ResolvedScannerWorkflowConfig{}, errors.New("got error getting config")) s.env.ExecuteWorkflow(NewTestWorkflow, "test-workflow", ScannerWorkflowParams{ Shards: Shards{ List: []int{1, 2, 3}, }, }) s.True(s.env.IsWorkflowCompleted()) s.Equal("got error getting config", s.env.GetWorkflowError().Error()) } func (s *workflowsSuite) TestScannerWorkflow_Requires_Name() { s.env.OnActivity(ActivityScannerConfig, mock.Anything, mock.Anything).Return(ResolvedScannerWorkflowConfig{}, errors.New("got error getting config")) s.env.ExecuteWorkflow(NewTestWorkflow, "", ScannerWorkflowParams{ Shards: Shards{ List: []int{1, 2, 3}, }, }) s.True(s.env.IsWorkflowCompleted()) s.Equal("workflow name is not provided", s.env.GetWorkflowError().Error()) } func (s *workflowsSuite) TestScannerWorkflow_Requires_Valid_ShardConfig() { s.env.OnActivity(ActivityScannerConfig, mock.Anything, mock.Anything).Return(ResolvedScannerWorkflowConfig{}, errors.New("got error getting config")) s.env.ExecuteWorkflow(NewTestWorkflow, "test-workflow", ScannerWorkflowParams{}) s.True(s.env.IsWorkflowCompleted()) s.Equal("must provide either List or Range", s.env.GetWorkflowError().Error()) } func (s *workflowsSuite) TestScannerWorkflow_Success_Disabled() { s.env.OnActivity(ActivityScannerConfig, mock.Anything, mock.Anything).Return(ResolvedScannerWorkflowConfig{ GenericScannerConfig: GenericScannerConfig{ Enabled: false, }, }, nil) s.env.ExecuteWorkflow(NewTestWorkflow, "test-workflow", ScannerWorkflowParams{ Shards: Shards{ List: []int{1, 2, 3}, }, }) s.True(s.env.IsWorkflowCompleted()) s.NoError(s.env.GetWorkflowError()) } func (s *workflowsSuite) TestFixerWorkflow_Success() { corruptedKeys := make([]CorruptedKeysEntry, 30) for i := 0; i < 30; i++ { corruptedKeys[i] = CorruptedKeysEntry{ ShardID: i, } } s.env.OnActivity(ActivityFixerCorruptedKeys, mock.Anything, mock.Anything).Return(&FixerCorruptedKeysActivityResult{ CorruptedKeys: corruptedKeys, MinShard: common.IntPtr(0), MaxShard: common.IntPtr(29), ShardQueryPaginationToken: ShardQueryPaginationToken{ IsDone: true, NextShardID: nil, }, }, nil) enabledFixInvariants := CustomScannerConfig{ // historically enabled by default invariant.CollectionHistory.String(): "true", invariant.CollectionMutableState.String(): "true", // disabled by default invariant.CollectionStale.String(): "false", } s.env.OnActivity(ActivityFixerConfig, mock.Anything, FixShardConfigParams{ /* no contents currently */ }).Return(&FixShardConfigResults{ EnabledInvariants: enabledFixInvariants, }, nil) fixerWorkflowConfigOverwrites := FixerWorkflowConfigOverwrites{ Concurrency: common.IntPtr(3), BlobstoreFlushThreshold: common.IntPtr(1000), ActivityBatchSize: common.IntPtr(5), } resolvedFixerWorkflowConfig := ResolvedFixerWorkflowConfig{ Concurrency: 3, ActivityBatchSize: 5, BlobstoreFlushThreshold: 1000, } batches := [][]int{ {0, 3, 6, 9, 12}, {15, 18, 21, 24, 27}, {1, 4, 7, 10, 13}, {16, 19, 22, 25, 28}, {2, 5, 8, 11, 14}, {17, 20, 23, 26, 29}, } for _, batch := range batches { var corruptedKeys []CorruptedKeysEntry for _, shard := range batch { corruptedKeys = append(corruptedKeys, CorruptedKeysEntry{ ShardID: shard, }) } var reports []FixReport for i, s := range batch { if i == 0 { reports = append(reports, FixReport{ ShardID: s, Stats: FixStats{ EntitiesCount: 10, }, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "got control flow failure", }, }, }) } else { reports = append(reports, FixReport{ ShardID: s, Stats: FixStats{ EntitiesCount: 10, FixedCount: 2, SkippedCount: 1, FailedCount: 1, }, Result: FixResult{ ShardFixKeys: &FixKeys{ Skipped: &store.Keys{ UUID: "skipped_keys", }, Failed: &store.Keys{ UUID: "failed_keys", }, Fixed: &store.Keys{ UUID: "fixed_keys", }, }, }, }) } } s.env.OnActivity(ActivityFixShard, mock.Anything, FixShardActivityParams{ CorruptedKeysEntries: corruptedKeys, ResolvedFixerWorkflowConfig: resolvedFixerWorkflowConfig, EnabledInvariants: enabledFixInvariants, }).Return(reports, nil) } s.env.ExecuteWorkflow(NewTestFixerWorkflow, FixerWorkflowParams{ ScannerWorkflowWorkflowID: "test_wid", ScannerWorkflowRunID: "test_rid", FixerWorkflowConfigOverwrites: fixerWorkflowConfigOverwrites, }) s.True(s.env.IsWorkflowCompleted()) s.NoError(s.env.GetWorkflowError()) aggValue, err := s.env.QueryWorkflow(AggregateReportQuery) s.NoError(err) var agg AggregateFixReportResult s.NoError(aggValue.Get(&agg)) s.Equal(AggregateFixReportResult{ EntitiesCount: 240, FixedCount: 48, FailedCount: 24, SkippedCount: 24, }, agg) for i := 0; i < 30; i++ { shardReportValue, err := s.env.QueryWorkflow(ShardReportQuery, i) s.NoError(err) var shardReport *FixReport s.NoError(shardReportValue.Get(&shardReport)) if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { s.Equal(&FixReport{ ShardID: i, Stats: FixStats{ EntitiesCount: 10, }, Result: FixResult{ ControlFlowFailure: &ControlFlowFailure{ Info: "got control flow failure", }, }, }, shardReport) } else { s.Equal(&FixReport{ ShardID: i, Stats: FixStats{ EntitiesCount: 10, FixedCount: 2, FailedCount: 1, SkippedCount: 1, }, Result: FixResult{ ShardFixKeys: &FixKeys{ Skipped: &store.Keys{ UUID: "skipped_keys", }, Failed: &store.Keys{ UUID: "failed_keys", }, Fixed: &store.Keys{ UUID: "fixed_keys", }, }, }, }, shardReport) } } statusValue, err := s.env.QueryWorkflow(ShardStatusQuery, PaginatedShardQueryRequest{}) s.NoError(err) var status *ShardStatusQueryResult s.NoError(statusValue.Get(&status)) expected := make(map[int]ShardStatus) for i := 0; i < 30; i++ { if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { expected[i] = ShardStatusControlFlowFailure } else { expected[i] = ShardStatusSuccess } } s.Equal(ShardStatusResult(expected), status.Result) // check for paginated query result statusValue, err = s.env.QueryWorkflow(ShardStatusQuery, PaginatedShardQueryRequest{ StartingShardID: common.IntPtr(5), LimitShards: common.IntPtr(10), }) s.NoError(err) status = &ShardStatusQueryResult{} s.NoError(statusValue.Get(&status)) expected = make(map[int]ShardStatus) for i := 5; i < 15; i++ { if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { expected[i] = ShardStatusControlFlowFailure } else { expected[i] = ShardStatusSuccess } } s.Equal(ShardStatusResult(expected), status.Result) s.False(status.ShardQueryPaginationToken.IsDone) s.Equal(15, *status.ShardQueryPaginationToken.NextShardID) } func (s *workflowsSuite) TestGetCorruptedKeys_Success() { s.env.OnActivity(ActivityFixerCorruptedKeys, mock.Anything, FixerCorruptedKeysActivityParams{ ScannerWorkflowWorkflowID: "test_wid", ScannerWorkflowRunID: "test_rid", StartingShardID: nil, }).Return(&FixerCorruptedKeysActivityResult{ CorruptedKeys: []CorruptedKeysEntry{{ShardID: 1}, {ShardID: 5}, {ShardID: 10}}, MinShard: common.IntPtr(1), MaxShard: common.IntPtr(10), ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: common.IntPtr(11), IsDone: false, }, }, nil) s.env.OnActivity(ActivityFixerCorruptedKeys, mock.Anything, FixerCorruptedKeysActivityParams{ ScannerWorkflowWorkflowID: "test_wid", ScannerWorkflowRunID: "test_rid", StartingShardID: common.IntPtr(11), }).Return(&FixerCorruptedKeysActivityResult{ CorruptedKeys: []CorruptedKeysEntry{{ShardID: 11}, {ShardID: 12}}, MinShard: common.IntPtr(11), MaxShard: common.IntPtr(12), ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: common.IntPtr(13), IsDone: false, }, }, nil) s.env.OnActivity(ActivityFixerCorruptedKeys, mock.Anything, FixerCorruptedKeysActivityParams{ ScannerWorkflowWorkflowID: "test_wid", ScannerWorkflowRunID: "test_rid", StartingShardID: common.IntPtr(13), }).Return(&FixerCorruptedKeysActivityResult{ CorruptedKeys: []CorruptedKeysEntry{{ShardID: 20}, {ShardID: 41}}, MinShard: common.IntPtr(20), MaxShard: common.IntPtr(41), ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: common.IntPtr(42), IsDone: false, }, }, nil) s.env.OnActivity(ActivityFixerCorruptedKeys, mock.Anything, FixerCorruptedKeysActivityParams{ ScannerWorkflowWorkflowID: "test_wid", ScannerWorkflowRunID: "test_rid", StartingShardID: common.IntPtr(42), }).Return(&FixerCorruptedKeysActivityResult{ CorruptedKeys: []CorruptedKeysEntry{}, MinShard: nil, MaxShard: nil, ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: nil, IsDone: true, }, }, nil) s.env.ExecuteWorkflow(GetCorruptedKeys, FixerWorkflowParams{ ScannerWorkflowWorkflowID: "test_wid", ScannerWorkflowRunID: "test_rid", }) s.True(s.env.IsWorkflowCompleted()) s.NoError(s.env.GetWorkflowError()) var result *FixerCorruptedKeysActivityResult s.NoError(s.env.GetWorkflowResult(&result)) s.Equal(&FixerCorruptedKeysActivityResult{ CorruptedKeys: []CorruptedKeysEntry{ {ShardID: 1}, {ShardID: 5}, {ShardID: 10}, {ShardID: 11}, {ShardID: 12}, {ShardID: 20}, {ShardID: 41}, }, MinShard: common.IntPtr(1), MaxShard: common.IntPtr(41), ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: nil, IsDone: true, }, }, result) } func (s *workflowsSuite) TestGetCorruptedKeys_Error() { s.env.OnActivity(ActivityFixerCorruptedKeys, mock.Anything, FixerCorruptedKeysActivityParams{ ScannerWorkflowWorkflowID: "test_wid", ScannerWorkflowRunID: "test_rid", StartingShardID: nil, }).Return(&FixerCorruptedKeysActivityResult{ CorruptedKeys: []CorruptedKeysEntry{{ShardID: 1}, {ShardID: 5}, {ShardID: 10}}, MinShard: common.IntPtr(1), MaxShard: common.IntPtr(10), ShardQueryPaginationToken: ShardQueryPaginationToken{ NextShardID: common.IntPtr(11), IsDone: false, }, }, nil) s.env.OnActivity(ActivityFixerCorruptedKeys, mock.Anything, FixerCorruptedKeysActivityParams{ ScannerWorkflowWorkflowID: "test_wid", ScannerWorkflowRunID: "test_rid", StartingShardID: common.IntPtr(11), }).Return(nil, errors.New("got error")) s.env.ExecuteWorkflow(GetCorruptedKeys, FixerWorkflowParams{ ScannerWorkflowWorkflowID: "test_wid", ScannerWorkflowRunID: "test_rid", }) s.True(s.env.IsWorkflowCompleted()) s.Error(s.env.GetWorkflowError()) } func (s *workflowsSuite) TestScannerWorkflow_Failure_CorruptedKeysActivity() { s.env.OnActivity(ActivityFixerCorruptedKeys, mock.Anything, mock.Anything).Return(nil, errors.New("got error getting corrupted keys")) s.env.ExecuteWorkflow(NewTestFixerWorkflow, FixerWorkflowParams{}) s.True(s.env.IsWorkflowCompleted()) s.Equal("got error getting corrupted keys", s.env.GetWorkflowError().Error()) } func NewTestWorkflow(ctx workflow.Context, name string, params ScannerWorkflowParams) error { wf, err := NewScannerWorkflow(ctx, name, params) if err != nil { return err } return wf.Start(ctx) } func NewTestFixerWorkflow(ctx workflow.Context, params FixerWorkflowParams) error { wf, err := NewFixerWorkflow(ctx, "test-fixer", params) if err != nil { return err } return wf.Start(ctx) } ================================================ FILE: service/worker/scanner/tasklist/db.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "context" "time" "github.com/uber/cadence/common/backoff" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" ) var retryForeverPolicy = newRetryForeverPolicy() func (s *Scavenger) completeTasks(info *p.TaskListInfo, taskID int64, limit int) (int, error) { var resp *p.CompleteTasksLessThanResponse var err error domainName, errorDomain := s.cache.GetDomainName(info.DomainID) if errorDomain != nil { return 0, errorDomain } err = s.retryForever(func(ctx context.Context) error { resp, err = s.db.CompleteTasksLessThan(ctx, &p.CompleteTasksLessThanRequest{ DomainID: info.DomainID, TaskListName: info.Name, TaskType: info.TaskType, TaskID: taskID, Limit: limit, DomainName: domainName, }) return err }) if resp != nil { return resp.TasksCompleted, err } return 0, err } func (s *Scavenger) getOrphanTasks(limit int) (*p.GetOrphanTasksResponse, error) { var tasks *p.GetOrphanTasksResponse var err error err = s.retryForever(func(ctx context.Context) error { tasks, err = s.db.GetOrphanTasks(ctx, &p.GetOrphanTasksRequest{ Limit: limit, }) return err }) return tasks, err } func (s *Scavenger) completeTask(info *p.TaskListInfo, taskid int64) error { var err error domainName, errorDomain := s.cache.GetDomainName(info.DomainID) if errorDomain != nil { return errorDomain } err = s.retryForever(func(ctx context.Context) error { err = s.db.CompleteTask(ctx, &p.CompleteTaskRequest{ TaskList: info, TaskID: taskid, DomainName: domainName, }) return err }) return err } func (s *Scavenger) getTasks(info *p.TaskListInfo, batchSize int) (*p.GetTasksResponse, error) { var err error var resp *p.GetTasksResponse domainName, errorDomain := s.cache.GetDomainName(info.DomainID) if errorDomain != nil { return nil, errorDomain } err = s.retryForever(func(ctx context.Context) error { resp, err = s.db.GetTasks(ctx, &p.GetTasksRequest{ DomainID: info.DomainID, TaskList: info.Name, TaskType: info.TaskType, ReadLevel: -1, // get the first N tasks sorted by taskID BatchSize: batchSize, DomainName: domainName, }) return err }) return resp, err } func (s *Scavenger) listTaskList(pageSize int, pageToken []byte) (*p.ListTaskListResponse, error) { var err error var resp *p.ListTaskListResponse err = s.retryForever(func(ctx context.Context) error { resp, err = s.db.ListTaskList(ctx, &p.ListTaskListRequest{ PageSize: pageSize, PageToken: pageToken, }) return err }) return resp, err } func (s *Scavenger) deleteTaskList(info *p.TaskListInfo) error { domainName, errorDomain := s.cache.GetDomainName(info.DomainID) if errorDomain != nil { return errorDomain } op := func(ctx context.Context) error { return s.db.DeleteTaskList(ctx, &p.DeleteTaskListRequest{ DomainID: info.DomainID, TaskListName: info.Name, TaskListType: info.TaskType, RangeID: info.RangeID, DomainName: domainName, }) } // retry only on service busy errors throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryForeverPolicy), backoff.WithRetryableError(func(err error) bool { _, ok := err.(*types.ServiceBusyError) return ok }), ) return throttleRetry.Do(s.ctx, op) } func (s *Scavenger) retryForever(op func(ctx context.Context) error) error { throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(retryForeverPolicy), backoff.WithRetryableError(s.isRetryable), ) return throttleRetry.Do(s.ctx, op) } func newRetryForeverPolicy() backoff.RetryPolicy { policy := backoff.NewExponentialRetryPolicy(250 * time.Millisecond) policy.SetExpirationInterval(backoff.NoInterval) policy.SetMaximumInterval(30 * time.Second) return policy } func (s *Scavenger) isRetryable(err error) bool { return s.Alive() } ================================================ FILE: service/worker/scanner/tasklist/handler.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "strings" "sync/atomic" "time" "github.com/uber/cadence/common/log/tag" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/wrappers/ratelimited" "github.com/uber/cadence/service/worker/scanner/executor" ) type handlerStatus = executor.TaskStatus const ( handlerStatusDone = executor.TaskStatusDone handlerStatusErr = executor.TaskStatusErr handlerStatusDefer = executor.TaskStatusDefer ) const scannerTaskListPrefix = "cadence-sys-tl-scanner" // deleteHandler handles deletions for a given task list // this handler limits the amount of tasks deleted to maxTasksPerJob // for fairness among all the task-list in the system - when there // is more work to do subsequently, this handler will return StatusDefer // with the assumption that the executor will schedule this task later // // Each loop of the handler proceeds as follows // - Retrieve the next batch of tasks sorted by task_id for this task-list from persistence // - If there are 0 tasks for this task-list, try deleting the task-list if its idle // - If any of the tasks in the batch isn't expired, we are done. Since tasks are retrieved // in sorted order, if one of the tasks isn't expired, chances are, none of the tasks above // it are expired as well - so, we give up and wait for the next run // - Delete the entire batch of tasks // - If the number of tasks retrieved is less than batchSize, there are no more tasks in the task-list // Try deleting the task-list if its idle func (s *Scavenger) deleteHandler(taskListInfo *p.TaskListInfo) handlerStatus { var err error var nProcessed, nDeleted int defer func() { s.deleteHandlerLog(taskListInfo, nProcessed, nDeleted, err) }() taskBatchSize := s.taskBatchSizeFn() maxTasksPerJob := s.maxTasksPerJobFn() for nProcessed < maxTasksPerJob { resp, err1 := s.getTasks(taskListInfo, taskBatchSize) if err1 != nil { err = err1 return handlerStatusErr } nTasks := len(resp.Tasks) if nTasks == 0 { s.tryDeleteTaskList(taskListInfo) return handlerStatusDone } for _, task := range resp.Tasks { nProcessed++ if !s.isTaskExpired(task) { return handlerStatusDone } } taskID := resp.Tasks[nTasks-1].TaskID if _, err = s.completeTasks(taskListInfo, taskID, nTasks); err != nil { return handlerStatusErr } nDeleted += nTasks if nTasks < taskBatchSize { s.tryDeleteTaskList(taskListInfo) return handlerStatusDone } } return handlerStatusDefer } func (s *Scavenger) tryDeleteTaskList(info *p.TaskListInfo) { if strings.HasPrefix(info.Name, scannerTaskListPrefix) { return // avoid deleting our own task list } delta := time.Since(info.LastUpdated) if delta < taskListGracePeriod { return } // usually, matching engine is the authoritative owner of a tasklist // and its incorrect for any other entity to mutate executorTask lists (including deleting it) // the delete here is safe because of two reasons: // - we delete the executorTask list only if the lastUpdated is > 48H. If a executorTask list is idle for // this amount of time, it will no longer be owned by any host in matching engine (because // of idle timeout). If any new host has to take ownership of this at this time, it can only // do so by updating the rangeID // - deleteTaskList is a conditional delete where condition is the rangeID if err := s.deleteTaskList(info); err != nil { s.logger.Error("deleteTaskList error", tag.Error(err)) return } atomic.AddInt64(&s.stats.tasklist.nDeleted, 1) s.logger.Info("tasklist deleted", tag.WorkflowDomainID(info.DomainID), tag.WorkflowTaskListName(info.Name), tag.TaskType(info.TaskType)) } func (s *Scavenger) deleteHandlerLog(info *p.TaskListInfo, nProcessed int, nDeleted int, err error) { atomic.AddInt64(&s.stats.task.nDeleted, int64(nDeleted)) atomic.AddInt64(&s.stats.task.nProcessed, int64(nProcessed)) if err != nil { s.logger.Error("scavenger.deleteHandler processed.", tag.Error(err), tag.WorkflowDomainID(info.DomainID), tag.WorkflowTaskListName(info.Name), tag.TaskType(info.TaskType), tag.NumberProcessed(nProcessed), tag.NumberDeleted(nDeleted)) return } if nProcessed > 0 { s.logger.Info("scavenger.deleteHandler processed.", tag.WorkflowDomainID(info.DomainID), tag.WorkflowTaskListName(info.Name), tag.TaskType(info.TaskType), tag.NumberProcessed(nProcessed), tag.NumberDeleted(nDeleted)) } } func (s *Scavenger) isTaskExpired(t *p.TaskInfo) bool { return t.Expiry.After(time.Unix(0, 0)) && time.Now().After(t.Expiry) } func (s *Scavenger) completeOrphanTasksHandler() handlerStatus { var nDeleted int batchSize := s.getOrphanTasksPageSizeFn() resp, err := s.getOrphanTasks(batchSize) if err == ratelimited.ErrPersistenceLimitExceeded { s.logger.Info("scavenger.completeOrphanTasksHandler query was ratelimited; will retry") return handlerStatusDefer } if err != nil { s.logger.Error("scavenger.completeOrphanTasksHandler error getting orphan tasks") return handlerStatusErr } for _, taskKey := range resp.Tasks { err = s.completeTask(&p.TaskListInfo{ DomainID: taskKey.DomainID, Name: taskKey.TaskListName, TaskType: taskKey.TaskType, }, taskKey.TaskID) if err == ratelimited.ErrPersistenceLimitExceeded { s.logger.Info("scavenger.completeOrphanTasksHandler query was ratelimited; will retry") return handlerStatusDefer } if err != nil { s.logger.Error("scavenger.completeOrphanTasksHandler error getting orphan tasks") return handlerStatusErr } nDeleted++ atomic.AddInt64(&s.stats.task.nDeleted, 1) atomic.AddInt64(&s.stats.task.nProcessed, 1) } s.logger.Info("scavenger.completeOrphanTasksHandler deleted.", tag.NumberDeleted(nDeleted)) if len(resp.Tasks) < batchSize { return handlerStatusDone } return handlerStatusDefer } ================================================ FILE: service/worker/scanner/tasklist/mocks_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "encoding/binary" "sync" "time" "github.com/pborman/uuid" p "github.com/uber/cadence/common/persistence" ) type ( mockTaskTable struct { domainID string workflowID string runID string nextTaskID int64 tasks []*p.TaskInfo } mockTaskListTable struct { sync.Mutex info []p.TaskListInfo } ) func newMockTaskTable() *mockTaskTable { return &mockTaskTable{ domainID: uuid.New(), workflowID: uuid.New(), runID: uuid.New(), } } func (tbl *mockTaskListTable) generate(name string, idle bool) { tl := p.TaskListInfo{ DomainID: uuid.New(), Name: name, RangeID: 22, LastUpdated: time.Now(), } if idle { tl.LastUpdated = time.Unix(1000, 1000) } tbl.info = append(tbl.info, tl) } func (tbl *mockTaskListTable) list(token []byte, count int) ([]p.TaskListInfo, []byte) { tbl.Lock() defer tbl.Unlock() if tbl.info == nil { return nil, nil } var off int if token != nil { off = int(binary.BigEndian.Uint32(token)) } rem := len(tbl.info) - count + 1 if rem < count { return tbl.info[off:], nil } token = make([]byte, 4) binary.BigEndian.PutUint32(token, uint32(off+count)) return tbl.info[off : off+count], token } func (tbl *mockTaskListTable) delete(name string) { tbl.Lock() defer tbl.Unlock() var newInfo []p.TaskListInfo for _, tl := range tbl.info { if tl.Name != name { newInfo = append(newInfo, tl) } } tbl.info = newInfo } func (tbl *mockTaskListTable) get(name string) *p.TaskListInfo { tbl.Lock() defer tbl.Unlock() for _, tl := range tbl.info { if tl.Name == name { return &tl } } return nil } func (tbl *mockTaskTable) generate(count int, expired bool) { for i := 0; i < count; i++ { ti := &p.TaskInfo{ DomainID: tbl.domainID, WorkflowID: tbl.workflowID, RunID: tbl.runID, TaskID: tbl.nextTaskID, ScheduleID: 3, ScheduleToStartTimeoutSeconds: 30, Expiry: time.Now().Add(time.Hour), } if expired { ti.ScheduleToStartTimeoutSeconds = -33 ti.Expiry = time.Unix(0, time.Now().UnixNano()-int64(time.Second*33)) } tbl.tasks = append(tbl.tasks, ti) tbl.nextTaskID++ } } func (tbl *mockTaskTable) get(count int) []*p.TaskInfo { if len(tbl.tasks) >= count { return tbl.tasks[:count] } return tbl.tasks[:] } func (tbl *mockTaskTable) deleteLessThan(id int64, limit int) int { count := 0 for _, t := range tbl.tasks { if t.TaskID <= id && count < limit { count++ continue } break } switch { case count == 0: case count == len(tbl.tasks): tbl.tasks = nil default: tbl.tasks = tbl.tasks[count:] } return count } ================================================ FILE: service/worker/scanner/tasklist/scavenger.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "context" "sync" "sync/atomic" "time" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/metrics" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/service/worker/scanner/executor" ) const ( executorMaxDeferredTasks = 10000 taskListBatchSize = 32 // maximum number of task list we process concurrently taskBatchSize = 16 taskListGracePeriod = 48 * time.Hour // amount of time a executorTask list has to be idle before it becomes a candidate for deletion ) type ( // Scavenger is the type that holds the state for task list scavenger daemon Scavenger struct { ctx context.Context db p.TaskManager cache cache.DomainCache executor executor.Executor scope metrics.Scope logger log.Logger stats stats status int32 getOrphanTasksPageSizeFn dynamicproperties.IntPropertyFn taskBatchSizeFn dynamicproperties.IntPropertyFn maxTasksPerJobFn dynamicproperties.IntPropertyFn cleanOrphans dynamicproperties.BoolPropertyFn pollInterval time.Duration timeSource clock.TimeSource // stopC is used to signal the scavenger to stop stopC chan struct{} // stopWG is used to wait for the scavenger to stop stopWG sync.WaitGroup // stoppedC is closed once the scavenger is stopped stopped chan struct{} } stats struct { tasklist struct { nProcessed int64 nDeleted int64 } task struct { nProcessed int64 nDeleted int64 } } // Options is used to customize scavenger operations Options struct { GetOrphanTasksPageSizeFn dynamicproperties.IntPropertyFn TaskBatchSizeFn dynamicproperties.IntPropertyFn EnableCleaning dynamicproperties.BoolPropertyFn MaxTasksPerJobFn dynamicproperties.IntPropertyFn ExecutorPollInterval time.Duration } // executorTask is a runnable task that adheres to the executor.Task interface // for the scavenger, each of this task processes a single task list executorTask struct { taskListInfo p.TaskListInfo scvg *Scavenger } // orphanExecutorTask is a runnable task that processes a limited block of // orphans orphanExecutorTask struct { scvg *Scavenger } ) // NewScavenger returns an instance of executorTask list scavenger daemon // The Scavenger can be started by calling the Start() method on the // returned object. Calling the Start() method will result in one // complete iteration over all of the task lists in the system. For // each task list, the scavenger will attempt // - deletion of expired tasks in the task lists // - deletion of task list itself, if there are no tasks and the task list hasn't been updated for a grace period // // The scavenger will retry on all persistence errors infinitely and will only stop under // two conditions // - either all task lists are processed successfully (or) // - Stop() method is called to stop the scavenger func NewScavenger( ctx context.Context, db p.TaskManager, metricsClient metrics.Client, logger log.Logger, opts *Options, cache cache.DomainCache, ) *Scavenger { taskExecutor := executor.NewFixedSizePoolExecutor( taskListBatchSize, executorMaxDeferredTasks, metricsClient, metrics.TaskListScavengerScope, ) if opts == nil { opts = &Options{} } cleanOrphans := opts.EnableCleaning if cleanOrphans == nil { cleanOrphans = func(opts ...dynamicproperties.FilterOption) bool { return false } } getOrphanTasksPageSize := opts.GetOrphanTasksPageSizeFn if getOrphanTasksPageSize == nil { getOrphanTasksPageSize = func(opts ...dynamicproperties.FilterOption) int { return dynamicproperties.ScannerGetOrphanTasksPageSize.DefaultInt() } } taskBatchSizeFn := opts.TaskBatchSizeFn if taskBatchSizeFn == nil { taskBatchSizeFn = func(opts ...dynamicproperties.FilterOption) int { return taskBatchSize } } maxTasksPerJobFn := opts.MaxTasksPerJobFn if maxTasksPerJobFn == nil { maxTasksPerJobFn = func(opts ...dynamicproperties.FilterOption) int { return dynamicproperties.ScannerMaxTasksProcessedPerTasklistJob.DefaultInt() } } pollInterval := opts.ExecutorPollInterval if pollInterval == 0 { pollInterval = time.Minute } return &Scavenger{ ctx: ctx, db: db, cache: cache, scope: metricsClient.Scope(metrics.TaskListScavengerScope), logger: logger, stopC: make(chan struct{}), stopped: make(chan struct{}), executor: taskExecutor, cleanOrphans: cleanOrphans, taskBatchSizeFn: taskBatchSizeFn, pollInterval: pollInterval, maxTasksPerJobFn: maxTasksPerJobFn, getOrphanTasksPageSizeFn: getOrphanTasksPageSize, timeSource: clock.NewRealTimeSource(), } } // Start starts the scavenger func (s *Scavenger) Start() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } s.logger.Info("Tasklist scavenger starting") s.stopWG.Add(1) s.executor.Start() go s.run() s.scope.IncCounter(metrics.StartedCount) s.logger.Info("Tasklist scavenger started") } // Stop stops the scavenger func (s *Scavenger) Stop() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } s.scope.IncCounter(metrics.StoppedCount) s.logger.Info("Tasklist scavenger stopping") close(s.stopC) s.executor.Stop() s.stopWG.Wait() s.logger.Info("Tasklist scavenger stopped") close(s.stopped) } // Alive returns true if the scavenger is still running func (s *Scavenger) Alive() bool { return atomic.LoadInt32(&s.status) == common.DaemonStatusStarted } // run does a single run over all executorTask lists func (s *Scavenger) run() { defer func() { s.emitStats() go s.Stop() s.stopWG.Done() }() // Start a task to delete orphaned tasks from the tasks table, if enabled if s.cleanOrphans() { s.executor.Submit(&orphanExecutorTask{scvg: s}) } var pageToken []byte for { resp, err := s.listTaskList(taskListBatchSize, pageToken) if err != nil { s.logger.Error("listTaskList error", tag.Error(err)) return } for _, item := range resp.Items { atomic.AddInt64(&s.stats.tasklist.nProcessed, 1) if !s.executor.Submit(s.newTask(&item)) { return } } pageToken = resp.NextPageToken if pageToken == nil { break } } s.awaitExecutor() } // process is a callback function that gets invoked from within the executor.Run() method func (s *Scavenger) process(taskListInfo *p.TaskListInfo) executor.TaskStatus { return s.deleteHandler(taskListInfo) } func (s *Scavenger) awaitExecutor() { outstanding := s.executor.TaskCount() ticker := s.timeSource.NewTicker(s.pollInterval) defer ticker.Stop() for outstanding > 0 { select { case <-ticker.Chan(): outstanding = s.executor.TaskCount() s.scope.UpdateGauge(metrics.TaskListOutstandingCount, float64(outstanding)) case <-s.stopC: return } } } func (s *Scavenger) emitStats() { s.scope.UpdateGauge(metrics.TaskProcessedCount, float64(s.stats.task.nProcessed)) s.scope.UpdateGauge(metrics.TaskDeletedCount, float64(s.stats.task.nDeleted)) s.scope.UpdateGauge(metrics.TaskListProcessedCount, float64(s.stats.tasklist.nProcessed)) s.scope.UpdateGauge(metrics.TaskListDeletedCount, float64(s.stats.tasklist.nDeleted)) } // newTask returns a new instance of an executable task which will process a single task list func (s *Scavenger) newTask(info *p.TaskListInfo) executor.Task { return &executorTask{ taskListInfo: *info, scvg: s, } } // Run runs the task func (t *executorTask) Run() executor.TaskStatus { return t.scvg.process(&t.taskListInfo) } func (t *orphanExecutorTask) Run() executor.TaskStatus { return t.scvg.completeOrphanTasksHandler() } ================================================ FILE: service/worker/scanner/tasklist/scavenger_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tasklist import ( "context" "errors" "fmt" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/uber-go/tally" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" p "github.com/uber/cadence/common/persistence" ) type ( ScavengerTestSuite struct { suite.Suite taskListTable *mockTaskListTable taskTables map[string]*mockTaskTable taskMgr *mocks.TaskManager scvgr *Scavenger scvgrCancelFn context.CancelFunc mockDomainCache *cache.MockDomainCache } ) const ( scavengerTestTimeout = 10 * time.Second ) var errTest = errors.New("transient error") func TestScavengerTestSuite(t *testing.T) { suite.Run(t, new(ScavengerTestSuite)) } func (s *ScavengerTestSuite) SetupTest() { s.taskMgr = &mocks.TaskManager{} s.taskListTable = &mockTaskListTable{} s.taskTables = make(map[string]*mockTaskTable) logger := testlogger.New(s.T()) ctrl := gomock.NewController(s.T()) s.mockDomainCache = cache.NewMockDomainCache(ctrl) scvgrCtx, scvgrCancelFn := context.WithTimeout(context.Background(), scavengerTestTimeout) s.scvgr = NewScavenger( scvgrCtx, s.taskMgr, metrics.NewClient(tally.NoopScope, metrics.Worker, metrics.HistogramMigration{}), logger, &Options{ EnableCleaning: dynamicproperties.GetBoolPropertyFn(true), TaskBatchSizeFn: dynamicproperties.GetIntPropertyFn(16), GetOrphanTasksPageSizeFn: dynamicproperties.GetIntPropertyFn(16), ExecutorPollInterval: time.Millisecond * 50, }, s.mockDomainCache, ) s.scvgrCancelFn = scvgrCancelFn } func (s *ScavengerTestSuite) TestAllExpiredTasks() { nTasks := 32 nTaskLists := 3 for i := 0; i < nTaskLists; i++ { name := fmt.Sprintf("test-expired-tl-%v", i) s.taskListTable.generate(name, true) tt := newMockTaskTable() tt.generate(nTasks, true) s.taskTables[name] = tt } s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test_domain_name", nil).AnyTimes() s.setupTaskMgrMocks() s.runScavenger() for tl, tbl := range s.taskTables { tasks := tbl.get(100) s.Equal(0, len(tasks), "failed to delete all expired tasks") s.Nil(s.taskListTable.get(tl), "failed to delete expired executorTask list") } } func (s *ScavengerTestSuite) TestAllAliveTasks() { nTasks := 32 nTaskLists := 3 for i := 0; i < nTaskLists; i++ { name := fmt.Sprintf("test-Alive-tl-%v", i) s.taskListTable.generate(name, true) tt := newMockTaskTable() tt.generate(nTasks, false) s.taskTables[name] = tt } s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test_domain_name", nil).AnyTimes() s.setupTaskMgrMocks() s.runScavenger() for tl, tbl := range s.taskTables { tasks := tbl.get(100) s.Equal(nTasks, len(tasks), "scavenger deleted a non-expired executorTask") s.NotNil(s.taskListTable.get(tl), "scavenger deleted a non-expired executorTask list") } } func (s *ScavengerTestSuite) TestExpiredTasksFollowedByAlive() { nTasks := 32 nTaskLists := 3 for i := 0; i < nTaskLists; i++ { name := fmt.Sprintf("test-Alive-tl-%v", i) s.taskListTable.generate(name, true) tt := newMockTaskTable() tt.generate(nTasks/2, true) tt.generate(nTasks/2, false) s.taskTables[name] = tt } s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test_domain_name", nil).AnyTimes() s.setupTaskMgrMocks() s.runScavenger() for tl, tbl := range s.taskTables { tasks := tbl.get(100) s.Equal(nTasks/2, len(tasks), "scavenger deleted non-expired tasks") s.Equal(int64(nTasks/2), tasks[0].TaskID, "scavenger deleted wrong set of tasks") s.NotNil(s.taskListTable.get(tl), "scavenger deleted a non-expired executorTask list") } } func (s *ScavengerTestSuite) TestAliveTasksFollowedByExpired() { nTasks := 32 nTaskLists := 3 for i := 0; i < nTaskLists; i++ { name := fmt.Sprintf("test-Alive-tl-%v", i) s.taskListTable.generate(name, true) tt := newMockTaskTable() tt.generate(nTasks/2, false) tt.generate(nTasks/2, true) s.taskTables[name] = tt } s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test_domain_name", nil).AnyTimes() s.setupTaskMgrMocks() s.runScavenger() for tl, tbl := range s.taskTables { tasks := tbl.get(100) s.Equal(nTasks, len(tasks), "scavenger deleted non-expired tasks") s.NotNil(s.taskListTable.get(tl), "scavenger deleted a non-expired executorTask list") } } func (s *ScavengerTestSuite) TestAllExpiredTasksWithErrors() { nTasks := 32 nTaskLists := 3 for i := 0; i < nTaskLists; i++ { name := fmt.Sprintf("test-expired-tl-%v", i) s.taskListTable.generate(name, true) tt := newMockTaskTable() tt.generate(nTasks, true) s.taskTables[name] = tt } s.mockDomainCache.EXPECT().GetDomainName(gomock.Any()).Return("test_domain_name", nil).AnyTimes() s.setupTaskMgrMocksWithErrors() s.runScavenger() for _, tbl := range s.taskTables { tasks := tbl.get(100) s.Equal(0, len(tasks), "failed to delete all expired tasks") } result, _ := s.taskListTable.list(nil, 10) s.Equal(1, len(result), "expected partial deletion due to transient errors") } func (s *ScavengerTestSuite) runScavenger() { s.scvgr.Start() defer s.scvgr.Stop() timer := time.NewTimer(scavengerTestTimeout) select { case <-s.scvgr.stopped: timer.Stop() return case <-timer.C: s.Fail("timed out waiting for scavenger to finish") } } func (s *ScavengerTestSuite) setupTaskMgrMocks() { s.taskMgr.On("ListTaskList", mock.Anything, mock.Anything).Return( func(_ context.Context, req *p.ListTaskListRequest) *p.ListTaskListResponse { items, next := s.taskListTable.list(req.PageToken, req.PageSize) return &p.ListTaskListResponse{Items: items, NextPageToken: next} }, nil) s.taskMgr.On("DeleteTaskList", mock.Anything, mock.Anything).Return( func(_ context.Context, req *p.DeleteTaskListRequest) error { s.taskListTable.delete(req.TaskListName) return nil }) s.taskMgr.On("GetTasks", mock.Anything, mock.Anything).Return( func(_ context.Context, req *p.GetTasksRequest) *p.GetTasksResponse { result := s.taskTables[req.TaskList].get(req.BatchSize) return &p.GetTasksResponse{Tasks: result} }, nil) s.taskMgr.On("CompleteTasksLessThan", mock.Anything, mock.Anything).Return( func(_ context.Context, req *p.CompleteTasksLessThanRequest) *p.CompleteTasksLessThanResponse { rowsDeleted := s.taskTables[req.TaskListName].deleteLessThan(req.TaskID, req.Limit) return &p.CompleteTasksLessThanResponse{TasksCompleted: rowsDeleted} }, nil) s.taskMgr.On("GetOrphanTasks", mock.Anything, mock.Anything).Return( func(_ context.Context, req *p.GetOrphanTasksRequest) *p.GetOrphanTasksResponse { return &p.GetOrphanTasksResponse{ Tasks: make([]*p.TaskKey, 0), } }, nil) } func (s *ScavengerTestSuite) setupTaskMgrMocksWithErrors() { s.taskMgr.On("ListTaskList", mock.Anything, mock.Anything).Return(nil, errTest).Once() s.taskMgr.On("GetTasks", mock.Anything, mock.Anything).Return(nil, errTest).Once() s.taskMgr.On("CompleteTasksLessThan", mock.Anything, mock.Anything).Return(nil, errTest).Once() s.taskMgr.On("DeleteTaskList", mock.Anything, mock.Anything).Return(errTest).Once() s.taskMgr.On("GetOrphanTasks", mock.Anything, mock.Anything).Return(nil, errTest).Once() s.setupTaskMgrMocks() } ================================================ FILE: service/worker/scanner/timers/timers.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package timers import ( "context" "strconv" "time" "go.uber.org/cadence/client" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/blobstore" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/pagination" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/fetcher" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/service/worker/scanner/shardscanner" ) const ( // ScannerWFTypeName defines workflow type name for concrete executions scanner ScannerWFTypeName = "cadence-sys-timers-scanner-workflow" wfid = "cadence-sys-timers-scanner" scannerTaskListName = "cadence-sys-timers-scanner-tasklist-0" // FixerWFTypeName defines workflow type name for timers fixer FixerWFTypeName = "cadence-sys-timers-fixer-workflow" fixerTaskListName = "cadence-sys-timers-fixer-tasklist-0" fixerwfid = "cadence-sys-timers-fixer" periodStartKey = "period_start" periodEndKey = "period_end" ) // ScannerWorkflow starts timers scanner. func ScannerWorkflow( ctx workflow.Context, params shardscanner.ScannerWorkflowParams, ) error { wf, err := shardscanner.NewScannerWorkflow(ctx, ScannerWFTypeName, params) if err != nil { return err } return wf.Start(ctx) } // FixerWorkflow starts timers executions fixer. func FixerWorkflow( ctx workflow.Context, params shardscanner.FixerWorkflowParams, ) error { wf, err := shardscanner.NewFixerWorkflow(ctx, FixerWFTypeName, params) if err != nil { return err } return wf.Start(ctx) } // ScannerHooks provides hooks for timers scanner. func ScannerHooks() *shardscanner.ScannerHooks { h, err := shardscanner.NewScannerHooks(Manager, Iterator, Config) if err != nil { return nil } return h } // FixerHooks provides hooks needed for timers fixer. func FixerHooks() *shardscanner.FixerHooks { h, err := shardscanner.NewFixerHooks(FixerManager, FixerIterator, timerCustomConfig) if err != nil { return nil } return h } func timerCustomConfig(_ shardscanner.FixerContext) shardscanner.CustomScannerConfig { // must be non-empty to pass backwards-compat check, // and this allows safely adding more invariants in the future. // // currently this is not read anywhere because "fixer enabled" // means "run this one invariant's fixes". return map[string]string{ invariant.TimerInvalidName: "true", } } // Manager provides invariant manager for timers scanner. func Manager( _ context.Context, pr persistence.Retryer, _ shardscanner.ScanShardActivityParams, cache cache.DomainCache, ) invariant.Manager { return invariant.NewInvariantManager(getInvariants(pr, cache)) } // Iterator provides iterator for timers scanner. func Iterator( ctx context.Context, pr persistence.Retryer, params shardscanner.ScanShardActivityParams, ) pagination.Iterator { start, err := strconv.Atoi(params.ScannerConfig[periodStartKey]) if err != nil { return nil } end, err := strconv.Atoi(params.ScannerConfig[periodEndKey]) if err != nil { return nil } startAt := time.Now().Add(time.Hour * time.Duration(start)) endAt := startAt.Add(time.Hour * time.Duration(end)) return fetcher.TimerIterator(ctx, pr, startAt, endAt, params.PageSize) } // FixerIterator provides iterator for timers fixer. func FixerIterator( ctx context.Context, client blobstore.Client, keys store.Keys, _ shardscanner.FixShardActivityParams, ) store.ScanOutputIterator { return store.NewBlobstoreIterator(ctx, client, keys, &entity.Timer{}) } // FixerManager provides invariant manager for timers fixer. func FixerManager( _ context.Context, pr persistence.Retryer, _ shardscanner.FixShardActivityParams, cache cache.DomainCache, ) invariant.Manager { return invariant.NewInvariantManager(getInvariants(pr, cache)) } // Config resolves dynamic config for timers scanner. func Config(ctx shardscanner.ScannerContext) shardscanner.CustomScannerConfig { res := shardscanner.CustomScannerConfig{} res[periodStartKey] = strconv.Itoa(ctx.Config.DynamicCollection.GetIntProperty(dynamicproperties.TimersScannerPeriodStart)()) res[periodEndKey] = strconv.Itoa(ctx.Config.DynamicCollection.GetIntProperty(dynamicproperties.TimersScannerPeriodEnd)()) return res } // ScannerConfig configures timers scanner func ScannerConfig(dc *dynamicconfig.Collection) *shardscanner.ScannerConfig { return &shardscanner.ScannerConfig{ ScannerWFTypeName: ScannerWFTypeName, FixerWFTypeName: FixerWFTypeName, DynamicParams: shardscanner.DynamicParams{ ScannerEnabled: dc.GetBoolProperty(dynamicproperties.TimersScannerEnabled), FixerEnabled: dc.GetBoolProperty(dynamicproperties.TimersFixerEnabled), Concurrency: dc.GetIntProperty(dynamicproperties.TimersScannerConcurrency), PageSize: dc.GetIntProperty(dynamicproperties.TimersScannerPersistencePageSize), BlobstoreFlushThreshold: dc.GetIntProperty(dynamicproperties.TimersScannerBlobstoreFlushThreshold), ActivityBatchSize: dc.GetIntProperty(dynamicproperties.TimersScannerActivityBatchSize), AllowDomain: dc.GetBoolPropertyFilteredByDomain(dynamicproperties.TimersFixerDomainAllow), }, DynamicCollection: dc, ScannerHooks: ScannerHooks, FixerHooks: FixerHooks, StartWorkflowOptions: client.StartWorkflowOptions{ ID: wfid, TaskList: scannerTaskListName, ExecutionStartToCloseTimeout: 20 * 365 * 24 * time.Hour, WorkflowIDReusePolicy: client.WorkflowIDReusePolicyAllowDuplicate, CronSchedule: "*/5 * * * *", }, StartFixerOptions: client.StartWorkflowOptions{ ID: fixerwfid, TaskList: fixerTaskListName, ExecutionStartToCloseTimeout: 20 * 365 * 24 * time.Hour, WorkflowIDReusePolicy: client.WorkflowIDReusePolicyAllowDuplicate, CronSchedule: "*/5 * * * *", }, } } func getInvariants(pr persistence.Retryer, cache cache.DomainCache) []invariant.Invariant { return []invariant.Invariant{ invariant.NewTimerInvalid(pr, cache), } } ================================================ FILE: service/worker/scanner/timers/timers_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package timers import ( "testing" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/workflow" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/service/worker/scanner/shardscanner" ) type timersWorkflowsSuite struct { suite.Suite testsuite.WorkflowTestSuite controller *gomock.Controller } func TestScannerWorkflowSuite(t *testing.T) { suite.Run(t, new(timersWorkflowsSuite)) } func (s *timersWorkflowsSuite) SetupSuite() { workflow.Register(ScannerWorkflow) s.controller = gomock.NewController(s.T()) } func (s *timersWorkflowsSuite) TestScannerWorkflow_SetsHooks() { dcClient := dynamicconfig.NewMockClient(s.controller) logger := log.NewNoop() dc := dynamicconfig.NewCollection(dcClient, logger) cfg := ScannerConfig(dc) s.Equal(ScannerWFTypeName, cfg.ScannerWFTypeName, "scanner wf type name is set") s.NotNil(cfg.FixerHooks) s.NotNil(cfg.ScannerHooks) } func (s *timersWorkflowsSuite) TestScannerWorkflow_Success() { env := s.NewTestWorkflowEnvironment() cconfig := shardscanner.CustomScannerConfig{ periodStartKey: "1", periodEndKey: "2", } env.OnActivity(shardscanner.ActivityScannerConfig, mock.Anything, mock.Anything).Return(shardscanner.ResolvedScannerWorkflowConfig{ GenericScannerConfig: shardscanner.GenericScannerConfig{ Enabled: true, Concurrency: 3, ActivityBatchSize: 5, }, CustomScannerConfig: cconfig, }, nil) env.OnActivity(shardscanner.ActivityScannerEmitMetrics, mock.Anything, mock.Anything).Return(nil) shards := shardscanner.Shards{ Range: &shardscanner.ShardRange{ Min: 0, Max: 30, }, } batches := [][]int{ {0, 3, 6, 9, 12}, {15, 18, 21, 24, 27}, {1, 4, 7, 10, 13}, {16, 19, 22, 25, 28}, {2, 5, 8, 11, 14}, {17, 20, 23, 26, 29}, } for _, batch := range batches { var reports []shardscanner.ScanReport for i := range batch { if i == 0 { reports = append(reports, shardscanner.ScanReport{ ShardID: batch[i], Stats: shardscanner.ScanStats{ EntitiesCount: 10, }, Result: shardscanner.ScanResult{ ControlFlowFailure: &shardscanner.ControlFlowFailure{ Info: "got control flow failure", }, }, }) } else { reports = append(reports, shardscanner.ScanReport{ ShardID: batch[i], Stats: shardscanner.ScanStats{ EntitiesCount: 10, CorruptedCount: 2, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{}, }, Result: shardscanner.ScanResult{ ShardScanKeys: &shardscanner.ScanKeys{ Corrupt: &store.Keys{ UUID: "test_uuid", MinPage: 0, MaxPage: 10, }, }, }, }) } } // var customc shardscanner.CustomScannerConfig env.OnActivity(shardscanner.ActivityScanShard, mock.Anything, shardscanner.ScanShardActivityParams{ Shards: batch, ScannerConfig: cconfig, }).Return(reports, nil) } env.ExecuteWorkflow(ScannerWorkflow, shardscanner.ScannerWorkflowParams{ Shards: shards, }) s.True(env.IsWorkflowCompleted()) s.NoError(env.GetWorkflowError()) aggValue, err := env.QueryWorkflow(shardscanner.AggregateReportQuery) s.NoError(err) var agg shardscanner.AggregateScanReportResult s.NoError(aggValue.Get(&agg)) s.Equal(shardscanner.AggregateScanReportResult{ EntitiesCount: 240, CorruptedCount: 48, CheckFailedCount: 24, CorruptionByType: map[invariant.Name]int64{}, }, agg) for i := 0; i < 30; i++ { shardReportValue, err := env.QueryWorkflow(shardscanner.ShardReportQuery, i) s.NoError(err) var shardReport *shardscanner.ScanReport s.NoError(shardReportValue.Get(&shardReport)) if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { s.Equal(&shardscanner.ScanReport{ ShardID: i, Stats: shardscanner.ScanStats{ EntitiesCount: 10, }, Result: shardscanner.ScanResult{ ControlFlowFailure: &shardscanner.ControlFlowFailure{ Info: "got control flow failure", }, }, }, shardReport) } else { s.Equal(&shardscanner.ScanReport{ ShardID: i, Stats: shardscanner.ScanStats{ EntitiesCount: 10, CorruptedCount: 2, CheckFailedCount: 1, CorruptionByType: map[invariant.Name]int64{}, }, Result: shardscanner.ScanResult{ ShardScanKeys: &shardscanner.ScanKeys{ Corrupt: &store.Keys{ UUID: "test_uuid", MinPage: 0, MaxPage: 10, }, }, }, }, shardReport) } } statusValue, err := env.QueryWorkflow(shardscanner.ShardStatusQuery, shardscanner.PaginatedShardQueryRequest{}) s.NoError(err) var status *shardscanner.ShardStatusQueryResult s.NoError(statusValue.Get(&status)) expected := make(map[int]shardscanner.ShardStatus) for i := 0; i < 30; i++ { if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { expected[i] = shardscanner.ShardStatusControlFlowFailure } else { expected[i] = shardscanner.ShardStatusSuccess } } s.Equal(shardscanner.ShardStatusResult(expected), status.Result) s.True(status.ShardQueryPaginationToken.IsDone) s.Nil(status.ShardQueryPaginationToken.NextShardID) // check for paginated query result statusValue, err = env.QueryWorkflow(shardscanner.ShardStatusQuery, shardscanner.PaginatedShardQueryRequest{ StartingShardID: common.IntPtr(5), LimitShards: common.IntPtr(10), }) s.NoError(err) status = &shardscanner.ShardStatusQueryResult{} s.NoError(statusValue.Get(&status)) expected = make(map[int]shardscanner.ShardStatus) for i := 5; i < 15; i++ { if i == 0 || i == 1 || i == 2 || i == 15 || i == 16 || i == 17 { expected[i] = shardscanner.ShardStatusControlFlowFailure } else { expected[i] = shardscanner.ShardStatusSuccess } } s.Equal(shardscanner.ShardStatusResult(expected), status.Result) s.False(status.ShardQueryPaginationToken.IsDone) s.Equal(15, *status.ShardQueryPaginationToken.NextShardID) corruptionKeysValue, err := env.QueryWorkflow(shardscanner.ShardCorruptKeysQuery, shardscanner.PaginatedShardQueryRequest{}) s.NoError(err) var shardCorruptKeysResult *shardscanner.ShardCorruptKeysQueryResult s.NoError(corruptionKeysValue.Get(&shardCorruptKeysResult)) expectedCorrupted := make(map[int]store.Keys) for i := 0; i < 30; i++ { if i != 0 && i != 1 && i != 2 && i != 15 && i != 16 && i != 17 { expectedCorrupted[i] = store.Keys{ UUID: "test_uuid", MinPage: 0, MaxPage: 10, } } } s.Equal(shardscanner.ShardCorruptKeysResult(expectedCorrupted), shardCorruptKeysResult.Result) } ================================================ FILE: service/worker/scanner/workflow.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scanner import ( "context" "time" "go.uber.org/cadence" "go.uber.org/cadence/activity" cclient "go.uber.org/cadence/client" "go.uber.org/cadence/workflow" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/service/worker/scanner/executions" "github.com/uber/cadence/service/worker/scanner/history" "github.com/uber/cadence/service/worker/scanner/tasklist" "github.com/uber/cadence/service/worker/scanner/timers" ) const ( maxConcurrentActivityExecutionSize = 10 maxConcurrentDecisionTaskExecutionSize = 10 infiniteDuration = 20 * 365 * 24 * time.Hour tlScannerWFID = "cadence-sys-tl-scanner" tlScannerWFTypeName = "cadence-sys-tl-scanner-workflow" tlScannerTaskListName = "cadence-sys-tl-scanner-tasklist-0" taskListScavengerActivityName = "cadence-sys-tl-scanner-scvg-activity" historyScannerWFID = "cadence-sys-history-scanner" historyScannerWFTypeName = "cadence-sys-history-scanner-workflow" historyScannerTaskListName = "cadence-sys-history-scanner-tasklist-0" historyScavengerActivityName = "cadence-sys-history-scanner-scvg-activity" ) var ( tlScavengerHBInterval = 10 * time.Second activityRetryPolicy = cadence.RetryPolicy{ InitialInterval: 10 * time.Second, BackoffCoefficient: 1.7, MaximumInterval: 5 * time.Minute, ExpirationInterval: infiniteDuration, } activityOptions = workflow.ActivityOptions{ ScheduleToStartTimeout: 5 * time.Minute, StartToCloseTimeout: infiniteDuration, HeartbeatTimeout: 5 * time.Minute, RetryPolicy: &activityRetryPolicy, } tlScannerWFStartOptions = cclient.StartWorkflowOptions{ ID: tlScannerWFID, TaskList: tlScannerTaskListName, ExecutionStartToCloseTimeout: 5 * 24 * time.Hour, WorkflowIDReusePolicy: cclient.WorkflowIDReusePolicyAllowDuplicate, CronSchedule: "0 */12 * * *", } historyScannerWFStartOptions = cclient.StartWorkflowOptions{ ID: historyScannerWFID, TaskList: historyScannerTaskListName, ExecutionStartToCloseTimeout: infiniteDuration, WorkflowIDReusePolicy: cclient.WorkflowIDReusePolicyAllowDuplicate, CronSchedule: "0 */12 * * *", } ) func init() { workflow.RegisterWithOptions(TaskListScannerWorkflow, workflow.RegisterOptions{Name: tlScannerWFTypeName}) activity.RegisterWithOptions(TaskListScavengerActivity, activity.RegisterOptions{Name: taskListScavengerActivityName}) workflow.RegisterWithOptions(HistoryScannerWorkflow, workflow.RegisterOptions{Name: historyScannerWFTypeName}) activity.RegisterWithOptions(HistoryScavengerActivity, activity.RegisterOptions{Name: historyScavengerActivityName}) workflow.RegisterWithOptions(executions.ConcreteScannerWorkflow, workflow.RegisterOptions{Name: executions.ConcreteExecutionsScannerWFTypeName}) workflow.RegisterWithOptions(executions.CurrentScannerWorkflow, workflow.RegisterOptions{Name: executions.CurrentExecutionsScannerWFTypeName}) workflow.RegisterWithOptions(executions.ConcreteFixerWorkflow, workflow.RegisterOptions{Name: executions.ConcreteExecutionsFixerWFTypeName}) workflow.RegisterWithOptions(executions.CurrentFixerWorkflow, workflow.RegisterOptions{Name: executions.CurrentExecutionsFixerWFTypeName}) workflow.RegisterWithOptions(timers.ScannerWorkflow, workflow.RegisterOptions{Name: timers.ScannerWFTypeName}) workflow.RegisterWithOptions(timers.FixerWorkflow, workflow.RegisterOptions{Name: timers.FixerWFTypeName}) } // TaskListScannerWorkflow is the workflow that runs the task-list scanner background daemon func TaskListScannerWorkflow( ctx workflow.Context, ) error { future := workflow.ExecuteActivity(workflow.WithActivityOptions(ctx, activityOptions), taskListScavengerActivityName) return future.Get(ctx, nil) } // HistoryScannerWorkflow is the workflow that runs the history scanner background daemon func HistoryScannerWorkflow( ctx workflow.Context, ) error { future := workflow.ExecuteActivity( workflow.WithActivityOptions(ctx, activityOptions), historyScavengerActivityName, ) return future.Get(ctx, nil) } // HistoryScavengerActivity is the activity that runs history scavenger func HistoryScavengerActivity( activityCtx context.Context, ) (history.ScavengerHeartbeatDetails, error) { ctx, err := getScannerContext(activityCtx) if err != nil { return history.ScavengerHeartbeatDetails{}, err } rps := ctx.cfg.ScannerPersistenceMaxQPS() res := ctx.resource hbd := history.ScavengerHeartbeatDetails{} if activity.HasHeartbeatDetails(activityCtx) { if err := activity.GetHeartbeatDetails(activityCtx, &hbd); err != nil { res.GetLogger().Error("Failed to recover from last heartbeat, start over from beginning", tag.Error(err)) } } cache := res.GetDomainCache() scavenger := history.NewScavenger( res.GetHistoryManager(), rps, res.GetHistoryClient(), hbd, res.GetMetricsClient(), res.GetLogger(), ctx.cfg.MaxWorkflowRetentionInDays, cache, ) return scavenger.Run(activityCtx) } // TaskListScavengerActivity is the activity that runs task list scavenger func TaskListScavengerActivity( activityCtx context.Context, ) error { ctx, err := getScannerContext(activityCtx) if err != nil { return err } res := ctx.resource scavenger := tasklist.NewScavenger( activityCtx, res.GetTaskManager(), res.GetMetricsClient(), res.GetLogger(), &ctx.cfg.TaskListScannerOptions, res.GetDomainCache(), ) res.GetLogger().Info("Starting task list scavenger") scavenger.Start() for scavenger.Alive() { activity.RecordHeartbeat(activityCtx) if activityCtx.Err() != nil { res.GetLogger().Info("activity context error, stopping scavenger", tag.Error(activityCtx.Err())) scavenger.Stop() return activityCtx.Err() } time.Sleep(tlScavengerHBInterval) } return nil } ================================================ FILE: service/worker/scanner/workflow_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scanner import ( "context" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/cadence/testsuite" "go.uber.org/cadence/worker" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/metrics" p "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/service/worker/scanner/tasklist" ) type scannerWorkflowTestSuite struct { suite.Suite testsuite.WorkflowTestSuite } func TestScannerWorkflowTestSuite(t *testing.T) { suite.Run(t, new(scannerWorkflowTestSuite)) } func (s *scannerWorkflowTestSuite) TestWorkflow() { env := s.NewTestWorkflowEnvironment() env.OnActivity(taskListScavengerActivityName, mock.Anything).Return(nil) env.ExecuteWorkflow(tlScannerWFTypeName) s.True(env.IsWorkflowCompleted()) } func (s *scannerWorkflowTestSuite) TestScavengerActivity() { env := s.NewTestActivityEnvironment() controller := gomock.NewController(s.T()) mockResource := resource.NewTest(s.T(), controller, metrics.Worker) defer mockResource.Finish(s.T()) mockResource.TaskMgr.On("ListTaskList", mock.Anything, mock.Anything).Return(&p.ListTaskListResponse{}, nil) mockResource.TaskMgr.On("GetOrphanTasks", mock.Anything, mock.Anything).Return(&p.GetOrphanTasksResponse{}, nil) ctx := scannerContext{ resource: mockResource, cfg: Config{ TaskListScannerOptions: tasklist.Options{ GetOrphanTasksPageSizeFn: dynamicproperties.GetIntPropertyFn(dynamicproperties.ScannerGetOrphanTasksPageSize.DefaultInt()), EnableCleaning: dynamicproperties.GetBoolPropertyFn(true), ExecutorPollInterval: time.Millisecond * 50, }, }, } env.SetTestTimeout(time.Second * 5) env.SetWorkerOptions(worker.Options{ BackgroundActivityContext: NewScannerContext(context.Background(), "default-test-workflow-type-name", ctx), }) tlScavengerHBInterval = time.Millisecond * 10 _, err := env.ExecuteActivity(taskListScavengerActivityName) s.NoError(err) } ================================================ FILE: service/worker/scheduler/activity.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scheduler import ( "context" "fmt" "time" "github.com/google/uuid" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/types" ) // schedulerRequestIDNamespace is a stable UUID namespace used to derive // deterministic RequestIDs. Cassandra's schema stores create_request_id as // a uuid column, so plain strings are rejected by the gocql driver. var schedulerRequestIDNamespace = uuid.NewSHA1(uuid.NameSpaceDNS, []byte("cadence.scheduler")) type contextKey string const schedulerContextKey contextKey = "schedulerContext" // schedulerContext is the context passed to activities via BackgroundActivityContext. type schedulerContext struct { FrontendClient frontend.Client } // startWorkflowActivity starts a workflow execution as specified by the schedule's action. // It generates a deterministic workflow ID from the schedule ID and scheduled time // to ensure idempotent execution. func startWorkflowActivity(ctx context.Context, req StartWorkflowRequest) (*StartWorkflowResult, error) { sc, ok := ctx.Value(schedulerContextKey).(schedulerContext) if !ok { return nil, fmt.Errorf("scheduler context not found in activity context") } workflowID := generateWorkflowID(req.Action.WorkflowIDPrefix, req.ScheduleID, req.ScheduledTime) reusePolicy := types.WorkflowIDReusePolicyAllowDuplicate startReq := &types.StartWorkflowExecutionRequest{ Domain: req.Domain, WorkflowID: workflowID, WorkflowType: req.Action.WorkflowType, TaskList: req.Action.TaskList, Input: req.Action.Input, ExecutionStartToCloseTimeoutSeconds: req.Action.ExecutionStartToCloseTimeoutSeconds, TaskStartToCloseTimeoutSeconds: req.Action.TaskStartToCloseTimeoutSeconds, RequestID: generateRequestID(req.ScheduleID, req.ScheduledTime.UnixNano(), req.TriggerSource), WorkflowIDReusePolicy: &reusePolicy, RetryPolicy: req.Action.RetryPolicy, Memo: req.Action.Memo, SearchAttributes: req.Action.SearchAttributes, } resp, err := sc.FrontendClient.StartWorkflowExecution(ctx, startReq) if err != nil { if isAlreadyStartedError(err) { return &StartWorkflowResult{ WorkflowID: workflowID, Skipped: true, }, nil } return nil, fmt.Errorf("failed to start workflow: %w", err) } return &StartWorkflowResult{ WorkflowID: workflowID, RunID: resp.GetRunID(), Started: true, }, nil } // generateWorkflowID creates a deterministic workflow ID from the // schedule's prefix (or schedule ID) and the scheduled time. // Example: "my-prefix-2026-01-15T10:00:00Z" func generateWorkflowID(prefix, scheduleID string, scheduledTime time.Time) string { if prefix == "" { prefix = scheduleID } return fmt.Sprintf("%s-%s", prefix, scheduledTime.UTC().Format(time.RFC3339)) } // generateRequestID produces a deterministic UUID from the schedule ID, // scheduled time, and trigger source. Including the trigger source ensures // that a backfill for the same timestamp as a normal schedule fire produces // a distinct RequestID, avoiding unintended server-side deduplication. func generateRequestID(scheduleID string, scheduledTimeNanos int64, source TriggerSource) string { name := fmt.Sprintf("%s-%d-%s", scheduleID, scheduledTimeNanos, source) return uuid.NewSHA1(schedulerRequestIDNamespace, []byte(name)).String() } func isAlreadyStartedError(err error) bool { _, ok := err.(*types.WorkflowExecutionAlreadyStartedError) return ok } ================================================ FILE: service/worker/scheduler/activity_test.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scheduler import ( "context" "errors" "testing" "time" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/types" ) func TestGenerateWorkflowID(t *testing.T) { ts := time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC) tests := []struct { name string prefix string scheduleID string time time.Time want string }{ { name: "uses prefix when provided", prefix: "my-workflow", scheduleID: "sched-123", time: ts, want: "my-workflow-2026-01-15T10:00:00Z", }, { name: "falls back to scheduleID when prefix is empty", prefix: "", scheduleID: "sched-456", time: ts, want: "sched-456-2026-01-15T10:00:00Z", }, { name: "deterministic for same inputs", prefix: "wf", scheduleID: "sched-789", time: ts, want: "wf-2026-01-15T10:00:00Z", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := generateWorkflowID(tc.prefix, tc.scheduleID, tc.time) assert.Equal(t, tc.want, got) }) } } func TestGenerateRequestID(t *testing.T) { t.Run("returns valid UUID", func(t *testing.T) { id := generateRequestID("sched-1", 1000000000, TriggerSourceSchedule) _, err := uuid.Parse(id) assert.NoError(t, err) }) t.Run("deterministic for same inputs", func(t *testing.T) { a := generateRequestID("sched-1", 1000000000, TriggerSourceSchedule) b := generateRequestID("sched-1", 1000000000, TriggerSourceSchedule) assert.Equal(t, a, b) }) t.Run("different for different scheduleID", func(t *testing.T) { a := generateRequestID("sched-1", 1000000000, TriggerSourceSchedule) b := generateRequestID("sched-2", 1000000000, TriggerSourceSchedule) assert.NotEqual(t, a, b) }) t.Run("different for different time", func(t *testing.T) { a := generateRequestID("sched-1", 1000000000, TriggerSourceSchedule) b := generateRequestID("sched-1", 2000000000, TriggerSourceSchedule) assert.NotEqual(t, a, b) }) t.Run("different for different trigger source", func(t *testing.T) { a := generateRequestID("sched-1", 1000000000, TriggerSourceSchedule) b := generateRequestID("sched-1", 1000000000, TriggerSourceBackfill) assert.NotEqual(t, a, b) }) } func TestIsAlreadyStartedError(t *testing.T) { tests := []struct { name string err error want bool }{ { name: "WorkflowExecutionAlreadyStartedError returns true", err: &types.WorkflowExecutionAlreadyStartedError{Message: "already started"}, want: true, }, { name: "other error returns false", err: errors.New("some other error"), want: false, }, { name: "nil error returns false", err: nil, want: false, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := isAlreadyStartedError(tc.err) assert.Equal(t, tc.want, got) }) } } func TestStartWorkflowActivity(t *testing.T) { scheduledTime := time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC) int32Ptr := func(v int32) *int32 { return &v } baseReq := StartWorkflowRequest{ Domain: "test-domain", ScheduleID: "sched-1", Action: types.StartWorkflowAction{ WorkflowType: &types.WorkflowType{Name: "my-workflow"}, TaskList: &types.TaskList{Name: "my-tasklist"}, Input: []byte(`{"key":"value"}`), WorkflowIDPrefix: "my-prefix", ExecutionStartToCloseTimeoutSeconds: int32Ptr(3600), TaskStartToCloseTimeoutSeconds: int32Ptr(60), }, ScheduledTime: scheduledTime, TriggerSource: TriggerSourceSchedule, } tests := []struct { name string req StartWorkflowRequest setupMock func(m *frontend.MockClient) wantResult *StartWorkflowResult wantErr bool }{ { name: "successful start", req: baseReq, setupMock: func(m *frontend.MockClient) { m.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, req *types.StartWorkflowExecutionRequest, _ ...interface{}) (*types.StartWorkflowExecutionResponse, error) { assert.Equal(t, "test-domain", req.Domain) assert.Equal(t, "my-prefix-"+formatTime(scheduledTime), req.WorkflowID) assert.Equal(t, "my-workflow", req.WorkflowType.Name) assert.Equal(t, "my-tasklist", req.TaskList.Name) _, uuidErr := uuid.Parse(req.RequestID) assert.NoError(t, uuidErr, "RequestID must be a valid UUID") assert.Equal(t, generateRequestID("sched-1", scheduledTime.UnixNano(), TriggerSourceSchedule), req.RequestID, "RequestID must be deterministic") return &types.StartWorkflowExecutionResponse{RunID: "run-abc"}, nil }) }, wantResult: &StartWorkflowResult{ WorkflowID: "my-prefix-" + formatTime(scheduledTime), RunID: "run-abc", Started: true, }, }, { name: "already started returns skipped", req: baseReq, setupMock: func(m *frontend.MockClient) { m.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()). Return(nil, &types.WorkflowExecutionAlreadyStartedError{Message: "already started"}) }, wantResult: &StartWorkflowResult{ WorkflowID: "my-prefix-" + formatTime(scheduledTime), Skipped: true, }, }, { name: "transient error propagated", req: baseReq, setupMock: func(m *frontend.MockClient) { m.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()). Return(nil, errors.New("connection refused")) }, wantErr: true, }, { name: "empty prefix falls back to scheduleID", req: func() StartWorkflowRequest { r := baseReq r.Action.WorkflowIDPrefix = "" return r }(), setupMock: func(m *frontend.MockClient) { m.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, req *types.StartWorkflowExecutionRequest, _ ...interface{}) (*types.StartWorkflowExecutionResponse, error) { assert.Equal(t, "sched-1-"+formatTime(scheduledTime), req.WorkflowID) return &types.StartWorkflowExecutionResponse{RunID: "run-def"}, nil }) }, wantResult: &StartWorkflowResult{ WorkflowID: "sched-1-" + formatTime(scheduledTime), RunID: "run-def", Started: true, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockClient := frontend.NewMockClient(ctrl) tc.setupMock(mockClient) ctx := context.WithValue(context.Background(), schedulerContextKey, schedulerContext{ FrontendClient: mockClient, }) result, err := startWorkflowActivity(ctx, tc.req) if tc.wantErr { require.Error(t, err) return } require.NoError(t, err) assert.Equal(t, tc.wantResult, result) }) } } func TestStartWorkflowActivity_MissingContext(t *testing.T) { ctx := context.Background() _, err := startWorkflowActivity(ctx, StartWorkflowRequest{}) require.Error(t, err) assert.Contains(t, err.Error(), "scheduler context not found") } func formatTime(t time.Time) string { return t.UTC().Format(time.RFC3339) } ================================================ FILE: service/worker/scheduler/client_worker.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scheduler import ( "context" "sync" "time" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" cadenceworker "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/service" ) const ( defaultRefreshInterval = 1 * time.Minute defaultShutdownTimeout = 5 * time.Second ) // BootstrapParams contains the parameters needed to create a scheduler worker manager. type BootstrapParams struct { ServiceClient workflowserviceclient.Interface FrontendClient frontend.Client Logger log.Logger DomainCache cache.DomainCache MembershipResolver membership.Resolver HostInfo membership.HostInfo } // workerHandle is the subset of cadenceworker.Worker used by the manager, // extracted to allow unit testing without starting real pollers. type workerHandle interface { Stop() } // workerFactory creates a worker for a given domain. Returns a workerHandle // and an error. The default factory creates a real Cadence SDK worker. type workerFactory func(domainName string) (workerHandle, error) // WorkerManager manages per-domain scheduler workers. It periodically scans // the domain cache and uses the membership hashring to determine which domains // this host owns. For each owned domain it starts a Cadence SDK worker polling // the scheduler task list in that domain. type WorkerManager struct { enabledFn dynamicproperties.BoolPropertyFn serviceClient workflowserviceclient.Interface frontendClient frontend.Client logger log.Logger domainCache cache.DomainCache membershipResolver membership.Resolver hostInfo membership.HostInfo timeSrc clock.TimeSource refreshInterval time.Duration shutdownTimeout time.Duration ctx context.Context cancelFn context.CancelFunc wg sync.WaitGroup activeWorkers map[string]workerHandle // domain name -> worker createWorker workerFactory } // NewWorkerManager creates a new per-domain scheduler worker manager. func NewWorkerManager(params *BootstrapParams, enabledFn dynamicproperties.BoolPropertyFn) *WorkerManager { ctx, cancel := context.WithCancel(context.Background()) wm := &WorkerManager{ enabledFn: enabledFn, serviceClient: params.ServiceClient, frontendClient: params.FrontendClient, logger: params.Logger.WithTags(tag.ComponentScheduler), domainCache: params.DomainCache, membershipResolver: params.MembershipResolver, hostInfo: params.HostInfo, timeSrc: clock.NewRealTimeSource(), refreshInterval: defaultRefreshInterval, shutdownTimeout: defaultShutdownTimeout, ctx: ctx, cancelFn: cancel, activeWorkers: make(map[string]workerHandle), } wm.createWorker = wm.defaultCreateWorker return wm } // Start begins the background loop that manages per-domain workers. func (m *WorkerManager) Start() { m.logger.Info("scheduler worker manager starting") m.wg.Add(1) go m.run() } // Stop signals the background loop to stop and waits for it to finish. // It then stops all active workers. func (m *WorkerManager) Stop() { m.logger.Info("scheduler worker manager stopping") m.cancelFn() if !common.AwaitWaitGroup(&m.wg, m.shutdownTimeout) { m.logger.Warn("scheduler worker manager timed out on shutdown") } m.stopAllWorkers() m.logger.Info("scheduler worker manager stopped") } func (m *WorkerManager) run() { defer m.wg.Done() ticker := m.timeSrc.NewTicker(m.refreshInterval) defer ticker.Stop() enabled := m.enabledFn() if enabled { m.refreshWorkers() } else { m.logger.Info("scheduler worker manager is disabled, skipping initial refresh") } for { select { case <-ticker.Chan(): previouslyEnabled := enabled enabled = m.enabledFn() if enabled != previouslyEnabled { m.logger.Info("scheduler worker manager enabled state changed", tag.Dynamic("enabled", enabled), ) } if enabled { m.refreshWorkers() } else { m.stopAllWorkers() } case <-m.ctx.Done(): m.logger.Info("scheduler worker manager background loop stopped") return } } } // refreshWorkers scans all domains and reconciles the set of active workers // with the domains this host owns via the membership hashring. func (m *WorkerManager) refreshWorkers() { domains := m.domainCache.GetAllDomain() ownedDomains := make(map[string]struct{}, len(domains)) lookupFailed := make(map[string]struct{}) for _, domainEntry := range domains { select { case <-m.ctx.Done(): return default: } if domainEntry.IsDeprecatedOrDeleted() { continue } domainName := domainEntry.GetInfo().Name owner, err := m.membershipResolver.Lookup(service.Worker, domainName) if err != nil { m.logger.Warn("failed to look up domain owner, skipping", tag.WorkflowDomainName(domainName), tag.Error(err), ) lookupFailed[domainName] = struct{}{} continue } if owner.Identity() != m.hostInfo.Identity() { continue } ownedDomains[domainName] = struct{}{} if _, exists := m.activeWorkers[domainName]; exists { continue } m.startWorkerForDomain(domainName) } for domainName, w := range m.activeWorkers { if _, owned := ownedDomains[domainName]; owned { continue } // Keep workers running for domains where lookup failed to avoid // unnecessary churn during transient membership ring issues. if _, failed := lookupFailed[domainName]; failed { continue } m.logger.Info("stopping scheduler worker for domain no longer owned", tag.WorkflowDomainName(domainName), ) w.Stop() delete(m.activeWorkers, domainName) } m.logger.Debug("scheduler workers refreshed", tag.Dynamic("active-worker-count", len(m.activeWorkers)), ) } func (m *WorkerManager) startWorkerForDomain(domainName string) { w, err := m.createWorker(domainName) if err != nil { m.logger.Error("failed to start scheduler worker for domain", tag.WorkflowDomainName(domainName), tag.Error(err), ) return } m.activeWorkers[domainName] = w m.logger.Info("started scheduler worker for domain", tag.WorkflowDomainName(domainName), ) } func (m *WorkerManager) defaultCreateWorker(domainName string) (workerHandle, error) { actCtx := context.WithValue(context.Background(), schedulerContextKey, schedulerContext{ FrontendClient: m.frontendClient, }) w := cadenceworker.New(m.serviceClient, domainName, TaskListName, cadenceworker.Options{ BackgroundActivityContext: actCtx, }) w.RegisterWorkflowWithOptions(SchedulerWorkflow, workflow.RegisterOptions{Name: WorkflowTypeName}) if err := w.Start(); err != nil { return nil, err } return w, nil } func (m *WorkerManager) stopAllWorkers() { for domainName, w := range m.activeWorkers { w.Stop() m.logger.Info("stopped scheduler worker for domain", tag.WorkflowDomainName(domainName), ) delete(m.activeWorkers, domainName) } } ================================================ FILE: service/worker/scheduler/client_worker_test.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scheduler import ( "context" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/testlogger" "github.com/uber/cadence/common/membership" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" ) func TestRefreshWorkers(t *testing.T) { selfHost := membership.NewDetailedHostInfo("10.0.0.1:7933", "self", nil) otherHost := membership.NewDetailedHostInfo("10.0.0.2:7933", "other", nil) makeDomainEntry := func(name string) *cache.DomainCacheEntry { return cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: name}, nil, false, nil, 0, nil, 0, 0, 0, ) } tests := []struct { name string domains map[string]*cache.DomainCacheEntry lookupResults map[string]membership.HostInfo lookupErrors map[string]error existingWorkers []string wantActiveWorkers []string wantStoppedWorkers []string wantStartedWorkers []string }{ { name: "starts workers for owned domains", domains: map[string]*cache.DomainCacheEntry{ "domain-a": makeDomainEntry("domain-a"), "domain-b": makeDomainEntry("domain-b"), }, lookupResults: map[string]membership.HostInfo{ "domain-a": selfHost, "domain-b": selfHost, }, wantActiveWorkers: []string{"domain-a", "domain-b"}, wantStartedWorkers: []string{"domain-a", "domain-b"}, }, { name: "skips domains owned by other hosts", domains: map[string]*cache.DomainCacheEntry{ "domain-a": makeDomainEntry("domain-a"), "domain-b": makeDomainEntry("domain-b"), }, lookupResults: map[string]membership.HostInfo{ "domain-a": selfHost, "domain-b": otherHost, }, wantActiveWorkers: []string{"domain-a"}, wantStartedWorkers: []string{"domain-a"}, }, { name: "stops workers for domains no longer owned", domains: map[string]*cache.DomainCacheEntry{ "domain-a": makeDomainEntry("domain-a"), }, lookupResults: map[string]membership.HostInfo{ "domain-a": otherHost, }, existingWorkers: []string{"domain-a"}, wantActiveWorkers: []string{}, wantStoppedWorkers: []string{"domain-a"}, }, { name: "stops workers for domains that disappeared from cache", domains: map[string]*cache.DomainCacheEntry{ "domain-b": makeDomainEntry("domain-b"), }, lookupResults: map[string]membership.HostInfo{ "domain-b": selfHost, }, existingWorkers: []string{"domain-a"}, wantActiveWorkers: []string{"domain-b"}, wantStoppedWorkers: []string{"domain-a"}, wantStartedWorkers: []string{"domain-b"}, }, { name: "no domains means no workers", domains: map[string]*cache.DomainCacheEntry{}, wantActiveWorkers: []string{}, }, { name: "lookup error skips domain without stopping existing worker", domains: map[string]*cache.DomainCacheEntry{ "domain-a": makeDomainEntry("domain-a"), }, lookupErrors: map[string]error{ "domain-a": fmt.Errorf("ring not ready"), }, existingWorkers: []string{"domain-a"}, wantActiveWorkers: []string{"domain-a"}, }, { name: "does not restart already running worker", domains: map[string]*cache.DomainCacheEntry{ "domain-a": makeDomainEntry("domain-a"), }, lookupResults: map[string]membership.HostInfo{ "domain-a": selfHost, }, existingWorkers: []string{"domain-a"}, wantActiveWorkers: []string{"domain-a"}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockDomainCache := cache.NewMockDomainCache(ctrl) mockDomainCache.EXPECT().GetAllDomain().Return(tc.domains) mockResolver := membership.NewMockResolver(ctrl) for domainName, host := range tc.lookupResults { mockResolver.EXPECT().Lookup(service.Worker, domainName).Return(host, nil) } for domainName, err := range tc.lookupErrors { mockResolver.EXPECT().Lookup(service.Worker, domainName).Return(membership.HostInfo{}, err) } stopped := make(map[string]bool) started := make(map[string]bool) ctx, cancel := context.WithCancel(context.Background()) defer cancel() wm := &WorkerManager{ enabledFn: dynamicproperties.GetBoolPropertyFn(true), logger: testlogger.New(t), domainCache: mockDomainCache, membershipResolver: mockResolver, hostInfo: selfHost, activeWorkers: make(map[string]workerHandle), ctx: ctx, createWorker: func(domainName string) (workerHandle, error) { started[domainName] = true return &fakeWorker{ stopFn: func() { stopped[domainName] = true }, }, nil }, } for _, d := range tc.existingWorkers { domain := d wm.activeWorkers[d] = &fakeWorker{ stopFn: func() { stopped[domain] = true }, } } wm.refreshWorkers() assert.Equal(t, len(tc.wantActiveWorkers), len(wm.activeWorkers), "active worker count mismatch") for _, d := range tc.wantActiveWorkers { _, exists := wm.activeWorkers[d] assert.True(t, exists, "expected active worker for domain %s", d) } for _, d := range tc.wantStoppedWorkers { assert.True(t, stopped[d], "expected worker for domain %s to be stopped", d) } for _, d := range tc.wantStartedWorkers { assert.True(t, started[d], "expected worker for domain %s to be started", d) } }) } } func TestRefreshWorkersHandlesCreateWorkerError(t *testing.T) { ctrl := gomock.NewController(t) selfHost := membership.NewDetailedHostInfo("10.0.0.1:7933", "self", nil) mockDomainCache := cache.NewMockDomainCache(ctrl) mockDomainCache.EXPECT().GetAllDomain().Return(map[string]*cache.DomainCacheEntry{ "domain-a": cache.NewDomainCacheEntryForTest( &persistence.DomainInfo{Name: "domain-a"}, nil, false, nil, 0, nil, 0, 0, 0, ), }) mockResolver := membership.NewMockResolver(ctrl) mockResolver.EXPECT().Lookup(service.Worker, "domain-a").Return(selfHost, nil) ctx, cancel := context.WithCancel(context.Background()) defer cancel() wm := &WorkerManager{ enabledFn: dynamicproperties.GetBoolPropertyFn(true), logger: testlogger.New(t), domainCache: mockDomainCache, membershipResolver: mockResolver, hostInfo: selfHost, activeWorkers: make(map[string]workerHandle), ctx: ctx, createWorker: func(domainName string) (workerHandle, error) { return nil, fmt.Errorf("connection refused") }, } wm.refreshWorkers() assert.Empty(t, wm.activeWorkers, "worker should not be added on creation error") } func TestStopAllWorkers(t *testing.T) { wm := &WorkerManager{ logger: testlogger.New(t), activeWorkers: make(map[string]workerHandle), } stoppedDomains := make(map[string]bool) for _, d := range []string{"domain-a", "domain-b", "domain-c"} { domain := d wm.activeWorkers[d] = &fakeWorker{ stopFn: func() { stoppedDomains[domain] = true }, } } wm.stopAllWorkers() require.Empty(t, wm.activeWorkers) assert.True(t, stoppedDomains["domain-a"]) assert.True(t, stoppedDomains["domain-b"]) assert.True(t, stoppedDomains["domain-c"]) } type fakeWorker struct { stopFn func() } func (f *fakeWorker) Stop() { if f.stopFn != nil { f.stopFn() } } ================================================ FILE: service/worker/scheduler/types.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scheduler import ( "time" "github.com/uber/cadence/common/types" ) const ( WorkflowTypeName = "cadence-scheduler" TaskListName = "cadence-scheduler" SignalNamePause = "scheduler-pause" SignalNameUnpause = "scheduler-unpause" SignalNameUpdate = "scheduler-update" SignalNameBackfill = "scheduler-backfill" SignalNameDelete = "scheduler-delete" QueryTypeDescribe = "scheduler-describe" maxIterationsBeforeContinueAsNew = 500 maxCatchUpFiresPerExecution = 10 maxBackfillFiresPerExecution = 10 maxPendingBackfills = 10 localActivityScheduleToCloseTimeout = 60 * time.Second localActivityMaxRetries = 3 localActivityRetryInitialInterval = time.Second localActivityRetryMaxInterval = 10 * time.Second ) // SchedulerWorkflowInput is the input to the scheduler workflow. // It carries the schedule definition and any prior state (for ContinueAsNew). type SchedulerWorkflowInput struct { Domain string `json:"domain"` ScheduleID string `json:"scheduleId"` Spec types.ScheduleSpec `json:"spec"` Action types.ScheduleAction `json:"action"` Policies types.SchedulePolicies `json:"policies"` State SchedulerWorkflowState `json:"state"` } // SchedulerWorkflowState is the mutable runtime state that survives ContinueAsNew. type SchedulerWorkflowState struct { Paused bool `json:"paused"` PauseReason string `json:"pauseReason,omitempty"` PausedBy string `json:"pausedBy,omitempty"` Deleted bool `json:"-"` // transient flag, not persisted across ContinueAsNew LastRunTime time.Time `json:"lastRunTime,omitempty"` // last time a workflow was actually started LastProcessedTime time.Time `json:"lastProcessedTime,omitempty"` // catch-up watermark: latest missed fire we've processed (fired or skipped) NextRunTime time.Time `json:"nextRunTime,omitempty"` TotalRuns int64 `json:"totalRuns"` MissedRuns int64 `json:"missedRuns"` SkippedRuns int64 `json:"skippedRuns"` Iterations int `json:"iterations"` BufferedRuns int `json:"bufferedRuns"` PendingBackfills []BackfillRequest `json:"pendingBackfills,omitempty"` } // BackfillRequest is a queued backfill that persists across ContinueAsNew. type BackfillRequest struct { StartTime time.Time `json:"startTime"` EndTime time.Time `json:"endTime"` OverlapPolicy types.ScheduleOverlapPolicy `json:"overlapPolicy"` BackfillID string `json:"backfillId,omitempty"` } // PauseSignal is the payload sent with a pause signal. type PauseSignal struct { Reason string `json:"reason,omitempty"` PausedBy string `json:"pausedBy,omitempty"` } // UnpauseSignal is the payload sent with an unpause signal. type UnpauseSignal struct { Reason string `json:"reason,omitempty"` CatchUpPolicy types.ScheduleCatchUpPolicy `json:"catchUpPolicy,omitempty"` } // UpdateSignal is the payload sent with an update signal. type UpdateSignal struct { Spec *types.ScheduleSpec `json:"spec,omitempty"` Action *types.ScheduleAction `json:"action,omitempty"` Policies *types.SchedulePolicies `json:"policies,omitempty"` } // BackfillSignal is the payload sent with a backfill signal. type BackfillSignal struct { StartTime time.Time `json:"startTime"` EndTime time.Time `json:"endTime"` OverlapPolicy types.ScheduleOverlapPolicy `json:"overlapPolicy"` BackfillID string `json:"backfillId,omitempty"` } // ScheduleDescription is the query result returned by the describe query handler. // It provides a snapshot of the schedule's current configuration and runtime state. type ScheduleDescription struct { ScheduleID string `json:"scheduleId"` Domain string `json:"domain"` Spec types.ScheduleSpec `json:"spec"` Action types.ScheduleAction `json:"action"` Policies types.SchedulePolicies `json:"policies"` Paused bool `json:"paused"` PauseReason string `json:"pauseReason,omitempty"` PausedBy string `json:"pausedBy,omitempty"` LastRunTime time.Time `json:"lastRunTime,omitempty"` NextRunTime time.Time `json:"nextRunTime,omitempty"` TotalRuns int64 `json:"totalRuns"` MissedRuns int64 `json:"missedRuns"` SkippedRuns int64 `json:"skippedRuns"` } // TriggerSource identifies what caused a schedule fire, used to differentiate // RequestIDs so that e.g. a backfill for the same timestamp as a normal fire // does not collide in server-side deduplication. type TriggerSource string const ( TriggerSourceSchedule TriggerSource = "schedule" TriggerSourceBackfill TriggerSource = "backfill" ) // StartWorkflowRequest is the input to the start-workflow activity. type StartWorkflowRequest struct { Domain string `json:"domain"` ScheduleID string `json:"scheduleId"` Action types.StartWorkflowAction `json:"action"` ScheduledTime time.Time `json:"scheduledTime"` TriggerSource TriggerSource `json:"triggerSource"` } // StartWorkflowResult is the output of the start-workflow activity. type StartWorkflowResult struct { WorkflowID string `json:"workflowId"` RunID string `json:"runId"` Started bool `json:"started"` Skipped bool `json:"skipped"` } ================================================ FILE: service/worker/scheduler/workflow.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scheduler import ( "fmt" "time" "github.com/robfig/cron/v3" "go.uber.org/cadence/workflow" "go.uber.org/zap" "github.com/uber/cadence/common/types" ) // signalChannels bundles the signal channels used by the scheduler workflow type signalChannels struct { pause workflow.Channel unpause workflow.Channel update workflow.Channel backfill workflow.Channel delete workflow.Channel } // SchedulerWorkflow is a long-running workflow that manages a single schedule. // It computes the next fire time from the cron expression, waits via a timer, // and dispatches the configured action. Signals control pause/unpause, update, // backfill, and deletion. // // The main loop follows a state-machine pattern: all inputs (signals and timer) // uniformly mutate state, and then a single decision point inspects the resulting // state to determine what to do next. ContinueAsNew is triggered on any // state-changing signal (pause, unpause, update) so the new execution's input // is always the authoritative source of truth. func SchedulerWorkflow(ctx workflow.Context, input SchedulerWorkflowInput) error { logger := workflow.GetLogger(ctx) logger.Info("scheduler workflow started", zap.String("domain", input.Domain), zap.String("scheduleId", input.ScheduleID), zap.Bool("paused", input.State.Paused), ) state := &input.State err := workflow.SetQueryHandler(ctx, QueryTypeDescribe, func() (*ScheduleDescription, error) { return buildScheduleDescription(&input, state), nil }) if err != nil { return fmt.Errorf("failed to register query handler: %w", err) } chs := signalChannels{ pause: workflow.GetSignalChannel(ctx, SignalNamePause), unpause: workflow.GetSignalChannel(ctx, SignalNameUnpause), update: workflow.GetSignalChannel(ctx, SignalNameUpdate), backfill: workflow.GetSignalChannel(ctx, SignalNameBackfill), delete: workflow.GetSignalChannel(ctx, SignalNameDelete), } sched, err := cron.ParseStandard(input.Spec.CronExpression) if err != nil { logger.Error("invalid cron expression, terminating", zap.String("cron", input.Spec.CronExpression), zap.Error(err)) return fmt.Errorf("invalid cron expression %q: %w", input.Spec.CronExpression, err) } // On the first iteration (after ContinueAsNew or fresh start), check for // fires that were missed during the transition gap or prior pause period. // Subsequent iterations don't need this because the timer handles fire times. // If more missed fires remain beyond the per-execution cap, ContinueAsNew // immediately so each batch runs in its own decision task. if moreMissed := processMissedRuns(ctx, logger, sched, &input, state); moreMissed { return safeContinueAsNew(ctx, logger, chs.delete, input, state) } // Process any pending backfill requests carried over from a previous execution. if moreBackfills := processBackfills(ctx, logger, sched, &input, state); moreBackfills { return safeContinueAsNew(ctx, logger, chs.delete, input, state) } for { state.Iterations++ // Set up timer only when not paused. When paused, applyAllInputs // blocks on signals alone until an unpause or delete arrives. var timerFuture workflow.Future var timerCancel func() if !state.Paused { now := workflow.Now(ctx) nextRun := computeNextRunTime(sched, now, input.Spec) if nextRun.IsZero() { logger.Info("schedule has no more runs (past end time), completing") return nil } state.NextRunTime = nextRun dur := nextRun.Sub(now) if dur < 0 { dur = 0 } var timerCtx workflow.Context timerCtx, timerCancel = workflow.WithCancel(ctx) timerFuture = workflow.NewTimer(timerCtx, dur) } changed, timerFired := applyAllInputs(ctx, logger, timerFuture, chs, state, &input) if timerCancel != nil { timerCancel() } if state.Deleted { logger.Info("schedule deleted") return nil } if timerFired && !state.Paused { processScheduleFire(ctx, logger, &input, state, state.NextRunTime, TriggerSourceSchedule) } if changed || state.Iterations >= maxIterationsBeforeContinueAsNew { return safeContinueAsNew(ctx, logger, chs.delete, input, state) } } } // applyAllInputs blocks until at least one input (signal or timer) arrives, // processes it, then drains any remaining buffered signals. // Signals and the timer are treated uniformly: each mutates state without // triggering side effects (no timer cancellation, no ContinueAsNew). // Returns (stateChanged, timerFired): stateChanged is true if a state-changing // signal (pause, unpause, update) was received; timerFired is true if the timer // completed successfully. func applyAllInputs( ctx workflow.Context, logger *zap.Logger, timerFuture workflow.Future, chs signalChannels, state *SchedulerWorkflowState, input *SchedulerWorkflowInput, ) (bool, bool) { selector := workflow.NewSelector(ctx) stateChanged := false timerFired := false if timerFuture != nil { selector.AddFuture(timerFuture, func(f workflow.Future) { if f.Get(ctx, nil) != nil { return } timerFired = true }) } selector.AddReceive(chs.pause, func(c workflow.Channel, more bool) { var sig PauseSignal c.Receive(ctx, &sig) if handlePause(logger, sig, state) { stateChanged = true } }) selector.AddReceive(chs.unpause, func(c workflow.Channel, more bool) { var sig UnpauseSignal c.Receive(ctx, &sig) if handleUnpause(logger, sig, state) { stateChanged = true } }) selector.AddReceive(chs.update, func(c workflow.Channel, more bool) { var sig UpdateSignal c.Receive(ctx, &sig) if handleUpdate(logger, sig, input, state) { stateChanged = true } }) selector.AddReceive(chs.backfill, func(c workflow.Channel, more bool) { var sig BackfillSignal c.Receive(ctx, &sig) if handleBackfill(logger, sig, state) { stateChanged = true } }) selector.AddReceive(chs.delete, func(c workflow.Channel, more bool) { c.Receive(ctx, nil) state.Deleted = true }) selector.Select(ctx) if drainBufferedSignals(logger, chs, state, input) { stateChanged = true } return stateChanged, timerFired } // drainBufferedSignals processes any remaining buffered signals without blocking. // Delete signals are checked first to prevent signal loss across ContinueAsNew boundaries. // Returns true if a state-changing signal was found. func drainBufferedSignals( logger *zap.Logger, chs signalChannels, state *SchedulerWorkflowState, input *SchedulerWorkflowInput, ) bool { if chs.delete.ReceiveAsync(nil) { state.Deleted = true return false } stateChanged := false for { var sig PauseSignal if !chs.pause.ReceiveAsync(&sig) { break } if handlePause(logger, sig, state) { stateChanged = true } } for { var sig UnpauseSignal if !chs.unpause.ReceiveAsync(&sig) { break } if handleUnpause(logger, sig, state) { stateChanged = true } } for { var sig UpdateSignal if !chs.update.ReceiveAsync(&sig) { break } if handleUpdate(logger, sig, input, state) { stateChanged = true } } for { var sig BackfillSignal if !chs.backfill.ReceiveAsync(&sig) { break } if handleBackfill(logger, sig, state) { stateChanged = true } } return stateChanged } func handlePause(logger *zap.Logger, sig PauseSignal, state *SchedulerWorkflowState) bool { state.Paused = true state.PauseReason = sig.Reason state.PausedBy = sig.PausedBy logger.Info("schedule paused", zap.String("reason", sig.Reason), zap.String("pausedBy", sig.PausedBy)) return true } func handleUnpause(logger *zap.Logger, sig UnpauseSignal, state *SchedulerWorkflowState) bool { if !state.Paused { logger.Info("ignoring unpause signal, schedule is not paused") return false } state.Paused = false state.PauseReason = "" state.PausedBy = "" logger.Info("schedule unpaused", zap.String("reason", sig.Reason), zap.String("catchUpPolicy", sig.CatchUpPolicy.String())) return true } func handleUpdate(logger *zap.Logger, sig UpdateSignal, input *SchedulerWorkflowInput, state *SchedulerWorkflowState) bool { if sig.Spec == nil && sig.Action == nil && sig.Policies == nil { logger.Info("ignoring empty update signal") return false } changed := false if sig.Spec != nil { if _, err := cron.ParseStandard(sig.Spec.CronExpression); err != nil { logger.Error("ignoring update with invalid cron expression", zap.String("cron", sig.Spec.CronExpression), zap.Error(err)) } else { input.Spec = *sig.Spec changed = true if len(state.PendingBackfills) > 0 { logger.Warn("spec change cleared pending backfills", zap.Int("clearedCount", len(state.PendingBackfills))) state.PendingBackfills = nil } } } if sig.Action != nil { input.Action = *sig.Action changed = true } if sig.Policies != nil { input.Policies = *sig.Policies changed = true } if changed { logger.Info("schedule updated") } return changed } func handleBackfill(logger *zap.Logger, sig BackfillSignal, state *SchedulerWorkflowState) bool { if !sig.EndTime.After(sig.StartTime) { logger.Warn("ignoring backfill with invalid time range", zap.Time("startTime", sig.StartTime), zap.Time("endTime", sig.EndTime), ) return false } if len(state.PendingBackfills) >= maxPendingBackfills { logger.Warn("ignoring backfill: pending backfill queue is full", zap.String("backfillId", sig.BackfillID), zap.Int("queueSize", len(state.PendingBackfills)), zap.Int("maxPendingBackfills", maxPendingBackfills), ) return false } for _, existing := range state.PendingBackfills { if sig.StartTime.Before(existing.EndTime) && sig.EndTime.After(existing.StartTime) { logger.Warn("backfill window overlaps with pending backfill, fires for overlapping times will be deduplicated", zap.String("newBackfillId", sig.BackfillID), zap.String("existingBackfillId", existing.BackfillID), zap.Time("overlapStart", maxTime(sig.StartTime, existing.StartTime)), zap.Time("overlapEnd", minTime(sig.EndTime, existing.EndTime)), ) } } state.PendingBackfills = append(state.PendingBackfills, BackfillRequest{ StartTime: sig.StartTime, EndTime: sig.EndTime, OverlapPolicy: sig.OverlapPolicy, BackfillID: sig.BackfillID, }) logger.Info("backfill queued", zap.Time("startTime", sig.StartTime), zap.Time("endTime", sig.EndTime), zap.String("overlapPolicy", sig.OverlapPolicy.String()), zap.String("backfillId", sig.BackfillID), zap.Int("pendingCount", len(state.PendingBackfills)), ) return true } // processScheduleFire executes the configured action for a single schedule fire. // It calls the start-workflow activity, updates state counters, and logs the outcome. // Activity failures do not terminate the schedule, they are logged and counted as missed runs. func processScheduleFire(ctx workflow.Context, logger *zap.Logger, input *SchedulerWorkflowInput, state *SchedulerWorkflowState, scheduledTime time.Time, trigger TriggerSource) { state.LastRunTime = scheduledTime state.TotalRuns++ logger.Info("schedule fired", zap.Time("scheduledTime", scheduledTime), zap.Int64("totalRuns", state.TotalRuns), ) activityOpts := workflow.LocalActivityOptions{ ScheduleToCloseTimeout: localActivityScheduleToCloseTimeout, RetryPolicy: &workflow.RetryPolicy{ InitialInterval: localActivityRetryInitialInterval, MaximumInterval: localActivityRetryMaxInterval, MaximumAttempts: localActivityMaxRetries, BackoffCoefficient: 2, }, } actCtx := workflow.WithLocalActivityOptions(ctx, activityOpts) if input.Action.StartWorkflow == nil { state.MissedRuns++ logger.Error("schedule action has no StartWorkflow configuration") return } req := StartWorkflowRequest{ Domain: input.Domain, ScheduleID: input.ScheduleID, Action: *input.Action.StartWorkflow, ScheduledTime: scheduledTime, TriggerSource: trigger, } var result StartWorkflowResult err := workflow.ExecuteLocalActivity(actCtx, startWorkflowActivity, req).Get(ctx, &result) if err != nil { state.MissedRuns++ logger.Error("scheduled action failed", zap.Time("scheduledTime", scheduledTime), zap.Error(err), ) return } if result.Skipped { state.SkippedRuns++ logger.Info("scheduled action skipped (already running)", zap.String("workflowId", result.WorkflowID), ) return } logger.Info("scheduled workflow started", zap.String("workflowId", result.WorkflowID), zap.String("runId", result.RunID), ) } // computeNextRunTime determines the next fire time for the cron schedule, // respecting the spec's StartTime and EndTime boundaries. func computeNextRunTime(sched cron.Schedule, now time.Time, spec types.ScheduleSpec) time.Time { if !spec.StartTime.IsZero() && now.Before(spec.StartTime) { now = spec.StartTime.Add(-time.Second) } next := sched.Next(now) if !spec.EndTime.IsZero() && next.After(spec.EndTime) { return time.Time{} } return next } // missedFiresResult holds the output of computeMissedFireTimes. type missedFiresResult struct { times []time.Time truncated bool // true if the result was capped at maxCatchUpFires } // computeMissedFireTimes returns all cron fire times between (lastRun, now]. // It caps the result at maxCatchUpFires to prevent unbounded iteration // for very frequent schedules that were paused for a long time. // The truncated flag signals that more fires exist beyond the cap. func computeMissedFireTimes(sched cron.Schedule, lastRun, now time.Time, spec types.ScheduleSpec) missedFiresResult { const maxCatchUpFires = 1000 var missed []time.Time t := lastRun for len(missed) < maxCatchUpFires { next := computeNextRunTime(sched, t, spec) if next.IsZero() || next.After(now) { return missedFiresResult{times: missed, truncated: false} } missed = append(missed, next) t = next } return missedFiresResult{times: missed, truncated: true} } // missedRunPolicyResult is the output of applyMissedRunPolicy. type missedRunPolicyResult struct { toFire []time.Time // fire times to execute, in order skipped int64 // fires to count as skipped } // applyMissedRunPolicy is a pure function that determines which missed fires // to execute and how many to skip, given the catch-up policy and window. // It is separated from processMissedRuns to allow direct unit testing. func applyMissedRunPolicy(policy types.ScheduleCatchUpPolicy, window time.Duration, missed []time.Time, now time.Time, logger *zap.Logger) missedRunPolicyResult { var eligible []time.Time for _, t := range missed { if window <= 0 || now.Sub(t) <= window { eligible = append(eligible, t) } } outOfWindow := int64(len(missed) - len(eligible)) switch policy { case types.ScheduleCatchUpPolicyOne: if len(eligible) == 0 { return missedRunPolicyResult{skipped: int64(len(missed))} } return missedRunPolicyResult{ toFire: []time.Time{eligible[len(eligible)-1]}, skipped: outOfWindow + int64(len(eligible)-1), } case types.ScheduleCatchUpPolicyAll: return missedRunPolicyResult{ toFire: eligible, skipped: outOfWindow, } case types.ScheduleCatchUpPolicySkip: return missedRunPolicyResult{skipped: int64(len(missed))} default: logger.Warn("unknown catch-up policy, defaulting to skip", zap.Int32("policy", int32(policy)), ) return missedRunPolicyResult{skipped: int64(len(missed))} } } // processMissedRuns checks for and processes any cron fires that were missed // while the schedule was paused or during ContinueAsNew transitions. // The catch-up policy determines how missed fires are handled: // - Skip: all missed fires are counted as skipped // - One: only the most recent eligible fire (within CatchUpWindow) is executed // - All: all eligible fires within the CatchUpWindow are executed // // To avoid exceeding the decision task timeout, at most maxCatchUpFiresPerExecution // fires are executed per workflow execution. Returns true if there are more missed // fires remaining, signalling the caller to ContinueAsNew for the next batch. func processMissedRuns(ctx workflow.Context, logger *zap.Logger, sched cron.Schedule, input *SchedulerWorkflowInput, state *SchedulerWorkflowState) bool { // Use LastProcessedTime as the catch-up watermark; fall back to // LastRunTime for schedules created before this field existed. watermark := state.LastProcessedTime if watermark.IsZero() { watermark = state.LastRunTime } if state.Paused || watermark.IsZero() { return false } now := workflow.Now(ctx) fires := computeMissedFireTimes(sched, watermark, now, input.Spec) if len(fires.times) == 0 { return false } if fires.truncated { logger.Warn("missed fires truncated, remaining will be caught up after ContinueAsNew", zap.Int("count", len(fires.times)), zap.Time("lastProcessedTime", watermark), zap.Time("now", now), ) } result := applyMissedRunPolicy(input.Policies.CatchUpPolicy, input.Policies.CatchUpWindow, fires.times, now, logger) fired := 0 for _, t := range result.toFire { if fired >= maxCatchUpFiresPerExecution { break } processScheduleFire(ctx, logger, input, state, t, TriggerSourceSchedule) fired++ } unfired := int64(len(result.toFire) - fired) if result.skipped > 0 { state.SkippedRuns += result.skipped logger.Info("catch-up skipped missed fires", zap.Int64("skipped", result.skipped), zap.Int("total_missed", len(fires.times)), zap.String("policy", input.Policies.CatchUpPolicy.String()), ) } // Advance watermark past all fires we've fully processed (fired or // skipped) to avoid re-discovering them after ContinueAsNew. // If we capped fires via maxCatchUpFiresPerExecution, only advance // to the last one we actually fired so the rest are retried. if unfired > 0 { state.LastProcessedTime = result.toFire[fired-1] } else if last := fires.times[len(fires.times)-1]; last.After(state.LastProcessedTime) { state.LastProcessedTime = last } return unfired > 0 || fires.truncated } // processBackfills drains pending backfill requests from state, computing // cron fire times for each request's time range and executing them. // Like processMissedRuns, it caps fires per execution and returns true // if more work remains (signalling the caller to ContinueAsNew). func processBackfills(ctx workflow.Context, logger *zap.Logger, sched cron.Schedule, input *SchedulerWorkflowInput, state *SchedulerWorkflowState) bool { // Backfills respect the pause state: an explicit user request to replay a time // range should not fire workflows while the schedule is paused. The pending // backfills are preserved in state and will execute once the schedule is unpaused. if state.Paused || len(state.PendingBackfills) == 0 { return false } fired := 0 for len(state.PendingBackfills) > 0 { bf := &state.PendingBackfills[0] fires := computeMissedFireTimes(sched, bf.StartTime.Add(-time.Second), bf.EndTime, input.Spec) for _, t := range fires.times { if fired >= maxBackfillFiresPerExecution { bf.StartTime = t logger.Info("backfill batch cap reached, continuing after ContinueAsNew", zap.String("backfillId", bf.BackfillID), zap.Time("resumeFrom", t), zap.Int("firedThisBatch", fired), ) return true } processScheduleFire(ctx, logger, input, state, t, TriggerSourceBackfill) fired++ } if fires.truncated { // More fires exist beyond the 1000-fire scan cap. // Advance start past the last processed fire so it isn't replayed. if len(fires.times) > 0 { bf.StartTime = fires.times[len(fires.times)-1].Add(time.Second) } logger.Info("backfill range has more fires beyond scan cap, continuing after ContinueAsNew", zap.String("backfillId", bf.BackfillID), zap.Int("firedThisBatch", fired), ) return true } logger.Info("backfill completed", zap.String("backfillId", bf.BackfillID), zap.Int("firedTotal", fired), ) state.PendingBackfills = state.PendingBackfills[1:] } return false } // buildScheduleDescription creates a snapshot of the current schedule // configuration and runtime state for the describe query handler. func buildScheduleDescription(input *SchedulerWorkflowInput, state *SchedulerWorkflowState) *ScheduleDescription { return &ScheduleDescription{ ScheduleID: input.ScheduleID, Domain: input.Domain, Spec: input.Spec, Action: input.Action, Policies: input.Policies, Paused: state.Paused, PauseReason: state.PauseReason, PausedBy: state.PausedBy, LastRunTime: state.LastRunTime, NextRunTime: state.NextRunTime, TotalRuns: state.TotalRuns, MissedRuns: state.MissedRuns, SkippedRuns: state.SkippedRuns, } } // safeContinueAsNew drains the delete channel before performing ContinueAsNew. // Buffered signals are not carried across ContinueAsNew boundaries, so a delete // signal that arrived alongside a state-changing signal would be lost without this check. func safeContinueAsNew(ctx workflow.Context, logger *zap.Logger, deleteCh workflow.Channel, input SchedulerWorkflowInput, state *SchedulerWorkflowState) error { if deleteCh.ReceiveAsync(nil) { logger.Info("schedule deleted (caught before ContinueAsNew)") return nil } state.Iterations = 0 input.State = *state return workflow.NewContinueAsNewError(ctx, WorkflowTypeName, input) } func minTime(a, b time.Time) time.Time { if a.Before(b) { return a } return b } func maxTime(a, b time.Time) time.Time { if a.After(b) { return a } return b } ================================================ FILE: service/worker/scheduler/workflow_test.go ================================================ // Copyright (c) 2026 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package scheduler import ( "testing" "time" "github.com/robfig/cron/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "github.com/uber/cadence/common/types" ) var testLogger = zap.NewNop() func mustParseCron(t *testing.T, expr string) cron.Schedule { t.Helper() s, err := cron.ParseStandard(expr) require.NoError(t, err) return s } func TestComputeNextRunTime(t *testing.T) { now := time.Date(2026, 1, 15, 10, 30, 0, 0, time.UTC) tests := []struct { name string cron string now time.Time spec types.ScheduleSpec wantZero bool wantTime time.Time }{ { name: "every hour - next on the hour", cron: "0 * * * *", now: now, spec: types.ScheduleSpec{}, wantTime: time.Date(2026, 1, 15, 11, 0, 0, 0, time.UTC), }, { name: "every day at midnight", cron: "0 0 * * *", now: now, spec: types.ScheduleSpec{}, wantTime: time.Date(2026, 1, 16, 0, 0, 0, 0, time.UTC), }, { name: "now is before startTime - uses startTime as base", cron: "0 * * * *", now: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), spec: types.ScheduleSpec{StartTime: time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)}, wantTime: time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC), }, { name: "next run is after endTime - returns zero", cron: "0 0 * * *", now: time.Date(2026, 1, 15, 23, 0, 0, 0, time.UTC), spec: types.ScheduleSpec{EndTime: time.Date(2026, 1, 15, 23, 59, 0, 0, time.UTC)}, wantZero: true, }, { name: "next run is before endTime - returns next", cron: "0 * * * *", now: now, spec: types.ScheduleSpec{EndTime: time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)}, wantTime: time.Date(2026, 1, 15, 11, 0, 0, 0, time.UTC), }, { name: "startTime and endTime together - within window", cron: "0 * * * *", now: time.Date(2026, 6, 1, 5, 30, 0, 0, time.UTC), spec: types.ScheduleSpec{ StartTime: time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 6, 2, 0, 0, 0, 0, time.UTC), }, wantTime: time.Date(2026, 6, 1, 6, 0, 0, 0, time.UTC), }, { name: "every minute", cron: "* * * * *", now: now, spec: types.ScheduleSpec{}, wantTime: time.Date(2026, 1, 15, 10, 31, 0, 0, time.UTC), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sched := mustParseCron(t, tt.cron) got := computeNextRunTime(sched, tt.now, tt.spec) if tt.wantZero { assert.True(t, got.IsZero(), "expected zero time, got %v", got) } else { assert.Equal(t, tt.wantTime, got) } }) } } func TestComputeMissedFireTimes(t *testing.T) { tests := []struct { name string cron string lastRun time.Time now time.Time spec types.ScheduleSpec wantTimes []time.Time wantTruncated bool }{ { name: "no missed fires - now is before next fire", cron: "0 * * * *", lastRun: time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC), now: time.Date(2026, 1, 15, 10, 30, 0, 0, time.UTC), wantTimes: nil, }, { name: "one missed fire", cron: "0 * * * *", lastRun: time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC), now: time.Date(2026, 1, 15, 11, 30, 0, 0, time.UTC), wantTimes: []time.Time{ time.Date(2026, 1, 15, 11, 0, 0, 0, time.UTC), }, }, { name: "multiple missed fires", cron: "0 * * * *", lastRun: time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC), now: time.Date(2026, 1, 15, 13, 30, 0, 0, time.UTC), wantTimes: []time.Time{ time.Date(2026, 1, 15, 11, 0, 0, 0, time.UTC), time.Date(2026, 1, 15, 12, 0, 0, 0, time.UTC), time.Date(2026, 1, 15, 13, 0, 0, 0, time.UTC), }, }, { name: "missed fire exactly at now is included", cron: "0 * * * *", lastRun: time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC), now: time.Date(2026, 1, 15, 11, 0, 0, 0, time.UTC), wantTimes: []time.Time{ time.Date(2026, 1, 15, 11, 0, 0, 0, time.UTC), }, }, { name: "respects endTime - no fires past end", cron: "0 * * * *", lastRun: time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC), now: time.Date(2026, 1, 15, 14, 0, 0, 0, time.UTC), spec: types.ScheduleSpec{EndTime: time.Date(2026, 1, 15, 12, 30, 0, 0, time.UTC)}, wantTimes: []time.Time{ time.Date(2026, 1, 15, 11, 0, 0, 0, time.UTC), time.Date(2026, 1, 15, 12, 0, 0, 0, time.UTC), }, }, { name: "lastRun equals now - no missed fires", cron: "0 * * * *", lastRun: time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC), now: time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC), wantTimes: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sched := mustParseCron(t, tt.cron) got := computeMissedFireTimes(sched, tt.lastRun, tt.now, tt.spec) assert.Equal(t, tt.wantTimes, got.times) assert.Equal(t, tt.wantTruncated, got.truncated) }) } } func TestCatchUpOrchestration(t *testing.T) { now := time.Date(2026, 1, 15, 14, 0, 0, 0, time.UTC) lastProcessed := time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC) cronExpr := "0 * * * *" tests := []struct { name string policy types.ScheduleCatchUpPolicy window time.Duration wantFiredCount int wantSkipped int64 wantLastProcessedAfter time.Time }{ { name: "Skip advances watermark past all missed, fires nothing", policy: types.ScheduleCatchUpPolicySkip, wantFiredCount: 0, wantSkipped: 4, // 11:00, 12:00, 13:00, 14:00 wantLastProcessedAfter: time.Date(2026, 1, 15, 14, 0, 0, 0, time.UTC), }, { name: "One fires most recent, skips rest, advances watermark", policy: types.ScheduleCatchUpPolicyOne, wantFiredCount: 1, wantSkipped: 3, // 11:00, 12:00, 13:00 skipped wantLastProcessedAfter: time.Date(2026, 1, 15, 14, 0, 0, 0, time.UTC), }, { name: "All fires everything, skips nothing, advances watermark", policy: types.ScheduleCatchUpPolicyAll, wantFiredCount: 4, wantSkipped: 0, wantLastProcessedAfter: time.Date(2026, 1, 15, 14, 0, 0, 0, time.UTC), }, { name: "One with window excludes old fires", policy: types.ScheduleCatchUpPolicyOne, window: 90 * time.Minute, wantFiredCount: 1, wantSkipped: 3, // 11:00, 12:00 out of window + 13:00 skipped eligible wantLastProcessedAfter: time.Date(2026, 1, 15, 14, 0, 0, 0, time.UTC), }, { name: "All with tight window fires only recent", policy: types.ScheduleCatchUpPolicyAll, window: 90 * time.Minute, wantFiredCount: 2, // 13:00 and 14:00 within 90min of 14:00 wantSkipped: 2, // 11:00 and 12:00 out of window wantLastProcessedAfter: time.Date(2026, 1, 15, 14, 0, 0, 0, time.UTC), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sched := mustParseCron(t, cronExpr) fires := computeMissedFireTimes(sched, lastProcessed, now, types.ScheduleSpec{}) require.False(t, fires.truncated) require.Equal(t, 4, len(fires.times)) // 11:00, 12:00, 13:00, 14:00 result := applyMissedRunPolicy(tt.policy, tt.window, fires.times, now, testLogger) assert.Equal(t, tt.wantFiredCount, len(result.toFire), "fired count") assert.Equal(t, tt.wantSkipped, result.skipped, "skipped count") lastMissed := fires.times[len(fires.times)-1] assert.True(t, !lastMissed.Before(tt.wantLastProcessedAfter), "watermark should advance to at least %v", tt.wantLastProcessedAfter) }) } } func TestApplyMissedRunPolicy(t *testing.T) { now := time.Date(2026, 1, 15, 14, 0, 0, 0, time.UTC) fires := []time.Time{ time.Date(2026, 1, 15, 11, 0, 0, 0, time.UTC), // 3h ago time.Date(2026, 1, 15, 12, 0, 0, 0, time.UTC), // 2h ago time.Date(2026, 1, 15, 13, 0, 0, 0, time.UTC), // 1h ago } tests := []struct { name string policy types.ScheduleCatchUpPolicy window time.Duration wantToFire []time.Time wantSkipped int64 }{ { name: "Skip - all missed fires are skipped", policy: types.ScheduleCatchUpPolicySkip, wantToFire: nil, wantSkipped: 3, }, { name: "Invalid (zero value) - defaults to skip", policy: types.ScheduleCatchUpPolicyInvalid, wantToFire: nil, wantSkipped: 3, }, { name: "One - no window, fires most recent", policy: types.ScheduleCatchUpPolicyOne, wantToFire: []time.Time{fires[2]}, wantSkipped: 2, }, { name: "One - window excludes two oldest, fires most recent eligible", policy: types.ScheduleCatchUpPolicyOne, window: 90 * time.Minute, wantToFire: []time.Time{fires[2]}, // only 13:00 is within 90min of 14:00 wantSkipped: 2, // 2 out-of-window, 0 skipped eligible }, { name: "One - window excludes all, nothing fired", policy: types.ScheduleCatchUpPolicyOne, window: 30 * time.Minute, wantToFire: nil, wantSkipped: 3, }, { name: "All - no window, fires all", policy: types.ScheduleCatchUpPolicyAll, wantToFire: fires, wantSkipped: 0, }, { name: "All - window filters two oldest, fires most recent only", policy: types.ScheduleCatchUpPolicyAll, window: 90 * time.Minute, wantToFire: fires[2:], // only 13:00 is within 90min of 14:00 wantSkipped: 2, }, { name: "All - window excludes all, nothing fired", policy: types.ScheduleCatchUpPolicyAll, window: 30 * time.Minute, wantToFire: nil, wantSkipped: 3, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := applyMissedRunPolicy(tt.policy, tt.window, fires, now, testLogger) assert.Equal(t, tt.wantToFire, got.toFire) assert.Equal(t, tt.wantSkipped, got.skipped) }) } } func TestBuildScheduleDescription(t *testing.T) { lastRun := time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC) nextRun := time.Date(2026, 1, 15, 11, 0, 0, 0, time.UTC) tests := []struct { name string input SchedulerWorkflowInput state SchedulerWorkflowState want *ScheduleDescription }{ { name: "running schedule with counters", input: SchedulerWorkflowInput{ ScheduleID: "sched-1", Domain: "test-domain", Spec: types.ScheduleSpec{CronExpression: "0 * * * *"}, Action: types.ScheduleAction{ StartWorkflow: &types.StartWorkflowAction{ WorkflowType: &types.WorkflowType{Name: "my-wf"}, }, }, Policies: types.SchedulePolicies{OverlapPolicy: types.ScheduleOverlapPolicySkipNew}, }, state: SchedulerWorkflowState{ LastRunTime: lastRun, NextRunTime: nextRun, TotalRuns: 42, MissedRuns: 1, SkippedRuns: 3, }, want: &ScheduleDescription{ ScheduleID: "sched-1", Domain: "test-domain", Spec: types.ScheduleSpec{CronExpression: "0 * * * *"}, Action: types.ScheduleAction{ StartWorkflow: &types.StartWorkflowAction{ WorkflowType: &types.WorkflowType{Name: "my-wf"}, }, }, Policies: types.SchedulePolicies{OverlapPolicy: types.ScheduleOverlapPolicySkipNew}, LastRunTime: lastRun, NextRunTime: nextRun, TotalRuns: 42, MissedRuns: 1, SkippedRuns: 3, }, }, { name: "paused schedule", input: SchedulerWorkflowInput{ ScheduleID: "sched-2", Domain: "prod", Spec: types.ScheduleSpec{CronExpression: "0 0 * * *"}, }, state: SchedulerWorkflowState{ Paused: true, PauseReason: "maintenance", PausedBy: "admin@test.com", TotalRuns: 10, }, want: &ScheduleDescription{ ScheduleID: "sched-2", Domain: "prod", Spec: types.ScheduleSpec{CronExpression: "0 0 * * *"}, Paused: true, PauseReason: "maintenance", PausedBy: "admin@test.com", TotalRuns: 10, }, }, { name: "fresh schedule with no runs", input: SchedulerWorkflowInput{ScheduleID: "sched-new", Domain: "dev"}, state: SchedulerWorkflowState{}, want: &ScheduleDescription{ScheduleID: "sched-new", Domain: "dev"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := buildScheduleDescription(&tt.input, &tt.state) assert.Equal(t, tt.want, got) }) } } func TestHandlePause(t *testing.T) { tests := []struct { name string initial SchedulerWorkflowState sig PauseSignal wantPaused bool wantReason string wantPausedBy string wantChanged bool }{ { name: "pause from running", initial: SchedulerWorkflowState{}, sig: PauseSignal{Reason: "maintenance", PausedBy: "admin@test.com"}, wantPaused: true, wantReason: "maintenance", wantPausedBy: "admin@test.com", wantChanged: true, }, { name: "pause overwrites previous pause reason", initial: SchedulerWorkflowState{Paused: true, PauseReason: "old", PausedBy: "old-user"}, sig: PauseSignal{Reason: "new reason", PausedBy: "new-user"}, wantPaused: true, wantReason: "new reason", wantPausedBy: "new-user", wantChanged: true, }, { name: "pause with empty reason", initial: SchedulerWorkflowState{}, sig: PauseSignal{}, wantPaused: true, wantReason: "", wantPausedBy: "", wantChanged: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { state := tt.initial changed := handlePause(testLogger, tt.sig, &state) assert.Equal(t, tt.wantChanged, changed) assert.Equal(t, tt.wantPaused, state.Paused) assert.Equal(t, tt.wantReason, state.PauseReason) assert.Equal(t, tt.wantPausedBy, state.PausedBy) }) } } func TestHandleUnpause(t *testing.T) { tests := []struct { name string initial SchedulerWorkflowState sig UnpauseSignal wantPaused bool wantReason string wantPausedBy string wantChanged bool }{ { name: "unpause from paused", initial: SchedulerWorkflowState{Paused: true, PauseReason: "maintenance", PausedBy: "admin"}, sig: UnpauseSignal{Reason: "maintenance done"}, wantPaused: false, wantReason: "", wantPausedBy: "", wantChanged: true, }, { name: "unpause when not paused is a no-op", initial: SchedulerWorkflowState{Paused: false}, sig: UnpauseSignal{Reason: "shouldn't matter"}, wantPaused: false, wantReason: "", wantPausedBy: "", wantChanged: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { state := tt.initial changed := handleUnpause(testLogger, tt.sig, &state) assert.Equal(t, tt.wantChanged, changed) assert.Equal(t, tt.wantPaused, state.Paused) assert.Equal(t, tt.wantReason, state.PauseReason) assert.Equal(t, tt.wantPausedBy, state.PausedBy) }) } } func TestHandleUpdate(t *testing.T) { original := SchedulerWorkflowInput{ Domain: "test-domain", ScheduleID: "sched-1", Spec: types.ScheduleSpec{CronExpression: "0 * * * *"}, Action: types.ScheduleAction{ StartWorkflow: &types.StartWorkflowAction{ WorkflowType: &types.WorkflowType{Name: "old-workflow"}, }, }, Policies: types.SchedulePolicies{OverlapPolicy: types.ScheduleOverlapPolicySkipNew}, } tests := []struct { name string sig UpdateSignal wantCron string wantWF string wantPol types.ScheduleOverlapPolicy wantChanged bool }{ { name: "update spec only", sig: UpdateSignal{ Spec: &types.ScheduleSpec{CronExpression: "*/5 * * * *"}, }, wantCron: "*/5 * * * *", wantWF: "old-workflow", wantPol: types.ScheduleOverlapPolicySkipNew, wantChanged: true, }, { name: "update action only", sig: UpdateSignal{ Action: &types.ScheduleAction{ StartWorkflow: &types.StartWorkflowAction{ WorkflowType: &types.WorkflowType{Name: "new-workflow"}, }, }, }, wantCron: "0 * * * *", wantWF: "new-workflow", wantPol: types.ScheduleOverlapPolicySkipNew, wantChanged: true, }, { name: "update policies only", sig: UpdateSignal{ Policies: &types.SchedulePolicies{OverlapPolicy: types.ScheduleOverlapPolicyConcurrent}, }, wantCron: "0 * * * *", wantWF: "old-workflow", wantPol: types.ScheduleOverlapPolicyConcurrent, wantChanged: true, }, { name: "nil fields leave input unchanged", sig: UpdateSignal{}, wantCron: "0 * * * *", wantWF: "old-workflow", wantPol: types.ScheduleOverlapPolicySkipNew, wantChanged: false, }, { name: "invalid cron expression is rejected, spec unchanged", sig: UpdateSignal{ Spec: &types.ScheduleSpec{CronExpression: "not-a-cron"}, }, wantCron: "0 * * * *", wantWF: "old-workflow", wantPol: types.ScheduleOverlapPolicySkipNew, wantChanged: false, }, { name: "invalid cron rejected but action and policies still applied", sig: UpdateSignal{ Spec: &types.ScheduleSpec{CronExpression: "bad"}, Action: &types.ScheduleAction{StartWorkflow: &types.StartWorkflowAction{WorkflowType: &types.WorkflowType{Name: "new-workflow"}}}, Policies: &types.SchedulePolicies{OverlapPolicy: types.ScheduleOverlapPolicyConcurrent}, }, wantCron: "0 * * * *", wantWF: "new-workflow", wantPol: types.ScheduleOverlapPolicyConcurrent, wantChanged: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { input := original state := &SchedulerWorkflowState{} changed := handleUpdate(testLogger, tt.sig, &input, state) assert.Equal(t, tt.wantChanged, changed) assert.Equal(t, tt.wantCron, input.Spec.CronExpression) assert.Equal(t, tt.wantWF, input.Action.StartWorkflow.WorkflowType.Name) assert.Equal(t, tt.wantPol, input.Policies.OverlapPolicy) }) } t.Run("spec change clears pending backfills", func(t *testing.T) { input := original state := &SchedulerWorkflowState{ PendingBackfills: []BackfillRequest{ {StartTime: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 2, 0, 0, 0, 0, time.UTC)}, {StartTime: time.Date(2026, 1, 3, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 4, 0, 0, 0, 0, time.UTC)}, }, } changed := handleUpdate(testLogger, UpdateSignal{ Spec: &types.ScheduleSpec{CronExpression: "*/5 * * * *"}, }, &input, state) assert.True(t, changed) assert.Equal(t, "*/5 * * * *", input.Spec.CronExpression) assert.Empty(t, state.PendingBackfills) }) t.Run("action-only update preserves pending backfills", func(t *testing.T) { input := original state := &SchedulerWorkflowState{ PendingBackfills: []BackfillRequest{ {StartTime: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 2, 0, 0, 0, 0, time.UTC)}, }, } changed := handleUpdate(testLogger, UpdateSignal{ Action: &types.ScheduleAction{StartWorkflow: &types.StartWorkflowAction{WorkflowType: &types.WorkflowType{Name: "new-workflow"}}}, }, &input, state) assert.True(t, changed) assert.Len(t, state.PendingBackfills, 1) }) t.Run("invalid cron does not clear pending backfills", func(t *testing.T) { input := original state := &SchedulerWorkflowState{ PendingBackfills: []BackfillRequest{ {StartTime: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 2, 0, 0, 0, 0, time.UTC)}, }, } changed := handleUpdate(testLogger, UpdateSignal{ Spec: &types.ScheduleSpec{CronExpression: "not-a-cron"}, }, &input, state) assert.False(t, changed) assert.Len(t, state.PendingBackfills, 1) }) } func TestHandleBackfill(t *testing.T) { tests := []struct { name string sig BackfillSignal initialPending int wantQueued bool wantPendingLen int }{ { name: "valid backfill is queued", sig: BackfillSignal{ StartTime: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 2, 0, 0, 0, 0, time.UTC), OverlapPolicy: types.ScheduleOverlapPolicyConcurrent, BackfillID: "bf-1", }, wantQueued: true, wantPendingLen: 1, }, { name: "invalid range (end <= start) is rejected", sig: BackfillSignal{ StartTime: time.Date(2026, 1, 2, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), }, wantQueued: false, wantPendingLen: 0, }, { name: "equal start and end is rejected", sig: BackfillSignal{ StartTime: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), }, wantQueued: false, wantPendingLen: 0, }, { name: "multiple backfills accumulate", sig: BackfillSignal{ StartTime: time.Date(2026, 1, 3, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 4, 0, 0, 0, 0, time.UTC), BackfillID: "bf-2", }, initialPending: 1, wantQueued: true, wantPendingLen: 2, }, { name: "overlapping backfill is queued with warning", sig: BackfillSignal{ StartTime: time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 3, 0, 0, 0, 0, time.UTC), BackfillID: "bf-overlap", }, initialPending: 1, wantQueued: true, wantPendingLen: 2, }, { name: "backfill rejected when queue is full", sig: BackfillSignal{ StartTime: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 2, 2, 0, 0, 0, 0, time.UTC), BackfillID: "bf-over-cap", }, initialPending: maxPendingBackfills, wantQueued: false, wantPendingLen: maxPendingBackfills, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { state := &SchedulerWorkflowState{} for i := 0; i < tt.initialPending; i++ { state.PendingBackfills = append(state.PendingBackfills, BackfillRequest{ StartTime: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 2, 0, 0, 0, 0, time.UTC), }) } got := handleBackfill(testLogger, tt.sig, state) assert.Equal(t, tt.wantQueued, got) assert.Equal(t, tt.wantPendingLen, len(state.PendingBackfills)) }) } } func TestProcessBackfillsRespectsPause(t *testing.T) { sched := mustParseCron(t, "0 * * * *") input := &SchedulerWorkflowInput{ Spec: types.ScheduleSpec{CronExpression: "0 * * * *"}, } state := &SchedulerWorkflowState{ Paused: true, PendingBackfills: []BackfillRequest{ { StartTime: time.Date(2026, 1, 1, 10, 0, 0, 0, time.UTC), EndTime: time.Date(2026, 1, 1, 13, 0, 0, 0, time.UTC), BackfillID: "bf-paused", }, }, } // processBackfills should short-circuit without touching PendingBackfills moreWork := processBackfills(nil, testLogger, sched, input, state) assert.False(t, moreWork, "paused schedule should not process backfills") assert.Len(t, state.PendingBackfills, 1, "pending backfills should be preserved while paused") } func TestBackfillFireComputation(t *testing.T) { sched := mustParseCron(t, "0 * * * *") tests := []struct { name string startTime time.Time endTime time.Time wantFires int }{ { name: "3-hour window [10:00, 13:00] produces 4 fires (inclusive both ends)", startTime: time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC), endTime: time.Date(2026, 1, 15, 13, 0, 0, 0, time.UTC), wantFires: 4, // 10:00, 11:00, 12:00, 13:00 }, { name: "exact boundary [10:00, 11:00] includes both endpoints", startTime: time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC), endTime: time.Date(2026, 1, 15, 11, 0, 0, 0, time.UTC), wantFires: 2, // 10:00, 11:00 }, { name: "sub-hour window [10:00, 10:30] includes start fire only", startTime: time.Date(2026, 1, 15, 10, 0, 0, 0, time.UTC), endTime: time.Date(2026, 1, 15, 10, 30, 0, 0, time.UTC), wantFires: 1, // 10:00 }, { name: "24-hour window [00:00, 00:00+1d] produces 25 fires", startTime: time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC), endTime: time.Date(2026, 1, 16, 0, 0, 0, 0, time.UTC), wantFires: 25, // 00:00 through 00:00 next day, inclusive }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { fires := computeMissedFireTimes(sched, tt.startTime.Add(-time.Second), tt.endTime, types.ScheduleSpec{}) assert.Equal(t, tt.wantFires, len(fires.times)) }) } } ================================================ FILE: service/worker/service.go ================================================ // The MIT License (MIT) // // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package worker import ( "context" "fmt" "sync/atomic" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/resource" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/archiver" "github.com/uber/cadence/service/worker/asyncworkflow" "github.com/uber/cadence/service/worker/batcher" "github.com/uber/cadence/service/worker/diagnostics" "github.com/uber/cadence/service/worker/domaindeprecation" "github.com/uber/cadence/service/worker/esanalyzer" "github.com/uber/cadence/service/worker/failovermanager" "github.com/uber/cadence/service/worker/indexer" "github.com/uber/cadence/service/worker/parentclosepolicy" "github.com/uber/cadence/service/worker/replicator" "github.com/uber/cadence/service/worker/scanner" "github.com/uber/cadence/service/worker/scanner/executions" "github.com/uber/cadence/service/worker/scanner/shardscanner" "github.com/uber/cadence/service/worker/scanner/tasklist" "github.com/uber/cadence/service/worker/scanner/timers" "github.com/uber/cadence/service/worker/scheduler" ) type ( // Service represents the cadence-worker service. This service hosts all background processing needed for cadence cluster: // 1. Replicator: Handles applying replication tasks generated by remote clusters. // 2. Indexer: Handles uploading of visibility records to elastic search. // 3. Archiver: Handles archival of workflow histories. Service struct { resource.Resource status int32 stopC chan struct{} params *resource.Params config *Config } // Config contains all the service config for worker Config struct { AdminOperationToken dynamicproperties.StringPropertyFn KafkaCfg config.KafkaConfig ArchiverConfig *archiver.Config IndexerCfg *indexer.Config ScannerCfg *scanner.Config BatcherCfg *batcher.Config ESAnalyzerCfg *esanalyzer.Config failoverManagerCfg *failovermanager.Config ThrottledLogRPS dynamicproperties.IntPropertyFn PersistenceGlobalMaxQPS dynamicproperties.IntPropertyFn PersistenceMaxQPS dynamicproperties.IntPropertyFn EnableBatcher dynamicproperties.BoolPropertyFn EnableScheduler dynamicproperties.BoolPropertyFn EnableParentClosePolicyWorker dynamicproperties.BoolPropertyFn NumParentClosePolicySystemWorkflows dynamicproperties.IntPropertyFn EnableFailoverManager dynamicproperties.BoolPropertyFn DomainReplicationMaxRetryDuration dynamicproperties.DurationPropertyFn EnableESAnalyzer dynamicproperties.BoolPropertyFn EnableAsyncWorkflowConsumption dynamicproperties.BoolPropertyFn EnableDomainAuditLogging dynamicproperties.BoolPropertyFn HostName string } ) // NewService builds a new cadence-worker service func NewService(params *resource.Params) (resource.Resource, error) { serviceConfig := NewConfig(params) serviceResource, err := resource.New( params, service.Worker, &service.Config{ PersistenceMaxQPS: serviceConfig.PersistenceMaxQPS, PersistenceGlobalMaxQPS: serviceConfig.PersistenceGlobalMaxQPS, ThrottledLoggerMaxRPS: serviceConfig.ThrottledLogRPS, IsErrorRetryableFunction: common.IsServiceTransientError, // worker service doesn't need visibility config as it never call visibilityManager API }, ) if err != nil { return nil, err } return &Service{ Resource: serviceResource, status: common.DaemonStatusInitialized, config: serviceConfig, params: params, stopC: make(chan struct{}), }, nil } // NewConfig builds the new Config for cadence-worker service func NewConfig(params *resource.Params) *Config { dc := dynamicconfig.NewCollection( params.DynamicConfig, params.Logger, dynamicproperties.ClusterNameFilter(params.ClusterMetadata.GetCurrentClusterName()), ) config := &Config{ AdminOperationToken: dc.GetStringProperty(dynamicproperties.AdminOperationToken), ArchiverConfig: &archiver.Config{ ArchiverConcurrency: dc.GetIntProperty(dynamicproperties.WorkerArchiverConcurrency), ArchivalsPerIteration: dc.GetIntProperty(dynamicproperties.WorkerArchivalsPerIteration), TimeLimitPerArchivalIteration: dc.GetDurationProperty(dynamicproperties.WorkerTimeLimitPerArchivalIteration), AllowArchivingIncompleteHistory: dc.GetBoolProperty(dynamicproperties.AllowArchivingIncompleteHistory), }, ScannerCfg: &scanner.Config{ ScannerPersistenceMaxQPS: dc.GetIntProperty(dynamicproperties.ScannerPersistenceMaxQPS), TaskListScannerOptions: tasklist.Options{ GetOrphanTasksPageSizeFn: dc.GetIntProperty(dynamicproperties.ScannerGetOrphanTasksPageSize), TaskBatchSizeFn: dc.GetIntProperty(dynamicproperties.ScannerBatchSizeForTasklistHandler), EnableCleaning: dc.GetBoolProperty(dynamicproperties.EnableCleaningOrphanTaskInTasklistScavenger), MaxTasksPerJobFn: dc.GetIntProperty(dynamicproperties.ScannerMaxTasksProcessedPerTasklistJob), }, Persistence: ¶ms.PersistenceConfig, ClusterMetadata: params.ClusterMetadata, TaskListScannerEnabled: dc.GetBoolProperty(dynamicproperties.TaskListScannerEnabled), HistoryScannerEnabled: dc.GetBoolProperty(dynamicproperties.HistoryScannerEnabled), ShardScanners: []*shardscanner.ScannerConfig{ executions.ConcreteExecutionConfig(dc), executions.CurrentExecutionConfig(dc), timers.ScannerConfig(dc), }, MaxWorkflowRetentionInDays: dc.GetIntProperty(dynamicproperties.MaxRetentionDays), }, KafkaCfg: params.KafkaConfig, BatcherCfg: &batcher.Config{ AdminOperationToken: dc.GetStringProperty(dynamicproperties.AdminOperationToken), ClusterMetadata: params.ClusterMetadata, }, failoverManagerCfg: &failovermanager.Config{ AdminOperationToken: dc.GetStringProperty(dynamicproperties.AdminOperationToken), ClusterMetadata: params.ClusterMetadata, }, ESAnalyzerCfg: &esanalyzer.Config{ ESAnalyzerPause: dc.GetBoolProperty(dynamicproperties.ESAnalyzerPause), ESAnalyzerTimeWindow: dc.GetDurationProperty(dynamicproperties.ESAnalyzerTimeWindow), ESAnalyzerMaxNumDomains: dc.GetIntProperty(dynamicproperties.ESAnalyzerMaxNumDomains), ESAnalyzerMaxNumWorkflowTypes: dc.GetIntProperty(dynamicproperties.ESAnalyzerMaxNumWorkflowTypes), ESAnalyzerLimitToTypes: dc.GetStringProperty(dynamicproperties.ESAnalyzerLimitToTypes), ESAnalyzerEnableAvgDurationBasedChecks: dc.GetBoolProperty(dynamicproperties.ESAnalyzerEnableAvgDurationBasedChecks), ESAnalyzerLimitToDomains: dc.GetStringProperty(dynamicproperties.ESAnalyzerLimitToDomains), ESAnalyzerNumWorkflowsToRefresh: dc.GetIntPropertyFilteredByWorkflowType(dynamicproperties.ESAnalyzerNumWorkflowsToRefresh), ESAnalyzerBufferWaitTime: dc.GetDurationPropertyFilteredByWorkflowType(dynamicproperties.ESAnalyzerBufferWaitTime), ESAnalyzerMinNumWorkflowsForAvg: dc.GetIntPropertyFilteredByWorkflowType(dynamicproperties.ESAnalyzerMinNumWorkflowsForAvg), ESAnalyzerWorkflowDurationWarnThresholds: dc.GetStringProperty(dynamicproperties.ESAnalyzerWorkflowDurationWarnThresholds), ESAnalyzerWorkflowVersionDomains: dc.GetStringProperty(dynamicproperties.ESAnalyzerWorkflowVersionMetricDomains), ESAnalyzerWorkflowTypeDomains: dc.GetStringProperty(dynamicproperties.ESAnalyzerWorkflowTypeMetricDomains), }, EnableBatcher: dc.GetBoolProperty(dynamicproperties.EnableBatcher), EnableScheduler: dc.GetBoolProperty(dynamicproperties.EnableScheduler), EnableParentClosePolicyWorker: dc.GetBoolProperty(dynamicproperties.EnableParentClosePolicyWorker), NumParentClosePolicySystemWorkflows: dc.GetIntProperty(dynamicproperties.NumParentClosePolicySystemWorkflows), EnableESAnalyzer: dc.GetBoolProperty(dynamicproperties.EnableESAnalyzer), EnableFailoverManager: dc.GetBoolProperty(dynamicproperties.EnableFailoverManager), ThrottledLogRPS: dc.GetIntProperty(dynamicproperties.WorkerThrottledLogRPS), PersistenceGlobalMaxQPS: dc.GetIntProperty(dynamicproperties.WorkerPersistenceGlobalMaxQPS), PersistenceMaxQPS: dc.GetIntProperty(dynamicproperties.WorkerPersistenceMaxQPS), DomainReplicationMaxRetryDuration: dc.GetDurationProperty(dynamicproperties.WorkerReplicationTaskMaxRetryDuration), EnableAsyncWorkflowConsumption: dc.GetBoolProperty(dynamicproperties.EnableAsyncWorkflowConsumption), EnableDomainAuditLogging: dc.GetBoolProperty(dynamicproperties.EnableDomainAuditLogging), HostName: params.HostName, } advancedVisWritingMode := dc.GetStringProperty( dynamicproperties.WriteVisibilityStoreName, ) if shouldStartIndexer(params, advancedVisWritingMode) { config.IndexerCfg = &indexer.Config{ IndexerConcurrency: dc.GetIntProperty(dynamicproperties.WorkerIndexerConcurrency), ESProcessorNumOfWorkers: dc.GetIntProperty(dynamicproperties.WorkerESProcessorNumOfWorkers), ESProcessorBulkActions: dc.GetIntProperty(dynamicproperties.WorkerESProcessorBulkActions), ESProcessorBulkSize: dc.GetIntProperty(dynamicproperties.WorkerESProcessorBulkSize), ESProcessorFlushInterval: dc.GetDurationProperty(dynamicproperties.WorkerESProcessorFlushInterval), ValidSearchAttributes: dc.GetMapProperty(dynamicproperties.ValidSearchAttributes), EnableQueryAttributeValidation: dc.GetBoolProperty(dynamicproperties.EnableQueryAttributeValidation), } } return config } // Start is called to start the service func (s *Service) Start() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusInitialized, common.DaemonStatusStarted) { return } logger := s.GetLogger() logger.Info("worker starting", tag.ComponentWorker) s.Resource.Start() s.Resource.GetDomainReplicationQueue().Start() s.ensureDomainExists(constants.SystemLocalDomainName) s.startScanner() s.startFixerWorkflowWorker() if s.config.IndexerCfg != nil { if shouldStartMigrationIndexer(s.params) { s.startMigrationDualIndexer() } else { s.startIndexer() } } s.startReplicator() s.startDiagnostics() s.startDomainDeprecation() if s.GetArchivalMetadata().GetHistoryConfig().ClusterConfiguredForArchival() { s.startArchiver() } if s.config.EnableBatcher() { s.ensureDomainExists(constants.BatcherLocalDomainName) s.startBatcher() } if s.config.EnableScheduler() { sm := s.startSchedulerWorkerManager() defer sm.Stop() } if s.config.EnableParentClosePolicyWorker() { s.startParentClosePolicyProcessor() } if s.config.EnableESAnalyzer() { s.startESAnalyzer() } if s.config.EnableFailoverManager() { s.startFailoverManager() } cm := s.startAsyncWorkflowConsumerManager() defer cm.Stop() logger.Info("worker started", tag.ComponentWorker) <-s.stopC } // Stop is called to stop the service func (s *Service) Stop() { if !atomic.CompareAndSwapInt32(&s.status, common.DaemonStatusStarted, common.DaemonStatusStopped) { return } s.GetLogger().Info("worker stopping", tag.ComponentWorker) close(s.stopC) s.Resource.Stop() s.Resource.GetDomainReplicationQueue().Stop() s.GetLogger().Info("worker stopped", tag.ComponentWorker) } func (s *Service) startParentClosePolicyProcessor() { params := &parentclosepolicy.BootstrapParams{ ServiceClient: s.params.PublicClient, MetricsClient: s.GetMetricsClient(), Logger: s.GetLogger(), TallyScope: s.params.MetricScope, ClientBean: s.GetClientBean(), DomainCache: s.GetDomainCache(), NumWorkflows: s.config.NumParentClosePolicySystemWorkflows(), } processor := parentclosepolicy.New(params) if err := processor.Start(); err != nil { s.GetLogger().Fatal("error starting parentclosepolicy processor", tag.Error(err)) } } func (s *Service) startESAnalyzer() { esClient := s.params.ESClient esConfig := s.params.ESConfig // when es client is not configured, use os client, this can happen during os migration, we will use os client to read from os // there is another case is pinot migration and migration mode is off, in this case nil es config/client is expected if esClient == nil && s.params.OSClient != nil { esClient = s.params.OSClient esConfig = s.params.OSConfig } analyzer := esanalyzer.New( s.params.PublicClient, s.GetFrontendClient(), s.GetClientBean(), esClient, s.params.PinotClient, esConfig, s.params.PinotConfig, s.GetLogger(), s.params.MetricScope, s.Resource, s.GetDomainCache(), s.config.ESAnalyzerCfg, ) if err := analyzer.Start(); err != nil { s.GetLogger().Fatal("error starting esanalyzer", tag.Error(err)) } } func (s *Service) startBatcher() { params := &batcher.BootstrapParams{ Config: *s.config.BatcherCfg, ServiceClient: s.params.PublicClient, MetricsClient: s.GetMetricsClient(), Logger: s.GetLogger(), TallyScope: s.params.MetricScope, ClientBean: s.GetClientBean(), } if err := batcher.New(params).Start(); err != nil { s.GetLogger().Fatal("error starting batcher", tag.Error(err)) } } func (s *Service) startSchedulerWorkerManager() *scheduler.WorkerManager { params := &scheduler.BootstrapParams{ ServiceClient: s.params.PublicClient, FrontendClient: s.GetClientBean().GetFrontendClient(), Logger: s.GetLogger(), DomainCache: s.GetDomainCache(), MembershipResolver: s.GetMembershipResolver(), HostInfo: s.GetHostInfo(), } wm := scheduler.NewWorkerManager(params, s.config.EnableScheduler) wm.Start() return wm } func (s *Service) startScanner() { params := &scanner.BootstrapParams{ Config: *s.config.ScannerCfg, TallyScope: s.params.MetricScope, } if err := scanner.New(s.Resource, params).Start(); err != nil { s.GetLogger().Fatal("error starting scanner", tag.Error(err)) } } func (s *Service) startFixerWorkflowWorker() { params := &scanner.BootstrapParams{ Config: *s.config.ScannerCfg, TallyScope: s.params.MetricScope, } if err := scanner.NewDataCorruptionWorkflowWorker(s.Resource, params).StartDataCorruptionWorkflowWorker(); err != nil { s.GetLogger().Fatal("error starting fixer workflow worker", tag.Error(err)) } } func (s *Service) startDiagnostics() { params := diagnostics.Params{ ServiceClient: s.params.PublicClient, MetricsClient: s.GetMetricsClient(), MessagingClient: s.GetMessagingClient(), TallyScope: s.params.MetricScope, ClientBean: s.GetClientBean(), Logger: s.GetLogger(), Invariants: s.params.DiagnosticsInvariants, ClusterMetadata: s.GetClusterMetadata(), } if err := diagnostics.New(params).Start(); err != nil { s.Stop() s.GetLogger().Fatal("error starting diagnostics", tag.Error(err)) } } func (s *Service) startReplicator() { domainReplicationTaskExecutor := domain.NewReplicationTaskExecutor( s.Resource.GetDomainManager(), s.Resource.GetDomainAuditManager(), s.Resource.GetTimeSource(), s.Resource.GetLogger(), s.config.EnableDomainAuditLogging, ) msgReplicator := replicator.NewReplicator( s.GetClusterMetadata(), s.GetClientBean(), s.GetLogger(), s.GetMetricsClient(), s.GetHostInfo(), s.GetMembershipResolver(), s.GetDomainReplicationQueue(), domainReplicationTaskExecutor, s.config.DomainReplicationMaxRetryDuration(), ) if err := msgReplicator.Start(); err != nil { msgReplicator.Stop() s.GetLogger().Fatal("fail to start replicator", tag.Error(err)) } } func (s *Service) startIndexer() { visibilityIndexer := indexer.NewIndexer( s.config.IndexerCfg, s.GetMessagingClient(), s.params.ESClient, s.params.ESConfig.Indices[constants.VisibilityAppName], s.params.ESConfig.ConsumerName, s.GetLogger(), s.GetMetricsClient(), ) if err := visibilityIndexer.Start(); err != nil { visibilityIndexer.Stop() s.GetLogger().Fatal("fail to start indexer", tag.Error(err)) } } func (s *Service) startMigrationDualIndexer() { visibilityDualIndexer := indexer.NewMigrationDualIndexer( s.config.IndexerCfg, s.GetMessagingClient(), s.params.ESClient, s.params.OSClient, s.params.ESConfig.Indices[constants.VisibilityAppName], s.params.OSConfig.Indices[constants.VisibilityAppName], s.params.ESConfig.ConsumerName, s.params.OSConfig.ConsumerName, s.GetLogger(), s.GetMetricsClient(), ) if err := visibilityDualIndexer.Start(); err != nil { // not need to call visibilityDualIndexer.Stop() since it has been called inside Start() s.GetLogger().Fatal("fail to start indexer", tag.Error(err)) } } func (s *Service) startArchiver() { bc := &archiver.BootstrapContainer{ PublicClient: s.GetSDKClient(), MetricsClient: s.GetMetricsClient(), Logger: s.GetLogger(), HistoryV2Manager: s.GetHistoryManager(), DomainCache: s.GetDomainCache(), Config: s.config.ArchiverConfig, ArchiverProvider: s.GetArchiverProvider(), } clientWorker := archiver.NewClientWorker(bc) if err := clientWorker.Start(); err != nil { clientWorker.Stop() s.GetLogger().Fatal("failed to start archiver", tag.Error(err)) } } func (s *Service) startFailoverManager() { params := &failovermanager.BootstrapParams{ Config: *s.config.failoverManagerCfg, ServiceClient: s.params.PublicClient, MetricsClient: s.GetMetricsClient(), Logger: s.GetLogger(), TallyScope: s.params.MetricScope, ClientBean: s.GetClientBean(), } if err := failovermanager.New(params).Start(); err != nil { s.Stop() s.GetLogger().Fatal("error starting failoverManager", tag.Error(err)) } } func (s *Service) startAsyncWorkflowConsumerManager() common.Daemon { cm := asyncworkflow.NewConsumerManager( s.GetLogger(), s.GetMetricsClient(), s.GetDomainCache(), s.Resource.GetAsyncWorkflowQueueProvider(), s.GetFrontendClient(), asyncworkflow.WithEnabledPropertyFn(s.config.EnableAsyncWorkflowConsumption), ) cm.Start() return cm } func (s *Service) startDomainDeprecation() { params := domaindeprecation.Params{ Config: domaindeprecation.Config{ AdminOperationToken: s.config.AdminOperationToken, }, ServiceClient: s.params.PublicClient, ClientBean: s.GetClientBean(), MetricsClient: s.GetMetricsClient(), Tally: s.params.MetricScope, Logger: s.GetLogger(), } if err := domaindeprecation.New(params).Start(); err != nil { s.Stop() s.GetLogger().Fatal("error starting domain deprecator", tag.Error(err)) } } func (s *Service) ensureDomainExists(domain string) { _, err := s.GetDomainManager().GetDomain(context.Background(), &persistence.GetDomainRequest{Name: domain}) switch err.(type) { case nil: // noop case *types.EntityNotExistsError: s.GetLogger().Info(fmt.Sprintf("domain %s does not exist, attempting to register domain", domain)) s.registerSystemDomain(domain) default: s.GetLogger().Fatal("failed to verify if system domain exists", tag.Error(err)) } } func (s *Service) registerSystemDomain(domain string) { currentClusterName := s.GetClusterMetadata().GetCurrentClusterName() _, err := s.GetDomainManager().CreateDomain(context.Background(), &persistence.CreateDomainRequest{ Info: &persistence.DomainInfo{ ID: getDomainID(domain), Name: domain, Description: "Cadence internal system domain", }, Config: &persistence.DomainConfig{ Retention: constants.SystemDomainRetentionDays, EmitMetric: true, }, ReplicationConfig: &persistence.DomainReplicationConfig{ ActiveClusterName: currentClusterName, Clusters: cluster.GetOrUseDefaultClusters(currentClusterName, nil), }, IsGlobalDomain: false, FailoverVersion: constants.EmptyVersion, }) if err != nil { if _, ok := err.(*types.DomainAlreadyExistsError); ok { return } s.GetLogger().Fatal("failed to register system domain", tag.Error(err)) } } func getDomainID(domain string) string { var domainID string switch domain { case constants.SystemLocalDomainName: domainID = constants.SystemDomainID case constants.BatcherLocalDomainName: domainID = constants.BatcherDomainID case constants.ShadowerLocalDomainName: domainID = constants.ShadowerDomainID } return domainID } func shouldStartIndexer(params *resource.Params, advancedWritingMode dynamicproperties.StringPropertyFn) bool { // only start indexer when advanced visibility writing mode is set to on and advanced visibility store is configured if !common.IsAdvancedVisibilityWritingEnabled(advancedWritingMode(), params.PersistenceConfig.IsAdvancedVisibilityConfigExist()) { return false } // when it is using pinot and not in migration mode, indexer should not be started since Pinot will direclty ingest from kafka if params.PersistenceConfig.AdvancedVisibilityStore == constants.PinotVisibilityStoreName && params.PinotConfig != nil && !params.PinotConfig.Migration.Enabled { return false } return true } func shouldStartMigrationIndexer(params *resource.Params) bool { // not need to check IsAdvancedVisibilityWritingEnabled here since it was already checked or the s.config.IndexerCfg will be nil // when it is using OS and in migration mode, we should start dual indexer to write to both ES and OS if params.PersistenceConfig.AdvancedVisibilityStore == constants.OSVisibilityStoreName && params.OSConfig != nil && params.OSConfig.Migration.Enabled { return true } return false } ================================================ FILE: service/worker/worker/worker_interface.go ================================================ // The MIT License (MIT) // Copyright (c) 2024 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package worker import ( "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" ) //go:generate mockgen -source $GOFILE -destination worker_mock.go -package worker github.com/uber/cadence/service/worker/worker Worker type Worker interface { RegisterActivity(activity interface{}) RegisterActivityWithOptions(activity interface{}, options activity.RegisterOptions) RegisterWorkflow(workflow interface{}) RegisterWorkflowWithOptions(workflow interface{}, options workflow.RegisterOptions) Start() error Stop() Run() error } ================================================ FILE: service/worker/worker/worker_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: worker_interface.go // // Generated by this command: // // mockgen -source worker_interface.go -destination worker_mock.go -package worker github.com/uber/cadence/service/worker/worker Worker // // Package worker is a generated GoMock package. package worker import ( reflect "reflect" activity "go.uber.org/cadence/activity" workflow "go.uber.org/cadence/workflow" gomock "go.uber.org/mock/gomock" ) // MockWorker is a mock of Worker interface. type MockWorker struct { ctrl *gomock.Controller recorder *MockWorkerMockRecorder isgomock struct{} } // MockWorkerMockRecorder is the mock recorder for MockWorker. type MockWorkerMockRecorder struct { mock *MockWorker } // NewMockWorker creates a new mock instance. func NewMockWorker(ctrl *gomock.Controller) *MockWorker { mock := &MockWorker{ctrl: ctrl} mock.recorder = &MockWorkerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockWorker) EXPECT() *MockWorkerMockRecorder { return m.recorder } // RegisterActivity mocks base method. func (m *MockWorker) RegisterActivity(activity any) { m.ctrl.T.Helper() m.ctrl.Call(m, "RegisterActivity", activity) } // RegisterActivity indicates an expected call of RegisterActivity. func (mr *MockWorkerMockRecorder) RegisterActivity(activity any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterActivity", reflect.TypeOf((*MockWorker)(nil).RegisterActivity), activity) } // RegisterActivityWithOptions mocks base method. func (m *MockWorker) RegisterActivityWithOptions(activity any, options activity.RegisterOptions) { m.ctrl.T.Helper() m.ctrl.Call(m, "RegisterActivityWithOptions", activity, options) } // RegisterActivityWithOptions indicates an expected call of RegisterActivityWithOptions. func (mr *MockWorkerMockRecorder) RegisterActivityWithOptions(activity, options any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterActivityWithOptions", reflect.TypeOf((*MockWorker)(nil).RegisterActivityWithOptions), activity, options) } // RegisterWorkflow mocks base method. func (m *MockWorker) RegisterWorkflow(workflow any) { m.ctrl.T.Helper() m.ctrl.Call(m, "RegisterWorkflow", workflow) } // RegisterWorkflow indicates an expected call of RegisterWorkflow. func (mr *MockWorkerMockRecorder) RegisterWorkflow(workflow any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterWorkflow", reflect.TypeOf((*MockWorker)(nil).RegisterWorkflow), workflow) } // RegisterWorkflowWithOptions mocks base method. func (m *MockWorker) RegisterWorkflowWithOptions(workflow any, options workflow.RegisterOptions) { m.ctrl.T.Helper() m.ctrl.Call(m, "RegisterWorkflowWithOptions", workflow, options) } // RegisterWorkflowWithOptions indicates an expected call of RegisterWorkflowWithOptions. func (mr *MockWorkerMockRecorder) RegisterWorkflowWithOptions(workflow, options any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterWorkflowWithOptions", reflect.TypeOf((*MockWorker)(nil).RegisterWorkflowWithOptions), workflow, options) } // Run mocks base method. func (m *MockWorker) Run() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Run") ret0, _ := ret[0].(error) return ret0 } // Run indicates an expected call of Run. func (mr *MockWorkerMockRecorder) Run() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockWorker)(nil).Run)) } // Start mocks base method. func (m *MockWorker) Start() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start") ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *MockWorkerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockWorker)(nil).Start)) } // Stop mocks base method. func (m *MockWorker) Stop() { m.ctrl.T.Helper() m.ctrl.Call(m, "Stop") } // Stop indicates an expected call of Stop. func (mr *MockWorkerMockRecorder) Stop() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockWorker)(nil).Stop)) } ================================================ FILE: service/worker/workercommon/util.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package workercommon import ( "context" "fmt" "time" "go.uber.org/cadence/client" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/resource" ) func StartWorkflowWithRetry( workflowType string, startUpDelay time.Duration, resource resource.Resource, startWorkflow func(client client.Client) error, ) error { // let history / matching service warm up time.Sleep(startUpDelay) sdkClient := client.NewClient( resource.GetSDKClient(), constants.SystemLocalDomainName, nil, /* &client.Options{} */ ) policy := backoff.NewExponentialRetryPolicy(time.Second) policy.SetMaximumInterval(time.Minute) policy.SetExpirationInterval(backoff.NoInterval) throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(func(_ error) bool { return true }), ) err := throttleRetry.Do(context.Background(), func(ctx context.Context) error { return startWorkflow(sdkClient) }) if err != nil { panic(fmt.Sprintf("unreachable: %#v", err)) } else { resource.GetLogger().Info("starting workflow", tag.WorkflowType(workflowType)) } return err } ================================================ FILE: simulation/README.md ================================================ # Simulation Tests Simulation tests are black box tests that validate complex multi-component scenarios against a Cadence cluster. They enable systematic testing of workflows that would otherwise require manual sanity checks. ## Overview Each suite of simulation tests: - instantiates a local docker cluster - makes requests to the local cluster, based on the test configuration - analyses the results ### Test Types - **replication** - Tests the edge-case behaviour of workflows replicated to multiple clusters, e.g during failover, replication lag, or in active-active domains. - **matching** - Tests the performance of the cadence-matching service. - **history** - Tests the behaviour of the history service. See individual subdirectory READMEs for specific test details. ## Running Simulations Running the tests requires that the developer has - [setup their development environment](../CONTRIBUTING.md#development-environment) - has docker running ### Quick Start ```bash # Run a basic replication test ./simulation/replication/run.sh --scenario default # Run a matching performance test ./simulation/matching/run.sh --scenario throughput # Run a history analysis test ./simulation/history/run.sh --scenario default ``` ### Local Development Some contributors may require a custom dockerfile to be passed to allow the simulation tests to build locally. Add your dockerfile to `docker/github_actions/` with a custom suffix, and then pass the `--dockerfile-suffix` paramater to use it: ```bash # For a file named Dockerfile.local in docker/github_actions: ./simulation/replication/run.sh --scenario default --dockerfile-suffix .local ``` ## Adding New Simulations Any time you are writing code that is complex, touches multiple components, or requires manual testing to sanity check its behaviour it is a candidate for a simulation test. To add a new simulation: 1. Create scenario file: `{type}/testdata/{type}_simulation_{name}.yaml` 2. Add config overrides: `config/dynamicconfig/{type}_simulation_{name}.yml` (if needed) 3. Test locally: `./simulation/{type}/run.sh --scenario {name}` 4. Add to CI matrix in `.github/workflows/{type}-simulation.yml` ### CI/CD Note: only replication simulations run in GitHub Actions currently. To add a new replication scenario to CI, update the matrix in `.github/workflows/replication-simulation.yml`. ================================================ FILE: simulation/history/dynamicconfig/default.yaml ================================================ system.workflowDeletionJitterRange: - value: 0 constraints: {} ================================================ FILE: simulation/history/dynamicconfig/queuev2.yaml ================================================ system.workflowDeletionJitterRange: - value: 0 constraints: {} history.enableTimerQueueV2: - value: true constraints: {} history.enableTransferQueueV2: - value: true constraints: {} history.queueMaxPendingTaskCount: - value: 10000 constraints: {} history.timerTaskBatchSize: - value: 100 constraints: {} history.timerProcessorUpdateAckInterval: - value: 30s constraints: {} history.timerProcessorUpdateAckIntervalJitterCoefficient: - value: 0 constraints: {} history.timerProcessorMaxPollRPS: - value: 20 constraints: {} history.transferTaskBatchSize: - value: 100 constraints: {} history.transferProcessorUpdateAckInterval: - value: 30s constraints: {} history.transferProcessorUpdateAckIntervalJitterCoefficient: - value: 0 constraints: {} history.transferProcessorMaxPollRPS: - value: 20 constraints: {} ================================================ FILE: simulation/history/dynamicconfig/queuev2_split.yaml ================================================ system.workflowDeletionJitterRange: - value: 0 constraints: {} history.enableTimerQueueV2: - value: true constraints: {} history.enableTransferQueueV2: - value: true constraints: {} history.timerTaskBatchSize: - value: 100 constraints: {} history.timerProcessorUpdateAckInterval: - value: 5s constraints: {} history.timerProcessorUpdateAckIntervalJitterCoefficient: - value: 0 constraints: {} history.timerProcessorMaxPollRPS: - value: 20 constraints: {} history.transferTaskBatchSize: - value: 100 constraints: {} history.transferProcessorUpdateAckInterval: - value: 5s constraints: {} history.transferProcessorUpdateAckIntervalJitterCoefficient: - value: 0 constraints: {} history.transferProcessorMaxPollRPS: - value: 20 constraints: {} history.shardUpdateMinInterval: - value: 3s constraints: {} history.queueProcessorPollBackoffInterval: - value: 5s constraints: {} history.virtualSliceForceAppendInterval: - value: 5ms constraints: {} # Only Enable 1 level of split, which has been tested manually history.queueMaxVirtualQueueCount: - value: 2 constraints: {} # Enable task rate limiter so that the number of pending tasks increases history.taskSchedulerEnableRateLimiter: - value: true constraints: {} history.taskSchedulerEnableRateLimiterShadowMode: - value: false constraints: {} history.taskSchedulerGlobalDomainRPS: - value: 5 constraints: {} history.enableTransferQueueV2PendingTaskCountAlert: - value: false constraints: {} - value: true constraints: shardID: 0 history.enableTimerQueueV2PendingTaskCountAlert: - value: false constraints: {} - value: true constraints: shardID: 1 # Set a low number to trigger queue pause with load from tests history.queueMaxPendingTaskCount: - value: 20 constraints: {} # Set a low number to trigger queue split with load from tests history.queueCriticalPendingTaskCount: - value: 18 constraints: {} ================================================ FILE: simulation/history/history_simulation_test.go ================================================ package history import ( "context" "flag" "os" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/client" "go.uber.org/cadence/compatibility" "go.uber.org/cadence/worker" "go.uber.org/yarpc" "go.uber.org/yarpc/transport/grpc" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log/tag" "github.com/uber/cadence/common/persistence" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/service" "github.com/uber/cadence/common/types" "github.com/uber/cadence/host" "github.com/uber/cadence/simulation/history/workflow" ) const ( defaultTestCase = "testdata/history_simulation_default.yaml" ) type HistorySimulationSuite struct { *require.Assertions *host.IntegrationBase wfService workflowserviceclient.Interface wfClient client.Client worker worker.Worker taskList string } func TestHistorySimulation(t *testing.T) { flag.Parse() confPath := os.Getenv("HISTORY_SIMULATION_CONFIG") if confPath == "" { confPath = defaultTestCase } clusterConfig, err := host.GetTestClusterConfig(confPath) if err != nil { t.Fatalf("failed creating cluster config from %s, err: %v", confPath, err) } testCluster := host.NewPersistenceTestCluster(t, clusterConfig) s := new(HistorySimulationSuite) params := host.IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = host.NewIntegrationBase(params) suite.Run(t, s) } func (s *HistorySimulationSuite) SetupSuite() { s.SetupLogger() s.Logger.Info("Running integration test against test cluster") clusterMetadata := host.NewClusterMetadata(s.T(), s.TestClusterConfig) dc := persistence.DynamicConfiguration{ EnableCassandraAllConsistencyLevelDelete: dynamicproperties.GetBoolPropertyFn(true), PersistenceSampleLoggingRate: dynamicproperties.GetIntPropertyFn(100), EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(true), EnableHistoryTaskDualWriteMode: dynamicproperties.GetBoolPropertyFn(true), ReadNoSQLHistoryTaskFromDataBlob: dynamicproperties.GetBoolPropertyFn(false), SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), ReadNoSQLShardFromDataBlob: dynamicproperties.GetBoolPropertyFn(true), } params := pt.TestBaseParams{ DefaultTestCluster: s.DefaultTestCluster, VisibilityTestCluster: s.VisibilityTestCluster, ClusterMetadata: clusterMetadata, DynamicConfiguration: dc, } cluster, err := host.NewCluster(s.T(), s.TestClusterConfig, s.Logger, params) s.Require().NoError(err) s.TestCluster = cluster s.Engine = s.TestCluster.GetFrontendClient() s.AdminClient = s.TestCluster.GetAdminClient() s.DomainName = s.RandomizeStr("integration-test-domain") s.Require().NoError(s.RegisterDomain(s.DomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) time.Sleep(2 * time.Second) s.wfService = s.buildServiceClient() s.wfClient = client.NewClient(s.wfService, s.DomainName, nil) s.taskList = "history-simulation-tasklist" s.worker = worker.New(s.wfService, s.DomainName, s.taskList, worker.Options{}) workflow.RegisterWorker(s.worker) if err := s.worker.Start(); err != nil { s.Logger.Fatal("Error when start worker", tag.Error(err)) } else { s.Logger.Info("Worker started") } } func (s *HistorySimulationSuite) buildServiceClient() workflowserviceclient.Interface { cadenceClientName := "cadence-client" hostPort := "127.0.0.1:7114" // use grpc port if host.TestFlags.FrontendAddr != "" { hostPort = host.TestFlags.FrontendAddr } ch := grpc.NewTransport( grpc.ServerMaxRecvMsgSize(32*1024*1024), grpc.ClientMaxRecvMsgSize(32*1024*1024), ) dispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: cadenceClientName, Outbounds: yarpc.Outbounds{ service.Frontend: {Unary: ch.NewSingleOutbound(hostPort)}, }, }) if err := dispatcher.Start(); err != nil { s.Logger.Fatal("Failed to create outbound transport channel", tag.Error(err)) } cc := dispatcher.ClientConfig(service.Frontend) return compatibility.NewThrift2ProtoAdapter( apiv1.NewDomainAPIYARPCClient(cc), apiv1.NewWorkflowAPIYARPCClient(cc), apiv1.NewWorkerAPIYARPCClient(cc), apiv1.NewVisibilityAPIYARPCClient(cc), ) } func (s *HistorySimulationSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *HistorySimulationSuite) TearDownSuite() { s.worker.Stop() // Sleep for a while to ensure all metrics are emitted/scraped by prometheus time.Sleep(5 * time.Second) s.TearDownBaseSuite() } func (s *HistorySimulationSuite) TestHistorySimulation() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() var runs []client.WorkflowRun for i := 0; i < 100; i++ { // set a short timeout so that timer tasks can be executed before complete workflowOptions := client.StartWorkflowOptions{ TaskList: s.taskList, ExecutionStartToCloseTimeout: 120 * time.Second, DecisionTaskStartToCloseTimeout: 5 * time.Second, } we, err := s.wfClient.ExecuteWorkflow(ctx, workflowOptions, workflow.NoopWorkflow) if err != nil { s.Logger.Fatal("Start workflow with err", tag.Error(err)) } s.NotNil(we) s.True(we.GetRunID() != "") s.Logger.Info("successfully start a workflow", tag.WorkflowID(we.GetID()), tag.WorkflowRunID(we.GetRunID())) runs = append(runs, we) } for _, we := range runs { s.NoError(we.Get(ctx, nil)) } time.Sleep(120 * time.Second) } ================================================ FILE: simulation/history/run.sh ================================================ #!/bin/bash # Cadence History Simulation Test Script # # Usage: # ./simulation/history/run.sh [OPTIONS] # # Examples: # # Run default scenario # ./simulation/history/run.sh --scenario default # # # Run specific scenario # ./simulation/history/run.sh --scenario queuev2 # # # Run with custom timestamp # ./simulation/history/run.sh --scenario default --timestamp 2024-01-15-10-30-00 # # # Run with custom dockerfile # ./simulation/history/run.sh --scenario queuev2 --dockerfile-suffix .local # # TODO: Add simulation/history/README.md. set -eo pipefail show_help() { cat << EOF Cadence History Simulation Test Script USAGE: $0 [OPTIONS] OPTIONS: -s, --scenario SCENARIO Test scenario to run (required) Corresponds to testdata/history_simulation_SCENARIO.yaml -t, --timestamp TIMESTAMP Custom timestamp for test naming (default: current time) Format: YYYY-MM-DD-HH-MM-SS -d, --dockerfile-suffix SUFFIX Dockerfile suffix for custom builds (default: empty) Example: .local for Dockerfile.local -h, --help Show this help message EXAMPLES: # Run default scenario $0 --scenario default # Run specific scenario $0 --scenario queuev2 # Run with custom timestamp $0 --scenario default --timestamp 2024-01-15-10-30-00 # Run with custom dockerfile $0 --scenario queuev2 --dockerfile-suffix .local EOF } # Default values testCase="" timestamp="" dockerFileSuffix="" # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in -s|--scenario) testCase="$2" shift 2 ;; -t|--timestamp) timestamp="$2" shift 2 ;; -d|--dockerfile-suffix) dockerFileSuffix="$2" shift 2 ;; -h|--help) show_help exit 0 ;; --) shift break ;; -*) echo "Unknown option: $1" >&2 echo "Use --help for usage information." >&2 exit 1 ;; *) echo "Unexpected positional argument: $1" >&2 echo "Use --scenario to specify the test scenario." >&2 echo "Use --help for usage information." >&2 exit 1 ;; esac done # Require scenario parameter if [[ -z "$testCase" ]]; then echo "Error: --scenario parameter is required" >&2 echo "" >&2 show_help exit 1 fi # Set default timestamp if not provided if [[ -z "$timestamp" ]]; then timestamp="$(date '+%Y-%m-%d-%H-%M-%S')" fi testCfg="testdata/history_simulation_$testCase.yaml" testName="test-$testCase-$timestamp" resultFolder="history-simulator-output" mkdir -p "$resultFolder" eventLogsFile="$resultFolder/$testName-events.json" testSummaryFile="$resultFolder/$testName-summary.txt" echo "Building test image" DOCKERFILE_SUFFIX=$dockerFileSuffix docker compose -f docker/github_actions/docker-compose-local-history-simulation.yml \ build history-simulator function check_test_failure() { faillog=$(grep 'FAIL: TestHistorySimulationSuite' -B 10 test.log 2>/dev/null || true) timeoutlog=$(grep 'test timed out' test.log 2>/dev/null || true) if [ -z "$faillog" ] && [ -z "$timeoutlog" ]; then echo "Passed" else echo 'Test failed!!!' echo "Fail log: $faillog" echo "Timeout log: $timeoutlog" echo "Check test.log file for more details" exit 1 fi } trap check_test_failure EXIT echo "Running the test $testCase" DOCKERFILE_SUFFIX=$dockerFileSuffix docker compose \ -f docker/github_actions/docker-compose-local-history-simulation.yml \ run -e HISTORY_SIMULATION_CONFIG=$testCfg --rm --remove-orphans --service-ports --use-aliases \ history-simulator \ | grep -a --line-buffered "History New Event" \ | sed "s/History New Event: //" \ | jq . > "$eventLogsFile" echo "---- Simulation Summary ----" format_counts() { event_name="$1" header="$2" echo "$header:" jq -r --arg event "$event_name" ' select(.EventName == $event) | "\(.ShardID)|\(.Payload.task_category)|\(.Payload.task_type)" ' "$eventLogsFile" | sort | uniq -c | sort -k2,2n -k3,3 -k4,4 | awk ' BEGIN { last_shard = "__unset__" } { count = $1 sub(/^[ \t]*[0-9]+[ \t]*/, "", $0) split($0, parts, "|") shard = parts[1] category = parts[2] type = parts[3] if (shard != last_shard) { print "ShardID: " shard last_shard = shard } printf " TaskCategory: %s, TaskType: %s, Count: %d\n", category, type, count }' echo "" } format_counts "Create History Task" "Tasks created" | tee -a "$testSummaryFile" format_counts "Execute History Task" "Tasks executed" | tee -a "$testSummaryFile" create_tasks=$(jq -r ' select(.EventName == "Create History Task") | "\(.ShardID)|\(.Payload.task_category)|\(.Payload.task_type)|\(.Payload.task_key.taskID)|\(.Payload.task_key.scheduledTime)"' "$eventLogsFile" ) execute_tasks=$(jq -r ' select(.EventName == "Execute History Task") | "\(.ShardID)|\(.Payload.task_category)|\(.Payload.task_type)|\(.Payload.task_key.taskID)|\(.Payload.task_key.scheduledTime)"' "$eventLogsFile" ) missing=$(echo "$create_tasks" | while IFS= read -r line; do if ! echo "$execute_tasks" | grep -Fxq "$line"; then echo "$line" fi done) echo "Tasks that were created but not executed:" | tee -a "$testSummaryFile" # Group and print nicely echo "$missing" | sort -t '|' -k1,1n -k2,2 -k3,3n -k4,4n | awk -F'|' ' BEGIN { last_key = "__none__"; last_shard = "__none__"} { shard = $1 category = $2 type = $3 taskid = $4 sched = $5 key = shard "|" category "|" type if (key != last_key) { if (shard != last_shard) { print "ShardID: " shard last_shard = shard } print " TaskCategory: " category ", TaskType: " type last_key = key } print " TaskID: " taskid ", ScheduledTime: " sched }' | tee -a "$testSummaryFile" printf "\nResults are saved in %s\n" "$testSummaryFile" printf "For further ad-hoc analysis, please check %s via jq queries\n" "$eventLogsFile" printf "Visit http://localhost:3000/ to view Cadence History grafana dashboard\n" ================================================ FILE: simulation/history/testdata/history_simulation_default.yaml ================================================ enablearchival: false clusterno: 0 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enableasyncwfconsumer: false enablearchiver: false enablereplicator: false enableindexer: false dynamicclientconfig: filepath: "dynamicconfig/default.yaml" pollInterval: "10s" ================================================ FILE: simulation/history/testdata/history_simulation_queuev2.yaml ================================================ enablearchival: false clusterno: 0 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enableasyncwfconsumer: false enablearchiver: false enablereplicator: false enableindexer: false dynamicclientconfig: filepath: "dynamicconfig/queuev2.yaml" pollInterval: "10s" ================================================ FILE: simulation/history/testdata/history_simulation_queuev2_split.yaml ================================================ enablearchival: false clusterno: 0 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 1 workerconfig: enableasyncwfconsumer: false enablearchiver: false enablereplicator: false enableindexer: false dynamicclientconfig: filepath: "dynamicconfig/queuev2_split.yaml" pollInterval: "10s" ================================================ FILE: simulation/history/workflow/workflow.go ================================================ package workflow import ( "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" ) func RegisterWorker(w worker.Registry) { w.RegisterWorkflow(NoopWorkflow) } func NoopWorkflow(ctx workflow.Context) error { return nil } ================================================ FILE: simulation/matching/comparison/README.md ================================================ This tool runs a set of matching simulation tests, extracts stats from their output and generates a csv to compare them easily. Note: The parsing logic might break in the future if the `simulation/matching/run.sh` starts spitting different shaped lines. Alternative is to load all the event logs into a sqlite table and then run queries on top instead of parsing outputs of jq in this tool. Run all the scenarios and compare: ``` go run simulation/matching/comparison/*.go ``` Run subset of scenarios and compare: ``` go run simulation/matching/comparison/*.go \ --scenarios "fluctuating" ``` If you have already run some scenarios before and made changes in the csv output then run in Compare mode ``` go run simulation/matching/comparison/*.go \ --ts 2024-11-27-21-29-55 \ --mode Compare ``` ================================================ FILE: simulation/matching/comparison/main.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package main import ( "bytes" "encoding/csv" "flag" "fmt" "log" "os" "os/exec" "path" "regexp" "sort" "strings" "time" ) var ( scenarioFilter = flag.String("scenarios", ".*", "Regex to filter the tests to execute by name") mode = flag.String("mode", "RunAndCompare", "Mode to run the tool in. Options: RunAndCompare, Compare. "+ "RunAndCompare runs the simulation and compares the results. "+ "Compare only compares the results of previously run results. Compare requires --ts flag to be set.") timestamp = flag.String("ts", "", "Timestamp of the simulation run to compare in following format '2006-01-02-15-04-05'. Required when mode is Compare") ) const ( outputFolder = "matching-simulator-output" ) var ( simNameRegex = regexp.MustCompile(`matching_simulation_(?P.*).yaml`) // oneLineStatRegex extracts key value pairs that represent a single line stat from the summary file // e.g. Max Task latency (ms): 8273 // // See https://regex101.com/r/ZqeiWC/1 for example matches oneLineStatRegex = regexp.MustCompile(`^(?P[a-zA-Z]+[a-zA-Z0-9\s\(\)]+):[\s]+(?P[+-]?[0-9]*[.]?[0-9]+[s]?)$`) // multiLineStatRegex matches with the start of a multi line stat in the summary file. // Subsequent lines that start with a space are part of the same stat and parsed in the code. // e.g. // Per tasklist sync matches: // 179 "/__cadence_sys/my-tasklist/1" // 375 "/__cadence_sys/my-tasklist/2" // 470 "/__cadence_sys/my-tasklist/3" // 3222 "my-tasklist" // // See https://regex101.com/r/Un3yLo/1 for example matches multiLineStatRegex = regexp.MustCompile(`(?m)^(?P[a-zA-Z]+[a-zA-Z0-9\s\(\)]+):[\s]?$`) ) func main() { validateAndParseFlags() root := mustGetRootDir() scenarios := mustGetSimulationScenarios(root) ts := time.Now().UTC().Format("2006-01-02-15-04-05") if *timestamp != "" { ts = *timestamp } if *mode == "RunAndCompare" { for _, scenario := range scenarios { mustRunScenario(root, scenario, ts) } } mustGenerateReports(root, scenarios, ts) } func mustGenerateReports(root string, scenarios []string, ts string) { // outer key is scenario name, inner key is stat name, value is stat value csvData := make(map[string]map[string]string) var missingScenarios []string var missingScenariosReasons []string for _, scenario := range scenarios { if reason, ok := scenarioHasRun(root, scenario, ts); !ok { missingScenarios = append(missingScenarios, scenario) missingScenariosReasons = append(missingScenariosReasons, reason) continue } summaryFile := scenarioSummaryFile(root, scenario, ts) csvData[scenario] = mustParseSummaryFile(summaryFile, scenario) } if len(missingScenarios) == len(scenarios) { log.Fatalf("No simulation results found for any of the scenarios for timestamp: %s, reasons:\n%s", ts, strings.Join(missingScenariosReasons, "\n")) } allStatKeys := mustGetAllStatKeys(csvData) headers := append([]string{"scenario"}, allStatKeys...) var data [][]string outputFile := csvFilePath(root) writer := mustNewCSVWriter(csvFilePath(root)) for scenario, stats := range csvData { row := []string{scenario} for _, key := range allStatKeys { row = append(row, stats[key]) } data = append(data, row) } writer.Write(headers) for _, row := range data { writer.Write(row) } writer.Flush() if err := writer.Error(); err != nil { log.Fatalf("Error writing to output file, err: %v", err) } fmt.Printf("Comparison CSV generated at: %s\n", outputFile) } func csvFilePath(root string) string { return path.Join(root, outputFolder, "comparison.csv") } func mustGetAllStatKeys(csvData map[string]map[string]string) []string { allStatKeys := make(map[string]bool) for _, stats := range csvData { for k := range stats { allStatKeys[k] = true } } var allStatKeysSlice []string for k := range allStatKeys { allStatKeysSlice = append(allStatKeysSlice, k) } sort.Strings(allStatKeysSlice) return allStatKeysSlice } func mustParseSummaryFile(path, scenario string) map[string]string { content, err := os.ReadFile(path) if err != nil { log.Fatalf("Could not read file %s, err: %v", path, err) } strContent := string(content) stats := make(map[string]string) // extract one line stats for _, line := range strings.Split(strContent, "\n") { matches := oneLineStatRegex.FindStringSubmatch(line) if len(matches) == 0 { continue } stats[matches[oneLineStatRegex.SubexpIndex("key")]] = matches[oneLineStatRegex.SubexpIndex("val")] } fmt.Printf("Scenario %q has %d oneline stats\n", scenario, len(stats)) // extract multi line stats indices := multiLineStatRegex.FindAllStringIndex(strContent, -1) fmt.Printf("Scenario %q has %d multiline stats\n", scenario, len(indices)) for _, idx := range indices { start := idx[0] end := idx[1] key := strContent[start:end] // value is all lines that start with a space after the key var b bytes.Buffer rest := strContent[end:] for i := strings.Index(rest, "\n"); i < len(rest); i++ { if rest[i] == '\n' && i+1 < len(rest) && rest[i+1] != ' ' { break } b.WriteByte(rest[i]) } stats[key] = b.String() } return stats } func mustNewCSVWriter(outputFile string) *csv.Writer { file, err := os.Create(outputFile) if err != nil { log.Fatalf("Could not create output file, err: %v", err) } return csv.NewWriter(file) } func scenarioSummaryFile(root, scenario, ts string) string { // e.g. matching-simulator-output/test-default-2024-09-12-18-16-44-summary.txt return path.Join(root, fmt.Sprintf("matching-simulator-output/test-%s-%s-summary.txt", scenario, ts)) } func scenarioRawEventsFile(root, scenario, ts string) string { // e.g. matching-simulator-output/test-default-2024-09-12-18-16-44-events.json return path.Join(root, fmt.Sprintf("matching-simulator-output/test-%s-%s-events.json", scenario, ts)) } func mustRunScenario(root, scenario, ts string) { if _, ok := scenarioHasRun(root, scenario, ts); ok { fmt.Printf("Scenario %s already ran for timestamp %s, skipping\n", scenario, ts) return } fmt.Printf("Running scenario: %s\n", scenario) start := time.Now() cmd := exec.Command("bash", path.Join(root, "simulation/matching/run.sh"), "--scenario", scenario, "--timestamp", ts) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() if err != nil { log.Fatalf("Could not run scenario %s, err: %v,\n-----stdout:-----\n%s\n----stderr:----\n%s\nMore details can be found in test.log file.\n", scenario, err, stdout.String(), stderr.String()) } fmt.Printf("Finished running scenario: %s in %v seconds\n", scenario, time.Since(start).Seconds()) } func mustGetSimulationScenarios(root string) []string { path := path.Join(root, "simulation/matching/testdata") entries, err := os.ReadDir(path) if err != nil { log.Fatal(err) } scenarioFilterRegex, err := regexp.Compile(*scenarioFilter) if err != nil { log.Fatalf("Error parsing scenarios flag: %v", err) } var scenarios []string for _, entry := range entries { if entry.IsDir() { continue } matches := simNameRegex.FindStringSubmatch(entry.Name()) if len(matches) == 0 { continue } scenarioName := matches[simNameRegex.SubexpIndex("name")] if scenarioFilterRegex.MatchString(scenarioName) { scenarios = append(scenarios, scenarioName) } else { log.Printf("Skipping %s due to scenario filter", scenarioName) } } fmt.Println("Simulation scenarios found: \n ", strings.Join(scenarios, "\n ")) return scenarios } func validateAndParseFlags() { flag.Parse() if *mode != "RunAndCompare" && *mode != "Compare" { fmt.Println("--mode must be RunAndCompare or Compare") os.Exit(1) } if *mode == "Compare" && *timestamp == "" { fmt.Println("--ts is required when mode is Compare") os.Exit(1) } } func scenarioHasRun(root, scenario, ts string) (string, bool) { // check if summary file exists and complete summaryFilePath := scenarioSummaryFile(root, scenario, ts) _, err := os.Stat(summaryFilePath) if os.IsNotExist(err) { return fmt.Sprintf("summary file %s doesn't exist", summaryFilePath), false } content, err := os.ReadFile(summaryFilePath) if err != nil { log.Fatalf("Could not read summary file %s, err: %v", summaryFilePath, err) } if !strings.Contains(string(content), "End of summary") { return fmt.Sprintf("summary file %s doesn't contain 'End of summary'", summaryFilePath), false } // check if raw events file exists eventFilePath := scenarioRawEventsFile(root, scenario, ts) _, err = os.Stat(eventFilePath) if os.IsNotExist(err) { return fmt.Sprintf("event file %s doesn't exist", eventFilePath), false } return "", true } func mustGetRootDir() string { root, err := os.Getwd() if err != nil { log.Fatalf("Could not get executable path, err: %v", err) } return root } ================================================ FILE: simulation/matching/matching_simulation_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. /* To run locally: 1. Pick a scenario from the existing config files simulation/matching/testdata/matching_simulation_.*.yaml or add a new one 2. Run the scenario `./simulation/matching/run.sh default` Full test logs can be found at test.log file. Event json logs can be found at matching-simulator-output folder. See the run.sh script for more details about how to parse events. If you want to run multiple scenarios and compare them refer to simulation/matching/comparison/README.md */ package matching import ( "context" "errors" "flag" "fmt" "math/rand" "os" "reflect" "sort" "strings" "sync" "sync/atomic" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "golang.org/x/exp/slices" "golang.org/x/time/rate" "github.com/uber/cadence/client/history" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/isolationgroup" "github.com/uber/cadence/common/persistence" pt "github.com/uber/cadence/common/persistence/persistence-tests" "github.com/uber/cadence/common/types" "github.com/uber/cadence/host" _ "github.com/uber/cadence/common/asyncworkflow/queue/kafka" // needed to load kafka asyncworkflow queue ) type operation string const ( operationPollForDecisionTask operation = "PollForDecisionTask" operationPollReceivedTask operation = "PollReceivedTask" operationAddDecisionTask operation = "AddDecisionTask" defaultTestCase = "testdata/matching_simulation_default.yaml" ) type operationStats struct { op operation dur time.Duration err error timestamp time.Time } type operationAggStats struct { successCnt int failCnt int totalDuration time.Duration maxDuration time.Duration lastUpdated time.Time } type MatchingSimulationSuite struct { *require.Assertions *host.IntegrationBase } func TestMatchingSimulation(t *testing.T) { flag.Parse() confPath := os.Getenv("MATCHING_SIMULATION_CONFIG") if confPath == "" { confPath = defaultTestCase } clusterConfig, err := host.GetTestClusterConfig(confPath) if err != nil { t.Fatalf("failed creating cluster config from %s, err: %v", confPath, err) } isolationGroups := getIsolationGroups(clusterConfig.MatchingConfig.SimulationConfig) clusterConfig.MatchingDynamicConfigOverrides = map[dynamicproperties.Key]interface{}{ dynamicproperties.MatchingNumTasklistWritePartitions: getPartitions(clusterConfig.MatchingConfig.SimulationConfig.TaskListWritePartitions), dynamicproperties.MatchingNumTasklistReadPartitions: getPartitions(clusterConfig.MatchingConfig.SimulationConfig.TaskListReadPartitions), dynamicproperties.MatchingForwarderMaxOutstandingPolls: getForwarderMaxOutstandingPolls(clusterConfig.MatchingConfig.SimulationConfig.ForwarderMaxOutstandingPolls), dynamicproperties.MatchingForwarderMaxOutstandingTasks: getForwarderMaxOutstandingTasks(clusterConfig.MatchingConfig.SimulationConfig.ForwarderMaxOutstandingTasks), dynamicproperties.MatchingForwarderMaxRatePerSecond: getForwarderMaxRPS(clusterConfig.MatchingConfig.SimulationConfig.ForwarderMaxRatePerSecond), dynamicproperties.MatchingForwarderMaxChildrenPerNode: getForwarderMaxChildPerNode(clusterConfig.MatchingConfig.SimulationConfig.ForwarderMaxChildrenPerNode), dynamicproperties.LocalPollWaitTime: clusterConfig.MatchingConfig.SimulationConfig.LocalPollWaitTime, dynamicproperties.LocalTaskWaitTime: clusterConfig.MatchingConfig.SimulationConfig.LocalTaskWaitTime, dynamicproperties.EnableTasklistIsolation: len(isolationGroups) > 0, dynamicproperties.AllIsolationGroups: isolationGroups, dynamicproperties.TasklistLoadBalancerStrategy: getTasklistLoadBalancerStrategy(clusterConfig.MatchingConfig.SimulationConfig.TasklistLoadBalancerStrategy), dynamicproperties.MatchingEnableGetNumberOfPartitionsFromCache: clusterConfig.MatchingConfig.SimulationConfig.GetPartitionConfigFromDB, dynamicproperties.MatchingEnableAdaptiveScaler: clusterConfig.MatchingConfig.SimulationConfig.EnableAdaptiveScaler, dynamicproperties.MatchingPartitionDownscaleFactor: clusterConfig.MatchingConfig.SimulationConfig.PartitionDownscaleFactor, dynamicproperties.MatchingPartitionUpscaleRPS: clusterConfig.MatchingConfig.SimulationConfig.PartitionUpscaleRPS, dynamicproperties.MatchingPartitionUpscaleSustainedDuration: clusterConfig.MatchingConfig.SimulationConfig.PartitionUpscaleSustainedDuration, dynamicproperties.MatchingPartitionDownscaleSustainedDuration: clusterConfig.MatchingConfig.SimulationConfig.PartitionDownscaleSustainedDuration, dynamicproperties.MatchingAdaptiveScalerUpdateInterval: clusterConfig.MatchingConfig.SimulationConfig.AdaptiveScalerUpdateInterval, dynamicproperties.MatchingQPSTrackerInterval: getQPSTrackerInterval(clusterConfig.MatchingConfig.SimulationConfig.QPSTrackerInterval), dynamicproperties.TaskIsolationDuration: clusterConfig.MatchingConfig.SimulationConfig.TaskIsolationDuration, } ctrl := gomock.NewController(t) mockHistoryCl := history.NewMockClient(ctrl) mockHistoryCl.EXPECT().RecordDecisionTaskStarted(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, req *types.RecordDecisionTaskStartedRequest, opts ...yarpc.CallOption) (*types.RecordDecisionTaskStartedResponse, error) { time.Sleep(getRecordDecisionTaskStartedTime(clusterConfig.MatchingConfig.SimulationConfig.RecordDecisionTaskStartedTime)) return &types.RecordDecisionTaskStartedResponse{ ScheduledEventID: req.ScheduleID, }, nil }).AnyTimes() clusterConfig.HistoryConfig.MockClient = mockHistoryCl testCluster := host.NewPersistenceTestCluster(t, clusterConfig) s := new(MatchingSimulationSuite) params := host.IntegrationBaseParams{ DefaultTestCluster: testCluster, VisibilityTestCluster: testCluster, TestClusterConfig: clusterConfig, } s.IntegrationBase = host.NewIntegrationBase(params) suite.Run(t, s) } func (s *MatchingSimulationSuite) SetupSuite() { s.SetupLogger() s.Logger.Info("Running integration test against test cluster") clusterMetadata := host.NewClusterMetadata(s.T(), s.TestClusterConfig) dc := persistence.DynamicConfiguration{ EnableCassandraAllConsistencyLevelDelete: dynamicproperties.GetBoolPropertyFn(true), PersistenceSampleLoggingRate: dynamicproperties.GetIntPropertyFn(100), EnableShardIDMetrics: dynamicproperties.GetBoolPropertyFn(true), EnableHistoryTaskDualWriteMode: dynamicproperties.GetBoolPropertyFn(true), SerializationEncoding: dynamicproperties.GetStringPropertyFn(string(constants.EncodingTypeThriftRW)), } params := pt.TestBaseParams{ DefaultTestCluster: s.DefaultTestCluster, VisibilityTestCluster: s.VisibilityTestCluster, ClusterMetadata: clusterMetadata, DynamicConfiguration: dc, } cluster, err := host.NewCluster(s.T(), s.TestClusterConfig, s.Logger, params) s.Require().NoError(err) s.TestCluster = cluster s.Engine = s.TestCluster.GetFrontendClient() s.AdminClient = s.TestCluster.GetAdminClient() s.DomainName = s.RandomizeStr("integration-test-domain") s.Require().NoError(s.RegisterDomain(s.DomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) s.SecondaryDomainName = s.RandomizeStr("unused-test-domain") s.Require().NoError(s.RegisterDomain(s.SecondaryDomainName, 1, types.ArchivalStatusDisabled, "", types.ArchivalStatusDisabled, "", nil)) time.Sleep(2 * time.Second) } func (s *MatchingSimulationSuite) SetupTest() { s.Assertions = require.New(s.T()) } func (s *MatchingSimulationSuite) TearDownSuite() { // Sleep for a while to ensure all metrics are emitted/scraped by prometheus time.Sleep(5 * time.Second) s.TearDownBaseSuite() } func (s *MatchingSimulationSuite) TestMatchingSimulation() { matchingClients := s.TestCluster.GetMatchingClients() ctx, cancel := context.WithCancel(context.Background()) domainID := s.domainID(ctx) tasklist := "my-tasklist" if s.TestClusterConfig.MatchingConfig.SimulationConfig.GetPartitionConfigFromDB && !s.TestClusterConfig.MatchingConfig.SimulationConfig.EnableAdaptiveScaler { _, err := s.TestCluster.GetMatchingClient().UpdateTaskListPartitionConfig(ctx, &types.MatchingUpdateTaskListPartitionConfigRequest{ DomainUUID: domainID, TaskList: &types.TaskList{Name: tasklist, Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: getPartitions(s.TestClusterConfig.MatchingConfig.SimulationConfig.TaskListReadPartitions), WritePartitions: getPartitions(s.TestClusterConfig.MatchingConfig.SimulationConfig.TaskListWritePartitions), }, }) s.NoError(err) } // Start stat collector statsCh := make(chan *operationStats, 200000) aggStats := make(map[operation]*operationAggStats) var collectorWG sync.WaitGroup collectorWG.Add(1) go s.collectStats(statsCh, aggStats, &collectorWG) totalTaskCount := getTotalTasks(s.TestClusterConfig.MatchingConfig.SimulationConfig.Tasks) seed := time.Now().UnixNano() rand.Seed(seed) totalBacklogCount := 0 for idx, backlogConfig := range s.TestClusterConfig.MatchingConfig.SimulationConfig.Backlogs { totalBacklogCount += backlogConfig.BacklogCount partition := getPartitionTaskListName(tasklist, backlogConfig.Partition) for i := 0; i < backlogConfig.BacklogCount; i++ { isolationGroup := "" if len(backlogConfig.IsolationGroups) > 0 { isolationGroup = randomlyPickKey(backlogConfig.IsolationGroups) } decisionTask := newDecisionTask(domainID, partition, isolationGroup, idx) reqCtx, cancel := context.WithTimeout(ctx, 2*time.Second) _, err := matchingClients[0].AddDecisionTask(reqCtx, decisionTask) cancel() if err != nil { s.log("Error when adding decision task, err: %v", err) } } } // Start pollers numPollers := 0 var tasksToReceive sync.WaitGroup tasksToReceive.Add(totalTaskCount + totalBacklogCount) var pollerWG sync.WaitGroup for idx, pollerConfig := range s.TestClusterConfig.MatchingConfig.SimulationConfig.Pollers { for i := 0; i < getNumPollers(pollerConfig); i++ { numPollers++ pollerWG.Add(1) pollerID := fmt.Sprintf("[%d]-%s-%d", idx, getIsolationGroup(pollerConfig), i) config := pollerConfig go s.poll(ctx, matchingClients[i%len(matchingClients)], domainID, tasklist, pollerID, &pollerWG, statsCh, &tasksToReceive, config) } } // wait a bit for pollers to start. time.Sleep(300 * time.Millisecond) startTime := time.Now() // Start task generators numGenerators := 0 var generatorWG sync.WaitGroup lastTaskScheduleID := int32(0) for _, taskConfig := range s.TestClusterConfig.MatchingConfig.SimulationConfig.Tasks { tasksGenerated := int32(0) rateLimiter := newSimulationRateLimiter(taskConfig, startTime, clock.NewRealTimeSource(), s.log) for i := 0; i < getNumTaskGenerators(taskConfig); i++ { numGenerators++ generatorWG.Add(1) config := taskConfig go s.generate( ctx, matchingClients[i%len(matchingClients)], domainID, tasklist, &tasksGenerated, &lastTaskScheduleID, &generatorWG, statsCh, config, rateLimiter, ) } } // Let it run until all tasks have been polled. // There's a test timeout configured in docker/github_actions/docker-compose-local-matching-simulation.yml that you // can change if your test case needs more time s.log("Waiting until all tasks are received") tasksToReceive.Wait() executionTime := time.Since(startTime) s.log("Completed benchmark in %v", executionTime) s.log("Canceling context to stop pollers and task generators") cancel() pollerWG.Wait() s.log("Pollers stopped") generatorWG.Wait() s.log("Generators stopped") s.log("Stopping stats collector") close(statsCh) collectorWG.Wait() s.log("Stats collector stopped") // Print the test summary. // Don't change the start/end line format as it is used by scripts to parse the summary info testSummary := []string{} testSummary = append(testSummary, "Simulation Summary:") testSummary = append(testSummary, fmt.Sprintf("Random seed: %v", seed)) testSummary = append(testSummary, fmt.Sprintf("Task generate Duration: %v", aggStats[operationAddDecisionTask].lastUpdated.Sub(startTime))) testSummary = append(testSummary, fmt.Sprintf("Simulation Duration: %v", executionTime)) testSummary = append(testSummary, fmt.Sprintf("Num of Pollers: %d", numPollers)) testSummary = append(testSummary, fmt.Sprintf("Num of Task Generators: %d", numGenerators)) testSummary = append(testSummary, fmt.Sprintf("Record Decision Task Started Time: %v", s.TestClusterConfig.MatchingConfig.SimulationConfig.RecordDecisionTaskStartedTime)) testSummary = append(testSummary, fmt.Sprintf("Num of Write Partitions: %d", s.TestClusterConfig.MatchingDynamicConfigOverrides[dynamicproperties.MatchingNumTasklistWritePartitions])) testSummary = append(testSummary, fmt.Sprintf("Num of Read Partitions: %d", s.TestClusterConfig.MatchingDynamicConfigOverrides[dynamicproperties.MatchingNumTasklistReadPartitions])) testSummary = append(testSummary, fmt.Sprintf("Get Num of Partitions from DB: %v", s.TestClusterConfig.MatchingDynamicConfigOverrides[dynamicproperties.MatchingEnableGetNumberOfPartitionsFromCache])) testSummary = append(testSummary, fmt.Sprintf("Tasklist load balancer strategy: %v", s.TestClusterConfig.MatchingDynamicConfigOverrides[dynamicproperties.TasklistLoadBalancerStrategy])) testSummary = append(testSummary, fmt.Sprintf("Forwarder Max Outstanding Polls: %d", s.TestClusterConfig.MatchingDynamicConfigOverrides[dynamicproperties.MatchingForwarderMaxOutstandingPolls])) testSummary = append(testSummary, fmt.Sprintf("Forwarder Max Outstanding Tasks: %d", s.TestClusterConfig.MatchingDynamicConfigOverrides[dynamicproperties.MatchingForwarderMaxOutstandingTasks])) testSummary = append(testSummary, fmt.Sprintf("Forwarder Max RPS: %d", s.TestClusterConfig.MatchingDynamicConfigOverrides[dynamicproperties.MatchingForwarderMaxRatePerSecond])) testSummary = append(testSummary, fmt.Sprintf("Forwarder Max Children per Node: %d", s.TestClusterConfig.MatchingDynamicConfigOverrides[dynamicproperties.MatchingForwarderMaxChildrenPerNode])) testSummary = append(testSummary, fmt.Sprintf("Local Poll Wait Time: %v", s.TestClusterConfig.MatchingDynamicConfigOverrides[dynamicproperties.LocalPollWaitTime])) testSummary = append(testSummary, fmt.Sprintf("Local Task Wait Time: %v", s.TestClusterConfig.MatchingDynamicConfigOverrides[dynamicproperties.LocalTaskWaitTime])) testSummary = append(testSummary, fmt.Sprintf("Tasks generated: %d", aggStats[operationAddDecisionTask].successCnt)) testSummary = append(testSummary, fmt.Sprintf("Tasks polled: %d", aggStats[operationPollReceivedTask].successCnt)) testSummary = appendMetric(testSummary, operationPollForDecisionTask, aggStats) testSummary = appendMetric(testSummary, operationAddDecisionTask, aggStats) testSummary = append(testSummary, "End of Simulation Summary") fmt.Println(strings.Join(testSummary, "\n")) } func (s *MatchingSimulationSuite) log(msg string, args ...interface{}) { msg = time.Now().Format(time.RFC3339Nano) + "\t" + msg s.T().Logf(msg, args...) } func (s *MatchingSimulationSuite) generate( ctx context.Context, matchingClient host.MatchingClient, domainID, tasklist string, tasksGenerated *int32, lastTaskScheduleID *int32, wg *sync.WaitGroup, statsCh chan *operationStats, taskConfig host.SimulationTaskConfiguration, rateLimiter *simulationRateLimiter, ) { defer wg.Done() for { select { case <-ctx.Done(): s.log("Generator done") return default: if err := rateLimiter.Wait(ctx); err != nil { if !errors.Is(err, context.Canceled) { s.T().Error("Rate limiter failed: ", err) } return } newTasksGenerated := int(atomic.AddInt32(tasksGenerated, 1)) if newTasksGenerated > getMaxTasksToGenerate(taskConfig) { s.log("Generated %d tasks so generator will stop", newTasksGenerated) return } isolationGroup := "" if igs := getTaskIsolationGroups(taskConfig); len(igs) > 0 { isolationGroup = igs[newTasksGenerated%len(igs)] } scheduleID := int(atomic.AddInt32(lastTaskScheduleID, 1)) start := time.Now() decisionTask := newDecisionTask(domainID, tasklist, isolationGroup, scheduleID) reqCtx, cancel := context.WithTimeout(ctx, 2*time.Second) _, err := matchingClient.AddDecisionTask(reqCtx, decisionTask) statsCh <- &operationStats{ op: operationAddDecisionTask, dur: time.Since(start), err: err, timestamp: time.Now(), } cancel() if err != nil { s.log("Error when adding decision task, err: %v", err) continue } } } } func (s *MatchingSimulationSuite) poll( ctx context.Context, matchingClient host.MatchingClient, domainID, tasklist, pollerID string, wg *sync.WaitGroup, statsCh chan *operationStats, tasksToReceive *sync.WaitGroup, pollerConfig host.SimulationPollerConfiguration, ) { defer wg.Done() for { select { case <-ctx.Done(): s.log("Poller done") return default: s.log("Poller will initiate a poll") reqCtx, cancel := context.WithTimeout(ctx, getPollTimeout(pollerConfig)) start := time.Now() resp, err := matchingClient.PollForDecisionTask(reqCtx, &types.MatchingPollForDecisionTaskRequest{ DomainUUID: domainID, PollerID: pollerID, PollRequest: &types.PollForDecisionTaskRequest{ TaskList: &types.TaskList{ Name: tasklist, Kind: types.TaskListKindNormal.Ptr(), }, Identity: pollerID, }, IsolationGroup: getIsolationGroup(pollerConfig), }) cancel() statsCh <- &operationStats{ op: operationPollForDecisionTask, dur: time.Since(start), err: err, timestamp: time.Now(), } if err != nil { s.log("PollForDecisionTask failed: %v", err) continue } empty := &types.MatchingPollForDecisionTaskResponse{} if reflect.DeepEqual(empty, resp) { s.log("PollForDecisionTask response is empty") continue } statsCh <- &operationStats{ op: operationPollReceivedTask, timestamp: time.Now(), } s.log("PollForDecisionTask got a task with startedid: %d. resp: %+v", resp.StartedEventID, resp) tasksToReceive.Done() time.Sleep(getTaskProcessTime(pollerConfig)) } } } func (s *MatchingSimulationSuite) collectStats(statsCh chan *operationStats, aggStats map[operation]*operationAggStats, wg *sync.WaitGroup) { defer wg.Done() for stat := range statsCh { opAggStats, ok := aggStats[stat.op] if !ok { opAggStats = &operationAggStats{} aggStats[stat.op] = opAggStats } if stat.timestamp.After(opAggStats.lastUpdated) { opAggStats.lastUpdated = stat.timestamp } if stat.err != nil { opAggStats.failCnt++ } else { opAggStats.successCnt++ } opAggStats.totalDuration += stat.dur if stat.dur > opAggStats.maxDuration { opAggStats.maxDuration = stat.dur } } s.log("Stats collector done") } func (s *MatchingSimulationSuite) domainID(ctx context.Context) string { reqCtx, cancel := context.WithTimeout(ctx, 250*time.Millisecond) defer cancel() domainDesc, err := s.TestCluster.GetFrontendClient().DescribeDomain(reqCtx, &types.DescribeDomainRequest{ Name: &s.DomainName, }) s.Require().NoError(err, "Error when describing domain") domainID := domainDesc.GetDomainInfo().UUID s.T().Logf("DomainID: %s", domainID) return domainID } func newDecisionTask(domainID, tasklist, isolationGroup string, i int) *types.AddDecisionTaskRequest { return &types.AddDecisionTaskRequest{ DomainUUID: domainID, Execution: &types.WorkflowExecution{ WorkflowID: "test-workflow-id", RunID: uuid.New(), }, TaskList: &types.TaskList{ Name: tasklist, Kind: types.TaskListKindNormal.Ptr(), }, ScheduleID: int64(i), PartitionConfig: map[string]string{ isolationgroup.GroupKey: isolationGroup, }, } } func appendMetric(testSummary []string, op operation, aggStats map[operation]*operationAggStats) []string { total := 0 if pollStats, ok := aggStats[op]; ok { total = pollStats.successCnt + pollStats.failCnt } testSummary = append(testSummary, fmt.Sprintf("Operation Summary (%v): ", op)) if total == 0 { testSummary = append(testSummary, " N/A") } else { testSummary = append(testSummary, fmt.Sprintf(" Total: %d", total)) testSummary = append(testSummary, fmt.Sprintf(" Failure rate %%: %d", 100*aggStats[op].failCnt/total)) testSummary = append(testSummary, fmt.Sprintf(" Avg duration (ms): %d", (aggStats[op].totalDuration/time.Duration(total)).Milliseconds())) testSummary = append(testSummary, fmt.Sprintf(" Max duration (ms): %d", aggStats[op].maxDuration.Milliseconds())) } return testSummary } func getTotalTasks(tasks []host.SimulationTaskConfiguration) int { total := 0 for _, taskConfiguration := range tasks { total += getMaxTasksToGenerate(taskConfiguration) } return total } func getIsolationGroups(c host.MatchingSimulationConfig) []any { groups := make(map[string]struct{}) for _, poller := range c.Pollers { if getIsolationGroup(poller) != "" { groups[getIsolationGroup(poller)] = struct{}{} } } for _, task := range c.Tasks { for _, group := range getTaskIsolationGroups(task) { groups[group] = struct{}{} } } var uniqueGroups []any for group := range groups { uniqueGroups = append(uniqueGroups, group) } return uniqueGroups } func getNumTaskGenerators(c host.SimulationTaskConfiguration) int { if c.NumTaskGenerators == 0 { return 1 } return c.NumTaskGenerators } func getMaxTasksToGenerate(c host.SimulationTaskConfiguration) int { if c.MaxTaskToGenerate == 0 { return 2000 } return c.MaxTaskToGenerate } func getTasksPerSecond(c host.SimulationTaskConfiguration) int { if c.TasksPerSecond == 0 { return 40 } return c.TasksPerSecond } func getTasksBurst(c host.SimulationTaskConfiguration) int { if c.TasksBurst == 0 { return 1 } return c.TasksBurst } func getTaskIsolationGroups(c host.SimulationTaskConfiguration) []string { return c.IsolationGroups } func getPartitions(i int) map[int]*types.TaskListPartition { if i == 0 { return map[int]*types.TaskListPartition{ 0: {}, } } result := make(map[int]*types.TaskListPartition) for j := 0; j < i; j++ { result[j] = &types.TaskListPartition{} } return result } func getForwarderMaxOutstandingPolls(i int) int { return i } func getForwarderMaxOutstandingTasks(i int) int { return i } func getForwarderMaxRPS(i int) int { if i == 0 { return 10 } return i } func getForwarderMaxChildPerNode(i int) int { if i == 0 { return 20 } return i } func getNumPollers(c host.SimulationPollerConfiguration) int { if c.NumPollers == 0 { return 1 } return c.NumPollers } func getTaskProcessTime(c host.SimulationPollerConfiguration) time.Duration { if c.TaskProcessTime == 0 { return time.Millisecond } return c.TaskProcessTime } func getPollTimeout(c host.SimulationPollerConfiguration) time.Duration { if c.PollTimeout == 0 { return 15 * time.Second } return c.PollTimeout } func getIsolationGroup(c host.SimulationPollerConfiguration) string { return c.IsolationGroup } func getRecordDecisionTaskStartedTime(duration time.Duration) time.Duration { if duration == 0 { return time.Millisecond } return duration } func getTasklistLoadBalancerStrategy(strategy string) string { if strategy == "" { return "random" } return strategy } func getPartitionTaskListName(root string, partition int) string { if partition <= 0 { return root } return fmt.Sprintf("%v%v/%v", constants.ReservedTaskListPrefix, root, partition) } func getQPSTrackerInterval(duration time.Duration) time.Duration { if duration == 0 { return 10 * time.Second } return duration } func randomlyPickKey(weights map[string]int) string { // Calculate the total weight totalWeight := 0 for _, weight := range weights { totalWeight += weight } // Generate a random number between 0 and totalWeight - 1 randomWeight := rand.Intn(totalWeight) // Iterate through the map to find the key corresponding to the random weight for key, weight := range weights { if randomWeight < weight { return key } randomWeight -= weight } // Return an empty string as a fallback (should not happen if weights are positive) return "" } type rateLimiterForTimeRange struct { limiter *rate.Limiter start, end int } func (r *rateLimiterForTimeRange) String() string { return fmt.Sprintf("{start: %d, end: %d}", r.start, r.end) } type simulationRateLimiter struct { startTime time.Time timeSrc clock.TimeSource rateLimiters []*rateLimiterForTimeRange logFn func(msg string, args ...interface{}) } func newSimulationRateLimiter( taskConfig host.SimulationTaskConfiguration, startTime time.Time, timeSrc clock.TimeSource, logFn func(msg string, args ...interface{}), ) *simulationRateLimiter { var rateLimiters []*rateLimiterForTimeRange if len(taskConfig.OverTime) == 0 { l := rate.NewLimiter(rate.Limit(getTasksPerSecond(taskConfig)), getTasksBurst(taskConfig)) rateLimiters = append(rateLimiters, &rateLimiterForTimeRange{limiter: l, start: 0, end: -1}) } else { start := 0 for _, spec := range taskConfig.OverTime { l := rate.NewLimiter(rate.Limit(spec.TasksPerSecond), spec.TasksBurst) end := -1 if spec.Duration != nil { end = start + int(spec.Duration.Seconds()) } rateLimiters = append(rateLimiters, &rateLimiterForTimeRange{limiter: l, start: start, end: end}) start = end } } sort.Slice(rateLimiters, func(i, j int) bool { return rateLimiters[i].start < rateLimiters[j].start }) logFn("Rate limiters: %v", rateLimiters) return &simulationRateLimiter{ startTime: startTime, timeSrc: timeSrc, rateLimiters: rateLimiters, logFn: logFn, } } func (r *simulationRateLimiter) Wait(ctx context.Context) error { limiter, err := r.getLimiter() if err != nil { return err } return limiter.limiter.Wait(ctx) } func (r *simulationRateLimiter) getLimiter() (*rateLimiterForTimeRange, error) { elapsed := int(r.timeSrc.Since(r.startTime).Seconds()) idx, ok := slices.BinarySearchFunc(r.rateLimiters, elapsed, func(r *rateLimiterForTimeRange, t int) int { if t >= r.start && (r.end == -1 || t < r.end) { return 0 } if r.start > t { return 1 } return -1 }) if !ok { return nil, fmt.Errorf("rate limiter not found, elapsed: %ds", elapsed) } r.logFn("Elapsed %vs so using rate limiter at index %d", elapsed, idx) return r.rateLimiters[idx], nil } func TestMatchingSimulation_RateLimiterBST(t *testing.T) { mockTimeSrc := clock.NewMockedTimeSource() srl := &simulationRateLimiter{ startTime: mockTimeSrc.Now(), timeSrc: mockTimeSrc, rateLimiters: []*rateLimiterForTimeRange{ {limiter: rate.NewLimiter(rate.Limit(10), 1), start: 0, end: 5}, {limiter: rate.NewLimiter(rate.Limit(10), 1), start: 5, end: -1}, }, logFn: t.Logf, } // t = 0 l, err := srl.getLimiter() require.NoError(t, err) require.Equal(t, 0, l.start) // limiter at index 0 should be used // t = 3 mockTimeSrc.Advance(time.Second * 3) l, err = srl.getLimiter() require.NoError(t, err) require.Equal(t, 0, l.start) // limiter at index 0 should be used // t = 5 mockTimeSrc.Advance(time.Second * 2) l, err = srl.getLimiter() require.NoError(t, err) require.Equal(t, 5, l.start) // limiter at index 1 should be used // t = 10 mockTimeSrc.Advance(time.Second * 5) l, err = srl.getLimiter() require.NoError(t, err) require.Equal(t, 5, l.start) // limiter at index 1 should be used } ================================================ FILE: simulation/matching/run.sh ================================================ #!/bin/bash # Cadence Matching Simulation Test Script # # Usage: # ./simulation/matching/run.sh [OPTIONS] # # Examples: # # Run default scenario # ./simulation/matching/run.sh --scenario default # # # Run specific scenario # ./simulation/matching/run.sh --scenario throughput # # # Run with custom timestamp # ./simulation/matching/run.sh --scenario default --timestamp 2024-01-15-10-30-00 # # # Run with custom dockerfile # ./simulation/matching/run.sh --scenario throughput --dockerfile-suffix .local # # TODO: Add README with more information on how to analyse the test results, as well as more detailed information on the comparison tests. set -eo pipefail show_help() { cat << EOF Cadence Matching Simulation Test Script USAGE: $0 [OPTIONS] OPTIONS: -s, --scenario SCENARIO Test scenario to run (required) Corresponds to testdata/matching_simulation_SCENARIO.yaml -t, --timestamp TIMESTAMP Custom timestamp for test naming (default: current time) Format: YYYY-MM-DD-HH-MM-SS -d, --dockerfile-suffix SUFFIX Dockerfile suffix for custom builds (default: empty) Example: .local for Dockerfile.local -h, --help Show this help message EXAMPLES: # Run default scenario $0 --scenario default # Run specific scenario $0 --scenario throughput # Run with custom timestamp $0 --scenario default --timestamp 2024-01-15-10-30-00 # Run with custom dockerfile $0 --scenario throughput --dockerfile-suffix .local EOF } # Default values testCase="" timestamp="" dockerFileSuffix="" # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in -s|--scenario) testCase="$2" shift 2 ;; -t|--timestamp) timestamp="$2" shift 2 ;; -d|--dockerfile-suffix) dockerFileSuffix="$2" shift 2 ;; -h|--help) show_help exit 0 ;; --) shift break ;; -*) echo "Unknown option: $1" >&2 echo "Use --help for usage information." >&2 exit 1 ;; *) echo "Unexpected positional argument: $1" >&2 echo "Use --scenario to specify the test scenario." >&2 echo "Use --help for usage information." >&2 exit 1 ;; esac done # Require scenario parameter if [[ -z "$testCase" ]]; then echo "Error: --scenario parameter is required" >&2 echo "" >&2 show_help exit 1 fi # Set default timestamp if not provided if [[ -z "$timestamp" ]]; then timestamp="$(date '+%Y-%m-%d-%H-%M-%S')" fi testCfg="testdata/matching_simulation_$testCase.yaml" testName="test-$testCase-$timestamp" resultFolder="matching-simulator-output" mkdir -p "$resultFolder" eventLogsFile="$resultFolder/$testName-events.json" testSummaryFile="$resultFolder/$testName-summary.txt" echo "Building test image" DOCKERFILE_SUFFIX=$dockerFileSuffix docker compose -f docker/github_actions/docker-compose-local-matching-simulation.yml \ build matching-simulator function check_test_failure() { faillog=$(grep 'FAIL: TestMatchingSimulationSuite' -B 10 test.log 2>/dev/null || true) timeoutlog=$(grep 'test timed out' test.log 2>/dev/null || true) if [ -z "$faillog" ] && [ -z "$timeoutlog" ]; then echo "Passed" else echo 'Test failed!!!' echo "Fail log: $faillog" echo "Timeout log: $timeoutlog" echo "Check test.log file for more details" exit 1 fi } trap check_test_failure EXIT echo "Running the test $testCase" DOCKERFILE_SUFFIX=$dockerFileSuffix docker compose \ -f docker/github_actions/docker-compose-local-matching-simulation.yml \ run -e MATCHING_SIMULATION_CONFIG=$testCfg --rm --remove-orphans --service-ports --use-aliases \ matching-simulator \ | grep -a --line-buffered "Matching New Event" \ | sed "s/Matching New Event: //" \ | jq . > "$eventLogsFile" echo "---- Simulation Summary ----" cat test.log \ | sed -n '/Simulation Summary/,/End of Simulation Summary/p' \ | grep -v "Simulation Summary" \ | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq .Payload.Latency | awk '{s+=$0}END{print s/NR}') echo "Avg Task latency (ms): $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq .Payload.Latency | sort -n | awk '{a[NR]=$0}END{print a[int(NR*0.50)]}') echo "P50 Task latency (ms): $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq .Payload.Latency | sort -n | awk '{a[NR]=$0}END{print a[int(NR*0.75)]}') echo "P75 Task latency (ms): $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq .Payload.Latency | sort -n | awk '{a[NR]=$0}END{print a[int(NR*0.95)]}') echo "P95 Task latency (ms): $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq .Payload.Latency | sort -n | awk '{a[NR]=$0}END{print a[int(NR*0.99)]}') echo "P99 Task latency (ms): $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq .Payload.Latency | sort -n | tail -n 1) echo "Max Task latency (ms): $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq -s 'map(if .Payload.IsolationGroup == .PartitionConfig."original-isolation-group" then 1 else 0 end) | add / length') echo "Task Containment: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq '{ScheduleID,TaskListName,EventName,Payload}' \ | jq -c '.' | wc -l) echo "Worker Polls that returned a task: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task" and .Payload.TaskIsForwarded == true)' \ | jq '{ScheduleID,TaskListName,EventName,Payload}' \ | jq -c '.' | wc -l) echo "Worker Polls that returned a forwarded task: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returned no tasks")' \ | jq '{ScheduleID,TaskListName,EventName,Payload}' \ | jq -c '.' | wc -l) echo "Worker Polls that returned NO task: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Matcher Falling Back to Non-Local Polling")' \ | jq '{ScheduleID,TaskListName,EventName,Payload}' \ | jq -c '.' | wc -l) echo "Worker Polls that falled back to non-local polling: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Attempting to Forward Poll")' \ | jq '{ScheduleID,TaskListName,EventName,Payload}' \ | jq -c '.' | wc -l) echo "Poll forward attempts: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Forwarded Poll returned task")' \ | jq '{ScheduleID,TaskListName,EventName,Payload}' \ | jq -c '.' | wc -l) echo "Forwarded poll returned task: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Task Written to DB")' \ | jq '{ScheduleID,TaskListName,EventName,Payload}' \ | jq -c '.' | wc -l) echo "Tasks Written to DB: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Attempting to Forward Task")' \ | jq '{ScheduleID,TaskListName,EventName,Payload}' \ | jq -c '.' | wc -l) echo "Task forward attempts: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName | contains("Matched Task"))' \ | jq -c 'select((.Payload.SyncMatched == true) and (.Payload.TaskIsForwarded == true))' \ | jq '{ScheduleID,TaskListName}' \ | jq -c '.' | wc -l) echo "Sync matches - task is forwarded: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName | contains("Matched Task"))' \ | jq -c 'select((.Payload.SyncMatched == true) and (.Payload.TaskIsForwarded == false))' \ | jq '{ScheduleID,TaskListName}' \ | jq -c '.' | wc -l) echo "Sync matches - task is not forwarded: $tmp" | tee -a $testSummaryFile echo "Per tasklist sync matches:" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "SyncMatched so not persisted")' \ | jq '.TaskListName' \ | jq -c '.' | sort -n | uniq -c | sed -e 's/^/ /' | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Could not SyncMatched Forwarded Task so not persisted")' \ | jq '{ScheduleID,TaskListName}' \ | jq -c '.' | wc -l) echo "Forwarded Task failed to sync match: $tmp" | tee -a $testSummaryFile tmp=$(cat "$eventLogsFile" \ | jq -c 'select(.EventName | contains("Matched Task"))' \ | jq -c 'select(.Payload.SyncMatched != true)' \ | jq '{ScheduleID,TaskListName,Payload}' \ | jq -c '.' | wc -l) echo "Async matches: $tmp" | tee -a $testSummaryFile echo "Matched tasks per tasklist:" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName | contains("Matched Task"))' \ | jq '.TaskListName' \ | jq -c '.' | sort -n | uniq -c | sed -e 's/^/ /' | tee -a $testSummaryFile echo "AddDecisionTask request per tasklist (excluding forwarded):" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Received AddDecisionTask" and .Payload.RequestForwardedFrom == "")' \ | jq '.TaskListName' \ | jq -c '.' | sort -n | uniq -c | sed -e 's/^/ /' | tee -a $testSummaryFile echo "AddDecisionTask request per tasklist (forwarded):" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Received AddDecisionTask" and .Payload.RequestForwardedFrom != "")' \ | jq '.TaskListName' \ | jq -c '.' | sort -n | uniq -c | sed -e 's/^/ /' | tee -a $testSummaryFile echo "PollForDecisionTask request per tasklist (excluding forwarded):" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Received PollForDecisionTask" and .Payload.RequestForwardedFrom == "")' \ | jq '.TaskListName' \ | jq -c '.' | sort -n | uniq -c | sed -e 's/^/ /' | tee -a $testSummaryFile echo "PollForDecisionTask request per tasklist (forwarded):" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Received PollForDecisionTask" and .Payload.RequestForwardedFrom != "")' \ | jq '.TaskListName' \ | jq -c '.' | sort -n | uniq -c | sed -e 's/^/ /' | tee -a $testSummaryFile echo "AddDecisionTask request per isolation group (excluding forwarded):" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Received AddDecisionTask" and .Payload.RequestForwardedFrom == "")' \ | jq '.PartitionConfig."original-isolation-group" // .PartitionConfig."isolation-group"' \ | jq -c '.' | sort -n | uniq -c | sed -e 's/^/ /' | tee -a $testSummaryFile echo "AddDecisionTask request per isolation group (forwarded):" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Received AddDecisionTask" and .Payload.RequestForwardedFrom != "")' \ | jq '.PartitionConfig."original-isolation-group" // .PartitionConfig."isolation-group"' \ | jq -c '.' | sort -n | uniq -c | sed -e 's/^/ /' | tee -a $testSummaryFile echo "PollForDecisionTask request per isolation group (excluding forwarded):" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Received PollForDecisionTask" and .Payload.RequestForwardedFrom == "")' \ | jq '.Payload.IsolationGroup' \ | jq -c '.' | sort -n | uniq -c | sed -e 's/^/ /' | tee -a $testSummaryFile echo "PollForDecisionTask request per isolation group (forwarded):" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "Received PollForDecisionTask" and .Payload.RequestForwardedFrom != "")' \ | jq '.Payload.IsolationGroup' \ | jq -c '.' | sort -n | uniq -c | sed -e 's/^/ /' | tee -a $testSummaryFile echo "Latency per isolation group:" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq -s 'group_by(.PartitionConfig."original-isolation-group")[] | {"IsolationGroup": .[0].PartitionConfig."original-isolation-group", "Avg": (map(.Payload.Latency | tonumber) | add / length), "Median": (map(.Payload.Latency | tonumber) | sort | .[length/2]), "Max":(map(.Payload.Latency | tonumber) | max) }'\ | jq -s 'sort_by(.IsolationGroup)[]'\ | jq -r '[.IsolationGroup, .Median, .Avg, .Max] | @tsv' \ | sort -n | column -t | sed -e 's/^/ /' | tee -a $testSummaryFile echo "Latency per isolation group and task list:" | tee -a $testSummaryFile cat "$eventLogsFile" \ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq -s 'group_by(.PartitionConfig."original-isolation-group", .TaskListName)[] | {"IsolationGroup": .[0].PartitionConfig."original-isolation-group", "TaskListName": .[0].TaskListName, "Avg": (map(.Payload.Latency | tonumber) | add / length), "Median": (map(.Payload.Latency | tonumber) | sort | .[length/2]), "Max":(map(.Payload.Latency | tonumber) | max) }'\ | jq -s 'sort_by(.TaskListName, .IsolationGroup)[]'\ | jq -r '[.TaskListName, .IsolationGroup, .Median, .Avg, .Max] | @tsv' \ | sort -n | column -t | sed -e 's/^/ /' | tee -a $testSummaryFile echo "Task Containment per isolation group and task list:" | tee -a $testSummaryFile cat "$eventLogsFile"\ | jq -c 'select(.EventName == "PollForDecisionTask returning task")' \ | jq -s 'group_by(.PartitionConfig."original-isolation-group", (.Payload.RequestForwardedFrom // .TaskListName))[] | {"IsolationGroup": .[0].PartitionConfig."original-isolation-group", "TaskListName": (.[0].Payload.RequestForwardedFrom // .[0].TaskListName), "Containment": (map(if .Payload.IsolationGroup == .PartitionConfig."original-isolation-group" then 1 else 0 end) | add / length) }'\ | jq -s 'sort_by(.TaskListName, .IsolationGroup)[]'\ | jq -r '[.TaskListName, .IsolationGroup, .Containment] | @tsv' \ | sort -n | column -t | sed -e 's/^/ /' | tee -a $testSummaryFile echo "End of summary" | tee -a $testSummaryFile printf "\nResults are saved in $testSummaryFile\n" printf "For further ad-hoc analysis, please check $eventLogsFile via jq queries\n" printf "Visit http://localhost:3000/ to view Cadence Matching grafana dashboard\n" ================================================ FILE: simulation/matching/testdata/matching_simulation_burst.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 0ms localtaskwaittime: 0ms tasks: - numtaskgenerators: 10 taskspersecond: 500 maxtasktogenerate: 10000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_burst_adaptive.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 0 # this doesn't matter. adaptive scaler will start from 1 tasklistreadpartitions: 0 # this doesn't matter. adaptive scaler will start from 1 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms tasks: - numtaskgenerators: 10 taskspersecond: 500 maxtasktogenerate: 10000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s getpartitionconfigfromdb: true enableadaptivescaler: true partitiondownscalefactor: 0.75 partitionupscalerps: 200 partitionupscalesustainedduration: 5s partitiondownscalesustainedduration: 5s adaptivescalerupdateinterval: 1s qpstrackerinterval: 2s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_default.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms tasks: - numtaskgenerators: 2 taskspersecond: 80 maxtasktogenerate: 3000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_fluctuating.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 1 tasklistreadpartitions: 1 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms tasks: - numtaskgenerators: 4 maxtasktogenerate: 5000 overtime: - taskspersecond: 10 tasksburst: 10 duration: 30s - taskspersecond: 250 tasksburst: 250 duration: 15s - taskspersecond: 10 tasksburst: 10 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_fluctuating_adaptive.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 0 # this doesn't matter. adaptive scaler will start from 1 tasklistreadpartitions: 0 # this doesn't matter. adaptive scaler will start from 1 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms tasks: - numtaskgenerators: 3 maxtasktogenerate: 8000 overtime: - taskspersecond: 10 tasksburst: 10 duration: 15s - taskspersecond: 250 tasksburst: 250 duration: 30s - taskspersecond: 10 tasksburst: 10 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s getpartitionconfigfromdb: true enableadaptivescaler: true partitiondownscalefactor: 0.7 partitionupscalerps: 120 partitionupscalesustainedduration: 3s partitiondownscalesustainedduration: 3s adaptivescalerupdateinterval: 1s qpstrackerinterval: 2s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_get_partition_config_from_db.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms getpartitionconfigfromdb: true tasks: - numtaskgenerators: 2 taskspersecond: 80 maxtasktogenerate: 3000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_more_read_partitions.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 8 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms tasks: - numtaskgenerators: 2 taskspersecond: 80 maxtasktogenerate: 3000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_no_forwarding.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 0 forwardermaxoutstandingtasks: 0 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms tasks: - numtaskgenerators: 2 taskspersecond: 80 maxtasktogenerate: 3000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_no_wait_time_for_addtask.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 0ms tasks: - numtaskgenerators: 2 taskspersecond: 80 maxtasktogenerate: 3000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_no_wait_time_for_polltask.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 0ms localtaskwaittime: 10ms tasks: - numtaskgenerators: 2 taskspersecond: 80 maxtasktogenerate: 3000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_no_wait_time_for_polltask_and_addtask.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 0ms localtaskwaittime: 0ms tasks: - numtaskgenerators: 2 taskspersecond: 80 maxtasktogenerate: 3000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_round_robin_load_balancer.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 8 simulationconfig: tasklistwritepartitions: 2 tasklistreadpartitions: 2 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 0ms localtaskwaittime: 0ms recorddecisiontaskstartedtime: 13ms tasklistloadbalancerstrategy: round-robin tasks: - numtaskgenerators: 100 taskspersecond: 300 maxtasktogenerate: 30000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_throughput.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 1 tasklistreadpartitions: 1 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms tasks: - numtaskgenerators: 2 taskspersecond: 80 maxtasktogenerate: 3000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_weighted_load_balancer_with_backlog.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms tasklistloadbalancerstrategy: weighted tasks: - numtaskgenerators: 2 taskspersecond: 80 maxtasktogenerate: 3000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s backlogs: - partition: 1 backlogcount: 1000 - partition: 2 backlogcount: 2000 workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_with_backlog.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms tasks: - numtaskgenerators: 2 taskspersecond: 80 maxtasktogenerate: 3000 pollers: - taskprocesstime: 1ms numpollers: 8 polltimeout: 60s backlogs: - partition: 1 backlogcount: 1000 - partition: 2 backlogcount: 2000 workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_zonal_isolation.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms taskisolationduration: 1s tasks: - numtaskgenerators: 30 taskspersecond: 500 maxtasktogenerate: 5000 isolationgroups: ['a', 'b', 'c', 'd'] pollers: - isolationgroup: 'a' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'b' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'c' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'd' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_zonal_isolation_few_pollers.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms taskisolationduration: 1s tasklistloadbalancerstrategy: isolation tasks: - numtaskgenerators: 30 taskspersecond: 500 maxtasktogenerate: 5000 isolationgroups: ['a', 'b', 'c', 'd'] pollers: - isolationgroup: 'a' taskprocesstime: 1ms numpollers: 2 polltimeout: 60s - isolationgroup: 'b' taskprocesstime: 1ms numpollers: 2 polltimeout: 60s - isolationgroup: 'c' taskprocesstime: 1ms numpollers: 2 polltimeout: 60s - isolationgroup: 'd' taskprocesstime: 1ms numpollers: 2 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_zonal_isolation_many_pollers.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms taskisolationduration: 1s tasklistloadbalancerstrategy: isolation tasks: - numtaskgenerators: 30 taskspersecond: 500 maxtasktogenerate: 5000 isolationgroups: ['a', 'b', 'c', 'd'] pollers: - isolationgroup: 'a' taskprocesstime: 1ms numpollers: 16 polltimeout: 60s - isolationgroup: 'b' taskprocesstime: 1ms numpollers: 16 polltimeout: 60s - isolationgroup: 'c' taskprocesstime: 1ms numpollers: 16 polltimeout: 60s - isolationgroup: 'd' taskprocesstime: 1ms numpollers: 16 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_zonal_isolation_single_partition.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 1 tasklistreadpartitions: 1 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms taskisolationduration: 1s tasklistloadbalancerstrategy: isolation tasks: - numtaskgenerators: 30 taskspersecond: 500 maxtasktogenerate: 5000 isolationgroups: ['a', 'b', 'c', 'd'] pollers: - isolationgroup: 'a' taskprocesstime: 1ms numpollers: 2 polltimeout: 60s - isolationgroup: 'b' taskprocesstime: 1ms numpollers: 2 polltimeout: 60s - isolationgroup: 'c' taskprocesstime: 1ms numpollers: 2 polltimeout: 60s - isolationgroup: 'd' taskprocesstime: 1ms numpollers: 2 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_zonal_isolation_skew.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms taskisolationduration: 1s tasklistloadbalancerstrategy: isolation tasks: - numtaskgenerators: 10 taskspersecond: 180 maxtasktogenerate: 1800 isolationgroups: ['a', 'b', 'c'] - numtaskgenerators: 20 taskspersecond: 320 maxtasktogenerate: 3200 isolationgroups: [ 'd' ] pollers: - isolationgroup: 'a' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'b' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'c' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'd' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_zonal_isolation_skew_extreme.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms taskisolationduration: 1s tasklistloadbalancerstrategy: isolation tasks: - numtaskgenerators: 3 taskspersecond: 50 maxtasktogenerate: 500 isolationgroups: ['a', 'b', 'c'] - numtaskgenerators: 27 taskspersecond: 450 maxtasktogenerate: 4500 isolationgroups: [ 'd' ] pollers: - isolationgroup: 'a' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'b' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'c' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'd' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/matching/testdata/matching_simulation_zonal_isolation_skew_forwarding.yaml ================================================ enablearchival: false clusterno: 1 messagingclientconfig: usemock: true historyconfig: numhistoryshards: 4 numhistoryhosts: 1 matchingconfig: nummatchinghosts: 4 simulationconfig: tasklistwritepartitions: 4 tasklistreadpartitions: 4 forwardermaxoutstandingpolls: 1 forwardermaxoutstandingtasks: 1 forwardermaxratepersecond: 10000 forwardermaxchildrenpernode: 20 localpollwaittime: 10ms localtaskwaittime: 10ms taskisolationduration: 1s tasklistloadbalancerstrategy: isolation tasks: - numtaskgenerators: 10 taskspersecond: 180 maxtasktogenerate: 1800 isolationgroups: ['a', 'b', 'c'] - numtaskgenerators: 20 taskspersecond: 320 maxtasktogenerate: 3200 isolationgroups: [ 'd' ] pollers: - isolationgroup: 'a' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'b' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'c' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s - isolationgroup: 'd' taskprocesstime: 25ms numpollers: 8 polltimeout: 60s workerconfig: enableasyncwfconsumer: false ================================================ FILE: simulation/replication/README.md ================================================ # Replication Simulation These simulations test replication of workflows across clusters. As replication is handled at the application layer there are many edge case scenarios that lead to conflict resolution when: - there is a delay in replication - a domain is failed over from one cluster to another - active-active is enabled and a customer is making requests to both clusters There are also sanity checks for the request routing layer. ## Quick Run ```bash # Basic test ./simulation/replication/run.sh --scenario default # Active-active test ./simulation/replication/run.sh --scenario activeactive # With custom dockerfile ./simulation/replication/run.sh --scenario default --dockerfile-suffix .local # Rerun without rebuilding ./simulation/replication/run.sh --scenario default --rerun ``` ### Results Results are output to the following files: - **Test logs**: `test.log` contains the summary of the test run - **Summary**: `replication-simulator-output/test-{scenario}-{timestamp}-summary.txt` contains a summary of the test run You can also use the [Cadence UI](http://localhost:8088) to debug the workflows that ran during your test. To further debug, you can query for logs against the running docker containers. ## Configuration Scenarios are written in `testdata/replication_simulation_{scenario}.yaml`. This naming convention is required by `run.sh` to find test scenarios to run. To configure the cadence instances that are running for the test use a dynamic config file at `config/dynamicconfig/replication_simulation_{scenario}.yml`. Dynamic config can change any feature flag supported by Cadence - these feature flags can be used to hide full features, or to hide test-specific implementations that expose additional data required by your test. ## CI/CD Scenarios run automatically in GitHub Actions via `.github/workflows/replication-simulation.yml`. Add new scenarios to the matrix once they pass locally. ================================================ FILE: simulation/replication/replication_simulation_test.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. /* To run locally: 1. Pick a scenario from the existing config files simulation/replication/testdata/replication_simulation_${scenario}.yaml or add a new one 2. Run the scenario `./simulation/replication/run.sh default` Full test logs can be found at test.log file. Event json logs can be found at replication-simulator-output folder. See the run.sh script for more details about how to parse events. */ package replication import ( "context" "encoding/json" "flag" "fmt" "net/http" "reflect" "sort" "strings" "testing" "time" "github.com/pborman/uuid" "github.com/stretchr/testify/require" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" simTypes "github.com/uber/cadence/simulation/replication/types" ) func TestReplicationSimulation(t *testing.T) { flag.Parse() simTypes.Logf(t, "Starting Replication Simulation") simTypes.Logf(t, "Sleeping for 30 seconds to allow services to start/warmup") time.Sleep(30 * time.Second) // load config simCfg, err := simTypes.LoadConfig() require.NoError(t, err, "failed to load config") // initialize replication simulation sim := simTypes.NewReplicationSimulation() // initialize cadence clients for clusterName := range simCfg.Clusters { simCfg.MustInitClientsFor(t, clusterName) } simTypes.Logf(t, "Registering domains") for domainName, domainCfg := range simCfg.Domains { simTypes.Logf(t, "Domain: %s", domainName) simCfg.MustRegisterDomain(t, domainName, domainCfg) } // wait for domain data to be replicated and workers to start. waitUntilWorkersReady(t) sort.Slice(simCfg.Operations, func(i, j int) bool { return simCfg.Operations[i].At < simCfg.Operations[j].At }) startTime := time.Now().UTC() simTypes.Logf(t, "Simulation start time: %v", startTime) for i, op := range simCfg.Operations { waitForOpTime(t, op, startTime) var err error switch op.Type { case simTypes.ReplicationSimulationOperationStartWorkflow: err = startWorkflow(t, op, simCfg, sim) case simTypes.ReplicationSimulationOperationResetWorkflow: err = resetWorkflow(t, op, simCfg) case simTypes.ReplicationSimulationOperationChangeActiveClusters: err = changeActiveClusters(t, op, simCfg) case simTypes.ReplicationSimulationOperationValidate: err = validate(t, op, simCfg, sim) case simTypes.ReplicationSimulationOperationQueryWorkflow: err = queryWorkflow(t, op, simCfg, sim) case simTypes.ReplicationSimulationOperationSignalWithStartWorkflow: err = signalWithStartWorkflow(t, op, simCfg, sim) case simTypes.ReplicationSimulationOperationValidateWorkflowReplication: err = validateWorkflowReplication(t, op, simCfg) default: require.Failf(t, "unknown operation type", "operation type: %s", op.Type) } if err != nil { t.Fatalf("Operation %d failed: %v", i, err) } } // Print the test summary. // Don't change the start/end line format as it is used by scripts to parse the summary info executionTime := time.Since(startTime) var testSummary []string testSummary = append(testSummary, "Simulation Summary:") testSummary = append(testSummary, fmt.Sprintf("Simulation Duration: %v", executionTime)) testSummary = append(testSummary, "End of Simulation Summary") fmt.Println(strings.Join(testSummary, "\n")) } func startWorkflow( t *testing.T, op *simTypes.Operation, simCfg *simTypes.ReplicationSimulationConfig, sim *simTypes.ReplicationSimulation, ) error { t.Helper() simTypes.Logf(t, "Starting workflow: %s on domain %s on cluster: %s", op.WorkflowID, op.Domain, op.Cluster) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() if op.WorkflowExecutionStartToCloseTimeout == 0 || op.WorkflowExecutionStartToCloseTimeout < op.WorkflowDuration { return fmt.Errorf("workflow execution start to close timeout must be specified and should be greater than workflow duration") } input := mustJSON(t, &simTypes.WorkflowInput{ Duration: op.WorkflowDuration, ActivityCount: op.ActivityCount, ChildWorkflowID: op.ChildWorkflowID, ChildWorkflowTimeout: op.ChildWorkflowTimeout, }) workflowIDReusePolicy := types.WorkflowIDReusePolicyAllowDuplicate.Ptr() if op.WorkflowIDReusePolicy != nil { workflowIDReusePolicy = op.WorkflowIDReusePolicy } resp, err := simCfg.MustGetFrontendClient(t, op.Cluster).StartWorkflowExecution(ctx, &types.StartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: op.Domain, WorkflowID: op.WorkflowID, WorkflowType: &types.WorkflowType{Name: op.WorkflowType}, TaskList: &types.TaskList{Name: simTypes.TasklistName}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(int32((op.WorkflowExecutionStartToCloseTimeout).Seconds())), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(5), Input: input, WorkflowIDReusePolicy: workflowIDReusePolicy, DelayStartSeconds: common.Int32Ptr(op.DelayStartSeconds), CronSchedule: op.CronSchedule, ActiveClusterSelectionPolicy: op.ActiveClusterSelectionPolicy, }) if err != nil { if op.Want.Error != "" { if strings.Contains(err.Error(), op.Want.Error) { simTypes.Logf(t, "Start workflow got expected error: %s on domain: %s on cluster: %s. Error: %s", op.WorkflowID, op.Domain, op.Cluster, err.Error()) return nil } return fmt.Errorf("expected error: %s, but got: %s", op.Want.Error, err.Error()) } return err } runID := resp.GetRunID() simTypes.Logf(t, "Started workflow: %s on domain: %s on cluster: %s. RunID: %s", op.WorkflowID, op.Domain, op.Cluster, runID) // Store RunID if runIDKey is specified if op.RunIDKey != "" { sim.StoreRunID(op.RunIDKey, runID) simTypes.Logf(t, "Stored RunID %s with key: %s", runID, op.RunIDKey) } return nil } func resetWorkflow( t *testing.T, op *simTypes.Operation, simCfg *simTypes.ReplicationSimulationConfig, ) error { t.Helper() simTypes.Logf(t, "Resetting workflow: %s on domain %s on cluster: %s", op.WorkflowID, op.Domain, op.Cluster) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() resp, err := simCfg.MustGetFrontendClient(t, op.Cluster).DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: op.Domain, Execution: &types.WorkflowExecution{ WorkflowID: op.WorkflowID, }, }) if err != nil { return fmt.Errorf("failed to describe workflow %s: %w", op.WorkflowID, err) } simTypes.Logf(t, "DescribeWorkflowExecution workflowID: %s on domain: %s on cluster: %s. RunID: %s", op.WorkflowID, op.Domain, op.Cluster, resp.GetWorkflowExecutionInfo().Execution.RunID) resetResp, err := simCfg.MustGetFrontendClient(t, op.Cluster).ResetWorkflowExecution(ctx, &types.ResetWorkflowExecutionRequest{ Domain: op.Domain, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: op.WorkflowID, RunID: resp.GetWorkflowExecutionInfo().Execution.RunID, }, Reason: "resetting workflow", DecisionFinishEventID: op.EventID, }) if err != nil { if op.Want.Error != "" { if strings.Contains(err.Error(), op.Want.Error) { simTypes.Logf(t, "Expected error: %s", op.Want.Error) return nil } return fmt.Errorf("expected error: %s, but got: %s", op.Want.Error, err.Error()) } simTypes.Logf(t, err.Error()) return err } if op.Want.Error != "" { return fmt.Errorf("expected error: %s, but got nil", op.Want.Error) } simTypes.Logf(t, "Reset workflowID: %s on domain: %s on cluster: %s. RunID: %s", op.WorkflowID, op.Domain, op.Cluster, resetResp.GetRunID()) return nil } // changeActiveClusters modifies the active clusters for a domain // It can be used to change the active cluster for a domain or a sub-set of the AttributeScopes for the domain func changeActiveClusters( t *testing.T, op *simTypes.Operation, simCfg *simTypes.ReplicationSimulationConfig, ) error { t.Helper() ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() descResp, err := simCfg.MustGetFrontendClient(t, simCfg.PrimaryCluster).DescribeDomain(ctx, &types.DescribeDomainRequest{Name: common.StringPtr(op.Domain)}) if err != nil { return fmt.Errorf("failed to describe domain %s: %w", op.Domain, err) } updateDomainRequest := &types.UpdateDomainRequest{ Name: op.Domain, ActiveClusters: &types.ActiveClusters{}, FailoverTimeoutInSeconds: op.FailoverTimeout, } if op.NewActiveCluster != "" { fromCluster := descResp.ReplicationConfiguration.ActiveClusterName toCluster := op.NewActiveCluster simTypes.Logf(t, "Changing active clusters for domain %s from %s to %s", op.Domain, fromCluster, toCluster) updateDomainRequest.ActiveClusterName = &toCluster } if !op.NewClusterAttributes.IsEmpty() { updateDomainRequest.ActiveClusters.AttributeScopes = op.NewClusterAttributes.ToAttributeScopes() simTypes.Logf(t, "Changing cluster attributes for domain %s from %+v to %+v", op.Domain, descResp.ReplicationConfiguration.ActiveClusters, updateDomainRequest.ActiveClusters) } ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) defer cancel() _, err = simCfg.MustGetFrontendClient(t, simCfg.PrimaryCluster).UpdateDomain(ctx, updateDomainRequest) if err != nil { return fmt.Errorf("failed to update ActiveClusters, err: %w", err) } simTypes.Logf(t, "Completed change to ActiveClusters") return nil } func queryWorkflow(t *testing.T, op *simTypes.Operation, simCfg *simTypes.ReplicationSimulationConfig, sim *simTypes.ReplicationSimulation) error { t.Helper() simTypes.Logf(t, "Querying workflow: %s on domain %s on cluster: %s", op.WorkflowID, op.Domain, op.Cluster) frontendCl := simCfg.MustGetFrontendClient(t, op.Cluster) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() consistencyLevel := types.QueryConsistencyLevelEventual.Ptr() if op.ConsistencyLevel == "strong" { consistencyLevel = types.QueryConsistencyLevelStrong.Ptr() } // Prepare workflow execution - use specific RunID if provided via runIDKey executionRequest := &types.WorkflowExecution{ WorkflowID: op.WorkflowID, } if op.RunIDKey != "" { if runID, err := sim.GetRunID(op.RunIDKey); err == nil && runID != "" { executionRequest.RunID = runID simTypes.Logf(t, "Using stored RunID %s for query (key: %s)", runID, op.RunIDKey) } else { return fmt.Errorf("runIDKey %s specified but no RunID found in registry", op.RunIDKey) } } queryResp, err := frontendCl.QueryWorkflow(ctx, &types.QueryWorkflowRequest{ Domain: op.Domain, Execution: executionRequest, Query: &types.WorkflowQuery{ QueryType: op.Query, }, QueryConsistencyLevel: consistencyLevel, }) if err != nil { return err } got := mustParseJSON(t, queryResp.GetQueryResult()) want := op.Want.QueryResult if !reflect.DeepEqual(want, got) { return fmt.Errorf("query result mismatch. want: %v (type: %T), got: %v (type: %T)", want, want, got, got) } simTypes.Logf(t, "Query workflow: %s on domain: %s on cluster: %s. Query result: %v", op.WorkflowID, op.Domain, op.Cluster, got) return nil } func signalWithStartWorkflow( t *testing.T, op *simTypes.Operation, simCfg *simTypes.ReplicationSimulationConfig, sim *simTypes.ReplicationSimulation, ) error { t.Helper() simTypes.Logf(t, "SignalWithStart workflow: %s on domain %s on cluster: %s", op.WorkflowID, op.Domain, op.Cluster) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() frontendCl := simCfg.MustGetFrontendClient(t, op.Cluster) workflowIDReusePolicy := types.WorkflowIDReusePolicyAllowDuplicate.Ptr() if op.WorkflowIDReusePolicy != nil { workflowIDReusePolicy = op.WorkflowIDReusePolicy } signalResp, err := frontendCl.SignalWithStartWorkflowExecution(ctx, &types.SignalWithStartWorkflowExecutionRequest{ RequestID: uuid.New(), Domain: op.Domain, WorkflowID: op.WorkflowID, WorkflowIDReusePolicy: workflowIDReusePolicy, WorkflowType: &types.WorkflowType{Name: op.WorkflowType}, SignalName: op.SignalName, SignalInput: mustJSON(t, op.SignalInput), TaskList: &types.TaskList{Name: simTypes.TasklistName}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(int32((op.WorkflowExecutionStartToCloseTimeout).Seconds())), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(5), Input: mustJSON(t, &simTypes.WorkflowInput{Duration: op.WorkflowDuration, ActivityCount: op.ActivityCount}), ActiveClusterSelectionPolicy: op.ActiveClusterSelectionPolicy, }) if err != nil { return err } runID := signalResp.GetRunID() simTypes.Logf(t, "SignalWithStart workflow: %s on domain %s on cluster: %s. RunID: %s", op.WorkflowID, op.Domain, op.Cluster, runID) // Store RunID if runIDKey is specified if op.RunIDKey != "" { sim.StoreRunID(op.RunIDKey, runID) simTypes.Logf(t, "Stored RunID %s with key: %s", runID, op.RunIDKey) } return nil } // validate performs validation based on given operation config. // validate function does not fail the test via t.Fail (or require.X). // It runs in separate goroutine. It should return an error. func validate( t *testing.T, op *simTypes.Operation, simCfg *simTypes.ReplicationSimulationConfig, sim *simTypes.ReplicationSimulation, ) error { t.Helper() simTypes.Logf(t, "Validating workflow: %s on cluster: %s", op.WorkflowID, op.Cluster) consistencyLevel := types.QueryConsistencyLevelEventual.Ptr() if op.ConsistencyLevel == "strong" { consistencyLevel = types.QueryConsistencyLevelStrong.Ptr() } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() // Prepare workflow execution - use specific RunID if provided via runIDKey executionRequest := &types.WorkflowExecution{ WorkflowID: op.WorkflowID, } if op.RunIDKey != "" { if runID, err := sim.GetRunID(op.RunIDKey); err == nil && runID != "" { executionRequest.RunID = runID simTypes.Logf(t, "Using stored RunID %s for validation (key: %s)", runID, op.RunIDKey) } else { return fmt.Errorf("runIDKey %s specified but no RunID found in registry", op.RunIDKey) } } resp, err := simCfg.MustGetFrontendClient(t, op.Cluster).DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: op.Domain, Execution: executionRequest, QueryConsistencyLevel: consistencyLevel, }) if err != nil { return err } workflowStatus := resp.GetWorkflowExecutionInfo().GetCloseStatus() workflowCloseTime := resp.GetWorkflowExecutionInfo().GetCloseTime() switch op.Want.Status { case "completed": // Validate workflow completed if workflowStatus != types.WorkflowExecutionCloseStatusCompleted || workflowCloseTime == 0 { return fmt.Errorf("workflow %s not completed. status: %s, close time: %v", op.WorkflowID, workflowStatus, time.Unix(0, workflowCloseTime)) } case "failed": // Validate workflow failed if workflowStatus != types.WorkflowExecutionCloseStatusFailed || workflowCloseTime == 0 { return fmt.Errorf("workflow %s not failed. status: %s, close time: %v", op.WorkflowID, workflowStatus, time.Unix(0, workflowCloseTime)) } case "canceled": // Validate workflow canceled if workflowStatus != types.WorkflowExecutionCloseStatusCanceled || workflowCloseTime == 0 { return fmt.Errorf("workflow %s not canceled. status: %s, close time: %v", op.WorkflowID, workflowStatus, time.Unix(0, workflowCloseTime)) } case "terminated": // Validate workflow terminated if workflowStatus != types.WorkflowExecutionCloseStatusTerminated || workflowCloseTime == 0 { return fmt.Errorf("workflow %s not terminated. status: %s, close time: %v", op.WorkflowID, workflowStatus, time.Unix(0, workflowCloseTime)) } case "continued-as-new": // Validate workflow continued as new if workflowStatus != types.WorkflowExecutionCloseStatusContinuedAsNew || workflowCloseTime == 0 { return fmt.Errorf("workflow %s not continued as new. status: %s, close time: %v", op.WorkflowID, workflowStatus, time.Unix(0, workflowCloseTime)) } case "timed-out": // Validate workflow timed out if workflowStatus != types.WorkflowExecutionCloseStatusTimedOut || workflowCloseTime == 0 { return fmt.Errorf("workflow %s not timed out. status: %s, close time: %v", op.WorkflowID, workflowStatus, time.Unix(0, workflowCloseTime)) } default: // Validate workflow is running if workflowCloseTime != 0 { return fmt.Errorf("workflow %s not running. status: %s, close time: %v", op.WorkflowID, workflowStatus, time.Unix(0, workflowCloseTime)) } } simTypes.Logf(t, "Validated workflow: %s on cluster: %s. Status: %s, CloseTime: %v", op.WorkflowID, op.Cluster, resp.GetWorkflowExecutionInfo().GetCloseStatus(), time.Unix(0, resp.GetWorkflowExecutionInfo().GetCloseTime())) // Get history to validate the worker identity that started and completed the workflow // Some workflows start in cluster0 and complete in cluster1. This is to validate that var runID string if op.RunIDKey != "" { runID, err = sim.GetRunID(op.RunIDKey) if err != nil { return err } } history, err := getAllHistory(t, simCfg, op.Cluster, op.Domain, op.WorkflowID, runID) if err != nil { return err } if len(history) == 0 { return fmt.Errorf("no history events found for workflow %s", op.WorkflowID) } startedWorker, err := firstDecisionTaskWorker(history) if err != nil { return err } if op.Want.StartedByWorkersInCluster != "" && startedWorker != simTypes.WorkerIdentityFor(op.Want.StartedByWorkersInCluster, op.Domain) { return fmt.Errorf("workflow %s started by worker %s, expected %s", op.WorkflowID, startedWorker, simTypes.WorkerIdentityFor(op.Want.StartedByWorkersInCluster, op.Domain)) } completedWorker, err := lastDecisionTaskWorker(history) if err != nil { return err } if op.Want.CompletedByWorkersInCluster != "" && completedWorker != simTypes.WorkerIdentityFor(op.Want.CompletedByWorkersInCluster, op.Domain) { return fmt.Errorf("workflow %s completed by worker %s, expected %s", op.WorkflowID, completedWorker, simTypes.WorkerIdentityFor(op.Want.CompletedByWorkersInCluster, op.Domain)) } return nil } func validateWorkflowReplication( t *testing.T, op *simTypes.Operation, simCfg *simTypes.ReplicationSimulationConfig, ) error { t.Helper() simTypes.Logf(t, "Describing workflow: %s on domain %s on cluster: %s", op.WorkflowID, op.Domain, op.Cluster) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() resp, err := simCfg.MustGetFrontendClient(t, op.SourceCluster).DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: op.Domain, Execution: &types.WorkflowExecution{ WorkflowID: op.WorkflowID, }, }) if err != nil { return err } sourceClusterWorkflowExecution := resp.GetWorkflowExecutionInfo().GetExecution() simTypes.Logf(t, "Described workflow: %s on domain: %s on cluster: %s. Status: %s, CloseTime: %v", op.WorkflowID, op.Domain, op.Cluster, resp.GetWorkflowExecutionInfo().GetCloseStatus(), time.Unix(0, resp.GetWorkflowExecutionInfo().GetCloseTime())) ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) defer cancel() resp, err = simCfg.MustGetFrontendClient(t, op.TargetCluster).DescribeWorkflowExecution(ctx, &types.DescribeWorkflowExecutionRequest{ Domain: op.Domain, Execution: &types.WorkflowExecution{ WorkflowID: op.WorkflowID, }, }) if err != nil { return err } targetClusterWorkflowExecution := resp.GetWorkflowExecutionInfo().GetExecution() if !reflect.DeepEqual(sourceClusterWorkflowExecution, targetClusterWorkflowExecution) { return fmt.Errorf("workflow execution info mismatch between source cluster %s and target cluster %s for workflow %s. \nSource: %+v\nTarget: %+v", op.SourceCluster, op.TargetCluster, op.WorkflowID, *sourceClusterWorkflowExecution, *targetClusterWorkflowExecution) } return nil } func firstDecisionTaskWorker(history []types.HistoryEvent) (string, error) { for _, event := range history { if event.GetEventType() == types.EventTypeDecisionTaskCompleted { return event.GetDecisionTaskCompletedEventAttributes().Identity, nil } } return "", fmt.Errorf("failed to find first decision task worker because there's no DecisionTaskCompleted event found in history") } func lastDecisionTaskWorker(history []types.HistoryEvent) (string, error) { for i := len(history) - 1; i >= 0; i-- { event := history[i] if event.GetEventType() == types.EventTypeDecisionTaskCompleted { return event.GetDecisionTaskCompletedEventAttributes().Identity, nil } } return "", fmt.Errorf("failed to find lastDecisionTaskWorker because there's no DecisionTaskCompleted event found in history") } func waitForOpTime(t *testing.T, op *simTypes.Operation, startTime time.Time) { t.Helper() d := startTime.Add(op.At).Sub(time.Now().UTC()) if d > 0 { simTypes.Logf(t, "Waiting for next operation time (t + %ds). Will sleep for %ds", int(op.At.Seconds()), int(d.Seconds())) <-time.After(d) } simTypes.Logf(t, "Operation time (t + %ds) reached: %v", int(op.At.Seconds()), startTime.Add(op.At)) } func getAllHistory(t *testing.T, simCfg *simTypes.ReplicationSimulationConfig, clusterName, domainName, wfID, runID string) ([]types.HistoryEvent, error) { frontendCl := simCfg.MustGetFrontendClient(t, clusterName) var nextPageToken []byte var history []types.HistoryEvent executionRequest := &types.WorkflowExecution{ WorkflowID: wfID, } if runID != "" { executionRequest.RunID = runID } for { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) response, err := frontendCl.GetWorkflowExecutionHistory(ctx, &types.GetWorkflowExecutionHistoryRequest{ Domain: domainName, Execution: executionRequest, MaximumPageSize: 1000, NextPageToken: nextPageToken, WaitForNewEvent: false, HistoryEventFilterType: types.HistoryEventFilterTypeAllEvent.Ptr(), SkipArchival: true, }) cancel() if err != nil { return nil, fmt.Errorf("failed to get history: %w", err) } for _, event := range response.GetHistory().GetEvents() { if event != nil { history = append(history, *event) } } if response.NextPageToken == nil { return history, nil } nextPageToken = response.NextPageToken time.Sleep(10 * time.Millisecond) // sleep to avoid throttling } } func mustParseJSON(t *testing.T, v []byte) any { var result interface{} err := json.Unmarshal(v, &result) require.NoError(t, err, "failed to unmarshal from json") return result } func mustJSON(t *testing.T, v interface{}) []byte { data, err := json.Marshal(v) require.NoError(t, err, "failed to marshal to json") return data } func waitUntilWorkersReady(t *testing.T) { // workers expose :6060/health endpoint. Poll on them to check if they are healthy simTypes.Logf(t, "Waiting for workers to start and report healthy") workerEndpoints := []string{ "http://cadence-worker0:6060/health", "http://cadence-worker1:6060/health", } for { allHealthy := true for _, endpoint := range workerEndpoints { resp, err := http.Get(endpoint) if err != nil || resp.StatusCode != http.StatusOK { allHealthy = false break } } if allHealthy { break } simTypes.Logf(t, "Workers are not reporting healthy yet. Sleep for 2s and try again") time.Sleep(2 * time.Second) } simTypes.Logf(t, "All workers are healthy") } ================================================ FILE: simulation/replication/run.sh ================================================ #!/bin/bash # Cadence Replication Simulation Test Script # # Usage: # ./simulation/replication/run.sh [OPTIONS] # # Examples: # # Run default scenario # ./simulation/replication/run.sh --scenario default # # # Run specific scenario with custom dockerfile # ./simulation/replication/run.sh --scenario activeactive --dockerfile-suffix .local # # # Rerun without rebuilding images # ./simulation/replication/run.sh --scenario default --rerun # # # Run with custom timestamp # ./simulation/replication/run.sh --scenario activeactive --timestamp 2024-01-15-10-30-00 # # See simulation/replication/README.md for scenario details and output information. set -eo pipefail show_help() { cat << EOF Cadence Replication Simulation Test Script USAGE: $0 [OPTIONS] OPTIONS: -s, --scenario SCENARIO Test scenario to run (required) Corresponds to testdata/replication_simulation_SCENARIO.yaml -r, --rerun Skip rebuilding images and reuse existing containers Useful for faster reruns during development -t, --timestamp TIMESTAMP Custom timestamp for test naming (default: current time) Format: YYYY-MM-DD-HH-MM-SS -d, --dockerfile-suffix SUFFIX Dockerfile suffix for custom builds (default: empty) Example: .local for Dockerfile.local -h, --help Show this help message EXAMPLES: # Run default scenario $0 --scenario default # Run specific scenario with custom dockerfile $0 --scenario activeactive --dockerfile-suffix .local # Rerun without rebuilding images $0 --scenario default --rerun # Run with all options $0 --scenario activeactive --rerun --timestamp 2024-01-15-10-30-00 --dockerfile-suffix .local EOF } # Default values testCase="" rerun="" timestamp="" dockerFileSuffix="" # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in -s|--scenario) testCase="$2" shift 2 ;; -r|--rerun) rerun="rerun" shift ;; -t|--timestamp) timestamp="$2" shift 2 ;; -d|--dockerfile-suffix) dockerFileSuffix="$2" shift 2 ;; -h|--help) show_help exit 0 ;; --) shift break ;; -*) echo "Unknown option: $1" >&2 echo "Use --help for usage information." >&2 exit 1 ;; *) echo "Unexpected positional argument: $1" >&2 echo "Use --scenario to specify the test scenario." >&2 echo "Use --help for usage information." >&2 exit 1 ;; esac done # Require scenario parameter if [[ -z "$testCase" ]]; then echo "Error: --scenario parameter is required" >&2 echo "" >&2 show_help exit 1 fi # Set default timestamp if not provided if [[ -z "$timestamp" ]]; then timestamp="$(date '+%Y-%m-%d-%H-%M-%S')" fi testCfg="testdata/replication_simulation_$testCase.yaml" testName="test-$testCase-$timestamp" resultFolder="replication-simulator-output" mkdir -p "$resultFolder" testSummaryFile="$resultFolder/$testName-summary.txt" # Prune everything and rebuild images unless rerun is specified if [ "$rerun" != "rerun" ]; then echo "Removing some of the previous containers (if exists) to start fresh" SCENARIO=$testCase DOCKERFILE_SUFFIX=$dockerFileSuffix docker compose -f docker/github_actions/docker-compose-local-replication-simulation.yml \ down cassandra cadence-cluster0 cadence-cluster1 cadence-worker0 cadence-worker1 replication-simulator echo "Each simulation run creates multiple new giant container images. Running docker system prune to avoid disk space issues" docker system prune -f echo "Building test images" SCENARIO=$testCase DOCKERFILE_SUFFIX=$dockerFileSuffix docker compose -f docker/github_actions/docker-compose-local-replication-simulation.yml \ build cadence-cluster0 cadence-cluster1 cadence-worker0 cadence-worker1 replication-simulator fi function check_test_failure() { faillog=$(grep 'FAIL: TestReplicationSimulation' -B 10 test.log 2>/dev/null || true) timeoutlog=$(grep 'test timed out' test.log 2>/dev/null || true) if [ -z "$faillog" ] && [ -z "$timeoutlog" ]; then echo "Passed" else echo 'Test failed!!!' echo "Fail log: $faillog" echo "Timeout log: $timeoutlog" echo "Check test.log file for more details" exit 1 fi } trap check_test_failure EXIT echo "Running the scenario '$testCase' with dockerfile suffix: '$dockerFileSuffix'" echo "Test name: $testName" SCENARIO=$testCase DOCKERFILE_SUFFIX=$dockerFileSuffix docker compose \ -f docker/github_actions/docker-compose-local-replication-simulation.yml \ run \ -e REPLICATION_SIMULATION_CONFIG=$testCfg \ --rm --remove-orphans --service-ports --use-aliases \ replication-simulator echo "---- Simulation Summary ----" cat test.log \ | sed -n '/Simulation Summary/,/End of Simulation Summary/p' \ | grep -v "Simulation Summary" \ | tee -a $testSummaryFile echo "End of summary" | tee -a $testSummaryFile printf "\nResults are saved in $testSummaryFile\n" printf "For further ad-hoc analysis, please check $eventLogsFile via jq queries\n" printf "Visit http://localhost:3000/ to view Cadence replication grafana dashboard\n" ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive.yml # Tests that active-active allows workflows to be created in multiple clusters clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: # start workflow in cluster0 - op: start_workflow at: 0s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 # start workflow in cluster1 - op: start_workflow at: 0s workflowID: wf2 workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 # validate that wf1 is started in cluster0 and completed in cluster1 - op: validate at: 70s workflowID: wf1 cluster: cluster0 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 # validate that wf2 is started and completed in cluster1 - op: validate at: 70s workflowID: wf2 cluster: cluster1 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_child.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_child # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_child.yml # Tests that active-active allows workflows to be created in multiple clusters clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: # start workflow in cluster0 - op: start_workflow at: 0s workflowID: wf1 workflowType: child-activity-loop-workflow cluster: cluster0 childWorkflowID: wf1-child childWorkflowTimeout: 65s domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 # start workflow in cluster1 - op: start_workflow at: 0s workflowID: wf2 workflowType: child-activity-loop-workflow cluster: cluster1 childWorkflowID: wf2-child childWorkflowTimeout: 65s domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 # validate that wf1 is started in cluster0 and completed in cluster1 - op: validate at: 70s workflowID: wf1-child cluster: cluster0 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 - op: validate at: 70s workflowID: wf1 cluster: cluster0 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 # validate that wf2 is started and completed in cluster1 - op: validate at: 70s workflowID: wf2-child cluster: cluster1 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 - op: validate at: 70s workflowID: wf2 cluster: cluster1 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_cron.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_cron # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_cron.yml # Tests active-active behaviour with cron workflows clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: # start a cron workflow that runs every minute in cluster0 - op: start_workflow at: 0s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 55s workflowDuration: 35s cronSchedule: "*/1 * * * *" activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 # start a cron workflow that runs every minute in cluster1 - op: start_workflow at: 0s workflowID: wf2 workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 55s workflowDuration: 35s cronSchedule: "*/1 * * * *" activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 # Failover from cluster0 to cluster1 - op: change_active_clusters at: 120s domain: test-domain-aa newClusterAttributes: region: region0: cluster1 # this is changed from cluster0 to cluster1 region1: cluster1 # TODO(active-active): validating cron is not as trivial as checking completion status. # It keeps creating new runs every minute. # Add a custom validation for cron workflows: # - Let it run for 3 minutes # - List the workflows by id # - Validate first 2 continued as new and last one is running (waiting for initial decision task) ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_invalid_cluster_attribute.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_invalid_cluster_attribute # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_invalid_cluster_attribute.yml # Tests that active-active allows workflows to be created in multiple clusters with invalid cluster attribute clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: # start workflow in cluster0 - op: start_workflow at: 0s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: invalid name: invalid want: error: "Cannot start workflow with a cluster attribute that is not found in the domain's metadata." # start workflow in cluster1 - op: start_workflow at: 0s workflowID: wf2 workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: invalid name: invalid want: error: "Cannot start workflow with a cluster attribute that is not found in the domain's metadata." ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_regional_failover.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_regional_failover # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_regional_failover.yml # Tests active-active behaviour during a failover clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: # Start wf1 on cluster0 before failover. It should start in cluster0 and complete in cluster1. - op: start_workflow at: 0s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 45s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 - op: validate at: 60s workflowID: wf1 cluster: cluster0 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster1 # Failover from cluster0 to cluster1 - op: change_active_clusters at: 10s domain: test-domain-aa newClusterAttributes: region: region0: cluster1 # this is changed from cluster0 to cluster1 region1: cluster1 # Start wf2 on cluster0 right before failover. It will be started by cluster0 workers and completed by cluster1 workers. - op: start_workflow at: 9s workflowID: wf2 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 45s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 - op: validate at: 70s workflowID: wf2 cluster: cluster0 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster1 # Start a few more workflows on cluster0 right at/after failover time with 10s delay start. # They will be processed by cluster1 workers. - op: start_workflow at: 10s workflowID: wf3 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 45s workflowDuration: 35s delayStartSeconds: 10 activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 - op: validate at: 70s workflowID: wf3 cluster: cluster0 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 - op: start_workflow at: 11s workflowID: wf4 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 45s workflowDuration: 35s delayStartSeconds: 10 activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 - op: validate at: 71s workflowID: wf4 cluster: cluster0 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 - op: start_workflow at: 12s workflowID: wf5 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 45s workflowDuration: 35s delayStartSeconds: 10 activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 - op: validate at: 72s workflowID: wf5 cluster: cluster0 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 - op: start_workflow at: 13s workflowID: wf6 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 45s workflowDuration: 35s delayStartSeconds: 10 activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 - op: validate at: 73s workflowID: wf6 cluster: cluster0 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 # Start a workflow on cluster0 after domain cache is updated (failover time + 10s refresh interval). Request should be forwarded to cluster1. - op: start_workflow at: 25s workflowID: wf7 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 45s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 - op: validate at: 75s workflowID: wf7 cluster: cluster0 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_regional_failover_start_same_wfid.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_regional_failover_start_same_wfid # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_regional_failover_start_same_wfid.yml # When a domain is configured as active-active # And a workflow is running in one cluster # And a failover occurs # It should not be possible to start another workflow with the same workflowID # And the request should be rejected with error "Workflow execution is already running" clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: # Start wf1 on cluster1 before failover. - op: start_workflow at: 0s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 70s workflowDuration: 60s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 # Failover from cluster1 to cluster0 - op: change_active_clusters at: 10s domain: test-domain-aa newClusterAttributes: region: region1: cluster0 # Attempt to start wf1 on cluster1 again. It will be forwarded to cluster0 and cluster0 should reject with error "Workflow execution is already running". - op: start_workflow at: 30s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 70s workflowDuration: 60s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 want: error: "Workflow execution is already running" # Validate that wf1 is completed in cluster0. - op: validate at: 80s workflowID: wf1 cluster: cluster1 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster0 ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_regional_failover_start_same_wfid_2.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_regional_failover_start_same_wfid_2 # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_regional_failover_start_same_wfid_2.yml # This test is a variant of the activeactive_regional_failover_start_same_wfid test # It tests that when a workflow is already running in one cluster the same workflow cannot be started in another cluster # even after failover clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: # Start wf1 on cluster1 before failover. - op: start_workflow at: 0s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 70s workflowDuration: 60s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 # Failover from cluster1 to cluster0 - op: change_active_clusters at: 10s domain: test-domain-aa newClusterAttributes: region: region1: cluster0 # Attempt to start wf1 on cluster0. It should reject with error "Workflow execution is already running". - op: start_workflow at: 30s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 70s workflowDuration: 60s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 want: error: "Workflow execution is already running" # Validate that wf1 is completed in cluster0. - op: validate at: 80s workflowID: wf1 cluster: cluster1 domain: test-domain-aa want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster0 ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_same_wfid.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_same_wfid # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_same_wfid.yml # Tests that the same wfid cannot be created in multiple clusters in active-active mode clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: # start workflow in cluster0 - op: start_workflow at: 0s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s runIDKey: cluster0 activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 # start workflow in cluster1 - op: start_workflow at: 2s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s runIDKey: cluster1 activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 # validate that wf1 is started in cluster0 and terminated - op: validate at: 70s workflowID: wf1 cluster: cluster0 domain: test-domain-aa runIDKey: cluster0 want: status: terminated # validate that wf2 is started and completed in cluster1 - op: validate at: 70s workflowID: wf1 cluster: cluster1 domain: test-domain-aa runIDKey: cluster1 want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_same_wfid_signalwithstart.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_same_wfid_signalwithstart # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_same_wfid_signalwithstart.yml # When a domain is configured as active-active # And the same workflow ID is used to SignalWithStart in multiple clusters # Then the 'earlier' workflow should be terminated # And the 'later' workflow should complete clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa-conflict: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: - op: signal_with_start_workflow at: 0s workflowID: conflict-wf workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa-conflict signalName: custom-signal signalInput: "cluster0-signal-data" workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s runIDKey: cluster0-run activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 - op: signal_with_start_workflow at: 0s workflowID: conflict-wf workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa-conflict signalName: custom-signal signalInput: "cluster1-signal-data" workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s runIDKey: cluster1-run activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 # Query the cluster0 run to validate it was started with the correct signal # Note that if this query is delayed it will eventually see the signal from the 'later' workflow, as conflict resolution will have occurred - op: query_workflow at: 0s workflowID: conflict-wf cluster: cluster0 domain: test-domain-aa-conflict query: latest-signal-content runIDKey: cluster0-run want: queryResult: ["cluster0-signal-data"] # Query the cluster1 run to validate it was started with the correct signal - op: query_workflow at: 0s workflowID: conflict-wf cluster: cluster1 domain: test-domain-aa-conflict query: latest-signal-content runIDKey: cluster1-run want: queryResult: ["cluster1-signal-data"] - op: validate at: 30s workflowID: conflict-wf runIDKey: cluster0-run cluster: cluster0 domain: test-domain-aa-conflict want: status: terminated startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 - op: validate at: 30s workflowID: conflict-wf runIDKey: cluster1-run cluster: cluster0 domain: test-domain-aa-conflict want: status: running startedByWorkersInCluster: cluster1 - op: validate at: 70s workflowID: conflict-wf runIDKey: cluster1-run cluster: cluster0 domain: test-domain-aa-conflict want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_same_wfid_signalwithstart_delayed.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_same_wfid_signalwithstart_delayed # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_same_wfid_signalwithstart_delayed.yml # When a domain is configured as active-active # And the same workflow ID is used to SignalWithStart in multiple clusters with a 20s delay # Then there should be no duplicate run - a single workflow should contain both signals clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa-delayed: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: - op: signal_with_start_workflow at: 0s workflowID: delayed-wf workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa-delayed signalName: custom-signal signalInput: "cluster0-signal-data" workflowExecutionStartToCloseTimeout: 90s workflowDuration: 60s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 - op: signal_with_start_workflow at: 20s workflowID: delayed-wf workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa-delayed signalName: custom-signal signalInput: "cluster1-signal-data" workflowExecutionStartToCloseTimeout: 90s workflowDuration: 60s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 # Query after the second signal to validate both signals are in the same workflow - op: query_workflow at: 25s workflowID: delayed-wf cluster: cluster0 domain: test-domain-aa-delayed query: latest-signal-content want: queryResult: ["cluster0-signal-data", "cluster1-signal-data"] - op: validate at: 45s workflowID: delayed-wf cluster: cluster0 domain: test-domain-aa-delayed want: status: running startedByWorkersInCluster: cluster0 # Validate workflow completes successfully with both signals - op: validate at: 70s workflowID: delayed-wf cluster: cluster1 domain: test-domain-aa-delayed want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_signalwithstart_terminateifrunning.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_signalwithstart_terminateifrunning # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_signalwithstart_terminateifrunning.yml # Tests that active-active allows workflows to be created in multiple clusters with terminate if running policy. # If the existing workflow is active in the same cluster with the new workflow, the existing workflow should be terminated. # If the existing workflow is active in a different cluster with the same active cluster selection policy, the start workflow should fail. clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: # start workflow in cluster0 with no active cluster selection policy - op: signal_with_start_workflow at: 0s workflowID: wf1 signalName: custom-signal signalInput: "cluster0-signal-data" workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s runIDKey: wf1-0 workflowIDReusePolicy: TERMINATEIFRUNNING # start workflow in cluster0 with active cluster selection policy and terminate if running policy - op: signal_with_start_workflow at: 5s workflowID: wf1 signalName: custom-signal signalInput: "cluster1-signal-data" workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 workflowIDReusePolicy: TERMINATEIFRUNNING runIDKey: wf1-1 # start workflow in cluster1 with different active cluster selection policy and terminate if running policy, should fail - op: signal_with_start_workflow at: 10s workflowID: wf1 signalName: custom-signal signalInput: "cluster2-signal-data" workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 workflowIDReusePolicy: TERMINATEIFRUNNING want: error: "Cannot terminate the existing workflow and start a new workflow because it is active in a different cluster with a different active cluster selection policy." # validate that wf1-0 is started in cluster0 and terminated - op: validate at: 20s workflowID: wf1 cluster: cluster0 domain: test-domain-aa runIDKey: wf1-0 want: status: terminated startedByWorkersInCluster: cluster0 # validate that wf1-1 is started and completed in cluster0 - op: validate at: 75s workflowID: wf1 cluster: cluster1 domain: test-domain-aa runIDKey: wf1-1 want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 ================================================ FILE: simulation/replication/testdata/replication_simulation_activeactive_start_terminateifrunning.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activeactive_start_terminateifrunning # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activeactive_start_terminateifrunning.yml # Tests that active-active allows workflows to be created in multiple clusters with terminate if running policy. # If the existing workflow is active in the same cluster with the new workflow, the existing workflow should be terminated. # If the existing workflow is active in a different cluster with the same active cluster selection policy, the start workflow should fail. clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain-aa: activeClusterName: cluster0 clusterAttributes: region: region0: cluster0 region1: cluster1 operations: # start workflow in cluster0 with no active cluster selection policy - op: start_workflow at: 0s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s runIDKey: wf1-0 workflowIDReusePolicy: TERMINATEIFRUNNING # start workflow in cluster0 with active cluster selection policy and terminate if running policy - op: start_workflow at: 5s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster0 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region0 workflowIDReusePolicy: TERMINATEIFRUNNING runIDKey: wf1-1 # start workflow in cluster1 with different active cluster selection policy and terminate if running policy, should fail - op: start_workflow at: 10s workflowID: wf1 workflowType: timer-activity-loop-workflow cluster: cluster1 domain: test-domain-aa workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 workflowIDReusePolicy: TERMINATEIFRUNNING want: error: "Cannot terminate the existing workflow and start a new workflow because it is active in a different cluster with a different active cluster selection policy." # validate that wf1-0 is started in cluster0 and terminated - op: validate at: 20s workflowID: wf1 cluster: cluster0 domain: test-domain-aa runIDKey: wf1-0 want: status: terminated startedByWorkersInCluster: cluster0 # validate that wf1-1 is started and completed in cluster0 - op: validate at: 75s workflowID: wf1 cluster: cluster1 domain: test-domain-aa runIDKey: wf1-1 want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 ================================================ FILE: simulation/replication/testdata/replication_simulation_activepassive_to_activeactive.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario activepassive_to_activeactive # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_activepassive_to_activeactive.yml # Validates behaviour of workflows in a domain converted from active-passive to active-active # When a domain is migrated from active-passive to active-active # It should run new workflows in region sticky mode clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain: activeClusterName: cluster0 operations: # start wf1 for active-passive domain and validate it runs in cluster0 smoothly # while the domain is migrating to active-active - op: start_workflow at: 0s workflowType: timer-activity-loop-workflow workflowID: wf1 cluster: cluster0 domain: test-domain workflowExecutionStartToCloseTimeout: 90s workflowDuration: 60s - op: validate at: 100s workflowID: wf1 cluster: cluster0 domain: test-domain want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 # migrate domain to active-active - op: change_active_clusters at: 40s domain: test-domain newClusterAttributes: region: region0: cluster0 region1: cluster1 # wait a bit so domain data is replicated to cluster1, # and start wf2 for active-active domain and validate it runs in cluster1 - op: start_workflow at: 75s workflowType: timer-activity-loop-workflow workflowID: wf2 cluster: cluster1 domain: test-domain workflowExecutionStartToCloseTimeout: 60s workflowDuration: 30s - op: validate at: 110s workflowID: wf2 cluster: cluster1 domain: test-domain want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 # and start wf3 for active-active domain and validate it runs in cluster1 - op: start_workflow at: 75s workflowType: timer-activity-loop-workflow workflowID: wf3 cluster: cluster1 domain: test-domain workflowExecutionStartToCloseTimeout: 60s workflowDuration: 30s activeClusterSelectionPolicy: clusterAttribute: scope: region name: region1 - op: validate at: 110s workflowID: wf3 cluster: cluster1 domain: test-domain want: status: completed startedByWorkersInCluster: cluster1 completedByWorkersInCluster: cluster1 ================================================ FILE: simulation/replication/testdata/replication_simulation_budget_manager.yaml ================================================ # This file is a replication simulation scenario spec for testing the budget manager. # It is parsed into ReplicationSimulationConfig struct. # Replication simulations can be run via ./simulation/replication/run.sh --scenario budget_manager # # Test replication with budget manager enabled # When workflows are running in a cluster with budget manager # And a failover occurs # Then tasks should be cached within budget limits # And workflows should continue to run and complete in the new cluster clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain: activeClusterName: cluster0 operations: - op: start_workflow at: 0s workflowType: timer-activity-loop-workflow workflowID: timer-activity-loop-workflow1 cluster: cluster0 domain: test-domain workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s - op: change_active_clusters # failover from cluster0 to cluster1 at: 20s domain: test-domain newActiveCluster: cluster1 # failoverTimeoutSec: 5 # unset means force failover. setting it means graceful failover request - op: validate at: 41s # with the current workflow implementation, it's possible that the workflow may finish right after the 40s mark workflowID: timer-activity-loop-workflow1 cluster: cluster1 domain: test-domain want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster1 # Validate replication occurred by checking the workflow exists in both clusters - op: validate_workflow_replication at: 42s workflowID: timer-activity-loop-workflow1 sourceCluster: cluster1 targetCluster: cluster0 domain: test-domain ================================================ FILE: simulation/replication/testdata/replication_simulation_clusterredirection.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario clusterredirection # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_clusterredirection.yml # Ensures redirection of RPCs behaves as expected clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain: activeClusterName: cluster0 operations: # Start wf1 in cluster1 will be handled by cluster0 (active cluster) - op: start_workflow at: 0s workflowType: query-workflow workflowID: wf1 cluster: cluster1 domain: test-domain workflowExecutionStartToCloseTimeout: 30s workflowDuration: 20s # Query the wf1 in cluster0 and expect cluster0 worker handles query - op: query_workflow at: 10s workflowID: wf1 cluster: cluster0 domain: test-domain query: cluster-name consistencyLevel: eventual want: queryResult: cluster0 # Query the wf1 in cluster1 with eventual consistency and expect cluster1 worker handles query - op: query_workflow at: 10s workflowID: wf1 cluster: cluster1 domain: test-domain query: cluster-name consistencyLevel: eventual want: queryResult: cluster1 # Query the wf1 in cluster1 with strong consistency and expect cluster0 (active cluster) worker handles query - op: query_workflow at: 10s workflowID: wf1 cluster: cluster1 domain: test-domain query: cluster-name consistencyLevel: strong want: queryResult: cluster0 # Validate wf1 completed in cluster0 - op: validate at: 25s workflowID: wf1 cluster: cluster0 domain: test-domain want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 # SignalWithStart wf2 in cluster1, will be handled by cluster0 (active cluster) - op: signal_with_start_workflow at: 30s workflowType: query-workflow workflowID: wf2 cluster: cluster1 domain: test-domain signalName: custom-signal signalInput: "x" workflowExecutionStartToCloseTimeout: 30s workflowDuration: 20s # SignalWithStart wf2 in cluster1 again and signal will be forwarded to cluster0 (active cluster) and wf2 will be started in cluster0 - op: signal_with_start_workflow at: 31s workflowType: query-workflow workflowID: wf2 cluster: cluster1 domain: test-domain signalName: custom-signal signalInput: "y" workflowExecutionStartToCloseTimeout: 30s workflowDuration: 20s # Query wf2 in cluster1 with strong consistency and expect cluster0 (active cluster) worker handles query # Should return signal count = 2 - op: query_workflow at: 40s workflowID: wf2 cluster: cluster1 domain: test-domain query: signal-count consistencyLevel: strong want: queryResult: 2.0 # query result unmarshalled to float64 so we need to use float64 here for comparison to work # Validate wf2 completed in cluster0 - op: validate at: 60s workflowID: wf2 cluster: cluster0 domain: test-domain want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 ================================================ FILE: simulation/replication/testdata/replication_simulation_default.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulations can be run via ./simulation/replication/run.sh --scenario default # # Test workflow behaviour during a failover # When a workflow is running in a cluster # And a failover occurs # Then the workflow should continue to run and complete in the new cluster clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain: activeClusterName: cluster0 operations: - op: start_workflow at: 0s workflowType: timer-activity-loop-workflow workflowID: timer-activity-loop-workflow1 cluster: cluster0 domain: test-domain workflowExecutionStartToCloseTimeout: 65s workflowDuration: 35s - op: change_active_clusters # failover from cluster0 to cluster1 at: 20s domain: test-domain newActiveCluster: cluster1 # failoverTimeoutSec: 5 # unset means force failover. setting it means graceful failover request - op: validate at: 41s # with the current workflow implementation, it's possible that the workflow may finish right after the 40s mark workflowID: timer-activity-loop-workflow1 cluster: cluster1 domain: test-domain want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster1 ================================================ FILE: simulation/replication/testdata/replication_simulation_reset.yaml ================================================ # This file is a replication simulation scenario spec. # It is parsed into ReplicationSimulationConfig struct. # Replication simulation for this file can be run via ./simulation/replication/run.sh --scenario reset # Dynamic config overrides can be set via config/dynamicconfig/replication_simulation_reset.yml # Tests the behaviour of workflows when failovers and reset commands are issued clusters: cluster0: grpcEndpoint: "cadence-cluster0:7833" cluster1: grpcEndpoint: "cadence-cluster1:7833" # primaryCluster is where domain data is written to and replicates to others. e.g. domain registration primaryCluster: "cluster0" domains: test-domain: activeClusterName: cluster0 test-domain-2: activeClusterName: cluster0 operations: # Start two workflows in different domains - op: start_workflow at: 0s workflowType: activity-loop-workflow workflowID: activity-loop-workflow1 activityCount: 45 cluster: cluster0 domain: test-domain workflowExecutionStartToCloseTimeout: 60s - op: start_workflow at: 0s workflowType: activity-loop-workflow workflowID: activity-loop-workflow1 activityCount: 45 cluster: cluster0 domain: test-domain-2 workflowExecutionStartToCloseTimeout: 60s # Failover from cluster0 to cluster1 - op: change_active_clusters at: 20s domain: test-domain newActiveCluster: cluster1 # failoverTimeoutSec: 5 # unset means force failover. setting it means graceful failover request - op: validate at: 62s workflowID: activity-loop-workflow1 cluster: cluster0 domain: test-domain-2 want: status: timed-out startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 - op: validate_workflow_replication at: 62s workflowID: activity-loop-workflow1 sourceCluster: cluster0 targetCluster: cluster1 domain: test-domain-2 - op: validate at: 62s workflowID: activity-loop-workflow1 cluster: cluster0 domain: test-domain want: status: timed-out startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster1 - op: validate_workflow_replication at: 62s workflowID: activity-loop-workflow1 sourceCluster: cluster0 targetCluster: cluster1 domain: test-domain - op: reset_workflow at: 70s workflowID: activity-loop-workflow1 cluster: cluster0 domain: test-domain-2 eventID: 94 - op: reset_workflow at: 70s workflowID: activity-loop-workflow1 cluster: cluster1 domain: test-domain eventID: 94 - op: validate at: 132s workflowID: activity-loop-workflow1 cluster: cluster0 domain: test-domain-2 want: status: timed-out startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 - op: validate_workflow_replication at: 132s workflowID: activity-loop-workflow1 sourceCluster: cluster0 targetCluster: cluster1 domain: test-domain-2 - op: validate at: 132s workflowID: activity-loop-workflow1 cluster: cluster1 domain: test-domain want: status: timed-out startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster1 - op: validate_workflow_replication at: 132s workflowID: activity-loop-workflow1 sourceCluster: cluster1 targetCluster: cluster0 domain: test-domain - op: reset_workflow at: 140s workflowID: activity-loop-workflow1 cluster: cluster0 domain: test-domain-2 eventID: 205 - op: reset_workflow at: 140s workflowID: activity-loop-workflow1 cluster: cluster1 domain: test-domain eventID: 205 - op: validate at: 202s workflowID: activity-loop-workflow1 cluster: cluster0 domain: test-domain-2 want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster0 - op: validate_workflow_replication at: 202s workflowID: activity-loop-workflow1 sourceCluster: cluster0 targetCluster: cluster1 domain: test-domain-2 - op: validate at: 202s workflowID: activity-loop-workflow1 cluster: cluster1 domain: test-domain want: status: completed startedByWorkersInCluster: cluster0 completedByWorkersInCluster: cluster1 - op: validate_workflow_replication at: 202s workflowID: activity-loop-workflow1 sourceCluster: cluster1 targetCluster: cluster0 domain: test-domain ================================================ FILE: simulation/replication/types/repl_sim_config.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package types import ( "context" "fmt" "os" "testing" "time" "github.com/stretchr/testify/require" adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/yarpc" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/transport/grpc" "gopkg.in/yaml.v2" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" grpcClient "github.com/uber/cadence/client/wrappers/grpc" "github.com/uber/cadence/common/types" ) type ReplicationSimulationOperation string const ( ReplicationSimulationOperationStartWorkflow ReplicationSimulationOperation = "start_workflow" ReplicationSimulationOperationResetWorkflow ReplicationSimulationOperation = "reset_workflow" ReplicationSimulationOperationChangeActiveClusters ReplicationSimulationOperation = "change_active_clusters" ReplicationSimulationOperationValidate ReplicationSimulationOperation = "validate" ReplicationSimulationOperationQueryWorkflow ReplicationSimulationOperation = "query_workflow" ReplicationSimulationOperationSignalWithStartWorkflow ReplicationSimulationOperation = "signal_with_start_workflow" ReplicationSimulationOperationValidateWorkflowReplication ReplicationSimulationOperation = "validate_workflow_replication" ) type ReplicationSimulationConfig struct { // Clusters is the map of all clusters Clusters map[string]*Cluster `yaml:"clusters"` // PrimaryCluster is used for domain registration PrimaryCluster string `yaml:"primaryCluster"` Domains map[string]ReplicationDomainConfig `yaml:"domains"` Operations []*Operation `yaml:"operations"` } type ReplicationDomainConfig struct { ActiveClusterName string `yaml:"activeClusterName"` // ClusterAttributes specifies the Attributes for a domain and is passed to ActiveClusters in the RegisterDomainRequest // It is a simplified expression for the AttributeScopes type // The format should be expressed as follows: // clusterAttributes: // This will be mapped to AttributeScopes // region: // This is the key for a ClusterAttributeScope. All children are mapped to ClusterAttributes // us-west1: cluster0 // us-west1 is the key within a ClusterAttributeScope // us-east1: cluster1 // cluster1 is the ActiveClusterName for the corresponding ActiveClusterInfo // cityID: // ... ClusterAttributes ClusterAttributesMap `yaml:"clusterAttributes"` } type Operation struct { Type ReplicationSimulationOperation `yaml:"op"` At time.Duration `yaml:"at"` Cluster string `yaml:"cluster"` SourceCluster string `yaml:"sourceCluster"` TargetCluster string `yaml:"targetCluster"` WorkflowType string `yaml:"workflowType"` WorkflowID string `yaml:"workflowID"` WorkflowExecutionStartToCloseTimeout time.Duration `yaml:"workflowExecutionStartToCloseTimeout"` WorkflowDuration time.Duration `yaml:"workflowDuration"` ChildWorkflowID string `yaml:"childWorkflowID"` ChildWorkflowTimeout time.Duration `yaml:"childWorkflowTimeout"` ActivityCount int `yaml:"activityCount"` DelayStartSeconds int32 `yaml:"delayStartSeconds"` CronSchedule string `yaml:"cronSchedule"` ActiveClusterSelectionPolicy *types.ActiveClusterSelectionPolicy `yaml:"activeClusterSelectionPolicy"` WorkflowIDReusePolicy *types.WorkflowIDReusePolicy `yaml:"workflowIDReusePolicy"` Query string `yaml:"query"` ConsistencyLevel string `yaml:"consistencyLevel"` SignalName string `yaml:"signalName"` SignalInput any `yaml:"signalInput"` EventID int64 `yaml:"eventID"` Domain string `yaml:"domain"` NewActiveCluster string `yaml:"newActiveCluster"` // NewClusterAttributes specifies the AttributeScopes to change for the domain // This can be a sub-set of the total AttributeScopes for the domain NewClusterAttributes ClusterAttributesMap `yaml:"newClusterAttributes"` FailoverTimeout *int32 `yaml:"failoverTimeoutSec"` // RunIDKey specifies a key to store/retrieve RunID for this operation RunIDKey string `yaml:"runIDKey"` Want Validation `yaml:"want"` } type Validation struct { Status string `yaml:"status"` StartedByWorkersInCluster string `yaml:"startedByWorkersInCluster"` CompletedByWorkersInCluster string `yaml:"completedByWorkersInCluster"` Error string `yaml:"error"` QueryResult any `yaml:"queryResult"` } type Cluster struct { GRPCEndpoint string `yaml:"grpcEndpoint"` AdminClient admin.Client `yaml:"-"` FrontendClient frontend.Client `yaml:"-"` } func LoadConfig() (*ReplicationSimulationConfig, error) { path := os.Getenv("REPLICATION_SIMULATION_CONFIG") if path == "" { path = DefaultTestCase } confContent, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("failed to read config file: %w", err) } var cfg ReplicationSimulationConfig err = yaml.Unmarshal(confContent, &cfg) if err != nil { return nil, fmt.Errorf("failed to unmarshal config: %w", err) } fmt.Printf("Loaded config from path: %s\n", path) return &cfg, nil } func (s *ReplicationSimulationConfig) MustGetFrontendClient(t *testing.T, clusterName string) frontend.Client { t.Helper() cluster, ok := s.Clusters[clusterName] require.True(t, ok, "Cluster %s not found in the config", clusterName) require.NotNil(t, cluster.FrontendClient, "Cluster %s frontend client not initialized", clusterName) return cluster.FrontendClient } func (s *ReplicationSimulationConfig) MustInitClientsFor(t *testing.T, clusterName string) { t.Helper() cluster, ok := s.Clusters[clusterName] require.True(t, ok, "Cluster %s not found in the config", clusterName) outbounds := transport.Outbounds{Unary: grpc.NewTransport().NewSingleOutbound(cluster.GRPCEndpoint)} dispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: "cadence-client", Outbounds: yarpc.Outbounds{ "cadence-frontend": outbounds, }, }) if err := dispatcher.Start(); err != nil { dispatcher.Stop() require.NoError(t, err, "failed to create outbound transport channel") } clientConfig := dispatcher.ClientConfig("cadence-frontend") cluster.FrontendClient = grpcClient.NewFrontendClient( apiv1.NewDomainAPIYARPCClient(clientConfig), apiv1.NewWorkflowAPIYARPCClient(clientConfig), apiv1.NewWorkerAPIYARPCClient(clientConfig), apiv1.NewVisibilityAPIYARPCClient(clientConfig), ) cluster.AdminClient = grpcClient.NewAdminClient(adminv1.NewAdminAPIYARPCClient(clientConfig)) Logf(t, "Initialized clients for cluster %s", clusterName) } func (s *ReplicationSimulationConfig) IsActiveActiveDomain(domainName string) bool { domainCfg, ok := s.Domains[domainName] if !ok { return false } return len(domainCfg.ClusterAttributes.attributeScopes) > 0 } func (s *ReplicationSimulationConfig) MustRegisterDomain( t *testing.T, domainName string, domainCfg ReplicationDomainConfig, ) { Logf(t, "Registering domain: %s", domainName) var clusters []*types.ClusterReplicationConfiguration for name := range s.Clusters { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: name, }) } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() req := &types.RegisterDomainRequest{ Name: domainName, Clusters: clusters, WorkflowExecutionRetentionPeriodInDays: 1, IsGlobalDomain: true, } if len(domainCfg.ActiveClusterName) > 0 { req.ActiveClusterName = domainCfg.ActiveClusterName } else { // ActiveClusterName is required for all global domains require.Fail(t, "activeClusterName is required but missing for domain %s", domainName) } if !domainCfg.ClusterAttributes.IsEmpty() { req.ActiveClusters = &types.ActiveClusters{} req.ActiveClusters.AttributeScopes = domainCfg.ClusterAttributes.ToAttributeScopes() } err := s.MustGetFrontendClient(t, s.PrimaryCluster).RegisterDomain(ctx, req) if err != nil { if _, ok := err.(*types.DomainAlreadyExistsError); !ok { require.NoError(t, err, "failed to register domain") } else { Logf(t, "Domains already exists: %s", domainName) } return } Logf(t, "Registered domain: %s", domainName) } // ClusterAttributesMap is a custom type for YAML unmarshalling of cluster attributes. type ClusterAttributesMap struct { attributeScopes map[string]types.ClusterAttributeScope } // UnmarshalYAML implements custom YAML unmarshalling for ClusterAttributesMap. func (c *ClusterAttributesMap) UnmarshalYAML(unmarshal func(interface{}) error) error { // Unmarshal into the simplified map structure var clusterAttributes map[string]map[string]string if err := unmarshal(&clusterAttributes); err != nil { return err } // Convert to AttributeScopes c.attributeScopes = make(map[string]types.ClusterAttributeScope) // scopeType is the key for ClusterAttributeScope // It is the name of the scope, e.g region, datacenter, city, etc. for scopeType, clusterAttributeScope := range clusterAttributes { attributeScope := types.ClusterAttributeScope{ ClusterAttributes: make(map[string]types.ActiveClusterInfo), } // attributeName is the ClusterAttribute key, e.g seattle for a city scope, us-west for a region scope, etc. // activeClusterName is the name of a cluster corresponding to the clusterMetadata setup for attributeName, activeClusterName := range clusterAttributeScope { attributeScope.ClusterAttributes[attributeName] = types.ActiveClusterInfo{ ActiveClusterName: activeClusterName, } } c.attributeScopes[scopeType] = attributeScope } return nil } // ToAttributeScopes is a convenience method to return the parsed AttributeScopes map. func (c *ClusterAttributesMap) ToAttributeScopes() map[string]types.ClusterAttributeScope { if c == nil { return nil } return c.attributeScopes } func (c *ClusterAttributesMap) IsEmpty() bool { return c == nil || len(c.attributeScopes) == 0 } ================================================ FILE: simulation/replication/types/types.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package types import ( "fmt" "testing" "time" ) const ( DefaultTestCase = "testdata/replication_simulation_default.yaml" TasklistName = "test-tasklist" TimerInterval = 5 * time.Second ) type OperationFunction func(t *testing.T, op *Operation, simCfg *ReplicationSimulationConfig) error type WorkflowInput struct { Duration time.Duration ActivityCount int ChildWorkflowID string ChildWorkflowTimeout time.Duration } type WorkflowOutput struct { Count int } type ReplicationSimulation struct { RunIDRegistry map[string]string } func NewReplicationSimulation() *ReplicationSimulation { return &ReplicationSimulation{ RunIDRegistry: make(map[string]string), } } func (s *ReplicationSimulation) StoreRunID(key, runID string) error { if s.RunIDRegistry == nil { return fmt.Errorf("runIDRegistry is nil") } s.RunIDRegistry[key] = runID return nil } func (s *ReplicationSimulation) GetRunID(key string) (string, error) { if s.RunIDRegistry == nil { return "", fmt.Errorf("runIDRegistry is nil") } return s.RunIDRegistry[key], nil } func WorkerIdentityFor(clusterName string, domainName string) string { if domainName == "" { return fmt.Sprintf("worker-%s", clusterName) } return fmt.Sprintf("worker-%s-%s", domainName, clusterName) } func Logf(t *testing.T, msg string, args ...interface{}) { t.Helper() msg = time.Now().Format(time.RFC3339Nano) + "\t" + msg t.Logf(msg, args...) } ================================================ FILE: simulation/replication/worker/cmd/main.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "context" "flag" "fmt" "net/http" "os" "os/signal" "sync" "sync/atomic" "syscall" "time" "github.com/uber-go/tally" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" "go.uber.org/cadence/.gen/go/shared" "go.uber.org/cadence/activity" "go.uber.org/cadence/compatibility" "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/yarpc" "go.uber.org/yarpc/transport/grpc" "go.uber.org/zap" "go.uber.org/zap/zapcore" "github.com/uber/cadence/common" simTypes "github.com/uber/cadence/simulation/replication/types" "github.com/uber/cadence/simulation/replication/workflows" ) var ( clusterName = flag.String("cluster", "", "cluster name") ready int32 ) func main() { config := zap.NewDevelopmentConfig() config.Level.SetLevel(zapcore.InfoLevel) logger, err := config.Build() if err != nil { panic(fmt.Sprintf("failed to create logger: %v", err)) } flag.Parse() if *clusterName == "" { logger.Fatal("cluster name is not set") } simCfg, err := simTypes.LoadConfig() if err != nil { logger.Fatal("failed to load simulation config", zap.Error(err)) } cluster, ok := simCfg.Clusters[*clusterName] if !ok { logger.Fatal("cluster not found in config", zap.String("cluster", *clusterName)) } dispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: simTypes.WorkerIdentityFor(*clusterName, ""), Outbounds: yarpc.Outbounds{ "cadence-frontend": {Unary: grpc.NewTransport().NewSingleOutbound(cluster.GRPCEndpoint)}, }, }) err = dispatcher.Start() if err != nil { logger.Fatal("Failed to start dispatcher", zap.Error(err)) } defer dispatcher.Stop() clientConfig := dispatcher.ClientConfig("cadence-frontend") cadenceClient := compatibility.NewThrift2ProtoAdapter( apiv1.NewDomainAPIYARPCClient(clientConfig), apiv1.NewWorkflowAPIYARPCClient(clientConfig), apiv1.NewWorkerAPIYARPCClient(clientConfig), apiv1.NewVisibilityAPIYARPCClient(clientConfig), ) http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { if atomic.LoadInt32(&ready) == int32(len(simCfg.Domains)) { w.WriteHeader(http.StatusOK) } else { w.WriteHeader(http.StatusServiceUnavailable) } }) go http.ListenAndServe(":6060", nil) wg := sync.WaitGroup{} for domainName := range simCfg.Domains { wg.Add(1) go func(name string) { waitUntilDomainReady(logger, cadenceClient, name) wg.Done() }(domainName) } wg.Wait() // Create a channel to receive termination signals sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) // Create a slice to hold the started workers var workers []worker.Worker for domainName := range simCfg.Domains { workerOptions := worker.Options{ Identity: simTypes.WorkerIdentityFor(*clusterName, domainName), Logger: logger, MetricsScope: tally.NewTestScope(simTypes.TasklistName, map[string]string{"cluster": *clusterName}), } w := worker.New( cadenceClient, domainName, simTypes.TasklistName, workerOptions, ) for name, wf := range workflows.Workflows(*clusterName) { w.RegisterWorkflowWithOptions(wf, workflow.RegisterOptions{Name: name}) } for name, act := range workflows.Activities { w.RegisterActivityWithOptions(act, activity.RegisterOptions{Name: name}) } err := w.Start() if err != nil { logger.Fatal("Failed to start worker", zap.Error(err)) } workers = append(workers, w) // Add the worker to the slice fmt.Printf("Started worker for domain: %s\n", domainName) logger.Info("Started worker", zap.String("cluster", *clusterName), zap.String("endpoint", cluster.GRPCEndpoint)) } logger.Info("All workers started. Waiting for SIGINT or SIGTERM") sig := <-sigs logger.Sugar().Infof("Received signal: %v so terminating", sig) // Stop each worker gracefully logger.Info("Stopping workers...") for _, w := range workers { w.Stop() logger.Info("Stopped worker") } } func waitUntilDomainReady(logger *zap.Logger, client workflowserviceclient.Interface, domainName string) { for { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) _, err := client.DescribeDomain(ctx, &shared.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) cancel() if err == nil { logger.Info("Domains is ready", zap.String("domain", domainName)) atomic.AddInt32(&ready, 1) return } logger.Info("Domains not ready", zap.String("domain", domainName), zap.Error(err)) time.Sleep(2 * time.Second) } } ================================================ FILE: simulation/replication/workflows/activityloop/workflow.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package activityloop import ( "context" "fmt" "time" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "github.com/uber/cadence/simulation/replication/types" ) func Workflow(ctx workflow.Context, input types.WorkflowInput) (types.WorkflowOutput, error) { logger := workflow.GetLogger(ctx) logger.Sugar().Infof("single-serial-activity-workflow started with input: %+v", input) count := 0 for count < input.ActivityCount { logger.Sugar().Infof("single-serial-activity-workflow iteration %d", count) selector := workflow.NewSelector(ctx) activityFuture := workflow.ExecuteActivity(workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ TaskList: types.TasklistName, ScheduleToStartTimeout: 10 * time.Second, StartToCloseTimeout: 10 * time.Second, }), FormatStringActivity, "World") selector.AddFuture(activityFuture, func(f workflow.Future) { logger.Info("single-serial-activity-workflow completed activity") }) selector.Select(ctx) count++ } logger.Info("single-serial-activity-workflow completed") return types.WorkflowOutput{Count: count}, nil } func FormatStringActivity(ctx context.Context, input string) (string, error) { logger := activity.GetLogger(ctx) logger.Info("single-serial-activity-workflow format-string-activity started") time.Sleep(3 * time.Second) return fmt.Sprintf("Hello, %s!", input), nil } ================================================ FILE: simulation/replication/workflows/childactivityloop/workflow.go ================================================ package childactivityloop import ( "go.uber.org/cadence/workflow" "github.com/uber/cadence/simulation/replication/types" ) func Workflow(ctx workflow.Context, input types.WorkflowInput) (types.WorkflowOutput, error) { logger := workflow.GetLogger(ctx) logger.Sugar().Infof("child-activity-loop-workflow started with input: %+v", input) cwo := workflow.ChildWorkflowOptions{ WorkflowID: input.ChildWorkflowID, ExecutionStartToCloseTimeout: input.ChildWorkflowTimeout, } ctx = workflow.WithChildOptions(ctx, cwo) var output types.WorkflowOutput err := workflow.ExecuteChildWorkflow(ctx, "timer-activity-loop-workflow", input).Get(ctx, &output) if err != nil { logger.Sugar().Errorf("failed to execute child workflow: %v", err) return types.WorkflowOutput{}, err } return output, nil } ================================================ FILE: simulation/replication/workflows/query/workflow.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package query import ( "go.uber.org/cadence/workflow" "github.com/uber/cadence/simulation/replication/types" ) const ( signalName = "custom-signal" clusterNameQuery = "cluster-name" signalCountQuery = "signal-count" ) type Runner struct { ClusterName string } func (r *Runner) Workflow(ctx workflow.Context, input types.WorkflowInput) (types.WorkflowOutput, error) { logger := workflow.GetLogger(ctx) logger.Sugar().Infof("query workflow started with input: %+v", input) err := workflow.SetQueryHandler(ctx, clusterNameQuery, func() (string, error) { logger.Sugar().Infof("query handler called. returning cluster name: %s", r.ClusterName) return r.ClusterName, nil }) if err != nil { logger.Sugar().Errorf("failed to set query handler: %v", err) return types.WorkflowOutput{}, err } signalCount := 0 err = workflow.SetQueryHandler(ctx, signalCountQuery, func() (int, error) { logger.Sugar().Infof("query handler called. returning signal count: %d", signalCount) return signalCount, nil }) if err != nil { logger.Sugar().Errorf("failed to set query handler: %v", err) return types.WorkflowOutput{}, err } endTime := workflow.Now(ctx).Add(input.Duration) signalCh := workflow.GetSignalChannel(ctx, signalName) done := false for { selector := workflow.NewSelector(ctx) // timer timerCtx, timerCancel := workflow.WithCancel(ctx) waitTimer := workflow.NewTimer(timerCtx, endTime.Sub(workflow.Now(ctx))) selector.AddFuture(waitTimer, func(f workflow.Future) { done = true }) // signal selector.AddReceive(signalCh, func(c workflow.Channel, more bool) { var signal string for c.ReceiveAsync(&signal) { signalCount++ logger.Sugar().Infof("signal received: %s", signal) } }) selector.Select(ctx) timerCancel() if done { break } } logger.Info("query workflow completed") return types.WorkflowOutput{}, nil } ================================================ FILE: simulation/replication/workflows/timeractivityloop/workflow.go ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package timeractivityloop import ( "context" "fmt" "time" "go.uber.org/cadence/activity" "go.uber.org/cadence/workflow" "github.com/uber/cadence/simulation/replication/types" ) const ( latestSignalContentQuery = "latest-signal-content" signalName = "custom-signal" ) func Workflow(ctx workflow.Context, input types.WorkflowInput) (types.WorkflowOutput, error) { logger := workflow.GetLogger(ctx) logger.Sugar().Infof("timer-activity-loop-workflow started with input: %+v", input) signalContent := make([]string, 0) err := workflow.SetQueryHandler(ctx, latestSignalContentQuery, func() ([]string, error) { logger.Sugar().Infof("query handler called. returning all signal content: %s", signalContent) return signalContent, nil }) if err != nil { logger.Sugar().Errorf("failed to set query handler: %v", err) return types.WorkflowOutput{}, err } signalCh := workflow.GetSignalChannel(ctx, signalName) endTime := workflow.Now(ctx).Add(input.Duration) count := 0 for { logger.Sugar().Infof("timer-activity-loop-workflow iteration %d", count) selector := workflow.NewSelector(ctx) activityFuture := workflow.ExecuteActivity(workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ TaskList: types.TasklistName, ScheduleToStartTimeout: 10 * time.Second, StartToCloseTimeout: 10 * time.Second, }), FormatStringActivity, "World") selector.AddFuture(activityFuture, func(f workflow.Future) { logger.Info("timer-activity-loop-workflow completed activity") }) // use timer future to send notification email if processing takes too long timerFuture := workflow.NewTimer(ctx, types.TimerInterval) selector.AddFuture(timerFuture, func(f workflow.Future) { logger.Info("timer-activity-loop-workflow timer fired") }) selector.AddReceive(signalCh, func(c workflow.Channel, more bool) { var signal string for c.ReceiveAsync(&signal) { logger.Sugar().Infof("signal received: %s", signal) signalContent = append(signalContent, signal) } }) // wait for both activity and timer to complete selector.Select(ctx) selector.Select(ctx) count++ now := workflow.Now(ctx) if now.Before(endTime) { logger.Sugar().Infof("timer-activity-loop-workflow will continue iteration because [now %v] < [endTime %v]", now, endTime) } else { logger.Sugar().Infof("timer-activity-loop-workflow will exit because [now %v] >= [endTime %v]", now, endTime) break } } logger.Info("timer-activity-loop-workflow completed") return types.WorkflowOutput{Count: count}, nil } func FormatStringActivity(ctx context.Context, input string) (string, error) { logger := activity.GetLogger(ctx) logger.Info("timer-activity-loop-workflow format-string-activity started") return fmt.Sprintf("Hello, %s!", input), nil } ================================================ FILE: simulation/replication/workflows/workflows.go ================================================ package workflows import ( "github.com/uber/cadence/simulation/replication/workflows/activityloop" "github.com/uber/cadence/simulation/replication/workflows/childactivityloop" "github.com/uber/cadence/simulation/replication/workflows/query" "github.com/uber/cadence/simulation/replication/workflows/timeractivityloop" ) // Add workflows and activities to this map to register them with the worker. var ( Workflows = func(clusterName string) map[string]any { queryWFRunner := &query.Runner{ClusterName: clusterName} return map[string]any{ "timer-activity-loop-workflow": timeractivityloop.Workflow, "activity-loop-workflow": activityloop.Workflow, "query-workflow": queryWFRunner.Workflow, "child-activity-loop-workflow": childactivityloop.Workflow, } } Activities = map[string]any{ "timer-activity-loop-format-string-activity": timeractivityloop.FormatStringActivity, "activity-loop-format-string-activity": activityloop.FormatStringActivity, } ) ================================================ FILE: testflags/testflags.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package testflags import ( "fmt" "os" "testing" ) // Pass these testflags in as environment variables in order to run tests marked as needing an // external dependency. // Tests can be marked as having an external dependency by calling the Require* functions below. // In order to start external dependencies, see documentation at https://github.com/uber/cadence var ( cassandra = "CASSANDRA" mongodb = "MONGODB" mysql = "MYSQL" postgres = "POSTGRES" etcd = "ETCD" ) // NOTE: We are using environment variables instead of go testflags or go build directives, because we: // 1) Want tests to be built all the time (go build directives don't give us that) // 2) Want tests to be marked as skipped (go build directives don't give us that) // 3) Want to be able to run individual tests (go testflags errors if the test doesn't import // something that defines the testflags.) func RequireMySQL(t *testing.T) { require(t, mysql) } func RequirePostgres(t *testing.T) { require(t, postgres) } func RequireMongoDB(t *testing.T) { require(t, mongodb) } func RequireCassandra(t *testing.T) { require(t, cassandra) } func RequireEtcd(t *testing.T) { require(t, etcd) } func require(t *testing.T, name string) { if !checkEnv(name) { t.Skip(fmt.Sprintf("Skipping test that requires %s to run - start %s and set '%s=1' environment variable to run this test.", name, name, name)) } } func checkEnv(name string) bool { return os.Getenv(name) != "" } ================================================ FILE: tools/cassandra/README.md ================================================ ## Using the cassandra schema tool This package contains the tooling for cadence cassandra operations. ## For localhost development ``` make install-schema ``` > NOTE: See [CONTRIBUTING](/CONTRIBUTING.md) for prerequisite of make command. ## For production ### Get the Cassandra Schema tool * Use brew to install CLI: `brew install cadence-workflow` which includes `cadence-cassandra-tool` * The schema files are located at `/usr/local/etc/cadence/schema/`. * Follow the [instructions](https://github.com/cadence-workflow/cadence/discussions/4457) if you need to install older versions of schema tools via homebrew. However, easier way is to use new versions of schema tools with old versions of schemas. All you need is to check out the older version of schemas from this repo. Run `git checkout v0.21.3` to get the v0.21.3 schemas in [the schema folder](/schema). * Or build yourself, with `make cadence-cassandra-tool`. See [CONTRIBUTING](/CONTRIBUTING.md) for prerequisite of make command. > Note: The binaries can also be found in the `ubercadence/server` docker images ### Do one time database creation and schema setup for a new cluster This uses Cassandra's SimpleStratagey for replication. For production, we recommend using a replication factor of 3 with NetworkTopologyStrategy. ``` cadence-cassandra-tool --ep $CASSANDRA_SEEDS create -k $KEYSPACE --rf $RF ``` See https://www.ecyrd.com/cassandracalculator for an easy way to determine how many nodes and what replication factor you will want to use. Note that Cadence by default uses `Quorum` for read and write consistency. ``` ./cadence-cassandra-tool -ep 127.0.0.1 -k cadence setup-schema -v 0.0 # this sets up just the schema version tables with initial version of 0.0 ./cadence-cassandra-tool -ep 127.0.0.1 -k cadence update-schema -d ./schema/cassandra/cadence/versioned # upgrades your schema to the latest version ./cadence-cassandra-tool -ep 127.0.0.1 -k cadence_visibility setup-schema -v 0.0 # this sets up just the schema version tables with initial version of 0.0 for visibility ./cadence-cassandra-tool -ep 127.0.0.1 -k cadence_visibility update-schema -d ./schema/cassandra/visibility/versioned # upgrades your schema to the latest version for visibility ``` ### Update schema as part of a release You can only upgrade to a new version after the initial setup done above. ``` ./cadence-cassandra-tool -ep 127.0.0.1 -k cadence update-schema -d ./schema/cassandra/cadence/versioned -v x.xx -dryrun # executes a dryrun of upgrade to version x.xx ./cadence-cassandra-tool -ep 127.0.0.1 -k cadence update-schema -d ./schema/cassandra/cadence/versioned -v x.xx # actually executes the upgrade to version x.xx ./cadence-cassandra-tool -ep 127.0.0.1 -k cadence_visibility update-schema -d ./schema/cassandra/visibility/versioned -v x.xx --dryrun # executes a dryrun of upgrade to version x.xx ./cadence-cassandra-tool -ep 127.0.0.1 -k cadence_visibility update-schema -d ./schema/cassandra/visibility/versioned -v x.xx # actually executes the upgrade to version x.xx ``` ================================================ FILE: tools/cassandra/cqlclient.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "context" "fmt" "log" "time" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/tools/common/schema" ) type ( CqlClient interface { CreateDatabase(name string) error DropDatabase(name string) error CreateKeyspace(name string) error CreateNTSKeyspace(name string, datacenter string) error DropKeyspace(name string) error DropAllTables() error CreateSchemaVersionTables() error ReadSchemaVersion() (string, error) UpdateSchemaVersion(newVersion string, minCompatibleVersion string) error WriteSchemaUpdateLog(oldVersion string, newVersion string, manifestMD5 string, desc string) error ExecDDLQuery(stmt string, args ...interface{}) error Close() ListTables() ([]string, error) ListTypes() ([]string, error) DropTable(name string) error DropType(name string) error DropAllTablesTypes() error } CqlClientImpl struct { nReplicas int session gocql.Session cfg *CQLClientConfig } // CQLClientConfig contains the configuration for cql client CQLClientConfig struct { Hosts string Port int User string Password string AllowedAuthenticators []string Keyspace string Timeout int ConnectTimeout int NumReplicas int ProtoVersion int TLS *config.TLS } ) const ( DefaultTimeout = 30 // Timeout in seconds DefaultConnectTimeout = 2 // Connect timeout in seconds DefaultCassandraPort = 9042 SystemKeyspace = "system" ) const ( // Retry policy constants ClientCreationRetryInitialInterval = time.Second ClientCreationRetryMaximumInterval = 10 * time.Second ClientCreationRetryMaximumAttempts = 3 ) const ( readSchemaVersionCQL = `SELECT curr_version from schema_version where keyspace_name=?` listTablesCQL = `SELECT table_name from system_schema.tables where keyspace_name=?` listTypesCQL = `SELECT type_name from system_schema.types where keyspace_name=?` writeSchemaVersionCQL = `INSERT into schema_version(keyspace_name, creation_time, curr_version, min_compatible_version) VALUES (?,?,?,?)` writeSchemaUpdateHistoryCQL = `INSERT into schema_update_history(year, month, update_time, old_version, new_version, manifest_md5, description) VALUES(?,?,?,?,?,?,?)` createSchemaVersionTableCQL = `CREATE TABLE schema_version(keyspace_name text PRIMARY KEY, ` + `creation_time timestamp, ` + `curr_version text, ` + `min_compatible_version text);` createSchemaUpdateHistoryTableCQL = `CREATE TABLE schema_update_history(` + `year int, ` + `month int, ` + `update_time timestamp, ` + `description text, ` + `manifest_md5 text, ` + `new_version text, ` + `old_version text, ` + `PRIMARY KEY ((year, month), update_time));` createKeyspaceCQL = `CREATE KEYSPACE IF NOT EXISTS %v ` + `WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : %v};` createNTSKeyspaceCQL = `CREATE KEYSPACE IF NOT EXISTS %v ` + `WITH replication = { 'class' : 'NetworkTopologyStrategy', '%v' : %v};` ) var _ schema.SchemaClient = (*CqlClientImpl)(nil) func NewCQLClient(cfg *CQLClientConfig, expectedConsistency gocql.Consistency) (CqlClient, error) { retrier := createClientCreationRetrier() cassClient := gocql.GetRegisteredClient() return NewCQLClientWithRetry(cfg, expectedConsistency, cassClient, retrier) } // NewCQLClient returns a new instance of CQLClient func NewCQLClientWithRetry(cfg *CQLClientConfig, expectedConsistency gocql.Consistency, cassClient gocql.Client, retrier *backoff.ThrottleRetry) (CqlClient, error) { cqlClient := new(CqlClientImpl) cqlClient.cfg = cfg cqlClient.nReplicas = cfg.NumReplicas err := retrier.Do(context.Background(), func(ctx context.Context) error { var err error cqlClient.session, err = cassClient.CreateSession(gocql.ClusterConfig{ Hosts: cfg.Hosts, Port: cfg.Port, User: cfg.User, Password: cfg.Password, AllowedAuthenticators: cfg.AllowedAuthenticators, Keyspace: cfg.Keyspace, TLS: cfg.TLS, Timeout: time.Duration(cfg.Timeout) * time.Second, ConnectTimeout: time.Duration(cfg.ConnectTimeout) * time.Second, ProtoVersion: cfg.ProtoVersion, Consistency: expectedConsistency, }) return err }) if err != nil { return nil, err } return cqlClient, nil } func (client *CqlClientImpl) CreateDatabase(name string) error { return client.CreateKeyspace(name) } func (client *CqlClientImpl) DropDatabase(name string) error { return client.DropKeyspace(name) } // CreateNTSKeyspace creates a cassandra Keyspace if it doesn't exist using network topology strategy func (client *CqlClientImpl) CreateKeyspace(name string) error { return client.ExecDDLQuery(fmt.Sprintf(createKeyspaceCQL, name, client.nReplicas)) } // CreateNTSKeyspace creates a cassandra Keyspace if it doesn't exist using network topology strategy func (client *CqlClientImpl) CreateNTSKeyspace(name string, datacenter string) error { return client.ExecDDLQuery(fmt.Sprintf(createNTSKeyspaceCQL, name, datacenter, client.nReplicas)) } // DropKeyspace drops a Keyspace func (client *CqlClientImpl) DropKeyspace(name string) error { return client.ExecDDLQuery(fmt.Sprintf("DROP KEYSPACE %v", name)) } func (client *CqlClientImpl) DropAllTables() error { return client.DropAllTablesTypes() } // CreateSchemaVersionTables sets up the schema version tables func (client *CqlClientImpl) CreateSchemaVersionTables() error { if err := client.ExecDDLQuery(createSchemaVersionTableCQL); err != nil { return err } return client.ExecDDLQuery(createSchemaUpdateHistoryTableCQL) } // ReadSchemaVersion returns the current schema version for the Keyspace func (client *CqlClientImpl) ReadSchemaVersion() (string, error) { query := client.session.Query(readSchemaVersionCQL, client.cfg.Keyspace) iter := query.Iter() var version string if !iter.Scan(&version) { err := iter.Close() return "", fmt.Errorf("reading schema version: %w", err) } if err := iter.Close(); err != nil { return "", err } return version, nil } // UpdateSchemaVersion updates the schema version for the Keyspace func (client *CqlClientImpl) UpdateSchemaVersion(newVersion string, minCompatibleVersion string) error { query := client.session.Query(writeSchemaVersionCQL, client.cfg.Keyspace, time.Now(), newVersion, minCompatibleVersion) return query.Exec() } // WriteSchemaUpdateLog adds an entry to the schema update history table func (client *CqlClientImpl) WriteSchemaUpdateLog(oldVersion string, newVersion string, manifestMD5 string, desc string) error { now := time.Now().UTC() query := client.session.Query(writeSchemaUpdateHistoryCQL) query.Bind(now.Year(), int(now.Month()), now, oldVersion, newVersion, manifestMD5, desc) return query.Exec() } // ExecDDLQuery executes a cql statement func (client *CqlClientImpl) ExecDDLQuery(stmt string, args ...interface{}) error { return client.session.Query(stmt, args...).Exec() } // Close closes the cql client func (client *CqlClientImpl) Close() { if client.session != nil { client.session.Close() } } // ListTables lists the table names in a Keyspace func (client *CqlClientImpl) ListTables() ([]string, error) { query := client.session.Query(listTablesCQL, client.cfg.Keyspace) iter := query.Iter() var names []string var name string for iter.Scan(&name) { names = append(names, name) } if err := iter.Close(); err != nil { return nil, err } return names, nil } // ListTypes lists the User defined types in a Keyspace. func (client *CqlClientImpl) ListTypes() ([]string, error) { qry := client.session.Query(listTypesCQL, client.cfg.Keyspace) iter := qry.Iter() var names []string var name string for iter.Scan(&name) { names = append(names, name) } if err := iter.Close(); err != nil { return nil, err } return names, nil } // DropTable drops a given table from the Keyspace func (client *CqlClientImpl) DropTable(name string) error { return client.ExecDDLQuery(fmt.Sprintf("DROP TABLE %v", name)) } // DropType drops a given type from the Keyspace func (client *CqlClientImpl) DropType(name string) error { return client.ExecDDLQuery(fmt.Sprintf("DROP TYPE %v", name)) } // DropAllTablesTypes deletes all tables/types in the // Keyspace without deleting the Keyspace func (client *CqlClientImpl) DropAllTablesTypes() error { tables, err := client.ListTables() if err != nil { return err } log.Printf("Dropping following tables: %v\n", tables) for _, table := range tables { err1 := client.DropTable(table) if err1 != nil { log.Printf("Error dropping table %v, err=%v\n", table, err1) } } types, err := client.ListTypes() if err != nil { return err } log.Printf("Dropping following types: %v\n", types) numOfTypes := len(types) for i := 0; i < numOfTypes && len(types) > 0; i++ { var erroredTypes []string for _, t := range types { err = client.DropType(t) if err != nil { log.Printf("Error dropping type %v, err=%v\n", t, err) erroredTypes = append(erroredTypes, t) } } types = erroredTypes } if len(types) > 0 { return err } return nil } func createClientCreationRetrier() *backoff.ThrottleRetry { policy := backoff.NewExponentialRetryPolicy(ClientCreationRetryInitialInterval) policy.SetMaximumInterval(ClientCreationRetryMaximumInterval) policy.SetMaximumAttempts(ClientCreationRetryMaximumAttempts) retrier := backoff.NewThrottleRetry( backoff.WithRetryPolicy(policy), backoff.WithRetryableError(func(err error) bool { return true }), ) return retrier } ================================================ FILE: tools/cassandra/cqlclient_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cassandra import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" ) func TestNewCQLClientWithRetry(t *testing.T) { defer goleak.VerifyNone(t) mockClient, mockSession, mockClock := setUpMocks(t) cfg := configuration() expectedCfg := expectedConfig() // Fail the first two times, then succeed. mockClient.EXPECT().CreateSession(expectedCfg).Return(mockSession, assert.AnError) mockClient.EXPECT().CreateSession(expectedCfg).Return(mockSession, assert.AnError) mockClient.EXPECT().CreateSession(expectedCfg).Return(mockSession, nil) go func() { // First retry happens after 1 second mockClock.BlockUntil(1) mockClock.Advance(1 * time.Second) // Second retry happens after 2 seconds mockClock.BlockUntil(1) mockClock.Advance(2 * time.Second) }() testRetrier := createClientCreationRetrier() backoff.WithClock(mockClock)(testRetrier) cqlClient, err := NewCQLClientWithRetry(cfg, gocql.One, mockClient, testRetrier) assert.NoError(t, err) cqlClientImpl, ok := cqlClient.(*CqlClientImpl) require.True(t, ok) assert.Equal(t, 333, cqlClientImpl.nReplicas) assert.Equal(t, cfg, cqlClientImpl.cfg) assert.Equal(t, mockSession, cqlClientImpl.session) } func TestNewCQLClientWithRetry_Fail(t *testing.T) { defer goleak.VerifyNone(t) mockClient, mockSession, mockClock := setUpMocks(t) cfg := configuration() expectedCfg := expectedConfig() // There is a off by one error in the retrier implementation, so we need to add one more call // if this is fixed, it's fine to remove the +1 mockClient.EXPECT().CreateSession(expectedCfg).Return(mockSession, assert.AnError).Times(ClientCreationRetryMaximumAttempts + 1) go func() { // We double the wait time each iteration nextAdvance := 1 * time.Second // Wait until someone sleeps, then advance the clock more than the backoff would be. for i := 0; i < ClientCreationRetryMaximumAttempts; i++ { mockClock.BlockUntil(1) mockClock.Advance(nextAdvance) nextAdvance *= 2 } }() testRetrier := createClientCreationRetrier() backoff.WithClock(mockClock)(testRetrier) _, err := NewCQLClientWithRetry(cfg, gocql.One, mockClient, testRetrier) assert.ErrorIs(t, err, assert.AnError) } func setUpMocks(t *testing.T) (*gocql.MockClient, *gocql.MockSession, clock.MockedTimeSource) { ctrl := gomock.NewController(t) mockClient := gocql.NewMockClient(ctrl) mockSession := gocql.NewMockSession(ctrl) mockClock := clock.NewMockedTimeSource() return mockClient, mockSession, mockClock } func configuration() *CQLClientConfig { return &CQLClientConfig{ Hosts: "testHost", Port: 1234, User: "testUser", Password: "testPassword", AllowedAuthenticators: []string{"testAuthenticator"}, Keyspace: "testKeyspace", Timeout: 111, ConnectTimeout: 222, NumReplicas: 333, ProtoVersion: 444, TLS: &config.TLS{Enabled: true}, } } func expectedConfig() gocql.ClusterConfig { return gocql.ClusterConfig{ Hosts: "testHost", Port: 1234, User: "testUser", Password: "testPassword", AllowedAuthenticators: []string{"testAuthenticator"}, Keyspace: "testKeyspace", TLS: &config.TLS{Enabled: true}, Timeout: 111 * time.Second, ConnectTimeout: 222 * time.Second, ProtoVersion: 444, Consistency: gocql.One, } } ================================================ FILE: tools/cassandra/handler.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "fmt" "log" "github.com/urfave/cli/v2" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/schema/cassandra" "github.com/uber/cadence/tools/common/schema" ) const defaultNumReplicas = 1 // SetupSchemaConfig contains the configuration params needed to setup schema tables type SetupSchemaConfig struct { CQLClientConfig schema.SetupConfig } // VerifyCompatibleVersion ensures that the installed version of cadence and visibility keyspaces // is greater than or equal to the expected version. // In most cases, the versions should match. However if after a schema upgrade there is a code // rollback, the code version (expected version) would fall lower than the actual version in // cassandra. func VerifyCompatibleVersion( cfg config.Persistence, expectedConsistency gocql.Consistency, ) error { if ds, ok := cfg.DataStores[cfg.DefaultStore]; ok { if err := verifyCompatibleVersion(ds, cassandra.Version, expectedConsistency); err != nil { return err } } if ds, ok := cfg.DataStores[cfg.VisibilityStore]; ok { if err := verifyCompatibleVersion(ds, cassandra.VisibilityVersion, expectedConsistency); err != nil { return err } } return nil } func verifyCompatibleVersion( ds config.DataStore, expectedCassandraVersion string, expectedConsistency gocql.Consistency, ) error { if ds.NoSQL != nil { return verifyPluginVersion(ds.NoSQL, expectedCassandraVersion, expectedConsistency) } if ds.ShardedNoSQL != nil { for shardName, connection := range ds.ShardedNoSQL.Connections { err := verifyPluginVersion(connection.NoSQLPlugin, expectedCassandraVersion, expectedConsistency) if err != nil { return fmt.Errorf("Failed to verify version for DB shard: %v. Error: %v", shardName, err.Error()) } } } // not using nosql return nil } func verifyPluginVersion(plugin *config.NoSQL, expectedCassandraVersion string, expectedConsistency gocql.Consistency) error { // Use hardcoded instead of constant because of cycle dependency issue. // However, this file will be refactor to support NoSQL soon. After the refactoring, cycle dependency issue // should be gone and we can use constant at that time if plugin.PluginName != "cassandra" { return fmt.Errorf("unknown NoSQL plugin name: %q", plugin.PluginName) } return CheckCompatibleVersion(*plugin, expectedCassandraVersion, expectedConsistency) } // CheckCompatibleVersion check the version compatibility func CheckCompatibleVersion( cfg config.Cassandra, expectedVersion string, expectedConsistency gocql.Consistency, ) error { client, err := NewCQLClient(&CQLClientConfig{ Hosts: cfg.Hosts, Port: cfg.Port, User: cfg.User, Password: cfg.Password, Keyspace: cfg.Keyspace, AllowedAuthenticators: cfg.AllowedAuthenticators, Timeout: DefaultTimeout, ConnectTimeout: DefaultConnectTimeout, TLS: cfg.TLS, ProtoVersion: cfg.ProtoVersion, }, expectedConsistency) if err != nil { return fmt.Errorf("creating CQL client: %w", err) } defer client.Close() return schema.VerifyCompatibleVersion(client, cfg.Keyspace, expectedVersion) } // setupSchema executes the setupSchemaTask // using the given command line arguments // as input func setupSchema(cli *cli.Context) error { config, err := newCQLClientConfig(cli) if err != nil { return handleErr(schema.NewConfigError(err.Error())) } client, err := NewCQLClient(config, gocql.All) if err != nil { return handleErr(err) } defer client.Close() if err := schema.Setup(cli, client); err != nil { return handleErr(err) } return nil } // updateSchema executes the updateSchemaTask // using the given command lien args as input func updateSchema(cli *cli.Context) error { config, err := newCQLClientConfig(cli) if err != nil { return handleErr(schema.NewConfigError(err.Error())) } client, err := NewCQLClient(config, gocql.All) if err != nil { return handleErr(err) } defer client.Close() if err := schema.Update(cli, client); err != nil { return handleErr(err) } return nil } // createKeyspace creates a cassandra Keyspace func createKeyspace(cli *cli.Context) error { config, err := newCQLClientConfig(cli) if err != nil { return handleErr(schema.NewConfigError(err.Error())) } keyspace := cli.String(schema.CLIOptKeyspace) if keyspace == "" { return handleErr(schema.NewConfigError("missing " + flag(schema.CLIOptKeyspace) + " argument ")) } datacenter := cli.String(schema.CLIOptDatacenter) err = doCreateKeyspace(*config, keyspace, datacenter) if err != nil { return handleErr(fmt.Errorf("error creating Keyspace:%v", err)) } return nil } func doCreateKeyspace(cfg CQLClientConfig, name string, datacenter string) error { cfg.Keyspace = SystemKeyspace client, err := NewCQLClient(&cfg, gocql.All) if err != nil { return err } defer client.Close() if datacenter != "" { return client.CreateNTSKeyspace(name, datacenter) } return client.CreateKeyspace(name) } func newCQLClientConfig(cli *cli.Context) (*CQLClientConfig, error) { cqlConfig := new(CQLClientConfig) cqlConfig.Hosts = cli.String(schema.CLIOptEndpoint) cqlConfig.Port = cli.Int(schema.CLIOptPort) cqlConfig.User = cli.String(schema.CLIOptUser) cqlConfig.Password = cli.String(schema.CLIOptPassword) cqlConfig.AllowedAuthenticators = cli.StringSlice(schema.CLIOptAllowedAuthenticators) cqlConfig.Timeout = cli.Int(schema.CLIOptTimeout) cqlConfig.ConnectTimeout = cli.Int(schema.CLIOptConnectTimeout) cqlConfig.Keyspace = cli.String(schema.CLIOptKeyspace) cqlConfig.NumReplicas = cli.Int(schema.CLIOptReplicationFactor) cqlConfig.ProtoVersion = cli.Int(schema.CLIOptProtoVersion) if cli.Bool(schema.CLIFlagEnableTLS) { cqlConfig.TLS = &config.TLS{ Enabled: true, CertFile: cli.String(schema.CLIFlagTLSCertFile), KeyFile: cli.String(schema.CLIFlagTLSKeyFile), CaFile: cli.String(schema.CLIFlagTLSCaFile), EnableHostVerification: cli.Bool(schema.CLIFlagTLSEnableHostVerification), ServerName: cli.String(schema.CLIFlagTLSServerName), } } if err := validateCQLClientConfig(cqlConfig); err != nil { return nil, err } return cqlConfig, nil } func validateCQLClientConfig(config *CQLClientConfig) error { if len(config.Hosts) == 0 { return schema.NewConfigError("missing cassandra endpoint argument " + flag(schema.CLIOptEndpoint)) } if config.Keyspace == "" { return schema.NewConfigError("missing " + flag(schema.CLIOptKeyspace) + " argument ") } if config.Port == 0 { config.Port = DefaultCassandraPort } if config.NumReplicas == 0 { config.NumReplicas = defaultNumReplicas } return nil } func flag(opt string) string { return "(-" + opt + ")" } func handleErr(err error) error { log.Println(err) return err } ================================================ FILE: tools/cassandra/handler_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/uber/cadence/environment" ) type ( HandlerTestSuite struct { *require.Assertions // override suite.Suite.Assertions with require.Assertions; this means that s.NotNil(nil) will stop the test, not merely log an error suite.Suite } ) func TestHandlerTestSuite(t *testing.T) { suite.Run(t, new(HandlerTestSuite)) } func (s *HandlerTestSuite) SetupTest() { s.Assertions = require.New(s.T()) // Have to define our overridden assertions in the test setup. If we did it earlier, s.T() will return nil } func (s *HandlerTestSuite) TestValidateCQLClientConfig() { config := new(CQLClientConfig) s.NotNil(validateCQLClientConfig(config)) config.Hosts = environment.GetCassandraAddress() s.NotNil(validateCQLClientConfig(config)) config.Keyspace = "foobar" s.Nil(validateCQLClientConfig(config)) } ================================================ FILE: tools/cassandra/main.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cassandra import ( "fmt" "github.com/urfave/cli/v2" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra/gocql" "github.com/uber/cadence/tools/common/schema" ) // RunTool runs the cadence-cassandra-tool command line tool func RunTool(args []string) error { app := BuildCLIOptions() return app.Run(args) // exits on error } // SetupSchema setups the cassandra schema func SetupSchema(config *SetupSchemaConfig) error { if err := validateCQLClientConfig(&config.CQLClientConfig); err != nil { return err } db, err := NewCQLClient(&config.CQLClientConfig, gocql.All) if err != nil { return err } return schema.SetupFromConfig(&config.SetupConfig, db) } // root handler for all cli commands func cliHandler(c *cli.Context, handler func(c *cli.Context) error) error { quiet := c.Bool(schema.CLIOptQuiet) err := handler(c) if err != nil { if quiet { // if quiet, don't return error fmt.Println("fail to run tool: ", err) return nil } return err } return nil } func BuildCLIOptions() *cli.App { app := cli.NewApp() app.Name = "cadence-cassandra-tool" app.Usage = "Command line tool for cadence cassandra operations" app.Version = "0.0.1" app.Flags = []cli.Flag{ &cli.StringFlag{ Name: schema.CLIFlagEndpoint, Aliases: []string{"ep"}, Value: "127.0.0.1", Usage: "hostname or ip address of cassandra host to connect to", EnvVars: []string{"CASSANDRA_HOST"}, }, &cli.IntFlag{ Name: schema.CLIFlagPort, Aliases: []string{"p"}, Value: DefaultCassandraPort, Usage: "Port of cassandra host to connect to", EnvVars: []string{"CASSANDRA_DB_PORT"}, }, &cli.StringFlag{ Name: schema.CLIFlagUser, Aliases: []string{"u"}, Value: "", Usage: "User name used for authentication for connecting to cassandra host", EnvVars: []string{"CASSANDRA_USER"}, }, &cli.StringFlag{ Name: schema.CLIFlagPassword, Aliases: []string{"pw"}, Value: "", Usage: "Password used for authentication for connecting to cassandra host", EnvVars: []string{"CASSANDRA_PASSWORD"}, }, &cli.StringSliceFlag{ Name: schema.CLIFlagAllowedAuthenticators, Aliases: []string{"aa"}, Value: cli.NewStringSlice(""), Usage: "Set allowed authenticators for servers with custom authenticators", }, &cli.IntFlag{ Name: schema.CLIFlagTimeout, Aliases: []string{"t"}, Value: DefaultTimeout, Usage: "request Timeout in seconds used for cql client", EnvVars: []string{"CASSANDRA_TIMEOUT"}, }, &cli.IntFlag{ Name: schema.CLIOptConnectTimeout, Value: DefaultConnectTimeout, Usage: "Connection Timeout in seconds used for cql client", }, &cli.StringFlag{ Name: schema.CLIFlagKeyspace, Aliases: []string{"k"}, Value: "cadence", Usage: "name of the cassandra Keyspace", EnvVars: []string{"CASSANDRA_KEYSPACE"}, }, &cli.BoolFlag{ Name: schema.CLIFlagQuiet, Aliases: []string{"q"}, Usage: "Don't set exit status to 1 on error", }, &cli.IntFlag{ Name: schema.CLIFlagProtoVersion, Aliases: []string{"pv"}, Usage: "Protocol Version to connect to cassandra host", EnvVars: []string{"CASSANDRA_PROTO_VERSION"}, }, &cli.BoolFlag{ Name: schema.CLIFlagEnableTLS, Usage: "enable TLS", EnvVars: []string{"CASSANDRA_ENABLE_TLS"}, }, &cli.StringFlag{ Name: schema.CLIFlagTLSCertFile, Usage: "TLS cert file", EnvVars: []string{"CASSANDRA_TLS_CERT"}, }, &cli.StringFlag{ Name: schema.CLIFlagTLSKeyFile, Usage: "TLS key file", EnvVars: []string{"CASSANDRA_TLS_KEY"}, }, &cli.StringFlag{ Name: schema.CLIFlagTLSCaFile, Usage: "TLS CA file", EnvVars: []string{"CASSANDRA_TLS_CA"}, }, &cli.BoolFlag{ Name: schema.CLIFlagTLSEnableHostVerification, Usage: "TLS host verification", EnvVars: []string{"CASSANDRA_TLS_VERIFY_HOST"}, }, &cli.StringFlag{ Name: schema.CLIFlagTLSServerName, Usage: "TLS ServerName", EnvVars: []string{"CASSANDRA_TLS_SERVER_NAME"}, }, } app.Commands = []*cli.Command{ { Name: "setup-schema", Aliases: []string{"setup"}, Usage: "setup initial version of cassandra schema", Flags: []cli.Flag{ &cli.StringFlag{ Name: schema.CLIFlagVersion, Aliases: []string{"v"}, Usage: "initial version of the schema, cannot be used with disable-versioning", }, &cli.StringFlag{ Name: schema.CLIFlagSchemaFile, Aliases: []string{"f"}, Usage: "path to the .cql schema file; if un-specified, will just setup versioning tables", }, &cli.BoolFlag{ Name: schema.CLIFlagDisableVersioning, Aliases: []string{"d"}, Usage: "disable setup of schema versioning", }, &cli.BoolFlag{ Name: schema.CLIFlagOverwrite, Aliases: []string{"o"}, Usage: "drop all existing tables before setting up new schema", }, }, Action: func(c *cli.Context) error { return cliHandler(c, setupSchema) }, }, { Name: "update-schema", Aliases: []string{"update"}, Usage: "update cassandra schema to a specific version", Flags: []cli.Flag{ &cli.StringFlag{ Name: schema.CLIFlagTargetVersion, Aliases: []string{"v"}, Usage: "target version for the schema update, defaults to latest", }, &cli.StringFlag{ Name: schema.CLIFlagSchemaDir, Aliases: []string{"d"}, Usage: "path to directory containing versioned schema", }, &cli.BoolFlag{ Name: schema.CLIFlagDryrun, Usage: "do a dryrun", }, }, Action: func(c *cli.Context) error { return cliHandler(c, updateSchema) }, }, { Name: "create-Keyspace", Aliases: []string{"create"}, Usage: "creates a Keyspace with simple strategy. If datacenter is provided, will use network topology strategy", Flags: []cli.Flag{ &cli.StringFlag{ Name: schema.CLIFlagKeyspace, Aliases: []string{"k"}, Usage: "name of the Keyspace", }, &cli.StringFlag{ Name: schema.CLIFlagDatacenter, Aliases: []string{"dc"}, Value: "", Usage: "name of the cassandra datacenter, used when creating the keyspace with network topology strategy", }, &cli.IntFlag{ Name: schema.CLIFlagReplicationFactor, Aliases: []string{"rf"}, Value: 1, Usage: "replication factor for the Keyspace", }, }, Action: func(c *cli.Context) error { return cliHandler(c, createKeyspace) }, }, } return app } ================================================ FILE: tools/cli/README.md ================================================ Documentation for the Cadence command line interface is located at our [main site](https://cadenceworkflow.io/docs/cli/). ### Build CLI binary locally To build the CLI tool locally check out the version tag (e.g. `git checkout v0.21.3`) and run `make tools`. This produces an executable called cadence. Run help command with a local build: ```` ./cadence --help ```` Command to describe a domain would look like this: ```` ./cadence --domain samples-domain domain describe ```` ================================================ FILE: tools/cli/admin.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "fmt" "strings" "time" "github.com/urfave/cli/v2" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/service/worker/scanner/executions" ) func newAdminWorkflowCommands() []*cli.Command { return []*cli.Command{ { Name: "show", Usage: "show workflow history from database", Flags: append(getDBFlags(), // v2 history events &cli.StringFlag{ Name: FlagTreeID, Usage: "TreeID", }, &cli.StringFlag{ Name: FlagBranchID, Usage: "BranchID", }, &cli.Int64Flag{ Name: FlagMinEventID, Value: 1, Usage: "MinEventID", }, &cli.Int64Flag{ Name: FlagMaxEventID, Value: 10000, Usage: "MaxEventID", }, &cli.StringFlag{ Name: FlagOutputFilename, Aliases: []string{"of"}, Usage: "output file", }, // support mysql query &cli.IntFlag{ Name: FlagShardID, Aliases: []string{"sid"}, Usage: "ShardID", }), Action: AdminShowWorkflow, }, { Name: "describe", Aliases: []string{"desc"}, Usage: "Describe internal information of workflow execution", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"r", "rid"}, Usage: "RunID", }, }, Action: AdminDescribeWorkflow, }, { Name: "refresh-tasks", Aliases: []string{"rt"}, Usage: "Refreshes all the tasks of a workflow", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"r", "rid"}, Usage: "RunID", }, }, Action: AdminRefreshWorkflowTasks, }, { Name: "delete", Aliases: []string{"del"}, Usage: "Delete current workflow execution and the mutableState record", Flags: append(getDBFlags(), &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"r", "rid"}, Usage: "RunID", }, &cli.BoolFlag{ Name: FlagSkipErrorMode, Aliases: []string{"serr"}, Usage: "skip errors when deleting history", }, &cli.BoolFlag{ Name: FlagRemote, Usage: "Executes deletion on server side", }), Action: AdminDeleteWorkflow, }, { Name: "fix_corruption", Aliases: []string{"fc"}, Usage: "Checks if workflow record is corrupted in database and cleans up", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"r", "rid"}, Usage: "RunID", }, &cli.BoolFlag{ Name: FlagSkipErrorMode, Aliases: []string{"serr"}, Usage: "Skip errors and tries to delete as much as possible from the DB", }, }, Action: AdminMaintainCorruptWorkflow, }, } } func newAdminShardManagementCommands() []*cli.Command { return []*cli.Command{ { Name: "describe", Aliases: []string{"d"}, Usage: "Describe shard by Id", Flags: append( getDBFlags(), &cli.IntFlag{ Name: FlagShardID, Usage: "The Id of the shard to describe", }, ), Action: AdminDescribeShard, }, { Name: "list", Aliases: []string{"l"}, Usage: "List shard distribution", Flags: []cli.Flag{ &cli.IntFlag{ Name: FlagPageSize, Value: 100, Usage: "Max number of results to return", }, &cli.IntFlag{ Name: FlagPageID, Value: 0, Usage: "Option to show results offset from pagesize * page_id", }, getFormatFlag(), }, Action: AdminDescribeShardDistribution, }, { Name: "setRangeID", Aliases: []string{"srid"}, Usage: "Force update shard rangeID", Flags: append( getDBFlags(), &cli.IntFlag{ Name: FlagShardID, Aliases: []string{"sid"}, Usage: "ID of the shard to reset", }, &cli.Int64Flag{ Name: FlagRangeID, Aliases: []string{"rid"}, Usage: "new shard rangeID", }, ), Action: AdminSetShardRangeID, }, { Name: "closeShard", Aliases: []string{"clsh"}, Usage: "close a shard given a shard id", Flags: []cli.Flag{ &cli.IntFlag{ Name: FlagShardID, Usage: "ShardID for the cadence cluster to manage", }, }, Action: AdminCloseShard, }, { Name: "removeTask", Aliases: []string{"rmtk"}, Usage: "remove a task based on shardID, task type, taskID, and task visibility timestamp", Flags: []cli.Flag{ &cli.IntFlag{ Name: FlagShardID, Usage: "shardID", }, &cli.Int64Flag{ Name: FlagTaskID, Usage: "taskID", }, &cli.IntFlag{ Name: FlagTaskType, Usage: "task type: 2 (transfer task), 3 (timer task), 4 (replication task) or 6 (cross-cluster task)", }, &cli.Int64Flag{ Name: FlagTaskVisibilityTimestamp, Usage: "task visibility timestamp in nano (required for removing timer task)", }, &cli.StringFlag{ Name: FlagCluster, Usage: "target cluster of the task (required for removing cross-cluster task)", }, }, Action: AdminRemoveTask, }, { Name: "timers", Usage: "get scheduled timers for a given time range", Flags: append(getDBFlags(), &cli.IntFlag{ Name: FlagShardID, Usage: "shardID", }, &cli.IntFlag{ Name: FlagPageSize, Usage: "page size used to query db executions table", Value: 500, }, &cli.StringFlag{ Name: FlagStartDate, Usage: "start date", Value: time.Now().UTC().Format(time.RFC3339), }, &cli.StringFlag{ Name: FlagEndDate, Usage: "end date", Value: time.Now().UTC().Add(24 * time.Hour).Format(time.RFC3339), }, &cli.StringFlag{ Name: FlagDomainID, Usage: "filter tasks by DomainID", }, &cli.IntSliceFlag{ Name: FlagTimerType, Usage: "timer types: 0 - DecisionTimeoutTask, 1 - TaskTypeActivityTimeout, " + "2 - TaskTypeUserTimer, 3 - TaskTypeWorkflowTimeout, 4 - TaskTypeDeleteHistoryEvent, " + "5 - TaskTypeActivityRetryTimer, 6 - TaskTypeWorkflowBackoffTimer", Value: cli.NewIntSlice(-1), }, &cli.BoolFlag{ Name: FlagPrintJSON, Usage: "print raw json data instead of histogram", }, &cli.BoolFlag{ Name: FlagSkipErrorMode, Usage: "skip errors", }, &cli.StringFlag{ Name: FlagInputFile, Usage: "file to use, will not connect to persistence", }, &cli.StringFlag{ Name: FlagDateFormat, Usage: "create buckets using time format. Use Go reference time: Mon Jan 2 15:04:05 MST 2006. If set, --" + FlagBucketSize + " is ignored", }, &cli.StringFlag{ Name: FlagBucketSize, Value: "hour", Usage: "group timers by time bucket. Available: day, hour, minute, second", }, &cli.IntFlag{ Name: FlagShardMultiplier, Usage: "multiply timer counters for histogram", Value: 16384, }, ), Action: AdminTimers, }, } } func newAdminHistoryHostCommands() []*cli.Command { return []*cli.Command{ { Name: "describe", Aliases: []string{"desc"}, Usage: "Describe internal information of history host", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagHistoryAddress, Aliases: []string{"had"}, Usage: "History Host address(IP:PORT)", }, &cli.IntFlag{ Name: FlagShardID, Aliases: []string{"sid"}, Usage: "ShardID", }, &cli.BoolFlag{ Name: FlagPrintFullyDetail, Aliases: []string{"pf"}, Usage: "Print fully detail", }, }, Action: AdminDescribeHistoryHost, }, { Name: "getshard", Aliases: []string{"gsh"}, Usage: "Get shardID for a workflowID", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"wid", "w"}, Usage: "WorkflowID", }, &cli.IntFlag{ Name: FlagNumberOfShards, Usage: "NumberOfShards for the cadence cluster(see config for numHistoryShards)", }, }, Action: AdminGetShardID, }, } } // may be better to do this inside an "ensure setup" in each method, // but this reduces branches for testing. func withDomainClient(c *cli.Context, admin bool, cb func(dc *domainCLIImpl) error) error { dc, err := newDomainCLI(c, admin) if err != nil { return err } return cb(dc) } func newAdminDomainCommands() []*cli.Command { return []*cli.Command{ { Name: "register", Aliases: []string{"re"}, Usage: "Register workflow domain", Flags: adminRegisterDomainFlags, Action: func(c *cli.Context) error { return withDomainClient(c, true, func(dc *domainCLIImpl) error { return dc.RegisterDomain(c) }) }, }, { Name: "update", Aliases: []string{"up", "u"}, Usage: "Update existing workflow domain", Flags: adminUpdateDomainFlags, Action: func(c *cli.Context) error { return withDomainClient(c, true, func(dc *domainCLIImpl) error { return dc.UpdateDomain(c) }) }, }, { Name: "delete", Aliases: []string{"del"}, Usage: "Delete existing workflow domain", Flags: adminDeleteDomainFlags, Action: func(c *cli.Context) error { return withDomainClient(c, true, func(dc *domainCLIImpl) error { return dc.DeleteDomain(c) }) }, }, { Name: "deprecate", Aliases: []string{"dep"}, Usage: "Deprecate existing workflow domain", Flags: adminDeprecateDomainFlags, Action: func(c *cli.Context) error { return withDomainClient(c, true, func(dc *domainCLIImpl) error { return dc.DeprecateDomain(c) }) }, }, { Name: "describe", Aliases: []string{"desc"}, Usage: "Describe existing workflow domain", Flags: adminDescribeDomainFlags, Action: func(c *cli.Context) error { return withDomainClient(c, true, func(dc *domainCLIImpl) error { return dc.DescribeDomain(c) }) }, }, { Name: "getdomainidorname", Aliases: []string{"getdn"}, Usage: "Get domainID or domainName", Flags: append(getDBFlags(), &cli.StringFlag{ Name: FlagDomainID, Usage: "Domain ID(uuid)", }), Action: AdminGetDomainIDOrName, }, { Name: "list", Aliases: []string{"l"}, Usage: "List all domains in the cluster", Flags: []cli.Flag{ &cli.IntFlag{ Name: FlagPageSize, Aliases: []string{"ps"}, Value: 10, Usage: "Result page size", }, &cli.BoolFlag{ Name: FlagAll, Aliases: []string{"a"}, Usage: "List all domains, by default only domains in REGISTERED status are listed", }, &cli.BoolFlag{ Name: FlagDeprecated, Aliases: []string{"dep"}, Usage: "List deprecated domains only, by default only domains in REGISTERED status are listed", }, &cli.StringFlag{ Name: FlagPrefix, Usage: "List domains that are matching to the given prefix", Value: "", }, &cli.BoolFlag{ Name: FlagPrintFullyDetail, Aliases: []string{"pf"}, Usage: "Print full domain detail", }, &cli.BoolFlag{ Name: FlagPrintJSON, Aliases: []string{"pjson"}, Usage: "Print in raw json format (DEPRECATED: instead use --format json)", }, getFormatFlag(), }, Action: func(c *cli.Context) error { return withDomainClient(c, false, func(dc *domainCLIImpl) error { return dc.ListDomains(c) }) }, }, } } func newAdminKafkaCommands() []*cli.Command { return []*cli.Command{ { // TODO: do we still need this command given that kafka replication has been deprecated? Name: "parse", Aliases: []string{"par"}, Usage: "Parse replication tasks from kafka messages", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagInputFile, Aliases: []string{"if"}, Usage: "Input file to use, if not present assumes piping", }, &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"r", "rid"}, Usage: "RunID", }, &cli.StringFlag{ Name: FlagOutputFilename, Aliases: []string{"of"}, Usage: "Output file to write to, if not provided output is written to stdout", }, &cli.BoolFlag{ Name: FlagSkipErrorMode, Aliases: []string{"serr"}, Usage: "Skip errors in parsing messages", }, &cli.BoolFlag{ Name: FlagHeadersMode, Aliases: []string{"he"}, Usage: "Output headers of messages in format: DomainID, WorkflowID, RunID, FirstEventID, NextEventID", }, &cli.IntFlag{ Name: FlagMessageType, Aliases: []string{"mt"}, Usage: "Kafka message type (0: replicationTasks; 1: visibility)", Value: 0, }, }, Action: AdminKafkaParse, }, { // TODO: move this command be a subcommand of admin workflow Name: "rereplicate", Aliases: []string{"rrp"}, Usage: "Rereplicate replication tasks from history tables", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagSourceCluster, Usage: "Name of source cluster to resend the replication task", }, &cli.StringFlag{ Name: FlagDomainID, Usage: "DomainID", }, &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"r", "rid"}, Usage: "RunID", }, &cli.Int64Flag{ Name: FlagMaxEventID, Usage: "MaxEventID Optional, default to all events", }, &cli.StringFlag{ Name: FlagEndEventVersion, Usage: "Workflow end event version, required if MaxEventID is specified", }}, Action: AdminRereplicate, }, } } func newAdminElasticSearchCommands() []*cli.Command { return []*cli.Command{ { Name: "catIndex", Aliases: []string{"cind"}, Usage: "Cat Indices on ElasticSearch", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagURL, Usage: "URL of ElasticSearch cluster", }, getFormatFlag(), }, Action: AdminCatIndices, }, { Name: "index", Aliases: []string{"ind"}, Usage: "Index docs on ElasticSearch", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagURL, Usage: "URL of ElasticSearch cluster", }, &cli.StringFlag{ Name: FlagIndex, Usage: "ElasticSearch target index", }, &cli.StringFlag{ Name: FlagInputFile, Aliases: []string{"if"}, Usage: "Input file of indexer.Message in json format, separated by newline", }, &cli.IntFlag{ Name: FlagBatchSize, Aliases: []string{"bs"}, Usage: "Optional batch size of actions for bulk operations", Value: 1000, }, }, Action: AdminIndex, }, { Name: "delete", Aliases: []string{"del"}, Usage: "Delete docs on ElasticSearch", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagURL, Usage: "URL of ElasticSearch cluster", }, &cli.StringFlag{ Name: FlagIndex, Usage: "ElasticSearch target index", }, &cli.StringFlag{ Name: FlagInputFile, Aliases: []string{"if"}, Usage: "Input file name. Redirect cadence wf list result (with tale format) to a file and use as delete input. " + "First line should be table header like WORKFLOW TYPE | WORKFLOW ID | RUN ID | ...", }, &cli.IntFlag{ Name: FlagBatchSize, Aliases: []string{"bs"}, Usage: "Optional batch size of actions for bulk operations", Value: 1000, }, &cli.IntFlag{ Name: FlagRPS, Usage: "Optional batch request rate per second", Value: 30, }, }, Action: AdminDelete, }, { Name: "report", Aliases: []string{"rep"}, Usage: "Generate Report by Aggregation functions on ElasticSearch", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagURL, Usage: "URL of ElasticSearch cluster", }, &cli.StringFlag{ Name: FlagIndex, Usage: "ElasticSearch target index", }, &cli.StringFlag{ Name: FlagListQuery, Usage: "SQL query of the report", }, &cli.StringFlag{ Name: FlagOutputFormat, Usage: "Additional output format (html or csv)", }, &cli.StringFlag{ Name: FlagOutputFilename, Usage: "Additional output filename with path", }, }, Action: GenerateReport, }, } } func newAdminTaskListCommands() []*cli.Command { return []*cli.Command{ { Name: "describe", Aliases: []string{"desc"}, Usage: "Describe pollers and status information of tasklist", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagTaskList, Aliases: []string{"tl"}, Usage: "TaskList description", }, &cli.StringFlag{ Name: FlagTaskListType, Aliases: []string{"tlt"}, Value: "decision", Usage: "Optional TaskList type [decision|activity]", }, }, Action: AdminDescribeTaskList, }, { Name: "list", Aliases: []string{"l"}, Usage: "List active tasklist under a domain", Action: AdminListTaskList, }, { Name: "update-partition", Aliases: []string{"up"}, Usage: "Update tasklist's partition config", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagTaskList, Aliases: []string{"tl"}, Usage: "TaskList Name", }, &cli.StringFlag{ Name: FlagTaskListType, Aliases: []string{"tlt"}, Usage: "TaskList type [decision|activity]", }, &cli.IntFlag{ Name: FlagNumReadPartitions, Aliases: []string{"nrp"}, Usage: "Number of read partitions", }, &cli.IntFlag{ Name: FlagNumWritePartitions, Aliases: []string{"nwp"}, Usage: "Number of write partitions", }, &cli.BoolFlag{ Name: FlagForce, Aliases: []string{"f"}, Usage: "Force an update operation that may be unsafe", }, }, Action: AdminUpdateTaskListPartitionConfig, }, } } func newAdminClusterCommands() []*cli.Command { return []*cli.Command{ { Name: "add-search-attr", Aliases: []string{"asa"}, Usage: "whitelist search attribute", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagSearchAttributesKey, Usage: "Search Attribute key to be whitelisted", }, &cli.IntFlag{ Name: FlagSearchAttributesType, Value: -1, Usage: "Search Attribute value type. [0:String, 1:Keyword, 2:Int, 3:Double, 4:Bool, 5:Datetime]", }, &cli.StringFlag{ Name: FlagSecurityToken, Aliases: []string{"st"}, Usage: "Optional token for security check", }, }, Action: AdminAddSearchAttribute, }, { Name: "describe", Aliases: []string{"d"}, Usage: "Describe cluster information", Action: AdminDescribeCluster, }, { Name: "failover", Aliases: []string{"fo"}, Usage: "Failover domains with domain data IsManagedByCadence=true to target cluster", Subcommands: newAdminFailoverCommands(), }, { Name: "failover_fast", Aliases: []string{"fof"}, Usage: "Failover domains with domain data IsManagedByCadence=true to target cluster", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagTargetCluster, Aliases: []string{"tc"}, Usage: "Target active cluster name", }, }, Action: func(c *cli.Context) error { return withDomainClient(c, false, func(dc *domainCLIImpl) error { return dc.FailoverDomains(c) }) }, }, { Name: "rebalance", Aliases: []string{"rb"}, Usage: "Rebalance the domains active cluster", Subcommands: newAdminRebalanceCommands(), }, } } func getDLQFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Name: FlagShards, Usage: "Comma separated shard IDs or inclusive ranges. Example: \"2,5-6,10\". Alternatively, feed one shard ID per line via STDIN.", }, &cli.StringFlag{ Name: FlagDLQType, Aliases: []string{"dt"}, Usage: "Type of DLQ to manage. (Options: domain, history)", Value: "history", }, &cli.StringFlag{ Name: FlagSourceCluster, Usage: "The cluster where the task is generated", }, &cli.IntFlag{ Name: FlagLastMessageID, Aliases: []string{"lm"}, Usage: "The upper boundary of the read message", }, } } func newAdminDLQCommands() []*cli.Command { return []*cli.Command{ { Name: "count", Aliases: []string{"c"}, Usage: "Count DLQ Messages", Flags: []cli.Flag{ getFormatFlag(), &cli.StringFlag{ Name: FlagDLQType, Aliases: []string{"dt"}, Usage: "Type of DLQ to manage. (Options: domain, history)", Value: "history", }, &cli.BoolFlag{ Name: FlagForce, Usage: "Force fetch latest counts (will put additional stress on DB)", }, }, Action: AdminCountDLQMessages, }, { Name: "read", Aliases: []string{"r"}, Usage: "Read DLQ Messages", Flags: append(getDLQFlags(), &cli.IntFlag{ Name: FlagMaxMessageCount, Aliases: []string{"mmc"}, Usage: "Max message size to fetch", }, getFormatFlag(), ), Action: AdminGetDLQMessages, }, { Name: "purge", Aliases: []string{"p"}, Usage: "Delete DLQ messages with equal or smaller ids than the provided task id", Flags: getDLQFlags(), Action: AdminPurgeDLQMessages, }, { Name: "merge", Aliases: []string{"m"}, Usage: "Merge DLQ messages with equal or smaller ids than the provided task id", Flags: getDLQFlags(), Action: AdminMergeDLQMessages, }, } } func newAdminQueueCommands() []*cli.Command { return []*cli.Command{ { Name: "reset", Usage: "reset processing queue states for transfer or timer queue processor", Flags: getQueueCommandFlags(), Action: AdminResetQueue, }, { Name: "describe", Aliases: []string{"desc"}, Usage: "describe processing queue states for transfer or timer queue processor", Flags: getQueueCommandFlags(), Action: AdminDescribeQueue, }, } } func newAdminAsyncQueueCommands() []*cli.Command { return []*cli.Command{ { Name: "get", Usage: "get async workflow queue configuration of a domain", Action: AdminGetAsyncWFConfig, }, { Name: "update", Usage: "upsert async workflow queue configuration of a domain", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagJSON, Usage: `AsyncWorkflowConfiguration in json format. Schema can be found in https://github.com/uber/cadence/blob/master/common/types/admin.go`, Required: true, }, }, Action: AdminUpdateAsyncWFConfig, }, } } func newDBCommands() []*cli.Command { var collections cli.StringSlice = *cli.NewStringSlice(invariant.CollectionStrings()...) scanFlag := &cli.StringFlag{ Name: FlagScanType, Usage: "Scan type to use: " + strings.Join(executions.ScanTypeStrings(), ", "), Required: true, } collectionsFlag := &cli.StringSliceFlag{ Name: FlagInvariantCollection, Usage: "Scan collection type to use: " + strings.Join(collections.Value(), ", "), Value: &collections, } verboseFlag := &cli.BoolFlag{ Name: FlagVerbose, Usage: "verbose output", Required: false, } return []*cli.Command{ { Name: "scan", Usage: "scan executions in database and detect corruptions", Flags: append(getDBFlags(), &cli.IntFlag{ Name: FlagNumberOfShards, Usage: "NumberOfShards for the cadence cluster (see config for numHistoryShards)", Required: true, }, scanFlag, collectionsFlag, &cli.StringFlag{ Name: FlagInputFile, Aliases: []string{"if"}, Usage: "Input file of executions to scan in JSON format {\"DomainID\":\"x\",\"WorkflowID\":\"x\",\"RunID\":\"x\"} separated by a newline", }, verboseFlag, ), Action: AdminDBScan, }, { Name: "unsupported-workflow", Usage: "use this command when upgrade the Cadence server from version less than 0.16.0. This scan database and detect unsupported workflow type.", Flags: append(getDBFlags(), &cli.IntFlag{ Name: FlagRPS, Usage: "NumberOfShards for the cadence cluster (see config for numHistoryShards)", Value: 1000, }, &cli.StringFlag{ Name: FlagOutputFilename, Aliases: []string{"of"}, Usage: "Output file to write to, if not provided output is written to stdout", }, &cli.IntFlag{ Name: FlagLowerShardBound, Usage: "FlagLowerShardBound for the start shard to scan. (Default: 0)", Value: 0, Required: true, }, &cli.IntFlag{ Name: FlagUpperShardBound, Usage: "FlagLowerShardBound for the end shard to scan. (Default: 16383)", Value: 16383, Required: true, }, ), Action: AdminDBScanUnsupportedWorkflow, }, { Name: "clean", Usage: "clean up corrupted workflows", Flags: append(getDBFlags(), scanFlag, collectionsFlag, &cli.StringFlag{ Name: FlagInputFile, Aliases: []string{"if"}, Usage: "Input file of execution to clean in JSON format. Use `scan` command to generate list of executions.", }, verboseFlag, ), Action: AdminDBClean, }, { Name: "decode_thrift", Usage: "decode thrift object, print into JSON if the data is matching with any supported struct", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagInput, Aliases: []string{"i"}, EnvVars: []string{"Input"}, Usage: "Input of Thrift encoded data structure.", }, &cli.StringFlag{ Name: FlagInputEncoding, Aliases: []string{"enc"}, Usage: "Encoding of the input: [hex|base64] (Default: hex)", }, }, Action: AdminDBDataDecodeThrift, }, } } func getQueueCommandFlags() []cli.Flag { return []cli.Flag{ &cli.IntFlag{ Name: FlagShardID, Aliases: []string{"sid"}, Usage: "shardID", }, &cli.StringFlag{ Name: FlagCluster, Usage: "cluster the task processor is responsible for", }, &cli.IntFlag{ Name: FlagQueueType, Usage: "queue type: 2 (transfer queue), 3 (timer queue) or 6 (cross-cluster queue)", }, } } func newAdminFailoverCommands() []*cli.Command { return []*cli.Command{ { Name: "start", Aliases: []string{"s"}, Usage: "start failover workflow", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagTargetCluster, Aliases: []string{"tc"}, Usage: "Target cluster name", }, &cli.StringFlag{ Name: FlagSourceCluster, Aliases: []string{"sc"}, Usage: "Source cluster name", }, &cli.IntFlag{ Name: FlagFailoverTimeout, Aliases: []string{"fts"}, Usage: "Optional graceful failover timeout in seconds. If this field is define, the failover will use graceful failover.", }, &cli.IntFlag{ Name: FlagExecutionTimeout, Aliases: []string{"et"}, Usage: "Optional Failover workflow timeout in seconds", Value: defaultFailoverWorkflowTimeoutInSeconds, }, &cli.IntFlag{ Name: FlagFailoverWaitTime, Aliases: []string{"fwts"}, Usage: "Optional Failover wait time after each batch in seconds", Value: defaultBatchFailoverWaitTimeInSeconds, }, &cli.IntFlag{ Name: FlagFailoverBatchSize, Aliases: []string{"fbs"}, Usage: "Optional number of domains to failover in one batch", Value: defaultBatchFailoverSize, }, &cli.StringSliceFlag{ Name: FlagFailoverDomains, Usage: "Optional domains to failover, eg d1,d2..,dn. " + "Only provided domains in source cluster will be failover.", }, &cli.IntFlag{ Name: FlagFailoverDrillWaitTime, Aliases: []string{"fdws"}, Usage: "Optional failover drill wait time. " + "After the wait time, the domains will be reset to original regions." + "This field is required if the cron schedule is specified.", }, &cli.StringFlag{ Name: FlagCronSchedule, Usage: "Optional cron schedule on failover drill. Please specify failover drill wait time " + "if this field is specific", }, }, Action: AdminFailoverStart, }, { Name: "pause", Aliases: []string{"p"}, Usage: "pause failover workflow", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"rid", "r"}, Usage: "Optional Failover workflow runID, default is latest runID", }, &cli.BoolFlag{ Name: FlagFailoverDrill, Aliases: []string{"fd"}, Usage: "Optional to pause failover workflow or failover drill workflow." + " The default is normal failover workflow", }, }, Action: AdminFailoverPause, }, { Name: "resume", Aliases: []string{"re"}, Usage: "resume paused failover workflow", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"rid", "r"}, Usage: "Optional Failover workflow runID, default is latest runID", }, &cli.BoolFlag{ Name: FlagFailoverDrill, Aliases: []string{"fd"}, Usage: "Optional to resume failover workflow or failover drill workflow." + " The default is normal failover workflow", }, }, Action: AdminFailoverResume, }, { Name: "query", Aliases: []string{"q"}, Usage: "query failover workflow state", Flags: []cli.Flag{ &cli.BoolFlag{ Name: FlagFailoverDrill, Aliases: []string{"fd"}, Usage: "Optional to query failover workflow or failover drill workflow." + " The default is normal failover workflow", }, &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"rid", "r"}, Usage: "Optional Failover workflow runID, default is latest runID", }, }, Action: AdminFailoverQuery, }, { Name: "abort", Aliases: []string{"a"}, Usage: "abort failover workflow", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"rid", "r"}, Usage: "Optional Failover workflow runID, default is latest runID", }, &cli.StringFlag{ Name: FlagReason, Aliases: []string{"re"}, Usage: "Optional reason why abort", }, &cli.BoolFlag{ Name: FlagFailoverDrill, Aliases: []string{"fd"}, Usage: "Optional to abort failover workflow or failover drill workflow." + " The default is normal failover workflow", }, }, Action: AdminFailoverAbort, }, { Name: "rollback", Aliases: []string{"ro"}, Usage: "rollback failover workflow", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"rid"}, Usage: "Optional Failover workflow runID, default is latest runID", }, &cli.IntFlag{ Name: FlagFailoverTimeout, Aliases: []string{"fts"}, Usage: "Optional graceful failover timeout in seconds. If this field is define, the failover will use graceful failover.", }, &cli.IntFlag{ Name: FlagExecutionTimeout, Aliases: []string{"et"}, Usage: "Optional Failover workflow timeout in seconds", Value: defaultFailoverWorkflowTimeoutInSeconds, }, &cli.IntFlag{ Name: FlagFailoverWaitTime, Aliases: []string{"fwts"}, Usage: "Optional Failover wait time after each batch in seconds", Value: defaultBatchFailoverWaitTimeInSeconds, }, &cli.IntFlag{ Name: FlagFailoverBatchSize, Aliases: []string{"fbs"}, Usage: "Optional number of domains to failover in one batch", Value: defaultBatchFailoverSize, }, }, Action: AdminFailoverRollback, }, { Name: "list", Aliases: []string{"l"}, Usage: "list failover workflow runs closed/open. This is just a simplified list cmd", Flags: []cli.Flag{ &cli.BoolFlag{ Name: FlagOpen, Aliases: []string{"op"}, Usage: "List for open workflow executions, default is to list for closed ones", }, &cli.IntFlag{ Name: FlagPageSize, Aliases: []string{"ps"}, Value: 10, Usage: "Result page size", }, &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"wid", "w"}, Usage: "Ignore this. It is a dummy flag which will be forced overwrite", }, &cli.BoolFlag{ Name: FlagFailoverDrill, Aliases: []string{"fd"}, Usage: "Optional to query failover workflow or failover drill workflow." + " The default is normal failover workflow", }, }, Action: AdminFailoverList, }, } } func newAdminRebalanceCommands() []*cli.Command { return []*cli.Command{ { Name: "start", Aliases: []string{"s"}, Usage: "start rebalance workflow", Flags: []cli.Flag{}, Action: AdminRebalanceStart, }, { Name: "list", Aliases: []string{"l"}, Usage: "list rebalance workflow runs closed/open.", Flags: []cli.Flag{ &cli.BoolFlag{ Name: FlagOpen, Aliases: []string{"op"}, Usage: "List for open workflow executions, default is to list for closed ones", }, &cli.IntFlag{ Name: FlagPageSize, Aliases: []string{"ps"}, Value: 10, Usage: "Result page size", }, }, Action: AdminRebalanceList, }, } } func newAdminConfigStoreCommands() []*cli.Command { return []*cli.Command{ { Name: "get", Aliases: []string{"g"}, Usage: "Get Dynamic Config Value", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagDynamicConfigName, Usage: "Name of Dynamic Config parameter to get value of", Required: true, }, &cli.StringFlag{ Name: FlagDynamicConfigFilter, Usage: fmt.Sprintf(`Optional. ex: --%s '{"domainName":"global-samples-domain", "shardID":1, "isEnabled": true}'`, FlagDynamicConfigFilter), }, }, Action: AdminGetDynamicConfig, }, { Name: "update", Aliases: []string{"u"}, Usage: "Update Dynamic Config Value", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagDynamicConfigName, Usage: "Name of Dynamic Config parameter to update value of", Required: true, }, &cli.StringSliceFlag{ Name: FlagDynamicConfigValue, Usage: fmt.Sprintf(`Can be specified multiple times for multiple values. ex: --%s '{"Value":true,"Filters":[]}'`, FlagDynamicConfigValue), Required: true, }, }, Action: AdminUpdateDynamicConfig, }, { Name: "restore", Aliases: []string{"r"}, Usage: "Restore Dynamic Config Value", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagDynamicConfigName, Usage: "Name of Dynamic Config parameter to restore", Required: true, }, &cli.StringFlag{ Name: FlagDynamicConfigFilter, Usage: fmt.Sprintf(`Optional. ex: --%s '{"domainName":"global-samples-domain", "shardID":1, "isEnabled": true}'`, FlagDynamicConfigFilter), }, }, Action: AdminRestoreDynamicConfig, }, { Name: "list", Aliases: []string{"l"}, Usage: "List Dynamic Config Value", Flags: []cli.Flag{}, Action: AdminListDynamicConfig, }, { Name: "listall", Aliases: []string{"la"}, Usage: "List all available configuration keys", Flags: []cli.Flag{getFormatFlag()}, Action: AdminListConfigKeys, }, } } func newAdminIsolationGroupCommands() []*cli.Command { return []*cli.Command{ { Name: "get-global", Usage: "gets the global isolation groups", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagFormat, Usage: `output format`, }, }, Action: AdminGetGlobalIsolationGroups, }, { Name: "update-global", Usage: "sets the global isolation groups", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagJSON, Usage: `the configurations to upsert: eg: [{"Name": "zone-1": "State": 2}]. To remove groups, specify an empty configuration`, Required: false, }, &cli.StringSliceFlag{ Name: FlagIsolationGroupSetDrains, Usage: "Use to upsert the configuration for all drains. Note that this is an upsert operation and will overwrite all existing configuration", Required: false, }, &cli.BoolFlag{ Name: FlagIsolationGroupsRemoveAllDrains, Usage: "Removes all drains", Required: false, }, }, Action: AdminUpdateGlobalIsolationGroups, }, { Name: "get-domain", Usage: "gets the domain isolation groups", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagFormat, Usage: `output format`, }, }, Action: AdminGetDomainIsolationGroups, }, { Name: "update-domain", Usage: "sets the domain isolation groups", Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagJSON, Usage: `the configurations to upsert: eg: [{"Name": "zone-1": "State": 2}]. To remove groups, specify an empty configuration`, Required: false, }, &cli.StringSliceFlag{ Name: FlagIsolationGroupSetDrains, Usage: "Use to upsert the configuration for all drains. Note that this is an upsert operation and will overwrite all existing configuration", Required: false, }, &cli.BoolFlag{ Name: FlagIsolationGroupsRemoveAllDrains, Usage: "Removes all drains", Required: false, }, }, Action: AdminUpdateDomainIsolationGroups, }, } } ================================================ FILE: tools/cli/admin_async_queue_commands.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "encoding/json" "fmt" "github.com/urfave/cli/v2" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/common/commoncli" ) func AdminGetAsyncWFConfig(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } domainName, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not present:", err) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } req := &types.GetDomainAsyncWorkflowConfiguratonRequest{ Domain: domainName, } resp, err := adminClient.GetDomainAsyncWorkflowConfiguraton(ctx, req) if err != nil { return commoncli.Problem("Failed to get async wf queue config", err) } if resp == nil || resp.Configuration == nil { fmt.Printf("Async workflow queue config not found for domain %s\n", domainName) return nil } fmt.Printf("Async workflow queue config for domain %s:\n", domainName) prettyPrintJSONObject(getDeps(c).Output(), resp.Configuration) return nil } func AdminUpdateAsyncWFConfig(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } domainName, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not present:", err) } asyncWFCfgJSON, err := getRequiredOption(c, FlagJSON) if err != nil { return commoncli.Problem("Required flag not present:", err) } var cfg types.AsyncWorkflowConfiguration err = json.Unmarshal([]byte(asyncWFCfgJSON), &cfg) if err != nil { return commoncli.Problem("Failed to parse async workflow config", err) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } req := &types.UpdateDomainAsyncWorkflowConfiguratonRequest{ Domain: domainName, Configuration: &cfg, } _, err = adminClient.UpdateDomainAsyncWorkflowConfiguraton(ctx, req) if err != nil { return commoncli.Problem("Failed to update async workflow queue config", err) } fmt.Printf("Successfully updated async workflow queue config for domain %s\n", domainName) return nil } ================================================ FILE: tools/cli/admin_async_queue_commands_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "fmt" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/cli/clitest" ) func TestAdminGetAsyncWFConfig(t *testing.T) { mockCtrl := gomock.NewController(t) // Define table-driven tests tests := []struct { name string setupMocks func(*admin.MockClient) expectedError string expectedStr string cmdline string mockDepsError error mockContextError error }{ { name: "Success", setupMocks: func(client *admin.MockClient) { expectedResponse := &types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: &types.AsyncWorkflowConfiguration{ Enabled: true, QueueType: "queueType", }, } client.EXPECT(). GetDomainAsyncWorkflowConfiguraton(gomock.Any(), gomock.Any()). Return(expectedResponse, nil). Times(1) }, expectedError: "", expectedStr: "PredefinedQueueName", cmdline: "cadence --domain test-domain admin async-wf-queue get", }, { name: "Required flag not present", setupMocks: func(client *admin.MockClient) { // No call to the mock admin client is expected }, expectedError: "Required flag not present:", cmdline: "cadence admin async-wf-queue get", // --domain is missing }, { name: "Config not found (resp.Configuration == nil)", setupMocks: func(client *admin.MockClient) { client.EXPECT(). GetDomainAsyncWorkflowConfiguraton(gomock.Any(), gomock.Any()). Return(&types.GetDomainAsyncWorkflowConfiguratonResponse{ Configuration: nil, }, nil). Times(1) }, expectedError: "", cmdline: "cadence --domain test-domain admin async-wf-queue get", }, { name: "Failed to get async wf config", setupMocks: func(client *admin.MockClient) { client.EXPECT(). GetDomainAsyncWorkflowConfiguraton(gomock.Any(), gomock.Any()). Return(nil, fmt.Errorf("failed to get async config")). Times(1) }, expectedError: "Failed to get async wf queue config", cmdline: "cadence --domain test-domain admin async-wf-queue get", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Mock the admin client adminClient := admin.NewMockClient(mockCtrl) // Set up mocks for the current test case tt.setupMocks(adminClient) ioHandler := &testIOHandler{} // Create mock app with clientFactoryMock app := NewCliApp(&clientFactoryMock{ serverAdminClient: adminClient, }, WithIOHandler(ioHandler)) err := clitest.RunCommandLine(t, app, tt.cmdline) if tt.expectedError != "" { assert.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) } else { assert.NoError(t, err) assert.Contains(t, ioHandler.outputBytes.String(), tt.expectedStr) } }) } } func TestAdminUpdateAsyncWFConfig(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() // Define table-driven tests tests := []struct { name string setupMocks func(*admin.MockClient) expectedError string cmdline string mockContextError error unmarshalError error }{ { name: "Success", setupMocks: func(client *admin.MockClient) { client.EXPECT(). UpdateDomainAsyncWorkflowConfiguraton(gomock.Any(), gomock.Any()). Return(&types.UpdateDomainAsyncWorkflowConfiguratonResponse{}, nil). Times(1) }, expectedError: "", cmdline: `cadence --domain test-domain admin async-wf-queue update --json '{"Enabled": true}'`, }, { name: "Required flag not present for domain", setupMocks: func(client *admin.MockClient) { // No call to the mock admin client is expected }, expectedError: "Required flag not present:", cmdline: `cadence admin async-wf-queue update --json '{"Enabled": true}'`, // --domain is missing }, { name: "Required flag not present for JSON", setupMocks: func(client *admin.MockClient) { // No call to the mock admin client is expected }, expectedError: "Required flag not present:", cmdline: `cadence --domain test-domain admin async-wf-queue update --json ""`, // empty --json flag }, { name: "Failed to parse async workflow config", setupMocks: func(client *admin.MockClient) { // No call setup for this test case as JSON parsing fails }, expectedError: "Failed to parse async workflow config", cmdline: `cadence --domain test-domain admin async-wf-queue update --json invalid-json`, unmarshalError: fmt.Errorf("unmarshal error"), }, { name: "Failed to update async workflow config", setupMocks: func(client *admin.MockClient) { client.EXPECT(). UpdateDomainAsyncWorkflowConfiguraton(gomock.Any(), gomock.Any()). Return(nil, fmt.Errorf("update failed")). Times(1) }, expectedError: "Failed to update async workflow queue config", cmdline: `cadence --domain test-domain admin async-wf-queue update --json '{"Enabled": true}'`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Mock the admin client adminClient := admin.NewMockClient(mockCtrl) // Set up mocks for the current test case tt.setupMocks(adminClient) // Create mock app with clientFactoryMock app := NewCliApp(&clientFactoryMock{ serverAdminClient: adminClient, }) err := clitest.RunCommandLine(t, app, tt.cmdline) if tt.expectedError != "" { assert.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: tools/cli/admin_cluster_commands.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "encoding/json" "fmt" "github.com/fatih/color" "github.com/pborman/uuid" "github.com/urfave/cli/v2" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/visibility" "github.com/uber/cadence/service/worker/failovermanager" "github.com/uber/cadence/tools/common/commoncli" ) // An indirection for the prompt function so that it can be mocked in the unit tests var promptFn = prompt // AdminAddSearchAttribute to whitelist search attribute func AdminAddSearchAttribute(c *cli.Context) error { key, err := getRequiredOption(c, FlagSearchAttributesKey) if err != nil { return commoncli.Problem("Required flag not present:", err) } if err := visibility.ValidateSearchAttributeKey(key); err != nil { return commoncli.Problem("Invalid search-attribute key.", err) } valType, err := getRequiredIntOption(c, FlagSearchAttributesType) if err != nil { return commoncli.Problem("Required flag not present:", err) } if !isValueTypeValid(valType) { return commoncli.Problem("Unknown Search Attributes value type.", nil) } // ask user for confirmation promptMsg := fmt.Sprintf("Are you trying to add key [%s] with Type [%s]? y/N", color.YellowString(key), color.YellowString(intValTypeToString(valType))) promptFn(promptMsg) adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } ctx, cancel, err := newContext(c) if err != nil { return commoncli.Problem("Error in creating context: ", err) } defer cancel() request := &types.AddSearchAttributeRequest{ SearchAttribute: map[string]types.IndexedValueType{ key: types.IndexedValueType(valType), }, SecurityToken: c.String(FlagSecurityToken), } err = adminClient.AddSearchAttribute(ctx, request) if err != nil { return commoncli.Problem("Add search attribute failed.", err) } fmt.Println("Success. Note that for a multi-node Cadence cluster, DynamicConfig MUST be updated separately to whitelist the new attributes.") return nil } // AdminDescribeCluster is used to dump information about the cluster func AdminDescribeCluster(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } response, err := adminClient.DescribeCluster(ctx) if err != nil { return commoncli.Problem("Operation DescribeCluster failed.", err) } prettyPrintJSONObject(getDeps(c).Output(), response) return nil } func AdminRebalanceStart(c *cli.Context) error { client, err := getCadenceClient(c) if err != nil { return err } tcCtx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Context not created:", err) } workflowID := failovermanager.RebalanceWorkflowID rbParams := &failovermanager.RebalanceParams{ BatchFailoverSize: 100, BatchFailoverWaitTimeInSeconds: 10, } input, err := json.Marshal(rbParams) if err != nil { return commoncli.Problem("Failed to serialize params for failover workflow", err) } op, err := getOperator() if err != nil { return commoncli.Problem("Failed to get operator", err) } memo, err := getWorkflowMemo(map[string]interface{}{ constants.MemoKeyForOperator: op, }) if err != nil { return commoncli.Problem("Failed to serialize memo", err) } request := &types.StartWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, WorkflowID: workflowID, RequestID: uuid.New(), Identity: getCliIdentity(), WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(int32(defaultDecisionTimeoutInSeconds)), Input: input, TaskList: &types.TaskList{ Name: failovermanager.TaskListName, }, Memo: memo, WorkflowType: &types.WorkflowType{ Name: failovermanager.RebalanceWorkflowTypeName, }, } resp, err := client.StartWorkflowExecution(tcCtx, request) if err != nil { return commoncli.Problem("Failed to start failover workflow", err) } output := getDeps(c).Output() output.Write([]byte("Rebalance workflow started\n")) output.Write([]byte("wid: " + workflowID + "\n")) output.Write([]byte("rid: " + resp.GetRunID() + "\n")) return nil } func AdminRebalanceList(c *cli.Context) error { if err := c.Set(FlagWorkflowID, failovermanager.RebalanceWorkflowID); err != nil { return err } if err := c.Set(FlagDomain, constants.SystemLocalDomainName); err != nil { return err } return ListWorkflow(c) } func intValTypeToString(valType int) string { switch valType { case 0: return "String" case 1: return "Keyword" case 2: return "Int" case 3: return "Double" case 4: return "Bool" case 5: return "Datetime" default: return "" } } func isValueTypeValid(valType int) bool { return valType >= 0 && valType <= 5 } ================================================ FILE: tools/cli/admin_cluster_commands_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/urfave/cli/v2" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/visibility" "github.com/uber/cadence/service/worker/failovermanager" "github.com/uber/cadence/tools/cli/clitest" ) func TestAdminAddSearchAttribute_isValueTypeValid(t *testing.T) { testCases := []struct { name string input int expected bool }{ { name: "negative", input: -1, expected: false, }, { name: "valid", input: 0, expected: true, }, { name: "valid", input: 5, expected: true, }, { name: "unknown", input: 6, expected: false, }, } for _, testCase := range testCases { assert.Equal(t, testCase.expected, isValueTypeValid(testCase.input)) } } func TestAdminFailover(t *testing.T) { var listDomainsResponse = &types.ListDomainsResponse{ Domains: []*types.DescribeDomainResponse{ { DomainInfo: &types.DomainInfo{ Name: "test-domain", Description: "a test domain", OwnerEmail: "test@uber.com", Data: map[string]string{ constants.DomainDataKeyForManagedFailover: "true", }, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "active", Clusters: []*types.ClusterReplicationConfiguration{ { ClusterName: "active", }, { ClusterName: "standby", }, }, }, }, }, } t.Run("standby cluster", func(t *testing.T) { td := newCLITestData(t) cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagActiveClusterName, "standby"), ) td.mockFrontendClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(listDomainsResponse, nil).Times(1) td.mockFrontendClient.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) domainCLI := &domainCLIImpl{ frontendClient: td.mockFrontendClient, } succeed, failed, err := domainCLI.failoverDomains(cliCtx) assert.Equal(t, []string{"test-domain"}, succeed) assert.Equal(t, 0, len(failed)) assert.NoError(t, err) }) t.Run("active cluster", func(t *testing.T) { td := newCLITestData(t) cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagActiveClusterName, "active"), ) td.mockFrontendClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(listDomainsResponse, nil).Times(1) domainCLI := &domainCLIImpl{ frontendClient: td.mockFrontendClient, } succeed, failed, err := domainCLI.failoverDomains(cliCtx) assert.Equal(t, 0, len(succeed)) assert.Equal(t, 0, len(failed)) assert.NoError(t, err) }) } func TestValidSearchAttributeKey(t *testing.T) { assert.NoError(t, visibility.ValidateSearchAttributeKey("city")) assert.NoError(t, visibility.ValidateSearchAttributeKey("cityId")) assert.NoError(t, visibility.ValidateSearchAttributeKey("paymentProfileUUID")) assert.NoError(t, visibility.ValidateSearchAttributeKey("job_type")) assert.Error(t, visibility.ValidateSearchAttributeKey("payments-biling-invoices-TransactionUUID")) assert.Error(t, visibility.ValidateSearchAttributeKey("9lives")) assert.Error(t, visibility.ValidateSearchAttributeKey("tax%")) } func TestAdminDescribeCluster(t *testing.T) { tests := []struct { name string mockSetup func(td *cliTestData) expectedError string expectedOutput string }{ { name: "Success", mockSetup: func(td *cliTestData) { // Expected response from DescribeCluster expectedResponse := &types.DescribeClusterResponse{ SupportedClientVersions: &types.SupportedClientVersions{ GoSdk: "1.5.0", }, } td.mockAdminClient.EXPECT().DescribeCluster(gomock.Any()).Return(expectedResponse, nil).Times(1) }, expectedOutput: `{ "supportedClientVersions": { "goSdk": "1.5.0" } } `, expectedError: "", }, { name: "DescribeClusterError", mockSetup: func(td *cliTestData) { // Mock DescribeCluster to return an error td.mockAdminClient.EXPECT().DescribeCluster(gomock.Any()).Return(nil, fmt.Errorf("DescribeCluster failed")).Times(1) }, expectedOutput: "", expectedError: "Operation DescribeCluster failed.", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) // Set up mock based on the specific test case tt.mockSetup(td) cliCtx := clitest.NewCLIContext(t, td.app) err := AdminDescribeCluster(cliCtx) if tt.expectedError != "" { assert.ErrorContains(t, err, tt.expectedError) } else { assert.NoError(t, err) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestAdminRebalanceStart(t *testing.T) { tests := []struct { name string mockSetup func(td *cliTestData) expectedError string expectedOutput string }{ { name: "Success", mockSetup: func(td *cliTestData) { mockResponse := &types.StartWorkflowExecutionResponse{ RunID: "test-run-id", } td.mockFrontendClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(mockResponse, nil).Times(1) }, expectedOutput: "Rebalance workflow started\nwid: cadence-rebalance-workflow\nrid: test-run-id\n", expectedError: "", }, { name: "StartWorkflowExecutionError", mockSetup: func(td *cliTestData) { // Mock StartWorkflowExecution to return an error td.mockFrontendClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("failed to start workflow")).Times(1) }, expectedOutput: "", expectedError: "Failed to start failover workflow", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) tt.mockSetup(td) cliCtx := clitest.NewCLIContext(t, td.app, clitest.StringArgument(FlagDomain, testDomain)) err := AdminRebalanceStart(cliCtx) if tt.expectedError != "" { assert.ErrorContains(t, err, tt.expectedError) } else { assert.NoError(t, err) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestIntValTypeToString(t *testing.T) { tests := []struct { name string valType int expected string }{ { name: "StringType", valType: 0, expected: "String", }, { name: "KeywordType", valType: 1, expected: "Keyword", }, { name: "IntType", valType: 2, expected: "Int", }, { name: "DoubleType", valType: 3, expected: "Double", }, { name: "BoolType", valType: 4, expected: "Bool", }, { name: "DatetimeType", valType: 5, expected: "Datetime", }, { name: "UnknownType", valType: 999, expected: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Call the function and check the result result := intValTypeToString(tt.valType) assert.Equal(t, tt.expected, result) }) } } func TestAdminRebalanceList(t *testing.T) { tests := []struct { name string prepareEnv func(td *cliTestData) *cli.Context expectedError string expectedOutput string }{ { name: "Success", prepareEnv: func(td *cliTestData) *cli.Context { // Mock successful ListWorkflow call td.mockFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.CountWorkflowExecutionsResponse{}, nil).Times(1) td.mockFrontendClient.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.ListClosedWorkflowExecutionsResponse{}, nil).Times(1) return clitest.NewCLIContext(t, td.app, clitest.StringArgument(FlagWorkflowID, failovermanager.RebalanceWorkflowID), clitest.StringArgument(FlagDomain, constants.SystemLocalDomainName), ) }, expectedOutput: "\n", // Example output for success case expectedError: "", }, { name: "SetWorkflowIDError", prepareEnv: func(td *cliTestData) *cli.Context { // Create CLI app and set up flag set without FlagWorkflowID return clitest.NewCLIContext(t, td.app, clitest.StringArgument(FlagDomain, constants.SystemLocalDomainName), ) }, expectedOutput: "", expectedError: "no such flag -workflow_id", }, { name: "SetDomainError", prepareEnv: func(td *cliTestData) *cli.Context { // Create CLI app and set up flag set without FlagDomain return clitest.NewCLIContext(t, td.app, clitest.StringArgument(FlagWorkflowID, failovermanager.RebalanceWorkflowID), ) }, expectedOutput: "", expectedError: "no such flag -domain", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) // Prepare the test environment for the specific test case c := tt.prepareEnv(td) err := AdminRebalanceList(c) if tt.expectedError != "" { assert.ErrorContains(t, err, tt.expectedError) } else { assert.NoError(t, err) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestAdminAddSearchAttribute_errors(t *testing.T) { tests := []struct { name string setupContext func(app *cli.App) *cli.Context expectedOutput string expectedError string }{ { name: "MissingSearchAttributesKey", setupContext: func(app *cli.App) *cli.Context { return clitest.NewCLIContext(t, app /* missing FlagSearchAttributesKey argument */) }, expectedOutput: "", // In this case, likely no output expectedError: "Required flag not present:", }, { name: "InvalidSearchAttributeKey", setupContext: func(app *cli.App) *cli.Context { const invalidSearchAttr = "123_invalid_key" // Invalid key, starts with number return clitest.NewCLIContext(t, app, clitest.StringArgument(FlagSearchAttributesKey, invalidSearchAttr)) }, expectedOutput: "", // In this case, likely no output expectedError: "Invalid search-attribute key.", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) // Set up the CLI context for the specific test case cliCtx := tt.setupContext(td.app) err := AdminAddSearchAttribute(cliCtx) if tt.expectedError != "" { assert.ErrorContains(t, err, tt.expectedError) } else { assert.NoError(t, err) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } ================================================ FILE: tools/cli/admin_commands.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "encoding/json" "fmt" "io/ioutil" "strconv" "time" "github.com/urfave/cli/v2" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/common" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" "github.com/uber/cadence/tools/common/commoncli" ) const ( tableRenderSize = 10 remoteHint = " (Hint: use --remote flag to run this operation via the server API instead of direct DB access)" ) // AdminShowWorkflow shows history func AdminShowWorkflow(c *cli.Context) error { tid := c.String(FlagTreeID) bid := c.String(FlagBranchID) sid := c.Int(FlagShardID) minEventID := c.Int64(FlagMinEventID) maxEventID := c.Int64(FlagMaxEventID) outputFileName := c.String(FlagOutputFilename) domainName := c.String(FlagDomain) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } serializer := persistence.NewPayloadSerializer() var history []*persistence.DataBlob if len(tid) != 0 { thriftrwEncoder := codec.NewThriftRWEncoder() histV2, err := getDeps(c).initializeHistoryManager(c) if err != nil { return commoncli.Problem("Error in Admin delete WF: ", err) } branchToken, err := thriftrwEncoder.Encode(&shared.HistoryBranch{ TreeID: &tid, BranchID: &bid, }) if err != nil { return commoncli.Problem("encoding branch token err", err) } resp, err := histV2.ReadRawHistoryBranch(ctx, &persistence.ReadHistoryBranchRequest{ BranchToken: branchToken, MinEventID: minEventID, MaxEventID: maxEventID, PageSize: int(maxEventID - minEventID + 1), ShardID: &sid, DomainName: domainName, }) if err != nil { return commoncli.Problem("ReadHistoryBranch err", err) } history = resp.HistoryEventBlobs } else { return commoncli.Problem("need to specify TreeID/BranchID/ShardID", nil) } if len(history) == 0 { return commoncli.Problem("no events", nil) } allEvents := &shared.History{} totalSize := 0 for idx, b := range history { totalSize += len(b.Data) fmt.Printf("======== batch %v, blob len: %v ======\n", idx+1, len(b.Data)) internalHistoryBatch, err := serializer.DeserializeBatchEvents(b) if err != nil { return commoncli.Problem("DeserializeBatchEvents err", err) } historyBatch := thrift.FromHistoryEventArray(internalHistoryBatch) allEvents.Events = append(allEvents.Events, historyBatch...) for _, e := range historyBatch { jsonstr, err := json.Marshal(e) if err != nil { return commoncli.Problem("json.Marshal err", err) } fmt.Println(string(jsonstr)) } } fmt.Printf("======== total batches %v, total blob len: %v ======\n", len(history), totalSize) if outputFileName != "" { data, err := json.Marshal(allEvents.Events) if err != nil { return commoncli.Problem("Failed to serialize history data.", err) } if err := ioutil.WriteFile(outputFileName, data, 0666); err != nil { return commoncli.Problem("Failed to export history data file.", err) } } return nil } // AdminDescribeWorkflow describe a new workflow execution for admin func AdminDescribeWorkflow(c *cli.Context) error { resp, err := describeMutableState(c) if err != nil { return err } prettyPrintJSONObject(getDeps(c).Output(), resp) if resp != nil { msStr := resp.GetMutableStateInDatabase() ms := persistence.WorkflowMutableState{} err := json.Unmarshal([]byte(msStr), &ms) if err != nil { return commoncli.Problem("json.Unmarshal err", err) } currentBranchToken := ms.ExecutionInfo.BranchToken if ms.VersionHistories != nil { // if VersionHistories is set, then all branch infos are stored in VersionHistories currentVersionHistory, err := ms.VersionHistories.GetCurrentVersionHistory() if err != nil { return commoncli.Problem("ms.VersionHistories.GetCurrentVersionHistory err", err) } currentBranchToken = currentVersionHistory.GetBranchToken() } branchInfo := shared.HistoryBranch{} thriftrwEncoder := codec.NewThriftRWEncoder() err = thriftrwEncoder.Decode(currentBranchToken, &branchInfo) if err != nil { return commoncli.Problem("thriftrwEncoder.Decode err", err) } prettyPrintJSONObject(getDeps(c).Output(), branchInfo) if ms.ExecutionInfo.AutoResetPoints != nil { getDeps(c).Output().Write([]byte("auto-reset-points:")) for _, p := range ms.ExecutionInfo.AutoResetPoints.Points { createT := time.Unix(0, p.GetCreatedTimeNano()) expireT := time.Unix(0, p.GetExpiringTimeNano()) getDeps(c).Output().Write([]byte(fmt.Sprintln(p.GetBinaryChecksum(), p.GetRunID(), p.GetFirstDecisionCompletedID(), p.GetResettable(), createT, expireT))) } } } return nil } func describeMutableState(c *cli.Context) (*types.AdminDescribeWorkflowExecutionResponse, error) { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return nil, err } domain, err := getRequiredOption(c, FlagDomain) if err != nil { return nil, commoncli.Problem("Required flag not found", err) } wid, err := getRequiredOption(c, FlagWorkflowID) if err != nil { return nil, commoncli.Problem("Required flag not found", err) } rid := c.String(FlagRunID) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return nil, commoncli.Problem("Error in creating context: ", err) } resp, err := adminClient.DescribeWorkflowExecution( ctx, &types.AdminDescribeWorkflowExecutionRequest{ Domain: domain, Execution: &types.WorkflowExecution{ WorkflowID: wid, RunID: rid, }, }, ) if err != nil { return nil, commoncli.Problem("Get workflow mutableState failed", err) } return resp, nil } // AdminMaintainCorruptWorkflow deletes workflow from DB if it's corrupt func AdminMaintainCorruptWorkflow(c *cli.Context) error { domainName, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found", err) } workflowID := c.String(FlagWorkflowID) runID := c.String(FlagRunID) skipErrors := c.Bool(FlagSkipErrorMode) adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } request := &types.AdminMaintainWorkflowRequest{ Domain: domainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, SkipErrors: skipErrors, } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } _, err = adminClient.MaintainCorruptWorkflow(ctx, request) if err != nil { return commoncli.Problem("Operation AdminMaintainCorruptWorkflow failed.", err) } return err } // AdminDeleteWorkflow delete a workflow execution for admin func AdminDeleteWorkflow(c *cli.Context) error { domain, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found", err) } wid, err := getRequiredOption(c, FlagWorkflowID) if err != nil { return commoncli.Problem("Required flag not found", err) } rid := c.String(FlagRunID) remote := c.Bool(FlagRemote) skipError := c.Bool(FlagSkipErrorMode) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } // With remote flag, we run the command on the server side using existing APIs // Without remote, commands are run directly through some DB clients. This is // useful if server is down somehow. However, we only support couple DB clients // currently. If the server side hosts working, remote is a cleaner approach if remote { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } request := &types.AdminDeleteWorkflowRequest{ Domain: domain, Execution: &types.WorkflowExecution{ WorkflowID: wid, RunID: rid, }, SkipErrors: skipError, } _, err = adminClient.DeleteWorkflow(ctx, request) if err != nil { return commoncli.Problem("Operation AdminMaintainCorruptWorkflow failed.", err) } return nil } resp, err := describeMutableState(c) if err != nil { return commoncli.Problem("Failed to get workflow mutable state"+remoteHint, err) } msStr := resp.GetMutableStateInDatabase() ms := persistence.WorkflowMutableState{} err = json.Unmarshal([]byte(msStr), &ms) if err != nil { return commoncli.Problem("json.Unmarshal err", err) } domainID := ms.ExecutionInfo.DomainID shardID := resp.GetShardID() shardIDInt, err := strconv.Atoi(shardID) if err != nil { return commoncli.Problem("strconv.Atoi(shardID) err", err) } histV2, err := getDeps(c).initializeHistoryManager(c) if err != nil { return commoncli.Problem("Unable to initialize history manager"+remoteHint, err) } defer histV2.Close() exeStore, err := getDeps(c).initializeExecutionManager(c, shardIDInt) if err != nil { return commoncli.Problem("Unable to initialize execution manager"+remoteHint, err) } branchInfo := shared.HistoryBranch{} thriftrwEncoder := codec.NewThriftRWEncoder() branchTokens := [][]byte{ms.ExecutionInfo.BranchToken} if ms.VersionHistories != nil { // if VersionHistories is set, then all branch infos are stored in VersionHistories branchTokens = [][]byte{} for _, versionHistory := range ms.VersionHistories.ToInternalType().Histories { branchTokens = append(branchTokens, versionHistory.BranchToken) } } for _, branchToken := range branchTokens { err = thriftrwEncoder.Decode(branchToken, &branchInfo) if err != nil { return commoncli.Problem("thriftrwEncoder.Decode err", err) } fmt.Println("deleting history events for ...") prettyPrintJSONObject(getDeps(c).Output(), branchInfo) err = histV2.DeleteHistoryBranch(ctx, &persistence.DeleteHistoryBranchRequest{ BranchToken: branchToken, ShardID: &shardIDInt, DomainName: domain, }) if err != nil { if skipError { fmt.Println("failed to delete history, ", err) } else { return commoncli.Problem("DeleteHistoryBranch err"+remoteHint, err) } } } req := &persistence.DeleteWorkflowExecutionRequest{ DomainID: domainID, WorkflowID: wid, RunID: rid, DomainName: domain, } err = exeStore.DeleteWorkflowExecution(ctx, req) if err != nil { if skipError { fmt.Println("delete mutableState row failed, ", err) } else { return commoncli.Problem("delete mutableState row failed"+remoteHint, err) } } fmt.Println("delete mutableState row successfully") deleteCurrentReq := &persistence.DeleteCurrentWorkflowExecutionRequest{ DomainID: domainID, WorkflowID: wid, RunID: rid, } err = exeStore.DeleteCurrentWorkflowExecution(ctx, deleteCurrentReq) if err != nil { if skipError { fmt.Println("delete current row failed, ", err) } else { return commoncli.Problem("delete current row failed"+remoteHint, err) } } fmt.Println("delete current row successfully") return nil } // AdminGetDomainIDOrName map domain func AdminGetDomainIDOrName(c *cli.Context) error { domainID := c.String(FlagDomainID) domainName := c.String(FlagDomain) if (len(domainID) == 0 && len(domainName) == 0) || (len(domainID) != 0 && len(domainName) != 0) { return commoncli.Problem("Need either domainName or domainID", nil) } domainManager, err := getDeps(c).initializeDomainManager(c) if err != nil { return commoncli.Problem("Error in Admin delete WF: ", err) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } output := getDeps(c).Output() if len(domainID) > 0 { res, err := domainManager.GetDomain(ctx, &persistence.GetDomainRequest{ID: domainID}) if err != nil { return commoncli.Problem("GetDomain error", err) } fmt.Fprintf(output, "domainName for domainID %v is %v\n", domainID, res.Info.Name) } else { res, err := domainManager.GetDomain(ctx, &persistence.GetDomainRequest{Name: domainName}) if err != nil { return commoncli.Problem("GetDomain error", err) } fmt.Fprintf(output, "domainID for domainName %v is %v\n", domainName, res.Info.ID) } return nil } // AdminGetShardID get shardID func AdminGetShardID(c *cli.Context) error { wid, err := getRequiredOption(c, FlagWorkflowID) if err != nil { return commoncli.Problem("Required flag not found", err) } numberOfShards := c.Int(FlagNumberOfShards) if numberOfShards <= 0 { return commoncli.Problem("numberOfShards is required", nil) } shardID := common.WorkflowIDToHistoryShard(wid, numberOfShards) fmt.Fprintf(getDeps(c).Output(), "ShardID for workflowID: %v is %v\n", wid, shardID) return nil } // AdminRemoveTask describes history host func AdminRemoveTask(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } shardID, err := getRequiredIntOption(c, FlagShardID) if err != nil { return commoncli.Problem("Required flag not found", err) } taskID, err := getRequiredInt64Option(c, FlagTaskID) if err != nil { return commoncli.Problem("Required flag not found", err) } typeID, err := getRequiredIntOption(c, FlagTaskType) if err != nil { return commoncli.Problem("Required flag not found", err) } var visibilityTimestamp int64 if constants.TaskType(typeID) == constants.TaskTypeTimer { visibilityTimestamp, err = getRequiredInt64Option(c, FlagTaskVisibilityTimestamp) if err != nil { return commoncli.Problem("Required flag not found", err) } } var clusterName string ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } req := &types.RemoveTaskRequest{ ShardID: int32(shardID), Type: common.Int32Ptr(int32(typeID)), TaskID: taskID, VisibilityTimestamp: common.Int64Ptr(visibilityTimestamp), ClusterName: clusterName, } err = adminClient.RemoveTask(ctx, req) if err != nil { return commoncli.Problem("Remove task has failed", err) } return nil } // AdminDescribeShard describes shard by shard id func AdminDescribeShard(c *cli.Context) error { sid, err := getRequiredIntOption(c, FlagShardID) if err != nil { return commoncli.Problem("Required flag not found", err) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } shardManager, err := getDeps(c).initializeShardManager(c) if err != nil { return commoncli.Problem("Error in describe shard: ", err) } getShardReq := &persistence.GetShardRequest{ShardID: sid} resp, err := shardManager.GetShard(ctx, getShardReq) if err != nil { return commoncli.Problem("Failed to describe shard.", err) } prettyPrintJSONObject(getDeps(c).Output(), resp) return nil } // AdminSetShardRangeID set shard rangeID by shard id func AdminSetShardRangeID(c *cli.Context) error { sid, err := getRequiredIntOption(c, FlagShardID) if err != nil { return commoncli.Problem("Required flag not found", err) } rid, err := getRequiredInt64Option(c, FlagRangeID) if err != nil { return commoncli.Problem("Required flag not found", err) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } shardManager, err := getDeps(c).initializeShardManager(c) if err != nil { return commoncli.Problem("Error in Admin SetShardRangeID: ", err) } getShardResp, err := shardManager.GetShard(ctx, &persistence.GetShardRequest{ShardID: sid}) if err != nil { return commoncli.Problem("Failed to get shardInfo.", err) } previousRangeID := getShardResp.ShardInfo.RangeID updatedShardInfo := getShardResp.ShardInfo updatedShardInfo.RangeID = rid updatedShardInfo.StolenSinceRenew++ updatedShardInfo.Owner = "" updatedShardInfo.UpdatedAt = time.Now() err = shardManager.UpdateShard(ctx, &persistence.UpdateShardRequest{ PreviousRangeID: previousRangeID, ShardInfo: updatedShardInfo, }) if err != nil { return commoncli.Problem("Failed to reset shard rangeID.", err) } fmt.Fprintf(getDeps(c).Output(), "Successfully updated rangeID from %v to %v for shard %v.\n", previousRangeID, rid, sid) return nil } // AdminCloseShard closes shard by shard id func AdminCloseShard(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } sid, err := getRequiredIntOption(c, FlagShardID) if err != nil { return commoncli.Problem("Required option not found", err) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context", err) } req := &types.CloseShardRequest{} req.ShardID = int32(sid) err = adminClient.CloseShard(ctx, req) if err != nil { return commoncli.Problem("Close shard task has failed", err) } return nil } type ShardRow struct { ShardID int32 `header:"ShardID"` Identity string `header:"Identity"` } // AdminDescribeShardDistribution describes shard distribution func AdminDescribeShardDistribution(c *cli.Context) error { output := getDeps(c).Output() adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context", err) } req := &types.DescribeShardDistributionRequest{ PageSize: int32(c.Int(FlagPageSize)), PageID: int32(c.Int(FlagPageID)), } resp, err := adminClient.DescribeShardDistribution(ctx, req) if err != nil { return commoncli.Problem("Shard list failed", err) } fmt.Fprintf(output, "Total Number of Shards: %d\n", resp.NumberOfShards) fmt.Fprintf(output, "Number of Shards Returned: %d\n", len(resp.Shards)) if len(resp.Shards) == 0 { return nil } table := []ShardRow{} opts := RenderOptions{DefaultTemplate: templateTable, Color: true} outputPageSize := tableRenderSize for shardID, identity := range resp.Shards { if outputPageSize == 0 { if err := Render(c, table, opts); err != nil { return fmt.Errorf("error rendering: %w", err) } table = []ShardRow{} if !showNextPage(output) { break } outputPageSize = tableRenderSize } table = append(table, ShardRow{ShardID: shardID, Identity: identity}) outputPageSize-- } // output the remaining rows return Render(c, table, opts) } // AdminDescribeHistoryHost describes history host func AdminDescribeHistoryHost(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } wid := c.String(FlagWorkflowID) sid := c.Int(FlagShardID) addr := c.String(FlagHistoryAddress) printFully := c.Bool(FlagPrintFullyDetail) if len(wid) == 0 && !c.IsSet(FlagShardID) && len(addr) == 0 { return commoncli.Problem("at least one of them is required to provide to lookup host: workflowID, shardID and host address", nil) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context", err) } req := &types.DescribeHistoryHostRequest{} if len(wid) > 0 { req.ExecutionForHost = &types.WorkflowExecution{WorkflowID: wid} } if c.IsSet(FlagShardID) { req.ShardIDForHost = common.Int32Ptr(int32(sid)) } if len(addr) > 0 { req.HostAddress = common.StringPtr(addr) } resp, err := adminClient.DescribeHistoryHost(ctx, req) if err != nil { return commoncli.Problem("Describe history host failed", err) } if !printFully { resp.ShardIDs = nil } prettyPrintJSONObject(getDeps(c).Output(), resp) return nil } // AdminRefreshWorkflowTasks refreshes all the tasks of a workflow func AdminRefreshWorkflowTasks(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } domain, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found", err) } wid, err := getRequiredOption(c, FlagWorkflowID) if err != nil { return commoncli.Problem("Required flag not found", err) } rid := c.String(FlagRunID) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } err = adminClient.RefreshWorkflowTasks(ctx, &types.RefreshWorkflowTasksRequest{ Domain: domain, Execution: &types.WorkflowExecution{ WorkflowID: wid, RunID: rid, }, }) if err != nil { return commoncli.Problem("Refresh workflow task failed", err) } fmt.Fprintln(getDeps(c).Output(), "Refresh workflow task succeeded.") return nil } // AdminResetQueue resets task processing queue states func AdminResetQueue(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } shardID, err := getRequiredIntOption(c, FlagShardID) if err != nil { return commoncli.Problem("Required flag not found", err) } clusterName, err := getRequiredOption(c, FlagCluster) if err != nil { return commoncli.Problem("Required flag not found", err) } typeID, err := getRequiredIntOption(c, FlagQueueType) if err != nil { return commoncli.Problem("Required flag not found", err) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } req := &types.ResetQueueRequest{ ShardID: int32(shardID), ClusterName: clusterName, Type: common.Int32Ptr(int32(typeID)), } err = adminClient.ResetQueue(ctx, req) if err != nil { return commoncli.Problem("Failed to reset queue", err) } fmt.Fprintln(getDeps(c).Output(), "Reset queue state succeeded") return nil } // AdminDescribeQueue describes task processing queue states func AdminDescribeQueue(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } shardID, err := getRequiredIntOption(c, FlagShardID) if err != nil { return commoncli.Problem("Required flag not found", err) } clusterName, err := getRequiredOption(c, FlagCluster) if err != nil { return commoncli.Problem("Required flag not found", err) } typeID, err := getRequiredIntOption(c, FlagQueueType) if err != nil { return commoncli.Problem("Required flag not found", err) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } req := &types.DescribeQueueRequest{ ShardID: int32(shardID), ClusterName: clusterName, Type: common.Int32Ptr(int32(typeID)), } resp, err := adminClient.DescribeQueue(ctx, req) if err != nil { return commoncli.Problem("Failed to describe queue", err) } output := getDeps(c).Output() for _, state := range resp.ProcessingQueueStates { fmt.Fprintln(output, state) } return nil } ================================================ FILE: tools/cli/admin_commands_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "context" "encoding/json" "errors" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/cli/clitest" ) const ( testShardID = 1234 testCluster = "test-cluster" testDomain = "test-domain" testDomainID = "0000001-0000002-0000003" testTaskList = "test-tasklist" testTaskListType = "decision" testQueueType = 2 // transfer queue testWorkflowID = "test-workflow-id" testRunID = "test-run-id" ) type cliTestData struct { ctrl *gomock.Controller mockFrontendClient *frontend.MockClient mockAdminClient *admin.MockClient ioHandler *testIOHandler app *cli.App mockManagerFactory *MockManagerFactory } func newCLITestData(t *testing.T) *cliTestData { var td cliTestData td.ctrl = gomock.NewController(t) td.mockFrontendClient = frontend.NewMockClient(td.ctrl) td.mockAdminClient = admin.NewMockClient(td.ctrl) td.mockManagerFactory = NewMockManagerFactory(td.ctrl) td.ioHandler = &testIOHandler{} // Create a new CLI app with client factory and persistence manager factory td.app = NewCliApp( &clientFactoryMock{ serverFrontendClient: td.mockFrontendClient, serverAdminClient: td.mockAdminClient, }, WithIOHandler(td.ioHandler), WithManagerFactory(td.mockManagerFactory), // Inject the mocked persistence manager factory ) return &td } func (td *cliTestData) consoleOutput() string { return td.ioHandler.outputBytes.String() } func TestAdminResetQueue(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "no shardID argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Required flag not found", }, { name: "missing cluster argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), /* no cluster argument */ ) }, errContains: "Required flag not found", }, { name: "missing queue type argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.StringArgument(FlagCluster, testCluster), /* no queue type argument */ ) }, errContains: "Required flag not found", }, { name: "all arguments provided", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.StringArgument(FlagCluster, testCluster), clitest.IntArgument(FlagQueueType, testQueueType), ) td.mockAdminClient.EXPECT().ResetQueue(gomock.Any(), &types.ResetQueueRequest{ ShardID: testShardID, ClusterName: testCluster, Type: common.Int32Ptr(testQueueType), }) return cliCtx }, errContains: "", expectedOutput: "Reset queue state succeeded\n", }, { name: "ResetQueue returns an error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.StringArgument(FlagCluster, testCluster), clitest.IntArgument(FlagQueueType, testQueueType), ) td.mockAdminClient.EXPECT().ResetQueue(gomock.Any(), gomock.Any()). Return(errors.New("critical error")) return cliCtx }, errContains: "Failed to reset queue", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminResetQueue(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestAdminDescribeQueue(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "no shardID argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Required flag not found", }, { name: "missing cluster argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), /* no cluster argument */ ) }, errContains: "Required flag not found", }, { name: "missing queue type argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.StringArgument(FlagCluster, testCluster), /* no queue type argument */ ) }, errContains: "Required flag not found", }, { name: "all arguments provided", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.StringArgument(FlagCluster, testCluster), clitest.IntArgument(FlagQueueType, testQueueType), ) td.mockAdminClient.EXPECT().DescribeQueue(gomock.Any(), &types.DescribeQueueRequest{ ShardID: testShardID, ClusterName: testCluster, Type: common.Int32Ptr(testQueueType), }).Return(&types.DescribeQueueResponse{ ProcessingQueueStates: []string{"state1", "state2"}, }, nil) return cliCtx }, errContains: "", expectedOutput: "state1\nstate2\n", }, { name: "DescribeQueue returns an error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.StringArgument(FlagCluster, testCluster), clitest.IntArgument(FlagQueueType, testQueueType), ) td.mockAdminClient.EXPECT().DescribeQueue(gomock.Any(), gomock.Any()). Return(nil, errors.New("critical error")) return cliCtx }, errContains: "Failed to describe queue", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminDescribeQueue(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestAdminRefreshWorkflowTasks(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "no domain argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Required flag not found", }, { name: "missing workflowID argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), /* no workflowID argument */ ) }, errContains: "Required flag not found", }, { name: "all arguments provided", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagWorkflowID, testWorkflowID), clitest.StringArgument(FlagRunID, testRunID), ) td.mockAdminClient.EXPECT().RefreshWorkflowTasks(gomock.Any(), &types.RefreshWorkflowTasksRequest{ Domain: testDomain, Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, }).Return(nil) return cliCtx }, errContains: "", expectedOutput: "Refresh workflow task succeeded.\n", }, { name: "RefreshWorkflowTasks returns an error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagWorkflowID, testWorkflowID), clitest.StringArgument(FlagRunID, testRunID), ) td.mockAdminClient.EXPECT().RefreshWorkflowTasks(gomock.Any(), gomock.Any()). Return(errors.New("critical error")) return cliCtx }, errContains: "Refresh workflow task failed", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminRefreshWorkflowTasks(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestAdminDescribeHistoryHost(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "no arguments provided", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "at least one of them is required to provide to lookup host: workflowID, shardID and host address", }, { name: "workflow-id provided, but empty", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument(FlagWorkflowID, "")) }, errContains: "at least one of them is required to provide to lookup host: workflowID, shardID and host address", }, { name: "addr provided, but empty", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument(FlagHistoryAddress, "")) }, errContains: "at least one of them is required to provide to lookup host: workflowID, shardID and host address", }, { name: "calling with all arguments", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.StringArgument(FlagWorkflowID, testWorkflowID), clitest.StringArgument(FlagHistoryAddress, "host:port"), clitest.BoolArgument(FlagPrintFullyDetail, false), ) td.mockAdminClient.EXPECT().DescribeHistoryHost(gomock.Any(), &types.DescribeHistoryHostRequest{ ExecutionForHost: &types.WorkflowExecution{WorkflowID: testWorkflowID}, ShardIDForHost: common.Int32Ptr(int32(testShardID)), HostAddress: common.StringPtr("host:port"), }).Return( &types.DescribeHistoryHostResponse{NumberOfShards: 12, Address: "host:port"}, nil, ) return cliCtx }, errContains: "", expectedOutput: `{ "numberOfShards": 12, "address": "host:port" } `, }, { name: "DescribeHistoryHost returns an error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagWorkflowID, testWorkflowID), ) td.mockAdminClient.EXPECT().DescribeHistoryHost(gomock.Any(), gomock.Any()). Return(nil, errors.New("critical error")) return cliCtx }, errContains: "Describe history host failed", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminDescribeHistoryHost(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestAdminCloseShard(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected }{ { name: "no arguments provided", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Required option not found", }, { name: "calling with all arguments", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), ) td.mockAdminClient.EXPECT().CloseShard(gomock.Any(), &types.CloseShardRequest{ ShardID: testShardID, }).Return(nil) return cliCtx }, errContains: "", }, { name: "CloseShard returns an error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), ) td.mockAdminClient.EXPECT().CloseShard(gomock.Any(), gomock.Any()). Return(errors.New("critical error")) return cliCtx }, errContains: "Close shard task has failed", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminCloseShard(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } }) } } func TestAdminRemoveTask(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected }{ { name: "no arguments provided", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Required flag not found", }, { name: "missing ShardID", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.Int64Argument(FlagTaskID, 123), clitest.IntArgument(FlagTaskType, 1), // type is provided ) }, errContains: "Required flag not found", }, { name: "missing TaskID", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.IntArgument(FlagShardID, 1), clitest.IntArgument(FlagTaskType, 1), // type is provided ) }, errContains: "Required flag not found", }, { name: "missing TaskType", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.IntArgument(FlagShardID, 1), clitest.Int64Argument(FlagTaskID, 123), ) }, errContains: "Required flag not found", }, { name: "calling with all arguments", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.Int64Argument(FlagTaskID, 123), clitest.IntArgument(FlagTaskType, 1), // some valid type ) td.mockAdminClient.EXPECT().RemoveTask(gomock.Any(), &types.RemoveTaskRequest{ ShardID: int32(testShardID), Type: common.Int32Ptr(1), TaskID: 123, VisibilityTimestamp: common.Int64Ptr(0), ClusterName: "", }).Return(nil) return cliCtx }, errContains: "", }, { name: "RemoveTask returns an error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.Int64Argument(FlagTaskID, 123), clitest.IntArgument(FlagTaskType, 1), // some valid type ) td.mockAdminClient.EXPECT().RemoveTask(gomock.Any(), gomock.Any()). Return(errors.New("critical error")) return cliCtx }, errContains: "Remove task has failed", }, { name: "calling with Timer task requiring visibility timestamp", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.Int64Argument(FlagTaskID, 123), clitest.IntArgument(FlagTaskType, int(constants.TaskTypeTimer)), clitest.Int64Argument(FlagTaskVisibilityTimestamp, 1616161616), // visibility timestamp ) td.mockAdminClient.EXPECT().RemoveTask(gomock.Any(), &types.RemoveTaskRequest{ ShardID: int32(testShardID), Type: common.Int32Ptr(int32(constants.TaskTypeTimer)), TaskID: 123, VisibilityTimestamp: common.Int64Ptr(1616161616), ClusterName: "", }).Return(nil) return cliCtx }, errContains: "", }, { name: "calling with Timer task requiring visibility timestamp, but not provided", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.Int64Argument(FlagTaskID, 123), clitest.IntArgument(FlagTaskType, int(constants.TaskTypeTimer)), // visibility timestamp is missing though FlagTaskType is common.TaskTypeTimer ) return cliCtx }, errContains: "Required flag not found", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminRemoveTask(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } }) } } func TestAdminGetShardID(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context expectedOutput string // expected output to check against errContains string // empty if no error is expected }{ { name: "no WorkflowID provided", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Required flag not found", }, { name: "numberOfShards not provided", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument(FlagWorkflowID, "some-workflow-id"), /* numberOfShards is missing */ ) }, errContains: "numberOfShards is required", }, { name: "numberOfShards is zero", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument(FlagWorkflowID, "some-workflow-id"), clitest.IntArgument(FlagNumberOfShards, 0), // zero is invalid ) }, errContains: "numberOfShards is required", }, { name: "valid inputs", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext(t, td.app, clitest.StringArgument(FlagWorkflowID, testWorkflowID), clitest.IntArgument(FlagNumberOfShards, 10), // valid number of shards ) return cliCtx }, expectedOutput: "ShardID for workflowID: test-workflow-id is 6\n", errContains: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminGetShardID(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } // If there is no error, check the output if tt.expectedOutput != "" { assert.Contains(t, tt.expectedOutput, td.consoleOutput()) } }) } } func TestAdminDescribeShardDistribution(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "all arguments provided", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagPageSize, 10), clitest.IntArgument(FlagPageID, 1), clitest.StringArgument(FlagFormat, formatJSON), ) td.mockAdminClient.EXPECT().DescribeShardDistribution( gomock.Any(), &types.DescribeShardDistributionRequest{ PageSize: 10, PageID: 1, }).Return( &types.DescribeShardDistributionResponse{ NumberOfShards: 1, Shards: map[int32]string{ 1: "identity1", }, }, nil, ) return cliCtx }, errContains: "", expectedOutput: `Total Number of Shards: 1 Number of Shards Returned: 1 [ { "ShardID": 1, "Identity": "identity1" } ] `, }, { name: "no shards are returned", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagPageSize, 10), clitest.IntArgument(FlagPageID, 1), clitest.StringArgument(FlagFormat, formatJSON), ) td.mockAdminClient.EXPECT().DescribeShardDistribution(gomock.Any(), gomock.Any()). Return( &types.DescribeShardDistributionResponse{ NumberOfShards: 10, Shards: nil, // no shards }, nil, ) return cliCtx }, errContains: "", expectedOutput: "Total Number of Shards: 10\nNumber of Shards Returned: 0\n", }, { name: "DescribeShardDistribution returns an error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagPageSize, 10), clitest.IntArgument(FlagPageID, 1), ) td.mockAdminClient.EXPECT().DescribeShardDistribution(gomock.Any(), gomock.Any()). Return(nil, errors.New("critical error")) return cliCtx }, errContains: "Shard list failed", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminDescribeShardDistribution(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestAdminMaintainCorruptWorkflow(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected }{ { name: "no domain argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Required flag not found", }, { name: "all arguments provided", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagWorkflowID, testWorkflowID), clitest.StringArgument(FlagRunID, testRunID), clitest.BoolArgument(FlagSkipErrorMode, true), ) td.mockAdminClient.EXPECT().MaintainCorruptWorkflow(gomock.Any(), &types.AdminMaintainWorkflowRequest{ Domain: testDomain, Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, SkipErrors: true, }).Return(nil, nil) return cliCtx }, errContains: "", }, { name: "MaintainCorruptWorkflow returns an error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagWorkflowID, testWorkflowID), clitest.StringArgument(FlagRunID, testRunID), clitest.BoolArgument(FlagSkipErrorMode, false), ) td.mockAdminClient.EXPECT().MaintainCorruptWorkflow(gomock.Any(), gomock.Any()). Return(nil, errors.New("critical error")) return cliCtx }, errContains: "Operation AdminMaintainCorruptWorkflow failed.", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminMaintainCorruptWorkflow(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } }) } } func TestAdminDescribeShard(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected checkOutput func(td *cliTestData) }{ { name: "no ShardID argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Required flag not found", }, { name: "all arguments provided", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), ) mockShardManager := persistence.NewMockShardManager(td.ctrl) mockShardManager.EXPECT().GetShard( gomock.Any(), &persistence.GetShardRequest{ShardID: testShardID}, ).Return(&persistence.GetShardResponse{ ShardInfo: &persistence.ShardInfo{ ShardID: testShardID, Owner: "host-abc", }, }, nil) td.mockManagerFactory.EXPECT().initializeShardManager(gomock.Any()). Return(mockShardManager, nil) return cliCtx }, errContains: "", checkOutput: func(td *cliTestData) { // We must have visualised GetShardResponse in console. // Check it is a valid JSON and important fields are there var resp persistence.GetShardResponse require.NoError(t, json.Unmarshal([]byte(td.consoleOutput()), &resp)) assert.Equal(t, testShardID, resp.ShardInfo.ShardID) assert.Equal(t, "host-abc", resp.ShardInfo.Owner) }, }, { name: "all arguments provided", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), ) mockShardManager := persistence.NewMockShardManager(td.ctrl) mockShardManager.EXPECT().GetShard( gomock.Any(), &persistence.GetShardRequest{ShardID: testShardID}, ).Return(&persistence.GetShardResponse{ ShardInfo: &persistence.ShardInfo{ ShardID: testShardID, Owner: "host-abc", }, }, nil) td.mockManagerFactory.EXPECT().initializeShardManager(gomock.Any()). Return(mockShardManager, nil) return cliCtx }, errContains: "", checkOutput: func(td *cliTestData) { // We must have visualised GetShardResponse in console. // Check it is a valid JSON and important fields are there var resp persistence.GetShardResponse require.NoError(t, json.Unmarshal([]byte(td.consoleOutput()), &resp)) assert.Equal(t, testShardID, resp.ShardInfo.ShardID) assert.Equal(t, "host-abc", resp.ShardInfo.Owner) }, }, { name: "GetShard returns an error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), ) mockShardManager := persistence.NewMockShardManager(td.ctrl) mockShardManager.EXPECT().GetShard(gomock.Any(), gomock.Any()). Return(nil, errors.New("critical error")) td.mockManagerFactory.EXPECT().initializeShardManager(gomock.Any()). Return(mockShardManager, nil) return cliCtx }, errContains: "Failed to describe shard", }, { name: "failed to initializeShardManager", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), ) td.mockManagerFactory.EXPECT().initializeShardManager(gomock.Any()). Return(nil, errors.New("failed to initializeShardManager")) return cliCtx }, errContains: "failed to initializeShardManager", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminDescribeShard(cliCtx) if tt.errContains == "" { assert.NoError(t, err) require.NotNil(t, tt.checkOutput) tt.checkOutput(td) } else { assert.ErrorContains(t, err, tt.errContains) } }) } } func TestAdminSetShardRangeID(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "no ShardID argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Required flag not found", }, { name: "no RangeID argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.IntArgument(FlagShardID, testShardID), // FlagRangeID is missing ) }, errContains: "Required flag not found", }, { name: "all arguments provided", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.Int64Argument(FlagRangeID, 133), ) mockShardManager := persistence.NewMockShardManager(td.ctrl) mockShardManager.EXPECT().GetShard( gomock.Any(), &persistence.GetShardRequest{ShardID: testShardID}, ).Return(&persistence.GetShardResponse{ ShardInfo: &persistence.ShardInfo{ ShardID: testShardID, Owner: "host-abc", RangeID: 123, StolenSinceRenew: 100, // this supposed to be incremented }, }, nil) mockShardManager.EXPECT().UpdateShard( gomock.Any(), gomock.Any(), ).Do(func(ctx context.Context, req *persistence.UpdateShardRequest) { // we can't use input arguments matching as it contains current time assert.Equal(t, int64(123), req.PreviousRangeID) assert.Equal(t, 101, req.ShardInfo.StolenSinceRenew) assert.Equal(t, int64(133), req.ShardInfo.RangeID) now := time.Now() // check time is really updated assert.WithinRange( t, req.ShardInfo.UpdatedAt, now.Add(-time.Minute), time.Now(), "didn't update UpdatedAt?", ) }).Return(nil) td.mockManagerFactory.EXPECT().initializeShardManager(gomock.Any()). Return(mockShardManager, nil) return cliCtx }, errContains: "", expectedOutput: "Successfully updated rangeID from 123 to 133 for shard 1234.\n", }, { name: "all arguments provided, but UpdateShard fails", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.Int64Argument(FlagRangeID, 133), ) mockShardManager := persistence.NewMockShardManager(td.ctrl) mockShardManager.EXPECT().GetShard( gomock.Any(), gomock.Any(), ).Return(&persistence.GetShardResponse{ ShardInfo: &persistence.ShardInfo{ ShardID: testShardID, }, }, nil) mockShardManager.EXPECT().UpdateShard( gomock.Any(), gomock.Any(), ).Return(errors.New("critical failure")) td.mockManagerFactory.EXPECT().initializeShardManager(gomock.Any()). Return(mockShardManager, nil) return cliCtx }, errContains: "Failed to reset shard rangeID.", }, { name: "GetShard returns an error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.Int64Argument(FlagRangeID, 123), ) mockShardManager := persistence.NewMockShardManager(td.ctrl) mockShardManager.EXPECT().GetShard(gomock.Any(), gomock.Any()). Return(nil, errors.New("critical error")) td.mockManagerFactory.EXPECT().initializeShardManager(gomock.Any()). Return(mockShardManager, nil) return cliCtx }, errContains: "Failed to get shardInfo.", }, { name: "failed to initializeShardManager", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.IntArgument(FlagShardID, testShardID), clitest.Int64Argument(FlagRangeID, 123), ) td.mockManagerFactory.EXPECT().initializeShardManager(gomock.Any()). Return(nil, errors.New("failed to initializeShardManager")) return cliCtx }, errContains: "failed to initializeShardManager", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminSetShardRangeID(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestAdminGetDomainIDOrName(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "no DomainID or DomainName argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Need either domainName or domainID", }, { name: "DomainID provided, valid response", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomainID, testDomainID), ) mockDomainManager := persistence.NewMockDomainManager(td.ctrl) mockDomainManager.EXPECT().GetDomain( gomock.Any(), &persistence.GetDomainRequest{ID: testDomainID}, ).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ ID: testDomainID, Name: testDomain, }, }, nil) td.mockManagerFactory.EXPECT().initializeDomainManager(gomock.Any()). Return(mockDomainManager, nil) return cliCtx }, errContains: "", expectedOutput: fmt.Sprintf("domainName for domainID %v is %v\n", testDomainID, testDomain), }, { name: "DomainName provided, valid response", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), ) mockDomainManager := persistence.NewMockDomainManager(td.ctrl) mockDomainManager.EXPECT().GetDomain( gomock.Any(), &persistence.GetDomainRequest{Name: testDomain}, ).Return(&persistence.GetDomainResponse{ Info: &persistence.DomainInfo{ ID: testDomainID, Name: testDomain, }, }, nil) td.mockManagerFactory.EXPECT().initializeDomainManager(gomock.Any()). Return(mockDomainManager, nil) return cliCtx }, errContains: "", expectedOutput: fmt.Sprintf("domainID for domainName %v is %v\n", testDomain, testDomainID), }, { name: "DomainManager returns an error for domainID", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomainID, testDomainID), ) mockDomainManager := persistence.NewMockDomainManager(td.ctrl) mockDomainManager.EXPECT().GetDomain(gomock.Any(), gomock.Any()). Return(nil, errors.New("critical error")) td.mockManagerFactory.EXPECT().initializeDomainManager(gomock.Any()). Return(mockDomainManager, nil) return cliCtx }, errContains: "GetDomain error", }, { name: "DomainManager returns an error for domainName", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), ) mockDomainManager := persistence.NewMockDomainManager(td.ctrl) mockDomainManager.EXPECT().GetDomain(gomock.Any(), gomock.Any()). Return(nil, errors.New("critical error")) td.mockManagerFactory.EXPECT().initializeDomainManager(gomock.Any()). Return(mockDomainManager, nil) return cliCtx }, errContains: "GetDomain error", }, { name: "failed to initializeDomainManager", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomainID, testDomainID), ) td.mockManagerFactory.EXPECT().initializeDomainManager(gomock.Any()). Return(nil, errors.New("failed to initializeDomainManager")) return cliCtx }, errContains: "failed to initializeDomainManager", }, { name: "both DomainID and DomainName provided", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomainID, testDomainID), clitest.StringArgument(FlagDomain, testDomain), ) return cliCtx }, errContains: "Need either domainName or domainID", // Expecting the error when both are provided }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminGetDomainIDOrName(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestAdminDeleteWorkflow(t *testing.T) { tests := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected }{ { name: "no domain argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app /* arguments are missing */) }, errContains: "Required flag not found", }, { name: "no workflow ID argument", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), ) }, errContains: "Required flag not found", }, { name: "remote delete success", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagWorkflowID, testWorkflowID), clitest.StringArgument(FlagRunID, testRunID), clitest.BoolArgument(FlagRemote, true), clitest.BoolArgument(FlagSkipErrorMode, false), ) td.mockAdminClient.EXPECT().DeleteWorkflow(gomock.Any(), &types.AdminDeleteWorkflowRequest{ Domain: testDomain, Execution: &types.WorkflowExecution{ WorkflowID: testWorkflowID, RunID: testRunID, }, SkipErrors: false, }).Return(&types.AdminDeleteWorkflowResponse{}, nil) return cliCtx }, errContains: "", }, { name: "remote delete returns error", testSetup: func(td *cliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagWorkflowID, testWorkflowID), clitest.StringArgument(FlagRunID, testRunID), clitest.BoolArgument(FlagRemote, true), ) td.mockAdminClient.EXPECT().DeleteWorkflow(gomock.Any(), gomock.Any()). Return(nil, errors.New("delete failed")) return cliCtx }, errContains: "Operation AdminMaintainCorruptWorkflow failed.", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) cliCtx := tt.testSetup(td) err := AdminDeleteWorkflow(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } }) } } ================================================ FILE: tools/cli/admin_config_store_commands.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "encoding/json" "fmt" "sort" "strings" "github.com/olekukonko/tablewriter" "github.com/urfave/cli/v2" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/common/commoncli" ) type cliEntry struct { Name string DefaultValue interface{} `json:"defaultValue,omitempty"` Values []*cliValue } type cliValue struct { Value interface{} Filters []*cliFilter } type cliFilter struct { Name string Value interface{} } // AdminGetDynamicConfig gets value of specified dynamic config parameter matching specified filter func AdminGetDynamicConfig(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } configName, err := getRequiredOption(c, FlagDynamicConfigName) if err != nil { return commoncli.Problem("Required flag not found", err) } filter := c.String(FlagDynamicConfigFilter) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } parsedFilters, err := parseInputFilter(filter) if err != nil { return commoncli.Problem("Failed to parse input filter array", err) } req := &types.GetDynamicConfigRequest{ ConfigName: configName, Filters: parsedFilters, } val, err := adminClient.GetDynamicConfig(ctx, req) if err != nil { return commoncli.Problem("Failed to get dynamic config value", err) } var umVal interface{} err = json.Unmarshal(val.Value.Data, &umVal) if err != nil { return commoncli.Problem("Failed to unmarshal response", err) } if umVal == nil { fmt.Printf("No values stored for specified dynamic config.\n") } else { prettyPrintJSONObject(getDeps(c).Output(), umVal) } return nil } // AdminUpdateDynamicConfig updates specified dynamic config parameter with specified values func AdminUpdateDynamicConfig(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } dcName, err := getRequiredOption(c, FlagDynamicConfigName) if err != nil { return commoncli.Problem("Required flag not found", err) } dcValuesRaw := c.StringSlice(FlagDynamicConfigValue) // WORKAROUND: urfave/cli v2 StringSliceFlag splits on commas by default. // This breaks JSON values. Try reassembling the split pieces. var dcValues []string if len(dcValuesRaw) > 1 && strings.HasPrefix(dcValuesRaw[0], "{") { assembled := strings.Join(dcValuesRaw, ",") var test interface{} if json.Unmarshal([]byte(assembled), &test) == nil { dcValues = []string{assembled} } else { dcValues = dcValuesRaw } } else { dcValues = dcValuesRaw } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } var parsedValues []*types.DynamicConfigValue if dcValues != nil { parsedValues = make([]*types.DynamicConfigValue, 0, len(dcValues)) for _, valueString := range dcValues { var parsedInputValue *cliValue err := json.Unmarshal([]byte(valueString), &parsedInputValue) if err != nil { return commoncli.Problem("Unable to unmarshal value to inputValue", err) } parsedValue, err := convertFromInputValue(parsedInputValue) if err != nil { return commoncli.Problem("Unable to convert from inputValue to DynamicConfigValue", err) } parsedValues = append(parsedValues, parsedValue) } } else { parsedValues = nil } req := &types.UpdateDynamicConfigRequest{ ConfigName: dcName, ConfigValues: parsedValues, } err = adminClient.UpdateDynamicConfig(ctx, req) if err != nil { return commoncli.Problem("Failed to update dynamic config value", err) } fmt.Printf("Dynamic Config %q updated with %s \n", dcName, dcValues) return nil } // AdminRestoreDynamicConfig removes values of specified dynamic config parameter matching specified filter func AdminRestoreDynamicConfig(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } dcName, err := getRequiredOption(c, FlagDynamicConfigName) if err != nil { return commoncli.Problem("Required flag not found", err) } filter := c.String(FlagDynamicConfigFilter) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } parsedFilters, err := parseInputFilter(filter) if err != nil { return commoncli.Problem("Failed to parse input filter", err) } req := &types.RestoreDynamicConfigRequest{ ConfigName: dcName, Filters: parsedFilters, } err = adminClient.RestoreDynamicConfig(ctx, req) if err != nil { return commoncli.Problem("Failed to restore dynamic config value", err) } fmt.Printf("Dynamic Config %q restored\n", dcName) return nil } // AdminListDynamicConfig lists all values associated with specified dynamic config parameter or all values for all dc parameter if none is specified. func AdminListDynamicConfig(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } req := &types.ListDynamicConfigRequest{ ConfigName: "", // empty string means all config values } val, err := adminClient.ListDynamicConfig(ctx, req) if err != nil { return commoncli.Problem("Failed to list dynamic config value(s)", err) } if val == nil || val.Entries == nil || len(val.Entries) == 0 { fmt.Printf("No dynamic config values stored to list.\n") } else { cliEntries := make([]*cliEntry, 0, len(val.Entries)) for _, dcEntry := range val.Entries { cliEntry, err := convertToInputEntry(dcEntry) if err != nil { fmt.Printf("Cannot parse list response.\n") } cliEntries = append(cliEntries, cliEntry) } prettyPrintJSONObject(getDeps(c).Output(), cliEntries) } return nil } // AdminListConfigKeys lists all available dynamic config keys with description and default value func AdminListConfigKeys(c *cli.Context) error { type ConfigRow struct { Name string `header:"Name" json:"name"` Description string `header:"Description" json:"description"` Default interface{} `header:"Default value" json:"default"` } var rows []ConfigRow for name, k := range dynamicproperties.GetAllKeys() { rows = append(rows, ConfigRow{ Name: name, Description: k.Description(), Default: k.DefaultValue(), }) } // sorting config key names alphabetically sort.SliceStable(rows, func(i, j int) bool { return rows[i].Name < rows[j].Name }) return Render(c, rows, RenderOptions{ DefaultTemplate: templateTable, Color: true, Border: true, ColumnAlignment: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_RIGHT}}, ) } func convertToInputEntry(dcEntry *types.DynamicConfigEntry) (*cliEntry, error) { newValues := make([]*cliValue, 0, len(dcEntry.Values)) for _, value := range dcEntry.Values { newValue, err := convertToInputValue(value) if err != nil { return nil, err } newValues = append(newValues, newValue) } return &cliEntry{ Name: dcEntry.Name, Values: newValues, }, nil } func convertToInputValue(dcValue *types.DynamicConfigValue) (*cliValue, error) { newFilters := make([]*cliFilter, 0, len(dcValue.Filters)) for _, filter := range dcValue.Filters { newFilter, err := convertToInputFilter(filter) if err != nil { return nil, err } newFilters = append(newFilters, newFilter) } var val interface{} err := json.Unmarshal(dcValue.Value.Data, &val) if err != nil { return nil, err } return &cliValue{ Value: val, Filters: newFilters, }, nil } func convertToInputFilter(dcFilter *types.DynamicConfigFilter) (*cliFilter, error) { var val interface{} err := json.Unmarshal(dcFilter.Value.Data, &val) if err != nil { return nil, err } return &cliFilter{ Name: dcFilter.Name, Value: val, }, nil } func convertFromInputValue(inputValue *cliValue) (*types.DynamicConfigValue, error) { encodedValue, err := json.Marshal(inputValue.Value) if err != nil { return nil, err } blob := &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: encodedValue, } dcFilters := make([]*types.DynamicConfigFilter, 0, len(inputValue.Filters)) for _, inputFilter := range inputValue.Filters { dcFilter, err := convertFromInputFilter(inputFilter) if err != nil { return nil, err } dcFilters = append(dcFilters, dcFilter) } return &types.DynamicConfigValue{ Value: blob, Filters: dcFilters, }, nil } func convertFromInputFilter(inputFilter *cliFilter) (*types.DynamicConfigFilter, error) { encodedValue, err := json.Marshal(inputFilter.Value) if err != nil { return nil, err } return &types.DynamicConfigFilter{ Name: inputFilter.Name, Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: encodedValue, }, }, nil } func parseInputFilter(inputFilter string) ([]*types.DynamicConfigFilter, error) { if inputFilter == "" || inputFilter == "{}" { return nil, nil } var mapFilter = make(map[string]interface{}) if err := json.Unmarshal([]byte(inputFilter), &mapFilter); err != nil { return nil, err } var parsedFilters = make([]*types.DynamicConfigFilter, 0, len(mapFilter)) for name, value := range mapFilter { parsedFilter, err := convertFromInputFilter(&cliFilter{ Name: name, Value: value, }) if err != nil { return nil, err } parsedFilters = append(parsedFilters, parsedFilter) } return parsedFilters, nil } ================================================ FILE: tools/cli/admin_config_store_commands_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "context" "encoding/json" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/cli/clitest" ) func TestAdminGetDynamicConfig(t *testing.T) { tests := []struct { name string cmdline string setupMock func(td *cliTestData) errContains string // empty if no error is expected }{ { cmdline: `cadence admin config get --name ""`, name: "no arguments provided", setupMock: func(td *cliTestData) { // empty since arguments are missing }, errContains: "Required flag not found", }, { name: "failed to get dynamic config values", cmdline: `cadence admin config get --name test-dynamic-config-name`, setupMock: func(td *cliTestData) { td.mockAdminClient.EXPECT().GetDynamicConfig(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, request *types.GetDynamicConfigRequest, _ ...yarpc.CallOption) (*types.GetDynamicConfigResponse, error) { assert.Equal(t, "test-dynamic-config-name", request.ConfigName) return nil, assert.AnError }) }, errContains: "Failed to get dynamic config value", }, { name: "received a dynamic config value successfully", cmdline: `cadence admin config get --name test-dynamic-config-name`, setupMock: func(td *cliTestData) { td.mockAdminClient.EXPECT().GetDynamicConfig(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, request *types.GetDynamicConfigRequest, _ ...yarpc.CallOption) (*types.GetDynamicConfigResponse, error) { assert.Equal(t, "test-dynamic-config-name", request.ConfigName) return &types.GetDynamicConfigResponse{ Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte(`"config-value"`), }, }, nil }) }, errContains: "", }, { name: "received a dynamic config value with filters successfully", cmdline: `cadence admin config get --name test-dynamic-config-name --filter '{"domainName":"test-domain", "shardID": 1, "isEnabled": true}'`, setupMock: func(td *cliTestData) { td.mockAdminClient.EXPECT().GetDynamicConfig(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, request *types.GetDynamicConfigRequest, _ ...yarpc.CallOption) (*types.GetDynamicConfigResponse, error) { assert.Equal(t, "test-dynamic-config-name", request.ConfigName) assert.ElementsMatch(t, request.Filters, []*types.DynamicConfigFilter{ { Name: "domainName", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`"test-domain"`), }, }, { Name: "shardID", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`1`), }, }, { Name: "isEnabled", Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: []byte(`true`), }, }, }) return &types.GetDynamicConfigResponse{ Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte(`"config-value"`), }, }, nil }) }, errContains: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) tt.setupMock(td) err := clitest.RunCommandLine(t, td.app, tt.cmdline) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } }) } } func TestAdminUpdateDynamicConfig(t *testing.T) { tests := []struct { name string cmdline string setupMocks func(td *cliTestData) errContains string // empty if no error is expected }{ { name: "no arguments provided", cmdline: `cadence admin config update --name "" --value ""`, setupMocks: func(td *cliTestData) { // empty since arguments are missing }, errContains: "Required flag not found", }, { name: "calling with required arguments", cmdline: `cadence admin config update --name test-dynamic-config-name --value "{}"`, setupMocks: func(td *cliTestData) { td.mockAdminClient.EXPECT().UpdateDynamicConfig(gomock.Any(), gomock.Any()).Return(nil) }, errContains: "", }, { name: "failed to update dynamic config values", cmdline: `cadence admin config update --name test-dynamic-config-name --value "{}"`, setupMocks: func(td *cliTestData) { td.mockAdminClient.EXPECT().UpdateDynamicConfig(gomock.Any(), gomock.Any()).Return(assert.AnError) }, errContains: "Failed to update dynamic config value", }, { name: "update with simple boolean value", cmdline: `cadence admin config update --name test.config --value ` + `'{"Value":true,"Filters":[]}'`, setupMocks: func(td *cliTestData) { td.mockAdminClient.EXPECT().UpdateDynamicConfig(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, request *types.UpdateDynamicConfigRequest, _ ...yarpc.CallOption) error { assert.Equal(t, "test.config", request.ConfigName) assert.Len(t, request.ConfigValues, 1) // Verify the value is correctly parsed var actualValue interface{} err := json.Unmarshal(request.ConfigValues[0].Value.Data, &actualValue) assert.NoError(t, err) assert.Equal(t, true, actualValue) // Verify no filters assert.Empty(t, request.ConfigValues[0].Filters) return nil }) }, errContains: "", }, { name: "update with map value and no filters", cmdline: `cadence admin config update --name frontend.validSearchAttributes --value ` + `'{"Value":{"DomainID":1,"WorkflowID":1},"Filters":[]}'`, setupMocks: func(td *cliTestData) { td.mockAdminClient.EXPECT().UpdateDynamicConfig(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, request *types.UpdateDynamicConfigRequest, _ ...yarpc.CallOption) error { assert.Equal(t, "frontend.validSearchAttributes", request.ConfigName) assert.Len(t, request.ConfigValues, 1) // Verify the value is correctly parsed var actualValue interface{} err := json.Unmarshal(request.ConfigValues[0].Value.Data, &actualValue) assert.NoError(t, err) expectedValue := map[string]interface{}{ "DomainID": float64(1), // JSON numbers unmarshal to float64 "WorkflowID": float64(1), } assert.Equal(t, expectedValue, actualValue) // Verify no filters assert.Empty(t, request.ConfigValues[0].Filters) return nil }) }, errContains: "", }, { name: "update with map value and filters", cmdline: `cadence admin config update --name frontend.validSearchAttributes --value ` + `'{"Value":{"DomainID":1,"WorkflowID":1},"Filters":[{"Name":"domainName","Value":"test-domain"}]}'`, setupMocks: func(td *cliTestData) { td.mockAdminClient.EXPECT().UpdateDynamicConfig(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, request *types.UpdateDynamicConfigRequest, _ ...yarpc.CallOption) error { assert.Equal(t, "frontend.validSearchAttributes", request.ConfigName) assert.Len(t, request.ConfigValues, 1) // Verify the value is correctly parsed var actualValue interface{} err := json.Unmarshal(request.ConfigValues[0].Value.Data, &actualValue) assert.NoError(t, err) expectedValue := map[string]interface{}{ "DomainID": float64(1), "WorkflowID": float64(1), } assert.Equal(t, expectedValue, actualValue) // Verify filters assert.Len(t, request.ConfigValues[0].Filters, 1) assert.Equal(t, "domainName", request.ConfigValues[0].Filters[0].Name) var filterValue interface{} err = json.Unmarshal(request.ConfigValues[0].Filters[0].Value.Data, &filterValue) assert.NoError(t, err) assert.Equal(t, "test-domain", filterValue) return nil }) }, errContains: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) tt.setupMocks(td) err := clitest.RunCommandLine(t, td.app, tt.cmdline) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } }) } } func TestAdminRestoreDynamicConfig(t *testing.T) { tests := []struct { name string cmdline string setupMocks func(td *cliTestData) errContains string // empty if no error is expected }{ { name: "no arguments provided", cmdline: `cadence admin config restore --name ''`, setupMocks: func(td *cliTestData) { // empty since args are missing }, errContains: "Required flag not found", }, { name: "calling with required arguments", cmdline: `cadence admin config restore --name test-dynamic-config-name --filter '{"domainName":"test-domain"}'`, setupMocks: func(td *cliTestData) { td.mockAdminClient.EXPECT().RestoreDynamicConfig(gomock.Any(), gomock.Any()).Return(nil) }, errContains: "", }, { name: "failed to update dynamic config values", cmdline: `cadence admin config restore --name test-dynamic-config-name --filter '{"Value":"some-value","Filters":[]}'`, setupMocks: func(td *cliTestData) { td.mockAdminClient.EXPECT().RestoreDynamicConfig(gomock.Any(), gomock.Any()).Return(assert.AnError) }, errContains: "Failed to restore dynamic config value", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) tt.setupMocks(td) err := clitest.RunCommandLine(t, td.app, tt.cmdline) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } }) } } func TestAdminListDynamicConfig(t *testing.T) { tests := []struct { name string setupMocks func(td *cliTestData) errContains string // empty if no error is expected }{ { name: "failed with no dynamic config values stored to list", setupMocks: func(td *cliTestData) { td.mockAdminClient.EXPECT().ListDynamicConfig(gomock.Any(), gomock.Any()).Return(nil, nil) }, errContains: "", }, { name: "failed to list dynamic config values", setupMocks: func(td *cliTestData) { td.mockAdminClient.EXPECT().ListDynamicConfig(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) }, errContains: "Failed to list dynamic config value(s)", }, { name: "succeeded to list dynamic config values", setupMocks: func(td *cliTestData) { td.mockAdminClient.EXPECT().ListDynamicConfig(gomock.Any(), gomock.Any()).Return(&types.ListDynamicConfigResponse{ Entries: []*types.DynamicConfigEntry{ { Name: "test-dynamic-config-name", Values: []*types.DynamicConfigValue{ { Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("config-value"), }, Filters: []*types.DynamicConfigFilter{ { Name: "Filter1", Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("filter-value"), }, }, }, }, }, }, }}, nil) }, errContains: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) tt.setupMocks(td) err := clitest.RunCommandLine(t, td.app, "cadence admin config list") if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } }) } } func TestAdminListConfigKeys(t *testing.T) { t.Run("list config keys", func(t *testing.T) { td := newCLITestData(t) assert.NoError(t, clitest.RunCommandLine(t, td.app, "cadence admin config listall")) }) } ================================================ FILE: tools/cli/admin_db_clean_command.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "encoding/json" "fmt" "io" "os" "github.com/urfave/cli/v2" "go.uber.org/zap" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/entity" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/service/worker/scanner/executions" "github.com/uber/cadence/tools/common/commoncli" ) // AdminDBClean is the command to clean up unhealthy executions. // Input is a JSON stream provided via STDIN or a file. func AdminDBClean(c *cli.Context) error { flagscantype, err := getRequiredOption(c, FlagScanType) if err != nil { return commoncli.Problem("Required flag not found: ", err) } scanType, err := executions.ScanTypeString(flagscantype) if err != nil { return commoncli.Problem("unknown scan type", err) } collectionSlice := c.StringSlice(FlagInvariantCollection) blob := scanType.ToBlobstoreEntity() var collections []invariant.Collection for _, v := range collectionSlice { collection, err := invariant.CollectionString(v) if err != nil { return commoncli.Problem("unknown invariant collection", err) } collections = append(collections, collection) } logger := zap.NewNop() if c.Bool(FlagVerbose) { logger, err = zap.NewDevelopment() if err != nil { // probably impossible with default config return commoncli.Problem("could not construct logger", err) } } invariants := scanType.ToInvariants(collections, logger) if len(invariants) < 1 { return commoncli.Problem( fmt.Sprintf("no invariants for scantype %q and collections %q", scanType.String(), collectionSlice), nil, ) } input, err := getInputFile(c.String(FlagInputFile)) if err != nil { return commoncli.Problem("Required flag not found: ", err) } dec := json.NewDecoder(input) var data []*store.ScanOutputEntity for { soe := &store.ScanOutputEntity{ Execution: blob.Clone(), } if err := dec.Decode(&soe); err != nil { if err == io.EOF || err == io.ErrUnexpectedEOF { break } } else { data = append(data, soe) } } for _, e := range data { result, err := fixExecution(c, invariants, e) if err != nil { return commoncli.Problem("Error in fix execution: ", err) } out := store.FixOutputEntity{ Execution: e.Execution, Input: *e, Result: result, } data, err := json.Marshal(out) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) continue } output := getDeps(c).Output() output.Write([]byte(string(data) + "\n")) } return nil } func fixExecution( c *cli.Context, invariants []executions.InvariantFactory, execution *store.ScanOutputEntity, ) (invariant.ManagerFixResult, error) { execManager, err := getDeps(c).initializeExecutionManager(c, execution.Execution.(entity.Entity).GetShardID()) if err != nil { return invariant.ManagerFixResult{}, err } defer execManager.Close() historyV2Mgr, err := getDeps(c).initializeHistoryManager(c) if err != nil { return invariant.ManagerFixResult{}, err } defer historyV2Mgr.Close() pr := persistence.NewPersistenceRetryer( execManager, historyV2Mgr, common.CreatePersistenceRetryPolicy(), ) var ivs []invariant.Invariant for _, fn := range invariants { ivs = append(ivs, fn(pr, cache.NewNoOpDomainCache())) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return invariant.ManagerFixResult{}, err } invariantManager, err := getDeps(c).initializeInvariantManager(ivs) if err != nil { return invariant.ManagerFixResult{}, err } return invariantManager.RunFixes(ctx, execution.Execution.(entity.Entity)), nil } ================================================ FILE: tools/cli/admin_db_clean_command_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "flag" "fmt" "os" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/invariant" ) func TestAdminDBClean_noFixExecution(t *testing.T) { tests := []struct { name string setupContext func(app *cli.App) *cli.Context inputFileData string // Simulate the content of the input file expectedOutput string expectedError string }{ { name: "SuccessCase_emptyResult", setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) // Define flags set.String(FlagScanType, "", "scan type flag") set.String(FlagInputFile, "", "Input file flag") // Use a StringSliceFlag to simulate multiple collection values set.Var(cli.NewStringSlice("CollectionHistory", "CollectionDomain"), FlagInvariantCollection, "invariant collection flag") // Set actual values for the flags require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType")) require.NoError(t, set.Set(FlagInputFile, "input.json")) return cli.NewContext(app, set, nil) }, inputFileData: `[{"Execution": {"ShardID": 1}}]`, // Simulate the content of input file expectedOutput: ``, expectedError: "", }, { name: "MissingRequiredFlagScanType", setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) set.String(FlagInputFile, "", "Input file flag") // Missing FlagScanType _ = set.Set(FlagInputFile, "input.json") return cli.NewContext(app, set, nil) }, inputFileData: ``, expectedOutput: "", expectedError: "Required flag not found:", }, { name: "UnknownScanType", setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) set.String(FlagScanType, "", "scan type flag") set.String(FlagInputFile, "", "Input file flag") require.NoError(t, set.Set(FlagScanType, "unknown")) require.NoError(t, set.Set(FlagInputFile, "input.json")) return cli.NewContext(app, set, nil) }, inputFileData: ``, expectedOutput: "", expectedError: "unknown scan type", }, { name: "InvalidInvariantCollection", setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) // Define FlagScanType and FlagInputFile set.String(FlagScanType, "", "scan type flag") set.String(FlagInputFile, "", "Input file flag") // Simulate the collection slice with multiple collections (including an invalid one) set.Var(cli.NewStringSlice("invalid_collection", "history"), FlagInvariantCollection, "invariant collection flag") // Set actual values for the flags require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType")) require.NoError(t, set.Set(FlagInputFile, "input.json")) return cli.NewContext(app, set, nil) }, inputFileData: ``, expectedOutput: "", expectedError: "unknown invariant collection", }, { name: "NoInvariantsError", setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) set.String(FlagScanType, "", "scan type flag") set.String(FlagInvariantCollection, "", "invariant collection flag") set.String(FlagInputFile, "", "Input file flag") require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType")) require.NoError(t, set.Set(FlagInputFile, "input.json")) require.NoError(t, set.Set(FlagInvariantCollection, "invalid_collection")) return cli.NewContext(app, set, nil) }, inputFileData: `[{"Execution": {"ShardID": 1}}]`, expectedOutput: "", expectedError: "no invariants for scantype", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create temp input file with the test's input data inputFile, err := os.CreateTemp("", "test_input_*.json") assert.NoError(t, err) defer os.Remove(inputFile.Name()) // Clean up after test // Write input data to the temp file if tt.inputFileData != "" { _, err = inputFile.WriteString(tt.inputFileData) assert.NoError(t, err) } inputFile.Close() // Create test IO handler to capture output ioHandler := &testIOHandler{} // Set up the CLI app app := NewCliApp(nil, WithIOHandler(ioHandler)) // Set up the CLI context c := tt.setupContext(app) // Overwrite the FlagInputFile with the actual temp file path _ = c.Set(FlagInputFile, inputFile.Name()) // Call AdminDBClean and validate output and errors err = AdminDBClean(c) if tt.expectedError != "" { assert.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) } else { assert.NoError(t, err) } // Validate the captured output assert.Contains(t, ioHandler.outputBytes.String(), tt.expectedOutput) }) } } func TestAdminDBClean_inFixExecution(t *testing.T) { tests := []struct { name string contextSetup func(td *cliTestData, inputFilePath string) *cli.Context mockSetup func(td *cliTestData) inputFileData string expectedError string expectedOutput string }{ { name: "Success", contextSetup: func(td *cliTestData, inputFilePath string) *cli.Context { set := flag.NewFlagSet("test", 0) set.String(FlagScanType, "", "scan type flag") set.String(FlagInputFile, "", "Input file flag") set.Var(cli.NewStringSlice("CollectionHistory", "CollectionDomain"), FlagInvariantCollection, "invariant collection flag") require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType")) require.NoError(t, set.Set(FlagInputFile, inputFilePath)) return cli.NewContext(td.app, set, nil) }, mockSetup: func(td *cliTestData) { ctrl := gomock.NewController(t) mockExecManager := persistence.NewMockExecutionManager(ctrl) mockHistoryManager := persistence.NewMockHistoryManager(ctrl) mockInvariantManager := invariant.NewMockManager(ctrl) mockExecManager.EXPECT().Close().Times(1) mockHistoryManager.EXPECT().Close().Times(1) mockInvariantManager.EXPECT().RunFixes(gomock.Any(), gomock.Any()).Return(invariant.ManagerFixResult{}).Times(1) td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), gomock.Any()).Return(mockExecManager, nil).Times(1) td.mockManagerFactory.EXPECT().initializeHistoryManager(gomock.Any()).Return(mockHistoryManager, nil).Times(1) td.mockManagerFactory.EXPECT().initializeInvariantManager(gomock.Any()).Return(mockInvariantManager, nil).Times(1) }, inputFileData: `{"Execution": {"ShardID": 1}, "Result": {}}`, expectedOutput: `{"Execution":{"BranchToken":null,"TreeID":"","BranchID":"","ShardID":1,"DomainID":"","WorkflowID":"","RunID":"","State":0},"Input":{"Execution":{"BranchToken":null,"TreeID":"","BranchID":"","ShardID":1,"DomainID":"","WorkflowID":"","RunID":"","State":0},"Result":{"CheckResultType":"","DeterminingInvariantType":null,"CheckResults":null}},"Result":{"FixResultType":"","DeterminingInvariantName":null,"FixResults":null}} `, expectedError: "", }, { name: "init invariant manager error", contextSetup: func(td *cliTestData, inputFilePath string) *cli.Context { set := flag.NewFlagSet("test", 0) set.String(FlagScanType, "", "scan type flag") set.String(FlagInputFile, "", "Input file flag") set.Var(cli.NewStringSlice("CollectionHistory", "CollectionDomain"), FlagInvariantCollection, "invariant collection flag") require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType")) require.NoError(t, set.Set(FlagInputFile, inputFilePath)) return cli.NewContext(td.app, set, nil) }, mockSetup: func(td *cliTestData) { ctrl := gomock.NewController(t) mockExecManager := persistence.NewMockExecutionManager(ctrl) mockHistoryManager := persistence.NewMockHistoryManager(ctrl) mockExecManager.EXPECT().Close().Times(1) mockHistoryManager.EXPECT().Close().Times(1) td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), gomock.Any()).Return(mockExecManager, nil).Times(1) td.mockManagerFactory.EXPECT().initializeHistoryManager(gomock.Any()).Return(mockHistoryManager, nil).Times(1) td.mockManagerFactory.EXPECT().initializeInvariantManager(gomock.Any()).Return(nil, fmt.Errorf("init invariant manager error")).Times(1) }, inputFileData: `{"Execution": {"ShardID": 1}, "Result": {}}`, expectedOutput: ``, expectedError: "Error in fix execution: : init invariant manager error", }, { name: "init execution manager error", contextSetup: func(td *cliTestData, inputFilePath string) *cli.Context { set := flag.NewFlagSet("test", 0) set.String(FlagScanType, "", "scan type flag") set.String(FlagInputFile, "", "Input file flag") set.Var(cli.NewStringSlice("CollectionHistory", "CollectionDomain"), FlagInvariantCollection, "invariant collection flag") require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType")) require.NoError(t, set.Set(FlagInputFile, inputFilePath)) return cli.NewContext(td.app, set, nil) }, mockSetup: func(td *cliTestData) { td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("init execution manager error")).Times(1) }, inputFileData: `{"Execution": {"ShardID": 1}, "Result": {}}`, expectedOutput: ``, expectedError: "Error in fix execution: : init execution manager error", }, { name: "init history manager error", contextSetup: func(td *cliTestData, inputFilePath string) *cli.Context { set := flag.NewFlagSet("test", 0) set.String(FlagScanType, "", "scan type flag") set.String(FlagInputFile, "", "Input file flag") set.Var(cli.NewStringSlice("CollectionHistory", "CollectionDomain"), FlagInvariantCollection, "invariant collection flag") require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType")) require.NoError(t, set.Set(FlagInputFile, inputFilePath)) return cli.NewContext(td.app, set, nil) }, mockSetup: func(td *cliTestData) { ctrl := gomock.NewController(t) mockExecManager := persistence.NewMockExecutionManager(ctrl) mockExecManager.EXPECT().Close().Times(1) td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), gomock.Any()).Return(mockExecManager, nil).Times(1) td.mockManagerFactory.EXPECT().initializeHistoryManager(gomock.Any()).Return(nil, fmt.Errorf("init history manager error")).Times(1) }, inputFileData: `{"Execution": {"ShardID": 1}, "Result": {}}`, expectedOutput: ``, expectedError: "Error in fix execution: : init history manager error", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) tt.mockSetup(td) inputFile, err := os.CreateTemp("", "test_input_*.json") assert.NoError(t, err) defer os.Remove(inputFile.Name()) if tt.inputFileData != "" { _, err = inputFile.WriteString(tt.inputFileData) assert.NoError(t, err) } inputFile.Close() c := tt.contextSetup(td, inputFile.Name()) err = AdminDBClean(c) if tt.expectedError != "" { assert.ErrorContains(t, err, tt.expectedError) } else { assert.NoError(t, err) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } ================================================ FILE: tools/cli/admin_db_decode_thrift.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "bytes" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "strings" "github.com/davecgh/go-spew/spew" "github.com/urfave/cli/v2" "github.com/uber/cadence/.gen/go/config" "github.com/uber/cadence/.gen/go/history" "github.com/uber/cadence/.gen/go/replicator" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/.gen/go/sqlblobs" "github.com/uber/cadence/common/codec" "github.com/uber/cadence/tools/common/commoncli" ) var decodingTypes = map[string]func() codec.ThriftObject{ "shared.History": func() codec.ThriftObject { return &shared.History{} }, "shared.HistoryEvent": func() codec.ThriftObject { return &shared.HistoryEvent{} }, "shared.HistoryBranch": func() codec.ThriftObject { return &shared.HistoryBranch{} }, "shared.Memo": func() codec.ThriftObject { return &shared.Memo{} }, "shared.ResetPoints": func() codec.ThriftObject { return &shared.ResetPoints{} }, "shared.BadBinaries": func() codec.ThriftObject { return &shared.BadBinaries{} }, "shared.VersionHistories": func() codec.ThriftObject { return &shared.VersionHistories{} }, "replicator.FailoverMarkers": func() codec.ThriftObject { return &replicator.FailoverMarkers{} }, "history.ProcessingQueueStates": func() codec.ThriftObject { return &history.ProcessingQueueStates{} }, "config.DynamicConfigBlob": func() codec.ThriftObject { return &config.DynamicConfigBlob{} }, "sqlblobs.ShardInfo": func() codec.ThriftObject { return &sqlblobs.ShardInfo{} }, "sqlblobs.DomainInfo": func() codec.ThriftObject { return &sqlblobs.DomainInfo{} }, "sqlblobs.HistoryTreeInfo": func() codec.ThriftObject { return &sqlblobs.HistoryTreeInfo{} }, "sqlblobs.WorkflowExecutionInfo": func() codec.ThriftObject { return &sqlblobs.WorkflowExecutionInfo{} }, "sqlblobs.ActivityInfo": func() codec.ThriftObject { return &sqlblobs.ActivityInfo{} }, "sqlblobs.ChildExecutionInfo": func() codec.ThriftObject { return &sqlblobs.ChildExecutionInfo{} }, "sqlblobs.SignalInfo": func() codec.ThriftObject { return &sqlblobs.SignalInfo{} }, "sqlblobs.RequestCancelInfo": func() codec.ThriftObject { return &sqlblobs.RequestCancelInfo{} }, "sqlblobs.TimerInfo": func() codec.ThriftObject { return &sqlblobs.TimerInfo{} }, "sqlblobs.TaskInfo": func() codec.ThriftObject { return &sqlblobs.TaskInfo{} }, "sqlblobs.TaskListInfo": func() codec.ThriftObject { return &sqlblobs.TaskListInfo{} }, "sqlblobs.TransferTaskInfo": func() codec.ThriftObject { return &sqlblobs.TransferTaskInfo{} }, "sqlblobs.TimerTaskInfo": func() codec.ThriftObject { return &sqlblobs.TimerTaskInfo{} }, "sqlblobs.ReplicationTaskInfo": func() codec.ThriftObject { return &sqlblobs.ReplicationTaskInfo{} }, "shared.AsyncWorkflowConfiguration": func() codec.ThriftObject { return &shared.AsyncWorkflowConfiguration{} }, "shared.ActiveClusters": func() codec.ThriftObject { return &shared.ActiveClusters{} }, "shared.ActiveClusterSelectionPolicy": func() codec.ThriftObject { return &shared.ActiveClusterSelectionPolicy{} }, } type decodeError struct { shortMsg string err error } // AdminDBDataDecodeThrift is the command to decode thrift binary into JSON func AdminDBDataDecodeThrift(c *cli.Context) error { input, err := getRequiredOption(c, FlagInput) if err != nil { return commoncli.Problem("Required flag not found", err) } encoding := c.String(FlagInputEncoding) data, err := decodeUserInput(input, encoding) if err != nil { return commoncli.Problem("failed to decode input", err) } if _, err := decodeThriftPayload(data); err != nil { return commoncli.Problem("failed to decode thrift payload", err.err) } return nil } func decodeThriftPayload(data []byte) (codec.ThriftObject, *decodeError) { encoder := codec.NewThriftRWEncoder() // this is an inconsistency in the code base, some place use ThriftRWEncoder(version0Thriftrw.go) some use thriftEncoder(thrift_encoder.go) dataWithPrepend := []byte{0x59} dataWithPrepend = append(dataWithPrepend, data...) datas := [][]byte{data, dataWithPrepend} for _, data := range datas { for typeName, objFn := range decodingTypes { t := objFn() if err := encoder.Decode(data, t); err != nil { continue } // encoding back to confirm data2, err := encoder.Encode(t) if err != nil { return nil, &decodeError{ shortMsg: "cannot encode back to confirm", err: err, } } if !bytes.Equal(data, data2) { continue } fmt.Printf("======= Decode into type %v ========\n", typeName) spew.Dump(t) // json-ify it for easier mechanical use js, err := json.Marshal(t) if err == nil { fmt.Println("======= As JSON ========") fmt.Println(string(js)) } return t, nil } } return nil, &decodeError{ shortMsg: "input data cannot be decoded into any struct", err: nil, } } func decodeUserInput(input, encoding string) ([]byte, error) { switch encoding { case "", "hex": // remove "0x" from the beginning of the input. hex library doesn't expect it but that's how it's printed it out by csql input = strings.TrimPrefix(input, "0x") return hex.DecodeString(input) case "base64": return base64.StdEncoding.DecodeString(input) } return nil, fmt.Errorf("unknown input encoding: %s", encoding) } ================================================ FILE: tools/cli/admin_db_decode_thrift_test.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "testing" "github.com/google/go-cmp/cmp" "github.com/uber/cadence/.gen/go/history" "github.com/uber/cadence/.gen/go/shared" "github.com/uber/cadence/.gen/go/sqlblobs" "github.com/uber/cadence/common" "github.com/uber/cadence/common/codec" ) func TestThriftDecodeHelper(t *testing.T) { tests := []struct { desc string input string encoding string wantObjFn func(*testing.T) codec.ThriftObject wantErr bool }{ { desc: "invalid base64 input", input: "not-a-valid-base64", encoding: "base64", wantErr: true, }, { desc: "invalid hex input", input: "not-a-valid-hex", encoding: "hex", wantErr: true, }, { desc: "VersionHistories hex with '0x' prefix", input: "0x5908000a000000000f00140c000000010b000a0000004c590b000a000000067472656569640b0014000000086272616e636869640f001e0c000000010b000a000000086272616e636869640a001400000000000000010a001e000000000000006400000000", encoding: "hex", wantObjFn: generateTestVersionHistories, }, { desc: "VersionHistories hex without '0x' prefix", input: "5908000a000000000f00140c000000010b000a0000004c590b000a000000067472656569640b0014000000086272616e636869640f001e0c000000010b000a000000086272616e636869640a001400000000000000010a001e000000000000006400000000", encoding: "hex", wantObjFn: generateTestVersionHistories, }, { desc: "VersionHistories base64", input: "WQgACgAAAAAPABQMAAAAAQsACgAAAExZCwAKAAAABnRyZWVpZAsAFAAAAAhicmFuY2hpZA8AHgwAAAABCwAKAAAACGJyYW5jaGlkCgAUAAAAAAAAAAEKAB4AAAAAAAAAZAAAAAA=", encoding: "base64", wantObjFn: generateTestVersionHistories, }, { desc: "ResetPoints hex", input: "590f000a0c000000010b000a00000008636865636b73756d0b00140000000572756e69640a001e00000000000000010a002800000000000000010a0032000000000000000102003c010000", encoding: "hex", wantObjFn: generateTestResetPoints, }, { desc: "ProcessingQueueStates hex", input: "590d000a0b0f0000000100000008636c7573746572310c0000000208000a000000000a001400000000000003e80a001e00000000000007d00c00280f000a0b0000000100000006646f6d61696e000008000a000000010a0014000000000000012c0a001e00000000000001900c00280f000a0b0000000100000006646f6d61696e000000", encoding: "hex", wantObjFn: generateTestProcessingQueueStates, }, { desc: "DomainInfo hex", input: "590b000a000000046e616d650b000c0000000b6465736372697074696f6e0b000e000000056f776e65720800100000000306001200070d00260b0b0000000100000007646174616b6579000000096461746176616c756500", encoding: "hex", wantObjFn: generateTestDomainInfo, }, { desc: "HistoryTreeInfo hex", input: "590a000a0000000218711a000f000c0c000000010b000a000000086272616e636869640a001400000000000000010a001e0000000000000064000b000e00000009736f6d6520696e666f00", encoding: "hex", wantObjFn: generateTestHistoryTreeInfo, }, { desc: "TimerInfo hex", input: "590a000a00000000000000010a000c00000000000000010a000e00000000000003e80a0010000000000000000500", encoding: "hex", wantObjFn: generateTestTimerInfo, }, { desc: "Active clusters config", input: "0x590d000b0b0c00000001000000086c6f636174696f6e0d000a0b0c00000001000000066c6f6e646f6e0b000a00000008636c7573746572320a00140000000000000002000000", encoding: "hex", wantObjFn: generateTestActiveClustersConfig, }, { desc: "Active cluster selection policy", input: "5908000a000000000b001400000007726567696f6e300b001e000000000b00280000000000", encoding: "hex", wantObjFn: generateTestActiveClusterSelectionPolicy, }, } for _, tc := range tests { tc := tc t.Run(tc.desc, func(t *testing.T) { t.Parallel() data, err := decodeUserInput(tc.input, tc.encoding) if (err != nil) != tc.wantErr { t.Fatalf("decodeUserInput() error: %v, wantErr: %v", err, tc.wantErr) } if err != nil { return } gotObj, decodeErr := decodeThriftPayload(data) if (decodeErr != nil) != tc.wantErr { t.Fatalf("decodeUserInput() error: %v, wantErr: %v", decodeErr, tc.wantErr) } if decodeErr != nil { return } want := tc.wantObjFn(t) if diff := cmp.Diff(want, gotObj); diff != "" { t.Fatalf("Object mismatch (-want +got):\n%s", diff) } }) } } func mustThriftEncode(t *testing.T, obj codec.ThriftObject) []byte { t.Helper() data, err := codec.NewThriftRWEncoder().Encode(obj) if err != nil { t.Fatalf("Failed to encode thrift obj, err: %v", err) } return data } func generateTestVersionHistories(t *testing.T) codec.ThriftObject { t.Helper() historyBranch := &shared.HistoryBranch{ TreeID: common.StringPtr("treeid"), BranchID: common.StringPtr("branchid"), Ancestors: []*shared.HistoryBranchRange{ { BranchID: common.StringPtr("branchid"), BeginNodeID: common.Int64Ptr(1), EndNodeID: common.Int64Ptr(100), }, }, } return &shared.VersionHistories{ CurrentVersionHistoryIndex: common.Int32Ptr(0), Histories: []*shared.VersionHistory{ { BranchToken: mustThriftEncode(t, historyBranch), }, }, } } func generateTestResetPoints(t *testing.T) codec.ThriftObject { t.Helper() return &shared.ResetPoints{ Points: []*shared.ResetPointInfo{ { BinaryChecksum: common.StringPtr("checksum"), RunId: common.StringPtr("runid"), FirstDecisionCompletedId: common.Int64Ptr(1), CreatedTimeNano: common.Int64Ptr(1), Resettable: common.BoolPtr(true), ExpiringTimeNano: common.Int64Ptr(1), }, }, } } func generateTestProcessingQueueStates(t *testing.T) codec.ThriftObject { t.Helper() return &history.ProcessingQueueStates{ StatesByCluster: map[string][]*history.ProcessingQueueState{ "cluster1": { { Level: common.Int32Ptr(0), AckLevel: common.Int64Ptr(1000), MaxLevel: common.Int64Ptr(2000), DomainFilter: &history.DomainFilter{ DomainIDs: []string{"domain"}, }, }, { Level: common.Int32Ptr(1), AckLevel: common.Int64Ptr(300), MaxLevel: common.Int64Ptr(400), DomainFilter: &history.DomainFilter{ DomainIDs: []string{"domain"}, }, }, }, }, } } func generateTestDomainInfo(t *testing.T) codec.ThriftObject { t.Helper() return &sqlblobs.DomainInfo{ Name: common.StringPtr("name"), Owner: common.StringPtr("owner"), Status: common.Int32Ptr(3), RetentionDays: common.Int16Ptr(7), Description: common.StringPtr("description"), Data: map[string]string{ "datakey": "datavalue", }, } } func generateTestHistoryTreeInfo(t *testing.T) codec.ThriftObject { t.Helper() return &sqlblobs.HistoryTreeInfo{ CreatedTimeNanos: common.Int64Ptr(9000000000), Ancestors: []*shared.HistoryBranchRange{ { BranchID: common.StringPtr("branchid"), BeginNodeID: common.Int64Ptr(1), EndNodeID: common.Int64Ptr(100), }, }, Info: common.StringPtr("some info"), } } func generateTestTimerInfo(t *testing.T) codec.ThriftObject { t.Helper() return &sqlblobs.TimerInfo{ Version: common.Int64Ptr(1), StartedID: common.Int64Ptr(1), ExpiryTimeNanos: common.Int64Ptr(1000), TaskID: common.Int64Ptr(5), } } func generateTestActiveClusterSelectionPolicy(t *testing.T) codec.ThriftObject { t.Helper() return &shared.ActiveClusterSelectionPolicy{ Strategy: shared.ActiveClusterSelectionStrategyRegionSticky.Ptr(), StickyRegion: common.StringPtr("region0"), ExternalEntityType: common.StringPtr(""), ExternalEntityKey: common.StringPtr(""), } } func generateTestActiveClustersConfig(t *testing.T) codec.ThriftObject { t.Helper() return &shared.ActiveClusters{ ActiveClustersByClusterAttribute: map[string]*shared.ClusterAttributeScope{ "location": &shared.ClusterAttributeScope{ ClusterAttributes: map[string]*shared.ActiveClusterInfo{ "london": { ActiveClusterName: common.Ptr("cluster2"), FailoverVersion: common.Int64Ptr(2), }, }, }, }, } } ================================================ FILE: tools/cli/admin_db_scan_command.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "context" "encoding/json" "fmt" "io" "os" "time" "github.com/urfave/cli/v2" "go.uber.org/zap" "github.com/uber/cadence/common" "github.com/uber/cadence/common/cache" "github.com/uber/cadence/common/collection" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/reconciliation/fetcher" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/common/reconciliation/store" "github.com/uber/cadence/service/worker/scanner/executions" "github.com/uber/cadence/tools/common/commoncli" ) const ( listContextTimeout = time.Minute ) // AdminDBScan is used to scan over executions in database and detect corruptions. func AdminDBScan(c *cli.Context) error { scanType, err := executions.ScanTypeString(c.String(FlagScanType)) if err != nil { return commoncli.Problem("unknown scan type", err) } numberOfShards, err := getRequiredIntOption(c, FlagNumberOfShards) if err != nil { return commoncli.Problem("Required flag not found", err) } collectionSlice := c.StringSlice(FlagInvariantCollection) var collections []invariant.Collection for _, v := range collectionSlice { collection, err := invariant.CollectionString(v) if err != nil { return commoncli.Problem("unknown invariant collection", err) } collections = append(collections, collection) } logger := zap.NewNop() if c.Bool(FlagVerbose) { logger, err = zap.NewDevelopment() if err != nil { // probably impossible with default config return commoncli.Problem("could not construct logger", err) } } invariants := scanType.ToInvariants(collections, logger) if len(invariants) < 1 { return commoncli.Problem( fmt.Sprintf("no invariants for scan type %q and collections %q", scanType.String(), collectionSlice), nil, ) } ef := scanType.ToExecutionFetcher() input, err := getInputFile(c.String(FlagInputFile)) if err != nil { return commoncli.Problem("Input file not found", err) } dec := json.NewDecoder(input) if err != nil { return commoncli.Problem("", err) } var data []fetcher.ExecutionRequest for { var exec fetcher.ExecutionRequest if err := dec.Decode(&exec); err != nil { if err == io.EOF || err == io.ErrUnexpectedEOF { break } return commoncli.Problem("Error decoding input file", err) } data = append(data, exec) } if len(data) == 0 { return commoncli.Problem("Input file contained no data to scan", nil) } for _, e := range data { execution, result, err := checkExecution(c, numberOfShards, e, invariants, ef) if err != nil { return commoncli.Problem("Execution check failed", err) } out := store.ScanOutputEntity{ Execution: execution, Result: result, } data, err := json.Marshal(out) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) continue } getDeps(c).Output().Write(data) } return nil } func checkExecution( c *cli.Context, numberOfShards int, req fetcher.ExecutionRequest, invariants []executions.InvariantFactory, fetcher executions.ExecutionFetcher, ) (interface{}, invariant.ManagerCheckResult, error) { execManager, err := getDeps(c).initializeExecutionManager(c, common.WorkflowIDToHistoryShard(req.WorkflowID, numberOfShards)) if err != nil { return nil, invariant.ManagerCheckResult{}, fmt.Errorf("initialize execution manager: %w", err) } defer execManager.Close() historyV2Mgr, err := getDeps(c).initializeHistoryManager(c) if err != nil { return nil, invariant.ManagerCheckResult{}, fmt.Errorf("initialize history manager: %w", err) } defer historyV2Mgr.Close() pr := persistence.NewPersistenceRetryer( execManager, historyV2Mgr, common.CreatePersistenceRetryPolicy(), ) ctx, cancel, err := newContext(c) if err != nil { return nil, invariant.ManagerCheckResult{}, fmt.Errorf("Error in creating context: %w", err) } defer cancel() execution, err := fetcher(ctx, pr, req) if err != nil { return nil, invariant.ManagerCheckResult{}, fmt.Errorf("fetching execution: %w", err) } var ivs []invariant.Invariant for _, fn := range invariants { ivs = append(ivs, fn(pr, cache.NewNoOpDomainCache())) } return execution, invariant.NewInvariantManager(ivs).RunChecks(ctx, execution), nil } // AdminDBScanUnsupportedWorkflow is to scan DB for unsupported workflow for a new release func AdminDBScanUnsupportedWorkflow(c *cli.Context) error { outputFile, err := getOutputFile(c.String(FlagOutputFilename)) if err != nil { return commoncli.Problem("Error in admin db scan unsupported wf: ", err) } startShardID := c.Int(FlagLowerShardBound) endShardID := c.Int(FlagUpperShardBound) defer outputFile.Close() for i := startShardID; i <= endShardID; i++ { if err := listExecutionsByShardID(c, i, outputFile); err != nil { return err } fmt.Printf("Shard %v scan operation is completed.\n", i) } return nil } func listExecutionsByShardID( c *cli.Context, shardID int, outputFile *os.File, ) error { client, err := getDeps(c).initializeExecutionManager(c, shardID) if err != nil { commoncli.Problem("initialize execution manager:", err) } defer client.Close() paginationFunc := func(paginationToken []byte) ([]interface{}, []byte, error) { ctx, cancel := context.WithTimeout(c.Context, listContextTimeout) defer cancel() resp, err := client.ListConcreteExecutions( ctx, &persistence.ListConcreteExecutionsRequest{ PageSize: 1000, PageToken: paginationToken, }, ) if err != nil { return nil, nil, err } var paginateItems []interface{} for _, history := range resp.Executions { paginateItems = append(paginateItems, history) } return paginateItems, resp.PageToken, nil } executionIterator := collection.NewPagingIterator(paginationFunc) for executionIterator.HasNext() { result, err := executionIterator.Next() if err != nil { return commoncli.Problem(fmt.Sprintf("Failed to scan shard ID: %v for unsupported workflow. Please retry.", shardID), err) } execution := result.(*persistence.ListConcreteExecutionsEntity) executionInfo := execution.ExecutionInfo if executionInfo != nil && executionInfo.CloseStatus == 0 && execution.VersionHistories == nil { outStr := fmt.Sprintf("cadence --address : --domain <%v> workflow reset --wid %v --rid %v --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade'\n", executionInfo.DomainID, executionInfo.WorkflowID, executionInfo.RunID, ) if _, err = outputFile.WriteString(outStr); err != nil { return commoncli.Problem("Failed to write data to file", err) } if err = outputFile.Sync(); err != nil { return commoncli.Problem("Failed to sync data to file", err) } } } return nil } ================================================ FILE: tools/cli/admin_db_scan_command_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "fmt" "os" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/tools/cli/clitest" ) func TestAdminDBScanErrorCases(t *testing.T) { cases := []struct { name string testSetup func(td *cliTestData) *cli.Context errContains string // empty if no error is expected }{ { name: "scan type not provided", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app) }, errContains: "unknown scan type", }, { name: "unknown scan type provided", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "some_unknown_scan_type"), ) }, errContains: "unknown scan type", }, { name: "number of shards not provided", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "ConcreteExecutionType"), ) }, errContains: "Required flag not found", }, { name: "invariant collection not provided", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "ConcreteExecutionType"), clitest.IntArgument("number_of_shards", 16384), ) }, errContains: "no invariants for scan type \"ConcreteExecutionType\" and collections []", }, { name: "invalid invariant collection provided", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "ConcreteExecutionType"), clitest.IntArgument("number_of_shards", 16384), clitest.StringSliceArgument("invariant_collection", "some_unknown_invariant_collection"), ) }, errContains: "unknown invariant collection: some_unknown_invariant_collection", }, { name: "input file not found", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "ConcreteExecutionType"), clitest.IntArgument("number_of_shards", 16384), clitest.StringSliceArgument("invariant_collection", "CollectionHistory"), clitest.StringArgument("input_file", "testdata/non-existant-file.json"), ) }, errContains: "Input file not found", }, { name: "input file is empty", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "ConcreteExecutionType"), clitest.IntArgument("number_of_shards", 16384), clitest.StringSliceArgument("invariant_collection", "CollectionHistory"), clitest.StringArgument("input_file", "testdata/scan_input_empty.json"), ) }, errContains: "Input file contained no data to scan", }, { name: "bad data in input file", testSetup: func(td *cliTestData) *cli.Context { return clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "ConcreteExecutionType"), clitest.IntArgument("number_of_shards", 16384), clitest.StringSliceArgument("invariant_collection", "CollectionHistory"), clitest.StringArgument("input_file", "testdata/scan_input_bad_data.json"), ) }, errContains: "Error decoding input file", }, { name: "execution manager initialization error", testSetup: func(td *cliTestData) *cli.Context { td.mockManagerFactory.EXPECT(). initializeExecutionManager(gomock.Any(), gomock.Any()). Return(nil, assert.AnError) return clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "ConcreteExecutionType"), clitest.IntArgument("number_of_shards", 16384), clitest.StringSliceArgument("invariant_collection", "CollectionHistory"), clitest.StringArgument("input_file", "testdata/scan_input.json"), ) }, errContains: "Execution check failed: initialize execution manager: assert.AnError general error for testing", }, { name: "historyV2 manager initialization error", testSetup: func(td *cliTestData) *cli.Context { shardID1 := common.WorkflowIDToHistoryShard("test-workflow-id1", 16384) mockExecutionManager := persistence.NewMockExecutionManager(td.ctrl) mockExecutionManager.EXPECT().Close() td.mockManagerFactory.EXPECT(). initializeExecutionManager(gomock.Any(), shardID1). Return(mockExecutionManager, nil) td.mockManagerFactory.EXPECT(). initializeHistoryManager(gomock.Any()). Return(nil, assert.AnError) return clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "ConcreteExecutionType"), clitest.IntArgument("number_of_shards", 16384), clitest.StringSliceArgument("invariant_collection", "CollectionHistory"), clitest.StringArgument("input_file", "testdata/scan_input.json"), ) }, errContains: "Execution check failed: initialize history manager: assert.AnError general error for testing", }, { name: "failed to fetch execution", testSetup: func(td *cliTestData) *cli.Context { shardID1 := common.WorkflowIDToHistoryShard("test-workflow-id1", 16384) mockExecutionManager := persistence.NewMockExecutionManager(td.ctrl) mockExecutionManager.EXPECT().Close() td.mockManagerFactory.EXPECT(). initializeExecutionManager(gomock.Any(), shardID1). Return(mockExecutionManager, nil) mockHistoryManager := persistence.NewMockHistoryManager(td.ctrl) mockHistoryManager.EXPECT().Close() td.mockManagerFactory.EXPECT(). initializeHistoryManager(gomock.Any()). Return(mockHistoryManager, nil) mockExecutionManager.EXPECT().GetWorkflowExecution(gomock.Any(), gomock.Any()). Return(nil, assert.AnError) return clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "ConcreteExecutionType"), clitest.IntArgument("number_of_shards", 16384), clitest.StringSliceArgument("invariant_collection", "CollectionHistory"), clitest.StringArgument("input_file", "testdata/scan_input.json"), ) }, errContains: "Execution check failed: fetching execution: assert.AnError general error for testing", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { td := newCLITestData(t) testCtx := tc.testSetup(td) err := AdminDBScan(testCtx) if tc.errContains != "" { assert.Error(t, err) assert.ErrorContains(t, err, tc.errContains) } else { assert.NoError(t, err) } }) } } func TestAdminDBScan(t *testing.T) { td := newCLITestData(t) expectWorkFlow(td, "test-workflow-id1") expectWorkFlow(td, "test-workflow-id2") expectWorkFlow(td, "test-workflow-id3") cliCtx := clitest.NewCLIContext(t, td.app, clitest.StringArgument("scan_type", "CurrentExecutionType"), clitest.IntArgument("number_of_shards", 16384), clitest.StringSliceArgument("invariant_collection", "CollectionMutableState"), clitest.StringArgument("input_file", "testdata/scan_input.json"), ) err := AdminDBScan(cliCtx) assert.NoError(t, err) assert.Equal(t, expectedAdminDBScanOutput, td.ioHandler.outputBytes.String()) } // The expected output does not have any newlines or tabs // so we use strings.Join(strings.Fields()) to remove them var expectedAdminDBScanOutput = strings.Join(strings.Fields(` { "Execution":{ "CurrentRunID":"test-run-id1", "ShardID":8946, "DomainID":"test-domain-id1", "WorkflowID":"test-workflow-id1", "RunID":"test-run-id1", "State":2 }, "Result":{ "CheckResultType":"healthy", "DeterminingInvariantType":null, "CheckResults":[{"CheckResultType":"healthy","InvariantName":"concrete_execution_exists","Info":"","InfoDetails":""}] } }{ "Execution":{ "CurrentRunID":"test-run-id1", "ShardID":14767, "DomainID":"test-domain-id2", "WorkflowID":"test-workflow-id2", "RunID":"test-run-id1", "State":2 }, "Result":{ "CheckResultType":"healthy", "DeterminingInvariantType":null, "CheckResults":[{"CheckResultType":"healthy","InvariantName":"concrete_execution_exists","Info":"","InfoDetails":""}] } }{ "Execution":{ "CurrentRunID":"test-run-id1", "ShardID":14582, "DomainID":"test-domain-id3", "WorkflowID":"test-workflow-id3", "RunID":"test-run-id1","State":2 }, "Result":{ "CheckResultType":"healthy", "DeterminingInvariantType":null, "CheckResults":[{"CheckResultType":"healthy","InvariantName":"concrete_execution_exists","Info":"","InfoDetails":""}] } }`, ), "") func expectWorkFlow(td *cliTestData, workflowID string) { shardID1 := common.WorkflowIDToHistoryShard(workflowID, 16384) mockExecutionManager := persistence.NewMockExecutionManager(td.ctrl) mockExecutionManager.EXPECT().Close().Times(1) td.mockManagerFactory.EXPECT(). initializeExecutionManager(gomock.Any(), shardID1). Return(mockExecutionManager, nil). Times(1) mockHistoryManager := persistence.NewMockHistoryManager(td.ctrl) mockHistoryManager.EXPECT().Close().Times(1) td.mockManagerFactory.EXPECT(). initializeHistoryManager(gomock.Any()). Return(mockHistoryManager, nil). Times(1) mockExecutionManager.EXPECT().GetCurrentExecution(gomock.Any(), gomock.Any()). Return(&persistence.GetCurrentExecutionResponse{ RunID: "test-run-id1", State: persistence.WorkflowStateCompleted, }, nil). Times(1) mockExecutionManager.EXPECT().GetShardID().Return(shardID1).Times(1) mockExecutionManager.EXPECT().IsWorkflowExecutionExists(gomock.Any(), gomock.Any()). Return(&persistence.IsWorkflowExecutionExistsResponse{ Exists: true, }, nil). Times(1) } func TestAdminDBScanUnsupportedWorkflow(t *testing.T) { td := newCLITestData(t) outPutFile := createTempFileWithContent(t, "") expectShard(td, 123) expectShard(td, 124) expectShard(td, 125) cliCtx := clitest.NewCLIContext(t, td.app, clitest.StringArgument("output_filename", outPutFile), clitest.IntArgument("lower_shard_bound", 123), clitest.IntArgument("upper_shard_bound", 125), ) err := AdminDBScanUnsupportedWorkflow(cliCtx) assert.NoError(t, err) actual, err := os.ReadFile(outPutFile) require.NoError(t, err) assert.Equal(t, expectedAdminDBScanUnsupportedOutput, string(actual)) } func expectShard(td *cliTestData, shardID int) { mockExecutionManager := persistence.NewMockExecutionManager(td.ctrl) mockExecutionManager.EXPECT().Close().Times(1) // Return 2 executions in the first call and a page token mockExecutionManager.EXPECT().ListConcreteExecutions(gomock.Any(), &persistence.ListConcreteExecutionsRequest{ PageSize: 1000, PageToken: nil, }, ). Return(&persistence.ListConcreteExecutionsResponse{ Executions: []*persistence.ListConcreteExecutionsEntity{ createListConcreteExecutionsEntity(1, shardID), createListConcreteExecutionsEntity(2, shardID), }, PageToken: []byte("some-next-page-token"), }, nil).Times(1) // Return 1 execution in the second call and no page token mockExecutionManager.EXPECT().ListConcreteExecutions(gomock.Any(), &persistence.ListConcreteExecutionsRequest{ PageSize: 1000, PageToken: []byte("some-next-page-token"), }, ). Return(&persistence.ListConcreteExecutionsResponse{ Executions: []*persistence.ListConcreteExecutionsEntity{ createListConcreteExecutionsEntity(3, shardID), }, PageToken: nil, }, nil).Times(1) td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), shardID). Return(mockExecutionManager, nil).Times(1) } func createListConcreteExecutionsEntity(number int, shardID int) *persistence.ListConcreteExecutionsEntity { return &persistence.ListConcreteExecutionsEntity{ ExecutionInfo: &persistence.WorkflowExecutionInfo{ DomainID: fmt.Sprintf("%d-test-domain-id%d", shardID, number), WorkflowID: fmt.Sprintf("%d-test-workflow-id%d", shardID, number), RunID: fmt.Sprintf("%d-test-run-id%d", shardID, number), }, VersionHistories: nil, } } const expectedAdminDBScanUnsupportedOutput = `cadence --address : --domain <123-test-domain-id1> workflow reset --wid 123-test-workflow-id1 --rid 123-test-run-id1 --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade' cadence --address : --domain <123-test-domain-id2> workflow reset --wid 123-test-workflow-id2 --rid 123-test-run-id2 --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade' cadence --address : --domain <123-test-domain-id3> workflow reset --wid 123-test-workflow-id3 --rid 123-test-run-id3 --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade' cadence --address : --domain <124-test-domain-id1> workflow reset --wid 124-test-workflow-id1 --rid 124-test-run-id1 --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade' cadence --address : --domain <124-test-domain-id2> workflow reset --wid 124-test-workflow-id2 --rid 124-test-run-id2 --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade' cadence --address : --domain <124-test-domain-id3> workflow reset --wid 124-test-workflow-id3 --rid 124-test-run-id3 --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade' cadence --address : --domain <125-test-domain-id1> workflow reset --wid 125-test-workflow-id1 --rid 125-test-run-id1 --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade' cadence --address : --domain <125-test-domain-id2> workflow reset --wid 125-test-workflow-id2 --rid 125-test-run-id2 --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade' cadence --address : --domain <125-test-domain-id3> workflow reset --wid 125-test-workflow-id3 --rid 125-test-run-id3 --reset_type LastDecisionCompleted --reason 'release 0.16 upgrade' ` ================================================ FILE: tools/cli/admin_dlq_commands.go ================================================ // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "bufio" "fmt" "io" "os" "sort" "strconv" "strings" "time" "github.com/urfave/cli/v2" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/common/commoncli" ) const ( defaultPageSize = 1000 ) type DLQRow struct { ShardID int `header:"Shard ID" json:"shardID"` DomainName string `header:"Domain Name" json:"domainName"` DomainID string `header:"Domain ID" json:"domainID"` WorkflowID string `header:"Workflow ID" json:"workflowID"` RunID string `header:"Run ID" json:"runID"` TaskID int64 `header:"Task ID" json:"taskID"` TaskType *types.ReplicationTaskType `header:"Task Type" json:"taskType"` Version int64 `json:"version"` FirstEventID int64 `json:"firstEventID"` NextEventID int64 `json:"nextEventID"` ScheduledID int64 `json:"scheduledID"` ReplicationTask *types.ReplicationTask `json:"replicationTask"` // Those are deserialized variants from history replications task Events []*types.HistoryEvent `json:"events"` NewRunEvents []*types.HistoryEvent `json:"newRunEvents,omitempty"` // Only event IDs for compact table representation EventIDs []int64 `header:"Event IDs"` NewRunEventIDs []int64 `header:"New Run Event IDs"` } type HistoryDLQCountRow struct { SourceCluster string `header:"Source Cluster" json:"sourceCluster"` ShardID int32 `header:"Shard ID" json:"shardID"` Count int64 `header:"Count" json:"count"` } // AdminCountDLQMessages returns info how many and where DLQ messages are queued func AdminCountDLQMessages(c *cli.Context) error { force := c.Bool(FlagForce) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } response, err := adminClient.CountDLQMessages(ctx, &types.CountDLQMessagesRequest{ForceFetch: force}) if err != nil { return fmt.Errorf("Error occurred while getting DLQ count, results may be partial: %w", err) } if c.String(FlagDLQType) == "domain" { fmt.Println(response.Domain) return nil } table := []HistoryDLQCountRow{} for key, count := range response.History { table = append(table, HistoryDLQCountRow{ SourceCluster: key.SourceCluster, ShardID: key.ShardID, Count: count, }) } sort.Slice(table, func(i, j int) bool { // First sort by source cluster switch strings.Compare(table[i].SourceCluster, table[j].SourceCluster) { case -1: return true case 1: return false } // Then by count in decreasing order diff := table[i].Count - table[j].Count if diff > 0 { return true } if diff < 0 { return false } // Finally by shard in increasing order return table[i].ShardID < table[j].ShardID }) return Render(c, table, RenderOptions{Color: true, DefaultTemplate: templateTable}) } // AdminGetDLQMessages gets DLQ metadata func AdminGetDLQMessages(c *cli.Context) error { ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context:", err) } client, err := getDeps(c).ServerFrontendClient(c) if err != nil { return err } adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } fdlqtype, err := getRequiredOption(c, FlagDLQType) if err != nil { return commoncli.Problem("Required flag not found", err) } dlqType, err := toQueueType(fdlqtype) if err != nil { return commoncli.Problem("Failed to convert queue type", err) } sourceCluster, err := getRequiredOption(c, FlagSourceCluster) if err != nil { return commoncli.Problem("Required flag not found", err) } remainingMessageCount := constants.InclusiveEndMessageID if c.IsSet(FlagMaxMessageCount) { remainingMessageCount = c.Int64(FlagMaxMessageCount) } lastMessageID := constants.InclusiveEndMessageID if c.IsSet(FlagLastMessageID) { lastMessageID = c.Int64(FlagLastMessageID) } // Cache for domain names domainNames := map[string]string{} getDomainName := func(domainId string) (string, error) { if domainName, ok := domainNames[domainId]; ok { return domainName, nil } resp, err := client.DescribeDomain(ctx, &types.DescribeDomainRequest{UUID: common.StringPtr(domainId)}) if err != nil { return "", commoncli.Problem("failed to describe domain", err) } domainNames[domainId] = resp.DomainInfo.Name return resp.DomainInfo.Name, nil } readShard := func(shardID int) ([]DLQRow, error) { var rows []DLQRow var pageToken []byte for { resp, err := adminClient.ReadDLQMessages(ctx, &types.ReadDLQMessagesRequest{ Type: dlqType, SourceCluster: sourceCluster, ShardID: int32(shardID), InclusiveEndMessageID: common.Int64Ptr(lastMessageID), MaximumPageSize: defaultPageSize, NextPageToken: pageToken, }) if err != nil { return nil, commoncli.Problem(fmt.Sprintf("fail to read dlq message for shard: %d", shardID), err) } replicationTasks := map[int64]*types.ReplicationTask{} for _, task := range resp.ReplicationTasks { replicationTasks[task.SourceTaskID] = task } for _, info := range resp.ReplicationTasksInfo { task := replicationTasks[info.TaskID] var taskType *types.ReplicationTaskType if task != nil { taskType = task.TaskType } events, err := deserializeBatchEvents(task.GetHistoryTaskV2Attributes().GetEvents()) if err != nil { return nil, fmt.Errorf("Error in deserializing batch events: %w", err) } newRunEvents, err := deserializeBatchEvents(task.GetHistoryTaskV2Attributes().GetNewRunEvents()) if err != nil { return nil, fmt.Errorf("Error in deserializing new run batch events: %w", err) } domainName, err := getDomainName(info.DomainID) if err != nil { return nil, err } rows = append(rows, DLQRow{ ShardID: shardID, DomainName: domainName, DomainID: info.DomainID, WorkflowID: info.WorkflowID, RunID: info.RunID, TaskType: taskType, TaskID: info.TaskID, Version: info.Version, FirstEventID: info.FirstEventID, NextEventID: info.NextEventID, ScheduledID: info.ScheduledID, ReplicationTask: task, Events: events, EventIDs: collectEventIDs(events), NewRunEvents: newRunEvents, NewRunEventIDs: collectEventIDs(newRunEvents), }) remainingMessageCount-- if remainingMessageCount <= 0 { return rows, nil } } if len(resp.NextPageToken) == 0 { break } pageToken = resp.NextPageToken } return rows, nil } table := []DLQRow{} for shardID := range getShards(c) { if remainingMessageCount <= 0 { break } tablesInShard, err := readShard(shardID) if err != nil { return fmt.Errorf("failed to read DLQ messages in shard %v: %w", shardID, err) } table = append(table, tablesInShard...) } return Render(c, table, RenderOptions{DefaultTemplate: templateTable, Color: true}) } // AdminPurgeDLQMessages deletes messages from DLQ func AdminPurgeDLQMessages(c *cli.Context) error { fdlqtype, err := getRequiredOption(c, FlagDLQType) if err != nil { return commoncli.Problem("Required flag not found", err) } dlqType, err := toQueueType(fdlqtype) if err != nil { return commoncli.Problem("Failed to convert queue type", err) } sourceCluster, err := getRequiredOption(c, FlagSourceCluster) if err != nil { return commoncli.Problem("Required option not found", err) } var lastMessageID *int64 if c.IsSet(FlagLastMessageID) { lastMessageID = common.Int64Ptr(c.Int64(FlagLastMessageID)) } adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } for shardID := range getShards(c) { ctx, cancel, err := newContext(c) if err != nil { return commoncli.Problem("Error in creating context: ", err) } err = adminClient.PurgeDLQMessages(ctx, &types.PurgeDLQMessagesRequest{ Type: dlqType, SourceCluster: sourceCluster, ShardID: int32(shardID), InclusiveEndMessageID: lastMessageID, }) cancel() if err != nil { fmt.Printf("Failed to purge DLQ message in shard %v with error: %v.\n", shardID, err) continue } time.Sleep(10 * time.Millisecond) fmt.Printf("Successfully purge DLQ Messages in shard %v.\n", shardID) } return nil } // AdminMergeDLQMessages merges message from DLQ func AdminMergeDLQMessages(c *cli.Context) error { fdlqtype, err := getRequiredOption(c, FlagDLQType) if err != nil { return commoncli.Problem("Required flag not found", err) } dlqType, err := toQueueType(fdlqtype) if err != nil { return commoncli.Problem("Failed to convert queue type", err) } sourceCluster, err := getRequiredOption(c, FlagSourceCluster) if err != nil { return commoncli.Problem("Required option not found", err) } var lastMessageID *int64 if c.IsSet(FlagLastMessageID) { lastMessageID = common.Int64Ptr(c.Int64(FlagLastMessageID)) } adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } ShardIDLoop: for shardID := range getShards(c) { request := &types.MergeDLQMessagesRequest{ Type: dlqType, SourceCluster: sourceCluster, ShardID: int32(shardID), InclusiveEndMessageID: lastMessageID, MaximumPageSize: defaultPageSize, } for { ctx, cancel, err := newContext(c) if err != nil { return commoncli.Problem("Error in creating context:", err) } response, err := adminClient.MergeDLQMessages(ctx, request) cancel() if err != nil { fmt.Printf("Failed to merge DLQ message in shard %v with error: %v.\n", shardID, err) continue ShardIDLoop } if len(response.NextPageToken) == 0 { break } request.NextPageToken = response.NextPageToken } fmt.Printf("Successfully merged all messages in shard %v.\n", shardID) } return nil } func getShards(c *cli.Context) chan int { // Check if we have stdin available stat, err := os.Stdin.Stat() if err == nil && (stat.Mode()&os.ModeCharDevice) == 0 { return readShardsFromStdin() } return generateShardRangeFromFlags(c) } func generateShardRangeFromFlags(c *cli.Context) chan int { shards := make(chan int) go func() { shardRange, err := parseIntMultiRange(c.String(FlagShards)) if err != nil { fmt.Printf("failed to parse shard range: %q\n", c.String(FlagShards)) } else { for _, shard := range shardRange { shards <- shard } } close(shards) }() return shards } func readShardsFromStdin() chan int { shards := make(chan int) go func() { reader := bufio.NewReader(os.Stdin) for { line, err := reader.ReadString('\n') if err == io.EOF { break } if err != nil { fmt.Printf("Unable to read from stdin: %v", err) continue } shard, err := strconv.ParseInt(strings.TrimSpace(line), 10, 64) if err != nil { fmt.Printf("Failed to parse shard id: %q\n", line) continue } shards <- int(shard) } close(shards) }() return shards } func toQueueType(dlqType string) (*types.DLQType, error) { switch dlqType { case "domain": return types.DLQTypeDomain.Ptr(), nil case "history": return types.DLQTypeReplication.Ptr(), nil default: return nil, fmt.Errorf("the queue type is not supported. Type: %v", dlqType) } } func deserializeBatchEvents(blob *types.DataBlob) ([]*types.HistoryEvent, error) { if blob == nil { return nil, nil } serializer := persistence.NewPayloadSerializer() events, err := serializer.DeserializeBatchEvents(persistence.NewDataBlobFromInternal(blob)) if err != nil { return nil, fmt.Errorf("Failed to decode DLQ history replication events: %w", err) } return events, nil } func collectEventIDs(events []*types.HistoryEvent) []int64 { ids := make([]int64, 0, len(events)) for _, event := range events { ids = append(ids, event.ID) } return ids } ================================================ FILE: tools/cli/admin_elastic_search_commands.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "bufio" "encoding/json" "fmt" "math" "os" "strconv" "strings" "time" "github.com/olekukonko/tablewriter" "github.com/olivere/elastic" "github.com/urfave/cli/v2" "golang.org/x/time/rate" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/elasticsearch" es "github.com/uber/cadence/common/elasticsearch" "github.com/uber/cadence/common/elasticsearch/esql" "github.com/uber/cadence/tools/common/commoncli" ) const ( versionTypeExternal = "external" ) var timeKeys = map[string]bool{ "StartTime": true, "CloseTime": true, "ExecutionTime": true, } func timeKeyFilter(key string) bool { return timeKeys[key] } func timeValProcess(timeStr string) (string, error) { // first check if already in int64 format if _, err := strconv.ParseInt(timeStr, 10, 64); err == nil { return timeStr, nil } // try to parse time parsedTime, err := time.Parse(defaultDateTimeFormat, timeStr) if err != nil { return "", err } return fmt.Sprintf("%v", parsedTime.UnixNano()), nil } type ESIndexRow struct { Health string `header:"Health"` Status string `header:"Status"` Index string `header:"Index"` PrimaryShards int `header:"Pri"` ReplicaShards int `header:"Rep"` DocsCount int `header:"Docs Count"` DocsDeleted int `header:"Docs Deleted"` StorageSize string `header:"Store Size"` PrimaryStorageSize string `header:"Pri Store Size"` } // AdminCatIndices cat indices for ES cluster func AdminCatIndices(c *cli.Context) error { esClient, err := getDeps(c).ElasticSearchClient(c) if err != nil { return err } ctx := c.Context resp, err := esClient.CatIndices().Do(ctx) if err != nil { return commoncli.Problem("Unable to cat indices", err) } table := []ESIndexRow{} for _, row := range resp { table = append(table, ESIndexRow{ Health: row.Health, Status: row.Status, Index: row.Index, PrimaryShards: row.Pri, ReplicaShards: row.Rep, DocsCount: row.DocsCount, DocsDeleted: row.DocsDeleted, StorageSize: row.StoreSize, PrimaryStorageSize: row.PriStoreSize, }) } return Render(c, table, RenderOptions{DefaultTemplate: templateTable, Color: true, Border: true}) } // AdminIndex used to bulk insert message from kafka parse func AdminIndex(c *cli.Context) error { esClient, err := getDeps(c).ElasticSearchClient(c) if err != nil { return err } indexName, err := getRequiredOption(c, FlagIndex) if err != nil { return commoncli.Problem("Required flag not found: ", err) } inputFileName, err := getRequiredOption(c, FlagInputFile) if err != nil { return commoncli.Problem("Required flag not found: ", err) } batchSize := c.Int(FlagBatchSize) messages, err := parseIndexerMessage(inputFileName) if err != nil { return commoncli.Problem("Unable to parse indexer message", err) } bulkRequest := esClient.Bulk() bulkConductFn := func() error { _, err := bulkRequest.Do(c.Context) if err != nil { return commoncli.Problem("Bulk failed", err) } if bulkRequest.NumberOfActions() != 0 { return commoncli.Problem(fmt.Sprintf("Bulk request not done, %d", bulkRequest.NumberOfActions()), err) } return nil } for i, message := range messages { docID := message.GetWorkflowID() + elasticsearch.GetESDocDelimiter() + message.GetRunID() var req elastic.BulkableRequest switch message.GetMessageType() { case indexer.MessageTypeIndex: doc, err := generateESDoc(message) if err != nil { return commoncli.Problem("Error in Admin Index: ", err) } req = elastic.NewBulkIndexRequest(). Index(indexName). Type(elasticsearch.GetESDocType()). Id(docID). VersionType(versionTypeExternal). Version(message.GetVersion()). Doc(doc) case indexer.MessageTypeDelete: req = elastic.NewBulkDeleteRequest(). Index(indexName). Type(elasticsearch.GetESDocType()). Id(docID). VersionType(versionTypeExternal). Version(message.GetVersion()) case indexer.MessageTypeCreate: req = elastic.NewBulkIndexRequest(). OpType("create"). Index(indexName). Type(elasticsearch.GetESDocType()). Id(docID). VersionType("internal") default: return commoncli.Problem("Unknown message type", nil) } bulkRequest.Add(req) if i%batchSize == batchSize-1 { if err := bulkConductFn(); err != nil { return err } } } if bulkRequest.NumberOfActions() != 0 { if err := bulkConductFn(); err != nil { return err } } return nil } // AdminDelete used to delete documents from ElasticSearch with input of list result func AdminDelete(c *cli.Context) error { esClient, err := getDeps(c).ElasticSearchClient(c) if err != nil { return err } indexName, err := getRequiredOption(c, FlagIndex) if err != nil { return commoncli.Problem("Required flag not found: ", err) } inputFileName, err := getRequiredOption(c, FlagInputFile) if err != nil { return commoncli.Problem("Required flag not found: ", err) } batchSize := c.Int(FlagBatchSize) rps := c.Int(FlagRPS) if rps <= 0 { err = fmt.Errorf("FlagRPS must be positive value but got %v", rps) return commoncli.Problem("Invalid RPS value: ", err) } ratelimiter := clock.NewRatelimiter(rate.Limit(rps), rps) // This is only executed from the CLI by an admin user // #nosec file, err := os.Open(inputFileName) if err != nil { return commoncli.Problem("Cannot open input file", nil) } defer file.Close() scanner := bufio.NewScanner(file) scanner.Scan() // skip first line i := 0 bulkRequest := esClient.Bulk() bulkConductFn := func() error { // Wait for rate limiter token to become available if err = ratelimiter.Wait(c.Context); err != nil { return err } _, err = bulkRequest.Do(c.Context) if err != nil { return commoncli.Problem(fmt.Sprintf("Bulk failed, current processed row %d", i), err) } if bulkRequest.NumberOfActions() != 0 { return commoncli.Problem(fmt.Sprintf("Bulk request not done, current processed row %d", i), err) } return nil } for scanner.Scan() { line := strings.Split(scanner.Text(), "|") docID := strings.TrimSpace(line[1]) + elasticsearch.GetESDocDelimiter() + strings.TrimSpace(line[2]) req := elastic.NewBulkDeleteRequest(). Index(indexName). Type(elasticsearch.GetESDocType()). Id(docID). VersionType(versionTypeExternal). Version(math.MaxInt64) bulkRequest.Add(req) if i%batchSize == batchSize-1 { if err = bulkConductFn(); err != nil { return err } } i++ } if bulkRequest.NumberOfActions() != 0 { return bulkConductFn() } return nil } func parseIndexerMessage(fileName string) (messages []*indexer.Message, err error) { // Executed from the CLI to parse existing elastiseach files // #nosec file, err := os.Open(fileName) if err != nil { return nil, err } defer file.Close() scanner := bufio.NewScanner(file) idx := 0 for scanner.Scan() { idx++ line := strings.TrimSpace(scanner.Text()) if len(line) == 0 { fmt.Printf("line %v is empty, skipped\n", idx) continue } msg := &indexer.Message{} err := json.Unmarshal([]byte(line), msg) if err != nil { fmt.Printf("line %v cannot be deserialized to indexer message: %v.\n", idx, line) return nil, err } messages = append(messages, msg) } if err := scanner.Err(); err != nil { return nil, err } return messages, nil } func generateESDoc(msg *indexer.Message) (map[string]interface{}, error) { doc := make(map[string]interface{}) doc[es.DomainID] = msg.GetDomainID() doc[es.WorkflowID] = msg.GetWorkflowID() doc[es.RunID] = msg.GetRunID() for k, v := range msg.Fields { switch v.GetType() { case indexer.FieldTypeString: doc[k] = v.GetStringData() case indexer.FieldTypeInt: doc[k] = v.GetIntData() case indexer.FieldTypeBool: doc[k] = v.GetBoolData() case indexer.FieldTypeBinary: doc[k] = v.GetBinaryData() default: return nil, fmt.Errorf("Unknown field type %v", nil) } } return doc, nil } // This function is used to trim unnecessary tag in returned json for table header func trimBucketKey(k string) string { // group key is in form of "group_key", we only need "key" as the column name k = strings.TrimPrefix(k, "group_") k = strings.TrimPrefix(k, "Attr_") return fmt.Sprintf(`%v(*)`, k) } // parse the returned time to readable string if time is in int64 format func toTimeStr(s interface{}) string { floatTime, err := strconv.ParseFloat(s.(string), 64) intTime := int64(floatTime) if err != nil { return s.(string) } t := time.Unix(0, intTime) return t.Format(time.RFC3339) } // GenerateReport generate report for an aggregation query to ES func GenerateReport(c *cli.Context) error { output := getDeps(c).Output() // use url command argument to create client index, err := getRequiredOption(c, FlagIndex) if err != nil { return commoncli.Problem("Required flag not found: ", err) } sql, err := getRequiredOption(c, FlagListQuery) if err != nil { return commoncli.Problem("Required flag not found: ", err) } var reportFormat, reportFilePath string if c.IsSet(FlagOutputFormat) { reportFormat = c.String(FlagOutputFormat) } if c.IsSet(FlagOutputFilename) { reportFilePath = c.String(FlagOutputFilename) } else { reportFilePath = "./report." + reportFormat } esClient, err := getDeps(c).ElasticSearchClient(c) if err != nil { return err } ctx := c.Context // convert sql to dsl e := esql.NewESql() e.SetCadence(true) e.ProcessQueryValue(timeKeyFilter, timeValProcess) dsl, sortFields, err := e.ConvertPrettyCadence(sql, "") if err != nil { return commoncli.Problem("Fail to convert sql to dsl", err) } // query client resp, err := esClient.Search(index).Source(dsl).Do(ctx) if err != nil { return commoncli.Problem("Fail to talk with ES", err) } // Show result to terminal table := tablewriter.NewWriter(output) var headers []string var groupby, bucket map[string]interface{} var buckets []interface{} err = json.Unmarshal(*resp.Aggregations["groupby"], &groupby) if err != nil { return commoncli.Problem("Fail to parse groupby", err) } buckets = groupby["buckets"].([]interface{}) if len(buckets) == 0 { output.Write([]byte("no matching bucket\n")) return nil } // get the FIRST bucket in bucket list to extract all tags. These extracted tags are to be used as table heads bucket = buckets[0].(map[string]interface{}) // record the column position in the table of each returned item ids := make(map[string]int) // We want these 3 columns shows at leftmost of the table in cadence report usage. It can be changed in future. primaryCols := []string{"group_DomainID", "group_WorkflowType", "group_CloseStatus"} primaryColsMap := map[string]int{ "group_DomainID": 1, "group_WorkflowType": 1, "group_CloseStatus": 1, } buckKeys := 0 // number of bucket keys, used for table collapsing in html report if v, exist := bucket["key"]; exist { vmap := v.(map[string]interface{}) // first search whether primaryCols keys exist, if found, put them at the table beginning for _, k := range primaryCols { if _, exist := vmap[k]; exist { k = trimBucketKey(k) // trim the unnecessary prefix headers = append(headers, k) ids[k] = len(ids) buckKeys++ } } // extract all remaining bucket keys for k := range vmap { if _, exist := primaryColsMap[k]; !exist { k = trimBucketKey(k) headers = append(headers, k) ids[k] = len(ids) buckKeys++ } } } // extract all other non-key items and set the table head accordingly for k := range bucket { if k != "key" { if k == "doc_count" { k = "count" } headers = append(headers, k) ids[k] = len(ids) } } table.SetHeader(headers) // read each bucket and fill the table, use map ids to find the correct spot var tableData [][]string for _, b := range buckets { bucket = b.(map[string]interface{}) data := make([]string, len(headers)) for k, v := range bucket { switch k { case "key": // fill group key vmap := v.(map[string]interface{}) for kk, vv := range vmap { kk = trimBucketKey(kk) data[ids[kk]] = fmt.Sprintf("%v", vv) } case "doc_count": // fill bucket size count data[ids["count"]] = fmt.Sprintf("%v", v) default: var datum string vmap := v.(map[string]interface{}) if strings.Contains(k, "Attr_CustomDatetimeField") { datum = fmt.Sprintf("%v", vmap["value_as_string"]) } else { datum = fmt.Sprintf("%v", vmap["value"]) // convert Cadence stored time (unix nano) to readable format if strings.Contains(k, "Time") && !strings.Contains(k, "Attr_") { datum = toTimeStr(datum) } } data[ids[k]] = datum } } table.Append(data) tableData = append(tableData, data) } table.Render() switch reportFormat { case "html", "HTML": sorted := len(sortFields) > 0 || strings.Contains(sql, "ORDER BY") || strings.Contains(sql, "order by") return generateHTMLReport(reportFilePath, buckKeys, sorted, headers, tableData) case "csv", "CSV": return generateCSVReport(reportFilePath, headers, tableData) default: return commoncli.Problem(fmt.Sprintf(`Report format %v not supported.`, reportFormat), nil) } } func generateCSVReport(reportFileName string, headers []string, tableData [][]string) error { // write csv report f, err := os.Create(reportFileName) if err != nil { return commoncli.Problem("Fail to create csv report file", err) } csvContent := strings.Join(headers, ",") + "\n" for _, data := range tableData { csvContent += strings.Join(data, ",") + "\n" } _, err = f.WriteString(csvContent) if err != nil { fmt.Printf("Error write to file, err: %v", err) } return f.Close() } func generateHTMLReport(reportFileName string, numBuckKeys int, sorted bool, headers []string, tableData [][]string) error { // write html report f, err := os.Create(reportFileName) if err != nil { return commoncli.Problem("Fail to create html report file", err) } var htmlContent string m, n := len(headers), len(tableData) rowSpan := make([]int, m) // record the collapsing size of each column for i := 0; i < m; i++ { rowSpan[i] = 1 cell := wrapWithTag(headers[i], "td", "") htmlContent += cell } htmlContent = wrapWithTag(htmlContent, "tr", "") for row := 0; row < n; row++ { var rowData string for col := 0; col < m; col++ { rowSpan[col]-- // don't do collapsing if sorted if col < numBuckKeys-1 && !sorted { if rowSpan[col] == 0 { for i := row; i < n; i++ { if tableData[i][col] == tableData[row][col] { rowSpan[col]++ } else { break } } var property string if rowSpan[col] > 1 { property = fmt.Sprintf(`rowspan="%d"`, rowSpan[col]) } cell := wrapWithTag(tableData[row][col], "td", property) rowData += cell } } else { cell := wrapWithTag(tableData[row][col], "td", "") rowData += cell } } rowData = wrapWithTag(rowData, "tr", "") htmlContent += rowData } htmlContent = wrapWithTag(htmlContent, "table", "") htmlContent = wrapWithTag(htmlContent, "body", "") htmlContent = wrapWithTag(htmlContent, "html", "") //nolint:errcheck f.WriteString("\n") f.WriteString(` ` + "\n") f.WriteString(htmlContent) return f.Close() } // return a string that use tag to wrap content func wrapWithTag(content string, tag string, property string) string { if property != "" { property = " " + property } if tag != "td" { content = "\n" + content } return "<" + tag + property + ">" + content + "\n" } ================================================ FILE: tools/cli/admin_elastic_search_commands_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "bufio" "flag" "fmt" "net/http" "net/http/httptest" "os" "regexp" "strings" "testing" "time" "github.com/olivere/elastic" "github.com/stretchr/testify/assert" "github.com/urfave/cli/v2" "go.uber.org/mock/gomock" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/common/elasticsearch" ) // Tests for timeKeyFilter function func TestTimeKeyFilter(t *testing.T) { tests := []struct { name string key string expected bool }{ { name: "ValidTimeKeyStartTime", key: "StartTime", expected: true, }, { name: "ValidTimeKeyCloseTime", key: "CloseTime", expected: true, }, { name: "ValidTimeKeyExecutionTime", key: "ExecutionTime", expected: true, }, { name: "InvalidTimeKey", key: "SomeOtherKey", expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := timeKeyFilter(tt.key) assert.Equal(t, tt.expected, result) }) } } // Tests for timeValProcess function func TestTimeValProcess(t *testing.T) { tests := []struct { name string timeStr string expected string expectError bool }{ { name: "ValidInt64TimeString", timeStr: "1630425600000000000", // Already in int64 format expected: "1630425600000000000", expectError: false, }, { name: "ValidDateTimeString", timeStr: "2021-09-01T00:00:00Z", // A valid time string expected: fmt.Sprintf("%v", time.Date(2021, 9, 1, 0, 0, 0, 0, time.UTC).UnixNano()), expectError: false, }, { name: "InvalidTimeString", timeStr: "invalid-time", expected: "", expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := timeValProcess(tt.timeStr) if tt.expectError { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.expected, result) } }) } } // Helper function to remove ANSI color codes from the output func removeANSIColors(text string) string { ansiEscapePattern := `\x1b\[[0-9;]*m` re := regexp.MustCompile(ansiEscapePattern) return re.ReplaceAllString(text, "") } func TestAdminCatIndices(t *testing.T) { tests := []struct { name string handler http.HandlerFunc expectedOutput string expectedError string handlerCalled bool }{ { name: "Success", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a successful response from Elasticsearch CatIndices API if r.URL.Path == "/_cat/indices" && r.Method == "GET" { w.WriteHeader(http.StatusOK) w.Write([]byte(`[{"health":"green","status":"open","index":"test-index","pri":"5","rep":"1","docs.count":"1000","docs.deleted":"50","store.size":"10gb","pri.store.size":"5gb"}]`)) } else { w.WriteHeader(http.StatusNotFound) } }), expectedOutput: `+--------+--------+------------+-----+-----+------------+--------------+------------+----------------+ | HEALTH | STATUS | INDEX | PRI | REP | DOCS COUNT | DOCS DELETED | STORE SIZE | PRI STORE SIZE | +--------+--------+------------+-----+-----+------------+--------------+------------+----------------+ | green | open | test-index | 5 | 1 | 1000 | 50 | 10gb | 5gb | +--------+--------+------------+-----+-----+------------+--------------+------------+----------------+ `, expectedError: "", handlerCalled: true, }, { name: "CatIndices Error", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate an error response w.WriteHeader(http.StatusInternalServerError) }), expectedOutput: "", expectedError: "Unable to cat indices", handlerCalled: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { handlerCalled := false // Wrap the test case's handler to track if it was called wrappedHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handlerCalled = true tt.handler.ServeHTTP(w, r) }) // Create mock Elasticsearch client and server esClient, testServer := getMockClient(t, wrappedHandler) defer testServer.Close() // Initialize mock controller mockCtrl := gomock.NewController(t) // Create mock Cadence client factory mockClientFactory := NewMockClientFactory(mockCtrl) // Create test IO handler to capture output ioHandler := &testIOHandler{} // Set up the CLI app app := NewCliApp(mockClientFactory, WithIOHandler(ioHandler)) // Expect ElasticSearchClient to return the mock client created by getMockClient mockClientFactory.EXPECT().ElasticSearchClient(gomock.Any()).Return(esClient, nil).Times(1) // Create a mock CLI context c := setContextMock(app) // Call AdminCatIndices err := AdminCatIndices(c) // Validate handler was called assert.Equal(t, tt.handlerCalled, handlerCalled, "Expected handler to be called") // Check for expected error or success if tt.expectedError != "" { assert.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) } else { assert.NoError(t, err) // Remove ANSI color codes from the captured output actualOutput := removeANSIColors(ioHandler.outputBytes.String()) // Validate the output captured by testIOHandler assert.Equal(t, tt.expectedOutput, actualOutput) } }) } } func setContextMock(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) set.String(FlagDomain, "test-domain", "Domain flag") c := cli.NewContext(app, set, nil) return c } // getMockClient creates a mock elastic.Client using the provided HTTP handler and returns the client and the test server func getMockClient(t *testing.T, handler http.HandlerFunc) (*elastic.Client, *httptest.Server) { // Create a mock HTTP test server testServer := httptest.NewTLSServer(handler) // Create an Elasticsearch client using the test server's URL mockClient, err := elastic.NewClient( elastic.SetURL(testServer.URL), elastic.SetSniff(false), elastic.SetHealthcheck(false), elastic.SetHttpClient(testServer.Client()), ) // Ensure no error occurred while creating the mock client assert.NoError(t, err) // Return the elastic.Client and the test server return mockClient, testServer } func TestAdminIndex(t *testing.T) { tests := []struct { name string handler http.HandlerFunc createInputFile bool messageType indexer.MessageType expectedOutput string expectedError string }{ { name: "SuccessIndexMessage", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a successful Bulk request if r.URL.Path == "/_bulk" && r.Method == "POST" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"took": 30, "errors": false}`)) } else { w.WriteHeader(http.StatusNotFound) } }), createInputFile: true, messageType: indexer.MessageTypeIndex, expectedOutput: "", // Example output for success case expectedError: "", }, { name: "SuccessCreateMessage", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a successful Bulk request if r.URL.Path == "/_bulk" && r.Method == "POST" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"took": 30, "errors": false}`)) } else { w.WriteHeader(http.StatusNotFound) } }), createInputFile: true, messageType: indexer.MessageTypeCreate, expectedOutput: "", // Example output for create case expectedError: "", }, { name: "SuccessDeleteMessage", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a successful Bulk request if r.URL.Path == "/_bulk" && r.Method == "POST" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"took": 30, "errors": false}`)) } else { w.WriteHeader(http.StatusNotFound) } }), createInputFile: true, messageType: indexer.MessageTypeDelete, expectedOutput: "", // Example output for delete case expectedError: "", }, { name: "UnknownMessageType", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // No bulk request needed for this case w.WriteHeader(http.StatusOK) }), createInputFile: true, messageType: indexer.MessageType(9999), expectedOutput: "", expectedError: "Unknown message type", }, { name: "BulkRequestFailure", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a Bulk request failure w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(`{"error": "Bulk request failed"}`)) }), createInputFile: true, messageType: indexer.MessageTypeIndex, expectedOutput: "", expectedError: "Bulk failed", }, { name: "ParseIndexerMessageError", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // In this test case, we are simulating a parse error, so no bulk request needed. w.WriteHeader(http.StatusOK) }), createInputFile: false, // No valid input file created messageType: indexer.MessageTypeIndex, expectedOutput: "", expectedError: "Unable to parse indexer message", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var inputFileName string var err error if tt.createInputFile { // Create a temporary input file with a valid message inputFileName, err = createTempIndexerInputFileWithMessageType(tt.messageType, false) assert.NoError(t, err) defer os.Remove(inputFileName) // Clean up after test } // Create mock Elasticsearch client and server esClient, testServer := getMockClient(t, tt.handler) defer testServer.Close() // Initialize mock controller mockCtrl := gomock.NewController(t) // Create mock client factory mockClientFactory := NewMockClientFactory(mockCtrl) // Create test IO handler to capture output ioHandler := &testIOHandler{} // Set up the CLI app app := NewCliApp(mockClientFactory, WithIOHandler(ioHandler)) // Expect ElasticSearchClient to return the mock client created by getMockClient mockClientFactory.EXPECT().ElasticSearchClient(gomock.Any()).Return(esClient, nil).Times(1) // Setup flag values for the CLI context set := flag.NewFlagSet("test", 0) set.String(FlagIndex, "test-index", "Index flag") if tt.createInputFile { set.String(FlagInputFile, inputFileName, "Input file flag") } else { set.String(FlagInputFile, "invalid-input-file", "Input file flag") } set.Int(FlagBatchSize, 1, "Batch size flag") // Create a mock CLI context c := cli.NewContext(app, set, nil) // Call AdminIndex err = AdminIndex(c) // Validate results if tt.expectedError != "" { assert.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) } else { assert.NoError(t, err) // Validate the output captured by testIOHandler assert.Equal(t, tt.expectedOutput, ioHandler.outputBytes.String()) } }) } } // Helper function to create a temporary input file for AdminIndex or AdminDelete with valid data func createTempIndexerInputFileWithMessageType(messageType indexer.MessageType, forDelete bool) (string, error) { file, err := os.CreateTemp("", "indexer_input_*.txt") if err != nil { return "", err } defer file.Close() writer := bufio.NewWriter(file) if forDelete { // For AdminDelete, we need to simulate workflow-id|run-id format _, err = writer.WriteString("Header\n") // First line is skipped in AdminDelete if err != nil { return "", err } _, err = writer.WriteString("some-value|workflow-id|run-id\n") // Simulate document deletion data if err != nil { return "", err } } else { // For AdminIndex, we need to generate a JSON message format message := `{"WorkflowID": "test-workflow-id", "RunID": "test-run-id", "Version": 1, "MessageType": ` + fmt.Sprintf("%d", messageType) + `}` _, err = writer.WriteString(message + "\n") if err != nil { return "", err } } writer.Flush() return file.Name(), nil } func TestAdminDelete(t *testing.T) { tests := []struct { name string handler http.HandlerFunc createInputFile bool expectedOutput string expectedError string }{ { name: "SuccessDelete", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a successful Bulk delete request if r.URL.Path == "/_bulk" && r.Method == "POST" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"took": 30, "errors": false}`)) } else { w.WriteHeader(http.StatusNotFound) } }), createInputFile: true, expectedOutput: "", // Example output for delete case expectedError: "", }, { name: "BulkRequestFailure", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate an error in the Bulk delete request w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(`{"error": "Bulk request failed"}`)) }), createInputFile: true, expectedOutput: "", expectedError: "Bulk failed", }, { name: "ParseFileError", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // No bulk request needed in this case, just simulating a file parsing error w.WriteHeader(http.StatusOK) }), createInputFile: false, // No valid input file created expectedOutput: "", expectedError: "Cannot open input file", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var inputFileName string var err error if tt.createInputFile { // Reuse the temp input file creation helper from previous tests inputFileName, err = createTempIndexerInputFileWithMessageType(indexer.MessageTypeDelete, true) assert.NoError(t, err) defer os.Remove(inputFileName) // Clean up after test } // Create mock Elasticsearch client and server esClient, testServer := getMockClient(t, tt.handler) defer testServer.Close() // Initialize mock controller mockCtrl := gomock.NewController(t) // Create mock client factory mockClientFactory := NewMockClientFactory(mockCtrl) // Expect ElasticSearchClient to return the mock client created by getMockClient mockClientFactory.EXPECT().ElasticSearchClient(gomock.Any()).Return(esClient, nil).Times(1) // Create test IO handler to capture output ioHandler := &testIOHandler{} // Set up the CLI app app := NewCliApp(mockClientFactory, WithIOHandler(ioHandler)) // Setup flag values for the CLI context set := flag.NewFlagSet("test", 0) set.String(FlagIndex, "test-index", "Index flag") if tt.createInputFile { set.String(FlagInputFile, inputFileName, "Input file flag") } else { set.String(FlagInputFile, "invalid-input-file", "Input file flag") } set.Int(FlagBatchSize, 1, "Batch size flag") set.Int(FlagRPS, 10, "RPS flag") // Create a mock CLI context c := cli.NewContext(app, set, nil) // Call AdminDelete err = AdminDelete(c) // Validate results if tt.expectedError != "" { if err != nil { assert.Contains(t, err.Error(), tt.expectedError) } else { t.Errorf("Expected error: %s, but got no error", tt.expectedError) } } else { assert.NoError(t, err) // Validate the output captured by testIOHandler assert.Equal(t, tt.expectedOutput, ioHandler.outputBytes.String()) } }) } } func TestParseIndexerMessage(t *testing.T) { workflowID := "test-workflow-id" runID := "test-run-id" version := int64(1) messageType := indexer.MessageTypeIndex tests := []struct { name string messageType indexer.MessageType createInputFile bool expectedError string expectedResult []*indexer.Message }{ { name: "SuccessParse", messageType: indexer.MessageTypeIndex, createInputFile: true, expectedError: "", expectedResult: []*indexer.Message{ { WorkflowID: &workflowID, RunID: &runID, Version: &version, MessageType: &messageType, }, }, }, { name: "FileNotExist", messageType: 0, createInputFile: false, // No file created expectedError: "open nonexistent-file.txt: no such file or directory", expectedResult: nil, }, { name: "SkipEmptyLines", messageType: indexer.MessageTypeIndex, createInputFile: true, expectedError: "", expectedResult: []*indexer.Message{ { WorkflowID: &workflowID, RunID: &runID, Version: &version, MessageType: &messageType, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var fileName string var err error if tt.createInputFile { // Use the existing createTempIndexerInputFileWithMessageType function fileName, err = createTempIndexerInputFileWithMessageType(tt.messageType, false) // forDelete=false for AdminIndex assert.NoError(t, err) defer os.Remove(fileName) // Clean up after test } else { // Simulate file not found fileName = "nonexistent-file.txt" } // Call the function being tested messages, err := parseIndexerMessage(fileName) // Validate results if tt.expectedError != "" { assert.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) assert.Nil(t, messages) } else { assert.NoError(t, err) assert.Equal(t, tt.expectedResult, messages) } }) } } func TestGenerateESDoc(t *testing.T) { tests := []struct { name string message *indexer.Message expectedDoc map[string]interface{} expectedError string }{ { name: "SuccessWithAllFieldTypes", message: &indexer.Message{ DomainID: &[]string{"domain1"}[0], WorkflowID: &[]string{"workflow1"}[0], RunID: &[]string{"run1"}[0], Fields: map[string]*indexer.Field{ "field_string": { Type: &[]indexer.FieldType{indexer.FieldTypeString}[0], StringData: &[]string{"string_value"}[0], }, "field_int": { Type: &[]indexer.FieldType{indexer.FieldTypeInt}[0], IntData: &[]int64{123}[0], }, "field_bool": { Type: &[]indexer.FieldType{indexer.FieldTypeBool}[0], BoolData: &[]bool{true}[0], }, "field_binary": { Type: &[]indexer.FieldType{indexer.FieldTypeBinary}[0], BinaryData: []byte("binary_value"), }, }, }, expectedDoc: map[string]interface{}{ elasticsearch.DomainID: "domain1", elasticsearch.WorkflowID: "workflow1", elasticsearch.RunID: "run1", "field_string": "string_value", "field_int": int64(123), "field_bool": true, "field_binary": []byte("binary_value"), }, expectedError: "", }, { name: "UnknownFieldType", message: &indexer.Message{ DomainID: &[]string{"domain1"}[0], WorkflowID: &[]string{"workflow1"}[0], RunID: &[]string{"run1"}[0], Fields: map[string]*indexer.Field{ "unknown_field": { Type: &[]indexer.FieldType{9999}[0], // Invalid field type }, }, }, expectedDoc: nil, expectedError: "Unknown field type", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Call the function being tested doc, err := generateESDoc(tt.message) // Validate results if tt.expectedError != "" { assert.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) assert.Nil(t, doc) } else { assert.NoError(t, err) assert.Equal(t, tt.expectedDoc, doc) } }) } } func TestGenerateReport(t *testing.T) { tests := []struct { name string handler http.HandlerFunc setupContext func(app *cli.App) *cli.Context setupMocks func(mockClientFactory *MockClientFactory, esClient *elastic.Client) expectedOutput string expectedError string }{ { name: "SuccessCSVReportWithExtraKey", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a successful response with extra key not in primaryColsMap expectedPath := "/test-index/_search" if r.URL.Path == expectedPath && r.Method == "POST" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "aggregations": { "groupby": { "buckets": [ { "key": { "group_DomainID": "domain1", "group_CustomKey": "custom-value" }, "Attr_CustomDatetimeField": { "value_as_string": "2023-10-01T12:34:56.789Z" } } ] } } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) // Define and set flags set.String(FlagIndex, "", "Index flag") set.String(FlagListQuery, "", "List query flag") set.String(FlagOutputFormat, "", "Output format flag") set.String(FlagOutputFilename, "", "Output file flag") // Set the actual values _ = set.Set(FlagIndex, "test-index") _ = set.Set(FlagListQuery, "SELECT * FROM logs") _ = set.Set(FlagOutputFormat, "csv") _ = set.Set(FlagOutputFilename, "test-report.csv") return cli.NewContext(app, set, nil) }, setupMocks: func(mockClientFactory *MockClientFactory, esClient *elastic.Client) { mockClientFactory.EXPECT().ElasticSearchClient(gomock.Any()).Return(esClient, nil).Times(1) }, expectedOutput: `+-------------+--------------+--------------------------+ | DOMAINID(*) | CUSTOMKEY(*) | ATTR CUSTOMDATETIMEFIELD | +-------------+--------------+--------------------------+ | domain1 | custom-value | 2023-10-01T12:34:56.789Z | +-------------+--------------+--------------------------+ `, expectedError: "", }, // can't put all keys all together because keys generated in reports are in a random order, thus will fail tests { name: "SuccessCSVReportWithOtherExtraKey", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a successful response with extra key not in primaryColsMap expectedPath := "/test-index/_search" if r.URL.Path == expectedPath && r.Method == "POST" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "aggregations": { "groupby": { "buckets": [ { "key": { "group_DomainID": "domain1", "group_CustomKey": "custom-value" }, "Attr_CustomStringField": { "value": "test-string" } } ] } } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) // Define and set flags set.String(FlagIndex, "", "Index flag") set.String(FlagListQuery, "", "List query flag") set.String(FlagOutputFormat, "", "Output format flag") set.String(FlagOutputFilename, "", "Output file flag") // Set the actual values _ = set.Set(FlagIndex, "test-index") _ = set.Set(FlagListQuery, "SELECT * FROM logs") _ = set.Set(FlagOutputFormat, "csv") _ = set.Set(FlagOutputFilename, "test-report.csv") return cli.NewContext(app, set, nil) }, setupMocks: func(mockClientFactory *MockClientFactory, esClient *elastic.Client) { mockClientFactory.EXPECT().ElasticSearchClient(gomock.Any()).Return(esClient, nil).Times(1) }, expectedOutput: `+-------------+--------------+------------------------+ | DOMAINID(*) | CUSTOMKEY(*) | ATTR CUSTOMSTRINGFIELD | +-------------+--------------+------------------------+ | domain1 | custom-value | test-string | +-------------+--------------+------------------------+ `, expectedError: "", }, { name: "SuccessHTMLReportWithExtraKey", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a successful response with extra key not in primaryColsMap expectedPath := "/test-index/_search" if r.URL.Path == expectedPath && r.Method == "POST" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "aggregations": { "groupby": { "buckets": [ { "key": { "group_DomainID": "domain1", "group_CustomKey": "custom-value" }, "doc_count": 10 } ] } } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) // Define and set flags set.String(FlagIndex, "", "Index flag") set.String(FlagListQuery, "", "List query flag") set.String(FlagOutputFormat, "", "Output format flag") set.String(FlagOutputFilename, "", "Output file flag") // Set the actual values _ = set.Set(FlagIndex, "test-index") _ = set.Set(FlagListQuery, "SELECT * FROM logs") _ = set.Set(FlagOutputFormat, "html") _ = set.Set(FlagOutputFilename, "test-report.csv") return cli.NewContext(app, set, nil) }, setupMocks: func(mockClientFactory *MockClientFactory, esClient *elastic.Client) { mockClientFactory.EXPECT().ElasticSearchClient(gomock.Any()).Return(esClient, nil).Times(1) }, expectedOutput: `+-------------+--------------+-------+ | DOMAINID(*) | CUSTOMKEY(*) | COUNT | +-------------+--------------+-------+ | domain1 | custom-value | 10 | +-------------+--------------+-------+ `, expectedError: "", }, { name: "EmptyBucket", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate a successful response with extra key not in primaryColsMap expectedPath := "/test-index/_search" if r.URL.Path == expectedPath && r.Method == "POST" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "aggregations": { "groupby": { "buckets": [] } } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) // Define and set flags set.String(FlagIndex, "", "Index flag") set.String(FlagListQuery, "", "List query flag") set.String(FlagOutputFormat, "", "Output format flag") set.String(FlagOutputFilename, "", "Output file flag") // Set the actual values _ = set.Set(FlagIndex, "test-index") _ = set.Set(FlagListQuery, "SELECT * FROM logs") _ = set.Set(FlagOutputFormat, "html") _ = set.Set(FlagOutputFilename, "test-report.csv") return cli.NewContext(app, set, nil) }, setupMocks: func(mockClientFactory *MockClientFactory, esClient *elastic.Client) { mockClientFactory.EXPECT().ElasticSearchClient(gomock.Any()).Return(esClient, nil).Times(1) }, expectedOutput: `no matching bucket `, expectedError: "", }, { name: "UnsupportedReportFormat", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Elasticsearch request returns successful response if r.URL.Path == "/test-index/_search" && r.Method == "POST" { w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "aggregations": { "groupby": { "buckets": [ { "key": {"group_DomainID": "domain1"}, "doc_count": 10 } ] } } }`)) } else { w.WriteHeader(http.StatusNotFound) } }), setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) // Define and set flags set.String(FlagIndex, "", "Index flag") set.String(FlagListQuery, "", "List query flag") set.String(FlagOutputFormat, "", "Output format flag") set.String(FlagOutputFilename, "", "Output file flag") // Set the actual values _ = set.Set(FlagIndex, "test-index") _ = set.Set(FlagListQuery, "SELECT * FROM logs") _ = set.Set(FlagOutputFormat, "unsupported-format") _ = set.Set(FlagOutputFilename, "test-report.unsupported") return cli.NewContext(app, set, nil) }, setupMocks: func(mockClientFactory *MockClientFactory, esClient *elastic.Client) { mockClientFactory.EXPECT().ElasticSearchClient(gomock.Any()).Return(esClient, nil).Times(1) }, expectedOutput: "", expectedError: "Report format unsupported-format not supported.", }, { name: "ElasticsearchQueryError", handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Simulate an error response from Elasticsearch w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(`{"error": "query failed"}`)) }), setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) // Define and set flags set.String(FlagIndex, "", "Index flag") set.String(FlagListQuery, "", "List query flag") // Set the actual values _ = set.Set(FlagIndex, "test-index") _ = set.Set(FlagListQuery, "SELECT * FROM logs") return cli.NewContext(app, set, nil) }, setupMocks: func(mockClientFactory *MockClientFactory, esClient *elastic.Client) { mockClientFactory.EXPECT().ElasticSearchClient(gomock.Any()).Return(esClient, nil).Times(1) }, expectedOutput: "", expectedError: "Fail to talk with ES", }, { name: "MissingRequiredFlagIndex", handler: nil, // No handler needed since the error occurs before any Elasticsearch interaction setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) // Only setting FlagListQuery, but missing FlagIndex to trigger the error set.String(FlagListQuery, "", "List query flag") _ = set.Set(FlagListQuery, "SELECT * FROM logs") return cli.NewContext(app, set, nil) }, setupMocks: func(mockClientFactory *MockClientFactory, esClient *elastic.Client) {}, expectedOutput: "", expectedError: "Required flag not found: ", }, { name: "MissingRequiredFlagListQuery", handler: nil, // No handler needed since the error occurs before any Elasticsearch interaction setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) // Only setting FlagIndex, but missing FlagListQuery to trigger the error set.String(FlagIndex, "", "Index flag") _ = set.Set(FlagIndex, "test-index") return cli.NewContext(app, set, nil) }, setupMocks: func(mockClientFactory *MockClientFactory, esClient *elastic.Client) {}, expectedOutput: "", expectedError: "Required flag not found: ", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create mock Elasticsearch client and server esClient, testServer := getMockClient(t, tt.handler) defer testServer.Close() // Initialize mock controller mockCtrl := gomock.NewController(t) // Create mock client factory mockClientFactory := NewMockClientFactory(mockCtrl) // Create test IO handler to capture output ioHandler := &testIOHandler{} // Set up the CLI app app := NewCliApp(mockClientFactory, WithIOHandler(ioHandler)) // Expect ElasticSearchClient to return the mock client created by getMockClient tt.setupMocks(mockClientFactory, esClient) // Set up the context for the specific test case c := tt.setupContext(app) // Call GenerateReport err := GenerateReport(c) // Validate results if tt.expectedError != "" { assert.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) } else { assert.NoError(t, err) // Validate the output captured by testIOHandler assert.Equal(t, tt.expectedOutput, ioHandler.outputBytes.String()) } os.Remove("test-report.csv") }) } } func TestGenerateHTMLReport_RowSpanLogic(t *testing.T) { // Prepare headers and tableData to trigger the rowspan logic headers := []string{"Domain", "Status", "Count"} tableData := [][]string{ {"domain1", "open", "10"}, {"domain1", "open", "15"}, {"domain2", "closed", "20"}, {"domain2", "closed", "25"}, } // Prepare temp file to write HTML report tempFile, err := os.CreateTemp("", "test_report_*.html") assert.NoError(t, err) defer os.Remove(tempFile.Name()) // Clean up // Call generateHTMLReport with numBuckKeys to control the column collapsing err = generateHTMLReport(tempFile.Name(), 2, false, headers, tableData) assert.NoError(t, err) // Read and validate the generated HTML content content, err := os.ReadFile(tempFile.Name()) assert.NoError(t, err) // Remove all newlines and spaces to simplify comparison actualContent := string(content) actualContent = removeWhitespace(actualContent) // Expected HTML content (also simplified by removing whitespace) expectedHTMLStructure := removeWhitespace(` domain1open10 open15`) // Validate the rowspan logic was applied correctly assert.Contains(t, actualContent, expectedHTMLStructure) } // Helper function to remove all whitespace from a string func removeWhitespace(input string) string { return strings.ReplaceAll(strings.ReplaceAll(input, "\n", ""), "\t", "") } ================================================ FILE: tools/cli/admin_failover_commands.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "context" "encoding/json" "fmt" "os/user" "time" "github.com/pborman/uuid" "github.com/urfave/cli/v2" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/failovermanager" "github.com/uber/cadence/tools/common/commoncli" ) const ( defaultAbortReason = "Failover aborted through admin CLI" defaultBatchFailoverSize = 20 defaultBatchFailoverWaitTimeInSeconds = 30 defaultFailoverWorkflowTimeoutInSeconds = 1200 ) var ( uuidFn = uuid.New getOperatorFn = getOperator ) type startParams struct { targetCluster string sourceCluster string batchFailoverSize int batchFailoverWaitTimeInSeconds int failoverWorkflowTimeout int failoverTimeout int domains []string drillWaitTime int cron string } // AdminFailoverStart start failover workflow func AdminFailoverStart(c *cli.Context) error { tc, err := getRequiredOption(c, FlagTargetCluster) if err != nil { return commoncli.Problem("Required flag not found: ", err) } sc, err := getRequiredOption(c, FlagSourceCluster) if err != nil { return commoncli.Problem("Required flag not found: ", err) } params := &startParams{ targetCluster: tc, sourceCluster: sc, batchFailoverSize: c.Int(FlagFailoverBatchSize), batchFailoverWaitTimeInSeconds: c.Int(FlagFailoverWaitTime), failoverTimeout: c.Int(FlagFailoverTimeout), failoverWorkflowTimeout: c.Int(FlagExecutionTimeout), domains: c.StringSlice(FlagFailoverDomains), drillWaitTime: c.Int(FlagFailoverDrillWaitTime), cron: c.String(FlagCronSchedule), } return failoverStart(c, params) } // AdminFailoverPause pause failover workflow func AdminFailoverPause(c *cli.Context) error { err := executePauseOrResume(c, getFailoverWorkflowID(c), true) if err != nil { return commoncli.Problem("Failed to pause failover workflow", err) } fmt.Println("Failover paused on " + getFailoverWorkflowID(c)) return nil } // AdminFailoverResume resume a paused failover workflow func AdminFailoverResume(c *cli.Context) error { err := executePauseOrResume(c, getFailoverWorkflowID(c), false) if err != nil { return commoncli.Problem("Failed to resume failover workflow", err) } fmt.Println("Failover resumed on " + getFailoverWorkflowID(c)) return nil } // AdminFailoverQuery query a failover workflow func AdminFailoverQuery(c *cli.Context) error { client, err := getCadenceClient(c) if err != nil { return err } tcCtx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } workflowID := getFailoverWorkflowID(c) runID := getRunID(c) result, err := query(tcCtx, client, workflowID, runID) if err != nil { return err } request := &types.DescribeWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, } descResp, err := client.DescribeWorkflowExecution(tcCtx, request) if err != nil { return commoncli.Problem("Failed to describe workflow", err) } if isWorkflowTerminated(descResp) { result.State = failovermanager.WorkflowAborted } prettyPrintJSONObject(getDeps(c).Output(), result) return nil } // AdminFailoverAbort abort a failover workflow func AdminFailoverAbort(c *cli.Context) error { client, err := getCadenceClient(c) if err != nil { return err } tcCtx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } reason := c.String(FlagReason) if len(reason) == 0 { reason = defaultAbortReason } workflowID := getFailoverWorkflowID(c) runID := getRunID(c) request := &types.TerminateWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Reason: reason, } err = client.TerminateWorkflowExecution(tcCtx, request) if err != nil { return commoncli.Problem("Failed to abort failover workflow", err) } fmt.Println("Failover aborted") return nil } // AdminFailoverRollback rollback a failover run func AdminFailoverRollback(c *cli.Context) error { client, err := getCadenceClient(c) if err != nil { return err } tcCtx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } runID := getRunID(c) queryResult, err := query(tcCtx, client, failovermanager.FailoverWorkflowID, runID) if err != nil { return err } if isWorkflowRunning(queryResult) { request := &types.TerminateWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: failovermanager.FailoverWorkflowID, RunID: runID, }, Reason: "Rollback", Identity: getCliIdentity(), } err := client.TerminateWorkflowExecution(tcCtx, request) if err != nil { return commoncli.Problem("Failed to terminate failover workflow", err) } } // query again queryResult, err = query(tcCtx, client, failovermanager.FailoverWorkflowID, runID) if err != nil { return err } var rollbackDomains []string // rollback includes both success and failed domains to make sure no leftover domains rollbackDomains = append(rollbackDomains, queryResult.SuccessDomains...) rollbackDomains = append(rollbackDomains, queryResult.FailedDomains...) params := &startParams{ targetCluster: queryResult.SourceCluster, sourceCluster: queryResult.TargetCluster, domains: rollbackDomains, batchFailoverSize: c.Int(FlagFailoverBatchSize), batchFailoverWaitTimeInSeconds: c.Int(FlagFailoverWaitTime), failoverTimeout: c.Int(FlagFailoverTimeout), failoverWorkflowTimeout: c.Int(FlagExecutionTimeout), } return failoverStart(c, params) } // AdminFailoverList list failover runs func AdminFailoverList(c *cli.Context) error { if err := c.Set(FlagWorkflowID, getFailoverWorkflowID(c)); err != nil { return err } if err := c.Set(FlagDomain, constants.SystemLocalDomainName); err != nil { return err } return ListWorkflow(c) } func query( tcCtx context.Context, client frontend.Client, workflowID string, runID string) (*failovermanager.QueryResult, error) { request := &types.QueryWorkflowRequest{ Domain: constants.SystemLocalDomainName, Execution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, Query: &types.WorkflowQuery{ QueryType: failovermanager.QueryType, }, } queryResp, err := client.QueryWorkflow(tcCtx, request) if err != nil { return nil, commoncli.Problem("Failed to query failover workflow", err) } if queryResp.GetQueryResult() == nil { return nil, commoncli.Problem("QueryResult has no value", nil) } var queryResult failovermanager.QueryResult err = json.Unmarshal(queryResp.GetQueryResult(), &queryResult) if err != nil { return nil, commoncli.Problem("Unable to deserialize QueryResult", nil) } return &queryResult, nil } func isWorkflowRunning(queryResult *failovermanager.QueryResult) bool { return queryResult.State == failovermanager.WorkflowRunning || queryResult.State == failovermanager.WorkflowPaused } func getCadenceClient(c *cli.Context) (frontend.Client, error) { svcClient, err := getDeps(c).ServerFrontendClient(c) if err != nil { return nil, err } return svcClient, nil } func getRunID(c *cli.Context) string { if c.IsSet(FlagRunID) { return c.String(FlagRunID) } return "" } func failoverStart(c *cli.Context, params *startParams) error { if err := validateStartParams(params); err != nil { return commoncli.Problem("Invalid input parameters", err) } workflowID := failovermanager.FailoverWorkflowID targetCluster := params.targetCluster sourceCluster := params.sourceCluster batchFailoverSize := params.batchFailoverSize batchFailoverWaitTimeInSeconds := params.batchFailoverWaitTimeInSeconds workflowTimeout := int32(params.failoverWorkflowTimeout) domains := params.domains drillWaitTime := time.Duration(params.drillWaitTime) * time.Second var gracefulFailoverTimeoutInSeconds *int32 if params.failoverTimeout > 0 { gracefulFailoverTimeoutInSeconds = common.Int32Ptr(int32(params.failoverTimeout)) } client, err := getCadenceClient(c) if err != nil { return err } tcCtx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } op, err := getOperatorFn() if err != nil { return commoncli.Problem("Error in getting operator: ", err) } memo, err := getWorkflowMemo(map[string]interface{}{ constants.MemoKeyForOperator: op, }) if err != nil { return commoncli.Problem("Failed to serialize memo", err) } request := &types.StartWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, RequestID: uuidFn(), WorkflowID: workflowID, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), TaskList: &types.TaskList{Name: failovermanager.TaskListName}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(workflowTimeout), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(defaultDecisionTimeoutInSeconds), Memo: memo, WorkflowType: &types.WorkflowType{Name: failovermanager.FailoverWorkflowTypeName}, } if params.drillWaitTime > 0 { request.WorkflowID = failovermanager.DrillWorkflowID request.CronSchedule = params.cron } else { if len(params.cron) > 0 { return commoncli.Problem("The drill wait time is required when cron is specified.", nil) } // block if there is an on-going failover drill if err := executePauseOrResume(c, failovermanager.DrillWorkflowID, true); err != nil { switch err.(type) { case *types.EntityNotExistsError: break case *types.WorkflowExecutionAlreadyCompletedError: break default: return commoncli.Problem("Failed to send pause signal to drill workflow", err) } } } foParams := failovermanager.FailoverParams{ TargetCluster: targetCluster, SourceCluster: sourceCluster, BatchFailoverSize: batchFailoverSize, BatchFailoverWaitTimeInSeconds: batchFailoverWaitTimeInSeconds, Domains: domains, DrillWaitTime: drillWaitTime, GracefulFailoverTimeoutInSeconds: gracefulFailoverTimeoutInSeconds, } input, err := json.Marshal(foParams) if err != nil { return commoncli.Problem("Failed to serialize Failover Params", err) } request.Input = input wf, err := client.StartWorkflowExecution(tcCtx, request) if err != nil { return commoncli.Problem("Failed to start failover workflow", err) } fmt.Println("Failover workflow started") fmt.Println("wid: " + workflowID) fmt.Println("rid: " + wf.GetRunID()) return nil } func getFailoverWorkflowID(c *cli.Context) string { if c.Bool(FlagFailoverDrill) { return failovermanager.DrillWorkflowID } return failovermanager.FailoverWorkflowID } func getOperator() (string, error) { user, err := user.Current() if err != nil { return "", fmt.Errorf("failed to get operator info %w", err) } return fmt.Sprintf("%s (username: %s)", user.Name, user.Username), nil } func isWorkflowTerminated(descResp *types.DescribeWorkflowExecutionResponse) bool { return types.WorkflowExecutionCloseStatusTerminated.String() == descResp.GetWorkflowExecutionInfo().GetCloseStatus().String() } func executePauseOrResume(c *cli.Context, workflowID string, isPause bool) error { client, err := getCadenceClient(c) if err != nil { return err } tcCtx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } runID := getRunID(c) var signalName string if isPause { signalName = failovermanager.PauseSignal } else { signalName = failovermanager.ResumeSignal } request := &types.SignalWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: workflowID, RunID: runID, }, SignalName: signalName, Identity: getCliIdentity(), } return client.SignalWorkflowExecution(tcCtx, request) } func validateStartParams(params *startParams) error { if len(params.targetCluster) == 0 { return fmt.Errorf("targetCluster is not provided: %v", nil) } if len(params.sourceCluster) == 0 { return fmt.Errorf("sourceCluster is not provided: %v", nil) } if params.targetCluster == params.sourceCluster { return fmt.Errorf("targetCluster is same as sourceCluster: %v", nil) } if params.batchFailoverSize <= 0 { params.batchFailoverSize = defaultBatchFailoverSize } if params.batchFailoverWaitTimeInSeconds <= 0 { params.batchFailoverWaitTimeInSeconds = defaultBatchFailoverWaitTimeInSeconds } if params.failoverWorkflowTimeout <= 0 { params.failoverWorkflowTimeout = defaultFailoverWorkflowTimeoutInSeconds } return nil } ================================================ FILE: tools/cli/admin_failover_commands_test.go ================================================ // Copyright (c) 2017-2020 Uber Technologies Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "context" "encoding/json" "fmt" "strconv" "strings" "testing" "github.com/google/go-cmp/cmp" "go.uber.org/mock/gomock" "go.uber.org/yarpc" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/failovermanager" ) func TestAdminFailoverStart(t *testing.T) { oldUUIDFn := uuidFn uuidFn = func() string { return "test-uuid" } oldGetOperatorFn := getOperatorFn getOperatorFn = func() (string, error) { return "test-user", nil } defer func() { uuidFn = oldUUIDFn getOperatorFn = oldGetOperatorFn }() tests := []struct { desc string sourceCluster string targetCluster string failoverBatchSize int failoverWaitTime int gracefulFailoverTimeout int failoverWFTimeout int failoverDomains []string failoverDrillWaitTime int failoverCron string runID string mockFn func(*testing.T, *frontend.MockClient) wantErr bool }{ { desc: "success", sourceCluster: "cluster1", targetCluster: "cluster2", failoverBatchSize: 10, failoverWaitTime: 120, gracefulFailoverTimeout: 300, failoverWFTimeout: 600, failoverDomains: []string{"domain1", "domain2"}, mockFn: func(t *testing.T, m *frontend.MockClient) { // first drill workflow will be signalled to pause in case it is running. m.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) // then failover workflow will be started wantReq := &types.StartWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, RequestID: "test-uuid", WorkflowID: failovermanager.FailoverWorkflowID, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), TaskList: &types.TaskList{Name: failovermanager.TaskListName}, Input: []byte(`{"TargetCluster":"cluster2","SourceCluster":"cluster1","BatchFailoverSize":10,"BatchFailoverWaitTimeInSeconds":120,"Domains":["domain1","domain2"],"DrillWaitTime":0,"GracefulFailoverTimeoutInSeconds":300}`), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(600), // == failoverWFTimeout TaskStartToCloseTimeoutSeconds: common.Int32Ptr(defaultDecisionTimeoutInSeconds), Memo: mustGetWorkflowMemo(t, map[string]interface{}{ constants.MemoKeyForOperator: "test-user", }), WorkflowType: &types.WorkflowType{Name: failovermanager.FailoverWorkflowTypeName}, } resp := &types.StartWorkflowExecutionResponse{} m.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.StartWorkflowExecutionRequest, opts ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return resp, nil }).Times(1) }, }, { desc: "startworkflow fails", wantErr: true, sourceCluster: "cluster1", targetCluster: "cluster2", mockFn: func(t *testing.T, m *frontend.MockClient) { // first drill workflow will be signalled to pause in case it is running. m.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) // then failover workflow will be started m.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.StartWorkflowExecutionRequest, opts ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { return nil, fmt.Errorf("failed to start workflow") }).Times(1) }, }, { desc: "source and target cluster same", wantErr: true, sourceCluster: "cluster1", targetCluster: "cluster1", mockFn: func(t *testing.T, m *frontend.MockClient) { // no frontend calls due to validation failure }, }, { desc: "no source cluster specified", wantErr: true, sourceCluster: "", targetCluster: "cluster2", mockFn: func(t *testing.T, m *frontend.MockClient) { // no frontend calls due to validation failure }, }, { desc: "no target cluster specified", wantErr: true, sourceCluster: "cluster1", targetCluster: "", mockFn: func(t *testing.T, m *frontend.MockClient) { // no frontend calls due to validation failure }, }, { desc: "success with cron", sourceCluster: "cluster1", targetCluster: "cluster2", failoverBatchSize: 10, failoverWaitTime: 120, gracefulFailoverTimeout: 300, failoverWFTimeout: 600, failoverDrillWaitTime: 30, failoverCron: "0 0 * * *", failoverDomains: []string{"domain1", "domain2"}, mockFn: func(t *testing.T, m *frontend.MockClient) { // failover drill workflow will be started wantReq := &types.StartWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, RequestID: "test-uuid", CronSchedule: "0 0 * * *", WorkflowID: failovermanager.DrillWorkflowID, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), TaskList: &types.TaskList{Name: failovermanager.TaskListName}, Input: []byte(`{"TargetCluster":"cluster2","SourceCluster":"cluster1","BatchFailoverSize":10,"BatchFailoverWaitTimeInSeconds":120,"Domains":["domain1","domain2"],"DrillWaitTime":30000000000,"GracefulFailoverTimeoutInSeconds":300}`), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(600), // == failoverWFTimeout TaskStartToCloseTimeoutSeconds: common.Int32Ptr(defaultDecisionTimeoutInSeconds), Memo: mustGetWorkflowMemo(t, map[string]interface{}{ constants.MemoKeyForOperator: "test-user", }), WorkflowType: &types.WorkflowType{Name: failovermanager.FailoverWorkflowTypeName}, } resp := &types.StartWorkflowExecutionResponse{} m.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.StartWorkflowExecutionRequest, opts ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return resp, nil }).Times(1) }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) frontendCl := frontend.NewMockClient(ctrl) // Set up mocks for the current test case tc.mockFn(t, frontendCl) // Create mock app with clientFactoryMock, including any deps errors app := NewCliApp(&clientFactoryMock{ serverFrontendClient: frontendCl, }) args := []string{"", "admin", "cluster", "failover", "start", "--sc", tc.sourceCluster, "--tc", tc.targetCluster, "--failover_batch_size", strconv.Itoa(tc.failoverBatchSize), "--failover_wait_time_second", strconv.Itoa(tc.failoverWaitTime), "--failover_timeout_seconds", strconv.Itoa(tc.gracefulFailoverTimeout), "--execution_timeout", strconv.Itoa(tc.failoverWFTimeout), "--domains", strings.Join(tc.failoverDomains, ","), "--failover_drill_wait_second", strconv.Itoa(tc.failoverDrillWaitTime), "--cron", tc.failoverCron, } err := app.Run(args) if (err != nil) != tc.wantErr { t.Errorf("Got error: %v, wantErr?: %v", err, tc.wantErr) } }) } } func TestAdminFailoverPauseResume(t *testing.T) { tests := []struct { desc string runID string pauseOrResume string mockFn func(*testing.T, *frontend.MockClient) wantErr bool }{ { desc: "pause success", pauseOrResume: "pause", runID: "runid1", mockFn: func(t *testing.T, m *frontend.MockClient) { m.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.SignalWorkflowExecutionRequest, opts ...yarpc.CallOption) error { wantReq := &types.SignalWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: failovermanager.FailoverWorkflowID, RunID: "runid1", }, SignalName: failovermanager.PauseSignal, Identity: getCliIdentity(), } if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return nil }).Times(1) }, }, { desc: "pause signal workflow fails", pauseOrResume: "pause", wantErr: true, mockFn: func(t *testing.T, m *frontend.MockClient) { m.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, r *types.SignalWorkflowExecutionRequest, opts ...yarpc.CallOption) error { return fmt.Errorf("failed to signal workflow") }).Times(1) }, }, { desc: "resume success", pauseOrResume: "resume", runID: "runid1", mockFn: func(t *testing.T, m *frontend.MockClient) { wantReq := &types.SignalWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: failovermanager.FailoverWorkflowID, RunID: "runid1", }, SignalName: failovermanager.ResumeSignal, Identity: getCliIdentity(), } m.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.SignalWorkflowExecutionRequest, opts ...yarpc.CallOption) error { if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return nil }).Times(1) }, }, { desc: "resume signal workflow fails", pauseOrResume: "resume", wantErr: true, mockFn: func(t *testing.T, m *frontend.MockClient) { m.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, r *types.SignalWorkflowExecutionRequest, opts ...yarpc.CallOption) error { return fmt.Errorf("failed to signal workflow") }).Times(1) }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) frontendCl := frontend.NewMockClient(ctrl) // Set up mocks for the current test case tc.mockFn(t, frontendCl) // Create mock app with clientFactoryMock, including any deps errors app := NewCliApp(&clientFactoryMock{ serverFrontendClient: frontendCl, }) args := []string{"", "admin", "cluster", "failover", tc.pauseOrResume, "--rid", tc.runID, } err := app.Run(args) if (err != nil) != tc.wantErr { t.Errorf("Got error: %v, wantErr?: %v", err, tc.wantErr) } }) } } func TestAdminFailoverQuery(t *testing.T) { queryResult := failovermanager.QueryResult{ TotalDomains: 10, Success: 2, Failed: 3, } tests := []struct { desc string mockFn func(*testing.T, *frontend.MockClient) wantErr bool }{ { desc: "success", mockFn: func(t *testing.T, m *frontend.MockClient) { m.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.QueryWorkflowRequest, opts ...yarpc.CallOption) (*types.QueryWorkflowResponse, error) { wantReq := &types.QueryWorkflowRequest{ Domain: constants.SystemLocalDomainName, Execution: &types.WorkflowExecution{ WorkflowID: failovermanager.FailoverWorkflowID, RunID: "", }, Query: &types.WorkflowQuery{ QueryType: failovermanager.QueryType, }, } if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return &types.QueryWorkflowResponse{ QueryResult: mustMarshalQueryResult(t, queryResult), }, nil }).Times(1) m.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption) (*types.DescribeWorkflowExecutionResponse, error) { wantReq := &types.DescribeWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, Execution: &types.WorkflowExecution{ WorkflowID: failovermanager.FailoverWorkflowID, RunID: "", }, } if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{ CloseStatus: types.WorkflowExecutionCloseStatusTerminated.Ptr(), }, }, nil }).Times(1) }, }, { desc: "query failed", wantErr: true, mockFn: func(t *testing.T, m *frontend.MockClient) { m.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.QueryWorkflowRequest, opts ...yarpc.CallOption) (*types.QueryWorkflowResponse, error) { return nil, fmt.Errorf("failed to query workflow") }).Times(1) }, }, { desc: "describe failed", wantErr: true, mockFn: func(t *testing.T, m *frontend.MockClient) { m.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.QueryWorkflowRequest, opts ...yarpc.CallOption) (*types.QueryWorkflowResponse, error) { wantReq := &types.QueryWorkflowRequest{ Domain: constants.SystemLocalDomainName, Execution: &types.WorkflowExecution{ WorkflowID: failovermanager.FailoverWorkflowID, RunID: "", }, Query: &types.WorkflowQuery{ QueryType: failovermanager.QueryType, }, } if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return &types.QueryWorkflowResponse{ QueryResult: mustMarshalQueryResult(t, queryResult), }, nil }).Times(1) m.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.DescribeWorkflowExecutionRequest, opts ...yarpc.CallOption) (*types.DescribeWorkflowExecutionResponse, error) { return nil, fmt.Errorf("failed to describe workflow") }).Times(1) }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) frontendCl := frontend.NewMockClient(ctrl) // Set up mocks for the current test case tc.mockFn(t, frontendCl) // Create mock app with clientFactoryMock, including any deps errors app := NewCliApp(&clientFactoryMock{ serverFrontendClient: frontendCl, }) args := []string{"", "admin", "cluster", "failover", "query"} err := app.Run(args) if (err != nil) != tc.wantErr { t.Errorf("Got error: %v, wantErr?: %v", err, tc.wantErr) } }) } } func TestAdminFailoverAbort(t *testing.T) { tests := []struct { desc string mockFn func(*testing.T, *frontend.MockClient) wantErr bool }{ { desc: "success", mockFn: func(t *testing.T, m *frontend.MockClient) { m.EXPECT().TerminateWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.TerminateWorkflowExecutionRequest, opts ...yarpc.CallOption) error { wantReq := &types.TerminateWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: failovermanager.FailoverWorkflowID, RunID: "", }, Reason: "Failover aborted through admin CLI", } if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return nil }).Times(1) }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) frontendCl := frontend.NewMockClient(ctrl) // Set up mocks for the current test case tc.mockFn(t, frontendCl) // Create mock app with clientFactoryMock, including any deps errors app := NewCliApp(&clientFactoryMock{ serverFrontendClient: frontendCl, }) args := []string{"", "admin", "cluster", "failover", "abort"} err := app.Run(args) if (err != nil) != tc.wantErr { t.Errorf("Got error: %v, wantErr?: %v", err, tc.wantErr) } }) } } func TestAdminFailoverRollback(t *testing.T) { oldUUIDFn := uuidFn uuidFn = func() string { return "test-uuid" } oldGetOperatorFn := getOperatorFn getOperatorFn = func() (string, error) { return "test-user", nil } defer func() { uuidFn = oldUUIDFn getOperatorFn = oldGetOperatorFn }() tests := []struct { desc string mockFn func(*testing.T, *frontend.MockClient) wantErr bool }{ { desc: "success", mockFn: func(t *testing.T, m *frontend.MockClient) { // query to check if it's running. m.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.QueryWorkflowRequest, opts ...yarpc.CallOption) (*types.QueryWorkflowResponse, error) { wantReq := &types.QueryWorkflowRequest{ Domain: constants.SystemLocalDomainName, Execution: &types.WorkflowExecution{ WorkflowID: failovermanager.FailoverWorkflowID, RunID: "", }, Query: &types.WorkflowQuery{ QueryType: failovermanager.QueryType, }, } if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return &types.QueryWorkflowResponse{ QueryResult: mustMarshalQueryResult(t, failovermanager.QueryResult{ State: failovermanager.WorkflowRunning, }), }, nil }).Times(1) // terminate since it's running m.EXPECT().TerminateWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.TerminateWorkflowExecutionRequest, opts ...yarpc.CallOption) error { wantReq := &types.TerminateWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, WorkflowExecution: &types.WorkflowExecution{ WorkflowID: failovermanager.FailoverWorkflowID, RunID: "", }, Reason: "Rollback", Identity: getCliIdentity(), } if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return nil }).Times(1) // query again to get domains m.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.QueryWorkflowRequest, opts ...yarpc.CallOption) (*types.QueryWorkflowResponse, error) { wantReq := &types.QueryWorkflowRequest{ Domain: constants.SystemLocalDomainName, Execution: &types.WorkflowExecution{ WorkflowID: failovermanager.FailoverWorkflowID, RunID: "", }, Query: &types.WorkflowQuery{ QueryType: failovermanager.QueryType, }, } if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return &types.QueryWorkflowResponse{ QueryResult: mustMarshalQueryResult(t, failovermanager.QueryResult{ State: failovermanager.WorkflowAborted, SourceCluster: "cluster1", TargetCluster: "cluster2", SuccessDomains: []string{"domain1", "domain2"}, FailedDomains: []string{"domain3"}, }), }, nil }).Times(1) // failback for success+failed domains // // first drill workflow will be signalled to pause in case it is running. m.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil).Times(1) // then failover workflow will be started to perform failback resp := &types.StartWorkflowExecutionResponse{} m.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, gotReq *types.StartWorkflowExecutionRequest, opts ...yarpc.CallOption) (*types.StartWorkflowExecutionResponse, error) { wantReq := &types.StartWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, RequestID: "test-uuid", WorkflowID: failovermanager.FailoverWorkflowID, WorkflowIDReusePolicy: types.WorkflowIDReusePolicyAllowDuplicate.Ptr(), TaskList: &types.TaskList{Name: failovermanager.TaskListName}, Input: mustMarshalFailoverParams(t, failovermanager.FailoverParams{ SourceCluster: "cluster2", TargetCluster: "cluster1", Domains: []string{"domain1", "domain2", "domain3"}, BatchFailoverSize: 20, // default value will be used BatchFailoverWaitTimeInSeconds: 30, // default value will be used }), ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(1200), // default value will be used TaskStartToCloseTimeoutSeconds: common.Int32Ptr(defaultDecisionTimeoutInSeconds), Memo: mustGetWorkflowMemo(t, map[string]interface{}{ constants.MemoKeyForOperator: "test-user", }), WorkflowType: &types.WorkflowType{Name: failovermanager.FailoverWorkflowTypeName}, } if diff := cmp.Diff(wantReq, gotReq); diff != "" { t.Fatalf("Request mismatch (-want +got):\n%s", diff) } return resp, nil }).Times(1) }, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) frontendCl := frontend.NewMockClient(ctrl) // Set up mocks for the current test case tc.mockFn(t, frontendCl) // Create mock app with clientFactoryMock, including any deps errors app := NewCliApp(&clientFactoryMock{ serverFrontendClient: frontendCl, }) args := []string{"", "admin", "cluster", "failover", "rollback"} err := app.Run(args) if (err != nil) != tc.wantErr { t.Errorf("Got error: %v, wantErr?: %v", err, tc.wantErr) } }) } } func mustGetWorkflowMemo(t *testing.T, input map[string]interface{}) *types.Memo { memo, err := getWorkflowMemo(input) if err != nil { t.Fatalf("failed to get workflow memo: %v", err) } return memo } func mustMarshalQueryResult(t *testing.T, queryResult failovermanager.QueryResult) []byte { res, err := json.Marshal(queryResult) if err != nil { t.Fatalf("failed to marshal query result: %v", err) } return res } func mustMarshalFailoverParams(t *testing.T, p failovermanager.FailoverParams) []byte { res, err := json.Marshal(p) if err != nil { t.Fatalf("failed to marshal failover params: %v", err) } return res } ================================================ FILE: tools/cli/admin_kafka_commands.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "bufio" "bytes" "context" "encoding/json" "errors" "fmt" "io" "os" "regexp" "sync/atomic" "time" "github.com/urfave/cli/v2" "go.uber.org/thriftrw/protocol/binary" "go.uber.org/thriftrw/wire" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/.gen/go/replicator" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/common/types/mapper/thrift" "github.com/uber/cadence/tools/common/commoncli" ) type ( filterFn func(*types.ReplicationTask) bool filterFnForVisibility func(*indexer.Message) bool kafkaMessageType int historyV2Task struct { Task *types.ReplicationTask Events []*types.HistoryEvent NewRunEvents []*types.HistoryEvent } ) const ( kafkaMessageTypeReplicationTask kafkaMessageType = iota kafkaMessageTypeVisibilityMsg ) const ( bufferSize = 8192 preambleVersion0 byte = 0x59 malformedMessage = "Input was malformed" chanBufferSize = 10000 maxRereplicateEventID = 999999 defaultResendContextTimeout = 30 * time.Second ) var ( r = regexp.MustCompile(`Partition: .*?, Offset: .*?, Key: .*?`) ) type writerChannel struct { Type kafkaMessageType ReplicationTaskChannel chan *types.ReplicationTask VisibilityMsgChannel chan *indexer.Message } func newWriterChannel(messageType kafkaMessageType) *writerChannel { ch := &writerChannel{ Type: messageType, } switch messageType { case kafkaMessageTypeReplicationTask: ch.ReplicationTaskChannel = make(chan *types.ReplicationTask, chanBufferSize) case kafkaMessageTypeVisibilityMsg: ch.VisibilityMsgChannel = make(chan *indexer.Message, chanBufferSize) } return ch } func (ch *writerChannel) Close() { if ch.ReplicationTaskChannel != nil { close(ch.ReplicationTaskChannel) } if ch.VisibilityMsgChannel != nil { close(ch.VisibilityMsgChannel) } } // AdminKafkaParse parses the output of k8read and outputs replication tasks func AdminKafkaParse(c *cli.Context) error { inputFile, err := getInputFile(c.String(FlagInputFile)) defer inputFile.Close() if err != nil { return commoncli.Problem("Error in Admin kafka parse: ", err) } outputFile, err := getOutputFile(c.String(FlagOutputFilename)) defer outputFile.Close() if err != nil { return commoncli.Problem("Error in Admin kafka parse: ", err) } readerCh := make(chan []byte, chanBufferSize) writerCh := newWriterChannel(kafkaMessageType(c.Int(FlagMessageType))) doneCh := make(chan struct{}) serializer := persistence.NewPayloadSerializer() var skippedCount int32 skipErrMode := c.Bool(FlagSkipErrorMode) go startReader(inputFile, readerCh) go startParser(readerCh, writerCh, skipErrMode, &skippedCount) go startWriter(outputFile, writerCh, doneCh, &skippedCount, serializer, c) <-doneCh if skipErrMode { fmt.Printf("%v messages were skipped due to errors in parsing", atomic.LoadInt32(&skippedCount)) } return nil } func buildFilterFn(workflowID, runID string) filterFn { return func(task *types.ReplicationTask) bool { if len(workflowID) != 0 || len(runID) != 0 { if task.GetHistoryTaskV2Attributes() == nil { return false } } if len(workflowID) != 0 && task.GetHistoryTaskV2Attributes().WorkflowID != workflowID { return false } if len(runID) != 0 && task.GetHistoryTaskV2Attributes().RunID != runID { return false } return true } } func buildFilterFnForVisibility(workflowID, runID string) filterFnForVisibility { return func(msg *indexer.Message) bool { if len(workflowID) != 0 && msg.GetWorkflowID() != workflowID { return false } if len(runID) != 0 && msg.GetRunID() != runID { return false } return true } } func getOutputFile(outputFile string) (*os.File, error) { if len(outputFile) == 0 { return os.Stdout, nil } f, err := os.Create(outputFile) if err != nil { return nil, fmt.Errorf("failed to create output file %w", err) } return f, nil } func startReader(file io.Reader, readerCh chan<- []byte) error { defer close(readerCh) reader := bufio.NewReader(file) for { buf := make([]byte, bufferSize) n, err := reader.Read(buf) if err != nil { if err != io.EOF { return fmt.Errorf("failed to read from reader: %w", err) } break } buf = buf[:n] readerCh <- buf } return nil } func startParser(readerCh <-chan []byte, writerCh *writerChannel, skipErrors bool, skippedCount *int32) error { defer writerCh.Close() var buffer []byte for data := range readerCh { buffer = append(buffer, data...) parsedData, nextBuffer, err := splitBuffer(buffer) if err != nil { if skipErrors { atomic.AddInt32(skippedCount, 1) continue } else { return fmt.Errorf("error in splitBuffer: %w", err) } } buffer = nextBuffer parse(parsedData, skipErrors, skippedCount, writerCh) } parse(buffer, skipErrors, skippedCount, writerCh) return nil } func startWriter( outputFile *os.File, writerCh *writerChannel, doneCh chan struct{}, skippedCount *int32, serializer persistence.PayloadSerializer, c *cli.Context, ) error { defer close(doneCh) skipErrMode := c.Bool(FlagSkipErrorMode) headerMode := c.Bool(FlagHeadersMode) switch writerCh.Type { case kafkaMessageTypeReplicationTask: return writeReplicationTask(outputFile, writerCh, skippedCount, skipErrMode, headerMode, serializer, c) case kafkaMessageTypeVisibilityMsg: return writeVisibilityMessage(outputFile, writerCh, skippedCount, skipErrMode, headerMode, c) default: return fmt.Errorf("unsupported message type: %v", writerCh.Type) } } func writeReplicationTask( outputFile *os.File, writerCh *writerChannel, skippedCount *int32, skipErrMode bool, headerMode bool, serializer persistence.PayloadSerializer, c *cli.Context, ) error { filter := buildFilterFn(c.String(FlagWorkflowID), c.String(FlagRunID)) for task := range writerCh.ReplicationTaskChannel { if filter(task) { jsonStr, err := decodeReplicationTask(task, serializer) if err != nil { if !skipErrMode { return fmt.Errorf("malformed message, failed to encode into json: %w", err) } atomic.AddInt32(skippedCount, 1) continue } var outStr string if !headerMode { outStr = string(jsonStr) } else { outStr = fmt.Sprintf( "%v, %v, %v", task.GetHistoryTaskV2Attributes().DomainID, task.GetHistoryTaskV2Attributes().WorkflowID, task.GetHistoryTaskV2Attributes().RunID, ) } _, err = outputFile.WriteString(fmt.Sprintf("%v\n", outStr)) if err != nil { return fmt.Errorf("failed to write to file: %w", err) } } } return nil } func writeVisibilityMessage( outputFile *os.File, writerCh *writerChannel, skippedCount *int32, skipErrMode bool, headerMode bool, c *cli.Context, ) error { filter := buildFilterFnForVisibility(c.String(FlagWorkflowID), c.String(FlagRunID)) for msg := range writerCh.VisibilityMsgChannel { if filter(msg) { jsonStr, err := json.Marshal(msg) if err != nil { if !skipErrMode { return fmt.Errorf("malformed message: failed to encode into json, err: %w", err) } atomic.AddInt32(skippedCount, 1) continue } var outStr string if !headerMode { outStr = string(jsonStr) } else { outStr = fmt.Sprintf( "%v, %v, %v, %v, %v", msg.GetDomainID(), msg.GetWorkflowID(), msg.GetRunID(), msg.GetMessageType().String(), msg.GetVersion(), ) } _, err = outputFile.WriteString(fmt.Sprintf("%v\n", outStr)) if err != nil { return fmt.Errorf("failed to write to file: %w", err) } } } return nil } func splitBuffer(buffer []byte) ([]byte, []byte, error) { matches := r.FindAllIndex(buffer, -1) if len(matches) == 0 { return nil, nil, fmt.Errorf("header not found, did you generate dump with -v") } splitIndex := matches[len(matches)-1][0] return buffer[:splitIndex], buffer[splitIndex:], nil } func parse(bytes []byte, skipErrors bool, skippedCount *int32, writerCh *writerChannel) error { messages, skippedGetMsgCount, err := getMessages(bytes, skipErrors) if err != nil { return fmt.Errorf("parsing failed: %w", err) } switch writerCh.Type { case kafkaMessageTypeReplicationTask: msgs, skippedDeserializeCount, err := deserializeMessages(messages, skipErrors) if err != nil { return fmt.Errorf("parsing failed: %w", err) } atomic.AddInt32(skippedCount, skippedGetMsgCount+skippedDeserializeCount) for _, msg := range msgs { writerCh.ReplicationTaskChannel <- msg } case kafkaMessageTypeVisibilityMsg: msgs, skippedDeserializeCount, err := deserializeVisibilityMessages(messages, skipErrors) if err != nil { return fmt.Errorf("parsing failed: %w", err) } atomic.AddInt32(skippedCount, skippedGetMsgCount+skippedDeserializeCount) for _, msg := range msgs { writerCh.VisibilityMsgChannel <- msg } } return nil } func getMessages(data []byte, skipErrors bool) ([][]byte, int32, error) { str := string(data) messagesWithHeaders := r.Split(str, -1) if len(messagesWithHeaders[0]) != 0 { return nil, 0, fmt.Errorf(malformedMessage+"Error: %v", errors.New("got data chunk to handle that does not start with valid header")) } messagesWithHeaders = messagesWithHeaders[1:] var rawMessages [][]byte var skipped int32 for _, m := range messagesWithHeaders { if len(m) == 0 { return nil, 0, fmt.Errorf(malformedMessage+"Error: %v", errors.New("got empty message between valid headers")) } curr := []byte(m) messageStart := bytes.Index(curr, []byte{preambleVersion0}) if messageStart == -1 { if !skipErrors { return nil, 0, fmt.Errorf(malformedMessage+"Error: %v", errors.New("failed to find message preamble")) } skipped++ continue } rawMessages = append(rawMessages, curr[messageStart:]) } return rawMessages, skipped, nil } func deserializeMessages(messages [][]byte, skipErrors bool) ([]*types.ReplicationTask, int32, error) { var replicationTasks []*types.ReplicationTask var skipped int32 for _, m := range messages { var task replicator.ReplicationTask err := decode(m, &task) if err != nil { if !skipErrors { return nil, 0, fmt.Errorf(malformedMessage+"Error: %v", err) } skipped++ continue } replicationTasks = append(replicationTasks, thrift.ToReplicationTask(&task)) } return replicationTasks, skipped, nil } func decode(message []byte, val *replicator.ReplicationTask) error { reader := bytes.NewReader(message[1:]) wireVal, err := binary.Default.Decode(reader, wire.TStruct) if err != nil { return err } return val.FromWire(wireVal) } func deserializeVisibilityMessages(messages [][]byte, skipErrors bool) ([]*indexer.Message, int32, error) { var visibilityMessages []*indexer.Message var skipped int32 for _, m := range messages { var msg indexer.Message err := decodeVisibility(m, &msg) if err != nil { if !skipErrors { return nil, 0, fmt.Errorf(malformedMessage+"Error: %v", err) } skipped++ continue } visibilityMessages = append(visibilityMessages, &msg) } return visibilityMessages, skipped, nil } func decodeVisibility(message []byte, val *indexer.Message) error { reader := bytes.NewReader(message[1:]) wireVal, err := binary.Default.Decode(reader, wire.TStruct) if err != nil { return err } return val.FromWire(wireVal) } // ClustersConfig describes the kafka clusters type ClustersConfig struct { Clusters map[string]config.ClusterConfig TLS config.TLS } func doRereplicate( ctx context.Context, domainID string, wid string, rid string, endEventID *int64, endEventVersion *int64, sourceCluster string, adminClient admin.Client, ) error { fmt.Printf("Start rereplication for wid: %v, rid:%v \n", wid, rid) if err := adminClient.ResendReplicationTasks( ctx, &types.ResendReplicationTasksRequest{ DomainID: domainID, WorkflowID: wid, RunID: rid, RemoteCluster: sourceCluster, EndEventID: endEventID, EndVersion: endEventVersion, }, ); err != nil { return commoncli.Problem("Failed to resend ndc workflow", err) } fmt.Printf("Done rereplication for wid: %v, rid:%v \n", wid, rid) return nil } // AdminRereplicate parses will re-publish replication tasks to topic func AdminRereplicate(c *cli.Context) error { sourceCluster, err := getRequiredOption(c, FlagSourceCluster) if err != nil { return commoncli.Problem("Required flag not found: ", err) } adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } var endEventID, endVersion *int64 if c.IsSet(FlagMaxEventID) { endEventID = common.Int64Ptr(c.Int64(FlagMaxEventID) + 1) } if c.IsSet(FlagEndEventVersion) { endVersion = common.Int64Ptr(c.Int64(FlagEndEventVersion)) } domainID, err := getRequiredOption(c, FlagDomainID) if err != nil { return commoncli.Problem("Required flag not found: ", err) } wid, err := getRequiredOption(c, FlagWorkflowID) if err != nil { return commoncli.Problem("Required flag not found: ", err) } rid, err := getRequiredOption(c, FlagRunID) if err != nil { return commoncli.Problem("Required flag not found: ", err) } contextTimeout := defaultResendContextTimeout if c.IsSet(FlagContextTimeout) { contextTimeout = time.Duration(c.Int(FlagContextTimeout)) * time.Second } ctx, cancel := context.WithTimeout(c.Context, contextTimeout) defer cancel() return doRereplicate( ctx, domainID, wid, rid, endEventID, endVersion, sourceCluster, adminClient, ) } func decodeReplicationTask( task *types.ReplicationTask, serializer persistence.PayloadSerializer, ) ([]byte, error) { switch task.GetTaskType() { case types.ReplicationTaskTypeHistoryV2: historyV2 := task.GetHistoryTaskV2Attributes() events, err := serializer.DeserializeBatchEvents( persistence.NewDataBlobFromInternal(historyV2.Events), ) if err != nil { return nil, err } var newRunEvents []*types.HistoryEvent if historyV2.NewRunEvents != nil { newRunEvents, err = serializer.DeserializeBatchEvents( persistence.NewDataBlobFromInternal(historyV2.NewRunEvents), ) if err != nil { return nil, err } } historyV2.Events = nil historyV2.NewRunEvents = nil historyV2Attributes := &historyV2Task{ Task: task, Events: events, NewRunEvents: newRunEvents, } return json.Marshal(historyV2Attributes) default: return json.Marshal(task) } } ================================================ FILE: tools/cli/admin_kafka_commands_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "bytes" "context" "errors" "os" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "go.uber.org/thriftrw/protocol/binary" "go.uber.org/thriftrw/ptr" "github.com/uber/cadence/.gen/go/indexer" "github.com/uber/cadence/.gen/go/replicator" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/cli/clitest" "github.com/uber/cadence/tools/common/commoncli" ) func TestWriterChannel(t *testing.T) { t.Run("replication task type", func(t *testing.T) { ch := newWriterChannel(kafkaMessageTypeReplicationTask) assert.Equal(t, kafkaMessageType(0), ch.Type) assert.Nil(t, ch.VisibilityMsgChannel) assert.NotNil(t, ch.ReplicationTaskChannel) assert.Equal(t, 10000, cap(ch.ReplicationTaskChannel)) }) t.Run("msg visibility type", func(t *testing.T) { ch := newWriterChannel(kafkaMessageTypeVisibilityMsg) assert.Equal(t, kafkaMessageType(1), ch.Type) assert.Nil(t, ch.ReplicationTaskChannel) assert.NotNil(t, ch.VisibilityMsgChannel) assert.Equal(t, 10000, cap(ch.VisibilityMsgChannel)) }) t.Run("replication task type close channel", func(t *testing.T) { ch := newWriterChannel(kafkaMessageTypeReplicationTask) ch.ReplicationTaskChannel <- nil _, ok := <-ch.ReplicationTaskChannel assert.True(t, ok) ch.Close() _, ok = <-ch.ReplicationTaskChannel assert.False(t, ok) }) t.Run("msg visibility type close channel", func(t *testing.T) { ch := newWriterChannel(kafkaMessageTypeVisibilityMsg) ch.VisibilityMsgChannel <- nil _, ok := <-ch.VisibilityMsgChannel assert.True(t, ok) ch.Close() _, ok = <-ch.VisibilityMsgChannel assert.False(t, ok) }) } func TestBuildFilterFn(t *testing.T) { allTasks := []*types.ReplicationTask{ nil, {HistoryTaskV2Attributes: nil}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{RunID: "run-id-1"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{RunID: "run-id-2"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "my-workflow-id"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "my-workflow-id", RunID: "run-id-1"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "my-workflow-id", RunID: "run-id-2"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "alien-workflow-id"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "alien-workflow-id", RunID: "run-id-1"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "alien-workflow-id", RunID: "run-id-2"}}, } tests := []struct { name string filterFn filterFn expectedFilteredTaskList []*types.ReplicationTask }{ {"empty filter always return true", buildFilterFn("", ""), allTasks}, { "filter with workflowId only", buildFilterFn("my-workflow-id", ""), []*types.ReplicationTask{ {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "my-workflow-id"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "my-workflow-id", RunID: "run-id-1"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "my-workflow-id", RunID: "run-id-2"}}, }, }, { "filter with runId only", buildFilterFn("", "run-id-1"), []*types.ReplicationTask{ {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{RunID: "run-id-1"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "my-workflow-id", RunID: "run-id-1"}}, {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "alien-workflow-id", RunID: "run-id-1"}}, }, }, { "filter with workflow and runId", buildFilterFn("my-workflow-id", "run-id-1"), []*types.ReplicationTask{ {HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{WorkflowID: "my-workflow-id", RunID: "run-id-1"}}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var result []*types.ReplicationTask for _, task := range allTasks { if tt.filterFn(task) { result = append(result, task) } } assert.Equal(t, tt.expectedFilteredTaskList, result) }) } } func TestBuildFilterFnForVisibility(t *testing.T) { allMessages := []*indexer.Message{ nil, {}, {RunID: ptr.String("run-id-1")}, {RunID: ptr.String("run-id-2")}, {WorkflowID: ptr.String("my-workflow-id")}, {WorkflowID: ptr.String("my-workflow-id"), RunID: ptr.String("run-id-1")}, {WorkflowID: ptr.String("my-workflow-id"), RunID: ptr.String("run-id-2")}, {WorkflowID: ptr.String("alien-workflow-id")}, {WorkflowID: ptr.String("alien-workflow-id"), RunID: ptr.String("run-id-1")}, {WorkflowID: ptr.String("alien-workflow-id"), RunID: ptr.String("run-id-2")}, } tests := []struct { name string filterFn filterFnForVisibility expectedFilteredTaskList []*indexer.Message }{ {"empty filter always return true", buildFilterFnForVisibility("", ""), allMessages}, { "filter with workflowId only", buildFilterFnForVisibility("my-workflow-id", ""), []*indexer.Message{ {WorkflowID: ptr.String("my-workflow-id")}, {WorkflowID: ptr.String("my-workflow-id"), RunID: ptr.String("run-id-1")}, {WorkflowID: ptr.String("my-workflow-id"), RunID: ptr.String("run-id-2")}, }, }, { "filter with runId only", buildFilterFnForVisibility("", "run-id-1"), []*indexer.Message{ {RunID: ptr.String("run-id-1")}, {WorkflowID: ptr.String("my-workflow-id"), RunID: ptr.String("run-id-1")}, {WorkflowID: ptr.String("alien-workflow-id"), RunID: ptr.String("run-id-1")}, }, }, { "filter with workflow and runId", buildFilterFnForVisibility("my-workflow-id", "run-id-1"), []*indexer.Message{ {WorkflowID: ptr.String("my-workflow-id"), RunID: ptr.String("run-id-1")}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var result []*indexer.Message for _, task := range allMessages { if tt.filterFn(task) { result = append(result, task) } } assert.Equal(t, tt.expectedFilteredTaskList, result) }) } } func TestGetOutputFile(t *testing.T) { t.Run("returns stdout if filename is empty", func(t *testing.T) { file, err := getOutputFile("") assert.NoError(t, err) assert.Equal(t, os.Stdout, file) }) t.Run("creates a file", func(t *testing.T) { file, err := getOutputFile("test.txt") defer func() { if file != nil { _ = file.Close() _ = os.Remove("test.txt") } }() assert.NoError(t, err) assert.NotNil(t, file) info, err := file.Stat() assert.Equal(t, "test.txt", info.Name()) }) t.Run("fails to create a file", func(t *testing.T) { file, err := getOutputFile("/non/existent/directory/test.txt") assert.EqualError(t, err, "failed to create output file open /non/existent/directory/test.txt: no such file or directory") assert.Nil(t, file) }) } type FailingIOReader struct{} func (*FailingIOReader) Read(_ []byte) (n int, err error) { return 0, errors.New("failed to read") } func TestStartReader(t *testing.T) { t.Run("starts reader successfully", func(t *testing.T) { ch := make(chan []byte) file := bytes.NewBuffer([]byte{1, 2, 3, 4}) go func() { err := startReader(file, ch) assert.NoError(t, err) }() readBytes := <-ch assert.Equal(t, []byte{1, 2, 3, 4}, readBytes) }) t.Run("reader stops successfully at the EOF", func(t *testing.T) { ch := make(chan []byte) file := bytes.NewBuffer([]byte{1, 2, 3, 4}) go func() { <-ch }() err := startReader(file, ch) assert.NoError(t, err) }) t.Run("returns error if couldn't read", func(t *testing.T) { err := startReader(&FailingIOReader{}, make(chan<- []byte)) assert.EqualError(t, err, "failed to read from reader: failed to read") }) } func TestStartParser(t *testing.T) { t.Run("skipErrors ignores invalid data", func(t *testing.T) { readerChannel := make(chan []byte) writerChannel := newWriterChannel(kafkaMessageTypeReplicationTask) skippedMessagesCount := int32(0) go func() { readerChannel <- []byte{1, 2, 3, 4} close(readerChannel) }() err := startParser(readerChannel, writerChannel, true, &skippedMessagesCount) assert.NoError(t, err) assert.Equal(t, int32(1), skippedMessagesCount) }) t.Run("without skipErrors the invalid data returns error", func(t *testing.T) { readerChannel := make(chan []byte) writerChannel := newWriterChannel(kafkaMessageTypeReplicationTask) count := int32(0) go func() { readerChannel <- []byte{1, 2, 3, 4} close(readerChannel) }() err := startParser(readerChannel, writerChannel, false, &count) assert.EqualError(t, err, "error in splitBuffer: header not found, did you generate dump with -v") }) t.Run("successfully start parser", func(t *testing.T) { readerChannel := make(chan []byte) writerChannel := newWriterChannel(kafkaMessageTypeReplicationTask) count := int32(0) go func() { readerChannel <- []byte("Partition: abc, Offset: 0, Key: 123") close(readerChannel) }() go func() { err := startParser(readerChannel, writerChannel, false, &count) assert.NoError(t, err) }() result := <-writerChannel.ReplicationTaskChannel assert.Equal(t, (*types.ReplicationTask)(nil), result) }) } func TestDeserializeMessage(t *testing.T) { t.Run("skipErrors ignores malformed messages", func(t *testing.T) { tasks, skipped, err := deserializeMessages([][]byte{{1, 2, 3, 4}}, true) assert.Equal(t, []*types.ReplicationTask(nil), tasks) assert.Equal(t, int32(1), skipped) assert.NoError(t, err) }) t.Run("without skipErrors malformed messages return error", func(t *testing.T) { tasks, skipped, err := deserializeMessages([][]byte{{1, 2, 3, 4}}, false) assert.Nil(t, tasks) assert.Equal(t, int32(0), skipped) assert.EqualError(t, err, "Input was malformedError: unexpected EOF") }) t.Run("successful deserialization", func(t *testing.T) { wireVal, _ := (&replicator.ReplicationTask{CreationTime: ptr.Int64(123)}).ToWire() encoded := bytes.NewBuffer([]byte{}) _ = binary.Default.Encode(wireVal, encoded) tasks, skipped, err := deserializeMessages([][]byte{encoded.Bytes()}, false) assert.Equal(t, []*types.ReplicationTask{{}}, tasks) assert.Equal(t, int32(0), skipped) assert.NoError(t, err) }) } func TestDeserializeVisibilityMessage(t *testing.T) { t.Run("skipErrors ignores malformed messages", func(t *testing.T) { tasks, skipped, err := deserializeVisibilityMessages([][]byte{{1, 2, 3, 4}}, true) assert.Equal(t, []*indexer.Message(nil), tasks) assert.Equal(t, int32(1), skipped) assert.NoError(t, err) }) t.Run("without skipErrors malformed messages return error", func(t *testing.T) { tasks, skipped, err := deserializeVisibilityMessages([][]byte{{1, 2, 3, 4}}, false) assert.Nil(t, tasks) assert.Equal(t, int32(0), skipped) assert.EqualError(t, err, "Input was malformedError: unexpected EOF") }) t.Run("successful deserialization", func(t *testing.T) { wireVal, _ := (&indexer.Message{Version: ptr.Int64(123)}).ToWire() encoded := bytes.NewBuffer([]byte{}) _ = binary.Default.Encode(wireVal, encoded) tasks, skipped, err := deserializeVisibilityMessages([][]byte{encoded.Bytes()}, false) assert.Equal(t, []*indexer.Message{{}}, tasks) assert.Equal(t, int32(0), skipped) assert.NoError(t, err) }) } func TestDecodeReplicationTask(t *testing.T) { t.Run("decode replication task", func(t *testing.T) { result, err := decodeReplicationTask(&types.ReplicationTask{TaskType: types.ReplicationTaskTypeHistory.Ptr()}, persistence.NewPayloadSerializer()) expectedTaskJSONBytes := []byte("{\"taskType\":\"History\"}") assert.NoError(t, err) assert.Equal(t, expectedTaskJSONBytes, result) }) t.Run("decode replication task type HistoryV2", func(t *testing.T) { task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ Events: &types.DataBlob{ EncodingType: nil, Data: nil, }, NewRunEvents: &types.DataBlob{ EncodingType: nil, Data: nil, }, }, } result, err := decodeReplicationTask(task, persistence.NewPayloadSerializer()) expectedTaskJSONBytes := []byte("{\"Task\":{\"taskType\":\"HistoryV2\",\"historyTaskV2Attributes\":{}},\"Events\":null,\"NewRunEvents\":null}") assert.NoError(t, err) assert.Equal(t, expectedTaskJSONBytes, result) }) t.Run("deserialize returns an error", func(t *testing.T) { task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ Events: &types.DataBlob{EncodingType: nil, Data: []byte{}}, }, } eventsBlob := persistence.NewDataBlobFromInternal(&types.DataBlob{EncodingType: nil, Data: []byte{}}) serializerMock := persistence.NewMockPayloadSerializer(gomock.NewController(t)) serializerMock.EXPECT().DeserializeBatchEvents(eventsBlob).Return(nil, assert.AnError).Times(1) result, err := decodeReplicationTask(task, serializerMock) assert.Equal(t, err, assert.AnError) assert.Nil(t, result) }) t.Run("deserialize returns an error for NewRunEvents", func(t *testing.T) { task := &types.ReplicationTask{ TaskType: types.ReplicationTaskTypeHistoryV2.Ptr(), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ Events: &types.DataBlob{EncodingType: nil, Data: []byte{}}, NewRunEvents: &types.DataBlob{EncodingType: nil, Data: []byte{}}, }, } eventsBlob := persistence.NewDataBlobFromInternal(&types.DataBlob{EncodingType: nil, Data: []byte{}}) newRunEventsBlob := persistence.NewDataBlobFromInternal(&types.DataBlob{EncodingType: nil, Data: []byte{}}) serializerMock := persistence.NewMockPayloadSerializer(gomock.NewController(t)) serializerMock.EXPECT().DeserializeBatchEvents(eventsBlob).Return(nil, nil).Times(1) serializerMock.EXPECT().DeserializeBatchEvents(newRunEventsBlob).Return(nil, assert.AnError).Times(1) result, err := decodeReplicationTask(task, serializerMock) assert.Equal(t, err, assert.AnError) assert.Nil(t, result) }) } func TestDoRereplicate(t *testing.T) { t.Run("successful rereplication", func(t *testing.T) { clientMock := admin.NewMockClient(gomock.NewController(t)) clientMock.EXPECT().ResendReplicationTasks(context.Background(), &types.ResendReplicationTasksRequest{ DomainID: "domainID", WorkflowID: "wid", RunID: "rid", RemoteCluster: "sourceCluster", EndEventID: ptr.Int64(1), EndVersion: ptr.Int64(1), }).Return(nil).Times(1) err := doRereplicate(context.Background(), "domainID", "wid", "rid", ptr.Int64(1), ptr.Int64(1), "sourceCluster", clientMock) assert.NoError(t, err) }) t.Run("returns err", func(t *testing.T) { clientMock := admin.NewMockClient(gomock.NewController(t)) clientMock.EXPECT().ResendReplicationTasks(context.Background(), &types.ResendReplicationTasksRequest{ DomainID: "domainID", WorkflowID: "wid", RunID: "rid", RemoteCluster: "sourceCluster", EndEventID: ptr.Int64(1), EndVersion: ptr.Int64(1), }).Return(assert.AnError).Times(1) err := doRereplicate(context.Background(), "domainID", "wid", "rid", ptr.Int64(1), ptr.Int64(1), "sourceCluster", clientMock) assert.EqualError(t, err, "Failed to resend ndc workflow: assert.AnError general error for testing") }) } func TestAdminRereplicate(t *testing.T) { tests := []struct { name string args []clitest.CliArgument err error }{ { name: "success", args: []clitest.CliArgument{ clitest.StringArgument(FlagSourceCluster, "sourceCluster"), clitest.IntArgument(FlagMaxEventID, 123), clitest.IntArgument(FlagEndEventVersion, 321), clitest.StringArgument(FlagDomainID, "domainID"), clitest.StringArgument(FlagWorkflowID, "workflowID"), clitest.StringArgument(FlagRunID, "runID"), clitest.StringArgument(FlagContextTimeout, "10s"), }, err: nil, }, { name: "source cluster is missing", args: []clitest.CliArgument{ clitest.IntArgument(FlagMaxEventID, 123), clitest.IntArgument(FlagEndEventVersion, 321), clitest.StringArgument(FlagDomainID, "domainID"), clitest.StringArgument(FlagWorkflowID, "workflowID"), clitest.StringArgument(FlagRunID, "runID"), clitest.StringArgument(FlagContextTimeout, "10s"), }, err: commoncli.Problem("Required flag not found: ", errors.New("option source_cluster is required")), }, { name: "domain ID is missing", args: []clitest.CliArgument{ clitest.StringArgument(FlagSourceCluster, "sourceCluster"), clitest.IntArgument(FlagMaxEventID, 123), clitest.IntArgument(FlagEndEventVersion, 321), clitest.StringArgument(FlagWorkflowID, "workflowID"), clitest.StringArgument(FlagRunID, "runID"), clitest.StringArgument(FlagContextTimeout, "10s"), }, err: commoncli.Problem("Required flag not found: ", errors.New("option domain_id is required")), }, { name: "workflow ID is missing", args: []clitest.CliArgument{ clitest.StringArgument(FlagSourceCluster, "sourceCluster"), clitest.IntArgument(FlagMaxEventID, 123), clitest.IntArgument(FlagEndEventVersion, 321), clitest.StringArgument(FlagDomainID, "domainID"), clitest.StringArgument(FlagRunID, "runID"), clitest.StringArgument(FlagContextTimeout, "10s"), }, err: commoncli.Problem("Required flag not found: ", errors.New("option workflow_id is required")), }, { name: "run ID is missing", args: []clitest.CliArgument{ clitest.StringArgument(FlagSourceCluster, "sourceCluster"), clitest.IntArgument(FlagMaxEventID, 123), clitest.IntArgument(FlagEndEventVersion, 321), clitest.StringArgument(FlagDomainID, "domainID"), clitest.StringArgument(FlagWorkflowID, "workflowID"), clitest.StringArgument(FlagContextTimeout, "10s"), }, err: commoncli.Problem("Required flag not found: ", errors.New("option run_id is required")), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { testData := newCLITestData(t) testData.mockAdminClient.EXPECT().ResendReplicationTasks(gomock.Any(), &types.ResendReplicationTasksRequest{ DomainID: "domainID", WorkflowID: "workflowID", RunID: "runID", RemoteCluster: "sourceCluster", EndEventID: ptr.Int64(124), EndVersion: ptr.Int64(321), }).Return(nil).MaxTimes(1) cliContext := clitest.NewCLIContext(t, testData.app, tt.args...) err := AdminRereplicate(cliContext) assert.Equal(t, tt.err, err) }) } } func TestParse(t *testing.T) { t.Run("successful parse replication task", func(t *testing.T) { wireVal, _ := (&replicator.ReplicationTask{CreationTime: ptr.Int64(123)}).ToWire() encoded := bytes.NewBuffer([]byte{}) _ = binary.Default.Encode(wireVal, encoded) data := []byte("Partition: abc, Offset: 0, Key: 123") data = append(data, preambleVersion0) data = append(data, encoded.Bytes()...) skipped := ptr.Int32(0) ch := newWriterChannel(kafkaMessageTypeReplicationTask) go func() { err := parse(data, false, skipped, ch) assert.NoError(t, err) close(ch.ReplicationTaskChannel) }() result := <-ch.ReplicationTaskChannel assert.NotNil(t, result) }) t.Run("successful parse visibility task", func(t *testing.T) { wireVal, _ := (&indexer.Message{Version: ptr.Int64(123)}).ToWire() encoded := bytes.NewBuffer([]byte{}) _ = binary.Default.Encode(wireVal, encoded) data := []byte("Partition: abc, Offset: 0, Key: 123") data = append(data, preambleVersion0) data = append(data, encoded.Bytes()...) skipped := ptr.Int32(0) ch := newWriterChannel(kafkaMessageTypeVisibilityMsg) go func() { err := parse(data, false, skipped, ch) assert.NoError(t, err) close(ch.VisibilityMsgChannel) }() result := <-ch.VisibilityMsgChannel assert.NotNil(t, result) }) t.Run("invalid replication task", func(t *testing.T) { data := []byte("Partition: abc, Offset: 0, Key: 123") data = append(data, preambleVersion0) data = append(data, []byte{1, 2, 3, 4}...) skipped := ptr.Int32(0) ch := newWriterChannel(kafkaMessageTypeReplicationTask) go func() { err := parse(data, false, skipped, ch) assert.EqualError(t, err, "parsing failed: Input was malformedError: unknown ttype Type(1)") close(ch.ReplicationTaskChannel) }() result := <-ch.ReplicationTaskChannel assert.Nil(t, result) }) t.Run("invalid visibility task", func(t *testing.T) { data := []byte("Partition: abc, Offset: 0, Key: 123") data = append(data, preambleVersion0) data = append(data, []byte{1, 2, 3, 4}...) skipped := ptr.Int32(0) ch := newWriterChannel(kafkaMessageTypeVisibilityMsg) go func() { err := parse(data, false, skipped, ch) assert.EqualError(t, err, "parsing failed: Input was malformedError: unknown ttype Type(1)") close(ch.VisibilityMsgChannel) }() result := <-ch.VisibilityMsgChannel assert.Nil(t, result) }) } func TestAdminKafkaParse(t *testing.T) { t.Run("success", func(t *testing.T) { wireVal, _ := (&replicator.ReplicationTask{CreationTime: ptr.Int64(123)}).ToWire() encoded := bytes.NewBuffer([]byte{}) _ = binary.Default.Encode(wireVal, encoded) data := []byte("Partition: abc, Offset: 0, Key: 123") data = append(data, preambleVersion0) data = append(data, encoded.Bytes()...) inputFileName := createTempFileWithContent(t, string(data)) outputFileName := createTempFileWithContent(t, "") testData := newCLITestData(t) cliContext := clitest.NewCLIContext(t, testData.app, clitest.StringArgument(FlagInputFile, inputFileName), clitest.StringArgument(FlagOutputFilename, outputFileName), ) err := AdminKafkaParse(cliContext) assert.NoError(t, err) result, err := os.ReadFile(outputFileName) assert.Equal(t, "{\"creationTime\":123}\n", string(result)) }) t.Run("can't open input file", func(t *testing.T) { testData := newCLITestData(t) cliContext := clitest.NewCLIContext(t, testData.app, clitest.StringArgument(FlagInputFile, "/path/do/not/exist/input.txt"), clitest.StringArgument(FlagOutputFilename, "output.txt"), ) err := AdminKafkaParse(cliContext) assert.EqualError(t, err, "Error in Admin kafka parse: : failed to open input file for reading: /path/do/not/exist/input.txt: open /path/do/not/exist/input.txt: no such file or directory") }) t.Run("can't open output file", func(t *testing.T) { testData := newCLITestData(t) inputFileName := createTempFileWithContent(t, "") cliContext := clitest.NewCLIContext(t, testData.app, clitest.StringArgument(FlagInputFile, inputFileName), clitest.StringArgument(FlagOutputFilename, "/path/do/not/exist/output.txt"), ) err := AdminKafkaParse(cliContext) assert.EqualError(t, err, "Error in Admin kafka parse: : failed to create output file open /path/do/not/exist/output.txt: no such file or directory") }) } func TestWriteVisibilityMessage(t *testing.T) { t.Run("successful write", func(t *testing.T) { filename := createTempFileWithContent(t, "") file, _ := os.OpenFile(filename, os.O_WRONLY, 0600) defer file.Close() ch := newWriterChannel(kafkaMessageTypeVisibilityMsg) skippedCount := ptr.Int32(0) cliContext := clitest.NewCLIContext(t, newCLITestData(t).app) go func() { ch.VisibilityMsgChannel <- &indexer.Message{ DomainID: ptr.String("domain_id"), WorkflowID: ptr.String("workflow_id"), RunID: ptr.String("run_id"), } ch.Close() }() err := writeVisibilityMessage(file, ch, skippedCount, false, false, cliContext) assert.NoError(t, err) result, _ := os.ReadFile(filename) assert.Equal(t, "{\"domainID\":\"domain_id\",\"workflowID\":\"workflow_id\",\"runID\":\"run_id\"}\n", string(result)) }) t.Run("successful write with header mode", func(t *testing.T) { filename := createTempFileWithContent(t, "") file, _ := os.OpenFile(filename, os.O_WRONLY, 0600) defer file.Close() ch := newWriterChannel(kafkaMessageTypeVisibilityMsg) skippedCount := ptr.Int32(0) cliContext := clitest.NewCLIContext(t, newCLITestData(t).app) go func() { ch.VisibilityMsgChannel <- &indexer.Message{ DomainID: ptr.String("domain_id"), WorkflowID: ptr.String("workflow_id"), RunID: ptr.String("run_id"), } ch.Close() }() err := writeVisibilityMessage(file, ch, skippedCount, false, true, cliContext) assert.NoError(t, err) result, _ := os.ReadFile(filename) assert.Equal(t, "domain_id, workflow_id, run_id, Index, 0\n", string(result)) }) t.Run("returns error if can't write to the file", func(t *testing.T) { filename := createTempFileWithContent(t, "") file, _ := os.OpenFile(filename, os.O_WRONLY, 0600) ch := newWriterChannel(kafkaMessageTypeVisibilityMsg) skippedCount := ptr.Int32(0) cliContext := clitest.NewCLIContext(t, newCLITestData(t).app) go func() { ch.VisibilityMsgChannel <- &indexer.Message{} ch.Close() }() _ = file.Close() err := writeVisibilityMessage(file, ch, skippedCount, false, true, cliContext) assert.Errorf(t, err, "failed to write to file: write %v: file already closed", filename) }) } func TestWriteReplicationTask(t *testing.T) { t.Run("successful write", func(t *testing.T) { filename := createTempFileWithContent(t, "") file, _ := os.OpenFile(filename, os.O_WRONLY, 0600) defer file.Close() ch := newWriterChannel(kafkaMessageTypeReplicationTask) skippedCount := ptr.Int32(0) cliContext := clitest.NewCLIContext(t, newCLITestData(t).app) go func() { ch.ReplicationTaskChannel <- &types.ReplicationTask{ SourceTaskID: 123, CreationTime: ptr.Int64(123), } ch.Close() }() err := writeReplicationTask(file, ch, skippedCount, false, false, persistence.NewPayloadSerializer(), cliContext) assert.NoError(t, err) result, _ := os.ReadFile(filename) assert.Equal(t, "{\"sourceTaskId\":123,\"creationTime\":123}\n", string(result)) }) t.Run("successful write with header mode", func(t *testing.T) { filename := createTempFileWithContent(t, "") file, _ := os.OpenFile(filename, os.O_WRONLY, 0600) defer file.Close() ch := newWriterChannel(kafkaMessageTypeReplicationTask) skippedCount := ptr.Int32(0) cliContext := clitest.NewCLIContext(t, newCLITestData(t).app) go func() { ch.ReplicationTaskChannel <- &types.ReplicationTask{ SourceTaskID: 123, CreationTime: ptr.Int64(123), HistoryTaskV2Attributes: &types.HistoryTaskV2Attributes{ DomainID: "domain_id", WorkflowID: "workflow_id", RunID: "run_id", }, } ch.Close() }() err := writeReplicationTask(file, ch, skippedCount, false, true, persistence.NewPayloadSerializer(), cliContext) assert.NoError(t, err) result, _ := os.ReadFile(filename) assert.Equal(t, "domain_id, workflow_id, run_id\n", string(result)) }) t.Run("returns error if can't write to the file", func(t *testing.T) { filename := createTempFileWithContent(t, "") file, _ := os.OpenFile(filename, os.O_WRONLY, 0600) ch := newWriterChannel(kafkaMessageTypeReplicationTask) skippedCount := ptr.Int32(0) cliContext := clitest.NewCLIContext(t, newCLITestData(t).app) go func() { ch.ReplicationTaskChannel <- &types.ReplicationTask{} ch.Close() }() file.Close() err := writeReplicationTask(file, ch, skippedCount, false, false, persistence.NewPayloadSerializer(), cliContext) assert.Errorf(t, err, "failed to write to file: write %v: file already closed", filename) }) } func TestStartWriter(t *testing.T) { t.Run("successful write to replication task", func(t *testing.T) { filename := createTempFileWithContent(t, "") file, _ := os.OpenFile(filename, os.O_WRONLY, 0600) defer file.Close() ch := newWriterChannel(kafkaMessageTypeReplicationTask) doneCh := make(chan struct{}) skippedCount := ptr.Int32(0) cliContext := clitest.NewCLIContext(t, newCLITestData(t).app) go func() { ch.ReplicationTaskChannel <- &types.ReplicationTask{ SourceTaskID: 123, CreationTime: ptr.Int64(123), } ch.Close() }() err := startWriter(file, ch, doneCh, skippedCount, persistence.NewPayloadSerializer(), cliContext) assert.NoError(t, err) result, _ := os.ReadFile(filename) assert.Equal(t, "{\"sourceTaskId\":123,\"creationTime\":123}\n", string(result)) }) t.Run("successful write with visibility task", func(t *testing.T) { filename := createTempFileWithContent(t, "") file, _ := os.OpenFile(filename, os.O_WRONLY, 0600) defer file.Close() ch := newWriterChannel(kafkaMessageTypeVisibilityMsg) doneCh := make(chan struct{}) skippedCount := ptr.Int32(0) cliContext := clitest.NewCLIContext(t, newCLITestData(t).app) go func() { ch.VisibilityMsgChannel <- &indexer.Message{ DomainID: ptr.String("domain_id"), WorkflowID: ptr.String("workflow_id"), RunID: ptr.String("run_id"), } ch.Close() }() err := startWriter(file, ch, doneCh, skippedCount, persistence.NewPayloadSerializer(), cliContext) assert.NoError(t, err) result, _ := os.ReadFile(filename) assert.Equal(t, "{\"domainID\":\"domain_id\",\"workflowID\":\"workflow_id\",\"runID\":\"run_id\"}\n", string(result)) }) t.Run("unsupported type", func(t *testing.T) { ch := newWriterChannel(kafkaMessageType(321)) doneCh := make(chan struct{}) cliContext := clitest.NewCLIContext(t, newCLITestData(t).app) err := startWriter(nil, ch, doneCh, nil, persistence.NewPayloadSerializer(), cliContext) assert.EqualError(t, err, "unsupported message type: 321") }) } ================================================ FILE: tools/cli/admin_task_list_commands.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "context" "fmt" "io" "os" "slices" "strings" "github.com/urfave/cli/v2" "go.uber.org/multierr" "golang.org/x/exp/maps" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/common/commoncli" ) type ( TaskListRow struct { Name string `header:"Task List Name"` ActivityPollerCount int `header:"Activity Poller Count"` DecisionPollerCount int `header:"Decision Poller Count"` } TaskListStatusRow struct { Type string `header:"Type"` PollerCount int `header:"PollerCount"` ReadLevel int64 `header:"Read Level"` AckLevel int64 `header:"Ack Level"` Backlog int64 `header:"Backlog"` RPS float64 `header:"RPS"` StartID int64 `header:"Lease Start TaskID"` EndID int64 `header:"Lease End TaskID"` } TaskListPartitionConfigRow struct { Type string `header:"Type"` Version int64 `header:"Version"` ReadPartitions map[int]*types.TaskListPartition `header:"Read Partitions"` WritePartitions map[int]*types.TaskListPartition `header:"Write Partitions"` } ) // AdminDescribeTaskList displays poller and status information of task list. func AdminDescribeTaskList(c *cli.Context) error { frontendClient, err := getDeps(c).ServerFrontendClient(c) if err != nil { return err } domain, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found: ", err) } taskList, err := getRequiredOption(c, FlagTaskList) if err != nil { return commoncli.Problem("Required flag not found: ", err) } taskListTypes, err := getTaskListTypes(c) if err != nil { return err } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context:", err) } responses := make(map[types.TaskListType]*types.DescribeTaskListResponse) for _, tlType := range taskListTypes { request := &types.DescribeTaskListRequest{ Domain: domain, TaskList: &types.TaskList{Name: taskList}, TaskListType: tlType.Ptr(), IncludeTaskListStatus: true, } response, err := frontendClient.DescribeTaskList(ctx, request) if err != nil { return commoncli.Problem("Operation DescribeTaskList failed for type: "+tlType.String(), err) } responses[tlType] = response } if c.String(FlagFormat) == formatJSON { prettyPrintJSONObject(getDeps(c).Output(), responses) return nil } if err := printTaskListStatus(getDeps(c).Output(), responses); err != nil { return fmt.Errorf("failed to print task list status: %w", err) } getDeps(c).Output().Write([]byte("\n")) if err := printTaskListPartitionConfig(getDeps(c).Output(), responses); err != nil { return fmt.Errorf("failed to print task list partition config: %w", err) } getDeps(c).Output().Write([]byte("\n")) return nil } // AdminListTaskList displays all task lists under a domain. func AdminListTaskList(c *cli.Context) error { frontendClient, err := getDeps(c).ServerFrontendClient(c) if err != nil { return err } domain, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found: ", err) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } request := &types.GetTaskListsByDomainRequest{ Domain: domain, } response, err := frontendClient.GetTaskListsByDomain(ctx, request) if err != nil { return commoncli.Problem("Operation GetTaskListByDomain failed.", err) } if c.String(FlagFormat) == formatJSON { prettyPrintJSONObject(getDeps(c).Output(), response) return nil } _, _ = fmt.Fprintln(getDeps(c).Output(), "Task Lists for domain ", domain, ":") tlByName := make(map[string]TaskListRow) for name, taskList := range response.GetDecisionTaskListMap() { row := tlByName[name] row.Name = name row.DecisionPollerCount = len(taskList.GetPollers()) tlByName[name] = row } for name, taskList := range response.GetActivityTaskListMap() { row := tlByName[name] row.Name = name row.ActivityPollerCount = len(taskList.GetPollers()) tlByName[name] = row } table := maps.Values(tlByName) slices.SortFunc(table, func(a, b TaskListRow) int { return strings.Compare(a.Name, b.Name) }) return RenderTable(os.Stdout, table, RenderOptions{Color: true, Border: true}) } func printTaskListStatus(w io.Writer, responses map[types.TaskListType]*types.DescribeTaskListResponse) error { var table []TaskListStatusRow for tlType, response := range responses { taskListStatus := response.TaskListStatus table = append(table, TaskListStatusRow{ Type: tlType.String(), PollerCount: len(response.Pollers), ReadLevel: taskListStatus.GetReadLevel(), AckLevel: taskListStatus.GetAckLevel(), Backlog: taskListStatus.GetBacklogCountHint(), RPS: taskListStatus.GetRatePerSecond(), StartID: taskListStatus.GetTaskIDBlock().GetStartID(), EndID: taskListStatus.GetTaskIDBlock().GetEndID(), }) } slices.SortFunc(table, func(a, b TaskListStatusRow) int { return strings.Compare(a.Type, b.Type) }) return RenderTable(w, table, RenderOptions{Color: true}) } func printTaskListPartitionConfig(w io.Writer, responses map[types.TaskListType]*types.DescribeTaskListResponse) error { var table []TaskListPartitionConfigRow for tlType, response := range responses { config := response.PartitionConfig if config == nil { config = &types.TaskListPartitionConfig{ Version: 0, ReadPartitions: createPartitions(1), WritePartitions: createPartitions(1), } } table = append(table, TaskListPartitionConfigRow{ Type: tlType.String(), Version: config.Version, ReadPartitions: config.ReadPartitions, WritePartitions: config.WritePartitions, }) } slices.SortFunc(table, func(a, b TaskListPartitionConfigRow) int { return strings.Compare(a.Type, b.Type) }) return RenderTable(w, table, RenderOptions{Color: true}) } func AdminUpdateTaskListPartitionConfig(c *cli.Context) error { adminClient, err := getDeps(c).ServerAdminClient(c) if err != nil { return err } frontendClient, err := getDeps(c).ServerFrontendClient(c) if err != nil { return err } domain, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found: ", err) } taskList, err := getRequiredOption(c, FlagTaskList) if err != nil { return commoncli.Problem("Required flag not found: ", err) } force := c.Bool(FlagForce) taskListTypes, err := getTaskListTypes(c) if err != nil { return err } numReadPartitions, err := getRequiredIntOption(c, FlagNumReadPartitions) if err != nil { return commoncli.Problem("Required flag not found: ", err) } numWritePartitions, err := getRequiredIntOption(c, FlagNumWritePartitions) if err != nil { return commoncli.Problem("Required flag not found: ", err) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context:", err) } cfg := &types.TaskListPartitionConfig{ ReadPartitions: createPartitions(numReadPartitions), WritePartitions: createPartitions(numWritePartitions), } tl := &types.TaskList{Name: taskList, Kind: types.TaskListKindNormal.Ptr()} if !force { hasPollers := false var errors []error for _, tlType := range taskListTypes { typeHasPollers, typeErr := validateChange(ctx, frontendClient, domain, tl, tlType.Ptr(), cfg) if typeErr != nil { errors = append(errors, fmt.Errorf("%s:%s failed validation: %w", tl.Name, tlType, typeErr)) } hasPollers = typeHasPollers || hasPollers } if len(errors) > 0 { return commoncli.Problem("Potentially unsafe operation. Specify '--force' to proceed anyway", multierr.Combine(errors...)) } if !hasPollers { return commoncli.Problem(fmt.Sprintf("Operation is safe but %s has no pollers of the specified types. Is the name correct? Specify '--force' to proceed anyway", tl.Name), nil) } } for _, tlType := range taskListTypes { _, err = adminClient.UpdateTaskListPartitionConfig(ctx, &types.UpdateTaskListPartitionConfigRequest{ Domain: domain, TaskList: tl, TaskListType: tlType.Ptr(), PartitionConfig: cfg, }) if err != nil { return commoncli.Problem("Operation UpdateTaskListPartitionConfig failed for type: "+tlType.String(), err) } _, _ = fmt.Fprintln(getDeps(c).Output(), "Successfully updated ", tl.Name, ":", tlType) } return nil } func validateChange(ctx context.Context, client frontend.Client, domain string, tl *types.TaskList, tlt *types.TaskListType, newCfg *types.TaskListPartitionConfig) (bool, error) { description, err := client.DescribeTaskList(ctx, &types.DescribeTaskListRequest{ Domain: domain, TaskList: tl, TaskListType: tlt, }) if err != nil { return false, fmt.Errorf("DescribeTaskList failed: %w", err) } // Illegal operations are rejected by the server (read < write), but unsafe ones are still allowed if description.PartitionConfig != nil { oldCfg := description.PartitionConfig // Ensure they're not removing active write partitions if len(newCfg.ReadPartitions) < len(oldCfg.WritePartitions) { return false, fmt.Errorf("remove write partitions, then read partitions. Removing an active write partition risks losing tasks. Proposed read count is less than current write count (%d < %d)", len(newCfg.ReadPartitions), len(oldCfg.WritePartitions)) } // Ensure removed read partitions are drained for i := len(newCfg.ReadPartitions); i < len(oldCfg.ReadPartitions); i++ { partition, err := client.DescribeTaskList(ctx, &types.DescribeTaskListRequest{ Domain: domain, TaskList: &types.TaskList{Name: getPartitionTaskListName(tl.Name, i), Kind: tl.Kind}, TaskListType: tlt, IncludeTaskListStatus: true, }) if err != nil { return false, fmt.Errorf("DescribeTaskList failed for partition %d: %w", i, err) } if partition.TaskListStatus.BacklogCountHint != 0 { return false, fmt.Errorf("partition %d still has %d tasks remaining", i, partition.TaskListStatus.BacklogCountHint) } } } // If it's otherwise valid but there are no pollers, they might have mistyped the name return len(description.Pollers) > 0, nil } func createPartitions(num int) map[int]*types.TaskListPartition { result := make(map[int]*types.TaskListPartition, num) for i := 0; i < num; i++ { result[i] = &types.TaskListPartition{} } return result } func getPartitionTaskListName(root string, partition int) string { if partition <= 0 { return root } return fmt.Sprintf("%v%v/%v", constants.ReservedTaskListPrefix, root, partition) } func getTaskListTypes(c *cli.Context) ([]types.TaskListType, error) { var taskListTypes []types.TaskListType if strings.ToLower(c.String(FlagTaskListType)) == "activity" { taskListTypes = []types.TaskListType{types.TaskListTypeActivity} } else if strings.ToLower(c.String(FlagTaskListType)) == "decision" { taskListTypes = []types.TaskListType{types.TaskListTypeDecision} } else if c.String(FlagTaskListType) == "" { taskListTypes = []types.TaskListType{types.TaskListTypeActivity, types.TaskListTypeDecision} } else { return nil, commoncli.Problem("Invalid task list type: valid types are 'activity', 'decision', or empty (both)", nil) } return taskListTypes, nil } ================================================ FILE: tools/cli/admin_task_list_commands_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "encoding/json" "errors" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/cli/clitest" ) func TestAdminDescribeTaskList(t *testing.T) { response := &types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "test-poller", }, }, TaskListStatus: &types.TaskListStatus{ BacklogCountHint: 10, }, } taskList := &types.TaskList{ Name: testTaskList, } tests := []struct { name string allowance func(td *cliTestData) tlType string format string baseArgs []clitest.CliArgument assertions func(t *testing.T, td *cliTestData) expectedErr string }{ { name: "success", allowance: func(td *cliTestData) { td.mockFrontendClient.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: testDomain, TaskList: taskList, TaskListType: types.TaskListTypeActivity.Ptr(), IncludeTaskListStatus: true, }).Return(response, nil).Times(1) td.mockFrontendClient.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: testDomain, TaskList: taskList, TaskListType: types.TaskListTypeDecision.Ptr(), IncludeTaskListStatus: true, }).Return(response, nil).Times(1) }, }, { name: "success - decision only", tlType: "decision", allowance: func(td *cliTestData) { td.mockFrontendClient.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: testDomain, TaskList: taskList, TaskListType: types.TaskListTypeDecision.Ptr(), IncludeTaskListStatus: true, }).Return(response, nil).Times(1) }, }, { name: "success - activity only", tlType: "activity", allowance: func(td *cliTestData) { td.mockFrontendClient.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: testDomain, TaskList: taskList, TaskListType: types.TaskListTypeActivity.Ptr(), IncludeTaskListStatus: true, }).Return(response, nil).Times(1) }, }, { name: "json", tlType: "decision", format: "json", allowance: func(td *cliTestData) { td.mockFrontendClient.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: testDomain, TaskList: taskList, TaskListType: types.TaskListTypeDecision.Ptr(), IncludeTaskListStatus: true, }).Return(response, nil).Times(1) }, assertions: func(t *testing.T, td *cliTestData) { expected := map[types.TaskListType]*types.DescribeTaskListResponse{ types.TaskListTypeDecision: response, } output := td.consoleOutput() var result map[types.TaskListType]*types.DescribeTaskListResponse err := json.Unmarshal([]byte(output), &result) require.NoError(t, err) assert.Equal(t, expected, result) }, }, { name: "error - missing domain", expectedErr: "Required flag not found", baseArgs: []clitest.CliArgument{ clitest.StringArgument(FlagTaskList, testTaskList), }, }, { name: "error - missing tasklist", expectedErr: "Required flag not found", baseArgs: []clitest.CliArgument{ clitest.StringArgument(FlagDomain, testDomain), }, }, { name: "error - from server", tlType: "decision", allowance: func(td *cliTestData) { td.mockFrontendClient.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: testDomain, TaskList: taskList, TaskListType: types.TaskListTypeDecision.Ptr(), IncludeTaskListStatus: true, }).Return(nil, errors.New("oh no")).Times(1) }, expectedErr: "oh no", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { td := newCLITestData(t) var args []clitest.CliArgument if tc.baseArgs == nil { args = []clitest.CliArgument{ clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagTaskList, testTaskList), } } else { args = tc.baseArgs } if tc.tlType != "" { args = append(args, clitest.StringArgument(FlagTaskListType, tc.tlType)) } if tc.format != "" { args = append(args, clitest.StringArgument(FlagFormat, tc.format)) } cliCtx := clitest.NewCLIContext( t, td.app, args..., ) if tc.allowance != nil { tc.allowance(td) } err := AdminDescribeTaskList(cliCtx) if tc.expectedErr == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tc.expectedErr) } if tc.assertions != nil { tc.assertions(t, td) } }) } } func TestAdminListTaskList(t *testing.T) { expectedResponse := &types.GetTaskListsByDomainResponse{ DecisionTaskListMap: map[string]*types.DescribeTaskListResponse{ "decision-tasklist-1": { Pollers: []*types.PollerInfo{ {Identity: "poller1"}, {Identity: "poller2"}, }, }, "decision-tasklist-2": { Pollers: []*types.PollerInfo{ {Identity: "poller3"}, }, }, }, ActivityTaskListMap: map[string]*types.DescribeTaskListResponse{ "activity-tasklist-1": { Pollers: []*types.PollerInfo{ {Identity: "poller4"}, }, }, }, } // Define table of test cases tests := []struct { name string setupMocks func(*frontend.MockClient) assertions func(t *testing.T, td *cliTestData) expectedError string domainFlag string format string }{ { name: "Success", setupMocks: func(client *frontend.MockClient) { client.EXPECT(). GetTaskListsByDomain(gomock.Any(), gomock.Any()). Return(expectedResponse, nil). Times(1) }, domainFlag: "test-domain", }, { name: "Success - json", format: "json", setupMocks: func(client *frontend.MockClient) { client.EXPECT(). GetTaskListsByDomain(gomock.Any(), gomock.Any()). Return(expectedResponse, nil). Times(1) }, assertions: func(t *testing.T, td *cliTestData) { output := td.consoleOutput() var result *types.GetTaskListsByDomainResponse err := json.Unmarshal([]byte(output), &result) require.NoError(t, err) assert.Equal(t, expectedResponse, result) }, domainFlag: "test-domain", }, { name: "GetTaskListsByDomainFails", setupMocks: func(client *frontend.MockClient) { client.EXPECT(). GetTaskListsByDomain(gomock.Any(), gomock.Any()). Return(nil, fmt.Errorf("GetTaskListsByDomain failed")). Times(1) }, expectedError: "Operation GetTaskListByDomain failed", domainFlag: "test-domain", }, { name: "NoDomainFlag", setupMocks: func(client *frontend.MockClient) {}, expectedError: "Required flag not found", domainFlag: "", // Omit Domain flag }, } // Loop through test cases for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) // Set up mocks for the current test case tt.setupMocks(td.mockFrontendClient) var cliCtx *cli.Context if tt.domainFlag == "" { cliCtx = clitest.NewCLIContext( t, td.app, /* omit the domain flag */ clitest.StringArgument(FlagFormat, tt.format), ) } else { cliCtx = clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagFormat, tt.format), ) } err := AdminListTaskList(cliCtx) if tt.expectedError == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.expectedError) } if tt.assertions != nil { tt.assertions(t, td) } }) } } func TestAdminUpdateTaskListPartitionConfig(t *testing.T) { // Define table of test cases tests := []struct { name string setupMocks func(*admin.MockClient, *frontend.MockClient) expectedError string domainFlag string taskListFlag string taskListType string numReadPartitions int numWritePartitions int force bool }{ { name: "Success", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "poller", }, }, PartitionConfig: nil, }, nil).Times(1) client.EXPECT(). UpdateTaskListPartitionConfig(gomock.Any(), &types.UpdateTaskListPartitionConfigRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: createPartitions(2), WritePartitions: createPartitions(2), }, }). Return(&types.UpdateTaskListPartitionConfigResponse{}, nil). Times(1) }, expectedError: "", domainFlag: "test-domain", taskListFlag: "test-tasklist", taskListType: "decision", numReadPartitions: 2, numWritePartitions: 2, }, { name: "Success - both types", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "poller", }, }, PartitionConfig: nil, }, nil).Times(1) f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeActivity.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "poller", }, }, PartitionConfig: nil, }, nil).Times(1) client.EXPECT(). UpdateTaskListPartitionConfig(gomock.Any(), &types.UpdateTaskListPartitionConfigRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: createPartitions(2), WritePartitions: createPartitions(2), }, }). Return(&types.UpdateTaskListPartitionConfigResponse{}, nil). Times(1) client.EXPECT(). UpdateTaskListPartitionConfig(gomock.Any(), &types.UpdateTaskListPartitionConfigRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeActivity.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: createPartitions(2), WritePartitions: createPartitions(2), }, }). Return(&types.UpdateTaskListPartitionConfigResponse{}, nil). Times(1) }, expectedError: "", domainFlag: "test-domain", taskListFlag: "test-tasklist", numReadPartitions: 2, numWritePartitions: 2, }, { name: "Success - force", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { client.EXPECT(). UpdateTaskListPartitionConfig(gomock.Any(), &types.UpdateTaskListPartitionConfigRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: createPartitions(2), WritePartitions: createPartitions(2), }, }). Return(&types.UpdateTaskListPartitionConfigResponse{}, nil). Times(1) }, expectedError: "", domainFlag: "test-domain", taskListFlag: "test-tasklist", taskListType: "decision", numReadPartitions: 2, numWritePartitions: 2, force: true, }, { name: "UpdateTaskListPartitionConfigFails", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "poller", }, }, PartitionConfig: nil, }, nil).Times(1) client.EXPECT(). UpdateTaskListPartitionConfig(gomock.Any(), gomock.Any()). Return(nil, fmt.Errorf("API failed")). Times(1) }, expectedError: "API failed", domainFlag: "test-domain", taskListFlag: "test-tasklist", taskListType: "decision", numReadPartitions: 2, numWritePartitions: 2, }, { name: "NoDomainFlag", expectedError: "Required flag not found", domainFlag: "", // Omit Domain flag taskListFlag: "test-tasklist", taskListType: "decision", numReadPartitions: 2, numWritePartitions: 2, }, { name: "NoTaskListFlag", expectedError: "Required flag not found", domainFlag: "test-domain", taskListType: "decision", numReadPartitions: 2, numWritePartitions: 2, }, { name: "Invalid task list type", expectedError: "Invalid task list type: valid types are 'activity', 'decision', or empty (both)", domainFlag: "test-domain", taskListFlag: "test-tasklist", taskListType: "ihsdajhi", numReadPartitions: 2, numWritePartitions: 2, }, { name: "NoReadPartitionFlag", expectedError: "Required flag not found", domainFlag: "test-domain", taskListFlag: "test-tasklist", taskListType: "decision", numWritePartitions: 2, }, { name: "NoWritePartitionFlag", expectedError: "Required flag not found", domainFlag: "test-domain", taskListFlag: "test-tasklist", taskListType: "decision", numReadPartitions: 2, }, { name: "safe - removing drained partition", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { // Check the root for pollers and config safety f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "poller", }, }, PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: createPartitions(3), WritePartitions: createPartitions(1), }, }, nil).Times(1) // Check the partitions being drained f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: getPartitionTaskListName("test-tasklist", 2), Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), IncludeTaskListStatus: true, }).Return(&types.DescribeTaskListResponse{ Pollers: nil, TaskListStatus: &types.TaskListStatus{ BacklogCountHint: 0, }, PartitionConfig: nil, }, nil).Times(1) f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: getPartitionTaskListName("test-tasklist", 1), Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), IncludeTaskListStatus: true, }).Return(&types.DescribeTaskListResponse{ Pollers: nil, TaskListStatus: &types.TaskListStatus{ BacklogCountHint: 0, }, PartitionConfig: nil, }, nil).Times(1) // do the update client.EXPECT(). UpdateTaskListPartitionConfig(gomock.Any(), &types.UpdateTaskListPartitionConfigRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: createPartitions(1), WritePartitions: createPartitions(1), }, }). Return(&types.UpdateTaskListPartitionConfigResponse{}, nil). Times(1) }, expectedError: "", domainFlag: "test-domain", taskListFlag: "test-tasklist", taskListType: "decision", numReadPartitions: 1, numWritePartitions: 1, }, { name: "Safe - only one type missing pollers", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "poller", }, }, PartitionConfig: nil, }, nil).Times(1) f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeActivity.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: nil, PartitionConfig: nil, }, nil).Times(1) client.EXPECT(). UpdateTaskListPartitionConfig(gomock.Any(), &types.UpdateTaskListPartitionConfigRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: createPartitions(2), WritePartitions: createPartitions(2), }, }). Return(&types.UpdateTaskListPartitionConfigResponse{}, nil). Times(1) client.EXPECT(). UpdateTaskListPartitionConfig(gomock.Any(), &types.UpdateTaskListPartitionConfigRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeActivity.Ptr(), PartitionConfig: &types.TaskListPartitionConfig{ ReadPartitions: createPartitions(2), WritePartitions: createPartitions(2), }, }). Return(&types.UpdateTaskListPartitionConfigResponse{}, nil). Times(1) }, domainFlag: "test-domain", taskListFlag: "test-tasklist", numReadPartitions: 2, numWritePartitions: 2, }, { name: "Unsafe - no pollers", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: nil, PartitionConfig: nil, }, nil).Times(1) }, expectedError: "no pollers", domainFlag: "test-domain", taskListFlag: "test-tasklist", taskListType: "decision", numReadPartitions: 2, numWritePartitions: 2, }, { name: "Unsafe - both types no pollers", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: nil, PartitionConfig: nil, }, nil).Times(1) f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeActivity.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: nil, PartitionConfig: nil, }, nil).Times(1) }, expectedError: "no pollers", domainFlag: "test-domain", taskListFlag: "test-tasklist", numReadPartitions: 2, numWritePartitions: 2, }, { name: "Unsafe - removing active write partition", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "poller", }, }, PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: createPartitions(3), WritePartitions: createPartitions(3), }, }, nil).Times(1) }, expectedError: "remove write partitions, then read partitions", domainFlag: "test-domain", taskListFlag: "test-tasklist", taskListType: "decision", numReadPartitions: 2, numWritePartitions: 2, }, { name: "Unsafe - removing non-drained partition", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "poller", }, }, PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: createPartitions(3), WritePartitions: createPartitions(2), }, }, nil).Times(1) f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: getPartitionTaskListName("test-tasklist", 2), Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), IncludeTaskListStatus: true, }).Return(&types.DescribeTaskListResponse{ Pollers: nil, TaskListStatus: &types.TaskListStatus{ BacklogCountHint: 1, }, PartitionConfig: nil, }, nil) }, expectedError: "partition 2 still has 1 tasks remaining", domainFlag: "test-domain", taskListFlag: "test-tasklist", taskListType: "decision", numReadPartitions: 2, numWritePartitions: 2, }, { name: "Unsafe - one type fails validation", setupMocks: func(client *admin.MockClient, f *frontend.MockClient) { f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeDecision.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "poller", }, }, PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: createPartitions(3), WritePartitions: createPartitions(3), }, }, nil).Times(1) f.EXPECT().DescribeTaskList(gomock.Any(), &types.DescribeTaskListRequest{ Domain: "test-domain", TaskList: &types.TaskList{Name: "test-tasklist", Kind: types.TaskListKindNormal.Ptr()}, TaskListType: types.TaskListTypeActivity.Ptr(), }).Return(&types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { Identity: "poller", }, }, PartitionConfig: &types.TaskListPartitionConfig{ Version: 1, ReadPartitions: createPartitions(2), WritePartitions: createPartitions(2), }, }, nil).Times(1) }, expectedError: "test-tasklist:Decision failed validation:", domainFlag: "test-domain", taskListFlag: "test-tasklist", numReadPartitions: 2, numWritePartitions: 2, }, } // Loop through test cases for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) if tt.setupMocks != nil { tt.setupMocks(td.mockAdminClient, td.mockFrontendClient) } var cliArgs []clitest.CliArgument if tt.domainFlag != "" { cliArgs = append(cliArgs, clitest.StringArgument(FlagDomain, tt.domainFlag)) } if tt.taskListFlag != "" { cliArgs = append(cliArgs, clitest.StringArgument(FlagTaskList, tt.taskListFlag)) } if tt.taskListType != "" { cliArgs = append(cliArgs, clitest.StringArgument(FlagTaskListType, tt.taskListType)) } if tt.numReadPartitions != 0 { cliArgs = append(cliArgs, clitest.IntArgument(FlagNumReadPartitions, tt.numReadPartitions)) } if tt.numWritePartitions != 0 { cliArgs = append(cliArgs, clitest.IntArgument(FlagNumWritePartitions, tt.numWritePartitions)) } if tt.force { cliArgs = append(cliArgs, clitest.BoolArgument(FlagForce, true)) } cliCtx := clitest.NewCLIContext( t, td.app, cliArgs..., ) err := AdminUpdateTaskListPartitionConfig(cliCtx) if tt.expectedError == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.expectedError) } }) } } ================================================ FILE: tools/cli/admin_timers.go ================================================ // The MIT License (MIT) // // Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination admin_timers_mock.go github.com/uber/cadence/tools/cli LoadCloser,Printer package cli import ( "context" "encoding/json" "fmt" "io" "os" "github.com/urfave/cli/v2" "github.com/uber/cadence/common" "github.com/uber/cadence/common/backoff" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/tools/common/commoncli" ) // LoadCloser loads timer task information type LoadCloser interface { Load() ([]*persistence.TimerTaskInfo, error) Close() } // Printer prints timer task information type Printer interface { Print(output io.Writer, timers []*persistence.TimerTaskInfo) error } // Reporter wraps LoadCloser, Printer and a filter on time task type and domainID type Reporter struct { domainID string timerTypes []int loader LoadCloser printer Printer } type dbLoadCloser struct { ctx *cli.Context executionManager persistence.ExecutionManager } type fileLoadCloser struct { file *os.File } type histogramPrinter struct { ctx *cli.Context timeFormat string } type jsonPrinter struct { ctx *cli.Context } // NewDBLoadCloser creates a new LoadCloser to load timer task information from database func NewDBLoadCloser(c *cli.Context) (LoadCloser, error) { shardID, err := getRequiredIntOption(c, FlagShardID) if err != nil { return nil, fmt.Errorf("error in NewDBLoadCloser: failed to get shard ID: %w", err) } executionManager, err := getDeps(c).initializeExecutionManager(c, shardID) if err != nil { return nil, fmt.Errorf("error in NewDBLoadCloser: failed to initialize execution store: %w", err) } return &dbLoadCloser{ ctx: c, executionManager: executionManager, }, nil } func NewFileLoadCloser(c *cli.Context) (LoadCloser, error) { file, err := os.Open(c.String(FlagInputFile)) if err != nil { return nil, fmt.Errorf("error in NewFileLoadCloser: cannot open file: %w", err) } return &fileLoadCloser{ file: file, }, nil } // NewReporter creates a new Reporter func NewReporter(domain string, timerTypes []int, loader LoadCloser, printer Printer) *Reporter { return &Reporter{ timerTypes: timerTypes, domainID: domain, loader: loader, printer: printer, } } // NewHistogramPrinter creates a new Printer to display timer task information in a histogram func NewHistogramPrinter(c *cli.Context, timeFormat string) Printer { return &histogramPrinter{ ctx: c, timeFormat: timeFormat, } } // NewJSONPrinter creates a new Printer to display timer task information in a JSON format func NewJSONPrinter(c *cli.Context) Printer { return &jsonPrinter{ ctx: c, } } func (r *Reporter) filter(timers []*persistence.TimerTaskInfo) []*persistence.TimerTaskInfo { taskTypes := intSliceToSet(r.timerTypes) for i, t := range timers { if len(r.domainID) > 0 && t.DomainID != r.domainID { timers[i] = nil continue } if _, ok := taskTypes[t.TaskType]; !ok { timers[i] = nil continue } } return timers } // Report loads, filters and prints timer tasks func (r *Reporter) Report(output io.Writer) error { loader, err := r.loader.Load() if err != nil { return err } return r.printer.Print(output, r.filter(loader)) } // AdminTimers is used to list scheduled timers. func AdminTimers(c *cli.Context) error { timerTypes := c.IntSlice(FlagTimerType) if !c.IsSet(FlagTimerType) || (len(timerTypes) == 1 && timerTypes[0] == -1) { timerTypes = []int{ persistence.TaskTypeDecisionTimeout, persistence.TaskTypeActivityTimeout, persistence.TaskTypeUserTimer, persistence.TaskTypeWorkflowTimeout, persistence.TaskTypeDeleteHistoryEvent, persistence.TaskTypeActivityRetryTimer, persistence.TaskTypeWorkflowBackoffTimer, } } // setup loader var loader LoadCloser var err error if !c.IsSet(FlagInputFile) { loader, err = NewDBLoadCloser(c) if err != nil { return commoncli.Problem("Error in timer: ", err) } } else { loader, err = NewFileLoadCloser(c) if err != nil { return commoncli.Problem("Error in timer: ", err) } } defer loader.Close() // setup printer var printer Printer if !c.Bool(FlagPrintJSON) { var timerFormat string if c.IsSet(FlagDateFormat) { timerFormat = c.String(FlagDateFormat) } else { switch c.String(FlagBucketSize) { case "day": timerFormat = "2006-01-02" case "hour": timerFormat = "2006-01-02T15" case "minute": timerFormat = "2006-01-02T15:04" case "second": timerFormat = "2006-01-02T15:04:05" default: return commoncli.Problem("unknown bucket size: "+c.String(FlagBucketSize), nil) } } printer = NewHistogramPrinter(c, timerFormat) } else { printer = NewJSONPrinter(c) } reporter := NewReporter(c.String(FlagDomainID), timerTypes, loader, printer) output := getDeps(c).Output() if err := reporter.Report(output); err != nil { return commoncli.Problem("Reporter failed", err) } return nil } func (jp *jsonPrinter) Print(output io.Writer, timers []*persistence.TimerTaskInfo) error { for _, t := range timers { if t == nil { continue } data, err := json.Marshal(t) if err != nil { if !jp.ctx.Bool(FlagSkipErrorMode) { return commoncli.Problem("cannot marshal timer to json", err) } output.Write([]byte(fmt.Sprintf("%s\n", err.Error()))) } else { output.Write([]byte(fmt.Sprintf("%s\n", data))) } } return nil } func (cl *dbLoadCloser) Load() ([]*persistence.TimerTaskInfo, error) { batchSize := cl.ctx.Int(FlagBatchSize) startDate := cl.ctx.String(FlagStartDate) endDate := cl.ctx.String(FlagEndDate) st, err := parseSingleTs(startDate) if err != nil { return nil, fmt.Errorf("wrong date format for "+FlagEndDate+" Error: %v", err) } et, err := parseSingleTs(endDate) if err != nil { return nil, fmt.Errorf("wrong date format for "+FlagEndDate+" Error: %v", err) } var timers []*persistence.TimerTaskInfo isRetryable := func(err error) bool { return persistence.IsTransientError(err) || common.IsContextTimeoutError(err) } throttleRetry := backoff.NewThrottleRetry( backoff.WithRetryPolicy(common.CreatePersistenceRetryPolicy()), backoff.WithRetryableError(isRetryable), ) var token []byte isFirstIteration := true for isFirstIteration || len(token) != 0 { isFirstIteration = false req := persistence.GetHistoryTasksRequest{ TaskCategory: persistence.HistoryTaskCategoryTimer, InclusiveMinTaskKey: persistence.NewHistoryTaskKey(st, 0), ExclusiveMaxTaskKey: persistence.NewHistoryTaskKey(et, 0), PageSize: batchSize, NextPageToken: token, } resp := &persistence.GetHistoryTasksResponse{} op := func(ctx context.Context) error { ctx, cancel, err := newContext(cl.ctx) defer cancel() if err != nil { return commoncli.Problem("Error in creating context:", err) } resp, err = cl.executionManager.GetHistoryTasks(ctx, &req) return err } err = throttleRetry.Do(cl.ctx.Context, op) if err != nil { return nil, fmt.Errorf("cannot get timer tasks for shard: %v", err) } token = resp.NextPageToken for _, task := range resp.Tasks { timer, err := task.ToTimerTaskInfo() if err != nil { return nil, fmt.Errorf("error converting task to timer task info: %w", err) } timers = append(timers, timer) } } return timers, nil } func (cl *dbLoadCloser) Close() { if cl.executionManager != nil { cl.executionManager.Close() } } func (fl *fileLoadCloser) Load() ([]*persistence.TimerTaskInfo, error) { var data []*persistence.TimerTaskInfo dec := json.NewDecoder(fl.file) for { var timer persistence.TimerTaskInfo if err := dec.Decode(&timer); err != nil { if err == io.EOF || err == io.ErrUnexpectedEOF { break } return nil, fmt.Errorf("error decoding timer: %w", err) } data = append(data, &timer) } return data, nil } func (fl *fileLoadCloser) Close() { if fl.file != nil { fl.file.Close() } } func (hp *histogramPrinter) Print(output io.Writer, timers []*persistence.TimerTaskInfo) error { h := NewHistogram() for _, t := range timers { if t == nil { continue } h.Add(t.VisibilityTimestamp.Format(hp.timeFormat)) } return h.Print(output, hp.ctx.Int(FlagShardMultiplier)) } ================================================ FILE: tools/cli/admin_timers_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber/cadence/tools/cli (interfaces: LoadCloser,Printer) // // Generated by this command: // // mockgen -package cli -destination admin_timers_mock.go github.com/uber/cadence/tools/cli LoadCloser,Printer // // Package cli is a generated GoMock package. package cli import ( io "io" reflect "reflect" gomock "go.uber.org/mock/gomock" persistence "github.com/uber/cadence/common/persistence" ) // MockLoadCloser is a mock of LoadCloser interface. type MockLoadCloser struct { ctrl *gomock.Controller recorder *MockLoadCloserMockRecorder isgomock struct{} } // MockLoadCloserMockRecorder is the mock recorder for MockLoadCloser. type MockLoadCloserMockRecorder struct { mock *MockLoadCloser } // NewMockLoadCloser creates a new mock instance. func NewMockLoadCloser(ctrl *gomock.Controller) *MockLoadCloser { mock := &MockLoadCloser{ctrl: ctrl} mock.recorder = &MockLoadCloserMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockLoadCloser) EXPECT() *MockLoadCloserMockRecorder { return m.recorder } // Close mocks base method. func (m *MockLoadCloser) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockLoadCloserMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockLoadCloser)(nil).Close)) } // Load mocks base method. func (m *MockLoadCloser) Load() ([]*persistence.TimerTaskInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Load") ret0, _ := ret[0].([]*persistence.TimerTaskInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // Load indicates an expected call of Load. func (mr *MockLoadCloserMockRecorder) Load() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Load", reflect.TypeOf((*MockLoadCloser)(nil).Load)) } // MockPrinter is a mock of Printer interface. type MockPrinter struct { ctrl *gomock.Controller recorder *MockPrinterMockRecorder isgomock struct{} } // MockPrinterMockRecorder is the mock recorder for MockPrinter. type MockPrinterMockRecorder struct { mock *MockPrinter } // NewMockPrinter creates a new mock instance. func NewMockPrinter(ctrl *gomock.Controller) *MockPrinter { mock := &MockPrinter{ctrl: ctrl} mock.recorder = &MockPrinterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPrinter) EXPECT() *MockPrinterMockRecorder { return m.recorder } // Print mocks base method. func (m *MockPrinter) Print(output io.Writer, timers []*persistence.TimerTaskInfo) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Print", output, timers) ret0, _ := ret[0].(error) return ret0 } // Print indicates an expected call of Print. func (mr *MockPrinterMockRecorder) Print(output, timers any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Print", reflect.TypeOf((*MockPrinter)(nil).Print), output, timers) } ================================================ FILE: tools/cli/admin_timers_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "encoding/json" "flag" "fmt" "os" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" "go.uber.org/mock/gomock" "github.com/uber/cadence/common/persistence" ) func TestNewDBLoadCloser(t *testing.T) { tests := []struct { name string contextSetup func(td *cliTestData) *cli.Context mockSetup func(td *cliTestData) expectedError string }{ { name: "Success", contextSetup: func(td *cliTestData) *cli.Context { set := flag.NewFlagSet("test", 0) set.Int(FlagShardID, 1, "shard ID flag") require.NoError(t, set.Set(FlagShardID, "1")) return cli.NewContext(td.app, set, nil) }, mockSetup: func(td *cliTestData) { mockExecManager := persistence.NewMockExecutionManager(gomock.NewController(t)) td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), 1).Return(mockExecManager, nil).Times(1) }, expectedError: "", }, { name: "Missing ShardID Error", contextSetup: func(td *cliTestData) *cli.Context { set := flag.NewFlagSet("test", 0) return cli.NewContext(td.app, set, nil) }, mockSetup: func(td *cliTestData) {}, expectedError: "error in NewDBLoadCloser: failed to get shard ID", }, { name: "Execution Manager Initialization Error", contextSetup: func(td *cliTestData) *cli.Context { set := flag.NewFlagSet("test", 0) set.Int(FlagShardID, 1, "shard ID flag") require.NoError(t, set.Set(FlagShardID, "1")) return cli.NewContext(td.app, set, nil) }, mockSetup: func(td *cliTestData) { td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), 1).Return(nil, fmt.Errorf("failed to initialize execution store")).Times(1) }, expectedError: "error in NewDBLoadCloser: failed to initialize execution store", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) c := tt.contextSetup(td) tt.mockSetup(td) _, err := NewDBLoadCloser(c) if tt.expectedError != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) } else { require.NoError(t, err) } }) } } func TestNewFileLoadCloser(t *testing.T) { tests := []struct { name string contextSetup func(td *cliTestData) (*cli.Context, string) // Return context and temp file path expectedError string }{ { name: "Success", // Setup context with a temporary file to simulate successful file opening contextSetup: func(td *cliTestData) (*cli.Context, string) { set := flag.NewFlagSet("test", 0) set.String(FlagInputFile, "", "Input file flag") // Create a temporary file for testing tempFile, err := os.CreateTemp("", "testfile_*.txt") require.NoError(t, err) // Close the file but do not remove it yet, so it remains available during the test require.NoError(t, tempFile.Close()) // Set FlagInputFile to the path of the temporary file require.NoError(t, set.Set(FlagInputFile, tempFile.Name())) return cli.NewContext(td.app, set, nil), tempFile.Name() // Return context and file path }, expectedError: "", }, { name: "File Open Error", // Setup context with a non-existent file path to simulate file open failure contextSetup: func(td *cliTestData) (*cli.Context, string) { set := flag.NewFlagSet("test", 0) set.String(FlagInputFile, "non_existent_file.txt", "input file flag") require.NoError(t, set.Set(FlagInputFile, "non_existent_file.txt")) return cli.NewContext(td.app, set, nil), "" // No file to delete after test }, expectedError: "error in NewFileLoadCloser: cannot open file", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Initialize test data td := newCLITestData(t) // Setup context and get temp file path c, tempFilePath := tt.contextSetup(td) // Defer file deletion after the test completes if tempFilePath != "" { defer os.Remove(tempFilePath) // Clean up the temporary file } // Call NewFileLoadCloser and capture result closer, err := NewFileLoadCloser(c) if tt.expectedError != "" { // Verify error message when expected require.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) assert.Nil(t, closer) } else { // Verify no error and a non-nil LoadCloser instance on success require.NoError(t, err) assert.NotNil(t, closer) closer.Close() // Close the file to prevent resource leakage } }) } } func TestNewReporter(t *testing.T) { // Mock dependencies mockLoader := &MockLoadCloser{} // Substitute with actual mock creation mockPrinter := &MockPrinter{} // Substitute with actual mock creation // Define test inputs domain := "testDomain" timerTypes := []int{1, 2, 3} // Call the function under test reporter := NewReporter(domain, timerTypes, mockLoader, mockPrinter) // Validate the output require.NotNil(t, reporter) assert.Equal(t, domain, reporter.domainID) assert.Equal(t, timerTypes, reporter.timerTypes) assert.Equal(t, mockLoader, reporter.loader) assert.Equal(t, mockPrinter, reporter.printer) } func TestNewHistogramPrinter(t *testing.T) { // Mock CLI context td := newCLITestData(t) // Setup test data for CLI context c := cli.NewContext(td.app, flag.NewFlagSet("test", 0), nil) // Define test inputs timeFormat := "2006-01-02 15:04:05" // Call the function under test printer := NewHistogramPrinter(c, timeFormat) // Validate the output require.NotNil(t, printer) histPrinter, ok := printer.(*histogramPrinter) require.True(t, ok, "Expected Printer to be of type *histogramPrinter") assert.Equal(t, c, histPrinter.ctx) assert.Equal(t, timeFormat, histPrinter.timeFormat) } func TestNewJSONPrinter(t *testing.T) { // Mock CLI context td := newCLITestData(t) // Setup test data for CLI context c := cli.NewContext(td.app, flag.NewFlagSet("test", 0), nil) // Call the function under test printer := NewJSONPrinter(c) // Validate the output require.NotNil(t, printer) jsonPrinter, ok := printer.(*jsonPrinter) require.True(t, ok, "Expected Printer to be of type *jsonPrinter") assert.Equal(t, c, jsonPrinter.ctx) } func TestReporter_Report(t *testing.T) { tests := []struct { name string domainID string timerTypes []int loaderOutput []*persistence.TimerTaskInfo loaderError error expectedOutput []*persistence.TimerTaskInfo expectedError string mockSetup func(mockLoader *MockLoadCloser, mockPrinter *MockPrinter) }{ { name: "Successful Report with Filtering", domainID: "testDomain", timerTypes: []int{1, 2}, loaderOutput: []*persistence.TimerTaskInfo{ {TaskType: 1, DomainID: "testDomain"}, {TaskType: 3, DomainID: "testDomain"}, {TaskType: 2, DomainID: "otherDomain"}, }, expectedOutput: []*persistence.TimerTaskInfo{ {TaskType: 1, DomainID: "testDomain"}, nil, // TaskType 3 filtered out nil, // DomainID otherDomain filtered out }, expectedError: "", mockSetup: func(mockLoader *MockLoadCloser, mockPrinter *MockPrinter) { mockLoader.EXPECT().Load().Return([]*persistence.TimerTaskInfo{ {TaskType: 1, DomainID: "testDomain"}, {TaskType: 3, DomainID: "testDomain"}, {TaskType: 2, DomainID: "otherDomain"}, }, nil).Times(1) mockPrinter.EXPECT().Print(gomock.Any(), gomock.Any()).Return(nil).Times(1) }, }, { name: "Loader Error", domainID: "testDomain", timerTypes: []int{1}, loaderOutput: nil, loaderError: fmt.Errorf("failed to load tasks"), expectedOutput: nil, expectedError: "failed to load tasks", mockSetup: func(mockLoader *MockLoadCloser, mockPrinter *MockPrinter) { mockLoader.EXPECT().Load().Return(nil, fmt.Errorf("failed to load tasks")).Times(1) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) // Initialize mocks mockLoader := NewMockLoadCloser(ctrl) mockPrinter := NewMockPrinter(ctrl) // Set up mock expectations tt.mockSetup(mockLoader, mockPrinter) // Create reporter instance reporter := NewReporter(tt.domainID, tt.timerTypes, mockLoader, mockPrinter) // Call Report and capture error err := reporter.Report(nil) // Check results based on expected error if tt.expectedError != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) } else { require.NoError(t, err) } }) } } func TestAdminTimers(t *testing.T) { tests := []struct { name string contextSetup func(td *cliTestData) *cli.Context mockSetup func(td *cliTestData) expectedError string expectedOutput string }{ { name: "Default Timer Types with DB Loader and Histogram Printer", contextSetup: func(td *cliTestData) *cli.Context { set := flag.NewFlagSet("test", 0) set.Var(cli.NewIntSlice(-1), FlagTimerType, "timer type flag") // Default timer types set.Bool(FlagPrintJSON, false, "print JSON flag") // Use histogram printer set.String(FlagBucketSize, "", "bucket size flag") set.Int(FlagShardID, 1, "shard ID flag") // Set shard ID for DB loader set.Int(FlagShardMultiplier, 1, "shard multiplier flag") // Set a non-zero multiplier set.String(FlagEndDate, "", "end date flag") // Explicitly set end date format set.String(FlagStartDate, "", "start date flag") // Explicitly set start date format require.NoError(t, set.Set(FlagTimerType, "-1")) require.NoError(t, set.Set(FlagPrintJSON, "false")) require.NoError(t, set.Set(FlagBucketSize, "hour")) require.NoError(t, set.Set(FlagShardID, "1")) require.NoError(t, set.Set(FlagShardMultiplier, "1")) require.NoError(t, set.Set(FlagEndDate, "2023-01-01T00:00:00Z")) require.NoError(t, set.Set(FlagStartDate, "2022-01-01T00:00:00Z")) return cli.NewContext(td.app, set, nil) }, mockSetup: func(td *cliTestData) { ctrl := gomock.NewController(t) mockExecManager := persistence.NewMockExecutionManager(ctrl) td.mockManagerFactory.EXPECT(). initializeExecutionManager(gomock.Any(), gomock.Any()). Return(mockExecManager, nil).Times(1) mockExecManager.EXPECT(). GetHistoryTasks(gomock.Any(), gomock.Any()). Return(&persistence.GetHistoryTasksResponse{ Tasks: []persistence.Task{ &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomain", WorkflowID: "testWorkflow1", RunID: "testRun1", }, TaskData: persistence.TaskData{ TaskID: 1, VisibilityTimestamp: time.Now(), }, }, &persistence.UserTimerTask{ WorkflowIdentifier: persistence.WorkflowIdentifier{ DomainID: "testDomain", WorkflowID: "testWorkflow2", RunID: "testRun2", }, TaskData: persistence.TaskData{ TaskID: 2, VisibilityTimestamp: time.Now().Add(time.Hour), }, }, }, NextPageToken: nil, }, nil).Times(1) mockExecManager.EXPECT().Close().Times(1) }, expectedError: "", expectedOutput: "Bucket Count\n", // Customize this with expected histogram output }, { name: "File Loader with JSON Printer", contextSetup: func(td *cliTestData) *cli.Context { set := flag.NewFlagSet("test", 0) set.Var(cli.NewIntSlice(persistence.TaskTypeUserTimer), FlagTimerType, "timer type flag") set.Bool(FlagPrintJSON, true, "print JSON flag") // Use JSON printer set.String(FlagInputFile, "", "input file flag") // Set input file for file loader // Create a temporary file and write test timer data to it tempFile, err := os.CreateTemp("", "testfile_*.txt") require.NoError(t, err) // Write a sample TimerTaskInfo JSON object to the file timerTask := persistence.TimerTaskInfo{ DomainID: "testDomain", WorkflowID: "testWorkflow1", RunID: "testRun1", VisibilityTimestamp: time.Now(), TaskID: 1, TaskType: persistence.TaskTypeUserTimer, } timerData, err := json.Marshal(timerTask) require.NoError(t, err) _, err = tempFile.Write(timerData) require.NoError(t, err) tempFile.Close() // Close after writing to ensure data is saved // Set flag values require.NoError(t, set.Set(FlagTimerType, fmt.Sprint(persistence.TaskTypeUserTimer))) require.NoError(t, set.Set(FlagPrintJSON, "true")) require.NoError(t, set.Set(FlagInputFile, tempFile.Name())) // Ensure file is removed after the test t.Cleanup(func() { os.Remove(tempFile.Name()) }) return cli.NewContext(td.app, set, nil) }, mockSetup: func(td *cliTestData) {}, expectedError: "", expectedOutput: `{"DomainID":"testDomain","WorkflowID":"testWorkflow1","RunID":"testRun1"`, // Partial match to validate JSON structure }, { name: "Error with Unknown Bucket Size", contextSetup: func(td *cliTestData) *cli.Context { set := flag.NewFlagSet("test", 0) set.String(FlagBucketSize, "", "bucket size flag") set.Int(FlagShardID, 1, "shard ID flag") require.NoError(t, set.Set(FlagBucketSize, "unknown")) require.NoError(t, set.Set(FlagShardID, "1")) return cli.NewContext(td.app, set, nil) }, mockSetup: func(td *cliTestData) { ctrl := gomock.NewController(t) mockExecManager := persistence.NewMockExecutionManager(ctrl) mockExecManager.EXPECT().Close().Times(1) td.mockManagerFactory.EXPECT(). initializeExecutionManager(gomock.Any(), gomock.Any()). Return(mockExecManager, nil).Times(1) }, expectedError: "unknown bucket size", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newCLITestData(t) c := tt.contextSetup(td) tt.mockSetup(td) err := AdminTimers(c) // Validate expected error if tt.expectedError != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) } else { require.NoError(t, err) // Validate the output using testIOHandler's outputBytes output := td.ioHandler.outputBytes.String() assert.Contains(t, output, tt.expectedOutput) } }) } } ================================================ FILE: tools/cli/app.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "fmt" "io" "github.com/urfave/cli/v2" "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/metrics" ) const depsKey = "deps" type CLIAppOptions func(*cli.App) // WithIOHandler sets the IOHandler for the CLI app. By default the app uses urfave's default Reader/Writer/ErrorWriter. func WithIOHandler(h IOHandler) CLIAppOptions { return func(app *cli.App) { if app.Metadata == nil { return } d, ok := app.Metadata[depsKey].(*deps) if !ok { return } d.IOHandler = h } } // WithManagerFactory sets the ManagerFactory for the CLI app. func WithManagerFactory(factory ManagerFactory) CLIAppOptions { return func(app *cli.App) { if app.Metadata == nil { return } d, ok := app.Metadata[depsKey].(*deps) if !ok { return } d.ManagerFactory = factory } } // NewCliApp instantiates a new instance of the CLI application func NewCliApp(cf ClientFactory, opts ...CLIAppOptions) *cli.App { version := fmt.Sprintf("CLI feature version: %v \n"+ " Release version: %v\n"+ " Build commit: %v\n"+ " Note: CLI feature version is for compatibility checking between server and CLI if enabled feature checking. Server is always backward compatible to older CLI versions, but not accepting newer than it can support.", client.SupportedCLIVersion, metrics.ReleaseVersion, metrics.Revision) app := cli.NewApp() app.Name = "cadence" app.Usage = "A command-line tool for cadence users" app.Version = version app.Metadata = map[string]any{ depsKey: &deps{ClientFactory: cf, IOHandler: &defaultIOHandler{app: app}, ManagerFactory: &defaultManagerFactory{}}, } app.Flags = []cli.Flag{ &cli.StringFlag{ Name: FlagAddress, Aliases: []string{"ad"}, Value: "", Usage: "host:port for cadence frontend service", EnvVars: []string{"CADENCE_CLI_ADDRESS"}, }, &cli.StringFlag{ Name: FlagDomain, Aliases: []string{"do"}, Usage: "cadence workflow domain", EnvVars: []string{"CADENCE_CLI_DOMAIN"}, }, &cli.IntFlag{ Name: FlagContextTimeout, Aliases: []string{"ct"}, Value: defaultContextTimeoutInSeconds, Usage: "optional timeout for context of RPC call in seconds", EnvVars: []string{"CADENCE_CONTEXT_TIMEOUT"}, }, &cli.StringFlag{ Name: FlagJWT, Usage: "optional JWT for authorization. Either this or --jwt-private-key is needed for jwt authorization", EnvVars: []string{"CADENCE_CLI_JWT"}, }, &cli.StringFlag{ Name: FlagJWTPrivateKey, Aliases: []string{"jwt-pk"}, Usage: "optional private key path to create JWT. Either this or --jwt is needed for jwt authorization. --jwt flag has priority over this one if both provided", EnvVars: []string{"CADENCE_CLI_JWT_PRIVATE_KEY"}, }, &cli.StringFlag{ Name: FlagTransport, Aliases: []string{"t"}, Usage: "optional argument for transport protocol format, either 'grpc' or 'tchannel'. Defaults to tchannel if not provided", EnvVars: []string{"CADENCE_CLI_TRANSPORT_PROTOCOL"}, }, &cli.StringFlag{ Name: FlagTLSCertPath, Aliases: []string{"tcp"}, Usage: "optional argument for path to TLS certificate. Defaults to an empty string if not provided", EnvVars: []string{"CADENCE_CLI_TLS_CERT_PATH"}, }, } app.Commands = []*cli.Command{ { Name: "domain", Aliases: []string{"d"}, Usage: "Operate cadence domain", Subcommands: newDomainCommands(), }, { Name: "workflow", Aliases: []string{"wf"}, Usage: "Operate cadence workflow", Subcommands: newWorkflowCommands(), }, { Name: "tasklist", Aliases: []string{"tl"}, Usage: "Operate cadence tasklist", Subcommands: newTaskListCommands(), }, { Name: "admin", Aliases: []string{"adm"}, Usage: "Run admin operation", Subcommands: []*cli.Command{ { Name: "workflow", Aliases: []string{"wf"}, Usage: "Run admin operation on workflow", Subcommands: newAdminWorkflowCommands(), }, { Name: "shard", Aliases: []string{"shar"}, Usage: "Run admin operation on specific shard", Subcommands: newAdminShardManagementCommands(), }, { Name: "history_host", Aliases: []string{"hist"}, Usage: "Run admin operation on history host", Subcommands: newAdminHistoryHostCommands(), }, { Name: "kafka", Aliases: []string{"ka"}, Usage: "Run admin operation on kafka messages", Subcommands: newAdminKafkaCommands(), }, { Name: "domain", Aliases: []string{"d"}, Usage: "Run admin operation on domain", Subcommands: newAdminDomainCommands(), }, { Name: "elasticsearch", Aliases: []string{"es"}, Usage: "Run admin operation on ElasticSearch", Subcommands: newAdminElasticSearchCommands(), }, { Name: "tasklist", Aliases: []string{"tl"}, Usage: "Run admin operation on taskList", Subcommands: newAdminTaskListCommands(), }, { Name: "cluster", Aliases: []string{"cl"}, Usage: "Run admin operation on cluster", Subcommands: newAdminClusterCommands(), }, { Name: "isolation-groups", Aliases: []string{"ig"}, Usage: "Run admin operation on isolation-groups", Subcommands: newAdminIsolationGroupCommands(), }, { Name: "dlq", Usage: "Run admin operation on DLQ", Subcommands: newAdminDLQCommands(), }, { Name: "database", Aliases: []string{"db"}, Usage: "Run admin operations on database", Subcommands: newDBCommands(), }, { Name: "queue", Aliases: []string{"q"}, Usage: "Run admin operations on queue", Subcommands: newAdminQueueCommands(), }, { Name: "async-wf-queue", Aliases: []string{"aq"}, Usage: "Run admin operations on async workflow queues", Subcommands: newAdminAsyncQueueCommands(), }, { Name: "config", Aliases: []string{"c"}, Usage: "Run admin operation on config store", Subcommands: newAdminConfigStoreCommands(), }, }, }, { Name: "cluster", Aliases: []string{"cl"}, Usage: "Operate cadence cluster", Subcommands: newClusterCommands(), }, } app.CommandNotFound = func(context *cli.Context, command string) { output := getDeps(context).Output() printMessage(output, "command not found: "+command) } for _, opt := range opts { opt(app) } return app } func getDeps(ctx *cli.Context) cliDeps { // currently Metadata is completely unused by urfave/cli/v2, and it has fewer ways to fail // than using the ctx.Context (as you must use RunContext to supply dependencies via the Context). // // this is fairly easy to move to ctx.Context if needed, it just leads to slightly more complex code. // intentionally panics when an invalid context is not passed in, to help collapse logic branches. // generally speaking this should not be possible to trigger without doing something obviously questionable. return ctx.App.Metadata[depsKey].(cliDeps) } // cliDeps is an interface primarily to allow it to be mocked in tests, // so individual client-getter funcs can be asserted as used or unused. // // exposing a struct may be good enough, it just hasn't been done yet. type cliDeps interface { ClientFactory IOHandler ManagerFactory } type IOHandler interface { // cli.Context does not contain readers/writers, they are only in cli.App. // which isn't passed to commands. // // since needing to output something is extremely common, this wrapper adds it to the context. // Input is the "primary" input to read from. // // This is currently always os.Stdin, as files are handled separately. Input() io.Reader // Output should be used to write to the primary output target. // This may be os.Stdout or a file (or an in-memory writer), // and it should only be used for the "results" of a command so values // can be redirected / grepped / etc reasonably. // // For progress or info-like output, use Progress() instead. // // For errors, return an error value instead. Output() io.Writer // Progress should be used for any "non-result" output, // e.g. "loading X" or "press enter to continue" or similar. // // This generally writes to os.Stderr so any displayed text will // be visible when the CLI is piped, and will not be sent into "result" files. // // For error reporting, return an error instead, such as a commoncli.Problem Progress() io.Writer } type defaultIOHandler struct { app *cli.App } func (d *defaultIOHandler) Input() io.Reader { return d.app.Reader } func (d *defaultIOHandler) Output() io.Writer { return d.app.Writer } func (d *defaultIOHandler) Progress() io.Writer { return d.app.ErrWriter } var _ cliDeps = &deps{} type deps struct { ClientFactory IOHandler ManagerFactory } ================================================ FILE: tools/cli/app_test.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "bytes" "fmt" "io" "os" "strings" "testing" "time" "github.com/olekukonko/tablewriter" "github.com/olivere/elastic" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/urfave/cli/v2" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/cli/clitest" ) type ( cliAppSuite struct { suite.Suite app *cli.App mockCtrl *gomock.Controller serverFrontendClient *frontend.MockClient serverAdminClient *admin.MockClient testIOHandler *testIOHandler } testcase struct { name string command string err string mock func() } ) var _ ClientFactory = (*clientFactoryMock)(nil) type clientFactoryMock struct { serverFrontendClient frontend.Client serverAdminClient admin.Client config *config.Config } func (m *clientFactoryMock) ServerFrontendClient(c *cli.Context) (frontend.Client, error) { return m.serverFrontendClient, nil } func (m *clientFactoryMock) ServerAdminClient(c *cli.Context) (admin.Client, error) { return m.serverAdminClient, nil } func (m *clientFactoryMock) ServerFrontendClientForMigration(c *cli.Context) (frontend.Client, error) { panic("not implemented") } func (m *clientFactoryMock) ServerAdminClientForMigration(c *cli.Context) (admin.Client, error) { panic("not implemented") } func (m *clientFactoryMock) ElasticSearchClient(c *cli.Context) (*elastic.Client, error) { panic("not implemented") } func (m *clientFactoryMock) ServerConfig(c *cli.Context) (*config.Config, error) { if m.config != nil { return m.config, nil } return nil, fmt.Errorf("config not set") } var commands = []string{ "domain", "d", "workflow", "wf", "tasklist", "tl", } var domainName = "cli-test-domain" // Implements IOHandler to be used for validation in tests type testIOHandler struct { outputBytes bytes.Buffer } func (t *testIOHandler) Input() io.Reader { return os.Stdin } func (t *testIOHandler) Output() io.Writer { return &t.outputBytes } func (t *testIOHandler) Progress() io.Writer { return os.Stdout } func TestCLIAppSuite(t *testing.T) { s := new(cliAppSuite) suite.Run(t, s) } func (s *cliAppSuite) SetupTest() { s.mockCtrl = gomock.NewController(s.T()) s.serverFrontendClient = frontend.NewMockClient(s.mockCtrl) s.serverAdminClient = admin.NewMockClient(s.mockCtrl) s.testIOHandler = &testIOHandler{} s.app = NewCliApp(&clientFactoryMock{ serverFrontendClient: s.serverFrontendClient, serverAdminClient: s.serverAdminClient, }, WithIOHandler(s.testIOHandler)) } func (s *cliAppSuite) TearDownTest() { s.mockCtrl.Finish() // assert mock’s expectations } func (s *cliAppSuite) SetupSubTest() { // this is required to re-establish mocks on subtest-level // otherwise you will confusingly see expectations from previous test-cases on errors s.SetupTest() } func (s *cliAppSuite) TearDownSubTest() { s.TearDownTest() } func (s *cliAppSuite) runTestCase(tt testcase) { if tt.mock != nil { tt.mock() } err := clitest.RunCommandLine(s.T(), s.app, tt.command) if tt.err != "" { assert.ErrorContains(s.T(), err, tt.err) } else { assert.NoError(s.T(), err) } } func (s *cliAppSuite) TestAppCommands() { for _, test := range commands { cmd := s.app.Command(test) s.NotNil(cmd) } } var describeDomainResponseServer = &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "test-domain", Description: "a test domain", OwnerEmail: "test@uber.com", }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 3, EmitMetric: true, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "active", Clusters: []*types.ClusterReplicationConfiguration{ { ClusterName: "active", }, { ClusterName: "standby", }, }, }, } // This is used to mock the domain deletion confirmation func (s *cliAppSuite) mockStdinInput(input string) func() { oldStdin := os.Stdin r, w, err := os.Pipe() s.Require().NoError(err) os.Stdin = r go func() { _, wrErr := w.Write([]byte(input + "\n")) s.Require().NoError(wrErr) err = w.Close() s.Require().NoError(err) }() return func() { os.Stdin = oldStdin r.Close() } } func (s *cliAppSuite) TestDomainDelete() { resp := describeDomainResponseServer s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(resp, nil) s.serverFrontendClient.EXPECT().DeleteDomain(gomock.Any(), gomock.Any()).Return(nil) defer s.mockStdinInput("Yes")() err := s.app.Run([]string{"", "--do", domainName, "domain", "delete", "--st", "test-token"}) s.Nil(err) } func (s *cliAppSuite) TestDomainDelete_DomainNotExist() { s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(nil, &types.EntityNotExistsError{}) s.Error(s.app.Run([]string{"", "--do", domainName, "domain", "delete", "--st", "test-token"})) } func (s *cliAppSuite) TestDomainDelete_Failed() { resp := describeDomainResponseServer s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(resp, nil) s.serverFrontendClient.EXPECT().DeleteDomain(gomock.Any(), gomock.Any()).Return(&types.BadRequestError{"mock error"}) defer s.mockStdinInput("Yes")() s.Error(s.app.Run([]string{"", "--do", domainName, "domain", "delete", "--st", "test-token"})) } func (s *cliAppSuite) TestDomainDelete_Cancelled() { resp := describeDomainResponseServer s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(resp, nil) oldStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w defer func() { os.Stdout = oldStdout }() defer s.mockStdinInput("No")() err := s.app.Run([]string{"", "--do", domainName, "domain", "delete", "--st", "test-token"}) s.Nil(err) w.Close() var stdoutBuf bytes.Buffer io.Copy(&stdoutBuf, r) s.Contains(stdoutBuf.String(), "Domain deletion cancelled") } func (s *cliAppSuite) TestDomainDeprecate() { s.serverFrontendClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(&types.StartWorkflowExecutionResponse{ RunID: "run-id-example", }, nil) err := s.app.Run([]string{"", "--do", domainName, "domain", "deprecate", "--st", "secretToken"}) s.Nil(err) } func (s *cliAppSuite) TestDomainDeprecate_FailedToStartDeprecationWorkflow() { s.serverFrontendClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &types.BadRequestError{"mock error"}) s.Error(s.app.Run([]string{"", "--do", domainName, "domain", "deprecate", "--st", "secretToken"})) } func (s *cliAppSuite) TestDomainDescribe() { resp := describeDomainResponseServer s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "domain", "describe"}) s.Nil(err) } func (s *cliAppSuite) TestDomainDescribe_DomainNotExist() { resp := describeDomainResponseServer s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(resp, &types.EntityNotExistsError{}) s.Error(s.app.Run([]string{"", "--do", domainName, "domain", "describe"})) } func (s *cliAppSuite) TestDomainDescribe_Failed() { resp := describeDomainResponseServer s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(resp, &types.BadRequestError{"faked error"}) s.Error(s.app.Run([]string{"", "--do", domainName, "domain", "describe"})) } var ( eventType = types.EventTypeWorkflowExecutionStarted getWorkflowExecutionHistoryResponse = &types.GetWorkflowExecutionHistoryResponse{ History: &types.History{ Events: []*types.HistoryEvent{ { EventType: &eventType, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: "TestWorkflow"}, TaskList: &types.TaskList{Name: "taskList"}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "tester", }, }, }, }, NextPageToken: nil, } ) func (s *cliAppSuite) TestShowHistory() { resp := getWorkflowExecutionHistoryResponse s.serverFrontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any()).Return(resp, nil) describeResp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, } s.serverFrontendClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(describeResp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "show", "-w", "wid"}) s.Nil(err) } func (s *cliAppSuite) TestShowHistoryWithID() { resp := getWorkflowExecutionHistoryResponse s.serverFrontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any()).Return(resp, nil) describeResp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, } s.serverFrontendClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(describeResp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "showid", "wid"}) s.Nil(err) } func (s *cliAppSuite) TestShowHistoryWithQueryConsistencyLevel() { resp := getWorkflowExecutionHistoryResponse s.serverFrontendClient.EXPECT(). GetWorkflowExecutionHistory(gomock.Any(), &types.GetWorkflowExecutionHistoryRequest{ Domain: domainName, Execution: &types.WorkflowExecution{ WorkflowID: "wid", }, HistoryEventFilterType: types.HistoryEventFilterTypeAllEvent.Ptr(), QueryConsistencyLevel: types.QueryConsistencyLevelStrong.Ptr(), }). Return(resp, nil) describeResp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, } s.serverFrontendClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(describeResp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "show", "-w", "wid", "--query_consistency_level", "strong"}) s.Nil(err) } func (s *cliAppSuite) TestShowHistoryWithInvalidQueryConsistencyLevel() { err := s.app.Run([]string{"", "--do", domainName, "workflow", "show", "-w", "wid", "--query_consistency_level", "invalid"}) s.Error(err) s.Contains(err.Error(), "invalid query consistency level") } func (s *cliAppSuite) TestShowHistory_PrintRawTime() { resp := getWorkflowExecutionHistoryResponse s.serverFrontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any()).Return(resp, nil) describeResp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, } s.serverFrontendClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(describeResp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "show", "-w", "wid", "-prt"}) s.Nil(err) } func (s *cliAppSuite) TestShowHistory_PrintDateTime() { resp := getWorkflowExecutionHistoryResponse s.serverFrontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any()).Return(resp, nil) describeResp := &types.DescribeWorkflowExecutionResponse{ WorkflowExecutionInfo: &types.WorkflowExecutionInfo{}, } s.serverFrontendClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(describeResp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "show", "-w", "wid", "-pdt"}) s.Nil(err) } func (s *cliAppSuite) TestRestartWorkflow() { resp := &types.RestartWorkflowExecutionResponse{RunID: uuid.New()} s.serverFrontendClient.EXPECT().RestartWorkflowExecution(gomock.Any(), gomock.Any()).Return(resp, nil).Times(1) err := s.app.Run([]string{"", "--do", domainName, "workflow", "restart", "-w", "wid"}) s.Nil(err) } func (s *cliAppSuite) TestRestartWorkflow_Failed() { resp := &types.RestartWorkflowExecutionResponse{RunID: uuid.New()} s.serverFrontendClient.EXPECT().RestartWorkflowExecution(gomock.Any(), gomock.Any()).Return(resp, &types.BadRequestError{"faked error"}) s.Error(s.app.Run([]string{"", "--do", domainName, "workflow", "restart", "-w", "wid"})) } func (s *cliAppSuite) TestDiagnoseWorkflow() { resp := &types.DiagnoseWorkflowExecutionResponse{Domain: "test", DiagnosticWorkflowExecution: &types.WorkflowExecution{WorkflowID: "123", RunID: uuid.New()}} s.serverFrontendClient.EXPECT().DiagnoseWorkflowExecution(gomock.Any(), gomock.Any()).Return(resp, nil).Times(1) err := s.app.Run([]string{"", "--do", domainName, "workflow", "diagnose", "-w", "wid", "-r", "rid"}) s.Nil(err) } func (s *cliAppSuite) TestDiagnoseWorkflow_Failed() { s.serverFrontendClient.EXPECT().DiagnoseWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &types.BadRequestError{"faked error"}) s.Error(s.app.Run([]string{"", "--do", domainName, "workflow", "diagnose", "-w", "wid", "-r", "rid"})) } func (s *cliAppSuite) TestStartWorkflow() { resp := &types.StartWorkflowExecutionResponse{RunID: uuid.New()} s.serverFrontendClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(resp, nil).Times(2) // start with wid err := s.app.Run([]string{"", "--do", domainName, "workflow", "start", "-tl", "testTaskList", "-wt", "testWorkflowType", "-et", "60", "-w", "wid", "wrp", "2"}) s.Nil(err) // start without wid err = s.app.Run([]string{"", "--do", domainName, "workflow", "start", "-tl", "testTaskList", "-wt", "testWorkflowType", "-et", "60", "wrp", "2"}) s.Nil(err) } func (s *cliAppSuite) TestStartWorkflow_Failed() { resp := &types.StartWorkflowExecutionResponse{RunID: uuid.New()} s.serverFrontendClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(resp, &types.BadRequestError{"faked error"}) // start with wid s.Error(s.app.Run([]string{"", "--do", domainName, "workflow", "start", "-tl", "testTaskList", "-wt", "testWorkflowType", "-et", "60", "-w", "wid"})) } func (s *cliAppSuite) TestRunWorkflow() { resp := &types.StartWorkflowExecutionResponse{RunID: uuid.New()} history := getWorkflowExecutionHistoryResponse s.serverFrontendClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(resp, nil).Times(2) s.serverFrontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any()).Return(history, nil).Times(2) // start with wid err := s.app.Run([]string{"", "--do", domainName, "workflow", "run", "-tl", "testTaskList", "-wt", "testWorkflowType", "-et", "60", "-w", "wid", "wrp", "2"}) s.Nil(err) // start without wid err = s.app.Run([]string{"", "--do", domainName, "workflow", "run", "-tl", "testTaskList", "-wt", "testWorkflowType", "-et", "60", "wrp", "2"}) s.Nil(err) } func (s *cliAppSuite) TestRunWorkflow_Failed() { resp := &types.StartWorkflowExecutionResponse{RunID: uuid.New()} s.serverFrontendClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any()).Return(resp, &types.BadRequestError{"faked error"}) // start with wid s.Error(s.app.Run([]string{"", "--do", domainName, "workflow", "run", "-tl", "testTaskList", "-wt", "testWorkflowType", "-et", "60", "-w", "wid"})) } func (s *cliAppSuite) TestTerminateWorkflow() { s.serverFrontendClient.EXPECT().TerminateWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "terminate", "-w", "wid"}) s.Nil(err) } func (s *cliAppSuite) TestTerminateWorkflow_Failed() { s.serverFrontendClient.EXPECT().TerminateWorkflowExecution(gomock.Any(), gomock.Any()).Return(&types.BadRequestError{"faked error"}) s.Error(s.app.Run([]string{"", "--do", domainName, "workflow", "terminate", "-w", "wid"})) } func (s *cliAppSuite) TestCancelWorkflow() { s.serverFrontendClient.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "cancel", "-w", "wid"}) s.Nil(err) } func (s *cliAppSuite) TestCancelWorkflow_Failed() { s.serverFrontendClient.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), gomock.Any()).Return(&types.BadRequestError{"faked error"}) s.Error(s.app.Run([]string{"", "--do", domainName, "workflow", "cancel", "-w", "wid"})) } func (s *cliAppSuite) TestSignalWorkflow() { s.serverFrontendClient.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "signal", "-w", "wid", "-n", "signal-name"}) s.Nil(err) } func (s *cliAppSuite) TestSignalWorkflow_Failed() { s.serverFrontendClient.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any()).Return(&types.BadRequestError{"faked error"}) s.Error(s.app.Run([]string{"", "--do", domainName, "workflow", "signal", "-w", "wid", "-n", "signal-name"})) } func (s *cliAppSuite) TestQueryWorkflowUsingStackTrace() { resp := &types.QueryWorkflowResponse{ QueryResult: []byte("query-result"), } s.serverFrontendClient.EXPECT().QueryWorkflow(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "stack", "-w", "wid"}) s.Nil(err) } var ( closeStatus = types.WorkflowExecutionCloseStatusCompleted listClosedWorkflowExecutionsResponse = &types.ListClosedWorkflowExecutionsResponse{ Executions: []*types.WorkflowExecutionInfo{ { Execution: &types.WorkflowExecution{ WorkflowID: "test-list-workflow-id", RunID: uuid.New(), }, Type: &types.WorkflowType{ Name: "test-list-workflow-type", }, StartTime: common.Int64Ptr(time.Now().UnixNano()), CloseTime: common.Int64Ptr(time.Now().Add(time.Hour).UnixNano()), CloseStatus: &closeStatus, HistoryLength: 12, }, }, } listOpenWorkflowExecutionsResponse = &types.ListOpenWorkflowExecutionsResponse{ Executions: []*types.WorkflowExecutionInfo{ { Execution: &types.WorkflowExecution{ WorkflowID: "test-list-open-workflow-id", RunID: uuid.New(), }, Type: &types.WorkflowType{ Name: "test-list-open-workflow-type", }, StartTime: common.Int64Ptr(time.Now().UnixNano()), CloseTime: common.Int64Ptr(time.Now().Add(time.Hour).UnixNano()), HistoryLength: 12, }, }, } ) func (s *cliAppSuite) TestListWorkflow() { resp := listClosedWorkflowExecutionsResponse countWorkflowResp := &types.CountWorkflowExecutionsResponse{} s.serverFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(countWorkflowResp, nil) s.serverFrontendClient.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "list"}) s.Nil(err) } func (s *cliAppSuite) TestListWorkflow_WithWorkflowID() { resp := &types.ListClosedWorkflowExecutionsResponse{} countWorkflowResp := &types.CountWorkflowExecutionsResponse{} s.serverFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(countWorkflowResp, nil) s.serverFrontendClient.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "list", "-wid", "nothing"}) s.Nil(err) } func (s *cliAppSuite) TestListWorkflow_WithWorkflowType() { resp := &types.ListClosedWorkflowExecutionsResponse{} countWorkflowResp := &types.CountWorkflowExecutionsResponse{} s.serverFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(countWorkflowResp, nil) s.serverFrontendClient.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "list", "-wt", "no-type"}) s.Nil(err) } func (s *cliAppSuite) TestListWorkflow_PrintDateTime() { resp := listClosedWorkflowExecutionsResponse countWorkflowResp := &types.CountWorkflowExecutionsResponse{} s.serverFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(countWorkflowResp, nil) s.serverFrontendClient.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "list", "-pdt"}) s.Nil(err) } func (s *cliAppSuite) TestListWorkflow_PrintRawTime() { resp := listClosedWorkflowExecutionsResponse countWorkflowResp := &types.CountWorkflowExecutionsResponse{} s.serverFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(countWorkflowResp, nil) s.serverFrontendClient.EXPECT().ListClosedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "list", "-prt"}) s.Nil(err) } func (s *cliAppSuite) TestListWorkflow_Open() { resp := listOpenWorkflowExecutionsResponse countWorkflowResp := &types.CountWorkflowExecutionsResponse{} s.serverFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(countWorkflowResp, nil) s.serverFrontendClient.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "list", "-op"}) s.Nil(err) } func (s *cliAppSuite) TestListWorkflow_Open_WithWorkflowID() { resp := &types.ListOpenWorkflowExecutionsResponse{} countWorkflowResp := &types.CountWorkflowExecutionsResponse{} s.serverFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(countWorkflowResp, nil) s.serverFrontendClient.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "list", "-op", "-wid", "nothing"}) s.Nil(err) } func (s *cliAppSuite) TestListWorkflow_Open_WithWorkflowType() { resp := &types.ListOpenWorkflowExecutionsResponse{} countWorkflowResp := &types.CountWorkflowExecutionsResponse{} s.serverFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(countWorkflowResp, nil) s.serverFrontendClient.EXPECT().ListOpenWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "list", "-op", "-wt", "no-type"}) s.Nil(err) } func (s *cliAppSuite) TestListArchivedWorkflow() { resp := &types.ListArchivedWorkflowExecutionsResponse{} s.serverFrontendClient.EXPECT().ListArchivedWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "listarchived", "-q", "some query string", "--ps", "200", "--all"}) s.Nil(err) } func (s *cliAppSuite) TestCountWorkflow() { resp := &types.CountWorkflowExecutionsResponse{} s.serverFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "workflow", "count"}) s.Nil(err) s.serverFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(resp, nil) err = s.app.Run([]string{"", "--do", domainName, "workflow", "count", "-q", "'CloseTime = missing'"}) s.Nil(err) } var describeTaskListResponse = &types.DescribeTaskListResponse{ Pollers: []*types.PollerInfo{ { LastAccessTime: common.Int64Ptr(time.Now().UnixNano()), Identity: "tester", }, }, } func (s *cliAppSuite) TestAdminDescribeWorkflow() { resp := &types.AdminDescribeWorkflowExecutionResponse{ ShardID: "test-shard-id", HistoryAddr: "ip:port", MutableStateInDatabase: "{\"ExecutionInfo\":{\"BranchToken\":\"WQsACgAAACQ2MzI5YzEzMi1mMGI0LTQwZmUtYWYxMS1hODVmMDA3MzAzODQLABQAAAAkOWM5OWI1MjItMGEyZi00NTdmLWEyNDgtMWU0OTA0ZDg4YzVhDwAeDAAAAAAA\"}}", } s.serverAdminClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "admin", "wf", "describe", "-w", "test-wf-id"}) s.Nil(err) } func (s *cliAppSuite) TestAdminDescribeWorkflow_Failed() { s.serverAdminClient.EXPECT().DescribeWorkflowExecution(gomock.Any(), gomock.Any()).Return(nil, &types.BadRequestError{"faked error"}) s.Error(s.app.Run(([]string{"", "--do", domainName, "admin", "wf", "describe", "-w", "test-wf-id"}))) } func (s *cliAppSuite) TestAdminAddSearchAttribute() { var promptMsg string promptFn = func(msg string) { promptMsg = msg } request := &types.AddSearchAttributeRequest{ SearchAttribute: map[string]types.IndexedValueType{ "testKey": types.IndexedValueType(1), }, } s.serverAdminClient.EXPECT().AddSearchAttribute(gomock.Any(), request).Return(nil) err := s.app.Run([]string{"", "--do", domainName, "admin", "cl", "asa", "--search_attr_key", "testKey", "--search_attr_type", "1"}) s.Equal("Are you trying to add key [testKey] with Type [Keyword]? y/N", promptMsg) s.Nil(err) } func (s *cliAppSuite) TestAdminFailover() { resp := &types.StartWorkflowExecutionResponse{RunID: uuid.New()} s.serverFrontendClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(resp, nil) s.serverFrontendClient.EXPECT().SignalWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) err := s.app.Run([]string{"", "admin", "cl", "fo", "start", "--tc", "standby", "--sc", "active"}) s.Nil(err) } func (s *cliAppSuite) TestDescribeTaskList() { resp := describeTaskListResponse s.serverFrontendClient.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "tasklist", "describe", "-tl", "test-taskList"}) s.Nil(err) } func (s *cliAppSuite) TestDescribeTaskList_Activity() { resp := describeTaskListResponse s.serverFrontendClient.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any()).Return(resp, nil) err := s.app.Run([]string{"", "--do", domainName, "tasklist", "describe", "-tl", "test-taskList", "-tlt", "activity"}) s.Nil(err) } func (s *cliAppSuite) TestObserveWorkflow() { history := getWorkflowExecutionHistoryResponse s.serverFrontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any()).Return(history, nil).Times(2) err := s.app.Run([]string{"", "--do", domainName, "workflow", "observe", "-w", "wid"}) s.Nil(err) err = s.app.Run([]string{"", "--do", domainName, "workflow", "observe", "-w", "wid", "-sd"}) s.Nil(err) } func (s *cliAppSuite) TestObserveWorkflowWithID() { history := getWorkflowExecutionHistoryResponse s.serverFrontendClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any()).Return(history, nil).Times(2) err := s.app.Run([]string{"", "--do", domainName, "workflow", "observeid", "wid"}) s.Nil(err) err = s.app.Run([]string{"", "--do", domainName, "workflow", "observeid", "wid", "-sd"}) s.Nil(err) } // TestParseTime tests the parsing of date argument in UTC and UnixNano formats func (s *cliAppSuite) TestParseTime() { pt, err := parseTime("", 100) s.NoError(err) s.Equal(int64(100), pt) pt, err = parseTime("2018-06-07T15:04:05+00:00", 0) s.Equal(int64(1528383845000000000), pt) pt, err = parseTime("1528383845000000000", 0) s.Equal(int64(1528383845000000000), pt) } // TestParseTimeDateRange tests the parsing of date argument in time range format, N // where N is the integral multiplier, and duration can be second/minute/hour/day/week/month/year func (s *cliAppSuite) TestParseTimeDateRange() { tests := []struct { timeStr string // input defVal int64 // input expected int64 // expected unix nano (approx) }{ { timeStr: "1s", defVal: int64(0), expected: time.Now().Add(-time.Second).UnixNano(), }, { timeStr: "100second", defVal: int64(0), expected: time.Now().Add(-100 * time.Second).UnixNano(), }, { timeStr: "2m", defVal: int64(0), expected: time.Now().Add(-2 * time.Minute).UnixNano(), }, { timeStr: "200minute", defVal: int64(0), expected: time.Now().Add(-200 * time.Minute).UnixNano(), }, { timeStr: "3h", defVal: int64(0), expected: time.Now().Add(-3 * time.Hour).UnixNano(), }, { timeStr: "1000hour", defVal: int64(0), expected: time.Now().Add(-1000 * time.Hour).UnixNano(), }, { timeStr: "5d", defVal: int64(0), expected: time.Now().Add(-5 * day).UnixNano(), }, { timeStr: "25day", defVal: int64(0), expected: time.Now().Add(-25 * day).UnixNano(), }, { timeStr: "5w", defVal: int64(0), expected: time.Now().Add(-5 * week).UnixNano(), }, { timeStr: "52week", defVal: int64(0), expected: time.Now().Add(-52 * week).UnixNano(), }, { timeStr: "3M", defVal: int64(0), expected: time.Now().Add(-3 * month).UnixNano(), }, { timeStr: "6month", defVal: int64(0), expected: time.Now().Add(-6 * month).UnixNano(), }, { timeStr: "1y", defVal: int64(0), expected: time.Now().Add(-year).UnixNano(), }, { timeStr: "7year", defVal: int64(0), expected: time.Now().Add(-7 * year).UnixNano(), }, { timeStr: "100y", // epoch time will be returned as that's the minimum unix timestamp possible defVal: int64(0), expected: time.Unix(0, 0).UnixNano(), }, } delta := int64(50 * time.Millisecond) for _, te := range tests { pt, err := parseTime(te.timeStr, te.defVal) s.NoError(err) s.True(te.expected <= pt) pt, err = parseTime(te.timeStr, te.defVal) s.True(te.expected+delta >= pt) } } func (s *cliAppSuite) TestBreakLongWords() { s.Equal("111 222 333 4", breakLongWords("1112223334", 3)) s.Equal("111 2 223", breakLongWords("1112 223", 3)) s.Equal("11 122 23", breakLongWords("11 12223", 3)) s.Equal("111", breakLongWords("111", 3)) s.Equal("", breakLongWords("", 3)) s.Equal("111 222", breakLongWords("111 222", 3)) } func (s *cliAppSuite) TestAnyToString() { arg := strings.Repeat("LongText", 80) event := &types.HistoryEvent{ ID: 1, EventType: &eventType, WorkflowExecutionStartedEventAttributes: &types.WorkflowExecutionStartedEventAttributes{ WorkflowType: &types.WorkflowType{Name: "code.uber.internal/devexp/cadence-samples.git/cmd/samples/recipes/helloworld.Workflow"}, TaskList: &types.TaskList{Name: "taskList"}, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(60), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(10), Identity: "tester", Input: []byte(arg), }, } res := anyToString(event, false, defaultMaxFieldLength) ss, l := tablewriter.WrapString(res, 10) s.Equal(9, len(ss)) s.Equal(147, l) } func (s *cliAppSuite) TestAnyToString_DecodeMapValues() { fields := map[string][]byte{ "TestKey": []byte("testValue"), } execution := &types.WorkflowExecutionInfo{ Memo: &types.Memo{Fields: fields}, } s.Equal("{HistoryLength:0, Memo:{Fields:map{TestKey:testValue}}, IsCron:false, PartitionConfig:map{}}", anyToString(execution, true, 0)) fields["TestKey2"] = []byte(`anotherTestValue`) execution.Memo = &types.Memo{Fields: fields} got := anyToString(execution, true, 0) expected := got == "{HistoryLength:0, Memo:{Fields:map{TestKey2:anotherTestValue, TestKey:testValue}}, IsCron:false, PartitionConfig:map{}}" || got == "{HistoryLength:0, Memo:{Fields:map{TestKey:testValue, TestKey2:anotherTestValue}}, IsCron:false, PartitionConfig:map{}}" s.True(expected) } func (s *cliAppSuite) TestIsAttributeName() { s.True(isAttributeName("WorkflowExecutionStartedEventAttributes")) s.False(isAttributeName("workflowExecutionStartedEventAttributes")) } func (s *cliAppSuite) TestGetWorkflowIdReusePolicy() { res, err := getWorkflowIDReusePolicy(2) s.NoError(err) s.Equal(res.String(), types.WorkflowIDReusePolicyRejectDuplicate.String()) } func (s *cliAppSuite) TestGetWorkflowIdReusePolicy_Failed_ExceedRange() { _, err := getWorkflowIDReusePolicy(2147483647) s.Error(err) } func (s *cliAppSuite) TestGetWorkflowIdReusePolicy_Failed_Negative() { _, err := getWorkflowIDReusePolicy(-1) s.Error(err) } func (s *cliAppSuite) TestGetSearchAttributes() { resp := &types.GetSearchAttributesResponse{} s.serverFrontendClient.EXPECT().GetSearchAttributes(gomock.Any()).Return(resp, nil).Times(2) err := s.app.Run([]string{"", "cluster", "get-search-attr"}) s.Nil(err) err = s.app.Run([]string{"", "--do", domainName, "cluster", "get-search-attr"}) s.Nil(err) } func (s *cliAppSuite) TestParseBool() { res, err := parseBool("true") s.NoError(err) s.True(res) res, err = parseBool("false") s.NoError(err) s.False(res) for _, v := range []string{"True, TRUE, False, FALSE, T, F"} { res, err = parseBool(v) s.Error(err) s.False(res) } } func (s *cliAppSuite) TestConvertStringToRealType() { var res interface{} // int res = convertStringToRealType("1") s.Equal(int64(1), res) // bool res = convertStringToRealType("true") s.Equal(true, res) res = convertStringToRealType("false") s.Equal(false, res) // double res = convertStringToRealType("1.0") s.Equal(float64(1.0), res) // datetime res = convertStringToRealType("2019-01-01T01:01:01Z") s.Equal(time.Date(2019, 1, 1, 1, 1, 1, 0, time.UTC), res) // array res = convertStringToRealType(`["a", "b", "c"]`) s.Equal([]interface{}{"a", "b", "c"}, res) // string res = convertStringToRealType("test string") s.Equal("test string", res) } func (s *cliAppSuite) TestConvertArray() { t1, _ := time.Parse(defaultDateTimeFormat, "2019-06-07T16:16:34-08:00") t2, _ := time.Parse(defaultDateTimeFormat, "2019-06-07T17:16:34-08:00") testCases := []struct { name string input string expected interface{} }{ { name: "string", input: `["a", "b", "c"]`, expected: []interface{}{"a", "b", "c"}, }, { name: "int", input: `[1, 2, 3]`, expected: []interface{}{"1", "2", "3"}, }, { name: "double", input: `[1.1, 2.2, 3.3]`, expected: []interface{}{"1.1", "2.2", "3.3"}, }, { name: "bool", input: `["true", "false"]`, expected: []interface{}{"true", "false"}, }, { name: "datetime", input: `["2019-06-07T16:16:34-08:00", "2019-06-07T17:16:34-08:00"]`, expected: []interface{}{t1, t2}, }, } for _, testCase := range testCases { res, err := parseArray(testCase.input) s.Nil(err) s.Equal(testCase.expected, res) } testCases2 := []struct { name string input string expected error }{ { name: "not array", input: "normal string", }, { name: "empty string", input: "", }, { name: "not json array", input: "[a, b, c]", }, } for _, testCase := range testCases2 { res, err := parseArray(testCase.input) s.NotNil(err) s.Nil(res) } } func TestWithManagerFactory(t *testing.T) { tests := []struct { name string app *cli.App expectFactory bool expectNilMeta bool expectDepsType bool }{ { name: "Valid Metadata with deps key", app: &cli.App{ Metadata: map[string]interface{}{ depsKey: &deps{}, }, }, expectFactory: true, expectNilMeta: false, expectDepsType: true, }, { name: "No Metadata", app: &cli.App{Metadata: nil}, expectFactory: false, expectNilMeta: true, expectDepsType: false, }, { name: "Incorrect deps key type", app: &cli.App{ Metadata: map[string]interface{}{ depsKey: "invalid_type", }, }, expectFactory: false, expectNilMeta: false, expectDepsType: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockFactory := NewMockManagerFactory(ctrl) option := WithManagerFactory(mockFactory) option(tt.app) if tt.expectNilMeta { assert.Nil(t, tt.app.Metadata, "Expected Metadata to be nil") } else { d, ok := tt.app.Metadata[depsKey].(*deps) if tt.expectDepsType { assert.True(t, ok, "Expected depsKey to be of type *deps") if tt.expectFactory { assert.Equal(t, mockFactory, d.ManagerFactory, "Expected ManagerFactory to be set correctly") } } else { assert.False(t, ok, "Expected depsKey not to be of type *deps") } } }) } } func TestWithIOHandler(t *testing.T) { tests := []struct { name string app *cli.App expectHandler bool expectNilMeta bool expectDepsType bool }{ { name: "Valid Metadata with deps key", app: &cli.App{ Metadata: map[string]interface{}{ depsKey: &deps{}, }, }, expectHandler: true, expectNilMeta: false, expectDepsType: true, }, { name: "No Metadata", app: &cli.App{Metadata: nil}, expectHandler: false, expectNilMeta: true, expectDepsType: false, }, { name: "Incorrect deps key type", app: &cli.App{ Metadata: map[string]interface{}{ depsKey: "invalid_type", }, }, expectHandler: false, expectNilMeta: false, expectDepsType: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { testHandler := testIOHandler{} option := WithIOHandler(&testHandler) option(tt.app) if tt.expectNilMeta { assert.Nil(t, tt.app.Metadata, "Expected Metadata to be nil") } else { d, ok := tt.app.Metadata[depsKey].(*deps) if tt.expectDepsType { assert.True(t, ok, "Expected depsKey to be of type *deps") if tt.expectHandler { assert.Equal(t, &testHandler, d.IOHandler, "Expected IOHandler to be set correctly") } } else { assert.False(t, ok, "Expected depsKey not to be of type *deps") } } }) } } ================================================ FILE: tools/cli/clitest/context.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clitest import ( "flag" "strconv" "testing" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" ) // CliArgument is a way to apply argument to given flagset/context type CliArgument func(t *testing.T, flags *flag.FlagSet, c *cli.Context) // StringArgument introduces a new string argument for cli context func StringArgument(name, value string) CliArgument { return func(t *testing.T, flags *flag.FlagSet, c *cli.Context) { t.Helper() flags.String(name, "", "") require.NoError(t, c.Set(name, value)) } } // IntArgument introduces a new int argument for cli context func IntArgument(name string, value int) CliArgument { return func(t *testing.T, flags *flag.FlagSet, c *cli.Context) { t.Helper() flags.Int(name, 0, "") require.NoError(t, c.Set(name, strconv.Itoa(value))) } } // BoolArgument introduces a new boolean argument for cli context func BoolArgument(name string, value bool) CliArgument { return func(t *testing.T, flags *flag.FlagSet, c *cli.Context) { t.Helper() flags.Bool(name, value, "") require.NoError(t, c.Set(name, strconv.FormatBool(value))) } } // Int64Argument introduces a new int64 argument for cli context func Int64Argument(name string, value int64) CliArgument { return func(t *testing.T, flags *flag.FlagSet, c *cli.Context) { t.Helper() flags.Int64(name, value, "") require.NoError(t, c.Set(name, strconv.FormatInt(value, 10))) } } // StringSliceArgument introduces a new string slice argument for cli context func StringSliceArgument(name string, values ...string) CliArgument { return func(t *testing.T, flags *flag.FlagSet, c *cli.Context) { t.Helper() flags.Var(&cli.StringSlice{}, name, "") for _, v := range values { require.NoError(t, c.Set(name, v)) } } } // NewCLIContext creates a new cli context with optional arguments // this is a useful to make testing of commands compact func NewCLIContext(t *testing.T, app *cli.App, args ...CliArgument) *cli.Context { t.Helper() flags := flag.NewFlagSet("test", 0) cliCtx := cli.NewContext(app, flags, nil) for _, arg := range args { arg(t, flags, cliCtx) } return cliCtx } ================================================ FILE: tools/cli/clitest/context_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package clitest import ( "testing" "github.com/stretchr/testify/assert" "github.com/urfave/cli/v2" ) func TestNewCLIContext(t *testing.T) { app := cli.NewApp() ctx := NewCLIContext( t, app, StringArgument("region", "dca"), IntArgument("shards", 1024), BoolArgument("verbose", true), BoolArgument("exit-if-error", false), Int64Argument("bytes-per-minute", 999876543210), StringSliceArgument("tags", "tag1", "tag2"), ) assert.True(t, ctx.IsSet("region")) assert.Equal(t, "dca", ctx.String("region")) assert.True(t, ctx.IsSet("shards")) assert.Equal(t, 1024, ctx.Int("shards")) assert.True(t, ctx.IsSet("verbose")) assert.True(t, ctx.Bool("verbose")) assert.True(t, ctx.IsSet("exit-if-error")) assert.False(t, ctx.Bool("exit-if-error")) assert.True(t, ctx.IsSet("bytes-per-minute")) assert.Equal(t, int64(999876543210), ctx.Int64("bytes-per-minute")) assert.True(t, ctx.IsSet("tags")) assert.Equal(t, []string{"tag1", "tag2"}, ctx.StringSlice("tags")) assert.False(t, ctx.IsSet("should-not-exist")) } ================================================ FILE: tools/cli/clitest/runner.go ================================================ package clitest import ( "testing" "github.com/flynn/go-shlex" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" ) // RunCommandLine runs cmdline (a whole line like you'd type it in Shell) func RunCommandLine(t *testing.T, app *cli.App, cmdline string) error { t.Helper() args, err := shlex.Split(cmdline) require.NoError(t, err) return app.Run(args) } ================================================ FILE: tools/cli/clitest/runner_test.go ================================================ package clitest import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" ) func TestRunCommandLine(t *testing.T) { var output string testApp := &cli.App{ Name: "devmngr", Commands: []*cli.Command{ { Name: "upgrade", Flags: []cli.Flag{ &cli.StringFlag{Name: "device"}, &cli.BoolFlag{Name: "enabled"}, &cli.StringFlag{Name: "reason"}, }, Action: func(c *cli.Context) error { output = fmt.Sprintf( "upgrading device: \"%s\"; enabled: %v; reason: \"%s\"", c.String("device"), c.Bool("enabled"), c.String("reason"), ) return nil }, }, }, } err := RunCommandLine( t, testApp, "devmngr upgrade --device video-card --enabled --reason \"Doom: The Dark Ages is released\"", ) require.NoError(t, err) assert.Equal(t, "upgrading device: \"video-card\"; enabled: true; reason: \"Doom: The Dark Ages is released\"", output, ) } ================================================ FILE: tools/cli/cluster.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import "github.com/urfave/cli/v2" func newClusterCommands() []*cli.Command { return []*cli.Command{ { Name: "get-search-attr", Usage: "get list of legal search attributes that can be used in list workflow query.", Action: GetSearchAttributes, }, } } ================================================ FILE: tools/cli/cluster_commands.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "os" "sort" "github.com/urfave/cli/v2" "github.com/uber/cadence/tools/common/commoncli" ) type ( SearchAttributesRow struct { Key string `header:"Key"` ValueType string `header:"Value type"` } SearchAttributesTable []SearchAttributesRow ) func (s SearchAttributesTable) Len() int { return len(s) } func (s SearchAttributesTable) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s SearchAttributesTable) Less(i, j int) bool { return s[i].Key < s[j].Key } // GetSearchAttributes get valid search attributes func GetSearchAttributes(c *cli.Context) error { wfClient, err := getWorkflowClient(c) if err != nil { return err } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context:", err) } resp, err := wfClient.GetSearchAttributes(ctx) if err != nil { return commoncli.Problem("Failed to get search attributes.", err) } table := SearchAttributesTable{} for k, v := range resp.Keys { table = append(table, SearchAttributesRow{Key: k, ValueType: v.String()}) } sort.Sort(table) return RenderTable(os.Stdout, table, RenderOptions{Color: true, Border: true}) } ================================================ FILE: tools/cli/database.go ================================================ // Copyright (c) 2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -destination mock_manager_factory.go -self_package github.com/uber/cadence/tools/cli github.com/uber/cadence/tools/cli ManagerFactory package cli import ( "fmt" "net" "github.com/urfave/cli/v2" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/client" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra" "github.com/uber/cadence/common/persistence/sql" "github.com/uber/cadence/common/reconciliation/invariant" "github.com/uber/cadence/tools/common/flag" ) var supportedDBs = append(sql.GetRegisteredPluginNames(), "cassandra") func getDBFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Name: FlagServiceConfigDir, Aliases: []string{"scd"}, Value: "config", Usage: "service configuration dir", EnvVars: []string{config.EnvKeyConfigDir}, }, &cli.StringFlag{ Name: FlagServiceEnv, Aliases: []string{"se"}, Usage: "service env for loading service configuration", EnvVars: []string{config.EnvKeyEnvironment}, }, &cli.StringFlag{ Name: FlagServiceZone, Aliases: []string{"sz"}, Usage: "service zone for loading service configuration", EnvVars: []string{config.EnvKeyAvailabilityZone}, }, &cli.StringFlag{ Name: FlagDBType, Value: "cassandra", Usage: fmt.Sprintf("persistence type. Current supported options are %v", supportedDBs), }, &cli.StringFlag{ Name: FlagDBAddress, Value: "127.0.0.1", Usage: "persistence address (right now only cassandra is fully supported)", }, &cli.IntFlag{ Name: FlagDBPort, Value: 9042, Usage: "persistence port", }, &cli.StringFlag{ Name: FlagDBRegion, Usage: "persistence region", }, &cli.IntFlag{ Name: FlagDBShard, Usage: "number of db shards in a sharded SQL database", }, &cli.StringFlag{ Name: FlagUsername, Usage: "persistence username", }, &cli.StringFlag{ Name: FlagPassword, Usage: "persistence password", }, &cli.StringFlag{ Name: FlagKeyspace, Value: "cadence", Usage: "cassandra keyspace", }, &cli.StringFlag{ Name: FlagDatabaseName, Value: "cadence", Usage: "sql database name", }, &cli.StringFlag{ Name: FlagEncodingType, Value: "thriftrw", Usage: "sql database encoding type", }, &cli.StringSliceFlag{ Name: FlagDecodingTypes, Value: cli.NewStringSlice("thriftrw"), Usage: "sql database decoding types", }, &cli.IntFlag{ Name: FlagProtoVersion, Value: 4, Usage: "cassandra protocol version", }, &cli.BoolFlag{ Name: FlagEnableTLS, Usage: "enable TLS over cassandra connection", }, &cli.StringFlag{ Name: FlagTLSCertPath, Usage: "cassandra tls client cert path (tls must be enabled)", }, &cli.StringFlag{ Name: FlagTLSKeyPath, Usage: "cassandra tls client key path (tls must be enabled)", }, &cli.StringFlag{ Name: FlagTLSCaPath, Usage: "cassandra tls client ca path (tls must be enabled)", }, &cli.BoolFlag{ Name: FlagTLSEnableHostVerification, Usage: "cassandra tls verify hostname and server cert (tls must be enabled)", }, &cli.GenericFlag{ Name: FlagConnectionAttributes, Usage: "a key-value set of sql database connection attributes (must be in key1=value1,key2=value2,...,keyN=valueN format, e.g. cluster=dca or cluster=dca,instance=cadence)", Value: &flag.StringMap{}, }, &cli.IntFlag{ Name: FlagRPS, Usage: "target rps of database queries", Value: 100, }, } } type ManagerFactory interface { initializeExecutionManager(c *cli.Context, shardID int) (persistence.ExecutionManager, error) initializeHistoryManager(c *cli.Context) (persistence.HistoryManager, error) initializeShardManager(c *cli.Context) (persistence.ShardManager, error) initializeDomainManager(c *cli.Context) (persistence.DomainManager, error) initPersistenceFactory(c *cli.Context) (client.Factory, error) initializeInvariantManager(ivs []invariant.Invariant) (invariant.Manager, error) } type defaultManagerFactory struct { persistenceFactory client.Factory } func (f *defaultManagerFactory) initializeExecutionManager(c *cli.Context, shardID int) (persistence.ExecutionManager, error) { factory, err := f.getPersistenceFactory(c) if err != nil { return nil, fmt.Errorf("Failed to get persistence factory: %w", err) } executionManager, err := factory.NewExecutionManager(shardID) if err != nil { return nil, fmt.Errorf("Failed to initialize history manager %w", err) } return executionManager, nil } func (f *defaultManagerFactory) initializeHistoryManager(c *cli.Context) (persistence.HistoryManager, error) { factory, err := f.getPersistenceFactory(c) if err != nil { return nil, fmt.Errorf("Failed to get persistence factory: %w", err) } historyManager, err := factory.NewHistoryManager() if err != nil { return nil, fmt.Errorf("Failed to initialize history manager %w", err) } return historyManager, nil } func (f *defaultManagerFactory) initializeShardManager(c *cli.Context) (persistence.ShardManager, error) { factory, err := f.getPersistenceFactory(c) if err != nil { return nil, fmt.Errorf("Failed to get persistence factory: %w", err) } shardManager, err := factory.NewShardManager() if err != nil { return nil, fmt.Errorf("Failed to initialize shard manager %w", err) } return shardManager, nil } func (f *defaultManagerFactory) initializeDomainManager(c *cli.Context) (persistence.DomainManager, error) { factory, err := f.getPersistenceFactory(c) if err != nil { return nil, fmt.Errorf("Failed to get persistence factory: %w", err) } domainManager, err := factory.NewDomainManager() if err != nil { return nil, fmt.Errorf("Failed to initialize domain manager: %w", err) } return domainManager, nil } func (f *defaultManagerFactory) getPersistenceFactory(c *cli.Context) (client.Factory, error) { var err error if f.persistenceFactory == nil { f.persistenceFactory, err = getDeps(c).initPersistenceFactory(c) if err != nil { return f.persistenceFactory, fmt.Errorf("%w", err) } } return f.persistenceFactory, nil } func (f *defaultManagerFactory) initPersistenceFactory(c *cli.Context) (client.Factory, error) { cfg, err := getDeps(c).ServerConfig(c) if err != nil { cfg = &config.Config{ Persistence: config.Persistence{ DefaultStore: "default", DataStores: map[string]config.DataStore{ "default": {NoSQL: &config.NoSQL{PluginName: cassandra.PluginName}}, }, }, ClusterGroupMetadata: &config.ClusterGroupMetadata{ CurrentClusterName: "current-cluster", }, } } // If there are any overrides provided via CLI flags, apply them here defaultStore := cfg.Persistence.DataStores[cfg.Persistence.DefaultStore] defaultStore, err = overrideDataStore(c, defaultStore) if err != nil { return nil, fmt.Errorf("Error in init persistence factory: %w", err) } cfg.Persistence.DataStores[cfg.Persistence.DefaultStore] = defaultStore cfg.Persistence.TransactionSizeLimit = dynamicproperties.GetIntPropertyFn(constants.DefaultTransactionSizeLimit) cfg.Persistence.ErrorInjectionRate = dynamicproperties.GetFloatPropertyFn(0.0) rps := c.Float64(FlagRPS) return client.NewFactory( &cfg.Persistence, func() float64 { return rps }, cfg.ClusterGroupMetadata.CurrentClusterName, metrics.NewNoopMetricsClient(), log.NewNoop(), &persistence.DynamicConfiguration{ EnableSQLAsyncTransaction: dynamicproperties.GetBoolPropertyFn(false), }, ), nil } func (f *defaultManagerFactory) initializeInvariantManager(ivs []invariant.Invariant) (invariant.Manager, error) { return invariant.NewInvariantManager(ivs), nil } func overrideDataStore(c *cli.Context, ds config.DataStore) (config.DataStore, error) { if c.IsSet(FlagDBType) { // overriding DBType will wipe out all settings, everything will be set from flags only var err error ds, err = createDataStore(c) if err != nil { return config.DataStore{}, fmt.Errorf("Error in overriding data store: %w", err) } } if ds.NoSQL != nil { overrideNoSQLDataStore(c, ds.NoSQL) } if ds.SQL != nil { overrideSQLDataStore(c, ds.SQL) } return ds, nil } func createDataStore(c *cli.Context) (config.DataStore, error) { dbType := c.String(FlagDBType) switch dbType { case cassandra.PluginName: return config.DataStore{NoSQL: &config.NoSQL{PluginName: cassandra.PluginName}}, nil default: if sql.PluginRegistered(dbType) { return config.DataStore{SQL: &config.SQL{PluginName: dbType}}, nil } } fmt.Errorf("The DB type is not supported. Options are: %s. Error %v", supportedDBs, nil) return config.DataStore{}, nil } func overrideNoSQLDataStore(c *cli.Context, cfg *config.NoSQL) { if c.IsSet(FlagDBAddress) || cfg.Hosts == "" { cfg.Hosts = c.String(FlagDBAddress) } if c.IsSet(FlagDBPort) || cfg.Port == 0 { cfg.Port = c.Int(FlagDBPort) } if c.IsSet(FlagDBRegion) || cfg.Region == "" { cfg.Region = c.String(FlagDBRegion) } if c.IsSet(FlagUsername) || cfg.User == "" { cfg.User = c.String(FlagUsername) } if c.IsSet(FlagPassword) || cfg.Password == "" { cfg.Password = c.String(FlagPassword) } if c.IsSet(FlagKeyspace) || cfg.Keyspace == "" { cfg.Keyspace = c.String(FlagKeyspace) } if c.IsSet(FlagProtoVersion) || cfg.ProtoVersion == 0 { cfg.ProtoVersion = c.Int(FlagProtoVersion) } if cfg.TLS == nil { cfg.TLS = &config.TLS{} } overrideTLS(c, cfg.TLS) } func overrideSQLDataStore(c *cli.Context, cfg *config.SQL) { host, port, _ := net.SplitHostPort(cfg.ConnectAddr) if c.IsSet(FlagDBAddress) || cfg.ConnectAddr == "" { host = c.String(FlagDBAddress) } if c.IsSet(FlagDBPort) || cfg.ConnectAddr == "" { port = c.String(FlagDBPort) } cfg.ConnectAddr = net.JoinHostPort(host, port) if c.IsSet(FlagDBType) || cfg.PluginName == "" { cfg.PluginName = c.String(FlagDBType) } if c.IsSet(FlagDBShard) || cfg.NumShards == 0 { cfg.NumShards = c.Int(FlagDBShard) } if c.IsSet(FlagUsername) || cfg.User == "" { cfg.User = c.String(FlagUsername) } if c.IsSet(FlagPassword) || cfg.Password == "" { cfg.Password = c.String(FlagPassword) } if c.IsSet(FlagDatabaseName) || cfg.DatabaseName == "" { cfg.DatabaseName = c.String(FlagDatabaseName) } if c.IsSet(FlagEncodingType) || cfg.EncodingType == "" { cfg.EncodingType = c.String(FlagEncodingType) } if c.IsSet(FlagDecodingTypes) || len(cfg.DecodingTypes) == 0 { cfg.DecodingTypes = c.StringSlice(FlagDecodingTypes) } if c.IsSet(FlagConnectionAttributes) || cfg.ConnectAttributes == nil { cfg.ConnectAttributes = c.Generic(FlagConnectionAttributes).(*flag.StringMap).Value() } if cfg.TLS == nil { cfg.TLS = &config.TLS{} } overrideTLS(c, cfg.TLS) } func overrideTLS(c *cli.Context, tls *config.TLS) { tls.Enabled = c.Bool(FlagEnableTLS) if c.IsSet(FlagTLSCertPath) { tls.CertFile = c.String(FlagTLSCertPath) } if c.IsSet(FlagTLSKeyPath) { tls.KeyFile = c.String(FlagTLSKeyPath) } if c.IsSet(FlagTLSCaPath) { tls.CaFile = c.String(FlagTLSCaPath) } if c.IsSet(FlagTLSEnableHostVerification) { tls.EnableHostVerification = c.Bool(FlagTLSEnableHostVerification) } } ================================================ FILE: tools/cli/database_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "flag" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/persistence/client" "github.com/uber/cadence/common/persistence/nosql/nosqlplugin/cassandra" "github.com/uber/cadence/common/persistence/sql" "github.com/uber/cadence/common/persistence/sql/sqlplugin" "github.com/uber/cadence/common/reconciliation/invariant" commonFlag "github.com/uber/cadence/tools/common/flag" ) func TestDefaultManagerFactory(t *testing.T) { tests := []struct { name string setupMocks func(*client.MockFactory, *gomock.Controller) interface{} methodToTest func(*defaultManagerFactory, *cli.Context) (interface{}, error) expectError bool }{ { name: "initializeExecutionManager - success", setupMocks: func(mockFactory *client.MockFactory, ctrl *gomock.Controller) interface{} { mockExecutionManager := persistence.NewMockExecutionManager(ctrl) mockFactory.EXPECT().NewExecutionManager(gomock.Any()).Return(mockExecutionManager, nil) return mockExecutionManager }, methodToTest: func(f *defaultManagerFactory, ctx *cli.Context) (interface{}, error) { return f.initializeExecutionManager(ctx, 1) }, expectError: false, }, { name: "initializeExecutionManager - error", setupMocks: func(mockFactory *client.MockFactory, ctrl *gomock.Controller) interface{} { mockFactory.EXPECT().NewExecutionManager(gomock.Any()).Return(nil, fmt.Errorf("some error")) return nil }, methodToTest: func(f *defaultManagerFactory, ctx *cli.Context) (interface{}, error) { return f.initializeExecutionManager(ctx, 1) }, expectError: true, }, { name: "initializeHistoryManager - success", setupMocks: func(mockFactory *client.MockFactory, ctrl *gomock.Controller) interface{} { mockHistoryManager := persistence.NewMockHistoryManager(ctrl) mockFactory.EXPECT().NewHistoryManager().Return(mockHistoryManager, nil) return mockHistoryManager }, methodToTest: func(f *defaultManagerFactory, ctx *cli.Context) (interface{}, error) { return f.initializeHistoryManager(ctx) }, expectError: false, }, { name: "initializeHistoryManager - error", setupMocks: func(mockFactory *client.MockFactory, ctrl *gomock.Controller) interface{} { mockFactory.EXPECT().NewHistoryManager().Return(nil, fmt.Errorf("some error")) return nil }, methodToTest: func(f *defaultManagerFactory, ctx *cli.Context) (interface{}, error) { return f.initializeHistoryManager(ctx) }, expectError: true, }, { name: "initializeShardManager - success", setupMocks: func(mockFactory *client.MockFactory, ctrl *gomock.Controller) interface{} { mockShardManager := persistence.NewMockShardManager(ctrl) mockFactory.EXPECT().NewShardManager().Return(mockShardManager, nil) return mockShardManager }, methodToTest: func(f *defaultManagerFactory, ctx *cli.Context) (interface{}, error) { return f.initializeShardManager(ctx) }, expectError: false, }, { name: "initializeShardManager - error", setupMocks: func(mockFactory *client.MockFactory, ctrl *gomock.Controller) interface{} { mockFactory.EXPECT().NewShardManager().Return(nil, fmt.Errorf("some error")) return nil }, methodToTest: func(f *defaultManagerFactory, ctx *cli.Context) (interface{}, error) { return f.initializeShardManager(ctx) }, expectError: true, }, { name: "initializeDomainManager - success", setupMocks: func(mockFactory *client.MockFactory, ctrl *gomock.Controller) interface{} { mockDomainManager := persistence.NewMockDomainManager(ctrl) mockFactory.EXPECT().NewDomainManager().Return(mockDomainManager, nil) return mockDomainManager }, methodToTest: func(f *defaultManagerFactory, ctx *cli.Context) (interface{}, error) { return f.initializeDomainManager(ctx) }, expectError: false, }, { name: "initializeDomainManager - error", setupMocks: func(mockFactory *client.MockFactory, ctrl *gomock.Controller) interface{} { mockFactory.EXPECT().NewDomainManager().Return(nil, fmt.Errorf("some error")) return nil }, methodToTest: func(f *defaultManagerFactory, ctx *cli.Context) (interface{}, error) { return f.initializeDomainManager(ctx) }, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockFactory := client.NewMockFactory(ctrl) expectedManager := tt.setupMocks(mockFactory, ctrl) f := &defaultManagerFactory{persistenceFactory: mockFactory} app := &cli.App{} ctx := cli.NewContext(app, nil, nil) result, err := tt.methodToTest(f, ctx) if tt.expectError { assert.Error(t, err) assert.Nil(t, result) } else { assert.NoError(t, err) assert.Equal(t, expectedManager, result) } }) } } func TestInitPersistenceFactory(t *testing.T) { ctrl := gomock.NewController(t) // Mock the ManagerFactory and ClientFactory mockClientFactory := NewMockClientFactory(ctrl) mockPersistenceFactory := client.NewMockFactory(ctrl) // Set up the context and app set := flag.NewFlagSet("test", 0) app := NewCliApp(mockClientFactory) c := cli.NewContext(app, set, nil) // Mock ServerConfig to return an error mockClientFactory.EXPECT().ServerConfig(gomock.Any()).Return(nil, fmt.Errorf("config error")).Times(1) // Initialize the ManagerFactory with the mock ClientFactory managerFactory := defaultManagerFactory{ persistenceFactory: mockPersistenceFactory, } // Call initPersistenceFactory and validate results factory, err := managerFactory.initPersistenceFactory(c) // Assert that no error occurred and a default config was used assert.NoError(t, err) assert.NotNil(t, factory) } func TestInitializeInvariantManager(t *testing.T) { // Create an instance of defaultManagerFactory factory := &defaultManagerFactory{} // Define some fake invariants for testing invariants := []invariant.Invariant{} // Call initializeInvariantManager manager, err := factory.initializeInvariantManager(invariants) // Check that no error is returned require.NoError(t, err, "Expected no error from initializeInvariantManager") // Check that the returned Manager is not nil require.NotNil(t, manager, "Expected non-nil invariant.Manager") } func TestOverrideDataStore(t *testing.T) { tests := []struct { name string setupContext func(app *cli.App) *cli.Context inputDataStore config.DataStore expectedError string expectedSQL *config.SQL }{ { name: "OverrideDBType_Cassandra", setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) set.String(FlagDBType, cassandra.PluginName, "DB type flag") require.NoError(t, set.Set(FlagDBType, cassandra.PluginName)) // Set DBType to Cassandra return cli.NewContext(app, set, nil) }, inputDataStore: config.DataStore{}, // Empty DataStore to trigger createDataStore expectedError: "", expectedSQL: nil, // No SQL expected for Cassandra }, { name: "OverrideSQLDataStore", setupContext: func(app *cli.App) *cli.Context { // Create a new mock SQL plugin using gomock ctrl := gomock.NewController(t) mockSQLPlugin := sqlplugin.NewMockPlugin(ctrl) // Register the mock SQL plugin for "mysql" sql.RegisterPlugin("mysql", mockSQLPlugin) set := flag.NewFlagSet("test", 0) set.String(FlagDBType, "mysql", "DB type flag") // Set SQL database type set.String(FlagDBAddress, "127.0.0.1", "DB address flag") set.String(FlagDBPort, "3306", "DB port flag") set.String(FlagUsername, "testuser", "DB username flag") set.String(FlagPassword, "testpass", "DB password flag") connAttr := &commonFlag.StringMap{} require.NoError(t, connAttr.Set("attr1=value1")) require.NoError(t, connAttr.Set("attr2=value2")) set.Var(connAttr, FlagConnectionAttributes, "Connection attributes flag") require.NoError(t, set.Set(FlagDBType, "mysql")) require.NoError(t, set.Set(FlagDBAddress, "127.0.0.1")) require.NoError(t, set.Set(FlagDBPort, "3306")) require.NoError(t, set.Set(FlagUsername, "testuser")) require.NoError(t, set.Set(FlagPassword, "testpass")) return cli.NewContext(app, set, nil) }, expectedError: "", expectedSQL: &config.SQL{ PluginName: "mysql", ConnectAddr: "127.0.0.1:3306", User: "testuser", Password: "testpass", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Set up app and context app := cli.NewApp() c := tt.setupContext(app) // Call overrideDataStore with initial DataStore and capture result result, err := overrideDataStore(c, tt.inputDataStore) if tt.expectedError != "" { assert.ErrorContains(t, err, tt.expectedError) } else { assert.NoError(t, err) // Validate SQL DataStore settings if expected if tt.expectedSQL != nil && result.SQL != nil { assert.Equal(t, tt.expectedSQL.PluginName, result.SQL.PluginName) assert.Equal(t, tt.expectedSQL.ConnectAddr, result.SQL.ConnectAddr) assert.Equal(t, tt.expectedSQL.User, result.SQL.User) assert.Equal(t, tt.expectedSQL.Password, result.SQL.Password) } } }) } } func TestOverrideTLS(t *testing.T) { tests := []struct { name string setupContext func(app *cli.App) *cli.Context expectedTLS config.TLS }{ { name: "AllTLSFlagsSet", setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) set.Bool(FlagEnableTLS, true, "Enable TLS flag") set.String(FlagTLSCertPath, "/path/to/cert", "TLS Cert Path") set.String(FlagTLSKeyPath, "/path/to/key", "TLS Key Path") set.String(FlagTLSCaPath, "/path/to/ca", "TLS CA Path") set.Bool(FlagTLSEnableHostVerification, true, "Enable Host Verification") require.NoError(t, set.Set(FlagEnableTLS, "true")) require.NoError(t, set.Set(FlagTLSCertPath, "/path/to/cert")) require.NoError(t, set.Set(FlagTLSKeyPath, "/path/to/key")) require.NoError(t, set.Set(FlagTLSCaPath, "/path/to/ca")) require.NoError(t, set.Set(FlagTLSEnableHostVerification, "true")) return cli.NewContext(app, set, nil) }, expectedTLS: config.TLS{ Enabled: true, CertFile: "/path/to/cert", KeyFile: "/path/to/key", CaFile: "/path/to/ca", EnableHostVerification: true, }, }, { name: "PartialTLSFlagsSet", setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) set.Bool(FlagEnableTLS, true, "Enable TLS flag") set.String(FlagTLSCertPath, "/path/to/cert", "TLS Cert Path") require.NoError(t, set.Set(FlagEnableTLS, "true")) require.NoError(t, set.Set(FlagTLSCertPath, "/path/to/cert")) return cli.NewContext(app, set, nil) }, expectedTLS: config.TLS{ Enabled: true, CertFile: "/path/to/cert", }, }, { name: "NoTLSFlagsSet", setupContext: func(app *cli.App) *cli.Context { set := flag.NewFlagSet("test", 0) return cli.NewContext(app, set, nil) }, expectedTLS: config.TLS{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Set up app and context app := cli.NewApp() c := tt.setupContext(app) // Initialize an empty TLS config and apply overrideTLS tlsConfig := &config.TLS{} overrideTLS(c, tlsConfig) // Validate TLS config settings assert.Equal(t, tt.expectedTLS.Enabled, tlsConfig.Enabled) assert.Equal(t, tt.expectedTLS.CertFile, tlsConfig.CertFile) assert.Equal(t, tt.expectedTLS.KeyFile, tlsConfig.KeyFile) assert.Equal(t, tt.expectedTLS.CaFile, tlsConfig.CaFile) assert.Equal(t, tt.expectedTLS.EnableHostVerification, tlsConfig.EnableHostVerification) }) } } func newClientFactoryMock() *clientFactoryMock { return &clientFactoryMock{ serverFrontendClient: frontend.NewMockClient(gomock.NewController(nil)), serverAdminClient: admin.NewMockClient(gomock.NewController(nil)), config: &config.Config{ Persistence: config.Persistence{ DefaultStore: "default", DataStores: map[string]config.DataStore{ "default": {NoSQL: &config.NoSQL{PluginName: cassandra.PluginName}}, }, }, ClusterGroupMetadata: &config.ClusterGroupMetadata{ CurrentClusterName: "current-cluster", }, }, } } ================================================ FILE: tools/cli/defs.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "os" "time" "github.com/fatih/color" "github.com/uber/cadence/common/types" ) const ( localHost = "127.0.0.1" tchannelPort = localHost + ":7933" grpcPort = localHost + ":7833" grpcTransport = "grpc" thriftTransport = "tchannel" maxOutputStringLength = 200 // max length for output string maxWorkflowTypeLength = 32 // max item length for output workflow type in table defaultMaxFieldLength = 500 // default max length for each attribute field maxWordLength = 120 // if text length is larger than maxWordLength, it will be inserted spaces // regex expression for parsing time durations, shorter, longer notations and numeric value respectively defaultDateTimeRangeShortRE = "^[1-9][0-9]*[smhdwMy]$" // eg. 1s, 20m, 300h etc. defaultDateTimeRangeLongRE = "^[1-9][0-9]*(second|minute|hour|day|week|month|year)$" // eg. 1second, 20minute, 300hour etc. defaultDateTimeRangeNum = "^[1-9][0-9]*" // eg. 1, 20, 300 etc. // time ranges day = 24 * time.Hour week = 7 * day month = 30 * day year = 365 * day defaultTimeFormat = "15:04:05" // used for converting UnixNano to string like 16:16:36 (only time) defaultDateTimeFormat = time.RFC3339 // used for converting UnixNano to string like 2018-02-15T16:16:36-08:00 defaultDomainRetentionDays = 3 defaultContextTimeoutInSeconds = 5 defaultContextTimeout = defaultContextTimeoutInSeconds * time.Second defaultContextTimeoutForLongPoll = 2 * time.Minute defaultContextTimeoutForListArchivedWorkflow = 3 * time.Minute defaultDecisionTimeoutInSeconds = 10 defaultPageSizeForList = 500 defaultPageSizeForScan = 2000 defaultWorkflowIDReusePolicy = types.WorkflowIDReusePolicyAllowDuplicateFailedOnly workflowStatusNotSet = -1 showErrorStackEnv = `CADENCE_CLI_SHOW_STACKS` searchAttrInputSeparator = "|" defaultGracefulFailoverTimeoutInSeconds = 60 ) var envKeysForUserName = []string{ "USER", "LOGNAME", "HOME", } const resetTypeFirstDecisionCompleted = "FirstDecisionCompleted" const resetTypeLastDecisionCompleted = "LastDecisionCompleted" const resetTypeLastContinuedAsNew = "LastContinuedAsNew" const resetTypeBadBinary = "BadBinary" const resetTypeDecisionCompletedTime = "DecisionCompletedTime" const resetTypeFirstDecisionScheduled = "FirstDecisionScheduled" const resetTypeLastDecisionScheduled = "LastDecisionScheduled" var resetTypesMap = map[string]string{ resetTypeFirstDecisionCompleted: "", resetTypeLastDecisionCompleted: "", resetTypeLastContinuedAsNew: "", resetTypeBadBinary: FlagResetBadBinaryChecksum, resetTypeDecisionCompletedTime: FlagEarliestTime, resetTypeFirstDecisionScheduled: "", resetTypeLastDecisionScheduled: "", } type jsonType int const ( jsonTypeInput jsonType = iota jsonTypeMemo jsonTypeHeader jsonTypeSignal ) var ( colorRed = color.New(color.FgRed).SprintFunc() colorMagenta = color.New(color.FgMagenta).SprintFunc() colorGreen = color.New(color.FgGreen).SprintFunc() optionErr = "there is something wrong with your command options" osExit = os.Exit workflowClosedStatusMap = map[string]types.WorkflowExecutionCloseStatus{ "completed": types.WorkflowExecutionCloseStatusCompleted, "failed": types.WorkflowExecutionCloseStatusFailed, "canceled": types.WorkflowExecutionCloseStatusCanceled, "terminated": types.WorkflowExecutionCloseStatusTerminated, "continued_as_new": types.WorkflowExecutionCloseStatusContinuedAsNew, "timed_out": types.WorkflowExecutionCloseStatusTimedOut, // below are some alias "c": types.WorkflowExecutionCloseStatusCompleted, "complete": types.WorkflowExecutionCloseStatusCompleted, "f": types.WorkflowExecutionCloseStatusFailed, "fail": types.WorkflowExecutionCloseStatusFailed, "cancel": types.WorkflowExecutionCloseStatusCanceled, "terminate": types.WorkflowExecutionCloseStatusTerminated, "term": types.WorkflowExecutionCloseStatusTerminated, "continue": types.WorkflowExecutionCloseStatusContinuedAsNew, "cont": types.WorkflowExecutionCloseStatusContinuedAsNew, "continuedasnew": types.WorkflowExecutionCloseStatusContinuedAsNew, "continueasnew": types.WorkflowExecutionCloseStatusContinuedAsNew, "timedout": types.WorkflowExecutionCloseStatusTimedOut, "timeout": types.WorkflowExecutionCloseStatusTimedOut, } ) ================================================ FILE: tools/cli/domain.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "fmt" "strings" "github.com/urfave/cli/v2" "github.com/uber/cadence/tools/common/commoncli" ) // by default we don't require any domain data. But this can be overridden by calling SetRequiredDomainDataKeys() var requiredDomainDataKeys = []string{} // SetRequiredDomainDataKeys will set requiredDomainDataKeys func SetRequiredDomainDataKeys(keys []string) { requiredDomainDataKeys = keys } func checkRequiredDomainDataKVs(domainData map[string]string) error { // check requiredDomainDataKeys for _, k := range requiredDomainDataKeys { _, ok := domainData[k] if !ok { return fmt.Errorf("domain data error, missing required key %v . All required keys: %v", k, requiredDomainDataKeys) } } return nil } func checkNoAdditionalArgsPassed(c *cli.Context) error { if c.NArg() > 0 { return fmt.Errorf("Domain commands cannot have arguments: <%v>\nClusters are now specified as --clusters c1,c2 see help for more info", strings.Join(c.Args().Slice(), " ")) } return nil } func newDomainCommands() []*cli.Command { return []*cli.Command{ { Name: "register", Aliases: []string{"re"}, Usage: "Register workflow domain", Flags: registerDomainFlags, Action: func(c *cli.Context) error { err := checkNoAdditionalArgsPassed(c) if err != nil { return err } return withDomainClient(c, false, func(dc *domainCLIImpl) error { return dc.RegisterDomain(c) }) }, }, { Name: "update", Aliases: []string{"up", "u"}, Usage: "Update existing workflow domain", Flags: updateDomainFlags, Action: func(c *cli.Context) error { err := checkNoAdditionalArgsPassed(c) if err != nil { return err } return withDomainClient(c, false, func(dc *domainCLIImpl) error { return dc.UpdateDomain(c) }) }, }, { Name: "delete", Aliases: []string{"del"}, Usage: "Delete existing workflow domain", Flags: deleteDomainFlags, Action: func(c *cli.Context) error { err := checkNoAdditionalArgsPassed(c) if err != nil { return err } return withDomainClient(c, false, func(dc *domainCLIImpl) error { return dc.DeleteDomain(c) }) }, }, { Name: "deprecate", Aliases: []string{"dep"}, Usage: "Deprecate existing workflow domain", Flags: deprecateDomainFlags, Action: func(c *cli.Context) error { err := checkNoAdditionalArgsPassed(c) if err != nil { return err } return withDomainClient(c, false, func(dc *domainCLIImpl) error { return dc.DeprecateDomain(c) }) }, }, { Name: "describe", Aliases: []string{"desc"}, Usage: "Describe existing workflow domain", Flags: describeDomainFlags, Action: func(c *cli.Context) error { err := checkNoAdditionalArgsPassed(c) if err != nil { return err } return withDomainClient(c, false, func(dc *domainCLIImpl) error { return dc.DescribeDomain(c) }) }, }, { Name: "migration", Aliases: []string{"mi"}, Usage: "Migrate existing domain to new domain. This command only validates the settings. It does not perform actual data migration", Flags: migrateDomainFlags, Action: func(c *cli.Context) error { err := checkNoAdditionalArgsPassed(c) if err != nil { return err } // exit on error already handled in the command // TODO best practice is to return error if validation fails err = NewDomainMigrationCommand(c).Validation(c) if err != nil { return commoncli.Problem("Failed validation: ", err) } return nil }, }, { Name: "failover", Aliases: []string{"fo"}, Usage: "Failover workflow domain to target active cluster", Flags: failoverDomainFlags, Action: func(c *cli.Context) error { err := checkNoAdditionalArgsPassed(c) if err != nil { return err } return withDomainClient(c, false, func(dc *domainCLIImpl) error { return dc.FailoverDomain(c) }) }, }, { Name: "list-failover-history", Aliases: []string{"lfh"}, Usage: "List failover history for a domain", Flags: listFailoverHistoryFlags, Action: func(c *cli.Context) error { err := checkNoAdditionalArgsPassed(c) if err != nil { return err } return withDomainClient(c, false, func(dc *domainCLIImpl) error { return dc.ListFailoverHistory(c) }) }, }, } } ================================================ FILE: tools/cli/domain_commands.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "bufio" "context" "encoding/json" "errors" "fmt" "os" "regexp" "strconv" "strings" "time" "github.com/olekukonko/tablewriter" "github.com/pborman/uuid" "github.com/urfave/cli/v2" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common" "github.com/uber/cadence/common/constants" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" "github.com/uber/cadence/service/worker/domaindeprecation" "github.com/uber/cadence/tools/common/commoncli" "github.com/uber/cadence/tools/common/flag" ) const ( decisionTimeoutInSeconds = 5 * 60 workflowStartToCloseTimeout = 24 * 30 * 60 * 60 // 30 days ) var ( gracefulFailoverType = "grace" ) type ( domainCLIImpl struct { // used when making RPC call to frontend service frontendClient frontend.Client frontendAdminClient admin.Client // act as admin to modify domain in DB directly domainHandler domain.Handler } ) // newDomainCLI creates a domain CLI func newDomainCLI( c *cli.Context, isAdminMode bool, ) (*domainCLIImpl, error) { d := &domainCLIImpl{} var err error d.frontendClient, err = initializeFrontendClient(c) if err != nil { return nil, err } if isAdminMode { d.frontendAdminClient, err = initializeFrontendAdminClient(c) if err != nil { return nil, err } d.domainHandler, err = initializeAdminDomainHandler(c) if err != nil { return nil, err } } return d, nil } // RegisterDomain register a domain func (d *domainCLIImpl) RegisterDomain(c *cli.Context) error { domainName, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found: ", err) } description := c.String(FlagDescription) ownerEmail := c.String(FlagOwnerEmail) retentionDays := defaultDomainRetentionDays if c.IsSet(FlagRetentionDays) { retentionDays = c.Int(FlagRetentionDays) } securityToken := c.String(FlagSecurityToken) isGlobalDomain := true if c.IsSet(FlagIsGlobalDomain) { isGlobalDomain, err = strconv.ParseBool(c.String(FlagIsGlobalDomain)) if err != nil { return commoncli.Problem(fmt.Sprintf("Option %s format is invalid.", FlagIsGlobalDomain), err) } } var domainData *flag.StringMap if c.IsSet(FlagDomainData) { domainData = c.Generic(FlagDomainData).(*flag.StringMap) } if len(requiredDomainDataKeys) > 0 { err = checkRequiredDomainDataKVs(domainData.Value()) if err != nil { return commoncli.Problem("Domain data missed required data.", err) } } activeClusterName := "" if c.IsSet(FlagActiveClusterName) { activeClusterName = c.String(FlagActiveClusterName) } var clusters []*types.ClusterReplicationConfiguration if c.IsSet(FlagClusters) { for _, clusterStr := range c.StringSlice(FlagClusters) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: clusterStr, }) } } has, err := archivalStatus(c, FlagHistoryArchivalStatus) if err != nil { return fmt.Errorf("failed to parse %s flag: %w", FlagHistoryArchivalStatus, err) } vas, err := archivalStatus(c, FlagVisibilityArchivalStatus) if err != nil { return fmt.Errorf("failed to parse %s flag: %w", FlagVisibilityArchivalStatus, err) } var activeClusters *types.ActiveClusters if c.IsSet(FlagActiveClusters) { ac, err := parseActiveClustersByClusterAttribute(c.String(FlagActiveClusters)) if err != nil { return err } activeClusters = &ac } request := &types.RegisterDomainRequest{ Name: domainName, Description: description, OwnerEmail: ownerEmail, Data: domainData.Value(), WorkflowExecutionRetentionPeriodInDays: int32(retentionDays), Clusters: clusters, ActiveClusterName: activeClusterName, ActiveClusters: activeClusters, SecurityToken: securityToken, HistoryArchivalStatus: has, HistoryArchivalURI: c.String(FlagHistoryArchivalURI), VisibilityArchivalStatus: vas, VisibilityArchivalURI: c.String(FlagVisibilityArchivalURI), IsGlobalDomain: isGlobalDomain, } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context:", err) } err = d.registerDomain(ctx, request) if err != nil { if _, ok := err.(*types.DomainAlreadyExistsError); !ok { return commoncli.Problem("Register Domain operation failed.", err) } return commoncli.Problem(fmt.Sprintf("Domain %s already registered.", domainName), err) } fmt.Printf("Domain %s successfully registered.\n", domainName) return nil } // UpdateDomain updates a domain func (d *domainCLIImpl) UpdateDomain(c *cli.Context) error { domainName, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found: ", err) } var updateRequest *types.UpdateDomainRequest ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } if c.IsSet(FlagActiveClusterName) { // active-passive domain failover activeCluster := c.String(FlagActiveClusterName) fmt.Printf("Will set active cluster name to: %s, other flag will be omitted.\n", activeCluster) var failoverTimeout *int32 if c.String(FlagFailoverType) == gracefulFailoverType { timeout := int32(c.Int(FlagFailoverTimeout)) failoverTimeout = &timeout } updateRequest = &types.UpdateDomainRequest{ Name: domainName, ActiveClusterName: common.StringPtr(activeCluster), FailoverTimeoutInSeconds: failoverTimeout, } } else if c.IsSet(FlagActiveClusters) { // active-active domain failover activeClusters, err := parseActiveClustersByClusterAttribute(c.String(FlagActiveClusters)) if err != nil { return err } updateRequest = &types.UpdateDomainRequest{ Name: domainName, ActiveClusters: &types.ActiveClusters{ AttributeScopes: activeClusters.AttributeScopes, }, } } else { resp, err := d.describeDomain(ctx, &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) if err != nil { if _, ok := err.(*types.EntityNotExistsError); !ok { return commoncli.Problem("Operation UpdateDomain failed.", err) } return commoncli.Problem(fmt.Sprintf("Domain %s does not exist.", domainName), err) } description := resp.DomainInfo.GetDescription() ownerEmail := resp.DomainInfo.GetOwnerEmail() retentionDays := resp.Configuration.GetWorkflowExecutionRetentionPeriodInDays() emitMetric := resp.Configuration.GetEmitMetric() var clusters []*types.ClusterReplicationConfiguration if c.IsSet(FlagDescription) { description = c.String(FlagDescription) } if c.IsSet(FlagOwnerEmail) { ownerEmail = c.String(FlagOwnerEmail) } var domainData *flag.StringMap if c.IsSet(FlagDomainData) { domainData = c.Generic(FlagDomainData).(*flag.StringMap) } if c.IsSet(FlagRetentionDays) { retentionDays = int32(c.Int(FlagRetentionDays)) } if c.IsSet(FlagClusters) { for _, clusterStr := range c.StringSlice(FlagClusters) { clusters = append(clusters, &types.ClusterReplicationConfiguration{ ClusterName: clusterStr, }) } } var binBinaries *types.BadBinaries if c.IsSet(FlagAddBadBinary) { if !c.IsSet(FlagReason) { return commoncli.Problem("Must provide a reason.", nil) } binChecksum := c.String(FlagAddBadBinary) reason := c.String(FlagReason) operator := getCurrentUserFromEnv() binBinaries = &types.BadBinaries{ Binaries: map[string]*types.BadBinaryInfo{ binChecksum: { Reason: reason, Operator: operator, }, }, } } var badBinaryToDelete *string if c.IsSet(FlagRemoveBadBinary) { badBinaryToDelete = common.StringPtr(c.String(FlagRemoveBadBinary)) } has, err := archivalStatus(c, FlagHistoryArchivalStatus) if err != nil { return fmt.Errorf("failed to parse %s flag: %w", FlagHistoryArchivalStatus, err) } vas, err := archivalStatus(c, FlagVisibilityArchivalStatus) if err != nil { return fmt.Errorf("failed to parse %s flag: %w", FlagVisibilityArchivalStatus, err) } updateRequest = &types.UpdateDomainRequest{ Name: domainName, Description: common.StringPtr(description), OwnerEmail: common.StringPtr(ownerEmail), Data: domainData.Value(), WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(retentionDays), EmitMetric: common.BoolPtr(emitMetric), HistoryArchivalStatus: has, HistoryArchivalURI: common.StringPtr(c.String(FlagHistoryArchivalURI)), VisibilityArchivalStatus: vas, VisibilityArchivalURI: common.StringPtr(c.String(FlagVisibilityArchivalURI)), BadBinaries: binBinaries, Clusters: clusters, DeleteBadBinary: badBinaryToDelete, } } securityToken := c.String(FlagSecurityToken) updateRequest.SecurityToken = securityToken _, err = d.updateDomain(ctx, updateRequest) if err != nil { var entityNotExistsErr *types.EntityNotExistsError if errors.As(err, &entityNotExistsErr) { return commoncli.Problem(fmt.Sprintf("Domain %s does not exist.", domainName), err) } var accessDeniedErr *types.AccessDeniedError if errors.As(err, &accessDeniedErr) { fmt.Fprintf(os.Stderr, "WARNING: Update domain operation may not be available for general user use and is reserved for administrative operations.\n") fmt.Fprintf(os.Stderr, "Please use the 'cadence domain failover' cli-command instead for domain management.\n") return commoncli.Problem("Operation UpdateDomain failed due to authorization.", err) } return commoncli.Problem("Operation UpdateDomain failed.", err) } fmt.Printf("Domain %s successfully updated.\n", domainName) return nil } func (d *domainCLIImpl) DeleteDomain(c *cli.Context) error { domainName, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found: ", err) } securityToken := c.String(FlagSecurityToken) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } // First describe the domain to show its details describeRequest := &types.DescribeDomainRequest{ Name: &domainName, } resp, err := d.describeDomain(ctx, describeRequest) if err != nil { if _, ok := err.(*types.EntityNotExistsError); !ok { return commoncli.Problem("Operation DescribeDomain failed.", err) } return commoncli.Problem(fmt.Sprintf("Domain %s does not exist.", domainName), err) } if err := Render(c, newDomainRow(resp), RenderOptions{ DefaultTemplate: templateDomain, Color: true, Border: true, PrintDateTime: true, }); err != nil { return fmt.Errorf("failed to render domain details: %w", err) } fmt.Print("Do you want to delete this domain? \n") reader := bufio.NewReader(os.Stdin) for { fmt.Print("Please confirm[Yes/No]:") text, err := reader.ReadString('\n') if err != nil { return commoncli.Problem("Failed to get confirmation for domain deletion", err) } if strings.EqualFold(strings.TrimSpace(text), "yes") { break } else { fmt.Println("Domain deletion cancelled") return nil } } err = d.deleteDomain(ctx, &types.DeleteDomainRequest{ Name: domainName, SecurityToken: securityToken, }) if err != nil { return commoncli.Problem("Operation Delete domain failed.", err) } fmt.Printf("Domain %s successfully deleted.\n", domainName) return nil } func (d *domainCLIImpl) DeprecateDomain(c *cli.Context) error { domainName, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not provided: ", err) } securityToken := c.String(FlagSecurityToken) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } frontendClient, err := getDeps(c).ServerFrontendClient(c) if err != nil { return err } params := domaindeprecation.DomainDeprecationParams{ DomainName: domainName, SecurityToken: securityToken, } input, err := json.Marshal(params) if err != nil { return commoncli.Problem("Failed to encode domain deprecation parameters", err) } workflowID := uuid.NewRandom().String() startRequest := &types.StartWorkflowExecutionRequest{ Domain: constants.SystemLocalDomainName, WorkflowID: fmt.Sprintf("domain-deprecation-%s-%s", domainName, workflowID), WorkflowType: &types.WorkflowType{ Name: domaindeprecation.DomainDeprecationWorkflowTypeName, }, TaskList: &types.TaskList{ Name: domaindeprecation.DomainDeprecationTaskListName, }, ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(int32(workflowStartToCloseTimeout)), TaskStartToCloseTimeoutSeconds: common.Int32Ptr(decisionTimeoutInSeconds), RequestID: uuid.New(), Input: input, } resp, err := frontendClient.StartWorkflowExecution(ctx, startRequest) if err != nil { return commoncli.Problem("Failed to start domain deprecation workflow", err) } fmt.Printf("Domain deprecation is in progress. Workflow ID: %s, Run ID: %s\n", startRequest.WorkflowID, resp.GetRunID()) return nil } // FailoverDomain fails over a single domain to a target cluster func (d *domainCLIImpl) FailoverDomain(c *cli.Context) error { domainName, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found: ", err) } failoverRequest := &types.FailoverDomainRequest{ DomainName: domainName, } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } if c.IsSet(FlagActiveClustersJSON) && c.IsSet(FlagActiveClusters) { return commoncli.Problem("Cannot use both --active_clusters_json and --active_clusters flags.", nil) } if !c.IsSet(FlagActiveClusterName) && !c.IsSet(FlagActiveClusters) && !c.IsSet(FlagActiveClustersJSON) { return commoncli.Problem("At least one of the flags --active_cluster, --active_clusters or --active_clusters_json must be provided.", nil) } if c.IsSet(FlagActiveClusterName) { // active-passive domain failover activeCluster := c.String(FlagActiveClusterName) failoverRequest.DomainActiveClusterName = common.StringPtr(activeCluster) } if c.IsSet(FlagActiveClusters) { // active-active domain failover ac, err := parseActiveClustersByClusterAttribute(c.String(FlagActiveClusters)) if err != nil { return err } failoverRequest.ActiveClusters = &ac } if c.IsSet(FlagActiveClustersJSON) { ac, err := parseActiveClustersByClusterAttributeFromJSON(c.String(FlagActiveClustersJSON)) if err != nil { return err } failoverRequest.ActiveClusters = &ac } // Set the reason for failover (will use default value if not specified) reason := c.String(FlagFailoverReason) if reason != "" { failoverRequest.Reason = common.StringPtr(reason) } _, err = d.failoverDomain(ctx, failoverRequest) if err != nil { if _, ok := err.(*types.EntityNotExistsError); ok { return commoncli.Problem(fmt.Sprintf("Domain %s does not exist.", domainName), err) } return commoncli.Problem("Operation FailoverDomain failed.", err) } fmt.Printf("Domain %s successfully failed over.\n", domainName) return nil } // FailoverDomains is used for managed failover all domains with domain data IsManagedByCadence=true func (d *domainCLIImpl) FailoverDomains(c *cli.Context) error { // ask user for confirmation prompt("You are trying to failover all managed domains, continue? y/N") _, _, err := d.failoverDomains(c) return err } // return succeed and failed domains for testing purpose func (d *domainCLIImpl) failoverDomains(c *cli.Context) ([]string, []string, error) { targetCluster, err := getRequiredOption(c, FlagActiveClusterName) if err != nil { return nil, nil, commoncli.Problem("Required flag not found: ", err) } domains, err := d.getAllDomains(c) if err != nil { return nil, nil, fmt.Errorf("Failed to list domains: %w", err) } shouldFailover := func(domain *types.DescribeDomainResponse) bool { isDomainNotActiveInTargetCluster := domain.ReplicationConfiguration.GetActiveClusterName() != targetCluster return isDomainNotActiveInTargetCluster && isDomainFailoverManagedByCadence(domain) } var succeedDomains []string var failedDomains []string for _, domain := range domains { if shouldFailover(domain) { domainName := domain.GetDomainInfo().GetName() err := d.failover(c, domainName, targetCluster) if err != nil { printError(getDeps(c).Output(), fmt.Sprintf("Failed failover domain: %s\n", domainName), err) failedDomains = append(failedDomains, domainName) } else { fmt.Printf("Success failover domain: %s\n", domainName) succeedDomains = append(succeedDomains, domainName) } } } fmt.Printf("Succeed %d: %v\n", len(succeedDomains), succeedDomains) fmt.Printf("Failed %d: %v\n", len(failedDomains), failedDomains) return succeedDomains, failedDomains, nil } func (d *domainCLIImpl) getAllDomains(c *cli.Context) ([]*types.DescribeDomainResponse, error) { var res []*types.DescribeDomainResponse pagesize := int32(200) var token []byte ctx, cancel, err := newContext(c) defer cancel() if err != nil { return nil, commoncli.Problem("Error in creating context: ", err) } for more := true; more; more = len(token) > 0 { listRequest := &types.ListDomainsRequest{ PageSize: pagesize, NextPageToken: token, } listResp, err := d.listDomains(ctx, listRequest) if err != nil { return nil, commoncli.Problem("Error when list domains info", err) } token = listResp.GetNextPageToken() res = append(res, listResp.GetDomains()...) } return res, nil } func isDomainFailoverManagedByCadence(domain *types.DescribeDomainResponse) bool { domainData := domain.DomainInfo.GetData() return strings.ToLower(strings.TrimSpace(domainData[constants.DomainDataKeyForManagedFailover])) == "true" } func (d *domainCLIImpl) failover(c *cli.Context, domainName string, targetCluster string) error { updateRequest := &types.UpdateDomainRequest{ Name: domainName, ActiveClusterName: common.StringPtr(targetCluster), } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } _, err = d.updateDomain(ctx, updateRequest) return err } var templateDomain = `Name: {{.Name}} UUID: {{.UUID}} Description: {{.Description}} OwnerEmail: {{.OwnerEmail}} DomainData: {{.DomainData}} Status: {{.Status}} RetentionInDays: {{.RetentionDays}} EmitMetrics: {{.EmitMetrics}} IsGlobal(XDC)Domain: {{.IsGlobal}} ActiveClusterName: {{.ActiveCluster}} IsActiveActiveDomain: {{.IsActiveActiveDomain}} Clusters: {{if .IsGlobal}}{{.Clusters}}{{else}}N/A, Not a global domain{{end}} HistoryArchivalStatus: {{.HistoryArchivalStatus}}{{with .HistoryArchivalURI}} HistoryArchivalURI: {{.}}{{end}} VisibilityArchivalStatus: {{.VisibilityArchivalStatus}}{{with .VisibilityArchivalURI}} VisibilityArchivalURI: {{.}}{{end}} {{with .BadBinaries}}Bad binaries to reset: {{table .}}{{end}} {{with .FailoverInfo}}Graceful failover info: {{table .}}{{end}} {{if .IsActiveActiveDomain}}To see active clusters by cluster attribute use --print-json. {{end}}` // DescribeDomain updates a domain func (d *domainCLIImpl) DescribeDomain(c *cli.Context) error { domainName := c.String(FlagDomain) domainID := c.String(FlagDomainID) printJSON := c.Bool(FlagPrintJSON) request := types.DescribeDomainRequest{} if domainID != "" { request.UUID = &domainID } if domainName != "" { request.Name = &domainName } if domainID == "" && domainName == "" { return commoncli.Problem("At least domainID or domainName must be provided.", nil) } ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } resp, err := d.describeDomain(ctx, &request) if err != nil { if _, ok := err.(*types.EntityNotExistsError); !ok { return commoncli.Problem("Operation DescribeDomain failed.", err) } return commoncli.Problem(fmt.Sprintf("Domain %s does not exist.", domainName), err) } if printJSON { output, err := json.Marshal(resp) if err != nil { return commoncli.Problem("Failed to encode domain response into JSON.", err) } fmt.Println(string(output)) return nil } return Render(c, newDomainRow(resp), RenderOptions{ DefaultTemplate: templateDomain, Color: true, Border: true, PrintDateTime: true, }) } type BadBinaryRow struct { Checksum string `header:"Binary Checksum"` Operator string `header:"Operator"` StartTime time.Time `header:"Start Time"` Reason string `header:"Reason"` } type FailoverInfoRow struct { FailoverVersion int64 `header:"Failover Version"` StartTime time.Time `header:"Start Time"` ExpireTime time.Time `header:"Expire Time"` CompletedShardCount int32 `header:"Completed Shard Count"` PendingShard []int32 `header:"Pending Shard"` } type FailoverHistoryRow struct { EventID string `header:"Event ID"` CreatedTime time.Time `header:"Created Time"` FromCluster string `header:"From Cluster"` ToCluster string `header:"To Cluster"` Attribute string `header:"Cluster Attribute"` } type DomainRow struct { Name string `header:"Name"` UUID string `header:"UUID"` Description string OwnerEmail string DomainData map[string]string `header:"Domain Data"` Status types.DomainStatus `header:"Status"` IsGlobal bool `header:"Is Global Domain"` ActiveCluster string `header:"Active Cluster"` Clusters []string `header:"Clusters"` RetentionDays int32 `header:"Retention Days"` EmitMetrics bool HistoryArchivalStatus types.ArchivalStatus `header:"History Archival Status"` HistoryArchivalURI string `header:"History Archival URI"` VisibilityArchivalStatus types.ArchivalStatus `header:"Visibility Archival Status"` VisibilityArchivalURI string `header:"Visibility Archival URI"` BadBinaries []BadBinaryRow FailoverInfo *FailoverInfoRow LongRunningWorkFlowNum *int IsActiveActiveDomain bool } type DomainMigrationRow struct { ValidationCheck string `header:"Validation Checker"` ValidationResult bool `header:"Validation Result"` ValidationDetails ValidationDetails } type ValidationDetails struct { CurrentDomainRow *types.DescribeDomainResponse NewDomainRow *types.DescribeDomainResponse MismatchedDomainMetaData string LongRunningWorkFlowNum *int MismatchedDynamicConfig []MismatchedDynamicConfig MissingCurrSearchAttributes []string MissingNewSearchAttributes []string } type MismatchedDynamicConfig struct { Key dynamicproperties.Key CurrValues []*types.DynamicConfigValue NewValues []*types.DynamicConfigValue } func newDomainRow(domain *types.DescribeDomainResponse) DomainRow { return DomainRow{ Name: domain.DomainInfo.Name, UUID: domain.DomainInfo.UUID, Description: domain.DomainInfo.Description, OwnerEmail: domain.DomainInfo.OwnerEmail, DomainData: domain.DomainInfo.GetData(), Status: domain.DomainInfo.GetStatus(), IsGlobal: domain.IsGlobalDomain, ActiveCluster: domain.ReplicationConfiguration.GetActiveClusterName(), Clusters: clustersToStrings(domain.ReplicationConfiguration.GetClusters()), RetentionDays: domain.Configuration.GetWorkflowExecutionRetentionPeriodInDays(), EmitMetrics: domain.Configuration.GetEmitMetric(), HistoryArchivalStatus: domain.Configuration.GetHistoryArchivalStatus(), HistoryArchivalURI: domain.Configuration.GetHistoryArchivalURI(), VisibilityArchivalStatus: domain.Configuration.GetVisibilityArchivalStatus(), VisibilityArchivalURI: domain.Configuration.GetVisibilityArchivalURI(), BadBinaries: newBadBinaryRows(domain.Configuration.BadBinaries), FailoverInfo: newFailoverInfoRow(domain.FailoverInfo), IsActiveActiveDomain: domain.ReplicationConfiguration.IsActiveActive(), } } func newFailoverInfoRow(info *types.FailoverInfo) *FailoverInfoRow { if info == nil { return nil } return &FailoverInfoRow{ FailoverVersion: info.GetFailoverVersion(), StartTime: time.Unix(0, info.GetFailoverStartTimestamp()), ExpireTime: time.Unix(0, info.GetFailoverExpireTimestamp()), CompletedShardCount: info.GetCompletedShardCount(), PendingShard: info.GetPendingShards(), } } func newBadBinaryRows(bb *types.BadBinaries) []BadBinaryRow { if bb == nil { return nil } rows := []BadBinaryRow{} for cs, bin := range bb.Binaries { rows = append(rows, BadBinaryRow{ Checksum: cs, Operator: bin.GetOperator(), StartTime: time.Unix(0, bin.GetCreatedTimeNano()), Reason: bin.GetReason(), }) } return rows } func renderFailoverHistoryTable(response *types.ListFailoverHistoryResponse) { renderFailoverHistoryTableToWriter(os.Stdout, response) } func renderFailoverHistoryTableToWriter(writer interface{ Write([]byte) (int, error) }, response *types.ListFailoverHistoryResponse) { table := tablewriter.NewWriter(writer) table.SetRowLine(true) table.SetBorder(true) table.SetAutoWrapText(false) table.SetAutoFormatHeaders(true) table.SetAutoMergeCells(true) table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) table.SetAlignment(tablewriter.ALIGN_LEFT) displayClusterAttributeCol := false var columnsToRender [][]string for _, event := range response.GetFailoverEvents() { eventID := event.GetID() createdTime := time.Unix(0, event.GetCreatedTime()).Format(time.RFC3339) clusterFailovers := event.GetClusterFailovers() if len(clusterFailovers) == 0 { // If no cluster failovers, show event info only table.Append([]string{eventID, createdTime, "-", "-", "-"}) continue } for _, failover := range clusterFailovers { fromCluster := "" if fromInfo := failover.GetFromCluster(); fromInfo != nil { fromCluster = fromInfo.ActiveClusterName } toCluster := "" if toInfo := failover.GetToCluster(); toInfo != nil { toCluster = toInfo.ActiveClusterName } domainFailover := fmt.Sprintf("%s -> %s", fromCluster, toCluster) attribute := "" if attr := failover.GetClusterAttribute(); attr != nil { if attr.Scope != "" && attr.Name != "" { attribute = fmt.Sprintf("%s.%s", attr.Scope, attr.Name) displayClusterAttributeCol = true } } if displayClusterAttributeCol { columnsToRender = append(columnsToRender, []string{eventID, createdTime, domainFailover, attribute}) } else { columnsToRender = append(columnsToRender, []string{eventID, createdTime, domainFailover}) } } } table.AppendBulk(columnsToRender) if displayClusterAttributeCol { table.SetHeader([]string{"Event ID", "Failover Timestamp", "Failover", "Cluster Attribute"}) } else { table.SetHeader([]string{"Event ID", "Failover Timestamp", "Failover"}) } table.Render() } func domainTableOptions(c *cli.Context) RenderOptions { printAll := c.Bool(FlagAll) printFull := c.Bool(FlagPrintFullyDetail) return RenderOptions{ DefaultTemplate: templateTable, Color: true, OptionalColumns: map[string]bool{ "Status": printAll || printFull, "Clusters": printFull, "Retention Days": printFull, "History Archival Status": printFull, "History Archival URI": printFull, "Visibility Archival Status": printFull, "Visibility Archival URI": printFull, }, } } func (d *domainCLIImpl) ListDomains(c *cli.Context) error { output := getDeps(c).Output() pageSize := c.Int(FlagPageSize) prefix := c.String(FlagPrefix) printAll := c.Bool(FlagAll) printDeprecated := c.Bool(FlagDeprecated) printJSON := c.Bool(FlagPrintJSON) if printAll && printDeprecated { return commoncli.Problem(fmt.Sprintf("Cannot specify %s and %s flags at the same time.", FlagAll, FlagDeprecated), nil) } domains, err := d.getAllDomains(c) if err != nil { return err } var filteredDomains []*types.DescribeDomainResponse // Only list domains that are matching to the prefix if prefix is provided if len(prefix) > 0 { var prefixDomains []*types.DescribeDomainResponse for _, domain := range domains { if strings.Index(domain.DomainInfo.Name, prefix) == 0 { prefixDomains = append(prefixDomains, domain) } } domains = prefixDomains } if printAll { filteredDomains = domains } else { filteredDomains = make([]*types.DescribeDomainResponse, 0, len(domains)) for _, domain := range domains { if printDeprecated && *domain.DomainInfo.Status == types.DomainStatusDeprecated { filteredDomains = append(filteredDomains, domain) } else if !printDeprecated && *domain.DomainInfo.Status == types.DomainStatusRegistered { filteredDomains = append(filteredDomains, domain) } } } if printJSON { output, err := json.Marshal(filteredDomains) if err != nil { return commoncli.Problem("Failed to encode domain results into JSON.", err) } fmt.Println(string(output)) return nil } table := make([]DomainRow, 0, pageSize) currentPageSize := 0 for i, domain := range filteredDomains { table = append(table, newDomainRow(domain)) currentPageSize++ if currentPageSize != pageSize { continue } // page is full if err := Render(c, table, domainTableOptions(c)); err != nil { return fmt.Errorf("failed to render domain list: %w", err) } if i == len(domains)-1 || !showNextPage(output) { return nil } table = make([]DomainRow, 0, pageSize) currentPageSize = 0 } return Render(c, table, domainTableOptions(c)) } func (d *domainCLIImpl) listDomains( ctx context.Context, request *types.ListDomainsRequest, ) (*types.ListDomainsResponse, error) { if d.frontendClient != nil { return d.frontendClient.ListDomains(ctx, request) } return d.domainHandler.ListDomains(ctx, request) } func (d *domainCLIImpl) registerDomain( ctx context.Context, request *types.RegisterDomainRequest, ) error { if d.frontendClient != nil { return d.frontendClient.RegisterDomain(ctx, request) } return d.domainHandler.RegisterDomain(ctx, request) } func (d *domainCLIImpl) updateDomain( ctx context.Context, request *types.UpdateDomainRequest, ) (*types.UpdateDomainResponse, error) { if d.frontendClient != nil { return d.frontendClient.UpdateDomain(ctx, request) } return d.domainHandler.UpdateDomain(ctx, request) } func (d *domainCLIImpl) deleteDomain( ctx context.Context, request *types.DeleteDomainRequest, ) error { if d.frontendClient != nil { return d.frontendClient.DeleteDomain(ctx, request) } return d.domainHandler.DeleteDomain(ctx, request) } // ListFailoverHistory lists the failover history for a domain func (d *domainCLIImpl) ListFailoverHistory(c *cli.Context) error { domainName, err := getRequiredOption(c, FlagDomain) if err != nil { return commoncli.Problem("Required flag not found: ", err) } // Get domain ID by describing the domain first ctx, cancel, err := newContext(c) defer cancel() if err != nil { return commoncli.Problem("Error in creating context: ", err) } describeResp, err := d.describeDomain(ctx, &types.DescribeDomainRequest{ Name: common.StringPtr(domainName), }) if err != nil { if _, ok := err.(*types.EntityNotExistsError); ok { return commoncli.Problem(fmt.Sprintf("Domain %s does not exist.", domainName), err) } return commoncli.Problem("Failed to describe domain.", err) } domainID := describeResp.DomainInfo.GetUUID() limit := 10 if c.Bool(FlagAll) { limit = -1 } printJSON := c.Bool(FlagPrintJSON) allResponses := &types.ListFailoverHistoryResponse{} var nextPageToken []byte for { request := &types.ListFailoverHistoryRequest{ Filters: &types.ListFailoverHistoryRequestFilters{ DomainID: domainID, }, Pagination: &types.PaginationOptions{ PageSize: common.Int32Ptr(int32(10)), NextPageToken: nextPageToken, }, } ctx, cancel, err = newContext(c) if err != nil { return commoncli.Problem("Error in creating context: ", err) } resp, err := d.frontendClient.ListFailoverHistory(ctx, request) cancel() if err != nil { return commoncli.Problem("Failed to list failover history.", err) } allResponses.FailoverEvents = append(allResponses.FailoverEvents, resp.FailoverEvents...) nextPageToken = resp.NextPageToken if len(nextPageToken) == 0 || nextPageToken == nil { break } if limit > 0 && len(allResponses.FailoverEvents) >= int(limit) { break } } if printJSON { output, err := json.Marshal(allResponses) if err != nil { return commoncli.Problem("Failed to encode failover history into JSON.", err) } fmt.Println(string(output)) return nil } if len(allResponses.GetFailoverEvents()) == 0 { fmt.Println("No failover history found for domain:", domainName) return nil } renderFailoverHistoryTable(allResponses) return nil } func (d *domainCLIImpl) describeDomain( ctx context.Context, request *types.DescribeDomainRequest, ) (*types.DescribeDomainResponse, error) { if d.frontendClient != nil { return d.frontendClient.DescribeDomain(ctx, request) } return d.domainHandler.DescribeDomain(ctx, request) } func (d *domainCLIImpl) failoverDomain( ctx context.Context, request *types.FailoverDomainRequest, ) (*types.FailoverDomainResponse, error) { if d.frontendClient != nil { return d.frontendClient.FailoverDomain(ctx, request) } return d.domainHandler.FailoverDomain(ctx, request) } func archivalStatus(c *cli.Context, statusFlagName string) (*types.ArchivalStatus, error) { if c.IsSet(statusFlagName) { switch c.String(statusFlagName) { case "disabled": return types.ArchivalStatusDisabled.Ptr(), nil case "enabled": return types.ArchivalStatusEnabled.Ptr(), nil default: return nil, commoncli.Problem(fmt.Sprintf("Option %s format is invalid.", statusFlagName), errors.New("invalid status, valid values are \"disabled\" and \"enabled\"")) } } return nil, nil } func clustersToStrings(clusters []*types.ClusterReplicationConfiguration) []string { var res []string for _, cluster := range clusters { res = append(res, cluster.GetClusterName()) } return res } func parseActiveClustersByClusterAttributeFromJSON(jsonStr string) (types.ActiveClusters, error) { ac := types.ActiveClusters{} if err := json.Unmarshal([]byte(jsonStr), &ac); err != nil { return types.ActiveClusters{}, fmt.Errorf("couldn't parse the JSON: %w. Got %s", err, jsonStr) } return ac, nil } func parseActiveClustersByClusterAttribute(clusters string) (types.ActiveClusters, error) { split := regexp.MustCompile(`(?P[a-zA-Z0-9_-]+).(?P[a-zA-Z0-9_-]+):(?P[a-zA-Z0-9_-]+)`) matches := split.FindAllStringSubmatch(clusters, -1) if len(matches) == 0 { return types.ActiveClusters{}, fmt.Errorf("option %s format is invalid. Expected format is 'region.dca:dev2_dca,region.phx:dev2_phx'", FlagActiveClusters) } out := types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{}, } for _, match := range matches { if len(match) != 4 { return types.ActiveClusters{}, fmt.Errorf("option %s format is invalid. Expected format is 'region.dca:dev2_dca,region.phx:dev2_phx'", FlagActiveClusters) } attribute := match[1] scope := match[2] name := match[3] existing, ok := out.AttributeScopes[attribute] if !ok { out.AttributeScopes[attribute] = types.ClusterAttributeScope{ ClusterAttributes: map[string]types.ActiveClusterInfo{ scope: {ActiveClusterName: name}, }, } } else { if _, ok := existing.ClusterAttributes[scope]; ok { return types.ActiveClusters{}, fmt.Errorf("option active_clusters format is invalid. the key %q was duplicated. This can only map to a single active cluster", scope) } existing.ClusterAttributes[scope] = types.ActiveClusterInfo{ActiveClusterName: name} out.AttributeScopes[attribute] = existing } out.AttributeScopes[attribute].ClusterAttributes[scope] = types.ActiveClusterInfo{ActiveClusterName: name} } return out, nil } ================================================ FILE: tools/cli/domain_commands_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "fmt" "strings" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/uber/cadence/common" "github.com/uber/cadence/common/types" ) func (s *cliAppSuite) TestDomainRegister() { testCases := []testcase{ { "local", "cadence --do test-domain domain register --global_domain false", "", func() { s.serverFrontendClient.EXPECT().RegisterDomain(gomock.Any(), &types.RegisterDomainRequest{ Name: "test-domain", WorkflowExecutionRetentionPeriodInDays: 3, IsGlobalDomain: false, }).Return(nil) }, }, { "global", "cadence --do test-domain domain register --global_domain true", "", func() { s.serverFrontendClient.EXPECT().RegisterDomain(gomock.Any(), &types.RegisterDomainRequest{ Name: "test-domain", WorkflowExecutionRetentionPeriodInDays: 3, IsGlobalDomain: true, }).Return(nil) }, }, { "active-active domain", "cadence --do test-domain domain register --active_clusters region.region1:cluster1,region.region2:cluster2", "", func() { s.serverFrontendClient.EXPECT().RegisterDomain(gomock.Any(), &types.RegisterDomainRequest{ Name: "test-domain", WorkflowExecutionRetentionPeriodInDays: 3, IsGlobalDomain: true, ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": {ActiveClusterName: "cluster1"}, "region2": {ActiveClusterName: "cluster2"}, }, }, }, }, }).Return(nil) }, }, { "active-active domain with invalid active clusters by region", "cadence --do test-domain domain register --active_clusters region1=cluster1", "option active_clusters format is invalid. Expected format is 'region.dca:dev2_dca,region.phx:dev2_phx", nil, }, { "domain with other options", "cadence --do test-domain domain register --global_domain true --retention 5 --desc description --active_cluster c1 --clusters c1,c2 --domain_data key1=value1,key2=value2", "", func() { s.serverFrontendClient.EXPECT().RegisterDomain(gomock.Any(), &types.RegisterDomainRequest{ Name: "test-domain", WorkflowExecutionRetentionPeriodInDays: 5, IsGlobalDomain: true, Description: "description", Data: map[string]string{ "key1": "value1", "key2": "value2", }, ActiveClusterName: "c1", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "c1"}, {ClusterName: "c2"}, }, }).Return(nil) }, }, { "domain with other options and clusters mentioned as multiple options", "cadence --do test-domain domain register --global_domain true --retention 5 --desc description --active_cluster c1 --cl c1 --cl c2 --domain_data key1=value1,key2=value2 --cl c3", "", func() { s.serverFrontendClient.EXPECT().RegisterDomain(gomock.Any(), &types.RegisterDomainRequest{ Name: "test-domain", WorkflowExecutionRetentionPeriodInDays: 5, IsGlobalDomain: true, Description: "description", Data: map[string]string{ "key1": "value1", "key2": "value2", }, ActiveClusterName: "c1", Clusters: []*types.ClusterReplicationConfiguration{ {ClusterName: "c1"}, {ClusterName: "c2"}, {ClusterName: "c3"}, }, }).Return(nil) }, }, { "domain exists", "cadence --do test-domain domain register --global_domain true", "Domain test-domain already registered", func() { s.serverFrontendClient.EXPECT().RegisterDomain(gomock.Any(), &types.RegisterDomainRequest{ Name: "test-domain", WorkflowExecutionRetentionPeriodInDays: 3, IsGlobalDomain: true, }).Return(&types.DomainAlreadyExistsError{}) }, }, { "failed", "cadence --do test-domain domain register --global_domain true", "Register Domain operation failed", func() { s.serverFrontendClient.EXPECT().RegisterDomain(gomock.Any(), &types.RegisterDomainRequest{ Name: "test-domain", WorkflowExecutionRetentionPeriodInDays: 3, IsGlobalDomain: true, }).Return(&types.BadRequestError{Message: "fake error"}) }, }, { "missing flag", "cadence domain register", "option domain is required", nil, }, { "fail on extra arguments at end", "cadence --do test-domain domain register --global_domain true --retention 5 --desc description --active_cluster c1 --clusters c1,c2 unused_arg", "Domain commands cannot have arguments: \nClusters are now specified as --clusters c1,c2 see help for more info", nil, }, { "fail on extra arguments at in command", "cadence --do test-domain domain register --global_domain true --retention 5 --desc description --active_cluster c1 unused_arg --clusters c1,c2", "Domain commands cannot have arguments: \nClusters are now specified as --clusters c1,c2 see help for more info", nil, }, { "invalid global domain flag", "cadence --do test-domain domain register --global_domain invalid", "format is invalid", nil, }, { "invalid history archival status", "cadence --do test-domain domain register --global_domain false --history_archival_status invalid", "failed to parse", nil, }, { "invalid visibility archival status", "cadence --do test-domain domain register --global_domain false --visibility_archival_status invalid", "failed to parse", nil, }, } for _, tt := range testCases { s.Run(tt.name, func() { s.runTestCase(tt) }) } } func (s *cliAppSuite) TestDomainUpdate() { describeResponse := &types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{ Name: "test-domain", Description: "a test domain", OwnerEmail: "test@cadence.io", Data: map[string]string{ "key1": "value1", }, }, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 3, }, ReplicationConfiguration: &types.DomainReplicationConfiguration{ ActiveClusterName: "c1", Clusters: []*types.ClusterReplicationConfiguration{ { ClusterName: "c1", }, { ClusterName: "c2", }, }, }, } testCases := []testcase{ { "update nothing", "cadence --do test-domain domain update", "", func() { s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: common.StringPtr("test-domain"), }).Return(describeResponse, nil) s.serverFrontendClient.EXPECT().UpdateDomain(gomock.Any(), &types.UpdateDomainRequest{ Name: "test-domain", Description: common.StringPtr("a test domain"), OwnerEmail: common.StringPtr("test@cadence.io"), Data: nil, WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(3), EmitMetric: common.BoolPtr(false), HistoryArchivalURI: common.StringPtr(""), VisibilityArchivalURI: common.StringPtr(""), ActiveClusterName: nil, Clusters: nil, }).Return(&types.UpdateDomainResponse{}, nil) }, }, { "update description", "cadence --do test-domain domain update --desc new-description", "", func() { s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: common.StringPtr("test-domain"), }).Return(describeResponse, nil) s.serverFrontendClient.EXPECT().UpdateDomain(gomock.Any(), &types.UpdateDomainRequest{ Name: "test-domain", Description: common.StringPtr("new-description"), OwnerEmail: common.StringPtr("test@cadence.io"), Data: nil, WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(3), EmitMetric: common.BoolPtr(false), HistoryArchivalURI: common.StringPtr(""), VisibilityArchivalURI: common.StringPtr(""), ActiveClusterName: nil, Clusters: nil, }).Return(&types.UpdateDomainResponse{}, nil) }, }, { "active-passive domain failover", "cadence --do test-domain domain update --ac c2", "", func() { s.serverFrontendClient.EXPECT().UpdateDomain(gomock.Any(), &types.UpdateDomainRequest{ Name: "test-domain", ActiveClusterName: common.StringPtr("c2"), }).Return(&types.UpdateDomainResponse{}, nil) }, }, { "active-passive domain graceful failover", "cadence --do test-domain domain update --ac c2 --failover_type grace --failover_timeout_seconds 10", "", func() { s.serverFrontendClient.EXPECT().UpdateDomain(gomock.Any(), &types.UpdateDomainRequest{ Name: "test-domain", ActiveClusterName: common.StringPtr("c2"), FailoverTimeoutInSeconds: common.Int32Ptr(10), }).Return(&types.UpdateDomainResponse{}, nil) }, }, { "active-active domain failover", "cadence --do test-domain domain update --active_clusters region.region1:c1,region.region2:c2", "", func() { s.serverFrontendClient.EXPECT().UpdateDomain(gomock.Any(), &types.UpdateDomainRequest{ Name: "test-domain", ActiveClusters: &types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": { ClusterAttributes: map[string]types.ActiveClusterInfo{ "region1": {ActiveClusterName: "c1"}, "region2": {ActiveClusterName: "c2"}, }, }, }, }, }).Return(&types.UpdateDomainResponse{}, nil) }, }, { "domain not exist", "cadence --do test-domain domain update --desc new-description", "does not exist", func() { s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: common.StringPtr("test-domain"), }).Return(nil, &types.EntityNotExistsError{}) }, }, { "describe failure", "cadence --do test-domain domain update --desc new-description", "describe error", func() { s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: common.StringPtr("test-domain"), }).Return(nil, fmt.Errorf("describe error")) }, }, { "update failure", "cadence --do test-domain domain update --desc new-description", "update error", func() { s.serverFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: common.StringPtr("test-domain"), }).Return(describeResponse, nil) s.serverFrontendClient.EXPECT().UpdateDomain(gomock.Any(), &types.UpdateDomainRequest{ Name: "test-domain", Description: common.StringPtr("new-description"), OwnerEmail: common.StringPtr("test@cadence.io"), Data: nil, WorkflowExecutionRetentionPeriodInDays: common.Int32Ptr(3), EmitMetric: common.BoolPtr(false), HistoryArchivalURI: common.StringPtr(""), VisibilityArchivalURI: common.StringPtr(""), ActiveClusterName: nil, Clusters: nil, }).Return(nil, fmt.Errorf("update error")) }, }, } for _, tt := range testCases { s.Run(tt.name, func() { s.runTestCase(tt) }) } } func (s *cliAppSuite) TestListDomains() { testCases := []testcase{ { "list domains by default", "cadence admin domain list", "", func() { s.serverFrontendClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(&types.ListDomainsResponse{ Domains: []*types.DescribeDomainResponse{ { DomainInfo: &types.DomainInfo{ Name: "test-domain", Status: types.DomainStatusRegistered.Ptr(), }, ReplicationConfiguration: &types.DomainReplicationConfiguration{}, Configuration: &types.DomainConfiguration{}, FailoverInfo: &types.FailoverInfo{}, }, }, }, nil) }, }, } for _, tt := range testCases { s.Run(tt.name, func() { s.runTestCase(tt) }) } } func TestParseActiveClustersByClusterAttribute(t *testing.T) { testCases := map[string]struct { clusters string expected types.ActiveClusters expectedError error }{ "valid active clusters by cluster attribute": { clusters: "region.newyork:cluster0,region.manilla:cluster1", expected: types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": {ClusterAttributes: map[string]types.ActiveClusterInfo{ "newyork": {ActiveClusterName: "cluster0"}, "manilla": {ActiveClusterName: "cluster1"}, }}, }, }, }, "valid active clusters by cluster attribute with multiple scopes": { clusters: "region.newyork:cluster0,location.brussels:cluster2,region.madrid:cluster1", expected: types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": {ClusterAttributes: map[string]types.ActiveClusterInfo{ "newyork": {ActiveClusterName: "cluster0"}, "madrid": {ActiveClusterName: "cluster1"}, }}, "location": {ClusterAttributes: map[string]types.ActiveClusterInfo{ "brussels": {ActiveClusterName: "cluster2"}, }}, }, }, }, "clusters apparently can contain dashes": { clusters: "region.newyork:cluster-0-us-east-1", expected: types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region": {ClusterAttributes: map[string]types.ActiveClusterInfo{ "newyork": {ActiveClusterName: "cluster-0-us-east-1"}, }}, }, }, }, "other things can use dashes too": { clusters: "region-us-east1.new-york:cluster-0-us-east-1", expected: types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region-us-east1": {ClusterAttributes: map[string]types.ActiveClusterInfo{ "new-york": {ActiveClusterName: "cluster-0-us-east-1"}, }}, }, }, }, "duplicate keys consistutes an error in parsing and shouldn't be allowed": { clusters: "region.newyork:cluster0,region.newyork:cluster1", expectedError: fmt.Errorf(`option active_clusters format is invalid. the key "newyork" was duplicated. This can only map to a single active cluster`), }, "Some invalid input": { clusters: "bad-data", expectedError: fmt.Errorf("option active_clusters format is invalid. Expected format is 'region.dca:dev2_dca,region.phx:dev2_phx'"), }, "empty input": { clusters: "", expectedError: fmt.Errorf("option active_clusters format is invalid. Expected format is 'region.dca:dev2_dca,region.phx:dev2_phx'"), }, } for name, td := range testCases { t.Run(name, func(t *testing.T) { activeClusters, err := parseActiveClustersByClusterAttribute(td.clusters) assert.Equal(t, td.expected, activeClusters) assert.Equal(t, td.expectedError, err) }) } } func TestParseActiveClustersByClusterAttributeFromJSON(t *testing.T) { testCases := map[string]struct { jsonStr string expected types.ActiveClusters expectedError bool }{ "valid JSON": { jsonStr: `{"attributeScopes":{"region-us-east1":{"clusterAttributes":{"new-york":{"activeClusterName":"cluster-0-us-east-1","failoverVersion":0}}}}}`, expected: types.ActiveClusters{ AttributeScopes: map[string]types.ClusterAttributeScope{ "region-us-east1": {ClusterAttributes: map[string]types.ActiveClusterInfo{ "new-york": {ActiveClusterName: "cluster-0-us-east-1", FailoverVersion: 0}, }}, }, }, }, "making nerds upset with brackets": { jsonStr: `{"`, expectedError: true, }, } for name, td := range testCases { t.Run(name, func(t *testing.T) { activeClusters, err := parseActiveClustersByClusterAttributeFromJSON(td.jsonStr) assert.Equal(t, td.expected, activeClusters) if td.expectedError { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestRenderFailoverHistoryTable(t *testing.T) { testCases := map[string]struct { response *types.ListFailoverHistoryResponse expectedOutput []string // Substrings that should be present in output }{ "single failover event with one cluster failover": { response: &types.ListFailoverHistoryResponse{ FailoverEvents: []*types.FailoverEvent{ { ID: common.StringPtr("event-1"), CreatedTime: common.Int64Ptr(1700000000000000000), // 2023-11-14T22:13:20Z FailoverType: func() *types.FailoverType { t := types.FailoverTypeGraceful return &t }(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-a"}, ToCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-b"}, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, }, }, }, }, }, expectedOutput: []string{ "event-1", "2023-11-14", // Just check date, not full timestamp with timezone "cluster-a -> cluster-b", "region.us-west", "FAILOVER TIMESTAMP", // New column header (uppercase) "CLUSTER ATTRIBUTE", // Conditional column header (uppercase) }, }, "single failover event with multiple cluster failovers": { response: &types.ListFailoverHistoryResponse{ FailoverEvents: []*types.FailoverEvent{ { ID: common.StringPtr("event-2"), CreatedTime: common.Int64Ptr(1700000000000000000), FailoverType: func() *types.FailoverType { t := types.FailoverTypeForce return &t }(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-a"}, ToCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-b"}, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-west", }, }, { FromCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-c"}, ToCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-d"}, ClusterAttribute: &types.ClusterAttribute{ Scope: "zone", Name: "az-1", }, }, }, }, }, }, expectedOutput: []string{ "event-2", "cluster-a -> cluster-b", "region.us-west", "cluster-c -> cluster-d", "zone.az-1", }, }, "failover event with nil ActiveClusterName": { response: &types.ListFailoverHistoryResponse{ FailoverEvents: []*types.FailoverEvent{ { ID: common.StringPtr("event-3"), CreatedTime: common.Int64Ptr(1700000000000000000), FailoverType: func() *types.FailoverType { t := types.FailoverTypeGraceful return &t }(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ActiveClusterName: ""}, ToCluster: &types.ActiveClusterInfo{ActiveClusterName: ""}, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-east", }, }, }, }, }, }, expectedOutput: []string{ "event-3", " -> ", "region.us-east", }, }, "failover event with nil ClusterAttribute": { response: &types.ListFailoverHistoryResponse{ FailoverEvents: []*types.FailoverEvent{ { ID: common.StringPtr("event-4"), CreatedTime: common.Int64Ptr(1700000000000000000), FailoverType: func() *types.FailoverType { t := types.FailoverTypeForce return &t }(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-x"}, ToCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-y"}, ClusterAttribute: nil, }, }, }, }, }, expectedOutput: []string{ "event-4", "cluster-x -> cluster-y", "FAILOVER", // Should have 3-column table (no Cluster Attribute column, uppercase) }, }, "failover event with empty ClusterAttribute scope and name": { response: &types.ListFailoverHistoryResponse{ FailoverEvents: []*types.FailoverEvent{ { ID: common.StringPtr("event-5"), CreatedTime: common.Int64Ptr(1700000000000000000), FailoverType: func() *types.FailoverType { t := types.FailoverTypeGraceful return &t }(), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-m"}, ToCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-n"}, ClusterAttribute: &types.ClusterAttribute{ Scope: "", Name: "", }, }, }, }, }, }, expectedOutput: []string{ "event-5", "cluster-m -> cluster-n", }, }, "failover event with no cluster failovers": { response: &types.ListFailoverHistoryResponse{ FailoverEvents: []*types.FailoverEvent{ { ID: common.StringPtr("event-6"), CreatedTime: common.Int64Ptr(1700000000000000000), ClusterFailovers: []*types.ClusterFailover{}, }, }, }, expectedOutput: []string{ "event-6", "2023-11-14", // Just check date, not full timestamp with timezone }, }, "failover event with nil FromCluster and ToCluster": { response: &types.ListFailoverHistoryResponse{ FailoverEvents: []*types.FailoverEvent{ { ID: common.StringPtr("event-7"), CreatedTime: common.Int64Ptr(1700000000000000000), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: nil, ToCluster: nil, ClusterAttribute: &types.ClusterAttribute{ Scope: "region", Name: "us-central", }, }, }, }, }, }, expectedOutput: []string{ "event-7", " -> ", "region.us-central", }, }, "multiple failover events": { response: &types.ListFailoverHistoryResponse{ FailoverEvents: []*types.FailoverEvent{ { ID: common.StringPtr("event-8"), CreatedTime: common.Int64Ptr(1700000000000000000), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-1"}, ToCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-2"}, ClusterAttribute: &types.ClusterAttribute{ Scope: "dc", Name: "dc1", }, }, }, }, { ID: common.StringPtr("event-9"), CreatedTime: common.Int64Ptr(1700001000000000000), ClusterFailovers: []*types.ClusterFailover{ { FromCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-3"}, ToCluster: &types.ActiveClusterInfo{ActiveClusterName: "cluster-4"}, ClusterAttribute: &types.ClusterAttribute{ Scope: "dc", Name: "dc2", }, }, }, }, }, }, expectedOutput: []string{ "event-8", "cluster-1 -> cluster-2", "dc.dc1", "event-9", "cluster-3 -> cluster-4", "dc.dc2", }, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { var output strings.Builder renderFailoverHistoryTableToWriter(&output, tc.response) result := output.String() for _, expected := range tc.expectedOutput { assert.Contains(t, result, expected, "output should contain '%s'", expected) } }) } } ================================================ FILE: tools/cli/domain_migration_command.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "context" "encoding/json" "fmt" "reflect" "strconv" "strings" "sync" "time" "github.com/urfave/cli/v2" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/types" ) const ( // workflows running longer than 14 days are considered long running workflows longRunningDuration = 14 * 24 * time.Hour ) var ( domainMigrationTemplate = `Validation Check: {{- range .}} - {{.ValidationCheck}}: {{.ValidationResult}} {{- with .ValidationDetails}} {{- with .CurrentDomainRow}} Current Domain: Name: {{.DomainInfo.Name}} UUID: {{.DomainInfo.UUID}} {{- end}} {{- with .NewDomainRow}} New Domain: Name: {{.DomainInfo.Name}} UUID: {{.DomainInfo.UUID}} {{- end}} {{- if ne (len .MismatchedDomainMetaData) 0 }} Mismatched Domain Meta Data: {{.MismatchedDomainMetaData}} {{- end }} {{- if .LongRunningWorkFlowNum}} Long Running Workflow Num (> 14 days): {{.LongRunningWorkFlowNum}} {{- end}} {{- if .MissingCurrSearchAttributes}} Missing Search Attributes in Current Domain: {{- range .MissingCurrSearchAttributes}} - {{.}} {{- end}} {{- end}} {{- if .MissingNewSearchAttributes}} Missing Search Attributes in New Domain: {{- range .MissingNewSearchAttributes}} - {{.}} {{- end}} {{- end}} {{- range .MismatchedDynamicConfig}} {{- $dynamicConfig := . }} - Config Key: {{.Key}} {{- range $i, $v := .CurrValues}} Current Response: Data: {{ printf "%s" (index $dynamicConfig.CurrValues $i).Value.Data }} Filters: {{- range $filter := (index $dynamicConfig.CurrValues $i).Filters}} - Name: {{ $filter.Name }} Value: {{ printf "%s" $filter.Value.Data }} {{- end}} New Response: Data: {{ printf "%s" (index $dynamicConfig.NewValues $i).Value.Data }} Filters: {{- range $filter := (index $dynamicConfig.NewValues $i).Filters}} - Name: {{ $filter.Name }} Value: {{ printf "%s" $filter.Value.Data }} {{- end}} {{- end}} {{- end}} {{- end}} {{- end}} ` emptyGetDynamicConfigRequest = &types.GetDynamicConfigResponse{ Value: &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), }, } ) type DomainMigrationCommand interface { Validation(c *cli.Context) error DomainMetaDataCheck(c *cli.Context) (DomainMigrationRow, error) DomainWorkFlowCheck(c *cli.Context) (DomainMigrationRow, error) SearchAttributesChecker(c *cli.Context) (DomainMigrationRow, error) DynamicConfigCheck(c *cli.Context) (DomainMigrationRow, error) } func (d *domainMigrationCLIImpl) NewDomainMigrationCLIImpl(c *cli.Context) (*domainMigrationCLIImpl, error) { fc, err := getDeps(c).ServerFrontendClient(c) if err != nil { return nil, err } fcm, err := getDeps(c).ServerFrontendClientForMigration(c) if err != nil { return nil, err } ac, err := getDeps(c).ServerAdminClient(c) if err != nil { return nil, err } acm, err := getDeps(c).ServerAdminClientForMigration(c) if err != nil { return nil, err } return &domainMigrationCLIImpl{ frontendClient: fc, destinationClient: fcm, frontendAdminClient: ac, destinationAdminClient: acm, }, nil } // Export a function to create an instance of the domainMigrationCLIImpl. func NewDomainMigrationCommand(c *cli.Context) DomainMigrationCommand { return &domainMigrationCLIImpl{} } type domainMigrationCLIImpl struct { frontendClient, destinationClient frontend.Client frontendAdminClient, destinationAdminClient admin.Client } func (d *domainMigrationCLIImpl) Validation(c *cli.Context) error { checkers := []func(*cli.Context) (DomainMigrationRow, error){ d.DomainMetaDataCheck, d.DomainWorkFlowCheck, d.DynamicConfigCheck, d.SearchAttributesChecker, } wg := &sync.WaitGroup{} errCh := make(chan error, len(checkers)) // Channel to capture errors results := make([]DomainMigrationRow, len(checkers)) var err error for i := range checkers { go func(i int) { defer wg.Done() results[i], err = checkers[i](c) if err != nil { errCh <- fmt.Errorf("Error in checkers: %w", err) return } errCh <- nil }(i) } wg.Wait() close(errCh) for err := range errCh { if err != nil { return err } } renderOpts := RenderOptions{ DefaultTemplate: domainMigrationTemplate, Color: true, Border: true, PrintDateTime: true, } if err := Render(c, results, renderOpts); err != nil { return fmt.Errorf("failed to render: %w", err) } return nil } func (d *domainMigrationCLIImpl) DomainMetaDataCheck(c *cli.Context) (DomainMigrationRow, error) { domain := c.String(FlagDomain) newDomain := c.String(FlagDestinationDomain) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return DomainMigrationRow{}, fmt.Errorf("Error in Domain meta data check: %w", err) } currResp, err := d.frontendClient.DescribeDomain(ctx, &types.DescribeDomainRequest{ Name: &domain, }) if err != nil { return DomainMigrationRow{}, fmt.Errorf("Could not describe old domain, Please check to see if old domain exists before migrating. %w", err) } newResp, err := d.destinationClient.DescribeDomain(ctx, &types.DescribeDomainRequest{ Name: &newDomain, }) if err != nil { return DomainMigrationRow{}, fmt.Errorf("Could not describe new domain, Please check to see if new domain exists before migrating. %w", err) } validationResult, mismatchedMetaData := metaDataValidation(currResp, newResp) validationRow := DomainMigrationRow{ ValidationCheck: "Domain Meta Data", ValidationResult: validationResult, ValidationDetails: ValidationDetails{ CurrentDomainRow: currResp, NewDomainRow: newResp, MismatchedDomainMetaData: mismatchedMetaData, }, } return validationRow, nil } func metaDataValidation(currResp *types.DescribeDomainResponse, newResp *types.DescribeDomainResponse) (bool, string) { if !reflect.DeepEqual(currResp.Configuration, newResp.Configuration) { return false, "mismatched DomainConfiguration" } if currResp.DomainInfo.OwnerEmail != newResp.DomainInfo.OwnerEmail { return false, "mismatched OwnerEmail" } return true, "" } func (d *domainMigrationCLIImpl) DomainWorkFlowCheck(c *cli.Context) (DomainMigrationRow, error) { countWorkFlows, err := d.countLongRunningWorkflow(c) if err != nil { return DomainMigrationRow{}, fmt.Errorf("Error in Domain flow check: %w", err) } check := countWorkFlows == 0 dmr := DomainMigrationRow{ ValidationCheck: "Workflow Check", ValidationResult: check, ValidationDetails: ValidationDetails{ LongRunningWorkFlowNum: &countWorkFlows, }, } return dmr, nil } func (d *domainMigrationCLIImpl) countLongRunningWorkflow(c *cli.Context) (int, error) { domain := c.String(FlagDomain) thresholdOfLongRunning := time.Now().Add(-longRunningDuration) request := &types.CountWorkflowExecutionsRequest{ Domain: domain, Query: "CloseTime=missing AND StartTime < " + strconv.FormatInt(thresholdOfLongRunning.UnixNano(), 10), } ctx, cancel, err := newContextForLongPoll(c) defer cancel() if err != nil { return 0, fmt.Errorf("Error in creating long context: %w", err) } response, err := d.frontendClient.CountWorkflowExecutions(ctx, request) if err != nil { return 0, fmt.Errorf("Failed to count workflow. %w", err) } return int(response.GetCount()), nil } func (d *domainMigrationCLIImpl) SearchAttributesChecker(c *cli.Context) (DomainMigrationRow, error) { ctx, cancel, err := newContext(c) defer cancel() if err != nil { return DomainMigrationRow{}, fmt.Errorf("Error in creating long context: %w", err) } // getting user provided search attributes searchAttributes := c.StringSlice(FlagSearchAttribute) if len(searchAttributes) == 0 { dmr := DomainMigrationRow{ ValidationCheck: "Search Attributes Check", ValidationResult: true, } return dmr, nil } // Parse the provided search attributes into a map[string]IndexValueType requiredAttributes := make(map[string]types.IndexedValueType) for _, attr := range searchAttributes { parts := strings.SplitN(attr, ":", 2) if len(parts) != 2 { return DomainMigrationRow{}, fmt.Errorf("Invalid search attribute format: %s, Error: %v", attr, nil) } key, valueType := parts[0], parts[1] ivt, err := parseIndexedValueType(valueType) if err != nil { return DomainMigrationRow{}, fmt.Errorf("Invalid search attribute type for %s: %s , Error: %v", key, valueType, err) } requiredAttributes[key] = ivt } // getting search attributes for current domain currentSearchAttributes, err := d.frontendClient.GetSearchAttributes(ctx) if err != nil { return DomainMigrationRow{}, fmt.Errorf("Unable to get search attributes for old domain. %w", err) } // getting search attributes for new domain destinationSearchAttributes, err := d.destinationClient.GetSearchAttributes(ctx) if err != nil { return DomainMigrationRow{}, fmt.Errorf("Unable to get search attributes for new domain. %w", err) } currentSearchAttrs := currentSearchAttributes.Keys destinationSearchAttrs := destinationSearchAttributes.Keys // checking to see if search attributes exist missingInCurrent := findMissingAttributes(requiredAttributes, currentSearchAttrs) missingInNew := findMissingAttributes(requiredAttributes, destinationSearchAttrs) validationResult := len(missingInCurrent) == 0 && len(missingInNew) == 0 validationRow := DomainMigrationRow{ ValidationCheck: "Search Attributes Check", ValidationResult: validationResult, ValidationDetails: ValidationDetails{ MissingCurrSearchAttributes: missingInCurrent, MissingNewSearchAttributes: missingInNew, }, } return validationRow, nil } // helper to parse types.IndexedValueType from string func parseIndexedValueType(valueType string) (types.IndexedValueType, error) { var result types.IndexedValueType valueTypeBytes := []byte(valueType) if err := result.UnmarshalText(valueTypeBytes); err != nil { return 0, err } return result, nil } // finds missing attributed in a map of existing attributed based on required attributes func findMissingAttributes(requiredAttributes map[string]types.IndexedValueType, existingAttributes map[string]types.IndexedValueType) []string { missingAttributes := make([]string, 0) for key, requiredType := range requiredAttributes { existingType, ok := existingAttributes[key] if !ok || existingType != requiredType { // construct the key:type string format attr := fmt.Sprintf("%s:%s", key, requiredType) missingAttributes = append(missingAttributes, attr) } } return missingAttributes } func (d *domainMigrationCLIImpl) DynamicConfigCheck(c *cli.Context) (DomainMigrationRow, error) { var mismatchedConfigs []MismatchedDynamicConfig check := true resp := dynamicproperties.ListAllProductionKeys() currDomain := c.String(FlagDomain) newDomain := c.String(FlagDestinationDomain) ctx, cancel, err := newContext(c) defer cancel() if err != nil { return DomainMigrationRow{}, fmt.Errorf("Failed to create context: %w", err) } currentDomainID, err1 := getDomainID(ctx, currDomain, d.frontendClient) destinationDomainID, err2 := getDomainID(ctx, newDomain, d.destinationClient) if currentDomainID == "" || err1 != nil { return DomainMigrationRow{}, fmt.Errorf("Failed to get domainID for the current domain. %v", nil) } if destinationDomainID == "" || err2 != nil { return DomainMigrationRow{}, fmt.Errorf("Failed to get domainID for the destination domain. %v", nil) } for _, configKey := range resp { if len(configKey.Filters()) == 1 && configKey.Filters()[0] == dynamicproperties.DomainName { // Validate dynamic configs with only domainName filter currRequest := dynamicproperties.ToGetDynamicConfigFilterRequest(configKey.String(), []dynamicproperties.FilterOption{ dynamicproperties.DomainFilter(currDomain), }) newRequest := dynamicproperties.ToGetDynamicConfigFilterRequest(configKey.String(), []dynamicproperties.FilterOption{ dynamicproperties.DomainFilter(newDomain), }) currResp, err := d.frontendAdminClient.GetDynamicConfig(ctx, currRequest) if err != nil { // empty to indicate N/A currResp = emptyGetDynamicConfigRequest } newResp, err := d.destinationAdminClient.GetDynamicConfig(ctx, newRequest) if err != nil { // empty to indicate N/A newResp = emptyGetDynamicConfigRequest } if !reflect.DeepEqual(currResp.Value, newResp.Value) { check = false mismatchedConfigs = append(mismatchedConfigs, MismatchedDynamicConfig{ Key: configKey, CurrValues: []*types.DynamicConfigValue{ toDynamicConfigValue(currResp.Value, map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: currDomain, }), }, NewValues: []*types.DynamicConfigValue{ toDynamicConfigValue(newResp.Value, map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: newDomain, }), }, }) } } else if len(configKey.Filters()) == 1 && configKey.Filters()[0] == dynamicproperties.DomainID { // Validate dynamic configs with only domainID filter currRequest := dynamicproperties.ToGetDynamicConfigFilterRequest(configKey.String(), []dynamicproperties.FilterOption{ dynamicproperties.DomainIDFilter(currentDomainID), }) newRequest := dynamicproperties.ToGetDynamicConfigFilterRequest(configKey.String(), []dynamicproperties.FilterOption{ dynamicproperties.DomainIDFilter(destinationDomainID), }) currResp, err := d.frontendAdminClient.GetDynamicConfig(ctx, currRequest) if err != nil { // empty to indicate N/A currResp = emptyGetDynamicConfigRequest } newResp, err := d.destinationAdminClient.GetDynamicConfig(ctx, newRequest) if err != nil { // empty to indicate N/A newResp = emptyGetDynamicConfigRequest } if !reflect.DeepEqual(currResp.Value, newResp.Value) { check = false mismatchedConfigs = append(mismatchedConfigs, MismatchedDynamicConfig{ Key: configKey, CurrValues: []*types.DynamicConfigValue{ toDynamicConfigValue(currResp.Value, map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainID: currentDomainID, }), }, NewValues: []*types.DynamicConfigValue{ toDynamicConfigValue(newResp.Value, map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainID: destinationDomainID, }), }, }) } } else if containsFilter(configKey, dynamicproperties.DomainName.String()) && containsFilter(configKey, dynamicproperties.TaskListName.String()) { // Validate dynamic configs with only domainName and TaskList filters taskLists := c.StringSlice(FlagTaskList) var mismatchedCurValues []*types.DynamicConfigValue var mismatchedNewValues []*types.DynamicConfigValue for _, taskList := range taskLists { currRequest := dynamicproperties.ToGetDynamicConfigFilterRequest(configKey.String(), []dynamicproperties.FilterOption{ dynamicproperties.DomainFilter(currDomain), dynamicproperties.TaskListFilter(taskList), }) newRequest := dynamicproperties.ToGetDynamicConfigFilterRequest(configKey.String(), []dynamicproperties.FilterOption{ dynamicproperties.DomainFilter(newDomain), dynamicproperties.TaskListFilter(taskList), }) currResp, err := d.frontendAdminClient.GetDynamicConfig(ctx, currRequest) if err != nil { // empty to indicate N/A currResp = emptyGetDynamicConfigRequest } newResp, err := d.destinationAdminClient.GetDynamicConfig(ctx, newRequest) if err != nil { // empty to indicate N/A newResp = emptyGetDynamicConfigRequest } if !reflect.DeepEqual(currResp.Value, newResp.Value) { check = false mismatchedCurValues = append(mismatchedCurValues, toDynamicConfigValue(currResp.Value, map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: currDomain, dynamicproperties.TaskListName: taskLists, })) mismatchedNewValues = append(mismatchedNewValues, toDynamicConfigValue(newResp.Value, map[dynamicproperties.Filter]interface{}{ dynamicproperties.DomainName: newDomain, dynamicproperties.TaskListName: taskLists, })) } } if len(mismatchedCurValues) > 0 && len(mismatchedNewValues) > 0 { mismatchedConfigs = append(mismatchedConfigs, MismatchedDynamicConfig{ Key: configKey, CurrValues: mismatchedCurValues, NewValues: mismatchedNewValues, }) } } } validationRow := DomainMigrationRow{ ValidationCheck: "Dynamic Config Check", ValidationResult: check, ValidationDetails: ValidationDetails{ MismatchedDynamicConfig: mismatchedConfigs, }, } return validationRow, nil } func getDomainID(c context.Context, domain string, client frontend.Client) (string, error) { resp, err := client.DescribeDomain(c, &types.DescribeDomainRequest{Name: &domain}) if err != nil { return "", fmt.Errorf("Failed to describe domain. %w", err) } return resp.DomainInfo.GetUUID(), nil } func valueToDataBlob(value interface{}) *types.DataBlob { if value == nil { return nil } // No need to handle error as this is a private helper method // where the correct value will always be passed regardless data, _ := json.Marshal(value) return &types.DataBlob{ EncodingType: types.EncodingTypeJSON.Ptr(), Data: data, } } func toDynamicConfigValue(value *types.DataBlob, filterMaps map[dynamicproperties.Filter]interface{}) *types.DynamicConfigValue { var configFilters []*types.DynamicConfigFilter for filter, filterValue := range filterMaps { configFilters = append(configFilters, &types.DynamicConfigFilter{ Name: filter.String(), Value: valueToDataBlob(filterValue), }) } return &types.DynamicConfigValue{ Value: value, Filters: configFilters, } } func containsFilter(key dynamicproperties.Key, value string) bool { filters := key.Filters() for _, filter := range filters { if filter.String() == value { return true } } return false } ================================================ FILE: tools/cli/domain_migration_command_test.go ================================================ // The MIT License (MIT) // Copyright (c) 2017-2020 Uber Technologies Inc. // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package cli import ( "testing" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "github.com/urfave/cli/v2" "go.uber.org/mock/gomock" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/cli/clitest" ) const ( testDomainName = "test-domain" testNewDomainName = "test-new-domain" testDomainOwner = "test-owner" ) type domainMigrationCliTestData struct { mockFrontendClient *frontend.MockClient mockAdminClient *admin.MockClient ioHandler *testIOHandler app *cli.App mockManagerFactory *MockManagerFactory domainMigrationCLIImpl DomainMigrationCommand } func newDomainMigrationCliTestData(t *testing.T) *domainMigrationCliTestData { var td domainMigrationCliTestData ctrl := gomock.NewController(t) td.mockFrontendClient = frontend.NewMockClient(ctrl) td.mockAdminClient = admin.NewMockClient(ctrl) td.mockManagerFactory = NewMockManagerFactory(ctrl) td.ioHandler = &testIOHandler{} // Create a new CLI app with client factory and persistence manager factory td.app = NewCliApp( &clientFactoryMock{ serverFrontendClient: td.mockFrontendClient, serverAdminClient: td.mockAdminClient, }, WithIOHandler(td.ioHandler), WithManagerFactory(td.mockManagerFactory), // Inject the mocked persistence manager factory ) td.domainMigrationCLIImpl = &domainMigrationCLIImpl{ frontendClient: td.mockFrontendClient, destinationClient: td.mockFrontendClient, frontendAdminClient: td.mockAdminClient, destinationAdminClient: td.mockAdminClient, } return &td } func (td *domainMigrationCliTestData) consoleOutput() string { return td.ioHandler.outputBytes.String() } func TestDomainMetaDataCheck(t *testing.T) { testDomain := testDomainName testNewDomain := testNewDomainName tests := []struct { name string testSetup func(td *domainMigrationCliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "all arguments provided", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagDestinationDomain, testNewDomain), ) td.mockFrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{OwnerEmail: testDomainOwner}, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 30, }, }, nil).Times(1) td.mockFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{ Name: &testNewDomain, }).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{OwnerEmail: testDomainOwner}, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 30, }, }, nil).Times(1) return cliCtx }, errContains: "", }, { name: "could not describe old domain", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagDestinationDomain, testNewDomain), ) td.mockFrontendClient.EXPECT().DescribeDomain( gomock.Any(), gomock.Any(), ).Return(nil, assert.AnError).Times(1) return cliCtx }, errContains: "Could not describe old domain, Please check to see if old domain exists before migrating.", }, { name: "could not describe new domain", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagDestinationDomain, testNewDomain), ) td.mockFrontendClient.EXPECT().DescribeDomain(gomock.Any(), gomock.Any()).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{OwnerEmail: testDomainOwner}, Configuration: &types.DomainConfiguration{ WorkflowExecutionRetentionPeriodInDays: 30, }, }, nil).Times(1) td.mockFrontendClient.EXPECT().DescribeDomain( gomock.Any(), gomock.Any(), ).Return(nil, assert.AnError).Times(1) return cliCtx }, errContains: "Could not describe new domain, Please check to see if new domain exists before migrating.", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newDomainMigrationCliTestData(t) cliCtx := tt.testSetup(td) _, err := td.domainMigrationCLIImpl.DomainMetaDataCheck(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestDomainWorkflowCheck(t *testing.T) { tests := []struct { name string testSetup func(td *domainMigrationCliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "all arguments provided", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomainName), ) td.mockFrontendClient.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any()).Return(&types.CountWorkflowExecutionsResponse{ Count: 25, }, nil).Times(1) return cliCtx }, errContains: "", }, { name: "could not count workflows", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomainName), ) td.mockFrontendClient.EXPECT().CountWorkflowExecutions( gomock.Any(), gomock.Any(), ).Return(nil, assert.AnError).Times(1) return cliCtx }, errContains: "Failed to count workflow.", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newDomainMigrationCliTestData(t) cliCtx := tt.testSetup(td) _, err := td.domainMigrationCLIImpl.DomainWorkFlowCheck(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestSearchAttributesChecker(t *testing.T) { tests := []struct { name string testSetup func(td *domainMigrationCliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "all arguments provided", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringSliceArgument(FlagSearchAttribute, "Domain:STRING", "WorkflowID:INT"), ) td.mockFrontendClient.EXPECT().GetSearchAttributes(gomock.Any()).Return(&types.GetSearchAttributesResponse{ Keys: map[string]types.IndexedValueType{ "Domain": types.IndexedValueTypeString, "WorkflowID": types.IndexedValueTypeInt, }, }, nil).Times(2) return cliCtx }, errContains: "", }, { name: "without the search attributes argument", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, ) return cliCtx }, errContains: "", }, { name: "invalid search attribute format", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringSliceArgument(FlagSearchAttribute, "Domain"), ) return cliCtx }, errContains: "Invalid search attribute format:", }, { name: "invalid search attribute type", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringSliceArgument(FlagSearchAttribute, "DomainData:Blob"), ) return cliCtx }, errContains: "Invalid search attribute type for DomainData: Blob", }, { name: "failed to get search attributes for old domain", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringSliceArgument(FlagSearchAttribute, "Domain:STRING", "WorkflowID:INT"), ) td.mockFrontendClient.EXPECT().GetSearchAttributes(gomock.Any()).Return(nil, assert.AnError).Times(1) return cliCtx }, errContains: "Unable to get search attributes for old domain.", }, { name: "failed to get search attributes for new domain", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringSliceArgument(FlagSearchAttribute, "Domain:STRING", "WorkflowID:INT"), ) td.mockFrontendClient.EXPECT().GetSearchAttributes(gomock.Any()).Return(&types.GetSearchAttributesResponse{ Keys: map[string]types.IndexedValueType{ "Domain": types.IndexedValueTypeString, "WorkflowID": types.IndexedValueTypeInt, }, }, nil).Times(1) td.mockFrontendClient.EXPECT().GetSearchAttributes(gomock.Any()).Return(nil, assert.AnError).Times(1) return cliCtx }, errContains: "Unable to get search attributes for new domain.", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newDomainMigrationCliTestData(t) cliCtx := tt.testSetup(td) _, err := td.domainMigrationCLIImpl.SearchAttributesChecker(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } func TestDomainConfigCheck(t *testing.T) { testDomain := testDomainName testNewDomain := testNewDomainName testDomainUUID := uuid.New() testNewDomainUUID := uuid.New() tests := []struct { name string testSetup func(td *domainMigrationCliTestData) *cli.Context errContains string // empty if no error is expected expectedOutput string }{ { name: "dynamic configs are equal", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagDestinationDomain, testNewDomainName), ) td.mockFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{Name: &testDomain}).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{UUID: testDomainUUID}, }, nil).Times(1) td.mockFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{Name: &testNewDomain}).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{UUID: testNewDomainUUID}, }, nil).Times(1) td.mockAdminClient.EXPECT().GetDynamicConfig(gomock.Any(), gomock.Any()).Return(&types.GetDynamicConfigResponse{ Value: &types.DataBlob{ EncodingType: types.EncodingTypeThriftRW.Ptr(), Data: []byte("config-value"), }, }, nil).AnyTimes() return cliCtx }, errContains: "", }, { name: "failed to get domainID fo the current domain", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagDestinationDomain, testNewDomainName), ) td.mockFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{Name: &testDomain}).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{UUID: testDomainUUID}, }, assert.AnError).Times(1) td.mockFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{Name: &testNewDomain}).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{UUID: testNewDomainUUID}, }, assert.AnError).Times(1) return cliCtx }, errContains: "Failed to get domainID for the current domain.", }, { name: "failed to get domainID fo the destination domain", testSetup: func(td *domainMigrationCliTestData) *cli.Context { cliCtx := clitest.NewCLIContext( t, td.app, clitest.StringArgument(FlagDomain, testDomain), clitest.StringArgument(FlagDestinationDomain, testNewDomainName), ) td.mockFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{Name: &testDomain}).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{UUID: testDomainUUID}, }, nil).Times(1) td.mockFrontendClient.EXPECT().DescribeDomain(gomock.Any(), &types.DescribeDomainRequest{Name: &testNewDomain}).Return(&types.DescribeDomainResponse{ DomainInfo: &types.DomainInfo{UUID: testNewDomainUUID}, }, assert.AnError).Times(1) return cliCtx }, errContains: "Failed to get domainID for the destination domain.", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { td := newDomainMigrationCliTestData(t) cliCtx := tt.testSetup(td) _, err := td.domainMigrationCLIImpl.DynamicConfigCheck(cliCtx) if tt.errContains == "" { assert.NoError(t, err) } else { assert.ErrorContains(t, err, tt.errContains) } assert.Equal(t, tt.expectedOutput, td.consoleOutput()) }) } } ================================================ FILE: tools/cli/domain_utils.go ================================================ // Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import ( "fmt" "strings" "github.com/stretchr/testify/mock" "github.com/uber-go/tally" "github.com/urfave/cli/v2" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" "github.com/uber/cadence/common/archiver" "github.com/uber/cadence/common/archiver/provider" "github.com/uber/cadence/common/clock" "github.com/uber/cadence/common/cluster" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/domain" "github.com/uber/cadence/common/dynamicconfig" "github.com/uber/cadence/common/dynamicconfig/dynamicproperties" "github.com/uber/cadence/common/log" "github.com/uber/cadence/common/metrics" "github.com/uber/cadence/common/mocks" "github.com/uber/cadence/common/persistence" "github.com/uber/cadence/common/service" "github.com/uber/cadence/tools/common/flag" ) const ( dependencyMaxQPS = 100 ) var ( registerDomainFlags = []cli.Flag{ &cli.StringFlag{ Name: FlagDescription, Aliases: []string{"desc"}, Usage: "Domain description", }, &cli.StringFlag{ Name: FlagOwnerEmail, Aliases: []string{"oe"}, Usage: "Owner email", }, &cli.StringFlag{ Name: FlagRetentionDays, Aliases: []string{"rd"}, Usage: "Workflow execution retention in days", }, &cli.StringFlag{ Name: FlagActiveClusterName, Aliases: []string{"ac"}, Usage: "Active cluster name", }, &cli.StringSliceFlag{ Name: FlagActiveClusters, Aliases: []string{"acs"}, Usage: "Active clusters by cluster attribute in the format '.: ie: region.manilla:cluster0,region.newyork:cluster1'", }, &cli.StringSliceFlag{ Name: FlagClusters, Aliases: []string{"cl"}, Usage: FlagClustersUsage, }, &cli.StringFlag{ Name: FlagIsGlobalDomain, Aliases: []string{"gd"}, Usage: "Flag to indicate whether domain is a global domain (active-passive domain). Default to true. Local domain is now legacy.", Value: "true", }, &cli.GenericFlag{ Name: FlagDomainData, Aliases: []string{"dmd"}, Usage: "Domain data of key value pairs (must be in key1=value1,key2=value2,...,keyN=valueN format, e.g. cluster=dca or cluster=dca,instance=cadence)", Value: &flag.StringMap{}, }, &cli.StringFlag{ Name: FlagSecurityToken, Aliases: []string{"st"}, Usage: "Optional token for security check", }, &cli.StringFlag{ Name: FlagHistoryArchivalStatus, Aliases: []string{"has"}, Usage: "Flag to set history archival status, valid values are \"disabled\" and \"enabled\"", }, &cli.StringFlag{ Name: FlagHistoryArchivalURI, Aliases: []string{"huri"}, Usage: "Optionally specify history archival URI (cannot be changed after first time archival is enabled)", }, &cli.StringFlag{ Name: FlagVisibilityArchivalStatus, Aliases: []string{"vas"}, Usage: "Flag to set visibility archival status, valid values are \"disabled\" and \"enabled\"", }, &cli.StringFlag{ Name: FlagVisibilityArchivalURI, Aliases: []string{"vuri"}, Usage: "Optionally specify visibility archival URI (cannot be changed after first time archival is enabled)", }, } updateDomainFlags = []cli.Flag{ &cli.StringFlag{ Name: FlagDescription, Aliases: []string{"desc"}, Usage: "Domain description", }, &cli.StringFlag{ Name: FlagOwnerEmail, Aliases: []string{"oe"}, Usage: "Owner email", }, &cli.StringFlag{ Name: FlagRetentionDays, Aliases: []string{"rd"}, Usage: "Workflow execution retention in days", }, &cli.StringFlag{ Name: FlagActiveClusterName, Aliases: []string{"ac"}, Usage: "Active cluster name", }, &cli.StringSliceFlag{ Name: FlagActiveClusters, Aliases: []string{"acs"}, Usage: "Active clusters by cluster attribute in the format '.: ie: region.manilla:cluster0,region.newyork:cluster1'", }, &cli.StringSliceFlag{ Name: FlagClusters, Aliases: []string{"cl"}, Usage: FlagClustersUsage, }, &cli.GenericFlag{ Name: FlagDomainData, Usage: "Domain data of key value pairs (must be in key1=value1,key2=value2,...,keyN=valueN format, e.g. cluster=dca or cluster=dca,instance=cadence)", Value: &flag.StringMap{}, }, &cli.StringFlag{ Name: FlagSecurityToken, Aliases: []string{"st"}, Usage: "Optional token for security check", }, &cli.StringFlag{ Name: FlagHistoryArchivalStatus, Aliases: []string{"has"}, Usage: "Flag to set history archival status, valid values are \"disabled\" and \"enabled\"", }, &cli.StringFlag{ Name: FlagHistoryArchivalURI, Aliases: []string{"huri"}, Usage: "Optionally specify history archival URI (cannot be changed after first time archival is enabled)", }, &cli.StringFlag{ Name: FlagVisibilityArchivalStatus, Aliases: []string{"vas"}, Usage: "Flag to set visibility archival status, valid values are \"disabled\" and \"enabled\"", }, &cli.StringFlag{ Name: FlagVisibilityArchivalURI, Aliases: []string{"vuri"}, Usage: "Optionally specify visibility archival URI (cannot be changed after first time archival is enabled)", }, &cli.StringFlag{ Name: FlagAddBadBinary, Usage: "Binary checksum to add for resetting workflow", }, &cli.StringFlag{ Name: FlagRemoveBadBinary, Usage: "Binary checksum to remove for resetting workflow", }, &cli.StringFlag{ Name: FlagReason, Usage: "Reason for the operation", }, &cli.StringFlag{ Name: FlagFailoverType, Aliases: []string{"ft"}, Usage: "Domain failover type. Default value: force. Options: [force,grace]", }, &cli.IntFlag{ Name: FlagFailoverTimeout, Aliases: []string{"fts"}, Value: defaultGracefulFailoverTimeoutInSeconds, Usage: "[Optional] Domain failover timeout in seconds.", }, } deleteDomainFlags = []cli.Flag{ &cli.StringFlag{ Name: FlagSecurityToken, Aliases: []string{"st"}, Usage: "Optional token for security check", }, } deprecateDomainFlags = []cli.Flag{ &cli.StringFlag{ Name: FlagSecurityToken, Aliases: []string{"st"}, Usage: "Optional token for security check", }, &cli.BoolFlag{ Name: FlagForce, Usage: "Deprecate domain regardless of domain history.", }, } describeDomainFlags = []cli.Flag{ &cli.StringFlag{ Name: FlagDomainID, Usage: "Domain UUID (required if not specify domainName)", }, &cli.BoolFlag{ Name: FlagPrintJSON, Aliases: []string{"pjson"}, Usage: "Print in raw JSON format", }, getFormatFlag(), } migrateDomainFlags = []cli.Flag{ &cli.StringFlag{ Name: FlagDestinationAddress, Usage: "Destination cadence-frontend address in : format", }, &cli.StringFlag{ Name: FlagDestinationDomain, Usage: "Destination domain name", }, &cli.StringSliceFlag{ Name: FlagTaskList, Usage: "All tasklists in the current domain", }, &cli.StringSliceFlag{ Name: FlagSearchAttribute, Usage: "Specify search attributes in the format key:type, available types are STRING, KEYWORD, INT, DOUBLE, BOOL, DATETIME", }, getFormatFlag(), } failoverDomainFlags = []cli.Flag{ &cli.StringFlag{ Name: FlagActiveClusterName, Aliases: []string{"ac"}, Usage: "Active cluster name", }, &cli.StringSliceFlag{ Name: FlagActiveClusters, Aliases: []string{"acs"}, Usage: "Active clusters by cluster attribute in the format '.: ie: region.manilla:cluster0,region.newyork:cluster1'", }, &cli.StringFlag{ Name: FlagActiveClustersJSON, Aliases: []string{"acs-json"}, Usage: `Active clusters by cluster attribute in JSON format. Eg {"attributeScopes":{"region-us-east1":{"clusterAttributes":{"new-york":{"activeClusterName":"cluster1"}}}}}`, }, &cli.StringFlag{ Name: FlagFailoverReason, Aliases: []string{"r"}, Usage: "Reason for failover (for tracking and transparency)", }, } listFailoverHistoryFlags = []cli.Flag{ &cli.BoolFlag{ Name: FlagAll, Aliases: []string{"a"}, Usage: "List all failover history events", }, &cli.BoolFlag{ Name: FlagPrintJSON, Aliases: []string{"pjson"}, Usage: "Print in raw JSON format", }, getFormatFlag(), } adminDomainCommonFlags = getDBFlags() adminRegisterDomainFlags = append( registerDomainFlags, adminDomainCommonFlags..., ) adminUpdateDomainFlags = append( updateDomainFlags, adminDomainCommonFlags..., ) adminDeleteDomainFlags = append( deleteDomainFlags, adminDomainCommonFlags..., ) adminDeprecateDomainFlags = append( deprecateDomainFlags, adminDomainCommonFlags..., ) adminDescribeDomainFlags = append( updateDomainFlags, adminDomainCommonFlags..., ) ) func initializeFrontendClient(c *cli.Context) (frontend.Client, error) { return getDeps(c).ServerFrontendClient(c) } func initializeFrontendAdminClient(c *cli.Context) (admin.Client, error) { return getDeps(c).ServerAdminClient(c) } func initializeAdminDomainHandler(c *cli.Context) (domain.Handler, error) { configuration, err := getDeps(c).ServerConfig(c) if err != nil { return nil, err } metricsClient := initializeMetricsClient() logger, err := initializeLogger(configuration) if err != nil { return nil, fmt.Errorf("Error in init admin domain handler: %w", err) } clusterMetadata := initializeClusterMetadata(configuration, metricsClient, logger) metadataMgr, err := getDeps(c).initializeDomainManager(c) if err != nil { return nil, fmt.Errorf("Error in init admin domain handler: %w", err) } dynamicConfig, err := initializeDynamicConfig(configuration, logger) if err != nil { return nil, fmt.Errorf("Error in init admin domain handler: %w", err) } archivalprovider, err := initializeArchivalProvider(configuration, clusterMetadata, metricsClient, logger) if err != nil { return nil, fmt.Errorf("Error in init admin domain handler: %w", err) } domainhandler := initializeDomainHandler( logger, metadataMgr, clusterMetadata, initializeArchivalMetadata(configuration, dynamicConfig), archivalprovider, ) return domainhandler, nil } func loadConfig( context *cli.Context, ) (*config.Config, error) { env := getEnvironment(context) zone := getZone(context) configDir, err := getConfigDir(context) if err != nil { return nil, fmt.Errorf("Unable to load config. %w", err) } var cfg config.Config err = config.Load(env, configDir, zone, &cfg) if err != nil { return nil, fmt.Errorf("Unable to load config. %w", err) } return &cfg, nil } func initializeDomainHandler( logger log.Logger, domainManager persistence.DomainManager, clusterMetadata cluster.Metadata, archivalMetadata archiver.ArchivalMetadata, archiverProvider provider.ArchiverProvider, ) domain.Handler { domainConfig := domain.Config{ MinRetentionDays: dynamicproperties.GetIntPropertyFn(dynamicproperties.MinRetentionDays.DefaultInt()), MaxBadBinaryCount: dynamicproperties.GetIntPropertyFilteredByDomain(dynamicproperties.FrontendMaxBadBinaries.DefaultInt()), FailoverCoolDown: dynamicproperties.GetDurationPropertyFnFilteredByDomain(dynamicproperties.FrontendFailoverCoolDown.DefaultDuration()), } return domain.NewHandler( domainConfig, logger, domainManager, nil, // domainAuditManager not needed for CLI tools clusterMetadata, initializeDomainReplicator(logger), archivalMetadata, archiverProvider, clock.NewRealTimeSource(), ) } func initializeLogger( serviceConfig *config.Config, ) (log.Logger, error) { zapLogger, err := serviceConfig.Log.NewZapLogger() if err != nil { return nil, fmt.Errorf("failed to create zap logger, err: %w", err) } return log.NewLogger(zapLogger), nil } func initializeClusterMetadata(serviceConfig *config.Config, metrics metrics.Client, logger log.Logger) cluster.Metadata { clusterGroupMetadata := serviceConfig.ClusterGroupMetadata return cluster.NewMetadata( *clusterGroupMetadata, func(d string) bool { return false }, metrics, logger, ) } func initializeArchivalMetadata( serviceConfig *config.Config, dynamicConfig *dynamicconfig.Collection, ) archiver.ArchivalMetadata { return archiver.NewArchivalMetadata( dynamicConfig, serviceConfig.Archival.History.Status, serviceConfig.Archival.History.EnableRead, serviceConfig.Archival.Visibility.Status, serviceConfig.Archival.Visibility.EnableRead, &serviceConfig.DomainDefaults.Archival, ) } func initializeArchivalProvider( serviceConfig *config.Config, clusterMetadata cluster.Metadata, metricsClient metrics.Client, logger log.Logger, ) (provider.ArchiverProvider, error) { archiverProvider := provider.NewArchiverProvider( serviceConfig.Archival.History.Provider, serviceConfig.Archival.Visibility.Provider, ) historyArchiverBootstrapContainer := &archiver.HistoryBootstrapContainer{ HistoryV2Manager: nil, // not used Logger: logger, MetricsClient: metricsClient, ClusterMetadata: clusterMetadata, DomainCache: nil, // not used } visibilityArchiverBootstrapContainer := &archiver.VisibilityBootstrapContainer{ Logger: logger, MetricsClient: metricsClient, ClusterMetadata: clusterMetadata, DomainCache: nil, // not used } err := archiverProvider.RegisterBootstrapContainer( service.Frontend, historyArchiverBootstrapContainer, visibilityArchiverBootstrapContainer, ) if err != nil { return nil, fmt.Errorf("Error initializing archival provider. %w", err) } return archiverProvider, nil } func initializeDomainReplicator( logger log.Logger, ) domain.Replicator { replicationMessageSink := &mocks.KafkaProducer{} replicationMessageSink.On("Publish", mock.Anything, mock.Anything).Return(nil) return domain.NewDomainReplicator(replicationMessageSink, logger) } func initializeDynamicConfig( serviceConfig *config.Config, logger log.Logger, ) (*dynamicconfig.Collection, error) { // the done channel is used by dynamic config to stop refreshing // and CLI does not need that, so just close the done channel doneChan := make(chan struct{}) close(doneChan) dynamicConfigClient, err := dynamicconfig.NewFileBasedClient( &serviceConfig.DynamicConfig.FileBased, logger, doneChan, ) if err != nil { return nil, fmt.Errorf("Error initializing dynamic config. %w", err) } return dynamicconfig.NewCollection(dynamicConfigClient, logger), nil } func initializeMetricsClient() metrics.Client { return metrics.NewClient(tally.NoopScope, metrics.Common, metrics.HistogramMigration{}) } func getEnvironment(c *cli.Context) string { return strings.TrimSpace(c.String(FlagServiceEnv)) } func getZone(c *cli.Context) string { return strings.TrimSpace(c.String(FlagServiceZone)) } func getConfigDir(c *cli.Context) (string, error) { dirPath := c.String(FlagServiceConfigDir) if len(dirPath) == 0 { return "", fmt.Errorf("Must provide service configuration dir path. %v", nil) } return dirPath, nil } ================================================ FILE: tools/cli/factory.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination factory_mock.go -self_package github.com/uber/cadence/tools/cli package cli import ( "context" "crypto/tls" "crypto/x509" "fmt" "os" "time" "github.com/olivere/elastic" adminv1 "github.com/uber/cadence-idl/go/proto/admin/v1" apiv1 "github.com/uber/cadence-idl/go/proto/api/v1" "github.com/urfave/cli/v2" "go.uber.org/yarpc" "go.uber.org/yarpc/api/transport" "go.uber.org/yarpc/peer" "go.uber.org/yarpc/peer/hostport" "go.uber.org/yarpc/transport/grpc" "go.uber.org/yarpc/transport/tchannel" "go.uber.org/zap" "google.golang.org/grpc/credentials" serverAdmin "github.com/uber/cadence/.gen/go/admin/adminserviceclient" serverFrontend "github.com/uber/cadence/.gen/go/cadence/workflowserviceclient" "github.com/uber/cadence/client/admin" "github.com/uber/cadence/client/frontend" grpcClient "github.com/uber/cadence/client/wrappers/grpc" "github.com/uber/cadence/client/wrappers/thrift" "github.com/uber/cadence/common" cc "github.com/uber/cadence/common/client" "github.com/uber/cadence/common/config" "github.com/uber/cadence/common/types" "github.com/uber/cadence/tools/common/commoncli" ) const ( cadenceClientName = "cadence-client" cadenceFrontendService = "cadence-frontend" ) // ContextKey is an alias for string, used as context key type ContextKey string const ( // CtxKeyJWT is the name of the context key for the JWT CtxKeyJWT = ContextKey("ctxKeyJWT") ) // ClientFactory is used to construct rpc clients type ClientFactory interface { ServerFrontendClient(c *cli.Context) (frontend.Client, error) ServerAdminClient(c *cli.Context) (admin.Client, error) // ServerFrontendClientForMigration frontend client of the migration destination ServerFrontendClientForMigration(c *cli.Context) (frontend.Client, error) // ServerAdminClientForMigration admin client of the migration destination ServerAdminClientForMigration(c *cli.Context) (admin.Client, error) ElasticSearchClient(c *cli.Context) (*elastic.Client, error) ServerConfig(c *cli.Context) (*config.Config, error) } type clientFactory struct { dispatcher *yarpc.Dispatcher // lazy, via ensureDispatcher dispatcherMigration *yarpc.Dispatcher // lazy, via ensureDispatcherForMigration logger *zap.Logger } // NewClientFactory creates a new ClientFactory func NewClientFactory(logger *zap.Logger) ClientFactory { return &clientFactory{ logger: logger, } } // ServerConfig returns Cadence server configs. // Use in some CLI admin operations (e.g. accessing DB directly) func (b *clientFactory) ServerConfig(c *cli.Context) (*config.Config, error) { env := c.String(FlagServiceEnv) zone := c.String(FlagServiceZone) configDir := c.String(FlagServiceConfigDir) var cfg config.Config err := config.Load(env, configDir, zone, &cfg) if err != nil { return nil, commoncli.Problem( fmt.Sprintf( "failed to load config (for --%v %q --%v %q --%v %q)", FlagServiceEnv, env, FlagServiceZone, zone, FlagServiceConfigDir, configDir, ), err, ) } return &cfg, nil } // ServerFrontendClient builds a frontend client (based on server side thrift interface) func (b *clientFactory) ServerFrontendClient(c *cli.Context) (frontend.Client, error) { err := b.ensureDispatcher(c) if err != nil { return nil, commoncli.Problem("failed to create frontend client dependency", err) } clientConfig := b.dispatcher.ClientConfig(cadenceFrontendService) if c.String(FlagTransport) == grpcTransport { return grpcClient.NewFrontendClient( apiv1.NewDomainAPIYARPCClient(clientConfig), apiv1.NewWorkflowAPIYARPCClient(clientConfig), apiv1.NewWorkerAPIYARPCClient(clientConfig), apiv1.NewVisibilityAPIYARPCClient(clientConfig), ), nil } return thrift.NewFrontendClient(serverFrontend.New(clientConfig)), nil } // ServerAdminClient builds an admin client (based on server side thrift interface) func (b *clientFactory) ServerAdminClient(c *cli.Context) (admin.Client, error) { err := b.ensureDispatcher(c) if err != nil { return nil, commoncli.Problem("failed to create admin client dependency", err) } clientConfig := b.dispatcher.ClientConfig(cadenceFrontendService) if c.String(FlagTransport) == grpcTransport { return grpcClient.NewAdminClient(adminv1.NewAdminAPIYARPCClient(clientConfig)), nil } return thrift.NewAdminClient(serverAdmin.New(clientConfig)), nil } // ServerFrontendClientForMigration builds a frontend client (based on server side thrift interface) func (b *clientFactory) ServerFrontendClientForMigration(c *cli.Context) (frontend.Client, error) { err := b.ensureDispatcherForMigration(c) if err != nil { return nil, commoncli.Problem("failed to create frontend client dependency", err) } clientConfig := b.dispatcherMigration.ClientConfig(cadenceFrontendService) if c.String(FlagTransport) == grpcTransport { return grpcClient.NewFrontendClient( apiv1.NewDomainAPIYARPCClient(clientConfig), apiv1.NewWorkflowAPIYARPCClient(clientConfig), apiv1.NewWorkerAPIYARPCClient(clientConfig), apiv1.NewVisibilityAPIYARPCClient(clientConfig), ), nil } return thrift.NewFrontendClient(serverFrontend.New(clientConfig)), nil } // ServerAdminClientForMigration builds an admin client (based on server side thrift interface) func (b *clientFactory) ServerAdminClientForMigration(c *cli.Context) (admin.Client, error) { err := b.ensureDispatcherForMigration(c) if err != nil { return nil, commoncli.Problem("failed to create admin client dependency", err) } clientConfig := b.dispatcherMigration.ClientConfig(cadenceFrontendService) if c.String(FlagTransport) == grpcTransport { return grpcClient.NewAdminClient(adminv1.NewAdminAPIYARPCClient(clientConfig)), nil } return thrift.NewAdminClient(serverAdmin.New(clientConfig)), nil } // ElasticSearchClient builds an ElasticSearch client func (b *clientFactory) ElasticSearchClient(c *cli.Context) (*elastic.Client, error) { url, err := getRequiredOption(c, FlagURL) if err != nil { return nil, fmt.Errorf("Required flag not present %w", err) } retrier := elastic.NewBackoffRetrier(elastic.NewExponentialBackoff(128*time.Millisecond, 513*time.Millisecond)) client, err := elastic.NewClient( elastic.SetURL(url), elastic.SetRetrier(retrier), ) if err != nil { return nil, commoncli.Problem( fmt.Sprintf("failed to create ElasticSearch client (for --%v %q)", FlagURL, url), err, ) } return client, nil } func (b *clientFactory) ensureDispatcher(c *cli.Context) error { if b.dispatcher != nil { return nil } d, err := b.newClientDispatcher(c, c.String(FlagAddress)) if err != nil { return commoncli.Problem( fmt.Sprintf("failed to create dispatcher (for --%v %q)", FlagAddress, c.String(FlagAddress)), err, ) } b.dispatcher = d return nil } func (b *clientFactory) ensureDispatcherForMigration(c *cli.Context) error { if b.dispatcherMigration != nil { return nil } dm, err := b.newClientDispatcher(c, c.String(FlagDestinationAddress)) if err != nil { return fmt.Errorf( "failed to create dispatcher for migration (for --%v %q): %w", FlagDestinationAddress, c.String(FlagDestinationAddress), err, ) } b.dispatcherMigration = dm return nil } func (b *clientFactory) newClientDispatcher(c *cli.Context, hostPortOverride string) (*yarpc.Dispatcher, error) { shouldUseGrpc := c.String(FlagTransport) == grpcTransport hostPort := tchannelPort if shouldUseGrpc { hostPort = grpcPort } if hostPortOverride != "" { hostPort = hostPortOverride } var outbounds transport.Outbounds if shouldUseGrpc { grpcTransport := grpc.NewTransport() outbounds = transport.Outbounds{Unary: grpc.NewTransport().NewSingleOutbound(hostPort)} tlsCertificatePath := c.String(FlagTLSCertPath) if tlsCertificatePath != "" { caCert, err := os.ReadFile(tlsCertificatePath) if err != nil { return nil, commoncli.Problem( fmt.Sprintf("unable to find server CA certificate, from --%s %q", FlagTLSCertPath, tlsCertificatePath), err, ) } caCertPool := x509.NewCertPool() if !caCertPool.AppendCertsFromPEM(caCert) { b.logger.Fatal("Failed to add server CA certificate", zap.Error(err)) } tlsConfig := tls.Config{ RootCAs: caCertPool, } tlsCreds := credentials.NewTLS(&tlsConfig) tlsChooser := peer.NewSingle(hostport.Identify(hostPort), grpcTransport.NewDialer(grpc.DialerCredentials(tlsCreds))) outbounds = transport.Outbounds{Unary: grpc.NewTransport().NewOutbound(tlsChooser)} } } else { ch, err := tchannel.NewChannelTransport(tchannel.ServiceName(cadenceClientName), tchannel.ListenAddr("127.0.0.1:0")) if err != nil { return nil, commoncli.Problem("failed create tchannel client transport", err) } outbounds = transport.Outbounds{Unary: ch.NewSingleOutbound(hostPort)} } dispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: cadenceClientName, Outbounds: yarpc.Outbounds{cadenceFrontendService: outbounds}, OutboundMiddleware: yarpc.OutboundMiddleware{ Unary: &versionMiddleware{}, }, }) if err := dispatcher.Start(); err != nil { if stoperr := dispatcher.Stop(); stoperr != nil { return nil, commoncli.Problem( "failed to start (and stop) tchannel dispatcher", // currently does not print very well due to joined errors, but that can be improved fmt.Errorf("start err: %w, stop err: %w", err, stoperr), ) } return nil, commoncli.Problem("failed to start tchannel dispatcher", err) } return dispatcher, nil } type versionMiddleware struct { } func (vm *versionMiddleware) Call(ctx context.Context, request *transport.Request, out transport.UnaryOutbound) (*transport.Response, error) { request.Headers = request.Headers. With(common.ClientImplHeaderName, cc.CLI). With(common.FeatureVersionHeaderName, cc.SupportedCLIVersion). With(common.ClientFeatureFlagsHeaderName, cc.FeatureFlagsHeader(cc.DefaultCLIFeatureFlags)). With(common.CallerTypeHeaderName, types.CallerTypeCLI.String()) if jwtKey, ok := ctx.Value(CtxKeyJWT).(string); ok { request.Headers = request.Headers.With(common.AuthorizationTokenHeaderName, jwtKey) } return out.Call(ctx, request) } func getJWT(c *cli.Context) string { return c.String(FlagJWT) } func getJWTPrivateKey(c *cli.Context) string { return c.String(FlagJWTPrivateKey) } ================================================ FILE: tools/cli/factory_mock.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: factory.go // // Generated by this command: // // mockgen -package cli -source factory.go -destination factory_mock.go -self_package github.com/uber/cadence/tools/cli // // Package cli is a generated GoMock package. package cli import ( reflect "reflect" elastic "github.com/olivere/elastic" cli "github.com/urfave/cli/v2" gomock "go.uber.org/mock/gomock" admin "github.com/uber/cadence/client/admin" frontend "github.com/uber/cadence/client/frontend" config "github.com/uber/cadence/common/config" ) // MockClientFactory is a mock of ClientFactory interface. type MockClientFactory struct { ctrl *gomock.Controller recorder *MockClientFactoryMockRecorder isgomock struct{} } // MockClientFactoryMockRecorder is the mock recorder for MockClientFactory. type MockClientFactoryMockRecorder struct { mock *MockClientFactory } // NewMockClientFactory creates a new mock instance. func NewMockClientFactory(ctrl *gomock.Controller) *MockClientFactory { mock := &MockClientFactory{ctrl: ctrl} mock.recorder = &MockClientFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClientFactory) EXPECT() *MockClientFactoryMockRecorder { return m.recorder } // ElasticSearchClient mocks base method. func (m *MockClientFactory) ElasticSearchClient(c *cli.Context) (*elastic.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ElasticSearchClient", c) ret0, _ := ret[0].(*elastic.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // ElasticSearchClient indicates an expected call of ElasticSearchClient. func (mr *MockClientFactoryMockRecorder) ElasticSearchClient(c any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ElasticSearchClient", reflect.TypeOf((*MockClientFactory)(nil).ElasticSearchClient), c) } // ServerAdminClient mocks base method. func (m *MockClientFactory) ServerAdminClient(c *cli.Context) (admin.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ServerAdminClient", c) ret0, _ := ret[0].(admin.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // ServerAdminClient indicates an expected call of ServerAdminClient. func (mr *MockClientFactoryMockRecorder) ServerAdminClient(c any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServerAdminClient", reflect.TypeOf((*MockClientFactory)(nil).ServerAdminClient), c) } // ServerAdminClientForMigration mocks base method. func (m *MockClientFactory) ServerAdminClientForMigration(c *cli.Context) (admin.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ServerAdminClientForMigration", c) ret0, _ := ret[0].(admin.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // ServerAdminClientForMigration indicates an expected call of ServerAdminClientForMigration. func (mr *MockClientFactoryMockRecorder) ServerAdminClientForMigration(c any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServerAdminClientForMigration", reflect.TypeOf((*MockClientFactory)(nil).ServerAdminClientForMigration), c) } // ServerConfig mocks base method. func (m *MockClientFactory) ServerConfig(c *cli.Context) (*config.Config, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ServerConfig", c) ret0, _ := ret[0].(*config.Config) ret1, _ := ret[1].(error) return ret0, ret1 } // ServerConfig indicates an expected call of ServerConfig. func (mr *MockClientFactoryMockRecorder) ServerConfig(c any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServerConfig", reflect.TypeOf((*MockClientFactory)(nil).ServerConfig), c) } // ServerFrontendClient mocks base method. func (m *MockClientFactory) ServerFrontendClient(c *cli.Context) (frontend.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ServerFrontendClient", c) ret0, _ := ret[0].(frontend.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // ServerFrontendClient indicates an expected call of ServerFrontendClient. func (mr *MockClientFactoryMockRecorder) ServerFrontendClient(c any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServerFrontendClient", reflect.TypeOf((*MockClientFactory)(nil).ServerFrontendClient), c) } // ServerFrontendClientForMigration mocks base method. func (m *MockClientFactory) ServerFrontendClientForMigration(c *cli.Context) (frontend.Client, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ServerFrontendClientForMigration", c) ret0, _ := ret[0].(frontend.Client) ret1, _ := ret[1].(error) return ret0, ret1 } // ServerFrontendClientForMigration indicates an expected call of ServerFrontendClientForMigration. func (mr *MockClientFactoryMockRecorder) ServerFrontendClientForMigration(c any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServerFrontendClientForMigration", reflect.TypeOf((*MockClientFactory)(nil).ServerFrontendClientForMigration), c) } ================================================ FILE: tools/cli/flags.go ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cli import "github.com/urfave/cli/v2" // Flags used to specify cli command line arguments const ( FlagVerbose = "verbose" FlagUsername = "username" FlagPassword = "password" FlagKeyspace = "keyspace" FlagDatabaseName = "db_name" FlagEncodingType = "encoding_type" FlagDecodingTypes = "decoding_types" FlagAddress = "address" FlagDestinationAddress = "destination_address" FlagHistoryAddress = "history_address" FlagDBType = "db_type" FlagDBAddress = "db_address" FlagDBPort = "db_port" FlagDBRegion = "db_region" FlagDBShard = "db_shard" FlagProtoVersion = "protocol_version" FlagDomainID = "domain_id" FlagDomain = "domain" FlagDestinationDomain = "destination_domain" FlagShardID = "shard_id" FlagShards = "shards" FlagRangeID = "range_id" FlagWorkflowID = "workflow_id" FlagRunID = "run_id" FlagTreeID = "tree_id" FlagBranchID = "branch_id" FlagNumberOfShards = "number_of_shards" FlagTargetCluster = "target_cluster" FlagSourceCluster = "source_cluster" FlagMinEventID = "min_event_id" FlagMaxEventID = "max_event_id" FlagEndEventVersion = "end_event_version" FlagTaskList = "tasklist" FlagTaskListType = "tasklisttype" FlagWorkflowIDReusePolicy = "workflowidreusepolicy" FlagCronSchedule = "cron" FlagWorkflowType = "workflow_type" FlagWorkflowStatus = "status" FlagExecutionTimeout = "execution_timeout" FlagDecisionTimeout = "decision_timeout" FlagContextTimeout = "context_timeout" FlagInput = "input" FlagInputFile = "input_file" FlagInputEncoding = "encoding" FlagSignalInput = "signal_input" FlagSignalInputFile = "signal_input_file" FlagExcludeFile = "exclude_file" FlagInputSeparator = "input_separator" FlagParallelism = "input_parallelism" FlagParallismDeprecated = "input_parallism" // typo, replaced by FlagParallelism FlagScanType = "scan_type" FlagInvariantCollection = "invariant_collection" FlagSkipCurrentOpen = "skip_current_open" FlagSkipCurrentCompleted = "skip_current_completed" FlagSkipBaseIsNotCurrent = "skip_base_is_not_current" FlagDryRun = "dry_run" FlagNonDeterministicOnly = "only_non_deterministic" FlagInputTopic = "input_topic" FlagHostFile = "host_file" FlagCluster = "cluster" FlagInputCluster = "input_cluster" FlagStartOffset = "start_offset" FlagTopic = "topic" FlagGroup = "group" FlagResult = "result" FlagIdentity = "identity" FlagDetail = "detail" FlagReason = "reason" FlagOpen = "open" FlagMore = "more" FlagAll = "all" FlagPrefix = "prefix" FlagDeprecated = "deprecated" FlagForce = "force" FlagPageID = "page_id" FlagPageSize = "pagesize" FlagLimit = "limit" FlagEarliestTime = "earliest_time" FlagLatestTime = "latest_time" FlagPrintEventVersion = "print_event_version" FlagPrintFullyDetail = "print_full" FlagPrintRawTime = "print_raw_time" FlagPrintRaw = "print_raw" FlagPrintDateTime = "print_datetime" FlagPrintMemo = "print_memo" FlagPrintSearchAttr = "print_search_attr" FlagPrintCron = "print_cron" FlagPrintJSON = "print_json" // Deprecated: use --format json FlagDescription = "description" FlagOwnerEmail = "owner_email" FlagRetentionDays = "retention" FlagHistoryArchivalStatus = "history_archival_status" FlagHistoryArchivalURI = "history_uri" FlagVisibilityArchivalStatus = "visibility_archival_status" FlagVisibilityArchivalURI = "visibility_uri" FlagName = "name" FlagOutputFilename = "output_filename" FlagOutputFormat = "output" FlagQueryType = "query_type" FlagQueryRejectCondition = "query_reject_condition" FlagQueryConsistencyLevel = "query_consistency_level" FlagShowDetail = "show_detail" FlagActiveClusterName = "active_cluster" FlagActiveClusters = "active_clusters" FlagActiveClustersJSON = "active_clusters_json" FlagClusters = "clusters" FlagFailoverReason = "reason" FlagIsGlobalDomain = "global_domain" // active-passive domain FlagDomainData = "domain_data" FlagEventID = "event_id" FlagActivityID = "activity_id" FlagMaxFieldLength = "max_field_length" FlagSecurityToken = "security_token" FlagSkipErrorMode = "skip_errors" FlagRemote = "remote" FlagTimerType = "timer_type" FlagHeadersMode = "headers" FlagMessageType = "message_type" FlagURL = "url" FlagIndex = "index" FlagBatchSize = "batch_size" FlagMemoKey = "memo_key" FlagMemo = "memo" FlagMemoFile = "memo_file" FlagSearchAttributesKey = "search_attr_key" FlagSearchAttributesVal = "search_attr_value" FlagSearchAttributesType = "search_attr_type" FlagAddBadBinary = "add_bad_binary" FlagRemoveBadBinary = "remove_bad_binary" FlagResetType = "reset_type" FlagDecisionOffset = "decision_offset" FlagResetPointsOnly = "reset_points_only" FlagResetBadBinaryChecksum = "reset_bad_binary_checksum" FlagSkipSignalReapply = "skip_signal_reapply" FlagListQuery = "query" FlagExcludeWorkflowIDByQuery = "exclude_query" FlagBatchType = "batch_type" FlagSignalName = "signal_name" FlagTaskID = "task_id" FlagTaskType = "task_type" FlagTaskVisibilityTimestamp = "task_timestamp" FlagQueueType = "queue_type" FlagStartingRPS = "starting_rps" FlagRPS = "rps" FlagRPSScaleUpSeconds = "rps_scale_up_seconds" FlagJobID = "job_id" FlagYes = "yes" FlagServiceConfigDir = "service_config_dir" FlagServiceEnv = "service_env" FlagServiceZone = "service_zone" FlagEnableTLS = "tls" FlagTLSCertPath = "tls_cert_path" FlagTLSKeyPath = "tls_key_path" FlagTLSCaPath = "tls_ca_path" FlagTLSEnableHostVerification = "tls_enable_host_verification" FlagDLQType = "dlq_type" FlagMaxMessageCount = "max_message_count" FlagLastMessageID = "last_message_id" FlagConcurrency = "concurrency" FlagReportRate = "report_rate" FlagLowerShardBound = "lower_shard_bound" FlagUpperShardBound = "upper_shard_bound" FlagInputDirectory = "input_directory" FlagSkipHistoryChecks = "skip_history_checks" FlagFailoverType = "failover_type" FlagFailoverTimeout = "failover_timeout_seconds" FlagActivityHeartBeatTimeout = "heart_beat_timeout_seconds" FlagFailoverWaitTime = "failover_wait_time_second" FlagFailoverBatchSize = "failover_batch_size" FlagFailoverDomains = "domains" FlagFailoverDrillWaitTime = "failover_drill_wait_second" FlagFailoverDrill = "failover_drill" FlagRetryInterval = "retry_interval" FlagRetryAttempts = "retry_attempts" FlagMaxActivityRetries = "max_activity_retries" FlagRetryExpiration = "retry_expiration" FlagRetryBackoff = "retry_backoff" FlagRetryMaxInterval = "retry_max_interval" FlagHeaderKey = "header_key" FlagHeaderValue = "header_value" FlagHeaderFile = "header_file" FlagStartDate = "start_date" FlagEndDate = "end_date" FlagDateFormat = "date_format" FlagShardMultiplier = "shard_multiplier" FlagBucketSize = "bucket_size" DelayStartSeconds = "delay_start_seconds" JitterStartSeconds = "jitter_start_seconds" FirstRunAtTime = "first_run_at_time" FlagConnectionAttributes = "conn_attrs" FlagJWT = "jwt" FlagJWTPrivateKey = "jwt-private-key" FlagDynamicConfigName = "name" FlagDynamicConfigFilter = "filter" FlagDynamicConfigValue = "value" FlagTransport = "transport" FlagFormat = "format" FlagJSON = "json" FlagIsolationGroupSetDrains = "set-drains" FlagIsolationGroupsRemoveAllDrains = "remove-all-drains" FlagSearchAttribute = "search_attr" FlagNumReadPartitions = "num_read_partitions" FlagNumWritePartitions = "num_write_partitions" FlagCronOverlapPolicy = "cron_overlap_policy" FlagClusterAttributeScope = "cluster_attribute_scope" FlagClusterAttributeName = "cluster_attribute_name" FlagClustersUsage = "Clusters (example: --clusters clusterA,clusterB or --cl clusterA --cl clusterB)" ) var flagsForExecution = []cli.Flag{ &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"r", "rid"}, Usage: "RunID", }, } var flagsOfExecutionForShow = []cli.Flag{ &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"r", "rid"}, Usage: "RunID, required for archived history", }, } func getFlagsForShow() []cli.Flag { return append(flagsOfExecutionForShow, getFlagsForShowID()...) } func getFlagsForShowID() []cli.Flag { return []cli.Flag{ &cli.BoolFlag{ Name: FlagPrintDateTime, Aliases: []string{"pdt"}, Usage: "Print timestamp", }, &cli.BoolFlag{ Name: FlagPrintRawTime, Aliases: []string{"prt"}, Usage: "Print raw timestamp", }, &cli.StringFlag{ Name: FlagOutputFilename, Aliases: []string{"of"}, Usage: "Serialize history event to a file", }, &cli.BoolFlag{ Name: FlagPrintFullyDetail, Aliases: []string{"pf"}, Usage: "Print fully event detail", }, &cli.BoolFlag{ Name: FlagPrintEventVersion, Aliases: []string{"pev"}, Usage: "Print event version", }, &cli.IntFlag{ Name: FlagEventID, Aliases: []string{"eid"}, Usage: "Print specific event details", }, &cli.IntFlag{ Name: FlagMaxFieldLength, Aliases: []string{"maxl"}, Usage: "Maximum length for each attribute field", Value: defaultMaxFieldLength, }, &cli.BoolFlag{ Name: FlagResetPointsOnly, Usage: "Only show events that are eligible for reset", }, &cli.StringFlag{ Name: FlagQueryConsistencyLevel, Aliases: []string{"qcl"}, Usage: "Optional flag to set query consistency level. Valid values are \"eventual\" and \"strong\"", }, } } func getFlagsForStart() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Name: FlagTaskList, Aliases: []string{"tl"}, Usage: "TaskList", }, &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagWorkflowType, Aliases: []string{"wt"}, Usage: "WorkflowTypeName", }, &cli.IntFlag{ Name: FlagExecutionTimeout, Aliases: []string{"et"}, Usage: "Execution start to close timeout in seconds", }, &cli.IntFlag{ Name: FlagDecisionTimeout, Aliases: []string{"dt"}, Value: defaultDecisionTimeoutInSeconds, Usage: "Decision task start to close timeout in seconds", }, &cli.StringFlag{ Name: FlagCronSchedule, Usage: "Optional cron schedule for the workflow. Cron spec is as following: \n" + "\t┌───────────── minute (0 - 59) \n" + "\t│ ┌───────────── hour (0 - 23) \n" + "\t│ │ ┌───────────── day of the month (1 - 31) \n" + "\t│ │ │ ┌───────────── month (1 - 12) \n" + "\t│ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday) \n" + "\t│ │ │ │ │ \n" + "\t* * * * *", }, &cli.IntFlag{ Name: FlagCronOverlapPolicy, Aliases: []string{"cop"}, Usage: "Optional cron overlap policy for the workflow when a cron run overlaps with next scheduled run. " + "Available options: 0: Skip running if cron run overlaps, 1: Start new run immediately if previous run overlaps and completes", }, &cli.IntFlag{ Name: FlagWorkflowIDReusePolicy, Aliases: []string{"wrp"}, Usage: "Optional input to configure if the same workflow ID is allow to use for new workflow execution. " + "Available options: 0: AllowDuplicateFailedOnly, 1: AllowDuplicate, 2: RejectDuplicate, 3:TerminateIfRunning", }, &cli.StringFlag{ Name: FlagInput, Aliases: []string{"i"}, Usage: "Optional input for the workflow, in JSON format. If there are multiple parameters, concatenate them and separate by space.", }, &cli.StringFlag{ Name: FlagInputFile, Aliases: []string{"if"}, Usage: "Optional input for the workflow from JSON file. If there are multiple JSON, concatenate them and separate by space or newline. " + "Input from file will be overwrite by input from command line", }, &cli.StringFlag{ Name: FlagMemoKey, Usage: "Optional key of memo. If there are multiple keys, concatenate them and separate by space", }, &cli.StringFlag{ Name: FlagMemo, Usage: "Optional info that can be showed when list workflow, in JSON format. If there are multiple JSON, concatenate them and separate by space. " + "The order must be same as memo_key", }, &cli.StringFlag{ Name: FlagMemoFile, Usage: "Optional info that can be listed in list workflow, from JSON format file. If there are multiple JSON, concatenate them and separate by space or newline. " + "The order must be same as memo_key", }, &cli.StringFlag{ Name: FlagHeaderKey, Usage: "Optional key of header. If there are multiple keys, concatenate them and separate by space", }, &cli.StringFlag{ Name: FlagHeaderValue, Usage: "Optional info to propogate via workflow context, in JSON format. If there are multiple JSON, concatenate them and separate by space. " + "The order must be same as " + FlagHeaderKey, }, &cli.StringFlag{ Name: FlagHeaderFile, Usage: "Optional info to propogate via workflow context, from JSON format file. If there are multiple JSON, concatenate them and separate by space or newline. " + "The order must be same as " + FlagHeaderKey, }, &cli.StringFlag{ Name: FlagSearchAttributesKey, Usage: "Optional search attributes keys that can be be used in list query. If there are multiple keys, concatenate them and separate by |. " + "Use 'cluster get-search-attr' cmd to list legal keys.", }, &cli.StringFlag{ Name: FlagSearchAttributesVal, Usage: "Optional search attributes value that can be be used in list query. If there are multiple keys, concatenate them and separate by |. " + "If value is array, use json array like [\"a\",\"b\"], [1,2], [\"true\",\"false\"], [\"2019-06-07T17:16:34-08:00\",\"2019-06-07T18:16:34-08:00\"]. " + "Use 'cluster get-search-attr' cmd to list legal keys and value types", }, &cli.IntFlag{ Name: FlagRetryExpiration, Usage: "Optional retry expiration in seconds. If set workflow will be retried for the specified period of time. retry_attempts and retry_expiration must not both be 0.", }, &cli.IntFlag{ Name: FlagRetryAttempts, Usage: "Optional retry attempts. If set workflow will be retried the specified amount of times. retry_attempts and retry_expiration must not both be 0.", }, &cli.IntFlag{ Name: FlagRetryInterval, Value: 10, Usage: "Optional retry interval in seconds.", }, &cli.Float64Flag{ Name: FlagRetryBackoff, Value: 1.0, Usage: "Optional retry backoff coefficient. Must be or equal or greater than 1.", }, &cli.IntFlag{ Name: FlagRetryMaxInterval, Usage: "Optional retry maximum interval in seconds. If set will give an upper bound for retry interval. Must be equal or greater than retry interval.", }, &cli.IntFlag{ Name: DelayStartSeconds, Usage: "Optional workflow start delay in seconds. If set workflow start will be delayed this many seconds", }, &cli.IntFlag{ Name: JitterStartSeconds, Usage: "Optional workflow start jitter in seconds. If set, workflow start will be jittered between 0-n seconds (after delay)", }, &cli.StringFlag{ Name: FirstRunAtTime, Usage: "Optional workflow's first run start time in RFC3339 format, like \"1970-01-01T00:00:00Z\". If set, first run of the workflow will start at the specified time.", }, &cli.StringFlag{ Name: FlagClusterAttributeScope, Usage: "Optional cluster attribute to specify how to select the active cluster. Examples might be 'region' or 'location'", Aliases: []string{"cascope"}, }, &cli.StringFlag{ Name: FlagClusterAttributeName, Usage: "Optional cluster attribute name, paired with a cluster attribute scope, to specify how to select the active cluster. This specifies which attribute to tie the workflow to, for example, if the scope is 'region' and the name is 'Lisbon' or 'San Francisco'", Aliases: []string{"caname"}, }, } } func getFlagsForRun() []cli.Flag { flagsForRun := []cli.Flag{ &cli.BoolFlag{ Name: FlagShowDetail, Aliases: []string{"sd"}, Usage: "Show event details", }, &cli.IntFlag{ Name: FlagMaxFieldLength, Aliases: []string{"maxl"}, Usage: "Maximum length for each attribute field", }, } flagsForRun = append(getFlagsForStart(), flagsForRun...) return flagsForRun } func getFlagsForSignal() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Name: FlagWorkflowID, Aliases: []string{"w", "wid"}, Usage: "WorkflowID", }, &cli.StringFlag{ Name: FlagRunID, Aliases: []string{"r", "rid"}, Usage: "RunID", }, &cli.StringFlag{ Name: FlagName, Aliases: []string{"n"}, Usage: "SignalName", }, &cli.StringFlag{ Name: FlagInput, Aliases: []string{"i"}, Usage: "Input for the signal, in JSON format.", }, &cli.StringFlag{ Name: FlagInputFile, Aliases: []string{"if"}, Usage: "Input for the signal from JSON file.", }, } } func getFlagsForSignalWithStart() []cli.Flag { return append(getFlagsForStart(), &cli.StringFlag{ Name: FlagName, Aliases: []string{"n"}, Usage: "SignalName", }, &cli.StringFlag{ Name: FlagSignalInput, Aliases: []string{"si"}, Usage: "Input for the signal, in JSON format.", }, &cli.StringFlag{ Name: FlagSignalInputFile, Aliases: []string{"sif"}, Usage: "Input for the signal from JSON file.", }) } func getFlagsForTerminate() []cli.Flag { return append(flagsForExecution, &cli.StringFlag{ Name: FlagReason, Aliases: []string{"re"}, Usage: "The reason you want to terminate the workflow", }) } func getFlagsForCancel() []cli.Flag { return append(flagsForExecution, &cli.StringFlag{ Name: FlagReason, Aliases: []string{"re"}, Usage: "The reason you want to cancel the workflow", }) } func getFormatFlag() cli.Flag { return &cli.StringFlag{ Name: FlagFormat, Usage: "Format [table|json|